summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.HEADER24
-rw-r--r--.clang-format92
-rw-r--r--.devcontainer/devcontainer.json3
-rwxr-xr-x.devcontainer/setup.sh9
-rw-r--r--.editorconfig21
-rw-r--r--.gitattributes2
-rw-r--r--.github/ISSUE_TEMPLATE19
-rw-r--r--.github/actions/run-build/action.yml45
-rw-r--r--.github/release.yml29
-rw-r--r--.github/workflows/benchmark.yml82
-rw-r--r--.github/workflows/build-containers.yml72
-rw-r--r--.github/workflows/main.yml304
-rw-r--r--.github/workflows/nightly.yml410
-rw-r--r--.gitignore8
-rw-r--r--.mailmap22
-rw-r--r--.vscode/launch.json27
-rw-r--r--.vscode/tasks.json27
-rw-r--r--AUTHORS78
-rw-r--r--CMakeLists.txt145
-rw-r--r--COPYING1216
-rw-r--r--README.md440
-rw-r--r--SECURITY.md14
-rw-r--r--api.docurium13
-rwxr-xr-xci/build.sh93
-rwxr-xr-xci/coverity.sh62
-rw-r--r--ci/docker/bionic49
-rw-r--r--ci/docker/centos760
-rw-r--r--ci/docker/centos858
-rw-r--r--ci/docker/docurium5
-rw-r--r--ci/docker/focal85
-rw-r--r--ci/docker/xenial84
-rwxr-xr-xci/getcontainer.sh55
-rwxr-xr-xci/setup-mingw-build.sh27
-rwxr-xr-xci/setup-osx-benchmark.sh6
-rwxr-xr-xci/setup-osx-build.sh8
-rwxr-xr-xci/setup-ubuntu-benchmark.sh20
-rwxr-xr-xci/setup-win32-benchmark.sh9
-rwxr-xr-xci/setup-win32-build.sh27
-rwxr-xr-xci/test.sh451
-rw-r--r--cmake/AddCFlagIfSupported.cmake30
-rw-r--r--cmake/AddClarTest.cmake7
-rw-r--r--cmake/CheckPrototypeDefinitionSafe.cmake16
-rw-r--r--cmake/DefaultCFlags.cmake154
-rw-r--r--cmake/EnableWarnings.cmake15
-rw-r--r--cmake/ExperimentalFeatures.cmake23
-rw-r--r--cmake/FindCoreFoundation.cmake26
-rw-r--r--cmake/FindGSSAPI.cmake208
-rw-r--r--cmake/FindGSSFramework.cmake28
-rw-r--r--cmake/FindHTTPParser.cmake39
-rw-r--r--cmake/FindIconv.cmake45
-rw-r--r--cmake/FindLibSSH2.cmake13
-rw-r--r--cmake/FindPCRE.cmake37
-rw-r--r--cmake/FindPCRE2.cmake37
-rw-r--r--cmake/FindPkgLibraries.cmake28
-rw-r--r--cmake/FindSecurity.cmake28
-rw-r--r--cmake/FindStatNsec.cmake20
-rw-r--r--cmake/Findfutimens.cmake14
-rw-r--r--cmake/FindmbedTLS.cmake86
-rw-r--r--cmake/IdeSplitSources.cmake22
-rw-r--r--cmake/PkgBuildConfig.cmake77
-rw-r--r--cmake/SanitizeBool.cmake20
-rw-r--r--cmake/SelectGSSAPI.cmake48
-rw-r--r--cmake/SelectHTTPParser.cmake19
-rw-r--r--cmake/SelectHTTPSBackend.cmake143
-rw-r--r--cmake/SelectHashes.cmake104
-rw-r--r--cmake/SelectRegex.cmake51
-rw-r--r--cmake/SelectSSH.cmake41
-rw-r--r--cmake/SelectXdiff.cmake9
-rw-r--r--cmake/SelectZlib.cmake34
-rw-r--r--deps/xdiff/CMakeLists.txt28
-rw-r--r--deps/xdiff/git-xdiff.h56
-rw-r--r--deps/xdiff/xdiff.h150
-rw-r--r--deps/xdiff/xdiffi.c1089
-rw-r--r--deps/xdiff/xdiffi.h62
-rw-r--r--deps/xdiff/xemit.c330
-rw-r--r--deps/xdiff/xemit.h36
-rw-r--r--deps/xdiff/xhistogram.c370
-rw-r--r--deps/xdiff/xinclude.h36
-rw-r--r--deps/xdiff/xmacros.h71
-rw-r--r--deps/xdiff/xmerge.c739
-rw-r--r--deps/xdiff/xpatience.c373
-rw-r--r--deps/xdiff/xprepare.c461
-rw-r--r--deps/xdiff/xprepare.h34
-rw-r--r--deps/xdiff/xtypes.h67
-rw-r--r--deps/xdiff/xutils.c451
-rw-r--r--deps/xdiff/xutils.h48
-rw-r--r--docs/api-stability.md63
-rw-r--r--docs/buffers.md63
-rw-r--r--docs/changelog.md2450
-rw-r--r--docs/checkout-internals.md231
-rw-r--r--docs/code_of_conduct.md75
-rw-r--r--docs/coding-style.md364
-rw-r--r--docs/contributing.md183
-rw-r--r--docs/conventions.md266
-rw-r--r--docs/diff-internals.md92
-rw-r--r--docs/differences-from-git.md27
-rw-r--r--docs/error-handling.md284
-rw-r--r--docs/fuzzing.md72
-rw-r--r--docs/merge-df_conflicts.txt41
-rw-r--r--docs/projects.md93
-rw-r--r--docs/release.md83
-rw-r--r--docs/threading.md110
-rw-r--r--docs/troubleshooting.md13
-rw-r--r--docs/win32-longpaths.md36
-rw-r--r--examples/CMakeLists.txt18
-rw-r--r--examples/COPYING121
-rw-r--r--examples/README.md22
-rw-r--r--examples/add.c157
-rw-r--r--examples/args.c197
-rw-r--r--examples/args.h90
-rw-r--r--examples/blame.c198
-rw-r--r--examples/cat-file.c239
-rw-r--r--examples/checkout.c290
-rw-r--r--examples/clone.c104
-rw-r--r--examples/commit.c86
-rw-r--r--examples/common.c260
-rw-r--r--examples/common.h136
-rw-r--r--examples/config.c71
-rw-r--r--examples/describe.c162
-rw-r--r--examples/diff.c377
-rw-r--r--examples/fetch.c109
-rw-r--r--examples/for-each-ref.c44
-rw-r--r--examples/general.c798
-rw-r--r--examples/index-pack.c76
-rw-r--r--examples/init.c248
-rw-r--r--examples/lg2.c124
-rw-r--r--examples/log.c483
-rw-r--r--examples/ls-files.c131
-rw-r--r--examples/ls-remote.c60
-rw-r--r--examples/merge.c361
-rw-r--r--examples/push.c56
-rw-r--r--examples/remote.c256
-rw-r--r--examples/rev-list.c158
-rw-r--r--examples/rev-parse.c102
-rw-r--r--examples/show-index.c70
-rw-r--r--examples/stash.c157
-rw-r--r--examples/status.c498
-rw-r--r--examples/tag.c310
-rw-r--r--fuzzers/CMakeLists.txt28
-rw-r--r--fuzzers/commit_graph_fuzzer.c85
-rw-r--r--fuzzers/config_file_fuzzer.c61
-rw-r--r--fuzzers/corpora/commit_graph/005682ce1cb5b20c20fccf4be5dbd47ca399e53ebin0 -> 1092 bytes
-rw-r--r--fuzzers/corpora/commit_graph/00574fc29fd1323e93d18d625cde80d3ea20e8ccbin0 -> 2428 bytes
-rw-r--r--fuzzers/corpora/commit_graph/00916ec21ddbd3c622bde6e4dc824250176b9e88bin0 -> 223 bytes
-rw-r--r--fuzzers/corpora/commit_graph/00b6dde4b8d5e68a5ec40d88c39134cf2f1f8bc3bin0 -> 82 bytes
-rw-r--r--fuzzers/corpora/commit_graph/020f0e77e42d8b3810019050f4c5ceadd205b37cbin0 -> 965 bytes
-rw-r--r--fuzzers/corpora/commit_graph/02739c05abc1715fac1ce995b532e482abc8d4dcbin0 -> 982 bytes
-rw-r--r--fuzzers/corpora/commit_graph/02a276faa5dc8c7df5b82a57ab6cd195a13e4ae0bin0 -> 671 bytes
-rw-r--r--fuzzers/corpora/commit_graph/02de15987d68a97db3d9fd964cfd785bcbd54d3abin0 -> 96 bytes
-rw-r--r--fuzzers/corpora/commit_graph/02e106f97a91b1d3aef4dd2d31368ae5077bd42bbin0 -> 146 bytes
-rw-r--r--fuzzers/corpora/commit_graph/038555bcb4cc2daf764840f79ebce4023bdb7670bin0 -> 240 bytes
-rw-r--r--fuzzers/corpora/commit_graph/04c159a04b0732e04ac4c59ed3356860af8dffcebin0 -> 1385 bytes
-rw-r--r--fuzzers/corpora/commit_graph/0560ec993882ffbd8d46dcab0ed430089c4f2aa1bin0 -> 86 bytes
-rw-r--r--fuzzers/corpora/commit_graph/059b3aab3fde6b4c9404aff83fed638596f594bbbin0 -> 90 bytes
-rw-r--r--fuzzers/corpora/commit_graph/06168e726aa0260f520165be4ea0c88244831049bin0 -> 430 bytes
-rw-r--r--fuzzers/corpora/commit_graph/066d1ec700a526b97009cedd0305b6a47242fababin0 -> 1980 bytes
-rw-r--r--fuzzers/corpora/commit_graph/086a5f8cbfa9f058b5c938a6eb724c9e4c5f84f3bin0 -> 68 bytes
-rw-r--r--fuzzers/corpora/commit_graph/089313c698f3e351433e9a45af2ace1d85b9673ebin0 -> 86 bytes
-rw-r--r--fuzzers/corpora/commit_graph/092eb973a771fa14cf0b567d65bd2c99130f543ebin0 -> 32 bytes
-rw-r--r--fuzzers/corpora/commit_graph/094b8cd1aa3e40b1f9ff83680892d52e246df0f8bin0 -> 155 bytes
-rw-r--r--fuzzers/corpora/commit_graph/0ce990c9c2ec121b8c78ba2bdf84679e04c0bdaebin0 -> 278 bytes
-rw-r--r--fuzzers/corpora/commit_graph/0dd0770c34fcf6b1f13219450190616d344db021bin0 -> 264 bytes
-rw-r--r--fuzzers/corpora/commit_graph/0e2b2e6a32733b8a625bc7e812e2ea508d69a5e4bin0 -> 2221 bytes
-rw-r--r--fuzzers/corpora/commit_graph/0e8d0bd07c543d708aecaca377106492b7a74fa3bin0 -> 287 bytes
-rw-r--r--fuzzers/corpora/commit_graph/0f0d16e1b8c8671dbe1074115c1d86aa9b359e7ebin0 -> 551 bytes
-rw-r--r--fuzzers/corpora/commit_graph/102ef78036de5a30927e7f751377b05441c41a08bin0 -> 1980 bytes
-rw-r--r--fuzzers/corpora/commit_graph/10494e7cc9cb8dff289c431d7560bcee0d1b14edbin0 -> 157 bytes
-rw-r--r--fuzzers/corpora/commit_graph/107b11d86381345f50aa19b8485477a870ff399fbin0 -> 2908 bytes
-rw-r--r--fuzzers/corpora/commit_graph/10bb37e18fb3c0897dabacf9c464b4d324007dc3bin0 -> 346 bytes
-rw-r--r--fuzzers/corpora/commit_graph/10ee715f64b08549c3e8261204276694728eb841bin0 -> 174 bytes
-rw-r--r--fuzzers/corpora/commit_graph/123e4eeb7a731f48d06e336b4d29af717f8b6550bin0 -> 123 bytes
-rw-r--r--fuzzers/corpora/commit_graph/125a228afb923970e0a6d4412f7257ba998594a1bin0 -> 283 bytes
-rw-r--r--fuzzers/corpora/commit_graph/130d96c16fba06dcbe7e2a661ab959a3274a4bd9bin0 -> 214 bytes
-rw-r--r--fuzzers/corpora/commit_graph/131c5a2fec55cb0d63f7dc055d6fad5f3dc3c974bin0 -> 408 bytes
-rw-r--r--fuzzers/corpora/commit_graph/13e562d61acb3aa36260a819a00b07ff16450335bin0 -> 3784 bytes
-rw-r--r--fuzzers/corpora/commit_graph/1414e6e8ab6bad1b5c51fed807c514a9d6575e66bin0 -> 584 bytes
-rw-r--r--fuzzers/corpora/commit_graph/1432d191846ae2d0e381813efcfacff2f1dba0e4bin0 -> 475 bytes
-rw-r--r--fuzzers/corpora/commit_graph/14a84cdc6f8d432be4cd3d3eafce92ae385e472fbin0 -> 472 bytes
-rw-r--r--fuzzers/corpora/commit_graph/14e3e735dba88791f2cadd6e0dc5d662a104a6d7bin0 -> 214 bytes
-rw-r--r--fuzzers/corpora/commit_graph/1574abb020203103ea629d677edd21c967fc0f4cbin0 -> 96 bytes
-rw-r--r--fuzzers/corpora/commit_graph/169cc492ba94948a6206765436881a1a0c601780bin0 -> 199 bytes
-rw-r--r--fuzzers/corpora/commit_graph/16a2130c1d75129f3bae3bf8f2c2de41fb3533c0bin0 -> 351 bytes
-rw-r--r--fuzzers/corpora/commit_graph/16ba602eadfc9a3f74c0845394eda0de42b61571bin0 -> 345 bytes
-rw-r--r--fuzzers/corpora/commit_graph/17555fb2dfc444d171ba686667d72e388bd6c041bin0 -> 63 bytes
-rw-r--r--fuzzers/corpora/commit_graph/1a10450d99c1e53d9b7f97b8014cb7fc01906ef2bin0 -> 956 bytes
-rw-r--r--fuzzers/corpora/commit_graph/1af670b5515231fc04b2be9038ee30a7e066b09bbin0 -> 650 bytes
-rw-r--r--fuzzers/corpora/commit_graph/1b72cfa68259e3f3b3802906902a0a29368f86b5bin0 -> 227 bytes
-rw-r--r--fuzzers/corpora/commit_graph/1c62ac5d632aa9e449a4335b675941107d8825aebin0 -> 497 bytes
-rw-r--r--fuzzers/corpora/commit_graph/1d95b5db2f802011b33d10212a66fbe40827dfd4bin0 -> 284 bytes
-rw-r--r--fuzzers/corpora/commit_graph/1e068537ce1211a325aab42ae1263a109131c9f9bin0 -> 81 bytes
-rw-r--r--fuzzers/corpora/commit_graph/1e9c882c9d33304a5791ef6c98eee65e142bd7fdbin0 -> 114 bytes
-rw-r--r--fuzzers/corpora/commit_graph/1f54935df929403a29e77591c97f767d94871aeabin0 -> 202 bytes
-rw-r--r--fuzzers/corpora/commit_graph/206015659641771bb0d668728c2fdc4209e65ddabin0 -> 152 bytes
-rw-r--r--fuzzers/corpora/commit_graph/2096493a2bcc2d15b7ae5bf3112fe49c39976ad8bin0 -> 146 bytes
-rw-r--r--fuzzers/corpora/commit_graph/209b74e08abe8c787b7c5ba81e51cb69c57ecdedbin0 -> 519 bytes
-rw-r--r--fuzzers/corpora/commit_graph/21137876575fbca357fc0c96db1de73c6737e1aebin0 -> 32 bytes
-rw-r--r--fuzzers/corpora/commit_graph/2143d9db9802f076c72a71184cd9d0cb4581e9e7bin0 -> 466 bytes
-rw-r--r--fuzzers/corpora/commit_graph/21a52a5282145407d951ac73c2ff27876783899dbin0 -> 408 bytes
-rw-r--r--fuzzers/corpora/commit_graph/21d5c8c8ac3a09bcba5388c472df32795986a5cb1
-rw-r--r--fuzzers/corpora/commit_graph/22170d1110a1c18009b7feb21a470681f55e85fbbin0 -> 469 bytes
-rw-r--r--fuzzers/corpora/commit_graph/22f55dff94785f24252d7a070f713840f59b0870bin0 -> 609 bytes
-rw-r--r--fuzzers/corpora/commit_graph/23d10ee9694e1c66bedc7060990f19a2ac3eaee3bin0 -> 766 bytes
-rw-r--r--fuzzers/corpora/commit_graph/2435430ca19502c3b0ec4987508d4a8fbdbc898cbin0 -> 349 bytes
-rw-r--r--fuzzers/corpora/commit_graph/244d2ea0c5c3117000b599cfab37680ba8f04513bin0 -> 216 bytes
-rw-r--r--fuzzers/corpora/commit_graph/248bf94143d150da2459cfdca099c30c6daff00abin0 -> 1049 bytes
-rw-r--r--fuzzers/corpora/commit_graph/25bc53498129bb3717671f00c355d2637a91c86abin0 -> 531 bytes
-rw-r--r--fuzzers/corpora/commit_graph/2614f60da2d7e291501397238366d27513bff773bin0 -> 80 bytes
-rw-r--r--fuzzers/corpora/commit_graph/2651b3d5a8b4616b1faa81dabe27ab2712a27561bin0 -> 787 bytes
-rw-r--r--fuzzers/corpora/commit_graph/270257a2872b33dd13c4fd466cbc1ae67d613f9bbin0 -> 600 bytes
-rw-r--r--fuzzers/corpora/commit_graph/2830c6244c74656f6c5649c8226953905a582a38bin0 -> 353 bytes
-rw-r--r--fuzzers/corpora/commit_graph/2889a85c07c20551ff0b97fc640e3c91b33aa4a1bin0 -> 1067 bytes
-rw-r--r--fuzzers/corpora/commit_graph/295ce43fdd56def8948d1ba2bfa7fdf0c47b5318bin0 -> 2227 bytes
-rw-r--r--fuzzers/corpora/commit_graph/296cbb94c4e68ab86972a174405308ee34d0c40fbin0 -> 3003 bytes
-rw-r--r--fuzzers/corpora/commit_graph/2975adf222cad108ec90d8225fd655e30e3bf253bin0 -> 407 bytes
-rw-r--r--fuzzers/corpora/commit_graph/29f5d27760c9254ab4db661a6cd0323dd11c34cabin0 -> 282 bytes
-rw-r--r--fuzzers/corpora/commit_graph/2a359fb09eaad968e57d353453908027645873d1bin0 -> 190 bytes
-rw-r--r--fuzzers/corpora/commit_graph/2a6b65a8d6c28febaa081d220a4433f8366d02bcbin0 -> 3325 bytes
-rw-r--r--fuzzers/corpora/commit_graph/2b14dcade4d0919b0a17830fe353738015f492a6bin0 -> 214 bytes
-rw-r--r--fuzzers/corpora/commit_graph/2b298a13abbd9829e965424a1486baa13d4166c4bin0 -> 146 bytes
-rw-r--r--fuzzers/corpora/commit_graph/2b44d8cd8e70e25172b4c740ebe38ef411c965b3bin0 -> 168 bytes
-rw-r--r--fuzzers/corpora/commit_graph/2b590c4e61fdfcf21c017b29440747a1894b1534bin0 -> 217 bytes
-rw-r--r--fuzzers/corpora/commit_graph/2becb18a971ae30e1a8f6680982fd7305708caa0bin0 -> 324 bytes
-rw-r--r--fuzzers/corpora/commit_graph/2bf78b02099a1fe4ce50d065254e843ca55e280fbin0 -> 412 bytes
-rw-r--r--fuzzers/corpora/commit_graph/2c1541ecd01aa7b9e99bccfe9804198b3e79f118bin0 -> 2267 bytes
-rw-r--r--fuzzers/corpora/commit_graph/2c6798057af5894c27631ff63e845fe1e4bdc9eebin0 -> 221 bytes
-rw-r--r--fuzzers/corpora/commit_graph/2cf7eb7fe489e5acd64df755e820c871784c2ba1bin0 -> 182 bytes
-rw-r--r--fuzzers/corpora/commit_graph/2d49ba35ca404baa0d593925f36a81ce53943c8dbin0 -> 466 bytes
-rw-r--r--fuzzers/corpora/commit_graph/2d507d42ca43ffc2f3c8892826e1db74144ec096bin0 -> 2772 bytes
-rw-r--r--fuzzers/corpora/commit_graph/2e4da693e3e336d2b1a40311a7ccf94def035b6bbin0 -> 280 bytes
-rw-r--r--fuzzers/corpora/commit_graph/2e71ff86128b5618f0f067c407a76ff645ae2019bin0 -> 602 bytes
-rw-r--r--fuzzers/corpora/commit_graph/2eb777c6d7e6ee9bd7a44e37372595043aad596bbin0 -> 141 bytes
-rw-r--r--fuzzers/corpora/commit_graph/2ec3ebffba165b9dd49e755a9e77e23aed796628bin0 -> 138 bytes
-rw-r--r--fuzzers/corpora/commit_graph/302703e3b0d74219868aca39ee7593944c0b2400bin0 -> 126 bytes
-rw-r--r--fuzzers/corpora/commit_graph/3048c6908dc3176707fa8bcb0196824e3358357abin0 -> 395 bytes
-rw-r--r--fuzzers/corpora/commit_graph/30616cb39d3ad6060324fada03709d611ad28d5cbin0 -> 2593 bytes
-rw-r--r--fuzzers/corpora/commit_graph/306beadd9b3135a00037323760eb5377c88a403ebin0 -> 315 bytes
-rw-r--r--fuzzers/corpora/commit_graph/31464a6fbad023923a7e4700fc11564e811bcbd2bin0 -> 2182 bytes
-rw-r--r--fuzzers/corpora/commit_graph/317f4bcfecf066961ef1982d551cd14e63c9f008bin0 -> 215 bytes
-rw-r--r--fuzzers/corpora/commit_graph/31b2248faaabbec69a06098c8cb0f69c5d0aa208bin0 -> 3848 bytes
-rw-r--r--fuzzers/corpora/commit_graph/31d1c3d1147385d58dbe6f82898a5523320fbcacbin0 -> 415 bytes
-rw-r--r--fuzzers/corpora/commit_graph/32c9bc1616a78a230a3724abc02150db1cc40aa0bin0 -> 152 bytes
-rw-r--r--fuzzers/corpora/commit_graph/331e2866416b091252f0299e98d32cfb29237029bin0 -> 5 bytes
-rw-r--r--fuzzers/corpora/commit_graph/331eb3876dd2f3f0bd51f380ac431d86d6e3bb5ebin0 -> 1072 bytes
-rw-r--r--fuzzers/corpora/commit_graph/346bd6eaeadeafcb840ff9441614b309330db63ebin0 -> 2850 bytes
-rw-r--r--fuzzers/corpora/commit_graph/349931f447981f21476481448576e805c093a25bbin0 -> 269 bytes
-rw-r--r--fuzzers/corpora/commit_graph/34a2da1e9adaac1b4be1d40b1ece81fe00643d49bin0 -> 1302 bytes
-rw-r--r--fuzzers/corpora/commit_graph/34bb8f475e7384a8a39618fd15fdc5fb1b12c1a1bin0 -> 391 bytes
-rw-r--r--fuzzers/corpora/commit_graph/351a036c6eb95db9364706b861f7e75ad26194e8bin0 -> 1980 bytes
-rw-r--r--fuzzers/corpora/commit_graph/355452c1da8e7689d816d67cdde040b5df7eabd7bin0 -> 298 bytes
-rw-r--r--fuzzers/corpora/commit_graph/35c157ad2b100b4f334cddcf3dea6ef2d85462bebin0 -> 127 bytes
-rw-r--r--fuzzers/corpora/commit_graph/36a81a45eabfcf53e1ae0361aa234791e2fdb750bin0 -> 128 bytes
-rw-r--r--fuzzers/corpora/commit_graph/36ee20f6dbeb3a34e91eafbbe2e379f9ac6cfa43bin0 -> 86 bytes
-rw-r--r--fuzzers/corpora/commit_graph/377627c19bcac6adc880202048a9eac07b5417d4bin0 -> 350 bytes
-rw-r--r--fuzzers/corpora/commit_graph/38747e7c8bec2f9c923739d50ba54ff88ba6503fbin0 -> 603 bytes
-rw-r--r--fuzzers/corpora/commit_graph/3945843a6fab2ec71030f09b237c125b97cd3ea5bin0 -> 601 bytes
-rw-r--r--fuzzers/corpora/commit_graph/396321d39b82ffaccbc64115117df7e822b0f515bin0 -> 207 bytes
-rw-r--r--fuzzers/corpora/commit_graph/396e78eb9b54e2cefb52cd76a22137c8abd6cbcfbin0 -> 868 bytes
-rw-r--r--fuzzers/corpora/commit_graph/39c1ab66035adc104cd06a6d98b77668172d21afbin0 -> 243 bytes
-rw-r--r--fuzzers/corpora/commit_graph/3a1078c35f5401ce09b5ba921fc348dde37530bbbin0 -> 260 bytes
-rw-r--r--fuzzers/corpora/commit_graph/3aa3d8f40392d1c863d23799b8ec0aedc7191302bin0 -> 192 bytes
-rw-r--r--fuzzers/corpora/commit_graph/3b08c505601271cb92345ec7f0ff0b28daf90a9cbin0 -> 525 bytes
-rw-r--r--fuzzers/corpora/commit_graph/3b41702587be45f678b36823ad2f7e5002337dc4bin0 -> 343 bytes
-rw-r--r--fuzzers/corpora/commit_graph/3b69108cc919aba0248f9b864d4e71c5f6d1931ebin0 -> 208 bytes
-rw-r--r--fuzzers/corpora/commit_graph/3b90507501bb3bcfe0094f9c92cc2869f1a7dda5bin0 -> 135 bytes
-rw-r--r--fuzzers/corpora/commit_graph/3bc7fe44c3a1464dd35a4d22b482f46cdeda0405bin0 -> 188 bytes
-rw-r--r--fuzzers/corpora/commit_graph/3ce99994986efb6df3f3568423e0077b53c7ef78bin0 -> 1178 bytes
-rw-r--r--fuzzers/corpora/commit_graph/3d6cb3ba21181c9f0ab08b2608eab773f36773f2bin0 -> 279 bytes
-rw-r--r--fuzzers/corpora/commit_graph/3d8ec41450b943d5dea73fb1e393960b03d7c3b9bin0 -> 2210 bytes
-rw-r--r--fuzzers/corpora/commit_graph/3e29e8baaac0f6c7e4cf3d5adca2ab3a2c491ac7bin0 -> 310 bytes
-rw-r--r--fuzzers/corpora/commit_graph/3e9469b3c68ba334671aacda7a7669b0e97b74d6bin0 -> 76 bytes
-rw-r--r--fuzzers/corpora/commit_graph/3eeda3bfa7abef69911c94520c009a08c49b9942bin0 -> 237 bytes
-rw-r--r--fuzzers/corpora/commit_graph/3f0f5021016451b57f673d0603cd9e4830c2198dbin0 -> 473 bytes
-rw-r--r--fuzzers/corpora/commit_graph/3f46540fbd94bf0337c1d0d7437ec992a3568f09bin0 -> 178 bytes
-rw-r--r--fuzzers/corpora/commit_graph/402d9c25d5833d42630882ab5c57833266bef785bin0 -> 617 bytes
-rw-r--r--fuzzers/corpora/commit_graph/4048bb3c26d67c345630ff9e86db551a3add6549bin0 -> 1308 bytes
-rw-r--r--fuzzers/corpora/commit_graph/40792f23c1281842dab671e8b213fc408d1ec39fbin0 -> 816 bytes
-rw-r--r--fuzzers/corpora/commit_graph/41cd0b5d9a9540947b7b1841a55e4c11bd4346a2bin0 -> 32 bytes
-rw-r--r--fuzzers/corpora/commit_graph/41d86e5ea3df4a0de60d42aeb16e2a5599aedeaebin0 -> 465 bytes
-rw-r--r--fuzzers/corpora/commit_graph/42b4e5430b2b1b17a361067fb9dd33ab74e52232bin0 -> 202 bytes
-rw-r--r--fuzzers/corpora/commit_graph/42ef1c9d234b90acaf1651d930fc52d5f8f158f2bin0 -> 275 bytes
-rw-r--r--fuzzers/corpora/commit_graph/4570c8ff26d7f31afe73b3d9a35a29bc1274d68abin0 -> 153 bytes
-rw-r--r--fuzzers/corpora/commit_graph/45cf4751a5929930a7c30ec10134434b9ee13c3dbin0 -> 2873 bytes
-rw-r--r--fuzzers/corpora/commit_graph/46e9d351dd5bb71f7d4d8f15b3fad312c781452ebin0 -> 164 bytes
-rw-r--r--fuzzers/corpora/commit_graph/472421633b984556b96bc20f1fcf7a98c25736f3bin0 -> 322 bytes
-rw-r--r--fuzzers/corpora/commit_graph/47f35b91699caee098cacdde0161ffab21bdfc57bin0 -> 123 bytes
-rw-r--r--fuzzers/corpora/commit_graph/48b9da327218f9409287687a43b7eead4789a588bin0 -> 217 bytes
-rw-r--r--fuzzers/corpora/commit_graph/48d14fca326d5d591d18d34c2821a457277819a2bin0 -> 153 bytes
-rw-r--r--fuzzers/corpora/commit_graph/48f3a33e2a027f5735d0a333ec4acd5a2aa57118bin0 -> 2166 bytes
-rw-r--r--fuzzers/corpora/commit_graph/49e0eee24eab094a9c62f6b37b6ba01f8aece4e4bin0 -> 2095 bytes
-rw-r--r--fuzzers/corpora/commit_graph/4b45bcb707d2a0bc23b415e9bc3d7eb1f7f0e188bin0 -> 659 bytes
-rw-r--r--fuzzers/corpora/commit_graph/4c428300fe4866fe81cff02ad4bc14b6848f7f73bin0 -> 281 bytes
-rw-r--r--fuzzers/corpora/commit_graph/4d69c567df2e858c5f248b3fc8e4a9c04f02481cbin0 -> 696 bytes
-rw-r--r--fuzzers/corpora/commit_graph/4d88b6c9b513d5db2e07313a39b43d112d3d4562bin0 -> 66 bytes
-rw-r--r--fuzzers/corpora/commit_graph/4da73370cf854ef8bd08c7f79b92a187cdbff278bin0 -> 81 bytes
-rw-r--r--fuzzers/corpora/commit_graph/4e4b2827351bbfd414b718052a8f950a9e3eb7eebin0 -> 738 bytes
-rw-r--r--fuzzers/corpora/commit_graph/4ed43f7d3c0305461edcbc86f62e0c6ad56df01ebin0 -> 804 bytes
-rw-r--r--fuzzers/corpora/commit_graph/4f011529809e88205421fa8ce39dcc025293bcb8bin0 -> 216 bytes
-rw-r--r--fuzzers/corpora/commit_graph/4f1928b6376369ab6acf8a282284366cc3bf71efbin0 -> 2654 bytes
-rw-r--r--fuzzers/corpora/commit_graph/4f669eca3416c44f0d003ef2720d03e697e2230ebin0 -> 80 bytes
-rw-r--r--fuzzers/corpora/commit_graph/4f750f24ecb5080bea2845061cfd3ce4529d30eebin0 -> 280 bytes
-rw-r--r--fuzzers/corpora/commit_graph/4fab9bb2bacf562e65f4a8681c429e6ea92aaed7bin0 -> 1863 bytes
-rw-r--r--fuzzers/corpora/commit_graph/4fd757c7251c17413b3005fb38aee0fd029d89ecbin0 -> 861 bytes
-rw-r--r--fuzzers/corpora/commit_graph/506092de91dcf93254cdd5ad9e02a953a38099eabin0 -> 280 bytes
-rw-r--r--fuzzers/corpora/commit_graph/50e934fb52d9bc5cd2a531adced1cad7f102a112bin0 -> 385 bytes
-rw-r--r--fuzzers/corpora/commit_graph/512e49a9e789656964988950009e6534907e6317bin0 -> 2021 bytes
-rw-r--r--fuzzers/corpora/commit_graph/51404149f1ea30ee6959fafe81a52acabed97e9ebin0 -> 607 bytes
-rw-r--r--fuzzers/corpora/commit_graph/5150f8a67399ee16178a2b08198cf91a90c0e53ebin0 -> 538 bytes
-rw-r--r--fuzzers/corpora/commit_graph/51a1fd23dfe5a8062cd4601d235509247f3bc2dcbin0 -> 218 bytes
-rw-r--r--fuzzers/corpora/commit_graph/51a963486f041a60c422f0dd6da3b69c52f12fb7bin0 -> 131 bytes
-rw-r--r--fuzzers/corpora/commit_graph/51fbf57a2a35ec33164838fa254fe605a3c868e9bin0 -> 986 bytes
-rw-r--r--fuzzers/corpora/commit_graph/53068b9f9cb54bb52d076e9602ccd55f169ef39abin0 -> 410 bytes
-rw-r--r--fuzzers/corpora/commit_graph/5314619e15fa5ee67df44481b8213a53786d39c5bin0 -> 146 bytes
-rw-r--r--fuzzers/corpora/commit_graph/533f5f00275968129846522fe01e2819746272ebbin0 -> 80 bytes
-rw-r--r--fuzzers/corpora/commit_graph/53a62799135c282435a17e032deda03eaf9daf0fbin0 -> 83 bytes
-rw-r--r--fuzzers/corpora/commit_graph/53c9d5cd849977e523d92dd2d639e9b0e721be50bin0 -> 680 bytes
-rw-r--r--fuzzers/corpora/commit_graph/54767a0bb3b96d39f5b2004ce3f274465f1a927ebin0 -> 2022 bytes
-rw-r--r--fuzzers/corpora/commit_graph/548de37dbe6a3829b73d976996ec9838cf608554bin0 -> 296 bytes
-rw-r--r--fuzzers/corpora/commit_graph/5522cefa54b798ea4aba8ef2a42ad248a7fb02eebin0 -> 60 bytes
-rw-r--r--fuzzers/corpora/commit_graph/554fab3eef5d8709f06d1d4319efe5c0c437421bbin0 -> 192 bytes
-rw-r--r--fuzzers/corpora/commit_graph/567fe73919dae39b0bcb78b03d655643a71714a8bin0 -> 409 bytes
-rw-r--r--fuzzers/corpora/commit_graph/5717a281aa722ee4a32dfa1cc72fc5d6081f6755bin0 -> 2126 bytes
-rw-r--r--fuzzers/corpora/commit_graph/577d814e0be43df9321c5b27119c398bd00a00c5bin0 -> 317 bytes
-rw-r--r--fuzzers/corpora/commit_graph/58680611707c6188f9f067f8747b699cd2fe82d3bin0 -> 453 bytes
-rw-r--r--fuzzers/corpora/commit_graph/5915b7f91dd43ec37a4718061c90cbec2686b916bin0 -> 1339 bytes
-rw-r--r--fuzzers/corpora/commit_graph/599516e368ff621dd06d8450837350f4e9558c38bin0 -> 452 bytes
-rw-r--r--fuzzers/corpora/commit_graph/5a2d01d141e4d523e718c30e20cb07c3ad98f33dbin0 -> 308 bytes
-rw-r--r--fuzzers/corpora/commit_graph/5a9803ef8cd88d1e8f1d6e5920b8afd170cafb11bin0 -> 280 bytes
-rw-r--r--fuzzers/corpora/commit_graph/5ba93c9db0cff93f52b521d7420e43f6eda2784fbin0 -> 1 bytes
-rw-r--r--fuzzers/corpora/commit_graph/5bf0ca772092e6fa34b6822f61a1b1c3d7f2c6e3bin0 -> 318 bytes
-rw-r--r--fuzzers/corpora/commit_graph/5cfbfb3e12b629dc9f74baf0a8741345ec288795bin0 -> 802 bytes
-rw-r--r--fuzzers/corpora/commit_graph/5d8cc97b739c39820b761b6551d34dd647da6816bin0 -> 342 bytes
-rw-r--r--fuzzers/corpora/commit_graph/5dcbb3e1c2fc9a191dd3f3443b86f6bc38c39e37bin0 -> 176 bytes
-rw-r--r--fuzzers/corpora/commit_graph/5ec17d081aef9872f746e88ad8b03553719f9c36bin0 -> 402 bytes
-rw-r--r--fuzzers/corpora/commit_graph/5f88e3ba60c11be25c47a842763d8870d23cc7f2bin0 -> 675 bytes
-rw-r--r--fuzzers/corpora/commit_graph/6045e4d2bf85013c78a32e71b014ba3d4a4b7c61bin0 -> 540 bytes
-rw-r--r--fuzzers/corpora/commit_graph/615c7ba7ffbce955ffd964682e2a0f7ef3c767e4bin0 -> 86 bytes
-rw-r--r--fuzzers/corpora/commit_graph/6189f29cbbe88ac6cb32fdefecda1bd6194332a6bin0 -> 1007 bytes
-rw-r--r--fuzzers/corpora/commit_graph/627224cb8484c62992dcbc4cdebdbfa48a3c021abin0 -> 153 bytes
-rw-r--r--fuzzers/corpora/commit_graph/629fff0962d298a7283a3d1e1d1b940dfef9b315bin0 -> 251 bytes
-rw-r--r--fuzzers/corpora/commit_graph/6322594cff2a99d0abb1139e6a43b06df76d539abin0 -> 284 bytes
-rw-r--r--fuzzers/corpora/commit_graph/63de5e8e042222d53bf05640c87da376aefb76ccbin0 -> 2084 bytes
-rw-r--r--fuzzers/corpora/commit_graph/647dbb1d05fe0fab685bfe126bd9ac3a12b6bccfbin0 -> 279 bytes
-rw-r--r--fuzzers/corpora/commit_graph/647e5e265d8d1079784fc2a3da25f7ba58126acdbin0 -> 73 bytes
-rw-r--r--fuzzers/corpora/commit_graph/653bd480dfd1e5f4bdca702aba3dfd8da0c204b7bin0 -> 103 bytes
-rw-r--r--fuzzers/corpora/commit_graph/65485740a465377213c80fa68028727f281299fbbin0 -> 80 bytes
-rw-r--r--fuzzers/corpora/commit_graph/6551f8c8c3028006d0cc4997943df8a86ee3f598bin0 -> 444 bytes
-rw-r--r--fuzzers/corpora/commit_graph/67799e79d33883510f85ae9705ab3932862128a2bin0 -> 991 bytes
-rw-r--r--fuzzers/corpora/commit_graph/67b475481e5a21351b49789874adbc988aefd64cbin0 -> 89 bytes
-rw-r--r--fuzzers/corpora/commit_graph/67e5a649967dee002d1c181e079748c404e29767bin0 -> 1116 bytes
-rw-r--r--fuzzers/corpora/commit_graph/687424a4a31a66a78d1637c680c9c10746741007bin0 -> 1573 bytes
-rw-r--r--fuzzers/corpora/commit_graph/68fa6dd52832657cb8dd7e1485d6fbafd4e93903bin0 -> 536 bytes
-rw-r--r--fuzzers/corpora/commit_graph/691696af1c042115f4d9f9b8e24f7b8c06ed189bbin0 -> 284 bytes
-rw-r--r--fuzzers/corpora/commit_graph/6a80152f9b1afa3a3080bf3f6aa48e84c2e18497bin0 -> 2116 bytes
-rw-r--r--fuzzers/corpora/commit_graph/6af27e4cf4c7bcce128a5949ee27fc73ab2cc71ebin0 -> 1620 bytes
-rw-r--r--fuzzers/corpora/commit_graph/6afd8f82d5639b774de0dfd418ae85322f4168ddbin0 -> 253 bytes
-rw-r--r--fuzzers/corpora/commit_graph/6c64a9e26e0e1480bb5e60b7044ca6ce17104a80bin0 -> 1043 bytes
-rw-r--r--fuzzers/corpora/commit_graph/6c850c17db130ca0152f7c75562fa191f7ef89debin0 -> 41 bytes
-rw-r--r--fuzzers/corpora/commit_graph/6c9afe4527371a2baf33c5e220e4ca21a3207f94bin0 -> 640 bytes
-rw-r--r--fuzzers/corpora/commit_graph/6ce3d40b0225923a7f4123a919b1c5d70841fad7bin0 -> 268 bytes
-rw-r--r--fuzzers/corpora/commit_graph/6cfd064aa6197813eb18f38df967ae4cdba9c6dabin0 -> 357 bytes
-rw-r--r--fuzzers/corpora/commit_graph/6e6675676c53bcddc870e06605d2432e3429f224bin0 -> 616 bytes
-rw-r--r--fuzzers/corpora/commit_graph/6e6e82579b7abae2b43d90448d3f2ead4dfcba78bin0 -> 2115 bytes
-rw-r--r--fuzzers/corpora/commit_graph/6f13d23c75a562eddefafe85e208e602832294e2bin0 -> 236 bytes
-rw-r--r--fuzzers/corpora/commit_graph/6fed59b0472927f5d2396d0ee4d7fd13579377cebin0 -> 751 bytes
-rw-r--r--fuzzers/corpora/commit_graph/71f7724196f9f8fcfe3ee0161a84893bb9c4ab11bin0 -> 795 bytes
-rw-r--r--fuzzers/corpora/commit_graph/7335ecb1d41e713bf3909adf5802b90e22bc1581bin0 -> 153 bytes
-rw-r--r--fuzzers/corpora/commit_graph/73afaa73175f461e1d19d5138e055c1649926dfebin0 -> 544 bytes
-rw-r--r--fuzzers/corpora/commit_graph/73e2fcb45c4df90d19091056b235e7a317631a62bin0 -> 150 bytes
-rw-r--r--fuzzers/corpora/commit_graph/741cb2d5ae11b0a9e0608b58ec7284d75129a1f2bin0 -> 69 bytes
-rw-r--r--fuzzers/corpora/commit_graph/7431bb0097a9bb52e1ceaaa8674a13cd3486a387bin0 -> 1223 bytes
-rw-r--r--fuzzers/corpora/commit_graph/7455b805995d0c96ac12f8a1c1264caaffcfac1cbin0 -> 742 bytes
-rw-r--r--fuzzers/corpora/commit_graph/74e39b8a82fc06f9ed8f83ea30545ddf6df66811bin0 -> 268 bytes
-rw-r--r--fuzzers/corpora/commit_graph/75d51e413d3e916560dc0c2ee5092d2f4972aec1bin0 -> 343 bytes
-rw-r--r--fuzzers/corpora/commit_graph/75e068964ea6beb7310a154d763de74a70071f48bin0 -> 453 bytes
-rw-r--r--fuzzers/corpora/commit_graph/763bf498dd847bd2b4af7b611199619bd428bea6bin0 -> 1188 bytes
-rw-r--r--fuzzers/corpora/commit_graph/77064ae04581a3c6d2a77158ef1a0b1e60db414abin0 -> 314 bytes
-rw-r--r--fuzzers/corpora/commit_graph/783bb14d68021061f592601607f40fe232ad17c4bin0 -> 210 bytes
-rw-r--r--fuzzers/corpora/commit_graph/7862814cb684310b54ef920b35403515efaba13cbin0 -> 276 bytes
-rw-r--r--fuzzers/corpora/commit_graph/791fd85b6ffb2429e9fa5ba29eebdce214ad88c7bin0 -> 436 bytes
-rw-r--r--fuzzers/corpora/commit_graph/79396d4f6142a53e26e14aa6ccb4afb4fd8fc580bin0 -> 216 bytes
-rw-r--r--fuzzers/corpora/commit_graph/79661b8e529e2182d5c612faba9f26e32a122b78bin0 -> 102 bytes
-rw-r--r--fuzzers/corpora/commit_graph/7969143acb3334bffac46c6dfd96362c81644191bin0 -> 418 bytes
-rw-r--r--fuzzers/corpora/commit_graph/79d84866dc8c067508c02516b65c0e48cf689b56bin0 -> 83 bytes
-rw-r--r--fuzzers/corpora/commit_graph/7b61f8f4a96e309bbe64ed82637fc81492a9652fbin0 -> 2710 bytes
-rw-r--r--fuzzers/corpora/commit_graph/7b8123f973edfb0f3cab027c0cd6b8efc7b11d6bbin0 -> 805 bytes
-rw-r--r--fuzzers/corpora/commit_graph/7b8dd3093efba07f7a4d3bab4b90b8f6e4f28bfbbin0 -> 1049 bytes
-rw-r--r--fuzzers/corpora/commit_graph/7cc771aab0f3be7730881a46d952ae0a06958201bin0 -> 740 bytes
-rw-r--r--fuzzers/corpora/commit_graph/7d177f4207de78d50df2493a3bc07f2cd578b363bin0 -> 82 bytes
-rw-r--r--fuzzers/corpora/commit_graph/7d2df075f3e73ea9809c31586c37ece0f568b7fabin0 -> 362 bytes
-rw-r--r--fuzzers/corpora/commit_graph/7d386e68e4c733a1fb11c0117f379fb4b9955fbb1
-rw-r--r--fuzzers/corpora/commit_graph/7e4260830352479d29310bd6e1022e19a68ffe76bin0 -> 412 bytes
-rw-r--r--fuzzers/corpora/commit_graph/7e4dfdae52be18cf95555c2eb1f54af7f69c6ddebin0 -> 217 bytes
-rw-r--r--fuzzers/corpora/commit_graph/7eafedf7e7f20e86ecdf9ba51febf8492bdbc1f1bin0 -> 593 bytes
-rw-r--r--fuzzers/corpora/commit_graph/7ef1829a378d66b1dd70a767729127a0dc5edcaebin0 -> 4 bytes
-rw-r--r--fuzzers/corpora/commit_graph/80b7d2b9d7e8c8fd7ae239b8d307b592f97ee000bin0 -> 2303 bytes
-rw-r--r--fuzzers/corpora/commit_graph/810f577ff5c1af7807a26226af912687558158cdbin0 -> 281 bytes
-rw-r--r--fuzzers/corpora/commit_graph/81603f1fe8d8e29005418d0fc9a9b33972366038bin0 -> 212 bytes
-rw-r--r--fuzzers/corpora/commit_graph/81c8b4d6884f954935fa4a8e828c4637db04b61abin0 -> 262 bytes
-rw-r--r--fuzzers/corpora/commit_graph/8226846e9b092561f85cc2956ab89d8cc1ae61e0bin0 -> 197 bytes
-rw-r--r--fuzzers/corpora/commit_graph/825cfceea434e2392cce161356e3cb5f81ec2b3abin0 -> 274 bytes
-rw-r--r--fuzzers/corpora/commit_graph/82603febce83d95adf68b85cabf15d43ca0c4ee9bin0 -> 532 bytes
-rw-r--r--fuzzers/corpora/commit_graph/827f0826cc4156e19b4c4938bec74e38de62fe9cbin0 -> 346 bytes
-rw-r--r--fuzzers/corpora/commit_graph/8486397ff8d1156249676c19b419a7758ff53f9abin0 -> 1361 bytes
-rw-r--r--fuzzers/corpora/commit_graph/84d99ee359bec1b8ee0f59e9bd96f1da062030b7bin0 -> 376 bytes
-rw-r--r--fuzzers/corpora/commit_graph/84e629bc7416039f1feb81fa9168d7c1ee3141c2bin0 -> 539 bytes
-rw-r--r--fuzzers/corpora/commit_graph/84e885752179076fb38739ca7bc4345716bee56abin0 -> 217 bytes
-rw-r--r--fuzzers/corpora/commit_graph/859ef05494c8070057810b5c20df00fc81f81cf5bin0 -> 2796 bytes
-rw-r--r--fuzzers/corpora/commit_graph/859fe592f33abc1d959c0e73ecd6cd4bffe23a97bin0 -> 345 bytes
-rw-r--r--fuzzers/corpora/commit_graph/860da5e8a468805b76a44b9ac99b4575be16ea15bin0 -> 338 bytes
-rw-r--r--fuzzers/corpora/commit_graph/865e415745cead02a826f058a5ee49099bdf9562bin0 -> 417 bytes
-rw-r--r--fuzzers/corpora/commit_graph/878bfce051a9c7462847d4e99b7e926dc821b7b8bin0 -> 95 bytes
-rw-r--r--fuzzers/corpora/commit_graph/880492e4dc7259577c227bb4f075d7165e875c29bin0 -> 150 bytes
-rw-r--r--fuzzers/corpora/commit_graph/88b7de1bd1c96454a1350286d115c0ee368511f9bin0 -> 120 bytes
-rw-r--r--fuzzers/corpora/commit_graph/896268e4a5775b7ce33923ac6daeb0810420c55bbin0 -> 3274 bytes
-rw-r--r--fuzzers/corpora/commit_graph/8978f8da89f9652878edabad164f5513ef508f27bin0 -> 244 bytes
-rw-r--r--fuzzers/corpora/commit_graph/89a6525b7db0e6ec211a484efd2880abef928d4ebin0 -> 152 bytes
-rw-r--r--fuzzers/corpora/commit_graph/8ae86cba2bba6664fc5eb97be8e9777b8825d823bin0 -> 506 bytes
-rw-r--r--fuzzers/corpora/commit_graph/8b845fbd2aa14e4f83c4dbc8b4b0b54d06482acdbin0 -> 273 bytes
-rw-r--r--fuzzers/corpora/commit_graph/8c4121e6ce5956cfa408b980f16d276f456374dcbin0 -> 152 bytes
-rw-r--r--fuzzers/corpora/commit_graph/8cb6a5b8ab41e3d27668d5735b5c09ff1f2eab65bin0 -> 403 bytes
-rw-r--r--fuzzers/corpora/commit_graph/8d80a70ffd362a89b88663e27f11e8ab69b70c1bbin0 -> 274 bytes
-rw-r--r--fuzzers/corpora/commit_graph/8db603c1720b3680047f831f2ea9862567a7cdc4bin0 -> 1009 bytes
-rw-r--r--fuzzers/corpora/commit_graph/8dd40b2d27c7dd4b986c35d87f826da287c09c4cbin0 -> 229 bytes
-rw-r--r--fuzzers/corpora/commit_graph/8e9d6e6408e5f708a1924e8370e687e2c202a4c4bin0 -> 519 bytes
-rw-r--r--fuzzers/corpora/commit_graph/8f2dff1a30ee28e5985cb9379828aea5658d5849bin0 -> 222 bytes
-rw-r--r--fuzzers/corpora/commit_graph/8f7d18cdd6e605b85784ada14571fd5e5a184f2abin0 -> 1125 bytes
-rw-r--r--fuzzers/corpora/commit_graph/903ae52f0ac9af8348038b12f9259741b0de42f1bin0 -> 1198 bytes
-rw-r--r--fuzzers/corpora/commit_graph/9119e331f59e9337d419739c324f49d1bd62c8bfbin0 -> 3311 bytes
-rw-r--r--fuzzers/corpora/commit_graph/91d54d03b0917314ea1d67a70690df9247dd08d2bin0 -> 536 bytes
-rw-r--r--fuzzers/corpora/commit_graph/922da3b96725bfd0e3f6ce119f1e2249d53f9086bin0 -> 542 bytes
-rw-r--r--fuzzers/corpora/commit_graph/9277561e0524cccba2f851970b0d88ec4f4d3f5ebin0 -> 1304 bytes
-rw-r--r--fuzzers/corpora/commit_graph/92a4d571804026b7bbe957396185e079e756b894bin0 -> 334 bytes
-rw-r--r--fuzzers/corpora/commit_graph/931224cc80168fd362a360d99bab813ed7bbf8cebin0 -> 127 bytes
-rw-r--r--fuzzers/corpora/commit_graph/936ea5dad3bf023c552aa0bbeea8f7f66a11612cbin0 -> 454 bytes
-rw-r--r--fuzzers/corpora/commit_graph/93aa4e0b1864933dce0abc0df69fe3d261f117f2bin0 -> 146 bytes
-rw-r--r--fuzzers/corpora/commit_graph/93d5b084965cf1b09085c4079a972e25207b3659bin0 -> 159 bytes
-rw-r--r--fuzzers/corpora/commit_graph/9443fd3468bcc0bc3ff8dfe765225f045ab43d0abin0 -> 225 bytes
-rw-r--r--fuzzers/corpora/commit_graph/9624c26cefb5804b7906147d262e81ee4000b6d6bin0 -> 2327 bytes
-rw-r--r--fuzzers/corpora/commit_graph/9890933a73f39208627bd36e2fe88a6d54343a74bin0 -> 186 bytes
-rw-r--r--fuzzers/corpora/commit_graph/989dad0448e79af10040d5080f74eba2b8a401babin0 -> 191 bytes
-rw-r--r--fuzzers/corpora/commit_graph/98ed4808b4a8da66a91fcea1be63be6371a7c7acbin0 -> 214 bytes
-rw-r--r--fuzzers/corpora/commit_graph/9928e516b85e22fbad58d562d3b7e814d9ce812dbin0 -> 213 bytes
-rw-r--r--fuzzers/corpora/commit_graph/994c7cc5599252b5628d89cd0ba4b5574d32bf00bin0 -> 3091 bytes
-rw-r--r--fuzzers/corpora/commit_graph/99c8557c2a02ea030de42869af42c1f7c77114dbbin0 -> 86 bytes
-rw-r--r--fuzzers/corpora/commit_graph/9a14c867272f102b84efdba73662d318c3e51cfebin0 -> 2161 bytes
-rw-r--r--fuzzers/corpora/commit_graph/9a6f158c176d4a1982d541be2bc27a8afba4ea57bin0 -> 99 bytes
-rw-r--r--fuzzers/corpora/commit_graph/9aa4af603192823a2fdc53d95ed36896bc3309b2bin0 -> 371 bytes
-rw-r--r--fuzzers/corpora/commit_graph/9b40c2190123cec66af3b37212f6c567869efda3bin0 -> 152 bytes
-rw-r--r--fuzzers/corpora/commit_graph/9b6268c11d78c35db5164f1346905e602b6a49febin0 -> 989 bytes
-rw-r--r--fuzzers/corpora/commit_graph/9c6883ba5cedb7d711b12733d66ef1a1156dd0afbin0 -> 1988 bytes
-rw-r--r--fuzzers/corpora/commit_graph/9c85c90f44b454ce0d52882c447f5ecb8d303634bin0 -> 348 bytes
-rw-r--r--fuzzers/corpora/commit_graph/9cb7a2e89ec636da3fd41ecc49ebe25e5344e2c6bin0 -> 477 bytes
-rw-r--r--fuzzers/corpora/commit_graph/9d912dc5a3497e4b5b40b37202fc0ffbf5263666bin0 -> 3848 bytes
-rw-r--r--fuzzers/corpora/commit_graph/9dcbafe8c5345194ee0ce7eb4f6efaeb55543626bin0 -> 3972 bytes
-rw-r--r--fuzzers/corpora/commit_graph/9f4b0f3d2d25e6405ba6093f24d0605327711573bin0 -> 452 bytes
-rw-r--r--fuzzers/corpora/commit_graph/a047bf683239fa208dbac09424b105820ac23f43bin0 -> 539 bytes
-rw-r--r--fuzzers/corpora/commit_graph/a1379dcd89ef5e73eabbfcc395113e3636e0ae09bin0 -> 134 bytes
-rw-r--r--fuzzers/corpora/commit_graph/a38c7ef56adabd0916abac514154b1f362d40434bin0 -> 1704 bytes
-rw-r--r--fuzzers/corpora/commit_graph/a38ec6ad4a8466b4feb88e67b16524e8f3feac64bin0 -> 487 bytes
-rw-r--r--fuzzers/corpora/commit_graph/a3fdea21020268b3b2409c1115d50697d9ae8f8cbin0 -> 257 bytes
-rw-r--r--fuzzers/corpora/commit_graph/a45f1987a444b2c27e90fc1477e8b0815f75383fbin0 -> 607 bytes
-rw-r--r--fuzzers/corpora/commit_graph/a4682958fb7029384c0a01a4a1356ac6f2f44fe1bin0 -> 78 bytes
-rw-r--r--fuzzers/corpora/commit_graph/a4de41561725960d6f48f210a4fb74d527f7b0c2bin0 -> 965 bytes
-rw-r--r--fuzzers/corpora/commit_graph/a5935f34435ecdd6587ad4f77b20d479d3387dbebin0 -> 279 bytes
-rw-r--r--fuzzers/corpora/commit_graph/a5b394beb2b1d463ad80924a8c8c70584bf5c629bin0 -> 234 bytes
-rw-r--r--fuzzers/corpora/commit_graph/a62bc806f8c98ba7986243c2185a0548a8dd57efbin0 -> 118 bytes
-rw-r--r--fuzzers/corpora/commit_graph/a7013e97948893e0118c686c06e332cc611bea7ebin0 -> 214 bytes
-rw-r--r--fuzzers/corpora/commit_graph/a74f5df8c7f25c37c15c0f74ed50019d17338225bin0 -> 539 bytes
-rw-r--r--fuzzers/corpora/commit_graph/a7ab3559fb3da3f027e67091116253f3bdfd7828bin0 -> 1626 bytes
-rw-r--r--fuzzers/corpora/commit_graph/a845c8258a02022d447ea9249788b345f5504648bin0 -> 144 bytes
-rw-r--r--fuzzers/corpora/commit_graph/a8d3e026e2393587eb170afb32e94ff0e1f8a8bebin0 -> 353 bytes
-rw-r--r--fuzzers/corpora/commit_graph/a8d547e41ee21e163e65cf0a186d469dfa50ec19bin0 -> 2448 bytes
-rw-r--r--fuzzers/corpora/commit_graph/a8fa22521dd6813e595cc0a9586ee71fff305fe2bin0 -> 127 bytes
-rw-r--r--fuzzers/corpora/commit_graph/a9969442d585d9a53259c71c73b095701280eac5bin0 -> 160 bytes
-rw-r--r--fuzzers/corpora/commit_graph/a99789d0ce2d7b937aaa8afa3cfc0f4ccd7be95fbin0 -> 150 bytes
-rw-r--r--fuzzers/corpora/commit_graph/aaca30ee3ab38edfa2b061fcbcbca0c0ea657f15bin0 -> 108 bytes
-rw-r--r--fuzzers/corpora/commit_graph/aacdec3f05e98eb6eedddb9c6edb968e1a63c551bin0 -> 3654 bytes
-rw-r--r--fuzzers/corpora/commit_graph/aadd85127241b94a41d02d9e9699e3e9773de1c9bin0 -> 539 bytes
-rw-r--r--fuzzers/corpora/commit_graph/ab8ad126702803d21dbafc85713bbee7f25f36e5bin0 -> 3031 bytes
-rw-r--r--fuzzers/corpora/commit_graph/ac26f9afd599ff6f33396c2e02130654f3e2390cbin0 -> 324 bytes
-rw-r--r--fuzzers/corpora/commit_graph/ac8b129e4756fda0c50c9dd0eb13e34c7b41ce8e1
-rw-r--r--fuzzers/corpora/commit_graph/aceaf3b72c2627dd3dd065974b854150681c093fbin0 -> 793 bytes
-rw-r--r--fuzzers/corpora/commit_graph/ad1fcdc3bf806392e754a902eba9edd3b344c31fbin0 -> 98 bytes
-rw-r--r--fuzzers/corpora/commit_graph/ad8c80e532482f9dfbfbb7c0d447f1f4e592bf72bin0 -> 457 bytes
-rw-r--r--fuzzers/corpora/commit_graph/add92b71bf897da2f71f691e6abcb6d02cb8e99fbin0 -> 231 bytes
-rw-r--r--fuzzers/corpora/commit_graph/aeb8ccf6d82be9236c9e689e1580d043bd701eb0bin0 -> 3419 bytes
-rw-r--r--fuzzers/corpora/commit_graph/af1a827aedbf674fff2bdeb5589554eec62787abbin0 -> 215 bytes
-rw-r--r--fuzzers/corpora/commit_graph/afaab9a75414d231176ad4582b6f8d81b5dbedb3bin0 -> 2686 bytes
-rw-r--r--fuzzers/corpora/commit_graph/afc12c4ebed1f3ab962d7dcef110b5328b1e24c3bin0 -> 673 bytes
-rw-r--r--fuzzers/corpora/commit_graph/b0044f3744cf019658d668a33f8d1e53ef8bd6cebin0 -> 406 bytes
-rw-r--r--fuzzers/corpora/commit_graph/b06adc81a4e1cdcda3786970ca07ed9dee0b6401bin0 -> 344 bytes
-rw-r--r--fuzzers/corpora/commit_graph/b139802a1cc90fd5b86cae044c221361892c688dbin0 -> 3071 bytes
-rw-r--r--fuzzers/corpora/commit_graph/b1b8f251542db01bdb01be3b6d5b117b07db1834bin0 -> 790 bytes
-rw-r--r--fuzzers/corpora/commit_graph/b1b9af93f84ed6861b9c0ade39980e89ef828c8fbin0 -> 213 bytes
-rw-r--r--fuzzers/corpora/commit_graph/b2eae68035cafd4077f6a4c3e4e961fdc1e8122bbin0 -> 1878 bytes
-rw-r--r--fuzzers/corpora/commit_graph/b32897a6aedaa8c5a6e656dd808bafabc4ee5608bin0 -> 343 bytes
-rw-r--r--fuzzers/corpora/commit_graph/b376e4fc517297f92ac1713803ae3b60d5ebbe43bin0 -> 32 bytes
-rw-r--r--fuzzers/corpora/commit_graph/b3fd100b139cfbffaad68aacf7d462861e9dca35bin0 -> 136 bytes
-rw-r--r--fuzzers/corpora/commit_graph/b40808ca955faab4829811bced1cccb2ab58ea58bin0 -> 87 bytes
-rw-r--r--fuzzers/corpora/commit_graph/b43daf9f87a514bce74af3e5a39284c69c4e7011bin0 -> 187 bytes
-rw-r--r--fuzzers/corpora/commit_graph/b477da07f3e5796ff4a98c8a5bdb0e4a634954bfbin0 -> 342 bytes
-rw-r--r--fuzzers/corpora/commit_graph/b4a2ef09cf59ca5ccf810a6f001cce710cc02f6bbin0 -> 1248 bytes
-rw-r--r--fuzzers/corpora/commit_graph/b4b75e588cb83430c502a34ec3dcfaf774a00359bin0 -> 1976 bytes
-rw-r--r--fuzzers/corpora/commit_graph/b4ce98acd2b288b6cfc00461e2e15e0f8004030cbin0 -> 1246 bytes
-rw-r--r--fuzzers/corpora/commit_graph/b75563f30f7e4fb369d2449b723ee8b282d03effbin0 -> 1989 bytes
-rw-r--r--fuzzers/corpora/commit_graph/b7a0a820afa7057081de186728d0d887131d9314bin0 -> 3572 bytes
-rw-r--r--fuzzers/corpora/commit_graph/b7e880446146c735a3f820fb93969c8c172c2fb5bin0 -> 275 bytes
-rw-r--r--fuzzers/corpora/commit_graph/b833073d3006e7cbac03c494603a9b75e7b2a723bin0 -> 150 bytes
-rw-r--r--fuzzers/corpora/commit_graph/b89459c1fb6fc918db4c81a32a75ee66217f9ab8bin0 -> 101 bytes
-rw-r--r--fuzzers/corpora/commit_graph/b8aab6c9b2c706f8df0ff695ff94969171f9c807bin0 -> 136 bytes
-rw-r--r--fuzzers/corpora/commit_graph/b9751182a36acb79b77585e1e379857a530e95c8bin0 -> 1247 bytes
-rw-r--r--fuzzers/corpora/commit_graph/b9ddb239b5a2c1348d972ec70a08507c35ba4432bin0 -> 247 bytes
-rw-r--r--fuzzers/corpora/commit_graph/ba8f573256a0fbb95c5626f399ebc3ef50bbd826bin0 -> 69 bytes
-rw-r--r--fuzzers/corpora/commit_graph/bc165749042d5425c5d6d4e29b17769a2315a80dbin0 -> 344 bytes
-rw-r--r--fuzzers/corpora/commit_graph/bc910bd349319e1ed44d7c7266e3ac99cc29ecc6bin0 -> 410 bytes
-rw-r--r--fuzzers/corpora/commit_graph/bc97b1d4f57eb7770bc3983e2d57c8c01b21d29ebin0 -> 942 bytes
-rw-r--r--fuzzers/corpora/commit_graph/bd06f768e35ded4437cb88e2bc0ddd0bea3fa84cbin0 -> 68 bytes
-rw-r--r--fuzzers/corpora/commit_graph/bd702faff9725a7a1957fd0f85cc52799f37b682bin0 -> 411 bytes
-rw-r--r--fuzzers/corpora/commit_graph/bee4464861e1cae3cfdd5fbcb340efbf02e8d8cabin0 -> 1840 bytes
-rw-r--r--fuzzers/corpora/commit_graph/bf7ad994b098ec85d62683a16e067635e21a8af5bin0 -> 2455 bytes
-rw-r--r--fuzzers/corpora/commit_graph/c054fc89ed72101dec861668ff1738ef85b728b9bin0 -> 114 bytes
-rw-r--r--fuzzers/corpora/commit_graph/c06752415ac037fefe5172dc7245cd7c49ca7fcabin0 -> 280 bytes
-rw-r--r--fuzzers/corpora/commit_graph/c0c8b54354d172a0be751e3e9b80be961bb15ddbbin0 -> 2211 bytes
-rw-r--r--fuzzers/corpora/commit_graph/c0e7ca9b5b4d0e72d23d7dc9e9d1f2463a17a20dbin0 -> 282 bytes
-rw-r--r--fuzzers/corpora/commit_graph/c13576a29c98bee02aa47f646f5f170f9b7d83f9bin0 -> 1523 bytes
-rw-r--r--fuzzers/corpora/commit_graph/c14edf1d34f40b3cc74772c81ebe5d72172cc662bin0 -> 956 bytes
-rw-r--r--fuzzers/corpora/commit_graph/c2789364cb35d111f08f924d0d7550ea9785c61ebin0 -> 2130 bytes
-rw-r--r--fuzzers/corpora/commit_graph/c2d8b07acb13e43a89b6c4afb3ecb9817dd4a8e9bin0 -> 2088 bytes
-rw-r--r--fuzzers/corpora/commit_graph/c36ed796c1bf839668db8fc3475a2ffb32ad8cebbin0 -> 1708 bytes
-rw-r--r--fuzzers/corpora/commit_graph/c41ec9dd94427423e4704721e7f21eae0c44ef20bin0 -> 473 bytes
-rw-r--r--fuzzers/corpora/commit_graph/c42c544fa9dbb1264b39bf920b40985384db1d16bin0 -> 1990 bytes
-rw-r--r--fuzzers/corpora/commit_graph/c45ec3f594abc15de0a8cc3ad748ba23cb34ec64bin0 -> 81 bytes
-rw-r--r--fuzzers/corpora/commit_graph/c49004d980961f288616a4eb9ebf68123fd68ffabin0 -> 129 bytes
-rw-r--r--fuzzers/corpora/commit_graph/c4c3c3c8df24adf505127627b3090116de78d9a6bin0 -> 151 bytes
-rw-r--r--fuzzers/corpora/commit_graph/c5c1921293af4a5953cb386092694042715fcfb3bin0 -> 89 bytes
-rw-r--r--fuzzers/corpora/commit_graph/c615caad21cd8a754fcb2008420234c5511c62b7bin0 -> 2534 bytes
-rw-r--r--fuzzers/corpora/commit_graph/c6a9ee3f8fdc42566c4799db3912a83c8c438d7fbin0 -> 396 bytes
-rw-r--r--fuzzers/corpora/commit_graph/c6b661e976282051285b913b3728383f36103ef8bin0 -> 792 bytes
-rw-r--r--fuzzers/corpora/commit_graph/c716ba47f810c238fe7bda1fbdc7b1ccc34e98481
-rw-r--r--fuzzers/corpora/commit_graph/c85b2fa4421302e2fa333a9e33d59a882aa04f4fbin0 -> 2112 bytes
-rw-r--r--fuzzers/corpora/commit_graph/c871d135f2d3117b326688355bc0fa6f26d56cd6bin0 -> 83 bytes
-rw-r--r--fuzzers/corpora/commit_graph/c915b02265a27d185a8b028305f082ddb3ebd704bin0 -> 201 bytes
-rw-r--r--fuzzers/corpora/commit_graph/c952d38b3e642db4795d7f954b85f4f6d2a041aabin0 -> 107 bytes
-rw-r--r--fuzzers/corpora/commit_graph/c98ee52065736c4172f6ee0c31977bf1b560d685bin0 -> 471 bytes
-rw-r--r--fuzzers/corpora/commit_graph/c99b183a2cd0dd8a4c1a141cc6eebb0311501fa51
-rw-r--r--fuzzers/corpora/commit_graph/ca0cd26baff2f2c0759e619800ebbe7314d2bb95bin0 -> 262 bytes
-rw-r--r--fuzzers/corpora/commit_graph/ca3e0d745c35d7cceb0f6e3f8a709eb658b7e5a8bin0 -> 86 bytes
-rw-r--r--fuzzers/corpora/commit_graph/cac667320e99e93a796bb89842de4675735eb4a4bin0 -> 353 bytes
-rw-r--r--fuzzers/corpora/commit_graph/cb41b00e9db33a07e27b3ee05d3bbecaf853b963bin0 -> 2238 bytes
-rw-r--r--fuzzers/corpora/commit_graph/cbdbd3f320eee627097778f15b9fb2c1dc2bd15fbin0 -> 2318 bytes
-rw-r--r--fuzzers/corpora/commit_graph/cc7f114000c83abb2ab17f0deab6dcfc2acde7f5bin0 -> 806 bytes
-rw-r--r--fuzzers/corpora/commit_graph/cc9bb93a6b7a1362a15f04898845dbe1447ec382bin0 -> 392 bytes
-rw-r--r--fuzzers/corpora/commit_graph/cce7355f826bbcf3955394596d358abc7df6fe6fbin0 -> 2458 bytes
-rw-r--r--fuzzers/corpora/commit_graph/cceff2878a558166fb5bf2a0354c1be31dcc4e21bin0 -> 2605 bytes
-rw-r--r--fuzzers/corpora/commit_graph/cd96909f3ded7aa54bb2ffd2f2f47f8acc6f99e2bin0 -> 81 bytes
-rw-r--r--fuzzers/corpora/commit_graph/cee9f69d7d1a227833fba127a529ea2a10341da3bin0 -> 68 bytes
-rw-r--r--fuzzers/corpora/commit_graph/d064f27a3109afde629165432f78f389da73ff07bin0 -> 32 bytes
-rw-r--r--fuzzers/corpora/commit_graph/d07e3094f02b0c0e3bab370684c2d8c5634224d5bin0 -> 1178 bytes
-rw-r--r--fuzzers/corpora/commit_graph/d0ba3413d5706de17de64824d78233d48c6efbecbin0 -> 97 bytes
-rw-r--r--fuzzers/corpora/commit_graph/d136511364a74973b009f2be9b021d4122f71a6cbin0 -> 655 bytes
-rw-r--r--fuzzers/corpora/commit_graph/d1d215c40bcc8dd4ce02b0c0621e90b183b40b3ebin0 -> 191 bytes
-rw-r--r--fuzzers/corpora/commit_graph/d1e35b137b2027b61def408f3f3c8cf9bcab274ebin0 -> 1848 bytes
-rw-r--r--fuzzers/corpora/commit_graph/d349d137e57fb1a60ab8babd20e2acedc7a9042ebin0 -> 1989 bytes
-rw-r--r--fuzzers/corpora/commit_graph/d3714ec4d3acc6262295b0fc99c6ba699f5bfe65bin0 -> 594 bytes
-rw-r--r--fuzzers/corpora/commit_graph/d419df696512216074f1c6b17ea1dfc81c0e6e20bin0 -> 1251 bytes
-rw-r--r--fuzzers/corpora/commit_graph/d49ad4fdafac251ceec32481826228c1698360aabin0 -> 1001 bytes
-rw-r--r--fuzzers/corpora/commit_graph/d4f85ba549c87ccaba59971a25da7e07b57c9f4ebin0 -> 538 bytes
-rw-r--r--fuzzers/corpora/commit_graph/d51ade0715bcea7decee2a045934599a10c1b07abin0 -> 227 bytes
-rw-r--r--fuzzers/corpora/commit_graph/d5447fb72c97462a3f47c8b2d55deb0afaa225f8bin0 -> 1113 bytes
-rw-r--r--fuzzers/corpora/commit_graph/d6611a91c29291872ed2932455cb15ddb3801323bin0 -> 83 bytes
-rw-r--r--fuzzers/corpora/commit_graph/d676f5e7efd6de6f2e1773231479471d2bba7261bin0 -> 37 bytes
-rw-r--r--fuzzers/corpora/commit_graph/d6a21eaa08a957d8f428192e193c2508fca2c218bin0 -> 179 bytes
-rw-r--r--fuzzers/corpora/commit_graph/d778052a29539344a9e3144e262e68df9628ebdebin0 -> 330 bytes
-rw-r--r--fuzzers/corpora/commit_graph/d884f6944adfff7cb41728062bf91cac5cdacfc9bin0 -> 466 bytes
-rw-r--r--fuzzers/corpora/commit_graph/d89aae18d8e320bbae55eaae6a0514d7e005a883bin0 -> 2410 bytes
-rw-r--r--fuzzers/corpora/commit_graph/d926fde818c63f7b34f38c9f018bc833bc0bf7e1bin0 -> 409 bytes
-rw-r--r--fuzzers/corpora/commit_graph/d9d542d7c56774143cb6362e5a63739055469349bin0 -> 2801 bytes
-rw-r--r--fuzzers/corpora/commit_graph/da99bc9ce5b831f132dfb2eb11b8537e5cccfcd4bin0 -> 120 bytes
-rw-r--r--fuzzers/corpora/commit_graph/dabff2729fa69ab507fb00b7392aee1262056a29bin0 -> 296 bytes
-rw-r--r--fuzzers/corpora/commit_graph/dac4f4b91e33847bcedf7c66ef6e4ad0181e8ad8bin0 -> 153 bytes
-rw-r--r--fuzzers/corpora/commit_graph/db10ff6d01c7a66aa1823b9f99193590ddce99c6bin0 -> 252 bytes
-rw-r--r--fuzzers/corpora/commit_graph/dbbda2208fa688a5275dda0d304630db01ca081dbin0 -> 284 bytes
-rw-r--r--fuzzers/corpora/commit_graph/dc47c5037be68a2747ff8a9fa450e1078a5ac5a5bin0 -> 209 bytes
-rw-r--r--fuzzers/corpora/commit_graph/dc760f136b123e38677aec72853e3365f08010fcbin0 -> 602 bytes
-rw-r--r--fuzzers/corpora/commit_graph/dca41b901bf1612d4197e6a450366a00ac036ec3bin0 -> 346 bytes
-rw-r--r--fuzzers/corpora/commit_graph/dca62f21fce50d1c8c51b82e0d7eeedc6746e652bin0 -> 169 bytes
-rw-r--r--fuzzers/corpora/commit_graph/dcc7e6c444f95b10d634b1137413824e2cd68f62bin0 -> 152 bytes
-rw-r--r--fuzzers/corpora/commit_graph/dcf4b6addda69040f792c9b860ade2af0b77a14cbin0 -> 1888 bytes
-rw-r--r--fuzzers/corpora/commit_graph/dd6178166ac1eed82d132fea491bcda0d953227cbin0 -> 566 bytes
-rw-r--r--fuzzers/corpora/commit_graph/ddbd5d3074323ccd7cd70bf5de5a2f30de977d99bin0 -> 130 bytes
-rw-r--r--fuzzers/corpora/commit_graph/ddd8ec5632bf1b8153d03a4537d3d76517c497d5bin0 -> 150 bytes
-rw-r--r--fuzzers/corpora/commit_graph/de7a56f36e10d7b9ff43160b1cea3e76b24386d1bin0 -> 146 bytes
-rw-r--r--fuzzers/corpora/commit_graph/defa60aa46ea5a47c09b6962b4e4296ef1bcad92bin0 -> 80 bytes
-rw-r--r--fuzzers/corpora/commit_graph/e0ae419425207832518d66c0ef35d11cbdc20361bin0 -> 1691 bytes
-rw-r--r--fuzzers/corpora/commit_graph/e0f519accbf15bc57a1bf1d7cc46d2a0b07a67f5bin0 -> 214 bytes
-rw-r--r--fuzzers/corpora/commit_graph/e128eff8ca7572d9bb0bfc84f64d79c52afc2c67bin0 -> 536 bytes
-rw-r--r--fuzzers/corpora/commit_graph/e17fdc21ae03243bd1d31bb6301b4187cab6fe47bin0 -> 1617 bytes
-rw-r--r--fuzzers/corpora/commit_graph/e340ace35a2db7f89d6aa21cc1300766a74be4e1bin0 -> 536 bytes
-rw-r--r--fuzzers/corpora/commit_graph/e36dfc11bcaab1e42df13924a2d7da024684db2ebin0 -> 353 bytes
-rw-r--r--fuzzers/corpora/commit_graph/e39e0c87ac5ce0b78c89ae2df84226baba666372bin0 -> 864 bytes
-rw-r--r--fuzzers/corpora/commit_graph/e46b4666c6bfcd6f589ec3617a48cce9c968e833bin0 -> 406 bytes
-rw-r--r--fuzzers/corpora/commit_graph/e57219555e11f9221d3166d5029ed2ad92300608bin0 -> 101 bytes
-rw-r--r--fuzzers/corpora/commit_graph/e58ce590c2454e7ebe18e0a31a943b0b754fbd13bin0 -> 2317 bytes
-rw-r--r--fuzzers/corpora/commit_graph/e595f8fef5c8014cb0867978c6580301078ca0d9bin0 -> 2474 bytes
-rw-r--r--fuzzers/corpora/commit_graph/e5b76398f60628e879328d7009b9fa89feea14cbbin0 -> 452 bytes
-rw-r--r--fuzzers/corpora/commit_graph/e5cec0217eea93b18a59d76b0aed6b46b13fa6a9bin0 -> 921 bytes
-rw-r--r--fuzzers/corpora/commit_graph/e637b4e0b47d0d6cd870502e6a2d6a53bf917f73bin0 -> 1782 bytes
-rw-r--r--fuzzers/corpora/commit_graph/e7a6cb6e5a1552837fdbee9025fc48a9373f8564bin0 -> 1370 bytes
-rw-r--r--fuzzers/corpora/commit_graph/e7f57c48016e1180c9af95acd34470881f10bd06bin0 -> 90 bytes
-rw-r--r--fuzzers/corpora/commit_graph/e8253c668bfe37df5c5ada3226860cee74fb33a2bin0 -> 218 bytes
-rw-r--r--fuzzers/corpora/commit_graph/e8f9981443c34ece02bca3c66130f3429d7b3375bin0 -> 474 bytes
-rw-r--r--fuzzers/corpora/commit_graph/e91ed5416bbcd1b03803197b99c08f42c9869139bin0 -> 537 bytes
-rw-r--r--fuzzers/corpora/commit_graph/e94201cfa88df7b198abd3abae9007a6780b52a7bin0 -> 345 bytes
-rw-r--r--fuzzers/corpora/commit_graph/e967bbd6a0d251ae62c9c38b784271d707f792c0bin0 -> 138 bytes
-rw-r--r--fuzzers/corpora/commit_graph/ea01737ceed783b3e0f66d9d0c409cb496c1d526bin0 -> 86 bytes
-rw-r--r--fuzzers/corpora/commit_graph/ea40f7879a58d1e52a46404c761f76a949e14a31bin0 -> 69 bytes
-rw-r--r--fuzzers/corpora/commit_graph/ea5ad04a54f95963baea1f47845847626e08dd55bin0 -> 211 bytes
-rw-r--r--fuzzers/corpora/commit_graph/ea608a401f54b0ca70e42b897f0c8ce6efdbc0efbin0 -> 404 bytes
-rw-r--r--fuzzers/corpora/commit_graph/eb8700d6b3728e6e70c2a0fe504543771639f2b6bin0 -> 1989 bytes
-rw-r--r--fuzzers/corpora/commit_graph/ec1f271b04c322353865f4819153d46df7def873bin0 -> 1754 bytes
-rw-r--r--fuzzers/corpora/commit_graph/ee215536e7f0cfbd07b53dd65c5af9a604a01830bin0 -> 948 bytes
-rw-r--r--fuzzers/corpora/commit_graph/ee4d4393d7d79b755f85ef5bf8f6e3d743bfa258bin0 -> 468 bytes
-rw-r--r--fuzzers/corpora/commit_graph/ee8099331b2c392e7e036ffcd4a9b36ec2c2082dbin0 -> 1908 bytes
-rw-r--r--fuzzers/corpora/commit_graph/eede9da76db25513f8347f972e170102831de91abin0 -> 80 bytes
-rw-r--r--fuzzers/corpora/commit_graph/ef707cdeaa9548b6c820f769c1d8ad607b3c4514bin0 -> 119 bytes
-rw-r--r--fuzzers/corpora/commit_graph/ef98609d8196dc158365dfcbbc47e3d1699c50c2bin0 -> 1944 bytes
-rw-r--r--fuzzers/corpora/commit_graph/efa38b4269f978f3714b44b501831bea678244e0bin0 -> 357 bytes
-rw-r--r--fuzzers/corpora/commit_graph/efba428e29811d233720ccaaf41966a309312a29bin0 -> 86 bytes
-rw-r--r--fuzzers/corpora/commit_graph/efd514f056d8d83498b4724249c4623560e0390dbin0 -> 177 bytes
-rw-r--r--fuzzers/corpora/commit_graph/f00e449ba67ef15e7f29df1e6948c28155d72baabin0 -> 3505 bytes
-rw-r--r--fuzzers/corpora/commit_graph/f0a83929d588466051dced6eae0c387db307d646bin0 -> 944 bytes
-rw-r--r--fuzzers/corpora/commit_graph/f0e53b72e5d69467e7c014474028ea734f4fcb26bin0 -> 5 bytes
-rw-r--r--fuzzers/corpora/commit_graph/f186265b3f10f4383f4174e9fb74f0a0cdfa3fcabin0 -> 95 bytes
-rw-r--r--fuzzers/corpora/commit_graph/f18932fcce5a9db5d6c8f59d622eabc25e255e12bin0 -> 100 bytes
-rw-r--r--fuzzers/corpora/commit_graph/f2ea163bddb95d67597e2a747779ebf4651cb2a9bin0 -> 417 bytes
-rw-r--r--fuzzers/corpora/commit_graph/f2f7d48a6d86143ecb4969808d634163576065b1bin0 -> 2677 bytes
-rw-r--r--fuzzers/corpora/commit_graph/f34a833faf2b0dcbae8aaad142c76c7c7e534e99bin0 -> 71 bytes
-rw-r--r--fuzzers/corpora/commit_graph/f5c044ce01645c069334698fb8c4750e44835912bin0 -> 2625 bytes
-rw-r--r--fuzzers/corpora/commit_graph/f680112645c2502f0612e9d017bbb50cb28affbfbin0 -> 580 bytes
-rw-r--r--fuzzers/corpora/commit_graph/f6b778d1b34415a7715905f54968c8b6eb057912bin0 -> 296 bytes
-rw-r--r--fuzzers/corpora/commit_graph/f6ca6a62dc885c6b2a4b40c4aa1a7cb8118e30bbbin0 -> 355 bytes
-rw-r--r--fuzzers/corpora/commit_graph/f733a8770c23fde182d2fef7e0d96e67244274d5bin0 -> 90 bytes
-rw-r--r--fuzzers/corpora/commit_graph/f8529ddf17d4505c0932c3d40abe33cbfd8c6f22bin0 -> 453 bytes
-rw-r--r--fuzzers/corpora/commit_graph/f96f8419a3fc3719ae86d64e1147e7b7f66a2470bin0 -> 2139 bytes
-rw-r--r--fuzzers/corpora/commit_graph/fae241a6c87af37781a3b49e534b7ddb6636eda8bin0 -> 803 bytes
-rw-r--r--fuzzers/corpora/commit_graph/faf8817a04b77c6a976ab0a3d1e905f79bb7f799bin0 -> 1757 bytes
-rw-r--r--fuzzers/corpora/commit_graph/fb3e769019fb25d384d4be9d38e4cbce00a6adbcbin0 -> 1790 bytes
-rw-r--r--fuzzers/corpora/commit_graph/fb9b4b2a46f1c65076340a7bd03b076eb101b760bin0 -> 144 bytes
-rw-r--r--fuzzers/corpora/commit_graph/fca9b0a398832c9ba02cdc811f625b97d5beb18ebin0 -> 351 bytes
-rw-r--r--fuzzers/corpora/commit_graph/fcb1b42c706e61245d5e86f708be777ae63f2772bin0 -> 469 bytes
-rw-r--r--fuzzers/corpora/commit_graph/fd6c463e7c30b0e51198c0d1ebbea25f20145e3fbin0 -> 357 bytes
-rw-r--r--fuzzers/corpora/commit_graph/fdcbaa49097ad120c6d7709b29d5b65b8cf8e719bin0 -> 613 bytes
-rw-r--r--fuzzers/corpora/commit_graph/fe46775b28a2923b8770b44381552a8a1560d875bin0 -> 154 bytes
-rw-r--r--fuzzers/corpora/commit_graph/ff04441135ef3308fec2687cf688069c6df8aa31bin0 -> 601 bytes
-rw-r--r--fuzzers/corpora/config_file/git2.dat11
-rw-r--r--fuzzers/corpora/download_refs/clone.datbin0 -> 632 bytes
-rw-r--r--fuzzers/corpora/midx/037cbbe0dc03807dd9d9e8629f1712d7df34ee18bin0 -> 45 bytes
-rw-r--r--fuzzers/corpora/midx/039ee34fef8f323ed618a10abc0109df123d0cb5bin0 -> 1062 bytes
-rw-r--r--fuzzers/corpora/midx/054ee2c82bdb6a170106eb5d35f21bde2119d5841
-rw-r--r--fuzzers/corpora/midx/055ca4cbc961ebf5fd5c922b4f73880d3fbfe39dbin0 -> 1880 bytes
-rw-r--r--fuzzers/corpora/midx/05c4e5eb1b97bc9b6973921fcb30d4c5e2eb79e4bin0 -> 227 bytes
-rw-r--r--fuzzers/corpora/midx/0672eeda541a191cfc68d521a3c7ac0aac4057a6bin0 -> 2600 bytes
-rw-r--r--fuzzers/corpora/midx/06a58d1bd5562a668ebf01ef297fd774e0e587a6bin0 -> 143 bytes
-rw-r--r--fuzzers/corpora/midx/06bf7c2461ae1049030f31b83ae76babfcc20c83bin0 -> 26 bytes
-rw-r--r--fuzzers/corpora/midx/06c2db67ea65758d971346bfd6beaa61ed12f22cbin0 -> 108 bytes
-rw-r--r--fuzzers/corpora/midx/07f88eefaf12609b7370fe78b82be2955f1b41fdbin0 -> 96 bytes
-rw-r--r--fuzzers/corpora/midx/08495c5f3828a56c167de870d385c46ffdce03c5bin0 -> 40 bytes
-rw-r--r--fuzzers/corpora/midx/08ec8594e5b35fb9e8e0726584f720154f0b2b5dbin0 -> 133 bytes
-rw-r--r--fuzzers/corpora/midx/0903e378a493c596298074d6bff8de7f9ac25aa71
-rw-r--r--fuzzers/corpora/midx/09144a846f90f894049ef8a0ed0cc7ab4588dc6c1
-rw-r--r--fuzzers/corpora/midx/09b40dd618373bfe4d3f2838f686a70f645e640bbin0 -> 97 bytes
-rw-r--r--fuzzers/corpora/midx/0a00ef44d234c18d365ec41724dbf4f21b09d0c5bin0 -> 1987 bytes
-rw-r--r--fuzzers/corpora/midx/0a94e9f4a9b8cf56d52a9e3e7f2fa9a0a5c80d30bin0 -> 172 bytes
-rw-r--r--fuzzers/corpora/midx/0b35a123104b7872a7f15a710a23ef3594ace04dbin0 -> 1115 bytes
-rw-r--r--fuzzers/corpora/midx/0c3d7e6be32c014ea873440b0f095961d391af1abin0 -> 243 bytes
-rw-r--r--fuzzers/corpora/midx/0c65de477b89afc312a7e89cde06f8a17f65bd54bin0 -> 52 bytes
-rw-r--r--fuzzers/corpora/midx/0c81d0f368e979d2a0eb4598cbf1c9283936ba0cbin0 -> 308 bytes
-rw-r--r--fuzzers/corpora/midx/0c95a44ae995070a5279a2991c36de2251081460bin0 -> 450 bytes
-rw-r--r--fuzzers/corpora/midx/0de38e2cb13167df7d5a882570633596f64bc4f4bin0 -> 121 bytes
-rw-r--r--fuzzers/corpora/midx/0de96aa193045315457ade63c2614610c503db9ebin0 -> 59 bytes
-rw-r--r--fuzzers/corpora/midx/0e02deca2b16d71f8637933bd56dc8592ed9fdff1
-rw-r--r--fuzzers/corpora/midx/0e44fc9176fe2c1bae4209369da5bc057f54b2d2bin0 -> 2325 bytes
-rw-r--r--fuzzers/corpora/midx/0f6c5fc9b6a68835364bbef8937560ee5a4819381
-rw-r--r--fuzzers/corpora/midx/10d542d5c7da060a5f0664e21478a0d598e29844bin0 -> 322 bytes
-rw-r--r--fuzzers/corpora/midx/118735f7786ae6b4c2f6b36314ab1f2cafe9c3c81
-rw-r--r--fuzzers/corpora/midx/119b58eb353aa344264005016297fb911510ea0dbin0 -> 261 bytes
-rw-r--r--fuzzers/corpora/midx/127626832c30d6d94bb29384c0fde7ac6bca75ecbin0 -> 151 bytes
-rw-r--r--fuzzers/corpora/midx/1284f1a162588d4de87ca17149474644a0863b27bin0 -> 59 bytes
-rw-r--r--fuzzers/corpora/midx/1458599f19f1a967c787562bf8ec3e67677da9c8bin0 -> 386 bytes
-rw-r--r--fuzzers/corpora/midx/14ba6c1ddd05b22c6f2eae5f894721cd3efcbb16bin0 -> 57 bytes
-rw-r--r--fuzzers/corpora/midx/158cdc0a5aa005f167a8588d0beed9eee4aa36f2bin0 -> 145 bytes
-rw-r--r--fuzzers/corpora/midx/15dafc6fa800327f694b5eb2fc4ebf007be9c117bin0 -> 185 bytes
-rw-r--r--fuzzers/corpora/midx/1613ed4b2e909871f8897fd6354ff80a4ac12f87bin0 -> 120 bytes
-rw-r--r--fuzzers/corpora/midx/16daf4cb967bb47cf4566e9be7d96d3125bd2e12bin0 -> 252 bytes
-rw-r--r--fuzzers/corpora/midx/177783dce78efee878f6d6020fd87ab107bb11a1bin0 -> 121 bytes
-rw-r--r--fuzzers/corpora/midx/17a5090400a1fedc45070e4b530a26f320a89097bin0 -> 133 bytes
-rw-r--r--fuzzers/corpora/midx/17dea5cfa498f4d54384289a1daed0d15a85e7ccbin0 -> 450 bytes
-rw-r--r--fuzzers/corpora/midx/17e76ae5b54316679981113f52c27edc87dbcdeabin0 -> 426 bytes
-rw-r--r--fuzzers/corpora/midx/191ed5e9334693c53fc843f692dbc3c2c63e8241bin0 -> 379 bytes
-rw-r--r--fuzzers/corpora/midx/196a0ba4edb5bbfd66c1cda669abf0496573cf0ebin0 -> 123 bytes
-rw-r--r--fuzzers/corpora/midx/19742b6cee79fa5bf9b27dcbe367c82d0a399904bin0 -> 237 bytes
-rw-r--r--fuzzers/corpora/midx/1a21d7581d3b0a8d67934d48e91d45bd818836e8bin0 -> 1814 bytes
-rw-r--r--fuzzers/corpora/midx/1b2f96c5d75c7ca09b1012be4e6c3a7b248ed924bin0 -> 211 bytes
-rw-r--r--fuzzers/corpora/midx/1b604ff0683d0e23dc7945431f6514ba30d6ca0dbin0 -> 137 bytes
-rw-r--r--fuzzers/corpora/midx/1b771dd5bd3ae2b1c42c4efe6c896c83b88a4f91bin0 -> 450 bytes
-rw-r--r--fuzzers/corpora/midx/1b793a4ee73fa8bf423da70fca5f39ef32a8d288bin0 -> 311 bytes
-rw-r--r--fuzzers/corpora/midx/1c9599ce00978780519272be279f508c402e32681
-rw-r--r--fuzzers/corpora/midx/1cc0068f9f63b12dc8fdd38f9ebfb96c42664e95bin0 -> 1664 bytes
-rw-r--r--fuzzers/corpora/midx/1de6e1f5579da6e5c40f4ee23ac62e29e4f90541bin0 -> 282 bytes
-rw-r--r--fuzzers/corpora/midx/1eec93083260ebfab5f4c6d13119cf27c374b7e9bin0 -> 363 bytes
-rw-r--r--fuzzers/corpora/midx/1f0f574addd363d1fed131289f301c5c033aaa8fbin0 -> 66 bytes
-rw-r--r--fuzzers/corpora/midx/1f3e85cffdb545c1ba7c8bbe1ca18ec13e341038bin0 -> 156 bytes
-rw-r--r--fuzzers/corpora/midx/1f6a66a92d5f083a73a82280a0a1ae0800e56ae5bin0 -> 115 bytes
-rw-r--r--fuzzers/corpora/midx/208e422322052efcdaeb1a09bbf06c5f476b8efcbin0 -> 259 bytes
-rw-r--r--fuzzers/corpora/midx/22d75b2c3937957b14eded621b638283ce7fe1febin0 -> 1351 bytes
-rw-r--r--fuzzers/corpora/midx/22f90ff68166a409acf8f89bf60a31ad2c64ab37bin0 -> 255 bytes
-rw-r--r--fuzzers/corpora/midx/236ebad449d432b039d6ace1f250ef1fa2aa364dbin0 -> 390 bytes
-rw-r--r--fuzzers/corpora/midx/252a4e4bf7fb21792ec2f305fd88fa7c9168505fbin0 -> 123 bytes
-rw-r--r--fuzzers/corpora/midx/259e1faf7b7f12250062d36ded1193a9dbcae0f5bin0 -> 3 bytes
-rw-r--r--fuzzers/corpora/midx/25ad3dfb655ab4c853d0d277872310d9579c8e83bin0 -> 258 bytes
-rw-r--r--fuzzers/corpora/midx/26210f5b8fdbf81b312feea48659ec6e2e083c0bbin0 -> 953 bytes
-rw-r--r--fuzzers/corpora/midx/263a2a0915be36d8cb2bc30774e37e0344262347bin0 -> 65 bytes
-rw-r--r--fuzzers/corpora/midx/2679bfbc2f4f7c10a304245da4e156e235377b63bin0 -> 130 bytes
-rw-r--r--fuzzers/corpora/midx/270b7b567a63dd94bb2a90448bbbc2e2bbc4a261bin0 -> 2293 bytes
-rw-r--r--fuzzers/corpora/midx/271cd5c5e254a293d115588ee130040ef26b59e8bin0 -> 328 bytes
-rw-r--r--fuzzers/corpora/midx/27839a8035b48f8c19ab073808a03a95b6a90cc3bin0 -> 144 bytes
-rw-r--r--fuzzers/corpora/midx/2810c385c9285cbdb65bcdab5175999fe547cbadbin0 -> 449 bytes
-rw-r--r--fuzzers/corpora/midx/28afaf4ab4b092ccf987661e58009f96126bba63bin0 -> 2041 bytes
-rw-r--r--fuzzers/corpora/midx/29f842e86a891cff9f0b44c8aec19f7e23a47000bin0 -> 105 bytes
-rw-r--r--fuzzers/corpora/midx/2aa2549f617f19402d1feac61d4ca1af3545cc8abin0 -> 41 bytes
-rw-r--r--fuzzers/corpora/midx/2b73c2902eda6da41321493601003b29c3445713bin0 -> 257 bytes
-rw-r--r--fuzzers/corpora/midx/2bcec1274c5e7b2d7a581d851c016ef5b553fabebin0 -> 208 bytes
-rw-r--r--fuzzers/corpora/midx/2dd9a328b6d4e29e42684347be5c4b7cd7dc1a66bin0 -> 152 bytes
-rw-r--r--fuzzers/corpora/midx/2ddc17ee7ee89bb7dbc673328d5f3e55c76e686ebin0 -> 377 bytes
-rw-r--r--fuzzers/corpora/midx/2f71d5e99dc93618ed99fdb7c244a8f5e4a7eb4abin0 -> 961 bytes
-rw-r--r--fuzzers/corpora/midx/2f7cd0154d71a83e7b104670b2a77fbd285ffde2bin0 -> 247 bytes
-rw-r--r--fuzzers/corpora/midx/2f9d40ef790f5213234e95d123dce942b2d1d389bin0 -> 269 bytes
-rw-r--r--fuzzers/corpora/midx/31577bacbca7017308d2a0c9ebfdd4fce513bbe4bin0 -> 517 bytes
-rw-r--r--fuzzers/corpora/midx/3278f1bab88b80597d0066812d49f8bd3c7b1dcfbin0 -> 121 bytes
-rw-r--r--fuzzers/corpora/midx/328160cae6235605ff70951a2f6ac669ba7bb397bin0 -> 8 bytes
-rw-r--r--fuzzers/corpora/midx/337ed1bf91701a4c8926840259077e55938c6efcbin0 -> 517 bytes
-rw-r--r--fuzzers/corpora/midx/33a97d83ff7a774797b1751ea4bffbb4a22c58d9bin0 -> 644 bytes
-rw-r--r--fuzzers/corpora/midx/341021da9516401cf364ed2b7dfdda346db04f2fbin0 -> 320 bytes
-rw-r--r--fuzzers/corpora/midx/341773a439cdecc58f55fb205ac584cd93ffe0f21
-rw-r--r--fuzzers/corpora/midx/366091157510e40bca08fc2102b9018ccf4697debin0 -> 1172 bytes
-rw-r--r--fuzzers/corpora/midx/37096157e2f9f2ec8e0b97b21d335bd653f3edbdbin0 -> 199 bytes
-rw-r--r--fuzzers/corpora/midx/373a74b8613d09babcb567f91047e7b556a8de90bin0 -> 199 bytes
-rw-r--r--fuzzers/corpora/midx/3748b07ee7bec7bdd202ee14222cefca182417d1bin0 -> 122 bytes
-rw-r--r--fuzzers/corpora/midx/38b7906b9f956dca01dc92d0a901388ec1cbc8b1bin0 -> 112 bytes
-rw-r--r--fuzzers/corpora/midx/38ddf3424559f1a6e7687eff8469a358184b833b1
-rw-r--r--fuzzers/corpora/midx/38e31d0a7dcc3835ce1a4afeeda8446fb3d7ed73bin0 -> 11 bytes
-rw-r--r--fuzzers/corpora/midx/3955ec4497b226391ef9eb40f38af6dee4fa26b7bin0 -> 260 bytes
-rw-r--r--fuzzers/corpora/midx/3b6b424342133feb0f587f22bcd8f21595c004e5bin0 -> 252 bytes
-rw-r--r--fuzzers/corpora/midx/3bb71f41200e0ebf8d19532e7d6e384c48aa2d03bin0 -> 18 bytes
-rw-r--r--fuzzers/corpora/midx/3c5a6063797aba9ffe5ea9903bbfcf87193652d3bin0 -> 1091 bytes
-rw-r--r--fuzzers/corpora/midx/3dfb9927d959f2462f6944a32d080b60a265abfebin0 -> 418 bytes
-rw-r--r--fuzzers/corpora/midx/3e19242a63ec92a0c3f7138ebbc31bfe7cbd40cdbin0 -> 78 bytes
-rw-r--r--fuzzers/corpora/midx/3ec53ce4ea1f41f040a3c2beed929572af95dd43bin0 -> 7 bytes
-rw-r--r--fuzzers/corpora/midx/3f0762fdf49a58c0d8fd6683964a85caddee391bbin0 -> 89 bytes
-rw-r--r--fuzzers/corpora/midx/3f71ae863c4e9bac98e49a554b8ec4d78b17492d1
-rw-r--r--fuzzers/corpora/midx/3f9df30bfb66a28fbe6f1951ef7ae4ca9f19fdf2bin0 -> 56 bytes
-rw-r--r--fuzzers/corpora/midx/3fabb14670c008c22094c1d7cd7b1e23b4c48b3dbin0 -> 288 bytes
-rw-r--r--fuzzers/corpora/midx/408fba9c66c5d1deb31e4c69f1dd0677844dbc1bbin0 -> 85 bytes
-rw-r--r--fuzzers/corpora/midx/40ca8645081087e950ad61bccf8d43450366356ebin0 -> 342 bytes
-rw-r--r--fuzzers/corpora/midx/412faec949b9d04498de939561664ee559a583a71
-rw-r--r--fuzzers/corpora/midx/4148bd5336f89e73b2a5416c67d491c0ec4a2b7dbin0 -> 3 bytes
-rw-r--r--fuzzers/corpora/midx/41933e61fa20fbe2b190f9ae7ceae4a4b1220021bin0 -> 149 bytes
-rw-r--r--fuzzers/corpora/midx/423d90f3fc7ddc146095ec5a4b4f455aa876b69bbin0 -> 2941 bytes
-rw-r--r--fuzzers/corpora/midx/42a6c52249aa12cfef1db1bf302a483a01c972f3bin0 -> 2732 bytes
-rw-r--r--fuzzers/corpora/midx/42a82726f0e70da9b87b6c52bc1b3415576025f2bin0 -> 10 bytes
-rw-r--r--fuzzers/corpora/midx/4458e19f99e38c61ad9792b0b3bf8ac79f8236f11
-rw-r--r--fuzzers/corpora/midx/44a4411a8d6ed67ee3ea61d91d2afafe89295b0fbin0 -> 252 bytes
-rw-r--r--fuzzers/corpora/midx/44e04754d1b6c0c045e05509dd7033d19a926b10bin0 -> 2282 bytes
-rw-r--r--fuzzers/corpora/midx/45259e9f0a2cc7739a94eccaafb37c1570f73884bin0 -> 459 bytes
-rw-r--r--fuzzers/corpora/midx/46c0d7e952200cabc08b9cd776a9f6759f4208c3bin0 -> 73 bytes
-rw-r--r--fuzzers/corpora/midx/46deac8631633ea3c32005124e20a2bc2bbabadebin0 -> 387 bytes
-rw-r--r--fuzzers/corpora/midx/46e7edf6e9d6cbcdabde3b48f1c4efd93be40348bin0 -> 687 bytes
-rw-r--r--fuzzers/corpora/midx/46fe9556c28c94f7321baa2519a3cbeabbd54d09bin0 -> 1986 bytes
-rw-r--r--fuzzers/corpora/midx/49223681729e73b48b26a2262e4a66b2ba00e176bin0 -> 100 bytes
-rw-r--r--fuzzers/corpora/midx/499e61b689f6cc7e4efb0631684739c2a6f97c7dbin0 -> 1813 bytes
-rw-r--r--fuzzers/corpora/midx/4a06ad8c4d717bd048a7a1315a3d609d70f0162dbin0 -> 41 bytes
-rw-r--r--fuzzers/corpora/midx/4adb7d4791a4c6370478dff2eb987d715554bf09bin0 -> 1794 bytes
-rw-r--r--fuzzers/corpora/midx/4b01c479cdc9b750a31d5e7ac5004309222d218dbin0 -> 442 bytes
-rw-r--r--fuzzers/corpora/midx/4bce7460a6becba6d26984bb438d7d3aa4e4fc56bin0 -> 517 bytes
-rw-r--r--fuzzers/corpora/midx/4cc96483b6800dda296f00887b12a35154115090bin0 -> 57 bytes
-rw-r--r--fuzzers/corpora/midx/4f3aa59bae0619c9a06b631d9cb7767591810ab0bin0 -> 505 bytes
-rw-r--r--fuzzers/corpora/midx/501840d963cedd2945018de59e0202444d7ebf4bbin0 -> 57 bytes
-rw-r--r--fuzzers/corpora/midx/50479958c030d1addceb1ca8c27f24447e555e65bin0 -> 247 bytes
-rw-r--r--fuzzers/corpora/midx/508ba8ef164a809f739834a39d690e700101a7a1bin0 -> 521 bytes
-rw-r--r--fuzzers/corpora/midx/521d345313812e54bc6c944485e19dbb39a87768bin0 -> 258 bytes
-rw-r--r--fuzzers/corpora/midx/5369d74ac157f85b597c1b28bbd6768105e9327bbin0 -> 194 bytes
-rw-r--r--fuzzers/corpora/midx/53997b0146ff49bfe464be203b130a67ea93fd26bin0 -> 309 bytes
-rw-r--r--fuzzers/corpora/midx/560ea8bd7d11b00e0d21631b6d9ec7e63f0a5286bin0 -> 794 bytes
-rw-r--r--fuzzers/corpora/midx/5682ebc6878e247ce9bc636d34ada6ad338fcaf0bin0 -> 148 bytes
-rw-r--r--fuzzers/corpora/midx/5762abb5234edd913754b69e1ab03274c711ee68bin0 -> 2043 bytes
-rw-r--r--fuzzers/corpora/midx/579406f055070559bda3c6120107feb3e637c4812
-rw-r--r--fuzzers/corpora/midx/5837d16af4a9c1f2616467cc4aa9ec8836e05c58bin0 -> 155 bytes
-rw-r--r--fuzzers/corpora/midx/58901e865fe20b9fa136cca4b253d3ae73c2b78ebin0 -> 4 bytes
-rw-r--r--fuzzers/corpora/midx/58a87098a14572e46b53c87340083f999d8fcfc2bin0 -> 156 bytes
-rw-r--r--fuzzers/corpora/midx/59ae139a21448e0eb7371ddc6ef57f0c9dfe9c85bin0 -> 560 bytes
-rw-r--r--fuzzers/corpora/midx/5a7e81419f895168c555ac9b4e75a1ad4f04b34abin0 -> 166 bytes
-rw-r--r--fuzzers/corpora/midx/5b848c1f56a150d64020e9b0bb398a286dca4096bin0 -> 200 bytes
-rw-r--r--fuzzers/corpora/midx/5bd311bd846336149b2815666052fdb7e8bf2ea6bin0 -> 395 bytes
-rw-r--r--fuzzers/corpora/midx/5ce77eb98473a2e01d04909939edf7aabef5762cbin0 -> 267 bytes
-rw-r--r--fuzzers/corpora/midx/5e5cd5819811507ac69bd8abad27433ccd6b7521bin0 -> 389 bytes
-rw-r--r--fuzzers/corpora/midx/5ea114ae3dbb140364000c416152b0f32ce3de23bin0 -> 3368 bytes
-rw-r--r--fuzzers/corpora/midx/5f181bb0a79603c84534a9b8e37ecdeb1d2aeeb51
-rw-r--r--fuzzers/corpora/midx/5f428ce1169e28353cedb8be3e2f6edd0ef354e4bin0 -> 7 bytes
-rw-r--r--fuzzers/corpora/midx/5f9bc7729dc331e3c4d8e52df0688abad6d4aee8bin0 -> 7 bytes
-rw-r--r--fuzzers/corpora/midx/619527e1d650cd1c26e9bc61e424c9fdc04b17b9bin0 -> 202 bytes
-rw-r--r--fuzzers/corpora/midx/625d3676de25188865e05db2a3933c38508406fcbin0 -> 65 bytes
-rw-r--r--fuzzers/corpora/midx/6368569cfde7fbe369a0ee4695fa4d5a7d7887a6bin0 -> 1089 bytes
-rw-r--r--fuzzers/corpora/midx/6388fe4d630064ea1ea33aa85381d9c82e328e95bin0 -> 51 bytes
-rw-r--r--fuzzers/corpora/midx/64cff4e110f0bcb3ea833c1afda6e27a57dac0bcbin0 -> 78 bytes
-rw-r--r--fuzzers/corpora/midx/66449b87ce47b681c6326f337bebf03366a0ee99bin0 -> 507 bytes
-rw-r--r--fuzzers/corpora/midx/666a779eed16847c6930a71c0547a34e52db409ebin0 -> 62 bytes
-rw-r--r--fuzzers/corpora/midx/66ae3584497a1823a955c33e5bc53f7434c13e49bin0 -> 93 bytes
-rw-r--r--fuzzers/corpora/midx/66e238a6ad70fb30c82171ff1b73ea71b4379355bin0 -> 577 bytes
-rw-r--r--fuzzers/corpora/midx/66f345dc060ac5a1fe8bcf0828102d072deb1111bin0 -> 213 bytes
-rw-r--r--fuzzers/corpora/midx/66f839146ef46deed25fd2cd169a4f1a2a3533fabin0 -> 2001 bytes
-rw-r--r--fuzzers/corpora/midx/671720ee2b7ba45920b41b8016eb5206b88168eebin0 -> 253 bytes
-rw-r--r--fuzzers/corpora/midx/679c7140ad60ed32aeb7ee464499dd52b0fc212fbin0 -> 194 bytes
-rw-r--r--fuzzers/corpora/midx/67c5e6ce7bb47cefe54d749374f3288a2c915936bin0 -> 962 bytes
-rw-r--r--fuzzers/corpora/midx/69592399b45f2f83e0cc823c5f0e3865ac3fa611bin0 -> 205 bytes
-rw-r--r--fuzzers/corpora/midx/6abf97508f0ed808b7fe0d9bb2439981153badd2bin0 -> 240 bytes
-rw-r--r--fuzzers/corpora/midx/6b2dfb51b35b78680cb02ff54e06f0c983c04866bin0 -> 604 bytes
-rw-r--r--fuzzers/corpora/midx/6bbf6ab605fedd41ed6c7581ec9f87c75403e9c3bin0 -> 2361 bytes
-rw-r--r--fuzzers/corpora/midx/6c0656104902e1323f3a19c46df7cffecae94f1cbin0 -> 146 bytes
-rw-r--r--fuzzers/corpora/midx/6c0ce8006b3ebd8202e61fe5f4cc2285248bd1babin0 -> 237 bytes
-rw-r--r--fuzzers/corpora/midx/6cc635e6dd4e430ed4fb68a9f5add38aa02ae14fbin0 -> 391 bytes
-rw-r--r--fuzzers/corpora/midx/6d1b281d7bdd9887e53505fd5d040731db18ba48bin0 -> 1019 bytes
-rw-r--r--fuzzers/corpora/midx/6d5c35f9d87253b2fbe383bfde3775a9f737da121
-rw-r--r--fuzzers/corpora/midx/6d95355018cadedd97bed65c45a44a7ff6f065f7bin0 -> 966 bytes
-rw-r--r--fuzzers/corpora/midx/6dd70e887ab94db3327d9aaa0335914a1f4986b7bin0 -> 441 bytes
-rw-r--r--fuzzers/corpora/midx/6ed51a953a8b6671de417406e340d8d0a211aa12bin0 -> 198 bytes
-rw-r--r--fuzzers/corpora/midx/6f911f19652a4457c93ef92b594bc1dc2ca900f8bin0 -> 11 bytes
-rw-r--r--fuzzers/corpora/midx/6fa76cbaeb3cf0417c7a372132167bcd737db66bbin0 -> 516 bytes
-rw-r--r--fuzzers/corpora/midx/71f66d3f7da318d69681a22ebbceb1a2bb290658bin0 -> 1029 bytes
-rw-r--r--fuzzers/corpora/midx/7227a2dc335af13e7225536c49969f46a800750a1
-rw-r--r--fuzzers/corpora/midx/72fce27bbccf582f2023f5e168853251e576592abin0 -> 962 bytes
-rw-r--r--fuzzers/corpora/midx/738e5543ae005a6de85dfcd960eb8e3e6faa8947bin0 -> 623 bytes
-rw-r--r--fuzzers/corpora/midx/739d9c8868445202305d0a4e5766df1c68932688bin0 -> 1091 bytes
-rw-r--r--fuzzers/corpora/midx/7438b07314917c84d348e7d9629e3712190c7da7bin0 -> 586 bytes
-rw-r--r--fuzzers/corpora/midx/7490f00d660f5d8dedfa606cca241bd07be86a4fbin0 -> 587 bytes
-rw-r--r--fuzzers/corpora/midx/75c64e8b61af41b28516c2c0fe902362d55a24f3bin0 -> 3684 bytes
-rw-r--r--fuzzers/corpora/midx/75e94b59a41e7f086b6f7ab3bca801251744ae3dbin0 -> 316 bytes
-rw-r--r--fuzzers/corpora/midx/7612ceb3a989f97a7bb19f57c7f9c61366953642bin0 -> 356 bytes
-rw-r--r--fuzzers/corpora/midx/76ac2328e1c979bca648b4082b8bfe6f2e2e73eabin0 -> 245 bytes
-rw-r--r--fuzzers/corpora/midx/76f296039ba4d666c9147ad234d43b55050808e3bin0 -> 258 bytes
-rw-r--r--fuzzers/corpora/midx/777f248eea53e3dd2b726e1e0de5eeda43b6d3231
-rw-r--r--fuzzers/corpora/midx/792fcd0075bd8031a98a68ce04d6a9f23feef7b4bin0 -> 3612 bytes
-rw-r--r--fuzzers/corpora/midx/7a936c3e69013b2c71dcb72f0eccd99c93367533bin0 -> 194 bytes
-rw-r--r--fuzzers/corpora/midx/7b30d0cd07108f2e45ce1a3fab3f971b25dcf5cdbin0 -> 89 bytes
-rw-r--r--fuzzers/corpora/midx/7b87f367b5fa3bf29bae19031814e5d0120a15babin0 -> 277 bytes
-rw-r--r--fuzzers/corpora/midx/7c12e4bca60858eae13c47a66e54cd9e96a50909bin0 -> 513 bytes
-rw-r--r--fuzzers/corpora/midx/7c59f95e649b3be6344f4f835afd0d9a894c1144bin0 -> 142 bytes
-rw-r--r--fuzzers/corpora/midx/7dcb6494c3614a8690dc496309f90e0f23634c37bin0 -> 1776 bytes
-rw-r--r--fuzzers/corpora/midx/7e64b86827ea98f0a4eb54736c460a59b0c30420bin0 -> 166 bytes
-rw-r--r--fuzzers/corpora/midx/8125d9eaa09b3d2283fea73223866cb36877c4a4bin0 -> 333 bytes
-rw-r--r--fuzzers/corpora/midx/81c7fc514fa9a07b5b87b94cf9c00df2b1325a74bin0 -> 455 bytes
-rw-r--r--fuzzers/corpora/midx/81f9df0493052d980ca13918637bc6ce565615b3bin0 -> 65 bytes
-rw-r--r--fuzzers/corpora/midx/82556b9345134dd689cb9d0d08d3dc8459454181bin0 -> 196 bytes
-rw-r--r--fuzzers/corpora/midx/82d35a7a6ffb333b02d0d597e88ffdd481237a8bbin0 -> 232 bytes
-rw-r--r--fuzzers/corpora/midx/82e931da372a2c69c0f10274342173c2be091f1cbin0 -> 334 bytes
-rw-r--r--fuzzers/corpora/midx/83e2b53f22afe8f7ee21d30fae2619ad0d6a71e3bin0 -> 216 bytes
-rw-r--r--fuzzers/corpora/midx/83f4d70189dbc0d3aaf5025977c53d4d34fc5893bin0 -> 699 bytes
-rw-r--r--fuzzers/corpora/midx/85e17cceba7850be893afdc04c8233bea1ef6e72bin0 -> 83 bytes
-rw-r--r--fuzzers/corpora/midx/874d4abdcd7db751eb930928231669afe90589f5bin0 -> 2633 bytes
-rw-r--r--fuzzers/corpora/midx/87894ec663568153d7837f49b80f6d2e99818bd7bin0 -> 186 bytes
-rw-r--r--fuzzers/corpora/midx/88052b76108b4ede342f3dd87bb6835b2f71ea83bin0 -> 77 bytes
-rw-r--r--fuzzers/corpora/midx/884c54256c0ec2cf1c5fa08a0b3d9c2fea021300bin0 -> 573 bytes
-rw-r--r--fuzzers/corpora/midx/8858f36373db5fd6b805a768af55c21019c664b2bin0 -> 213 bytes
-rw-r--r--fuzzers/corpora/midx/88fe8b6767c1bd32308208b22e0b00697e5eddf7bin0 -> 159 bytes
-rw-r--r--fuzzers/corpora/midx/898cac1610f2f2fb67eb092cd053f0006c3070e3bin0 -> 175 bytes
-rw-r--r--fuzzers/corpora/midx/89d0f5573ae1b524e7e9bdb1fb54ea4ce99e3ef0bin0 -> 4 bytes
-rw-r--r--fuzzers/corpora/midx/8a55300e400efd56be5e12258ebf575c4f3b55edbin0 -> 98 bytes
-rw-r--r--fuzzers/corpora/midx/8bf7b464aaa2c2b536aa1d76a1297c19155f56031
-rw-r--r--fuzzers/corpora/midx/8c05e8ef26302a79c89670ad3aa4e8d0bc921923bin0 -> 194 bytes
-rw-r--r--fuzzers/corpora/midx/8c15f5a268ded9663197d66e8d7d4098e0ae9bf5bin0 -> 27 bytes
-rw-r--r--fuzzers/corpora/midx/8ca9e85a9e628f0016ea4e6413945b3830730c24bin0 -> 228 bytes
-rw-r--r--fuzzers/corpora/midx/8e74126a239927900a8f655c813a4b230191a5babin0 -> 297 bytes
-rw-r--r--fuzzers/corpora/midx/8ee63e791c004427dd033b468b2ed7446ee6e2e0bin0 -> 255 bytes
-rw-r--r--fuzzers/corpora/midx/9028113aa78b649e13ff259027a4e450d469e5dabin0 -> 71 bytes
-rw-r--r--fuzzers/corpora/midx/90db2115b8262ebecbefbe8f0a07c451e39bca07bin0 -> 217 bytes
-rw-r--r--fuzzers/corpora/midx/923f28a4d1917e20ee0736b90695c2123c0c987cbin0 -> 728 bytes
-rw-r--r--fuzzers/corpora/midx/92a5c74e0506d65d1a12686496452870367b169abin0 -> 2584 bytes
-rw-r--r--fuzzers/corpora/midx/92dcf94eb2f92b4e1a232eab3b3f808f4236f118bin0 -> 195 bytes
-rw-r--r--fuzzers/corpora/midx/9414502aedbef5e307897683625418dd4ac575acbin0 -> 1758 bytes
-rw-r--r--fuzzers/corpora/midx/9422e25bec5fec9f84603a85673b54b1a5e77a40bin0 -> 33 bytes
-rw-r--r--fuzzers/corpora/midx/943754e865888063e0684aec838222522390d43ebin0 -> 205 bytes
-rw-r--r--fuzzers/corpora/midx/943e067806ae069afbc029ea7a612410e5395687bin0 -> 553 bytes
-rw-r--r--fuzzers/corpora/midx/9547646cc1a5d260df099b00ea7ee2b95567aee1bin0 -> 208 bytes
-rw-r--r--fuzzers/corpora/midx/968f7027ec9fbf75a519069ea5189e85a81448b2bin0 -> 1239 bytes
-rw-r--r--fuzzers/corpora/midx/9691046a2f8b31319a6fdfde0506c9a72aed839abin0 -> 2082 bytes
-rw-r--r--fuzzers/corpora/midx/96a8cd5c33986cc26cc00eb2de627149f5259e33bin0 -> 458 bytes
-rw-r--r--fuzzers/corpora/midx/972466bbc33d2d7d7c21be21c7594b51e78675c5bin0 -> 131 bytes
-rw-r--r--fuzzers/corpora/midx/980f21a3609762154030f7cf0fe98a892d20f220bin0 -> 149 bytes
-rw-r--r--fuzzers/corpora/midx/9865f12189ef977418d8410fceebb6830c74d820bin0 -> 296 bytes
-rw-r--r--fuzzers/corpora/midx/98a1096d609545083878d5126743bbc5985786a9bin0 -> 2325 bytes
-rw-r--r--fuzzers/corpora/midx/98c3b6bbf5dc19bc4aad894087277a36d7c79669bin0 -> 57 bytes
-rw-r--r--fuzzers/corpora/midx/98f9cd44400b592f809596004125267acf848435bin0 -> 31 bytes
-rw-r--r--fuzzers/corpora/midx/9a97260f04ecfe0918499ede95cf4bcb3dbc2b51bin0 -> 66 bytes
-rw-r--r--fuzzers/corpora/midx/9ba0dba2ca4405d04113086309882dac6182e6b8bin0 -> 2063 bytes
-rw-r--r--fuzzers/corpora/midx/9c329ee4b02f2d26ee1a399c873b0452aedca3c0bin0 -> 638 bytes
-rw-r--r--fuzzers/corpora/midx/9e8e638837e202d83ff606a22dd0e310150fa260bin0 -> 387 bytes
-rw-r--r--fuzzers/corpora/midx/9ee03d17e070df72547e423a412da0b6a60ad565bin0 -> 396 bytes
-rw-r--r--fuzzers/corpora/midx/9fad6bd2b07d65e607039bb2bcda0816410cf983bin0 -> 1841 bytes
-rw-r--r--fuzzers/corpora/midx/9fcbd21f4dd194a623d832422384a1519742f0bbbin0 -> 7 bytes
-rw-r--r--fuzzers/corpora/midx/a019fb7f17aa36a9743c530e1f11d5613b8b1158bin0 -> 2 bytes
-rw-r--r--fuzzers/corpora/midx/a0b8c6ef20198377b19f59e41f08f4cf2107f460bin0 -> 64 bytes
-rw-r--r--fuzzers/corpora/midx/a14079a3e8cbc2112da4fa747ef20cdfd580e068bin0 -> 66 bytes
-rw-r--r--fuzzers/corpora/midx/a14d61ba0c609665d37e6c6da929cb53c5b70545bin0 -> 322 bytes
-rw-r--r--fuzzers/corpora/midx/a15cf2a13e408cb76af0091a0c286af7ffce58e1bin0 -> 663 bytes
-rw-r--r--fuzzers/corpora/midx/a1ddedbdd05eac99b8b31322635771cd9c999f8abin0 -> 805 bytes
-rw-r--r--fuzzers/corpora/midx/a235661c3f8b0174a1658e9c435a69577c49256abin0 -> 365 bytes
-rw-r--r--fuzzers/corpora/midx/a261397a4db5ac196c72d73ba6999e9fd4fc5c1fbin0 -> 63 bytes
-rw-r--r--fuzzers/corpora/midx/a3a803fd6a56d31269717983bbdf2fceebb626c3bin0 -> 327 bytes
-rw-r--r--fuzzers/corpora/midx/a3d5b0b21d977e8f94d401250de1bbd4fa1d0ee0bin0 -> 321 bytes
-rw-r--r--fuzzers/corpora/midx/a42f2900ca519bd15b8d6f507449d1a07de2ef75bin0 -> 3549 bytes
-rw-r--r--fuzzers/corpora/midx/a4884775b414eaf9643224564f3be405519cf99abin0 -> 77 bytes
-rw-r--r--fuzzers/corpora/midx/a48da63e9a5709c24cb66f598a7a964cbc7ccfc7bin0 -> 456 bytes
-rw-r--r--fuzzers/corpora/midx/a5789fd83dff18079ea7ba41c999f57bee4db41bbin0 -> 381 bytes
-rw-r--r--fuzzers/corpora/midx/a5bb1c60191742df4a91afb622e9b22a2f0b7765bin0 -> 128 bytes
-rw-r--r--fuzzers/corpora/midx/a5fdfade1cef5e7e494dd6e3791bca5a663d7012bin0 -> 299 bytes
-rw-r--r--fuzzers/corpora/midx/a6c66f79f5aaf2c1a26ff16754fe1a8c22627e0cbin0 -> 983 bytes
-rw-r--r--fuzzers/corpora/midx/a7478a05a1fc04a9e035be5593bfb6a281ec460fbin0 -> 188 bytes
-rw-r--r--fuzzers/corpora/midx/a75193dd600661d2b417d4e29b23faa7d721c214bin0 -> 193 bytes
-rw-r--r--fuzzers/corpora/midx/a7ccae74c641ffcdda0042e6c04438d5b32c4cf3bin0 -> 1220 bytes
-rw-r--r--fuzzers/corpora/midx/a94aa5881abdea5374775b8155812121673f89c3bin0 -> 525 bytes
-rw-r--r--fuzzers/corpora/midx/a98d794f0f24be7a36917826121fc14a241208931
-rw-r--r--fuzzers/corpora/midx/a993077e321bc4e1831bb5a8ac7511d90d32ae27bin0 -> 193 bytes
-rw-r--r--fuzzers/corpora/midx/aa3bc67656945e43f9342d3aaaef247584d96cfabin0 -> 66 bytes
-rw-r--r--fuzzers/corpora/midx/ab111c4d72e3d6796e3d7391e9f35b4e6fefc04abin0 -> 129 bytes
-rw-r--r--fuzzers/corpora/midx/ab248c42f77952d5d17d6f5203adaa5925c05c64bin0 -> 260 bytes
-rw-r--r--fuzzers/corpora/midx/ab8451fadf805e5087837d9f6d91ef7eb6fa5edbbin0 -> 240 bytes
-rw-r--r--fuzzers/corpora/midx/abbee3b37aff879b1cef47390001b89b0f6ebc0abin0 -> 126 bytes
-rw-r--r--fuzzers/corpora/midx/ac15b23f03af8be6dbbb3bbb8d3877a1f9e074a3bin0 -> 321 bytes
-rw-r--r--fuzzers/corpora/midx/ac47bda12269c06d773f5f3c6517f78513a54a08bin0 -> 435 bytes
-rw-r--r--fuzzers/corpora/midx/ad1f4fb57f481a00a9bb231517a3155ef0d0877fbin0 -> 58 bytes
-rw-r--r--fuzzers/corpora/midx/ad25e7ffabedd94833d2529886af4d459529ec9dbin0 -> 182 bytes
-rw-r--r--fuzzers/corpora/midx/ad796ebb423f58187806c4a7ee7b787394353ce6bin0 -> 281 bytes
-rw-r--r--fuzzers/corpora/midx/adc83b19e793491b1c6ea0fd8b46cd9f32e592fc1
-rw-r--r--fuzzers/corpora/midx/ae14b80f26f5cee2d85d5154c2cef1eefafa8cc9bin0 -> 527 bytes
-rw-r--r--fuzzers/corpora/midx/ae3ba892de543801b3c1dfbce370eb2c80a6fb27bin0 -> 850 bytes
-rw-r--r--fuzzers/corpora/midx/aecc0c7f08810803da234e26e7c6fa7a9f1c0593bin0 -> 393 bytes
-rw-r--r--fuzzers/corpora/midx/aed2e85d5d39d25e738a34f30a722680bde30368bin0 -> 229 bytes
-rw-r--r--fuzzers/corpora/midx/b00a75de1987c6f549bf73a63e8f23a2de6641b3bin0 -> 6 bytes
-rw-r--r--fuzzers/corpora/midx/b02e9f951ce9f10a8eb80f1fc61cd3d2832dd7f4bin0 -> 234 bytes
-rw-r--r--fuzzers/corpora/midx/b04aff8ab2e133d45bf44565bd4bf9e33b795a97bin0 -> 2242 bytes
-rw-r--r--fuzzers/corpora/midx/b12097ed83db761f7bb79411a59e2474de9b1199bin0 -> 261 bytes
-rw-r--r--fuzzers/corpora/midx/b1beb2f462b4cb30a09d534b9f49f2e08d76363c1
-rw-r--r--fuzzers/corpora/midx/b201733b6165f4544578bd6aad3f55aeafd9a194bin0 -> 259 bytes
-rw-r--r--fuzzers/corpora/midx/b2699f25c21ffe453dcce20e31b3093e0f9b2abf1
-rw-r--r--fuzzers/corpora/midx/b34a5760a1036f909e0243cd857fcef65e40d752bin0 -> 1931 bytes
-rw-r--r--fuzzers/corpora/midx/b3fdacd639073cc1954bcb1f31046d094e2d2296bin0 -> 449 bytes
-rw-r--r--fuzzers/corpora/midx/b40a6dbe32c8e6a9b777331e7fd97f0d94ceca1cbin0 -> 7 bytes
-rw-r--r--fuzzers/corpora/midx/b52ff2010f22ae6758cde5d529fd19de2a7d5fc5bin0 -> 123 bytes
-rw-r--r--fuzzers/corpora/midx/b53a7a2afd9dfc55c328b4e06a36882c53126e95bin0 -> 524 bytes
-rw-r--r--fuzzers/corpora/midx/b548ae8a77a62b7f375b8b48e7184ceed59bc8f8bin0 -> 327 bytes
-rw-r--r--fuzzers/corpora/midx/b66eaf0b689495cc7c194ab1fca7d36ae9da9758bin0 -> 172 bytes
-rw-r--r--fuzzers/corpora/midx/b680bf23da22b8b7e77d847169fe9b6968d79e8b1
-rw-r--r--fuzzers/corpora/midx/b70d6e7d230fb1393b8f665adcd5658cad7059febin0 -> 70 bytes
-rw-r--r--fuzzers/corpora/midx/b87a59f78adb3ef18b0176a8e7fe7e90c2ab4ef7bin0 -> 127 bytes
-rw-r--r--fuzzers/corpora/midx/b88c5233090e859e923acbdfa9b168f95d7fc14bbin0 -> 145 bytes
-rw-r--r--fuzzers/corpora/midx/ba1923ea69eec8fe765e8d1222eccb928ca6c3c2bin0 -> 554 bytes
-rw-r--r--fuzzers/corpora/midx/ba4d695c1eb02c702bd99a3db27838c7ba617d79bin0 -> 519 bytes
-rw-r--r--fuzzers/corpora/midx/ba7e4f999dc22d223c7f75db36646bfa05848572bin0 -> 2252 bytes
-rw-r--r--fuzzers/corpora/midx/bc5f0cd338d1d17a230378390aa810bc7b103cdabin0 -> 1371 bytes
-rw-r--r--fuzzers/corpora/midx/bcbb4cf10018a177dd9a6c642d887e0de3d8e522bin0 -> 209 bytes
-rw-r--r--fuzzers/corpora/midx/bcfeb114df6d5c6e0c85cbe1081631bc321ff65bbin0 -> 508 bytes
-rw-r--r--fuzzers/corpora/midx/bd582237a9293e2a53d8222722a69e7d215822bfbin0 -> 500 bytes
-rw-r--r--fuzzers/corpora/midx/bdc83a415da40f74825379203538a2e4d27cffa7bin0 -> 130 bytes
-rw-r--r--fuzzers/corpora/midx/be160536594c87dc07554a71c7d24cd1d718aeccbin0 -> 185 bytes
-rw-r--r--fuzzers/corpora/midx/be8f3c744a23f67fb316a39609ca11ddac025b58bin0 -> 325 bytes
-rw-r--r--fuzzers/corpora/midx/bf873b027b48f3fd7b727473c832486d99ddb196bin0 -> 151 bytes
-rw-r--r--fuzzers/corpora/midx/bf8e20ef6b79131ef9bab8c9c1bb7dbecbead6a5bin0 -> 259 bytes
-rw-r--r--fuzzers/corpora/midx/bf90507b8f7c7eebb89edeaabf6a432d86e7df4abin0 -> 164 bytes
-rw-r--r--fuzzers/corpora/midx/bfa7a5ce666899fb3e2a7216dbf59886da672658bin0 -> 979 bytes
-rw-r--r--fuzzers/corpora/midx/bfeaa454d8db33efabba88f146bee6c803369ba0bin0 -> 523 bytes
-rw-r--r--fuzzers/corpora/midx/c0388910e8d88dcd2e65848ba2cef465caa6b258bin0 -> 223 bytes
-rw-r--r--fuzzers/corpora/midx/c0db293f75bb44668bcbb79286ebed87df141a85bin0 -> 448 bytes
-rw-r--r--fuzzers/corpora/midx/c1234da1441255244aba15ecad2a4fa7fd47115ebin0 -> 130 bytes
-rw-r--r--fuzzers/corpora/midx/c2206ac3c289a759ee0e9d0d31cc336f0802f7bc1
-rw-r--r--fuzzers/corpora/midx/c22340ab36e5cff088a58272f63cf69e54a1a9f4bin0 -> 78 bytes
-rw-r--r--fuzzers/corpora/midx/c242c9336c4c22b316e0e56d616b2d45b3318ca4bin0 -> 1933 bytes
-rw-r--r--fuzzers/corpora/midx/c25a51d8dfaf58d5b609729bb156a80e3b0d892fbin0 -> 48 bytes
-rw-r--r--fuzzers/corpora/midx/c3419450240d06982b0d828911b188a903355216bin0 -> 953 bytes
-rw-r--r--fuzzers/corpora/midx/c36ea7651f76ae817d8d60cae580e47638741372bin0 -> 3715 bytes
-rw-r--r--fuzzers/corpora/midx/c42300c021bfd35702f564e917839503922cbe9abin0 -> 775 bytes
-rw-r--r--fuzzers/corpora/midx/c45d82ddade99ef857b563e435f2efe89e58b0bebin0 -> 11 bytes
-rw-r--r--fuzzers/corpora/midx/c4d1e9187de1e13353b3beb3c1ab16dd62cda571bin0 -> 1290 bytes
-rw-r--r--fuzzers/corpora/midx/c4e98278a25011c54734494d4534a97489cf4c24bin0 -> 704 bytes
-rw-r--r--fuzzers/corpora/midx/c4f996ab08f56ce2e9fec7a0428ded510dd6a04abin0 -> 5 bytes
-rw-r--r--fuzzers/corpora/midx/c544850a7325e7226583895204f99de730525803bin0 -> 393 bytes
-rw-r--r--fuzzers/corpora/midx/c56629528d5bebdb94f85522caf0f36bbcb19106bin0 -> 245 bytes
-rw-r--r--fuzzers/corpora/midx/c5c75b58883ccf41b20b140740e2ce763c6086cdbin0 -> 2377 bytes
-rw-r--r--fuzzers/corpora/midx/c62da85dca0d4dfb1d7af5d0520eb74993a1e3b0bin0 -> 97 bytes
-rw-r--r--fuzzers/corpora/midx/c6379aaaecd282b8ed6d0b4291d0d9fdc763160abin0 -> 198 bytes
-rw-r--r--fuzzers/corpora/midx/c6431921184e3edf4fd3e47384c69654cdac0189bin0 -> 840 bytes
-rw-r--r--fuzzers/corpora/midx/c6f2ca17c6d313a35676cbacd094eb40fd74b23ebin0 -> 64 bytes
-rw-r--r--fuzzers/corpora/midx/c83e04d58e04fccac37b9dd313eab72011fe8ea1bin0 -> 257 bytes
-rw-r--r--fuzzers/corpora/midx/c88dc350b98a5c5ae0503683318c1f30443906a81
-rw-r--r--fuzzers/corpora/midx/c89ee2e9e30a474b5f9532ec61d7aad78377baa0bin0 -> 367 bytes
-rw-r--r--fuzzers/corpora/midx/c921be4abeb44d1ff07f76f632a16e86526bc4bebin0 -> 52 bytes
-rw-r--r--fuzzers/corpora/midx/c98bdc0431aaece1e8a721aff0ea511cfb8062a6bin0 -> 81 bytes
-rw-r--r--fuzzers/corpora/midx/c9c456fd3e35a942ef4ab756e04e725cf1f71167bin0 -> 1503 bytes
-rw-r--r--fuzzers/corpora/midx/cd1f1a31b79af77e1e764102942ba7a79dcd24cfbin0 -> 10 bytes
-rw-r--r--fuzzers/corpora/midx/cd57e5904254c2278e9ecf28ed7414d7aed8eef1bin0 -> 108 bytes
-rw-r--r--fuzzers/corpora/midx/cd665cdc2bd6a26eb68c9af6d1728a7d4f6eb309bin0 -> 33 bytes
-rw-r--r--fuzzers/corpora/midx/ce72111c4314b22c4c7824bc8ea340ebd6d3fa84bin0 -> 218 bytes
-rw-r--r--fuzzers/corpora/midx/ceacc7ace2f4be962b0db2eeeea3fe6a00ca9dd6bin0 -> 58 bytes
-rw-r--r--fuzzers/corpora/midx/cf40769d8b4fcbac1b10ced2e0c3c1294f23fcddbin0 -> 1095 bytes
-rw-r--r--fuzzers/corpora/midx/d120111a77a3e3d1d504a04bbfc5f53effa14eb0bin0 -> 275 bytes
-rw-r--r--fuzzers/corpora/midx/d1ce81240a32aec2de1b0d779aa29a62c36f291fbin0 -> 1166 bytes
-rw-r--r--fuzzers/corpora/midx/d21869b290cd2b448e7b8103dced97e62fefffccbin0 -> 258 bytes
-rw-r--r--fuzzers/corpora/midx/d2bffcd01e87ce9860007b244ff1e79ecd3d4d0fbin0 -> 260 bytes
-rw-r--r--fuzzers/corpora/midx/d2d668b6e28fca83da5146021879c2b006406fa4bin0 -> 2113 bytes
-rw-r--r--fuzzers/corpora/midx/d37abe0d299b8ad1e90f5b7af302c24f411a7ed1bin0 -> 357 bytes
-rw-r--r--fuzzers/corpora/midx/d3d689a12ab3808313d5ba0044e8c67ecb4337e4bin0 -> 208 bytes
-rw-r--r--fuzzers/corpora/midx/d48eb559213edf05aa2850a14194885ae2086ba4bin0 -> 33 bytes
-rw-r--r--fuzzers/corpora/midx/d490f365693e49ebdeaf658b3f549311a399c6a8bin0 -> 503 bytes
-rw-r--r--fuzzers/corpora/midx/d54821b652a8611c486bedd8645081d3a4b1c8d1bin0 -> 252 bytes
-rw-r--r--fuzzers/corpora/midx/d5586a91f9a879e8a67ec7b09a48038909918ad9bin0 -> 509 bytes
-rw-r--r--fuzzers/corpora/midx/d696362920a2fad8e280293e8d1c92b18c87e4aebin0 -> 235 bytes
-rw-r--r--fuzzers/corpora/midx/d6b546a2b1bc1c8f80028e4be10c45a06014b32dbin0 -> 868 bytes
-rw-r--r--fuzzers/corpora/midx/d7f04cbca92b2122e0f437ed4dd8fd1782d40f7fbin0 -> 1163 bytes
-rw-r--r--fuzzers/corpora/midx/d81944a0c1ed56d11129533d9a3d0d038113d53dbin0 -> 300 bytes
-rw-r--r--fuzzers/corpora/midx/d8524e83ea63c9b365c2e93af75a8100a08b1b69bin0 -> 248 bytes
-rw-r--r--fuzzers/corpora/midx/d8d9a2d06763cf6feb433cef92a80ef14baab31abin0 -> 169 bytes
-rw-r--r--fuzzers/corpora/midx/d917fbd641cc40786246387456a636899d56b5a6bin0 -> 597 bytes
-rw-r--r--fuzzers/corpora/midx/d99f9ec8b504029457185ac03ea8ba21c2611737bin0 -> 188 bytes
-rw-r--r--fuzzers/corpora/midx/d9c9c90c1bbc55beb81875838e9067c473d0fa92bin0 -> 47 bytes
-rw-r--r--fuzzers/corpora/midx/d9e908317a6ef08a7528924672836a550d34cb5fbin0 -> 193 bytes
-rw-r--r--fuzzers/corpora/midx/d9ef71deb57fa6f40e027be2c84fa37d288e1cc5bin0 -> 646 bytes
-rw-r--r--fuzzers/corpora/midx/da8841b9d04382d62d4aeb3fde4dc78466f31543bin0 -> 447 bytes
-rw-r--r--fuzzers/corpora/midx/db3fbb74c9c9c4185f91eca85f14c3d2c3d9f487bin0 -> 24 bytes
-rw-r--r--fuzzers/corpora/midx/db7a31de22258d4dc17d44a27d9340946e9c9ee9bin0 -> 179 bytes
-rw-r--r--fuzzers/corpora/midx/dbbe57fc653930b4ff43f168565ba84ef25f60c2bin0 -> 261 bytes
-rw-r--r--fuzzers/corpora/midx/dbe74c0d9e7b62c1fd87d5e3ea73ee04f0337154bin0 -> 1995 bytes
-rw-r--r--fuzzers/corpora/midx/dbebf36a6b91568ac059142c3ca3211226da12a8bin0 -> 284 bytes
-rw-r--r--fuzzers/corpora/midx/ddacbb379242b31a00d62fdff5777dffc1e899c2bin0 -> 786 bytes
-rw-r--r--fuzzers/corpora/midx/decd2cd9cef352610ac9e5cc461df1829543f9f03
-rw-r--r--fuzzers/corpora/midx/deeae69363db06972798b296a0c5c99e02cb2b4cbin0 -> 2078 bytes
-rw-r--r--fuzzers/corpora/midx/e03e105323e6e7b2af90ad876b5c547af90d8f6bbin0 -> 15 bytes
-rw-r--r--fuzzers/corpora/midx/e3133215848c9cde428338c9d51424c8a81b96f5bin0 -> 745 bytes
-rw-r--r--fuzzers/corpora/midx/e32a25f0347b0e95d4bea16c27a1f374847683bdbin0 -> 130 bytes
-rw-r--r--fuzzers/corpora/midx/e45ce97522194abcdd7ff9beb931e20b86c97a79bin0 -> 1993 bytes
-rw-r--r--fuzzers/corpora/midx/e484023d50fc1036e46a437053b965c527700d42bin0 -> 154 bytes
-rw-r--r--fuzzers/corpora/midx/e4e60e77fe3a050940d0afcc7dbab7ef06b04ba3bin0 -> 137 bytes
-rw-r--r--fuzzers/corpora/midx/e51629784092d9cf811ea1bd894297f062ed7ec4bin0 -> 583 bytes
-rw-r--r--fuzzers/corpora/midx/e5a7e837029178b3fb6a26d77ea4574ffeaa219dbin0 -> 3162 bytes
-rw-r--r--fuzzers/corpora/midx/e5c616e9efdd9c80181f9210d0e3a81c08fe9b4dbin0 -> 1981 bytes
-rw-r--r--fuzzers/corpora/midx/e5e6e84e2c7770537c744bcfdbe8303afe39ef92bin0 -> 263 bytes
-rw-r--r--fuzzers/corpora/midx/e5fb20928feec1ee3114597317edc0e06c413749bin0 -> 95 bytes
-rw-r--r--fuzzers/corpora/midx/e789319791fe704e5a8ffd7cb570c8d2722ac35fbin0 -> 587 bytes
-rw-r--r--fuzzers/corpora/midx/e9fdb9f08f225b4231f01dda9c7b61e7b78bf7d3bin0 -> 132 bytes
-rw-r--r--fuzzers/corpora/midx/ea6780324dca9a06db28598dfb590436d846d99fbin0 -> 419 bytes
-rw-r--r--fuzzers/corpora/midx/ea6afcc92b8a6c9e14cc053d351909ad5b0a3fdfbin0 -> 450 bytes
-rw-r--r--fuzzers/corpora/midx/ea8c569029c0cacc4ae75e95b2f4e84abb6867f4bin0 -> 2099 bytes
-rw-r--r--fuzzers/corpora/midx/eb3e80c3ea9cfe9e08b2eef117aaa522a51a619cbin0 -> 955 bytes
-rw-r--r--fuzzers/corpora/midx/ec55b30741fe8fffeec584176c8d20f6a679cfa1bin0 -> 441 bytes
-rw-r--r--fuzzers/corpora/midx/ed0724a6c3804a3ab20a980b5ca48671689a602fbin0 -> 1167 bytes
-rw-r--r--fuzzers/corpora/midx/edeb545d1cf852dc9582fa764010fe844a5e3515bin0 -> 258 bytes
-rw-r--r--fuzzers/corpora/midx/ee70b920de91f1be6b4448070ee2d1bd9e08286dbin0 -> 2337 bytes
-rw-r--r--fuzzers/corpora/midx/ef004af4e947d25b4d1d1dd16502260d4c7a99cdbin0 -> 142 bytes
-rw-r--r--fuzzers/corpora/midx/f009d226503b73aed0f1fd952ef8725433d158bebin0 -> 1995 bytes
-rw-r--r--fuzzers/corpora/midx/f0a821dffe21afd357932febaf6e8ee331f53197bin0 -> 200 bytes
-rw-r--r--fuzzers/corpora/midx/f101a2fe93dfaaed1c596022b4e509cf3a591c8abin0 -> 186 bytes
-rw-r--r--fuzzers/corpora/midx/f1101f71657385174f8cb920026a761404b4395dbin0 -> 316 bytes
-rw-r--r--fuzzers/corpora/midx/f138c84e42d3cc61a219c4be9db791750f0541c81
-rw-r--r--fuzzers/corpora/midx/f1da273522bfff4a4971b4ffc31e365f60fdbbfebin0 -> 969 bytes
-rw-r--r--fuzzers/corpora/midx/f2fe69d30ec47e78a9e92f1423698a52270672b2bin0 -> 192 bytes
-rw-r--r--fuzzers/corpora/midx/f368bb6f633587a7bb271de7e20695f178c89686bin0 -> 2021 bytes
-rw-r--r--fuzzers/corpora/midx/f38ced5a16edaceb5f527ebc35e7870f42586c90bin0 -> 215 bytes
-rw-r--r--fuzzers/corpora/midx/f404371362ae68ffb2837ce1766346ebb645d173bin0 -> 71 bytes
-rw-r--r--fuzzers/corpora/midx/f473b5e1cf51502345f5c1840ec3948d308dd314bin0 -> 25 bytes
-rw-r--r--fuzzers/corpora/midx/f4ad43d6f913c3be6243dfc439e4b6f5b2e814b9bin0 -> 241 bytes
-rw-r--r--fuzzers/corpora/midx/f4cde4083a974d755a38bf5ea3820f78b576754abin0 -> 508 bytes
-rw-r--r--fuzzers/corpora/midx/f5888d0dcacda196d73772aabc18fe2ad6e1dfa2bin0 -> 179 bytes
-rw-r--r--fuzzers/corpora/midx/f5c3577a62d401f071d5edaa77c54ae98d6a03181
-rw-r--r--fuzzers/corpora/midx/f5dee0d9da0d6950069ac36b1880090a20f50f3ebin0 -> 96 bytes
-rw-r--r--fuzzers/corpora/midx/f605fff495fef2719585c706c05c350812402a35bin0 -> 397 bytes
-rw-r--r--fuzzers/corpora/midx/f6fdaf4e77e29c780b9e0b91637777575bebfab4bin0 -> 33 bytes
-rw-r--r--fuzzers/corpora/midx/f72700bd65fa86c588607ec50d60a9c684c86f43bin0 -> 258 bytes
-rw-r--r--fuzzers/corpora/midx/f777db12ef18f002febd5af13e2b948c95d964c3bin0 -> 69 bytes
-rw-r--r--fuzzers/corpora/midx/f777e9274d508e7ac1069e2a04bedc042942491cbin0 -> 92 bytes
-rw-r--r--fuzzers/corpora/midx/f81306f8ceaec3d06d5d34afa9769d15f0d209ebbin0 -> 528 bytes
-rw-r--r--fuzzers/corpora/midx/f84c2b36689f22809d9bda00febab557c381ffa4bin0 -> 259 bytes
-rw-r--r--fuzzers/corpora/midx/f98168fa74c26b17ad0c3002f2263beb0af7c0cebin0 -> 259 bytes
-rw-r--r--fuzzers/corpora/midx/fa6759d6a2807bbad83ba21761772c0119122c35bin0 -> 263 bytes
-rw-r--r--fuzzers/corpora/midx/fa8b927b25a67fa3d60b12c53ac365366cc2b52dbin0 -> 191 bytes
-rw-r--r--fuzzers/corpora/midx/fc5e4b78e59daebed1118389b57a386981f2430ebin0 -> 237 bytes
-rw-r--r--fuzzers/corpora/midx/fdaf408880429153cfcf5d978727cd7b84c3d60ebin0 -> 33 bytes
-rw-r--r--fuzzers/corpora/midx/fe50e7564a28683b24c57f8bcdcb3fbfa61f5c6abin0 -> 583 bytes
-rw-r--r--fuzzers/corpora/midx/fe64b998872d3ad87df2019173ddc52686841d7dbin0 -> 1818 bytes
-rw-r--r--fuzzers/corpora/midx/fec56c7cc86871aaa9c7a947c4084307cac2778dbin0 -> 219 bytes
-rw-r--r--fuzzers/corpora/midx/ff164dfc56dd28709488130dc6dfc17406bf9e9dbin0 -> 811 bytes
-rw-r--r--fuzzers/corpora/midx/ff7035b3c055718728a6025b3cdf55c34c4c744bbin0 -> 251 bytes
-rw-r--r--fuzzers/corpora/objects/blob359
-rw-r--r--fuzzers/corpora/objects/commit20
-rw-r--r--fuzzers/corpora/objects/tag6
-rw-r--r--fuzzers/corpora/objects/treebin0 -> 1177 bytes
-rw-r--r--fuzzers/corpora/packfile/004bd06c91c0dc8ab7e963f4b5e87be00292911e1
-rw-r--r--fuzzers/corpora/packfile/00b67414c7b17916b3bd0a3d02284937fa0c4378bin0 -> 2 bytes
-rw-r--r--fuzzers/corpora/packfile/02eaeb43f0ec7dbfd91bd75e7ddcc7fd590dbc77bin0 -> 13 bytes
-rw-r--r--fuzzers/corpora/packfile/02f4286569be24124d8ab209733b7492f7560310bin0 -> 33 bytes
-rw-r--r--fuzzers/corpora/packfile/037ba5f9d6d695aa4739810f8bea6e795c1d7614bin0 -> 37 bytes
-rw-r--r--fuzzers/corpora/packfile/038e06289ac876f109fc12ca4b8284497ca26821bin0 -> 32 bytes
-rw-r--r--fuzzers/corpora/packfile/042dc4512fa3d391c5170cf3aa61e6a638f843421
-rw-r--r--fuzzers/corpora/packfile/044bf19babf3f9cde07adbfa2a45c7508538cbe8bin0 -> 55 bytes
-rw-r--r--fuzzers/corpora/packfile/044e12ea43bee3c4befe27ba4687bee98d505fd7bin0 -> 35 bytes
-rw-r--r--fuzzers/corpora/packfile/061fb208431db793bbd3645b7a16058a1e2a24121
-rw-r--r--fuzzers/corpora/packfile/06576556d1ad802f247cad11ae748be47b70cd9c1
-rw-r--r--fuzzers/corpora/packfile/06ceea0c98756d302c302410fffe0dc54a055486bin0 -> 18 bytes
-rw-r--r--fuzzers/corpora/packfile/071e65ac0bf08f2424a89a4a499004c1bb9f3f6cbin0 -> 112 bytes
-rw-r--r--fuzzers/corpora/packfile/0739ff2f064568a4d775c8061958e66c419dbea0bin0 -> 111 bytes
-rw-r--r--fuzzers/corpora/packfile/077760469bf8392342d09329c732b98d24be2c30bin0 -> 47 bytes
-rw-r--r--fuzzers/corpora/packfile/08534f33c201a45017b502e90a800f1b708ebcb31
-rw-r--r--fuzzers/corpora/packfile/09e9046a7d6125cf2915a326a1504dd75d0543b51
-rw-r--r--fuzzers/corpora/packfile/0ade7c2cf97f75d009975f4d720d1fa6c19f48971
-rw-r--r--fuzzers/corpora/packfile/0c316c67c1450aee57ffa3f74c19ea5d32d44622bin0 -> 36 bytes
-rw-r--r--fuzzers/corpora/packfile/0c395c44e4dd5b67caae8a98a66741e17e8af136bin0 -> 124 bytes
-rw-r--r--fuzzers/corpora/packfile/0cb9120e5ae00b0d660a111ef85fc9194a5f244abin0 -> 27 bytes
-rw-r--r--fuzzers/corpora/packfile/0d44e7156d04cd269fd1219c58c3b79dc8c41d60bin0 -> 20 bytes
-rw-r--r--fuzzers/corpora/packfile/0d77a48bea1dde6e5d391a65456dc0aa3d9cc5e3bin0 -> 15 bytes
-rw-r--r--fuzzers/corpora/packfile/0db25107ff248616cadc75b7819b21d06394cf25bin0 -> 74 bytes
-rw-r--r--fuzzers/corpora/packfile/0debae2db7ef2933f386bac21a2d3bebb473070ebin0 -> 17 bytes
-rw-r--r--fuzzers/corpora/packfile/0e2d48524de33394ca82ea3a43f5f04aac6e86c7bin0 -> 93 bytes
-rw-r--r--fuzzers/corpora/packfile/0e49f6aa78f3b2f6c3fa5d281d5b1052fa9951dcbin0 -> 16 bytes
-rw-r--r--fuzzers/corpora/packfile/0f2982027f0b3b05250267b19e3969f8797e389e1
-rw-r--r--fuzzers/corpora/packfile/11f6ad8ec52a2984abaafd7c3b516503785c20721
-rw-r--r--fuzzers/corpora/packfile/123ca693d81a8cfd99760ff5ca9e152ded878537bin0 -> 33 bytes
-rw-r--r--fuzzers/corpora/packfile/12878ca5643ab15a4a847e74ddd84fb365736af2bin0 -> 83 bytes
-rw-r--r--fuzzers/corpora/packfile/13f292a24a9e79ae911f5d5e1ef7db0112601e64bin0 -> 55 bytes
-rw-r--r--fuzzers/corpora/packfile/13facd9b4b5b4509fee92c7ccc1c82ed90624172bin0 -> 124 bytes
-rw-r--r--fuzzers/corpora/packfile/140092a21903fdc56c98de126725fa6bead98ab1bin0 -> 33 bytes
-rw-r--r--fuzzers/corpora/packfile/1489f923c4dca729178b3e3233458550d8dddf29bin0 -> 2 bytes
-rw-r--r--fuzzers/corpora/packfile/1501a58834f24f95442f190653a50dd67d01e19dbin0 -> 59 bytes
-rw-r--r--fuzzers/corpora/packfile/15eddee57cafb11e927810d62230a6e104de1d5cbin0 -> 19 bytes
-rw-r--r--fuzzers/corpora/packfile/1632aa4b049f1118306485b11c70c499a0200dd5bin0 -> 25 bytes
-rw-r--r--fuzzers/corpora/packfile/18e1cf33b179a5cbaaf0baac8279ec4ed1cbdcf3bin0 -> 238 bytes
-rw-r--r--fuzzers/corpora/packfile/18e768865207e0b90047487b66532b20bc74b1a2bin0 -> 23 bytes
-rw-r--r--fuzzers/corpora/packfile/1940c66b45a3bd5c847330b207fd87aee6e96194bin0 -> 47 bytes
-rw-r--r--fuzzers/corpora/packfile/1966ab31dc80ab75196b0cbf28e3960a0eb3f6c5bin0 -> 18 bytes
-rw-r--r--fuzzers/corpora/packfile/19da91f2603889267dfd77786e07a5b8f067d62a1
-rw-r--r--fuzzers/corpora/packfile/1a72795a3dffdfc999b030d9aab7237dea1e2bc1bin0 -> 27 bytes
-rw-r--r--fuzzers/corpora/packfile/1e29cf67a66f225b338610fbcdf1b8185a8f5b7dbin0 -> 13 bytes
-rw-r--r--fuzzers/corpora/packfile/1eb8977ef8c3be9ee896d785663c762c7e32be28bin0 -> 93 bytes
-rw-r--r--fuzzers/corpora/packfile/1f0837530c1c3d122157f2eaa9c2178dcc3580dfbin0 -> 93 bytes
-rw-r--r--fuzzers/corpora/packfile/1f3c5fd6dc091faa397bce776aa97b457388fdaebin0 -> 17 bytes
-rw-r--r--fuzzers/corpora/packfile/20528983163f834108150a7191600ff94ae2c1d2bin0 -> 33 bytes
-rw-r--r--fuzzers/corpora/packfile/20a725140a8ffbe11bb71c1b83f19452147e5180bin0 -> 51 bytes
-rw-r--r--fuzzers/corpora/packfile/2149aa9e07dda9bbf502e088d8d0a38e8fb94f2e1
-rw-r--r--fuzzers/corpora/packfile/21b664aa8c86aaee4007d9bdbc2d63bf82bd5109bin0 -> 35 bytes
-rw-r--r--fuzzers/corpora/packfile/21b9ec8a7d7ac4d542c9bf7b2e26581cfcfaaab6bin0 -> 18 bytes
-rw-r--r--fuzzers/corpora/packfile/21c07e2affed6b0134d5dc28ea6c4937e691c761bin0 -> 64 bytes
-rw-r--r--fuzzers/corpora/packfile/23841d4076641ebcb4f58d1fd03047528c9d359bbin0 -> 54 bytes
-rw-r--r--fuzzers/corpora/packfile/23b9174c42560de6525b1f103125f599479f95cbbin0 -> 111 bytes
-rw-r--r--fuzzers/corpora/packfile/241cbd6dfb6e53c43c73b62f9384359091dcbf561
-rw-r--r--fuzzers/corpora/packfile/245a2ddea41e6531944933c4420b8c9790ac351bbin0 -> 20 bytes
-rw-r--r--fuzzers/corpora/packfile/2541e340271ea496a392870bcc20d3510287b9e9bin0 -> 146 bytes
-rw-r--r--fuzzers/corpora/packfile/276af22d5bf94344737fb1a5eb5de7d335004493bin0 -> 94 bytes
-rw-r--r--fuzzers/corpora/packfile/27d5482eebd075de44389774fce28c69f45c8a751
-rw-r--r--fuzzers/corpora/packfile/28334bd612cb539de776370995f60c8032215434bin0 -> 338 bytes
-rw-r--r--fuzzers/corpora/packfile/2973e2ac092cba077d7350bfffe1cf2e0644a6e1bin0 -> 111 bytes
-rw-r--r--fuzzers/corpora/packfile/2adcd01e876b12d867c858ffaec38c42c59c36c7bin0 -> 59 bytes
-rw-r--r--fuzzers/corpora/packfile/2b28470644f5d0323643da99c831d82f20a7a74fbin0 -> 124 bytes
-rw-r--r--fuzzers/corpora/packfile/2b86229020ba808df84e16f800dc152254f18f64bin0 -> 19 bytes
-rw-r--r--fuzzers/corpora/packfile/2cc5bf2f780cd85ad93d232890f418625f4d12741
-rw-r--r--fuzzers/corpora/packfile/2d6ae8fa82b656879dd3371d0a6899e88ef34e76bin0 -> 124 bytes
-rw-r--r--fuzzers/corpora/packfile/2e74d24e887678f0681d4c7c010477b8b9697f1a1
-rw-r--r--fuzzers/corpora/packfile/2f436c68a7b0be43aa6d4ad5126ec9401a9f9211bin0 -> 627 bytes
-rw-r--r--fuzzers/corpora/packfile/2fec48b0dcb45b98597bfec12bf0dc650543b3e3bin0 -> 187 bytes
-rw-r--r--fuzzers/corpora/packfile/31bd25636a9807d6024e78b9b3d02fbb1a02835e1
-rw-r--r--fuzzers/corpora/packfile/323c88be36ecc20ff30b21cf417106554042db04bin0 -> 14 bytes
-rw-r--r--fuzzers/corpora/packfile/33b3aa957ca4fb31873033a7f460617f1fe81e32bin0 -> 93 bytes
-rw-r--r--fuzzers/corpora/packfile/34303d14e37c9ddfb0bad130e006fec927e13785bin0 -> 14 bytes
-rw-r--r--fuzzers/corpora/packfile/34dac9466a4a2c15aaeef13a9567f6827ace7748bin0 -> 13 bytes
-rw-r--r--fuzzers/corpora/packfile/356a192b7913b04c54574d18c28d46e6395428ab1
-rw-r--r--fuzzers/corpora/packfile/35bf585248be2c6d3940e15b85f72c4146903097bin0 -> 104 bytes
-rw-r--r--fuzzers/corpora/packfile/3725a1c4431714019827277deac8ec2efeed8f1dbin0 -> 21 bytes
-rw-r--r--fuzzers/corpora/packfile/37a2b7de1fadc9eab2d5fabf5dfe7007c245dbeebin0 -> 112 bytes
-rw-r--r--fuzzers/corpora/packfile/37ab8a0ca81fc62279057401761f7730a5a8f1b2bin0 -> 16 bytes
-rw-r--r--fuzzers/corpora/packfile/38011be20a664dcd2594e712a26c063c2d50efcdbin0 -> 338 bytes
-rw-r--r--fuzzers/corpora/packfile/3838851a5da8239c2ae6cbbe869c81446c720e87bin0 -> 16 bytes
-rw-r--r--fuzzers/corpora/packfile/3921322ac01429b001f88d64c8319088fe49218ebin0 -> 84 bytes
-rw-r--r--fuzzers/corpora/packfile/395df8f7c51f007019cb30201c49e884b46b92fa1
-rw-r--r--fuzzers/corpora/packfile/3e98eb4fd65d3f2c41fa979db0f5678b310e51febin0 -> 46 bytes
-rw-r--r--fuzzers/corpora/packfile/3f9ec359d0cb573cb6d2b2df64c9f4048ea298b8bin0 -> 32 bytes
-rw-r--r--fuzzers/corpora/packfile/4067250457728bf775aa310ef253b223ae2fe4dcbin0 -> 82 bytes
-rw-r--r--fuzzers/corpora/packfile/40818db87e110b29cb864f73265586cc054f5bbbbin0 -> 58 bytes
-rw-r--r--fuzzers/corpora/packfile/418f9fb9ce1d4efdf481ca8fff9dadd09caee9fcbin0 -> 55 bytes
-rw-r--r--fuzzers/corpora/packfile/41ca0ae865b686089b8d99e9d661da291ce51019bin0 -> 93 bytes
-rw-r--r--fuzzers/corpora/packfile/42099b4af021e53fd8fd4e056c2568d7c2e3ffa81
-rw-r--r--fuzzers/corpora/packfile/420ce645ce1c93cee59a06da2159cbbb251e4c01bin0 -> 13 bytes
-rw-r--r--fuzzers/corpora/packfile/4345cb1fa27885a8fbfe7c0c830a592cc76a552b1
-rw-r--r--fuzzers/corpora/packfile/450718a71a93a1b5ff982595432400b0fa876fb6bin0 -> 1779 bytes
-rw-r--r--fuzzers/corpora/packfile/453a312eb77b9d4198ac62faef10ecf3e283120cbin0 -> 34 bytes
-rw-r--r--fuzzers/corpora/packfile/45470317334b614ce4d119c05ed2d6250dbc6061bin0 -> 93 bytes
-rw-r--r--fuzzers/corpora/packfile/45a65193e30784b0124f4fed659eb7e46552c2d01
-rw-r--r--fuzzers/corpora/packfile/45cff3494791ded181e1e3dab1c7a0e40130b57bbin0 -> 37 bytes
-rw-r--r--fuzzers/corpora/packfile/481dedc2855981510024d48828af0fe35a1503ddbin0 -> 36 bytes
-rw-r--r--fuzzers/corpora/packfile/49a6448a722742b1b392f0471542ee0c572c5a9abin0 -> 13 bytes
-rw-r--r--fuzzers/corpora/packfile/4a6e6af93dea13a5720be52b95f2948e0cab4602bin0 -> 60 bytes
-rw-r--r--fuzzers/corpora/packfile/4ac25548f35e06eb9f91b0f15b89db3cb5bcb283bin0 -> 83 bytes
-rw-r--r--fuzzers/corpora/packfile/4b586169f192749a0aa023ad6e4edd2e15d67f53bin0 -> 12 bytes
-rw-r--r--fuzzers/corpora/packfile/4c3c8ec0e25da9342dc87c2e78d3040c381514cebin0 -> 2 bytes
-rw-r--r--fuzzers/corpora/packfile/4d5189cd1411daaa579df34591c6a5946204c9a0bin0 -> 33 bytes
-rw-r--r--fuzzers/corpora/packfile/4d7f1bfa928c0d3399598d562e346c6e23de6a03bin0 -> 14 bytes
-rw-r--r--fuzzers/corpora/packfile/4eee38183d6fce3f42224738be58d0e3975300f4bin0 -> 36 bytes
-rw-r--r--fuzzers/corpora/packfile/4f2e2af611d6567abcf5b6bfc579487ac417a8d4bin0 -> 20 bytes
-rw-r--r--fuzzers/corpora/packfile/4fa04b2c3ac839c36c4a3b51bf882eb99b7cd097bin0 -> 14 bytes
-rw-r--r--fuzzers/corpora/packfile/4fbe10aede9fd9ce2030c6e567a9281e1a5338f4bin0 -> 124 bytes
-rw-r--r--fuzzers/corpora/packfile/5037f4f74273aed9a09122af5f4acc10f42c033abin0 -> 28 bytes
-rw-r--r--fuzzers/corpora/packfile/511993d3c99719e38a6779073019dacd7178ddb91
-rw-r--r--fuzzers/corpora/packfile/520aa436eab6343c3729f51f0f8048e6b87f6aeb1
-rw-r--r--fuzzers/corpora/packfile/521e228f3b62dca81d87d2e7d5476657d7b5e0a9bin0 -> 13 bytes
-rw-r--r--fuzzers/corpora/packfile/52e37dfd77d56769dc8a96388aa26695a8108dacbin0 -> 34 bytes
-rw-r--r--fuzzers/corpora/packfile/5374fb6be0a406cf8d0e95771ecb032254d21305bin0 -> 137 bytes
-rw-r--r--fuzzers/corpora/packfile/53e1d4898c15c8ee3ef5e2fb279d151108725731bin0 -> 100 bytes
-rw-r--r--fuzzers/corpora/packfile/53e61ad37ca92b7f6c0396e5fab1ed8743e73da0bin0 -> 85 bytes
-rw-r--r--fuzzers/corpora/packfile/55df2a59ed6a888ee2f0cdfdcc8582696702de7a1
-rw-r--r--fuzzers/corpora/packfile/56a2020f68c5eb72786ea168cc2a7e8ea34ad9c2bin0 -> 24 bytes
-rw-r--r--fuzzers/corpora/packfile/578678e78b72f8bcb9f414e4129ae5d85a4af914bin0 -> 31 bytes
-rw-r--r--fuzzers/corpora/packfile/5a8bc5597fd0b2b44194ffabce46e2fa94c1ffd7bin0 -> 111 bytes
-rw-r--r--fuzzers/corpora/packfile/5bab61eb53176449e25c2c82f172b82cb13ffb9d1
-rw-r--r--fuzzers/corpora/packfile/5c2dd944dde9e08881bef0894fe7b22a5c9c4b061
-rw-r--r--fuzzers/corpora/packfile/5cb4674f4468d39f061d1df3c95b9c2dca529c54bin0 -> 7 bytes
-rw-r--r--fuzzers/corpora/packfile/60b6fbfe65dc1796a09a734e054223aba8c90260bin0 -> 13 bytes
-rw-r--r--fuzzers/corpora/packfile/611f5b9368427ef823f7ed89ad23667b02a06435bin0 -> 48 bytes
-rw-r--r--fuzzers/corpora/packfile/6214b4afdbfe63400ce428d47a58a2e29f682738bin0 -> 338 bytes
-rw-r--r--fuzzers/corpora/packfile/634b675b80d51b52c3c6fbc73181ed47f61749babin0 -> 98 bytes
-rw-r--r--fuzzers/corpora/packfile/6431f1b31dc492fad89732b7d3e511fa7361985dbin0 -> 136 bytes
-rw-r--r--fuzzers/corpora/packfile/6442fd4bbb7656f142c92050da17b0e81e79fad1bin0 -> 366 bytes
-rw-r--r--fuzzers/corpora/packfile/6486c8cf6bcc2fca60502564924f6b266399df3dbin0 -> 21 bytes
-rw-r--r--fuzzers/corpora/packfile/651c573b6fdd393e97536a47f8b9e65226e29c33bin0 -> 77 bytes
-rw-r--r--fuzzers/corpora/packfile/657fc646e93cb999417f43f0ec77fbad694e3e18bin0 -> 124 bytes
-rw-r--r--fuzzers/corpora/packfile/65cc90263dec0020ceabc727d33aa587e57fc1751
-rw-r--r--fuzzers/corpora/packfile/688934845f22049cb14668832efa33d45013b6b9bin0 -> 2 bytes
-rw-r--r--fuzzers/corpora/packfile/6b0d31c0d563223024da45691584643ac78c96e81
-rw-r--r--fuzzers/corpora/packfile/6b4dc6028a3a684a20dbc427b15a37ea2fd12dd1bin0 -> 43 bytes
-rw-r--r--fuzzers/corpora/packfile/6b7486fc2a47a40eb5a85a5edf53af60d48be7d5bin0 -> 14 bytes
-rw-r--r--fuzzers/corpora/packfile/6bace82ea640ac0a78963c79483faf0faa7fd1681
-rw-r--r--fuzzers/corpora/packfile/6ca38da5f096a5847776e4d50cb63121341fd67cbin0 -> 15 bytes
-rw-r--r--fuzzers/corpora/packfile/6d344a65b9edced36045f94215b6810799789334bin0 -> 13 bytes
-rw-r--r--fuzzers/corpora/packfile/6dd655e8ef0573eb1c41151af924974aa1e9c454bin0 -> 13 bytes
-rw-r--r--fuzzers/corpora/packfile/6e118259c2940fafba0a9ceeef6308e12e881ae1bin0 -> 111 bytes
-rw-r--r--fuzzers/corpora/packfile/6e4b5ef83333606a16a63b579f221f6ffb7b48ee1
-rw-r--r--fuzzers/corpora/packfile/6f47ff60d54c012103a0c28851ffa9eab3002511bin0 -> 59 bytes
-rw-r--r--fuzzers/corpora/packfile/701a765befff451207517d56c3fe8609d059867dbin0 -> 15 bytes
-rw-r--r--fuzzers/corpora/packfile/7050f56d64b28499c89d5430761f18a8a2a933d4bin0 -> 60 bytes
-rw-r--r--fuzzers/corpora/packfile/724fa0194f615e1a0f08184a9f1520123f8e2833bin0 -> 93 bytes
-rw-r--r--fuzzers/corpora/packfile/72c52d0d98717e21dfee45418a046a19198b5d5dbin0 -> 17 bytes
-rw-r--r--fuzzers/corpora/packfile/72cec0949c5743ee1df67b41ece5d6806f9bede6bin0 -> 173 bytes
-rw-r--r--fuzzers/corpora/packfile/72e6bfb7b881befc0b461334411d70ae227a426abin0 -> 22 bytes
-rw-r--r--fuzzers/corpora/packfile/73b74736664ad85828ce1be2e29fb4a68d24402b1
-rw-r--r--fuzzers/corpora/packfile/745bedb79413d20844a8b0e96fbec51b4989c65d1
-rw-r--r--fuzzers/corpora/packfile/748142c17e56d0f0ad9e4d6525b39294d81261d6bin0 -> 59 bytes
-rw-r--r--fuzzers/corpora/packfile/74dfea2e26741a8778fb942d1d60a84d0759d7a01
-rw-r--r--fuzzers/corpora/packfile/767b2efbb7502a2f652a448b471693d32c128677bin0 -> 93 bytes
-rw-r--r--fuzzers/corpora/packfile/78abe558c4277852128d4b91282edcb68f86bdeabin0 -> 48 bytes
-rw-r--r--fuzzers/corpora/packfile/7960246c2db6d39e68dfe93ded358a3acba8f896bin0 -> 18 bytes
-rw-r--r--fuzzers/corpora/packfile/7a4ff814176b55af008ad9580201d5e659242f05bin0 -> 59 bytes
-rw-r--r--fuzzers/corpora/packfile/7a752694fce29ded083fbabbc9ff95f5b92a3d9cbin0 -> 26 bytes
-rw-r--r--fuzzers/corpora/packfile/7a81af3e591ac713f81ea1efe93dcf36157d83761
-rw-r--r--fuzzers/corpora/packfile/7c957a1fd650f9ae0eadc112837ea451a692affcbin0 -> 34 bytes
-rw-r--r--fuzzers/corpora/packfile/7cda4ab6a0daf50f675d5225cbc166c86a8ef95fbin0 -> 29 bytes
-rw-r--r--fuzzers/corpora/packfile/7cf184f4c67ad58283ecb19349720b0cae7568291
-rw-r--r--fuzzers/corpora/packfile/7df1ea8d86d601c3bd39977ea85e5f74c9db6acbbin0 -> 15 bytes
-rw-r--r--fuzzers/corpora/packfile/820ec3e39089d863641a1be6942445db3ff34270bin0 -> 48 bytes
-rw-r--r--fuzzers/corpora/packfile/827704fd978bd02a46268b7396b202a52ad261edbin0 -> 34 bytes
-rw-r--r--fuzzers/corpora/packfile/828acfc1d49a0fdbcd9f238138ff65582c2a9fc8bin0 -> 14 bytes
-rw-r--r--fuzzers/corpora/packfile/8306a2f04a47fe4c95098675ffa25c074ecd89debin0 -> 14 bytes
-rw-r--r--fuzzers/corpora/packfile/8327db1c6a884d8b3e3cefd94cec9eb94bffae0abin0 -> 111 bytes
-rw-r--r--fuzzers/corpora/packfile/847f4e42f8f2730a48d19951d8829621b2e70082bin0 -> 124 bytes
-rw-r--r--fuzzers/corpora/packfile/84a516841ba77a5b4648de2cd0dfcb30ea46dbb41
-rw-r--r--fuzzers/corpora/packfile/8552526f5aba95119c0b95b61cd40386e7a3738bbin0 -> 34 bytes
-rw-r--r--fuzzers/corpora/packfile/8565db62ac64209ff009ac40e7c2d2ac4ae944ebbin0 -> 32 bytes
-rw-r--r--fuzzers/corpora/packfile/859b3346967c5c3c136459e565b402f9a936aa0dbin0 -> 129 bytes
-rw-r--r--fuzzers/corpora/packfile/86a69caf3c5866d78d80da087b1b843cfea5e907bin0 -> 48 bytes
-rw-r--r--fuzzers/corpora/packfile/86e1fb54a04fc18ee482b794ba3b2b306f6515d4bin0 -> 31 bytes
-rw-r--r--fuzzers/corpora/packfile/86f217ee467d31ad9ad2a8c502b91279cd7f1c40bin0 -> 36 bytes
-rw-r--r--fuzzers/corpora/packfile/8768a53e1d4c182907306300f9ca90cfd80183831
-rw-r--r--fuzzers/corpora/packfile/8834a7aac170c494f45aa4da71b9605a52d82435bin0 -> 13 bytes
-rw-r--r--fuzzers/corpora/packfile/883f023f38a031d8a8e8ce2cba6614b9bff5d41fbin0 -> 17 bytes
-rw-r--r--fuzzers/corpora/packfile/88738071086eb04e47b77d1ca28b35ddbfaa0968bin0 -> 96 bytes
-rw-r--r--fuzzers/corpora/packfile/892aef744c87c6ee4ba3dd457c7ca02ba3d359bdbin0 -> 13 bytes
-rw-r--r--fuzzers/corpora/packfile/8afb5c282d23c4055500f88a10b26383c682c900bin0 -> 23 bytes
-rw-r--r--fuzzers/corpora/packfile/8b3dfce4cd7b8942eedb52af0e9ca4caa5c6de61bin0 -> 10 bytes
-rw-r--r--fuzzers/corpora/packfile/8c2cccf751bb5844bea8dc63c22e3f8e4489411ebin0 -> 27 bytes
-rw-r--r--fuzzers/corpora/packfile/8e30894298502ba4d43af98f1ec3088f9b8f29d5bin0 -> 59 bytes
-rw-r--r--fuzzers/corpora/packfile/8eb4d738f7170d2e0594b779f782eb3171c9d421bin0 -> 49 bytes
-rw-r--r--fuzzers/corpora/packfile/8f46a043da3aa5d466ade170e62b0b9f362b4c5bbin0 -> 20 bytes
-rw-r--r--fuzzers/corpora/packfile/9295f39686016bf3abb1d6e9924d6725c1263920bin0 -> 23 bytes
-rw-r--r--fuzzers/corpora/packfile/92fa2c2237724e7ba49e9c59adad8d61ce400bbfbin0 -> 124 bytes
-rw-r--r--fuzzers/corpora/packfile/936548b53e1a1e30cb30747a87203abd4eae78eabin0 -> 15 bytes
-rw-r--r--fuzzers/corpora/packfile/9835ad3ff27939bc1315632d6a22980b377c36e41
-rw-r--r--fuzzers/corpora/packfile/9857740c36a95415fa3be04cdf184db7b41a8b3ebin0 -> 16 bytes
-rw-r--r--fuzzers/corpora/packfile/98c35b9d5e7b430d0d4ef70f642d8e2f3266b6d4bin0 -> 93 bytes
-rw-r--r--fuzzers/corpora/packfile/9929b50ac145c0781a0347be1559764edc6681731
-rw-r--r--fuzzers/corpora/packfile/9bf6a450d87badf2d495c2df37081ea16156915abin0 -> 13 bytes
-rw-r--r--fuzzers/corpora/packfile/9bffb3ff7a4429144305b770162074bbffe39ce9bin0 -> 319 bytes
-rw-r--r--fuzzers/corpora/packfile/9c040d3207196e3aeee0df389170d6e59733ba0fbin0 -> 112 bytes
-rw-r--r--fuzzers/corpora/packfile/9c740d0f3b8875a3b19f1cf1a88d5192a997a68dbin0 -> 62 bytes
-rw-r--r--fuzzers/corpora/packfile/9cf72097400efb70d06179e6b00abb4cdec74e66bin0 -> 27 bytes
-rw-r--r--fuzzers/corpora/packfile/9d36c270ef1f14214742562238dc747242d4756ebin0 -> 15 bytes
-rw-r--r--fuzzers/corpora/packfile/9fb415ccadc8e7b0f38646ec5782d5895111e259bin0 -> 13 bytes
-rw-r--r--fuzzers/corpora/packfile/a13b7fbb454fe3bdebd07a51d466484aa41ee142bin0 -> 59 bytes
-rw-r--r--fuzzers/corpora/packfile/a1a7715c7596c77b892dc6d4debb7c108ca4ef971
-rw-r--r--fuzzers/corpora/packfile/a1ac8b656af02b56aefe6029db36b1af9fb664efbin0 -> 20 bytes
-rw-r--r--fuzzers/corpora/packfile/a343687e2522222c2d49e8cb18d3feda64cf1d66bin0 -> 17 bytes
-rw-r--r--fuzzers/corpora/packfile/a6f57425137e9aa54537f0b3f5364ce165aedb0a1
-rw-r--r--fuzzers/corpora/packfile/a73df4ce29f75cc638a7a2d823fee57d909ab681bin0 -> 59 bytes
-rw-r--r--fuzzers/corpora/packfile/a7ee38bb7be4fc44198cb2685d9601dcf2b9f5691
-rw-r--r--fuzzers/corpora/packfile/a8b9b91157274e617bf4ac5045fc0c6ac97e76f7bin0 -> 111 bytes
-rw-r--r--fuzzers/corpora/packfile/a9c697f383f59a3b0642cd55b88190bce6201baebin0 -> 95 bytes
-rw-r--r--fuzzers/corpora/packfile/ab064cd6847c0fa546bbec4241eb9b095e0e73dabin0 -> 39 bytes
-rw-r--r--fuzzers/corpora/packfile/ab2c64588d3d9dc5c54c48d414e6d46d6a78cfa6bin0 -> 93 bytes
-rw-r--r--fuzzers/corpora/packfile/abe729b06750880778312618dcebb43257ec03e0bin0 -> 15 bytes
-rw-r--r--fuzzers/corpora/packfile/ac1bf5a5fe61e5784f72b364ef1bcddfb0d13716bin0 -> 18 bytes
-rw-r--r--fuzzers/corpora/packfile/ac47b6d3f0e479df3292131535f8e0d99c288de9bin0 -> 37 bytes
-rw-r--r--fuzzers/corpora/packfile/ac9231da4082430afe8f4d40127814c613648d8e1
-rw-r--r--fuzzers/corpora/packfile/ace9ffcaa273c01c0eb60381321e47edf4842332bin0 -> 59 bytes
-rw-r--r--fuzzers/corpora/packfile/ad6ba9b0bc076987efbeb11ce3fc92bc1df69d0abin0 -> 13 bytes
-rw-r--r--fuzzers/corpora/packfile/ae99dcb9b5e1b09aa5df6bb2fada3a3de61268febin0 -> 9 bytes
-rw-r--r--fuzzers/corpora/packfile/aeeacf0499ace69549fe2c76757d4948ba65a10bbin0 -> 59 bytes
-rw-r--r--fuzzers/corpora/packfile/af6614c37604ee5d3f7b00cddca761a8776283b5bin0 -> 27 bytes
-rw-r--r--fuzzers/corpora/packfile/afd44f8c385a922c8caacc1ea5688d324bad5b39bin0 -> 15 bytes
-rw-r--r--fuzzers/corpora/packfile/aff024fe4ab0fece4091de044c58c9ae4233383a1
-rw-r--r--fuzzers/corpora/packfile/b1f86f05d4928c8393fe0f138c0714df3978f0bbbin0 -> 19 bytes
-rw-r--r--fuzzers/corpora/packfile/b452cd4b70f2827e3cbd6d5dd20f678b6e55f813bin0 -> 90 bytes
-rw-r--r--fuzzers/corpora/packfile/b491dbad4c3edc87aa5a7f12b2c9a447a712c20dbin0 -> 20 bytes
-rw-r--r--fuzzers/corpora/packfile/b54664965911c6fe91e18cd01b68a75c8183b5301
-rw-r--r--fuzzers/corpora/packfile/b68542373c05c0ed25231d09955b2c699d37c45b1
-rw-r--r--fuzzers/corpora/packfile/b706e78cf7110a78dfccce991cd4ce22c6fd898abin0 -> 60 bytes
-rw-r--r--fuzzers/corpora/packfile/b8d3910a75ad8a7058f9c3f202f8eb27419137d7bin0 -> 24 bytes
-rw-r--r--fuzzers/corpora/packfile/b93abe6094fb4ebbfa7414fbceb7199ce766075bbin0 -> 12 bytes
-rw-r--r--fuzzers/corpora/packfile/b9a64cc0694f3ac4a3c530c721bbf69026192187bin0 -> 47 bytes
-rw-r--r--fuzzers/corpora/packfile/b9e5319eca8fbc26e5c322e0b151ed8ed60628d1bin0 -> 25 bytes
-rw-r--r--fuzzers/corpora/packfile/ba390745a04c5394601f7aa73fe795097b814d1abin0 -> 34 bytes
-rw-r--r--fuzzers/corpora/packfile/bb7d065b776833337d3e1a3071de4d5d2759d78b1
-rw-r--r--fuzzers/corpora/packfile/bb99cf0bb3e5d75d59300e6ca9cb1f67ce315e3abin0 -> 33 bytes
-rw-r--r--fuzzers/corpora/packfile/bd9722d91e0615cbdae3cee3476ec6181fbad98dbin0 -> 14 bytes
-rw-r--r--fuzzers/corpora/packfile/bf8b4530d8d246dd74ac53a13471bba17941dff71
-rw-r--r--fuzzers/corpora/packfile/bffc4698ad4aaddd977fe857da20858aa6654263bin0 -> 34 bytes
-rw-r--r--fuzzers/corpora/packfile/c0ea828d8f9c4a2c0fc6253908cd283f6c7994a1bin0 -> 55 bytes
-rw-r--r--fuzzers/corpora/packfile/c151b760696d665265187501c51f38cd845036341
-rw-r--r--fuzzers/corpora/packfile/c16f90096603258174790bc85f076413dad0e228bin0 -> 81 bytes
-rw-r--r--fuzzers/corpora/packfile/c20fc8fb8f1d44050c281089191b8eac2dc9444cbin0 -> 18 bytes
-rw-r--r--fuzzers/corpora/packfile/c2143b1a0db17957bec1b41bb2e5f75aa135981e1
-rw-r--r--fuzzers/corpora/packfile/c22c3fba53bb2c5579b47852fa9ec54a88c03472bin0 -> 72 bytes
-rw-r--r--fuzzers/corpora/packfile/c297564cff1bb4f7933221050cfcffa36c59f691bin0 -> 33 bytes
-rw-r--r--fuzzers/corpora/packfile/c2c4da76233acd3efe08eaebb7ae8dc9b3036527bin0 -> 18 bytes
-rw-r--r--fuzzers/corpora/packfile/c3d47118536d19a8d1a601978510cc24344aa8dfbin0 -> 111 bytes
-rw-r--r--fuzzers/corpora/packfile/c4cbb032db94c57061003a85d30bdf4117979b1ebin0 -> 338 bytes
-rw-r--r--fuzzers/corpora/packfile/c7090127a03c0e7230c11a649e4f98fcb4ca2b75bin0 -> 108 bytes
-rw-r--r--fuzzers/corpora/packfile/c8b839665bd381ff7d006b1b08c35f94f1818556bin0 -> 19 bytes
-rw-r--r--fuzzers/corpora/packfile/ca8c7c16d1d6b60e951dcfb558cc97e14231c750bin0 -> 326 bytes
-rw-r--r--fuzzers/corpora/packfile/cb46c744c83541a0900e1e61780c18d43031a08b1
-rw-r--r--fuzzers/corpora/packfile/cbf1e454dc7ac878bd23e3dfd0b6a28a50b2155dbin0 -> 13 bytes
-rw-r--r--fuzzers/corpora/packfile/cbfb8cae82ddd82c04996f474fdb4f1b80dcb6dbbin0 -> 124 bytes
-rw-r--r--fuzzers/corpora/packfile/cf74f755c004ca634818f8ba44c99fffbaa950a1bin0 -> 37 bytes
-rw-r--r--fuzzers/corpora/packfile/d07e4bc786c88b8d2304f84c7db2098666f822c01
-rw-r--r--fuzzers/corpora/packfile/d17ab0db9edea68e8f9f51f471decae84b192a1abin0 -> 13 bytes
-rw-r--r--fuzzers/corpora/packfile/d1854cae891ec7b29161ccaf79a24b00c274bdaa1
-rw-r--r--fuzzers/corpora/packfile/d22aac18f8b435fc34566fe0d3f42464aec9458cbin0 -> 59 bytes
-rw-r--r--fuzzers/corpora/packfile/d446a50788c32053358495358696f9d595358bcfbin0 -> 124 bytes
-rw-r--r--fuzzers/corpora/packfile/d461cbcff85c87b0068f0e9c15d2056197cdfa52bin0 -> 29 bytes
-rw-r--r--fuzzers/corpora/packfile/d54709a1b46002c81f57da533379e57f00afe942bin0 -> 124 bytes
-rw-r--r--fuzzers/corpora/packfile/d567007f84b83e82df7069838bf8b6c5826b18a8bin0 -> 200 bytes
-rw-r--r--fuzzers/corpora/packfile/d5e60b9f94126a9ec865fda83feb6835d294b76bbin0 -> 44 bytes
-rw-r--r--fuzzers/corpora/packfile/d81092a4f3607ddbba85862facf2285459696078bin0 -> 62 bytes
-rw-r--r--fuzzers/corpora/packfile/d898eb860ceac044950605db88429e029ea278efbin0 -> 53 bytes
-rw-r--r--fuzzers/corpora/packfile/d8fc60ccdd8f555c1858b9f0820f263e3d2b58ec1
-rw-r--r--fuzzers/corpora/packfile/d9b69c63cdc0c1622f2fab84d1307f9e0c0fa3b9bin0 -> 48 bytes
-rw-r--r--fuzzers/corpora/packfile/db1bb4b7348d387623dcaf0a743d0b11fa18409fbin0 -> 15 bytes
-rw-r--r--fuzzers/corpora/packfile/db291360a195c79ae504a3dfb2cd0f71cbc11902bin0 -> 22 bytes
-rw-r--r--fuzzers/corpora/packfile/dc98359b3ef2ced9c3d07636c89d475a564c39d9bin0 -> 16 bytes
-rw-r--r--fuzzers/corpora/packfile/dccc5642917b20b4dd64d3e44b71d08da30445e9bin0 -> 16 bytes
-rw-r--r--fuzzers/corpora/packfile/dd79c8cfb8beeacd0460429944b4ecbe95a315611
-rw-r--r--fuzzers/corpora/packfile/de9550264c4e2dbef14e1281ff3693f2d19dc1c9bin0 -> 18 bytes
-rw-r--r--fuzzers/corpora/packfile/df8b4d163e9ed75634eb56467343bde73b13263ebin0 -> 21 bytes
-rw-r--r--fuzzers/corpora/packfile/e0184adedf913b076626646d3f52c3b49c39ad6d1
-rw-r--r--fuzzers/corpora/packfile/e0905bac594c818b9cfa909269114977c4d6d1b2bin0 -> 38 bytes
-rw-r--r--fuzzers/corpora/packfile/e0bcb16cd6b42128201e1b6454323175a7e412f0bin0 -> 111 bytes
-rw-r--r--fuzzers/corpora/packfile/e1ac9563c33f4f31b3e147b9d2fef80fca550948bin0 -> 16 bytes
-rw-r--r--fuzzers/corpora/packfile/e230c91352f1b07f6f34da803d07e75c06897b30bin0 -> 2 bytes
-rw-r--r--fuzzers/corpora/packfile/e26b3bacbfd6603d021d4ddadbac94b7b7aa00341
-rw-r--r--fuzzers/corpora/packfile/e2a6f8dc3dc5d6c859f19d6e0fa64745201df0a6bin0 -> 9 bytes
-rw-r--r--fuzzers/corpora/packfile/e2ba004118345660b379df5147bfa7a39d884dbcbin0 -> 27 bytes
-rw-r--r--fuzzers/corpora/packfile/e45aaf139d726366a18dce9e4854ee6c82901677bin0 -> 15 bytes
-rw-r--r--fuzzers/corpora/packfile/e4b3ab7e8c18de815fc8bd6ebfd5d52cf1924a8ebin0 -> 80 bytes
-rw-r--r--fuzzers/corpora/packfile/e6818b96c50bb749911248959af81a9c412a0223bin0 -> 26 bytes
-rw-r--r--fuzzers/corpora/packfile/e69f20e9f683920d3fb4329abd951e878b1f93721
-rw-r--r--fuzzers/corpora/packfile/e6eb439fef7d5461bc3552aa7c064d24e44c5f32bin0 -> 16 bytes
-rw-r--r--fuzzers/corpora/packfile/e9d9930dc3fea44fbc7acb0d1ef4bd867f1c902bbin0 -> 16 bytes
-rw-r--r--fuzzers/corpora/packfile/eb05b6ad73fb1f69ef750d0b9cb6c606ab9d949fbin0 -> 13 bytes
-rw-r--r--fuzzers/corpora/packfile/eb0814ae767e5f28b87c998b0f44dcf80814db1bbin0 -> 111 bytes
-rw-r--r--fuzzers/corpora/packfile/ebbd9763912dd557d08abd1373c867a4b56e6a41bin0 -> 38 bytes
-rw-r--r--fuzzers/corpora/packfile/ebcdcb7effcc3f06e0d503638ac621de877fc5541
-rw-r--r--fuzzers/corpora/packfile/eddccafb2716adafb9ad48203f0621bb00ebc73fbin0 -> 111 bytes
-rw-r--r--fuzzers/corpora/packfile/edfbf20c83d3cec45470105581f7dc8b7e0889dabin0 -> 34 bytes
-rw-r--r--fuzzers/corpora/packfile/f03218467b1c74e465cebb3b8092e21a5122f31dbin0 -> 53 bytes
-rw-r--r--fuzzers/corpora/packfile/f28600befd899a94bed8e62853e90655d614f439bin0 -> 13 bytes
-rw-r--r--fuzzers/corpora/packfile/f3b15185b7a9a10716752d58434fe656d839092ebin0 -> 26 bytes
-rw-r--r--fuzzers/corpora/packfile/f436ed7933482610e08e18b40e9eec102b63b7d4bin0 -> 17 bytes
-rw-r--r--fuzzers/corpora/packfile/f55ea5b7c1cf5400aae56d7faf65a42320d0323abin0 -> 14 bytes
-rw-r--r--fuzzers/corpora/packfile/f5eeab2d009aa4984378df6bfdd89366b7ecbb32bin0 -> 93 bytes
-rw-r--r--fuzzers/corpora/packfile/f6250c8b3cc0510e0f8f621100be83f018e2d234bin0 -> 38 bytes
-rw-r--r--fuzzers/corpora/packfile/f7168410c7158ff7331698930937f9cdc54f4d8abin0 -> 93 bytes
-rw-r--r--fuzzers/corpora/packfile/f91847640af285c1b8a6df27f5c50686ed0deb70bin0 -> 20 bytes
-rw-r--r--fuzzers/corpora/packfile/fa58a6b2d3286a136a43afeeaac589d2dde0a2a6bin0 -> 111 bytes
-rw-r--r--fuzzers/corpora/packfile/fe3667be5fd2ffdd553ae04a534a2e9ce5445188bin0 -> 34 bytes
-rw-r--r--fuzzers/corpora/packfile/ff21cad92ddd105224408fa696e91080a8cf98fbbin0 -> 38 bytes
-rw-r--r--fuzzers/corpora/packfile/ff9804ac04790bd58cdd124526c00b920469b812bin0 -> 15 bytes
-rw-r--r--fuzzers/corpora/packfile/ffc54ca808e7666f250133ad0ae2185ad688a8261
-rw-r--r--fuzzers/corpora/patch_parse/edit-file.diff13
-rw-r--r--fuzzers/corpora/patch_parse/patch_fuzzer-patch.diff45
-rw-r--r--fuzzers/download_refs_fuzzer.c225
-rw-r--r--fuzzers/midx_fuzzer.c80
-rw-r--r--fuzzers/objects_fuzzer.c49
-rw-r--r--fuzzers/packfile_fuzzer.c143
-rw-r--r--fuzzers/patch_parse_fuzzer.c40
-rw-r--r--fuzzers/standalone_driver.c73
-rw-r--r--fuzzers/standalone_driver.h14
-rw-r--r--git.git-authors77
-rw-r--r--include/git2.h74
-rw-r--r--include/git2/annotated_commit.h125
-rw-r--r--include/git2/apply.h165
-rw-r--r--include/git2/attr.h370
-rw-r--r--include/git2/blame.h285
-rw-r--r--include/git2/blob.h311
-rw-r--r--include/git2/branch.h336
-rw-r--r--include/git2/buffer.h74
-rw-r--r--include/git2/cert.h172
-rw-r--r--include/git2/checkout.h417
-rw-r--r--include/git2/cherrypick.h92
-rw-r--r--include/git2/clone.h209
-rw-r--r--include/git2/commit.h546
-rw-r--r--include/git2/common.h517
-rw-r--r--include/git2/config.h782
-rw-r--r--include/git2/cred_helpers.h15
-rw-r--r--include/git2/credential.h315
-rw-r--r--include/git2/credential_helpers.h53
-rw-r--r--include/git2/deprecated.h939
-rw-r--r--include/git2/describe.h194
-rw-r--r--include/git2/diff.h1477
-rw-r--r--include/git2/email.h127
-rw-r--r--include/git2/errors.h181
-rw-r--r--include/git2/experimental.h20
-rw-r--r--include/git2/filter.h276
-rw-r--r--include/git2/global.h44
-rw-r--r--include/git2/graph.h77
-rw-r--r--include/git2/ignore.h78
-rw-r--r--include/git2/index.h848
-rw-r--r--include/git2/indexer.h195
-rw-r--r--include/git2/mailmap.h115
-rw-r--r--include/git2/merge.h626
-rw-r--r--include/git2/message.h86
-rw-r--r--include/git2/net.h54
-rw-r--r--include/git2/notes.h306
-rw-r--r--include/git2/object.h279
-rw-r--r--include/git2/odb.h654
-rw-r--r--include/git2/odb_backend.h223
-rw-r--r--include/git2/oid.h373
-rw-r--r--include/git2/oidarray.h37
-rw-r--r--include/git2/pack.h267
-rw-r--r--include/git2/patch.h290
-rw-r--r--include/git2/pathspec.h280
-rw-r--r--include/git2/proxy.h98
-rw-r--r--include/git2/rebase.h399
-rw-r--r--include/git2/refdb.h71
-rw-r--r--include/git2/reflog.h170
-rw-r--r--include/git2/refs.h771
-rw-r--r--include/git2/refspec.h121
-rw-r--r--include/git2/remote.h1173
-rw-r--r--include/git2/repository.h983
-rw-r--r--include/git2/reset.h111
-rw-r--r--include/git2/revert.h91
-rw-r--r--include/git2/revparse.h113
-rw-r--r--include/git2/revwalk.h302
-rw-r--r--include/git2/signature.h103
-rw-r--r--include/git2/stash.h314
-rw-r--r--include/git2/status.h452
-rw-r--r--include/git2/stdint.h247
-rw-r--r--include/git2/strarray.h43
-rw-r--r--include/git2/submodule.h668
-rw-r--r--include/git2/sys/alloc.h67
-rw-r--r--include/git2/sys/commit.h80
-rw-r--r--include/git2/sys/commit_graph.h184
-rw-r--r--include/git2/sys/config.h130
-rw-r--r--include/git2/sys/cred.h15
-rw-r--r--include/git2/sys/credential.h90
-rw-r--r--include/git2/sys/diff.h94
-rw-r--r--include/git2/sys/email.h45
-rw-r--r--include/git2/sys/filter.h353
-rw-r--r--include/git2/sys/hashsig.h106
-rw-r--r--include/git2/sys/index.h182
-rw-r--r--include/git2/sys/mempack.h87
-rw-r--r--include/git2/sys/merge.h182
-rw-r--r--include/git2/sys/midx.h78
-rw-r--r--include/git2/sys/odb_backend.h172
-rw-r--r--include/git2/sys/openssl.h38
-rw-r--r--include/git2/sys/path.h64
-rw-r--r--include/git2/sys/refdb_backend.h361
-rw-r--r--include/git2/sys/reflog.h21
-rw-r--r--include/git2/sys/refs.h49
-rw-r--r--include/git2/sys/remote.h46
-rw-r--r--include/git2/sys/repository.h180
-rw-r--r--include/git2/sys/stream.h152
-rw-r--r--include/git2/sys/transport.h465
-rw-r--r--include/git2/tag.h383
-rw-r--r--include/git2/trace.h67
-rw-r--r--include/git2/transaction.h121
-rw-r--r--include/git2/transport.h42
-rw-r--r--include/git2/tree.h475
-rw-r--r--include/git2/types.h371
-rw-r--r--include/git2/version.h39
-rw-r--r--include/git2/worktree.h268
-rw-r--r--package.json7
-rwxr-xr-xscript/backport.sh23
-rwxr-xr-xscript/leaks.sh6
-rwxr-xr-xscript/release.py191
-rw-r--r--script/sanitizers.supp4
-rw-r--r--script/thread-sanitizer.supp26
-rw-r--r--script/user_model.c98
-rw-r--r--script/user_nodefs.h34
-rwxr-xr-xscript/valgrind.sh2
-rw-r--r--script/valgrind.supp244
-rw-r--r--src/CMakeLists.txt214
-rw-r--r--src/README.md12
-rw-r--r--src/cli/CMakeLists.txt56
-rw-r--r--src/cli/README.md26
-rw-r--r--src/cli/cli.h20
-rw-r--r--src/cli/cmd.c21
-rw-r--r--src/cli/cmd.h33
-rw-r--r--src/cli/cmd_cat_file.c204
-rw-r--r--src/cli/cmd_clone.c192
-rw-r--r--src/cli/cmd_hash_object.c154
-rw-r--r--src/cli/cmd_help.c86
-rw-r--r--src/cli/error.h51
-rw-r--r--src/cli/main.c106
-rw-r--r--src/cli/opt.c669
-rw-r--r--src/cli/opt.h349
-rw-r--r--src/cli/opt_usage.c194
-rw-r--r--src/cli/opt_usage.h35
-rw-r--r--src/cli/progress.c346
-rw-r--r--src/cli/progress.h117
-rw-r--r--src/cli/sighandler.h20
-rw-r--r--src/cli/unix/sighandler.c36
-rw-r--r--src/cli/win32/precompiled.c1
-rw-r--r--src/cli/win32/precompiled.h3
-rw-r--r--src/cli/win32/sighandler.c37
-rw-r--r--src/libgit2/CMakeLists.txt122
-rw-r--r--src/libgit2/annotated_commit.c240
-rw-r--r--src/libgit2/annotated_commit.h52
-rw-r--r--src/libgit2/apply.c899
-rw-r--r--src/libgit2/apply.h25
-rw-r--r--src/libgit2/attr.c700
-rw-r--r--src/libgit2/attr.h15
-rw-r--r--src/libgit2/attr_file.c1027
-rw-r--r--src/libgit2/attr_file.h241
-rw-r--r--src/libgit2/attrcache.c478
-rw-r--r--src/libgit2/attrcache.h56
-rw-r--r--src/libgit2/blame.c566
-rw-r--r--src/libgit2/blame.h95
-rw-r--r--src/libgit2/blame_git.c684
-rw-r--r--src/libgit2/blame_git.h22
-rw-r--r--src/libgit2/blob.c530
-rw-r--r--src/libgit2/blob.h52
-rw-r--r--src/libgit2/branch.c823
-rw-r--r--src/libgit2/branch.h31
-rw-r--r--src/libgit2/buf.c126
-rw-r--r--src/libgit2/buf.h50
-rw-r--r--src/libgit2/cache.c253
-rw-r--r--src/libgit2/cache.h69
-rw-r--r--src/libgit2/checkout.c2813
-rw-r--r--src/libgit2/checkout.h27
-rw-r--r--src/libgit2/cherrypick.c242
-rw-r--r--src/libgit2/clone.c686
-rw-r--r--src/libgit2/clone.h20
-rw-r--r--src/libgit2/commit.c1099
-rw-r--r--src/libgit2/commit.h87
-rw-r--r--src/libgit2/commit_graph.c1309
-rw-r--r--src/libgit2/commit_graph.h188
-rw-r--r--src/libgit2/commit_list.c219
-rw-r--r--src/libgit2/commit_list.h58
-rw-r--r--src/libgit2/common.h55
-rw-r--r--src/libgit2/config.c1576
-rw-r--r--src/libgit2/config.h110
-rw-r--r--src/libgit2/config_backend.h96
-rw-r--r--src/libgit2/config_cache.c142
-rw-r--r--src/libgit2/config_entries.c237
-rw-r--r--src/libgit2/config_entries.h24
-rw-r--r--src/libgit2/config_file.c1200
-rw-r--r--src/libgit2/config_mem.c220
-rw-r--r--src/libgit2/config_parse.c580
-rw-r--r--src/libgit2/config_parse.h65
-rw-r--r--src/libgit2/config_snapshot.c207
-rw-r--r--src/libgit2/crlf.c426
-rw-r--r--src/libgit2/delta.c628
-rw-r--r--src/libgit2/delta.h136
-rw-r--r--src/libgit2/describe.c912
-rw-r--r--src/libgit2/diff.c402
-rw-r--r--src/libgit2/diff.h67
-rw-r--r--src/libgit2/diff_driver.c522
-rw-r--r--src/libgit2/diff_driver.h52
-rw-r--r--src/libgit2/diff_file.c490
-rw-r--r--src/libgit2/diff_file.h63
-rw-r--r--src/libgit2/diff_generate.c1750
-rw-r--r--src/libgit2/diff_generate.h130
-rw-r--r--src/libgit2/diff_parse.c123
-rw-r--r--src/libgit2/diff_parse.h20
-rw-r--r--src/libgit2/diff_print.c849
-rw-r--r--src/libgit2/diff_stats.c376
-rw-r--r--src/libgit2/diff_stats.h18
-rw-r--r--src/libgit2/diff_tform.c1125
-rw-r--r--src/libgit2/diff_tform.h25
-rw-r--r--src/libgit2/diff_xdiff.c261
-rw-r--r--src/libgit2/diff_xdiff.h35
-rw-r--r--src/libgit2/email.c316
-rw-r--r--src/libgit2/email.h25
-rw-r--r--src/libgit2/errors.c293
-rw-r--r--src/libgit2/errors.h80
-rw-r--r--src/libgit2/experimental.h.in13
-rw-r--r--src/libgit2/fetch.c239
-rw-r--r--src/libgit2/fetch.h20
-rw-r--r--src/libgit2/fetchhead.c340
-rw-r--r--src/libgit2/fetchhead.h35
-rw-r--r--src/libgit2/filter.c1221
-rw-r--r--src/libgit2/filter.h85
-rw-r--r--src/libgit2/git2.rc59
-rw-r--r--src/libgit2/grafts.c272
-rw-r--r--src/libgit2/grafts.h36
-rw-r--r--src/libgit2/graph.c249
-rw-r--r--src/libgit2/hashsig.c375
-rw-r--r--src/libgit2/ident.c139
-rw-r--r--src/libgit2/idxmap.c157
-rw-r--r--src/libgit2/idxmap.h177
-rw-r--r--src/libgit2/ignore.c652
-rw-r--r--src/libgit2/ignore.h65
-rw-r--r--src/libgit2/index.c3972
-rw-r--r--src/libgit2/index.h207
-rw-r--r--src/libgit2/indexer.c1483
-rw-r--r--src/libgit2/indexer.h16
-rw-r--r--src/libgit2/iterator.c2456
-rw-r--r--src/libgit2/iterator.h325
-rw-r--r--src/libgit2/libgit2.c483
-rw-r--r--src/libgit2/libgit2.h15
-rw-r--r--src/libgit2/mailmap.c500
-rw-r--r--src/libgit2/mailmap.h35
-rw-r--r--src/libgit2/merge.c3440
-rw-r--r--src/libgit2/merge.h204
-rw-r--r--src/libgit2/merge_driver.c432
-rw-r--r--src/libgit2/merge_driver.h62
-rw-r--r--src/libgit2/merge_file.c325
-rw-r--r--src/libgit2/message.c75
-rw-r--r--src/libgit2/midx.c928
-rw-r--r--src/libgit2/midx.h121
-rw-r--r--src/libgit2/mwindow.c544
-rw-r--r--src/libgit2/mwindow.h57
-rw-r--r--src/libgit2/notes.c810
-rw-r--r--src/libgit2/notes.h32
-rw-r--r--src/libgit2/object.c691
-rw-r--r--src/libgit2/object.h86
-rw-r--r--src/libgit2/object_api.c148
-rw-r--r--src/libgit2/odb.c2000
-rw-r--r--src/libgit2/odb.h188
-rw-r--r--src/libgit2/odb_loose.c1240
-rw-r--r--src/libgit2/odb_mempack.c189
-rw-r--r--src/libgit2/odb_pack.c986
-rw-r--r--src/libgit2/offmap.c101
-rw-r--r--src/libgit2/offmap.h133
-rw-r--r--src/libgit2/oid.c508
-rw-r--r--src/libgit2/oid.h273
-rw-r--r--src/libgit2/oidarray.c89
-rw-r--r--src/libgit2/oidarray.h24
-rw-r--r--src/libgit2/oidmap.c107
-rw-r--r--src/libgit2/oidmap.h128
-rw-r--r--src/libgit2/pack-objects.c1839
-rw-r--r--src/libgit2/pack-objects.h109
-rw-r--r--src/libgit2/pack.c1658
-rw-r--r--src/libgit2/pack.h214
-rw-r--r--src/libgit2/parse.c138
-rw-r--r--src/libgit2/parse.h61
-rw-r--r--src/libgit2/patch.c230
-rw-r--r--src/libgit2/patch.h75
-rw-r--r--src/libgit2/patch_generate.c934
-rw-r--r--src/libgit2/patch_generate.h69
-rw-r--r--src/libgit2/patch_parse.c1239
-rw-r--r--src/libgit2/patch_parse.h51
-rw-r--r--src/libgit2/path.c375
-rw-r--r--src/libgit2/path.h68
-rw-r--r--src/libgit2/pathspec.c722
-rw-r--r--src/libgit2/pathspec.h76
-rw-r--r--src/libgit2/proxy.c49
-rw-r--r--src/libgit2/proxy.h17
-rw-r--r--src/libgit2/push.c568
-rw-r--r--src/libgit2/push.h128
-rw-r--r--src/libgit2/reader.c269
-rw-r--r--src/libgit2/reader.h107
-rw-r--r--src/libgit2/rebase.c1469
-rw-r--r--src/libgit2/refdb.c424
-rw-r--r--src/libgit2/refdb.h128
-rw-r--r--src/libgit2/refdb_fs.c2508
-rw-r--r--src/libgit2/reflog.c234
-rw-r--r--src/libgit2/reflog.h40
-rw-r--r--src/libgit2/refs.c1404
-rw-r--r--src/libgit2/refs.h130
-rw-r--r--src/libgit2/refspec.c420
-rw-r--r--src/libgit2/refspec.h54
-rw-r--r--src/libgit2/remote.c3097
-rw-r--r--src/libgit2/remote.h101
-rw-r--r--src/libgit2/repo_template.h58
-rw-r--r--src/libgit2/repository.c3841
-rw-r--r--src/libgit2/repository.h283
-rw-r--r--src/libgit2/reset.c204
-rw-r--r--src/libgit2/revert.c240
-rw-r--r--src/libgit2/revparse.c968
-rw-r--r--src/libgit2/revwalk.c846
-rw-r--r--src/libgit2/revwalk.h73
-rw-r--r--src/libgit2/settings.h11
-rw-r--r--src/libgit2/signature.c339
-rw-r--r--src/libgit2/signature.h23
-rw-r--r--src/libgit2/stash.c1286
-rw-r--r--src/libgit2/status.c584
-rw-r--r--src/libgit2/status.h25
-rw-r--r--src/libgit2/strarray.c65
-rw-r--r--src/libgit2/strarray.h25
-rw-r--r--src/libgit2/stream.h86
-rw-r--r--src/libgit2/streams/mbedtls.c481
-rw-r--r--src/libgit2/streams/mbedtls.h23
-rw-r--r--src/libgit2/streams/openssl.c739
-rw-r--r--src/libgit2/streams/openssl.h31
-rw-r--r--src/libgit2/streams/openssl_dynamic.c313
-rw-r--r--src/libgit2/streams/openssl_dynamic.h348
-rw-r--r--src/libgit2/streams/openssl_legacy.c203
-rw-r--r--src/libgit2/streams/openssl_legacy.h63
-rw-r--r--src/libgit2/streams/registry.c119
-rw-r--r--src/libgit2/streams/registry.h19
-rw-r--r--src/libgit2/streams/schannel.c715
-rw-r--r--src/libgit2/streams/schannel.h28
-rw-r--r--src/libgit2/streams/socket.c428
-rw-r--r--src/libgit2/streams/socket.h25
-rw-r--r--src/libgit2/streams/stransport.c354
-rw-r--r--src/libgit2/streams/stransport.h21
-rw-r--r--src/libgit2/streams/tls.c80
-rw-r--r--src/libgit2/streams/tls.h31
-rw-r--r--src/libgit2/submodule.c2384
-rw-r--r--src/libgit2/submodule.h164
-rw-r--r--src/libgit2/sysdir.c650
-rw-r--r--src/libgit2/sysdir.h145
-rw-r--r--src/libgit2/tag.c599
-rw-r--r--src/libgit2/tag.h31
-rw-r--r--src/libgit2/threadstate.c97
-rw-r--r--src/libgit2/threadstate.h22
-rw-r--r--src/libgit2/trace.c25
-rw-r--r--src/libgit2/trace.h51
-rw-r--r--src/libgit2/trailer.c430
-rw-r--r--src/libgit2/transaction.c395
-rw-r--r--src/libgit2/transaction.h14
-rw-r--r--src/libgit2/transport.c222
-rw-r--r--src/libgit2/transports/auth.c74
-rw-r--r--src/libgit2/transports/auth.h69
-rw-r--r--src/libgit2/transports/auth_gssapi.c314
-rw-r--r--src/libgit2/transports/auth_negotiate.h27
-rw-r--r--src/libgit2/transports/auth_ntlm.h37
-rw-r--r--src/libgit2/transports/auth_ntlmclient.c227
-rw-r--r--src/libgit2/transports/auth_sspi.c341
-rw-r--r--src/libgit2/transports/credential.c486
-rw-r--r--src/libgit2/transports/credential_helpers.c68
-rw-r--r--src/libgit2/transports/git.c360
-rw-r--r--src/libgit2/transports/http.c766
-rw-r--r--src/libgit2/transports/http.h28
-rw-r--r--src/libgit2/transports/httpclient.c1593
-rw-r--r--src/libgit2/transports/httpclient.h200
-rw-r--r--src/libgit2/transports/local.c777
-rw-r--r--src/libgit2/transports/smart.c525
-rw-r--r--src/libgit2/transports/smart.h217
-rw-r--r--src/libgit2/transports/smart_pkt.c870
-rw-r--r--src/libgit2/transports/smart_protocol.c1231
-rw-r--r--src/libgit2/transports/ssh.c1147
-rw-r--r--src/libgit2/transports/ssh.h14
-rw-r--r--src/libgit2/transports/winhttp.c1690
-rw-r--r--src/libgit2/tree-cache.c287
-rw-r--r--src/libgit2/tree-cache.h40
-rw-r--r--src/libgit2/tree.c1330
-rw-r--r--src/libgit2/tree.h58
-rw-r--r--src/libgit2/userdiff.h210
-rw-r--r--src/libgit2/worktree.c672
-rw-r--r--src/libgit2/worktree.h39
-rw-r--r--src/util/CMakeLists.txt79
-rw-r--r--src/util/alloc.c115
-rw-r--r--src/util/alloc.h65
-rw-r--r--src/util/allocators/failalloc.c32
-rw-r--r--src/util/allocators/failalloc.h17
-rw-r--r--src/util/allocators/stdalloc.c47
-rw-r--r--src/util/allocators/stdalloc.h17
-rw-r--r--src/util/allocators/win32_leakcheck.c50
-rw-r--r--src/util/allocators/win32_leakcheck.h17
-rw-r--r--src/util/array.h129
-rw-r--r--src/util/assert_safe.h74
-rw-r--r--src/util/bitvec.h75
-rw-r--r--src/util/cc-compat.h108
-rw-r--r--src/util/date.c899
-rw-r--r--src/util/date.h33
-rw-r--r--src/util/filebuf.c600
-rw-r--r--src/util/filebuf.h107
-rw-r--r--src/util/fs_path.c2066
-rw-r--r--src/util/fs_path.h790
-rw-r--r--src/util/futils.c1236
-rw-r--r--src/util/futils.h403
-rw-r--r--src/util/git2_features.h.in68
-rw-r--r--src/util/git2_util.h168
-rw-r--r--src/util/hash.c158
-rw-r--r--src/util/hash.h61
-rw-r--r--src/util/hash/builtin.c53
-rw-r--r--src/util/hash/builtin.h19
-rw-r--r--src/util/hash/collisiondetect.c48
-rw-r--r--src/util/hash/collisiondetect.h19
-rw-r--r--src/util/hash/common_crypto.c112
-rw-r--r--src/util/hash/common_crypto.h27
-rw-r--r--src/util/hash/mbedtls.c92
-rw-r--r--src/util/hash/mbedtls.h29
-rw-r--r--src/util/hash/openssl.c195
-rw-r--r--src/util/hash/openssl.h45
-rw-r--r--src/util/hash/rfc6234/sha.h243
-rw-r--r--src/util/hash/rfc6234/sha224-256.c601
-rw-r--r--src/util/hash/sha.h70
-rw-r--r--src/util/hash/sha1dc/sha1.c1909
-rw-r--r--src/util/hash/sha1dc/sha1.h110
-rw-r--r--src/util/hash/sha1dc/ubc_check.c372
-rw-r--r--src/util/hash/sha1dc/ubc_check.h52
-rw-r--r--src/util/hash/win32.c549
-rw-r--r--src/util/hash/win32.h60
-rw-r--r--src/util/integer.h218
-rw-r--r--src/util/khash.h615
-rw-r--r--src/util/map.h46
-rw-r--r--src/util/net.c1154
-rw-r--r--src/util/net.h110
-rw-r--r--src/util/pool.c260
-rw-r--r--src/util/pool.h146
-rw-r--r--src/util/posix.c357
-rw-r--r--src/util/posix.h220
-rw-r--r--src/util/pqueue.c125
-rw-r--r--src/util/pqueue.h59
-rw-r--r--src/util/rand.c236
-rw-r--r--src/util/rand.h37
-rw-r--r--src/util/regexp.c221
-rw-r--r--src/util/regexp.h97
-rw-r--r--src/util/runtime.c162
-rw-r--r--src/util/runtime.h62
-rw-r--r--src/util/sortedcache.c380
-rw-r--r--src/util/sortedcache.h182
-rw-r--r--src/util/staticstr.h66
-rw-r--r--src/util/str.c1372
-rw-r--r--src/util/str.h357
-rw-r--r--src/util/strmap.c100
-rw-r--r--src/util/strmap.h131
-rw-r--r--src/util/strnlen.h24
-rw-r--r--src/util/thread.c140
-rw-r--r--src/util/thread.h480
-rw-r--r--src/util/tsort.c382
-rw-r--r--src/util/unix/map.c76
-rw-r--r--src/util/unix/posix.h104
-rw-r--r--src/util/unix/pthread.h57
-rw-r--r--src/util/unix/realpath.c32
-rw-r--r--src/util/utf8.c150
-rw-r--r--src/util/utf8.h52
-rw-r--r--src/util/util.c824
-rw-r--r--src/util/util.h396
-rw-r--r--src/util/varint.c43
-rw-r--r--src/util/varint.h17
-rw-r--r--src/util/vector.c431
-rw-r--r--src/util/vector.h128
-rw-r--r--src/util/wildmatch.c320
-rw-r--r--src/util/wildmatch.h23
-rw-r--r--src/util/win32/dir.c122
-rw-r--r--src/util/win32/dir.h44
-rw-r--r--src/util/win32/error.c53
-rw-r--r--src/util/win32/error.h15
-rw-r--r--src/util/win32/map.c141
-rw-r--r--src/util/win32/mingw-compat.h23
-rw-r--r--src/util/win32/msvc-compat.h36
-rw-r--r--src/util/win32/path_w32.c642
-rw-r--r--src/util/win32/path_w32.h91
-rw-r--r--src/util/win32/posix.h62
-rw-r--r--src/util/win32/posix_w32.c1047
-rw-r--r--src/util/win32/precompiled.c1
-rw-r--r--src/util/win32/precompiled.h21
-rw-r--r--src/util/win32/reparse.h57
-rw-r--r--src/util/win32/thread.c262
-rw-r--r--src/util/win32/thread.h64
-rw-r--r--src/util/win32/utf-conv.c144
-rw-r--r--src/util/win32/utf-conv.h127
-rw-r--r--src/util/win32/version.h37
-rw-r--r--src/util/win32/w32_buffer.c57
-rw-r--r--src/util/win32/w32_buffer.h19
-rw-r--r--src/util/win32/w32_common.h48
-rw-r--r--src/util/win32/w32_leakcheck.c581
-rw-r--r--src/util/win32/w32_leakcheck.h222
-rw-r--r--src/util/win32/w32_util.c126
-rw-r--r--src/util/win32/w32_util.h144
-rw-r--r--src/util/win32/win32-compat.h52
-rw-r--r--src/util/zstream.c210
-rw-r--r--src/util/zstream.h54
-rw-r--r--tests/CMakeLists.txt6
-rw-r--r--tests/README.md83
-rw-r--r--tests/benchmarks/README.md121
-rwxr-xr-xtests/benchmarks/benchmark.sh298
-rw-r--r--tests/benchmarks/benchmark_helpers.sh363
-rwxr-xr-xtests/benchmarks/hash-object__text_100kb7
-rwxr-xr-xtests/benchmarks/hash-object__text_10mb7
-rwxr-xr-xtests/benchmarks/hash-object__text_1kb7
-rwxr-xr-xtests/benchmarks/hash-object__text_cached_100kb7
-rwxr-xr-xtests/benchmarks/hash-object__text_cached_10mb7
-rwxr-xr-xtests/benchmarks/hash-object__text_cached_1kb7
-rwxr-xr-xtests/benchmarks/hash-object__write_text_100kb9
-rwxr-xr-xtests/benchmarks/hash-object__write_text_10mb9
-rwxr-xr-xtests/benchmarks/hash-object__write_text_1kb9
-rwxr-xr-xtests/benchmarks/hash-object__write_text_cached_100kb9
-rwxr-xr-xtests/benchmarks/hash-object__write_text_cached_10mb9
-rwxr-xr-xtests/benchmarks/hash-object__write_text_cached_1kb9
-rw-r--r--tests/clar/clar.c840
-rw-r--r--tests/clar/clar.h173
-rw-r--r--tests/clar/clar/fixtures.h50
-rw-r--r--tests/clar/clar/fs.h520
-rw-r--r--tests/clar/clar/print.h211
-rw-r--r--tests/clar/clar/sandbox.h154
-rw-r--r--tests/clar/clar/summary.h143
-rw-r--r--tests/clar/clar_libgit2.c710
-rw-r--r--tests/clar/clar_libgit2.h269
-rw-r--r--tests/clar/clar_libgit2_alloc.c110
-rw-r--r--tests/clar/clar_libgit2_alloc.h11
-rw-r--r--tests/clar/clar_libgit2_timer.c30
-rw-r--r--tests/clar/clar_libgit2_timer.h35
-rw-r--r--tests/clar/clar_libgit2_trace.c263
-rw-r--r--tests/clar/clar_libgit2_trace.h7
-rw-r--r--tests/clar/generate.py316
-rw-r--r--tests/clar/main.c52
-rw-r--r--tests/headertest/CMakeLists.txt14
-rw-r--r--tests/headertest/headertest.c13
-rw-r--r--tests/libgit2/CMakeLists.txt77
-rw-r--r--tests/libgit2/apply/apply_helpers.c135
-rw-r--r--tests/libgit2/apply/apply_helpers.h498
-rw-r--r--tests/libgit2/apply/both.c747
-rw-r--r--tests/libgit2/apply/callbacks.c128
-rw-r--r--tests/libgit2/apply/check.c121
-rw-r--r--tests/libgit2/apply/fromdiff.c378
-rw-r--r--tests/libgit2/apply/fromfile.c449
-rw-r--r--tests/libgit2/apply/index.c321
-rw-r--r--tests/libgit2/apply/partial.c231
-rw-r--r--tests/libgit2/apply/tree.c94
-rw-r--r--tests/libgit2/apply/workdir.c358
-rw-r--r--tests/libgit2/attr/attr_expect.h43
-rw-r--r--tests/libgit2/attr/file.c243
-rw-r--r--tests/libgit2/attr/flags.c108
-rw-r--r--tests/libgit2/attr/lookup.c263
-rw-r--r--tests/libgit2/attr/macro.c197
-rw-r--r--tests/libgit2/attr/repo.c405
-rw-r--r--tests/libgit2/blame/blame_helpers.c67
-rw-r--r--tests/libgit2/blame/blame_helpers.h14
-rw-r--r--tests/libgit2/blame/buffer.c192
-rw-r--r--tests/libgit2/blame/getters.c56
-rw-r--r--tests/libgit2/blame/harder.c79
-rw-r--r--tests/libgit2/blame/simple.c362
-rw-r--r--tests/libgit2/checkout/binaryunicode.c59
-rw-r--r--tests/libgit2/checkout/checkout_helpers.c151
-rw-r--r--tests/libgit2/checkout/checkout_helpers.h30
-rw-r--r--tests/libgit2/checkout/conflict.c1145
-rw-r--r--tests/libgit2/checkout/crlf.c496
-rw-r--r--tests/libgit2/checkout/head.c292
-rw-r--r--tests/libgit2/checkout/icase.c292
-rw-r--r--tests/libgit2/checkout/index.c882
-rw-r--r--tests/libgit2/checkout/nasty.c386
-rw-r--r--tests/libgit2/checkout/tree.c1685
-rw-r--r--tests/libgit2/checkout/typechange.c335
-rw-r--r--tests/libgit2/cherrypick/bare.c105
-rw-r--r--tests/libgit2/cherrypick/workdir.c469
-rw-r--r--tests/libgit2/clone/empty.c102
-rw-r--r--tests/libgit2/clone/local.c212
-rw-r--r--tests/libgit2/clone/nonetwork.c362
-rw-r--r--tests/libgit2/clone/transport.c51
-rw-r--r--tests/libgit2/commit/commit.c189
-rw-r--r--tests/libgit2/commit/parent.c60
-rw-r--r--tests/libgit2/commit/parse.c553
-rw-r--r--tests/libgit2/commit/signature.c148
-rw-r--r--tests/libgit2/commit/write.c424
-rw-r--r--tests/libgit2/config/add.c37
-rw-r--r--tests/libgit2/config/backend.c24
-rw-r--r--tests/libgit2/config/conditionals.c175
-rw-r--r--tests/libgit2/config/config_helpers.c67
-rw-r--r--tests/libgit2/config/config_helpers.h13
-rw-r--r--tests/libgit2/config/configlevel.c73
-rw-r--r--tests/libgit2/config/find.c11
-rw-r--r--tests/libgit2/config/global.c172
-rw-r--r--tests/libgit2/config/include.c267
-rw-r--r--tests/libgit2/config/memory.c139
-rw-r--r--tests/libgit2/config/multivar.c288
-rw-r--r--tests/libgit2/config/new.c34
-rw-r--r--tests/libgit2/config/read.c1035
-rw-r--r--tests/libgit2/config/readonly.c65
-rw-r--r--tests/libgit2/config/rename.c89
-rw-r--r--tests/libgit2/config/snapshot.c139
-rw-r--r--tests/libgit2/config/stress.c206
-rw-r--r--tests/libgit2/config/validkeyname.c49
-rw-r--r--tests/libgit2/config/write.c764
-rw-r--r--tests/libgit2/core/buf.c54
-rw-r--r--tests/libgit2/core/env.c320
-rw-r--r--tests/libgit2/core/features.c35
-rw-r--r--tests/libgit2/core/hashsig.c182
-rw-r--r--tests/libgit2/core/oid.c213
-rw-r--r--tests/libgit2/core/oidarray.c98
-rw-r--r--tests/libgit2/core/oidmap.c130
-rw-r--r--tests/libgit2/core/opts.c91
-rw-r--r--tests/libgit2/core/pool.c38
-rw-r--r--tests/libgit2/core/structinit.c206
-rw-r--r--tests/libgit2/core/useragent.c17
-rw-r--r--tests/libgit2/date/date.c22
-rw-r--r--tests/libgit2/date/rfc2822.c37
-rw-r--r--tests/libgit2/delta/apply.c21
-rw-r--r--tests/libgit2/describe/describe.c55
-rw-r--r--tests/libgit2/describe/describe_helpers.c44
-rw-r--r--tests/libgit2/describe/describe_helpers.h14
-rw-r--r--tests/libgit2/describe/t6120.c156
-rw-r--r--tests/libgit2/diff/binary.c545
-rw-r--r--tests/libgit2/diff/blob.c1063
-rw-r--r--tests/libgit2/diff/diff_helpers.c333
-rw-r--r--tests/libgit2/diff/diff_helpers.h77
-rw-r--r--tests/libgit2/diff/diffiter.c453
-rw-r--r--tests/libgit2/diff/drivers.c279
-rw-r--r--tests/libgit2/diff/externalmodifications.c133
-rw-r--r--tests/libgit2/diff/format_email.c523
-rw-r--r--tests/libgit2/diff/index.c303
-rw-r--r--tests/libgit2/diff/notify.c258
-rw-r--r--tests/libgit2/diff/parse.c476
-rw-r--r--tests/libgit2/diff/patch.c703
-rw-r--r--tests/libgit2/diff/patchid.c94
-rw-r--r--tests/libgit2/diff/pathspec.c93
-rw-r--r--tests/libgit2/diff/racediffiter.c129
-rw-r--r--tests/libgit2/diff/rename.c2034
-rw-r--r--tests/libgit2/diff/stats.c379
-rw-r--r--tests/libgit2/diff/submodules.c543
-rw-r--r--tests/libgit2/diff/tree.c575
-rw-r--r--tests/libgit2/diff/userdiff.c25
-rw-r--r--tests/libgit2/diff/workdir.c2327
-rw-r--r--tests/libgit2/email/create.c364
-rw-r--r--tests/libgit2/fetch/local.c67
-rw-r--r--tests/libgit2/fetchhead/fetchhead_data.h48
-rw-r--r--tests/libgit2/fetchhead/nonetwork.c548
-rw-r--r--tests/libgit2/filter/bare.c188
-rw-r--r--tests/libgit2/filter/blob.c145
-rw-r--r--tests/libgit2/filter/crlf.c253
-rw-r--r--tests/libgit2/filter/crlf.h30
-rw-r--r--tests/libgit2/filter/custom.c268
-rw-r--r--tests/libgit2/filter/custom_helpers.c163
-rw-r--r--tests/libgit2/filter/custom_helpers.h19
-rw-r--r--tests/libgit2/filter/file.c98
-rw-r--r--tests/libgit2/filter/ident.c133
-rw-r--r--tests/libgit2/filter/query.c90
-rw-r--r--tests/libgit2/filter/stream.c215
-rw-r--r--tests/libgit2/filter/systemattrs.c85
-rw-r--r--tests/libgit2/filter/wildcard.c188
-rw-r--r--tests/libgit2/grafts/basic.c121
-rw-r--r--tests/libgit2/grafts/parse.c149
-rw-r--r--tests/libgit2/grafts/shallow.c134
-rw-r--r--tests/libgit2/graph/ahead_behind.c58
-rw-r--r--tests/libgit2/graph/commitgraph.c180
-rw-r--r--tests/libgit2/graph/descendant_of.c55
-rw-r--r--tests/libgit2/graph/reachable_from_any.c236
-rw-r--r--tests/libgit2/ignore/path.c589
-rw-r--r--tests/libgit2/ignore/status.c1341
-rw-r--r--tests/libgit2/index/add.c108
-rw-r--r--tests/libgit2/index/addall.c542
-rw-r--r--tests/libgit2/index/bypath.c359
-rw-r--r--tests/libgit2/index/cache.c238
-rw-r--r--tests/libgit2/index/collision.c149
-rw-r--r--tests/libgit2/index/conflicts.c462
-rw-r--r--tests/libgit2/index/conflicts.h7
-rw-r--r--tests/libgit2/index/crlf.c403
-rw-r--r--tests/libgit2/index/filemodes.c319
-rw-r--r--tests/libgit2/index/inmemory.c23
-rw-r--r--tests/libgit2/index/names.c187
-rw-r--r--tests/libgit2/index/nsec.c129
-rw-r--r--tests/libgit2/index/racy.c323
-rw-r--r--tests/libgit2/index/read_index.c232
-rw-r--r--tests/libgit2/index/read_tree.c46
-rw-r--r--tests/libgit2/index/rename.c86
-rw-r--r--tests/libgit2/index/reuc.c376
-rw-r--r--tests/libgit2/index/splitindex.c21
-rw-r--r--tests/libgit2/index/stage.c62
-rw-r--r--tests/libgit2/index/tests.c1173
-rw-r--r--tests/libgit2/index/tests256.c1169
-rw-r--r--tests/libgit2/index/version.c140
-rw-r--r--tests/libgit2/iterator/index.c1385
-rw-r--r--tests/libgit2/iterator/iterator_helpers.c143
-rw-r--r--tests/libgit2/iterator/iterator_helpers.h16
-rw-r--r--tests/libgit2/iterator/tree.c1080
-rw-r--r--tests/libgit2/iterator/workdir.c1523
-rw-r--r--tests/libgit2/mailmap/basic.c101
-rw-r--r--tests/libgit2/mailmap/blame.c64
-rw-r--r--tests/libgit2/mailmap/mailmap_testdata.h21
-rw-r--r--tests/libgit2/mailmap/parsing.c269
-rw-r--r--tests/libgit2/merge/analysis.c184
-rw-r--r--tests/libgit2/merge/annotated_commit.c26
-rw-r--r--tests/libgit2/merge/conflict_data.h112
-rw-r--r--tests/libgit2/merge/driver.c396
-rw-r--r--tests/libgit2/merge/files.c465
-rw-r--r--tests/libgit2/merge/merge_helpers.c365
-rw-r--r--tests/libgit2/merge/merge_helpers.h72
-rw-r--r--tests/libgit2/merge/trees/automerge.c195
-rw-r--r--tests/libgit2/merge/trees/commits.c148
-rw-r--r--tests/libgit2/merge/trees/modeconflict.c58
-rw-r--r--tests/libgit2/merge/trees/recursive.c458
-rw-r--r--tests/libgit2/merge/trees/renames.c352
-rw-r--r--tests/libgit2/merge/trees/treediff.c555
-rw-r--r--tests/libgit2/merge/trees/trivial.c306
-rw-r--r--tests/libgit2/merge/trees/whitespace.c81
-rw-r--r--tests/libgit2/merge/workdir/dirty.c351
-rw-r--r--tests/libgit2/merge/workdir/recursive.c84
-rw-r--r--tests/libgit2/merge/workdir/renames.c155
-rw-r--r--tests/libgit2/merge/workdir/setup.c1096
-rw-r--r--tests/libgit2/merge/workdir/simple.c778
-rw-r--r--tests/libgit2/merge/workdir/submodules.c130
-rw-r--r--tests/libgit2/merge/workdir/trivial.c262
-rw-r--r--tests/libgit2/message/trailer.c164
-rw-r--r--tests/libgit2/network/cred.c46
-rw-r--r--tests/libgit2/network/fetchlocal.c552
-rw-r--r--tests/libgit2/network/refspecs.c191
-rw-r--r--tests/libgit2/network/remote/defaultbranch.c107
-rw-r--r--tests/libgit2/network/remote/delete.c46
-rw-r--r--tests/libgit2/network/remote/isvalidname.c24
-rw-r--r--tests/libgit2/network/remote/local.c483
-rw-r--r--tests/libgit2/network/remote/push.c115
-rw-r--r--tests/libgit2/network/remote/remotes.c575
-rw-r--r--tests/libgit2/network/remote/rename.c245
-rw-r--r--tests/libgit2/notes/notes.c658
-rw-r--r--tests/libgit2/notes/notesref.c67
-rw-r--r--tests/libgit2/object/blob/filter.c149
-rw-r--r--tests/libgit2/object/blob/fromstream.c86
-rw-r--r--tests/libgit2/object/blob/write.c68
-rw-r--r--tests/libgit2/object/cache.c276
-rw-r--r--tests/libgit2/object/commit/commitstagedfile.c218
-rw-r--r--tests/libgit2/object/commit/parse.c476
-rw-r--r--tests/libgit2/object/lookup.c122
-rw-r--r--tests/libgit2/object/lookup256.c153
-rw-r--r--tests/libgit2/object/lookupbypath.c83
-rw-r--r--tests/libgit2/object/message.c197
-rw-r--r--tests/libgit2/object/peel.c118
-rw-r--r--tests/libgit2/object/raw/chars.c41
-rw-r--r--tests/libgit2/object/raw/compare.c123
-rw-r--r--tests/libgit2/object/raw/convert.c112
-rw-r--r--tests/libgit2/object/raw/data.h323
-rw-r--r--tests/libgit2/object/raw/fromstr.c29
-rw-r--r--tests/libgit2/object/raw/hash.c176
-rw-r--r--tests/libgit2/object/raw/short.c140
-rw-r--r--tests/libgit2/object/raw/size.c13
-rw-r--r--tests/libgit2/object/raw/type2string.c54
-rw-r--r--tests/libgit2/object/raw/write.c462
-rw-r--r--tests/libgit2/object/shortid.c51
-rw-r--r--tests/libgit2/object/tag/list.c117
-rw-r--r--tests/libgit2/object/tag/parse.c218
-rw-r--r--tests/libgit2/object/tag/peel.c61
-rw-r--r--tests/libgit2/object/tag/read.c179
-rw-r--r--tests/libgit2/object/tag/write.c279
-rw-r--r--tests/libgit2/object/tree/attributes.c118
-rw-r--r--tests/libgit2/object/tree/duplicateentries.c161
-rw-r--r--tests/libgit2/object/tree/frompath.c68
-rw-r--r--tests/libgit2/object/tree/parse.c164
-rw-r--r--tests/libgit2/object/tree/read.c119
-rw-r--r--tests/libgit2/object/tree/update.c303
-rw-r--r--tests/libgit2/object/tree/walk.c177
-rw-r--r--tests/libgit2/object/tree/write.c528
-rw-r--r--tests/libgit2/object/validate.c154
-rw-r--r--tests/libgit2/odb/alternates.c80
-rw-r--r--tests/libgit2/odb/backend/backend_helpers.c172
-rw-r--r--tests/libgit2/odb/backend/backend_helpers.h24
-rw-r--r--tests/libgit2/odb/backend/loose.c62
-rw-r--r--tests/libgit2/odb/backend/mempack.c61
-rw-r--r--tests/libgit2/odb/backend/multiple.c121
-rw-r--r--tests/libgit2/odb/backend/nobackend.c47
-rw-r--r--tests/libgit2/odb/backend/nonrefreshing.c147
-rw-r--r--tests/libgit2/odb/backend/refreshing.c176
-rw-r--r--tests/libgit2/odb/backend/simple.c250
-rw-r--r--tests/libgit2/odb/emptyobjects.c58
-rw-r--r--tests/libgit2/odb/foreach.c149
-rw-r--r--tests/libgit2/odb/freshen.c183
-rw-r--r--tests/libgit2/odb/largefiles.c189
-rw-r--r--tests/libgit2/odb/loose.c393
-rw-r--r--tests/libgit2/odb/loose_data.h893
-rw-r--r--tests/libgit2/odb/mixed.c286
-rw-r--r--tests/libgit2/odb/open.c34
-rw-r--r--tests/libgit2/odb/pack_data.h151
-rw-r--r--tests/libgit2/odb/pack_data_256.h154
-rw-r--r--tests/libgit2/odb/pack_data_one.h19
-rw-r--r--tests/libgit2/odb/pack_data_one256.h21
-rw-r--r--tests/libgit2/odb/packed.c79
-rw-r--r--tests/libgit2/odb/packed256.c98
-rw-r--r--tests/libgit2/odb/packedone.c67
-rw-r--r--tests/libgit2/odb/packedone256.c78
-rw-r--r--tests/libgit2/odb/sorting.c104
-rw-r--r--tests/libgit2/odb/streamwrite.c56
-rw-r--r--tests/libgit2/online/badssl.c75
-rw-r--r--tests/libgit2/online/clone.c1388
-rw-r--r--tests/libgit2/online/customcert.c79
-rw-r--r--tests/libgit2/online/fetch.c352
-rw-r--r--tests/libgit2/online/fetchhead.c169
-rw-r--r--tests/libgit2/online/push.c932
-rw-r--r--tests/libgit2/online/push_util.c139
-rw-r--r--tests/libgit2/online/push_util.h83
-rw-r--r--tests/libgit2/online/remotes.c127
-rw-r--r--tests/libgit2/online/shallow.c166
-rw-r--r--tests/libgit2/pack/filelimit.c136
-rw-r--r--tests/libgit2/pack/indexer.c369
-rw-r--r--tests/libgit2/pack/midx.c116
-rw-r--r--tests/libgit2/pack/packbuilder.c293
-rw-r--r--tests/libgit2/pack/sharing.c42
-rw-r--r--tests/libgit2/pack/threadsafety.c62
-rw-r--r--tests/libgit2/patch/parse.c221
-rw-r--r--tests/libgit2/patch/patch_common.h1014
-rw-r--r--tests/libgit2/patch/print.c186
-rw-r--r--tests/libgit2/path/validate.c63
-rw-r--r--tests/libgit2/perf/helper__perf__do_merge.c75
-rw-r--r--tests/libgit2/perf/helper__perf__do_merge.h4
-rw-r--r--tests/libgit2/perf/helper__perf__timer.c73
-rw-r--r--tests/libgit2/perf/helper__perf__timer.h27
-rw-r--r--tests/libgit2/perf/merge.c31
-rw-r--r--tests/libgit2/precompiled.c1
-rw-r--r--tests/libgit2/precompiled.h4
-rw-r--r--tests/libgit2/rebase/abort.c250
-rw-r--r--tests/libgit2/rebase/inmemory.c210
-rw-r--r--tests/libgit2/rebase/iterator.c140
-rw-r--r--tests/libgit2/rebase/merge.c855
-rw-r--r--tests/libgit2/rebase/setup.c596
-rw-r--r--tests/libgit2/rebase/sign.c491
-rw-r--r--tests/libgit2/rebase/submodule.c95
-rw-r--r--tests/libgit2/refs/basic.c85
-rw-r--r--tests/libgit2/refs/branches/checkedout.c53
-rw-r--r--tests/libgit2/refs/branches/create.c287
-rw-r--r--tests/libgit2/refs/branches/delete.c188
-rw-r--r--tests/libgit2/refs/branches/ishead.c98
-rw-r--r--tests/libgit2/refs/branches/iterator.c151
-rw-r--r--tests/libgit2/refs/branches/lookup.c68
-rw-r--r--tests/libgit2/refs/branches/move.c250
-rw-r--r--tests/libgit2/refs/branches/name.c62
-rw-r--r--tests/libgit2/refs/branches/remote.c65
-rw-r--r--tests/libgit2/refs/branches/upstream.c218
-rw-r--r--tests/libgit2/refs/branches/upstreamname.c35
-rw-r--r--tests/libgit2/refs/crashes.c44
-rw-r--r--tests/libgit2/refs/create.c362
-rw-r--r--tests/libgit2/refs/delete.c118
-rw-r--r--tests/libgit2/refs/dup.c42
-rw-r--r--tests/libgit2/refs/foreachglob.c100
-rw-r--r--tests/libgit2/refs/isvalidname.c38
-rw-r--r--tests/libgit2/refs/iterator.c276
-rw-r--r--tests/libgit2/refs/list.c57
-rw-r--r--tests/libgit2/refs/listall.c47
-rw-r--r--tests/libgit2/refs/lookup.c68
-rw-r--r--tests/libgit2/refs/namespaces.c36
-rw-r--r--tests/libgit2/refs/normalize.c401
-rw-r--r--tests/libgit2/refs/overwrite.c136
-rw-r--r--tests/libgit2/refs/pack.c105
-rw-r--r--tests/libgit2/refs/peel.c131
-rw-r--r--tests/libgit2/refs/races.c170
-rw-r--r--tests/libgit2/refs/read.c299
-rw-r--r--tests/libgit2/refs/ref_helpers.c25
-rw-r--r--tests/libgit2/refs/ref_helpers.h1
-rw-r--r--tests/libgit2/refs/reflog/drop.c115
-rw-r--r--tests/libgit2/refs/reflog/messages.c421
-rw-r--r--tests/libgit2/refs/reflog/reflog.c469
-rw-r--r--tests/libgit2/refs/reflog/reflog_helpers.c120
-rw-r--r--tests/libgit2/refs/reflog/reflog_helpers.h12
-rw-r--r--tests/libgit2/refs/rename.c368
-rw-r--r--tests/libgit2/refs/revparse.c891
-rw-r--r--tests/libgit2/refs/setter.c99
-rw-r--r--tests/libgit2/refs/shorthand.c27
-rw-r--r--tests/libgit2/refs/tags/name.c17
-rw-r--r--tests/libgit2/refs/transactions.c157
-rw-r--r--tests/libgit2/refs/unicode.c54
-rw-r--r--tests/libgit2/refs/update.c26
-rw-r--r--tests/libgit2/remote/create.c388
-rw-r--r--tests/libgit2/remote/fetch.c169
-rw-r--r--tests/libgit2/remote/httpproxy.c180
-rw-r--r--tests/libgit2/remote/insteadof.c154
-rw-r--r--tests/libgit2/remote/list.c43
-rw-r--r--tests/libgit2/repo/config.c210
-rw-r--r--tests/libgit2/repo/discover.c213
-rw-r--r--tests/libgit2/repo/env.c369
-rw-r--r--tests/libgit2/repo/extensions.c72
-rw-r--r--tests/libgit2/repo/getters.c53
-rw-r--r--tests/libgit2/repo/hashfile.c172
-rw-r--r--tests/libgit2/repo/head.c182
-rw-r--r--tests/libgit2/repo/headtree.c53
-rw-r--r--tests/libgit2/repo/init.c757
-rw-r--r--tests/libgit2/repo/message.c39
-rw-r--r--tests/libgit2/repo/new.c27
-rw-r--r--tests/libgit2/repo/objectformat.c69
-rw-r--r--tests/libgit2/repo/open.c762
-rw-r--r--tests/libgit2/repo/pathspec.c385
-rw-r--r--tests/libgit2/repo/repo_helpers.c37
-rw-r--r--tests/libgit2/repo/repo_helpers.h7
-rw-r--r--tests/libgit2/repo/reservedname.c132
-rw-r--r--tests/libgit2/repo/setters.c110
-rw-r--r--tests/libgit2/repo/shallow.c39
-rw-r--r--tests/libgit2/repo/state.c131
-rw-r--r--tests/libgit2/repo/template.c305
-rw-r--r--tests/libgit2/reset/default.c212
-rw-r--r--tests/libgit2/reset/hard.c294
-rw-r--r--tests/libgit2/reset/mixed.c85
-rw-r--r--tests/libgit2/reset/reset_helpers.c20
-rw-r--r--tests/libgit2/reset/reset_helpers.h7
-rw-r--r--tests/libgit2/reset/soft.c189
-rw-r--r--tests/libgit2/revert/bare.c106
-rw-r--r--tests/libgit2/revert/rename.c49
-rw-r--r--tests/libgit2/revert/workdir.c576
-rw-r--r--tests/libgit2/revwalk/basic.c644
-rw-r--r--tests/libgit2/revwalk/hidecb.c230
-rw-r--r--tests/libgit2/revwalk/mergebase.c514
-rw-r--r--tests/libgit2/revwalk/signatureparsing.c47
-rw-r--r--tests/libgit2/revwalk/simplify.c56
-rw-r--r--tests/libgit2/stash/apply.c449
-rw-r--r--tests/libgit2/stash/drop.c174
-rw-r--r--tests/libgit2/stash/foreach.c126
-rw-r--r--tests/libgit2/stash/save.c527
-rw-r--r--tests/libgit2/stash/stash_helpers.c57
-rw-r--r--tests/libgit2/stash/stash_helpers.h8
-rw-r--r--tests/libgit2/stash/submodules.c83
-rw-r--r--tests/libgit2/status/renames.c844
-rw-r--r--tests/libgit2/status/single.c46
-rw-r--r--tests/libgit2/status/status_data.h326
-rw-r--r--tests/libgit2/status/status_helpers.c97
-rw-r--r--tests/libgit2/status/status_helpers.h51
-rw-r--r--tests/libgit2/status/submodules.c563
-rw-r--r--tests/libgit2/status/worktree.c1362
-rw-r--r--tests/libgit2/status/worktree_init.c339
-rw-r--r--tests/libgit2/stream/deprecated.c60
-rw-r--r--tests/libgit2/stream/registration.c119
-rw-r--r--tests/libgit2/stress/diff.c146
-rw-r--r--tests/libgit2/submodule/add.c251
-rw-r--r--tests/libgit2/submodule/escape.c98
-rw-r--r--tests/libgit2/submodule/init.c115
-rw-r--r--tests/libgit2/submodule/inject_option.c80
-rw-r--r--tests/libgit2/submodule/lookup.c517
-rw-r--r--tests/libgit2/submodule/modify.c233
-rw-r--r--tests/libgit2/submodule/nosubs.c130
-rw-r--r--tests/libgit2/submodule/open.c90
-rw-r--r--tests/libgit2/submodule/repository_init.c38
-rw-r--r--tests/libgit2/submodule/status.c354
-rw-r--r--tests/libgit2/submodule/submodule_helpers.c245
-rw-r--r--tests/libgit2/submodule/submodule_helpers.h25
-rw-r--r--tests/libgit2/submodule/update.c460
-rw-r--r--tests/libgit2/threads/atomic.c125
-rw-r--r--tests/libgit2/threads/basic.c83
-rw-r--r--tests/libgit2/threads/diff.c218
-rw-r--r--tests/libgit2/threads/iterator.c55
-rw-r--r--tests/libgit2/threads/refdb.c220
-rw-r--r--tests/libgit2/threads/thread_helpers.c44
-rw-r--r--tests/libgit2/threads/thread_helpers.h8
-rw-r--r--tests/libgit2/threads/tlsdata.c65
-rw-r--r--tests/libgit2/trace/trace.c106
-rw-r--r--tests/libgit2/trace/windows/stacktrace.c152
-rw-r--r--tests/libgit2/transport/register.c224
-rw-r--r--tests/libgit2/transports/smart/packet.c353
-rw-r--r--tests/libgit2/valgrind-supp-mac.txt176
-rw-r--r--tests/libgit2/win32/forbidden.c182
-rw-r--r--tests/libgit2/win32/longpath.c130
-rw-r--r--tests/libgit2/win32/systemdir.c337
-rw-r--r--tests/libgit2/worktree/bare.c72
-rw-r--r--tests/libgit2/worktree/config.c47
-rw-r--r--tests/libgit2/worktree/merge.c121
-rw-r--r--tests/libgit2/worktree/open.c126
-rw-r--r--tests/libgit2/worktree/reflog.c91
-rw-r--r--tests/libgit2/worktree/refs.c198
-rw-r--r--tests/libgit2/worktree/repository.c67
-rw-r--r--tests/libgit2/worktree/submodule.c92
-rw-r--r--tests/libgit2/worktree/worktree.c660
-rw-r--r--tests/libgit2/worktree/worktree_helpers.c30
-rw-r--r--tests/libgit2/worktree/worktree_helpers.h11
-rw-r--r--tests/resources/.gitattributes2
-rw-r--r--tests/resources/.gitignore1
-rw-r--r--tests/resources/attr/.gitted/HEAD1
-rw-r--r--tests/resources/attr/.gitted/config6
-rw-r--r--tests/resources/attr/.gitted/description1
-rw-r--r--tests/resources/attr/.gitted/indexbin0 -> 1856 bytes
-rw-r--r--tests/resources/attr/.gitted/info/attributes4
-rw-r--r--tests/resources/attr/.gitted/info/exclude6
-rw-r--r--tests/resources/attr/.gitted/logs/HEAD9
-rw-r--r--tests/resources/attr/.gitted/logs/refs/heads/master9
-rw-r--r--tests/resources/attr/.gitted/objects/10/8bb4e7fd7b16490dc33ff7d972151e73d7166ebin0 -> 130 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/16/983da6643656bb44c43965ecb6855c6d574512bin0 -> 446 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/21/7878ab49e1314388ea2e32dc6fdb58a1b969e04
-rw-r--r--tests/resources/attr/.gitted/objects/24/fa9a9fc4e202313e24b648087495441dab432bbin0 -> 180 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/29/29de282ce999e95183aedac6451d3384559c4bbin0 -> 58 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/2b/40c5aca159b04ea8d20ffe36cdf8b09369b14a1
-rw-r--r--tests/resources/attr/.gitted/objects/2c/66e14f77196ea763fb1e41612c1aa2bc2d8ed2bin0 -> 316 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/2d/e7dfe3588f3c7e9ad59e7d50ba90e3329df9d9bin0 -> 124 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/37/0fe9ec224ce33e71f9e5ec2bd1142ce9937a6abin0 -> 177 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/3a/6df026462ebafe455af9867d27eda20a9e0974bin0 -> 84 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/3b/74db7ab381105dc0d28f8295a77f6a82989292bin0 -> 276 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/3e/42ffc54a663f9401cc25843d6c0e71a33e4249bin0 -> 596 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/45/141a79a77842c59a63229403220a4e4be74e3dbin0 -> 36 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/45/5a314fa848d52ae1f11d254da4f60858fc97f4bin0 -> 446 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057bin0 -> 18 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/4d/713dc48e6b1bd75b0d61ad078ba9ca3a56745d2
-rw-r--r--tests/resources/attr/.gitted/objects/4e/49ba8c5b6c32ff28cd9dcb60be34df50fcc485bin0 -> 81 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/55/6f8c827b8e4a02ad5cab77dca2bcb3e226b0b3bin0 -> 24 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/58/19a185d77b03325aaf87cafc771db36f6ddca7bin0 -> 19 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/60/5812ab7fe421fdd325a935d35cb06a9234a7d72
-rw-r--r--tests/resources/attr/.gitted/objects/6b/ab5c79cd5140d0f800917f550eb2a3dc32b0da3
-rw-r--r--tests/resources/attr/.gitted/objects/6d/968d62c89c7d9ea23a4c9a7b665d017c3d8ffdbin0 -> 422 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/71/7fc31f6b84f9d6fc3a4edbca259d7fc92beee2bin0 -> 422 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/8d/0b9df9bd30be7910ddda60548d485bc302b9111
-rw-r--r--tests/resources/attr/.gitted/objects/93/61f40bb97239cf55811892e14de2e344168ba1bin0 -> 45 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/94/da4faa0a6bfb8ee6ccf7153801a69202b31857bin0 -> 124 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/96/089fd31ce1d3ee2afb0ba09ba063066932f027bin0 -> 422 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/99/eae476896f4907224978b88e5ecaa6c5bb67a9bin0 -> 95 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/9e/5bdc47d6a80f2be0ea3049ad74231b94609242bin0 -> 20 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/9f/b40b6675dde60b5697afceae91b66d908c02d9bin0 -> 151 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/a0/f7217ae99f5ac3e88534f5cea267febc5fa85b1
-rw-r--r--tests/resources/attr/.gitted/objects/a5/6bbcecaeac760cc26239384d2d4c614e7e4320bin0 -> 351 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/a5/d76cad53f66f1312bd995909a5bab3c08207704
-rw-r--r--tests/resources/attr/.gitted/objects/a9/7cc019851d401a4f1d091cb91a15890a0dd1ba2
-rw-r--r--tests/resources/attr/.gitted/objects/b4/35cd5689a0fb54afbeda4ac20368aa480e8f04bin0 -> 40 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/c0/091889c0c77142b87a1fa5123a6398a61d33e7bin0 -> 290 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/c4/85abe35abd4aa6fd83b076a78bbea9e2e7e06cbin0 -> 129 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/c7/aadd770d5907a8475c29e9ee21a27b88bf675dbin0 -> 60 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/c9/6bbb2c2557a8325ae1559e3ba79cdcecb230762
-rw-r--r--tests/resources/attr/.gitted/objects/ce/39a97a7fb1fa90bcf5e711249c1e507476ae0ebin0 -> 446 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/d5/7da33c16b14326ecb05d19bbea908f5e4c47d9bin0 -> 379 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/d8/00886d9c86731ae5c4a62b0b77c437015e00d2bin0 -> 18 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/dc/cada462d3df8ac6de596fb8c896aba9344f941bin0 -> 35 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/de/863bff4976c9ed7e17a4da0fd524908dc84049bin0 -> 4115 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/e5/63cf4758f0d646f1b14b76016aa17fa9e549a4bin0 -> 39 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/ec/b97df2a174987475ac816e3847fc8e9f6c596bbin0 -> 171 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/ed/f3dcee4003d71f139777898882ccd097e34c53bin0 -> 6289 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/f2/c6d717cf4a5a3e6b02684155ab07b766982165bin0 -> 44 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/f5/b0af1fb4f5c0cd7aad880711d368a07333c3072
-rw-r--r--tests/resources/attr/.gitted/objects/fb/5067b1aef3ac1ada4b379dbcb7d17255df7d78bin0 -> 28 bytes
-rw-r--r--tests/resources/attr/.gitted/objects/fe/773770c5a6cc7185580c9204b1ff18a33ff3fc1
-rw-r--r--tests/resources/attr/.gitted/objects/ff/69f8639ce2e6010b3f33a74160aad98b48da2bbin0 -> 18 bytes
-rw-r--r--tests/resources/attr/.gitted/refs/heads/master1
-rw-r--r--tests/resources/attr/attr01
-rw-r--r--tests/resources/attr/attr129
-rw-r--r--tests/resources/attr/attr221
-rw-r--r--tests/resources/attr/attr34
-rw-r--r--tests/resources/attr/attr47
-rw-r--r--tests/resources/attr/binfile1
-rw-r--r--tests/resources/attr/dir/file0
-rw-r--r--tests/resources/attr/file1
-rw-r--r--tests/resources/attr/gitattributes29
-rw-r--r--tests/resources/attr/gitignore2
-rw-r--r--tests/resources/attr/ign1
-rw-r--r--tests/resources/attr/macro_bad1
-rw-r--r--tests/resources/attr/macro_test1
-rw-r--r--tests/resources/attr/root_test11
-rw-r--r--tests/resources/attr/root_test26
-rw-r--r--tests/resources/attr/root_test319
-rw-r--r--tests/resources/attr/root_test4.txt14
-rw-r--r--tests/resources/attr/sub/.gitattributes7
-rw-r--r--tests/resources/attr/sub/abc37
-rw-r--r--tests/resources/attr/sub/dir/file0
-rw-r--r--tests/resources/attr/sub/file1
-rw-r--r--tests/resources/attr/sub/ign/file1
-rw-r--r--tests/resources/attr/sub/ign/sub/file1
-rw-r--r--tests/resources/attr/sub/sub/.gitattributes3
-rw-r--r--tests/resources/attr/sub/sub/dir0
-rw-r--r--tests/resources/attr/sub/sub/file1
-rw-r--r--tests/resources/attr/sub/sub/subsub.txt1
-rw-r--r--tests/resources/attr/sub/subdir_test12
-rw-r--r--tests/resources/attr/sub/subdir_test2.txt1
-rw-r--r--tests/resources/attr_index/.gitted/HEAD1
-rw-r--r--tests/resources/attr_index/.gitted/config6
-rw-r--r--tests/resources/attr_index/.gitted/description1
-rw-r--r--tests/resources/attr_index/.gitted/indexbin0 -> 520 bytes
-rw-r--r--tests/resources/attr_index/.gitted/info/exclude6
-rw-r--r--tests/resources/attr_index/.gitted/info/refs1
-rw-r--r--tests/resources/attr_index/.gitted/logs/HEAD4
-rw-r--r--tests/resources/attr_index/.gitted/logs/refs/heads/master4
-rw-r--r--tests/resources/attr_index/.gitted/objects/38/12cfef36615db1788d4e63f90028007e17a3483
-rw-r--r--tests/resources/attr_index/.gitted/objects/59/d942b8be2784bc96db9b22202c10815c9a077b1
-rw-r--r--tests/resources/attr_index/.gitted/objects/cd/f17ea3fe625ef812f4dce7f423f4f299287505bin0 -> 61 bytes
-rw-r--r--tests/resources/attr_index/.gitted/objects/f7/2502ddd01412bb20796ff812af56fd53b82b52bin0 -> 149 bytes
-rw-r--r--tests/resources/attr_index/.gitted/objects/info/packs2
-rw-r--r--tests/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.idxbin0 -> 1492 bytes
-rw-r--r--tests/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.packbin0 -> 1106 bytes
-rw-r--r--tests/resources/attr_index/.gitted/packed-refs2
-rw-r--r--tests/resources/attr_index/.gitted/refs/heads/master1
-rw-r--r--tests/resources/attr_index/README.md1
-rw-r--r--tests/resources/attr_index/README.txt1
-rw-r--r--tests/resources/attr_index/gitattributes4
-rw-r--r--tests/resources/attr_index/sub/sub/.gitattributes3
-rw-r--r--tests/resources/attr_index/sub/sub/README.md1
-rw-r--r--tests/resources/attr_index/sub/sub/README.txt1
-rw-r--r--tests/resources/bad.indexbin0 -> 412 bytes
-rw-r--r--tests/resources/bad_tag.git/HEAD1
-rw-r--r--tests/resources/bad_tag.git/config5
-rw-r--r--tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.idxbin0 -> 1268 bytes
-rw-r--r--tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.packbin0 -> 596 bytes
-rw-r--r--tests/resources/bad_tag.git/packed-refs5
-rw-r--r--tests/resources/bad_tag.git/refs/dummy-marker.txt0
-rw-r--r--tests/resources/big.indexbin0 -> 335272 bytes
-rw-r--r--tests/resources/binaryunicode/.gitted/HEAD1
-rw-r--r--tests/resources/binaryunicode/.gitted/config6
-rw-r--r--tests/resources/binaryunicode/.gitted/description1
-rw-r--r--tests/resources/binaryunicode/.gitted/indexbin0 -> 104 bytes
-rw-r--r--tests/resources/binaryunicode/.gitted/info/exclude6
-rw-r--r--tests/resources/binaryunicode/.gitted/info/refs3
-rw-r--r--tests/resources/binaryunicode/.gitted/objects/info/packs2
-rw-r--r--tests/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.idxbin0 -> 1380 bytes
-rw-r--r--tests/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.packbin0 -> 20879 bytes
-rw-r--r--tests/resources/binaryunicode/.gitted/refs/heads/branch11
-rw-r--r--tests/resources/binaryunicode/.gitted/refs/heads/branch21
-rw-r--r--tests/resources/binaryunicode/.gitted/refs/heads/master1
-rw-r--r--tests/resources/binaryunicode/file.txt1
-rw-r--r--tests/resources/blametest.git/HEAD1
-rw-r--r--tests/resources/blametest.git/config5
-rw-r--r--tests/resources/blametest.git/description1
-rw-r--r--tests/resources/blametest.git/objects/0c/bab4d45fd61e55a1c9697f9f9cb07a12e15448bin0 -> 46 bytes
-rw-r--r--tests/resources/blametest.git/objects/1a/ac69ae5d96461afc4d81d0066cb12f5b05a35bbin0 -> 28 bytes
-rw-r--r--tests/resources/blametest.git/objects/1b/5f0775af166331c854bd8d1bca3450eaf2532abin0 -> 35 bytes
-rw-r--r--tests/resources/blametest.git/objects/1d/81379086fd6d91ee027e883cf6f4703a107dfbbin0 -> 133 bytes
-rw-r--r--tests/resources/blametest.git/objects/34/73c3e21e76492d09b80b7c75569edc275dffcfbin0 -> 66 bytes
-rw-r--r--tests/resources/blametest.git/objects/37/681a80ca21064efd5c3bf2ef41eb3d05a1428bbin0 -> 106 bytes
-rw-r--r--tests/resources/blametest.git/objects/40/fcae7fb84378fdb037dc6a3ccbb33669c3f26dbin0 -> 63 bytes
-rw-r--r--tests/resources/blametest.git/objects/46/ef45f4ae55c1f5dca64b9e1d7ca77c1798069bbin0 -> 132 bytes
-rw-r--r--tests/resources/blametest.git/objects/48/2f2c370e35c2c314fc1f96db2beb33f955a26abin0 -> 35 bytes
-rw-r--r--tests/resources/blametest.git/objects/4b/0ca755f5bfd69ed6074f268b05bb0542a42c68bin0 -> 853 bytes
-rw-r--r--tests/resources/blametest.git/objects/4d/8400b7ce2d15ef5045c2775ed33e82a326786ebin0 -> 160 bytes
-rw-r--r--tests/resources/blametest.git/objects/4e/ecfea484f8005d101e547f6bfb07c99e2b114ebin0 -> 163 bytes
-rw-r--r--tests/resources/blametest.git/objects/5a/572e2e94825f54b95417eacaa089d560c5a5e9bin0 -> 324 bytes
-rw-r--r--tests/resources/blametest.git/objects/63/d671eb32d250e4a83766ebbc60e818c1e1e93a3
-rw-r--r--tests/resources/blametest.git/objects/63/eb57322e363e18d460da5ea8284f3cd2340b36bin0 -> 76 bytes
-rw-r--r--tests/resources/blametest.git/objects/66/53ff42313eb5c82806f145391b18a9699800c7bin0 -> 160 bytes
-rw-r--r--tests/resources/blametest.git/objects/6b/52ee554131a5e7bacd15553fbd22408c5a8a6fbin0 -> 62 bytes
-rw-r--r--tests/resources/blametest.git/objects/70/2c7aa5250abc42be69ef78ee8fa47a346cb2cebin0 -> 828 bytes
-rw-r--r--tests/resources/blametest.git/objects/77/c796837eb003c81d2cd8a6577ef4e7edc61222bin0 -> 74 bytes
-rw-r--r--tests/resources/blametest.git/objects/7e/135d94af53b6c5edbae6a77df8a0f09375e823bin0 -> 74 bytes
-rw-r--r--tests/resources/blametest.git/objects/83/6bc00b06cb60eb0f629e237ad2b58adb2cfc7e3
-rw-r--r--tests/resources/blametest.git/objects/8b/137891791fe96927ad78e64b0aad7bded08bdcbin0 -> 16 bytes
-rw-r--r--tests/resources/blametest.git/objects/92/5bddd7a536a66eecb32faa41abd5bc9c192311bin0 -> 72 bytes
-rw-r--r--tests/resources/blametest.git/objects/96/679d59cf9f74d69b3c920f258559b5e8c9a18abin0 -> 47 bytes
-rw-r--r--tests/resources/blametest.git/objects/98/89d6e5557761aa8e3607e80c874a6dc51ada7cbin0 -> 43 bytes
-rw-r--r--tests/resources/blametest.git/objects/a3/4ead35680be7b9704fc4c6d750d182e228e02bbin0 -> 160 bytes
-rw-r--r--tests/resources/blametest.git/objects/a4/641ad869ffad601aa8347e0770e949bb6d90dfbin0 -> 161 bytes
-rw-r--r--tests/resources/blametest.git/objects/a8/ba8436b5d8ccbdfd5be597c194e7bb8e0a092f1
-rw-r--r--tests/resources/blametest.git/objects/aa/06ecca6c4ad6432ab9313e556ca92ba4bcf9e91
-rw-r--r--tests/resources/blametest.git/objects/ad/9cb4eac23df2fe5e1264287a5872ea2a1ff8b2bin0 -> 106 bytes
-rw-r--r--tests/resources/blametest.git/objects/b1/76dfc3a4dc8734e4c579f77236a9c8d0a965d2bin0 -> 76 bytes
-rw-r--r--tests/resources/blametest.git/objects/b9/0bb887b7c03750ae6b352ffe76ab9d2e86ee7dbin0 -> 56 bytes
-rw-r--r--tests/resources/blametest.git/objects/b9/9f7ac0b88909253d829554c14af488c3b0f3a52
-rw-r--r--tests/resources/blametest.git/objects/ba/9089263dce882885ad84513f31495bf9d31132bin0 -> 132 bytes
-rw-r--r--tests/resources/blametest.git/objects/bc/7c5ac2bafe828a68e9d1d460343718d6fbe1363
-rw-r--r--tests/resources/blametest.git/objects/c3/8d3c99946b74173f9b037279f07d505195563fbin0 -> 849 bytes
-rw-r--r--tests/resources/blametest.git/objects/c4/c13c153a611418325c70d6e630fed373546c4dbin0 -> 824 bytes
-rw-r--r--tests/resources/blametest.git/objects/cf/e0e1e1e3ba18f149fd47f5e1aef6016b2260c3bin0 -> 76 bytes
-rw-r--r--tests/resources/blametest.git/objects/d0/67729932057cdb7527a833d6799c4ddc5206401
-rw-r--r--tests/resources/blametest.git/objects/d2/bc4f27cbb72260eeec350087d81a60a122efe9bin0 -> 60 bytes
-rw-r--r--tests/resources/blametest.git/objects/d3/c7316f0075debfe53b25e58f56b0a4b46e18c34
-rw-r--r--tests/resources/blametest.git/objects/d6/7268771ef5244f4aa224df29d4e4ae0bed2fd8bin0 -> 73 bytes
-rw-r--r--tests/resources/blametest.git/objects/d6/afeea2c4657c743dedab24a8a62da96f63547dbin0 -> 161 bytes
-rw-r--r--tests/resources/blametest.git/objects/d9/3e87a0863c7ec5e772f99e72ca9efddf0ca718bin0 -> 825 bytes
-rw-r--r--tests/resources/blametest.git/objects/da/237394e6132d20d30f175b9b73c8638fddddda4
-rw-r--r--tests/resources/blametest.git/objects/de/9fe35f9906e1994e083cc59c87232bf418795bbin0 -> 331 bytes
-rw-r--r--tests/resources/blametest.git/objects/e5/b41c1ea533f87388ab69b13baf0b5a562d6243bin0 -> 76 bytes
-rw-r--r--tests/resources/blametest.git/objects/ef/32df4d259143933715c74951f932d9892364d1bin0 -> 42 bytes
-rw-r--r--tests/resources/blametest.git/objects/f0/5190494260c2f6b6d045ac9bf27cb6d7e0abccbin0 -> 133 bytes
-rw-r--r--tests/resources/blametest.git/objects/f4/f4b926582a2c23c6e3ba05309eaa89244c1d68bin0 -> 839 bytes
-rw-r--r--tests/resources/blametest.git/objects/f9/264f7fbd31ae7a18b7931ed8946fb0aebb0af3bin0 -> 28 bytes
-rw-r--r--tests/resources/blametest.git/objects/fa/01940156471352d5483b4f26b7c849dfaa7eefbin0 -> 826 bytes
-rw-r--r--tests/resources/blametest.git/refs/heads/master1
-rw-r--r--tests/resources/certs/61f2ddb6.031
-rw-r--r--tests/resources/certs/db4f60b0.031
-rw-r--r--tests/resources/cherrypick/.gitted/HEAD1
-rw-r--r--tests/resources/cherrypick/.gitted/config7
-rw-r--r--tests/resources/cherrypick/.gitted/indexbin0 -> 248 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/info/exclude6
-rw-r--r--tests/resources/cherrypick/.gitted/objects/01/a2b453c2647c71ccfefc285f2266d1f00b8253bin0 -> 30 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/02/67838e09bbc5969bba035be2d27c8a6de694d8bin0 -> 38 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/06/3fc9f01e6e9ec2a8d8f749885e931875e50d37bin0 -> 141 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/08/9ac03f76058b5ba0b44bb268f317f9242481e93
-rw-r--r--tests/resources/cherrypick/.gitted/objects/0d/447a6c2528b06616cde3b209a4b4ea3dcb8d65bin0 -> 107 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/11/24c2c1ae07b26fded662d6c3f3631d9dc16f88bin0 -> 31 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/12/905f4ea5b76f9d3fdcfe73e462201c06ae632abin0 -> 108 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/19/c5c7207054604b69c84d08a7571ef9672bb5c2bin0 -> 28 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/1c/2116845780455ecf916538c1cc27c4222452afbin0 -> 116 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/1c/c85eb4ff0a8438fde1b14274c6f87f891b36a0bin0 -> 117 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/1e/1cb7391d25dcd8daba88f1f627f3045982286cbin0 -> 32 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/20/fc1a4c9d994021f43d33ab75e4252e27ca661dbin0 -> 126 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/28/d9eb4208074ad1cc84e71ccc908b34573f05d2bin0 -> 28 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/2a/26c7e88b285613b302ba76712bc998863f3cbc1
-rw-r--r--tests/resources/cherrypick/.gitted/objects/2a/c3b376093de405b0a951bff578655b1c2b7fa11
-rw-r--r--tests/resources/cherrypick/.gitted/objects/2c/acbcaabf785f1ac231e8519849d4ad38692f2cbin0 -> 26 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/35/cb210149022c7379b0a67b0dec13cc628ff87dbin0 -> 137 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/38/c05a857e831a7e759d83778bfc85d003e21c45bin0 -> 27 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/3f/9eed8946df9e2c737d3b8dc0b8e78959aacd925
-rw-r--r--tests/resources/cherrypick/.gitted/objects/40/9a1bec58bf35348e8b62b72bb9c1f45cf5a587bin0 -> 33 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/44/cd2ed2052c9c68f9a439d208e9614dc2a55c701
-rw-r--r--tests/resources/cherrypick/.gitted/objects/48/7434cace79238a7091e2220611d4f20a765690bin0 -> 33 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/49/20ad2f17162dcc8823ad491444dcb87f5899c9bin0 -> 36 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904bin0 -> 15 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/4c/532774cc1fea37f6efc2256763a64d38c8cddebin0 -> 26 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/51/145af30d411a50195b66517d825e69bf57ed22bin0 -> 107 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/54/61de53ffadbf15be4dd6345997c156895732094
-rw-r--r--tests/resources/cherrypick/.gitted/objects/54/784f10955e92ab27e4fa832e40cb2baf1edbdcbin0 -> 74 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/56/3f6473a3858f99b80e5f93c660512ed38e1e6fbin0 -> 31 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/58/a957ef0061c1a8ef995c855dfab4f5da8d6617bin0 -> 32 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/5d/c7e1f440ce74d5503a0dfbc6c30e091475f774bin0 -> 31 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/5e/2206cda1c56430ad107a6866a829c159e0b9ea1
-rw-r--r--tests/resources/cherrypick/.gitted/objects/5f/77a2a13935ac62a629553f8944ad57b1ed8b4abin0 -> 106 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/63/c0d92b95253c4a40d3883f423a54be47d2c4c8bin0 -> 30 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/6c/e83eb5f0fd34a10c3d25c6b36d2ed7ec0d6ce7bin0 -> 108 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/6d/1c2afe5eeb9e497528e2780ac468a5465cbc961
-rw-r--r--tests/resources/cherrypick/.gitted/objects/74/f06b5bfec6d33d7264f73606b57a7c0b963819bin0 -> 141 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/82/8b08c52d2cba30952e0e008f60b25b5ba0d41abin0 -> 107 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/85/36dd6f0ec3ddecb9f9b6c8c64c6d322cd01211bin0 -> 36 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/85/a4a1d791973644f24c72f5e89420d3064cc452bin0 -> 27 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/8b/5c30499a71001189b647f4d5b57fa8f04897cebin0 -> 107 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/96/4ea3da044d9083181a88ba6701de9e35778bf4bin0 -> 181 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/9c/c39fca3765a2facbe31157f7d60c2602193f36bin0 -> 107 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/9c/cb9bf50c011fd58dcbaa65df917bf79539717fbin0 -> 30 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/a1/0b59f4280491afe6e430c30654a7acc67d4a33bin0 -> 30 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/a2/1b4bfe7a04ab18024fb57f4ae9a52a1acef394bin0 -> 173 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/a4/3a050c588d4e92f11a6b139680923e9728477d1
-rw-r--r--tests/resources/cherrypick/.gitted/objects/a5/8ca3fee5eb68b11adc2703e5843f968c9dad1ebin0 -> 28 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/a6/61b5dec1004e2c62654ded3762370c27cf266bbin0 -> 27 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/a6/9ef8fcbb9a2c509a7dbf4f23d257eb551d56101
-rw-r--r--tests/resources/cherrypick/.gitted/objects/a8/3c6f70297b805dedc549e6583582966f6ebcabbin0 -> 138 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/a9/020cd240774e4d672732bcb82d516d9685da76bin0 -> 26 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/ab/4115f808bc585b60f822da7020af86d20f62c8bin0 -> 213 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/ab/e4603bc7cd5b8167a267e0e2418fd2348f8cff4
-rw-r--r--tests/resources/cherrypick/.gitted/objects/b8/26e9b36e22e949ec885e7a1f3db496bbab6cd0bin0 -> 108 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/ba/fbf6912c09505ac60575cd43d3f2aba3bd84d8bin0 -> 175 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/bb/14296ffa9dfbf935ec9ce2f9ed7808d952226bbin0 -> 38 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/bc/4dd0744364d1db380a9811bd264c101065231ebin0 -> 55 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/bd/65d4083845ed5ed4e1fe5feb85ac395d0760c82
-rw-r--r--tests/resources/cherrypick/.gitted/objects/bd/6ffc8c6c41f0f85ff9e3d61c9479516bac0024bin0 -> 31 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/bd/a51965cb36c0c5731c8cb50b80a36cac81018ebin0 -> 107 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/ce/d8fb81b6ec534d5deaf2a48b4b96c7997125071
-rw-r--r--tests/resources/cherrypick/.gitted/objects/cf/c4f0999a8367568e049af4f72e452d40828a15bin0 -> 180 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/d0/f21e17beb5b9d953b1d8349049818a4f2edd1e1
-rw-r--r--tests/resources/cherrypick/.gitted/objects/d3/d77487660ee3c0194ee01dc5eaf478782b1c7e1
-rw-r--r--tests/resources/cherrypick/.gitted/objects/e2/33b9ed408a95e9d4b65fec7fc34943a556deb2bin0 -> 31 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/e5/183bfd18e3a0a691fadde2f0d5610b73282d31bin0 -> 33 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/e6/ae8889c40c77d7be02758235b5b3f7a4f2a129bin0 -> 107 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/e7/811a2bc55635f182750f0420da5ad232c1af91bin0 -> 107 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/e9/b63f3655b2ad80c0ff587389b5a9589a3a71102
-rw-r--r--tests/resources/cherrypick/.gitted/objects/eb/da71fe44dcb60c53b8fbd53208a1204d32e959bin0 -> 36 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/f0/5ed049854c1596a7cc0e957fab34961077f3aebin0 -> 36 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/f0/a4e1c66bb548cd2b22eebefda703872e969775bin0 -> 191 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/f2/ec8c8cf1a9fb7aa047a25a4308bfe860237ad4bin0 -> 32 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/f5/684c96bf40c709877b56404cd8a5dd2d2a7978bin0 -> 106 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/objects/f9/0f9dcbdac2cce5cc166346160e19cb693ef4e8bin0 -> 31 bytes
-rw-r--r--tests/resources/cherrypick/.gitted/refs/heads/automerge-branch1
-rw-r--r--tests/resources/cherrypick/.gitted/refs/heads/master1
-rw-r--r--tests/resources/cherrypick/.gitted/refs/heads/merge-branch1
-rw-r--r--tests/resources/cherrypick/.gitted/refs/heads/merge-conflicts1
-rw-r--r--tests/resources/cherrypick/.gitted/refs/heads/merge-mainline1
-rw-r--r--tests/resources/cherrypick/.gitted/refs/heads/orphan1
-rw-r--r--tests/resources/cherrypick/.gitted/refs/heads/renames1
-rw-r--r--tests/resources/cherrypick/file1.txt15
-rw-r--r--tests/resources/cherrypick/file2.txt15
-rw-r--r--tests/resources/cherrypick/file3.txt15
-rw-r--r--tests/resources/config/.gitconfig3
-rw-r--r--tests/resources/config/config-include2
-rw-r--r--tests/resources/config/config-included2
-rw-r--r--tests/resources/config/config-nosection1
-rw-r--r--tests/resources/config/config-oom40000
-rw-r--r--tests/resources/config/config07
-rw-r--r--tests/resources/config/config15
-rw-r--r--tests/resources/config/config101
-rw-r--r--tests/resources/config/config115
-rw-r--r--tests/resources/config/config1223
-rw-r--r--tests/resources/config/config132
-rw-r--r--tests/resources/config/config144
-rw-r--r--tests/resources/config/config153
-rw-r--r--tests/resources/config/config163
-rw-r--r--tests/resources/config/config173
-rw-r--r--tests/resources/config/config185
-rw-r--r--tests/resources/config/config195
-rw-r--r--tests/resources/config/config25
-rw-r--r--tests/resources/config/config2011
-rw-r--r--tests/resources/config/config215
-rw-r--r--tests/resources/config/config2210
-rw-r--r--tests/resources/config/config33
-rw-r--r--tests/resources/config/config45
-rw-r--r--tests/resources/config/config59
-rw-r--r--tests/resources/config/config65
-rw-r--r--tests/resources/config/config75
-rw-r--r--tests/resources/config/config80
-rw-r--r--tests/resources/config/config99
-rw-r--r--tests/resources/crlf.git/HEAD1
-rw-r--r--tests/resources/crlf.git/config13
-rw-r--r--tests/resources/crlf.git/logs/HEAD2
-rw-r--r--tests/resources/crlf.git/logs/refs/heads/master2
-rw-r--r--tests/resources/crlf.git/logs/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/crlf.git/objects/04/4bcd5c9bf5ebdd51e514a9a36457018f06f6e11
-rw-r--r--tests/resources/crlf.git/objects/04/de00b358f13389948756732158eaaaefa1448cbin0 -> 28 bytes
-rw-r--r--tests/resources/crlf.git/objects/05/5c8729cdcc372500a08db659c045e16c4409fbbin0 -> 20 bytes
-rw-r--r--tests/resources/crlf.git/objects/09/7722be9b67b48dfe3b19396d02fd535300ee46bin0 -> 344 bytes
-rw-r--r--tests/resources/crlf.git/objects/0a/a76e474d259bd7c13eb726a1396c381db55c88bin0 -> 27 bytes
-rw-r--r--tests/resources/crlf.git/objects/0d/06894e14df22e066763ae906e0ed3eb79c205fbin0 -> 134 bytes
-rw-r--r--tests/resources/crlf.git/objects/0e/052888828a954ca17e5882638e3c6a083e75c0bin0 -> 107 bytes
-rw-r--r--tests/resources/crlf.git/objects/0f/f5a53f19bfd2b5eea1ba550295c47515678987bin0 -> 29 bytes
-rw-r--r--tests/resources/crlf.git/objects/12/4f4293444614aa8da53be149792c2e43e9bfd94
-rw-r--r--tests/resources/crlf.git/objects/16/78031ee023a23bd3515e4e1693b661a69f0a73bin0 -> 193 bytes
-rw-r--r--tests/resources/crlf.git/objects/16/c72b67861f8524a5bebc05cd20472d3fca00dabin0 -> 64 bytes
-rw-r--r--tests/resources/crlf.git/objects/18/c637c5d9aba6eed226ee1840cd1ca2e6c4e4c5bin0 -> 442 bytes
-rw-r--r--tests/resources/crlf.git/objects/1e/c507638b806aba45d6142082885f2a9e88322dbin0 -> 170 bytes
-rw-r--r--tests/resources/crlf.git/objects/20/3555c5676d75cd80d69b50beb1f4b588c59cebbin0 -> 36 bytes
-rw-r--r--tests/resources/crlf.git/objects/23/f4582779e60bfa7f14750ad507399a58876611bin0 -> 219 bytes
-rw-r--r--tests/resources/crlf.git/objects/2a/d3df895f68f4dda6a0a815c620b909bdd27c05bin0 -> 462 bytes
-rw-r--r--tests/resources/crlf.git/objects/2b/55b4b94f655c857635b6a9005c056aa7de35322
-rw-r--r--tests/resources/crlf.git/objects/2b/d9d81b51a867352bab307b89cbb5b4a69adfe1bin0 -> 336 bytes
-rw-r--r--tests/resources/crlf.git/objects/2c/03f9f407b576eae80327864bab572e282a33eabin0 -> 455 bytes
-rw-r--r--tests/resources/crlf.git/objects/33/cdead44e1c3ec178e39a4a69085280dbacf01bbin0 -> 221 bytes
-rw-r--r--tests/resources/crlf.git/objects/38/1cfe630df902bc29271a202d3277981180e4a6bin0 -> 25 bytes
-rw-r--r--tests/resources/crlf.git/objects/3f/96bdca0e37616026afaa325c148cec4aa62d04bin0 -> 164 bytes
-rw-r--r--tests/resources/crlf.git/objects/41/7786fc35b3c71aa546e3f95eb5da3c8dad8c41bin0 -> 36 bytes
-rw-r--r--tests/resources/crlf.git/objects/44/b0be18671a284f1156117b6338edac2663341cbin0 -> 658 bytes
-rw-r--r--tests/resources/crlf.git/objects/47/fbc2c28a18df0dc773276a253eb85c7516ca50bin0 -> 36 bytes
-rw-r--r--tests/resources/crlf.git/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904bin0 -> 15 bytes
-rw-r--r--tests/resources/crlf.git/objects/55/1b8fce462bba005ab6d34a2244d8a3f6b03dd0bin0 -> 658 bytes
-rw-r--r--tests/resources/crlf.git/objects/5a/fb6a14a864e30787857dd92af837e8cdd2cb1bbin0 -> 194 bytes
-rw-r--r--tests/resources/crlf.git/objects/68/03c385642cebc8103fddd526ef395d75678a7e2
-rw-r--r--tests/resources/crlf.git/objects/69/597764abeaa1a403ebf589d2ea579c6a8f877e1
-rw-r--r--tests/resources/crlf.git/objects/6a/e3e9c11a51f0aabebcffcbd5c00f4beed143c9bin0 -> 87 bytes
-rw-r--r--tests/resources/crlf.git/objects/6b/9d5748663795f573ea857276eb2a5f8330efa01
-rw-r--r--tests/resources/crlf.git/objects/6c/589757f65a970a6cc07c71c3f3d2528c611cbc2
-rw-r--r--tests/resources/crlf.git/objects/72/10e91413baa3d9b90215e970ae53397ecc526ebin0 -> 85 bytes
-rw-r--r--tests/resources/crlf.git/objects/77/afe26d93c49279ca90604c125496920753fedebin0 -> 178 bytes
-rw-r--r--tests/resources/crlf.git/objects/78/db270c1841841f75a8157321bdcb50ab12e6c3bin0 -> 156 bytes
-rw-r--r--tests/resources/crlf.git/objects/79/9770d1cff46753a57db7a066159b5610da6e3abin0 -> 20 bytes
-rw-r--r--tests/resources/crlf.git/objects/7c/ce67e58173e2b01f7db124ceaabe3183d19c49bin0 -> 24 bytes
-rw-r--r--tests/resources/crlf.git/objects/85/340755cfe5e28c2835781978bb1cece91b3d0fbin0 -> 37 bytes
-rw-r--r--tests/resources/crlf.git/objects/92/0e90a663bea5d740989d5f935f6dfb473a0c5dbin0 -> 303 bytes
-rw-r--r--tests/resources/crlf.git/objects/96/87e444bcbb85645cb496080434c292f1b571821
-rw-r--r--tests/resources/crlf.git/objects/97/449da2d225557c558ac244384d487e66c3e591bin0 -> 177 bytes
-rw-r--r--tests/resources/crlf.git/objects/9a/6c3533fef19abd6eec8e61206b5c51982b80d9bin0 -> 58 bytes
-rw-r--r--tests/resources/crlf.git/objects/9d/29b5bb165bf65637ffcb5ededb82ddd7c3fd13bin0 -> 227 bytes
-rw-r--r--tests/resources/crlf.git/objects/a2/34455d62297f1856c4603686150c59fcb0aafebin0 -> 189 bytes
-rw-r--r--tests/resources/crlf.git/objects/a9/a2e8913c1dbe2812fac5e6b4e0a4bd5d0d59661
-rw-r--r--tests/resources/crlf.git/objects/aa/f083a9cb53dac3669dcfa0e48921580d629ec7bin0 -> 37 bytes
-rw-r--r--tests/resources/crlf.git/objects/af/6fcf6da196f615d7cda269b55b5c4ecfb4a5b3bin0 -> 36 bytes
-rw-r--r--tests/resources/crlf.git/objects/b8/986fec0f7bde90f78ac72706e782d82f24f2f03
-rw-r--r--tests/resources/crlf.git/objects/bb/29a7b46b5d4ba3ea17b238ae561b81d59dc818bin0 -> 170 bytes
-rw-r--r--tests/resources/crlf.git/objects/c3/e11722855ff260bd27418988ac1467c4e9e73abin0 -> 261 bytes
-rw-r--r--tests/resources/crlf.git/objects/c8/d0b1ebcaccdd8f968c4aae3c2175e7fed651fe2
-rw-r--r--tests/resources/crlf.git/objects/cd/574f5a2baa4c79504f8837b730fa0b11defe99bin0 -> 62 bytes
-rw-r--r--tests/resources/crlf.git/objects/cd/d3dacc5c0501d5ea57bbdf90e3d80176606139bin0 -> 565 bytes
-rw-r--r--tests/resources/crlf.git/objects/d1/1e7ef63ba7db1db3b1b99cdbafc57a8549f8a4bin0 -> 35 bytes
-rw-r--r--tests/resources/crlf.git/objects/dc/88e3b917de821e25962bea7ec1f55c4ce2112cbin0 -> 32 bytes
-rw-r--r--tests/resources/crlf.git/objects/de/5bfa165999d9d6c6dbafad2a7e709f93ec30fdbin0 -> 179 bytes
-rw-r--r--tests/resources/crlf.git/objects/e0/be8c0fa467f0a554484347c12802799d6c04fabin0 -> 600 bytes
-rw-r--r--tests/resources/crlf.git/objects/e1/379fd9942d04e7e80892b866d37bdb7da9e4e1bin0 -> 599 bytes
-rw-r--r--tests/resources/crlf.git/objects/e5/062da7d7802cf492975eda580f09ac4876bd881
-rw-r--r--tests/resources/crlf.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391bin0 -> 15 bytes
-rw-r--r--tests/resources/crlf.git/objects/ea/030d3c6cec212069eca698cabaa5b4550f1511bin0 -> 32 bytes
-rw-r--r--tests/resources/crlf.git/objects/ea/a6ce5bc192f4c3c19354e7434c01e4686e95d7bin0 -> 629 bytes
-rw-r--r--tests/resources/crlf.git/objects/ef/0dcd356d77221e9c27f4f3928ad28e80b87cebbin0 -> 168 bytes
-rw-r--r--tests/resources/crlf.git/objects/f2/b745d7f47d114a3a6b31a7b628e61e804d1a58bin0 -> 561 bytes
-rw-r--r--tests/resources/crlf.git/objects/f4/d25b796d86387205a5498175d66e91d1e5006abin0 -> 106 bytes
-rw-r--r--tests/resources/crlf.git/objects/fa/1385d99a319b43c06f5309d1aae9fdd3adea46bin0 -> 23 bytes
-rw-r--r--tests/resources/crlf.git/objects/fe/085d9ace90cc675b87df15e1aeed0c3a31407fbin0 -> 139 bytes
-rw-r--r--tests/resources/crlf.git/objects/fe/ab3713c4659bb22700042b3c55b8d60d0a952bbin0 -> 568 bytes
-rw-r--r--tests/resources/crlf.git/packed-refs3
-rw-r--r--tests/resources/crlf.git/refs/heads/empty-files1
-rw-r--r--tests/resources/crlf.git/refs/heads/ident1
-rw-r--r--tests/resources/crlf.git/refs/heads/master1
-rw-r--r--tests/resources/crlf.git/refs/heads/no-ident1
-rw-r--r--tests/resources/crlf.git/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/crlf/.gitattributes1
-rw-r--r--tests/resources/crlf/.gitted/HEAD1
-rw-r--r--tests/resources/crlf/.gitted/config0
-rw-r--r--tests/resources/crlf/.gitted/indexbin0 -> 1912 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/04/4bcd5c9bf5ebdd51e514a9a36457018f06f6e11
-rw-r--r--tests/resources/crlf/.gitted/objects/04/de00b358f13389948756732158eaaaefa1448cbin0 -> 28 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/09/7722be9b67b48dfe3b19396d02fd535300ee46bin0 -> 344 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/0a/a76e474d259bd7c13eb726a1396c381db55c88bin0 -> 27 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/0d/06894e14df22e066763ae906e0ed3eb79c205fbin0 -> 134 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/0e/052888828a954ca17e5882638e3c6a083e75c0bin0 -> 107 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/0f/f5a53f19bfd2b5eea1ba550295c47515678987bin0 -> 29 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/16/78031ee023a23bd3515e4e1693b661a69f0a73bin0 -> 193 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/16/c72b67861f8524a5bebc05cd20472d3fca00dabin0 -> 64 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/18/c637c5d9aba6eed226ee1840cd1ca2e6c4e4c5bin0 -> 442 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/20/3555c5676d75cd80d69b50beb1f4b588c59cebbin0 -> 36 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/23/f4582779e60bfa7f14750ad507399a58876611bin0 -> 219 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/2a/d3df895f68f4dda6a0a815c620b909bdd27c05bin0 -> 462 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/2b/55b4b94f655c857635b6a9005c056aa7de35322
-rw-r--r--tests/resources/crlf/.gitted/objects/2b/d9d81b51a867352bab307b89cbb5b4a69adfe1bin0 -> 336 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/2c/03f9f407b576eae80327864bab572e282a33eabin0 -> 455 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/33/cdead44e1c3ec178e39a4a69085280dbacf01bbin0 -> 221 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/38/1cfe630df902bc29271a202d3277981180e4a6bin0 -> 25 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/3f/96bdca0e37616026afaa325c148cec4aa62d04bin0 -> 164 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/41/7786fc35b3c71aa546e3f95eb5da3c8dad8c41bin0 -> 36 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/47/fbc2c28a18df0dc773276a253eb85c7516ca50bin0 -> 36 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904bin0 -> 15 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/5a/fb6a14a864e30787857dd92af837e8cdd2cb1bbin0 -> 194 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/68/03c385642cebc8103fddd526ef395d75678a7e2
-rw-r--r--tests/resources/crlf/.gitted/objects/69/597764abeaa1a403ebf589d2ea579c6a8f877e1
-rw-r--r--tests/resources/crlf/.gitted/objects/6a/e3e9c11a51f0aabebcffcbd5c00f4beed143c9bin0 -> 87 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/6c/589757f65a970a6cc07c71c3f3d2528c611cbc2
-rw-r--r--tests/resources/crlf/.gitted/objects/77/afe26d93c49279ca90604c125496920753fedebin0 -> 178 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/78/db270c1841841f75a8157321bdcb50ab12e6c3bin0 -> 156 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/79/9770d1cff46753a57db7a066159b5610da6e3abin0 -> 20 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/7c/ce67e58173e2b01f7db124ceaabe3183d19c49bin0 -> 24 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/85/340755cfe5e28c2835781978bb1cece91b3d0fbin0 -> 37 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/92/0e90a663bea5d740989d5f935f6dfb473a0c5dbin0 -> 303 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/96/87e444bcbb85645cb496080434c292f1b571821
-rw-r--r--tests/resources/crlf/.gitted/objects/97/449da2d225557c558ac244384d487e66c3e591bin0 -> 177 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/9a/6c3533fef19abd6eec8e61206b5c51982b80d9bin0 -> 58 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/9d/29b5bb165bf65637ffcb5ededb82ddd7c3fd13bin0 -> 227 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/a2/34455d62297f1856c4603686150c59fcb0aafebin0 -> 189 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/a9/a2e8913c1dbe2812fac5e6b4e0a4bd5d0d59661
-rw-r--r--tests/resources/crlf/.gitted/objects/aa/f083a9cb53dac3669dcfa0e48921580d629ec7bin0 -> 37 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/af/6fcf6da196f615d7cda269b55b5c4ecfb4a5b3bin0 -> 36 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/bb/29a7b46b5d4ba3ea17b238ae561b81d59dc818bin0 -> 170 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/c3/e11722855ff260bd27418988ac1467c4e9e73abin0 -> 261 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/c8/d0b1ebcaccdd8f968c4aae3c2175e7fed651fe2
-rw-r--r--tests/resources/crlf/.gitted/objects/cd/574f5a2baa4c79504f8837b730fa0b11defe99bin0 -> 62 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/cd/d3dacc5c0501d5ea57bbdf90e3d80176606139bin0 -> 565 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/d1/1e7ef63ba7db1db3b1b99cdbafc57a8549f8a4bin0 -> 35 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/dc/88e3b917de821e25962bea7ec1f55c4ce2112cbin0 -> 32 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/de/5bfa165999d9d6c6dbafad2a7e709f93ec30fdbin0 -> 179 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/e5/062da7d7802cf492975eda580f09ac4876bd881
-rw-r--r--tests/resources/crlf/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391bin0 -> 15 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/ea/030d3c6cec212069eca698cabaa5b4550f1511bin0 -> 32 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/ef/0dcd356d77221e9c27f4f3928ad28e80b87cebbin0 -> 168 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/f2/b745d7f47d114a3a6b31a7b628e61e804d1a58bin0 -> 561 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/f4/d25b796d86387205a5498175d66e91d1e5006abin0 -> 106 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/fe/085d9ace90cc675b87df15e1aeed0c3a31407fbin0 -> 139 bytes
-rw-r--r--tests/resources/crlf/.gitted/objects/fe/ab3713c4659bb22700042b3c55b8d60d0a952bbin0 -> 568 bytes
-rw-r--r--tests/resources/crlf/.gitted/refs/heads/empty-files1
-rw-r--r--tests/resources/crlf/.gitted/refs/heads/master1
-rw-r--r--tests/resources/crlf/all-crlf4
-rw-r--r--tests/resources/crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf/all-lf5
-rw-r--r--tests/resources/crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf/more-crlf5
-rw-r--r--tests/resources/crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf/more-lf5
-rw-r--r--tests/resources/crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/.gitattributes1
-rw-r--r--tests/resources/crlf_data/README3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/all-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/binary-all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/few-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/many-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/all-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/all-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/binary-all-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/binary-mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/few-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/many-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/all-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/binary-all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/few-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/many-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/all-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/all-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/binary-all-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/few-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/many-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/all-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/binary-all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/few-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/many-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/all-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/binary-all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/few-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/many-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/all-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/all-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/all-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/few-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/many-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/all-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/few-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/many-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/all-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/binary-all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/few-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/many-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/all-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/all-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/binary-all-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/binary-mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/few-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/many-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/all-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/binary-all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/few-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/many-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/all-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/all-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/binary-all-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/few-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/many-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/all-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/binary-all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/few-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/many-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/all-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/binary-all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/few-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/many-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/all-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/all-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/all-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/few-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/many-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/all-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/few-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/many-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/all-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/few-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/many-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/all-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/all-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/binary-all-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/binary-mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/few-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/many-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/all-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/all-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/binary-all-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/binary-mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/few-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/many-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/all-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/binary-all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/few-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/many-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/all-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/all-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/binary-all-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/few-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/many-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/all-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/binary-all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/few-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/many-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/all-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/all-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/binary-all-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/binary-mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/few-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/many-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/all-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/all-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/all-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/few-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/many-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/all-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/all-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/few-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/many-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/all-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/all-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/few-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/many-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/HEAD1
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/branches0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_false/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_input/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/zero-byte0
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true/all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true/all-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true/more-crlf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true/more-lf5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/posix_to_workdir/autocrlf_true/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/all-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/all-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/binary-all-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/binary-mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/few-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/many-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/all-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/all-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/binary-all-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/binary-mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/few-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/many-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/all-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/binary-all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/few-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/many-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/all-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/all-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/binary-all-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/few-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/many-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/all-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/binary-all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/few-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/many-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/all-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/all-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/binary-all-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/binary-mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/few-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/many-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/all-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/all-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/all-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/few-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/many-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/all-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/all-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/few-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/many-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/all-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/binary-all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/few-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/many-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/all-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/all-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/binary-all-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/binary-mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/few-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/many-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/all-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/binary-all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/few-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/many-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/all-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/all-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/binary-all-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/few-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/many-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/all-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/binary-all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/few-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/many-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/all-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/binary-all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/few-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/many-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/all-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/all-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/all-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/few-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/many-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/all-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/few-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/many-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/all-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/few-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/many-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/all-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/all-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/binary-all-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/binary-mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/few-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/many-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/all-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/all-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/binary-all-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/binary-mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/few-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/many-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/all-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/binary-all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/few-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/many-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/all-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/all-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/binary-all-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/few-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/many-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/all-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/binary-all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/few-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/many-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/all-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/all-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/binary-all-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/binary-mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/binary-mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/few-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/many-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/mixed-lf-cr-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/mixed-lf-cr.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/all-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/all-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/all-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/all-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/few-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/many-utf8-chars-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/all-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/all-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/few-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/many-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/all-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/all-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/few-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/many-utf8-chars-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/more-crlf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/more-crlf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/more-lf-utf8bom.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/more-lf.fail1
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_false/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_input/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/zero-byte0
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true/all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true/all-crlf-utf8bom4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true/all-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true/all-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true/binary-all-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true/binary-all-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true/binary-mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true/binary-mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true/few-utf8-chars-crlf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true/few-utf8-chars-lf22
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true/many-utf8-chars-crlf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true/many-utf8-chars-lf4
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true/mixed-lf-cr3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true/mixed-lf-cr-crlf3
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true/more-crlf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true/more-crlf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true/more-lf5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true/more-lf-utf8bom5
-rw-r--r--tests/resources/crlf_data/windows_to_workdir/autocrlf_true/zero-byte0
-rw-r--r--tests/resources/deprecated-mode.git/HEAD1
-rw-r--r--tests/resources/deprecated-mode.git/config6
-rw-r--r--tests/resources/deprecated-mode.git/description1
-rw-r--r--tests/resources/deprecated-mode.git/indexbin0 -> 112 bytes
-rw-r--r--tests/resources/deprecated-mode.git/info/exclude2
-rw-r--r--tests/resources/deprecated-mode.git/objects/06/262edc257418e9987caf999f9a7a3e1547adffbin0 -> 124 bytes
-rw-r--r--tests/resources/deprecated-mode.git/objects/08/10fb7818088ff5ac41ee49199b51473b1bd6c7bin0 -> 350 bytes
-rw-r--r--tests/resources/deprecated-mode.git/objects/1b/05fdaa881ee45b48cbaa5e9b037d667a47745ebin0 -> 57 bytes
-rw-r--r--tests/resources/deprecated-mode.git/objects/3d/0970ec547fc41ef8a5882dde99c6adce65b021bin0 -> 29 bytes
-rw-r--r--tests/resources/deprecated-mode.git/refs/heads/master1
-rw-r--r--tests/resources/describe/.gitted/HEAD1
-rw-r--r--tests/resources/describe/.gitted/config8
-rw-r--r--tests/resources/describe/.gitted/indexbin0 -> 262 bytes
-rw-r--r--tests/resources/describe/.gitted/logs/HEAD14
-rw-r--r--tests/resources/describe/.gitted/logs/refs/heads/master14
-rw-r--r--tests/resources/describe/.gitted/objects/03/00021985931292d0611b9232e757035fefc04dbin0 -> 108 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/10/8b485d8268ea595df8ffea74f0f4b186577d32bin0 -> 125 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/10/bd08b099ecb79184c60183f5c94ca915f427adbin0 -> 127 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/17/8481050188cf00d7d9cd5a11e43ab8fab9294fbin0 -> 17 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/19/1faf88a5826a99f475baaf8b13652c4e40bfe6bin0 -> 19 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/1e/016431ec7b22dd3e23f3e6f5f68f358f9227cfbin0 -> 156 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/22/3b7836fb19fdf64ba2d3cd6173c6a283141f78bin0 -> 17 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/25/d5edf8c0ef17e8a13b8da75913dcec4ea7afc1bin0 -> 87 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/2b/df67abb163a4ffb2d7f3f0880c9fe5068ce782bin0 -> 21 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/31/fc9136820b507e938a9c6b88bf2c567a9f6f4bbin0 -> 153 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/42/8f9554a2eec22de29898819b579466af7c1583bin0 -> 80 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/4d/6558b8fa764baeb0f19c1e857df91e0eda5a0fbin0 -> 155 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/4f/2d9ce01ad5249cabdc6565366af8aff85b1525bin0 -> 18 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/52/912fbab0715dec53d43053966e78ad213ba359bin0 -> 127 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/56/26abf0f72e58d7a153368ba57db4c673c0e171bin0 -> 19 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/61/26a5f9c57ebc81e64370ec3095184ad92dab1cbin0 -> 151 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/62/d8fe9f6db631bd3a19140699101c9e281c9f9dbin0 -> 17 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/65/a91bc2262480dce4c5979519aae6668368eb4ebin0 -> 77 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/68/0166b6cd31f76354fee2572618e6b0142d05e62
-rw-r--r--tests/resources/describe/.gitted/objects/69/3a3de402bb23897ed5c931273e53c78eff0495bin0 -> 49 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/6a/12b56088706aa6c39ccd23b7c7ce60f3a0b9a1bin0 -> 154 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/6d/218e42592043041c4da016ff298cf241b86c3cbin0 -> 77 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/75/bb152c600647586c226d98411b1d2f9861af5abin0 -> 80 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/81/f4b1aac643e6983fab370eae8aefccecbf3a4cbin0 -> 152 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/8e/c1d96451ff05451720e4e8968812c46b35e5e4bin0 -> 49 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/94/9b98e208015bfc0e2f573debc34ae2f97a7f0ebin0 -> 186 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/9c/06d71b8406ab97537e3acdc39a2c4ade7a9411bin0 -> 49 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/a6/095f816e81f64651595d488badc42399837d6abin0 -> 153 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/a9/e3325a07117aa5381e044a8d96c26eb30d729dbin0 -> 49 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/a9/eb02af13df030159e39f70330d5c8a476556912
-rw-r--r--tests/resources/describe/.gitted/objects/aa/d8d5cef3915ab78b3227abaaac99b62db9eb54bin0 -> 49 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/aa/ddd4f14847e0e323924ec262c2343249a84f8bbin0 -> 125 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/b2/40c0fb88c5a629e00ebc1275fa1f33e364a7053
-rw-r--r--tests/resources/describe/.gitted/objects/ce/1c4f8b6120122e23d4442925d98c56c41917d8bin0 -> 187 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/d5/aab219a814ddbe4b3aaedf03cdea491b218ec4bin0 -> 80 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/f2/ad6c76f0115a6ba5b00456a849810e7ec0af20bin0 -> 17 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/f7/0f10e4db19068f79bc43844b49f3eece45c4e8bin0 -> 17 bytes
-rw-r--r--tests/resources/describe/.gitted/objects/f7/19efd430d52bcfc8566a43b2eb655688d38871bin0 -> 19 bytes
-rw-r--r--tests/resources/describe/.gitted/refs/heads/master1
-rw-r--r--tests/resources/describe/.gitted/refs/tags/A1
-rw-r--r--tests/resources/describe/.gitted/refs/tags/B1
-rw-r--r--tests/resources/describe/.gitted/refs/tags/D1
-rw-r--r--tests/resources/describe/.gitted/refs/tags/R1
-rw-r--r--tests/resources/describe/.gitted/refs/tags/c1
-rw-r--r--tests/resources/describe/.gitted/refs/tags/e1
-rw-r--r--tests/resources/describe/another1
-rw-r--r--tests/resources/describe/file1
-rw-r--r--tests/resources/describe/side1
-rw-r--r--tests/resources/diff/.gitted/HEAD1
-rw-r--r--tests/resources/diff/.gitted/config6
-rw-r--r--tests/resources/diff/.gitted/description1
-rw-r--r--tests/resources/diff/.gitted/indexbin0 -> 225 bytes
-rw-r--r--tests/resources/diff/.gitted/info/exclude6
-rw-r--r--tests/resources/diff/.gitted/logs/HEAD2
-rw-r--r--tests/resources/diff/.gitted/logs/refs/heads/master2
-rw-r--r--tests/resources/diff/.gitted/objects/29/ab7053bb4dde0298e03e2c179e890b7dd465a7bin0 -> 730 bytes
-rw-r--r--tests/resources/diff/.gitted/objects/3e/5bcbad2a68e5bc60a53b8388eea53a1a7ab847bin0 -> 1108 bytes
-rw-r--r--tests/resources/diff/.gitted/objects/54/6c735f16a3b44d9784075c2c0dab2ac9bf1989bin0 -> 1110 bytes
-rw-r--r--tests/resources/diff/.gitted/objects/7a/9e0b02e63179929fed24f0a3e0f19168114d10bin0 -> 160 bytes
-rw-r--r--tests/resources/diff/.gitted/objects/7b/808f723a8ca90df319682c221187235af76693bin0 -> 922 bytes
-rw-r--r--tests/resources/diff/.gitted/objects/88/789109439c1e1c3cd45224001edee5304ed53c1
-rw-r--r--tests/resources/diff/.gitted/objects/cb/8294e696339863df760b2ff5d1e275bee72455bin0 -> 86 bytes
-rw-r--r--tests/resources/diff/.gitted/objects/d7/0d245ed97ed2aa596dd1af6536e4bfdb047b691
-rw-r--r--tests/resources/diff/.gitted/refs/heads/master1
-rw-r--r--tests/resources/diff/another.txt38
-rw-r--r--tests/resources/diff/readme.txt36
-rw-r--r--tests/resources/diff_format_email/.gitted/HEAD1
-rw-r--r--tests/resources/diff_format_email/.gitted/config7
-rw-r--r--tests/resources/diff_format_email/.gitted/indexbin0 -> 621 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/info/exclude6
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/06/b7b69a62cbd1e53c6c4e0c3f16473dcfdb4af6bin0 -> 159 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/07/594f324ebcf92902334c6016e30e716431dfbcbin0 -> 152 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/0a/37045ca6d8503e9bcf06a12abbbc8e92664ccebin0 -> 29 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/0d/593fca18d1ab11deb6e8025c9fe417456fe883bin0 -> 56 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/0d/b2a262bc8c5c3cba55254730045a8258da7a372
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/10/808fe9c9be5a190c0ba68d1a002233fb363508bin0 -> 176 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/13/ecf3d572dbc5e5b32c8ba067d1d1e0939572e8bin0 -> 34 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/17/cfad36e93db7706b16bef5ef842ba1e5ca06abbin0 -> 155 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/1a/9932083f96b0db42552103d40076f62fa8235ebin0 -> 54 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/1a/e3be57f869687d983066a0f5d2aaea1b82ddc5bin0 -> 162 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/1b/525b0a6c5218b069b601ce91fce8eaf0a54e20bin0 -> 31 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/1e/82c3b234e37da82e5b23e0e2a70bca68ee12c6bin0 -> 28 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/1e/875da9b1e67f853b2eec3e202c21c867097234bin0 -> 121 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/20/609dbbc32bbfc827528eec3fcea2d024e6dd8abin0 -> 121 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/23/f92946d3f38bd090f700d3e8e7b728ffc58264bin0 -> 155 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/24/97c5249408494e66e25070a8c74e49eaeeb6c3bin0 -> 162 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/24/9a4263be23b4d1c02484cb840b6eca4c6cf74dbin0 -> 171 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/25/2a3e19fd2c6fb7b20c111142c5bd5fb9ea6b8ebin0 -> 121 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/27/93544db9060bab4f9169e5b89c82f9fa7c7fa6bin0 -> 120 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/29/1f1ff3cbb9a6f153678d9657679e3d4bf257dfbin0 -> 29 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/2f/f7b811eee62a73959350b1f7349f6f4d0c882dbin0 -> 54 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/39/91dce9e71a0641ca49a6a4eea6c9e7ff402ed4bin0 -> 166 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/39/ed6bbd76bca81c50db3aaca261456284f5d5b8bin0 -> 49 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/45/eef2a9317e179984649de247269e38cd5d99cf2
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/4a/076277b884c519a932be67e346db2ac80a98fabin0 -> 40 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/4c/3bd7182ad66ea7aa20ba47ae82812b710d169cbin0 -> 179 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/4c/a10087e696d2ba78d07b146a118e9a7096ed4fbin0 -> 173 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/4d/de2b17d1c982cd988f21d24350a214401e4a1ebin0 -> 121 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/4f/31e0248ac800a1edc78b74f74e86f5eba90e87bin0 -> 54 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/50/17c9456d013b2c7712d29aab73b681c880f509bin0 -> 54 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/50/438cfa585c1d15cf3650ed1bf641da937cc261bin0 -> 123 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/52/19b9784f9a92d7bd7cb567a6d6a21bfb86697ebin0 -> 160 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/52/c3cd1ff6234b95fecbaf9ef13624da17697b8dbin0 -> 41 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/53/525d4cc3ef3ba4a5cbf69492fdffb4e4a74558bin0 -> 121 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/53/92ef3c959f744b892bbebb168bbbb7b05c03f3bin0 -> 150 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/55/0d730ba1b8c4937ea170b37c7ba91d792c0aaabin0 -> 123 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/62/7e7e12d87e07a83fad5b6bfa25e86ead4a52701
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/66/81f1844dc677e5ff07ffd993461f5c441e6af5bin0 -> 35 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/69/ddefb5c245e2f9ee62bd4cabd8ebe60a01e448bin0 -> 54 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/6b/6c2067c6d968f9bddb9b900ee1ab7e5b0674302
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/6b/ef49b206b29d9c46456e075722cd1a48b41e4cbin0 -> 121 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/6c/15659c036377aebf3b4569959ca1f5bedb551fbin0 -> 167 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/6e/05acc5a5dab507d91a0a0cc0fb05a3dd98892d2
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/73/09653445ecf038d3e3dd9ed55edb6cb541a4babin0 -> 28 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/74/6d514eae0c330261d37940cab33aa97fefbd931
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/74/a4d5394ebcfa7e9f445680897dfbc96586bc86bin0 -> 38 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/77/d0a3ed37236a7941d564f08d68d3b36462d2312
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/7a/de76dd34bba4733cf9878079f9fd4a456a91893
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/7a/ff11da95ca2be0bfb74b06e7cc1c480559dbe7bin0 -> 26 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/7f/854619451620f7fbcec7ea171675e615ce92b6bin0 -> 179 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/85/fa91713734c588c897dd6dd67a39576f67ae50bin0 -> 157 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/87/3806f6f27e631eb0b23e4b56bea2bfac14a373bin0 -> 181 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/89/47a46e2097638ca6040ad4877246f4186ec3bd2
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/89/7d3af16ca9e420cd071b1c4541bd2b91d04c8c1
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/8c/637df9661edd808932b2f5d383f2c41ced9f06bin0 -> 150 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/8d/7523f6fcb2404257889abe0d96f093d9f524f91
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/8d/fa038554d5b682a51bda8ee3038cee6c63be76bin0 -> 120 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/92/64b96c6d104d0e07ae33d3007b6a48246c6f92bin0 -> 181 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/94/350226b3aa14efac831c803a51f7a09f3fc31abin0 -> 24 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/94/75e21dcbc515af8f641576400e4b450e5f4c03bin0 -> 62 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/94/aaae8954e8bb613de636071da663a621695911bin0 -> 29 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/94/f84b3e65e8bcbe8bffef2c885339343a802dd4bin0 -> 53 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/9a/2d780ac2ea0aeabdb9d2a876e6bbfff17b2c44bin0 -> 28 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/9a/c0329b8b7a4046210d8b8b02ac02055667de63bin0 -> 29 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/9a/c35ff15cd8864aeafd889e4826a3150f0b06c4bin0 -> 20 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/9b/997daca2a0beb5cc44b32c64f100a9a26d4d4bbin0 -> 22 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/a3/ac918e3a6604294b239cb956363e83d71abb3b1
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/a5/ac978d4f2a1784f847f41223a34c3e78934238bin0 -> 54 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/a7/29eab45c84563135e8631d4010230bc0479f1fbin0 -> 40 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/a7/a65f98355b5a7567bcc395f6f7936c9252cf57bin0 -> 28 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/a9/7157a0d0571698728b6f2f7675b456c98c5961bin0 -> 62 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/af/8f41d0cb7a3079a8f8e231ea2ab8b97837ce13bin0 -> 50 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/b0/5cecf1949d192b6df852b3f71853ef820ee235bin0 -> 37 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/b4/f457c219dbb3517be908d4e70f0ada2fd8b8f9bin0 -> 54 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/bb/07a00e58b260c4eb9a82f8afbc1d80ad9739bfbin0 -> 171 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/bd/474b2519cc15eab801ff851cc7d50f0dee49a1bin0 -> 18 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/bd/f7ba6bc5c4e57ca6595928dcbe6753c8a663ffbin0 -> 35 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/c7/1a05d36025c806496a74d46d7d596eb23295c4bin0 -> 28 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/cb/a89408dc016f4caddb6dc886fcb58f587a78df3
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/cd/471f0d8770371e1bc78bcbb38db4c7e4106bd2bin0 -> 180 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/cd/ed722d05305c6b181f188c118d2d9810f39bb8bin0 -> 163 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/ce/2792fcae8d704a56901754a0583a7418a21d8abin0 -> 121 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/d1/4aa252e52a709d03a3d3d0d965e177eb0a674e1
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/d3/b6b38486f620b5b532a8cc6e0198ab7c3f52e4bin0 -> 121 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/d5/ff67764c82f729b13c26a09576570d884d9687bin0 -> 121 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/d7/bb447df12c6a8aba8727005482fb211f11297abin0 -> 156 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/db/e8727e4806ae88ccc3f0755cae8f8cb7efa2ccbin0 -> 175 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/e1/2af77c510e8ce4c261a3758736109c2c2dd1f0bin0 -> 51 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391bin0 -> 15 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/e9/091231467304a5ef112de02361d795ef051ee1bin0 -> 24 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/ee/251372f131d82e575f16fe51c778406d88f8c22
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/f3/d35bd592fefd8280fc0c302fa9f27dbdd721a31
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/f4/07be01334e07bfb8f57cd2078f0ee3eb61e0851
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/f9/e215d309644e24fa50d6bd6e6eedba166e56bc2
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/fc/a0c10eb9f1af6494a448d5733d283f5232a514bin0 -> 176 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/objects/ff/8d35b41494f7f0dc92f95d67f54fff274d3fcbbin0 -> 29 bytes
-rw-r--r--tests/resources/diff_format_email/.gitted/refs/heads/binary1
-rw-r--r--tests/resources/diff_format_email/.gitted/refs/heads/master1
-rw-r--r--tests/resources/diff_format_email/.gitted/refs/heads/multihunk1
-rw-r--r--tests/resources/diff_format_email/.gitted/refs/heads/rename1
-rw-r--r--tests/resources/diff_format_email/dir/renamed.txt5
-rwxr-xr-xtests/resources/diff_format_email/file1.txt.renamed17
-rw-r--r--tests/resources/diff_format_email/file2.txt5
-rw-r--r--tests/resources/diff_format_email/file3.txt7
-rw-r--r--tests/resources/duplicate.git/COMMIT_EDITMSG1
-rw-r--r--tests/resources/duplicate.git/HEAD1
-rw-r--r--tests/resources/duplicate.git/config5
-rw-r--r--tests/resources/duplicate.git/description1
-rw-r--r--tests/resources/duplicate.git/indexbin0 -> 104 bytes
-rw-r--r--tests/resources/duplicate.git/info/exclude6
-rw-r--r--tests/resources/duplicate.git/info/refs1
-rw-r--r--tests/resources/duplicate.git/logs/HEAD1
-rw-r--r--tests/resources/duplicate.git/logs/refs/heads/master1
-rw-r--r--tests/resources/duplicate.git/objects/03/8d718da6a1ebbc6a7780a96ed75a70cc2ad6e2bin0 -> 23 bytes
-rw-r--r--tests/resources/duplicate.git/objects/0d/deadede9e6d6ccddce0ee1e5749eed0485e5eabin0 -> 22 bytes
-rw-r--r--tests/resources/duplicate.git/objects/ce/013625030ba8dba906f756967f9e9ca394464abin0 -> 21 bytes
-rw-r--r--tests/resources/duplicate.git/objects/info/packs3
-rw-r--r--tests/resources/duplicate.git/objects/pack/pack-29a4896f0a0b9c9947b0927c57a5c03dcae052e3.idxbin0 -> 1184 bytes
-rw-r--r--tests/resources/duplicate.git/objects/pack/pack-29a4896f0a0b9c9947b0927c57a5c03dcae052e3.packbin0 -> 249 bytes
-rw-r--r--tests/resources/duplicate.git/objects/pack/pack-b18eeacbd65cbd30a365d7564b45a468e8bd43d6.idxbin0 -> 1268 bytes
-rw-r--r--tests/resources/duplicate.git/objects/pack/pack-b18eeacbd65cbd30a365d7564b45a468e8bd43d6.packbin0 -> 369 bytes
-rw-r--r--tests/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.idxbin0 -> 1156 bytes
-rw-r--r--tests/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.packbin0 -> 213 bytes
-rw-r--r--tests/resources/duplicate.git/objects/pack/pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.idxbin0 -> 1100 bytes
-rw-r--r--tests/resources/duplicate.git/objects/pack/pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.packbin0 -> 47 bytes
-rw-r--r--tests/resources/duplicate.git/packed-refs2
-rw-r--r--tests/resources/duplicate.git/refs/heads/dummy-marker.txt1
-rw-r--r--tests/resources/empty_bare.git/HEAD1
-rw-r--r--tests/resources/empty_bare.git/config7
-rw-r--r--tests/resources/empty_bare.git/description1
-rw-r--r--tests/resources/empty_bare.git/info/exclude6
-rw-r--r--tests/resources/empty_bare.git/objects/info/dummy-marker.txt0
-rw-r--r--tests/resources/empty_bare.git/objects/pack/dummy-marker.txt0
-rw-r--r--tests/resources/empty_bare.git/refs/heads/dummy-marker.txt0
-rw-r--r--tests/resources/empty_bare.git/refs/tags/dummy-marker.txt0
-rw-r--r--tests/resources/empty_standard_repo/.gitted/HEAD1
-rw-r--r--tests/resources/empty_standard_repo/.gitted/config8
-rw-r--r--tests/resources/empty_standard_repo/.gitted/description1
-rw-r--r--tests/resources/empty_standard_repo/.gitted/info/exclude6
-rw-r--r--tests/resources/empty_standard_repo/.gitted/objects/info/dummy-marker.txt0
-rw-r--r--tests/resources/empty_standard_repo/.gitted/objects/pack/dummy-marker.txt0
-rw-r--r--tests/resources/empty_standard_repo/.gitted/refs/heads/dummy-marker.txt0
-rw-r--r--tests/resources/empty_standard_repo/.gitted/refs/tags/dummy-marker.txt0
-rw-r--r--tests/resources/filemodes/.gitted/HEAD1
-rw-r--r--tests/resources/filemodes/.gitted/config6
-rw-r--r--tests/resources/filemodes/.gitted/description1
-rw-r--r--tests/resources/filemodes/.gitted/indexbin0 -> 528 bytes
-rw-r--r--tests/resources/filemodes/.gitted/info/exclude6
-rw-r--r--tests/resources/filemodes/.gitted/logs/HEAD1
-rw-r--r--tests/resources/filemodes/.gitted/logs/refs/heads/master1
-rw-r--r--tests/resources/filemodes/.gitted/objects/99/62c8453ba6f0cf8dac7c5dcc2fa2897fa9964abin0 -> 139 bytes
-rw-r--r--tests/resources/filemodes/.gitted/objects/a5/c5dd0fc6c313159a69b1d19d7f61a9f978e8f1bin0 -> 21 bytes
-rw-r--r--tests/resources/filemodes/.gitted/objects/e7/48d196331bcb20267eaaee4ff3326cb73b8182bin0 -> 99 bytes
-rw-r--r--tests/resources/filemodes/.gitted/refs/heads/master1
-rw-r--r--tests/resources/filemodes/exec_off1
-rwxr-xr-xtests/resources/filemodes/exec_off2on_staged1
-rwxr-xr-xtests/resources/filemodes/exec_off2on_workdir1
-rw-r--r--tests/resources/filemodes/exec_off_untracked1
-rwxr-xr-xtests/resources/filemodes/exec_on1
-rw-r--r--tests/resources/filemodes/exec_on2off_staged1
-rw-r--r--tests/resources/filemodes/exec_on2off_workdir1
-rwxr-xr-xtests/resources/filemodes/exec_on_untracked1
-rwxr-xr-xtests/resources/generate_crlf.sh147
-rw-r--r--tests/resources/git-sha256.indexbin0 -> 458900 bytes
-rw-r--r--tests/resources/gitgit.indexbin0 -> 134799 bytes
-rw-r--r--tests/resources/grafted.git/HEAD1
-rw-r--r--tests/resources/grafted.git/config6
-rw-r--r--tests/resources/grafted.git/info/grafts3
-rw-r--r--tests/resources/grafted.git/objects/05/12adebd3782157f0d5c9b22b043f87b4aaff9ebin0 -> 133 bytes
-rw-r--r--tests/resources/grafted.git/objects/1c/18e80a276611bb9b146590616bbc5aebdf2945bin0 -> 170 bytes
-rw-r--r--tests/resources/grafted.git/objects/1c/3f11eca55d76bc1bf7353ca7e4226246d353edbin0 -> 46 bytes
-rw-r--r--tests/resources/grafted.git/objects/2a/f02ebff1fc0142d2380c98758d81c67b365869bin0 -> 73 bytes
-rw-r--r--tests/resources/grafted.git/objects/2b/ecadd3f1ecad07a054392421edf9c0e1c375b2bin0 -> 74 bytes
-rw-r--r--tests/resources/grafted.git/objects/2f/3053cbff8a4ca2f0666de364ddb734a28a31a9bin0 -> 133 bytes
-rw-r--r--tests/resources/grafted.git/objects/45/342912745ba6f8893b1e126df4653a4355df1abin0 -> 50 bytes
-rw-r--r--tests/resources/grafted.git/objects/48/b2b333732644eafb385771a992b923fa88f135bin0 -> 49 bytes
-rw-r--r--tests/resources/grafted.git/objects/5d/31bf4b437e1191b6c709c665f1bd329d0ed0bfbin0 -> 74 bytes
-rw-r--r--tests/resources/grafted.git/objects/66/cc22a015f6ca75b34c82d28f78ba663876bade2
-rw-r--r--tests/resources/grafted.git/objects/6c/f192eb71cd3243c9fbbe2551012c4449de3fcfbin0 -> 36 bytes
-rw-r--r--tests/resources/grafted.git/objects/7c/9da502b2744b70522bb694cd607fb00104a233bin0 -> 76 bytes
-rw-r--r--tests/resources/grafted.git/objects/8a/00e91619098618be97c0d2ceabb05a2c58edd92
-rw-r--r--tests/resources/grafted.git/objects/a0/4de168dd5c43aa2af594d794d62e922f8b3b34bin0 -> 42 bytes
-rw-r--r--tests/resources/grafted.git/objects/b2/b4f9e5fe5dacbb2f98bd71d1dc86c7b571ddd1bin0 -> 54 bytes
-rw-r--r--tests/resources/grafted.git/objects/ba/54010f8d41532eb130eba420f50248881f7fc2bin0 -> 37 bytes
-rw-r--r--tests/resources/grafted.git/objects/d7/224d49d6d5aff6ade596ed74f4bcd4f77b29e22
-rw-r--r--tests/resources/grafted.git/objects/db/8e43f297a313c439530c977b733aaa8c10d54ebin0 -> 35 bytes
-rw-r--r--tests/resources/grafted.git/objects/e4/14f42f4e6bc6934563a2349a8600f0ab68618ebin0 -> 139 bytes
-rw-r--r--tests/resources/grafted.git/objects/e6/7b587a57850c69f6f9351ee10c7c8a41dacc78bin0 -> 74 bytes
-rw-r--r--tests/resources/grafted.git/objects/f0/7330bc2e4ed4bd0bf2301505f6c6bbad01aa2abin0 -> 77 bytes
-rw-r--r--tests/resources/grafted.git/objects/f5/03807ffa920e407a600cfaee96b7152259acc72
-rw-r--r--tests/resources/grafted.git/refs/heads/bottom1
-rw-r--r--tests/resources/grafted.git/refs/heads/branch1
-rw-r--r--tests/resources/grafted.git/refs/heads/master1
-rw-r--r--tests/resources/grafted.git/refs/heads/top1
-rw-r--r--tests/resources/icase/.gitted/HEAD1
-rw-r--r--tests/resources/icase/.gitted/config7
-rw-r--r--tests/resources/icase/.gitted/description1
-rw-r--r--tests/resources/icase/.gitted/indexbin0 -> 1392 bytes
-rw-r--r--tests/resources/icase/.gitted/info/exclude6
-rw-r--r--tests/resources/icase/.gitted/logs/HEAD1
-rw-r--r--tests/resources/icase/.gitted/logs/refs/heads/master1
-rw-r--r--tests/resources/icase/.gitted/objects/3e/257c57f136a1cb8f2b8e9a2e5bc8ec0258bdcebin0 -> 114 bytes
-rw-r--r--tests/resources/icase/.gitted/objects/4d/d6027d083575c7431396dc2a3174afeb393c93bin0 -> 61 bytes
-rw-r--r--tests/resources/icase/.gitted/objects/62/e0af52c199ec731fe4ad230041cd3286192d49bin0 -> 19 bytes
-rw-r--r--tests/resources/icase/.gitted/objects/76/d6e1d231b1085fcce151427e9899335de74be63
-rw-r--r--tests/resources/icase/.gitted/objects/d4/4e18fb93b7107b5cd1b95d601591d77869a1b6bin0 -> 21 bytes
-rw-r--r--tests/resources/icase/.gitted/refs/heads/master1
-rw-r--r--tests/resources/icase/B1
-rw-r--r--tests/resources/icase/D1
-rw-r--r--tests/resources/icase/F1
-rw-r--r--tests/resources/icase/H1
-rw-r--r--tests/resources/icase/J1
-rw-r--r--tests/resources/icase/L/11
-rw-r--r--tests/resources/icase/L/B1
-rw-r--r--tests/resources/icase/L/D1
-rw-r--r--tests/resources/icase/L/a1
-rw-r--r--tests/resources/icase/L/c1
-rw-r--r--tests/resources/icase/a1
-rw-r--r--tests/resources/icase/c1
-rw-r--r--tests/resources/icase/e1
-rw-r--r--tests/resources/icase/g1
-rw-r--r--tests/resources/icase/i1
-rw-r--r--tests/resources/icase/k/11
-rw-r--r--tests/resources/icase/k/B1
-rw-r--r--tests/resources/icase/k/D1
-rw-r--r--tests/resources/icase/k/a1
-rw-r--r--tests/resources/icase/k/c1
-rw-r--r--tests/resources/indexv4/.gitted/HEAD1
-rw-r--r--tests/resources/indexv4/.gitted/config5
-rw-r--r--tests/resources/indexv4/.gitted/indexbin0 -> 572 bytes
-rw-r--r--tests/resources/indexv4/.gitted/objects/4c/9109b3e671d851eec87e0e72f6305b582e7e99bin0 -> 70 bytes
-rw-r--r--tests/resources/indexv4/.gitted/objects/b0/952dbb50bed5f01e03e31b296184cb183e54a7bin0 -> 154 bytes
-rw-r--r--tests/resources/indexv4/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391bin0 -> 15 bytes
-rw-r--r--tests/resources/indexv4/.gitted/refs/heads/master1
-rw-r--r--tests/resources/indexv4/file.tx0
-rw-r--r--tests/resources/indexv4/file.txt0
-rw-r--r--tests/resources/indexv4/file.txz0
-rw-r--r--tests/resources/indexv4/foo0
-rw-r--r--tests/resources/indexv4/zzz0
-rw-r--r--tests/resources/issue_1397/.gitted/HEAD1
-rw-r--r--tests/resources/issue_1397/.gitted/config6
-rw-r--r--tests/resources/issue_1397/.gitted/indexbin0 -> 233 bytes
-rw-r--r--tests/resources/issue_1397/.gitted/objects/7f/483a738f867e5b21c8f377d70311f011eb48b53
-rw-r--r--tests/resources/issue_1397/.gitted/objects/83/12e0889a9cbab77c732b6bc39b51a683e3a318bin0 -> 48 bytes
-rw-r--r--tests/resources/issue_1397/.gitted/objects/8a/7ef047fc933edb62e84e7977b0612ec3f6f283bin0 -> 141 bytes
-rw-r--r--tests/resources/issue_1397/.gitted/objects/8e/8f80088a9274fd23584992f587083ca1bcbbacbin0 -> 63 bytes
-rw-r--r--tests/resources/issue_1397/.gitted/objects/f2/c62dea0372a0578e053697d5c1ba1ac05e774abin0 -> 94 bytes
-rw-r--r--tests/resources/issue_1397/.gitted/objects/ff/3578d64d199d5b48d92bbb569e0a273e411741bin0 -> 73 bytes
-rw-r--r--tests/resources/issue_1397/.gitted/refs/heads/master1
-rw-r--r--tests/resources/issue_1397/crlf_file.txt3
-rw-r--r--tests/resources/issue_1397/some_other_crlf_file.txt3
-rw-r--r--tests/resources/issue_592/.gitted/COMMIT_EDITMSG1
-rw-r--r--tests/resources/issue_592/.gitted/HEAD1
-rw-r--r--tests/resources/issue_592/.gitted/config8
-rw-r--r--tests/resources/issue_592/.gitted/indexbin0 -> 392 bytes
-rw-r--r--tests/resources/issue_592/.gitted/info/exclude6
-rw-r--r--tests/resources/issue_592/.gitted/logs/HEAD2
-rw-r--r--tests/resources/issue_592/.gitted/logs/refs/heads/master2
-rw-r--r--tests/resources/issue_592/.gitted/objects/06/07ee9d4ccce8e4c4fa13c2c7d727e7faba4e0ebin0 -> 87 bytes
-rw-r--r--tests/resources/issue_592/.gitted/objects/49/363a72a90d9424240258cd3759f23788ecf1d8bin0 -> 55 bytes
-rw-r--r--tests/resources/issue_592/.gitted/objects/4d/383e87f0371ba8fa353f3912db6862b2625e852
-rw-r--r--tests/resources/issue_592/.gitted/objects/71/44be264b61825fbff68046fe999bdfe96a1792bin0 -> 50 bytes
-rw-r--r--tests/resources/issue_592/.gitted/objects/be/de83ee10b5b3f00239660b00acec2d55fd0b84bin0 -> 107 bytes
-rw-r--r--tests/resources/issue_592/.gitted/objects/e3/8fcc7a6060f5eb5b876e836b52ae4769363f21bin0 -> 137 bytes
-rw-r--r--tests/resources/issue_592/.gitted/objects/f1/adef63cb08891a0942b76fc4b9c50c6c494bc7bin0 -> 29 bytes
-rw-r--r--tests/resources/issue_592/.gitted/refs/heads/master1
-rw-r--r--tests/resources/issue_592/a.txt1
-rw-r--r--tests/resources/issue_592/c/a.txt1
-rw-r--r--tests/resources/issue_592/l.txt1
-rw-r--r--tests/resources/issue_592/t/a.txt1
-rw-r--r--tests/resources/issue_592/t/b.txt1
-rw-r--r--tests/resources/issue_592b/.gitted/HEAD1
-rw-r--r--tests/resources/issue_592b/.gitted/config6
-rw-r--r--tests/resources/issue_592b/.gitted/description1
-rw-r--r--tests/resources/issue_592b/.gitted/indexbin0 -> 376 bytes
-rw-r--r--tests/resources/issue_592b/.gitted/info/exclude6
-rw-r--r--tests/resources/issue_592b/.gitted/logs/HEAD1
-rw-r--r--tests/resources/issue_592b/.gitted/logs/refs/heads/master1
-rw-r--r--tests/resources/issue_592b/.gitted/objects/3f/bf1852f72fd268e36457b13a18cdd9a4c9ea352
-rw-r--r--tests/resources/issue_592b/.gitted/objects/6f/a891d3e578c83e1c03bdb9e0fdd8e6e934157fbin0 -> 28 bytes
-rw-r--r--tests/resources/issue_592b/.gitted/objects/80/07d41d5794e6ce4d4d2c97e370d5a9aa6d5213bin0 -> 24 bytes
-rw-r--r--tests/resources/issue_592b/.gitted/objects/a6/5fb6583a7c425284142f285bc359a2d6565513bin0 -> 93 bytes
-rw-r--r--tests/resources/issue_592b/.gitted/objects/ae/be7a55922c7097ef91ca3a7bc327a901d87c2cbin0 -> 122 bytes
-rw-r--r--tests/resources/issue_592b/.gitted/objects/b3/44b055867fcdc1f01eaa75056a43e868eb4fbcbin0 -> 36 bytes
-rw-r--r--tests/resources/issue_592b/.gitted/objects/f7/d75fbfad8b1d2e307ced287ea78aad403cdce3bin0 -> 57 bytes
-rw-r--r--tests/resources/issue_592b/.gitted/refs/heads/master1
-rw-r--r--tests/resources/issue_592b/gitignore1
-rw-r--r--tests/resources/issue_592b/ignored/contained/ignored3.txt1
-rw-r--r--tests/resources/issue_592b/ignored/contained/tracked3.txt1
-rw-r--r--tests/resources/issue_592b/ignored/ignored2.txt1
-rw-r--r--tests/resources/issue_592b/ignored/tracked2.txt1
-rw-r--r--tests/resources/issue_592b/ignored1.txt1
-rw-r--r--tests/resources/issue_592b/tracked1.txt1
-rw-r--r--tests/resources/mailmap/.gitted/HEAD1
-rw-r--r--tests/resources/mailmap/.gitted/config5
-rw-r--r--tests/resources/mailmap/.gitted/description1
-rw-r--r--tests/resources/mailmap/.gitted/indexbin0 -> 289 bytes
-rw-r--r--tests/resources/mailmap/.gitted/info/exclude6
-rw-r--r--tests/resources/mailmap/.gitted/objects/00/1387531bed84262f137837125d4d998a9ba65dbin0 -> 70 bytes
-rw-r--r--tests/resources/mailmap/.gitted/objects/02/7b2816ae0d7a08ba656d0417c09b4eac18cf00bin0 -> 85 bytes
-rw-r--r--tests/resources/mailmap/.gitted/objects/09/20975110511365e56aec2263082d0c3d56d1fabin0 -> 113 bytes
-rw-r--r--tests/resources/mailmap/.gitted/objects/0c/d99501dfbec781a22ff7b84426b7bb308e709abin0 -> 53 bytes
-rw-r--r--tests/resources/mailmap/.gitted/objects/1e/1212e7674820c17f7b8797aee7bf38ece0e8382
-rw-r--r--tests/resources/mailmap/.gitted/objects/36/370b71f5aad1dd46bec5e14145280a843c9f49bin0 -> 53 bytes
-rw-r--r--tests/resources/mailmap/.gitted/objects/3a/1295dbc9234c0c5947c72803618c7112a014472
-rw-r--r--tests/resources/mailmap/.gitted/objects/3f/134546ae8fbe95a39dd20ea8c12b5fb0f48afb3
-rw-r--r--tests/resources/mailmap/.gitted/objects/43/179dc93939196f59b25387b5e44e9e8794f84c2
-rw-r--r--tests/resources/mailmap/.gitted/objects/46/b5bb908c78b575cac9f9e6e42ff9ba3f769a46bin0 -> 53 bytes
-rw-r--r--tests/resources/mailmap/.gitted/objects/4b/4d2010ba256ef339c1d1854d20249da7478f01bin0 -> 54 bytes
-rw-r--r--tests/resources/mailmap/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904bin0 -> 15 bytes
-rw-r--r--tests/resources/mailmap/.gitted/objects/4d/61d588546529ad27b2d77a3d6b05460ecb4be0bin0 -> 79 bytes
-rw-r--r--tests/resources/mailmap/.gitted/objects/50/d69f4e64be2cff2cedde8f9b7f970257caf4dd1
-rw-r--r--tests/resources/mailmap/.gitted/objects/61/293f4c3d7500d227a755a7a8258e28e53449b2bin0 -> 52 bytes
-rw-r--r--tests/resources/mailmap/.gitted/objects/62/7f0bd2f4fb5e949b79ba450d84676fa876b1c8bin0 -> 53 bytes
-rw-r--r--tests/resources/mailmap/.gitted/objects/68/dfd5e5cb6138488680246d134f47ce559f4cf1bin0 -> 149 bytes
-rw-r--r--tests/resources/mailmap/.gitted/objects/69/b9768d022706dab26e2af4dd5a13f42039e36fbin0 -> 123 bytes
-rw-r--r--tests/resources/mailmap/.gitted/objects/6a/0fc44893d4867166f9d321f78c269b3e42b08bbin0 -> 67 bytes
-rw-r--r--tests/resources/mailmap/.gitted/objects/6c/dec08ab9bfcd5a3d889f27bbed650317e3ec13bin0 -> 76 bytes
-rw-r--r--tests/resources/mailmap/.gitted/objects/71/00e631fb4d5deba31fdc8acc98f4fb5c1573fdbin0 -> 178 bytes
-rw-r--r--tests/resources/mailmap/.gitted/objects/7e/cbb98d860b304f622b38ce9ab8f08d14d981a8bin0 -> 198 bytes
-rw-r--r--tests/resources/mailmap/.gitted/objects/7e/e7b9a4a2a1eda925f6260338c063d8211d5ad52
-rw-r--r--tests/resources/mailmap/.gitted/objects/83/714a9223f3e072b85f0d4301cd2081fff3acf2bin0 -> 52 bytes
-rw-r--r--tests/resources/mailmap/.gitted/objects/87/ce8d4920a30ddb9547334e7c65806518863ff12
-rw-r--r--tests/resources/mailmap/.gitted/objects/8c/f0547fcb649b44ebaf39b8104982bb0abb4e69bin0 -> 121 bytes
-rw-r--r--tests/resources/mailmap/.gitted/objects/94/7ff75c33ac7941a32fe9900118b6ba85ab2be9bin0 -> 49 bytes
-rw-r--r--tests/resources/mailmap/.gitted/objects/95/d03c49d94de67d5a05553a1bb22e78f7cdf5ca1
-rw-r--r--tests/resources/mailmap/.gitted/objects/96/78a4325710507f7bf598a0fde5ebbd88148614bin0 -> 132 bytes
-rw-r--r--tests/resources/mailmap/.gitted/objects/a1/6db1cbb8817dddcf199c12d3c81221cf8eefc41
-rw-r--r--tests/resources/mailmap/.gitted/objects/a7/054a4b356b3ecdec60cee66e50beaa5b8637553
-rw-r--r--tests/resources/mailmap/.gitted/objects/a7/eb40274887baeb01a958ead80d106b5977312cbin0 -> 52 bytes
-rw-r--r--tests/resources/mailmap/.gitted/objects/c9/e5462de8ec453e94d85f26f64b80ea76fda6d4bin0 -> 53 bytes
-rw-r--r--tests/resources/mailmap/.gitted/objects/d3/e5e624cc7bfb09ac1960ebb6c458021b098f87bin0 -> 97 bytes
-rw-r--r--tests/resources/mailmap/.gitted/objects/f6/3578091d884c3066a003c50eb6c85ae75422692
-rw-r--r--tests/resources/mailmap/.gitted/objects/fe/dd34e8baffdb2acfe9a6860bf339287ca942bcbin0 -> 53 bytes
-rw-r--r--tests/resources/mailmap/.gitted/objects/fe/ef8f2135df4835496e4d576b1f1bd23510e1c51
-rw-r--r--tests/resources/mailmap/.gitted/refs/heads/master1
-rw-r--r--tests/resources/mailmap/.mailmap9
-rw-r--r--tests/resources/mailmap/file.txt10
-rw-r--r--tests/resources/mailmap/file_override2
-rw-r--r--tests/resources/merge-recursive/.gitted/HEAD1
-rw-r--r--tests/resources/merge-recursive/.gitted/config7
-rw-r--r--tests/resources/merge-recursive/.gitted/indexbin0 -> 619 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/info/refs1
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/00/6b298c5702b04c00370d0414959765b82fd722bin0 -> 207 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/00/7f1ee2af8e5d99906867c4237510e1790a89b83
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/01/6eef4a6fefd36bdcaa93ad773449ddc5c73cbbbin0 -> 208 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/03/9d0da126f24b819a5a38186249c7f96be3824cbin0 -> 71 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/05/63b7706dcdcf94bc0c02cd96c137940278eca9bin0 -> 151 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/05/c6a04ac101ab1a9836a95d5ec8d16b6f6304fdbin0 -> 208 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/06/d3fefb8726ab1099acc76e02dfb85e034b2538bin0 -> 375 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/06/db153c36829fc656e05cdf5a3bf7183f3c10aa2
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/07/10c3c796e0704361472ecb904413fca0107a25bin0 -> 208 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/07/2d89dcf3a7671ac34a8e875bb72fb39bcf14d7bin0 -> 208 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/08/f01e1bff7e442d574eb221913515b4bd27ccd6bin0 -> 64 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/0b/b7ed583d7e9ad507e8b902594f5c9126ea456bbin0 -> 161 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/0b/beee1982b493330e375a85bbfddaba3d561556bin0 -> 19 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/0c/e202f64fa8356c1a32835fce4058ca76b0c7edbin0 -> 183 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/0e/8126647ec607f0a14122cec4b15315d790c8ffbin0 -> 208 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/0e/c39d71c1b074905350ce20ce3f0629f737a2a9bin0 -> 91 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/0f/a6ead2731b9d138afe38c336c9727ea05027a71
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/12/4d4fe29d3433fdaa2f0f455d226f2c79d89cf3bin0 -> 208 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/15/311229e70fa62653f73dde1d4deef1a8e47a11bin0 -> 710 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/15/faa0c9991f2d65686e844651faa2ff9827887bbin0 -> 665 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/16/895aa5e13f8907d4adab81285557d938fad342bin0 -> 634 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/17/946ad3088f931102e5d81f94cf2825fc188953bin0 -> 19 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/18/2d0d250d1d7adcc60c178be5be98358b3a2fd15
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/1b/c7bcccf4bbdc8bfba2331a37ad5e9cf1dd321cbin0 -> 91 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/1b/de1883de4977ea3e664b315da951d1f614c3b1bin0 -> 45 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/1c/1bdb80c04233d1a9b9755913ee233987be6175bin0 -> 208 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/1e/8dff96faaaa24f84943d2d9601dde61cb0398abin0 -> 268 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/21/950d5e4e4d1a871b4dfcf72ecb6b9c162c434ebin0 -> 670 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/23/b427bf6278724433e64ef4cf6dc166c4f2e246bin0 -> 67 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/23/cf2687a9327d55abbbd788ff04fa932072aebcbin0 -> 52 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/26/d3c94459b4faa08f009b15867993ca341535922
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/2c/ba583804a4a6fad1baf97c959be447238d1489bin0 -> 23 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/2e/7ae0d42fb7b6126f6a08ac6314ac07833a52f6bin0 -> 61 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/30/39c07db695c8c99d0a7c7e32f0afe40eae0be03
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/34/8f16ffaeb73f319a75cec5b16a0a47d2d5e27cbin0 -> 208 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/35/8efd6f589384fa8baf92234db9c7899a53916ebin0 -> 51 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/35/dda4f3f9b3794d92a46d908790e550ed100eaebin0 -> 52 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/36/71e42c8c8302d1a71c0ed7bf2b0a938e9e20f9bin0 -> 51 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/37/185b25a204309bf74817da1a607518f13ca3edbin0 -> 715 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/37/a5054a9f9b4628e3924c5cb8f2147c6e2a3efcbin0 -> 630 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/38/55170cef875708da06ab9ad7fc6a73b531cda1bin0 -> 664 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/39/78944e4cd53edcc10a170ab2ff142f7295b958bin0 -> 185 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/3a/0dc89a8bd20e74fae69d2e038b47360fafb02ebin0 -> 64 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/3a/3f5a6ec1c968d1d2d5d20dee0d161a4351f2791
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/3a/8c70144d0334721154b1e0529716b368483d6fbin0 -> 91 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/3b/919b6e8a575b4779c8243ebea3e3beb436e88fbin0 -> 208 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/3e/eff81b57a0ac15a5ab6bb3a8e92511a01a429cbin0 -> 66 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/3f/d41804a7906db846af5e868444782e546af46abin0 -> 206 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/40/9f5d072decec684331672f2d6c0a9bc3640adbbin0 -> 51 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/41/71bb8d40e9fc830d79b757dc06ec6c14548b78bin0 -> 207 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/42/1b392106e079df6d412babd5636697938269ec2
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/42/44d13e2bbc38510320443bbb003f3967d12436bin0 -> 207 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/42/cdad903aef3e7b614675e6584a8be417941911bin0 -> 208 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/43/2faca0c62dc556ad71a22f23e541a46a8b0f6f2
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/43/5424798e5e1b21dd4588d1c291ba4eb179a838bin0 -> 208 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/43/6ea75c99f527e4b42fddb46abedf7726eb719d2
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/44/faf5fba1af850dae54f8b2345938d3c7ae479fbin0 -> 91 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/48/3065df53c0f4a02cdc6b2910b05d388fc17ffbbin0 -> 165 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/4a/06b258fed8a4d15967ec4253ae7366b70f727dbin0 -> 42 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/4b/7c5650008b2e747fe1809eeb5a1dde0e80850abin0 -> 615 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904bin0 -> 15 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/4c/49317a0912ca559d2048bc329994eb7d10474fbin0 -> 183 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/4c/62e9482ed42c1a6d08891906b26126daa4a8f5bin0 -> 182 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/4d/fc1be85a9d6c9898152444d32b238b4aecf8ccbin0 -> 168 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/4e/21d2d63357bde5027d1625f5ec6b430cdeb143bin0 -> 662 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/4e/70a6b06fc62481f80fbb74327849e7170eebffbin0 -> 207 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/4f/4e85a0ab8515e34302721fbcec06fa9d9c1a9abin0 -> 631 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/50/4dd93fb5b9c2a28c094c6e84ef0606de1e9b5cbin0 -> 50 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/50/dfa64a56b488fe8082371b182c8a3e3c942332bin0 -> 19 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/50/e4facaafb746cfed89287206274193c14172882
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/51/135c5884d7dd132fef3b432cca5826bab98f37bin0 -> 154 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/51/60ab78c1973dcd7cdebe2345dc8fcfc755e76dbin0 -> 91 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/53/9bd011c4822c560c1d17cab095006b7a10f707bin0 -> 163 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/56/07a8c4601a737daadd1f470bde3142aff570261
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/56/fcbad344aafe519bafcc33c87b8e64849d82abbin0 -> 19 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/5a/47615db824433f816ba62217dda6d46c5a7640bin0 -> 91 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/5a/ba269b3be41fc8db38068d3948c8af543fe609bin0 -> 208 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/5b/8e1e56cb99e8b99ac22eec8aebf6422ecd08c0bin0 -> 208 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/5c/27b5f7c6f6dd4e5b4d64976741d56c2df8f48abin0 -> 160 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/5d/998d5f278aff5693711bc48f6852aac4b603adbin0 -> 19 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/5e/8747f5200fac0f945a07daf6163ca9cb1a8da9bin0 -> 672 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/5f/18576d464946eb2338daeb8b4030019961f505bin0 -> 208 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/61/6d1209afac499b005f68309e1593b44899b054bin0 -> 69 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/63/e8773becdea9c3699c95a5740be5baa8be8d69bin0 -> 207 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/65/bea8448ca5b3104628ffbca553c54bde54b0fc3
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/66/6ffdfcf1eaa5641fa31064bf2607327e843c09bin0 -> 664 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/68/a2e1ee61a23a4728fe6b35580fbbbf729df370bin0 -> 665 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/68/af1fc7407fd9addf1701a87eb1c95c7494c598bin0 -> 443 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/68/f6182f4c85d39e1309d97c7e456156dc9c0096bin0 -> 755 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/6c/778edd0e4cf394f5a3df8b96db516024cc1bb8bin0 -> 636 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/6e/f31d35a3f5abc1e24f4f9afa5cb2016f03fa2d1
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/71/3e438567b28543235faf265c4c5b02b437c7fdbin0 -> 207 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/71/c50785d8d512293bd3af838b131f3da5829ebcbin0 -> 91 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/72/3181f1bfd30e47a6d1d36a4d874e31e7a0a1a42
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/73/b20c8e09fa2726d69ff66969186014165da3c3bin0 -> 208 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/74/4df1bdf0f7bca20deb23e5a5eb8255fc237901bin0 -> 207 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/75/afa96db00c26c6ebf3b377615b4e2a20563ee4bin0 -> 19 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/75/c653822173a8e5795153ec3773dfe44bb9bb631
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/76/6afbfd7d42f757f1fac9ea550c9fcbc8041b89bin0 -> 180 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/78/3d6539dde96b8873c5b5da3e79cc14cd64830b4
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/7a/9277e0c5ec75339f011c176d0c20e513c4de1c1
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/7c/61830f8b8632665bb44ae5d219f520f5aa5bb4bin0 -> 91 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/7c/7bf85e978f1d18c0566f702d2cb7766b9c8d4f1
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/7c/7e08f9559d9e1551b91e1cf68f1d0066109addbin0 -> 443 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/7c/9a30d8dcee320a3b1f9ed10b582479faa9d3a1bin0 -> 152 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/7e/2d2bad4fc21f2832ca2afd48b1f95ab37ffb92bin0 -> 19 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/7e/3056f6765b3044ab09701077dbe1eb5b0e9ad0bin0 -> 208 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/7e/70a7872576bba7e299cde45abb7da1e4d7ba81bin0 -> 61 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/7f/9c1d78d760cbfa99273bc1ef642d994c6baa5cbin0 -> 91 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/81/5b5a1c80ca749d705c7aa0cb294a00cbedd3405
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/81/60cb53660b86c954144b8dbbb0b6e4db4ba6babin0 -> 51 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/88/8588a782ad433fbf0cc526e07cfe6f4a6b60b3bin0 -> 208 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/88/eb3f98849f4b8d0555395f514800900a01dc8fbin0 -> 209 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/89/8d12687fb35be271c27c795a6b32c8b51da79ebin0 -> 663 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/8a/bda8de114a93f2d3c5a975ee2960f31e24be582
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/8f/1b918542a5fe9b3bb7a8770a7525ad5b3b5864bin0 -> 51 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/8f/35f30bfe09513f96cf8aa4df0834ae34e93bae1
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/94/d2c01087f48213bd157222d54edfefd77c9bbabin0 -> 621 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/95/78b04e2087976e382622322ba476aa40398dc7bin0 -> 620 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/96/23368f0fc562d6d840372ae17dc4cc32d51a802
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/97/3b70322e758da87e1ce21d2195d86c5e4e96471
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/97/5dd228fd1b0cacf2988167088fd1190c9ac0f5bin0 -> 186 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/98/1c79eb38518d3821e73bb159dc413bb42d6614bin0 -> 208 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/98/5b725cf91c6861b5e7a419415d03cbcf5f16cabin0 -> 91 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/98/cacbdd1fac7bbab54a6c7c97aa2103219e08b8bin0 -> 19 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/99/754e36599906b81b917447280c4918269e14ffbin0 -> 19 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/9a/228c1ee87f286202ec9a25de837a18550013b5bin0 -> 91 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/9a/e63b4a8ce0f181b2d1d098971733a103226917bin0 -> 240 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/9b/258ad4c39f40c24f66bf1faf48eb6202d59c85bin0 -> 240 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/9c/3f1c70db28c00ce74b22ba3edafe16d9cf03d4bin0 -> 208 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/9c/dde216049c6a5ccddac0ad81f604419d8990ed2
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/9e/12bce04446d097ae1782967a5888c2e2a0d35bbin0 -> 268 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/a0/2d4fd126e0cc8fb46ee48cf38bad36d44f2dbcbin0 -> 649 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/a0/65d3022e99a1943177c10a53cce38bc2127042bin0 -> 162 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/a0/ce8909834f389b4f8be6a6ec420868422d40a1bin0 -> 154 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/a2/817ed0e8ca6fe52bf0a20b2f50eb94b9ea5415bin0 -> 153 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/a2/8c21c90aa36580641b345011869d1a899a67832
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/a2/fa36ffc4a565a223e225d15b18774f87d0c4f03
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/a3/4e5a16feabbd0335a633aadb8217c9f3dba58dbin0 -> 164 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/a3/5aa65d86215fce909fc0bcce8949d12becba44bin0 -> 91 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/a3/ca4c462e93fee824c8ad500917ae34b800dea4bin0 -> 91 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/a6/64873b1c0b9a1ed300f8644dde536fdaa3a34fbin0 -> 51 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/a7/b066537e6be7109abfe4ff97b675d4e077da20bin0 -> 621 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/a8/2a121ea36b115548d6dad2cd86ec27f06f7b30bin0 -> 208 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/a9/9bf55117ab1958171fccfeb19885f707bd08fdbin0 -> 90 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/aa/9e263294fd2f6f6fd9ceab23ca8ce3ea2ce707bin0 -> 175 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/ad/1ea02c2cc4f55c1dff87b80a086206a73885eb2
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/ad/2ace9e15f66b3d1138922e6ffdc3ea3f967fa6bin0 -> 170 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/ad/98bfa4679fb00b89207a0a11b8bbf91a3e4de9bin0 -> 208 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/b0/1de62cf11945685c98ec671edabdff3e90ddc5bin0 -> 185 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/b0/4823b75c8220b89c2f8da54709cda262304cd3bin0 -> 19 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/b1/71224a4f604b6091072007765419b14c232c1dbin0 -> 181 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/b2/908343e3c16249d0036dd444fc0d4662cd8c0ebin0 -> 154 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/b2/a81ead9e722af0099fccfb478cea88eea749a2bin0 -> 664 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/b4/cefb3c75770e57bb8bb44e4a50d9578009e847bin0 -> 639 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/b6/bd0f9952f396e757d3f91e08c59a7e91707201bin0 -> 74 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/b7/de2b52ba055688061355fad1599a5d214ce8f8bin0 -> 74 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/b8/a3b657edcf31e6365a2f1c45d45e6c9ebe8f02bin0 -> 91 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/b9/1ef5ffa8612616c8e76051901caafd723f0e2cbin0 -> 712 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/ba/9dcfe079848e8e5c1b53bc3b6e47ff57f6e481bin0 -> 91 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/bb/4e0014fb09d24312f0af37c7a45e5488f195104
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/bd/97980c22d122509cdd915fd9788d56c8d3ae20bin0 -> 163 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/c0/bd078a61d2cc22c52ca5ce04abdcdc5cc1829ebin0 -> 207 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/c0/dcb4bfcd86e65a822090aa7a0455413828886bbin0 -> 64 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/c4/44758b02d4af6e3145ac2fc0e3ed02199cf7ecbin0 -> 17 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/c4/83ca4bb087174af5cb51d7caa9c09fe4a28ccb1
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/c4/e6cca3ec6ae0148ed231f97257df8c311e015f1
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/c7/f3257db72e885d6612080c003e0f2ef480e0c4bin0 -> 69 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/ca/224bba0a8a24f1768804fe5f565b1014af7ef2bin0 -> 170 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/ca/49d1a8b6116ffeba22667bba265fa5261df7ab2
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/ca/7d316d6d9af99d2481e980d68b77e572d80fe7bin0 -> 207 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/ca/fa936d25f0b397432a27201f6b3284c47df8bebin0 -> 712 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/cb/49ad76147f5f9439cbd6133708b76142660660bin0 -> 641 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/ce/0d744cd2e18eacf883d43471636f231c0995e3bin0 -> 91 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/d0/97bcf99adb1022a6b7d2e94fed2031ebd9d89cbin0 -> 19 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/d0/c9bd6e2a3e327d81a32de51201d3bd58909f7cbin0 -> 181 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/d0/dd5d9083bda65ec99aa8b9b64a5a278771b70abin0 -> 620 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/d2/682aaf9594080ce877b5eeee110850fd6e34801
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/d3/482dbdca5bb83aaf3e3768359855d55aef84d7bin0 -> 91 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/d5/015f9436b2d8c842bf6616e7cf5bc54eb79cedbin0 -> 91 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/d6/04c75019c282144bdbbf3fd3462ba74b240efcbin0 -> 620 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/d7/1c24b3b113fd1d1909998c5bfe33b86a65ee03bin0 -> 240 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/d8/dd349b78f19a4ebe3357bacb8138f00bf5ed41bin0 -> 277 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/d8/e05a90b3c2240d71a20c2502c937d9b7d227772
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/da/b7b53383a1fec46632e60a1d847ce4f9ae14f2bin0 -> 208 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/db/203155a789fb749aa3c14e93eea2c744a9c6c71
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/db/51adf2b699eed93e883d6425f5e6c50165a9c2bin0 -> 91 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/db/7e2af8ca83b8943adce7ba37d85f8fe7d7d2a9bin0 -> 61 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/de/a7215f259b2cced87d1bda6c72f8b4ce37a2ffbin0 -> 357 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/de/de92a05a0841faa8e4ad6700285cd208184458bin0 -> 182 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/e0/15ebd79a72a88b9291df11771caf56f463e8f9bin0 -> 91 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/e1/512550f09d980214e46e6d3f5a2b20c3d75755bin0 -> 208 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/e1/dcfc3038be54195a59817c89782b261e46cb051
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/e2/93bfdddb81a853bbb16b8b58e68626f30841a4bin0 -> 207 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/e2/c84bb33992a455b1a7a5019f0e38d883d3f475bin0 -> 208 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/e2/d185fa827d58134cea20b9e1df893833c6560ebin0 -> 208 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/e5/0fbbd701458757bdfe9815f58ed717c588d1b53
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/e5/20e6aaf8d1e68a433e29d4360c1e74aa4b24d1bin0 -> 91 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/e6/269ce9017816d67c7189a58b6d0d22bf4b8a1abin0 -> 91 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/e9/30c8c67848df4aa66319c5752fab6b8fdec765bin0 -> 64 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/ea/3521485adfa0b0373deaaa06db9218a22edae8bin0 -> 91 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/ef/1783444b61a8671beea4ce1f4d0202677dfbfb3
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/f0/856993e005c0d8ed2dc7cdc222cc1d89fb3c77bin0 -> 51 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/f1/3e1bc6ba935fce2efffa5be4c4832404034ef1bin0 -> 206 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/f1/72517a8cf39e009ffff541ee52429b89e418f3bin0 -> 268 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/f1/b44c04989a3a1c14b036cfadfa328d53a7bc5ebin0 -> 672 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/f2/9ccca75754d8476e5dad8cf250e03d43fe9e6cbin0 -> 51 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/f3/2c284f537ff1a55d3cbfe9a37d431b6edfadc2bin0 -> 19 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/f3/5f159ff5d44dfd9f52d63dd5b659f0521ff569bin0 -> 669 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/f4/c149e7d0983e90e9ee802ff57ae3c905ba63dabin0 -> 91 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/f5/1658077d85f2264fa179b4d0848268cb3475c32
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/f6/5de1834f57708e76d8dc25502b7f1ecbcce162bin0 -> 156 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/f7/929c5a67a4bdc98247fb4b5098675723932a64bin0 -> 207 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/f9/c04e4e9d4aaf1e6fe7478a7cc0756554974c2bbin0 -> 45 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/fa/567f568ed72157c0c617438d077695b99d9aacbin0 -> 662 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/fd/8b5fe88cda995e70a22ed98701e65b843e05ecbin0 -> 165 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/fe/f01f3104c8047d05e8572e521c454f8fd4b8dbbin0 -> 207 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/ff/b36e513f5fdf8a6ba850a20142676a2ac4807dbin0 -> 355 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/objects/info/commit-graphbin0 -> 4624 bytes
-rw-r--r--tests/resources/merge-recursive/.gitted/refs/heads/branchA-11
-rw-r--r--tests/resources/merge-recursive/.gitted/refs/heads/branchA-21
-rw-r--r--tests/resources/merge-recursive/.gitted/refs/heads/branchB-11
-rw-r--r--tests/resources/merge-recursive/.gitted/refs/heads/branchB-21
-rw-r--r--tests/resources/merge-recursive/.gitted/refs/heads/branchC-11
-rw-r--r--tests/resources/merge-recursive/.gitted/refs/heads/branchC-21
-rw-r--r--tests/resources/merge-recursive/.gitted/refs/heads/branchD-11
-rw-r--r--tests/resources/merge-recursive/.gitted/refs/heads/branchD-21
-rw-r--r--tests/resources/merge-recursive/.gitted/refs/heads/branchE-11
-rw-r--r--tests/resources/merge-recursive/.gitted/refs/heads/branchE-21
-rw-r--r--tests/resources/merge-recursive/.gitted/refs/heads/branchE-31
-rw-r--r--tests/resources/merge-recursive/.gitted/refs/heads/branchF-11
-rw-r--r--tests/resources/merge-recursive/.gitted/refs/heads/branchF-21
-rw-r--r--tests/resources/merge-recursive/.gitted/refs/heads/branchG-11
-rw-r--r--tests/resources/merge-recursive/.gitted/refs/heads/branchG-21
-rw-r--r--tests/resources/merge-recursive/.gitted/refs/heads/branchH-11
-rw-r--r--tests/resources/merge-recursive/.gitted/refs/heads/branchH-21
-rw-r--r--tests/resources/merge-recursive/.gitted/refs/heads/branchI-11
-rw-r--r--tests/resources/merge-recursive/.gitted/refs/heads/branchI-21
-rw-r--r--tests/resources/merge-recursive/.gitted/refs/heads/branchJ-11
-rw-r--r--tests/resources/merge-recursive/.gitted/refs/heads/branchJ-21
-rw-r--r--tests/resources/merge-recursive/.gitted/refs/heads/branchK-11
-rw-r--r--tests/resources/merge-recursive/.gitted/refs/heads/branchK-21
-rw-r--r--tests/resources/merge-recursive/asparagus.txt10
-rw-r--r--tests/resources/merge-recursive/beef.txt22
-rw-r--r--tests/resources/merge-recursive/bouilli.txt18
-rw-r--r--tests/resources/merge-recursive/gravy.txt8
-rw-r--r--tests/resources/merge-recursive/oyster.txt13
-rw-r--r--tests/resources/merge-recursive/veal.txt20
-rw-r--r--tests/resources/merge-resolve/.gitted/COMMIT_EDITMSG1
-rw-r--r--tests/resources/merge-resolve/.gitted/HEAD1
-rw-r--r--tests/resources/merge-resolve/.gitted/ORIG_HEAD1
-rw-r--r--tests/resources/merge-resolve/.gitted/config8
-rw-r--r--tests/resources/merge-resolve/.gitted/description1
-rw-r--r--tests/resources/merge-resolve/.gitted/indexbin0 -> 624 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/HEAD236
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/branch2
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/df_ancestor5
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/df_side114
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/df_side29
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/ff_branch5
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/master5
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/octo12
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/octo22
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/octo32
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/octo42
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/octo52
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/octo63
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/renames12
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/renames23
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-103
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10-branch2
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-113
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11-branch2
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-133
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13-branch2
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-143
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14-branch2
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt2
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt-branch2
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt3
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt-branch1
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-42
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4-branch2
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-12
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1-branch2
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-23
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2-branch2
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-63
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6-branch2
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-73
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7-branch5
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-83
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8-branch2
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-93
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9-branch2
-rw-r--r--tests/resources/merge-resolve/.gitted/logs/refs/heads/unrelated1
-rw-r--r--tests/resources/merge-resolve/.gitted/modules/submodule/HEAD1
-rw-r--r--tests/resources/merge-resolve/.gitted/modules/submodule/ORIG_HEAD1
-rw-r--r--tests/resources/merge-resolve/.gitted/modules/submodule/config15
-rw-r--r--tests/resources/merge-resolve/.gitted/modules/submodule/indexbin0 -> 153 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/modules/submodule/info/exclude6
-rw-r--r--tests/resources/merge-resolve/.gitted/modules/submodule/objects/18/fae1354bba0a5f1e6a531f9988369142c24a9ebin0 -> 54 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/modules/submodule/objects/29/7aa6cd028b3336c7802c7a6f49143da4e1602dbin0 -> 161 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/modules/submodule/objects/38/6c80dc813b89d719797668f40c1be0a6efa996bin0 -> 32 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/modules/submodule/objects/ab/435a147bae6d5906ecfd0916a570c4ab3eeea8bin0 -> 64 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/modules/submodule/objects/ad/16e0a7684ea95bf892980a2ee412293ae979ccbin0 -> 64 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/modules/submodule/objects/ae/39c77c70cb6bad18bb471912460c4e1ba0f5862
-rw-r--r--tests/resources/merge-resolve/.gitted/modules/submodule/objects/c2/0765f6e24e8bbb63a648d0d11d84da63170190bin0 -> 52 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/modules/submodule/objects/d3/d806a4bef96889117fd7ebac0e3cb5ec1529323
-rw-r--r--tests/resources/merge-resolve/.gitted/modules/submodule/objects/f1/065ff5593604072837fecaad3e2e268cb0147bbin0 -> 64 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/modules/submodule/packed-refs3
-rw-r--r--tests/resources/merge-resolve/.gitted/modules/submodule/refs/heads/master1
-rw-r--r--tests/resources/merge-resolve/.gitted/modules/submodule/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/00/5b6fcc8fec71d2550bef8462d169b3c26aa14bbin0 -> 168 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/00/9b9cab6fdac02915a88ecd078b7a792ed802d8bin0 -> 164 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/00/c7d33f1ffa79d19c2272b370fcaeaadba49c08bin0 -> 147 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/01/f149e1b8f84bd8896aaff6d6b22af88459ded0bin0 -> 166 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/02/04a84f822acbf6386b36d33f1f6bc68bbbf858bin0 -> 168 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/02/251f990ca8e92e7ae61d3426163fa821c64001bin0 -> 264 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/03/21415405cb906c46869919af56d51dbbe5e85cbin0 -> 271 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/03/2ebc5ab85d9553bb187d3cd40875ff23a63ed0bin0 -> 29 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/03/b87706555accbf874ccd410dbda01e8e70a67fbin0 -> 353 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/03/dad1005e5d06d418f50b12e0bcd48ff2306a03bin0 -> 264 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/05/1ffd7901a442faf56b226161649074f15c7c471
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/05/8541fc37114bfc1dddf6bd6bffc7fae5c2e6febin0 -> 63 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/05/f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91cbin0 -> 170 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/07/a759da919f737221791d542f176ab49c88837fbin0 -> 165 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/07/c514b04698e068892b31c8d352b85813b99c6ebin0 -> 32 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/09/055301463b7f2f8ee5d368f8ed5c0a40ad8515bin0 -> 41 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/09/17bb159596aea4d295f4857da77e8f96b3c7dcbin0 -> 36 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/09/2ce8682d7f3a2a3a769a6daca58950168ba5c4bin0 -> 163 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/09/3bebf072dd4bbba88833667d6ffe454df199e1bin0 -> 266 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/09/768bed22680cdb0859683fa9677ccc8d5a25c1bin0 -> 275 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/0a/75d9aac1dc84fb5aa51f7325c0ab53242ddef7bin0 -> 275 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/0c/fd6c54ef6532d862408f562309dc9c74a401e8bin0 -> 28 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/0d/52e3a556e189ba0948ae56780918011c1b167dbin0 -> 235 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/0d/872f8e871a30208305978ecbf9e66d864f1638bin0 -> 89 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/0e/c5f433959cd46177f745903353efb5be08d151bin0 -> 165 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/0f/3fc5dddc8964b9ac1040d0e957f9eb02d9efb3bin0 -> 47 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/11/aeee27ac45a8402c2fd5b875d66dd844e5df00bin0 -> 51 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/11/deab00b2d3a6f5a3073988ac050c2d7b6655e2bin0 -> 34 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/11/f4f3c08b737f5fd896cbefa1425ee63b21b2fa1
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/13/d1be4ea52a6ced1d7a1d832f0ee3c399348e5ebin0 -> 168 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/14/39088f509b79b1535b64193137d3ce4b240734bin0 -> 58 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/15/8dc7bedb202f5b26502bf3574faa7f4238d56c2
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/16/f825815cfd20a07a75c71554e82d8eede0b0611
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/17/8940b450f238a56c0d75b7955cb57b38191982bin0 -> 65 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/18/3310e30fb1499af8c619108ffea4d300b5e778bin0 -> 170 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/18/cb316b1cefa0f8a6946f0e201a8e1a6f845ab9bin0 -> 68 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/19/b7ac485269b672a101060894de3ba9c2a24dd1bin0 -> 53 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/1a/010b1c0f081b2e8901d55307a15c29ff30af0ebin0 -> 19 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/1c/51d885170f57a0c4e8c69ff6363d91a5b51f85bin0 -> 30 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/1c/ff9ec6a47a537380dedfdd17c9e76d74259a2bbin0 -> 33 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/1e/4ff029aee68d0d69ef9eb6efa6cbf1ec732f99bin0 -> 29 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/1f/81433e3161efbf250576c58fede7f6b836f3d3bin0 -> 262 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/20/91d94c8bd3eb0835dc5220de5e8bb310fa1513bin0 -> 271 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/21/671e290278286fb2ce4c63d01699b67adce331bin0 -> 79 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/22/7792b52aaa0b238bea00ec7e509b02623f168cbin0 -> 102 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/23/3c0919c998ed110a4b6ff36f353aec8b713487bin0 -> 43 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/23/92a2dacc9efb562b8635d6579fb458751c7c5bbin0 -> 142 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/23/ed141a6ae1e798b2f721afedbe947c119111babin0 -> 30 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/24/1a1005cd9b980732741b74385b891142bcba28bin0 -> 67 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/24/2591eb280ee9eeb2ce63524b9a8b9bc4cb515dbin0 -> 30 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/24/90b9f1a079420870027deefb49f51d6656cf74bin0 -> 268 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/25/9d08ca43af9200e9ea9a098e44a5a350ebd9b3bin0 -> 381 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/25/c40b7660c08c8fb581f770312f41b9b03119d1bin0 -> 31 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/26/153a3ff3649b6c2bb652d3f06878c6e0a172f9bin0 -> 48 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/27/133da702ba3c60af2a01e96c2555ff4045d692bin0 -> 32 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/27/4bbe983022fb4c02f8a2bf2ebe8da4fe130054bin0 -> 24 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/2a/f2d9bcbc73723ac988bb202d4397f72a6ca7a0bin0 -> 105 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/2b/0de5dc27505dcdd83a75c8bf1fcd9462cd7addbin0 -> 147 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/2b/5f1f181ee3b58ea751f5dd5d8f9b445520a136bin0 -> 53 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863bin0 -> 56 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/2b/fdd7e1b6c6ae993f23dfe8e84a8e06a772fa2abin0 -> 231 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/2d/a538570bc1e5b2c3e855bf702f35248ad0735f2
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/2f/2e37b7ebbae467978610896ca3aafcdad2ee67bin0 -> 52 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/2f/4024ce528d36d8670c289cce5a7963e625bb0cbin0 -> 179 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/2f/56120107d680129a5d9791b521cb1e73a2ed313
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/2f/598248eeccfc27e5ca44d9d96383f6dfea7b161
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/31/68dca1a561889b045a6441909f4c56145e666d2
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/31/d5472536041a83d986829240bbbdc897c6f8a6bin0 -> 41 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/32/21dd512b7e2dc4b5bd03046df6c81b2ab2070bbin0 -> 47 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/33/46d64325b39e5323733492cd55f808994a2475bin0 -> 33 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/33/d500f588fbbe65901d82b4e6b008e549064be02
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/34/8dcd41e2b467991578e92bedd16971b877ef1ebin0 -> 51 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/34/bfafff88eaf118402b44e6f3e2dbbf1a582b051
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/35/0c6eb3010efc403a6bed682332635314e9ed58bin0 -> 92 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/35/411bfb77cd2cc431f3a03a2b4976ed94b5d241bin0 -> 31 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/35/4704d3613ad4228e4786fc76656b11e98236c4bin0 -> 41 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/35/632e43612c06a3ea924bfbacd48333da874c291
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/35/75826c96a975031d2c14368529cc5c4353a8fdbin0 -> 163 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/36/219b49367146cb2e6a1555b5a9ebd4d0328495bin0 -> 68 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/36/4bbe4ce80c7bd31e6307dce77d46e3e1759fb3bin0 -> 35 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/37/48859b001c6e627e712a07951aee40afd19b41bin0 -> 41 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/37/f53a5a14f64e91089a39ea58e71c87d81df7651
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/38/5c8a0f26ddf79e9041e15e17dc352ed2c4cced2
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/3b/47b031b3e55ae11e14a05260b1c3ffd6838d55bin0 -> 161 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/3b/bf0bf59b20df5d5fc58b9fc1dc07be637c301fbin0 -> 269 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/3e/f4d30382ca33fdeba9fda895a99e0891ba37aabin0 -> 36 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/3e/f9bfe82f9635518ae89152322f3b46fd4ba25bbin0 -> 172 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/40/2784a46a4a3982294231594cbeb431f506d22cbin0 -> 83 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/41/2b32fb66137366147f1801ecc962452757d48a2
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/42/18670ab81cc219a9f94befb5c5dad90ec52648bin0 -> 47 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/43/aafd43bea779ec74317dc361f45ae3f532a505bin0 -> 37 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/43/c338656342227a3a3cd3aa85cbf784061f5425bin0 -> 266 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/45/299c1ca5e07bba1fd90843056fb559f96b1f5abin0 -> 58 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/46/6daf8552b891e5c22bc58c9d7fc1a2eb8f0289bin0 -> 382 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/47/6dbb3e207313d1d8aaa120c6ad204bf1295e53bin0 -> 522 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/47/8172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2bin0 -> 165 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/49/130a28ef567af9a6a6104c38773fedfa5f9742bin0 -> 37 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/49/9df817155e4bdd3c6ee192a72c52f481818230bin0 -> 35 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/49/fd9edac79d15c8fbfca2d481cbb900beba22a63
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/4a/9550ebcc97ce22b22f45af7b829bb030d003f5bin0 -> 53 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/4b/253da36a0ae8bfce63aeabd8c5b584299255942
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/4b/48deed3a433909bfd6b6ab3d4b91348b6af464bin0 -> 24 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904bin0 -> 15 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/4c/9fac0707f8d4195037ae5a681aa48626491541bin0 -> 167 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/4c/a408a8c88655f7586a1b580be6fad138121e98bin0 -> 159 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/4d/d1ef7569b18d92d93c0a35bb6b93049137b355bin0 -> 30 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/4e/0d9401aee78eb345a8685a859d37c8c3c0bbedbin0 -> 262 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/4e/886e602529caa9ab11d71f86634bd1b6e0de10bin0 -> 56 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/4e/b04c9e79e88f6640d01ff5b25ca2a60764f216bin0 -> 34 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/4f/e93c0ec83eb6305cbace3dace88ecee1b63cb6bin0 -> 161 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/50/12fd565b1393bdfda1805d4ec38ce6619e1fd1bin0 -> 29 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/50/4f75ac95a71ef98051817618576a68505b92f9bin0 -> 93 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/50/84fc2a88b6bdba8db93bd3953a8f4fdb470238bin0 -> 53 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/50/c5dc8cdfe40c688eb0a0e23be54dd57cae2e782
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/50/ce7d7d01217679e26c55939eef119e0c93e272bin0 -> 159 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/51/95a1b480f66691b667f10a9e41e70115a78351bin0 -> 170 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/52/d8bc572af2b6d4ee0d5e62ed5d1fbad92210a93
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/53/825f41ac8d640612f9423a2f03a69f3d96809abin0 -> 164 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/54/269b3f6ec3d7d4ede24dd350dd5d605495c3ae2
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/54/59c89aa0026d543ce8343bd89871bce543f9c23
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/54/7607c690372fe81fab8e3bb44c530e129118fdbin0 -> 58 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/55/b4e4687e7a0d9ca367016ed930f385d4022e6f1
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/56/6ab53c220a2eafc1212af1a024513230280ab93
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/56/a638b76b75e068590ac999c2f8621e7f3e264c1
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/57/079a46233ae2b6df62e9ade71c4948512abefbbin0 -> 168 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/58/43febcb23480df0b5edb22a21c59c772bb8e29bin0 -> 71 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/58/87a5e516c53bd58efb0f02ec6aa031b6fe9ad7bin0 -> 47 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/58/e853f66699fd02629fd50bde08082bc005933abin0 -> 160 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/59/6803b523203a4851c824c07366906f8353f4adbin0 -> 163 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/5c/2411f8075f48a6b2fdb85ebc0d371747c4df15bin0 -> 37 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/5c/341ead2ba6f2af98ce5ec3fe84f6b6d2899c0dbin0 -> 37 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/5c/3b68a71fc4fa5d362fd3875e53137c6a5ab7a5bin0 -> 40 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/5d/c1018e90b19654bee986b7a0c268804d39659dbin0 -> 168 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/5d/dd0fe66f990dc0e5cf9fec6d9b465240e9537fbin0 -> 43 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/5e/b7bb6a146eb3c7fd3990b240a2308eceb1cf8dbin0 -> 268 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/5f/bfbdc04b4eca46f54f4853a3c5a1dce28f5165bin0 -> 283 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/60/61fe116ecba0800c26113ea1a7dfac2e16eeafbin0 -> 87 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/60/91fc2c036a382a69489e3f518ee5aae9a4e567bin0 -> 258 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/61/340eeed7340fa6a8792def9a5938bb5d4434bbbin0 -> 92 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/61/78885b38fe96e825ac0f492c0a941f288b37f6bin0 -> 289 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/62/12c31dab5e482247d7977e4f0dd3601decf13bbin0 -> 45 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/62/269111c3b02a9355badcb9da8678b1bf41787bbin0 -> 269 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/62/33c6a0670228627f93c01cef32485a30403670bin0 -> 44 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/62/c4f6533c9a3894191fdcb96a3be935ade63f1abin0 -> 53 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/63/247125386de9ec90a27ad36169307bf8a11a381
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/63/ec604d491161ddafdae4179843c26d54bd999abin0 -> 30 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/67/110d77886b2af6309b9212961e72b8583e5fa91
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/67/18a45909532d1fcf5600d0877f7fe7e78f0b861
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/68/c6c84b091926c7d90aa6a79b2bc3bb6adccd8ebin0 -> 55 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/68/f7c02064019d89e40e51d7776b6f67914420a2bin0 -> 105 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/69/f570c57b24ea7c086e94c5e574964798321435bin0 -> 266 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/6a/e1a3967031a42cf955d9d5c2395211ac82f6cfbin0 -> 272 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/6b/7e37be8ce0b897093f2878a9dcd8f396beda2cbin0 -> 53 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/6c/06dcd163587c2cc18be44857e0b71116382aebbin0 -> 30 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/6e/3b9eb35214d4e31ed5789afc7d520ac798ce55bin0 -> 51 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/6f/32739c3724d1d5f855299309f388606f407468bin0 -> 630 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/6f/a33014764bf1120a454eb8437ae098238e409bbin0 -> 168 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/6f/be9fb85c86d7d1435f728da418bdff52c640a9bin0 -> 83 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/71/17467b18605a660ebe5586df69e2311ed5609fbin0 -> 265 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/71/2ebba6669ea847d9829e4f1059d6c830c8b531bin0 -> 152 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/71/add2d7b93d55bf3600f8a1582beceebbd050c8bin0 -> 264 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/72/cdb057b340205164478565e91eb71647e66891bin0 -> 65 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/72/ea499e108df5ff0a4a913e7655bbeeb1fb69f2bin0 -> 26 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/74/df13f0793afdaa972150bba976f7de8284914ebin0 -> 26 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/75/a811bf6bc57694adb3fe604786f3a4efd1cd1b2
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/76/63fce0130db092936b137cabd693ec234eb060bin0 -> 49 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/76/ab0e2868197ec158ddd6c78d8a0d2fd73d38f9bin0 -> 37 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/7a/a3edf2bcfee22398e6b55295aa56366b7aaf76bin0 -> 271 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/7a/a825857f87aea74ddf13d954568aa30dfcdeb4bin0 -> 117 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/7a/f14d9c679baaef35555095f4f5d33e9a569ab9bin0 -> 149 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/7c/04ca611203ed320c5f495b9813054dd23be3be2
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/7c/2c5228c9e90170d4a35e6558e47163daf092e5bin0 -> 172 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/7c/b63eed597130ba4abb87b3e544b850219055203
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/7e/2d058d5fedf8329db44db4fac610d6b1a89159bin0 -> 165 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/7f/7a2da58126226986d71c6ddfab4afba693280dbin0 -> 199 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/80/a8fbb3abb1ba423d554e9630b8fc2e5698f86bbin0 -> 168 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/81/1c70fcb6d5bbd022d04cc31836d30b436f9551bin0 -> 169 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/81/87117062b750eed4f93fd7e899f17b52ce554dbin0 -> 170 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/83/07d93a155903a5c49576583f0ce1f6ff897c0ebin0 -> 30 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/83/6b8b82b26cab22eaaed8820877c76d6c8bca19bin0 -> 30 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/83/824a8c6658768e2013905219cc8c64cc3d9a2ebin0 -> 382 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/84/9619b03ae540acee4d1edec96b86993da6b4973
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/84/de84f8f3a6d63e636ee9ad81f4b80512fa9bbebin0 -> 41 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/86/088dae8bade454995b21a1c88107b0e1accdabbin0 -> 47 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/87/b4926260d77a3b851e71ecce06839bd650b231bin0 -> 43 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/88/e185910a15cd13bdf44854ad037f4842b03b29bin0 -> 177 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/8a/ad9d0ea334951da47b621a475b39cc6ed759bfbin0 -> 51 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/8a/ae714f7d939309d7f132b30646d96743134a9f1
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/8b/095d8fd01594f4d14454d073e3ac57b9ce485fbin0 -> 201 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/8b/5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a1
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/8b/7cd60d49ce3a1a770ece43b7d29b5cf462a33abin0 -> 82 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/8b/fb012a6d809e499bd8d3e194a3929bc8995b93bin0 -> 34 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/8c/749d9968d4b10dcfb06c9f97d0e5d92d3370712
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/8f/4433f8593ddd65b7dd43dd4564d841f4d9c8aabin0 -> 164 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/90/a336c7dacbe295159413559b0043b8bdc60d57bin0 -> 271 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/91/2b2d7819cf9c1029e414883857ed61d597a1a5bin0 -> 295 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/91/8bb3e09090a9995d48af9a2a6296d7e6088d1cbin0 -> 38 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/91/f44111cb1cb1358ac6944ad356ca1738813ea1bin0 -> 149 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/92/7d4943cdbdc9a667db8e62cfd0a41870235c51bin0 -> 535 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/93/77fccdb210540b8c0520cc6e80eb632c20bd25bin0 -> 53 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/94/29c05dd6f6f39fc567b4ce923b16df5d3d7a7abin0 -> 94 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/94/4f5dd1a867cab4c2bbcb896493435cae1dcc1a2
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/94/8ba6e701c1edab0c2d394fb7c5538334129793bin0 -> 71 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/95/646149ab6b6ba6edc83cff678582538b457b2b3
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/95/9de65e568274120fdf9e3af9f77b1550122149bin0 -> 40 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/96/8ca794a4597f7f6abbb2b8d940b4078a0f3fd4bin0 -> 53 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/96/bca8d4f05cc4c5e33e4389f80a1309e86fe054bin0 -> 149 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/97/7c696519c5a3004c5f1d15d60c89dbeb8f235fbin0 -> 160 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/98/ba4205fcf31f5dd93c916d35fe3f3b3d0e67141
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/98/d52d07c0b0bbf2b46548f6aa521295c2cb55db3
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/99/b4f7e4f24470fa06b980bc21f1095c2a9425c0bin0 -> 164 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/9a/301fbe6fada7dcb74fcd7c20269b5c743459a7bin0 -> 163 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/9a/f731fa116d1eb9a6c0109562472cfee6f5a979bin0 -> 48 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/9c/0b6c34ef379a42d858f03fef38630f476b9102bin0 -> 38 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/9e/7f4359c469f309b6057febf4c6e80742cbed5bbin0 -> 539 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/9e/fe7723802d4305142eee177e018fee1572c4f4bin0 -> 36 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/9f/74397a3397b3585faf09e9926b110d7f654254bin0 -> 621 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a0/31a28ae70e33a641ce4b8a8f6317f1ab79dee4bin0 -> 37 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a1/07e18a58f38c46086c8f8f1dcd54c40154eeb63
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a2/d8d1824c68541cca94ffb90f79291eba495921bin0 -> 30 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a3/9a620dae5bc8b4e771cd4d251b7d080401a21ebin0 -> 29 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a3/fabece9eb8748da810e1e08266fef9b7136ad4bin0 -> 164 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a4/1b1bb6d0be3c22fb654234c33b428e15c8cc27bin0 -> 92 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a4/3150a738849c59376cf30bb2a68348a83c8f48bin0 -> 162 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a5/563304ddf6caba25cb50323a2ea6f7dbfcadcabin0 -> 48 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a7/08b253bd507417ec42d1467a7fd2d7519c4956bin0 -> 40 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a7/65fb87eb2f7a1920b73b2d5a057f8f8476a42bbin0 -> 170 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a7/7a56a49f8f3ae242e02717f18ebbc60c5cc543bin0 -> 65 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a7/dbfcbfc1a60709cb80b5ca24539008456531d01
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a8/02e06f1782a9645b9851bc7202cee74a8a4972bin0 -> 172 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a8/87dd39ad3edd610fc9083dcb61e40ab50673d11
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/a9/0bc3fb6f15181972a2959a921429efbd81a4732
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ab/40af3cb8a3ed2e2843e96d9aa7871336b94573bin0 -> 161 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ab/6c44a2e84492ad4b41bb6bac87353e9d02ac8bbin0 -> 33 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ab/929391ac42572f92110f3deeb4f0844a951e22bin0 -> 40 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ac/4045f965119e6998f4340ed0f411decfb3ec05bin0 -> 29 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ad/01aebfdf2ac13145efafe3f9fcf798882f1730bin0 -> 158 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ad/26b598134264fd284292cb233fc0b2f25851dabin0 -> 43 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ad/a14492498136771f69dd451866cabcb0e9ef9abin0 -> 39 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ad/a55a45d14527dc3dfc714ea1c65d2e1e6fbe871
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/b2/d399ae15224e1d58066e3c8df70ce37de7a6562
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/b4/2712cfe99a1a500b2a51fe984e0b8a7702ba115
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/b6/9fe837e4cecfd4c9a40cdca7c138468687df072
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/b6/f610aef53bd343e6c96227de874c66f00ee8e8bin0 -> 162 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/b7/a2576f9fc20024ac9ef17cb134acbd1ac73127bin0 -> 320 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/b8/a3a806d3950e8c0a03a34f234a92eff0e2c68dbin0 -> 286 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ba/cac9b3493509aa15e1730e1545fc0919d1dae0bin0 -> 29 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/bc/744705e1d8a019993cf88f62bc4020f1b809192
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/bc/85d1aad435ff3705a8c30ace85f7542c5736cbbin0 -> 104 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/bc/95c75d59386147d1e79a87c33068d8dbfd71f2bin0 -> 348 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/bd/593285fc7fe4ca18ccdbabf027f5d689101452bin0 -> 159 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/bd/867fbae2faa80b920b002b80b1c91bcade7784bin0 -> 48 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/bd/9cb4cd0a770cb9adcb5fce212142ef40ea1c35bin0 -> 51 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/be/f6e37b3ee632ba74159168836f382fed21d77d2
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c0/6a9be584ac49aa02c5551312d9e2982c91df10bin0 -> 348 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c1/b17981db0840109a820dae8674ee29684134ffbin0 -> 348 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c1/b6a51bbb87c2f82b161412c3d20b59fc69b090bin0 -> 47 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c3/5dee9bcc0e989f3b0c40f68372a9a51b6c4e6abin0 -> 162 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c3/d02eeef75183df7584d8d13ac03053910c1301bin0 -> 67 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c4/efe31e9decccc8b2b4d3df9aac2cdfe2995618bin0 -> 538 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c5/0d0f1cb60b8b0fe1615ad20ace557e9d68d7bd1
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c5/bbe550b9f09444bdddd3ecf3d97c0b42aa786cbin0 -> 269 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c6/07fc30883e335def28cd686b51f6cfa02b06ec2
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c6/92ecf62007c0ac9fb26e2aa884de2933de15edbin0 -> 40 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c8/26ef8b17b5cd2c4a0023f265f3a423b3aa0388bin0 -> 157 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c8/f06f2e3bb2964174677e91f0abead0e43c9e5dbin0 -> 45 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c9/174cef549ec94ecbc43ef03cdc775b4950becb2
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/c9/4b27e41064c521120627e07e2035cca1d24ffabin0 -> 162 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ca/b2cf23998b40f1af2d9d9a756dc9e285a8df4bbin0 -> 40 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ca/ff6b7d44973f53e3e0cf31d0d695188b19aec6bin0 -> 54 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/cb/491780d82e46dc88a065b965ab307a038f2bc2bin0 -> 163 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/cb/6693a788715b82440a54e0eacd19ba9f6ec559bin0 -> 41 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/cc/338e4710c9b257106b8d16d82f86458d5beaf12
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/cc/3e3009134cb88014129fc8858d1101359e5e2f2
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/cd/3e8d4aa06bdc781f264171030bc28f2b370feebin0 -> 94 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ce/8860d49e3bea6fd745874a01b7c3e46da8cbc3bin0 -> 48 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ce/e656c392ad0557b3aae0fb411475c206e2926fbin0 -> 32 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/cf/8c5cc8a85a1ff5a4ba51e0bc7cf5665669924dbin0 -> 29 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d0/7ec190c306ec690bac349e87d01c4358e49bb22
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d0/d4594e16f2e19107e3fa7ea63e7aaaff305ffbbin0 -> 51 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d2/f8637f2eab2507a1e13cbc9df4729ec386627ebin0 -> 268 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d3/3cedf513c059e0515653fa2c2e386631387a05bin0 -> 46 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d3/719a5ae8e4d92276b5313ce976f6ee5af2b4362
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d3/7aa3bbfe1c0c49b909781251b956dbabe85f96bin0 -> 80 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d3/7ad72a2052685fc6201c2af90103ad42d2079bbin0 -> 233 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d4/207f77243500bec335ab477f9227fcdb1e271a2
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46bin0 -> 38 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d5/093787ef302b941b6aab081b99fb4880038bd8bin0 -> 30 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d5/a61b0b4992a4f0caa887fa08b52431e727bb6fbin0 -> 81 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d5/b6fc965c926a1bfc9ee456042b94088b5c5d21bin0 -> 319 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d5/ec1152fe25e9fec00189eb00b3db71db24c218bin0 -> 24 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d6/42b9770c66bba94a08df09b5efb095001f76d7bin0 -> 539 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d6/462fa3f5292857db599c54aea2bf91616230c5bin0 -> 48 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d6/cf6c7741b3316826af1314042550c97ded1d502
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d7/308cc367b2cc23f710834ec1fd8ffbacf1b4601
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d8/74671ef5b20184836cb983bb273e5280384d0bbin0 -> 162 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d8/dec75ff2f8b41d1c5bfef0cd57b7300c834f66bin0 -> 164 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d8/fa77b6833082c1ea36b7828a582d4c438824501
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/d9/63979c237d08b6ba39062ee7bf64c7d34a27f8bin0 -> 48 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/da/178208145ef585a1bd5ca5f4c9785d738df2cfbin0 -> 41 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/db/6261a7c65c7fd678520c9bb6f2c47582ab9ed5bin0 -> 624 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/dd/2ae5ab264e5592aa754235d5ad5eac8f0ecdfdbin0 -> 149 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/dd/9a570c3400e6e07bc4d7651d6e20b08926b3d9bin0 -> 36 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/de/872ee3618b894992e9d1e18ba2ebe256a112f91
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/df/e3f22baa1f6fce5447901c3086bae368de6bddbin0 -> 40 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e0/67f9361140f19391472df8a82d6610813c73b7bin0 -> 53 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e1/129b3cfb5898e0fbd606e0cb80b2755e50d161bin0 -> 92 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e1/7ace1492648c9dc5701bad5c47af9d1b60c4e9bin0 -> 264 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e2/6b8888956137218d8589368a3e606cf50fbb56bin0 -> 105 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e2/c6abbd55fed5ac71a5f2751e29b4a34726a5951
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e3/1e7ad3ed298f24e383c4950f4671993ec078e4bin0 -> 210 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e3/76fbdd06ebf021c92724da9f26f44212734e3e3
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e4/9f917b448d1340b31d76e54ba388268fd4c922bin0 -> 36 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e4/f618a2c3ed0669308735727df5ebf2447f022f2
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e5/060729746ca9888239cba08fdcf4bee907b406bin0 -> 24 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e6/5a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812bin0 -> 160 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391bin0 -> 15 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e8/107f24196736b870a318a0e28f048e29f6feff3
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e9/2cdb7017dc6c5aed25cb4202c5b0104b872246bin0 -> 48 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e9/ad6ec3e38364a3d07feda7c4197d4d845c53b5bin0 -> 36 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/e9/f48beccc62d535739bfbdebe0a55ed716d8366bin0 -> 382 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/eb/c09d0137cfb0c26697aed0109fb943ad906f3fbin0 -> 166 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ec/67e5a86adff465359f1c8f995e12dbdfa08d8abin0 -> 166 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ed/9523e62e453e50dd9be1606af19399b96e397abin0 -> 87 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ee/1d6f164893c1866a323f072eeed36b855656bebin0 -> 291 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccfbin0 -> 64 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ee/a9286df54245fea72c5b557291470eb825f38fbin0 -> 235 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ef/58fdd8086c243bdc81f99e379acacfd21d32d62
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ef/c499524cf105d5264ac7fc54e07e95764e8075bin0 -> 32 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ef/c9121fdedaf08ba180b53ebfbcf71bd488ed09bin0 -> 160 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f0/053b8060bb3f0be5cbcc3147a07ece26bf097ebin0 -> 163 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f0/ce2b8e4986084d9b308fb72709e414c23eb5e6bin0 -> 125 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f2/0c9063fa0bda9a397c96947a7b687305c49753bin0 -> 29 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f2/9e7fb590551095230c6149cbe72f2e9104a796bin0 -> 41 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f2/e1550a0c9e53d5811175864a29536642ae3821bin0 -> 73 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f3/293571dcd708b6a3faf03818cd2844d000e1981
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f3/f1164b68b57b1995b658a828320e6df3081faebin0 -> 310 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f4/15caf3fcad16304cb424b67f0ee6b12dc03aaebin0 -> 320 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f4/8097eb340dc5a7cae55aabcf1faf4548aa821fbin0 -> 165 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f5/504f36e6f4eb797a56fc5bac6c6c7f32969bf2bin0 -> 42 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f5/b50c85a87cac64d7eb3254cdd1aec9564c0293bin0 -> 35 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f5/f9dd5886a6ee20272be0aafc790cba43b31931bin0 -> 244 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f6/65b45cde9b568009c6e6b7b568e89cfe717df8bin0 -> 132 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f6/be049e284c0f9dcbbc745543885be3502ea521bin0 -> 265 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f7/c332bd4d4d4b777366cae4d24d1687477576bfbin0 -> 156 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f8/958bdf4d365a84a9a178b1f5f35ff1dacbd8842
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/f9/7da95f156121bea8f978623628f4cbdbf30b361
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/fa/c03f2c5139618d87d53614c153823bf1f31396bin0 -> 76 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/fa/da9356aa3f74622327a3038ae9c6f92e1c5c1dbin0 -> 168 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/fb/738a106cfd097a4acb96ce132ecb1ad6c46b03bin0 -> 264 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/fc/4c636d6515e9e261f9260dbcf3cc6eca97ea08bin0 -> 29 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/fc/7d7b805f7a9428574f4f802b2e34cd20ab9d99bin0 -> 575 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/fc/90237dc4891fa6c69827fc465632225e391618bin0 -> 163 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/fd/57d2d6770fad8e9959124793a17f441b571e66bin0 -> 279 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/fd/89f8cffb663ac89095a0f9764902e93ceaca6a2
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/fe/5407fc50a53aecb41d1a6e9ea7b612e581af87bin0 -> 48 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ff/49d07869831ad761bbdaea026086f8789bcb00bin0 -> 24 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/objects/ff/b312248d607284c290023f9502eea010d34efdbin0 -> 68 bytes
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/branch1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/delete-submodule1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/df_ancestor1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/df_side11
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/df_side21
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/ff_branch1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/master1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/octo11
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/octo21
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/octo31
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/octo41
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/octo51
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/octo61
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/previous1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ancestor1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ours1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_theirs1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/renames11
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/renames21
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/submodule_rename11
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/submodule_rename21
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/submodules1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/submodules-branch1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/submodules-branch21
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-101
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-10-branch1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-111
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-11-branch1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-131
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-13-branch1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-141
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-14-branch1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-2alt1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-2alt-branch1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-3alt1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-3alt-branch1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-41
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-4-branch1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-11
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1-branch1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-21
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2-branch1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-61
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-6-branch1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-71
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-7-branch1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-81
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-8-branch1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-91
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/trivial-9-branch1
-rw-r--r--tests/resources/merge-resolve/.gitted/refs/heads/unrelated1
-rw-r--r--tests/resources/merge-resolve/added-in-master.txt1
-rw-r--r--tests/resources/merge-resolve/automergeable.txt9
-rw-r--r--tests/resources/merge-resolve/changed-in-branch.txt1
-rw-r--r--tests/resources/merge-resolve/changed-in-master.txt1
-rw-r--r--tests/resources/merge-resolve/conflicting.txt1
-rw-r--r--tests/resources/merge-resolve/removed-in-branch.txt1
-rw-r--r--tests/resources/merge-resolve/unchanged.txt1
-rw-r--r--tests/resources/merge-whitespace/.gitted/HEAD1
-rw-r--r--tests/resources/merge-whitespace/.gitted/config7
-rw-r--r--tests/resources/merge-whitespace/.gitted/indexbin0 -> 137 bytes
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/01/bd650462136a4f0a266dfc91ab93b3fef0f7cbbin0 -> 49 bytes
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/08/3f868fb4324e32a4999173b2437b31d7a1ef25bin0 -> 53 bytes
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/0a/a2acaa63cacc7a99fab0c2ce3d56572911df191
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/11/89e10a62aadf2fea8cd018afb52c1980f40b4fbin0 -> 183 bytes
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/24/2c8f6cf388e96e2c12b6e49cb7ae60167cba1ebin0 -> 50 bytes
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/25/246acb001858ffeffb03ea399fd2c0a163b832bin0 -> 53 bytes
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/26/2f67de0de2e535a59ae1bc3c739601e98c354dbin0 -> 38 bytes
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/2f/6727d2e570bf962d9dd926423cf6fe5072071abin0 -> 169 bytes
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/3c/43e7fc2a56fc825c31dfee65abd6dda8d16dcabin0 -> 52 bytes
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/40/26a6c83f39c56881c9ac62e7582db9e3d33a4fbin0 -> 40 bytes
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/42/dabb8d5dba2de103815a77e4369bb3966e64efbin0 -> 138 bytes
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/43/9230587f2eb38e9540a5c99e9831f65641eab9bin0 -> 214 bytes
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/43/ad73e75e15f03bb0b4398a48a57ecfc20788e2bin0 -> 53 bytes
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904bin0 -> 15 bytes
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/54/74989173042512ab630191ad71cdcedb646b9abin0 -> 53 bytes
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/5e/fb9bc29c482e023e40e0a2b3b7e49cec842034bin0 -> 53 bytes
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/70/d3d2e7d51a18fcc6f035a67e5c3f33069be04dbin0 -> 53 bytes
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/74/e83b6c5df14f1fba7c4ea1f99c6d007b591002bin0 -> 43 bytes
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/77/f40c621ceae77ad8d756ef507bdbafe2713aa7bin0 -> 53 bytes
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/9c/5362069759fb37ae036cef6e4b2f95c6c5eaabbin0 -> 184 bytes
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/a2/9e7dabd68dfb38a717e6b1648713cd5c7adee2bin0 -> 53 bytes
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/a4/e6a86e07ef5afe036e26602fbbaa27496d00a92
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/a8/27eab4fd66ab37a6ebcfaa7b7e341abfd55947bin0 -> 51 bytes
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/a9/66acc271e50b5d4595911752a77def0a5e5d40bin0 -> 132 bytes
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/b2/a69114f4897109fedf1aafea363cb2d2557029bin0 -> 178 bytes
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/bc/83ac0422ba1082c80e406234910377984cfbb6bin0 -> 137 bytes
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/bf/e4ea5805af22a5b194259bda6f5f634486f8911
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/c3/b1fb31424c98072542cc8e42b48c92e52f494abin0 -> 39 bytes
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/c7/e2f386736445936f5ba181269a0e0967e280e82
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/d9/5182053c31f8aa09df4fa225f4e668c5320b595
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391bin0 -> 15 bytes
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/ec/5a35c75b8d3ee29bed37996b14e909d04fdceebin0 -> 52 bytes
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/ee/3c2aac8e03224c323b58ecb1f9eef616745467bin0 -> 38 bytes
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/ef/e94a4bf4e697f7f0270f0d1b8a93af784a19d0bin0 -> 72 bytes
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/f0/0c965d8307308469e537302baa73048488f162bin0 -> 37 bytes
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/f1/90a0d111ca1688778657798743ddfb4ed4bd642
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/f4/9b2c244e9d3b0647fdfb95954c38fbfeecf3ad2
-rw-r--r--tests/resources/merge-whitespace/.gitted/objects/f8/7905f99f0e66d179a8379d8ca4d8cbbd32c2311
-rw-r--r--tests/resources/merge-whitespace/.gitted/refs/heads/branch_a_change1
-rw-r--r--tests/resources/merge-whitespace/.gitted/refs/heads/branch_a_eol1
-rw-r--r--tests/resources/merge-whitespace/.gitted/refs/heads/branch_b_change1
-rw-r--r--tests/resources/merge-whitespace/.gitted/refs/heads/branch_b_eol1
-rw-r--r--tests/resources/merge-whitespace/.gitted/refs/heads/master1
-rw-r--r--tests/resources/merge-whitespace/test.txt11
-rw-r--r--tests/resources/mergedrepo/.gitted/COMMIT_EDITMSG1
-rw-r--r--tests/resources/mergedrepo/.gitted/HEAD1
-rw-r--r--tests/resources/mergedrepo/.gitted/MERGE_HEAD1
-rw-r--r--tests/resources/mergedrepo/.gitted/MERGE_MODE0
-rw-r--r--tests/resources/mergedrepo/.gitted/MERGE_MSG5
-rw-r--r--tests/resources/mergedrepo/.gitted/ORIG_HEAD1
-rw-r--r--tests/resources/mergedrepo/.gitted/config6
-rw-r--r--tests/resources/mergedrepo/.gitted/description1
-rw-r--r--tests/resources/mergedrepo/.gitted/indexbin0 -> 842 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/info/exclude6
-rw-r--r--tests/resources/mergedrepo/.gitted/logs/HEAD5
-rw-r--r--tests/resources/mergedrepo/.gitted/logs/refs/heads/branch2
-rw-r--r--tests/resources/mergedrepo/.gitted/logs/refs/heads/master2
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/03/db1d37504ca0c4f7c26d7776b0e28bdea08712bin0 -> 141 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/17/0efc1023e0ed2390150bb4469c8456b63e8f91bin0 -> 141 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/1f/85ca51b8e0aac893a621b61a9c2661d6aa6d81bin0 -> 34 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/22/0bd62631c8cf7a83ef39c6b94595f00517211ebin0 -> 42 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/32/d55d59265db86dd690f0a7fc563db43e2bc6a6bin0 -> 159 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/38/e2d82b9065a237904af4b780b4d68da6950534bin0 -> 74 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/3a/34580a35add43a4cf361e8e9a30060a905c8762
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/44/58b8bc9e72b6c8755ae456f60e9844d0538d8cbin0 -> 39 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/47/8871385b9cd03908c5383acfd568bef023c6b3bin0 -> 36 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/51/6bd85f78061e09ccc714561d7b504672cb52dabin0 -> 36 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/53/c1d95a01f4514b162066fc98564500c96c46adbin0 -> 45 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/6a/ea5f295304c36144ad6e9247a291b7f8112399bin0 -> 49 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/70/68e30a7f0090ae32db35dfa1e4189d8780fcb8bin0 -> 85 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/75/938de1e367098b3e9a7b1ec3c4ac4548afffe4bin0 -> 41 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/7b/26923aaf452b1977eb08617c59475fb3f74b71bin0 -> 41 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/84/af62840be1b1c47b778a8a249f3ff45155038cbin0 -> 40 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/88/71f7a2ee3addfc4ba39fbd0783c8e738d04cdabin0 -> 66 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/88/7b153b165d32409c70163e0f734c090f12f673bin0 -> 38 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/8a/ad34cc83733590e74b93d0f7cf00375e2a735abin0 -> 78 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/8b/3f43d2402825c200f835ca1762413e386fd0b2bin0 -> 57 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/8b/72416545c7e761b64cecad4f1686eae4078aa8bin0 -> 38 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/8f/3c06cff9a83757cec40c80bc9bf31a2582bde9bin0 -> 39 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/8f/fcc405925511824a2240a6d3686aa7f8c7ac50bin0 -> 140 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/9a/05ccb4e0f948de03128e095f39dae6976751c51
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/9d/81f82fccc7dcd7de7a1ffead1815294c2e092cbin0 -> 36 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/b7/cedb8ad4cbb22b6363f9578cbd749797f7ef0dbin0 -> 66 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/d0/1885ea594926eae9ba5b54ad76692af5969f51bin0 -> 55 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/e2/809157a7766f272e4cfe26e61ef2678a5357ff3
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/e6/2cac5c88b9928f2695b934c70efa4285324478bin0 -> 87 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/objects/f7/2784290c151092abf04ce6b875068547f70406bin0 -> 141 bytes
-rw-r--r--tests/resources/mergedrepo/.gitted/refs/heads/branch1
-rw-r--r--tests/resources/mergedrepo/.gitted/refs/heads/master1
-rw-r--r--tests/resources/mergedrepo/conflicts-one.txt5
-rw-r--r--tests/resources/mergedrepo/conflicts-two.txt5
-rw-r--r--tests/resources/mergedrepo/one.txt10
-rw-r--r--tests/resources/mergedrepo/two.txt12
-rw-r--r--tests/resources/namespace.git/COMMIT_EDITMSG1
-rw-r--r--tests/resources/namespace.git/HEAD1
-rw-r--r--tests/resources/namespace.git/config7
-rw-r--r--tests/resources/namespace.git/description1
-rw-r--r--tests/resources/namespace.git/indexbin0 -> 321 bytes
-rw-r--r--tests/resources/namespace.git/info/exclude6
-rw-r--r--tests/resources/namespace.git/logs/HEAD9
-rw-r--r--tests/resources/namespace.git/logs/refs/heads/branch3
-rw-r--r--tests/resources/namespace.git/logs/refs/heads/four2
-rw-r--r--tests/resources/namespace.git/logs/refs/heads/main2
-rw-r--r--tests/resources/namespace.git/logs/refs/heads/one1
-rw-r--r--tests/resources/namespace.git/objects/04/433ff5b52d6ad534fd6288de4a57b81cc12188bin0 -> 160 bytes
-rw-r--r--tests/resources/namespace.git/objects/0a/890bd10328d68f6d85efd2535e3a4c588ee8e6bin0 -> 54 bytes
-rw-r--r--tests/resources/namespace.git/objects/10/fcb1c85bd6b3bc6f43e0a3932ff5859121a84ebin0 -> 54 bytes
-rw-r--r--tests/resources/namespace.git/objects/24/bbdca8b223aaa3384d78312f730c58492aa30abin0 -> 54 bytes
-rw-r--r--tests/resources/namespace.git/objects/27/0c611ee72c567bc1b2abec4cbc345bab9f15babin0 -> 30 bytes
-rw-r--r--tests/resources/namespace.git/objects/2b/df67abb163a4ffb2d7f3f0880c9fe5068ce782bin0 -> 21 bytes
-rw-r--r--tests/resources/namespace.git/objects/3d/669d1b33ec8add4609d8043d025527db4989eb2
-rw-r--r--tests/resources/namespace.git/objects/42/0d51ce75a87909e29659da2072ffd3d5daf5b7bin0 -> 128 bytes
-rw-r--r--tests/resources/namespace.git/objects/56/26abf0f72e58d7a153368ba57db4c673c0e171bin0 -> 19 bytes
-rw-r--r--tests/resources/namespace.git/objects/56/300b5eae653453102ac1213e921973c066425bbin0 -> 95 bytes
-rw-r--r--tests/resources/namespace.git/objects/7e/eaa70d7c5592db920a2e107ce3918bd4c8a4252
-rw-r--r--tests/resources/namespace.git/objects/85/10665149157c2bc901848c3e0b746954e9cbd9bin0 -> 20 bytes
-rw-r--r--tests/resources/namespace.git/objects/9e/bfa6bdc9d38075d29d26aa5df89b1cf635b269bin0 -> 137 bytes
-rw-r--r--tests/resources/namespace.git/objects/9e/f15e3c5c0c8c6913936f843ad967cbe5541f0dbin0 -> 121 bytes
-rw-r--r--tests/resources/namespace.git/objects/af/5626b4a114abcb82d63db7c8082c3c4756e51bbin0 -> 30 bytes
-rw-r--r--tests/resources/namespace.git/objects/af/81e4bd99cbfe6f05a501f1e4c82db2bf803e02bin0 -> 30 bytes
-rw-r--r--tests/resources/namespace.git/objects/bf/d17f429f4e2d121769213171ad57ca2e5173f92
-rw-r--r--tests/resources/namespace.git/objects/ec/947e3dd7a7752d078f1ed0cfde7457b21fef58bin0 -> 54 bytes
-rw-r--r--tests/resources/namespace.git/objects/f7/19efd430d52bcfc8566a43b2eb655688d38871bin0 -> 19 bytes
-rw-r--r--tests/resources/namespace.git/objects/f7/5ba05f340c51065cbea2e1fdbfe5fe13144c97bin0 -> 30 bytes
-rw-r--r--tests/resources/namespace.git/refs/heads/branch1
-rw-r--r--tests/resources/namespace.git/refs/heads/main1
-rw-r--r--tests/resources/namespace.git/refs/namespaces/name1/refs/heads/four1
-rw-r--r--tests/resources/namespace.git/refs/namespaces/name1/refs/heads/one1
-rw-r--r--tests/resources/nasty/.gitted/HEAD1
-rw-r--r--tests/resources/nasty/.gitted/indexbin0 -> 120 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/02/28b21d477f67b9f7720565da9e760b84c8b85b3
-rw-r--r--tests/resources/nasty/.gitted/objects/04/18f28a75dc0c4951c01842e0d794843a88178abin0 -> 46 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/04/fab819d8388295cbe3496310e4e53ef8f4a115bin0 -> 49 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/05/1229bf9d30ec923052ff42db8069ccdc17159dbin0 -> 53 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/07/f9d4d85b75187e4db5b9cbcad3e6218582bd57bin0 -> 93 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/09/9ed86cb8501ae483b1855c351fe1a506ac9631bin0 -> 133 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/0a/78e40e54cc471c0415ca0680550f242e7843e2bin0 -> 51 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/0b/8206dd72a3b3b932fb562f92d29199b9398390bin0 -> 50 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/0d/45fb57852c2229346a800bd3fc58e32527a21cbin0 -> 45 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/10/cb44a89d1a9e8bf74de3f11a2a61ee833f13b1bin0 -> 50 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/11/9f6cd3535de0e2a15654947a7b1a5affbf1406bin0 -> 50 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/12/12c12915820e1ad523b6305c0dcdefea8b7e971
-rw-r--r--tests/resources/nasty/.gitted/objects/13/e5f8be09e8b7db074fb39b96e08215cc4a36f1bin0 -> 56 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/14/e70ab559b4c6a8a6fc9b6f538bd1f3934be725bin0 -> 48 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/15/f7d9f9514eeb65b9588c49b10b1da145a729a22
-rw-r--r--tests/resources/nasty/.gitted/objects/16/35c47d80914f0abfa43dd4234a948db5bdb1072
-rw-r--r--tests/resources/nasty/.gitted/objects/16/a701796bc3670e5c2fdaeccb7f1280c60b373fbin0 -> 62 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/19/1381ee74dec49c89f99a62d055cb1058ba0de9bin0 -> 19 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/1e/3c845808fa5883aa4bcf2f882172edb72a7a322
-rw-r--r--tests/resources/nasty/.gitted/objects/24/676d5e93f9fa7b568f38d7bce01772908e982bbin0 -> 47 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/26/b665c162f67acae67779445f3c7b9782b0a6d71
-rw-r--r--tests/resources/nasty/.gitted/objects/27/db66b046536a0e4f64c4f8c3a490641c3fa5e5bin0 -> 44 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/2a/9eb82c733e31ae312cee349084dcbc6f69639abin0 -> 26 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/2b/4b774d8c5441b22786531f34ffc77800cda8cfbin0 -> 50 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/2d/23d51590ec2f53fe4b5bb3e5ca62e35e4ef85abin0 -> 49 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/33/8190107c7ee7d8f5aa30061fc19b7d5ddcda86bin0 -> 55 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/35/ae236308929a536fb4e852278a9b98c42babb31
-rw-r--r--tests/resources/nasty/.gitted/objects/38/0b9e58872ccf1d858be4b0fc612514a080bc40bin0 -> 49 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/39/fb3af508440cf970b92767f6d081c811574d2a2
-rw-r--r--tests/resources/nasty/.gitted/objects/3b/24e5c751ee9c7c89df32a0d959748aa3d0112c2
-rw-r--r--tests/resources/nasty/.gitted/objects/42/1376db9e8aee847e9d774891e73098a7415e94bin0 -> 21 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/44/14ac920acabc3eb00e3cf9375eeb0cb6859c15bin0 -> 135 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/44/2894787eddb1e84a952f17a027590e2c6c02cdbin0 -> 137 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/46/fe10fa23259b089ab050788b06df979cd7d054bin0 -> 137 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/4a/a347c8bb0456230f43f34833c97b9f52c40f623
-rw-r--r--tests/resources/nasty/.gitted/objects/4d/83272d0d372e1232ddc4ff3260d76fdfa2015a2
-rw-r--r--tests/resources/nasty/.gitted/objects/53/41a7b545d71198b076b8ba3374a75c9a2906403
-rw-r--r--tests/resources/nasty/.gitted/objects/5d/1ee4f24f66dcd62a30248588d33804656b2073bin0 -> 46 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/65/94bdbad86bbc8d3ed0806a23827203fbab56c6bin0 -> 132 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/68/e8bce48725490c376d57ebc60f0170605951a5bin0 -> 58 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/69/7dc3d723a018538eb819d5db2035c15109af73bin0 -> 132 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/6b/7d8a5a48a3c753b75a8fe5196f9c8704ac64adbin0 -> 50 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/6c/1f5f6fec515d33036b44c596bfae28fc460cbabin0 -> 47 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/71/2ceb8eb3e57072447715bc4057c57aa50f629abin0 -> 138 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/7a/0538bc4e20aecb36ef221f2077eb30ebe0bcb22
-rw-r--r--tests/resources/nasty/.gitted/objects/7a/e174dda8f105a582c593b52d74545a3565819dbin0 -> 51 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/7b/b1dd08b2c7d73084934954e4196e67004b0279bin0 -> 83 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/7d/4e382485ace068fb83b768ba1a1c674afbdc1dbin0 -> 62 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/7f/924ca37670afa06c7a481a2487b728b2c0185abin0 -> 47 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/80/24458e7ee49c456fd8c45d3591e9936bf613b3bin0 -> 50 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/80/a8fe4f10626c50b3a4fd065a4604bafc9f30fabin0 -> 23 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/81/e2b84864f16ebd285b34a2b1e87ebb41f4c230bin0 -> 49 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/82/482ad2e683edfc14f7de359e4f9a5e88909c51bin0 -> 45 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/88/6c0f5f71057d846f71f05a05fdffad332bc070bin0 -> 50 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/89/9ff28744bed5bece69c78ba752c7dc3e954629bin0 -> 136 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/8b/cbb6e0c0f9554efd5401e1ec14a4b2595eb3bf2
-rw-r--r--tests/resources/nasty/.gitted/objects/8c/e7a3ef59c3d602a0296321eb964218f3d52faebin0 -> 56 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/8f/1dcd43aa0164eb6ec319c3ec8879ca5cf62c1e2
-rw-r--r--tests/resources/nasty/.gitted/objects/91/602c85bb50dd834205edd30435b77d5bb9ccf03
-rw-r--r--tests/resources/nasty/.gitted/objects/91/cd2c95af92883550b45fcc838013ae7e2954dfbin0 -> 138 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/94/f37c29173c8fa45a232b17e745c82132b2fafdbin0 -> 132 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/96/156716851c0afb4702b0d2c4ac8c496a730e291
-rw-r--r--tests/resources/nasty/.gitted/objects/96/3fdf003bf7261b9155c5748dc0945349b69e68bin0 -> 44 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/97/c14994866466aeb73e769a6f34e07c7f4b53f7bin0 -> 65 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/9a/b85e507899c19dca57778c9b6e5f1ec799b9113
-rw-r--r--tests/resources/nasty/.gitted/objects/9d/5898503adc01d763e279ac8fcefbe865b190314
-rw-r--r--tests/resources/nasty/.gitted/objects/9e/24726d64589ba02430da8cebb5712dad35593dbin0 -> 136 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/9e/683cdaf9ea2727c891b4cf8f7f11e9e28a67cabin0 -> 50 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/a0/d89aa95628fcd6b64fd5b23dd56b906b06bfe2bin0 -> 166 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/a5/76a98d3279989226992610372035b76a01a3e9bin0 -> 136 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/a7/8dde970cffbb71d67bef2a74aa72c6621d9819bin0 -> 86 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/ac/84d85a425b2a21fd0ffccacac6c48823fc98c8bin0 -> 48 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/af/45aa1eb7edf804ed10f70efb96fd178527c17cbin0 -> 58 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/b1/1df9aee97a65817e8904a74f5e6a1c62c7a275bin0 -> 50 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/b8/3795b1e0eb54f22f7056119db132500d0cdc05bin0 -> 56 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/b8/edf3ad62dbcbc983857a5bfee7b0181ee1a513bin0 -> 135 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/bb/29ec85546d29b0bcc314242660d7772b0a3803bin0 -> 50 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/bc/e2dabe5766838216d95f199d95aa4fd479a084bin0 -> 83 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/bf/7ab4723fcc57ecc7fceccf591d6c47734915692
-rw-r--r--tests/resources/nasty/.gitted/objects/c2/a2ddd339574e5cbfd9228be840eb1bf496de4ebin0 -> 137 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/c3/a70f8a376f17adccfb52b48e2831bfef2a21722
-rw-r--r--tests/resources/nasty/.gitted/objects/c4/89e70ed6d9f6331770eae21a77d15afd11cd99bin0 -> 56 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/c6/72414d4d08111145ef8202f21c95fa7e688aeebin0 -> 56 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/c8/f98a1762ec016c30f0d73512df399dedefc3fd3
-rw-r--r--tests/resources/nasty/.gitted/objects/cc/bbfdb796f9b03298f5c7225e8f830784e1a3b12
-rw-r--r--tests/resources/nasty/.gitted/objects/cd/44b4ea1066b3fa1d4b3baad8dc1531aec287a6bin0 -> 47 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/ce/22b3cd9a01efafc370879c1938e0c32fb6f1953
-rw-r--r--tests/resources/nasty/.gitted/objects/cf/6fcf8cdf7e8d4cda3b11b0ba02d0d5125fbbd72
-rw-r--r--tests/resources/nasty/.gitted/objects/d2/eb26d4938550487de59a017a7bfee8ca46b5f42
-rw-r--r--tests/resources/nasty/.gitted/objects/dc/37c5f1521fb76fe1c1ac7b13187f9396a59247bin0 -> 58 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/de/bdc4a004fda6141a17d9c297617be70d40248f2
-rw-r--r--tests/resources/nasty/.gitted/objects/e2/377bdbc93b30a34ed5deefedded89b947ff8f42
-rw-r--r--tests/resources/nasty/.gitted/objects/e3/0b60b120761f44ebd0f0a7b0e9445ce8e11d68bin0 -> 146 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/e3/99c4fc4c07cb7947d2f3d966bc374df6ccc6912
-rw-r--r--tests/resources/nasty/.gitted/objects/e4/edb361e51932b5ccedbc7ee41b4d3a4289aecebin0 -> 50 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/e5/1c3fa44fe981ec290c8f47fea736f3ff2af2a6bin0 -> 51 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/e7/3a04f71f11ab9d7dde72ff793882757a03f16ebin0 -> 50 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/e8/68b1d6833710021785581a9e11dba8468f3a55bin0 -> 49 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/e8/7caf56c91ab8d14e4ee8eb56308533503d18852
-rw-r--r--tests/resources/nasty/.gitted/objects/eb/82bf596b66f90e25f881ce9b92cb55bab4fdf5bin0 -> 50 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/ed/4bc023f61dc345ff0084b922b229d24de206e7bin0 -> 47 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/ef/6ed8a2b15f95795aed82a974b995cace02dbfebin0 -> 43 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/f2/c059dab35f6534b3f16d90b2f1de308615320cbin0 -> 50 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/fa/9cfdbeaaf3a91ff4b84d74412cd59d9b16a615bin0 -> 136 bytes
-rw-r--r--tests/resources/nasty/.gitted/objects/fd/7a37d92197267e55e1fc0cc4f283a815bd79b8bin0 -> 43 bytes
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dot_backslash_dotcapitalgit_path1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dot_dotcapitalgit_path1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dot_dotgit_path1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dot_dotgit_tree1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dot_git_colon1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dot_git_colon_stuff1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dot_git_dot1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dot_path1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dot_path_two1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dot_tree1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_backslash_path1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_path1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_tree1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotdot_dotcapitalgit_path1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotdot_dotgit_path1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotdot_dotgit_tree1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotdot_path1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotdot_tree1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_alternate_data_stream1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_backslash_path1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_11
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_101
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_111
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_121
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_131
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_141
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_151
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_161
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_21
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_31
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_41
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_51
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_61
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_71
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_81
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_91
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_path1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/dotgit_tree1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/git_tilde11
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/git_tilde21
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/git_tilde31
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/gitmodules-symlink1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/master1
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/symlink11
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/symlink21
-rw-r--r--tests/resources/nasty/.gitted/refs/heads/symlink31
-rw-r--r--tests/resources/nsecs/.gitted/HEAD1
-rw-r--r--tests/resources/nsecs/.gitted/config8
-rw-r--r--tests/resources/nsecs/.gitted/indexbin0 -> 281 bytes
-rw-r--r--tests/resources/nsecs/.gitted/objects/03/1986a8372d1442cfe9e3b54906a9aadc524a7e2
-rw-r--r--tests/resources/nsecs/.gitted/objects/03/9afd91c98f82c14e425bb6796d8ca98e9c8cacbin0 -> 102 bytes
-rw-r--r--tests/resources/nsecs/.gitted/objects/6d/8b18077cc99abd8dda05a6062c646406abb2d4bin0 -> 22 bytes
-rw-r--r--tests/resources/nsecs/.gitted/objects/c5/12b6c64656b87ea8caf37a32bc5a562d797745bin0 -> 22 bytes
-rw-r--r--tests/resources/nsecs/.gitted/objects/df/78d3d51c369e1d2f1eadb73464aadd931d56b4bin0 -> 22 bytes
-rw-r--r--tests/resources/nsecs/.gitted/refs/heads/master1
-rw-r--r--tests/resources/nsecs/a.txt1
-rw-r--r--tests/resources/nsecs/b.txt1
-rw-r--r--tests/resources/nsecs/c.txt1
-rw-r--r--tests/resources/packfile-sha256/pack-b4a043c0ec5e079e8ac67d823776d752efc71661592db317474a0cf292915f31.idxbin0 -> 1376 bytes
-rw-r--r--tests/resources/packfile-sha256/pack-b4a043c0ec5e079e8ac67d823776d752efc71661592db317474a0cf292915f31.packbin0 -> 1406 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/HEAD1
-rw-r--r--tests/resources/partial-testrepo/.gitted/config7
-rw-r--r--tests/resources/partial-testrepo/.gitted/indexbin0 -> 328 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08bin0 -> 19 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0ebin0 -> 163 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925bin0 -> 147 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7bin0 -> 51 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccdbin0 -> 119 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057bin0 -> 18 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd20452
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63bin0 -> 50 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf36442
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dccbin0 -> 50 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735bin0 -> 19 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60abin0 -> 119 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344dbin0 -> 82 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479bin0 -> 126 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a3
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f2
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bdbin0 -> 28 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6bin0 -> 26 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd3
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9bin0 -> 162 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83bin0 -> 147 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1bin0 -> 82 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92bin0 -> 24 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765bin0 -> 82 bytes
-rw-r--r--tests/resources/partial-testrepo/.gitted/objects/pack/.gitkeep0
-rw-r--r--tests/resources/partial-testrepo/.gitted/refs/heads/dir1
-rw-r--r--tests/resources/peeled.git/HEAD1
-rw-r--r--tests/resources/peeled.git/config8
-rw-r--r--tests/resources/peeled.git/objects/info/packs2
-rw-r--r--tests/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.idxbin0 -> 1156 bytes
-rw-r--r--tests/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.packbin0 -> 274 bytes
-rw-r--r--tests/resources/peeled.git/packed-refs6
-rw-r--r--tests/resources/peeled.git/refs/heads/master1
-rw-r--r--tests/resources/push.sh55
-rw-r--r--tests/resources/push_src/.gitted/COMMIT_EDITMSG1
-rw-r--r--tests/resources/push_src/.gitted/HEAD1
-rw-r--r--tests/resources/push_src/.gitted/ORIG_HEAD1
-rw-r--r--tests/resources/push_src/.gitted/config10
-rw-r--r--tests/resources/push_src/.gitted/description1
-rw-r--r--tests/resources/push_src/.gitted/indexbin0 -> 470 bytes
-rw-r--r--tests/resources/push_src/.gitted/info/exclude6
-rw-r--r--tests/resources/push_src/.gitted/logs/HEAD10
-rw-r--r--tests/resources/push_src/.gitted/logs/refs/heads/b11
-rw-r--r--tests/resources/push_src/.gitted/logs/refs/heads/b21
-rw-r--r--tests/resources/push_src/.gitted/logs/refs/heads/b32
-rw-r--r--tests/resources/push_src/.gitted/logs/refs/heads/b42
-rw-r--r--tests/resources/push_src/.gitted/logs/refs/heads/b52
-rw-r--r--tests/resources/push_src/.gitted/logs/refs/heads/master3
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/HEAD1
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/config15
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/description1
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/indexbin0 -> 256 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/info/exclude6
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/logs/HEAD1
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/logs/refs/heads/master1
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/logs/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/08/b041783f40edfe12bb406c9c9a8a040177c125bin0 -> 54 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08bin0 -> 19 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/18/1037049a54a1eb5fab404658a3a250b44335d7bin0 -> 51 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/18/10dff58d8a660512d4832e740f692884338ccdbin0 -> 119 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fdbin0 -> 122 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/1b/8cbad43e867676df601306689fe7c3def5e689bin0 -> 51 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9bbin0 -> 21 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10bin0 -> 168 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/27/0b8ea76056d5cad83af921837702d3e3c2924dbin0 -> 21 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7bin0 -> 44 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54bin0 -> 50 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022ccbin0 -> 23 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057bin0 -> 18 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd20452
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/4b/22b35d44b5a4f589edf3dc89196399771796eabin0 -> 44 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91bin0 -> 152 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/5b/5b025afb0b4c913b4c338a42934a3863bf36442
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/75/057dd4114e74cca1d750d0aee1647c903cb60abin0 -> 119 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af1
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/7b/4384978d2493e851f9cca7858815fac9b10980bin0 -> 145 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/81/4889a078c031f61ed08ab5fa863aea9314344dbin0 -> 82 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/84/96071c1b46c854b31185ea97743be6a8774479bin0 -> 126 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe2
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe181621
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4bin0 -> 50 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d22
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a3
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f2
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe45477503
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bdbin0 -> 28 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6bin0 -> 26 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/ae/90f12eea699729ed24555e40b9fd669da12a12bin0 -> 148 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d12
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593bin0 -> 80 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f6443
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd3
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659bin0 -> 149 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487fbin0 -> 21 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759bin0 -> 79 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391bin0 -> 15 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0bin0 -> 21 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3bin0 -> 103 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1bin0 -> 82 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/fa/49b077972391ad58037050f2a75f74e3671e92bin0 -> 24 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/fd/093bff70906175335656e6ce6ae05783708765bin0 -> 82 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09bin0 -> 152 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idxbin0 -> 46656 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.packbin0 -> 386089 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idxbin0 -> 1240 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.packbin0 -> 491 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idxbin0 -> 1240 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.packbin0 -> 498 bytes
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/packed-refs24
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/refs/heads/master1
-rw-r--r--tests/resources/push_src/.gitted/modules/submodule/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/push_src/.gitted/objects/08/585692ce06452da6f82ae66b90d98b55536fca1
-rw-r--r--tests/resources/push_src/.gitted/objects/27/b7ce66243eb1403862d05f958c002312df173d4
-rw-r--r--tests/resources/push_src/.gitted/objects/28/905c54ea45a4bed8d7b90f51bd8bd81eec8840bin0 -> 109 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/36/6226fb970ac0caa9d3f55967ab01334a548f60bin0 -> 20 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/36/f79b2846017d3761e0a02d0bccd573e0f90c572
-rw-r--r--tests/resources/push_src/.gitted/objects/5c/0bb3d1b9449d1cc69d7519fd05166f01840915bin0 -> 128 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/61/780798228d17af2d34fce4cfbdf35556832472bin0 -> 17 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/64/fd55f9b6390202db5e5666fd1fb339089fba4dbin0 -> 176 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/78/981922613b2afb6025042ff6bd878ac1994e85bin0 -> 17 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/80/5c54522e614f29f70d2413a0470247d8b424acbin0 -> 131 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/95/1bbbb90e2259a4c8950db78946784fb53fcbce2
-rw-r--r--tests/resources/push_src/.gitted/objects/a7/8705c3b2725f931d3ee05348d83cc26700f247bin0 -> 166 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/b4/83ae7ba66decee9aee971f501221dea84b14983
-rw-r--r--tests/resources/push_src/.gitted/objects/b4/e1f2b375a64c1ccd40c5ff6aa8bc96839ba4fdbin0 -> 148 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/c1/0409136a7a75e025fa502a1b2fd7b62b77d279bin0 -> 22 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/cd/881f90f2933db2e4cc26b8c71fe6037ac7fe4cbin0 -> 80 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/d9/b63a88223d8367516f50bd131a5f7349b7f3e42
-rw-r--r--tests/resources/push_src/.gitted/objects/dc/ab83249f6f9d1ed735d651352a80519339b591bin0 -> 80 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/ee/a4f2705eeec2db3813f2430829afce99cd00b5bin0 -> 141 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/f7/8a3106c85fb549c65198b2a2086276c6174928bin0 -> 65 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/f8/f7aefc2900a3d737cea9eee45729fd55761e1abin0 -> 50 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/fa/38b91f199934685819bea316186d8b008c52a22
-rw-r--r--tests/resources/push_src/.gitted/objects/ff/83aa4c5e5d28e3bcba2f5c6e2adc61286a4e5e4
-rw-r--r--tests/resources/push_src/.gitted/objects/ff/fe95c7fd0a37fa2ed702f8f93b56b2196b3925bin0 -> 109 bytes
-rw-r--r--tests/resources/push_src/.gitted/objects/pack/dummy0
-rw-r--r--tests/resources/push_src/.gitted/refs/heads/b11
-rw-r--r--tests/resources/push_src/.gitted/refs/heads/b21
-rw-r--r--tests/resources/push_src/.gitted/refs/heads/b31
-rw-r--r--tests/resources/push_src/.gitted/refs/heads/b41
-rw-r--r--tests/resources/push_src/.gitted/refs/heads/b51
-rw-r--r--tests/resources/push_src/.gitted/refs/heads/b61
-rw-r--r--tests/resources/push_src/.gitted/refs/tags/tag-blob1
-rw-r--r--tests/resources/push_src/.gitted/refs/tags/tag-commit1
-rw-r--r--tests/resources/push_src/.gitted/refs/tags/tag-commit-two1
-rw-r--r--tests/resources/push_src/.gitted/refs/tags/tag-lightweight1
-rw-r--r--tests/resources/push_src/.gitted/refs/tags/tag-tag1
-rw-r--r--tests/resources/push_src/.gitted/refs/tags/tag-tree1
-rw-r--r--tests/resources/push_src/a.txt2
-rw-r--r--tests/resources/push_src/fold/b.txt1
-rw-r--r--tests/resources/push_src/foldb.txt1
-rw-r--r--tests/resources/push_src/gitmodules3
-rw-r--r--tests/resources/push_src/submodule/.gitted1
-rw-r--r--tests/resources/push_src/submodule/README1
-rw-r--r--tests/resources/push_src/submodule/branch_file.txt2
-rw-r--r--tests/resources/push_src/submodule/new.txt1
-rw-r--r--tests/resources/rebase-submodule/.gitted/HEAD1
-rw-r--r--tests/resources/rebase-submodule/.gitted/ORIG_HEAD1
-rw-r--r--tests/resources/rebase-submodule/.gitted/config9
-rw-r--r--tests/resources/rebase-submodule/.gitted/description1
-rw-r--r--tests/resources/rebase-submodule/.gitted/indexbin0 -> 681 bytes
-rw-r--r--tests/resources/rebase-submodule/.gitted/info/exclude6
-rw-r--r--tests/resources/rebase-submodule/.gitted/info/refs4
-rw-r--r--tests/resources/rebase-submodule/.gitted/objects/01/971e2453a407e4b9f6c865e2c37f4db21da2941
-rw-r--r--tests/resources/rebase-submodule/.gitted/objects/17/f8ae8ebdd08a4bb272f61b897b308ad42b1b121
-rw-r--r--tests/resources/rebase-submodule/.gitted/objects/42/cdad903aef3e7b614675e6584a8be417941911bin0 -> 208 bytes
-rw-r--r--tests/resources/rebase-submodule/.gitted/objects/4b/7c5650008b2e747fe1809eeb5a1dde0e80850abin0 -> 615 bytes
-rw-r--r--tests/resources/rebase-submodule/.gitted/objects/5b/1e8bccf7787e942aecf61912f94a2c274f85a5bin0 -> 368 bytes
-rw-r--r--tests/resources/rebase-submodule/.gitted/objects/68/af1fc7407fd9addf1701a87eb1c95c7494c598bin0 -> 443 bytes
-rw-r--r--tests/resources/rebase-submodule/.gitted/objects/68/f6182f4c85d39e1309d97c7e456156dc9c0096bin0 -> 755 bytes
-rw-r--r--tests/resources/rebase-submodule/.gitted/objects/7c/71f7606bd3bfb25d063c970804e7fc00b9605bbin0 -> 279 bytes
-rw-r--r--tests/resources/rebase-submodule/.gitted/objects/7c/7bf85e978f1d18c0566f702d2cb7766b9c8d4f1
-rw-r--r--tests/resources/rebase-submodule/.gitted/objects/a7/b066537e6be7109abfe4ff97b675d4e077da20bin0 -> 621 bytes
-rw-r--r--tests/resources/rebase-submodule/.gitted/objects/ab/6cf22b4c67a274aa8d31b5877d92341e8c2a9cbin0 -> 279 bytes
-rw-r--r--tests/resources/rebase-submodule/.gitted/objects/c4/e6cca3ec6ae0148ed231f97257df8c311e015f1
-rw-r--r--tests/resources/rebase-submodule/.gitted/objects/f3/6de77de6f53dddafeb024ecaf375e45c3d9dddbin0 -> 58 bytes
-rw-r--r--tests/resources/rebase-submodule/.gitted/objects/ff/b36e513f5fdf8a6ba850a20142676a2ac4807dbin0 -> 355 bytes
-rw-r--r--tests/resources/rebase-submodule/.gitted/packed-refs2
-rw-r--r--tests/resources/rebase-submodule/.gitted/refs/heads/asparagus1
-rw-r--r--tests/resources/rebase-submodule/.gitted/refs/heads/master1
-rw-r--r--tests/resources/rebase-submodule/asparagus.txt10
-rw-r--r--tests/resources/rebase-submodule/beef.txt22
-rw-r--r--tests/resources/rebase-submodule/bouilli.txt18
-rw-r--r--tests/resources/rebase-submodule/gitmodules3
-rw-r--r--tests/resources/rebase-submodule/gravy.txt8
-rw-r--r--tests/resources/rebase-submodule/oyster.txt13
-rw-r--r--tests/resources/rebase-submodule/veal.txt18
-rw-r--r--tests/resources/rebase/.gitted/HEAD1
-rw-r--r--tests/resources/rebase/.gitted/config4
-rw-r--r--tests/resources/rebase/.gitted/indexbin0 -> 488 bytes
-rw-r--r--tests/resources/rebase/.gitted/info/exclude6
-rw-r--r--tests/resources/rebase/.gitted/logs/HEAD1
-rw-r--r--tests/resources/rebase/.gitted/objects/00/66204dd469ee930e551fbcf123f98e211c99cebin0 -> 806 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/00/f1b9a0948a7d5d14405eba6030efcdfbb8ff4a3
-rw-r--r--tests/resources/rebase/.gitted/objects/01/3cc32d341bab0e6f039f50f153c18986f16c58bin0 -> 175 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/01/a17f7d154ab5bf9f8bfede3d82dd00ddf7e7dcbin0 -> 370 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/02/2d3b6bbd0bfbdf147319476fb8bf405691cb0dbin0 -> 208 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/05/3808a709cf91385985369159b296cf61a177acbin0 -> 241 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/0e/f2e2b2a2b8d6e1f8dff5e621e0eca21b693d0c3
-rw-r--r--tests/resources/rebase/.gitted/objects/0f/5f6d3353be1a9966fa5767b7d604b051798224bin0 -> 183 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/11/fac10ca1b9318ce361a0be0c3d889d777e299cbin0 -> 208 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/12/c084412b952396962eb420716df01022b847cc2
-rw-r--r--tests/resources/rebase/.gitted/objects/12/f28ed978639d331269d9dc2b74e87db58e10573
-rw-r--r--tests/resources/rebase/.gitted/objects/19/14d57ddf6c5c997664521cc94f190df46dc1c2bin0 -> 277 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/1b/1d19799fcc89fa3cb821581fcf7f2e8fd2cc4dbin0 -> 178 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/1d/83f106355e4309a293e42ad2a2c4b8bdbe77aebin0 -> 31 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/1f/2214c1b13b134d5508f41f6a3b77cc6a8f5182bin0 -> 209 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/20/db906c85e78c6dde82eb2ec6d3231c4b96fce8bin0 -> 796 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/22/adb22bef75a0371e85ff6d82e5e60e4b425501bin0 -> 380 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/2a/a3ce842094e08ebac152b3d6d5b0fff39f9c6e1
-rw-r--r--tests/resources/rebase/.gitted/objects/2b/4ebffd3111546d278bb5df62e5630930b605fbbin0 -> 208 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/30/69cc907e6294623e5917ef6de663928c1febfb1
-rw-r--r--tests/resources/rebase/.gitted/objects/32/52a0692ace4c4c709f22011227d9dc4845f289bin0 -> 209 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/33/f915f9e4dbd9f4b24430e48731a59b45b155001
-rw-r--r--tests/resources/rebase/.gitted/objects/34/86a9d4cdf0b7b4a702c199eed541dc3af13a031
-rw-r--r--tests/resources/rebase/.gitted/objects/3c/33b080bf75724c8899d8e703614cb59bfbd047bin0 -> 208 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/3d/a85aca38a95b44d77ef55a8deb445e49ba19b4bin0 -> 55 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/3e/8989b5a16d5258c935d998ef0e6bb139cc47572
-rw-r--r--tests/resources/rebase/.gitted/objects/3f/05a038dd89f51ba2b3d7b14ba1f8c00f0e31acbin0 -> 209 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/3f/d8d53cf02de539b9a25a5941030451f76a152fbin0 -> 89 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/40/0d89e8ee6cd91b67b1f45de1ca190e1c580c6f1
-rw-r--r--tests/resources/rebase/.gitted/objects/41/4dfc71ead79c07acd4ea47fecf91f289afc4b9bin0 -> 376 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/41/c5a0a761bb4a7670924c1af0800b30fe9a21bebin0 -> 384 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/42/cdad903aef3e7b614675e6584a8be417941911bin0 -> 208 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/44/c801fe026abbc141b52a4dec5df15fa98249c6bin0 -> 367 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/4b/21eb6eeeec7f8fc89a1d334faff9bd5f5f8c342
-rw-r--r--tests/resources/rebase/.gitted/objects/4b/7c5650008b2e747fe1809eeb5a1dde0e80850abin0 -> 615 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/4b/ed71df7017283cac61bbf726197ad6a5a18b842
-rw-r--r--tests/resources/rebase/.gitted/objects/4c/acc6f6e740a5bc64faa33e04b8ef0733d8a127bin0 -> 169 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/4f/b698bde45d7d2833e3f2aacfbfe8a7e7f60a65bin0 -> 208 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/50/8be4ff49d38465ad3de58f66d38f70e59f881fbin0 -> 241 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/53/f75e45a463033854e52fa8d39dc858e45537d0bin0 -> 209 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/58/8e5d2f04d49707fe4aab865e1deacaf7ef67871
-rw-r--r--tests/resources/rebase/.gitted/objects/5a/72bf3bf964fdb176ffa4587312e69e2039695abin0 -> 49 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/5b/1e8bccf7787e942aecf61912f94a2c274f85a5bin0 -> 368 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/60/29cb003b59f710f9a8ebd9da9ece2d73070b69bin0 -> 655 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/61/139b9b40a3e489f4abbc6af14e10ae14006e47bin0 -> 224 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/61/30e5fcbdce2aa8b3cfd84706c58a892e7d8dd0bin0 -> 208 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/63/c18bf188b8a1ab0bad85161dc3fb43c48ed0dbbin0 -> 208 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/67/ed7afb256807556f9b74fa4f7c9284aaec1120bin0 -> 391 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/68/af1fc7407fd9addf1701a87eb1c95c7494c598bin0 -> 443 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/68/f6182f4c85d39e1309d97c7e456156dc9c0096bin0 -> 755 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/6c/8e16469b6ca09a07e00f0e07a5143c31dcfb641
-rw-r--r--tests/resources/rebase/.gitted/objects/6d/77ce8fa2cd93c6489236e33e45e35203ca748c1
-rw-r--r--tests/resources/rebase/.gitted/objects/6d/fb87d20f3dbca02da4a39890114fd9ba6a51e7bin0 -> 364 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/73/f346c88d965227a03c0af8d555870b8c5021d4bin0 -> 89 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/74/0a804e8963759c98e5b8cb912e15ae74a7a4a6bin0 -> 207 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/77/0f14546ee2563a26c52afa5cc4139a96e5d360bin0 -> 238 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/78/c320b06544e23d786a9ec84ee93861f2933094bin0 -> 55 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/79/e28694aae0d3064b06f96a5207b943a2357f07bin0 -> 287 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/7a/05900f340af0252aaa4e34941f040c5d2fe7f7bin0 -> 176 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/7a/677f6201c8f9d46bdfe1f4b08cb504e360a34ebin0 -> 208 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/7c/7bf85e978f1d18c0566f702d2cb7766b9c8d4f1
-rw-r--r--tests/resources/rebase/.gitted/objects/7f/37fe2d7320360f8a9118b1ed8fba6f384816791
-rw-r--r--tests/resources/rebase/.gitted/objects/80/32d630f37266bace093e353f7b97d7f8b20950bin0 -> 209 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/80/dce0e74f0534811db734a68c23b49f98584d7a2
-rw-r--r--tests/resources/rebase/.gitted/objects/83/53b9f9deff7c707f280e0f656c80772cca7cd92
-rw-r--r--tests/resources/rebase/.gitted/objects/85/258e426a341cc1aa035ac7f6d18f84fed2ab38bin0 -> 208 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/85/f34ce9ca9e0f33d4146afec9cbe5a26757500abin0 -> 185 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/86/a5415741ed3754ccb0cac1fc19fd82587840a4bin0 -> 634 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/8d/1f13f93c4995760ac07d129246ac1ff64c0be92
-rw-r--r--tests/resources/rebase/.gitted/objects/8d/95ea62e621f1d38d230d9e7d206e41096d76afbin0 -> 773 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/8f/4de6c781b9ff9cedfd7f9f9f224e744f97b2591
-rw-r--r--tests/resources/rebase/.gitted/objects/91/4f3c604d1098847b7fe275f659ee329878153fbin0 -> 48 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/92/54a37fde7e97f9a28dee2967fdb2c5d1ed94e91
-rw-r--r--tests/resources/rebase/.gitted/objects/95/39b2cc291d6a6b1b266df8474d31fdd344dd79bin0 -> 173 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/9a/8535dfcaf7554c728d874f047c5461fb2c71d1bin0 -> 208 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/9c/d483e7da23819d7f71d24e98438123378867531
-rw-r--r--tests/resources/rebase/.gitted/objects/a0/1a6ee390f65d834375e072952deaee0c5e92f7bin0 -> 90 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/a0/fa65f96c1e3bdc7287e334229279dcc1248fa43
-rw-r--r--tests/resources/rebase/.gitted/objects/a1/25b9b655932711abceaf8962948e6b601d67b6bin0 -> 89 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/a7/00acc970eccccc73be53cd269462176544e6d1bin0 -> 373 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/a7/b066537e6be7109abfe4ff97b675d4e077da20bin0 -> 621 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/aa/4c42aecdfc7cd989bbc3209934ea7cda3f4d881
-rw-r--r--tests/resources/rebase/.gitted/objects/ab/25a53ef5622d443ecb0492b7516725f0deac8fbin0 -> 208 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/ad/c97cfb874cdfb9d5ab17b54f3771dea6e02ccfbin0 -> 240 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/ae/87cae12879a3c37d7cc994afc6395bcb0eaf99bin0 -> 209 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/b1/46bd7608eac53d9bf9e1a6963543588b555c641
-rw-r--r--tests/resources/rebase/.gitted/objects/b1/b94ec02f8ed87d0efa4c65fb38d5d6da7e8b32bin0 -> 208 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/b6/72b141d48c369fee6c4deeb32a904387594365bin0 -> 174 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/b7/c536a5883c8adaeb34d5e198c5a3dbbdc608b5bin0 -> 209 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/b9/f72b9158fa8c49fb4e4c10b26817ed867be8033
-rw-r--r--tests/resources/rebase/.gitted/objects/bc/cc8eabb5cfe2ec09959c7f4155aa73429fd604bin0 -> 207 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/c4/e6cca3ec6ae0148ed231f97257df8c311e015f1
-rw-r--r--tests/resources/rebase/.gitted/objects/c5/17380440ed78865ffe3fa130b9738615c76618bin0 -> 649 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/cb/20a10406172afd6ca3138ce36ecaf8b1269e8ebin0 -> 213 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/d4/82e77aecb8e07da43e4cad6e0dcb59219e12afbin0 -> 175 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/d6/16d97082eb7bb2dc6f180a7cca940993b7a56f1
-rw-r--r--tests/resources/rebase/.gitted/objects/d6/b9ec0dfb972a6815ace42545cde5f2631cd776bin0 -> 790 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/d9/c5185186d95d233dc007c1927cb3bdd6cde35bbin0 -> 165 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/da/82b3a60c50cf5ac524ec3000d743447329465dbin0 -> 369 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/da/9c51a23d02d931a486f45ad18cda05cf5d2b942
-rw-r--r--tests/resources/rebase/.gitted/objects/dc/12ac1e10f2be70e8ecd52132a08da98a309c3a1
-rw-r--r--tests/resources/rebase/.gitted/objects/df/d3d25264693fcd7348ad286f3c34f3f6b30918bin0 -> 177 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/e4/f809f826c1a9fc929874bc0e4644dd2f2a1af43
-rw-r--r--tests/resources/rebase/.gitted/objects/e5/2ff405da5b7e1e9b0929939fa8405d81fe8a453
-rw-r--r--tests/resources/rebase/.gitted/objects/e7/bb00c4eab291e08361fda376733a12b4150aa9bin0 -> 241 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/e8/8cc0a6919a74599ce8e1dcb81eb2bbae33a645bin0 -> 208 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/e9/5f47e016dcc70b0b888df8e40e97b8aabafd4cbin0 -> 279 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/e9/f22c10ffb378446c0bbcab7ee3d9d5a00406722
-rw-r--r--tests/resources/rebase/.gitted/objects/ec/725f5639730640f91cd0be5f2d6d7ac5d69c79bin0 -> 377 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/ed/f7b3ffde1624c60d2d6b1a2bb792d86de172e03
-rw-r--r--tests/resources/rebase/.gitted/objects/ee/23c5eeedadf8595c0ff60a366d970a165e373dbin0 -> 372 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/ee/f0edde5daa94da5f297d4ddb5dfbc1980f0902bin0 -> 55 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/ef/ad0b11c47cb2f0220cbd6f5b0f93bb99064b001
-rw-r--r--tests/resources/rebase/.gitted/objects/f5/56d5fef35003561dc0b64b37057d7541239105bin0 -> 90 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/f6/3fa37e285bd11b0a7b48fa584a4091814a3adabin0 -> 55 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/f7/5c193a1df47186727179f24867bc4d27a8991fbin0 -> 802 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/f8/7d14a4a236582a0278a916340a7937142568642
-rw-r--r--tests/resources/rebase/.gitted/objects/fc/e0584b379f535e50e036db587db71884ea6b36bin0 -> 281 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/ff/b36e513f5fdf8a6ba850a20142676a2ac4807dbin0 -> 355 bytes
-rw-r--r--tests/resources/rebase/.gitted/objects/ff/dfa89389040a87008c4ab1834120d3046daaeabin0 -> 208 bytes
-rw-r--r--tests/resources/rebase/.gitted/refs/heads/asparagus1
-rw-r--r--tests/resources/rebase/.gitted/refs/heads/barley1
-rw-r--r--tests/resources/rebase/.gitted/refs/heads/beef1
-rw-r--r--tests/resources/rebase/.gitted/refs/heads/deep_gravy1
-rw-r--r--tests/resources/rebase/.gitted/refs/heads/dried_pea1
-rw-r--r--tests/resources/rebase/.gitted/refs/heads/gravy1
-rw-r--r--tests/resources/rebase/.gitted/refs/heads/green_pea1
-rw-r--r--tests/resources/rebase/.gitted/refs/heads/master1
-rw-r--r--tests/resources/rebase/.gitted/refs/heads/veal1
-rw-r--r--tests/resources/rebase/asparagus.txt10
-rw-r--r--tests/resources/rebase/beef.txt22
-rw-r--r--tests/resources/rebase/bouilli.txt18
-rw-r--r--tests/resources/rebase/gravy.txt8
-rw-r--r--tests/resources/rebase/oyster.txt13
-rw-r--r--tests/resources/rebase/veal.txt18
-rw-r--r--tests/resources/redundant.git/HEAD1
-rwxr-xr-xtests/resources/redundant.git/config5
-rw-r--r--tests/resources/redundant.git/objects/info/packs2
-rw-r--r--tests/resources/redundant.git/objects/pack/pack-3d944c0c5bcb6b16209af847052c6ff1a521529d.idxbin0 -> 121136 bytes
-rw-r--r--tests/resources/redundant.git/objects/pack/pack-3d944c0c5bcb6b16209af847052c6ff1a521529d.packbin0 -> 309860 bytes
-rw-r--r--tests/resources/redundant.git/packed-refs3
-rw-r--r--tests/resources/redundant.git/refs/.gitkeep0
-rw-r--r--tests/resources/renames/.gitted/HEAD1
-rw-r--r--tests/resources/renames/.gitted/ORIG_HEAD1
-rw-r--r--tests/resources/renames/.gitted/config7
-rw-r--r--tests/resources/renames/.gitted/description1
-rw-r--r--tests/resources/renames/.gitted/indexbin0 -> 385 bytes
-rw-r--r--tests/resources/renames/.gitted/info/exclude6
-rw-r--r--tests/resources/renames/.gitted/logs/HEAD9
-rw-r--r--tests/resources/renames/.gitted/logs/refs/heads/master4
-rw-r--r--tests/resources/renames/.gitted/objects/03/da7ad872536bd448da8d88eb7165338bf923a7bin0 -> 90 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/17/58bdd7c16a72ff7c17d8de0c957ced3ccad6455
-rw-r--r--tests/resources/renames/.gitted/objects/19/dd32dfb1520a64e5bbaae8dce6ef423dfa2f131
-rw-r--r--tests/resources/renames/.gitted/objects/1c/068dee5790ef1580cfc4cd670915b48d790084bin0 -> 176 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/2b/c7f351d20b53f1c72c16c4b036e491c478c49abin0 -> 173 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/2c/136d294960f7d939f1ed1903f1ced78b874c87bin0 -> 126 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/31/e47d8c1fa36d7f8d537b96158e3f024de0a9f2bin0 -> 131 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/35/92953ff3ea5e8ba700c429f3aefe33c8806754bin0 -> 80 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/36/020db6cdacaa93497f31edcd8f242ff9bc366dbin0 -> 431 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/3c/04741dd4b96c4ae4b00ec0f6e10c816a30aad2bin0 -> 159 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/41/2a2eaf2c13103ea976b3b02c17abee7a358432bin0 -> 82 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/41/a766bb079e18ff6a24571831bd751168799a021
-rw-r--r--tests/resources/renames/.gitted/objects/42/10ffd5c390b21dd5483375e75288dea9ede512bin0 -> 1145 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/44/4a76ed3e45b183753f49376af30da8c3fe276abin0 -> 135 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/47/184c1e7eb22abcbed2bf4ee87d4e38096f7951bin0 -> 229 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/4e/4cae3e7dd56ed74bff39526d0469e554432953bin0 -> 452 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/50/e90273af7d826ff0a95865bcd3ba8412c447d93
-rw-r--r--tests/resources/renames/.gitted/objects/5a/71babaaac78a758b52576a60cea3c218c8b546bin0 -> 2804 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/5e/26abc56a5a84d89790f45416648899cbe13109bin0 -> 163 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/61/8c6f2f8740bd6049b2fb9eb93fc15726462745bin0 -> 106 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/66/311f5cfbe7836c27510a3ba2f43e282e2c8bbabin0 -> 1155 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/7e/7bfb88ba9bc65fd700fee1819cf1c317aafa562
-rw-r--r--tests/resources/renames/.gitted/objects/84/d8efa38af7ace2b302de0adbda16b1f1cd2e1b1
-rw-r--r--tests/resources/renames/.gitted/objects/89/7dda8ecb7fa2e092bc3f9e2a179d7c1b0364dbbin0 -> 130 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/93/f538c45a57a87eb4c1e86f91c6ee41d66c7ba7bin0 -> 229 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/95/ceb126bf79e76020d8879a8b0d50a73307a97fbin0 -> 1189 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/9a/69d960ae94b060f56c2a8702545e2bb1abb935bin0 -> 464 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/ad/0a8e55a104ac54a8a29ed4b84b49e76837a113bin0 -> 415 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/b9/25b224cc91f897001a9993fbce169fdaa8858fbin0 -> 76 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/ba/8b004914491b129b4feff51b5fd8553b8e8156bin0 -> 191 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/be/053a189b0bbde545e0a3f59ce00b46ad29ce0dbin0 -> 163 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/bf/102db0c9c0c1513909a90e0611b5dddfc87c932
-rw-r--r--tests/resources/renames/.gitted/objects/cc/980ffac5f1393696d4cd703ea9d2fde67dd367bin0 -> 2801 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/d7/9b202de198fa61b02424b9e25e840dc75e1323bin0 -> 421 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/db/98035f715427eef1f5e17f03e1801c05301e9ebin0 -> 199 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/e5/6d39ca6d946331aa79c9c443d46c8a6ed4f550bin0 -> 36 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/ea/c43f5195a2cee53b7458d8dad16aedde10711bbin0 -> 118 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/ea/f4a3e3bfe68585e90cada20736ace491cd100b5
-rw-r--r--tests/resources/renames/.gitted/objects/eb/b3b7af1d25c8492d2f626826c92458b7cefd60bin0 -> 5164 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/ed/2a95c4a6c295b9afcea50baff63ec544ccf600bin0 -> 5161 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/f6/7e2f70efe89665e829ea0d77c46965ad1307e4bin0 -> 5164 bytes
-rw-r--r--tests/resources/renames/.gitted/objects/f9/0d4fc20ecddf21eebe6a37e9225d244339d2b5bin0 -> 441 bytes
-rw-r--r--tests/resources/renames/.gitted/refs/heads/break_rewrite1
-rw-r--r--tests/resources/renames/.gitted/refs/heads/case-insensitive-status1
-rw-r--r--tests/resources/renames/.gitted/refs/heads/delete-and-rename1
-rw-r--r--tests/resources/renames/.gitted/refs/heads/master1
-rw-r--r--tests/resources/renames/.gitted/refs/heads/renames_similar1
-rw-r--r--tests/resources/renames/.gitted/refs/heads/renames_similar_two1
-rw-r--r--tests/resources/renames/.gitted/refs/heads/rewrite-and-delete1
-rw-r--r--tests/resources/renames/ikeepsix.txt27
-rw-r--r--tests/resources/renames/sixserving.txt25
-rw-r--r--tests/resources/renames/songof7cities.txt49
-rw-r--r--tests/resources/renames/untimely.txt24
-rw-r--r--tests/resources/revert-rename.git/HEAD1
-rw-r--r--tests/resources/revert-rename.git/config5
-rw-r--r--tests/resources/revert-rename.git/indexbin0 -> 209 bytes
-rw-r--r--tests/resources/revert-rename.git/objects/info/packs2
-rw-r--r--tests/resources/revert-rename.git/objects/pack/pack-4363774fb90141e8aa7a326ace0566366114e869.idxbin0 -> 1296 bytes
-rw-r--r--tests/resources/revert-rename.git/objects/pack/pack-4363774fb90141e8aa7a326ace0566366114e869.packbin0 -> 783 bytes
-rw-r--r--tests/resources/revert-rename.git/packed-refs2
-rw-r--r--tests/resources/revert-rename.git/refs/heads/master1
-rw-r--r--tests/resources/revert/.gitted/HEAD1
-rw-r--r--tests/resources/revert/.gitted/config8
-rw-r--r--tests/resources/revert/.gitted/indexbin0 -> 464 bytes
-rw-r--r--tests/resources/revert/.gitted/info/exclude6
-rw-r--r--tests/resources/revert/.gitted/objects/00/c97c9299419874a7bfc4d853d462c568e1be2dbin0 -> 137 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/0a/a8c7e40d342fff78d60b29a4ba8e993ed79c512
-rw-r--r--tests/resources/revert/.gitted/objects/0a/b09ea6d4c3634bdf6c221626d8b6f7dd890767bin0 -> 28 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/0a/d19525be6d8cae5e5deb2770fc244b65255057bin0 -> 133 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/0c/db66192ee192f70f891f05a47636057420e871bin0 -> 35 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/0f/5bfcf58c558d865da6be0281d7795993646ceebin0 -> 45 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/10/10c8f4711d60d04bad16197a0f4b0d4d19c542bin0 -> 53 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/13/a6fdfd10bd74b1f258fb58801215985dd2e797bin0 -> 121 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/13/ee9cd5d8e1023c218e0e1ea684ec0c582b5050bin0 -> 161 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/15/6ef9bcb968dccec8472a0f2eff49f1a713bc6bbin0 -> 133 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/18/1aab27ddb37b40d9a284fb4733497006d57091bin0 -> 133 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/1b/c915c5cb7185a9438de28a7b1a7dfe8c01ee7fbin0 -> 1169 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/1f/a4e069a641f10f5fb7588138b2d147fcd22c36bin0 -> 133 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/1f/f0c423042b46cb1d617b81efb715defbe8054dbin0 -> 751 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/21/a96a98ed84d45866e1de6e266fd3a61a4ae9dcbin0 -> 19 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/29/6a6d3be1dff05c5d1f631d2459389fa7b619ebbin0 -> 40 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/2d/440f2b3147d3dc7ad1085813478d6d869d5a4d2
-rw-r--r--tests/resources/revert/.gitted/objects/33/c6fd981c49a2abf2971482089350bfc5cda8eabin0 -> 47 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/39/467716290f6df775a91cdb9a4eb39295018145bin0 -> 162 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/39/9fb3aba3d9d13f7d40a9254ce4402067ef31492
-rw-r--r--tests/resources/revert/.gitted/objects/3a/3ef367eaf3fe79effbfb0a56b269c04c2b59febin0 -> 33 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/46/ff0854663aeb2182b9838c8da68e33ac23bc1ebin0 -> 20 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/4b/8fcff56437e60f58e9a6bc630dd242ebf6ea2cbin0 -> 43 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/52/c95c4264245469a0617e289a7d737f156826b42
-rw-r--r--tests/resources/revert/.gitted/objects/55/568c8de5322ff9a95d72747a239cdb64a199651
-rw-r--r--tests/resources/revert/.gitted/objects/55/acf326a69f0aab7a974ec53ffa55a50bcac14ebin0 -> 30 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/5a/cdc74af27172ec491d213ee36cea7eb9ef25793
-rw-r--r--tests/resources/revert/.gitted/objects/6b/ccd0dc58cea5ccff86014f3d64b31bd8c02a37bin0 -> 171 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/71/eb9c2b53dbbf3c45fb28b27c850db4b7fb8011bin0 -> 148 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/72/333f47d4e83616630ff3b0ffe4c0faebcc3c45bin0 -> 172 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/73/ec36fa120f8066963a0bc9105bb273dbd903d7bin0 -> 31 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/74/7726e021bc5f44b86de60e3032fd6f9f1b8383bin0 -> 28 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/75/ec9929465623f17ff3ad68c0438ea56faba815bin0 -> 163 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/77/31926a337c4eaba1e2187d90ebfa0a93659382bin0 -> 37 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/83/f65df4606c4f8dbf8da43de25de1b7e4c03238bin0 -> 113 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/87/59ad453cf01cf7daf14e2a668f8218f9a678ebbin0 -> 122 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/8b/e77695228eadd004606af0508462457961ca4abin0 -> 52 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/8f/d40e13fff575b63e86af87175e70fa7fb92f80bin0 -> 80 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/97/e52d5e81f541080cd6b92829fb85bc4d81d90bbin0 -> 163 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/97/f3574e92f1730d365fb9e00c10e3c507c1cfe9bin0 -> 115 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/9a/95fd974e03c5b93828ceedd28755965b5d5c60bin0 -> 122 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/a6/9f74efcb51634b88e04ea81273158a85257f41bin0 -> 146 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/a8/c86221b400b836010567cc3593db6e96c1a83abin0 -> 24 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/aa/7e281435d1fe6740d712f4bcc6fe89c425bedcbin0 -> 53 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/ac/c4d33902092efeb3b714aa0b1007c329e2f2e62
-rw-r--r--tests/resources/revert/.gitted/objects/b7/a55408832174c54708906a372a9be2ffe3649bbin0 -> 133 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/be/ead165e017269e8dc0dd6f01195726a2e1e01bbin0 -> 133 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/ce/f56612d71a6af8d8015691e4865f7fece905b5bin0 -> 174 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/d1/d403d22cbe24592d725f442835cf46fe60c8acbin0 -> 164 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/dd/9a159c89509e73fd37d6af99619994cf7dfc06bin0 -> 133 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/e3/4ef1afe54eb526fd92eec66084125f340f1d65bin0 -> 150 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/e5/f831f064adf9224d8c3ce556959d9d61b3c0a91
-rw-r--r--tests/resources/revert/.gitted/objects/ea/392a157085bc32daccd59aa1998fe2f5fb9fc0bin0 -> 134 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/eb/b03002cee5d66c7732dd06241119fe72ab96a52
-rw-r--r--tests/resources/revert/.gitted/objects/ee/c6adcb2f3ceca0cadeccfe01b19382252ece9bbin0 -> 66 bytes
-rw-r--r--tests/resources/revert/.gitted/objects/f4/e107c230d08a60fb419d19869f1f282b272d9cbin0 -> 30 bytes
-rw-r--r--tests/resources/revert/.gitted/refs/heads/master1
-rw-r--r--tests/resources/revert/.gitted/refs/heads/merges1
-rw-r--r--tests/resources/revert/.gitted/refs/heads/merges-branch1
-rw-r--r--tests/resources/revert/.gitted/refs/heads/reverted-branch1
-rw-r--r--tests/resources/revert/.gitted/refs/heads/two1
-rw-r--r--tests/resources/revert/file1.txt14
-rw-r--r--tests/resources/revert/file2.txt16
-rw-r--r--tests/resources/revert/file3.txt16
-rw-r--r--tests/resources/revert/file6.txt14
-rw-r--r--tests/resources/revwalk.git/HEAD1
-rw-r--r--tests/resources/revwalk.git/config6
-rw-r--r--tests/resources/revwalk.git/description1
-rw-r--r--tests/resources/revwalk.git/objects/info/packs2
-rw-r--r--tests/resources/revwalk.git/objects/pack/pack-9adacb9971981a1a3264fd473da5b800f2715959.idxbin0 -> 1520 bytes
-rw-r--r--tests/resources/revwalk.git/objects/pack/pack-9adacb9971981a1a3264fd473da5b800f2715959.packbin0 -> 1862 bytes
-rw-r--r--tests/resources/revwalk.git/packed-refs7
-rw-r--r--tests/resources/revwalk.git/refs/.gitkeep0
-rw-r--r--tests/resources/self-signed.pem20
-rw-r--r--tests/resources/sha1/empty0
-rw-r--r--tests/resources/sha1/hello_c6
-rw-r--r--tests/resources/sha1/shattered-1.pdfbin0 -> 422435 bytes
-rw-r--r--tests/resources/shallow.git/HEAD1
-rw-r--r--tests/resources/shallow.git/config8
-rw-r--r--tests/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.idxbin0 -> 1324 bytes
-rw-r--r--tests/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.packbin0 -> 791 bytes
-rw-r--r--tests/resources/shallow.git/packed-refs2
-rw-r--r--tests/resources/shallow.git/refs/.gitkeep0
-rw-r--r--tests/resources/shallow.git/shallow1
-rw-r--r--tests/resources/short_tag.git/HEAD1
-rw-r--r--tests/resources/short_tag.git/config5
-rw-r--r--tests/resources/short_tag.git/indexbin0 -> 104 bytes
-rw-r--r--tests/resources/short_tag.git/objects/4a/5ed60bafcf4638b7c8356bd4ce1916bfede93cbin0 -> 169 bytes
-rw-r--r--tests/resources/short_tag.git/objects/4d/5fcadc293a348e88f777dc0920f11e7d71441cbin0 -> 48 bytes
-rw-r--r--tests/resources/short_tag.git/objects/5d/a7760512a953e3c7c4e47e4392c7a4338fb7291
-rw-r--r--tests/resources/short_tag.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391bin0 -> 15 bytes
-rw-r--r--tests/resources/short_tag.git/packed-refs1
-rw-r--r--tests/resources/short_tag.git/refs/heads/master1
-rw-r--r--tests/resources/splitindex/.gitted/HEAD1
-rw-r--r--tests/resources/splitindex/.gitted/config8
-rw-r--r--tests/resources/splitindex/.gitted/indexbin0 -> 100 bytes
-rw-r--r--tests/resources/splitindex/.gitted/info/exclude6
-rw-r--r--tests/resources/splitindex/.gitted/objects/.gitkeep0
-rw-r--r--tests/resources/splitindex/.gitted/refs/.gitkeep0
-rw-r--r--tests/resources/splitindex/.gitted/sharedindex.39d890139ee5356c7ef572216cebcd27aa41f9dfbin0 -> 32 bytes
-rw-r--r--tests/resources/status/.gitted/COMMIT_EDITMSG1
-rw-r--r--tests/resources/status/.gitted/HEAD1
-rw-r--r--tests/resources/status/.gitted/ORIG_HEAD1
-rw-r--r--tests/resources/status/.gitted/config6
-rw-r--r--tests/resources/status/.gitted/description1
-rw-r--r--tests/resources/status/.gitted/indexbin0 -> 1160 bytes
-rw-r--r--tests/resources/status/.gitted/info/exclude8
-rw-r--r--tests/resources/status/.gitted/logs/HEAD3
-rw-r--r--tests/resources/status/.gitted/logs/refs/heads/master3
-rw-r--r--tests/resources/status/.gitted/objects/00/17bd4ab1ec30440b17bae1680cff124ab5f1f62
-rw-r--r--tests/resources/status/.gitted/objects/06/1d42a44cacde5726057b67558821d95db96f19bin0 -> 44 bytes
-rw-r--r--tests/resources/status/.gitted/objects/18/88c805345ba265b0ee9449b8877b6064592058bin0 -> 36 bytes
-rw-r--r--tests/resources/status/.gitted/objects/19/d9cc8584ac2c7dcf57d2680375e80f099dc481bin0 -> 22 bytes
-rw-r--r--tests/resources/status/.gitted/objects/26/a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f2
-rw-r--r--tests/resources/status/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863bin0 -> 56 bytes
-rw-r--r--tests/resources/status/.gitted/objects/32/504b727382542f9f089e24fddac5e78533e96cbin0 -> 31 bytes
-rw-r--r--tests/resources/status/.gitted/objects/37/fcb02ccc1a85d1941e7f106d52dc3702dcf0d0bin0 -> 331 bytes
-rw-r--r--tests/resources/status/.gitted/objects/45/2e4244b5d083ddf0460acf1ecc74db9dcfa11abin0 -> 30 bytes
-rw-r--r--tests/resources/status/.gitted/objects/52/9a16e8e762d4acb7b9636ff540a00831f9155abin0 -> 32 bytes
-rw-r--r--tests/resources/status/.gitted/objects/53/ace0d1cc1145a5f4fe4f78a186a60263190733bin0 -> 36 bytes
-rw-r--r--tests/resources/status/.gitted/objects/54/52d32f1dd538eb0405e8a83cc185f79e25e80fbin0 -> 29 bytes
-rw-r--r--tests/resources/status/.gitted/objects/55/d316c9ba708999f1918e9677d01dfcae69c6b9bin0 -> 33 bytes
-rw-r--r--tests/resources/status/.gitted/objects/70/bd9443ada07063e7fbf0b3ff5c13f7494d89c2bin0 -> 44 bytes
-rw-r--r--tests/resources/status/.gitted/objects/73/5b6a258cd196a8f7c9428419b02c1dca93fd75bin0 -> 160 bytes
-rw-r--r--tests/resources/status/.gitted/objects/75/6e27627e67bfbc048d01ece5819c6de733d7eabin0 -> 301 bytes
-rw-r--r--tests/resources/status/.gitted/objects/90/6ee7711f4f4928ddcb2a5f8fbc500deba0d2a8bin0 -> 46 bytes
-rw-r--r--tests/resources/status/.gitted/objects/90/b8c29d8ba39434d1c63e1b093daaa26e5bd972bin0 -> 41 bytes
-rw-r--r--tests/resources/status/.gitted/objects/9c/2e02cdffa8d73e6c189074594477a6baf87960bin0 -> 268 bytes
-rw-r--r--tests/resources/status/.gitted/objects/a0/de7e0ac200c489c41c59dfa910154a70264e6ebin0 -> 29 bytes
-rw-r--r--tests/resources/status/.gitted/objects/a6/191982709b746d5650e93c2acf34ef74e11504bin0 -> 37 bytes
-rw-r--r--tests/resources/status/.gitted/objects/a6/be623522ce87a1d862128ac42672604f7b468bbin0 -> 46 bytes
-rw-r--r--tests/resources/status/.gitted/objects/aa/27a641456848200fdb7f7c99ba36f8a0952877bin0 -> 120 bytes
-rw-r--r--tests/resources/status/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46bin0 -> 38 bytes
-rw-r--r--tests/resources/status/.gitted/objects/da/bc8af9bd6e9f5bbe96a176f1a24baf3d1f8916bin0 -> 42 bytes
-rw-r--r--tests/resources/status/.gitted/objects/e8/ee89e15bbe9b20137715232387b3de5b28972ebin0 -> 38 bytes
-rw-r--r--tests/resources/status/.gitted/objects/e9/b9107f290627c04d097733a10055af941f6bcabin0 -> 37 bytes
-rw-r--r--tests/resources/status/.gitted/objects/ed/062903b8f6f3dccb2fa81117ba6590944ef9bdbin0 -> 42 bytes
-rw-r--r--tests/resources/status/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccfbin0 -> 64 bytes
-rw-r--r--tests/resources/status/.gitted/refs/heads/master1
-rw-r--r--tests/resources/status/current_file1
-rw-r--r--tests/resources/status/ignored_file1
-rw-r--r--tests/resources/status/modified_file2
-rw-r--r--tests/resources/status/new_file1
-rw-r--r--tests/resources/status/staged_changes2
-rw-r--r--tests/resources/status/staged_changes_modified_file3
-rw-r--r--tests/resources/status/staged_delete_modified_file1
-rw-r--r--tests/resources/status/staged_new_file1
-rw-r--r--tests/resources/status/staged_new_file_modified_file2
-rw-r--r--tests/resources/status/subdir.txt2
-rw-r--r--tests/resources/status/subdir/current_file1
-rw-r--r--tests/resources/status/subdir/modified_file2
-rw-r--r--tests/resources/status/subdir/new_file1
-rw-r--r--tests/resources/status/è¿™1
-rw-r--r--tests/resources/sub.git/HEAD1
-rw-r--r--tests/resources/sub.git/config8
-rw-r--r--tests/resources/sub.git/indexbin0 -> 405 bytes
-rw-r--r--tests/resources/sub.git/logs/HEAD1
-rw-r--r--tests/resources/sub.git/logs/refs/heads/master1
-rw-r--r--tests/resources/sub.git/objects/10/ddd6d257e01349d514541981aeecea6b2e741dbin0 -> 22 bytes
-rw-r--r--tests/resources/sub.git/objects/17/6a458f94e0ea5272ce67c36bf30b6be9caf623bin0 -> 28 bytes
-rw-r--r--tests/resources/sub.git/objects/94/c7d78d85c933d1d95b56bc2de01833ba8559fbbin0 -> 132 bytes
-rw-r--r--tests/resources/sub.git/objects/b7/a59b3f4ea13b985f8a1e0d3757d5cd3331add8bin0 -> 139 bytes
-rw-r--r--tests/resources/sub.git/objects/d0/ee23c41b28746d7e822511d7838bce784ae773bin0 -> 54 bytes
-rw-r--r--tests/resources/sub.git/refs/heads/master1
-rw-r--r--tests/resources/submod2/.gitted/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/config20
-rw-r--r--tests/resources/submod2/.gitted/description1
-rw-r--r--tests/resources/submod2/.gitted/indexbin0 -> 944 bytes
-rw-r--r--tests/resources/submod2/.gitted/info/exclude6
-rw-r--r--tests/resources/submod2/.gitted/logs/HEAD4
-rw-r--r--tests/resources/submod2/.gitted/logs/refs/heads/master4
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/config13
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/description1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/indexbin0 -> 192 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/info/exclude6
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/heads/master1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/packed-refs2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/heads/master1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/config13
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/description1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/indexbin0 -> 192 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/info/exclude6
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/logs/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/heads/master1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/packed-refs2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/refs/heads/master1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_file/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/COMMIT_EDITMSG1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/config13
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/description1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/indexbin0 -> 192 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/info/exclude6
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/logs/HEAD2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/heads/master2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/3d/9386c507f6b093471a3e324085657a3c2b42473
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/77/fb0ed3e58568d6ad362c78de08ab8649d76e29bin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/8e/b1e637ed9fc8e5454fa20d38f809091f9395f42
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/packed-refs2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/refs/heads/master1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_head/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/config13
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/description1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/indexbin0 -> 192 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/info/exclude6
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/logs/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/heads/master1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/a0/2d31770687965547ab7a04cee199b29ee458d6bin0 -> 134 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/packed-refs2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/refs/heads/master1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_index/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/config13
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/description1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/indexbin0 -> 192 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/info/exclude6
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/heads/master1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/packed-refs2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/heads/master1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/config13
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/description1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/indexbin0 -> 192 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/info/exclude6
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/heads/master1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/packed-refs2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/refs/heads/master1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_missing_commits/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/config13
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/description1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/indexbin0 -> 192 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/info/exclude6
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/logs/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/heads/master1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/packed-refs2
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/refs/heads/master1
-rw-r--r--tests/resources/submod2/.gitted/modules/sm_unchanged/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod2/.gitted/objects/09/460e5b6cbcb05a3e404593c32a3aa7221eca0ebin0 -> 197 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/14/fe9ccf104058df25e0a08361c4494e167ef2431
-rw-r--r--tests/resources/submod2/.gitted/objects/22/ce3e0311dda73a5992d54a4a595518d3876ea74
-rw-r--r--tests/resources/submod2/.gitted/objects/25/5546424b0efb847b1bfc91dbf7348b277f8970bin0 -> 157 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/2a/30f1e6f94b20917005a21273f65b406d0f8badbin0 -> 144 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/42/cfb95cd01bf9225b659b5ee3edcc78e8eeb478bin0 -> 40 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/57/958699c2dc394f81cfc76950e9c3ac3025c398bin0 -> 136 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/59/01da4f1c67756eeadc5121d206bec2431f253b2
-rw-r--r--tests/resources/submod2/.gitted/objects/60/7d96653d4d0a4f733107f7890c2e67b55b620dbin0 -> 53 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/74/84482eb8db738cafa696993664607500a3f2b9bin0 -> 173 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/7b/a4c5c3561daa5ab1a86215cfb0587e96d404d6bin0 -> 48 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/87/3585b94bdeabccea991ea5e3ec1a277895b698bin0 -> 137 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/97/4cf7c73de336b0c4e019f918f3cee367d72e842
-rw-r--r--tests/resources/submod2/.gitted/objects/9d/bc299bc013ea253583b40bf327b5a6e4037b89bin0 -> 80 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/a9/104bf89e911387244ef499413960ba472066d9bin0 -> 165 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/b6/14088620bbdc1d29549d223ceba0f4419fd4cbbin0 -> 110 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/d4/07f19e50c1da1ff584beafe0d6dac7237c5d06bin0 -> 55 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/d9/3e95571d92cceb5de28c205f1d5f3cc8b88bc82
-rw-r--r--tests/resources/submod2/.gitted/objects/e3/b83bf274ee065eee48734cf8c6dfaf5e81471cbin0 -> 246 bytes
-rw-r--r--tests/resources/submod2/.gitted/objects/f5/4414c25e6d24fe39f5c3f128d7c8a17bc238332
-rw-r--r--tests/resources/submod2/.gitted/objects/f9/90a25a74d1a8281ce2ab018ea8df66795cd60b1
-rw-r--r--tests/resources/submod2/.gitted/refs/heads/master1
-rw-r--r--tests/resources/submod2/README.txt3
-rw-r--r--tests/resources/submod2/gitmodules24
-rw-r--r--tests/resources/submod2/just_a_dir/contents1
-rw-r--r--tests/resources/submod2/just_a_file1
-rw-r--r--tests/resources/submod2/not-submodule/.gitted/HEAD1
-rw-r--r--tests/resources/submod2/not-submodule/.gitted/config6
-rw-r--r--tests/resources/submod2/not-submodule/.gitted/description1
-rw-r--r--tests/resources/submod2/not-submodule/.gitted/indexbin0 -> 112 bytes
-rw-r--r--tests/resources/submod2/not-submodule/.gitted/info/exclude6
-rw-r--r--tests/resources/submod2/not-submodule/.gitted/logs/HEAD1
-rw-r--r--tests/resources/submod2/not-submodule/.gitted/logs/refs/heads/master1
-rw-r--r--tests/resources/submod2/not-submodule/.gitted/objects/68/e92c611b80ee1ed8f38314ff9577f0d15b2444bin0 -> 132 bytes
-rw-r--r--tests/resources/submod2/not-submodule/.gitted/objects/71/ff9927d7c8a5639e062c38a7d35c433c424627bin0 -> 52 bytes
-rw-r--r--tests/resources/submod2/not-submodule/.gitted/objects/f0/1d56b18efd353ef2bb93a4585d590a0847195ebin0 -> 55 bytes
-rw-r--r--tests/resources/submod2/not-submodule/.gitted/refs/heads/master1
-rw-r--r--tests/resources/submod2/not-submodule/README.txt1
-rw-r--r--tests/resources/submod2/not/.gitted/notempty1
-rw-r--r--tests/resources/submod2/not/README.txt1
-rw-r--r--tests/resources/submod2/sm_added_and_uncommited/.gitted1
-rw-r--r--tests/resources/submod2/sm_added_and_uncommited/README.txt3
-rw-r--r--tests/resources/submod2/sm_added_and_uncommited/file_to_modify3
-rw-r--r--tests/resources/submod2/sm_changed_file/.gitted1
-rw-r--r--tests/resources/submod2/sm_changed_file/README.txt3
-rw-r--r--tests/resources/submod2/sm_changed_file/file_to_modify4
-rw-r--r--tests/resources/submod2/sm_changed_head/.gitted1
-rw-r--r--tests/resources/submod2/sm_changed_head/README.txt3
-rw-r--r--tests/resources/submod2/sm_changed_head/file_to_modify4
-rw-r--r--tests/resources/submod2/sm_changed_index/.gitted1
-rw-r--r--tests/resources/submod2/sm_changed_index/README.txt3
-rw-r--r--tests/resources/submod2/sm_changed_index/file_to_modify4
-rw-r--r--tests/resources/submod2/sm_changed_untracked_file/.gitted1
-rw-r--r--tests/resources/submod2/sm_changed_untracked_file/README.txt3
-rw-r--r--tests/resources/submod2/sm_changed_untracked_file/file_to_modify3
-rw-r--r--tests/resources/submod2/sm_changed_untracked_file/i_am_untracked1
-rw-r--r--tests/resources/submod2/sm_missing_commits/.gitted1
-rw-r--r--tests/resources/submod2/sm_missing_commits/README.txt3
-rw-r--r--tests/resources/submod2/sm_missing_commits/file_to_modify3
-rw-r--r--tests/resources/submod2/sm_unchanged/.gitted1
-rw-r--r--tests/resources/submod2/sm_unchanged/README.txt3
-rw-r--r--tests/resources/submod2/sm_unchanged/file_to_modify3
-rw-r--r--tests/resources/submod2_target/.gitted/HEAD1
-rw-r--r--tests/resources/submod2_target/.gitted/config6
-rw-r--r--tests/resources/submod2_target/.gitted/description1
-rw-r--r--tests/resources/submod2_target/.gitted/indexbin0 -> 192 bytes
-rw-r--r--tests/resources/submod2_target/.gitted/info/exclude6
-rw-r--r--tests/resources/submod2_target/.gitted/logs/HEAD4
-rw-r--r--tests/resources/submod2_target/.gitted/logs/refs/heads/master4
-rw-r--r--tests/resources/submod2_target/.gitted/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests/resources/submod2_target/.gitted/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests/resources/submod2_target/.gitted/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod2_target/.gitted/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests/resources/submod2_target/.gitted/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests/resources/submod2_target/.gitted/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests/resources/submod2_target/.gitted/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests/resources/submod2_target/.gitted/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests/resources/submod2_target/.gitted/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests/resources/submod2_target/.gitted/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests/resources/submod2_target/.gitted/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests/resources/submod2_target/.gitted/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod2_target/.gitted/refs/heads/master1
-rw-r--r--tests/resources/submod2_target/README.txt3
-rw-r--r--tests/resources/submod2_target/file_to_modify3
-rw-r--r--tests/resources/submod3/.gitted/HEAD1
-rw-r--r--tests/resources/submod3/.gitted/config25
-rw-r--r--tests/resources/submod3/.gitted/indexbin0 -> 832 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/EIGHT/HEAD1
-rwxr-xr-xtests/resources/submod3/.gitted/modules/EIGHT/config12
-rw-r--r--tests/resources/submod3/.gitted/modules/EIGHT/indexbin0 -> 192 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/EIGHT/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/EIGHT/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/EIGHT/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/EIGHT/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/EIGHT/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/EIGHT/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/EIGHT/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests/resources/submod3/.gitted/modules/EIGHT/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/EIGHT/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests/resources/submod3/.gitted/modules/EIGHT/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests/resources/submod3/.gitted/modules/EIGHT/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/EIGHT/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/EIGHT/packed-refs2
-rw-r--r--tests/resources/submod3/.gitted/modules/EIGHT/refs/heads/master1
-rw-r--r--tests/resources/submod3/.gitted/modules/EIGHT/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod3/.gitted/modules/Five/HEAD1
-rwxr-xr-xtests/resources/submod3/.gitted/modules/Five/config12
-rw-r--r--tests/resources/submod3/.gitted/modules/Five/indexbin0 -> 192 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/Five/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/Five/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/Five/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/Five/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/Five/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/Five/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/Five/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests/resources/submod3/.gitted/modules/Five/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/Five/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests/resources/submod3/.gitted/modules/Five/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests/resources/submod3/.gitted/modules/Five/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/Five/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/Five/packed-refs2
-rw-r--r--tests/resources/submod3/.gitted/modules/Five/refs/heads/master1
-rw-r--r--tests/resources/submod3/.gitted/modules/Five/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod3/.gitted/modules/FoUr/HEAD1
-rwxr-xr-xtests/resources/submod3/.gitted/modules/FoUr/config12
-rw-r--r--tests/resources/submod3/.gitted/modules/FoUr/indexbin0 -> 192 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/FoUr/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/FoUr/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/FoUr/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/FoUr/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/FoUr/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/FoUr/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/FoUr/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests/resources/submod3/.gitted/modules/FoUr/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/FoUr/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests/resources/submod3/.gitted/modules/FoUr/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests/resources/submod3/.gitted/modules/FoUr/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/FoUr/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/FoUr/packed-refs2
-rw-r--r--tests/resources/submod3/.gitted/modules/FoUr/refs/heads/master1
-rw-r--r--tests/resources/submod3/.gitted/modules/FoUr/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod3/.gitted/modules/One/HEAD1
-rwxr-xr-xtests/resources/submod3/.gitted/modules/One/config12
-rw-r--r--tests/resources/submod3/.gitted/modules/One/indexbin0 -> 192 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/One/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/One/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/One/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/One/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/One/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/One/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/One/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests/resources/submod3/.gitted/modules/One/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/One/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests/resources/submod3/.gitted/modules/One/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests/resources/submod3/.gitted/modules/One/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/One/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/One/packed-refs2
-rw-r--r--tests/resources/submod3/.gitted/modules/One/refs/heads/master1
-rw-r--r--tests/resources/submod3/.gitted/modules/One/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod3/.gitted/modules/TEN/HEAD1
-rwxr-xr-xtests/resources/submod3/.gitted/modules/TEN/config12
-rw-r--r--tests/resources/submod3/.gitted/modules/TEN/indexbin0 -> 192 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/TEN/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/TEN/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/TEN/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/TEN/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/TEN/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/TEN/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/TEN/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests/resources/submod3/.gitted/modules/TEN/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/TEN/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests/resources/submod3/.gitted/modules/TEN/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests/resources/submod3/.gitted/modules/TEN/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/TEN/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/TEN/packed-refs2
-rw-r--r--tests/resources/submod3/.gitted/modules/TEN/refs/heads/master1
-rw-r--r--tests/resources/submod3/.gitted/modules/TEN/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod3/.gitted/modules/TWO/HEAD1
-rwxr-xr-xtests/resources/submod3/.gitted/modules/TWO/config12
-rw-r--r--tests/resources/submod3/.gitted/modules/TWO/indexbin0 -> 192 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/TWO/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/TWO/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/TWO/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/TWO/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/TWO/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/TWO/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/TWO/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests/resources/submod3/.gitted/modules/TWO/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/TWO/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests/resources/submod3/.gitted/modules/TWO/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests/resources/submod3/.gitted/modules/TWO/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/TWO/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/TWO/packed-refs2
-rw-r--r--tests/resources/submod3/.gitted/modules/TWO/refs/heads/master1
-rw-r--r--tests/resources/submod3/.gitted/modules/TWO/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod3/.gitted/modules/nine/HEAD1
-rwxr-xr-xtests/resources/submod3/.gitted/modules/nine/config12
-rw-r--r--tests/resources/submod3/.gitted/modules/nine/indexbin0 -> 192 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/nine/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/nine/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/nine/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/nine/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/nine/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/nine/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/nine/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests/resources/submod3/.gitted/modules/nine/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/nine/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests/resources/submod3/.gitted/modules/nine/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests/resources/submod3/.gitted/modules/nine/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/nine/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/nine/packed-refs2
-rw-r--r--tests/resources/submod3/.gitted/modules/nine/refs/heads/master1
-rw-r--r--tests/resources/submod3/.gitted/modules/nine/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod3/.gitted/modules/sEvEn/HEAD1
-rwxr-xr-xtests/resources/submod3/.gitted/modules/sEvEn/config12
-rw-r--r--tests/resources/submod3/.gitted/modules/sEvEn/indexbin0 -> 192 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/sEvEn/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/sEvEn/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/sEvEn/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/sEvEn/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/sEvEn/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/sEvEn/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/sEvEn/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests/resources/submod3/.gitted/modules/sEvEn/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/sEvEn/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests/resources/submod3/.gitted/modules/sEvEn/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests/resources/submod3/.gitted/modules/sEvEn/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/sEvEn/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/sEvEn/packed-refs2
-rw-r--r--tests/resources/submod3/.gitted/modules/sEvEn/refs/heads/master1
-rw-r--r--tests/resources/submod3/.gitted/modules/sEvEn/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod3/.gitted/modules/six/HEAD1
-rwxr-xr-xtests/resources/submod3/.gitted/modules/six/config12
-rw-r--r--tests/resources/submod3/.gitted/modules/six/indexbin0 -> 192 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/six/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/six/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/six/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/six/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/six/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/six/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/six/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests/resources/submod3/.gitted/modules/six/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/six/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests/resources/submod3/.gitted/modules/six/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests/resources/submod3/.gitted/modules/six/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/six/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/six/packed-refs2
-rw-r--r--tests/resources/submod3/.gitted/modules/six/refs/heads/master1
-rw-r--r--tests/resources/submod3/.gitted/modules/six/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod3/.gitted/modules/three/HEAD1
-rwxr-xr-xtests/resources/submod3/.gitted/modules/three/config12
-rw-r--r--tests/resources/submod3/.gitted/modules/three/indexbin0 -> 192 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/three/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/three/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/three/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/three/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/three/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/three/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/three/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests/resources/submod3/.gitted/modules/three/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/three/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests/resources/submod3/.gitted/modules/three/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests/resources/submod3/.gitted/modules/three/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/three/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests/resources/submod3/.gitted/modules/three/packed-refs2
-rw-r--r--tests/resources/submod3/.gitted/modules/three/refs/heads/master1
-rw-r--r--tests/resources/submod3/.gitted/modules/three/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submod3/.gitted/objects/0d/db6db2835a283823cd700a1d18f17b0ba6520dbin0 -> 116 bytes
-rw-r--r--tests/resources/submod3/.gitted/objects/0e/db4f085060fea212aecc25242d4c7685cdc01dbin0 -> 107 bytes
-rw-r--r--tests/resources/submod3/.gitted/objects/33/61720a7115648e518342a6524b29cc627ea11abin0 -> 146 bytes
-rw-r--r--tests/resources/submod3/.gitted/objects/4f/6ea8092cb19f39e25cd1b21c061893b6ce17bdbin0 -> 155 bytes
-rw-r--r--tests/resources/submod3/.gitted/objects/5b/6f93e4846d705ee2daa5d60348e7fc1c6715edbin0 -> 137 bytes
-rw-r--r--tests/resources/submod3/.gitted/objects/6e/7201a58feeaa462ac9f545928fe0b961ad9495bin0 -> 141 bytes
-rw-r--r--tests/resources/submod3/.gitted/objects/6f/b39bdc90378a0a9a05a127da035e560ced3900bin0 -> 64 bytes
-rw-r--r--tests/resources/submod3/.gitted/objects/90/459b51713bde15eb97852ff22c29270752b4321
-rw-r--r--tests/resources/submod3/.gitted/objects/d0/ea28557a5f50013ac72938bc285c2d8572e50dbin0 -> 126 bytes
-rw-r--r--tests/resources/submod3/.gitted/objects/d0/fe2af38ea8925d5b4982b56354ca17816b7e11bin0 -> 87 bytes
-rw-r--r--tests/resources/submod3/.gitted/objects/e6/4b5c9e517bbb34962611400cde683690e56aa8bin0 -> 76 bytes
-rw-r--r--tests/resources/submod3/.gitted/objects/e7/b6f5010b47e84573eb670d8b31f19fccab6964bin0 -> 97 bytes
-rw-r--r--tests/resources/submod3/.gitted/refs/heads/master1
-rw-r--r--tests/resources/submod3/EIGHT/.gitted1
-rw-r--r--tests/resources/submod3/EIGHT/README.txt3
-rw-r--r--tests/resources/submod3/EIGHT/file_to_modify3
-rw-r--r--tests/resources/submod3/Five/.gitted1
-rw-r--r--tests/resources/submod3/Five/README.txt3
-rw-r--r--tests/resources/submod3/Five/file_to_modify3
-rw-r--r--tests/resources/submod3/FoUr/.gitted1
-rw-r--r--tests/resources/submod3/FoUr/README.txt3
-rw-r--r--tests/resources/submod3/FoUr/file_to_modify3
-rw-r--r--tests/resources/submod3/One/.gitted1
-rw-r--r--tests/resources/submod3/One/README.txt3
-rw-r--r--tests/resources/submod3/One/file_to_modify3
-rw-r--r--tests/resources/submod3/TEN/.gitted1
-rw-r--r--tests/resources/submod3/TEN/README.txt3
-rw-r--r--tests/resources/submod3/TEN/file_to_modify3
-rw-r--r--tests/resources/submod3/TWO/.gitted1
-rw-r--r--tests/resources/submod3/TWO/README.txt3
-rw-r--r--tests/resources/submod3/TWO/file_to_modify3
-rw-r--r--tests/resources/submod3/gitmodules30
-rw-r--r--tests/resources/submod3/nine/.gitted1
-rw-r--r--tests/resources/submod3/nine/README.txt3
-rw-r--r--tests/resources/submod3/nine/file_to_modify3
-rw-r--r--tests/resources/submod3/sEvEn/.gitted1
-rw-r--r--tests/resources/submod3/sEvEn/README.txt3
-rw-r--r--tests/resources/submod3/sEvEn/file_to_modify3
-rw-r--r--tests/resources/submod3/six/.gitted1
-rw-r--r--tests/resources/submod3/six/README.txt3
-rw-r--r--tests/resources/submod3/six/file_to_modify3
-rw-r--r--tests/resources/submod3/three/.gitted1
-rw-r--r--tests/resources/submod3/three/README.txt3
-rw-r--r--tests/resources/submod3/three/file_to_modify3
-rw-r--r--tests/resources/submodule_simple/.gitmodules3
-rw-r--r--tests/resources/submodule_simple/.gitted/HEAD1
-rw-r--r--tests/resources/submodule_simple/.gitted/config8
-rw-r--r--tests/resources/submodule_simple/.gitted/description1
-rw-r--r--tests/resources/submodule_simple/.gitted/indexbin0 -> 184 bytes
-rw-r--r--tests/resources/submodule_simple/.gitted/objects/22/9cea838964f435d4fc2c11561ddb7447003609bin0 -> 134 bytes
-rw-r--r--tests/resources/submodule_simple/.gitted/objects/5b/19f7523fbf55c96153ff5a94875583f1115a36bin0 -> 91 bytes
-rw-r--r--tests/resources/submodule_simple/.gitted/objects/a8/575e6aaececba78823993e4f11abbc6172aabdbin0 -> 174 bytes
-rw-r--r--tests/resources/submodule_simple/.gitted/objects/b4/f28943fad380f4ee3a9c6b95259b28204cc25abin0 -> 65 bytes
-rw-r--r--tests/resources/submodule_simple/.gitted/objects/d6/9ff504a3ba631f2fdb35bff93cc8cb8e85f4f8bin0 -> 92 bytes
-rw-r--r--tests/resources/submodule_simple/.gitted/packed-refs3
-rw-r--r--tests/resources/submodule_simple/.gitted/refs/heads/alternate_11
-rw-r--r--tests/resources/submodule_simple/.gitted/refs/heads/master1
-rw-r--r--tests/resources/submodule_with_path/.gitmodules3
-rw-r--r--tests/resources/submodule_with_path/.gitted/HEAD1
-rw-r--r--tests/resources/submodule_with_path/.gitted/config8
-rw-r--r--tests/resources/submodule_with_path/.gitted/indexbin0 -> 253 bytes
-rw-r--r--tests/resources/submodule_with_path/.gitted/objects/18/372280a56a54340fa600aa91315065c6c4c693bin0 -> 85 bytes
-rw-r--r--tests/resources/submodule_with_path/.gitted/objects/36/683131578275f6a8fd1c539e0d5da0d8adff26bin0 -> 63 bytes
-rw-r--r--tests/resources/submodule_with_path/.gitted/objects/89/ca686bb21bfb75dda99a02313831a0c418f921bin0 -> 161 bytes
-rw-r--r--tests/resources/submodule_with_path/.gitted/objects/b1/620ef2628d10416a84d19c783e33dc4556c9c3bin0 -> 86 bytes
-rw-r--r--tests/resources/submodule_with_path/.gitted/objects/ba/34c47dc9d3d0b1bb335b45c9d26ba1f0fc90c7bin0 -> 68 bytes
-rw-r--r--tests/resources/submodule_with_path/.gitted/objects/c8/4bf57ba2254dba216ab5c6eb1a19fe8bd0e0d6bin0 -> 127 bytes
-rw-r--r--tests/resources/submodule_with_path/.gitted/objects/d5/45fc6b40ec9e67332b6a1d2dedcbdb1bffeb6bbin0 -> 51 bytes
-rw-r--r--tests/resources/submodule_with_path/.gitted/refs/heads/master1
-rw-r--r--tests/resources/submodules-worktree-child/.gitted1
-rw-r--r--tests/resources/submodules-worktree-child/README1
-rw-r--r--tests/resources/submodules-worktree-child/branch_file.txt2
-rw-r--r--tests/resources/submodules-worktree-child/new.txt1
-rw-r--r--tests/resources/submodules-worktree-parent/.gitmodules3
-rw-r--r--tests/resources/submodules-worktree-parent/.gitted1
-rw-r--r--tests/resources/submodules-worktree-parent/deleted1
-rw-r--r--tests/resources/submodules-worktree-parent/modified1
-rw-r--r--tests/resources/submodules-worktree-parent/unmodified1
-rw-r--r--tests/resources/submodules.git/HEAD1
-rw-r--r--tests/resources/submodules.git/config4
-rw-r--r--tests/resources/submodules.git/objects/26/a3b32a9b7d97486c5557f5902e8ac94638145e2
-rw-r--r--tests/resources/submodules.git/objects/78/308c9251cf4eee8b25a76c7d2790c73d797357bin0 -> 97 bytes
-rw-r--r--tests/resources/submodules.git/objects/97/896810b3210244a62a82458b8e0819ecfc68503
-rw-r--r--tests/resources/submodules.git/objects/b6/0fd986699ba4e9e68bea07cf8e793f323ef888bin0 -> 138 bytes
-rw-r--r--tests/resources/submodules.git/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818bin0 -> 21 bytes
-rw-r--r--tests/resources/submodules.git/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4aebin0 -> 120 bytes
-rw-r--r--tests/resources/submodules.git/objects/info/packs2
-rw-r--r--tests/resources/submodules.git/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.idxbin0 -> 1156 bytes
-rw-r--r--tests/resources/submodules.git/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.packbin0 -> 228 bytes
-rw-r--r--tests/resources/submodules.git/refs/heads/master1
-rw-r--r--tests/resources/submodules/.gitted/HEAD1
-rw-r--r--tests/resources/submodules/.gitted/config6
-rw-r--r--tests/resources/submodules/.gitted/description1
-rw-r--r--tests/resources/submodules/.gitted/indexbin0 -> 408 bytes
-rw-r--r--tests/resources/submodules/.gitted/info/exclude8
-rw-r--r--tests/resources/submodules/.gitted/info/refs1
-rw-r--r--tests/resources/submodules/.gitted/logs/HEAD2
-rw-r--r--tests/resources/submodules/.gitted/logs/refs/heads/master2
-rw-r--r--tests/resources/submodules/.gitted/logs/refs/heads/submodules-worktree-parent1
-rw-r--r--tests/resources/submodules/.gitted/objects/26/a3b32a9b7d97486c5557f5902e8ac94638145e2
-rw-r--r--tests/resources/submodules/.gitted/objects/78/308c9251cf4eee8b25a76c7d2790c73d797357bin0 -> 97 bytes
-rw-r--r--tests/resources/submodules/.gitted/objects/97/896810b3210244a62a82458b8e0819ecfc68503
-rw-r--r--tests/resources/submodules/.gitted/objects/b6/0fd986699ba4e9e68bea07cf8e793f323ef888bin0 -> 138 bytes
-rw-r--r--tests/resources/submodules/.gitted/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818bin0 -> 21 bytes
-rw-r--r--tests/resources/submodules/.gitted/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4aebin0 -> 120 bytes
-rw-r--r--tests/resources/submodules/.gitted/objects/info/packs2
-rw-r--r--tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.idxbin0 -> 1156 bytes
-rw-r--r--tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.packbin0 -> 228 bytes
-rw-r--r--tests/resources/submodules/.gitted/packed-refs2
-rw-r--r--tests/resources/submodules/.gitted/refs/heads/master1
-rw-r--r--tests/resources/submodules/.gitted/refs/heads/submodules-worktree-parent1
-rw-r--r--tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/HEAD1
-rw-r--r--tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/ORIG_HEAD1
-rw-r--r--tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/commondir1
-rw-r--r--tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/gitdir1
-rw-r--r--tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/indexbin0 -> 441 bytes
-rw-r--r--tests/resources/submodules/added1
-rw-r--r--tests/resources/submodules/gitmodules6
-rw-r--r--tests/resources/submodules/ignored1
-rw-r--r--tests/resources/submodules/modified2
-rw-r--r--tests/resources/submodules/testrepo/.gitted/HEAD1
-rw-r--r--tests/resources/submodules/testrepo/.gitted/config9
-rw-r--r--tests/resources/submodules/testrepo/.gitted/description1
-rw-r--r--tests/resources/submodules/testrepo/.gitted/indexbin0 -> 256 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/info/exclude6
-rw-r--r--tests/resources/submodules/testrepo/.gitted/logs/HEAD1
-rw-r--r--tests/resources/submodules/testrepo/.gitted/logs/refs/heads/master1
-rw-r--r--tests/resources/submodules/testrepo/.gitted/logs/refs/heads/submodules-worktree-child1
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08bin0 -> 19 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7bin0 -> 51 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccdbin0 -> 119 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9bbin0 -> 21 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924dbin0 -> 21 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54bin0 -> 50 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022ccbin0 -> 23 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057bin0 -> 18 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd20452
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf36442
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60abin0 -> 119 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af1
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980bin0 -> 145 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344dbin0 -> 82 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479bin0 -> 126 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe181621
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4bin0 -> 50 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a3
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f2
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe45477503
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bdbin0 -> 28 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6bin0 -> 26 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12bin0 -> 148 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d12
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593bin0 -> 80 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f6443
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd3
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487fbin0 -> 21 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391bin0 -> 15 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0bin0 -> 21 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3bin0 -> 103 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1bin0 -> 82 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92bin0 -> 24 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765bin0 -> 82 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idxbin0 -> 46656 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.packbin0 -> 386089 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idxbin0 -> 1240 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.packbin0 -> 491 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idxbin0 -> 1240 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.packbin0 -> 498 bytes
-rw-r--r--tests/resources/submodules/testrepo/.gitted/packed-refs12
-rw-r--r--tests/resources/submodules/testrepo/.gitted/refs/heads/master1
-rw-r--r--tests/resources/submodules/testrepo/.gitted/refs/heads/submodules-worktree-child1
-rw-r--r--tests/resources/submodules/testrepo/.gitted/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/HEAD1
-rw-r--r--tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/ORIG_HEAD1
-rw-r--r--tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/commondir1
-rw-r--r--tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/gitdir1
-rw-r--r--tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/indexbin0 -> 289 bytes
-rw-r--r--tests/resources/submodules/testrepo/README1
-rw-r--r--tests/resources/submodules/testrepo/branch_file.txt2
-rw-r--r--tests/resources/submodules/testrepo/new.txt1
-rw-r--r--tests/resources/submodules/unmodified1
-rw-r--r--tests/resources/submodules/untracked1
-rw-r--r--tests/resources/super/.gitted/COMMIT_EDITMSG1
-rw-r--r--tests/resources/super/.gitted/HEAD1
-rw-r--r--tests/resources/super/.gitted/config10
-rw-r--r--tests/resources/super/.gitted/indexbin0 -> 217 bytes
-rw-r--r--tests/resources/super/.gitted/objects/51/589c218bf77a8da9e9d8dbc097d76a742726c4bin0 -> 90 bytes
-rw-r--r--tests/resources/super/.gitted/objects/79/d0d58ca6aa1688a073d280169908454cad5b91bin0 -> 132 bytes
-rw-r--r--tests/resources/super/.gitted/objects/d7/57768b570a83e80d02edcc1032db14573e5034bin0 -> 87 bytes
-rw-r--r--tests/resources/super/.gitted/refs/heads/master1
-rw-r--r--tests/resources/super/gitmodules3
-rw-r--r--tests/resources/template/branches/.gitignore2
-rw-r--r--tests/resources/template/description1
-rwxr-xr-xtests/resources/template/hooks/update.sample9
-rw-r--r--tests/resources/template/info/exclude6
-rw-r--r--tests/resources/testrepo-worktree/.gitted1
-rw-r--r--tests/resources/testrepo-worktree/README1
-rw-r--r--tests/resources/testrepo-worktree/branch_file.txt2
l---------tests/resources/testrepo-worktree/link_to_new.txt1
-rw-r--r--tests/resources/testrepo-worktree/new.txt1
-rw-r--r--tests/resources/testrepo.git/FETCH_HEAD2
-rw-r--r--tests/resources/testrepo.git/HEAD1
-rw-r--r--tests/resources/testrepo.git/HEAD_TRACKER1
-rw-r--r--tests/resources/testrepo.git/config40
-rw-r--r--tests/resources/testrepo.git/indexbin0 -> 10041 bytes
-rw-r--r--tests/resources/testrepo.git/logs/HEAD7
-rw-r--r--tests/resources/testrepo.git/logs/refs/heads/br22
-rw-r--r--tests/resources/testrepo.git/logs/refs/heads/master2
-rw-r--r--tests/resources/testrepo.git/logs/refs/heads/not-good1
-rw-r--r--tests/resources/testrepo.git/logs/refs/heads/with-empty-log0
-rw-r--r--tests/resources/testrepo.git/logs/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/testrepo.git/logs/refs/remotes/test/master2
-rw-r--r--tests/resources/testrepo.git/objects/08/b041783f40edfe12bb406c9c9a8a040177c125bin0 -> 54 bytes
-rw-r--r--tests/resources/testrepo.git/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08bin0 -> 19 bytes
-rw-r--r--tests/resources/testrepo.git/objects/18/1037049a54a1eb5fab404658a3a250b44335d7bin0 -> 51 bytes
-rw-r--r--tests/resources/testrepo.git/objects/18/10dff58d8a660512d4832e740f692884338ccdbin0 -> 119 bytes
-rw-r--r--tests/resources/testrepo.git/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fdbin0 -> 122 bytes
-rw-r--r--tests/resources/testrepo.git/objects/1b/8cbad43e867676df601306689fe7c3def5e689bin0 -> 51 bytes
-rw-r--r--tests/resources/testrepo.git/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9bbin0 -> 21 bytes
-rw-r--r--tests/resources/testrepo.git/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10bin0 -> 168 bytes
-rw-r--r--tests/resources/testrepo.git/objects/27/0b8ea76056d5cad83af921837702d3e3c2924dbin0 -> 21 bytes
-rw-r--r--tests/resources/testrepo.git/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7bin0 -> 44 bytes
-rw-r--r--tests/resources/testrepo.git/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54bin0 -> 50 bytes
-rw-r--r--tests/resources/testrepo.git/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022ccbin0 -> 23 bytes
-rw-r--r--tests/resources/testrepo.git/objects/43/da5ec3274dd061df152ff5e69853d562b018422
-rw-r--r--tests/resources/testrepo.git/objects/43/e968a905a821532069bb413801d35b200631cf4
-rw-r--r--tests/resources/testrepo.git/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057bin0 -> 18 bytes
-rw-r--r--tests/resources/testrepo.git/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd20452
-rw-r--r--tests/resources/testrepo.git/objects/4a/23e2e65ad4e31c4c9db7dc746650bfad082679bin0 -> 83 bytes
-rw-r--r--tests/resources/testrepo.git/objects/4b/22b35d44b5a4f589edf3dc89196399771796eabin0 -> 44 bytes
-rw-r--r--tests/resources/testrepo.git/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91bin0 -> 152 bytes
-rw-r--r--tests/resources/testrepo.git/objects/5b/5b025afb0b4c913b4c338a42934a3863bf36442
-rw-r--r--tests/resources/testrepo.git/objects/5d/0f8f7891e872d284beef38254882dc879b2602bin0 -> 149 bytes
-rw-r--r--tests/resources/testrepo.git/objects/5f/34cd6e3285089647165983482cf90873d50940bin0 -> 37 bytes
-rw-r--r--tests/resources/testrepo.git/objects/75/057dd4114e74cca1d750d0aee1647c903cb60abin0 -> 119 bytes
-rw-r--r--tests/resources/testrepo.git/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af1
-rw-r--r--tests/resources/testrepo.git/objects/7b/4384978d2493e851f9cca7858815fac9b10980bin0 -> 145 bytes
-rw-r--r--tests/resources/testrepo.git/objects/81/4889a078c031f61ed08ab5fa863aea9314344dbin0 -> 82 bytes
-rw-r--r--tests/resources/testrepo.git/objects/84/96071c1b46c854b31185ea97743be6a8774479bin0 -> 126 bytes
-rw-r--r--tests/resources/testrepo.git/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe2
-rw-r--r--tests/resources/testrepo.git/objects/8e/73b769e97678d684b809b163bebdae2911720f2
-rw-r--r--tests/resources/testrepo.git/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe181621
-rw-r--r--tests/resources/testrepo.git/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4bin0 -> 50 bytes
-rw-r--r--tests/resources/testrepo.git/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d22
-rw-r--r--tests/resources/testrepo.git/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a3
-rw-r--r--tests/resources/testrepo.git/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f2
-rw-r--r--tests/resources/testrepo.git/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe45477503
-rw-r--r--tests/resources/testrepo.git/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bdbin0 -> 28 bytes
-rw-r--r--tests/resources/testrepo.git/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6bin0 -> 26 bytes
-rw-r--r--tests/resources/testrepo.git/objects/ae/90f12eea699729ed24555e40b9fd669da12a12bin0 -> 148 bytes
-rw-r--r--tests/resources/testrepo.git/objects/b2/04707bbc546a1a770ef6ced37c7089cc3bfe6b2
-rw-r--r--tests/resources/testrepo.git/objects/b2/35959d89084af8d3544fbdf675e47944f86524bin0 -> 77 bytes
-rw-r--r--tests/resources/testrepo.git/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d12
-rw-r--r--tests/resources/testrepo.git/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593bin0 -> 80 bytes
-rw-r--r--tests/resources/testrepo.git/objects/b9/1e763008b10db366442469339f90a2b8400d0abin0 -> 206 bytes
-rw-r--r--tests/resources/testrepo.git/objects/bd/758010071961f28336333bc41e9c64c9a64866bin0 -> 162 bytes
-rw-r--r--tests/resources/testrepo.git/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f6443
-rw-r--r--tests/resources/testrepo.git/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd3
-rw-r--r--tests/resources/testrepo.git/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659bin0 -> 149 bytes
-rw-r--r--tests/resources/testrepo.git/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487fbin0 -> 21 bytes
-rw-r--r--tests/resources/testrepo.git/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759bin0 -> 79 bytes
-rw-r--r--tests/resources/testrepo.git/objects/db/4df74a2fc340a0d0cb0cafc0db471fdfff10482
-rw-r--r--tests/resources/testrepo.git/objects/db/793a00a5615eca1aac97e42b3a68b1acfa8bfdbin0 -> 193 bytes
-rw-r--r--tests/resources/testrepo.git/objects/db/c0be625bed24b5d8f5d9a927484f2065d321afbin0 -> 175 bytes
-rw-r--r--tests/resources/testrepo.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391bin0 -> 15 bytes
-rw-r--r--tests/resources/testrepo.git/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0bin0 -> 21 bytes
-rw-r--r--tests/resources/testrepo.git/objects/f0/a2a10243ca64f935dbe3dccb89ec8bf16bdacebin0 -> 38 bytes
-rw-r--r--tests/resources/testrepo.git/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3bin0 -> 103 bytes
-rw-r--r--tests/resources/testrepo.git/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1bin0 -> 82 bytes
-rw-r--r--tests/resources/testrepo.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92bin0 -> 24 bytes
-rw-r--r--tests/resources/testrepo.git/objects/fd/093bff70906175335656e6ce6ae05783708765bin0 -> 82 bytes
-rw-r--r--tests/resources/testrepo.git/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09bin0 -> 152 bytes
-rw-r--r--tests/resources/testrepo.git/objects/info/commit-graphbin0 -> 1940 bytes
-rw-r--r--tests/resources/testrepo.git/objects/pack/multi-pack-indexbin0 -> 47188 bytes
-rw-r--r--tests/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idxbin0 -> 46656 bytes
-rw-r--r--tests/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.packbin0 -> 386089 bytes
-rw-r--r--tests/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idxbin0 -> 1240 bytes
-rw-r--r--tests/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.packbin0 -> 491 bytes
-rw-r--r--tests/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idxbin0 -> 1240 bytes
-rw-r--r--tests/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.packbin0 -> 498 bytes
-rw-r--r--tests/resources/testrepo.git/packed-refs3
-rw-r--r--tests/resources/testrepo.git/refs/blobs/annotated_tag_to_blob1
-rw-r--r--tests/resources/testrepo.git/refs/heads/br21
-rw-r--r--tests/resources/testrepo.git/refs/heads/cannot-fetch1
-rw-r--r--tests/resources/testrepo.git/refs/heads/chomped1
-rw-r--r--tests/resources/testrepo.git/refs/heads/haacked1
-rw-r--r--tests/resources/testrepo.git/refs/heads/master1
-rw-r--r--tests/resources/testrepo.git/refs/heads/not-good1
-rw-r--r--tests/resources/testrepo.git/refs/heads/packed-test1
-rw-r--r--tests/resources/testrepo.git/refs/heads/subtrees1
-rw-r--r--tests/resources/testrepo.git/refs/heads/test1
-rw-r--r--tests/resources/testrepo.git/refs/heads/track-local1
-rw-r--r--tests/resources/testrepo.git/refs/heads/trailing1
-rw-r--r--tests/resources/testrepo.git/refs/heads/with-empty-log1
-rw-r--r--tests/resources/testrepo.git/refs/notes/fanout1
-rw-r--r--tests/resources/testrepo.git/refs/remotes/test/master1
-rw-r--r--tests/resources/testrepo.git/refs/tags/annotated_tag_to_blob1
-rw-r--r--tests/resources/testrepo.git/refs/tags/e90810b1
-rw-r--r--tests/resources/testrepo.git/refs/tags/hard_tag1
-rw-r--r--tests/resources/testrepo.git/refs/tags/point_to_blob1
-rw-r--r--tests/resources/testrepo.git/refs/tags/taggerless1
-rw-r--r--tests/resources/testrepo.git/refs/tags/test1
-rw-r--r--tests/resources/testrepo.git/refs/tags/wrapped_tag1
-rw-r--r--tests/resources/testrepo/.gitted/HEAD1
-rw-r--r--tests/resources/testrepo/.gitted/HEAD_TRACKER1
-rw-r--r--tests/resources/testrepo/.gitted/config8
-rw-r--r--tests/resources/testrepo/.gitted/indexbin0 -> 10232 bytes
-rw-r--r--tests/resources/testrepo/.gitted/logs/refs/heads/testrepo-worktree1
-rw-r--r--tests/resources/testrepo/.gitted/objects/09/9fabac3a9ea935598528c27f866e34089c2eff1
-rw-r--r--tests/resources/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08bin0 -> 19 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0ebin0 -> 163 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925bin0 -> 147 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7bin0 -> 51 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccdbin0 -> 119 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/1d/d0968be3ff95fcaecb6fa4245662db9fdc4568bin0 -> 73 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9bbin0 -> 21 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924dbin0 -> 21 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863bin0 -> 56 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54bin0 -> 50 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022ccbin0 -> 23 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057bin0 -> 18 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/45/dd856fdd4d89b884c340ba0e047752d9b085d6bin0 -> 156 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd20452
-rw-r--r--tests/resources/testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63bin0 -> 50 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/4e/886e602529caa9ab11d71f86634bd1b6e0de10bin0 -> 56 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/57/43a3ef145d3638a0fa28233ca92897117ad74fbin0 -> 160 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf36442
-rw-r--r--tests/resources/testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dccbin0 -> 50 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735bin0 -> 19 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/6b/377958d8c6a4906e8573b53672a1a23a4e8ce6bin0 -> 167 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/6b/9b767af9992b4abad5e24ffb1ba2d688ca602ebin0 -> 41 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/6f/d5c7dd2ab27b48c493023f794be09861e9045f1
-rw-r--r--tests/resources/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60abin0 -> 119 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af1
-rw-r--r--tests/resources/testrepo/.gitted/objects/7b/2417a23b63e1fdde88c80e14b33247c6e5785abin0 -> 187 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980bin0 -> 145 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344dbin0 -> 82 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479bin0 -> 126 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/87/380ae84009e9c503506c2f6143a4fc6c60bf80bin0 -> 161 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe181621
-rw-r--r--tests/resources/testrepo/.gitted/objects/97/328ac7e3bd0bcd3900cb3e7a624d71dd0df888bin0 -> 40 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/99/1f1b12603e1d78411c1b4042719f964efa7adfbin0 -> 5599 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4bin0 -> 50 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/9b/1719f5cf069568785080a0bbabbe7c377e22aebin0 -> 24 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a3
-rw-r--r--tests/resources/testrepo/.gitted/objects/a3/8d028f71eaa590febb7d716b1ca32350cf70dabin0 -> 155 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f2
-rw-r--r--tests/resources/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe45477503
-rw-r--r--tests/resources/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bdbin0 -> 28 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6bin0 -> 26 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/ad/edac69457183c8265c8a9614c1c4fed31d1ff3bin0 -> 119 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12bin0 -> 148 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/af/e4393b2b2a965f06acf2ca9658eaa01e0cd6b6bin0 -> 171 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d12
-rw-r--r--tests/resources/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593bin0 -> 80 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f6443
-rw-r--r--tests/resources/testrepo/.gitted/objects/c0/528fd6cc988c0a40ce0be11bc192fc8dc5346ebin0 -> 22 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/c3/6d8ea75da8cb510fcb0c408c1d7e53f9a99dbebin0 -> 192 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd3
-rw-r--r--tests/resources/testrepo/.gitted/objects/ce/054d4c5e3c83522aed8bc061987b46b7ede3bebin0 -> 194 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9bin0 -> 162 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46bin0 -> 38 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83bin0 -> 147 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487fbin0 -> 21 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/e3/6900c3224db4adf4c7f7a09d4ac80247978a13bin0 -> 59 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391bin0 -> 15 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0bin0 -> 21 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccfbin0 -> 64 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3bin0 -> 103 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1bin0 -> 82 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/f9/ed4af42472941da45a3ce44458455ed227a6be2
-rw-r--r--tests/resources/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92bin0 -> 24 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765bin0 -> 82 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idxbin0 -> 46656 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.packbin0 -> 386089 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idxbin0 -> 1240 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.packbin0 -> 491 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idxbin0 -> 1240 bytes
-rw-r--r--tests/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.packbin0 -> 498 bytes
-rw-r--r--tests/resources/testrepo/.gitted/packed-refs4
-rw-r--r--tests/resources/testrepo/.gitted/refs/heads/br21
-rw-r--r--tests/resources/testrepo/.gitted/refs/heads/dir1
-rw-r--r--tests/resources/testrepo/.gitted/refs/heads/executable1
-rw-r--r--tests/resources/testrepo/.gitted/refs/heads/ident1
-rw-r--r--tests/resources/testrepo/.gitted/refs/heads/long-file-name1
-rw-r--r--tests/resources/testrepo/.gitted/refs/heads/master1
-rw-r--r--tests/resources/testrepo/.gitted/refs/heads/merge-conflict1
-rw-r--r--tests/resources/testrepo/.gitted/refs/heads/packed-test1
-rw-r--r--tests/resources/testrepo/.gitted/refs/heads/subtrees1
-rw-r--r--tests/resources/testrepo/.gitted/refs/heads/test1
-rw-r--r--tests/resources/testrepo/.gitted/refs/heads/testrepo-worktree1
-rw-r--r--tests/resources/testrepo/.gitted/refs/symref1
-rw-r--r--tests/resources/testrepo/.gitted/refs/tags/e90810b1
-rw-r--r--tests/resources/testrepo/.gitted/refs/tags/foo/bar1
-rw-r--r--tests/resources/testrepo/.gitted/refs/tags/foo/foo/bar1
-rw-r--r--tests/resources/testrepo/.gitted/refs/tags/point_to_blob1
-rw-r--r--tests/resources/testrepo/.gitted/refs/tags/test1
-rw-r--r--tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/HEAD1
-rw-r--r--tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/commondir1
-rw-r--r--tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/gitdir1
-rw-r--r--tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/indexbin0 -> 369 bytes
-rw-r--r--tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/logs/HEAD1
-rw-r--r--tests/resources/testrepo2/.gitted/HEAD1
-rw-r--r--tests/resources/testrepo2/.gitted/config49
-rw-r--r--tests/resources/testrepo2/.gitted/description1
-rw-r--r--tests/resources/testrepo2/.gitted/indexbin0 -> 512 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/info/exclude6
-rw-r--r--tests/resources/testrepo2/.gitted/logs/HEAD1
-rw-r--r--tests/resources/testrepo2/.gitted/logs/refs/heads/master1
-rw-r--r--tests/resources/testrepo2/.gitted/logs/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/testrepo2/.gitted/objects/0c/37a5391bbff43c37f0d0371823a5509eed5b1dbin0 -> 134 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08bin0 -> 19 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7bin0 -> 51 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccdbin0 -> 119 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/2d/2eff63372b08adf0a9eb84109ccf7d19e2f3a2bin0 -> 125 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/36/060c58702ed4c2a40832c51758d5344201d89a2
-rw-r--r--tests/resources/testrepo2/.gitted/objects/39/6c7f1adb7925f51ba13a75f48252f44c5a14a2bin0 -> 71 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057bin0 -> 18 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd20452
-rw-r--r--tests/resources/testrepo2/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf36442
-rw-r--r--tests/resources/testrepo2/.gitted/objects/61/9f9935957e010c419cb9d15621916ddfcc0b96bin0 -> 116 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60abin0 -> 119 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/7f/043268ea43ce18e3540acaabf9e090c91965b0bin0 -> 55 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344dbin0 -> 82 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479bin0 -> 126 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a3
-rw-r--r--tests/resources/testrepo2/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f2
-rw-r--r--tests/resources/testrepo2/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bdbin0 -> 28 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6bin0 -> 26 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f6443
-rw-r--r--tests/resources/testrepo2/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd3
-rw-r--r--tests/resources/testrepo2/.gitted/objects/c4/dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90bbin0 -> 116 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391bin0 -> 15 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1bin0 -> 82 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92bin0 -> 24 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765bin0 -> 82 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idxbin0 -> 1240 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.packbin0 -> 491 bytes
-rw-r--r--tests/resources/testrepo2/.gitted/packed-refs6
-rw-r--r--tests/resources/testrepo2/.gitted/refs/heads/master1
-rw-r--r--tests/resources/testrepo2/.gitted/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/testrepo2/README1
-rw-r--r--tests/resources/testrepo2/new.txt1
-rw-r--r--tests/resources/testrepo2/subdir/README1
-rw-r--r--tests/resources/testrepo2/subdir/new.txt1
-rw-r--r--tests/resources/testrepo2/subdir/subdir2/README1
-rw-r--r--tests/resources/testrepo2/subdir/subdir2/new.txt1
-rw-r--r--tests/resources/testrepo_256.git/FETCH_HEAD2
-rw-r--r--tests/resources/testrepo_256.git/HEAD1
-rw-r--r--tests/resources/testrepo_256.git/HEAD_TRACKER1
-rw-r--r--tests/resources/testrepo_256.git/config8
-rw-r--r--tests/resources/testrepo_256.git/description1
-rwxr-xr-xtests/resources/testrepo_256.git/hooks/applypatch-msg.sample15
-rwxr-xr-xtests/resources/testrepo_256.git/hooks/commit-msg.sample24
-rwxr-xr-xtests/resources/testrepo_256.git/hooks/fsmonitor-watchman.sample173
-rwxr-xr-xtests/resources/testrepo_256.git/hooks/post-update.sample8
-rwxr-xr-xtests/resources/testrepo_256.git/hooks/pre-applypatch.sample14
-rwxr-xr-xtests/resources/testrepo_256.git/hooks/pre-commit.sample49
-rwxr-xr-xtests/resources/testrepo_256.git/hooks/pre-merge-commit.sample13
-rwxr-xr-xtests/resources/testrepo_256.git/hooks/pre-push.sample53
-rwxr-xr-xtests/resources/testrepo_256.git/hooks/pre-rebase.sample169
-rwxr-xr-xtests/resources/testrepo_256.git/hooks/pre-receive.sample24
-rwxr-xr-xtests/resources/testrepo_256.git/hooks/prepare-commit-msg.sample42
-rwxr-xr-xtests/resources/testrepo_256.git/hooks/push-to-checkout.sample78
-rwxr-xr-xtests/resources/testrepo_256.git/hooks/update.sample128
-rw-r--r--tests/resources/testrepo_256.git/indexbin0 -> 11148 bytes
-rw-r--r--tests/resources/testrepo_256.git/info/exclude6
-rw-r--r--tests/resources/testrepo_256.git/logs/HEAD7
-rw-r--r--tests/resources/testrepo_256.git/logs/refs/heads/br22
-rw-r--r--tests/resources/testrepo_256.git/logs/refs/heads/master2
-rw-r--r--tests/resources/testrepo_256.git/logs/refs/heads/not-good1
-rw-r--r--tests/resources/testrepo_256.git/logs/refs/heads/with-empty-log0
-rw-r--r--tests/resources/testrepo_256.git/logs/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/testrepo_256.git/logs/refs/remotes/test/master2
-rw-r--r--tests/resources/testrepo_256.git/object-idx1700
-rw-r--r--tests/resources/testrepo_256.git/objects/00/404e6179d86039bbc01a925bdc34ccdab778bd1d824f5562aaa319c6c8f045bin0 -> 267 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/01/18010feb81fe41b9df646d13866742a9070b56fd0ba9ab8dff828fc36c1f781
-rw-r--r--tests/resources/testrepo_256.git/objects/02/df938cfb169b0b6ba0dd16acdd727ea9364f7d48c55afed2f7dd889804065bbin0 -> 103 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/05/f7b70a01b0ade8afa5a5fcd19f12cc38faf337d10ec03ef4363d1a86f63750bin0 -> 21 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/14/bd335f9d7188c778d44eba8801fe9bda46b66593291f5b9f7cd5f8888af12f1
-rw-r--r--tests/resources/testrepo_256.git/objects/17/9496410f66032c03bd2b7e8ddfc9c8c47820fab5615cc04d904989ce800498bin0 -> 64 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/19/0a1349522cc11f8682e34acca4ce4e1ea8508dfd77c24cefd461b65cead09ebin0 -> 92 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/1b/4b74772bd83ff28bf44cda9be93f4afc2279623bb5b36c9194a660b7623c24bin0 -> 236 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/21/e1e1ebe45b2c1ef79ab050334e36a8015a546f0740bea4505e10d81a946f61bin0 -> 162 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/23/8a501cf11a036f2f248008d88e14af624bb07fced6390997a0fa6abdad950abin0 -> 143 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/26/149bf1ac4612f24b532ae50a12b15f26aace3718749624f008bde68670352abin0 -> 202 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/2d/b6069c27ca4c08b784048644c307e17d0afe29b55f6488398cb59f13feb2f2bin0 -> 238 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/33/e415b835a670bb5c3c760efa0433ac0cbd2d44679f68f2df3a9ae7014cf2a8bin0 -> 19 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/34/f79ad1c813b93d2ee11c830c2134815a31d9629e6aa9773338fedaab90976b1
-rw-r--r--tests/resources/testrepo_256.git/objects/36/eac24505d4c4405864ccf2f30d79af178374166daeceefbf11e2f058d30d60bin0 -> 21 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/39/bf1ac28cc3f8432ba7cfeeca6bfffd9a0fe641784db85de2eb0f57b7553869bin0 -> 106 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/3b/58565ee067f13349cd4f89aa396d10f71c69e168d5c48ea23de59734ec3ab1bin0 -> 38 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/43/e084a4599ca42c476919917e3db8fde0045ee66305fd5e634b0c793c536a1b1
-rw-r--r--tests/resources/testrepo_256.git/objects/47/3a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813bin0 -> 15 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/4b/c142808884e472ee6cc331b132e66ef18f564d41efb055804ec1dd28efb3f5bin0 -> 28 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/4d/f8ed86acaac5dc82b5652170996ce459d39e3a441e9759b635b0bc4ecc43fdbin0 -> 57 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/5a/2d5699fea33657b42ba98c22b7898baaa0eda205a21cafdcb7e0f94b07bb9bbin0 -> 64 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/5c/a8959deb2b8327458e0344523eb1ddeeef4bce03e35864640b452f84d268481
-rw-r--r--tests/resources/testrepo_256.git/objects/5d/bb1fff5c0094b31b25b4635ab9fbee66d65fe5dda47dd0ac5f01dd69a84c6f3
-rw-r--r--tests/resources/testrepo_256.git/objects/61/489e9e831f1d9001084d39b79f964c293db8620d679ea3596673c8a326446ebin0 -> 157 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/6d/5fd291bb0f67444e99ab492f1bf1fcdf5dca09dab24cf331e05111b4cfc1a3bin0 -> 24 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/70/30f925768d9beb65654ab8f436e3ca0a82b25eddefd237bf5a26a0441c2aa7bin0 -> 107 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/73/8ff86401dbc5af692c83e660a4d510603c3f36e782a1a32ebd0388db6411edbin0 -> 181 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/73/b4f3c4f3182e6c8dd2c98aeb2c7811556538e7673e4b325307c71685fbf5b6bin0 -> 108 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/7e/4633ae1b0e83503dbea4417f9d5ccaf22b877c5a4522b6d1d2b16090ee2f6fbin0 -> 141 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/7e/9424c06052ca33bfc599bccadee60065d8664a9af7648a1455100c4f772e1c2
-rw-r--r--tests/resources/testrepo_256.git/objects/80/91b686de8bf697ef632dda9b3179f2419717275e3bfd2055b303489dbbfa471
-rw-r--r--tests/resources/testrepo_256.git/objects/81/55958bbda08eed88c8ac908dc44452ed38911cffa54ccc06076f30a1ffb1bfbin0 -> 108 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/90/1505c3355518bee35475c5d3f23bac1dded688b2bd314cc32b7f157e1007241
-rw-r--r--tests/resources/testrepo_256.git/objects/93/1093620e5f050e2127fb0b96786ebaa9ee6535fb698ec01b5f7a800fa27cbebin0 -> 137 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/94/ed253efa9e86fc636805c294c441d08b89b455903c0c14e9b16587fec081f5bin0 -> 188 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/96/c18f0297e38d01f4b2dacddea4259aea6b2961eb0822bd2c0c3f6029030045bin0 -> 18 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/9c/cfa556cd7f73b426a7bedb7fc3a850e94f8c5ac1d71b9afa365a89005aff54bin0 -> 221 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/9d/aab17c25f647d652c72c8cc3cf4602c270a369beebc7d0b67238897bbc426b1
-rw-r--r--tests/resources/testrepo_256.git/objects/a4/813ef6708e6011e8187224297e83e4a285f58bf5eabb1db270351388603c95bin0 -> 244 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/ab/ee32b3339d1566d75613ea61f40c14bdfc5b101b60fde4f44b58dd06667640bin0 -> 63 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/ae/a29dc305d40e362df25c3fdeed5502fd56b182af01b7740d297a24459333c5bin0 -> 26 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/b1/95873b48c824d995c974a3497ade7f62d2cd818bf388775cfa721de4068ebdbin0 -> 54 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/b2/1c8c27a05a3f0bf9f0f44ebf05e11d9c591b04cfdaff7cc860310356d718271
-rw-r--r--tests/resources/testrepo_256.git/objects/b6/1b940a8cd979a32e005682c5c09c22053675e2db24ea6b4b28cc75e9c10890bin0 -> 37 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/b8/3624f6ac0995273c0034a7ab8c68929bdc91b69ad54ef94979b93eba3f6022bin0 -> 190 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/bd/f2066a28e11603a1af04157ee4aad97814279fe500340eb3465797cbd3be23bin0 -> 23 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/bf/a3b3b9a161d354e2254a444b12c412210e9689c17e51bfc318ce4bb4360f19bin0 -> 57 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/bf/cc4074ac517ed24d61b0aaa96359f304c3dc97e95f336269ed474ea846ada5bin0 -> 198 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/c2/58f010a08328a29cde33411d955520e0375fcbbcc14b7636a70f7536c32ef62
-rw-r--r--tests/resources/testrepo_256.git/objects/ca/31f7336e882a233a2943787c5e94ba024ac9a4f763cb1d9bfd8e63aa7f7269bin0 -> 182 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/cb/282e7c15fd8aeb2265cd621f5a228cb33dc84192980ca426cf9ab2a48cb9f0bin0 -> 187 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/cc/b5a03da85607c230d111abfa899655d1b00e6529101a40d42f6acb059dff9fbin0 -> 21 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/cf/84e5be57f8d5d51f136d3d137b654c602721c469c1b0a58e7e95647a9cf1c03
-rw-r--r--tests/resources/testrepo_256.git/objects/d8/8b60d2641df3656381dc8e201abb820a414de03eb63c065b06a2ab37d3f5ca1
-rw-r--r--tests/resources/testrepo_256.git/objects/de/caff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4fbin0 -> 181 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/eb/ead5965196dfaeab52b1a5d92b78e54493fdaa78f72268d4cc69b61d5feee1bin0 -> 21 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/f2/a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736bin0 -> 157 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/f2/c8da1a7c2eb49ff25c47441f0b3f387faeddde1b37d0ad2f3f6a63f5327978bin0 -> 192 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/f3/1459efb9367c5a19c9dd24c75107423d5773066922ea5e55eaeb6490979562bin0 -> 157 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/pack/pack-b87f1f214098b19ce092afb9ef6e7643653c03e7f91faa27b767e3eb8225f0f6.idxbin0 -> 1336 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/pack/pack-b87f1f214098b19ce092afb9ef6e7643653c03e7f91faa27b767e3eb8225f0f6.packbin0 -> 569 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/pack/pack-e2f07f30db7e480ea84a0e64ee791b9b270067124b2609019b74f33f256f33fa.idxbin0 -> 66216 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/pack/pack-e2f07f30db7e480ea84a0e64ee791b9b270067124b2609019b74f33f256f33fa.packbin0 -> 562646 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/pack/pack-f72bbfa35af982c2a60735152c80b24ee981cf102db76764c383f9b87935d0d3.idxbin0 -> 1336 bytes
-rw-r--r--tests/resources/testrepo_256.git/objects/pack/pack-f72bbfa35af982c2a60735152c80b24ee981cf102db76764c383f9b87935d0d3.packbin0 -> 612 bytes
-rw-r--r--tests/resources/testrepo_256.git/packed-refs3
-rw-r--r--tests/resources/testrepo_256.git/refs/blobs/annotated_tag_to_blob1
-rw-r--r--tests/resources/testrepo_256.git/refs/heads/br21
-rw-r--r--tests/resources/testrepo_256.git/refs/heads/cannot-fetch1
-rw-r--r--tests/resources/testrepo_256.git/refs/heads/chomped1
-rw-r--r--tests/resources/testrepo_256.git/refs/heads/haacked1
-rw-r--r--tests/resources/testrepo_256.git/refs/heads/master1
-rw-r--r--tests/resources/testrepo_256.git/refs/heads/not-good1
-rw-r--r--tests/resources/testrepo_256.git/refs/heads/packed-test1
-rw-r--r--tests/resources/testrepo_256.git/refs/heads/subtrees1
-rw-r--r--tests/resources/testrepo_256.git/refs/heads/test1
-rw-r--r--tests/resources/testrepo_256.git/refs/heads/track-local1
-rw-r--r--tests/resources/testrepo_256.git/refs/heads/trailing1
-rw-r--r--tests/resources/testrepo_256.git/refs/heads/with-empty-log1
-rw-r--r--tests/resources/testrepo_256.git/refs/notes/fanout1
-rw-r--r--tests/resources/testrepo_256.git/refs/remotes/test/master1
-rw-r--r--tests/resources/testrepo_256.git/refs/tags/annotated_tag_to_blob1
-rw-r--r--tests/resources/testrepo_256.git/refs/tags/e90810b1
-rw-r--r--tests/resources/testrepo_256.git/refs/tags/hard_tag1
-rw-r--r--tests/resources/testrepo_256.git/refs/tags/point_to_blob1
-rw-r--r--tests/resources/testrepo_256.git/refs/tags/taggerless1
-rw-r--r--tests/resources/testrepo_256.git/refs/tags/test1
-rw-r--r--tests/resources/testrepo_256.git/refs/tags/wrapped_tag1
-rw-r--r--tests/resources/testrepo_256/.gitted/HEAD1
-rw-r--r--tests/resources/testrepo_256/.gitted/config15
-rw-r--r--tests/resources/testrepo_256/.gitted/description1
-rw-r--r--tests/resources/testrepo_256/.gitted/indexbin0 -> 361 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/info/exclude6
-rw-r--r--tests/resources/testrepo_256/.gitted/logs/HEAD1
-rw-r--r--tests/resources/testrepo_256/.gitted/logs/refs/heads/master1
-rw-r--r--tests/resources/testrepo_256/.gitted/logs/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/00/404e6179d86039bbc01a925bdc34ccdab778bd1d824f5562aaa319c6c8f045bin0 -> 267 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/01/18010feb81fe41b9df646d13866742a9070b56fd0ba9ab8dff828fc36c1f781
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/02/df938cfb169b0b6ba0dd16acdd727ea9364f7d48c55afed2f7dd889804065bbin0 -> 103 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/05/f7b70a01b0ade8afa5a5fcd19f12cc38faf337d10ec03ef4363d1a86f63750bin0 -> 21 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/14/bd335f9d7188c778d44eba8801fe9bda46b66593291f5b9f7cd5f8888af12f1
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/17/9496410f66032c03bd2b7e8ddfc9c8c47820fab5615cc04d904989ce800498bin0 -> 64 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/19/0a1349522cc11f8682e34acca4ce4e1ea8508dfd77c24cefd461b65cead09ebin0 -> 92 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/1b/4b74772bd83ff28bf44cda9be93f4afc2279623bb5b36c9194a660b7623c24bin0 -> 236 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/21/e1e1ebe45b2c1ef79ab050334e36a8015a546f0740bea4505e10d81a946f61bin0 -> 162 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/23/8a501cf11a036f2f248008d88e14af624bb07fced6390997a0fa6abdad950abin0 -> 143 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/26/149bf1ac4612f24b532ae50a12b15f26aace3718749624f008bde68670352abin0 -> 202 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/2d/b6069c27ca4c08b784048644c307e17d0afe29b55f6488398cb59f13feb2f2bin0 -> 238 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/33/e415b835a670bb5c3c760efa0433ac0cbd2d44679f68f2df3a9ae7014cf2a8bin0 -> 19 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/34/f79ad1c813b93d2ee11c830c2134815a31d9629e6aa9773338fedaab90976b1
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/36/eac24505d4c4405864ccf2f30d79af178374166daeceefbf11e2f058d30d60bin0 -> 21 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/39/bf1ac28cc3f8432ba7cfeeca6bfffd9a0fe641784db85de2eb0f57b7553869bin0 -> 106 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/3b/58565ee067f13349cd4f89aa396d10f71c69e168d5c48ea23de59734ec3ab1bin0 -> 38 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/43/e084a4599ca42c476919917e3db8fde0045ee66305fd5e634b0c793c536a1b1
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/47/3a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813bin0 -> 15 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/4b/c142808884e472ee6cc331b132e66ef18f564d41efb055804ec1dd28efb3f5bin0 -> 28 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/4d/f8ed86acaac5dc82b5652170996ce459d39e3a441e9759b635b0bc4ecc43fdbin0 -> 57 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/5a/2d5699fea33657b42ba98c22b7898baaa0eda205a21cafdcb7e0f94b07bb9bbin0 -> 64 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/5c/a8959deb2b8327458e0344523eb1ddeeef4bce03e35864640b452f84d268481
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/5d/bb1fff5c0094b31b25b4635ab9fbee66d65fe5dda47dd0ac5f01dd69a84c6f3
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/61/489e9e831f1d9001084d39b79f964c293db8620d679ea3596673c8a326446ebin0 -> 157 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/6d/5fd291bb0f67444e99ab492f1bf1fcdf5dca09dab24cf331e05111b4cfc1a3bin0 -> 24 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/70/30f925768d9beb65654ab8f436e3ca0a82b25eddefd237bf5a26a0441c2aa7bin0 -> 107 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/73/8ff86401dbc5af692c83e660a4d510603c3f36e782a1a32ebd0388db6411edbin0 -> 181 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/73/b4f3c4f3182e6c8dd2c98aeb2c7811556538e7673e4b325307c71685fbf5b6bin0 -> 108 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/7e/4633ae1b0e83503dbea4417f9d5ccaf22b877c5a4522b6d1d2b16090ee2f6fbin0 -> 141 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/7e/9424c06052ca33bfc599bccadee60065d8664a9af7648a1455100c4f772e1c2
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/80/91b686de8bf697ef632dda9b3179f2419717275e3bfd2055b303489dbbfa471
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/81/55958bbda08eed88c8ac908dc44452ed38911cffa54ccc06076f30a1ffb1bfbin0 -> 108 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/90/1505c3355518bee35475c5d3f23bac1dded688b2bd314cc32b7f157e1007241
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/93/1093620e5f050e2127fb0b96786ebaa9ee6535fb698ec01b5f7a800fa27cbebin0 -> 137 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/94/ed253efa9e86fc636805c294c441d08b89b455903c0c14e9b16587fec081f5bin0 -> 188 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/96/c18f0297e38d01f4b2dacddea4259aea6b2961eb0822bd2c0c3f6029030045bin0 -> 18 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/9c/cfa556cd7f73b426a7bedb7fc3a850e94f8c5ac1d71b9afa365a89005aff54bin0 -> 221 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/9d/aab17c25f647d652c72c8cc3cf4602c270a369beebc7d0b67238897bbc426b1
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/a4/813ef6708e6011e8187224297e83e4a285f58bf5eabb1db270351388603c95bin0 -> 244 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/ab/ee32b3339d1566d75613ea61f40c14bdfc5b101b60fde4f44b58dd06667640bin0 -> 63 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/ae/a29dc305d40e362df25c3fdeed5502fd56b182af01b7740d297a24459333c5bin0 -> 26 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/b1/95873b48c824d995c974a3497ade7f62d2cd818bf388775cfa721de4068ebdbin0 -> 54 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/b2/1c8c27a05a3f0bf9f0f44ebf05e11d9c591b04cfdaff7cc860310356d718271
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/b6/1b940a8cd979a32e005682c5c09c22053675e2db24ea6b4b28cc75e9c10890bin0 -> 37 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/b8/3624f6ac0995273c0034a7ab8c68929bdc91b69ad54ef94979b93eba3f6022bin0 -> 190 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/bd/f2066a28e11603a1af04157ee4aad97814279fe500340eb3465797cbd3be23bin0 -> 23 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/bf/a3b3b9a161d354e2254a444b12c412210e9689c17e51bfc318ce4bb4360f19bin0 -> 57 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/bf/cc4074ac517ed24d61b0aaa96359f304c3dc97e95f336269ed474ea846ada5bin0 -> 198 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/c2/58f010a08328a29cde33411d955520e0375fcbbcc14b7636a70f7536c32ef62
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/ca/31f7336e882a233a2943787c5e94ba024ac9a4f763cb1d9bfd8e63aa7f7269bin0 -> 182 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/cb/282e7c15fd8aeb2265cd621f5a228cb33dc84192980ca426cf9ab2a48cb9f0bin0 -> 187 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/cc/b5a03da85607c230d111abfa899655d1b00e6529101a40d42f6acb059dff9fbin0 -> 21 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/cf/84e5be57f8d5d51f136d3d137b654c602721c469c1b0a58e7e95647a9cf1c03
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/d8/8b60d2641df3656381dc8e201abb820a414de03eb63c065b06a2ab37d3f5ca1
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/de/caff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4fbin0 -> 181 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/eb/ead5965196dfaeab52b1a5d92b78e54493fdaa78f72268d4cc69b61d5feee1bin0 -> 21 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/f2/a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736bin0 -> 157 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/f2/c8da1a7c2eb49ff25c47441f0b3f387faeddde1b37d0ad2f3f6a63f5327978bin0 -> 192 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/f3/1459efb9367c5a19c9dd24c75107423d5773066922ea5e55eaeb6490979562bin0 -> 157 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/pack/pack-b87f1f214098b19ce092afb9ef6e7643653c03e7f91faa27b767e3eb8225f0f6.idxbin0 -> 1336 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/pack/pack-b87f1f214098b19ce092afb9ef6e7643653c03e7f91faa27b767e3eb8225f0f6.packbin0 -> 569 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/pack/pack-e2f07f30db7e480ea84a0e64ee791b9b270067124b2609019b74f33f256f33fa.idxbin0 -> 66216 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/pack/pack-e2f07f30db7e480ea84a0e64ee791b9b270067124b2609019b74f33f256f33fa.packbin0 -> 562646 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/pack/pack-f72bbfa35af982c2a60735152c80b24ee981cf102db76764c383f9b87935d0d3.idxbin0 -> 1336 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/objects/pack/pack-f72bbfa35af982c2a60735152c80b24ee981cf102db76764c383f9b87935d0d3.packbin0 -> 612 bytes
-rw-r--r--tests/resources/testrepo_256/.gitted/packed-refs27
-rw-r--r--tests/resources/testrepo_256/.gitted/refs/heads/master1
-rw-r--r--tests/resources/testrepo_256/.gitted/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/testrepo_256/README1
-rw-r--r--tests/resources/testrepo_256/branch_file.txt2
-rw-r--r--tests/resources/testrepo_256/new.txt1
-rw-r--r--tests/resources/twowaymerge.git/HEAD1
-rw-r--r--tests/resources/twowaymerge.git/config5
-rw-r--r--tests/resources/twowaymerge.git/description1
-rw-r--r--tests/resources/twowaymerge.git/info/exclude6
-rw-r--r--tests/resources/twowaymerge.git/objects/0c/8a3f1f3d5f421cf83048c7c73ee3b55a5e0f29bin0 -> 157 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/10/2dce8e3081f398e4bdd9fd894dc85ac3ca6a67bin0 -> 54 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/17/7d8634a28e26ec7819284752757ebe01a479d5bin0 -> 80 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/1c/30b88f5f3ee66d78df6520a7de9e89b890818b3
-rw-r--r--tests/resources/twowaymerge.git/objects/1f/4c0311a24b63f6fc209a59a1e404942d4a50062
-rw-r--r--tests/resources/twowaymerge.git/objects/22/24e191514cb4bd8c566d80dac22dfcb1e9bb833
-rw-r--r--tests/resources/twowaymerge.git/objects/29/6e56023cdc034d2735fee8c0d85a659d1b07f4bin0 -> 51 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/31/51880ae2b363f1c262cf98b750c1f169a0d432bin0 -> 68 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/3b/287f8730c81d0b763c2d294618a5e32b67b4f8bin0 -> 54 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/42/b7311aa626e712891940c1ec5d5cba201946a43
-rw-r--r--tests/resources/twowaymerge.git/objects/49/6d6428b9cf92981dc9495211e6e1120fb6f2babin0 -> 46 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/59/b0cf7d74659e1cdb13305319d6d4ce2733c118bin0 -> 65 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/6a/b5d28acbf3c3bdff276f7ccfdf29c1520e542f1
-rw-r--r--tests/resources/twowaymerge.git/objects/6c/fca542b55b8b37017e6125a4b8f59a6eae6f11bin0 -> 68 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/76/5b32c65d38f04c4f287abda055818ec0f26912bin0 -> 54 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/7b/8c336c45fc6895c1c60827260fe5d798e5d2473
-rw-r--r--tests/resources/twowaymerge.git/objects/82/bf9a1a10a4b25c1f14c9607b60970705e925451
-rw-r--r--tests/resources/twowaymerge.git/objects/8b/82fb1794cb1c8c7f172ec730a4c2db0ae3e6503
-rw-r--r--tests/resources/twowaymerge.git/objects/9a/40a2f11c191f180c47e54b11567cb3c1e89b30bin0 -> 62 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/9b/219343610c88a1187c996d0dc58330b55cee282
-rw-r--r--tests/resources/twowaymerge.git/objects/9f/e06a50f4d1634d6c6879854d01d80857388706bin0 -> 65 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/a4/1a49f8f5cd9b6cb14a076bf8394881ed0b4d193
-rw-r--r--tests/resources/twowaymerge.git/objects/a9/53a018c5b10b20c86e69fef55ebc8ad4c5a4171
-rw-r--r--tests/resources/twowaymerge.git/objects/a9/cce3cd1b3efbda5b1f4a6dcc3f1570b2d3d74c1
-rw-r--r--tests/resources/twowaymerge.git/objects/bd/1732c43c68d712ad09e1d872b9be6d4b9efdc4bin0 -> 158 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/c3/7a783c20d92ac92362a78a32860f7eebf938efbin0 -> 158 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/cb/dd40facab1682754eb67f7a43f29e672903cf6bin0 -> 51 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/cd/f97fd3bb48eb3827638bb33d208f5fd32d0aa6bin0 -> 158 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/d6/f10d549cb335b9e6d38afc1f0088be69b50494bin0 -> 62 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/d9/acdc7ae7632adfeec67fa73c1e343cf4d1f47e1
-rw-r--r--tests/resources/twowaymerge.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391bin0 -> 15 bytes
-rw-r--r--tests/resources/twowaymerge.git/objects/ef/0488f0b722f0be8bcb90a7730ac7efafd1d6941
-rw-r--r--tests/resources/twowaymerge.git/objects/fc/f7e3f51c11d199ab7a78403ee4f9ccd028da25bin0 -> 62 bytes
-rw-r--r--tests/resources/twowaymerge.git/refs/heads/first-branch1
-rw-r--r--tests/resources/twowaymerge.git/refs/heads/master1
-rw-r--r--tests/resources/twowaymerge.git/refs/heads/second-branch1
-rw-r--r--tests/resources/typechanges/.gitted/HEAD1
-rw-r--r--tests/resources/typechanges/.gitted/config12
-rw-r--r--tests/resources/typechanges/.gitted/description1
-rw-r--r--tests/resources/typechanges/.gitted/indexbin0 -> 184 bytes
-rw-r--r--tests/resources/typechanges/.gitted/info/exclude6
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/HEAD1
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/config13
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/description1
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/indexbin0 -> 192 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/info/exclude6
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/packed-refs2
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/refs/heads/master1
-rw-r--r--tests/resources/typechanges/.gitted/modules/b/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/HEAD1
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/config13
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/description1
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/indexbin0 -> 192 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/info/exclude6
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/packed-refs2
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/refs/heads/master1
-rw-r--r--tests/resources/typechanges/.gitted/modules/d/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/HEAD1
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/config13
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/description1
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/indexbin0 -> 192 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/info/exclude6
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1bin0 -> 55 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484bin0 -> 53 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5bin0 -> 93 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/41/bd4bc3df978de695f67ace64c560913da11653bin0 -> 163 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/48/0095882d281ed676fe5b863569520e54a7d5c0bin0 -> 163 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/5e/4963595a9774b90524d35a807169049de8ccadbin0 -> 167 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/6b/31c659545507c381e9cd34ec508f16c04e149e2
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/73/ba924a80437097795ae839e66e187c55d3babfbin0 -> 93 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a2
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/78/9efbdadaa4a582778d4584385495559ea0994b2
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6ebin0 -> 81 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5bin0 -> 93 bytes
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/packed-refs2
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/refs/heads/master1
-rw-r--r--tests/resources/typechanges/.gitted/modules/e/refs/remotes/origin/HEAD1
-rw-r--r--tests/resources/typechanges/.gitted/objects/0d/78578795b7ca49fd8df6c4b6d27c5c02d991d8bin0 -> 76 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/0e/7ed140b514b8cae23254cb8656fe1674403affbin0 -> 162 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/0f/f461da9689266f482d8f6654a4400b4e33c586bin0 -> 486 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/18/aa7e45bbe4c3cc24a0b079696c59d36675af97bin0 -> 89 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/1b/63caae4a5ca96f78e8dfefc376c6a39a142475bin0 -> 161 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/1e/abe82aa3b2365a394f6108f24435df6e193d02bin0 -> 549 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/42/061c01a1c70097d1e4579f29a5adf40abdec95bin0 -> 24 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/46/2838cee476a87e7cff32196b66fa18ed756592bin0 -> 76 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/63/499e4ea8e096b831515ceb1d5a7593e4d87ae5bin0 -> 18 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/68/1af94e10eaf262f3ab7cb9b8fd5f4158ba4d3ebin0 -> 24 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/6a/9008602b811e69a9b7a2d83496f39a794fdeebbin0 -> 602 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/6e/ae26c90e8ccc4d16208972119c40635489c6f0bin0 -> 160 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/6f/39eabbb8a7541515e0d35971078bccb502e7e0bin0 -> 66 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/71/54d3083461536dfc71ad5542f3e65e723a06c4bin0 -> 657 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/75/56c1d893a4c0ca85ac8ac51de47ff399758729bin0 -> 226 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/76/fef844064c26d5e06c2508240dae661e7231b2bin0 -> 66 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/79/b9f23e85f55ea36a472a902e875bc1121a94cb2
-rw-r--r--tests/resources/typechanges/.gitted/objects/85/28da0ea65eacf1f74f9ed6696adbac547963adbin0 -> 451 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/8b/3726b365824ad5a07c537247f4bc73ed7d37eabin0 -> 76 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/93/3e28c1c8a68838a763d250bdf0b2c6068289c3bin0 -> 226 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/96/2710fe5b4e453e9e827945b3487c525968ec4abin0 -> 76 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/96/6cf1b3598e195b31b2cde3784f9a19f0728a6fbin0 -> 226 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/99/e8bab9ece009f0fba7eb41f850f4c12bedb9b7bin0 -> 701 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/9b/19edf33a03a0c59cdfc113bfa5c06179bf9b1a5
-rw-r--r--tests/resources/typechanges/.gitted/objects/9b/db75b73836a99e3dbeea640a81de81031fdc29bin0 -> 162 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/9d/0235c7a7edc0889a18f97a42ee6db9fe688447bin0 -> 160 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/9e/ffc457877f109b2a4319e14bee613a15f2a00dbin0 -> 226 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/a0/a9bad6f6f40325198f938a0e3ae981622d7707bin0 -> 54 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/b1/977dc4e573b812d4619754c98138c56999dc0dbin0 -> 518 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/d7/5992dd02391e128dac332dcc78d649dd9ab095bin0 -> 577 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/da/e2709d638df52212b1f43ff61797ebfedfcc7cbin0 -> 78 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/e1/152adcb9adf37ec551ada9ba377ab53aec3badbin0 -> 19 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/e4/ed436a9eb0f198cda722886a5f8d6d6c836b7bbin0 -> 225 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391bin0 -> 15 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/f2/0b79342712e0b2315647cd8227a573fd3bc46ebin0 -> 66 bytes
-rw-r--r--tests/resources/typechanges/.gitted/objects/fd/e0147e3b59f381635a3b016e3fe6dacb70779dbin0 -> 53 bytes
-rw-r--r--tests/resources/typechanges/.gitted/refs/heads/master1
-rw-r--r--tests/resources/typechanges/README.md43
-rw-r--r--tests/resources/typechanges/gitmodules0
-rw-r--r--tests/resources/unsymlinked.git/HEAD1
-rw-r--r--tests/resources/unsymlinked.git/config6
-rw-r--r--tests/resources/unsymlinked.git/description1
-rw-r--r--tests/resources/unsymlinked.git/info/exclude2
-rw-r--r--tests/resources/unsymlinked.git/objects/08/8b64704e0d6b8bd061dea879418cb5442a3fbfbin0 -> 49 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/13/a5e939bca25940c069fd2169d993dba328e30bbin0 -> 44 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/19/bf568e59e3a0b363cafb4106226e62d4a4c41cbin0 -> 29 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/58/1fadd35b4cf320d102a152f918729011604773bin0 -> 47 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/5c/87b6791e8b13da658a14d1ef7e09b5dc3bac8cbin0 -> 78 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/6f/e5f5398af85fb3de8a6aba0339b6d3bfa26a27bin0 -> 49 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/7f/ccd75616ec188b8f1b23d67506a334cc34a49dbin0 -> 132 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/80/6999882bf91d24241e4077906b9017605eb1f3bin0 -> 170 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/83/7d176303c5005505ec1e4a30231c40930c0230bin0 -> 44 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/a8/595ccca04f40818ae0155c8f9c77a230e597b62
-rw-r--r--tests/resources/unsymlinked.git/objects/cf/8f1cf5cce859c438d6cc067284cb5e161206e7bin0 -> 49 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/d5/278d05c8607ec420bfee4cf219fbc0eeebfd6abin0 -> 49 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/f4/e16fb76536591a41454194058d048d8e4dd2e9bin0 -> 44 bytes
-rw-r--r--tests/resources/unsymlinked.git/objects/f9/e65619d93fdf2673882e0a261c5e93b1a84006bin0 -> 32 bytes
-rw-r--r--tests/resources/unsymlinked.git/refs/heads/exe-file1
-rw-r--r--tests/resources/unsymlinked.git/refs/heads/master1
-rw-r--r--tests/resources/unsymlinked.git/refs/heads/reg-file1
-rw-r--r--tests/resources/userdiff/.gitted/HEAD1
-rw-r--r--tests/resources/userdiff/.gitted/config7
-rw-r--r--tests/resources/userdiff/.gitted/description1
-rw-r--r--tests/resources/userdiff/.gitted/indexbin0 -> 1558 bytes
-rw-r--r--tests/resources/userdiff/.gitted/info/refs1
-rw-r--r--tests/resources/userdiff/.gitted/objects/09/65b377c214bbe5e0d18fcdaf556df7fa7ed7c8bin0 -> 850 bytes
-rw-r--r--tests/resources/userdiff/.gitted/objects/0c/20ef1409ae1df4d5a76cdbd98d5c33ccdb6bccbin0 -> 120 bytes
-rw-r--r--tests/resources/userdiff/.gitted/objects/39/ea75107a09091ba54ff86fcc780b59477e42cdbin0 -> 854 bytes
-rw-r--r--tests/resources/userdiff/.gitted/objects/3c/c08384deae5957247bc36776ab626cc9e0582bbin0 -> 116 bytes
-rw-r--r--tests/resources/userdiff/.gitted/objects/46/8d6f2afc940e14c76347fa9af26e429a3c9044bin0 -> 851 bytes
-rw-r--r--tests/resources/userdiff/.gitted/objects/53/917973acfe0111f93c2cfaacf854be245880e8bin0 -> 846 bytes
-rw-r--r--tests/resources/userdiff/.gitted/objects/63/1d44e0c72e8cd1b594fa11d7d1ee8a6d67ff67bin0 -> 183 bytes
-rw-r--r--tests/resources/userdiff/.gitted/objects/f3/be389d351e4bcc6dcc4b5fe22134ef0f63f8bdbin0 -> 117 bytes
-rw-r--r--tests/resources/userdiff/.gitted/objects/info/packs2
-rw-r--r--tests/resources/userdiff/.gitted/objects/pack/pack-1652578900ac63564f2a24b9714529821276ceb9.idxbin0 -> 2500 bytes
-rw-r--r--tests/resources/userdiff/.gitted/objects/pack/pack-1652578900ac63564f2a24b9714529821276ceb9.packbin0 -> 7102 bytes
-rw-r--r--tests/resources/userdiff/.gitted/packed-refs2
-rw-r--r--tests/resources/userdiff/.gitted/refs/dummy-marker.txt0
-rw-r--r--tests/resources/userdiff/after/file.html41
-rw-r--r--tests/resources/userdiff/after/file.javascript108
-rw-r--r--tests/resources/userdiff/after/file.php50
-rw-r--r--tests/resources/userdiff/before/file.html41
-rw-r--r--tests/resources/userdiff/before/file.javascript109
-rw-r--r--tests/resources/userdiff/before/file.php49
-rw-r--r--tests/resources/userdiff/expected/driver/diff.html26
-rw-r--r--tests/resources/userdiff/expected/driver/diff.javascript27
-rw-r--r--tests/resources/userdiff/expected/driver/diff.php26
-rw-r--r--tests/resources/userdiff/expected/nodriver/diff.html26
-rw-r--r--tests/resources/userdiff/expected/nodriver/diff.javascript27
-rw-r--r--tests/resources/userdiff/expected/nodriver/diff.php26
-rw-r--r--tests/resources/userdiff/files/file.html41
-rw-r--r--tests/resources/userdiff/files/file.javascript108
-rw-r--r--tests/resources/userdiff/files/file.php50
-rw-r--r--tests/resources/win32-forbidden/.gitted/HEAD1
-rw-r--r--tests/resources/win32-forbidden/.gitted/config7
-rw-r--r--tests/resources/win32-forbidden/.gitted/indexbin0 -> 577 bytes
-rw-r--r--tests/resources/win32-forbidden/.gitted/info/exclude6
-rw-r--r--tests/resources/win32-forbidden/.gitted/objects/10/68072702a28a82c78902cf5bf82c3864cf4356bin0 -> 143 bytes
-rw-r--r--tests/resources/win32-forbidden/.gitted/objects/17/6a458f94e0ea5272ce67c36bf30b6be9caf623bin0 -> 28 bytes
-rw-r--r--tests/resources/win32-forbidden/.gitted/objects/2d/7445a749d25269f32724aa621cb70b196bcc40bin0 -> 105 bytes
-rw-r--r--tests/resources/win32-forbidden/.gitted/objects/34/96991d72d500af36edef68bbfcccd1661d88db3
-rw-r--r--tests/resources/win32-forbidden/.gitted/objects/8f/45aad6f23b9509f8786c617e19c127ae76609a2
-rw-r--r--tests/resources/win32-forbidden/.gitted/objects/da/623abd956bb2fd8052c708c7ed43f05d192d37bin0 -> 59 bytes
-rw-r--r--tests/resources/win32-forbidden/.gitted/objects/ea/c7621a652e5261ef1c1d3e7ae31b0d84fcbaba3
-rw-r--r--tests/resources/win32-forbidden/.gitted/refs/heads/master1
-rw-r--r--tests/util/CMakeLists.txt68
-rw-r--r--tests/util/alloc.c68
-rw-r--r--tests/util/array.c57
-rw-r--r--tests/util/assert.c117
-rw-r--r--tests/util/bitvec.c64
-rw-r--r--tests/util/copy.c162
-rw-r--r--tests/util/crlf.h30
-rw-r--r--tests/util/dirent.c306
-rw-r--r--tests/util/encoding.c42
-rw-r--r--tests/util/errors.c222
-rw-r--r--tests/util/filebuf.c267
-rw-r--r--tests/util/ftruncate.c48
-rw-r--r--tests/util/futils.c115
-rw-r--r--tests/util/gitstr.c1044
-rw-r--r--tests/util/hex.c22
-rw-r--r--tests/util/hostname.c13
-rw-r--r--tests/util/iconv.c78
-rw-r--r--tests/util/init.c54
-rw-r--r--tests/util/integer.c253
-rw-r--r--tests/util/link.c630
-rw-r--r--tests/util/memmem.c46
-rw-r--r--tests/util/mkdir.c291
-rw-r--r--tests/util/path.c768
-rw-r--r--tests/util/path/core.c343
-rw-r--r--tests/util/path/win32.c282
-rw-r--r--tests/util/pool.c62
-rw-r--r--tests/util/posix.c238
-rw-r--r--tests/util/pqueue.c150
-rw-r--r--tests/util/precompiled.c1
-rw-r--r--tests/util/precompiled.h3
-rw-r--r--tests/util/qsort.c90
-rw-r--r--tests/util/regexp.c197
-rw-r--r--tests/util/rmdir.c120
-rw-r--r--tests/util/sha1.c100
-rw-r--r--tests/util/sha256.c113
-rw-r--r--tests/util/sortedcache.c363
-rw-r--r--tests/util/stat.c113
-rw-r--r--tests/util/str/basic.c50
-rw-r--r--tests/util/str/oom.c71
-rw-r--r--tests/util/str/percent.c48
-rw-r--r--tests/util/str/quote.c87
-rw-r--r--tests/util/str/splice.c92
-rw-r--r--tests/util/string.c136
-rw-r--r--tests/util/strmap.c190
-rw-r--r--tests/util/strtol.c128
-rw-r--r--tests/util/url/http.c752
-rw-r--r--tests/util/url/joinpath.c193
-rw-r--r--tests/util/url/parse.c805
-rw-r--r--tests/util/url/pattern.c103
-rw-r--r--tests/util/url/redirect.c146
-rw-r--r--tests/util/url/scp.c317
-rw-r--r--tests/util/url/valid.c17
-rw-r--r--tests/util/utf8.c20
-rw-r--r--tests/util/vector.c430
-rw-r--r--tests/util/wildmatch.c248
-rw-r--r--tests/util/zstream.c167
11704 files changed, 379769 insertions, 0 deletions
diff --git a/.HEADER b/.HEADER
new file mode 100644
index 0000000..fd8430b
--- /dev/null
+++ b/.HEADER
@@ -0,0 +1,24 @@
+/*
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2,
+ * as published by the Free Software Foundation.
+ *
+ * In addition to the permissions in the GNU General Public License,
+ * the authors give you unlimited permission to link the compiled
+ * version of this file into combinations with other programs,
+ * and to distribute those combinations without any restriction
+ * coming from the use of this file. (The General Public License
+ * restrictions do apply in other respects; for example, they cover
+ * modification of the file, and distribution when not linked into
+ * a combined executable.)
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 0000000..d6e9cfc
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,92 @@
+# This file is an example configuration for clang-format 5.0.
+#
+# Note that this style definition should only be understood as a hint
+# for writing new code. The rules are still work-in-progress and does
+# not yet exactly match the style we have in the existing code.
+
+# C Language specifics
+Language: Cpp
+
+# Use tabs whenever we need to fill whitespace that spans at least from one tab
+# stop to the next one.
+#
+# These settings are mirrored in .editorconfig. Keep them in sync.
+UseTab: ForIndentation
+TabWidth: 8
+IndentWidth: 8
+ContinuationIndentWidth: 8
+ColumnLimit: 80
+
+AlignAfterOpenBracket: AlwaysBreak
+AlignEscapedNewlines: Left
+AlignTrailingComments: false
+
+# Allow putting parameters onto the next line
+AllowAllArgumentsOnNextLine: false
+AllowAllParametersOfDeclarationOnNextLine: false
+
+# Don't allow short braced statements to be on a single line
+# if (a) not if (a) return;
+# return;
+AllowShortBlocksOnASingleLine: false
+AllowShortFunctionsOnASingleLine: None
+AllowShortLoopsOnASingleLine: false
+AllowShortLambdasOnASingleLine: None
+
+# Pack as many parameters or arguments onto the same line as possible
+# int myFunction(int aaaaaaaaaaaa, int bbbbbbbb,
+# int cccc);
+BinPackArguments: true
+BinPackParameters: false
+
+BreakBeforeBraces: Linux
+BreakBeforeBinaryOperators: None
+BreakBeforeTernaryOperators: false
+BreakStringLiterals: false
+
+# The number of spaces before trailing line comments (// - comments).
+# This does not affect trailing block comments (/* - comments).
+SpacesBeforeTrailingComments: 1
+
+# Don't insert spaces in casts
+# x = (int32) y; not x = ( int32 ) y;
+SpacesInCStyleCastParentheses: false
+
+# Don't insert spaces inside container literals
+# var arr = [1, 2, 3]; not var arr = [ 1, 2, 3 ];
+SpacesInContainerLiterals: false
+
+# Don't insert spaces after '(' or before ')'
+# f(arg); not f( arg );
+SpacesInParentheses: false
+
+# Don't insert spaces after '[' or before ']'
+# int a[5]; not int a[ 5 ];
+SpacesInSquareBrackets: false
+
+# Insert a space after '{' and before '}' in struct initializers
+Cpp11BracedListStyle: false
+
+# A list of macros that should be interpreted as foreach loops instead of as
+# function calls.
+ForEachMacros:
+ - 'git_array_foreach'
+ - 'git_vector_foreach'
+
+# The maximum number of consecutive empty lines to keep.
+MaxEmptyLinesToKeep: 1
+
+# No empty line at the start of a block.
+KeepEmptyLinesAtTheStartOfBlocks: false
+
+# Penalties
+# This decides what order things should be done if a line is too long
+PenaltyBreakAssignment: 10
+PenaltyBreakBeforeFirstCallParameter: 30
+PenaltyBreakComment: 10
+PenaltyBreakFirstLessLess: 0
+PenaltyBreakString: 10
+PenaltyExcessCharacter: 100
+PenaltyReturnTypeOnItsOwnLine: 60
+
+SortIncludes: false
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644
index 0000000..bc6344b
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,3 @@
+{
+ "postCreateCommand": "bash .devcontainer/setup.sh"
+}
diff --git a/.devcontainer/setup.sh b/.devcontainer/setup.sh
new file mode 100755
index 0000000..c328bf3
--- /dev/null
+++ b/.devcontainer/setup.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+set -e
+
+sudo apt-get update
+sudo apt-get -y --no-install-recommends install cmake
+
+mkdir build
+cd build
+cmake .. \ No newline at end of file
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..2230fd8
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,21 @@
+; Check http://editorconfig.org/ for more informations
+root = true
+
+[*]
+indent_style = tab
+tab_width = 8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.yml]
+indent_style = space
+indent_size = 2
+
+[*.md]
+indent_style = space
+indent_size = 4
+trim_trailing_whitespace = false
+
+[*.py]
+indent_style = space
+indent_size = 4
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..3d90b7d
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,2 @@
+* text=auto
+tests/resources/** linguist-vendored
diff --git a/.github/ISSUE_TEMPLATE b/.github/ISSUE_TEMPLATE
new file mode 100644
index 0000000..717f8b9
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE
@@ -0,0 +1,19 @@
+You are opening a _bug report_ against the libgit2 project: we use
+GitHub Issues for tracking bug reports and feature requests. If you
+have a question about an API or usage, please ask on StackOverflow:
+http://stackoverflow.com/questions/tagged/libgit2. If you want to
+have high-level discussions about the libgit2 project itself, visit
+https://github.com/libgit2/discussions.
+
+Otherwise, to report a bug, please fill out the reproduction steps
+(below) and delete these introductory paragraphs. Thanks!
+
+### Reproduction steps
+
+### Expected behavior
+
+### Actual behavior
+
+### Version of libgit2 (release number or SHA1)
+
+### Operating system(s) tested
diff --git a/.github/actions/run-build/action.yml b/.github/actions/run-build/action.yml
new file mode 100644
index 0000000..41145d3
--- /dev/null
+++ b/.github/actions/run-build/action.yml
@@ -0,0 +1,45 @@
+# Run a build step in a container or directly on the Actions runner
+name: Run Build Step
+description: Run a build step in a container or directly on the Actions runner
+
+inputs:
+ command:
+ description: Command to run
+ required: true
+ type: string
+ container:
+ description: Optional container to run in
+ type: string
+ container-version:
+ description: Version of the container to run
+ type: string
+
+runs:
+ using: 'composite'
+ steps:
+ - run: |
+ if [ -n "${{ inputs.container }}" ]; then
+ docker run \
+ --rm \
+ --user "$(id -u):$(id -g)" \
+ -v "$(pwd)/source:/home/libgit2/source" \
+ -v "$(pwd)/build:/home/libgit2/build" \
+ -w /home/libgit2 \
+ -e ASAN_SYMBOLIZER_PATH \
+ -e CC \
+ -e CFLAGS \
+ -e CMAKE_GENERATOR \
+ -e CMAKE_OPTIONS \
+ -e GITTEST_NEGOTIATE_PASSWORD \
+ -e GITTEST_FLAKY_STAT \
+ -e PKG_CONFIG_PATH \
+ -e SKIP_NEGOTIATE_TESTS \
+ -e SKIP_SSH_TESTS \
+ -e TSAN_OPTIONS \
+ -e UBSAN_OPTIONS \
+ ${{ inputs.container-version }} \
+ /bin/bash -c "${{ inputs.command }}"
+ else
+ ${{ inputs.command }}
+ fi
+ shell: bash
diff --git a/.github/release.yml b/.github/release.yml
new file mode 100644
index 0000000..7a00321
--- /dev/null
+++ b/.github/release.yml
@@ -0,0 +1,29 @@
+changelog:
+ categories:
+ - title: New features
+ labels:
+ - feature
+ - title: Performance improvements
+ labels:
+ - performance
+ - title: Bug fixes
+ labels:
+ - bug
+ - title: Security fixes
+ labels:
+ - security
+ - title: Code cleanups
+ labels:
+ - cleanup
+ - title: Build and CI improvements
+ labels:
+ - build
+ - title: Documentation improvements
+ labels:
+ - documentation
+ - title: Git compatibility fixes
+ labels:
+ - git compatibility
+ - title: Other changes
+ labels:
+ - '*'
diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml
new file mode 100644
index 0000000..bf21674
--- /dev/null
+++ b/.github/workflows/benchmark.yml
@@ -0,0 +1,82 @@
+# Benchmark libgit2 against the git reference implementation.
+name: Benchmark
+
+on:
+ workflow_dispatch:
+ schedule:
+ - cron: '15 4 * * *'
+
+jobs:
+ # Run our nightly builds. We build a matrix with the various build
+ # targets and their details. Then we build either in a docker container
+ # (Linux) or on the actual hosts (macOS, Windows).
+ build:
+ # Only run scheduled workflows on the main repository; prevents people
+ # from using build minutes on their forks.
+ if: github.repository == 'libgit2/libgit2'
+
+ strategy:
+ matrix:
+ platform:
+ - name: "Linux (clang, OpenSSL)"
+ env:
+ CC: clang
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DREGEX_BACKEND=builtin -DDEPRECATE_HARD=ON -DUSE_GSSAPI=ON -DBUILD_TESTS=OFF -DBUILD_EXAMPLES=OFF -DBUILD_CLI=ON -DCMAKE_BUILD_TYPE=Release
+ CMAKE_BUILD_OPTIONS: --config Release
+ id: linux
+ os: ubuntu-latest
+ setup-script: ubuntu
+ - name: "macOS"
+ os: macos-11
+ env:
+ CC: clang
+ CMAKE_OPTIONS: -DREGEX_BACKEND=regcomp_l -DDEPRECATE_HARD=ON -DUSE_GSSAPI=ON -DBUILD_TESTS=OFF -DBUILD_EXAMPLES=OFF -DBUILD_CLI=ON -DCMAKE_BUILD_TYPE=Release
+ CMAKE_BUILD_OPTIONS: --config Release
+ PKG_CONFIG_PATH: /usr/local/opt/openssl/lib/pkgconfig
+ id: macos
+ setup-script: osx
+ - name: "Windows (amd64, Visual Studio)"
+ os: windows-2019
+ env:
+ ARCH: amd64
+ CMAKE_GENERATOR: Visual Studio 16 2019
+ CMAKE_OPTIONS: -A x64 -DDEPRECATE_HARD=ON -DBUILD_TESTS=OFF -DBUILD_EXAMPLES=OFF -DBUILD_CLI=ON -DCMAKE_BUILD_TYPE=Release
+ CMAKE_BUILD_OPTIONS: --config Release
+ id: windows
+ setup-script: win32
+ fail-fast: false
+ name: "Build ${{ matrix.platform.name }}"
+ env: ${{ matrix.platform.env }}
+ runs-on: ${{ matrix.platform.os }}
+ steps:
+ - name: Check out repository
+ uses: actions/checkout@v3
+ with:
+ path: source
+ fetch-depth: 0
+ - name: Set up benchmark environment
+ run: source/ci/setup-${{ matrix.platform.setup-script }}-benchmark.sh
+ shell: bash
+ if: matrix.platform.setup-script != ''
+ - name: Build
+ run: |
+ mkdir build && cd build
+ ../source/ci/build.sh
+ shell: bash
+ - name: Benchmark
+ run: |
+ if [[ "$(uname -s)" == MINGW* ]]; then
+ GIT2_CLI="$(cygpath -w $(pwd))\\build\\Release\\git2"
+ else
+ GIT2_CLI="$(pwd)/build/git2"
+ fi
+
+ mkdir benchmark && cd benchmark
+ ../source/tests/benchmarks/benchmark.sh --baseline-cli "git" --cli "${GIT2_CLI}" --json benchmarks.json --zip benchmarks.zip
+ shell: bash
+ - name: Upload results
+ uses: actions/upload-artifact@v2
+ with:
+ name: benchmark-${{ matrix.platform.id }}
+ path: benchmark
+ if: always()
diff --git a/.github/workflows/build-containers.yml b/.github/workflows/build-containers.yml
new file mode 100644
index 0000000..767798b
--- /dev/null
+++ b/.github/workflows/build-containers.yml
@@ -0,0 +1,72 @@
+# Generate the containers that we use for builds.
+name: Build Containers
+
+on:
+ workflow_call:
+
+env:
+ docker-registry: ghcr.io
+ docker-config-path: source/ci/docker
+
+jobs:
+ # Build the docker container images that we will use for our Linux
+ # builds. This will identify the last commit to the repository that
+ # updated the docker images, and try to download the image tagged with
+ # that sha. If it does not exist, we'll do a docker build and push
+ # the image up to GitHub Packages for the actual CI/CD runs. We tag
+ # with both the sha and "latest" so that the subsequent runs need not
+ # know the sha. Only do this on CI builds (when the event is a "push")
+ # because PR builds from forks lack permission to write packages.
+ containers:
+ strategy:
+ matrix:
+ container:
+ - name: xenial
+ - name: bionic
+ - name: focal
+ - name: docurium
+ - name: bionic-x86
+ dockerfile: bionic
+ base: multiarch/ubuntu-core:x86-bionic
+ qemu: true
+ - name: bionic-arm32
+ dockerfile: bionic
+ base: multiarch/ubuntu-core:armhf-bionic
+ qemu: true
+ - name: bionic-arm64
+ dockerfile: bionic
+ base: multiarch/ubuntu-core:arm64-bionic
+ qemu: true
+ - name: centos7
+ - name: centos8
+ runs-on: ubuntu-latest
+ name: "Create container: ${{ matrix.container.name }}"
+ steps:
+ - name: Check out repository
+ uses: actions/checkout@v3
+ with:
+ path: source
+ fetch-depth: 0
+ if: github.event_name != 'pull_request'
+ - name: Setup QEMU
+ run: docker run --rm --privileged multiarch/qemu-user-static:register --reset
+ if: matrix.container.qemu == true
+ - name: Download existing container
+ run: |
+ "${{ github.workspace }}/source/ci/getcontainer.sh" "${{ matrix.container.name }}" "${{ matrix.container.dockerfile }}"
+ env:
+ DOCKER_REGISTRY: ${{ env.docker-registry }}
+ GITHUB_TOKEN: ${{ secrets.github_token }}
+ working-directory: ${{ env.docker-config-path }}
+ if: github.event_name != 'pull_request'
+ - name: Build and publish image
+ run: |
+ if [ "${{ matrix.container.base }}" != "" ]; then
+ BASE_ARG="--build-arg BASE=${{ matrix.container.base }}"
+ fi
+ docker build -t ${{ env.docker-registry-container-sha }} --build-arg UID=$(id -u) --build-arg GID=$(id -g) ${BASE_ARG} -f ${{ env.dockerfile }} .
+ docker tag ${{ env.docker-registry-container-sha }} ${{ env.docker-registry-container-latest }}
+ docker push ${{ env.docker-registry-container-sha }}
+ docker push ${{ env.docker-registry-container-latest }}
+ working-directory: ${{ env.docker-config-path }}
+ if: github.event_name != 'pull_request' && env.docker-container-exists != 'true'
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 0000000..d84ded0
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,304 @@
+# Continuous integration and pull request validation builds for the
+# main and maintenance branches.
+name: CI Build
+
+on:
+ push:
+ branches: [ main, maint/* ]
+ pull_request:
+ branches: [ main, maint/* ]
+ workflow_dispatch:
+
+env:
+ docker-registry: ghcr.io
+ docker-config-path: source/ci/docker
+
+jobs:
+ containers:
+ uses: ./.github/workflows/build-containers.yml
+
+ # Run our CI/CD builds. We build a matrix with the various build targets
+ # and their details. Then we build either in a docker container (Linux)
+ # or on the actual hosts (macOS, Windows).
+ build:
+ needs: [ containers ]
+ strategy:
+ matrix:
+ platform:
+ - name: "Linux (Xenial, GCC, OpenSSL)"
+ id: xenial-gcc-openssl
+ container:
+ name: xenial
+ env:
+ CC: gcc
+ CMAKE_GENERATOR: Ninja
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DREGEX_BACKEND=builtin -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON -DDEBUG_STRICT_ALLOC=ON -DDEBUG_STRICT_OPEN=ON
+ os: ubuntu-latest
+ - name: Linux (Xenial, GCC, mbedTLS)
+ id: xenial-gcc-mbedtls
+ container:
+ name: xenial
+ env:
+ CC: gcc
+ CMAKE_GENERATOR: Ninja
+ CMAKE_OPTIONS: -DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
+ os: ubuntu-latest
+ - name: "Linux (Xenial, Clang, OpenSSL)"
+ id: xenial-clang-openssl
+ container:
+ name: xenial
+ env:
+ CC: clang
+ CMAKE_GENERATOR: Ninja
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
+ os: ubuntu-latest
+ - name: "Linux (Xenial, Clang, mbedTLS)"
+ id: xenial-clang-mbedtls
+ container:
+ name: xenial
+ env:
+ CC: clang
+ CMAKE_OPTIONS: -DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
+ CMAKE_GENERATOR: Ninja
+ os: ubuntu-latest
+ - name: "macOS"
+ id: macos
+ os: macos-11
+ env:
+ CC: clang
+ CMAKE_OPTIONS: -DREGEX_BACKEND=regcomp_l -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=leaks -DUSE_GSSAPI=ON
+ PKG_CONFIG_PATH: /usr/local/opt/openssl/lib/pkgconfig
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ setup-script: osx
+ - name: "Windows (amd64, Visual Studio, Schannel)"
+ id: windows-amd64-vs
+ os: windows-2019
+ setup-script: win32
+ env:
+ ARCH: amd64
+ CMAKE_GENERATOR: Visual Studio 16 2019
+ CMAKE_OPTIONS: -A x64 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON -DUSE_HTTPS=Schannel -DUSE_SSH=ON -DCMAKE_PREFIX_PATH=D:\Temp\libssh2
+ BUILD_PATH: C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\CMake\bin;D:\Temp\libssh2\bin
+ BUILD_TEMP: D:\Temp
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ - name: "Windows (x86, Visual Studio, WinHTTP)"
+ id: windows-x86-vs
+ os: windows-2019
+ setup-script: win32
+ env:
+ ARCH: x86
+ CMAKE_GENERATOR: Visual Studio 16 2019
+ CMAKE_OPTIONS: -A Win32 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON -DUSE_SHA1=HTTPS -DUSE_BUNDLED_ZLIB=ON -DUSE_SSH=ON -DCMAKE_PREFIX_PATH=D:\Temp\libssh2
+ BUILD_PATH: C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\CMake\bin;D:\Temp\libssh2\bin
+ BUILD_TEMP: D:\Temp
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ - name: "Windows (amd64, mingw, WinHTTP)"
+ id: windows-amd64-mingw
+ os: windows-2019
+ setup-script: mingw
+ env:
+ ARCH: amd64
+ CMAKE_GENERATOR: MinGW Makefiles
+ CMAKE_OPTIONS: -DDEPRECATE_HARD=ON
+ BUILD_TEMP: D:\Temp
+ BUILD_PATH: D:\Temp\mingw64\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\CMake\bin
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ - name: "Windows (x86, mingw, Schannel)"
+ id: windows-x86-mingw
+ os: windows-2019
+ setup-script: mingw
+ env:
+ ARCH: x86
+ CMAKE_GENERATOR: MinGW Makefiles
+ CMAKE_OPTIONS: -DDEPRECATE_HARD=ON -DUSE_HTTPS=Schannel
+ BUILD_TEMP: D:\Temp
+ BUILD_PATH: D:\Temp\mingw32\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\CMake\bin
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+
+ # Sanitizers
+ - name: "Sanitizer (Memory)"
+ id: memorysanitizer
+ container:
+ name: focal
+ env:
+ CC: clang-10
+ CFLAGS: -fsanitize=memory -fsanitize-memory-track-origins=2 -fsanitize-blacklist=/home/libgit2/source/script/sanitizers.supp -fno-optimize-sibling-calls -fno-omit-frame-pointer
+ CMAKE_OPTIONS: -DCMAKE_PREFIX_PATH=/usr/local/msan -DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_BUNDLED_ZLIB=ON -DUSE_SSH=ON
+ CMAKE_GENERATOR: Ninja
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ ASAN_SYMBOLIZER_PATH: /usr/bin/llvm-symbolizer-10
+ UBSAN_OPTIONS: print_stacktrace=1
+ os: ubuntu-latest
+ - name: "Sanitizer (UndefinedBehavior)"
+ id: ubsanitizer
+ container:
+ name: focal
+ env:
+ CC: clang-10
+ CFLAGS: -fsanitize=undefined,nullability -fno-sanitize-recover=undefined,nullability -fsanitize-blacklist=/home/libgit2/source/script/sanitizers.supp -fno-optimize-sibling-calls -fno-omit-frame-pointer
+ CMAKE_OPTIONS: -DCMAKE_PREFIX_PATH=/usr/local -DUSE_HTTPS=OpenSSL -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_BUNDLED_ZLIB=ON -DUSE_SSH=ON
+ CMAKE_GENERATOR: Ninja
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ ASAN_SYMBOLIZER_PATH: /usr/bin/llvm-symbolizer-10
+ UBSAN_OPTIONS: print_stacktrace=1
+ os: ubuntu-latest
+ - name: "Sanitizer (Thread)"
+ id: threadsanitizer
+ container:
+ name: focal
+ env:
+ CC: clang-10
+ CFLAGS: -fsanitize=thread -fno-optimize-sibling-calls -fno-omit-frame-pointer
+ CMAKE_OPTIONS: -DCMAKE_PREFIX_PATH=/usr/local -DUSE_HTTPS=OpenSSL -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_BUNDLED_ZLIB=ON -DUSE_SSH=ON
+ CMAKE_GENERATOR: Ninja
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ ASAN_SYMBOLIZER_PATH: /usr/bin/llvm-symbolizer-10
+ UBSAN_OPTIONS: print_stacktrace=1
+ TSAN_OPTIONS: suppressions=/home/libgit2/source/script/thread-sanitizer.supp second_deadlock_stack=1
+ os: ubuntu-latest
+
+ # Experimental: SHA256 support
+ - name: "Linux (SHA256, Xenial, Clang, OpenSSL)"
+ id: xenial-clang-openssl
+ container:
+ name: xenial
+ env:
+ CC: clang
+ CMAKE_GENERATOR: Ninja
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON -DEXPERIMENTAL_SHA256=ON
+ os: ubuntu-latest
+ - name: "macOS (SHA256)"
+ id: macos
+ os: macos-11
+ env:
+ CC: clang
+ CMAKE_OPTIONS: -DREGEX_BACKEND=regcomp_l -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=leaks -DUSE_GSSAPI=ON -DEXPERIMENTAL_SHA256=ON
+ PKG_CONFIG_PATH: /usr/local/opt/openssl/lib/pkgconfig
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ setup-script: osx
+ - name: "Windows (SHA256, amd64, Visual Studio)"
+ id: windows-amd64-vs
+ os: windows-2019
+ env:
+ ARCH: amd64
+ CMAKE_GENERATOR: Visual Studio 16 2019
+ CMAKE_OPTIONS: -A x64 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON -DEXPERIMENTAL_SHA256=ON
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ fail-fast: false
+ env: ${{ matrix.platform.env }}
+ runs-on: ${{ matrix.platform.os }}
+ name: "Build: ${{ matrix.platform.name }}"
+ steps:
+ - name: Check out repository
+ uses: actions/checkout@v3
+ with:
+ path: source
+ fetch-depth: 0
+ - name: Set up build environment
+ run: source/ci/setup-${{ matrix.platform.setup-script }}-build.sh
+ shell: bash
+ if: matrix.platform.setup-script != ''
+ - name: Setup QEMU
+ run: docker run --rm --privileged multiarch/qemu-user-static:register --reset
+ if: matrix.platform.container.qemu == true
+ - name: Download container
+ run: |
+ "${{ github.workspace }}/source/ci/getcontainer.sh" "${{ matrix.platform.container.name }}" "${{ matrix.platform.container.dockerfile }}"
+ env:
+ DOCKER_REGISTRY: ${{ env.docker-registry }}
+ GITHUB_TOKEN: ${{ secrets.github_token }}
+ working-directory: ${{ env.docker-config-path }}
+ if: matrix.platform.container.name != ''
+ - name: Create container
+ run: |
+ if [ "${{ matrix.container.base }}" != "" ]; then
+ BASE_ARG="--build-arg BASE=${{ matrix.container.base }}"
+ fi
+ docker build -t ${{ env.docker-registry-container-sha }} --build-arg UID=$(id -u) --build-arg GID=$(id -g) ${BASE_ARG} -f ${{ env.dockerfile }} .
+ working-directory: ${{ env.docker-config-path }}
+ if: matrix.platform.container.name != '' && env.docker-container-exists != 'true'
+ - name: Prepare build
+ run: mkdir build
+ - name: Build
+ uses: ./source/.github/actions/run-build
+ with:
+ command: cd build && ../source/ci/build.sh
+ container: ${{ matrix.platform.container.name }}
+ container-version: ${{ env.docker-registry-container-sha }}
+ - name: Test
+ uses: ./source/.github/actions/run-build
+ with:
+ command: cd build && ../source/ci/test.sh
+ container: ${{ matrix.platform.container.name }}
+ container-version: ${{ env.docker-registry-container-sha }}
+ - name: Upload test results
+ uses: actions/upload-artifact@v3
+ if: success() || failure()
+ with:
+ name: test-results-${{ matrix.platform.id }}
+ path: build/results_*.xml
+
+ test_results:
+ name: Test results
+ needs: [ build ]
+ if: always()
+ runs-on: ubuntu-latest
+ steps:
+ - name: Download test results
+ uses: actions/download-artifact@v3
+ - name: Generate test summary
+ uses: test-summary/action@v2
+ with:
+ paths: 'test-results-*/*.xml'
+
+
+ # Generate documentation using docurium. We'll upload the documentation
+ # as a build artifact so that it can be reviewed as part of a pull
+ # request or in a forked build. For CI builds in the main repository's
+ # main branch, we'll push the gh-pages branch back up so that it is
+ # published to our documentation site.
+ documentation:
+ name: Generate documentation
+ needs: [ containers ]
+ if: success() || failure()
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check out repository
+ uses: actions/checkout@v3
+ with:
+ path: source
+ fetch-depth: 0
+ - name: Generate documentation
+ working-directory: source
+ run: |
+ git config user.name 'Documentation Generation'
+ git config user.email 'libgit2@users.noreply.github.com'
+ git branch gh-pages origin/gh-pages
+ docker login https://${{ env.docker-registry }} -u ${{ github.actor }} -p ${{ github.token }}
+ docker run \
+ --rm \
+ -v "$(pwd):/home/libgit2" \
+ -w /home/libgit2 \
+ ${{ env.docker-registry }}/${{ github.repository }}/docurium:latest \
+ cm doc api.docurium
+ git checkout gh-pages
+ zip --exclude .git/\* --exclude .gitignore --exclude .gitattributes -r api-documentation.zip .
+ - uses: actions/upload-artifact@v3
+ name: Upload artifact
+ with:
+ name: api-documentation
+ path: source/api-documentation.zip
+ - name: Push documentation branch
+ working-directory: source
+ run: git push origin gh-pages
+ if: github.event_name == 'push' && github.repository == 'libgit2/libgit2'
diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml
new file mode 100644
index 0000000..18328a7
--- /dev/null
+++ b/.github/workflows/nightly.yml
@@ -0,0 +1,410 @@
+# Nightly build for the main branch across multiple targets.
+name: Nightly Build
+
+on:
+ workflow_dispatch:
+ schedule:
+ - cron: '15 1 * * *'
+
+env:
+ docker-registry: ghcr.io
+ docker-config-path: source/ci/docker
+
+jobs:
+ # Run our nightly builds. We build a matrix with the various build
+ # targets and their details. Then we build either in a docker container
+ # (Linux) or on the actual hosts (macOS, Windows).
+ build:
+ # Only run scheduled workflows on the main repository; prevents people
+ # from using build minutes on their forks.
+ if: github.repository == 'libgit2/libgit2'
+
+ strategy:
+ matrix:
+ platform:
+ - name: Linux (Xenial, GCC, OpenSSL)
+ container:
+ name: xenial
+ env:
+ CC: gcc
+ CMAKE_GENERATOR: Ninja
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DREGEX_BACKEND=builtin -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
+ os: ubuntu-latest
+ - name: "Linux (Xenial, GCC, mbedTLS)"
+ container:
+ name: xenial
+ env:
+ CC: gcc
+ CMAKE_GENERATOR: Ninja
+ CMAKE_OPTIONS: -DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
+ os: ubuntu-latest
+ - name: "Linux (Xenial, Clang, OpenSSL)"
+ container:
+ name: xenial
+ env:
+ CC: clang
+ CMAKE_GENERATOR: Ninja
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
+ os: ubuntu-latest
+ - name: "Linux (Xenial, Clang, mbedTLS)"
+ container:
+ name: xenial
+ env:
+ CC: clang
+ CMAKE_OPTIONS: -DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
+ CMAKE_GENERATOR: Ninja
+ os: ubuntu-latest
+ - name: "Linux (no threads)"
+ container:
+ name: xenial
+ env:
+ CC: gcc
+ CMAKE_OPTIONS: -DTHREADSAFE=OFF -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
+ CMAKE_GENERATOR: Ninja
+ os: ubuntu-latest
+ - name: "Linux (dynamically-loaded OpenSSL)"
+ container:
+ name: xenial
+ env:
+ CC: clang
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL-Dynamic -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
+ CMAKE_GENERATOR: Ninja
+ os: ubuntu-latest
+ - name: "Linux (MemorySanitizer)"
+ container:
+ name: focal
+ env:
+ CC: clang-10
+ CFLAGS: -fsanitize=memory -fsanitize-memory-track-origins=2 -fsanitize-blacklist=/home/libgit2/source/script/sanitizers.supp -fno-optimize-sibling-calls -fno-omit-frame-pointer
+ CMAKE_OPTIONS: -DCMAKE_PREFIX_PATH=/usr/local/msan -DUSE_HTTPS=mbedTLS -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_BUNDLED_ZLIB=ON -DUSE_SSH=ON
+ CMAKE_GENERATOR: Ninja
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ ASAN_SYMBOLIZER_PATH: /usr/bin/llvm-symbolizer-10
+ UBSAN_OPTIONS: print_stacktrace=1
+ os: ubuntu-latest
+ - name: "Linux (UndefinedBehaviorSanitizer)"
+ container:
+ name: focal
+ env:
+ CC: clang-10
+ CFLAGS: -fsanitize=undefined,nullability -fno-sanitize-recover=undefined,nullability -fsanitize-blacklist=/home/libgit2/source/script/sanitizers.supp -fno-optimize-sibling-calls -fno-omit-frame-pointer
+ CMAKE_OPTIONS: -DCMAKE_PREFIX_PATH=/usr/local -DUSE_HTTPS=OpenSSL -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_BUNDLED_ZLIB=ON
+ CMAKE_GENERATOR: Ninja
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ ASAN_SYMBOLIZER_PATH: /usr/bin/llvm-symbolizer-10
+ os: ubuntu-latest
+ - name: "Linux (ThreadSanitizer)"
+ container:
+ name: focal
+ env:
+ CC: clang-10
+ CFLAGS: -fsanitize=thread -fno-optimize-sibling-calls -fno-omit-frame-pointer
+ CMAKE_OPTIONS: -DCMAKE_PREFIX_PATH=/usr/local -DUSE_HTTPS=OpenSSL -DUSE_SHA1=HTTPS -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_BUNDLED_ZLIB=ON
+ CMAKE_GENERATOR: Ninja
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ ASAN_SYMBOLIZER_PATH: /usr/bin/llvm-symbolizer-10
+ TSAN_OPTIONS: suppressions=/home/libgit2/source/script/thread-sanitizer.supp second_deadlock_stack=1
+ os: ubuntu-latest
+ - name: "Linux (no mmap)"
+ container:
+ name: focal
+ env:
+ CC: clang-10
+ CFLAGS: -DNO_MMAP
+ CMAKE_OPTIONS: -DCMAKE_PREFIX_PATH=/usr/local
+ CMAKE_GENERATOR: Ninja
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ os: ubuntu-latest
+ - name: "Linux (CentOS 7)"
+ container:
+ name: centos7
+ env:
+ CMAKE_OPTIONS: -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
+ PKG_CONFIG_PATH: /usr/local/lib/pkgconfig
+ SKIP_NEGOTIATE_TESTS: true
+ os: ubuntu-latest
+ - name: "Linux (CentOS 7, dynamically-loaded OpenSSL)"
+ container:
+ name: centos7
+ env:
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL-Dynamic -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
+ PKG_CONFIG_PATH: /usr/local/lib/pkgconfig
+ SKIP_NEGOTIATE_TESTS: true
+ os: ubuntu-latest
+ - name: "Linux (CentOS 8)"
+ container:
+ name: centos8
+ env:
+ CMAKE_OPTIONS: -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON
+ PKG_CONFIG_PATH: /usr/local/lib/pkgconfig
+ SKIP_NEGOTIATE_TESTS: true
+ SKIP_SSH_TESTS: true
+ os: ubuntu-latest
+ - name: "Linux (CentOS 8, dynamically-loaded OpenSSL)"
+ container:
+ name: centos8
+ env:
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL-Dynamic -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON
+ PKG_CONFIG_PATH: /usr/local/lib/pkgconfig
+ SKIP_NEGOTIATE_TESTS: true
+ SKIP_SSH_TESTS: true
+ os: ubuntu-latest
+ - name: "macOS"
+ os: macos-11
+ env:
+ CC: clang
+ CMAKE_OPTIONS: -DREGEX_BACKEND=regcomp_l -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=leaks -DUSE_GSSAPI=ON
+ PKG_CONFIG_PATH: /usr/local/opt/openssl/lib/pkgconfig
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ setup-script: osx
+ - name: "Windows (amd64, Visual Studio, WinHTTP)"
+ os: windows-2019
+ env:
+ ARCH: amd64
+ CMAKE_GENERATOR: Visual Studio 16 2019
+ CMAKE_OPTIONS: -A x64 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON -DUSE_HTTPS=WinHTTP
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ - name: "Windows (x86, Visual Studio, WinHTTP)"
+ os: windows-2019
+ env:
+ ARCH: x86
+ CMAKE_GENERATOR: Visual Studio 16 2019
+ CMAKE_OPTIONS: -A Win32 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON -DUSE_HTTPS=WinHTTP -DUSE_SHA1=HTTPS -DUSE_BUNDLED_ZLIB=ON
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ - name: "Windows (amd64, Visual Studio, Schannel)"
+ os: windows-2019
+ env:
+ ARCH: amd64
+ CMAKE_GENERATOR: Visual Studio 16 2019
+ CMAKE_OPTIONS: -A x64 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON -DUSE_HTTPS=Schannel
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ - name: "Windows (x86, Visual Studio, Schannel)"
+ os: windows-2019
+ env:
+ ARCH: x86
+ CMAKE_GENERATOR: Visual Studio 16 2019
+ CMAKE_OPTIONS: -A Win32 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON -DUSE_HTTPS=Schannel -DUSE_BUNDLED_ZLIB=ON
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ - name: "Windows (amd64, mingw, WinHTTP)"
+ os: windows-2019
+ setup-script: mingw
+ env:
+ ARCH: amd64
+ CMAKE_GENERATOR: MinGW Makefiles
+ CMAKE_OPTIONS: -DDEPRECATE_HARD=ON
+ BUILD_TEMP: D:\Temp
+ BUILD_PATH: D:\Temp\mingw64\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\CMake\bin
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ - name: "Windows (x86, mingw, Schannel)"
+ os: windows-2019
+ setup-script: mingw
+ env:
+ ARCH: x86
+ CMAKE_GENERATOR: MinGW Makefiles
+ CMAKE_OPTIONS: -DDEPRECATE_HARD=ON -DUSE_HTTPS=Schannel
+ BUILD_TEMP: D:\Temp
+ BUILD_PATH: D:\Temp\mingw32\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Program Files (x86)\CMake\bin
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ - name: "Windows (no mmap)"
+ os: windows-2019
+ env:
+ ARCH: amd64
+ CMAKE_GENERATOR: Visual Studio 16 2019
+ CFLAGS: -DNO_MMAP
+ CMAKE_OPTIONS: -A x64 -DDEPRECATE_HARD=ON
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ - name: "Linux (Bionic, GCC, dynamically-loaded OpenSSL)"
+ container:
+ name: bionic
+ dockerfile: bionic
+ env:
+ CC: gcc
+ CMAKE_GENERATOR: Ninja
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL-Dynamic -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
+ RUN_INVASIVE_TESTS: true
+ os: ubuntu-latest
+ - name: "Linux (x86, Bionic, Clang, OpenSSL)"
+ container:
+ name: bionic-x86
+ dockerfile: bionic
+ qemu: true
+ env:
+ CC: clang
+ CMAKE_GENERATOR: Ninja
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
+ RUN_INVASIVE_TESTS: true
+ os: ubuntu-latest
+ - name: "Linux (x86, Bionic, GCC, OpenSSL)"
+ container:
+ name: bionic-x86
+ dockerfile: bionic
+ env:
+ CC: gcc
+ CMAKE_GENERATOR: Ninja
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
+ RUN_INVASIVE_TESTS: true
+ os: ubuntu-latest
+ - name: "Linux (arm32, Bionic, GCC, OpenSSL)"
+ container:
+ name: bionic-arm32
+ dockerfile: bionic
+ qemu: true
+ env:
+ CC: gcc
+ CMAKE_GENERATOR: Ninja
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_GSSAPI=ON -DUSE_SSH=ON
+ RUN_INVASIVE_TESTS: true
+ SKIP_PROXY_TESTS: true
+ GITTEST_FLAKY_STAT: true
+ os: ubuntu-latest
+ - name: "Linux (arm64, Bionic, GCC, OpenSSL)"
+ container:
+ name: bionic-arm64
+ dockerfile: bionic
+ qemu: true
+ env:
+ CC: gcc
+ CMAKE_GENERATOR: Ninja
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_GSSAPI=ON -DUSE_SSH=ON
+ RUN_INVASIVE_TESTS: true
+ SKIP_PROXY_TESTS: true
+ os: ubuntu-latest
+
+ # Experimental: SHA256 support
+ - name: "Linux (SHA256, Xenial, Clang, OpenSSL)"
+ id: xenial-clang-openssl
+ container:
+ name: xenial
+ env:
+ CC: clang
+ CMAKE_GENERATOR: Ninja
+ CMAKE_OPTIONS: -DUSE_HTTPS=OpenSSL -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=valgrind -DUSE_GSSAPI=ON -DUSE_SSH=ON
+ os: ubuntu-latest
+ - name: "macOS (SHA256)"
+ id: macos
+ os: macos-10.15
+ env:
+ CC: clang
+ CMAKE_OPTIONS: -DREGEX_BACKEND=regcomp_l -DDEPRECATE_HARD=ON -DUSE_LEAK_CHECKER=leaks -DUSE_GSSAPI=ON -DEXPERIMENTAL_SHA256=ON
+ PKG_CONFIG_PATH: /usr/local/opt/openssl/lib/pkgconfig
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ setup-script: osx
+ - name: "Windows (SHA256, amd64, Visual Studio)"
+ id: windows-amd64-vs
+ os: windows-2019
+ env:
+ ARCH: amd64
+ CMAKE_GENERATOR: Visual Studio 16 2019
+ CMAKE_OPTIONS: -A x64 -DWIN32_LEAKCHECK=ON -DDEPRECATE_HARD=ON -DEXPERIMENTAL_SHA256=ON
+ SKIP_SSH_TESTS: true
+ SKIP_NEGOTIATE_TESTS: true
+ fail-fast: false
+ env: ${{ matrix.platform.env }}
+ runs-on: ${{ matrix.platform.os }}
+ name: "Build ${{ matrix.platform.name }}"
+ steps:
+ - name: Check out repository
+ uses: actions/checkout@v3
+ with:
+ path: source
+ fetch-depth: 0
+ - name: Set up build environment
+ run: source/ci/setup-${{ matrix.platform.setup-script }}-build.sh
+ shell: bash
+ if: matrix.platform.setup-script != ''
+ - name: Setup QEMU
+ run: docker run --rm --privileged multiarch/qemu-user-static:register --reset
+ if: matrix.platform.container.qemu == true
+ - name: Download container
+ run: |
+ "${{ github.workspace }}/source/ci/getcontainer.sh" "${{ matrix.platform.container.name }}" "${{ matrix.platform.container.dockerfile }}"
+ env:
+ DOCKER_REGISTRY: ${{ env.docker-registry }}
+ GITHUB_TOKEN: ${{ secrets.github_token }}
+ working-directory: ${{ env.docker-config-path }}
+ if: matrix.platform.container.name != ''
+ - name: Create container
+ run: docker build -t ${{ env.docker-registry-container-sha }} -f ${{ env.dockerfile }} .
+ working-directory: ${{ env.docker-config-path }}
+ if: matrix.platform.container.name != '' && env.docker-container-exists != 'true'
+ - name: Prepare build
+ run: mkdir build
+ - name: Build
+ uses: ./source/.github/actions/run-build
+ with:
+ command: cd build && ../source/ci/build.sh
+ container: ${{ matrix.platform.container.name }}
+ container-version: ${{ env.docker-registry-container-sha }}
+ - name: Test
+ uses: ./source/.github/actions/run-build
+ with:
+ command: cd build && ../source/ci/test.sh
+ container: ${{ matrix.platform.container.name }}
+ container-version: ${{ env.docker-registry-container-sha }}
+
+ coverity:
+ # Only run scheduled workflows on the main repository; prevents people
+ # from using build minutes on their forks.
+ if: github.repository == 'libgit2/libgit2'
+
+ name: Coverity
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check out repository
+ uses: actions/checkout@v3
+ with:
+ path: source
+ fetch-depth: 0
+ - name: Download container
+ run: |
+ "${{ github.workspace }}/source/ci/getcontainer.sh" xenial
+ env:
+ DOCKER_REGISTRY: ${{ env.docker-registry }}
+ GITHUB_TOKEN: ${{ secrets.github_token }}
+ working-directory: ${{ env.docker-config-path }}
+ - name: Run Coverity
+ run: source/ci/coverity.sh
+ env:
+ COVERITY_TOKEN: ${{ secrets.coverity_token }}
+
+ codeql:
+ # Only run scheduled workflows on the main repository; prevents people
+ # from using build minutes on their forks.
+ if: github.repository == 'libgit2/libgit2'
+
+ name: CodeQL
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check out repository
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+
+ # Initializes the CodeQL tools for scanning.
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v2
+ with:
+ languages: 'cpp'
+
+ - name: Build
+ run: |
+ mkdir build
+ cd build
+ cmake .. -DREGEX_BACKEND=pcre -DDEPRECATE_HARD=ON -DUSE_BUNDLED_ZLIB=ON
+ cmake --build .
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v2
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1b482f0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+/build/
+.DS_Store
+*~
+.*.swp
+/tags
+CMakeSettings.json
+.vs
+.idea
diff --git a/.mailmap b/.mailmap
new file mode 100644
index 0000000..0b16a7e
--- /dev/null
+++ b/.mailmap
@@ -0,0 +1,22 @@
+Vicent Martí <vicent@github.com> Vicent Marti <tanoku@gmail.com>
+Vicent Martí <vicent@github.com> Vicent Martí <tanoku@gmail.com>
+Michael Schubert <schu@schu.io> schu <schu-github@schulog.org>
+Ben Straub <bs@github.com> Ben Straub <ben@straubnet.net>
+Ben Straub <bs@github.com> Ben Straub <bstraub@github.com>
+Carlos Martín Nieto <cmn@dwim.me> <carlos@cmartin.tk>
+Carlos Martín Nieto <cmn@dwim.me> <cmn@elego.de>
+nulltoken <emeric.fermas@gmail.com> <emeric.fermas@gmail.com>
+Scott J. Goldman <scottjg@github.com> <scottjgo@gmail.com>
+Martin Woodward <martin.woodward@microsoft.com> <martinwo@microsoft.com>
+Peter Drahoš <drahosp@gmail.com> <drahosp@gmail.com>
+Adam Roben <adam@github.com> <adam@roben.org>
+Adam Roben <adam@github.com> <adam@github.com>
+Xavier L. <xavier.l@afrosoft.tk> <xavier.l@afrosoft.ca>
+Xavier L. <xavier.l@afrosoft.tk> <xavier.l@afrosoft.tk>
+Sascha Cunz <sascha@babbelbox.org> <Sascha@BabbelBox.org>
+Authmillenon <authmillenon@googlemail.com> <martin@ucsmail.de>
+Authmillenon <authmillenon@googlemail.com> <authmillenon@googlemail.com>
+Edward Thomson <ethomson@edwardthomson.com> <ethomson@microsoft.com>
+Edward Thomson <ethomson@edwardthomson.com> <ethomson@github.com>
+J. David Ibáñez <jdavid.ibp@gmail.com> <jdavid@itaapy.com>
+Russell Belfer <rb@github.com> <arrbee@arrbee.com>
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..62d4ec9
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,27 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "(gdb) Launch",
+ "type": "cppdbg",
+ "request": "launch",
+ "program": "${workspaceFolder}/build/libgit2_tests",
+ "args": [],
+ "stopAtEntry": false,
+ "cwd": "${fileDirname}",
+ "environment": [],
+ "externalConsole": false,
+ "MIMode": "gdb",
+ "setupCommands": [
+ {
+ "description": "Enable pretty-printing for gdb",
+ "text": "-enable-pretty-printing",
+ "ignoreFailures": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 0000000..24b4d74
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,27 @@
+{
+ // See https://go.microsoft.com/fwlink/?LinkId=733558
+ // for the documentation about the tasks.json format
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "Build",
+ "type": "shell",
+ "command": "cd build && cmake --build . --parallel",
+ "group": "build",
+ "presentation": {
+ "reveal": "always",
+ "panel": "new"
+ }
+ },
+ {
+ "label": "Run Tests",
+ "type": "shell",
+ "command": "build/libgit2_tests -v",
+ "group": "test",
+ "presentation": {
+ "reveal": "always",
+ "panel": "new"
+ }
+ }
+ ]
+ }
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..784bab3
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,78 @@
+The following people contribute or have contributed
+to the libgit2 project (sorted alphabetically):
+
+Alex Budovski
+Alexei Sholik
+Andreas Ericsson
+Anton "antong" Gyllenberg
+Ankur Sethi
+Arthur Schreiber
+Ben Noordhuis
+Ben Straub
+Benjamin C Meyer
+Brian Downing
+Brian Lopez
+Carlos Martín Nieto
+Colin Timmermans
+Daniel Huckstep
+Dave Borowitz
+David Boyce
+David Glesser
+Dmitry Kakurin
+Dmitry Kovega
+Emeric Fermas
+Emmanuel Rodriguez
+Eric Myhre
+Erik Aigner
+Florian Forster
+Holger Weiss
+Ingmar Vanhassel
+J. David Ibáñez
+Jacques Germishuys
+Jakob Pfender
+Jason Penny
+Jason R. McNeil
+Jerome Lambourg
+Johan 't Hart
+John Wiegley
+Jonathan "Duke" Leto
+Julien Miotte
+Julio Espinoza-Sokal
+Justin Love
+Kelly "kelly.leahy" Leahy
+Kirill A. Shutemov
+Lambert CLARA
+Luc Bertrand
+Marc Pegon
+Marcel Groothuis
+Marco Villegas
+Michael "schu" Schubert
+Microsoft Corporation
+Olivier Ramonat
+Peter Drahoš
+Pierre Habouzit
+Pierre-Olivier Latour
+Przemyslaw Pawelczyk
+Ramsay Jones
+Robert G. Jakabosky
+Romain Geissler
+Romain Muller
+Russell Belfer
+Sakari Jokinen
+Samuel Charles "Sam" Day
+Sarath Lakshman
+Sascha Cunz
+Sascha Peilicke
+Scott Chacon
+Sebastian Schuberth
+Sergey Nikishin
+Shawn O. Pearce
+Shuhei Tanuma
+Steve Frécinaux
+Sven Strickroth
+Tim Branyen
+Tim Clem
+Tim Harder
+Torsten Bögershausen
+Trent Mick
+Vicent Marti
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..76d2714
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,145 @@
+# libgit2: the cross-platform, linkable library implementation of git.
+# See `README.md` for build instructions.
+#
+# This top-level CMakeLists.txt sets up configuration options and
+# determines which subprojects to build.
+
+cmake_minimum_required(VERSION 3.5.1)
+
+project(libgit2 VERSION "1.7.2" LANGUAGES C)
+
+# Add find modules to the path
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake")
+
+#
+# Build options
+#
+
+# Experimental features
+option(EXPERIMENTAL_SHA256 "Enable experimental SHA256 support (for R&D/testing)" OFF)
+
+# Optional subsystems
+option(BUILD_SHARED_LIBS "Build Shared Library (OFF for Static)" ON)
+option(BUILD_TESTS "Build Tests using the Clar suite" ON)
+option(BUILD_CLI "Build the command-line interface" ON)
+option(BUILD_EXAMPLES "Build library usage example apps" OFF)
+option(BUILD_FUZZERS "Build the fuzz targets" OFF)
+
+# Suggested functionality that may not be available on a per-platform basis
+option(USE_THREADS "Use threads for parallel processing when possible" ON)
+option(USE_NSEC "Support nanosecond precision file mtimes and ctimes" ON)
+
+# Backend selection
+option(USE_SSH "Link with libssh2 to enable SSH support" OFF)
+option(USE_HTTPS "Enable HTTPS support. Can be set to a specific backend" ON)
+option(USE_SHA1 "Enable SHA1. Can be set to CollisionDetection(ON)/HTTPS" ON)
+option(USE_SHA256 "Enable SHA256. Can be set to HTTPS/Builtin" ON)
+option(USE_GSSAPI "Link with libgssapi for SPNEGO auth" OFF)
+ set(USE_HTTP_PARSER "" CACHE STRING "Specifies the HTTP Parser implementation; either system or builtin.")
+# set(USE_XDIFF "" CACHE STRING "Specifies the xdiff implementation; either system or builtin.")
+ set(REGEX_BACKEND "" CACHE STRING "Regular expression implementation. One of regcomp_l, pcre2, pcre, regcomp, or builtin.")
+option(USE_BUNDLED_ZLIB "Use the bundled version of zlib. Can be set to one of Bundled(ON)/Chromium. The Chromium option requires a x86_64 processor with SSE4.2 and CLMUL" OFF)
+
+# Debugging options
+option(USE_LEAK_CHECKER "Run tests with leak checker" OFF)
+option(USE_STANDALONE_FUZZERS "Enable standalone fuzzers (compatible with gcc)" OFF)
+option(DEBUG_POOL "Enable debug pool allocator" OFF)
+option(DEBUG_STRICT_ALLOC "Enable strict allocator behavior" OFF)
+option(DEBUG_STRICT_OPEN "Enable path validation in open" OFF)
+
+# Output options
+option(SONAME "Set the (SO)VERSION of the target" ON)
+ set(LIBGIT2_FILENAME "git2" CACHE STRING "Name of the produced binary")
+option(DEPRECATE_HARD "Do not include deprecated functions in the library" OFF)
+
+# Compilation options
+option(ENABLE_WERROR "Enable compilation with -Werror" OFF)
+
+if(UNIX)
+ # NTLM client requires crypto libraries from the system HTTPS stack
+ if(NOT USE_HTTPS)
+ option(USE_NTLMCLIENT "Enable NTLM support on Unix." OFF)
+ else()
+ option(USE_NTLMCLIENT "Enable NTLM support on Unix." ON)
+ endif()
+
+ option(ENABLE_REPRODUCIBLE_BUILDS "Enable reproducible builds" OFF)
+endif()
+
+if(APPLE)
+ option(USE_ICONV "Link with and use iconv library" ON)
+endif()
+
+if(MSVC)
+ # This option must match the settings used in your program, in particular if you
+ # are linking statically
+ option(STATIC_CRT "Link the static CRT libraries" ON)
+
+ # If you want to embed a copy of libssh2 into libgit2, pass a
+ # path to libssh2
+ option(EMBED_SSH_PATH "Path to libssh2 to embed (Windows)" OFF)
+
+ # Enable leak checking using the debugging C runtime.
+ option(WIN32_LEAKCHECK "Enable leak reporting via crtdbg" OFF)
+endif()
+
+if(NOT CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE)
+ set(CMAKE_BUILD_TYPE "Debug" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE)
+endif()
+
+
+# Modules
+
+include(CheckLibraryExists)
+include(CheckFunctionExists)
+include(CheckSymbolExists)
+include(CheckStructHasMember)
+include(CheckPrototypeDefinitionSafe)
+include(AddCFlagIfSupported)
+include(FindPkgLibraries)
+include(FindThreads)
+include(FindStatNsec)
+include(Findfutimens)
+include(GNUInstallDirs)
+include(IdeSplitSources)
+include(FeatureSummary)
+include(EnableWarnings)
+include(DefaultCFlags)
+include(ExperimentalFeatures)
+
+
+#
+# Subdirectories
+#
+
+add_subdirectory(src)
+
+if(BUILD_TESTS)
+ enable_testing()
+ add_subdirectory(tests)
+endif()
+
+if(BUILD_EXAMPLES)
+ add_subdirectory(examples)
+endif()
+
+if(BUILD_FUZZERS)
+ if((BUILD_TESTS OR BUILD_EXAMPLES) AND NOT USE_STANDALONE_FUZZERS)
+ message(FATAL_ERROR "Cannot build the fuzzer and the tests or examples together")
+ endif()
+ add_subdirectory(fuzzers)
+endif()
+
+
+# Export for people who use us as a dependency
+
+if(NOT "${CMAKE_CURRENT_SOURCE_DIR}" STREQUAL "${CMAKE_SOURCE_DIR}")
+ set(LIBGIT2_DEPENDENCY_OBJECTS ${LIBGIT2_DEPENDENCY_OBJECTS} PARENT_SCOPE)
+ set(LIBGIT2_SYSTEM_LIBS ${LIBGIT2_SYSTEM_LIBS} PARENT_SCOPE)
+endif()
+
+
+# Summary
+
+feature_summary(WHAT ENABLED_FEATURES DESCRIPTION "Enabled features:")
+feature_summary(WHAT DISABLED_FEATURES DESCRIPTION "Disabled features:")
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..25c8d8c
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,1216 @@
+ libgit2 is Copyright (C) the libgit2 contributors,
+ unless otherwise stated. See the AUTHORS file for details.
+
+ Note that the only valid version of the GPL as far as this project
+ is concerned is _this_ particular version of the license (ie v2, not
+ v2.2 or v3.x or whatever), unless explicitly otherwise stated.
+
+----------------------------------------------------------------------
+
+ LINKING EXCEPTION
+
+ In addition to the permissions in the GNU General Public License,
+ the authors give you unlimited permission to link the compiled
+ version of this library into combinations with other programs,
+ and to distribute those combinations without any restriction
+ coming from the use of this file. (The General Public License
+ restrictions do apply in other respects; for example, they cover
+ modification of the file, and distribution when not linked into
+ a combined executable.)
+
+----------------------------------------------------------------------
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
+
+----------------------------------------------------------------------
+
+The bundled ZLib code is licensed under the ZLib license:
+
+Copyright (C) 1995-2010 Jean-loup Gailly and Mark Adler
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+
+ Jean-loup Gailly Mark Adler
+ jloup@gzip.org madler@alumni.caltech.edu
+
+----------------------------------------------------------------------
+
+The Clar framework is licensed under the ISC license:
+
+Copyright (c) 2011-2015 Vicent Marti
+
+Permission to use, copy, modify, and/or distribute this software for any
+purpose with or without fee is hereby granted, provided that the above
+copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+----------------------------------------------------------------------
+
+The bundled PCRE implementation (deps/pcre/) is licensed under the BSD
+license.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ * Neither the name of the University of Cambridge nor the name of Google
+ Inc. nor the names of their contributors may be used to endorse or
+ promote products derived from this software without specific prior
+ written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+
+The bundled winhttp definition files (deps/winhttp/) are licensed under
+the GNU LGPL (available at the end of this file).
+
+Copyright (C) 2007 Francois Gouget
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+
+----------------------------------------------------------------------
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
+
+----------------------------------------------------------------------
+
+The bundled SHA1 collision detection code is licensed under the MIT license:
+
+MIT License
+
+Copyright (c) 2017:
+ Marc Stevens
+ Cryptology Group
+ Centrum Wiskunde & Informatica
+ P.O. Box 94079, 1090 GB Amsterdam, Netherlands
+ marc@marc-stevens.nl
+
+ Dan Shumow
+ Microsoft Research
+ danshu@microsoft.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+----------------------------------------------------------------------
+
+The bundled wildmatch code is licensed under the BSD license:
+
+Copyright Rich Salz.
+All rights reserved.
+
+Redistribution and use in any form are permitted provided that the
+following restrictions are are met:
+
+1. Source distributions must retain this entire copyright notice
+ and comment.
+2. Binary distributions must include the acknowledgement ``This
+ product includes software developed by Rich Salz'' in the
+ documentation or other materials provided with the
+ distribution. This must not be represented as an endorsement
+ or promotion without specific prior written permission.
+3. The origin of this software must not be misrepresented, either
+ by explicit claim or by omission. Credits must appear in the
+ source and documentation.
+4. Altered versions must be plainly marked as such in the source
+ and documentation and must not be misrepresented as being the
+ original software.
+
+THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+----------------------------------------------------------------------
+
+Portions of the OpenSSL headers are included under the OpenSSL license:
+
+Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+All rights reserved.
+
+This package is an SSL implementation written
+by Eric Young (eay@cryptsoft.com).
+The implementation was written so as to conform with Netscapes SSL.
+
+This library is free for commercial and non-commercial use as long as
+the following conditions are aheared to. The following conditions
+apply to all code found in this distribution, be it the RC4, RSA,
+lhash, DES, etc., code; not just the SSL code. The SSL documentation
+included with this distribution is covered by the same copyright terms
+except that the holder is Tim Hudson (tjh@cryptsoft.com).
+
+Copyright remains Eric Young's, and as such any Copyright notices in
+the code are not to be removed.
+If this package is used in a product, Eric Young should be given attribution
+as the author of the parts of the library used.
+This can be in the form of a textual message at program startup or
+in documentation (online or textual) provided with the package.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+ "This product includes cryptographic software written by
+ Eric Young (eay@cryptsoft.com)"
+ The word 'cryptographic' can be left out if the rouines from the library
+ being used are not cryptographic related :-).
+4. If you include any Windows specific code (or a derivative thereof) from
+ the apps directory (application code) you must include an acknowledgement:
+ "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+
+THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
+
+The licence and distribution terms for any publically available version or
+derivative of this code cannot be changed. i.e. this code cannot simply be
+copied and put under another distribution licence
+[including the GNU Public Licence.]
+
+====================================================================
+Copyright (c) 1998-2007 The OpenSSL Project. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+3. All advertising materials mentioning features or use of this
+ software must display the following acknowledgment:
+ "This product includes software developed by the OpenSSL Project
+ for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+
+4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ endorse or promote products derived from this software without
+ prior written permission. For written permission, please contact
+ openssl-core@openssl.org.
+
+5. Products derived from this software may not be called "OpenSSL"
+ nor may "OpenSSL" appear in their names without prior written
+ permission of the OpenSSL Project.
+
+6. Redistributions of any form whatsoever must retain the following
+ acknowledgment:
+ "This product includes software developed by the OpenSSL Project
+ for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+
+THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+
+The xoroshiro256** implementation is licensed in the public domain:
+
+Written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org)
+
+To the extent possible under law, the author has dedicated all copyright
+and related and neighboring rights to this software to the public domain
+worldwide. This software is distributed without any warranty.
+
+See <http://creativecommons.org/publicdomain/zero/1.0/>.
+
+----------------------------------------------------------------------
+
+The built-in SHA256 support (src/hash/rfc6234) is taken from RFC 6234
+under the following license:
+
+Copyright (c) 2011 IETF Trust and the persons identified as
+authors of the code. All rights reserved.
+
+Redistribution and use in source and binary forms, with or
+without modification, are permitted provided that the following
+conditions are met:
+
+- Redistributions of source code must retain the above
+ copyright notice, this list of conditions and
+ the following disclaimer.
+
+- Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+- Neither the name of Internet Society, IETF or IETF Trust, nor
+ the names of specific contributors, may be used to endorse or
+ promote products derived from this software without specific
+ prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+----------------------------------------------------------------------
+
+The built-in git_fs_path_basename_r() function is based on the
+Android implementation, BSD licensed:
+
+Copyright (C) 2008 The Android Open Source Project
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..711e848
--- /dev/null
+++ b/README.md
@@ -0,0 +1,440 @@
+libgit2 - the Git linkable library
+==================================
+
+| Build Status | |
+| ------------ | - |
+| **main** branch CI builds | [![CI Build](https://github.com/libgit2/libgit2/workflows/CI%20Build/badge.svg?event=push)](https://github.com/libgit2/libgit2/actions?query=workflow%3A%22CI+Build%22+event%3Apush) |
+| **v1.7 branch** CI builds | [![CI Build](https://github.com/libgit2/libgit2/workflows/CI%20Build/badge.svg?branch=maint%2Fv1.7&event=push)](https://github.com/libgit2/libgit2/actions?query=workflow%3A%22CI+Build%22+event%3Apush+branch%3Amaint%2Fv1.7) |
+| **v1.6 branch** CI builds | [![CI Build](https://github.com/libgit2/libgit2/workflows/CI%20Build/badge.svg?branch=maint%2Fv1.6&event=push)](https://github.com/libgit2/libgit2/actions?query=workflow%3A%22CI+Build%22+event%3Apush+branch%3Amaint%2Fv1.6) |
+| **Nightly** builds | [![Nightly Build](https://github.com/libgit2/libgit2/workflows/Nightly%20Build/badge.svg)](https://github.com/libgit2/libgit2/actions?query=workflow%3A%22Nightly+Build%22) [![Coverity Scan Status](https://scan.coverity.com/projects/639/badge.svg)](https://scan.coverity.com/projects/639) |
+
+`libgit2` is a portable, pure C implementation of the Git core methods
+provided as a linkable library with a solid API, allowing to build Git
+functionality into your application. Language bindings like
+[Rugged](https://github.com/libgit2/rugged) (Ruby),
+[LibGit2Sharp](https://github.com/libgit2/libgit2sharp) (.NET),
+[pygit2](http://www.pygit2.org/) (Python) and
+[NodeGit](http://nodegit.org) (Node) allow you to build Git tooling
+in your favorite language.
+
+`libgit2` is used to power Git GUI clients like
+[GitKraken](https://gitkraken.com/) and [gmaster](https://gmaster.io/)
+and on Git hosting providers like [GitHub](https://github.com/),
+[GitLab](https://gitlab.com/) and
+[Azure DevOps](https://azure.com/devops).
+We perform the merge every time you click "merge pull request".
+
+`libgit2` is licensed under a **very permissive license** (GPLv2 with a special
+Linking Exception). This means that you can link against the library with any
+kind of software without making that software fall under the GPL.
+Changes to libgit2 would still be covered under its GPL license.
+Additionally, the example code has been released to the public domain (see the
+[separate license](examples/COPYING) for more information).
+
+Table of Contents
+=================
+
+* [Using libgit2](#using-libgit2)
+* [Quick Start](#quick-start)
+* [Getting Help](#getting-help)
+* [What It Can Do](#what-it-can-do)
+* [Optional dependencies](#optional-dependencies)
+* [Initialization](#initialization)
+* [Threading](#threading)
+* [Conventions](#conventions)
+* [Building libgit2 - Using CMake](#building-libgit2---using-cmake)
+ * [Building](#building)
+ * [Installation](#installation)
+ * [Advanced Usage](#advanced-usage)
+ * [Compiler and linker options](#compiler-and-linker-options)
+ * [MacOS X](#macos-x)
+ * [Android](#android)
+ * [MinGW](#mingw)
+* [Language Bindings](#language-bindings)
+* [How Can I Contribute?](#how-can-i-contribute)
+* [License](#license)
+
+Using libgit2
+=============
+
+Most of these instructions assume that you're writing an application
+in C and want to use libgit2 directly. If you're _not_ using C,
+and you're writing in a different language or platform like .NET,
+Node.js, or Ruby, then there is probably a
+"[language binding](#language-bindings)" that you can use to take care
+of the messy tasks of calling into native code.
+
+But if you _do_ want to use libgit2 directly - because you're building
+an application in C - then you may be able use an existing binary.
+There are packages for the
+[vcpkg](https://github.com/Microsoft/vcpkg) and
+[conan](https://conan.io/center/libgit2)
+package managers. And libgit2 is available in
+[Homebrew](https://formulae.brew.sh/formula/libgit2) and most Linux
+distributions.
+
+However, these versions _may_ be outdated and we recommend using the
+latest version if possible. Thankfully libgit2 is not hard to compile.
+
+Quick Start
+===========
+
+**Prerequisites** for building libgit2:
+
+1. [CMake](https://cmake.org/), and is recommended to be installed into
+ your `PATH`.
+2. [Python](https://www.python.org) is used by our test framework, and
+ should be installed into your `PATH`.
+3. C compiler: libgit2 is C90 and should compile on most compilers.
+ * Windows: Visual Studio is recommended
+ * Mac: Xcode is recommended
+ * Unix: gcc or clang is recommended.
+
+**Build**
+
+1. Create a build directory beneath the libgit2 source directory, and change
+ into it: `mkdir build && cd build`
+2. Create the cmake build environment: `cmake ..`
+3. Build libgit2: `cmake --build .`
+
+Trouble with these steps? Read our [troubleshooting guide](docs/troubleshooting.md).
+More detailed build guidance is available below.
+
+Getting Help
+============
+
+**Chat with us**
+
+- via IRC: join [#libgit2](https://web.libera.chat/#libgit2) on
+ [libera](https://libera.chat).
+- via Slack: visit [slack.libgit2.org](http://slack.libgit2.org/) to sign up,
+ then join us in `#libgit2`
+
+**Getting Help**
+
+If you have questions about the library, please be sure to check out the
+[API documentation](http://libgit2.github.com/libgit2/). If you still have
+questions, reach out to us on Slack or post a question on
+[StackOverflow](http://stackoverflow.com/questions/tagged/libgit2) (with the `libgit2` tag).
+
+**Reporting Bugs**
+
+Please open a [GitHub Issue](https://github.com/libgit2/libgit2/issues) and
+include as much information as possible. If possible, provide sample code
+that illustrates the problem you're seeing. If you're seeing a bug only
+on a specific repository, please provide a link to it if possible.
+
+We ask that you not open a GitHub Issue for help, only for bug reports.
+
+**Reporting Security Issues**
+
+Please have a look at SECURITY.md.
+
+What It Can Do
+==============
+
+libgit2 provides you with the ability to manage Git repositories in the
+programming language of your choice. It's used in production to power many
+applications including GitHub.com, Plastic SCM and Azure DevOps.
+
+It does not aim to replace the git tool or its user-facing commands. Some APIs
+resemble the plumbing commands as those align closely with the concepts of the
+Git system, but most commands a user would type are out of scope for this
+library to implement directly.
+
+The library provides:
+
+* SHA conversions, formatting and shortening
+* abstracted ODB backend system
+* commit, tag, tree and blob parsing, editing, and write-back
+* tree traversal
+* revision walking
+* index file (staging area) manipulation
+* reference management (including packed references)
+* config file management
+* high level repository management
+* thread safety and reentrancy
+* descriptive and detailed error messages
+* ...and more (over 175 different API calls)
+
+As libgit2 is purely a consumer of the Git system, we have to
+adjust to changes made upstream. This has two major consequences:
+
+* Some changes may require us to change provided interfaces. While we try to
+ implement functions in a generic way so that no future changes are required,
+ we cannot promise a completely stable API.
+* As we have to keep up with changes in behavior made upstream, we may lag
+ behind in some areas. We usually to document these incompatibilities in our
+ issue tracker with the label "git change".
+
+Optional dependencies
+=====================
+
+While the library provides git functionality without the need for
+dependencies, it can make use of a few libraries to add to it:
+
+- pthreads (non-Windows) to enable threadsafe access as well as multi-threaded pack generation
+- OpenSSL (non-Windows) to talk over HTTPS and provide the SHA-1 functions
+- LibSSH2 to enable the SSH transport
+- iconv (OSX) to handle the HFS+ path encoding peculiarities
+
+Initialization
+===============
+
+The library needs to keep track of some global state. Call
+
+ git_libgit2_init();
+
+before calling any other libgit2 functions. You can call this function many times. A matching number of calls to
+
+ git_libgit2_shutdown();
+
+will free the resources. Note that if you have worker threads, you should
+call `git_libgit2_shutdown` *after* those threads have exited. If you
+require assistance coordinating this, simply have the worker threads call
+`git_libgit2_init` at startup and `git_libgit2_shutdown` at shutdown.
+
+Threading
+=========
+
+See [threading](docs/threading.md) for information
+
+Conventions
+===========
+
+See [conventions](docs/conventions.md) for an overview of the external
+and internal API/coding conventions we use.
+
+Building libgit2 - Using CMake
+==============================
+
+Building
+--------
+
+`libgit2` builds cleanly on most platforms without any external dependencies.
+Under Unix-like systems, like Linux, \*BSD and Mac OS X, libgit2 expects `pthreads` to be available;
+they should be installed by default on all systems. Under Windows, libgit2 uses the native Windows API
+for threading.
+
+The `libgit2` library is built using [CMake](<https://cmake.org/>) (version 2.8 or newer) on all platforms.
+
+On most systems you can build the library using the following commands
+
+ $ mkdir build && cd build
+ $ cmake ..
+ $ cmake --build .
+
+Alternatively you can point the CMake GUI tool to the CMakeLists.txt file and generate platform specific build project or IDE workspace.
+
+If you're not familiar with CMake, [a more detailed explanation](https://preshing.com/20170511/how-to-build-a-cmake-based-project/) may be helpful.
+
+Running Tests
+-------------
+
+Once built, you can run the tests from the `build` directory with the command
+
+ $ ctest -V
+
+Alternatively you can run the test suite directly using,
+
+ $ ./libgit2_tests
+
+Invoking the test suite directly is useful because it allows you to execute
+individual tests, or groups of tests using the `-s` flag. For example, to
+run the index tests:
+
+ $ ./libgit2_tests -sindex
+
+To run a single test named `index::racy::diff`, which corresponds to the test
+function [`test_index_racy__diff`](https://github.com/libgit2/libgit2/blob/main/tests/index/racy.c#L23):
+
+ $ ./libgit2_tests -sindex::racy::diff
+
+The test suite will print a `.` for every passing test, and an `F` for any
+failing test. An `S` indicates that a test was skipped because it is not
+applicable to your platform or is particularly expensive.
+
+**Note:** There should be _no_ failing tests when you build an unmodified
+source tree from a [release](https://github.com/libgit2/libgit2/releases),
+or from the [main branch](https://github.com/libgit2/libgit2/tree/main).
+Please contact us or [open an issue](https://github.com/libgit2/libgit2/issues)
+if you see test failures.
+
+Installation
+------------
+
+To install the library you can specify the install prefix by setting:
+
+ $ cmake .. -DCMAKE_INSTALL_PREFIX=/install/prefix
+ $ cmake --build . --target install
+
+Advanced Usage
+--------------
+
+For more advanced use or questions about CMake please read <https://cmake.org/Wiki/CMake_FAQ>.
+
+The following CMake variables are declared:
+
+- `CMAKE_INSTALL_BINDIR`: Where to install binaries to.
+- `CMAKE_INSTALL_LIBDIR`: Where to install libraries to.
+- `CMAKE_INSTALL_INCLUDEDIR`: Where to install headers to.
+- `BUILD_SHARED_LIBS`: Build libgit2 as a Shared Library (defaults to ON)
+- `BUILD_TESTS`: Build the unit and integration test suites (defaults to ON)
+- `USE_THREADS`: Build libgit2 with threading support (defaults to ON)
+
+To list all build options and their current value, you can do the
+following:
+
+ # Create and set up a build directory
+ $ mkdir build
+ $ cmake ..
+ # List all build options and their values
+ $ cmake -L
+
+Compiler and linker options
+---------------------------
+
+CMake lets you specify a few variables to control the behavior of the
+compiler and linker. These flags are rarely used but can be useful for
+64-bit to 32-bit cross-compilation.
+
+- `CMAKE_C_FLAGS`: Set your own compiler flags
+- `CMAKE_FIND_ROOT_PATH`: Override the search path for libraries
+- `ZLIB_LIBRARY`, `OPENSSL_SSL_LIBRARY` AND `OPENSSL_CRYPTO_LIBRARY`:
+Tell CMake where to find those specific libraries
+- `LINK_WITH_STATIC_LIBRARIES`: Link only with static versions of
+system libraries
+
+MacOS X
+-------
+
+If you want to build a universal binary for Mac OS X, CMake sets it
+all up for you if you use `-DCMAKE_OSX_ARCHITECTURES="i386;x86_64"`
+when configuring.
+
+Android
+-------
+
+Extract toolchain from NDK using, `make-standalone-toolchain.sh` script.
+Optionally, crosscompile and install OpenSSL inside of it. Then create CMake
+toolchain file that configures paths to your crosscompiler (substitute `{PATH}`
+with full path to the toolchain):
+
+ SET(CMAKE_SYSTEM_NAME Linux)
+ SET(CMAKE_SYSTEM_VERSION Android)
+
+ SET(CMAKE_C_COMPILER {PATH}/bin/arm-linux-androideabi-gcc)
+ SET(CMAKE_CXX_COMPILER {PATH}/bin/arm-linux-androideabi-g++)
+ SET(CMAKE_FIND_ROOT_PATH {PATH}/sysroot/)
+
+ SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+ SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+ SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+
+Add `-DCMAKE_TOOLCHAIN_FILE={pathToToolchainFile}` to cmake command
+when configuring.
+
+MinGW
+-----
+
+If you want to build the library in MinGW environment with SSH support enabled,
+you may need to pass `-DCMAKE_LIBRARY_PATH="${MINGW_PREFIX}/${MINGW_CHOST}/lib/"` flag
+to CMake when configuring. This is because CMake cannot find the Win32 libraries in
+MinGW folders by default and you might see an error message stating that CMake
+could not resolve `ws2_32` library during configuration.
+
+Another option would be to install `msys2-w32api-runtime` package before configuring.
+This package installs the Win32 libraries into `/usr/lib` folder which is by default
+recognized as the library path by CMake. Please note though that this package is meant
+for MSYS subsystem which is different from MinGW.
+
+Language Bindings
+==================================
+
+Here are the bindings to libgit2 that are currently available:
+
+* C++
+ * libqgit2, Qt bindings <https://projects.kde.org/projects/playground/libs/libqgit2/repository/>
+* Chicken Scheme
+ * chicken-git <https://wiki.call-cc.org/egg/git>
+* D
+ * dlibgit <https://github.com/s-ludwig/dlibgit>
+* Delphi
+ * GitForDelphi <https://github.com/libgit2/GitForDelphi>
+ * libgit2-delphi <https://github.com/todaysoftware/libgit2-delphi>
+* Erlang
+ * Geef <https://github.com/carlosmn/geef>
+* Go
+ * git2go <https://github.com/libgit2/git2go>
+* GObject
+ * libgit2-glib <https://wiki.gnome.org/Projects/Libgit2-glib>
+* Guile
+ * Guile-Git <https://gitlab.com/guile-git/guile-git>
+* Haskell
+ * hgit2 <https://github.com/jwiegley/gitlib>
+* Java
+ * Jagged <https://github.com/ethomson/jagged>
+ * Git24j <https://github.com/git24j/git24j>
+* Javascript / WebAssembly ( browser and nodejs )
+ * WASM-git <https://github.com/petersalomonsen/wasm-git>
+* Julia
+ * LibGit2.jl <https://github.com/JuliaLang/julia/tree/master/stdlib/LibGit2>
+* Lua
+ * luagit2 <https://github.com/libgit2/luagit2>
+* .NET
+ * libgit2sharp <https://github.com/libgit2/libgit2sharp>
+* Node.js
+ * nodegit <https://github.com/nodegit/nodegit>
+* Objective-C
+ * objective-git <https://github.com/libgit2/objective-git>
+* OCaml
+ * ocaml-libgit2 <https://github.com/fxfactorial/ocaml-libgit2>
+* Parrot Virtual Machine
+ * parrot-libgit2 <https://github.com/letolabs/parrot-libgit2>
+* Perl
+ * Git-Raw <https://github.com/jacquesg/p5-Git-Raw>
+* Pharo Smalltalk
+ * libgit2-pharo-bindings <https://github.com/pharo-vcs/libgit2-pharo-bindings>
+* PHP
+ * php-git2 <https://github.com/RogerGee/php-git2>
+* Python
+ * pygit2 <https://github.com/libgit2/pygit2>
+* R
+ * gert <https://docs.ropensci.org/gert>
+ * git2r <https://github.com/ropensci/git2r>
+* Ruby
+ * Rugged <https://github.com/libgit2/rugged>
+* Rust
+ * git2-rs <https://github.com/rust-lang/git2-rs>
+* Swift
+ * SwiftGit2 <https://github.com/SwiftGit2/SwiftGit2>
+* Tcl
+ * lg2 <https://github.com/apnadkarni/tcl-libgit2>
+* Vala
+ * libgit2.vapi <https://github.com/apmasell/vapis/blob/master/libgit2.vapi>
+
+If you start another language binding to libgit2, please let us know so
+we can add it to the list.
+
+How Can I Contribute?
+==================================
+
+We welcome new contributors! We have a number of issues marked as
+["up for grabs"](https://github.com/libgit2/libgit2/issues?q=is%3Aissue+is%3Aopen+label%3A%22up+for+grabs%22)
+and
+["easy fix"](https://github.com/libgit2/libgit2/issues?utf8=✓&q=is%3Aissue+is%3Aopen+label%3A%22easy+fix%22)
+that are good places to jump in and get started. There's much more detailed
+information in our list of [outstanding projects](docs/projects.md).
+
+Please be sure to check the [contribution guidelines](docs/contributing.md) to
+understand our workflow, and the libgit2 [coding conventions](docs/conventions.md).
+
+License
+==================================
+
+`libgit2` is under GPL2 **with linking exception**. This means you can link to
+and use the library from any program, proprietary or open source; paid or
+gratis. However, if you modify libgit2 itself, you must distribute the
+source to your modified version of libgit2.
+
+See the [COPYING file](COPYING) for the full license text.
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..f98eebf
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,14 @@
+# Security Policy
+
+## Supported Versions
+
+This project will always provide security fixes for the latest two released
+versions. E.g. if the latest version is v0.28.x, then we will provide security
+fixes for both v0.28.x and v0.27.y, but no later versions.
+
+## Reporting a Vulnerability
+
+In case you think to have found a security issue with libgit2, please do not
+open a public issue. Instead, you can report the issue to the private mailing
+list [security@libgit2.com](mailto:security@libgit2.com). We will acknowledge
+receipt of your message in at most three days and try to clarify further steps.
diff --git a/api.docurium b/api.docurium
new file mode 100644
index 0000000..bf73327
--- /dev/null
+++ b/api.docurium
@@ -0,0 +1,13 @@
+{
+ "name": "libgit2",
+ "github": "libgit2/libgit2",
+ "input": "include",
+ "prefix": "git_",
+ "output": "docs",
+ "branch": "gh-pages",
+ "examples": "examples",
+ "legacy": {
+ "input": {"src/git": ["v0.1.0"],
+ "src/git2": ["v0.2.0", "v0.3.0"]}
+ }
+}
diff --git a/ci/build.sh b/ci/build.sh
new file mode 100755
index 0000000..80e7a61
--- /dev/null
+++ b/ci/build.sh
@@ -0,0 +1,93 @@
+#!/usr/bin/env bash
+#
+# Environment variables:
+#
+# SOURCE_DIR: Set to the directory of the libgit2 source (optional)
+# If not set, it will be derived relative to this script.
+
+set -e
+
+SOURCE_DIR=${SOURCE_DIR:-$( cd "$( dirname "${BASH_SOURCE[0]}" )" && dirname $( pwd ) )}
+BUILD_DIR=$(pwd)
+BUILD_PATH=${BUILD_PATH:=$PATH}
+CMAKE=$(which cmake)
+CMAKE_GENERATOR=${CMAKE_GENERATOR:-Unix Makefiles}
+
+indent() { sed "s/^/ /"; }
+
+cygfullpath() {
+ result=$(echo "${1}" | tr \; \\n | while read -r element; do
+ if [ "${last}" != "" ]; then echo -n ":"; fi
+ echo -n $(cygpath "${element}")
+ last="${element}"
+ done)
+ if [ "${result}" = "" ]; then exit 1; fi
+ echo "${result}"
+}
+
+if [[ "$(uname -s)" == MINGW* ]]; then
+ BUILD_PATH=$(cygfullpath "${BUILD_PATH}")
+fi
+
+
+echo "Source directory: ${SOURCE_DIR}"
+echo "Build directory: ${BUILD_DIR}"
+echo ""
+
+echo "Platform:"
+uname -s | indent
+
+if [ "$(uname -s)" = "Darwin" ]; then
+ echo "macOS version:"
+ sw_vers | indent
+fi
+
+if [ -f "/etc/debian_version" ]; then
+ echo "Debian version:"
+ (source /etc/lsb-release && echo "${DISTRIB_DESCRIPTION}") | indent
+fi
+
+CORES=$(getconf _NPROCESSORS_ONLN || true)
+echo "Number of cores: ${CORES:-(Unknown)}"
+
+echo "Kernel version:"
+uname -a 2>&1 | indent
+
+echo "CMake version:"
+env PATH="${BUILD_PATH}" "${CMAKE}" --version | head -1 2>&1 | indent
+
+if test -n "${CC}"; then
+ echo "Compiler version:"
+ "${CC}" --version 2>&1 | indent
+fi
+echo "Environment:"
+if test -n "${CC}"; then
+ echo "CC=${CC}" | indent
+fi
+if test -n "${CFLAGS}"; then
+ echo "CFLAGS=${CFLAGS}" | indent
+fi
+echo ""
+
+echo "##############################################################################"
+echo "## Configuring build environment"
+echo "##############################################################################"
+
+echo "${CMAKE}" -DENABLE_WERROR=ON -DBUILD_EXAMPLES=ON -DBUILD_FUZZERS=ON -DUSE_STANDALONE_FUZZERS=ON -G \"${CMAKE_GENERATOR}\" ${CMAKE_OPTIONS} -S \"${SOURCE_DIR}\"
+env PATH="${BUILD_PATH}" "${CMAKE}" -DENABLE_WERROR=ON -DBUILD_EXAMPLES=ON -DBUILD_FUZZERS=ON -DUSE_STANDALONE_FUZZERS=ON -G "${CMAKE_GENERATOR}" ${CMAKE_OPTIONS} -S "${SOURCE_DIR}"
+
+echo ""
+echo "##############################################################################"
+echo "## Building libgit2"
+echo "##############################################################################"
+
+# Determine parallelism; newer cmake supports `--build --parallel` but
+# we cannot yet rely on that.
+if [ "${CMAKE_GENERATOR}" = "Unix Makefiles" -a "${CORES}" != "" -a "${CMAKE_BUILD_OPTIONS}" = "" ]; then
+ BUILDER=(make -j ${CORES})
+else
+ BUILDER=("${CMAKE}" --build . ${CMAKE_BUILD_OPTIONS})
+fi
+
+echo "${BUILDER[@]}"
+env PATH="${BUILD_PATH}" "${BUILDER[@]}"
diff --git a/ci/coverity.sh b/ci/coverity.sh
new file mode 100755
index 0000000..c68b6f8
--- /dev/null
+++ b/ci/coverity.sh
@@ -0,0 +1,62 @@
+#!/bin/bash -e
+
+if test -z "$COVERITY_TOKEN"
+then
+ echo "Need to set a coverity token"
+ exit 1
+fi
+
+case $(uname -m) in
+ i?86)
+ BITS=32;;
+ amd64|x86_64)
+ BITS=64;;
+ *)
+ echo "Unsupported arch '$(uname -m)'"
+ exit 1;;
+esac
+
+SCAN_TOOL=https://scan.coverity.com/download/cxx/linux${BITS}
+SOURCE_DIR=$(realpath "$(dirname "${BASH_SOURCE[0]}")"/..)
+BUILD_DIR=${SOURCE_DIR}/coverity-build
+TOOL_DIR=${BUILD_DIR}/coverity-tools
+
+# Install coverity tools
+if ! test -d "$TOOL_DIR"
+then
+ mkdir -p "$TOOL_DIR"
+ curl --silent --show-error --location --data "project=libgit2&token=$COVERITY_TOKEN" "$SCAN_TOOL" |
+ tar -xzC "$TOOL_DIR"
+ ln -s "$(find "$TOOL_DIR" -type d -name 'cov-analysis*')" "$TOOL_DIR"/cov-analysis
+fi
+
+cp "${SOURCE_DIR}/script/user_nodefs.h" "$TOOL_DIR"/cov-analysis/config/
+
+# Build libgit2 with Coverity
+mkdir -p "$BUILD_DIR"
+cd "$BUILD_DIR"
+cmake "$SOURCE_DIR"
+COVERITY_UNSUPPORTED=1 \
+ "$TOOL_DIR/cov-analysis/bin/cov-build" --dir cov-int \
+ cmake --build .
+
+# Upload results
+tar -czf libgit2.tgz cov-int
+REVISION=$(cd ${SOURCE_DIR} && git rev-parse --short HEAD)
+HTML="$(curl \
+ --silent --show-error \
+ --write-out "\n%{http_code}" \
+ --form token="$COVERITY_TOKEN" \
+ --form email=libgit2@gmail.com \
+ --form file=@libgit2.tgz \
+ --form version="$REVISION" \
+ --form description="libgit2 build" \
+ https://scan.coverity.com/builds?project=libgit2)"
+
+# Status code is the last line
+STATUS_CODE="$(echo "$HTML" | tail -n1)"
+if test "${STATUS_CODE}" != 200 && test "${STATUS_CODE}" != 201
+then
+ echo "Received error code ${STATUS_CODE} from Coverity"
+ exit 1
+fi
diff --git a/ci/docker/bionic b/ci/docker/bionic
new file mode 100644
index 0000000..f1b69ed
--- /dev/null
+++ b/ci/docker/bionic
@@ -0,0 +1,49 @@
+ARG BASE=ubuntu:bionic
+
+FROM ${BASE} AS apt
+RUN apt-get update && \
+ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
+ clang \
+ cmake \
+ curl \
+ gcc \
+ git \
+ krb5-user \
+ libcurl4-openssl-dev \
+ libkrb5-dev \
+ libpcre3-dev \
+ libssh2-1-dev \
+ libssl-dev \
+ libz-dev \
+ ninja-build \
+ openjdk-8-jre-headless \
+ openssh-server \
+ openssl \
+ pkgconf \
+ python \
+ sudo \
+ valgrind \
+ && \
+ rm -rf /var/lib/apt/lists/*
+
+FROM apt AS mbedtls
+RUN cd /tmp && \
+ curl --location --silent --show-error https://github.com/Mbed-TLS/mbedtls/archive/refs/tags/mbedtls-2.16.2.tar.gz | \
+ tar -xz && \
+ cd mbedtls-mbedtls-2.16.2 && \
+ scripts/config.pl set MBEDTLS_MD4_C 1 && \
+ CFLAGS=-fPIC cmake -G Ninja -DENABLE_PROGRAMS=OFF -DENABLE_TESTING=OFF -DUSE_SHARED_MBEDTLS_LIBRARY=OFF -DUSE_STATIC_MBEDTLS_LIBRARY=ON . && \
+ ninja install && \
+ cd .. && \
+ rm -rf mbedtls-mbedtls-2.16.2
+
+FROM mbedtls AS adduser
+ARG UID=""
+ARG GID=""
+RUN if [ "${UID}" != "" ]; then USER_ARG="--uid ${UID}"; fi && \
+ if [ "${GID}" != "" ]; then GROUP_ARG="--gid ${GID}"; fi && \
+ groupadd ${GROUP_ARG} libgit2 && \
+ useradd ${USER_ARG} --gid libgit2 --shell /bin/bash --create-home libgit2
+
+FROM adduser AS configure
+RUN mkdir /var/run/sshd
diff --git a/ci/docker/centos7 b/ci/docker/centos7
new file mode 100644
index 0000000..28ed650
--- /dev/null
+++ b/ci/docker/centos7
@@ -0,0 +1,60 @@
+ARG BASE=centos:7
+
+FROM ${BASE} AS yum
+RUN yum install -y \
+ which \
+ bzip2 \
+ git \
+ libarchive \
+ gcc \
+ gcc-c++ \
+ make \
+ openssl-devel \
+ openssh-server \
+ git-daemon \
+ java-1.8.0-openjdk-headless \
+ sudo \
+ python
+
+FROM yum AS libssh2
+RUN cd /tmp && \
+ curl --location --silent --show-error https://www.libssh2.org/download/libssh2-1.8.0.tar.gz | tar -xz && \
+ cd libssh2-1.8.0 && \
+ ./configure && \
+ make && \
+ make install && \
+ cd .. && \
+ rm -rf libssh-1.8.0
+
+FROM libssh2 AS valgrind
+RUN cd /tmp && \
+ curl --insecure --location --silent --show-error https://sourceware.org/pub/valgrind/valgrind-3.15.0.tar.bz2 | \
+ tar -xj && \
+ cd valgrind-3.15.0 && \
+ ./configure && \
+ make MAKEFLAGS="-j -l$(grep -c ^processor /proc/cpuinfo)" && \
+ make install && \
+ cd .. && \
+ rm -rf valgrind-3.15.0
+
+FROM valgrind AS cmake
+RUN cd /tmp && \
+ curl -L https://github.com/Kitware/CMake/releases/download/v3.21.1/cmake-3.21.1.tar.gz | tar -xz && \
+ cd cmake-3.21.1 && \
+ ./configure && \
+ make && \
+ make install && \
+ cd .. && \
+ rm -rf cmake-3.21.1
+
+FROM cmake AS adduser
+ARG UID=""
+ARG GID=""
+RUN if [ "${UID}" != "" ]; then USER_ARG="--uid ${UID}"; fi && \
+ if [ "${GID}" != "" ]; then GROUP_ARG="--gid ${GID}"; fi && \
+ groupadd ${GROUP_ARG} libgit2 && \
+ useradd ${USER_ARG} --gid libgit2 --shell /bin/bash --create-home libgit2
+
+FROM adduser AS configure
+ENV PKG_CONFIG_PATH /usr/local/lib/pkgconfig
+RUN mkdir /var/run/sshd
diff --git a/ci/docker/centos8 b/ci/docker/centos8
new file mode 100644
index 0000000..81f0c3c
--- /dev/null
+++ b/ci/docker/centos8
@@ -0,0 +1,58 @@
+ARG BASE=centos:8
+
+FROM ${BASE} AS stream
+RUN dnf -y --disablerepo '*' --enablerepo=extras swap centos-linux-repos centos-stream-repos && \
+ dnf -y distro-sync
+
+FROM stream AS yum
+RUN yum install -y \
+ which \
+ bzip2 \
+ git \
+ libarchive \
+ cmake \
+ gcc \
+ make \
+ openssl-devel \
+ openssh-server \
+ git-daemon \
+ java-1.8.0-openjdk-headless \
+ sudo \
+ python39 \
+ krb5-workstation \
+ krb5-libs
+
+FROM yum AS libssh2
+RUN cd /tmp && \
+ curl --location --silent --show-error https://www.libssh2.org/download/libssh2-1.8.0.tar.gz | tar -xz && \
+ cd libssh2-1.8.0 && \
+ ./configure && \
+ make && \
+ make install && \
+ cd .. && \
+ rm -rf libssh2-1.8.0
+
+FROM libssh2 AS valgrind
+RUN cd /tmp && \
+ curl --insecure --location --silent --show-error https://sourceware.org/pub/valgrind/valgrind-3.15.0.tar.bz2 | \
+ tar -xj && \
+ cd valgrind-3.15.0 && \
+ ./configure && \
+ make MAKEFLAGS="-j -l$(grep -c ^processor /proc/cpuinfo)" && \
+ make install && \
+ cd .. && \
+ rm -rf valgrind-3.15.0
+
+FROM valgrind AS adduser
+ARG UID=""
+ARG GID=""
+RUN if [ "${UID}" != "" ]; then USER_ARG="--uid ${UID}"; fi && \
+ if [ "${GID}" != "" ]; then GROUP_ARG="--gid ${GID}"; fi && \
+ groupadd ${GROUP_ARG} libgit2 && \
+ useradd ${USER_ARG} --gid libgit2 --shell /bin/bash --create-home libgit2
+
+FROM adduser AS configure
+ENV PKG_CONFIG_PATH /usr/local/lib/pkgconfig
+RUN mkdir /var/run/sshd
+RUN echo "/usr/local/lib" > /etc/ld.so.conf.d/local && \
+ ldconfig
diff --git a/ci/docker/docurium b/ci/docker/docurium
new file mode 100644
index 0000000..1957bbb
--- /dev/null
+++ b/ci/docker/docurium
@@ -0,0 +1,5 @@
+ARG BASE=ubuntu:bionic
+
+FROM ${BASE}
+RUN apt update && apt install -y cmake pkg-config ruby ruby-dev llvm libclang-dev libssl-dev python-pygments
+RUN gem install docurium
diff --git a/ci/docker/focal b/ci/docker/focal
new file mode 100644
index 0000000..b3a402c
--- /dev/null
+++ b/ci/docker/focal
@@ -0,0 +1,85 @@
+ARG BASE=ubuntu:focal
+
+FROM ${BASE} AS apt
+RUN apt-get update && \
+ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
+ bzip2 \
+ clang-10 \
+ cmake \
+ curl \
+ gcc-10 \
+ git \
+ krb5-user \
+ libcurl4-gnutls-dev \
+ libgcrypt20-dev \
+ libkrb5-dev \
+ libpcre3-dev \
+ libssl-dev \
+ libz-dev \
+ llvm-10 \
+ make \
+ ninja-build \
+ openjdk-8-jre-headless \
+ openssh-server \
+ openssl \
+ pkgconf \
+ python \
+ sudo \
+ valgrind \
+ && \
+ rm -rf /var/lib/apt/lists/* && \
+ mkdir /usr/local/msan
+
+FROM apt AS mbedtls
+RUN cd /tmp && \
+ curl --location --silent --show-error https://github.com/Mbed-TLS/mbedtls/archive/refs/tags/mbedtls-2.16.2.tar.gz | \
+ tar -xz && \
+ cd mbedtls-mbedtls-2.16.2 && \
+ scripts/config.pl unset MBEDTLS_AESNI_C && \
+ scripts/config.pl set MBEDTLS_MD4_C 1 && \
+ mkdir build build-msan && \
+ cd build && \
+ CC=clang-10 CFLAGS="-fPIC" cmake -G Ninja -DENABLE_PROGRAMS=OFF -DENABLE_TESTING=OFF -DUSE_SHARED_MBEDTLS_LIBRARY=ON -DUSE_STATIC_MBEDTLS_LIBRARY=OFF -DCMAKE_BUILD_TYPE=Debug -DCMAKE_PREFIX_PATH=/usr/local -DCMAKE_INSTALL_PREFIX=/usr/local .. && \
+ ninja install && \
+ cd ../build-msan && \
+ CC=clang-10 CFLAGS="-fPIC" cmake -G Ninja -DENABLE_PROGRAMS=OFF -DENABLE_TESTING=OFF -DUSE_SHARED_MBEDTLS_LIBRARY=ON -DUSE_STATIC_MBEDTLS_LIBRARY=OFF -DCMAKE_BUILD_TYPE=MemSanDbg -DCMAKE_INSTALL_PREFIX=/usr/local/msan .. && \
+ ninja install && \
+ cd .. && \
+ rm -rf mbedtls-mbedtls-2.16.2
+
+FROM mbedtls AS libssh2
+RUN cd /tmp && \
+ curl --location --silent --show-error https://www.libssh2.org/download/libssh2-1.9.0.tar.gz | tar -xz && \
+ cd libssh2-1.9.0 && \
+ mkdir build build-msan && \
+ cd build && \
+ CC=clang-10 CFLAGS="-fPIC" cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCRYPTO_BACKEND=Libgcrypt -DCMAKE_PREFIX_PATH=/usr/local -DCMAKE_INSTALL_PREFIX=/usr/local .. && \
+ ninja install && \
+ cd ../build-msan && \
+ CC=clang-10 CFLAGS="-fPIC -fsanitize=memory -fno-optimize-sibling-calls -fsanitize-memory-track-origins=2 -fno-omit-frame-pointer" LDFLAGS="-fsanitize=memory" cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCRYPTO_BACKEND=mbedTLS -DCMAKE_PREFIX_PATH=/usr/local/msan -DCMAKE_INSTALL_PREFIX=/usr/local/msan .. && \
+ ninja install && \
+ cd .. && \
+ rm -rf libssh2-1.9.0
+
+FROM libssh2 AS valgrind
+RUN cd /tmp && \
+ curl --insecure --location --silent --show-error https://sourceware.org/pub/valgrind/valgrind-3.15.0.tar.bz2 | \
+ tar -xj && \
+ cd valgrind-3.15.0 && \
+ CC=clang-10 ./configure && \
+ make MAKEFLAGS="-j -l$(grep -c ^processor /proc/cpuinfo)" && \
+ make install && \
+ cd .. && \
+ rm -rf valgrind-3.15.0
+
+FROM valgrind AS adduser
+ARG UID=""
+ARG GID=""
+RUN if [ "${UID}" != "" ]; then USER_ARG="--uid ${UID}"; fi && \
+ if [ "${GID}" != "" ]; then GROUP_ARG="--gid ${GID}"; fi && \
+ groupadd ${GROUP_ARG} libgit2 && \
+ useradd ${USER_ARG} --gid libgit2 --shell /bin/bash --create-home libgit2
+
+
+FROM adduser AS configure
+RUN mkdir /var/run/sshd
diff --git a/ci/docker/xenial b/ci/docker/xenial
new file mode 100644
index 0000000..578f0a9
--- /dev/null
+++ b/ci/docker/xenial
@@ -0,0 +1,84 @@
+ARG BASE=ubuntu:xenial
+
+FROM ${BASE} AS apt
+RUN apt-get update && \
+ DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
+ bzip2 \
+ clang \
+ cmake \
+ curl \
+ gettext \
+ gcc \
+ krb5-user \
+ libcurl4-gnutls-dev \
+ libexpat1-dev \
+ libgcrypt20-dev \
+ libintl-perl \
+ libkrb5-dev \
+ libpcre3-dev \
+ libssl-dev \
+ libz-dev \
+ make \
+ ninja-build \
+ openjdk-8-jre-headless \
+ openssh-server \
+ openssl \
+ pkgconf \
+ python \
+ sudo \
+ valgrind \
+ && \
+ rm -rf /var/lib/apt/lists/*
+
+FROM apt AS git
+RUN cd /tmp && \
+ curl --location --silent --show-error https://github.com/git/git/archive/refs/tags/v2.39.1.tar.gz | \
+ tar -xz && \
+ cd git-2.39.1 && \
+ make && \
+ make prefix=/usr install && \
+ cd .. && \
+ rm -rf git-2.39.1
+
+FROM git AS mbedtls
+RUN cd /tmp && \
+ curl --location --silent --show-error https://github.com/Mbed-TLS/mbedtls/archive/refs/tags/mbedtls-2.16.2.tar.gz | \
+ tar -xz && \
+ cd mbedtls-mbedtls-2.16.2 && \
+ scripts/config.pl set MBEDTLS_MD4_C 1 && \
+ CFLAGS=-fPIC cmake -G Ninja -DENABLE_PROGRAMS=OFF -DENABLE_TESTING=OFF -DUSE_SHARED_MBEDTLS_LIBRARY=OFF -DUSE_STATIC_MBEDTLS_LIBRARY=ON . && \
+ ninja install && \
+ cd .. && \
+ rm -rf mbedtls-mbedtls-2.16.2
+
+FROM mbedtls AS libssh2
+RUN cd /tmp && \
+ curl --location --silent --show-error https://www.libssh2.org/download/libssh2-1.8.2.tar.gz | tar -xz && \
+ cd libssh2-1.8.2 && \
+ CFLAGS=-fPIC cmake -G Ninja -DBUILD_SHARED_LIBS=ON -DCRYPTO_BACKEND=Libgcrypt . && \
+ ninja install && \
+ cd .. && \
+ rm -rf libssh2-1.8.2
+
+FROM libssh2 AS valgrind
+RUN cd /tmp && \
+ curl --insecure --location --silent --show-error https://sourceware.org/pub/valgrind/valgrind-3.15.0.tar.bz2 | \
+ tar -xj && \
+ cd valgrind-3.15.0 && \
+ ./configure && \
+ make && \
+ make install && \
+ cd .. && \
+ rm -rf valgrind-3.15.0
+
+FROM valgrind AS adduser
+ARG UID=""
+ARG GID=""
+RUN if [ "${UID}" != "" ]; then USER_ARG="--uid ${UID}"; fi && \
+ if [ "${GID}" != "" ]; then GROUP_ARG="--gid ${GID}"; fi && \
+ groupadd ${GROUP_ARG} libgit2 && \
+ useradd ${USER_ARG} --gid libgit2 --shell /bin/bash --create-home libgit2
+
+
+FROM adduser AS configure
+RUN mkdir /var/run/sshd
diff --git a/ci/getcontainer.sh b/ci/getcontainer.sh
new file mode 100755
index 0000000..81d0c1d
--- /dev/null
+++ b/ci/getcontainer.sh
@@ -0,0 +1,55 @@
+#!/bin/bash
+
+set -e
+
+IMAGE_NAME=$1
+DOCKERFILE_PATH=$2
+
+if [ "${IMAGE_NAME}" = "" ]; then
+ echo "usage: $0 image_name [dockerfile]"
+ exit 1
+fi
+
+if [ "${DOCKERFILE_PATH}" = "" ]; then
+ DOCKERFILE_PATH="${IMAGE_NAME}"
+fi
+
+if [ "${DOCKER_REGISTRY}" = "" ]; then
+ echo "DOCKER_REGISTRY environment variable is unset."
+ echo "Not running inside GitHub Actions or misconfigured?"
+ exit 1
+fi
+
+DOCKER_CONTAINER="${GITHUB_REPOSITORY}/${IMAGE_NAME}"
+DOCKER_REGISTRY_CONTAINER="${DOCKER_REGISTRY}/${DOCKER_CONTAINER}"
+
+echo "dockerfile=${DOCKERFILE_PATH}" >> $GITHUB_ENV
+echo "docker-container=${DOCKER_CONTAINER}" >> $GITHUB_ENV
+echo "docker-registry-container=${DOCKER_REGISTRY_CONTAINER}" >> $GITHUB_ENV
+
+# Identify the last git commit that touched the Dockerfiles
+# Use this as a hash to identify the resulting docker containers
+DOCKER_SHA=$(git log -1 --pretty=format:"%h" -- "${DOCKERFILE_PATH}")
+echo "docker-sha=${DOCKER_SHA}" >> $GITHUB_ENV
+
+DOCKER_REGISTRY_CONTAINER_SHA="${DOCKER_REGISTRY_CONTAINER}:${DOCKER_SHA}"
+
+echo "docker-registry-container-sha=${DOCKER_REGISTRY_CONTAINER_SHA}" >> $GITHUB_ENV
+echo "docker-registry-container-latest=${DOCKER_REGISTRY_CONTAINER}:latest" >> $GITHUB_ENV
+
+echo "::: logging in to ${DOCKER_REGISTRY} as ${GITHUB_ACTOR}"
+
+exists="true"
+docker login https://${DOCKER_REGISTRY} -u ${GITHUB_ACTOR} -p ${GITHUB_TOKEN} || exists="false"
+
+echo "::: pulling ${DOCKER_REGISTRY_CONTAINER_SHA}"
+
+if [ "${exists}" != "false" ]; then
+ docker pull ${DOCKER_REGISTRY_CONTAINER_SHA} || exists="false"
+fi
+
+if [ "${exists}" = "true" ]; then
+ echo "docker-container-exists=true" >> $GITHUB_ENV
+else
+ echo "docker-container-exists=false" >> $GITHUB_ENV
+fi
diff --git a/ci/setup-mingw-build.sh b/ci/setup-mingw-build.sh
new file mode 100755
index 0000000..6c444f5
--- /dev/null
+++ b/ci/setup-mingw-build.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+set -ex
+
+echo "##############################################################################"
+echo "## Downloading mingw"
+echo "##############################################################################"
+
+BUILD_TEMP=${BUILD_TEMP:=$TEMP}
+BUILD_TEMP=$(cygpath $BUILD_TEMP)
+
+case "$ARCH" in
+ amd64)
+ MINGW_URI="https://github.com/libgit2/ci-dependencies/releases/download/2023-01-23/mingw-x86_64-8.1.0-release-win32-sjlj-rt_v6-rev0.zip";;
+ x86)
+ MINGW_URI="https://github.com/libgit2/ci-dependencies/releases/download/2023-01-23/mingw-i686-8.1.0-release-win32-sjlj-rt_v6-rev0.zip";;
+esac
+
+if [ -z "$MINGW_URI" ]; then
+ echo "No URL"
+ exit 1
+fi
+
+mkdir -p "$BUILD_TEMP"
+
+curl -s -L "$MINGW_URI" -o "$BUILD_TEMP"/mingw-"$ARCH".zip
+unzip -q "$BUILD_TEMP"/mingw-"$ARCH".zip -d "$BUILD_TEMP"
diff --git a/ci/setup-osx-benchmark.sh b/ci/setup-osx-benchmark.sh
new file mode 100755
index 0000000..80d8768
--- /dev/null
+++ b/ci/setup-osx-benchmark.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+set -ex
+
+brew update
+brew install hyperfine
diff --git a/ci/setup-osx-build.sh b/ci/setup-osx-build.sh
new file mode 100755
index 0000000..0b95e76
--- /dev/null
+++ b/ci/setup-osx-build.sh
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+set -ex
+
+brew update
+brew install pkgconfig zlib curl openssl libssh2 ninja
+
+ln -s /Applications/Xcode.app/Contents/Developer/usr/lib/libLeaksAtExit.dylib /usr/local/lib
diff --git a/ci/setup-ubuntu-benchmark.sh b/ci/setup-ubuntu-benchmark.sh
new file mode 100755
index 0000000..561a18f
--- /dev/null
+++ b/ci/setup-ubuntu-benchmark.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+set -ex
+
+sudo apt-get update
+sudo apt-get install -y --no-install-recommends \
+ cargo \
+ cmake \
+ gcc \
+ git \
+ krb5-user \
+ libkrb5-dev \
+ libssl-dev \
+ libz-dev \
+ make \
+ ninja-build \
+ pkgconf
+
+wget https://github.com/sharkdp/hyperfine/releases/download/v1.12.0/hyperfine_1.12.0_amd64.deb
+sudo dpkg -i hyperfine_1.12.0_amd64.deb
diff --git a/ci/setup-win32-benchmark.sh b/ci/setup-win32-benchmark.sh
new file mode 100755
index 0000000..0eac2f6
--- /dev/null
+++ b/ci/setup-win32-benchmark.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+
+set -ex
+
+choco install hyperfine zip
+
+CHOCO_PATH=$(mktemp -d)
+curl -L https://github.com/ethomson/PurgeStandbyList/releases/download/v1.0/purgestandbylist.1.0.0.nupkg -o "${CHOCO_PATH}/purgestandbylist.1.0.0.nupkg"
+choco install purgestandbylist -s $(cygpath -w "${CHOCO_PATH}")
diff --git a/ci/setup-win32-build.sh b/ci/setup-win32-build.sh
new file mode 100755
index 0000000..a8b81e5
--- /dev/null
+++ b/ci/setup-win32-build.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+set -ex
+
+echo "##############################################################################"
+echo "## Downloading libssh2"
+echo "##############################################################################"
+
+BUILD_TEMP=${BUILD_TEMP:=$TEMP}
+BUILD_TEMP=$(cygpath $BUILD_TEMP)
+
+case "$ARCH" in
+ amd64)
+ LIBSSH2_URI="https://github.com/libgit2/ci-dependencies/releases/download/2023-02-01/libssh2-20230201-amd64.zip";;
+ x86)
+ LIBSSH2_URI="https://github.com/libgit2/ci-dependencies/releases/download/2023-02-01-v2/libssh2-20230201-x86.zip";;
+esac
+
+if [ -z "$LIBSSH2_URI" ]; then
+ echo "No URL"
+ exit 1
+fi
+
+mkdir -p "$BUILD_TEMP"
+
+curl -s -L "$LIBSSH2_URI" -o "$BUILD_TEMP"/libssh2-"$ARCH".zip
+unzip -q "$BUILD_TEMP"/libssh2-"$ARCH".zip -d "$BUILD_TEMP"
diff --git a/ci/test.sh b/ci/test.sh
new file mode 100755
index 0000000..ee6801a
--- /dev/null
+++ b/ci/test.sh
@@ -0,0 +1,451 @@
+#!/usr/bin/env bash
+
+set -e
+
+if [ -n "$SKIP_TESTS" ]; then
+ exit 0
+fi
+
+# Windows doesn't run the NTLM tests properly (yet)
+if [[ "$(uname -s)" == MINGW* ]]; then
+ SKIP_NTLM_TESTS=1
+fi
+
+SOURCE_DIR=${SOURCE_DIR:-$( cd "$( dirname "${BASH_SOURCE[0]}" )" && dirname $( pwd ) )}
+BUILD_DIR=$(pwd)
+BUILD_PATH=${BUILD_PATH:=$PATH}
+CTEST=$(which ctest)
+TMPDIR=${TMPDIR:-/tmp}
+USER=${USER:-$(whoami)}
+
+HOME=`mktemp -d ${TMPDIR}/home.XXXXXXXX`
+export CLAR_HOMEDIR=${HOME}
+
+SUCCESS=1
+CONTINUE_ON_FAILURE=0
+
+cleanup() {
+ echo "Cleaning up..."
+
+ if [ ! -z "$GIT_STANDARD_PID" ]; then
+ echo "Stopping git daemon (standard)..."
+ kill $GIT_STANDARD_PID
+ fi
+
+ if [ ! -z "$GIT_NAMESPACE_PID" ]; then
+ echo "Stopping git daemon (namespace)..."
+ kill $GIT_NAMESPACE_PID
+ fi
+
+ if [ ! -z "$GIT_SHA256_PID" ]; then
+ echo "Stopping git daemon (sha256)..."
+ kill $GIT_SHA256_PID
+ fi
+
+ if [ ! -z "$PROXY_BASIC_PID" ]; then
+ echo "Stopping proxy (Basic)..."
+ kill $PROXY_BASIC_PID
+ fi
+
+ if [ ! -z "$PROXY_NTLM_PID" ]; then
+ echo "Stopping proxy (NTLM)..."
+ kill $PROXY_NTLM_PID
+ fi
+
+ if [ ! -z "$HTTP_PID" ]; then
+ echo "Stopping HTTP server..."
+ kill $HTTP_PID
+ fi
+
+ if [ ! -z "$SSHD_DIR" -a -f "${SSHD_DIR}/pid" ]; then
+ echo "Stopping SSH server..."
+ kill $(cat "${SSHD_DIR}/pid")
+ fi
+
+ echo "Done."
+}
+
+run_test() {
+ if [[ "$GITTEST_FLAKY_RETRY" > 0 ]]; then
+ ATTEMPTS_REMAIN=$GITTEST_FLAKY_RETRY
+ else
+ ATTEMPTS_REMAIN=1
+ fi
+
+ FAILED=0
+ while [[ "$ATTEMPTS_REMAIN" > 0 ]]; do
+ if [ "$FAILED" -eq 1 ]; then
+ echo ""
+ echo "Re-running flaky ${1} tests..."
+ echo ""
+ fi
+
+ RETURN_CODE=0
+
+ (
+ export PATH="${BUILD_PATH}"
+ export CLAR_SUMMARY="${BUILD_DIR}/results_${1}.xml"
+ "${CTEST}" -V -R "^${1}$"
+ ) || RETURN_CODE=$? && true
+
+ if [ "$RETURN_CODE" -eq 0 ]; then
+ FAILED=0
+ break
+ fi
+
+ echo "Test exited with code: $RETURN_CODE"
+ ATTEMPTS_REMAIN="$(($ATTEMPTS_REMAIN-1))"
+ FAILED=1
+ done
+
+ if [ "$FAILED" -ne 0 ]; then
+ if [ "$CONTINUE_ON_FAILURE" -ne 1 ]; then
+ exit 1
+ fi
+
+ SUCCESS=0
+ fi
+}
+
+indent() { sed "s/^/ /"; }
+
+cygfullpath() {
+ result=$(echo "${1}" | tr \; \\n | while read -r element; do
+ if [ "${last}" != "" ]; then echo -n ":"; fi
+ echo -n $(cygpath "${element}")
+ last="${element}"
+ done)
+ if [ "${result}" = "" ]; then exit 1; fi
+ echo "${result}"
+}
+
+if [[ "$(uname -s)" == MINGW* ]]; then
+ BUILD_PATH=$(cygfullpath "$BUILD_PATH")
+fi
+
+
+# Configure the test environment; run them early so that we're certain
+# that they're started by the time we need them.
+
+echo "CTest version:"
+env PATH="${BUILD_PATH}" "${CTEST}" --version | head -1 2>&1 | indent
+
+echo ""
+
+echo "##############################################################################"
+echo "## Configuring test environment"
+echo "##############################################################################"
+
+echo ""
+
+if [ -z "$SKIP_GITDAEMON_TESTS" ]; then
+ echo "Starting git daemon (standard)..."
+ GIT_STANDARD_DIR=`mktemp -d ${TMPDIR}/git_standard.XXXXXXXX`
+ git init --bare "${GIT_STANDARD_DIR}/test.git" >/dev/null
+ git daemon --listen=localhost --export-all --enable=receive-pack --base-path="${GIT_STANDARD_DIR}" "${GIT_STANDARD_DIR}" 2>/dev/null &
+ GIT_STANDARD_PID=$!
+
+ echo "Starting git daemon (namespace)..."
+ GIT_NAMESPACE_DIR=`mktemp -d ${TMPDIR}/git_namespace.XXXXXXXX`
+ cp -R "${SOURCE_DIR}/tests/resources/namespace.git" "${GIT_NAMESPACE_DIR}/namespace.git"
+ GIT_NAMESPACE="name1" git daemon --listen=localhost --port=9419 --export-all --enable=receive-pack --base-path="${GIT_NAMESPACE_DIR}" "${GIT_NAMESPACE_DIR}" &
+ GIT_NAMESPACE_PID=$!
+
+ echo "Starting git daemon (sha256)..."
+ GIT_SHA256_DIR=`mktemp -d ${TMPDIR}/git_sha256.XXXXXXXX`
+ cp -R "${SOURCE_DIR}/tests/resources/testrepo_256.git" "${GIT_SHA256_DIR}/testrepo_256.git"
+ git daemon --listen=localhost --port=9420 --export-all --enable=receive-pack --base-path="${GIT_SHA256_DIR}" "${GIT_SHA256_DIR}" &
+ GIT_SHA256_PID=$!
+fi
+
+if [ -z "$SKIP_PROXY_TESTS" ]; then
+ curl --location --silent --show-error https://github.com/ethomson/poxyproxy/releases/download/v0.7.0/poxyproxy-0.7.0.jar >poxyproxy.jar
+
+ echo "Starting HTTP proxy (Basic)..."
+ java -jar poxyproxy.jar --address 127.0.0.1 --port 8080 --credentials foo:bar --auth-type basic --quiet &
+ PROXY_BASIC_PID=$!
+
+ echo "Starting HTTP proxy (NTLM)..."
+ java -jar poxyproxy.jar --address 127.0.0.1 --port 8090 --credentials foo:bar --auth-type ntlm --quiet &
+ PROXY_NTLM_PID=$!
+fi
+
+if [ -z "$SKIP_NTLM_TESTS" -o -z "$SKIP_ONLINE_TESTS" ]; then
+ curl --location --silent --show-error https://github.com/ethomson/poxygit/releases/download/v0.6.0/poxygit-0.6.0.jar >poxygit.jar
+
+ echo "Starting HTTP server..."
+ HTTP_DIR=`mktemp -d ${TMPDIR}/http.XXXXXXXX`
+ git init --bare "${HTTP_DIR}/test.git"
+ java -jar poxygit.jar --address 127.0.0.1 --port 9000 --credentials foo:baz --quiet "${HTTP_DIR}" &
+ HTTP_PID=$!
+fi
+
+if [ -z "$SKIP_SSH_TESTS" ]; then
+ echo "Starting SSH server..."
+ SSHD_DIR=`mktemp -d ${TMPDIR}/sshd.XXXXXXXX`
+ git init --bare "${SSHD_DIR}/test.git" >/dev/null
+ cat >"${SSHD_DIR}/sshd_config" <<-EOF
+ Port 2222
+ ListenAddress 0.0.0.0
+ Protocol 2
+ HostKey ${SSHD_DIR}/id_rsa
+ PidFile ${SSHD_DIR}/pid
+ AuthorizedKeysFile ${HOME}/.ssh/authorized_keys
+ LogLevel DEBUG
+ RSAAuthentication yes
+ PasswordAuthentication yes
+ PubkeyAuthentication yes
+ ChallengeResponseAuthentication no
+ StrictModes no
+ # Required here as sshd will simply close connection otherwise
+ UsePAM no
+ EOF
+ ssh-keygen -t rsa -f "${SSHD_DIR}/id_rsa" -N "" -q
+ /usr/sbin/sshd -f "${SSHD_DIR}/sshd_config" -E "${SSHD_DIR}/log"
+
+ # Set up keys
+ mkdir "${HOME}/.ssh"
+ ssh-keygen -t rsa -f "${HOME}/.ssh/id_rsa" -N "" -q
+ cat "${HOME}/.ssh/id_rsa.pub" >>"${HOME}/.ssh/authorized_keys"
+ while read algorithm key comment; do
+ echo "[localhost]:2222 $algorithm $key" >>"${HOME}/.ssh/known_hosts"
+ done <"${SSHD_DIR}/id_rsa.pub"
+
+ # Append the github.com keys for the tests that don't override checks.
+ # We ask for ssh-rsa to test that the selection based off of known_hosts
+ # is working.
+ ssh-keyscan -t ssh-rsa github.com >>"${HOME}/.ssh/known_hosts"
+
+ # Get the fingerprint for localhost and remove the colons so we can
+ # parse it as a hex number. Older versions have a different output
+ # format.
+ if [[ $(ssh -V 2>&1) == OpenSSH_6* ]]; then
+ SSH_FINGERPRINT=$(ssh-keygen -F '[localhost]:2222' -f "${HOME}/.ssh/known_hosts" -l | tail -n 1 | cut -d ' ' -f 2 | tr -d ':')
+ else
+ SSH_FINGERPRINT=$(ssh-keygen -E md5 -F '[localhost]:2222' -f "${HOME}/.ssh/known_hosts" -l | tail -n 1 | cut -d ' ' -f 3 | cut -d : -f2- | tr -d :)
+ fi
+fi
+
+# Run the tests that do not require network connectivity.
+
+if [ -z "$SKIP_OFFLINE_TESTS" ]; then
+ echo ""
+ echo "##############################################################################"
+ echo "## Running core tests"
+ echo "##############################################################################"
+
+ echo ""
+ echo "Running libgit2 integration (offline) tests"
+ echo ""
+ run_test offline
+
+ echo ""
+ echo "Running utility tests"
+ echo ""
+ run_test util
+fi
+
+if [ -n "$RUN_INVASIVE_TESTS" ]; then
+ echo ""
+ echo "Running invasive tests"
+ echo ""
+
+ export GITTEST_INVASIVE_FS_SIZE=1
+ export GITTEST_INVASIVE_MEMORY=1
+ export GITTEST_INVASIVE_SPEED=1
+ run_test invasive
+ unset GITTEST_INVASIVE_FS_SIZE
+ unset GITTEST_INVASIVE_MEMORY
+ unset GITTEST_INVASIVE_SPEED
+fi
+
+if [ -z "$SKIP_ONLINE_TESTS" ]; then
+ # Run the online tests. The "online" test suite only includes the
+ # default online tests that do not require additional configuration.
+ # The "proxy" and "ssh" test suites require further setup.
+
+ echo ""
+ echo "##############################################################################"
+ echo "## Running networking (online) tests"
+ echo "##############################################################################"
+
+ export GITTEST_REMOTE_REDIRECT_INITIAL="http://localhost:9000/initial-redirect/libgit2/TestGitRepository"
+ export GITTEST_REMOTE_REDIRECT_SUBSEQUENT="http://localhost:9000/subsequent-redirect/libgit2/TestGitRepository"
+ export GITTEST_REMOTE_SPEED_SLOW="http://localhost:9000/speed-9600/test.git"
+ export GITTEST_REMOTE_SPEED_TIMESOUT="http://localhost:9000/speed-0.5/test.git"
+ run_test online
+ unset GITTEST_REMOTE_REDIRECT_INITIAL
+ unset GITTEST_REMOTE_REDIRECT_SUBSEQUENT
+ unset GITTEST_REMOTE_SPEED_SLOW
+ unset GITTEST_REMOTE_SPEED_TIMESOUT
+
+ # Run the online tests that immutably change global state separately
+ # to avoid polluting the test environment.
+ echo ""
+ echo "Running custom certificate (online_customcert) tests"
+ echo ""
+
+ run_test online_customcert
+fi
+
+if [ -z "$SKIP_GITDAEMON_TESTS" ]; then
+ echo ""
+ echo "Running gitdaemon (standard) tests"
+ echo ""
+
+ export GITTEST_REMOTE_URL="git://localhost/test.git"
+ run_test gitdaemon
+ unset GITTEST_REMOTE_URL
+
+ echo ""
+ echo "Running gitdaemon (namespace) tests"
+ echo ""
+
+ export GITTEST_REMOTE_URL="git://localhost:9419/namespace.git"
+ export GITTEST_REMOTE_BRANCH="four"
+ run_test gitdaemon_namespace
+ unset GITTEST_REMOTE_URL
+ unset GITTEST_REMOTE_BRANCH
+
+ echo ""
+ echo "Running gitdaemon (sha256) tests"
+ echo ""
+
+ export GITTEST_REMOTE_URL="git://localhost:9420/testrepo_256.git"
+ run_test gitdaemon_sha256
+ unset GITTEST_REMOTE_URL
+fi
+
+if [ -z "$SKIP_PROXY_TESTS" ]; then
+ echo ""
+ echo "Running proxy tests (Basic authentication)"
+ echo ""
+
+ export GITTEST_REMOTE_PROXY_HOST="localhost:8080"
+ export GITTEST_REMOTE_PROXY_USER="foo"
+ export GITTEST_REMOTE_PROXY_PASS="bar"
+ run_test proxy
+ unset GITTEST_REMOTE_PROXY_HOST
+ unset GITTEST_REMOTE_PROXY_USER
+ unset GITTEST_REMOTE_PROXY_PASS
+
+ echo ""
+ echo "Running proxy tests (NTLM authentication)"
+ echo ""
+
+ export GITTEST_REMOTE_PROXY_HOST="localhost:8090"
+ export GITTEST_REMOTE_PROXY_USER="foo"
+ export GITTEST_REMOTE_PROXY_PASS="bar"
+ run_test proxy
+ unset GITTEST_REMOTE_PROXY_HOST
+ unset GITTEST_REMOTE_PROXY_USER
+ unset GITTEST_REMOTE_PROXY_PASS
+fi
+
+if [ -z "$SKIP_NTLM_TESTS" ]; then
+ echo ""
+ echo "Running NTLM tests (IIS emulation)"
+ echo ""
+
+ export GITTEST_REMOTE_URL="http://localhost:9000/ntlm/test.git"
+ export GITTEST_REMOTE_USER="foo"
+ export GITTEST_REMOTE_PASS="baz"
+ run_test auth_clone_and_push
+ unset GITTEST_REMOTE_URL
+ unset GITTEST_REMOTE_USER
+ unset GITTEST_REMOTE_PASS
+
+ echo ""
+ echo "Running NTLM tests (Apache emulation)"
+ echo ""
+
+ export GITTEST_REMOTE_URL="http://localhost:9000/broken-ntlm/test.git"
+ export GITTEST_REMOTE_USER="foo"
+ export GITTEST_REMOTE_PASS="baz"
+ run_test auth_clone_and_push
+ unset GITTEST_REMOTE_URL
+ unset GITTEST_REMOTE_USER
+ unset GITTEST_REMOTE_PASS
+fi
+
+if [ -z "$SKIP_NEGOTIATE_TESTS" -a -n "$GITTEST_NEGOTIATE_PASSWORD" ]; then
+ echo ""
+ echo "Running SPNEGO tests"
+ echo ""
+
+ if [ "$(uname -s)" = "Darwin" ]; then
+ KINIT_FLAGS="--password-file=STDIN"
+ fi
+
+ echo $GITTEST_NEGOTIATE_PASSWORD | kinit $KINIT_FLAGS test@LIBGIT2.ORG
+ klist -5f
+
+ export GITTEST_REMOTE_URL="https://test.libgit2.org/kerberos/empty.git"
+ export GITTEST_REMOTE_DEFAULT="true"
+ run_test auth_clone
+ unset GITTEST_REMOTE_URL
+ unset GITTEST_REMOTE_DEFAULT
+
+ echo ""
+ echo "Running SPNEGO tests (expect/continue)"
+ echo ""
+
+ export GITTEST_REMOTE_URL="https://test.libgit2.org/kerberos/empty.git"
+ export GITTEST_REMOTE_DEFAULT="true"
+ export GITTEST_REMOTE_EXPECTCONTINUE="true"
+ run_test auth_clone
+ unset GITTEST_REMOTE_URL
+ unset GITTEST_REMOTE_DEFAULT
+ unset GITTEST_REMOTE_EXPECTCONTINUE
+
+ kdestroy -A
+fi
+
+if [ -z "$SKIP_SSH_TESTS" ]; then
+ export GITTEST_REMOTE_USER=$USER
+ export GITTEST_REMOTE_SSH_KEY="${HOME}/.ssh/id_rsa"
+ export GITTEST_REMOTE_SSH_PUBKEY="${HOME}/.ssh/id_rsa.pub"
+ export GITTEST_REMOTE_SSH_PASSPHRASE=""
+ export GITTEST_REMOTE_SSH_FINGERPRINT="${SSH_FINGERPRINT}"
+
+ echo ""
+ echo "Running ssh tests"
+ echo ""
+
+ export GITTEST_REMOTE_URL="ssh://localhost:2222/$SSHD_DIR/test.git"
+ run_test ssh
+ unset GITTEST_REMOTE_URL
+
+ echo ""
+ echo "Running ssh tests (scp-style paths)"
+ echo ""
+
+ export GITTEST_REMOTE_URL="[localhost:2222]:$SSHD_DIR/test.git"
+ run_test ssh
+ unset GITTEST_REMOTE_URL
+
+ unset GITTEST_REMOTE_USER
+ unset GITTEST_REMOTE_SSH_KEY
+ unset GITTEST_REMOTE_SSH_PUBKEY
+ unset GITTEST_REMOTE_SSH_PASSPHRASE
+ unset GITTEST_REMOTE_SSH_FINGERPRINT
+fi
+
+if [ -z "$SKIP_FUZZERS" ]; then
+ echo ""
+ echo "##############################################################################"
+ echo "## Running fuzzers"
+ echo "##############################################################################"
+
+ env PATH="${BUILD_PATH}" "${CTEST}" -V -R 'fuzzer'
+fi
+
+cleanup
+
+if [ "$SUCCESS" -ne 1 ]; then
+ echo "Some tests failed."
+ exit 1
+fi
+
+echo "Success."
+exit 0
diff --git a/cmake/AddCFlagIfSupported.cmake b/cmake/AddCFlagIfSupported.cmake
new file mode 100644
index 0000000..685f26a
--- /dev/null
+++ b/cmake/AddCFlagIfSupported.cmake
@@ -0,0 +1,30 @@
+# - Append compiler flag to CMAKE_C_FLAGS if compiler supports it
+# ADD_C_FLAG_IF_SUPPORTED(<flag>)
+# <flag> - the compiler flag to test
+# This internally calls the CHECK_C_COMPILER_FLAG macro.
+
+include(CheckCCompilerFlag)
+
+macro(ADD_C_FLAG _FLAG)
+ string(TOUPPER ${_FLAG} UPCASE)
+ string(REGEX REPLACE "[-=]" "_" UPCASE_PRETTY ${UPCASE})
+ string(REGEX REPLACE "^_+" "" UPCASE_PRETTY ${UPCASE_PRETTY})
+ check_c_compiler_flag(${_FLAG} IS_${UPCASE_PRETTY}_SUPPORTED)
+
+ if(IS_${UPCASE_PRETTY}_SUPPORTED)
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${_FLAG}")
+ else()
+ message(FATAL_ERROR "Required flag ${_FLAG} is not supported")
+ endif()
+endmacro()
+
+macro(ADD_C_FLAG_IF_SUPPORTED _FLAG)
+ string(TOUPPER ${_FLAG} UPCASE)
+ string(REGEX REPLACE "[-=]" "_" UPCASE_PRETTY ${UPCASE})
+ string(REGEX REPLACE "^_+" "" UPCASE_PRETTY ${UPCASE_PRETTY})
+ check_c_compiler_flag(${_FLAG} IS_${UPCASE_PRETTY}_SUPPORTED)
+
+ if(IS_${UPCASE_PRETTY}_SUPPORTED)
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${_FLAG}")
+ endif()
+endmacro()
diff --git a/cmake/AddClarTest.cmake b/cmake/AddClarTest.cmake
new file mode 100644
index 0000000..7439416
--- /dev/null
+++ b/cmake/AddClarTest.cmake
@@ -0,0 +1,7 @@
+function(ADD_CLAR_TEST project name)
+ if(NOT USE_LEAK_CHECKER STREQUAL "OFF")
+ add_test(${name} "${PROJECT_SOURCE_DIR}/script/${USE_LEAK_CHECKER}.sh" "${PROJECT_BINARY_DIR}/${project}" ${ARGN})
+ else()
+ add_test(${name} "${PROJECT_BINARY_DIR}/${project}" ${ARGN})
+ endif()
+endfunction(ADD_CLAR_TEST)
diff --git a/cmake/CheckPrototypeDefinitionSafe.cmake b/cmake/CheckPrototypeDefinitionSafe.cmake
new file mode 100644
index 0000000..f82603d
--- /dev/null
+++ b/cmake/CheckPrototypeDefinitionSafe.cmake
@@ -0,0 +1,16 @@
+include(CheckPrototypeDefinition)
+
+function(check_prototype_definition_safe function prototype return header variable)
+ # temporarily save CMAKE_C_FLAGS and disable warnings about unused
+ # unused functions and parameters, otherwise they will always fail
+ # if ENABLE_WERROR is on
+ set(SAVED_CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
+
+ disable_warnings(unused-function)
+ disable_warnings(unused-parameter)
+
+ check_prototype_definition("${function}" "${prototype}" "${return}" "${header}" "${variable}")
+
+ # restore CMAKE_C_FLAGS
+ set(CMAKE_C_FLAGS "${SAVED_CMAKE_C_FLAGS}")
+endfunction()
diff --git a/cmake/DefaultCFlags.cmake b/cmake/DefaultCFlags.cmake
new file mode 100644
index 0000000..a9c9ab9
--- /dev/null
+++ b/cmake/DefaultCFlags.cmake
@@ -0,0 +1,154 @@
+# Platform specific compilation flags
+if(MSVC)
+ add_definitions(-D_SCL_SECURE_NO_WARNINGS)
+ add_definitions(-D_CRT_SECURE_NO_DEPRECATE)
+ add_definitions(-D_CRT_NONSTDC_NO_DEPRECATE)
+
+ string(REPLACE "/Zm1000" " " CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
+
+ # /GF - String pooling
+ # /MP - Parallel build
+ set(CMAKE_C_FLAGS "/GF /MP /nologo ${CMAKE_C_FLAGS}")
+
+ # /Gd - explicitly set cdecl calling convention
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /Gd")
+
+ if(NOT (MSVC_VERSION LESS 1900))
+ # /guard:cf - Enable Control Flow Guard
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /guard:cf")
+ endif()
+
+ if(STATIC_CRT)
+ set(CRT_FLAG_DEBUG "/MTd")
+ set(CRT_FLAG_RELEASE "/MT")
+ else()
+ set(CRT_FLAG_DEBUG "/MDd")
+ set(CRT_FLAG_RELEASE "/MD")
+ endif()
+
+ if(WIN32_LEAKCHECK)
+ set(GIT_WIN32_LEAKCHECK 1)
+ set(CRT_FLAG_DEBUG "${CRT_FLAG_DEBUG}")
+ set(CMAKE_C_STANDARD_LIBRARIES "${CMAKE_C_STANDARD_LIBRARIES} Dbghelp.lib")
+ endif()
+
+ # /Zi - Create debugging information
+ # /Od - Disable optimization
+ # /D_DEBUG - #define _DEBUG
+ # /MTd - Statically link the multithreaded debug version of the CRT
+ # /MDd - Dynamically link the multithreaded debug version of the CRT
+ # /RTC1 - Run time checks
+ set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} /Zi /Od /D_DEBUG /RTC1 ${CRT_FLAG_DEBUG}")
+
+ # /DNDEBUG - Disables asserts
+ # /MT - Statically link the multithreaded release version of the CRT
+ # /MD - Dynamically link the multithreaded release version of the CRT
+ # /O2 - Optimize for speed
+ # /Oy - Enable frame pointer omission (FPO) (otherwise CMake will automatically turn it off)
+ # /GL - Link time code generation (whole program optimization)
+ # /Gy - Function-level linking
+ set(CMAKE_C_FLAGS_RELEASE "/DNDEBUG /O2 /Oy /GL /Gy ${CRT_FLAG_RELEASE}")
+
+ # /Oy- - Disable frame pointer omission (FPO)
+ set(CMAKE_C_FLAGS_RELWITHDEBINFO "/DNDEBUG /Zi /O2 /Oy- /GL /Gy ${CRT_FLAG_RELEASE}")
+
+ # /O1 - Optimize for size
+ set(CMAKE_C_FLAGS_MINSIZEREL "/DNDEBUG /O1 /Oy /GL /Gy ${CRT_FLAG_RELEASE}")
+
+ # /IGNORE:4221 - Ignore empty compilation units
+ set(CMAKE_STATIC_LINKER_FLAGS "/IGNORE:4221")
+
+ # /DYNAMICBASE - Address space load randomization (ASLR)
+ # /NXCOMPAT - Data execution prevention (DEP)
+ # /LARGEADDRESSAWARE - >2GB user address space on x86
+ # /VERSION - Embed version information in PE header
+ set(CMAKE_EXE_LINKER_FLAGS "/DYNAMICBASE /NXCOMPAT /LARGEADDRESSAWARE /VERSION:${libgit2_VERSION_MAJOR}.${libgit2_VERSION_MINOR}")
+
+ if(NOT (MSVC_VERSION LESS 1900))
+ # /GUARD:CF - Enable Control Flow Guard
+ set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /GUARD:CF")
+ endif()
+
+ # /DEBUG - Create a PDB
+ # /LTCG - Link time code generation (whole program optimization)
+ # /OPT:REF /OPT:ICF - Fold out duplicate code at link step
+ # /INCREMENTAL:NO - Required to use /LTCG
+ # /DEBUGTYPE:cv,fixup - Additional data embedded in the PDB (requires /INCREMENTAL:NO, so not on for Debug)
+ set(CMAKE_EXE_LINKER_FLAGS_DEBUG "/DEBUG")
+ set(CMAKE_EXE_LINKER_FLAGS_RELEASE "/RELEASE /LTCG /OPT:REF /OPT:ICF /INCREMENTAL:NO")
+ set(CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO "/DEBUG /RELEASE /LTCG /OPT:REF /OPT:ICF /INCREMENTAL:NO /DEBUGTYPE:cv,fixup")
+ set(CMAKE_EXE_LINKER_FLAGS_MINSIZEREL "/RELEASE /LTCG /OPT:REF /OPT:ICF /INCREMENTAL:NO")
+
+ # Same linker settings for DLL as EXE
+ set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}")
+ set(CMAKE_SHARED_LINKER_FLAGS_DEBUG "${CMAKE_EXE_LINKER_FLAGS_DEBUG}")
+ set(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
+ set(CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO "${CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO}")
+ set(CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL "${CMAKE_EXE_LINKER_FLAGS_MINSIZEREL}")
+else()
+ if(ENABLE_REPRODUCIBLE_BUILDS)
+ set(CMAKE_C_ARCHIVE_CREATE "<CMAKE_AR> Dqc <TARGET> <LINK_FLAGS> <OBJECTS>")
+ set(CMAKE_C_ARCHIVE_APPEND "<CMAKE_AR> Dq <TARGET> <LINK_FLAGS> <OBJECTS>")
+ set(CMAKE_C_ARCHIVE_FINISH "<CMAKE_RANLIB> -D <TARGET>")
+ endif()
+
+ if(NOT BUILD_SHARED_LIBS AND LINK_WITH_STATIC_LIBRARIES)
+ set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
+ endif()
+
+ set(CMAKE_C_FLAGS "-D_GNU_SOURCE ${CMAKE_C_FLAGS}")
+
+ enable_warnings(all)
+ enable_warnings(extra)
+
+ if(CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
+ set(CMAKE_C_FLAGS "-D_POSIX_C_SOURCE=200112L -D__EXTENSIONS__ -D_POSIX_PTHREAD_SEMANTICS ${CMAKE_C_FLAGS}")
+ endif()
+
+ set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -D_DEBUG -O0")
+
+ if(MINGW OR MSYS) # MinGW and MSYS always do PIC and complain if we tell them to
+ string(REGEX REPLACE "-fPIC" "" CMAKE_SHARED_LIBRARY_C_FLAGS "${CMAKE_SHARED_LIBRARY_C_FLAGS}")
+ elseif(BUILD_SHARED_LIBS)
+ add_c_flag_IF_SUPPORTED(-fvisibility=hidden)
+
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC")
+ endif()
+
+ if(MINGW)
+ # MinGW >= 3.14 uses the C99-style stdio functions
+ # automatically, but forks like mingw-w64 still want
+ # us to define this in order to use them
+ add_definitions(-D__USE_MINGW_ANSI_STDIO=1)
+ endif()
+
+ enable_warnings(documentation)
+ disable_warnings(documentation-deprecated-sync)
+ disable_warnings(missing-field-initializers)
+ enable_warnings(missing-declarations)
+ enable_warnings(strict-aliasing)
+ enable_warnings(strict-prototypes)
+ enable_warnings(declaration-after-statement)
+ enable_warnings(shift-count-overflow)
+ enable_warnings(unused-const-variable)
+ enable_warnings(unused-function)
+ enable_warnings(int-conversion)
+ enable_warnings(c11-extensions)
+ enable_warnings(c99-c11-compat)
+
+ # MinGW uses gcc, which expects POSIX formatting for printf, but
+ # uses the Windows C library, which uses its own format specifiers.
+ # Disable format specifier warnings.
+ if(MINGW)
+ disable_warnings(format)
+ disable_warnings(format-security)
+ else()
+ enable_warnings(format)
+ enable_warnings(format-security)
+ endif()
+endif()
+
+# Ensure that MinGW provides the correct header files.
+if(WIN32 AND NOT CYGWIN)
+ add_definitions(-DWIN32 -D_WIN32_WINNT=0x0600)
+endif()
diff --git a/cmake/EnableWarnings.cmake b/cmake/EnableWarnings.cmake
new file mode 100644
index 0000000..0700b52
--- /dev/null
+++ b/cmake/EnableWarnings.cmake
@@ -0,0 +1,15 @@
+macro(ENABLE_WARNINGS flag)
+ add_c_flag_if_supported(-W${flag})
+endmacro()
+
+macro(DISABLE_WARNINGS flag)
+ add_c_flag_if_supported(-Wno-${flag})
+endmacro()
+
+if(ENABLE_WERROR)
+ if(MSVC)
+ add_compile_options(-WX)
+ else()
+ add_c_flag_if_supported(-Werror)
+ endif()
+endif()
diff --git a/cmake/ExperimentalFeatures.cmake b/cmake/ExperimentalFeatures.cmake
new file mode 100644
index 0000000..7eff40b
--- /dev/null
+++ b/cmake/ExperimentalFeatures.cmake
@@ -0,0 +1,23 @@
+# Experimental feature support for libgit2 - developers can opt in to
+# experimental functionality, like sha256 support. When experimental
+# functionality is enabled, we set both a cmake flag *and* a compile
+# definition. The cmake flag is used to generate `experimental.h`,
+# which will be installed by a `make install`. But the compile definition
+# is used by the libgit2 sources to detect the functionality at library
+# build time. This allows us to have an in-tree `experimental.h` with
+# *no* experiments enabled. This lets us support users who build without
+# cmake and cannot generate the `experimental.h` file.
+
+if(EXPERIMENTAL_SHA256)
+ add_feature_info("SHA256 API" ON "experimental SHA256 APIs")
+
+ set(EXPERIMENTAL 1)
+ set(GIT_EXPERIMENTAL_SHA256 1)
+ add_definitions(-DGIT_EXPERIMENTAL_SHA256=1)
+else()
+ add_feature_info("SHA256 API" OFF "experimental SHA256 APIs")
+endif()
+
+if(EXPERIMENTAL)
+ set(LIBGIT2_FILENAME "${LIBGIT2_FILENAME}-experimental")
+endif()
diff --git a/cmake/FindCoreFoundation.cmake b/cmake/FindCoreFoundation.cmake
new file mode 100644
index 0000000..b419ec9
--- /dev/null
+++ b/cmake/FindCoreFoundation.cmake
@@ -0,0 +1,26 @@
+# Find CoreFoundation.framework
+# This will define :
+#
+# COREFOUNDATION_FOUND
+# COREFOUNDATION_LIBRARIES
+# COREFOUNDATION_LDFLAGS
+#
+
+find_path(COREFOUNDATION_INCLUDE_DIR NAMES CoreFoundation.h)
+find_library(COREFOUNDATION_LIBRARIES NAMES CoreFoundation)
+if(COREFOUNDATION_INCLUDE_DIR AND COREFOUNDATION_LIBRARIES)
+ if(NOT CoreFoundation_FIND_QUIETLY)
+ message(STATUS "Found CoreFoundation ${COREFOUNDATION_LIBRARIES}")
+ endif()
+ set(COREFOUNDATION_FOUND TRUE)
+ set(COREFOUNDATION_LDFLAGS "-framework CoreFoundation")
+endif()
+
+if(CoreFoundation_FIND_REQUIRED AND NOT COREFOUNDATION_FOUND)
+ message(FATAL_ERROR "CoreFoundation not found")
+endif()
+
+mark_as_advanced(
+ COREFOUNDATION_INCLUDE_DIR
+ COREFOUNDATION_LIBRARIES
+)
diff --git a/cmake/FindGSSAPI.cmake b/cmake/FindGSSAPI.cmake
new file mode 100644
index 0000000..a11d72a
--- /dev/null
+++ b/cmake/FindGSSAPI.cmake
@@ -0,0 +1,208 @@
+# - Try to find GSSAPI
+# Once done this will define
+#
+# KRB5_CONFIG - Path to krb5-config
+# GSSAPI_ROOT_DIR - Set this variable to the root installation of GSSAPI
+#
+# Read-Only variables:
+# GSSAPI_FLAVOR_MIT - set to TRUE if MIT Kerberos has been found
+# GSSAPI_FLAVOR_HEIMDAL - set to TRUE if Heimdal Kerberos has been found
+# GSSAPI_FOUND - system has GSSAPI
+# GSSAPI_INCLUDE_DIR - the GSSAPI include directory
+# GSSAPI_LIBRARIES - Link these to use GSSAPI
+# GSSAPI_DEFINITIONS - Compiler switches required for using GSSAPI
+#
+#=============================================================================
+# Copyright (c) 2013 Andreas Schneider <asn@cryptomilk.org>
+#
+# Distributed under the OSI-approved BSD License (the "License");
+# see accompanying file Copyright.txt for details.
+#
+# This software is distributed WITHOUT ANY WARRANTY; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+# See the License for more information.
+#=============================================================================
+#
+
+find_path(GSSAPI_ROOT_DIR
+ NAMES include/gssapi.h include/gssapi/gssapi.h
+ HINTS ${_GSSAPI_ROOT_HINTS}
+ PATHS ${_GSSAPI_ROOT_PATHS})
+mark_as_advanced(GSSAPI_ROOT_DIR)
+
+if(UNIX)
+ find_program(KRB5_CONFIG
+ NAMES krb5-config
+ PATHS ${GSSAPI_ROOT_DIR}/bin /opt/local/bin)
+ mark_as_advanced(KRB5_CONFIG)
+
+ if(KRB5_CONFIG)
+ # Check if we have MIT KRB5
+ execute_process(
+ COMMAND ${KRB5_CONFIG} --vendor
+ RESULT_VARIABLE _GSSAPI_VENDOR_RESULT
+ OUTPUT_VARIABLE _GSSAPI_VENDOR_STRING)
+
+ if(_GSSAPI_VENDOR_STRING MATCHES ".*Massachusetts.*")
+ set(GSSAPI_FLAVOR_MIT TRUE)
+ else()
+ execute_process(
+ COMMAND ${KRB5_CONFIG} --libs gssapi
+ RESULT_VARIABLE _GSSAPI_LIBS_RESULT
+ OUTPUT_VARIABLE _GSSAPI_LIBS_STRING)
+
+ if(_GSSAPI_LIBS_STRING MATCHES ".*roken.*")
+ set(GSSAPI_FLAVOR_HEIMDAL TRUE)
+ endif()
+ endif()
+
+ # Get the include dir
+ execute_process(
+ COMMAND ${KRB5_CONFIG} --cflags gssapi
+ RESULT_VARIABLE _GSSAPI_INCLUDE_RESULT
+ OUTPUT_VARIABLE _GSSAPI_INCLUDE_STRING)
+ string(REGEX REPLACE "(\r?\n)+$" "" _GSSAPI_INCLUDE_STRING "${_GSSAPI_INCLUDE_STRING}")
+ string(REGEX REPLACE " *-I" "" _GSSAPI_INCLUDEDIR "${_GSSAPI_INCLUDE_STRING}")
+ endif()
+
+ if(NOT GSSAPI_FLAVOR_MIT AND NOT GSSAPI_FLAVOR_HEIMDAL)
+ # Check for HEIMDAL
+ find_package(PkgConfig)
+ if(PKG_CONFIG_FOUND)
+ pkg_check_modules(_GSSAPI heimdal-gssapi)
+ endif()
+
+ if(_GSSAPI_FOUND)
+ set(GSSAPI_FLAVOR_HEIMDAL TRUE)
+ else()
+ find_path(_GSSAPI_ROKEN
+ NAMES roken.h
+ PATHS ${GSSAPI_ROOT_DIR}/include ${_GSSAPI_INCLUDEDIR})
+ if(_GSSAPI_ROKEN)
+ set(GSSAPI_FLAVOR_HEIMDAL TRUE)
+ endif()
+ endif()
+ endif()
+endif()
+
+find_path(GSSAPI_INCLUDE_DIR
+ NAMES gssapi.h gssapi/gssapi.h
+ PATHS ${GSSAPI_ROOT_DIR}/include ${_GSSAPI_INCLUDEDIR})
+
+if(GSSAPI_FLAVOR_MIT)
+ find_library(GSSAPI_LIBRARY
+ NAMES gssapi_krb5
+ PATHS ${GSSAPI_ROOT_DIR}/lib ${_GSSAPI_LIBDIR})
+
+ find_library(KRB5_LIBRARY
+ NAMES krb5
+ PATHS ${GSSAPI_ROOT_DIR}/lib ${_GSSAPI_LIBDIR})
+
+ find_library(K5CRYPTO_LIBRARY
+ NAMES k5crypto
+ PATHS ${GSSAPI_ROOT_DIR}/lib ${_GSSAPI_LIBDIR})
+
+ find_library(COM_ERR_LIBRARY
+ NAMES com_err
+ PATHS ${GSSAPI_ROOT_DIR}/lib ${_GSSAPI_LIBDIR})
+
+ if(GSSAPI_LIBRARY)
+ set(GSSAPI_LIBRARIES ${GSSAPI_LIBRARIES} ${GSSAPI_LIBRARY})
+ endif()
+
+ if(KRB5_LIBRARY)
+ set(GSSAPI_LIBRARIES ${GSSAPI_LIBRARIES} ${KRB5_LIBRARY})
+ endif()
+
+ if(K5CRYPTO_LIBRARY)
+ set(GSSAPI_LIBRARIES ${GSSAPI_LIBRARIES} ${K5CRYPTO_LIBRARY})
+ endif()
+
+ if(COM_ERR_LIBRARY)
+ set(GSSAPI_LIBRARIES ${GSSAPI_LIBRARIES} ${COM_ERR_LIBRARY})
+ endif()
+endif()
+
+if(GSSAPI_FLAVOR_HEIMDAL)
+ find_library(GSSAPI_LIBRARY
+ NAMES gssapi
+ PATHS ${GSSAPI_ROOT_DIR}/lib ${_GSSAPI_LIBDIR})
+
+ find_library(KRB5_LIBRARY
+ NAMES krb5
+ PATHS ${GSSAPI_ROOT_DIR}/lib ${_GSSAPI_LIBDIR})
+
+ find_library(HCRYPTO_LIBRARY
+ NAMES hcrypto
+ PATHS ${GSSAPI_ROOT_DIR}/lib ${_GSSAPI_LIBDIR})
+
+ find_library(COM_ERR_LIBRARY
+ NAMES com_err
+ PATHS ${GSSAPI_ROOT_DIR}/lib ${_GSSAPI_LIBDIR})
+
+ find_library(HEIMNTLM_LIBRARY
+ NAMES heimntlm
+ PATHS ${GSSAPI_ROOT_DIR}/lib ${_GSSAPI_LIBDIR})
+
+ find_library(HX509_LIBRARY
+ NAMES hx509
+ PATHS ${GSSAPI_ROOT_DIR}/lib ${_GSSAPI_LIBDIR})
+
+ find_library(ASN1_LIBRARY
+ NAMES asn1
+ PATHS ${GSSAPI_ROOT_DIR}/lib ${_GSSAPI_LIBDIR})
+
+ find_library(WIND_LIBRARY
+ NAMES wind
+ PATHS ${GSSAPI_ROOT_DIR}/lib ${_GSSAPI_LIBDIR})
+
+ find_library(ROKEN_LIBRARY
+ NAMES roken
+ PATHS ${GSSAPI_ROOT_DIR}/lib ${_GSSAPI_LIBDIR})
+
+ if(GSSAPI_LIBRARY)
+ set(GSSAPI_LIBRARIES ${GSSAPI_LIBRARIES} ${GSSAPI_LIBRARY})
+ endif()
+
+ if(KRB5_LIBRARY)
+ set(GSSAPI_LIBRARIES ${GSSAPI_LIBRARIES} ${KRB5_LIBRARY})
+ endif()
+
+ if(HCRYPTO_LIBRARY)
+ set(GSSAPI_LIBRARIES ${GSSAPI_LIBRARIES} ${HCRYPTO_LIBRARY})
+ endif()
+
+ if(COM_ERR_LIBRARY)
+ set(GSSAPI_LIBRARIES ${GSSAPI_LIBRARIES} ${COM_ERR_LIBRARY})
+ endif()
+
+ if(HEIMNTLM_LIBRARY)
+ set(GSSAPI_LIBRARIES ${GSSAPI_LIBRARIES} ${HEIMNTLM_LIBRARY})
+ endif()
+
+ if(HX509_LIBRARY)
+ set(GSSAPI_LIBRARIES ${GSSAPI_LIBRARIES} ${HX509_LIBRARY})
+ endif()
+
+ if(ASN1_LIBRARY)
+ set(GSSAPI_LIBRARIES ${GSSAPI_LIBRARIES} ${ASN1_LIBRARY})
+ endif()
+
+ if(WIND_LIBRARY)
+ set(GSSAPI_LIBRARIES ${GSSAPI_LIBRARIES} ${WIND_LIBRARY})
+ endif()
+
+ if(ROKEN_LIBRARY)
+ set(GSSAPI_LIBRARIES ${GSSAPI_LIBRARIES} ${WIND_LIBRARY})
+ endif()
+endif()
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(GSSAPI DEFAULT_MSG GSSAPI_LIBRARIES GSSAPI_INCLUDE_DIR)
+
+if(GSSAPI_INCLUDE_DIRS AND GSSAPI_LIBRARIES)
+ set(GSSAPI_FOUND TRUE)
+endif(GSSAPI_INCLUDE_DIRS AND GSSAPI_LIBRARIES)
+
+# show the GSSAPI_INCLUDE_DIRS and GSSAPI_LIBRARIES variables only in the advanced view
+mark_as_advanced(GSSAPI_INCLUDE_DIRS GSSAPI_LIBRARIES)
diff --git a/cmake/FindGSSFramework.cmake b/cmake/FindGSSFramework.cmake
new file mode 100644
index 0000000..1b0c936
--- /dev/null
+++ b/cmake/FindGSSFramework.cmake
@@ -0,0 +1,28 @@
+# Find GSS.framework
+# This will define :
+#
+# GSSFRAMEWORK_FOUND
+# GSSFRAMEWORK_INCLUDE_DIR
+# GSSFRAMEWORK_LIBRARIES
+# GSSFRAMEWORK_LDFLAGS
+#
+
+find_path(GSSFRAMEWORK_INCLUDE_DIR NAMES GSS.h)
+find_library(GSSFRAMEWORK_LIBRARIES NAMES GSS)
+if(GSSFRAMEWORK_INCLUDE_DIR AND GSSFRAMEWORK_LIBRARIES)
+ if(NOT CoreFoundation_FIND_QUIETLY)
+ message(STATUS "Found GSS.framework ${GSSFRAMEWORK_LIBRARIES}")
+ endif()
+ set(GSSFRAMEWORK_FOUND TRUE)
+ set(GSSFRAMEWORK_LDFLAGS "-framework GSS")
+endif()
+
+if(GSS_FIND_REQUIRED AND NOT GSSFRAMEWORK_FOUND)
+ message(FATAL_ERROR "CoreFoundation not found")
+endif()
+
+mark_as_advanced(
+ GSSFRAMEWORK_INCLUDE_DIR
+ GSSFRAMEWORK_LIBRARIES
+ GSSFRAMEWORK_LDFLAGS
+)
diff --git a/cmake/FindHTTPParser.cmake b/cmake/FindHTTPParser.cmake
new file mode 100644
index 0000000..3350190
--- /dev/null
+++ b/cmake/FindHTTPParser.cmake
@@ -0,0 +1,39 @@
+# - Try to find http-parser
+#
+# Defines the following variables:
+#
+# HTTP_PARSER_FOUND - system has http-parser
+# HTTP_PARSER_INCLUDE_DIR - the http-parser include directory
+# HTTP_PARSER_LIBRARIES - Link these to use http-parser
+# HTTP_PARSER_VERSION_MAJOR - major version
+# HTTP_PARSER_VERSION_MINOR - minor version
+# HTTP_PARSER_VERSION_STRING - the version of http-parser found
+
+# Find the header and library
+find_path(HTTP_PARSER_INCLUDE_DIR NAMES http_parser.h)
+find_library(HTTP_PARSER_LIBRARY NAMES http_parser libhttp_parser)
+
+# Found the header, read version
+if(HTTP_PARSER_INCLUDE_DIR AND EXISTS "${HTTP_PARSER_INCLUDE_DIR}/http_parser.h")
+ file(READ "${HTTP_PARSER_INCLUDE_DIR}/http_parser.h" HTTP_PARSER_H)
+ if(HTTP_PARSER_H)
+ string(REGEX REPLACE ".*#define[\t ]+HTTP_PARSER_VERSION_MAJOR[\t ]+([0-9]+).*" "\\1" HTTP_PARSER_VERSION_MAJOR "${HTTP_PARSER_H}")
+ string(REGEX REPLACE ".*#define[\t ]+HTTP_PARSER_VERSION_MINOR[\t ]+([0-9]+).*" "\\1" HTTP_PARSER_VERSION_MINOR "${HTTP_PARSER_H}")
+ set(HTTP_PARSER_VERSION_STRING "${HTTP_PARSER_VERSION_MAJOR}.${HTTP_PARSER_VERSION_MINOR}")
+ endif()
+ unset(HTTP_PARSER_H)
+endif()
+
+# Handle the QUIETLY and REQUIRED arguments and set HTTP_PARSER_FOUND
+# to TRUE if all listed variables are TRUE
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(HTTP_Parser REQUIRED_VARS HTTP_PARSER_INCLUDE_DIR HTTP_PARSER_LIBRARY)
+
+# Hide advanced variables
+mark_as_advanced(HTTP_PARSER_INCLUDE_DIR HTTP_PARSER_LIBRARY)
+
+# Set standard variables
+if(HTTP_PARSER_FOUND)
+ set(HTTP_PARSER_LIBRARIES ${HTTP_PARSER_LIBRARY})
+ set(HTTP_PARSER_INCLUDE_DIRS ${HTTP_PARSER_INCLUDE_DIR})
+endif()
diff --git a/cmake/FindIconv.cmake b/cmake/FindIconv.cmake
new file mode 100644
index 0000000..9e6ded9
--- /dev/null
+++ b/cmake/FindIconv.cmake
@@ -0,0 +1,45 @@
+# - Try to find Iconv
+# Once done this will define
+#
+# ICONV_FOUND - system has Iconv
+# ICONV_INCLUDE_DIR - the Iconv include directory
+# ICONV_LIBRARIES - Link these to use Iconv
+#
+
+if(ICONV_INCLUDE_DIR AND ICONV_LIBRARIES)
+ # Already in cache, be silent
+ set(ICONV_FIND_QUIETLY TRUE)
+endif()
+
+find_path(ICONV_INCLUDE_DIR iconv.h)
+check_function_exists(iconv_open libc_has_iconv)
+find_library(iconv_lib NAMES iconv libiconv libiconv-2 c)
+
+if(ICONV_INCLUDE_DIR AND libc_has_iconv)
+ set(ICONV_FOUND TRUE)
+ set(ICONV_LIBRARIES "")
+ if(NOT ICONV_FIND_QUIETLY)
+ message(STATUS "Found Iconv: provided by libc")
+ endif(NOT ICONV_FIND_QUIETLY)
+elseif(ICONV_INCLUDE_DIR AND iconv_lib)
+ set(ICONV_FOUND TRUE)
+ # split iconv into -L and -l linker options, so we can
+ # set them for pkg-config
+ get_filename_component(iconv_path ${iconv_lib} PATH)
+ get_filename_component(iconv_name ${iconv_lib} NAME_WE)
+ string(REGEX REPLACE "^lib" "" iconv_name ${iconv_name})
+ set(ICONV_LIBRARIES "-L${iconv_path} -l${iconv_name}")
+
+ if(NOT ICONV_FIND_QUIETLY)
+ message(STATUS "Found Iconv: ${ICONV_LIBRARIES}")
+ endif()
+else()
+ if(Iconv_FIND_REQUIRED)
+ message(FATAL_ERROR "Could not find Iconv")
+ endif(Iconv_FIND_REQUIRED)
+endif()
+
+mark_as_advanced(
+ ICONV_INCLUDE_DIR
+ ICONV_LIBRARIES
+)
diff --git a/cmake/FindLibSSH2.cmake b/cmake/FindLibSSH2.cmake
new file mode 100644
index 0000000..c571997
--- /dev/null
+++ b/cmake/FindLibSSH2.cmake
@@ -0,0 +1,13 @@
+# LIBSSH2_FOUND - system has the libssh2 library
+# LIBSSH2_INCLUDE_DIR - the libssh2 include directory
+# LIBSSH2_LIBRARY - the libssh2 library name
+
+find_path(LIBSSH2_INCLUDE_DIR libssh2.h)
+
+find_library(LIBSSH2_LIBRARY NAMES ssh2 libssh2)
+
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(LibSSH2
+ REQUIRED_VARS LIBSSH2_LIBRARY LIBSSH2_INCLUDE_DIR)
+
+mark_as_advanced(LIBSSH2_INCLUDE_DIR LIBSSH2_LIBRARY)
diff --git a/cmake/FindPCRE.cmake b/cmake/FindPCRE.cmake
new file mode 100644
index 0000000..02e7edc
--- /dev/null
+++ b/cmake/FindPCRE.cmake
@@ -0,0 +1,37 @@
+# Copyright (C) 2007-2009 LuaDist.
+# Created by Peter Kapec <kapecp@gmail.com>
+# Redistribution and use of this file is allowed according to the terms of the MIT license.
+# For details see the COPYRIGHT file distributed with LuaDist.
+# Note:
+# Searching headers and libraries is very simple and is NOT as powerful as scripts
+# distributed with CMake, because LuaDist defines directories to search for.
+# Everyone is encouraged to contact the author with improvements. Maybe this file
+# becomes part of CMake distribution sometimes.
+
+# - Find pcre
+# Find the native PCRE headers and libraries.
+#
+# PCRE_INCLUDE_DIRS - where to find pcre.h, etc.
+# PCRE_LIBRARIES - List of libraries when using pcre.
+# PCRE_FOUND - True if pcre found.
+
+# Look for the header file.
+find_path(PCRE_INCLUDE_DIR NAMES pcre.h)
+
+# Look for the library.
+find_library(PCRE_LIBRARY NAMES pcre)
+
+# Handle the QUIETLY and REQUIRED arguments and set PCRE_FOUND to TRUE if all listed variables are TRUE.
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(PCRE DEFAULT_MSG PCRE_LIBRARY PCRE_INCLUDE_DIR)
+
+# Copy the results to the output variables.
+if(PCRE_FOUND)
+ set(PCRE_LIBRARIES ${PCRE_LIBRARY})
+ set(PCRE_INCLUDE_DIRS ${PCRE_INCLUDE_DIR})
+else(PCRE_FOUND)
+ set(PCRE_LIBRARIES)
+ set(PCRE_INCLUDE_DIRS)
+endif()
+
+mark_as_advanced(PCRE_INCLUDE_DIRS PCRE_LIBRARIES)
diff --git a/cmake/FindPCRE2.cmake b/cmake/FindPCRE2.cmake
new file mode 100644
index 0000000..41c165b
--- /dev/null
+++ b/cmake/FindPCRE2.cmake
@@ -0,0 +1,37 @@
+# Copyright (C) 2007-2009 LuaDist.
+# Created by Peter Kapec <kapecp@gmail.com>
+# Redistribution and use of this file is allowed according to the terms of the MIT license.
+# For details see the COPYRIGHT file distributed with LuaDist.
+# Note:
+# Searching headers and libraries is very simple and is NOT as powerful as scripts
+# distributed with CMake, because LuaDist defines directories to search for.
+# Everyone is encouraged to contact the author with improvements. Maybe this file
+# becomes part of CMake distribution sometimes.
+
+# - Find pcre
+# Find the native PCRE2 headers and libraries.
+#
+# PCRE2_INCLUDE_DIRS - where to find pcre.h, etc.
+# PCRE2_LIBRARIES - List of libraries when using pcre.
+# PCRE2_FOUND - True if pcre found.
+
+# Look for the header file.
+find_path(PCRE2_INCLUDE_DIR NAMES pcre2.h)
+
+# Look for the library.
+find_library(PCRE2_LIBRARY NAMES pcre2-8)
+
+# Handle the QUIETLY and REQUIRED arguments and set PCRE2_FOUND to TRUE if all listed variables are TRUE.
+include(FindPackageHandleStandardArgs)
+find_package_handle_standard_args(PCRE2 DEFAULT_MSG PCRE2_LIBRARY PCRE2_INCLUDE_DIR)
+
+# Copy the results to the output variables.
+if(PCRE2_FOUND)
+ set(PCRE2_LIBRARIES ${PCRE2_LIBRARY})
+ set(PCRE2_INCLUDE_DIRS ${PCRE2_INCLUDE_DIR})
+else(PCRE2_FOUND)
+ set(PCRE2_LIBRARIES)
+ set(PCRE2_INCLUDE_DIRS)
+endif()
+
+mark_as_advanced(PCRE2_INCLUDE_DIRS PCRE2_LIBRARIES)
diff --git a/cmake/FindPkgLibraries.cmake b/cmake/FindPkgLibraries.cmake
new file mode 100644
index 0000000..220bb2c
--- /dev/null
+++ b/cmake/FindPkgLibraries.cmake
@@ -0,0 +1,28 @@
+include(FindPkgConfig)
+
+# This function will find and set up a pkg-config based module.
+# If a pc-file was found, it will resolve library paths to
+# absolute paths. Furthermore, the function will automatically
+# fall back to use static libraries in case no dynamic libraries
+# were found.
+function(FIND_PKGLIBRARIES prefix package)
+ pkg_check_modules(${prefix} ${package})
+ if(NOT ${prefix}_FOUND)
+ return()
+ endif()
+
+ foreach(LIBRARY ${${prefix}_LIBRARIES})
+ find_library(${LIBRARY}_RESOLVED ${LIBRARY} PATHS ${${prefix}_LIBRARY_DIRS})
+ if(${${LIBRARY}_RESOLVED} STREQUAL "${LIBRARY}_RESOLVED-NOTFOUND")
+ message(FATAL_ERROR "could not resolve ${LIBRARY}")
+ endif()
+ list(APPEND RESOLVED_LIBRARIES ${${LIBRARY}_RESOLVED})
+ endforeach()
+
+ set(${prefix}_FOUND 1 PARENT_SCOPE)
+ set(${prefix}_LIBRARIES ${RESOLVED_LIBRARIES} PARENT_SCOPE)
+ set(${prefix}_INCLUDE_DIRS ${${prefix}_INCLUDE_DIRS} PARENT_SCOPE)
+ set(${prefix}_LDFLAGS ${${prefix}_LDFLAGS} PARENT_SCOPE)
+
+ message(STATUS " Resolved libraries: ${RESOLVED_LIBRARIES}")
+endfunction()
diff --git a/cmake/FindSecurity.cmake b/cmake/FindSecurity.cmake
new file mode 100644
index 0000000..14a2e2d
--- /dev/null
+++ b/cmake/FindSecurity.cmake
@@ -0,0 +1,28 @@
+# Find Security.framework
+# This will define :
+#
+# SECURITY_FOUND
+# SECURITY_LIBRARIES
+# SECURITY_LDFLAGS
+# SECURITY_HAS_SSLCREATECONTEXT
+#
+
+find_path(SECURITY_INCLUDE_DIR NAMES Security/Security.h)
+find_library(SECURITY_LIBRARIES NAMES Security)
+if(SECURITY_INCLUDE_DIR AND SECURITY_LIBRARIES)
+ if(NOT Security_FIND_QUIETLY)
+ message(STATUS "Found Security ${SECURITY_LIBRARIES}")
+ endif()
+ set(SECURITY_FOUND TRUE)
+ set(SECURITY_LDFLAGS "-framework Security")
+ check_library_exists("${SECURITY_LIBRARIES}" SSLCreateContext "Security/SecureTransport.h" SECURITY_HAS_SSLCREATECONTEXT)
+endif()
+
+if(Security_FIND_REQUIRED AND NOT SECURITY_FOUND)
+ message(FATAL_ERROR "Security not found")
+endif()
+
+mark_as_advanced(
+ SECURITY_INCLUDE_DIR
+ SECURITY_LIBRARIES
+)
diff --git a/cmake/FindStatNsec.cmake b/cmake/FindStatNsec.cmake
new file mode 100644
index 0000000..9dfdf51
--- /dev/null
+++ b/cmake/FindStatNsec.cmake
@@ -0,0 +1,20 @@
+include(FeatureSummary)
+
+check_struct_has_member("struct stat" st_mtim "sys/types.h;sys/stat.h"
+ HAVE_STRUCT_STAT_ST_MTIM LANGUAGE C)
+check_struct_has_member("struct stat" st_mtimespec "sys/types.h;sys/stat.h"
+ HAVE_STRUCT_STAT_ST_MTIMESPEC LANGUAGE C)
+check_struct_has_member("struct stat" st_mtime_nsec sys/stat.h
+ HAVE_STRUCT_STAT_MTIME_NSEC LANGUAGE C)
+
+if(HAVE_STRUCT_STAT_ST_MTIM)
+ check_struct_has_member("struct stat" st_mtim.tv_nsec sys/stat.h
+ HAVE_STRUCT_STAT_NSEC LANGUAGE C)
+elseif(HAVE_STRUCT_STAT_ST_MTIMESPEC)
+ check_struct_has_member("struct stat" st_mtimespec.tv_nsec sys/stat.h
+ HAVE_STRUCT_STAT_NSEC LANGUAGE C)
+else()
+ set(HAVE_STRUCT_STAT_NSEC ON )
+endif()
+
+add_feature_info(nanoseconds USE_NSEC "support nanosecond precision file mtimes and ctimes")
diff --git a/cmake/Findfutimens.cmake b/cmake/Findfutimens.cmake
new file mode 100644
index 0000000..3449c9d
--- /dev/null
+++ b/cmake/Findfutimens.cmake
@@ -0,0 +1,14 @@
+include(EnableWarnings)
+
+if(APPLE)
+ # We cannot simply CHECK_FUNCTION_EXISTS on macOS because
+ # MACOSX_DEPLOYMENT_TARGET may be set to a version in the past
+ # that doesn't have futimens. Instead we need to enable warnings
+ # as errors, then check for the symbol existing in `sys/stat.h`,
+ # then reset warnings as errors.
+ enable_warnings(error)
+ check_symbol_exists(futimens sys/stat.h HAVE_FUTIMENS)
+ disable_warnings(error)
+else()
+ check_function_exists(futimens HAVE_FUTIMENS)
+endif()
diff --git a/cmake/FindmbedTLS.cmake b/cmake/FindmbedTLS.cmake
new file mode 100644
index 0000000..a4a5487
--- /dev/null
+++ b/cmake/FindmbedTLS.cmake
@@ -0,0 +1,86 @@
+# - Try to find mbedTLS
+# Once done this will define
+#
+# Read-Only variables
+# MBEDTLS_FOUND - system has mbedTLS
+# MBEDTLS_INCLUDE_DIR - the mbedTLS include directory
+# MBEDTLS_LIBRARY_DIR - the mbedTLS library directory
+# MBEDTLS_LIBRARIES - Link these to use mbedTLS
+# MBEDTLS_LIBRARY - path to mbedTLS library
+# MBEDX509_LIBRARY - path to mbedTLS X.509 library
+# MBEDCRYPTO_LIBRARY - path to mbedTLS Crypto library
+#
+# Hint
+# MBEDTLS_ROOT_DIR can be pointed to a local mbedTLS installation.
+
+set(_MBEDTLS_ROOT_HINTS
+ ${MBEDTLS_ROOT_DIR}
+ ENV MBEDTLS_ROOT_DIR)
+
+set(_MBEDTLS_ROOT_HINTS_AND_PATHS
+ HINTS ${_MBEDTLS_ROOT_HINTS}
+ PATHS ${_MBEDTLS_ROOT_PATHS})
+
+find_path(MBEDTLS_INCLUDE_DIR
+ NAMES mbedtls/version.h
+ ${_MBEDTLS_ROOT_HINTS_AND_PATHS}
+ PATH_SUFFIXES include)
+
+if(MBEDTLS_INCLUDE_DIR AND MBEDTLS_LIBRARIES)
+ # Already in cache, be silent
+ set(MBEDTLS_FIND_QUIETLY TRUE)
+endif()
+
+find_library(MBEDTLS_LIBRARY
+ NAMES mbedtls libmbedtls
+ ${_MBEDTLS_ROOT_HINTS_AND_PATHS}
+ PATH_SUFFIXES library)
+find_library(MBEDX509_LIBRARY
+ NAMES mbedx509 libmbedx509
+ ${_MBEDTLS_ROOT_HINTS_AND_PATHS}
+ PATH_SUFFIXES library)
+find_library(MBEDCRYPTO_LIBRARY
+ NAMES mbedcrypto libmbedcrypto
+ ${_MBEDTLS_ROOT_HINTS_AND_PATHS}
+ PATH_SUFFIXES library)
+
+if(MBEDTLS_INCLUDE_DIR AND MBEDTLS_LIBRARY AND MBEDX509_LIBRARY AND MBEDCRYPTO_LIBRARY)
+ set(MBEDTLS_FOUND TRUE)
+endif()
+
+if(MBEDTLS_FOUND)
+ # split mbedTLS into -L and -l linker options, so we can set them for pkg-config
+ get_filename_component(MBEDTLS_LIBRARY_DIR ${MBEDTLS_LIBRARY} PATH)
+ get_filename_component(MBEDTLS_LIBRARY_FILE ${MBEDTLS_LIBRARY} NAME_WE)
+ get_filename_component(MBEDX509_LIBRARY_FILE ${MBEDX509_LIBRARY} NAME_WE)
+ get_filename_component(MBEDCRYPTO_LIBRARY_FILE ${MBEDCRYPTO_LIBRARY} NAME_WE)
+ string(REGEX REPLACE "^lib" "" MBEDTLS_LIBRARY_FILE ${MBEDTLS_LIBRARY_FILE})
+ string(REGEX REPLACE "^lib" "" MBEDX509_LIBRARY_FILE ${MBEDX509_LIBRARY_FILE})
+ string(REGEX REPLACE "^lib" "" MBEDCRYPTO_LIBRARY_FILE ${MBEDCRYPTO_LIBRARY_FILE})
+ set(MBEDTLS_LIBRARIES "-L${MBEDTLS_LIBRARY_DIR} -l${MBEDTLS_LIBRARY_FILE} -l${MBEDX509_LIBRARY_FILE} -l${MBEDCRYPTO_LIBRARY_FILE}")
+
+ if(NOT MBEDTLS_FIND_QUIETLY)
+ message(STATUS "Found mbedTLS:")
+ file(READ ${MBEDTLS_INCLUDE_DIR}/mbedtls/version.h MBEDTLSCONTENT)
+ string(REGEX MATCH "MBEDTLS_VERSION_STRING +\"[0-9|.]+\"" MBEDTLSMATCH ${MBEDTLSCONTENT})
+ if(MBEDTLSMATCH)
+ string(REGEX REPLACE "MBEDTLS_VERSION_STRING +\"([0-9|.]+)\"" "\\1" MBEDTLS_VERSION ${MBEDTLSMATCH})
+ message(STATUS " version ${MBEDTLS_VERSION}")
+ endif()
+ message(STATUS " TLS: ${MBEDTLS_LIBRARY}")
+ message(STATUS " X509: ${MBEDX509_LIBRARY}")
+ message(STATUS " Crypto: ${MBEDCRYPTO_LIBRARY}")
+ endif()
+else(MBEDTLS_FOUND)
+ if(MBEDTLS_FIND_REQUIRED)
+ message(FATAL_ERROR "Could not find mbedTLS")
+ endif()
+endif()
+
+mark_as_advanced(
+ MBEDTLS_INCLUDE_DIR
+ MBEDTLS_LIBRARY_DIR
+ MBEDTLS_LIBRARIES
+ MBEDTLS_LIBRARY
+ MBEDX509_LIBRARY
+ MBEDCRYPTO_LIBRARY)
diff --git a/cmake/IdeSplitSources.cmake b/cmake/IdeSplitSources.cmake
new file mode 100644
index 0000000..4f928ac
--- /dev/null
+++ b/cmake/IdeSplitSources.cmake
@@ -0,0 +1,22 @@
+# This function splits the sources files up into their appropriate
+# subdirectories. This is especially useful for IDEs like Xcode and
+# Visual Studio, so that you can navigate into the libgit2_tests project,
+# and see the folders within the tests folder (instead of just seeing all
+# source and tests in a single folder.)
+function(IDE_SPLIT_SOURCES target)
+ if(MSVC_IDE OR CMAKE_GENERATOR STREQUAL Xcode)
+ get_target_property(sources ${target} SOURCES)
+ foreach(source ${sources})
+ if(source MATCHES ".*/")
+ string(REPLACE ${PROJECT_SOURCE_DIR}/ "" rel ${source})
+ if(rel)
+ string(REGEX REPLACE "/([^/]*)$" "" rel ${rel})
+ if(rel)
+ string(REPLACE "/" "\\\\" rel ${rel})
+ source_group(${rel} FILES ${source})
+ endif()
+ endif()
+ endif()
+ endforeach()
+ endif()
+endfunction()
diff --git a/cmake/PkgBuildConfig.cmake b/cmake/PkgBuildConfig.cmake
new file mode 100644
index 0000000..c8939e6
--- /dev/null
+++ b/cmake/PkgBuildConfig.cmake
@@ -0,0 +1,77 @@
+# pkg-config file generation
+#
+
+function(pkg_build_config)
+ set(options)
+ set(oneValueArgs NAME DESCRIPTION VERSION FILENAME LIBS_SELF)
+ set(multiValueArgs LIBS PRIVATE_LIBS REQUIRES CFLAGS)
+
+ cmake_parse_arguments(PKGCONFIG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ if (NOT DEFINED PKGCONFIG_FILENAME AND DEFINED PKGCONFIG_NAME)
+ set(PKGCONFIG_FILENAME ${PKGCONFIG_NAME})
+ endif()
+ if (NOT DEFINED PKGCONFIG_FILENAME)
+ message(FATAL_ERROR "Missing FILENAME argument")
+ endif()
+ set(PKGCONFIG_FILE "${PROJECT_BINARY_DIR}/${PKGCONFIG_FILENAME}.pc")
+
+ if (NOT DEFINED PKGCONFIG_DESCRIPTION)
+ message(FATAL_ERROR "Missing DESCRIPTION argument")
+ endif()
+
+ if (NOT DEFINED PKGCONFIG_VERSION)
+ message(FATAL_ERROR "Missing VERSION argument")
+ endif()
+
+ # Write .pc "header"
+ file(WRITE "${PKGCONFIG_FILE}"
+ "prefix=\"${CMAKE_INSTALL_PREFIX}\"\n"
+ "libdir=\"${CMAKE_INSTALL_FULL_LIBDIR}\"\n"
+ "includedir=\"${CMAKE_INSTALL_FULL_INCLUDEDIR}\"\n"
+ "\n"
+ "Name: ${PKGCONFIG_NAME}\n"
+ "Description: ${PKGCONFIG_DESCRIPTION}\n"
+ "Version: ${PKGCONFIG_VERSION}\n"
+ )
+
+ # Prepare Libs
+ if(NOT DEFINED PKGCONFIG_LIBS_SELF)
+ set(PKGCONFIG_LIBS_SELF "${PKGCONFIG_FILE}")
+ endif()
+
+ if(NOT DEFINED PKGCONFIG_LIBS)
+ set(PKGCONFIG_LIBS "-l${PKGCONFIG_LIBS_SELF}")
+ else()
+ list(INSERT PKGCONFIG_LIBS 0 "-l${PKGCONFIG_LIBS_SELF}")
+ endif()
+
+ list(REMOVE_DUPLICATES PKGCONFIG_LIBS)
+ string(REPLACE ";" " " PKGCONFIG_LIBS "${PKGCONFIG_LIBS}")
+ file(APPEND "${PKGCONFIG_FILE}" "Libs: -L\${libdir} ${PKGCONFIG_LIBS}\n")
+
+ # Prepare Libs.private
+ if(DEFINED PKGCONFIG_PRIVATE_LIBS)
+ list(REMOVE_DUPLICATES PKGCONFIG_PRIVATE_LIBS)
+ string(REPLACE ";" " " PKGCONFIG_PRIVATE_LIBS "${PKGCONFIG_PRIVATE_LIBS}")
+ file(APPEND "${PKGCONFIG_FILE}" "Libs.private: ${PKGCONFIG_PRIVATE_LIBS}\n")
+ endif()
+
+ # Prepare Requires.private
+ if(DEFINED PKGCONFIG_REQUIRES)
+ list(REMOVE_DUPLICATES PKGCONFIG_REQUIRES)
+ string(REPLACE ";" " " PKGCONFIG_REQUIRES "${PKGCONFIG_REQUIRES}")
+ file(APPEND "${PKGCONFIG_FILE}" "Requires.private: ${PKGCONFIG_REQUIRES}\n")
+ endif()
+
+ # Prepare Cflags
+ if(DEFINED PKGCONFIG_CFLAGS)
+ string(REPLACE ";" " " PKGCONFIG_CFLAGS "${PKGCONFIG_CFLAGS}")
+ else()
+ set(PKGCONFIG_CFLAGS "")
+ endif()
+ file(APPEND "${PKGCONFIG_FILE}" "Cflags: -I\${includedir} ${PKGCONFIG_CFLAGS}\n")
+
+ # Install .pc file
+ install(FILES "${PKGCONFIG_FILE}" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
+endfunction()
diff --git a/cmake/SanitizeBool.cmake b/cmake/SanitizeBool.cmake
new file mode 100644
index 0000000..586c17d
--- /dev/null
+++ b/cmake/SanitizeBool.cmake
@@ -0,0 +1,20 @@
+function(SanitizeBool VAR)
+ string(TOLOWER "${${VAR}}" VALUE)
+ if(VALUE STREQUAL "on")
+ set(${VAR} "ON" PARENT_SCOPE)
+ elseif(VALUE STREQUAL "yes")
+ set(${VAR} "ON" PARENT_SCOPE)
+ elseif(VALUE STREQUAL "true")
+ set(${VAR} "ON" PARENT_SCOPE)
+ elseif(VALUE STREQUAL "1")
+ set(${VAR} "ON" PARENT_SCOPE)
+ elseif(VALUE STREQUAL "off")
+ set(${VAR} "OFF" PARENT_SCOPE)
+ elseif(VALUE STREQUAL "no")
+ set(${VAR} "OFF" PARENT_SCOPE)
+ elseif(VALUE STREQUAL "false")
+ set(${VAR} "OFF" PARENT_SCOPE)
+ elseif(VALUE STREQUAL "0")
+ set(${VAR} "OFF" PARENT_SCOPE)
+ endif()
+endfunction()
diff --git a/cmake/SelectGSSAPI.cmake b/cmake/SelectGSSAPI.cmake
new file mode 100644
index 0000000..5bde116
--- /dev/null
+++ b/cmake/SelectGSSAPI.cmake
@@ -0,0 +1,48 @@
+include(SanitizeBool)
+
+# We try to find any packages our backends might use
+find_package(GSSAPI)
+if(CMAKE_SYSTEM_NAME MATCHES "Darwin")
+ include(FindGSSFramework)
+endif()
+
+if(USE_GSSAPI)
+ # Auto-select GSS backend
+ sanitizebool(USE_GSSAPI)
+ if(USE_GSSAPI STREQUAL ON)
+ if(GSSFRAMEWORK_FOUND)
+ set(USE_GSSAPI "GSS.framework")
+ elseif(GSSAPI_FOUND)
+ set(USE_GSSAPI "gssapi")
+ else()
+ message(FATAL_ERROR "Unable to autodetect a usable GSS backend."
+ "Please pass the backend name explicitly (-DUSE_GSS=backend)")
+ endif()
+ endif()
+
+ # Check that we can find what's required for the selected backend
+ if(USE_GSSAPI STREQUAL "GSS.framework")
+ if(NOT GSSFRAMEWORK_FOUND)
+ message(FATAL_ERROR "Asked for GSS.framework backend, but it wasn't found")
+ endif()
+
+ list(APPEND LIBGIT2_SYSTEM_LIBS ${GSSFRAMEWORK_LIBRARIES})
+
+ set(GIT_GSSFRAMEWORK 1)
+ add_feature_info(GSSAPI GIT_GSSFRAMEWORK "GSSAPI support for SPNEGO authentication (${USE_GSSAPI})")
+ elseif(USE_GSSAPI STREQUAL "gssapi")
+ if(NOT GSSAPI_FOUND)
+ message(FATAL_ERROR "Asked for gssapi GSS backend, but it wasn't found")
+ endif()
+
+ list(APPEND LIBGIT2_SYSTEM_LIBS ${GSSAPI_LIBRARIES})
+
+ set(GIT_GSSAPI 1)
+ add_feature_info(GSSAPI GIT_GSSAPI "GSSAPI support for SPNEGO authentication (${USE_GSSAPI})")
+ else()
+ message(FATAL_ERROR "Asked for backend ${USE_GSSAPI} but it wasn't found")
+ endif()
+else()
+ set(GIT_GSSAPI 0)
+ add_feature_info(GSSAPI NO "GSSAPI support for SPNEGO authentication")
+endif()
diff --git a/cmake/SelectHTTPParser.cmake b/cmake/SelectHTTPParser.cmake
new file mode 100644
index 0000000..955aea3
--- /dev/null
+++ b/cmake/SelectHTTPParser.cmake
@@ -0,0 +1,19 @@
+# Optional external dependency: http-parser
+if(USE_HTTP_PARSER STREQUAL "system")
+ find_package(HTTPParser)
+
+ if(HTTP_PARSER_FOUND AND HTTP_PARSER_VERSION_MAJOR EQUAL 2)
+ list(APPEND LIBGIT2_SYSTEM_INCLUDES ${HTTP_PARSER_INCLUDE_DIRS})
+ list(APPEND LIBGIT2_SYSTEM_LIBS ${HTTP_PARSER_LIBRARIES})
+ list(APPEND LIBGIT2_PC_LIBS "-lhttp_parser")
+ add_feature_info(http-parser ON "http-parser support (system)")
+ else()
+ message(FATAL_ERROR "http-parser support was requested but not found")
+ endif()
+else()
+ message(STATUS "http-parser version 2 was not found or disabled; using bundled 3rd-party sources.")
+ add_subdirectory("${PROJECT_SOURCE_DIR}/deps/http-parser" "${PROJECT_BINARY_DIR}/deps/http-parser")
+ list(APPEND LIBGIT2_DEPENDENCY_INCLUDES "${PROJECT_SOURCE_DIR}/deps/http-parser")
+ list(APPEND LIBGIT2_DEPENDENCY_OBJECTS "$<TARGET_OBJECTS:http-parser>")
+ add_feature_info(http-parser ON "http-parser support (bundled)")
+endif()
diff --git a/cmake/SelectHTTPSBackend.cmake b/cmake/SelectHTTPSBackend.cmake
new file mode 100644
index 0000000..d149416
--- /dev/null
+++ b/cmake/SelectHTTPSBackend.cmake
@@ -0,0 +1,143 @@
+include(SanitizeBool)
+
+# We try to find any packages our backends might use
+find_package(OpenSSL)
+find_package(mbedTLS)
+if(CMAKE_SYSTEM_NAME MATCHES "Darwin")
+ find_package(Security)
+ find_package(CoreFoundation)
+endif()
+
+if(USE_HTTPS)
+ # Auto-select TLS backend
+ sanitizebool(USE_HTTPS)
+ if(USE_HTTPS STREQUAL ON)
+ if(SECURITY_FOUND)
+ if(SECURITY_HAS_SSLCREATECONTEXT)
+ set(USE_HTTPS "SecureTransport")
+ else()
+ message(STATUS "Security framework is too old, falling back to OpenSSL")
+ set(USE_HTTPS "OpenSSL")
+ endif()
+ elseif(WIN32)
+ set(USE_HTTPS "WinHTTP")
+ elseif(OPENSSL_FOUND)
+ set(USE_HTTPS "OpenSSL")
+ elseif(MBEDTLS_FOUND)
+ set(USE_HTTPS "mbedTLS")
+ else()
+ message(FATAL_ERROR "Unable to autodetect a usable HTTPS backend."
+ "Please pass the backend name explicitly (-DUSE_HTTPS=backend)")
+ endif()
+ endif()
+
+ # Check that we can find what's required for the selected backend
+ if(USE_HTTPS STREQUAL "SecureTransport")
+ if(NOT COREFOUNDATION_FOUND)
+ message(FATAL_ERROR "Cannot use SecureTransport backend, CoreFoundation.framework not found")
+ endif()
+ if(NOT SECURITY_FOUND)
+ message(FATAL_ERROR "Cannot use SecureTransport backend, Security.framework not found")
+ endif()
+ if(NOT SECURITY_HAS_SSLCREATECONTEXT)
+ message(FATAL_ERROR "Cannot use SecureTransport backend, SSLCreateContext not supported")
+ endif()
+
+ set(GIT_SECURE_TRANSPORT 1)
+ list(APPEND LIBGIT2_SYSTEM_INCLUDES ${SECURITY_INCLUDE_DIR})
+ list(APPEND LIBGIT2_SYSTEM_LIBS ${COREFOUNDATION_LDFLAGS} ${SECURITY_LDFLAGS})
+ list(APPEND LIBGIT2_PC_LIBS ${COREFOUNDATION_LDFLAGS} ${SECURITY_LDFLAGS})
+ elseif(USE_HTTPS STREQUAL "OpenSSL")
+ if(NOT OPENSSL_FOUND)
+ message(FATAL_ERROR "Asked for OpenSSL TLS backend, but it wasn't found")
+ endif()
+
+ set(GIT_OPENSSL 1)
+ list(APPEND LIBGIT2_SYSTEM_INCLUDES ${OPENSSL_INCLUDE_DIR})
+ list(APPEND LIBGIT2_SYSTEM_LIBS ${OPENSSL_LIBRARIES})
+ list(APPEND LIBGIT2_PC_LIBS ${OPENSSL_LDFLAGS})
+ list(APPEND LIBGIT2_PC_REQUIRES "openssl")
+ elseif(USE_HTTPS STREQUAL "mbedTLS")
+ if(NOT MBEDTLS_FOUND)
+ message(FATAL_ERROR "Asked for mbedTLS backend, but it wasn't found")
+ endif()
+
+ if(NOT CERT_LOCATION)
+ message(STATUS "Auto-detecting default certificates location")
+ if(EXISTS "/usr/local/opt/openssl/bin/openssl")
+ # Check for an Homebrew installation
+ set(OPENSSL_CMD "/usr/local/opt/openssl/bin/openssl")
+ else()
+ set(OPENSSL_CMD "openssl")
+ endif()
+ execute_process(COMMAND ${OPENSSL_CMD} version -d OUTPUT_VARIABLE OPENSSL_DIR OUTPUT_STRIP_TRAILING_WHITESPACE)
+ if(OPENSSL_DIR)
+ string(REGEX REPLACE "^OPENSSLDIR: \"(.*)\"$" "\\1/" OPENSSL_DIR ${OPENSSL_DIR})
+
+ set(OPENSSL_CA_LOCATIONS
+ "ca-bundle.pem" # OpenSUSE Leap 42.1
+ "cert.pem" # Ubuntu 14.04, FreeBSD
+ "certs/ca-certificates.crt" # Ubuntu 16.04
+ "certs/ca.pem" # Debian 7
+ )
+ foreach(SUFFIX IN LISTS OPENSSL_CA_LOCATIONS)
+ set(LOC "${OPENSSL_DIR}${SUFFIX}")
+ if(NOT CERT_LOCATION AND EXISTS "${OPENSSL_DIR}${SUFFIX}")
+ set(CERT_LOCATION ${LOC})
+ endif()
+ endforeach()
+ else()
+ message(FATAL_ERROR "Unable to find OpenSSL executable. Please provide default certificate location via CERT_LOCATION")
+ endif()
+ endif()
+
+ if(CERT_LOCATION)
+ if(NOT EXISTS ${CERT_LOCATION})
+ message(FATAL_ERROR "Cannot use CERT_LOCATION=${CERT_LOCATION} as it doesn't exist")
+ endif()
+ add_feature_info(CERT_LOCATION ON "using certificates from ${CERT_LOCATION}")
+ add_definitions(-DGIT_DEFAULT_CERT_LOCATION="${CERT_LOCATION}")
+ endif()
+
+ set(GIT_MBEDTLS 1)
+ list(APPEND LIBGIT2_SYSTEM_INCLUDES ${MBEDTLS_INCLUDE_DIR})
+ list(APPEND LIBGIT2_SYSTEM_LIBS ${MBEDTLS_LIBRARIES})
+ # mbedTLS has no pkgconfig file, hence we can't require it
+ # https://github.com/ARMmbed/mbedtls/issues/228
+ # For now, pass its link flags as our own
+ list(APPEND LIBGIT2_PC_LIBS ${MBEDTLS_LIBRARIES})
+ elseif(USE_HTTPS STREQUAL "Schannel")
+ set(GIT_SCHANNEL 1)
+
+ list(APPEND LIBGIT2_SYSTEM_LIBS "rpcrt4" "crypt32" "ole32" "secur32")
+ list(APPEND LIBGIT2_PC_LIBS "-lrpcrt4" "-lcrypt32" "-lole32" "-lsecur32")
+ elseif(USE_HTTPS STREQUAL "WinHTTP")
+ set(GIT_WINHTTP 1)
+
+ # Since MinGW does not come with headers or an import library for winhttp,
+ # we have to include a private header and generate our own import library
+ if(MINGW)
+ add_subdirectory("${PROJECT_SOURCE_DIR}/deps/winhttp" "${PROJECT_BINARY_DIR}/deps/winhttp")
+ list(APPEND LIBGIT2_SYSTEM_LIBS winhttp)
+ list(APPEND LIBGIT2_DEPENDENCY_INCLUDES "${PROJECT_SOURCE_DIR}/deps/winhttp")
+ else()
+ list(APPEND LIBGIT2_SYSTEM_LIBS "winhttp")
+ list(APPEND LIBGIT2_PC_LIBS "-lwinhttp")
+ endif()
+
+ list(APPEND LIBGIT2_SYSTEM_LIBS "rpcrt4" "crypt32" "ole32" "secur32")
+ list(APPEND LIBGIT2_PC_LIBS "-lrpcrt4" "-lcrypt32" "-lole32" "-lsecur32")
+ elseif(USE_HTTPS STREQUAL "OpenSSL-Dynamic")
+ set(GIT_OPENSSL 1)
+ set(GIT_OPENSSL_DYNAMIC 1)
+ list(APPEND LIBGIT2_SYSTEM_LIBS dl)
+ else()
+ message(FATAL_ERROR "Asked for backend ${USE_HTTPS} but it wasn't found")
+ endif()
+
+ set(GIT_HTTPS 1)
+ add_feature_info(HTTPS GIT_HTTPS "using ${USE_HTTPS}")
+else()
+ set(GIT_HTTPS 0)
+ add_feature_info(HTTPS NO "")
+endif()
diff --git a/cmake/SelectHashes.cmake b/cmake/SelectHashes.cmake
new file mode 100644
index 0000000..5c007e5
--- /dev/null
+++ b/cmake/SelectHashes.cmake
@@ -0,0 +1,104 @@
+# Select a hash backend
+
+include(SanitizeBool)
+
+# USE_SHA1=CollisionDetection(ON)/HTTPS/Generic/OFF
+sanitizebool(USE_SHA1)
+sanitizebool(USE_SHA256)
+
+# sha1
+
+if(USE_SHA1 STREQUAL ON)
+ SET(USE_SHA1 "CollisionDetection")
+elseif(USE_SHA1 STREQUAL "HTTPS")
+ if(USE_HTTPS STREQUAL "SecureTransport")
+ set(USE_SHA1 "CommonCrypto")
+ elseif(USE_HTTPS STREQUAL "Schannel")
+ set(USE_SHA1 "Win32")
+ elseif(USE_HTTPS STREQUAL "WinHTTP")
+ set(USE_SHA1 "Win32")
+ elseif(USE_HTTPS)
+ set(USE_SHA1 ${USE_HTTPS})
+ else()
+ set(USE_SHA1 "CollisionDetection")
+ endif()
+endif()
+
+if(USE_SHA1 STREQUAL "CollisionDetection")
+ set(GIT_SHA1_COLLISIONDETECT 1)
+elseif(USE_SHA1 STREQUAL "OpenSSL")
+ set(GIT_SHA1_OPENSSL 1)
+elseif(USE_SHA1 STREQUAL "OpenSSL-Dynamic")
+ set(GIT_SHA1_OPENSSL 1)
+ set(GIT_SHA1_OPENSSL_DYNAMIC 1)
+ list(APPEND LIBGIT2_SYSTEM_LIBS dl)
+elseif(USE_SHA1 STREQUAL "CommonCrypto")
+ set(GIT_SHA1_COMMON_CRYPTO 1)
+elseif(USE_SHA1 STREQUAL "mbedTLS")
+ set(GIT_SHA1_MBEDTLS 1)
+elseif(USE_SHA1 STREQUAL "Win32")
+ set(GIT_SHA1_WIN32 1)
+else()
+ message(FATAL_ERROR "Asked for unknown SHA1 backend: ${USE_SHA1}")
+endif()
+
+# sha256
+
+if(USE_SHA256 STREQUAL ON AND USE_HTTPS)
+ SET(USE_SHA256 "HTTPS")
+elseif(USE_SHA256 STREQUAL ON)
+ SET(USE_SHA256 "Builtin")
+endif()
+
+if(USE_SHA256 STREQUAL "HTTPS")
+ if(USE_HTTPS STREQUAL "SecureTransport")
+ set(USE_SHA256 "CommonCrypto")
+ elseif(USE_HTTPS STREQUAL "Schannel")
+ set(USE_SHA256 "Win32")
+ elseif(USE_HTTPS STREQUAL "WinHTTP")
+ set(USE_SHA256 "Win32")
+ elseif(USE_HTTPS)
+ set(USE_SHA256 ${USE_HTTPS})
+ endif()
+endif()
+
+if(USE_SHA256 STREQUAL "Builtin")
+ set(GIT_SHA256_BUILTIN 1)
+elseif(USE_SHA256 STREQUAL "OpenSSL")
+ set(GIT_SHA256_OPENSSL 1)
+elseif(USE_SHA256 STREQUAL "OpenSSL-Dynamic")
+ set(GIT_SHA256_OPENSSL 1)
+ set(GIT_SHA256_OPENSSL_DYNAMIC 1)
+ list(APPEND LIBGIT2_SYSTEM_LIBS dl)
+elseif(USE_SHA256 STREQUAL "CommonCrypto")
+ set(GIT_SHA256_COMMON_CRYPTO 1)
+elseif(USE_SHA256 STREQUAL "mbedTLS")
+ set(GIT_SHA256_MBEDTLS 1)
+elseif(USE_SHA256 STREQUAL "Win32")
+ set(GIT_SHA256_WIN32 1)
+else()
+ message(FATAL_ERROR "Asked for unknown SHA256 backend: ${USE_SHA256}")
+endif()
+
+# add library requirements
+if(USE_SHA1 STREQUAL "OpenSSL" OR USE_SHA256 STREQUAL "OpenSSL")
+ if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
+ list(APPEND LIBGIT2_PC_LIBS "-lssl")
+ else()
+ list(APPEND LIBGIT2_PC_REQUIRES "openssl")
+ endif()
+endif()
+
+if(USE_SHA1 STREQUAL "mbedTLS" OR USE_SHA256 STREQUAL "mbedTLS")
+ list(APPEND LIBGIT2_SYSTEM_INCLUDES ${MBEDTLS_INCLUDE_DIR})
+ list(APPEND LIBGIT2_SYSTEM_LIBS ${MBEDTLS_LIBRARIES})
+ # mbedTLS has no pkgconfig file, hence we can't require it
+ # https://github.com/ARMmbed/mbedtls/issues/228
+ # For now, pass its link flags as our own
+ list(APPEND LIBGIT2_PC_LIBS ${MBEDTLS_LIBRARIES})
+endif()
+
+# notify feature enablement
+
+add_feature_info(SHA1 ON "using ${USE_SHA1}")
+add_feature_info(SHA256 ON "using ${USE_SHA256}")
diff --git a/cmake/SelectRegex.cmake b/cmake/SelectRegex.cmake
new file mode 100644
index 0000000..2a3a91b
--- /dev/null
+++ b/cmake/SelectRegex.cmake
@@ -0,0 +1,51 @@
+# Specify regular expression implementation
+find_package(PCRE)
+
+if(REGEX_BACKEND STREQUAL "")
+ check_symbol_exists(regcomp_l "regex.h;xlocale.h" HAVE_REGCOMP_L)
+
+ if(HAVE_REGCOMP_L)
+ set(REGEX_BACKEND "regcomp_l")
+ elseif(PCRE_FOUND)
+ set(REGEX_BACKEND "pcre")
+ else()
+ set(REGEX_BACKEND "builtin")
+ endif()
+endif()
+
+if(REGEX_BACKEND STREQUAL "regcomp_l")
+ add_feature_info(regex ON "using system regcomp_l")
+ set(GIT_REGEX_REGCOMP_L 1)
+elseif(REGEX_BACKEND STREQUAL "pcre2")
+ find_package(PCRE2)
+
+ if(NOT PCRE2_FOUND)
+ MESSAGE(FATAL_ERROR "PCRE2 support was requested but not found")
+ endif()
+
+ add_feature_info(regex ON "using system PCRE2")
+ set(GIT_REGEX_PCRE2 1)
+
+ list(APPEND LIBGIT2_SYSTEM_INCLUDES ${PCRE2_INCLUDE_DIRS})
+ list(APPEND LIBGIT2_SYSTEM_LIBS ${PCRE2_LIBRARIES})
+ list(APPEND LIBGIT2_PC_REQUIRES "libpcre2-8")
+elseif(REGEX_BACKEND STREQUAL "pcre")
+ add_feature_info(regex ON "using system PCRE")
+ set(GIT_REGEX_PCRE 1)
+
+ list(APPEND LIBGIT2_SYSTEM_INCLUDES ${PCRE_INCLUDE_DIRS})
+ list(APPEND LIBGIT2_SYSTEM_LIBS ${PCRE_LIBRARIES})
+ list(APPEND LIBGIT2_PC_REQUIRES "libpcre")
+elseif(REGEX_BACKEND STREQUAL "regcomp")
+ add_feature_info(regex ON "using system regcomp")
+ set(GIT_REGEX_REGCOMP 1)
+elseif(REGEX_BACKEND STREQUAL "builtin")
+ add_feature_info(regex ON "using bundled PCRE")
+ set(GIT_REGEX_BUILTIN 1)
+
+ add_subdirectory("${PROJECT_SOURCE_DIR}/deps/pcre" "${PROJECT_BINARY_DIR}/deps/pcre")
+ list(APPEND LIBGIT2_DEPENDENCY_INCLUDES "${PROJECT_SOURCE_DIR}/deps/pcre")
+ list(APPEND LIBGIT2_DEPENDENCY_OBJECTS $<TARGET_OBJECTS:pcre>)
+else()
+ message(FATAL_ERROR "The REGEX_BACKEND option provided is not supported")
+endif()
diff --git a/cmake/SelectSSH.cmake b/cmake/SelectSSH.cmake
new file mode 100644
index 0000000..23dfc97
--- /dev/null
+++ b/cmake/SelectSSH.cmake
@@ -0,0 +1,41 @@
+# Optional external dependency: libssh2
+if(USE_SSH)
+ find_pkglibraries(LIBSSH2 libssh2)
+ if(NOT LIBSSH2_FOUND)
+ find_package(LibSSH2)
+ set(LIBSSH2_INCLUDE_DIRS ${LIBSSH2_INCLUDE_DIR})
+ get_filename_component(LIBSSH2_LIBRARY_DIRS "${LIBSSH2_LIBRARY}" DIRECTORY)
+ set(LIBSSH2_LIBRARIES ${LIBSSH2_LIBRARY})
+ set(LIBSSH2_LDFLAGS "-lssh2")
+ endif()
+
+ if(NOT LIBSSH2_FOUND)
+ message(FATAL_ERROR "LIBSSH2 not found. Set CMAKE_PREFIX_PATH if it is installed outside of the default search path.")
+ endif()
+endif()
+
+if(LIBSSH2_FOUND)
+ set(GIT_SSH 1)
+ list(APPEND LIBGIT2_SYSTEM_INCLUDES ${LIBSSH2_INCLUDE_DIRS})
+ list(APPEND LIBGIT2_SYSTEM_LIBS ${LIBSSH2_LIBRARIES})
+ list(APPEND LIBGIT2_PC_LIBS ${LIBSSH2_LDFLAGS})
+
+ check_library_exists("${LIBSSH2_LIBRARIES}" libssh2_userauth_publickey_frommemory "${LIBSSH2_LIBRARY_DIRS}" HAVE_LIBSSH2_MEMORY_CREDENTIALS)
+ if(HAVE_LIBSSH2_MEMORY_CREDENTIALS)
+ set(GIT_SSH_MEMORY_CREDENTIALS 1)
+ endif()
+else()
+ message(STATUS "LIBSSH2 not found. Set CMAKE_PREFIX_PATH if it is installed outside of the default search path.")
+endif()
+
+if(WIN32 AND EMBED_SSH_PATH)
+ file(GLOB SSH_SRC "${EMBED_SSH_PATH}/src/*.c")
+ list(SORT SSH_SRC)
+ list(APPEND LIBGIT2_DEPENDENCY_OBJECTS ${SSH_SRC})
+
+ list(APPEND LIBGIT2_DEPENDENCY_INCLUDES "${EMBED_SSH_PATH}/include")
+ file(WRITE "${EMBED_SSH_PATH}/src/libssh2_config.h" "#define HAVE_WINCNG\n#define LIBSSH2_WINCNG\n#include \"../win32/libssh2_config.h\"")
+ set(GIT_SSH 1)
+endif()
+
+add_feature_info(SSH GIT_SSH "SSH transport support")
diff --git a/cmake/SelectXdiff.cmake b/cmake/SelectXdiff.cmake
new file mode 100644
index 0000000..9ab9f3f
--- /dev/null
+++ b/cmake/SelectXdiff.cmake
@@ -0,0 +1,9 @@
+# Optional external dependency: xdiff
+if(USE_XDIFF STREQUAL "system")
+ message(FATAL_ERROR "external/system xdiff is not yet supported")
+else()
+ add_subdirectory("${PROJECT_SOURCE_DIR}/deps/xdiff" "${PROJECT_BINARY_DIR}/deps/xdiff")
+ list(APPEND LIBGIT2_DEPENDENCY_INCLUDES "${PROJECT_SOURCE_DIR}/deps/xdiff")
+ list(APPEND LIBGIT2_DEPENDENCY_OBJECTS "$<TARGET_OBJECTS:xdiff>")
+ add_feature_info(xdiff ON "xdiff support (bundled)")
+endif()
diff --git a/cmake/SelectZlib.cmake b/cmake/SelectZlib.cmake
new file mode 100644
index 0000000..fb4361a
--- /dev/null
+++ b/cmake/SelectZlib.cmake
@@ -0,0 +1,34 @@
+# Optional external dependency: zlib
+include(SanitizeBool)
+
+SanitizeBool(USE_BUNDLED_ZLIB)
+if(USE_BUNDLED_ZLIB STREQUAL ON)
+ set(USE_BUNDLED_ZLIB "Bundled")
+endif()
+
+if(USE_BUNDLED_ZLIB STREQUAL "OFF")
+ find_package(ZLIB)
+ if(ZLIB_FOUND)
+ list(APPEND LIBGIT2_SYSTEM_INCLUDES ${ZLIB_INCLUDE_DIRS})
+ list(APPEND LIBGIT2_SYSTEM_LIBS ${ZLIB_LIBRARIES})
+ if(APPLE OR CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
+ list(APPEND LIBGIT2_PC_LIBS "-lz")
+ else()
+ list(APPEND LIBGIT2_PC_REQUIRES "zlib")
+ endif()
+ add_feature_info(zlib ON "using system zlib")
+ else()
+ message(STATUS "zlib was not found; using bundled 3rd-party sources." )
+ endif()
+endif()
+if(USE_BUNDLED_ZLIB STREQUAL "Chromium")
+ add_subdirectory("${PROJECT_SOURCE_DIR}/deps/chromium-zlib" "${PROJECT_BINARY_DIR}/deps/chromium-zlib")
+ list(APPEND LIBGIT2_DEPENDENCY_INCLUDES "${PROJECT_SOURCE_DIR}/deps/chromium-zlib")
+ list(APPEND LIBGIT2_DEPENDENCY_OBJECTS $<TARGET_OBJECTS:chromium_zlib>)
+ add_feature_info(zlib ON "using (Chromium) bundled zlib")
+elseif(USE_BUNDLED_ZLIB OR NOT ZLIB_FOUND)
+ add_subdirectory("${PROJECT_SOURCE_DIR}/deps/zlib" "${PROJECT_BINARY_DIR}/deps/zlib")
+ list(APPEND LIBGIT2_DEPENDENCY_INCLUDES "${PROJECT_SOURCE_DIR}/deps/zlib")
+ list(APPEND LIBGIT2_DEPENDENCY_OBJECTS $<TARGET_OBJECTS:zlib>)
+ add_feature_info(zlib ON "using bundled zlib")
+endif()
diff --git a/deps/xdiff/CMakeLists.txt b/deps/xdiff/CMakeLists.txt
new file mode 100644
index 0000000..743ac63
--- /dev/null
+++ b/deps/xdiff/CMakeLists.txt
@@ -0,0 +1,28 @@
+
+file(GLOB SRC_XDIFF "*.c" "*.h")
+list(SORT SRC_XDIFF)
+
+add_library(xdiff OBJECT ${SRC_XDIFF})
+target_include_directories(xdiff SYSTEM PRIVATE
+ "${PROJECT_SOURCE_DIR}/include"
+ "${PROJECT_SOURCE_DIR}/src/util"
+ "${PROJECT_BINARY_DIR}/src/util"
+ ${LIBGIT2_SYSTEM_INCLUDES}
+ ${LIBGIT2_DEPENDENCY_INCLUDES})
+
+# the xdiff dependency is not (yet) warning-free, disable warnings
+# as errors for the xdiff sources until we've sorted them out
+if(MSVC)
+ set_source_files_properties(xdiffi.c PROPERTIES COMPILE_FLAGS -WX-)
+ set_source_files_properties(xemit.c PROPERTIES COMPILE_FLAGS -WX-)
+ set_source_files_properties(xhistogram.c PROPERTIES COMPILE_FLAGS -WX-)
+ set_source_files_properties(xmerge.c PROPERTIES COMPILE_FLAGS -WX-)
+ set_source_files_properties(xutils.c PROPERTIES COMPILE_FLAGS -WX-)
+ set_source_files_properties(xpatience.c PROPERTIES COMPILE_FLAGS -WX-)
+else()
+ set_source_files_properties(xdiffi.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare -Wno-unused-parameter")
+ set_source_files_properties(xemit.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare -Wno-unused-parameter")
+ set_source_files_properties(xhistogram.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare")
+ set_source_files_properties(xutils.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare")
+ set_source_files_properties(xpatience.c PROPERTIES COMPILE_FLAGS "-Wno-sign-compare")
+endif()
diff --git a/deps/xdiff/git-xdiff.h b/deps/xdiff/git-xdiff.h
new file mode 100644
index 0000000..1450ab3
--- /dev/null
+++ b/deps/xdiff/git-xdiff.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+/*
+ * This file provides the necessary indirection between xdiff and
+ * libgit2. libgit2-specific functionality should live here, so
+ * that git and libgit2 can share a common xdiff implementation.
+ */
+
+#ifndef INCLUDE_git_xdiff_h__
+#define INCLUDE_git_xdiff_h__
+
+#include "regexp.h"
+
+/* Work around C90-conformance issues */
+#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L)
+# if defined(_MSC_VER)
+# define inline __inline
+# elif defined(__GNUC__)
+# define inline __inline__
+# else
+# define inline
+# endif
+#endif
+
+#define XDL_UNUSED GIT_UNUSED_ARG
+
+#define xdl_malloc(x) git__malloc(x)
+#define xdl_calloc(n, sz) git__calloc(n, sz)
+#define xdl_free(ptr) git__free(ptr)
+#define xdl_realloc(ptr, x) git__realloc(ptr, x)
+
+#define XDL_BUG(msg) GIT_ASSERT(!msg)
+
+#define xdl_regex_t git_regexp
+#define xdl_regmatch_t git_regmatch
+
+GIT_INLINE(int) xdl_regexec_buf(
+ const xdl_regex_t *preg, const char *buf, size_t size,
+ size_t nmatch, xdl_regmatch_t pmatch[], int eflags)
+{
+ GIT_UNUSED(preg);
+ GIT_UNUSED(buf);
+ GIT_UNUSED(size);
+ GIT_UNUSED(nmatch);
+ GIT_UNUSED(pmatch);
+ GIT_UNUSED(eflags);
+ GIT_ASSERT("not implemented");
+ return -1;
+}
+
+#endif
diff --git a/deps/xdiff/xdiff.h b/deps/xdiff/xdiff.h
new file mode 100644
index 0000000..fb47f63
--- /dev/null
+++ b/deps/xdiff/xdiff.h
@@ -0,0 +1,150 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#if !defined(XDIFF_H)
+#define XDIFF_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* #ifdef __cplusplus */
+
+#include "git-xdiff.h"
+
+/* xpparm_t.flags */
+#define XDF_NEED_MINIMAL (1 << 0)
+
+#define XDF_IGNORE_WHITESPACE (1 << 1)
+#define XDF_IGNORE_WHITESPACE_CHANGE (1 << 2)
+#define XDF_IGNORE_WHITESPACE_AT_EOL (1 << 3)
+#define XDF_IGNORE_CR_AT_EOL (1 << 4)
+#define XDF_WHITESPACE_FLAGS (XDF_IGNORE_WHITESPACE | \
+ XDF_IGNORE_WHITESPACE_CHANGE | \
+ XDF_IGNORE_WHITESPACE_AT_EOL | \
+ XDF_IGNORE_CR_AT_EOL)
+
+#define XDF_IGNORE_BLANK_LINES (1 << 7)
+
+#define XDF_PATIENCE_DIFF (1 << 14)
+#define XDF_HISTOGRAM_DIFF (1 << 15)
+#define XDF_DIFF_ALGORITHM_MASK (XDF_PATIENCE_DIFF | XDF_HISTOGRAM_DIFF)
+#define XDF_DIFF_ALG(x) ((x) & XDF_DIFF_ALGORITHM_MASK)
+
+#define XDF_INDENT_HEURISTIC (1 << 23)
+
+/* xdemitconf_t.flags */
+#define XDL_EMIT_FUNCNAMES (1 << 0)
+#define XDL_EMIT_NO_HUNK_HDR (1 << 1)
+#define XDL_EMIT_FUNCCONTEXT (1 << 2)
+
+/* merge simplification levels */
+#define XDL_MERGE_MINIMAL 0
+#define XDL_MERGE_EAGER 1
+#define XDL_MERGE_ZEALOUS 2
+#define XDL_MERGE_ZEALOUS_ALNUM 3
+
+/* merge favor modes */
+#define XDL_MERGE_FAVOR_OURS 1
+#define XDL_MERGE_FAVOR_THEIRS 2
+#define XDL_MERGE_FAVOR_UNION 3
+
+/* merge output styles */
+#define XDL_MERGE_DIFF3 1
+#define XDL_MERGE_ZEALOUS_DIFF3 2
+
+typedef struct s_mmfile {
+ char *ptr;
+ long size;
+} mmfile_t;
+
+typedef struct s_mmbuffer {
+ char *ptr;
+ long size;
+} mmbuffer_t;
+
+typedef struct s_xpparam {
+ unsigned long flags;
+
+ /* -I<regex> */
+ xdl_regex_t **ignore_regex;
+ size_t ignore_regex_nr;
+
+ /* See Documentation/diff-options.txt. */
+ char **anchors;
+ size_t anchors_nr;
+} xpparam_t;
+
+typedef struct s_xdemitcb {
+ void *priv;
+ int (*out_hunk)(void *,
+ long old_begin, long old_nr,
+ long new_begin, long new_nr,
+ const char *func, long funclen);
+ int (*out_line)(void *, mmbuffer_t *, int);
+} xdemitcb_t;
+
+typedef long (*find_func_t)(const char *line, long line_len, char *buffer, long buffer_size, void *priv);
+
+typedef int (*xdl_emit_hunk_consume_func_t)(long start_a, long count_a,
+ long start_b, long count_b,
+ void *cb_data);
+
+typedef struct s_xdemitconf {
+ long ctxlen;
+ long interhunkctxlen;
+ unsigned long flags;
+ find_func_t find_func;
+ void *find_func_priv;
+ xdl_emit_hunk_consume_func_t hunk_func;
+} xdemitconf_t;
+
+typedef struct s_bdiffparam {
+ long bsize;
+} bdiffparam_t;
+
+
+void *xdl_mmfile_first(mmfile_t *mmf, long *size);
+long xdl_mmfile_size(mmfile_t *mmf);
+
+int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+ xdemitconf_t const *xecfg, xdemitcb_t *ecb);
+
+typedef struct s_xmparam {
+ xpparam_t xpp;
+ int marker_size;
+ int level;
+ int favor;
+ int style;
+ const char *ancestor; /* label for orig */
+ const char *file1; /* label for mf1 */
+ const char *file2; /* label for mf2 */
+} xmparam_t;
+
+#define DEFAULT_CONFLICT_MARKER_SIZE 7
+
+int xdl_merge(mmfile_t *orig, mmfile_t *mf1, mmfile_t *mf2,
+ xmparam_t const *xmp, mmbuffer_t *result);
+
+#ifdef __cplusplus
+}
+#endif /* #ifdef __cplusplus */
+
+#endif /* #if !defined(XDIFF_H) */
diff --git a/deps/xdiff/xdiffi.c b/deps/xdiff/xdiffi.c
new file mode 100644
index 0000000..ea36143
--- /dev/null
+++ b/deps/xdiff/xdiffi.c
@@ -0,0 +1,1089 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#include "xinclude.h"
+
+#define XDL_MAX_COST_MIN 256
+#define XDL_HEUR_MIN_COST 256
+#define XDL_LINE_MAX (long)((1UL << (CHAR_BIT * sizeof(long) - 1)) - 1)
+#define XDL_SNAKE_CNT 20
+#define XDL_K_HEUR 4
+
+typedef struct s_xdpsplit {
+ long i1, i2;
+ int min_lo, min_hi;
+} xdpsplit_t;
+
+/*
+ * See "An O(ND) Difference Algorithm and its Variations", by Eugene Myers.
+ * Basically considers a "box" (off1, off2, lim1, lim2) and scan from both
+ * the forward diagonal starting from (off1, off2) and the backward diagonal
+ * starting from (lim1, lim2). If the K values on the same diagonal crosses
+ * returns the furthest point of reach. We might encounter expensive edge cases
+ * using this algorithm, so a little bit of heuristic is needed to cut the
+ * search and to return a suboptimal point.
+ */
+static long xdl_split(unsigned long const *ha1, long off1, long lim1,
+ unsigned long const *ha2, long off2, long lim2,
+ long *kvdf, long *kvdb, int need_min, xdpsplit_t *spl,
+ xdalgoenv_t *xenv) {
+ long dmin = off1 - lim2, dmax = lim1 - off2;
+ long fmid = off1 - off2, bmid = lim1 - lim2;
+ long odd = (fmid - bmid) & 1;
+ long fmin = fmid, fmax = fmid;
+ long bmin = bmid, bmax = bmid;
+ long ec, d, i1, i2, prev1, best, dd, v, k;
+
+ /*
+ * Set initial diagonal values for both forward and backward path.
+ */
+ kvdf[fmid] = off1;
+ kvdb[bmid] = lim1;
+
+ for (ec = 1;; ec++) {
+ int got_snake = 0;
+
+ /*
+ * We need to extend the diagonal "domain" by one. If the next
+ * values exits the box boundaries we need to change it in the
+ * opposite direction because (max - min) must be a power of
+ * two.
+ *
+ * Also we initialize the external K value to -1 so that we can
+ * avoid extra conditions in the check inside the core loop.
+ */
+ if (fmin > dmin)
+ kvdf[--fmin - 1] = -1;
+ else
+ ++fmin;
+ if (fmax < dmax)
+ kvdf[++fmax + 1] = -1;
+ else
+ --fmax;
+
+ for (d = fmax; d >= fmin; d -= 2) {
+ if (kvdf[d - 1] >= kvdf[d + 1])
+ i1 = kvdf[d - 1] + 1;
+ else
+ i1 = kvdf[d + 1];
+ prev1 = i1;
+ i2 = i1 - d;
+ for (; i1 < lim1 && i2 < lim2 && ha1[i1] == ha2[i2]; i1++, i2++);
+ if (i1 - prev1 > xenv->snake_cnt)
+ got_snake = 1;
+ kvdf[d] = i1;
+ if (odd && bmin <= d && d <= bmax && kvdb[d] <= i1) {
+ spl->i1 = i1;
+ spl->i2 = i2;
+ spl->min_lo = spl->min_hi = 1;
+ return ec;
+ }
+ }
+
+ /*
+ * We need to extend the diagonal "domain" by one. If the next
+ * values exits the box boundaries we need to change it in the
+ * opposite direction because (max - min) must be a power of
+ * two.
+ *
+ * Also we initialize the external K value to -1 so that we can
+ * avoid extra conditions in the check inside the core loop.
+ */
+ if (bmin > dmin)
+ kvdb[--bmin - 1] = XDL_LINE_MAX;
+ else
+ ++bmin;
+ if (bmax < dmax)
+ kvdb[++bmax + 1] = XDL_LINE_MAX;
+ else
+ --bmax;
+
+ for (d = bmax; d >= bmin; d -= 2) {
+ if (kvdb[d - 1] < kvdb[d + 1])
+ i1 = kvdb[d - 1];
+ else
+ i1 = kvdb[d + 1] - 1;
+ prev1 = i1;
+ i2 = i1 - d;
+ for (; i1 > off1 && i2 > off2 && ha1[i1 - 1] == ha2[i2 - 1]; i1--, i2--);
+ if (prev1 - i1 > xenv->snake_cnt)
+ got_snake = 1;
+ kvdb[d] = i1;
+ if (!odd && fmin <= d && d <= fmax && i1 <= kvdf[d]) {
+ spl->i1 = i1;
+ spl->i2 = i2;
+ spl->min_lo = spl->min_hi = 1;
+ return ec;
+ }
+ }
+
+ if (need_min)
+ continue;
+
+ /*
+ * If the edit cost is above the heuristic trigger and if
+ * we got a good snake, we sample current diagonals to see
+ * if some of them have reached an "interesting" path. Our
+ * measure is a function of the distance from the diagonal
+ * corner (i1 + i2) penalized with the distance from the
+ * mid diagonal itself. If this value is above the current
+ * edit cost times a magic factor (XDL_K_HEUR) we consider
+ * it interesting.
+ */
+ if (got_snake && ec > xenv->heur_min) {
+ for (best = 0, d = fmax; d >= fmin; d -= 2) {
+ dd = d > fmid ? d - fmid: fmid - d;
+ i1 = kvdf[d];
+ i2 = i1 - d;
+ v = (i1 - off1) + (i2 - off2) - dd;
+
+ if (v > XDL_K_HEUR * ec && v > best &&
+ off1 + xenv->snake_cnt <= i1 && i1 < lim1 &&
+ off2 + xenv->snake_cnt <= i2 && i2 < lim2) {
+ for (k = 1; ha1[i1 - k] == ha2[i2 - k]; k++)
+ if (k == xenv->snake_cnt) {
+ best = v;
+ spl->i1 = i1;
+ spl->i2 = i2;
+ break;
+ }
+ }
+ }
+ if (best > 0) {
+ spl->min_lo = 1;
+ spl->min_hi = 0;
+ return ec;
+ }
+
+ for (best = 0, d = bmax; d >= bmin; d -= 2) {
+ dd = d > bmid ? d - bmid: bmid - d;
+ i1 = kvdb[d];
+ i2 = i1 - d;
+ v = (lim1 - i1) + (lim2 - i2) - dd;
+
+ if (v > XDL_K_HEUR * ec && v > best &&
+ off1 < i1 && i1 <= lim1 - xenv->snake_cnt &&
+ off2 < i2 && i2 <= lim2 - xenv->snake_cnt) {
+ for (k = 0; ha1[i1 + k] == ha2[i2 + k]; k++)
+ if (k == xenv->snake_cnt - 1) {
+ best = v;
+ spl->i1 = i1;
+ spl->i2 = i2;
+ break;
+ }
+ }
+ }
+ if (best > 0) {
+ spl->min_lo = 0;
+ spl->min_hi = 1;
+ return ec;
+ }
+ }
+
+ /*
+ * Enough is enough. We spent too much time here and now we
+ * collect the furthest reaching path using the (i1 + i2)
+ * measure.
+ */
+ if (ec >= xenv->mxcost) {
+ long fbest, fbest1, bbest, bbest1;
+
+ fbest = fbest1 = -1;
+ for (d = fmax; d >= fmin; d -= 2) {
+ i1 = XDL_MIN(kvdf[d], lim1);
+ i2 = i1 - d;
+ if (lim2 < i2)
+ i1 = lim2 + d, i2 = lim2;
+ if (fbest < i1 + i2) {
+ fbest = i1 + i2;
+ fbest1 = i1;
+ }
+ }
+
+ bbest = bbest1 = XDL_LINE_MAX;
+ for (d = bmax; d >= bmin; d -= 2) {
+ i1 = XDL_MAX(off1, kvdb[d]);
+ i2 = i1 - d;
+ if (i2 < off2)
+ i1 = off2 + d, i2 = off2;
+ if (i1 + i2 < bbest) {
+ bbest = i1 + i2;
+ bbest1 = i1;
+ }
+ }
+
+ if ((lim1 + lim2) - bbest < fbest - (off1 + off2)) {
+ spl->i1 = fbest1;
+ spl->i2 = fbest - fbest1;
+ spl->min_lo = 1;
+ spl->min_hi = 0;
+ } else {
+ spl->i1 = bbest1;
+ spl->i2 = bbest - bbest1;
+ spl->min_lo = 0;
+ spl->min_hi = 1;
+ }
+ return ec;
+ }
+ }
+}
+
+
+/*
+ * Rule: "Divide et Impera" (divide & conquer). Recursively split the box in
+ * sub-boxes by calling the box splitting function. Note that the real job
+ * (marking changed lines) is done in the two boundary reaching checks.
+ */
+int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,
+ diffdata_t *dd2, long off2, long lim2,
+ long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv) {
+ unsigned long const *ha1 = dd1->ha, *ha2 = dd2->ha;
+
+ /*
+ * Shrink the box by walking through each diagonal snake (SW and NE).
+ */
+ for (; off1 < lim1 && off2 < lim2 && ha1[off1] == ha2[off2]; off1++, off2++);
+ for (; off1 < lim1 && off2 < lim2 && ha1[lim1 - 1] == ha2[lim2 - 1]; lim1--, lim2--);
+
+ /*
+ * If one dimension is empty, then all records on the other one must
+ * be obviously changed.
+ */
+ if (off1 == lim1) {
+ char *rchg2 = dd2->rchg;
+ long *rindex2 = dd2->rindex;
+
+ for (; off2 < lim2; off2++)
+ rchg2[rindex2[off2]] = 1;
+ } else if (off2 == lim2) {
+ char *rchg1 = dd1->rchg;
+ long *rindex1 = dd1->rindex;
+
+ for (; off1 < lim1; off1++)
+ rchg1[rindex1[off1]] = 1;
+ } else {
+ xdpsplit_t spl;
+ spl.i1 = spl.i2 = 0;
+
+ /*
+ * Divide ...
+ */
+ if (xdl_split(ha1, off1, lim1, ha2, off2, lim2, kvdf, kvdb,
+ need_min, &spl, xenv) < 0) {
+
+ return -1;
+ }
+
+ /*
+ * ... et Impera.
+ */
+ if (xdl_recs_cmp(dd1, off1, spl.i1, dd2, off2, spl.i2,
+ kvdf, kvdb, spl.min_lo, xenv) < 0 ||
+ xdl_recs_cmp(dd1, spl.i1, lim1, dd2, spl.i2, lim2,
+ kvdf, kvdb, spl.min_hi, xenv) < 0) {
+
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+
+int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+ xdfenv_t *xe) {
+ long ndiags;
+ long *kvd, *kvdf, *kvdb;
+ xdalgoenv_t xenv;
+ diffdata_t dd1, dd2;
+ int res;
+
+ if (xdl_prepare_env(mf1, mf2, xpp, xe) < 0)
+ return -1;
+
+ if (XDF_DIFF_ALG(xpp->flags) == XDF_PATIENCE_DIFF) {
+ res = xdl_do_patience_diff(xpp, xe);
+ goto out;
+ }
+
+ if (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF) {
+ res = xdl_do_histogram_diff(xpp, xe);
+ goto out;
+ }
+
+ /*
+ * Allocate and setup K vectors to be used by the differential
+ * algorithm.
+ *
+ * One is to store the forward path and one to store the backward path.
+ */
+ ndiags = xe->xdf1.nreff + xe->xdf2.nreff + 3;
+ if (!XDL_ALLOC_ARRAY(kvd, 2 * ndiags + 2)) {
+
+ xdl_free_env(xe);
+ return -1;
+ }
+ kvdf = kvd;
+ kvdb = kvdf + ndiags;
+ kvdf += xe->xdf2.nreff + 1;
+ kvdb += xe->xdf2.nreff + 1;
+
+ xenv.mxcost = xdl_bogosqrt(ndiags);
+ if (xenv.mxcost < XDL_MAX_COST_MIN)
+ xenv.mxcost = XDL_MAX_COST_MIN;
+ xenv.snake_cnt = XDL_SNAKE_CNT;
+ xenv.heur_min = XDL_HEUR_MIN_COST;
+
+ dd1.nrec = xe->xdf1.nreff;
+ dd1.ha = xe->xdf1.ha;
+ dd1.rchg = xe->xdf1.rchg;
+ dd1.rindex = xe->xdf1.rindex;
+ dd2.nrec = xe->xdf2.nreff;
+ dd2.ha = xe->xdf2.ha;
+ dd2.rchg = xe->xdf2.rchg;
+ dd2.rindex = xe->xdf2.rindex;
+
+ res = xdl_recs_cmp(&dd1, 0, dd1.nrec, &dd2, 0, dd2.nrec,
+ kvdf, kvdb, (xpp->flags & XDF_NEED_MINIMAL) != 0,
+ &xenv);
+ xdl_free(kvd);
+ out:
+ if (res < 0)
+ xdl_free_env(xe);
+
+ return res;
+}
+
+
+static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1, long chg2) {
+ xdchange_t *xch;
+
+ if (!(xch = (xdchange_t *) xdl_malloc(sizeof(xdchange_t))))
+ return NULL;
+
+ xch->next = xscr;
+ xch->i1 = i1;
+ xch->i2 = i2;
+ xch->chg1 = chg1;
+ xch->chg2 = chg2;
+ xch->ignore = 0;
+
+ return xch;
+}
+
+
+static int recs_match(xrecord_t *rec1, xrecord_t *rec2)
+{
+ return (rec1->ha == rec2->ha);
+}
+
+/*
+ * If a line is indented more than this, get_indent() just returns this value.
+ * This avoids having to do absurd amounts of work for data that are not
+ * human-readable text, and also ensures that the output of get_indent fits
+ * within an int.
+ */
+#define MAX_INDENT 200
+
+/*
+ * Return the amount of indentation of the specified line, treating TAB as 8
+ * columns. Return -1 if line is empty or contains only whitespace. Clamp the
+ * output value at MAX_INDENT.
+ */
+static int get_indent(xrecord_t *rec)
+{
+ long i;
+ int ret = 0;
+
+ for (i = 0; i < rec->size; i++) {
+ char c = rec->ptr[i];
+
+ if (!XDL_ISSPACE(c))
+ return ret;
+ else if (c == ' ')
+ ret += 1;
+ else if (c == '\t')
+ ret += 8 - ret % 8;
+ /* ignore other whitespace characters */
+
+ if (ret >= MAX_INDENT)
+ return MAX_INDENT;
+ }
+
+ /* The line contains only whitespace. */
+ return -1;
+}
+
+/*
+ * If more than this number of consecutive blank rows are found, just return
+ * this value. This avoids requiring O(N^2) work for pathological cases, and
+ * also ensures that the output of score_split fits in an int.
+ */
+#define MAX_BLANKS 20
+
+/* Characteristics measured about a hypothetical split position. */
+struct split_measurement {
+ /*
+ * Is the split at the end of the file (aside from any blank lines)?
+ */
+ int end_of_file;
+
+ /*
+ * How much is the line immediately following the split indented (or -1
+ * if the line is blank):
+ */
+ int indent;
+
+ /*
+ * How many consecutive lines above the split are blank?
+ */
+ int pre_blank;
+
+ /*
+ * How much is the nearest non-blank line above the split indented (or
+ * -1 if there is no such line)?
+ */
+ int pre_indent;
+
+ /*
+ * How many lines after the line following the split are blank?
+ */
+ int post_blank;
+
+ /*
+ * How much is the nearest non-blank line after the line following the
+ * split indented (or -1 if there is no such line)?
+ */
+ int post_indent;
+};
+
+struct split_score {
+ /* The effective indent of this split (smaller is preferred). */
+ int effective_indent;
+
+ /* Penalty for this split (smaller is preferred). */
+ int penalty;
+};
+
+/*
+ * Fill m with information about a hypothetical split of xdf above line split.
+ */
+static void measure_split(const xdfile_t *xdf, long split,
+ struct split_measurement *m)
+{
+ long i;
+
+ if (split >= xdf->nrec) {
+ m->end_of_file = 1;
+ m->indent = -1;
+ } else {
+ m->end_of_file = 0;
+ m->indent = get_indent(xdf->recs[split]);
+ }
+
+ m->pre_blank = 0;
+ m->pre_indent = -1;
+ for (i = split - 1; i >= 0; i--) {
+ m->pre_indent = get_indent(xdf->recs[i]);
+ if (m->pre_indent != -1)
+ break;
+ m->pre_blank += 1;
+ if (m->pre_blank == MAX_BLANKS) {
+ m->pre_indent = 0;
+ break;
+ }
+ }
+
+ m->post_blank = 0;
+ m->post_indent = -1;
+ for (i = split + 1; i < xdf->nrec; i++) {
+ m->post_indent = get_indent(xdf->recs[i]);
+ if (m->post_indent != -1)
+ break;
+ m->post_blank += 1;
+ if (m->post_blank == MAX_BLANKS) {
+ m->post_indent = 0;
+ break;
+ }
+ }
+}
+
+/*
+ * The empirically-determined weight factors used by score_split() below.
+ * Larger values means that the position is a less favorable place to split.
+ *
+ * Note that scores are only ever compared against each other, so multiplying
+ * all of these weight/penalty values by the same factor wouldn't change the
+ * heuristic's behavior. Still, we need to set that arbitrary scale *somehow*.
+ * In practice, these numbers are chosen to be large enough that they can be
+ * adjusted relative to each other with sufficient precision despite using
+ * integer math.
+ */
+
+/* Penalty if there are no non-blank lines before the split */
+#define START_OF_FILE_PENALTY 1
+
+/* Penalty if there are no non-blank lines after the split */
+#define END_OF_FILE_PENALTY 21
+
+/* Multiplier for the number of blank lines around the split */
+#define TOTAL_BLANK_WEIGHT (-30)
+
+/* Multiplier for the number of blank lines after the split */
+#define POST_BLANK_WEIGHT 6
+
+/*
+ * Penalties applied if the line is indented more than its predecessor
+ */
+#define RELATIVE_INDENT_PENALTY (-4)
+#define RELATIVE_INDENT_WITH_BLANK_PENALTY 10
+
+/*
+ * Penalties applied if the line is indented less than both its predecessor and
+ * its successor
+ */
+#define RELATIVE_OUTDENT_PENALTY 24
+#define RELATIVE_OUTDENT_WITH_BLANK_PENALTY 17
+
+/*
+ * Penalties applied if the line is indented less than its predecessor but not
+ * less than its successor
+ */
+#define RELATIVE_DEDENT_PENALTY 23
+#define RELATIVE_DEDENT_WITH_BLANK_PENALTY 17
+
+/*
+ * We only consider whether the sum of the effective indents for splits are
+ * less than (-1), equal to (0), or greater than (+1) each other. The resulting
+ * value is multiplied by the following weight and combined with the penalty to
+ * determine the better of two scores.
+ */
+#define INDENT_WEIGHT 60
+
+/*
+ * How far do we slide a hunk at most?
+ */
+#define INDENT_HEURISTIC_MAX_SLIDING 100
+
+/*
+ * Compute a badness score for the hypothetical split whose measurements are
+ * stored in m. The weight factors were determined empirically using the tools
+ * and corpus described in
+ *
+ * https://github.com/mhagger/diff-slider-tools
+ *
+ * Also see that project if you want to improve the weights based on, for
+ * example, a larger or more diverse corpus.
+ */
+static void score_add_split(const struct split_measurement *m, struct split_score *s)
+{
+ /*
+ * A place to accumulate penalty factors (positive makes this index more
+ * favored):
+ */
+ int post_blank, total_blank, indent, any_blanks;
+
+ if (m->pre_indent == -1 && m->pre_blank == 0)
+ s->penalty += START_OF_FILE_PENALTY;
+
+ if (m->end_of_file)
+ s->penalty += END_OF_FILE_PENALTY;
+
+ /*
+ * Set post_blank to the number of blank lines following the split,
+ * including the line immediately after the split:
+ */
+ post_blank = (m->indent == -1) ? 1 + m->post_blank : 0;
+ total_blank = m->pre_blank + post_blank;
+
+ /* Penalties based on nearby blank lines: */
+ s->penalty += TOTAL_BLANK_WEIGHT * total_blank;
+ s->penalty += POST_BLANK_WEIGHT * post_blank;
+
+ if (m->indent != -1)
+ indent = m->indent;
+ else
+ indent = m->post_indent;
+
+ any_blanks = (total_blank != 0);
+
+ /* Note that the effective indent is -1 at the end of the file: */
+ s->effective_indent += indent;
+
+ if (indent == -1) {
+ /* No additional adjustments needed. */
+ } else if (m->pre_indent == -1) {
+ /* No additional adjustments needed. */
+ } else if (indent > m->pre_indent) {
+ /*
+ * The line is indented more than its predecessor.
+ */
+ s->penalty += any_blanks ?
+ RELATIVE_INDENT_WITH_BLANK_PENALTY :
+ RELATIVE_INDENT_PENALTY;
+ } else if (indent == m->pre_indent) {
+ /*
+ * The line has the same indentation level as its predecessor.
+ * No additional adjustments needed.
+ */
+ } else {
+ /*
+ * The line is indented less than its predecessor. It could be
+ * the block terminator of the previous block, but it could
+ * also be the start of a new block (e.g., an "else" block, or
+ * maybe the previous block didn't have a block terminator).
+ * Try to distinguish those cases based on what comes next:
+ */
+ if (m->post_indent != -1 && m->post_indent > indent) {
+ /*
+ * The following line is indented more. So it is likely
+ * that this line is the start of a block.
+ */
+ s->penalty += any_blanks ?
+ RELATIVE_OUTDENT_WITH_BLANK_PENALTY :
+ RELATIVE_OUTDENT_PENALTY;
+ } else {
+ /*
+ * That was probably the end of a block.
+ */
+ s->penalty += any_blanks ?
+ RELATIVE_DEDENT_WITH_BLANK_PENALTY :
+ RELATIVE_DEDENT_PENALTY;
+ }
+ }
+}
+
+static int score_cmp(struct split_score *s1, struct split_score *s2)
+{
+ /* -1 if s1.effective_indent < s2->effective_indent, etc. */
+ int cmp_indents = ((s1->effective_indent > s2->effective_indent) -
+ (s1->effective_indent < s2->effective_indent));
+
+ return INDENT_WEIGHT * cmp_indents + (s1->penalty - s2->penalty);
+}
+
+/*
+ * Represent a group of changed lines in an xdfile_t (i.e., a contiguous group
+ * of lines that was inserted or deleted from the corresponding version of the
+ * file). We consider there to be such a group at the beginning of the file, at
+ * the end of the file, and between any two unchanged lines, though most such
+ * groups will usually be empty.
+ *
+ * If the first line in a group is equal to the line following the group, then
+ * the group can be slid down. Similarly, if the last line in a group is equal
+ * to the line preceding the group, then the group can be slid up. See
+ * group_slide_down() and group_slide_up().
+ *
+ * Note that loops that are testing for changed lines in xdf->rchg do not need
+ * index bounding since the array is prepared with a zero at position -1 and N.
+ */
+struct xdlgroup {
+ /*
+ * The index of the first changed line in the group, or the index of
+ * the unchanged line above which the (empty) group is located.
+ */
+ long start;
+
+ /*
+ * The index of the first unchanged line after the group. For an empty
+ * group, end is equal to start.
+ */
+ long end;
+};
+
+/*
+ * Initialize g to point at the first group in xdf.
+ */
+static void group_init(xdfile_t *xdf, struct xdlgroup *g)
+{
+ g->start = g->end = 0;
+ while (xdf->rchg[g->end])
+ g->end++;
+}
+
+/*
+ * Move g to describe the next (possibly empty) group in xdf and return 0. If g
+ * is already at the end of the file, do nothing and return -1.
+ */
+static inline int group_next(xdfile_t *xdf, struct xdlgroup *g)
+{
+ if (g->end == xdf->nrec)
+ return -1;
+
+ g->start = g->end + 1;
+ for (g->end = g->start; xdf->rchg[g->end]; g->end++)
+ ;
+
+ return 0;
+}
+
+/*
+ * Move g to describe the previous (possibly empty) group in xdf and return 0.
+ * If g is already at the beginning of the file, do nothing and return -1.
+ */
+static inline int group_previous(xdfile_t *xdf, struct xdlgroup *g)
+{
+ if (g->start == 0)
+ return -1;
+
+ g->end = g->start - 1;
+ for (g->start = g->end; xdf->rchg[g->start - 1]; g->start--)
+ ;
+
+ return 0;
+}
+
+/*
+ * If g can be slid toward the end of the file, do so, and if it bumps into a
+ * following group, expand this group to include it. Return 0 on success or -1
+ * if g cannot be slid down.
+ */
+static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g)
+{
+ if (g->end < xdf->nrec &&
+ recs_match(xdf->recs[g->start], xdf->recs[g->end])) {
+ xdf->rchg[g->start++] = 0;
+ xdf->rchg[g->end++] = 1;
+
+ while (xdf->rchg[g->end])
+ g->end++;
+
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+/*
+ * If g can be slid toward the beginning of the file, do so, and if it bumps
+ * into a previous group, expand this group to include it. Return 0 on success
+ * or -1 if g cannot be slid up.
+ */
+static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g)
+{
+ if (g->start > 0 &&
+ recs_match(xdf->recs[g->start - 1], xdf->recs[g->end - 1])) {
+ xdf->rchg[--g->start] = 1;
+ xdf->rchg[--g->end] = 0;
+
+ while (xdf->rchg[g->start - 1])
+ g->start--;
+
+ return 0;
+ } else {
+ return -1;
+ }
+}
+
+/*
+ * Move back and forward change groups for a consistent and pretty diff output.
+ * This also helps in finding joinable change groups and reducing the diff
+ * size.
+ */
+int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) {
+ struct xdlgroup g, go;
+ long earliest_end, end_matching_other;
+ long groupsize;
+
+ group_init(xdf, &g);
+ group_init(xdfo, &go);
+
+ while (1) {
+ /*
+ * If the group is empty in the to-be-compacted file, skip it:
+ */
+ if (g.end == g.start)
+ goto next;
+
+ /*
+ * Now shift the change up and then down as far as possible in
+ * each direction. If it bumps into any other changes, merge
+ * them.
+ */
+ do {
+ groupsize = g.end - g.start;
+
+ /*
+ * Keep track of the last "end" index that causes this
+ * group to align with a group of changed lines in the
+ * other file. -1 indicates that we haven't found such
+ * a match yet:
+ */
+ end_matching_other = -1;
+
+ /* Shift the group backward as much as possible: */
+ while (!group_slide_up(xdf, &g))
+ if (group_previous(xdfo, &go))
+ XDL_BUG("group sync broken sliding up");
+
+ /*
+ * This is this highest that this group can be shifted.
+ * Record its end index:
+ */
+ earliest_end = g.end;
+
+ if (go.end > go.start)
+ end_matching_other = g.end;
+
+ /* Now shift the group forward as far as possible: */
+ while (1) {
+ if (group_slide_down(xdf, &g))
+ break;
+ if (group_next(xdfo, &go))
+ XDL_BUG("group sync broken sliding down");
+
+ if (go.end > go.start)
+ end_matching_other = g.end;
+ }
+ } while (groupsize != g.end - g.start);
+
+ /*
+ * If the group can be shifted, then we can possibly use this
+ * freedom to produce a more intuitive diff.
+ *
+ * The group is currently shifted as far down as possible, so
+ * the heuristics below only have to handle upwards shifts.
+ */
+
+ if (g.end == earliest_end) {
+ /* no shifting was possible */
+ } else if (end_matching_other != -1) {
+ /*
+ * Move the possibly merged group of changes back to
+ * line up with the last group of changes from the
+ * other file that it can align with.
+ */
+ while (go.end == go.start) {
+ if (group_slide_up(xdf, &g))
+ XDL_BUG("match disappeared");
+ if (group_previous(xdfo, &go))
+ XDL_BUG("group sync broken sliding to match");
+ }
+ } else if (flags & XDF_INDENT_HEURISTIC) {
+ /*
+ * Indent heuristic: a group of pure add/delete lines
+ * implies two splits, one between the end of the
+ * "before" context and the start of the group, and
+ * another between the end of the group and the
+ * beginning of the "after" context. Some splits are
+ * aesthetically better and some are worse. We compute
+ * a badness "score" for each split, and add the scores
+ * for the two splits to define a "score" for each
+ * position that the group can be shifted to. Then we
+ * pick the shift with the lowest score.
+ */
+ long shift, best_shift = -1;
+ struct split_score best_score;
+
+ shift = earliest_end;
+ if (g.end - groupsize - 1 > shift)
+ shift = g.end - groupsize - 1;
+ if (g.end - INDENT_HEURISTIC_MAX_SLIDING > shift)
+ shift = g.end - INDENT_HEURISTIC_MAX_SLIDING;
+ for (; shift <= g.end; shift++) {
+ struct split_measurement m;
+ struct split_score score = {0, 0};
+
+ measure_split(xdf, shift, &m);
+ score_add_split(&m, &score);
+ measure_split(xdf, shift - groupsize, &m);
+ score_add_split(&m, &score);
+ if (best_shift == -1 ||
+ score_cmp(&score, &best_score) <= 0) {
+ best_score.effective_indent = score.effective_indent;
+ best_score.penalty = score.penalty;
+ best_shift = shift;
+ }
+ }
+
+ while (g.end > best_shift) {
+ if (group_slide_up(xdf, &g))
+ XDL_BUG("best shift unreached");
+ if (group_previous(xdfo, &go))
+ XDL_BUG("group sync broken sliding to blank line");
+ }
+ }
+
+ next:
+ /* Move past the just-processed group: */
+ if (group_next(xdf, &g))
+ break;
+ if (group_next(xdfo, &go))
+ XDL_BUG("group sync broken moving to next group");
+ }
+
+ if (!group_next(xdfo, &go))
+ XDL_BUG("group sync broken at end of file");
+
+ return 0;
+}
+
+
+int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr) {
+ xdchange_t *cscr = NULL, *xch;
+ char *rchg1 = xe->xdf1.rchg, *rchg2 = xe->xdf2.rchg;
+ long i1, i2, l1, l2;
+
+ /*
+ * Trivial. Collects "groups" of changes and creates an edit script.
+ */
+ for (i1 = xe->xdf1.nrec, i2 = xe->xdf2.nrec; i1 >= 0 || i2 >= 0; i1--, i2--)
+ if (rchg1[i1 - 1] || rchg2[i2 - 1]) {
+ for (l1 = i1; rchg1[i1 - 1]; i1--);
+ for (l2 = i2; rchg2[i2 - 1]; i2--);
+
+ if (!(xch = xdl_add_change(cscr, i1, i2, l1 - i1, l2 - i2))) {
+ xdl_free_script(cscr);
+ return -1;
+ }
+ cscr = xch;
+ }
+
+ *xscr = cscr;
+
+ return 0;
+}
+
+
+void xdl_free_script(xdchange_t *xscr) {
+ xdchange_t *xch;
+
+ while ((xch = xscr) != NULL) {
+ xscr = xscr->next;
+ xdl_free(xch);
+ }
+}
+
+static int xdl_call_hunk_func(xdfenv_t *xe XDL_UNUSED, xdchange_t *xscr, xdemitcb_t *ecb,
+ xdemitconf_t const *xecfg)
+{
+ xdchange_t *xch, *xche;
+
+ for (xch = xscr; xch; xch = xche->next) {
+ xche = xdl_get_hunk(&xch, xecfg);
+ if (!xch)
+ break;
+ if (xecfg->hunk_func(xch->i1, xche->i1 + xche->chg1 - xch->i1,
+ xch->i2, xche->i2 + xche->chg2 - xch->i2,
+ ecb->priv) < 0)
+ return -1;
+ }
+ return 0;
+}
+
+static void xdl_mark_ignorable_lines(xdchange_t *xscr, xdfenv_t *xe, long flags)
+{
+ xdchange_t *xch;
+
+ for (xch = xscr; xch; xch = xch->next) {
+ int ignore = 1;
+ xrecord_t **rec;
+ long i;
+
+ rec = &xe->xdf1.recs[xch->i1];
+ for (i = 0; i < xch->chg1 && ignore; i++)
+ ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);
+
+ rec = &xe->xdf2.recs[xch->i2];
+ for (i = 0; i < xch->chg2 && ignore; i++)
+ ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags);
+
+ xch->ignore = ignore;
+ }
+}
+
+static int record_matches_regex(xrecord_t *rec, xpparam_t const *xpp) {
+ xdl_regmatch_t regmatch;
+ int i;
+
+ for (i = 0; i < xpp->ignore_regex_nr; i++)
+ if (!xdl_regexec_buf(xpp->ignore_regex[i], rec->ptr, rec->size, 1,
+ &regmatch, 0))
+ return 1;
+
+ return 0;
+}
+
+static void xdl_mark_ignorable_regex(xdchange_t *xscr, const xdfenv_t *xe,
+ xpparam_t const *xpp)
+{
+ xdchange_t *xch;
+
+ for (xch = xscr; xch; xch = xch->next) {
+ xrecord_t **rec;
+ int ignore = 1;
+ long i;
+
+ /*
+ * Do not override --ignore-blank-lines.
+ */
+ if (xch->ignore)
+ continue;
+
+ rec = &xe->xdf1.recs[xch->i1];
+ for (i = 0; i < xch->chg1 && ignore; i++)
+ ignore = record_matches_regex(rec[i], xpp);
+
+ rec = &xe->xdf2.recs[xch->i2];
+ for (i = 0; i < xch->chg2 && ignore; i++)
+ ignore = record_matches_regex(rec[i], xpp);
+
+ xch->ignore = ignore;
+ }
+}
+
+int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+ xdemitconf_t const *xecfg, xdemitcb_t *ecb) {
+ xdchange_t *xscr;
+ xdfenv_t xe;
+ emit_func_t ef = xecfg->hunk_func ? xdl_call_hunk_func : xdl_emit_diff;
+
+ if (xdl_do_diff(mf1, mf2, xpp, &xe) < 0) {
+
+ return -1;
+ }
+ if (xdl_change_compact(&xe.xdf1, &xe.xdf2, xpp->flags) < 0 ||
+ xdl_change_compact(&xe.xdf2, &xe.xdf1, xpp->flags) < 0 ||
+ xdl_build_script(&xe, &xscr) < 0) {
+
+ xdl_free_env(&xe);
+ return -1;
+ }
+ if (xscr) {
+ if (xpp->flags & XDF_IGNORE_BLANK_LINES)
+ xdl_mark_ignorable_lines(xscr, &xe, xpp->flags);
+
+ if (xpp->ignore_regex)
+ xdl_mark_ignorable_regex(xscr, &xe, xpp);
+
+ if (ef(&xe, xscr, ecb, xecfg) < 0) {
+
+ xdl_free_script(xscr);
+ xdl_free_env(&xe);
+ return -1;
+ }
+ xdl_free_script(xscr);
+ }
+ xdl_free_env(&xe);
+
+ return 0;
+}
diff --git a/deps/xdiff/xdiffi.h b/deps/xdiff/xdiffi.h
new file mode 100644
index 0000000..126c9d8
--- /dev/null
+++ b/deps/xdiff/xdiffi.h
@@ -0,0 +1,62 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#if !defined(XDIFFI_H)
+#define XDIFFI_H
+
+
+typedef struct s_diffdata {
+ long nrec;
+ unsigned long const *ha;
+ long *rindex;
+ char *rchg;
+} diffdata_t;
+
+typedef struct s_xdalgoenv {
+ long mxcost;
+ long snake_cnt;
+ long heur_min;
+} xdalgoenv_t;
+
+typedef struct s_xdchange {
+ struct s_xdchange *next;
+ long i1, i2;
+ long chg1, chg2;
+ int ignore;
+} xdchange_t;
+
+
+
+int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1,
+ diffdata_t *dd2, long off2, long lim2,
+ long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv);
+int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+ xdfenv_t *xe);
+int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags);
+int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr);
+void xdl_free_script(xdchange_t *xscr);
+int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
+ xdemitconf_t const *xecfg);
+int xdl_do_patience_diff(xpparam_t const *xpp, xdfenv_t *env);
+int xdl_do_histogram_diff(xpparam_t const *xpp, xdfenv_t *env);
+
+#endif /* #if !defined(XDIFFI_H) */
diff --git a/deps/xdiff/xemit.c b/deps/xdiff/xemit.c
new file mode 100644
index 0000000..75f0fe4
--- /dev/null
+++ b/deps/xdiff/xemit.c
@@ -0,0 +1,330 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#include "xinclude.h"
+
+static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec) {
+
+ *rec = xdf->recs[ri]->ptr;
+
+ return xdf->recs[ri]->size;
+}
+
+
+static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb) {
+ long size, psize = strlen(pre);
+ char const *rec;
+
+ size = xdl_get_rec(xdf, ri, &rec);
+ if (xdl_emit_diffrec(rec, size, pre, psize, ecb) < 0) {
+
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * Starting at the passed change atom, find the latest change atom to be included
+ * inside the differential hunk according to the specified configuration.
+ * Also advance xscr if the first changes must be discarded.
+ */
+xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg)
+{
+ xdchange_t *xch, *xchp, *lxch;
+ long max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen;
+ long max_ignorable = xecfg->ctxlen;
+ unsigned long ignored = 0; /* number of ignored blank lines */
+
+ /* remove ignorable changes that are too far before other changes */
+ for (xchp = *xscr; xchp && xchp->ignore; xchp = xchp->next) {
+ xch = xchp->next;
+
+ if (xch == NULL ||
+ xch->i1 - (xchp->i1 + xchp->chg1) >= max_ignorable)
+ *xscr = xch;
+ }
+
+ if (!*xscr)
+ return NULL;
+
+ lxch = *xscr;
+
+ for (xchp = *xscr, xch = xchp->next; xch; xchp = xch, xch = xch->next) {
+ long distance = xch->i1 - (xchp->i1 + xchp->chg1);
+ if (distance > max_common)
+ break;
+
+ if (distance < max_ignorable && (!xch->ignore || lxch == xchp)) {
+ lxch = xch;
+ ignored = 0;
+ } else if (distance < max_ignorable && xch->ignore) {
+ ignored += xch->chg2;
+ } else if (lxch != xchp &&
+ xch->i1 + ignored - (lxch->i1 + lxch->chg1) > max_common) {
+ break;
+ } else if (!xch->ignore) {
+ lxch = xch;
+ ignored = 0;
+ } else {
+ ignored += xch->chg2;
+ }
+ }
+
+ return lxch;
+}
+
+
+static long def_ff(const char *rec, long len, char *buf, long sz)
+{
+ if (len > 0 &&
+ (isalpha((unsigned char)*rec) || /* identifier? */
+ *rec == '_' || /* also identifier? */
+ *rec == '$')) { /* identifiers from VMS and other esoterico */
+ if (len > sz)
+ len = sz;
+ while (0 < len && isspace((unsigned char)rec[len - 1]))
+ len--;
+ memcpy(buf, rec, len);
+ return len;
+ }
+ return -1;
+}
+
+static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri,
+ char *buf, long sz)
+{
+ const char *rec;
+ long len = xdl_get_rec(xdf, ri, &rec);
+ if (!xecfg->find_func)
+ return def_ff(rec, len, buf, sz);
+ return xecfg->find_func(rec, len, buf, sz, xecfg->find_func_priv);
+}
+
+static int is_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri)
+{
+ char dummy[1];
+ return match_func_rec(xdf, xecfg, ri, dummy, sizeof(dummy)) >= 0;
+}
+
+struct func_line {
+ long len;
+ char buf[80];
+};
+
+static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg,
+ struct func_line *func_line, long start, long limit)
+{
+ long l, size, step = (start > limit) ? -1 : 1;
+ char *buf, dummy[1];
+
+ buf = func_line ? func_line->buf : dummy;
+ size = func_line ? sizeof(func_line->buf) : sizeof(dummy);
+
+ for (l = start; l != limit && 0 <= l && l < xe->xdf1.nrec; l += step) {
+ long len = match_func_rec(&xe->xdf1, xecfg, l, buf, size);
+ if (len >= 0) {
+ if (func_line)
+ func_line->len = len;
+ return l;
+ }
+ }
+ return -1;
+}
+
+static int is_empty_rec(xdfile_t *xdf, long ri)
+{
+ const char *rec;
+ long len = xdl_get_rec(xdf, ri, &rec);
+
+ while (len > 0 && XDL_ISSPACE(*rec)) {
+ rec++;
+ len--;
+ }
+ return !len;
+}
+
+int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
+ xdemitconf_t const *xecfg) {
+ long s1, s2, e1, e2, lctx;
+ xdchange_t *xch, *xche;
+ long funclineprev = -1;
+ struct func_line func_line = { 0 };
+
+ for (xch = xscr; xch; xch = xche->next) {
+ xdchange_t *xchp = xch;
+ xche = xdl_get_hunk(&xch, xecfg);
+ if (!xch)
+ break;
+
+pre_context_calculation:
+ s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0);
+ s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0);
+
+ if (xecfg->flags & XDL_EMIT_FUNCCONTEXT) {
+ long fs1, i1 = xch->i1;
+
+ /* Appended chunk? */
+ if (i1 >= xe->xdf1.nrec) {
+ long i2 = xch->i2;
+
+ /*
+ * We don't need additional context if
+ * a whole function was added.
+ */
+ while (i2 < xe->xdf2.nrec) {
+ if (is_func_rec(&xe->xdf2, xecfg, i2))
+ goto post_context_calculation;
+ i2++;
+ }
+
+ /*
+ * Otherwise get more context from the
+ * pre-image.
+ */
+ i1 = xe->xdf1.nrec - 1;
+ }
+
+ fs1 = get_func_line(xe, xecfg, NULL, i1, -1);
+ while (fs1 > 0 && !is_empty_rec(&xe->xdf1, fs1 - 1) &&
+ !is_func_rec(&xe->xdf1, xecfg, fs1 - 1))
+ fs1--;
+ if (fs1 < 0)
+ fs1 = 0;
+ if (fs1 < s1) {
+ s2 = XDL_MAX(s2 - (s1 - fs1), 0);
+ s1 = fs1;
+
+ /*
+ * Did we extend context upwards into an
+ * ignored change?
+ */
+ while (xchp != xch &&
+ xchp->i1 + xchp->chg1 <= s1 &&
+ xchp->i2 + xchp->chg2 <= s2)
+ xchp = xchp->next;
+
+ /* If so, show it after all. */
+ if (xchp != xch) {
+ xch = xchp;
+ goto pre_context_calculation;
+ }
+ }
+ }
+
+ post_context_calculation:
+ lctx = xecfg->ctxlen;
+ lctx = XDL_MIN(lctx, xe->xdf1.nrec - (xche->i1 + xche->chg1));
+ lctx = XDL_MIN(lctx, xe->xdf2.nrec - (xche->i2 + xche->chg2));
+
+ e1 = xche->i1 + xche->chg1 + lctx;
+ e2 = xche->i2 + xche->chg2 + lctx;
+
+ if (xecfg->flags & XDL_EMIT_FUNCCONTEXT) {
+ long fe1 = get_func_line(xe, xecfg, NULL,
+ xche->i1 + xche->chg1,
+ xe->xdf1.nrec);
+ while (fe1 > 0 && is_empty_rec(&xe->xdf1, fe1 - 1))
+ fe1--;
+ if (fe1 < 0)
+ fe1 = xe->xdf1.nrec;
+ if (fe1 > e1) {
+ e2 = XDL_MIN(e2 + (fe1 - e1), xe->xdf2.nrec);
+ e1 = fe1;
+ }
+
+ /*
+ * Overlap with next change? Then include it
+ * in the current hunk and start over to find
+ * its new end.
+ */
+ if (xche->next) {
+ long l = XDL_MIN(xche->next->i1,
+ xe->xdf1.nrec - 1);
+ if (l - xecfg->ctxlen <= e1 ||
+ get_func_line(xe, xecfg, NULL, l, e1) < 0) {
+ xche = xche->next;
+ goto post_context_calculation;
+ }
+ }
+ }
+
+ /*
+ * Emit current hunk header.
+ */
+
+ if (xecfg->flags & XDL_EMIT_FUNCNAMES) {
+ get_func_line(xe, xecfg, &func_line,
+ s1 - 1, funclineprev);
+ funclineprev = s1 - 1;
+ }
+ if (!(xecfg->flags & XDL_EMIT_NO_HUNK_HDR) &&
+ xdl_emit_hunk_hdr(s1 + 1, e1 - s1, s2 + 1, e2 - s2,
+ func_line.buf, func_line.len, ecb) < 0)
+ return -1;
+
+ /*
+ * Emit pre-context.
+ */
+ for (; s2 < xch->i2; s2++)
+ if (xdl_emit_record(&xe->xdf2, s2, " ", ecb) < 0)
+ return -1;
+
+ for (s1 = xch->i1, s2 = xch->i2;; xch = xch->next) {
+ /*
+ * Merge previous with current change atom.
+ */
+ for (; s1 < xch->i1 && s2 < xch->i2; s1++, s2++)
+ if (xdl_emit_record(&xe->xdf2, s2, " ", ecb) < 0)
+ return -1;
+
+ /*
+ * Removes lines from the first file.
+ */
+ for (s1 = xch->i1; s1 < xch->i1 + xch->chg1; s1++)
+ if (xdl_emit_record(&xe->xdf1, s1, "-", ecb) < 0)
+ return -1;
+
+ /*
+ * Adds lines from the second file.
+ */
+ for (s2 = xch->i2; s2 < xch->i2 + xch->chg2; s2++)
+ if (xdl_emit_record(&xe->xdf2, s2, "+", ecb) < 0)
+ return -1;
+
+ if (xch == xche)
+ break;
+ s1 = xch->i1 + xch->chg1;
+ s2 = xch->i2 + xch->chg2;
+ }
+
+ /*
+ * Emit post-context.
+ */
+ for (s2 = xche->i2 + xche->chg2; s2 < e2; s2++)
+ if (xdl_emit_record(&xe->xdf2, s2, " ", ecb) < 0)
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/deps/xdiff/xemit.h b/deps/xdiff/xemit.h
new file mode 100644
index 0000000..1b9887e
--- /dev/null
+++ b/deps/xdiff/xemit.h
@@ -0,0 +1,36 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#if !defined(XEMIT_H)
+#define XEMIT_H
+
+
+typedef int (*emit_func_t)(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
+ xdemitconf_t const *xecfg);
+
+xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg);
+int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
+ xdemitconf_t const *xecfg);
+
+
+
+#endif /* #if !defined(XEMIT_H) */
diff --git a/deps/xdiff/xhistogram.c b/deps/xdiff/xhistogram.c
new file mode 100644
index 0000000..16a8fe2
--- /dev/null
+++ b/deps/xdiff/xhistogram.c
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) 2010, Google Inc.
+ * and other copyright owners as documented in JGit's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "xinclude.h"
+
+#define MAX_PTR UINT_MAX
+#define MAX_CNT UINT_MAX
+
+#define LINE_END(n) (line##n + count##n - 1)
+#define LINE_END_PTR(n) (*line##n + *count##n - 1)
+
+struct histindex {
+ struct record {
+ unsigned int ptr, cnt;
+ struct record *next;
+ } **records, /* an occurrence */
+ **line_map; /* map of line to record chain */
+ chastore_t rcha;
+ unsigned int *next_ptrs;
+ unsigned int table_bits,
+ records_size,
+ line_map_size;
+
+ unsigned int max_chain_length,
+ key_shift,
+ ptr_shift;
+
+ unsigned int cnt,
+ has_common;
+
+ xdfenv_t *env;
+ xpparam_t const *xpp;
+};
+
+struct region {
+ unsigned int begin1, end1;
+ unsigned int begin2, end2;
+};
+
+#define LINE_MAP(i, a) (i->line_map[(a) - i->ptr_shift])
+
+#define NEXT_PTR(index, ptr) \
+ (index->next_ptrs[(ptr) - index->ptr_shift])
+
+#define CNT(index, ptr) \
+ ((LINE_MAP(index, ptr))->cnt)
+
+#define REC(env, s, l) \
+ (env->xdf##s.recs[l - 1])
+
+static int cmp_recs(xrecord_t *r1, xrecord_t *r2)
+{
+ return r1->ha == r2->ha;
+
+}
+
+#define CMP(i, s1, l1, s2, l2) \
+ (cmp_recs(REC(i->env, s1, l1), REC(i->env, s2, l2)))
+
+#define TABLE_HASH(index, side, line) \
+ XDL_HASHLONG((REC(index->env, side, line))->ha, index->table_bits)
+
+static int scanA(struct histindex *index, int line1, int count1)
+{
+ unsigned int ptr, tbl_idx;
+ unsigned int chain_len;
+ struct record **rec_chain, *rec;
+
+ for (ptr = LINE_END(1); line1 <= ptr; ptr--) {
+ tbl_idx = TABLE_HASH(index, 1, ptr);
+ rec_chain = index->records + tbl_idx;
+ rec = *rec_chain;
+
+ chain_len = 0;
+ while (rec) {
+ if (CMP(index, 1, rec->ptr, 1, ptr)) {
+ /*
+ * ptr is identical to another element. Insert
+ * it onto the front of the existing element
+ * chain.
+ */
+ NEXT_PTR(index, ptr) = rec->ptr;
+ rec->ptr = ptr;
+ /* cap rec->cnt at MAX_CNT */
+ rec->cnt = XDL_MIN(MAX_CNT, rec->cnt + 1);
+ LINE_MAP(index, ptr) = rec;
+ goto continue_scan;
+ }
+
+ rec = rec->next;
+ chain_len++;
+ }
+
+ if (chain_len == index->max_chain_length)
+ return -1;
+
+ /*
+ * This is the first time we have ever seen this particular
+ * element in the sequence. Construct a new chain for it.
+ */
+ if (!(rec = xdl_cha_alloc(&index->rcha)))
+ return -1;
+ rec->ptr = ptr;
+ rec->cnt = 1;
+ rec->next = *rec_chain;
+ *rec_chain = rec;
+ LINE_MAP(index, ptr) = rec;
+
+continue_scan:
+ ; /* no op */
+ }
+
+ return 0;
+}
+
+static int try_lcs(struct histindex *index, struct region *lcs, int b_ptr,
+ int line1, int count1, int line2, int count2)
+{
+ unsigned int b_next = b_ptr + 1;
+ struct record *rec = index->records[TABLE_HASH(index, 2, b_ptr)];
+ unsigned int as, ae, bs, be, np, rc;
+ int should_break;
+
+ for (; rec; rec = rec->next) {
+ if (rec->cnt > index->cnt) {
+ if (!index->has_common)
+ index->has_common = CMP(index, 1, rec->ptr, 2, b_ptr);
+ continue;
+ }
+
+ as = rec->ptr;
+ if (!CMP(index, 1, as, 2, b_ptr))
+ continue;
+
+ index->has_common = 1;
+ for (;;) {
+ should_break = 0;
+ np = NEXT_PTR(index, as);
+ bs = b_ptr;
+ ae = as;
+ be = bs;
+ rc = rec->cnt;
+
+ while (line1 < as && line2 < bs
+ && CMP(index, 1, as - 1, 2, bs - 1)) {
+ as--;
+ bs--;
+ if (1 < rc)
+ rc = XDL_MIN(rc, CNT(index, as));
+ }
+ while (ae < LINE_END(1) && be < LINE_END(2)
+ && CMP(index, 1, ae + 1, 2, be + 1)) {
+ ae++;
+ be++;
+ if (1 < rc)
+ rc = XDL_MIN(rc, CNT(index, ae));
+ }
+
+ if (b_next <= be)
+ b_next = be + 1;
+ if (lcs->end1 - lcs->begin1 < ae - as || rc < index->cnt) {
+ lcs->begin1 = as;
+ lcs->begin2 = bs;
+ lcs->end1 = ae;
+ lcs->end2 = be;
+ index->cnt = rc;
+ }
+
+ if (np == 0)
+ break;
+
+ while (np <= ae) {
+ np = NEXT_PTR(index, np);
+ if (np == 0) {
+ should_break = 1;
+ break;
+ }
+ }
+
+ if (should_break)
+ break;
+
+ as = np;
+ }
+ }
+ return b_next;
+}
+
+static int fall_back_to_classic_diff(xpparam_t const *xpp, xdfenv_t *env,
+ int line1, int count1, int line2, int count2)
+{
+ xpparam_t xpparam;
+
+ memset(&xpparam, 0, sizeof(xpparam));
+ xpparam.flags = xpp->flags & ~XDF_DIFF_ALGORITHM_MASK;
+
+ return xdl_fall_back_diff(env, &xpparam,
+ line1, count1, line2, count2);
+}
+
+static inline void free_index(struct histindex *index)
+{
+ xdl_free(index->records);
+ xdl_free(index->line_map);
+ xdl_free(index->next_ptrs);
+ xdl_cha_free(&index->rcha);
+}
+
+static int find_lcs(xpparam_t const *xpp, xdfenv_t *env,
+ struct region *lcs,
+ int line1, int count1, int line2, int count2)
+{
+ int b_ptr;
+ int ret = -1;
+ struct histindex index;
+
+ memset(&index, 0, sizeof(index));
+
+ index.env = env;
+ index.xpp = xpp;
+
+ index.records = NULL;
+ index.line_map = NULL;
+ /* in case of early xdl_cha_free() */
+ index.rcha.head = NULL;
+
+ index.table_bits = xdl_hashbits(count1);
+ index.records_size = 1 << index.table_bits;
+ if (!XDL_CALLOC_ARRAY(index.records, index.records_size))
+ goto cleanup;
+
+ index.line_map_size = count1;
+ if (!XDL_CALLOC_ARRAY(index.line_map, index.line_map_size))
+ goto cleanup;
+
+ if (!XDL_CALLOC_ARRAY(index.next_ptrs, index.line_map_size))
+ goto cleanup;
+
+ /* lines / 4 + 1 comes from xprepare.c:xdl_prepare_ctx() */
+ if (xdl_cha_init(&index.rcha, sizeof(struct record), count1 / 4 + 1) < 0)
+ goto cleanup;
+
+ index.ptr_shift = line1;
+ index.max_chain_length = 64;
+
+ if (scanA(&index, line1, count1))
+ goto cleanup;
+
+ index.cnt = index.max_chain_length + 1;
+
+ for (b_ptr = line2; b_ptr <= LINE_END(2); )
+ b_ptr = try_lcs(&index, lcs, b_ptr, line1, count1, line2, count2);
+
+ if (index.has_common && index.max_chain_length < index.cnt)
+ ret = 1;
+ else
+ ret = 0;
+
+cleanup:
+ free_index(&index);
+ return ret;
+}
+
+static int histogram_diff(xpparam_t const *xpp, xdfenv_t *env,
+ int line1, int count1, int line2, int count2)
+{
+ struct region lcs;
+ int lcs_found;
+ int result;
+redo:
+ result = -1;
+
+ if (count1 <= 0 && count2 <= 0)
+ return 0;
+
+ if (LINE_END(1) >= MAX_PTR)
+ return -1;
+
+ if (!count1) {
+ while(count2--)
+ env->xdf2.rchg[line2++ - 1] = 1;
+ return 0;
+ } else if (!count2) {
+ while(count1--)
+ env->xdf1.rchg[line1++ - 1] = 1;
+ return 0;
+ }
+
+ memset(&lcs, 0, sizeof(lcs));
+ lcs_found = find_lcs(xpp, env, &lcs, line1, count1, line2, count2);
+ if (lcs_found < 0)
+ goto out;
+ else if (lcs_found)
+ result = fall_back_to_classic_diff(xpp, env, line1, count1, line2, count2);
+ else {
+ if (lcs.begin1 == 0 && lcs.begin2 == 0) {
+ while (count1--)
+ env->xdf1.rchg[line1++ - 1] = 1;
+ while (count2--)
+ env->xdf2.rchg[line2++ - 1] = 1;
+ result = 0;
+ } else {
+ result = histogram_diff(xpp, env,
+ line1, lcs.begin1 - line1,
+ line2, lcs.begin2 - line2);
+ if (result)
+ goto out;
+ /*
+ * result = histogram_diff(xpp, env,
+ * lcs.end1 + 1, LINE_END(1) - lcs.end1,
+ * lcs.end2 + 1, LINE_END(2) - lcs.end2);
+ * but let's optimize tail recursion ourself:
+ */
+ count1 = LINE_END(1) - lcs.end1;
+ line1 = lcs.end1 + 1;
+ count2 = LINE_END(2) - lcs.end2;
+ line2 = lcs.end2 + 1;
+ goto redo;
+ }
+ }
+out:
+ return result;
+}
+
+int xdl_do_histogram_diff(xpparam_t const *xpp, xdfenv_t *env)
+{
+ return histogram_diff(xpp, env,
+ env->xdf1.dstart + 1, env->xdf1.dend - env->xdf1.dstart + 1,
+ env->xdf2.dstart + 1, env->xdf2.dend - env->xdf2.dstart + 1);
+}
diff --git a/deps/xdiff/xinclude.h b/deps/xdiff/xinclude.h
new file mode 100644
index 0000000..75db1d8
--- /dev/null
+++ b/deps/xdiff/xinclude.h
@@ -0,0 +1,36 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#if !defined(XINCLUDE_H)
+#define XINCLUDE_H
+
+#include "git-xdiff.h"
+#include "xmacros.h"
+#include "xdiff.h"
+#include "xtypes.h"
+#include "xutils.h"
+#include "xprepare.h"
+#include "xdiffi.h"
+#include "xemit.h"
+
+
+#endif /* #if !defined(XINCLUDE_H) */
diff --git a/deps/xdiff/xmacros.h b/deps/xdiff/xmacros.h
new file mode 100644
index 0000000..8487bb3
--- /dev/null
+++ b/deps/xdiff/xmacros.h
@@ -0,0 +1,71 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#if !defined(XMACROS_H)
+#define XMACROS_H
+
+
+
+
+#define XDL_MIN(a, b) ((a) < (b) ? (a): (b))
+#define XDL_MAX(a, b) ((a) > (b) ? (a): (b))
+#define XDL_ABS(v) ((v) >= 0 ? (v): -(v))
+#define XDL_ISDIGIT(c) ((c) >= '0' && (c) <= '9')
+#define XDL_ISSPACE(c) (isspace((unsigned char)(c)))
+#define XDL_ADDBITS(v,b) ((v) + ((v) >> (b)))
+#define XDL_MASKBITS(b) ((1UL << (b)) - 1)
+#define XDL_HASHLONG(v,b) (XDL_ADDBITS((unsigned long)(v), b) & XDL_MASKBITS(b))
+#define XDL_LE32_PUT(p, v) \
+do { \
+ unsigned char *__p = (unsigned char *) (p); \
+ *__p++ = (unsigned char) (v); \
+ *__p++ = (unsigned char) ((v) >> 8); \
+ *__p++ = (unsigned char) ((v) >> 16); \
+ *__p = (unsigned char) ((v) >> 24); \
+} while (0)
+#define XDL_LE32_GET(p, v) \
+do { \
+ unsigned char const *__p = (unsigned char const *) (p); \
+ (v) = (unsigned long) __p[0] | ((unsigned long) __p[1]) << 8 | \
+ ((unsigned long) __p[2]) << 16 | ((unsigned long) __p[3]) << 24; \
+} while (0)
+
+/* Allocate an array of nr elements, returns NULL on failure */
+#define XDL_ALLOC_ARRAY(p, nr) \
+ ((p) = SIZE_MAX / sizeof(*(p)) >= (size_t)(nr) \
+ ? xdl_malloc((nr) * sizeof(*(p))) \
+ : NULL)
+
+/* Allocate an array of nr zeroed out elements, returns NULL on failure */
+#define XDL_CALLOC_ARRAY(p, nr) ((p) = xdl_calloc(nr, sizeof(*(p))))
+
+/*
+ * Ensure array p can accommodate at least nr elements, growing the
+ * array and updating alloc (which is the number of allocated
+ * elements) as necessary. Frees p and returns -1 on failure, returns
+ * 0 on success
+ */
+#define XDL_ALLOC_GROW(p, nr, alloc) \
+ (-!((nr) <= (alloc) || \
+ ((p) = xdl_alloc_grow_helper((p), (nr), &(alloc), sizeof(*(p))))))
+
+#endif /* #if !defined(XMACROS_H) */
diff --git a/deps/xdiff/xmerge.c b/deps/xdiff/xmerge.c
new file mode 100644
index 0000000..af40c88
--- /dev/null
+++ b/deps/xdiff/xmerge.c
@@ -0,0 +1,739 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003-2006 Davide Libenzi, Johannes E. Schindelin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#include "xinclude.h"
+
+typedef struct s_xdmerge {
+ struct s_xdmerge *next;
+ /*
+ * 0 = conflict,
+ * 1 = no conflict, take first,
+ * 2 = no conflict, take second.
+ * 3 = no conflict, take both.
+ */
+ int mode;
+ /*
+ * These point at the respective postimages. E.g. <i1,chg1> is
+ * how side #1 wants to change the common ancestor; if there is no
+ * overlap, lines before i1 in the postimage of side #1 appear
+ * in the merge result as a region touched by neither side.
+ */
+ long i1, i2;
+ long chg1, chg2;
+ /*
+ * These point at the preimage; of course there is just one
+ * preimage, that is from the shared common ancestor.
+ */
+ long i0;
+ long chg0;
+} xdmerge_t;
+
+static int xdl_append_merge(xdmerge_t **merge, int mode,
+ long i0, long chg0,
+ long i1, long chg1,
+ long i2, long chg2)
+{
+ xdmerge_t *m = *merge;
+ if (m && (i1 <= m->i1 + m->chg1 || i2 <= m->i2 + m->chg2)) {
+ if (mode != m->mode)
+ m->mode = 0;
+ m->chg0 = i0 + chg0 - m->i0;
+ m->chg1 = i1 + chg1 - m->i1;
+ m->chg2 = i2 + chg2 - m->i2;
+ } else {
+ m = xdl_malloc(sizeof(xdmerge_t));
+ if (!m)
+ return -1;
+ m->next = NULL;
+ m->mode = mode;
+ m->i0 = i0;
+ m->chg0 = chg0;
+ m->i1 = i1;
+ m->chg1 = chg1;
+ m->i2 = i2;
+ m->chg2 = chg2;
+ if (*merge)
+ (*merge)->next = m;
+ *merge = m;
+ }
+ return 0;
+}
+
+static int xdl_cleanup_merge(xdmerge_t *c)
+{
+ int count = 0;
+ xdmerge_t *next_c;
+
+ /* were there conflicts? */
+ for (; c; c = next_c) {
+ if (c->mode == 0)
+ count++;
+ next_c = c->next;
+ free(c);
+ }
+ return count;
+}
+
+static int xdl_merge_cmp_lines(xdfenv_t *xe1, int i1, xdfenv_t *xe2, int i2,
+ int line_count, long flags)
+{
+ int i;
+ xrecord_t **rec1 = xe1->xdf2.recs + i1;
+ xrecord_t **rec2 = xe2->xdf2.recs + i2;
+
+ for (i = 0; i < line_count; i++) {
+ int result = xdl_recmatch(rec1[i]->ptr, rec1[i]->size,
+ rec2[i]->ptr, rec2[i]->size, flags);
+ if (!result)
+ return -1;
+ }
+ return 0;
+}
+
+static int xdl_recs_copy_0(int use_orig, xdfenv_t *xe, int i, int count, int needs_cr, int add_nl, char *dest)
+{
+ xrecord_t **recs;
+ int size = 0;
+
+ recs = (use_orig ? xe->xdf1.recs : xe->xdf2.recs) + i;
+
+ if (count < 1)
+ return 0;
+
+ for (i = 0; i < count; size += recs[i++]->size)
+ if (dest)
+ memcpy(dest + size, recs[i]->ptr, recs[i]->size);
+ if (add_nl) {
+ i = recs[count - 1]->size;
+ if (i == 0 || recs[count - 1]->ptr[i - 1] != '\n') {
+ if (needs_cr) {
+ if (dest)
+ dest[size] = '\r';
+ size++;
+ }
+
+ if (dest)
+ dest[size] = '\n';
+ size++;
+ }
+ }
+ return size;
+}
+
+static int xdl_recs_copy(xdfenv_t *xe, int i, int count, int needs_cr, int add_nl, char *dest)
+{
+ return xdl_recs_copy_0(0, xe, i, count, needs_cr, add_nl, dest);
+}
+
+static int xdl_orig_copy(xdfenv_t *xe, int i, int count, int needs_cr, int add_nl, char *dest)
+{
+ return xdl_recs_copy_0(1, xe, i, count, needs_cr, add_nl, dest);
+}
+
+/*
+ * Returns 1 if the i'th line ends in CR/LF (if it is the last line and
+ * has no eol, the preceding line, if any), 0 if it ends in LF-only, and
+ * -1 if the line ending cannot be determined.
+ */
+static int is_eol_crlf(xdfile_t *file, int i)
+{
+ long size;
+
+ if (i < file->nrec - 1)
+ /* All lines before the last *must* end in LF */
+ return (size = file->recs[i]->size) > 1 &&
+ file->recs[i]->ptr[size - 2] == '\r';
+ if (!file->nrec)
+ /* Cannot determine eol style from empty file */
+ return -1;
+ if ((size = file->recs[i]->size) &&
+ file->recs[i]->ptr[size - 1] == '\n')
+ /* Last line; ends in LF; Is it CR/LF? */
+ return size > 1 &&
+ file->recs[i]->ptr[size - 2] == '\r';
+ if (!i)
+ /* The only line has no eol */
+ return -1;
+ /* Determine eol from second-to-last line */
+ return (size = file->recs[i - 1]->size) > 1 &&
+ file->recs[i - 1]->ptr[size - 2] == '\r';
+}
+
+static int is_cr_needed(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m)
+{
+ int needs_cr;
+
+ /* Match post-images' preceding, or first, lines' end-of-line style */
+ needs_cr = is_eol_crlf(&xe1->xdf2, m->i1 ? m->i1 - 1 : 0);
+ if (needs_cr)
+ needs_cr = is_eol_crlf(&xe2->xdf2, m->i2 ? m->i2 - 1 : 0);
+ /* Look at pre-image's first line, unless we already settled on LF */
+ if (needs_cr)
+ needs_cr = is_eol_crlf(&xe1->xdf1, 0);
+ /* If still undecided, use LF-only */
+ return needs_cr < 0 ? 0 : needs_cr;
+}
+
+static int fill_conflict_hunk(xdfenv_t *xe1, const char *name1,
+ xdfenv_t *xe2, const char *name2,
+ const char *name3,
+ int size, int i, int style,
+ xdmerge_t *m, char *dest, int marker_size)
+{
+ int marker1_size = (name1 ? strlen(name1) + 1 : 0);
+ int marker2_size = (name2 ? strlen(name2) + 1 : 0);
+ int marker3_size = (name3 ? strlen(name3) + 1 : 0);
+ int needs_cr = is_cr_needed(xe1, xe2, m);
+
+ if (marker_size <= 0)
+ marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
+
+ /* Before conflicting part */
+ size += xdl_recs_copy(xe1, i, m->i1 - i, 0, 0,
+ dest ? dest + size : NULL);
+
+ if (!dest) {
+ size += marker_size + 1 + needs_cr + marker1_size;
+ } else {
+ memset(dest + size, '<', marker_size);
+ size += marker_size;
+ if (marker1_size) {
+ dest[size] = ' ';
+ memcpy(dest + size + 1, name1, marker1_size - 1);
+ size += marker1_size;
+ }
+ if (needs_cr)
+ dest[size++] = '\r';
+ dest[size++] = '\n';
+ }
+
+ /* Postimage from side #1 */
+ size += xdl_recs_copy(xe1, m->i1, m->chg1, needs_cr, 1,
+ dest ? dest + size : NULL);
+
+ if (style == XDL_MERGE_DIFF3 || style == XDL_MERGE_ZEALOUS_DIFF3) {
+ /* Shared preimage */
+ if (!dest) {
+ size += marker_size + 1 + needs_cr + marker3_size;
+ } else {
+ memset(dest + size, '|', marker_size);
+ size += marker_size;
+ if (marker3_size) {
+ dest[size] = ' ';
+ memcpy(dest + size + 1, name3, marker3_size - 1);
+ size += marker3_size;
+ }
+ if (needs_cr)
+ dest[size++] = '\r';
+ dest[size++] = '\n';
+ }
+ size += xdl_orig_copy(xe1, m->i0, m->chg0, needs_cr, 1,
+ dest ? dest + size : NULL);
+ }
+
+ if (!dest) {
+ size += marker_size + 1 + needs_cr;
+ } else {
+ memset(dest + size, '=', marker_size);
+ size += marker_size;
+ if (needs_cr)
+ dest[size++] = '\r';
+ dest[size++] = '\n';
+ }
+
+ /* Postimage from side #2 */
+ size += xdl_recs_copy(xe2, m->i2, m->chg2, needs_cr, 1,
+ dest ? dest + size : NULL);
+ if (!dest) {
+ size += marker_size + 1 + needs_cr + marker2_size;
+ } else {
+ memset(dest + size, '>', marker_size);
+ size += marker_size;
+ if (marker2_size) {
+ dest[size] = ' ';
+ memcpy(dest + size + 1, name2, marker2_size - 1);
+ size += marker2_size;
+ }
+ if (needs_cr)
+ dest[size++] = '\r';
+ dest[size++] = '\n';
+ }
+ return size;
+}
+
+static int xdl_fill_merge_buffer(xdfenv_t *xe1, const char *name1,
+ xdfenv_t *xe2, const char *name2,
+ const char *ancestor_name,
+ int favor,
+ xdmerge_t *m, char *dest, int style,
+ int marker_size)
+{
+ int size, i;
+
+ for (size = i = 0; m; m = m->next) {
+ if (favor && !m->mode)
+ m->mode = favor;
+
+ if (m->mode == 0)
+ size = fill_conflict_hunk(xe1, name1, xe2, name2,
+ ancestor_name,
+ size, i, style, m, dest,
+ marker_size);
+ else if (m->mode & 3) {
+ /* Before conflicting part */
+ size += xdl_recs_copy(xe1, i, m->i1 - i, 0, 0,
+ dest ? dest + size : NULL);
+ /* Postimage from side #1 */
+ if (m->mode & 1) {
+ int needs_cr = is_cr_needed(xe1, xe2, m);
+
+ size += xdl_recs_copy(xe1, m->i1, m->chg1, needs_cr, (m->mode & 2),
+ dest ? dest + size : NULL);
+ }
+ /* Postimage from side #2 */
+ if (m->mode & 2)
+ size += xdl_recs_copy(xe2, m->i2, m->chg2, 0, 0,
+ dest ? dest + size : NULL);
+ } else
+ continue;
+ i = m->i1 + m->chg1;
+ }
+ size += xdl_recs_copy(xe1, i, xe1->xdf2.nrec - i, 0, 0,
+ dest ? dest + size : NULL);
+ return size;
+}
+
+static int recmatch(xrecord_t *rec1, xrecord_t *rec2, unsigned long flags)
+{
+ return xdl_recmatch(rec1->ptr, rec1->size,
+ rec2->ptr, rec2->size, flags);
+}
+
+/*
+ * Remove any common lines from the beginning and end of the conflicted region.
+ */
+static void xdl_refine_zdiff3_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,
+ xpparam_t const *xpp)
+{
+ xrecord_t **rec1 = xe1->xdf2.recs, **rec2 = xe2->xdf2.recs;
+ for (; m; m = m->next) {
+ /* let's handle just the conflicts */
+ if (m->mode)
+ continue;
+
+ while(m->chg1 && m->chg2 &&
+ recmatch(rec1[m->i1], rec2[m->i2], xpp->flags)) {
+ m->chg1--;
+ m->chg2--;
+ m->i1++;
+ m->i2++;
+ }
+ while (m->chg1 && m->chg2 &&
+ recmatch(rec1[m->i1 + m->chg1 - 1],
+ rec2[m->i2 + m->chg2 - 1], xpp->flags)) {
+ m->chg1--;
+ m->chg2--;
+ }
+ }
+}
+
+/*
+ * Sometimes, changes are not quite identical, but differ in only a few
+ * lines. Try hard to show only these few lines as conflicting.
+ */
+static int xdl_refine_conflicts(xdfenv_t *xe1, xdfenv_t *xe2, xdmerge_t *m,
+ xpparam_t const *xpp)
+{
+ for (; m; m = m->next) {
+ mmfile_t t1, t2;
+ xdfenv_t xe;
+ xdchange_t *xscr, *x;
+ int i1 = m->i1, i2 = m->i2;
+
+ /* let's handle just the conflicts */
+ if (m->mode)
+ continue;
+
+ /* no sense refining a conflict when one side is empty */
+ if (m->chg1 == 0 || m->chg2 == 0)
+ continue;
+
+ /*
+ * This probably does not work outside git, since
+ * we have a very simple mmfile structure.
+ */
+ t1.ptr = (char *)xe1->xdf2.recs[m->i1]->ptr;
+ t1.size = xe1->xdf2.recs[m->i1 + m->chg1 - 1]->ptr
+ + xe1->xdf2.recs[m->i1 + m->chg1 - 1]->size - t1.ptr;
+ t2.ptr = (char *)xe2->xdf2.recs[m->i2]->ptr;
+ t2.size = xe2->xdf2.recs[m->i2 + m->chg2 - 1]->ptr
+ + xe2->xdf2.recs[m->i2 + m->chg2 - 1]->size - t2.ptr;
+ if (xdl_do_diff(&t1, &t2, xpp, &xe) < 0)
+ return -1;
+ if (xdl_change_compact(&xe.xdf1, &xe.xdf2, xpp->flags) < 0 ||
+ xdl_change_compact(&xe.xdf2, &xe.xdf1, xpp->flags) < 0 ||
+ xdl_build_script(&xe, &xscr) < 0) {
+ xdl_free_env(&xe);
+ return -1;
+ }
+ if (!xscr) {
+ /* If this happens, the changes are identical. */
+ xdl_free_env(&xe);
+ m->mode = 4;
+ continue;
+ }
+ x = xscr;
+ m->i1 = xscr->i1 + i1;
+ m->chg1 = xscr->chg1;
+ m->i2 = xscr->i2 + i2;
+ m->chg2 = xscr->chg2;
+ while (xscr->next) {
+ xdmerge_t *m2 = xdl_malloc(sizeof(xdmerge_t));
+ if (!m2) {
+ xdl_free_env(&xe);
+ xdl_free_script(x);
+ return -1;
+ }
+ xscr = xscr->next;
+ m2->next = m->next;
+ m->next = m2;
+ m = m2;
+ m->mode = 0;
+ m->i1 = xscr->i1 + i1;
+ m->chg1 = xscr->chg1;
+ m->i2 = xscr->i2 + i2;
+ m->chg2 = xscr->chg2;
+ }
+ xdl_free_env(&xe);
+ xdl_free_script(x);
+ }
+ return 0;
+}
+
+static int line_contains_alnum(const char *ptr, long size)
+{
+ while (size--)
+ if (isalnum((unsigned char)*(ptr++)))
+ return 1;
+ return 0;
+}
+
+static int lines_contain_alnum(xdfenv_t *xe, int i, int chg)
+{
+ for (; chg; chg--, i++)
+ if (line_contains_alnum(xe->xdf2.recs[i]->ptr,
+ xe->xdf2.recs[i]->size))
+ return 1;
+ return 0;
+}
+
+/*
+ * This function merges m and m->next, marking everything between those hunks
+ * as conflicting, too.
+ */
+static void xdl_merge_two_conflicts(xdmerge_t *m)
+{
+ xdmerge_t *next_m = m->next;
+ m->chg1 = next_m->i1 + next_m->chg1 - m->i1;
+ m->chg2 = next_m->i2 + next_m->chg2 - m->i2;
+ m->next = next_m->next;
+ free(next_m);
+}
+
+/*
+ * If there are less than 3 non-conflicting lines between conflicts,
+ * it appears simpler -- because it takes up less (or as many) lines --
+ * if the lines are moved into the conflicts.
+ */
+static int xdl_simplify_non_conflicts(xdfenv_t *xe1, xdmerge_t *m,
+ int simplify_if_no_alnum)
+{
+ int result = 0;
+
+ if (!m)
+ return result;
+ for (;;) {
+ xdmerge_t *next_m = m->next;
+ int begin, end;
+
+ if (!next_m)
+ return result;
+
+ begin = m->i1 + m->chg1;
+ end = next_m->i1;
+
+ if (m->mode != 0 || next_m->mode != 0 ||
+ (end - begin > 3 &&
+ (!simplify_if_no_alnum ||
+ lines_contain_alnum(xe1, begin, end - begin)))) {
+ m = next_m;
+ } else {
+ result++;
+ xdl_merge_two_conflicts(m);
+ }
+ }
+}
+
+/*
+ * level == 0: mark all overlapping changes as conflict
+ * level == 1: mark overlapping changes as conflict only if not identical
+ * level == 2: analyze non-identical changes for minimal conflict set
+ * level == 3: analyze non-identical changes for minimal conflict set, but
+ * treat hunks not containing any letter or number as conflicting
+ *
+ * returns < 0 on error, == 0 for no conflicts, else number of conflicts
+ */
+static int xdl_do_merge(xdfenv_t *xe1, xdchange_t *xscr1,
+ xdfenv_t *xe2, xdchange_t *xscr2,
+ xmparam_t const *xmp, mmbuffer_t *result)
+{
+ xdmerge_t *changes, *c;
+ xpparam_t const *xpp = &xmp->xpp;
+ const char *const ancestor_name = xmp->ancestor;
+ const char *const name1 = xmp->file1;
+ const char *const name2 = xmp->file2;
+ int i0, i1, i2, chg0, chg1, chg2;
+ int level = xmp->level;
+ int style = xmp->style;
+ int favor = xmp->favor;
+
+ /*
+ * XDL_MERGE_DIFF3 does not attempt to refine conflicts by looking
+ * at common areas of sides 1 & 2, because the base (side 0) does
+ * not match and is being shown. Similarly, simplification of
+ * non-conflicts is also skipped due to the skipping of conflict
+ * refinement.
+ *
+ * XDL_MERGE_ZEALOUS_DIFF3, on the other hand, will attempt to
+ * refine conflicts looking for common areas of sides 1 & 2.
+ * However, since the base is being shown and does not match,
+ * it will only look for common areas at the beginning or end
+ * of the conflict block. Since XDL_MERGE_ZEALOUS_DIFF3's
+ * conflict refinement is much more limited in this fashion, the
+ * conflict simplification will be skipped.
+ */
+ if (style == XDL_MERGE_DIFF3 || style == XDL_MERGE_ZEALOUS_DIFF3) {
+ /*
+ * "diff3 -m" output does not make sense for anything
+ * more aggressive than XDL_MERGE_EAGER.
+ */
+ if (XDL_MERGE_EAGER < level)
+ level = XDL_MERGE_EAGER;
+ }
+
+ c = changes = NULL;
+
+ while (xscr1 && xscr2) {
+ if (!changes)
+ changes = c;
+ if (xscr1->i1 + xscr1->chg1 < xscr2->i1) {
+ i0 = xscr1->i1;
+ i1 = xscr1->i2;
+ i2 = xscr2->i2 - xscr2->i1 + xscr1->i1;
+ chg0 = xscr1->chg1;
+ chg1 = xscr1->chg2;
+ chg2 = xscr1->chg1;
+ if (xdl_append_merge(&c, 1,
+ i0, chg0, i1, chg1, i2, chg2)) {
+ xdl_cleanup_merge(changes);
+ return -1;
+ }
+ xscr1 = xscr1->next;
+ continue;
+ }
+ if (xscr2->i1 + xscr2->chg1 < xscr1->i1) {
+ i0 = xscr2->i1;
+ i1 = xscr1->i2 - xscr1->i1 + xscr2->i1;
+ i2 = xscr2->i2;
+ chg0 = xscr2->chg1;
+ chg1 = xscr2->chg1;
+ chg2 = xscr2->chg2;
+ if (xdl_append_merge(&c, 2,
+ i0, chg0, i1, chg1, i2, chg2)) {
+ xdl_cleanup_merge(changes);
+ return -1;
+ }
+ xscr2 = xscr2->next;
+ continue;
+ }
+ if (level == XDL_MERGE_MINIMAL || xscr1->i1 != xscr2->i1 ||
+ xscr1->chg1 != xscr2->chg1 ||
+ xscr1->chg2 != xscr2->chg2 ||
+ xdl_merge_cmp_lines(xe1, xscr1->i2,
+ xe2, xscr2->i2,
+ xscr1->chg2, xpp->flags)) {
+ /* conflict */
+ int off = xscr1->i1 - xscr2->i1;
+ int ffo = off + xscr1->chg1 - xscr2->chg1;
+
+ i0 = xscr1->i1;
+ i1 = xscr1->i2;
+ i2 = xscr2->i2;
+ if (off > 0) {
+ i0 -= off;
+ i1 -= off;
+ }
+ else
+ i2 += off;
+ chg0 = xscr1->i1 + xscr1->chg1 - i0;
+ chg1 = xscr1->i2 + xscr1->chg2 - i1;
+ chg2 = xscr2->i2 + xscr2->chg2 - i2;
+ if (ffo < 0) {
+ chg0 -= ffo;
+ chg1 -= ffo;
+ } else
+ chg2 += ffo;
+ if (xdl_append_merge(&c, 0,
+ i0, chg0, i1, chg1, i2, chg2)) {
+ xdl_cleanup_merge(changes);
+ return -1;
+ }
+ }
+
+ i1 = xscr1->i1 + xscr1->chg1;
+ i2 = xscr2->i1 + xscr2->chg1;
+
+ if (i1 >= i2)
+ xscr2 = xscr2->next;
+ if (i2 >= i1)
+ xscr1 = xscr1->next;
+ }
+ while (xscr1) {
+ if (!changes)
+ changes = c;
+ i0 = xscr1->i1;
+ i1 = xscr1->i2;
+ i2 = xscr1->i1 + xe2->xdf2.nrec - xe2->xdf1.nrec;
+ chg0 = xscr1->chg1;
+ chg1 = xscr1->chg2;
+ chg2 = xscr1->chg1;
+ if (xdl_append_merge(&c, 1,
+ i0, chg0, i1, chg1, i2, chg2)) {
+ xdl_cleanup_merge(changes);
+ return -1;
+ }
+ xscr1 = xscr1->next;
+ }
+ while (xscr2) {
+ if (!changes)
+ changes = c;
+ i0 = xscr2->i1;
+ i1 = xscr2->i1 + xe1->xdf2.nrec - xe1->xdf1.nrec;
+ i2 = xscr2->i2;
+ chg0 = xscr2->chg1;
+ chg1 = xscr2->chg1;
+ chg2 = xscr2->chg2;
+ if (xdl_append_merge(&c, 2,
+ i0, chg0, i1, chg1, i2, chg2)) {
+ xdl_cleanup_merge(changes);
+ return -1;
+ }
+ xscr2 = xscr2->next;
+ }
+ if (!changes)
+ changes = c;
+ /* refine conflicts */
+ if (style == XDL_MERGE_ZEALOUS_DIFF3) {
+ xdl_refine_zdiff3_conflicts(xe1, xe2, changes, xpp);
+ } else if (XDL_MERGE_ZEALOUS <= level &&
+ (xdl_refine_conflicts(xe1, xe2, changes, xpp) < 0 ||
+ xdl_simplify_non_conflicts(xe1, changes,
+ XDL_MERGE_ZEALOUS < level) < 0)) {
+ xdl_cleanup_merge(changes);
+ return -1;
+ }
+ /* output */
+ if (result) {
+ int marker_size = xmp->marker_size;
+ int size = xdl_fill_merge_buffer(xe1, name1, xe2, name2,
+ ancestor_name,
+ favor, changes, NULL, style,
+ marker_size);
+ result->ptr = xdl_malloc(size);
+ if (!result->ptr) {
+ xdl_cleanup_merge(changes);
+ return -1;
+ }
+ result->size = size;
+ xdl_fill_merge_buffer(xe1, name1, xe2, name2,
+ ancestor_name, favor, changes,
+ result->ptr, style, marker_size);
+ }
+ return xdl_cleanup_merge(changes);
+}
+
+int xdl_merge(mmfile_t *orig, mmfile_t *mf1, mmfile_t *mf2,
+ xmparam_t const *xmp, mmbuffer_t *result)
+{
+ xdchange_t *xscr1 = NULL, *xscr2 = NULL;
+ xdfenv_t xe1, xe2;
+ int status = -1;
+ xpparam_t const *xpp = &xmp->xpp;
+
+ result->ptr = NULL;
+ result->size = 0;
+
+ if (xdl_do_diff(orig, mf1, xpp, &xe1) < 0)
+ return -1;
+
+ if (xdl_do_diff(orig, mf2, xpp, &xe2) < 0)
+ goto free_xe1; /* avoid double free of xe2 */
+
+ if (xdl_change_compact(&xe1.xdf1, &xe1.xdf2, xpp->flags) < 0 ||
+ xdl_change_compact(&xe1.xdf2, &xe1.xdf1, xpp->flags) < 0 ||
+ xdl_build_script(&xe1, &xscr1) < 0)
+ goto out;
+
+ if (xdl_change_compact(&xe2.xdf1, &xe2.xdf2, xpp->flags) < 0 ||
+ xdl_change_compact(&xe2.xdf2, &xe2.xdf1, xpp->flags) < 0 ||
+ xdl_build_script(&xe2, &xscr2) < 0)
+ goto out;
+
+ if (!xscr1) {
+ result->ptr = xdl_malloc(mf2->size);
+ if (!result->ptr)
+ goto out;
+ status = 0;
+ memcpy(result->ptr, mf2->ptr, mf2->size);
+ result->size = mf2->size;
+ } else if (!xscr2) {
+ result->ptr = xdl_malloc(mf1->size);
+ if (!result->ptr)
+ goto out;
+ status = 0;
+ memcpy(result->ptr, mf1->ptr, mf1->size);
+ result->size = mf1->size;
+ } else {
+ status = xdl_do_merge(&xe1, xscr1,
+ &xe2, xscr2,
+ xmp, result);
+ }
+ out:
+ xdl_free_script(xscr1);
+ xdl_free_script(xscr2);
+
+ xdl_free_env(&xe2);
+ free_xe1:
+ xdl_free_env(&xe1);
+
+ return status;
+}
diff --git a/deps/xdiff/xpatience.c b/deps/xdiff/xpatience.c
new file mode 100644
index 0000000..a2d8955
--- /dev/null
+++ b/deps/xdiff/xpatience.c
@@ -0,0 +1,373 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003-2016 Davide Libenzi, Johannes E. Schindelin
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+#include "xinclude.h"
+
+/*
+ * The basic idea of patience diff is to find lines that are unique in
+ * both files. These are intuitively the ones that we want to see as
+ * common lines.
+ *
+ * The maximal ordered sequence of such line pairs (where ordered means
+ * that the order in the sequence agrees with the order of the lines in
+ * both files) naturally defines an initial set of common lines.
+ *
+ * Now, the algorithm tries to extend the set of common lines by growing
+ * the line ranges where the files have identical lines.
+ *
+ * Between those common lines, the patience diff algorithm is applied
+ * recursively, until no unique line pairs can be found; these line ranges
+ * are handled by the well-known Myers algorithm.
+ */
+
+#define NON_UNIQUE ULONG_MAX
+
+/*
+ * This is a hash mapping from line hash to line numbers in the first and
+ * second file.
+ */
+struct hashmap {
+ int nr, alloc;
+ struct entry {
+ unsigned long hash;
+ /*
+ * 0 = unused entry, 1 = first line, 2 = second, etc.
+ * line2 is NON_UNIQUE if the line is not unique
+ * in either the first or the second file.
+ */
+ unsigned long line1, line2;
+ /*
+ * "next" & "previous" are used for the longest common
+ * sequence;
+ * initially, "next" reflects only the order in file1.
+ */
+ struct entry *next, *previous;
+
+ /*
+ * If 1, this entry can serve as an anchor. See
+ * Documentation/diff-options.txt for more information.
+ */
+ unsigned anchor : 1;
+ } *entries, *first, *last;
+ /* were common records found? */
+ unsigned long has_matches;
+ xdfenv_t *env;
+ xpparam_t const *xpp;
+};
+
+static int is_anchor(xpparam_t const *xpp, const char *line)
+{
+ int i;
+ for (i = 0; i < xpp->anchors_nr; i++) {
+ if (!strncmp(line, xpp->anchors[i], strlen(xpp->anchors[i])))
+ return 1;
+ }
+ return 0;
+}
+
+/* The argument "pass" is 1 for the first file, 2 for the second. */
+static void insert_record(xpparam_t const *xpp, int line, struct hashmap *map,
+ int pass)
+{
+ xrecord_t **records = pass == 1 ?
+ map->env->xdf1.recs : map->env->xdf2.recs;
+ xrecord_t *record = records[line - 1];
+ /*
+ * After xdl_prepare_env() (or more precisely, due to
+ * xdl_classify_record()), the "ha" member of the records (AKA lines)
+ * is _not_ the hash anymore, but a linearized version of it. In
+ * other words, the "ha" member is guaranteed to start with 0 and
+ * the second record's ha can only be 0 or 1, etc.
+ *
+ * So we multiply ha by 2 in the hope that the hashing was
+ * "unique enough".
+ */
+ int index = (int)((record->ha << 1) % map->alloc);
+
+ while (map->entries[index].line1) {
+ if (map->entries[index].hash != record->ha) {
+ if (++index >= map->alloc)
+ index = 0;
+ continue;
+ }
+ if (pass == 2)
+ map->has_matches = 1;
+ if (pass == 1 || map->entries[index].line2)
+ map->entries[index].line2 = NON_UNIQUE;
+ else
+ map->entries[index].line2 = line;
+ return;
+ }
+ if (pass == 2)
+ return;
+ map->entries[index].line1 = line;
+ map->entries[index].hash = record->ha;
+ map->entries[index].anchor = is_anchor(xpp, map->env->xdf1.recs[line - 1]->ptr);
+ if (!map->first)
+ map->first = map->entries + index;
+ if (map->last) {
+ map->last->next = map->entries + index;
+ map->entries[index].previous = map->last;
+ }
+ map->last = map->entries + index;
+ map->nr++;
+}
+
+/*
+ * This function has to be called for each recursion into the inter-hunk
+ * parts, as previously non-unique lines can become unique when being
+ * restricted to a smaller part of the files.
+ *
+ * It is assumed that env has been prepared using xdl_prepare().
+ */
+static int fill_hashmap(xpparam_t const *xpp, xdfenv_t *env,
+ struct hashmap *result,
+ int line1, int count1, int line2, int count2)
+{
+ result->xpp = xpp;
+ result->env = env;
+
+ /* We know exactly how large we want the hash map */
+ result->alloc = count1 * 2;
+ if (!XDL_CALLOC_ARRAY(result->entries, result->alloc))
+ return -1;
+
+ /* First, fill with entries from the first file */
+ while (count1--)
+ insert_record(xpp, line1++, result, 1);
+
+ /* Then search for matches in the second file */
+ while (count2--)
+ insert_record(xpp, line2++, result, 2);
+
+ return 0;
+}
+
+/*
+ * Find the longest sequence with a smaller last element (meaning a smaller
+ * line2, as we construct the sequence with entries ordered by line1).
+ */
+static int binary_search(struct entry **sequence, int longest,
+ struct entry *entry)
+{
+ int left = -1, right = longest;
+
+ while (left + 1 < right) {
+ int middle = left + (right - left) / 2;
+ /* by construction, no two entries can be equal */
+ if (sequence[middle]->line2 > entry->line2)
+ right = middle;
+ else
+ left = middle;
+ }
+ /* return the index in "sequence", _not_ the sequence length */
+ return left;
+}
+
+/*
+ * The idea is to start with the list of common unique lines sorted by
+ * the order in file1. For each of these pairs, the longest (partial)
+ * sequence whose last element's line2 is smaller is determined.
+ *
+ * For efficiency, the sequences are kept in a list containing exactly one
+ * item per sequence length: the sequence with the smallest last
+ * element (in terms of line2).
+ */
+static int find_longest_common_sequence(struct hashmap *map, struct entry **res)
+{
+ struct entry **sequence;
+ int longest = 0, i;
+ struct entry *entry;
+
+ /*
+ * If not -1, this entry in sequence must never be overridden.
+ * Therefore, overriding entries before this has no effect, so
+ * do not do that either.
+ */
+ int anchor_i = -1;
+
+ if (!XDL_ALLOC_ARRAY(sequence, map->nr))
+ return -1;
+
+ for (entry = map->first; entry; entry = entry->next) {
+ if (!entry->line2 || entry->line2 == NON_UNIQUE)
+ continue;
+ i = binary_search(sequence, longest, entry);
+ entry->previous = i < 0 ? NULL : sequence[i];
+ ++i;
+ if (i <= anchor_i)
+ continue;
+ sequence[i] = entry;
+ if (entry->anchor) {
+ anchor_i = i;
+ longest = anchor_i + 1;
+ } else if (i == longest) {
+ longest++;
+ }
+ }
+
+ /* No common unique lines were found */
+ if (!longest) {
+ *res = NULL;
+ xdl_free(sequence);
+ return 0;
+ }
+
+ /* Iterate starting at the last element, adjusting the "next" members */
+ entry = sequence[longest - 1];
+ entry->next = NULL;
+ while (entry->previous) {
+ entry->previous->next = entry;
+ entry = entry->previous;
+ }
+ *res = entry;
+ xdl_free(sequence);
+ return 0;
+}
+
+static int match(struct hashmap *map, int line1, int line2)
+{
+ xrecord_t *record1 = map->env->xdf1.recs[line1 - 1];
+ xrecord_t *record2 = map->env->xdf2.recs[line2 - 1];
+ return record1->ha == record2->ha;
+}
+
+static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,
+ int line1, int count1, int line2, int count2);
+
+static int walk_common_sequence(struct hashmap *map, struct entry *first,
+ int line1, int count1, int line2, int count2)
+{
+ int end1 = line1 + count1, end2 = line2 + count2;
+ int next1, next2;
+
+ for (;;) {
+ /* Try to grow the line ranges of common lines */
+ if (first) {
+ next1 = first->line1;
+ next2 = first->line2;
+ while (next1 > line1 && next2 > line2 &&
+ match(map, next1 - 1, next2 - 1)) {
+ next1--;
+ next2--;
+ }
+ } else {
+ next1 = end1;
+ next2 = end2;
+ }
+ while (line1 < next1 && line2 < next2 &&
+ match(map, line1, line2)) {
+ line1++;
+ line2++;
+ }
+
+ /* Recurse */
+ if (next1 > line1 || next2 > line2) {
+ if (patience_diff(map->xpp, map->env,
+ line1, next1 - line1,
+ line2, next2 - line2))
+ return -1;
+ }
+
+ if (!first)
+ return 0;
+
+ while (first->next &&
+ first->next->line1 == first->line1 + 1 &&
+ first->next->line2 == first->line2 + 1)
+ first = first->next;
+
+ line1 = first->line1 + 1;
+ line2 = first->line2 + 1;
+
+ first = first->next;
+ }
+}
+
+static int fall_back_to_classic_diff(struct hashmap *map,
+ int line1, int count1, int line2, int count2)
+{
+ xpparam_t xpp;
+
+ memset(&xpp, 0, sizeof(xpp));
+ xpp.flags = map->xpp->flags & ~XDF_DIFF_ALGORITHM_MASK;
+
+ return xdl_fall_back_diff(map->env, &xpp,
+ line1, count1, line2, count2);
+}
+
+/*
+ * Recursively find the longest common sequence of unique lines,
+ * and if none was found, ask xdl_do_diff() to do the job.
+ *
+ * This function assumes that env was prepared with xdl_prepare_env().
+ */
+static int patience_diff(xpparam_t const *xpp, xdfenv_t *env,
+ int line1, int count1, int line2, int count2)
+{
+ struct hashmap map;
+ struct entry *first;
+ int result = 0;
+
+ /* trivial case: one side is empty */
+ if (!count1) {
+ while(count2--)
+ env->xdf2.rchg[line2++ - 1] = 1;
+ return 0;
+ } else if (!count2) {
+ while(count1--)
+ env->xdf1.rchg[line1++ - 1] = 1;
+ return 0;
+ }
+
+ memset(&map, 0, sizeof(map));
+ if (fill_hashmap(xpp, env, &map,
+ line1, count1, line2, count2))
+ return -1;
+
+ /* are there any matching lines at all? */
+ if (!map.has_matches) {
+ while(count1--)
+ env->xdf1.rchg[line1++ - 1] = 1;
+ while(count2--)
+ env->xdf2.rchg[line2++ - 1] = 1;
+ xdl_free(map.entries);
+ return 0;
+ }
+
+ result = find_longest_common_sequence(&map, &first);
+ if (result)
+ goto out;
+ if (first)
+ result = walk_common_sequence(&map, first,
+ line1, count1, line2, count2);
+ else
+ result = fall_back_to_classic_diff(&map,
+ line1, count1, line2, count2);
+ out:
+ xdl_free(map.entries);
+ return result;
+}
+
+int xdl_do_patience_diff(xpparam_t const *xpp, xdfenv_t *env)
+{
+ return patience_diff(xpp, env, 1, env->xdf1.nrec, 1, env->xdf2.nrec);
+}
diff --git a/deps/xdiff/xprepare.c b/deps/xdiff/xprepare.c
new file mode 100644
index 0000000..c84549f
--- /dev/null
+++ b/deps/xdiff/xprepare.c
@@ -0,0 +1,461 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#include "xinclude.h"
+
+
+#define XDL_KPDIS_RUN 4
+#define XDL_MAX_EQLIMIT 1024
+#define XDL_SIMSCAN_WINDOW 100
+#define XDL_GUESS_NLINES1 256
+#define XDL_GUESS_NLINES2 20
+
+
+typedef struct s_xdlclass {
+ struct s_xdlclass *next;
+ unsigned long ha;
+ char const *line;
+ long size;
+ long idx;
+ long len1, len2;
+} xdlclass_t;
+
+typedef struct s_xdlclassifier {
+ unsigned int hbits;
+ long hsize;
+ xdlclass_t **rchash;
+ chastore_t ncha;
+ xdlclass_t **rcrecs;
+ long alloc;
+ long count;
+ long flags;
+} xdlclassifier_t;
+
+
+
+
+static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags);
+static void xdl_free_classifier(xdlclassifier_t *cf);
+static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash,
+ unsigned int hbits, xrecord_t *rec);
+static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,
+ xdlclassifier_t *cf, xdfile_t *xdf);
+static void xdl_free_ctx(xdfile_t *xdf);
+static int xdl_clean_mmatch(char const *dis, long i, long s, long e);
+static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);
+static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2);
+static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2);
+
+
+
+
+static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags) {
+ cf->flags = flags;
+
+ cf->hbits = xdl_hashbits((unsigned int) size);
+ cf->hsize = 1 << cf->hbits;
+
+ if (xdl_cha_init(&cf->ncha, sizeof(xdlclass_t), size / 4 + 1) < 0) {
+
+ return -1;
+ }
+ if (!XDL_CALLOC_ARRAY(cf->rchash, cf->hsize)) {
+
+ xdl_cha_free(&cf->ncha);
+ return -1;
+ }
+
+ cf->alloc = size;
+ if (!XDL_ALLOC_ARRAY(cf->rcrecs, cf->alloc)) {
+
+ xdl_free(cf->rchash);
+ xdl_cha_free(&cf->ncha);
+ return -1;
+ }
+
+ cf->count = 0;
+
+ return 0;
+}
+
+
+static void xdl_free_classifier(xdlclassifier_t *cf) {
+
+ xdl_free(cf->rcrecs);
+ xdl_free(cf->rchash);
+ xdl_cha_free(&cf->ncha);
+}
+
+
+static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash,
+ unsigned int hbits, xrecord_t *rec) {
+ long hi;
+ char const *line;
+ xdlclass_t *rcrec;
+
+ line = rec->ptr;
+ hi = (long) XDL_HASHLONG(rec->ha, cf->hbits);
+ for (rcrec = cf->rchash[hi]; rcrec; rcrec = rcrec->next)
+ if (rcrec->ha == rec->ha &&
+ xdl_recmatch(rcrec->line, rcrec->size,
+ rec->ptr, rec->size, cf->flags))
+ break;
+
+ if (!rcrec) {
+ if (!(rcrec = xdl_cha_alloc(&cf->ncha))) {
+
+ return -1;
+ }
+ rcrec->idx = cf->count++;
+ if (XDL_ALLOC_GROW(cf->rcrecs, cf->count, cf->alloc))
+ return -1;
+ cf->rcrecs[rcrec->idx] = rcrec;
+ rcrec->line = line;
+ rcrec->size = rec->size;
+ rcrec->ha = rec->ha;
+ rcrec->len1 = rcrec->len2 = 0;
+ rcrec->next = cf->rchash[hi];
+ cf->rchash[hi] = rcrec;
+ }
+
+ (pass == 1) ? rcrec->len1++ : rcrec->len2++;
+
+ rec->ha = (unsigned long) rcrec->idx;
+
+ hi = (long) XDL_HASHLONG(rec->ha, hbits);
+ rec->next = rhash[hi];
+ rhash[hi] = rec;
+
+ return 0;
+}
+
+
+static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp,
+ xdlclassifier_t *cf, xdfile_t *xdf) {
+ unsigned int hbits;
+ long nrec, hsize, bsize;
+ unsigned long hav;
+ char const *blk, *cur, *top, *prev;
+ xrecord_t *crec;
+ xrecord_t **recs;
+ xrecord_t **rhash;
+ unsigned long *ha;
+ char *rchg;
+ long *rindex;
+
+ ha = NULL;
+ rindex = NULL;
+ rchg = NULL;
+ rhash = NULL;
+ recs = NULL;
+
+ if (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0)
+ goto abort;
+ if (!XDL_ALLOC_ARRAY(recs, narec))
+ goto abort;
+
+ hbits = xdl_hashbits((unsigned int) narec);
+ hsize = 1 << hbits;
+ if (!XDL_CALLOC_ARRAY(rhash, hsize))
+ goto abort;
+
+ nrec = 0;
+ if ((cur = blk = xdl_mmfile_first(mf, &bsize))) {
+ for (top = blk + bsize; cur < top; ) {
+ prev = cur;
+ hav = xdl_hash_record(&cur, top, xpp->flags);
+ if (XDL_ALLOC_GROW(recs, nrec + 1, narec))
+ goto abort;
+ if (!(crec = xdl_cha_alloc(&xdf->rcha)))
+ goto abort;
+ crec->ptr = prev;
+ crec->size = (long) (cur - prev);
+ crec->ha = hav;
+ recs[nrec++] = crec;
+ if (xdl_classify_record(pass, cf, rhash, hbits, crec) < 0)
+ goto abort;
+ }
+ }
+
+ if (!XDL_CALLOC_ARRAY(rchg, nrec + 2))
+ goto abort;
+
+ if ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&
+ (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF)) {
+ if (!XDL_ALLOC_ARRAY(rindex, nrec + 1))
+ goto abort;
+ if (!XDL_ALLOC_ARRAY(ha, nrec + 1))
+ goto abort;
+ }
+
+ xdf->nrec = nrec;
+ xdf->recs = recs;
+ xdf->hbits = hbits;
+ xdf->rhash = rhash;
+ xdf->rchg = rchg + 1;
+ xdf->rindex = rindex;
+ xdf->nreff = 0;
+ xdf->ha = ha;
+ xdf->dstart = 0;
+ xdf->dend = nrec - 1;
+
+ return 0;
+
+abort:
+ xdl_free(ha);
+ xdl_free(rindex);
+ xdl_free(rchg);
+ xdl_free(rhash);
+ xdl_free(recs);
+ xdl_cha_free(&xdf->rcha);
+ return -1;
+}
+
+
+static void xdl_free_ctx(xdfile_t *xdf) {
+
+ xdl_free(xdf->rhash);
+ xdl_free(xdf->rindex);
+ xdl_free(xdf->rchg - 1);
+ xdl_free(xdf->ha);
+ xdl_free(xdf->recs);
+ xdl_cha_free(&xdf->rcha);
+}
+
+
+int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+ xdfenv_t *xe) {
+ long enl1, enl2, sample;
+ xdlclassifier_t cf;
+
+ memset(&cf, 0, sizeof(cf));
+
+ /*
+ * For histogram diff, we can afford a smaller sample size and
+ * thus a poorer estimate of the number of lines, as the hash
+ * table (rhash) won't be filled up/grown. The number of lines
+ * (nrecs) will be updated correctly anyway by
+ * xdl_prepare_ctx().
+ */
+ sample = (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF
+ ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1);
+
+ enl1 = xdl_guess_lines(mf1, sample) + 1;
+ enl2 = xdl_guess_lines(mf2, sample) + 1;
+
+ if (xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0)
+ return -1;
+
+ if (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) {
+
+ xdl_free_classifier(&cf);
+ return -1;
+ }
+ if (xdl_prepare_ctx(2, mf2, enl2, xpp, &cf, &xe->xdf2) < 0) {
+
+ xdl_free_ctx(&xe->xdf1);
+ xdl_free_classifier(&cf);
+ return -1;
+ }
+
+ if ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) &&
+ (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) &&
+ xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) {
+
+ xdl_free_ctx(&xe->xdf2);
+ xdl_free_ctx(&xe->xdf1);
+ xdl_free_classifier(&cf);
+ return -1;
+ }
+
+ xdl_free_classifier(&cf);
+
+ return 0;
+}
+
+
+void xdl_free_env(xdfenv_t *xe) {
+
+ xdl_free_ctx(&xe->xdf2);
+ xdl_free_ctx(&xe->xdf1);
+}
+
+
+static int xdl_clean_mmatch(char const *dis, long i, long s, long e) {
+ long r, rdis0, rpdis0, rdis1, rpdis1;
+
+ /*
+ * Limits the window the is examined during the similar-lines
+ * scan. The loops below stops when dis[i - r] == 1 (line that
+ * has no match), but there are corner cases where the loop
+ * proceed all the way to the extremities by causing huge
+ * performance penalties in case of big files.
+ */
+ if (i - s > XDL_SIMSCAN_WINDOW)
+ s = i - XDL_SIMSCAN_WINDOW;
+ if (e - i > XDL_SIMSCAN_WINDOW)
+ e = i + XDL_SIMSCAN_WINDOW;
+
+ /*
+ * Scans the lines before 'i' to find a run of lines that either
+ * have no match (dis[j] == 0) or have multiple matches (dis[j] > 1).
+ * Note that we always call this function with dis[i] > 1, so the
+ * current line (i) is already a multimatch line.
+ */
+ for (r = 1, rdis0 = 0, rpdis0 = 1; (i - r) >= s; r++) {
+ if (!dis[i - r])
+ rdis0++;
+ else if (dis[i - r] == 2)
+ rpdis0++;
+ else
+ break;
+ }
+ /*
+ * If the run before the line 'i' found only multimatch lines, we
+ * return 0 and hence we don't make the current line (i) discarded.
+ * We want to discard multimatch lines only when they appear in the
+ * middle of runs with nomatch lines (dis[j] == 0).
+ */
+ if (rdis0 == 0)
+ return 0;
+ for (r = 1, rdis1 = 0, rpdis1 = 1; (i + r) <= e; r++) {
+ if (!dis[i + r])
+ rdis1++;
+ else if (dis[i + r] == 2)
+ rpdis1++;
+ else
+ break;
+ }
+ /*
+ * If the run after the line 'i' found only multimatch lines, we
+ * return 0 and hence we don't make the current line (i) discarded.
+ */
+ if (rdis1 == 0)
+ return 0;
+ rdis1 += rdis0;
+ rpdis1 += rpdis0;
+
+ return rpdis1 * XDL_KPDIS_RUN < (rpdis1 + rdis1);
+}
+
+
+/*
+ * Try to reduce the problem complexity, discard records that have no
+ * matches on the other file. Also, lines that have multiple matches
+ * might be potentially discarded if they happear in a run of discardable.
+ */
+static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {
+ long i, nm, nreff, mlim;
+ xrecord_t **recs;
+ xdlclass_t *rcrec;
+ char *dis, *dis1, *dis2;
+
+ if (!XDL_CALLOC_ARRAY(dis, xdf1->nrec + xdf2->nrec + 2))
+ return -1;
+ dis1 = dis;
+ dis2 = dis1 + xdf1->nrec + 1;
+
+ if ((mlim = xdl_bogosqrt(xdf1->nrec)) > XDL_MAX_EQLIMIT)
+ mlim = XDL_MAX_EQLIMIT;
+ for (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) {
+ rcrec = cf->rcrecs[(*recs)->ha];
+ nm = rcrec ? rcrec->len2 : 0;
+ dis1[i] = (nm == 0) ? 0: (nm >= mlim) ? 2: 1;
+ }
+
+ if ((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT)
+ mlim = XDL_MAX_EQLIMIT;
+ for (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) {
+ rcrec = cf->rcrecs[(*recs)->ha];
+ nm = rcrec ? rcrec->len1 : 0;
+ dis2[i] = (nm == 0) ? 0: (nm >= mlim) ? 2: 1;
+ }
+
+ for (nreff = 0, i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart];
+ i <= xdf1->dend; i++, recs++) {
+ if (dis1[i] == 1 ||
+ (dis1[i] == 2 && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) {
+ xdf1->rindex[nreff] = i;
+ xdf1->ha[nreff] = (*recs)->ha;
+ nreff++;
+ } else
+ xdf1->rchg[i] = 1;
+ }
+ xdf1->nreff = nreff;
+
+ for (nreff = 0, i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart];
+ i <= xdf2->dend; i++, recs++) {
+ if (dis2[i] == 1 ||
+ (dis2[i] == 2 && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) {
+ xdf2->rindex[nreff] = i;
+ xdf2->ha[nreff] = (*recs)->ha;
+ nreff++;
+ } else
+ xdf2->rchg[i] = 1;
+ }
+ xdf2->nreff = nreff;
+
+ xdl_free(dis);
+
+ return 0;
+}
+
+
+/*
+ * Early trim initial and terminal matching records.
+ */
+static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2) {
+ long i, lim;
+ xrecord_t **recs1, **recs2;
+
+ recs1 = xdf1->recs;
+ recs2 = xdf2->recs;
+ for (i = 0, lim = XDL_MIN(xdf1->nrec, xdf2->nrec); i < lim;
+ i++, recs1++, recs2++)
+ if ((*recs1)->ha != (*recs2)->ha)
+ break;
+
+ xdf1->dstart = xdf2->dstart = i;
+
+ recs1 = xdf1->recs + xdf1->nrec - 1;
+ recs2 = xdf2->recs + xdf2->nrec - 1;
+ for (lim -= i, i = 0; i < lim; i++, recs1--, recs2--)
+ if ((*recs1)->ha != (*recs2)->ha)
+ break;
+
+ xdf1->dend = xdf1->nrec - i - 1;
+ xdf2->dend = xdf2->nrec - i - 1;
+
+ return 0;
+}
+
+
+static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) {
+
+ if (xdl_trim_ends(xdf1, xdf2) < 0 ||
+ xdl_cleanup_records(cf, xdf1, xdf2) < 0) {
+
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/deps/xdiff/xprepare.h b/deps/xdiff/xprepare.h
new file mode 100644
index 0000000..947d9fc
--- /dev/null
+++ b/deps/xdiff/xprepare.h
@@ -0,0 +1,34 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#if !defined(XPREPARE_H)
+#define XPREPARE_H
+
+
+
+int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp,
+ xdfenv_t *xe);
+void xdl_free_env(xdfenv_t *xe);
+
+
+
+#endif /* #if !defined(XPREPARE_H) */
diff --git a/deps/xdiff/xtypes.h b/deps/xdiff/xtypes.h
new file mode 100644
index 0000000..8442bd4
--- /dev/null
+++ b/deps/xdiff/xtypes.h
@@ -0,0 +1,67 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#if !defined(XTYPES_H)
+#define XTYPES_H
+
+
+
+typedef struct s_chanode {
+ struct s_chanode *next;
+ long icurr;
+} chanode_t;
+
+typedef struct s_chastore {
+ chanode_t *head, *tail;
+ long isize, nsize;
+ chanode_t *ancur;
+ chanode_t *sncur;
+ long scurr;
+} chastore_t;
+
+typedef struct s_xrecord {
+ struct s_xrecord *next;
+ char const *ptr;
+ long size;
+ unsigned long ha;
+} xrecord_t;
+
+typedef struct s_xdfile {
+ chastore_t rcha;
+ long nrec;
+ unsigned int hbits;
+ xrecord_t **rhash;
+ long dstart, dend;
+ xrecord_t **recs;
+ char *rchg;
+ long *rindex;
+ long nreff;
+ unsigned long *ha;
+} xdfile_t;
+
+typedef struct s_xdfenv {
+ xdfile_t xdf1, xdf2;
+} xdfenv_t;
+
+
+
+#endif /* #if !defined(XTYPES_H) */
diff --git a/deps/xdiff/xutils.c b/deps/xdiff/xutils.c
new file mode 100644
index 0000000..9e36f24
--- /dev/null
+++ b/deps/xdiff/xutils.c
@@ -0,0 +1,451 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#include "xinclude.h"
+
+
+long xdl_bogosqrt(long n) {
+ long i;
+
+ /*
+ * Classical integer square root approximation using shifts.
+ */
+ for (i = 1; n > 0; n >>= 2)
+ i <<= 1;
+
+ return i;
+}
+
+
+int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize,
+ xdemitcb_t *ecb) {
+ int i = 2;
+ mmbuffer_t mb[3];
+
+ mb[0].ptr = (char *) pre;
+ mb[0].size = psize;
+ mb[1].ptr = (char *) rec;
+ mb[1].size = size;
+ if (size > 0 && rec[size - 1] != '\n') {
+ mb[2].ptr = (char *) "\n\\ No newline at end of file\n";
+ mb[2].size = strlen(mb[2].ptr);
+ i++;
+ }
+ if (ecb->out_line(ecb->priv, mb, i) < 0) {
+
+ return -1;
+ }
+
+ return 0;
+}
+
+void *xdl_mmfile_first(mmfile_t *mmf, long *size)
+{
+ *size = mmf->size;
+ return mmf->ptr;
+}
+
+
+long xdl_mmfile_size(mmfile_t *mmf)
+{
+ return mmf->size;
+}
+
+
+int xdl_cha_init(chastore_t *cha, long isize, long icount) {
+
+ cha->head = cha->tail = NULL;
+ cha->isize = isize;
+ cha->nsize = icount * isize;
+ cha->ancur = cha->sncur = NULL;
+ cha->scurr = 0;
+
+ return 0;
+}
+
+
+void xdl_cha_free(chastore_t *cha) {
+ chanode_t *cur, *tmp;
+
+ for (cur = cha->head; (tmp = cur) != NULL;) {
+ cur = cur->next;
+ xdl_free(tmp);
+ }
+}
+
+
+void *xdl_cha_alloc(chastore_t *cha) {
+ chanode_t *ancur;
+ void *data;
+
+ if (!(ancur = cha->ancur) || ancur->icurr == cha->nsize) {
+ if (!(ancur = (chanode_t *) xdl_malloc(sizeof(chanode_t) + cha->nsize))) {
+
+ return NULL;
+ }
+ ancur->icurr = 0;
+ ancur->next = NULL;
+ if (cha->tail)
+ cha->tail->next = ancur;
+ if (!cha->head)
+ cha->head = ancur;
+ cha->tail = ancur;
+ cha->ancur = ancur;
+ }
+
+ data = (char *) ancur + sizeof(chanode_t) + ancur->icurr;
+ ancur->icurr += cha->isize;
+
+ return data;
+}
+
+long xdl_guess_lines(mmfile_t *mf, long sample) {
+ long nl = 0, size, tsize = 0;
+ char const *data, *cur, *top;
+
+ if ((cur = data = xdl_mmfile_first(mf, &size))) {
+ for (top = data + size; nl < sample && cur < top; ) {
+ nl++;
+ if (!(cur = memchr(cur, '\n', top - cur)))
+ cur = top;
+ else
+ cur++;
+ }
+ tsize += (long) (cur - data);
+ }
+
+ if (nl && tsize)
+ nl = xdl_mmfile_size(mf) / (tsize / nl);
+
+ return nl + 1;
+}
+
+int xdl_blankline(const char *line, long size, long flags)
+{
+ long i;
+
+ if (!(flags & XDF_WHITESPACE_FLAGS))
+ return (size <= 1);
+
+ for (i = 0; i < size && XDL_ISSPACE(line[i]); i++)
+ ;
+
+ return (i == size);
+}
+
+/*
+ * Have we eaten everything on the line, except for an optional
+ * CR at the very end?
+ */
+static int ends_with_optional_cr(const char *l, long s, long i)
+{
+ int complete = s && l[s-1] == '\n';
+
+ if (complete)
+ s--;
+ if (s == i)
+ return 1;
+ /* do not ignore CR at the end of an incomplete line */
+ if (complete && s == i + 1 && l[i] == '\r')
+ return 1;
+ return 0;
+}
+
+int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags)
+{
+ int i1, i2;
+
+ if (s1 == s2 && !memcmp(l1, l2, s1))
+ return 1;
+ if (!(flags & XDF_WHITESPACE_FLAGS))
+ return 0;
+
+ i1 = 0;
+ i2 = 0;
+
+ /*
+ * -w matches everything that matches with -b, and -b in turn
+ * matches everything that matches with --ignore-space-at-eol,
+ * which in turn matches everything that matches with --ignore-cr-at-eol.
+ *
+ * Each flavor of ignoring needs different logic to skip whitespaces
+ * while we have both sides to compare.
+ */
+ if (flags & XDF_IGNORE_WHITESPACE) {
+ goto skip_ws;
+ while (i1 < s1 && i2 < s2) {
+ if (l1[i1++] != l2[i2++])
+ return 0;
+ skip_ws:
+ while (i1 < s1 && XDL_ISSPACE(l1[i1]))
+ i1++;
+ while (i2 < s2 && XDL_ISSPACE(l2[i2]))
+ i2++;
+ }
+ } else if (flags & XDF_IGNORE_WHITESPACE_CHANGE) {
+ while (i1 < s1 && i2 < s2) {
+ if (XDL_ISSPACE(l1[i1]) && XDL_ISSPACE(l2[i2])) {
+ /* Skip matching spaces and try again */
+ while (i1 < s1 && XDL_ISSPACE(l1[i1]))
+ i1++;
+ while (i2 < s2 && XDL_ISSPACE(l2[i2]))
+ i2++;
+ continue;
+ }
+ if (l1[i1++] != l2[i2++])
+ return 0;
+ }
+ } else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL) {
+ while (i1 < s1 && i2 < s2 && l1[i1] == l2[i2]) {
+ i1++;
+ i2++;
+ }
+ } else if (flags & XDF_IGNORE_CR_AT_EOL) {
+ /* Find the first difference and see how the line ends */
+ while (i1 < s1 && i2 < s2 && l1[i1] == l2[i2]) {
+ i1++;
+ i2++;
+ }
+ return (ends_with_optional_cr(l1, s1, i1) &&
+ ends_with_optional_cr(l2, s2, i2));
+ }
+
+ /*
+ * After running out of one side, the remaining side must have
+ * nothing but whitespace for the lines to match. Note that
+ * ignore-whitespace-at-eol case may break out of the loop
+ * while there still are characters remaining on both lines.
+ */
+ if (i1 < s1) {
+ while (i1 < s1 && XDL_ISSPACE(l1[i1]))
+ i1++;
+ if (s1 != i1)
+ return 0;
+ }
+ if (i2 < s2) {
+ while (i2 < s2 && XDL_ISSPACE(l2[i2]))
+ i2++;
+ return (s2 == i2);
+ }
+ return 1;
+}
+
+static unsigned long xdl_hash_record_with_whitespace(char const **data,
+ char const *top, long flags) {
+ unsigned long ha = 5381;
+ char const *ptr = *data;
+ int cr_at_eol_only = (flags & XDF_WHITESPACE_FLAGS) == XDF_IGNORE_CR_AT_EOL;
+
+ for (; ptr < top && *ptr != '\n'; ptr++) {
+ if (cr_at_eol_only) {
+ /* do not ignore CR at the end of an incomplete line */
+ if (*ptr == '\r' &&
+ (ptr + 1 < top && ptr[1] == '\n'))
+ continue;
+ }
+ else if (XDL_ISSPACE(*ptr)) {
+ const char *ptr2 = ptr;
+ int at_eol;
+ while (ptr + 1 < top && XDL_ISSPACE(ptr[1])
+ && ptr[1] != '\n')
+ ptr++;
+ at_eol = (top <= ptr + 1 || ptr[1] == '\n');
+ if (flags & XDF_IGNORE_WHITESPACE)
+ ; /* already handled */
+ else if (flags & XDF_IGNORE_WHITESPACE_CHANGE
+ && !at_eol) {
+ ha += (ha << 5);
+ ha ^= (unsigned long) ' ';
+ }
+ else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL
+ && !at_eol) {
+ while (ptr2 != ptr + 1) {
+ ha += (ha << 5);
+ ha ^= (unsigned long) *ptr2;
+ ptr2++;
+ }
+ }
+ continue;
+ }
+ ha += (ha << 5);
+ ha ^= (unsigned long) *ptr;
+ }
+ *data = ptr < top ? ptr + 1: ptr;
+
+ return ha;
+}
+
+unsigned long xdl_hash_record(char const **data, char const *top, long flags) {
+ unsigned long ha = 5381;
+ char const *ptr = *data;
+
+ if (flags & XDF_WHITESPACE_FLAGS)
+ return xdl_hash_record_with_whitespace(data, top, flags);
+
+ for (; ptr < top && *ptr != '\n'; ptr++) {
+ ha += (ha << 5);
+ ha ^= (unsigned long) *ptr;
+ }
+ *data = ptr < top ? ptr + 1: ptr;
+
+ return ha;
+}
+
+unsigned int xdl_hashbits(unsigned int size) {
+ unsigned int val = 1, bits = 0;
+
+ for (; val < size && bits < CHAR_BIT * sizeof(unsigned int); val <<= 1, bits++);
+ return bits ? bits: 1;
+}
+
+
+int xdl_num_out(char *out, long val) {
+ char *ptr, *str = out;
+ char buf[32];
+
+ ptr = buf + sizeof(buf) - 1;
+ *ptr = '\0';
+ if (val < 0) {
+ *--ptr = '-';
+ val = -val;
+ }
+ for (; val && ptr > buf; val /= 10)
+ *--ptr = "0123456789"[val % 10];
+ if (*ptr)
+ for (; *ptr; ptr++, str++)
+ *str = *ptr;
+ else
+ *str++ = '0';
+ *str = '\0';
+
+ return str - out;
+}
+
+static int xdl_format_hunk_hdr(long s1, long c1, long s2, long c2,
+ const char *func, long funclen,
+ xdemitcb_t *ecb) {
+ int nb = 0;
+ mmbuffer_t mb;
+ char buf[128];
+
+ memcpy(buf, "@@ -", 4);
+ nb += 4;
+
+ nb += xdl_num_out(buf + nb, c1 ? s1: s1 - 1);
+
+ if (c1 != 1) {
+ memcpy(buf + nb, ",", 1);
+ nb += 1;
+
+ nb += xdl_num_out(buf + nb, c1);
+ }
+
+ memcpy(buf + nb, " +", 2);
+ nb += 2;
+
+ nb += xdl_num_out(buf + nb, c2 ? s2: s2 - 1);
+
+ if (c2 != 1) {
+ memcpy(buf + nb, ",", 1);
+ nb += 1;
+
+ nb += xdl_num_out(buf + nb, c2);
+ }
+
+ memcpy(buf + nb, " @@", 3);
+ nb += 3;
+ if (func && funclen) {
+ buf[nb++] = ' ';
+ if (funclen > sizeof(buf) - nb - 1)
+ funclen = sizeof(buf) - nb - 1;
+ memcpy(buf + nb, func, funclen);
+ nb += funclen;
+ }
+ buf[nb++] = '\n';
+
+ mb.ptr = buf;
+ mb.size = nb;
+ if (ecb->out_line(ecb->priv, &mb, 1) < 0)
+ return -1;
+ return 0;
+}
+
+int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,
+ const char *func, long funclen,
+ xdemitcb_t *ecb) {
+ if (!ecb->out_hunk)
+ return xdl_format_hunk_hdr(s1, c1, s2, c2, func, funclen, ecb);
+ if (ecb->out_hunk(ecb->priv,
+ c1 ? s1 : s1 - 1, c1,
+ c2 ? s2 : s2 - 1, c2,
+ func, funclen) < 0)
+ return -1;
+ return 0;
+}
+
+int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,
+ int line1, int count1, int line2, int count2)
+{
+ /*
+ * This probably does not work outside Git, since
+ * we have a very simple mmfile structure.
+ *
+ * Note: ideally, we would reuse the prepared environment, but
+ * the libxdiff interface does not (yet) allow for diffing only
+ * ranges of lines instead of the whole files.
+ */
+ mmfile_t subfile1, subfile2;
+ xdfenv_t env;
+
+ subfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1]->ptr;
+ subfile1.size = diff_env->xdf1.recs[line1 + count1 - 2]->ptr +
+ diff_env->xdf1.recs[line1 + count1 - 2]->size - subfile1.ptr;
+ subfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1]->ptr;
+ subfile2.size = diff_env->xdf2.recs[line2 + count2 - 2]->ptr +
+ diff_env->xdf2.recs[line2 + count2 - 2]->size - subfile2.ptr;
+ if (xdl_do_diff(&subfile1, &subfile2, xpp, &env) < 0)
+ return -1;
+
+ memcpy(diff_env->xdf1.rchg + line1 - 1, env.xdf1.rchg, count1);
+ memcpy(diff_env->xdf2.rchg + line2 - 1, env.xdf2.rchg, count2);
+
+ xdl_free_env(&env);
+
+ return 0;
+}
+
+void* xdl_alloc_grow_helper(void *p, long nr, long *alloc, size_t size)
+{
+ void *tmp = NULL;
+ size_t n = ((LONG_MAX - 16) / 2 >= *alloc) ? 2 * *alloc + 16 : LONG_MAX;
+ if (nr > n)
+ n = nr;
+ if (SIZE_MAX / size >= n)
+ tmp = xdl_realloc(p, n * size);
+ if (tmp) {
+ *alloc = n;
+ } else {
+ xdl_free(p);
+ *alloc = 0;
+ }
+ return tmp;
+}
diff --git a/deps/xdiff/xutils.h b/deps/xdiff/xutils.h
new file mode 100644
index 0000000..fd0bba9
--- /dev/null
+++ b/deps/xdiff/xutils.h
@@ -0,0 +1,48 @@
+/*
+ * LibXDiff by Davide Libenzi ( File Differential Library )
+ * Copyright (C) 2003 Davide Libenzi
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * Davide Libenzi <davidel@xmailserver.org>
+ *
+ */
+
+#if !defined(XUTILS_H)
+#define XUTILS_H
+
+
+
+long xdl_bogosqrt(long n);
+int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize,
+ xdemitcb_t *ecb);
+int xdl_cha_init(chastore_t *cha, long isize, long icount);
+void xdl_cha_free(chastore_t *cha);
+void *xdl_cha_alloc(chastore_t *cha);
+long xdl_guess_lines(mmfile_t *mf, long sample);
+int xdl_blankline(const char *line, long size, long flags);
+int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags);
+unsigned long xdl_hash_record(char const **data, char const *top, long flags);
+unsigned int xdl_hashbits(unsigned int size);
+int xdl_num_out(char *out, long val);
+int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2,
+ const char *func, long funclen, xdemitcb_t *ecb);
+int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp,
+ int line1, int count1, int line2, int count2);
+
+/* Do not call this function, use XDL_ALLOC_GROW instead */
+void* xdl_alloc_grow_helper(void* p, long nr, long* alloc, size_t size);
+
+#endif /* #if !defined(XUTILS_H) */
diff --git a/docs/api-stability.md b/docs/api-stability.md
new file mode 100644
index 0000000..a02e864
--- /dev/null
+++ b/docs/api-stability.md
@@ -0,0 +1,63 @@
+The maintainers of the libgit2 project believe that having a stable API
+to program against is important for our users and the ecosystem - whether
+you're building against the libgit2 C APIs directly, creating a wrapper to
+a managed language, or programming against one of those managed wrappers
+like LibGit2Sharp or Rugged.
+
+Our API stability considerations are:
+
+* Our standard API is considered stable through a major release.
+
+ * We define our "standard API" to be anything included in the "git2.h"
+ header - in other words, anything defined in a header in the `git2`
+ directory.
+
+ * APIs will maintain their signature and will not be removed within a
+ major release, but new APIs may be added.
+
+ * Any APIs may be marked as deprecated within a major release, but will
+ not be removed until the next major release (at the earliest). You
+ may define `GIT_DEPRECATE_HARD` to produce compiler warnings if you
+ target these deprecated APIs.
+
+ * We consider API compatibility to be against the C APIs. That means
+ that we may use macros to keep API compatibility - for example, if we
+ rename a structure from `git_widget_options` to `git_foobar_options`
+ then we would `#define git_widget_options git_foobar_options` to retain
+ API compatibility. Note that this does _not_ provide ABI compatibility.
+
+* Our systems API is only considered stable through a _minor_ release.
+
+ * We define our "systems API" to be anything included in the `git2/sys`
+ directory. These are not "standard" APIs but are mechanisms to extend
+ libgit2 by adding new extensions - for example, a custom HTTPS transport,
+ TLS engine, or merge strategy.
+
+ * Additionally, the cmake options and the resulting constants that it
+ produces to be "systems API".
+
+ * Generally these mechanism are well defined and will not need significant
+ changes, but are considered a part of the library itself and may need
+
+ * Systems API changes will be noted specially within a release's changelog.
+
+* Our ABI is only considered stable through a _minor_ release.
+
+ * Our ABI consists of actual symbol names in the library, the function
+ signatures, and the actual layout of structures. These are only
+ stable within minor releases, they are not stable within major releases
+ (yet).
+
+ * Since many FFIs use ABIs directly (for example, .NET P/Invoke or Rust),
+ this instability is unfortunate.
+
+ * In a future major release, we will begin providing ABI stability
+ throughout the major release cycle.
+
+ * ABI changes will be noted specially within a release's changelog.
+
+* Point releases are _generally_ only for bugfixes, and generally do _not_
+ include new features. This means that point releases generally do _not_
+ include new APIs. Point releases will never break API, systems API or
+ ABI compatibility.
+
diff --git a/docs/buffers.md b/docs/buffers.md
new file mode 100644
index 0000000..2f2148b
--- /dev/null
+++ b/docs/buffers.md
@@ -0,0 +1,63 @@
+Memory allocation and ownership
+-------------------------------
+
+Any library needs to _take_ data from users, and then _return_ data to
+users. With some types this is simple - integer parameters and return
+types are trivial. But with more complex data types, things are more
+complicated. Even something seemingly simple, like a C string, requires
+discipline: we cannot simple return an allocated hunk of memory for
+callers to `free`, since some systems have multiple allocators and users
+cannot necessarily reason about the allocator used and which corresponding
+deallocation function to call to free the memory.
+
+## Objects
+
+Most types in libgit2 are "opaque" types, which we treat as "objects" (even
+though C is "not an object oriented language"). You may create an object -
+for example, with `git_odb_new`, or libgit2 may return you an object as an
+"out" parameter - for example, with `git_repository_open`. With any of
+these objects, you should call their corresponding `_free` function (for
+example, `git_odb_free` or `git_repository_free`) when you are done using
+them.
+
+## Structures
+
+libgit2 will often take _input_ as structures (for example, options
+structures like `git_merge_options`). Rarely, libgit2 will return data in
+a structure. This is typically used for simpler data types, like `git_buf`
+and `git_strarray`. Users should allocate the structure themselves (either
+on the stack or the heap) and pass a pointer to it. Since libgit2 does not
+allocate the structure itself, only the data inside of it, the deallocation
+functions are suffixed with `_dispose` instead of `_free`, since they only
+free the data _inside_ the structure.
+
+## Strings or continuous memory buffers (`git_buf`)
+
+libgit2 typically _takes_ NUL-terminated strings ("C strings") with a
+`const char *`, and typically _takes_ raw data with a `const char *` and a
+corresponding `size_t` for its size. libgit2 typically _returns_ strings
+or raw data in a `git_buf` structure. The given data buffer will always be
+NUL terminated (even if it contains binary data) and the `size` member will
+always contain the size (in bytes) of the contents of the pointer (excluding
+the NUL terminator).
+
+In other words, if a `git_buf` contains the string `foo` then the memory
+buffer will be { `f`, `o`, `o`, `\0` } and the size will be `3`.
+
+Callers _must_ initialize the buffer with `GIT_BUF_INIT` (or by setting
+all the members to `0`) when it is created, before passing a pointer
+to the buffer to libgit2 for the first time.
+
+Subsequent calls to libgit2 APIs that take a buffer can re-use a
+buffer that was previously used. The buffer will be cleared and grown
+to accommodate the new contents (if necessary). The new data will
+written into the buffer, overwriting the previous contents. This
+allows callers to reduce the number of allocations performed by the
+library.
+
+Callers must call `git_buf_dispose` when they have finished.
+
+Note that the deprecated `git_diff_format_email` API does not follow
+this behavior; subsequent calls will concatenate data to the buffer
+instead of rewriting it. Users should move to the new `git_email`
+APIs that follow the `git_buf` standards.
diff --git a/docs/changelog.md b/docs/changelog.md
new file mode 100644
index 0000000..1748309
--- /dev/null
+++ b/docs/changelog.md
@@ -0,0 +1,2450 @@
+v1.7.2
+------
+
+## What's Changed
+
+This release fixes three bugs that can cause undefined behavior when given well-crafted inputs, either in input files or over network connections. These bugs may be able to be leveraged to cause denial of service attacks or unauthorized code execution.
+
+Two of these issues were discovered and reported by security engineers at Amazon Web Services. We thank the AWS Security team for their efforts to identify these issues, provide helpful reproduction cases, and responsibly disclose their findings.
+
+### Security fixes
+
+* transport: safely handle messages with no caps
+* revparse: fix parsing bug for trailing `@`
+* index: correct index has_dir_name check
+
+**Full Changelog**: https://github.com/libgit2/libgit2/compare/v1.7.1...v1.7.2
+
+v1.7.1
+------
+
+## What's Changed
+
+### Bug fixes
+
+* proxy: Return an error for invalid proxy URLs instead of crashing. by @lrm29 in https://github.com/libgit2/libgit2/pull/6597
+* ssh: fix known_hosts leak in _git_ssh_setup_conn by @steven9724 in https://github.com/libgit2/libgit2/pull/6599
+* repository: make cleanup safe for re-use with grafts by @carlosmn in https://github.com/libgit2/libgit2/pull/6600
+* fix: Add missing include for oidarray. by @dvzrv in https://github.com/libgit2/libgit2/pull/6608
+* Revert "CMake: Search for ssh2 instead of libssh2." by @ethomson in https://github.com/libgit2/libgit2/pull/6619
+
+### Compatibility improvements
+
+* stransport: macOS: replace errSSLNetworkTimeout, with hard-coded value by @mascguy in https://github.com/libgit2/libgit2/pull/6610
+
+## New Contributors
+* @dvzrv made their first contribution in https://github.com/libgit2/libgit2/pull/6608
+* @steven9724 made their first contribution in https://github.com/libgit2/libgit2/pull/6599
+
+**Full Changelog**: https://github.com/libgit2/libgit2/compare/v1.7.0...v1.7.1
+
+v1.7
+----
+
+This is release v1.7.0, "Kleine Raupe Nimmersatt". This release adds
+shallow clone support, completes the experimental SHA256 support,
+adds Schannel support for Windows, and includes many other new
+features and bugfixes.
+
+## Major changes
+
+* **Shallow clone support**
+ libgit2 now supports shallow clone and shallow repositories, thanks
+ to a significant investment from many community members -- hundreds
+ of commits by many contributors.
+
+ * Shallow (#6396) with some fixes from review by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6557
+ * Shallow Clone Support by @lya001 in
+ https://github.com/libgit2/libgit2/pull/6396
+ * Shallow support v2 by @pks-t in
+ https://github.com/libgit2/libgit2/pull/5254
+
+* **SHA256 support**
+ libgit2 should now support SHA256 repositories using the
+ `extensions.objectFormat` configuration option when the library is
+ built with `EXPERIMENTAL_SHA256=ON`. Users are encouraged to begin
+ testing their applications with this option and provide bug reports
+ and feedback. This _is_ a breaking API change; SHA256 support will
+ be enabled by default in libgit2 v2.0.
+
+ * sha256: less hardcoded SHA1 types and lengths by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6549
+ * Support SHA256 in git_repository_wrap_odb by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6556
+
+* **Schannel and SSPI for Windows**
+ libgit2 now supports the Windows Schannel and SSPI APIs for HTTPS
+ support on Windows, when configured with `USE_HTTPS=Schannel`.
+ Setting this option will not use the existing WinHTTP support, but
+ will use libgit2's standard HTTP client stack with Windows TLS
+ primitives. Windows users are encouraged to begin testing their
+ applications with this option and provide bug reports and feedback.
+ This will be enabled by default in a future version of libgit2.
+
+ * Introduce Schannel and SSPI for Windows by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6533
+
+## Breaking changes
+
+* **Simplify custom pluggable allocator** (System API / ABI breaking change)
+ The `git_allocator` structure (configurable by the
+ `GIT_OPT_SET_ALLOCATOR` option) now only contains `gmalloc`,
+ `grealloc` and `gfree` members. This simplifies both the work needed
+ by an implementer _and_ allows more flexibility and correctness in
+ libgit2 itself, especially during out-of-memory situations and
+ errors during bootstrapping.
+
+ * tests: add allocator with limited number of bytes by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6563
+
+## Other changes
+
+### New features
+* repo: honor environment variables for more scenarios by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6544
+* Introduce timeouts on sockets by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6535
+
+### Performance improvements
+* midx: do not try to look at every object in the index by @carlosmn in
+ https://github.com/libgit2/libgit2/pull/6585
+* Partial fix for #6532: insert-by-date order. by @arroz in
+ https://github.com/libgit2/libgit2/pull/6539
+
+### Bug fixes
+* repo: don't allow repeated extensions by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6505
+* config: return `GIT_ENOTFOUND` for missing programdata by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6547
+* Fix missing oid type for "fake" repositories by @oreiche in
+ https://github.com/libgit2/libgit2/pull/6554
+* Thread-local storage: handle failure cases by @ethomson in
+ https://github.com/libgit2/libgit2/pull/5722
+* midx: allow unknown chunk ids in multi-pack index files by @carlosmn in
+ https://github.com/libgit2/libgit2/pull/6583
+* pack: cast the number of objects to size_t by @carlosmn in
+ https://github.com/libgit2/libgit2/pull/6584
+* Fixes #6344: git_branch_move now renames the reflog instead of deleting
+ by @arroz in https://github.com/libgit2/libgit2/pull/6345
+* #6576 git_diff_index_to_workdir reverse now loads untracked content by
+ @arroz in https://github.com/libgit2/libgit2/pull/6577
+
+### Build and CI improvements
+* meta: the main branch is now v1.7.0 by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6516
+* xdiff: move xdiff to 'deps' by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6482
+* util: detect all possible qsort_r and qsort_s variants by
+ @DimitryAndric in https://github.com/libgit2/libgit2/pull/6555
+* Work around -Werror problems when detecting qsort variants by
+ @DimitryAndric in https://github.com/libgit2/libgit2/pull/6558
+* actions: simplify execution with composite action by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6488
+* CMake: Search for ssh2 instead of libssh2. by @Faless in
+ https://github.com/libgit2/libgit2/pull/6586
+
+### Documentation improvements
+* docs: fix IRC server from freenode to libera by @vincenzopalazzo in
+ https://github.com/libgit2/libgit2/pull/6590
+
+### Dependency upgrades
+* Update xdiff to git 2.40.1's version by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6561
+* deps: update pcre to 8.45 by @ethomson in
+ https://github.com/libgit2/libgit2/pull/6593
+
+## New Contributors
+* @oreiche made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6554
+* @DimitryAndric made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6555
+* @vincenzopalazzo made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6590
+* @Faless made their first contribution in
+ https://github.com/libgit2/libgit2/pull/6586
+
+**Full Changelog**: https://github.com/libgit2/libgit2/compare/v1.6.3...v1.7.0
+
+v1.6.3
+------
+
+## What's Changed
+
+### Bug fixes
+
+* odb: restore `git_odb_open` by @ethomson in https://github.com/libgit2/libgit2/pull/6520
+* Ensure that `git_index_add_all` handles ignored directories by @ethomson in https://github.com/libgit2/libgit2/pull/6521
+* pack: use 64 bits for the number of objects by @carlosmn in https://github.com/libgit2/libgit2/pull/6530
+
+### Build and CI improvements
+
+* Remove unused wditer variable by @georgthegreat in https://github.com/libgit2/libgit2/pull/6518
+* fs_path: let root run the ownership tests by @ethomson in https://github.com/libgit2/libgit2/pull/6513
+* sysdir: Do not declare win32 functions on non-win32 platforms by @Batchyx in https://github.com/libgit2/libgit2/pull/6527
+* cmake: don't include `include/git2` by @ethomson in https://github.com/libgit2/libgit2/pull/6529
+
+## New Contributors
+* @georgthegreat made their first contribution in https://github.com/libgit2/libgit2/pull/6518
+
+**Full Changelog**: https://github.com/libgit2/libgit2/compare/v1.6.2...v1.6.3
+
+v1.6.2
+------
+
+## What's Changed
+### Bug fixes
+
+* remote: always populate old id in update tips by @ethomson in https://github.com/libgit2/libgit2/pull/6506
+ The update tips callback would not always be properly provided with an empty (`0000000...`) OID for new refs.
+
+* Revert #6503 by @ethomson in https://github.com/libgit2/libgit2/pull/6511
+ The certificate callback added port information for callbacks in #6503, but the format was ambiguous with IPv6 addresses. Revert this change temporarily.
+
+* Add `git_odb_backend_loose` back by @ethomson in https://github.com/libgit2/libgit2/pull/6512
+ During SHA256 refactoring, the `git_odb_backend_loose` API was accidentally removed. Add it back.
+
+* meta: configure pkg-config .pc correctly by @ethomson in https://github.com/libgit2/libgit2/pull/6514
+ During SHA256 refactoring, the pkg-config `.pc` file was erroneously renamed to `git2` instead of `libgit2`. Repair this.
+
+**Full Changelog**: https://github.com/libgit2/libgit2/compare/v1.6.1...v1.6.2
+
+v1.6
+----
+
+This is release v1.6.1, "Hubbeliges Krokodil". This release adds experimental SHA256 support and includes many new features and bugfixes. This release replaces libgit2 v1.6.0, which did not correctly update its version number(s).
+
+## What's Changed
+
+### New features
+
+* **Support for bare repositories with SHA256 support (experimental)** by @ethomson in https://github.com/libgit2/libgit2/pull/6191
+ You can configure experimental SHA256 support in libgit2 with `cmake -DEXPERIMENTAL_SHA256=ON` during project setup. This is useful for considering future integrations, work on clients, and work on language bindings. At present, working with bare repositories should largely work, including remote operations. But many pieces of functionality - including working with the index - are not yet supported. As a result, **libgit2 with SHA256 support should not be used in production or released with package distribution.**
+
+* **Support the notion of a home directory separately from global configuration directory** by @ethomson in https://github.com/libgit2/libgit2/pull/6455 and https://github.com/libgit2/libgit2/pull/6456
+ Callers and language bindings can now configure the home directory that libgit2 uses for file lookups (eg, the `.ssh` directory). This configuration is separate from the git global configuration path.
+
+* **stash: partial stash specific files** by @gitkraken-jacobw in https://github.com/libgit2/libgit2/pull/6330
+ A stash can be created with only specific files, using a pathspec. This is similar to the `git stash push` command.
+
+* **push: revparse refspec source, so you can push things that are not refs** by @sven-of-cord in https://github.com/libgit2/libgit2/pull/6362
+ Pushes can be performed using refspecs instead of only references.
+
+* **Support OpenSSL3** by @ethomson in https://github.com/libgit2/libgit2/pull/6464 and https://github.com/libgit2/libgit2/pull/6471
+ OpenSSL 3 is now supported, both when compiled directly and dynamically loaded.
+
+### Bug fixes
+* winhttp: support long custom headers by @kcsaul in https://github.com/libgit2/libgit2/pull/6363
+* Fix memory leak by @csware in https://github.com/libgit2/libgit2/pull/6382
+* Don't fail the whole clone if you can't find a default branch by @torvalds in https://github.com/libgit2/libgit2/pull/6369
+* #6366: When a worktree is missing, return `GIT_ENOTFOUND`. by @arroz in https://github.com/libgit2/libgit2/pull/6395
+* commit-graph: only verify csum on `git_commit_graph_open()`. by @derrickstolee in https://github.com/libgit2/libgit2/pull/6420
+* Ignore missing 'safe.directory' config during ownership checks by @kcsaul in https://github.com/libgit2/libgit2/pull/6408
+* Fix leak in `git_tag_create_from_buffer` by @julianmesa-gitkraken in https://github.com/libgit2/libgit2/pull/6421
+* http: Update httpclient options when reusing an existing connection. by @slackner in https://github.com/libgit2/libgit2/pull/6416
+* Add support for `safe.directory *` by @csware in https://github.com/libgit2/libgit2/pull/6429
+* URL parsing for google-compatible URLs by @ethomson in https://github.com/libgit2/libgit2/pull/6326
+* Fixes #6433: `git_submodule_update` fails to update configured but missing submodule by @tagesuhu in https://github.com/libgit2/libgit2/pull/6434
+* transport: fix capabilities calculation by @russell in https://github.com/libgit2/libgit2/pull/6435
+* push: use resolved oid as the source by @ethomson in https://github.com/libgit2/libgit2/pull/6452
+* Use `git_clone__submodule` to avoid file checks in workdir by @abizjak in https://github.com/libgit2/libgit2/pull/6444
+* #6422: handle dangling symbolic refs gracefully by @arroz in https://github.com/libgit2/libgit2/pull/6423
+* `diff_file`: Fix crash when freeing a patch representing an empty untracked file by @jorio in https://github.com/libgit2/libgit2/pull/6475
+* clone: clean up options on failure by @ethomson in https://github.com/libgit2/libgit2/pull/6479
+* stash: update strarray usage by @ethomson in https://github.com/libgit2/libgit2/pull/6487
+* #6491: Sets `oid_type` on repos open with `git_repository_open_bare` by @arroz in https://github.com/libgit2/libgit2/pull/6492
+* Handle Win32 shares by @ethomson in https://github.com/libgit2/libgit2/pull/6493
+* Make failure to connect to ssh-agent non-fatal by @fxcoudert in https://github.com/libgit2/libgit2/pull/6497
+* odb: don't unconditionally add `oid_type` to stream by @ethomson in https://github.com/libgit2/libgit2/pull/6499
+* Pass hostkey & port to host verify callback by @fxcoudert in https://github.com/libgit2/libgit2/pull/6503
+
+### Code cleanups
+* meta: update version number to v1.6.0-alpha by @ethomson in https://github.com/libgit2/libgit2/pull/6352
+* sha256: indirection for experimental functions by @ethomson in https://github.com/libgit2/libgit2/pull/6354
+* Delete `create.c.bak` by @lrm29 in https://github.com/libgit2/libgit2/pull/6398
+* Support non-cmake builds with an in-tree `experimental.h` by @ethomson in https://github.com/libgit2/libgit2/pull/6405
+
+### Build and CI improvements
+* tests: skip flaky-ass googlesource tests by @ethomson in https://github.com/libgit2/libgit2/pull/6353
+* clar: remove ftrunacte from libgit2 tests by @boretrk in https://github.com/libgit2/libgit2/pull/6357
+* CI Improvements by @ethomson in https://github.com/libgit2/libgit2/pull/6403
+* fix compile on Windows with `-DWIN32_LEAN_AND_MEAN` by @christoph-cullmann in https://github.com/libgit2/libgit2/pull/6373
+* Fixes #6365 : Uppercase windows.h include fails build in case-sensitive OS by @Vinz2008 in https://github.com/libgit2/libgit2/pull/6377
+* ci: update version numbers of actions by @ethomson in https://github.com/libgit2/libgit2/pull/6448
+* thread: avoid warnings when building without threads by @ethomson in https://github.com/libgit2/libgit2/pull/6432
+* src: hide unused hmac() prototype by @0-wiz-0 in https://github.com/libgit2/libgit2/pull/6458
+* tests: update clar test runner by @ethomson in https://github.com/libgit2/libgit2/pull/6459
+* ci: always create test summaries, even on failure by @ethomson in https://github.com/libgit2/libgit2/pull/6460
+* Fix build failure with `-DEMBED_SSH_PATH` by @vicr123 in https://github.com/libgit2/libgit2/pull/6374
+* Define correct `off64_t` for AIX by @bzEq in https://github.com/libgit2/libgit2/pull/6376
+* Fix some warnings in main by @ethomson in https://github.com/libgit2/libgit2/pull/6480
+* strarray: remove deprecated declaration by @ethomson in https://github.com/libgit2/libgit2/pull/6486
+* tests: always unset `HTTP_PROXY` before starting tests by @ethomson in https://github.com/libgit2/libgit2/pull/6498
+
+### Documentation improvements
+* add 2-clause BSD license to COPYING by @martinvonz in https://github.com/libgit2/libgit2/pull/6413
+* Add new PHP bindings project to language bindings section of README.md by @RogerGee in https://github.com/libgit2/libgit2/pull/6473
+* README: clarify the linking exception by @ethomson in https://github.com/libgit2/libgit2/pull/6494
+* Correct the definition of "empty" in the docs for `git_repository_is_empty` by @timrogers in https://github.com/libgit2/libgit2/pull/6500
+
+## New Contributors
+* @christoph-cullmann made their first contribution in https://github.com/libgit2/libgit2/pull/6373
+* @Vinz2008 made their first contribution in https://github.com/libgit2/libgit2/pull/6377
+* @torvalds made their first contribution in https://github.com/libgit2/libgit2/pull/6369
+* @derrickstolee made their first contribution in https://github.com/libgit2/libgit2/pull/6420
+* @julianmesa-gitkraken made their first contribution in https://github.com/libgit2/libgit2/pull/6421
+* @slackner made their first contribution in https://github.com/libgit2/libgit2/pull/6416
+* @martinvonz made their first contribution in https://github.com/libgit2/libgit2/pull/6413
+* @tagesuhu made their first contribution in https://github.com/libgit2/libgit2/pull/6434
+* @russell made their first contribution in https://github.com/libgit2/libgit2/pull/6435
+* @sven-of-cord made their first contribution in https://github.com/libgit2/libgit2/pull/6362
+* @0-wiz-0 made their first contribution in https://github.com/libgit2/libgit2/pull/6458
+* @abizjak made their first contribution in https://github.com/libgit2/libgit2/pull/6444
+* @vicr123 made their first contribution in https://github.com/libgit2/libgit2/pull/6374
+* @bzEq made their first contribution in https://github.com/libgit2/libgit2/pull/6376
+* @gitkraken-jacobw made their first contribution in https://github.com/libgit2/libgit2/pull/6330
+* @fxcoudert made their first contribution in https://github.com/libgit2/libgit2/pull/6497
+* @timrogers made their first contribution in https://github.com/libgit2/libgit2/pull/6500
+
+**Full Changelog**: https://github.com/libgit2/libgit2/compare/v1.5.0...v1.6.0
+
+v1.5
+----
+
+This is release v1.5.0, "Stubentiger". This release adds the basis for an experimental CLI, continues preparing for SHA256 support, adds a benchmarking utility, and has numerous new features and bugfixes.
+
+## What's Changed
+### New features
+* The beginnings of a git-compatible CLI for testing and benchmarking by @ethomson in https://github.com/libgit2/libgit2/pull/6133
+* Add `clone` support to the CLI @ethomson in https://github.com/libgit2/libgit2/pull/6274
+* A benchmarking suite to compare libgit2 functionality against git by @ethomson in https://github.com/libgit2/libgit2/pull/6235
+* SHA256: add a SHA256 implementation backend by @ethomson in https://github.com/libgit2/libgit2/pull/6144
+* SHA256: support dynamically loaded openssl by @ethomson in https://github.com/libgit2/libgit2/pull/6258
+* Transport: introduce `git_transport_smart_remote_connect_options` by @lhchavez in https://github.com/libgit2/libgit2/pull/6278
+### Bug fixes
+* Free parent and ref in lg2_commit before returning. by @apnadkarni in https://github.com/libgit2/libgit2/pull/6219
+* xdiff: use xdl_free not free by @ethomson in https://github.com/libgit2/libgit2/pull/6223
+* remote: do store the update_tips callback error value by @carlosmn in https://github.com/libgit2/libgit2/pull/6226
+* win32: `find_system_dirs` does not return `GIT_ENOTFOUND` by @ethomson in https://github.com/libgit2/libgit2/pull/6228
+* Some minor fixes for issues discovered by coverity by @ethomson in https://github.com/libgit2/libgit2/pull/6238
+* Fix a string concatenation bug when validating extensions by @bierbaum in https://github.com/libgit2/libgit2/pull/6246
+* fetch: support OID refspec without dst by @ethomson in https://github.com/libgit2/libgit2/pull/6251
+* Fix crash when regenerating a patch with unquoted spaces in filename by @jorio in https://github.com/libgit2/libgit2/pull/6244
+* midx: Fix an undefined behavior (left-shift signed overflow) by @lhchavez in https://github.com/libgit2/libgit2/pull/6260
+* Validate repository directory ownership by @ethomson in https://github.com/libgit2/libgit2/pull/6266
+* midx: fix large offset table check. by @ccstolley in https://github.com/libgit2/libgit2/pull/6309
+* midx: do not verify the checksum on load by @carlosmn in https://github.com/libgit2/libgit2/pull/6291
+* revparse: Remove error-prone, redundant test by @dongcarl in https://github.com/libgit2/libgit2/pull/6299
+* refs: fix missing error message by @zawata in https://github.com/libgit2/libgit2/pull/6305
+* CLI: progress updates by @ethomson in https://github.com/libgit2/libgit2/pull/6319
+* A couple of simplications around mwindow by @carlosmn in https://github.com/libgit2/libgit2/pull/6288
+* config: update config entry iteration lifecycle by @ethomson in https://github.com/libgit2/libgit2/pull/6320
+* repo: allow administrator to own the configuration by @ethomson in https://github.com/libgit2/libgit2/pull/6321
+* filter: Fix Segfault by @zawata in https://github.com/libgit2/libgit2/pull/6303
+* ntlmclient: LibreSSL 3.5 removed HMAC_CTX_cleanup by @vishwin in https://github.com/libgit2/libgit2/pull/6340
+* Fix internal git_sysdir_find* function usage within public git_config_find* functions by @kcsaul in https://github.com/libgit2/libgit2/pull/6335
+* fix interactive rebase detect. by @i-tengfei in https://github.com/libgit2/libgit2/pull/6334
+* cmake: drop posix dependency from pcre* detection by @jpalus in https://github.com/libgit2/libgit2/pull/6333
+* Fix erroneously lax configuration ownership checks by @ethomson in https://github.com/libgit2/libgit2/pull/6341
+* pack: don't pretend we support pack files v3 by @ethomson in https://github.com/libgit2/libgit2/pull/6347
+* Fix creation of branches and tags with invalid names by @lya001 in https://github.com/libgit2/libgit2/pull/6348
+### Security fixes
+* Fixes for CVE 2022-29187 by @ethomson in https://github.com/libgit2/libgit2/pull/6349
+* zlib: update bundled zlib to v1.2.12 by @ethomson in https://github.com/libgit2/libgit2/pull/6350
+### Code cleanups
+* sha256: refactoring in preparation for sha256 by @ethomson in https://github.com/libgit2/libgit2/pull/6265
+* remote: Delete a now-inexistent API declaration by @lhchavez in https://github.com/libgit2/libgit2/pull/6276
+* Fix missing include by @cschlack in https://github.com/libgit2/libgit2/pull/6277
+### Build and CI improvements
+* meta: show build status for v1.3 and v1.4 branches by @ethomson in https://github.com/libgit2/libgit2/pull/6216
+* cmake: Fix package name for system http-parser by @mgorny in https://github.com/libgit2/libgit2/pull/6217
+* meta: update version number to v1.5.0-alpha by @ethomson in https://github.com/libgit2/libgit2/pull/6220
+* cmake: export libraries needed to compile against libgit2 by @ethomson in https://github.com/libgit2/libgit2/pull/6239
+* clone: update bitbucket tests by @ethomson in https://github.com/libgit2/libgit2/pull/6252
+* diff: don't stat empty file on arm32 (flaky test) by @ethomson in https://github.com/libgit2/libgit2/pull/6259
+* tests: support flaky stat by @ethomson in https://github.com/libgit2/libgit2/pull/6262
+* Include test results data in CI by @ethomson in https://github.com/libgit2/libgit2/pull/6306
+* Add a .clang-format with our style by @ethomson in https://github.com/libgit2/libgit2/pull/6023
+* CI: limits actions scheduled workflows to the main repo by @ethomson in https://github.com/libgit2/libgit2/pull/6342
+* ci: update dockerfiles for mbedTLS new url by @ethomson in https://github.com/libgit2/libgit2/pull/6343
+### Documentation improvements
+* Add Pharo to language bindings by @theseion in https://github.com/libgit2/libgit2/pull/6310
+* Add link to Tcl bindings for libgit2 by @apnadkarni in https://github.com/libgit2/libgit2/pull/6318
+* fix couple of typos by @SkinnyMind in https://github.com/libgit2/libgit2/pull/6287
+* update documentation for default status options by @ethomson in https://github.com/libgit2/libgit2/pull/6322
+
+## New Contributors
+* @bierbaum made their first contribution in https://github.com/libgit2/libgit2/pull/6246
+* @dongcarl made their first contribution in https://github.com/libgit2/libgit2/pull/6299
+* @SkinnyMind made their first contribution in https://github.com/libgit2/libgit2/pull/6287
+* @zawata made their first contribution in https://github.com/libgit2/libgit2/pull/6305
+* @vishwin made their first contribution in https://github.com/libgit2/libgit2/pull/6340
+* @i-tengfei made their first contribution in https://github.com/libgit2/libgit2/pull/6334
+* @jpalus made their first contribution in https://github.com/libgit2/libgit2/pull/6333
+* @lya001 made their first contribution in https://github.com/libgit2/libgit2/pull/6348
+
+**Full Changelog**: https://github.com/libgit2/libgit2/compare/v1.4.0...v1.5.0
+
+v1.4
+----
+
+This is release v1.4.0, "Fisematenten". This release includes several new features and bugfixes, improves compatibility with git, and begins preparation for SHA256 support in a future release.
+
+## What's Changed
+### New features
+* diff: update rename limit to 1000 to match git's behavior by @ethomson in https://github.com/libgit2/libgit2/pull/6092
+* odb: support checking for object existence without refresh by @joshtriplett in https://github.com/libgit2/libgit2/pull/6107
+* object: provide a low-level mechanism to validate whether a raw object is valid (`git_object_rawcontent_is_valid`) by @ethomson in https://github.com/libgit2/libgit2/pull/6128
+* blob: provide a function to identify binary content by @ethomson in https://github.com/libgit2/libgit2/pull/6142
+* status: add `rename_threshold` to `git_status_options`. by @arroz in https://github.com/libgit2/libgit2/pull/6158
+* remote: support `http.followRedirects` (`false` and `initial`) and follow initial redirects by default by @ethomson in https://github.com/libgit2/libgit2/pull/6175
+* remote: support scp style paths with ports (`[git@github.com:22]:libgit2/libgit2`) by @ethomson in https://github.com/libgit2/libgit2/pull/6167
+* win32: update git for windows configuration file location compatibility by @csware in https://github.com/libgit2/libgit2/pull/6151 and @ethomson in https://github.com/libgit2/libgit2/pull/6180
+* refs: speed up packed reference lookups when packed refs are sorted by @ccstolley in https://github.com/libgit2/libgit2/pull/6138
+* merge: support zdiff3 conflict styles by @ethomson in https://github.com/libgit2/libgit2/pull/6195
+* remote: support fetching by object id (using "+oid:ref" refspec syntax) by @ethomson in https://github.com/libgit2/libgit2/pull/6203
+* merge: callers can specify virtual-base building behavior and to optionally accept conflict markers as a resolution by @boretrk in https://github.com/libgit2/libgit2/pull/6204
+
+### Bug fixes
+* Fix a gcc 11 warning in src/threadstate.c by @lhchavez in https://github.com/libgit2/libgit2/pull/6115
+* Fix a gcc 11 warning in src/thread.h by @lhchavez in https://github.com/libgit2/libgit2/pull/6116
+* cmake: re-enable WinHTTP by @ethomson in https://github.com/libgit2/libgit2/pull/6120
+* Fix repo init when template dir is non-existent by @ammgws in https://github.com/libgit2/libgit2/pull/6106
+* cmake: use project-specific root variable instead of CMAKE_SOURCE_DIR by @Qix- in https://github.com/libgit2/libgit2/pull/6146
+* Better revparse compatibility for at time notation by @yoichi in https://github.com/libgit2/libgit2/pull/6095
+* remotes: fix insteadOf/pushInsteadOf handling by @mkhl in https://github.com/libgit2/libgit2/pull/6101
+* git_commit_summary: ignore lines with spaces by @stforek in https://github.com/libgit2/libgit2/pull/6125
+* Config parsing by @csware in https://github.com/libgit2/libgit2/pull/6124
+* config: handle empty conditional in includeIf by @ethomson in https://github.com/libgit2/libgit2/pull/6165
+* #6154 git_status_list_new case insensitive fix by @arroz in https://github.com/libgit2/libgit2/pull/6159
+* futils_mktmp: don't use umask by @boretrk in https://github.com/libgit2/libgit2/pull/6178
+* revparse: support bare '@' by @ethomson in https://github.com/libgit2/libgit2/pull/6196
+* odb: check for write failures by @ethomson in https://github.com/libgit2/libgit2/pull/6206
+* push: Prepare pack before sending pack header. by @ccstolley in https://github.com/libgit2/libgit2/pull/6205
+* mktmp: improve our temp file creation by @ethomson in https://github.com/libgit2/libgit2/pull/6207
+* diff_file: fix crash if size of diffed file changes in workdir by @jorio in https://github.com/libgit2/libgit2/pull/6208
+* merge: comment conflicts lines in MERGE_MSG by @ethomson in https://github.com/libgit2/libgit2/pull/6197
+* Fix crashes in example programs on Windows (sprintf_s not compatible with snprintf) by @apnadkarni in https://github.com/libgit2/libgit2/pull/6212
+
+### Code cleanups
+* Introduce `git_remote_connect_options` by @ethomson in https://github.com/libgit2/libgit2/pull/6161
+* hash: separate hashes and git_oid by @ethomson in https://github.com/libgit2/libgit2/pull/6082
+* `git_buf`: now a public-only API (`git_str` is our internal API) by @ethomson in https://github.com/libgit2/libgit2/pull/6078
+* cmake: cleanups and consistency by @ethomson in https://github.com/libgit2/libgit2/pull/6084
+* path: refactor utility path functions by @ethomson in https://github.com/libgit2/libgit2/pull/6104
+* str: git_str_free is never a function by @ethomson in https://github.com/libgit2/libgit2/pull/6111
+* cmake refactorings by @ethomson in https://github.com/libgit2/libgit2/pull/6112
+* Add missing-declarations warning globally by @ethomson in https://github.com/libgit2/libgit2/pull/6113
+* cmake: further refactorings by @ethomson in https://github.com/libgit2/libgit2/pull/6114
+* tag: set validity to 0 by default by @ethomson in https://github.com/libgit2/libgit2/pull/6119
+* util: minor cleanup and refactoring to the date class by @ethomson in https://github.com/libgit2/libgit2/pull/6121
+* Minor code cleanups by @ethomson in https://github.com/libgit2/libgit2/pull/6122
+* Fix a long long that crept past by @NattyNarwhal in https://github.com/libgit2/libgit2/pull/6094
+* remote: refactor insteadof application by @ethomson in https://github.com/libgit2/libgit2/pull/6147
+* ntmlclient: fix linking with libressl by @boretrk in https://github.com/libgit2/libgit2/pull/6157
+* c99: change single bit flags to unsigned by @boretrk in https://github.com/libgit2/libgit2/pull/6179
+* Fix typos by @rex4539 in https://github.com/libgit2/libgit2/pull/6164
+* diff_driver: split global_drivers array into separate elements by @boretrk in https://github.com/libgit2/libgit2/pull/6184
+* cmake: disable some gnu extensions by @boretrk in https://github.com/libgit2/libgit2/pull/6185
+* Disabling setting `CMAKE_FIND_LIBRARY_SUFFIXES` on Apple platforms. by @arroz in https://github.com/libgit2/libgit2/pull/6153
+* C90: add inline macro to xdiff and mbedtls by @boretrk in https://github.com/libgit2/libgit2/pull/6200
+* SHA256: early preparation by @ethomson in https://github.com/libgit2/libgit2/pull/6192
+
+### CI improvements
+* tests: rename test runner to `libgit2_tests`, build option to `BUILD_TESTS`. by @ethomson in https://github.com/libgit2/libgit2/pull/6083
+* ci: only update docs on push by @ethomson in https://github.com/libgit2/libgit2/pull/6108
+* Pedantic header test by @boretrk in https://github.com/libgit2/libgit2/pull/6086
+* ci: build with ssh on nightly by @ethomson in https://github.com/libgit2/libgit2/pull/6148
+* ci: improve the name in CI runs by @ethomson in https://github.com/libgit2/libgit2/pull/6198
+
+### Documentation improvements
+* Document that `git_odb` is thread-safe by @joshtriplett in https://github.com/libgit2/libgit2/pull/6109
+* Improve documentation by @punkymaniac in https://github.com/libgit2/libgit2/pull/6168
+
+### Other changes
+* libgit2_clar is now libgit2_tests by @mkhl in https://github.com/libgit2/libgit2/pull/6100
+* Remove PSGit from Language Bindings section of README by @cestrand in https://github.com/libgit2/libgit2/pull/6150
+* COPYING: remove regex copyright, add PCRE copyright by @ethomson in https://github.com/libgit2/libgit2/pull/6187
+* meta: add a release configuration file by @ethomson in https://github.com/libgit2/libgit2/pull/6211
+
+## New Contributors
+* @mkhl made their first contribution in https://github.com/libgit2/libgit2/pull/6100
+* @ammgws made their first contribution in https://github.com/libgit2/libgit2/pull/6106
+* @yoichi made their first contribution in https://github.com/libgit2/libgit2/pull/6095
+* @stforek made their first contribution in https://github.com/libgit2/libgit2/pull/6125
+* @cestrand made their first contribution in https://github.com/libgit2/libgit2/pull/6150
+* @rex4539 made their first contribution in https://github.com/libgit2/libgit2/pull/6164
+* @jorio made their first contribution in https://github.com/libgit2/libgit2/pull/6208
+
+**Full Changelog**: https://github.com/libgit2/libgit2/compare/v1.3.0...v1.4.0
+
+v1.3
+----
+
+This is release v1.3.0, "Zugunruhe". This release includes only minor new features that will be helpful for users to have an orderly transition to the v2.0 lineage.
+
+## New Features
+* Support custom git extensions by @ethomson in https://github.com/libgit2/libgit2/pull/6031
+* Introduce `git_email_create`; deprecate `git_diff_format_email` by @ethomson in https://github.com/libgit2/libgit2/pull/6061
+
+## Deprecated APIs
+* `git_oidarray_free` is deprecated; callers should use `git_oidarray_dispose`
+
+## Bug fixes
+* #6028: Check if `threadstate->error_t.message` is not `git_buf__initbuf` before freeing. by @arroz in https://github.com/libgit2/libgit2/pull/6029
+* remote: Mark `git_remote_name_is_valid` as `GIT_EXTERN` by @lhchavez in https://github.com/libgit2/libgit2/pull/6032
+* Fix config parsing for multiline with multiple quoted comment chars by @basile-henry in https://github.com/libgit2/libgit2/pull/6043
+* indexer: Avoid one `mmap(2)`/`munmap(2)` pair per `git_indexer_append` call by @lhchavez in https://github.com/libgit2/libgit2/pull/6039
+* merge: Check file mode when resolving renames by @ccstolley in https://github.com/libgit2/libgit2/pull/6060
+* Allow proxy options when connecting with a detached remote. by @lrm29 in https://github.com/libgit2/libgit2/pull/6058
+* win32: allow empty environment variables by @ethomson in https://github.com/libgit2/libgit2/pull/6063
+* Fixes for deprecated APIs by @ethomson in https://github.com/libgit2/libgit2/pull/6066
+* filter: use a `git_oid` in filter options, not a pointer by @ethomson in https://github.com/libgit2/libgit2/pull/6067
+* diff: update `GIT_DIFF_IGNORE_BLANK_LINES` by @ethomson in https://github.com/libgit2/libgit2/pull/6068
+* Attribute lookups are always on relative paths by @ethomson in https://github.com/libgit2/libgit2/pull/6073
+* Handle long paths when querying attributes by @ethomson in https://github.com/libgit2/libgit2/pull/6075
+
+## Code cleanups
+* notes: use a buffer internally by @ethomson in https://github.com/libgit2/libgit2/pull/6047
+* Fix coding style for pointer by @punkymaniac in https://github.com/libgit2/libgit2/pull/6045
+* Use __typeof__ GNUC keyword for ISO C compatibility by @duncanthomson in https://github.com/libgit2/libgit2/pull/6041
+* Discover libssh2 without pkg-config by @stac47 in https://github.com/libgit2/libgit2/pull/6053
+* Longpath filter bug by @lrm29 in https://github.com/libgit2/libgit2/pull/6055
+* Add test to ensure empty proxy env behaves like unset env by @sathieu in https://github.com/libgit2/libgit2/pull/6052
+* Stdint header condition has been reverted. by @lolgear in https://github.com/libgit2/libgit2/pull/6020
+* buf: `common_prefix` takes a string array by @ethomson in https://github.com/libgit2/libgit2/pull/6077
+* oidarray: introduce `git_oidarray_dispose` by @ethomson in https://github.com/libgit2/libgit2/pull/6076
+* examples: Free the git_config and git_config_entry after use by @257 in https://github.com/libgit2/libgit2/pull/6071
+
+## CI Improvements
+* ci: pull libssh2 from www.libssh2.org by @ethomson in https://github.com/libgit2/libgit2/pull/6064
+
+## Documentation changes
+* Update README.md by @shijinglu in https://github.com/libgit2/libgit2/pull/6050
+
+## New Contributors
+* @basile-henry made their first contribution in https://github.com/libgit2/libgit2/pull/6043
+* @duncanthomson made their first contribution in https://github.com/libgit2/libgit2/pull/6041
+* @stac47 made their first contribution in https://github.com/libgit2/libgit2/pull/6053
+* @shijinglu made their first contribution in https://github.com/libgit2/libgit2/pull/6050
+* @ccstolley made their first contribution in https://github.com/libgit2/libgit2/pull/6060
+* @sathieu made their first contribution in https://github.com/libgit2/libgit2/pull/6052
+* @257 made their first contribution in https://github.com/libgit2/libgit2/pull/6071
+
+**Full Changelog**: https://github.com/libgit2/libgit2/compare/v1.2.0...v1.3.0
+
+---------------------------------------------------------------------
+
+v1.2
+-----
+
+This is release v1.2.0, "Absacker". This release includes many new features: in particular, support for commit graphs, multi-pack indexes, and `core.longpaths` support.
+
+This is meant to be the final minor release in the v1 lineage. v2.0 will be the next major release and will remove deprecated APIs and may include breaking changes.
+
+## Deprecated APIs
+
+* revspec: rename git_revparse_mode_t to git_revspec_t by @ethomson in https://github.com/libgit2/libgit2/pull/5786
+* tree: deprecate `git_treebuilder_write_with_buffer` by @ethomson in https://github.com/libgit2/libgit2/pull/5815
+* Deprecate `is_valid_name` functions; replace with `name_is_valid` functions by @ethomson in https://github.com/libgit2/libgit2/pull/5659
+* filter: stop taking git_buf as user input by @ethomson in https://github.com/libgit2/libgit2/pull/5859
+* remote: introduce remote_ready_cb, deprecate resolve_url callback by @ethomson in https://github.com/libgit2/libgit2/pull/6012
+* Introduce `create_commit_cb`, deprecate `signing_cb` by @ethomson in https://github.com/libgit2/libgit2/pull/6016
+* filter: filter drivers stop taking git_buf as user input by @ethomson in https://github.com/libgit2/libgit2/pull/6011
+* buf: deprecate public git_buf writing functions by @ethomson in https://github.com/libgit2/libgit2/pull/6017
+
+## New features
+
+* winhttp: support optional client cert by @ianhattendorf in https://github.com/libgit2/libgit2/pull/5384
+* Add support for additional SSH hostkey types. by @arroz in https://github.com/libgit2/libgit2/pull/5750
+* Handle ipv6 addresses by @ethomson in https://github.com/libgit2/libgit2/pull/5741
+* zlib: Add support for building with Chromium's zlib implementation by @lhchavez in https://github.com/libgit2/libgit2/pull/5748
+* commit-graph: Introduce a parser for commit-graph files by @lhchavez in https://github.com/libgit2/libgit2/pull/5762
+* patch: add owner accessor by @KOLANICH in https://github.com/libgit2/libgit2/pull/5731
+* commit-graph: Support lookups of entries in a commit-graph by @lhchavez in https://github.com/libgit2/libgit2/pull/5763
+* commit-graph: Introduce `git_commit_graph_needs_refresh()` by @lhchavez in https://github.com/libgit2/libgit2/pull/5764
+* Working directory path validation by @ethomson in https://github.com/libgit2/libgit2/pull/5823
+* Support `core.longpaths` on Windows by @ethomson in https://github.com/libgit2/libgit2/pull/5857
+* git_reference_create_matching: Treat all-zero OID as "must be absent" by @novalis in https://github.com/libgit2/libgit2/pull/5842
+* diff:add option to ignore blank line changes by @yuuri in https://github.com/libgit2/libgit2/pull/5853
+* [Submodule] Git submodule dup by @lolgear in https://github.com/libgit2/libgit2/pull/5890
+* commit-graph: Use the commit-graph in revwalks by @lhchavez in https://github.com/libgit2/libgit2/pull/5765
+* commit-graph: Introduce `git_commit_list_generation_cmp` by @lhchavez in https://github.com/libgit2/libgit2/pull/5766
+* graph: Create `git_graph_reachable_from_any()` by @lhchavez in https://github.com/libgit2/libgit2/pull/5767
+* Support reading attributes from a specific commit by @ethomson in https://github.com/libgit2/libgit2/pull/5952
+* [Branch] Branch upstream with format by @lolgear in https://github.com/libgit2/libgit2/pull/5861
+* Dynamically load OpenSSL (optionally) by @ethomson in https://github.com/libgit2/libgit2/pull/5974
+* Set refs/remotes/origin/HEAD to default branch when branch is specified by @A-Ovchinnikov-mx in https://github.com/libgit2/libgit2/pull/6010
+* midx: Add a way to write multi-pack-index files by @lhchavez in https://github.com/libgit2/libgit2/pull/5404
+* Use error code GIT_EAUTH for authentication failures by @josharian in https://github.com/libgit2/libgit2/pull/5395
+* midx: Introduce git_odb_write_multi_pack_index() by @lhchavez in https://github.com/libgit2/libgit2/pull/5405
+* Checkout dry-run by @J0Nes90 in https://github.com/libgit2/libgit2/pull/5841
+* mbedTLS: Fix setting certificate directory by @mikezackles in https://github.com/libgit2/libgit2/pull/6004
+* remote: introduce remote_ready_cb, deprecate resolve_url callback by @ethomson in https://github.com/libgit2/libgit2/pull/6012
+* Introduce `create_commit_cb`, deprecate `signing_cb` by @ethomson in https://github.com/libgit2/libgit2/pull/6016
+* commit-graph: Add a way to write commit-graph files by @lhchavez in https://github.com/libgit2/libgit2/pull/5778
+
+## Bug fixes
+
+* Define `git___load` when building with `-DTHREADSAFE=OFF` by @lhchavez in https://github.com/libgit2/libgit2/pull/5664
+* Make the Windows leak detection more robust by @lhchavez in https://github.com/libgit2/libgit2/pull/5661
+* Refactor "global" state by @ethomson in https://github.com/libgit2/libgit2/pull/5546
+* threadstate: rename tlsdata when building w/o threads by @ethomson in https://github.com/libgit2/libgit2/pull/5668
+* Include `${MBEDTLS_INCLUDE_DIR}` when compiling `crypt_mbedtls.c` by @staticfloat in https://github.com/libgit2/libgit2/pull/5685
+* Fix the `-DTHREADSAFE=OFF` build by @lhchavez in https://github.com/libgit2/libgit2/pull/5690
+* Add missing worktree_dir check and test case by @rbmclean in https://github.com/libgit2/libgit2/pull/5692
+* msvc crtdbg -> win32 leakcheck by @ethomson in https://github.com/libgit2/libgit2/pull/5580
+* Introduce GIT_ASSERT macros by @ethomson in https://github.com/libgit2/libgit2/pull/5327
+* Also add the raw hostkey to `git_cert_hostkey` by @lhchavez in https://github.com/libgit2/libgit2/pull/5704
+* Make the odb race-free by @lhchavez in https://github.com/libgit2/libgit2/pull/5595
+* Make the pack and mwindow implementations data-race-free by @lhchavez in https://github.com/libgit2/libgit2/pull/5593
+* Thread-free implementation by @ethomson in https://github.com/libgit2/libgit2/pull/5719
+* Thread-local storage: a generic internal library (with no allocations) by @ethomson in https://github.com/libgit2/libgit2/pull/5720
+* Friendlier getting started in the lack of git_libgit2_init by @ethomson in https://github.com/libgit2/libgit2/pull/5578
+* Make git__strntol64() ~70%* faster by @lhchavez in https://github.com/libgit2/libgit2/pull/5735
+* Cache the parsed submodule config when diffing by @lhchavez in https://github.com/libgit2/libgit2/pull/5727
+* pack: continue zlib while we can make progress by @ethomson in https://github.com/libgit2/libgit2/pull/5740
+* Avoid using `__builtin_mul_overflow` with the clang+32-bit combo by @lhchavez in https://github.com/libgit2/libgit2/pull/5742
+* repository: use intptr_t's in the config map cache by @ethomson in https://github.com/libgit2/libgit2/pull/5746
+* Build with NO_MMAP by @0xdky in https://github.com/libgit2/libgit2/pull/5583
+* Add documentation for git_blob_filter_options.version by @JoshuaS3 in https://github.com/libgit2/libgit2/pull/5759
+* blob: fix name of `GIT_BLOB_FILTER_ATTRIBUTES_FROM_HEAD` by @ethomson in https://github.com/libgit2/libgit2/pull/5760
+* Cope with empty default branch by @ethomson in https://github.com/libgit2/libgit2/pull/5770
+* README: instructions for using libgit2 without compiling by @ethomson in https://github.com/libgit2/libgit2/pull/5772
+* Use `p_pwrite`/`p_pread` consistently throughout the codebase by @lhchavez in https://github.com/libgit2/libgit2/pull/5769
+* midx: Fix a bug in `git_midx_needs_refresh()` by @lhchavez in https://github.com/libgit2/libgit2/pull/5768
+* mwindow: Fix a bug in the LRU window finding code by @lhchavez in https://github.com/libgit2/libgit2/pull/5783
+* refdb_fs: Check git_sortedcache wlock/rlock errors by @mamapanda in https://github.com/libgit2/libgit2/pull/5800
+* index: Check git_vector_dup error in write_entries by @mamapanda in https://github.com/libgit2/libgit2/pull/5801
+* Fix documentation formating on repository.h by @punkymaniac in https://github.com/libgit2/libgit2/pull/5806
+* include: fix typos in comments by @tniessen in https://github.com/libgit2/libgit2/pull/5805
+* Fix some typos by @aaronfranke in https://github.com/libgit2/libgit2/pull/5797
+* Check git_signature_dup failure by @mamapanda in https://github.com/libgit2/libgit2/pull/5817
+* merge: Check insert_head_ids error in create_virtual_base by @mamapanda in https://github.com/libgit2/libgit2/pull/5818
+* winhttp: skip certificate check if unable to send request by @ianhattendorf in https://github.com/libgit2/libgit2/pull/5814
+* Default to GIT_BRANCH_DEFAULT if init.defaultBranch is empty string by @ianhattendorf in https://github.com/libgit2/libgit2/pull/5832
+* Fix diff_entrycount -> diff_num_deltas doc typo by @mjsir911 in https://github.com/libgit2/libgit2/pull/5838
+* repo: specify init.defaultbranch is meant to be a branch name by @carlosmn in https://github.com/libgit2/libgit2/pull/5835
+* repo: remove an inappropriate use of PASSTHROUGH by @carlosmn in https://github.com/libgit2/libgit2/pull/5834
+* src: fix typos in header files by @tniessen in https://github.com/libgit2/libgit2/pull/5843
+* test: clean up memory leaks by @ethomson in https://github.com/libgit2/libgit2/pull/5858
+* buf: remove unnecessary buf_text namespace by @ethomson in https://github.com/libgit2/libgit2/pull/5860
+* Fix bug in git_diff_find_similar. by @staktrace in https://github.com/libgit2/libgit2/pull/5839
+* Fix issues with Proxy Authentication after httpclient refactor by @implausible in https://github.com/libgit2/libgit2/pull/5852
+* tests: clean up memory leak, fail on leak for win32 by @ethomson in https://github.com/libgit2/libgit2/pull/5892
+* Tolerate readlink size less than st_size by @dtolnay in https://github.com/libgit2/libgit2/pull/5900
+* Define WINHTTP_NO_CLIENT_CERT_CONTEXT if needed by @jacquesg in https://github.com/libgit2/libgit2/pull/5929
+* Update from regex to pcre licensing information in docs/contributing.md by @boretrk in https://github.com/libgit2/libgit2/pull/5916
+* Consider files executable only if the user can execute them by @novalis in https://github.com/libgit2/libgit2/pull/5915
+* git__timer: Limit ITimer usage to AmigaOS4 by @boretrk in https://github.com/libgit2/libgit2/pull/5936
+* Fix memory leak in git_smart__connect by @punkymaniac in https://github.com/libgit2/libgit2/pull/5908
+* config: fix included configs not refreshed more than once by @Batchyx in https://github.com/libgit2/libgit2/pull/5926
+* Fix wrong time_t used in function by @NattyNarwhal in https://github.com/libgit2/libgit2/pull/5938
+* fix check for ignoring of negate rules by @palmin in https://github.com/libgit2/libgit2/pull/5824
+* Make `FIND_PACKAGE(PythonInterp)` prefer `python3` by @lhchavez in https://github.com/libgit2/libgit2/pull/5913
+* git__timer: Allow compilation on systems without CLOCK_MONOTONIC by @boretrk in https://github.com/libgit2/libgit2/pull/5945
+* stdintification: use int64_t and INT64_C instead of long long by @NattyNarwhal in https://github.com/libgit2/libgit2/pull/5941
+* Optional stricter allocation checking (for `malloc(0)` cases) by @ethomson in https://github.com/libgit2/libgit2/pull/5951
+* Variadic arguments aren't in C89 by @NattyNarwhal in https://github.com/libgit2/libgit2/pull/5948
+* Fix typo in general.c by @Crayon2000 in https://github.com/libgit2/libgit2/pull/5954
+* common.h: use inline when compiling for C99 and later by @boretrk in https://github.com/libgit2/libgit2/pull/5953
+* Fix one memory leak in master by @lhchavez in https://github.com/libgit2/libgit2/pull/5957
+* tests: reset odb backend priority by @ethomson in https://github.com/libgit2/libgit2/pull/5961
+* cmake: extended futimens checking on macOS by @ethomson in https://github.com/libgit2/libgit2/pull/5962
+* amiga: use ';' as path list separator on AmigaOS by @boretrk in https://github.com/libgit2/libgit2/pull/5978
+* Respect the force flag on refspecs in git_remote_fetch by @alexjg in https://github.com/libgit2/libgit2/pull/5854
+* Fix LIBGIT2_FILENAME not being passed to the resource compiler by @jairbubbles in https://github.com/libgit2/libgit2/pull/5994
+* sha1dc: remove conditional for <sys/types.h> by @boretrk in https://github.com/libgit2/libgit2/pull/5997
+* openssl: don't fail when we can't customize allocators by @ethomson in https://github.com/libgit2/libgit2/pull/5999
+* C11 warnings by @boretrk in https://github.com/libgit2/libgit2/pull/6005
+* open: input validation for empty segments in path by @boretrk in https://github.com/libgit2/libgit2/pull/5950
+* Introduce GIT_WARN_UNUSED_RESULT by @lhchavez in https://github.com/libgit2/libgit2/pull/5802
+* GCC C11 warnings by @boretrk in https://github.com/libgit2/libgit2/pull/6006
+* array: check dereference from void * type by @boretrk in https://github.com/libgit2/libgit2/pull/6007
+* Homogenize semantics for atomic-related functions by @lhchavez in https://github.com/libgit2/libgit2/pull/5747
+* git_array_alloc: return objects of correct type by @boretrk in https://github.com/libgit2/libgit2/pull/6008
+* CMake. hash sha1 header has been added. by @lolgear in https://github.com/libgit2/libgit2/pull/6013
+* tests: change comments to c89 style by @boretrk in https://github.com/libgit2/libgit2/pull/6015
+* Set Host Header to match CONNECT authority target by @lollipopman in https://github.com/libgit2/libgit2/pull/6022
+* Fix worktree iteration when repository has no common directory by @kcsaul in https://github.com/libgit2/libgit2/pull/5943
+
+## Documentation improvements
+
+* Update README.md for additional Delphi bindings by @todaysoftware in https://github.com/libgit2/libgit2/pull/5831
+* Fix documentation formatting by @punkymaniac in https://github.com/libgit2/libgit2/pull/5850
+* docs: fix incorrect comment marker by @tiennou in https://github.com/libgit2/libgit2/pull/5897
+* Patch documentation by @punkymaniac in https://github.com/libgit2/libgit2/pull/5903
+* Fix misleading doc for `git_index_find` by @arxanas in https://github.com/libgit2/libgit2/pull/5910
+* docs: stop mentioning libgit2's "master" branch by @Batchyx in https://github.com/libgit2/libgit2/pull/5925
+* docs: fix some missing includes that cause Docurium to error out by @tiennou in https://github.com/libgit2/libgit2/pull/5917
+* Patch documentation by @punkymaniac in https://github.com/libgit2/libgit2/pull/5940
+
+## Development improvements
+
+* WIP: .devcontainer: settings for a codespace workflow by @ethomson in https://github.com/libgit2/libgit2/pull/5508
+
+## CI Improvements
+
+* Add a ThreadSanitizer build by @lhchavez in https://github.com/libgit2/libgit2/pull/5597
+* ci: more GitHub Actions by @ethomson in https://github.com/libgit2/libgit2/pull/5706
+* ci: run coverity in the nightly builds by @ethomson in https://github.com/libgit2/libgit2/pull/5707
+* ci: only report main branch in README status by @ethomson in https://github.com/libgit2/libgit2/pull/5708
+* Fix the `ENABLE_WERROR=ON` build in Groovy Gorilla (gcc 10.2) by @lhchavez in https://github.com/libgit2/libgit2/pull/5715
+* Re-enable the RC4 test by @carlosmn in https://github.com/libgit2/libgit2/pull/4418
+* ci: run codeql by @ethomson in https://github.com/libgit2/libgit2/pull/5709
+* github-actions: Also rename the main branch here by @lhchavez in https://github.com/libgit2/libgit2/pull/5771
+* ci: don't use ninja on macOS by @ethomson in https://github.com/libgit2/libgit2/pull/5780
+* ci: use GitHub for storing mingw-w64 build dependency by @ethomson in https://github.com/libgit2/libgit2/pull/5855
+* docker: remove the entrypoint by @ethomson in https://github.com/libgit2/libgit2/pull/5980
+* http: don't require a password by @ethomson in https://github.com/libgit2/libgit2/pull/5972
+* ci: update nightly to use source path by @ethomson in https://github.com/libgit2/libgit2/pull/5989
+* ci: add centos 7 and centos 8 by @ethomson in https://github.com/libgit2/libgit2/pull/5992
+* ci: update centos builds by @ethomson in https://github.com/libgit2/libgit2/pull/5995
+* ci: tag new containers with the latest tag by @ethomson in https://github.com/libgit2/libgit2/pull/6000
+
+## Dependency updates
+
+* ntlm: [ntlmclient](https://github.com/ethomson/ntlmclient) is now v0.9.1
+
+**Full Changelog**: https://github.com/libgit2/libgit2/compare/v1.1.0...v1.2.0
+
+---------------------------------------------------------------------
+
+v1.1
+----
+
+This is release v1.1, "Fernweh".
+
+### Changes or improvements
+
+* Our bundled PCRE dependency has been updated to 8.44.
+
+* The `refs/remotes/origin/HEAD` file will be created at clone time to
+ point to the origin's default branch.
+
+* libgit2 now uses the `__atomic_` intrinsics instead of `__sync_`
+ intrinsics on supported gcc and clang versions.
+
+* The `init.defaultBranch` setting is now respected and `master` is
+ no longer the hardcoded as the default branch name.
+
+* Patch files that do not contain an `index` line can now be parsed.
+
+* Configuration files with multi-line values can now contain quotes
+ split across multiple lines.
+
+* Windows clients now attempt to use TLS1.3 when available.
+
+* Servers that request an upgrade to a newer HTTP version are
+ silently ignored instead of erroneously failing.
+
+* Users can pass `NULL` to the options argument to
+ `git_describe_commit`.
+
+* Clones and fetches of very large packfiles now succeeds on 32-bit
+ platforms.
+
+* Custom reference database backends can now handle the repository's
+ `HEAD` correctly.
+
+* Repositories with a large number of packfiles no longer exhaust the
+ number of file descriptors.
+
+* The test framework now supports TAP output when the `-t` flag is
+ specified.
+
+* The test framework can now specify an exact match to a test
+ function using a trailing `$`.
+
+* All checkout types support `GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH`.
+
+* `git_blame` now can ignore whitespace changes using the option
+ `GIT_BLAME_IGNORE_WHITESPACE`.
+
+* Several new examples have been created, including an examples for
+ commit, add and push.
+
+* Mode changes during rename are now supported in patch application.
+
+* `git_checkout_head` now correctly removes untracked files in a
+ subdirectory when the `FORCE | REMOVE_UNTRACKED` options are specified.
+
+v1.0.1
+------
+
+This is a bugfix release with the following changes:
+
+- Calculating information about renamed files during merges is more
+ efficient because dissimilarity about files is now being cached and
+ no longer needs to be recomputed.
+
+- The `git_worktree_prune_init_options` has been correctly restored for
+ backward compatibility. In v1.0 it was incorrectly deprecated with a
+ typo.
+
+- The optional ntlmclient dependency now supports NetBSD.
+
+- A bug where attempting to stash on a bare repository may have failed
+ has been fixed.
+
+- Configuration files that are unreadable due to permissions are now
+ silently ignored, and treated as if they do not exist. This matches
+ git's behavior; previously this case would have been an error.
+
+- v4 index files are now correctly written; previously we would read
+ them correctly but would not write the prefix-compression accurately,
+ causing corruption.
+
+- A bug where the smart HTTP transport could not read large data packets
+ has been fixed. Previously, fetching from servers like Gerrit, that
+ sent large data packets, would error.
+
+---------------------------------------------------------------------
+
+v1.0
+----
+
+This is release v1.0 "Luftschloss", which is the first stabe release of
+libgit2. The API will stay compatible across all releases of the same major
+version. This release includes bugfixes only and supersedes v0.99, which will
+stop being maintained. Both v0.27 and v0.28 stay supported in accordance with
+our release policy.
+
+### Changes or improvements
+
+- CMake was converted to make use of the GNUInstallDirs module for both our
+ pkgconfig and install targets in favor of our custom build options
+ `BIN_INSTALL_DIR`, `LIB_INSTALL_DIR` and `INCLUDE_INSTALL_DIR`. Instead, you
+ can now use CMakes standard variables `CMAKE_INSTALL_BINDIR`,
+ `CMAKE_INSTALL_LIBDIR` and `CMAKE_INSTALL_INCLUDEDIR`.
+
+- Some CMake build options accepted either a specific value or a boolean value
+ to disable the option altogether or use automatic detection. We only accepted
+ "ON" or "OFF", but none of the other values CMake recognizes as boolean. This
+ was aligned with CMake's understanding of booleans.
+
+- The installed pkgconfig file contained incorrect values for both `libdir` and
+ `includedir` variables.
+
+- If using pcre2 for regular expressions, then we incorrectly added "pcre2"
+ instead of "pcre2-8" to our pkgconfig dependencies, which was corrected.
+
+- Fixed building the bundled ntlmclient dependency on FreeBSD, OpenBSD and
+ SunOS.
+
+- When writing symlinks on Windows, we incorrectly handled relative symlink
+ targets, which was corrected.
+
+- When using the HTTP protocol via macOS' SecureTransport implementation, reads
+ could stall at the end of the session and only continue after a timeout of 60
+ seconds was reached.
+
+- The filesystem-based reference callback didn't corectly initialize the backend
+ version.
+
+- A segmentation fault was fixed when calling `git_blame_buffer()` for files
+ that were modified and added to the index.
+
+- A backwards-incompatible change was introduced when we moved some structures
+ from "git2/credentials.h" into "git2/sys/credentials.h". This was fixed in the
+ case where you do not use hard deprecation.
+
+- Improved error handling in various places.
+
+
+v0.99
+-----
+
+This is v0.99 "Torschlusspanik". This will be the last minor release
+before libgit2 v1.0. We expect to only respond to bugs in this release,
+to stabilize it for next major release.
+
+It contains significant refactorings, but is expected to be API-compatible
+with v0.28.0.
+
+### Changes or improvements
+
+* When fetching from an anonymous remote using a URL with authentication
+ information provided in the URL (eg `https://foo:bar@example.com/repo`),
+ we would erroneously include the literal URL in the FETCH_HEAD file.
+ We now remove that to match git's behavior.
+
+* Some credential structures, enums and values have been renamed:
+ `git_cred` is now `git_credential`. `git_credtype_t` is now
+ `git_credential_t`. Functions and types beginning with
+ `git_cred_` now begin with `git_credential`, and constants beginning
+ with `GIT_CREDTYPE` now begin with `GIT_CREDENTIAL`. The former names
+ are deprecated.
+
+* Several function signatures have been changed to return an `int` to
+ indicate error conditions. We encourage you to check them for errors
+ in the standard way.
+
+ * `git_attr_cache_flush`
+ * `git_error_set_str`
+ * `git_index_name_clear`
+ * `git_index_reuc_clear`
+ * `git_libgit2_version`
+ * `git_mempack_reset`
+ * `git_oid_cpy`
+ * `git_oid_fmt`
+ * `git_oid_fromraw`
+ * `git_oid_nfmt`
+ * `git_oid_pathfmt`
+ * `git_remote_stop`
+ * `git_remote_disconnect`
+ * `git_repository__cleanup`
+ * `git_repository_set_config`
+ * `git_repository_set_index`
+ * `git_repository_set_odb`
+ * `git_repository_set_refdb`
+ * `git_revwalk_reset`
+ * `git_revwalk_simplify_first_parent`
+ * `git_revwalk_sorting`
+ * `git_treebuilder_clear`
+ * `git_treebuilder_filter`
+
+* The NTLM and Negotiate authentication mechanisms are now supported when
+ talking to git implementations hosted on Apache or nginx servers.
+
+* The `HEAD` symbolic reference can no longer be deleted.
+
+* `git_merge_driver_source_repo` no longer returns a `const git_repository *`,
+ it now returns a non-`const` `git_repository *`.
+
+* Relative symbolic links are now supported on Windows when `core.symlinks`
+ is enabled.
+
+* Servers that provide query parameters with a redirect are now supported.
+
+* `git_submodule_sync` will now resolve relative URLs.
+
+* When creating git endpoint URLs, double-slashes are no longer used when
+ the given git URL has a trailing slash.
+
+* On Windows, a `DllMain` function is no longer included and thread-local
+ storage has moved to fiber-local storage in order to prevent race
+ conditions during shutdown.
+
+* The tracing mechanism (`GIT_TRACE`) is now enabled by default and does
+ not need to be explicitly enabled in CMake.
+
+* The size of Git objects is now represented by `git_object_size_t`
+ instead of `off_t`.
+
+* Binary patches without data can now be parsed.
+
+* A configuration snapshot can now be created from another configuration
+ snapshot, not just a "true" configuration object.
+
+* The `git_commit_with_signature` API will now ensure that referenced
+ objects exist in the object database.
+
+* Stash messages containing newlines will now be replaced with spaces;
+ they will no longer be (erroneously) written to the repository.
+
+* `git_commit_create_with_signature` now verifies the commit information
+ to ensure that it points to a valid tree and valid parents.
+
+* `git_apply` has an option `GIT_APPLY_CHECK` that will only do a dry-run.
+ The index and working directory will remain unmodified, and application
+ will report if it would have worked.
+
+* Patches produced by Mercurial (those that lack some git extended headers)
+ can now be parsed and applied.
+
+* Reference locks are obeyed correctly on POSIX platforms, instead of
+ being removed.
+
+* Patches with empty new files can now be read and applied.
+
+* `git_apply_to_tree` can now correctly apply patches that add new files.
+
+* The program data configuration on Windows (`C:\ProgramData\Git\config`)
+ must be owned by an administrator, a system account or the current user
+ to be read.
+
+* `git_blob_filtered_content` is now deprecated in favor of `git_blob_filter`.
+
+* Configuration files can now be included conditionally using the
+ `onbranch` conditional.
+
+* Checkout can now properly create and remove symbolic links to directories
+ on Windows.
+
+* Stash no longer recomputes trees when committing a worktree, for
+ improved performance.
+
+* Repository templates can now include a `HEAD` file to default the
+ initial default branch.
+
+* Some configuration structures, enums and values have been renamed:
+ `git_cvar_map` is now `git_configmap`, `git_cvar_t` is now
+ `git_configmap_t`, `GIT_CVAR_FALSE` is now `GIT_CONFIGMAP_FALSE`,
+ `GIT_CVAR_TRUE` is now `GIT_CONFIGMAP_TRUE`, `GIT_CVAR_INT32` is now
+ `GIT_CONFIGMAP_INT32`, and `GIT_CVAR_STRING` is now `GIT_CONFIGMAP_STRING`.
+ The former names are deprecated.
+
+* Repositories can now be created at the root of a Windows drive.
+
+* Configuration lookups are now more efficiently cached.
+
+* `git_commit_create_with_signature` now supports a `NULL` signature,
+ which will create a commit without adding a signature.
+
+* When a repository lacks an `info` "common directory", we will no
+ longer erroneously return `GIT_ENOTFOUND` for all attribute lookups.
+
+* Several attribute macros have been renamed: `GIT_ATTR_TRUE` is now
+ `GIT_ATTR_IS_TRUE`, `GIT_ATTR_FALSE` is now `GIT_ATTR_IS_FALSE`,
+ `GIT_ATTR_UNSPECIFIED` is now `GIT_ATTR_IS_UNSPECIFIED`. The
+ attribute enum `git_attr_t` is now `git_attr_value_t` and its
+ values have been renamed: `GIT_ATTR_UNSPECIFIED_T` is now
+ `GIT_ATTR_VALUE_UNSPECIFIED`, `GIT_ATTR_TRUE_T` is now
+ `GIT_ATTR_VALUE_TRUE`, `GIT_ATTR_FALSE_T` is now `GIT_ATTR_VALUE_FALSE`,
+ and `GIT_ATTR_VALUE_T` is now `GIT_ATTR_VALUE_STRING`. The
+ former names are deprecated.
+
+* `git_object__size` is now `git_object_size`. The former name is
+ deprecated.
+
+* `git_tag_create_frombuffer` is now `git_tag_create_from_buffer`. The
+ former name is deprecated.
+
+* Several blob creation functions have been renamed:
+ `git_blob_create_frombuffer` is now named `git_blob_create_from_buffer`,
+ `git_blob_create_fromdisk` is now named `git_blob_create_from_disk`,
+ `git_blob_create_fromworkdir` is now named `git_blob_create_from_workdir`,
+ `git_blob_create_fromstream` is now named `git_blob_create_from_stream`,
+ and `git_blob_create_fromstream_commit` is now named
+ `git_blob_create_from_stream_commit`. The former names are deprecated.
+
+* The function `git_oid_iszero` is now named `git_oid_is_zero`. The
+ former name is deprecated.
+
+* Pattern matching is now done using `wildmatch` instead of `fnmatch`
+ for compatibility with git.
+
+* The option initialization functions suffixed by `init_options` are now
+ suffixed with `options_init`. (For example, `git_checkout_init_options`
+ is now `git_checkout_options_init`.) The former names are deprecated.
+
+* NTLM2 authentication is now supported on non-Windows platforms.
+
+* The `git_cred_sign_callback` callback is now named `git_cred_sign_cb`.
+ The `git_cred_ssh_interactive_callback` callback is now named
+ `git_cred_ssh_interactive_cb`.
+
+* Ignore files now:
+
+ * honor escaped trailing whitespace.
+ * do not incorrectly negate sibling paths of a negated pattern.
+ * honor rules that stop ignoring files after a wildcard
+
+* Attribute files now:
+
+ * honor leading and trailing whitespace.
+ * treat paths beginning with `\` as absolute only on Windows.
+ * properly handle escaped characters.
+ * stop reading macros defined in subdirectories
+
+* The C locale is now correctly used when parsing regular expressions.
+
+* The system PCRE2 or PCRE regular expression libraries are now used
+ when `regcomp_l` is not available on the system. If none of these
+ are available on the system, an included version of PCRE is used.
+
+* Wildcards in reference specifications are now supported beyond simply
+ a bare wildcard (`*`) for compatibility with git.
+
+* When `git_ignore_path_is_ignored` is provided a path with a trailing
+ slash (eg, `dir/`), it will now treat it as a directory for the
+ purposes of ignore matching.
+
+* Patches that add or remove a file with a space in the path can now
+ be correctly parsed.
+
+* The `git_remote_completion_type` type is now `git_remote_completion_t`.
+ The former name is deprecated.
+
+* The `git_odb_backend_malloc` is now `git_odb_backend_data_alloc`. The
+ former name is deprecated.
+
+* The `git_transfer_progress_cb` callback is now `git_indexer_progress_cb`
+ and the `git_transfer_progress` structure is now `git_indexer_progress`.
+ The former names are deprecated.
+
+* The example projects are now contained in a single `lg2` executable
+ for ease of use.
+
+* libgit2 now correctly handles more URLs, such as
+ `http://example.com:/repo.git` (colon but no port),
+ `http://example.com` (no path),
+ and `http://example.com:8080/` (path is /, nonstandard port).
+
+* A carefully constructed commit object with a very large number
+ of parents may lead to potential out-of-bounds writes or
+ potential denial of service.
+
+* The ProgramData configuration file is always read for compatibility
+ with Git for Windows and Portable Git installations. The ProgramData
+ location is not necessarily writable only by administrators, so we
+ now ensure that the configuration file is owned by the administrator
+ or the current user.
+
+### API additions
+
+* The SSH host key now supports SHA-256 when `GIT_CERT_SSH_SHA256` is set.
+
+* The diff format option `GIT_DIFF_FORMAT_PATCH_ID` can now be used to
+ emit an output like `git patch-id`.
+
+* The `git_apply_options_init` function will initialize a
+ `git_apply_options` structure.
+
+* The remote callbacks structure adds a `git_url_resolve_cb` callback
+ that is invoked when connecting to a server, so that applications
+ may edit or replace the URL before connection.
+
+* The information about the original `HEAD` in a rebase operation is
+ available with `git_rebase_orig_head_name`. Its ID is available with
+ `git_rebase_orig_head_id`. The `onto` reference name is available with
+ `git_rebase_onto_name` and its ID is available with `git_rebase_onto_id`.
+
+* ODB backends can now free backend data when an error occurs during its
+ backend data creation using `git_odb_backend_data_free`.
+
+* Options may be specified to `git_repository_foreach_head` to control
+ its behavior: `GIT_REPOSITORY_FOREACH_HEAD_SKIP_REPO` will not skip
+ the main repository's HEAD reference, while
+ `GIT_REPOSITORY_FOREACH_HEAD_SKIP_WORKTREES` will now skip the
+ worktree HEAD references.
+
+* The `GIT_OPT_DISABLE_PACK_KEEP_FILE_CHECKS` option can be specified to
+ `git_libgit2_opts()` to avoid looking for `.keep` files that correspond
+ to packfiles. This setting can improve performance when packfiles are
+ stored on high-latency filesystems like network filesystems.
+
+* Blobs can now be filtered with `git_blob_filter`, which allows for
+ options to be set with `git_blob_filter_options`, including
+ `GIT_FILTER_NO_SYSTEM_ATTRIBUTES` to disable filtering with system-level
+ attributes in `/etc/gitattributes` and `GIT_ATTR_CHECK_INCLUDE_HEAD` to
+ enable filtering with `.gitattributes` files in the HEAD revision.
+
+### API removals
+
+* The unused `git_headlist_cb` function declaration was removed.
+
+* The unused `git_time_monotonic` API is removed.
+
+* The erroneously exported `inttypes.h` header was removed.
+
+# Security Fixes
+
+- CVE-2019-1348: the fast-import stream command "feature
+ export-marks=path" allows writing to arbitrary file paths. As
+ libgit2 does not offer any interface for fast-import, it is not
+ susceptible to this vulnerability.
+
+- CVE-2019-1349: by using NTFS 8.3 short names, backslashes or
+ alternate filesystreams, it is possible to cause submodules to
+ be written into pre-existing directories during a recursive
+ clone using git. As libgit2 rejects cloning into non-empty
+ directories by default, it is not susceptible to this
+ vulnerability.
+
+- CVE-2019-1350: recursive clones may lead to arbitrary remote
+ code executing due to improper quoting of command line
+ arguments. As libgit2 uses libssh2, which does not require us
+ to perform command line parsing, it is not susceptible to this
+ vulnerability.
+
+- CVE-2019-1351: Windows provides the ability to substitute
+ drive letters with arbitrary letters, including multi-byte
+ Unicode letters. To fix any potential issues arising from
+ interpreting such paths as relative paths, we have extended
+ detection of DOS drive prefixes to accomodate for such cases.
+
+- CVE-2019-1352: by using NTFS-style alternative file streams for
+ the ".git" directory, it is possible to overwrite parts of the
+ repository. While this has been fixed in the past for Windows,
+ the same vulnerability may also exist on other systems that
+ write to NTFS filesystems. We now reject any paths starting
+ with ".git:" on all systems.
+
+- CVE-2019-1353: by using NTFS-style 8.3 short names, it was
+ possible to write to the ".git" directory and thus overwrite
+ parts of the repository, leading to possible remote code
+ execution. While this problem was already fixed in the past for
+ Windows, other systems accessing NTFS filesystems are
+ vulnerable to this issue too. We now enable NTFS protecions by
+ default on all systems to fix this attack vector.
+
+- CVE-2019-1354: on Windows, backslashes are not a valid part of
+ a filename but are instead interpreted as directory separators.
+ As other platforms allowed to use such paths, it was possible
+ to write such invalid entries into a Git repository and was
+ thus an attack vector to write into the ".git" dierctory. We
+ now reject any entries starting with ".git\" on all systems.
+
+- CVE-2019-1387: it is possible to let a submodule's git
+ directory point into a sibling's submodule directory, which may
+ result in overwriting parts of the Git repository and thus lead
+ to arbitrary command execution. As libgit2 doesn't provide any
+ way to do submodule clones natively, it is not susceptible to
+ this vulnerability. Users of libgit2 that have implemented
+ recursive submodule clones manually are encouraged to review
+ their implementation for this vulnerability.
+
+### Breaking API changes
+
+* The "private" implementation details of the `git_cred` structure have been
+ moved to a dedicated `git2/sys/cred.h` header, to clarify that the underlying
+ structures are only provided for custom transport implementers.
+ The breaking change is that the `username` member of the underlying struct
+ is now hidden, and a new `git_cred_get_username` function has been provided.
+
+* Some errors of class `GIT_ERROR_NET` now have class `GIT_ERROR_HTTP`.
+ Most authentication failures now have error code `GIT_EAUTH` instead of `GIT_ERROR`.
+
+### Breaking CMake configuration changes
+
+* The CMake option to use a system http-parser library, instead of the
+ bundled dependency, has changed. This is due to a deficiency in
+ http-parser that we have fixed in our implementation. The bundled
+ library is now the default, but if you wish to force the use of the
+ system http-parser implementation despite incompatibilities, you can
+ specify `-DUSE_HTTP_PARSER=system` to CMake.
+
+* The interactions between `USE_HTTPS` and `SHA1_BACKEND` have been
+ streamlined. The detection was moved to a new `USE_SHA1`, modeled after
+ `USE_HTTPS`, which takes the values "CollisionDetection/Backend/Generic", to
+ better match how the "hashing backend" is selected, the default (ON) being
+ "CollisionDetection". If you were using `SHA1_BACKEND` previously, you'll
+ need to check the value you've used, or switch to the autodetection.
+
+### Authors
+
+The following individuals provided changes that were included in this
+release:
+
+* Aaron Patterson
+* Alberto Fanjul
+* Anders Borum
+* Augie Fackler
+* Augustin Fabre
+* Ayush Shridhar
+* brian m. carlson
+* buddyspike
+* Carlos Martín Nieto
+* cheese1
+* Dan Skorupski
+* Daniel Cohen Gindi
+* Dave Lee
+* David Brooks
+* David Turner
+* Denis Laxalde
+* Dhruva Krishnamurthy
+* Dominik Ritter
+* Drew DeVault
+* Edward Thomson
+* Eric Huss
+* Erik Aigner
+* Etienne Samson
+* Gregory Herrero
+* Heiko Voigt
+* Ian Hattendorf
+* Jacques Germishuys
+* Janardhan Pulivarthi
+* Jason Haslam
+* Johannes Schindelin
+* Jordan Wallet
+* Josh Bleecher Snyder
+* kas
+* kdj0c
+* Laurence McGlashan
+* lhchavez
+* Lukas Berk
+* Max Kostyukevich
+* Patrick Steinhardt
+* pcpthm
+* Remy Suen
+* Robert Coup
+* romkatv
+* Scott Furry
+* Sebastian Henke
+* Stefan Widgren
+* Steve King Jr
+* Sven Strickroth
+* Tobias Nießen
+* Tyler Ang-Wanek
+* Tyler Wanek
+
+---------------------------------------------------------------------
+
+v0.28
+-----
+
+### Changes or improvements
+
+* The library is now always built with cdecl calling conventions on
+ Windows; the ability to build a stdcall library has been removed.
+
+* Reference log creation now honors `core.logallrefupdates=always`.
+
+* Fix some issues with the error-reporting in the OpenSSL backend.
+
+* HTTP proxy support is now builtin; libcurl is no longer used to support
+ proxies and is removed as a dependency.
+
+* Certificate and credential callbacks can now return `GIT_PASSTHROUGH`
+ to decline to act; libgit2 will behave as if there was no callback set
+ in the first place.
+
+* The line-ending filtering logic - when checking out files - has been
+ updated to match newer git (>= git 2.9) for proper interoperability.
+
+* Symbolic links are now supported on Windows when `core.symlinks` is set
+ to `true`.
+
+* Submodules with names which attempt to perform path traversal now have their
+ configuration ignored. Such names were blindly appended to the
+ `$GIT_DIR/modules` and a malicious name could lead to an attacker writing to
+ an arbitrary location. This matches git's handling of CVE-2018-11235.
+
+* Object validation is now performed during tree creation in the
+ `git_index_write_tree_to` API.
+
+* Configuration variable may now be specified on the same line as a section
+ header; previously this was erroneously a parser error.
+
+* When an HTTP server supports both NTLM and Negotiate authentication
+ mechanisms, we would previously fail to authenticate with any mechanism.
+
+* The `GIT_OPT_SET_PACK_MAX_OBJECTS` option can now set the maximum
+ number of objects allowed in a packfile being downloaded; this can help
+ limit the maximum memory used when fetching from an untrusted remote.
+
+* Line numbers in diffs loaded from patch files were not being populated;
+ they are now included in the results.
+
+* The repository's index is reloaded from disk at the beginning of
+ `git_merge` operations to ensure that it is up-to-date.
+
+* Mailmap handling APIs have been introduced, and the new commit APIs
+ `git_commit_committer_with_mailmap` and `git_commit_author_with_mailmap`
+ will use the mailmap to resolve the committer and author information.
+ In addition, blame will use the mailmap given when the
+ `GIT_BLAME_USE_MAILMAP` option.
+
+* Ignore handling for files in ignored folders would be ignored.
+
+* Worktrees can now be backed by bare repositories.
+
+* Trailing spaces are supported in `.gitignore` files, these spaces were
+ previously (and erroneously) treated as part of the pattern.
+
+* The library can now be built with mbedTLS support for HTTPS.
+
+* The diff status character 'T' will now be presented by the
+ `git_diff_status_char` API for diff entries that change type.
+
+* Revision walks previously would sometimes include commits that should
+ have been ignored; this is corrected.
+
+* Revision walks are now more efficient when the output is unsorted;
+ we now avoid walking all the way to the beginning of history unnecessarily.
+
+* Error-handling around index extension loading has been fixed. We were
+ previously always misreporting a truncated index (#4858).
+
+### API additions
+
+* The index may now be iterated atomically using `git_index_iterator`.
+
+* Remote objects can now be created with extended options using the
+ `git_remote_create_with_opts` API.
+
+* Diff objects can now be applied as changes to the working directory,
+ index or both, emulating the `git apply` command. Additionally,
+ `git_apply_to_tree` can apply those changes to a tree object as a
+ fully in-memory operation.
+
+* You can now swap out memory allocators via the
+ `GIT_OPT_SET_ALLOCATOR` option with `git_libgit2_opts()`.
+
+* You can now ensure that functions do not discard unwritten changes to the
+ index via the `GIT_OPT_ENABLE_UNSAVED_INDEX_SAFETY` option to
+ `git_libgit2_opts()`. This will cause functions that implicitly re-read
+ the index (eg, `git_checkout`) to fail if you have staged changes to the
+ index but you have not written the index to disk. (Unless the checkout
+ has the FORCE flag specified.)
+
+ At present, this defaults to off, but we intend to enable this more
+ broadly in the future, as a warning or error. We encourage you to
+ examine your code to ensure that you are not relying on the current
+ behavior that implicitly removes staged changes.
+
+* Reference specifications can be parsed from an arbitrary string with
+ the `git_refspec_parse` API.
+
+* You can now get the name and path of worktrees using the
+ `git_worktree_name` and `git_worktree_path` APIs, respectively.
+
+* The `ref` field has been added to `git_worktree_add_options` to enable
+ the creation of a worktree from a pre-existing branch.
+
+* It's now possible to analyze merge relationships between any two
+ references, not just against `HEAD`, using `git_merge_analysis_for_ref`.
+
+### API removals
+
+* The `git_buf_free` API is deprecated; it has been renamed to
+ `git_buf_dispose` for consistency. The `git_buf_free` API will be
+ retained for backward compatibility for the foreseeable future.
+
+* The `git_otype` enumeration and its members are deprecated and have
+ been renamed for consistency. The `GIT_OBJ_` enumeration values are
+ now prefixed with `GIT_OBJECT_`. The old enumerations and macros
+ will be retained for backward compatibility for the foreseeable future.
+
+* Several index-related APIs have been renamed for consistency. The
+ `GIT_IDXENTRY_` enumeration values and macros have been renamed to
+ be prefixed with `GIT_INDEX_ENTRY_`. The `GIT_INDEXCAP` enumeration
+ values are now prefixed with `GIT_INDEX_CAPABILITY_`. The old
+ enumerations and macros will be retained for backward compatibility
+ for the foreseeable future.
+
+* The error functions and enumeration values have been renamed for
+ consistency. The `giterr_` functions and values prefix have been
+ renamed to be prefixed with `git_error_`; similarly, the `GITERR_`
+ constants have been renamed to be prefixed with `GIT_ERROR_`.
+ The old enumerations and macros will be retained for backward
+ compatibility for the foreseeable future.
+
+### Breaking API changes
+
+* The default checkout strategy changed from `DRY_RUN` to `SAFE` (#4531).
+
+* Adding a symlink as .gitmodules into the index from the workdir or checking
+ out such files is not allowed as this can make a Git implementation write
+ outside of the repository and bypass the fsck checks for CVE-2018-11235.
+
+---------------------------------------------------------------------
+
+v0.27
+---------
+
+### Changes or improvements
+
+* Improved `p_unlink` in `posix_w32.c` to try and make a file writable
+ before sleeping in the retry loop to prevent unnecessary calls to sleep.
+
+* The CMake build infrastructure has been improved to speed up building time.
+
+* A new CMake option "-DUSE_HTTPS=<backend>" makes it possible to explicitly
+ choose an HTTP backend.
+
+* A new CMake option "-DSHA1_BACKEND=<backend>" makes it possible to explicitly
+ choose an SHA1 backend. The collision-detecting backend is now the default.
+
+* A new CMake option "-DUSE_BUNDLED_ZLIB" makes it possible to explicitly use
+ the bundled zlib library.
+
+* A new CMake option "-DENABLE_REPRODUCIBLE_BUILDS" makes it possible to
+ generate a reproducible static archive. This requires support from your
+ toolchain.
+
+* The minimum required CMake version has been bumped to 2.8.11.
+
+* Writing to a configuration file now preserves the case of the key given by the
+ caller for the case-insensitive portions of the key (existing sections are
+ used even if they don't match).
+
+* We now support conditional includes in configuration files.
+
+* Fix for handling re-reading of configuration files with includes.
+
+* Fix for reading patches which contain exact renames only.
+
+* Fix for reading patches with whitespace in the compared files' paths.
+
+* We will now fill `FETCH_HEAD` from all passed refspecs instead of overwriting
+ with the last one.
+
+* There is a new diff option, `GIT_DIFF_INDENT_HEURISTIC` which activates a
+ heuristic which takes into account whitespace and indentation in order to
+ produce better diffs when dealing with ambiguous diff hunks.
+
+* Fix for pattern-based ignore rules where files ignored by a rule cannot be
+ un-ignored by another rule.
+
+* Sockets opened by libgit2 are now being closed on exec(3) if the platform
+ supports it.
+
+* Fix for peeling annotated tags from packed-refs files.
+
+* Fix reading huge loose objects from the object database.
+
+* Fix files not being treated as modified when only the file mode has changed.
+
+* We now explicitly reject adding submodules to the index via
+ `git_index_add_frombuffer`.
+
+* Fix handling of `GIT_DIFF_FIND_RENAMES_FROM_REWRITES` raising `SIGABRT` when
+ one file has been deleted and another file has been rewritten.
+
+* Fix for WinHTTP not properly handling NTLM and Negotiate challenges.
+
+* When using SSH-based transports, we now repeatedly ask for the passphrase to
+ decrypt the private key in case a wrong passphrase is being provided.
+
+* When generating conflict markers, they will now use the same line endings as
+ the rest of the file.
+
+### API additions
+
+* The `git_merge_file_options` structure now contains a new setting,
+ `marker_size`. This allows users to set the size of markers that
+ delineate the sides of merged files in the output conflict file.
+ By default this is 7 (`GIT_MERGE_CONFLICT_MARKER_SIZE`), which
+ produces output markers like `<<<<<<<` and `>>>>>>>`.
+
+* `git_remote_create_detached()` creates a remote that is not associated
+ to any repository (and does not apply configuration like 'insteadof' rules).
+ This is mostly useful for e.g. emulating `git ls-remote` behavior.
+
+* `git_diff_patchid()` lets you generate patch IDs for diffs.
+
+* `git_status_options` now has an additional field `baseline` to allow creating
+ status lists against different trees.
+
+* New family of functions to allow creating notes for a specific notes commit
+ instead of for a notes reference.
+
+* New family of functions to allow parsing message trailers. This API is still
+ experimental and may change in future releases.
+
+### API removals
+
+### Breaking API changes
+
+* Signatures now distinguish between +0000 and -0000 UTC offsets.
+
+* The certificate check callback in the WinHTTP transport will now receive the
+ `message_cb_payload` instead of the `cred_acquire_payload`.
+
+* We are now reading symlinked directories under .git/refs.
+
+* We now refuse creating branches named "HEAD".
+
+* We now refuse reading and writing all-zero object IDs into the
+ object database.
+
+* We now read the effective user's configuration file instead of the real user's
+ configuration in case libgit2 runs as part of a setuid binary.
+
+* The `git_odb_open_rstream` function and its `readstream` callback in the
+ `git_odb_backend` interface have changed their signatures to allow providing
+ the object's size and type to the caller.
+
+---------------------------------------------------------------------
+
+v0.26
+-----
+
+### Changes or improvements
+
+* Support for opening, creating and modifying worktrees.
+
+* We can now detect SHA1 collisions resulting from the SHAttered attack. These
+ checks can be enabled at build time via `-DUSE_SHA1DC`.
+
+* Fix for missing implementation of `git_merge_driver_source` getters.
+
+* Fix for installed pkg-config file being broken when the prefix contains
+ spaces.
+
+* We now detect when the hashsum of on-disk objects does not match their
+ expected hashsum.
+
+* We now support open-ended ranges (e.g. "master..", "...master") in our
+ revision range parsing code.
+
+* We now correctly compute ignores with leading "/" in subdirectories.
+
+* We now optionally call `fsync` on loose objects, packfiles and their indexes,
+ loose references and packed reference files.
+
+* We can now build against OpenSSL v1.1 and against LibreSSL.
+
+* `GIT_MERGE_OPTIONS_INIT` now includes a setting to perform rename detection.
+ This aligns this structure with the default by `git_merge` and
+ `git_merge_trees` when `NULL` was provided for the options.
+
+* Improvements for reading index v4 files.
+
+* Perform additional retries for filesystem operations on Windows when files
+ are temporarily locked by other processes.
+
+### API additions
+
+* New family of functions to handle worktrees:
+
+ * `git_worktree_list()` lets you look up worktrees for a repository.
+ * `git_worktree_lookup()` lets you get a specific worktree.
+ * `git_worktree_open_from_repository()` lets you get the associated worktree
+ of a repository.
+ a worktree.
+ * `git_worktree_add` lets you create new worktrees.
+ * `git_worktree_prune` lets you remove worktrees from disk.
+ * `git_worktree_lock()` and `git_worktree_unlock()` let you lock
+ respectively unlock a worktree.
+ * `git_repository_open_from_worktree()` lets you open a repository via
+ * `git_repository_head_for_worktree()` lets you get the current `HEAD` for a
+ linked worktree.
+ * `git_repository_head_detached_for_worktree()` lets you check whether a
+ linked worktree is in detached HEAD mode.
+
+* `git_repository_item_path()` lets you retrieve paths for various repository
+ files.
+
+* `git_repository_commondir()` lets you retrieve the common directory of a
+ repository.
+
+* `git_branch_is_checked_out()` allows you to check whether a branch is checked
+ out in a repository or any of its worktrees.
+
+* `git_repository_submodule_cache_all()` and
+ `git_repository_submodule_cache_clear()` functions allow you to prime or clear
+ the submodule cache of a repository.
+
+* You can disable strict hash verifications via the
+ `GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION` option with `git_libgit2_opts()`.
+
+* You can enable us calling `fsync` for various files inside the ".git"
+ directory by setting the `GIT_OPT_ENABLE_FSYNC_GITDIR` option with
+ `git_libgit2_opts()`.
+
+* You can now enable "offset deltas" when creating packfiles and negotiating
+ packfiles with a remote server by setting `GIT_OPT_ENABLE_OFS_DELTA` option
+ with `GIT_libgit2_opts()`.
+
+* You can now set the default share mode on Windows for opening files using
+ `GIT_OPT_SET_WINDOWS_SHAREMODE` option with `git_libgit2_opts()`.
+ You can query the current share mode with `GIT_OPT_GET_WINDOWS_SHAREMODE`.
+
+* `git_transport_smart_proxy_options()' enables you to get the proxy options for
+ smart transports.
+
+* The `GIT_FILTER_INIT` macro and the `git_filter_init` function are provided
+ to initialize a `git_filter` structure.
+
+### Breaking API changes
+
+* `clone_checkout_strategy` has been removed from
+ `git_submodule_update_option`. The checkout strategy used to clone will
+ be the same strategy specified in `checkout_opts`.
+
+v0.25
+-------
+
+### Changes or improvements
+
+* Fix repository discovery with `git_repository_discover` and
+ `git_repository_open_ext` to match git's handling of a ceiling
+ directory at the current directory. git only checks ceiling
+ directories when its search ascends to a parent directory. A ceiling
+ directory matching the starting directory will not prevent git from
+ finding a repository in the starting directory or a parent directory.
+
+* Do not fail when deleting remotes in the presence of broken
+ global configs which contain branches.
+
+* Support for reading and writing git index v4 files
+
+* Improve the performance of the revwalk and bring us closer to git's code.
+
+* The reference db has improved support for concurrency and returns `GIT_ELOCKED`
+ when an operation could not be performed due to locking.
+
+* Nanosecond resolution is now activated by default, following git's change to
+ do this.
+
+* We now restrict the set of ciphers we let OpenSSL use by default.
+
+* Users can now register their own merge drivers for use with `.gitattributes`.
+ The library also gained built-in support for the union merge driver.
+
+* The default for creating references is now to validate that the object does
+ exist.
+
+* Add `git_proxy_options` which is used by the different networking
+ implementations to let the caller specify the proxy settings instead of
+ relying on the environment variables.
+
+### API additions
+
+* You can now get the user-agent used by libgit2 using the
+ `GIT_OPT_GET_USER_AGENT` option with `git_libgit2_opts()`.
+ It is the counterpart to `GIT_OPT_SET_USER_AGENT`.
+
+* The `GIT_OPT_SET_SSL_CIPHERS` option for `git_libgit2_opts()` lets you specify
+ a custom list of ciphers to use for OpenSSL.
+
+* `git_commit_create_buffer()` creates a commit and writes it into a
+ user-provided buffer instead of writing it into the object db. Combine it with
+ `git_commit_create_with_signature()` in order to create a commit with a
+ cryptographic signature.
+
+* `git_blob_create_fromstream()` and
+ `git_blob_create_fromstream_commit()` allow you to create a blob by
+ writing into a stream. Useful when you do not know the final size or
+ want to copy the contents from another stream.
+
+* New flags for `git_repository_open_ext`:
+
+ * `GIT_REPOSITORY_OPEN_NO_DOTGIT` - Do not check for a repository by
+ appending `/.git` to the `start_path`; only open the repository if
+ `start_path` itself points to the git directory.
+ * `GIT_REPOSITORY_OPEN_FROM_ENV` - Find and open a git repository,
+ respecting the environment variables used by the git command-line
+ tools. If set, `git_repository_open_ext` will ignore the other
+ flags and the `ceiling_dirs` argument, and will allow a NULL
+ `path` to use `GIT_DIR` or search from the current directory. The
+ search for a repository will respect `$GIT_CEILING_DIRECTORIES`
+ and `$GIT_DISCOVERY_ACROSS_FILESYSTEM`. The opened repository
+ will respect `$GIT_INDEX_FILE`, `$GIT_NAMESPACE`,
+ `$GIT_OBJECT_DIRECTORY`, and `$GIT_ALTERNATE_OBJECT_DIRECTORIES`.
+ In the future, this flag will also cause `git_repository_open_ext`
+ to respect `$GIT_WORK_TREE` and `$GIT_COMMON_DIR`; currently,
+ `git_repository_open_ext` with this flag will error out if either
+ `$GIT_WORK_TREE` or `$GIT_COMMON_DIR` is set.
+
+* `git_diff_from_buffer()` can create a `git_diff` object from the contents
+ of a git-style patch file.
+
+* `git_index_version()` and `git_index_set_version()` to get and set
+ the index version
+
+* `git_odb_expand_ids()` lets you check for the existence of multiple
+ objects at once.
+
+* The new `git_blob_dup()`, `git_commit_dup()`, `git_tag_dup()` and
+ `git_tree_dup()` functions provide type-specific wrappers for
+ `git_object_dup()` to reduce noise and increase type safety for callers.
+
+* `git_reference_dup()` lets you duplicate a reference to aid in ownership
+ management and cleanup.
+
+* `git_signature_from_buffer()` lets you create a signature from a string in the
+ format that appear in objects.
+
+* `git_tree_create_updated()` lets you create a tree based on another one
+ together with a list of updates. For the covered update cases, it's more
+ efficient than the `git_index` route.
+
+* `git_apply_patch()` applies hunks from a `git_patch` to a buffer.
+
+* `git_diff_to_buf()` lets you print an entire diff directory to a buffer,
+ similar to how `git_patch_to_buf()` works.
+
+* `git_proxy_init_options()` is added to initialize a `git_proxy_options`
+ structure at run-time.
+
+* `git_merge_driver_register()`, `git_merge_driver_unregister()` let you
+ register and unregister a custom merge driver to be used when `.gitattributes`
+ specifies it.
+
+* `git_merge_driver_lookup()` can be used to look up a merge driver by name.
+
+* `git_merge_driver_source_repo()`, `git_merge_driver_source_ancestor()`,
+ `git_merge_driver_source_ours()`, `git_merge_driver_source_theirs()`,
+ `git_merge_driver_source_file_options()` added as accessors to
+ `git_merge_driver_source`.
+
+### API removals
+
+* `git_blob_create_fromchunks()` has been removed in favour of
+ `git_blob_create_fromstream()`.
+
+### Breaking API changes
+
+* `git_packbuilder_object_count` and `git_packbuilder_written` now
+ return a `size_t` instead of a `uint32_t` for more thorough
+ compatibility with the rest of the library.
+
+* `git_packbuiler_progress` now provides explicitly sized `uint32_t`
+ values instead of `unsigned int`.
+
+* `git_diff_file` now includes an `id_abbrev` field that reflects the
+ number of nibbles set in the `id` field.
+
+* `git_odb_backend` now has a `freshen` function pointer. This optional
+ function pointer is similar to the `exists` function, but it will update
+ a last-used marker. For filesystem-based object databases, this updates
+ the timestamp of the file containing the object, to indicate "freshness".
+ If this is `NULL`, then it will not be called and the `exists` function
+ will be used instead.
+
+* `git_remote_connect()` now accepts `git_proxy_options` argument, and
+ `git_fetch_options` and `git_push_options` each have a `proxy_opts` field.
+
+* `git_merge_options` now provides a `default_driver` that can be used
+ to provide the name of a merge driver to be used to handle files changed
+ during a merge.
+
+---------------------------------------------------------------------
+
+v0.24
+-------
+
+### Changes or improvements
+
+* Custom merge drivers can now be registered, which allows callers to
+ configure callbacks to honor `merge=driver` configuration in
+ `.gitattributes`.
+
+* Custom filters can now be registered with wildcard attributes, for
+ example `filter=*`. Consumers should examine the attributes parameter
+ of the `check` function for details.
+
+* Symlinks are now followed when locking a file, which can be
+ necessary when multiple worktrees share a base repository.
+
+* You can now set your own user-agent to be sent for HTTP requests by
+ using the `GIT_OPT_SET_USER_AGENT` with `git_libgit2_opts()`.
+
+* You can set custom HTTP header fields to be sent along with requests
+ by passing them in the fetch and push options.
+
+* Tree objects are now assumed to be sorted. If a tree is not
+ correctly formed, it will give bad results. This is the git approach
+ and cuts a significant amount of time when reading the trees.
+
+* Filter registration is now protected against concurrent
+ registration.
+
+* Filenames which are not valid on Windows in an index no longer cause
+ to fail to parse it on that OS.
+
+* Rebases can now be performed purely in-memory, without touching the
+ repository's workdir.
+
+* When adding objects to the index, or when creating new tree or commit
+ objects, the inputs are validated to ensure that the dependent objects
+ exist and are of the correct type. This object validation can be
+ disabled with the GIT_OPT_ENABLE_STRICT_OBJECT_CREATION option.
+
+* The WinHTTP transport's handling of bad credentials now behaves like
+ the others, asking for credentials again.
+
+### API additions
+
+* `git_config_lock()` has been added, which allow for
+ transactional/atomic complex updates to the configuration, removing
+ the opportunity for concurrent operations and not committing any
+ changes until the unlock.
+
+* `git_diff_options` added a new callback `progress_cb` to report on the
+ progress of the diff as files are being compared. The documentation of
+ the existing callback `notify_cb` was updated to reflect that it only
+ gets called when new deltas are added to the diff.
+
+* `git_fetch_options` and `git_push_options` have gained a `custom_headers`
+ field to set the extra HTTP header fields to send.
+
+* `git_stream_register_tls()` lets you register a callback to be used
+ as the constructor for a TLS stream instead of the libgit2 built-in
+ one.
+
+* `git_commit_header_field()` allows you to look up a specific header
+ field in a commit.
+
+* `git_commit_extract_signature()` extracts the signature from a
+ commit and gives you both the signature and the signed data so you
+ can verify it.
+
+### API removals
+
+* No APIs were removed in this version.
+
+### Breaking API changes
+
+* The `git_merge_tree_flag_t` is now `git_merge_flag_t`. Subsequently,
+ its members are no longer prefixed with `GIT_MERGE_TREE_FLAG` but are
+ now prefixed with `GIT_MERGE_FLAG`, and the `tree_flags` field of the
+ `git_merge_options` structure is now named `flags`.
+
+* The `git_merge_file_flags_t` enum is now `git_merge_file_flag_t` for
+ consistency with other enum type names.
+
+* `git_cert` descendent types now have a proper `parent` member
+
+* It is the responsibility of the refdb backend to decide what to do
+ with the reflog on ref deletion. The file-based backend must delete
+ it, a database-backed one may wish to archive it.
+
+* `git_config_backend` has gained two entries. `lock` and `unlock`
+ with which to implement the transactional/atomic semantics for the
+ configuration backend.
+
+* `git_index_add` and `git_index_conflict_add()` will now use the case
+ as provided by the caller on case insensitive systems. Previous
+ versions would keep the case as it existed in the index. This does
+ not affect the higher-level `git_index_add_bypath` or
+ `git_index_add_frombuffer` functions.
+
+* The `notify_payload` field of `git_diff_options` was renamed to `payload`
+ to reflect that it's also the payload for the new progress callback.
+
+* The `git_config_level_t` enum has gained a higher-priority value
+ `GIT_CONFIG_LEVEL_PROGRAMDATA` which represent a rough Windows equivalent
+ to the system level configuration.
+
+* `git_rebase_options` now has a `merge_options` field.
+
+* The index no longer performs locking itself. This is not something
+ users of the library should have been relying on as it's not part of
+ the concurrency guarantees.
+
+* `git_remote_connect()` now takes a `custom_headers` argument to set
+ the extra HTTP header fields to send.
+
+---------------------------------------------------------------------
+
+v0.23
+------
+
+### Changes or improvements
+
+* Patience and minimal diff drivers can now be used for merges.
+
+* Merges can now ignore whitespace changes.
+
+* Updated binary identification in CRLF filtering to avoid false positives in
+ UTF-8 files.
+
+* Rename and copy detection is enabled for small files.
+
+* Checkout can now handle an initial checkout of a repository, making
+ `GIT_CHECKOUT_SAFE_CREATE` unnecessary for users of clone.
+
+* The signature parameter in the ref-modifying functions has been
+ removed. Use `git_repository_set_ident()` and
+ `git_repository_ident()` to override the signature to be used.
+
+* The local transport now auto-scales the number of threads to use
+ when creating the packfile instead of sticking to one.
+
+* Reference renaming now uses the right id for the old value.
+
+* The annotated version of branch creation, HEAD detaching and reset
+ allow for specifying the expression from the user to be put into the
+ reflog.
+
+* `git_rebase_commit` now returns `GIT_EUNMERGED` when you attempt to
+ commit with unstaged changes.
+
+* On Mac OS X, we now use SecureTransport to provide the cryptographic
+ support for HTTPS connections insead of OpenSSL.
+
+* Checkout can now accept an index for the baseline computations via the
+ `baseline_index` member.
+
+* The configuration for fetching is no longer stored inside the
+ `git_remote` struct but has been moved to a `git_fetch_options`. The
+ remote functions now take these options or the callbacks instead of
+ setting them beforehand.
+
+* `git_submodule` instances are no longer cached or shared across
+ lookup. Each submodule represents the configuration at the time of
+ loading.
+
+* The index now uses diffs for `add_all()` and `update_all()` which
+ gives it a speed boost and closer semantics to git.
+
+* The ssh transport now reports the stderr output from the server as
+ the error message, which allows you to get the "repository not
+ found" messages.
+
+* `git_index_conflict_add()` will remove staged entries that exist for
+ conflicted paths.
+
+* The flags for a `git_diff_file` will now have the `GIT_DIFF_FLAG_EXISTS`
+ bit set when a file exists on that side of the diff. This is useful
+ for understanding whether a side of the diff exists in the presence of
+ a conflict.
+
+* The constructor for a write-stream into the odb now takes
+ `git_off_t` instead of `size_t` for the size of the blob, which
+ allows putting large files into the odb on 32-bit systems.
+
+* The remote's push and pull URLs now honor the url.$URL.insteadOf
+ configuration. This allows modifying URL prefixes to a custom
+ value via gitconfig.
+
+* `git_diff_foreach`, `git_diff_blobs`, `git_diff_blob_to_buffer`,
+ and `git_diff_buffers` now accept a new binary callback of type
+ `git_diff_binary_cb` that includes the binary diff information.
+
+* The race condition mitigations described in `racy-git.txt` have been
+ implemented.
+
+* If libcurl is installed, we will use it to connect to HTTP(S)
+ servers.
+
+### API additions
+
+* The `git_merge_options` gained a `file_flags` member.
+
+* Parsing and retrieving a configuration value as a path is exposed
+ via `git_config_parse_path()` and `git_config_get_path()`
+ respectively.
+
+* `git_repository_set_ident()` and `git_repository_ident()` serve to
+ set and query which identity will be used when writing to the
+ reflog.
+
+* `git_config_entry_free()` frees a config entry.
+
+* `git_config_get_string_buf()` provides a way to safely retrieve a
+ string from a non-snapshot configuration.
+
+* `git_annotated_commit_from_revspec()` allows to get an annotated
+ commit from an extended sha synatx string.
+
+* `git_repository_set_head_detached_from_annotated()`,
+ `git_branch_create_from_annotated()` and
+ `git_reset_from_annotated()` allow for the caller to provide an
+ annotated commit through which they can control what expression is
+ put into the reflog as the source/target.
+
+* `git_index_add_frombuffer()` can now create a blob from memory
+ buffer and add it to the index which is attached to a repository.
+
+* The structure `git_fetch_options` has been added to determine the
+ runtime configuration for fetching, such as callbacks, pruning and
+ autotag behaviour. It has the runtime initializer
+ `git_fetch_init_options()`.
+
+* The enum `git_fetch_prune_t` has been added, letting you specify the
+ pruning behaviour for a fetch.
+
+* A push operation will notify the caller of what updates it indends
+ to perform on the remote, which provides similar information to
+ git's pre-push hook.
+
+* `git_stash_apply()` can now apply a stashed state from the stash list,
+ placing the data into the working directory and index.
+
+* `git_stash_pop()` will apply a stashed state (like `git_stash_apply()`)
+ but will remove the stashed state after a successful application.
+
+* A new error code `GIT_EEOF` indicates an early EOF from the
+ server. This typically indicates an error with the URL or
+ configuration of the server, and tools can use this to show messages
+ about failing to communicate with the server.
+
+* A new error code `GIT_EINVALID` indicates that an argument to a
+ function is invalid, or an invalid operation was requested.
+
+* `git_diff_index_to_workdir()` and `git_diff_tree_to_index()` will now
+ produce deltas of type `GIT_DELTA_CONFLICTED` to indicate that the index
+ side of the delta is a conflict.
+
+* The `git_status` family of functions will now produce status of type
+ `GIT_STATUS_CONFLICTED` to indicate that a conflict exists for that file
+ in the index.
+
+* `git_index_entry_is_conflict()` is a utility function to determine if
+ a given index entry has a non-zero stage entry, indicating that it is
+ one side of a conflict.
+
+* It is now possible to pass a keypair via a buffer instead of a
+ path. For this, `GIT_CREDTYPE_SSH_MEMORY` and
+ `git_cred_ssh_key_memory_new()` have been added.
+
+* `git_filter_list_contains` will indicate whether a particular
+ filter will be run in the given filter list.
+
+* `git_commit_header_field()` has been added, which allows retrieving
+ the contents of an arbitrary header field.
+
+* `git_submodule_set_branch()` allows to set the configured branch for
+ a submodule.
+
+### API removals
+
+* `git_remote_save()` and `git_remote_clear_refspecs()` have been
+ removed. Remote's configuration is changed via the configuration
+ directly or through a convenience function which performs changes to
+ the configuration directly.
+
+* `git_remote_set_callbacks()`, `git_remote_get_callbacks()` and
+ `git_remote_set_transport()` have been removed and the remote no
+ longer stores this configuration.
+
+* `git_remote_set_fetch_refpecs()` and
+ `git_remote_set_push_refspecs()` have been removed. There is no
+ longer a way to set the base refspecs at run-time.
+
+* `git_submodule_save()` has been removed. The submodules are no
+ longer configured via the objects.
+
+* `git_submodule_reload_all()` has been removed as we no longer cache
+ submodules.
+
+### Breaking API changes
+
+* `git_smart_subtransport_cb` now has a `param` parameter.
+
+* The `git_merge_options` structure member `flags` has been renamed
+ to `tree_flags`.
+
+* The `git_merge_file_options` structure member `flags` is now
+ an unsigned int. It was previously a `git_merge_file_flags_t`.
+
+* `GIT_CHECKOUT_SAFE_CREATE` has been removed. Most users will generally
+ be able to switch to `GIT_CHECKOUT_SAFE`, but if you require missing
+ file handling during checkout, you may now use `GIT_CHECKOUT_SAFE |
+ GIT_CHECKOUT_RECREATE_MISSING`.
+
+* The `git_clone_options` and `git_submodule_update_options`
+ structures no longer have a `signature` field.
+
+* The following functions have removed the signature and/or log message
+ parameters in favour of git-emulating ones.
+
+ * `git_branch_create()`, `git_branch_move()`
+ * `git_rebase_init()`, `git_rebase_abort()`
+ * `git_reference_symbolic_create_matching()`,
+ `git_reference_symbolic_create()`, `git_reference_create()`,
+ `git_reference_create_matching()`,
+ `git_reference_symbolic_set_target()`,
+ `git_reference_set_target()`, `git_reference_rename()`
+ * `git_remote_update_tips()`, `git_remote_fetch()`, `git_remote_push()`
+ * `git_repository_set_head()`,
+ `git_repository_set_head_detached()`,
+ `git_repository_detach_head()`
+ * `git_reset()`
+
+* `git_config_get_entry()` now gives back a ref-counted
+ `git_config_entry`. You must free it when you no longer need it.
+
+* `git_config_get_string()` will return an error if used on a
+ non-snapshot configuration, as there can be no guarantee that the
+ returned pointer is valid.
+
+* `git_note_default_ref()` now uses a `git_buf` to return the string,
+ as the string is otherwise not guaranteed to stay allocated.
+
+* `git_rebase_operation_current()` will return `GIT_REBASE_NO_OPERATION`
+ if it is called immediately after creating a rebase session but before
+ you have applied the first patch.
+
+* `git_rebase_options` now contains a `git_checkout_options` struct
+ that will be used for functions that modify the working directory,
+ namely `git_rebase_init`, `git_rebase_next` and
+ `git_rebase_abort`. As a result, `git_rebase_open` now also takes
+ a `git_rebase_options` and only the `git_rebase_init` and
+ `git_rebase_open` functions take a `git_rebase_options`, where they
+ will persist the options to subsequent `git_rebase` calls.
+
+* The `git_clone_options` struct now has fetch options in a
+ `fetch_opts` field instead of remote callbacks in
+ `remote_callbacks`.
+
+* The remote callbacks has gained a new member `push_negotiation`
+ which gets called before sending the update commands to the server.
+
+* The following functions no longer act on a remote instance but
+ change the repository's configuration. Their signatures have changed
+ accordingly:
+
+ * `git_remote_set_url()`, `git_remote_seturl()`
+ * `git_remote_add_fetch()`, `git_remote_add_push()` and
+ * `git_remote_set_autotag()`
+
+* `git_remote_connect()` and `git_remote_prune()` now take a pointer
+ to the callbacks.
+
+* `git_remote_fetch()` and `git_remote_download()` now take a pointer
+ to fetch options which determine the runtime configuration.
+
+* The `git_remote_autotag_option_t` values have been changed. It has
+ gained a `_UNSPECIFIED` default value to specify no override for the
+ configured setting.
+
+* `git_remote_update_tips()` now takes a pointer to the callbacks as
+ well as a boolean whether to write `FETCH_HEAD` and the autotag
+ setting.
+
+* `git_remote_create_anonymous()` no longer takes a fetch refspec as
+ url-only remotes cannot have configured refspecs.
+
+* The `git_submodule_update_options` struct now has fetch options in
+ the `fetch_opts` field instead of callbacks in the
+ `remote_callbacks` field.
+
+* The following functions no longer act on a submodule instance but
+ change the repository's configuration. Their signatures have changed
+ accordingly:
+
+ * `git_submodule_set_url()`, `git_submodule_set_ignore()`,
+ `git_submodule_set_update()`,
+ `git_submodule_set_fetch_recurse_submodules()`.
+
+* `git_submodule_status()` no longer takes a submodule instance but a
+ repsitory, a submodule name and an ignore setting.
+
+* The `push` function in the `git_transport` interface now takes a
+ pointer to the remote callbacks.
+
+* The `git_index_entry` struct's fields' types have been changed to
+ more accurately reflect what is in fact stored in the
+ index. Specifically, time and file size are 32 bits intead of 64, as
+ these values are truncated.
+
+* `GIT_EMERGECONFLICT` is now `GIT_ECONFLICT`, which more accurately
+ describes the nature of the error.
+
+* It is no longer allowed to call `git_buf_grow()` on buffers
+ borrowing the memory they point to.
+
+---------------------------------------------------------------------
+
+v0.22
+------
+
+### Changes or improvements
+
+* `git_signature_new()` now requires a non-empty email address.
+
+* Use CommonCrypto libraries for SHA-1 calculation on Mac OS X.
+
+* Disable SSL compression and SSLv2 and SSLv3 ciphers in favor of TLSv1
+ in OpenSSL.
+
+* The fetch behavior of remotes with autotag set to `GIT_REMOTE_DOWNLOAD_TAGS_ALL`
+ has been changed to match git 1.9.0 and later. In this mode, libgit2 now
+ fetches all tags in addition to whatever else needs to be fetched.
+
+* `git_checkout()` now handles case-changing renames correctly on
+ case-insensitive filesystems; for example renaming "readme" to "README".
+
+* The search for libssh2 is now done via pkg-config instead of a
+ custom search of a few directories.
+
+* Add support for core.protectHFS and core.protectNTFS. Add more
+ validation for filenames which we write such as references.
+
+* The local transport now generates textual progress output like
+ git-upload-pack does ("counting objects").
+
+* `git_checkout_index()` can now check out an in-memory index that is not
+ necessarily the repository's index, so you may check out an index
+ that was produced by git_merge and friends while retaining the cached
+ information.
+
+* Remove the default timeout for receiving / sending data over HTTP using
+ the WinHTTP transport layer.
+
+* Add SPNEGO (Kerberos) authentication using GSSAPI on Unix systems.
+
+* Provide built-in objects for the empty blob (e69de29) and empty
+ tree (4b825dc) objects.
+
+* The index' tree cache is now filled upon read-tree and write-tree
+ and the cache is written to disk.
+
+* LF -> CRLF filter refuses to handle mixed-EOL files
+
+* LF -> CRLF filter now runs when * text = auto (with Git for Windows 1.9.4)
+
+* File unlocks are atomic again via rename. Read-only files on Windows are
+ made read-write if necessary.
+
+* Share open packfiles across repositories to share descriptors and mmaps.
+
+* Use a map for the treebuilder, making insertion O(1)
+
+* The build system now accepts an option EMBED_SSH_PATH which when set
+ tells it to include a copy of libssh2 at the given location. This is
+ enabled for MSVC.
+
+* Add support for refspecs with the asterisk in the middle of a
+ pattern.
+
+* Fetching now performs opportunistic updates. To achieve this, we
+ introduce a difference between active and passive refspecs, which
+ make `git_remote_download()` and `git_remote_fetch()` to take a list of
+ resfpecs to be the active list, similarly to how git fetch accepts a
+ list on the command-line.
+
+* The THREADSAFE option to build libgit2 with threading support has
+ been flipped to be on by default.
+
+* The remote object has learnt to prune remote-tracking branches. If
+ the remote is configured to do so, this will happen via
+ `git_remote_fetch()`. You can also call `git_remote_prune()` after
+ connecting or fetching to perform the prune.
+
+
+### API additions
+
+* Introduce `git_buf_text_is_binary()` and `git_buf_text_contains_nul()` for
+ consumers to perform binary detection on a git_buf.
+
+* `git_branch_upstream_remote()` has been introduced to provide the
+ branch.<name>.remote configuration value.
+
+* Introduce `git_describe_commit()` and `git_describe_workdir()` to provide
+ a description of the current commit (and working tree, respectively)
+ based on the nearest tag or reference
+
+* Introduce `git_merge_bases()` and the `git_oidarray` type to expose all
+ merge bases between two commits.
+
+* Introduce `git_merge_bases_many()` to expose all merge bases between
+ multiple commits.
+
+* Introduce rebase functionality (using the merge algorithm only).
+ Introduce `git_rebase_init()` to begin a new rebase session,
+ `git_rebase_open()` to open an in-progress rebase session,
+ `git_rebase_commit()` to commit the current rebase operation,
+ `git_rebase_next()` to apply the next rebase operation,
+ `git_rebase_abort()` to abort an in-progress rebase and `git_rebase_finish()`
+ to complete a rebase operation.
+
+* Introduce `git_note_author()` and `git_note_committer()` to get the author
+ and committer information on a `git_note`, respectively.
+
+* A factory function for ssh has been added which allows to change the
+ path of the programs to execute for receive-pack and upload-pack on
+ the server, `git_transport_ssh_with_paths()`.
+
+* The ssh transport supports asking the remote host for accepted
+ credential types as well as multiple challeges using a single
+ connection. This requires to know which username you want to connect
+ as, so this introduces the USERNAME credential type which the ssh
+ transport will use to ask for the username.
+
+* The `GIT_EPEEL` error code has been introduced when we cannot peel a tag
+ to the requested object type; if the given object otherwise cannot be
+ peeled, `GIT_EINVALIDSPEC` is returned.
+
+* Introduce `GIT_REPOSITORY_INIT_RELATIVE_GITLINK` to use relative paths
+ when writing gitlinks, as is used by git core for submodules.
+
+* `git_remote_prune()` has been added. See above for description.
+
+
+* Introduce reference transactions, which allow multiple references to
+ be locked at the same time and updates be queued. This also allows
+ us to safely update a reflog with arbitrary contents, as we need to
+ do for stash.
+
+### API removals
+
+* `git_remote_supported_url()` and `git_remote_is_valid_url()` have been
+ removed as they have become essentially useless with rsync-style ssh paths.
+
+* `git_clone_into()` and `git_clone_local_into()` have been removed from the
+ public API in favour of `git_clone callbacks`.
+
+* The option to ignore certificate errors via `git_remote_cert_check()`
+ is no longer present. Instead, `git_remote_callbacks` has gained a new
+ entry which lets the user perform their own certificate checks.
+
+### Breaking API changes
+
+* `git_cherry_pick()` is now `git_cherrypick()`.
+
+* The `git_submodule_update()` function was renamed to
+ `git_submodule_update_strategy()`. `git_submodule_update()` is now used to
+ provide functionalty similar to "git submodule update".
+
+* `git_treebuilder_create()` was renamed to `git_treebuilder_new()` to better
+ reflect it being a constructor rather than something which writes to
+ disk.
+
+* `git_treebuilder_new()` (was `git_treebuilder_create()`) now takes a
+ repository so that it can query repository configuration.
+ Subsequently, `git_treebuilder_write()` no longer takes a repository.
+
+* `git_threads_init()` and `git_threads_shutdown()` have been renamed to
+ `git_libgit2_init()` and `git_libgit2_shutdown()` to better explain what
+ their purpose is, as it's grown to be more than just about threads.
+
+* `git_libgit2_init()` and `git_libgit2_shutdown()` now return the number of
+ initializations of the library, so consumers may schedule work on the
+ first initialization.
+
+* The `git_transport_register()` function no longer takes a priority and takes
+ a URL scheme name (eg "http") instead of a prefix like "http://"
+
+* `git_index_name_entrycount()` and `git_index_reuc_entrycount()` now
+ return size_t instead of unsigned int.
+
+* The `context_lines` and `interhunk_lines` fields in `git_diff`_options are
+ now `uint32_t` instead of `uint16_t`. This allows to set them to `UINT_MAX`,
+ in effect asking for "infinite" context e.g. to iterate over all the
+ unmodified lines of a diff.
+
+* `git_status_file()` now takes an exact path. Use `git_status_list_new()` if
+ pathspec searching is needed.
+
+* `git_note_create()` has changed the position of the notes reference
+ name to match `git_note_remove()`.
+
+* Rename `git_remote_load()` to `git_remote_lookup()` to bring it in line
+ with the rest of the lookup functions.
+
+* `git_remote_rename()` now takes the repository and the remote's
+ current name. Accepting a remote indicates we want to change it,
+ which we only did partially. It is much clearer if we accept a name
+ and no loaded objects are changed.
+
+* `git_remote_delete()` now accepts the repository and the remote's name
+ instead of a loaded remote.
+
+* `git_merge_head` is now `git_annotated_commit`, to better reflect its usage
+ for multiple functions (including rebase)
+
+* The `git_clone_options` struct no longer provides the `ignore_cert_errors` or
+ `remote_name` members for remote customization.
+
+ Instead, the `git_clone_options` struct has two new members, `remote_cb` and
+ `remote_cb_payload`, which allow the caller to completely override the remote
+ creation process. If needed, the caller can use this callback to give their
+ remote a name other than the default (origin) or disable cert checking.
+
+ The `remote_callbacks` member has been preserved for convenience, although it
+ is not used when a remote creation callback is supplied.
+
+* The `git_clone`_options struct now provides `repository_cb` and
+ `repository_cb_payload` to allow the user to create a repository with
+ custom options.
+
+* The `git_push` struct to perform a push has been replaced with
+ `git_remote_upload()`. The refspecs and options are passed as a
+ function argument. `git_push_update_tips()` is now also
+ `git_remote_update_tips()` and the callbacks are in the same struct as
+ the rest.
+
+* The `git_remote_set_transport()` function now sets a transport factory function,
+ rather than a pre-existing transport instance.
+
+* The `git_transport` structure definition has moved into the sys/transport.h
+ file.
+
+* libgit2 no longer automatically sets the OpenSSL locking
+ functions. This is not something which we can know to do. A
+ last-resort convenience function is provided in sys/openssl.h,
+ `git_openssl_set_locking()` which can be used to set the locking.
+
+* `git_reference_*()` functions use mmap() + binary search for packed
+ refs lookups when using the fs backend. Previously all entries were
+ read into a hashtable, which could be slow for repositories with a
+ large number of refs.
diff --git a/docs/checkout-internals.md b/docs/checkout-internals.md
new file mode 100644
index 0000000..e0b2583
--- /dev/null
+++ b/docs/checkout-internals.md
@@ -0,0 +1,231 @@
+Checkout Internals
+==================
+
+Checkout has to handle a lot of different cases. It examines the
+differences between the target tree, the baseline tree and the working
+directory, plus the contents of the index, and groups files into five
+categories:
+
+1. UNMODIFIED - Files that match in all places.
+2. SAFE - Files where the working directory and the baseline content
+ match that can be safely updated to the target.
+3. DIRTY/MISSING - Files where the working directory differs from the
+ baseline but there is no conflicting change with the target. One
+ example is a file that doesn't exist in the working directory - no
+ data would be lost as a result of writing this file. Which action
+ will be taken with these files depends on the options you use.
+4. CONFLICTS - Files where changes in the working directory conflict
+ with changes to be applied by the target. If conflicts are found,
+ they prevent any other modifications from being made (although there
+ are options to override that and force the update, of course).
+5. UNTRACKED/IGNORED - Files in the working directory that are untracked
+ or ignored (i.e. only in the working directory, not the other places).
+
+Right now, this classification is done via 3 iterators (for the three
+trees), with a final lookup in the index. At some point, this may move to
+a 4 iterator version to incorporate the index better.
+
+The actual checkout is done in five phases (at least right now).
+
+1. The diff between the baseline and the target tree is used as a base
+ list of possible updates to be applied.
+2. Iterate through the diff and the working directory, building a list of
+ actions to be taken (and sending notifications about conflicts and
+ dirty files).
+3. Remove any files / directories as needed (because alphabetical
+ iteration means that an untracked directory will end up sorted *after*
+ a blob that should be checked out with the same name).
+4. Update all blobs.
+5. Update all submodules (after 4 in case a new .gitmodules blob was
+ checked out)
+
+Checkout could be driven either off a target-to-workdir diff or a
+baseline-to-target diff. There are pros and cons of each.
+
+Target-to-workdir means the diff includes every file that could be
+modified, which simplifies bookkeeping, but the code to constantly refer
+back to the baseline gets complicated.
+
+Baseline-to-target has simpler code because the diff defines the action to
+take, but needs special handling for untracked and ignored files, if they
+need to be removed.
+
+The current checkout implementation is based on a baseline-to-target diff.
+
+
+Picking Actions
+===============
+
+The most interesting aspect of this is phase 2, picking the actions that
+should be taken. There are a lot of corner cases, so it may be easier to
+start by looking at the rules for a simple 2-iterator diff:
+
+Key
+---
+- B1,B2,B3 - blobs with different SHAs,
+- Bi - ignored blob (WD only)
+- T1,T2,T3 - trees with different SHAs,
+- Ti - ignored tree (WD only)
+- S1,S2 - submodules with different SHAs
+- Sd - dirty submodule (WD only)
+- x - nothing
+
+Diff with 2 non-workdir iterators
+---------------------------------
+
+| | Old | New | |
+|----|-----|-----|------------------------------------------------------------|
+| 0 | x | x | nothing |
+| 1 | x | B1 | added blob |
+| 2 | x | T1 | added tree |
+| 3 | B1 | x | removed blob |
+| 4 | B1 | B1 | unmodified blob |
+| 5 | B1 | B2 | modified blob |
+| 6 | B1 | T1 | typechange blob -> tree |
+| 7 | T1 | x | removed tree |
+| 8 | T1 | B1 | typechange tree -> blob |
+| 9 | T1 | T1 | unmodified tree |
+| 10 | T1 | T2 | modified tree (implies modified/added/removed blob inside) |
+
+
+Now, let's make the "New" iterator into a working directory iterator, so
+we replace "added" items with either untracked or ignored, like this:
+
+Diff with non-work & workdir iterators
+--------------------------------------
+
+| | Old | New | |
+|----|-----|-----|------------------------------------------------------------|
+| 0 | x | x | nothing |
+| 1 | x | B1 | untracked blob |
+| 2 | x | Bi | ignored file |
+| 3 | x | T1 | untracked tree |
+| 4 | x | Ti | ignored tree |
+| 5 | B1 | x | removed blob |
+| 6 | B1 | B1 | unmodified blob |
+| 7 | B1 | B2 | modified blob |
+| 8 | B1 | T1 | typechange blob -> tree |
+| 9 | B1 | Ti | removed blob AND ignored tree as separate items |
+| 10 | T1 | x | removed tree |
+| 11 | T1 | B1 | typechange tree -> blob |
+| 12 | T1 | Bi | removed tree AND ignored blob as separate items |
+| 13 | T1 | T1 | unmodified tree |
+| 14 | T1 | T2 | modified tree (implies modified/added/removed blob inside) |
+
+Note: if there is a corresponding entry in the old tree, then a working
+directory item won't be ignored (i.e. no Bi or Ti for tracked items).
+
+
+Now, expand this to three iterators: a baseline tree, a target tree, and
+an actual working directory tree:
+
+Checkout From 3 Iterators (2 not workdir, 1 workdir)
+----------------------------------------------------
+
+(base == old HEAD; target == what to checkout; actual == working dir)
+
+| |base | target | actual/workdir | |
+|-----|-----|------- |----------------|--------------------------------------------------------------------|
+| 0 | x | x | x | nothing |
+| 1 | x | x | B1/Bi/T1/Ti | untracked/ignored blob/tree (SAFE) |
+| 2+ | x | B1 | x | add blob (SAFE) |
+| 3 | x | B1 | B1 | independently added blob (FORCEABLE-2) |
+| 4* | x | B1 | B2/Bi/T1/Ti | add blob with content conflict (FORCEABLE-2) |
+| 5+ | x | T1 | x | add tree (SAFE) |
+| 6* | x | T1 | B1/Bi | add tree with blob conflict (FORCEABLE-2) |
+| 7 | x | T1 | T1/i | independently added tree (SAFE+MISSING) |
+| 8 | B1 | x | x | independently deleted blob (SAFE+MISSING) |
+| 9- | B1 | x | B1 | delete blob (SAFE) |
+| 10- | B1 | x | B2 | delete of modified blob (FORCEABLE-1) |
+| 11 | B1 | x | T1/Ti | independently deleted blob AND untrack/ign tree (SAFE+MISSING !!!) |
+| 12 | B1 | B1 | x | locally deleted blob (DIRTY || SAFE+CREATE) |
+| 13+ | B1 | B2 | x | update to deleted blob (SAFE+MISSING) |
+| 14 | B1 | B1 | B1 | unmodified file (SAFE) |
+| 15 | B1 | B1 | B2 | locally modified file (DIRTY) |
+| 16+ | B1 | B2 | B1 | update unmodified blob (SAFE) |
+| 17 | B1 | B2 | B2 | independently updated blob (FORCEABLE-1) |
+| 18+ | B1 | B2 | B3 | update to modified blob (FORCEABLE-1) |
+| 19 | B1 | B1 | T1/Ti | locally deleted blob AND untrack/ign tree (DIRTY) |
+| 20* | B1 | B2 | T1/Ti | update to deleted blob AND untrack/ign tree (F-1) |
+| 21+ | B1 | T1 | x | add tree with locally deleted blob (SAFE+MISSING) |
+| 22* | B1 | T1 | B1 | add tree AND deleted blob (SAFE) |
+| 23* | B1 | T1 | B2 | add tree with delete of modified blob (F-1) |
+| 24 | B1 | T1 | T1 | add tree with deleted blob (F-1) |
+| 25 | T1 | x | x | independently deleted tree (SAFE+MISSING) |
+| 26 | T1 | x | B1/Bi | independently deleted tree AND untrack/ign blob (F-1) |
+| 27- | T1 | x | T1 | deleted tree (MAYBE SAFE) |
+| 28+ | T1 | B1 | x | deleted tree AND added blob (SAFE+MISSING) |
+| 29 | T1 | B1 | B1 | independently typechanged tree -> blob (F-1) |
+| 30+ | T1 | B1 | B2 | typechange tree->blob with conflicting blob (F-1) |
+| 31* | T1 | B1 | T1/T2 | typechange tree->blob (MAYBE SAFE) |
+| 32+ | T1 | T1 | x | restore locally deleted tree (SAFE+MISSING) |
+| 33 | T1 | T1 | B1/Bi | locally typechange tree->untrack/ign blob (DIRTY) |
+| 34 | T1 | T1 | T1/T2 | unmodified tree (MAYBE SAFE) |
+| 35+ | T1 | T2 | x | update locally deleted tree (SAFE+MISSING) |
+| 36* | T1 | T2 | B1/Bi | update to tree with typechanged tree->blob conflict (F-1) |
+| 37 | T1 | T2 | T1/T2/T3 | update to existing tree (MAYBE SAFE) |
+| 38+ | x | S1 | x | add submodule (SAFE) |
+| 39 | x | S1 | S1/Sd | independently added submodule (SUBMODULE) |
+| 40* | x | S1 | B1 | add submodule with blob confilct (FORCEABLE) |
+| 41* | x | S1 | T1 | add submodule with tree conflict (FORCEABLE) |
+| 42 | S1 | x | S1/Sd | deleted submodule (SUBMODULE) |
+| 43 | S1 | x | x | independently deleted submodule (SUBMODULE) |
+| 44 | S1 | x | B1 | independently deleted submodule with added blob (SAFE+MISSING) |
+| 45 | S1 | x | T1 | independently deleted submodule with added tree (SAFE+MISSING) |
+| 46 | S1 | S1 | x | locally deleted submodule (SUBMODULE) |
+| 47+ | S1 | S2 | x | update locally deleted submodule (SAFE) |
+| 48 | S1 | S1 | S2 | locally updated submodule commit (SUBMODULE) |
+| 49 | S1 | S2 | S1 | updated submodule commit (SUBMODULE) |
+| 50+ | S1 | B1 | x | add blob with locally deleted submodule (SAFE+MISSING) |
+| 51* | S1 | B1 | S1 | typechange submodule->blob (SAFE) |
+| 52* | S1 | B1 | Sd | typechange dirty submodule->blob (SAFE!?!?) |
+| 53+ | S1 | T1 | x | add tree with locally deleted submodule (SAFE+MISSING) |
+| 54* | S1 | T1 | S1/Sd | typechange submodule->tree (MAYBE SAFE) |
+| 55+ | B1 | S1 | x | add submodule with locally deleted blob (SAFE+MISSING) |
+| 56* | B1 | S1 | B1 | typechange blob->submodule (SAFE) |
+| 57+ | T1 | S1 | x | add submodule with locally deleted tree (SAFE+MISSING) |
+| 58* | T1 | S1 | T1 | typechange tree->submodule (SAFE) |
+
+
+The number is followed by ' ' if no change is needed or '+' if the case
+needs to write to disk or '-' if something must be deleted and '*' if
+there should be a delete followed by an write.
+
+There are four tiers of safe cases:
+
+* SAFE == completely safe to update
+* SAFE+MISSING == safe except the workdir is missing the expect content
+* MAYBE SAFE == safe if workdir tree matches (or is missing) baseline
+ content, which is unknown at this point
+* FORCEABLE == conflict unless FORCE is given
+* DIRTY == no conflict but change is not applied unless FORCE
+* SUBMODULE == no conflict and no change is applied unless a deleted
+ submodule dir is empty
+
+Some slightly unusual circumstances:
+
+* 8 - parent dir is only deleted when file is, so parent will be left if
+ empty even though it would be deleted if the file were present
+* 11 - core git does not consider this a conflict but attempts to delete T1
+ and gives "unable to unlink file" error yet does not skip the rest
+ of the operation
+* 12 - without FORCE file is left deleted (i.e. not restored) so new wd is
+ dirty (and warning message "D file" is printed), with FORCE, file is
+ restored.
+* 24 - This should be considered MAYBE SAFE since effectively it is 7 and 8
+ combined, but core git considers this a conflict unless forced.
+* 26 - This combines two cases (1 & 25) (and also implied 8 for tree content)
+ which are ok on their own, but core git treat this as a conflict.
+ If not forced, this is a conflict. If forced, this actually doesn't
+ have to write anything and leaves the new blob as an untracked file.
+* 32 - This is the only case where the baseline and target values match
+ and yet we will still write to the working directory. In all other
+ cases, if baseline == target, we don't touch the workdir (it is
+ either already right or is "dirty"). However, since this case also
+ implies that a ?/B1/x case will exist as well, it can be skipped.
+* 41 - It's not clear how core git distinguishes this case from 39 (mode?).
+* 52 - Core git makes destructive changes without any warning when the
+ submodule is dirty and the type changes to a blob.
+
+Cases 3, 17, 24, 26, and 29 are all considered conflicts even though
+none of them will require making any updates to the working directory.
diff --git a/docs/code_of_conduct.md b/docs/code_of_conduct.md
new file mode 100644
index 0000000..0a0e4eb
--- /dev/null
+++ b/docs/code_of_conduct.md
@@ -0,0 +1,75 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to making participation in our project and
+our community a harassment-free experience for everyone, regardless of age, body
+size, disability, ethnicity, gender identity and expression, level of experience,
+nationality, personal appearance, race, religion, or sexual identity and
+orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or
+advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic
+ address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community. Examples of
+representing a project or community include using an official project e-mail
+address, posting via an official social media account, or acting as an appointed
+representative at an online or offline event. Representation of a project may be
+further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting the project team at [libgit2@gmail.com][email]. All
+complaints will be reviewed and investigated and will result in a response that
+is deemed necessary and appropriate to the circumstances. The project team is
+obligated to maintain confidentiality with regard to the reporter of an incident.
+Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good
+faith may face temporary or permanent repercussions as determined by other
+members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
+available at [http://contributor-covenant.org/version/1/4][version]
+
+[email]: mailto:libgit2@gmail.com
+[homepage]: http://contributor-covenant.org
+[version]: http://contributor-covenant.org/version/1/4/
diff --git a/docs/coding-style.md b/docs/coding-style.md
new file mode 100644
index 0000000..b8b94d6
--- /dev/null
+++ b/docs/coding-style.md
@@ -0,0 +1,364 @@
+# libgit2 Coding Style
+
+This documentation describes the preferred coding style for the libgit2 project.
+While not all parts of our code base conform to this coding style, the outlined
+rules are what we aim for.
+
+Note that in no case do we accept changes that convert huge parts of the code
+base to use our coding style. Instead, it is encouraged to modernize small parts
+of code you're going to modify anyway for a given change you want to introduce.
+A good rule to follow is the Boy Scout Rule: "Leave the campground cleaner than
+you found it."
+
+## C Coding Style
+
+The following sections define the coding style for all code files and headers.
+
+### Indentation and Alignment
+
+Code is indented by tabs, where a tab is 8 spaces. Each opening scope increases
+the indentation level.
+
+```c
+int foobar(int void)
+{
+ if (condition)
+ doit();
+ /* Body */
+}
+```
+
+Switch statements have their `case`s aligned with the `switch` keyword. Case
+bodies are indented by an additional level. Case bodies should not open their
+own scope to declare variables.
+
+```c
+switch (c) {
+case 'a':
+case 'b':
+ return 0;
+default:
+ return -1;
+}
+```
+
+Multi-line conditions should be aligned with the opening brace of the current
+statement:
+
+```c
+if (one_very_long_condition(c) &&
+ another_very_long_condition(c))
+ doit();
+```
+
+### Spaces
+
+There must be no space between the function and its arguments, arguments must be
+separated by a space:
+
+```c
+int doit(int first_arg, int second_arg);
+doit(1, 2);
+```
+
+For any binary or ternary operators, the arguments and separator must be
+separated by a space:
+
+```c
+1 + 2;
+x ? x : NULL;
+```
+
+Unary operators do not have a space between them and the argument they refer to:
+
+```c
+*c
+&c
+```
+
+The `sizeof` operator always must not have a space and must use braces around
+the type:
+
+```
+sizeof(int)
+```
+
+There must be a space after the keywords `if`, `switch`, `case`, `do` and
+`while`.
+
+### Braces
+
+Functions must have their opening brace on the following line:
+
+```c
+void foobar(void)
+{
+ doit();
+}
+```
+
+For conditions, braces should be placed on the same line as the condition:
+
+```c
+if (condition(c)) {
+ doit();
+ dothat();
+}
+
+while (true) {
+ doit();
+}
+```
+
+In case a condition's body has a single line, only, it's allowed to omit braces,
+except if any of its `else if` or `else` branches has more than one line:
+
+```c
+if (condition(c))
+ doit();
+
+if (condition(c))
+ doit();
+else if (other_condition(c))
+ doit();
+
+/* This example must use braces as the `else if` requires them. */
+if (condition(c)) {
+ doit();
+} else if (other_condition(c)) {
+ doit();
+ dothat();
+} else {
+ abort();
+}
+```
+
+### Comments
+
+Comments must use C-style `/* */` comments. C++-style `// `comments are not
+allowed in our codebase. This is a strict requirement as libgit2 tries to be
+compliant with the ISO C90 standard, which only allows C-style comments.
+
+Single-line comments may have their opening and closing tag on the same line:
+
+```c
+/* This is a short comment. */
+```
+
+For multi-line comments, the opening and closing tag should be empty:
+
+```c
+/*
+ * This is a rather long and potentially really unwiedly but informative
+ * multiline comment that helps quite a lot.
+ */
+```
+
+Public functions must have documentation that explain their usage, internal
+functions should have a comment. We use Docurium to generate documentation
+derived from these comments, which uses syntax similar to Doxygen. The first
+line should be a short summary of what the function does. More in-depth
+explanation should be separated from that first line by an empty line.
+Parameters and return values should be documented via `@return` and `@param`
+tags:
+
+```c
+/*
+ * Froznicate the string.
+ *
+ * Froznicate the string by foobaring its internal structure into a more obvious
+ * translation. Note that the returned string is a newly allocated string that
+ * shall be `free`d by the caller.
+ *
+ * @param s String to froznicate
+ * @return A newly allocated string or `NULL` in case an error occurred.
+ */
+char *froznicate(const char *s);
+```
+
+### Variables
+
+Variables must be declared at the beginning of their scope. This is a strict
+requirement as libgit2 tries to be compliant with the ISO C90 standard, which
+forbids mixed declarations and code:
+
+```c
+void foobar(void)
+{
+ char *c = NULL;
+ int a, b;
+
+ a = 0;
+ b = 1;
+
+ return c;
+}
+```
+
+### Naming
+
+Variables must have all-lowercase names. In case a variable name has multiple
+words, words should be separated by an underscore `_` character. While
+recommended to use descriptive naming, common variable names like `i` for
+indices are allowed.
+
+All public functions must have a `git` prefix as well as a prefix indicating
+their respective subsystem. E.g. a function that opens a repository should be
+called `git_repository_open()`. Functions that are not public but declared in
+an internal header file for use by other subsystems should follow the same
+naming pattern. File-local static functions must not have a `git` prefix, but
+should have a prefix indicating their respective subsystem.
+
+All structures declared in the libgit2 project must have a `typedef`, we do not
+use `struct type` variables. Type names follow the same schema as functions.
+
+### Error Handling
+
+The libgit2 project mostly uses error codes to indicate errors. Error codes are
+always of type `int`, where `0` indicates success and a negative error code
+indicates an error case. In some cases, positive error codes may be used to
+indicate special cases. Returned values that are not an error code should be
+returned via an out parameter. Out parameters must always come first in the list
+of arguments.
+
+```c
+int doit(const char **out, int arg)
+{
+ if (!arg)
+ return -1;
+ *out = "Got an argument";
+ return 0;
+}
+```
+
+To avoid repetitive and fragile error handling in case a function has resources
+that need to be free'd, we use `goto out`s:
+
+```c
+int doit(char **out, int arg)
+{
+ int error = 0;
+ char *c;
+
+ c = malloc(strlen("Got an argument") + 1);
+ if (!c) {
+ error = -1;
+ goto out;
+ }
+
+ if (!arg) {
+ error = -1;
+ goto out;
+ }
+
+ strcpy(c, "Got an argument")
+ *out = c;
+
+out:
+ if (error)
+ free(c);
+ return error;
+}
+```
+
+When calling functions that return an error code, you should assign the error
+code to an `error` variable and, in case an error case is indicated and no
+custom error handling is required, return that error code:
+
+```c
+int foobar(void)
+{
+ int error;
+
+ if ((error = doit()) < 0)
+ return error;
+
+ return 0;
+}
+```
+
+When doing multiple function calls where all of the functions return an error
+code, it's common practice to chain these calls together:
+
+```c
+int doit(void)
+{
+ int error;
+
+ if ((error = dothis()) < 0 ||
+ (error = dothat()) < 0)
+ return error;
+
+ return 0;
+}
+```
+
+## CMake Coding Style
+
+The following section defines the coding style for our CMake build system.
+
+### Indentation
+
+Code is indented by tabs, where a tab is 8 spaces. Each opening scope increases
+the indentation level.
+
+```cmake
+if(CONDITION)
+ doit()
+endif()
+```
+
+### Spaces
+
+There must be no space between keywords and their opening brace. While this is
+the same as in our C codebase for function calls, this also applies to
+conditional keywords. This is done to avoid the awkward-looking `else ()`
+statement.
+
+```cmake
+if(CONDITION)
+ doit()
+else()
+ dothat()
+endif()
+```
+
+### Case
+
+While CMake is completely case-insensitive when it comes to function calls, we
+want to agree on a common coding style for this. To reduce the danger of
+repetitive strain injuries, all function calls should be lower-case (NB: this is
+not currently the case yet, but introduced as a new coding style by this
+document).
+
+Variables are written all-uppercase. In contrast to functions, variables are
+case-sensitive in CMake. As CMake itself uses upper-case variables in all
+places, we should follow suit and do the same.
+
+Control flow keywords must be all lowercase. In contrast to that, test keywords
+must be all uppercase:
+
+```cmake
+if(NOT CONDITION)
+ doit()
+elseif(FOO AND BAR)
+ dothat()
+endif()
+```
+
+### Targets
+
+CMake code should not use functions that modify the global scope but prefer
+their targeted equivalents, instead. E.g. instead of using
+`include_directories()`, you must use `target_include_directories()`. An
+exception to this rule is setting up global compiler flags like warnings or
+flags required to set up the build type.
+
+### Dependencies
+
+Dependencies should not be discovered or set up in the main "CMakeLists.txt"
+module. Instead, they should either have their own module in our top-level
+"cmake/" directory or have a "CMakeLists.txt" in their respective "deps/"
+directory in case it is a vendored library. All dependencies should expose
+interface library targets that can be linked against with
+`target_link_libraries()`.
diff --git a/docs/contributing.md b/docs/contributing.md
new file mode 100644
index 0000000..382d955
--- /dev/null
+++ b/docs/contributing.md
@@ -0,0 +1,183 @@
+# Welcome to libgit2!
+
+We're making it easy to do interesting things with git, and we'd love to have
+your help.
+
+## Licensing
+
+By contributing to libgit2, you agree to release your contribution under
+the terms of the license. Except for the `examples` and the
+`deps` directories, all code is released under the [GPL v2 with
+linking exception](../COPYING).
+
+The `examples` code is governed by the
+[CC0 Public Domain Dedication](../examples/COPYING), so that you may copy
+from them into your own application.
+
+The bundled dependencies in the `deps` directories are governed
+by the following licenses:
+
+- http-parser is licensed under [MIT license](../deps/http-parser/COPYING)
+- pcre is governed by [BSD license](../deps/pcre/LICENCE)
+- winhttp is governed by [LGPL v2.1+](../deps/winhttp/COPYING.LGPL) and [GPL v2 with linking exception](../deps/winhttp/COPYING.GPL)
+- zlib is governed by [zlib license](../deps/zlib/COPYING)
+
+## Discussion & Chat
+
+We hang out in the [#libgit2](https://web.libera.chat/#libgit2) channel on
+[libera](https://libera.chat).
+
+Also, feel free to open an
+[Issue](https://github.com/libgit2/libgit2/issues/new) to start a discussion
+about any concerns you have. We like to use Issues for that so there is an
+easily accessible permanent record of the conversation.
+
+## Libgit2 Versions
+
+The `main` branch is the main branch where development happens.
+Releases are tagged
+(e.g. [v0.21.0](https://github.com/libgit2/libgit2/releases/tag/v0.21.0) )
+and when a critical bug fix needs to be backported, it will be done on a
+`<tag>-maint` maintenance branch.
+
+## Reporting Bugs
+
+First, know which version of libgit2 your problem is in and include it in
+your bug report. This can either be a tag (e.g.
+[v0.17.0](https://github.com/libgit2/libgit2/releases/tag/v0.17.0)) or a
+commit SHA
+(e.g. [01be7863](https://github.com/libgit2/libgit2/commit/01be7863)).
+Using [`git describe`](http://git-scm.com/docs/git-describe) is a
+great way to tell us what version you're working with.
+
+If you're not running against the latest `main` branch version,
+please compile and test against that to avoid re-reporting an issue that's
+already been fixed.
+
+It's *incredibly* helpful to be able to reproduce the problem. Please
+include a list of steps, a bit of code, and/or a zipped repository (if
+possible). Note that some of the libgit2 developers are employees of
+GitHub, so if your repository is private, find us on IRC and we'll figure
+out a way to help you.
+
+## Pull Requests
+
+Our work flow is a [typical GitHub
+flow](https://guides.github.com/introduction/flow/index.html), where
+contributors fork the [libgit2 repository](https://github.com/libgit2/libgit2),
+make their changes on branch, and submit a
+[Pull Request](https://help.github.com/articles/using-pull-requests)
+(a.k.a. "PR"). Pull requests should usually be targeted at the `main`
+branch.
+
+Life will be a lot easier for you (and us) if you follow this pattern
+(i.e. fork, named branch, submit PR). If you use your fork's `main`
+branch directly, things can get messy.
+
+Please include a nice description of your changes when you submit your PR;
+if we have to read the whole diff to figure out why you're contributing
+in the first place, you're less likely to get feedback and have your change
+merged in.
+
+In addition to outlining your thought process in the PR's description, please
+also try to document it in your commits. We welcome it if every commit has a
+description of why you have been doing your changes alongside with your
+reasoning why this is a good idea. The messages shall be written in
+present-tense and in an imperative style (e.g. "Add feature foobar", not "Added
+feature foobar" or "Adding feature foobar"). Lines should be wrapped at 80
+characters so people with small screens are able to read the commit messages in
+their terminal without any problem.
+
+To make it easier to attribute commits to certain parts of our code base, we
+also prefer to have the commit subject be prefixed with a "scope". E.g. if you
+are changing code in our merging subsystem, make sure to prefix the subject with
+"merge:". The first word following the colon shall start with an lowercase
+letter. The maximum line length for the subject is 70 characters, preferably
+shorter.
+
+If you are starting to work on a particular area, feel free to submit a PR
+that highlights your work in progress (and note in the PR title that it's
+not ready to merge). These early PRs are welcome and will help in getting
+visibility for your fix, allow others to comment early on the changes and
+also let others know that you are currently working on something.
+
+Before wrapping up a PR, you should be sure to:
+
+* Write tests to cover any functional changes
+* Update documentation for any changed public APIs
+* Add to the [`changelog.md`](changelog.md) file describing any major changes
+
+## Unit Tests
+
+We believe that our unit tests allow us to keep the quality of libgit2
+high: any new changes must not cause unit test failures, and new changes
+should include unit tests that cover the bug fixes or new features.
+For bug fixes, we prefer unit tests that illustrate the failure before
+the change, but pass with your changes.
+
+In addition to new tests, please ensure that your changes do not cause
+any other test failures. Running the entire test suite is helpful
+before you submit a pull request. When you build libgit2, the test
+suite will also be built. You can run most of the tests by simply running
+the resultant `libgit2_tests` binary. If you want to run a specific
+unit test, you can name it with the `-s` option. For example:
+
+ libgit2_tests -sstatus::worktree::long_filenames
+
+Or you can run an entire class of tests. For example, to run all the
+worktree status tests:
+
+ libgit2_tests -sstatus::worktree
+
+The default test run is fairly exhaustive, but it will exclude some
+unit tests by default: in particular, those that talk to network
+servers and the tests that manipulate the filesystem in onerous
+ways (and may need to have special privileges to run). To run the
+network tests:
+
+ libgit2_tests -ionline
+
+In addition, various tests may be enabled by environment variables,
+like the ones that write exceptionally large repositories or manipulate
+the filesystem structure in unexpected ways. These tests *may be
+dangerous* to run on a normal machine and may harm your filesystem. It's
+not recommended that you run these; instead, the continuous integration
+servers will run these (in a sandbox).
+
+## Porting Code From Other Open-Source Projects
+
+`libgit2` is licensed under the terms of the GPL v2 with a linking
+exception. Any code brought in must be compatible with those terms.
+
+The most common case is porting code from core Git. Git is a pure GPL
+project, which means that in order to port code to this project, we need the
+explicit permission of the author. Check the
+[`git.git-authors`](https://github.com/libgit2/libgit2/blob/development/git.git-authors)
+file for authors who have already consented.
+
+Other licenses have other requirements; check the license of the library
+you're porting code *from* to see what you need to do. As a general rule,
+MIT and BSD (3-clause) licenses are typically no problem. Apache 2.0
+license typically doesn't work due to GPL incompatibility.
+
+If your pull request uses code from core Git, another project, or code
+from a forum / Stack Overflow, then *please* flag this in your PR and make
+sure you've given proper credit to the original author in the code
+snippet.
+
+## Style Guide
+
+The public API of `libgit2` is [ANSI C](http://en.wikipedia.org/wiki/ANSI_C)
+(a.k.a. C89) compatible. Internally, `libgit2` is written using a portable
+subset of C99 - in order to compile with GCC, Clang, MSVC, etc., we keep
+local variable declarations at the tops of blocks only and avoid `//` style
+comments. Additionally, `libgit2` follows some extra conventions for
+function and type naming, code formatting, and testing.
+
+We like to keep the source code consistent and easy to read. Maintaining
+this takes some discipline, but it's been more than worth it. Take a look
+at the [conventions file](conventions.md).
+
+## Starter Projects
+
+See our [projects list](projects.md).
diff --git a/docs/conventions.md b/docs/conventions.md
new file mode 100644
index 0000000..a017db1
--- /dev/null
+++ b/docs/conventions.md
@@ -0,0 +1,266 @@
+# Libgit2 Conventions
+
+We like to keep the source consistent and readable. Herein are some
+guidelines that should help with that.
+
+## External API
+
+We have a few rules to avoid surprising ways of calling functions and
+some rules for consumers of the library to avoid stepping on each
+other's toes.
+
+ - Property accessors return the value directly (e.g. an `int` or
+ `const char *`) but if a function can fail, we return a `int` value
+ and the output parameters go first in the parameter list, followed
+ by the object that a function is operating on, and then any other
+ arguments the function may need.
+
+ - If a function returns an object as a return value, that function is
+ a getter and the object's lifetime is tied to the parent
+ object. Objects which are returned as the first argument as a
+ pointer-to-pointer are owned by the caller and it is responsible
+ for freeing it. Strings are returned via `git_buf` in order to
+ allow for re-use and safe freeing.
+
+ - Most of what libgit2 does relates to I/O so as a general rule
+ you should assume that any function can fail due to errors as even
+ getting data from the filesystem can result in all sorts of errors
+ and complex failure cases.
+
+ - Paths inside the Git system are separated by a slash (0x2F). If a
+ function accepts a path on disk, then backslashes (0x5C) are also
+ accepted on Windows.
+
+ - Do not mix allocators. If something has been allocated by libgit2,
+ you do not know which is the right free function in the general
+ case. Use the free functions provided for each object type.
+
+## Compatibility
+
+`libgit2` runs on many different platforms with many different compilers.
+
+The public API of `libgit2` is [ANSI C](http://en.wikipedia.org/wiki/ANSI_C)
+(a.k.a. C89) compatible.
+
+Internally, `libgit2` is written using a portable subset of C99 - in order
+to maximize compatibility (e.g. with MSVC) we avoid certain C99
+extensions. Specifically, we keep local variable declarations at the tops
+of blocks only and we avoid `//` style comments.
+
+Also, to the greatest extent possible, we try to avoid lots of `#ifdef`s
+inside the core code base. This is somewhat unavoidable, but since it can
+really hamper maintainability, we keep it to a minimum.
+
+## Match Surrounding Code
+
+If there is one rule to take away from this document, it is *new code should
+match the surrounding code in a way that makes it impossible to distinguish
+the new from the old.* Consistency is more important to us than anyone's
+personal opinion about where braces should be placed or spaces vs. tabs.
+
+If a section of code is being completely rewritten, it is okay to bring it
+in line with the standards that are laid out here, but we will not accept
+submissions that contain a large number of changes that are merely
+reformatting.
+
+## Naming Things
+
+All external types and functions start with `git_` and all `#define` macros
+start with `GIT_`. The `libgit2` API is mostly broken into related
+functional modules each with a corresponding header. All functions in a
+module should be named like `git_modulename_functioname()`
+(e.g. `git_repository_open()`).
+
+Functions with a single output parameter should name that parameter `out`.
+Multiple outputs should be named `foo_out`, `bar_out`, etc.
+
+Parameters of type `git_oid` should be named `id`, or `foo_id`. Calls that
+return an OID should be named `git_foo_id`.
+
+Where a callback function is used, the function should also include a
+user-supplied extra input that is a `void *` named "payload" that will be
+passed through to the callback at each invocation.
+
+## Typedefs
+
+Wherever possible, use `typedef`. In some cases, if a structure is just a
+collection of function pointers, the pointer types don't need to be
+separately typedef'd, but loose function pointer types should be.
+
+## Exports
+
+All exported functions must be declared as:
+
+```c
+GIT_EXTERN(result_type) git_modulename_functionname(arg_list);
+```
+
+## Internals
+
+Functions whose *modulename* is followed by two underscores,
+for example `git_odb__read_packed`, are semi-private functions.
+They are primarily intended for use within the library itself,
+and may disappear or change their signature in a future release.
+
+## Parameters
+
+Out parameters come first.
+
+Whenever possible, pass argument pointers as `const`. Some structures (such
+as `git_repository` and `git_index`) have mutable internal structure that
+prevents this.
+
+Callbacks should always take a `void *` payload as their last parameter.
+Callback pointers are grouped with their payloads, and typically come last
+when passed as arguments:
+
+```c
+int git_foo(git_repository *repo, git_foo_cb callback, void *payload);
+```
+
+## Memory Ownership
+
+Some APIs allocate memory which the caller is responsible for freeing; others
+return a pointer into a buffer that's owned by some other object. Make this
+explicit in the documentation.
+
+## Return codes
+
+Most public APIs should return an `int` error code. As is typical with most
+C library functions, a zero value indicates success and a negative value
+indicates failure.
+
+Some bindings will transform these returned error codes into exception
+types, so returning a semantically appropriate error code is important.
+Check
+[`include/git2/errors.h`](https://github.com/libgit2/libgit2/blob/development/include/git2/errors.h)
+for the return codes already defined.
+
+In your implementation, use `git_error_set()` to provide extended error
+information to callers.
+
+If a `libgit2` function internally invokes another function that reports an
+error, but the error is not propagated up, use `git_error_clear()` to prevent
+callers from getting the wrong error message later on.
+
+
+## Structs
+
+Most public types should be opaque, e.g.:
+
+```C
+typedef struct git_odb git_odb;
+```
+
+...with allocation functions returning an "instance" created within
+the library, and not within the application. This allows the type
+to grow (or shrink) in size without rebuilding client code.
+
+To preserve ABI compatibility, include an `int version` field in all transparent
+structures, and initialize to the latest version in the constructor call.
+Increment the "latest" version whenever the structure changes, and try to only
+append to the end of the structure.
+
+## Option Structures
+
+If a function's parameter count is too high, it may be desirable to package
+up the options in a structure. Make them transparent, include a version
+field, and provide an initializer constant or constructor. Using these
+structures should be this easy:
+
+```C
+git_foo_options opts = GIT_FOO_OPTIONS_INIT;
+opts.baz = BAZ_OPTION_ONE;
+git_foo(&opts);
+```
+
+## Enumerations
+
+Typedef all enumerated types. If each option stands alone, use the enum
+type for passing them as parameters; if they are flags to be OR'ed together,
+pass them as `unsigned int` or `uint32_t` or some appropriate type.
+
+## Code Layout
+
+Try to keep lines less than 80 characters long. This is a loose
+requirement, but going significantly over 80 columns is not nice.
+
+Use common sense to wrap most code lines; public function declarations
+can use a couple of different styles:
+
+```c
+/** All on one line is okay if it fits */
+GIT_EXTERN(int) git_foo_simple(git_oid *id);
+
+/** Otherwise one argument per line is a good next step */
+GIT_EXTERN(int) git_foo_id(
+ git_oid **out,
+ int a,
+ int b);
+```
+
+Indent with tabs; set your editor's tab width to eight for best effect.
+
+Avoid trailing whitespace and only commit Unix-style newlines (i.e. no CRLF
+in the repository - just set `core.autocrlf` to true if you are writing code
+on a Windows machine).
+
+## Documentation
+
+All comments should conform to Doxygen "javadoc" style conventions for
+formatting the public API documentation. Try to document every parameter,
+and keep the comments up to date if you change the parameter list.
+
+## Public Header Template
+
+Use this template when creating a new public header.
+
+```C
+#ifndef INCLUDE_git_${filename}_h__
+#define INCLUDE_git_${filename}_h__
+
+#include "git/common.h"
+
+/**
+ * @file git/${filename}.h
+ * @brief Git some description
+ * @defgroup git_${filename} some description routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/* ... definitions ... */
+
+/** @} */
+GIT_END_DECL
+#endif
+```
+
+## Inlined functions
+
+All inlined functions must be declared as:
+
+```C
+GIT_INLINE(result_type) git_modulename_functionname(arg_list);
+```
+
+`GIT_INLINE` (or `inline`) should not be used in public headers in order
+to preserve ANSI C compatibility.
+
+## Tests
+
+`libgit2` uses the [clar](https://github.com/vmg/clar) testing framework.
+
+All PRs should have corresponding tests.
+
+* If the PR fixes an existing issue, the test should fail prior to applying
+ the PR and succeed after applying it.
+* If the PR is for new functionality, then the tests should exercise that
+ new functionality to a certain extent. We don't require 100% coverage
+ right now (although we are getting stricter over time).
+
+When adding new tests, we prefer if you attempt to reuse existing test data
+(in `tests-clar/resources/`) if possible. If you are going to add new test
+repositories, please try to strip them of unnecessary files (e.g. sample
+hooks, etc).
diff --git a/docs/diff-internals.md b/docs/diff-internals.md
new file mode 100644
index 0000000..da4c5a1
--- /dev/null
+++ b/docs/diff-internals.md
@@ -0,0 +1,92 @@
+Diff is broken into four phases:
+
+1. Building a list of things that have changed. These changes are called
+ deltas (git_diff_delta objects) and are grouped into a git_diff_list.
+2. Applying file similarity measurement for rename and copy detection (and
+ to potentially split files that have changed radically). This step is
+ optional.
+3. Computing the textual diff for each delta. Not all deltas have a
+ meaningful textual diff. For those that do, the textual diff can
+ either be generated on the fly and passed to output callbacks or can be
+ turned into a git_diff_patch object.
+4. Formatting the diff and/or patch into standard text formats (such as
+ patches, raw lists, etc).
+
+In the source code, step 1 is implemented in `src/diff.c`, step 2 in
+`src/diff_tform.c`, step 3 in `src/diff_patch.c`, and step 4 in
+`src/diff_print.c`. Additionally, when it comes to accessing file
+content, everything goes through diff drivers that are implemented in
+`src/diff_driver.c`.
+
+External Objects
+----------------
+
+* `git_diff_options` represents user choices about how a diff should be
+ performed and is passed to most diff generating functions.
+* `git_diff_file` represents an item on one side of a possible delta
+* `git_diff_delta` represents a pair of items that have changed in some
+ way - it contains two `git_diff_file` plus a status and other stuff.
+* `git_diff_list` is a list of deltas along with information about how
+ those particular deltas were found.
+* `git_diff_patch` represents the actual diff between a pair of items. In
+ some cases, a delta may not have a corresponding patch, if the objects
+ are binary, for example. The content of a patch will be a set of hunks
+ and lines.
+* A `hunk` is range of lines described by a `git_diff_range` (i.e. "lines
+ 10-20 in the old file became lines 12-23 in the new"). It will have a
+ header that compactly represents that information, and it will have a
+ number of lines of context surrounding added and deleted lines.
+* A `line` is simple a line of data along with a `git_diff_line_t` value
+ that tells how the data should be interpreted (e.g. context or added).
+
+Internal Objects
+----------------
+
+* `git_diff_file_content` is an internal structure that represents the
+ data on one side of an item to be diffed; it is an augmented
+ `git_diff_file` with more flags and the actual file data.
+
+ * it is created from a repository plus a) a git_diff_file, b) a git_blob,
+ or c) raw data and size
+ * there are three main operations on git_diff_file_content:
+
+ * _initialization_ sets up the data structure and does what it can up to,
+ but not including loading and looking at the actual data
+ * _loading_ loads the data, preprocesses it (i.e. applies filters) and
+ potentially analyzes it (to decide if binary)
+ * _free_ releases loaded data and frees any allocated memory
+
+* The internal structure of a `git_diff_patch` stores the actual diff
+ between a pair of `git_diff_file_content` items
+
+ * it may be "unset" if the items are not diffable
+ * "empty" if the items are the same
+ * otherwise it will consist of a set of hunks each of which covers some
+ number of lines of context, additions and deletions
+ * a patch is created from two git_diff_file_content items
+ * a patch is fully instantiated in three phases:
+
+ * initial creation and initialization
+ * loading of data and preliminary data examination
+ * diffing of data and optional storage of diffs
+ * (TBD) if a patch is asked to store the diffs and the size of the diff
+ is significantly smaller than the raw data of the two sides, then the
+ patch may be flattened using a pool of string data
+
+* `git_diff_output` is an internal structure that represents an output
+ target for a `git_diff_patch`
+ * It consists of file, hunk, and line callbacks, plus a payload
+ * There is a standard flattened output that can be used for plain text output
+ * Typically we use a `git_xdiff_output` which drives the callbacks via the
+ xdiff code taken from core Git.
+
+* `git_diff_driver` is an internal structure that encapsulates the logic
+ for a given type of file
+ * a driver is looked up based on the name and mode of a file.
+ * the driver can then be used to:
+ * determine if a file is binary (by attributes, by git_diff_options
+ settings, or by examining the content)
+ * give you a function pointer that is used to evaluate function context
+ for hunk headers
+ * At some point, the logic for getting a filtered version of file content
+ or calculating the OID of a file may be moved into the driver.
diff --git a/docs/differences-from-git.md b/docs/differences-from-git.md
new file mode 100644
index 0000000..3f46508
--- /dev/null
+++ b/docs/differences-from-git.md
@@ -0,0 +1,27 @@
+# Differences from Git
+
+In some instances, the functionality of libgit2 deviates slightly from Git. This can be because of technical limitations when developing a library, licensing limitations when converting functionality from Git to libgit2, or various other reasons.
+
+Repository and Workdir Path Reporting
+-------------------------------------
+
+When asking Git for the absolute path of a repository via `git rev-parse --absolute-git-dir`, it will output the path to the ".git" folder without a trailing slash. In contrast to that, the call `git_repository_path(repo)` will return the path with a trailing slash:
+
+```
+git rev-parse --absolute-git-dir -> /home/user/projects/libgit2/.git
+git_repository_path(repo) -> /home/user/projects/libgit2/.git/
+```
+
+The same difference exists when listing worktrees:
+
+```
+git worktree list -> /home/user/projects/libgit2
+git_repository_workdir(repo) -> /home/user/projects/libgit2/
+```
+
+Windows Junction Points
+-----------------------
+
+In libgit2, junction points are treated like symbolic links. They're handled specially in `git_win32__file_attribute_to_stat` in `src/win/w32_util.h`. This means that libgit2 tracks the directory itself as a link.
+
+In Git for Windows, junction points are treated like regular directories. This means that Git for Windows tracks the contents of the directory.
diff --git a/docs/error-handling.md b/docs/error-handling.md
new file mode 100644
index 0000000..13ce78f
--- /dev/null
+++ b/docs/error-handling.md
@@ -0,0 +1,284 @@
+Error reporting in libgit2
+==========================
+
+Libgit2 tries to follow the POSIX style: functions return an `int` value
+with 0 (zero) indicating success and negative values indicating an error.
+There are specific negative error codes for each "expected failure"
+(e.g. `GIT_ENOTFOUND` for files that take a path which might be missing)
+and a generic error code (-1) for all critical or non-specific failures
+(e.g. running out of memory or system corruption).
+
+When a negative value is returned, an error message is also set. The
+message can be accessed via the `git_error_last` function which will return a
+pointer to a `git_error` structure containing the error message text and
+the class of error (i.e. what part of the library generated the error).
+
+For instance: An object lookup by SHA prefix (`git_object_lookup_prefix`)
+has two expected failure cases: the SHA is not found at all which returns
+`GIT_ENOTFOUND` or the SHA prefix is ambiguous (i.e. two or more objects
+share the prefix) which returns `GIT_EAMBIGUOUS`. There are any number of
+critical failures (such as a packfile being corrupted, a loose object
+having the wrong access permissions, etc.) all of which will return -1.
+When the object lookup is successful, it will return 0.
+
+If libgit2 was compiled with threads enabled (`-DUSE_THREADS=ON` when using
+CMake), then the error message will be kept in thread-local storage, so it
+will not be modified by other threads. If threads are not enabled, then
+the error message is in global data.
+
+All of the error return codes, the `git_error` type, the error access
+functions, and the error classes are defined in `include/git2/errors.h`.
+See the documentation there for details on the APIs for accessing,
+clearing, and even setting error codes.
+
+When writing libgit2 code, please be smart and conservative when returning
+error codes. Functions usually have a maximum of two or three "expected
+errors" and in most cases only one. If you feel there are more possible
+expected error scenarios, then the API you are writing may be at too high
+a level for core libgit2.
+
+Example usage
+-------------
+
+When using libgit2, you will typically capture the return value from
+functions using an `int` variable and check to see if it is negative.
+When that happens, you can, if you wish, look at the specific value or
+look at the error message that was generated.
+
+~~~c
+{
+ git_repository *repo;
+ int error = git_repository_open(&repo, "path/to/repo");
+
+ if (error < 0) {
+ fprintf(stderr, "Could not open repository: %s\n", git_error_last()->message);
+ exit(1);
+ }
+
+ ... use `repo` here ...
+
+ git_repository_free(repo); /* void function - no error return code */
+}
+~~~
+
+Some of the error return values do have meaning. Optionally, you can look
+at the specific error values to decide what to do.
+
+~~~c
+{
+ git_repository *repo;
+ const char *path = "path/to/repo";
+ int error = git_repository_open(&repo, path);
+
+ if (error < 0) {
+ if (error == GIT_ENOTFOUND)
+ fprintf(stderr, "Could not find repository at path '%s'\n", path);
+ else
+ fprintf(stderr, "Unable to open repository: %s\n",
+ git_error_last()->message);
+ exit(1);
+ }
+
+ ... happy ...
+}
+~~~
+
+Some of the higher-level language bindings may use a range of information
+from libgit2 to convert error return codes into exceptions, including the
+specific error return codes and even the class of error and the error
+message returned by `git_error_last`, but the full range of that logic is
+beyond the scope of this document.
+
+Example internal implementation
+-------------------------------
+
+Internally, libgit2 detects error scenarios, records error messages, and
+returns error values. Errors from low-level functions are generally
+passed upwards (unless the higher level can either handle the error or
+wants to translate the error into something more meaningful).
+
+~~~c
+int git_repository_open(git_repository **repository, const char *path)
+{
+ /* perform some logic to open the repository */
+ if (p_exists(path) < 0) {
+ git_error_set(GIT_ERROR_REPOSITORY, "The path '%s' doesn't exist", path);
+ return GIT_ENOTFOUND;
+ }
+
+ ...
+}
+~~~
+
+Note that some error codes have been defined with a specific meaning in the
+context of callbacks:
+- `GIT_EUSER` provides a way to bubble up a non libgit2-related failure, which
+ allows it to be preserved all the way up to the initial function call (a `git_cred`
+ setup trying to access an unavailable LDAP server for instance).
+- `GIT_EPASSTHROUGH` provides a way to tell libgit2 that it should behave as if
+ no callback was provided. This is of special interest to bindings, which would
+ always provide a C function as a "trampoline", and decide at runtime what to do.
+
+The public error API
+--------------------
+
+- `const git_error *git_error_last(void)`: The main function used to look up
+ the last error. This may return NULL if no error has occurred.
+ Otherwise this should return a `git_error` object indicating the class
+ of error and the error message that was generated by the library.
+ Do not use this function unless the prior call to a libgit2 API
+ returned an error, as it can otherwise give misleading results.
+ libgit2's error strings are not cleared aggressively,
+ and this function may return an error string that reflects a prior error,
+ possibly even reflecting internal state.
+
+ The last error is stored in thread-local storage when libgit2 is
+ compiled with thread support, so you do not have to worry about another
+ thread overwriting the value. When thread support is off, the last
+ error is a global value.
+
+ _Note_ There are some known bugs in the library where this may return
+ NULL even when an error code was generated. Please report these as
+ bugs, but in the meantime, please code defensively and check for NULL
+ when calling this function.
+
+- `void git_error_clear(void)`: This function clears the last error. The
+ library will call this when an error is generated by low level function
+ and the higher level function handles the error.
+
+ _Note_ There are some known bugs in the library where a low level
+ function's error message is not cleared by higher level code that
+ handles the error and returns zero. Please report these as bugs, but in
+ the meantime, a zero return value from a libgit2 API does not guarantee
+ that `git_error_last()` will return NULL.
+
+- `void git_error_set(int error_class, const char *message)`: This
+ function can be used when writing a custom backend module to set the
+ libgit2 error message. See the documentation on this function for its
+ use. Normal usage of libgit2 will probably never need to call this API.
+
+- `void git_error_set_oom(void)`: This is a standard function for reporting
+ an out-of-memory error. It is written in a manner that it doesn't have
+ to allocate any extra memory in order to record the error, so this is
+ the best way to report that scenario.
+
+Deviations from the standard
+----------------------------
+
+There are some public functions that do not return `int` values. There
+are two primary cases:
+
+* `void` return values: If a function has a `void` return, then it will
+ never fail. This primary will be used for object destructors.
+
+* `git_xyz *` return values: These are simple accessor functions where the
+ only meaningful error would typically be looking something up by index
+ and having the index be out of bounds. In those cases, the function
+ will typically return NULL.
+
+* Boolean return values: There are some cases where a function cannot fail
+ and wants to return a boolean value. In those cases, we try to return 1
+ for true and 0 for false. These cases are rare and the return value for
+ the function should probably be an `unsigned int` to denote these cases.
+ If you find an exception, please open an issue and let's fix it.
+
+There are a few other exceptions to these rules here and there in the
+library, but those are extremely rare and should probably be converted
+over to other to more standard patterns for usage. Feel free to open
+issues pointing these out.
+
+There are some known bugs in the library where some functions may return a
+negative value but not set an error message and some other functions may
+return zero (no error) and yet leave an error message set. Please report
+these cases as issues and they will be fixed. In the meanwhile, please
+code defensively, checking that the return value of `git_error_last` is not
+NULL before using it, and not relying on `git_error_last` to return NULL when
+a function returns 0 for success.
+
+The internal error API
+----------------------
+
+- `void git_error_set(int error_class, const char *fmt, ...)`: This is the
+ main internal function for setting an error. It works like `printf` to
+ format the error message. See the notes of `git_error_set_str` for a
+ general description of how error messages are stored (and also about
+ special handling for `error_class` of `GIT_ERROR_OS`).
+
+Writing error messages
+----------------------
+
+Here are some guidelines when writing error messages:
+
+- Use proper English, and an impersonal or past tenses: *The given path
+ does not exist*, *Failed to lookup object in ODB*
+
+- Use short, direct and objective messages. **One line, max**. libgit2 is
+ a low level library: think that all the messages reported will be thrown
+ as Ruby or Python exceptions. Think how long are common exception
+ messages in those languages.
+
+- **Do not add redundant information to the error message**, specially
+ information that can be inferred from the context.
+
+ E.g. in `git_repository_open`, do not report a message like "Failed to
+ open repository: path not found". Somebody is calling that
+ function. If it fails, they already know that the repository failed to
+ open!
+
+General guidelines for error reporting
+--------------------------------------
+
+- Libgit2 does not handle programming errors with these
+ functions. Programming errors are `assert`ed, and when their source is
+ internal, fixed as soon as possible. This is C, people.
+
+ Example of programming errors that would **not** be handled: passing
+ NULL to a function that expects a valid pointer; passing a `git_tree`
+ to a function that expects a `git_commit`. All these cases need to be
+ identified with `assert` and fixed asap.
+
+ Example of a runtime error: failing to parse a `git_tree` because it
+ contains invalid data. Failing to open a file because it doesn't exist
+ on disk. These errors are handled, a meaningful error message is set,
+ and an error code is returned.
+
+- In general, *do not* try to overwrite errors internally and *do*
+ propagate error codes from lower level functions to the higher level.
+ There are some cases where propagating an error code will be more
+ confusing rather than less, so there are some exceptions to this rule,
+ but the default behavior should be to simply clean up and pass the error
+ on up to the caller.
+
+ **WRONG**
+
+ ~~~c
+ int git_commit_parent(...)
+ {
+ ...
+
+ if (git_commit_lookup(parent, repo, parent_id) < 0) {
+ git_error_set(GIT_ERROR_COMMIT, "Overwrite lookup error message");
+ return -1; /* mask error code */
+ }
+
+ ...
+ }
+ ~~~
+
+ **RIGHT**
+
+ ~~~c
+ int git_commit_parent(...)
+ {
+ ...
+
+ error = git_commit_lookup(parent, repo, parent_id);
+ if (error < 0) {
+ /* cleanup intermediate objects if necessary */
+ /* leave error message and propagate error code */
+ return error;
+ }
+
+ ...
+ }
+ ~~~
diff --git a/docs/fuzzing.md b/docs/fuzzing.md
new file mode 100644
index 0000000..2bf4ccc
--- /dev/null
+++ b/docs/fuzzing.md
@@ -0,0 +1,72 @@
+# Fuzzing
+
+libgit2 is currently using [libFuzzer](https://libfuzzer.info) to perform
+automated fuzz testing. libFuzzer only works with clang.
+
+## Prerequisites for building fuzz targets:
+
+1. All the prerequisites for [building libgit2](https://github.com/libgit2/libgit2).
+2. A recent version of clang. 6.0 is preferred. [pre-build Debian/Ubuntu
+ packages](https://github.com/libgit2/libgit2)
+
+## Build
+
+1. Create a build directory beneath the libgit2 source directory, and change
+ into it: `mkdir build && cd build`
+2. Choose one sanitizers to add. The currently supported sanitizers are
+ [`address`](https://clang.llvm.org/docs/AddressSanitizer.html),
+ [`undefined`](https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html),
+ and [`leak`/`address,leak`](https://clang.llvm.org/docs/LeakSanitizer.html).
+3. Create the cmake build environment and configure the build with the
+ sanitizer chosen: `CC=/usr/bin/clang-6.0 CFLAGS="-fsanitize=address" cmake
+ -DBUILD_TESTS=OFF -DBUILD_FUZZERS=ON -DCMAKE_BUILD_TYPE=RelWithDebInfo ..`.
+ Note that building the fuzzer targets is incompatible with the
+ tests and examples.
+4. Build libgit2: `cmake --build .`
+5. Exit the cmake build environment: `cd ..`
+
+## Run the fuzz targets
+
+1. `ASAN_SYMBOLIZER_PATH=/usr/bin/llvm-symbolize
+ LSAN_OPTIONS=allocator_may_return_null=1
+ ASAN_OPTIONS=allocator_may_return_null=1 ./build/fuzzers/packfile_fuzzer
+ fuzzers/corpora/packfile/`
+
+The `LSAN_OPTIONS` and `ASAN_OPTIONS` are there to allow `malloc(3)` to return
+`NULL`, which is expected if a huge chunk of memory is allocated. The
+`LLVM_PROFILE_FILE` environment string can also be added to override the path
+where libFuzzer will write the coverage report.
+
+## Get coverage
+
+In order to get coverage information, you need to add the "-fcoverage-mapping"
+and "-fprofile-instr-generate CFLAGS, and then run the fuzz target with
+`-runs=0`. That will produce a file called `default.profraw` (this behavior can
+be overridden by setting the `LLVM_PROFILE_FILE="yourfile.profraw"` environment
+variable).
+
+1. `llvm-profdata-6.0 merge -sparse default.profraw -o
+ fuzz_packfile_raw.profdata` transforms the data from a sparse representation
+ into a format that can be used by the other tools.
+2. `llvm-cov-6.0 report ./build/fuzz/fuzz_packfile_raw
+ -instr-profile=fuzz_packfile_raw.profdata` shows a high-level per-file
+ coverage report.
+3. `llvm-cov-6.0 show ./build/fuzz/fuzz_packfile_raw
+ -instr-profile=fuzz_packfile_raw.profdata [source file]` shows a line-by-line
+ coverage analysis of all the codebase (or a single source file).
+
+## Standalone mode
+
+In order to ensure that there are no regresions, each fuzzer target can be run
+in a standalone mode. This can be done by passing `-DUSE_STANDALONE_FUZZERS=ON`.
+This makes it compatible with gcc. This does not use the fuzzing engine, but
+just invokes every file in the chosen corpus.
+
+In order to get full coverage, though, you might want to also enable one of the
+sanitizers. You might need a recent version of clang to get full support.
+
+## References
+
+* [libFuzzer](https://llvm.org/docs/LibFuzzer.html) documentation.
+* [Source-based Code
+ Coverage](https://clang.llvm.org/docs/SourceBasedCodeCoverage.html).
diff --git a/docs/merge-df_conflicts.txt b/docs/merge-df_conflicts.txt
new file mode 100644
index 0000000..09780ee
--- /dev/null
+++ b/docs/merge-df_conflicts.txt
@@ -0,0 +1,41 @@
+Anc / Our / Thr represent the ancestor / ours / theirs side of a merge
+from branch "branch" into HEAD. Workdir represents the expected files in
+the working directory. Index represents the expected files in the index,
+with stage markers.
+
+ Anc Our Thr Workdir Index
+1 D D
+ D/F D/F D/F [0]
+
+2 D D+ D~HEAD (mod/del) D/F [0]
+ D/F D/F D [1]
+ D [2]
+
+3 D D D/F D/F [0]
+ D/F
+
+4 D D+ D~branch (mod/del) D/F [0]
+ D/F D/F D [1]
+ D [3]
+
+5 D D/F (add/add) D/F [2]
+ D/F D/F [3]
+ D/F
+
+6 D/F D/F D D [0]
+ D
+
+7 D/F D/F+ D/F (mod/del) D/F [1]
+ D D~branch (fil/dir) D/F [2]
+ D [3]
+
+8 D/F D/F D D [0]
+ D
+
+9 D/F D/F+ D/F (mod/del) D/F [1]
+ D D~HEAD (fil/dir) D [2]
+ D/F [3]
+
+10 D/F D/F (fil/dir) D/F [0]
+ D D~HEAD D [2]
+ D
diff --git a/docs/projects.md b/docs/projects.md
new file mode 100644
index 0000000..1b9f476
--- /dev/null
+++ b/docs/projects.md
@@ -0,0 +1,93 @@
+Projects For LibGit2
+====================
+
+So, you want to start helping out with `libgit2`? That's fantastic! We
+welcome contributions and we promise we'll try to be nice.
+
+This is a list of libgit2 related projects that new contributors can take
+on. It includes a number of good starter projects as well as some larger
+ideas that no one is actively working on.
+
+## Before You Start
+
+Please start by reading the [README.md](../README.md),
+[contributing.md](contributing.md), and [conventions.md](conventions.md)
+files before diving into one of these projects. Those explain our work
+flow and coding conventions to help ensure that your work will be easily
+integrated into libgit2.
+
+Next, work through the build instructions and make sure you can clone the
+repository, compile it, and run the tests successfully. That will make
+sure that your development environment is set up correctly and you are
+ready to start on libgit2 development.
+
+## Starter Projects
+
+These are good small projects to get started with libgit2.
+
+* Look at the `examples/` programs, find an existing one that mirrors a
+ core Git command and add a missing command-line option. There are many
+ gaps right now and this helps demonstrate how to use the library. Here
+ are some specific ideas (though there are many more):
+ * Fix the `examples/diff.c` implementation of the `-B`
+ (a.k.a. `--break-rewrites`) command line option to actually look for
+ the optional `[<n>][/<m>]` configuration values. There is an
+ existing comment that reads `/* TODO: parse thresholds */`. The
+ trick to this one will be doing it in a manner that is clean and
+ simple, but still handles the various cases correctly (e.g. `-B/70%`
+ is apparently a legal setting).
+ * As an extension to the matching idea for `examples/log.c`, add the
+ `-i` option to use `strcasestr()` for matches.
+ * For `examples/log.c`, implement the `--first-parent` option now that
+ libgit2 supports it in the revwalk API.
+* Pick a Git command that is not already emulated in `examples/` and write
+ a new example that mirrors the behavior. Examples don't have to be
+ perfect emulations, but should demonstrate how to use the libgit2 APIs
+ to get results that are similar to Git commands. This lets you (and us)
+ easily exercise a particular facet of the API and measure compatibility
+ and feature parity with core git.
+* Submit a PR to clarify documentation! While we do try to document all of
+ the APIs, your fresh eyes on the documentation will find areas that are
+ confusing much more easily.
+
+If none of these appeal to you, take a look at our issues list to see if
+there are any unresolved issues you'd like to jump in on.
+
+## Larger Projects
+
+These are ideas for larger projects mostly taken from our backlog of
+[Issues](https://github.com/libgit2/libgit2/issues). Please don't dive
+into one of these as a first project for libgit2 - we'd rather get to
+know you first by successfully shipping your work on one of the smaller
+projects above.
+
+Some of these projects are broken down into subprojects and/or have
+some incremental steps listed towards the larger goal. Those steps
+might make good smaller projects by themselves.
+
+* Port part of the Git test suite to run against the command line emulation
+ in `examples/`
+ * Pick a Git command that is emulated in our `examples/` area
+ * Extract the Git tests that exercise that command
+ * Convert the tests to call our emulation
+ * These tests could go in `examples/tests/`...
+* Add hooks API to enumerate and manage hooks (not run them at this point)
+ * Enumeration of available hooks
+ * Lookup API to see which hooks have a script and get the script
+ * Read/write API to load a hook script and write a hook script
+ * Eventually, callback API to invoke a hook callback when libgit2
+ executes the action in question
+* Isolate logic of ignore evaluation into a standalone API
+* Upgrade internal libxdiff code to latest from core Git
+* Tree builder improvements:
+ * Extend to allow building a tree hierarchy
+* Apply-patch API
+* Add a patch editing API to enable "git add -p" type operations
+* Textconv API to filter binary data before generating diffs (something
+ like the current Filter API, probably).
+* Performance profiling and improvement
+* Support "git replace" ref replacements
+* Include conflicts in diff results and in status
+ * GIT_DELTA_CONFLICT for items in conflict (with multiple files)
+ * Appropriate flags for status
+* Support sparse checkout (i.e. "core.sparsecheckout" and ".git/info/sparse-checkout")
diff --git a/docs/release.md b/docs/release.md
new file mode 100644
index 0000000..87fccaa
--- /dev/null
+++ b/docs/release.md
@@ -0,0 +1,83 @@
+# Releasing the library
+
+We have three kinds of releases: "full" releases, maintenance releases and security releases. Full ones release the state of the `main` branch whereas maintenance releases provide bugfixes building on top of the currently released series. Security releases are also for the current series but only contain security fixes on top of the previous release.
+
+## Full release
+
+We aim to release once every six months. We start the process by opening an issue. This is accompanied with a feature freeze. From now until the release, only bug fixes are to be merged. Use the following as a base for the issue
+
+ Release v0.X
+
+ Let's release v0.X, codenamed: <something witty>
+
+ - [ ] Bump the versions in the headers (`include/git2/version.h`)
+ - [ ] Bump the versions in the clib manifest (`package.json`)
+ - [ ] Make a release candidate
+ - [ ] Plug any final leaks
+ - [ ] Fix any last-minute issues
+ - [ ] Make sure changelog.md reflects everything worth discussing
+ - [ ] Update the version in changelog.md and the header
+ - [ ] Produce a release candidate
+ - [ ] Tag
+ - [ ] Create maint/v0.X
+ - [ ] Update any bindings the core team works with
+
+We tag at least one release candidate. This RC must carry the new version in the headers, including the SOVERSION. If there are no significant issues found, we can go straight to the release after a single RC. This is up to the discretion of the release manager. There is no set time to have the candidate out, but we should we should give downstream projects at least a week to give feedback.
+
+Preparing the first release candidate includes updating the version number of libgit2 to the new version number. To do so, a pull request shall be submitted that adjusts the version number in the following places:
+
+- docs/changelog.md
+- include/git2/version.h
+- package.json
+
+As soon as the pull request is merged, the merge commit shall be tagged with a lightweight tag.
+
+The tagging happens via GitHub's "releases" tab which lets us attach release notes to a particular tag. In the description we include the changes in `docs/changelog.md` between the last full release. Use the following as a base for the release notes
+
+ This is the first release of the v0.X series, <codename>. The changelog follows.
+
+followed by the three sections in the changelog. For release candidates we can avoid copying the full changelog and only include any new entries.
+
+During the freeze, and certainly after the first release candidate, any bindings the core team work with should be updated in order to discover any issues that might come up with the multitude of approaches to memory management, embedding or linking.
+
+Create a branch `maint/v0.X` at the current state of `main` after you've created the tag. This will be used for maintenance releases and lets our dependents track the latest state of the series.
+
+## Maintenance release
+
+Every once in a while, when we feel we've accumulated a significant amount of backportable fixes in the mainline branch, we produce a maintenance release in order to provide fixes or improvements for those who track the releases. This also lets our users and integrators receive updates without having to upgrade to the next full release.
+
+As a rule of thumb, it's a good idea to produce a maintenance release for the current series when we're getting ready for a full release. This gives the (still) current series a last round of fixes without having to upgrade (which with us potentially means adjusting to API changes).
+
+Start by opening an issue. Use the following as a base.
+
+ Release v0.X.Y
+
+ Enough fixes have accumulated, let's release v0.X.Y
+
+ - [ ] Select the changes we want to backport
+ - [ ] Update maint/v0.X
+ - [ ] Tag
+
+The list of changes to backport does not need to be comprehensive and we might not backport something if the code in mainline has diverged significantly. These fixes do not include those which require API or ABI changes as we release under the same SOVERSION.
+
+Do not merge into the `maint/v0.X` until we are getting ready to produce a new release. There is always the possibility that we will need to produce a security release and those must only include the relevant security fixes and not arbitrary fixes we were planning on releasing at some point.
+
+Here we do not use release candidates as the changes are supposed to be small and proven.
+
+## Security releases
+
+This is the same as a maintenance release, except that the fix itself will most likely be developed in a private repository and will only be visible to a select group of people until the release.
+
+We have committed to providing security fixes for the latest two released versions. E.g. if the latest version is v0.28.x, then we will provide security fixes for both v0.28.x and v0.27.y.
+
+## Updating documentation
+
+We use docurium to generate our documentation. It is a tool written in ruby which leverages libclang's documentation parser. Install docurium
+
+ gem install docurium
+
+and run it against our description file with the tip of `main` checked out.
+
+ cm doc api.docurium
+
+It will start up a few processes and write out the results as a new commit onto the `gh-pages` branch. That can be pushed to GitHub to update what will show up on our documentation reference site.
diff --git a/docs/threading.md b/docs/threading.md
new file mode 100644
index 0000000..de085c8
--- /dev/null
+++ b/docs/threading.md
@@ -0,0 +1,110 @@
+Threading in libgit2
+==================
+
+Unless otherwise specified, libgit2 objects cannot be safely accessed by
+multiple threads simultaneously.
+
+There are also caveats on the cryptographic libraries libgit2 or its
+dependencies link to (more on this later). For libgit2 itself,
+provided you take the following into consideration you won't run into
+issues:
+
+Sharing objects
+---------------
+
+Use an object from a single thread at a time. Most data structures do
+not guard against concurrent access themselves. This is because they
+are rarely used in isolation and it makes more sense to synchronize
+access via a larger lock or similar mechanism.
+
+There are some objects which are read-only/immutable and are thus safe
+to share across threads, such as references and configuration
+snapshots.
+
+The `git_odb` object uses locking internally, and is thread-safe to use from
+multiple threads simultaneously.
+
+Error messages
+--------------
+
+The error message is thread-local. The `git_error_last()` call must
+happen on the same thread as the error in order to get the
+message. Often this will be the case regardless, but if you use
+something like the [GCD](http://en.wikipedia.org/wiki/Grand_Central_Dispatch)
+on macOS (where code is executed on an arbitrary thread), the code
+must make sure to retrieve the error code on the thread where the error
+happened.
+
+Threading and cryptographic libraries
+=======================================
+
+On Windows
+----------
+
+When built as a native Windows DLL, libgit2 uses WinCNG and WinHTTP,
+both of which are thread-safe. You do not need to do anything special.
+
+When using libssh2 which itself uses WinCNG, there are no special
+steps necessary. If you are using a MinGW or similar environment where
+libssh2 uses OpenSSL or libgcrypt, then the general case affects
+you.
+
+On macOS
+-----------
+
+By default we make use of CommonCrypto and SecureTransport for cryptographic
+support. These are thread-safe and you do not need to do anything special.
+
+Note that libssh2 may still use OpenSSL itself. In that case, the
+general case still affects you if you use ssh.
+
+General Case
+------------
+
+libgit2 will default to OpenSSL for HTTPS transport (except on Windows and
+macOS, as mentioned above). On any system, mbedTLS _may_ be optionally
+enabled as the security provider. OpenSSL is thread-safe starting at
+version 1.1.0. If your copy of libgit2 is linked against that version,
+you do not need to take any further steps.
+
+Older versions of OpenSSL are made to be thread-implementation agnostic, and the
+users of the library must set which locking function it should use. libgit2
+cannot know what to set as the user of libgit2 may also be using OpenSSL independently and
+the locking settings must then live outside the lifetime of libgit2.
+
+Even if libgit2 doesn't use OpenSSL directly, OpenSSL can still be used by
+libssh2 depending on the configuration. If OpenSSL is used by
+more than one library, you only need to set up threading for OpenSSL once.
+
+If libgit2 is linked against OpenSSL < 1.1.0, it provides a last-resort convenience function
+`git_openssl_set_locking()` (available in `sys/openssl.h`) to use the
+platform-native mutex mechanisms to perform the locking, which you can use
+if you do not want to use OpenSSL outside of libgit2, or you
+know that libgit2 will outlive the rest of the operations. It is then not
+safe to use OpenSSL multi-threaded after libgit2's shutdown function
+has been called. Note `git_openssl_set_locking()` only works if
+libgit2 uses OpenSSL directly - if OpenSSL is only used as a dependency
+of libssh2 as described above, `git_openssl_set_locking()` is a no-op.
+
+If your programming language offers a package/bindings for OpenSSL,
+you should very strongly prefer to use that in order to set up
+locking, as they provide a level of coordination which is impossible
+when using this function.
+
+See the
+[OpenSSL documentation](https://www.openssl.org/docs/crypto/threads.html)
+on threading for more details, and http://trac.libssh2.org/wiki/MultiThreading
+for a specific example of providing the threading callbacks.
+
+libssh2 may be linked against OpenSSL or libgcrypt. If it uses OpenSSL,
+see the above paragraphs. If it uses libgcrypt, then you need to
+set up its locking before using it multi-threaded. libgit2 has no
+direct connection to libgcrypt and thus has no convenience functions for
+it (but libgcrypt has macros). Read libgcrypt's
+[threading documentation for more information](http://www.gnupg.org/documentation/manuals/gcrypt/Multi_002dThreading.html)
+
+It is your responsibility as an application author or packager to know
+what your dependencies are linked against and to take the appropriate
+steps to ensure the cryptographic libraries are thread-safe. We agree
+that this situation is far from ideal but at this time it is something
+the application authors need to deal with.
diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md
new file mode 100644
index 0000000..085fff8
--- /dev/null
+++ b/docs/troubleshooting.md
@@ -0,0 +1,13 @@
+Troubleshooting libgit2 Problems
+================================
+
+CMake Failures
+--------------
+
+* **`Asked for OpenSSL TLS backend, but it wasn't found`**
+ CMake cannot find your SSL/TLS libraries. By default, libgit2 always
+ builds with HTTPS support, and you are encouraged to install the
+ OpenSSL libraries for your system (eg, `apt-get install libssl-dev`).
+
+ For development, if you simply want to disable HTTPS support entirely,
+ pass the `-DUSE_HTTPS=OFF` argument to `cmake` when configuring it.
diff --git a/docs/win32-longpaths.md b/docs/win32-longpaths.md
new file mode 100644
index 0000000..a18152f
--- /dev/null
+++ b/docs/win32-longpaths.md
@@ -0,0 +1,36 @@
+core.longpaths support
+======================
+
+Historically, Windows has limited absolute path lengths to `MAX_PATH`
+(260) characters.
+
+Unfortunately, 260 characters is a punishing small maximum. This is
+especially true for developers where dependencies may have dependencies
+in a folder, each dependency themselves having dependencies in a
+sub-folder, ad (seemingly) infinitum.
+
+So although the Windows APIs _by default_ honor this 260 character
+maximum, you can get around this by using separate APIs. Git honors a
+`core.longpaths` configuration option that allows some paths on Windows
+to exceed these 260 character limits.
+
+And because they've gone and done it, that means that libgit2 has to
+honor this value, too.
+
+Since `core.longpaths` is a _configuration option_ that means that we
+need to be able to resolve a configuration - including in _the repository
+itself_ in order to know whether long paths should be supported.
+
+Therefore, in libgit2, `core.longpaths` affects paths in working
+directories _only_. Paths to the repository, and to items inside the
+`.git` folder, must be no longer than 260 characters.
+
+This definition is required to avoid a paradoxical setting: if you
+had a repository in a folder that was 280 characters long, how would
+you know whether `core.longpaths` support should be enabled? Even if
+`core.longpaths` was set to true in a system configuration file, the
+repository itself may set `core.longpaths` to false in _its_ configuration
+file, which you could only read if `core.longpaths` were set to true.
+
+Thus, `core.longpaths` must _only_ apply to working directory items,
+and cannot apply to the `.git` folder or its contents.
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
new file mode 100644
index 0000000..8e38c7d
--- /dev/null
+++ b/examples/CMakeLists.txt
@@ -0,0 +1,18 @@
+# examples: code usage examples of libgit2
+
+file(GLOB SRC_EXAMPLES *.c *.h)
+
+add_executable(lg2 ${SRC_EXAMPLES})
+set_target_properties(lg2 PROPERTIES C_STANDARD 90)
+
+# Ensure that we do not use deprecated functions internally
+add_definitions(-DGIT_DEPRECATE_HARD)
+
+target_include_directories(lg2 PRIVATE ${LIBGIT2_INCLUDES} ${LIBGIT2_DEPENDENCY_INCLUDES})
+target_include_directories(lg2 SYSTEM PRIVATE ${LIBGIT2_SYSTEM_INCLUDES})
+
+if(WIN32 OR ANDROID)
+ target_link_libraries(lg2 libgit2package)
+else()
+ target_link_libraries(lg2 libgit2package pthread)
+endif()
diff --git a/examples/COPYING b/examples/COPYING
new file mode 100644
index 0000000..0e259d4
--- /dev/null
+++ b/examples/COPYING
@@ -0,0 +1,121 @@
+Creative Commons Legal Code
+
+CC0 1.0 Universal
+
+ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+ LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+ ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+ INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+ REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+ PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+ THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+ HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator
+and subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for
+the purpose of contributing to a commons of creative, cultural and
+scientific works ("Commons") that the public can reliably and without fear
+of later claims of infringement build upon, modify, incorporate in other
+works, reuse and redistribute as freely as possible in any form whatsoever
+and for any purposes, including without limitation commercial purposes.
+These owners may contribute to the Commons to promote the ideal of a free
+culture and the further production of creative, cultural and scientific
+works, or to gain reputation or greater distribution for their Work in
+part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any
+expectation of additional consideration or compensation, the person
+associating CC0 with a Work (the "Affirmer"), to the extent that he or she
+is an owner of Copyright and Related Rights in the Work, voluntarily
+elects to apply CC0 to the Work and publicly distribute the Work under its
+terms, with knowledge of his or her Copyright and Related Rights in the
+Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not
+limited to, the following:
+
+ i. the right to reproduce, adapt, distribute, perform, display,
+ communicate, and translate a Work;
+ ii. moral rights retained by the original author(s) and/or performer(s);
+iii. publicity and privacy rights pertaining to a person's image or
+ likeness depicted in a Work;
+ iv. rights protecting against unfair competition in regards to a Work,
+ subject to the limitations in paragraph 4(a), below;
+ v. rights protecting the extraction, dissemination, use and reuse of data
+ in a Work;
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+ European Parliament and of the Council of 11 March 1996 on the legal
+ protection of databases, and under any national implementation
+ thereof, including any amended or successor version of such
+ directive); and
+vii. other similar, equivalent or corresponding rights throughout the
+ world based on applicable law or treaty, and any national
+ implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention
+of, applicable law, Affirmer hereby overtly, fully, permanently,
+irrevocably and unconditionally waives, abandons, and surrenders all of
+Affirmer's Copyright and Related Rights and associated claims and causes
+of action, whether now known or unknown (including existing as well as
+future claims and causes of action), in the Work (i) in all territories
+worldwide, (ii) for the maximum duration provided by applicable law or
+treaty (including future time extensions), (iii) in any current or future
+medium and for any number of copies, and (iv) for any purpose whatsoever,
+including without limitation commercial, advertising or promotional
+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
+member of the public at large and to the detriment of Affirmer's heirs and
+successors, fully intending that such Waiver shall not be subject to
+revocation, rescission, cancellation, termination, or any other legal or
+equitable action to disrupt the quiet enjoyment of the Work by the public
+as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason
+be judged legally invalid or ineffective under applicable law, then the
+Waiver shall be preserved to the maximum extent permitted taking into
+account Affirmer's express Statement of Purpose. In addition, to the
+extent the Waiver is so judged Affirmer hereby grants to each affected
+person a royalty-free, non transferable, non sublicensable, non exclusive,
+irrevocable and unconditional license to exercise Affirmer's Copyright and
+Related Rights in the Work (i) in all territories worldwide, (ii) for the
+maximum duration provided by applicable law or treaty (including future
+time extensions), (iii) in any current or future medium and for any number
+of copies, and (iv) for any purpose whatsoever, including without
+limitation commercial, advertising or promotional purposes (the
+"License"). The License shall be deemed effective as of the date CC0 was
+applied by Affirmer to the Work. Should any part of the License for any
+reason be judged legally invalid or ineffective under applicable law, such
+partial invalidity or ineffectiveness shall not invalidate the remainder
+of the License, and in such case Affirmer hereby affirms that he or she
+will not (i) exercise any of his or her remaining Copyright and Related
+Rights in the Work or (ii) assert any associated claims and causes of
+action with respect to the Work, in either case contrary to Affirmer's
+express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+ surrendered, licensed or otherwise affected by this document.
+ b. Affirmer offers the Work as-is and makes no representations or
+ warranties of any kind concerning the Work, express, implied,
+ statutory or otherwise, including without limitation warranties of
+ title, merchantability, fitness for a particular purpose, non
+ infringement, or the absence of latent or other defects, accuracy, or
+ the present or absence of errors, whether or not discoverable, all to
+ the greatest extent permissible under applicable law.
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+ that may apply to the Work or any use thereof, including without
+ limitation any person's Copyright and Related Rights in the Work.
+ Further, Affirmer disclaims responsibility for obtaining any necessary
+ consents, permissions or other rights required for any use of the
+ Work.
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+ party to this document and has no duty or obligation with respect to
+ this CC0 or use of the Work.
diff --git a/examples/README.md b/examples/README.md
new file mode 100644
index 0000000..769c4b2
--- /dev/null
+++ b/examples/README.md
@@ -0,0 +1,22 @@
+libgit2 examples
+================
+
+These examples are a mixture of basic emulation of core Git command line
+functions and simple snippets demonstrating libgit2 API usage (for use
+with Docurium). As a whole, they are not vetted carefully for bugs, error
+handling, and cross-platform compatibility in the same manner as the rest
+of the code in libgit2, so copy with caution.
+
+That being said, you are welcome to copy code from these examples as
+desired when using libgit2. They have been [released to the public domain][cc0],
+so there are no restrictions on their use.
+
+[cc0]: COPYING
+
+For annotated HTML versions, see the "Examples" section of:
+
+ http://libgit2.github.com/libgit2
+
+such as:
+
+ http://libgit2.github.com/libgit2/ex/HEAD/general.html
diff --git a/examples/add.c b/examples/add.c
new file mode 100644
index 0000000..1c93b11
--- /dev/null
+++ b/examples/add.c
@@ -0,0 +1,157 @@
+/*
+ * libgit2 "add" example - shows how to modify the index
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+
+/**
+ * The following example demonstrates how to add files with libgit2.
+ *
+ * It will use the repository in the current working directory, and act
+ * on files passed as its parameters.
+ *
+ * Recognized options are:
+ * -v/--verbose: show the file's status after acting on it.
+ * -n/--dry-run: do not actually change the index.
+ * -u/--update: update the index instead of adding to it.
+ */
+
+enum index_mode {
+ INDEX_NONE,
+ INDEX_ADD
+};
+
+struct index_options {
+ int dry_run;
+ int verbose;
+ git_repository *repo;
+ enum index_mode mode;
+ int add_update;
+};
+
+/* Forward declarations for helpers */
+static void parse_opts(const char **repo_path, struct index_options *opts, struct args_info *args);
+int print_matched_cb(const char *path, const char *matched_pathspec, void *payload);
+
+int lg2_add(git_repository *repo, int argc, char **argv)
+{
+ git_index_matched_path_cb matched_cb = NULL;
+ git_index *index;
+ git_strarray array = {0};
+ struct index_options options = {0};
+ struct args_info args = ARGS_INFO_INIT;
+
+ options.mode = INDEX_ADD;
+
+ /* Parse the options & arguments. */
+ parse_opts(NULL, &options, &args);
+ strarray_from_args(&array, &args);
+
+ /* Grab the repository's index. */
+ check_lg2(git_repository_index(&index, repo), "Could not open repository index", NULL);
+
+ /* Setup a callback if the requested options need it */
+ if (options.verbose || options.dry_run) {
+ matched_cb = &print_matched_cb;
+ }
+
+ options.repo = repo;
+
+ /* Perform the requested action with the index and files */
+ if (options.add_update) {
+ git_index_update_all(index, &array, matched_cb, &options);
+ } else {
+ git_index_add_all(index, &array, 0, matched_cb, &options);
+ }
+
+ /* Cleanup memory */
+ git_index_write(index);
+ git_index_free(index);
+
+ return 0;
+}
+
+/*
+ * This callback is called for each file under consideration by
+ * git_index_(update|add)_all above.
+ * It makes uses of the callback's ability to abort the action.
+ */
+int print_matched_cb(const char *path, const char *matched_pathspec, void *payload)
+{
+ struct index_options *opts = (struct index_options *)(payload);
+ int ret;
+ unsigned status;
+ (void)matched_pathspec;
+
+ /* Get the file status */
+ if (git_status_file(&status, opts->repo, path) < 0)
+ return -1;
+
+ if ((status & GIT_STATUS_WT_MODIFIED) || (status & GIT_STATUS_WT_NEW)) {
+ printf("add '%s'\n", path);
+ ret = 0;
+ } else {
+ ret = 1;
+ }
+
+ if (opts->dry_run)
+ ret = 1;
+
+ return ret;
+}
+
+static void print_usage(void)
+{
+ fprintf(stderr, "usage: add [options] [--] file-spec [file-spec] [...]\n\n");
+ fprintf(stderr, "\t-n, --dry-run dry run\n");
+ fprintf(stderr, "\t-v, --verbose be verbose\n");
+ fprintf(stderr, "\t-u, --update update tracked files\n");
+ exit(1);
+}
+
+static void parse_opts(const char **repo_path, struct index_options *opts, struct args_info *args)
+{
+ if (args->argc <= 1)
+ print_usage();
+
+ for (args->pos = 1; args->pos < args->argc; ++args->pos) {
+ const char *curr = args->argv[args->pos];
+
+ if (curr[0] != '-') {
+ if (!strcmp("add", curr)) {
+ opts->mode = INDEX_ADD;
+ continue;
+ } else if (opts->mode == INDEX_NONE) {
+ fprintf(stderr, "missing command: %s", curr);
+ print_usage();
+ break;
+ } else {
+ /* We might be looking at a filename */
+ break;
+ }
+ } else if (match_bool_arg(&opts->verbose, args, "--verbose") ||
+ match_bool_arg(&opts->dry_run, args, "--dry-run") ||
+ match_str_arg(repo_path, args, "--git-dir") ||
+ (opts->mode == INDEX_ADD && match_bool_arg(&opts->add_update, args, "--update"))) {
+ continue;
+ } else if (match_bool_arg(NULL, args, "--help")) {
+ print_usage();
+ break;
+ } else if (match_arg_separator(args)) {
+ break;
+ } else {
+ fprintf(stderr, "Unsupported option %s.\n", curr);
+ print_usage();
+ }
+ }
+}
diff --git a/examples/args.c b/examples/args.c
new file mode 100644
index 0000000..533e157
--- /dev/null
+++ b/examples/args.c
@@ -0,0 +1,197 @@
+#include "common.h"
+#include "args.h"
+
+size_t is_prefixed(const char *str, const char *pfx)
+{
+ size_t len = strlen(pfx);
+ return strncmp(str, pfx, len) ? 0 : len;
+}
+
+int optional_str_arg(
+ const char **out, struct args_info *args, const char *opt, const char *def)
+{
+ const char *found = args->argv[args->pos];
+ size_t len = is_prefixed(found, opt);
+
+ if (!len)
+ return 0;
+
+ if (!found[len]) {
+ if (args->pos + 1 == args->argc) {
+ *out = def;
+ return 1;
+ }
+ args->pos += 1;
+ *out = args->argv[args->pos];
+ return 1;
+ }
+
+ if (found[len] == '=') {
+ *out = found + len + 1;
+ return 1;
+ }
+
+ return 0;
+}
+
+int match_str_arg(
+ const char **out, struct args_info *args, const char *opt)
+{
+ const char *found = args->argv[args->pos];
+ size_t len = is_prefixed(found, opt);
+
+ if (!len)
+ return 0;
+
+ if (!found[len]) {
+ if (args->pos + 1 == args->argc)
+ fatal("expected value following argument", opt);
+ args->pos += 1;
+ *out = args->argv[args->pos];
+ return 1;
+ }
+
+ if (found[len] == '=') {
+ *out = found + len + 1;
+ return 1;
+ }
+
+ return 0;
+}
+
+static const char *match_numeric_arg(struct args_info *args, const char *opt)
+{
+ const char *found = args->argv[args->pos];
+ size_t len = is_prefixed(found, opt);
+
+ if (!len)
+ return NULL;
+
+ if (!found[len]) {
+ if (args->pos + 1 == args->argc)
+ fatal("expected numeric value following argument", opt);
+ args->pos += 1;
+ found = args->argv[args->pos];
+ } else {
+ found = found + len;
+ if (*found == '=')
+ found++;
+ }
+
+ return found;
+}
+
+int match_uint16_arg(
+ uint16_t *out, struct args_info *args, const char *opt)
+{
+ const char *found = match_numeric_arg(args, opt);
+ uint16_t val;
+ char *endptr = NULL;
+
+ if (!found)
+ return 0;
+
+ val = (uint16_t)strtoul(found, &endptr, 0);
+ if (!endptr || *endptr != '\0')
+ fatal("expected number after argument", opt);
+
+ if (out)
+ *out = val;
+ return 1;
+}
+
+int match_uint32_arg(
+ uint32_t *out, struct args_info *args, const char *opt)
+{
+ const char *found = match_numeric_arg(args, opt);
+ uint16_t val;
+ char *endptr = NULL;
+
+ if (!found)
+ return 0;
+
+ val = (uint32_t)strtoul(found, &endptr, 0);
+ if (!endptr || *endptr != '\0')
+ fatal("expected number after argument", opt);
+
+ if (out)
+ *out = val;
+ return 1;
+}
+
+static int match_int_internal(
+ int *out, const char *str, int allow_negative, const char *opt)
+{
+ char *endptr = NULL;
+ int val = (int)strtol(str, &endptr, 10);
+
+ if (!endptr || *endptr != '\0')
+ fatal("expected number", opt);
+ else if (val < 0 && !allow_negative)
+ fatal("negative values are not allowed", opt);
+
+ if (out)
+ *out = val;
+
+ return 1;
+}
+
+int match_bool_arg(int *out, struct args_info *args, const char *opt)
+{
+ const char *found = args->argv[args->pos];
+
+ if (!strcmp(found, opt)) {
+ *out = 1;
+ return 1;
+ }
+
+ if (!strncmp(found, "--no-", strlen("--no-")) &&
+ !strcmp(found + strlen("--no-"), opt + 2)) {
+ *out = 0;
+ return 1;
+ }
+
+ *out = -1;
+ return 0;
+}
+
+int is_integer(int *out, const char *str, int allow_negative)
+{
+ return match_int_internal(out, str, allow_negative, NULL);
+}
+
+int match_int_arg(
+ int *out, struct args_info *args, const char *opt, int allow_negative)
+{
+ const char *found = match_numeric_arg(args, opt);
+ if (!found)
+ return 0;
+ return match_int_internal(out, found, allow_negative, opt);
+}
+
+int match_arg_separator(struct args_info *args)
+{
+ if (args->opts_done)
+ return 1;
+
+ if (strcmp(args->argv[args->pos], "--") != 0)
+ return 0;
+
+ args->opts_done = 1;
+ args->pos++;
+ return 1;
+}
+
+void strarray_from_args(git_strarray *array, struct args_info *args)
+{
+ size_t i;
+
+ array->count = args->argc - args->pos;
+ array->strings = calloc(array->count, sizeof(char *));
+ assert(array->strings != NULL);
+
+ for (i = 0; args->pos < args->argc; ++args->pos) {
+ array->strings[i++] = args->argv[args->pos];
+ }
+ args->pos = args->argc;
+}
diff --git a/examples/args.h b/examples/args.h
new file mode 100644
index 0000000..d626f98
--- /dev/null
+++ b/examples/args.h
@@ -0,0 +1,90 @@
+#ifndef INCLUDE_examples_args_h__
+#define INCLUDE_examples_args_h__
+
+/**
+ * Argument-processing helper structure
+ */
+struct args_info {
+ int argc;
+ char **argv;
+ int pos;
+ int opts_done : 1; /**< Did we see a -- separator */
+};
+#define ARGS_INFO_INIT { argc, argv, 0, 0 }
+#define ARGS_CURRENT(args) args->argv[args->pos]
+
+/**
+ * Check if a string has the given prefix. Returns 0 if not prefixed
+ * or the length of the prefix if it is.
+ */
+extern size_t is_prefixed(const char *str, const char *pfx);
+
+/**
+ * Match an integer string, returning 1 if matched, 0 if not.
+ */
+extern int is_integer(int *out, const char *str, int allow_negative);
+
+/**
+ * Check current `args` entry against `opt` string. If it matches
+ * exactly, take the next arg as a string; if it matches as a prefix with
+ * an equal sign, take the remainder as a string; if value not supplied,
+ * default value `def` will be given. otherwise return 0.
+ */
+extern int optional_str_arg(
+ const char **out, struct args_info *args, const char *opt, const char *def);
+
+/**
+ * Check current `args` entry against `opt` string. If it matches
+ * exactly, take the next arg as a string; if it matches as a prefix with
+ * an equal sign, take the remainder as a string; otherwise return 0.
+ */
+extern int match_str_arg(
+ const char **out, struct args_info *args, const char *opt);
+
+/**
+ * Check current `args` entry against `opt` string parsing as uint16. If
+ * `opt` matches exactly, take the next arg as a uint16_t value; if `opt`
+ * is a prefix (equal sign optional), take the remainder of the arg as a
+ * uint16_t value; otherwise return 0.
+ */
+extern int match_uint16_arg(
+ uint16_t *out, struct args_info *args, const char *opt);
+
+/**
+ * Check current `args` entry against `opt` string parsing as uint32. If
+ * `opt` matches exactly, take the next arg as a uint16_t value; if `opt`
+ * is a prefix (equal sign optional), take the remainder of the arg as a
+ * uint32_t value; otherwise return 0.
+ */
+extern int match_uint32_arg(
+ uint32_t *out, struct args_info *args, const char *opt);
+
+/**
+ * Check current `args` entry against `opt` string parsing as int. If
+ * `opt` matches exactly, take the next arg as an int value; if it matches
+ * as a prefix (equal sign optional), take the remainder of the arg as a
+ * int value; otherwise return 0.
+ */
+extern int match_int_arg(
+ int *out, struct args_info *args, const char *opt, int allow_negative);
+
+/**
+ * Check current `args` entry against a "bool" `opt` (ie. --[no-]progress).
+ * If `opt` matches positively, out will be set to 1, or if `opt` matches
+ * negatively, out will be set to 0, and in both cases 1 will be returned.
+ * If neither the positive or the negative form of opt matched, out will be -1,
+ * and 0 will be returned.
+ */
+extern int match_bool_arg(int *out, struct args_info *args, const char *opt);
+
+/**
+ * Check if we're processing past the single -- separator
+ */
+extern int match_arg_separator(struct args_info *args);
+
+/**
+ * Consume all remaining arguments in a git_strarray
+ */
+extern void strarray_from_args(git_strarray *array, struct args_info *args);
+
+#endif
diff --git a/examples/blame.c b/examples/blame.c
new file mode 100644
index 0000000..77087a5
--- /dev/null
+++ b/examples/blame.c
@@ -0,0 +1,198 @@
+/*
+ * libgit2 "blame" example - shows how to use the blame API
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+
+/**
+ * This example demonstrates how to invoke the libgit2 blame API to roughly
+ * simulate the output of `git blame` and a few of its command line arguments.
+ */
+
+struct blame_opts {
+ char *path;
+ char *commitspec;
+ int C;
+ int M;
+ int start_line;
+ int end_line;
+ int F;
+};
+static void parse_opts(struct blame_opts *o, int argc, char *argv[]);
+
+int lg2_blame(git_repository *repo, int argc, char *argv[])
+{
+ int line, break_on_null_hunk;
+ git_object_size_t i, rawsize;
+ char spec[1024] = {0};
+ struct blame_opts o = {0};
+ const char *rawdata;
+ git_revspec revspec = {0};
+ git_blame_options blameopts = GIT_BLAME_OPTIONS_INIT;
+ git_blame *blame = NULL;
+ git_blob *blob;
+ git_object *obj;
+
+ parse_opts(&o, argc, argv);
+ if (o.M) blameopts.flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES;
+ if (o.C) blameopts.flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES;
+ if (o.F) blameopts.flags |= GIT_BLAME_FIRST_PARENT;
+
+ /**
+ * The commit range comes in "committish" form. Use the rev-parse API to
+ * nail down the end points.
+ */
+ if (o.commitspec) {
+ check_lg2(git_revparse(&revspec, repo, o.commitspec), "Couldn't parse commit spec", NULL);
+ if (revspec.flags & GIT_REVSPEC_SINGLE) {
+ git_oid_cpy(&blameopts.newest_commit, git_object_id(revspec.from));
+ git_object_free(revspec.from);
+ } else {
+ git_oid_cpy(&blameopts.oldest_commit, git_object_id(revspec.from));
+ git_oid_cpy(&blameopts.newest_commit, git_object_id(revspec.to));
+ git_object_free(revspec.from);
+ git_object_free(revspec.to);
+ }
+ }
+
+ /** Run the blame. */
+ check_lg2(git_blame_file(&blame, repo, o.path, &blameopts), "Blame error", NULL);
+
+ /**
+ * Get the raw data inside the blob for output. We use the
+ * `committish:path/to/file.txt` format to find it.
+ */
+ if (git_oid_is_zero(&blameopts.newest_commit))
+ strcpy(spec, "HEAD");
+ else
+ git_oid_tostr(spec, sizeof(spec), &blameopts.newest_commit);
+ strcat(spec, ":");
+ strcat(spec, o.path);
+
+ check_lg2(git_revparse_single(&obj, repo, spec), "Object lookup error", NULL);
+ check_lg2(git_blob_lookup(&blob, repo, git_object_id(obj)), "Blob lookup error", NULL);
+ git_object_free(obj);
+
+ rawdata = git_blob_rawcontent(blob);
+ rawsize = git_blob_rawsize(blob);
+
+ /** Produce the output. */
+ line = 1;
+ i = 0;
+ break_on_null_hunk = 0;
+ while (i < rawsize) {
+ const char *eol = memchr(rawdata + i, '\n', (size_t)(rawsize - i));
+ char oid[10] = {0};
+ const git_blame_hunk *hunk = git_blame_get_hunk_byline(blame, line);
+
+ if (break_on_null_hunk && !hunk)
+ break;
+
+ if (hunk) {
+ char sig[128] = {0};
+ break_on_null_hunk = 1;
+
+ git_oid_tostr(oid, 10, &hunk->final_commit_id);
+ snprintf(sig, 30, "%s <%s>", hunk->final_signature->name, hunk->final_signature->email);
+
+ printf("%s ( %-30s %3d) %.*s\n",
+ oid,
+ sig,
+ line,
+ (int)(eol - rawdata - i),
+ rawdata + i);
+ }
+
+ i = (int)(eol - rawdata + 1);
+ line++;
+ }
+
+ /** Cleanup. */
+ git_blob_free(blob);
+ git_blame_free(blame);
+
+ return 0;
+}
+
+/** Tell the user how to make this thing work. */
+static void usage(const char *msg, const char *arg)
+{
+ if (msg && arg)
+ fprintf(stderr, "%s: %s\n", msg, arg);
+ else if (msg)
+ fprintf(stderr, "%s\n", msg);
+ fprintf(stderr, "usage: blame [options] [<commit range>] <path>\n");
+ fprintf(stderr, "\n");
+ fprintf(stderr, " <commit range> example: `HEAD~10..HEAD`, or `1234abcd`\n");
+ fprintf(stderr, " -L <n,m> process only line range n-m, counting from 1\n");
+ fprintf(stderr, " -M find line moves within and across files\n");
+ fprintf(stderr, " -C find line copies within and across files\n");
+ fprintf(stderr, " -F follow only the first parent commits\n");
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+/** Parse the arguments. */
+static void parse_opts(struct blame_opts *o, int argc, char *argv[])
+{
+ int i;
+ char *bare_args[3] = {0};
+
+ if (argc < 2) usage(NULL, NULL);
+
+ for (i=1; i<argc; i++) {
+ char *a = argv[i];
+
+ if (a[0] != '-') {
+ int i=0;
+ while (bare_args[i] && i < 3) ++i;
+ if (i >= 3)
+ usage("Invalid argument set", NULL);
+ bare_args[i] = a;
+ }
+ else if (!strcmp(a, "--"))
+ continue;
+ else if (!strcasecmp(a, "-M"))
+ o->M = 1;
+ else if (!strcasecmp(a, "-C"))
+ o->C = 1;
+ else if (!strcasecmp(a, "-F"))
+ o->F = 1;
+ else if (!strcasecmp(a, "-L")) {
+ i++; a = argv[i];
+ if (i >= argc) fatal("Not enough arguments to -L", NULL);
+ check_lg2(sscanf(a, "%d,%d", &o->start_line, &o->end_line)-2, "-L format error", NULL);
+ }
+ else {
+ /* commit range */
+ if (o->commitspec) fatal("Only one commit spec allowed", NULL);
+ o->commitspec = a;
+ }
+ }
+
+ /* Handle the bare arguments */
+ if (!bare_args[0]) usage("Please specify a path", NULL);
+ o->path = bare_args[0];
+ if (bare_args[1]) {
+ /* <commitspec> <path> */
+ o->path = bare_args[1];
+ o->commitspec = bare_args[0];
+ }
+ if (bare_args[2]) {
+ /* <oldcommit> <newcommit> <path> */
+ char spec[128] = {0};
+ o->path = bare_args[2];
+ sprintf(spec, "%s..%s", bare_args[0], bare_args[1]);
+ o->commitspec = spec;
+ }
+}
diff --git a/examples/cat-file.c b/examples/cat-file.c
new file mode 100644
index 0000000..741edb4
--- /dev/null
+++ b/examples/cat-file.c
@@ -0,0 +1,239 @@
+/*
+ * libgit2 "cat-file" example - shows how to print data from the ODB
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+
+static void print_signature(const char *header, const git_signature *sig)
+{
+ char sign;
+ int offset, hours, minutes;
+
+ if (!sig)
+ return;
+
+ offset = sig->when.offset;
+ if (offset < 0) {
+ sign = '-';
+ offset = -offset;
+ } else {
+ sign = '+';
+ }
+
+ hours = offset / 60;
+ minutes = offset % 60;
+
+ printf("%s %s <%s> %ld %c%02d%02d\n",
+ header, sig->name, sig->email, (long)sig->when.time,
+ sign, hours, minutes);
+}
+
+/** Printing out a blob is simple, get the contents and print */
+static void show_blob(const git_blob *blob)
+{
+ /* ? Does this need crlf filtering? */
+ fwrite(git_blob_rawcontent(blob), (size_t)git_blob_rawsize(blob), 1, stdout);
+}
+
+/** Show each entry with its type, id and attributes */
+static void show_tree(const git_tree *tree)
+{
+ size_t i, max_i = (int)git_tree_entrycount(tree);
+ char oidstr[GIT_OID_SHA1_HEXSIZE + 1];
+ const git_tree_entry *te;
+
+ for (i = 0; i < max_i; ++i) {
+ te = git_tree_entry_byindex(tree, i);
+
+ git_oid_tostr(oidstr, sizeof(oidstr), git_tree_entry_id(te));
+
+ printf("%06o %s %s\t%s\n",
+ git_tree_entry_filemode(te),
+ git_object_type2string(git_tree_entry_type(te)),
+ oidstr, git_tree_entry_name(te));
+ }
+}
+
+/**
+ * Commits and tags have a few interesting fields in their header.
+ */
+static void show_commit(const git_commit *commit)
+{
+ unsigned int i, max_i;
+ char oidstr[GIT_OID_SHA1_HEXSIZE + 1];
+
+ git_oid_tostr(oidstr, sizeof(oidstr), git_commit_tree_id(commit));
+ printf("tree %s\n", oidstr);
+
+ max_i = (unsigned int)git_commit_parentcount(commit);
+ for (i = 0; i < max_i; ++i) {
+ git_oid_tostr(oidstr, sizeof(oidstr), git_commit_parent_id(commit, i));
+ printf("parent %s\n", oidstr);
+ }
+
+ print_signature("author", git_commit_author(commit));
+ print_signature("committer", git_commit_committer(commit));
+
+ if (git_commit_message(commit))
+ printf("\n%s\n", git_commit_message(commit));
+}
+
+static void show_tag(const git_tag *tag)
+{
+ char oidstr[GIT_OID_SHA1_HEXSIZE + 1];
+
+ git_oid_tostr(oidstr, sizeof(oidstr), git_tag_target_id(tag));;
+ printf("object %s\n", oidstr);
+ printf("type %s\n", git_object_type2string(git_tag_target_type(tag)));
+ printf("tag %s\n", git_tag_name(tag));
+ print_signature("tagger", git_tag_tagger(tag));
+
+ if (git_tag_message(tag))
+ printf("\n%s\n", git_tag_message(tag));
+}
+
+typedef enum {
+ SHOW_TYPE = 1,
+ SHOW_SIZE = 2,
+ SHOW_NONE = 3,
+ SHOW_PRETTY = 4
+} catfile_mode;
+
+/* Forward declarations for option-parsing helper */
+struct catfile_options {
+ const char *dir;
+ const char *rev;
+ catfile_mode action;
+ int verbose;
+};
+
+static void parse_opts(struct catfile_options *o, int argc, char *argv[]);
+
+
+/** Entry point for this command */
+int lg2_cat_file(git_repository *repo, int argc, char *argv[])
+{
+ struct catfile_options o = { ".", NULL, 0, 0 };
+ git_object *obj = NULL;
+ char oidstr[GIT_OID_SHA1_HEXSIZE + 1];
+
+ parse_opts(&o, argc, argv);
+
+ check_lg2(git_revparse_single(&obj, repo, o.rev),
+ "Could not resolve", o.rev);
+
+ if (o.verbose) {
+ char oidstr[GIT_OID_SHA1_HEXSIZE + 1];
+ git_oid_tostr(oidstr, sizeof(oidstr), git_object_id(obj));
+
+ printf("%s %s\n--\n",
+ git_object_type2string(git_object_type(obj)), oidstr);
+ }
+
+ switch (o.action) {
+ case SHOW_TYPE:
+ printf("%s\n", git_object_type2string(git_object_type(obj)));
+ break;
+ case SHOW_SIZE: {
+ git_odb *odb;
+ git_odb_object *odbobj;
+
+ check_lg2(git_repository_odb(&odb, repo), "Could not open ODB", NULL);
+ check_lg2(git_odb_read(&odbobj, odb, git_object_id(obj)),
+ "Could not find obj", NULL);
+
+ printf("%ld\n", (long)git_odb_object_size(odbobj));
+
+ git_odb_object_free(odbobj);
+ git_odb_free(odb);
+ }
+ break;
+ case SHOW_NONE:
+ /* just want return result */
+ break;
+ case SHOW_PRETTY:
+
+ switch (git_object_type(obj)) {
+ case GIT_OBJECT_BLOB:
+ show_blob((const git_blob *)obj);
+ break;
+ case GIT_OBJECT_COMMIT:
+ show_commit((const git_commit *)obj);
+ break;
+ case GIT_OBJECT_TREE:
+ show_tree((const git_tree *)obj);
+ break;
+ case GIT_OBJECT_TAG:
+ show_tag((const git_tag *)obj);
+ break;
+ default:
+ printf("unknown %s\n", oidstr);
+ break;
+ }
+ break;
+ }
+
+ git_object_free(obj);
+
+ return 0;
+}
+
+/** Print out usage information */
+static void usage(const char *message, const char *arg)
+{
+ if (message && arg)
+ fprintf(stderr, "%s: %s\n", message, arg);
+ else if (message)
+ fprintf(stderr, "%s\n", message);
+ fprintf(stderr,
+ "usage: cat-file (-t | -s | -e | -p) [-v] [-q] "
+ "[-h|--help] [--git-dir=<dir>] <object>\n");
+ exit(1);
+}
+
+/** Parse the command-line options taken from git */
+static void parse_opts(struct catfile_options *o, int argc, char *argv[])
+{
+ struct args_info args = ARGS_INFO_INIT;
+
+ for (args.pos = 1; args.pos < argc; ++args.pos) {
+ char *a = argv[args.pos];
+
+ if (a[0] != '-') {
+ if (o->rev != NULL)
+ usage("Only one rev should be provided", NULL);
+ else
+ o->rev = a;
+ }
+ else if (!strcmp(a, "-t"))
+ o->action = SHOW_TYPE;
+ else if (!strcmp(a, "-s"))
+ o->action = SHOW_SIZE;
+ else if (!strcmp(a, "-e"))
+ o->action = SHOW_NONE;
+ else if (!strcmp(a, "-p"))
+ o->action = SHOW_PRETTY;
+ else if (!strcmp(a, "-q"))
+ o->verbose = 0;
+ else if (!strcmp(a, "-v"))
+ o->verbose = 1;
+ else if (!strcmp(a, "--help") || !strcmp(a, "-h"))
+ usage(NULL, NULL);
+ else if (!match_str_arg(&o->dir, &args, "--git-dir"))
+ usage("Unknown option", a);
+ }
+
+ if (!o->action || !o->rev)
+ usage(NULL, NULL);
+
+}
diff --git a/examples/checkout.c b/examples/checkout.c
new file mode 100644
index 0000000..ac7b742
--- /dev/null
+++ b/examples/checkout.c
@@ -0,0 +1,290 @@
+/*
+ * libgit2 "checkout" example - shows how to perform checkouts
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+
+/* Define the printf format specifier to use for size_t output */
+#if defined(_MSC_VER) || defined(__MINGW32__)
+# define PRIuZ "Iu"
+# define PRIxZ "Ix"
+# define PRIdZ "Id"
+#else
+# define PRIuZ "zu"
+# define PRIxZ "zx"
+# define PRIdZ "zd"
+#endif
+
+/**
+ * The following example demonstrates how to do checkouts with libgit2.
+ *
+ * Recognized options are :
+ * --force: force the checkout to happen.
+ * --[no-]progress: show checkout progress, on by default.
+ * --perf: show performance data.
+ */
+
+typedef struct {
+ int force : 1;
+ int progress : 1;
+ int perf : 1;
+} checkout_options;
+
+static void print_usage(void)
+{
+ fprintf(stderr, "usage: checkout [options] <branch>\n"
+ "Options are :\n"
+ " --git-dir: use the following git repository.\n"
+ " --force: force the checkout.\n"
+ " --[no-]progress: show checkout progress.\n"
+ " --perf: show performance data.\n");
+ exit(1);
+}
+
+static void parse_options(const char **repo_path, checkout_options *opts, struct args_info *args)
+{
+ if (args->argc <= 1)
+ print_usage();
+
+ memset(opts, 0, sizeof(*opts));
+
+ /* Default values */
+ opts->progress = 1;
+
+ for (args->pos = 1; args->pos < args->argc; ++args->pos) {
+ const char *curr = args->argv[args->pos];
+ int bool_arg;
+
+ if (match_arg_separator(args)) {
+ break;
+ } else if (!strcmp(curr, "--force")) {
+ opts->force = 1;
+ } else if (match_bool_arg(&bool_arg, args, "--progress")) {
+ opts->progress = bool_arg;
+ } else if (match_bool_arg(&bool_arg, args, "--perf")) {
+ opts->perf = bool_arg;
+ } else if (match_str_arg(repo_path, args, "--git-dir")) {
+ continue;
+ } else {
+ break;
+ }
+ }
+}
+
+/**
+ * This function is called to report progression, ie. it's called once with
+ * a NULL path and the number of total steps, then for each subsequent path,
+ * the current completed_step value.
+ */
+static void print_checkout_progress(const char *path, size_t completed_steps, size_t total_steps, void *payload)
+{
+ (void)payload;
+ if (path == NULL) {
+ printf("checkout started: %" PRIuZ " steps\n", total_steps);
+ } else {
+ printf("checkout: %s %" PRIuZ "/%" PRIuZ "\n", path, completed_steps, total_steps);
+ }
+}
+
+/**
+ * This function is called when the checkout completes, and is used to report the
+ * number of syscalls performed.
+ */
+static void print_perf_data(const git_checkout_perfdata *perfdata, void *payload)
+{
+ (void)payload;
+ printf("perf: stat: %" PRIuZ " mkdir: %" PRIuZ " chmod: %" PRIuZ "\n",
+ perfdata->stat_calls, perfdata->mkdir_calls, perfdata->chmod_calls);
+}
+
+/**
+ * This is the main "checkout <branch>" function, responsible for performing
+ * a branch-based checkout.
+ */
+static int perform_checkout_ref(git_repository *repo, git_annotated_commit *target, const char *target_ref, checkout_options *opts)
+{
+ git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_reference *ref = NULL, *branch = NULL;
+ git_commit *target_commit = NULL;
+ int err;
+
+ /** Setup our checkout options from the parsed options */
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+ if (opts->force)
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ if (opts->progress)
+ checkout_opts.progress_cb = print_checkout_progress;
+
+ if (opts->perf)
+ checkout_opts.perfdata_cb = print_perf_data;
+
+ /** Grab the commit we're interested to move to */
+ err = git_commit_lookup(&target_commit, repo, git_annotated_commit_id(target));
+ if (err != 0) {
+ fprintf(stderr, "failed to lookup commit: %s\n", git_error_last()->message);
+ goto cleanup;
+ }
+
+ /**
+ * Perform the checkout so the workdir corresponds to what target_commit
+ * contains.
+ *
+ * Note that it's okay to pass a git_commit here, because it will be
+ * peeled to a tree.
+ */
+ err = git_checkout_tree(repo, (const git_object *)target_commit, &checkout_opts);
+ if (err != 0) {
+ fprintf(stderr, "failed to checkout tree: %s\n", git_error_last()->message);
+ goto cleanup;
+ }
+
+ /**
+ * Now that the checkout has completed, we have to update HEAD.
+ *
+ * Depending on the "origin" of target (ie. it's an OID or a branch name),
+ * we might need to detach HEAD.
+ */
+ if (git_annotated_commit_ref(target)) {
+ const char *target_head;
+
+ if ((err = git_reference_lookup(&ref, repo, git_annotated_commit_ref(target))) < 0)
+ goto error;
+
+ if (git_reference_is_remote(ref)) {
+ if ((err = git_branch_create_from_annotated(&branch, repo, target_ref, target, 0)) < 0)
+ goto error;
+ target_head = git_reference_name(branch);
+ } else {
+ target_head = git_annotated_commit_ref(target);
+ }
+
+ err = git_repository_set_head(repo, target_head);
+ } else {
+ err = git_repository_set_head_detached_from_annotated(repo, target);
+ }
+
+error:
+ if (err != 0) {
+ fprintf(stderr, "failed to update HEAD reference: %s\n", git_error_last()->message);
+ goto cleanup;
+ }
+
+cleanup:
+ git_commit_free(target_commit);
+ git_reference_free(branch);
+ git_reference_free(ref);
+
+ return err;
+}
+
+/**
+ * This corresponds to `git switch --guess`: if a given ref does
+ * not exist, git will by default try to guess the reference by
+ * seeing whether any remote has a branch called <ref>. If there
+ * is a single remote only that has it, then it is assumed to be
+ * the desired reference and a local branch is created for it.
+ *
+ * The following is a simplified implementation. It will not try
+ * to check whether the ref is unique across all remotes.
+ */
+static int guess_refish(git_annotated_commit **out, git_repository *repo, const char *ref)
+{
+ git_strarray remotes = { NULL, 0 };
+ git_reference *remote_ref = NULL;
+ int error;
+ size_t i;
+
+ if ((error = git_remote_list(&remotes, repo)) < 0)
+ goto out;
+
+ for (i = 0; i < remotes.count; i++) {
+ char *refname = NULL;
+ size_t reflen;
+
+ reflen = snprintf(refname, 0, "refs/remotes/%s/%s", remotes.strings[i], ref);
+ if ((refname = malloc(reflen + 1)) == NULL) {
+ error = -1;
+ goto next;
+ }
+ snprintf(refname, reflen + 1, "refs/remotes/%s/%s", remotes.strings[i], ref);
+
+ if ((error = git_reference_lookup(&remote_ref, repo, refname)) < 0)
+ goto next;
+
+ break;
+next:
+ free(refname);
+ if (error < 0 && error != GIT_ENOTFOUND)
+ break;
+ }
+
+ if (!remote_ref) {
+ error = GIT_ENOTFOUND;
+ goto out;
+ }
+
+ if ((error = git_annotated_commit_from_ref(out, repo, remote_ref)) < 0)
+ goto out;
+
+out:
+ git_reference_free(remote_ref);
+ git_strarray_dispose(&remotes);
+ return error;
+}
+
+/** That example's entry point */
+int lg2_checkout(git_repository *repo, int argc, char **argv)
+{
+ struct args_info args = ARGS_INFO_INIT;
+ checkout_options opts;
+ git_repository_state_t state;
+ git_annotated_commit *checkout_target = NULL;
+ int err = 0;
+ const char *path = ".";
+
+ /** Parse our command line options */
+ parse_options(&path, &opts, &args);
+
+ /** Make sure we're not about to checkout while something else is going on */
+ state = git_repository_state(repo);
+ if (state != GIT_REPOSITORY_STATE_NONE) {
+ fprintf(stderr, "repository is in unexpected state %d\n", state);
+ goto cleanup;
+ }
+
+ if (match_arg_separator(&args)) {
+ /**
+ * Try to checkout the given path
+ */
+
+ fprintf(stderr, "unhandled path-based checkout\n");
+ err = 1;
+ goto cleanup;
+ } else {
+ /**
+ * Try to resolve a "refish" argument to a target libgit2 can use
+ */
+ if ((err = resolve_refish(&checkout_target, repo, args.argv[args.pos])) < 0 &&
+ (err = guess_refish(&checkout_target, repo, args.argv[args.pos])) < 0) {
+ fprintf(stderr, "failed to resolve %s: %s\n", args.argv[args.pos], git_error_last()->message);
+ goto cleanup;
+ }
+ err = perform_checkout_ref(repo, checkout_target, args.argv[args.pos], &opts);
+ }
+
+cleanup:
+ git_annotated_commit_free(checkout_target);
+
+ return err;
+}
diff --git a/examples/clone.c b/examples/clone.c
new file mode 100644
index 0000000..22d9d9b
--- /dev/null
+++ b/examples/clone.c
@@ -0,0 +1,104 @@
+#include "common.h"
+
+typedef struct progress_data {
+ git_indexer_progress fetch_progress;
+ size_t completed_steps;
+ size_t total_steps;
+ const char *path;
+} progress_data;
+
+static void print_progress(const progress_data *pd)
+{
+ int network_percent = pd->fetch_progress.total_objects > 0 ?
+ (100*pd->fetch_progress.received_objects) / pd->fetch_progress.total_objects :
+ 0;
+ int index_percent = pd->fetch_progress.total_objects > 0 ?
+ (100*pd->fetch_progress.indexed_objects) / pd->fetch_progress.total_objects :
+ 0;
+
+ int checkout_percent = pd->total_steps > 0
+ ? (int)((100 * pd->completed_steps) / pd->total_steps)
+ : 0;
+ size_t kbytes = pd->fetch_progress.received_bytes / 1024;
+
+ if (pd->fetch_progress.total_objects &&
+ pd->fetch_progress.received_objects == pd->fetch_progress.total_objects) {
+ printf("Resolving deltas %u/%u\r",
+ pd->fetch_progress.indexed_deltas,
+ pd->fetch_progress.total_deltas);
+ } else {
+ printf("net %3d%% (%4" PRIuZ " kb, %5u/%5u) / idx %3d%% (%5u/%5u) / chk %3d%% (%4" PRIuZ "/%4" PRIuZ")%s\n",
+ network_percent, kbytes,
+ pd->fetch_progress.received_objects, pd->fetch_progress.total_objects,
+ index_percent, pd->fetch_progress.indexed_objects, pd->fetch_progress.total_objects,
+ checkout_percent,
+ pd->completed_steps, pd->total_steps,
+ pd->path);
+ }
+}
+
+static int sideband_progress(const char *str, int len, void *payload)
+{
+ (void)payload; /* unused */
+
+ printf("remote: %.*s", len, str);
+ fflush(stdout);
+ return 0;
+}
+
+static int fetch_progress(const git_indexer_progress *stats, void *payload)
+{
+ progress_data *pd = (progress_data*)payload;
+ pd->fetch_progress = *stats;
+ print_progress(pd);
+ return 0;
+}
+static void checkout_progress(const char *path, size_t cur, size_t tot, void *payload)
+{
+ progress_data *pd = (progress_data*)payload;
+ pd->completed_steps = cur;
+ pd->total_steps = tot;
+ pd->path = path;
+ print_progress(pd);
+}
+
+
+int lg2_clone(git_repository *repo, int argc, char **argv)
+{
+ progress_data pd = {{0}};
+ git_repository *cloned_repo = NULL;
+ git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT;
+ git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
+ const char *url = argv[1];
+ const char *path = argv[2];
+ int error;
+
+ (void)repo; /* unused */
+
+ /* Validate args */
+ if (argc < 3) {
+ printf ("USAGE: %s <url> <path>\n", argv[0]);
+ return -1;
+ }
+
+ /* Set up options */
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+ checkout_opts.progress_cb = checkout_progress;
+ checkout_opts.progress_payload = &pd;
+ clone_opts.checkout_opts = checkout_opts;
+ clone_opts.fetch_opts.callbacks.sideband_progress = sideband_progress;
+ clone_opts.fetch_opts.callbacks.transfer_progress = &fetch_progress;
+ clone_opts.fetch_opts.callbacks.credentials = cred_acquire_cb;
+ clone_opts.fetch_opts.callbacks.payload = &pd;
+
+ /* Do the clone */
+ error = git_clone(&cloned_repo, url, path, &clone_opts);
+ printf("\n");
+ if (error != 0) {
+ const git_error *err = git_error_last();
+ if (err) printf("ERROR %d: %s\n", err->klass, err->message);
+ else printf("ERROR %d: no detailed info\n", error);
+ }
+ else if (cloned_repo) git_repository_free(cloned_repo);
+ return error;
+}
diff --git a/examples/commit.c b/examples/commit.c
new file mode 100644
index 0000000..aedc1af
--- /dev/null
+++ b/examples/commit.c
@@ -0,0 +1,86 @@
+/*
+ * libgit2 "commit" example - shows how to create a git commit
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+
+/**
+ * This example demonstrates the libgit2 commit APIs to roughly
+ * simulate `git commit` with the commit message argument.
+ *
+ * This does not have:
+ *
+ * - Robust error handling
+ * - Most of the `git commit` options
+ *
+ * This does have:
+ *
+ * - Example of performing a git commit with a comment
+ *
+ */
+int lg2_commit(git_repository *repo, int argc, char **argv)
+{
+ const char *opt = argv[1];
+ const char *comment = argv[2];
+ int error;
+
+ git_oid commit_oid,tree_oid;
+ git_tree *tree;
+ git_index *index;
+ git_object *parent = NULL;
+ git_reference *ref = NULL;
+ git_signature *signature;
+
+ /* Validate args */
+ if (argc < 3 || strcmp(opt, "-m") != 0) {
+ printf ("USAGE: %s -m <comment>\n", argv[0]);
+ return -1;
+ }
+
+ error = git_revparse_ext(&parent, &ref, repo, "HEAD");
+ if (error == GIT_ENOTFOUND) {
+ printf("HEAD not found. Creating first commit\n");
+ error = 0;
+ } else if (error != 0) {
+ const git_error *err = git_error_last();
+ if (err) printf("ERROR %d: %s\n", err->klass, err->message);
+ else printf("ERROR %d: no detailed info\n", error);
+ }
+
+ check_lg2(git_repository_index(&index, repo), "Could not open repository index", NULL);
+ check_lg2(git_index_write_tree(&tree_oid, index), "Could not write tree", NULL);;
+ check_lg2(git_index_write(index), "Could not write index", NULL);;
+
+ check_lg2(git_tree_lookup(&tree, repo, &tree_oid), "Error looking up tree", NULL);
+
+ check_lg2(git_signature_default(&signature, repo), "Error creating signature", NULL);
+
+ check_lg2(git_commit_create_v(
+ &commit_oid,
+ repo,
+ "HEAD",
+ signature,
+ signature,
+ NULL,
+ comment,
+ tree,
+ parent ? 1 : 0, parent), "Error creating commit", NULL);
+
+ git_index_free(index);
+ git_signature_free(signature);
+ git_tree_free(tree);
+ git_object_free(parent);
+ git_reference_free(ref);
+
+ return error;
+}
diff --git a/examples/common.c b/examples/common.c
new file mode 100644
index 0000000..b068b84
--- /dev/null
+++ b/examples/common.c
@@ -0,0 +1,260 @@
+/*
+ * Utilities library for libgit2 examples
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+
+#include "common.h"
+
+#ifndef _WIN32
+# include <unistd.h>
+#endif
+#include <errno.h>
+
+void check_lg2(int error, const char *message, const char *extra)
+{
+ const git_error *lg2err;
+ const char *lg2msg = "", *lg2spacer = "";
+
+ if (!error)
+ return;
+
+ if ((lg2err = git_error_last()) != NULL && lg2err->message != NULL) {
+ lg2msg = lg2err->message;
+ lg2spacer = " - ";
+ }
+
+ if (extra)
+ fprintf(stderr, "%s '%s' [%d]%s%s\n",
+ message, extra, error, lg2spacer, lg2msg);
+ else
+ fprintf(stderr, "%s [%d]%s%s\n",
+ message, error, lg2spacer, lg2msg);
+
+ exit(1);
+}
+
+void fatal(const char *message, const char *extra)
+{
+ if (extra)
+ fprintf(stderr, "%s %s\n", message, extra);
+ else
+ fprintf(stderr, "%s\n", message);
+
+ exit(1);
+}
+
+int diff_output(
+ const git_diff_delta *d,
+ const git_diff_hunk *h,
+ const git_diff_line *l,
+ void *p)
+{
+ FILE *fp = (FILE*)p;
+
+ (void)d; (void)h;
+
+ if (!fp)
+ fp = stdout;
+
+ if (l->origin == GIT_DIFF_LINE_CONTEXT ||
+ l->origin == GIT_DIFF_LINE_ADDITION ||
+ l->origin == GIT_DIFF_LINE_DELETION)
+ fputc(l->origin, fp);
+
+ fwrite(l->content, 1, l->content_len, fp);
+
+ return 0;
+}
+
+void treeish_to_tree(
+ git_tree **out, git_repository *repo, const char *treeish)
+{
+ git_object *obj = NULL;
+
+ check_lg2(
+ git_revparse_single(&obj, repo, treeish),
+ "looking up object", treeish);
+
+ check_lg2(
+ git_object_peel((git_object **)out, obj, GIT_OBJECT_TREE),
+ "resolving object to tree", treeish);
+
+ git_object_free(obj);
+}
+
+void *xrealloc(void *oldp, size_t newsz)
+{
+ void *p = realloc(oldp, newsz);
+ if (p == NULL) {
+ fprintf(stderr, "Cannot allocate memory, exiting.\n");
+ exit(1);
+ }
+ return p;
+}
+
+int resolve_refish(git_annotated_commit **commit, git_repository *repo, const char *refish)
+{
+ git_reference *ref;
+ git_object *obj;
+ int err = 0;
+
+ assert(commit != NULL);
+
+ err = git_reference_dwim(&ref, repo, refish);
+ if (err == GIT_OK) {
+ git_annotated_commit_from_ref(commit, repo, ref);
+ git_reference_free(ref);
+ return 0;
+ }
+
+ err = git_revparse_single(&obj, repo, refish);
+ if (err == GIT_OK) {
+ err = git_annotated_commit_lookup(commit, repo, git_object_id(obj));
+ git_object_free(obj);
+ }
+
+ return err;
+}
+
+static int readline(char **out)
+{
+ int c, error = 0, length = 0, allocated = 0;
+ char *line = NULL;
+
+ errno = 0;
+
+ while ((c = getchar()) != EOF) {
+ if (length == allocated) {
+ allocated += 16;
+
+ if ((line = realloc(line, allocated)) == NULL) {
+ error = -1;
+ goto error;
+ }
+ }
+
+ if (c == '\n')
+ break;
+
+ line[length++] = c;
+ }
+
+ if (errno != 0) {
+ error = -1;
+ goto error;
+ }
+
+ line[length] = '\0';
+ *out = line;
+ line = NULL;
+ error = length;
+error:
+ free(line);
+ return error;
+}
+
+static int ask(char **out, const char *prompt, char optional)
+{
+ printf("%s ", prompt);
+ fflush(stdout);
+
+ if (!readline(out) && !optional) {
+ fprintf(stderr, "Could not read response: %s", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+int cred_acquire_cb(git_credential **out,
+ const char *url,
+ const char *username_from_url,
+ unsigned int allowed_types,
+ void *payload)
+{
+ char *username = NULL, *password = NULL, *privkey = NULL, *pubkey = NULL;
+ int error = 1;
+
+ UNUSED(url);
+ UNUSED(payload);
+
+ if (username_from_url) {
+ if ((username = strdup(username_from_url)) == NULL)
+ goto out;
+ } else if ((error = ask(&username, "Username:", 0)) < 0) {
+ goto out;
+ }
+
+ if (allowed_types & GIT_CREDENTIAL_SSH_KEY) {
+ int n;
+
+ if ((error = ask(&privkey, "SSH Key:", 0)) < 0 ||
+ (error = ask(&password, "Password:", 1)) < 0)
+ goto out;
+
+ if ((n = snprintf(NULL, 0, "%s.pub", privkey)) < 0 ||
+ (pubkey = malloc(n + 1)) == NULL ||
+ (n = snprintf(pubkey, n + 1, "%s.pub", privkey)) < 0)
+ goto out;
+
+ error = git_credential_ssh_key_new(out, username, pubkey, privkey, password);
+ } else if (allowed_types & GIT_CREDENTIAL_USERPASS_PLAINTEXT) {
+ if ((error = ask(&password, "Password:", 1)) < 0)
+ goto out;
+
+ error = git_credential_userpass_plaintext_new(out, username, password);
+ } else if (allowed_types & GIT_CREDENTIAL_USERNAME) {
+ error = git_credential_username_new(out, username);
+ }
+
+out:
+ free(username);
+ free(password);
+ free(privkey);
+ free(pubkey);
+ return error;
+}
+
+char *read_file(const char *path)
+{
+ ssize_t total = 0;
+ char *buf = NULL;
+ struct stat st;
+ int fd = -1;
+
+ if ((fd = open(path, O_RDONLY)) < 0 || fstat(fd, &st) < 0)
+ goto out;
+
+ if ((buf = malloc(st.st_size + 1)) == NULL)
+ goto out;
+
+ while (total < st.st_size) {
+ ssize_t bytes = read(fd, buf + total, st.st_size - total);
+ if (bytes <= 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ free(buf);
+ buf = NULL;
+ goto out;
+ }
+ total += bytes;
+ }
+
+ buf[total] = '\0';
+
+out:
+ if (fd >= 0)
+ close(fd);
+ return buf;
+}
+
diff --git a/examples/common.h b/examples/common.h
new file mode 100644
index 0000000..901c041
--- /dev/null
+++ b/examples/common.h
@@ -0,0 +1,136 @@
+/*
+ * Utilities library for libgit2 examples
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+#ifndef INCLUDE_examples_common_h__
+#define INCLUDE_examples_common_h__
+
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <git2.h>
+#include <fcntl.h>
+
+#ifdef _WIN32
+# include <io.h>
+# include <Windows.h>
+# define open _open
+# define read _read
+# define close _close
+# define ssize_t int
+# define sleep(a) Sleep(a * 1000)
+#else
+# include <unistd.h>
+#endif
+
+#ifndef PRIuZ
+/* Define the printf format specifier to use for size_t output */
+#if defined(_MSC_VER) || defined(__MINGW32__)
+# define PRIuZ "Iu"
+#else
+# define PRIuZ "zu"
+#endif
+#endif
+
+#ifdef _MSC_VER
+#define snprintf _snprintf
+#define strcasecmp strcmpi
+#endif
+
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof(*x))
+#define UNUSED(x) (void)(x)
+
+#include "args.h"
+
+extern int lg2_add(git_repository *repo, int argc, char **argv);
+extern int lg2_blame(git_repository *repo, int argc, char **argv);
+extern int lg2_cat_file(git_repository *repo, int argc, char **argv);
+extern int lg2_checkout(git_repository *repo, int argc, char **argv);
+extern int lg2_clone(git_repository *repo, int argc, char **argv);
+extern int lg2_commit(git_repository *repo, int argc, char **argv);
+extern int lg2_config(git_repository *repo, int argc, char **argv);
+extern int lg2_describe(git_repository *repo, int argc, char **argv);
+extern int lg2_diff(git_repository *repo, int argc, char **argv);
+extern int lg2_fetch(git_repository *repo, int argc, char **argv);
+extern int lg2_for_each_ref(git_repository *repo, int argc, char **argv);
+extern int lg2_general(git_repository *repo, int argc, char **argv);
+extern int lg2_index_pack(git_repository *repo, int argc, char **argv);
+extern int lg2_init(git_repository *repo, int argc, char **argv);
+extern int lg2_log(git_repository *repo, int argc, char **argv);
+extern int lg2_ls_files(git_repository *repo, int argc, char **argv);
+extern int lg2_ls_remote(git_repository *repo, int argc, char **argv);
+extern int lg2_merge(git_repository *repo, int argc, char **argv);
+extern int lg2_push(git_repository *repo, int argc, char **argv);
+extern int lg2_remote(git_repository *repo, int argc, char **argv);
+extern int lg2_rev_list(git_repository *repo, int argc, char **argv);
+extern int lg2_rev_parse(git_repository *repo, int argc, char **argv);
+extern int lg2_show_index(git_repository *repo, int argc, char **argv);
+extern int lg2_stash(git_repository *repo, int argc, char **argv);
+extern int lg2_status(git_repository *repo, int argc, char **argv);
+extern int lg2_tag(git_repository *repo, int argc, char **argv);
+
+/**
+ * Check libgit2 error code, printing error to stderr on failure and
+ * exiting the program.
+ */
+extern void check_lg2(int error, const char *message, const char *extra);
+
+/**
+ * Read a file into a buffer
+ *
+ * @param path The path to the file that shall be read
+ * @return NUL-terminated buffer if the file was successfully read, NULL-pointer otherwise
+ */
+extern char *read_file(const char *path);
+
+/**
+ * Exit the program, printing error to stderr
+ */
+extern void fatal(const char *message, const char *extra);
+
+/**
+ * Basic output function for plain text diff output
+ * Pass `FILE*` such as `stdout` or `stderr` as payload (or NULL == `stdout`)
+ */
+extern int diff_output(
+ const git_diff_delta*, const git_diff_hunk*, const git_diff_line*, void*);
+
+/**
+ * Convert a treeish argument to an actual tree; this will call check_lg2
+ * and exit the program if `treeish` cannot be resolved to a tree
+ */
+extern void treeish_to_tree(
+ git_tree **out, git_repository *repo, const char *treeish);
+
+/**
+ * A realloc that exits on failure
+ */
+extern void *xrealloc(void *oldp, size_t newsz);
+
+/**
+ * Convert a refish to an annotated commit.
+ */
+extern int resolve_refish(git_annotated_commit **commit, git_repository *repo, const char *refish);
+
+/**
+ * Acquire credentials via command line
+ */
+extern int cred_acquire_cb(git_credential **out,
+ const char *url,
+ const char *username_from_url,
+ unsigned int allowed_types,
+ void *payload);
+
+#endif
diff --git a/examples/config.c b/examples/config.c
new file mode 100644
index 0000000..6e14ce8
--- /dev/null
+++ b/examples/config.c
@@ -0,0 +1,71 @@
+/*
+ * libgit2 "config" example - shows how to use the config API
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+
+static int config_get(git_config *cfg, const char *key)
+{
+ git_config_entry *entry;
+ int error;
+
+ if ((error = git_config_get_entry(&entry, cfg, key)) < 0) {
+ if (error != GIT_ENOTFOUND)
+ printf("Unable to get configuration: %s\n", git_error_last()->message);
+ return 1;
+ }
+
+ puts(entry->value);
+
+ /* Free the git_config_entry after use with `git_config_entry_free()`. */
+ git_config_entry_free(entry);
+
+ return 0;
+}
+
+static int config_set(git_config *cfg, const char *key, const char *value)
+{
+ if (git_config_set_string(cfg, key, value) < 0) {
+ printf("Unable to set configuration: %s\n", git_error_last()->message);
+ return 1;
+ }
+ return 0;
+}
+
+int lg2_config(git_repository *repo, int argc, char **argv)
+{
+ git_config *cfg;
+ int error;
+
+ if ((error = git_repository_config(&cfg, repo)) < 0) {
+ printf("Unable to obtain repository config: %s\n", git_error_last()->message);
+ goto out;
+ }
+
+ if (argc == 2) {
+ error = config_get(cfg, argv[1]);
+ } else if (argc == 3) {
+ error = config_set(cfg, argv[1], argv[2]);
+ } else {
+ printf("USAGE: %s config <KEY> [<VALUE>]\n", argv[0]);
+ error = 1;
+ }
+
+ /**
+ * The configuration file must be freed once it's no longer
+ * being used by the user.
+ */
+ git_config_free(cfg);
+out:
+ return error;
+}
diff --git a/examples/describe.c b/examples/describe.c
new file mode 100644
index 0000000..1236272
--- /dev/null
+++ b/examples/describe.c
@@ -0,0 +1,162 @@
+/*
+ * libgit2 "describe" example - shows how to describe commits
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+
+/**
+ * The following example partially reimplements the `git describe` command
+ * and some of its options.
+ *
+ * These commands should work:
+
+ * - Describe HEAD with default options (`describe`)
+ * - Describe specified revision (`describe master~2`)
+ * - Describe specified revisions (`describe master~2 HEAD~3`)
+ * - Describe HEAD with dirty state suffix (`describe --dirty=*`)
+ * - Describe consider all refs (`describe --all master`)
+ * - Describe consider lightweight tags (`describe --tags temp-tag`)
+ * - Describe show non-default abbreviated size (`describe --abbrev=10`)
+ * - Describe always output the long format if matches a tag (`describe --long v1.0`)
+ * - Describe consider only tags of specified pattern (`describe --match v*-release`)
+ * - Describe show the fallback result (`describe --always`)
+ * - Describe follow only the first parent commit (`describe --first-parent`)
+ *
+ * The command line parsing logic is simplified and doesn't handle
+ * all of the use cases.
+ */
+
+/** describe_options represents the parsed command line options */
+struct describe_options {
+ const char **commits;
+ size_t commit_count;
+ git_describe_options describe_options;
+ git_describe_format_options format_options;
+};
+
+static void opts_add_commit(struct describe_options *opts, const char *commit)
+{
+ size_t sz;
+
+ assert(opts != NULL);
+
+ sz = ++opts->commit_count * sizeof(opts->commits[0]);
+ opts->commits = xrealloc((void *) opts->commits, sz);
+ opts->commits[opts->commit_count - 1] = commit;
+}
+
+static void do_describe_single(git_repository *repo, struct describe_options *opts, const char *rev)
+{
+ git_object *commit;
+ git_describe_result *describe_result;
+ git_buf buf = { 0 };
+
+ if (rev) {
+ check_lg2(git_revparse_single(&commit, repo, rev),
+ "Failed to lookup rev", rev);
+
+ check_lg2(git_describe_commit(&describe_result, commit, &opts->describe_options),
+ "Failed to describe rev", rev);
+ }
+ else
+ check_lg2(git_describe_workdir(&describe_result, repo, &opts->describe_options),
+ "Failed to describe workdir", NULL);
+
+ check_lg2(git_describe_format(&buf, describe_result, &opts->format_options),
+ "Failed to format describe rev", rev);
+
+ printf("%s\n", buf.ptr);
+}
+
+static void do_describe(git_repository *repo, struct describe_options *opts)
+{
+ if (opts->commit_count == 0)
+ do_describe_single(repo, opts, NULL);
+ else
+ {
+ size_t i;
+ for (i = 0; i < opts->commit_count; i++)
+ do_describe_single(repo, opts, opts->commits[i]);
+ }
+}
+
+static void print_usage(void)
+{
+ fprintf(stderr, "usage: see `git help describe`\n");
+ exit(1);
+}
+
+/** Parse command line arguments */
+static void parse_options(struct describe_options *opts, int argc, char **argv)
+{
+ struct args_info args = ARGS_INFO_INIT;
+
+ for (args.pos = 1; args.pos < argc; ++args.pos) {
+ const char *curr = argv[args.pos];
+
+ if (curr[0] != '-') {
+ opts_add_commit(opts, curr);
+ } else if (!strcmp(curr, "--all")) {
+ opts->describe_options.describe_strategy = GIT_DESCRIBE_ALL;
+ } else if (!strcmp(curr, "--tags")) {
+ opts->describe_options.describe_strategy = GIT_DESCRIBE_TAGS;
+ } else if (!strcmp(curr, "--exact-match")) {
+ opts->describe_options.max_candidates_tags = 0;
+ } else if (!strcmp(curr, "--long")) {
+ opts->format_options.always_use_long_format = 1;
+ } else if (!strcmp(curr, "--always")) {
+ opts->describe_options.show_commit_oid_as_fallback = 1;
+ } else if (!strcmp(curr, "--first-parent")) {
+ opts->describe_options.only_follow_first_parent = 1;
+ } else if (optional_str_arg(&opts->format_options.dirty_suffix, &args, "--dirty", "-dirty")) {
+ } else if (match_int_arg((int *)&opts->format_options.abbreviated_size, &args, "--abbrev", 0)) {
+ } else if (match_int_arg((int *)&opts->describe_options.max_candidates_tags, &args, "--candidates", 0)) {
+ } else if (match_str_arg(&opts->describe_options.pattern, &args, "--match")) {
+ } else {
+ print_usage();
+ }
+ }
+
+ if (opts->commit_count > 0) {
+ if (opts->format_options.dirty_suffix)
+ fatal("--dirty is incompatible with commit-ishes", NULL);
+ }
+ else {
+ if (!opts->format_options.dirty_suffix || !opts->format_options.dirty_suffix[0]) {
+ opts_add_commit(opts, "HEAD");
+ }
+ }
+}
+
+/** Initialize describe_options struct */
+static void describe_options_init(struct describe_options *opts)
+{
+ memset(opts, 0, sizeof(*opts));
+
+ opts->commits = NULL;
+ opts->commit_count = 0;
+ git_describe_options_init(&opts->describe_options, GIT_DESCRIBE_OPTIONS_VERSION);
+ git_describe_format_options_init(&opts->format_options, GIT_DESCRIBE_FORMAT_OPTIONS_VERSION);
+}
+
+int lg2_describe(git_repository *repo, int argc, char **argv)
+{
+ struct describe_options opts;
+
+ describe_options_init(&opts);
+ parse_options(&opts, argc, argv);
+
+ do_describe(repo, &opts);
+
+ return 0;
+}
diff --git a/examples/diff.c b/examples/diff.c
new file mode 100644
index 0000000..80c5200
--- /dev/null
+++ b/examples/diff.c
@@ -0,0 +1,377 @@
+/*
+ * libgit2 "diff" example - shows how to use the diff API
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+
+/**
+ * This example demonstrates the use of the libgit2 diff APIs to
+ * create `git_diff` objects and display them, emulating a number of
+ * core Git `diff` command line options.
+ *
+ * This covers on a portion of the core Git diff options and doesn't
+ * have particularly good error handling, but it should show most of
+ * the core libgit2 diff APIs, including various types of diffs and
+ * how to do renaming detection and patch formatting.
+ */
+
+static const char *colors[] = {
+ "\033[m", /* reset */
+ "\033[1m", /* bold */
+ "\033[31m", /* red */
+ "\033[32m", /* green */
+ "\033[36m" /* cyan */
+};
+
+enum {
+ OUTPUT_DIFF = (1 << 0),
+ OUTPUT_STAT = (1 << 1),
+ OUTPUT_SHORTSTAT = (1 << 2),
+ OUTPUT_NUMSTAT = (1 << 3),
+ OUTPUT_SUMMARY = (1 << 4)
+};
+
+enum {
+ CACHE_NORMAL = 0,
+ CACHE_ONLY = 1,
+ CACHE_NONE = 2
+};
+
+/** The 'diff_options' struct captures all the various parsed command line options. */
+struct diff_options {
+ git_diff_options diffopts;
+ git_diff_find_options findopts;
+ int color;
+ int no_index;
+ int cache;
+ int output;
+ git_diff_format_t format;
+ const char *treeish1;
+ const char *treeish2;
+ const char *dir;
+};
+
+/** These functions are implemented at the end */
+static void usage(const char *message, const char *arg);
+static void parse_opts(struct diff_options *o, int argc, char *argv[]);
+static int color_printer(
+ const git_diff_delta*, const git_diff_hunk*, const git_diff_line*, void*);
+static void diff_print_stats(git_diff *diff, struct diff_options *o);
+static void compute_diff_no_index(git_diff **diff, struct diff_options *o);
+
+int lg2_diff(git_repository *repo, int argc, char *argv[])
+{
+ git_tree *t1 = NULL, *t2 = NULL;
+ git_diff *diff;
+ struct diff_options o = {
+ GIT_DIFF_OPTIONS_INIT, GIT_DIFF_FIND_OPTIONS_INIT,
+ -1, -1, 0, 0, GIT_DIFF_FORMAT_PATCH, NULL, NULL, "."
+ };
+
+ parse_opts(&o, argc, argv);
+
+ /**
+ * Possible argument patterns:
+ *
+ * * &lt;sha1&gt; &lt;sha2&gt;
+ * * &lt;sha1&gt; --cached
+ * * &lt;sha1&gt;
+ * * --cached
+ * * --nocache (don't use index data in diff at all)
+ * * --no-index &lt;file1&gt; &lt;file2&gt;
+ * * nothing
+ *
+ * Currently ranged arguments like &lt;sha1&gt;..&lt;sha2&gt; and &lt;sha1&gt;...&lt;sha2&gt;
+ * are not supported in this example
+ */
+
+ if (o.no_index >= 0) {
+ compute_diff_no_index(&diff, &o);
+ } else {
+ if (o.treeish1)
+ treeish_to_tree(&t1, repo, o.treeish1);
+ if (o.treeish2)
+ treeish_to_tree(&t2, repo, o.treeish2);
+
+ if (t1 && t2)
+ check_lg2(
+ git_diff_tree_to_tree(&diff, repo, t1, t2, &o.diffopts),
+ "diff trees", NULL);
+ else if (o.cache != CACHE_NORMAL) {
+ if (!t1)
+ treeish_to_tree(&t1, repo, "HEAD");
+
+ if (o.cache == CACHE_NONE)
+ check_lg2(
+ git_diff_tree_to_workdir(&diff, repo, t1, &o.diffopts),
+ "diff tree to working directory", NULL);
+ else
+ check_lg2(
+ git_diff_tree_to_index(&diff, repo, t1, NULL, &o.diffopts),
+ "diff tree to index", NULL);
+ }
+ else if (t1)
+ check_lg2(
+ git_diff_tree_to_workdir_with_index(&diff, repo, t1, &o.diffopts),
+ "diff tree to working directory", NULL);
+ else
+ check_lg2(
+ git_diff_index_to_workdir(&diff, repo, NULL, &o.diffopts),
+ "diff index to working directory", NULL);
+
+ /** Apply rename and copy detection if requested. */
+
+ if ((o.findopts.flags & GIT_DIFF_FIND_ALL) != 0)
+ check_lg2(
+ git_diff_find_similar(diff, &o.findopts),
+ "finding renames and copies", NULL);
+ }
+
+ /** Generate simple output using libgit2 display helper. */
+
+ if (!o.output)
+ o.output = OUTPUT_DIFF;
+
+ if (o.output != OUTPUT_DIFF)
+ diff_print_stats(diff, &o);
+
+ if ((o.output & OUTPUT_DIFF) != 0) {
+ if (o.color >= 0)
+ fputs(colors[0], stdout);
+
+ check_lg2(
+ git_diff_print(diff, o.format, color_printer, &o.color),
+ "displaying diff", NULL);
+
+ if (o.color >= 0)
+ fputs(colors[0], stdout);
+ }
+
+ /** Cleanup before exiting. */
+ git_diff_free(diff);
+ git_tree_free(t1);
+ git_tree_free(t2);
+
+ return 0;
+}
+
+static void compute_diff_no_index(git_diff **diff, struct diff_options *o) {
+ git_patch *patch = NULL;
+ char *file1_str = NULL;
+ char *file2_str = NULL;
+ git_buf buf = {0};
+
+ if (!o->treeish1 || !o->treeish2) {
+ usage("two files should be provided as arguments", NULL);
+ }
+ file1_str = read_file(o->treeish1);
+ if (file1_str == NULL) {
+ usage("file cannot be read", o->treeish1);
+ }
+ file2_str = read_file(o->treeish2);
+ if (file2_str == NULL) {
+ usage("file cannot be read", o->treeish2);
+ }
+ check_lg2(
+ git_patch_from_buffers(&patch, file1_str, strlen(file1_str), o->treeish1, file2_str, strlen(file2_str), o->treeish2, &o->diffopts),
+ "patch buffers", NULL);
+ check_lg2(
+ git_patch_to_buf(&buf, patch),
+ "patch to buf", NULL);
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ check_lg2(
+ git_diff_from_buffer(diff, buf.ptr, buf.size, NULL),
+ "diff from patch", NULL);
+#else
+ check_lg2(
+ git_diff_from_buffer(diff, buf.ptr, buf.size),
+ "diff from patch", NULL);
+#endif
+
+ git_patch_free(patch);
+ git_buf_dispose(&buf);
+ free(file1_str);
+ free(file2_str);
+}
+
+static void usage(const char *message, const char *arg)
+{
+ if (message && arg)
+ fprintf(stderr, "%s: %s\n", message, arg);
+ else if (message)
+ fprintf(stderr, "%s\n", message);
+ fprintf(stderr, "usage: diff [<tree-oid> [<tree-oid>]]\n");
+ exit(1);
+}
+
+/** This implements very rudimentary colorized output. */
+static int color_printer(
+ const git_diff_delta *delta,
+ const git_diff_hunk *hunk,
+ const git_diff_line *line,
+ void *data)
+{
+ int *last_color = data, color = 0;
+
+ (void)delta; (void)hunk;
+
+ if (*last_color >= 0) {
+ switch (line->origin) {
+ case GIT_DIFF_LINE_ADDITION: color = 3; break;
+ case GIT_DIFF_LINE_DELETION: color = 2; break;
+ case GIT_DIFF_LINE_ADD_EOFNL: color = 3; break;
+ case GIT_DIFF_LINE_DEL_EOFNL: color = 2; break;
+ case GIT_DIFF_LINE_FILE_HDR: color = 1; break;
+ case GIT_DIFF_LINE_HUNK_HDR: color = 4; break;
+ default: break;
+ }
+
+ if (color != *last_color) {
+ if (*last_color == 1 || color == 1)
+ fputs(colors[0], stdout);
+ fputs(colors[color], stdout);
+ *last_color = color;
+ }
+ }
+
+ return diff_output(delta, hunk, line, stdout);
+}
+
+/** Parse arguments as copied from git-diff. */
+static void parse_opts(struct diff_options *o, int argc, char *argv[])
+{
+ struct args_info args = ARGS_INFO_INIT;
+
+ for (args.pos = 1; args.pos < argc; ++args.pos) {
+ const char *a = argv[args.pos];
+
+ if (a[0] != '-') {
+ if (o->treeish1 == NULL)
+ o->treeish1 = a;
+ else if (o->treeish2 == NULL)
+ o->treeish2 = a;
+ else
+ usage("Only one or two tree identifiers can be provided", NULL);
+ }
+ else if (!strcmp(a, "-p") || !strcmp(a, "-u") ||
+ !strcmp(a, "--patch")) {
+ o->output |= OUTPUT_DIFF;
+ o->format = GIT_DIFF_FORMAT_PATCH;
+ }
+ else if (!strcmp(a, "--cached")) {
+ o->cache = CACHE_ONLY;
+ if (o->no_index >= 0) usage("--cached and --no-index are incompatible", NULL);
+ } else if (!strcmp(a, "--nocache"))
+ o->cache = CACHE_NONE;
+ else if (!strcmp(a, "--name-only") || !strcmp(a, "--format=name"))
+ o->format = GIT_DIFF_FORMAT_NAME_ONLY;
+ else if (!strcmp(a, "--name-status") ||
+ !strcmp(a, "--format=name-status"))
+ o->format = GIT_DIFF_FORMAT_NAME_STATUS;
+ else if (!strcmp(a, "--raw") || !strcmp(a, "--format=raw"))
+ o->format = GIT_DIFF_FORMAT_RAW;
+ else if (!strcmp(a, "--format=diff-index")) {
+ o->format = GIT_DIFF_FORMAT_RAW;
+ o->diffopts.id_abbrev = 40;
+ }
+ else if (!strcmp(a, "--no-index")) {
+ o->no_index = 0;
+ if (o->cache == CACHE_ONLY) usage("--cached and --no-index are incompatible", NULL);
+ } else if (!strcmp(a, "--color"))
+ o->color = 0;
+ else if (!strcmp(a, "--no-color"))
+ o->color = -1;
+ else if (!strcmp(a, "-R"))
+ o->diffopts.flags |= GIT_DIFF_REVERSE;
+ else if (!strcmp(a, "-a") || !strcmp(a, "--text"))
+ o->diffopts.flags |= GIT_DIFF_FORCE_TEXT;
+ else if (!strcmp(a, "--ignore-space-at-eol"))
+ o->diffopts.flags |= GIT_DIFF_IGNORE_WHITESPACE_EOL;
+ else if (!strcmp(a, "-b") || !strcmp(a, "--ignore-space-change"))
+ o->diffopts.flags |= GIT_DIFF_IGNORE_WHITESPACE_CHANGE;
+ else if (!strcmp(a, "-w") || !strcmp(a, "--ignore-all-space"))
+ o->diffopts.flags |= GIT_DIFF_IGNORE_WHITESPACE;
+ else if (!strcmp(a, "--ignored"))
+ o->diffopts.flags |= GIT_DIFF_INCLUDE_IGNORED;
+ else if (!strcmp(a, "--untracked"))
+ o->diffopts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
+ else if (!strcmp(a, "--patience"))
+ o->diffopts.flags |= GIT_DIFF_PATIENCE;
+ else if (!strcmp(a, "--minimal"))
+ o->diffopts.flags |= GIT_DIFF_MINIMAL;
+ else if (!strcmp(a, "--stat"))
+ o->output |= OUTPUT_STAT;
+ else if (!strcmp(a, "--numstat"))
+ o->output |= OUTPUT_NUMSTAT;
+ else if (!strcmp(a, "--shortstat"))
+ o->output |= OUTPUT_SHORTSTAT;
+ else if (!strcmp(a, "--summary"))
+ o->output |= OUTPUT_SUMMARY;
+ else if (match_uint16_arg(
+ &o->findopts.rename_threshold, &args, "-M") ||
+ match_uint16_arg(
+ &o->findopts.rename_threshold, &args, "--find-renames"))
+ o->findopts.flags |= GIT_DIFF_FIND_RENAMES;
+ else if (match_uint16_arg(
+ &o->findopts.copy_threshold, &args, "-C") ||
+ match_uint16_arg(
+ &o->findopts.copy_threshold, &args, "--find-copies"))
+ o->findopts.flags |= GIT_DIFF_FIND_COPIES;
+ else if (!strcmp(a, "--find-copies-harder"))
+ o->findopts.flags |= GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED;
+ else if (is_prefixed(a, "-B") || is_prefixed(a, "--break-rewrites"))
+ /* TODO: parse thresholds */
+ o->findopts.flags |= GIT_DIFF_FIND_REWRITES;
+ else if (!match_uint32_arg(
+ &o->diffopts.context_lines, &args, "-U") &&
+ !match_uint32_arg(
+ &o->diffopts.context_lines, &args, "--unified") &&
+ !match_uint32_arg(
+ &o->diffopts.interhunk_lines, &args, "--inter-hunk-context") &&
+ !match_uint16_arg(
+ &o->diffopts.id_abbrev, &args, "--abbrev") &&
+ !match_str_arg(&o->diffopts.old_prefix, &args, "--src-prefix") &&
+ !match_str_arg(&o->diffopts.new_prefix, &args, "--dst-prefix") &&
+ !match_str_arg(&o->dir, &args, "--git-dir"))
+ usage("Unknown command line argument", a);
+ }
+}
+
+/** Display diff output with "--stat", "--numstat", or "--shortstat" */
+static void diff_print_stats(git_diff *diff, struct diff_options *o)
+{
+ git_diff_stats *stats;
+ git_buf b = GIT_BUF_INIT;
+ git_diff_stats_format_t format = 0;
+
+ check_lg2(
+ git_diff_get_stats(&stats, diff), "generating stats for diff", NULL);
+
+ if (o->output & OUTPUT_STAT)
+ format |= GIT_DIFF_STATS_FULL;
+ if (o->output & OUTPUT_SHORTSTAT)
+ format |= GIT_DIFF_STATS_SHORT;
+ if (o->output & OUTPUT_NUMSTAT)
+ format |= GIT_DIFF_STATS_NUMBER;
+ if (o->output & OUTPUT_SUMMARY)
+ format |= GIT_DIFF_STATS_INCLUDE_SUMMARY;
+
+ check_lg2(
+ git_diff_stats_to_buf(&b, stats, format, 80), "formatting stats", NULL);
+
+ fputs(b.ptr, stdout);
+
+ git_buf_dispose(&b);
+ git_diff_stats_free(stats);
+}
diff --git a/examples/fetch.c b/examples/fetch.c
new file mode 100644
index 0000000..bbd882c
--- /dev/null
+++ b/examples/fetch.c
@@ -0,0 +1,109 @@
+#include "common.h"
+
+static int progress_cb(const char *str, int len, void *data)
+{
+ (void)data;
+ printf("remote: %.*s", len, str);
+ fflush(stdout); /* We don't have the \n to force the flush */
+ return 0;
+}
+
+/**
+ * This function gets called for each remote-tracking branch that gets
+ * updated. The message we output depends on whether it's a new one or
+ * an update.
+ */
+static int update_cb(const char *refname, const git_oid *a, const git_oid *b, void *data)
+{
+ char a_str[GIT_OID_SHA1_HEXSIZE+1], b_str[GIT_OID_SHA1_HEXSIZE+1];
+ (void)data;
+
+ git_oid_fmt(b_str, b);
+ b_str[GIT_OID_SHA1_HEXSIZE] = '\0';
+
+ if (git_oid_is_zero(a)) {
+ printf("[new] %.20s %s\n", b_str, refname);
+ } else {
+ git_oid_fmt(a_str, a);
+ a_str[GIT_OID_SHA1_HEXSIZE] = '\0';
+ printf("[updated] %.10s..%.10s %s\n", a_str, b_str, refname);
+ }
+
+ return 0;
+}
+
+/**
+ * This gets called during the download and indexing. Here we show
+ * processed and total objects in the pack and the amount of received
+ * data. Most frontends will probably want to show a percentage and
+ * the download rate.
+ */
+static int transfer_progress_cb(const git_indexer_progress *stats, void *payload)
+{
+ (void)payload;
+
+ if (stats->received_objects == stats->total_objects) {
+ printf("Resolving deltas %u/%u\r",
+ stats->indexed_deltas, stats->total_deltas);
+ } else if (stats->total_objects > 0) {
+ printf("Received %u/%u objects (%u) in %" PRIuZ " bytes\r",
+ stats->received_objects, stats->total_objects,
+ stats->indexed_objects, stats->received_bytes);
+ }
+ return 0;
+}
+
+/** Entry point for this command */
+int lg2_fetch(git_repository *repo, int argc, char **argv)
+{
+ git_remote *remote = NULL;
+ const git_indexer_progress *stats;
+ git_fetch_options fetch_opts = GIT_FETCH_OPTIONS_INIT;
+
+ if (argc < 2) {
+ fprintf(stderr, "usage: %s fetch <repo>\n", argv[-1]);
+ return EXIT_FAILURE;
+ }
+
+ /* Figure out whether it's a named remote or a URL */
+ printf("Fetching %s for repo %p\n", argv[1], repo);
+ if (git_remote_lookup(&remote, repo, argv[1]) < 0)
+ if (git_remote_create_anonymous(&remote, repo, argv[1]) < 0)
+ goto on_error;
+
+ /* Set up the callbacks (only update_tips for now) */
+ fetch_opts.callbacks.update_tips = &update_cb;
+ fetch_opts.callbacks.sideband_progress = &progress_cb;
+ fetch_opts.callbacks.transfer_progress = transfer_progress_cb;
+ fetch_opts.callbacks.credentials = cred_acquire_cb;
+
+ /**
+ * Perform the fetch with the configured refspecs from the
+ * config. Update the reflog for the updated references with
+ * "fetch".
+ */
+ if (git_remote_fetch(remote, NULL, &fetch_opts, "fetch") < 0)
+ goto on_error;
+
+ /**
+ * If there are local objects (we got a thin pack), then tell
+ * the user how many objects we saved from having to cross the
+ * network.
+ */
+ stats = git_remote_stats(remote);
+ if (stats->local_objects > 0) {
+ printf("\rReceived %u/%u objects in %" PRIuZ " bytes (used %u local objects)\n",
+ stats->indexed_objects, stats->total_objects, stats->received_bytes, stats->local_objects);
+ } else{
+ printf("\rReceived %u/%u objects in %" PRIuZ "bytes\n",
+ stats->indexed_objects, stats->total_objects, stats->received_bytes);
+ }
+
+ git_remote_free(remote);
+
+ return 0;
+
+ on_error:
+ git_remote_free(remote);
+ return -1;
+}
diff --git a/examples/for-each-ref.c b/examples/for-each-ref.c
new file mode 100644
index 0000000..a070674
--- /dev/null
+++ b/examples/for-each-ref.c
@@ -0,0 +1,44 @@
+#include <git2.h>
+#include "common.h"
+
+static int show_ref(git_reference *ref, void *data)
+{
+ git_repository *repo = data;
+ git_reference *resolved = NULL;
+ char hex[GIT_OID_SHA1_HEXSIZE+1];
+ const git_oid *oid;
+ git_object *obj;
+
+ if (git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC)
+ check_lg2(git_reference_resolve(&resolved, ref),
+ "Unable to resolve symbolic reference",
+ git_reference_name(ref));
+
+ oid = git_reference_target(resolved ? resolved : ref);
+ git_oid_fmt(hex, oid);
+ hex[GIT_OID_SHA1_HEXSIZE] = 0;
+ check_lg2(git_object_lookup(&obj, repo, oid, GIT_OBJECT_ANY),
+ "Unable to lookup object", hex);
+
+ printf("%s %-6s\t%s\n",
+ hex,
+ git_object_type2string(git_object_type(obj)),
+ git_reference_name(ref));
+
+ if (resolved)
+ git_reference_free(resolved);
+ return 0;
+}
+
+int lg2_for_each_ref(git_repository *repo, int argc, char **argv)
+{
+ UNUSED(argv);
+
+ if (argc != 1)
+ fatal("Sorry, no for-each-ref options supported yet", NULL);
+
+ check_lg2(git_reference_foreach(repo, show_ref, repo),
+ "Could not iterate over references", NULL);
+
+ return 0;
+}
diff --git a/examples/general.c b/examples/general.c
new file mode 100644
index 0000000..7f44cd7
--- /dev/null
+++ b/examples/general.c
@@ -0,0 +1,798 @@
+/*
+ * libgit2 "general" example - shows basic libgit2 concepts
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+/**
+ * [**libgit2**][lg] is a portable, pure C implementation of the Git core
+ * methods provided as a re-entrant linkable library with a solid API,
+ * allowing you to write native speed custom Git applications in any
+ * language which supports C bindings.
+ *
+ * This file is an example of using that API in a real, compilable C file.
+ * As the API is updated, this file will be updated to demonstrate the new
+ * functionality.
+ *
+ * If you're trying to write something in C using [libgit2][lg], you should
+ * also check out the generated [API documentation][ap]. We try to link to
+ * the relevant sections of the API docs in each section in this file.
+ *
+ * **libgit2** (for the most part) only implements the core plumbing
+ * functions, not really the higher level porcelain stuff. For a primer on
+ * Git Internals that you will need to know to work with Git at this level,
+ * check out [Chapter 10][pg] of the Pro Git book.
+ *
+ * [lg]: http://libgit2.github.com
+ * [ap]: http://libgit2.github.com/libgit2
+ * [pg]: https://git-scm.com/book/en/v2/Git-Internals-Plumbing-and-Porcelain
+ */
+
+#include "common.h"
+
+/**
+ * ### Includes
+ *
+ * Including the `git2.h` header will include all the other libgit2 headers
+ * that you need. It should be the only thing you need to include in order
+ * to compile properly and get all the libgit2 API.
+ */
+#include "git2.h"
+
+static void oid_parsing(git_oid *out);
+static void object_database(git_repository *repo, git_oid *oid);
+static void commit_writing(git_repository *repo);
+static void commit_parsing(git_repository *repo);
+static void tag_parsing(git_repository *repo);
+static void tree_parsing(git_repository *repo);
+static void blob_parsing(git_repository *repo);
+static void revwalking(git_repository *repo);
+static void index_walking(git_repository *repo);
+static void reference_listing(git_repository *repo);
+static void config_files(const char *repo_path, git_repository *repo);
+
+/**
+ * Almost all libgit2 functions return 0 on success or negative on error.
+ * This is not production quality error checking, but should be sufficient
+ * as an example.
+ */
+static void check_error(int error_code, const char *action)
+{
+ const git_error *error = git_error_last();
+ if (!error_code)
+ return;
+
+ printf("Error %d %s - %s\n", error_code, action,
+ (error && error->message) ? error->message : "???");
+
+ exit(1);
+}
+
+int lg2_general(git_repository *repo, int argc, char** argv)
+{
+ int error;
+ git_oid oid;
+ char *repo_path;
+
+ /**
+ * Initialize the library, this will set up any global state which libgit2 needs
+ * including threading and crypto
+ */
+ git_libgit2_init();
+
+ /**
+ * ### Opening the Repository
+ *
+ * There are a couple of methods for opening a repository, this being the
+ * simplest. There are also [methods][me] for specifying the index file
+ * and work tree locations, here we assume they are in the normal places.
+ *
+ * (Try running this program against tests/resources/testrepo.git.)
+ *
+ * [me]: http://libgit2.github.com/libgit2/#HEAD/group/repository
+ */
+ repo_path = (argc > 1) ? argv[1] : "/opt/libgit2-test/.git";
+
+ error = git_repository_open(&repo, repo_path);
+ check_error(error, "opening repository");
+
+ oid_parsing(&oid);
+ object_database(repo, &oid);
+ commit_writing(repo);
+ commit_parsing(repo);
+ tag_parsing(repo);
+ tree_parsing(repo);
+ blob_parsing(repo);
+ revwalking(repo);
+ index_walking(repo);
+ reference_listing(repo);
+ config_files(repo_path, repo);
+
+ /**
+ * Finally, when you're done with the repository, you can free it as well.
+ */
+ git_repository_free(repo);
+
+ return 0;
+}
+
+/**
+ * ### SHA-1 Value Conversions
+ */
+static void oid_parsing(git_oid *oid)
+{
+ char out[GIT_OID_SHA1_HEXSIZE+1];
+ char hex[] = "4a202b346bb0fb0db7eff3cffeb3c70babbd2045";
+
+ printf("*Hex to Raw*\n");
+
+ /**
+ * For our first example, we will convert a 40 character hex value to the
+ * 20 byte raw SHA1 value.
+ *
+ * The `git_oid` is the structure that keeps the SHA value. We will use
+ * this throughout the example for storing the value of the current SHA
+ * key we're working with.
+ */
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_oid_fromstr(oid, hex, GIT_OID_SHA1);
+#else
+ git_oid_fromstr(oid, hex);
+#endif
+
+ /*
+ * Once we've converted the string into the oid value, we can get the raw
+ * value of the SHA by accessing `oid.id`
+ *
+ * Next we will convert the 20 byte raw SHA1 value to a human readable 40
+ * char hex value.
+ */
+ printf("\n*Raw to Hex*\n");
+ out[GIT_OID_SHA1_HEXSIZE] = '\0';
+
+ /**
+ * If you have a oid, you can easily get the hex value of the SHA as well.
+ */
+ git_oid_fmt(out, oid);
+ printf("SHA hex string: %s\n", out);
+}
+
+/**
+ * ### Working with the Object Database
+ *
+ * **libgit2** provides [direct access][odb] to the object database. The
+ * object database is where the actual objects are stored in Git. For
+ * working with raw objects, we'll need to get this structure from the
+ * repository.
+ *
+ * [odb]: http://libgit2.github.com/libgit2/#HEAD/group/odb
+ */
+static void object_database(git_repository *repo, git_oid *oid)
+{
+ char oid_hex[GIT_OID_SHA1_HEXSIZE+1] = { 0 };
+ const unsigned char *data;
+ const char *str_type;
+ int error;
+ git_odb_object *obj;
+ git_odb *odb;
+ git_object_t otype;
+
+ git_repository_odb(&odb, repo);
+
+ /**
+ * #### Raw Object Reading
+ */
+
+ printf("\n*Raw Object Read*\n");
+
+ /**
+ * We can read raw objects directly from the object database if we have
+ * the oid (SHA) of the object. This allows us to access objects without
+ * knowing their type and inspect the raw bytes unparsed.
+ */
+ error = git_odb_read(&obj, odb, oid);
+ check_error(error, "finding object in repository");
+
+ /**
+ * A raw object only has three properties - the type (commit, blob, tree
+ * or tag), the size of the raw data and the raw, unparsed data itself.
+ * For a commit or tag, that raw data is human readable plain ASCII
+ * text. For a blob it is just file contents, so it could be text or
+ * binary data. For a tree it is a special binary format, so it's unlikely
+ * to be hugely helpful as a raw object.
+ */
+ data = (const unsigned char *)git_odb_object_data(obj);
+ otype = git_odb_object_type(obj);
+
+ /**
+ * We provide methods to convert from the object type which is an enum, to
+ * a string representation of that value (and vice-versa).
+ */
+ str_type = git_object_type2string(otype);
+ printf("object length and type: %d, %s\nobject data: %s\n",
+ (int)git_odb_object_size(obj),
+ str_type, data);
+
+ /**
+ * For proper memory management, close the object when you are done with
+ * it or it will leak memory.
+ */
+ git_odb_object_free(obj);
+
+ /**
+ * #### Raw Object Writing
+ */
+
+ printf("\n*Raw Object Write*\n");
+
+ /**
+ * You can also write raw object data to Git. This is pretty cool because
+ * it gives you direct access to the key/value properties of Git. Here
+ * we'll write a new blob object that just contains a simple string.
+ * Notice that we have to specify the object type as the `git_otype` enum.
+ */
+ git_odb_write(oid, odb, "test data", sizeof("test data") - 1, GIT_OBJECT_BLOB);
+
+ /**
+ * Now that we've written the object, we can check out what SHA1 was
+ * generated when the object was written to our database.
+ */
+ git_oid_fmt(oid_hex, oid);
+ printf("Written Object: %s\n", oid_hex);
+
+ /**
+ * Free the object database after usage.
+ */
+ git_odb_free(odb);
+}
+
+/**
+ * #### Writing Commits
+ *
+ * libgit2 provides a couple of methods to create commit objects easily as
+ * well. There are four different create signatures, we'll just show one
+ * of them here. You can read about the other ones in the [commit API
+ * docs][cd].
+ *
+ * [cd]: http://libgit2.github.com/libgit2/#HEAD/group/commit
+ */
+static void commit_writing(git_repository *repo)
+{
+ git_oid tree_id, parent_id, commit_id;
+ git_tree *tree;
+ git_commit *parent;
+ git_signature *author, *committer;
+ char oid_hex[GIT_OID_SHA1_HEXSIZE+1] = { 0 };
+
+ printf("\n*Commit Writing*\n");
+
+ /**
+ * Creating signatures for an authoring identity and time is simple. You
+ * will need to do this to specify who created a commit and when. Default
+ * values for the name and email should be found in the `user.name` and
+ * `user.email` configuration options. See the `config` section of this
+ * example file to see how to access config values.
+ */
+ git_signature_new(&author,
+ "Scott Chacon", "schacon@gmail.com", 123456789, 60);
+ git_signature_new(&committer,
+ "Scott A Chacon", "scott@github.com", 987654321, 90);
+
+ /**
+ * Commit objects need a tree to point to and optionally one or more
+ * parents. Here we're creating oid objects to create the commit with,
+ * but you can also use
+ */
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_oid_fromstr(&tree_id, "f60079018b664e4e79329a7ef9559c8d9e0378d1", GIT_OID_SHA1);
+ git_oid_fromstr(&parent_id, "5b5b025afb0b4c913b4c338a42934a3863bf3644", GIT_OID_SHA1);
+#else
+ git_oid_fromstr(&tree_id, "f60079018b664e4e79329a7ef9559c8d9e0378d1");
+ git_oid_fromstr(&parent_id, "5b5b025afb0b4c913b4c338a42934a3863bf3644");
+#endif
+ git_tree_lookup(&tree, repo, &tree_id);
+ git_commit_lookup(&parent, repo, &parent_id);
+
+ /**
+ * Here we actually create the commit object with a single call with all
+ * the values we need to create the commit. The SHA key is written to the
+ * `commit_id` variable here.
+ */
+ git_commit_create_v(
+ &commit_id, /* out id */
+ repo,
+ NULL, /* do not update the HEAD */
+ author,
+ committer,
+ NULL, /* use default message encoding */
+ "example commit",
+ tree,
+ 1, parent);
+
+ /**
+ * Now we can take a look at the commit SHA we've generated.
+ */
+ git_oid_fmt(oid_hex, &commit_id);
+ printf("New Commit: %s\n", oid_hex);
+
+ /**
+ * Free all objects used in the meanwhile.
+ */
+ git_tree_free(tree);
+ git_commit_free(parent);
+ git_signature_free(author);
+ git_signature_free(committer);
+}
+
+/**
+ * ### Object Parsing
+ *
+ * libgit2 has methods to parse every object type in Git so you don't have
+ * to work directly with the raw data. This is much faster and simpler
+ * than trying to deal with the raw data yourself.
+ */
+
+/**
+ * #### Commit Parsing
+ *
+ * [Parsing commit objects][pco] is simple and gives you access to all the
+ * data in the commit - the author (name, email, datetime), committer
+ * (same), tree, message, encoding and parent(s).
+ *
+ * [pco]: http://libgit2.github.com/libgit2/#HEAD/group/commit
+ */
+static void commit_parsing(git_repository *repo)
+{
+ const git_signature *author, *cmtter;
+ git_commit *commit, *parent;
+ git_oid oid;
+ char oid_hex[GIT_OID_SHA1_HEXSIZE+1];
+ const char *message;
+ unsigned int parents, p;
+ int error;
+ time_t time;
+
+ printf("\n*Commit Parsing*\n");
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_oid_fromstr(&oid, "8496071c1b46c854b31185ea97743be6a8774479", GIT_OID_SHA1);
+#else
+ git_oid_fromstr(&oid, "8496071c1b46c854b31185ea97743be6a8774479");
+#endif
+
+ error = git_commit_lookup(&commit, repo, &oid);
+ check_error(error, "looking up commit");
+
+ /**
+ * Each of the properties of the commit object are accessible via methods,
+ * including commonly needed variations, such as `git_commit_time` which
+ * returns the author time and `git_commit_message` which gives you the
+ * commit message (as a NUL-terminated string).
+ */
+ message = git_commit_message(commit);
+ author = git_commit_author(commit);
+ cmtter = git_commit_committer(commit);
+ time = git_commit_time(commit);
+
+ /**
+ * The author and committer methods return [git_signature] structures,
+ * which give you name, email and `when`, which is a `git_time` structure,
+ * giving you a timestamp and timezone offset.
+ */
+ printf("Author: %s (%s)\nCommitter: %s (%s)\nDate: %s\nMessage: %s\n",
+ author->name, author->email,
+ cmtter->name, cmtter->email,
+ ctime(&time), message);
+
+ /**
+ * Commits can have zero or more parents. The first (root) commit will
+ * have no parents, most commits will have one (i.e. the commit it was
+ * based on) and merge commits will have two or more. Commits can
+ * technically have any number, though it's rare to have more than two.
+ */
+ parents = git_commit_parentcount(commit);
+ for (p = 0;p < parents;p++) {
+ memset(oid_hex, 0, sizeof(oid_hex));
+
+ git_commit_parent(&parent, commit, p);
+ git_oid_fmt(oid_hex, git_commit_id(parent));
+ printf("Parent: %s\n", oid_hex);
+ git_commit_free(parent);
+ }
+
+ git_commit_free(commit);
+}
+
+/**
+ * #### Tag Parsing
+ *
+ * You can parse and create tags with the [tag management API][tm], which
+ * functions very similarly to the commit lookup, parsing and creation
+ * methods, since the objects themselves are very similar.
+ *
+ * [tm]: http://libgit2.github.com/libgit2/#HEAD/group/tag
+ */
+static void tag_parsing(git_repository *repo)
+{
+ git_commit *commit;
+ git_object_t type;
+ git_tag *tag;
+ git_oid oid;
+ const char *name, *message;
+ int error;
+
+ printf("\n*Tag Parsing*\n");
+
+ /**
+ * We create an oid for the tag object if we know the SHA and look it up
+ * the same way that we would a commit (or any other object).
+ */
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_oid_fromstr(&oid, "b25fa35b38051e4ae45d4222e795f9df2e43f1d1", GIT_OID_SHA1);
+#else
+ git_oid_fromstr(&oid, "b25fa35b38051e4ae45d4222e795f9df2e43f1d1");
+#endif
+
+ error = git_tag_lookup(&tag, repo, &oid);
+ check_error(error, "looking up tag");
+
+ /**
+ * Now that we have the tag object, we can extract the information it
+ * generally contains: the target (usually a commit object), the type of
+ * the target object (usually 'commit'), the name ('v1.0'), the tagger (a
+ * git_signature - name, email, timestamp), and the tag message.
+ */
+ git_tag_target((git_object **)&commit, tag);
+ name = git_tag_name(tag); /* "test" */
+ type = git_tag_target_type(tag); /* GIT_OBJECT_COMMIT (object_t enum) */
+ message = git_tag_message(tag); /* "tag message\n" */
+ printf("Tag Name: %s\nTag Type: %s\nTag Message: %s\n",
+ name, git_object_type2string(type), message);
+
+ /**
+ * Free both the commit and tag after usage.
+ */
+ git_commit_free(commit);
+ git_tag_free(tag);
+}
+
+/**
+ * #### Tree Parsing
+ *
+ * [Tree parsing][tp] is a bit different than the other objects, in that
+ * we have a subtype which is the tree entry. This is not an actual
+ * object type in Git, but a useful structure for parsing and traversing
+ * tree entries.
+ *
+ * [tp]: http://libgit2.github.com/libgit2/#HEAD/group/tree
+ */
+static void tree_parsing(git_repository *repo)
+{
+ const git_tree_entry *entry;
+ size_t cnt;
+ git_object *obj;
+ git_tree *tree;
+ git_oid oid;
+
+ printf("\n*Tree Parsing*\n");
+
+ /**
+ * Create the oid and lookup the tree object just like the other objects.
+ */
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_oid_fromstr(&oid, "f60079018b664e4e79329a7ef9559c8d9e0378d1", GIT_OID_SHA1);
+#else
+ git_oid_fromstr(&oid, "f60079018b664e4e79329a7ef9559c8d9e0378d1");
+#endif
+ git_tree_lookup(&tree, repo, &oid);
+
+ /**
+ * Getting the count of entries in the tree so you can iterate over them
+ * if you want to.
+ */
+ cnt = git_tree_entrycount(tree); /* 2 */
+ printf("tree entries: %d\n", (int) cnt);
+
+ entry = git_tree_entry_byindex(tree, 0);
+ printf("Entry name: %s\n", git_tree_entry_name(entry)); /* "README" */
+
+ /**
+ * You can also access tree entries by name if you know the name of the
+ * entry you're looking for.
+ */
+ entry = git_tree_entry_byname(tree, "README");
+ git_tree_entry_name(entry); /* "README" */
+
+ /**
+ * Once you have the entry object, you can access the content or subtree
+ * (or commit, in the case of submodules) that it points to. You can also
+ * get the mode if you want.
+ */
+ git_tree_entry_to_object(&obj, repo, entry); /* blob */
+
+ /**
+ * Remember to close the looked-up object and tree once you are done using it
+ */
+ git_object_free(obj);
+ git_tree_free(tree);
+}
+
+/**
+ * #### Blob Parsing
+ *
+ * The last object type is the simplest and requires the least parsing
+ * help. Blobs are just file contents and can contain anything, there is
+ * no structure to it. The main advantage to using the [simple blob
+ * api][ba] is that when you're creating blobs you don't have to calculate
+ * the size of the content. There is also a helper for reading a file
+ * from disk and writing it to the db and getting the oid back so you
+ * don't have to do all those steps yourself.
+ *
+ * [ba]: http://libgit2.github.com/libgit2/#HEAD/group/blob
+ */
+static void blob_parsing(git_repository *repo)
+{
+ git_blob *blob;
+ git_oid oid;
+
+ printf("\n*Blob Parsing*\n");
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_oid_fromstr(&oid, "1385f264afb75a56a5bec74243be9b367ba4ca08", GIT_OID_SHA1);
+#else
+ git_oid_fromstr(&oid, "1385f264afb75a56a5bec74243be9b367ba4ca08");
+#endif
+ git_blob_lookup(&blob, repo, &oid);
+
+ /**
+ * You can access a buffer with the raw contents of the blob directly.
+ * Note that this buffer may not be contain ASCII data for certain blobs
+ * (e.g. binary files): do not consider the buffer a NULL-terminated
+ * string, and use the `git_blob_rawsize` attribute to find out its exact
+ * size in bytes
+ * */
+ printf("Blob Size: %ld\n", (long)git_blob_rawsize(blob)); /* 8 */
+ git_blob_rawcontent(blob); /* "content" */
+
+ /**
+ * Free the blob after usage.
+ */
+ git_blob_free(blob);
+}
+
+/**
+ * ### Revwalking
+ *
+ * The libgit2 [revision walking api][rw] provides methods to traverse the
+ * directed graph created by the parent pointers of the commit objects.
+ * Since all commits point back to the commit that came directly before
+ * them, you can walk this parentage as a graph and find all the commits
+ * that were ancestors of (reachable from) a given starting point. This
+ * can allow you to create `git log` type functionality.
+ *
+ * [rw]: http://libgit2.github.com/libgit2/#HEAD/group/revwalk
+ */
+static void revwalking(git_repository *repo)
+{
+ const git_signature *cauth;
+ const char *cmsg;
+ int error;
+ git_revwalk *walk;
+ git_commit *wcommit;
+ git_oid oid;
+
+ printf("\n*Revwalking*\n");
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_oid_fromstr(&oid, "5b5b025afb0b4c913b4c338a42934a3863bf3644", GIT_OID_SHA1);
+#else
+ git_oid_fromstr(&oid, "5b5b025afb0b4c913b4c338a42934a3863bf3644");
+#endif
+
+ /**
+ * To use the revwalker, create a new walker, tell it how you want to sort
+ * the output and then push one or more starting points onto the walker.
+ * If you want to emulate the output of `git log` you would push the SHA
+ * of the commit that HEAD points to into the walker and then start
+ * traversing them. You can also 'hide' commits that you want to stop at
+ * or not see any of their ancestors. So if you want to emulate `git log
+ * branch1..branch2`, you would push the oid of `branch2` and hide the oid
+ * of `branch1`.
+ */
+ git_revwalk_new(&walk, repo);
+ git_revwalk_sorting(walk, GIT_SORT_TOPOLOGICAL | GIT_SORT_REVERSE);
+ git_revwalk_push(walk, &oid);
+
+ /**
+ * Now that we have the starting point pushed onto the walker, we start
+ * asking for ancestors. It will return them in the sorting order we asked
+ * for as commit oids. We can then lookup and parse the committed pointed
+ * at by the returned OID; note that this operation is specially fast
+ * since the raw contents of the commit object will be cached in memory
+ */
+ while ((git_revwalk_next(&oid, walk)) == 0) {
+ error = git_commit_lookup(&wcommit, repo, &oid);
+ check_error(error, "looking up commit during revwalk");
+
+ cmsg = git_commit_message(wcommit);
+ cauth = git_commit_author(wcommit);
+ printf("%s (%s)\n", cmsg, cauth->email);
+
+ git_commit_free(wcommit);
+ }
+
+ /**
+ * Like the other objects, be sure to free the revwalker when you're done
+ * to prevent memory leaks. Also, make sure that the repository being
+ * walked it not deallocated while the walk is in progress, or it will
+ * result in undefined behavior
+ */
+ git_revwalk_free(walk);
+}
+
+/**
+ * ### Index File Manipulation *
+ * The [index file API][gi] allows you to read, traverse, update and write
+ * the Git index file (sometimes thought of as the staging area).
+ *
+ * [gi]: http://libgit2.github.com/libgit2/#HEAD/group/index
+ */
+static void index_walking(git_repository *repo)
+{
+ git_index *index;
+ size_t i, ecount;
+
+ printf("\n*Index Walking*\n");
+
+ /**
+ * You can either open the index from the standard location in an open
+ * repository, as we're doing here, or you can open and manipulate any
+ * index file with `git_index_open_bare()`. The index for the repository
+ * will be located and loaded from disk.
+ */
+ git_repository_index(&index, repo);
+
+ /**
+ * For each entry in the index, you can get a bunch of information
+ * including the SHA (oid), path and mode which map to the tree objects
+ * that are written out. It also has filesystem properties to help
+ * determine what to inspect for changes (ctime, mtime, dev, ino, uid,
+ * gid, file_size and flags) All these properties are exported publicly in
+ * the `git_index_entry` struct
+ */
+ ecount = git_index_entrycount(index);
+ for (i = 0; i < ecount; ++i) {
+ const git_index_entry *e = git_index_get_byindex(index, i);
+
+ printf("path: %s\n", e->path);
+ printf("mtime: %d\n", (int)e->mtime.seconds);
+ printf("fs: %d\n", (int)e->file_size);
+ }
+
+ git_index_free(index);
+}
+
+/**
+ * ### References
+ *
+ * The [reference API][ref] allows you to list, resolve, create and update
+ * references such as branches, tags and remote references (everything in
+ * the .git/refs directory).
+ *
+ * [ref]: http://libgit2.github.com/libgit2/#HEAD/group/reference
+ */
+static void reference_listing(git_repository *repo)
+{
+ git_strarray ref_list;
+ unsigned i;
+
+ printf("\n*Reference Listing*\n");
+
+ /**
+ * Here we will implement something like `git for-each-ref` simply listing
+ * out all available references and the object SHA they resolve to.
+ *
+ * Now that we have the list of reference names, we can lookup each ref
+ * one at a time and resolve them to the SHA, then print both values out.
+ */
+
+ git_reference_list(&ref_list, repo);
+
+ for (i = 0; i < ref_list.count; ++i) {
+ git_reference *ref;
+ char oid_hex[GIT_OID_SHA1_HEXSIZE+1] = GIT_OID_SHA1_HEXZERO;
+ const char *refname;
+
+ refname = ref_list.strings[i];
+ git_reference_lookup(&ref, repo, refname);
+
+ switch (git_reference_type(ref)) {
+ case GIT_REFERENCE_DIRECT:
+ git_oid_fmt(oid_hex, git_reference_target(ref));
+ printf("%s [%s]\n", refname, oid_hex);
+ break;
+
+ case GIT_REFERENCE_SYMBOLIC:
+ printf("%s => %s\n", refname, git_reference_symbolic_target(ref));
+ break;
+ default:
+ fprintf(stderr, "Unexpected reference type\n");
+ exit(1);
+ }
+
+ git_reference_free(ref);
+ }
+
+ git_strarray_dispose(&ref_list);
+}
+
+/**
+ * ### Config Files
+ *
+ * The [config API][config] allows you to list and update config values
+ * in any of the accessible config file locations (system, global, local).
+ *
+ * [config]: http://libgit2.github.com/libgit2/#HEAD/group/config
+ */
+static void config_files(const char *repo_path, git_repository* repo)
+{
+ const char *email;
+ char config_path[256];
+ int32_t autocorrect;
+ git_config *cfg;
+ git_config *snap_cfg;
+ int error_code;
+
+ printf("\n*Config Listing*\n");
+
+ /**
+ * Open a config object so we can read global values from it.
+ */
+ sprintf(config_path, "%s/config", repo_path);
+ check_error(git_config_open_ondisk(&cfg, config_path), "opening config");
+
+ if (git_config_get_int32(&autocorrect, cfg, "help.autocorrect") == 0)
+ printf("Autocorrect: %d\n", autocorrect);
+
+ check_error(git_repository_config_snapshot(&snap_cfg, repo), "config snapshot");
+ git_config_get_string(&email, snap_cfg, "user.email");
+ printf("Email: %s\n", email);
+
+ error_code = git_config_get_int32(&autocorrect, cfg, "help.autocorrect");
+ switch (error_code)
+ {
+ case 0:
+ printf("Autocorrect: %d\n", autocorrect);
+ break;
+ case GIT_ENOTFOUND:
+ printf("Autocorrect: Undefined\n");
+ break;
+ default:
+ check_error(error_code, "get_int32 failed");
+ }
+ git_config_free(cfg);
+
+ check_error(git_repository_config_snapshot(&snap_cfg, repo), "config snapshot");
+ error_code = git_config_get_string(&email, snap_cfg, "user.email");
+ switch (error_code)
+ {
+ case 0:
+ printf("Email: %s\n", email);
+ break;
+ case GIT_ENOTFOUND:
+ printf("Email: Undefined\n");
+ break;
+ default:
+ check_error(error_code, "get_string failed");
+ }
+
+ git_config_free(snap_cfg);
+}
diff --git a/examples/index-pack.c b/examples/index-pack.c
new file mode 100644
index 0000000..0f8234c
--- /dev/null
+++ b/examples/index-pack.c
@@ -0,0 +1,76 @@
+#include "common.h"
+
+/*
+ * This could be run in the main loop whilst the application waits for
+ * the indexing to finish in a worker thread
+ */
+static int index_cb(const git_indexer_progress *stats, void *data)
+{
+ (void)data;
+ printf("\rProcessing %u of %u", stats->indexed_objects, stats->total_objects);
+
+ return 0;
+}
+
+int lg2_index_pack(git_repository *repo, int argc, char **argv)
+{
+ git_indexer *idx;
+ git_indexer_progress stats = {0, 0};
+ int error;
+ int fd;
+ ssize_t read_bytes;
+ char buf[512];
+
+ (void)repo;
+
+ if (argc < 2) {
+ fprintf(stderr, "usage: %s index-pack <packfile>\n", argv[-1]);
+ return EXIT_FAILURE;
+ }
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ error = git_indexer_new(&idx, ".", git_repository_oid_type(repo), NULL);
+#else
+ error = git_indexer_new(&idx, ".", 0, NULL, NULL);
+#endif
+
+ if (error < 0) {
+ puts("bad idx");
+ return -1;
+ }
+
+
+ if ((fd = open(argv[1], 0)) < 0) {
+ perror("open");
+ return -1;
+ }
+
+ do {
+ read_bytes = read(fd, buf, sizeof(buf));
+ if (read_bytes < 0)
+ break;
+
+ if ((error = git_indexer_append(idx, buf, read_bytes, &stats)) < 0)
+ goto cleanup;
+
+ index_cb(&stats, NULL);
+ } while (read_bytes > 0);
+
+ if (read_bytes < 0) {
+ error = -1;
+ perror("failed reading");
+ goto cleanup;
+ }
+
+ if ((error = git_indexer_commit(idx, &stats)) < 0)
+ goto cleanup;
+
+ printf("\rIndexing %u of %u\n", stats.indexed_objects, stats.total_objects);
+
+ puts(git_indexer_name(idx));
+
+ cleanup:
+ close(fd);
+ git_indexer_free(idx);
+ return error;
+}
diff --git a/examples/init.c b/examples/init.c
new file mode 100644
index 0000000..2f681c5
--- /dev/null
+++ b/examples/init.c
@@ -0,0 +1,248 @@
+/*
+ * libgit2 "init" example - shows how to initialize a new repo
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+
+/**
+ * This is a sample program that is similar to "git init". See the
+ * documentation for that (try "git help init") to understand what this
+ * program is emulating.
+ *
+ * This demonstrates using the libgit2 APIs to initialize a new repository.
+ *
+ * This also contains a special additional option that regular "git init"
+ * does not support which is "--initial-commit" to make a first empty commit.
+ * That is demonstrated in the "create_initial_commit" helper function.
+ */
+
+/** Forward declarations of helpers */
+struct init_opts {
+ int no_options;
+ int quiet;
+ int bare;
+ int initial_commit;
+ uint32_t shared;
+ const char *template;
+ const char *gitdir;
+ const char *dir;
+};
+static void create_initial_commit(git_repository *repo);
+static void parse_opts(struct init_opts *o, int argc, char *argv[]);
+
+int lg2_init(git_repository *repo, int argc, char *argv[])
+{
+ struct init_opts o = { 1, 0, 0, 0, GIT_REPOSITORY_INIT_SHARED_UMASK, 0, 0, 0 };
+
+ parse_opts(&o, argc, argv);
+
+ /* Initialize repository. */
+
+ if (o.no_options) {
+ /**
+ * No options were specified, so let's demonstrate the default
+ * simple case of git_repository_init() API usage...
+ */
+ check_lg2(git_repository_init(&repo, o.dir, 0),
+ "Could not initialize repository", NULL);
+ }
+ else {
+ /**
+ * Some command line options were specified, so we'll use the
+ * extended init API to handle them
+ */
+ git_repository_init_options initopts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+ initopts.flags = GIT_REPOSITORY_INIT_MKPATH;
+
+ if (o.bare)
+ initopts.flags |= GIT_REPOSITORY_INIT_BARE;
+
+ if (o.template) {
+ initopts.flags |= GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;
+ initopts.template_path = o.template;
+ }
+
+ if (o.gitdir) {
+ /**
+ * If you specified a separate git directory, then initialize
+ * the repository at that path and use the second path as the
+ * working directory of the repository (with a git-link file)
+ */
+ initopts.workdir_path = o.dir;
+ o.dir = o.gitdir;
+ }
+
+ if (o.shared != 0)
+ initopts.mode = o.shared;
+
+ check_lg2(git_repository_init_ext(&repo, o.dir, &initopts),
+ "Could not initialize repository", NULL);
+ }
+
+ /** Print a message to stdout like "git init" does. */
+
+ if (!o.quiet) {
+ if (o.bare || o.gitdir)
+ o.dir = git_repository_path(repo);
+ else
+ o.dir = git_repository_workdir(repo);
+
+ printf("Initialized empty Git repository in %s\n", o.dir);
+ }
+
+ /**
+ * As an extension to the basic "git init" command, this example
+ * gives the option to create an empty initial commit. This is
+ * mostly to demonstrate what it takes to do that, but also some
+ * people like to have that empty base commit in their repo.
+ */
+ if (o.initial_commit) {
+ create_initial_commit(repo);
+ printf("Created empty initial commit\n");
+ }
+
+ git_repository_free(repo);
+
+ return 0;
+}
+
+/**
+ * Unlike regular "git init", this example shows how to create an initial
+ * empty commit in the repository. This is the helper function that does
+ * that.
+ */
+static void create_initial_commit(git_repository *repo)
+{
+ git_signature *sig;
+ git_index *index;
+ git_oid tree_id, commit_id;
+ git_tree *tree;
+
+ /** First use the config to initialize a commit signature for the user. */
+
+ if (git_signature_default(&sig, repo) < 0)
+ fatal("Unable to create a commit signature.",
+ "Perhaps 'user.name' and 'user.email' are not set");
+
+ /* Now let's create an empty tree for this commit */
+
+ if (git_repository_index(&index, repo) < 0)
+ fatal("Could not open repository index", NULL);
+
+ /**
+ * Outside of this example, you could call git_index_add_bypath()
+ * here to put actual files into the index. For our purposes, we'll
+ * leave it empty for now.
+ */
+
+ if (git_index_write_tree(&tree_id, index) < 0)
+ fatal("Unable to write initial tree from index", NULL);
+
+ git_index_free(index);
+
+ if (git_tree_lookup(&tree, repo, &tree_id) < 0)
+ fatal("Could not look up initial tree", NULL);
+
+ /**
+ * Ready to create the initial commit.
+ *
+ * Normally creating a commit would involve looking up the current
+ * HEAD commit and making that be the parent of the initial commit,
+ * but here this is the first commit so there will be no parent.
+ */
+
+ if (git_commit_create_v(
+ &commit_id, repo, "HEAD", sig, sig,
+ NULL, "Initial commit", tree, 0) < 0)
+ fatal("Could not create the initial commit", NULL);
+
+ /** Clean up so we don't leak memory. */
+
+ git_tree_free(tree);
+ git_signature_free(sig);
+}
+
+static void usage(const char *error, const char *arg)
+{
+ fprintf(stderr, "error: %s '%s'\n", error, arg);
+ fprintf(stderr,
+ "usage: init [-q | --quiet] [--bare] [--template=<dir>]\n"
+ " [--shared[=perms]] [--initial-commit]\n"
+ " [--separate-git-dir] <directory>\n");
+ exit(1);
+}
+
+/** Parse the tail of the --shared= argument. */
+static uint32_t parse_shared(const char *shared)
+{
+ if (!strcmp(shared, "false") || !strcmp(shared, "umask"))
+ return GIT_REPOSITORY_INIT_SHARED_UMASK;
+
+ else if (!strcmp(shared, "true") || !strcmp(shared, "group"))
+ return GIT_REPOSITORY_INIT_SHARED_GROUP;
+
+ else if (!strcmp(shared, "all") || !strcmp(shared, "world") ||
+ !strcmp(shared, "everybody"))
+ return GIT_REPOSITORY_INIT_SHARED_ALL;
+
+ else if (shared[0] == '0') {
+ long val;
+ char *end = NULL;
+ val = strtol(shared + 1, &end, 8);
+ if (end == shared + 1 || *end != 0)
+ usage("invalid octal value for --shared", shared);
+ return (uint32_t)val;
+ }
+
+ else
+ usage("unknown value for --shared", shared);
+
+ return 0;
+}
+
+static void parse_opts(struct init_opts *o, int argc, char *argv[])
+{
+ struct args_info args = ARGS_INFO_INIT;
+ const char *sharedarg;
+
+ /** Process arguments. */
+
+ for (args.pos = 1; args.pos < argc; ++args.pos) {
+ char *a = argv[args.pos];
+
+ if (a[0] == '-')
+ o->no_options = 0;
+
+ if (a[0] != '-') {
+ if (o->dir != NULL)
+ usage("extra argument", a);
+ o->dir = a;
+ }
+ else if (!strcmp(a, "-q") || !strcmp(a, "--quiet"))
+ o->quiet = 1;
+ else if (!strcmp(a, "--bare"))
+ o->bare = 1;
+ else if (!strcmp(a, "--shared"))
+ o->shared = GIT_REPOSITORY_INIT_SHARED_GROUP;
+ else if (!strcmp(a, "--initial-commit"))
+ o->initial_commit = 1;
+ else if (match_str_arg(&sharedarg, &args, "--shared"))
+ o->shared = parse_shared(sharedarg);
+ else if (!match_str_arg(&o->template, &args, "--template") ||
+ !match_str_arg(&o->gitdir, &args, "--separate-git-dir"))
+ usage("unknown option", a);
+ }
+
+ if (!o->dir)
+ usage("must specify directory to init", "");
+}
diff --git a/examples/lg2.c b/examples/lg2.c
new file mode 100644
index 0000000..7946bc2
--- /dev/null
+++ b/examples/lg2.c
@@ -0,0 +1,124 @@
+#include "common.h"
+
+/* This part is not strictly libgit2-dependent, but you can use this
+ * as a starting point for a git-like tool */
+
+typedef int (*git_command_fn)(git_repository *, int , char **);
+
+struct {
+ char *name;
+ git_command_fn fn;
+ char requires_repo;
+} commands[] = {
+ { "add", lg2_add, 1 },
+ { "blame", lg2_blame, 1 },
+ { "cat-file", lg2_cat_file, 1 },
+ { "checkout", lg2_checkout, 1 },
+ { "clone", lg2_clone, 0 },
+ { "commit", lg2_commit, 1 },
+ { "config", lg2_config, 1 },
+ { "describe", lg2_describe, 1 },
+ { "diff", lg2_diff, 1 },
+ { "fetch", lg2_fetch, 1 },
+ { "for-each-ref", lg2_for_each_ref, 1 },
+ { "general", lg2_general, 0 },
+ { "index-pack", lg2_index_pack, 1 },
+ { "init", lg2_init, 0 },
+ { "log", lg2_log, 1 },
+ { "ls-files", lg2_ls_files, 1 },
+ { "ls-remote", lg2_ls_remote, 1 },
+ { "merge", lg2_merge, 1 },
+ { "push", lg2_push, 1 },
+ { "remote", lg2_remote, 1 },
+ { "rev-list", lg2_rev_list, 1 },
+ { "rev-parse", lg2_rev_parse, 1 },
+ { "show-index", lg2_show_index, 0 },
+ { "stash", lg2_stash, 1 },
+ { "status", lg2_status, 1 },
+ { "tag", lg2_tag, 1 },
+};
+
+static int run_command(git_command_fn fn, git_repository *repo, struct args_info args)
+{
+ int error;
+
+ /* Run the command. If something goes wrong, print the error message to stderr */
+ error = fn(repo, args.argc - args.pos, &args.argv[args.pos]);
+ if (error < 0) {
+ if (git_error_last() == NULL)
+ fprintf(stderr, "Error without message");
+ else
+ fprintf(stderr, "Bad news:\n %s\n", git_error_last()->message);
+ }
+
+ return !!error;
+}
+
+static int usage(const char *prog)
+{
+ size_t i;
+
+ fprintf(stderr, "usage: %s <cmd>...\n\nAvailable commands:\n\n", prog);
+ for (i = 0; i < ARRAY_SIZE(commands); i++)
+ fprintf(stderr, "\t%s\n", commands[i].name);
+
+ exit(EXIT_FAILURE);
+}
+
+int main(int argc, char **argv)
+{
+ struct args_info args = ARGS_INFO_INIT;
+ git_repository *repo = NULL;
+ const char *git_dir = NULL;
+ int return_code = 1;
+ size_t i;
+
+ if (argc < 2)
+ usage(argv[0]);
+
+ git_libgit2_init();
+
+ for (args.pos = 1; args.pos < args.argc; ++args.pos) {
+ char *a = args.argv[args.pos];
+
+ if (a[0] != '-') {
+ /* non-arg */
+ break;
+ } else if (optional_str_arg(&git_dir, &args, "--git-dir", ".git")) {
+ continue;
+ } else if (match_arg_separator(&args)) {
+ break;
+ }
+ }
+
+ if (args.pos == args.argc)
+ usage(argv[0]);
+
+ if (!git_dir)
+ git_dir = ".";
+
+ for (i = 0; i < ARRAY_SIZE(commands); ++i) {
+ if (strcmp(args.argv[args.pos], commands[i].name))
+ continue;
+
+ /*
+ * Before running the actual command, create an instance
+ * of the local repository and pass it to the function.
+ * */
+ if (commands[i].requires_repo) {
+ check_lg2(git_repository_open_ext(&repo, git_dir, 0, NULL),
+ "Unable to open repository '%s'", git_dir);
+ }
+
+ return_code = run_command(commands[i].fn, repo, args);
+ goto shutdown;
+ }
+
+ fprintf(stderr, "Command not found: %s\n", argv[1]);
+
+shutdown:
+ git_repository_free(repo);
+ git_libgit2_shutdown();
+
+ return return_code;
+}
diff --git a/examples/log.c b/examples/log.c
new file mode 100644
index 0000000..4b0a95d
--- /dev/null
+++ b/examples/log.c
@@ -0,0 +1,483 @@
+/*
+ * libgit2 "log" example - shows how to walk history and get commit info
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+
+/**
+ * This example demonstrates the libgit2 rev walker APIs to roughly
+ * simulate the output of `git log` and a few of command line arguments.
+ * `git log` has many many options and this only shows a few of them.
+ *
+ * This does not have:
+ *
+ * - Robust error handling
+ * - Colorized or paginated output formatting
+ * - Most of the `git log` options
+ *
+ * This does have:
+ *
+ * - Examples of translating command line arguments to equivalent libgit2
+ * revwalker configuration calls
+ * - Simplified options to apply pathspec limits and to show basic diffs
+ */
+
+/** log_state represents walker being configured while handling options */
+struct log_state {
+ git_repository *repo;
+ const char *repodir;
+ git_revwalk *walker;
+ int hide;
+ int sorting;
+ int revisions;
+};
+
+/** utility functions that are called to configure the walker */
+static void set_sorting(struct log_state *s, unsigned int sort_mode);
+static void push_rev(struct log_state *s, git_object *obj, int hide);
+static int add_revision(struct log_state *s, const char *revstr);
+
+/** log_options holds other command line options that affect log output */
+struct log_options {
+ int show_diff;
+ int show_log_size;
+ int skip, limit;
+ int min_parents, max_parents;
+ git_time_t before;
+ git_time_t after;
+ const char *author;
+ const char *committer;
+ const char *grep;
+};
+
+/** utility functions that parse options and help with log output */
+static int parse_options(
+ struct log_state *s, struct log_options *opt, int argc, char **argv);
+static void print_time(const git_time *intime, const char *prefix);
+static void print_commit(git_commit *commit, struct log_options *opts);
+static int match_with_parent(git_commit *commit, int i, git_diff_options *);
+
+/** utility functions for filtering */
+static int signature_matches(const git_signature *sig, const char *filter);
+static int log_message_matches(const git_commit *commit, const char *filter);
+
+int lg2_log(git_repository *repo, int argc, char *argv[])
+{
+ int i, count = 0, printed = 0, parents, last_arg;
+ struct log_state s;
+ struct log_options opt;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_oid oid;
+ git_commit *commit = NULL;
+ git_pathspec *ps = NULL;
+
+ /** Parse arguments and set up revwalker. */
+ last_arg = parse_options(&s, &opt, argc, argv);
+ s.repo = repo;
+
+ diffopts.pathspec.strings = &argv[last_arg];
+ diffopts.pathspec.count = argc - last_arg;
+ if (diffopts.pathspec.count > 0)
+ check_lg2(git_pathspec_new(&ps, &diffopts.pathspec),
+ "Building pathspec", NULL);
+
+ if (!s.revisions)
+ add_revision(&s, NULL);
+
+ /** Use the revwalker to traverse the history. */
+
+ printed = count = 0;
+
+ for (; !git_revwalk_next(&oid, s.walker); git_commit_free(commit)) {
+ check_lg2(git_commit_lookup(&commit, s.repo, &oid),
+ "Failed to look up commit", NULL);
+
+ parents = (int)git_commit_parentcount(commit);
+ if (parents < opt.min_parents)
+ continue;
+ if (opt.max_parents > 0 && parents > opt.max_parents)
+ continue;
+
+ if (diffopts.pathspec.count > 0) {
+ int unmatched = parents;
+
+ if (parents == 0) {
+ git_tree *tree;
+ check_lg2(git_commit_tree(&tree, commit), "Get tree", NULL);
+ if (git_pathspec_match_tree(
+ NULL, tree, GIT_PATHSPEC_NO_MATCH_ERROR, ps) != 0)
+ unmatched = 1;
+ git_tree_free(tree);
+ } else if (parents == 1) {
+ unmatched = match_with_parent(commit, 0, &diffopts) ? 0 : 1;
+ } else {
+ for (i = 0; i < parents; ++i) {
+ if (match_with_parent(commit, i, &diffopts))
+ unmatched--;
+ }
+ }
+
+ if (unmatched > 0)
+ continue;
+ }
+
+ if (!signature_matches(git_commit_author(commit), opt.author))
+ continue;
+
+ if (!signature_matches(git_commit_committer(commit), opt.committer))
+ continue;
+
+ if (!log_message_matches(commit, opt.grep))
+ continue;
+
+ if (count++ < opt.skip)
+ continue;
+ if (opt.limit != -1 && printed++ >= opt.limit) {
+ git_commit_free(commit);
+ break;
+ }
+
+ print_commit(commit, &opt);
+
+ if (opt.show_diff) {
+ git_tree *a = NULL, *b = NULL;
+ git_diff *diff = NULL;
+
+ if (parents > 1)
+ continue;
+ check_lg2(git_commit_tree(&b, commit), "Get tree", NULL);
+ if (parents == 1) {
+ git_commit *parent;
+ check_lg2(git_commit_parent(&parent, commit, 0), "Get parent", NULL);
+ check_lg2(git_commit_tree(&a, parent), "Tree for parent", NULL);
+ git_commit_free(parent);
+ }
+
+ check_lg2(git_diff_tree_to_tree(
+ &diff, git_commit_owner(commit), a, b, &diffopts),
+ "Diff commit with parent", NULL);
+ check_lg2(
+ git_diff_print(diff, GIT_DIFF_FORMAT_PATCH, diff_output, NULL),
+ "Displaying diff", NULL);
+
+ git_diff_free(diff);
+ git_tree_free(a);
+ git_tree_free(b);
+ }
+ }
+
+ git_pathspec_free(ps);
+ git_revwalk_free(s.walker);
+
+ return 0;
+}
+
+/** Determine if the given git_signature does not contain the filter text. */
+static int signature_matches(const git_signature *sig, const char *filter) {
+ if (filter == NULL)
+ return 1;
+
+ if (sig != NULL &&
+ (strstr(sig->name, filter) != NULL ||
+ strstr(sig->email, filter) != NULL))
+ return 1;
+
+ return 0;
+}
+
+static int log_message_matches(const git_commit *commit, const char *filter) {
+ const char *message = NULL;
+
+ if (filter == NULL)
+ return 1;
+
+ if ((message = git_commit_message(commit)) != NULL &&
+ strstr(message, filter) != NULL)
+ return 1;
+
+ return 0;
+}
+
+/** Push object (for hide or show) onto revwalker. */
+static void push_rev(struct log_state *s, git_object *obj, int hide)
+{
+ hide = s->hide ^ hide;
+
+ /** Create revwalker on demand if it doesn't already exist. */
+ if (!s->walker) {
+ check_lg2(git_revwalk_new(&s->walker, s->repo),
+ "Could not create revision walker", NULL);
+ git_revwalk_sorting(s->walker, s->sorting);
+ }
+
+ if (!obj)
+ check_lg2(git_revwalk_push_head(s->walker),
+ "Could not find repository HEAD", NULL);
+ else if (hide)
+ check_lg2(git_revwalk_hide(s->walker, git_object_id(obj)),
+ "Reference does not refer to a commit", NULL);
+ else
+ check_lg2(git_revwalk_push(s->walker, git_object_id(obj)),
+ "Reference does not refer to a commit", NULL);
+
+ git_object_free(obj);
+}
+
+/** Parse revision string and add revs to walker. */
+static int add_revision(struct log_state *s, const char *revstr)
+{
+ git_revspec revs;
+ int hide = 0;
+
+ if (!revstr) {
+ push_rev(s, NULL, hide);
+ return 0;
+ }
+
+ if (*revstr == '^') {
+ revs.flags = GIT_REVSPEC_SINGLE;
+ hide = !hide;
+
+ if (git_revparse_single(&revs.from, s->repo, revstr + 1) < 0)
+ return -1;
+ } else if (git_revparse(&revs, s->repo, revstr) < 0)
+ return -1;
+
+ if ((revs.flags & GIT_REVSPEC_SINGLE) != 0)
+ push_rev(s, revs.from, hide);
+ else {
+ push_rev(s, revs.to, hide);
+
+ if ((revs.flags & GIT_REVSPEC_MERGE_BASE) != 0) {
+ git_oid base;
+ check_lg2(git_merge_base(&base, s->repo,
+ git_object_id(revs.from), git_object_id(revs.to)),
+ "Could not find merge base", revstr);
+ check_lg2(
+ git_object_lookup(&revs.to, s->repo, &base, GIT_OBJECT_COMMIT),
+ "Could not find merge base commit", NULL);
+
+ push_rev(s, revs.to, hide);
+ }
+
+ push_rev(s, revs.from, !hide);
+ }
+
+ return 0;
+}
+
+/** Update revwalker with sorting mode. */
+static void set_sorting(struct log_state *s, unsigned int sort_mode)
+{
+ /** Open repo on demand if it isn't already open. */
+ if (!s->repo) {
+ if (!s->repodir) s->repodir = ".";
+ check_lg2(git_repository_open_ext(&s->repo, s->repodir, 0, NULL),
+ "Could not open repository", s->repodir);
+ }
+
+ /** Create revwalker on demand if it doesn't already exist. */
+ if (!s->walker)
+ check_lg2(git_revwalk_new(&s->walker, s->repo),
+ "Could not create revision walker", NULL);
+
+ if (sort_mode == GIT_SORT_REVERSE)
+ s->sorting = s->sorting ^ GIT_SORT_REVERSE;
+ else
+ s->sorting = sort_mode | (s->sorting & GIT_SORT_REVERSE);
+
+ git_revwalk_sorting(s->walker, s->sorting);
+}
+
+/** Helper to format a git_time value like Git. */
+static void print_time(const git_time *intime, const char *prefix)
+{
+ char sign, out[32];
+ struct tm *intm;
+ int offset, hours, minutes;
+ time_t t;
+
+ offset = intime->offset;
+ if (offset < 0) {
+ sign = '-';
+ offset = -offset;
+ } else {
+ sign = '+';
+ }
+
+ hours = offset / 60;
+ minutes = offset % 60;
+
+ t = (time_t)intime->time + (intime->offset * 60);
+
+ intm = gmtime(&t);
+ strftime(out, sizeof(out), "%a %b %e %T %Y", intm);
+
+ printf("%s%s %c%02d%02d\n", prefix, out, sign, hours, minutes);
+}
+
+/** Helper to print a commit object. */
+static void print_commit(git_commit *commit, struct log_options *opts)
+{
+ char buf[GIT_OID_SHA1_HEXSIZE + 1];
+ int i, count;
+ const git_signature *sig;
+ const char *scan, *eol;
+
+ git_oid_tostr(buf, sizeof(buf), git_commit_id(commit));
+ printf("commit %s\n", buf);
+
+ if (opts->show_log_size) {
+ printf("log size %d\n", (int)strlen(git_commit_message(commit)));
+ }
+
+ if ((count = (int)git_commit_parentcount(commit)) > 1) {
+ printf("Merge:");
+ for (i = 0; i < count; ++i) {
+ git_oid_tostr(buf, 8, git_commit_parent_id(commit, i));
+ printf(" %s", buf);
+ }
+ printf("\n");
+ }
+
+ if ((sig = git_commit_author(commit)) != NULL) {
+ printf("Author: %s <%s>\n", sig->name, sig->email);
+ print_time(&sig->when, "Date: ");
+ }
+ printf("\n");
+
+ for (scan = git_commit_message(commit); scan && *scan; ) {
+ for (eol = scan; *eol && *eol != '\n'; ++eol) /* find eol */;
+
+ printf(" %.*s\n", (int)(eol - scan), scan);
+ scan = *eol ? eol + 1 : NULL;
+ }
+ printf("\n");
+}
+
+/** Helper to find how many files in a commit changed from its nth parent. */
+static int match_with_parent(git_commit *commit, int i, git_diff_options *opts)
+{
+ git_commit *parent;
+ git_tree *a, *b;
+ git_diff *diff;
+ int ndeltas;
+
+ check_lg2(
+ git_commit_parent(&parent, commit, (size_t)i), "Get parent", NULL);
+ check_lg2(git_commit_tree(&a, parent), "Tree for parent", NULL);
+ check_lg2(git_commit_tree(&b, commit), "Tree for commit", NULL);
+ check_lg2(
+ git_diff_tree_to_tree(&diff, git_commit_owner(commit), a, b, opts),
+ "Checking diff between parent and commit", NULL);
+
+ ndeltas = (int)git_diff_num_deltas(diff);
+
+ git_diff_free(diff);
+ git_tree_free(a);
+ git_tree_free(b);
+ git_commit_free(parent);
+
+ return ndeltas > 0;
+}
+
+/** Print a usage message for the program. */
+static void usage(const char *message, const char *arg)
+{
+ if (message && arg)
+ fprintf(stderr, "%s: %s\n", message, arg);
+ else if (message)
+ fprintf(stderr, "%s\n", message);
+ fprintf(stderr, "usage: log [<options>]\n");
+ exit(1);
+}
+
+/** Parse some log command line options. */
+static int parse_options(
+ struct log_state *s, struct log_options *opt, int argc, char **argv)
+{
+ struct args_info args = ARGS_INFO_INIT;
+
+ memset(s, 0, sizeof(*s));
+ s->sorting = GIT_SORT_TIME;
+
+ memset(opt, 0, sizeof(*opt));
+ opt->max_parents = -1;
+ opt->limit = -1;
+
+ for (args.pos = 1; args.pos < argc; ++args.pos) {
+ const char *a = argv[args.pos];
+
+ if (a[0] != '-') {
+ if (!add_revision(s, a))
+ s->revisions++;
+ else
+ /** Try failed revision parse as filename. */
+ break;
+ } else if (!match_arg_separator(&args)) {
+ break;
+ }
+ else if (!strcmp(a, "--date-order"))
+ set_sorting(s, GIT_SORT_TIME);
+ else if (!strcmp(a, "--topo-order"))
+ set_sorting(s, GIT_SORT_TOPOLOGICAL);
+ else if (!strcmp(a, "--reverse"))
+ set_sorting(s, GIT_SORT_REVERSE);
+ else if (match_str_arg(&opt->author, &args, "--author"))
+ /** Found valid --author */
+ ;
+ else if (match_str_arg(&opt->committer, &args, "--committer"))
+ /** Found valid --committer */
+ ;
+ else if (match_str_arg(&opt->grep, &args, "--grep"))
+ /** Found valid --grep */
+ ;
+ else if (match_str_arg(&s->repodir, &args, "--git-dir"))
+ /** Found git-dir. */
+ ;
+ else if (match_int_arg(&opt->skip, &args, "--skip", 0))
+ /** Found valid --skip. */
+ ;
+ else if (match_int_arg(&opt->limit, &args, "--max-count", 0))
+ /** Found valid --max-count. */
+ ;
+ else if (a[1] >= '0' && a[1] <= '9')
+ is_integer(&opt->limit, a + 1, 0);
+ else if (match_int_arg(&opt->limit, &args, "-n", 0))
+ /** Found valid -n. */
+ ;
+ else if (!strcmp(a, "--merges"))
+ opt->min_parents = 2;
+ else if (!strcmp(a, "--no-merges"))
+ opt->max_parents = 1;
+ else if (!strcmp(a, "--no-min-parents"))
+ opt->min_parents = 0;
+ else if (!strcmp(a, "--no-max-parents"))
+ opt->max_parents = -1;
+ else if (match_int_arg(&opt->max_parents, &args, "--max-parents=", 1))
+ /** Found valid --max-parents. */
+ ;
+ else if (match_int_arg(&opt->min_parents, &args, "--min-parents=", 0))
+ /** Found valid --min_parents. */
+ ;
+ else if (!strcmp(a, "-p") || !strcmp(a, "-u") || !strcmp(a, "--patch"))
+ opt->show_diff = 1;
+ else if (!strcmp(a, "--log-size"))
+ opt->show_log_size = 1;
+ else
+ usage("Unsupported argument", a);
+ }
+
+ return args.pos;
+}
+
diff --git a/examples/ls-files.c b/examples/ls-files.c
new file mode 100644
index 0000000..a235069
--- /dev/null
+++ b/examples/ls-files.c
@@ -0,0 +1,131 @@
+/*
+ * libgit2 "ls-files" example - shows how to view all files currently in the index
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+
+/**
+ * This example demonstrates the libgit2 index APIs to roughly
+ * simulate the output of `git ls-files`.
+ * `git ls-files` has many options and this currently does not show them.
+ *
+ * `git ls-files` base command shows all paths in the index at that time.
+ * This includes staged and committed files, but unstaged files will not display.
+ *
+ * This currently supports the default behavior and the `--error-unmatch` option.
+ */
+
+struct ls_options {
+ int error_unmatch;
+ char *files[1024];
+ size_t file_count;
+};
+
+static void usage(const char *message, const char *arg)
+{
+ if (message && arg)
+ fprintf(stderr, "%s: %s\n", message, arg);
+ else if (message)
+ fprintf(stderr, "%s\n", message);
+ fprintf(stderr, "usage: ls-files [--error-unmatch] [--] [<file>...]\n");
+ exit(1);
+}
+
+static int parse_options(struct ls_options *opts, int argc, char *argv[])
+{
+ int parsing_files = 0;
+ int i;
+
+ memset(opts, 0, sizeof(struct ls_options));
+
+ if (argc < 2)
+ return 0;
+
+ for (i = 1; i < argc; ++i) {
+ char *a = argv[i];
+
+ /* if it doesn't start with a '-' or is after the '--' then it is a file */
+ if (a[0] != '-' || parsing_files) {
+ parsing_files = 1;
+
+ /* watch for overflows (just in case) */
+ if (opts->file_count == 1024) {
+ fprintf(stderr, "ls-files can only support 1024 files at this time.\n");
+ return -1;
+ }
+
+ opts->files[opts->file_count++] = a;
+ } else if (!strcmp(a, "--")) {
+ parsing_files = 1;
+ } else if (!strcmp(a, "--error-unmatch")) {
+ opts->error_unmatch = 1;
+ } else {
+ usage("Unsupported argument", a);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int print_paths(struct ls_options *opts, git_index *index)
+{
+ size_t i;
+ const git_index_entry *entry;
+
+ /* if there are no files explicitly listed by the user print all entries in the index */
+ if (opts->file_count == 0) {
+ size_t entry_count = git_index_entrycount(index);
+
+ for (i = 0; i < entry_count; i++) {
+ entry = git_index_get_byindex(index, i);
+ puts(entry->path);
+ }
+ return 0;
+ }
+
+ /* loop through the files found in the args and print them if they exist */
+ for (i = 0; i < opts->file_count; ++i) {
+ const char *path = opts->files[i];
+
+ if ((entry = git_index_get_bypath(index, path, GIT_INDEX_STAGE_NORMAL)) != NULL) {
+ puts(path);
+ } else if (opts->error_unmatch) {
+ fprintf(stderr, "error: pathspec '%s' did not match any file(s) known to git.\n", path);
+ fprintf(stderr, "Did you forget to 'git add'?\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int lg2_ls_files(git_repository *repo, int argc, char *argv[])
+{
+ git_index *index = NULL;
+ struct ls_options opts;
+ int error;
+
+ if ((error = parse_options(&opts, argc, argv)) < 0)
+ return error;
+
+ if ((error = git_repository_index(&index, repo)) < 0)
+ goto cleanup;
+
+ error = print_paths(&opts, index);
+
+cleanup:
+ git_index_free(index);
+
+ return error;
+}
diff --git a/examples/ls-remote.c b/examples/ls-remote.c
new file mode 100644
index 0000000..24caae7
--- /dev/null
+++ b/examples/ls-remote.c
@@ -0,0 +1,60 @@
+#include "common.h"
+
+static int use_remote(git_repository *repo, char *name)
+{
+ git_remote *remote = NULL;
+ int error;
+ const git_remote_head **refs;
+ size_t refs_len, i;
+ git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
+
+ /* Find the remote by name */
+ error = git_remote_lookup(&remote, repo, name);
+ if (error < 0) {
+ error = git_remote_create_anonymous(&remote, repo, name);
+ if (error < 0)
+ goto cleanup;
+ }
+
+ /**
+ * Connect to the remote and call the printing function for
+ * each of the remote references.
+ */
+ callbacks.credentials = cred_acquire_cb;
+
+ error = git_remote_connect(remote, GIT_DIRECTION_FETCH, &callbacks, NULL, NULL);
+ if (error < 0)
+ goto cleanup;
+
+ /**
+ * Get the list of references on the remote and print out
+ * their name next to what they point to.
+ */
+ if (git_remote_ls(&refs, &refs_len, remote) < 0)
+ goto cleanup;
+
+ for (i = 0; i < refs_len; i++) {
+ char oid[GIT_OID_SHA1_HEXSIZE + 1] = {0};
+ git_oid_fmt(oid, &refs[i]->oid);
+ printf("%s\t%s\n", oid, refs[i]->name);
+ }
+
+cleanup:
+ git_remote_free(remote);
+ return error;
+}
+
+/** Entry point for this command */
+int lg2_ls_remote(git_repository *repo, int argc, char **argv)
+{
+ int error;
+
+ if (argc < 2) {
+ fprintf(stderr, "usage: %s ls-remote <remote>\n", argv[-1]);
+ return EXIT_FAILURE;
+ }
+
+ error = use_remote(repo, argv[1]);
+
+ return error;
+}
diff --git a/examples/merge.c b/examples/merge.c
new file mode 100644
index 0000000..460c06a
--- /dev/null
+++ b/examples/merge.c
@@ -0,0 +1,361 @@
+/*
+ * libgit2 "merge" example - shows how to perform merges
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+
+/** The following example demonstrates how to do merges with libgit2.
+ *
+ * It will merge whatever commit-ish you pass in into the current branch.
+ *
+ * Recognized options are :
+ * --no-commit: don't actually commit the merge.
+ *
+ */
+
+struct merge_options {
+ const char **heads;
+ size_t heads_count;
+
+ git_annotated_commit **annotated;
+ size_t annotated_count;
+
+ int no_commit : 1;
+};
+
+static void print_usage(void)
+{
+ fprintf(stderr, "usage: merge [--no-commit] <commit...>\n");
+ exit(1);
+}
+
+static void merge_options_init(struct merge_options *opts)
+{
+ memset(opts, 0, sizeof(*opts));
+
+ opts->heads = NULL;
+ opts->heads_count = 0;
+ opts->annotated = NULL;
+ opts->annotated_count = 0;
+}
+
+static void opts_add_refish(struct merge_options *opts, const char *refish)
+{
+ size_t sz;
+
+ assert(opts != NULL);
+
+ sz = ++opts->heads_count * sizeof(opts->heads[0]);
+ opts->heads = xrealloc((void *) opts->heads, sz);
+ opts->heads[opts->heads_count - 1] = refish;
+}
+
+static void parse_options(const char **repo_path, struct merge_options *opts, int argc, char **argv)
+{
+ struct args_info args = ARGS_INFO_INIT;
+
+ if (argc <= 1)
+ print_usage();
+
+ for (args.pos = 1; args.pos < argc; ++args.pos) {
+ const char *curr = argv[args.pos];
+
+ if (curr[0] != '-') {
+ opts_add_refish(opts, curr);
+ } else if (!strcmp(curr, "--no-commit")) {
+ opts->no_commit = 1;
+ } else if (match_str_arg(repo_path, &args, "--git-dir")) {
+ continue;
+ } else {
+ print_usage();
+ }
+ }
+}
+
+static int resolve_heads(git_repository *repo, struct merge_options *opts)
+{
+ git_annotated_commit **annotated = calloc(opts->heads_count, sizeof(git_annotated_commit *));
+ size_t annotated_count = 0, i;
+ int err = 0;
+
+ for (i = 0; i < opts->heads_count; i++) {
+ err = resolve_refish(&annotated[annotated_count++], repo, opts->heads[i]);
+ if (err != 0) {
+ fprintf(stderr, "failed to resolve refish %s: %s\n", opts->heads[i], git_error_last()->message);
+ annotated_count--;
+ continue;
+ }
+ }
+
+ if (annotated_count != opts->heads_count) {
+ fprintf(stderr, "unable to parse some refish\n");
+ free(annotated);
+ return -1;
+ }
+
+ opts->annotated = annotated;
+ opts->annotated_count = annotated_count;
+ return 0;
+}
+
+static int perform_fastforward(git_repository *repo, const git_oid *target_oid, int is_unborn)
+{
+ git_checkout_options ff_checkout_options = GIT_CHECKOUT_OPTIONS_INIT;
+ git_reference *target_ref;
+ git_reference *new_target_ref;
+ git_object *target = NULL;
+ int err = 0;
+
+ if (is_unborn) {
+ const char *symbolic_ref;
+ git_reference *head_ref;
+
+ /* HEAD reference is unborn, lookup manually so we don't try to resolve it */
+ err = git_reference_lookup(&head_ref, repo, "HEAD");
+ if (err != 0) {
+ fprintf(stderr, "failed to lookup HEAD ref\n");
+ return -1;
+ }
+
+ /* Grab the reference HEAD should be pointing to */
+ symbolic_ref = git_reference_symbolic_target(head_ref);
+
+ /* Create our master reference on the target OID */
+ err = git_reference_create(&target_ref, repo, symbolic_ref, target_oid, 0, NULL);
+ if (err != 0) {
+ fprintf(stderr, "failed to create master reference\n");
+ return -1;
+ }
+
+ git_reference_free(head_ref);
+ } else {
+ /* HEAD exists, just lookup and resolve */
+ err = git_repository_head(&target_ref, repo);
+ if (err != 0) {
+ fprintf(stderr, "failed to get HEAD reference\n");
+ return -1;
+ }
+ }
+
+ /* Lookup the target object */
+ err = git_object_lookup(&target, repo, target_oid, GIT_OBJECT_COMMIT);
+ if (err != 0) {
+ fprintf(stderr, "failed to lookup OID %s\n", git_oid_tostr_s(target_oid));
+ return -1;
+ }
+
+ /* Checkout the result so the workdir is in the expected state */
+ ff_checkout_options.checkout_strategy = GIT_CHECKOUT_SAFE;
+ err = git_checkout_tree(repo, target, &ff_checkout_options);
+ if (err != 0) {
+ fprintf(stderr, "failed to checkout HEAD reference\n");
+ return -1;
+ }
+
+ /* Move the target reference to the target OID */
+ err = git_reference_set_target(&new_target_ref, target_ref, target_oid, NULL);
+ if (err != 0) {
+ fprintf(stderr, "failed to move HEAD reference\n");
+ return -1;
+ }
+
+ git_reference_free(target_ref);
+ git_reference_free(new_target_ref);
+ git_object_free(target);
+
+ return 0;
+}
+
+static void output_conflicts(git_index *index)
+{
+ git_index_conflict_iterator *conflicts;
+ const git_index_entry *ancestor;
+ const git_index_entry *our;
+ const git_index_entry *their;
+ int err = 0;
+
+ check_lg2(git_index_conflict_iterator_new(&conflicts, index), "failed to create conflict iterator", NULL);
+
+ while ((err = git_index_conflict_next(&ancestor, &our, &their, conflicts)) == 0) {
+ fprintf(stderr, "conflict: a:%s o:%s t:%s\n",
+ ancestor ? ancestor->path : "NULL",
+ our->path ? our->path : "NULL",
+ their->path ? their->path : "NULL");
+ }
+
+ if (err != GIT_ITEROVER) {
+ fprintf(stderr, "error iterating conflicts\n");
+ }
+
+ git_index_conflict_iterator_free(conflicts);
+}
+
+static int create_merge_commit(git_repository *repo, git_index *index, struct merge_options *opts)
+{
+ git_oid tree_oid, commit_oid;
+ git_tree *tree;
+ git_signature *sign;
+ git_reference *merge_ref = NULL;
+ git_annotated_commit *merge_commit;
+ git_reference *head_ref;
+ git_commit **parents = calloc(opts->annotated_count + 1, sizeof(git_commit *));
+ const char *msg_target = NULL;
+ size_t msglen = 0;
+ char *msg;
+ size_t i;
+ int err;
+
+ /* Grab our needed references */
+ check_lg2(git_repository_head(&head_ref, repo), "failed to get repo HEAD", NULL);
+ if (resolve_refish(&merge_commit, repo, opts->heads[0])) {
+ fprintf(stderr, "failed to resolve refish %s", opts->heads[0]);
+ free(parents);
+ return -1;
+ }
+
+ /* Maybe that's a ref, so DWIM it */
+ err = git_reference_dwim(&merge_ref, repo, opts->heads[0]);
+ check_lg2(err, "failed to DWIM reference", git_error_last()->message);
+
+ /* Grab a signature */
+ check_lg2(git_signature_now(&sign, "Me", "me@example.com"), "failed to create signature", NULL);
+
+#define MERGE_COMMIT_MSG "Merge %s '%s'"
+ /* Prepare a standard merge commit message */
+ if (merge_ref != NULL) {
+ check_lg2(git_branch_name(&msg_target, merge_ref), "failed to get branch name of merged ref", NULL);
+ } else {
+ msg_target = git_oid_tostr_s(git_annotated_commit_id(merge_commit));
+ }
+
+ msglen = snprintf(NULL, 0, MERGE_COMMIT_MSG, (merge_ref ? "branch" : "commit"), msg_target);
+ if (msglen > 0) msglen++;
+ msg = malloc(msglen);
+ err = snprintf(msg, msglen, MERGE_COMMIT_MSG, (merge_ref ? "branch" : "commit"), msg_target);
+
+ /* This is only to silence the compiler */
+ if (err < 0) goto cleanup;
+
+ /* Setup our parent commits */
+ err = git_reference_peel((git_object **)&parents[0], head_ref, GIT_OBJECT_COMMIT);
+ check_lg2(err, "failed to peel head reference", NULL);
+ for (i = 0; i < opts->annotated_count; i++) {
+ git_commit_lookup(&parents[i + 1], repo, git_annotated_commit_id(opts->annotated[i]));
+ }
+
+ /* Prepare our commit tree */
+ check_lg2(git_index_write_tree(&tree_oid, index), "failed to write merged tree", NULL);
+ check_lg2(git_tree_lookup(&tree, repo, &tree_oid), "failed to lookup tree", NULL);
+
+ /* Commit time ! */
+ err = git_commit_create(&commit_oid,
+ repo, git_reference_name(head_ref),
+ sign, sign,
+ NULL, msg,
+ tree,
+ opts->annotated_count + 1, (const git_commit **)parents);
+ check_lg2(err, "failed to create commit", NULL);
+
+ /* We're done merging, cleanup the repository state */
+ git_repository_state_cleanup(repo);
+
+cleanup:
+ free(parents);
+ return err;
+}
+
+int lg2_merge(git_repository *repo, int argc, char **argv)
+{
+ struct merge_options opts;
+ git_index *index;
+ git_repository_state_t state;
+ git_merge_analysis_t analysis;
+ git_merge_preference_t preference;
+ const char *path = ".";
+ int err = 0;
+
+ merge_options_init(&opts);
+ parse_options(&path, &opts, argc, argv);
+
+ state = git_repository_state(repo);
+ if (state != GIT_REPOSITORY_STATE_NONE) {
+ fprintf(stderr, "repository is in unexpected state %d\n", state);
+ goto cleanup;
+ }
+
+ err = resolve_heads(repo, &opts);
+ if (err != 0)
+ goto cleanup;
+
+ err = git_merge_analysis(&analysis, &preference,
+ repo,
+ (const git_annotated_commit **)opts.annotated,
+ opts.annotated_count);
+ check_lg2(err, "merge analysis failed", NULL);
+
+ if (analysis & GIT_MERGE_ANALYSIS_UP_TO_DATE) {
+ printf("Already up-to-date\n");
+ return 0;
+ } else if (analysis & GIT_MERGE_ANALYSIS_UNBORN ||
+ (analysis & GIT_MERGE_ANALYSIS_FASTFORWARD &&
+ !(preference & GIT_MERGE_PREFERENCE_NO_FASTFORWARD))) {
+ const git_oid *target_oid;
+ if (analysis & GIT_MERGE_ANALYSIS_UNBORN) {
+ printf("Unborn\n");
+ } else {
+ printf("Fast-forward\n");
+ }
+
+ /* Since this is a fast-forward, there can be only one merge head */
+ target_oid = git_annotated_commit_id(opts.annotated[0]);
+ assert(opts.annotated_count == 1);
+
+ return perform_fastforward(repo, target_oid, (analysis & GIT_MERGE_ANALYSIS_UNBORN));
+ } else if (analysis & GIT_MERGE_ANALYSIS_NORMAL) {
+ git_merge_options merge_opts = GIT_MERGE_OPTIONS_INIT;
+ git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ merge_opts.flags = 0;
+ merge_opts.file_flags = GIT_MERGE_FILE_STYLE_DIFF3;
+
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE|GIT_CHECKOUT_ALLOW_CONFLICTS;
+
+ if (preference & GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY) {
+ printf("Fast-forward is preferred, but only a merge is possible\n");
+ return -1;
+ }
+
+ err = git_merge(repo,
+ (const git_annotated_commit **)opts.annotated, opts.annotated_count,
+ &merge_opts, &checkout_opts);
+ check_lg2(err, "merge failed", NULL);
+ }
+
+ /* If we get here, we actually performed the merge above */
+
+ check_lg2(git_repository_index(&index, repo), "failed to get repository index", NULL);
+
+ if (git_index_has_conflicts(index)) {
+ /* Handle conflicts */
+ output_conflicts(index);
+ } else if (!opts.no_commit) {
+ create_merge_commit(repo, index, &opts);
+ printf("Merge made\n");
+ }
+
+cleanup:
+ free((char **)opts.heads);
+ free(opts.annotated);
+
+ return 0;
+}
diff --git a/examples/push.c b/examples/push.c
new file mode 100644
index 0000000..bcf3076
--- /dev/null
+++ b/examples/push.c
@@ -0,0 +1,56 @@
+/*
+ * libgit2 "push" example - shows how to push to remote
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+
+/**
+ * This example demonstrates the libgit2 push API to roughly
+ * simulate `git push`.
+ *
+ * This does not have:
+ *
+ * - Robust error handling
+ * - Any of the `git push` options
+ *
+ * This does have:
+ *
+ * - Example of push to origin/master
+ *
+ */
+
+/** Entry point for this command */
+int lg2_push(git_repository *repo, int argc, char **argv) {
+ git_push_options options;
+ git_remote* remote = NULL;
+ char *refspec = "refs/heads/master";
+ const git_strarray refspecs = {
+ &refspec,
+ 1
+ };
+
+ /* Validate args */
+ if (argc > 1) {
+ printf ("USAGE: %s\n\nsorry, no arguments supported yet\n", argv[0]);
+ return -1;
+ }
+
+ check_lg2(git_remote_lookup(&remote, repo, "origin" ), "Unable to lookup remote", NULL);
+
+ check_lg2(git_push_options_init(&options, GIT_PUSH_OPTIONS_VERSION ), "Error initializing push", NULL);
+
+ check_lg2(git_remote_push(remote, &refspecs, &options), "Error pushing", NULL);
+
+ printf("pushed\n");
+ return 0;
+}
diff --git a/examples/remote.c b/examples/remote.c
new file mode 100644
index 0000000..14fac03
--- /dev/null
+++ b/examples/remote.c
@@ -0,0 +1,256 @@
+/*
+ * libgit2 "remote" example - shows how to modify remotes for a repo
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+
+/**
+ * This is a sample program that is similar to "git remote". See the
+ * documentation for that (try "git help remote") to understand what this
+ * program is emulating.
+ *
+ * This demonstrates using the libgit2 APIs to modify remotes of a repository.
+ */
+
+enum subcmd {
+ subcmd_add,
+ subcmd_remove,
+ subcmd_rename,
+ subcmd_seturl,
+ subcmd_show
+};
+
+struct remote_opts {
+ enum subcmd cmd;
+
+ /* for command-specific args */
+ int argc;
+ char **argv;
+};
+
+static int cmd_add(git_repository *repo, struct remote_opts *o);
+static int cmd_remove(git_repository *repo, struct remote_opts *o);
+static int cmd_rename(git_repository *repo, struct remote_opts *o);
+static int cmd_seturl(git_repository *repo, struct remote_opts *o);
+static int cmd_show(git_repository *repo, struct remote_opts *o);
+
+static void parse_subcmd(
+ struct remote_opts *opt, int argc, char **argv);
+static void usage(const char *msg, const char *arg);
+
+int lg2_remote(git_repository *repo, int argc, char *argv[])
+{
+ int retval = 0;
+ struct remote_opts opt = {0};
+
+ parse_subcmd(&opt, argc, argv);
+
+ switch (opt.cmd)
+ {
+ case subcmd_add:
+ retval = cmd_add(repo, &opt);
+ break;
+ case subcmd_remove:
+ retval = cmd_remove(repo, &opt);
+ break;
+ case subcmd_rename:
+ retval = cmd_rename(repo, &opt);
+ break;
+ case subcmd_seturl:
+ retval = cmd_seturl(repo, &opt);
+ break;
+ case subcmd_show:
+ retval = cmd_show(repo, &opt);
+ break;
+ }
+
+ return retval;
+}
+
+static int cmd_add(git_repository *repo, struct remote_opts *o)
+{
+ char *name, *url;
+ git_remote *remote = {0};
+
+ if (o->argc != 2)
+ usage("you need to specify a name and URL", NULL);
+
+ name = o->argv[0];
+ url = o->argv[1];
+
+ check_lg2(git_remote_create(&remote, repo, name, url),
+ "could not create remote", NULL);
+
+ return 0;
+}
+
+static int cmd_remove(git_repository *repo, struct remote_opts *o)
+{
+ char *name;
+
+ if (o->argc != 1)
+ usage("you need to specify a name", NULL);
+
+ name = o->argv[0];
+
+ check_lg2(git_remote_delete(repo, name),
+ "could not delete remote", name);
+
+ return 0;
+}
+
+static int cmd_rename(git_repository *repo, struct remote_opts *o)
+{
+ int i, retval;
+ char *old, *new;
+ git_strarray problems = {0};
+
+ if (o->argc != 2)
+ usage("you need to specify old and new remote name", NULL);
+
+ old = o->argv[0];
+ new = o->argv[1];
+
+ retval = git_remote_rename(&problems, repo, old, new);
+ if (!retval)
+ return 0;
+
+ for (i = 0; i < (int) problems.count; i++) {
+ puts(problems.strings[0]);
+ }
+
+ git_strarray_dispose(&problems);
+
+ return retval;
+}
+
+static int cmd_seturl(git_repository *repo, struct remote_opts *o)
+{
+ int i, retval, push = 0;
+ char *name = NULL, *url = NULL;
+
+ for (i = 0; i < o->argc; i++) {
+ char *arg = o->argv[i];
+
+ if (!strcmp(arg, "--push")) {
+ push = 1;
+ } else if (arg[0] != '-' && name == NULL) {
+ name = arg;
+ } else if (arg[0] != '-' && url == NULL) {
+ url = arg;
+ } else {
+ usage("invalid argument to set-url", arg);
+ }
+ }
+
+ if (name == NULL || url == NULL)
+ usage("you need to specify remote and the new URL", NULL);
+
+ if (push)
+ retval = git_remote_set_pushurl(repo, name, url);
+ else
+ retval = git_remote_set_url(repo, name, url);
+
+ check_lg2(retval, "could not set URL", url);
+
+ return 0;
+}
+
+static int cmd_show(git_repository *repo, struct remote_opts *o)
+{
+ int i;
+ const char *arg, *name, *fetch, *push;
+ int verbose = 0;
+ git_strarray remotes = {0};
+ git_remote *remote = {0};
+
+ for (i = 0; i < o->argc; i++) {
+ arg = o->argv[i];
+
+ if (!strcmp(arg, "-v") || !strcmp(arg, "--verbose")) {
+ verbose = 1;
+ }
+ }
+
+ check_lg2(git_remote_list(&remotes, repo),
+ "could not retrieve remotes", NULL);
+
+ for (i = 0; i < (int) remotes.count; i++) {
+ name = remotes.strings[i];
+ if (!verbose) {
+ puts(name);
+ continue;
+ }
+
+ check_lg2(git_remote_lookup(&remote, repo, name),
+ "could not look up remote", name);
+
+ fetch = git_remote_url(remote);
+ if (fetch)
+ printf("%s\t%s (fetch)\n", name, fetch);
+ push = git_remote_pushurl(remote);
+ /* use fetch URL if no distinct push URL has been set */
+ push = push ? push : fetch;
+ if (push)
+ printf("%s\t%s (push)\n", name, push);
+
+ git_remote_free(remote);
+ }
+
+ git_strarray_dispose(&remotes);
+
+ return 0;
+}
+
+static void parse_subcmd(
+ struct remote_opts *opt, int argc, char **argv)
+{
+ char *arg = argv[1];
+ enum subcmd cmd = 0;
+
+ if (argc < 2)
+ usage("no command specified", NULL);
+
+ if (!strcmp(arg, "add")) {
+ cmd = subcmd_add;
+ } else if (!strcmp(arg, "remove")) {
+ cmd = subcmd_remove;
+ } else if (!strcmp(arg, "rename")) {
+ cmd = subcmd_rename;
+ } else if (!strcmp(arg, "set-url")) {
+ cmd = subcmd_seturl;
+ } else if (!strcmp(arg, "show")) {
+ cmd = subcmd_show;
+ } else {
+ usage("command is not valid", arg);
+ }
+ opt->cmd = cmd;
+
+ opt->argc = argc - 2; /* executable and subcommand are removed */
+ opt->argv = argv + 2;
+}
+
+static void usage(const char *msg, const char *arg)
+{
+ fputs("usage: remote add <name> <url>\n", stderr);
+ fputs(" remote remove <name>\n", stderr);
+ fputs(" remote rename <old> <new>\n", stderr);
+ fputs(" remote set-url [--push] <name> <newurl>\n", stderr);
+ fputs(" remote show [-v|--verbose]\n", stderr);
+
+ if (msg && !arg)
+ fprintf(stderr, "\n%s\n", msg);
+ else if (msg && arg)
+ fprintf(stderr, "\n%s: %s\n", msg, arg);
+ exit(1);
+}
diff --git a/examples/rev-list.c b/examples/rev-list.c
new file mode 100644
index 0000000..cf8ac30
--- /dev/null
+++ b/examples/rev-list.c
@@ -0,0 +1,158 @@
+/*
+ * libgit2 "rev-list" example - shows how to transform a rev-spec into a list
+ * of commit ids
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+
+#include <assert.h>
+
+static int revwalk_parse_options(git_sort_t *sort, struct args_info *args);
+static int revwalk_parse_revs(git_repository *repo, git_revwalk *walk, struct args_info *args);
+
+int lg2_rev_list(git_repository *repo, int argc, char **argv)
+{
+ struct args_info args = ARGS_INFO_INIT;
+ git_revwalk *walk;
+ git_oid oid;
+ git_sort_t sort;
+ char buf[GIT_OID_SHA1_HEXSIZE+1];
+
+ check_lg2(revwalk_parse_options(&sort, &args), "parsing options", NULL);
+
+ check_lg2(git_revwalk_new(&walk, repo), "allocating revwalk", NULL);
+ git_revwalk_sorting(walk, sort);
+ check_lg2(revwalk_parse_revs(repo, walk, &args), "parsing revs", NULL);
+
+ while (!git_revwalk_next(&oid, walk)) {
+ git_oid_fmt(buf, &oid);
+ buf[GIT_OID_SHA1_HEXSIZE] = '\0';
+ printf("%s\n", buf);
+ }
+
+ git_revwalk_free(walk);
+ return 0;
+}
+
+static int push_commit(git_revwalk *walk, const git_oid *oid, int hide)
+{
+ if (hide)
+ return git_revwalk_hide(walk, oid);
+ else
+ return git_revwalk_push(walk, oid);
+}
+
+static int push_spec(git_repository *repo, git_revwalk *walk, const char *spec, int hide)
+{
+ int error;
+ git_object *obj;
+
+ if ((error = git_revparse_single(&obj, repo, spec)) < 0)
+ return error;
+
+ error = push_commit(walk, git_object_id(obj), hide);
+ git_object_free(obj);
+ return error;
+}
+
+static int push_range(git_repository *repo, git_revwalk *walk, const char *range, int hide)
+{
+ git_revspec revspec;
+ int error = 0;
+
+ if ((error = git_revparse(&revspec, repo, range)))
+ return error;
+
+ if (revspec.flags & GIT_REVSPEC_MERGE_BASE) {
+ /* TODO: support "<commit>...<commit>" */
+ return GIT_EINVALIDSPEC;
+ }
+
+ if ((error = push_commit(walk, git_object_id(revspec.from), !hide)))
+ goto out;
+
+ error = push_commit(walk, git_object_id(revspec.to), hide);
+
+out:
+ git_object_free(revspec.from);
+ git_object_free(revspec.to);
+ return error;
+}
+
+static void print_usage(void)
+{
+ fprintf(stderr, "rev-list [--git-dir=dir] [--topo-order|--date-order] [--reverse] <revspec>\n");
+ exit(-1);
+}
+
+static int revwalk_parse_options(git_sort_t *sort, struct args_info *args)
+{
+ assert(sort && args);
+ *sort = GIT_SORT_NONE;
+
+ if (args->argc < 1)
+ print_usage();
+
+ for (args->pos = 1; args->pos < args->argc; ++args->pos) {
+ const char *curr = args->argv[args->pos];
+
+ if (!strcmp(curr, "--topo-order")) {
+ *sort |= GIT_SORT_TOPOLOGICAL;
+ } else if (!strcmp(curr, "--date-order")) {
+ *sort |= GIT_SORT_TIME;
+ } else if (!strcmp(curr, "--reverse")) {
+ *sort |= (*sort & ~GIT_SORT_REVERSE) ^ GIT_SORT_REVERSE;
+ } else {
+ break;
+ }
+ }
+ return 0;
+}
+
+static int revwalk_parse_revs(git_repository *repo, git_revwalk *walk, struct args_info *args)
+{
+ int hide, error;
+ git_oid oid;
+
+ hide = 0;
+ for (; args->pos < args->argc; ++args->pos) {
+ const char *curr = args->argv[args->pos];
+
+ if (!strcmp(curr, "--not")) {
+ hide = !hide;
+ } else if (curr[0] == '^') {
+ if ((error = push_spec(repo, walk, curr + 1, !hide)))
+ return error;
+ } else if (strstr(curr, "..")) {
+ if ((error = push_range(repo, walk, curr, hide)))
+ return error;
+ } else {
+ if (push_spec(repo, walk, curr, hide) == 0)
+ continue;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ if ((error = git_oid_fromstr(&oid, curr, GIT_OID_SHA1)))
+ return error;
+#else
+ if ((error = git_oid_fromstr(&oid, curr)))
+ return error;
+#endif
+
+ if ((error = push_commit(walk, &oid, hide)))
+ return error;
+ }
+ }
+
+ return 0;
+}
+
diff --git a/examples/rev-parse.c b/examples/rev-parse.c
new file mode 100644
index 0000000..3f68d79
--- /dev/null
+++ b/examples/rev-parse.c
@@ -0,0 +1,102 @@
+/*
+ * libgit2 "rev-parse" example - shows how to parse revspecs
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+
+/** Forward declarations for helpers. */
+struct parse_state {
+ const char *repodir;
+ const char *spec;
+ int not;
+};
+static void parse_opts(struct parse_state *ps, int argc, char *argv[]);
+static int parse_revision(git_repository *repo, struct parse_state *ps);
+
+int lg2_rev_parse(git_repository *repo, int argc, char *argv[])
+{
+ struct parse_state ps = {0};
+
+ parse_opts(&ps, argc, argv);
+
+ check_lg2(parse_revision(repo, &ps), "Parsing", NULL);
+
+ return 0;
+}
+
+static void usage(const char *message, const char *arg)
+{
+ if (message && arg)
+ fprintf(stderr, "%s: %s\n", message, arg);
+ else if (message)
+ fprintf(stderr, "%s\n", message);
+ fprintf(stderr, "usage: rev-parse [ --option ] <args>...\n");
+ exit(1);
+}
+
+static void parse_opts(struct parse_state *ps, int argc, char *argv[])
+{
+ struct args_info args = ARGS_INFO_INIT;
+
+ for (args.pos=1; args.pos < argc; ++args.pos) {
+ const char *a = argv[args.pos];
+
+ if (a[0] != '-') {
+ if (ps->spec)
+ usage("Too many specs", a);
+ ps->spec = a;
+ } else if (!strcmp(a, "--not"))
+ ps->not = !ps->not;
+ else if (!match_str_arg(&ps->repodir, &args, "--git-dir"))
+ usage("Cannot handle argument", a);
+ }
+}
+
+static int parse_revision(git_repository *repo, struct parse_state *ps)
+{
+ git_revspec rs;
+ char str[GIT_OID_SHA1_HEXSIZE + 1];
+
+ check_lg2(git_revparse(&rs, repo, ps->spec), "Could not parse", ps->spec);
+
+ if ((rs.flags & GIT_REVSPEC_SINGLE) != 0) {
+ git_oid_tostr(str, sizeof(str), git_object_id(rs.from));
+ printf("%s\n", str);
+ git_object_free(rs.from);
+ }
+ else if ((rs.flags & GIT_REVSPEC_RANGE) != 0) {
+ git_oid_tostr(str, sizeof(str), git_object_id(rs.to));
+ printf("%s\n", str);
+ git_object_free(rs.to);
+
+ if ((rs.flags & GIT_REVSPEC_MERGE_BASE) != 0) {
+ git_oid base;
+ check_lg2(git_merge_base(&base, repo,
+ git_object_id(rs.from), git_object_id(rs.to)),
+ "Could not find merge base", ps->spec);
+
+ git_oid_tostr(str, sizeof(str), &base);
+ printf("%s\n", str);
+ }
+
+ git_oid_tostr(str, sizeof(str), git_object_id(rs.from));
+ printf("^%s\n", str);
+ git_object_free(rs.from);
+ }
+ else {
+ fatal("Invalid results from git_revparse", ps->spec);
+ }
+
+ return 0;
+}
+
diff --git a/examples/show-index.c b/examples/show-index.c
new file mode 100644
index 0000000..0a5e7d1
--- /dev/null
+++ b/examples/show-index.c
@@ -0,0 +1,70 @@
+/*
+ * libgit2 "showindex" example - shows how to extract data from the index
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+
+int lg2_show_index(git_repository *repo, int argc, char **argv)
+{
+ git_index *index;
+ size_t i, ecount;
+ char *dir = ".";
+ size_t dirlen;
+ char out[GIT_OID_SHA1_HEXSIZE+1];
+ out[GIT_OID_SHA1_HEXSIZE] = '\0';
+
+ if (argc > 2)
+ fatal("usage: showindex [<repo-dir>]", NULL);
+ if (argc > 1)
+ dir = argv[1];
+
+ dirlen = strlen(dir);
+ if (dirlen > 5 && strcmp(dir + dirlen - 5, "index") == 0) {
+#ifdef GIT_EXPERIMENTAL_SHA256
+ check_lg2(git_index_open(&index, dir, GIT_OID_SHA1), "could not open index", dir);
+#else
+ check_lg2(git_index_open(&index, dir), "could not open index", dir);
+#endif
+ } else {
+ check_lg2(git_repository_open_ext(&repo, dir, 0, NULL), "could not open repository", dir);
+ check_lg2(git_repository_index(&index, repo), "could not open repository index", NULL);
+ git_repository_free(repo);
+ }
+
+ git_index_read(index, 0);
+
+ ecount = git_index_entrycount(index);
+ if (!ecount)
+ printf("Empty index\n");
+
+ for (i = 0; i < ecount; ++i) {
+ const git_index_entry *e = git_index_get_byindex(index, i);
+
+ git_oid_fmt(out, &e->id);
+
+ printf("File Path: %s\n", e->path);
+ printf(" Stage: %d\n", git_index_entry_stage(e));
+ printf(" Blob SHA: %s\n", out);
+ printf("File Mode: %07o\n", e->mode);
+ printf("File Size: %d bytes\n", (int)e->file_size);
+ printf("Dev/Inode: %d/%d\n", (int)e->dev, (int)e->ino);
+ printf(" UID/GID: %d/%d\n", (int)e->uid, (int)e->gid);
+ printf(" ctime: %d\n", (int)e->ctime.seconds);
+ printf(" mtime: %d\n", (int)e->mtime.seconds);
+ printf("\n");
+ }
+
+ git_index_free(index);
+
+ return 0;
+}
diff --git a/examples/stash.c b/examples/stash.c
new file mode 100644
index 0000000..8142439
--- /dev/null
+++ b/examples/stash.c
@@ -0,0 +1,157 @@
+/*
+ * libgit2 "stash" example - shows how to use the stash API
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include <stdarg.h>
+
+#include "common.h"
+
+enum subcmd {
+ SUBCMD_APPLY,
+ SUBCMD_LIST,
+ SUBCMD_POP,
+ SUBCMD_PUSH
+};
+
+struct opts {
+ enum subcmd cmd;
+ int argc;
+ char **argv;
+};
+
+static void usage(const char *fmt, ...)
+{
+ va_list ap;
+
+ fputs("usage: git stash list\n", stderr);
+ fputs(" or: git stash ( pop | apply )\n", stderr);
+ fputs(" or: git stash [push]\n", stderr);
+ fputs("\n", stderr);
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ exit(1);
+}
+
+static void parse_subcommand(struct opts *opts, int argc, char *argv[])
+{
+ char *arg = (argc < 2) ? "push" : argv[1];
+ enum subcmd cmd;
+
+ if (!strcmp(arg, "apply")) {
+ cmd = SUBCMD_APPLY;
+ } else if (!strcmp(arg, "list")) {
+ cmd = SUBCMD_LIST;
+ } else if (!strcmp(arg, "pop")) {
+ cmd = SUBCMD_POP;
+ } else if (!strcmp(arg, "push")) {
+ cmd = SUBCMD_PUSH;
+ } else {
+ usage("invalid command %s", arg);
+ return;
+ }
+
+ opts->cmd = cmd;
+ opts->argc = (argc < 2) ? argc - 1 : argc - 2;
+ opts->argv = argv;
+}
+
+static int cmd_apply(git_repository *repo, struct opts *opts)
+{
+ if (opts->argc)
+ usage("apply does not accept any parameters");
+
+ check_lg2(git_stash_apply(repo, 0, NULL),
+ "Unable to apply stash", NULL);
+
+ return 0;
+}
+
+static int list_stash_cb(size_t index, const char *message,
+ const git_oid *stash_id, void *payload)
+{
+ UNUSED(stash_id);
+ UNUSED(payload);
+ printf("stash@{%"PRIuZ"}: %s\n", index, message);
+ return 0;
+}
+
+static int cmd_list(git_repository *repo, struct opts *opts)
+{
+ if (opts->argc)
+ usage("list does not accept any parameters");
+
+ check_lg2(git_stash_foreach(repo, list_stash_cb, NULL),
+ "Unable to list stashes", NULL);
+
+ return 0;
+}
+
+static int cmd_push(git_repository *repo, struct opts *opts)
+{
+ git_signature *signature;
+ git_commit *stash;
+ git_oid stashid;
+
+ if (opts->argc)
+ usage("push does not accept any parameters");
+
+ check_lg2(git_signature_default(&signature, repo),
+ "Unable to get signature", NULL);
+ check_lg2(git_stash_save(&stashid, repo, signature, NULL, GIT_STASH_DEFAULT),
+ "Unable to save stash", NULL);
+ check_lg2(git_commit_lookup(&stash, repo, &stashid),
+ "Unable to lookup stash commit", NULL);
+
+ printf("Saved working directory %s\n", git_commit_summary(stash));
+
+ git_signature_free(signature);
+ git_commit_free(stash);
+
+ return 0;
+}
+
+static int cmd_pop(git_repository *repo, struct opts *opts)
+{
+ if (opts->argc)
+ usage("pop does not accept any parameters");
+
+ check_lg2(git_stash_pop(repo, 0, NULL),
+ "Unable to pop stash", NULL);
+
+ printf("Dropped refs/stash@{0}\n");
+
+ return 0;
+}
+
+int lg2_stash(git_repository *repo, int argc, char *argv[])
+{
+ struct opts opts = { 0 };
+
+ parse_subcommand(&opts, argc, argv);
+
+ switch (opts.cmd) {
+ case SUBCMD_APPLY:
+ return cmd_apply(repo, &opts);
+ case SUBCMD_LIST:
+ return cmd_list(repo, &opts);
+ case SUBCMD_PUSH:
+ return cmd_push(repo, &opts);
+ case SUBCMD_POP:
+ return cmd_pop(repo, &opts);
+ }
+
+ return -1;
+}
diff --git a/examples/status.c b/examples/status.c
new file mode 100644
index 0000000..e659efb
--- /dev/null
+++ b/examples/status.c
@@ -0,0 +1,498 @@
+/*
+ * libgit2 "status" example - shows how to use the status APIs
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+
+/**
+ * This example demonstrates the use of the libgit2 status APIs,
+ * particularly the `git_status_list` object, to roughly simulate the
+ * output of running `git status`. It serves as a simple example of
+ * using those APIs to get basic status information.
+ *
+ * This does not have:
+ *
+ * - Robust error handling
+ * - Colorized or paginated output formatting
+ *
+ * This does have:
+ *
+ * - Examples of translating command line arguments to the status
+ * options settings to mimic `git status` results.
+ * - A sample status formatter that matches the default "long" format
+ * from `git status`
+ * - A sample status formatter that matches the "short" format
+ */
+
+enum {
+ FORMAT_DEFAULT = 0,
+ FORMAT_LONG = 1,
+ FORMAT_SHORT = 2,
+ FORMAT_PORCELAIN = 3
+};
+
+#define MAX_PATHSPEC 8
+
+struct status_opts {
+ git_status_options statusopt;
+ char *repodir;
+ char *pathspec[MAX_PATHSPEC];
+ int npaths;
+ int format;
+ int zterm;
+ int showbranch;
+ int showsubmod;
+ int repeat;
+};
+
+static void parse_opts(struct status_opts *o, int argc, char *argv[]);
+static void show_branch(git_repository *repo, int format);
+static void print_long(git_status_list *status);
+static void print_short(git_repository *repo, git_status_list *status);
+static int print_submod(git_submodule *sm, const char *name, void *payload);
+
+int lg2_status(git_repository *repo, int argc, char *argv[])
+{
+ git_status_list *status;
+ struct status_opts o = { GIT_STATUS_OPTIONS_INIT, "." };
+
+ o.statusopt.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
+ o.statusopt.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX |
+ GIT_STATUS_OPT_SORT_CASE_SENSITIVELY;
+
+ parse_opts(&o, argc, argv);
+
+ if (git_repository_is_bare(repo))
+ fatal("Cannot report status on bare repository",
+ git_repository_path(repo));
+
+show_status:
+ if (o.repeat)
+ printf("\033[H\033[2J");
+
+ /**
+ * Run status on the repository
+ *
+ * We use `git_status_list_new()` to generate a list of status
+ * information which lets us iterate over it at our
+ * convenience and extract the data we want to show out of
+ * each entry.
+ *
+ * You can use `git_status_foreach()` or
+ * `git_status_foreach_ext()` if you'd prefer to execute a
+ * callback for each entry. The latter gives you more control
+ * about what results are presented.
+ */
+ check_lg2(git_status_list_new(&status, repo, &o.statusopt),
+ "Could not get status", NULL);
+
+ if (o.showbranch)
+ show_branch(repo, o.format);
+
+ if (o.showsubmod) {
+ int submod_count = 0;
+ check_lg2(git_submodule_foreach(repo, print_submod, &submod_count),
+ "Cannot iterate submodules", o.repodir);
+ }
+
+ if (o.format == FORMAT_LONG)
+ print_long(status);
+ else
+ print_short(repo, status);
+
+ git_status_list_free(status);
+
+ if (o.repeat) {
+ sleep(o.repeat);
+ goto show_status;
+ }
+
+ return 0;
+}
+
+/**
+ * If the user asked for the branch, let's show the short name of the
+ * branch.
+ */
+static void show_branch(git_repository *repo, int format)
+{
+ int error = 0;
+ const char *branch = NULL;
+ git_reference *head = NULL;
+
+ error = git_repository_head(&head, repo);
+
+ if (error == GIT_EUNBORNBRANCH || error == GIT_ENOTFOUND)
+ branch = NULL;
+ else if (!error) {
+ branch = git_reference_shorthand(head);
+ } else
+ check_lg2(error, "failed to get current branch", NULL);
+
+ if (format == FORMAT_LONG)
+ printf("# On branch %s\n",
+ branch ? branch : "Not currently on any branch.");
+ else
+ printf("## %s\n", branch ? branch : "HEAD (no branch)");
+
+ git_reference_free(head);
+}
+
+/**
+ * This function print out an output similar to git's status command
+ * in long form, including the command-line hints.
+ */
+static void print_long(git_status_list *status)
+{
+ size_t i, maxi = git_status_list_entrycount(status);
+ const git_status_entry *s;
+ int header = 0, changes_in_index = 0;
+ int changed_in_workdir = 0, rm_in_workdir = 0;
+ const char *old_path, *new_path;
+
+ /** Print index changes. */
+
+ for (i = 0; i < maxi; ++i) {
+ char *istatus = NULL;
+
+ s = git_status_byindex(status, i);
+
+ if (s->status == GIT_STATUS_CURRENT)
+ continue;
+
+ if (s->status & GIT_STATUS_WT_DELETED)
+ rm_in_workdir = 1;
+
+ if (s->status & GIT_STATUS_INDEX_NEW)
+ istatus = "new file: ";
+ if (s->status & GIT_STATUS_INDEX_MODIFIED)
+ istatus = "modified: ";
+ if (s->status & GIT_STATUS_INDEX_DELETED)
+ istatus = "deleted: ";
+ if (s->status & GIT_STATUS_INDEX_RENAMED)
+ istatus = "renamed: ";
+ if (s->status & GIT_STATUS_INDEX_TYPECHANGE)
+ istatus = "typechange:";
+
+ if (istatus == NULL)
+ continue;
+
+ if (!header) {
+ printf("# Changes to be committed:\n");
+ printf("# (use \"git reset HEAD <file>...\" to unstage)\n");
+ printf("#\n");
+ header = 1;
+ }
+
+ old_path = s->head_to_index->old_file.path;
+ new_path = s->head_to_index->new_file.path;
+
+ if (old_path && new_path && strcmp(old_path, new_path))
+ printf("#\t%s %s -> %s\n", istatus, old_path, new_path);
+ else
+ printf("#\t%s %s\n", istatus, old_path ? old_path : new_path);
+ }
+
+ if (header) {
+ changes_in_index = 1;
+ printf("#\n");
+ }
+ header = 0;
+
+ /** Print workdir changes to tracked files. */
+
+ for (i = 0; i < maxi; ++i) {
+ char *wstatus = NULL;
+
+ s = git_status_byindex(status, i);
+
+ /**
+ * With `GIT_STATUS_OPT_INCLUDE_UNMODIFIED` (not used in this example)
+ * `index_to_workdir` may not be `NULL` even if there are
+ * no differences, in which case it will be a `GIT_DELTA_UNMODIFIED`.
+ */
+ if (s->status == GIT_STATUS_CURRENT || s->index_to_workdir == NULL)
+ continue;
+
+ /** Print out the output since we know the file has some changes */
+ if (s->status & GIT_STATUS_WT_MODIFIED)
+ wstatus = "modified: ";
+ if (s->status & GIT_STATUS_WT_DELETED)
+ wstatus = "deleted: ";
+ if (s->status & GIT_STATUS_WT_RENAMED)
+ wstatus = "renamed: ";
+ if (s->status & GIT_STATUS_WT_TYPECHANGE)
+ wstatus = "typechange:";
+
+ if (wstatus == NULL)
+ continue;
+
+ if (!header) {
+ printf("# Changes not staged for commit:\n");
+ printf("# (use \"git add%s <file>...\" to update what will be committed)\n", rm_in_workdir ? "/rm" : "");
+ printf("# (use \"git checkout -- <file>...\" to discard changes in working directory)\n");
+ printf("#\n");
+ header = 1;
+ }
+
+ old_path = s->index_to_workdir->old_file.path;
+ new_path = s->index_to_workdir->new_file.path;
+
+ if (old_path && new_path && strcmp(old_path, new_path))
+ printf("#\t%s %s -> %s\n", wstatus, old_path, new_path);
+ else
+ printf("#\t%s %s\n", wstatus, old_path ? old_path : new_path);
+ }
+
+ if (header) {
+ changed_in_workdir = 1;
+ printf("#\n");
+ }
+
+ /** Print untracked files. */
+
+ header = 0;
+
+ for (i = 0; i < maxi; ++i) {
+ s = git_status_byindex(status, i);
+
+ if (s->status == GIT_STATUS_WT_NEW) {
+
+ if (!header) {
+ printf("# Untracked files:\n");
+ printf("# (use \"git add <file>...\" to include in what will be committed)\n");
+ printf("#\n");
+ header = 1;
+ }
+
+ printf("#\t%s\n", s->index_to_workdir->old_file.path);
+ }
+ }
+
+ header = 0;
+
+ /** Print ignored files. */
+
+ for (i = 0; i < maxi; ++i) {
+ s = git_status_byindex(status, i);
+
+ if (s->status == GIT_STATUS_IGNORED) {
+
+ if (!header) {
+ printf("# Ignored files:\n");
+ printf("# (use \"git add -f <file>...\" to include in what will be committed)\n");
+ printf("#\n");
+ header = 1;
+ }
+
+ printf("#\t%s\n", s->index_to_workdir->old_file.path);
+ }
+ }
+
+ if (!changes_in_index && changed_in_workdir)
+ printf("no changes added to commit (use \"git add\" and/or \"git commit -a\")\n");
+}
+
+/**
+ * This version of the output prefixes each path with two status
+ * columns and shows submodule status information.
+ */
+static void print_short(git_repository *repo, git_status_list *status)
+{
+ size_t i, maxi = git_status_list_entrycount(status);
+ const git_status_entry *s;
+ char istatus, wstatus;
+ const char *extra, *a, *b, *c;
+
+ for (i = 0; i < maxi; ++i) {
+ s = git_status_byindex(status, i);
+
+ if (s->status == GIT_STATUS_CURRENT)
+ continue;
+
+ a = b = c = NULL;
+ istatus = wstatus = ' ';
+ extra = "";
+
+ if (s->status & GIT_STATUS_INDEX_NEW)
+ istatus = 'A';
+ if (s->status & GIT_STATUS_INDEX_MODIFIED)
+ istatus = 'M';
+ if (s->status & GIT_STATUS_INDEX_DELETED)
+ istatus = 'D';
+ if (s->status & GIT_STATUS_INDEX_RENAMED)
+ istatus = 'R';
+ if (s->status & GIT_STATUS_INDEX_TYPECHANGE)
+ istatus = 'T';
+
+ if (s->status & GIT_STATUS_WT_NEW) {
+ if (istatus == ' ')
+ istatus = '?';
+ wstatus = '?';
+ }
+ if (s->status & GIT_STATUS_WT_MODIFIED)
+ wstatus = 'M';
+ if (s->status & GIT_STATUS_WT_DELETED)
+ wstatus = 'D';
+ if (s->status & GIT_STATUS_WT_RENAMED)
+ wstatus = 'R';
+ if (s->status & GIT_STATUS_WT_TYPECHANGE)
+ wstatus = 'T';
+
+ if (s->status & GIT_STATUS_IGNORED) {
+ istatus = '!';
+ wstatus = '!';
+ }
+
+ if (istatus == '?' && wstatus == '?')
+ continue;
+
+ /**
+ * A commit in a tree is how submodules are stored, so
+ * let's go take a look at its status.
+ */
+ if (s->index_to_workdir &&
+ s->index_to_workdir->new_file.mode == GIT_FILEMODE_COMMIT)
+ {
+ unsigned int smstatus = 0;
+
+ if (!git_submodule_status(&smstatus, repo, s->index_to_workdir->new_file.path,
+ GIT_SUBMODULE_IGNORE_UNSPECIFIED)) {
+ if (smstatus & GIT_SUBMODULE_STATUS_WD_MODIFIED)
+ extra = " (new commits)";
+ else if (smstatus & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED)
+ extra = " (modified content)";
+ else if (smstatus & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED)
+ extra = " (modified content)";
+ else if (smstatus & GIT_SUBMODULE_STATUS_WD_UNTRACKED)
+ extra = " (untracked content)";
+ }
+ }
+
+ /**
+ * Now that we have all the information, format the output.
+ */
+
+ if (s->head_to_index) {
+ a = s->head_to_index->old_file.path;
+ b = s->head_to_index->new_file.path;
+ }
+ if (s->index_to_workdir) {
+ if (!a)
+ a = s->index_to_workdir->old_file.path;
+ if (!b)
+ b = s->index_to_workdir->old_file.path;
+ c = s->index_to_workdir->new_file.path;
+ }
+
+ if (istatus == 'R') {
+ if (wstatus == 'R')
+ printf("%c%c %s %s %s%s\n", istatus, wstatus, a, b, c, extra);
+ else
+ printf("%c%c %s %s%s\n", istatus, wstatus, a, b, extra);
+ } else {
+ if (wstatus == 'R')
+ printf("%c%c %s %s%s\n", istatus, wstatus, a, c, extra);
+ else
+ printf("%c%c %s%s\n", istatus, wstatus, a, extra);
+ }
+ }
+
+ for (i = 0; i < maxi; ++i) {
+ s = git_status_byindex(status, i);
+
+ if (s->status == GIT_STATUS_WT_NEW)
+ printf("?? %s\n", s->index_to_workdir->old_file.path);
+ }
+}
+
+static int print_submod(git_submodule *sm, const char *name, void *payload)
+{
+ int *count = payload;
+ (void)name;
+
+ if (*count == 0)
+ printf("# Submodules\n");
+ (*count)++;
+
+ printf("# - submodule '%s' at %s\n",
+ git_submodule_name(sm), git_submodule_path(sm));
+
+ return 0;
+}
+
+/**
+ * Parse options that git's status command supports.
+ */
+static void parse_opts(struct status_opts *o, int argc, char *argv[])
+{
+ struct args_info args = ARGS_INFO_INIT;
+
+ for (args.pos = 1; args.pos < argc; ++args.pos) {
+ char *a = argv[args.pos];
+
+ if (a[0] != '-') {
+ if (o->npaths < MAX_PATHSPEC)
+ o->pathspec[o->npaths++] = a;
+ else
+ fatal("Example only supports a limited pathspec", NULL);
+ }
+ else if (!strcmp(a, "-s") || !strcmp(a, "--short"))
+ o->format = FORMAT_SHORT;
+ else if (!strcmp(a, "--long"))
+ o->format = FORMAT_LONG;
+ else if (!strcmp(a, "--porcelain"))
+ o->format = FORMAT_PORCELAIN;
+ else if (!strcmp(a, "-b") || !strcmp(a, "--branch"))
+ o->showbranch = 1;
+ else if (!strcmp(a, "-z")) {
+ o->zterm = 1;
+ if (o->format == FORMAT_DEFAULT)
+ o->format = FORMAT_PORCELAIN;
+ }
+ else if (!strcmp(a, "--ignored"))
+ o->statusopt.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED;
+ else if (!strcmp(a, "-uno") ||
+ !strcmp(a, "--untracked-files=no"))
+ o->statusopt.flags &= ~GIT_STATUS_OPT_INCLUDE_UNTRACKED;
+ else if (!strcmp(a, "-unormal") ||
+ !strcmp(a, "--untracked-files=normal"))
+ o->statusopt.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
+ else if (!strcmp(a, "-uall") ||
+ !strcmp(a, "--untracked-files=all"))
+ o->statusopt.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
+ else if (!strcmp(a, "--ignore-submodules=all"))
+ o->statusopt.flags |= GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
+ else if (!strncmp(a, "--git-dir=", strlen("--git-dir=")))
+ o->repodir = a + strlen("--git-dir=");
+ else if (!strcmp(a, "--repeat"))
+ o->repeat = 10;
+ else if (match_int_arg(&o->repeat, &args, "--repeat", 0))
+ /* okay */;
+ else if (!strcmp(a, "--list-submodules"))
+ o->showsubmod = 1;
+ else
+ check_lg2(-1, "Unsupported option", a);
+ }
+
+ if (o->format == FORMAT_DEFAULT)
+ o->format = FORMAT_LONG;
+ if (o->format == FORMAT_LONG)
+ o->showbranch = 1;
+ if (o->npaths > 0) {
+ o->statusopt.pathspec.strings = o->pathspec;
+ o->statusopt.pathspec.count = o->npaths;
+ }
+}
diff --git a/examples/tag.c b/examples/tag.c
new file mode 100644
index 0000000..e4f71ae
--- /dev/null
+++ b/examples/tag.c
@@ -0,0 +1,310 @@
+/*
+ * libgit2 "tag" example - shows how to list, create and delete tags
+ *
+ * Written by the libgit2 contributors
+ *
+ * To the extent possible under law, the author(s) have dedicated all copyright
+ * and related and neighboring rights to this software to the public domain
+ * worldwide. This software is distributed without any warranty.
+ *
+ * You should have received a copy of the CC0 Public Domain Dedication along
+ * with this software. If not, see
+ * <http://creativecommons.org/publicdomain/zero/1.0/>.
+ */
+
+#include "common.h"
+
+/**
+ * The following example partially reimplements the `git tag` command
+ * and some of its options.
+ *
+ * These commands should work:
+
+ * - Tag name listing (`tag`)
+ * - Filtered tag listing with messages (`tag -n3 -l "v0.1*"`)
+ * - Lightweight tag creation (`tag test v0.18.0`)
+ * - Tag creation (`tag -a -m "Test message" test v0.18.0`)
+ * - Tag deletion (`tag -d test`)
+ *
+ * The command line parsing logic is simplified and doesn't handle
+ * all of the use cases.
+ */
+
+/** tag_options represents the parsed command line options */
+struct tag_options {
+ const char *message;
+ const char *pattern;
+ const char *tag_name;
+ const char *target;
+ int num_lines;
+ int force;
+};
+
+/** tag_state represents the current program state for dragging around */
+typedef struct {
+ git_repository *repo;
+ struct tag_options *opts;
+} tag_state;
+
+/** An action to execute based on the command line arguments */
+typedef void (*tag_action)(tag_state *state);
+typedef struct args_info args_info;
+
+static void check(int result, const char *message)
+{
+ if (result) fatal(message, NULL);
+}
+
+/** Tag listing: Print individual message lines */
+static void print_list_lines(const char *message, const tag_state *state)
+{
+ const char *msg = message;
+ int num = state->opts->num_lines - 1;
+
+ if (!msg) return;
+
+ /** first line - headline */
+ while(*msg && *msg != '\n') printf("%c", *msg++);
+
+ /** skip over new lines */
+ while(*msg && *msg == '\n') msg++;
+
+ printf("\n");
+
+ /** print just headline? */
+ if (num == 0) return;
+ if (*msg && msg[1]) printf("\n");
+
+ /** print individual commit/tag lines */
+ while (*msg && num-- >= 2) {
+ printf(" ");
+
+ while (*msg && *msg != '\n') printf("%c", *msg++);
+
+ /** handle consecutive new lines */
+ if (*msg && *msg == '\n' && msg[1] == '\n') {
+ num--;
+ printf("\n");
+ }
+ while(*msg && *msg == '\n') msg++;
+
+ printf("\n");
+ }
+}
+
+/** Tag listing: Print an actual tag object */
+static void print_tag(git_tag *tag, const tag_state *state)
+{
+ printf("%-16s", git_tag_name(tag));
+
+ if (state->opts->num_lines) {
+ const char *msg = git_tag_message(tag);
+ print_list_lines(msg, state);
+ } else {
+ printf("\n");
+ }
+}
+
+/** Tag listing: Print a commit (target of a lightweight tag) */
+static void print_commit(git_commit *commit, const char *name,
+ const tag_state *state)
+{
+ printf("%-16s", name);
+
+ if (state->opts->num_lines) {
+ const char *msg = git_commit_message(commit);
+ print_list_lines(msg, state);
+ } else {
+ printf("\n");
+ }
+}
+
+/** Tag listing: Fallback, should not happen */
+static void print_name(const char *name)
+{
+ printf("%s\n", name);
+}
+
+/** Tag listing: Lookup tags based on ref name and dispatch to print */
+static int each_tag(const char *name, tag_state *state)
+{
+ git_repository *repo = state->repo;
+ git_object *obj;
+
+ check_lg2(git_revparse_single(&obj, repo, name),
+ "Failed to lookup rev", name);
+
+ switch (git_object_type(obj)) {
+ case GIT_OBJECT_TAG:
+ print_tag((git_tag *) obj, state);
+ break;
+ case GIT_OBJECT_COMMIT:
+ print_commit((git_commit *) obj, name, state);
+ break;
+ default:
+ print_name(name);
+ }
+
+ git_object_free(obj);
+ return 0;
+}
+
+static void action_list_tags(tag_state *state)
+{
+ const char *pattern = state->opts->pattern;
+ git_strarray tag_names = {0};
+ size_t i;
+
+ check_lg2(git_tag_list_match(&tag_names, pattern ? pattern : "*", state->repo),
+ "Unable to get list of tags", NULL);
+
+ for(i = 0; i < tag_names.count; i++) {
+ each_tag(tag_names.strings[i], state);
+ }
+
+ git_strarray_dispose(&tag_names);
+}
+
+static void action_delete_tag(tag_state *state)
+{
+ struct tag_options *opts = state->opts;
+ git_object *obj;
+ git_buf abbrev_oid = {0};
+
+ check(!opts->tag_name, "Name required");
+
+ check_lg2(git_revparse_single(&obj, state->repo, opts->tag_name),
+ "Failed to lookup rev", opts->tag_name);
+
+ check_lg2(git_object_short_id(&abbrev_oid, obj),
+ "Unable to get abbreviated OID", opts->tag_name);
+
+ check_lg2(git_tag_delete(state->repo, opts->tag_name),
+ "Unable to delete tag", opts->tag_name);
+
+ printf("Deleted tag '%s' (was %s)\n", opts->tag_name, abbrev_oid.ptr);
+
+ git_buf_dispose(&abbrev_oid);
+ git_object_free(obj);
+}
+
+static void action_create_lightweight_tag(tag_state *state)
+{
+ git_repository *repo = state->repo;
+ struct tag_options *opts = state->opts;
+ git_oid oid;
+ git_object *target;
+
+ check(!opts->tag_name, "Name required");
+
+ if (!opts->target) opts->target = "HEAD";
+
+ check(!opts->target, "Target required");
+
+ check_lg2(git_revparse_single(&target, repo, opts->target),
+ "Unable to resolve spec", opts->target);
+
+ check_lg2(git_tag_create_lightweight(&oid, repo, opts->tag_name,
+ target, opts->force), "Unable to create tag", NULL);
+
+ git_object_free(target);
+}
+
+static void action_create_tag(tag_state *state)
+{
+ git_repository *repo = state->repo;
+ struct tag_options *opts = state->opts;
+ git_signature *tagger;
+ git_oid oid;
+ git_object *target;
+
+ check(!opts->tag_name, "Name required");
+ check(!opts->message, "Message required");
+
+ if (!opts->target) opts->target = "HEAD";
+
+ check_lg2(git_revparse_single(&target, repo, opts->target),
+ "Unable to resolve spec", opts->target);
+
+ check_lg2(git_signature_default(&tagger, repo),
+ "Unable to create signature", NULL);
+
+ check_lg2(git_tag_create(&oid, repo, opts->tag_name,
+ target, tagger, opts->message, opts->force), "Unable to create tag", NULL);
+
+ git_object_free(target);
+ git_signature_free(tagger);
+}
+
+static void print_usage(void)
+{
+ fprintf(stderr, "usage: see `git help tag`\n");
+ exit(1);
+}
+
+/** Parse command line arguments and choose action to run when done */
+static void parse_options(tag_action *action, struct tag_options *opts, int argc, char **argv)
+{
+ args_info args = ARGS_INFO_INIT;
+ *action = &action_list_tags;
+
+ for (args.pos = 1; args.pos < argc; ++args.pos) {
+ const char *curr = argv[args.pos];
+
+ if (curr[0] != '-') {
+ if (!opts->tag_name)
+ opts->tag_name = curr;
+ else if (!opts->target)
+ opts->target = curr;
+ else
+ print_usage();
+
+ if (*action != &action_create_tag)
+ *action = &action_create_lightweight_tag;
+ } else if (!strcmp(curr, "-n")) {
+ opts->num_lines = 1;
+ *action = &action_list_tags;
+ } else if (!strcmp(curr, "-a")) {
+ *action = &action_create_tag;
+ } else if (!strcmp(curr, "-f")) {
+ opts->force = 1;
+ } else if (match_int_arg(&opts->num_lines, &args, "-n", 0)) {
+ *action = &action_list_tags;
+ } else if (match_str_arg(&opts->pattern, &args, "-l")) {
+ *action = &action_list_tags;
+ } else if (match_str_arg(&opts->tag_name, &args, "-d")) {
+ *action = &action_delete_tag;
+ } else if (match_str_arg(&opts->message, &args, "-m")) {
+ *action = &action_create_tag;
+ }
+ }
+}
+
+/** Initialize tag_options struct */
+static void tag_options_init(struct tag_options *opts)
+{
+ memset(opts, 0, sizeof(*opts));
+
+ opts->message = NULL;
+ opts->pattern = NULL;
+ opts->tag_name = NULL;
+ opts->target = NULL;
+ opts->num_lines = 0;
+ opts->force = 0;
+}
+
+int lg2_tag(git_repository *repo, int argc, char **argv)
+{
+ struct tag_options opts;
+ tag_action action;
+ tag_state state;
+
+ tag_options_init(&opts);
+ parse_options(&action, &opts, argc, argv);
+
+ state.repo = repo;
+ state.opts = &opts;
+ action(&state);
+
+ return 0;
+}
diff --git a/fuzzers/CMakeLists.txt b/fuzzers/CMakeLists.txt
new file mode 100644
index 0000000..a2c19ed
--- /dev/null
+++ b/fuzzers/CMakeLists.txt
@@ -0,0 +1,28 @@
+# fuzzers: libFuzzer and standalone fuzzing utilities
+
+if(BUILD_FUZZERS AND NOT USE_STANDALONE_FUZZERS)
+ set(CMAKE_REQUIRED_FLAGS "-fsanitize=fuzzer-no-link")
+ add_c_flag(-fsanitize=fuzzer)
+ add_c_flag(-fsanitize=fuzzer-no-link)
+ unset(CMAKE_REQUIRED_FLAGS)
+endif()
+
+file(GLOB SRC_FUZZERS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *_fuzzer.c)
+foreach(fuzz_target_src ${SRC_FUZZERS})
+ string(REPLACE ".c" "" fuzz_target_name ${fuzz_target_src})
+ string(REPLACE "_fuzzer" "" fuzz_name ${fuzz_target_name})
+
+ set(${fuzz_target_name}_SOURCES ${fuzz_target_src} ${LIBGIT2_OBJECTS})
+ if(USE_STANDALONE_FUZZERS)
+ list(APPEND ${fuzz_target_name}_SOURCES "standalone_driver.c")
+ endif()
+ add_executable(${fuzz_target_name} ${${fuzz_target_name}_SOURCES})
+ set_target_properties(${fuzz_target_name} PROPERTIES C_STANDARD 90)
+
+ target_include_directories(${fuzz_target_name} PRIVATE ${LIBGIT2_INCLUDES} ${LIBGIT2_DEPENDENCY_INCLUDES})
+ target_include_directories(${fuzz_target_name} SYSTEM PRIVATE ${LIBGIT2_SYSTEM_INCLUDES})
+
+ target_link_libraries(${fuzz_target_name} ${LIBGIT2_SYSTEM_LIBS})
+
+ add_test(${fuzz_target_name} "${CMAKE_CURRENT_BINARY_DIR}/${fuzz_target_name}" "${CMAKE_CURRENT_SOURCE_DIR}/corpora/${fuzz_name}")
+endforeach()
diff --git a/fuzzers/commit_graph_fuzzer.c b/fuzzers/commit_graph_fuzzer.c
new file mode 100644
index 0000000..9c1443e
--- /dev/null
+++ b/fuzzers/commit_graph_fuzzer.c
@@ -0,0 +1,85 @@
+/*
+ * libgit2 commit-graph fuzzer target.
+ *
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include <stdio.h>
+
+#include "git2.h"
+
+#include "common.h"
+#include "str.h"
+#include "futils.h"
+#include "hash.h"
+#include "commit_graph.h"
+
+#include "standalone_driver.h"
+
+int LLVMFuzzerInitialize(int *argc, char ***argv)
+{
+ GIT_UNUSED(argc);
+ GIT_UNUSED(argv);
+
+ if (git_libgit2_init() < 0) {
+ fprintf(stderr, "Failed to initialize libgit2\n");
+ abort();
+ }
+ return 0;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ git_commit_graph_file file = {{0}};
+ git_commit_graph_entry e;
+ git_str commit_graph_buf = GIT_STR_INIT;
+ unsigned char hash[GIT_HASH_SHA1_SIZE];
+ git_oid oid = GIT_OID_NONE;
+ bool append_hash = false;
+
+ if (size < 4)
+ return 0;
+
+ /*
+ * If the first byte in the stream has the high bit set, append the
+ * SHA1 hash so that the file is somewhat valid.
+ */
+ append_hash = *data & 0x80;
+ /* Keep a 4-byte alignment to avoid unaligned accesses. */
+ data += 4;
+ size -= 4;
+
+ if (append_hash) {
+ if (git_str_init(&commit_graph_buf, size + GIT_HASH_SHA1_SIZE) < 0)
+ goto cleanup;
+ if (git_hash_buf(hash, data, size, GIT_HASH_ALGORITHM_SHA1) < 0) {
+ fprintf(stderr, "Failed to compute the SHA1 hash\n");
+ abort();
+ }
+ memcpy(commit_graph_buf.ptr, data, size);
+ memcpy(commit_graph_buf.ptr + size, hash, GIT_HASH_SHA1_SIZE);
+
+ memcpy(oid.id, hash, GIT_OID_SHA1_SIZE);
+ } else {
+ git_str_attach_notowned(&commit_graph_buf, (char *)data, size);
+ }
+
+ if (git_commit_graph_file_parse(
+ &file,
+ (const unsigned char *)git_str_cstr(&commit_graph_buf),
+ git_str_len(&commit_graph_buf))
+ < 0)
+ goto cleanup;
+
+ /* Search for any oid, just to exercise that codepath. */
+ if (git_commit_graph_entry_find(&e, &file, &oid, GIT_OID_SHA1_HEXSIZE) < 0)
+ goto cleanup;
+
+cleanup:
+ git_commit_graph_file_close(&file);
+ git_str_dispose(&commit_graph_buf);
+ return 0;
+}
diff --git a/fuzzers/config_file_fuzzer.c b/fuzzers/config_file_fuzzer.c
new file mode 100644
index 0000000..890adbf
--- /dev/null
+++ b/fuzzers/config_file_fuzzer.c
@@ -0,0 +1,61 @@
+/*
+ * libgit2 config file parser fuzz target.
+ *
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "git2.h"
+#include "config_backend.h"
+
+#include "standalone_driver.h"
+
+#define UNUSED(x) (void)(x)
+
+static int foreach_cb(const git_config_entry *entry, void *payload)
+{
+ UNUSED(entry);
+ UNUSED(payload);
+
+ return 0;
+}
+
+int LLVMFuzzerInitialize(int *argc, char ***argv)
+{
+ UNUSED(argc);
+ UNUSED(argv);
+
+ if (git_libgit2_init() < 0)
+ abort();
+
+ return 0;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ git_config *cfg = NULL;
+ git_config_backend *backend = NULL;
+ int err = 0;
+
+ if ((err = git_config_new(&cfg)) != 0) {
+ goto out;
+ }
+
+ if ((err = git_config_backend_from_string(&backend, (const char*)data, size)) != 0) {
+ goto out;
+ }
+ if ((err = git_config_add_backend(cfg, backend, 0, NULL, 0)) != 0) {
+ goto out;
+ }
+ /* Now owned by the config */
+ backend = NULL;
+
+ git_config_foreach(cfg, foreach_cb, NULL);
+ out:
+ git_config_backend_free(backend);
+ git_config_free(cfg);
+
+ return 0;
+}
diff --git a/fuzzers/corpora/commit_graph/005682ce1cb5b20c20fccf4be5dbd47ca399e53e b/fuzzers/corpora/commit_graph/005682ce1cb5b20c20fccf4be5dbd47ca399e53e
new file mode 100644
index 0000000..15d0d28
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/005682ce1cb5b20c20fccf4be5dbd47ca399e53e
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/00574fc29fd1323e93d18d625cde80d3ea20e8cc b/fuzzers/corpora/commit_graph/00574fc29fd1323e93d18d625cde80d3ea20e8cc
new file mode 100644
index 0000000..4eabd00
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/00574fc29fd1323e93d18d625cde80d3ea20e8cc
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/00916ec21ddbd3c622bde6e4dc824250176b9e88 b/fuzzers/corpora/commit_graph/00916ec21ddbd3c622bde6e4dc824250176b9e88
new file mode 100644
index 0000000..d069fb5
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/00916ec21ddbd3c622bde6e4dc824250176b9e88
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/00b6dde4b8d5e68a5ec40d88c39134cf2f1f8bc3 b/fuzzers/corpora/commit_graph/00b6dde4b8d5e68a5ec40d88c39134cf2f1f8bc3
new file mode 100644
index 0000000..98f2d0e
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/00b6dde4b8d5e68a5ec40d88c39134cf2f1f8bc3
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/020f0e77e42d8b3810019050f4c5ceadd205b37c b/fuzzers/corpora/commit_graph/020f0e77e42d8b3810019050f4c5ceadd205b37c
new file mode 100644
index 0000000..d09327d
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/020f0e77e42d8b3810019050f4c5ceadd205b37c
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/02739c05abc1715fac1ce995b532e482abc8d4dc b/fuzzers/corpora/commit_graph/02739c05abc1715fac1ce995b532e482abc8d4dc
new file mode 100644
index 0000000..b9de08e
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/02739c05abc1715fac1ce995b532e482abc8d4dc
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/02a276faa5dc8c7df5b82a57ab6cd195a13e4ae0 b/fuzzers/corpora/commit_graph/02a276faa5dc8c7df5b82a57ab6cd195a13e4ae0
new file mode 100644
index 0000000..891d3ef
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/02a276faa5dc8c7df5b82a57ab6cd195a13e4ae0
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/02de15987d68a97db3d9fd964cfd785bcbd54d3a b/fuzzers/corpora/commit_graph/02de15987d68a97db3d9fd964cfd785bcbd54d3a
new file mode 100644
index 0000000..b8bd4ce
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/02de15987d68a97db3d9fd964cfd785bcbd54d3a
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/02e106f97a91b1d3aef4dd2d31368ae5077bd42b b/fuzzers/corpora/commit_graph/02e106f97a91b1d3aef4dd2d31368ae5077bd42b
new file mode 100644
index 0000000..f127739
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/02e106f97a91b1d3aef4dd2d31368ae5077bd42b
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/038555bcb4cc2daf764840f79ebce4023bdb7670 b/fuzzers/corpora/commit_graph/038555bcb4cc2daf764840f79ebce4023bdb7670
new file mode 100644
index 0000000..239f4e6
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/038555bcb4cc2daf764840f79ebce4023bdb7670
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/04c159a04b0732e04ac4c59ed3356860af8dffce b/fuzzers/corpora/commit_graph/04c159a04b0732e04ac4c59ed3356860af8dffce
new file mode 100644
index 0000000..856af0f
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/04c159a04b0732e04ac4c59ed3356860af8dffce
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/0560ec993882ffbd8d46dcab0ed430089c4f2aa1 b/fuzzers/corpora/commit_graph/0560ec993882ffbd8d46dcab0ed430089c4f2aa1
new file mode 100644
index 0000000..a236d1a
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/0560ec993882ffbd8d46dcab0ed430089c4f2aa1
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/059b3aab3fde6b4c9404aff83fed638596f594bb b/fuzzers/corpora/commit_graph/059b3aab3fde6b4c9404aff83fed638596f594bb
new file mode 100644
index 0000000..4900105
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/059b3aab3fde6b4c9404aff83fed638596f594bb
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/06168e726aa0260f520165be4ea0c88244831049 b/fuzzers/corpora/commit_graph/06168e726aa0260f520165be4ea0c88244831049
new file mode 100644
index 0000000..4c9e9df
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/06168e726aa0260f520165be4ea0c88244831049
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/066d1ec700a526b97009cedd0305b6a47242faba b/fuzzers/corpora/commit_graph/066d1ec700a526b97009cedd0305b6a47242faba
new file mode 100644
index 0000000..f5b1761
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/066d1ec700a526b97009cedd0305b6a47242faba
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/086a5f8cbfa9f058b5c938a6eb724c9e4c5f84f3 b/fuzzers/corpora/commit_graph/086a5f8cbfa9f058b5c938a6eb724c9e4c5f84f3
new file mode 100644
index 0000000..aa9cdca
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/086a5f8cbfa9f058b5c938a6eb724c9e4c5f84f3
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/089313c698f3e351433e9a45af2ace1d85b9673e b/fuzzers/corpora/commit_graph/089313c698f3e351433e9a45af2ace1d85b9673e
new file mode 100644
index 0000000..14fd3bb
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/089313c698f3e351433e9a45af2ace1d85b9673e
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/092eb973a771fa14cf0b567d65bd2c99130f543e b/fuzzers/corpora/commit_graph/092eb973a771fa14cf0b567d65bd2c99130f543e
new file mode 100644
index 0000000..3092a2b
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/092eb973a771fa14cf0b567d65bd2c99130f543e
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/094b8cd1aa3e40b1f9ff83680892d52e246df0f8 b/fuzzers/corpora/commit_graph/094b8cd1aa3e40b1f9ff83680892d52e246df0f8
new file mode 100644
index 0000000..ed62ec9
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/094b8cd1aa3e40b1f9ff83680892d52e246df0f8
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/0ce990c9c2ec121b8c78ba2bdf84679e04c0bdae b/fuzzers/corpora/commit_graph/0ce990c9c2ec121b8c78ba2bdf84679e04c0bdae
new file mode 100644
index 0000000..d708b68
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/0ce990c9c2ec121b8c78ba2bdf84679e04c0bdae
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/0dd0770c34fcf6b1f13219450190616d344db021 b/fuzzers/corpora/commit_graph/0dd0770c34fcf6b1f13219450190616d344db021
new file mode 100644
index 0000000..aea94b2
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/0dd0770c34fcf6b1f13219450190616d344db021
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/0e2b2e6a32733b8a625bc7e812e2ea508d69a5e4 b/fuzzers/corpora/commit_graph/0e2b2e6a32733b8a625bc7e812e2ea508d69a5e4
new file mode 100644
index 0000000..58e8801
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/0e2b2e6a32733b8a625bc7e812e2ea508d69a5e4
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/0e8d0bd07c543d708aecaca377106492b7a74fa3 b/fuzzers/corpora/commit_graph/0e8d0bd07c543d708aecaca377106492b7a74fa3
new file mode 100644
index 0000000..98a9230
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/0e8d0bd07c543d708aecaca377106492b7a74fa3
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/0f0d16e1b8c8671dbe1074115c1d86aa9b359e7e b/fuzzers/corpora/commit_graph/0f0d16e1b8c8671dbe1074115c1d86aa9b359e7e
new file mode 100644
index 0000000..bc322de
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/0f0d16e1b8c8671dbe1074115c1d86aa9b359e7e
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/102ef78036de5a30927e7f751377b05441c41a08 b/fuzzers/corpora/commit_graph/102ef78036de5a30927e7f751377b05441c41a08
new file mode 100644
index 0000000..3b14c35
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/102ef78036de5a30927e7f751377b05441c41a08
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/10494e7cc9cb8dff289c431d7560bcee0d1b14ed b/fuzzers/corpora/commit_graph/10494e7cc9cb8dff289c431d7560bcee0d1b14ed
new file mode 100644
index 0000000..293c49d
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/10494e7cc9cb8dff289c431d7560bcee0d1b14ed
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/107b11d86381345f50aa19b8485477a870ff399f b/fuzzers/corpora/commit_graph/107b11d86381345f50aa19b8485477a870ff399f
new file mode 100644
index 0000000..53bb34e
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/107b11d86381345f50aa19b8485477a870ff399f
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/10bb37e18fb3c0897dabacf9c464b4d324007dc3 b/fuzzers/corpora/commit_graph/10bb37e18fb3c0897dabacf9c464b4d324007dc3
new file mode 100644
index 0000000..e75a9c5
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/10bb37e18fb3c0897dabacf9c464b4d324007dc3
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/10ee715f64b08549c3e8261204276694728eb841 b/fuzzers/corpora/commit_graph/10ee715f64b08549c3e8261204276694728eb841
new file mode 100644
index 0000000..104eda6
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/10ee715f64b08549c3e8261204276694728eb841
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/123e4eeb7a731f48d06e336b4d29af717f8b6550 b/fuzzers/corpora/commit_graph/123e4eeb7a731f48d06e336b4d29af717f8b6550
new file mode 100644
index 0000000..22d1482
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/123e4eeb7a731f48d06e336b4d29af717f8b6550
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/125a228afb923970e0a6d4412f7257ba998594a1 b/fuzzers/corpora/commit_graph/125a228afb923970e0a6d4412f7257ba998594a1
new file mode 100644
index 0000000..3de2c87
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/125a228afb923970e0a6d4412f7257ba998594a1
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/130d96c16fba06dcbe7e2a661ab959a3274a4bd9 b/fuzzers/corpora/commit_graph/130d96c16fba06dcbe7e2a661ab959a3274a4bd9
new file mode 100644
index 0000000..66a4097
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/130d96c16fba06dcbe7e2a661ab959a3274a4bd9
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/131c5a2fec55cb0d63f7dc055d6fad5f3dc3c974 b/fuzzers/corpora/commit_graph/131c5a2fec55cb0d63f7dc055d6fad5f3dc3c974
new file mode 100644
index 0000000..b54bfad
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/131c5a2fec55cb0d63f7dc055d6fad5f3dc3c974
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/13e562d61acb3aa36260a819a00b07ff16450335 b/fuzzers/corpora/commit_graph/13e562d61acb3aa36260a819a00b07ff16450335
new file mode 100644
index 0000000..6682c84
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/13e562d61acb3aa36260a819a00b07ff16450335
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/1414e6e8ab6bad1b5c51fed807c514a9d6575e66 b/fuzzers/corpora/commit_graph/1414e6e8ab6bad1b5c51fed807c514a9d6575e66
new file mode 100644
index 0000000..c7f2386
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/1414e6e8ab6bad1b5c51fed807c514a9d6575e66
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/1432d191846ae2d0e381813efcfacff2f1dba0e4 b/fuzzers/corpora/commit_graph/1432d191846ae2d0e381813efcfacff2f1dba0e4
new file mode 100644
index 0000000..cf5b990
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/1432d191846ae2d0e381813efcfacff2f1dba0e4
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/14a84cdc6f8d432be4cd3d3eafce92ae385e472f b/fuzzers/corpora/commit_graph/14a84cdc6f8d432be4cd3d3eafce92ae385e472f
new file mode 100644
index 0000000..8f86651
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/14a84cdc6f8d432be4cd3d3eafce92ae385e472f
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/14e3e735dba88791f2cadd6e0dc5d662a104a6d7 b/fuzzers/corpora/commit_graph/14e3e735dba88791f2cadd6e0dc5d662a104a6d7
new file mode 100644
index 0000000..32fb993
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/14e3e735dba88791f2cadd6e0dc5d662a104a6d7
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/1574abb020203103ea629d677edd21c967fc0f4c b/fuzzers/corpora/commit_graph/1574abb020203103ea629d677edd21c967fc0f4c
new file mode 100644
index 0000000..b3da74c
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/1574abb020203103ea629d677edd21c967fc0f4c
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/169cc492ba94948a6206765436881a1a0c601780 b/fuzzers/corpora/commit_graph/169cc492ba94948a6206765436881a1a0c601780
new file mode 100644
index 0000000..2ce25f6
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/169cc492ba94948a6206765436881a1a0c601780
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/16a2130c1d75129f3bae3bf8f2c2de41fb3533c0 b/fuzzers/corpora/commit_graph/16a2130c1d75129f3bae3bf8f2c2de41fb3533c0
new file mode 100644
index 0000000..0b66385
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/16a2130c1d75129f3bae3bf8f2c2de41fb3533c0
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/16ba602eadfc9a3f74c0845394eda0de42b61571 b/fuzzers/corpora/commit_graph/16ba602eadfc9a3f74c0845394eda0de42b61571
new file mode 100644
index 0000000..c44bd06
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/16ba602eadfc9a3f74c0845394eda0de42b61571
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/17555fb2dfc444d171ba686667d72e388bd6c041 b/fuzzers/corpora/commit_graph/17555fb2dfc444d171ba686667d72e388bd6c041
new file mode 100644
index 0000000..3d00026
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/17555fb2dfc444d171ba686667d72e388bd6c041
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/1a10450d99c1e53d9b7f97b8014cb7fc01906ef2 b/fuzzers/corpora/commit_graph/1a10450d99c1e53d9b7f97b8014cb7fc01906ef2
new file mode 100644
index 0000000..f1fec60
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/1a10450d99c1e53d9b7f97b8014cb7fc01906ef2
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/1af670b5515231fc04b2be9038ee30a7e066b09b b/fuzzers/corpora/commit_graph/1af670b5515231fc04b2be9038ee30a7e066b09b
new file mode 100644
index 0000000..3bf7345
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/1af670b5515231fc04b2be9038ee30a7e066b09b
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/1b72cfa68259e3f3b3802906902a0a29368f86b5 b/fuzzers/corpora/commit_graph/1b72cfa68259e3f3b3802906902a0a29368f86b5
new file mode 100644
index 0000000..e6509d1
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/1b72cfa68259e3f3b3802906902a0a29368f86b5
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/1c62ac5d632aa9e449a4335b675941107d8825ae b/fuzzers/corpora/commit_graph/1c62ac5d632aa9e449a4335b675941107d8825ae
new file mode 100644
index 0000000..10c9e74
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/1c62ac5d632aa9e449a4335b675941107d8825ae
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/1d95b5db2f802011b33d10212a66fbe40827dfd4 b/fuzzers/corpora/commit_graph/1d95b5db2f802011b33d10212a66fbe40827dfd4
new file mode 100644
index 0000000..2353450
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/1d95b5db2f802011b33d10212a66fbe40827dfd4
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/1e068537ce1211a325aab42ae1263a109131c9f9 b/fuzzers/corpora/commit_graph/1e068537ce1211a325aab42ae1263a109131c9f9
new file mode 100644
index 0000000..0351738
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/1e068537ce1211a325aab42ae1263a109131c9f9
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/1e9c882c9d33304a5791ef6c98eee65e142bd7fd b/fuzzers/corpora/commit_graph/1e9c882c9d33304a5791ef6c98eee65e142bd7fd
new file mode 100644
index 0000000..d5b9da4
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/1e9c882c9d33304a5791ef6c98eee65e142bd7fd
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/1f54935df929403a29e77591c97f767d94871aea b/fuzzers/corpora/commit_graph/1f54935df929403a29e77591c97f767d94871aea
new file mode 100644
index 0000000..6f9b0a0
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/1f54935df929403a29e77591c97f767d94871aea
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/206015659641771bb0d668728c2fdc4209e65dda b/fuzzers/corpora/commit_graph/206015659641771bb0d668728c2fdc4209e65dda
new file mode 100644
index 0000000..086ab64
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/206015659641771bb0d668728c2fdc4209e65dda
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/2096493a2bcc2d15b7ae5bf3112fe49c39976ad8 b/fuzzers/corpora/commit_graph/2096493a2bcc2d15b7ae5bf3112fe49c39976ad8
new file mode 100644
index 0000000..7b33d38
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/2096493a2bcc2d15b7ae5bf3112fe49c39976ad8
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/209b74e08abe8c787b7c5ba81e51cb69c57ecded b/fuzzers/corpora/commit_graph/209b74e08abe8c787b7c5ba81e51cb69c57ecded
new file mode 100644
index 0000000..55dca76
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/209b74e08abe8c787b7c5ba81e51cb69c57ecded
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/21137876575fbca357fc0c96db1de73c6737e1ae b/fuzzers/corpora/commit_graph/21137876575fbca357fc0c96db1de73c6737e1ae
new file mode 100644
index 0000000..fea1ac1
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/21137876575fbca357fc0c96db1de73c6737e1ae
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/2143d9db9802f076c72a71184cd9d0cb4581e9e7 b/fuzzers/corpora/commit_graph/2143d9db9802f076c72a71184cd9d0cb4581e9e7
new file mode 100644
index 0000000..bacbe88
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/2143d9db9802f076c72a71184cd9d0cb4581e9e7
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/21a52a5282145407d951ac73c2ff27876783899d b/fuzzers/corpora/commit_graph/21a52a5282145407d951ac73c2ff27876783899d
new file mode 100644
index 0000000..1b5d2f0
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/21a52a5282145407d951ac73c2ff27876783899d
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/21d5c8c8ac3a09bcba5388c472df32795986a5cb b/fuzzers/corpora/commit_graph/21d5c8c8ac3a09bcba5388c472df32795986a5cb
new file mode 100644
index 0000000..b148c6f
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/21d5c8c8ac3a09bcba5388c472df32795986a5cb
@@ -0,0 +1 @@
+ÿúÿ¦ \ No newline at end of file
diff --git a/fuzzers/corpora/commit_graph/22170d1110a1c18009b7feb21a470681f55e85fb b/fuzzers/corpora/commit_graph/22170d1110a1c18009b7feb21a470681f55e85fb
new file mode 100644
index 0000000..6c16354
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/22170d1110a1c18009b7feb21a470681f55e85fb
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/22f55dff94785f24252d7a070f713840f59b0870 b/fuzzers/corpora/commit_graph/22f55dff94785f24252d7a070f713840f59b0870
new file mode 100644
index 0000000..b45c99a
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/22f55dff94785f24252d7a070f713840f59b0870
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/23d10ee9694e1c66bedc7060990f19a2ac3eaee3 b/fuzzers/corpora/commit_graph/23d10ee9694e1c66bedc7060990f19a2ac3eaee3
new file mode 100644
index 0000000..8790bfd
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/23d10ee9694e1c66bedc7060990f19a2ac3eaee3
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/2435430ca19502c3b0ec4987508d4a8fbdbc898c b/fuzzers/corpora/commit_graph/2435430ca19502c3b0ec4987508d4a8fbdbc898c
new file mode 100644
index 0000000..e1d58bd
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/2435430ca19502c3b0ec4987508d4a8fbdbc898c
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/244d2ea0c5c3117000b599cfab37680ba8f04513 b/fuzzers/corpora/commit_graph/244d2ea0c5c3117000b599cfab37680ba8f04513
new file mode 100644
index 0000000..5eedc85
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/244d2ea0c5c3117000b599cfab37680ba8f04513
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/248bf94143d150da2459cfdca099c30c6daff00a b/fuzzers/corpora/commit_graph/248bf94143d150da2459cfdca099c30c6daff00a
new file mode 100644
index 0000000..8b5f81b
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/248bf94143d150da2459cfdca099c30c6daff00a
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/25bc53498129bb3717671f00c355d2637a91c86a b/fuzzers/corpora/commit_graph/25bc53498129bb3717671f00c355d2637a91c86a
new file mode 100644
index 0000000..d86bb32
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/25bc53498129bb3717671f00c355d2637a91c86a
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/2614f60da2d7e291501397238366d27513bff773 b/fuzzers/corpora/commit_graph/2614f60da2d7e291501397238366d27513bff773
new file mode 100644
index 0000000..57cd70a
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/2614f60da2d7e291501397238366d27513bff773
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/2651b3d5a8b4616b1faa81dabe27ab2712a27561 b/fuzzers/corpora/commit_graph/2651b3d5a8b4616b1faa81dabe27ab2712a27561
new file mode 100644
index 0000000..5f838de
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/2651b3d5a8b4616b1faa81dabe27ab2712a27561
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/270257a2872b33dd13c4fd466cbc1ae67d613f9b b/fuzzers/corpora/commit_graph/270257a2872b33dd13c4fd466cbc1ae67d613f9b
new file mode 100644
index 0000000..3090496
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/270257a2872b33dd13c4fd466cbc1ae67d613f9b
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/2830c6244c74656f6c5649c8226953905a582a38 b/fuzzers/corpora/commit_graph/2830c6244c74656f6c5649c8226953905a582a38
new file mode 100644
index 0000000..895a8ef
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/2830c6244c74656f6c5649c8226953905a582a38
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/2889a85c07c20551ff0b97fc640e3c91b33aa4a1 b/fuzzers/corpora/commit_graph/2889a85c07c20551ff0b97fc640e3c91b33aa4a1
new file mode 100644
index 0000000..349ff15
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/2889a85c07c20551ff0b97fc640e3c91b33aa4a1
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/295ce43fdd56def8948d1ba2bfa7fdf0c47b5318 b/fuzzers/corpora/commit_graph/295ce43fdd56def8948d1ba2bfa7fdf0c47b5318
new file mode 100644
index 0000000..4a3ce80
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/295ce43fdd56def8948d1ba2bfa7fdf0c47b5318
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/296cbb94c4e68ab86972a174405308ee34d0c40f b/fuzzers/corpora/commit_graph/296cbb94c4e68ab86972a174405308ee34d0c40f
new file mode 100644
index 0000000..45c218e
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/296cbb94c4e68ab86972a174405308ee34d0c40f
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/2975adf222cad108ec90d8225fd655e30e3bf253 b/fuzzers/corpora/commit_graph/2975adf222cad108ec90d8225fd655e30e3bf253
new file mode 100644
index 0000000..6a16429
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/2975adf222cad108ec90d8225fd655e30e3bf253
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/29f5d27760c9254ab4db661a6cd0323dd11c34ca b/fuzzers/corpora/commit_graph/29f5d27760c9254ab4db661a6cd0323dd11c34ca
new file mode 100644
index 0000000..a9d81e1
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/29f5d27760c9254ab4db661a6cd0323dd11c34ca
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/2a359fb09eaad968e57d353453908027645873d1 b/fuzzers/corpora/commit_graph/2a359fb09eaad968e57d353453908027645873d1
new file mode 100644
index 0000000..a17910b
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/2a359fb09eaad968e57d353453908027645873d1
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/2a6b65a8d6c28febaa081d220a4433f8366d02bc b/fuzzers/corpora/commit_graph/2a6b65a8d6c28febaa081d220a4433f8366d02bc
new file mode 100644
index 0000000..597f363
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/2a6b65a8d6c28febaa081d220a4433f8366d02bc
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/2b14dcade4d0919b0a17830fe353738015f492a6 b/fuzzers/corpora/commit_graph/2b14dcade4d0919b0a17830fe353738015f492a6
new file mode 100644
index 0000000..33f4e6e
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/2b14dcade4d0919b0a17830fe353738015f492a6
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/2b298a13abbd9829e965424a1486baa13d4166c4 b/fuzzers/corpora/commit_graph/2b298a13abbd9829e965424a1486baa13d4166c4
new file mode 100644
index 0000000..837e45f
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/2b298a13abbd9829e965424a1486baa13d4166c4
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/2b44d8cd8e70e25172b4c740ebe38ef411c965b3 b/fuzzers/corpora/commit_graph/2b44d8cd8e70e25172b4c740ebe38ef411c965b3
new file mode 100644
index 0000000..8059ce4
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/2b44d8cd8e70e25172b4c740ebe38ef411c965b3
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/2b590c4e61fdfcf21c017b29440747a1894b1534 b/fuzzers/corpora/commit_graph/2b590c4e61fdfcf21c017b29440747a1894b1534
new file mode 100644
index 0000000..c3ea729
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/2b590c4e61fdfcf21c017b29440747a1894b1534
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/2becb18a971ae30e1a8f6680982fd7305708caa0 b/fuzzers/corpora/commit_graph/2becb18a971ae30e1a8f6680982fd7305708caa0
new file mode 100644
index 0000000..4b133a7
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/2becb18a971ae30e1a8f6680982fd7305708caa0
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/2bf78b02099a1fe4ce50d065254e843ca55e280f b/fuzzers/corpora/commit_graph/2bf78b02099a1fe4ce50d065254e843ca55e280f
new file mode 100644
index 0000000..9b459e2
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/2bf78b02099a1fe4ce50d065254e843ca55e280f
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/2c1541ecd01aa7b9e99bccfe9804198b3e79f118 b/fuzzers/corpora/commit_graph/2c1541ecd01aa7b9e99bccfe9804198b3e79f118
new file mode 100644
index 0000000..92daa0a
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/2c1541ecd01aa7b9e99bccfe9804198b3e79f118
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/2c6798057af5894c27631ff63e845fe1e4bdc9ee b/fuzzers/corpora/commit_graph/2c6798057af5894c27631ff63e845fe1e4bdc9ee
new file mode 100644
index 0000000..06a7d31
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/2c6798057af5894c27631ff63e845fe1e4bdc9ee
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/2cf7eb7fe489e5acd64df755e820c871784c2ba1 b/fuzzers/corpora/commit_graph/2cf7eb7fe489e5acd64df755e820c871784c2ba1
new file mode 100644
index 0000000..5614c74
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/2cf7eb7fe489e5acd64df755e820c871784c2ba1
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/2d49ba35ca404baa0d593925f36a81ce53943c8d b/fuzzers/corpora/commit_graph/2d49ba35ca404baa0d593925f36a81ce53943c8d
new file mode 100644
index 0000000..36d6b85
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/2d49ba35ca404baa0d593925f36a81ce53943c8d
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/2d507d42ca43ffc2f3c8892826e1db74144ec096 b/fuzzers/corpora/commit_graph/2d507d42ca43ffc2f3c8892826e1db74144ec096
new file mode 100644
index 0000000..35c28f4
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/2d507d42ca43ffc2f3c8892826e1db74144ec096
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/2e4da693e3e336d2b1a40311a7ccf94def035b6b b/fuzzers/corpora/commit_graph/2e4da693e3e336d2b1a40311a7ccf94def035b6b
new file mode 100644
index 0000000..42727e9
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/2e4da693e3e336d2b1a40311a7ccf94def035b6b
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/2e71ff86128b5618f0f067c407a76ff645ae2019 b/fuzzers/corpora/commit_graph/2e71ff86128b5618f0f067c407a76ff645ae2019
new file mode 100644
index 0000000..32f02fc
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/2e71ff86128b5618f0f067c407a76ff645ae2019
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/2eb777c6d7e6ee9bd7a44e37372595043aad596b b/fuzzers/corpora/commit_graph/2eb777c6d7e6ee9bd7a44e37372595043aad596b
new file mode 100644
index 0000000..7203c91
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/2eb777c6d7e6ee9bd7a44e37372595043aad596b
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/2ec3ebffba165b9dd49e755a9e77e23aed796628 b/fuzzers/corpora/commit_graph/2ec3ebffba165b9dd49e755a9e77e23aed796628
new file mode 100644
index 0000000..d4018de
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/2ec3ebffba165b9dd49e755a9e77e23aed796628
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/302703e3b0d74219868aca39ee7593944c0b2400 b/fuzzers/corpora/commit_graph/302703e3b0d74219868aca39ee7593944c0b2400
new file mode 100644
index 0000000..6b692e1
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/302703e3b0d74219868aca39ee7593944c0b2400
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/3048c6908dc3176707fa8bcb0196824e3358357a b/fuzzers/corpora/commit_graph/3048c6908dc3176707fa8bcb0196824e3358357a
new file mode 100644
index 0000000..c58805f
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/3048c6908dc3176707fa8bcb0196824e3358357a
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/30616cb39d3ad6060324fada03709d611ad28d5c b/fuzzers/corpora/commit_graph/30616cb39d3ad6060324fada03709d611ad28d5c
new file mode 100644
index 0000000..1fd655e
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/30616cb39d3ad6060324fada03709d611ad28d5c
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/306beadd9b3135a00037323760eb5377c88a403e b/fuzzers/corpora/commit_graph/306beadd9b3135a00037323760eb5377c88a403e
new file mode 100644
index 0000000..86c8672
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/306beadd9b3135a00037323760eb5377c88a403e
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/31464a6fbad023923a7e4700fc11564e811bcbd2 b/fuzzers/corpora/commit_graph/31464a6fbad023923a7e4700fc11564e811bcbd2
new file mode 100644
index 0000000..102de5c
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/31464a6fbad023923a7e4700fc11564e811bcbd2
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/317f4bcfecf066961ef1982d551cd14e63c9f008 b/fuzzers/corpora/commit_graph/317f4bcfecf066961ef1982d551cd14e63c9f008
new file mode 100644
index 0000000..5c2a5d7
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/317f4bcfecf066961ef1982d551cd14e63c9f008
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/31b2248faaabbec69a06098c8cb0f69c5d0aa208 b/fuzzers/corpora/commit_graph/31b2248faaabbec69a06098c8cb0f69c5d0aa208
new file mode 100644
index 0000000..555c7b1
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/31b2248faaabbec69a06098c8cb0f69c5d0aa208
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/31d1c3d1147385d58dbe6f82898a5523320fbcac b/fuzzers/corpora/commit_graph/31d1c3d1147385d58dbe6f82898a5523320fbcac
new file mode 100644
index 0000000..1c5ef07
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/31d1c3d1147385d58dbe6f82898a5523320fbcac
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/32c9bc1616a78a230a3724abc02150db1cc40aa0 b/fuzzers/corpora/commit_graph/32c9bc1616a78a230a3724abc02150db1cc40aa0
new file mode 100644
index 0000000..cfc45a1
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/32c9bc1616a78a230a3724abc02150db1cc40aa0
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/331e2866416b091252f0299e98d32cfb29237029 b/fuzzers/corpora/commit_graph/331e2866416b091252f0299e98d32cfb29237029
new file mode 100644
index 0000000..241e719
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/331e2866416b091252f0299e98d32cfb29237029
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/331eb3876dd2f3f0bd51f380ac431d86d6e3bb5e b/fuzzers/corpora/commit_graph/331eb3876dd2f3f0bd51f380ac431d86d6e3bb5e
new file mode 100644
index 0000000..a52780f
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/331eb3876dd2f3f0bd51f380ac431d86d6e3bb5e
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/346bd6eaeadeafcb840ff9441614b309330db63e b/fuzzers/corpora/commit_graph/346bd6eaeadeafcb840ff9441614b309330db63e
new file mode 100644
index 0000000..50b7f93
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/346bd6eaeadeafcb840ff9441614b309330db63e
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/349931f447981f21476481448576e805c093a25b b/fuzzers/corpora/commit_graph/349931f447981f21476481448576e805c093a25b
new file mode 100644
index 0000000..ae5b758
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/349931f447981f21476481448576e805c093a25b
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/34a2da1e9adaac1b4be1d40b1ece81fe00643d49 b/fuzzers/corpora/commit_graph/34a2da1e9adaac1b4be1d40b1ece81fe00643d49
new file mode 100644
index 0000000..f5d660f
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/34a2da1e9adaac1b4be1d40b1ece81fe00643d49
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/34bb8f475e7384a8a39618fd15fdc5fb1b12c1a1 b/fuzzers/corpora/commit_graph/34bb8f475e7384a8a39618fd15fdc5fb1b12c1a1
new file mode 100644
index 0000000..f940f66
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/34bb8f475e7384a8a39618fd15fdc5fb1b12c1a1
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/351a036c6eb95db9364706b861f7e75ad26194e8 b/fuzzers/corpora/commit_graph/351a036c6eb95db9364706b861f7e75ad26194e8
new file mode 100644
index 0000000..197bee1
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/351a036c6eb95db9364706b861f7e75ad26194e8
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/355452c1da8e7689d816d67cdde040b5df7eabd7 b/fuzzers/corpora/commit_graph/355452c1da8e7689d816d67cdde040b5df7eabd7
new file mode 100644
index 0000000..f135872
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/355452c1da8e7689d816d67cdde040b5df7eabd7
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/35c157ad2b100b4f334cddcf3dea6ef2d85462be b/fuzzers/corpora/commit_graph/35c157ad2b100b4f334cddcf3dea6ef2d85462be
new file mode 100644
index 0000000..7d73ba7
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/35c157ad2b100b4f334cddcf3dea6ef2d85462be
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/36a81a45eabfcf53e1ae0361aa234791e2fdb750 b/fuzzers/corpora/commit_graph/36a81a45eabfcf53e1ae0361aa234791e2fdb750
new file mode 100644
index 0000000..fc1b8dd
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/36a81a45eabfcf53e1ae0361aa234791e2fdb750
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/36ee20f6dbeb3a34e91eafbbe2e379f9ac6cfa43 b/fuzzers/corpora/commit_graph/36ee20f6dbeb3a34e91eafbbe2e379f9ac6cfa43
new file mode 100644
index 0000000..769017a
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/36ee20f6dbeb3a34e91eafbbe2e379f9ac6cfa43
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/377627c19bcac6adc880202048a9eac07b5417d4 b/fuzzers/corpora/commit_graph/377627c19bcac6adc880202048a9eac07b5417d4
new file mode 100644
index 0000000..d4ef66b
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/377627c19bcac6adc880202048a9eac07b5417d4
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/38747e7c8bec2f9c923739d50ba54ff88ba6503f b/fuzzers/corpora/commit_graph/38747e7c8bec2f9c923739d50ba54ff88ba6503f
new file mode 100644
index 0000000..6d94b57
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/38747e7c8bec2f9c923739d50ba54ff88ba6503f
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/3945843a6fab2ec71030f09b237c125b97cd3ea5 b/fuzzers/corpora/commit_graph/3945843a6fab2ec71030f09b237c125b97cd3ea5
new file mode 100644
index 0000000..76191ca
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/3945843a6fab2ec71030f09b237c125b97cd3ea5
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/396321d39b82ffaccbc64115117df7e822b0f515 b/fuzzers/corpora/commit_graph/396321d39b82ffaccbc64115117df7e822b0f515
new file mode 100644
index 0000000..74715a9
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/396321d39b82ffaccbc64115117df7e822b0f515
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/396e78eb9b54e2cefb52cd76a22137c8abd6cbcf b/fuzzers/corpora/commit_graph/396e78eb9b54e2cefb52cd76a22137c8abd6cbcf
new file mode 100644
index 0000000..b5648c4
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/396e78eb9b54e2cefb52cd76a22137c8abd6cbcf
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/39c1ab66035adc104cd06a6d98b77668172d21af b/fuzzers/corpora/commit_graph/39c1ab66035adc104cd06a6d98b77668172d21af
new file mode 100644
index 0000000..f9649f7
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/39c1ab66035adc104cd06a6d98b77668172d21af
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/3a1078c35f5401ce09b5ba921fc348dde37530bb b/fuzzers/corpora/commit_graph/3a1078c35f5401ce09b5ba921fc348dde37530bb
new file mode 100644
index 0000000..7e519f8
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/3a1078c35f5401ce09b5ba921fc348dde37530bb
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/3aa3d8f40392d1c863d23799b8ec0aedc7191302 b/fuzzers/corpora/commit_graph/3aa3d8f40392d1c863d23799b8ec0aedc7191302
new file mode 100644
index 0000000..3cbeaaf
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/3aa3d8f40392d1c863d23799b8ec0aedc7191302
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/3b08c505601271cb92345ec7f0ff0b28daf90a9c b/fuzzers/corpora/commit_graph/3b08c505601271cb92345ec7f0ff0b28daf90a9c
new file mode 100644
index 0000000..69b9bab
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/3b08c505601271cb92345ec7f0ff0b28daf90a9c
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/3b41702587be45f678b36823ad2f7e5002337dc4 b/fuzzers/corpora/commit_graph/3b41702587be45f678b36823ad2f7e5002337dc4
new file mode 100644
index 0000000..29069ab
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/3b41702587be45f678b36823ad2f7e5002337dc4
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/3b69108cc919aba0248f9b864d4e71c5f6d1931e b/fuzzers/corpora/commit_graph/3b69108cc919aba0248f9b864d4e71c5f6d1931e
new file mode 100644
index 0000000..207df24
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/3b69108cc919aba0248f9b864d4e71c5f6d1931e
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/3b90507501bb3bcfe0094f9c92cc2869f1a7dda5 b/fuzzers/corpora/commit_graph/3b90507501bb3bcfe0094f9c92cc2869f1a7dda5
new file mode 100644
index 0000000..5727232
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/3b90507501bb3bcfe0094f9c92cc2869f1a7dda5
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/3bc7fe44c3a1464dd35a4d22b482f46cdeda0405 b/fuzzers/corpora/commit_graph/3bc7fe44c3a1464dd35a4d22b482f46cdeda0405
new file mode 100644
index 0000000..e1a8bd3
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/3bc7fe44c3a1464dd35a4d22b482f46cdeda0405
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/3ce99994986efb6df3f3568423e0077b53c7ef78 b/fuzzers/corpora/commit_graph/3ce99994986efb6df3f3568423e0077b53c7ef78
new file mode 100644
index 0000000..21f9ab8
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/3ce99994986efb6df3f3568423e0077b53c7ef78
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/3d6cb3ba21181c9f0ab08b2608eab773f36773f2 b/fuzzers/corpora/commit_graph/3d6cb3ba21181c9f0ab08b2608eab773f36773f2
new file mode 100644
index 0000000..548a129
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/3d6cb3ba21181c9f0ab08b2608eab773f36773f2
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/3d8ec41450b943d5dea73fb1e393960b03d7c3b9 b/fuzzers/corpora/commit_graph/3d8ec41450b943d5dea73fb1e393960b03d7c3b9
new file mode 100644
index 0000000..150ed2c
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/3d8ec41450b943d5dea73fb1e393960b03d7c3b9
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/3e29e8baaac0f6c7e4cf3d5adca2ab3a2c491ac7 b/fuzzers/corpora/commit_graph/3e29e8baaac0f6c7e4cf3d5adca2ab3a2c491ac7
new file mode 100644
index 0000000..bc9597d
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/3e29e8baaac0f6c7e4cf3d5adca2ab3a2c491ac7
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/3e9469b3c68ba334671aacda7a7669b0e97b74d6 b/fuzzers/corpora/commit_graph/3e9469b3c68ba334671aacda7a7669b0e97b74d6
new file mode 100644
index 0000000..cb20df0
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/3e9469b3c68ba334671aacda7a7669b0e97b74d6
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/3eeda3bfa7abef69911c94520c009a08c49b9942 b/fuzzers/corpora/commit_graph/3eeda3bfa7abef69911c94520c009a08c49b9942
new file mode 100644
index 0000000..dbf559f
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/3eeda3bfa7abef69911c94520c009a08c49b9942
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/3f0f5021016451b57f673d0603cd9e4830c2198d b/fuzzers/corpora/commit_graph/3f0f5021016451b57f673d0603cd9e4830c2198d
new file mode 100644
index 0000000..2f4c523
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/3f0f5021016451b57f673d0603cd9e4830c2198d
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/3f46540fbd94bf0337c1d0d7437ec992a3568f09 b/fuzzers/corpora/commit_graph/3f46540fbd94bf0337c1d0d7437ec992a3568f09
new file mode 100644
index 0000000..7fbf350
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/3f46540fbd94bf0337c1d0d7437ec992a3568f09
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/402d9c25d5833d42630882ab5c57833266bef785 b/fuzzers/corpora/commit_graph/402d9c25d5833d42630882ab5c57833266bef785
new file mode 100644
index 0000000..6600281
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/402d9c25d5833d42630882ab5c57833266bef785
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/4048bb3c26d67c345630ff9e86db551a3add6549 b/fuzzers/corpora/commit_graph/4048bb3c26d67c345630ff9e86db551a3add6549
new file mode 100644
index 0000000..a07e195
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/4048bb3c26d67c345630ff9e86db551a3add6549
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/40792f23c1281842dab671e8b213fc408d1ec39f b/fuzzers/corpora/commit_graph/40792f23c1281842dab671e8b213fc408d1ec39f
new file mode 100644
index 0000000..9a0f9c2
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/40792f23c1281842dab671e8b213fc408d1ec39f
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/41cd0b5d9a9540947b7b1841a55e4c11bd4346a2 b/fuzzers/corpora/commit_graph/41cd0b5d9a9540947b7b1841a55e4c11bd4346a2
new file mode 100644
index 0000000..a1b3a07
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/41cd0b5d9a9540947b7b1841a55e4c11bd4346a2
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/41d86e5ea3df4a0de60d42aeb16e2a5599aedeae b/fuzzers/corpora/commit_graph/41d86e5ea3df4a0de60d42aeb16e2a5599aedeae
new file mode 100644
index 0000000..d749cf6
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/41d86e5ea3df4a0de60d42aeb16e2a5599aedeae
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/42b4e5430b2b1b17a361067fb9dd33ab74e52232 b/fuzzers/corpora/commit_graph/42b4e5430b2b1b17a361067fb9dd33ab74e52232
new file mode 100644
index 0000000..6adf001
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/42b4e5430b2b1b17a361067fb9dd33ab74e52232
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/42ef1c9d234b90acaf1651d930fc52d5f8f158f2 b/fuzzers/corpora/commit_graph/42ef1c9d234b90acaf1651d930fc52d5f8f158f2
new file mode 100644
index 0000000..0514ae8
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/42ef1c9d234b90acaf1651d930fc52d5f8f158f2
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/4570c8ff26d7f31afe73b3d9a35a29bc1274d68a b/fuzzers/corpora/commit_graph/4570c8ff26d7f31afe73b3d9a35a29bc1274d68a
new file mode 100644
index 0000000..834d62b
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/4570c8ff26d7f31afe73b3d9a35a29bc1274d68a
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/45cf4751a5929930a7c30ec10134434b9ee13c3d b/fuzzers/corpora/commit_graph/45cf4751a5929930a7c30ec10134434b9ee13c3d
new file mode 100644
index 0000000..b761279
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/45cf4751a5929930a7c30ec10134434b9ee13c3d
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/46e9d351dd5bb71f7d4d8f15b3fad312c781452e b/fuzzers/corpora/commit_graph/46e9d351dd5bb71f7d4d8f15b3fad312c781452e
new file mode 100644
index 0000000..ce26235
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/46e9d351dd5bb71f7d4d8f15b3fad312c781452e
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/472421633b984556b96bc20f1fcf7a98c25736f3 b/fuzzers/corpora/commit_graph/472421633b984556b96bc20f1fcf7a98c25736f3
new file mode 100644
index 0000000..4a2faa1
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/472421633b984556b96bc20f1fcf7a98c25736f3
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/47f35b91699caee098cacdde0161ffab21bdfc57 b/fuzzers/corpora/commit_graph/47f35b91699caee098cacdde0161ffab21bdfc57
new file mode 100644
index 0000000..a0f24ef
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/47f35b91699caee098cacdde0161ffab21bdfc57
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/48b9da327218f9409287687a43b7eead4789a588 b/fuzzers/corpora/commit_graph/48b9da327218f9409287687a43b7eead4789a588
new file mode 100644
index 0000000..c6f2b79
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/48b9da327218f9409287687a43b7eead4789a588
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/48d14fca326d5d591d18d34c2821a457277819a2 b/fuzzers/corpora/commit_graph/48d14fca326d5d591d18d34c2821a457277819a2
new file mode 100644
index 0000000..d223341
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/48d14fca326d5d591d18d34c2821a457277819a2
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/48f3a33e2a027f5735d0a333ec4acd5a2aa57118 b/fuzzers/corpora/commit_graph/48f3a33e2a027f5735d0a333ec4acd5a2aa57118
new file mode 100644
index 0000000..0604edb
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/48f3a33e2a027f5735d0a333ec4acd5a2aa57118
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/49e0eee24eab094a9c62f6b37b6ba01f8aece4e4 b/fuzzers/corpora/commit_graph/49e0eee24eab094a9c62f6b37b6ba01f8aece4e4
new file mode 100644
index 0000000..78f9425
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/49e0eee24eab094a9c62f6b37b6ba01f8aece4e4
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/4b45bcb707d2a0bc23b415e9bc3d7eb1f7f0e188 b/fuzzers/corpora/commit_graph/4b45bcb707d2a0bc23b415e9bc3d7eb1f7f0e188
new file mode 100644
index 0000000..3a1fdd7
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/4b45bcb707d2a0bc23b415e9bc3d7eb1f7f0e188
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/4c428300fe4866fe81cff02ad4bc14b6848f7f73 b/fuzzers/corpora/commit_graph/4c428300fe4866fe81cff02ad4bc14b6848f7f73
new file mode 100644
index 0000000..d7f09e3
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/4c428300fe4866fe81cff02ad4bc14b6848f7f73
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/4d69c567df2e858c5f248b3fc8e4a9c04f02481c b/fuzzers/corpora/commit_graph/4d69c567df2e858c5f248b3fc8e4a9c04f02481c
new file mode 100644
index 0000000..26ba525
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/4d69c567df2e858c5f248b3fc8e4a9c04f02481c
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/4d88b6c9b513d5db2e07313a39b43d112d3d4562 b/fuzzers/corpora/commit_graph/4d88b6c9b513d5db2e07313a39b43d112d3d4562
new file mode 100644
index 0000000..bfe64c9
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/4d88b6c9b513d5db2e07313a39b43d112d3d4562
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/4da73370cf854ef8bd08c7f79b92a187cdbff278 b/fuzzers/corpora/commit_graph/4da73370cf854ef8bd08c7f79b92a187cdbff278
new file mode 100644
index 0000000..da1801e
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/4da73370cf854ef8bd08c7f79b92a187cdbff278
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/4e4b2827351bbfd414b718052a8f950a9e3eb7ee b/fuzzers/corpora/commit_graph/4e4b2827351bbfd414b718052a8f950a9e3eb7ee
new file mode 100644
index 0000000..77d2e78
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/4e4b2827351bbfd414b718052a8f950a9e3eb7ee
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/4ed43f7d3c0305461edcbc86f62e0c6ad56df01e b/fuzzers/corpora/commit_graph/4ed43f7d3c0305461edcbc86f62e0c6ad56df01e
new file mode 100644
index 0000000..cfef7a1
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/4ed43f7d3c0305461edcbc86f62e0c6ad56df01e
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/4f011529809e88205421fa8ce39dcc025293bcb8 b/fuzzers/corpora/commit_graph/4f011529809e88205421fa8ce39dcc025293bcb8
new file mode 100644
index 0000000..2331acf
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/4f011529809e88205421fa8ce39dcc025293bcb8
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/4f1928b6376369ab6acf8a282284366cc3bf71ef b/fuzzers/corpora/commit_graph/4f1928b6376369ab6acf8a282284366cc3bf71ef
new file mode 100644
index 0000000..ad3d173
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/4f1928b6376369ab6acf8a282284366cc3bf71ef
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/4f669eca3416c44f0d003ef2720d03e697e2230e b/fuzzers/corpora/commit_graph/4f669eca3416c44f0d003ef2720d03e697e2230e
new file mode 100644
index 0000000..6a143b3
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/4f669eca3416c44f0d003ef2720d03e697e2230e
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/4f750f24ecb5080bea2845061cfd3ce4529d30ee b/fuzzers/corpora/commit_graph/4f750f24ecb5080bea2845061cfd3ce4529d30ee
new file mode 100644
index 0000000..ea36bdc
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/4f750f24ecb5080bea2845061cfd3ce4529d30ee
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/4fab9bb2bacf562e65f4a8681c429e6ea92aaed7 b/fuzzers/corpora/commit_graph/4fab9bb2bacf562e65f4a8681c429e6ea92aaed7
new file mode 100644
index 0000000..d00a794
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/4fab9bb2bacf562e65f4a8681c429e6ea92aaed7
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/4fd757c7251c17413b3005fb38aee0fd029d89ec b/fuzzers/corpora/commit_graph/4fd757c7251c17413b3005fb38aee0fd029d89ec
new file mode 100644
index 0000000..4f4db7d
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/4fd757c7251c17413b3005fb38aee0fd029d89ec
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/506092de91dcf93254cdd5ad9e02a953a38099ea b/fuzzers/corpora/commit_graph/506092de91dcf93254cdd5ad9e02a953a38099ea
new file mode 100644
index 0000000..64c5405
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/506092de91dcf93254cdd5ad9e02a953a38099ea
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/50e934fb52d9bc5cd2a531adced1cad7f102a112 b/fuzzers/corpora/commit_graph/50e934fb52d9bc5cd2a531adced1cad7f102a112
new file mode 100644
index 0000000..b630179
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/50e934fb52d9bc5cd2a531adced1cad7f102a112
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/512e49a9e789656964988950009e6534907e6317 b/fuzzers/corpora/commit_graph/512e49a9e789656964988950009e6534907e6317
new file mode 100644
index 0000000..7cf8e31
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/512e49a9e789656964988950009e6534907e6317
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/51404149f1ea30ee6959fafe81a52acabed97e9e b/fuzzers/corpora/commit_graph/51404149f1ea30ee6959fafe81a52acabed97e9e
new file mode 100644
index 0000000..394e4bf
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/51404149f1ea30ee6959fafe81a52acabed97e9e
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/5150f8a67399ee16178a2b08198cf91a90c0e53e b/fuzzers/corpora/commit_graph/5150f8a67399ee16178a2b08198cf91a90c0e53e
new file mode 100644
index 0000000..c0867a0
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/5150f8a67399ee16178a2b08198cf91a90c0e53e
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/51a1fd23dfe5a8062cd4601d235509247f3bc2dc b/fuzzers/corpora/commit_graph/51a1fd23dfe5a8062cd4601d235509247f3bc2dc
new file mode 100644
index 0000000..c448928
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/51a1fd23dfe5a8062cd4601d235509247f3bc2dc
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/51a963486f041a60c422f0dd6da3b69c52f12fb7 b/fuzzers/corpora/commit_graph/51a963486f041a60c422f0dd6da3b69c52f12fb7
new file mode 100644
index 0000000..f8a1c56
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/51a963486f041a60c422f0dd6da3b69c52f12fb7
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/51fbf57a2a35ec33164838fa254fe605a3c868e9 b/fuzzers/corpora/commit_graph/51fbf57a2a35ec33164838fa254fe605a3c868e9
new file mode 100644
index 0000000..d0c77cd
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/51fbf57a2a35ec33164838fa254fe605a3c868e9
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/53068b9f9cb54bb52d076e9602ccd55f169ef39a b/fuzzers/corpora/commit_graph/53068b9f9cb54bb52d076e9602ccd55f169ef39a
new file mode 100644
index 0000000..bed0af6
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/53068b9f9cb54bb52d076e9602ccd55f169ef39a
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/5314619e15fa5ee67df44481b8213a53786d39c5 b/fuzzers/corpora/commit_graph/5314619e15fa5ee67df44481b8213a53786d39c5
new file mode 100644
index 0000000..a00e14d
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/5314619e15fa5ee67df44481b8213a53786d39c5
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/533f5f00275968129846522fe01e2819746272eb b/fuzzers/corpora/commit_graph/533f5f00275968129846522fe01e2819746272eb
new file mode 100644
index 0000000..0830b01
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/533f5f00275968129846522fe01e2819746272eb
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/53a62799135c282435a17e032deda03eaf9daf0f b/fuzzers/corpora/commit_graph/53a62799135c282435a17e032deda03eaf9daf0f
new file mode 100644
index 0000000..f6bfddb
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/53a62799135c282435a17e032deda03eaf9daf0f
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/53c9d5cd849977e523d92dd2d639e9b0e721be50 b/fuzzers/corpora/commit_graph/53c9d5cd849977e523d92dd2d639e9b0e721be50
new file mode 100644
index 0000000..ed30c5e
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/53c9d5cd849977e523d92dd2d639e9b0e721be50
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/54767a0bb3b96d39f5b2004ce3f274465f1a927e b/fuzzers/corpora/commit_graph/54767a0bb3b96d39f5b2004ce3f274465f1a927e
new file mode 100644
index 0000000..fd9a0ac
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/54767a0bb3b96d39f5b2004ce3f274465f1a927e
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/548de37dbe6a3829b73d976996ec9838cf608554 b/fuzzers/corpora/commit_graph/548de37dbe6a3829b73d976996ec9838cf608554
new file mode 100644
index 0000000..89772e2
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/548de37dbe6a3829b73d976996ec9838cf608554
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/5522cefa54b798ea4aba8ef2a42ad248a7fb02ee b/fuzzers/corpora/commit_graph/5522cefa54b798ea4aba8ef2a42ad248a7fb02ee
new file mode 100644
index 0000000..6a4da78
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/5522cefa54b798ea4aba8ef2a42ad248a7fb02ee
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/554fab3eef5d8709f06d1d4319efe5c0c437421b b/fuzzers/corpora/commit_graph/554fab3eef5d8709f06d1d4319efe5c0c437421b
new file mode 100644
index 0000000..7a54bd0
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/554fab3eef5d8709f06d1d4319efe5c0c437421b
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/567fe73919dae39b0bcb78b03d655643a71714a8 b/fuzzers/corpora/commit_graph/567fe73919dae39b0bcb78b03d655643a71714a8
new file mode 100644
index 0000000..56b1e14
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/567fe73919dae39b0bcb78b03d655643a71714a8
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/5717a281aa722ee4a32dfa1cc72fc5d6081f6755 b/fuzzers/corpora/commit_graph/5717a281aa722ee4a32dfa1cc72fc5d6081f6755
new file mode 100644
index 0000000..77f0e51
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/5717a281aa722ee4a32dfa1cc72fc5d6081f6755
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/577d814e0be43df9321c5b27119c398bd00a00c5 b/fuzzers/corpora/commit_graph/577d814e0be43df9321c5b27119c398bd00a00c5
new file mode 100644
index 0000000..c892728
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/577d814e0be43df9321c5b27119c398bd00a00c5
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/58680611707c6188f9f067f8747b699cd2fe82d3 b/fuzzers/corpora/commit_graph/58680611707c6188f9f067f8747b699cd2fe82d3
new file mode 100644
index 0000000..81efaf3
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/58680611707c6188f9f067f8747b699cd2fe82d3
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/5915b7f91dd43ec37a4718061c90cbec2686b916 b/fuzzers/corpora/commit_graph/5915b7f91dd43ec37a4718061c90cbec2686b916
new file mode 100644
index 0000000..f22d2aa
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/5915b7f91dd43ec37a4718061c90cbec2686b916
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/599516e368ff621dd06d8450837350f4e9558c38 b/fuzzers/corpora/commit_graph/599516e368ff621dd06d8450837350f4e9558c38
new file mode 100644
index 0000000..6aff8ee
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/599516e368ff621dd06d8450837350f4e9558c38
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/5a2d01d141e4d523e718c30e20cb07c3ad98f33d b/fuzzers/corpora/commit_graph/5a2d01d141e4d523e718c30e20cb07c3ad98f33d
new file mode 100644
index 0000000..fb20c58
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/5a2d01d141e4d523e718c30e20cb07c3ad98f33d
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/5a9803ef8cd88d1e8f1d6e5920b8afd170cafb11 b/fuzzers/corpora/commit_graph/5a9803ef8cd88d1e8f1d6e5920b8afd170cafb11
new file mode 100644
index 0000000..38c2c08
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/5a9803ef8cd88d1e8f1d6e5920b8afd170cafb11
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/5ba93c9db0cff93f52b521d7420e43f6eda2784f b/fuzzers/corpora/commit_graph/5ba93c9db0cff93f52b521d7420e43f6eda2784f
new file mode 100644
index 0000000..f76dd23
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/5ba93c9db0cff93f52b521d7420e43f6eda2784f
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/5bf0ca772092e6fa34b6822f61a1b1c3d7f2c6e3 b/fuzzers/corpora/commit_graph/5bf0ca772092e6fa34b6822f61a1b1c3d7f2c6e3
new file mode 100644
index 0000000..06dd1e1
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/5bf0ca772092e6fa34b6822f61a1b1c3d7f2c6e3
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/5cfbfb3e12b629dc9f74baf0a8741345ec288795 b/fuzzers/corpora/commit_graph/5cfbfb3e12b629dc9f74baf0a8741345ec288795
new file mode 100644
index 0000000..73e257d
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/5cfbfb3e12b629dc9f74baf0a8741345ec288795
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/5d8cc97b739c39820b761b6551d34dd647da6816 b/fuzzers/corpora/commit_graph/5d8cc97b739c39820b761b6551d34dd647da6816
new file mode 100644
index 0000000..7a241f3
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/5d8cc97b739c39820b761b6551d34dd647da6816
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/5dcbb3e1c2fc9a191dd3f3443b86f6bc38c39e37 b/fuzzers/corpora/commit_graph/5dcbb3e1c2fc9a191dd3f3443b86f6bc38c39e37
new file mode 100644
index 0000000..04a1639
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/5dcbb3e1c2fc9a191dd3f3443b86f6bc38c39e37
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/5ec17d081aef9872f746e88ad8b03553719f9c36 b/fuzzers/corpora/commit_graph/5ec17d081aef9872f746e88ad8b03553719f9c36
new file mode 100644
index 0000000..84fa131
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/5ec17d081aef9872f746e88ad8b03553719f9c36
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/5f88e3ba60c11be25c47a842763d8870d23cc7f2 b/fuzzers/corpora/commit_graph/5f88e3ba60c11be25c47a842763d8870d23cc7f2
new file mode 100644
index 0000000..3669387
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/5f88e3ba60c11be25c47a842763d8870d23cc7f2
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/6045e4d2bf85013c78a32e71b014ba3d4a4b7c61 b/fuzzers/corpora/commit_graph/6045e4d2bf85013c78a32e71b014ba3d4a4b7c61
new file mode 100644
index 0000000..5ebec5a
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/6045e4d2bf85013c78a32e71b014ba3d4a4b7c61
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/615c7ba7ffbce955ffd964682e2a0f7ef3c767e4 b/fuzzers/corpora/commit_graph/615c7ba7ffbce955ffd964682e2a0f7ef3c767e4
new file mode 100644
index 0000000..8360672
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/615c7ba7ffbce955ffd964682e2a0f7ef3c767e4
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/6189f29cbbe88ac6cb32fdefecda1bd6194332a6 b/fuzzers/corpora/commit_graph/6189f29cbbe88ac6cb32fdefecda1bd6194332a6
new file mode 100644
index 0000000..40ec5b1
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/6189f29cbbe88ac6cb32fdefecda1bd6194332a6
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/627224cb8484c62992dcbc4cdebdbfa48a3c021a b/fuzzers/corpora/commit_graph/627224cb8484c62992dcbc4cdebdbfa48a3c021a
new file mode 100644
index 0000000..f73b59d
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/627224cb8484c62992dcbc4cdebdbfa48a3c021a
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/629fff0962d298a7283a3d1e1d1b940dfef9b315 b/fuzzers/corpora/commit_graph/629fff0962d298a7283a3d1e1d1b940dfef9b315
new file mode 100644
index 0000000..1c0c2c4
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/629fff0962d298a7283a3d1e1d1b940dfef9b315
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/6322594cff2a99d0abb1139e6a43b06df76d539a b/fuzzers/corpora/commit_graph/6322594cff2a99d0abb1139e6a43b06df76d539a
new file mode 100644
index 0000000..4dabbce
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/6322594cff2a99d0abb1139e6a43b06df76d539a
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/63de5e8e042222d53bf05640c87da376aefb76cc b/fuzzers/corpora/commit_graph/63de5e8e042222d53bf05640c87da376aefb76cc
new file mode 100644
index 0000000..2fd95a3
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/63de5e8e042222d53bf05640c87da376aefb76cc
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/647dbb1d05fe0fab685bfe126bd9ac3a12b6bccf b/fuzzers/corpora/commit_graph/647dbb1d05fe0fab685bfe126bd9ac3a12b6bccf
new file mode 100644
index 0000000..245d46b
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/647dbb1d05fe0fab685bfe126bd9ac3a12b6bccf
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/647e5e265d8d1079784fc2a3da25f7ba58126acd b/fuzzers/corpora/commit_graph/647e5e265d8d1079784fc2a3da25f7ba58126acd
new file mode 100644
index 0000000..633a2a3
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/647e5e265d8d1079784fc2a3da25f7ba58126acd
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/653bd480dfd1e5f4bdca702aba3dfd8da0c204b7 b/fuzzers/corpora/commit_graph/653bd480dfd1e5f4bdca702aba3dfd8da0c204b7
new file mode 100644
index 0000000..d123602
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/653bd480dfd1e5f4bdca702aba3dfd8da0c204b7
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/65485740a465377213c80fa68028727f281299fb b/fuzzers/corpora/commit_graph/65485740a465377213c80fa68028727f281299fb
new file mode 100644
index 0000000..c100bdb
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/65485740a465377213c80fa68028727f281299fb
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/6551f8c8c3028006d0cc4997943df8a86ee3f598 b/fuzzers/corpora/commit_graph/6551f8c8c3028006d0cc4997943df8a86ee3f598
new file mode 100644
index 0000000..0ff4dda
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/6551f8c8c3028006d0cc4997943df8a86ee3f598
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/67799e79d33883510f85ae9705ab3932862128a2 b/fuzzers/corpora/commit_graph/67799e79d33883510f85ae9705ab3932862128a2
new file mode 100644
index 0000000..2782b4f
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/67799e79d33883510f85ae9705ab3932862128a2
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/67b475481e5a21351b49789874adbc988aefd64c b/fuzzers/corpora/commit_graph/67b475481e5a21351b49789874adbc988aefd64c
new file mode 100644
index 0000000..263753a
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/67b475481e5a21351b49789874adbc988aefd64c
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/67e5a649967dee002d1c181e079748c404e29767 b/fuzzers/corpora/commit_graph/67e5a649967dee002d1c181e079748c404e29767
new file mode 100644
index 0000000..3bb8714
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/67e5a649967dee002d1c181e079748c404e29767
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/687424a4a31a66a78d1637c680c9c10746741007 b/fuzzers/corpora/commit_graph/687424a4a31a66a78d1637c680c9c10746741007
new file mode 100644
index 0000000..6b26c19
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/687424a4a31a66a78d1637c680c9c10746741007
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/68fa6dd52832657cb8dd7e1485d6fbafd4e93903 b/fuzzers/corpora/commit_graph/68fa6dd52832657cb8dd7e1485d6fbafd4e93903
new file mode 100644
index 0000000..fb966ff
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/68fa6dd52832657cb8dd7e1485d6fbafd4e93903
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/691696af1c042115f4d9f9b8e24f7b8c06ed189b b/fuzzers/corpora/commit_graph/691696af1c042115f4d9f9b8e24f7b8c06ed189b
new file mode 100644
index 0000000..e407bd3
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/691696af1c042115f4d9f9b8e24f7b8c06ed189b
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/6a80152f9b1afa3a3080bf3f6aa48e84c2e18497 b/fuzzers/corpora/commit_graph/6a80152f9b1afa3a3080bf3f6aa48e84c2e18497
new file mode 100644
index 0000000..a706cb0
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/6a80152f9b1afa3a3080bf3f6aa48e84c2e18497
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/6af27e4cf4c7bcce128a5949ee27fc73ab2cc71e b/fuzzers/corpora/commit_graph/6af27e4cf4c7bcce128a5949ee27fc73ab2cc71e
new file mode 100644
index 0000000..d0b2fd2
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/6af27e4cf4c7bcce128a5949ee27fc73ab2cc71e
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/6afd8f82d5639b774de0dfd418ae85322f4168dd b/fuzzers/corpora/commit_graph/6afd8f82d5639b774de0dfd418ae85322f4168dd
new file mode 100644
index 0000000..7840c31
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/6afd8f82d5639b774de0dfd418ae85322f4168dd
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/6c64a9e26e0e1480bb5e60b7044ca6ce17104a80 b/fuzzers/corpora/commit_graph/6c64a9e26e0e1480bb5e60b7044ca6ce17104a80
new file mode 100644
index 0000000..752046b
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/6c64a9e26e0e1480bb5e60b7044ca6ce17104a80
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/6c850c17db130ca0152f7c75562fa191f7ef89de b/fuzzers/corpora/commit_graph/6c850c17db130ca0152f7c75562fa191f7ef89de
new file mode 100644
index 0000000..a7f693b
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/6c850c17db130ca0152f7c75562fa191f7ef89de
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/6c9afe4527371a2baf33c5e220e4ca21a3207f94 b/fuzzers/corpora/commit_graph/6c9afe4527371a2baf33c5e220e4ca21a3207f94
new file mode 100644
index 0000000..d7b8ffc
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/6c9afe4527371a2baf33c5e220e4ca21a3207f94
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/6ce3d40b0225923a7f4123a919b1c5d70841fad7 b/fuzzers/corpora/commit_graph/6ce3d40b0225923a7f4123a919b1c5d70841fad7
new file mode 100644
index 0000000..3a2836b
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/6ce3d40b0225923a7f4123a919b1c5d70841fad7
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/6cfd064aa6197813eb18f38df967ae4cdba9c6da b/fuzzers/corpora/commit_graph/6cfd064aa6197813eb18f38df967ae4cdba9c6da
new file mode 100644
index 0000000..51778d5
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/6cfd064aa6197813eb18f38df967ae4cdba9c6da
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/6e6675676c53bcddc870e06605d2432e3429f224 b/fuzzers/corpora/commit_graph/6e6675676c53bcddc870e06605d2432e3429f224
new file mode 100644
index 0000000..1ddebef
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/6e6675676c53bcddc870e06605d2432e3429f224
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/6e6e82579b7abae2b43d90448d3f2ead4dfcba78 b/fuzzers/corpora/commit_graph/6e6e82579b7abae2b43d90448d3f2ead4dfcba78
new file mode 100644
index 0000000..a528901
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/6e6e82579b7abae2b43d90448d3f2ead4dfcba78
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/6f13d23c75a562eddefafe85e208e602832294e2 b/fuzzers/corpora/commit_graph/6f13d23c75a562eddefafe85e208e602832294e2
new file mode 100644
index 0000000..49a5393
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/6f13d23c75a562eddefafe85e208e602832294e2
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/6fed59b0472927f5d2396d0ee4d7fd13579377ce b/fuzzers/corpora/commit_graph/6fed59b0472927f5d2396d0ee4d7fd13579377ce
new file mode 100644
index 0000000..6b3970f
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/6fed59b0472927f5d2396d0ee4d7fd13579377ce
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/71f7724196f9f8fcfe3ee0161a84893bb9c4ab11 b/fuzzers/corpora/commit_graph/71f7724196f9f8fcfe3ee0161a84893bb9c4ab11
new file mode 100644
index 0000000..9e5c8dd
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/71f7724196f9f8fcfe3ee0161a84893bb9c4ab11
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/7335ecb1d41e713bf3909adf5802b90e22bc1581 b/fuzzers/corpora/commit_graph/7335ecb1d41e713bf3909adf5802b90e22bc1581
new file mode 100644
index 0000000..02e2fa6
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/7335ecb1d41e713bf3909adf5802b90e22bc1581
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/73afaa73175f461e1d19d5138e055c1649926dfe b/fuzzers/corpora/commit_graph/73afaa73175f461e1d19d5138e055c1649926dfe
new file mode 100644
index 0000000..0f45e0b
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/73afaa73175f461e1d19d5138e055c1649926dfe
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/73e2fcb45c4df90d19091056b235e7a317631a62 b/fuzzers/corpora/commit_graph/73e2fcb45c4df90d19091056b235e7a317631a62
new file mode 100644
index 0000000..549eeb3
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/73e2fcb45c4df90d19091056b235e7a317631a62
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/741cb2d5ae11b0a9e0608b58ec7284d75129a1f2 b/fuzzers/corpora/commit_graph/741cb2d5ae11b0a9e0608b58ec7284d75129a1f2
new file mode 100644
index 0000000..a16738d
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/741cb2d5ae11b0a9e0608b58ec7284d75129a1f2
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/7431bb0097a9bb52e1ceaaa8674a13cd3486a387 b/fuzzers/corpora/commit_graph/7431bb0097a9bb52e1ceaaa8674a13cd3486a387
new file mode 100644
index 0000000..d681b26
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/7431bb0097a9bb52e1ceaaa8674a13cd3486a387
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/7455b805995d0c96ac12f8a1c1264caaffcfac1c b/fuzzers/corpora/commit_graph/7455b805995d0c96ac12f8a1c1264caaffcfac1c
new file mode 100644
index 0000000..b3d2620
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/7455b805995d0c96ac12f8a1c1264caaffcfac1c
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/74e39b8a82fc06f9ed8f83ea30545ddf6df66811 b/fuzzers/corpora/commit_graph/74e39b8a82fc06f9ed8f83ea30545ddf6df66811
new file mode 100644
index 0000000..3cc9deb
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/74e39b8a82fc06f9ed8f83ea30545ddf6df66811
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/75d51e413d3e916560dc0c2ee5092d2f4972aec1 b/fuzzers/corpora/commit_graph/75d51e413d3e916560dc0c2ee5092d2f4972aec1
new file mode 100644
index 0000000..68f34e7
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/75d51e413d3e916560dc0c2ee5092d2f4972aec1
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/75e068964ea6beb7310a154d763de74a70071f48 b/fuzzers/corpora/commit_graph/75e068964ea6beb7310a154d763de74a70071f48
new file mode 100644
index 0000000..a08ee58
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/75e068964ea6beb7310a154d763de74a70071f48
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/763bf498dd847bd2b4af7b611199619bd428bea6 b/fuzzers/corpora/commit_graph/763bf498dd847bd2b4af7b611199619bd428bea6
new file mode 100644
index 0000000..4c28935
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/763bf498dd847bd2b4af7b611199619bd428bea6
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/77064ae04581a3c6d2a77158ef1a0b1e60db414a b/fuzzers/corpora/commit_graph/77064ae04581a3c6d2a77158ef1a0b1e60db414a
new file mode 100644
index 0000000..fbd0ca7
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/77064ae04581a3c6d2a77158ef1a0b1e60db414a
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/783bb14d68021061f592601607f40fe232ad17c4 b/fuzzers/corpora/commit_graph/783bb14d68021061f592601607f40fe232ad17c4
new file mode 100644
index 0000000..3cfa562
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/783bb14d68021061f592601607f40fe232ad17c4
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/7862814cb684310b54ef920b35403515efaba13c b/fuzzers/corpora/commit_graph/7862814cb684310b54ef920b35403515efaba13c
new file mode 100644
index 0000000..3066626
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/7862814cb684310b54ef920b35403515efaba13c
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/791fd85b6ffb2429e9fa5ba29eebdce214ad88c7 b/fuzzers/corpora/commit_graph/791fd85b6ffb2429e9fa5ba29eebdce214ad88c7
new file mode 100644
index 0000000..2c4739b
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/791fd85b6ffb2429e9fa5ba29eebdce214ad88c7
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/79396d4f6142a53e26e14aa6ccb4afb4fd8fc580 b/fuzzers/corpora/commit_graph/79396d4f6142a53e26e14aa6ccb4afb4fd8fc580
new file mode 100644
index 0000000..ef54342
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/79396d4f6142a53e26e14aa6ccb4afb4fd8fc580
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/79661b8e529e2182d5c612faba9f26e32a122b78 b/fuzzers/corpora/commit_graph/79661b8e529e2182d5c612faba9f26e32a122b78
new file mode 100644
index 0000000..75ebe29
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/79661b8e529e2182d5c612faba9f26e32a122b78
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/7969143acb3334bffac46c6dfd96362c81644191 b/fuzzers/corpora/commit_graph/7969143acb3334bffac46c6dfd96362c81644191
new file mode 100644
index 0000000..6ea2e66
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/7969143acb3334bffac46c6dfd96362c81644191
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/79d84866dc8c067508c02516b65c0e48cf689b56 b/fuzzers/corpora/commit_graph/79d84866dc8c067508c02516b65c0e48cf689b56
new file mode 100644
index 0000000..3080da7
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/79d84866dc8c067508c02516b65c0e48cf689b56
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/7b61f8f4a96e309bbe64ed82637fc81492a9652f b/fuzzers/corpora/commit_graph/7b61f8f4a96e309bbe64ed82637fc81492a9652f
new file mode 100644
index 0000000..28d93b7
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/7b61f8f4a96e309bbe64ed82637fc81492a9652f
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/7b8123f973edfb0f3cab027c0cd6b8efc7b11d6b b/fuzzers/corpora/commit_graph/7b8123f973edfb0f3cab027c0cd6b8efc7b11d6b
new file mode 100644
index 0000000..1c6699b
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/7b8123f973edfb0f3cab027c0cd6b8efc7b11d6b
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/7b8dd3093efba07f7a4d3bab4b90b8f6e4f28bfb b/fuzzers/corpora/commit_graph/7b8dd3093efba07f7a4d3bab4b90b8f6e4f28bfb
new file mode 100644
index 0000000..3e686e9
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/7b8dd3093efba07f7a4d3bab4b90b8f6e4f28bfb
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/7cc771aab0f3be7730881a46d952ae0a06958201 b/fuzzers/corpora/commit_graph/7cc771aab0f3be7730881a46d952ae0a06958201
new file mode 100644
index 0000000..ba94bfe
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/7cc771aab0f3be7730881a46d952ae0a06958201
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/7d177f4207de78d50df2493a3bc07f2cd578b363 b/fuzzers/corpora/commit_graph/7d177f4207de78d50df2493a3bc07f2cd578b363
new file mode 100644
index 0000000..a936354
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/7d177f4207de78d50df2493a3bc07f2cd578b363
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/7d2df075f3e73ea9809c31586c37ece0f568b7fa b/fuzzers/corpora/commit_graph/7d2df075f3e73ea9809c31586c37ece0f568b7fa
new file mode 100644
index 0000000..897276b
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/7d2df075f3e73ea9809c31586c37ece0f568b7fa
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/7d386e68e4c733a1fb11c0117f379fb4b9955fbb b/fuzzers/corpora/commit_graph/7d386e68e4c733a1fb11c0117f379fb4b9955fbb
new file mode 100644
index 0000000..f4f4d75
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/7d386e68e4c733a1fb11c0117f379fb4b9955fbb
@@ -0,0 +1 @@
+Øúö \ No newline at end of file
diff --git a/fuzzers/corpora/commit_graph/7e4260830352479d29310bd6e1022e19a68ffe76 b/fuzzers/corpora/commit_graph/7e4260830352479d29310bd6e1022e19a68ffe76
new file mode 100644
index 0000000..f478389
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/7e4260830352479d29310bd6e1022e19a68ffe76
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/7e4dfdae52be18cf95555c2eb1f54af7f69c6dde b/fuzzers/corpora/commit_graph/7e4dfdae52be18cf95555c2eb1f54af7f69c6dde
new file mode 100644
index 0000000..0f6d976
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/7e4dfdae52be18cf95555c2eb1f54af7f69c6dde
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/7eafedf7e7f20e86ecdf9ba51febf8492bdbc1f1 b/fuzzers/corpora/commit_graph/7eafedf7e7f20e86ecdf9ba51febf8492bdbc1f1
new file mode 100644
index 0000000..ae5f7c5
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/7eafedf7e7f20e86ecdf9ba51febf8492bdbc1f1
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/7ef1829a378d66b1dd70a767729127a0dc5edcae b/fuzzers/corpora/commit_graph/7ef1829a378d66b1dd70a767729127a0dc5edcae
new file mode 100644
index 0000000..e5d7355
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/7ef1829a378d66b1dd70a767729127a0dc5edcae
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/80b7d2b9d7e8c8fd7ae239b8d307b592f97ee000 b/fuzzers/corpora/commit_graph/80b7d2b9d7e8c8fd7ae239b8d307b592f97ee000
new file mode 100644
index 0000000..58ddc12
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/80b7d2b9d7e8c8fd7ae239b8d307b592f97ee000
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/810f577ff5c1af7807a26226af912687558158cd b/fuzzers/corpora/commit_graph/810f577ff5c1af7807a26226af912687558158cd
new file mode 100644
index 0000000..be8be9c
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/810f577ff5c1af7807a26226af912687558158cd
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/81603f1fe8d8e29005418d0fc9a9b33972366038 b/fuzzers/corpora/commit_graph/81603f1fe8d8e29005418d0fc9a9b33972366038
new file mode 100644
index 0000000..d9cb488
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/81603f1fe8d8e29005418d0fc9a9b33972366038
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/81c8b4d6884f954935fa4a8e828c4637db04b61a b/fuzzers/corpora/commit_graph/81c8b4d6884f954935fa4a8e828c4637db04b61a
new file mode 100644
index 0000000..47ae17b
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/81c8b4d6884f954935fa4a8e828c4637db04b61a
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/8226846e9b092561f85cc2956ab89d8cc1ae61e0 b/fuzzers/corpora/commit_graph/8226846e9b092561f85cc2956ab89d8cc1ae61e0
new file mode 100644
index 0000000..80d1cd9
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/8226846e9b092561f85cc2956ab89d8cc1ae61e0
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/825cfceea434e2392cce161356e3cb5f81ec2b3a b/fuzzers/corpora/commit_graph/825cfceea434e2392cce161356e3cb5f81ec2b3a
new file mode 100644
index 0000000..01f87ed
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/825cfceea434e2392cce161356e3cb5f81ec2b3a
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/82603febce83d95adf68b85cabf15d43ca0c4ee9 b/fuzzers/corpora/commit_graph/82603febce83d95adf68b85cabf15d43ca0c4ee9
new file mode 100644
index 0000000..d828b05
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/82603febce83d95adf68b85cabf15d43ca0c4ee9
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/827f0826cc4156e19b4c4938bec74e38de62fe9c b/fuzzers/corpora/commit_graph/827f0826cc4156e19b4c4938bec74e38de62fe9c
new file mode 100644
index 0000000..a391d62
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/827f0826cc4156e19b4c4938bec74e38de62fe9c
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/8486397ff8d1156249676c19b419a7758ff53f9a b/fuzzers/corpora/commit_graph/8486397ff8d1156249676c19b419a7758ff53f9a
new file mode 100644
index 0000000..dac9bbe
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/8486397ff8d1156249676c19b419a7758ff53f9a
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/84d99ee359bec1b8ee0f59e9bd96f1da062030b7 b/fuzzers/corpora/commit_graph/84d99ee359bec1b8ee0f59e9bd96f1da062030b7
new file mode 100644
index 0000000..a2e7cf3
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/84d99ee359bec1b8ee0f59e9bd96f1da062030b7
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/84e629bc7416039f1feb81fa9168d7c1ee3141c2 b/fuzzers/corpora/commit_graph/84e629bc7416039f1feb81fa9168d7c1ee3141c2
new file mode 100644
index 0000000..8ae4e39
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/84e629bc7416039f1feb81fa9168d7c1ee3141c2
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/84e885752179076fb38739ca7bc4345716bee56a b/fuzzers/corpora/commit_graph/84e885752179076fb38739ca7bc4345716bee56a
new file mode 100644
index 0000000..dd0c426
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/84e885752179076fb38739ca7bc4345716bee56a
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/859ef05494c8070057810b5c20df00fc81f81cf5 b/fuzzers/corpora/commit_graph/859ef05494c8070057810b5c20df00fc81f81cf5
new file mode 100644
index 0000000..2e3f299
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/859ef05494c8070057810b5c20df00fc81f81cf5
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/859fe592f33abc1d959c0e73ecd6cd4bffe23a97 b/fuzzers/corpora/commit_graph/859fe592f33abc1d959c0e73ecd6cd4bffe23a97
new file mode 100644
index 0000000..5289c8e
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/859fe592f33abc1d959c0e73ecd6cd4bffe23a97
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/860da5e8a468805b76a44b9ac99b4575be16ea15 b/fuzzers/corpora/commit_graph/860da5e8a468805b76a44b9ac99b4575be16ea15
new file mode 100644
index 0000000..c8b03da
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/860da5e8a468805b76a44b9ac99b4575be16ea15
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/865e415745cead02a826f058a5ee49099bdf9562 b/fuzzers/corpora/commit_graph/865e415745cead02a826f058a5ee49099bdf9562
new file mode 100644
index 0000000..a4ce658
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/865e415745cead02a826f058a5ee49099bdf9562
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/878bfce051a9c7462847d4e99b7e926dc821b7b8 b/fuzzers/corpora/commit_graph/878bfce051a9c7462847d4e99b7e926dc821b7b8
new file mode 100644
index 0000000..efb0bc5
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/878bfce051a9c7462847d4e99b7e926dc821b7b8
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/880492e4dc7259577c227bb4f075d7165e875c29 b/fuzzers/corpora/commit_graph/880492e4dc7259577c227bb4f075d7165e875c29
new file mode 100644
index 0000000..c977dff
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/880492e4dc7259577c227bb4f075d7165e875c29
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/88b7de1bd1c96454a1350286d115c0ee368511f9 b/fuzzers/corpora/commit_graph/88b7de1bd1c96454a1350286d115c0ee368511f9
new file mode 100644
index 0000000..7261eec
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/88b7de1bd1c96454a1350286d115c0ee368511f9
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/896268e4a5775b7ce33923ac6daeb0810420c55b b/fuzzers/corpora/commit_graph/896268e4a5775b7ce33923ac6daeb0810420c55b
new file mode 100644
index 0000000..0897bda
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/896268e4a5775b7ce33923ac6daeb0810420c55b
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/8978f8da89f9652878edabad164f5513ef508f27 b/fuzzers/corpora/commit_graph/8978f8da89f9652878edabad164f5513ef508f27
new file mode 100644
index 0000000..0dd9ba4
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/8978f8da89f9652878edabad164f5513ef508f27
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/89a6525b7db0e6ec211a484efd2880abef928d4e b/fuzzers/corpora/commit_graph/89a6525b7db0e6ec211a484efd2880abef928d4e
new file mode 100644
index 0000000..e5dd1cd
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/89a6525b7db0e6ec211a484efd2880abef928d4e
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/8ae86cba2bba6664fc5eb97be8e9777b8825d823 b/fuzzers/corpora/commit_graph/8ae86cba2bba6664fc5eb97be8e9777b8825d823
new file mode 100644
index 0000000..04a4aa5
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/8ae86cba2bba6664fc5eb97be8e9777b8825d823
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/8b845fbd2aa14e4f83c4dbc8b4b0b54d06482acd b/fuzzers/corpora/commit_graph/8b845fbd2aa14e4f83c4dbc8b4b0b54d06482acd
new file mode 100644
index 0000000..c711793
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/8b845fbd2aa14e4f83c4dbc8b4b0b54d06482acd
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/8c4121e6ce5956cfa408b980f16d276f456374dc b/fuzzers/corpora/commit_graph/8c4121e6ce5956cfa408b980f16d276f456374dc
new file mode 100644
index 0000000..1ba1891
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/8c4121e6ce5956cfa408b980f16d276f456374dc
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/8cb6a5b8ab41e3d27668d5735b5c09ff1f2eab65 b/fuzzers/corpora/commit_graph/8cb6a5b8ab41e3d27668d5735b5c09ff1f2eab65
new file mode 100644
index 0000000..e23d112
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/8cb6a5b8ab41e3d27668d5735b5c09ff1f2eab65
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/8d80a70ffd362a89b88663e27f11e8ab69b70c1b b/fuzzers/corpora/commit_graph/8d80a70ffd362a89b88663e27f11e8ab69b70c1b
new file mode 100644
index 0000000..02f765e
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/8d80a70ffd362a89b88663e27f11e8ab69b70c1b
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/8db603c1720b3680047f831f2ea9862567a7cdc4 b/fuzzers/corpora/commit_graph/8db603c1720b3680047f831f2ea9862567a7cdc4
new file mode 100644
index 0000000..d02cb03
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/8db603c1720b3680047f831f2ea9862567a7cdc4
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/8dd40b2d27c7dd4b986c35d87f826da287c09c4c b/fuzzers/corpora/commit_graph/8dd40b2d27c7dd4b986c35d87f826da287c09c4c
new file mode 100644
index 0000000..11aa235
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/8dd40b2d27c7dd4b986c35d87f826da287c09c4c
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/8e9d6e6408e5f708a1924e8370e687e2c202a4c4 b/fuzzers/corpora/commit_graph/8e9d6e6408e5f708a1924e8370e687e2c202a4c4
new file mode 100644
index 0000000..cbeb46b
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/8e9d6e6408e5f708a1924e8370e687e2c202a4c4
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/8f2dff1a30ee28e5985cb9379828aea5658d5849 b/fuzzers/corpora/commit_graph/8f2dff1a30ee28e5985cb9379828aea5658d5849
new file mode 100644
index 0000000..b7220c3
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/8f2dff1a30ee28e5985cb9379828aea5658d5849
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/8f7d18cdd6e605b85784ada14571fd5e5a184f2a b/fuzzers/corpora/commit_graph/8f7d18cdd6e605b85784ada14571fd5e5a184f2a
new file mode 100644
index 0000000..80fe175
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/8f7d18cdd6e605b85784ada14571fd5e5a184f2a
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/903ae52f0ac9af8348038b12f9259741b0de42f1 b/fuzzers/corpora/commit_graph/903ae52f0ac9af8348038b12f9259741b0de42f1
new file mode 100644
index 0000000..da8c220
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/903ae52f0ac9af8348038b12f9259741b0de42f1
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/9119e331f59e9337d419739c324f49d1bd62c8bf b/fuzzers/corpora/commit_graph/9119e331f59e9337d419739c324f49d1bd62c8bf
new file mode 100644
index 0000000..fc3a696
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/9119e331f59e9337d419739c324f49d1bd62c8bf
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/91d54d03b0917314ea1d67a70690df9247dd08d2 b/fuzzers/corpora/commit_graph/91d54d03b0917314ea1d67a70690df9247dd08d2
new file mode 100644
index 0000000..290da4d
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/91d54d03b0917314ea1d67a70690df9247dd08d2
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/922da3b96725bfd0e3f6ce119f1e2249d53f9086 b/fuzzers/corpora/commit_graph/922da3b96725bfd0e3f6ce119f1e2249d53f9086
new file mode 100644
index 0000000..c3bb009
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/922da3b96725bfd0e3f6ce119f1e2249d53f9086
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/9277561e0524cccba2f851970b0d88ec4f4d3f5e b/fuzzers/corpora/commit_graph/9277561e0524cccba2f851970b0d88ec4f4d3f5e
new file mode 100644
index 0000000..07d1da1
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/9277561e0524cccba2f851970b0d88ec4f4d3f5e
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/92a4d571804026b7bbe957396185e079e756b894 b/fuzzers/corpora/commit_graph/92a4d571804026b7bbe957396185e079e756b894
new file mode 100644
index 0000000..63b98b3
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/92a4d571804026b7bbe957396185e079e756b894
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/931224cc80168fd362a360d99bab813ed7bbf8ce b/fuzzers/corpora/commit_graph/931224cc80168fd362a360d99bab813ed7bbf8ce
new file mode 100644
index 0000000..df27b36
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/931224cc80168fd362a360d99bab813ed7bbf8ce
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/936ea5dad3bf023c552aa0bbeea8f7f66a11612c b/fuzzers/corpora/commit_graph/936ea5dad3bf023c552aa0bbeea8f7f66a11612c
new file mode 100644
index 0000000..847b537
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/936ea5dad3bf023c552aa0bbeea8f7f66a11612c
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/93aa4e0b1864933dce0abc0df69fe3d261f117f2 b/fuzzers/corpora/commit_graph/93aa4e0b1864933dce0abc0df69fe3d261f117f2
new file mode 100644
index 0000000..ac830ca
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/93aa4e0b1864933dce0abc0df69fe3d261f117f2
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/93d5b084965cf1b09085c4079a972e25207b3659 b/fuzzers/corpora/commit_graph/93d5b084965cf1b09085c4079a972e25207b3659
new file mode 100644
index 0000000..02a711a
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/93d5b084965cf1b09085c4079a972e25207b3659
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/9443fd3468bcc0bc3ff8dfe765225f045ab43d0a b/fuzzers/corpora/commit_graph/9443fd3468bcc0bc3ff8dfe765225f045ab43d0a
new file mode 100644
index 0000000..d1667ca
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/9443fd3468bcc0bc3ff8dfe765225f045ab43d0a
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/9624c26cefb5804b7906147d262e81ee4000b6d6 b/fuzzers/corpora/commit_graph/9624c26cefb5804b7906147d262e81ee4000b6d6
new file mode 100644
index 0000000..4c18831
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/9624c26cefb5804b7906147d262e81ee4000b6d6
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/9890933a73f39208627bd36e2fe88a6d54343a74 b/fuzzers/corpora/commit_graph/9890933a73f39208627bd36e2fe88a6d54343a74
new file mode 100644
index 0000000..dfbb39d
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/9890933a73f39208627bd36e2fe88a6d54343a74
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/989dad0448e79af10040d5080f74eba2b8a401ba b/fuzzers/corpora/commit_graph/989dad0448e79af10040d5080f74eba2b8a401ba
new file mode 100644
index 0000000..202bbd6
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/989dad0448e79af10040d5080f74eba2b8a401ba
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/98ed4808b4a8da66a91fcea1be63be6371a7c7ac b/fuzzers/corpora/commit_graph/98ed4808b4a8da66a91fcea1be63be6371a7c7ac
new file mode 100644
index 0000000..b06a167
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/98ed4808b4a8da66a91fcea1be63be6371a7c7ac
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/9928e516b85e22fbad58d562d3b7e814d9ce812d b/fuzzers/corpora/commit_graph/9928e516b85e22fbad58d562d3b7e814d9ce812d
new file mode 100644
index 0000000..d1e5a04
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/9928e516b85e22fbad58d562d3b7e814d9ce812d
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/994c7cc5599252b5628d89cd0ba4b5574d32bf00 b/fuzzers/corpora/commit_graph/994c7cc5599252b5628d89cd0ba4b5574d32bf00
new file mode 100644
index 0000000..d51d00d
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/994c7cc5599252b5628d89cd0ba4b5574d32bf00
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/99c8557c2a02ea030de42869af42c1f7c77114db b/fuzzers/corpora/commit_graph/99c8557c2a02ea030de42869af42c1f7c77114db
new file mode 100644
index 0000000..18f5349
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/99c8557c2a02ea030de42869af42c1f7c77114db
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/9a14c867272f102b84efdba73662d318c3e51cfe b/fuzzers/corpora/commit_graph/9a14c867272f102b84efdba73662d318c3e51cfe
new file mode 100644
index 0000000..fc473b2
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/9a14c867272f102b84efdba73662d318c3e51cfe
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/9a6f158c176d4a1982d541be2bc27a8afba4ea57 b/fuzzers/corpora/commit_graph/9a6f158c176d4a1982d541be2bc27a8afba4ea57
new file mode 100644
index 0000000..b99bc20
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/9a6f158c176d4a1982d541be2bc27a8afba4ea57
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/9aa4af603192823a2fdc53d95ed36896bc3309b2 b/fuzzers/corpora/commit_graph/9aa4af603192823a2fdc53d95ed36896bc3309b2
new file mode 100644
index 0000000..c36e298
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/9aa4af603192823a2fdc53d95ed36896bc3309b2
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/9b40c2190123cec66af3b37212f6c567869efda3 b/fuzzers/corpora/commit_graph/9b40c2190123cec66af3b37212f6c567869efda3
new file mode 100644
index 0000000..1662324
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/9b40c2190123cec66af3b37212f6c567869efda3
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/9b6268c11d78c35db5164f1346905e602b6a49fe b/fuzzers/corpora/commit_graph/9b6268c11d78c35db5164f1346905e602b6a49fe
new file mode 100644
index 0000000..4106e20
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/9b6268c11d78c35db5164f1346905e602b6a49fe
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/9c6883ba5cedb7d711b12733d66ef1a1156dd0af b/fuzzers/corpora/commit_graph/9c6883ba5cedb7d711b12733d66ef1a1156dd0af
new file mode 100644
index 0000000..84a193c
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/9c6883ba5cedb7d711b12733d66ef1a1156dd0af
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/9c85c90f44b454ce0d52882c447f5ecb8d303634 b/fuzzers/corpora/commit_graph/9c85c90f44b454ce0d52882c447f5ecb8d303634
new file mode 100644
index 0000000..e17438b
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/9c85c90f44b454ce0d52882c447f5ecb8d303634
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/9cb7a2e89ec636da3fd41ecc49ebe25e5344e2c6 b/fuzzers/corpora/commit_graph/9cb7a2e89ec636da3fd41ecc49ebe25e5344e2c6
new file mode 100644
index 0000000..55f76fd
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/9cb7a2e89ec636da3fd41ecc49ebe25e5344e2c6
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/9d912dc5a3497e4b5b40b37202fc0ffbf5263666 b/fuzzers/corpora/commit_graph/9d912dc5a3497e4b5b40b37202fc0ffbf5263666
new file mode 100644
index 0000000..c71c31a
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/9d912dc5a3497e4b5b40b37202fc0ffbf5263666
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/9dcbafe8c5345194ee0ce7eb4f6efaeb55543626 b/fuzzers/corpora/commit_graph/9dcbafe8c5345194ee0ce7eb4f6efaeb55543626
new file mode 100644
index 0000000..42c5e7d
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/9dcbafe8c5345194ee0ce7eb4f6efaeb55543626
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/9f4b0f3d2d25e6405ba6093f24d0605327711573 b/fuzzers/corpora/commit_graph/9f4b0f3d2d25e6405ba6093f24d0605327711573
new file mode 100644
index 0000000..0dc94be
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/9f4b0f3d2d25e6405ba6093f24d0605327711573
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/a047bf683239fa208dbac09424b105820ac23f43 b/fuzzers/corpora/commit_graph/a047bf683239fa208dbac09424b105820ac23f43
new file mode 100644
index 0000000..925c86c
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/a047bf683239fa208dbac09424b105820ac23f43
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/a1379dcd89ef5e73eabbfcc395113e3636e0ae09 b/fuzzers/corpora/commit_graph/a1379dcd89ef5e73eabbfcc395113e3636e0ae09
new file mode 100644
index 0000000..43d31b6
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/a1379dcd89ef5e73eabbfcc395113e3636e0ae09
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/a38c7ef56adabd0916abac514154b1f362d40434 b/fuzzers/corpora/commit_graph/a38c7ef56adabd0916abac514154b1f362d40434
new file mode 100644
index 0000000..944b4ea
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/a38c7ef56adabd0916abac514154b1f362d40434
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/a38ec6ad4a8466b4feb88e67b16524e8f3feac64 b/fuzzers/corpora/commit_graph/a38ec6ad4a8466b4feb88e67b16524e8f3feac64
new file mode 100644
index 0000000..a9d1adb
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/a38ec6ad4a8466b4feb88e67b16524e8f3feac64
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/a3fdea21020268b3b2409c1115d50697d9ae8f8c b/fuzzers/corpora/commit_graph/a3fdea21020268b3b2409c1115d50697d9ae8f8c
new file mode 100644
index 0000000..8d4f934
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/a3fdea21020268b3b2409c1115d50697d9ae8f8c
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/a45f1987a444b2c27e90fc1477e8b0815f75383f b/fuzzers/corpora/commit_graph/a45f1987a444b2c27e90fc1477e8b0815f75383f
new file mode 100644
index 0000000..d211a8b
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/a45f1987a444b2c27e90fc1477e8b0815f75383f
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/a4682958fb7029384c0a01a4a1356ac6f2f44fe1 b/fuzzers/corpora/commit_graph/a4682958fb7029384c0a01a4a1356ac6f2f44fe1
new file mode 100644
index 0000000..e8f66dc
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/a4682958fb7029384c0a01a4a1356ac6f2f44fe1
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/a4de41561725960d6f48f210a4fb74d527f7b0c2 b/fuzzers/corpora/commit_graph/a4de41561725960d6f48f210a4fb74d527f7b0c2
new file mode 100644
index 0000000..ac4c41c
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/a4de41561725960d6f48f210a4fb74d527f7b0c2
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/a5935f34435ecdd6587ad4f77b20d479d3387dbe b/fuzzers/corpora/commit_graph/a5935f34435ecdd6587ad4f77b20d479d3387dbe
new file mode 100644
index 0000000..2e00f59
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/a5935f34435ecdd6587ad4f77b20d479d3387dbe
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/a5b394beb2b1d463ad80924a8c8c70584bf5c629 b/fuzzers/corpora/commit_graph/a5b394beb2b1d463ad80924a8c8c70584bf5c629
new file mode 100644
index 0000000..eb8f700
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/a5b394beb2b1d463ad80924a8c8c70584bf5c629
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/a62bc806f8c98ba7986243c2185a0548a8dd57ef b/fuzzers/corpora/commit_graph/a62bc806f8c98ba7986243c2185a0548a8dd57ef
new file mode 100644
index 0000000..fb30d9e
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/a62bc806f8c98ba7986243c2185a0548a8dd57ef
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/a7013e97948893e0118c686c06e332cc611bea7e b/fuzzers/corpora/commit_graph/a7013e97948893e0118c686c06e332cc611bea7e
new file mode 100644
index 0000000..ab50113
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/a7013e97948893e0118c686c06e332cc611bea7e
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/a74f5df8c7f25c37c15c0f74ed50019d17338225 b/fuzzers/corpora/commit_graph/a74f5df8c7f25c37c15c0f74ed50019d17338225
new file mode 100644
index 0000000..b234c15
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/a74f5df8c7f25c37c15c0f74ed50019d17338225
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/a7ab3559fb3da3f027e67091116253f3bdfd7828 b/fuzzers/corpora/commit_graph/a7ab3559fb3da3f027e67091116253f3bdfd7828
new file mode 100644
index 0000000..838337a
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/a7ab3559fb3da3f027e67091116253f3bdfd7828
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/a845c8258a02022d447ea9249788b345f5504648 b/fuzzers/corpora/commit_graph/a845c8258a02022d447ea9249788b345f5504648
new file mode 100644
index 0000000..78311d9
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/a845c8258a02022d447ea9249788b345f5504648
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/a8d3e026e2393587eb170afb32e94ff0e1f8a8be b/fuzzers/corpora/commit_graph/a8d3e026e2393587eb170afb32e94ff0e1f8a8be
new file mode 100644
index 0000000..54457f3
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/a8d3e026e2393587eb170afb32e94ff0e1f8a8be
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/a8d547e41ee21e163e65cf0a186d469dfa50ec19 b/fuzzers/corpora/commit_graph/a8d547e41ee21e163e65cf0a186d469dfa50ec19
new file mode 100644
index 0000000..52d265b
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/a8d547e41ee21e163e65cf0a186d469dfa50ec19
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/a8fa22521dd6813e595cc0a9586ee71fff305fe2 b/fuzzers/corpora/commit_graph/a8fa22521dd6813e595cc0a9586ee71fff305fe2
new file mode 100644
index 0000000..5f90995
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/a8fa22521dd6813e595cc0a9586ee71fff305fe2
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/a9969442d585d9a53259c71c73b095701280eac5 b/fuzzers/corpora/commit_graph/a9969442d585d9a53259c71c73b095701280eac5
new file mode 100644
index 0000000..f757928
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/a9969442d585d9a53259c71c73b095701280eac5
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/a99789d0ce2d7b937aaa8afa3cfc0f4ccd7be95f b/fuzzers/corpora/commit_graph/a99789d0ce2d7b937aaa8afa3cfc0f4ccd7be95f
new file mode 100644
index 0000000..471add5
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/a99789d0ce2d7b937aaa8afa3cfc0f4ccd7be95f
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/aaca30ee3ab38edfa2b061fcbcbca0c0ea657f15 b/fuzzers/corpora/commit_graph/aaca30ee3ab38edfa2b061fcbcbca0c0ea657f15
new file mode 100644
index 0000000..cc981c1
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/aaca30ee3ab38edfa2b061fcbcbca0c0ea657f15
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/aacdec3f05e98eb6eedddb9c6edb968e1a63c551 b/fuzzers/corpora/commit_graph/aacdec3f05e98eb6eedddb9c6edb968e1a63c551
new file mode 100644
index 0000000..622c247
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/aacdec3f05e98eb6eedddb9c6edb968e1a63c551
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/aadd85127241b94a41d02d9e9699e3e9773de1c9 b/fuzzers/corpora/commit_graph/aadd85127241b94a41d02d9e9699e3e9773de1c9
new file mode 100644
index 0000000..b84e5f1
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/aadd85127241b94a41d02d9e9699e3e9773de1c9
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/ab8ad126702803d21dbafc85713bbee7f25f36e5 b/fuzzers/corpora/commit_graph/ab8ad126702803d21dbafc85713bbee7f25f36e5
new file mode 100644
index 0000000..9a19eda
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/ab8ad126702803d21dbafc85713bbee7f25f36e5
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/ac26f9afd599ff6f33396c2e02130654f3e2390c b/fuzzers/corpora/commit_graph/ac26f9afd599ff6f33396c2e02130654f3e2390c
new file mode 100644
index 0000000..843eaa2
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/ac26f9afd599ff6f33396c2e02130654f3e2390c
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/ac8b129e4756fda0c50c9dd0eb13e34c7b41ce8e b/fuzzers/corpora/commit_graph/ac8b129e4756fda0c50c9dd0eb13e34c7b41ce8e
new file mode 100644
index 0000000..4b41d07
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/ac8b129e4756fda0c50c9dd0eb13e34c7b41ce8e
@@ -0,0 +1 @@
+ïïïœïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïï@ïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïë \ No newline at end of file
diff --git a/fuzzers/corpora/commit_graph/aceaf3b72c2627dd3dd065974b854150681c093f b/fuzzers/corpora/commit_graph/aceaf3b72c2627dd3dd065974b854150681c093f
new file mode 100644
index 0000000..d490782
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/aceaf3b72c2627dd3dd065974b854150681c093f
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/ad1fcdc3bf806392e754a902eba9edd3b344c31f b/fuzzers/corpora/commit_graph/ad1fcdc3bf806392e754a902eba9edd3b344c31f
new file mode 100644
index 0000000..1379b75
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/ad1fcdc3bf806392e754a902eba9edd3b344c31f
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/ad8c80e532482f9dfbfbb7c0d447f1f4e592bf72 b/fuzzers/corpora/commit_graph/ad8c80e532482f9dfbfbb7c0d447f1f4e592bf72
new file mode 100644
index 0000000..bfe3911
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/ad8c80e532482f9dfbfbb7c0d447f1f4e592bf72
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/add92b71bf897da2f71f691e6abcb6d02cb8e99f b/fuzzers/corpora/commit_graph/add92b71bf897da2f71f691e6abcb6d02cb8e99f
new file mode 100644
index 0000000..1676dbf
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/add92b71bf897da2f71f691e6abcb6d02cb8e99f
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/aeb8ccf6d82be9236c9e689e1580d043bd701eb0 b/fuzzers/corpora/commit_graph/aeb8ccf6d82be9236c9e689e1580d043bd701eb0
new file mode 100644
index 0000000..10688e3
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/aeb8ccf6d82be9236c9e689e1580d043bd701eb0
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/af1a827aedbf674fff2bdeb5589554eec62787ab b/fuzzers/corpora/commit_graph/af1a827aedbf674fff2bdeb5589554eec62787ab
new file mode 100644
index 0000000..6f08ae5
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/af1a827aedbf674fff2bdeb5589554eec62787ab
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/afaab9a75414d231176ad4582b6f8d81b5dbedb3 b/fuzzers/corpora/commit_graph/afaab9a75414d231176ad4582b6f8d81b5dbedb3
new file mode 100644
index 0000000..8701780
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/afaab9a75414d231176ad4582b6f8d81b5dbedb3
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/afc12c4ebed1f3ab962d7dcef110b5328b1e24c3 b/fuzzers/corpora/commit_graph/afc12c4ebed1f3ab962d7dcef110b5328b1e24c3
new file mode 100644
index 0000000..0e10f77
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/afc12c4ebed1f3ab962d7dcef110b5328b1e24c3
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/b0044f3744cf019658d668a33f8d1e53ef8bd6ce b/fuzzers/corpora/commit_graph/b0044f3744cf019658d668a33f8d1e53ef8bd6ce
new file mode 100644
index 0000000..ede7bfe
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/b0044f3744cf019658d668a33f8d1e53ef8bd6ce
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/b06adc81a4e1cdcda3786970ca07ed9dee0b6401 b/fuzzers/corpora/commit_graph/b06adc81a4e1cdcda3786970ca07ed9dee0b6401
new file mode 100644
index 0000000..b5f08b2
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/b06adc81a4e1cdcda3786970ca07ed9dee0b6401
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/b139802a1cc90fd5b86cae044c221361892c688d b/fuzzers/corpora/commit_graph/b139802a1cc90fd5b86cae044c221361892c688d
new file mode 100644
index 0000000..c21f39e
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/b139802a1cc90fd5b86cae044c221361892c688d
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/b1b8f251542db01bdb01be3b6d5b117b07db1834 b/fuzzers/corpora/commit_graph/b1b8f251542db01bdb01be3b6d5b117b07db1834
new file mode 100644
index 0000000..b42f726
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/b1b8f251542db01bdb01be3b6d5b117b07db1834
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/b1b9af93f84ed6861b9c0ade39980e89ef828c8f b/fuzzers/corpora/commit_graph/b1b9af93f84ed6861b9c0ade39980e89ef828c8f
new file mode 100644
index 0000000..b367b87
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/b1b9af93f84ed6861b9c0ade39980e89ef828c8f
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/b2eae68035cafd4077f6a4c3e4e961fdc1e8122b b/fuzzers/corpora/commit_graph/b2eae68035cafd4077f6a4c3e4e961fdc1e8122b
new file mode 100644
index 0000000..1c62898
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/b2eae68035cafd4077f6a4c3e4e961fdc1e8122b
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/b32897a6aedaa8c5a6e656dd808bafabc4ee5608 b/fuzzers/corpora/commit_graph/b32897a6aedaa8c5a6e656dd808bafabc4ee5608
new file mode 100644
index 0000000..b3fbb81
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/b32897a6aedaa8c5a6e656dd808bafabc4ee5608
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/b376e4fc517297f92ac1713803ae3b60d5ebbe43 b/fuzzers/corpora/commit_graph/b376e4fc517297f92ac1713803ae3b60d5ebbe43
new file mode 100644
index 0000000..637eafd
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/b376e4fc517297f92ac1713803ae3b60d5ebbe43
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/b3fd100b139cfbffaad68aacf7d462861e9dca35 b/fuzzers/corpora/commit_graph/b3fd100b139cfbffaad68aacf7d462861e9dca35
new file mode 100644
index 0000000..da105f5
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/b3fd100b139cfbffaad68aacf7d462861e9dca35
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/b40808ca955faab4829811bced1cccb2ab58ea58 b/fuzzers/corpora/commit_graph/b40808ca955faab4829811bced1cccb2ab58ea58
new file mode 100644
index 0000000..4daa741
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/b40808ca955faab4829811bced1cccb2ab58ea58
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/b43daf9f87a514bce74af3e5a39284c69c4e7011 b/fuzzers/corpora/commit_graph/b43daf9f87a514bce74af3e5a39284c69c4e7011
new file mode 100644
index 0000000..5c8129d
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/b43daf9f87a514bce74af3e5a39284c69c4e7011
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/b477da07f3e5796ff4a98c8a5bdb0e4a634954bf b/fuzzers/corpora/commit_graph/b477da07f3e5796ff4a98c8a5bdb0e4a634954bf
new file mode 100644
index 0000000..dbe8abe
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/b477da07f3e5796ff4a98c8a5bdb0e4a634954bf
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/b4a2ef09cf59ca5ccf810a6f001cce710cc02f6b b/fuzzers/corpora/commit_graph/b4a2ef09cf59ca5ccf810a6f001cce710cc02f6b
new file mode 100644
index 0000000..3a58173
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/b4a2ef09cf59ca5ccf810a6f001cce710cc02f6b
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/b4b75e588cb83430c502a34ec3dcfaf774a00359 b/fuzzers/corpora/commit_graph/b4b75e588cb83430c502a34ec3dcfaf774a00359
new file mode 100644
index 0000000..4afd452
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/b4b75e588cb83430c502a34ec3dcfaf774a00359
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/b4ce98acd2b288b6cfc00461e2e15e0f8004030c b/fuzzers/corpora/commit_graph/b4ce98acd2b288b6cfc00461e2e15e0f8004030c
new file mode 100644
index 0000000..a707426
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/b4ce98acd2b288b6cfc00461e2e15e0f8004030c
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/b75563f30f7e4fb369d2449b723ee8b282d03eff b/fuzzers/corpora/commit_graph/b75563f30f7e4fb369d2449b723ee8b282d03eff
new file mode 100644
index 0000000..a101bca
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/b75563f30f7e4fb369d2449b723ee8b282d03eff
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/b7a0a820afa7057081de186728d0d887131d9314 b/fuzzers/corpora/commit_graph/b7a0a820afa7057081de186728d0d887131d9314
new file mode 100644
index 0000000..915cc26
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/b7a0a820afa7057081de186728d0d887131d9314
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/b7e880446146c735a3f820fb93969c8c172c2fb5 b/fuzzers/corpora/commit_graph/b7e880446146c735a3f820fb93969c8c172c2fb5
new file mode 100644
index 0000000..cd26631
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/b7e880446146c735a3f820fb93969c8c172c2fb5
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/b833073d3006e7cbac03c494603a9b75e7b2a723 b/fuzzers/corpora/commit_graph/b833073d3006e7cbac03c494603a9b75e7b2a723
new file mode 100644
index 0000000..0798037
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/b833073d3006e7cbac03c494603a9b75e7b2a723
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/b89459c1fb6fc918db4c81a32a75ee66217f9ab8 b/fuzzers/corpora/commit_graph/b89459c1fb6fc918db4c81a32a75ee66217f9ab8
new file mode 100644
index 0000000..bdc6de6
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/b89459c1fb6fc918db4c81a32a75ee66217f9ab8
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/b8aab6c9b2c706f8df0ff695ff94969171f9c807 b/fuzzers/corpora/commit_graph/b8aab6c9b2c706f8df0ff695ff94969171f9c807
new file mode 100644
index 0000000..810a305
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/b8aab6c9b2c706f8df0ff695ff94969171f9c807
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/b9751182a36acb79b77585e1e379857a530e95c8 b/fuzzers/corpora/commit_graph/b9751182a36acb79b77585e1e379857a530e95c8
new file mode 100644
index 0000000..eeb1ed0
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/b9751182a36acb79b77585e1e379857a530e95c8
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/b9ddb239b5a2c1348d972ec70a08507c35ba4432 b/fuzzers/corpora/commit_graph/b9ddb239b5a2c1348d972ec70a08507c35ba4432
new file mode 100644
index 0000000..0d746e5
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/b9ddb239b5a2c1348d972ec70a08507c35ba4432
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/ba8f573256a0fbb95c5626f399ebc3ef50bbd826 b/fuzzers/corpora/commit_graph/ba8f573256a0fbb95c5626f399ebc3ef50bbd826
new file mode 100644
index 0000000..330229e
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/ba8f573256a0fbb95c5626f399ebc3ef50bbd826
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/bc165749042d5425c5d6d4e29b17769a2315a80d b/fuzzers/corpora/commit_graph/bc165749042d5425c5d6d4e29b17769a2315a80d
new file mode 100644
index 0000000..af0d232
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/bc165749042d5425c5d6d4e29b17769a2315a80d
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/bc910bd349319e1ed44d7c7266e3ac99cc29ecc6 b/fuzzers/corpora/commit_graph/bc910bd349319e1ed44d7c7266e3ac99cc29ecc6
new file mode 100644
index 0000000..a38abe5
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/bc910bd349319e1ed44d7c7266e3ac99cc29ecc6
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/bc97b1d4f57eb7770bc3983e2d57c8c01b21d29e b/fuzzers/corpora/commit_graph/bc97b1d4f57eb7770bc3983e2d57c8c01b21d29e
new file mode 100644
index 0000000..631754c
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/bc97b1d4f57eb7770bc3983e2d57c8c01b21d29e
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/bd06f768e35ded4437cb88e2bc0ddd0bea3fa84c b/fuzzers/corpora/commit_graph/bd06f768e35ded4437cb88e2bc0ddd0bea3fa84c
new file mode 100644
index 0000000..954e73d
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/bd06f768e35ded4437cb88e2bc0ddd0bea3fa84c
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/bd702faff9725a7a1957fd0f85cc52799f37b682 b/fuzzers/corpora/commit_graph/bd702faff9725a7a1957fd0f85cc52799f37b682
new file mode 100644
index 0000000..928fe58
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/bd702faff9725a7a1957fd0f85cc52799f37b682
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/bee4464861e1cae3cfdd5fbcb340efbf02e8d8ca b/fuzzers/corpora/commit_graph/bee4464861e1cae3cfdd5fbcb340efbf02e8d8ca
new file mode 100644
index 0000000..8f636ce
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/bee4464861e1cae3cfdd5fbcb340efbf02e8d8ca
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/bf7ad994b098ec85d62683a16e067635e21a8af5 b/fuzzers/corpora/commit_graph/bf7ad994b098ec85d62683a16e067635e21a8af5
new file mode 100644
index 0000000..71ca107
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/bf7ad994b098ec85d62683a16e067635e21a8af5
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/c054fc89ed72101dec861668ff1738ef85b728b9 b/fuzzers/corpora/commit_graph/c054fc89ed72101dec861668ff1738ef85b728b9
new file mode 100644
index 0000000..988ab6b
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/c054fc89ed72101dec861668ff1738ef85b728b9
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/c06752415ac037fefe5172dc7245cd7c49ca7fca b/fuzzers/corpora/commit_graph/c06752415ac037fefe5172dc7245cd7c49ca7fca
new file mode 100644
index 0000000..d03fcac
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/c06752415ac037fefe5172dc7245cd7c49ca7fca
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/c0c8b54354d172a0be751e3e9b80be961bb15ddb b/fuzzers/corpora/commit_graph/c0c8b54354d172a0be751e3e9b80be961bb15ddb
new file mode 100644
index 0000000..233aa4e
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/c0c8b54354d172a0be751e3e9b80be961bb15ddb
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/c0e7ca9b5b4d0e72d23d7dc9e9d1f2463a17a20d b/fuzzers/corpora/commit_graph/c0e7ca9b5b4d0e72d23d7dc9e9d1f2463a17a20d
new file mode 100644
index 0000000..c36f7c1
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/c0e7ca9b5b4d0e72d23d7dc9e9d1f2463a17a20d
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/c13576a29c98bee02aa47f646f5f170f9b7d83f9 b/fuzzers/corpora/commit_graph/c13576a29c98bee02aa47f646f5f170f9b7d83f9
new file mode 100644
index 0000000..95bdcb9
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/c13576a29c98bee02aa47f646f5f170f9b7d83f9
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/c14edf1d34f40b3cc74772c81ebe5d72172cc662 b/fuzzers/corpora/commit_graph/c14edf1d34f40b3cc74772c81ebe5d72172cc662
new file mode 100644
index 0000000..63b1e39
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/c14edf1d34f40b3cc74772c81ebe5d72172cc662
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/c2789364cb35d111f08f924d0d7550ea9785c61e b/fuzzers/corpora/commit_graph/c2789364cb35d111f08f924d0d7550ea9785c61e
new file mode 100644
index 0000000..0044eb5
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/c2789364cb35d111f08f924d0d7550ea9785c61e
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/c2d8b07acb13e43a89b6c4afb3ecb9817dd4a8e9 b/fuzzers/corpora/commit_graph/c2d8b07acb13e43a89b6c4afb3ecb9817dd4a8e9
new file mode 100644
index 0000000..8cf6a91
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/c2d8b07acb13e43a89b6c4afb3ecb9817dd4a8e9
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/c36ed796c1bf839668db8fc3475a2ffb32ad8ceb b/fuzzers/corpora/commit_graph/c36ed796c1bf839668db8fc3475a2ffb32ad8ceb
new file mode 100644
index 0000000..77dd76b
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/c36ed796c1bf839668db8fc3475a2ffb32ad8ceb
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/c41ec9dd94427423e4704721e7f21eae0c44ef20 b/fuzzers/corpora/commit_graph/c41ec9dd94427423e4704721e7f21eae0c44ef20
new file mode 100644
index 0000000..9b828b8
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/c41ec9dd94427423e4704721e7f21eae0c44ef20
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/c42c544fa9dbb1264b39bf920b40985384db1d16 b/fuzzers/corpora/commit_graph/c42c544fa9dbb1264b39bf920b40985384db1d16
new file mode 100644
index 0000000..6088980
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/c42c544fa9dbb1264b39bf920b40985384db1d16
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/c45ec3f594abc15de0a8cc3ad748ba23cb34ec64 b/fuzzers/corpora/commit_graph/c45ec3f594abc15de0a8cc3ad748ba23cb34ec64
new file mode 100644
index 0000000..6a711ba
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/c45ec3f594abc15de0a8cc3ad748ba23cb34ec64
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/c49004d980961f288616a4eb9ebf68123fd68ffa b/fuzzers/corpora/commit_graph/c49004d980961f288616a4eb9ebf68123fd68ffa
new file mode 100644
index 0000000..0b5a6bc
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/c49004d980961f288616a4eb9ebf68123fd68ffa
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/c4c3c3c8df24adf505127627b3090116de78d9a6 b/fuzzers/corpora/commit_graph/c4c3c3c8df24adf505127627b3090116de78d9a6
new file mode 100644
index 0000000..528725c
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/c4c3c3c8df24adf505127627b3090116de78d9a6
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/c5c1921293af4a5953cb386092694042715fcfb3 b/fuzzers/corpora/commit_graph/c5c1921293af4a5953cb386092694042715fcfb3
new file mode 100644
index 0000000..2256d19
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/c5c1921293af4a5953cb386092694042715fcfb3
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/c615caad21cd8a754fcb2008420234c5511c62b7 b/fuzzers/corpora/commit_graph/c615caad21cd8a754fcb2008420234c5511c62b7
new file mode 100644
index 0000000..fa4c9cc
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/c615caad21cd8a754fcb2008420234c5511c62b7
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/c6a9ee3f8fdc42566c4799db3912a83c8c438d7f b/fuzzers/corpora/commit_graph/c6a9ee3f8fdc42566c4799db3912a83c8c438d7f
new file mode 100644
index 0000000..c3c69fc
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/c6a9ee3f8fdc42566c4799db3912a83c8c438d7f
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/c6b661e976282051285b913b3728383f36103ef8 b/fuzzers/corpora/commit_graph/c6b661e976282051285b913b3728383f36103ef8
new file mode 100644
index 0000000..6a156d2
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/c6b661e976282051285b913b3728383f36103ef8
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/c716ba47f810c238fe7bda1fbdc7b1ccc34e9848 b/fuzzers/corpora/commit_graph/c716ba47f810c238fe7bda1fbdc7b1ccc34e9848
new file mode 100644
index 0000000..271ec76
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/c716ba47f810c238fe7bda1fbdc7b1ccc34e9848
@@ -0,0 +1 @@
+ïïïœïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïï@ïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïï \ No newline at end of file
diff --git a/fuzzers/corpora/commit_graph/c85b2fa4421302e2fa333a9e33d59a882aa04f4f b/fuzzers/corpora/commit_graph/c85b2fa4421302e2fa333a9e33d59a882aa04f4f
new file mode 100644
index 0000000..eba46a6
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/c85b2fa4421302e2fa333a9e33d59a882aa04f4f
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/c871d135f2d3117b326688355bc0fa6f26d56cd6 b/fuzzers/corpora/commit_graph/c871d135f2d3117b326688355bc0fa6f26d56cd6
new file mode 100644
index 0000000..6ead612
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/c871d135f2d3117b326688355bc0fa6f26d56cd6
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/c915b02265a27d185a8b028305f082ddb3ebd704 b/fuzzers/corpora/commit_graph/c915b02265a27d185a8b028305f082ddb3ebd704
new file mode 100644
index 0000000..1960dfb
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/c915b02265a27d185a8b028305f082ddb3ebd704
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/c952d38b3e642db4795d7f954b85f4f6d2a041aa b/fuzzers/corpora/commit_graph/c952d38b3e642db4795d7f954b85f4f6d2a041aa
new file mode 100644
index 0000000..b8ee305
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/c952d38b3e642db4795d7f954b85f4f6d2a041aa
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/c98ee52065736c4172f6ee0c31977bf1b560d685 b/fuzzers/corpora/commit_graph/c98ee52065736c4172f6ee0c31977bf1b560d685
new file mode 100644
index 0000000..f8d7a23
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/c98ee52065736c4172f6ee0c31977bf1b560d685
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/c99b183a2cd0dd8a4c1a141cc6eebb0311501fa5 b/fuzzers/corpora/commit_graph/c99b183a2cd0dd8a4c1a141cc6eebb0311501fa5
new file mode 100644
index 0000000..9cd1ad4
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/c99b183a2cd0dd8a4c1a141cc6eebb0311501fa5
@@ -0,0 +1 @@
+Ëó@ÿ~ \ No newline at end of file
diff --git a/fuzzers/corpora/commit_graph/ca0cd26baff2f2c0759e619800ebbe7314d2bb95 b/fuzzers/corpora/commit_graph/ca0cd26baff2f2c0759e619800ebbe7314d2bb95
new file mode 100644
index 0000000..78a357b
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/ca0cd26baff2f2c0759e619800ebbe7314d2bb95
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/ca3e0d745c35d7cceb0f6e3f8a709eb658b7e5a8 b/fuzzers/corpora/commit_graph/ca3e0d745c35d7cceb0f6e3f8a709eb658b7e5a8
new file mode 100644
index 0000000..174863d
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/ca3e0d745c35d7cceb0f6e3f8a709eb658b7e5a8
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/cac667320e99e93a796bb89842de4675735eb4a4 b/fuzzers/corpora/commit_graph/cac667320e99e93a796bb89842de4675735eb4a4
new file mode 100644
index 0000000..20e9db9
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/cac667320e99e93a796bb89842de4675735eb4a4
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/cb41b00e9db33a07e27b3ee05d3bbecaf853b963 b/fuzzers/corpora/commit_graph/cb41b00e9db33a07e27b3ee05d3bbecaf853b963
new file mode 100644
index 0000000..612843d
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/cb41b00e9db33a07e27b3ee05d3bbecaf853b963
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/cbdbd3f320eee627097778f15b9fb2c1dc2bd15f b/fuzzers/corpora/commit_graph/cbdbd3f320eee627097778f15b9fb2c1dc2bd15f
new file mode 100644
index 0000000..a922018
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/cbdbd3f320eee627097778f15b9fb2c1dc2bd15f
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/cc7f114000c83abb2ab17f0deab6dcfc2acde7f5 b/fuzzers/corpora/commit_graph/cc7f114000c83abb2ab17f0deab6dcfc2acde7f5
new file mode 100644
index 0000000..7c9f84c
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/cc7f114000c83abb2ab17f0deab6dcfc2acde7f5
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/cc9bb93a6b7a1362a15f04898845dbe1447ec382 b/fuzzers/corpora/commit_graph/cc9bb93a6b7a1362a15f04898845dbe1447ec382
new file mode 100644
index 0000000..5dde225
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/cc9bb93a6b7a1362a15f04898845dbe1447ec382
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/cce7355f826bbcf3955394596d358abc7df6fe6f b/fuzzers/corpora/commit_graph/cce7355f826bbcf3955394596d358abc7df6fe6f
new file mode 100644
index 0000000..9b78038
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/cce7355f826bbcf3955394596d358abc7df6fe6f
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/cceff2878a558166fb5bf2a0354c1be31dcc4e21 b/fuzzers/corpora/commit_graph/cceff2878a558166fb5bf2a0354c1be31dcc4e21
new file mode 100644
index 0000000..94fec1b
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/cceff2878a558166fb5bf2a0354c1be31dcc4e21
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/cd96909f3ded7aa54bb2ffd2f2f47f8acc6f99e2 b/fuzzers/corpora/commit_graph/cd96909f3ded7aa54bb2ffd2f2f47f8acc6f99e2
new file mode 100644
index 0000000..22bbade
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/cd96909f3ded7aa54bb2ffd2f2f47f8acc6f99e2
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/cee9f69d7d1a227833fba127a529ea2a10341da3 b/fuzzers/corpora/commit_graph/cee9f69d7d1a227833fba127a529ea2a10341da3
new file mode 100644
index 0000000..2fff45e
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/cee9f69d7d1a227833fba127a529ea2a10341da3
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/d064f27a3109afde629165432f78f389da73ff07 b/fuzzers/corpora/commit_graph/d064f27a3109afde629165432f78f389da73ff07
new file mode 100644
index 0000000..e6d98c0
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/d064f27a3109afde629165432f78f389da73ff07
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/d07e3094f02b0c0e3bab370684c2d8c5634224d5 b/fuzzers/corpora/commit_graph/d07e3094f02b0c0e3bab370684c2d8c5634224d5
new file mode 100644
index 0000000..29d86aa
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/d07e3094f02b0c0e3bab370684c2d8c5634224d5
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/d0ba3413d5706de17de64824d78233d48c6efbec b/fuzzers/corpora/commit_graph/d0ba3413d5706de17de64824d78233d48c6efbec
new file mode 100644
index 0000000..96c8207
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/d0ba3413d5706de17de64824d78233d48c6efbec
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/d136511364a74973b009f2be9b021d4122f71a6c b/fuzzers/corpora/commit_graph/d136511364a74973b009f2be9b021d4122f71a6c
new file mode 100644
index 0000000..a663ab9
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/d136511364a74973b009f2be9b021d4122f71a6c
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/d1d215c40bcc8dd4ce02b0c0621e90b183b40b3e b/fuzzers/corpora/commit_graph/d1d215c40bcc8dd4ce02b0c0621e90b183b40b3e
new file mode 100644
index 0000000..a328d3e
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/d1d215c40bcc8dd4ce02b0c0621e90b183b40b3e
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/d1e35b137b2027b61def408f3f3c8cf9bcab274e b/fuzzers/corpora/commit_graph/d1e35b137b2027b61def408f3f3c8cf9bcab274e
new file mode 100644
index 0000000..79c43fd
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/d1e35b137b2027b61def408f3f3c8cf9bcab274e
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/d349d137e57fb1a60ab8babd20e2acedc7a9042e b/fuzzers/corpora/commit_graph/d349d137e57fb1a60ab8babd20e2acedc7a9042e
new file mode 100644
index 0000000..981aaf6
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/d349d137e57fb1a60ab8babd20e2acedc7a9042e
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/d3714ec4d3acc6262295b0fc99c6ba699f5bfe65 b/fuzzers/corpora/commit_graph/d3714ec4d3acc6262295b0fc99c6ba699f5bfe65
new file mode 100644
index 0000000..7f6e309
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/d3714ec4d3acc6262295b0fc99c6ba699f5bfe65
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/d419df696512216074f1c6b17ea1dfc81c0e6e20 b/fuzzers/corpora/commit_graph/d419df696512216074f1c6b17ea1dfc81c0e6e20
new file mode 100644
index 0000000..47c7019
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/d419df696512216074f1c6b17ea1dfc81c0e6e20
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/d49ad4fdafac251ceec32481826228c1698360aa b/fuzzers/corpora/commit_graph/d49ad4fdafac251ceec32481826228c1698360aa
new file mode 100644
index 0000000..9d4756c
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/d49ad4fdafac251ceec32481826228c1698360aa
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/d4f85ba549c87ccaba59971a25da7e07b57c9f4e b/fuzzers/corpora/commit_graph/d4f85ba549c87ccaba59971a25da7e07b57c9f4e
new file mode 100644
index 0000000..e6d72e7
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/d4f85ba549c87ccaba59971a25da7e07b57c9f4e
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/d51ade0715bcea7decee2a045934599a10c1b07a b/fuzzers/corpora/commit_graph/d51ade0715bcea7decee2a045934599a10c1b07a
new file mode 100644
index 0000000..2403b35
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/d51ade0715bcea7decee2a045934599a10c1b07a
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/d5447fb72c97462a3f47c8b2d55deb0afaa225f8 b/fuzzers/corpora/commit_graph/d5447fb72c97462a3f47c8b2d55deb0afaa225f8
new file mode 100644
index 0000000..07e14a4
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/d5447fb72c97462a3f47c8b2d55deb0afaa225f8
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/d6611a91c29291872ed2932455cb15ddb3801323 b/fuzzers/corpora/commit_graph/d6611a91c29291872ed2932455cb15ddb3801323
new file mode 100644
index 0000000..361431c
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/d6611a91c29291872ed2932455cb15ddb3801323
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/d676f5e7efd6de6f2e1773231479471d2bba7261 b/fuzzers/corpora/commit_graph/d676f5e7efd6de6f2e1773231479471d2bba7261
new file mode 100644
index 0000000..f9756ab
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/d676f5e7efd6de6f2e1773231479471d2bba7261
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/d6a21eaa08a957d8f428192e193c2508fca2c218 b/fuzzers/corpora/commit_graph/d6a21eaa08a957d8f428192e193c2508fca2c218
new file mode 100644
index 0000000..f845fbb
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/d6a21eaa08a957d8f428192e193c2508fca2c218
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/d778052a29539344a9e3144e262e68df9628ebde b/fuzzers/corpora/commit_graph/d778052a29539344a9e3144e262e68df9628ebde
new file mode 100644
index 0000000..beace90
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/d778052a29539344a9e3144e262e68df9628ebde
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/d884f6944adfff7cb41728062bf91cac5cdacfc9 b/fuzzers/corpora/commit_graph/d884f6944adfff7cb41728062bf91cac5cdacfc9
new file mode 100644
index 0000000..f6de651
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/d884f6944adfff7cb41728062bf91cac5cdacfc9
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/d89aae18d8e320bbae55eaae6a0514d7e005a883 b/fuzzers/corpora/commit_graph/d89aae18d8e320bbae55eaae6a0514d7e005a883
new file mode 100644
index 0000000..1f0bbf3
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/d89aae18d8e320bbae55eaae6a0514d7e005a883
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/d926fde818c63f7b34f38c9f018bc833bc0bf7e1 b/fuzzers/corpora/commit_graph/d926fde818c63f7b34f38c9f018bc833bc0bf7e1
new file mode 100644
index 0000000..fe1e274
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/d926fde818c63f7b34f38c9f018bc833bc0bf7e1
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/d9d542d7c56774143cb6362e5a63739055469349 b/fuzzers/corpora/commit_graph/d9d542d7c56774143cb6362e5a63739055469349
new file mode 100644
index 0000000..946106f
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/d9d542d7c56774143cb6362e5a63739055469349
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/da99bc9ce5b831f132dfb2eb11b8537e5cccfcd4 b/fuzzers/corpora/commit_graph/da99bc9ce5b831f132dfb2eb11b8537e5cccfcd4
new file mode 100644
index 0000000..91ed5b2
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/da99bc9ce5b831f132dfb2eb11b8537e5cccfcd4
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/dabff2729fa69ab507fb00b7392aee1262056a29 b/fuzzers/corpora/commit_graph/dabff2729fa69ab507fb00b7392aee1262056a29
new file mode 100644
index 0000000..9318cec
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/dabff2729fa69ab507fb00b7392aee1262056a29
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/dac4f4b91e33847bcedf7c66ef6e4ad0181e8ad8 b/fuzzers/corpora/commit_graph/dac4f4b91e33847bcedf7c66ef6e4ad0181e8ad8
new file mode 100644
index 0000000..9587c53
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/dac4f4b91e33847bcedf7c66ef6e4ad0181e8ad8
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/db10ff6d01c7a66aa1823b9f99193590ddce99c6 b/fuzzers/corpora/commit_graph/db10ff6d01c7a66aa1823b9f99193590ddce99c6
new file mode 100644
index 0000000..2d8d099
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/db10ff6d01c7a66aa1823b9f99193590ddce99c6
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/dbbda2208fa688a5275dda0d304630db01ca081d b/fuzzers/corpora/commit_graph/dbbda2208fa688a5275dda0d304630db01ca081d
new file mode 100644
index 0000000..7edfc3b
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/dbbda2208fa688a5275dda0d304630db01ca081d
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/dc47c5037be68a2747ff8a9fa450e1078a5ac5a5 b/fuzzers/corpora/commit_graph/dc47c5037be68a2747ff8a9fa450e1078a5ac5a5
new file mode 100644
index 0000000..e4ac972
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/dc47c5037be68a2747ff8a9fa450e1078a5ac5a5
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/dc760f136b123e38677aec72853e3365f08010fc b/fuzzers/corpora/commit_graph/dc760f136b123e38677aec72853e3365f08010fc
new file mode 100644
index 0000000..855c7b3
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/dc760f136b123e38677aec72853e3365f08010fc
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/dca41b901bf1612d4197e6a450366a00ac036ec3 b/fuzzers/corpora/commit_graph/dca41b901bf1612d4197e6a450366a00ac036ec3
new file mode 100644
index 0000000..9eec273
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/dca41b901bf1612d4197e6a450366a00ac036ec3
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/dca62f21fce50d1c8c51b82e0d7eeedc6746e652 b/fuzzers/corpora/commit_graph/dca62f21fce50d1c8c51b82e0d7eeedc6746e652
new file mode 100644
index 0000000..f8188c6
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/dca62f21fce50d1c8c51b82e0d7eeedc6746e652
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/dcc7e6c444f95b10d634b1137413824e2cd68f62 b/fuzzers/corpora/commit_graph/dcc7e6c444f95b10d634b1137413824e2cd68f62
new file mode 100644
index 0000000..247d648
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/dcc7e6c444f95b10d634b1137413824e2cd68f62
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/dcf4b6addda69040f792c9b860ade2af0b77a14c b/fuzzers/corpora/commit_graph/dcf4b6addda69040f792c9b860ade2af0b77a14c
new file mode 100644
index 0000000..6cbb0b7
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/dcf4b6addda69040f792c9b860ade2af0b77a14c
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/dd6178166ac1eed82d132fea491bcda0d953227c b/fuzzers/corpora/commit_graph/dd6178166ac1eed82d132fea491bcda0d953227c
new file mode 100644
index 0000000..4c13f99
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/dd6178166ac1eed82d132fea491bcda0d953227c
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/ddbd5d3074323ccd7cd70bf5de5a2f30de977d99 b/fuzzers/corpora/commit_graph/ddbd5d3074323ccd7cd70bf5de5a2f30de977d99
new file mode 100644
index 0000000..646febd
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/ddbd5d3074323ccd7cd70bf5de5a2f30de977d99
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/ddd8ec5632bf1b8153d03a4537d3d76517c497d5 b/fuzzers/corpora/commit_graph/ddd8ec5632bf1b8153d03a4537d3d76517c497d5
new file mode 100644
index 0000000..e948534
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/ddd8ec5632bf1b8153d03a4537d3d76517c497d5
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/de7a56f36e10d7b9ff43160b1cea3e76b24386d1 b/fuzzers/corpora/commit_graph/de7a56f36e10d7b9ff43160b1cea3e76b24386d1
new file mode 100644
index 0000000..6cae1f2
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/de7a56f36e10d7b9ff43160b1cea3e76b24386d1
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/defa60aa46ea5a47c09b6962b4e4296ef1bcad92 b/fuzzers/corpora/commit_graph/defa60aa46ea5a47c09b6962b4e4296ef1bcad92
new file mode 100644
index 0000000..2978755
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/defa60aa46ea5a47c09b6962b4e4296ef1bcad92
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/e0ae419425207832518d66c0ef35d11cbdc20361 b/fuzzers/corpora/commit_graph/e0ae419425207832518d66c0ef35d11cbdc20361
new file mode 100644
index 0000000..89404f4
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/e0ae419425207832518d66c0ef35d11cbdc20361
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/e0f519accbf15bc57a1bf1d7cc46d2a0b07a67f5 b/fuzzers/corpora/commit_graph/e0f519accbf15bc57a1bf1d7cc46d2a0b07a67f5
new file mode 100644
index 0000000..af59e9d
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/e0f519accbf15bc57a1bf1d7cc46d2a0b07a67f5
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/e128eff8ca7572d9bb0bfc84f64d79c52afc2c67 b/fuzzers/corpora/commit_graph/e128eff8ca7572d9bb0bfc84f64d79c52afc2c67
new file mode 100644
index 0000000..d963f77
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/e128eff8ca7572d9bb0bfc84f64d79c52afc2c67
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/e17fdc21ae03243bd1d31bb6301b4187cab6fe47 b/fuzzers/corpora/commit_graph/e17fdc21ae03243bd1d31bb6301b4187cab6fe47
new file mode 100644
index 0000000..381f8e1
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/e17fdc21ae03243bd1d31bb6301b4187cab6fe47
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/e340ace35a2db7f89d6aa21cc1300766a74be4e1 b/fuzzers/corpora/commit_graph/e340ace35a2db7f89d6aa21cc1300766a74be4e1
new file mode 100644
index 0000000..00cb20b
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/e340ace35a2db7f89d6aa21cc1300766a74be4e1
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/e36dfc11bcaab1e42df13924a2d7da024684db2e b/fuzzers/corpora/commit_graph/e36dfc11bcaab1e42df13924a2d7da024684db2e
new file mode 100644
index 0000000..25dd8ea
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/e36dfc11bcaab1e42df13924a2d7da024684db2e
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/e39e0c87ac5ce0b78c89ae2df84226baba666372 b/fuzzers/corpora/commit_graph/e39e0c87ac5ce0b78c89ae2df84226baba666372
new file mode 100644
index 0000000..acfc881
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/e39e0c87ac5ce0b78c89ae2df84226baba666372
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/e46b4666c6bfcd6f589ec3617a48cce9c968e833 b/fuzzers/corpora/commit_graph/e46b4666c6bfcd6f589ec3617a48cce9c968e833
new file mode 100644
index 0000000..92bca77
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/e46b4666c6bfcd6f589ec3617a48cce9c968e833
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/e57219555e11f9221d3166d5029ed2ad92300608 b/fuzzers/corpora/commit_graph/e57219555e11f9221d3166d5029ed2ad92300608
new file mode 100644
index 0000000..6f9d153
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/e57219555e11f9221d3166d5029ed2ad92300608
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/e58ce590c2454e7ebe18e0a31a943b0b754fbd13 b/fuzzers/corpora/commit_graph/e58ce590c2454e7ebe18e0a31a943b0b754fbd13
new file mode 100644
index 0000000..89d4790
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/e58ce590c2454e7ebe18e0a31a943b0b754fbd13
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/e595f8fef5c8014cb0867978c6580301078ca0d9 b/fuzzers/corpora/commit_graph/e595f8fef5c8014cb0867978c6580301078ca0d9
new file mode 100644
index 0000000..339b09e
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/e595f8fef5c8014cb0867978c6580301078ca0d9
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/e5b76398f60628e879328d7009b9fa89feea14cb b/fuzzers/corpora/commit_graph/e5b76398f60628e879328d7009b9fa89feea14cb
new file mode 100644
index 0000000..6ed637f
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/e5b76398f60628e879328d7009b9fa89feea14cb
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/e5cec0217eea93b18a59d76b0aed6b46b13fa6a9 b/fuzzers/corpora/commit_graph/e5cec0217eea93b18a59d76b0aed6b46b13fa6a9
new file mode 100644
index 0000000..7ae3eb4
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/e5cec0217eea93b18a59d76b0aed6b46b13fa6a9
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/e637b4e0b47d0d6cd870502e6a2d6a53bf917f73 b/fuzzers/corpora/commit_graph/e637b4e0b47d0d6cd870502e6a2d6a53bf917f73
new file mode 100644
index 0000000..f84ee8c
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/e637b4e0b47d0d6cd870502e6a2d6a53bf917f73
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/e7a6cb6e5a1552837fdbee9025fc48a9373f8564 b/fuzzers/corpora/commit_graph/e7a6cb6e5a1552837fdbee9025fc48a9373f8564
new file mode 100644
index 0000000..a5ae268
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/e7a6cb6e5a1552837fdbee9025fc48a9373f8564
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/e7f57c48016e1180c9af95acd34470881f10bd06 b/fuzzers/corpora/commit_graph/e7f57c48016e1180c9af95acd34470881f10bd06
new file mode 100644
index 0000000..07bbb9c
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/e7f57c48016e1180c9af95acd34470881f10bd06
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/e8253c668bfe37df5c5ada3226860cee74fb33a2 b/fuzzers/corpora/commit_graph/e8253c668bfe37df5c5ada3226860cee74fb33a2
new file mode 100644
index 0000000..0cb7581
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/e8253c668bfe37df5c5ada3226860cee74fb33a2
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/e8f9981443c34ece02bca3c66130f3429d7b3375 b/fuzzers/corpora/commit_graph/e8f9981443c34ece02bca3c66130f3429d7b3375
new file mode 100644
index 0000000..09fe9dd
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/e8f9981443c34ece02bca3c66130f3429d7b3375
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/e91ed5416bbcd1b03803197b99c08f42c9869139 b/fuzzers/corpora/commit_graph/e91ed5416bbcd1b03803197b99c08f42c9869139
new file mode 100644
index 0000000..3267ebb
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/e91ed5416bbcd1b03803197b99c08f42c9869139
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/e94201cfa88df7b198abd3abae9007a6780b52a7 b/fuzzers/corpora/commit_graph/e94201cfa88df7b198abd3abae9007a6780b52a7
new file mode 100644
index 0000000..0279a3c
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/e94201cfa88df7b198abd3abae9007a6780b52a7
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/e967bbd6a0d251ae62c9c38b784271d707f792c0 b/fuzzers/corpora/commit_graph/e967bbd6a0d251ae62c9c38b784271d707f792c0
new file mode 100644
index 0000000..2baf050
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/e967bbd6a0d251ae62c9c38b784271d707f792c0
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/ea01737ceed783b3e0f66d9d0c409cb496c1d526 b/fuzzers/corpora/commit_graph/ea01737ceed783b3e0f66d9d0c409cb496c1d526
new file mode 100644
index 0000000..75c988e
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/ea01737ceed783b3e0f66d9d0c409cb496c1d526
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/ea40f7879a58d1e52a46404c761f76a949e14a31 b/fuzzers/corpora/commit_graph/ea40f7879a58d1e52a46404c761f76a949e14a31
new file mode 100644
index 0000000..0fe4b30
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/ea40f7879a58d1e52a46404c761f76a949e14a31
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/ea5ad04a54f95963baea1f47845847626e08dd55 b/fuzzers/corpora/commit_graph/ea5ad04a54f95963baea1f47845847626e08dd55
new file mode 100644
index 0000000..79a1c4f
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/ea5ad04a54f95963baea1f47845847626e08dd55
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/ea608a401f54b0ca70e42b897f0c8ce6efdbc0ef b/fuzzers/corpora/commit_graph/ea608a401f54b0ca70e42b897f0c8ce6efdbc0ef
new file mode 100644
index 0000000..26c55fe
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/ea608a401f54b0ca70e42b897f0c8ce6efdbc0ef
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/eb8700d6b3728e6e70c2a0fe504543771639f2b6 b/fuzzers/corpora/commit_graph/eb8700d6b3728e6e70c2a0fe504543771639f2b6
new file mode 100644
index 0000000..cf35936
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/eb8700d6b3728e6e70c2a0fe504543771639f2b6
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/ec1f271b04c322353865f4819153d46df7def873 b/fuzzers/corpora/commit_graph/ec1f271b04c322353865f4819153d46df7def873
new file mode 100644
index 0000000..95cfa61
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/ec1f271b04c322353865f4819153d46df7def873
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/ee215536e7f0cfbd07b53dd65c5af9a604a01830 b/fuzzers/corpora/commit_graph/ee215536e7f0cfbd07b53dd65c5af9a604a01830
new file mode 100644
index 0000000..82241f0
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/ee215536e7f0cfbd07b53dd65c5af9a604a01830
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/ee4d4393d7d79b755f85ef5bf8f6e3d743bfa258 b/fuzzers/corpora/commit_graph/ee4d4393d7d79b755f85ef5bf8f6e3d743bfa258
new file mode 100644
index 0000000..3d0d7d4
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/ee4d4393d7d79b755f85ef5bf8f6e3d743bfa258
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/ee8099331b2c392e7e036ffcd4a9b36ec2c2082d b/fuzzers/corpora/commit_graph/ee8099331b2c392e7e036ffcd4a9b36ec2c2082d
new file mode 100644
index 0000000..4e8f26b
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/ee8099331b2c392e7e036ffcd4a9b36ec2c2082d
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/eede9da76db25513f8347f972e170102831de91a b/fuzzers/corpora/commit_graph/eede9da76db25513f8347f972e170102831de91a
new file mode 100644
index 0000000..d364605
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/eede9da76db25513f8347f972e170102831de91a
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/ef707cdeaa9548b6c820f769c1d8ad607b3c4514 b/fuzzers/corpora/commit_graph/ef707cdeaa9548b6c820f769c1d8ad607b3c4514
new file mode 100644
index 0000000..31daa3b
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/ef707cdeaa9548b6c820f769c1d8ad607b3c4514
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/ef98609d8196dc158365dfcbbc47e3d1699c50c2 b/fuzzers/corpora/commit_graph/ef98609d8196dc158365dfcbbc47e3d1699c50c2
new file mode 100644
index 0000000..6cac849
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/ef98609d8196dc158365dfcbbc47e3d1699c50c2
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/efa38b4269f978f3714b44b501831bea678244e0 b/fuzzers/corpora/commit_graph/efa38b4269f978f3714b44b501831bea678244e0
new file mode 100644
index 0000000..923b8c5
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/efa38b4269f978f3714b44b501831bea678244e0
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/efba428e29811d233720ccaaf41966a309312a29 b/fuzzers/corpora/commit_graph/efba428e29811d233720ccaaf41966a309312a29
new file mode 100644
index 0000000..b803c2e
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/efba428e29811d233720ccaaf41966a309312a29
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/efd514f056d8d83498b4724249c4623560e0390d b/fuzzers/corpora/commit_graph/efd514f056d8d83498b4724249c4623560e0390d
new file mode 100644
index 0000000..d0e1a33
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/efd514f056d8d83498b4724249c4623560e0390d
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/f00e449ba67ef15e7f29df1e6948c28155d72baa b/fuzzers/corpora/commit_graph/f00e449ba67ef15e7f29df1e6948c28155d72baa
new file mode 100644
index 0000000..32153f7
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/f00e449ba67ef15e7f29df1e6948c28155d72baa
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/f0a83929d588466051dced6eae0c387db307d646 b/fuzzers/corpora/commit_graph/f0a83929d588466051dced6eae0c387db307d646
new file mode 100644
index 0000000..fc53a85
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/f0a83929d588466051dced6eae0c387db307d646
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/f0e53b72e5d69467e7c014474028ea734f4fcb26 b/fuzzers/corpora/commit_graph/f0e53b72e5d69467e7c014474028ea734f4fcb26
new file mode 100644
index 0000000..bad38e8
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/f0e53b72e5d69467e7c014474028ea734f4fcb26
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/f186265b3f10f4383f4174e9fb74f0a0cdfa3fca b/fuzzers/corpora/commit_graph/f186265b3f10f4383f4174e9fb74f0a0cdfa3fca
new file mode 100644
index 0000000..2cc2dd0
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/f186265b3f10f4383f4174e9fb74f0a0cdfa3fca
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/f18932fcce5a9db5d6c8f59d622eabc25e255e12 b/fuzzers/corpora/commit_graph/f18932fcce5a9db5d6c8f59d622eabc25e255e12
new file mode 100644
index 0000000..85c6e0e
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/f18932fcce5a9db5d6c8f59d622eabc25e255e12
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/f2ea163bddb95d67597e2a747779ebf4651cb2a9 b/fuzzers/corpora/commit_graph/f2ea163bddb95d67597e2a747779ebf4651cb2a9
new file mode 100644
index 0000000..f974087
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/f2ea163bddb95d67597e2a747779ebf4651cb2a9
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/f2f7d48a6d86143ecb4969808d634163576065b1 b/fuzzers/corpora/commit_graph/f2f7d48a6d86143ecb4969808d634163576065b1
new file mode 100644
index 0000000..f2ad4a6
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/f2f7d48a6d86143ecb4969808d634163576065b1
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/f34a833faf2b0dcbae8aaad142c76c7c7e534e99 b/fuzzers/corpora/commit_graph/f34a833faf2b0dcbae8aaad142c76c7c7e534e99
new file mode 100644
index 0000000..2eaa521
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/f34a833faf2b0dcbae8aaad142c76c7c7e534e99
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/f5c044ce01645c069334698fb8c4750e44835912 b/fuzzers/corpora/commit_graph/f5c044ce01645c069334698fb8c4750e44835912
new file mode 100644
index 0000000..a67affa
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/f5c044ce01645c069334698fb8c4750e44835912
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/f680112645c2502f0612e9d017bbb50cb28affbf b/fuzzers/corpora/commit_graph/f680112645c2502f0612e9d017bbb50cb28affbf
new file mode 100644
index 0000000..dec09a8
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/f680112645c2502f0612e9d017bbb50cb28affbf
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/f6b778d1b34415a7715905f54968c8b6eb057912 b/fuzzers/corpora/commit_graph/f6b778d1b34415a7715905f54968c8b6eb057912
new file mode 100644
index 0000000..a93cb66
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/f6b778d1b34415a7715905f54968c8b6eb057912
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/f6ca6a62dc885c6b2a4b40c4aa1a7cb8118e30bb b/fuzzers/corpora/commit_graph/f6ca6a62dc885c6b2a4b40c4aa1a7cb8118e30bb
new file mode 100644
index 0000000..29ed04c
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/f6ca6a62dc885c6b2a4b40c4aa1a7cb8118e30bb
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/f733a8770c23fde182d2fef7e0d96e67244274d5 b/fuzzers/corpora/commit_graph/f733a8770c23fde182d2fef7e0d96e67244274d5
new file mode 100644
index 0000000..c6aa758
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/f733a8770c23fde182d2fef7e0d96e67244274d5
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/f8529ddf17d4505c0932c3d40abe33cbfd8c6f22 b/fuzzers/corpora/commit_graph/f8529ddf17d4505c0932c3d40abe33cbfd8c6f22
new file mode 100644
index 0000000..f004ecb
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/f8529ddf17d4505c0932c3d40abe33cbfd8c6f22
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/f96f8419a3fc3719ae86d64e1147e7b7f66a2470 b/fuzzers/corpora/commit_graph/f96f8419a3fc3719ae86d64e1147e7b7f66a2470
new file mode 100644
index 0000000..5dee3ca
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/f96f8419a3fc3719ae86d64e1147e7b7f66a2470
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/fae241a6c87af37781a3b49e534b7ddb6636eda8 b/fuzzers/corpora/commit_graph/fae241a6c87af37781a3b49e534b7ddb6636eda8
new file mode 100644
index 0000000..fc4e26b
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/fae241a6c87af37781a3b49e534b7ddb6636eda8
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/faf8817a04b77c6a976ab0a3d1e905f79bb7f799 b/fuzzers/corpora/commit_graph/faf8817a04b77c6a976ab0a3d1e905f79bb7f799
new file mode 100644
index 0000000..5164ecb
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/faf8817a04b77c6a976ab0a3d1e905f79bb7f799
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/fb3e769019fb25d384d4be9d38e4cbce00a6adbc b/fuzzers/corpora/commit_graph/fb3e769019fb25d384d4be9d38e4cbce00a6adbc
new file mode 100644
index 0000000..008337c
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/fb3e769019fb25d384d4be9d38e4cbce00a6adbc
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/fb9b4b2a46f1c65076340a7bd03b076eb101b760 b/fuzzers/corpora/commit_graph/fb9b4b2a46f1c65076340a7bd03b076eb101b760
new file mode 100644
index 0000000..8d0c735
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/fb9b4b2a46f1c65076340a7bd03b076eb101b760
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/fca9b0a398832c9ba02cdc811f625b97d5beb18e b/fuzzers/corpora/commit_graph/fca9b0a398832c9ba02cdc811f625b97d5beb18e
new file mode 100644
index 0000000..b2681f0
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/fca9b0a398832c9ba02cdc811f625b97d5beb18e
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/fcb1b42c706e61245d5e86f708be777ae63f2772 b/fuzzers/corpora/commit_graph/fcb1b42c706e61245d5e86f708be777ae63f2772
new file mode 100644
index 0000000..a98b661
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/fcb1b42c706e61245d5e86f708be777ae63f2772
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/fd6c463e7c30b0e51198c0d1ebbea25f20145e3f b/fuzzers/corpora/commit_graph/fd6c463e7c30b0e51198c0d1ebbea25f20145e3f
new file mode 100644
index 0000000..b253321
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/fd6c463e7c30b0e51198c0d1ebbea25f20145e3f
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/fdcbaa49097ad120c6d7709b29d5b65b8cf8e719 b/fuzzers/corpora/commit_graph/fdcbaa49097ad120c6d7709b29d5b65b8cf8e719
new file mode 100644
index 0000000..2167806
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/fdcbaa49097ad120c6d7709b29d5b65b8cf8e719
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/fe46775b28a2923b8770b44381552a8a1560d875 b/fuzzers/corpora/commit_graph/fe46775b28a2923b8770b44381552a8a1560d875
new file mode 100644
index 0000000..0acef6e
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/fe46775b28a2923b8770b44381552a8a1560d875
Binary files differ
diff --git a/fuzzers/corpora/commit_graph/ff04441135ef3308fec2687cf688069c6df8aa31 b/fuzzers/corpora/commit_graph/ff04441135ef3308fec2687cf688069c6df8aa31
new file mode 100644
index 0000000..33afa05
--- /dev/null
+++ b/fuzzers/corpora/commit_graph/ff04441135ef3308fec2687cf688069c6df8aa31
Binary files differ
diff --git a/fuzzers/corpora/config_file/git2.dat b/fuzzers/corpora/config_file/git2.dat
new file mode 100644
index 0000000..e556154
--- /dev/null
+++ b/fuzzers/corpora/config_file/git2.dat
@@ -0,0 +1,11 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+[remote "origin"]
+ url = git@github.com:libgit2/libgit2
+ fetch = +refs/heads/*:refs/remotes/origin/*
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/fuzzers/corpora/download_refs/clone.dat b/fuzzers/corpora/download_refs/clone.dat
new file mode 100644
index 0000000..f3e56b0
--- /dev/null
+++ b/fuzzers/corpora/download_refs/clone.dat
Binary files differ
diff --git a/fuzzers/corpora/midx/037cbbe0dc03807dd9d9e8629f1712d7df34ee18 b/fuzzers/corpora/midx/037cbbe0dc03807dd9d9e8629f1712d7df34ee18
new file mode 100644
index 0000000..2e5bda8
--- /dev/null
+++ b/fuzzers/corpora/midx/037cbbe0dc03807dd9d9e8629f1712d7df34ee18
Binary files differ
diff --git a/fuzzers/corpora/midx/039ee34fef8f323ed618a10abc0109df123d0cb5 b/fuzzers/corpora/midx/039ee34fef8f323ed618a10abc0109df123d0cb5
new file mode 100644
index 0000000..0c97a38
--- /dev/null
+++ b/fuzzers/corpora/midx/039ee34fef8f323ed618a10abc0109df123d0cb5
Binary files differ
diff --git a/fuzzers/corpora/midx/054ee2c82bdb6a170106eb5d35f21bde2119d584 b/fuzzers/corpora/midx/054ee2c82bdb6a170106eb5d35f21bde2119d584
new file mode 100644
index 0000000..f7711e4
--- /dev/null
+++ b/fuzzers/corpora/midx/054ee2c82bdb6a170106eb5d35f21bde2119d584
@@ -0,0 +1 @@
+ãMÿãa \ No newline at end of file
diff --git a/fuzzers/corpora/midx/055ca4cbc961ebf5fd5c922b4f73880d3fbfe39d b/fuzzers/corpora/midx/055ca4cbc961ebf5fd5c922b4f73880d3fbfe39d
new file mode 100644
index 0000000..adb91f9
--- /dev/null
+++ b/fuzzers/corpora/midx/055ca4cbc961ebf5fd5c922b4f73880d3fbfe39d
Binary files differ
diff --git a/fuzzers/corpora/midx/05c4e5eb1b97bc9b6973921fcb30d4c5e2eb79e4 b/fuzzers/corpora/midx/05c4e5eb1b97bc9b6973921fcb30d4c5e2eb79e4
new file mode 100644
index 0000000..1a53734
--- /dev/null
+++ b/fuzzers/corpora/midx/05c4e5eb1b97bc9b6973921fcb30d4c5e2eb79e4
Binary files differ
diff --git a/fuzzers/corpora/midx/0672eeda541a191cfc68d521a3c7ac0aac4057a6 b/fuzzers/corpora/midx/0672eeda541a191cfc68d521a3c7ac0aac4057a6
new file mode 100644
index 0000000..5b64955
--- /dev/null
+++ b/fuzzers/corpora/midx/0672eeda541a191cfc68d521a3c7ac0aac4057a6
Binary files differ
diff --git a/fuzzers/corpora/midx/06a58d1bd5562a668ebf01ef297fd774e0e587a6 b/fuzzers/corpora/midx/06a58d1bd5562a668ebf01ef297fd774e0e587a6
new file mode 100644
index 0000000..30e454a
--- /dev/null
+++ b/fuzzers/corpora/midx/06a58d1bd5562a668ebf01ef297fd774e0e587a6
Binary files differ
diff --git a/fuzzers/corpora/midx/06bf7c2461ae1049030f31b83ae76babfcc20c83 b/fuzzers/corpora/midx/06bf7c2461ae1049030f31b83ae76babfcc20c83
new file mode 100644
index 0000000..10751dc
--- /dev/null
+++ b/fuzzers/corpora/midx/06bf7c2461ae1049030f31b83ae76babfcc20c83
Binary files differ
diff --git a/fuzzers/corpora/midx/06c2db67ea65758d971346bfd6beaa61ed12f22c b/fuzzers/corpora/midx/06c2db67ea65758d971346bfd6beaa61ed12f22c
new file mode 100644
index 0000000..5641570
--- /dev/null
+++ b/fuzzers/corpora/midx/06c2db67ea65758d971346bfd6beaa61ed12f22c
Binary files differ
diff --git a/fuzzers/corpora/midx/07f88eefaf12609b7370fe78b82be2955f1b41fd b/fuzzers/corpora/midx/07f88eefaf12609b7370fe78b82be2955f1b41fd
new file mode 100644
index 0000000..8e09bb4
--- /dev/null
+++ b/fuzzers/corpora/midx/07f88eefaf12609b7370fe78b82be2955f1b41fd
Binary files differ
diff --git a/fuzzers/corpora/midx/08495c5f3828a56c167de870d385c46ffdce03c5 b/fuzzers/corpora/midx/08495c5f3828a56c167de870d385c46ffdce03c5
new file mode 100644
index 0000000..b4f5697
--- /dev/null
+++ b/fuzzers/corpora/midx/08495c5f3828a56c167de870d385c46ffdce03c5
Binary files differ
diff --git a/fuzzers/corpora/midx/08ec8594e5b35fb9e8e0726584f720154f0b2b5d b/fuzzers/corpora/midx/08ec8594e5b35fb9e8e0726584f720154f0b2b5d
new file mode 100644
index 0000000..772b4fd
--- /dev/null
+++ b/fuzzers/corpora/midx/08ec8594e5b35fb9e8e0726584f720154f0b2b5d
Binary files differ
diff --git a/fuzzers/corpora/midx/0903e378a493c596298074d6bff8de7f9ac25aa7 b/fuzzers/corpora/midx/0903e378a493c596298074d6bff8de7f9ac25aa7
new file mode 100644
index 0000000..34f5f3b
--- /dev/null
+++ b/fuzzers/corpora/midx/0903e378a493c596298074d6bff8de7f9ac25aa7
@@ -0,0 +1 @@
+ãü7 \ No newline at end of file
diff --git a/fuzzers/corpora/midx/09144a846f90f894049ef8a0ed0cc7ab4588dc6c b/fuzzers/corpora/midx/09144a846f90f894049ef8a0ed0cc7ab4588dc6c
new file mode 100644
index 0000000..ce7a43d
--- /dev/null
+++ b/fuzzers/corpora/midx/09144a846f90f894049ef8a0ed0cc7ab4588dc6c
@@ -0,0 +1 @@
+ÃØseed \ No newline at end of file
diff --git a/fuzzers/corpora/midx/09b40dd618373bfe4d3f2838f686a70f645e640b b/fuzzers/corpora/midx/09b40dd618373bfe4d3f2838f686a70f645e640b
new file mode 100644
index 0000000..09473b7
--- /dev/null
+++ b/fuzzers/corpora/midx/09b40dd618373bfe4d3f2838f686a70f645e640b
Binary files differ
diff --git a/fuzzers/corpora/midx/0a00ef44d234c18d365ec41724dbf4f21b09d0c5 b/fuzzers/corpora/midx/0a00ef44d234c18d365ec41724dbf4f21b09d0c5
new file mode 100644
index 0000000..861a986
--- /dev/null
+++ b/fuzzers/corpora/midx/0a00ef44d234c18d365ec41724dbf4f21b09d0c5
Binary files differ
diff --git a/fuzzers/corpora/midx/0a94e9f4a9b8cf56d52a9e3e7f2fa9a0a5c80d30 b/fuzzers/corpora/midx/0a94e9f4a9b8cf56d52a9e3e7f2fa9a0a5c80d30
new file mode 100644
index 0000000..11f08c0
--- /dev/null
+++ b/fuzzers/corpora/midx/0a94e9f4a9b8cf56d52a9e3e7f2fa9a0a5c80d30
Binary files differ
diff --git a/fuzzers/corpora/midx/0b35a123104b7872a7f15a710a23ef3594ace04d b/fuzzers/corpora/midx/0b35a123104b7872a7f15a710a23ef3594ace04d
new file mode 100644
index 0000000..eac151b
--- /dev/null
+++ b/fuzzers/corpora/midx/0b35a123104b7872a7f15a710a23ef3594ace04d
Binary files differ
diff --git a/fuzzers/corpora/midx/0c3d7e6be32c014ea873440b0f095961d391af1a b/fuzzers/corpora/midx/0c3d7e6be32c014ea873440b0f095961d391af1a
new file mode 100644
index 0000000..e9c6621
--- /dev/null
+++ b/fuzzers/corpora/midx/0c3d7e6be32c014ea873440b0f095961d391af1a
Binary files differ
diff --git a/fuzzers/corpora/midx/0c65de477b89afc312a7e89cde06f8a17f65bd54 b/fuzzers/corpora/midx/0c65de477b89afc312a7e89cde06f8a17f65bd54
new file mode 100644
index 0000000..8f4b25c
--- /dev/null
+++ b/fuzzers/corpora/midx/0c65de477b89afc312a7e89cde06f8a17f65bd54
Binary files differ
diff --git a/fuzzers/corpora/midx/0c81d0f368e979d2a0eb4598cbf1c9283936ba0c b/fuzzers/corpora/midx/0c81d0f368e979d2a0eb4598cbf1c9283936ba0c
new file mode 100644
index 0000000..a2dd163
--- /dev/null
+++ b/fuzzers/corpora/midx/0c81d0f368e979d2a0eb4598cbf1c9283936ba0c
Binary files differ
diff --git a/fuzzers/corpora/midx/0c95a44ae995070a5279a2991c36de2251081460 b/fuzzers/corpora/midx/0c95a44ae995070a5279a2991c36de2251081460
new file mode 100644
index 0000000..821b07b
--- /dev/null
+++ b/fuzzers/corpora/midx/0c95a44ae995070a5279a2991c36de2251081460
Binary files differ
diff --git a/fuzzers/corpora/midx/0de38e2cb13167df7d5a882570633596f64bc4f4 b/fuzzers/corpora/midx/0de38e2cb13167df7d5a882570633596f64bc4f4
new file mode 100644
index 0000000..80a27f6
--- /dev/null
+++ b/fuzzers/corpora/midx/0de38e2cb13167df7d5a882570633596f64bc4f4
Binary files differ
diff --git a/fuzzers/corpora/midx/0de96aa193045315457ade63c2614610c503db9e b/fuzzers/corpora/midx/0de96aa193045315457ade63c2614610c503db9e
new file mode 100644
index 0000000..342c6c9
--- /dev/null
+++ b/fuzzers/corpora/midx/0de96aa193045315457ade63c2614610c503db9e
Binary files differ
diff --git a/fuzzers/corpora/midx/0e02deca2b16d71f8637933bd56dc8592ed9fdff b/fuzzers/corpora/midx/0e02deca2b16d71f8637933bd56dc8592ed9fdff
new file mode 100644
index 0000000..d3b5fe2
--- /dev/null
+++ b/fuzzers/corpora/midx/0e02deca2b16d71f8637933bd56dc8592ed9fdff
@@ -0,0 +1 @@
+ãH \ No newline at end of file
diff --git a/fuzzers/corpora/midx/0e44fc9176fe2c1bae4209369da5bc057f54b2d2 b/fuzzers/corpora/midx/0e44fc9176fe2c1bae4209369da5bc057f54b2d2
new file mode 100644
index 0000000..7469002
--- /dev/null
+++ b/fuzzers/corpora/midx/0e44fc9176fe2c1bae4209369da5bc057f54b2d2
Binary files differ
diff --git a/fuzzers/corpora/midx/0f6c5fc9b6a68835364bbef8937560ee5a481938 b/fuzzers/corpora/midx/0f6c5fc9b6a68835364bbef8937560ee5a481938
new file mode 100644
index 0000000..309e2d8
--- /dev/null
+++ b/fuzzers/corpora/midx/0f6c5fc9b6a68835364bbef8937560ee5a481938
@@ -0,0 +1 @@
+©* \ No newline at end of file
diff --git a/fuzzers/corpora/midx/10d542d5c7da060a5f0664e21478a0d598e29844 b/fuzzers/corpora/midx/10d542d5c7da060a5f0664e21478a0d598e29844
new file mode 100644
index 0000000..777d32a
--- /dev/null
+++ b/fuzzers/corpora/midx/10d542d5c7da060a5f0664e21478a0d598e29844
Binary files differ
diff --git a/fuzzers/corpora/midx/118735f7786ae6b4c2f6b36314ab1f2cafe9c3c8 b/fuzzers/corpora/midx/118735f7786ae6b4c2f6b36314ab1f2cafe9c3c8
new file mode 100644
index 0000000..a91dbc9
--- /dev/null
+++ b/fuzzers/corpora/midx/118735f7786ae6b4c2f6b36314ab1f2cafe9c3c8
@@ -0,0 +1 @@
+ãÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛŠÛÛÛÛÛÛÛÛÛÛËÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛËÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ \ No newline at end of file
diff --git a/fuzzers/corpora/midx/119b58eb353aa344264005016297fb911510ea0d b/fuzzers/corpora/midx/119b58eb353aa344264005016297fb911510ea0d
new file mode 100644
index 0000000..b4883c2
--- /dev/null
+++ b/fuzzers/corpora/midx/119b58eb353aa344264005016297fb911510ea0d
Binary files differ
diff --git a/fuzzers/corpora/midx/127626832c30d6d94bb29384c0fde7ac6bca75ec b/fuzzers/corpora/midx/127626832c30d6d94bb29384c0fde7ac6bca75ec
new file mode 100644
index 0000000..3a0fe06
--- /dev/null
+++ b/fuzzers/corpora/midx/127626832c30d6d94bb29384c0fde7ac6bca75ec
Binary files differ
diff --git a/fuzzers/corpora/midx/1284f1a162588d4de87ca17149474644a0863b27 b/fuzzers/corpora/midx/1284f1a162588d4de87ca17149474644a0863b27
new file mode 100644
index 0000000..03a9f97
--- /dev/null
+++ b/fuzzers/corpora/midx/1284f1a162588d4de87ca17149474644a0863b27
Binary files differ
diff --git a/fuzzers/corpora/midx/1458599f19f1a967c787562bf8ec3e67677da9c8 b/fuzzers/corpora/midx/1458599f19f1a967c787562bf8ec3e67677da9c8
new file mode 100644
index 0000000..e9dc36e
--- /dev/null
+++ b/fuzzers/corpora/midx/1458599f19f1a967c787562bf8ec3e67677da9c8
Binary files differ
diff --git a/fuzzers/corpora/midx/14ba6c1ddd05b22c6f2eae5f894721cd3efcbb16 b/fuzzers/corpora/midx/14ba6c1ddd05b22c6f2eae5f894721cd3efcbb16
new file mode 100644
index 0000000..e0f281c
--- /dev/null
+++ b/fuzzers/corpora/midx/14ba6c1ddd05b22c6f2eae5f894721cd3efcbb16
Binary files differ
diff --git a/fuzzers/corpora/midx/158cdc0a5aa005f167a8588d0beed9eee4aa36f2 b/fuzzers/corpora/midx/158cdc0a5aa005f167a8588d0beed9eee4aa36f2
new file mode 100644
index 0000000..98de253
--- /dev/null
+++ b/fuzzers/corpora/midx/158cdc0a5aa005f167a8588d0beed9eee4aa36f2
Binary files differ
diff --git a/fuzzers/corpora/midx/15dafc6fa800327f694b5eb2fc4ebf007be9c117 b/fuzzers/corpora/midx/15dafc6fa800327f694b5eb2fc4ebf007be9c117
new file mode 100644
index 0000000..ff07ca2
--- /dev/null
+++ b/fuzzers/corpora/midx/15dafc6fa800327f694b5eb2fc4ebf007be9c117
Binary files differ
diff --git a/fuzzers/corpora/midx/1613ed4b2e909871f8897fd6354ff80a4ac12f87 b/fuzzers/corpora/midx/1613ed4b2e909871f8897fd6354ff80a4ac12f87
new file mode 100644
index 0000000..1afb32e
--- /dev/null
+++ b/fuzzers/corpora/midx/1613ed4b2e909871f8897fd6354ff80a4ac12f87
Binary files differ
diff --git a/fuzzers/corpora/midx/16daf4cb967bb47cf4566e9be7d96d3125bd2e12 b/fuzzers/corpora/midx/16daf4cb967bb47cf4566e9be7d96d3125bd2e12
new file mode 100644
index 0000000..729b22a
--- /dev/null
+++ b/fuzzers/corpora/midx/16daf4cb967bb47cf4566e9be7d96d3125bd2e12
Binary files differ
diff --git a/fuzzers/corpora/midx/177783dce78efee878f6d6020fd87ab107bb11a1 b/fuzzers/corpora/midx/177783dce78efee878f6d6020fd87ab107bb11a1
new file mode 100644
index 0000000..cc1810e
--- /dev/null
+++ b/fuzzers/corpora/midx/177783dce78efee878f6d6020fd87ab107bb11a1
Binary files differ
diff --git a/fuzzers/corpora/midx/17a5090400a1fedc45070e4b530a26f320a89097 b/fuzzers/corpora/midx/17a5090400a1fedc45070e4b530a26f320a89097
new file mode 100644
index 0000000..7255c85
--- /dev/null
+++ b/fuzzers/corpora/midx/17a5090400a1fedc45070e4b530a26f320a89097
Binary files differ
diff --git a/fuzzers/corpora/midx/17dea5cfa498f4d54384289a1daed0d15a85e7cc b/fuzzers/corpora/midx/17dea5cfa498f4d54384289a1daed0d15a85e7cc
new file mode 100644
index 0000000..00572bb
--- /dev/null
+++ b/fuzzers/corpora/midx/17dea5cfa498f4d54384289a1daed0d15a85e7cc
Binary files differ
diff --git a/fuzzers/corpora/midx/17e76ae5b54316679981113f52c27edc87dbcdea b/fuzzers/corpora/midx/17e76ae5b54316679981113f52c27edc87dbcdea
new file mode 100644
index 0000000..5f0e0d5
--- /dev/null
+++ b/fuzzers/corpora/midx/17e76ae5b54316679981113f52c27edc87dbcdea
Binary files differ
diff --git a/fuzzers/corpora/midx/191ed5e9334693c53fc843f692dbc3c2c63e8241 b/fuzzers/corpora/midx/191ed5e9334693c53fc843f692dbc3c2c63e8241
new file mode 100644
index 0000000..17fddd8
--- /dev/null
+++ b/fuzzers/corpora/midx/191ed5e9334693c53fc843f692dbc3c2c63e8241
Binary files differ
diff --git a/fuzzers/corpora/midx/196a0ba4edb5bbfd66c1cda669abf0496573cf0e b/fuzzers/corpora/midx/196a0ba4edb5bbfd66c1cda669abf0496573cf0e
new file mode 100644
index 0000000..4d68586
--- /dev/null
+++ b/fuzzers/corpora/midx/196a0ba4edb5bbfd66c1cda669abf0496573cf0e
Binary files differ
diff --git a/fuzzers/corpora/midx/19742b6cee79fa5bf9b27dcbe367c82d0a399904 b/fuzzers/corpora/midx/19742b6cee79fa5bf9b27dcbe367c82d0a399904
new file mode 100644
index 0000000..39e7054
--- /dev/null
+++ b/fuzzers/corpora/midx/19742b6cee79fa5bf9b27dcbe367c82d0a399904
Binary files differ
diff --git a/fuzzers/corpora/midx/1a21d7581d3b0a8d67934d48e91d45bd818836e8 b/fuzzers/corpora/midx/1a21d7581d3b0a8d67934d48e91d45bd818836e8
new file mode 100644
index 0000000..616b808
--- /dev/null
+++ b/fuzzers/corpora/midx/1a21d7581d3b0a8d67934d48e91d45bd818836e8
Binary files differ
diff --git a/fuzzers/corpora/midx/1b2f96c5d75c7ca09b1012be4e6c3a7b248ed924 b/fuzzers/corpora/midx/1b2f96c5d75c7ca09b1012be4e6c3a7b248ed924
new file mode 100644
index 0000000..148aad9
--- /dev/null
+++ b/fuzzers/corpora/midx/1b2f96c5d75c7ca09b1012be4e6c3a7b248ed924
Binary files differ
diff --git a/fuzzers/corpora/midx/1b604ff0683d0e23dc7945431f6514ba30d6ca0d b/fuzzers/corpora/midx/1b604ff0683d0e23dc7945431f6514ba30d6ca0d
new file mode 100644
index 0000000..1456048
--- /dev/null
+++ b/fuzzers/corpora/midx/1b604ff0683d0e23dc7945431f6514ba30d6ca0d
Binary files differ
diff --git a/fuzzers/corpora/midx/1b771dd5bd3ae2b1c42c4efe6c896c83b88a4f91 b/fuzzers/corpora/midx/1b771dd5bd3ae2b1c42c4efe6c896c83b88a4f91
new file mode 100644
index 0000000..92f6376
--- /dev/null
+++ b/fuzzers/corpora/midx/1b771dd5bd3ae2b1c42c4efe6c896c83b88a4f91
Binary files differ
diff --git a/fuzzers/corpora/midx/1b793a4ee73fa8bf423da70fca5f39ef32a8d288 b/fuzzers/corpora/midx/1b793a4ee73fa8bf423da70fca5f39ef32a8d288
new file mode 100644
index 0000000..8c4478c
--- /dev/null
+++ b/fuzzers/corpora/midx/1b793a4ee73fa8bf423da70fca5f39ef32a8d288
Binary files differ
diff --git a/fuzzers/corpora/midx/1c9599ce00978780519272be279f508c402e3268 b/fuzzers/corpora/midx/1c9599ce00978780519272be279f508c402e3268
new file mode 100644
index 0000000..c70f128
--- /dev/null
+++ b/fuzzers/corpora/midx/1c9599ce00978780519272be279f508c402e3268
@@ -0,0 +1 @@
+ãÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏÏ \ No newline at end of file
diff --git a/fuzzers/corpora/midx/1cc0068f9f63b12dc8fdd38f9ebfb96c42664e95 b/fuzzers/corpora/midx/1cc0068f9f63b12dc8fdd38f9ebfb96c42664e95
new file mode 100644
index 0000000..c41e6a8
--- /dev/null
+++ b/fuzzers/corpora/midx/1cc0068f9f63b12dc8fdd38f9ebfb96c42664e95
Binary files differ
diff --git a/fuzzers/corpora/midx/1de6e1f5579da6e5c40f4ee23ac62e29e4f90541 b/fuzzers/corpora/midx/1de6e1f5579da6e5c40f4ee23ac62e29e4f90541
new file mode 100644
index 0000000..d8c3fbf
--- /dev/null
+++ b/fuzzers/corpora/midx/1de6e1f5579da6e5c40f4ee23ac62e29e4f90541
Binary files differ
diff --git a/fuzzers/corpora/midx/1eec93083260ebfab5f4c6d13119cf27c374b7e9 b/fuzzers/corpora/midx/1eec93083260ebfab5f4c6d13119cf27c374b7e9
new file mode 100644
index 0000000..5f9c27f
--- /dev/null
+++ b/fuzzers/corpora/midx/1eec93083260ebfab5f4c6d13119cf27c374b7e9
Binary files differ
diff --git a/fuzzers/corpora/midx/1f0f574addd363d1fed131289f301c5c033aaa8f b/fuzzers/corpora/midx/1f0f574addd363d1fed131289f301c5c033aaa8f
new file mode 100644
index 0000000..12cbb60
--- /dev/null
+++ b/fuzzers/corpora/midx/1f0f574addd363d1fed131289f301c5c033aaa8f
Binary files differ
diff --git a/fuzzers/corpora/midx/1f3e85cffdb545c1ba7c8bbe1ca18ec13e341038 b/fuzzers/corpora/midx/1f3e85cffdb545c1ba7c8bbe1ca18ec13e341038
new file mode 100644
index 0000000..36b2d5e
--- /dev/null
+++ b/fuzzers/corpora/midx/1f3e85cffdb545c1ba7c8bbe1ca18ec13e341038
Binary files differ
diff --git a/fuzzers/corpora/midx/1f6a66a92d5f083a73a82280a0a1ae0800e56ae5 b/fuzzers/corpora/midx/1f6a66a92d5f083a73a82280a0a1ae0800e56ae5
new file mode 100644
index 0000000..ea50ac7
--- /dev/null
+++ b/fuzzers/corpora/midx/1f6a66a92d5f083a73a82280a0a1ae0800e56ae5
Binary files differ
diff --git a/fuzzers/corpora/midx/208e422322052efcdaeb1a09bbf06c5f476b8efc b/fuzzers/corpora/midx/208e422322052efcdaeb1a09bbf06c5f476b8efc
new file mode 100644
index 0000000..ce98394
--- /dev/null
+++ b/fuzzers/corpora/midx/208e422322052efcdaeb1a09bbf06c5f476b8efc
Binary files differ
diff --git a/fuzzers/corpora/midx/22d75b2c3937957b14eded621b638283ce7fe1fe b/fuzzers/corpora/midx/22d75b2c3937957b14eded621b638283ce7fe1fe
new file mode 100644
index 0000000..0aa34c4
--- /dev/null
+++ b/fuzzers/corpora/midx/22d75b2c3937957b14eded621b638283ce7fe1fe
Binary files differ
diff --git a/fuzzers/corpora/midx/22f90ff68166a409acf8f89bf60a31ad2c64ab37 b/fuzzers/corpora/midx/22f90ff68166a409acf8f89bf60a31ad2c64ab37
new file mode 100644
index 0000000..cdecbb6
--- /dev/null
+++ b/fuzzers/corpora/midx/22f90ff68166a409acf8f89bf60a31ad2c64ab37
Binary files differ
diff --git a/fuzzers/corpora/midx/236ebad449d432b039d6ace1f250ef1fa2aa364d b/fuzzers/corpora/midx/236ebad449d432b039d6ace1f250ef1fa2aa364d
new file mode 100644
index 0000000..0e213a1
--- /dev/null
+++ b/fuzzers/corpora/midx/236ebad449d432b039d6ace1f250ef1fa2aa364d
Binary files differ
diff --git a/fuzzers/corpora/midx/252a4e4bf7fb21792ec2f305fd88fa7c9168505f b/fuzzers/corpora/midx/252a4e4bf7fb21792ec2f305fd88fa7c9168505f
new file mode 100644
index 0000000..b23555c
--- /dev/null
+++ b/fuzzers/corpora/midx/252a4e4bf7fb21792ec2f305fd88fa7c9168505f
Binary files differ
diff --git a/fuzzers/corpora/midx/259e1faf7b7f12250062d36ded1193a9dbcae0f5 b/fuzzers/corpora/midx/259e1faf7b7f12250062d36ded1193a9dbcae0f5
new file mode 100644
index 0000000..f2f1de5
--- /dev/null
+++ b/fuzzers/corpora/midx/259e1faf7b7f12250062d36ded1193a9dbcae0f5
Binary files differ
diff --git a/fuzzers/corpora/midx/25ad3dfb655ab4c853d0d277872310d9579c8e83 b/fuzzers/corpora/midx/25ad3dfb655ab4c853d0d277872310d9579c8e83
new file mode 100644
index 0000000..325c385
--- /dev/null
+++ b/fuzzers/corpora/midx/25ad3dfb655ab4c853d0d277872310d9579c8e83
Binary files differ
diff --git a/fuzzers/corpora/midx/26210f5b8fdbf81b312feea48659ec6e2e083c0b b/fuzzers/corpora/midx/26210f5b8fdbf81b312feea48659ec6e2e083c0b
new file mode 100644
index 0000000..479a291
--- /dev/null
+++ b/fuzzers/corpora/midx/26210f5b8fdbf81b312feea48659ec6e2e083c0b
Binary files differ
diff --git a/fuzzers/corpora/midx/263a2a0915be36d8cb2bc30774e37e0344262347 b/fuzzers/corpora/midx/263a2a0915be36d8cb2bc30774e37e0344262347
new file mode 100644
index 0000000..8a90761
--- /dev/null
+++ b/fuzzers/corpora/midx/263a2a0915be36d8cb2bc30774e37e0344262347
Binary files differ
diff --git a/fuzzers/corpora/midx/2679bfbc2f4f7c10a304245da4e156e235377b63 b/fuzzers/corpora/midx/2679bfbc2f4f7c10a304245da4e156e235377b63
new file mode 100644
index 0000000..dbf598d
--- /dev/null
+++ b/fuzzers/corpora/midx/2679bfbc2f4f7c10a304245da4e156e235377b63
Binary files differ
diff --git a/fuzzers/corpora/midx/270b7b567a63dd94bb2a90448bbbc2e2bbc4a261 b/fuzzers/corpora/midx/270b7b567a63dd94bb2a90448bbbc2e2bbc4a261
new file mode 100644
index 0000000..0c08b8c
--- /dev/null
+++ b/fuzzers/corpora/midx/270b7b567a63dd94bb2a90448bbbc2e2bbc4a261
Binary files differ
diff --git a/fuzzers/corpora/midx/271cd5c5e254a293d115588ee130040ef26b59e8 b/fuzzers/corpora/midx/271cd5c5e254a293d115588ee130040ef26b59e8
new file mode 100644
index 0000000..89309dc
--- /dev/null
+++ b/fuzzers/corpora/midx/271cd5c5e254a293d115588ee130040ef26b59e8
Binary files differ
diff --git a/fuzzers/corpora/midx/27839a8035b48f8c19ab073808a03a95b6a90cc3 b/fuzzers/corpora/midx/27839a8035b48f8c19ab073808a03a95b6a90cc3
new file mode 100644
index 0000000..4595069
--- /dev/null
+++ b/fuzzers/corpora/midx/27839a8035b48f8c19ab073808a03a95b6a90cc3
Binary files differ
diff --git a/fuzzers/corpora/midx/2810c385c9285cbdb65bcdab5175999fe547cbad b/fuzzers/corpora/midx/2810c385c9285cbdb65bcdab5175999fe547cbad
new file mode 100644
index 0000000..0d3fc30
--- /dev/null
+++ b/fuzzers/corpora/midx/2810c385c9285cbdb65bcdab5175999fe547cbad
Binary files differ
diff --git a/fuzzers/corpora/midx/28afaf4ab4b092ccf987661e58009f96126bba63 b/fuzzers/corpora/midx/28afaf4ab4b092ccf987661e58009f96126bba63
new file mode 100644
index 0000000..6a29fb1
--- /dev/null
+++ b/fuzzers/corpora/midx/28afaf4ab4b092ccf987661e58009f96126bba63
Binary files differ
diff --git a/fuzzers/corpora/midx/29f842e86a891cff9f0b44c8aec19f7e23a47000 b/fuzzers/corpora/midx/29f842e86a891cff9f0b44c8aec19f7e23a47000
new file mode 100644
index 0000000..5644eb8
--- /dev/null
+++ b/fuzzers/corpora/midx/29f842e86a891cff9f0b44c8aec19f7e23a47000
Binary files differ
diff --git a/fuzzers/corpora/midx/2aa2549f617f19402d1feac61d4ca1af3545cc8a b/fuzzers/corpora/midx/2aa2549f617f19402d1feac61d4ca1af3545cc8a
new file mode 100644
index 0000000..7704585
--- /dev/null
+++ b/fuzzers/corpora/midx/2aa2549f617f19402d1feac61d4ca1af3545cc8a
Binary files differ
diff --git a/fuzzers/corpora/midx/2b73c2902eda6da41321493601003b29c3445713 b/fuzzers/corpora/midx/2b73c2902eda6da41321493601003b29c3445713
new file mode 100644
index 0000000..402d066
--- /dev/null
+++ b/fuzzers/corpora/midx/2b73c2902eda6da41321493601003b29c3445713
Binary files differ
diff --git a/fuzzers/corpora/midx/2bcec1274c5e7b2d7a581d851c016ef5b553fabe b/fuzzers/corpora/midx/2bcec1274c5e7b2d7a581d851c016ef5b553fabe
new file mode 100644
index 0000000..43e18c8
--- /dev/null
+++ b/fuzzers/corpora/midx/2bcec1274c5e7b2d7a581d851c016ef5b553fabe
Binary files differ
diff --git a/fuzzers/corpora/midx/2dd9a328b6d4e29e42684347be5c4b7cd7dc1a66 b/fuzzers/corpora/midx/2dd9a328b6d4e29e42684347be5c4b7cd7dc1a66
new file mode 100644
index 0000000..a3e6da5
--- /dev/null
+++ b/fuzzers/corpora/midx/2dd9a328b6d4e29e42684347be5c4b7cd7dc1a66
Binary files differ
diff --git a/fuzzers/corpora/midx/2ddc17ee7ee89bb7dbc673328d5f3e55c76e686e b/fuzzers/corpora/midx/2ddc17ee7ee89bb7dbc673328d5f3e55c76e686e
new file mode 100644
index 0000000..7b789f3
--- /dev/null
+++ b/fuzzers/corpora/midx/2ddc17ee7ee89bb7dbc673328d5f3e55c76e686e
Binary files differ
diff --git a/fuzzers/corpora/midx/2f71d5e99dc93618ed99fdb7c244a8f5e4a7eb4a b/fuzzers/corpora/midx/2f71d5e99dc93618ed99fdb7c244a8f5e4a7eb4a
new file mode 100644
index 0000000..7d2f004
--- /dev/null
+++ b/fuzzers/corpora/midx/2f71d5e99dc93618ed99fdb7c244a8f5e4a7eb4a
Binary files differ
diff --git a/fuzzers/corpora/midx/2f7cd0154d71a83e7b104670b2a77fbd285ffde2 b/fuzzers/corpora/midx/2f7cd0154d71a83e7b104670b2a77fbd285ffde2
new file mode 100644
index 0000000..645a39b
--- /dev/null
+++ b/fuzzers/corpora/midx/2f7cd0154d71a83e7b104670b2a77fbd285ffde2
Binary files differ
diff --git a/fuzzers/corpora/midx/2f9d40ef790f5213234e95d123dce942b2d1d389 b/fuzzers/corpora/midx/2f9d40ef790f5213234e95d123dce942b2d1d389
new file mode 100644
index 0000000..bca2348
--- /dev/null
+++ b/fuzzers/corpora/midx/2f9d40ef790f5213234e95d123dce942b2d1d389
Binary files differ
diff --git a/fuzzers/corpora/midx/31577bacbca7017308d2a0c9ebfdd4fce513bbe4 b/fuzzers/corpora/midx/31577bacbca7017308d2a0c9ebfdd4fce513bbe4
new file mode 100644
index 0000000..72972b1
--- /dev/null
+++ b/fuzzers/corpora/midx/31577bacbca7017308d2a0c9ebfdd4fce513bbe4
Binary files differ
diff --git a/fuzzers/corpora/midx/3278f1bab88b80597d0066812d49f8bd3c7b1dcf b/fuzzers/corpora/midx/3278f1bab88b80597d0066812d49f8bd3c7b1dcf
new file mode 100644
index 0000000..4177feb
--- /dev/null
+++ b/fuzzers/corpora/midx/3278f1bab88b80597d0066812d49f8bd3c7b1dcf
Binary files differ
diff --git a/fuzzers/corpora/midx/328160cae6235605ff70951a2f6ac669ba7bb397 b/fuzzers/corpora/midx/328160cae6235605ff70951a2f6ac669ba7bb397
new file mode 100644
index 0000000..1585907
--- /dev/null
+++ b/fuzzers/corpora/midx/328160cae6235605ff70951a2f6ac669ba7bb397
Binary files differ
diff --git a/fuzzers/corpora/midx/337ed1bf91701a4c8926840259077e55938c6efc b/fuzzers/corpora/midx/337ed1bf91701a4c8926840259077e55938c6efc
new file mode 100644
index 0000000..915128f
--- /dev/null
+++ b/fuzzers/corpora/midx/337ed1bf91701a4c8926840259077e55938c6efc
Binary files differ
diff --git a/fuzzers/corpora/midx/33a97d83ff7a774797b1751ea4bffbb4a22c58d9 b/fuzzers/corpora/midx/33a97d83ff7a774797b1751ea4bffbb4a22c58d9
new file mode 100644
index 0000000..852d8dc
--- /dev/null
+++ b/fuzzers/corpora/midx/33a97d83ff7a774797b1751ea4bffbb4a22c58d9
Binary files differ
diff --git a/fuzzers/corpora/midx/341021da9516401cf364ed2b7dfdda346db04f2f b/fuzzers/corpora/midx/341021da9516401cf364ed2b7dfdda346db04f2f
new file mode 100644
index 0000000..13c21ab
--- /dev/null
+++ b/fuzzers/corpora/midx/341021da9516401cf364ed2b7dfdda346db04f2f
Binary files differ
diff --git a/fuzzers/corpora/midx/341773a439cdecc58f55fb205ac584cd93ffe0f2 b/fuzzers/corpora/midx/341773a439cdecc58f55fb205ac584cd93ffe0f2
new file mode 100644
index 0000000..0446a88
--- /dev/null
+++ b/fuzzers/corpora/midx/341773a439cdecc58f55fb205ac584cd93ffe0f2
@@ -0,0 +1 @@
+ãyyyyyššyyyyyyyyÅÅ \ No newline at end of file
diff --git a/fuzzers/corpora/midx/366091157510e40bca08fc2102b9018ccf4697de b/fuzzers/corpora/midx/366091157510e40bca08fc2102b9018ccf4697de
new file mode 100644
index 0000000..f2148a1
--- /dev/null
+++ b/fuzzers/corpora/midx/366091157510e40bca08fc2102b9018ccf4697de
Binary files differ
diff --git a/fuzzers/corpora/midx/37096157e2f9f2ec8e0b97b21d335bd653f3edbd b/fuzzers/corpora/midx/37096157e2f9f2ec8e0b97b21d335bd653f3edbd
new file mode 100644
index 0000000..03600aa
--- /dev/null
+++ b/fuzzers/corpora/midx/37096157e2f9f2ec8e0b97b21d335bd653f3edbd
Binary files differ
diff --git a/fuzzers/corpora/midx/373a74b8613d09babcb567f91047e7b556a8de90 b/fuzzers/corpora/midx/373a74b8613d09babcb567f91047e7b556a8de90
new file mode 100644
index 0000000..9427eb0
--- /dev/null
+++ b/fuzzers/corpora/midx/373a74b8613d09babcb567f91047e7b556a8de90
Binary files differ
diff --git a/fuzzers/corpora/midx/3748b07ee7bec7bdd202ee14222cefca182417d1 b/fuzzers/corpora/midx/3748b07ee7bec7bdd202ee14222cefca182417d1
new file mode 100644
index 0000000..9699411
--- /dev/null
+++ b/fuzzers/corpora/midx/3748b07ee7bec7bdd202ee14222cefca182417d1
Binary files differ
diff --git a/fuzzers/corpora/midx/38b7906b9f956dca01dc92d0a901388ec1cbc8b1 b/fuzzers/corpora/midx/38b7906b9f956dca01dc92d0a901388ec1cbc8b1
new file mode 100644
index 0000000..d7b26a3
--- /dev/null
+++ b/fuzzers/corpora/midx/38b7906b9f956dca01dc92d0a901388ec1cbc8b1
Binary files differ
diff --git a/fuzzers/corpora/midx/38ddf3424559f1a6e7687eff8469a358184b833b b/fuzzers/corpora/midx/38ddf3424559f1a6e7687eff8469a358184b833b
new file mode 100644
index 0000000..972dd75
--- /dev/null
+++ b/fuzzers/corpora/midx/38ddf3424559f1a6e7687eff8469a358184b833b
@@ -0,0 +1 @@
+ðã½D \ No newline at end of file
diff --git a/fuzzers/corpora/midx/38e31d0a7dcc3835ce1a4afeeda8446fb3d7ed73 b/fuzzers/corpora/midx/38e31d0a7dcc3835ce1a4afeeda8446fb3d7ed73
new file mode 100644
index 0000000..2afdc8f
--- /dev/null
+++ b/fuzzers/corpora/midx/38e31d0a7dcc3835ce1a4afeeda8446fb3d7ed73
Binary files differ
diff --git a/fuzzers/corpora/midx/3955ec4497b226391ef9eb40f38af6dee4fa26b7 b/fuzzers/corpora/midx/3955ec4497b226391ef9eb40f38af6dee4fa26b7
new file mode 100644
index 0000000..cff10d8
--- /dev/null
+++ b/fuzzers/corpora/midx/3955ec4497b226391ef9eb40f38af6dee4fa26b7
Binary files differ
diff --git a/fuzzers/corpora/midx/3b6b424342133feb0f587f22bcd8f21595c004e5 b/fuzzers/corpora/midx/3b6b424342133feb0f587f22bcd8f21595c004e5
new file mode 100644
index 0000000..9f2bea4
--- /dev/null
+++ b/fuzzers/corpora/midx/3b6b424342133feb0f587f22bcd8f21595c004e5
Binary files differ
diff --git a/fuzzers/corpora/midx/3bb71f41200e0ebf8d19532e7d6e384c48aa2d03 b/fuzzers/corpora/midx/3bb71f41200e0ebf8d19532e7d6e384c48aa2d03
new file mode 100644
index 0000000..28fa133
--- /dev/null
+++ b/fuzzers/corpora/midx/3bb71f41200e0ebf8d19532e7d6e384c48aa2d03
Binary files differ
diff --git a/fuzzers/corpora/midx/3c5a6063797aba9ffe5ea9903bbfcf87193652d3 b/fuzzers/corpora/midx/3c5a6063797aba9ffe5ea9903bbfcf87193652d3
new file mode 100644
index 0000000..4a5725e
--- /dev/null
+++ b/fuzzers/corpora/midx/3c5a6063797aba9ffe5ea9903bbfcf87193652d3
Binary files differ
diff --git a/fuzzers/corpora/midx/3dfb9927d959f2462f6944a32d080b60a265abfe b/fuzzers/corpora/midx/3dfb9927d959f2462f6944a32d080b60a265abfe
new file mode 100644
index 0000000..c234cd1
--- /dev/null
+++ b/fuzzers/corpora/midx/3dfb9927d959f2462f6944a32d080b60a265abfe
Binary files differ
diff --git a/fuzzers/corpora/midx/3e19242a63ec92a0c3f7138ebbc31bfe7cbd40cd b/fuzzers/corpora/midx/3e19242a63ec92a0c3f7138ebbc31bfe7cbd40cd
new file mode 100644
index 0000000..c0e1b1d
--- /dev/null
+++ b/fuzzers/corpora/midx/3e19242a63ec92a0c3f7138ebbc31bfe7cbd40cd
Binary files differ
diff --git a/fuzzers/corpora/midx/3ec53ce4ea1f41f040a3c2beed929572af95dd43 b/fuzzers/corpora/midx/3ec53ce4ea1f41f040a3c2beed929572af95dd43
new file mode 100644
index 0000000..9a7ec6f
--- /dev/null
+++ b/fuzzers/corpora/midx/3ec53ce4ea1f41f040a3c2beed929572af95dd43
Binary files differ
diff --git a/fuzzers/corpora/midx/3f0762fdf49a58c0d8fd6683964a85caddee391b b/fuzzers/corpora/midx/3f0762fdf49a58c0d8fd6683964a85caddee391b
new file mode 100644
index 0000000..aec9b3d
--- /dev/null
+++ b/fuzzers/corpora/midx/3f0762fdf49a58c0d8fd6683964a85caddee391b
Binary files differ
diff --git a/fuzzers/corpora/midx/3f71ae863c4e9bac98e49a554b8ec4d78b17492d b/fuzzers/corpora/midx/3f71ae863c4e9bac98e49a554b8ec4d78b17492d
new file mode 100644
index 0000000..a9e06c7
--- /dev/null
+++ b/fuzzers/corpora/midx/3f71ae863c4e9bac98e49a554b8ec4d78b17492d
@@ -0,0 +1 @@
+ãïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïï*ïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïéïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïï \ No newline at end of file
diff --git a/fuzzers/corpora/midx/3f9df30bfb66a28fbe6f1951ef7ae4ca9f19fdf2 b/fuzzers/corpora/midx/3f9df30bfb66a28fbe6f1951ef7ae4ca9f19fdf2
new file mode 100644
index 0000000..ebb1cde
--- /dev/null
+++ b/fuzzers/corpora/midx/3f9df30bfb66a28fbe6f1951ef7ae4ca9f19fdf2
Binary files differ
diff --git a/fuzzers/corpora/midx/3fabb14670c008c22094c1d7cd7b1e23b4c48b3d b/fuzzers/corpora/midx/3fabb14670c008c22094c1d7cd7b1e23b4c48b3d
new file mode 100644
index 0000000..1d2d082
--- /dev/null
+++ b/fuzzers/corpora/midx/3fabb14670c008c22094c1d7cd7b1e23b4c48b3d
Binary files differ
diff --git a/fuzzers/corpora/midx/408fba9c66c5d1deb31e4c69f1dd0677844dbc1b b/fuzzers/corpora/midx/408fba9c66c5d1deb31e4c69f1dd0677844dbc1b
new file mode 100644
index 0000000..1cded07
--- /dev/null
+++ b/fuzzers/corpora/midx/408fba9c66c5d1deb31e4c69f1dd0677844dbc1b
Binary files differ
diff --git a/fuzzers/corpora/midx/40ca8645081087e950ad61bccf8d43450366356e b/fuzzers/corpora/midx/40ca8645081087e950ad61bccf8d43450366356e
new file mode 100644
index 0000000..834daf8
--- /dev/null
+++ b/fuzzers/corpora/midx/40ca8645081087e950ad61bccf8d43450366356e
Binary files differ
diff --git a/fuzzers/corpora/midx/412faec949b9d04498de939561664ee559a583a7 b/fuzzers/corpora/midx/412faec949b9d04498de939561664ee559a583a7
new file mode 100644
index 0000000..f15b10e
--- /dev/null
+++ b/fuzzers/corpora/midx/412faec949b9d04498de939561664ee559a583a7
@@ -0,0 +1 @@
+ïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïñïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïçïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïÿÿÿÿ \ No newline at end of file
diff --git a/fuzzers/corpora/midx/4148bd5336f89e73b2a5416c67d491c0ec4a2b7d b/fuzzers/corpora/midx/4148bd5336f89e73b2a5416c67d491c0ec4a2b7d
new file mode 100644
index 0000000..894ac62
--- /dev/null
+++ b/fuzzers/corpora/midx/4148bd5336f89e73b2a5416c67d491c0ec4a2b7d
Binary files differ
diff --git a/fuzzers/corpora/midx/41933e61fa20fbe2b190f9ae7ceae4a4b1220021 b/fuzzers/corpora/midx/41933e61fa20fbe2b190f9ae7ceae4a4b1220021
new file mode 100644
index 0000000..727789d
--- /dev/null
+++ b/fuzzers/corpora/midx/41933e61fa20fbe2b190f9ae7ceae4a4b1220021
Binary files differ
diff --git a/fuzzers/corpora/midx/423d90f3fc7ddc146095ec5a4b4f455aa876b69b b/fuzzers/corpora/midx/423d90f3fc7ddc146095ec5a4b4f455aa876b69b
new file mode 100644
index 0000000..fc73bd0
--- /dev/null
+++ b/fuzzers/corpora/midx/423d90f3fc7ddc146095ec5a4b4f455aa876b69b
Binary files differ
diff --git a/fuzzers/corpora/midx/42a6c52249aa12cfef1db1bf302a483a01c972f3 b/fuzzers/corpora/midx/42a6c52249aa12cfef1db1bf302a483a01c972f3
new file mode 100644
index 0000000..e35ab98
--- /dev/null
+++ b/fuzzers/corpora/midx/42a6c52249aa12cfef1db1bf302a483a01c972f3
Binary files differ
diff --git a/fuzzers/corpora/midx/42a82726f0e70da9b87b6c52bc1b3415576025f2 b/fuzzers/corpora/midx/42a82726f0e70da9b87b6c52bc1b3415576025f2
new file mode 100644
index 0000000..46117b4
--- /dev/null
+++ b/fuzzers/corpora/midx/42a82726f0e70da9b87b6c52bc1b3415576025f2
Binary files differ
diff --git a/fuzzers/corpora/midx/4458e19f99e38c61ad9792b0b3bf8ac79f8236f1 b/fuzzers/corpora/midx/4458e19f99e38c61ad9792b0b3bf8ac79f8236f1
new file mode 100644
index 0000000..80407ab
--- /dev/null
+++ b/fuzzers/corpora/midx/4458e19f99e38c61ad9792b0b3bf8ac79f8236f1
@@ -0,0 +1 @@
+ã]]ÿÿÿÿÿÿs4 \ No newline at end of file
diff --git a/fuzzers/corpora/midx/44a4411a8d6ed67ee3ea61d91d2afafe89295b0f b/fuzzers/corpora/midx/44a4411a8d6ed67ee3ea61d91d2afafe89295b0f
new file mode 100644
index 0000000..e9933b1
--- /dev/null
+++ b/fuzzers/corpora/midx/44a4411a8d6ed67ee3ea61d91d2afafe89295b0f
Binary files differ
diff --git a/fuzzers/corpora/midx/44e04754d1b6c0c045e05509dd7033d19a926b10 b/fuzzers/corpora/midx/44e04754d1b6c0c045e05509dd7033d19a926b10
new file mode 100644
index 0000000..5fe8e2e
--- /dev/null
+++ b/fuzzers/corpora/midx/44e04754d1b6c0c045e05509dd7033d19a926b10
Binary files differ
diff --git a/fuzzers/corpora/midx/45259e9f0a2cc7739a94eccaafb37c1570f73884 b/fuzzers/corpora/midx/45259e9f0a2cc7739a94eccaafb37c1570f73884
new file mode 100644
index 0000000..90134d1
--- /dev/null
+++ b/fuzzers/corpora/midx/45259e9f0a2cc7739a94eccaafb37c1570f73884
Binary files differ
diff --git a/fuzzers/corpora/midx/46c0d7e952200cabc08b9cd776a9f6759f4208c3 b/fuzzers/corpora/midx/46c0d7e952200cabc08b9cd776a9f6759f4208c3
new file mode 100644
index 0000000..e3a7a83
--- /dev/null
+++ b/fuzzers/corpora/midx/46c0d7e952200cabc08b9cd776a9f6759f4208c3
Binary files differ
diff --git a/fuzzers/corpora/midx/46deac8631633ea3c32005124e20a2bc2bbabade b/fuzzers/corpora/midx/46deac8631633ea3c32005124e20a2bc2bbabade
new file mode 100644
index 0000000..5f54eac
--- /dev/null
+++ b/fuzzers/corpora/midx/46deac8631633ea3c32005124e20a2bc2bbabade
Binary files differ
diff --git a/fuzzers/corpora/midx/46e7edf6e9d6cbcdabde3b48f1c4efd93be40348 b/fuzzers/corpora/midx/46e7edf6e9d6cbcdabde3b48f1c4efd93be40348
new file mode 100644
index 0000000..0314f2c
--- /dev/null
+++ b/fuzzers/corpora/midx/46e7edf6e9d6cbcdabde3b48f1c4efd93be40348
Binary files differ
diff --git a/fuzzers/corpora/midx/46fe9556c28c94f7321baa2519a3cbeabbd54d09 b/fuzzers/corpora/midx/46fe9556c28c94f7321baa2519a3cbeabbd54d09
new file mode 100644
index 0000000..75e8e6f
--- /dev/null
+++ b/fuzzers/corpora/midx/46fe9556c28c94f7321baa2519a3cbeabbd54d09
Binary files differ
diff --git a/fuzzers/corpora/midx/49223681729e73b48b26a2262e4a66b2ba00e176 b/fuzzers/corpora/midx/49223681729e73b48b26a2262e4a66b2ba00e176
new file mode 100644
index 0000000..3068c35
--- /dev/null
+++ b/fuzzers/corpora/midx/49223681729e73b48b26a2262e4a66b2ba00e176
Binary files differ
diff --git a/fuzzers/corpora/midx/499e61b689f6cc7e4efb0631684739c2a6f97c7d b/fuzzers/corpora/midx/499e61b689f6cc7e4efb0631684739c2a6f97c7d
new file mode 100644
index 0000000..d3c735b
--- /dev/null
+++ b/fuzzers/corpora/midx/499e61b689f6cc7e4efb0631684739c2a6f97c7d
Binary files differ
diff --git a/fuzzers/corpora/midx/4a06ad8c4d717bd048a7a1315a3d609d70f0162d b/fuzzers/corpora/midx/4a06ad8c4d717bd048a7a1315a3d609d70f0162d
new file mode 100644
index 0000000..caef168
--- /dev/null
+++ b/fuzzers/corpora/midx/4a06ad8c4d717bd048a7a1315a3d609d70f0162d
Binary files differ
diff --git a/fuzzers/corpora/midx/4adb7d4791a4c6370478dff2eb987d715554bf09 b/fuzzers/corpora/midx/4adb7d4791a4c6370478dff2eb987d715554bf09
new file mode 100644
index 0000000..ced1477
--- /dev/null
+++ b/fuzzers/corpora/midx/4adb7d4791a4c6370478dff2eb987d715554bf09
Binary files differ
diff --git a/fuzzers/corpora/midx/4b01c479cdc9b750a31d5e7ac5004309222d218d b/fuzzers/corpora/midx/4b01c479cdc9b750a31d5e7ac5004309222d218d
new file mode 100644
index 0000000..4ea5a88
--- /dev/null
+++ b/fuzzers/corpora/midx/4b01c479cdc9b750a31d5e7ac5004309222d218d
Binary files differ
diff --git a/fuzzers/corpora/midx/4bce7460a6becba6d26984bb438d7d3aa4e4fc56 b/fuzzers/corpora/midx/4bce7460a6becba6d26984bb438d7d3aa4e4fc56
new file mode 100644
index 0000000..41c7c3a
--- /dev/null
+++ b/fuzzers/corpora/midx/4bce7460a6becba6d26984bb438d7d3aa4e4fc56
Binary files differ
diff --git a/fuzzers/corpora/midx/4cc96483b6800dda296f00887b12a35154115090 b/fuzzers/corpora/midx/4cc96483b6800dda296f00887b12a35154115090
new file mode 100644
index 0000000..4f0179d
--- /dev/null
+++ b/fuzzers/corpora/midx/4cc96483b6800dda296f00887b12a35154115090
Binary files differ
diff --git a/fuzzers/corpora/midx/4f3aa59bae0619c9a06b631d9cb7767591810ab0 b/fuzzers/corpora/midx/4f3aa59bae0619c9a06b631d9cb7767591810ab0
new file mode 100644
index 0000000..0a67723
--- /dev/null
+++ b/fuzzers/corpora/midx/4f3aa59bae0619c9a06b631d9cb7767591810ab0
Binary files differ
diff --git a/fuzzers/corpora/midx/501840d963cedd2945018de59e0202444d7ebf4b b/fuzzers/corpora/midx/501840d963cedd2945018de59e0202444d7ebf4b
new file mode 100644
index 0000000..cd26169
--- /dev/null
+++ b/fuzzers/corpora/midx/501840d963cedd2945018de59e0202444d7ebf4b
Binary files differ
diff --git a/fuzzers/corpora/midx/50479958c030d1addceb1ca8c27f24447e555e65 b/fuzzers/corpora/midx/50479958c030d1addceb1ca8c27f24447e555e65
new file mode 100644
index 0000000..22159a4
--- /dev/null
+++ b/fuzzers/corpora/midx/50479958c030d1addceb1ca8c27f24447e555e65
Binary files differ
diff --git a/fuzzers/corpora/midx/508ba8ef164a809f739834a39d690e700101a7a1 b/fuzzers/corpora/midx/508ba8ef164a809f739834a39d690e700101a7a1
new file mode 100644
index 0000000..7cf01e1
--- /dev/null
+++ b/fuzzers/corpora/midx/508ba8ef164a809f739834a39d690e700101a7a1
Binary files differ
diff --git a/fuzzers/corpora/midx/521d345313812e54bc6c944485e19dbb39a87768 b/fuzzers/corpora/midx/521d345313812e54bc6c944485e19dbb39a87768
new file mode 100644
index 0000000..6e9550f
--- /dev/null
+++ b/fuzzers/corpora/midx/521d345313812e54bc6c944485e19dbb39a87768
Binary files differ
diff --git a/fuzzers/corpora/midx/5369d74ac157f85b597c1b28bbd6768105e9327b b/fuzzers/corpora/midx/5369d74ac157f85b597c1b28bbd6768105e9327b
new file mode 100644
index 0000000..bda1f8c
--- /dev/null
+++ b/fuzzers/corpora/midx/5369d74ac157f85b597c1b28bbd6768105e9327b
Binary files differ
diff --git a/fuzzers/corpora/midx/53997b0146ff49bfe464be203b130a67ea93fd26 b/fuzzers/corpora/midx/53997b0146ff49bfe464be203b130a67ea93fd26
new file mode 100644
index 0000000..12ea4cd
--- /dev/null
+++ b/fuzzers/corpora/midx/53997b0146ff49bfe464be203b130a67ea93fd26
Binary files differ
diff --git a/fuzzers/corpora/midx/560ea8bd7d11b00e0d21631b6d9ec7e63f0a5286 b/fuzzers/corpora/midx/560ea8bd7d11b00e0d21631b6d9ec7e63f0a5286
new file mode 100644
index 0000000..0c984e1
--- /dev/null
+++ b/fuzzers/corpora/midx/560ea8bd7d11b00e0d21631b6d9ec7e63f0a5286
Binary files differ
diff --git a/fuzzers/corpora/midx/5682ebc6878e247ce9bc636d34ada6ad338fcaf0 b/fuzzers/corpora/midx/5682ebc6878e247ce9bc636d34ada6ad338fcaf0
new file mode 100644
index 0000000..1b88140
--- /dev/null
+++ b/fuzzers/corpora/midx/5682ebc6878e247ce9bc636d34ada6ad338fcaf0
Binary files differ
diff --git a/fuzzers/corpora/midx/5762abb5234edd913754b69e1ab03274c711ee68 b/fuzzers/corpora/midx/5762abb5234edd913754b69e1ab03274c711ee68
new file mode 100644
index 0000000..6685722
--- /dev/null
+++ b/fuzzers/corpora/midx/5762abb5234edd913754b69e1ab03274c711ee68
Binary files differ
diff --git a/fuzzers/corpora/midx/579406f055070559bda3c6120107feb3e637c481 b/fuzzers/corpora/midx/579406f055070559bda3c6120107feb3e637c481
new file mode 100644
index 0000000..be7a59b
--- /dev/null
+++ b/fuzzers/corpora/midx/579406f055070559bda3c6120107feb3e637c481
@@ -0,0 +1,2 @@
+ÿ&ã
+ÿÿ)Å \ No newline at end of file
diff --git a/fuzzers/corpora/midx/5837d16af4a9c1f2616467cc4aa9ec8836e05c58 b/fuzzers/corpora/midx/5837d16af4a9c1f2616467cc4aa9ec8836e05c58
new file mode 100644
index 0000000..69bf0eb
--- /dev/null
+++ b/fuzzers/corpora/midx/5837d16af4a9c1f2616467cc4aa9ec8836e05c58
Binary files differ
diff --git a/fuzzers/corpora/midx/58901e865fe20b9fa136cca4b253d3ae73c2b78e b/fuzzers/corpora/midx/58901e865fe20b9fa136cca4b253d3ae73c2b78e
new file mode 100644
index 0000000..c360520
--- /dev/null
+++ b/fuzzers/corpora/midx/58901e865fe20b9fa136cca4b253d3ae73c2b78e
Binary files differ
diff --git a/fuzzers/corpora/midx/58a87098a14572e46b53c87340083f999d8fcfc2 b/fuzzers/corpora/midx/58a87098a14572e46b53c87340083f999d8fcfc2
new file mode 100644
index 0000000..f3711cd
--- /dev/null
+++ b/fuzzers/corpora/midx/58a87098a14572e46b53c87340083f999d8fcfc2
Binary files differ
diff --git a/fuzzers/corpora/midx/59ae139a21448e0eb7371ddc6ef57f0c9dfe9c85 b/fuzzers/corpora/midx/59ae139a21448e0eb7371ddc6ef57f0c9dfe9c85
new file mode 100644
index 0000000..953072c
--- /dev/null
+++ b/fuzzers/corpora/midx/59ae139a21448e0eb7371ddc6ef57f0c9dfe9c85
Binary files differ
diff --git a/fuzzers/corpora/midx/5a7e81419f895168c555ac9b4e75a1ad4f04b34a b/fuzzers/corpora/midx/5a7e81419f895168c555ac9b4e75a1ad4f04b34a
new file mode 100644
index 0000000..c6b2c58
--- /dev/null
+++ b/fuzzers/corpora/midx/5a7e81419f895168c555ac9b4e75a1ad4f04b34a
Binary files differ
diff --git a/fuzzers/corpora/midx/5b848c1f56a150d64020e9b0bb398a286dca4096 b/fuzzers/corpora/midx/5b848c1f56a150d64020e9b0bb398a286dca4096
new file mode 100644
index 0000000..17e91c1
--- /dev/null
+++ b/fuzzers/corpora/midx/5b848c1f56a150d64020e9b0bb398a286dca4096
Binary files differ
diff --git a/fuzzers/corpora/midx/5bd311bd846336149b2815666052fdb7e8bf2ea6 b/fuzzers/corpora/midx/5bd311bd846336149b2815666052fdb7e8bf2ea6
new file mode 100644
index 0000000..ccfa796
--- /dev/null
+++ b/fuzzers/corpora/midx/5bd311bd846336149b2815666052fdb7e8bf2ea6
Binary files differ
diff --git a/fuzzers/corpora/midx/5ce77eb98473a2e01d04909939edf7aabef5762c b/fuzzers/corpora/midx/5ce77eb98473a2e01d04909939edf7aabef5762c
new file mode 100644
index 0000000..b8ed8ee
--- /dev/null
+++ b/fuzzers/corpora/midx/5ce77eb98473a2e01d04909939edf7aabef5762c
Binary files differ
diff --git a/fuzzers/corpora/midx/5e5cd5819811507ac69bd8abad27433ccd6b7521 b/fuzzers/corpora/midx/5e5cd5819811507ac69bd8abad27433ccd6b7521
new file mode 100644
index 0000000..9069e16
--- /dev/null
+++ b/fuzzers/corpora/midx/5e5cd5819811507ac69bd8abad27433ccd6b7521
Binary files differ
diff --git a/fuzzers/corpora/midx/5ea114ae3dbb140364000c416152b0f32ce3de23 b/fuzzers/corpora/midx/5ea114ae3dbb140364000c416152b0f32ce3de23
new file mode 100644
index 0000000..2c03944
--- /dev/null
+++ b/fuzzers/corpora/midx/5ea114ae3dbb140364000c416152b0f32ce3de23
Binary files differ
diff --git a/fuzzers/corpora/midx/5f181bb0a79603c84534a9b8e37ecdeb1d2aeeb5 b/fuzzers/corpora/midx/5f181bb0a79603c84534a9b8e37ecdeb1d2aeeb5
new file mode 100644
index 0000000..c1a826f
--- /dev/null
+++ b/fuzzers/corpora/midx/5f181bb0a79603c84534a9b8e37ecdeb1d2aeeb5
@@ -0,0 +1 @@
+ãÂë®<™“ V¨Ý`oÓ¤Ük @rÜshuffleéDHE-PSK-ARÿÿÿÿÿÿÿ'MIDÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛßÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛàÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÙÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÙÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ[ÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛÛ \ No newline at end of file
diff --git a/fuzzers/corpora/midx/5f428ce1169e28353cedb8be3e2f6edd0ef354e4 b/fuzzers/corpora/midx/5f428ce1169e28353cedb8be3e2f6edd0ef354e4
new file mode 100644
index 0000000..e2b0852
--- /dev/null
+++ b/fuzzers/corpora/midx/5f428ce1169e28353cedb8be3e2f6edd0ef354e4
Binary files differ
diff --git a/fuzzers/corpora/midx/5f9bc7729dc331e3c4d8e52df0688abad6d4aee8 b/fuzzers/corpora/midx/5f9bc7729dc331e3c4d8e52df0688abad6d4aee8
new file mode 100644
index 0000000..31b96f9
--- /dev/null
+++ b/fuzzers/corpora/midx/5f9bc7729dc331e3c4d8e52df0688abad6d4aee8
Binary files differ
diff --git a/fuzzers/corpora/midx/619527e1d650cd1c26e9bc61e424c9fdc04b17b9 b/fuzzers/corpora/midx/619527e1d650cd1c26e9bc61e424c9fdc04b17b9
new file mode 100644
index 0000000..87c36d7
--- /dev/null
+++ b/fuzzers/corpora/midx/619527e1d650cd1c26e9bc61e424c9fdc04b17b9
Binary files differ
diff --git a/fuzzers/corpora/midx/625d3676de25188865e05db2a3933c38508406fc b/fuzzers/corpora/midx/625d3676de25188865e05db2a3933c38508406fc
new file mode 100644
index 0000000..95c2db2
--- /dev/null
+++ b/fuzzers/corpora/midx/625d3676de25188865e05db2a3933c38508406fc
Binary files differ
diff --git a/fuzzers/corpora/midx/6368569cfde7fbe369a0ee4695fa4d5a7d7887a6 b/fuzzers/corpora/midx/6368569cfde7fbe369a0ee4695fa4d5a7d7887a6
new file mode 100644
index 0000000..47ffb54
--- /dev/null
+++ b/fuzzers/corpora/midx/6368569cfde7fbe369a0ee4695fa4d5a7d7887a6
Binary files differ
diff --git a/fuzzers/corpora/midx/6388fe4d630064ea1ea33aa85381d9c82e328e95 b/fuzzers/corpora/midx/6388fe4d630064ea1ea33aa85381d9c82e328e95
new file mode 100644
index 0000000..85cf1e1
--- /dev/null
+++ b/fuzzers/corpora/midx/6388fe4d630064ea1ea33aa85381d9c82e328e95
Binary files differ
diff --git a/fuzzers/corpora/midx/64cff4e110f0bcb3ea833c1afda6e27a57dac0bc b/fuzzers/corpora/midx/64cff4e110f0bcb3ea833c1afda6e27a57dac0bc
new file mode 100644
index 0000000..29ac450
--- /dev/null
+++ b/fuzzers/corpora/midx/64cff4e110f0bcb3ea833c1afda6e27a57dac0bc
Binary files differ
diff --git a/fuzzers/corpora/midx/66449b87ce47b681c6326f337bebf03366a0ee99 b/fuzzers/corpora/midx/66449b87ce47b681c6326f337bebf03366a0ee99
new file mode 100644
index 0000000..a2b3b25
--- /dev/null
+++ b/fuzzers/corpora/midx/66449b87ce47b681c6326f337bebf03366a0ee99
Binary files differ
diff --git a/fuzzers/corpora/midx/666a779eed16847c6930a71c0547a34e52db409e b/fuzzers/corpora/midx/666a779eed16847c6930a71c0547a34e52db409e
new file mode 100644
index 0000000..ed9e0d0
--- /dev/null
+++ b/fuzzers/corpora/midx/666a779eed16847c6930a71c0547a34e52db409e
Binary files differ
diff --git a/fuzzers/corpora/midx/66ae3584497a1823a955c33e5bc53f7434c13e49 b/fuzzers/corpora/midx/66ae3584497a1823a955c33e5bc53f7434c13e49
new file mode 100644
index 0000000..0d5dcff
--- /dev/null
+++ b/fuzzers/corpora/midx/66ae3584497a1823a955c33e5bc53f7434c13e49
Binary files differ
diff --git a/fuzzers/corpora/midx/66e238a6ad70fb30c82171ff1b73ea71b4379355 b/fuzzers/corpora/midx/66e238a6ad70fb30c82171ff1b73ea71b4379355
new file mode 100644
index 0000000..6f28e8d
--- /dev/null
+++ b/fuzzers/corpora/midx/66e238a6ad70fb30c82171ff1b73ea71b4379355
Binary files differ
diff --git a/fuzzers/corpora/midx/66f345dc060ac5a1fe8bcf0828102d072deb1111 b/fuzzers/corpora/midx/66f345dc060ac5a1fe8bcf0828102d072deb1111
new file mode 100644
index 0000000..6d3181f
--- /dev/null
+++ b/fuzzers/corpora/midx/66f345dc060ac5a1fe8bcf0828102d072deb1111
Binary files differ
diff --git a/fuzzers/corpora/midx/66f839146ef46deed25fd2cd169a4f1a2a3533fa b/fuzzers/corpora/midx/66f839146ef46deed25fd2cd169a4f1a2a3533fa
new file mode 100644
index 0000000..5a6223d
--- /dev/null
+++ b/fuzzers/corpora/midx/66f839146ef46deed25fd2cd169a4f1a2a3533fa
Binary files differ
diff --git a/fuzzers/corpora/midx/671720ee2b7ba45920b41b8016eb5206b88168ee b/fuzzers/corpora/midx/671720ee2b7ba45920b41b8016eb5206b88168ee
new file mode 100644
index 0000000..564ff2b
--- /dev/null
+++ b/fuzzers/corpora/midx/671720ee2b7ba45920b41b8016eb5206b88168ee
Binary files differ
diff --git a/fuzzers/corpora/midx/679c7140ad60ed32aeb7ee464499dd52b0fc212f b/fuzzers/corpora/midx/679c7140ad60ed32aeb7ee464499dd52b0fc212f
new file mode 100644
index 0000000..1891323
--- /dev/null
+++ b/fuzzers/corpora/midx/679c7140ad60ed32aeb7ee464499dd52b0fc212f
Binary files differ
diff --git a/fuzzers/corpora/midx/67c5e6ce7bb47cefe54d749374f3288a2c915936 b/fuzzers/corpora/midx/67c5e6ce7bb47cefe54d749374f3288a2c915936
new file mode 100644
index 0000000..7e3303a
--- /dev/null
+++ b/fuzzers/corpora/midx/67c5e6ce7bb47cefe54d749374f3288a2c915936
Binary files differ
diff --git a/fuzzers/corpora/midx/69592399b45f2f83e0cc823c5f0e3865ac3fa611 b/fuzzers/corpora/midx/69592399b45f2f83e0cc823c5f0e3865ac3fa611
new file mode 100644
index 0000000..272619c
--- /dev/null
+++ b/fuzzers/corpora/midx/69592399b45f2f83e0cc823c5f0e3865ac3fa611
Binary files differ
diff --git a/fuzzers/corpora/midx/6abf97508f0ed808b7fe0d9bb2439981153badd2 b/fuzzers/corpora/midx/6abf97508f0ed808b7fe0d9bb2439981153badd2
new file mode 100644
index 0000000..8003633
--- /dev/null
+++ b/fuzzers/corpora/midx/6abf97508f0ed808b7fe0d9bb2439981153badd2
Binary files differ
diff --git a/fuzzers/corpora/midx/6b2dfb51b35b78680cb02ff54e06f0c983c04866 b/fuzzers/corpora/midx/6b2dfb51b35b78680cb02ff54e06f0c983c04866
new file mode 100644
index 0000000..2909a34
--- /dev/null
+++ b/fuzzers/corpora/midx/6b2dfb51b35b78680cb02ff54e06f0c983c04866
Binary files differ
diff --git a/fuzzers/corpora/midx/6bbf6ab605fedd41ed6c7581ec9f87c75403e9c3 b/fuzzers/corpora/midx/6bbf6ab605fedd41ed6c7581ec9f87c75403e9c3
new file mode 100644
index 0000000..d41ae61
--- /dev/null
+++ b/fuzzers/corpora/midx/6bbf6ab605fedd41ed6c7581ec9f87c75403e9c3
Binary files differ
diff --git a/fuzzers/corpora/midx/6c0656104902e1323f3a19c46df7cffecae94f1c b/fuzzers/corpora/midx/6c0656104902e1323f3a19c46df7cffecae94f1c
new file mode 100644
index 0000000..a47781b
--- /dev/null
+++ b/fuzzers/corpora/midx/6c0656104902e1323f3a19c46df7cffecae94f1c
Binary files differ
diff --git a/fuzzers/corpora/midx/6c0ce8006b3ebd8202e61fe5f4cc2285248bd1ba b/fuzzers/corpora/midx/6c0ce8006b3ebd8202e61fe5f4cc2285248bd1ba
new file mode 100644
index 0000000..ca2e6fb
--- /dev/null
+++ b/fuzzers/corpora/midx/6c0ce8006b3ebd8202e61fe5f4cc2285248bd1ba
Binary files differ
diff --git a/fuzzers/corpora/midx/6cc635e6dd4e430ed4fb68a9f5add38aa02ae14f b/fuzzers/corpora/midx/6cc635e6dd4e430ed4fb68a9f5add38aa02ae14f
new file mode 100644
index 0000000..8f04f86
--- /dev/null
+++ b/fuzzers/corpora/midx/6cc635e6dd4e430ed4fb68a9f5add38aa02ae14f
Binary files differ
diff --git a/fuzzers/corpora/midx/6d1b281d7bdd9887e53505fd5d040731db18ba48 b/fuzzers/corpora/midx/6d1b281d7bdd9887e53505fd5d040731db18ba48
new file mode 100644
index 0000000..93f359a
--- /dev/null
+++ b/fuzzers/corpora/midx/6d1b281d7bdd9887e53505fd5d040731db18ba48
Binary files differ
diff --git a/fuzzers/corpora/midx/6d5c35f9d87253b2fbe383bfde3775a9f737da12 b/fuzzers/corpora/midx/6d5c35f9d87253b2fbe383bfde3775a9f737da12
new file mode 100644
index 0000000..fe238cd
--- /dev/null
+++ b/fuzzers/corpora/midx/6d5c35f9d87253b2fbe383bfde3775a9f737da12
@@ -0,0 +1 @@
+ãÿÿÿÿÿÿÿïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïÿÿÿÿÿÿÓïïïïïïïïïïïïïïïïïïïï÷ïïïïïïïïïïïÿ \ No newline at end of file
diff --git a/fuzzers/corpora/midx/6d95355018cadedd97bed65c45a44a7ff6f065f7 b/fuzzers/corpora/midx/6d95355018cadedd97bed65c45a44a7ff6f065f7
new file mode 100644
index 0000000..1c1a354
--- /dev/null
+++ b/fuzzers/corpora/midx/6d95355018cadedd97bed65c45a44a7ff6f065f7
Binary files differ
diff --git a/fuzzers/corpora/midx/6dd70e887ab94db3327d9aaa0335914a1f4986b7 b/fuzzers/corpora/midx/6dd70e887ab94db3327d9aaa0335914a1f4986b7
new file mode 100644
index 0000000..c1edd0b
--- /dev/null
+++ b/fuzzers/corpora/midx/6dd70e887ab94db3327d9aaa0335914a1f4986b7
Binary files differ
diff --git a/fuzzers/corpora/midx/6ed51a953a8b6671de417406e340d8d0a211aa12 b/fuzzers/corpora/midx/6ed51a953a8b6671de417406e340d8d0a211aa12
new file mode 100644
index 0000000..d21f30c
--- /dev/null
+++ b/fuzzers/corpora/midx/6ed51a953a8b6671de417406e340d8d0a211aa12
Binary files differ
diff --git a/fuzzers/corpora/midx/6f911f19652a4457c93ef92b594bc1dc2ca900f8 b/fuzzers/corpora/midx/6f911f19652a4457c93ef92b594bc1dc2ca900f8
new file mode 100644
index 0000000..8c537dc
--- /dev/null
+++ b/fuzzers/corpora/midx/6f911f19652a4457c93ef92b594bc1dc2ca900f8
Binary files differ
diff --git a/fuzzers/corpora/midx/6fa76cbaeb3cf0417c7a372132167bcd737db66b b/fuzzers/corpora/midx/6fa76cbaeb3cf0417c7a372132167bcd737db66b
new file mode 100644
index 0000000..a506334
--- /dev/null
+++ b/fuzzers/corpora/midx/6fa76cbaeb3cf0417c7a372132167bcd737db66b
Binary files differ
diff --git a/fuzzers/corpora/midx/71f66d3f7da318d69681a22ebbceb1a2bb290658 b/fuzzers/corpora/midx/71f66d3f7da318d69681a22ebbceb1a2bb290658
new file mode 100644
index 0000000..317820a
--- /dev/null
+++ b/fuzzers/corpora/midx/71f66d3f7da318d69681a22ebbceb1a2bb290658
Binary files differ
diff --git a/fuzzers/corpora/midx/7227a2dc335af13e7225536c49969f46a800750a b/fuzzers/corpora/midx/7227a2dc335af13e7225536c49969f46a800750a
new file mode 100644
index 0000000..d7b9d6f
--- /dev/null
+++ b/fuzzers/corpora/midx/7227a2dc335af13e7225536c49969f46a800750a
@@ -0,0 +1 @@
+ãj \ No newline at end of file
diff --git a/fuzzers/corpora/midx/72fce27bbccf582f2023f5e168853251e576592a b/fuzzers/corpora/midx/72fce27bbccf582f2023f5e168853251e576592a
new file mode 100644
index 0000000..646d858
--- /dev/null
+++ b/fuzzers/corpora/midx/72fce27bbccf582f2023f5e168853251e576592a
Binary files differ
diff --git a/fuzzers/corpora/midx/738e5543ae005a6de85dfcd960eb8e3e6faa8947 b/fuzzers/corpora/midx/738e5543ae005a6de85dfcd960eb8e3e6faa8947
new file mode 100644
index 0000000..33dd640
--- /dev/null
+++ b/fuzzers/corpora/midx/738e5543ae005a6de85dfcd960eb8e3e6faa8947
Binary files differ
diff --git a/fuzzers/corpora/midx/739d9c8868445202305d0a4e5766df1c68932688 b/fuzzers/corpora/midx/739d9c8868445202305d0a4e5766df1c68932688
new file mode 100644
index 0000000..58450d9
--- /dev/null
+++ b/fuzzers/corpora/midx/739d9c8868445202305d0a4e5766df1c68932688
Binary files differ
diff --git a/fuzzers/corpora/midx/7438b07314917c84d348e7d9629e3712190c7da7 b/fuzzers/corpora/midx/7438b07314917c84d348e7d9629e3712190c7da7
new file mode 100644
index 0000000..197006e
--- /dev/null
+++ b/fuzzers/corpora/midx/7438b07314917c84d348e7d9629e3712190c7da7
Binary files differ
diff --git a/fuzzers/corpora/midx/7490f00d660f5d8dedfa606cca241bd07be86a4f b/fuzzers/corpora/midx/7490f00d660f5d8dedfa606cca241bd07be86a4f
new file mode 100644
index 0000000..9f056b2
--- /dev/null
+++ b/fuzzers/corpora/midx/7490f00d660f5d8dedfa606cca241bd07be86a4f
Binary files differ
diff --git a/fuzzers/corpora/midx/75c64e8b61af41b28516c2c0fe902362d55a24f3 b/fuzzers/corpora/midx/75c64e8b61af41b28516c2c0fe902362d55a24f3
new file mode 100644
index 0000000..7832dbc
--- /dev/null
+++ b/fuzzers/corpora/midx/75c64e8b61af41b28516c2c0fe902362d55a24f3
Binary files differ
diff --git a/fuzzers/corpora/midx/75e94b59a41e7f086b6f7ab3bca801251744ae3d b/fuzzers/corpora/midx/75e94b59a41e7f086b6f7ab3bca801251744ae3d
new file mode 100644
index 0000000..573a589
--- /dev/null
+++ b/fuzzers/corpora/midx/75e94b59a41e7f086b6f7ab3bca801251744ae3d
Binary files differ
diff --git a/fuzzers/corpora/midx/7612ceb3a989f97a7bb19f57c7f9c61366953642 b/fuzzers/corpora/midx/7612ceb3a989f97a7bb19f57c7f9c61366953642
new file mode 100644
index 0000000..1dddb9d
--- /dev/null
+++ b/fuzzers/corpora/midx/7612ceb3a989f97a7bb19f57c7f9c61366953642
Binary files differ
diff --git a/fuzzers/corpora/midx/76ac2328e1c979bca648b4082b8bfe6f2e2e73ea b/fuzzers/corpora/midx/76ac2328e1c979bca648b4082b8bfe6f2e2e73ea
new file mode 100644
index 0000000..fa3ab13
--- /dev/null
+++ b/fuzzers/corpora/midx/76ac2328e1c979bca648b4082b8bfe6f2e2e73ea
Binary files differ
diff --git a/fuzzers/corpora/midx/76f296039ba4d666c9147ad234d43b55050808e3 b/fuzzers/corpora/midx/76f296039ba4d666c9147ad234d43b55050808e3
new file mode 100644
index 0000000..114e764
--- /dev/null
+++ b/fuzzers/corpora/midx/76f296039ba4d666c9147ad234d43b55050808e3
Binary files differ
diff --git a/fuzzers/corpora/midx/777f248eea53e3dd2b726e1e0de5eeda43b6d323 b/fuzzers/corpora/midx/777f248eea53e3dd2b726e1e0de5eeda43b6d323
new file mode 100644
index 0000000..120ffd6
--- /dev/null
+++ b/fuzzers/corpora/midx/777f248eea53e3dd2b726e1e0de5eeda43b6d323
@@ -0,0 +1 @@
+› \ No newline at end of file
diff --git a/fuzzers/corpora/midx/792fcd0075bd8031a98a68ce04d6a9f23feef7b4 b/fuzzers/corpora/midx/792fcd0075bd8031a98a68ce04d6a9f23feef7b4
new file mode 100644
index 0000000..ca5572e
--- /dev/null
+++ b/fuzzers/corpora/midx/792fcd0075bd8031a98a68ce04d6a9f23feef7b4
Binary files differ
diff --git a/fuzzers/corpora/midx/7a936c3e69013b2c71dcb72f0eccd99c93367533 b/fuzzers/corpora/midx/7a936c3e69013b2c71dcb72f0eccd99c93367533
new file mode 100644
index 0000000..8c3d729
--- /dev/null
+++ b/fuzzers/corpora/midx/7a936c3e69013b2c71dcb72f0eccd99c93367533
Binary files differ
diff --git a/fuzzers/corpora/midx/7b30d0cd07108f2e45ce1a3fab3f971b25dcf5cd b/fuzzers/corpora/midx/7b30d0cd07108f2e45ce1a3fab3f971b25dcf5cd
new file mode 100644
index 0000000..08ddd34
--- /dev/null
+++ b/fuzzers/corpora/midx/7b30d0cd07108f2e45ce1a3fab3f971b25dcf5cd
Binary files differ
diff --git a/fuzzers/corpora/midx/7b87f367b5fa3bf29bae19031814e5d0120a15ba b/fuzzers/corpora/midx/7b87f367b5fa3bf29bae19031814e5d0120a15ba
new file mode 100644
index 0000000..146d3ee
--- /dev/null
+++ b/fuzzers/corpora/midx/7b87f367b5fa3bf29bae19031814e5d0120a15ba
Binary files differ
diff --git a/fuzzers/corpora/midx/7c12e4bca60858eae13c47a66e54cd9e96a50909 b/fuzzers/corpora/midx/7c12e4bca60858eae13c47a66e54cd9e96a50909
new file mode 100644
index 0000000..a3cb4ab
--- /dev/null
+++ b/fuzzers/corpora/midx/7c12e4bca60858eae13c47a66e54cd9e96a50909
Binary files differ
diff --git a/fuzzers/corpora/midx/7c59f95e649b3be6344f4f835afd0d9a894c1144 b/fuzzers/corpora/midx/7c59f95e649b3be6344f4f835afd0d9a894c1144
new file mode 100644
index 0000000..4a9c07c
--- /dev/null
+++ b/fuzzers/corpora/midx/7c59f95e649b3be6344f4f835afd0d9a894c1144
Binary files differ
diff --git a/fuzzers/corpora/midx/7dcb6494c3614a8690dc496309f90e0f23634c37 b/fuzzers/corpora/midx/7dcb6494c3614a8690dc496309f90e0f23634c37
new file mode 100644
index 0000000..ef09d98
--- /dev/null
+++ b/fuzzers/corpora/midx/7dcb6494c3614a8690dc496309f90e0f23634c37
Binary files differ
diff --git a/fuzzers/corpora/midx/7e64b86827ea98f0a4eb54736c460a59b0c30420 b/fuzzers/corpora/midx/7e64b86827ea98f0a4eb54736c460a59b0c30420
new file mode 100644
index 0000000..6062c8d
--- /dev/null
+++ b/fuzzers/corpora/midx/7e64b86827ea98f0a4eb54736c460a59b0c30420
Binary files differ
diff --git a/fuzzers/corpora/midx/8125d9eaa09b3d2283fea73223866cb36877c4a4 b/fuzzers/corpora/midx/8125d9eaa09b3d2283fea73223866cb36877c4a4
new file mode 100644
index 0000000..d91f4eb
--- /dev/null
+++ b/fuzzers/corpora/midx/8125d9eaa09b3d2283fea73223866cb36877c4a4
Binary files differ
diff --git a/fuzzers/corpora/midx/81c7fc514fa9a07b5b87b94cf9c00df2b1325a74 b/fuzzers/corpora/midx/81c7fc514fa9a07b5b87b94cf9c00df2b1325a74
new file mode 100644
index 0000000..2c19a2a
--- /dev/null
+++ b/fuzzers/corpora/midx/81c7fc514fa9a07b5b87b94cf9c00df2b1325a74
Binary files differ
diff --git a/fuzzers/corpora/midx/81f9df0493052d980ca13918637bc6ce565615b3 b/fuzzers/corpora/midx/81f9df0493052d980ca13918637bc6ce565615b3
new file mode 100644
index 0000000..1030a4b
--- /dev/null
+++ b/fuzzers/corpora/midx/81f9df0493052d980ca13918637bc6ce565615b3
Binary files differ
diff --git a/fuzzers/corpora/midx/82556b9345134dd689cb9d0d08d3dc8459454181 b/fuzzers/corpora/midx/82556b9345134dd689cb9d0d08d3dc8459454181
new file mode 100644
index 0000000..e8a113f
--- /dev/null
+++ b/fuzzers/corpora/midx/82556b9345134dd689cb9d0d08d3dc8459454181
Binary files differ
diff --git a/fuzzers/corpora/midx/82d35a7a6ffb333b02d0d597e88ffdd481237a8b b/fuzzers/corpora/midx/82d35a7a6ffb333b02d0d597e88ffdd481237a8b
new file mode 100644
index 0000000..1f2de8a
--- /dev/null
+++ b/fuzzers/corpora/midx/82d35a7a6ffb333b02d0d597e88ffdd481237a8b
Binary files differ
diff --git a/fuzzers/corpora/midx/82e931da372a2c69c0f10274342173c2be091f1c b/fuzzers/corpora/midx/82e931da372a2c69c0f10274342173c2be091f1c
new file mode 100644
index 0000000..34a1c15
--- /dev/null
+++ b/fuzzers/corpora/midx/82e931da372a2c69c0f10274342173c2be091f1c
Binary files differ
diff --git a/fuzzers/corpora/midx/83e2b53f22afe8f7ee21d30fae2619ad0d6a71e3 b/fuzzers/corpora/midx/83e2b53f22afe8f7ee21d30fae2619ad0d6a71e3
new file mode 100644
index 0000000..8815c9b
--- /dev/null
+++ b/fuzzers/corpora/midx/83e2b53f22afe8f7ee21d30fae2619ad0d6a71e3
Binary files differ
diff --git a/fuzzers/corpora/midx/83f4d70189dbc0d3aaf5025977c53d4d34fc5893 b/fuzzers/corpora/midx/83f4d70189dbc0d3aaf5025977c53d4d34fc5893
new file mode 100644
index 0000000..78b1f7c
--- /dev/null
+++ b/fuzzers/corpora/midx/83f4d70189dbc0d3aaf5025977c53d4d34fc5893
Binary files differ
diff --git a/fuzzers/corpora/midx/85e17cceba7850be893afdc04c8233bea1ef6e72 b/fuzzers/corpora/midx/85e17cceba7850be893afdc04c8233bea1ef6e72
new file mode 100644
index 0000000..0e157c9
--- /dev/null
+++ b/fuzzers/corpora/midx/85e17cceba7850be893afdc04c8233bea1ef6e72
Binary files differ
diff --git a/fuzzers/corpora/midx/874d4abdcd7db751eb930928231669afe90589f5 b/fuzzers/corpora/midx/874d4abdcd7db751eb930928231669afe90589f5
new file mode 100644
index 0000000..238ca42
--- /dev/null
+++ b/fuzzers/corpora/midx/874d4abdcd7db751eb930928231669afe90589f5
Binary files differ
diff --git a/fuzzers/corpora/midx/87894ec663568153d7837f49b80f6d2e99818bd7 b/fuzzers/corpora/midx/87894ec663568153d7837f49b80f6d2e99818bd7
new file mode 100644
index 0000000..d2775fd
--- /dev/null
+++ b/fuzzers/corpora/midx/87894ec663568153d7837f49b80f6d2e99818bd7
Binary files differ
diff --git a/fuzzers/corpora/midx/88052b76108b4ede342f3dd87bb6835b2f71ea83 b/fuzzers/corpora/midx/88052b76108b4ede342f3dd87bb6835b2f71ea83
new file mode 100644
index 0000000..c0a6a32
--- /dev/null
+++ b/fuzzers/corpora/midx/88052b76108b4ede342f3dd87bb6835b2f71ea83
Binary files differ
diff --git a/fuzzers/corpora/midx/884c54256c0ec2cf1c5fa08a0b3d9c2fea021300 b/fuzzers/corpora/midx/884c54256c0ec2cf1c5fa08a0b3d9c2fea021300
new file mode 100644
index 0000000..e0980ee
--- /dev/null
+++ b/fuzzers/corpora/midx/884c54256c0ec2cf1c5fa08a0b3d9c2fea021300
Binary files differ
diff --git a/fuzzers/corpora/midx/8858f36373db5fd6b805a768af55c21019c664b2 b/fuzzers/corpora/midx/8858f36373db5fd6b805a768af55c21019c664b2
new file mode 100644
index 0000000..50c738d
--- /dev/null
+++ b/fuzzers/corpora/midx/8858f36373db5fd6b805a768af55c21019c664b2
Binary files differ
diff --git a/fuzzers/corpora/midx/88fe8b6767c1bd32308208b22e0b00697e5eddf7 b/fuzzers/corpora/midx/88fe8b6767c1bd32308208b22e0b00697e5eddf7
new file mode 100644
index 0000000..da39bf1
--- /dev/null
+++ b/fuzzers/corpora/midx/88fe8b6767c1bd32308208b22e0b00697e5eddf7
Binary files differ
diff --git a/fuzzers/corpora/midx/898cac1610f2f2fb67eb092cd053f0006c3070e3 b/fuzzers/corpora/midx/898cac1610f2f2fb67eb092cd053f0006c3070e3
new file mode 100644
index 0000000..babd31d
--- /dev/null
+++ b/fuzzers/corpora/midx/898cac1610f2f2fb67eb092cd053f0006c3070e3
Binary files differ
diff --git a/fuzzers/corpora/midx/89d0f5573ae1b524e7e9bdb1fb54ea4ce99e3ef0 b/fuzzers/corpora/midx/89d0f5573ae1b524e7e9bdb1fb54ea4ce99e3ef0
new file mode 100644
index 0000000..43858e2
--- /dev/null
+++ b/fuzzers/corpora/midx/89d0f5573ae1b524e7e9bdb1fb54ea4ce99e3ef0
Binary files differ
diff --git a/fuzzers/corpora/midx/8a55300e400efd56be5e12258ebf575c4f3b55ed b/fuzzers/corpora/midx/8a55300e400efd56be5e12258ebf575c4f3b55ed
new file mode 100644
index 0000000..bf76c1a
--- /dev/null
+++ b/fuzzers/corpora/midx/8a55300e400efd56be5e12258ebf575c4f3b55ed
Binary files differ
diff --git a/fuzzers/corpora/midx/8bf7b464aaa2c2b536aa1d76a1297c19155f5603 b/fuzzers/corpora/midx/8bf7b464aaa2c2b536aa1d76a1297c19155f5603
new file mode 100644
index 0000000..6b10f95
--- /dev/null
+++ b/fuzzers/corpora/midx/8bf7b464aaa2c2b536aa1d76a1297c19155f5603
@@ -0,0 +1 @@
+Ã \ No newline at end of file
diff --git a/fuzzers/corpora/midx/8c05e8ef26302a79c89670ad3aa4e8d0bc921923 b/fuzzers/corpora/midx/8c05e8ef26302a79c89670ad3aa4e8d0bc921923
new file mode 100644
index 0000000..9f142f0
--- /dev/null
+++ b/fuzzers/corpora/midx/8c05e8ef26302a79c89670ad3aa4e8d0bc921923
Binary files differ
diff --git a/fuzzers/corpora/midx/8c15f5a268ded9663197d66e8d7d4098e0ae9bf5 b/fuzzers/corpora/midx/8c15f5a268ded9663197d66e8d7d4098e0ae9bf5
new file mode 100644
index 0000000..9b1b882
--- /dev/null
+++ b/fuzzers/corpora/midx/8c15f5a268ded9663197d66e8d7d4098e0ae9bf5
Binary files differ
diff --git a/fuzzers/corpora/midx/8ca9e85a9e628f0016ea4e6413945b3830730c24 b/fuzzers/corpora/midx/8ca9e85a9e628f0016ea4e6413945b3830730c24
new file mode 100644
index 0000000..e64a7c9
--- /dev/null
+++ b/fuzzers/corpora/midx/8ca9e85a9e628f0016ea4e6413945b3830730c24
Binary files differ
diff --git a/fuzzers/corpora/midx/8e74126a239927900a8f655c813a4b230191a5ba b/fuzzers/corpora/midx/8e74126a239927900a8f655c813a4b230191a5ba
new file mode 100644
index 0000000..32005e3
--- /dev/null
+++ b/fuzzers/corpora/midx/8e74126a239927900a8f655c813a4b230191a5ba
Binary files differ
diff --git a/fuzzers/corpora/midx/8ee63e791c004427dd033b468b2ed7446ee6e2e0 b/fuzzers/corpora/midx/8ee63e791c004427dd033b468b2ed7446ee6e2e0
new file mode 100644
index 0000000..a9d264a
--- /dev/null
+++ b/fuzzers/corpora/midx/8ee63e791c004427dd033b468b2ed7446ee6e2e0
Binary files differ
diff --git a/fuzzers/corpora/midx/9028113aa78b649e13ff259027a4e450d469e5da b/fuzzers/corpora/midx/9028113aa78b649e13ff259027a4e450d469e5da
new file mode 100644
index 0000000..12c09a3
--- /dev/null
+++ b/fuzzers/corpora/midx/9028113aa78b649e13ff259027a4e450d469e5da
Binary files differ
diff --git a/fuzzers/corpora/midx/90db2115b8262ebecbefbe8f0a07c451e39bca07 b/fuzzers/corpora/midx/90db2115b8262ebecbefbe8f0a07c451e39bca07
new file mode 100644
index 0000000..38fc26c
--- /dev/null
+++ b/fuzzers/corpora/midx/90db2115b8262ebecbefbe8f0a07c451e39bca07
Binary files differ
diff --git a/fuzzers/corpora/midx/923f28a4d1917e20ee0736b90695c2123c0c987c b/fuzzers/corpora/midx/923f28a4d1917e20ee0736b90695c2123c0c987c
new file mode 100644
index 0000000..8961e95
--- /dev/null
+++ b/fuzzers/corpora/midx/923f28a4d1917e20ee0736b90695c2123c0c987c
Binary files differ
diff --git a/fuzzers/corpora/midx/92a5c74e0506d65d1a12686496452870367b169a b/fuzzers/corpora/midx/92a5c74e0506d65d1a12686496452870367b169a
new file mode 100644
index 0000000..4e08402
--- /dev/null
+++ b/fuzzers/corpora/midx/92a5c74e0506d65d1a12686496452870367b169a
Binary files differ
diff --git a/fuzzers/corpora/midx/92dcf94eb2f92b4e1a232eab3b3f808f4236f118 b/fuzzers/corpora/midx/92dcf94eb2f92b4e1a232eab3b3f808f4236f118
new file mode 100644
index 0000000..0999c8a
--- /dev/null
+++ b/fuzzers/corpora/midx/92dcf94eb2f92b4e1a232eab3b3f808f4236f118
Binary files differ
diff --git a/fuzzers/corpora/midx/9414502aedbef5e307897683625418dd4ac575ac b/fuzzers/corpora/midx/9414502aedbef5e307897683625418dd4ac575ac
new file mode 100644
index 0000000..98260fb
--- /dev/null
+++ b/fuzzers/corpora/midx/9414502aedbef5e307897683625418dd4ac575ac
Binary files differ
diff --git a/fuzzers/corpora/midx/9422e25bec5fec9f84603a85673b54b1a5e77a40 b/fuzzers/corpora/midx/9422e25bec5fec9f84603a85673b54b1a5e77a40
new file mode 100644
index 0000000..e0d9ca7
--- /dev/null
+++ b/fuzzers/corpora/midx/9422e25bec5fec9f84603a85673b54b1a5e77a40
Binary files differ
diff --git a/fuzzers/corpora/midx/943754e865888063e0684aec838222522390d43e b/fuzzers/corpora/midx/943754e865888063e0684aec838222522390d43e
new file mode 100644
index 0000000..2a6ba4e
--- /dev/null
+++ b/fuzzers/corpora/midx/943754e865888063e0684aec838222522390d43e
Binary files differ
diff --git a/fuzzers/corpora/midx/943e067806ae069afbc029ea7a612410e5395687 b/fuzzers/corpora/midx/943e067806ae069afbc029ea7a612410e5395687
new file mode 100644
index 0000000..d51ab5f
--- /dev/null
+++ b/fuzzers/corpora/midx/943e067806ae069afbc029ea7a612410e5395687
Binary files differ
diff --git a/fuzzers/corpora/midx/9547646cc1a5d260df099b00ea7ee2b95567aee1 b/fuzzers/corpora/midx/9547646cc1a5d260df099b00ea7ee2b95567aee1
new file mode 100644
index 0000000..92426bc
--- /dev/null
+++ b/fuzzers/corpora/midx/9547646cc1a5d260df099b00ea7ee2b95567aee1
Binary files differ
diff --git a/fuzzers/corpora/midx/968f7027ec9fbf75a519069ea5189e85a81448b2 b/fuzzers/corpora/midx/968f7027ec9fbf75a519069ea5189e85a81448b2
new file mode 100644
index 0000000..56dfbcd
--- /dev/null
+++ b/fuzzers/corpora/midx/968f7027ec9fbf75a519069ea5189e85a81448b2
Binary files differ
diff --git a/fuzzers/corpora/midx/9691046a2f8b31319a6fdfde0506c9a72aed839a b/fuzzers/corpora/midx/9691046a2f8b31319a6fdfde0506c9a72aed839a
new file mode 100644
index 0000000..e3cfcb0
--- /dev/null
+++ b/fuzzers/corpora/midx/9691046a2f8b31319a6fdfde0506c9a72aed839a
Binary files differ
diff --git a/fuzzers/corpora/midx/96a8cd5c33986cc26cc00eb2de627149f5259e33 b/fuzzers/corpora/midx/96a8cd5c33986cc26cc00eb2de627149f5259e33
new file mode 100644
index 0000000..85fb8fa
--- /dev/null
+++ b/fuzzers/corpora/midx/96a8cd5c33986cc26cc00eb2de627149f5259e33
Binary files differ
diff --git a/fuzzers/corpora/midx/972466bbc33d2d7d7c21be21c7594b51e78675c5 b/fuzzers/corpora/midx/972466bbc33d2d7d7c21be21c7594b51e78675c5
new file mode 100644
index 0000000..b8fd7b3
--- /dev/null
+++ b/fuzzers/corpora/midx/972466bbc33d2d7d7c21be21c7594b51e78675c5
Binary files differ
diff --git a/fuzzers/corpora/midx/980f21a3609762154030f7cf0fe98a892d20f220 b/fuzzers/corpora/midx/980f21a3609762154030f7cf0fe98a892d20f220
new file mode 100644
index 0000000..d89f9cd
--- /dev/null
+++ b/fuzzers/corpora/midx/980f21a3609762154030f7cf0fe98a892d20f220
Binary files differ
diff --git a/fuzzers/corpora/midx/9865f12189ef977418d8410fceebb6830c74d820 b/fuzzers/corpora/midx/9865f12189ef977418d8410fceebb6830c74d820
new file mode 100644
index 0000000..c4d3f67
--- /dev/null
+++ b/fuzzers/corpora/midx/9865f12189ef977418d8410fceebb6830c74d820
Binary files differ
diff --git a/fuzzers/corpora/midx/98a1096d609545083878d5126743bbc5985786a9 b/fuzzers/corpora/midx/98a1096d609545083878d5126743bbc5985786a9
new file mode 100644
index 0000000..6cf168e
--- /dev/null
+++ b/fuzzers/corpora/midx/98a1096d609545083878d5126743bbc5985786a9
Binary files differ
diff --git a/fuzzers/corpora/midx/98c3b6bbf5dc19bc4aad894087277a36d7c79669 b/fuzzers/corpora/midx/98c3b6bbf5dc19bc4aad894087277a36d7c79669
new file mode 100644
index 0000000..7a11bb5
--- /dev/null
+++ b/fuzzers/corpora/midx/98c3b6bbf5dc19bc4aad894087277a36d7c79669
Binary files differ
diff --git a/fuzzers/corpora/midx/98f9cd44400b592f809596004125267acf848435 b/fuzzers/corpora/midx/98f9cd44400b592f809596004125267acf848435
new file mode 100644
index 0000000..d4e6332
--- /dev/null
+++ b/fuzzers/corpora/midx/98f9cd44400b592f809596004125267acf848435
Binary files differ
diff --git a/fuzzers/corpora/midx/9a97260f04ecfe0918499ede95cf4bcb3dbc2b51 b/fuzzers/corpora/midx/9a97260f04ecfe0918499ede95cf4bcb3dbc2b51
new file mode 100644
index 0000000..ba3011b
--- /dev/null
+++ b/fuzzers/corpora/midx/9a97260f04ecfe0918499ede95cf4bcb3dbc2b51
Binary files differ
diff --git a/fuzzers/corpora/midx/9ba0dba2ca4405d04113086309882dac6182e6b8 b/fuzzers/corpora/midx/9ba0dba2ca4405d04113086309882dac6182e6b8
new file mode 100644
index 0000000..4513cd9
--- /dev/null
+++ b/fuzzers/corpora/midx/9ba0dba2ca4405d04113086309882dac6182e6b8
Binary files differ
diff --git a/fuzzers/corpora/midx/9c329ee4b02f2d26ee1a399c873b0452aedca3c0 b/fuzzers/corpora/midx/9c329ee4b02f2d26ee1a399c873b0452aedca3c0
new file mode 100644
index 0000000..ac164f4
--- /dev/null
+++ b/fuzzers/corpora/midx/9c329ee4b02f2d26ee1a399c873b0452aedca3c0
Binary files differ
diff --git a/fuzzers/corpora/midx/9e8e638837e202d83ff606a22dd0e310150fa260 b/fuzzers/corpora/midx/9e8e638837e202d83ff606a22dd0e310150fa260
new file mode 100644
index 0000000..93f205f
--- /dev/null
+++ b/fuzzers/corpora/midx/9e8e638837e202d83ff606a22dd0e310150fa260
Binary files differ
diff --git a/fuzzers/corpora/midx/9ee03d17e070df72547e423a412da0b6a60ad565 b/fuzzers/corpora/midx/9ee03d17e070df72547e423a412da0b6a60ad565
new file mode 100644
index 0000000..874ce8f
--- /dev/null
+++ b/fuzzers/corpora/midx/9ee03d17e070df72547e423a412da0b6a60ad565
Binary files differ
diff --git a/fuzzers/corpora/midx/9fad6bd2b07d65e607039bb2bcda0816410cf983 b/fuzzers/corpora/midx/9fad6bd2b07d65e607039bb2bcda0816410cf983
new file mode 100644
index 0000000..160c9ab
--- /dev/null
+++ b/fuzzers/corpora/midx/9fad6bd2b07d65e607039bb2bcda0816410cf983
Binary files differ
diff --git a/fuzzers/corpora/midx/9fcbd21f4dd194a623d832422384a1519742f0bb b/fuzzers/corpora/midx/9fcbd21f4dd194a623d832422384a1519742f0bb
new file mode 100644
index 0000000..cce3746
--- /dev/null
+++ b/fuzzers/corpora/midx/9fcbd21f4dd194a623d832422384a1519742f0bb
Binary files differ
diff --git a/fuzzers/corpora/midx/a019fb7f17aa36a9743c530e1f11d5613b8b1158 b/fuzzers/corpora/midx/a019fb7f17aa36a9743c530e1f11d5613b8b1158
new file mode 100644
index 0000000..e6a45fa
--- /dev/null
+++ b/fuzzers/corpora/midx/a019fb7f17aa36a9743c530e1f11d5613b8b1158
Binary files differ
diff --git a/fuzzers/corpora/midx/a0b8c6ef20198377b19f59e41f08f4cf2107f460 b/fuzzers/corpora/midx/a0b8c6ef20198377b19f59e41f08f4cf2107f460
new file mode 100644
index 0000000..f10d52c
--- /dev/null
+++ b/fuzzers/corpora/midx/a0b8c6ef20198377b19f59e41f08f4cf2107f460
Binary files differ
diff --git a/fuzzers/corpora/midx/a14079a3e8cbc2112da4fa747ef20cdfd580e068 b/fuzzers/corpora/midx/a14079a3e8cbc2112da4fa747ef20cdfd580e068
new file mode 100644
index 0000000..1d17566
--- /dev/null
+++ b/fuzzers/corpora/midx/a14079a3e8cbc2112da4fa747ef20cdfd580e068
Binary files differ
diff --git a/fuzzers/corpora/midx/a14d61ba0c609665d37e6c6da929cb53c5b70545 b/fuzzers/corpora/midx/a14d61ba0c609665d37e6c6da929cb53c5b70545
new file mode 100644
index 0000000..d70d19e
--- /dev/null
+++ b/fuzzers/corpora/midx/a14d61ba0c609665d37e6c6da929cb53c5b70545
Binary files differ
diff --git a/fuzzers/corpora/midx/a15cf2a13e408cb76af0091a0c286af7ffce58e1 b/fuzzers/corpora/midx/a15cf2a13e408cb76af0091a0c286af7ffce58e1
new file mode 100644
index 0000000..23b70ad
--- /dev/null
+++ b/fuzzers/corpora/midx/a15cf2a13e408cb76af0091a0c286af7ffce58e1
Binary files differ
diff --git a/fuzzers/corpora/midx/a1ddedbdd05eac99b8b31322635771cd9c999f8a b/fuzzers/corpora/midx/a1ddedbdd05eac99b8b31322635771cd9c999f8a
new file mode 100644
index 0000000..066e405
--- /dev/null
+++ b/fuzzers/corpora/midx/a1ddedbdd05eac99b8b31322635771cd9c999f8a
Binary files differ
diff --git a/fuzzers/corpora/midx/a235661c3f8b0174a1658e9c435a69577c49256a b/fuzzers/corpora/midx/a235661c3f8b0174a1658e9c435a69577c49256a
new file mode 100644
index 0000000..ca719f2
--- /dev/null
+++ b/fuzzers/corpora/midx/a235661c3f8b0174a1658e9c435a69577c49256a
Binary files differ
diff --git a/fuzzers/corpora/midx/a261397a4db5ac196c72d73ba6999e9fd4fc5c1f b/fuzzers/corpora/midx/a261397a4db5ac196c72d73ba6999e9fd4fc5c1f
new file mode 100644
index 0000000..ae0d042
--- /dev/null
+++ b/fuzzers/corpora/midx/a261397a4db5ac196c72d73ba6999e9fd4fc5c1f
Binary files differ
diff --git a/fuzzers/corpora/midx/a3a803fd6a56d31269717983bbdf2fceebb626c3 b/fuzzers/corpora/midx/a3a803fd6a56d31269717983bbdf2fceebb626c3
new file mode 100644
index 0000000..f813f25
--- /dev/null
+++ b/fuzzers/corpora/midx/a3a803fd6a56d31269717983bbdf2fceebb626c3
Binary files differ
diff --git a/fuzzers/corpora/midx/a3d5b0b21d977e8f94d401250de1bbd4fa1d0ee0 b/fuzzers/corpora/midx/a3d5b0b21d977e8f94d401250de1bbd4fa1d0ee0
new file mode 100644
index 0000000..151c74b
--- /dev/null
+++ b/fuzzers/corpora/midx/a3d5b0b21d977e8f94d401250de1bbd4fa1d0ee0
Binary files differ
diff --git a/fuzzers/corpora/midx/a42f2900ca519bd15b8d6f507449d1a07de2ef75 b/fuzzers/corpora/midx/a42f2900ca519bd15b8d6f507449d1a07de2ef75
new file mode 100644
index 0000000..efd2d87
--- /dev/null
+++ b/fuzzers/corpora/midx/a42f2900ca519bd15b8d6f507449d1a07de2ef75
Binary files differ
diff --git a/fuzzers/corpora/midx/a4884775b414eaf9643224564f3be405519cf99a b/fuzzers/corpora/midx/a4884775b414eaf9643224564f3be405519cf99a
new file mode 100644
index 0000000..7e6260b
--- /dev/null
+++ b/fuzzers/corpora/midx/a4884775b414eaf9643224564f3be405519cf99a
Binary files differ
diff --git a/fuzzers/corpora/midx/a48da63e9a5709c24cb66f598a7a964cbc7ccfc7 b/fuzzers/corpora/midx/a48da63e9a5709c24cb66f598a7a964cbc7ccfc7
new file mode 100644
index 0000000..2e553bf
--- /dev/null
+++ b/fuzzers/corpora/midx/a48da63e9a5709c24cb66f598a7a964cbc7ccfc7
Binary files differ
diff --git a/fuzzers/corpora/midx/a5789fd83dff18079ea7ba41c999f57bee4db41b b/fuzzers/corpora/midx/a5789fd83dff18079ea7ba41c999f57bee4db41b
new file mode 100644
index 0000000..5d0a926
--- /dev/null
+++ b/fuzzers/corpora/midx/a5789fd83dff18079ea7ba41c999f57bee4db41b
Binary files differ
diff --git a/fuzzers/corpora/midx/a5bb1c60191742df4a91afb622e9b22a2f0b7765 b/fuzzers/corpora/midx/a5bb1c60191742df4a91afb622e9b22a2f0b7765
new file mode 100644
index 0000000..967cd25
--- /dev/null
+++ b/fuzzers/corpora/midx/a5bb1c60191742df4a91afb622e9b22a2f0b7765
Binary files differ
diff --git a/fuzzers/corpora/midx/a5fdfade1cef5e7e494dd6e3791bca5a663d7012 b/fuzzers/corpora/midx/a5fdfade1cef5e7e494dd6e3791bca5a663d7012
new file mode 100644
index 0000000..0739dd6
--- /dev/null
+++ b/fuzzers/corpora/midx/a5fdfade1cef5e7e494dd6e3791bca5a663d7012
Binary files differ
diff --git a/fuzzers/corpora/midx/a6c66f79f5aaf2c1a26ff16754fe1a8c22627e0c b/fuzzers/corpora/midx/a6c66f79f5aaf2c1a26ff16754fe1a8c22627e0c
new file mode 100644
index 0000000..8d22577
--- /dev/null
+++ b/fuzzers/corpora/midx/a6c66f79f5aaf2c1a26ff16754fe1a8c22627e0c
Binary files differ
diff --git a/fuzzers/corpora/midx/a7478a05a1fc04a9e035be5593bfb6a281ec460f b/fuzzers/corpora/midx/a7478a05a1fc04a9e035be5593bfb6a281ec460f
new file mode 100644
index 0000000..f25cd8e
--- /dev/null
+++ b/fuzzers/corpora/midx/a7478a05a1fc04a9e035be5593bfb6a281ec460f
Binary files differ
diff --git a/fuzzers/corpora/midx/a75193dd600661d2b417d4e29b23faa7d721c214 b/fuzzers/corpora/midx/a75193dd600661d2b417d4e29b23faa7d721c214
new file mode 100644
index 0000000..765d7f1
--- /dev/null
+++ b/fuzzers/corpora/midx/a75193dd600661d2b417d4e29b23faa7d721c214
Binary files differ
diff --git a/fuzzers/corpora/midx/a7ccae74c641ffcdda0042e6c04438d5b32c4cf3 b/fuzzers/corpora/midx/a7ccae74c641ffcdda0042e6c04438d5b32c4cf3
new file mode 100644
index 0000000..c180cfa
--- /dev/null
+++ b/fuzzers/corpora/midx/a7ccae74c641ffcdda0042e6c04438d5b32c4cf3
Binary files differ
diff --git a/fuzzers/corpora/midx/a94aa5881abdea5374775b8155812121673f89c3 b/fuzzers/corpora/midx/a94aa5881abdea5374775b8155812121673f89c3
new file mode 100644
index 0000000..3e2266b
--- /dev/null
+++ b/fuzzers/corpora/midx/a94aa5881abdea5374775b8155812121673f89c3
Binary files differ
diff --git a/fuzzers/corpora/midx/a98d794f0f24be7a36917826121fc14a24120893 b/fuzzers/corpora/midx/a98d794f0f24be7a36917826121fc14a24120893
new file mode 100644
index 0000000..2529c4e
--- /dev/null
+++ b/fuzzers/corpora/midx/a98d794f0f24be7a36917826121fc14a24120893
@@ -0,0 +1 @@
+Ñã \ No newline at end of file
diff --git a/fuzzers/corpora/midx/a993077e321bc4e1831bb5a8ac7511d90d32ae27 b/fuzzers/corpora/midx/a993077e321bc4e1831bb5a8ac7511d90d32ae27
new file mode 100644
index 0000000..c0d4c9b
--- /dev/null
+++ b/fuzzers/corpora/midx/a993077e321bc4e1831bb5a8ac7511d90d32ae27
Binary files differ
diff --git a/fuzzers/corpora/midx/aa3bc67656945e43f9342d3aaaef247584d96cfa b/fuzzers/corpora/midx/aa3bc67656945e43f9342d3aaaef247584d96cfa
new file mode 100644
index 0000000..a6e20c9
--- /dev/null
+++ b/fuzzers/corpora/midx/aa3bc67656945e43f9342d3aaaef247584d96cfa
Binary files differ
diff --git a/fuzzers/corpora/midx/ab111c4d72e3d6796e3d7391e9f35b4e6fefc04a b/fuzzers/corpora/midx/ab111c4d72e3d6796e3d7391e9f35b4e6fefc04a
new file mode 100644
index 0000000..4b65b4d
--- /dev/null
+++ b/fuzzers/corpora/midx/ab111c4d72e3d6796e3d7391e9f35b4e6fefc04a
Binary files differ
diff --git a/fuzzers/corpora/midx/ab248c42f77952d5d17d6f5203adaa5925c05c64 b/fuzzers/corpora/midx/ab248c42f77952d5d17d6f5203adaa5925c05c64
new file mode 100644
index 0000000..87bc327
--- /dev/null
+++ b/fuzzers/corpora/midx/ab248c42f77952d5d17d6f5203adaa5925c05c64
Binary files differ
diff --git a/fuzzers/corpora/midx/ab8451fadf805e5087837d9f6d91ef7eb6fa5edb b/fuzzers/corpora/midx/ab8451fadf805e5087837d9f6d91ef7eb6fa5edb
new file mode 100644
index 0000000..ad08d1c
--- /dev/null
+++ b/fuzzers/corpora/midx/ab8451fadf805e5087837d9f6d91ef7eb6fa5edb
Binary files differ
diff --git a/fuzzers/corpora/midx/abbee3b37aff879b1cef47390001b89b0f6ebc0a b/fuzzers/corpora/midx/abbee3b37aff879b1cef47390001b89b0f6ebc0a
new file mode 100644
index 0000000..0cfebbd
--- /dev/null
+++ b/fuzzers/corpora/midx/abbee3b37aff879b1cef47390001b89b0f6ebc0a
Binary files differ
diff --git a/fuzzers/corpora/midx/ac15b23f03af8be6dbbb3bbb8d3877a1f9e074a3 b/fuzzers/corpora/midx/ac15b23f03af8be6dbbb3bbb8d3877a1f9e074a3
new file mode 100644
index 0000000..15ed803
--- /dev/null
+++ b/fuzzers/corpora/midx/ac15b23f03af8be6dbbb3bbb8d3877a1f9e074a3
Binary files differ
diff --git a/fuzzers/corpora/midx/ac47bda12269c06d773f5f3c6517f78513a54a08 b/fuzzers/corpora/midx/ac47bda12269c06d773f5f3c6517f78513a54a08
new file mode 100644
index 0000000..7ba9b56
--- /dev/null
+++ b/fuzzers/corpora/midx/ac47bda12269c06d773f5f3c6517f78513a54a08
Binary files differ
diff --git a/fuzzers/corpora/midx/ad1f4fb57f481a00a9bb231517a3155ef0d0877f b/fuzzers/corpora/midx/ad1f4fb57f481a00a9bb231517a3155ef0d0877f
new file mode 100644
index 0000000..7ae9220
--- /dev/null
+++ b/fuzzers/corpora/midx/ad1f4fb57f481a00a9bb231517a3155ef0d0877f
Binary files differ
diff --git a/fuzzers/corpora/midx/ad25e7ffabedd94833d2529886af4d459529ec9d b/fuzzers/corpora/midx/ad25e7ffabedd94833d2529886af4d459529ec9d
new file mode 100644
index 0000000..a1de604
--- /dev/null
+++ b/fuzzers/corpora/midx/ad25e7ffabedd94833d2529886af4d459529ec9d
Binary files differ
diff --git a/fuzzers/corpora/midx/ad796ebb423f58187806c4a7ee7b787394353ce6 b/fuzzers/corpora/midx/ad796ebb423f58187806c4a7ee7b787394353ce6
new file mode 100644
index 0000000..17e5905
--- /dev/null
+++ b/fuzzers/corpora/midx/ad796ebb423f58187806c4a7ee7b787394353ce6
Binary files differ
diff --git a/fuzzers/corpora/midx/adc83b19e793491b1c6ea0fd8b46cd9f32e592fc b/fuzzers/corpora/midx/adc83b19e793491b1c6ea0fd8b46cd9f32e592fc
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/fuzzers/corpora/midx/adc83b19e793491b1c6ea0fd8b46cd9f32e592fc
@@ -0,0 +1 @@
+
diff --git a/fuzzers/corpora/midx/ae14b80f26f5cee2d85d5154c2cef1eefafa8cc9 b/fuzzers/corpora/midx/ae14b80f26f5cee2d85d5154c2cef1eefafa8cc9
new file mode 100644
index 0000000..d0309c3
--- /dev/null
+++ b/fuzzers/corpora/midx/ae14b80f26f5cee2d85d5154c2cef1eefafa8cc9
Binary files differ
diff --git a/fuzzers/corpora/midx/ae3ba892de543801b3c1dfbce370eb2c80a6fb27 b/fuzzers/corpora/midx/ae3ba892de543801b3c1dfbce370eb2c80a6fb27
new file mode 100644
index 0000000..7f0f42d
--- /dev/null
+++ b/fuzzers/corpora/midx/ae3ba892de543801b3c1dfbce370eb2c80a6fb27
Binary files differ
diff --git a/fuzzers/corpora/midx/aecc0c7f08810803da234e26e7c6fa7a9f1c0593 b/fuzzers/corpora/midx/aecc0c7f08810803da234e26e7c6fa7a9f1c0593
new file mode 100644
index 0000000..8edc209
--- /dev/null
+++ b/fuzzers/corpora/midx/aecc0c7f08810803da234e26e7c6fa7a9f1c0593
Binary files differ
diff --git a/fuzzers/corpora/midx/aed2e85d5d39d25e738a34f30a722680bde30368 b/fuzzers/corpora/midx/aed2e85d5d39d25e738a34f30a722680bde30368
new file mode 100644
index 0000000..006970c
--- /dev/null
+++ b/fuzzers/corpora/midx/aed2e85d5d39d25e738a34f30a722680bde30368
Binary files differ
diff --git a/fuzzers/corpora/midx/b00a75de1987c6f549bf73a63e8f23a2de6641b3 b/fuzzers/corpora/midx/b00a75de1987c6f549bf73a63e8f23a2de6641b3
new file mode 100644
index 0000000..6b7abb0
--- /dev/null
+++ b/fuzzers/corpora/midx/b00a75de1987c6f549bf73a63e8f23a2de6641b3
Binary files differ
diff --git a/fuzzers/corpora/midx/b02e9f951ce9f10a8eb80f1fc61cd3d2832dd7f4 b/fuzzers/corpora/midx/b02e9f951ce9f10a8eb80f1fc61cd3d2832dd7f4
new file mode 100644
index 0000000..1bc7029
--- /dev/null
+++ b/fuzzers/corpora/midx/b02e9f951ce9f10a8eb80f1fc61cd3d2832dd7f4
Binary files differ
diff --git a/fuzzers/corpora/midx/b04aff8ab2e133d45bf44565bd4bf9e33b795a97 b/fuzzers/corpora/midx/b04aff8ab2e133d45bf44565bd4bf9e33b795a97
new file mode 100644
index 0000000..fbba220
--- /dev/null
+++ b/fuzzers/corpora/midx/b04aff8ab2e133d45bf44565bd4bf9e33b795a97
Binary files differ
diff --git a/fuzzers/corpora/midx/b12097ed83db761f7bb79411a59e2474de9b1199 b/fuzzers/corpora/midx/b12097ed83db761f7bb79411a59e2474de9b1199
new file mode 100644
index 0000000..75c7855
--- /dev/null
+++ b/fuzzers/corpora/midx/b12097ed83db761f7bb79411a59e2474de9b1199
Binary files differ
diff --git a/fuzzers/corpora/midx/b1beb2f462b4cb30a09d534b9f49f2e08d76363c b/fuzzers/corpora/midx/b1beb2f462b4cb30a09d534b9f49f2e08d76363c
new file mode 100644
index 0000000..1f4bc7b
--- /dev/null
+++ b/fuzzers/corpora/midx/b1beb2f462b4cb30a09d534b9f49f2e08d76363c
@@ -0,0 +1 @@
+ãðc½D \ No newline at end of file
diff --git a/fuzzers/corpora/midx/b201733b6165f4544578bd6aad3f55aeafd9a194 b/fuzzers/corpora/midx/b201733b6165f4544578bd6aad3f55aeafd9a194
new file mode 100644
index 0000000..51e0787
--- /dev/null
+++ b/fuzzers/corpora/midx/b201733b6165f4544578bd6aad3f55aeafd9a194
Binary files differ
diff --git a/fuzzers/corpora/midx/b2699f25c21ffe453dcce20e31b3093e0f9b2abf b/fuzzers/corpora/midx/b2699f25c21ffe453dcce20e31b3093e0f9b2abf
new file mode 100644
index 0000000..19e656b
--- /dev/null
+++ b/fuzzers/corpora/midx/b2699f25c21ffe453dcce20e31b3093e0f9b2abf
@@ -0,0 +1 @@
+Åç \ No newline at end of file
diff --git a/fuzzers/corpora/midx/b34a5760a1036f909e0243cd857fcef65e40d752 b/fuzzers/corpora/midx/b34a5760a1036f909e0243cd857fcef65e40d752
new file mode 100644
index 0000000..1a9d7ce
--- /dev/null
+++ b/fuzzers/corpora/midx/b34a5760a1036f909e0243cd857fcef65e40d752
Binary files differ
diff --git a/fuzzers/corpora/midx/b3fdacd639073cc1954bcb1f31046d094e2d2296 b/fuzzers/corpora/midx/b3fdacd639073cc1954bcb1f31046d094e2d2296
new file mode 100644
index 0000000..f393c68
--- /dev/null
+++ b/fuzzers/corpora/midx/b3fdacd639073cc1954bcb1f31046d094e2d2296
Binary files differ
diff --git a/fuzzers/corpora/midx/b40a6dbe32c8e6a9b777331e7fd97f0d94ceca1c b/fuzzers/corpora/midx/b40a6dbe32c8e6a9b777331e7fd97f0d94ceca1c
new file mode 100644
index 0000000..c8ea5fc
--- /dev/null
+++ b/fuzzers/corpora/midx/b40a6dbe32c8e6a9b777331e7fd97f0d94ceca1c
Binary files differ
diff --git a/fuzzers/corpora/midx/b52ff2010f22ae6758cde5d529fd19de2a7d5fc5 b/fuzzers/corpora/midx/b52ff2010f22ae6758cde5d529fd19de2a7d5fc5
new file mode 100644
index 0000000..ffe4138
--- /dev/null
+++ b/fuzzers/corpora/midx/b52ff2010f22ae6758cde5d529fd19de2a7d5fc5
Binary files differ
diff --git a/fuzzers/corpora/midx/b53a7a2afd9dfc55c328b4e06a36882c53126e95 b/fuzzers/corpora/midx/b53a7a2afd9dfc55c328b4e06a36882c53126e95
new file mode 100644
index 0000000..1624d98
--- /dev/null
+++ b/fuzzers/corpora/midx/b53a7a2afd9dfc55c328b4e06a36882c53126e95
Binary files differ
diff --git a/fuzzers/corpora/midx/b548ae8a77a62b7f375b8b48e7184ceed59bc8f8 b/fuzzers/corpora/midx/b548ae8a77a62b7f375b8b48e7184ceed59bc8f8
new file mode 100644
index 0000000..2905356
--- /dev/null
+++ b/fuzzers/corpora/midx/b548ae8a77a62b7f375b8b48e7184ceed59bc8f8
Binary files differ
diff --git a/fuzzers/corpora/midx/b66eaf0b689495cc7c194ab1fca7d36ae9da9758 b/fuzzers/corpora/midx/b66eaf0b689495cc7c194ab1fca7d36ae9da9758
new file mode 100644
index 0000000..c02a240
--- /dev/null
+++ b/fuzzers/corpora/midx/b66eaf0b689495cc7c194ab1fca7d36ae9da9758
Binary files differ
diff --git a/fuzzers/corpora/midx/b680bf23da22b8b7e77d847169fe9b6968d79e8b b/fuzzers/corpora/midx/b680bf23da22b8b7e77d847169fe9b6968d79e8b
new file mode 100644
index 0000000..e75d39a
--- /dev/null
+++ b/fuzzers/corpora/midx/b680bf23da22b8b7e77d847169fe9b6968d79e8b
@@ -0,0 +1 @@
+ãoÙ¬ÿÿ \ No newline at end of file
diff --git a/fuzzers/corpora/midx/b70d6e7d230fb1393b8f665adcd5658cad7059fe b/fuzzers/corpora/midx/b70d6e7d230fb1393b8f665adcd5658cad7059fe
new file mode 100644
index 0000000..54a8486
--- /dev/null
+++ b/fuzzers/corpora/midx/b70d6e7d230fb1393b8f665adcd5658cad7059fe
Binary files differ
diff --git a/fuzzers/corpora/midx/b87a59f78adb3ef18b0176a8e7fe7e90c2ab4ef7 b/fuzzers/corpora/midx/b87a59f78adb3ef18b0176a8e7fe7e90c2ab4ef7
new file mode 100644
index 0000000..0b9db19
--- /dev/null
+++ b/fuzzers/corpora/midx/b87a59f78adb3ef18b0176a8e7fe7e90c2ab4ef7
Binary files differ
diff --git a/fuzzers/corpora/midx/b88c5233090e859e923acbdfa9b168f95d7fc14b b/fuzzers/corpora/midx/b88c5233090e859e923acbdfa9b168f95d7fc14b
new file mode 100644
index 0000000..a3841a8
--- /dev/null
+++ b/fuzzers/corpora/midx/b88c5233090e859e923acbdfa9b168f95d7fc14b
Binary files differ
diff --git a/fuzzers/corpora/midx/ba1923ea69eec8fe765e8d1222eccb928ca6c3c2 b/fuzzers/corpora/midx/ba1923ea69eec8fe765e8d1222eccb928ca6c3c2
new file mode 100644
index 0000000..e5534a2
--- /dev/null
+++ b/fuzzers/corpora/midx/ba1923ea69eec8fe765e8d1222eccb928ca6c3c2
Binary files differ
diff --git a/fuzzers/corpora/midx/ba4d695c1eb02c702bd99a3db27838c7ba617d79 b/fuzzers/corpora/midx/ba4d695c1eb02c702bd99a3db27838c7ba617d79
new file mode 100644
index 0000000..2a60fe9
--- /dev/null
+++ b/fuzzers/corpora/midx/ba4d695c1eb02c702bd99a3db27838c7ba617d79
Binary files differ
diff --git a/fuzzers/corpora/midx/ba7e4f999dc22d223c7f75db36646bfa05848572 b/fuzzers/corpora/midx/ba7e4f999dc22d223c7f75db36646bfa05848572
new file mode 100644
index 0000000..b942686
--- /dev/null
+++ b/fuzzers/corpora/midx/ba7e4f999dc22d223c7f75db36646bfa05848572
Binary files differ
diff --git a/fuzzers/corpora/midx/bc5f0cd338d1d17a230378390aa810bc7b103cda b/fuzzers/corpora/midx/bc5f0cd338d1d17a230378390aa810bc7b103cda
new file mode 100644
index 0000000..9b147c4
--- /dev/null
+++ b/fuzzers/corpora/midx/bc5f0cd338d1d17a230378390aa810bc7b103cda
Binary files differ
diff --git a/fuzzers/corpora/midx/bcbb4cf10018a177dd9a6c642d887e0de3d8e522 b/fuzzers/corpora/midx/bcbb4cf10018a177dd9a6c642d887e0de3d8e522
new file mode 100644
index 0000000..400d7bb
--- /dev/null
+++ b/fuzzers/corpora/midx/bcbb4cf10018a177dd9a6c642d887e0de3d8e522
Binary files differ
diff --git a/fuzzers/corpora/midx/bcfeb114df6d5c6e0c85cbe1081631bc321ff65b b/fuzzers/corpora/midx/bcfeb114df6d5c6e0c85cbe1081631bc321ff65b
new file mode 100644
index 0000000..200a997
--- /dev/null
+++ b/fuzzers/corpora/midx/bcfeb114df6d5c6e0c85cbe1081631bc321ff65b
Binary files differ
diff --git a/fuzzers/corpora/midx/bd582237a9293e2a53d8222722a69e7d215822bf b/fuzzers/corpora/midx/bd582237a9293e2a53d8222722a69e7d215822bf
new file mode 100644
index 0000000..9b34fbc
--- /dev/null
+++ b/fuzzers/corpora/midx/bd582237a9293e2a53d8222722a69e7d215822bf
Binary files differ
diff --git a/fuzzers/corpora/midx/bdc83a415da40f74825379203538a2e4d27cffa7 b/fuzzers/corpora/midx/bdc83a415da40f74825379203538a2e4d27cffa7
new file mode 100644
index 0000000..af4b9e3
--- /dev/null
+++ b/fuzzers/corpora/midx/bdc83a415da40f74825379203538a2e4d27cffa7
Binary files differ
diff --git a/fuzzers/corpora/midx/be160536594c87dc07554a71c7d24cd1d718aecc b/fuzzers/corpora/midx/be160536594c87dc07554a71c7d24cd1d718aecc
new file mode 100644
index 0000000..2207941
--- /dev/null
+++ b/fuzzers/corpora/midx/be160536594c87dc07554a71c7d24cd1d718aecc
Binary files differ
diff --git a/fuzzers/corpora/midx/be8f3c744a23f67fb316a39609ca11ddac025b58 b/fuzzers/corpora/midx/be8f3c744a23f67fb316a39609ca11ddac025b58
new file mode 100644
index 0000000..e903036
--- /dev/null
+++ b/fuzzers/corpora/midx/be8f3c744a23f67fb316a39609ca11ddac025b58
Binary files differ
diff --git a/fuzzers/corpora/midx/bf873b027b48f3fd7b727473c832486d99ddb196 b/fuzzers/corpora/midx/bf873b027b48f3fd7b727473c832486d99ddb196
new file mode 100644
index 0000000..f96da4c
--- /dev/null
+++ b/fuzzers/corpora/midx/bf873b027b48f3fd7b727473c832486d99ddb196
Binary files differ
diff --git a/fuzzers/corpora/midx/bf8e20ef6b79131ef9bab8c9c1bb7dbecbead6a5 b/fuzzers/corpora/midx/bf8e20ef6b79131ef9bab8c9c1bb7dbecbead6a5
new file mode 100644
index 0000000..199ed39
--- /dev/null
+++ b/fuzzers/corpora/midx/bf8e20ef6b79131ef9bab8c9c1bb7dbecbead6a5
Binary files differ
diff --git a/fuzzers/corpora/midx/bf90507b8f7c7eebb89edeaabf6a432d86e7df4a b/fuzzers/corpora/midx/bf90507b8f7c7eebb89edeaabf6a432d86e7df4a
new file mode 100644
index 0000000..428e567
--- /dev/null
+++ b/fuzzers/corpora/midx/bf90507b8f7c7eebb89edeaabf6a432d86e7df4a
Binary files differ
diff --git a/fuzzers/corpora/midx/bfa7a5ce666899fb3e2a7216dbf59886da672658 b/fuzzers/corpora/midx/bfa7a5ce666899fb3e2a7216dbf59886da672658
new file mode 100644
index 0000000..45cf661
--- /dev/null
+++ b/fuzzers/corpora/midx/bfa7a5ce666899fb3e2a7216dbf59886da672658
Binary files differ
diff --git a/fuzzers/corpora/midx/bfeaa454d8db33efabba88f146bee6c803369ba0 b/fuzzers/corpora/midx/bfeaa454d8db33efabba88f146bee6c803369ba0
new file mode 100644
index 0000000..82b09f3
--- /dev/null
+++ b/fuzzers/corpora/midx/bfeaa454d8db33efabba88f146bee6c803369ba0
Binary files differ
diff --git a/fuzzers/corpora/midx/c0388910e8d88dcd2e65848ba2cef465caa6b258 b/fuzzers/corpora/midx/c0388910e8d88dcd2e65848ba2cef465caa6b258
new file mode 100644
index 0000000..01e4b89
--- /dev/null
+++ b/fuzzers/corpora/midx/c0388910e8d88dcd2e65848ba2cef465caa6b258
Binary files differ
diff --git a/fuzzers/corpora/midx/c0db293f75bb44668bcbb79286ebed87df141a85 b/fuzzers/corpora/midx/c0db293f75bb44668bcbb79286ebed87df141a85
new file mode 100644
index 0000000..bb56c14
--- /dev/null
+++ b/fuzzers/corpora/midx/c0db293f75bb44668bcbb79286ebed87df141a85
Binary files differ
diff --git a/fuzzers/corpora/midx/c1234da1441255244aba15ecad2a4fa7fd47115e b/fuzzers/corpora/midx/c1234da1441255244aba15ecad2a4fa7fd47115e
new file mode 100644
index 0000000..e79db44
--- /dev/null
+++ b/fuzzers/corpora/midx/c1234da1441255244aba15ecad2a4fa7fd47115e
Binary files differ
diff --git a/fuzzers/corpora/midx/c2206ac3c289a759ee0e9d0d31cc336f0802f7bc b/fuzzers/corpora/midx/c2206ac3c289a759ee0e9d0d31cc336f0802f7bc
new file mode 100644
index 0000000..66a47b9
--- /dev/null
+++ b/fuzzers/corpora/midx/c2206ac3c289a759ee0e9d0d31cc336f0802f7bc
@@ -0,0 +1 @@
+ãïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïï;ïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïï \ No newline at end of file
diff --git a/fuzzers/corpora/midx/c22340ab36e5cff088a58272f63cf69e54a1a9f4 b/fuzzers/corpora/midx/c22340ab36e5cff088a58272f63cf69e54a1a9f4
new file mode 100644
index 0000000..5f2f36c
--- /dev/null
+++ b/fuzzers/corpora/midx/c22340ab36e5cff088a58272f63cf69e54a1a9f4
Binary files differ
diff --git a/fuzzers/corpora/midx/c242c9336c4c22b316e0e56d616b2d45b3318ca4 b/fuzzers/corpora/midx/c242c9336c4c22b316e0e56d616b2d45b3318ca4
new file mode 100644
index 0000000..b4824b4
--- /dev/null
+++ b/fuzzers/corpora/midx/c242c9336c4c22b316e0e56d616b2d45b3318ca4
Binary files differ
diff --git a/fuzzers/corpora/midx/c25a51d8dfaf58d5b609729bb156a80e3b0d892f b/fuzzers/corpora/midx/c25a51d8dfaf58d5b609729bb156a80e3b0d892f
new file mode 100644
index 0000000..1209373
--- /dev/null
+++ b/fuzzers/corpora/midx/c25a51d8dfaf58d5b609729bb156a80e3b0d892f
Binary files differ
diff --git a/fuzzers/corpora/midx/c3419450240d06982b0d828911b188a903355216 b/fuzzers/corpora/midx/c3419450240d06982b0d828911b188a903355216
new file mode 100644
index 0000000..b0ac7f4
--- /dev/null
+++ b/fuzzers/corpora/midx/c3419450240d06982b0d828911b188a903355216
Binary files differ
diff --git a/fuzzers/corpora/midx/c36ea7651f76ae817d8d60cae580e47638741372 b/fuzzers/corpora/midx/c36ea7651f76ae817d8d60cae580e47638741372
new file mode 100644
index 0000000..92ef972
--- /dev/null
+++ b/fuzzers/corpora/midx/c36ea7651f76ae817d8d60cae580e47638741372
Binary files differ
diff --git a/fuzzers/corpora/midx/c42300c021bfd35702f564e917839503922cbe9a b/fuzzers/corpora/midx/c42300c021bfd35702f564e917839503922cbe9a
new file mode 100644
index 0000000..553b6ee
--- /dev/null
+++ b/fuzzers/corpora/midx/c42300c021bfd35702f564e917839503922cbe9a
Binary files differ
diff --git a/fuzzers/corpora/midx/c45d82ddade99ef857b563e435f2efe89e58b0be b/fuzzers/corpora/midx/c45d82ddade99ef857b563e435f2efe89e58b0be
new file mode 100644
index 0000000..a45b45e
--- /dev/null
+++ b/fuzzers/corpora/midx/c45d82ddade99ef857b563e435f2efe89e58b0be
Binary files differ
diff --git a/fuzzers/corpora/midx/c4d1e9187de1e13353b3beb3c1ab16dd62cda571 b/fuzzers/corpora/midx/c4d1e9187de1e13353b3beb3c1ab16dd62cda571
new file mode 100644
index 0000000..12fdcbd
--- /dev/null
+++ b/fuzzers/corpora/midx/c4d1e9187de1e13353b3beb3c1ab16dd62cda571
Binary files differ
diff --git a/fuzzers/corpora/midx/c4e98278a25011c54734494d4534a97489cf4c24 b/fuzzers/corpora/midx/c4e98278a25011c54734494d4534a97489cf4c24
new file mode 100644
index 0000000..4cef0a1
--- /dev/null
+++ b/fuzzers/corpora/midx/c4e98278a25011c54734494d4534a97489cf4c24
Binary files differ
diff --git a/fuzzers/corpora/midx/c4f996ab08f56ce2e9fec7a0428ded510dd6a04a b/fuzzers/corpora/midx/c4f996ab08f56ce2e9fec7a0428ded510dd6a04a
new file mode 100644
index 0000000..78d9033
--- /dev/null
+++ b/fuzzers/corpora/midx/c4f996ab08f56ce2e9fec7a0428ded510dd6a04a
Binary files differ
diff --git a/fuzzers/corpora/midx/c544850a7325e7226583895204f99de730525803 b/fuzzers/corpora/midx/c544850a7325e7226583895204f99de730525803
new file mode 100644
index 0000000..cd87f9b
--- /dev/null
+++ b/fuzzers/corpora/midx/c544850a7325e7226583895204f99de730525803
Binary files differ
diff --git a/fuzzers/corpora/midx/c56629528d5bebdb94f85522caf0f36bbcb19106 b/fuzzers/corpora/midx/c56629528d5bebdb94f85522caf0f36bbcb19106
new file mode 100644
index 0000000..6416cd6
--- /dev/null
+++ b/fuzzers/corpora/midx/c56629528d5bebdb94f85522caf0f36bbcb19106
Binary files differ
diff --git a/fuzzers/corpora/midx/c5c75b58883ccf41b20b140740e2ce763c6086cd b/fuzzers/corpora/midx/c5c75b58883ccf41b20b140740e2ce763c6086cd
new file mode 100644
index 0000000..9f9a51a
--- /dev/null
+++ b/fuzzers/corpora/midx/c5c75b58883ccf41b20b140740e2ce763c6086cd
Binary files differ
diff --git a/fuzzers/corpora/midx/c62da85dca0d4dfb1d7af5d0520eb74993a1e3b0 b/fuzzers/corpora/midx/c62da85dca0d4dfb1d7af5d0520eb74993a1e3b0
new file mode 100644
index 0000000..8f9050c
--- /dev/null
+++ b/fuzzers/corpora/midx/c62da85dca0d4dfb1d7af5d0520eb74993a1e3b0
Binary files differ
diff --git a/fuzzers/corpora/midx/c6379aaaecd282b8ed6d0b4291d0d9fdc763160a b/fuzzers/corpora/midx/c6379aaaecd282b8ed6d0b4291d0d9fdc763160a
new file mode 100644
index 0000000..2478f35
--- /dev/null
+++ b/fuzzers/corpora/midx/c6379aaaecd282b8ed6d0b4291d0d9fdc763160a
Binary files differ
diff --git a/fuzzers/corpora/midx/c6431921184e3edf4fd3e47384c69654cdac0189 b/fuzzers/corpora/midx/c6431921184e3edf4fd3e47384c69654cdac0189
new file mode 100644
index 0000000..2acd685
--- /dev/null
+++ b/fuzzers/corpora/midx/c6431921184e3edf4fd3e47384c69654cdac0189
Binary files differ
diff --git a/fuzzers/corpora/midx/c6f2ca17c6d313a35676cbacd094eb40fd74b23e b/fuzzers/corpora/midx/c6f2ca17c6d313a35676cbacd094eb40fd74b23e
new file mode 100644
index 0000000..d1c63b9
--- /dev/null
+++ b/fuzzers/corpora/midx/c6f2ca17c6d313a35676cbacd094eb40fd74b23e
Binary files differ
diff --git a/fuzzers/corpora/midx/c83e04d58e04fccac37b9dd313eab72011fe8ea1 b/fuzzers/corpora/midx/c83e04d58e04fccac37b9dd313eab72011fe8ea1
new file mode 100644
index 0000000..d11f86a
--- /dev/null
+++ b/fuzzers/corpora/midx/c83e04d58e04fccac37b9dd313eab72011fe8ea1
Binary files differ
diff --git a/fuzzers/corpora/midx/c88dc350b98a5c5ae0503683318c1f30443906a8 b/fuzzers/corpora/midx/c88dc350b98a5c5ae0503683318c1f30443906a8
new file mode 100644
index 0000000..528a69e
--- /dev/null
+++ b/fuzzers/corpora/midx/c88dc350b98a5c5ae0503683318c1f30443906a8
@@ -0,0 +1 @@
+ãp \ No newline at end of file
diff --git a/fuzzers/corpora/midx/c89ee2e9e30a474b5f9532ec61d7aad78377baa0 b/fuzzers/corpora/midx/c89ee2e9e30a474b5f9532ec61d7aad78377baa0
new file mode 100644
index 0000000..6280ce2
--- /dev/null
+++ b/fuzzers/corpora/midx/c89ee2e9e30a474b5f9532ec61d7aad78377baa0
Binary files differ
diff --git a/fuzzers/corpora/midx/c921be4abeb44d1ff07f76f632a16e86526bc4be b/fuzzers/corpora/midx/c921be4abeb44d1ff07f76f632a16e86526bc4be
new file mode 100644
index 0000000..6f53b9d
--- /dev/null
+++ b/fuzzers/corpora/midx/c921be4abeb44d1ff07f76f632a16e86526bc4be
Binary files differ
diff --git a/fuzzers/corpora/midx/c98bdc0431aaece1e8a721aff0ea511cfb8062a6 b/fuzzers/corpora/midx/c98bdc0431aaece1e8a721aff0ea511cfb8062a6
new file mode 100644
index 0000000..cc0580c
--- /dev/null
+++ b/fuzzers/corpora/midx/c98bdc0431aaece1e8a721aff0ea511cfb8062a6
Binary files differ
diff --git a/fuzzers/corpora/midx/c9c456fd3e35a942ef4ab756e04e725cf1f71167 b/fuzzers/corpora/midx/c9c456fd3e35a942ef4ab756e04e725cf1f71167
new file mode 100644
index 0000000..6a7c367
--- /dev/null
+++ b/fuzzers/corpora/midx/c9c456fd3e35a942ef4ab756e04e725cf1f71167
Binary files differ
diff --git a/fuzzers/corpora/midx/cd1f1a31b79af77e1e764102942ba7a79dcd24cf b/fuzzers/corpora/midx/cd1f1a31b79af77e1e764102942ba7a79dcd24cf
new file mode 100644
index 0000000..883c745
--- /dev/null
+++ b/fuzzers/corpora/midx/cd1f1a31b79af77e1e764102942ba7a79dcd24cf
Binary files differ
diff --git a/fuzzers/corpora/midx/cd57e5904254c2278e9ecf28ed7414d7aed8eef1 b/fuzzers/corpora/midx/cd57e5904254c2278e9ecf28ed7414d7aed8eef1
new file mode 100644
index 0000000..709c1a4
--- /dev/null
+++ b/fuzzers/corpora/midx/cd57e5904254c2278e9ecf28ed7414d7aed8eef1
Binary files differ
diff --git a/fuzzers/corpora/midx/cd665cdc2bd6a26eb68c9af6d1728a7d4f6eb309 b/fuzzers/corpora/midx/cd665cdc2bd6a26eb68c9af6d1728a7d4f6eb309
new file mode 100644
index 0000000..094e31b
--- /dev/null
+++ b/fuzzers/corpora/midx/cd665cdc2bd6a26eb68c9af6d1728a7d4f6eb309
Binary files differ
diff --git a/fuzzers/corpora/midx/ce72111c4314b22c4c7824bc8ea340ebd6d3fa84 b/fuzzers/corpora/midx/ce72111c4314b22c4c7824bc8ea340ebd6d3fa84
new file mode 100644
index 0000000..9bf4135
--- /dev/null
+++ b/fuzzers/corpora/midx/ce72111c4314b22c4c7824bc8ea340ebd6d3fa84
Binary files differ
diff --git a/fuzzers/corpora/midx/ceacc7ace2f4be962b0db2eeeea3fe6a00ca9dd6 b/fuzzers/corpora/midx/ceacc7ace2f4be962b0db2eeeea3fe6a00ca9dd6
new file mode 100644
index 0000000..6ddf30d
--- /dev/null
+++ b/fuzzers/corpora/midx/ceacc7ace2f4be962b0db2eeeea3fe6a00ca9dd6
Binary files differ
diff --git a/fuzzers/corpora/midx/cf40769d8b4fcbac1b10ced2e0c3c1294f23fcdd b/fuzzers/corpora/midx/cf40769d8b4fcbac1b10ced2e0c3c1294f23fcdd
new file mode 100644
index 0000000..18327c9
--- /dev/null
+++ b/fuzzers/corpora/midx/cf40769d8b4fcbac1b10ced2e0c3c1294f23fcdd
Binary files differ
diff --git a/fuzzers/corpora/midx/d120111a77a3e3d1d504a04bbfc5f53effa14eb0 b/fuzzers/corpora/midx/d120111a77a3e3d1d504a04bbfc5f53effa14eb0
new file mode 100644
index 0000000..84043fe
--- /dev/null
+++ b/fuzzers/corpora/midx/d120111a77a3e3d1d504a04bbfc5f53effa14eb0
Binary files differ
diff --git a/fuzzers/corpora/midx/d1ce81240a32aec2de1b0d779aa29a62c36f291f b/fuzzers/corpora/midx/d1ce81240a32aec2de1b0d779aa29a62c36f291f
new file mode 100644
index 0000000..f49192c
--- /dev/null
+++ b/fuzzers/corpora/midx/d1ce81240a32aec2de1b0d779aa29a62c36f291f
Binary files differ
diff --git a/fuzzers/corpora/midx/d21869b290cd2b448e7b8103dced97e62fefffcc b/fuzzers/corpora/midx/d21869b290cd2b448e7b8103dced97e62fefffcc
new file mode 100644
index 0000000..1b98c7f
--- /dev/null
+++ b/fuzzers/corpora/midx/d21869b290cd2b448e7b8103dced97e62fefffcc
Binary files differ
diff --git a/fuzzers/corpora/midx/d2bffcd01e87ce9860007b244ff1e79ecd3d4d0f b/fuzzers/corpora/midx/d2bffcd01e87ce9860007b244ff1e79ecd3d4d0f
new file mode 100644
index 0000000..a7ef0a4
--- /dev/null
+++ b/fuzzers/corpora/midx/d2bffcd01e87ce9860007b244ff1e79ecd3d4d0f
Binary files differ
diff --git a/fuzzers/corpora/midx/d2d668b6e28fca83da5146021879c2b006406fa4 b/fuzzers/corpora/midx/d2d668b6e28fca83da5146021879c2b006406fa4
new file mode 100644
index 0000000..b469259
--- /dev/null
+++ b/fuzzers/corpora/midx/d2d668b6e28fca83da5146021879c2b006406fa4
Binary files differ
diff --git a/fuzzers/corpora/midx/d37abe0d299b8ad1e90f5b7af302c24f411a7ed1 b/fuzzers/corpora/midx/d37abe0d299b8ad1e90f5b7af302c24f411a7ed1
new file mode 100644
index 0000000..81365c3
--- /dev/null
+++ b/fuzzers/corpora/midx/d37abe0d299b8ad1e90f5b7af302c24f411a7ed1
Binary files differ
diff --git a/fuzzers/corpora/midx/d3d689a12ab3808313d5ba0044e8c67ecb4337e4 b/fuzzers/corpora/midx/d3d689a12ab3808313d5ba0044e8c67ecb4337e4
new file mode 100644
index 0000000..4afea5e
--- /dev/null
+++ b/fuzzers/corpora/midx/d3d689a12ab3808313d5ba0044e8c67ecb4337e4
Binary files differ
diff --git a/fuzzers/corpora/midx/d48eb559213edf05aa2850a14194885ae2086ba4 b/fuzzers/corpora/midx/d48eb559213edf05aa2850a14194885ae2086ba4
new file mode 100644
index 0000000..0b91a6f
--- /dev/null
+++ b/fuzzers/corpora/midx/d48eb559213edf05aa2850a14194885ae2086ba4
Binary files differ
diff --git a/fuzzers/corpora/midx/d490f365693e49ebdeaf658b3f549311a399c6a8 b/fuzzers/corpora/midx/d490f365693e49ebdeaf658b3f549311a399c6a8
new file mode 100644
index 0000000..7ed6c91
--- /dev/null
+++ b/fuzzers/corpora/midx/d490f365693e49ebdeaf658b3f549311a399c6a8
Binary files differ
diff --git a/fuzzers/corpora/midx/d54821b652a8611c486bedd8645081d3a4b1c8d1 b/fuzzers/corpora/midx/d54821b652a8611c486bedd8645081d3a4b1c8d1
new file mode 100644
index 0000000..eda0c25
--- /dev/null
+++ b/fuzzers/corpora/midx/d54821b652a8611c486bedd8645081d3a4b1c8d1
Binary files differ
diff --git a/fuzzers/corpora/midx/d5586a91f9a879e8a67ec7b09a48038909918ad9 b/fuzzers/corpora/midx/d5586a91f9a879e8a67ec7b09a48038909918ad9
new file mode 100644
index 0000000..0abb218
--- /dev/null
+++ b/fuzzers/corpora/midx/d5586a91f9a879e8a67ec7b09a48038909918ad9
Binary files differ
diff --git a/fuzzers/corpora/midx/d696362920a2fad8e280293e8d1c92b18c87e4ae b/fuzzers/corpora/midx/d696362920a2fad8e280293e8d1c92b18c87e4ae
new file mode 100644
index 0000000..b72ac49
--- /dev/null
+++ b/fuzzers/corpora/midx/d696362920a2fad8e280293e8d1c92b18c87e4ae
Binary files differ
diff --git a/fuzzers/corpora/midx/d6b546a2b1bc1c8f80028e4be10c45a06014b32d b/fuzzers/corpora/midx/d6b546a2b1bc1c8f80028e4be10c45a06014b32d
new file mode 100644
index 0000000..75ee6a9
--- /dev/null
+++ b/fuzzers/corpora/midx/d6b546a2b1bc1c8f80028e4be10c45a06014b32d
Binary files differ
diff --git a/fuzzers/corpora/midx/d7f04cbca92b2122e0f437ed4dd8fd1782d40f7f b/fuzzers/corpora/midx/d7f04cbca92b2122e0f437ed4dd8fd1782d40f7f
new file mode 100644
index 0000000..3b0ada6
--- /dev/null
+++ b/fuzzers/corpora/midx/d7f04cbca92b2122e0f437ed4dd8fd1782d40f7f
Binary files differ
diff --git a/fuzzers/corpora/midx/d81944a0c1ed56d11129533d9a3d0d038113d53d b/fuzzers/corpora/midx/d81944a0c1ed56d11129533d9a3d0d038113d53d
new file mode 100644
index 0000000..fa170d1
--- /dev/null
+++ b/fuzzers/corpora/midx/d81944a0c1ed56d11129533d9a3d0d038113d53d
Binary files differ
diff --git a/fuzzers/corpora/midx/d8524e83ea63c9b365c2e93af75a8100a08b1b69 b/fuzzers/corpora/midx/d8524e83ea63c9b365c2e93af75a8100a08b1b69
new file mode 100644
index 0000000..68f680b
--- /dev/null
+++ b/fuzzers/corpora/midx/d8524e83ea63c9b365c2e93af75a8100a08b1b69
Binary files differ
diff --git a/fuzzers/corpora/midx/d8d9a2d06763cf6feb433cef92a80ef14baab31a b/fuzzers/corpora/midx/d8d9a2d06763cf6feb433cef92a80ef14baab31a
new file mode 100644
index 0000000..2f47818
--- /dev/null
+++ b/fuzzers/corpora/midx/d8d9a2d06763cf6feb433cef92a80ef14baab31a
Binary files differ
diff --git a/fuzzers/corpora/midx/d917fbd641cc40786246387456a636899d56b5a6 b/fuzzers/corpora/midx/d917fbd641cc40786246387456a636899d56b5a6
new file mode 100644
index 0000000..e2d6948
--- /dev/null
+++ b/fuzzers/corpora/midx/d917fbd641cc40786246387456a636899d56b5a6
Binary files differ
diff --git a/fuzzers/corpora/midx/d99f9ec8b504029457185ac03ea8ba21c2611737 b/fuzzers/corpora/midx/d99f9ec8b504029457185ac03ea8ba21c2611737
new file mode 100644
index 0000000..c3e992a
--- /dev/null
+++ b/fuzzers/corpora/midx/d99f9ec8b504029457185ac03ea8ba21c2611737
Binary files differ
diff --git a/fuzzers/corpora/midx/d9c9c90c1bbc55beb81875838e9067c473d0fa92 b/fuzzers/corpora/midx/d9c9c90c1bbc55beb81875838e9067c473d0fa92
new file mode 100644
index 0000000..b149456
--- /dev/null
+++ b/fuzzers/corpora/midx/d9c9c90c1bbc55beb81875838e9067c473d0fa92
Binary files differ
diff --git a/fuzzers/corpora/midx/d9e908317a6ef08a7528924672836a550d34cb5f b/fuzzers/corpora/midx/d9e908317a6ef08a7528924672836a550d34cb5f
new file mode 100644
index 0000000..1c5b0c8
--- /dev/null
+++ b/fuzzers/corpora/midx/d9e908317a6ef08a7528924672836a550d34cb5f
Binary files differ
diff --git a/fuzzers/corpora/midx/d9ef71deb57fa6f40e027be2c84fa37d288e1cc5 b/fuzzers/corpora/midx/d9ef71deb57fa6f40e027be2c84fa37d288e1cc5
new file mode 100644
index 0000000..87ef310
--- /dev/null
+++ b/fuzzers/corpora/midx/d9ef71deb57fa6f40e027be2c84fa37d288e1cc5
Binary files differ
diff --git a/fuzzers/corpora/midx/da8841b9d04382d62d4aeb3fde4dc78466f31543 b/fuzzers/corpora/midx/da8841b9d04382d62d4aeb3fde4dc78466f31543
new file mode 100644
index 0000000..10eae46
--- /dev/null
+++ b/fuzzers/corpora/midx/da8841b9d04382d62d4aeb3fde4dc78466f31543
Binary files differ
diff --git a/fuzzers/corpora/midx/db3fbb74c9c9c4185f91eca85f14c3d2c3d9f487 b/fuzzers/corpora/midx/db3fbb74c9c9c4185f91eca85f14c3d2c3d9f487
new file mode 100644
index 0000000..a6344ce
--- /dev/null
+++ b/fuzzers/corpora/midx/db3fbb74c9c9c4185f91eca85f14c3d2c3d9f487
Binary files differ
diff --git a/fuzzers/corpora/midx/db7a31de22258d4dc17d44a27d9340946e9c9ee9 b/fuzzers/corpora/midx/db7a31de22258d4dc17d44a27d9340946e9c9ee9
new file mode 100644
index 0000000..32c8a8e
--- /dev/null
+++ b/fuzzers/corpora/midx/db7a31de22258d4dc17d44a27d9340946e9c9ee9
Binary files differ
diff --git a/fuzzers/corpora/midx/dbbe57fc653930b4ff43f168565ba84ef25f60c2 b/fuzzers/corpora/midx/dbbe57fc653930b4ff43f168565ba84ef25f60c2
new file mode 100644
index 0000000..9219ff3
--- /dev/null
+++ b/fuzzers/corpora/midx/dbbe57fc653930b4ff43f168565ba84ef25f60c2
Binary files differ
diff --git a/fuzzers/corpora/midx/dbe74c0d9e7b62c1fd87d5e3ea73ee04f0337154 b/fuzzers/corpora/midx/dbe74c0d9e7b62c1fd87d5e3ea73ee04f0337154
new file mode 100644
index 0000000..4ebae47
--- /dev/null
+++ b/fuzzers/corpora/midx/dbe74c0d9e7b62c1fd87d5e3ea73ee04f0337154
Binary files differ
diff --git a/fuzzers/corpora/midx/dbebf36a6b91568ac059142c3ca3211226da12a8 b/fuzzers/corpora/midx/dbebf36a6b91568ac059142c3ca3211226da12a8
new file mode 100644
index 0000000..d180a9b
--- /dev/null
+++ b/fuzzers/corpora/midx/dbebf36a6b91568ac059142c3ca3211226da12a8
Binary files differ
diff --git a/fuzzers/corpora/midx/ddacbb379242b31a00d62fdff5777dffc1e899c2 b/fuzzers/corpora/midx/ddacbb379242b31a00d62fdff5777dffc1e899c2
new file mode 100644
index 0000000..999ece9
--- /dev/null
+++ b/fuzzers/corpora/midx/ddacbb379242b31a00d62fdff5777dffc1e899c2
Binary files differ
diff --git a/fuzzers/corpora/midx/decd2cd9cef352610ac9e5cc461df1829543f9f0 b/fuzzers/corpora/midx/decd2cd9cef352610ac9e5cc461df1829543f9f0
new file mode 100644
index 0000000..28af13b
--- /dev/null
+++ b/fuzzers/corpora/midx/decd2cd9cef352610ac9e5cc461df1829543f9f0
@@ -0,0 +1,3 @@
+yã
+y \ No newline at end of file
diff --git a/fuzzers/corpora/midx/deeae69363db06972798b296a0c5c99e02cb2b4c b/fuzzers/corpora/midx/deeae69363db06972798b296a0c5c99e02cb2b4c
new file mode 100644
index 0000000..63cffc8
--- /dev/null
+++ b/fuzzers/corpora/midx/deeae69363db06972798b296a0c5c99e02cb2b4c
Binary files differ
diff --git a/fuzzers/corpora/midx/e03e105323e6e7b2af90ad876b5c547af90d8f6b b/fuzzers/corpora/midx/e03e105323e6e7b2af90ad876b5c547af90d8f6b
new file mode 100644
index 0000000..020fa17
--- /dev/null
+++ b/fuzzers/corpora/midx/e03e105323e6e7b2af90ad876b5c547af90d8f6b
Binary files differ
diff --git a/fuzzers/corpora/midx/e3133215848c9cde428338c9d51424c8a81b96f5 b/fuzzers/corpora/midx/e3133215848c9cde428338c9d51424c8a81b96f5
new file mode 100644
index 0000000..460d608
--- /dev/null
+++ b/fuzzers/corpora/midx/e3133215848c9cde428338c9d51424c8a81b96f5
Binary files differ
diff --git a/fuzzers/corpora/midx/e32a25f0347b0e95d4bea16c27a1f374847683bd b/fuzzers/corpora/midx/e32a25f0347b0e95d4bea16c27a1f374847683bd
new file mode 100644
index 0000000..3329d61
--- /dev/null
+++ b/fuzzers/corpora/midx/e32a25f0347b0e95d4bea16c27a1f374847683bd
Binary files differ
diff --git a/fuzzers/corpora/midx/e45ce97522194abcdd7ff9beb931e20b86c97a79 b/fuzzers/corpora/midx/e45ce97522194abcdd7ff9beb931e20b86c97a79
new file mode 100644
index 0000000..137a820
--- /dev/null
+++ b/fuzzers/corpora/midx/e45ce97522194abcdd7ff9beb931e20b86c97a79
Binary files differ
diff --git a/fuzzers/corpora/midx/e484023d50fc1036e46a437053b965c527700d42 b/fuzzers/corpora/midx/e484023d50fc1036e46a437053b965c527700d42
new file mode 100644
index 0000000..996f1e6
--- /dev/null
+++ b/fuzzers/corpora/midx/e484023d50fc1036e46a437053b965c527700d42
Binary files differ
diff --git a/fuzzers/corpora/midx/e4e60e77fe3a050940d0afcc7dbab7ef06b04ba3 b/fuzzers/corpora/midx/e4e60e77fe3a050940d0afcc7dbab7ef06b04ba3
new file mode 100644
index 0000000..31520a8
--- /dev/null
+++ b/fuzzers/corpora/midx/e4e60e77fe3a050940d0afcc7dbab7ef06b04ba3
Binary files differ
diff --git a/fuzzers/corpora/midx/e51629784092d9cf811ea1bd894297f062ed7ec4 b/fuzzers/corpora/midx/e51629784092d9cf811ea1bd894297f062ed7ec4
new file mode 100644
index 0000000..1f4127c
--- /dev/null
+++ b/fuzzers/corpora/midx/e51629784092d9cf811ea1bd894297f062ed7ec4
Binary files differ
diff --git a/fuzzers/corpora/midx/e5a7e837029178b3fb6a26d77ea4574ffeaa219d b/fuzzers/corpora/midx/e5a7e837029178b3fb6a26d77ea4574ffeaa219d
new file mode 100644
index 0000000..7607ce3
--- /dev/null
+++ b/fuzzers/corpora/midx/e5a7e837029178b3fb6a26d77ea4574ffeaa219d
Binary files differ
diff --git a/fuzzers/corpora/midx/e5c616e9efdd9c80181f9210d0e3a81c08fe9b4d b/fuzzers/corpora/midx/e5c616e9efdd9c80181f9210d0e3a81c08fe9b4d
new file mode 100644
index 0000000..476b4fd
--- /dev/null
+++ b/fuzzers/corpora/midx/e5c616e9efdd9c80181f9210d0e3a81c08fe9b4d
Binary files differ
diff --git a/fuzzers/corpora/midx/e5e6e84e2c7770537c744bcfdbe8303afe39ef92 b/fuzzers/corpora/midx/e5e6e84e2c7770537c744bcfdbe8303afe39ef92
new file mode 100644
index 0000000..79045de
--- /dev/null
+++ b/fuzzers/corpora/midx/e5e6e84e2c7770537c744bcfdbe8303afe39ef92
Binary files differ
diff --git a/fuzzers/corpora/midx/e5fb20928feec1ee3114597317edc0e06c413749 b/fuzzers/corpora/midx/e5fb20928feec1ee3114597317edc0e06c413749
new file mode 100644
index 0000000..d6758cf
--- /dev/null
+++ b/fuzzers/corpora/midx/e5fb20928feec1ee3114597317edc0e06c413749
Binary files differ
diff --git a/fuzzers/corpora/midx/e789319791fe704e5a8ffd7cb570c8d2722ac35f b/fuzzers/corpora/midx/e789319791fe704e5a8ffd7cb570c8d2722ac35f
new file mode 100644
index 0000000..19fdbe7
--- /dev/null
+++ b/fuzzers/corpora/midx/e789319791fe704e5a8ffd7cb570c8d2722ac35f
Binary files differ
diff --git a/fuzzers/corpora/midx/e9fdb9f08f225b4231f01dda9c7b61e7b78bf7d3 b/fuzzers/corpora/midx/e9fdb9f08f225b4231f01dda9c7b61e7b78bf7d3
new file mode 100644
index 0000000..576a1f8
--- /dev/null
+++ b/fuzzers/corpora/midx/e9fdb9f08f225b4231f01dda9c7b61e7b78bf7d3
Binary files differ
diff --git a/fuzzers/corpora/midx/ea6780324dca9a06db28598dfb590436d846d99f b/fuzzers/corpora/midx/ea6780324dca9a06db28598dfb590436d846d99f
new file mode 100644
index 0000000..d8b617c
--- /dev/null
+++ b/fuzzers/corpora/midx/ea6780324dca9a06db28598dfb590436d846d99f
Binary files differ
diff --git a/fuzzers/corpora/midx/ea6afcc92b8a6c9e14cc053d351909ad5b0a3fdf b/fuzzers/corpora/midx/ea6afcc92b8a6c9e14cc053d351909ad5b0a3fdf
new file mode 100644
index 0000000..8393e36
--- /dev/null
+++ b/fuzzers/corpora/midx/ea6afcc92b8a6c9e14cc053d351909ad5b0a3fdf
Binary files differ
diff --git a/fuzzers/corpora/midx/ea8c569029c0cacc4ae75e95b2f4e84abb6867f4 b/fuzzers/corpora/midx/ea8c569029c0cacc4ae75e95b2f4e84abb6867f4
new file mode 100644
index 0000000..2a86dc5
--- /dev/null
+++ b/fuzzers/corpora/midx/ea8c569029c0cacc4ae75e95b2f4e84abb6867f4
Binary files differ
diff --git a/fuzzers/corpora/midx/eb3e80c3ea9cfe9e08b2eef117aaa522a51a619c b/fuzzers/corpora/midx/eb3e80c3ea9cfe9e08b2eef117aaa522a51a619c
new file mode 100644
index 0000000..d1fb999
--- /dev/null
+++ b/fuzzers/corpora/midx/eb3e80c3ea9cfe9e08b2eef117aaa522a51a619c
Binary files differ
diff --git a/fuzzers/corpora/midx/ec55b30741fe8fffeec584176c8d20f6a679cfa1 b/fuzzers/corpora/midx/ec55b30741fe8fffeec584176c8d20f6a679cfa1
new file mode 100644
index 0000000..69efa4a
--- /dev/null
+++ b/fuzzers/corpora/midx/ec55b30741fe8fffeec584176c8d20f6a679cfa1
Binary files differ
diff --git a/fuzzers/corpora/midx/ed0724a6c3804a3ab20a980b5ca48671689a602f b/fuzzers/corpora/midx/ed0724a6c3804a3ab20a980b5ca48671689a602f
new file mode 100644
index 0000000..85ec5a6
--- /dev/null
+++ b/fuzzers/corpora/midx/ed0724a6c3804a3ab20a980b5ca48671689a602f
Binary files differ
diff --git a/fuzzers/corpora/midx/edeb545d1cf852dc9582fa764010fe844a5e3515 b/fuzzers/corpora/midx/edeb545d1cf852dc9582fa764010fe844a5e3515
new file mode 100644
index 0000000..3187fc7
--- /dev/null
+++ b/fuzzers/corpora/midx/edeb545d1cf852dc9582fa764010fe844a5e3515
Binary files differ
diff --git a/fuzzers/corpora/midx/ee70b920de91f1be6b4448070ee2d1bd9e08286d b/fuzzers/corpora/midx/ee70b920de91f1be6b4448070ee2d1bd9e08286d
new file mode 100644
index 0000000..6d1f0a2
--- /dev/null
+++ b/fuzzers/corpora/midx/ee70b920de91f1be6b4448070ee2d1bd9e08286d
Binary files differ
diff --git a/fuzzers/corpora/midx/ef004af4e947d25b4d1d1dd16502260d4c7a99cd b/fuzzers/corpora/midx/ef004af4e947d25b4d1d1dd16502260d4c7a99cd
new file mode 100644
index 0000000..ac39a8e
--- /dev/null
+++ b/fuzzers/corpora/midx/ef004af4e947d25b4d1d1dd16502260d4c7a99cd
Binary files differ
diff --git a/fuzzers/corpora/midx/f009d226503b73aed0f1fd952ef8725433d158be b/fuzzers/corpora/midx/f009d226503b73aed0f1fd952ef8725433d158be
new file mode 100644
index 0000000..55470ad
--- /dev/null
+++ b/fuzzers/corpora/midx/f009d226503b73aed0f1fd952ef8725433d158be
Binary files differ
diff --git a/fuzzers/corpora/midx/f0a821dffe21afd357932febaf6e8ee331f53197 b/fuzzers/corpora/midx/f0a821dffe21afd357932febaf6e8ee331f53197
new file mode 100644
index 0000000..c4464b1
--- /dev/null
+++ b/fuzzers/corpora/midx/f0a821dffe21afd357932febaf6e8ee331f53197
Binary files differ
diff --git a/fuzzers/corpora/midx/f101a2fe93dfaaed1c596022b4e509cf3a591c8a b/fuzzers/corpora/midx/f101a2fe93dfaaed1c596022b4e509cf3a591c8a
new file mode 100644
index 0000000..5576bd0
--- /dev/null
+++ b/fuzzers/corpora/midx/f101a2fe93dfaaed1c596022b4e509cf3a591c8a
Binary files differ
diff --git a/fuzzers/corpora/midx/f1101f71657385174f8cb920026a761404b4395d b/fuzzers/corpora/midx/f1101f71657385174f8cb920026a761404b4395d
new file mode 100644
index 0000000..59bd02e
--- /dev/null
+++ b/fuzzers/corpora/midx/f1101f71657385174f8cb920026a761404b4395d
Binary files differ
diff --git a/fuzzers/corpora/midx/f138c84e42d3cc61a219c4be9db791750f0541c8 b/fuzzers/corpora/midx/f138c84e42d3cc61a219c4be9db791750f0541c8
new file mode 100644
index 0000000..ab2d713
--- /dev/null
+++ b/fuzzers/corpora/midx/f138c84e42d3cc61a219c4be9db791750f0541c8
@@ -0,0 +1 @@
+ã]]ÿÿÿÿÿs5e \ No newline at end of file
diff --git a/fuzzers/corpora/midx/f1da273522bfff4a4971b4ffc31e365f60fdbbfe b/fuzzers/corpora/midx/f1da273522bfff4a4971b4ffc31e365f60fdbbfe
new file mode 100644
index 0000000..7417bb7
--- /dev/null
+++ b/fuzzers/corpora/midx/f1da273522bfff4a4971b4ffc31e365f60fdbbfe
Binary files differ
diff --git a/fuzzers/corpora/midx/f2fe69d30ec47e78a9e92f1423698a52270672b2 b/fuzzers/corpora/midx/f2fe69d30ec47e78a9e92f1423698a52270672b2
new file mode 100644
index 0000000..6eb21da
--- /dev/null
+++ b/fuzzers/corpora/midx/f2fe69d30ec47e78a9e92f1423698a52270672b2
Binary files differ
diff --git a/fuzzers/corpora/midx/f368bb6f633587a7bb271de7e20695f178c89686 b/fuzzers/corpora/midx/f368bb6f633587a7bb271de7e20695f178c89686
new file mode 100644
index 0000000..0c0cac9
--- /dev/null
+++ b/fuzzers/corpora/midx/f368bb6f633587a7bb271de7e20695f178c89686
Binary files differ
diff --git a/fuzzers/corpora/midx/f38ced5a16edaceb5f527ebc35e7870f42586c90 b/fuzzers/corpora/midx/f38ced5a16edaceb5f527ebc35e7870f42586c90
new file mode 100644
index 0000000..adc467a
--- /dev/null
+++ b/fuzzers/corpora/midx/f38ced5a16edaceb5f527ebc35e7870f42586c90
Binary files differ
diff --git a/fuzzers/corpora/midx/f404371362ae68ffb2837ce1766346ebb645d173 b/fuzzers/corpora/midx/f404371362ae68ffb2837ce1766346ebb645d173
new file mode 100644
index 0000000..a0b47bc
--- /dev/null
+++ b/fuzzers/corpora/midx/f404371362ae68ffb2837ce1766346ebb645d173
Binary files differ
diff --git a/fuzzers/corpora/midx/f473b5e1cf51502345f5c1840ec3948d308dd314 b/fuzzers/corpora/midx/f473b5e1cf51502345f5c1840ec3948d308dd314
new file mode 100644
index 0000000..301811d
--- /dev/null
+++ b/fuzzers/corpora/midx/f473b5e1cf51502345f5c1840ec3948d308dd314
Binary files differ
diff --git a/fuzzers/corpora/midx/f4ad43d6f913c3be6243dfc439e4b6f5b2e814b9 b/fuzzers/corpora/midx/f4ad43d6f913c3be6243dfc439e4b6f5b2e814b9
new file mode 100644
index 0000000..9e3bc98
--- /dev/null
+++ b/fuzzers/corpora/midx/f4ad43d6f913c3be6243dfc439e4b6f5b2e814b9
Binary files differ
diff --git a/fuzzers/corpora/midx/f4cde4083a974d755a38bf5ea3820f78b576754a b/fuzzers/corpora/midx/f4cde4083a974d755a38bf5ea3820f78b576754a
new file mode 100644
index 0000000..0699a32
--- /dev/null
+++ b/fuzzers/corpora/midx/f4cde4083a974d755a38bf5ea3820f78b576754a
Binary files differ
diff --git a/fuzzers/corpora/midx/f5888d0dcacda196d73772aabc18fe2ad6e1dfa2 b/fuzzers/corpora/midx/f5888d0dcacda196d73772aabc18fe2ad6e1dfa2
new file mode 100644
index 0000000..488911f
--- /dev/null
+++ b/fuzzers/corpora/midx/f5888d0dcacda196d73772aabc18fe2ad6e1dfa2
Binary files differ
diff --git a/fuzzers/corpora/midx/f5c3577a62d401f071d5edaa77c54ae98d6a0318 b/fuzzers/corpora/midx/f5c3577a62d401f071d5edaa77c54ae98d6a0318
new file mode 100644
index 0000000..bfec007
--- /dev/null
+++ b/fuzzers/corpora/midx/f5c3577a62d401f071d5edaa77c54ae98d6a0318
@@ -0,0 +1 @@
+Ïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïïï \ No newline at end of file
diff --git a/fuzzers/corpora/midx/f5dee0d9da0d6950069ac36b1880090a20f50f3e b/fuzzers/corpora/midx/f5dee0d9da0d6950069ac36b1880090a20f50f3e
new file mode 100644
index 0000000..9330af7
--- /dev/null
+++ b/fuzzers/corpora/midx/f5dee0d9da0d6950069ac36b1880090a20f50f3e
Binary files differ
diff --git a/fuzzers/corpora/midx/f605fff495fef2719585c706c05c350812402a35 b/fuzzers/corpora/midx/f605fff495fef2719585c706c05c350812402a35
new file mode 100644
index 0000000..7cae730
--- /dev/null
+++ b/fuzzers/corpora/midx/f605fff495fef2719585c706c05c350812402a35
Binary files differ
diff --git a/fuzzers/corpora/midx/f6fdaf4e77e29c780b9e0b91637777575bebfab4 b/fuzzers/corpora/midx/f6fdaf4e77e29c780b9e0b91637777575bebfab4
new file mode 100644
index 0000000..0b2545e
--- /dev/null
+++ b/fuzzers/corpora/midx/f6fdaf4e77e29c780b9e0b91637777575bebfab4
Binary files differ
diff --git a/fuzzers/corpora/midx/f72700bd65fa86c588607ec50d60a9c684c86f43 b/fuzzers/corpora/midx/f72700bd65fa86c588607ec50d60a9c684c86f43
new file mode 100644
index 0000000..3dfa423
--- /dev/null
+++ b/fuzzers/corpora/midx/f72700bd65fa86c588607ec50d60a9c684c86f43
Binary files differ
diff --git a/fuzzers/corpora/midx/f777db12ef18f002febd5af13e2b948c95d964c3 b/fuzzers/corpora/midx/f777db12ef18f002febd5af13e2b948c95d964c3
new file mode 100644
index 0000000..1869c3d
--- /dev/null
+++ b/fuzzers/corpora/midx/f777db12ef18f002febd5af13e2b948c95d964c3
Binary files differ
diff --git a/fuzzers/corpora/midx/f777e9274d508e7ac1069e2a04bedc042942491c b/fuzzers/corpora/midx/f777e9274d508e7ac1069e2a04bedc042942491c
new file mode 100644
index 0000000..cb62442
--- /dev/null
+++ b/fuzzers/corpora/midx/f777e9274d508e7ac1069e2a04bedc042942491c
Binary files differ
diff --git a/fuzzers/corpora/midx/f81306f8ceaec3d06d5d34afa9769d15f0d209eb b/fuzzers/corpora/midx/f81306f8ceaec3d06d5d34afa9769d15f0d209eb
new file mode 100644
index 0000000..4a36e1f
--- /dev/null
+++ b/fuzzers/corpora/midx/f81306f8ceaec3d06d5d34afa9769d15f0d209eb
Binary files differ
diff --git a/fuzzers/corpora/midx/f84c2b36689f22809d9bda00febab557c381ffa4 b/fuzzers/corpora/midx/f84c2b36689f22809d9bda00febab557c381ffa4
new file mode 100644
index 0000000..de7f301
--- /dev/null
+++ b/fuzzers/corpora/midx/f84c2b36689f22809d9bda00febab557c381ffa4
Binary files differ
diff --git a/fuzzers/corpora/midx/f98168fa74c26b17ad0c3002f2263beb0af7c0ce b/fuzzers/corpora/midx/f98168fa74c26b17ad0c3002f2263beb0af7c0ce
new file mode 100644
index 0000000..c1c2043
--- /dev/null
+++ b/fuzzers/corpora/midx/f98168fa74c26b17ad0c3002f2263beb0af7c0ce
Binary files differ
diff --git a/fuzzers/corpora/midx/fa6759d6a2807bbad83ba21761772c0119122c35 b/fuzzers/corpora/midx/fa6759d6a2807bbad83ba21761772c0119122c35
new file mode 100644
index 0000000..79b7fe1
--- /dev/null
+++ b/fuzzers/corpora/midx/fa6759d6a2807bbad83ba21761772c0119122c35
Binary files differ
diff --git a/fuzzers/corpora/midx/fa8b927b25a67fa3d60b12c53ac365366cc2b52d b/fuzzers/corpora/midx/fa8b927b25a67fa3d60b12c53ac365366cc2b52d
new file mode 100644
index 0000000..22522c2
--- /dev/null
+++ b/fuzzers/corpora/midx/fa8b927b25a67fa3d60b12c53ac365366cc2b52d
Binary files differ
diff --git a/fuzzers/corpora/midx/fc5e4b78e59daebed1118389b57a386981f2430e b/fuzzers/corpora/midx/fc5e4b78e59daebed1118389b57a386981f2430e
new file mode 100644
index 0000000..5c6a22d
--- /dev/null
+++ b/fuzzers/corpora/midx/fc5e4b78e59daebed1118389b57a386981f2430e
Binary files differ
diff --git a/fuzzers/corpora/midx/fdaf408880429153cfcf5d978727cd7b84c3d60e b/fuzzers/corpora/midx/fdaf408880429153cfcf5d978727cd7b84c3d60e
new file mode 100644
index 0000000..76f7c52
--- /dev/null
+++ b/fuzzers/corpora/midx/fdaf408880429153cfcf5d978727cd7b84c3d60e
Binary files differ
diff --git a/fuzzers/corpora/midx/fe50e7564a28683b24c57f8bcdcb3fbfa61f5c6a b/fuzzers/corpora/midx/fe50e7564a28683b24c57f8bcdcb3fbfa61f5c6a
new file mode 100644
index 0000000..f203720
--- /dev/null
+++ b/fuzzers/corpora/midx/fe50e7564a28683b24c57f8bcdcb3fbfa61f5c6a
Binary files differ
diff --git a/fuzzers/corpora/midx/fe64b998872d3ad87df2019173ddc52686841d7d b/fuzzers/corpora/midx/fe64b998872d3ad87df2019173ddc52686841d7d
new file mode 100644
index 0000000..1b4e28e
--- /dev/null
+++ b/fuzzers/corpora/midx/fe64b998872d3ad87df2019173ddc52686841d7d
Binary files differ
diff --git a/fuzzers/corpora/midx/fec56c7cc86871aaa9c7a947c4084307cac2778d b/fuzzers/corpora/midx/fec56c7cc86871aaa9c7a947c4084307cac2778d
new file mode 100644
index 0000000..c57cd18
--- /dev/null
+++ b/fuzzers/corpora/midx/fec56c7cc86871aaa9c7a947c4084307cac2778d
Binary files differ
diff --git a/fuzzers/corpora/midx/ff164dfc56dd28709488130dc6dfc17406bf9e9d b/fuzzers/corpora/midx/ff164dfc56dd28709488130dc6dfc17406bf9e9d
new file mode 100644
index 0000000..ceda10e
--- /dev/null
+++ b/fuzzers/corpora/midx/ff164dfc56dd28709488130dc6dfc17406bf9e9d
Binary files differ
diff --git a/fuzzers/corpora/midx/ff7035b3c055718728a6025b3cdf55c34c4c744b b/fuzzers/corpora/midx/ff7035b3c055718728a6025b3cdf55c34c4c744b
new file mode 100644
index 0000000..8c963ab
--- /dev/null
+++ b/fuzzers/corpora/midx/ff7035b3c055718728a6025b3cdf55c34c4c744b
Binary files differ
diff --git a/fuzzers/corpora/objects/blob b/fuzzers/corpora/objects/blob
new file mode 100644
index 0000000..b09444c
--- /dev/null
+++ b/fuzzers/corpora/objects/blob
@@ -0,0 +1,359 @@
+libgit2 - the Git linkable library
+==================================
+
+[![Travis Build Status](https://secure.travis-ci.org/libgit2/libgit2.svg?branch=master)](http://travis-ci.org/libgit2/libgit2)
+[![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/xvof5b4t5480a2q3/branch/master?svg=true)](https://ci.appveyor.com/project/libgit2/libgit2/branch/master)
+[![Coverity Scan Build Status](https://scan.coverity.com/projects/639/badge.svg)](https://scan.coverity.com/projects/639)
+
+`libgit2` is a portable, pure C implementation of the Git core methods
+provided as a linkable library with a solid API, allowing to build Git
+functionality into your application. Language bindings like
+[Rugged](https://github.com/libgit2/rugged) (Ruby),
+[LibGit2Sharp](https://github.com/libgit2/libgit2sharp) (.NET),
+[pygit2](http://www.pygit2.org/) (Python) and
+[NodeGit](http://nodegit.org) (Node) allow you to build Git tooling
+in your favorite language.
+
+`libgit2` is used to power Git GUI clients like
+[GitKraken](https://gitkraken.com/) and [gmaster](https://gmaster.io/)
+and on Git hosting providers like [GitHub](https://github.com/),
+[GitLab](https://gitlab.com/) and
+[Visual Studio Team Services](https://visualstudio.com/team-services/).
+We perform the merge every time you click "merge pull request".
+
+`libgit2` is licensed under a **very permissive license** (GPLv2 with a special
+Linking Exception). This basically means that you can link it (unmodified)
+with any kind of software without having to release its source code.
+Additionally, the example code has been released to the public domain (see the
+[separate license](examples/COPYING) for more information).
+
+Quick Start
+===========
+
+**Prerequisites** for building libgit2:
+
+1. [CMake](https://cmake.org/), and is recommended to be installed into
+ your `PATH`.
+2. [Python](https://www.python.org) is used by our test framework, and
+ should be installed into your `PATH`.
+3. C compiler: libgit2 is C90 and should compile on most compilers.
+ * Windows: Visual Studio is recommended
+ * Mac: Xcode is recommended
+ * Unix: gcc or clang is recommended.
+
+**Build**
+
+1. Create a build directory beneath the libgit2 source directory, and change
+ into it: `mkdir build && cd build`
+2. Create the cmake build environment: `cmake ..`
+3. Build libgit2: `cmake --build .`
+
+Trouble with these steps? Read `TROUBLESHOOTING.md`. More detailed build
+guidance is available below.
+
+Getting Help
+============
+
+**Join us on Slack**
+
+Visit [slack.libgit2.org](http://slack.libgit2.org/) to sign up, then join
+us in `#libgit2`. If you prefer IRC, you can also point your client to our
+slack channel once you've registered.
+
+**Getting Help**
+
+If you have questions about the library, please be sure to check out the
+[API documentation](http://libgit2.github.com/libgit2/). If you still have
+questions, reach out to us on Slack or post a question on
+[StackOverflow](http://stackoverflow.com/questions/tagged/libgit2) (with the `libgit2` tag).
+
+**Reporting Bugs**
+
+Please open a [GitHub Issue](https://github.com/libgit2/libgit2/issues) and
+include as much information as possible. If possible, provide sample code
+that illustrates the problem you're seeing. If you're seeing a bug only
+on a specific repository, please provide a link to it if possible.
+
+We ask that you not open a GitHub Issue for help, only for bug reports.
+
+**Reporting Security Issues**
+
+In case you think to have found a security issue with libgit2, please do not
+open a public issue. Instead, you can report the issue to the private mailing
+list [security@libgit2.com](mailto:security@libgit2.com).
+
+What It Can Do
+==============
+
+libgit2 provides you with the ability to manage Git repositories in the
+programming language of your choice. It's used in production to power many
+applications including GitHub.com, Plastic SCM and Visual Studio Team Services.
+
+It does not aim to replace the git tool or its user-facing commands. Some APIs
+resemble the plumbing commands as those align closely with the concepts of the
+Git system, but most commands a user would type are out of scope for this
+library to implement directly.
+
+The library provides:
+
+* SHA conversions, formatting and shortening
+* abstracted ODB backend system
+* commit, tag, tree and blob parsing, editing, and write-back
+* tree traversal
+* revision walking
+* index file (staging area) manipulation
+* reference management (including packed references)
+* config file management
+* high level repository management
+* thread safety and reentrancy
+* descriptive and detailed error messages
+* ...and more (over 175 different API calls)
+
+As libgit2 is purely a consumer of the Git system, we have to
+adjust to changes made upstream. This has two major consequences:
+
+* Some changes may require us to change provided interfaces. While we try to
+ implement functions in a generic way so that no future changes are required,
+ we cannot promise a completely stable API.
+* As we have to keep up with changes in behavior made upstream, we may lag
+ behind in some areas. We usually to document these incompatibilities in our
+ issue tracker with the label "git change".
+
+Optional dependencies
+=====================
+
+While the library provides git functionality without the need for
+dependencies, it can make use of a few libraries to add to it:
+
+- pthreads (non-Windows) to enable threadsafe access as well as multi-threaded pack generation
+- OpenSSL (non-Windows) to talk over HTTPS and provide the SHA-1 functions
+- LibSSH2 to enable the SSH transport
+- iconv (OSX) to handle the HFS+ path encoding peculiarities
+
+Initialization
+===============
+
+The library needs to keep track of some global state. Call
+
+ git_libgit2_init();
+
+before calling any other libgit2 functions. You can call this function many times. A matching number of calls to
+
+ git_libgit2_shutdown();
+
+will free the resources. Note that if you have worker threads, you should
+call `git_libgit2_shutdown` *after* those threads have exited. If you
+require assistance coordinating this, simply have the worker threads call
+`git_libgit2_init` at startup and `git_libgit2_shutdown` at shutdown.
+
+Threading
+=========
+
+See [THREADING](THREADING.md) for information
+
+Conventions
+===========
+
+See [CONVENTIONS](CONVENTIONS.md) for an overview of the external
+and internal API/coding conventions we use.
+
+Building libgit2 - Using CMake
+==============================
+
+Building
+--------
+
+`libgit2` builds cleanly on most platforms without any external dependencies.
+Under Unix-like systems, like Linux, \*BSD and Mac OS X, libgit2 expects `pthreads` to be available;
+they should be installed by default on all systems. Under Windows, libgit2 uses the native Windows API
+for threading.
+
+The `libgit2` library is built using [CMake](<https://cmake.org/>) (version 2.8 or newer) on all platforms.
+
+On most systems you can build the library using the following commands
+
+ $ mkdir build && cd build
+ $ cmake ..
+ $ cmake --build .
+
+Alternatively you can point the CMake GUI tool to the CMakeLists.txt file and generate platform specific build project or IDE workspace.
+
+Running Tests
+-------------
+
+Once built, you can run the tests from the `build` directory with the command
+
+ $ ctest -V
+
+Alternatively you can run the test suite directly using,
+
+ $ ./libgit2_tests
+
+Invoking the test suite directly is useful because it allows you to execute
+individual tests, or groups of tests using the `-s` flag. For example, to
+run the index tests:
+
+ $ ./libgit2_tests -sindex
+
+To run a single test named `index::racy::diff`, which corresponds to the test
+function (`test_index_racy__diff`)[https://github.com/libgit2/libgit2/blob/master/tests/index/racy.c#L23]:
+
+ $ ./libgit2_tests -sindex::racy::diff
+
+The test suite will print a `.` for every passing test, and an `F` for any
+failing test. An `S` indicates that a test was skipped because it is not
+applicable to your platform or is particularly expensive.
+
+**Note:** There should be _no_ failing tests when you build an unmodified
+source tree from a [release](https://github.com/libgit2/libgit2/releases),
+or from the [master branch](https://github.com/libgit2/libgit2/tree/master).
+Please contact us or [open an issue](https://github.com/libgit2/libgit2/issues)
+if you see test failures.
+
+Installation
+------------
+
+To install the library you can specify the install prefix by setting:
+
+ $ cmake .. -DCMAKE_INSTALL_PREFIX=/install/prefix
+ $ cmake --build . --target install
+
+Advanced Usage
+--------------
+
+For more advanced use or questions about CMake please read <https://cmake.org/Wiki/CMake_FAQ>.
+
+The following CMake variables are declared:
+
+- `BIN_INSTALL_DIR`: Where to install binaries to.
+- `LIB_INSTALL_DIR`: Where to install libraries to.
+- `INCLUDE_INSTALL_DIR`: Where to install headers to.
+- `BUILD_SHARED_LIBS`: Build libgit2 as a Shared Library (defaults to ON)
+- `BUILD_TESTS`: Build [Clar](https://github.com/vmg/clar)-based test suite (defaults to ON)
+- `THREADSAFE`: Build libgit2 with threading support (defaults to ON)
+- `STDCALL`: Build libgit2 as `stdcall`. Turn off for `cdecl` (Windows; defaults to ON)
+
+Compiler and linker options
+---------------------------
+
+CMake lets you specify a few variables to control the behavior of the
+compiler and linker. These flags are rarely used but can be useful for
+64-bit to 32-bit cross-compilation.
+
+- `CMAKE_C_FLAGS`: Set your own compiler flags
+- `CMAKE_FIND_ROOT_PATH`: Override the search path for libraries
+- `ZLIB_LIBRARY`, `OPENSSL_SSL_LIBRARY` AND `OPENSSL_CRYPTO_LIBRARY`:
+Tell CMake where to find those specific libraries
+
+MacOS X
+-------
+
+If you want to build a universal binary for Mac OS X, CMake sets it
+all up for you if you use `-DCMAKE_OSX_ARCHITECTURES="i386;x86_64"`
+when configuring.
+
+Android
+-------
+
+Extract toolchain from NDK using, `make-standalone-toolchain.sh` script.
+Optionally, crosscompile and install OpenSSL inside of it. Then create CMake
+toolchain file that configures paths to your crosscompiler (substitute `{PATH}`
+with full path to the toolchain):
+
+ SET(CMAKE_SYSTEM_NAME Linux)
+ SET(CMAKE_SYSTEM_VERSION Android)
+
+ SET(CMAKE_C_COMPILER {PATH}/bin/arm-linux-androideabi-gcc)
+ SET(CMAKE_CXX_COMPILER {PATH}/bin/arm-linux-androideabi-g++)
+ SET(CMAKE_FIND_ROOT_PATH {PATH}/sysroot/)
+
+ SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
+ SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
+ SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
+
+Add `-DCMAKE_TOOLCHAIN_FILE={pathToToolchainFile}` to cmake command
+when configuring.
+
+Language Bindings
+==================================
+
+Here are the bindings to libgit2 that are currently available:
+
+* C++
+ * libqgit2, Qt bindings <https://projects.kde.org/projects/playground/libs/libqgit2/repository/>
+* Chicken Scheme
+ * chicken-git <https://wiki.call-cc.org/egg/git>
+* D
+ * dlibgit <https://github.com/s-ludwig/dlibgit>
+* Delphi
+ * GitForDelphi <https://github.com/libgit2/GitForDelphi>
+* Erlang
+ * Geef <https://github.com/carlosmn/geef>
+* Go
+ * git2go <https://github.com/libgit2/git2go>
+* GObject
+ * libgit2-glib <https://wiki.gnome.org/Projects/Libgit2-glib>
+* Guile
+ * Guile-Git <https://gitlab.com/guile-git/guile-git>
+* Haskell
+ * hgit2 <https://github.com/jwiegley/gitlib>
+* Java
+ * Jagged <https://github.com/ethomson/jagged>
+* Julia
+ * LibGit2.jl <https://github.com/jakebolewski/LibGit2.jl>
+* Lua
+ * luagit2 <https://github.com/libgit2/luagit2>
+* .NET
+ * libgit2sharp <https://github.com/libgit2/libgit2sharp>
+* Node.js
+ * nodegit <https://github.com/nodegit/nodegit>
+* Objective-C
+ * objective-git <https://github.com/libgit2/objective-git>
+* OCaml
+ * ocaml-libgit2 <https://github.com/fxfactorial/ocaml-libgit2>
+* Parrot Virtual Machine
+ * parrot-libgit2 <https://github.com/letolabs/parrot-libgit2>
+* Perl
+ * Git-Raw <https://github.com/jacquesg/p5-Git-Raw>
+* PHP
+ * php-git <https://github.com/libgit2/php-git>
+* PowerShell
+ * PSGit <https://github.com/PoshCode/PSGit>
+* Python
+ * pygit2 <https://github.com/libgit2/pygit2>
+* R
+ * git2r <https://github.com/ropensci/git2r>
+* Ruby
+ * Rugged <https://github.com/libgit2/rugged>
+* Rust
+ * git2-rs <https://github.com/alexcrichton/git2-rs>
+* Swift
+ * SwiftGit2 <https://github.com/SwiftGit2/SwiftGit2>
+* Vala
+ * libgit2.vapi <https://github.com/apmasell/vapis/blob/master/libgit2.vapi>
+
+If you start another language binding to libgit2, please let us know so
+we can add it to the list.
+
+How Can I Contribute?
+==================================
+
+We welcome new contributors! We have a number of issues marked as
+["up for grabs"](https://github.com/libgit2/libgit2/issues?q=is%3Aissue+is%3Aopen+label%3A%22up+for+grabs%22)
+and
+["easy fix"](https://github.com/libgit2/libgit2/issues?utf8=✓&q=is%3Aissue+is%3Aopen+label%3A%22easy+fix%22)
+that are good places to jump in and get started. There's much more detailed
+information in our list of [outstanding projects](PROJECTS.md).
+
+Please be sure to check the [contribution guidelines](CONTRIBUTING.md) to
+understand our workflow, and the libgit2 [coding conventions](CONVENTIONS.md).
+
+License
+==================================
+
+`libgit2` is under GPL2 **with linking exception**. This means you can link to
+and use the library from any program, proprietary or open source; paid or
+gratis. However, if you modify libgit2 itself, you must distribute the
+source to your modified version of libgit2.
+
+See the [COPYING file](COPYING) for the full license text.
diff --git a/fuzzers/corpora/objects/commit b/fuzzers/corpora/objects/commit
new file mode 100644
index 0000000..519495f
--- /dev/null
+++ b/fuzzers/corpora/objects/commit
@@ -0,0 +1,20 @@
+tree 3e7ac388cadae960fe7e22175ce0da878afe9d18
+parent 8b89f362a34fcccdf1c6c5f3445895b71d9c6d56
+parent c590b41fe4057a84a9bd31a5605ceef2c309b0f8
+author Patrick Steinhardt <ps@pks.im> 1538760730 +0200
+committer GitHub <noreply@github.com> 1538760730 +0200
+gpgsig -----BEGIN PGP SIGNATURE-----
+
+ wsBcBAABCAAQBQJbt6AaCRBK7hj4Ov3rIwAAdHIIAKZGIpS0dAirVRt5NVFj3ZtC
+ o2Q3ADC0XpYLKkEsClhG7pVtr7MRZZ8+qaJpbxn9j9WZZ4UtEeDjseos+pMNn9Mf
+ OQQntNzGAbHSw0apyYT+mTUKaVONPev4fw9Lnc/RJ/iWwHx+4gmgNqLwV3foaCW9
+ w1JzCL+BVJyZI80jrEehihhUnpIUOuMBwGjzSt54Zn5JqviC4cIldF2sXFGQqvsq
+ 3WDNnEUYanU6cLAdb9Pd6bVBI1EJnRLxehSeYiSaRPmLhQyhkH8KZ5lSi8iuH1C4
+ bjA6HaEUwCeq0k9Le6BUu93BExEOFcuu8+zEKCrwCdSwdEQ3Iakv8dh7XlT9iUY=
+ =nGP0
+ -----END PGP SIGNATURE-----
+
+
+Merge pull request #4834 from pks-t/pks/v0.27.5
+
+Security release v0.27.5 \ No newline at end of file
diff --git a/fuzzers/corpora/objects/tag b/fuzzers/corpora/objects/tag
new file mode 100644
index 0000000..f5f1c5e
--- /dev/null
+++ b/fuzzers/corpora/objects/tag
@@ -0,0 +1,6 @@
+object a8d447f68076d1520f69649bb52629941be7031f
+type commit
+tag testtag
+tagger Patrick Steinhardt <ps@pks.im> 1539253015 +0200
+
+Tag message
diff --git a/fuzzers/corpora/objects/tree b/fuzzers/corpora/objects/tree
new file mode 100644
index 0000000..d6639d8
--- /dev/null
+++ b/fuzzers/corpora/objects/tree
Binary files differ
diff --git a/fuzzers/corpora/packfile/004bd06c91c0dc8ab7e963f4b5e87be00292911e b/fuzzers/corpora/packfile/004bd06c91c0dc8ab7e963f4b5e87be00292911e
new file mode 100644
index 0000000..53c9642
--- /dev/null
+++ b/fuzzers/corpora/packfile/004bd06c91c0dc8ab7e963f4b5e87be00292911e
@@ -0,0 +1 @@
+ÐPACK \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/00b67414c7b17916b3bd0a3d02284937fa0c4378 b/fuzzers/corpora/packfile/00b67414c7b17916b3bd0a3d02284937fa0c4378
new file mode 100644
index 0000000..838a007
--- /dev/null
+++ b/fuzzers/corpora/packfile/00b67414c7b17916b3bd0a3d02284937fa0c4378
Binary files differ
diff --git a/fuzzers/corpora/packfile/02eaeb43f0ec7dbfd91bd75e7ddcc7fd590dbc77 b/fuzzers/corpora/packfile/02eaeb43f0ec7dbfd91bd75e7ddcc7fd590dbc77
new file mode 100644
index 0000000..48fe2b3
--- /dev/null
+++ b/fuzzers/corpora/packfile/02eaeb43f0ec7dbfd91bd75e7ddcc7fd590dbc77
Binary files differ
diff --git a/fuzzers/corpora/packfile/02f4286569be24124d8ab209733b7492f7560310 b/fuzzers/corpora/packfile/02f4286569be24124d8ab209733b7492f7560310
new file mode 100644
index 0000000..3944cd0
--- /dev/null
+++ b/fuzzers/corpora/packfile/02f4286569be24124d8ab209733b7492f7560310
Binary files differ
diff --git a/fuzzers/corpora/packfile/037ba5f9d6d695aa4739810f8bea6e795c1d7614 b/fuzzers/corpora/packfile/037ba5f9d6d695aa4739810f8bea6e795c1d7614
new file mode 100644
index 0000000..f4ea551
--- /dev/null
+++ b/fuzzers/corpora/packfile/037ba5f9d6d695aa4739810f8bea6e795c1d7614
Binary files differ
diff --git a/fuzzers/corpora/packfile/038e06289ac876f109fc12ca4b8284497ca26821 b/fuzzers/corpora/packfile/038e06289ac876f109fc12ca4b8284497ca26821
new file mode 100644
index 0000000..24923c4
--- /dev/null
+++ b/fuzzers/corpora/packfile/038e06289ac876f109fc12ca4b8284497ca26821
Binary files differ
diff --git a/fuzzers/corpora/packfile/042dc4512fa3d391c5170cf3aa61e6a638f84342 b/fuzzers/corpora/packfile/042dc4512fa3d391c5170cf3aa61e6a638f84342
new file mode 100644
index 0000000..597a6db
--- /dev/null
+++ b/fuzzers/corpora/packfile/042dc4512fa3d391c5170cf3aa61e6a638f84342
@@ -0,0 +1 @@
+i \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/044bf19babf3f9cde07adbfa2a45c7508538cbe8 b/fuzzers/corpora/packfile/044bf19babf3f9cde07adbfa2a45c7508538cbe8
new file mode 100644
index 0000000..73a4013
--- /dev/null
+++ b/fuzzers/corpora/packfile/044bf19babf3f9cde07adbfa2a45c7508538cbe8
Binary files differ
diff --git a/fuzzers/corpora/packfile/044e12ea43bee3c4befe27ba4687bee98d505fd7 b/fuzzers/corpora/packfile/044e12ea43bee3c4befe27ba4687bee98d505fd7
new file mode 100644
index 0000000..1e12966
--- /dev/null
+++ b/fuzzers/corpora/packfile/044e12ea43bee3c4befe27ba4687bee98d505fd7
Binary files differ
diff --git a/fuzzers/corpora/packfile/061fb208431db793bbd3645b7a16058a1e2a2412 b/fuzzers/corpora/packfile/061fb208431db793bbd3645b7a16058a1e2a2412
new file mode 100644
index 0000000..12183ab
--- /dev/null
+++ b/fuzzers/corpora/packfile/061fb208431db793bbd3645b7a16058a1e2a2412
@@ -0,0 +1 @@
+º \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/06576556d1ad802f247cad11ae748be47b70cd9c b/fuzzers/corpora/packfile/06576556d1ad802f247cad11ae748be47b70cd9c
new file mode 100644
index 0000000..ac044e5
--- /dev/null
+++ b/fuzzers/corpora/packfile/06576556d1ad802f247cad11ae748be47b70cd9c
@@ -0,0 +1 @@
+R \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/06ceea0c98756d302c302410fffe0dc54a055486 b/fuzzers/corpora/packfile/06ceea0c98756d302c302410fffe0dc54a055486
new file mode 100644
index 0000000..add37f9
--- /dev/null
+++ b/fuzzers/corpora/packfile/06ceea0c98756d302c302410fffe0dc54a055486
Binary files differ
diff --git a/fuzzers/corpora/packfile/071e65ac0bf08f2424a89a4a499004c1bb9f3f6c b/fuzzers/corpora/packfile/071e65ac0bf08f2424a89a4a499004c1bb9f3f6c
new file mode 100644
index 0000000..748750c
--- /dev/null
+++ b/fuzzers/corpora/packfile/071e65ac0bf08f2424a89a4a499004c1bb9f3f6c
Binary files differ
diff --git a/fuzzers/corpora/packfile/0739ff2f064568a4d775c8061958e66c419dbea0 b/fuzzers/corpora/packfile/0739ff2f064568a4d775c8061958e66c419dbea0
new file mode 100644
index 0000000..aed5f16
--- /dev/null
+++ b/fuzzers/corpora/packfile/0739ff2f064568a4d775c8061958e66c419dbea0
Binary files differ
diff --git a/fuzzers/corpora/packfile/077760469bf8392342d09329c732b98d24be2c30 b/fuzzers/corpora/packfile/077760469bf8392342d09329c732b98d24be2c30
new file mode 100644
index 0000000..c9553bc
--- /dev/null
+++ b/fuzzers/corpora/packfile/077760469bf8392342d09329c732b98d24be2c30
Binary files differ
diff --git a/fuzzers/corpora/packfile/08534f33c201a45017b502e90a800f1b708ebcb3 b/fuzzers/corpora/packfile/08534f33c201a45017b502e90a800f1b708ebcb3
new file mode 100644
index 0000000..b7d5379
--- /dev/null
+++ b/fuzzers/corpora/packfile/08534f33c201a45017b502e90a800f1b708ebcb3
@@ -0,0 +1 @@
+\ \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/09e9046a7d6125cf2915a326a1504dd75d0543b5 b/fuzzers/corpora/packfile/09e9046a7d6125cf2915a326a1504dd75d0543b5
new file mode 100644
index 0000000..86d7321
--- /dev/null
+++ b/fuzzers/corpora/packfile/09e9046a7d6125cf2915a326a1504dd75d0543b5
@@ -0,0 +1 @@
+ÿ \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/0ade7c2cf97f75d009975f4d720d1fa6c19f4897 b/fuzzers/corpora/packfile/0ade7c2cf97f75d009975f4d720d1fa6c19f4897
new file mode 100644
index 0000000..f11c82a
--- /dev/null
+++ b/fuzzers/corpora/packfile/0ade7c2cf97f75d009975f4d720d1fa6c19f4897
@@ -0,0 +1 @@
+9 \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/0c316c67c1450aee57ffa3f74c19ea5d32d44622 b/fuzzers/corpora/packfile/0c316c67c1450aee57ffa3f74c19ea5d32d44622
new file mode 100644
index 0000000..08d7351
--- /dev/null
+++ b/fuzzers/corpora/packfile/0c316c67c1450aee57ffa3f74c19ea5d32d44622
Binary files differ
diff --git a/fuzzers/corpora/packfile/0c395c44e4dd5b67caae8a98a66741e17e8af136 b/fuzzers/corpora/packfile/0c395c44e4dd5b67caae8a98a66741e17e8af136
new file mode 100644
index 0000000..e899a79
--- /dev/null
+++ b/fuzzers/corpora/packfile/0c395c44e4dd5b67caae8a98a66741e17e8af136
Binary files differ
diff --git a/fuzzers/corpora/packfile/0cb9120e5ae00b0d660a111ef85fc9194a5f244a b/fuzzers/corpora/packfile/0cb9120e5ae00b0d660a111ef85fc9194a5f244a
new file mode 100644
index 0000000..9fb6a66
--- /dev/null
+++ b/fuzzers/corpora/packfile/0cb9120e5ae00b0d660a111ef85fc9194a5f244a
Binary files differ
diff --git a/fuzzers/corpora/packfile/0d44e7156d04cd269fd1219c58c3b79dc8c41d60 b/fuzzers/corpora/packfile/0d44e7156d04cd269fd1219c58c3b79dc8c41d60
new file mode 100644
index 0000000..cf18be0
--- /dev/null
+++ b/fuzzers/corpora/packfile/0d44e7156d04cd269fd1219c58c3b79dc8c41d60
Binary files differ
diff --git a/fuzzers/corpora/packfile/0d77a48bea1dde6e5d391a65456dc0aa3d9cc5e3 b/fuzzers/corpora/packfile/0d77a48bea1dde6e5d391a65456dc0aa3d9cc5e3
new file mode 100644
index 0000000..6d3c9c0
--- /dev/null
+++ b/fuzzers/corpora/packfile/0d77a48bea1dde6e5d391a65456dc0aa3d9cc5e3
Binary files differ
diff --git a/fuzzers/corpora/packfile/0db25107ff248616cadc75b7819b21d06394cf25 b/fuzzers/corpora/packfile/0db25107ff248616cadc75b7819b21d06394cf25
new file mode 100644
index 0000000..c158d57
--- /dev/null
+++ b/fuzzers/corpora/packfile/0db25107ff248616cadc75b7819b21d06394cf25
Binary files differ
diff --git a/fuzzers/corpora/packfile/0debae2db7ef2933f386bac21a2d3bebb473070e b/fuzzers/corpora/packfile/0debae2db7ef2933f386bac21a2d3bebb473070e
new file mode 100644
index 0000000..1f9443f
--- /dev/null
+++ b/fuzzers/corpora/packfile/0debae2db7ef2933f386bac21a2d3bebb473070e
Binary files differ
diff --git a/fuzzers/corpora/packfile/0e2d48524de33394ca82ea3a43f5f04aac6e86c7 b/fuzzers/corpora/packfile/0e2d48524de33394ca82ea3a43f5f04aac6e86c7
new file mode 100644
index 0000000..1fd3289
--- /dev/null
+++ b/fuzzers/corpora/packfile/0e2d48524de33394ca82ea3a43f5f04aac6e86c7
Binary files differ
diff --git a/fuzzers/corpora/packfile/0e49f6aa78f3b2f6c3fa5d281d5b1052fa9951dc b/fuzzers/corpora/packfile/0e49f6aa78f3b2f6c3fa5d281d5b1052fa9951dc
new file mode 100644
index 0000000..286d703
--- /dev/null
+++ b/fuzzers/corpora/packfile/0e49f6aa78f3b2f6c3fa5d281d5b1052fa9951dc
Binary files differ
diff --git a/fuzzers/corpora/packfile/0f2982027f0b3b05250267b19e3969f8797e389e b/fuzzers/corpora/packfile/0f2982027f0b3b05250267b19e3969f8797e389e
new file mode 100644
index 0000000..57505d2
--- /dev/null
+++ b/fuzzers/corpora/packfile/0f2982027f0b3b05250267b19e3969f8797e389e
@@ -0,0 +1 @@
+ÐPACKïïðŽ \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/11f6ad8ec52a2984abaafd7c3b516503785c2072 b/fuzzers/corpora/packfile/11f6ad8ec52a2984abaafd7c3b516503785c2072
new file mode 100644
index 0000000..c1b0730
--- /dev/null
+++ b/fuzzers/corpora/packfile/11f6ad8ec52a2984abaafd7c3b516503785c2072
@@ -0,0 +1 @@
+x \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/123ca693d81a8cfd99760ff5ca9e152ded878537 b/fuzzers/corpora/packfile/123ca693d81a8cfd99760ff5ca9e152ded878537
new file mode 100644
index 0000000..e680914
--- /dev/null
+++ b/fuzzers/corpora/packfile/123ca693d81a8cfd99760ff5ca9e152ded878537
Binary files differ
diff --git a/fuzzers/corpora/packfile/12878ca5643ab15a4a847e74ddd84fb365736af2 b/fuzzers/corpora/packfile/12878ca5643ab15a4a847e74ddd84fb365736af2
new file mode 100644
index 0000000..6029463
--- /dev/null
+++ b/fuzzers/corpora/packfile/12878ca5643ab15a4a847e74ddd84fb365736af2
Binary files differ
diff --git a/fuzzers/corpora/packfile/13f292a24a9e79ae911f5d5e1ef7db0112601e64 b/fuzzers/corpora/packfile/13f292a24a9e79ae911f5d5e1ef7db0112601e64
new file mode 100644
index 0000000..36b85ed
--- /dev/null
+++ b/fuzzers/corpora/packfile/13f292a24a9e79ae911f5d5e1ef7db0112601e64
Binary files differ
diff --git a/fuzzers/corpora/packfile/13facd9b4b5b4509fee92c7ccc1c82ed90624172 b/fuzzers/corpora/packfile/13facd9b4b5b4509fee92c7ccc1c82ed90624172
new file mode 100644
index 0000000..82f4fa0
--- /dev/null
+++ b/fuzzers/corpora/packfile/13facd9b4b5b4509fee92c7ccc1c82ed90624172
Binary files differ
diff --git a/fuzzers/corpora/packfile/140092a21903fdc56c98de126725fa6bead98ab1 b/fuzzers/corpora/packfile/140092a21903fdc56c98de126725fa6bead98ab1
new file mode 100644
index 0000000..11006ca
--- /dev/null
+++ b/fuzzers/corpora/packfile/140092a21903fdc56c98de126725fa6bead98ab1
Binary files differ
diff --git a/fuzzers/corpora/packfile/1489f923c4dca729178b3e3233458550d8dddf29 b/fuzzers/corpora/packfile/1489f923c4dca729178b3e3233458550d8dddf29
new file mode 100644
index 0000000..09f370e
--- /dev/null
+++ b/fuzzers/corpora/packfile/1489f923c4dca729178b3e3233458550d8dddf29
Binary files differ
diff --git a/fuzzers/corpora/packfile/1501a58834f24f95442f190653a50dd67d01e19d b/fuzzers/corpora/packfile/1501a58834f24f95442f190653a50dd67d01e19d
new file mode 100644
index 0000000..3bbe254
--- /dev/null
+++ b/fuzzers/corpora/packfile/1501a58834f24f95442f190653a50dd67d01e19d
Binary files differ
diff --git a/fuzzers/corpora/packfile/15eddee57cafb11e927810d62230a6e104de1d5c b/fuzzers/corpora/packfile/15eddee57cafb11e927810d62230a6e104de1d5c
new file mode 100644
index 0000000..4c563e6
--- /dev/null
+++ b/fuzzers/corpora/packfile/15eddee57cafb11e927810d62230a6e104de1d5c
Binary files differ
diff --git a/fuzzers/corpora/packfile/1632aa4b049f1118306485b11c70c499a0200dd5 b/fuzzers/corpora/packfile/1632aa4b049f1118306485b11c70c499a0200dd5
new file mode 100644
index 0000000..677f3ab
--- /dev/null
+++ b/fuzzers/corpora/packfile/1632aa4b049f1118306485b11c70c499a0200dd5
Binary files differ
diff --git a/fuzzers/corpora/packfile/18e1cf33b179a5cbaaf0baac8279ec4ed1cbdcf3 b/fuzzers/corpora/packfile/18e1cf33b179a5cbaaf0baac8279ec4ed1cbdcf3
new file mode 100644
index 0000000..8445262
--- /dev/null
+++ b/fuzzers/corpora/packfile/18e1cf33b179a5cbaaf0baac8279ec4ed1cbdcf3
Binary files differ
diff --git a/fuzzers/corpora/packfile/18e768865207e0b90047487b66532b20bc74b1a2 b/fuzzers/corpora/packfile/18e768865207e0b90047487b66532b20bc74b1a2
new file mode 100644
index 0000000..2069d5b
--- /dev/null
+++ b/fuzzers/corpora/packfile/18e768865207e0b90047487b66532b20bc74b1a2
Binary files differ
diff --git a/fuzzers/corpora/packfile/1940c66b45a3bd5c847330b207fd87aee6e96194 b/fuzzers/corpora/packfile/1940c66b45a3bd5c847330b207fd87aee6e96194
new file mode 100644
index 0000000..5b8513e
--- /dev/null
+++ b/fuzzers/corpora/packfile/1940c66b45a3bd5c847330b207fd87aee6e96194
Binary files differ
diff --git a/fuzzers/corpora/packfile/1966ab31dc80ab75196b0cbf28e3960a0eb3f6c5 b/fuzzers/corpora/packfile/1966ab31dc80ab75196b0cbf28e3960a0eb3f6c5
new file mode 100644
index 0000000..572d311
--- /dev/null
+++ b/fuzzers/corpora/packfile/1966ab31dc80ab75196b0cbf28e3960a0eb3f6c5
Binary files differ
diff --git a/fuzzers/corpora/packfile/19da91f2603889267dfd77786e07a5b8f067d62a b/fuzzers/corpora/packfile/19da91f2603889267dfd77786e07a5b8f067d62a
new file mode 100644
index 0000000..8b43ca9
--- /dev/null
+++ b/fuzzers/corpora/packfile/19da91f2603889267dfd77786e07a5b8f067d62a
@@ -0,0 +1 @@
+© \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/1a72795a3dffdfc999b030d9aab7237dea1e2bc1 b/fuzzers/corpora/packfile/1a72795a3dffdfc999b030d9aab7237dea1e2bc1
new file mode 100644
index 0000000..5a8a38b
--- /dev/null
+++ b/fuzzers/corpora/packfile/1a72795a3dffdfc999b030d9aab7237dea1e2bc1
Binary files differ
diff --git a/fuzzers/corpora/packfile/1e29cf67a66f225b338610fbcdf1b8185a8f5b7d b/fuzzers/corpora/packfile/1e29cf67a66f225b338610fbcdf1b8185a8f5b7d
new file mode 100644
index 0000000..52219de
--- /dev/null
+++ b/fuzzers/corpora/packfile/1e29cf67a66f225b338610fbcdf1b8185a8f5b7d
Binary files differ
diff --git a/fuzzers/corpora/packfile/1eb8977ef8c3be9ee896d785663c762c7e32be28 b/fuzzers/corpora/packfile/1eb8977ef8c3be9ee896d785663c762c7e32be28
new file mode 100644
index 0000000..d924eb7
--- /dev/null
+++ b/fuzzers/corpora/packfile/1eb8977ef8c3be9ee896d785663c762c7e32be28
Binary files differ
diff --git a/fuzzers/corpora/packfile/1f0837530c1c3d122157f2eaa9c2178dcc3580df b/fuzzers/corpora/packfile/1f0837530c1c3d122157f2eaa9c2178dcc3580df
new file mode 100644
index 0000000..26b0c60
--- /dev/null
+++ b/fuzzers/corpora/packfile/1f0837530c1c3d122157f2eaa9c2178dcc3580df
Binary files differ
diff --git a/fuzzers/corpora/packfile/1f3c5fd6dc091faa397bce776aa97b457388fdae b/fuzzers/corpora/packfile/1f3c5fd6dc091faa397bce776aa97b457388fdae
new file mode 100644
index 0000000..5b5232d
--- /dev/null
+++ b/fuzzers/corpora/packfile/1f3c5fd6dc091faa397bce776aa97b457388fdae
Binary files differ
diff --git a/fuzzers/corpora/packfile/20528983163f834108150a7191600ff94ae2c1d2 b/fuzzers/corpora/packfile/20528983163f834108150a7191600ff94ae2c1d2
new file mode 100644
index 0000000..05189f0
--- /dev/null
+++ b/fuzzers/corpora/packfile/20528983163f834108150a7191600ff94ae2c1d2
Binary files differ
diff --git a/fuzzers/corpora/packfile/20a725140a8ffbe11bb71c1b83f19452147e5180 b/fuzzers/corpora/packfile/20a725140a8ffbe11bb71c1b83f19452147e5180
new file mode 100644
index 0000000..f0337fa
--- /dev/null
+++ b/fuzzers/corpora/packfile/20a725140a8ffbe11bb71c1b83f19452147e5180
Binary files differ
diff --git a/fuzzers/corpora/packfile/2149aa9e07dda9bbf502e088d8d0a38e8fb94f2e b/fuzzers/corpora/packfile/2149aa9e07dda9bbf502e088d8d0a38e8fb94f2e
new file mode 100644
index 0000000..e7754ca
--- /dev/null
+++ b/fuzzers/corpora/packfile/2149aa9e07dda9bbf502e088d8d0a38e8fb94f2e
@@ -0,0 +1 @@
+À \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/21b664aa8c86aaee4007d9bdbc2d63bf82bd5109 b/fuzzers/corpora/packfile/21b664aa8c86aaee4007d9bdbc2d63bf82bd5109
new file mode 100644
index 0000000..44c6604
--- /dev/null
+++ b/fuzzers/corpora/packfile/21b664aa8c86aaee4007d9bdbc2d63bf82bd5109
Binary files differ
diff --git a/fuzzers/corpora/packfile/21b9ec8a7d7ac4d542c9bf7b2e26581cfcfaaab6 b/fuzzers/corpora/packfile/21b9ec8a7d7ac4d542c9bf7b2e26581cfcfaaab6
new file mode 100644
index 0000000..c2f5439
--- /dev/null
+++ b/fuzzers/corpora/packfile/21b9ec8a7d7ac4d542c9bf7b2e26581cfcfaaab6
Binary files differ
diff --git a/fuzzers/corpora/packfile/21c07e2affed6b0134d5dc28ea6c4937e691c761 b/fuzzers/corpora/packfile/21c07e2affed6b0134d5dc28ea6c4937e691c761
new file mode 100644
index 0000000..ee9fc89
--- /dev/null
+++ b/fuzzers/corpora/packfile/21c07e2affed6b0134d5dc28ea6c4937e691c761
Binary files differ
diff --git a/fuzzers/corpora/packfile/23841d4076641ebcb4f58d1fd03047528c9d359b b/fuzzers/corpora/packfile/23841d4076641ebcb4f58d1fd03047528c9d359b
new file mode 100644
index 0000000..76b0b49
--- /dev/null
+++ b/fuzzers/corpora/packfile/23841d4076641ebcb4f58d1fd03047528c9d359b
Binary files differ
diff --git a/fuzzers/corpora/packfile/23b9174c42560de6525b1f103125f599479f95cb b/fuzzers/corpora/packfile/23b9174c42560de6525b1f103125f599479f95cb
new file mode 100644
index 0000000..4bef2f6
--- /dev/null
+++ b/fuzzers/corpora/packfile/23b9174c42560de6525b1f103125f599479f95cb
Binary files differ
diff --git a/fuzzers/corpora/packfile/241cbd6dfb6e53c43c73b62f9384359091dcbf56 b/fuzzers/corpora/packfile/241cbd6dfb6e53c43c73b62f9384359091dcbf56
new file mode 100644
index 0000000..bd0fd35
--- /dev/null
+++ b/fuzzers/corpora/packfile/241cbd6dfb6e53c43c73b62f9384359091dcbf56
@@ -0,0 +1 @@
+­ \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/245a2ddea41e6531944933c4420b8c9790ac351b b/fuzzers/corpora/packfile/245a2ddea41e6531944933c4420b8c9790ac351b
new file mode 100644
index 0000000..99cbc9b
--- /dev/null
+++ b/fuzzers/corpora/packfile/245a2ddea41e6531944933c4420b8c9790ac351b
Binary files differ
diff --git a/fuzzers/corpora/packfile/2541e340271ea496a392870bcc20d3510287b9e9 b/fuzzers/corpora/packfile/2541e340271ea496a392870bcc20d3510287b9e9
new file mode 100644
index 0000000..ff63b2a
--- /dev/null
+++ b/fuzzers/corpora/packfile/2541e340271ea496a392870bcc20d3510287b9e9
Binary files differ
diff --git a/fuzzers/corpora/packfile/276af22d5bf94344737fb1a5eb5de7d335004493 b/fuzzers/corpora/packfile/276af22d5bf94344737fb1a5eb5de7d335004493
new file mode 100644
index 0000000..fd2b077
--- /dev/null
+++ b/fuzzers/corpora/packfile/276af22d5bf94344737fb1a5eb5de7d335004493
Binary files differ
diff --git a/fuzzers/corpora/packfile/27d5482eebd075de44389774fce28c69f45c8a75 b/fuzzers/corpora/packfile/27d5482eebd075de44389774fce28c69f45c8a75
new file mode 100644
index 0000000..be54354
--- /dev/null
+++ b/fuzzers/corpora/packfile/27d5482eebd075de44389774fce28c69f45c8a75
@@ -0,0 +1 @@
+h \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/28334bd612cb539de776370995f60c8032215434 b/fuzzers/corpora/packfile/28334bd612cb539de776370995f60c8032215434
new file mode 100644
index 0000000..6d67b28
--- /dev/null
+++ b/fuzzers/corpora/packfile/28334bd612cb539de776370995f60c8032215434
Binary files differ
diff --git a/fuzzers/corpora/packfile/2973e2ac092cba077d7350bfffe1cf2e0644a6e1 b/fuzzers/corpora/packfile/2973e2ac092cba077d7350bfffe1cf2e0644a6e1
new file mode 100644
index 0000000..1066746
--- /dev/null
+++ b/fuzzers/corpora/packfile/2973e2ac092cba077d7350bfffe1cf2e0644a6e1
Binary files differ
diff --git a/fuzzers/corpora/packfile/2adcd01e876b12d867c858ffaec38c42c59c36c7 b/fuzzers/corpora/packfile/2adcd01e876b12d867c858ffaec38c42c59c36c7
new file mode 100644
index 0000000..4323d10
--- /dev/null
+++ b/fuzzers/corpora/packfile/2adcd01e876b12d867c858ffaec38c42c59c36c7
Binary files differ
diff --git a/fuzzers/corpora/packfile/2b28470644f5d0323643da99c831d82f20a7a74f b/fuzzers/corpora/packfile/2b28470644f5d0323643da99c831d82f20a7a74f
new file mode 100644
index 0000000..a65ff49
--- /dev/null
+++ b/fuzzers/corpora/packfile/2b28470644f5d0323643da99c831d82f20a7a74f
Binary files differ
diff --git a/fuzzers/corpora/packfile/2b86229020ba808df84e16f800dc152254f18f64 b/fuzzers/corpora/packfile/2b86229020ba808df84e16f800dc152254f18f64
new file mode 100644
index 0000000..bd650d2
--- /dev/null
+++ b/fuzzers/corpora/packfile/2b86229020ba808df84e16f800dc152254f18f64
Binary files differ
diff --git a/fuzzers/corpora/packfile/2cc5bf2f780cd85ad93d232890f418625f4d1274 b/fuzzers/corpora/packfile/2cc5bf2f780cd85ad93d232890f418625f4d1274
new file mode 100644
index 0000000..f45a38c
--- /dev/null
+++ b/fuzzers/corpora/packfile/2cc5bf2f780cd85ad93d232890f418625f4d1274
@@ -0,0 +1 @@
+Pw \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/2d6ae8fa82b656879dd3371d0a6899e88ef34e76 b/fuzzers/corpora/packfile/2d6ae8fa82b656879dd3371d0a6899e88ef34e76
new file mode 100644
index 0000000..cd62a7b
--- /dev/null
+++ b/fuzzers/corpora/packfile/2d6ae8fa82b656879dd3371d0a6899e88ef34e76
Binary files differ
diff --git a/fuzzers/corpora/packfile/2e74d24e887678f0681d4c7c010477b8b9697f1a b/fuzzers/corpora/packfile/2e74d24e887678f0681d4c7c010477b8b9697f1a
new file mode 100644
index 0000000..ae9780b
--- /dev/null
+++ b/fuzzers/corpora/packfile/2e74d24e887678f0681d4c7c010477b8b9697f1a
@@ -0,0 +1 @@
+ˆ \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/2f436c68a7b0be43aa6d4ad5126ec9401a9f9211 b/fuzzers/corpora/packfile/2f436c68a7b0be43aa6d4ad5126ec9401a9f9211
new file mode 100644
index 0000000..4841ef5
--- /dev/null
+++ b/fuzzers/corpora/packfile/2f436c68a7b0be43aa6d4ad5126ec9401a9f9211
Binary files differ
diff --git a/fuzzers/corpora/packfile/2fec48b0dcb45b98597bfec12bf0dc650543b3e3 b/fuzzers/corpora/packfile/2fec48b0dcb45b98597bfec12bf0dc650543b3e3
new file mode 100644
index 0000000..0ded0e3
--- /dev/null
+++ b/fuzzers/corpora/packfile/2fec48b0dcb45b98597bfec12bf0dc650543b3e3
Binary files differ
diff --git a/fuzzers/corpora/packfile/31bd25636a9807d6024e78b9b3d02fbb1a02835e b/fuzzers/corpora/packfile/31bd25636a9807d6024e78b9b3d02fbb1a02835e
new file mode 100644
index 0000000..15d1751
--- /dev/null
+++ b/fuzzers/corpora/packfile/31bd25636a9807d6024e78b9b3d02fbb1a02835e
@@ -0,0 +1 @@
+ÿÿÿ@ \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/323c88be36ecc20ff30b21cf417106554042db04 b/fuzzers/corpora/packfile/323c88be36ecc20ff30b21cf417106554042db04
new file mode 100644
index 0000000..0e5c2cb
--- /dev/null
+++ b/fuzzers/corpora/packfile/323c88be36ecc20ff30b21cf417106554042db04
Binary files differ
diff --git a/fuzzers/corpora/packfile/33b3aa957ca4fb31873033a7f460617f1fe81e32 b/fuzzers/corpora/packfile/33b3aa957ca4fb31873033a7f460617f1fe81e32
new file mode 100644
index 0000000..cea3b12
--- /dev/null
+++ b/fuzzers/corpora/packfile/33b3aa957ca4fb31873033a7f460617f1fe81e32
Binary files differ
diff --git a/fuzzers/corpora/packfile/34303d14e37c9ddfb0bad130e006fec927e13785 b/fuzzers/corpora/packfile/34303d14e37c9ddfb0bad130e006fec927e13785
new file mode 100644
index 0000000..1adb6b1
--- /dev/null
+++ b/fuzzers/corpora/packfile/34303d14e37c9ddfb0bad130e006fec927e13785
Binary files differ
diff --git a/fuzzers/corpora/packfile/34dac9466a4a2c15aaeef13a9567f6827ace7748 b/fuzzers/corpora/packfile/34dac9466a4a2c15aaeef13a9567f6827ace7748
new file mode 100644
index 0000000..2b0708e
--- /dev/null
+++ b/fuzzers/corpora/packfile/34dac9466a4a2c15aaeef13a9567f6827ace7748
Binary files differ
diff --git a/fuzzers/corpora/packfile/356a192b7913b04c54574d18c28d46e6395428ab b/fuzzers/corpora/packfile/356a192b7913b04c54574d18c28d46e6395428ab
new file mode 100644
index 0000000..56a6051
--- /dev/null
+++ b/fuzzers/corpora/packfile/356a192b7913b04c54574d18c28d46e6395428ab
@@ -0,0 +1 @@
+1 \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/35bf585248be2c6d3940e15b85f72c4146903097 b/fuzzers/corpora/packfile/35bf585248be2c6d3940e15b85f72c4146903097
new file mode 100644
index 0000000..f17d4fa
--- /dev/null
+++ b/fuzzers/corpora/packfile/35bf585248be2c6d3940e15b85f72c4146903097
Binary files differ
diff --git a/fuzzers/corpora/packfile/3725a1c4431714019827277deac8ec2efeed8f1d b/fuzzers/corpora/packfile/3725a1c4431714019827277deac8ec2efeed8f1d
new file mode 100644
index 0000000..f58c271
--- /dev/null
+++ b/fuzzers/corpora/packfile/3725a1c4431714019827277deac8ec2efeed8f1d
Binary files differ
diff --git a/fuzzers/corpora/packfile/37a2b7de1fadc9eab2d5fabf5dfe7007c245dbee b/fuzzers/corpora/packfile/37a2b7de1fadc9eab2d5fabf5dfe7007c245dbee
new file mode 100644
index 0000000..0c71198
--- /dev/null
+++ b/fuzzers/corpora/packfile/37a2b7de1fadc9eab2d5fabf5dfe7007c245dbee
Binary files differ
diff --git a/fuzzers/corpora/packfile/37ab8a0ca81fc62279057401761f7730a5a8f1b2 b/fuzzers/corpora/packfile/37ab8a0ca81fc62279057401761f7730a5a8f1b2
new file mode 100644
index 0000000..d9b74cf
--- /dev/null
+++ b/fuzzers/corpora/packfile/37ab8a0ca81fc62279057401761f7730a5a8f1b2
Binary files differ
diff --git a/fuzzers/corpora/packfile/38011be20a664dcd2594e712a26c063c2d50efcd b/fuzzers/corpora/packfile/38011be20a664dcd2594e712a26c063c2d50efcd
new file mode 100644
index 0000000..bd80616
--- /dev/null
+++ b/fuzzers/corpora/packfile/38011be20a664dcd2594e712a26c063c2d50efcd
Binary files differ
diff --git a/fuzzers/corpora/packfile/3838851a5da8239c2ae6cbbe869c81446c720e87 b/fuzzers/corpora/packfile/3838851a5da8239c2ae6cbbe869c81446c720e87
new file mode 100644
index 0000000..d1282b9
--- /dev/null
+++ b/fuzzers/corpora/packfile/3838851a5da8239c2ae6cbbe869c81446c720e87
Binary files differ
diff --git a/fuzzers/corpora/packfile/3921322ac01429b001f88d64c8319088fe49218e b/fuzzers/corpora/packfile/3921322ac01429b001f88d64c8319088fe49218e
new file mode 100644
index 0000000..6ed50eb
--- /dev/null
+++ b/fuzzers/corpora/packfile/3921322ac01429b001f88d64c8319088fe49218e
Binary files differ
diff --git a/fuzzers/corpora/packfile/395df8f7c51f007019cb30201c49e884b46b92fa b/fuzzers/corpora/packfile/395df8f7c51f007019cb30201c49e884b46b92fa
new file mode 100644
index 0000000..fa7af8b
--- /dev/null
+++ b/fuzzers/corpora/packfile/395df8f7c51f007019cb30201c49e884b46b92fa
@@ -0,0 +1 @@
+z \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/3e98eb4fd65d3f2c41fa979db0f5678b310e51fe b/fuzzers/corpora/packfile/3e98eb4fd65d3f2c41fa979db0f5678b310e51fe
new file mode 100644
index 0000000..8f1d1f5
--- /dev/null
+++ b/fuzzers/corpora/packfile/3e98eb4fd65d3f2c41fa979db0f5678b310e51fe
Binary files differ
diff --git a/fuzzers/corpora/packfile/3f9ec359d0cb573cb6d2b2df64c9f4048ea298b8 b/fuzzers/corpora/packfile/3f9ec359d0cb573cb6d2b2df64c9f4048ea298b8
new file mode 100644
index 0000000..3c1f737
--- /dev/null
+++ b/fuzzers/corpora/packfile/3f9ec359d0cb573cb6d2b2df64c9f4048ea298b8
Binary files differ
diff --git a/fuzzers/corpora/packfile/4067250457728bf775aa310ef253b223ae2fe4dc b/fuzzers/corpora/packfile/4067250457728bf775aa310ef253b223ae2fe4dc
new file mode 100644
index 0000000..1e9a88b
--- /dev/null
+++ b/fuzzers/corpora/packfile/4067250457728bf775aa310ef253b223ae2fe4dc
Binary files differ
diff --git a/fuzzers/corpora/packfile/40818db87e110b29cb864f73265586cc054f5bbb b/fuzzers/corpora/packfile/40818db87e110b29cb864f73265586cc054f5bbb
new file mode 100644
index 0000000..e5e52b2
--- /dev/null
+++ b/fuzzers/corpora/packfile/40818db87e110b29cb864f73265586cc054f5bbb
Binary files differ
diff --git a/fuzzers/corpora/packfile/418f9fb9ce1d4efdf481ca8fff9dadd09caee9fc b/fuzzers/corpora/packfile/418f9fb9ce1d4efdf481ca8fff9dadd09caee9fc
new file mode 100644
index 0000000..93eb935
--- /dev/null
+++ b/fuzzers/corpora/packfile/418f9fb9ce1d4efdf481ca8fff9dadd09caee9fc
Binary files differ
diff --git a/fuzzers/corpora/packfile/41ca0ae865b686089b8d99e9d661da291ce51019 b/fuzzers/corpora/packfile/41ca0ae865b686089b8d99e9d661da291ce51019
new file mode 100644
index 0000000..e6f394f
--- /dev/null
+++ b/fuzzers/corpora/packfile/41ca0ae865b686089b8d99e9d661da291ce51019
Binary files differ
diff --git a/fuzzers/corpora/packfile/42099b4af021e53fd8fd4e056c2568d7c2e3ffa8 b/fuzzers/corpora/packfile/42099b4af021e53fd8fd4e056c2568d7c2e3ffa8
new file mode 100644
index 0000000..35ec3b9
--- /dev/null
+++ b/fuzzers/corpora/packfile/42099b4af021e53fd8fd4e056c2568d7c2e3ffa8
@@ -0,0 +1 @@
+/ \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/420ce645ce1c93cee59a06da2159cbbb251e4c01 b/fuzzers/corpora/packfile/420ce645ce1c93cee59a06da2159cbbb251e4c01
new file mode 100644
index 0000000..5e6efc2
--- /dev/null
+++ b/fuzzers/corpora/packfile/420ce645ce1c93cee59a06da2159cbbb251e4c01
Binary files differ
diff --git a/fuzzers/corpora/packfile/4345cb1fa27885a8fbfe7c0c830a592cc76a552b b/fuzzers/corpora/packfile/4345cb1fa27885a8fbfe7c0c830a592cc76a552b
new file mode 100644
index 0000000..02691e3
--- /dev/null
+++ b/fuzzers/corpora/packfile/4345cb1fa27885a8fbfe7c0c830a592cc76a552b
@@ -0,0 +1 @@
+% \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/450718a71a93a1b5ff982595432400b0fa876fb6 b/fuzzers/corpora/packfile/450718a71a93a1b5ff982595432400b0fa876fb6
new file mode 100644
index 0000000..977e595
--- /dev/null
+++ b/fuzzers/corpora/packfile/450718a71a93a1b5ff982595432400b0fa876fb6
Binary files differ
diff --git a/fuzzers/corpora/packfile/453a312eb77b9d4198ac62faef10ecf3e283120c b/fuzzers/corpora/packfile/453a312eb77b9d4198ac62faef10ecf3e283120c
new file mode 100644
index 0000000..b25fe53
--- /dev/null
+++ b/fuzzers/corpora/packfile/453a312eb77b9d4198ac62faef10ecf3e283120c
Binary files differ
diff --git a/fuzzers/corpora/packfile/45470317334b614ce4d119c05ed2d6250dbc6061 b/fuzzers/corpora/packfile/45470317334b614ce4d119c05ed2d6250dbc6061
new file mode 100644
index 0000000..4b9a0a9
--- /dev/null
+++ b/fuzzers/corpora/packfile/45470317334b614ce4d119c05ed2d6250dbc6061
Binary files differ
diff --git a/fuzzers/corpora/packfile/45a65193e30784b0124f4fed659eb7e46552c2d0 b/fuzzers/corpora/packfile/45a65193e30784b0124f4fed659eb7e46552c2d0
new file mode 100644
index 0000000..5bd7dea
--- /dev/null
+++ b/fuzzers/corpora/packfile/45a65193e30784b0124f4fed659eb7e46552c2d0
@@ -0,0 +1 @@
+¶ \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/45cff3494791ded181e1e3dab1c7a0e40130b57b b/fuzzers/corpora/packfile/45cff3494791ded181e1e3dab1c7a0e40130b57b
new file mode 100644
index 0000000..a92e1a5
--- /dev/null
+++ b/fuzzers/corpora/packfile/45cff3494791ded181e1e3dab1c7a0e40130b57b
Binary files differ
diff --git a/fuzzers/corpora/packfile/481dedc2855981510024d48828af0fe35a1503dd b/fuzzers/corpora/packfile/481dedc2855981510024d48828af0fe35a1503dd
new file mode 100644
index 0000000..3d95cc3
--- /dev/null
+++ b/fuzzers/corpora/packfile/481dedc2855981510024d48828af0fe35a1503dd
Binary files differ
diff --git a/fuzzers/corpora/packfile/49a6448a722742b1b392f0471542ee0c572c5a9a b/fuzzers/corpora/packfile/49a6448a722742b1b392f0471542ee0c572c5a9a
new file mode 100644
index 0000000..c767ee5
--- /dev/null
+++ b/fuzzers/corpora/packfile/49a6448a722742b1b392f0471542ee0c572c5a9a
Binary files differ
diff --git a/fuzzers/corpora/packfile/4a6e6af93dea13a5720be52b95f2948e0cab4602 b/fuzzers/corpora/packfile/4a6e6af93dea13a5720be52b95f2948e0cab4602
new file mode 100644
index 0000000..013d77b
--- /dev/null
+++ b/fuzzers/corpora/packfile/4a6e6af93dea13a5720be52b95f2948e0cab4602
Binary files differ
diff --git a/fuzzers/corpora/packfile/4ac25548f35e06eb9f91b0f15b89db3cb5bcb283 b/fuzzers/corpora/packfile/4ac25548f35e06eb9f91b0f15b89db3cb5bcb283
new file mode 100644
index 0000000..2ee6580
--- /dev/null
+++ b/fuzzers/corpora/packfile/4ac25548f35e06eb9f91b0f15b89db3cb5bcb283
Binary files differ
diff --git a/fuzzers/corpora/packfile/4b586169f192749a0aa023ad6e4edd2e15d67f53 b/fuzzers/corpora/packfile/4b586169f192749a0aa023ad6e4edd2e15d67f53
new file mode 100644
index 0000000..d517c58
--- /dev/null
+++ b/fuzzers/corpora/packfile/4b586169f192749a0aa023ad6e4edd2e15d67f53
Binary files differ
diff --git a/fuzzers/corpora/packfile/4c3c8ec0e25da9342dc87c2e78d3040c381514ce b/fuzzers/corpora/packfile/4c3c8ec0e25da9342dc87c2e78d3040c381514ce
new file mode 100644
index 0000000..c6a1059
--- /dev/null
+++ b/fuzzers/corpora/packfile/4c3c8ec0e25da9342dc87c2e78d3040c381514ce
Binary files differ
diff --git a/fuzzers/corpora/packfile/4d5189cd1411daaa579df34591c6a5946204c9a0 b/fuzzers/corpora/packfile/4d5189cd1411daaa579df34591c6a5946204c9a0
new file mode 100644
index 0000000..ac19046
--- /dev/null
+++ b/fuzzers/corpora/packfile/4d5189cd1411daaa579df34591c6a5946204c9a0
Binary files differ
diff --git a/fuzzers/corpora/packfile/4d7f1bfa928c0d3399598d562e346c6e23de6a03 b/fuzzers/corpora/packfile/4d7f1bfa928c0d3399598d562e346c6e23de6a03
new file mode 100644
index 0000000..712cf00
--- /dev/null
+++ b/fuzzers/corpora/packfile/4d7f1bfa928c0d3399598d562e346c6e23de6a03
Binary files differ
diff --git a/fuzzers/corpora/packfile/4eee38183d6fce3f42224738be58d0e3975300f4 b/fuzzers/corpora/packfile/4eee38183d6fce3f42224738be58d0e3975300f4
new file mode 100644
index 0000000..4434b93
--- /dev/null
+++ b/fuzzers/corpora/packfile/4eee38183d6fce3f42224738be58d0e3975300f4
Binary files differ
diff --git a/fuzzers/corpora/packfile/4f2e2af611d6567abcf5b6bfc579487ac417a8d4 b/fuzzers/corpora/packfile/4f2e2af611d6567abcf5b6bfc579487ac417a8d4
new file mode 100644
index 0000000..d16ad16
--- /dev/null
+++ b/fuzzers/corpora/packfile/4f2e2af611d6567abcf5b6bfc579487ac417a8d4
Binary files differ
diff --git a/fuzzers/corpora/packfile/4fa04b2c3ac839c36c4a3b51bf882eb99b7cd097 b/fuzzers/corpora/packfile/4fa04b2c3ac839c36c4a3b51bf882eb99b7cd097
new file mode 100644
index 0000000..0102fe3
--- /dev/null
+++ b/fuzzers/corpora/packfile/4fa04b2c3ac839c36c4a3b51bf882eb99b7cd097
Binary files differ
diff --git a/fuzzers/corpora/packfile/4fbe10aede9fd9ce2030c6e567a9281e1a5338f4 b/fuzzers/corpora/packfile/4fbe10aede9fd9ce2030c6e567a9281e1a5338f4
new file mode 100644
index 0000000..83ca019
--- /dev/null
+++ b/fuzzers/corpora/packfile/4fbe10aede9fd9ce2030c6e567a9281e1a5338f4
Binary files differ
diff --git a/fuzzers/corpora/packfile/5037f4f74273aed9a09122af5f4acc10f42c033a b/fuzzers/corpora/packfile/5037f4f74273aed9a09122af5f4acc10f42c033a
new file mode 100644
index 0000000..cc94079
--- /dev/null
+++ b/fuzzers/corpora/packfile/5037f4f74273aed9a09122af5f4acc10f42c033a
Binary files differ
diff --git a/fuzzers/corpora/packfile/511993d3c99719e38a6779073019dacd7178ddb9 b/fuzzers/corpora/packfile/511993d3c99719e38a6779073019dacd7178ddb9
new file mode 100644
index 0000000..675f43a
--- /dev/null
+++ b/fuzzers/corpora/packfile/511993d3c99719e38a6779073019dacd7178ddb9
@@ -0,0 +1 @@
+P \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/520aa436eab6343c3729f51f0f8048e6b87f6aeb b/fuzzers/corpora/packfile/520aa436eab6343c3729f51f0f8048e6b87f6aeb
new file mode 100644
index 0000000..b5ec5af
--- /dev/null
+++ b/fuzzers/corpora/packfile/520aa436eab6343c3729f51f0f8048e6b87f6aeb
@@ -0,0 +1 @@
+ÏÏ \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/521e228f3b62dca81d87d2e7d5476657d7b5e0a9 b/fuzzers/corpora/packfile/521e228f3b62dca81d87d2e7d5476657d7b5e0a9
new file mode 100644
index 0000000..c0ea9e9
--- /dev/null
+++ b/fuzzers/corpora/packfile/521e228f3b62dca81d87d2e7d5476657d7b5e0a9
Binary files differ
diff --git a/fuzzers/corpora/packfile/52e37dfd77d56769dc8a96388aa26695a8108dac b/fuzzers/corpora/packfile/52e37dfd77d56769dc8a96388aa26695a8108dac
new file mode 100644
index 0000000..57d12a9
--- /dev/null
+++ b/fuzzers/corpora/packfile/52e37dfd77d56769dc8a96388aa26695a8108dac
Binary files differ
diff --git a/fuzzers/corpora/packfile/5374fb6be0a406cf8d0e95771ecb032254d21305 b/fuzzers/corpora/packfile/5374fb6be0a406cf8d0e95771ecb032254d21305
new file mode 100644
index 0000000..42b16b0
--- /dev/null
+++ b/fuzzers/corpora/packfile/5374fb6be0a406cf8d0e95771ecb032254d21305
Binary files differ
diff --git a/fuzzers/corpora/packfile/53e1d4898c15c8ee3ef5e2fb279d151108725731 b/fuzzers/corpora/packfile/53e1d4898c15c8ee3ef5e2fb279d151108725731
new file mode 100644
index 0000000..516958e
--- /dev/null
+++ b/fuzzers/corpora/packfile/53e1d4898c15c8ee3ef5e2fb279d151108725731
Binary files differ
diff --git a/fuzzers/corpora/packfile/53e61ad37ca92b7f6c0396e5fab1ed8743e73da0 b/fuzzers/corpora/packfile/53e61ad37ca92b7f6c0396e5fab1ed8743e73da0
new file mode 100644
index 0000000..ded2968
--- /dev/null
+++ b/fuzzers/corpora/packfile/53e61ad37ca92b7f6c0396e5fab1ed8743e73da0
Binary files differ
diff --git a/fuzzers/corpora/packfile/55df2a59ed6a888ee2f0cdfdcc8582696702de7a b/fuzzers/corpora/packfile/55df2a59ed6a888ee2f0cdfdcc8582696702de7a
new file mode 100644
index 0000000..a4a063a
--- /dev/null
+++ b/fuzzers/corpora/packfile/55df2a59ed6a888ee2f0cdfdcc8582696702de7a
@@ -0,0 +1 @@
+ï \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/56a2020f68c5eb72786ea168cc2a7e8ea34ad9c2 b/fuzzers/corpora/packfile/56a2020f68c5eb72786ea168cc2a7e8ea34ad9c2
new file mode 100644
index 0000000..be0ff0d
--- /dev/null
+++ b/fuzzers/corpora/packfile/56a2020f68c5eb72786ea168cc2a7e8ea34ad9c2
Binary files differ
diff --git a/fuzzers/corpora/packfile/578678e78b72f8bcb9f414e4129ae5d85a4af914 b/fuzzers/corpora/packfile/578678e78b72f8bcb9f414e4129ae5d85a4af914
new file mode 100644
index 0000000..7482954
--- /dev/null
+++ b/fuzzers/corpora/packfile/578678e78b72f8bcb9f414e4129ae5d85a4af914
Binary files differ
diff --git a/fuzzers/corpora/packfile/5a8bc5597fd0b2b44194ffabce46e2fa94c1ffd7 b/fuzzers/corpora/packfile/5a8bc5597fd0b2b44194ffabce46e2fa94c1ffd7
new file mode 100644
index 0000000..4c88499
--- /dev/null
+++ b/fuzzers/corpora/packfile/5a8bc5597fd0b2b44194ffabce46e2fa94c1ffd7
Binary files differ
diff --git a/fuzzers/corpora/packfile/5bab61eb53176449e25c2c82f172b82cb13ffb9d b/fuzzers/corpora/packfile/5bab61eb53176449e25c2c82f172b82cb13ffb9d
new file mode 100644
index 0000000..0d758c9
--- /dev/null
+++ b/fuzzers/corpora/packfile/5bab61eb53176449e25c2c82f172b82cb13ffb9d
@@ -0,0 +1 @@
+? \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/5c2dd944dde9e08881bef0894fe7b22a5c9c4b06 b/fuzzers/corpora/packfile/5c2dd944dde9e08881bef0894fe7b22a5c9c4b06
new file mode 100644
index 0000000..0fe2fa5
--- /dev/null
+++ b/fuzzers/corpora/packfile/5c2dd944dde9e08881bef0894fe7b22a5c9c4b06
@@ -0,0 +1 @@
+j \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/5cb4674f4468d39f061d1df3c95b9c2dca529c54 b/fuzzers/corpora/packfile/5cb4674f4468d39f061d1df3c95b9c2dca529c54
new file mode 100644
index 0000000..9b54d9f
--- /dev/null
+++ b/fuzzers/corpora/packfile/5cb4674f4468d39f061d1df3c95b9c2dca529c54
Binary files differ
diff --git a/fuzzers/corpora/packfile/60b6fbfe65dc1796a09a734e054223aba8c90260 b/fuzzers/corpora/packfile/60b6fbfe65dc1796a09a734e054223aba8c90260
new file mode 100644
index 0000000..a7df9a6
--- /dev/null
+++ b/fuzzers/corpora/packfile/60b6fbfe65dc1796a09a734e054223aba8c90260
Binary files differ
diff --git a/fuzzers/corpora/packfile/611f5b9368427ef823f7ed89ad23667b02a06435 b/fuzzers/corpora/packfile/611f5b9368427ef823f7ed89ad23667b02a06435
new file mode 100644
index 0000000..56ee380
--- /dev/null
+++ b/fuzzers/corpora/packfile/611f5b9368427ef823f7ed89ad23667b02a06435
Binary files differ
diff --git a/fuzzers/corpora/packfile/6214b4afdbfe63400ce428d47a58a2e29f682738 b/fuzzers/corpora/packfile/6214b4afdbfe63400ce428d47a58a2e29f682738
new file mode 100644
index 0000000..09e3c40
--- /dev/null
+++ b/fuzzers/corpora/packfile/6214b4afdbfe63400ce428d47a58a2e29f682738
Binary files differ
diff --git a/fuzzers/corpora/packfile/634b675b80d51b52c3c6fbc73181ed47f61749ba b/fuzzers/corpora/packfile/634b675b80d51b52c3c6fbc73181ed47f61749ba
new file mode 100644
index 0000000..9afac4f
--- /dev/null
+++ b/fuzzers/corpora/packfile/634b675b80d51b52c3c6fbc73181ed47f61749ba
Binary files differ
diff --git a/fuzzers/corpora/packfile/6431f1b31dc492fad89732b7d3e511fa7361985d b/fuzzers/corpora/packfile/6431f1b31dc492fad89732b7d3e511fa7361985d
new file mode 100644
index 0000000..b68409e
--- /dev/null
+++ b/fuzzers/corpora/packfile/6431f1b31dc492fad89732b7d3e511fa7361985d
Binary files differ
diff --git a/fuzzers/corpora/packfile/6442fd4bbb7656f142c92050da17b0e81e79fad1 b/fuzzers/corpora/packfile/6442fd4bbb7656f142c92050da17b0e81e79fad1
new file mode 100644
index 0000000..2ca2d96
--- /dev/null
+++ b/fuzzers/corpora/packfile/6442fd4bbb7656f142c92050da17b0e81e79fad1
Binary files differ
diff --git a/fuzzers/corpora/packfile/6486c8cf6bcc2fca60502564924f6b266399df3d b/fuzzers/corpora/packfile/6486c8cf6bcc2fca60502564924f6b266399df3d
new file mode 100644
index 0000000..a80e209
--- /dev/null
+++ b/fuzzers/corpora/packfile/6486c8cf6bcc2fca60502564924f6b266399df3d
Binary files differ
diff --git a/fuzzers/corpora/packfile/651c573b6fdd393e97536a47f8b9e65226e29c33 b/fuzzers/corpora/packfile/651c573b6fdd393e97536a47f8b9e65226e29c33
new file mode 100644
index 0000000..ec11120
--- /dev/null
+++ b/fuzzers/corpora/packfile/651c573b6fdd393e97536a47f8b9e65226e29c33
Binary files differ
diff --git a/fuzzers/corpora/packfile/657fc646e93cb999417f43f0ec77fbad694e3e18 b/fuzzers/corpora/packfile/657fc646e93cb999417f43f0ec77fbad694e3e18
new file mode 100644
index 0000000..acb56a8
--- /dev/null
+++ b/fuzzers/corpora/packfile/657fc646e93cb999417f43f0ec77fbad694e3e18
Binary files differ
diff --git a/fuzzers/corpora/packfile/65cc90263dec0020ceabc727d33aa587e57fc175 b/fuzzers/corpora/packfile/65cc90263dec0020ceabc727d33aa587e57fc175
new file mode 100644
index 0000000..d14a6c3
--- /dev/null
+++ b/fuzzers/corpora/packfile/65cc90263dec0020ceabc727d33aa587e57fc175
@@ -0,0 +1 @@
+ÿ÷ \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/688934845f22049cb14668832efa33d45013b6b9 b/fuzzers/corpora/packfile/688934845f22049cb14668832efa33d45013b6b9
new file mode 100644
index 0000000..15294a5
--- /dev/null
+++ b/fuzzers/corpora/packfile/688934845f22049cb14668832efa33d45013b6b9
Binary files differ
diff --git a/fuzzers/corpora/packfile/6b0d31c0d563223024da45691584643ac78c96e8 b/fuzzers/corpora/packfile/6b0d31c0d563223024da45691584643ac78c96e8
new file mode 100644
index 0000000..08b9811
--- /dev/null
+++ b/fuzzers/corpora/packfile/6b0d31c0d563223024da45691584643ac78c96e8
@@ -0,0 +1 @@
+m \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/6b4dc6028a3a684a20dbc427b15a37ea2fd12dd1 b/fuzzers/corpora/packfile/6b4dc6028a3a684a20dbc427b15a37ea2fd12dd1
new file mode 100644
index 0000000..d76aa6f
--- /dev/null
+++ b/fuzzers/corpora/packfile/6b4dc6028a3a684a20dbc427b15a37ea2fd12dd1
Binary files differ
diff --git a/fuzzers/corpora/packfile/6b7486fc2a47a40eb5a85a5edf53af60d48be7d5 b/fuzzers/corpora/packfile/6b7486fc2a47a40eb5a85a5edf53af60d48be7d5
new file mode 100644
index 0000000..d1e0eed
--- /dev/null
+++ b/fuzzers/corpora/packfile/6b7486fc2a47a40eb5a85a5edf53af60d48be7d5
Binary files differ
diff --git a/fuzzers/corpora/packfile/6bace82ea640ac0a78963c79483faf0faa7fd168 b/fuzzers/corpora/packfile/6bace82ea640ac0a78963c79483faf0faa7fd168
new file mode 100644
index 0000000..8c53a7f
--- /dev/null
+++ b/fuzzers/corpora/packfile/6bace82ea640ac0a78963c79483faf0faa7fd168
@@ -0,0 +1 @@
+± \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/6ca38da5f096a5847776e4d50cb63121341fd67c b/fuzzers/corpora/packfile/6ca38da5f096a5847776e4d50cb63121341fd67c
new file mode 100644
index 0000000..628bda0
--- /dev/null
+++ b/fuzzers/corpora/packfile/6ca38da5f096a5847776e4d50cb63121341fd67c
Binary files differ
diff --git a/fuzzers/corpora/packfile/6d344a65b9edced36045f94215b6810799789334 b/fuzzers/corpora/packfile/6d344a65b9edced36045f94215b6810799789334
new file mode 100644
index 0000000..1c79442
--- /dev/null
+++ b/fuzzers/corpora/packfile/6d344a65b9edced36045f94215b6810799789334
Binary files differ
diff --git a/fuzzers/corpora/packfile/6dd655e8ef0573eb1c41151af924974aa1e9c454 b/fuzzers/corpora/packfile/6dd655e8ef0573eb1c41151af924974aa1e9c454
new file mode 100644
index 0000000..5304756
--- /dev/null
+++ b/fuzzers/corpora/packfile/6dd655e8ef0573eb1c41151af924974aa1e9c454
Binary files differ
diff --git a/fuzzers/corpora/packfile/6e118259c2940fafba0a9ceeef6308e12e881ae1 b/fuzzers/corpora/packfile/6e118259c2940fafba0a9ceeef6308e12e881ae1
new file mode 100644
index 0000000..23597c3
--- /dev/null
+++ b/fuzzers/corpora/packfile/6e118259c2940fafba0a9ceeef6308e12e881ae1
Binary files differ
diff --git a/fuzzers/corpora/packfile/6e4b5ef83333606a16a63b579f221f6ffb7b48ee b/fuzzers/corpora/packfile/6e4b5ef83333606a16a63b579f221f6ffb7b48ee
new file mode 100644
index 0000000..6d72c9a
--- /dev/null
+++ b/fuzzers/corpora/packfile/6e4b5ef83333606a16a63b579f221f6ffb7b48ee
@@ -0,0 +1 @@
+è \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/6f47ff60d54c012103a0c28851ffa9eab3002511 b/fuzzers/corpora/packfile/6f47ff60d54c012103a0c28851ffa9eab3002511
new file mode 100644
index 0000000..a7efd53
--- /dev/null
+++ b/fuzzers/corpora/packfile/6f47ff60d54c012103a0c28851ffa9eab3002511
Binary files differ
diff --git a/fuzzers/corpora/packfile/701a765befff451207517d56c3fe8609d059867d b/fuzzers/corpora/packfile/701a765befff451207517d56c3fe8609d059867d
new file mode 100644
index 0000000..ee936e3
--- /dev/null
+++ b/fuzzers/corpora/packfile/701a765befff451207517d56c3fe8609d059867d
Binary files differ
diff --git a/fuzzers/corpora/packfile/7050f56d64b28499c89d5430761f18a8a2a933d4 b/fuzzers/corpora/packfile/7050f56d64b28499c89d5430761f18a8a2a933d4
new file mode 100644
index 0000000..a3df396
--- /dev/null
+++ b/fuzzers/corpora/packfile/7050f56d64b28499c89d5430761f18a8a2a933d4
Binary files differ
diff --git a/fuzzers/corpora/packfile/724fa0194f615e1a0f08184a9f1520123f8e2833 b/fuzzers/corpora/packfile/724fa0194f615e1a0f08184a9f1520123f8e2833
new file mode 100644
index 0000000..dd6df78
--- /dev/null
+++ b/fuzzers/corpora/packfile/724fa0194f615e1a0f08184a9f1520123f8e2833
Binary files differ
diff --git a/fuzzers/corpora/packfile/72c52d0d98717e21dfee45418a046a19198b5d5d b/fuzzers/corpora/packfile/72c52d0d98717e21dfee45418a046a19198b5d5d
new file mode 100644
index 0000000..922e306
--- /dev/null
+++ b/fuzzers/corpora/packfile/72c52d0d98717e21dfee45418a046a19198b5d5d
Binary files differ
diff --git a/fuzzers/corpora/packfile/72cec0949c5743ee1df67b41ece5d6806f9bede6 b/fuzzers/corpora/packfile/72cec0949c5743ee1df67b41ece5d6806f9bede6
new file mode 100644
index 0000000..366439d
--- /dev/null
+++ b/fuzzers/corpora/packfile/72cec0949c5743ee1df67b41ece5d6806f9bede6
Binary files differ
diff --git a/fuzzers/corpora/packfile/72e6bfb7b881befc0b461334411d70ae227a426a b/fuzzers/corpora/packfile/72e6bfb7b881befc0b461334411d70ae227a426a
new file mode 100644
index 0000000..c7986f6
--- /dev/null
+++ b/fuzzers/corpora/packfile/72e6bfb7b881befc0b461334411d70ae227a426a
Binary files differ
diff --git a/fuzzers/corpora/packfile/73b74736664ad85828ce1be2e29fb4a68d24402b b/fuzzers/corpora/packfile/73b74736664ad85828ce1be2e29fb4a68d24402b
new file mode 100644
index 0000000..009080e
--- /dev/null
+++ b/fuzzers/corpora/packfile/73b74736664ad85828ce1be2e29fb4a68d24402b
@@ -0,0 +1 @@
+÷ \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/745bedb79413d20844a8b0e96fbec51b4989c65d b/fuzzers/corpora/packfile/745bedb79413d20844a8b0e96fbec51b4989c65d
new file mode 100644
index 0000000..0022a3e
--- /dev/null
+++ b/fuzzers/corpora/packfile/745bedb79413d20844a8b0e96fbec51b4989c65d
@@ -0,0 +1 @@
+ø \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/748142c17e56d0f0ad9e4d6525b39294d81261d6 b/fuzzers/corpora/packfile/748142c17e56d0f0ad9e4d6525b39294d81261d6
new file mode 100644
index 0000000..7ac1cad
--- /dev/null
+++ b/fuzzers/corpora/packfile/748142c17e56d0f0ad9e4d6525b39294d81261d6
Binary files differ
diff --git a/fuzzers/corpora/packfile/74dfea2e26741a8778fb942d1d60a84d0759d7a0 b/fuzzers/corpora/packfile/74dfea2e26741a8778fb942d1d60a84d0759d7a0
new file mode 100644
index 0000000..fe307f6
--- /dev/null
+++ b/fuzzers/corpora/packfile/74dfea2e26741a8778fb942d1d60a84d0759d7a0
@@ -0,0 +1 @@
+˜o \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/767b2efbb7502a2f652a448b471693d32c128677 b/fuzzers/corpora/packfile/767b2efbb7502a2f652a448b471693d32c128677
new file mode 100644
index 0000000..90a8f3a
--- /dev/null
+++ b/fuzzers/corpora/packfile/767b2efbb7502a2f652a448b471693d32c128677
Binary files differ
diff --git a/fuzzers/corpora/packfile/78abe558c4277852128d4b91282edcb68f86bdea b/fuzzers/corpora/packfile/78abe558c4277852128d4b91282edcb68f86bdea
new file mode 100644
index 0000000..30644d1
--- /dev/null
+++ b/fuzzers/corpora/packfile/78abe558c4277852128d4b91282edcb68f86bdea
Binary files differ
diff --git a/fuzzers/corpora/packfile/7960246c2db6d39e68dfe93ded358a3acba8f896 b/fuzzers/corpora/packfile/7960246c2db6d39e68dfe93ded358a3acba8f896
new file mode 100644
index 0000000..4d3703a
--- /dev/null
+++ b/fuzzers/corpora/packfile/7960246c2db6d39e68dfe93ded358a3acba8f896
Binary files differ
diff --git a/fuzzers/corpora/packfile/7a4ff814176b55af008ad9580201d5e659242f05 b/fuzzers/corpora/packfile/7a4ff814176b55af008ad9580201d5e659242f05
new file mode 100644
index 0000000..029e4fc
--- /dev/null
+++ b/fuzzers/corpora/packfile/7a4ff814176b55af008ad9580201d5e659242f05
Binary files differ
diff --git a/fuzzers/corpora/packfile/7a752694fce29ded083fbabbc9ff95f5b92a3d9c b/fuzzers/corpora/packfile/7a752694fce29ded083fbabbc9ff95f5b92a3d9c
new file mode 100644
index 0000000..82b5593
--- /dev/null
+++ b/fuzzers/corpora/packfile/7a752694fce29ded083fbabbc9ff95f5b92a3d9c
Binary files differ
diff --git a/fuzzers/corpora/packfile/7a81af3e591ac713f81ea1efe93dcf36157d8376 b/fuzzers/corpora/packfile/7a81af3e591ac713f81ea1efe93dcf36157d8376
new file mode 100644
index 0000000..883ad6e
--- /dev/null
+++ b/fuzzers/corpora/packfile/7a81af3e591ac713f81ea1efe93dcf36157d8376
@@ -0,0 +1 @@
+o \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/7c957a1fd650f9ae0eadc112837ea451a692affc b/fuzzers/corpora/packfile/7c957a1fd650f9ae0eadc112837ea451a692affc
new file mode 100644
index 0000000..251bc95
--- /dev/null
+++ b/fuzzers/corpora/packfile/7c957a1fd650f9ae0eadc112837ea451a692affc
Binary files differ
diff --git a/fuzzers/corpora/packfile/7cda4ab6a0daf50f675d5225cbc166c86a8ef95f b/fuzzers/corpora/packfile/7cda4ab6a0daf50f675d5225cbc166c86a8ef95f
new file mode 100644
index 0000000..9e58e19
--- /dev/null
+++ b/fuzzers/corpora/packfile/7cda4ab6a0daf50f675d5225cbc166c86a8ef95f
Binary files differ
diff --git a/fuzzers/corpora/packfile/7cf184f4c67ad58283ecb19349720b0cae756829 b/fuzzers/corpora/packfile/7cf184f4c67ad58283ecb19349720b0cae756829
new file mode 100644
index 0000000..8ac2eb5
--- /dev/null
+++ b/fuzzers/corpora/packfile/7cf184f4c67ad58283ecb19349720b0cae756829
@@ -0,0 +1 @@
+H \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/7df1ea8d86d601c3bd39977ea85e5f74c9db6acb b/fuzzers/corpora/packfile/7df1ea8d86d601c3bd39977ea85e5f74c9db6acb
new file mode 100644
index 0000000..0865a4a
--- /dev/null
+++ b/fuzzers/corpora/packfile/7df1ea8d86d601c3bd39977ea85e5f74c9db6acb
Binary files differ
diff --git a/fuzzers/corpora/packfile/820ec3e39089d863641a1be6942445db3ff34270 b/fuzzers/corpora/packfile/820ec3e39089d863641a1be6942445db3ff34270
new file mode 100644
index 0000000..a7a949a
--- /dev/null
+++ b/fuzzers/corpora/packfile/820ec3e39089d863641a1be6942445db3ff34270
Binary files differ
diff --git a/fuzzers/corpora/packfile/827704fd978bd02a46268b7396b202a52ad261ed b/fuzzers/corpora/packfile/827704fd978bd02a46268b7396b202a52ad261ed
new file mode 100644
index 0000000..855e5fc
--- /dev/null
+++ b/fuzzers/corpora/packfile/827704fd978bd02a46268b7396b202a52ad261ed
Binary files differ
diff --git a/fuzzers/corpora/packfile/828acfc1d49a0fdbcd9f238138ff65582c2a9fc8 b/fuzzers/corpora/packfile/828acfc1d49a0fdbcd9f238138ff65582c2a9fc8
new file mode 100644
index 0000000..cf479ad
--- /dev/null
+++ b/fuzzers/corpora/packfile/828acfc1d49a0fdbcd9f238138ff65582c2a9fc8
Binary files differ
diff --git a/fuzzers/corpora/packfile/8306a2f04a47fe4c95098675ffa25c074ecd89de b/fuzzers/corpora/packfile/8306a2f04a47fe4c95098675ffa25c074ecd89de
new file mode 100644
index 0000000..44cda73
--- /dev/null
+++ b/fuzzers/corpora/packfile/8306a2f04a47fe4c95098675ffa25c074ecd89de
Binary files differ
diff --git a/fuzzers/corpora/packfile/8327db1c6a884d8b3e3cefd94cec9eb94bffae0a b/fuzzers/corpora/packfile/8327db1c6a884d8b3e3cefd94cec9eb94bffae0a
new file mode 100644
index 0000000..315e1e6
--- /dev/null
+++ b/fuzzers/corpora/packfile/8327db1c6a884d8b3e3cefd94cec9eb94bffae0a
Binary files differ
diff --git a/fuzzers/corpora/packfile/847f4e42f8f2730a48d19951d8829621b2e70082 b/fuzzers/corpora/packfile/847f4e42f8f2730a48d19951d8829621b2e70082
new file mode 100644
index 0000000..22952d5
--- /dev/null
+++ b/fuzzers/corpora/packfile/847f4e42f8f2730a48d19951d8829621b2e70082
Binary files differ
diff --git a/fuzzers/corpora/packfile/84a516841ba77a5b4648de2cd0dfcb30ea46dbb4 b/fuzzers/corpora/packfile/84a516841ba77a5b4648de2cd0dfcb30ea46dbb4
new file mode 100644
index 0000000..3410062
--- /dev/null
+++ b/fuzzers/corpora/packfile/84a516841ba77a5b4648de2cd0dfcb30ea46dbb4
@@ -0,0 +1 @@
+c \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/8552526f5aba95119c0b95b61cd40386e7a3738b b/fuzzers/corpora/packfile/8552526f5aba95119c0b95b61cd40386e7a3738b
new file mode 100644
index 0000000..6a29f0f
--- /dev/null
+++ b/fuzzers/corpora/packfile/8552526f5aba95119c0b95b61cd40386e7a3738b
Binary files differ
diff --git a/fuzzers/corpora/packfile/8565db62ac64209ff009ac40e7c2d2ac4ae944eb b/fuzzers/corpora/packfile/8565db62ac64209ff009ac40e7c2d2ac4ae944eb
new file mode 100644
index 0000000..066b1da
--- /dev/null
+++ b/fuzzers/corpora/packfile/8565db62ac64209ff009ac40e7c2d2ac4ae944eb
Binary files differ
diff --git a/fuzzers/corpora/packfile/859b3346967c5c3c136459e565b402f9a936aa0d b/fuzzers/corpora/packfile/859b3346967c5c3c136459e565b402f9a936aa0d
new file mode 100644
index 0000000..9c10c98
--- /dev/null
+++ b/fuzzers/corpora/packfile/859b3346967c5c3c136459e565b402f9a936aa0d
Binary files differ
diff --git a/fuzzers/corpora/packfile/86a69caf3c5866d78d80da087b1b843cfea5e907 b/fuzzers/corpora/packfile/86a69caf3c5866d78d80da087b1b843cfea5e907
new file mode 100644
index 0000000..b122e37
--- /dev/null
+++ b/fuzzers/corpora/packfile/86a69caf3c5866d78d80da087b1b843cfea5e907
Binary files differ
diff --git a/fuzzers/corpora/packfile/86e1fb54a04fc18ee482b794ba3b2b306f6515d4 b/fuzzers/corpora/packfile/86e1fb54a04fc18ee482b794ba3b2b306f6515d4
new file mode 100644
index 0000000..7d31055
--- /dev/null
+++ b/fuzzers/corpora/packfile/86e1fb54a04fc18ee482b794ba3b2b306f6515d4
Binary files differ
diff --git a/fuzzers/corpora/packfile/86f217ee467d31ad9ad2a8c502b91279cd7f1c40 b/fuzzers/corpora/packfile/86f217ee467d31ad9ad2a8c502b91279cd7f1c40
new file mode 100644
index 0000000..9951e26
--- /dev/null
+++ b/fuzzers/corpora/packfile/86f217ee467d31ad9ad2a8c502b91279cd7f1c40
Binary files differ
diff --git a/fuzzers/corpora/packfile/8768a53e1d4c182907306300f9ca90cfd8018383 b/fuzzers/corpora/packfile/8768a53e1d4c182907306300f9ca90cfd8018383
new file mode 100644
index 0000000..3ea63c2
--- /dev/null
+++ b/fuzzers/corpora/packfile/8768a53e1d4c182907306300f9ca90cfd8018383
@@ -0,0 +1 @@
+… \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/8834a7aac170c494f45aa4da71b9605a52d82435 b/fuzzers/corpora/packfile/8834a7aac170c494f45aa4da71b9605a52d82435
new file mode 100644
index 0000000..40d2f60
--- /dev/null
+++ b/fuzzers/corpora/packfile/8834a7aac170c494f45aa4da71b9605a52d82435
Binary files differ
diff --git a/fuzzers/corpora/packfile/883f023f38a031d8a8e8ce2cba6614b9bff5d41f b/fuzzers/corpora/packfile/883f023f38a031d8a8e8ce2cba6614b9bff5d41f
new file mode 100644
index 0000000..30928b6
--- /dev/null
+++ b/fuzzers/corpora/packfile/883f023f38a031d8a8e8ce2cba6614b9bff5d41f
Binary files differ
diff --git a/fuzzers/corpora/packfile/88738071086eb04e47b77d1ca28b35ddbfaa0968 b/fuzzers/corpora/packfile/88738071086eb04e47b77d1ca28b35ddbfaa0968
new file mode 100644
index 0000000..7c1f28e
--- /dev/null
+++ b/fuzzers/corpora/packfile/88738071086eb04e47b77d1ca28b35ddbfaa0968
Binary files differ
diff --git a/fuzzers/corpora/packfile/892aef744c87c6ee4ba3dd457c7ca02ba3d359bd b/fuzzers/corpora/packfile/892aef744c87c6ee4ba3dd457c7ca02ba3d359bd
new file mode 100644
index 0000000..16e6f0a
--- /dev/null
+++ b/fuzzers/corpora/packfile/892aef744c87c6ee4ba3dd457c7ca02ba3d359bd
Binary files differ
diff --git a/fuzzers/corpora/packfile/8afb5c282d23c4055500f88a10b26383c682c900 b/fuzzers/corpora/packfile/8afb5c282d23c4055500f88a10b26383c682c900
new file mode 100644
index 0000000..cf91507
--- /dev/null
+++ b/fuzzers/corpora/packfile/8afb5c282d23c4055500f88a10b26383c682c900
Binary files differ
diff --git a/fuzzers/corpora/packfile/8b3dfce4cd7b8942eedb52af0e9ca4caa5c6de61 b/fuzzers/corpora/packfile/8b3dfce4cd7b8942eedb52af0e9ca4caa5c6de61
new file mode 100644
index 0000000..4c8cc0c
--- /dev/null
+++ b/fuzzers/corpora/packfile/8b3dfce4cd7b8942eedb52af0e9ca4caa5c6de61
Binary files differ
diff --git a/fuzzers/corpora/packfile/8c2cccf751bb5844bea8dc63c22e3f8e4489411e b/fuzzers/corpora/packfile/8c2cccf751bb5844bea8dc63c22e3f8e4489411e
new file mode 100644
index 0000000..70e2d30
--- /dev/null
+++ b/fuzzers/corpora/packfile/8c2cccf751bb5844bea8dc63c22e3f8e4489411e
Binary files differ
diff --git a/fuzzers/corpora/packfile/8e30894298502ba4d43af98f1ec3088f9b8f29d5 b/fuzzers/corpora/packfile/8e30894298502ba4d43af98f1ec3088f9b8f29d5
new file mode 100644
index 0000000..c4df802
--- /dev/null
+++ b/fuzzers/corpora/packfile/8e30894298502ba4d43af98f1ec3088f9b8f29d5
Binary files differ
diff --git a/fuzzers/corpora/packfile/8eb4d738f7170d2e0594b779f782eb3171c9d421 b/fuzzers/corpora/packfile/8eb4d738f7170d2e0594b779f782eb3171c9d421
new file mode 100644
index 0000000..410a7d6
--- /dev/null
+++ b/fuzzers/corpora/packfile/8eb4d738f7170d2e0594b779f782eb3171c9d421
Binary files differ
diff --git a/fuzzers/corpora/packfile/8f46a043da3aa5d466ade170e62b0b9f362b4c5b b/fuzzers/corpora/packfile/8f46a043da3aa5d466ade170e62b0b9f362b4c5b
new file mode 100644
index 0000000..172736f
--- /dev/null
+++ b/fuzzers/corpora/packfile/8f46a043da3aa5d466ade170e62b0b9f362b4c5b
Binary files differ
diff --git a/fuzzers/corpora/packfile/9295f39686016bf3abb1d6e9924d6725c1263920 b/fuzzers/corpora/packfile/9295f39686016bf3abb1d6e9924d6725c1263920
new file mode 100644
index 0000000..04be533
--- /dev/null
+++ b/fuzzers/corpora/packfile/9295f39686016bf3abb1d6e9924d6725c1263920
Binary files differ
diff --git a/fuzzers/corpora/packfile/92fa2c2237724e7ba49e9c59adad8d61ce400bbf b/fuzzers/corpora/packfile/92fa2c2237724e7ba49e9c59adad8d61ce400bbf
new file mode 100644
index 0000000..b890df0
--- /dev/null
+++ b/fuzzers/corpora/packfile/92fa2c2237724e7ba49e9c59adad8d61ce400bbf
Binary files differ
diff --git a/fuzzers/corpora/packfile/936548b53e1a1e30cb30747a87203abd4eae78ea b/fuzzers/corpora/packfile/936548b53e1a1e30cb30747a87203abd4eae78ea
new file mode 100644
index 0000000..2171770
--- /dev/null
+++ b/fuzzers/corpora/packfile/936548b53e1a1e30cb30747a87203abd4eae78ea
Binary files differ
diff --git a/fuzzers/corpora/packfile/9835ad3ff27939bc1315632d6a22980b377c36e4 b/fuzzers/corpora/packfile/9835ad3ff27939bc1315632d6a22980b377c36e4
new file mode 100644
index 0000000..025ad50
--- /dev/null
+++ b/fuzzers/corpora/packfile/9835ad3ff27939bc1315632d6a22980b377c36e4
@@ -0,0 +1 @@
+˜Pÿðÿðïÿðï \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/9857740c36a95415fa3be04cdf184db7b41a8b3e b/fuzzers/corpora/packfile/9857740c36a95415fa3be04cdf184db7b41a8b3e
new file mode 100644
index 0000000..7d3242f
--- /dev/null
+++ b/fuzzers/corpora/packfile/9857740c36a95415fa3be04cdf184db7b41a8b3e
Binary files differ
diff --git a/fuzzers/corpora/packfile/98c35b9d5e7b430d0d4ef70f642d8e2f3266b6d4 b/fuzzers/corpora/packfile/98c35b9d5e7b430d0d4ef70f642d8e2f3266b6d4
new file mode 100644
index 0000000..ea7c76e
--- /dev/null
+++ b/fuzzers/corpora/packfile/98c35b9d5e7b430d0d4ef70f642d8e2f3266b6d4
Binary files differ
diff --git a/fuzzers/corpora/packfile/9929b50ac145c0781a0347be1559764edc668173 b/fuzzers/corpora/packfile/9929b50ac145c0781a0347be1559764edc668173
new file mode 100644
index 0000000..33f1e92
--- /dev/null
+++ b/fuzzers/corpora/packfile/9929b50ac145c0781a0347be1559764edc668173
@@ -0,0 +1 @@
+žPACK³³ \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/9bf6a450d87badf2d495c2df37081ea16156915a b/fuzzers/corpora/packfile/9bf6a450d87badf2d495c2df37081ea16156915a
new file mode 100644
index 0000000..ff4c483
--- /dev/null
+++ b/fuzzers/corpora/packfile/9bf6a450d87badf2d495c2df37081ea16156915a
Binary files differ
diff --git a/fuzzers/corpora/packfile/9bffb3ff7a4429144305b770162074bbffe39ce9 b/fuzzers/corpora/packfile/9bffb3ff7a4429144305b770162074bbffe39ce9
new file mode 100644
index 0000000..aa9c335
--- /dev/null
+++ b/fuzzers/corpora/packfile/9bffb3ff7a4429144305b770162074bbffe39ce9
Binary files differ
diff --git a/fuzzers/corpora/packfile/9c040d3207196e3aeee0df389170d6e59733ba0f b/fuzzers/corpora/packfile/9c040d3207196e3aeee0df389170d6e59733ba0f
new file mode 100644
index 0000000..4c4e68a
--- /dev/null
+++ b/fuzzers/corpora/packfile/9c040d3207196e3aeee0df389170d6e59733ba0f
Binary files differ
diff --git a/fuzzers/corpora/packfile/9c740d0f3b8875a3b19f1cf1a88d5192a997a68d b/fuzzers/corpora/packfile/9c740d0f3b8875a3b19f1cf1a88d5192a997a68d
new file mode 100644
index 0000000..1f1ea65
--- /dev/null
+++ b/fuzzers/corpora/packfile/9c740d0f3b8875a3b19f1cf1a88d5192a997a68d
Binary files differ
diff --git a/fuzzers/corpora/packfile/9cf72097400efb70d06179e6b00abb4cdec74e66 b/fuzzers/corpora/packfile/9cf72097400efb70d06179e6b00abb4cdec74e66
new file mode 100644
index 0000000..25cfb39
--- /dev/null
+++ b/fuzzers/corpora/packfile/9cf72097400efb70d06179e6b00abb4cdec74e66
Binary files differ
diff --git a/fuzzers/corpora/packfile/9d36c270ef1f14214742562238dc747242d4756e b/fuzzers/corpora/packfile/9d36c270ef1f14214742562238dc747242d4756e
new file mode 100644
index 0000000..c54dc55
--- /dev/null
+++ b/fuzzers/corpora/packfile/9d36c270ef1f14214742562238dc747242d4756e
Binary files differ
diff --git a/fuzzers/corpora/packfile/9fb415ccadc8e7b0f38646ec5782d5895111e259 b/fuzzers/corpora/packfile/9fb415ccadc8e7b0f38646ec5782d5895111e259
new file mode 100644
index 0000000..473ebb7
--- /dev/null
+++ b/fuzzers/corpora/packfile/9fb415ccadc8e7b0f38646ec5782d5895111e259
Binary files differ
diff --git a/fuzzers/corpora/packfile/a13b7fbb454fe3bdebd07a51d466484aa41ee142 b/fuzzers/corpora/packfile/a13b7fbb454fe3bdebd07a51d466484aa41ee142
new file mode 100644
index 0000000..48edf15
--- /dev/null
+++ b/fuzzers/corpora/packfile/a13b7fbb454fe3bdebd07a51d466484aa41ee142
Binary files differ
diff --git a/fuzzers/corpora/packfile/a1a7715c7596c77b892dc6d4debb7c108ca4ef97 b/fuzzers/corpora/packfile/a1a7715c7596c77b892dc6d4debb7c108ca4ef97
new file mode 100644
index 0000000..3958311
--- /dev/null
+++ b/fuzzers/corpora/packfile/a1a7715c7596c77b892dc6d4debb7c108ca4ef97
@@ -0,0 +1 @@
+ù \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/a1ac8b656af02b56aefe6029db36b1af9fb664ef b/fuzzers/corpora/packfile/a1ac8b656af02b56aefe6029db36b1af9fb664ef
new file mode 100644
index 0000000..eeae769
--- /dev/null
+++ b/fuzzers/corpora/packfile/a1ac8b656af02b56aefe6029db36b1af9fb664ef
Binary files differ
diff --git a/fuzzers/corpora/packfile/a343687e2522222c2d49e8cb18d3feda64cf1d66 b/fuzzers/corpora/packfile/a343687e2522222c2d49e8cb18d3feda64cf1d66
new file mode 100644
index 0000000..7d24f8c
--- /dev/null
+++ b/fuzzers/corpora/packfile/a343687e2522222c2d49e8cb18d3feda64cf1d66
Binary files differ
diff --git a/fuzzers/corpora/packfile/a6f57425137e9aa54537f0b3f5364ce165aedb0a b/fuzzers/corpora/packfile/a6f57425137e9aa54537f0b3f5364ce165aedb0a
new file mode 100644
index 0000000..d50394e
--- /dev/null
+++ b/fuzzers/corpora/packfile/a6f57425137e9aa54537f0b3f5364ce165aedb0a
@@ -0,0 +1 @@
+Ì \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/a73df4ce29f75cc638a7a2d823fee57d909ab681 b/fuzzers/corpora/packfile/a73df4ce29f75cc638a7a2d823fee57d909ab681
new file mode 100644
index 0000000..1a443b9
--- /dev/null
+++ b/fuzzers/corpora/packfile/a73df4ce29f75cc638a7a2d823fee57d909ab681
Binary files differ
diff --git a/fuzzers/corpora/packfile/a7ee38bb7be4fc44198cb2685d9601dcf2b9f569 b/fuzzers/corpora/packfile/a7ee38bb7be4fc44198cb2685d9601dcf2b9f569
new file mode 100644
index 0000000..449e49e
--- /dev/null
+++ b/fuzzers/corpora/packfile/a7ee38bb7be4fc44198cb2685d9601dcf2b9f569
@@ -0,0 +1 @@
+K \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/a8b9b91157274e617bf4ac5045fc0c6ac97e76f7 b/fuzzers/corpora/packfile/a8b9b91157274e617bf4ac5045fc0c6ac97e76f7
new file mode 100644
index 0000000..8e4277b
--- /dev/null
+++ b/fuzzers/corpora/packfile/a8b9b91157274e617bf4ac5045fc0c6ac97e76f7
Binary files differ
diff --git a/fuzzers/corpora/packfile/a9c697f383f59a3b0642cd55b88190bce6201bae b/fuzzers/corpora/packfile/a9c697f383f59a3b0642cd55b88190bce6201bae
new file mode 100644
index 0000000..990de0a
--- /dev/null
+++ b/fuzzers/corpora/packfile/a9c697f383f59a3b0642cd55b88190bce6201bae
Binary files differ
diff --git a/fuzzers/corpora/packfile/ab064cd6847c0fa546bbec4241eb9b095e0e73da b/fuzzers/corpora/packfile/ab064cd6847c0fa546bbec4241eb9b095e0e73da
new file mode 100644
index 0000000..b95307b
--- /dev/null
+++ b/fuzzers/corpora/packfile/ab064cd6847c0fa546bbec4241eb9b095e0e73da
Binary files differ
diff --git a/fuzzers/corpora/packfile/ab2c64588d3d9dc5c54c48d414e6d46d6a78cfa6 b/fuzzers/corpora/packfile/ab2c64588d3d9dc5c54c48d414e6d46d6a78cfa6
new file mode 100644
index 0000000..02f5e79
--- /dev/null
+++ b/fuzzers/corpora/packfile/ab2c64588d3d9dc5c54c48d414e6d46d6a78cfa6
Binary files differ
diff --git a/fuzzers/corpora/packfile/abe729b06750880778312618dcebb43257ec03e0 b/fuzzers/corpora/packfile/abe729b06750880778312618dcebb43257ec03e0
new file mode 100644
index 0000000..fc1860b
--- /dev/null
+++ b/fuzzers/corpora/packfile/abe729b06750880778312618dcebb43257ec03e0
Binary files differ
diff --git a/fuzzers/corpora/packfile/ac1bf5a5fe61e5784f72b364ef1bcddfb0d13716 b/fuzzers/corpora/packfile/ac1bf5a5fe61e5784f72b364ef1bcddfb0d13716
new file mode 100644
index 0000000..5b9fb92
--- /dev/null
+++ b/fuzzers/corpora/packfile/ac1bf5a5fe61e5784f72b364ef1bcddfb0d13716
Binary files differ
diff --git a/fuzzers/corpora/packfile/ac47b6d3f0e479df3292131535f8e0d99c288de9 b/fuzzers/corpora/packfile/ac47b6d3f0e479df3292131535f8e0d99c288de9
new file mode 100644
index 0000000..def06ef
--- /dev/null
+++ b/fuzzers/corpora/packfile/ac47b6d3f0e479df3292131535f8e0d99c288de9
Binary files differ
diff --git a/fuzzers/corpora/packfile/ac9231da4082430afe8f4d40127814c613648d8e b/fuzzers/corpora/packfile/ac9231da4082430afe8f4d40127814c613648d8e
new file mode 100644
index 0000000..501a6bb
--- /dev/null
+++ b/fuzzers/corpora/packfile/ac9231da4082430afe8f4d40127814c613648d8e
@@ -0,0 +1 @@
+ \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/ace9ffcaa273c01c0eb60381321e47edf4842332 b/fuzzers/corpora/packfile/ace9ffcaa273c01c0eb60381321e47edf4842332
new file mode 100644
index 0000000..783b97f
--- /dev/null
+++ b/fuzzers/corpora/packfile/ace9ffcaa273c01c0eb60381321e47edf4842332
Binary files differ
diff --git a/fuzzers/corpora/packfile/ad6ba9b0bc076987efbeb11ce3fc92bc1df69d0a b/fuzzers/corpora/packfile/ad6ba9b0bc076987efbeb11ce3fc92bc1df69d0a
new file mode 100644
index 0000000..d6e99fc
--- /dev/null
+++ b/fuzzers/corpora/packfile/ad6ba9b0bc076987efbeb11ce3fc92bc1df69d0a
Binary files differ
diff --git a/fuzzers/corpora/packfile/ae99dcb9b5e1b09aa5df6bb2fada3a3de61268fe b/fuzzers/corpora/packfile/ae99dcb9b5e1b09aa5df6bb2fada3a3de61268fe
new file mode 100644
index 0000000..09e47fd
--- /dev/null
+++ b/fuzzers/corpora/packfile/ae99dcb9b5e1b09aa5df6bb2fada3a3de61268fe
Binary files differ
diff --git a/fuzzers/corpora/packfile/aeeacf0499ace69549fe2c76757d4948ba65a10b b/fuzzers/corpora/packfile/aeeacf0499ace69549fe2c76757d4948ba65a10b
new file mode 100644
index 0000000..4832b6e
--- /dev/null
+++ b/fuzzers/corpora/packfile/aeeacf0499ace69549fe2c76757d4948ba65a10b
Binary files differ
diff --git a/fuzzers/corpora/packfile/af6614c37604ee5d3f7b00cddca761a8776283b5 b/fuzzers/corpora/packfile/af6614c37604ee5d3f7b00cddca761a8776283b5
new file mode 100644
index 0000000..f41a799
--- /dev/null
+++ b/fuzzers/corpora/packfile/af6614c37604ee5d3f7b00cddca761a8776283b5
Binary files differ
diff --git a/fuzzers/corpora/packfile/afd44f8c385a922c8caacc1ea5688d324bad5b39 b/fuzzers/corpora/packfile/afd44f8c385a922c8caacc1ea5688d324bad5b39
new file mode 100644
index 0000000..eec2110
--- /dev/null
+++ b/fuzzers/corpora/packfile/afd44f8c385a922c8caacc1ea5688d324bad5b39
Binary files differ
diff --git a/fuzzers/corpora/packfile/aff024fe4ab0fece4091de044c58c9ae4233383a b/fuzzers/corpora/packfile/aff024fe4ab0fece4091de044c58c9ae4233383a
new file mode 100644
index 0000000..6bf0c97
--- /dev/null
+++ b/fuzzers/corpora/packfile/aff024fe4ab0fece4091de044c58c9ae4233383a
@@ -0,0 +1 @@
+w \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/b1f86f05d4928c8393fe0f138c0714df3978f0bb b/fuzzers/corpora/packfile/b1f86f05d4928c8393fe0f138c0714df3978f0bb
new file mode 100644
index 0000000..a36092c
--- /dev/null
+++ b/fuzzers/corpora/packfile/b1f86f05d4928c8393fe0f138c0714df3978f0bb
Binary files differ
diff --git a/fuzzers/corpora/packfile/b452cd4b70f2827e3cbd6d5dd20f678b6e55f813 b/fuzzers/corpora/packfile/b452cd4b70f2827e3cbd6d5dd20f678b6e55f813
new file mode 100644
index 0000000..f763b0d
--- /dev/null
+++ b/fuzzers/corpora/packfile/b452cd4b70f2827e3cbd6d5dd20f678b6e55f813
Binary files differ
diff --git a/fuzzers/corpora/packfile/b491dbad4c3edc87aa5a7f12b2c9a447a712c20d b/fuzzers/corpora/packfile/b491dbad4c3edc87aa5a7f12b2c9a447a712c20d
new file mode 100644
index 0000000..9e7d3e0
--- /dev/null
+++ b/fuzzers/corpora/packfile/b491dbad4c3edc87aa5a7f12b2c9a447a712c20d
Binary files differ
diff --git a/fuzzers/corpora/packfile/b54664965911c6fe91e18cd01b68a75c8183b530 b/fuzzers/corpora/packfile/b54664965911c6fe91e18cd01b68a75c8183b530
new file mode 100644
index 0000000..39e8d66
--- /dev/null
+++ b/fuzzers/corpora/packfile/b54664965911c6fe91e18cd01b68a75c8183b530
@@ -0,0 +1 @@
+ý \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/b68542373c05c0ed25231d09955b2c699d37c45b b/fuzzers/corpora/packfile/b68542373c05c0ed25231d09955b2c699d37c45b
new file mode 100644
index 0000000..050ac90
--- /dev/null
+++ b/fuzzers/corpora/packfile/b68542373c05c0ed25231d09955b2c699d37c45b
@@ -0,0 +1 @@
+þ \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/b706e78cf7110a78dfccce991cd4ce22c6fd898a b/fuzzers/corpora/packfile/b706e78cf7110a78dfccce991cd4ce22c6fd898a
new file mode 100644
index 0000000..e905006
--- /dev/null
+++ b/fuzzers/corpora/packfile/b706e78cf7110a78dfccce991cd4ce22c6fd898a
Binary files differ
diff --git a/fuzzers/corpora/packfile/b8d3910a75ad8a7058f9c3f202f8eb27419137d7 b/fuzzers/corpora/packfile/b8d3910a75ad8a7058f9c3f202f8eb27419137d7
new file mode 100644
index 0000000..0a0adc4
--- /dev/null
+++ b/fuzzers/corpora/packfile/b8d3910a75ad8a7058f9c3f202f8eb27419137d7
Binary files differ
diff --git a/fuzzers/corpora/packfile/b93abe6094fb4ebbfa7414fbceb7199ce766075b b/fuzzers/corpora/packfile/b93abe6094fb4ebbfa7414fbceb7199ce766075b
new file mode 100644
index 0000000..fb7b651
--- /dev/null
+++ b/fuzzers/corpora/packfile/b93abe6094fb4ebbfa7414fbceb7199ce766075b
Binary files differ
diff --git a/fuzzers/corpora/packfile/b9a64cc0694f3ac4a3c530c721bbf69026192187 b/fuzzers/corpora/packfile/b9a64cc0694f3ac4a3c530c721bbf69026192187
new file mode 100644
index 0000000..c775b6f
--- /dev/null
+++ b/fuzzers/corpora/packfile/b9a64cc0694f3ac4a3c530c721bbf69026192187
Binary files differ
diff --git a/fuzzers/corpora/packfile/b9e5319eca8fbc26e5c322e0b151ed8ed60628d1 b/fuzzers/corpora/packfile/b9e5319eca8fbc26e5c322e0b151ed8ed60628d1
new file mode 100644
index 0000000..54e0431
--- /dev/null
+++ b/fuzzers/corpora/packfile/b9e5319eca8fbc26e5c322e0b151ed8ed60628d1
Binary files differ
diff --git a/fuzzers/corpora/packfile/ba390745a04c5394601f7aa73fe795097b814d1a b/fuzzers/corpora/packfile/ba390745a04c5394601f7aa73fe795097b814d1a
new file mode 100644
index 0000000..1738d2d
--- /dev/null
+++ b/fuzzers/corpora/packfile/ba390745a04c5394601f7aa73fe795097b814d1a
Binary files differ
diff --git a/fuzzers/corpora/packfile/bb7d065b776833337d3e1a3071de4d5d2759d78b b/fuzzers/corpora/packfile/bb7d065b776833337d3e1a3071de4d5d2759d78b
new file mode 100644
index 0000000..2882b18
--- /dev/null
+++ b/fuzzers/corpora/packfile/bb7d065b776833337d3e1a3071de4d5d2759d78b
@@ -0,0 +1 @@
+“ \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/bb99cf0bb3e5d75d59300e6ca9cb1f67ce315e3a b/fuzzers/corpora/packfile/bb99cf0bb3e5d75d59300e6ca9cb1f67ce315e3a
new file mode 100644
index 0000000..7450e26
--- /dev/null
+++ b/fuzzers/corpora/packfile/bb99cf0bb3e5d75d59300e6ca9cb1f67ce315e3a
Binary files differ
diff --git a/fuzzers/corpora/packfile/bd9722d91e0615cbdae3cee3476ec6181fbad98d b/fuzzers/corpora/packfile/bd9722d91e0615cbdae3cee3476ec6181fbad98d
new file mode 100644
index 0000000..a8f95b6
--- /dev/null
+++ b/fuzzers/corpora/packfile/bd9722d91e0615cbdae3cee3476ec6181fbad98d
Binary files differ
diff --git a/fuzzers/corpora/packfile/bf8b4530d8d246dd74ac53a13471bba17941dff7 b/fuzzers/corpora/packfile/bf8b4530d8d246dd74ac53a13471bba17941dff7
new file mode 100644
index 0000000..6b2aaa7
--- /dev/null
+++ b/fuzzers/corpora/packfile/bf8b4530d8d246dd74ac53a13471bba17941dff7
@@ -0,0 +1 @@
+ \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/bffc4698ad4aaddd977fe857da20858aa6654263 b/fuzzers/corpora/packfile/bffc4698ad4aaddd977fe857da20858aa6654263
new file mode 100644
index 0000000..b7cde14
--- /dev/null
+++ b/fuzzers/corpora/packfile/bffc4698ad4aaddd977fe857da20858aa6654263
Binary files differ
diff --git a/fuzzers/corpora/packfile/c0ea828d8f9c4a2c0fc6253908cd283f6c7994a1 b/fuzzers/corpora/packfile/c0ea828d8f9c4a2c0fc6253908cd283f6c7994a1
new file mode 100644
index 0000000..ab56763
--- /dev/null
+++ b/fuzzers/corpora/packfile/c0ea828d8f9c4a2c0fc6253908cd283f6c7994a1
Binary files differ
diff --git a/fuzzers/corpora/packfile/c151b760696d665265187501c51f38cd84503634 b/fuzzers/corpora/packfile/c151b760696d665265187501c51f38cd84503634
new file mode 100644
index 0000000..4be2460
--- /dev/null
+++ b/fuzzers/corpora/packfile/c151b760696d665265187501c51f38cd84503634
@@ -0,0 +1 @@
+Ô \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/c16f90096603258174790bc85f076413dad0e228 b/fuzzers/corpora/packfile/c16f90096603258174790bc85f076413dad0e228
new file mode 100644
index 0000000..d736f27
--- /dev/null
+++ b/fuzzers/corpora/packfile/c16f90096603258174790bc85f076413dad0e228
Binary files differ
diff --git a/fuzzers/corpora/packfile/c20fc8fb8f1d44050c281089191b8eac2dc9444c b/fuzzers/corpora/packfile/c20fc8fb8f1d44050c281089191b8eac2dc9444c
new file mode 100644
index 0000000..221c482
--- /dev/null
+++ b/fuzzers/corpora/packfile/c20fc8fb8f1d44050c281089191b8eac2dc9444c
Binary files differ
diff --git a/fuzzers/corpora/packfile/c2143b1a0db17957bec1b41bb2e5f75aa135981e b/fuzzers/corpora/packfile/c2143b1a0db17957bec1b41bb2e5f75aa135981e
new file mode 100644
index 0000000..52e60b4
--- /dev/null
+++ b/fuzzers/corpora/packfile/c2143b1a0db17957bec1b41bb2e5f75aa135981e
@@ -0,0 +1 @@
+ \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/c22c3fba53bb2c5579b47852fa9ec54a88c03472 b/fuzzers/corpora/packfile/c22c3fba53bb2c5579b47852fa9ec54a88c03472
new file mode 100644
index 0000000..45df778
--- /dev/null
+++ b/fuzzers/corpora/packfile/c22c3fba53bb2c5579b47852fa9ec54a88c03472
Binary files differ
diff --git a/fuzzers/corpora/packfile/c297564cff1bb4f7933221050cfcffa36c59f691 b/fuzzers/corpora/packfile/c297564cff1bb4f7933221050cfcffa36c59f691
new file mode 100644
index 0000000..751e6bb
--- /dev/null
+++ b/fuzzers/corpora/packfile/c297564cff1bb4f7933221050cfcffa36c59f691
Binary files differ
diff --git a/fuzzers/corpora/packfile/c2c4da76233acd3efe08eaebb7ae8dc9b3036527 b/fuzzers/corpora/packfile/c2c4da76233acd3efe08eaebb7ae8dc9b3036527
new file mode 100644
index 0000000..5533ee3
--- /dev/null
+++ b/fuzzers/corpora/packfile/c2c4da76233acd3efe08eaebb7ae8dc9b3036527
Binary files differ
diff --git a/fuzzers/corpora/packfile/c3d47118536d19a8d1a601978510cc24344aa8df b/fuzzers/corpora/packfile/c3d47118536d19a8d1a601978510cc24344aa8df
new file mode 100644
index 0000000..dac4251
--- /dev/null
+++ b/fuzzers/corpora/packfile/c3d47118536d19a8d1a601978510cc24344aa8df
Binary files differ
diff --git a/fuzzers/corpora/packfile/c4cbb032db94c57061003a85d30bdf4117979b1e b/fuzzers/corpora/packfile/c4cbb032db94c57061003a85d30bdf4117979b1e
new file mode 100644
index 0000000..e46252e
--- /dev/null
+++ b/fuzzers/corpora/packfile/c4cbb032db94c57061003a85d30bdf4117979b1e
Binary files differ
diff --git a/fuzzers/corpora/packfile/c7090127a03c0e7230c11a649e4f98fcb4ca2b75 b/fuzzers/corpora/packfile/c7090127a03c0e7230c11a649e4f98fcb4ca2b75
new file mode 100644
index 0000000..7009494
--- /dev/null
+++ b/fuzzers/corpora/packfile/c7090127a03c0e7230c11a649e4f98fcb4ca2b75
Binary files differ
diff --git a/fuzzers/corpora/packfile/c8b839665bd381ff7d006b1b08c35f94f1818556 b/fuzzers/corpora/packfile/c8b839665bd381ff7d006b1b08c35f94f1818556
new file mode 100644
index 0000000..234b26a
--- /dev/null
+++ b/fuzzers/corpora/packfile/c8b839665bd381ff7d006b1b08c35f94f1818556
Binary files differ
diff --git a/fuzzers/corpora/packfile/ca8c7c16d1d6b60e951dcfb558cc97e14231c750 b/fuzzers/corpora/packfile/ca8c7c16d1d6b60e951dcfb558cc97e14231c750
new file mode 100644
index 0000000..e0b7aec
--- /dev/null
+++ b/fuzzers/corpora/packfile/ca8c7c16d1d6b60e951dcfb558cc97e14231c750
Binary files differ
diff --git a/fuzzers/corpora/packfile/cb46c744c83541a0900e1e61780c18d43031a08b b/fuzzers/corpora/packfile/cb46c744c83541a0900e1e61780c18d43031a08b
new file mode 100644
index 0000000..825026b
--- /dev/null
+++ b/fuzzers/corpora/packfile/cb46c744c83541a0900e1e61780c18d43031a08b
@@ -0,0 +1 @@
+Á \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/cbf1e454dc7ac878bd23e3dfd0b6a28a50b2155d b/fuzzers/corpora/packfile/cbf1e454dc7ac878bd23e3dfd0b6a28a50b2155d
new file mode 100644
index 0000000..7e60ba4
--- /dev/null
+++ b/fuzzers/corpora/packfile/cbf1e454dc7ac878bd23e3dfd0b6a28a50b2155d
Binary files differ
diff --git a/fuzzers/corpora/packfile/cbfb8cae82ddd82c04996f474fdb4f1b80dcb6db b/fuzzers/corpora/packfile/cbfb8cae82ddd82c04996f474fdb4f1b80dcb6db
new file mode 100644
index 0000000..7bd0640
--- /dev/null
+++ b/fuzzers/corpora/packfile/cbfb8cae82ddd82c04996f474fdb4f1b80dcb6db
Binary files differ
diff --git a/fuzzers/corpora/packfile/cf74f755c004ca634818f8ba44c99fffbaa950a1 b/fuzzers/corpora/packfile/cf74f755c004ca634818f8ba44c99fffbaa950a1
new file mode 100644
index 0000000..6c8a3e2
--- /dev/null
+++ b/fuzzers/corpora/packfile/cf74f755c004ca634818f8ba44c99fffbaa950a1
Binary files differ
diff --git a/fuzzers/corpora/packfile/d07e4bc786c88b8d2304f84c7db2098666f822c0 b/fuzzers/corpora/packfile/d07e4bc786c88b8d2304f84c7db2098666f822c0
new file mode 100644
index 0000000..5639b6d
--- /dev/null
+++ b/fuzzers/corpora/packfile/d07e4bc786c88b8d2304f84c7db2098666f822c0
@@ -0,0 +1 @@
+û \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/d17ab0db9edea68e8f9f51f471decae84b192a1a b/fuzzers/corpora/packfile/d17ab0db9edea68e8f9f51f471decae84b192a1a
new file mode 100644
index 0000000..e3ec67e
--- /dev/null
+++ b/fuzzers/corpora/packfile/d17ab0db9edea68e8f9f51f471decae84b192a1a
Binary files differ
diff --git a/fuzzers/corpora/packfile/d1854cae891ec7b29161ccaf79a24b00c274bdaa b/fuzzers/corpora/packfile/d1854cae891ec7b29161ccaf79a24b00c274bdaa
new file mode 100644
index 0000000..ef073cc
--- /dev/null
+++ b/fuzzers/corpora/packfile/d1854cae891ec7b29161ccaf79a24b00c274bdaa
@@ -0,0 +1 @@
+n \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/d22aac18f8b435fc34566fe0d3f42464aec9458c b/fuzzers/corpora/packfile/d22aac18f8b435fc34566fe0d3f42464aec9458c
new file mode 100644
index 0000000..766a326
--- /dev/null
+++ b/fuzzers/corpora/packfile/d22aac18f8b435fc34566fe0d3f42464aec9458c
Binary files differ
diff --git a/fuzzers/corpora/packfile/d446a50788c32053358495358696f9d595358bcf b/fuzzers/corpora/packfile/d446a50788c32053358495358696f9d595358bcf
new file mode 100644
index 0000000..1015f8e
--- /dev/null
+++ b/fuzzers/corpora/packfile/d446a50788c32053358495358696f9d595358bcf
Binary files differ
diff --git a/fuzzers/corpora/packfile/d461cbcff85c87b0068f0e9c15d2056197cdfa52 b/fuzzers/corpora/packfile/d461cbcff85c87b0068f0e9c15d2056197cdfa52
new file mode 100644
index 0000000..e475e61
--- /dev/null
+++ b/fuzzers/corpora/packfile/d461cbcff85c87b0068f0e9c15d2056197cdfa52
Binary files differ
diff --git a/fuzzers/corpora/packfile/d54709a1b46002c81f57da533379e57f00afe942 b/fuzzers/corpora/packfile/d54709a1b46002c81f57da533379e57f00afe942
new file mode 100644
index 0000000..010a294
--- /dev/null
+++ b/fuzzers/corpora/packfile/d54709a1b46002c81f57da533379e57f00afe942
Binary files differ
diff --git a/fuzzers/corpora/packfile/d567007f84b83e82df7069838bf8b6c5826b18a8 b/fuzzers/corpora/packfile/d567007f84b83e82df7069838bf8b6c5826b18a8
new file mode 100644
index 0000000..feb21a2
--- /dev/null
+++ b/fuzzers/corpora/packfile/d567007f84b83e82df7069838bf8b6c5826b18a8
Binary files differ
diff --git a/fuzzers/corpora/packfile/d5e60b9f94126a9ec865fda83feb6835d294b76b b/fuzzers/corpora/packfile/d5e60b9f94126a9ec865fda83feb6835d294b76b
new file mode 100644
index 0000000..63dd395
--- /dev/null
+++ b/fuzzers/corpora/packfile/d5e60b9f94126a9ec865fda83feb6835d294b76b
Binary files differ
diff --git a/fuzzers/corpora/packfile/d81092a4f3607ddbba85862facf2285459696078 b/fuzzers/corpora/packfile/d81092a4f3607ddbba85862facf2285459696078
new file mode 100644
index 0000000..0074e17
--- /dev/null
+++ b/fuzzers/corpora/packfile/d81092a4f3607ddbba85862facf2285459696078
Binary files differ
diff --git a/fuzzers/corpora/packfile/d898eb860ceac044950605db88429e029ea278ef b/fuzzers/corpora/packfile/d898eb860ceac044950605db88429e029ea278ef
new file mode 100644
index 0000000..a74a67e
--- /dev/null
+++ b/fuzzers/corpora/packfile/d898eb860ceac044950605db88429e029ea278ef
Binary files differ
diff --git a/fuzzers/corpora/packfile/d8fc60ccdd8f555c1858b9f0820f263e3d2b58ec b/fuzzers/corpora/packfile/d8fc60ccdd8f555c1858b9f0820f263e3d2b58ec
new file mode 100644
index 0000000..9d3cd68
--- /dev/null
+++ b/fuzzers/corpora/packfile/d8fc60ccdd8f555c1858b9f0820f263e3d2b58ec
@@ -0,0 +1 @@
+® \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/d9b69c63cdc0c1622f2fab84d1307f9e0c0fa3b9 b/fuzzers/corpora/packfile/d9b69c63cdc0c1622f2fab84d1307f9e0c0fa3b9
new file mode 100644
index 0000000..1f4966d
--- /dev/null
+++ b/fuzzers/corpora/packfile/d9b69c63cdc0c1622f2fab84d1307f9e0c0fa3b9
Binary files differ
diff --git a/fuzzers/corpora/packfile/db1bb4b7348d387623dcaf0a743d0b11fa18409f b/fuzzers/corpora/packfile/db1bb4b7348d387623dcaf0a743d0b11fa18409f
new file mode 100644
index 0000000..bed137b
--- /dev/null
+++ b/fuzzers/corpora/packfile/db1bb4b7348d387623dcaf0a743d0b11fa18409f
Binary files differ
diff --git a/fuzzers/corpora/packfile/db291360a195c79ae504a3dfb2cd0f71cbc11902 b/fuzzers/corpora/packfile/db291360a195c79ae504a3dfb2cd0f71cbc11902
new file mode 100644
index 0000000..aaebf73
--- /dev/null
+++ b/fuzzers/corpora/packfile/db291360a195c79ae504a3dfb2cd0f71cbc11902
Binary files differ
diff --git a/fuzzers/corpora/packfile/dc98359b3ef2ced9c3d07636c89d475a564c39d9 b/fuzzers/corpora/packfile/dc98359b3ef2ced9c3d07636c89d475a564c39d9
new file mode 100644
index 0000000..44b5bbe
--- /dev/null
+++ b/fuzzers/corpora/packfile/dc98359b3ef2ced9c3d07636c89d475a564c39d9
Binary files differ
diff --git a/fuzzers/corpora/packfile/dccc5642917b20b4dd64d3e44b71d08da30445e9 b/fuzzers/corpora/packfile/dccc5642917b20b4dd64d3e44b71d08da30445e9
new file mode 100644
index 0000000..d6430c2
--- /dev/null
+++ b/fuzzers/corpora/packfile/dccc5642917b20b4dd64d3e44b71d08da30445e9
Binary files differ
diff --git a/fuzzers/corpora/packfile/dd79c8cfb8beeacd0460429944b4ecbe95a31561 b/fuzzers/corpora/packfile/dd79c8cfb8beeacd0460429944b4ecbe95a31561
new file mode 100644
index 0000000..8a908ec
--- /dev/null
+++ b/fuzzers/corpora/packfile/dd79c8cfb8beeacd0460429944b4ecbe95a31561
@@ -0,0 +1 @@
+â \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/de9550264c4e2dbef14e1281ff3693f2d19dc1c9 b/fuzzers/corpora/packfile/de9550264c4e2dbef14e1281ff3693f2d19dc1c9
new file mode 100644
index 0000000..2c096ff
--- /dev/null
+++ b/fuzzers/corpora/packfile/de9550264c4e2dbef14e1281ff3693f2d19dc1c9
Binary files differ
diff --git a/fuzzers/corpora/packfile/df8b4d163e9ed75634eb56467343bde73b13263e b/fuzzers/corpora/packfile/df8b4d163e9ed75634eb56467343bde73b13263e
new file mode 100644
index 0000000..5c3529e
--- /dev/null
+++ b/fuzzers/corpora/packfile/df8b4d163e9ed75634eb56467343bde73b13263e
Binary files differ
diff --git a/fuzzers/corpora/packfile/e0184adedf913b076626646d3f52c3b49c39ad6d b/fuzzers/corpora/packfile/e0184adedf913b076626646d3f52c3b49c39ad6d
new file mode 100644
index 0000000..9fb75b8
--- /dev/null
+++ b/fuzzers/corpora/packfile/e0184adedf913b076626646d3f52c3b49c39ad6d
@@ -0,0 +1 @@
+E \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/e0905bac594c818b9cfa909269114977c4d6d1b2 b/fuzzers/corpora/packfile/e0905bac594c818b9cfa909269114977c4d6d1b2
new file mode 100644
index 0000000..40f2025
--- /dev/null
+++ b/fuzzers/corpora/packfile/e0905bac594c818b9cfa909269114977c4d6d1b2
Binary files differ
diff --git a/fuzzers/corpora/packfile/e0bcb16cd6b42128201e1b6454323175a7e412f0 b/fuzzers/corpora/packfile/e0bcb16cd6b42128201e1b6454323175a7e412f0
new file mode 100644
index 0000000..7386ac1
--- /dev/null
+++ b/fuzzers/corpora/packfile/e0bcb16cd6b42128201e1b6454323175a7e412f0
Binary files differ
diff --git a/fuzzers/corpora/packfile/e1ac9563c33f4f31b3e147b9d2fef80fca550948 b/fuzzers/corpora/packfile/e1ac9563c33f4f31b3e147b9d2fef80fca550948
new file mode 100644
index 0000000..c87f08b
--- /dev/null
+++ b/fuzzers/corpora/packfile/e1ac9563c33f4f31b3e147b9d2fef80fca550948
Binary files differ
diff --git a/fuzzers/corpora/packfile/e230c91352f1b07f6f34da803d07e75c06897b30 b/fuzzers/corpora/packfile/e230c91352f1b07f6f34da803d07e75c06897b30
new file mode 100644
index 0000000..ff1376f
--- /dev/null
+++ b/fuzzers/corpora/packfile/e230c91352f1b07f6f34da803d07e75c06897b30
Binary files differ
diff --git a/fuzzers/corpora/packfile/e26b3bacbfd6603d021d4ddadbac94b7b7aa0034 b/fuzzers/corpora/packfile/e26b3bacbfd6603d021d4ddadbac94b7b7aa0034
new file mode 100644
index 0000000..e6bdb3a
--- /dev/null
+++ b/fuzzers/corpora/packfile/e26b3bacbfd6603d021d4ddadbac94b7b7aa0034
@@ -0,0 +1 @@
+! \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/e2a6f8dc3dc5d6c859f19d6e0fa64745201df0a6 b/fuzzers/corpora/packfile/e2a6f8dc3dc5d6c859f19d6e0fa64745201df0a6
new file mode 100644
index 0000000..3afd5ca
--- /dev/null
+++ b/fuzzers/corpora/packfile/e2a6f8dc3dc5d6c859f19d6e0fa64745201df0a6
Binary files differ
diff --git a/fuzzers/corpora/packfile/e2ba004118345660b379df5147bfa7a39d884dbc b/fuzzers/corpora/packfile/e2ba004118345660b379df5147bfa7a39d884dbc
new file mode 100644
index 0000000..cb1ff94
--- /dev/null
+++ b/fuzzers/corpora/packfile/e2ba004118345660b379df5147bfa7a39d884dbc
Binary files differ
diff --git a/fuzzers/corpora/packfile/e45aaf139d726366a18dce9e4854ee6c82901677 b/fuzzers/corpora/packfile/e45aaf139d726366a18dce9e4854ee6c82901677
new file mode 100644
index 0000000..0ba1db8
--- /dev/null
+++ b/fuzzers/corpora/packfile/e45aaf139d726366a18dce9e4854ee6c82901677
Binary files differ
diff --git a/fuzzers/corpora/packfile/e4b3ab7e8c18de815fc8bd6ebfd5d52cf1924a8e b/fuzzers/corpora/packfile/e4b3ab7e8c18de815fc8bd6ebfd5d52cf1924a8e
new file mode 100644
index 0000000..786d94b
--- /dev/null
+++ b/fuzzers/corpora/packfile/e4b3ab7e8c18de815fc8bd6ebfd5d52cf1924a8e
Binary files differ
diff --git a/fuzzers/corpora/packfile/e6818b96c50bb749911248959af81a9c412a0223 b/fuzzers/corpora/packfile/e6818b96c50bb749911248959af81a9c412a0223
new file mode 100644
index 0000000..b8ac98e
--- /dev/null
+++ b/fuzzers/corpora/packfile/e6818b96c50bb749911248959af81a9c412a0223
Binary files differ
diff --git a/fuzzers/corpora/packfile/e69f20e9f683920d3fb4329abd951e878b1f9372 b/fuzzers/corpora/packfile/e69f20e9f683920d3fb4329abd951e878b1f9372
new file mode 100644
index 0000000..c137216
--- /dev/null
+++ b/fuzzers/corpora/packfile/e69f20e9f683920d3fb4329abd951e878b1f9372
@@ -0,0 +1 @@
+F \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/e6eb439fef7d5461bc3552aa7c064d24e44c5f32 b/fuzzers/corpora/packfile/e6eb439fef7d5461bc3552aa7c064d24e44c5f32
new file mode 100644
index 0000000..9ab1a4d
--- /dev/null
+++ b/fuzzers/corpora/packfile/e6eb439fef7d5461bc3552aa7c064d24e44c5f32
Binary files differ
diff --git a/fuzzers/corpora/packfile/e9d9930dc3fea44fbc7acb0d1ef4bd867f1c902b b/fuzzers/corpora/packfile/e9d9930dc3fea44fbc7acb0d1ef4bd867f1c902b
new file mode 100644
index 0000000..bd8ffbe
--- /dev/null
+++ b/fuzzers/corpora/packfile/e9d9930dc3fea44fbc7acb0d1ef4bd867f1c902b
Binary files differ
diff --git a/fuzzers/corpora/packfile/eb05b6ad73fb1f69ef750d0b9cb6c606ab9d949f b/fuzzers/corpora/packfile/eb05b6ad73fb1f69ef750d0b9cb6c606ab9d949f
new file mode 100644
index 0000000..aaba356
--- /dev/null
+++ b/fuzzers/corpora/packfile/eb05b6ad73fb1f69ef750d0b9cb6c606ab9d949f
Binary files differ
diff --git a/fuzzers/corpora/packfile/eb0814ae767e5f28b87c998b0f44dcf80814db1b b/fuzzers/corpora/packfile/eb0814ae767e5f28b87c998b0f44dcf80814db1b
new file mode 100644
index 0000000..f834b0b
--- /dev/null
+++ b/fuzzers/corpora/packfile/eb0814ae767e5f28b87c998b0f44dcf80814db1b
Binary files differ
diff --git a/fuzzers/corpora/packfile/ebbd9763912dd557d08abd1373c867a4b56e6a41 b/fuzzers/corpora/packfile/ebbd9763912dd557d08abd1373c867a4b56e6a41
new file mode 100644
index 0000000..9a5a0ed
--- /dev/null
+++ b/fuzzers/corpora/packfile/ebbd9763912dd557d08abd1373c867a4b56e6a41
Binary files differ
diff --git a/fuzzers/corpora/packfile/ebcdcb7effcc3f06e0d503638ac621de877fc554 b/fuzzers/corpora/packfile/ebcdcb7effcc3f06e0d503638ac621de877fc554
new file mode 100644
index 0000000..ef60809
--- /dev/null
+++ b/fuzzers/corpora/packfile/ebcdcb7effcc3f06e0d503638ac621de877fc554
@@ -0,0 +1 @@
+Ä \ No newline at end of file
diff --git a/fuzzers/corpora/packfile/eddccafb2716adafb9ad48203f0621bb00ebc73f b/fuzzers/corpora/packfile/eddccafb2716adafb9ad48203f0621bb00ebc73f
new file mode 100644
index 0000000..9b1c9fb
--- /dev/null
+++ b/fuzzers/corpora/packfile/eddccafb2716adafb9ad48203f0621bb00ebc73f
Binary files differ
diff --git a/fuzzers/corpora/packfile/edfbf20c83d3cec45470105581f7dc8b7e0889da b/fuzzers/corpora/packfile/edfbf20c83d3cec45470105581f7dc8b7e0889da
new file mode 100644
index 0000000..bf9c390
--- /dev/null
+++ b/fuzzers/corpora/packfile/edfbf20c83d3cec45470105581f7dc8b7e0889da
Binary files differ
diff --git a/fuzzers/corpora/packfile/f03218467b1c74e465cebb3b8092e21a5122f31d b/fuzzers/corpora/packfile/f03218467b1c74e465cebb3b8092e21a5122f31d
new file mode 100644
index 0000000..d7539e4
--- /dev/null
+++ b/fuzzers/corpora/packfile/f03218467b1c74e465cebb3b8092e21a5122f31d
Binary files differ
diff --git a/fuzzers/corpora/packfile/f28600befd899a94bed8e62853e90655d614f439 b/fuzzers/corpora/packfile/f28600befd899a94bed8e62853e90655d614f439
new file mode 100644
index 0000000..5087f32
--- /dev/null
+++ b/fuzzers/corpora/packfile/f28600befd899a94bed8e62853e90655d614f439
Binary files differ
diff --git a/fuzzers/corpora/packfile/f3b15185b7a9a10716752d58434fe656d839092e b/fuzzers/corpora/packfile/f3b15185b7a9a10716752d58434fe656d839092e
new file mode 100644
index 0000000..8575244
--- /dev/null
+++ b/fuzzers/corpora/packfile/f3b15185b7a9a10716752d58434fe656d839092e
Binary files differ
diff --git a/fuzzers/corpora/packfile/f436ed7933482610e08e18b40e9eec102b63b7d4 b/fuzzers/corpora/packfile/f436ed7933482610e08e18b40e9eec102b63b7d4
new file mode 100644
index 0000000..4a7dcff
--- /dev/null
+++ b/fuzzers/corpora/packfile/f436ed7933482610e08e18b40e9eec102b63b7d4
Binary files differ
diff --git a/fuzzers/corpora/packfile/f55ea5b7c1cf5400aae56d7faf65a42320d0323a b/fuzzers/corpora/packfile/f55ea5b7c1cf5400aae56d7faf65a42320d0323a
new file mode 100644
index 0000000..c5e3137
--- /dev/null
+++ b/fuzzers/corpora/packfile/f55ea5b7c1cf5400aae56d7faf65a42320d0323a
Binary files differ
diff --git a/fuzzers/corpora/packfile/f5eeab2d009aa4984378df6bfdd89366b7ecbb32 b/fuzzers/corpora/packfile/f5eeab2d009aa4984378df6bfdd89366b7ecbb32
new file mode 100644
index 0000000..15828cf
--- /dev/null
+++ b/fuzzers/corpora/packfile/f5eeab2d009aa4984378df6bfdd89366b7ecbb32
Binary files differ
diff --git a/fuzzers/corpora/packfile/f6250c8b3cc0510e0f8f621100be83f018e2d234 b/fuzzers/corpora/packfile/f6250c8b3cc0510e0f8f621100be83f018e2d234
new file mode 100644
index 0000000..ab03a5e
--- /dev/null
+++ b/fuzzers/corpora/packfile/f6250c8b3cc0510e0f8f621100be83f018e2d234
Binary files differ
diff --git a/fuzzers/corpora/packfile/f7168410c7158ff7331698930937f9cdc54f4d8a b/fuzzers/corpora/packfile/f7168410c7158ff7331698930937f9cdc54f4d8a
new file mode 100644
index 0000000..e42053d
--- /dev/null
+++ b/fuzzers/corpora/packfile/f7168410c7158ff7331698930937f9cdc54f4d8a
Binary files differ
diff --git a/fuzzers/corpora/packfile/f91847640af285c1b8a6df27f5c50686ed0deb70 b/fuzzers/corpora/packfile/f91847640af285c1b8a6df27f5c50686ed0deb70
new file mode 100644
index 0000000..615255f
--- /dev/null
+++ b/fuzzers/corpora/packfile/f91847640af285c1b8a6df27f5c50686ed0deb70
Binary files differ
diff --git a/fuzzers/corpora/packfile/fa58a6b2d3286a136a43afeeaac589d2dde0a2a6 b/fuzzers/corpora/packfile/fa58a6b2d3286a136a43afeeaac589d2dde0a2a6
new file mode 100644
index 0000000..860053e
--- /dev/null
+++ b/fuzzers/corpora/packfile/fa58a6b2d3286a136a43afeeaac589d2dde0a2a6
Binary files differ
diff --git a/fuzzers/corpora/packfile/fe3667be5fd2ffdd553ae04a534a2e9ce5445188 b/fuzzers/corpora/packfile/fe3667be5fd2ffdd553ae04a534a2e9ce5445188
new file mode 100644
index 0000000..d92e574
--- /dev/null
+++ b/fuzzers/corpora/packfile/fe3667be5fd2ffdd553ae04a534a2e9ce5445188
Binary files differ
diff --git a/fuzzers/corpora/packfile/ff21cad92ddd105224408fa696e91080a8cf98fb b/fuzzers/corpora/packfile/ff21cad92ddd105224408fa696e91080a8cf98fb
new file mode 100644
index 0000000..c1cfc00
--- /dev/null
+++ b/fuzzers/corpora/packfile/ff21cad92ddd105224408fa696e91080a8cf98fb
Binary files differ
diff --git a/fuzzers/corpora/packfile/ff9804ac04790bd58cdd124526c00b920469b812 b/fuzzers/corpora/packfile/ff9804ac04790bd58cdd124526c00b920469b812
new file mode 100644
index 0000000..4280c82
--- /dev/null
+++ b/fuzzers/corpora/packfile/ff9804ac04790bd58cdd124526c00b920469b812
Binary files differ
diff --git a/fuzzers/corpora/packfile/ffc54ca808e7666f250133ad0ae2185ad688a826 b/fuzzers/corpora/packfile/ffc54ca808e7666f250133ad0ae2185ad688a826
new file mode 100644
index 0000000..4489a65
--- /dev/null
+++ b/fuzzers/corpora/packfile/ffc54ca808e7666f250133ad0ae2185ad688a826
@@ -0,0 +1 @@
+² \ No newline at end of file
diff --git a/fuzzers/corpora/patch_parse/edit-file.diff b/fuzzers/corpora/patch_parse/edit-file.diff
new file mode 100644
index 0000000..d9e783a
--- /dev/null
+++ b/fuzzers/corpora/patch_parse/edit-file.diff
@@ -0,0 +1,13 @@
+diff --git a/fuzzers/patch_fuzzer.c b/fuzzers/patch_fuzzer.c
+index 76186b6fb..f7ce73ac8 100644
+--- a/fuzzers/patch_fuzzer.c
++++ b/fuzzers/patch_fuzzer.c
+@@ -32,7 +32,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+ git_patch* patch;
+ git_patch_options opts = {(uint32_t)data[0]};
+ int status = git_patch_from_buffer(&patch, (const char*)data+1, size-1, &opts);
+- if (status == 0 && patch) {
++ if (patch) {
+ git_patch_free(patch);
+ }
+ return 0;
diff --git a/fuzzers/corpora/patch_parse/patch_fuzzer-patch.diff b/fuzzers/corpora/patch_parse/patch_fuzzer-patch.diff
new file mode 100644
index 0000000..7c98d8a
--- /dev/null
+++ b/fuzzers/corpora/patch_parse/patch_fuzzer-patch.diff
@@ -0,0 +1,45 @@
+diff --git a/fuzzers/patch_fuzzer.c b/fuzzers/patch_fuzzer.c
+new file mode 100644
+index 000000000..76186b6fb
+--- /dev/null
++++ b/fuzzers/patch_fuzzer.c
+@@ -0,0 +1,39 @@
++/*
++ * libgit2 patch fuzzer target.
++ *
++ * Copyright (C) the libgit2 contributors. All rights reserved.
++ *
++ * This file is part of libgit2, distributed under the GNU GPL v2 with
++ * a Linking Exception. For full terms see the included COPYING file.
++ */
++
++#include "git2.h"
++#include "patch.h"
++#include "patch_parse.h"
++
++#define UNUSED(x) (void)(x)
++
++int LLVMFuzzerInitialize(int *argc, char ***argv)
++{
++ UNUSED(argc);
++ UNUSED(argv);
++
++ if (git_libgit2_init() < 0)
++ abort();
++
++ return 0;
++}
++
++int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
++{
++ if (size < 1) {
++ return 0;
++ }
++ git_patch* patch;
++ git_patch_options opts = {(uint32_t)data[0]};
++ int status = git_patch_from_buffer(&patch, (const char*)data+1, size-1, &opts);
++ if (status == 0 && patch) {
++ git_patch_free(patch);
++ }
++ return 0;
++}
diff --git a/fuzzers/download_refs_fuzzer.c b/fuzzers/download_refs_fuzzer.c
new file mode 100644
index 0000000..ff95cd1
--- /dev/null
+++ b/fuzzers/download_refs_fuzzer.c
@@ -0,0 +1,225 @@
+/*
+ * libgit2 raw packfile fuzz target.
+ *
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "git2.h"
+#include "git2/sys/transport.h"
+#include "futils.h"
+
+#include "standalone_driver.h"
+
+#define UNUSED(x) (void)(x)
+
+struct fuzzer_buffer {
+ const unsigned char *data;
+ size_t size;
+};
+
+struct fuzzer_stream {
+ git_smart_subtransport_stream base;
+ const unsigned char *readp;
+ const unsigned char *endp;
+};
+
+struct fuzzer_subtransport {
+ git_smart_subtransport base;
+ git_transport *owner;
+ struct fuzzer_buffer data;
+};
+
+static git_repository *repo;
+
+static int fuzzer_stream_read(git_smart_subtransport_stream *stream,
+ char *buffer,
+ size_t buf_size,
+ size_t *bytes_read)
+{
+ struct fuzzer_stream *fs = (struct fuzzer_stream *) stream;
+ size_t avail = fs->endp - fs->readp;
+
+ *bytes_read = (buf_size > avail) ? avail : buf_size;
+ memcpy(buffer, fs->readp, *bytes_read);
+ fs->readp += *bytes_read;
+
+ return 0;
+}
+
+static int fuzzer_stream_write(git_smart_subtransport_stream *stream,
+ const char *buffer, size_t len)
+{
+ UNUSED(stream);
+ UNUSED(buffer);
+ UNUSED(len);
+ return 0;
+}
+
+static void fuzzer_stream_free(git_smart_subtransport_stream *stream)
+{
+ free(stream);
+}
+
+static int fuzzer_stream_new(
+ struct fuzzer_stream **out,
+ const struct fuzzer_buffer *data)
+{
+ struct fuzzer_stream *stream = malloc(sizeof(*stream));
+ if (!stream)
+ return -1;
+
+ stream->readp = data->data;
+ stream->endp = data->data + data->size;
+ stream->base.read = fuzzer_stream_read;
+ stream->base.write = fuzzer_stream_write;
+ stream->base.free = fuzzer_stream_free;
+
+ *out = stream;
+
+ return 0;
+}
+
+static int fuzzer_subtransport_action(
+ git_smart_subtransport_stream **out,
+ git_smart_subtransport *transport,
+ const char *url,
+ git_smart_service_t action)
+{
+ struct fuzzer_subtransport *ft = (struct fuzzer_subtransport *) transport;
+
+ UNUSED(url);
+ UNUSED(action);
+
+ return fuzzer_stream_new((struct fuzzer_stream **) out, &ft->data);
+}
+
+static int fuzzer_subtransport_close(git_smart_subtransport *transport)
+{
+ UNUSED(transport);
+ return 0;
+}
+
+static void fuzzer_subtransport_free(git_smart_subtransport *transport)
+{
+ free(transport);
+}
+
+static int fuzzer_subtransport_new(
+ struct fuzzer_subtransport **out,
+ git_transport *owner,
+ const struct fuzzer_buffer *data)
+{
+ struct fuzzer_subtransport *sub = malloc(sizeof(*sub));
+ if (!sub)
+ return -1;
+
+ sub->owner = owner;
+ sub->data.data = data->data;
+ sub->data.size = data->size;
+ sub->base.action = fuzzer_subtransport_action;
+ sub->base.close = fuzzer_subtransport_close;
+ sub->base.free = fuzzer_subtransport_free;
+
+ *out = sub;
+
+ return 0;
+}
+
+static int fuzzer_subtransport_cb(
+ git_smart_subtransport **out,
+ git_transport *owner,
+ void *payload)
+{
+ struct fuzzer_buffer *buf = (struct fuzzer_buffer *) payload;
+ struct fuzzer_subtransport *sub;
+
+ if (fuzzer_subtransport_new(&sub, owner, buf) < 0)
+ return -1;
+
+ *out = &sub->base;
+ return 0;
+}
+
+static int fuzzer_transport_cb(git_transport **out, git_remote *owner, void *param)
+{
+ git_smart_subtransport_definition def = {
+ fuzzer_subtransport_cb,
+ 1,
+ param
+ };
+ return git_transport_smart(out, owner, &def);
+}
+
+static void fuzzer_git_abort(const char *op)
+{
+ const git_error *err = git_error_last();
+ fprintf(stderr, "unexpected libgit error: %s: %s\n",
+ op, err ? err->message : "<none>");
+ abort();
+}
+
+int LLVMFuzzerInitialize(int *argc, char ***argv)
+{
+#if defined(_WIN32)
+ char tmpdir[MAX_PATH], path[MAX_PATH];
+
+ if (GetTempPath((DWORD)sizeof(tmpdir), tmpdir) == 0)
+ abort();
+
+ if (GetTempFileName(tmpdir, "lg2", 1, path) == 0)
+ abort();
+
+ if (git_futils_mkdir(path, 0700, 0) < 0)
+ abort();
+#else
+ char path[] = "/tmp/git2.XXXXXX";
+
+ if (mkdtemp(path) != path)
+ abort();
+#endif
+
+ if (git_libgit2_init() < 0)
+ abort();
+
+ if (git_libgit2_opts(GIT_OPT_SET_PACK_MAX_OBJECTS, 10000000) < 0)
+ abort();
+
+ UNUSED(argc);
+ UNUSED(argv);
+
+ if (git_repository_init(&repo, path, 1) < 0)
+ fuzzer_git_abort("git_repository_init");
+
+ return 0;
+}
+
+int LLVMFuzzerTestOneInput(const unsigned char *data, size_t size)
+{
+ struct fuzzer_buffer buffer = { data, size };
+ git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
+ git_remote *remote;
+
+ if (git_remote_create_anonymous(&remote, repo, "fuzzer://remote-url") < 0)
+ fuzzer_git_abort("git_remote_create");
+
+ callbacks.transport = fuzzer_transport_cb;
+ callbacks.payload = &buffer;
+
+ if (git_remote_connect(remote, GIT_DIRECTION_FETCH,
+ &callbacks, NULL, NULL) < 0)
+ goto out;
+
+ git_remote_download(remote, NULL, NULL);
+
+ out:
+ git_remote_free(remote);
+
+ return 0;
+}
diff --git a/fuzzers/midx_fuzzer.c b/fuzzers/midx_fuzzer.c
new file mode 100644
index 0000000..21fb903
--- /dev/null
+++ b/fuzzers/midx_fuzzer.c
@@ -0,0 +1,80 @@
+/*
+ * libgit2 multi-pack-index fuzzer target.
+ *
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include <stdio.h>
+
+#include "git2.h"
+
+#include "common.h"
+#include "futils.h"
+#include "hash.h"
+#include "midx.h"
+
+#include "standalone_driver.h"
+
+int LLVMFuzzerInitialize(int *argc, char ***argv)
+{
+ GIT_UNUSED(argc);
+ GIT_UNUSED(argv);
+
+ if (git_libgit2_init() < 0) {
+ fprintf(stderr, "Failed to initialize libgit2\n");
+ abort();
+ }
+ return 0;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ git_midx_file idx = {{0}};
+ git_midx_entry e;
+ git_str midx_buf = GIT_STR_INIT;
+ unsigned char hash[GIT_HASH_SHA1_SIZE];
+ git_oid oid = GIT_OID_NONE;
+ bool append_hash = false;
+
+ if (size < 4)
+ return 0;
+
+ /*
+ * If the first byte in the stream has the high bit set, append the
+ * SHA1 hash so that the packfile is somewhat valid.
+ */
+ append_hash = *data & 0x80;
+ /* Keep a 4-byte alignment to avoid unaligned accesses. */
+ data += 4;
+ size -= 4;
+
+ if (append_hash) {
+ if (git_str_init(&midx_buf, size + GIT_HASH_SHA1_SIZE) < 0)
+ goto cleanup;
+ if (git_hash_buf(hash, data, size, GIT_HASH_ALGORITHM_SHA1) < 0) {
+ fprintf(stderr, "Failed to compute the SHA1 hash\n");
+ abort();
+ }
+ memcpy(midx_buf.ptr, data, size);
+ memcpy(midx_buf.ptr + size, hash, GIT_HASH_SHA1_SIZE);
+
+ memcpy(oid.id, hash, GIT_OID_SHA1_SIZE);
+ } else {
+ git_str_attach_notowned(&midx_buf, (char *)data, size);
+ }
+
+ if (git_midx_parse(&idx, (const unsigned char *)git_str_cstr(&midx_buf), git_str_len(&midx_buf)) < 0)
+ goto cleanup;
+
+ /* Search for any oid, just to exercise that codepath. */
+ if (git_midx_entry_find(&e, &idx, &oid, GIT_OID_SHA1_HEXSIZE) < 0)
+ goto cleanup;
+
+cleanup:
+ git_midx_close(&idx);
+ git_str_dispose(&midx_buf);
+ return 0;
+}
diff --git a/fuzzers/objects_fuzzer.c b/fuzzers/objects_fuzzer.c
new file mode 100644
index 0000000..7294e9b
--- /dev/null
+++ b/fuzzers/objects_fuzzer.c
@@ -0,0 +1,49 @@
+/*
+ * libgit2 packfile fuzzer target.
+ *
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "git2.h"
+#include "object.h"
+
+#include "standalone_driver.h"
+
+#define UNUSED(x) (void)(x)
+
+int LLVMFuzzerInitialize(int *argc, char ***argv)
+{
+ UNUSED(argc);
+ UNUSED(argv);
+
+ if (git_libgit2_init() < 0)
+ abort();
+
+ return 0;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ const git_object_t types[] = {
+ GIT_OBJECT_BLOB, GIT_OBJECT_TREE, GIT_OBJECT_COMMIT, GIT_OBJECT_TAG
+ };
+ git_object *object = NULL;
+ size_t i;
+
+ /*
+ * Brute-force parse this as every object type. We want
+ * to stress the parsing logic anyway, so this is fine
+ * to do.
+ */
+ for (i = 0; i < ARRAY_SIZE(types); i++) {
+ if (git_object__from_raw(&object, (const char *) data, size, types[i], GIT_OID_SHA1) < 0)
+ continue;
+ git_object_free(object);
+ object = NULL;
+ }
+
+ return 0;
+}
diff --git a/fuzzers/packfile_fuzzer.c b/fuzzers/packfile_fuzzer.c
new file mode 100644
index 0000000..aeba957
--- /dev/null
+++ b/fuzzers/packfile_fuzzer.c
@@ -0,0 +1,143 @@
+/*
+ * libgit2 packfile fuzzer target.
+ *
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include <stdio.h>
+
+#include "git2.h"
+#include "git2/sys/mempack.h"
+#include "common.h"
+#include "str.h"
+
+#include "standalone_driver.h"
+
+static git_odb *odb = NULL;
+static git_odb_backend *mempack = NULL;
+
+/* Arbitrary object to seed the ODB. */
+static const unsigned char base_obj[] = { 07, 076 };
+static const unsigned int base_obj_len = 2;
+
+int LLVMFuzzerInitialize(int *argc, char ***argv)
+{
+ GIT_UNUSED(argc);
+ GIT_UNUSED(argv);
+
+ if (git_libgit2_init() < 0) {
+ fprintf(stderr, "Failed to initialize libgit2\n");
+ abort();
+ }
+ if (git_libgit2_opts(GIT_OPT_SET_PACK_MAX_OBJECTS, 10000000) < 0) {
+ fprintf(stderr, "Failed to limit maximum pack object count\n");
+ abort();
+ }
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ if (git_odb_new(&odb, NULL) < 0) {
+ fprintf(stderr, "Failed to create the odb\n");
+ abort();
+ }
+#else
+ if (git_odb_new(&odb) < 0) {
+ fprintf(stderr, "Failed to create the odb\n");
+ abort();
+ }
+#endif
+
+ if (git_mempack_new(&mempack) < 0) {
+ fprintf(stderr, "Failed to create the mempack\n");
+ abort();
+ }
+ if (git_odb_add_backend(odb, mempack, 999) < 0) {
+ fprintf(stderr, "Failed to add the mempack\n");
+ abort();
+ }
+ return 0;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ git_indexer_progress stats = {0, 0};
+ git_indexer *indexer = NULL;
+ git_str path = GIT_STR_INIT;
+ git_oid oid;
+ bool append_hash = false;
+ int error;
+
+ if (size == 0)
+ return 0;
+
+ if (!odb || !mempack) {
+ fprintf(stderr, "Global state not initialized\n");
+ abort();
+ }
+ git_mempack_reset(mempack);
+
+ if (git_odb_write(&oid, odb, base_obj, base_obj_len, GIT_OBJECT_BLOB) < 0) {
+ fprintf(stderr, "Failed to add an object to the odb\n");
+ abort();
+ }
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ error = git_indexer_new(&indexer, ".", GIT_OID_SHA1, NULL);
+#else
+ error = git_indexer_new(&indexer, ".", 0, odb, NULL);
+#endif
+
+ if (error < 0) {
+ fprintf(stderr, "Failed to create the indexer: %s\n",
+ git_error_last()->message);
+ abort();
+ }
+
+ /*
+ * If the first byte in the stream has the high bit set, append the
+ * SHA1 hash so that the packfile is somewhat valid.
+ */
+ append_hash = *data & 0x80;
+ ++data;
+ --size;
+
+ if (git_indexer_append(indexer, data, size, &stats) < 0)
+ goto cleanup;
+ if (append_hash) {
+#ifdef GIT_EXPERIMENTAL_SHA256
+ if (git_odb_hash(&oid, data, size, GIT_OBJECT_BLOB, GIT_OID_SHA1) < 0) {
+ fprintf(stderr, "Failed to compute the SHA1 hash\n");
+ abort();
+ }
+#else
+ if (git_odb_hash(&oid, data, size, GIT_OBJECT_BLOB) < 0) {
+ fprintf(stderr, "Failed to compute the SHA1 hash\n");
+ abort();
+ }
+#endif
+
+ if (git_indexer_append(indexer, &oid.id, GIT_OID_SHA1_SIZE, &stats) < 0) {
+ goto cleanup;
+ }
+ }
+ if (git_indexer_commit(indexer, &stats) < 0)
+ goto cleanup;
+
+ if (git_str_printf(&path, "pack-%s.idx", git_indexer_name(indexer)) < 0)
+ goto cleanup;
+ p_unlink(git_str_cstr(&path));
+
+ git_str_clear(&path);
+
+ if (git_str_printf(&path, "pack-%s.pack", git_indexer_name(indexer)) < 0)
+ goto cleanup;
+ p_unlink(git_str_cstr(&path));
+
+cleanup:
+ git_mempack_reset(mempack);
+ git_indexer_free(indexer);
+ git_str_dispose(&path);
+ return 0;
+}
diff --git a/fuzzers/patch_parse_fuzzer.c b/fuzzers/patch_parse_fuzzer.c
new file mode 100644
index 0000000..2e65a01
--- /dev/null
+++ b/fuzzers/patch_parse_fuzzer.c
@@ -0,0 +1,40 @@
+/*
+ * libgit2 patch parser fuzzer target.
+ *
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "git2.h"
+#include "patch.h"
+#include "patch_parse.h"
+
+#include "standalone_driver.h"
+
+#define UNUSED(x) (void)(x)
+
+int LLVMFuzzerInitialize(int *argc, char ***argv)
+{
+ UNUSED(argc);
+ UNUSED(argv);
+
+ if (git_libgit2_init() < 0)
+ abort();
+
+ return 0;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ if (size) {
+ git_patch *patch = NULL;
+ git_patch_options opts = GIT_PATCH_OPTIONS_INIT;
+ opts.prefix_len = (uint32_t)data[0];
+ git_patch_from_buffer(&patch, (const char *)data + 1, size - 1,
+ &opts);
+ git_patch_free(patch);
+ }
+ return 0;
+}
diff --git a/fuzzers/standalone_driver.c b/fuzzers/standalone_driver.c
new file mode 100644
index 0000000..cd4f717
--- /dev/null
+++ b/fuzzers/standalone_driver.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include <stdio.h>
+
+#include "git2.h"
+#include "futils.h"
+#include "path.h"
+
+#include "standalone_driver.h"
+
+static int run_one_file(const char *filename)
+{
+ git_str buf = GIT_STR_INIT;
+ int error = 0;
+
+ if (git_futils_readbuffer(&buf, filename) < 0) {
+ fprintf(stderr, "Failed to read %s: %s\n", filename, git_error_last()->message);
+ error = -1;
+ goto exit;
+ }
+
+ LLVMFuzzerTestOneInput((const unsigned char *)buf.ptr, buf.size);
+exit:
+ git_str_dispose(&buf);
+ return error;
+}
+
+int main(int argc, char **argv)
+{
+ git_vector corpus_files = GIT_VECTOR_INIT;
+ char *filename = NULL;
+ unsigned i = 0;
+ int error = 0;
+
+ if (git_libgit2_init() < 0) {
+ fprintf(stderr, "Failed to initialize libgit2\n");
+ abort();
+ }
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: %s <corpus directory>\n", argv[0]);
+ error = -1;
+ goto exit;
+ }
+
+ fprintf(stderr, "Running %s against %s\n", argv[0], argv[1]);
+ LLVMFuzzerInitialize(&argc, &argv);
+
+ if (git_fs_path_dirload(&corpus_files, argv[1], 0, 0x0) < 0) {
+ fprintf(stderr, "Failed to scan corpus directory '%s': %s\n",
+ argv[1], git_error_last()->message);
+ error = -1;
+ goto exit;
+ }
+ git_vector_foreach(&corpus_files, i, filename) {
+ fprintf(stderr, "\tRunning %s...\n", filename);
+ if (run_one_file(filename) < 0) {
+ error = -1;
+ goto exit;
+ }
+ }
+ fprintf(stderr, "Done %d runs\n", i);
+
+exit:
+ git_vector_free_deep(&corpus_files);
+ git_libgit2_shutdown();
+ return error;
+}
diff --git a/fuzzers/standalone_driver.h b/fuzzers/standalone_driver.h
new file mode 100644
index 0000000..507fcb9
--- /dev/null
+++ b/fuzzers/standalone_driver.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_standalone_driver_h__
+#define INCLUDE_standalone_driver_h__
+
+extern int LLVMFuzzerTestOneInput(const unsigned char *data, size_t size);
+extern int LLVMFuzzerInitialize(int *argc, char ***argv);
+
+#endif
diff --git a/git.git-authors b/git.git-authors
new file mode 100644
index 0000000..d9a9114
--- /dev/null
+++ b/git.git-authors
@@ -0,0 +1,77 @@
+# This document lists the authors that have given voice to
+# their decision regarding relicensing the GPL'd code from
+# git.git to the GPL + gcc-exception license used by libgit2.
+#
+# Note that the permission is given for libgit2 use only. For
+# other uses, you must ask each of the contributors yourself.
+#
+# To show the owners of a file in git.git, one can run the
+# following command:
+#
+# git blame -C -C -M -- file | \
+# sed -e 's/[^(]*(\([^0-9]*\).*/\1/' -e 's/[\t ]*$//' | \
+# sort | uniq -c | sort -nr
+#
+# If everyone on the list that produces are on the list in
+# the recently added file "git.git-authors", it *should* be
+# safe to include that code in libgit2, but make sure to
+# read the file to ensure the code doesn't originate from
+# somewhere else.
+#
+# The format of this list is
+# "ok/no/ask/???"<tab>"Author"<space>"<email>"
+#
+# "ok" means the author consents to relicensing all their
+# contributed code (possibly with some exceptions)
+# "no" means the author does not consent
+# "ask" means that the contributor wants to give/withhold
+# their consent on a patch-by-patch basis.
+# "???" means the person is a prominent contributor who has
+# not yet made their standpoint clear.
+#
+# Please try to keep the list alphabetically ordered. It will
+# help in case we get all 600-ish git.git authors on it.
+#
+# (Paul Kocher is the author of the mozilla-sha1 implementation
+# but has otherwise not contributed to git.)
+#
+ok Adam Simpkins <adam@adamsimpkins.net> (http transport)
+ok Adrian Johnson <ajohnson@redneon.com>
+ok Alexey Shumkin <alex.crezoff@gmail.com>
+ok Andreas Ericsson <ae@op5.se>
+ok Antoine Pelisse <apelisse@gmail.com>
+ok Boyd Lynn Gerber <gerberb@zenez.com>
+ok Brandon Casey <drafnel@gmail.com>
+ok Brian Downing <bdowning@lavos.net>
+ok Brian Gernhardt <benji@silverinsanity.com>
+ok Christian Couder <chriscool@tuxfamily.org>
+ok Daniel Barkalow <barkalow@iabervon.org>
+ok Elijah Newren <newren@gmail.com>
+ok Florian Forster <octo@verplant.org>
+ok Gustaf Hendeby <hendeby@isy.liu.se>
+ok Holger Weiss <holger@zedat.fu-berlin.de>
+ok Jeff King <peff@peff.net>
+ok Johannes Schindelin <Johannes.Schindelin@gmx.de>
+ok Johannes Sixt <j6t@kdbg.org>
+ask Jonathan Nieder <jrnieder@gmail.com>
+ok Jonathan Tan <jonathantanmy@google.com>
+ok Junio C Hamano <gitster@pobox.com>
+ok Kristian Høgsberg <krh@redhat.com>
+ok Linus Torvalds <torvalds@linux-foundation.org>
+ok Lukas Sandström <lukass@etek.chalmers.se>
+ok Matthieu Moy <Matthieu.Moy@imag.fr>
+ok Michael Haggerty <mhagger@alum.mit.edu>
+ok Nicolas Pitre <nico@fluxnic.net> <nico@cam.org>
+ok Paolo Bonzini <bonzini@gnu.org>
+ok Paul Kocher <paul@cryptography.com>
+ok Peter Hagervall <hager@cs.umu.se>
+ok Petr Onderka <gsvick@gmail.com>
+ok Pierre Habouzit <madcoder@debian.org>
+ok Pieter de Bie <pdebie@ai.rug.nl>
+ok René Scharfe <rene.scharfe@lsrfire.ath.cx>
+ok Sebastian Schuberth <sschuberth@gmail.com>
+ok Shawn O. Pearce <spearce@spearce.org>
+ok Steffen Prohaska <prohaska@zib.de>
+ok Sven Verdoolaege <skimo@kotnet.org>
+ask Thomas Rast <tr@thomasrast.ch> (ok before 6-Oct-2013)
+ok Torsten Bögershausen <tboegi@web.de>
diff --git a/include/git2.h b/include/git2.h
new file mode 100644
index 0000000..3457e5f
--- /dev/null
+++ b/include/git2.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_git_git_h__
+#define INCLUDE_git_git_h__
+
+#include "git2/annotated_commit.h"
+#include "git2/apply.h"
+#include "git2/attr.h"
+#include "git2/blob.h"
+#include "git2/blame.h"
+#include "git2/branch.h"
+#include "git2/buffer.h"
+#include "git2/cert.h"
+#include "git2/checkout.h"
+#include "git2/cherrypick.h"
+#include "git2/clone.h"
+#include "git2/commit.h"
+#include "git2/common.h"
+#include "git2/config.h"
+#include "git2/credential.h"
+#include "git2/deprecated.h"
+#include "git2/describe.h"
+#include "git2/diff.h"
+#include "git2/email.h"
+#include "git2/errors.h"
+#include "git2/experimental.h"
+#include "git2/filter.h"
+#include "git2/global.h"
+#include "git2/graph.h"
+#include "git2/ignore.h"
+#include "git2/index.h"
+#include "git2/indexer.h"
+#include "git2/mailmap.h"
+#include "git2/merge.h"
+#include "git2/message.h"
+#include "git2/net.h"
+#include "git2/notes.h"
+#include "git2/object.h"
+#include "git2/odb.h"
+#include "git2/odb_backend.h"
+#include "git2/oid.h"
+#include "git2/pack.h"
+#include "git2/patch.h"
+#include "git2/pathspec.h"
+#include "git2/proxy.h"
+#include "git2/rebase.h"
+#include "git2/refdb.h"
+#include "git2/reflog.h"
+#include "git2/refs.h"
+#include "git2/refspec.h"
+#include "git2/remote.h"
+#include "git2/repository.h"
+#include "git2/reset.h"
+#include "git2/revert.h"
+#include "git2/revparse.h"
+#include "git2/revwalk.h"
+#include "git2/signature.h"
+#include "git2/stash.h"
+#include "git2/status.h"
+#include "git2/submodule.h"
+#include "git2/tag.h"
+#include "git2/transport.h"
+#include "git2/transaction.h"
+#include "git2/tree.h"
+#include "git2/types.h"
+#include "git2/version.h"
+#include "git2/worktree.h"
+
+#endif
diff --git a/include/git2/annotated_commit.h b/include/git2/annotated_commit.h
new file mode 100644
index 0000000..3b7103f
--- /dev/null
+++ b/include/git2/annotated_commit.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_annotated_commit_h__
+#define INCLUDE_git_annotated_commit_h__
+
+#include "common.h"
+#include "repository.h"
+#include "types.h"
+
+/**
+ * @file git2/annotated_commit.h
+ * @brief Git annotated commit routines
+ * @defgroup git_annotated_commit Git annotated commit routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Creates a `git_annotated_commit` from the given reference.
+ * The resulting git_annotated_commit must be freed with
+ * `git_annotated_commit_free`.
+ *
+ * @param out pointer to store the git_annotated_commit result in
+ * @param repo repository that contains the given reference
+ * @param ref reference to use to lookup the git_annotated_commit
+ * @return 0 on success or error code
+ */
+GIT_EXTERN(int) git_annotated_commit_from_ref(
+ git_annotated_commit **out,
+ git_repository *repo,
+ const git_reference *ref);
+
+/**
+ * Creates a `git_annotated_commit` from the given fetch head data.
+ * The resulting git_annotated_commit must be freed with
+ * `git_annotated_commit_free`.
+ *
+ * @param out pointer to store the git_annotated_commit result in
+ * @param repo repository that contains the given commit
+ * @param branch_name name of the (remote) branch
+ * @param remote_url url of the remote
+ * @param id the commit object id of the remote branch
+ * @return 0 on success or error code
+ */
+GIT_EXTERN(int) git_annotated_commit_from_fetchhead(
+ git_annotated_commit **out,
+ git_repository *repo,
+ const char *branch_name,
+ const char *remote_url,
+ const git_oid *id);
+
+/**
+ * Creates a `git_annotated_commit` from the given commit id.
+ * The resulting git_annotated_commit must be freed with
+ * `git_annotated_commit_free`.
+ *
+ * An annotated commit contains information about how it was
+ * looked up, which may be useful for functions like merge or
+ * rebase to provide context to the operation. For example,
+ * conflict files will include the name of the source or target
+ * branches being merged. It is therefore preferable to use the
+ * most specific function (eg `git_annotated_commit_from_ref`)
+ * instead of this one when that data is known.
+ *
+ * @param out pointer to store the git_annotated_commit result in
+ * @param repo repository that contains the given commit
+ * @param id the commit object id to lookup
+ * @return 0 on success or error code
+ */
+GIT_EXTERN(int) git_annotated_commit_lookup(
+ git_annotated_commit **out,
+ git_repository *repo,
+ const git_oid *id);
+
+/**
+ * Creates a `git_annotated_commit` from a revision string.
+ *
+ * See `man gitrevisions`, or
+ * http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions for
+ * information on the syntax accepted.
+ *
+ * @param out pointer to store the git_annotated_commit result in
+ * @param repo repository that contains the given commit
+ * @param revspec the extended sha syntax string to use to lookup the commit
+ * @return 0 on success or error code
+ */
+GIT_EXTERN(int) git_annotated_commit_from_revspec(
+ git_annotated_commit **out,
+ git_repository *repo,
+ const char *revspec);
+
+/**
+ * Gets the commit ID that the given `git_annotated_commit` refers to.
+ *
+ * @param commit the given annotated commit
+ * @return commit id
+ */
+GIT_EXTERN(const git_oid *) git_annotated_commit_id(
+ const git_annotated_commit *commit);
+
+/**
+ * Get the refname that the given `git_annotated_commit` refers to.
+ *
+ * @param commit the given annotated commit
+ * @return ref name.
+ */
+GIT_EXTERN(const char *) git_annotated_commit_ref(
+ const git_annotated_commit *commit);
+
+/**
+ * Frees a `git_annotated_commit`.
+ *
+ * @param commit annotated commit to free
+ */
+GIT_EXTERN(void) git_annotated_commit_free(
+ git_annotated_commit *commit);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/apply.h b/include/git2/apply.h
new file mode 100644
index 0000000..db652bd
--- /dev/null
+++ b/include/git2/apply.h
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_apply_h__
+#define INCLUDE_git_apply_h__
+
+#include "common.h"
+#include "types.h"
+#include "oid.h"
+#include "diff.h"
+
+/**
+ * @file git2/apply.h
+ * @brief Git patch application routines
+ * @defgroup git_apply Git patch application routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * When applying a patch, callback that will be made per delta (file).
+ *
+ * When the callback:
+ * - returns < 0, the apply process will be aborted.
+ * - returns > 0, the delta will not be applied, but the apply process
+ * continues
+ * - returns 0, the delta is applied, and the apply process continues.
+ *
+ * @param delta The delta to be applied
+ * @param payload User-specified payload
+ * @return 0 if the delta is applied, < 0 if the apply process will be aborted
+ * or > 0 if the delta will not be applied.
+ */
+typedef int GIT_CALLBACK(git_apply_delta_cb)(
+ const git_diff_delta *delta,
+ void *payload);
+
+/**
+ * When applying a patch, callback that will be made per hunk.
+ *
+ * When the callback:
+ * - returns < 0, the apply process will be aborted.
+ * - returns > 0, the hunk will not be applied, but the apply process
+ * continues
+ * - returns 0, the hunk is applied, and the apply process continues.
+ *
+ * @param hunk The hunk to be applied
+ * @param payload User-specified payload
+ * @return 0 if the hunk is applied, < 0 if the apply process will be aborted
+ * or > 0 if the hunk will not be applied.
+ */
+typedef int GIT_CALLBACK(git_apply_hunk_cb)(
+ const git_diff_hunk *hunk,
+ void *payload);
+
+/** Flags controlling the behavior of git_apply */
+typedef enum {
+ /**
+ * Don't actually make changes, just test that the patch applies.
+ * This is the equivalent of `git apply --check`.
+ */
+ GIT_APPLY_CHECK = (1 << 0)
+} git_apply_flags_t;
+
+/**
+ * Apply options structure
+ *
+ * Initialize with `GIT_APPLY_OPTIONS_INIT`. Alternatively, you can
+ * use `git_apply_options_init`.
+ *
+ * @see git_apply_to_tree, git_apply
+ */
+typedef struct {
+ unsigned int version; /**< The version */
+
+ /** When applying a patch, callback that will be made per delta (file). */
+ git_apply_delta_cb delta_cb;
+
+ /** When applying a patch, callback that will be made per hunk. */
+ git_apply_hunk_cb hunk_cb;
+
+ /** Payload passed to both delta_cb & hunk_cb. */
+ void *payload;
+
+ /** Bitmask of git_apply_flags_t */
+ unsigned int flags;
+} git_apply_options;
+
+#define GIT_APPLY_OPTIONS_VERSION 1
+#define GIT_APPLY_OPTIONS_INIT {GIT_APPLY_OPTIONS_VERSION}
+
+/**
+ * Initialize git_apply_options structure
+ *
+ * Initialize a `git_apply_options` with default values. Equivalent to creating
+ * an instance with GIT_APPLY_OPTIONS_INIT.
+ *
+ * @param opts The `git_apply_options` struct to initialize.
+ * @param version The struct version; pass `GIT_APPLY_OPTIONS_VERSION`
+ * @return 0 on success or -1 on failure.
+ */
+GIT_EXTERN(int) git_apply_options_init(git_apply_options *opts, unsigned int version);
+
+/**
+ * Apply a `git_diff` to a `git_tree`, and return the resulting image
+ * as an index.
+ *
+ * @param out the postimage of the application
+ * @param repo the repository to apply
+ * @param preimage the tree to apply the diff to
+ * @param diff the diff to apply
+ * @param options the options for the apply (or null for defaults)
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_apply_to_tree(
+ git_index **out,
+ git_repository *repo,
+ git_tree *preimage,
+ git_diff *diff,
+ const git_apply_options *options);
+
+/** Possible application locations for git_apply */
+typedef enum {
+ /**
+ * Apply the patch to the workdir, leaving the index untouched.
+ * This is the equivalent of `git apply` with no location argument.
+ */
+ GIT_APPLY_LOCATION_WORKDIR = 0,
+
+ /**
+ * Apply the patch to the index, leaving the working directory
+ * untouched. This is the equivalent of `git apply --cached`.
+ */
+ GIT_APPLY_LOCATION_INDEX = 1,
+
+ /**
+ * Apply the patch to both the working directory and the index.
+ * This is the equivalent of `git apply --index`.
+ */
+ GIT_APPLY_LOCATION_BOTH = 2
+} git_apply_location_t;
+
+/**
+ * Apply a `git_diff` to the given repository, making changes directly
+ * in the working directory, the index, or both.
+ *
+ * @param repo the repository to apply to
+ * @param diff the diff to apply
+ * @param location the location to apply (workdir, index or both)
+ * @param options the options for the apply (or null for defaults)
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_apply(
+ git_repository *repo,
+ git_diff *diff,
+ git_apply_location_t location,
+ const git_apply_options *options);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/attr.h b/include/git2/attr.h
new file mode 100644
index 0000000..0c83872
--- /dev/null
+++ b/include/git2/attr.h
@@ -0,0 +1,370 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_attr_h__
+#define INCLUDE_git_attr_h__
+
+#include "common.h"
+#include "types.h"
+
+/**
+ * @file git2/attr.h
+ * @brief Git attribute management routines
+ * @defgroup git_attr Git attribute management routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * GIT_ATTR_TRUE checks if an attribute is set on. In core git
+ * parlance, this the value for "Set" attributes.
+ *
+ * For example, if the attribute file contains:
+ *
+ * *.c foo
+ *
+ * Then for file `xyz.c` looking up attribute "foo" gives a value for
+ * which `GIT_ATTR_TRUE(value)` is true.
+ */
+#define GIT_ATTR_IS_TRUE(attr) (git_attr_value(attr) == GIT_ATTR_VALUE_TRUE)
+
+/**
+ * GIT_ATTR_FALSE checks if an attribute is set off. In core git
+ * parlance, this is the value for attributes that are "Unset" (not to
+ * be confused with values that a "Unspecified").
+ *
+ * For example, if the attribute file contains:
+ *
+ * *.h -foo
+ *
+ * Then for file `zyx.h` looking up attribute "foo" gives a value for
+ * which `GIT_ATTR_FALSE(value)` is true.
+ */
+#define GIT_ATTR_IS_FALSE(attr) (git_attr_value(attr) == GIT_ATTR_VALUE_FALSE)
+
+/**
+ * GIT_ATTR_UNSPECIFIED checks if an attribute is unspecified. This
+ * may be due to the attribute not being mentioned at all or because
+ * the attribute was explicitly set unspecified via the `!` operator.
+ *
+ * For example, if the attribute file contains:
+ *
+ * *.c foo
+ * *.h -foo
+ * onefile.c !foo
+ *
+ * Then for `onefile.c` looking up attribute "foo" yields a value with
+ * `GIT_ATTR_UNSPECIFIED(value)` of true. Also, looking up "foo" on
+ * file `onefile.rb` or looking up "bar" on any file will all give
+ * `GIT_ATTR_UNSPECIFIED(value)` of true.
+ */
+#define GIT_ATTR_IS_UNSPECIFIED(attr) (git_attr_value(attr) == GIT_ATTR_VALUE_UNSPECIFIED)
+
+/**
+ * GIT_ATTR_HAS_VALUE checks if an attribute is set to a value (as
+ * opposed to TRUE, FALSE or UNSPECIFIED). This would be the case if
+ * for a file with something like:
+ *
+ * *.txt eol=lf
+ *
+ * Given this, looking up "eol" for `onefile.txt` will give back the
+ * string "lf" and `GIT_ATTR_SET_TO_VALUE(attr)` will return true.
+ */
+#define GIT_ATTR_HAS_VALUE(attr) (git_attr_value(attr) == GIT_ATTR_VALUE_STRING)
+
+/**
+ * Possible states for an attribute
+ */
+typedef enum {
+ GIT_ATTR_VALUE_UNSPECIFIED = 0, /**< The attribute has been left unspecified */
+ GIT_ATTR_VALUE_TRUE, /**< The attribute has been set */
+ GIT_ATTR_VALUE_FALSE, /**< The attribute has been unset */
+ GIT_ATTR_VALUE_STRING /**< This attribute has a value */
+} git_attr_value_t;
+
+/**
+ * Return the value type for a given attribute.
+ *
+ * This can be either `TRUE`, `FALSE`, `UNSPECIFIED` (if the attribute
+ * was not set at all), or `VALUE`, if the attribute was set to an
+ * actual string.
+ *
+ * If the attribute has a `VALUE` string, it can be accessed normally
+ * as a NULL-terminated C string.
+ *
+ * @param attr The attribute
+ * @return the value type for the attribute
+ */
+GIT_EXTERN(git_attr_value_t) git_attr_value(const char *attr);
+
+/**
+ * Check attribute flags: Reading values from index and working directory.
+ *
+ * When checking attributes, it is possible to check attribute files
+ * in both the working directory (if there is one) and the index (if
+ * there is one). You can explicitly choose where to check and in
+ * which order using the following flags.
+ *
+ * Core git usually checks the working directory then the index,
+ * except during a checkout when it checks the index first. It will
+ * use index only for creating archives or for a bare repo (if an
+ * index has been specified for the bare repo).
+ */
+#define GIT_ATTR_CHECK_FILE_THEN_INDEX 0
+#define GIT_ATTR_CHECK_INDEX_THEN_FILE 1
+#define GIT_ATTR_CHECK_INDEX_ONLY 2
+
+/**
+ * Check attribute flags: controlling extended attribute behavior.
+ *
+ * Normally, attribute checks include looking in the /etc (or system
+ * equivalent) directory for a `gitattributes` file. Passing this
+ * flag will cause attribute checks to ignore that file.
+ * equivalent) directory for a `gitattributes` file. Passing the
+ * `GIT_ATTR_CHECK_NO_SYSTEM` flag will cause attribute checks to
+ * ignore that file.
+ *
+ * Passing the `GIT_ATTR_CHECK_INCLUDE_HEAD` flag will use attributes
+ * from a `.gitattributes` file in the repository at the HEAD revision.
+ *
+ * Passing the `GIT_ATTR_CHECK_INCLUDE_COMMIT` flag will use attributes
+ * from a `.gitattributes` file in a specific commit.
+ */
+#define GIT_ATTR_CHECK_NO_SYSTEM (1 << 2)
+#define GIT_ATTR_CHECK_INCLUDE_HEAD (1 << 3)
+#define GIT_ATTR_CHECK_INCLUDE_COMMIT (1 << 4)
+
+/**
+* An options structure for querying attributes.
+*/
+typedef struct {
+ unsigned int version;
+
+ /** A combination of GIT_ATTR_CHECK flags */
+ unsigned int flags;
+
+#ifdef GIT_DEPRECATE_HARD
+ void *reserved;
+#else
+ git_oid *commit_id;
+#endif
+
+ /**
+ * The commit to load attributes from, when
+ * `GIT_ATTR_CHECK_INCLUDE_COMMIT` is specified.
+ */
+ git_oid attr_commit_id;
+} git_attr_options;
+
+#define GIT_ATTR_OPTIONS_VERSION 1
+#define GIT_ATTR_OPTIONS_INIT {GIT_ATTR_OPTIONS_VERSION}
+
+/**
+ * Look up the value of one git attribute for path.
+ *
+ * @param value_out Output of the value of the attribute. Use the GIT_ATTR_...
+ * macros to test for TRUE, FALSE, UNSPECIFIED, etc. or just
+ * use the string value for attributes set to a value. You
+ * should NOT modify or free this value.
+ * @param repo The repository containing the path.
+ * @param flags A combination of GIT_ATTR_CHECK... flags.
+ * @param path The path to check for attributes. Relative paths are
+ * interpreted relative to the repo root. The file does
+ * not have to exist, but if it does not, then it will be
+ * treated as a plain file (not a directory).
+ * @param name The name of the attribute to look up.
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_attr_get(
+ const char **value_out,
+ git_repository *repo,
+ uint32_t flags,
+ const char *path,
+ const char *name);
+
+/**
+ * Look up the value of one git attribute for path with extended options.
+ *
+ * @param value_out Output of the value of the attribute. Use the GIT_ATTR_...
+ * macros to test for TRUE, FALSE, UNSPECIFIED, etc. or just
+ * use the string value for attributes set to a value. You
+ * should NOT modify or free this value.
+ * @param repo The repository containing the path.
+ * @param opts The `git_attr_options` to use when querying these attributes.
+ * @param path The path to check for attributes. Relative paths are
+ * interpreted relative to the repo root. The file does
+ * not have to exist, but if it does not, then it will be
+ * treated as a plain file (not a directory).
+ * @param name The name of the attribute to look up.
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_attr_get_ext(
+ const char **value_out,
+ git_repository *repo,
+ git_attr_options *opts,
+ const char *path,
+ const char *name);
+
+/**
+ * Look up a list of git attributes for path.
+ *
+ * Use this if you have a known list of attributes that you want to
+ * look up in a single call. This is somewhat more efficient than
+ * calling `git_attr_get()` multiple times.
+ *
+ * For example, you might write:
+ *
+ * const char *attrs[] = { "crlf", "diff", "foo" };
+ * const char **values[3];
+ * git_attr_get_many(values, repo, 0, "my/fun/file.c", 3, attrs);
+ *
+ * Then you could loop through the 3 values to get the settings for
+ * the three attributes you asked about.
+ *
+ * @param values_out An array of num_attr entries that will have string
+ * pointers written into it for the values of the attributes.
+ * You should not modify or free the values that are written
+ * into this array (although of course, you should free the
+ * array itself if you allocated it).
+ * @param repo The repository containing the path.
+ * @param flags A combination of GIT_ATTR_CHECK... flags.
+ * @param path The path inside the repo to check attributes. This
+ * does not have to exist, but if it does not, then
+ * it will be treated as a plain file (i.e. not a directory).
+ * @param num_attr The number of attributes being looked up
+ * @param names An array of num_attr strings containing attribute names.
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_attr_get_many(
+ const char **values_out,
+ git_repository *repo,
+ uint32_t flags,
+ const char *path,
+ size_t num_attr,
+ const char **names);
+
+/**
+ * Look up a list of git attributes for path with extended options.
+ *
+ * @param values_out An array of num_attr entries that will have string
+ * pointers written into it for the values of the attributes.
+ * You should not modify or free the values that are written
+ * into this array (although of course, you should free the
+ * array itself if you allocated it).
+ * @param repo The repository containing the path.
+ * @param opts The `git_attr_options` to use when querying these attributes.
+ * @param path The path inside the repo to check attributes. This
+ * does not have to exist, but if it does not, then
+ * it will be treated as a plain file (i.e. not a directory).
+ * @param num_attr The number of attributes being looked up
+ * @param names An array of num_attr strings containing attribute names.
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_attr_get_many_ext(
+ const char **values_out,
+ git_repository *repo,
+ git_attr_options *opts,
+ const char *path,
+ size_t num_attr,
+ const char **names);
+
+/**
+ * The callback used with git_attr_foreach.
+ *
+ * This callback will be invoked only once per attribute name, even if there
+ * are multiple rules for a given file. The highest priority rule will be
+ * used.
+ *
+ * @see git_attr_foreach.
+ *
+ * @param name The attribute name.
+ * @param value The attribute value. May be NULL if the attribute is explicitly
+ * set to UNSPECIFIED using the '!' sign.
+ * @param payload A user-specified pointer.
+ * @return 0 to continue looping, non-zero to stop. This value will be returned
+ * from git_attr_foreach.
+ */
+typedef int GIT_CALLBACK(git_attr_foreach_cb)(const char *name, const char *value, void *payload);
+
+/**
+ * Loop over all the git attributes for a path.
+ *
+ * @param repo The repository containing the path.
+ * @param flags A combination of GIT_ATTR_CHECK... flags.
+ * @param path Path inside the repo to check attributes. This does not have
+ * to exist, but if it does not, then it will be treated as a
+ * plain file (i.e. not a directory).
+ * @param callback Function to invoke on each attribute name and value.
+ * See git_attr_foreach_cb.
+ * @param payload Passed on as extra parameter to callback function.
+ * @return 0 on success, non-zero callback return value, or error code
+ */
+GIT_EXTERN(int) git_attr_foreach(
+ git_repository *repo,
+ uint32_t flags,
+ const char *path,
+ git_attr_foreach_cb callback,
+ void *payload);
+
+/**
+ * Loop over all the git attributes for a path with extended options.
+ *
+ * @param repo The repository containing the path.
+ * @param opts The `git_attr_options` to use when querying these attributes.
+ * @param path Path inside the repo to check attributes. This does not have
+ * to exist, but if it does not, then it will be treated as a
+ * plain file (i.e. not a directory).
+ * @param callback Function to invoke on each attribute name and value.
+ * See git_attr_foreach_cb.
+ * @param payload Passed on as extra parameter to callback function.
+ * @return 0 on success, non-zero callback return value, or error code
+ */
+GIT_EXTERN(int) git_attr_foreach_ext(
+ git_repository *repo,
+ git_attr_options *opts,
+ const char *path,
+ git_attr_foreach_cb callback,
+ void *payload);
+
+/**
+ * Flush the gitattributes cache.
+ *
+ * Call this if you have reason to believe that the attributes files on
+ * disk no longer match the cached contents of memory. This will cause
+ * the attributes files to be reloaded the next time that an attribute
+ * access function is called.
+ *
+ * @param repo The repository containing the gitattributes cache
+ * @return 0 on success, or an error code
+ */
+GIT_EXTERN(int) git_attr_cache_flush(
+ git_repository *repo);
+
+/**
+ * Add a macro definition.
+ *
+ * Macros will automatically be loaded from the top level `.gitattributes`
+ * file of the repository (plus the built-in "binary" macro). This
+ * function allows you to add others. For example, to add the default
+ * macro, you would call:
+ *
+ * git_attr_add_macro(repo, "binary", "-diff -crlf");
+ *
+ * @param repo The repository to add the macro in.
+ * @param name The name of the macro.
+ * @param values The value for the macro.
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_attr_add_macro(
+ git_repository *repo,
+ const char *name,
+ const char *values);
+
+/** @} */
+GIT_END_DECL
+#endif
+
diff --git a/include/git2/blame.h b/include/git2/blame.h
new file mode 100644
index 0000000..cb961a5
--- /dev/null
+++ b/include/git2/blame.h
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_git_blame_h__
+#define INCLUDE_git_blame_h__
+
+#include "common.h"
+#include "oid.h"
+
+/**
+ * @file git2/blame.h
+ * @brief Git blame routines
+ * @defgroup git_blame Git blame routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Flags for indicating option behavior for git_blame APIs.
+ */
+typedef enum {
+ /** Normal blame, the default */
+ GIT_BLAME_NORMAL = 0,
+
+ /**
+ * Track lines that have moved within a file (like `git blame -M`).
+ *
+ * This is not yet implemented and reserved for future use.
+ */
+ GIT_BLAME_TRACK_COPIES_SAME_FILE = (1<<0),
+
+ /**
+ * Track lines that have moved across files in the same commit
+ * (like `git blame -C`).
+ *
+ * This is not yet implemented and reserved for future use.
+ */
+ GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES = (1<<1),
+
+ /**
+ * Track lines that have been copied from another file that exists
+ * in the same commit (like `git blame -CC`). Implies SAME_FILE.
+ *
+ * This is not yet implemented and reserved for future use.
+ */
+ GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES = (1<<2),
+
+ /**
+ * Track lines that have been copied from another file that exists in
+ * *any* commit (like `git blame -CCC`). Implies SAME_COMMIT_COPIES.
+ *
+ * This is not yet implemented and reserved for future use.
+ */
+ GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES = (1<<3),
+
+ /**
+ * Restrict the search of commits to those reachable following only
+ * the first parents.
+ */
+ GIT_BLAME_FIRST_PARENT = (1<<4),
+
+ /**
+ * Use mailmap file to map author and committer names and email
+ * addresses to canonical real names and email addresses. The
+ * mailmap will be read from the working directory, or HEAD in a
+ * bare repository.
+ */
+ GIT_BLAME_USE_MAILMAP = (1<<5),
+
+ /** Ignore whitespace differences */
+ GIT_BLAME_IGNORE_WHITESPACE = (1<<6)
+} git_blame_flag_t;
+
+/**
+ * Blame options structure
+ *
+ * Initialize with `GIT_BLAME_OPTIONS_INIT`. Alternatively, you can
+ * use `git_blame_options_init`.
+ *
+ */
+typedef struct git_blame_options {
+ unsigned int version;
+
+ /** A combination of `git_blame_flag_t` */
+ uint32_t flags;
+
+ /**
+ * The lower bound on the number of alphanumeric characters that
+ * must be detected as moving/copying within a file for it to
+ * associate those lines with the parent commit. The default value
+ * is 20.
+ *
+ * This value only takes effect if any of the `GIT_BLAME_TRACK_COPIES_*`
+ * flags are specified.
+ */
+ uint16_t min_match_characters;
+
+ /** The id of the newest commit to consider. The default is HEAD. */
+ git_oid newest_commit;
+
+ /**
+ * The id of the oldest commit to consider.
+ * The default is the first commit encountered with a NULL parent.
+ */
+ git_oid oldest_commit;
+
+ /**
+ * The first line in the file to blame.
+ * The default is 1 (line numbers start with 1).
+ */
+ size_t min_line;
+
+ /**
+ * The last line in the file to blame.
+ * The default is the last line of the file.
+ */
+ size_t max_line;
+} git_blame_options;
+
+#define GIT_BLAME_OPTIONS_VERSION 1
+#define GIT_BLAME_OPTIONS_INIT {GIT_BLAME_OPTIONS_VERSION}
+
+/**
+ * Initialize git_blame_options structure
+ *
+ * Initializes a `git_blame_options` with default values. Equivalent to creating
+ * an instance with GIT_BLAME_OPTIONS_INIT.
+ *
+ * @param opts The `git_blame_options` struct to initialize.
+ * @param version The struct version; pass `GIT_BLAME_OPTIONS_VERSION`.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_blame_options_init(
+ git_blame_options *opts,
+ unsigned int version);
+
+/**
+ * Structure that represents a blame hunk.
+ */
+typedef struct git_blame_hunk {
+ /**
+ * The number of lines in this hunk.
+ */
+ size_t lines_in_hunk;
+
+ /**
+ * The OID of the commit where this line was last changed.
+ */
+ git_oid final_commit_id;
+
+ /**
+ * The 1-based line number where this hunk begins, in the final version
+ * of the file.
+ */
+ size_t final_start_line_number;
+
+ /**
+ * The author of `final_commit_id`. If `GIT_BLAME_USE_MAILMAP` has been
+ * specified, it will contain the canonical real name and email address.
+ */
+ git_signature *final_signature;
+
+ /**
+ * The OID of the commit where this hunk was found.
+ * This will usually be the same as `final_commit_id`, except when
+ * `GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES` has been specified.
+ */
+ git_oid orig_commit_id;
+
+ /**
+ * The path to the file where this hunk originated, as of the commit
+ * specified by `orig_commit_id`.
+ */
+ const char *orig_path;
+
+ /**
+ * The 1-based line number where this hunk begins in the file named by
+ * `orig_path` in the commit specified by `orig_commit_id`.
+ */
+ size_t orig_start_line_number;
+
+ /**
+ * The author of `orig_commit_id`. If `GIT_BLAME_USE_MAILMAP` has been
+ * specified, it will contain the canonical real name and email address.
+ */
+ git_signature *orig_signature;
+
+ /**
+ * The 1 iff the hunk has been tracked to a boundary commit (the root,
+ * or the commit specified in git_blame_options.oldest_commit)
+ */
+ char boundary;
+} git_blame_hunk;
+
+
+/** Opaque structure to hold blame results */
+typedef struct git_blame git_blame;
+
+/**
+ * Gets the number of hunks that exist in the blame structure.
+ *
+ * @param blame The blame structure to query.
+ * @return The number of hunks.
+ */
+GIT_EXTERN(uint32_t) git_blame_get_hunk_count(git_blame *blame);
+
+/**
+ * Gets the blame hunk at the given index.
+ *
+ * @param blame the blame structure to query
+ * @param index index of the hunk to retrieve
+ * @return the hunk at the given index, or NULL on error
+ */
+GIT_EXTERN(const git_blame_hunk*) git_blame_get_hunk_byindex(
+ git_blame *blame,
+ uint32_t index);
+
+/**
+ * Gets the hunk that relates to the given line number in the newest commit.
+ *
+ * @param blame the blame structure to query
+ * @param lineno the (1-based) line number to find a hunk for
+ * @return the hunk that contains the given line, or NULL on error
+ */
+GIT_EXTERN(const git_blame_hunk*) git_blame_get_hunk_byline(
+ git_blame *blame,
+ size_t lineno);
+
+/**
+ * Get the blame for a single file.
+ *
+ * @param out pointer that will receive the blame object
+ * @param repo repository whose history is to be walked
+ * @param path path to file to consider
+ * @param options options for the blame operation. If NULL, this is treated as
+ * though GIT_BLAME_OPTIONS_INIT were passed.
+ * @return 0 on success, or an error code. (use git_error_last for information
+ * about the error.)
+ */
+GIT_EXTERN(int) git_blame_file(
+ git_blame **out,
+ git_repository *repo,
+ const char *path,
+ git_blame_options *options);
+
+
+/**
+ * Get blame data for a file that has been modified in memory. The `reference`
+ * parameter is a pre-calculated blame for the in-odb history of the file. This
+ * means that once a file blame is completed (which can be expensive), updating
+ * the buffer blame is very fast.
+ *
+ * Lines that differ between the buffer and the committed version are marked as
+ * having a zero OID for their final_commit_id.
+ *
+ * @param out pointer that will receive the resulting blame data
+ * @param reference cached blame from the history of the file (usually the output
+ * from git_blame_file)
+ * @param buffer the (possibly) modified contents of the file
+ * @param buffer_len number of valid bytes in the buffer
+ * @return 0 on success, or an error code. (use git_error_last for information
+ * about the error)
+ */
+GIT_EXTERN(int) git_blame_buffer(
+ git_blame **out,
+ git_blame *reference,
+ const char *buffer,
+ size_t buffer_len);
+
+/**
+ * Free memory allocated by git_blame_file or git_blame_buffer.
+ *
+ * @param blame the blame structure to free
+ */
+GIT_EXTERN(void) git_blame_free(git_blame *blame);
+
+/** @} */
+GIT_END_DECL
+#endif
+
diff --git a/include/git2/blob.h b/include/git2/blob.h
new file mode 100644
index 0000000..6db85f3
--- /dev/null
+++ b/include/git2/blob.h
@@ -0,0 +1,311 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_blob_h__
+#define INCLUDE_git_blob_h__
+
+#include "common.h"
+#include "types.h"
+#include "oid.h"
+#include "object.h"
+#include "buffer.h"
+
+/**
+ * @file git2/blob.h
+ * @brief Git blob load and write routines
+ * @defgroup git_blob Git blob load and write routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Lookup a blob object from a repository.
+ *
+ * @param blob pointer to the looked up blob
+ * @param repo the repo to use when locating the blob.
+ * @param id identity of the blob to locate.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_blob_lookup(git_blob **blob, git_repository *repo, const git_oid *id);
+
+/**
+ * Lookup a blob object from a repository,
+ * given a prefix of its identifier (short id).
+ *
+ * @see git_object_lookup_prefix
+ *
+ * @param blob pointer to the looked up blob
+ * @param repo the repo to use when locating the blob.
+ * @param id identity of the blob to locate.
+ * @param len the length of the short identifier
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_blob_lookup_prefix(git_blob **blob, git_repository *repo, const git_oid *id, size_t len);
+
+/**
+ * Close an open blob
+ *
+ * This is a wrapper around git_object_free()
+ *
+ * IMPORTANT:
+ * It *is* necessary to call this method when you stop
+ * using a blob. Failure to do so will cause a memory leak.
+ *
+ * @param blob the blob to close
+ */
+GIT_EXTERN(void) git_blob_free(git_blob *blob);
+
+/**
+ * Get the id of a blob.
+ *
+ * @param blob a previously loaded blob.
+ * @return SHA1 hash for this blob.
+ */
+GIT_EXTERN(const git_oid *) git_blob_id(const git_blob *blob);
+
+/**
+ * Get the repository that contains the blob.
+ *
+ * @param blob A previously loaded blob.
+ * @return Repository that contains this blob.
+ */
+GIT_EXTERN(git_repository *) git_blob_owner(const git_blob *blob);
+
+/**
+ * Get a read-only buffer with the raw content of a blob.
+ *
+ * A pointer to the raw content of a blob is returned;
+ * this pointer is owned internally by the object and shall
+ * not be free'd. The pointer may be invalidated at a later
+ * time.
+ *
+ * @param blob pointer to the blob
+ * @return the pointer, or NULL on error
+ */
+GIT_EXTERN(const void *) git_blob_rawcontent(const git_blob *blob);
+
+/**
+ * Get the size in bytes of the contents of a blob
+ *
+ * @param blob pointer to the blob
+ * @return size on bytes
+ */
+GIT_EXTERN(git_object_size_t) git_blob_rawsize(const git_blob *blob);
+
+/**
+ * Flags to control the functionality of `git_blob_filter`.
+ */
+typedef enum {
+ /** When set, filters will not be applied to binary files. */
+ GIT_BLOB_FILTER_CHECK_FOR_BINARY = (1 << 0),
+
+ /**
+ * When set, filters will not load configuration from the
+ * system-wide `gitattributes` in `/etc` (or system equivalent).
+ */
+ GIT_BLOB_FILTER_NO_SYSTEM_ATTRIBUTES = (1 << 1),
+
+ /**
+ * When set, filters will be loaded from a `.gitattributes` file
+ * in the HEAD commit.
+ */
+ GIT_BLOB_FILTER_ATTRIBUTES_FROM_HEAD = (1 << 2),
+
+ /**
+ * When set, filters will be loaded from a `.gitattributes` file
+ * in the specified commit.
+ */
+ GIT_BLOB_FILTER_ATTRIBUTES_FROM_COMMIT = (1 << 3)
+} git_blob_filter_flag_t;
+
+/**
+ * The options used when applying filter options to a file.
+ *
+ * Initialize with `GIT_BLOB_FILTER_OPTIONS_INIT`. Alternatively, you can
+ * use `git_blob_filter_options_init`.
+ *
+ */
+typedef struct {
+ int version;
+
+ /** Flags to control the filtering process, see `git_blob_filter_flag_t` above */
+ uint32_t flags;
+
+#ifdef GIT_DEPRECATE_HARD
+ void *reserved;
+#else
+ git_oid *commit_id;
+#endif
+
+ /**
+ * The commit to load attributes from, when
+ * `GIT_BLOB_FILTER_ATTRIBUTES_FROM_COMMIT` is specified.
+ */
+ git_oid attr_commit_id;
+} git_blob_filter_options;
+
+#define GIT_BLOB_FILTER_OPTIONS_VERSION 1
+#define GIT_BLOB_FILTER_OPTIONS_INIT {GIT_BLOB_FILTER_OPTIONS_VERSION, GIT_BLOB_FILTER_CHECK_FOR_BINARY}
+
+/**
+ * Initialize git_blob_filter_options structure
+ *
+ * Initializes a `git_blob_filter_options` with default values. Equivalent
+ * to creating an instance with `GIT_BLOB_FILTER_OPTIONS_INIT`.
+ *
+ * @param opts The `git_blob_filter_options` struct to initialize.
+ * @param version The struct version; pass `GIT_BLOB_FILTER_OPTIONS_VERSION`.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_blob_filter_options_init(git_blob_filter_options *opts, unsigned int version);
+
+/**
+ * Get a buffer with the filtered content of a blob.
+ *
+ * This applies filters as if the blob was being checked out to the
+ * working directory under the specified filename. This may apply
+ * CRLF filtering or other types of changes depending on the file
+ * attributes set for the blob and the content detected in it.
+ *
+ * The output is written into a `git_buf` which the caller must free
+ * when done (via `git_buf_dispose`).
+ *
+ * If no filters need to be applied, then the `out` buffer will just
+ * be populated with a pointer to the raw content of the blob. In
+ * that case, be careful to *not* free the blob until done with the
+ * buffer or copy it into memory you own.
+ *
+ * @param out The git_buf to be filled in
+ * @param blob Pointer to the blob
+ * @param as_path Path used for file attribute lookups, etc.
+ * @param opts Options to use for filtering the blob
+ * @return 0 on success or an error code
+ */
+GIT_EXTERN(int) git_blob_filter(
+ git_buf *out,
+ git_blob *blob,
+ const char *as_path,
+ git_blob_filter_options *opts);
+
+/**
+ * Read a file from the working folder of a repository
+ * and write it to the Object Database as a loose blob
+ *
+ * @param id return the id of the written blob
+ * @param repo repository where the blob will be written.
+ * this repository cannot be bare
+ * @param relative_path file from which the blob will be created,
+ * relative to the repository's working dir
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_blob_create_from_workdir(git_oid *id, git_repository *repo, const char *relative_path);
+
+/**
+ * Read a file from the filesystem and write its content
+ * to the Object Database as a loose blob
+ *
+ * @param id return the id of the written blob
+ * @param repo repository where the blob will be written.
+ * this repository can be bare or not
+ * @param path file from which the blob will be created
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_blob_create_from_disk(git_oid *id, git_repository *repo, const char *path);
+
+/**
+ * Create a stream to write a new blob into the object db
+ *
+ * This function may need to buffer the data on disk and will in
+ * general not be the right choice if you know the size of the data
+ * to write. If you have data in memory, use
+ * `git_blob_create_from_buffer()`. If you do not, but know the size of
+ * the contents (and don't want/need to perform filtering), use
+ * `git_odb_open_wstream()`.
+ *
+ * Don't close this stream yourself but pass it to
+ * `git_blob_create_from_stream_commit()` to commit the write to the
+ * object db and get the object id.
+ *
+ * If the `hintpath` parameter is filled, it will be used to determine
+ * what git filters should be applied to the object before it is written
+ * to the object database.
+ *
+ * @param out the stream into which to write
+ * @param repo Repository where the blob will be written.
+ * This repository can be bare or not.
+ * @param hintpath If not NULL, will be used to select data filters
+ * to apply onto the content of the blob to be created.
+ * @return 0 or error code
+ */
+GIT_EXTERN(int) git_blob_create_from_stream(
+ git_writestream **out,
+ git_repository *repo,
+ const char *hintpath);
+
+/**
+ * Close the stream and write the blob to the object db
+ *
+ * The stream will be closed and freed.
+ *
+ * @param out the id of the new blob
+ * @param stream the stream to close
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_blob_create_from_stream_commit(
+ git_oid *out,
+ git_writestream *stream);
+
+/**
+ * Write an in-memory buffer to the ODB as a blob
+ *
+ * @param id return the id of the written blob
+ * @param repo repository where the blob will be written
+ * @param buffer data to be written into the blob
+ * @param len length of the data
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_blob_create_from_buffer(
+ git_oid *id, git_repository *repo, const void *buffer, size_t len);
+
+/**
+ * Determine if the blob content is most certainly binary or not.
+ *
+ * The heuristic used to guess if a file is binary is taken from core git:
+ * Searching for NUL bytes and looking for a reasonable ratio of printable
+ * to non-printable characters among the first 8000 bytes.
+ *
+ * @param blob The blob which content should be analyzed
+ * @return 1 if the content of the blob is detected
+ * as binary; 0 otherwise.
+ */
+GIT_EXTERN(int) git_blob_is_binary(const git_blob *blob);
+
+/**
+ * Determine if the given content is most certainly binary or not;
+ * this is the same mechanism used by `git_blob_is_binary` but only
+ * looking at raw data.
+ *
+ * @param data The blob data which content should be analyzed
+ * @param len The length of the data
+ * @return 1 if the content of the blob is detected
+ * as binary; 0 otherwise.
+ */
+GIT_EXTERN(int) git_blob_data_is_binary(const char *data, size_t len);
+
+/**
+ * Create an in-memory copy of a blob. The copy must be explicitly
+ * free'd or it will leak.
+ *
+ * @param out Pointer to store the copy of the object
+ * @param source Original object to copy
+ * @return 0.
+ */
+GIT_EXTERN(int) git_blob_dup(git_blob **out, git_blob *source);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/branch.h b/include/git2/branch.h
new file mode 100644
index 0000000..27c6fa1
--- /dev/null
+++ b/include/git2/branch.h
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_branch_h__
+#define INCLUDE_git_branch_h__
+
+#include "common.h"
+#include "oid.h"
+#include "types.h"
+
+/**
+ * @file git2/branch.h
+ * @brief Git branch parsing routines
+ * @defgroup git_branch Git branch management
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Create a new branch pointing at a target commit
+ *
+ * A new direct reference will be created pointing to
+ * this target commit. If `force` is true and a reference
+ * already exists with the given name, it'll be replaced.
+ *
+ * The returned reference must be freed by the user.
+ *
+ * The branch name will be checked for validity.
+ * See `git_tag_create()` for rules about valid names.
+ *
+ * @param out Pointer where to store the underlying reference.
+ *
+ * @param repo the repository to create the branch in.
+ *
+ * @param branch_name Name for the branch; this name is
+ * validated for consistency. It should also not conflict with
+ * an already existing branch name.
+ *
+ * @param target Commit to which this branch should point. This object
+ * must belong to the given `repo`.
+ *
+ * @param force Overwrite existing branch.
+ *
+ * @return 0, GIT_EINVALIDSPEC or an error code.
+ * A proper reference is written in the refs/heads namespace
+ * pointing to the provided target commit.
+ */
+GIT_EXTERN(int) git_branch_create(
+ git_reference **out,
+ git_repository *repo,
+ const char *branch_name,
+ const git_commit *target,
+ int force);
+
+/**
+ * Create a new branch pointing at a target commit
+ *
+ * This behaves like `git_branch_create()` but takes an annotated
+ * commit, which lets you specify which extended sha syntax string was
+ * specified by a user, allowing for more exact reflog messages.
+ *
+ * See the documentation for `git_branch_create()`.
+ *
+ * @see git_branch_create
+ */
+GIT_EXTERN(int) git_branch_create_from_annotated(
+ git_reference **ref_out,
+ git_repository *repository,
+ const char *branch_name,
+ const git_annotated_commit *commit,
+ int force);
+
+/**
+ * Delete an existing branch reference.
+ *
+ * Note that if the deletion succeeds, the reference object will not
+ * be valid anymore, and should be freed immediately by the user using
+ * `git_reference_free()`.
+ *
+ * @param branch A valid reference representing a branch
+ * @return 0 on success, or an error code.
+ */
+GIT_EXTERN(int) git_branch_delete(git_reference *branch);
+
+/** Iterator type for branches */
+typedef struct git_branch_iterator git_branch_iterator;
+
+/**
+ * Create an iterator which loops over the requested branches.
+ *
+ * @param out the iterator
+ * @param repo Repository where to find the branches.
+ * @param list_flags Filtering flags for the branch
+ * listing. Valid values are GIT_BRANCH_LOCAL, GIT_BRANCH_REMOTE
+ * or GIT_BRANCH_ALL.
+ *
+ * @return 0 on success or an error code
+ */
+GIT_EXTERN(int) git_branch_iterator_new(
+ git_branch_iterator **out,
+ git_repository *repo,
+ git_branch_t list_flags);
+
+/**
+ * Retrieve the next branch from the iterator
+ *
+ * @param out the reference
+ * @param out_type the type of branch (local or remote-tracking)
+ * @param iter the branch iterator
+ * @return 0 on success, GIT_ITEROVER if there are no more branches or an error code.
+ */
+GIT_EXTERN(int) git_branch_next(git_reference **out, git_branch_t *out_type, git_branch_iterator *iter);
+
+/**
+ * Free a branch iterator
+ *
+ * @param iter the iterator to free
+ */
+GIT_EXTERN(void) git_branch_iterator_free(git_branch_iterator *iter);
+
+/**
+ * Move/rename an existing local branch reference.
+ *
+ * The new branch name will be checked for validity.
+ * See `git_tag_create()` for rules about valid names.
+ *
+ * Note that if the move succeeds, the old reference object will not
+ * be valid anymore, and should be freed immediately by the user using
+ * `git_reference_free()`.
+ *
+ * @param out New reference object for the updated name.
+ *
+ * @param branch Current underlying reference of the branch.
+ *
+ * @param new_branch_name Target name of the branch once the move
+ * is performed; this name is validated for consistency.
+ *
+ * @param force Overwrite existing branch.
+ *
+ * @return 0 on success, GIT_EINVALIDSPEC or an error code.
+ */
+GIT_EXTERN(int) git_branch_move(
+ git_reference **out,
+ git_reference *branch,
+ const char *new_branch_name,
+ int force);
+
+/**
+ * Lookup a branch by its name in a repository.
+ *
+ * The generated reference must be freed by the user.
+ * The branch name will be checked for validity.
+ *
+ * @see git_tag_create for rules about valid names.
+ *
+ * @param out pointer to the looked-up branch reference
+ * @param repo the repository to look up the branch
+ * @param branch_name Name of the branch to be looked-up;
+ * this name is validated for consistency.
+ * @param branch_type Type of the considered branch. This should
+ * be valued with either GIT_BRANCH_LOCAL or GIT_BRANCH_REMOTE.
+ *
+ * @return 0 on success; GIT_ENOTFOUND when no matching branch
+ * exists, GIT_EINVALIDSPEC, otherwise an error code.
+ */
+GIT_EXTERN(int) git_branch_lookup(
+ git_reference **out,
+ git_repository *repo,
+ const char *branch_name,
+ git_branch_t branch_type);
+
+/**
+ * Get the branch name
+ *
+ * Given a reference object, this will check that it really is a branch (ie.
+ * it lives under "refs/heads/" or "refs/remotes/"), and return the branch part
+ * of it.
+ *
+ * @param out Pointer to the abbreviated reference name.
+ * Owned by ref, do not free.
+ *
+ * @param ref A reference object, ideally pointing to a branch
+ *
+ * @return 0 on success; GIT_EINVALID if the reference isn't either a local or
+ * remote branch, otherwise an error code.
+ */
+GIT_EXTERN(int) git_branch_name(
+ const char **out,
+ const git_reference *ref);
+
+/**
+ * Get the upstream of a branch
+ *
+ * Given a reference, this will return a new reference object corresponding
+ * to its remote tracking branch. The reference must be a local branch.
+ *
+ * @see git_branch_upstream_name for details on the resolution.
+ *
+ * @param out Pointer where to store the retrieved reference.
+ * @param branch Current underlying reference of the branch.
+ *
+ * @return 0 on success; GIT_ENOTFOUND when no remote tracking
+ * reference exists, otherwise an error code.
+ */
+GIT_EXTERN(int) git_branch_upstream(
+ git_reference **out,
+ const git_reference *branch);
+
+/**
+ * Set a branch's upstream branch
+ *
+ * This will update the configuration to set the branch named `branch_name` as the upstream of `branch`.
+ * Pass a NULL name to unset the upstream information.
+ *
+ * @note the actual tracking reference must have been already created for the
+ * operation to succeed.
+ *
+ * @param branch the branch to configure
+ * @param branch_name remote-tracking or local branch to set as upstream.
+ *
+ * @return 0 on success; GIT_ENOTFOUND if there's no branch named `branch_name`
+ * or an error code
+ */
+GIT_EXTERN(int) git_branch_set_upstream(
+ git_reference *branch,
+ const char *branch_name);
+
+/**
+ * Get the upstream name of a branch
+ *
+ * Given a local branch, this will return its remote-tracking branch information,
+ * as a full reference name, ie. "feature/nice" would become
+ * "refs/remote/origin/feature/nice", depending on that branch's configuration.
+ *
+ * @param out the buffer into which the name will be written.
+ * @param repo the repository where the branches live.
+ * @param refname reference name of the local branch.
+ *
+ * @return 0 on success, GIT_ENOTFOUND when no remote tracking reference exists,
+ * or an error code.
+ */
+GIT_EXTERN(int) git_branch_upstream_name(
+ git_buf *out,
+ git_repository *repo,
+ const char *refname);
+
+/**
+ * Determine if HEAD points to the given branch
+ *
+ * @param branch A reference to a local branch.
+ *
+ * @return 1 if HEAD points at the branch, 0 if it isn't, or a negative value
+ * as an error code.
+ */
+GIT_EXTERN(int) git_branch_is_head(
+ const git_reference *branch);
+
+/**
+ * Determine if any HEAD points to the current branch
+ *
+ * This will iterate over all known linked repositories (usually in the form of
+ * worktrees) and report whether any HEAD is pointing at the current branch.
+ *
+ * @param branch A reference to a local branch.
+ *
+ * @return 1 if branch is checked out, 0 if it isn't, an error code otherwise.
+ */
+GIT_EXTERN(int) git_branch_is_checked_out(
+ const git_reference *branch);
+
+/**
+ * Find the remote name of a remote-tracking branch
+ *
+ * This will return the name of the remote whose fetch refspec is matching
+ * the given branch. E.g. given a branch "refs/remotes/test/master", it will
+ * extract the "test" part. If refspecs from multiple remotes match,
+ * the function will return GIT_EAMBIGUOUS.
+ *
+ * @param out The buffer into which the name will be written.
+ * @param repo The repository where the branch lives.
+ * @param refname complete name of the remote tracking branch.
+ *
+ * @return 0 on success, GIT_ENOTFOUND when no matching remote was found,
+ * GIT_EAMBIGUOUS when the branch maps to several remotes,
+ * otherwise an error code.
+ */
+GIT_EXTERN(int) git_branch_remote_name(
+ git_buf *out,
+ git_repository *repo,
+ const char *refname);
+
+/**
+ * Retrieve the upstream remote of a local branch
+ *
+ * This will return the currently configured "branch.*.remote" for a given
+ * branch. This branch must be local.
+ *
+ * @param buf the buffer into which to write the name
+ * @param repo the repository in which to look
+ * @param refname the full name of the branch
+ * @return 0 or an error code
+ */
+ GIT_EXTERN(int) git_branch_upstream_remote(git_buf *buf, git_repository *repo, const char *refname);
+
+/**
+ * Retrieve the upstream merge of a local branch
+ *
+ * This will return the currently configured "branch.*.merge" for a given
+ * branch. This branch must be local.
+ *
+ * @param buf the buffer into which to write the name
+ * @param repo the repository in which to look
+ * @param refname the full name of the branch
+ * @return 0 or an error code
+ */
+ GIT_EXTERN(int) git_branch_upstream_merge(git_buf *buf, git_repository *repo, const char *refname);
+
+/**
+ * Determine whether a branch name is valid, meaning that (when prefixed
+ * with `refs/heads/`) that it is a valid reference name, and that any
+ * additional branch name restrictions are imposed (eg, it cannot start
+ * with a `-`).
+ *
+ * @param valid output pointer to set with validity of given branch name
+ * @param name a branch name to test
+ * @return 0 on success or an error code
+ */
+GIT_EXTERN(int) git_branch_name_is_valid(int *valid, const char *name);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/buffer.h b/include/git2/buffer.h
new file mode 100644
index 0000000..9fa9720
--- /dev/null
+++ b/include/git2/buffer.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_buf_h__
+#define INCLUDE_git_buf_h__
+
+#include "common.h"
+
+/**
+ * @file git2/buffer.h
+ * @brief Buffer export structure
+ *
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * A data buffer for exporting data from libgit2
+ *
+ * Sometimes libgit2 wants to return an allocated data buffer to the
+ * caller and have the caller take responsibility for freeing that memory.
+ * To make ownership clear in these cases, libgit2 uses `git_buf` to
+ * return this data. Callers should use `git_buf_dispose()` to release
+ * the memory when they are done.
+ *
+ * A `git_buf` contains a pointer to a NUL-terminated C string, and
+ * the length of the string (not including the NUL terminator).
+ */
+typedef struct {
+ /**
+ * The buffer contents. `ptr` points to the start of the buffer
+ * being returned. The buffer's length (in bytes) is specified
+ * by the `size` member of the structure, and contains a NUL
+ * terminator at position `(size + 1)`.
+ */
+ char *ptr;
+
+ /**
+ * This field is reserved and unused.
+ */
+ size_t reserved;
+
+ /**
+ * The length (in bytes) of the buffer pointed to by `ptr`,
+ * not including a NUL terminator.
+ */
+ size_t size;
+} git_buf;
+
+/**
+ * Use to initialize a `git_buf` before passing it to a function that
+ * will populate it.
+ */
+#define GIT_BUF_INIT { NULL, 0, 0 }
+
+/**
+ * Free the memory referred to by the git_buf.
+ *
+ * Note that this does not free the `git_buf` itself, just the memory
+ * pointed to by `buffer->ptr`.
+ *
+ * @param buffer The buffer to deallocate
+ */
+GIT_EXTERN(void) git_buf_dispose(git_buf *buffer);
+
+GIT_END_DECL
+
+/** @} */
+
+#endif
diff --git a/include/git2/cert.h b/include/git2/cert.h
new file mode 100644
index 0000000..05213a5
--- /dev/null
+++ b/include/git2/cert.h
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_cert_h__
+#define INCLUDE_git_cert_h__
+
+#include "common.h"
+#include "types.h"
+
+/**
+ * @file git2/cert.h
+ * @brief Git certificate objects
+ * @defgroup git_cert Certificate objects
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Type of host certificate structure that is passed to the check callback
+ */
+typedef enum git_cert_t {
+ /**
+ * No information about the certificate is available. This may
+ * happen when using curl.
+ */
+ GIT_CERT_NONE,
+ /**
+ * The `data` argument to the callback will be a pointer to
+ * the DER-encoded data.
+ */
+ GIT_CERT_X509,
+ /**
+ * The `data` argument to the callback will be a pointer to a
+ * `git_cert_hostkey` structure.
+ */
+ GIT_CERT_HOSTKEY_LIBSSH2,
+ /**
+ * The `data` argument to the callback will be a pointer to a
+ * `git_strarray` with `name:content` strings containing
+ * information about the certificate. This is used when using
+ * curl.
+ */
+ GIT_CERT_STRARRAY
+} git_cert_t;
+
+/**
+ * Parent type for `git_cert_hostkey` and `git_cert_x509`.
+ */
+struct git_cert {
+ /**
+ * Type of certificate. A `GIT_CERT_` value.
+ */
+ git_cert_t cert_type;
+};
+
+/**
+ * Callback for the user's custom certificate checks.
+ *
+ * @param cert The host certificate
+ * @param valid Whether the libgit2 checks (OpenSSL or WinHTTP) think
+ * this certificate is valid
+ * @param host Hostname of the host libgit2 connected to
+ * @param payload Payload provided by the caller
+ * @return 0 to proceed with the connection, < 0 to fail the connection
+ * or > 0 to indicate that the callback refused to act and that
+ * the existing validity determination should be honored
+ */
+typedef int GIT_CALLBACK(git_transport_certificate_check_cb)(git_cert *cert, int valid, const char *host, void *payload);
+
+/**
+ * Type of SSH host fingerprint
+ */
+typedef enum {
+ /** MD5 is available */
+ GIT_CERT_SSH_MD5 = (1 << 0),
+ /** SHA-1 is available */
+ GIT_CERT_SSH_SHA1 = (1 << 1),
+ /** SHA-256 is available */
+ GIT_CERT_SSH_SHA256 = (1 << 2),
+ /** Raw hostkey is available */
+ GIT_CERT_SSH_RAW = (1 << 3)
+} git_cert_ssh_t;
+
+typedef enum {
+ /** The raw key is of an unknown type. */
+ GIT_CERT_SSH_RAW_TYPE_UNKNOWN = 0,
+ /** The raw key is an RSA key. */
+ GIT_CERT_SSH_RAW_TYPE_RSA = 1,
+ /** The raw key is a DSS key. */
+ GIT_CERT_SSH_RAW_TYPE_DSS = 2,
+ /** The raw key is a ECDSA 256 key. */
+ GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_256 = 3,
+ /** The raw key is a ECDSA 384 key. */
+ GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_384 = 4,
+ /** The raw key is a ECDSA 521 key. */
+ GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_521 = 5,
+ /** The raw key is a ED25519 key. */
+ GIT_CERT_SSH_RAW_TYPE_KEY_ED25519 = 6
+} git_cert_ssh_raw_type_t;
+
+/**
+ * Hostkey information taken from libssh2
+ */
+typedef struct {
+ git_cert parent; /**< The parent cert */
+
+ /**
+ * A bitmask containing the available fields.
+ */
+ git_cert_ssh_t type;
+
+ /**
+ * Hostkey hash. If `type` has `GIT_CERT_SSH_MD5` set, this will
+ * have the MD5 hash of the hostkey.
+ */
+ unsigned char hash_md5[16];
+
+ /**
+ * Hostkey hash. If `type` has `GIT_CERT_SSH_SHA1` set, this will
+ * have the SHA-1 hash of the hostkey.
+ */
+ unsigned char hash_sha1[20];
+
+ /**
+ * Hostkey hash. If `type` has `GIT_CERT_SSH_SHA256` set, this will
+ * have the SHA-256 hash of the hostkey.
+ */
+ unsigned char hash_sha256[32];
+
+ /**
+ * Raw hostkey type. If `type` has `GIT_CERT_SSH_RAW` set, this will
+ * have the type of the raw hostkey.
+ */
+ git_cert_ssh_raw_type_t raw_type;
+
+ /**
+ * Pointer to the raw hostkey. If `type` has `GIT_CERT_SSH_RAW` set,
+ * this will have the raw contents of the hostkey.
+ */
+ const char *hostkey;
+
+ /**
+ * Raw hostkey length. If `type` has `GIT_CERT_SSH_RAW` set, this will
+ * have the length of the raw contents of the hostkey.
+ */
+ size_t hostkey_len;
+} git_cert_hostkey;
+
+/**
+ * X.509 certificate information
+ */
+typedef struct {
+ git_cert parent; /**< The parent cert */
+
+ /**
+ * Pointer to the X.509 certificate data
+ */
+ void *data;
+
+ /**
+ * Length of the memory block pointed to by `data`.
+ */
+ size_t len;
+} git_cert_x509;
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/checkout.h b/include/git2/checkout.h
new file mode 100644
index 0000000..9f83411
--- /dev/null
+++ b/include/git2/checkout.h
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_checkout_h__
+#define INCLUDE_git_checkout_h__
+
+#include "common.h"
+#include "types.h"
+#include "diff.h"
+
+/**
+ * @file git2/checkout.h
+ * @brief Git checkout routines
+ * @defgroup git_checkout Git checkout routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Checkout behavior flags
+ *
+ * In libgit2, checkout is used to update the working directory and index
+ * to match a target tree. Unlike git checkout, it does not move the HEAD
+ * commit for you - use `git_repository_set_head` or the like to do that.
+ *
+ * Checkout looks at (up to) four things: the "target" tree you want to
+ * check out, the "baseline" tree of what was checked out previously, the
+ * working directory for actual files, and the index for staged changes.
+ *
+ * You give checkout one of three strategies for update:
+ *
+ * - `GIT_CHECKOUT_NONE` is a dry-run strategy that checks for conflicts,
+ * etc., but doesn't make any actual changes.
+ *
+ * - `GIT_CHECKOUT_FORCE` is at the opposite extreme, taking any action to
+ * make the working directory match the target (including potentially
+ * discarding modified files).
+ *
+ * - `GIT_CHECKOUT_SAFE` is between these two options, it will only make
+ * modifications that will not lose changes.
+ *
+ * | target == baseline | target != baseline |
+ * ---------------------|-----------------------|----------------------|
+ * workdir == baseline | no action | create, update, or |
+ * | | delete file |
+ * ---------------------|-----------------------|----------------------|
+ * workdir exists and | no action | conflict (notify |
+ * is != baseline | notify dirty MODIFIED | and cancel checkout) |
+ * ---------------------|-----------------------|----------------------|
+ * workdir missing, | notify dirty DELETED | create file |
+ * baseline present | | |
+ * ---------------------|-----------------------|----------------------|
+ *
+ * To emulate `git checkout`, use `GIT_CHECKOUT_SAFE` with a checkout
+ * notification callback (see below) that displays information about dirty
+ * files. The default behavior will cancel checkout on conflicts.
+ *
+ * To emulate `git checkout-index`, use `GIT_CHECKOUT_SAFE` with a
+ * notification callback that cancels the operation if a dirty-but-existing
+ * file is found in the working directory. This core git command isn't
+ * quite "force" but is sensitive about some types of changes.
+ *
+ * To emulate `git checkout -f`, use `GIT_CHECKOUT_FORCE`.
+ *
+ *
+ * There are some additional flags to modify the behavior of checkout:
+ *
+ * - GIT_CHECKOUT_ALLOW_CONFLICTS makes SAFE mode apply safe file updates
+ * even if there are conflicts (instead of cancelling the checkout).
+ *
+ * - GIT_CHECKOUT_REMOVE_UNTRACKED means remove untracked files (i.e. not
+ * in target, baseline, or index, and not ignored) from the working dir.
+ *
+ * - GIT_CHECKOUT_REMOVE_IGNORED means remove ignored files (that are also
+ * untracked) from the working directory as well.
+ *
+ * - GIT_CHECKOUT_UPDATE_ONLY means to only update the content of files that
+ * already exist. Files will not be created nor deleted. This just skips
+ * applying adds, deletes, and typechanges.
+ *
+ * - GIT_CHECKOUT_DONT_UPDATE_INDEX prevents checkout from writing the
+ * updated files' information to the index.
+ *
+ * - Normally, checkout will reload the index and git attributes from disk
+ * before any operations. GIT_CHECKOUT_NO_REFRESH prevents this reload.
+ *
+ * - Unmerged index entries are conflicts. GIT_CHECKOUT_SKIP_UNMERGED skips
+ * files with unmerged index entries instead. GIT_CHECKOUT_USE_OURS and
+ * GIT_CHECKOUT_USE_THEIRS to proceed with the checkout using either the
+ * stage 2 ("ours") or stage 3 ("theirs") version of files in the index.
+ *
+ * - GIT_CHECKOUT_DONT_OVERWRITE_IGNORED prevents ignored files from being
+ * overwritten. Normally, files that are ignored in the working directory
+ * are not considered "precious" and may be overwritten if the checkout
+ * target contains that file.
+ *
+ * - GIT_CHECKOUT_DONT_REMOVE_EXISTING prevents checkout from removing
+ * files or folders that fold to the same name on case insensitive
+ * filesystems. This can cause files to retain their existing names
+ * and write through existing symbolic links.
+ */
+typedef enum {
+ GIT_CHECKOUT_NONE = 0, /**< default is a dry run, no actual updates */
+
+ /**
+ * Allow safe updates that cannot overwrite uncommitted data.
+ * If the uncommitted changes don't conflict with the checked out files,
+ * the checkout will still proceed, leaving the changes intact.
+ *
+ * Mutually exclusive with GIT_CHECKOUT_FORCE.
+ * GIT_CHECKOUT_FORCE takes precedence over GIT_CHECKOUT_SAFE.
+ */
+ GIT_CHECKOUT_SAFE = (1u << 0),
+
+ /**
+ * Allow all updates to force working directory to look like index.
+ *
+ * Mutually exclusive with GIT_CHECKOUT_SAFE.
+ * GIT_CHECKOUT_FORCE takes precedence over GIT_CHECKOUT_SAFE.
+ */
+ GIT_CHECKOUT_FORCE = (1u << 1),
+
+
+ /** Allow checkout to recreate missing files */
+ GIT_CHECKOUT_RECREATE_MISSING = (1u << 2),
+
+ /** Allow checkout to make safe updates even if conflicts are found */
+ GIT_CHECKOUT_ALLOW_CONFLICTS = (1u << 4),
+
+ /** Remove untracked files not in index (that are not ignored) */
+ GIT_CHECKOUT_REMOVE_UNTRACKED = (1u << 5),
+
+ /** Remove ignored files not in index */
+ GIT_CHECKOUT_REMOVE_IGNORED = (1u << 6),
+
+ /** Only update existing files, don't create new ones */
+ GIT_CHECKOUT_UPDATE_ONLY = (1u << 7),
+
+ /**
+ * Normally checkout updates index entries as it goes; this stops that.
+ * Implies `GIT_CHECKOUT_DONT_WRITE_INDEX`.
+ */
+ GIT_CHECKOUT_DONT_UPDATE_INDEX = (1u << 8),
+
+ /** Don't refresh index/config/etc before doing checkout */
+ GIT_CHECKOUT_NO_REFRESH = (1u << 9),
+
+ /** Allow checkout to skip unmerged files */
+ GIT_CHECKOUT_SKIP_UNMERGED = (1u << 10),
+ /** For unmerged files, checkout stage 2 from index */
+ GIT_CHECKOUT_USE_OURS = (1u << 11),
+ /** For unmerged files, checkout stage 3 from index */
+ GIT_CHECKOUT_USE_THEIRS = (1u << 12),
+
+ /** Treat pathspec as simple list of exact match file paths */
+ GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH = (1u << 13),
+
+ /** Ignore directories in use, they will be left empty */
+ GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES = (1u << 18),
+
+ /** Don't overwrite ignored files that exist in the checkout target */
+ GIT_CHECKOUT_DONT_OVERWRITE_IGNORED = (1u << 19),
+
+ /** Write normal merge files for conflicts */
+ GIT_CHECKOUT_CONFLICT_STYLE_MERGE = (1u << 20),
+
+ /** Include common ancestor data in diff3 format files for conflicts */
+ GIT_CHECKOUT_CONFLICT_STYLE_DIFF3 = (1u << 21),
+
+ /** Don't overwrite existing files or folders */
+ GIT_CHECKOUT_DONT_REMOVE_EXISTING = (1u << 22),
+
+ /** Normally checkout writes the index upon completion; this prevents that. */
+ GIT_CHECKOUT_DONT_WRITE_INDEX = (1u << 23),
+
+ /**
+ * Show what would be done by a checkout. Stop after sending
+ * notifications; don't update the working directory or index.
+ */
+ GIT_CHECKOUT_DRY_RUN = (1u << 24),
+
+ /** Include common ancestor data in zdiff3 format for conflicts */
+ GIT_CHECKOUT_CONFLICT_STYLE_ZDIFF3 = (1u << 25),
+
+ /**
+ * THE FOLLOWING OPTIONS ARE NOT YET IMPLEMENTED
+ */
+
+ /** Recursively checkout submodules with same options (NOT IMPLEMENTED) */
+ GIT_CHECKOUT_UPDATE_SUBMODULES = (1u << 16),
+ /** Recursively checkout submodules if HEAD moved in super repo (NOT IMPLEMENTED) */
+ GIT_CHECKOUT_UPDATE_SUBMODULES_IF_CHANGED = (1u << 17)
+
+} git_checkout_strategy_t;
+
+/**
+ * Checkout notification flags
+ *
+ * Checkout will invoke an options notification callback (`notify_cb`) for
+ * certain cases - you pick which ones via `notify_flags`:
+ *
+ * Returning a non-zero value from this callback will cancel the checkout.
+ * The non-zero return value will be propagated back and returned by the
+ * git_checkout_... call.
+ *
+ * Notification callbacks are made prior to modifying any files on disk,
+ * so canceling on any notification will still happen prior to any files
+ * being modified.
+ */
+typedef enum {
+ GIT_CHECKOUT_NOTIFY_NONE = 0,
+
+ /**
+ * Invokes checkout on conflicting paths.
+ */
+ GIT_CHECKOUT_NOTIFY_CONFLICT = (1u << 0),
+
+ /**
+ * Notifies about "dirty" files, i.e. those that do not need an update
+ * but no longer match the baseline. Core git displays these files when
+ * checkout runs, but won't stop the checkout.
+ */
+ GIT_CHECKOUT_NOTIFY_DIRTY = (1u << 1),
+
+ /**
+ * Sends notification for any file changed.
+ */
+ GIT_CHECKOUT_NOTIFY_UPDATED = (1u << 2),
+
+ /**
+ * Notifies about untracked files.
+ */
+ GIT_CHECKOUT_NOTIFY_UNTRACKED = (1u << 3),
+
+ /**
+ * Notifies about ignored files.
+ */
+ GIT_CHECKOUT_NOTIFY_IGNORED = (1u << 4),
+
+ GIT_CHECKOUT_NOTIFY_ALL = 0x0FFFFu
+} git_checkout_notify_t;
+
+/** Checkout performance-reporting structure */
+typedef struct {
+ size_t mkdir_calls;
+ size_t stat_calls;
+ size_t chmod_calls;
+} git_checkout_perfdata;
+
+/** Checkout notification callback function */
+typedef int GIT_CALLBACK(git_checkout_notify_cb)(
+ git_checkout_notify_t why,
+ const char *path,
+ const git_diff_file *baseline,
+ const git_diff_file *target,
+ const git_diff_file *workdir,
+ void *payload);
+
+/** Checkout progress notification function */
+typedef void GIT_CALLBACK(git_checkout_progress_cb)(
+ const char *path,
+ size_t completed_steps,
+ size_t total_steps,
+ void *payload);
+
+/** Checkout perfdata notification function */
+typedef void GIT_CALLBACK(git_checkout_perfdata_cb)(
+ const git_checkout_perfdata *perfdata,
+ void *payload);
+
+/**
+ * Checkout options structure
+ *
+ * Initialize with `GIT_CHECKOUT_OPTIONS_INIT`. Alternatively, you can
+ * use `git_checkout_options_init`.
+ *
+ */
+typedef struct git_checkout_options {
+ unsigned int version; /**< The version */
+
+ unsigned int checkout_strategy; /**< default will be a safe checkout */
+
+ int disable_filters; /**< don't apply filters like CRLF conversion */
+ unsigned int dir_mode; /**< default is 0755 */
+ unsigned int file_mode; /**< default is 0644 or 0755 as dictated by blob */
+ int file_open_flags; /**< default is O_CREAT | O_TRUNC | O_WRONLY */
+
+ unsigned int notify_flags; /**< see `git_checkout_notify_t` above */
+
+ /**
+ * Optional callback to get notifications on specific file states.
+ * @see git_checkout_notify_t
+ */
+ git_checkout_notify_cb notify_cb;
+
+ /** Payload passed to notify_cb */
+ void *notify_payload;
+
+ /** Optional callback to notify the consumer of checkout progress. */
+ git_checkout_progress_cb progress_cb;
+
+ /** Payload passed to progress_cb */
+ void *progress_payload;
+
+ /**
+ * A list of wildmatch patterns or paths.
+ *
+ * By default, all paths are processed. If you pass an array of wildmatch
+ * patterns, those will be used to filter which paths should be taken into
+ * account.
+ *
+ * Use GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH to treat as a simple list.
+ */
+ git_strarray paths;
+
+ /**
+ * The expected content of the working directory; defaults to HEAD.
+ *
+ * If the working directory does not match this baseline information,
+ * that will produce a checkout conflict.
+ */
+ git_tree *baseline;
+
+ /**
+ * Like `baseline` above, though expressed as an index. This
+ * option overrides `baseline`.
+ */
+ git_index *baseline_index;
+
+ const char *target_directory; /**< alternative checkout path to workdir */
+
+ const char *ancestor_label; /**< the name of the common ancestor side of conflicts */
+ const char *our_label; /**< the name of the "our" side of conflicts */
+ const char *their_label; /**< the name of the "their" side of conflicts */
+
+ /** Optional callback to notify the consumer of performance data. */
+ git_checkout_perfdata_cb perfdata_cb;
+
+ /** Payload passed to perfdata_cb */
+ void *perfdata_payload;
+} git_checkout_options;
+
+#define GIT_CHECKOUT_OPTIONS_VERSION 1
+#define GIT_CHECKOUT_OPTIONS_INIT {GIT_CHECKOUT_OPTIONS_VERSION, GIT_CHECKOUT_SAFE}
+
+/**
+ * Initialize git_checkout_options structure
+ *
+ * Initializes a `git_checkout_options` with default values. Equivalent to creating
+ * an instance with GIT_CHECKOUT_OPTIONS_INIT.
+ *
+ * @param opts The `git_checkout_options` struct to initialize.
+ * @param version The struct version; pass `GIT_CHECKOUT_OPTIONS_VERSION`.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_checkout_options_init(
+ git_checkout_options *opts,
+ unsigned int version);
+
+/**
+ * Updates files in the index and the working tree to match the content of
+ * the commit pointed at by HEAD.
+ *
+ * Note that this is _not_ the correct mechanism used to switch branches;
+ * do not change your `HEAD` and then call this method, that would leave
+ * you with checkout conflicts since your working directory would then
+ * appear to be dirty. Instead, checkout the target of the branch and
+ * then update `HEAD` using `git_repository_set_head` to point to the
+ * branch you checked out.
+ *
+ * @param repo repository to check out (must be non-bare)
+ * @param opts specifies checkout options (may be NULL)
+ * @return 0 on success, GIT_EUNBORNBRANCH if HEAD points to a non
+ * existing branch, non-zero value returned by `notify_cb`, or
+ * other error code < 0 (use git_error_last for error details)
+ */
+GIT_EXTERN(int) git_checkout_head(
+ git_repository *repo,
+ const git_checkout_options *opts);
+
+/**
+ * Updates files in the working tree to match the content of the index.
+ *
+ * @param repo repository into which to check out (must be non-bare)
+ * @param index index to be checked out (or NULL to use repository index)
+ * @param opts specifies checkout options (may be NULL)
+ * @return 0 on success, non-zero return value from `notify_cb`, or error
+ * code < 0 (use git_error_last for error details)
+ */
+GIT_EXTERN(int) git_checkout_index(
+ git_repository *repo,
+ git_index *index,
+ const git_checkout_options *opts);
+
+/**
+ * Updates files in the index and working tree to match the content of the
+ * tree pointed at by the treeish.
+ *
+ * @param repo repository to check out (must be non-bare)
+ * @param treeish a commit, tag or tree which content will be used to update
+ * the working directory (or NULL to use HEAD)
+ * @param opts specifies checkout options (may be NULL)
+ * @return 0 on success, non-zero return value from `notify_cb`, or error
+ * code < 0 (use git_error_last for error details)
+ */
+GIT_EXTERN(int) git_checkout_tree(
+ git_repository *repo,
+ const git_object *treeish,
+ const git_checkout_options *opts);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/cherrypick.h b/include/git2/cherrypick.h
new file mode 100644
index 0000000..0e6a252
--- /dev/null
+++ b/include/git2/cherrypick.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_cherrypick_h__
+#define INCLUDE_git_cherrypick_h__
+
+#include "common.h"
+#include "types.h"
+#include "merge.h"
+
+/**
+ * @file git2/cherrypick.h
+ * @brief Git cherry-pick routines
+ * @defgroup git_cherrypick Git cherry-pick routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Cherry-pick options
+ */
+typedef struct {
+ unsigned int version;
+
+ /** For merge commits, the "mainline" is treated as the parent. */
+ unsigned int mainline;
+
+ git_merge_options merge_opts; /**< Options for the merging */
+ git_checkout_options checkout_opts; /**< Options for the checkout */
+} git_cherrypick_options;
+
+#define GIT_CHERRYPICK_OPTIONS_VERSION 1
+#define GIT_CHERRYPICK_OPTIONS_INIT {GIT_CHERRYPICK_OPTIONS_VERSION, 0, GIT_MERGE_OPTIONS_INIT, GIT_CHECKOUT_OPTIONS_INIT}
+
+/**
+ * Initialize git_cherrypick_options structure
+ *
+ * Initializes a `git_cherrypick_options` with default values. Equivalent to creating
+ * an instance with GIT_CHERRYPICK_OPTIONS_INIT.
+ *
+ * @param opts The `git_cherrypick_options` struct to initialize.
+ * @param version The struct version; pass `GIT_CHERRYPICK_OPTIONS_VERSION`.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_cherrypick_options_init(
+ git_cherrypick_options *opts,
+ unsigned int version);
+
+/**
+ * Cherry-picks the given commit against the given "our" commit, producing an
+ * index that reflects the result of the cherry-pick.
+ *
+ * The returned index must be freed explicitly with `git_index_free`.
+ *
+ * @param out pointer to store the index result in
+ * @param repo the repository that contains the given commits
+ * @param cherrypick_commit the commit to cherry-pick
+ * @param our_commit the commit to cherry-pick against (eg, HEAD)
+ * @param mainline the parent of the `cherrypick_commit`, if it is a merge
+ * @param merge_options the merge options (or null for defaults)
+ * @return zero on success, -1 on failure.
+ */
+GIT_EXTERN(int) git_cherrypick_commit(
+ git_index **out,
+ git_repository *repo,
+ git_commit *cherrypick_commit,
+ git_commit *our_commit,
+ unsigned int mainline,
+ const git_merge_options *merge_options);
+
+/**
+ * Cherry-pick the given commit, producing changes in the index and working directory.
+ *
+ * @param repo the repository to cherry-pick
+ * @param commit the commit to cherry-pick
+ * @param cherrypick_options the cherry-pick options (or null for defaults)
+ * @return zero on success, -1 on failure.
+ */
+GIT_EXTERN(int) git_cherrypick(
+ git_repository *repo,
+ git_commit *commit,
+ const git_cherrypick_options *cherrypick_options);
+
+/** @} */
+GIT_END_DECL
+
+#endif
+
diff --git a/include/git2/clone.h b/include/git2/clone.h
new file mode 100644
index 0000000..3481f25
--- /dev/null
+++ b/include/git2/clone.h
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_clone_h__
+#define INCLUDE_git_clone_h__
+
+#include "common.h"
+#include "types.h"
+#include "indexer.h"
+#include "checkout.h"
+#include "remote.h"
+#include "transport.h"
+
+
+/**
+ * @file git2/clone.h
+ * @brief Git cloning routines
+ * @defgroup git_clone Git cloning routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Options for bypassing the git-aware transport on clone. Bypassing
+ * it means that instead of a fetch, libgit2 will copy the object
+ * database directory instead of figuring out what it needs, which is
+ * faster. If possible, it will hardlink the files to save space.
+ */
+typedef enum {
+ /**
+ * Auto-detect (default), libgit2 will bypass the git-aware
+ * transport for local paths, but use a normal fetch for
+ * `file://` urls.
+ */
+ GIT_CLONE_LOCAL_AUTO,
+ /**
+ * Bypass the git-aware transport even for a `file://` url.
+ */
+ GIT_CLONE_LOCAL,
+ /**
+ * Do no bypass the git-aware transport
+ */
+ GIT_CLONE_NO_LOCAL,
+ /**
+ * Bypass the git-aware transport, but do not try to use
+ * hardlinks.
+ */
+ GIT_CLONE_LOCAL_NO_LINKS
+} git_clone_local_t;
+
+/**
+ * The signature of a function matching git_remote_create, with an additional
+ * void* as a callback payload.
+ *
+ * Callers of git_clone may provide a function matching this signature to override
+ * the remote creation and customization process during a clone operation.
+ *
+ * @param out the resulting remote
+ * @param repo the repository in which to create the remote
+ * @param name the remote's name
+ * @param url the remote's url
+ * @param payload an opaque payload
+ * @return 0, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code
+ */
+typedef int GIT_CALLBACK(git_remote_create_cb)(
+ git_remote **out,
+ git_repository *repo,
+ const char *name,
+ const char *url,
+ void *payload);
+
+/**
+ * The signature of a function matching git_repository_init, with an
+ * additional void * as callback payload.
+ *
+ * Callers of git_clone my provide a function matching this signature
+ * to override the repository creation and customization process
+ * during a clone operation.
+ *
+ * @param out the resulting repository
+ * @param path path in which to create the repository
+ * @param bare whether the repository is bare. This is the value from the clone options
+ * @param payload payload specified by the options
+ * @return 0, or a negative value to indicate error
+ */
+typedef int GIT_CALLBACK(git_repository_create_cb)(
+ git_repository **out,
+ const char *path,
+ int bare,
+ void *payload);
+
+/**
+ * Clone options structure
+ *
+ * Initialize with `GIT_CLONE_OPTIONS_INIT`. Alternatively, you can
+ * use `git_clone_options_init`.
+ *
+ */
+typedef struct git_clone_options {
+ unsigned int version;
+
+ /**
+ * These options are passed to the checkout step. To disable
+ * checkout, set the `checkout_strategy` to
+ * `GIT_CHECKOUT_NONE`.
+ */
+ git_checkout_options checkout_opts;
+
+ /**
+ * Options which control the fetch, including callbacks.
+ *
+ * The callbacks are used for reporting fetch progress, and for acquiring
+ * credentials in the event they are needed.
+ */
+ git_fetch_options fetch_opts;
+
+ /**
+ * Set to zero (false) to create a standard repo, or non-zero
+ * for a bare repo
+ */
+ int bare;
+
+ /**
+ * Whether to use a fetch or copy the object database.
+ */
+ git_clone_local_t local;
+
+ /**
+ * The name of the branch to checkout. NULL means use the
+ * remote's default branch.
+ */
+ const char *checkout_branch;
+
+ /**
+ * A callback used to create the new repository into which to
+ * clone. If NULL, the 'bare' field will be used to determine
+ * whether to create a bare repository.
+ */
+ git_repository_create_cb repository_cb;
+
+ /**
+ * An opaque payload to pass to the git_repository creation callback.
+ * This parameter is ignored unless repository_cb is non-NULL.
+ */
+ void *repository_cb_payload;
+
+ /**
+ * A callback used to create the git_remote, prior to its being
+ * used to perform the clone operation. See the documentation for
+ * git_remote_create_cb for details. This parameter may be NULL,
+ * indicating that git_clone should provide default behavior.
+ */
+ git_remote_create_cb remote_cb;
+
+ /**
+ * An opaque payload to pass to the git_remote creation callback.
+ * This parameter is ignored unless remote_cb is non-NULL.
+ */
+ void *remote_cb_payload;
+} git_clone_options;
+
+#define GIT_CLONE_OPTIONS_VERSION 1
+#define GIT_CLONE_OPTIONS_INIT { GIT_CLONE_OPTIONS_VERSION, \
+ { GIT_CHECKOUT_OPTIONS_VERSION, GIT_CHECKOUT_SAFE }, \
+ GIT_FETCH_OPTIONS_INIT }
+
+/**
+ * Initialize git_clone_options structure
+ *
+ * Initializes a `git_clone_options` with default values. Equivalent to creating
+ * an instance with GIT_CLONE_OPTIONS_INIT.
+ *
+ * @param opts The `git_clone_options` struct to initialize.
+ * @param version The struct version; pass `GIT_CLONE_OPTIONS_VERSION`.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_clone_options_init(
+ git_clone_options *opts,
+ unsigned int version);
+
+/**
+ * Clone a remote repository.
+ *
+ * By default this creates its repository and initial remote to match
+ * git's defaults. You can use the options in the callback to
+ * customize how these are created.
+ *
+ * @param out pointer that will receive the resulting repository object
+ * @param url the remote repository to clone
+ * @param local_path local directory to clone to
+ * @param options configuration options for the clone. If NULL, the
+ * function works as though GIT_OPTIONS_INIT were passed.
+ * @return 0 on success, any non-zero return value from a callback
+ * function, or a negative value to indicate an error (use
+ * `git_error_last` for a detailed error message)
+ */
+GIT_EXTERN(int) git_clone(
+ git_repository **out,
+ const char *url,
+ const char *local_path,
+ const git_clone_options *options);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/commit.h b/include/git2/commit.h
new file mode 100644
index 0000000..67170cb
--- /dev/null
+++ b/include/git2/commit.h
@@ -0,0 +1,546 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_commit_h__
+#define INCLUDE_git_commit_h__
+
+#include "common.h"
+#include "types.h"
+#include "oid.h"
+#include "object.h"
+
+/**
+ * @file git2/commit.h
+ * @brief Git commit parsing, formatting routines
+ * @defgroup git_commit Git commit parsing, formatting routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Lookup a commit object from a repository.
+ *
+ * The returned object should be released with `git_commit_free` when no
+ * longer needed.
+ *
+ * @param commit pointer to the looked up commit
+ * @param repo the repo to use when locating the commit.
+ * @param id identity of the commit to locate. If the object is
+ * an annotated tag it will be peeled back to the commit.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_commit_lookup(
+ git_commit **commit, git_repository *repo, const git_oid *id);
+
+/**
+ * Lookup a commit object from a repository, given a prefix of its
+ * identifier (short id).
+ *
+ * The returned object should be released with `git_commit_free` when no
+ * longer needed.
+ *
+ * @see git_object_lookup_prefix
+ *
+ * @param commit pointer to the looked up commit
+ * @param repo the repo to use when locating the commit.
+ * @param id identity of the commit to locate. If the object is
+ * an annotated tag it will be peeled back to the commit.
+ * @param len the length of the short identifier
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_commit_lookup_prefix(
+ git_commit **commit, git_repository *repo, const git_oid *id, size_t len);
+
+/**
+ * Close an open commit
+ *
+ * This is a wrapper around git_object_free()
+ *
+ * IMPORTANT:
+ * It *is* necessary to call this method when you stop
+ * using a commit. Failure to do so will cause a memory leak.
+ *
+ * @param commit the commit to close
+ */
+
+GIT_EXTERN(void) git_commit_free(git_commit *commit);
+
+/**
+ * Get the id of a commit.
+ *
+ * @param commit a previously loaded commit.
+ * @return object identity for the commit.
+ */
+GIT_EXTERN(const git_oid *) git_commit_id(const git_commit *commit);
+
+/**
+ * Get the repository that contains the commit.
+ *
+ * @param commit A previously loaded commit.
+ * @return Repository that contains this commit.
+ */
+GIT_EXTERN(git_repository *) git_commit_owner(const git_commit *commit);
+
+/**
+ * Get the encoding for the message of a commit,
+ * as a string representing a standard encoding name.
+ *
+ * The encoding may be NULL if the `encoding` header
+ * in the commit is missing; in that case UTF-8 is assumed.
+ *
+ * @param commit a previously loaded commit.
+ * @return NULL, or the encoding
+ */
+GIT_EXTERN(const char *) git_commit_message_encoding(const git_commit *commit);
+
+/**
+ * Get the full message of a commit.
+ *
+ * The returned message will be slightly prettified by removing any
+ * potential leading newlines.
+ *
+ * @param commit a previously loaded commit.
+ * @return the message of a commit
+ */
+GIT_EXTERN(const char *) git_commit_message(const git_commit *commit);
+
+/**
+ * Get the full raw message of a commit.
+ *
+ * @param commit a previously loaded commit.
+ * @return the raw message of a commit
+ */
+GIT_EXTERN(const char *) git_commit_message_raw(const git_commit *commit);
+
+/**
+ * Get the short "summary" of the git commit message.
+ *
+ * The returned message is the summary of the commit, comprising the
+ * first paragraph of the message with whitespace trimmed and squashed.
+ *
+ * @param commit a previously loaded commit.
+ * @return the summary of a commit or NULL on error
+ */
+GIT_EXTERN(const char *) git_commit_summary(git_commit *commit);
+
+/**
+ * Get the long "body" of the git commit message.
+ *
+ * The returned message is the body of the commit, comprising
+ * everything but the first paragraph of the message. Leading and
+ * trailing whitespaces are trimmed.
+ *
+ * @param commit a previously loaded commit.
+ * @return the body of a commit or NULL when no the message only
+ * consists of a summary
+ */
+GIT_EXTERN(const char *) git_commit_body(git_commit *commit);
+
+/**
+ * Get the commit time (i.e. committer time) of a commit.
+ *
+ * @param commit a previously loaded commit.
+ * @return the time of a commit
+ */
+GIT_EXTERN(git_time_t) git_commit_time(const git_commit *commit);
+
+/**
+ * Get the commit timezone offset (i.e. committer's preferred timezone) of a commit.
+ *
+ * @param commit a previously loaded commit.
+ * @return positive or negative timezone offset, in minutes from UTC
+ */
+GIT_EXTERN(int) git_commit_time_offset(const git_commit *commit);
+
+/**
+ * Get the committer of a commit.
+ *
+ * @param commit a previously loaded commit.
+ * @return the committer of a commit
+ */
+GIT_EXTERN(const git_signature *) git_commit_committer(const git_commit *commit);
+
+/**
+ * Get the author of a commit.
+ *
+ * @param commit a previously loaded commit.
+ * @return the author of a commit
+ */
+GIT_EXTERN(const git_signature *) git_commit_author(const git_commit *commit);
+
+/**
+ * Get the committer of a commit, using the mailmap to map names and email
+ * addresses to canonical real names and email addresses.
+ *
+ * Call `git_signature_free` to free the signature.
+ *
+ * @param out a pointer to store the resolved signature.
+ * @param commit a previously loaded commit.
+ * @param mailmap the mailmap to resolve with. (may be NULL)
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_commit_committer_with_mailmap(
+ git_signature **out, const git_commit *commit, const git_mailmap *mailmap);
+
+/**
+ * Get the author of a commit, using the mailmap to map names and email
+ * addresses to canonical real names and email addresses.
+ *
+ * Call `git_signature_free` to free the signature.
+ *
+ * @param out a pointer to store the resolved signature.
+ * @param commit a previously loaded commit.
+ * @param mailmap the mailmap to resolve with. (may be NULL)
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_commit_author_with_mailmap(
+ git_signature **out, const git_commit *commit, const git_mailmap *mailmap);
+
+/**
+ * Get the full raw text of the commit header.
+ *
+ * @param commit a previously loaded commit
+ * @return the header text of the commit
+ */
+GIT_EXTERN(const char *) git_commit_raw_header(const git_commit *commit);
+
+/**
+ * Get the tree pointed to by a commit.
+ *
+ * @param tree_out pointer where to store the tree object
+ * @param commit a previously loaded commit.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_commit_tree(git_tree **tree_out, const git_commit *commit);
+
+/**
+ * Get the id of the tree pointed to by a commit. This differs from
+ * `git_commit_tree` in that no attempts are made to fetch an object
+ * from the ODB.
+ *
+ * @param commit a previously loaded commit.
+ * @return the id of tree pointed to by commit.
+ */
+GIT_EXTERN(const git_oid *) git_commit_tree_id(const git_commit *commit);
+
+/**
+ * Get the number of parents of this commit
+ *
+ * @param commit a previously loaded commit.
+ * @return integer of count of parents
+ */
+GIT_EXTERN(unsigned int) git_commit_parentcount(const git_commit *commit);
+
+/**
+ * Get the specified parent of the commit.
+ *
+ * @param out Pointer where to store the parent commit
+ * @param commit a previously loaded commit.
+ * @param n the position of the parent (from 0 to `parentcount`)
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_commit_parent(
+ git_commit **out,
+ const git_commit *commit,
+ unsigned int n);
+
+/**
+ * Get the oid of a specified parent for a commit. This is different from
+ * `git_commit_parent`, which will attempt to load the parent commit from
+ * the ODB.
+ *
+ * @param commit a previously loaded commit.
+ * @param n the position of the parent (from 0 to `parentcount`)
+ * @return the id of the parent, NULL on error.
+ */
+GIT_EXTERN(const git_oid *) git_commit_parent_id(
+ const git_commit *commit,
+ unsigned int n);
+
+/**
+ * Get the commit object that is the <n>th generation ancestor
+ * of the named commit object, following only the first parents.
+ * The returned commit has to be freed by the caller.
+ *
+ * Passing `0` as the generation number returns another instance of the
+ * base commit itself.
+ *
+ * @param ancestor Pointer where to store the ancestor commit
+ * @param commit a previously loaded commit.
+ * @param n the requested generation
+ * @return 0 on success; GIT_ENOTFOUND if no matching ancestor exists
+ * or an error code
+ */
+GIT_EXTERN(int) git_commit_nth_gen_ancestor(
+ git_commit **ancestor,
+ const git_commit *commit,
+ unsigned int n);
+
+/**
+ * Get an arbitrary header field
+ *
+ * @param out the buffer to fill; existing content will be
+ * overwritten
+ * @param commit the commit to look in
+ * @param field the header field to return
+ * @return 0 on succeess, GIT_ENOTFOUND if the field does not exist,
+ * or an error code
+ */
+GIT_EXTERN(int) git_commit_header_field(git_buf *out, const git_commit *commit, const char *field);
+
+/**
+ * Extract the signature from a commit
+ *
+ * If the id is not for a commit, the error class will be
+ * `GIT_ERROR_INVALID`. If the commit does not have a signature, the
+ * error class will be `GIT_ERROR_OBJECT`.
+ *
+ * @param signature the signature block; existing content will be
+ * overwritten
+ * @param signed_data signed data; this is the commit contents minus the signature block;
+ * existing content will be overwritten
+ * @param repo the repository in which the commit exists
+ * @param commit_id the commit from which to extract the data
+ * @param field the name of the header field containing the signature
+ * block; pass `NULL` to extract the default 'gpgsig'
+ * @return 0 on success, GIT_ENOTFOUND if the id is not for a commit
+ * or the commit does not have a signature.
+ */
+GIT_EXTERN(int) git_commit_extract_signature(git_buf *signature, git_buf *signed_data, git_repository *repo, git_oid *commit_id, const char *field);
+
+/**
+ * Create new commit in the repository from a list of `git_object` pointers
+ *
+ * The message will **not** be cleaned up automatically. You can do that
+ * with the `git_message_prettify()` function.
+ *
+ * @param id Pointer in which to store the OID of the newly created commit
+ *
+ * @param repo Repository where to store the commit
+ *
+ * @param update_ref If not NULL, name of the reference that
+ * will be updated to point to this commit. If the reference
+ * is not direct, it will be resolved to a direct reference.
+ * Use "HEAD" to update the HEAD of the current branch and
+ * make it point to this commit. If the reference doesn't
+ * exist yet, it will be created. If it does exist, the first
+ * parent must be the tip of this branch.
+ *
+ * @param author Signature with author and author time of commit
+ *
+ * @param committer Signature with committer and * commit time of commit
+ *
+ * @param message_encoding The encoding for the message in the
+ * commit, represented with a standard encoding name.
+ * E.g. "UTF-8". If NULL, no encoding header is written and
+ * UTF-8 is assumed.
+ *
+ * @param message Full message for this commit
+ *
+ * @param tree An instance of a `git_tree` object that will
+ * be used as the tree for the commit. This tree object must
+ * also be owned by the given `repo`.
+ *
+ * @param parent_count Number of parents for this commit
+ *
+ * @param parents Array of `parent_count` pointers to `git_commit`
+ * objects that will be used as the parents for this commit. This
+ * array may be NULL if `parent_count` is 0 (root commit). All the
+ * given commits must be owned by the `repo`.
+ *
+ * @return 0 or an error code
+ * The created commit will be written to the Object Database and
+ * the given reference will be updated to point to it
+ */
+GIT_EXTERN(int) git_commit_create(
+ git_oid *id,
+ git_repository *repo,
+ const char *update_ref,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *message_encoding,
+ const char *message,
+ const git_tree *tree,
+ size_t parent_count,
+ const git_commit *parents[]);
+
+/**
+ * Create new commit in the repository using a variable argument list.
+ *
+ * The message will **not** be cleaned up automatically. You can do that
+ * with the `git_message_prettify()` function.
+ *
+ * The parents for the commit are specified as a variable list of pointers
+ * to `const git_commit *`. Note that this is a convenience method which may
+ * not be safe to export for certain languages or compilers
+ *
+ * All other parameters remain the same as `git_commit_create()`.
+ *
+ * @see git_commit_create
+ */
+GIT_EXTERN(int) git_commit_create_v(
+ git_oid *id,
+ git_repository *repo,
+ const char *update_ref,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *message_encoding,
+ const char *message,
+ const git_tree *tree,
+ size_t parent_count,
+ ...);
+
+/**
+ * Amend an existing commit by replacing only non-NULL values.
+ *
+ * This creates a new commit that is exactly the same as the old commit,
+ * except that any non-NULL values will be updated. The new commit has
+ * the same parents as the old commit.
+ *
+ * The `update_ref` value works as in the regular `git_commit_create()`,
+ * updating the ref to point to the newly rewritten commit. If you want
+ * to amend a commit that is not currently the tip of the branch and then
+ * rewrite the following commits to reach a ref, pass this as NULL and
+ * update the rest of the commit chain and ref separately.
+ *
+ * Unlike `git_commit_create()`, the `author`, `committer`, `message`,
+ * `message_encoding`, and `tree` parameters can be NULL in which case this
+ * will use the values from the original `commit_to_amend`.
+ *
+ * All parameters have the same meanings as in `git_commit_create()`.
+ *
+ * @see git_commit_create
+ */
+GIT_EXTERN(int) git_commit_amend(
+ git_oid *id,
+ const git_commit *commit_to_amend,
+ const char *update_ref,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *message_encoding,
+ const char *message,
+ const git_tree *tree);
+
+/**
+ * Create a commit and write it into a buffer
+ *
+ * Create a commit as with `git_commit_create()` but instead of
+ * writing it to the objectdb, write the contents of the object into a
+ * buffer.
+ *
+ * @param out the buffer into which to write the commit object content
+ *
+ * @param repo Repository where the referenced tree and parents live
+ *
+ * @param author Signature with author and author time of commit
+ *
+ * @param committer Signature with committer and * commit time of commit
+ *
+ * @param message_encoding The encoding for the message in the
+ * commit, represented with a standard encoding name.
+ * E.g. "UTF-8". If NULL, no encoding header is written and
+ * UTF-8 is assumed.
+ *
+ * @param message Full message for this commit
+ *
+ * @param tree An instance of a `git_tree` object that will
+ * be used as the tree for the commit. This tree object must
+ * also be owned by the given `repo`.
+ *
+ * @param parent_count Number of parents for this commit
+ *
+ * @param parents Array of `parent_count` pointers to `git_commit`
+ * objects that will be used as the parents for this commit. This
+ * array may be NULL if `parent_count` is 0 (root commit). All the
+ * given commits must be owned by the `repo`.
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_commit_create_buffer(
+ git_buf *out,
+ git_repository *repo,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *message_encoding,
+ const char *message,
+ const git_tree *tree,
+ size_t parent_count,
+ const git_commit *parents[]);
+
+/**
+ * Create a commit object from the given buffer and signature
+ *
+ * Given the unsigned commit object's contents, its signature and the
+ * header field in which to store the signature, attach the signature
+ * to the commit and write it into the given repository.
+ *
+ * @param out the resulting commit id
+ * @param repo the repository to create the commit in.
+ * @param commit_content the content of the unsigned commit object
+ * @param signature the signature to add to the commit. Leave `NULL`
+ * to create a commit without adding a signature field.
+ * @param signature_field which header field should contain this
+ * signature. Leave `NULL` for the default of "gpgsig"
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_commit_create_with_signature(
+ git_oid *out,
+ git_repository *repo,
+ const char *commit_content,
+ const char *signature,
+ const char *signature_field);
+
+/**
+ * Create an in-memory copy of a commit. The copy must be explicitly
+ * free'd or it will leak.
+ *
+ * @param out Pointer to store the copy of the commit
+ * @param source Original commit to copy
+ * @return 0
+ */
+GIT_EXTERN(int) git_commit_dup(git_commit **out, git_commit *source);
+
+/**
+ * Commit creation callback: used when a function is going to create
+ * commits (for example, in `git_rebase_commit`) to allow callers to
+ * override the commit creation behavior. For example, users may
+ * wish to sign commits by providing this information to
+ * `git_commit_create_buffer`, signing that buffer, then calling
+ * `git_commit_create_with_signature`. The resultant commit id
+ * should be set in the `out` object id parameter.
+ *
+ * @param out pointer that this callback will populate with the object
+ * id of the commit that is created
+ * @param author the author name and time of the commit
+ * @param committer the committer name and time of the commit
+ * @param message_encoding the encoding of the given message, or NULL
+ * to assume UTF8
+ * @param message the commit message
+ * @param tree the tree to be committed
+ * @param parent_count the number of parents for this commit
+ * @param parents the commit parents
+ * @param payload the payload pointer in the rebase options
+ * @return 0 if this callback has created the commit and populated the out
+ * parameter, GIT_PASSTHROUGH if the callback has not created a
+ * commit and wants the calling function to create the commit as
+ * if no callback had been specified, any other value to stop
+ * and return a failure
+ */
+typedef int (*git_commit_create_cb)(
+ git_oid *out,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *message_encoding,
+ const char *message,
+ const git_tree *tree,
+ size_t parent_count,
+ const git_commit *parents[],
+ void *payload);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/common.h b/include/git2/common.h
new file mode 100644
index 0000000..ab6bc13
--- /dev/null
+++ b/include/git2/common.h
@@ -0,0 +1,517 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_common_h__
+#define INCLUDE_git_common_h__
+
+#include <time.h>
+#include <stdlib.h>
+
+#ifdef __cplusplus
+# define GIT_BEGIN_DECL extern "C" {
+# define GIT_END_DECL }
+#else
+ /** Start declarations in C mode */
+# define GIT_BEGIN_DECL /* empty */
+ /** End declarations in C mode */
+# define GIT_END_DECL /* empty */
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER < 1800
+# include <stdint.h>
+#elif !defined(__CLANG_INTTYPES_H)
+# include <inttypes.h>
+#endif
+
+#ifdef DOCURIUM
+/*
+ * This is so clang's doc parser acknowledges comments on functions
+ * with size_t parameters.
+ */
+typedef size_t size_t;
+#endif
+
+/** Declare a public function exported for application use. */
+#if __GNUC__ >= 4
+# define GIT_EXTERN(type) extern \
+ __attribute__((visibility("default"))) \
+ type
+#elif defined(_MSC_VER)
+# define GIT_EXTERN(type) __declspec(dllexport) type __cdecl
+#else
+# define GIT_EXTERN(type) extern type
+#endif
+
+/** Declare a callback function for application use. */
+#if defined(_MSC_VER)
+# define GIT_CALLBACK(name) (__cdecl *name)
+#else
+# define GIT_CALLBACK(name) (*name)
+#endif
+
+/** Declare a function as deprecated. */
+#if defined(__GNUC__)
+# define GIT_DEPRECATED(func) \
+ __attribute__((deprecated)) \
+ __attribute__((used)) \
+ func
+#elif defined(_MSC_VER)
+# define GIT_DEPRECATED(func) __declspec(deprecated) func
+#else
+# define GIT_DEPRECATED(func) func
+#endif
+
+/** Declare a function's takes printf style arguments. */
+#ifdef __GNUC__
+# define GIT_FORMAT_PRINTF(a,b) __attribute__((format (printf, a, b)))
+#else
+# define GIT_FORMAT_PRINTF(a,b) /* empty */
+#endif
+
+#if (defined(_WIN32)) && !defined(__CYGWIN__)
+#define GIT_WIN32 1
+#endif
+
+#ifdef __amigaos4__
+#include <netinet/in.h>
+#endif
+
+/**
+ * @file git2/common.h
+ * @brief Git common platform definitions
+ * @defgroup git_common Git common platform definitions
+ * @ingroup Git
+ * @{
+ */
+
+GIT_BEGIN_DECL
+
+/**
+ * The separator used in path list strings (ie like in the PATH
+ * environment variable). A semi-colon ";" is used on Windows and
+ * AmigaOS, and a colon ":" for all other systems.
+ */
+#if defined(GIT_WIN32) || defined(AMIGA)
+#define GIT_PATH_LIST_SEPARATOR ';'
+#else
+#define GIT_PATH_LIST_SEPARATOR ':'
+#endif
+
+/**
+ * The maximum length of a valid git path.
+ */
+#define GIT_PATH_MAX 4096
+
+/**
+ * Return the version of the libgit2 library
+ * being currently used.
+ *
+ * @param major Store the major version number
+ * @param minor Store the minor version number
+ * @param rev Store the revision (patch) number
+ * @return 0 on success or an error code on failure
+ */
+GIT_EXTERN(int) git_libgit2_version(int *major, int *minor, int *rev);
+
+/**
+ * Return the prerelease state of the libgit2 library currently being
+ * used. For nightly builds during active development, this will be
+ * "alpha". Releases may have a "beta" or release candidate ("rc1",
+ * "rc2", etc) prerelease. For a final release, this function returns
+ * NULL.
+ *
+ * @return the name of the prerelease state or NULL
+ */
+GIT_EXTERN(const char *) git_libgit2_prerelease(void);
+
+/**
+ * Combinations of these values describe the features with which libgit2
+ * was compiled
+ */
+typedef enum {
+ /**
+ * If set, libgit2 was built thread-aware and can be safely used from multiple
+ * threads.
+ */
+ GIT_FEATURE_THREADS = (1 << 0),
+ /**
+ * If set, libgit2 was built with and linked against a TLS implementation.
+ * Custom TLS streams may still be added by the user to support HTTPS
+ * regardless of this.
+ */
+ GIT_FEATURE_HTTPS = (1 << 1),
+ /**
+ * If set, libgit2 was built with and linked against libssh2. A custom
+ * transport may still be added by the user to support libssh2 regardless of
+ * this.
+ */
+ GIT_FEATURE_SSH = (1 << 2),
+ /**
+ * If set, libgit2 was built with support for sub-second resolution in file
+ * modification times.
+ */
+ GIT_FEATURE_NSEC = (1 << 3)
+} git_feature_t;
+
+/**
+ * Query compile time options for libgit2.
+ *
+ * @return A combination of GIT_FEATURE_* values.
+ *
+ * - GIT_FEATURE_THREADS
+ * Libgit2 was compiled with thread support. Note that thread support is
+ * still to be seen as a 'work in progress' - basic object lookups are
+ * believed to be threadsafe, but other operations may not be.
+ *
+ * - GIT_FEATURE_HTTPS
+ * Libgit2 supports the https:// protocol. This requires the openssl
+ * library to be found when compiling libgit2.
+ *
+ * - GIT_FEATURE_SSH
+ * Libgit2 supports the SSH protocol for network operations. This requires
+ * the libssh2 library to be found when compiling libgit2
+ *
+ * - GIT_FEATURE_NSEC
+ * Libgit2 supports the sub-second resolution in file modification times.
+ */
+GIT_EXTERN(int) git_libgit2_features(void);
+
+/**
+ * Global library options
+ *
+ * These are used to select which global option to set or get and are
+ * used in `git_libgit2_opts()`.
+ */
+typedef enum {
+ GIT_OPT_GET_MWINDOW_SIZE,
+ GIT_OPT_SET_MWINDOW_SIZE,
+ GIT_OPT_GET_MWINDOW_MAPPED_LIMIT,
+ GIT_OPT_SET_MWINDOW_MAPPED_LIMIT,
+ GIT_OPT_GET_SEARCH_PATH,
+ GIT_OPT_SET_SEARCH_PATH,
+ GIT_OPT_SET_CACHE_OBJECT_LIMIT,
+ GIT_OPT_SET_CACHE_MAX_SIZE,
+ GIT_OPT_ENABLE_CACHING,
+ GIT_OPT_GET_CACHED_MEMORY,
+ GIT_OPT_GET_TEMPLATE_PATH,
+ GIT_OPT_SET_TEMPLATE_PATH,
+ GIT_OPT_SET_SSL_CERT_LOCATIONS,
+ GIT_OPT_SET_USER_AGENT,
+ GIT_OPT_ENABLE_STRICT_OBJECT_CREATION,
+ GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION,
+ GIT_OPT_SET_SSL_CIPHERS,
+ GIT_OPT_GET_USER_AGENT,
+ GIT_OPT_ENABLE_OFS_DELTA,
+ GIT_OPT_ENABLE_FSYNC_GITDIR,
+ GIT_OPT_GET_WINDOWS_SHAREMODE,
+ GIT_OPT_SET_WINDOWS_SHAREMODE,
+ GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION,
+ GIT_OPT_SET_ALLOCATOR,
+ GIT_OPT_ENABLE_UNSAVED_INDEX_SAFETY,
+ GIT_OPT_GET_PACK_MAX_OBJECTS,
+ GIT_OPT_SET_PACK_MAX_OBJECTS,
+ GIT_OPT_DISABLE_PACK_KEEP_FILE_CHECKS,
+ GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE,
+ GIT_OPT_GET_MWINDOW_FILE_LIMIT,
+ GIT_OPT_SET_MWINDOW_FILE_LIMIT,
+ GIT_OPT_SET_ODB_PACKED_PRIORITY,
+ GIT_OPT_SET_ODB_LOOSE_PRIORITY,
+ GIT_OPT_GET_EXTENSIONS,
+ GIT_OPT_SET_EXTENSIONS,
+ GIT_OPT_GET_OWNER_VALIDATION,
+ GIT_OPT_SET_OWNER_VALIDATION,
+ GIT_OPT_GET_HOMEDIR,
+ GIT_OPT_SET_HOMEDIR,
+ GIT_OPT_SET_SERVER_CONNECT_TIMEOUT,
+ GIT_OPT_GET_SERVER_CONNECT_TIMEOUT,
+ GIT_OPT_SET_SERVER_TIMEOUT,
+ GIT_OPT_GET_SERVER_TIMEOUT
+} git_libgit2_opt_t;
+
+/**
+ * Set or query a library global option
+ *
+ * Available options:
+ *
+ * * opts(GIT_OPT_GET_MWINDOW_SIZE, size_t *):
+ *
+ * > Get the maximum mmap window size
+ *
+ * * opts(GIT_OPT_SET_MWINDOW_SIZE, size_t):
+ *
+ * > Set the maximum mmap window size
+ *
+ * * opts(GIT_OPT_GET_MWINDOW_MAPPED_LIMIT, size_t *):
+ *
+ * > Get the maximum memory that will be mapped in total by the library
+ *
+ * * opts(GIT_OPT_SET_MWINDOW_MAPPED_LIMIT, size_t):
+ *
+ * > Set the maximum amount of memory that can be mapped at any time
+ * > by the library
+ *
+ * * opts(GIT_OPT_GET_MWINDOW_FILE_LIMIT, size_t *):
+ *
+ * > Get the maximum number of files that will be mapped at any time by the
+ * > library
+ *
+ * * opts(GIT_OPT_SET_MWINDOW_FILE_LIMIT, size_t):
+ *
+ * > Set the maximum number of files that can be mapped at any time
+ * > by the library. The default (0) is unlimited.
+ *
+ * * opts(GIT_OPT_GET_SEARCH_PATH, int level, git_buf *buf)
+ *
+ * > Get the search path for a given level of config data. "level" must
+ * > be one of `GIT_CONFIG_LEVEL_SYSTEM`, `GIT_CONFIG_LEVEL_GLOBAL`,
+ * > `GIT_CONFIG_LEVEL_XDG`, or `GIT_CONFIG_LEVEL_PROGRAMDATA`.
+ * > The search path is written to the `out` buffer.
+ *
+ * * opts(GIT_OPT_SET_SEARCH_PATH, int level, const char *path)
+ *
+ * > Set the search path for a level of config data. The search path
+ * > applied to shared attributes and ignore files, too.
+ * >
+ * > - `path` lists directories delimited by GIT_PATH_LIST_SEPARATOR.
+ * > Pass NULL to reset to the default (generally based on environment
+ * > variables). Use magic path `$PATH` to include the old value
+ * > of the path (if you want to prepend or append, for instance).
+ * >
+ * > - `level` must be `GIT_CONFIG_LEVEL_SYSTEM`,
+ * > `GIT_CONFIG_LEVEL_GLOBAL`, `GIT_CONFIG_LEVEL_XDG`, or
+ * > `GIT_CONFIG_LEVEL_PROGRAMDATA`.
+ *
+ * * opts(GIT_OPT_SET_CACHE_OBJECT_LIMIT, git_object_t type, size_t size)
+ *
+ * > Set the maximum data size for the given type of object to be
+ * > considered eligible for caching in memory. Setting to value to
+ * > zero means that that type of object will not be cached.
+ * > Defaults to 0 for GIT_OBJECT_BLOB (i.e. won't cache blobs) and 4k
+ * > for GIT_OBJECT_COMMIT, GIT_OBJECT_TREE, and GIT_OBJECT_TAG.
+ *
+ * * opts(GIT_OPT_SET_CACHE_MAX_SIZE, ssize_t max_storage_bytes)
+ *
+ * > Set the maximum total data size that will be cached in memory
+ * > across all repositories before libgit2 starts evicting objects
+ * > from the cache. This is a soft limit, in that the library might
+ * > briefly exceed it, but will start aggressively evicting objects
+ * > from cache when that happens. The default cache size is 256MB.
+ *
+ * * opts(GIT_OPT_ENABLE_CACHING, int enabled)
+ *
+ * > Enable or disable caching completely.
+ * >
+ * > Because caches are repository-specific, disabling the cache
+ * > cannot immediately clear all cached objects, but each cache will
+ * > be cleared on the next attempt to update anything in it.
+ *
+ * * opts(GIT_OPT_GET_CACHED_MEMORY, ssize_t *current, ssize_t *allowed)
+ *
+ * > Get the current bytes in cache and the maximum that would be
+ * > allowed in the cache.
+ *
+ * * opts(GIT_OPT_GET_TEMPLATE_PATH, git_buf *out)
+ *
+ * > Get the default template path.
+ * > The path is written to the `out` buffer.
+ *
+ * * opts(GIT_OPT_SET_TEMPLATE_PATH, const char *path)
+ *
+ * > Set the default template path.
+ * >
+ * > - `path` directory of template.
+ *
+ * * opts(GIT_OPT_SET_SSL_CERT_LOCATIONS, const char *file, const char *path)
+ *
+ * > Set the SSL certificate-authority locations.
+ * >
+ * > - `file` is the location of a file containing several
+ * > certificates concatenated together.
+ * > - `path` is the location of a directory holding several
+ * > certificates, one per file.
+ * >
+ * > Either parameter may be `NULL`, but not both.
+ *
+ * * opts(GIT_OPT_SET_USER_AGENT, const char *user_agent)
+ *
+ * > Set the value of the User-Agent header. This value will be
+ * > appended to "git/1.0", for compatibility with other git clients.
+ * >
+ * > - `user_agent` is the value that will be delivered as the
+ * > User-Agent header on HTTP requests.
+ *
+ * * opts(GIT_OPT_SET_WINDOWS_SHAREMODE, unsigned long value)
+ *
+ * > Set the share mode used when opening files on Windows.
+ * > For more information, see the documentation for CreateFile.
+ * > The default is: FILE_SHARE_READ | FILE_SHARE_WRITE. This is
+ * > ignored and unused on non-Windows platforms.
+ *
+ * * opts(GIT_OPT_GET_WINDOWS_SHAREMODE, unsigned long *value)
+ *
+ * > Get the share mode used when opening files on Windows.
+ *
+ * * opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, int enabled)
+ *
+ * > Enable strict input validation when creating new objects
+ * > to ensure that all inputs to the new objects are valid. For
+ * > example, when this is enabled, the parent(s) and tree inputs
+ * > will be validated when creating a new commit. This defaults
+ * > to enabled.
+ *
+ * * opts(GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION, int enabled)
+ *
+ * > Validate the target of a symbolic ref when creating it. For
+ * > example, `foobar` is not a valid ref, therefore `foobar` is
+ * > not a valid target for a symbolic ref by default, whereas
+ * > `refs/heads/foobar` is. Disabling this bypasses validation
+ * > so that an arbitrary strings such as `foobar` can be used
+ * > for a symbolic ref target. This defaults to enabled.
+ *
+ * * opts(GIT_OPT_SET_SSL_CIPHERS, const char *ciphers)
+ *
+ * > Set the SSL ciphers use for HTTPS connections.
+ * >
+ * > - `ciphers` is the list of ciphers that are eanbled.
+ *
+ * * opts(GIT_OPT_GET_USER_AGENT, git_buf *out)
+ *
+ * > Get the value of the User-Agent header.
+ * > The User-Agent is written to the `out` buffer.
+ *
+ * * opts(GIT_OPT_ENABLE_OFS_DELTA, int enabled)
+ *
+ * > Enable or disable the use of "offset deltas" when creating packfiles,
+ * > and the negotiation of them when talking to a remote server.
+ * > Offset deltas store a delta base location as an offset into the
+ * > packfile from the current location, which provides a shorter encoding
+ * > and thus smaller resultant packfiles.
+ * > Packfiles containing offset deltas can still be read.
+ * > This defaults to enabled.
+ *
+ * * opts(GIT_OPT_ENABLE_FSYNC_GITDIR, int enabled)
+ *
+ * > Enable synchronized writes of files in the gitdir using `fsync`
+ * > (or the platform equivalent) to ensure that new object data
+ * > is written to permanent storage, not simply cached. This
+ * > defaults to disabled.
+ *
+ * opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, int enabled)
+ *
+ * > Enable strict verification of object hashsums when reading
+ * > objects from disk. This may impact performance due to an
+ * > additional checksum calculation on each object. This defaults
+ * > to enabled.
+ *
+ * opts(GIT_OPT_SET_ALLOCATOR, git_allocator *allocator)
+ *
+ * > Set the memory allocator to a different memory allocator. This
+ * > allocator will then be used to make all memory allocations for
+ * > libgit2 operations. If the given `allocator` is NULL, then the
+ * > system default will be restored.
+ *
+ * opts(GIT_OPT_ENABLE_UNSAVED_INDEX_SAFETY, int enabled)
+ *
+ * > Ensure that there are no unsaved changes in the index before
+ * > beginning any operation that reloads the index from disk (eg,
+ * > checkout). If there are unsaved changes, the instruction will
+ * > fail. (Using the FORCE flag to checkout will still overwrite
+ * > these changes.)
+ *
+ * opts(GIT_OPT_GET_PACK_MAX_OBJECTS, size_t *out)
+ *
+ * > Get the maximum number of objects libgit2 will allow in a pack
+ * > file when downloading a pack file from a remote. This can be
+ * > used to limit maximum memory usage when fetching from an untrusted
+ * > remote.
+ *
+ * opts(GIT_OPT_SET_PACK_MAX_OBJECTS, size_t objects)
+ *
+ * > Set the maximum number of objects libgit2 will allow in a pack
+ * > file when downloading a pack file from a remote.
+ *
+ * opts(GIT_OPT_DISABLE_PACK_KEEP_FILE_CHECKS, int enabled)
+ * > This will cause .keep file existence checks to be skipped when
+ * > accessing packfiles, which can help performance with remote filesystems.
+ *
+ * opts(GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE, int enabled)
+ * > When connecting to a server using NTLM or Negotiate
+ * > authentication, use expect/continue when POSTing data.
+ * > This option is not available on Windows.
+ *
+ * opts(GIT_OPT_SET_ODB_PACKED_PRIORITY, int priority)
+ * > Override the default priority of the packed ODB backend which
+ * > is added when default backends are assigned to a repository
+ *
+ * opts(GIT_OPT_SET_ODB_LOOSE_PRIORITY, int priority)
+ * > Override the default priority of the loose ODB backend which
+ * > is added when default backends are assigned to a repository
+ *
+ * opts(GIT_OPT_GET_EXTENSIONS, git_strarray *out)
+ * > Returns the list of git extensions that are supported. This
+ * > is the list of built-in extensions supported by libgit2 and
+ * > custom extensions that have been added with
+ * > `GIT_OPT_SET_EXTENSIONS`. Extensions that have been negated
+ * > will not be returned. The returned list should be released
+ * > with `git_strarray_dispose`.
+ *
+ * opts(GIT_OPT_SET_EXTENSIONS, const char **extensions, size_t len)
+ * > Set that the given git extensions are supported by the caller.
+ * > Extensions supported by libgit2 may be negated by prefixing
+ * > them with a `!`. For example: setting extensions to
+ * > { "!noop", "newext" } indicates that the caller does not want
+ * > to support repositories with the `noop` extension but does want
+ * > to support repositories with the `newext` extension.
+ *
+ * opts(GIT_OPT_GET_OWNER_VALIDATION, int *enabled)
+ * > Gets the owner validation setting for repository
+ * > directories.
+ *
+ * opts(GIT_OPT_SET_OWNER_VALIDATION, int enabled)
+ * > Set that repository directories should be owned by the current
+ * > user. The default is to validate ownership.
+ *
+ * opts(GIT_OPT_GET_HOMEDIR, git_buf *out)
+ * > Gets the current user's home directory, as it will be used
+ * > for file lookups. The path is written to the `out` buffer.
+ *
+ * opts(GIT_OPT_SET_HOMEDIR, const char *path)
+ * > Sets the directory used as the current user's home directory,
+ * > for file lookups.
+ * >
+ * > - `path` directory of home directory.
+ *
+ * opts(GIT_OPT_GET_SERVER_CONNECT_TIMEOUT, int *timeout)
+ * > Gets the timeout (in milliseconds) to attempt connections to
+ * > a remote server.
+ *
+ * opts(GIT_OPT_SET_SERVER_CONNECT_TIMEOUT, int timeout)
+ * > Sets the timeout (in milliseconds) to attempt connections to
+ * > a remote server. This is supported only for HTTP(S) connections
+ * > and is not supported by SSH. Set to 0 to use the system default.
+ * > Note that this may not be able to be configured longer than the
+ * > system default, typically 75 seconds.
+ *
+ * opts(GIT_OPT_GET_SERVER_TIMEOUT, int *timeout)
+ * > Gets the timeout (in milliseconds) for reading from and writing
+ * > to a remote server.
+ *
+ * opts(GIT_OPT_SET_SERVER_TIMEOUT, int timeout)
+ * > Sets the timeout (in milliseconds) for reading from and writing
+ * > to a remote server. This is supported only for HTTP(S)
+ * > connections and is not supported by SSH. Set to 0 to use the
+ * > system default.
+ *
+ * @param option Option key
+ * @param ... value to set the option
+ * @return 0 on success, <0 on failure
+ */
+GIT_EXTERN(int) git_libgit2_opts(int option, ...);
+
+/** @} */
+GIT_END_DECL
+
+#endif
diff --git a/include/git2/config.h b/include/git2/config.h
new file mode 100644
index 0000000..cfab0c7
--- /dev/null
+++ b/include/git2/config.h
@@ -0,0 +1,782 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_config_h__
+#define INCLUDE_git_config_h__
+
+#include "common.h"
+#include "types.h"
+#include "buffer.h"
+
+/**
+ * @file git2/config.h
+ * @brief Git config management routines
+ * @defgroup git_config Git config management routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Priority level of a config file.
+ * These priority levels correspond to the natural escalation logic
+ * (from higher to lower) when searching for config entries in git.git.
+ *
+ * git_config_open_default() and git_repository_config() honor those
+ * priority levels as well.
+ */
+typedef enum {
+ /** System-wide on Windows, for compatibility with portable git */
+ GIT_CONFIG_LEVEL_PROGRAMDATA = 1,
+
+ /** System-wide configuration file; /etc/gitconfig on Linux systems */
+ GIT_CONFIG_LEVEL_SYSTEM = 2,
+
+ /** XDG compatible configuration file; typically ~/.config/git/config */
+ GIT_CONFIG_LEVEL_XDG = 3,
+
+ /** User-specific configuration file (also called Global configuration
+ * file); typically ~/.gitconfig
+ */
+ GIT_CONFIG_LEVEL_GLOBAL = 4,
+
+ /** Repository specific configuration file; $WORK_DIR/.git/config on
+ * non-bare repos
+ */
+ GIT_CONFIG_LEVEL_LOCAL = 5,
+
+ /** Application specific configuration file; freely defined by applications
+ */
+ GIT_CONFIG_LEVEL_APP = 6,
+
+ /** Represents the highest level available config file (i.e. the most
+ * specific config file available that actually is loaded)
+ */
+ GIT_CONFIG_HIGHEST_LEVEL = -1
+} git_config_level_t;
+
+/**
+ * An entry in a configuration file
+ */
+typedef struct git_config_entry {
+ const char *name; /**< Name of the entry (normalised) */
+ const char *value; /**< String value of the entry */
+ unsigned int include_depth; /**< Depth of includes where this variable was found */
+ git_config_level_t level; /**< Which config file this was found in */
+ void GIT_CALLBACK(free)(struct git_config_entry *entry); /**< Free function for this entry */
+ void *payload; /**< Opaque value for the free function. Do not read or write */
+} git_config_entry;
+
+/**
+ * Free a config entry
+ *
+ * @param entry The entry to free.
+ */
+GIT_EXTERN(void) git_config_entry_free(git_config_entry *entry);
+
+/**
+ * A config enumeration callback
+ *
+ * @param entry the entry currently being enumerated
+ * @param payload a user-specified pointer
+ * @return non-zero to terminate the iteration.
+ */
+typedef int GIT_CALLBACK(git_config_foreach_cb)(const git_config_entry *entry, void *payload);
+
+/**
+ * An opaque structure for a configuration iterator
+ */
+typedef struct git_config_iterator git_config_iterator;
+
+/**
+ * Config var type
+ */
+typedef enum {
+ GIT_CONFIGMAP_FALSE = 0,
+ GIT_CONFIGMAP_TRUE = 1,
+ GIT_CONFIGMAP_INT32,
+ GIT_CONFIGMAP_STRING
+} git_configmap_t;
+
+/**
+ * Mapping from config variables to values.
+ */
+typedef struct {
+ git_configmap_t type;
+ const char *str_match;
+ int map_value;
+} git_configmap;
+
+/**
+ * Locate the path to the global configuration file
+ *
+ * The user or global configuration file is usually
+ * located in `$HOME/.gitconfig`.
+ *
+ * This method will try to guess the full path to that
+ * file, if the file exists. The returned path
+ * may be used on any `git_config` call to load the
+ * global configuration file.
+ *
+ * This method will not guess the path to the xdg compatible
+ * config file (`.config/git/config`).
+ *
+ * @param out Pointer to a user-allocated git_buf in which to store the path
+ * @return 0 if a global configuration file has been found. Its path will be stored in `out`.
+ */
+GIT_EXTERN(int) git_config_find_global(git_buf *out);
+
+/**
+ * Locate the path to the global xdg compatible configuration file
+ *
+ * The xdg compatible configuration file is usually
+ * located in `$HOME/.config/git/config`.
+ *
+ * This method will try to guess the full path to that
+ * file, if the file exists. The returned path
+ * may be used on any `git_config` call to load the
+ * xdg compatible configuration file.
+ *
+ * @param out Pointer to a user-allocated git_buf in which to store the path
+ * @return 0 if a xdg compatible configuration file has been
+ * found. Its path will be stored in `out`.
+ */
+GIT_EXTERN(int) git_config_find_xdg(git_buf *out);
+
+/**
+ * Locate the path to the system configuration file
+ *
+ * If `/etc/gitconfig` doesn't exist, it will look for
+ * `%PROGRAMFILES%\Git\etc\gitconfig`.
+ *
+ * @param out Pointer to a user-allocated git_buf in which to store the path
+ * @return 0 if a system configuration file has been
+ * found. Its path will be stored in `out`.
+ */
+GIT_EXTERN(int) git_config_find_system(git_buf *out);
+
+/**
+ * Locate the path to the configuration file in ProgramData
+ *
+ * Look for the file in `%PROGRAMDATA%\Git\config` used by portable git.
+ *
+ * @param out Pointer to a user-allocated git_buf in which to store the path
+ * @return 0 if a ProgramData configuration file has been
+ * found. Its path will be stored in `out`.
+ */
+GIT_EXTERN(int) git_config_find_programdata(git_buf *out);
+
+/**
+ * Open the global, XDG and system configuration files
+ *
+ * Utility wrapper that finds the global, XDG and system configuration files
+ * and opens them into a single prioritized config object that can be
+ * used when accessing default config data outside a repository.
+ *
+ * @param out Pointer to store the config instance
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_config_open_default(git_config **out);
+
+/**
+ * Allocate a new configuration object
+ *
+ * This object is empty, so you have to add a file to it before you
+ * can do anything with it.
+ *
+ * @param out pointer to the new configuration
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_config_new(git_config **out);
+
+/**
+ * Add an on-disk config file instance to an existing config
+ *
+ * The on-disk file pointed at by `path` will be opened and
+ * parsed; it's expected to be a native Git config file following
+ * the default Git config syntax (see man git-config).
+ *
+ * If the file does not exist, the file will still be added and it
+ * will be created the first time we write to it.
+ *
+ * Note that the configuration object will free the file
+ * automatically.
+ *
+ * Further queries on this config object will access each
+ * of the config file instances in order (instances with
+ * a higher priority level will be accessed first).
+ *
+ * @param cfg the configuration to add the file to
+ * @param path path to the configuration file to add
+ * @param level the priority level of the backend
+ * @param force replace config file at the given priority level
+ * @param repo optional repository to allow parsing of
+ * conditional includes
+ * @return 0 on success, GIT_EEXISTS when adding more than one file
+ * for a given priority level (and force_replace set to 0),
+ * GIT_ENOTFOUND when the file doesn't exist or error code
+ */
+GIT_EXTERN(int) git_config_add_file_ondisk(
+ git_config *cfg,
+ const char *path,
+ git_config_level_t level,
+ const git_repository *repo,
+ int force);
+
+/**
+ * Create a new config instance containing a single on-disk file
+ *
+ * This method is a simple utility wrapper for the following sequence
+ * of calls:
+ * - git_config_new
+ * - git_config_add_file_ondisk
+ *
+ * @param out The configuration instance to create
+ * @param path Path to the on-disk file to open
+ * @return 0 on success, or an error code
+ */
+GIT_EXTERN(int) git_config_open_ondisk(git_config **out, const char *path);
+
+/**
+ * Build a single-level focused config object from a multi-level one.
+ *
+ * The returned config object can be used to perform get/set/delete operations
+ * on a single specific level.
+ *
+ * Getting several times the same level from the same parent multi-level config
+ * will return different config instances, but containing the same config_file
+ * instance.
+ *
+ * @param out The configuration instance to create
+ * @param parent Multi-level config to search for the given level
+ * @param level Configuration level to search for
+ * @return 0, GIT_ENOTFOUND if the passed level cannot be found in the
+ * multi-level parent config, or an error code
+ */
+GIT_EXTERN(int) git_config_open_level(
+ git_config **out,
+ const git_config *parent,
+ git_config_level_t level);
+
+/**
+ * Open the global/XDG configuration file according to git's rules
+ *
+ * Git allows you to store your global configuration at
+ * `$HOME/.gitconfig` or `$XDG_CONFIG_HOME/git/config`. For backwards
+ * compatibility, the XDG file shouldn't be used unless the use has
+ * created it explicitly. With this function you'll open the correct
+ * one to write to.
+ *
+ * @param out pointer in which to store the config object
+ * @param config the config object in which to look
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_config_open_global(git_config **out, git_config *config);
+
+/**
+ * Create a snapshot of the configuration
+ *
+ * Create a snapshot of the current state of a configuration, which
+ * allows you to look into a consistent view of the configuration for
+ * looking up complex values (e.g. a remote, submodule).
+ *
+ * The string returned when querying such a config object is valid
+ * until it is freed.
+ *
+ * @param out pointer in which to store the snapshot config object
+ * @param config configuration to snapshot
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_config_snapshot(git_config **out, git_config *config);
+
+/**
+ * Free the configuration and its associated memory and files
+ *
+ * @param cfg the configuration to free
+ */
+GIT_EXTERN(void) git_config_free(git_config *cfg);
+
+/**
+ * Get the git_config_entry of a config variable.
+ *
+ * Free the git_config_entry after use with `git_config_entry_free()`.
+ *
+ * @param out pointer to the variable git_config_entry
+ * @param cfg where to look for the variable
+ * @param name the variable's name
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_config_get_entry(
+ git_config_entry **out,
+ const git_config *cfg,
+ const char *name);
+
+/**
+ * Get the value of an integer config variable.
+ *
+ * All config files will be looked into, in the order of their
+ * defined level. A higher level means a higher priority. The
+ * first occurrence of the variable will be returned here.
+ *
+ * @param out pointer to the variable where the value should be stored
+ * @param cfg where to look for the variable
+ * @param name the variable's name
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_config_get_int32(int32_t *out, const git_config *cfg, const char *name);
+
+/**
+ * Get the value of a long integer config variable.
+ *
+ * All config files will be looked into, in the order of their
+ * defined level. A higher level means a higher priority. The
+ * first occurrence of the variable will be returned here.
+ *
+ * @param out pointer to the variable where the value should be stored
+ * @param cfg where to look for the variable
+ * @param name the variable's name
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_config_get_int64(int64_t *out, const git_config *cfg, const char *name);
+
+/**
+ * Get the value of a boolean config variable.
+ *
+ * This function uses the usual C convention of 0 being false and
+ * anything else true.
+ *
+ * All config files will be looked into, in the order of their
+ * defined level. A higher level means a higher priority. The
+ * first occurrence of the variable will be returned here.
+ *
+ * @param out pointer to the variable where the value should be stored
+ * @param cfg where to look for the variable
+ * @param name the variable's name
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_config_get_bool(int *out, const git_config *cfg, const char *name);
+
+/**
+ * Get the value of a path config variable.
+ *
+ * A leading '~' will be expanded to the global search path (which
+ * defaults to the user's home directory but can be overridden via
+ * `git_libgit2_opts()`.
+ *
+ * All config files will be looked into, in the order of their
+ * defined level. A higher level means a higher priority. The
+ * first occurrence of the variable will be returned here.
+ *
+ * @param out the buffer in which to store the result
+ * @param cfg where to look for the variable
+ * @param name the variable's name
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_config_get_path(git_buf *out, const git_config *cfg, const char *name);
+
+/**
+ * Get the value of a string config variable.
+ *
+ * This function can only be used on snapshot config objects. The
+ * string is owned by the config and should not be freed by the
+ * user. The pointer will be valid until the config is freed.
+ *
+ * All config files will be looked into, in the order of their
+ * defined level. A higher level means a higher priority. The
+ * first occurrence of the variable will be returned here.
+ *
+ * @param out pointer to the string
+ * @param cfg where to look for the variable
+ * @param name the variable's name
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_config_get_string(const char **out, const git_config *cfg, const char *name);
+
+/**
+ * Get the value of a string config variable.
+ *
+ * The value of the config will be copied into the buffer.
+ *
+ * All config files will be looked into, in the order of their
+ * defined level. A higher level means a higher priority. The
+ * first occurrence of the variable will be returned here.
+ *
+ * @param out buffer in which to store the string
+ * @param cfg where to look for the variable
+ * @param name the variable's name
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_config_get_string_buf(git_buf *out, const git_config *cfg, const char *name);
+
+/**
+ * Get each value of a multivar in a foreach callback
+ *
+ * The callback will be called on each variable found
+ *
+ * The regular expression is applied case-sensitively on the normalized form of
+ * the variable name: the section and variable parts are lower-cased. The
+ * subsection is left unchanged.
+ *
+ * @param cfg where to look for the variable
+ * @param name the variable's name
+ * @param regexp regular expression to filter which variables we're
+ * interested in. Use NULL to indicate all
+ * @param callback the function to be called on each value of the variable
+ * @param payload opaque pointer to pass to the callback
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_config_get_multivar_foreach(const git_config *cfg, const char *name, const char *regexp, git_config_foreach_cb callback, void *payload);
+
+/**
+ * Get each value of a multivar
+ *
+ * The regular expression is applied case-sensitively on the normalized form of
+ * the variable name: the section and variable parts are lower-cased. The
+ * subsection is left unchanged.
+ *
+ * @param out pointer to store the iterator
+ * @param cfg where to look for the variable
+ * @param name the variable's name
+ * @param regexp regular expression to filter which variables we're
+ * interested in. Use NULL to indicate all
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_config_multivar_iterator_new(git_config_iterator **out, const git_config *cfg, const char *name, const char *regexp);
+
+/**
+ * Return the current entry and advance the iterator
+ *
+ * The pointers returned by this function are valid until the next call
+ * to `git_config_next` or until the iterator is freed.
+ *
+ * @param entry pointer to store the entry
+ * @param iter the iterator
+ * @return 0 or an error code. GIT_ITEROVER if the iteration has completed
+ */
+GIT_EXTERN(int) git_config_next(git_config_entry **entry, git_config_iterator *iter);
+
+/**
+ * Free a config iterator
+ *
+ * @param iter the iterator to free
+ */
+GIT_EXTERN(void) git_config_iterator_free(git_config_iterator *iter);
+
+/**
+ * Set the value of an integer config variable in the config file
+ * with the highest level (usually the local one).
+ *
+ * @param cfg where to look for the variable
+ * @param name the variable's name
+ * @param value Integer value for the variable
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_config_set_int32(git_config *cfg, const char *name, int32_t value);
+
+/**
+ * Set the value of a long integer config variable in the config file
+ * with the highest level (usually the local one).
+ *
+ * @param cfg where to look for the variable
+ * @param name the variable's name
+ * @param value Long integer value for the variable
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_config_set_int64(git_config *cfg, const char *name, int64_t value);
+
+/**
+ * Set the value of a boolean config variable in the config file
+ * with the highest level (usually the local one).
+ *
+ * @param cfg where to look for the variable
+ * @param name the variable's name
+ * @param value the value to store
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_config_set_bool(git_config *cfg, const char *name, int value);
+
+/**
+ * Set the value of a string config variable in the config file
+ * with the highest level (usually the local one).
+ *
+ * A copy of the string is made and the user is free to use it
+ * afterwards.
+ *
+ * @param cfg where to look for the variable
+ * @param name the variable's name
+ * @param value the string to store.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_config_set_string(git_config *cfg, const char *name, const char *value);
+
+/**
+ * Set a multivar in the local config file.
+ *
+ * The regular expression is applied case-sensitively on the value.
+ *
+ * @param cfg where to look for the variable
+ * @param name the variable's name
+ * @param regexp a regular expression to indicate which values to replace
+ * @param value the new value.
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value);
+
+/**
+ * Delete a config variable from the config file
+ * with the highest level (usually the local one).
+ *
+ * @param cfg the configuration
+ * @param name the variable to delete
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_config_delete_entry(git_config *cfg, const char *name);
+
+/**
+ * Deletes one or several entries from a multivar in the local config file.
+ *
+ * The regular expression is applied case-sensitively on the value.
+ *
+ * @param cfg where to look for the variables
+ * @param name the variable's name
+ * @param regexp a regular expression to indicate which values to delete
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_config_delete_multivar(git_config *cfg, const char *name, const char *regexp);
+
+/**
+ * Perform an operation on each config variable.
+ *
+ * The callback receives the normalized name and value of each variable
+ * in the config backend, and the data pointer passed to this function.
+ * If the callback returns a non-zero value, the function stops iterating
+ * and returns that value to the caller.
+ *
+ * The pointers passed to the callback are only valid as long as the
+ * iteration is ongoing.
+ *
+ * @param cfg where to get the variables from
+ * @param callback the function to call on each variable
+ * @param payload the data to pass to the callback
+ * @return 0 on success, non-zero callback return value, or error code
+ */
+GIT_EXTERN(int) git_config_foreach(
+ const git_config *cfg,
+ git_config_foreach_cb callback,
+ void *payload);
+
+/**
+ * Iterate over all the config variables
+ *
+ * Use `git_config_next` to advance the iteration and
+ * `git_config_iterator_free` when done.
+ *
+ * @param out pointer to store the iterator
+ * @param cfg where to get the variables from
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_config_iterator_new(git_config_iterator **out, const git_config *cfg);
+
+/**
+ * Iterate over all the config variables whose name matches a pattern
+ *
+ * Use `git_config_next` to advance the iteration and
+ * `git_config_iterator_free` when done.
+ *
+ * The regular expression is applied case-sensitively on the normalized form of
+ * the variable name: the section and variable parts are lower-cased. The
+ * subsection is left unchanged.
+ *
+ * @param out pointer to store the iterator
+ * @param cfg where to ge the variables from
+ * @param regexp regular expression to match the names
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_config_iterator_glob_new(git_config_iterator **out, const git_config *cfg, const char *regexp);
+
+/**
+ * Perform an operation on each config variable matching a regular expression.
+ *
+ * This behaves like `git_config_foreach` with an additional filter of a
+ * regular expression that filters which config keys are passed to the
+ * callback.
+ *
+ * The regular expression is applied case-sensitively on the normalized form of
+ * the variable name: the section and variable parts are lower-cased. The
+ * subsection is left unchanged.
+ *
+ * The regular expression is applied case-sensitively on the normalized form of
+ * the variable name: the case-insensitive parts are lower-case.
+ *
+ * @param cfg where to get the variables from
+ * @param regexp regular expression to match against config names
+ * @param callback the function to call on each variable
+ * @param payload the data to pass to the callback
+ * @return 0 or the return value of the callback which didn't return 0
+ */
+GIT_EXTERN(int) git_config_foreach_match(
+ const git_config *cfg,
+ const char *regexp,
+ git_config_foreach_cb callback,
+ void *payload);
+
+/**
+ * Query the value of a config variable and return it mapped to
+ * an integer constant.
+ *
+ * This is a helper method to easily map different possible values
+ * to a variable to integer constants that easily identify them.
+ *
+ * A mapping array looks as follows:
+ *
+ * git_configmap autocrlf_mapping[] = {
+ * {GIT_CVAR_FALSE, NULL, GIT_AUTO_CRLF_FALSE},
+ * {GIT_CVAR_TRUE, NULL, GIT_AUTO_CRLF_TRUE},
+ * {GIT_CVAR_STRING, "input", GIT_AUTO_CRLF_INPUT},
+ * {GIT_CVAR_STRING, "default", GIT_AUTO_CRLF_DEFAULT}};
+ *
+ * On any "false" value for the variable (e.g. "false", "FALSE", "no"), the
+ * mapping will store `GIT_AUTO_CRLF_FALSE` in the `out` parameter.
+ *
+ * The same thing applies for any "true" value such as "true", "yes" or "1", storing
+ * the `GIT_AUTO_CRLF_TRUE` variable.
+ *
+ * Otherwise, if the value matches the string "input" (with case insensitive comparison),
+ * the given constant will be stored in `out`, and likewise for "default".
+ *
+ * If not a single match can be made to store in `out`, an error code will be
+ * returned.
+ *
+ * @param out place to store the result of the mapping
+ * @param cfg config file to get the variables from
+ * @param name name of the config variable to lookup
+ * @param maps array of `git_configmap` objects specifying the possible mappings
+ * @param map_n number of mapping objects in `maps`
+ * @return 0 on success, error code otherwise
+ */
+GIT_EXTERN(int) git_config_get_mapped(
+ int *out,
+ const git_config *cfg,
+ const char *name,
+ const git_configmap *maps,
+ size_t map_n);
+
+/**
+ * Maps a string value to an integer constant
+ *
+ * @param out place to store the result of the parsing
+ * @param maps array of `git_configmap` objects specifying the possible mappings
+ * @param map_n number of mapping objects in `maps`
+ * @param value value to parse
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_config_lookup_map_value(
+ int *out,
+ const git_configmap *maps,
+ size_t map_n,
+ const char *value);
+
+/**
+ * Parse a string value as a bool.
+ *
+ * Valid values for true are: 'true', 'yes', 'on', 1 or any
+ * number different from 0
+ * Valid values for false are: 'false', 'no', 'off', 0
+ *
+ * @param out place to store the result of the parsing
+ * @param value value to parse
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_config_parse_bool(int *out, const char *value);
+
+/**
+ * Parse a string value as an int32.
+ *
+ * An optional value suffix of 'k', 'm', or 'g' will
+ * cause the value to be multiplied by 1024, 1048576,
+ * or 1073741824 prior to output.
+ *
+ * @param out place to store the result of the parsing
+ * @param value value to parse
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_config_parse_int32(int32_t *out, const char *value);
+
+/**
+ * Parse a string value as an int64.
+ *
+ * An optional value suffix of 'k', 'm', or 'g' will
+ * cause the value to be multiplied by 1024, 1048576,
+ * or 1073741824 prior to output.
+ *
+ * @param out place to store the result of the parsing
+ * @param value value to parse
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_config_parse_int64(int64_t *out, const char *value);
+
+/**
+ * Parse a string value as a path.
+ *
+ * A leading '~' will be expanded to the global search path (which
+ * defaults to the user's home directory but can be overridden via
+ * `git_libgit2_opts()`.
+ *
+ * If the value does not begin with a tilde, the input will be
+ * returned.
+ *
+ * @param out placae to store the result of parsing
+ * @param value the path to evaluate
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_config_parse_path(git_buf *out, const char *value);
+
+/**
+ * Perform an operation on each config variable in a given config backend,
+ * matching a regular expression.
+ *
+ * This behaves like `git_config_foreach_match` except that only config
+ * entries from the given backend entry are enumerated.
+ *
+ * The regular expression is applied case-sensitively on the normalized form of
+ * the variable name: the section and variable parts are lower-cased. The
+ * subsection is left unchanged.
+ *
+ * @param backend where to get the variables from
+ * @param regexp regular expression to match against config names (can be NULL)
+ * @param callback the function to call on each variable
+ * @param payload the data to pass to the callback
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_config_backend_foreach_match(
+ git_config_backend *backend,
+ const char *regexp,
+ git_config_foreach_cb callback,
+ void *payload);
+
+
+/**
+ * Lock the backend with the highest priority
+ *
+ * Locking disallows anybody else from writing to that backend. Any
+ * updates made after locking will not be visible to a reader until
+ * the file is unlocked.
+ *
+ * You can apply the changes by calling `git_transaction_commit()`
+ * before freeing the transaction. Either of these actions will unlock
+ * the config.
+ *
+ * @param tx the resulting transaction, use this to commit or undo the
+ * changes
+ * @param cfg the configuration in which to lock
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_config_lock(git_transaction **tx, git_config *cfg);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/cred_helpers.h b/include/git2/cred_helpers.h
new file mode 100644
index 0000000..3721b6d
--- /dev/null
+++ b/include/git2/cred_helpers.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_cred_helpers_h__
+#define INCLUDE_git_cred_helpers_h__
+
+/* These declarations have moved. */
+#ifndef GIT_DEPRECATE_HARD
+# include "git2/credential_helpers.h"
+#endif
+
+#endif
diff --git a/include/git2/credential.h b/include/git2/credential.h
new file mode 100644
index 0000000..7a04bc0
--- /dev/null
+++ b/include/git2/credential.h
@@ -0,0 +1,315 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_credential_h__
+#define INCLUDE_git_credential_h__
+
+#include "common.h"
+
+/**
+ * @file git2/credential.h
+ * @brief Git authentication & credential management
+ * @defgroup git_credential Authentication & credential management
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Supported credential types
+ *
+ * This represents the various types of authentication methods supported by
+ * the library.
+ */
+typedef enum {
+ /**
+ * A vanilla user/password request
+ * @see git_credential_userpass_plaintext_new
+ */
+ GIT_CREDENTIAL_USERPASS_PLAINTEXT = (1u << 0),
+
+ /**
+ * An SSH key-based authentication request
+ * @see git_credential_ssh_key_new
+ */
+ GIT_CREDENTIAL_SSH_KEY = (1u << 1),
+
+ /**
+ * An SSH key-based authentication request, with a custom signature
+ * @see git_credential_ssh_custom_new
+ */
+ GIT_CREDENTIAL_SSH_CUSTOM = (1u << 2),
+
+ /**
+ * An NTLM/Negotiate-based authentication request.
+ * @see git_credential_default
+ */
+ GIT_CREDENTIAL_DEFAULT = (1u << 3),
+
+ /**
+ * An SSH interactive authentication request
+ * @see git_credential_ssh_interactive_new
+ */
+ GIT_CREDENTIAL_SSH_INTERACTIVE = (1u << 4),
+
+ /**
+ * Username-only authentication request
+ *
+ * Used as a pre-authentication step if the underlying transport
+ * (eg. SSH, with no username in its URL) does not know which username
+ * to use.
+ *
+ * @see git_credential_username_new
+ */
+ GIT_CREDENTIAL_USERNAME = (1u << 5),
+
+ /**
+ * An SSH key-based authentication request
+ *
+ * Allows credentials to be read from memory instead of files.
+ * Note that because of differences in crypto backend support, it might
+ * not be functional.
+ *
+ * @see git_credential_ssh_key_memory_new
+ */
+ GIT_CREDENTIAL_SSH_MEMORY = (1u << 6)
+} git_credential_t;
+
+/**
+ * The base structure for all credential types
+ */
+typedef struct git_credential git_credential;
+
+typedef struct git_credential_userpass_plaintext git_credential_userpass_plaintext;
+
+/** Username-only credential information */
+typedef struct git_credential_username git_credential_username;
+
+/** A key for NTLM/Kerberos "default" credentials */
+typedef struct git_credential git_credential_default;
+
+/**
+ * A ssh key from disk
+ */
+typedef struct git_credential_ssh_key git_credential_ssh_key;
+
+/**
+ * Keyboard-interactive based ssh authentication
+ */
+typedef struct git_credential_ssh_interactive git_credential_ssh_interactive;
+
+/**
+ * A key with a custom signature function
+ */
+typedef struct git_credential_ssh_custom git_credential_ssh_custom;
+
+/**
+ * Credential acquisition callback.
+ *
+ * This callback is usually involved any time another system might need
+ * authentication. As such, you are expected to provide a valid
+ * git_credential object back, depending on allowed_types (a
+ * git_credential_t bitmask).
+ *
+ * Note that most authentication details are your responsibility - this
+ * callback will be called until the authentication succeeds, or you report
+ * an error. As such, it's easy to get in a loop if you fail to stop providing
+ * the same incorrect credentials.
+ *
+ * @param out The newly created credential object.
+ * @param url The resource for which we are demanding a credential.
+ * @param username_from_url The username that was embedded in a "user\@host"
+ * remote url, or NULL if not included.
+ * @param allowed_types A bitmask stating which credential types are OK to return.
+ * @param payload The payload provided when specifying this callback.
+ * @return 0 for success, < 0 to indicate an error, > 0 to indicate
+ * no credential was acquired
+ */
+typedef int GIT_CALLBACK(git_credential_acquire_cb)(
+ git_credential **out,
+ const char *url,
+ const char *username_from_url,
+ unsigned int allowed_types,
+ void *payload);
+
+/**
+ * Free a credential.
+ *
+ * This is only necessary if you own the object; that is, if you are a
+ * transport.
+ *
+ * @param cred the object to free
+ */
+GIT_EXTERN(void) git_credential_free(git_credential *cred);
+
+/**
+ * Check whether a credential object contains username information.
+ *
+ * @param cred object to check
+ * @return 1 if the credential object has non-NULL username, 0 otherwise
+ */
+GIT_EXTERN(int) git_credential_has_username(git_credential *cred);
+
+/**
+ * Return the username associated with a credential object.
+ *
+ * @param cred object to check
+ * @return the credential username, or NULL if not applicable
+ */
+GIT_EXTERN(const char *) git_credential_get_username(git_credential *cred);
+
+/**
+ * Create a new plain-text username and password credential object.
+ * The supplied credential parameter will be internally duplicated.
+ *
+ * @param out The newly created credential object.
+ * @param username The username of the credential.
+ * @param password The password of the credential.
+ * @return 0 for success or an error code for failure
+ */
+GIT_EXTERN(int) git_credential_userpass_plaintext_new(
+ git_credential **out,
+ const char *username,
+ const char *password);
+
+/**
+ * Create a "default" credential usable for Negotiate mechanisms like NTLM
+ * or Kerberos authentication.
+ *
+ * @param out The newly created credential object.
+ * @return 0 for success or an error code for failure
+ */
+GIT_EXTERN(int) git_credential_default_new(git_credential **out);
+
+/**
+ * Create a credential to specify a username.
+ *
+ * This is used with ssh authentication to query for the username if
+ * none is specified in the url.
+ *
+ * @param out The newly created credential object.
+ * @param username The username to authenticate with
+ * @return 0 for success or an error code for failure
+ */
+GIT_EXTERN(int) git_credential_username_new(git_credential **out, const char *username);
+
+/**
+ * Create a new passphrase-protected ssh key credential object.
+ * The supplied credential parameter will be internally duplicated.
+ *
+ * @param out The newly created credential object.
+ * @param username username to use to authenticate
+ * @param publickey The path to the public key of the credential.
+ * @param privatekey The path to the private key of the credential.
+ * @param passphrase The passphrase of the credential.
+ * @return 0 for success or an error code for failure
+ */
+GIT_EXTERN(int) git_credential_ssh_key_new(
+ git_credential **out,
+ const char *username,
+ const char *publickey,
+ const char *privatekey,
+ const char *passphrase);
+
+/**
+ * Create a new ssh key credential object reading the keys from memory.
+ *
+ * @param out The newly created credential object.
+ * @param username username to use to authenticate.
+ * @param publickey The public key of the credential.
+ * @param privatekey The private key of the credential.
+ * @param passphrase The passphrase of the credential.
+ * @return 0 for success or an error code for failure
+ */
+GIT_EXTERN(int) git_credential_ssh_key_memory_new(
+ git_credential **out,
+ const char *username,
+ const char *publickey,
+ const char *privatekey,
+ const char *passphrase);
+
+/*
+ * If the user hasn't included libssh2.h before git2.h, we need to
+ * define a few types for the callback signatures.
+ */
+#ifndef LIBSSH2_VERSION
+typedef struct _LIBSSH2_SESSION LIBSSH2_SESSION;
+typedef struct _LIBSSH2_USERAUTH_KBDINT_PROMPT LIBSSH2_USERAUTH_KBDINT_PROMPT;
+typedef struct _LIBSSH2_USERAUTH_KBDINT_RESPONSE LIBSSH2_USERAUTH_KBDINT_RESPONSE;
+#endif
+
+typedef void GIT_CALLBACK(git_credential_ssh_interactive_cb)(
+ const char *name,
+ int name_len,
+ const char *instruction, int instruction_len,
+ int num_prompts, const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts,
+ LIBSSH2_USERAUTH_KBDINT_RESPONSE *responses,
+ void **abstract);
+
+
+/**
+ * Create a new ssh keyboard-interactive based credential object.
+ * The supplied credential parameter will be internally duplicated.
+ *
+ * @param out The newly created credential object.
+ * @param username Username to use to authenticate.
+ * @param prompt_callback The callback method used for prompts.
+ * @param payload Additional data to pass to the callback.
+ * @return 0 for success or an error code for failure.
+ */
+GIT_EXTERN(int) git_credential_ssh_interactive_new(
+ git_credential **out,
+ const char *username,
+ git_credential_ssh_interactive_cb prompt_callback,
+ void *payload);
+
+/**
+ * Create a new ssh key credential object used for querying an ssh-agent.
+ * The supplied credential parameter will be internally duplicated.
+ *
+ * @param out The newly created credential object.
+ * @param username username to use to authenticate
+ * @return 0 for success or an error code for failure
+ */
+GIT_EXTERN(int) git_credential_ssh_key_from_agent(
+ git_credential **out,
+ const char *username);
+
+typedef int GIT_CALLBACK(git_credential_sign_cb)(
+ LIBSSH2_SESSION *session,
+ unsigned char **sig, size_t *sig_len,
+ const unsigned char *data, size_t data_len,
+ void **abstract);
+
+/**
+ * Create an ssh key credential with a custom signing function.
+ *
+ * This lets you use your own function to sign the challenge.
+ *
+ * This function and its credential type is provided for completeness
+ * and wraps `libssh2_userauth_publickey()`, which is undocumented.
+ *
+ * The supplied credential parameter will be internally duplicated.
+ *
+ * @param out The newly created credential object.
+ * @param username username to use to authenticate
+ * @param publickey The bytes of the public key.
+ * @param publickey_len The length of the public key in bytes.
+ * @param sign_callback The callback method to sign the data during the challenge.
+ * @param payload Additional data to pass to the callback.
+ * @return 0 for success or an error code for failure
+ */
+GIT_EXTERN(int) git_credential_ssh_custom_new(
+ git_credential **out,
+ const char *username,
+ const char *publickey,
+ size_t publickey_len,
+ git_credential_sign_cb sign_callback,
+ void *payload);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/credential_helpers.h b/include/git2/credential_helpers.h
new file mode 100644
index 0000000..f0fb070
--- /dev/null
+++ b/include/git2/credential_helpers.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_credential_helpers_h__
+#define INCLUDE_git_credential_helpers_h__
+
+#include "transport.h"
+
+/**
+ * @file git2/credential_helpers.h
+ * @brief Utility functions for credential management
+ * @defgroup git_credential_helpers credential management helpers
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Payload for git_credential_userpass_plaintext.
+ */
+typedef struct git_credential_userpass_payload {
+ const char *username;
+ const char *password;
+} git_credential_userpass_payload;
+
+
+/**
+ * Stock callback usable as a git_credential_acquire_cb. This calls
+ * git_cred_userpass_plaintext_new unless the protocol has not specified
+ * `GIT_CREDENTIAL_USERPASS_PLAINTEXT` as an allowed type.
+ *
+ * @param out The newly created credential object.
+ * @param url The resource for which we are demanding a credential.
+ * @param user_from_url The username that was embedded in a "user\@host"
+ * remote url, or NULL if not included.
+ * @param allowed_types A bitmask stating which credential types are OK to return.
+ * @param payload The payload provided when specifying this callback. (This is
+ * interpreted as a `git_credential_userpass_payload*`.)
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_credential_userpass(
+ git_credential **out,
+ const char *url,
+ const char *user_from_url,
+ unsigned int allowed_types,
+ void *payload);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/deprecated.h b/include/git2/deprecated.h
new file mode 100644
index 0000000..52864eb
--- /dev/null
+++ b/include/git2/deprecated.h
@@ -0,0 +1,939 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_deprecated_h__
+#define INCLUDE_git_deprecated_h__
+
+#include "attr.h"
+#include "config.h"
+#include "common.h"
+#include "blame.h"
+#include "buffer.h"
+#include "checkout.h"
+#include "cherrypick.h"
+#include "clone.h"
+#include "describe.h"
+#include "diff.h"
+#include "errors.h"
+#include "filter.h"
+#include "index.h"
+#include "indexer.h"
+#include "merge.h"
+#include "object.h"
+#include "proxy.h"
+#include "refs.h"
+#include "rebase.h"
+#include "remote.h"
+#include "trace.h"
+#include "repository.h"
+#include "revert.h"
+#include "revparse.h"
+#include "stash.h"
+#include "status.h"
+#include "submodule.h"
+#include "worktree.h"
+#include "credential.h"
+#include "credential_helpers.h"
+
+/*
+ * Users can avoid deprecated functions by defining `GIT_DEPRECATE_HARD`.
+ */
+#ifndef GIT_DEPRECATE_HARD
+
+/*
+ * The credential structures are now opaque by default, and their
+ * definition has moved into the `sys/credential.h` header; include
+ * them here for backward compatibility.
+ */
+#include "sys/credential.h"
+
+/**
+ * @file git2/deprecated.h
+ * @brief libgit2 deprecated functions and values
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/** @name Deprecated Attribute Constants
+ *
+ * These enumeration values are retained for backward compatibility.
+ * The newer versions of these functions should be preferred in all
+ * new code.
+ *
+ * There is no plan to remove these backward compatibility values at
+ * this time.
+ */
+/**@{*/
+
+#define GIT_ATTR_UNSPECIFIED_T GIT_ATTR_VALUE_UNSPECIFIED
+#define GIT_ATTR_TRUE_T GIT_ATTR_VALUE_TRUE
+#define GIT_ATTR_FALSE_T GIT_ATTR_VALUE_FALSE
+#define GIT_ATTR_VALUE_T GIT_ATTR_VALUE_STRING
+
+#define GIT_ATTR_TRUE(attr) GIT_ATTR_IS_TRUE(attr)
+#define GIT_ATTR_FALSE(attr) GIT_ATTR_IS_FALSE(attr)
+#define GIT_ATTR_UNSPECIFIED(attr) GIT_ATTR_IS_UNSPECIFIED(attr)
+
+typedef git_attr_value_t git_attr_t;
+
+/**@}*/
+
+/** @name Deprecated Blob Functions and Constants
+ *
+ * These functions and enumeration values are retained for backward
+ * compatibility. The newer versions of these functions and values
+ * should be preferred in all new code.
+ *
+ * There is no plan to remove these backward compatibility values at
+ * this time.
+ */
+/**@{*/
+
+#define GIT_BLOB_FILTER_ATTTRIBUTES_FROM_HEAD GIT_BLOB_FILTER_ATTRIBUTES_FROM_HEAD
+
+GIT_EXTERN(int) git_blob_create_fromworkdir(git_oid *id, git_repository *repo, const char *relative_path);
+GIT_EXTERN(int) git_blob_create_fromdisk(git_oid *id, git_repository *repo, const char *path);
+GIT_EXTERN(int) git_blob_create_fromstream(
+ git_writestream **out,
+ git_repository *repo,
+ const char *hintpath);
+GIT_EXTERN(int) git_blob_create_fromstream_commit(
+ git_oid *out,
+ git_writestream *stream);
+GIT_EXTERN(int) git_blob_create_frombuffer(
+ git_oid *id, git_repository *repo, const void *buffer, size_t len);
+
+/** Deprecated in favor of `git_blob_filter`.
+ *
+ * @deprecated Use git_blob_filter
+ * @see git_blob_filter
+ */
+GIT_EXTERN(int) git_blob_filtered_content(
+ git_buf *out,
+ git_blob *blob,
+ const char *as_path,
+ int check_for_binary_data);
+
+/**@}*/
+
+/** @name Deprecated Filter Functions
+ *
+ * These functions are retained for backward compatibility. The
+ * newer versions of these functions should be preferred in all
+ * new code.
+ *
+ * There is no plan to remove these backward compatibility values at
+ * this time.
+ */
+/**@{*/
+
+/** Deprecated in favor of `git_filter_list_stream_buffer`.
+ *
+ * @deprecated Use git_filter_list_stream_buffer
+ * @see Use git_filter_list_stream_buffer
+ */
+GIT_EXTERN(int) git_filter_list_stream_data(
+ git_filter_list *filters,
+ git_buf *data,
+ git_writestream *target);
+
+/** Deprecated in favor of `git_filter_list_apply_to_buffer`.
+ *
+ * @deprecated Use git_filter_list_apply_to_buffer
+ * @see Use git_filter_list_apply_to_buffer
+ */
+GIT_EXTERN(int) git_filter_list_apply_to_data(
+ git_buf *out,
+ git_filter_list *filters,
+ git_buf *in);
+
+/**@}*/
+
+/** @name Deprecated Tree Functions
+ *
+ * These functions are retained for backward compatibility. The
+ * newer versions of these functions and values should be preferred
+ * in all new code.
+ *
+ * There is no plan to remove these backward compatibility values at
+ * this time.
+ */
+/**@{*/
+
+/**
+ * Write the contents of the tree builder as a tree object.
+ * This is an alias of `git_treebuilder_write` and is preserved
+ * for backward compatibility.
+ *
+ * This function is deprecated, but there is no plan to remove this
+ * function at this time.
+ *
+ * @deprecated Use git_treebuilder_write
+ * @see git_treebuilder_write
+ */
+GIT_EXTERN(int) git_treebuilder_write_with_buffer(
+ git_oid *oid, git_treebuilder *bld, git_buf *tree);
+
+/**@}*/
+
+/** @name Deprecated Buffer Functions
+ *
+ * These functions and enumeration values are retained for backward
+ * compatibility. The newer versions of these functions should be
+ * preferred in all new code.
+ *
+ * There is no plan to remove these backward compatibility values at
+ * this time.
+ */
+/**@{*/
+
+/**
+ * Static initializer for git_buf from static buffer
+ */
+#define GIT_BUF_INIT_CONST(STR,LEN) { (char *)(STR), 0, (size_t)(LEN) }
+
+/**
+ * Resize the buffer allocation to make more space.
+ *
+ * This will attempt to grow the buffer to accommodate the target size.
+ *
+ * If the buffer refers to memory that was not allocated by libgit2 (i.e.
+ * the `asize` field is zero), then `ptr` will be replaced with a newly
+ * allocated block of data. Be careful so that memory allocated by the
+ * caller is not lost. As a special variant, if you pass `target_size` as
+ * 0 and the memory is not allocated by libgit2, this will allocate a new
+ * buffer of size `size` and copy the external data into it.
+ *
+ * Currently, this will never shrink a buffer, only expand it.
+ *
+ * If the allocation fails, this will return an error and the buffer will be
+ * marked as invalid for future operations, invaliding the contents.
+ *
+ * @param buffer The buffer to be resized; may or may not be allocated yet
+ * @param target_size The desired available size
+ * @return 0 on success, -1 on allocation failure
+ */
+GIT_EXTERN(int) git_buf_grow(git_buf *buffer, size_t target_size);
+
+/**
+ * Set buffer to a copy of some raw data.
+ *
+ * @param buffer The buffer to set
+ * @param data The data to copy into the buffer
+ * @param datalen The length of the data to copy into the buffer
+ * @return 0 on success, -1 on allocation failure
+ */
+GIT_EXTERN(int) git_buf_set(
+ git_buf *buffer, const void *data, size_t datalen);
+
+/**
+* Check quickly if buffer looks like it contains binary data
+*
+* @param buf Buffer to check
+* @return 1 if buffer looks like non-text data
+*/
+GIT_EXTERN(int) git_buf_is_binary(const git_buf *buf);
+
+/**
+* Check quickly if buffer contains a NUL byte
+*
+* @param buf Buffer to check
+* @return 1 if buffer contains a NUL byte
+*/
+GIT_EXTERN(int) git_buf_contains_nul(const git_buf *buf);
+
+/**
+ * Free the memory referred to by the git_buf. This is an alias of
+ * `git_buf_dispose` and is preserved for backward compatibility.
+ *
+ * This function is deprecated, but there is no plan to remove this
+ * function at this time.
+ *
+ * @deprecated Use git_buf_dispose
+ * @see git_buf_dispose
+ */
+GIT_EXTERN(void) git_buf_free(git_buf *buffer);
+
+/**@}*/
+
+/** @name Deprecated Commit Definitions
+ */
+/**@{*/
+
+/**
+ * Provide a commit signature during commit creation.
+ *
+ * Callers should instead define a `git_commit_create_cb` that
+ * generates a commit buffer using `git_commit_create_buffer`, sign
+ * that buffer and call `git_commit_create_with_signature`.
+ *
+ * @deprecated use a `git_commit_create_cb` instead
+ */
+typedef int (*git_commit_signing_cb)(
+ git_buf *signature,
+ git_buf *signature_field,
+ const char *commit_content,
+ void *payload);
+
+/**@}*/
+
+/** @name Deprecated Config Functions and Constants
+ */
+/**@{*/
+
+#define GIT_CVAR_FALSE GIT_CONFIGMAP_FALSE
+#define GIT_CVAR_TRUE GIT_CONFIGMAP_TRUE
+#define GIT_CVAR_INT32 GIT_CONFIGMAP_INT32
+#define GIT_CVAR_STRING GIT_CONFIGMAP_STRING
+
+typedef git_configmap git_cvar_map;
+
+/**@}*/
+
+/** @name Deprecated Diff Functions and Constants
+ *
+ * These functions and enumeration values are retained for backward
+ * compatibility. The newer versions of these functions and values
+ * should be preferred in all new code.
+ *
+ * There is no plan to remove these backward compatibility values at
+ * this time.
+ */
+/**@{*/
+
+/**
+ * Formatting options for diff e-mail generation
+ */
+typedef enum {
+ /** Normal patch, the default */
+ GIT_DIFF_FORMAT_EMAIL_NONE = 0,
+
+ /** Don't insert "[PATCH]" in the subject header*/
+ GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER = (1 << 0)
+
+} git_diff_format_email_flags_t;
+
+/**
+ * Options for controlling the formatting of the generated e-mail.
+ */
+typedef struct {
+ unsigned int version;
+
+ /** see `git_diff_format_email_flags_t` above */
+ uint32_t flags;
+
+ /** This patch number */
+ size_t patch_no;
+
+ /** Total number of patches in this series */
+ size_t total_patches;
+
+ /** id to use for the commit */
+ const git_oid *id;
+
+ /** Summary of the change */
+ const char *summary;
+
+ /** Commit message's body */
+ const char *body;
+
+ /** Author of the change */
+ const git_signature *author;
+} git_diff_format_email_options;
+
+#define GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION 1
+#define GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT {GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION, 0, 1, 1, NULL, NULL, NULL, NULL}
+
+/**
+ * Create an e-mail ready patch from a diff.
+ *
+ * @deprecated git_email_create_from_diff
+ * @see git_email_create_from_diff
+ */
+GIT_EXTERN(int) git_diff_format_email(
+ git_buf *out,
+ git_diff *diff,
+ const git_diff_format_email_options *opts);
+
+/**
+ * Create an e-mail ready patch for a commit.
+ *
+ * @deprecated git_email_create_from_commit
+ * @see git_email_create_from_commit
+ */
+GIT_EXTERN(int) git_diff_commit_as_email(
+ git_buf *out,
+ git_repository *repo,
+ git_commit *commit,
+ size_t patch_no,
+ size_t total_patches,
+ uint32_t flags,
+ const git_diff_options *diff_opts);
+
+/**
+ * Initialize git_diff_format_email_options structure
+ *
+ * Initializes a `git_diff_format_email_options` with default values. Equivalent
+ * to creating an instance with GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT.
+ *
+ * @param opts The `git_blame_options` struct to initialize.
+ * @param version The struct version; pass `GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION`.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_diff_format_email_options_init(
+ git_diff_format_email_options *opts,
+ unsigned int version);
+
+/**@}*/
+
+/** @name Deprecated Error Functions and Constants
+ *
+ * These functions and enumeration values are retained for backward
+ * compatibility. The newer versions of these functions and values
+ * should be preferred in all new code.
+ *
+ * There is no plan to remove these backward compatibility values at
+ * this time.
+ */
+/**@{*/
+
+#define GITERR_NONE GIT_ERROR_NONE
+#define GITERR_NOMEMORY GIT_ERROR_NOMEMORY
+#define GITERR_OS GIT_ERROR_OS
+#define GITERR_INVALID GIT_ERROR_INVALID
+#define GITERR_REFERENCE GIT_ERROR_REFERENCE
+#define GITERR_ZLIB GIT_ERROR_ZLIB
+#define GITERR_REPOSITORY GIT_ERROR_REPOSITORY
+#define GITERR_CONFIG GIT_ERROR_CONFIG
+#define GITERR_REGEX GIT_ERROR_REGEX
+#define GITERR_ODB GIT_ERROR_ODB
+#define GITERR_INDEX GIT_ERROR_INDEX
+#define GITERR_OBJECT GIT_ERROR_OBJECT
+#define GITERR_NET GIT_ERROR_NET
+#define GITERR_TAG GIT_ERROR_TAG
+#define GITERR_TREE GIT_ERROR_TREE
+#define GITERR_INDEXER GIT_ERROR_INDEXER
+#define GITERR_SSL GIT_ERROR_SSL
+#define GITERR_SUBMODULE GIT_ERROR_SUBMODULE
+#define GITERR_THREAD GIT_ERROR_THREAD
+#define GITERR_STASH GIT_ERROR_STASH
+#define GITERR_CHECKOUT GIT_ERROR_CHECKOUT
+#define GITERR_FETCHHEAD GIT_ERROR_FETCHHEAD
+#define GITERR_MERGE GIT_ERROR_MERGE
+#define GITERR_SSH GIT_ERROR_SSH
+#define GITERR_FILTER GIT_ERROR_FILTER
+#define GITERR_REVERT GIT_ERROR_REVERT
+#define GITERR_CALLBACK GIT_ERROR_CALLBACK
+#define GITERR_CHERRYPICK GIT_ERROR_CHERRYPICK
+#define GITERR_DESCRIBE GIT_ERROR_DESCRIBE
+#define GITERR_REBASE GIT_ERROR_REBASE
+#define GITERR_FILESYSTEM GIT_ERROR_FILESYSTEM
+#define GITERR_PATCH GIT_ERROR_PATCH
+#define GITERR_WORKTREE GIT_ERROR_WORKTREE
+#define GITERR_SHA1 GIT_ERROR_SHA1
+
+#define GIT_ERROR_SHA1 GIT_ERROR_SHA
+
+/**
+ * Return the last `git_error` object that was generated for the
+ * current thread. This is an alias of `git_error_last` and is
+ * preserved for backward compatibility.
+ *
+ * This function is deprecated, but there is no plan to remove this
+ * function at this time.
+ *
+ * @deprecated Use git_error_last
+ * @see git_error_last
+ */
+GIT_EXTERN(const git_error *) giterr_last(void);
+
+/**
+ * Clear the last error. This is an alias of `git_error_last` and is
+ * preserved for backward compatibility.
+ *
+ * This function is deprecated, but there is no plan to remove this
+ * function at this time.
+ *
+ * @deprecated Use git_error_clear
+ * @see git_error_clear
+ */
+GIT_EXTERN(void) giterr_clear(void);
+
+/**
+ * Sets the error message to the given string. This is an alias of
+ * `git_error_set_str` and is preserved for backward compatibility.
+ *
+ * This function is deprecated, but there is no plan to remove this
+ * function at this time.
+ *
+ * @deprecated Use git_error_set_str
+ * @see git_error_set_str
+ */
+GIT_EXTERN(void) giterr_set_str(int error_class, const char *string);
+
+/**
+ * Indicates that an out-of-memory situation occurred. This is an alias
+ * of `git_error_set_oom` and is preserved for backward compatibility.
+ *
+ * This function is deprecated, but there is no plan to remove this
+ * function at this time.
+ *
+ * @deprecated Use git_error_set_oom
+ * @see git_error_set_oom
+ */
+GIT_EXTERN(void) giterr_set_oom(void);
+
+/**@}*/
+
+/** @name Deprecated Index Functions and Constants
+ *
+ * These functions and enumeration values are retained for backward
+ * compatibility. The newer versions of these values should be
+ * preferred in all new code.
+ *
+ * There is no plan to remove these backward compatibility values at
+ * this time.
+ */
+/**@{*/
+
+#define GIT_IDXENTRY_NAMEMASK GIT_INDEX_ENTRY_NAMEMASK
+#define GIT_IDXENTRY_STAGEMASK GIT_INDEX_ENTRY_STAGEMASK
+#define GIT_IDXENTRY_STAGESHIFT GIT_INDEX_ENTRY_STAGESHIFT
+
+/* The git_indxentry_flag_t enum */
+#define GIT_IDXENTRY_EXTENDED GIT_INDEX_ENTRY_EXTENDED
+#define GIT_IDXENTRY_VALID GIT_INDEX_ENTRY_VALID
+
+#define GIT_IDXENTRY_STAGE(E) GIT_INDEX_ENTRY_STAGE(E)
+#define GIT_IDXENTRY_STAGE_SET(E,S) GIT_INDEX_ENTRY_STAGE_SET(E,S)
+
+/* The git_idxentry_extended_flag_t enum */
+#define GIT_IDXENTRY_INTENT_TO_ADD GIT_INDEX_ENTRY_INTENT_TO_ADD
+#define GIT_IDXENTRY_SKIP_WORKTREE GIT_INDEX_ENTRY_SKIP_WORKTREE
+#define GIT_IDXENTRY_EXTENDED_FLAGS (GIT_INDEX_ENTRY_INTENT_TO_ADD | GIT_INDEX_ENTRY_SKIP_WORKTREE)
+#define GIT_IDXENTRY_EXTENDED2 (1 << 15)
+#define GIT_IDXENTRY_UPDATE (1 << 0)
+#define GIT_IDXENTRY_REMOVE (1 << 1)
+#define GIT_IDXENTRY_UPTODATE (1 << 2)
+#define GIT_IDXENTRY_ADDED (1 << 3)
+#define GIT_IDXENTRY_HASHED (1 << 4)
+#define GIT_IDXENTRY_UNHASHED (1 << 5)
+#define GIT_IDXENTRY_WT_REMOVE (1 << 6)
+#define GIT_IDXENTRY_CONFLICTED (1 << 7)
+#define GIT_IDXENTRY_UNPACKED (1 << 8)
+#define GIT_IDXENTRY_NEW_SKIP_WORKTREE (1 << 9)
+
+/* The git_index_capability_t enum */
+#define GIT_INDEXCAP_IGNORE_CASE GIT_INDEX_CAPABILITY_IGNORE_CASE
+#define GIT_INDEXCAP_NO_FILEMODE GIT_INDEX_CAPABILITY_NO_FILEMODE
+#define GIT_INDEXCAP_NO_SYMLINKS GIT_INDEX_CAPABILITY_NO_SYMLINKS
+#define GIT_INDEXCAP_FROM_OWNER GIT_INDEX_CAPABILITY_FROM_OWNER
+
+GIT_EXTERN(int) git_index_add_frombuffer(
+ git_index *index,
+ const git_index_entry *entry,
+ const void *buffer, size_t len);
+
+/**@}*/
+
+/** @name Deprecated Object Constants
+ *
+ * These enumeration values are retained for backward compatibility. The
+ * newer versions of these values should be preferred in all new code.
+ *
+ * There is no plan to remove these backward compatibility values at
+ * this time.
+ */
+/**@{*/
+
+#define git_otype git_object_t
+
+#define GIT_OBJ_ANY GIT_OBJECT_ANY
+#define GIT_OBJ_BAD GIT_OBJECT_INVALID
+#define GIT_OBJ__EXT1 0
+#define GIT_OBJ_COMMIT GIT_OBJECT_COMMIT
+#define GIT_OBJ_TREE GIT_OBJECT_TREE
+#define GIT_OBJ_BLOB GIT_OBJECT_BLOB
+#define GIT_OBJ_TAG GIT_OBJECT_TAG
+#define GIT_OBJ__EXT2 5
+#define GIT_OBJ_OFS_DELTA GIT_OBJECT_OFS_DELTA
+#define GIT_OBJ_REF_DELTA GIT_OBJECT_REF_DELTA
+
+/**
+ * Get the size in bytes for the structure which
+ * acts as an in-memory representation of any given
+ * object type.
+ *
+ * For all the core types, this would the equivalent
+ * of calling `sizeof(git_commit)` if the core types
+ * were not opaque on the external API.
+ *
+ * @param type object type to get its size
+ * @return size in bytes of the object
+ */
+GIT_EXTERN(size_t) git_object__size(git_object_t type);
+
+/**@}*/
+
+/** @name Deprecated Remote Functions
+ *
+ * These functions are retained for backward compatibility. The newer
+ * versions of these functions should be preferred in all new code.
+ *
+ * There is no plan to remove these backward compatibility functions at
+ * this time.
+ */
+/**@{*/
+
+/**
+ * Ensure the remote name is well-formed.
+ *
+ * @deprecated Use git_remote_name_is_valid
+ * @param remote_name name to be checked.
+ * @return 1 if the reference name is acceptable; 0 if it isn't
+ */
+GIT_EXTERN(int) git_remote_is_valid_name(const char *remote_name);
+
+/**@}*/
+
+/** @name Deprecated Reference Functions and Constants
+ *
+ * These functions and enumeration values are retained for backward
+ * compatibility. The newer versions of these values should be
+ * preferred in all new code.
+ *
+ * There is no plan to remove these backward compatibility values at
+ * this time.
+ */
+/**@{*/
+
+ /** Basic type of any Git reference. */
+#define git_ref_t git_reference_t
+#define git_reference_normalize_t git_reference_format_t
+
+#define GIT_REF_INVALID GIT_REFERENCE_INVALID
+#define GIT_REF_OID GIT_REFERENCE_DIRECT
+#define GIT_REF_SYMBOLIC GIT_REFERENCE_SYMBOLIC
+#define GIT_REF_LISTALL GIT_REFERENCE_ALL
+
+#define GIT_REF_FORMAT_NORMAL GIT_REFERENCE_FORMAT_NORMAL
+#define GIT_REF_FORMAT_ALLOW_ONELEVEL GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL
+#define GIT_REF_FORMAT_REFSPEC_PATTERN GIT_REFERENCE_FORMAT_REFSPEC_PATTERN
+#define GIT_REF_FORMAT_REFSPEC_SHORTHAND GIT_REFERENCE_FORMAT_REFSPEC_SHORTHAND
+
+/**
+ * Ensure the reference name is well-formed.
+ *
+ * Valid reference names must follow one of two patterns:
+ *
+ * 1. Top-level names must contain only capital letters and underscores,
+ * and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD").
+ * 2. Names prefixed with "refs/" can be almost anything. You must avoid
+ * the characters '~', '^', ':', '\\', '?', '[', and '*', and the
+ * sequences ".." and "@{" which have special meaning to revparse.
+ *
+ * @deprecated Use git_reference_name_is_valid
+ * @param refname name to be checked.
+ * @return 1 if the reference name is acceptable; 0 if it isn't
+ */
+GIT_EXTERN(int) git_reference_is_valid_name(const char *refname);
+
+GIT_EXTERN(int) git_tag_create_frombuffer(
+ git_oid *oid,
+ git_repository *repo,
+ const char *buffer,
+ int force);
+
+/**@}*/
+
+/** @name Deprecated Revspec Constants
+ *
+ * These enumeration values are retained for backward compatibility.
+ * The newer versions of these values should be preferred in all new
+ * code.
+ *
+ * There is no plan to remove these backward compatibility values at
+ * this time.
+ */
+/**@{*/
+
+typedef git_revspec_t git_revparse_mode_t;
+
+#define GIT_REVPARSE_SINGLE GIT_REVSPEC_SINGLE
+#define GIT_REVPARSE_RANGE GIT_REVSPEC_RANGE
+#define GIT_REVPARSE_MERGE_BASE GIT_REVSPEC_MERGE_BASE
+
+/**@}*/
+
+/** @name Deprecated Credential Types
+ *
+ * These types are retained for backward compatibility. The newer
+ * versions of these values should be preferred in all new code.
+ *
+ * There is no plan to remove these backward compatibility values at
+ * this time.
+ */
+/**@{*/
+
+typedef git_credential git_cred;
+typedef git_credential_userpass_plaintext git_cred_userpass_plaintext;
+typedef git_credential_username git_cred_username;
+typedef git_credential_default git_cred_default;
+typedef git_credential_ssh_key git_cred_ssh_key;
+typedef git_credential_ssh_interactive git_cred_ssh_interactive;
+typedef git_credential_ssh_custom git_cred_ssh_custom;
+
+typedef git_credential_acquire_cb git_cred_acquire_cb;
+typedef git_credential_sign_cb git_cred_sign_callback;
+typedef git_credential_sign_cb git_cred_sign_cb;
+typedef git_credential_ssh_interactive_cb git_cred_ssh_interactive_callback;
+typedef git_credential_ssh_interactive_cb git_cred_ssh_interactive_cb;
+
+#define git_credtype_t git_credential_t
+
+#define GIT_CREDTYPE_USERPASS_PLAINTEXT GIT_CREDENTIAL_USERPASS_PLAINTEXT
+#define GIT_CREDTYPE_SSH_KEY GIT_CREDENTIAL_SSH_KEY
+#define GIT_CREDTYPE_SSH_CUSTOM GIT_CREDENTIAL_SSH_CUSTOM
+#define GIT_CREDTYPE_DEFAULT GIT_CREDENTIAL_DEFAULT
+#define GIT_CREDTYPE_SSH_INTERACTIVE GIT_CREDENTIAL_SSH_INTERACTIVE
+#define GIT_CREDTYPE_USERNAME GIT_CREDENTIAL_USERNAME
+#define GIT_CREDTYPE_SSH_MEMORY GIT_CREDENTIAL_SSH_MEMORY
+
+GIT_EXTERN(void) git_cred_free(git_credential *cred);
+GIT_EXTERN(int) git_cred_has_username(git_credential *cred);
+GIT_EXTERN(const char *) git_cred_get_username(git_credential *cred);
+GIT_EXTERN(int) git_cred_userpass_plaintext_new(
+ git_credential **out,
+ const char *username,
+ const char *password);
+GIT_EXTERN(int) git_cred_default_new(git_credential **out);
+GIT_EXTERN(int) git_cred_username_new(git_credential **out, const char *username);
+GIT_EXTERN(int) git_cred_ssh_key_new(
+ git_credential **out,
+ const char *username,
+ const char *publickey,
+ const char *privatekey,
+ const char *passphrase);
+GIT_EXTERN(int) git_cred_ssh_key_memory_new(
+ git_credential **out,
+ const char *username,
+ const char *publickey,
+ const char *privatekey,
+ const char *passphrase);
+GIT_EXTERN(int) git_cred_ssh_interactive_new(
+ git_credential **out,
+ const char *username,
+ git_credential_ssh_interactive_cb prompt_callback,
+ void *payload);
+GIT_EXTERN(int) git_cred_ssh_key_from_agent(
+ git_credential **out,
+ const char *username);
+GIT_EXTERN(int) git_cred_ssh_custom_new(
+ git_credential **out,
+ const char *username,
+ const char *publickey,
+ size_t publickey_len,
+ git_credential_sign_cb sign_callback,
+ void *payload);
+
+/* Deprecated Credential Helper Types */
+
+typedef git_credential_userpass_payload git_cred_userpass_payload;
+
+GIT_EXTERN(int) git_cred_userpass(
+ git_credential **out,
+ const char *url,
+ const char *user_from_url,
+ unsigned int allowed_types,
+ void *payload);
+
+/**@}*/
+
+/** @name Deprecated Trace Callback Types
+ *
+ * These types are retained for backward compatibility. The newer
+ * versions of these values should be preferred in all new code.
+ *
+ * There is no plan to remove these backward compatibility values at
+ * this time.
+ */
+/**@{*/
+
+typedef git_trace_cb git_trace_callback;
+
+/**@}*/
+
+/** @name Deprecated Object ID Types
+ *
+ * These types are retained for backward compatibility. The newer
+ * versions of these values should be preferred in all new code.
+ *
+ * There is no plan to remove these backward compatibility values at
+ * this time.
+ */
+/**@{*/
+
+#ifndef GIT_EXPERIMENTAL_SHA256
+# define GIT_OID_RAWSZ GIT_OID_SHA1_SIZE
+# define GIT_OID_HEXSZ GIT_OID_SHA1_HEXSIZE
+# define GIT_OID_HEX_ZERO GIT_OID_SHA1_HEXZERO
+#endif
+
+GIT_EXTERN(int) git_oid_iszero(const git_oid *id);
+
+/**@}*/
+
+/** @name Deprecated OID Array Functions
+ *
+ * These types are retained for backward compatibility. The newer
+ * versions of these values should be preferred in all new code.
+ *
+ * There is no plan to remove these backward compatibility values at
+ * this time.
+ */
+/**@{*/
+
+/**
+ * Free the memory referred to by the git_oidarray. This is an alias of
+ * `git_oidarray_dispose` and is preserved for backward compatibility.
+ *
+ * This function is deprecated, but there is no plan to remove this
+ * function at this time.
+ *
+ * @deprecated Use git_oidarray_dispose
+ * @see git_oidarray_dispose
+ */
+GIT_EXTERN(void) git_oidarray_free(git_oidarray *array);
+
+/**@}*/
+
+/** @name Deprecated Transfer Progress Types
+ *
+ * These types are retained for backward compatibility. The newer
+ * versions of these values should be preferred in all new code.
+ *
+ * There is no plan to remove these backward compatibility values at
+ * this time.
+ */
+/**@{*/
+
+/**
+ * This structure is used to provide callers information about the
+ * progress of indexing a packfile.
+ *
+ * This type is deprecated, but there is no plan to remove this
+ * type definition at this time.
+ */
+typedef git_indexer_progress git_transfer_progress;
+
+/**
+ * Type definition for progress callbacks during indexing.
+ *
+ * This type is deprecated, but there is no plan to remove this
+ * type definition at this time.
+ */
+typedef git_indexer_progress_cb git_transfer_progress_cb;
+
+/**
+ * Type definition for push transfer progress callbacks.
+ *
+ * This type is deprecated, but there is no plan to remove this
+ * type definition at this time.
+ */
+typedef git_push_transfer_progress_cb git_push_transfer_progress;
+
+ /** The type of a remote completion event */
+#define git_remote_completion_type git_remote_completion_t
+
+/**
+ * Callback for listing the remote heads
+ */
+typedef int GIT_CALLBACK(git_headlist_cb)(git_remote_head *rhead, void *payload);
+
+/**@}*/
+
+/** @name Deprecated String Array Functions
+ *
+ * These types are retained for backward compatibility. The newer
+ * versions of these values should be preferred in all new code.
+ *
+ * There is no plan to remove these backward compatibility values at
+ * this time.
+ */
+/**@{*/
+
+/**
+ * Copy a string array object from source to target.
+ *
+ * This function is deprecated, but there is no plan to remove this
+ * function at this time.
+ *
+ * @param tgt target
+ * @param src source
+ * @return 0 on success, < 0 on allocation failure
+ */
+GIT_EXTERN(int) git_strarray_copy(git_strarray *tgt, const git_strarray *src);
+
+/**
+ * Free the memory referred to by the git_strarray. This is an alias of
+ * `git_strarray_dispose` and is preserved for backward compatibility.
+ *
+ * This function is deprecated, but there is no plan to remove this
+ * function at this time.
+ *
+ * @deprecated Use git_strarray_dispose
+ * @see git_strarray_dispose
+ */
+GIT_EXTERN(void) git_strarray_free(git_strarray *array);
+
+/**@}*/
+
+/** @name Deprecated Options Initialization Functions
+ *
+ * These functions are retained for backward compatibility. The newer
+ * versions of these functions should be preferred in all new code.
+ *
+ * There is no plan to remove these backward compatibility functions at
+ * this time.
+ */
+/**@{*/
+
+GIT_EXTERN(int) git_blame_init_options(git_blame_options *opts, unsigned int version);
+GIT_EXTERN(int) git_checkout_init_options(git_checkout_options *opts, unsigned int version);
+GIT_EXTERN(int) git_cherrypick_init_options(git_cherrypick_options *opts, unsigned int version);
+GIT_EXTERN(int) git_clone_init_options(git_clone_options *opts, unsigned int version);
+GIT_EXTERN(int) git_describe_init_options(git_describe_options *opts, unsigned int version);
+GIT_EXTERN(int) git_describe_init_format_options(git_describe_format_options *opts, unsigned int version);
+GIT_EXTERN(int) git_diff_init_options(git_diff_options *opts, unsigned int version);
+GIT_EXTERN(int) git_diff_find_init_options(git_diff_find_options *opts, unsigned int version);
+GIT_EXTERN(int) git_diff_format_email_init_options(git_diff_format_email_options *opts, unsigned int version);
+GIT_EXTERN(int) git_diff_patchid_init_options(git_diff_patchid_options *opts, unsigned int version);
+GIT_EXTERN(int) git_fetch_init_options(git_fetch_options *opts, unsigned int version);
+GIT_EXTERN(int) git_indexer_init_options(git_indexer_options *opts, unsigned int version);
+GIT_EXTERN(int) git_merge_init_options(git_merge_options *opts, unsigned int version);
+GIT_EXTERN(int) git_merge_file_init_input(git_merge_file_input *input, unsigned int version);
+GIT_EXTERN(int) git_merge_file_init_options(git_merge_file_options *opts, unsigned int version);
+GIT_EXTERN(int) git_proxy_init_options(git_proxy_options *opts, unsigned int version);
+GIT_EXTERN(int) git_push_init_options(git_push_options *opts, unsigned int version);
+GIT_EXTERN(int) git_rebase_init_options(git_rebase_options *opts, unsigned int version);
+GIT_EXTERN(int) git_remote_create_init_options(git_remote_create_options *opts, unsigned int version);
+GIT_EXTERN(int) git_repository_init_init_options(git_repository_init_options *opts, unsigned int version);
+GIT_EXTERN(int) git_revert_init_options(git_revert_options *opts, unsigned int version);
+GIT_EXTERN(int) git_stash_apply_init_options(git_stash_apply_options *opts, unsigned int version);
+GIT_EXTERN(int) git_status_init_options(git_status_options *opts, unsigned int version);
+GIT_EXTERN(int) git_submodule_update_init_options(git_submodule_update_options *opts, unsigned int version);
+GIT_EXTERN(int) git_worktree_add_init_options(git_worktree_add_options *opts, unsigned int version);
+GIT_EXTERN(int) git_worktree_prune_init_options(git_worktree_prune_options *opts, unsigned int version);
+
+/**@}*/
+
+/** @} */
+GIT_END_DECL
+
+#endif
+
+#endif
diff --git a/include/git2/describe.h b/include/git2/describe.h
new file mode 100644
index 0000000..7a796f1
--- /dev/null
+++ b/include/git2/describe.h
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_describe_h__
+#define INCLUDE_git_describe_h__
+
+#include "common.h"
+#include "types.h"
+#include "buffer.h"
+
+/**
+ * @file git2/describe.h
+ * @brief Git describing routines
+ * @defgroup git_describe Git describing routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Reference lookup strategy
+ *
+ * These behave like the --tags and --all options to git-describe,
+ * namely they say to look for any reference in either refs/tags/ or
+ * refs/ respectively.
+ */
+typedef enum {
+ GIT_DESCRIBE_DEFAULT,
+ GIT_DESCRIBE_TAGS,
+ GIT_DESCRIBE_ALL
+} git_describe_strategy_t;
+
+/**
+ * Describe options structure
+ *
+ * Initialize with `GIT_DESCRIBE_OPTIONS_INIT`. Alternatively, you can
+ * use `git_describe_options_init`.
+ *
+ */
+typedef struct git_describe_options {
+ unsigned int version;
+
+ unsigned int max_candidates_tags; /**< default: 10 */
+ unsigned int describe_strategy; /**< default: GIT_DESCRIBE_DEFAULT */
+ const char *pattern;
+ /**
+ * When calculating the distance from the matching tag or
+ * reference, only walk down the first-parent ancestry.
+ */
+ int only_follow_first_parent;
+ /**
+ * If no matching tag or reference is found, the describe
+ * operation would normally fail. If this option is set, it
+ * will instead fall back to showing the full id of the
+ * commit.
+ */
+ int show_commit_oid_as_fallback;
+} git_describe_options;
+
+#define GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS 10
+#define GIT_DESCRIBE_DEFAULT_ABBREVIATED_SIZE 7
+
+#define GIT_DESCRIBE_OPTIONS_VERSION 1
+#define GIT_DESCRIBE_OPTIONS_INIT { \
+ GIT_DESCRIBE_OPTIONS_VERSION, \
+ GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS, \
+}
+
+/**
+ * Initialize git_describe_options structure
+ *
+ * Initializes a `git_describe_options` with default values. Equivalent to creating
+ * an instance with GIT_DESCRIBE_OPTIONS_INIT.
+ *
+ * @param opts The `git_describe_options` struct to initialize.
+ * @param version The struct version; pass `GIT_DESCRIBE_OPTIONS_VERSION`.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_describe_options_init(git_describe_options *opts, unsigned int version);
+
+/**
+ * Describe format options structure
+ *
+ * Initialize with `GIT_DESCRIBE_FORMAT_OPTIONS_INIT`. Alternatively, you can
+ * use `git_describe_format_options_init`.
+ *
+ */
+typedef struct {
+ unsigned int version;
+
+ /**
+ * Size of the abbreviated commit id to use. This value is the
+ * lower bound for the length of the abbreviated string. The
+ * default is 7.
+ */
+ unsigned int abbreviated_size;
+
+ /**
+ * Set to use the long format even when a shorter name could be used.
+ */
+ int always_use_long_format;
+
+ /**
+ * If the workdir is dirty and this is set, this string will
+ * be appended to the description string.
+ */
+ const char *dirty_suffix;
+} git_describe_format_options;
+
+#define GIT_DESCRIBE_FORMAT_OPTIONS_VERSION 1
+#define GIT_DESCRIBE_FORMAT_OPTIONS_INIT { \
+ GIT_DESCRIBE_FORMAT_OPTIONS_VERSION, \
+ GIT_DESCRIBE_DEFAULT_ABBREVIATED_SIZE, \
+ }
+
+/**
+ * Initialize git_describe_format_options structure
+ *
+ * Initializes a `git_describe_format_options` with default values. Equivalent to creating
+ * an instance with GIT_DESCRIBE_FORMAT_OPTIONS_INIT.
+ *
+ * @param opts The `git_describe_format_options` struct to initialize.
+ * @param version The struct version; pass `GIT_DESCRIBE_FORMAT_OPTIONS_VERSION`.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_describe_format_options_init(git_describe_format_options *opts, unsigned int version);
+
+/**
+ * A struct that stores the result of a describe operation.
+ */
+typedef struct git_describe_result git_describe_result;
+
+/**
+ * Describe a commit
+ *
+ * Perform the describe operation on the given committish object.
+ *
+ * @param result pointer to store the result. You must free this once
+ * you're done with it.
+ * @param committish a committish to describe
+ * @param opts the lookup options (or NULL for defaults)
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_describe_commit(
+ git_describe_result **result,
+ git_object *committish,
+ git_describe_options *opts);
+
+/**
+ * Describe a commit
+ *
+ * Perform the describe operation on the current commit and the
+ * worktree. After performing describe on HEAD, a status is run and the
+ * description is considered to be dirty if there are.
+ *
+ * @param out pointer to store the result. You must free this once
+ * you're done with it.
+ * @param repo the repository in which to perform the describe
+ * @param opts the lookup options (or NULL for defaults)
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_describe_workdir(
+ git_describe_result **out,
+ git_repository *repo,
+ git_describe_options *opts);
+
+/**
+ * Print the describe result to a buffer
+ *
+ * @param out The buffer to store the result
+ * @param result the result from `git_describe_commit()` or
+ * `git_describe_workdir()`.
+ * @param opts the formatting options (or NULL for defaults)
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_describe_format(
+ git_buf *out,
+ const git_describe_result *result,
+ const git_describe_format_options *opts);
+
+/**
+ * Free the describe result.
+ *
+ * @param result The result to free.
+ */
+GIT_EXTERN(void) git_describe_result_free(git_describe_result *result);
+
+/** @} */
+GIT_END_DECL
+
+#endif
diff --git a/include/git2/diff.h b/include/git2/diff.h
new file mode 100644
index 0000000..384b6e7
--- /dev/null
+++ b/include/git2/diff.h
@@ -0,0 +1,1477 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_diff_h__
+#define INCLUDE_git_diff_h__
+
+#include "common.h"
+#include "types.h"
+#include "oid.h"
+#include "tree.h"
+#include "refs.h"
+
+/**
+ * @file git2/diff.h
+ * @brief Git tree and file differencing routines.
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Flags for diff options. A combination of these flags can be passed
+ * in via the `flags` value in the `git_diff_options`.
+ */
+typedef enum {
+ /** Normal diff, the default */
+ GIT_DIFF_NORMAL = 0,
+
+ /*
+ * Options controlling which files will be in the diff
+ */
+
+ /** Reverse the sides of the diff */
+ GIT_DIFF_REVERSE = (1u << 0),
+
+ /** Include ignored files in the diff */
+ GIT_DIFF_INCLUDE_IGNORED = (1u << 1),
+
+ /** Even with GIT_DIFF_INCLUDE_IGNORED, an entire ignored directory
+ * will be marked with only a single entry in the diff; this flag
+ * adds all files under the directory as IGNORED entries, too.
+ */
+ GIT_DIFF_RECURSE_IGNORED_DIRS = (1u << 2),
+
+ /** Include untracked files in the diff */
+ GIT_DIFF_INCLUDE_UNTRACKED = (1u << 3),
+
+ /** Even with GIT_DIFF_INCLUDE_UNTRACKED, an entire untracked
+ * directory will be marked with only a single entry in the diff
+ * (a la what core Git does in `git status`); this flag adds *all*
+ * files under untracked directories as UNTRACKED entries, too.
+ */
+ GIT_DIFF_RECURSE_UNTRACKED_DIRS = (1u << 4),
+
+ /** Include unmodified files in the diff */
+ GIT_DIFF_INCLUDE_UNMODIFIED = (1u << 5),
+
+ /** Normally, a type change between files will be converted into a
+ * DELETED record for the old and an ADDED record for the new; this
+ * options enabled the generation of TYPECHANGE delta records.
+ */
+ GIT_DIFF_INCLUDE_TYPECHANGE = (1u << 6),
+
+ /** Even with GIT_DIFF_INCLUDE_TYPECHANGE, blob->tree changes still
+ * generally show as a DELETED blob. This flag tries to correctly
+ * label blob->tree transitions as TYPECHANGE records with new_file's
+ * mode set to tree. Note: the tree SHA will not be available.
+ */
+ GIT_DIFF_INCLUDE_TYPECHANGE_TREES = (1u << 7),
+
+ /** Ignore file mode changes */
+ GIT_DIFF_IGNORE_FILEMODE = (1u << 8),
+
+ /** Treat all submodules as unmodified */
+ GIT_DIFF_IGNORE_SUBMODULES = (1u << 9),
+
+ /** Use case insensitive filename comparisons */
+ GIT_DIFF_IGNORE_CASE = (1u << 10),
+
+ /** May be combined with `GIT_DIFF_IGNORE_CASE` to specify that a file
+ * that has changed case will be returned as an add/delete pair.
+ */
+ GIT_DIFF_INCLUDE_CASECHANGE = (1u << 11),
+
+ /** If the pathspec is set in the diff options, this flags indicates
+ * that the paths will be treated as literal paths instead of
+ * fnmatch patterns. Each path in the list must either be a full
+ * path to a file or a directory. (A trailing slash indicates that
+ * the path will _only_ match a directory). If a directory is
+ * specified, all children will be included.
+ */
+ GIT_DIFF_DISABLE_PATHSPEC_MATCH = (1u << 12),
+
+ /** Disable updating of the `binary` flag in delta records. This is
+ * useful when iterating over a diff if you don't need hunk and data
+ * callbacks and want to avoid having to load file completely.
+ */
+ GIT_DIFF_SKIP_BINARY_CHECK = (1u << 13),
+
+ /** When diff finds an untracked directory, to match the behavior of
+ * core Git, it scans the contents for IGNORED and UNTRACKED files.
+ * If *all* contents are IGNORED, then the directory is IGNORED; if
+ * any contents are not IGNORED, then the directory is UNTRACKED.
+ * This is extra work that may not matter in many cases. This flag
+ * turns off that scan and immediately labels an untracked directory
+ * as UNTRACKED (changing the behavior to not match core Git).
+ */
+ GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS = (1u << 14),
+
+ /** When diff finds a file in the working directory with stat
+ * information different from the index, but the OID ends up being the
+ * same, write the correct stat information into the index. Note:
+ * without this flag, diff will always leave the index untouched.
+ */
+ GIT_DIFF_UPDATE_INDEX = (1u << 15),
+
+ /** Include unreadable files in the diff */
+ GIT_DIFF_INCLUDE_UNREADABLE = (1u << 16),
+
+ /** Include unreadable files in the diff */
+ GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED = (1u << 17),
+
+ /*
+ * Options controlling how output will be generated
+ */
+
+ /** Use a heuristic that takes indentation and whitespace into account
+ * which generally can produce better diffs when dealing with ambiguous
+ * diff hunks.
+ */
+ GIT_DIFF_INDENT_HEURISTIC = (1u << 18),
+
+ /** Ignore blank lines */
+ GIT_DIFF_IGNORE_BLANK_LINES = (1u << 19),
+
+ /** Treat all files as text, disabling binary attributes & detection */
+ GIT_DIFF_FORCE_TEXT = (1u << 20),
+ /** Treat all files as binary, disabling text diffs */
+ GIT_DIFF_FORCE_BINARY = (1u << 21),
+
+ /** Ignore all whitespace */
+ GIT_DIFF_IGNORE_WHITESPACE = (1u << 22),
+ /** Ignore changes in amount of whitespace */
+ GIT_DIFF_IGNORE_WHITESPACE_CHANGE = (1u << 23),
+ /** Ignore whitespace at end of line */
+ GIT_DIFF_IGNORE_WHITESPACE_EOL = (1u << 24),
+
+ /** When generating patch text, include the content of untracked
+ * files. This automatically turns on GIT_DIFF_INCLUDE_UNTRACKED but
+ * it does not turn on GIT_DIFF_RECURSE_UNTRACKED_DIRS. Add that
+ * flag if you want the content of every single UNTRACKED file.
+ */
+ GIT_DIFF_SHOW_UNTRACKED_CONTENT = (1u << 25),
+
+ /** When generating output, include the names of unmodified files if
+ * they are included in the git_diff. Normally these are skipped in
+ * the formats that list files (e.g. name-only, name-status, raw).
+ * Even with this, these will not be included in patch format.
+ */
+ GIT_DIFF_SHOW_UNMODIFIED = (1u << 26),
+
+ /** Use the "patience diff" algorithm */
+ GIT_DIFF_PATIENCE = (1u << 28),
+ /** Take extra time to find minimal diff */
+ GIT_DIFF_MINIMAL = (1u << 29),
+
+ /** Include the necessary deflate / delta information so that `git-apply`
+ * can apply given diff information to binary files.
+ */
+ GIT_DIFF_SHOW_BINARY = (1u << 30)
+} git_diff_option_t;
+
+/**
+ * The diff object that contains all individual file deltas.
+ *
+ * A `diff` represents the cumulative list of differences between two
+ * snapshots of a repository (possibly filtered by a set of file name
+ * patterns).
+ *
+ * Calculating diffs is generally done in two phases: building a list of
+ * diffs then traversing it. This makes is easier to share logic across
+ * the various types of diffs (tree vs tree, workdir vs index, etc.), and
+ * also allows you to insert optional diff post-processing phases,
+ * such as rename detection, in between the steps. When you are done with
+ * a diff object, it must be freed.
+ *
+ * This is an opaque structure which will be allocated by one of the diff
+ * generator functions below (such as `git_diff_tree_to_tree`). You are
+ * responsible for releasing the object memory when done, using the
+ * `git_diff_free()` function.
+ *
+ */
+typedef struct git_diff git_diff;
+
+/**
+ * Flags for the delta object and the file objects on each side.
+ *
+ * These flags are used for both the `flags` value of the `git_diff_delta`
+ * and the flags for the `git_diff_file` objects representing the old and
+ * new sides of the delta. Values outside of this public range should be
+ * considered reserved for internal or future use.
+ */
+typedef enum {
+ GIT_DIFF_FLAG_BINARY = (1u << 0), /**< file(s) treated as binary data */
+ GIT_DIFF_FLAG_NOT_BINARY = (1u << 1), /**< file(s) treated as text data */
+ GIT_DIFF_FLAG_VALID_ID = (1u << 2), /**< `id` value is known correct */
+ GIT_DIFF_FLAG_EXISTS = (1u << 3), /**< file exists at this side of the delta */
+ GIT_DIFF_FLAG_VALID_SIZE = (1u << 4) /**< file size value is known correct */
+} git_diff_flag_t;
+
+/**
+ * What type of change is described by a git_diff_delta?
+ *
+ * `GIT_DELTA_RENAMED` and `GIT_DELTA_COPIED` will only show up if you run
+ * `git_diff_find_similar()` on the diff object.
+ *
+ * `GIT_DELTA_TYPECHANGE` only shows up given `GIT_DIFF_INCLUDE_TYPECHANGE`
+ * in the option flags (otherwise type changes will be split into ADDED /
+ * DELETED pairs).
+ */
+typedef enum {
+ GIT_DELTA_UNMODIFIED = 0, /**< no changes */
+ GIT_DELTA_ADDED = 1, /**< entry does not exist in old version */
+ GIT_DELTA_DELETED = 2, /**< entry does not exist in new version */
+ GIT_DELTA_MODIFIED = 3, /**< entry content changed between old and new */
+ GIT_DELTA_RENAMED = 4, /**< entry was renamed between old and new */
+ GIT_DELTA_COPIED = 5, /**< entry was copied from another old entry */
+ GIT_DELTA_IGNORED = 6, /**< entry is ignored item in workdir */
+ GIT_DELTA_UNTRACKED = 7, /**< entry is untracked item in workdir */
+ GIT_DELTA_TYPECHANGE = 8, /**< type of entry changed between old and new */
+ GIT_DELTA_UNREADABLE = 9, /**< entry is unreadable */
+ GIT_DELTA_CONFLICTED = 10 /**< entry in the index is conflicted */
+} git_delta_t;
+
+/**
+ * Description of one side of a delta.
+ *
+ * Although this is called a "file", it could represent a file, a symbolic
+ * link, a submodule commit id, or even a tree (although that only if you
+ * are tracking type changes or ignored/untracked directories).
+ */
+typedef struct {
+ /**
+ * The `git_oid` of the item. If the entry represents an
+ * absent side of a diff (e.g. the `old_file` of a `GIT_DELTA_ADDED` delta),
+ * then the oid will be zeroes.
+ */
+ git_oid id;
+
+ /**
+ * The NUL-terminated path to the entry relative to the working
+ * directory of the repository.
+ */
+ const char *path;
+
+ /**
+ * The size of the entry in bytes.
+ */
+ git_object_size_t size;
+
+ /**
+ * A combination of the `git_diff_flag_t` types
+ */
+ uint32_t flags;
+
+ /**
+ * Roughly, the stat() `st_mode` value for the item. This will
+ * be restricted to one of the `git_filemode_t` values.
+ */
+ uint16_t mode;
+
+ /**
+ * Represents the known length of the `id` field, when
+ * converted to a hex string. It is generally `GIT_OID_SHA1_HEXSIZE`, unless this
+ * delta was created from reading a patch file, in which case it may be
+ * abbreviated to something reasonable, like 7 characters.
+ */
+ uint16_t id_abbrev;
+} git_diff_file;
+
+/**
+ * Description of changes to one entry.
+ *
+ * A `delta` is a file pair with an old and new revision. The old version
+ * may be absent if the file was just created and the new version may be
+ * absent if the file was deleted. A diff is mostly just a list of deltas.
+ *
+ * When iterating over a diff, this will be passed to most callbacks and
+ * you can use the contents to understand exactly what has changed.
+ *
+ * The `old_file` represents the "from" side of the diff and the `new_file`
+ * represents to "to" side of the diff. What those means depend on the
+ * function that was used to generate the diff and will be documented below.
+ * You can also use the `GIT_DIFF_REVERSE` flag to flip it around.
+ *
+ * Although the two sides of the delta are named "old_file" and "new_file",
+ * they actually may correspond to entries that represent a file, a symbolic
+ * link, a submodule commit id, or even a tree (if you are tracking type
+ * changes or ignored/untracked directories).
+ *
+ * Under some circumstances, in the name of efficiency, not all fields will
+ * be filled in, but we generally try to fill in as much as possible. One
+ * example is that the "flags" field may not have either the `BINARY` or the
+ * `NOT_BINARY` flag set to avoid examining file contents if you do not pass
+ * in hunk and/or line callbacks to the diff foreach iteration function. It
+ * will just use the git attributes for those files.
+ *
+ * The similarity score is zero unless you call `git_diff_find_similar()`
+ * which does a similarity analysis of files in the diff. Use that
+ * function to do rename and copy detection, and to split heavily modified
+ * files in add/delete pairs. After that call, deltas with a status of
+ * GIT_DELTA_RENAMED or GIT_DELTA_COPIED will have a similarity score
+ * between 0 and 100 indicating how similar the old and new sides are.
+ *
+ * If you ask `git_diff_find_similar` to find heavily modified files to
+ * break, but to not *actually* break the records, then GIT_DELTA_MODIFIED
+ * records may have a non-zero similarity score if the self-similarity is
+ * below the split threshold. To display this value like core Git, invert
+ * the score (a la `printf("M%03d", 100 - delta->similarity)`).
+ */
+typedef struct {
+ git_delta_t status;
+ uint32_t flags; /**< git_diff_flag_t values */
+ uint16_t similarity; /**< for RENAMED and COPIED, value 0-100 */
+ uint16_t nfiles; /**< number of files in this delta */
+ git_diff_file old_file;
+ git_diff_file new_file;
+} git_diff_delta;
+
+/**
+ * Diff notification callback function.
+ *
+ * The callback will be called for each file, just before the `git_diff_delta`
+ * gets inserted into the diff.
+ *
+ * When the callback:
+ * - returns < 0, the diff process will be aborted.
+ * - returns > 0, the delta will not be inserted into the diff, but the
+ * diff process continues.
+ * - returns 0, the delta is inserted into the diff, and the diff process
+ * continues.
+ */
+typedef int GIT_CALLBACK(git_diff_notify_cb)(
+ const git_diff *diff_so_far,
+ const git_diff_delta *delta_to_add,
+ const char *matched_pathspec,
+ void *payload);
+
+/**
+ * Diff progress callback.
+ *
+ * Called before each file comparison.
+ *
+ * @param diff_so_far The diff being generated.
+ * @param old_path The path to the old file or NULL.
+ * @param new_path The path to the new file or NULL.
+ * @return Non-zero to abort the diff.
+ */
+typedef int GIT_CALLBACK(git_diff_progress_cb)(
+ const git_diff *diff_so_far,
+ const char *old_path,
+ const char *new_path,
+ void *payload);
+
+/**
+ * Structure describing options about how the diff should be executed.
+ *
+ * Setting all values of the structure to zero will yield the default
+ * values. Similarly, passing NULL for the options structure will
+ * give the defaults. The default values are marked below.
+ *
+ */
+typedef struct {
+ unsigned int version; /**< version for the struct */
+
+ /**
+ * A combination of `git_diff_option_t` values above.
+ * Defaults to GIT_DIFF_NORMAL
+ */
+ uint32_t flags;
+
+ /* options controlling which files are in the diff */
+
+ /** Overrides the submodule ignore setting for all submodules in the diff. */
+ git_submodule_ignore_t ignore_submodules;
+
+ /**
+ * An array of paths / fnmatch patterns to constrain diff.
+ * All paths are included by default.
+ */
+ git_strarray pathspec;
+
+ /**
+ * An optional callback function, notifying the consumer of changes to
+ * the diff as new deltas are added.
+ */
+ git_diff_notify_cb notify_cb;
+
+ /**
+ * An optional callback function, notifying the consumer of which files
+ * are being examined as the diff is generated.
+ */
+ git_diff_progress_cb progress_cb;
+
+ /** The payload to pass to the callback functions. */
+ void *payload;
+
+ /* options controlling how to diff text is generated */
+
+ /**
+ * The number of unchanged lines that define the boundary of a hunk
+ * (and to display before and after). Defaults to 3.
+ */
+ uint32_t context_lines;
+ /**
+ * The maximum number of unchanged lines between hunk boundaries before
+ * the hunks will be merged into one. Defaults to 0.
+ */
+ uint32_t interhunk_lines;
+
+ /**
+ * The object ID type to emit in diffs; this is used by functions
+ * that operate without a repository - namely `git_diff_buffers`,
+ * or `git_diff_blobs` and `git_diff_blob_to_buffer` when one blob
+ * is `NULL`.
+ *
+ * This may be omitted (set to `0`). If a repository is available,
+ * the object ID format of the repository will be used. If no
+ * repository is available then the default is `GIT_OID_SHA`.
+ *
+ * If this is specified and a repository is available, then the
+ * specified `oid_type` must match the repository's object ID
+ * format.
+ */
+ git_oid_t oid_type;
+
+ /**
+ * The abbreviation length to use when formatting object ids.
+ * Defaults to the value of 'core.abbrev' from the config, or 7 if unset.
+ */
+ uint16_t id_abbrev;
+
+ /**
+ * A size (in bytes) above which a blob will be marked as binary
+ * automatically; pass a negative value to disable.
+ * Defaults to 512MB.
+ */
+ git_off_t max_size;
+
+ /**
+ * The virtual "directory" prefix for old file names in hunk headers.
+ * Default is "a".
+ */
+ const char *old_prefix;
+
+ /**
+ * The virtual "directory" prefix for new file names in hunk headers.
+ * Defaults to "b".
+ */
+ const char *new_prefix;
+} git_diff_options;
+
+/* The current version of the diff options structure */
+#define GIT_DIFF_OPTIONS_VERSION 1
+
+/* Stack initializer for diff options. Alternatively use
+ * `git_diff_options_init` programmatic initialization.
+ */
+#define GIT_DIFF_OPTIONS_INIT \
+ {GIT_DIFF_OPTIONS_VERSION, 0, GIT_SUBMODULE_IGNORE_UNSPECIFIED, {NULL,0}, NULL, NULL, NULL, 3}
+
+/**
+ * Initialize git_diff_options structure
+ *
+ * Initializes a `git_diff_options` with default values. Equivalent to creating
+ * an instance with GIT_DIFF_OPTIONS_INIT.
+ *
+ * @param opts The `git_diff_options` struct to initialize.
+ * @param version The struct version; pass `GIT_DIFF_OPTIONS_VERSION`.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_diff_options_init(
+ git_diff_options *opts,
+ unsigned int version);
+
+/**
+ * When iterating over a diff, callback that will be made per file.
+ *
+ * @param delta A pointer to the delta data for the file
+ * @param progress Goes from 0 to 1 over the diff
+ * @param payload User-specified pointer from foreach function
+ */
+typedef int GIT_CALLBACK(git_diff_file_cb)(
+ const git_diff_delta *delta,
+ float progress,
+ void *payload);
+
+#define GIT_DIFF_HUNK_HEADER_SIZE 128
+
+/**
+ * When producing a binary diff, the binary data returned will be
+ * either the deflated full ("literal") contents of the file, or
+ * the deflated binary delta between the two sides (whichever is
+ * smaller).
+ */
+typedef enum {
+ /** There is no binary delta. */
+ GIT_DIFF_BINARY_NONE,
+
+ /** The binary data is the literal contents of the file. */
+ GIT_DIFF_BINARY_LITERAL,
+
+ /** The binary data is the delta from one side to the other. */
+ GIT_DIFF_BINARY_DELTA
+} git_diff_binary_t;
+
+/** The contents of one of the files in a binary diff. */
+typedef struct {
+ /** The type of binary data for this file. */
+ git_diff_binary_t type;
+
+ /** The binary data, deflated. */
+ const char *data;
+
+ /** The length of the binary data. */
+ size_t datalen;
+
+ /** The length of the binary data after inflation. */
+ size_t inflatedlen;
+} git_diff_binary_file;
+
+/**
+ * Structure describing the binary contents of a diff.
+ *
+ * A `binary` file / delta is a file (or pair) for which no text diffs
+ * should be generated. A diff can contain delta entries that are
+ * binary, but no diff content will be output for those files. There is
+ * a base heuristic for binary detection and you can further tune the
+ * behavior with git attributes or diff flags and option settings.
+ */
+typedef struct {
+ /**
+ * Whether there is data in this binary structure or not.
+ *
+ * If this is `1`, then this was produced and included binary content.
+ * If this is `0` then this was generated knowing only that a binary
+ * file changed but without providing the data, probably from a patch
+ * that said `Binary files a/file.txt and b/file.txt differ`.
+ */
+ unsigned int contains_data;
+ git_diff_binary_file old_file; /**< The contents of the old file. */
+ git_diff_binary_file new_file; /**< The contents of the new file. */
+} git_diff_binary;
+
+/**
+ * When iterating over a diff, callback that will be made for
+ * binary content within the diff.
+ */
+typedef int GIT_CALLBACK(git_diff_binary_cb)(
+ const git_diff_delta *delta,
+ const git_diff_binary *binary,
+ void *payload);
+
+/**
+ * Structure describing a hunk of a diff.
+ *
+ * A `hunk` is a span of modified lines in a delta along with some stable
+ * surrounding context. You can configure the amount of context and other
+ * properties of how hunks are generated. Each hunk also comes with a
+ * header that described where it starts and ends in both the old and new
+ * versions in the delta.
+ */
+typedef struct {
+ int old_start; /**< Starting line number in old_file */
+ int old_lines; /**< Number of lines in old_file */
+ int new_start; /**< Starting line number in new_file */
+ int new_lines; /**< Number of lines in new_file */
+ size_t header_len; /**< Number of bytes in header text */
+ char header[GIT_DIFF_HUNK_HEADER_SIZE]; /**< Header text, NUL-byte terminated */
+} git_diff_hunk;
+
+/**
+ * When iterating over a diff, callback that will be made per hunk.
+ */
+typedef int GIT_CALLBACK(git_diff_hunk_cb)(
+ const git_diff_delta *delta,
+ const git_diff_hunk *hunk,
+ void *payload);
+
+/**
+ * Line origin constants.
+ *
+ * These values describe where a line came from and will be passed to
+ * the git_diff_line_cb when iterating over a diff. There are some
+ * special origin constants at the end that are used for the text
+ * output callbacks to demarcate lines that are actually part of
+ * the file or hunk headers.
+ */
+typedef enum {
+ /* These values will be sent to `git_diff_line_cb` along with the line */
+ GIT_DIFF_LINE_CONTEXT = ' ',
+ GIT_DIFF_LINE_ADDITION = '+',
+ GIT_DIFF_LINE_DELETION = '-',
+
+ GIT_DIFF_LINE_CONTEXT_EOFNL = '=', /**< Both files have no LF at end */
+ GIT_DIFF_LINE_ADD_EOFNL = '>', /**< Old has no LF at end, new does */
+ GIT_DIFF_LINE_DEL_EOFNL = '<', /**< Old has LF at end, new does not */
+
+ /* The following values will only be sent to a `git_diff_line_cb` when
+ * the content of a diff is being formatted through `git_diff_print`.
+ */
+ GIT_DIFF_LINE_FILE_HDR = 'F',
+ GIT_DIFF_LINE_HUNK_HDR = 'H',
+ GIT_DIFF_LINE_BINARY = 'B' /**< For "Binary files x and y differ" */
+} git_diff_line_t;
+
+/**
+ * Structure describing a line (or data span) of a diff.
+ *
+ * A `line` is a range of characters inside a hunk. It could be a context
+ * line (i.e. in both old and new versions), an added line (i.e. only in
+ * the new version), or a removed line (i.e. only in the old version).
+ * Unfortunately, we don't know anything about the encoding of data in the
+ * file being diffed, so we cannot tell you much about the line content.
+ * Line data will not be NUL-byte terminated, however, because it will be
+ * just a span of bytes inside the larger file.
+ */
+typedef struct {
+ char origin; /**< A git_diff_line_t value */
+ int old_lineno; /**< Line number in old file or -1 for added line */
+ int new_lineno; /**< Line number in new file or -1 for deleted line */
+ int num_lines; /**< Number of newline characters in content */
+ size_t content_len; /**< Number of bytes of data */
+ git_off_t content_offset; /**< Offset in the original file to the content */
+ const char *content; /**< Pointer to diff text, not NUL-byte terminated */
+} git_diff_line;
+
+/**
+ * When iterating over a diff, callback that will be made per text diff
+ * line. In this context, the provided range will be NULL.
+ *
+ * When printing a diff, callback that will be made to output each line
+ * of text. This uses some extra GIT_DIFF_LINE_... constants for output
+ * of lines of file and hunk headers.
+ */
+typedef int GIT_CALLBACK(git_diff_line_cb)(
+ const git_diff_delta *delta, /**< delta that contains this data */
+ const git_diff_hunk *hunk, /**< hunk containing this data */
+ const git_diff_line *line, /**< line data */
+ void *payload); /**< user reference data */
+
+/**
+ * Flags to control the behavior of diff rename/copy detection.
+ */
+typedef enum {
+ /** Obey `diff.renames`. Overridden by any other GIT_DIFF_FIND_... flag. */
+ GIT_DIFF_FIND_BY_CONFIG = 0,
+
+ /** Look for renames? (`--find-renames`) */
+ GIT_DIFF_FIND_RENAMES = (1u << 0),
+
+ /** Consider old side of MODIFIED for renames? (`--break-rewrites=N`) */
+ GIT_DIFF_FIND_RENAMES_FROM_REWRITES = (1u << 1),
+
+ /** Look for copies? (a la `--find-copies`). */
+ GIT_DIFF_FIND_COPIES = (1u << 2),
+
+ /** Consider UNMODIFIED as copy sources? (`--find-copies-harder`).
+ *
+ * For this to work correctly, use GIT_DIFF_INCLUDE_UNMODIFIED when
+ * the initial `git_diff` is being generated.
+ */
+ GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED = (1u << 3),
+
+ /** Mark significant rewrites for split (`--break-rewrites=/M`) */
+ GIT_DIFF_FIND_REWRITES = (1u << 4),
+ /** Actually split large rewrites into delete/add pairs */
+ GIT_DIFF_BREAK_REWRITES = (1u << 5),
+ /** Mark rewrites for split and break into delete/add pairs */
+ GIT_DIFF_FIND_AND_BREAK_REWRITES =
+ (GIT_DIFF_FIND_REWRITES | GIT_DIFF_BREAK_REWRITES),
+
+ /** Find renames/copies for UNTRACKED items in working directory.
+ *
+ * For this to work correctly, use GIT_DIFF_INCLUDE_UNTRACKED when the
+ * initial `git_diff` is being generated (and obviously the diff must
+ * be against the working directory for this to make sense).
+ */
+ GIT_DIFF_FIND_FOR_UNTRACKED = (1u << 6),
+
+ /** Turn on all finding features. */
+ GIT_DIFF_FIND_ALL = (0x0ff),
+
+ /** Measure similarity ignoring leading whitespace (default) */
+ GIT_DIFF_FIND_IGNORE_LEADING_WHITESPACE = 0,
+ /** Measure similarity ignoring all whitespace */
+ GIT_DIFF_FIND_IGNORE_WHITESPACE = (1u << 12),
+ /** Measure similarity including all data */
+ GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE = (1u << 13),
+ /** Measure similarity only by comparing SHAs (fast and cheap) */
+ GIT_DIFF_FIND_EXACT_MATCH_ONLY = (1u << 14),
+
+ /** Do not break rewrites unless they contribute to a rename.
+ *
+ * Normally, GIT_DIFF_FIND_AND_BREAK_REWRITES will measure the self-
+ * similarity of modified files and split the ones that have changed a
+ * lot into a DELETE / ADD pair. Then the sides of that pair will be
+ * considered candidates for rename and copy detection.
+ *
+ * If you add this flag in and the split pair is *not* used for an
+ * actual rename or copy, then the modified record will be restored to
+ * a regular MODIFIED record instead of being split.
+ */
+ GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY = (1u << 15),
+
+ /** Remove any UNMODIFIED deltas after find_similar is done.
+ *
+ * Using GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED to emulate the
+ * --find-copies-harder behavior requires building a diff with the
+ * GIT_DIFF_INCLUDE_UNMODIFIED flag. If you do not want UNMODIFIED
+ * records in the final result, pass this flag to have them removed.
+ */
+ GIT_DIFF_FIND_REMOVE_UNMODIFIED = (1u << 16)
+} git_diff_find_t;
+
+/**
+ * Pluggable similarity metric
+ */
+typedef struct {
+ int GIT_CALLBACK(file_signature)(
+ void **out, const git_diff_file *file,
+ const char *fullpath, void *payload);
+ int GIT_CALLBACK(buffer_signature)(
+ void **out, const git_diff_file *file,
+ const char *buf, size_t buflen, void *payload);
+ void GIT_CALLBACK(free_signature)(void *sig, void *payload);
+ int GIT_CALLBACK(similarity)(int *score, void *siga, void *sigb, void *payload);
+ void *payload;
+} git_diff_similarity_metric;
+
+/**
+ * Control behavior of rename and copy detection
+ *
+ * These options mostly mimic parameters that can be passed to git-diff.
+ */
+typedef struct {
+ unsigned int version;
+
+ /**
+ * Combination of git_diff_find_t values (default GIT_DIFF_FIND_BY_CONFIG).
+ * NOTE: if you don't explicitly set this, `diff.renames` could be set
+ * to false, resulting in `git_diff_find_similar` doing nothing.
+ */
+ uint32_t flags;
+
+ /**
+ * Threshold above which similar files will be considered renames.
+ * This is equivalent to the -M option. Defaults to 50.
+ */
+ uint16_t rename_threshold;
+
+ /**
+ * Threshold below which similar files will be eligible to be a rename source.
+ * This is equivalent to the first part of the -B option. Defaults to 50.
+ */
+ uint16_t rename_from_rewrite_threshold;
+
+ /**
+ * Threshold above which similar files will be considered copies.
+ * This is equivalent to the -C option. Defaults to 50.
+ */
+ uint16_t copy_threshold;
+
+ /**
+ * Threshold below which similar files will be split into a delete/add pair.
+ * This is equivalent to the last part of the -B option. Defaults to 60.
+ */
+ uint16_t break_rewrite_threshold;
+
+ /**
+ * Maximum number of matches to consider for a particular file.
+ *
+ * This is a little different from the `-l` option from Git because we
+ * will still process up to this many matches before abandoning the search.
+ * Defaults to 1000.
+ */
+ size_t rename_limit;
+
+ /**
+ * The `metric` option allows you to plug in a custom similarity metric.
+ *
+ * Set it to NULL to use the default internal metric.
+ *
+ * The default metric is based on sampling hashes of ranges of data in
+ * the file, which is a pretty good similarity approximation that should
+ * work fairly well for both text and binary data while still being
+ * pretty fast with a fixed memory overhead.
+ */
+ git_diff_similarity_metric *metric;
+} git_diff_find_options;
+
+#define GIT_DIFF_FIND_OPTIONS_VERSION 1
+#define GIT_DIFF_FIND_OPTIONS_INIT {GIT_DIFF_FIND_OPTIONS_VERSION}
+
+/**
+ * Initialize git_diff_find_options structure
+ *
+ * Initializes a `git_diff_find_options` with default values. Equivalent to creating
+ * an instance with GIT_DIFF_FIND_OPTIONS_INIT.
+ *
+ * @param opts The `git_diff_find_options` struct to initialize.
+ * @param version The struct version; pass `GIT_DIFF_FIND_OPTIONS_VERSION`.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_diff_find_options_init(
+ git_diff_find_options *opts,
+ unsigned int version);
+
+/** @name Diff Generator Functions
+ *
+ * These are the functions you would use to create (or destroy) a
+ * git_diff from various objects in a repository.
+ */
+/**@{*/
+
+/**
+ * Deallocate a diff.
+ *
+ * @param diff The previously created diff; cannot be used after free.
+ */
+GIT_EXTERN(void) git_diff_free(git_diff *diff);
+
+/**
+ * Create a diff with the difference between two tree objects.
+ *
+ * This is equivalent to `git diff <old-tree> <new-tree>`
+ *
+ * The first tree will be used for the "old_file" side of the delta and the
+ * second tree will be used for the "new_file" side of the delta. You can
+ * pass NULL to indicate an empty tree, although it is an error to pass
+ * NULL for both the `old_tree` and `new_tree`.
+ *
+ * @param diff Output pointer to a git_diff pointer to be allocated.
+ * @param repo The repository containing the trees.
+ * @param old_tree A git_tree object to diff from, or NULL for empty tree.
+ * @param new_tree A git_tree object to diff to, or NULL for empty tree.
+ * @param opts Structure with options to influence diff or NULL for defaults.
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_diff_tree_to_tree(
+ git_diff **diff,
+ git_repository *repo,
+ git_tree *old_tree,
+ git_tree *new_tree,
+ const git_diff_options *opts);
+
+/**
+ * Create a diff between a tree and repository index.
+ *
+ * This is equivalent to `git diff --cached <treeish>` or if you pass
+ * the HEAD tree, then like `git diff --cached`.
+ *
+ * The tree you pass will be used for the "old_file" side of the delta, and
+ * the index will be used for the "new_file" side of the delta.
+ *
+ * If you pass NULL for the index, then the existing index of the `repo`
+ * will be used. In this case, the index will be refreshed from disk
+ * (if it has changed) before the diff is generated.
+ *
+ * @param diff Output pointer to a git_diff pointer to be allocated.
+ * @param repo The repository containing the tree and index.
+ * @param old_tree A git_tree object to diff from, or NULL for empty tree.
+ * @param index The index to diff with; repo index used if NULL.
+ * @param opts Structure with options to influence diff or NULL for defaults.
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_diff_tree_to_index(
+ git_diff **diff,
+ git_repository *repo,
+ git_tree *old_tree,
+ git_index *index,
+ const git_diff_options *opts);
+
+/**
+ * Create a diff between the repository index and the workdir directory.
+ *
+ * This matches the `git diff` command. See the note below on
+ * `git_diff_tree_to_workdir` for a discussion of the difference between
+ * `git diff` and `git diff HEAD` and how to emulate a `git diff <treeish>`
+ * using libgit2.
+ *
+ * The index will be used for the "old_file" side of the delta, and the
+ * working directory will be used for the "new_file" side of the delta.
+ *
+ * If you pass NULL for the index, then the existing index of the `repo`
+ * will be used. In this case, the index will be refreshed from disk
+ * (if it has changed) before the diff is generated.
+ *
+ * @param diff Output pointer to a git_diff pointer to be allocated.
+ * @param repo The repository.
+ * @param index The index to diff from; repo index used if NULL.
+ * @param opts Structure with options to influence diff or NULL for defaults.
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_diff_index_to_workdir(
+ git_diff **diff,
+ git_repository *repo,
+ git_index *index,
+ const git_diff_options *opts);
+
+/**
+ * Create a diff between a tree and the working directory.
+ *
+ * The tree you provide will be used for the "old_file" side of the delta,
+ * and the working directory will be used for the "new_file" side.
+ *
+ * This is not the same as `git diff <treeish>` or `git diff-index
+ * <treeish>`. Those commands use information from the index, whereas this
+ * function strictly returns the differences between the tree and the files
+ * in the working directory, regardless of the state of the index. Use
+ * `git_diff_tree_to_workdir_with_index` to emulate those commands.
+ *
+ * To see difference between this and `git_diff_tree_to_workdir_with_index`,
+ * consider the example of a staged file deletion where the file has then
+ * been put back into the working dir and further modified. The
+ * tree-to-workdir diff for that file is 'modified', but `git diff` would
+ * show status 'deleted' since there is a staged delete.
+ *
+ * @param diff A pointer to a git_diff pointer that will be allocated.
+ * @param repo The repository containing the tree.
+ * @param old_tree A git_tree object to diff from, or NULL for empty tree.
+ * @param opts Structure with options to influence diff or NULL for defaults.
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_diff_tree_to_workdir(
+ git_diff **diff,
+ git_repository *repo,
+ git_tree *old_tree,
+ const git_diff_options *opts);
+
+/**
+ * Create a diff between a tree and the working directory using index data
+ * to account for staged deletes, tracked files, etc.
+ *
+ * This emulates `git diff <tree>` by diffing the tree to the index and
+ * the index to the working directory and blending the results into a
+ * single diff that includes staged deleted, etc.
+ *
+ * @param diff A pointer to a git_diff pointer that will be allocated.
+ * @param repo The repository containing the tree.
+ * @param old_tree A git_tree object to diff from, or NULL for empty tree.
+ * @param opts Structure with options to influence diff or NULL for defaults.
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_diff_tree_to_workdir_with_index(
+ git_diff **diff,
+ git_repository *repo,
+ git_tree *old_tree,
+ const git_diff_options *opts);
+
+/**
+ * Create a diff with the difference between two index objects.
+ *
+ * The first index will be used for the "old_file" side of the delta and the
+ * second index will be used for the "new_file" side of the delta.
+ *
+ * @param diff Output pointer to a git_diff pointer to be allocated.
+ * @param repo The repository containing the indexes.
+ * @param old_index A git_index object to diff from.
+ * @param new_index A git_index object to diff to.
+ * @param opts Structure with options to influence diff or NULL for defaults.
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_diff_index_to_index(
+ git_diff **diff,
+ git_repository *repo,
+ git_index *old_index,
+ git_index *new_index,
+ const git_diff_options *opts);
+
+/**
+ * Merge one diff into another.
+ *
+ * This merges items from the "from" list into the "onto" list. The
+ * resulting diff will have all items that appear in either list.
+ * If an item appears in both lists, then it will be "merged" to appear
+ * as if the old version was from the "onto" list and the new version
+ * is from the "from" list (with the exception that if the item has a
+ * pending DELETE in the middle, then it will show as deleted).
+ *
+ * @param onto Diff to merge into.
+ * @param from Diff to merge.
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_diff_merge(
+ git_diff *onto,
+ const git_diff *from);
+
+/**
+ * Transform a diff marking file renames, copies, etc.
+ *
+ * This modifies a diff in place, replacing old entries that look
+ * like renames or copies with new entries reflecting those changes.
+ * This also will, if requested, break modified files into add/remove
+ * pairs if the amount of change is above a threshold.
+ *
+ * @param diff diff to run detection algorithms on
+ * @param options Control how detection should be run, NULL for defaults
+ * @return 0 on success, -1 on failure
+ */
+GIT_EXTERN(int) git_diff_find_similar(
+ git_diff *diff,
+ const git_diff_find_options *options);
+
+/**@}*/
+
+
+/** @name Diff Processor Functions
+ *
+ * These are the functions you apply to a diff to process it
+ * or read it in some way.
+ */
+/**@{*/
+
+/**
+ * Query how many diff records are there in a diff.
+ *
+ * @param diff A git_diff generated by one of the above functions
+ * @return Count of number of deltas in the list
+ */
+GIT_EXTERN(size_t) git_diff_num_deltas(const git_diff *diff);
+
+/**
+ * Query how many diff deltas are there in a diff filtered by type.
+ *
+ * This works just like `git_diff_num_deltas()` with an extra parameter
+ * that is a `git_delta_t` and returns just the count of how many deltas
+ * match that particular type.
+ *
+ * @param diff A git_diff generated by one of the above functions
+ * @param type A git_delta_t value to filter the count
+ * @return Count of number of deltas matching delta_t type
+ */
+GIT_EXTERN(size_t) git_diff_num_deltas_of_type(
+ const git_diff *diff, git_delta_t type);
+
+/**
+ * Return the diff delta for an entry in the diff list.
+ *
+ * The `git_diff_delta` pointer points to internal data and you do not
+ * have to release it when you are done with it. It will go away when
+ * the * `git_diff` (or any associated `git_patch`) goes away.
+ *
+ * Note that the flags on the delta related to whether it has binary
+ * content or not may not be set if there are no attributes set for the
+ * file and there has been no reason to load the file data at this point.
+ * For now, if you need those flags to be up to date, your only option is
+ * to either use `git_diff_foreach` or create a `git_patch`.
+ *
+ * @param diff Diff list object
+ * @param idx Index into diff list
+ * @return Pointer to git_diff_delta (or NULL if `idx` out of range)
+ */
+GIT_EXTERN(const git_diff_delta *) git_diff_get_delta(
+ const git_diff *diff, size_t idx);
+
+/**
+ * Check if deltas are sorted case sensitively or insensitively.
+ *
+ * @param diff diff to check
+ * @return 0 if case sensitive, 1 if case is ignored
+ */
+GIT_EXTERN(int) git_diff_is_sorted_icase(const git_diff *diff);
+
+/**
+ * Loop over all deltas in a diff issuing callbacks.
+ *
+ * This will iterate through all of the files described in a diff. You
+ * should provide a file callback to learn about each file.
+ *
+ * The "hunk" and "line" callbacks are optional, and the text diff of the
+ * files will only be calculated if they are not NULL. Of course, these
+ * callbacks will not be invoked for binary files on the diff or for
+ * files whose only changed is a file mode change.
+ *
+ * Returning a non-zero value from any of the callbacks will terminate
+ * the iteration and return the value to the user.
+ *
+ * @param diff A git_diff generated by one of the above functions.
+ * @param file_cb Callback function to make per file in the diff.
+ * @param binary_cb Optional callback to make for binary files.
+ * @param hunk_cb Optional callback to make per hunk of text diff. This
+ * callback is called to describe a range of lines in the
+ * diff. It will not be issued for binary files.
+ * @param line_cb Optional callback to make per line of diff text. This
+ * same callback will be made for context lines, added, and
+ * removed lines, and even for a deleted trailing newline.
+ * @param payload Reference pointer that will be passed to your callbacks.
+ * @return 0 on success, non-zero callback return value, or error code
+ */
+GIT_EXTERN(int) git_diff_foreach(
+ git_diff *diff,
+ git_diff_file_cb file_cb,
+ git_diff_binary_cb binary_cb,
+ git_diff_hunk_cb hunk_cb,
+ git_diff_line_cb line_cb,
+ void *payload);
+
+/**
+ * Look up the single character abbreviation for a delta status code.
+ *
+ * When you run `git diff --name-status` it uses single letter codes in
+ * the output such as 'A' for added, 'D' for deleted, 'M' for modified,
+ * etc. This function converts a git_delta_t value into these letters for
+ * your own purposes. GIT_DELTA_UNTRACKED will return a space (i.e. ' ').
+ *
+ * @param status The git_delta_t value to look up
+ * @return The single character label for that code
+ */
+GIT_EXTERN(char) git_diff_status_char(git_delta_t status);
+
+/**
+ * Possible output formats for diff data
+ */
+typedef enum {
+ GIT_DIFF_FORMAT_PATCH = 1u, /**< full git diff */
+ GIT_DIFF_FORMAT_PATCH_HEADER = 2u, /**< just the file headers of patch */
+ GIT_DIFF_FORMAT_RAW = 3u, /**< like git diff --raw */
+ GIT_DIFF_FORMAT_NAME_ONLY = 4u, /**< like git diff --name-only */
+ GIT_DIFF_FORMAT_NAME_STATUS = 5u, /**< like git diff --name-status */
+ GIT_DIFF_FORMAT_PATCH_ID = 6u /**< git diff as used by git patch-id */
+} git_diff_format_t;
+
+/**
+ * Iterate over a diff generating formatted text output.
+ *
+ * Returning a non-zero value from the callbacks will terminate the
+ * iteration and return the non-zero value to the caller.
+ *
+ * @param diff A git_diff generated by one of the above functions.
+ * @param format A git_diff_format_t value to pick the text format.
+ * @param print_cb Callback to make per line of diff text.
+ * @param payload Reference pointer that will be passed to your callback.
+ * @return 0 on success, non-zero callback return value, or error code
+ */
+GIT_EXTERN(int) git_diff_print(
+ git_diff *diff,
+ git_diff_format_t format,
+ git_diff_line_cb print_cb,
+ void *payload);
+
+/**
+ * Produce the complete formatted text output from a diff into a
+ * buffer.
+ *
+ * @param out A pointer to a user-allocated git_buf that will
+ * contain the diff text
+ * @param diff A git_diff generated by one of the above functions.
+ * @param format A git_diff_format_t value to pick the text format.
+ * @return 0 on success or error code
+ */
+GIT_EXTERN(int) git_diff_to_buf(
+ git_buf *out,
+ git_diff *diff,
+ git_diff_format_t format);
+
+/**@}*/
+
+/*
+ * Low-level file comparison, invoking callbacks per difference.
+ */
+
+/**
+ * Directly run a diff on two blobs.
+ *
+ * Compared to a file, a blob lacks some contextual information. As such,
+ * the `git_diff_file` given to the callback will have some fake data; i.e.
+ * `mode` will be 0 and `path` will be NULL.
+ *
+ * NULL is allowed for either `old_blob` or `new_blob` and will be treated
+ * as an empty blob, with the `oid` set to NULL in the `git_diff_file` data.
+ * Passing NULL for both blobs is a noop; no callbacks will be made at all.
+ *
+ * We do run a binary content check on the blob content and if either blob
+ * looks like binary data, the `git_diff_delta` binary attribute will be set
+ * to 1 and no call to the hunk_cb nor line_cb will be made (unless you pass
+ * `GIT_DIFF_FORCE_TEXT` of course).
+ *
+ * @param old_blob Blob for old side of diff, or NULL for empty blob
+ * @param old_as_path Treat old blob as if it had this filename; can be NULL
+ * @param new_blob Blob for new side of diff, or NULL for empty blob
+ * @param new_as_path Treat new blob as if it had this filename; can be NULL
+ * @param options Options for diff, or NULL for default options
+ * @param file_cb Callback for "file"; made once if there is a diff; can be NULL
+ * @param binary_cb Callback for binary files; can be NULL
+ * @param hunk_cb Callback for each hunk in diff; can be NULL
+ * @param line_cb Callback for each line in diff; can be NULL
+ * @param payload Payload passed to each callback function
+ * @return 0 on success, non-zero callback return value, or error code
+ */
+GIT_EXTERN(int) git_diff_blobs(
+ const git_blob *old_blob,
+ const char *old_as_path,
+ const git_blob *new_blob,
+ const char *new_as_path,
+ const git_diff_options *options,
+ git_diff_file_cb file_cb,
+ git_diff_binary_cb binary_cb,
+ git_diff_hunk_cb hunk_cb,
+ git_diff_line_cb line_cb,
+ void *payload);
+
+/**
+ * Directly run a diff between a blob and a buffer.
+ *
+ * As with `git_diff_blobs`, comparing a blob and buffer lacks some context,
+ * so the `git_diff_file` parameters to the callbacks will be faked a la the
+ * rules for `git_diff_blobs()`.
+ *
+ * Passing NULL for `old_blob` will be treated as an empty blob (i.e. the
+ * `file_cb` will be invoked with GIT_DELTA_ADDED and the diff will be the
+ * entire content of the buffer added). Passing NULL to the buffer will do
+ * the reverse, with GIT_DELTA_REMOVED and blob content removed.
+ *
+ * @param old_blob Blob for old side of diff, or NULL for empty blob
+ * @param old_as_path Treat old blob as if it had this filename; can be NULL
+ * @param buffer Raw data for new side of diff, or NULL for empty
+ * @param buffer_len Length of raw data for new side of diff
+ * @param buffer_as_path Treat buffer as if it had this filename; can be NULL
+ * @param options Options for diff, or NULL for default options
+ * @param file_cb Callback for "file"; made once if there is a diff; can be NULL
+ * @param binary_cb Callback for binary files; can be NULL
+ * @param hunk_cb Callback for each hunk in diff; can be NULL
+ * @param line_cb Callback for each line in diff; can be NULL
+ * @param payload Payload passed to each callback function
+ * @return 0 on success, non-zero callback return value, or error code
+ */
+GIT_EXTERN(int) git_diff_blob_to_buffer(
+ const git_blob *old_blob,
+ const char *old_as_path,
+ const char *buffer,
+ size_t buffer_len,
+ const char *buffer_as_path,
+ const git_diff_options *options,
+ git_diff_file_cb file_cb,
+ git_diff_binary_cb binary_cb,
+ git_diff_hunk_cb hunk_cb,
+ git_diff_line_cb line_cb,
+ void *payload);
+
+/**
+ * Directly run a diff between two buffers.
+ *
+ * Even more than with `git_diff_blobs`, comparing two buffer lacks
+ * context, so the `git_diff_file` parameters to the callbacks will be
+ * faked a la the rules for `git_diff_blobs()`.
+ *
+ * @param old_buffer Raw data for old side of diff, or NULL for empty
+ * @param old_len Length of the raw data for old side of the diff
+ * @param old_as_path Treat old buffer as if it had this filename; can be NULL
+ * @param new_buffer Raw data for new side of diff, or NULL for empty
+ * @param new_len Length of raw data for new side of diff
+ * @param new_as_path Treat buffer as if it had this filename; can be NULL
+ * @param options Options for diff, or NULL for default options
+ * @param file_cb Callback for "file"; made once if there is a diff; can be NULL
+ * @param binary_cb Callback for binary files; can be NULL
+ * @param hunk_cb Callback for each hunk in diff; can be NULL
+ * @param line_cb Callback for each line in diff; can be NULL
+ * @param payload Payload passed to each callback function
+ * @return 0 on success, non-zero callback return value, or error code
+ */
+GIT_EXTERN(int) git_diff_buffers(
+ const void *old_buffer,
+ size_t old_len,
+ const char *old_as_path,
+ const void *new_buffer,
+ size_t new_len,
+ const char *new_as_path,
+ const git_diff_options *options,
+ git_diff_file_cb file_cb,
+ git_diff_binary_cb binary_cb,
+ git_diff_hunk_cb hunk_cb,
+ git_diff_line_cb line_cb,
+ void *payload);
+
+/* Patch file parsing. */
+
+/**
+ * Options for parsing a diff / patch file.
+ */
+typedef struct {
+ unsigned int version;
+ git_oid_t oid_type;
+} git_diff_parse_options;
+
+/* The current version of the diff parse options structure */
+#define GIT_DIFF_PARSE_OPTIONS_VERSION 1
+
+/* Stack initializer for diff parse options. Alternatively use
+ * `git_diff_parse_options_init` programmatic initialization.
+ */
+#define GIT_DIFF_PARSE_OPTIONS_INIT \
+ { GIT_DIFF_PARSE_OPTIONS_VERSION, GIT_OID_DEFAULT }
+
+/**
+ * Read the contents of a git patch file into a `git_diff` object.
+ *
+ * The diff object produced is similar to the one that would be
+ * produced if you actually produced it computationally by comparing
+ * two trees, however there may be subtle differences. For example,
+ * a patch file likely contains abbreviated object IDs, so the
+ * object IDs in a `git_diff_delta` produced by this function will
+ * also be abbreviated.
+ *
+ * This function will only read patch files created by a git
+ * implementation, it will not read unified diffs produced by
+ * the `diff` program, nor any other types of patch files.
+ *
+ * @param out A pointer to a git_diff pointer that will be allocated.
+ * @param content The contents of a patch file
+ * @param content_len The length of the patch file contents
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_diff_from_buffer(
+ git_diff **out,
+ const char *content,
+ size_t content_len
+#ifdef GIT_EXPERIMENTAL_SHA256
+ , git_diff_parse_options *opts
+#endif
+ );
+
+/**
+ * This is an opaque structure which is allocated by `git_diff_get_stats`.
+ * You are responsible for releasing the object memory when done, using the
+ * `git_diff_stats_free()` function.
+ */
+typedef struct git_diff_stats git_diff_stats;
+
+/**
+ * Formatting options for diff stats
+ */
+typedef enum {
+ /** No stats*/
+ GIT_DIFF_STATS_NONE = 0,
+
+ /** Full statistics, equivalent of `--stat` */
+ GIT_DIFF_STATS_FULL = (1u << 0),
+
+ /** Short statistics, equivalent of `--shortstat` */
+ GIT_DIFF_STATS_SHORT = (1u << 1),
+
+ /** Number statistics, equivalent of `--numstat` */
+ GIT_DIFF_STATS_NUMBER = (1u << 2),
+
+ /** Extended header information such as creations, renames and mode changes, equivalent of `--summary` */
+ GIT_DIFF_STATS_INCLUDE_SUMMARY = (1u << 3)
+} git_diff_stats_format_t;
+
+/**
+ * Accumulate diff statistics for all patches.
+ *
+ * @param out Structure containing the diff statistics.
+ * @param diff A git_diff generated by one of the above functions.
+ * @return 0 on success; non-zero on error
+ */
+GIT_EXTERN(int) git_diff_get_stats(
+ git_diff_stats **out,
+ git_diff *diff);
+
+/**
+ * Get the total number of files changed in a diff
+ *
+ * @param stats A `git_diff_stats` generated by one of the above functions.
+ * @return total number of files changed in the diff
+ */
+GIT_EXTERN(size_t) git_diff_stats_files_changed(
+ const git_diff_stats *stats);
+
+/**
+ * Get the total number of insertions in a diff
+ *
+ * @param stats A `git_diff_stats` generated by one of the above functions.
+ * @return total number of insertions in the diff
+ */
+GIT_EXTERN(size_t) git_diff_stats_insertions(
+ const git_diff_stats *stats);
+
+/**
+ * Get the total number of deletions in a diff
+ *
+ * @param stats A `git_diff_stats` generated by one of the above functions.
+ * @return total number of deletions in the diff
+ */
+GIT_EXTERN(size_t) git_diff_stats_deletions(
+ const git_diff_stats *stats);
+
+/**
+ * Print diff statistics to a `git_buf`.
+ *
+ * @param out buffer to store the formatted diff statistics in.
+ * @param stats A `git_diff_stats` generated by one of the above functions.
+ * @param format Formatting option.
+ * @param width Target width for output (only affects GIT_DIFF_STATS_FULL)
+ * @return 0 on success; non-zero on error
+ */
+GIT_EXTERN(int) git_diff_stats_to_buf(
+ git_buf *out,
+ const git_diff_stats *stats,
+ git_diff_stats_format_t format,
+ size_t width);
+
+/**
+ * Deallocate a `git_diff_stats`.
+ *
+ * @param stats The previously created statistics object;
+ * cannot be used after free.
+ */
+GIT_EXTERN(void) git_diff_stats_free(git_diff_stats *stats);
+
+/**
+ * Patch ID options structure
+ *
+ * Initialize with `GIT_PATCHID_OPTIONS_INIT`. Alternatively, you can
+ * use `git_diff_patchid_options_init`.
+ *
+ */
+typedef struct git_diff_patchid_options {
+ unsigned int version;
+} git_diff_patchid_options;
+
+#define GIT_DIFF_PATCHID_OPTIONS_VERSION 1
+#define GIT_DIFF_PATCHID_OPTIONS_INIT { GIT_DIFF_PATCHID_OPTIONS_VERSION }
+
+/**
+ * Initialize git_diff_patchid_options structure
+ *
+ * Initializes a `git_diff_patchid_options` with default values. Equivalent to
+ * creating an instance with `GIT_DIFF_PATCHID_OPTIONS_INIT`.
+ *
+ * @param opts The `git_diff_patchid_options` struct to initialize.
+ * @param version The struct version; pass `GIT_DIFF_PATCHID_OPTIONS_VERSION`.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_diff_patchid_options_init(
+ git_diff_patchid_options *opts,
+ unsigned int version);
+
+/**
+ * Calculate the patch ID for the given patch.
+ *
+ * Calculate a stable patch ID for the given patch by summing the
+ * hash of the file diffs, ignoring whitespace and line numbers.
+ * This can be used to derive whether two diffs are the same with
+ * a high probability.
+ *
+ * Currently, this function only calculates stable patch IDs, as
+ * defined in git-patch-id(1), and should in fact generate the
+ * same IDs as the upstream git project does.
+ *
+ * @param out Pointer where the calculated patch ID should be stored
+ * @param diff The diff to calculate the ID for
+ * @param opts Options for how to calculate the patch ID. This is
+ * intended for future changes, as currently no options are
+ * available.
+ * @return 0 on success, an error code otherwise.
+ */
+GIT_EXTERN(int) git_diff_patchid(git_oid *out, git_diff *diff, git_diff_patchid_options *opts);
+
+GIT_END_DECL
+
+/** @} */
+
+#endif
diff --git a/include/git2/email.h b/include/git2/email.h
new file mode 100644
index 0000000..2039365
--- /dev/null
+++ b/include/git2/email.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_email_h__
+#define INCLUDE_git_email_h__
+
+#include "common.h"
+
+/**
+ * @file git2/email.h
+ * @brief Git email formatting and application routines.
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Formatting options for diff e-mail generation
+ */
+typedef enum {
+ /** Normal patch, the default */
+ GIT_EMAIL_CREATE_DEFAULT = 0,
+
+ /** Do not include patch numbers in the subject prefix. */
+ GIT_EMAIL_CREATE_OMIT_NUMBERS = (1u << 0),
+
+ /**
+ * Include numbers in the subject prefix even when the
+ * patch is for a single commit (1/1).
+ */
+ GIT_EMAIL_CREATE_ALWAYS_NUMBER = (1u << 1),
+
+ /** Do not perform rename or similarity detection. */
+ GIT_EMAIL_CREATE_NO_RENAMES = (1u << 2)
+} git_email_create_flags_t;
+
+/**
+ * Options for controlling the formatting of the generated e-mail.
+ */
+typedef struct {
+ unsigned int version;
+
+ /** see `git_email_create_flags_t` above */
+ uint32_t flags;
+
+ /** Options to use when creating diffs */
+ git_diff_options diff_opts;
+
+ /** Options for finding similarities within diffs */
+ git_diff_find_options diff_find_opts;
+
+ /**
+ * The subject prefix, by default "PATCH". If set to an empty
+ * string ("") then only the patch numbers will be shown in the
+ * prefix. If the subject_prefix is empty and patch numbers
+ * are not being shown, the prefix will be omitted entirely.
+ */
+ const char *subject_prefix;
+
+ /**
+ * The starting patch number; this cannot be 0. By default,
+ * this is 1.
+ */
+ size_t start_number;
+
+ /** The "re-roll" number. By default, there is no re-roll. */
+ size_t reroll_number;
+} git_email_create_options;
+
+/*
+ * By default, our options include rename detection and binary
+ * diffs to match `git format-patch`.
+ */
+#define GIT_EMAIL_CREATE_OPTIONS_VERSION 1
+#define GIT_EMAIL_CREATE_OPTIONS_INIT \
+{ \
+ GIT_EMAIL_CREATE_OPTIONS_VERSION, \
+ GIT_EMAIL_CREATE_DEFAULT, \
+ { GIT_DIFF_OPTIONS_VERSION, GIT_DIFF_SHOW_BINARY, GIT_SUBMODULE_IGNORE_UNSPECIFIED, {NULL,0}, NULL, NULL, NULL, 3 }, \
+ GIT_DIFF_FIND_OPTIONS_INIT \
+}
+
+/**
+ * Create a diff for a commit in mbox format for sending via email.
+ *
+ * @param out buffer to store the e-mail patch in
+ * @param diff the changes to include in the email
+ * @param patch_idx the patch index
+ * @param patch_count the total number of patches that will be included
+ * @param commit_id the commit id for this change
+ * @param summary the commit message for this change
+ * @param body optional text to include above the diffstat
+ * @param author the person who authored this commit
+ * @param opts email creation options
+ */
+GIT_EXTERN(int) git_email_create_from_diff(
+ git_buf *out,
+ git_diff *diff,
+ size_t patch_idx,
+ size_t patch_count,
+ const git_oid *commit_id,
+ const char *summary,
+ const char *body,
+ const git_signature *author,
+ const git_email_create_options *opts);
+
+/**
+ * Create a diff for a commit in mbox format for sending via email.
+ * The commit must not be a merge commit.
+ *
+ * @param out buffer to store the e-mail patch in
+ * @param commit commit to create a patch for
+ * @param opts email creation options
+ */
+GIT_EXTERN(int) git_email_create_from_commit(
+ git_buf *out,
+ git_commit *commit,
+ const git_email_create_options *opts);
+
+GIT_END_DECL
+
+/** @} */
+
+#endif
diff --git a/include/git2/errors.h b/include/git2/errors.h
new file mode 100644
index 0000000..7180852
--- /dev/null
+++ b/include/git2/errors.h
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_errors_h__
+#define INCLUDE_git_errors_h__
+
+#include "common.h"
+
+/**
+ * @file git2/errors.h
+ * @brief Git error handling routines and variables
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/** Generic return codes */
+typedef enum {
+ GIT_OK = 0, /**< No error */
+
+ GIT_ERROR = -1, /**< Generic error */
+ GIT_ENOTFOUND = -3, /**< Requested object could not be found */
+ GIT_EEXISTS = -4, /**< Object exists preventing operation */
+ GIT_EAMBIGUOUS = -5, /**< More than one object matches */
+ GIT_EBUFS = -6, /**< Output buffer too short to hold data */
+
+ /**
+ * GIT_EUSER is a special error that is never generated by libgit2
+ * code. You can return it from a callback (e.g to stop an iteration)
+ * to know that it was generated by the callback and not by libgit2.
+ */
+ GIT_EUSER = -7,
+
+ GIT_EBAREREPO = -8, /**< Operation not allowed on bare repository */
+ GIT_EUNBORNBRANCH = -9, /**< HEAD refers to branch with no commits */
+ GIT_EUNMERGED = -10, /**< Merge in progress prevented operation */
+ GIT_ENONFASTFORWARD = -11, /**< Reference was not fast-forwardable */
+ GIT_EINVALIDSPEC = -12, /**< Name/ref spec was not in a valid format */
+ GIT_ECONFLICT = -13, /**< Checkout conflicts prevented operation */
+ GIT_ELOCKED = -14, /**< Lock file prevented operation */
+ GIT_EMODIFIED = -15, /**< Reference value does not match expected */
+ GIT_EAUTH = -16, /**< Authentication error */
+ GIT_ECERTIFICATE = -17, /**< Server certificate is invalid */
+ GIT_EAPPLIED = -18, /**< Patch/merge has already been applied */
+ GIT_EPEEL = -19, /**< The requested peel operation is not possible */
+ GIT_EEOF = -20, /**< Unexpected EOF */
+ GIT_EINVALID = -21, /**< Invalid operation or input */
+ GIT_EUNCOMMITTED = -22, /**< Uncommitted changes in index prevented operation */
+ GIT_EDIRECTORY = -23, /**< The operation is not valid for a directory */
+ GIT_EMERGECONFLICT = -24, /**< A merge conflict exists and cannot continue */
+
+ GIT_PASSTHROUGH = -30, /**< A user-configured callback refused to act */
+ GIT_ITEROVER = -31, /**< Signals end of iteration with iterator */
+ GIT_RETRY = -32, /**< Internal only */
+ GIT_EMISMATCH = -33, /**< Hashsum mismatch in object */
+ GIT_EINDEXDIRTY = -34, /**< Unsaved changes in the index would be overwritten */
+ GIT_EAPPLYFAIL = -35, /**< Patch application failed */
+ GIT_EOWNER = -36, /**< The object is not owned by the current user */
+ GIT_TIMEOUT = -37 /**< The operation timed out */
+} git_error_code;
+
+/**
+ * Structure to store extra details of the last error that occurred.
+ *
+ * This is kept on a per-thread basis if GIT_THREADS was defined when the
+ * library was build, otherwise one is kept globally for the library
+ */
+typedef struct {
+ char *message;
+ int klass;
+} git_error;
+
+/** Error classes */
+typedef enum {
+ GIT_ERROR_NONE = 0,
+ GIT_ERROR_NOMEMORY,
+ GIT_ERROR_OS,
+ GIT_ERROR_INVALID,
+ GIT_ERROR_REFERENCE,
+ GIT_ERROR_ZLIB,
+ GIT_ERROR_REPOSITORY,
+ GIT_ERROR_CONFIG,
+ GIT_ERROR_REGEX,
+ GIT_ERROR_ODB,
+ GIT_ERROR_INDEX,
+ GIT_ERROR_OBJECT,
+ GIT_ERROR_NET,
+ GIT_ERROR_TAG,
+ GIT_ERROR_TREE,
+ GIT_ERROR_INDEXER,
+ GIT_ERROR_SSL,
+ GIT_ERROR_SUBMODULE,
+ GIT_ERROR_THREAD,
+ GIT_ERROR_STASH,
+ GIT_ERROR_CHECKOUT,
+ GIT_ERROR_FETCHHEAD,
+ GIT_ERROR_MERGE,
+ GIT_ERROR_SSH,
+ GIT_ERROR_FILTER,
+ GIT_ERROR_REVERT,
+ GIT_ERROR_CALLBACK,
+ GIT_ERROR_CHERRYPICK,
+ GIT_ERROR_DESCRIBE,
+ GIT_ERROR_REBASE,
+ GIT_ERROR_FILESYSTEM,
+ GIT_ERROR_PATCH,
+ GIT_ERROR_WORKTREE,
+ GIT_ERROR_SHA,
+ GIT_ERROR_HTTP,
+ GIT_ERROR_INTERNAL,
+ GIT_ERROR_GRAFTS
+} git_error_t;
+
+/**
+ * Return the last `git_error` object that was generated for the
+ * current thread.
+ *
+ * The default behaviour of this function is to return NULL if no previous error has occurred.
+ * However, libgit2's error strings are not cleared aggressively, so a prior
+ * (unrelated) error may be returned. This can be avoided by only calling
+ * this function if the prior call to a libgit2 API returned an error.
+ *
+ * @return A git_error object.
+ */
+GIT_EXTERN(const git_error *) git_error_last(void);
+
+/**
+ * Clear the last library error that occurred for this thread.
+ */
+GIT_EXTERN(void) git_error_clear(void);
+
+/**
+ * Set the error message string for this thread, using `printf`-style
+ * formatting.
+ *
+ * This function is public so that custom ODB backends and the like can
+ * relay an error message through libgit2. Most regular users of libgit2
+ * will never need to call this function -- actually, calling it in most
+ * circumstances (for example, calling from within a callback function)
+ * will just end up having the value overwritten by libgit2 internals.
+ *
+ * This error message is stored in thread-local storage and only applies
+ * to the particular thread that this libgit2 call is made from.
+ *
+ * @param error_class One of the `git_error_t` enum above describing the
+ * general subsystem that is responsible for the error.
+ * @param fmt The `printf`-style format string; subsequent arguments must
+ * be the arguments for the format string.
+ */
+GIT_EXTERN(void) git_error_set(int error_class, const char *fmt, ...)
+ GIT_FORMAT_PRINTF(2, 3);
+
+/**
+ * Set the error message string for this thread. This function is like
+ * `git_error_set` but takes a static string instead of a `printf`-style
+ * format.
+ *
+ * @param error_class One of the `git_error_t` enum above describing the
+ * general subsystem that is responsible for the error.
+ * @param string The error message to keep
+ * @return 0 on success or -1 on failure
+ */
+GIT_EXTERN(int) git_error_set_str(int error_class, const char *string);
+
+/**
+ * Set the error message to a special value for memory allocation failure.
+ *
+ * The normal `git_error_set_str()` function attempts to `strdup()` the
+ * string that is passed in. This is not a good idea when the error in
+ * question is a memory allocation failure. That circumstance has a
+ * special setter function that sets the error string to a known and
+ * statically allocated internal value.
+ */
+GIT_EXTERN(void) git_error_set_oom(void);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/experimental.h b/include/git2/experimental.h
new file mode 100644
index 0000000..06435f9
--- /dev/null
+++ b/include/git2/experimental.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_experimental_h__
+#define INCLUDE_experimental_h__
+
+/*
+ * This file exists to support users who build libgit2 with a bespoke
+ * build system and do not use our cmake configuration. Normally, cmake
+ * will create `experimental.h` from the `experimental.h.in` file and
+ * will include the generated file instead of this one. For non-cmake
+ * users, we bundle this `experimental.h` file which will be used
+ * instead.
+ */
+
+#endif
diff --git a/include/git2/filter.h b/include/git2/filter.h
new file mode 100644
index 0000000..79bf14c
--- /dev/null
+++ b/include/git2/filter.h
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_filter_h__
+#define INCLUDE_git_filter_h__
+
+#include "common.h"
+#include "types.h"
+#include "oid.h"
+#include "buffer.h"
+
+/**
+ * @file git2/filter.h
+ * @brief Git filter APIs
+ *
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Filters are applied in one of two directions: smudging - which is
+ * exporting a file from the Git object database to the working directory,
+ * and cleaning - which is importing a file from the working directory to
+ * the Git object database. These values control which direction of
+ * change is being applied.
+ */
+typedef enum {
+ GIT_FILTER_TO_WORKTREE = 0,
+ GIT_FILTER_SMUDGE = GIT_FILTER_TO_WORKTREE,
+ GIT_FILTER_TO_ODB = 1,
+ GIT_FILTER_CLEAN = GIT_FILTER_TO_ODB
+} git_filter_mode_t;
+
+/**
+ * Filter option flags.
+ */
+typedef enum {
+ GIT_FILTER_DEFAULT = 0u,
+
+ /** Don't error for `safecrlf` violations, allow them to continue. */
+ GIT_FILTER_ALLOW_UNSAFE = (1u << 0),
+
+ /** Don't load `/etc/gitattributes` (or the system equivalent) */
+ GIT_FILTER_NO_SYSTEM_ATTRIBUTES = (1u << 1),
+
+ /** Load attributes from `.gitattributes` in the root of HEAD */
+ GIT_FILTER_ATTRIBUTES_FROM_HEAD = (1u << 2),
+
+ /**
+ * Load attributes from `.gitattributes` in a given commit.
+ * This can only be specified in a `git_filter_options`.
+ */
+ GIT_FILTER_ATTRIBUTES_FROM_COMMIT = (1u << 3)
+} git_filter_flag_t;
+
+/**
+ * Filtering options
+ */
+typedef struct {
+ unsigned int version;
+
+ /** See `git_filter_flag_t` above */
+ uint32_t flags;
+
+#ifdef GIT_DEPRECATE_HARD
+ void *reserved;
+#else
+ git_oid *commit_id;
+#endif
+
+ /**
+ * The commit to load attributes from, when
+ * `GIT_FILTER_ATTRIBUTES_FROM_COMMIT` is specified.
+ */
+ git_oid attr_commit_id;
+} git_filter_options;
+
+ #define GIT_FILTER_OPTIONS_VERSION 1
+ #define GIT_FILTER_OPTIONS_INIT {GIT_FILTER_OPTIONS_VERSION}
+
+/**
+ * A filter that can transform file data
+ *
+ * This represents a filter that can be used to transform or even replace
+ * file data. Libgit2 includes one built in filter and it is possible to
+ * write your own (see git2/sys/filter.h for information on that).
+ *
+ * The two builtin filters are:
+ *
+ * * "crlf" which uses the complex rules with the "text", "eol", and
+ * "crlf" file attributes to decide how to convert between LF and CRLF
+ * line endings
+ * * "ident" which replaces "$Id$" in a blob with "$Id: <blob OID>$" upon
+ * checkout and replaced "$Id: <anything>$" with "$Id$" on checkin.
+ */
+typedef struct git_filter git_filter;
+
+/**
+ * List of filters to be applied
+ *
+ * This represents a list of filters to be applied to a file / blob. You
+ * can build the list with one call, apply it with another, and dispose it
+ * with a third. In typical usage, there are not many occasions where a
+ * git_filter_list is needed directly since the library will generally
+ * handle conversions for you, but it can be convenient to be able to
+ * build and apply the list sometimes.
+ */
+typedef struct git_filter_list git_filter_list;
+
+/**
+ * Load the filter list for a given path.
+ *
+ * This will return 0 (success) but set the output git_filter_list to NULL
+ * if no filters are requested for the given file.
+ *
+ * @param filters Output newly created git_filter_list (or NULL)
+ * @param repo Repository object that contains `path`
+ * @param blob The blob to which the filter will be applied (if known)
+ * @param path Relative path of the file to be filtered
+ * @param mode Filtering direction (WT->ODB or ODB->WT)
+ * @param flags Combination of `git_filter_flag_t` flags
+ * @return 0 on success (which could still return NULL if no filters are
+ * needed for the requested file), <0 on error
+ */
+GIT_EXTERN(int) git_filter_list_load(
+ git_filter_list **filters,
+ git_repository *repo,
+ git_blob *blob, /* can be NULL */
+ const char *path,
+ git_filter_mode_t mode,
+ uint32_t flags);
+
+/**
+ * Load the filter list for a given path.
+ *
+ * This will return 0 (success) but set the output git_filter_list to NULL
+ * if no filters are requested for the given file.
+ *
+ * @param filters Output newly created git_filter_list (or NULL)
+ * @param repo Repository object that contains `path`
+ * @param blob The blob to which the filter will be applied (if known)
+ * @param path Relative path of the file to be filtered
+ * @param mode Filtering direction (WT->ODB or ODB->WT)
+ * @param opts The `git_filter_options` to use when loading filters
+ * @return 0 on success (which could still return NULL if no filters are
+ * needed for the requested file), <0 on error
+ */
+GIT_EXTERN(int) git_filter_list_load_ext(
+ git_filter_list **filters,
+ git_repository *repo,
+ git_blob *blob,
+ const char *path,
+ git_filter_mode_t mode,
+ git_filter_options *opts);
+
+/**
+ * Query the filter list to see if a given filter (by name) will run.
+ * The built-in filters "crlf" and "ident" can be queried, otherwise this
+ * is the name of the filter specified by the filter attribute.
+ *
+ * This will return 0 if the given filter is not in the list, or 1 if
+ * the filter will be applied.
+ *
+ * @param filters A loaded git_filter_list (or NULL)
+ * @param name The name of the filter to query
+ * @return 1 if the filter is in the list, 0 otherwise
+ */
+GIT_EXTERN(int) git_filter_list_contains(
+ git_filter_list *filters,
+ const char *name);
+
+/**
+ * Apply filter list to a data buffer.
+ *
+ * @param out Buffer to store the result of the filtering
+ * @param filters A loaded git_filter_list (or NULL)
+ * @param in Buffer containing the data to filter
+ * @param in_len The length of the input buffer
+ * @return 0 on success, an error code otherwise
+ */
+GIT_EXTERN(int) git_filter_list_apply_to_buffer(
+ git_buf *out,
+ git_filter_list *filters,
+ const char *in,
+ size_t in_len);
+
+/**
+ * Apply a filter list to the contents of a file on disk
+ *
+ * @param out buffer into which to store the filtered file
+ * @param filters the list of filters to apply
+ * @param repo the repository in which to perform the filtering
+ * @param path the path of the file to filter, a relative path will be
+ * taken as relative to the workdir
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_filter_list_apply_to_file(
+ git_buf *out,
+ git_filter_list *filters,
+ git_repository *repo,
+ const char *path);
+
+/**
+ * Apply a filter list to the contents of a blob
+ *
+ * @param out buffer into which to store the filtered file
+ * @param filters the list of filters to apply
+ * @param blob the blob to filter
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_filter_list_apply_to_blob(
+ git_buf *out,
+ git_filter_list *filters,
+ git_blob *blob);
+
+/**
+ * Apply a filter list to an arbitrary buffer as a stream
+ *
+ * @param filters the list of filters to apply
+ * @param buffer the buffer to filter
+ * @param len the size of the buffer
+ * @param target the stream into which the data will be written
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_filter_list_stream_buffer(
+ git_filter_list *filters,
+ const char *buffer,
+ size_t len,
+ git_writestream *target);
+
+/**
+ * Apply a filter list to a file as a stream
+ *
+ * @param filters the list of filters to apply
+ * @param repo the repository in which to perform the filtering
+ * @param path the path of the file to filter, a relative path will be
+ * taken as relative to the workdir
+ * @param target the stream into which the data will be written
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_filter_list_stream_file(
+ git_filter_list *filters,
+ git_repository *repo,
+ const char *path,
+ git_writestream *target);
+
+/**
+ * Apply a filter list to a blob as a stream
+ *
+ * @param filters the list of filters to apply
+ * @param blob the blob to filter
+ * @param target the stream into which the data will be written
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_filter_list_stream_blob(
+ git_filter_list *filters,
+ git_blob *blob,
+ git_writestream *target);
+
+/**
+ * Free a git_filter_list
+ *
+ * @param filters A git_filter_list created by `git_filter_list_load`
+ */
+GIT_EXTERN(void) git_filter_list_free(git_filter_list *filters);
+
+
+GIT_END_DECL
+
+/** @} */
+
+#endif
diff --git a/include/git2/global.h b/include/git2/global.h
new file mode 100644
index 0000000..2a87e10
--- /dev/null
+++ b/include/git2/global.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_global_h__
+#define INCLUDE_git_global_h__
+
+#include "common.h"
+
+GIT_BEGIN_DECL
+
+/**
+ * Init the global state
+ *
+ * This function must be called before any other libgit2 function in
+ * order to set up global state and threading.
+ *
+ * This function may be called multiple times - it will return the number
+ * of times the initialization has been called (including this one) that have
+ * not subsequently been shutdown.
+ *
+ * @return the number of initializations of the library, or an error code.
+ */
+GIT_EXTERN(int) git_libgit2_init(void);
+
+/**
+ * Shutdown the global state
+ *
+ * Clean up the global state and threading context after calling it as
+ * many times as `git_libgit2_init()` was called - it will return the
+ * number of remainining initializations that have not been shutdown
+ * (after this one).
+ *
+ * @return the number of remaining initializations of the library, or an
+ * error code.
+ */
+GIT_EXTERN(int) git_libgit2_shutdown(void);
+
+/** @} */
+GIT_END_DECL
+#endif
+
diff --git a/include/git2/graph.h b/include/git2/graph.h
new file mode 100644
index 0000000..56edb2f
--- /dev/null
+++ b/include/git2/graph.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_graph_h__
+#define INCLUDE_git_graph_h__
+
+#include "common.h"
+#include "types.h"
+#include "oid.h"
+
+/**
+ * @file git2/graph.h
+ * @brief Git graph traversal routines
+ * @defgroup git_revwalk Git graph traversal routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Count the number of unique commits between two commit objects
+ *
+ * There is no need for branches containing the commits to have any
+ * upstream relationship, but it helps to think of one as a branch and
+ * the other as its upstream, the `ahead` and `behind` values will be
+ * what git would report for the branches.
+ *
+ * @param ahead number of unique from commits in `upstream`
+ * @param behind number of unique from commits in `local`
+ * @param repo the repository where the commits exist
+ * @param local the commit for local
+ * @param upstream the commit for upstream
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_graph_ahead_behind(size_t *ahead, size_t *behind, git_repository *repo, const git_oid *local, const git_oid *upstream);
+
+
+/**
+ * Determine if a commit is the descendant of another commit.
+ *
+ * Note that a commit is not considered a descendant of itself, in contrast
+ * to `git merge-base --is-ancestor`.
+ *
+ * @param repo the repository where the commits exist
+ * @param commit a previously loaded commit
+ * @param ancestor a potential ancestor commit
+ * @return 1 if the given commit is a descendant of the potential ancestor,
+ * 0 if not, error code otherwise.
+ */
+GIT_EXTERN(int) git_graph_descendant_of(
+ git_repository *repo,
+ const git_oid *commit,
+ const git_oid *ancestor);
+
+/**
+ * Determine if a commit is reachable from any of a list of commits by
+ * following parent edges.
+ *
+ * @param repo the repository where the commits exist
+ * @param commit a previously loaded commit
+ * @param length the number of commits in the provided `descendant_array`
+ * @param descendant_array oids of the commits
+ * @return 1 if the given commit is an ancestor of any of the given potential
+ * descendants, 0 if not, error code otherwise.
+ */
+GIT_EXTERN(int) git_graph_reachable_from_any(
+ git_repository *repo,
+ const git_oid *commit,
+ const git_oid descendant_array[],
+ size_t length);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/ignore.h b/include/git2/ignore.h
new file mode 100644
index 0000000..4c441c6
--- /dev/null
+++ b/include/git2/ignore.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_ignore_h__
+#define INCLUDE_git_ignore_h__
+
+#include "common.h"
+#include "types.h"
+
+GIT_BEGIN_DECL
+
+/**
+ * Add ignore rules for a repository.
+ *
+ * Excludesfile rules (i.e. .gitignore rules) are generally read from
+ * .gitignore files in the repository tree or from a shared system file
+ * only if a "core.excludesfile" config value is set. The library also
+ * keeps a set of per-repository internal ignores that can be configured
+ * in-memory and will not persist. This function allows you to add to
+ * that internal rules list.
+ *
+ * Example usage:
+ *
+ * error = git_ignore_add_rule(myrepo, "*.c\ndir/\nFile with space\n");
+ *
+ * This would add three rules to the ignores.
+ *
+ * @param repo The repository to add ignore rules to.
+ * @param rules Text of rules, the contents to add on a .gitignore file.
+ * It is okay to have multiple rules in the text; if so,
+ * each rule should be terminated with a newline.
+ * @return 0 on success
+ */
+GIT_EXTERN(int) git_ignore_add_rule(
+ git_repository *repo,
+ const char *rules);
+
+/**
+ * Clear ignore rules that were explicitly added.
+ *
+ * Resets to the default internal ignore rules. This will not turn off
+ * rules in .gitignore files that actually exist in the filesystem.
+ *
+ * The default internal ignores ignore ".", ".." and ".git" entries.
+ *
+ * @param repo The repository to remove ignore rules from.
+ * @return 0 on success
+ */
+GIT_EXTERN(int) git_ignore_clear_internal_rules(
+ git_repository *repo);
+
+/**
+ * Test if the ignore rules apply to a given path.
+ *
+ * This function checks the ignore rules to see if they would apply to the
+ * given file. This indicates if the file would be ignored regardless of
+ * whether the file is already in the index or committed to the repository.
+ *
+ * One way to think of this is if you were to do "git check-ignore --no-index"
+ * on the given file, would it be shown or not?
+ *
+ * @param ignored boolean returning 0 if the file is not ignored, 1 if it is
+ * @param repo a repository object
+ * @param path the file to check ignores for, relative to the repo's workdir.
+ * @return 0 if ignore rules could be processed for the file (regardless
+ * of whether it exists or not), or an error < 0 if they could not.
+ */
+GIT_EXTERN(int) git_ignore_path_is_ignored(
+ int *ignored,
+ git_repository *repo,
+ const char *path);
+
+GIT_END_DECL
+
+#endif
diff --git a/include/git2/index.h b/include/git2/index.h
new file mode 100644
index 0000000..6e80637
--- /dev/null
+++ b/include/git2/index.h
@@ -0,0 +1,848 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_index_h__
+#define INCLUDE_git_index_h__
+
+#include "common.h"
+#include "indexer.h"
+#include "types.h"
+#include "oid.h"
+#include "strarray.h"
+
+/**
+ * @file git2/index.h
+ * @brief Git index parsing and manipulation routines
+ * @defgroup git_index Git index parsing and manipulation routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/** Time structure used in a git index entry */
+typedef struct {
+ int32_t seconds;
+ /* nsec should not be stored as time_t compatible */
+ uint32_t nanoseconds;
+} git_index_time;
+
+/**
+ * In-memory representation of a file entry in the index.
+ *
+ * This is a public structure that represents a file entry in the index.
+ * The meaning of the fields corresponds to core Git's documentation (in
+ * "Documentation/technical/index-format.txt").
+ *
+ * The `flags` field consists of a number of bit fields which can be
+ * accessed via the first set of `GIT_INDEX_ENTRY_...` bitmasks below.
+ * These flags are all read from and persisted to disk.
+ *
+ * The `flags_extended` field also has a number of bit fields which can be
+ * accessed via the later `GIT_INDEX_ENTRY_...` bitmasks below. Some of
+ * these flags are read from and written to disk, but some are set aside
+ * for in-memory only reference.
+ *
+ * Note that the time and size fields are truncated to 32 bits. This
+ * is enough to detect changes, which is enough for the index to
+ * function as a cache, but it should not be taken as an authoritative
+ * source for that data.
+ */
+typedef struct git_index_entry {
+ git_index_time ctime;
+ git_index_time mtime;
+
+ uint32_t dev;
+ uint32_t ino;
+ uint32_t mode;
+ uint32_t uid;
+ uint32_t gid;
+ uint32_t file_size;
+
+ git_oid id;
+
+ uint16_t flags;
+ uint16_t flags_extended;
+
+ const char *path;
+} git_index_entry;
+
+/**
+ * Bitmasks for on-disk fields of `git_index_entry`'s `flags`
+ *
+ * These bitmasks match the four fields in the `git_index_entry` `flags`
+ * value both in memory and on disk. You can use them to interpret the
+ * data in the `flags`.
+ */
+
+#define GIT_INDEX_ENTRY_NAMEMASK (0x0fff)
+#define GIT_INDEX_ENTRY_STAGEMASK (0x3000)
+#define GIT_INDEX_ENTRY_STAGESHIFT 12
+
+/**
+ * Flags for index entries
+ */
+typedef enum {
+ GIT_INDEX_ENTRY_EXTENDED = (0x4000),
+ GIT_INDEX_ENTRY_VALID = (0x8000)
+} git_index_entry_flag_t;
+
+#define GIT_INDEX_ENTRY_STAGE(E) \
+ (((E)->flags & GIT_INDEX_ENTRY_STAGEMASK) >> GIT_INDEX_ENTRY_STAGESHIFT)
+
+#define GIT_INDEX_ENTRY_STAGE_SET(E,S) do { \
+ (E)->flags = ((E)->flags & ~GIT_INDEX_ENTRY_STAGEMASK) | \
+ (((S) & 0x03) << GIT_INDEX_ENTRY_STAGESHIFT); } while (0)
+
+/**
+ * Bitmasks for on-disk fields of `git_index_entry`'s `flags_extended`
+ *
+ * In memory, the `flags_extended` fields are divided into two parts: the
+ * fields that are read from and written to disk, and other fields that
+ * in-memory only and used by libgit2. Only the flags in
+ * `GIT_INDEX_ENTRY_EXTENDED_FLAGS` will get saved on-disk.
+ *
+ * Thee first three bitmasks match the three fields in the
+ * `git_index_entry` `flags_extended` value that belong on disk. You
+ * can use them to interpret the data in the `flags_extended`.
+ *
+ * The rest of the bitmasks match the other fields in the `git_index_entry`
+ * `flags_extended` value that are only used in-memory by libgit2.
+ * You can use them to interpret the data in the `flags_extended`.
+ *
+ */
+typedef enum {
+ GIT_INDEX_ENTRY_INTENT_TO_ADD = (1 << 13),
+ GIT_INDEX_ENTRY_SKIP_WORKTREE = (1 << 14),
+
+ GIT_INDEX_ENTRY_EXTENDED_FLAGS = (GIT_INDEX_ENTRY_INTENT_TO_ADD | GIT_INDEX_ENTRY_SKIP_WORKTREE),
+
+ GIT_INDEX_ENTRY_UPTODATE = (1 << 2)
+} git_index_entry_extended_flag_t;
+
+/** Capabilities of system that affect index actions. */
+typedef enum {
+ GIT_INDEX_CAPABILITY_IGNORE_CASE = 1,
+ GIT_INDEX_CAPABILITY_NO_FILEMODE = 2,
+ GIT_INDEX_CAPABILITY_NO_SYMLINKS = 4,
+ GIT_INDEX_CAPABILITY_FROM_OWNER = -1
+} git_index_capability_t;
+
+
+/** Callback for APIs that add/remove/update files matching pathspec */
+typedef int GIT_CALLBACK(git_index_matched_path_cb)(
+ const char *path, const char *matched_pathspec, void *payload);
+
+/** Flags for APIs that add files matching pathspec */
+typedef enum {
+ GIT_INDEX_ADD_DEFAULT = 0,
+ GIT_INDEX_ADD_FORCE = (1u << 0),
+ GIT_INDEX_ADD_DISABLE_PATHSPEC_MATCH = (1u << 1),
+ GIT_INDEX_ADD_CHECK_PATHSPEC = (1u << 2)
+} git_index_add_option_t;
+
+/** Git index stage states */
+typedef enum {
+ /**
+ * Match any index stage.
+ *
+ * Some index APIs take a stage to match; pass this value to match
+ * any entry matching the path regardless of stage.
+ */
+ GIT_INDEX_STAGE_ANY = -1,
+
+ /** A normal staged file in the index. */
+ GIT_INDEX_STAGE_NORMAL = 0,
+
+ /** The ancestor side of a conflict. */
+ GIT_INDEX_STAGE_ANCESTOR = 1,
+
+ /** The "ours" side of a conflict. */
+ GIT_INDEX_STAGE_OURS = 2,
+
+ /** The "theirs" side of a conflict. */
+ GIT_INDEX_STAGE_THEIRS = 3
+} git_index_stage_t;
+
+/**
+ * Create a new bare Git index object as a memory representation
+ * of the Git index file in 'index_path', without a repository
+ * to back it.
+ *
+ * Since there is no ODB or working directory behind this index,
+ * any Index methods which rely on these (e.g. index_add_bypath)
+ * will fail with the GIT_ERROR error code.
+ *
+ * If you need to access the index of an actual repository,
+ * use the `git_repository_index` wrapper.
+ *
+ * The index must be freed once it's no longer in use.
+ *
+ * @param out the pointer for the new index
+ * @param index_path the path to the index file in disk
+ * @return 0 or an error code
+ */
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+GIT_EXTERN(int) git_index_open(git_index **out, const char *index_path, git_oid_t oid_type);
+#else
+GIT_EXTERN(int) git_index_open(git_index **out, const char *index_path);
+#endif
+
+/**
+ * Create an in-memory index object.
+ *
+ * This index object cannot be read/written to the filesystem,
+ * but may be used to perform in-memory index operations.
+ *
+ * The index must be freed once it's no longer in use.
+ *
+ * @param out the pointer for the new index
+ * @return 0 or an error code
+ */
+#ifdef GIT_EXPERIMENTAL_SHA256
+GIT_EXTERN(int) git_index_new(git_index **out, git_oid_t oid_type);
+#else
+GIT_EXTERN(int) git_index_new(git_index **out);
+#endif
+
+/**
+ * Free an existing index object.
+ *
+ * @param index an existing index object
+ */
+GIT_EXTERN(void) git_index_free(git_index *index);
+
+/**
+ * Get the repository this index relates to
+ *
+ * @param index The index
+ * @return A pointer to the repository
+ */
+GIT_EXTERN(git_repository *) git_index_owner(const git_index *index);
+
+/**
+ * Read index capabilities flags.
+ *
+ * @param index An existing index object
+ * @return A combination of GIT_INDEX_CAPABILITY values
+ */
+GIT_EXTERN(int) git_index_caps(const git_index *index);
+
+/**
+ * Set index capabilities flags.
+ *
+ * If you pass `GIT_INDEX_CAPABILITY_FROM_OWNER` for the caps, then
+ * capabilities will be read from the config of the owner object,
+ * looking at `core.ignorecase`, `core.filemode`, `core.symlinks`.
+ *
+ * @param index An existing index object
+ * @param caps A combination of GIT_INDEX_CAPABILITY values
+ * @return 0 on success, -1 on failure
+ */
+GIT_EXTERN(int) git_index_set_caps(git_index *index, int caps);
+
+/**
+ * Get index on-disk version.
+ *
+ * Valid return values are 2, 3, or 4. If 3 is returned, an index
+ * with version 2 may be written instead, if the extension data in
+ * version 3 is not necessary.
+ *
+ * @param index An existing index object
+ * @return the index version
+ */
+GIT_EXTERN(unsigned int) git_index_version(git_index *index);
+
+/**
+ * Set index on-disk version.
+ *
+ * Valid values are 2, 3, or 4. If 2 is given, git_index_write may
+ * write an index with version 3 instead, if necessary to accurately
+ * represent the index.
+ *
+ * @param index An existing index object
+ * @param version The new version number
+ * @return 0 on success, -1 on failure
+ */
+GIT_EXTERN(int) git_index_set_version(git_index *index, unsigned int version);
+
+/**
+ * Update the contents of an existing index object in memory by reading
+ * from the hard disk.
+ *
+ * If `force` is true, this performs a "hard" read that discards in-memory
+ * changes and always reloads the on-disk index data. If there is no
+ * on-disk version, the index will be cleared.
+ *
+ * If `force` is false, this does a "soft" read that reloads the index
+ * data from disk only if it has changed since the last time it was
+ * loaded. Purely in-memory index data will be untouched. Be aware: if
+ * there are changes on disk, unwritten in-memory changes are discarded.
+ *
+ * @param index an existing index object
+ * @param force if true, always reload, vs. only read if file has changed
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_index_read(git_index *index, int force);
+
+/**
+ * Write an existing index object from memory back to disk
+ * using an atomic file lock.
+ *
+ * @param index an existing index object
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_index_write(git_index *index);
+
+/**
+ * Get the full path to the index file on disk.
+ *
+ * @param index an existing index object
+ * @return path to index file or NULL for in-memory index
+ */
+GIT_EXTERN(const char *) git_index_path(const git_index *index);
+
+#ifndef GIT_DEPRECATE_HARD
+/**
+ * Get the checksum of the index
+ *
+ * This checksum is the SHA-1 hash over the index file (except the
+ * last 20 bytes which are the checksum itself). In cases where the
+ * index does not exist on-disk, it will be zeroed out.
+ *
+ * @deprecated this function is deprecated with no replacement
+ * @param index an existing index object
+ * @return a pointer to the checksum of the index
+ */
+GIT_EXTERN(const git_oid *) git_index_checksum(git_index *index);
+#endif
+
+/**
+ * Read a tree into the index file with stats
+ *
+ * The current index contents will be replaced by the specified tree.
+ *
+ * @param index an existing index object
+ * @param tree tree to read
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_index_read_tree(git_index *index, const git_tree *tree);
+
+/**
+ * Write the index as a tree
+ *
+ * This method will scan the index and write a representation
+ * of its current state back to disk; it recursively creates
+ * tree objects for each of the subtrees stored in the index,
+ * but only returns the OID of the root tree. This is the OID
+ * that can be used e.g. to create a commit.
+ *
+ * The index instance cannot be bare, and needs to be associated
+ * to an existing repository.
+ *
+ * The index must not contain any file in conflict.
+ *
+ * @param out Pointer where to store the OID of the written tree
+ * @param index Index to write
+ * @return 0 on success, GIT_EUNMERGED when the index is not clean
+ * or an error code
+ */
+GIT_EXTERN(int) git_index_write_tree(git_oid *out, git_index *index);
+
+/**
+ * Write the index as a tree to the given repository
+ *
+ * This method will do the same as `git_index_write_tree`, but
+ * letting the user choose the repository where the tree will
+ * be written.
+ *
+ * The index must not contain any file in conflict.
+ *
+ * @param out Pointer where to store OID of the written tree
+ * @param index Index to write
+ * @param repo Repository where to write the tree
+ * @return 0 on success, GIT_EUNMERGED when the index is not clean
+ * or an error code
+ */
+GIT_EXTERN(int) git_index_write_tree_to(git_oid *out, git_index *index, git_repository *repo);
+
+/**@}*/
+
+/** @name Raw Index Entry Functions
+ *
+ * These functions work on index entries, and allow for raw manipulation
+ * of the entries.
+ */
+/**@{*/
+
+/* Index entry manipulation */
+
+/**
+ * Get the count of entries currently in the index
+ *
+ * @param index an existing index object
+ * @return integer of count of current entries
+ */
+GIT_EXTERN(size_t) git_index_entrycount(const git_index *index);
+
+/**
+ * Clear the contents (all the entries) of an index object.
+ *
+ * This clears the index object in memory; changes must be explicitly
+ * written to disk for them to take effect persistently.
+ *
+ * @param index an existing index object
+ * @return 0 on success, error code < 0 on failure
+ */
+GIT_EXTERN(int) git_index_clear(git_index *index);
+
+/**
+ * Get a pointer to one of the entries in the index
+ *
+ * The entry is not modifiable and should not be freed. Because the
+ * `git_index_entry` struct is a publicly defined struct, you should
+ * be able to make your own permanent copy of the data if necessary.
+ *
+ * @param index an existing index object
+ * @param n the position of the entry
+ * @return a pointer to the entry; NULL if out of bounds
+ */
+GIT_EXTERN(const git_index_entry *) git_index_get_byindex(
+ git_index *index, size_t n);
+
+/**
+ * Get a pointer to one of the entries in the index
+ *
+ * The entry is not modifiable and should not be freed. Because the
+ * `git_index_entry` struct is a publicly defined struct, you should
+ * be able to make your own permanent copy of the data if necessary.
+ *
+ * @param index an existing index object
+ * @param path path to search
+ * @param stage stage to search
+ * @return a pointer to the entry; NULL if it was not found
+ */
+GIT_EXTERN(const git_index_entry *) git_index_get_bypath(
+ git_index *index, const char *path, int stage);
+
+/**
+ * Remove an entry from the index
+ *
+ * @param index an existing index object
+ * @param path path to search
+ * @param stage stage to search
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_index_remove(git_index *index, const char *path, int stage);
+
+/**
+ * Remove all entries from the index under a given directory
+ *
+ * @param index an existing index object
+ * @param dir container directory path
+ * @param stage stage to search
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_index_remove_directory(
+ git_index *index, const char *dir, int stage);
+
+/**
+ * Add or update an index entry from an in-memory struct
+ *
+ * If a previous index entry exists that has the same path and stage
+ * as the given 'source_entry', it will be replaced. Otherwise, the
+ * 'source_entry' will be added.
+ *
+ * A full copy (including the 'path' string) of the given
+ * 'source_entry' will be inserted on the index.
+ *
+ * @param index an existing index object
+ * @param source_entry new entry object
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_index_add(git_index *index, const git_index_entry *source_entry);
+
+/**
+ * Return the stage number from a git index entry
+ *
+ * This entry is calculated from the entry's flag attribute like this:
+ *
+ * (entry->flags & GIT_INDEX_ENTRY_STAGEMASK) >> GIT_INDEX_ENTRY_STAGESHIFT
+ *
+ * @param entry The entry
+ * @return the stage number
+ */
+GIT_EXTERN(int) git_index_entry_stage(const git_index_entry *entry);
+
+/**
+ * Return whether the given index entry is a conflict (has a high stage
+ * entry). This is simply shorthand for `git_index_entry_stage > 0`.
+ *
+ * @param entry The entry
+ * @return 1 if the entry is a conflict entry, 0 otherwise
+ */
+GIT_EXTERN(int) git_index_entry_is_conflict(const git_index_entry *entry);
+
+/**@}*/
+
+/** @name Index Entry Iteration Functions
+ *
+ * These functions provide an iterator for index entries.
+ */
+/**@{*/
+
+/**
+ * Create an iterator that will return every entry contained in the
+ * index at the time of creation. Entries are returned in order,
+ * sorted by path. This iterator is backed by a snapshot that allows
+ * callers to modify the index while iterating without affecting the
+ * iterator.
+ *
+ * @param iterator_out The newly created iterator
+ * @param index The index to iterate
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_index_iterator_new(
+ git_index_iterator **iterator_out,
+ git_index *index);
+
+/**
+ * Return the next index entry in-order from the iterator.
+ *
+ * @param out Pointer to store the index entry in
+ * @param iterator The iterator
+ * @return 0, GIT_ITEROVER on iteration completion or an error code
+ */
+GIT_EXTERN(int) git_index_iterator_next(
+ const git_index_entry **out,
+ git_index_iterator *iterator);
+
+/**
+ * Free the index iterator
+ *
+ * @param iterator The iterator to free
+ */
+GIT_EXTERN(void) git_index_iterator_free(git_index_iterator *iterator);
+
+/**@}*/
+
+/** @name Workdir Index Entry Functions
+ *
+ * These functions work on index entries specifically in the working
+ * directory (ie, stage 0).
+ */
+/**@{*/
+
+/**
+ * Add or update an index entry from a file on disk
+ *
+ * The file `path` must be relative to the repository's
+ * working folder and must be readable.
+ *
+ * This method will fail in bare index instances.
+ *
+ * This forces the file to be added to the index, not looking
+ * at gitignore rules. Those rules can be evaluated through
+ * the git_status APIs (in status.h) before calling this.
+ *
+ * If this file currently is the result of a merge conflict, this
+ * file will no longer be marked as conflicting. The data about
+ * the conflict will be moved to the "resolve undo" (REUC) section.
+ *
+ * @param index an existing index object
+ * @param path filename to add
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_index_add_bypath(git_index *index, const char *path);
+
+/**
+ * Add or update an index entry from a buffer in memory
+ *
+ * This method will create a blob in the repository that owns the
+ * index and then add the index entry to the index. The `path` of the
+ * entry represents the position of the blob relative to the
+ * repository's root folder.
+ *
+ * If a previous index entry exists that has the same path as the
+ * given 'entry', it will be replaced. Otherwise, the 'entry' will be
+ * added.
+ *
+ * This forces the file to be added to the index, not looking
+ * at gitignore rules. Those rules can be evaluated through
+ * the git_status APIs (in status.h) before calling this.
+ *
+ * If this file currently is the result of a merge conflict, this
+ * file will no longer be marked as conflicting. The data about
+ * the conflict will be moved to the "resolve undo" (REUC) section.
+ *
+ * @param index an existing index object
+ * @param entry filename to add
+ * @param buffer data to be written into the blob
+ * @param len length of the data
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_index_add_from_buffer(
+ git_index *index,
+ const git_index_entry *entry,
+ const void *buffer, size_t len);
+
+/**
+ * Remove an index entry corresponding to a file on disk
+ *
+ * The file `path` must be relative to the repository's
+ * working folder. It may exist.
+ *
+ * If this file currently is the result of a merge conflict, this
+ * file will no longer be marked as conflicting. The data about
+ * the conflict will be moved to the "resolve undo" (REUC) section.
+ *
+ * @param index an existing index object
+ * @param path filename to remove
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_index_remove_bypath(git_index *index, const char *path);
+
+/**
+ * Add or update index entries matching files in the working directory.
+ *
+ * This method will fail in bare index instances.
+ *
+ * The `pathspec` is a list of file names or shell glob patterns that will
+ * be matched against files in the repository's working directory. Each
+ * file that matches will be added to the index (either updating an
+ * existing entry or adding a new entry). You can disable glob expansion
+ * and force exact matching with the `GIT_INDEX_ADD_DISABLE_PATHSPEC_MATCH`
+ * flag.
+ *
+ * Files that are ignored will be skipped (unlike `git_index_add_bypath`).
+ * If a file is already tracked in the index, then it *will* be updated
+ * even if it is ignored. Pass the `GIT_INDEX_ADD_FORCE` flag to skip
+ * the checking of ignore rules.
+ *
+ * To emulate `git add -A` and generate an error if the pathspec contains
+ * the exact path of an ignored file (when not using FORCE), add the
+ * `GIT_INDEX_ADD_CHECK_PATHSPEC` flag. This checks that each entry
+ * in the `pathspec` that is an exact match to a filename on disk is
+ * either not ignored or already in the index. If this check fails, the
+ * function will return GIT_EINVALIDSPEC.
+ *
+ * To emulate `git add -A` with the "dry-run" option, just use a callback
+ * function that always returns a positive value. See below for details.
+ *
+ * If any files are currently the result of a merge conflict, those files
+ * will no longer be marked as conflicting. The data about the conflicts
+ * will be moved to the "resolve undo" (REUC) section.
+ *
+ * If you provide a callback function, it will be invoked on each matching
+ * item in the working directory immediately *before* it is added to /
+ * updated in the index. Returning zero will add the item to the index,
+ * greater than zero will skip the item, and less than zero will abort the
+ * scan and return that value to the caller.
+ *
+ * @param index an existing index object
+ * @param pathspec array of path patterns
+ * @param flags combination of git_index_add_option_t flags
+ * @param callback notification callback for each added/updated path (also
+ * gets index of matching pathspec entry); can be NULL;
+ * return 0 to add, >0 to skip, <0 to abort scan.
+ * @param payload payload passed through to callback function
+ * @return 0 on success, negative callback return value, or error code
+ */
+GIT_EXTERN(int) git_index_add_all(
+ git_index *index,
+ const git_strarray *pathspec,
+ unsigned int flags,
+ git_index_matched_path_cb callback,
+ void *payload);
+
+/**
+ * Remove all matching index entries.
+ *
+ * If you provide a callback function, it will be invoked on each matching
+ * item in the index immediately *before* it is removed. Return 0 to
+ * remove the item, > 0 to skip the item, and < 0 to abort the scan.
+ *
+ * @param index An existing index object
+ * @param pathspec array of path patterns
+ * @param callback notification callback for each removed path (also
+ * gets index of matching pathspec entry); can be NULL;
+ * return 0 to add, >0 to skip, <0 to abort scan.
+ * @param payload payload passed through to callback function
+ * @return 0 on success, negative callback return value, or error code
+ */
+GIT_EXTERN(int) git_index_remove_all(
+ git_index *index,
+ const git_strarray *pathspec,
+ git_index_matched_path_cb callback,
+ void *payload);
+
+/**
+ * Update all index entries to match the working directory
+ *
+ * This method will fail in bare index instances.
+ *
+ * This scans the existing index entries and synchronizes them with the
+ * working directory, deleting them if the corresponding working directory
+ * file no longer exists otherwise updating the information (including
+ * adding the latest version of file to the ODB if needed).
+ *
+ * If you provide a callback function, it will be invoked on each matching
+ * item in the index immediately *before* it is updated (either refreshed
+ * or removed depending on working directory state). Return 0 to proceed
+ * with updating the item, > 0 to skip the item, and < 0 to abort the scan.
+ *
+ * @param index An existing index object
+ * @param pathspec array of path patterns
+ * @param callback notification callback for each updated path (also
+ * gets index of matching pathspec entry); can be NULL;
+ * return 0 to add, >0 to skip, <0 to abort scan.
+ * @param payload payload passed through to callback function
+ * @return 0 on success, negative callback return value, or error code
+ */
+GIT_EXTERN(int) git_index_update_all(
+ git_index *index,
+ const git_strarray *pathspec,
+ git_index_matched_path_cb callback,
+ void *payload);
+
+/**
+ * Find the first position of any entries which point to given
+ * path in the Git index.
+ *
+ * @param at_pos the address to which the position of the index entry is written (optional)
+ * @param index an existing index object
+ * @param path path to search
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_index_find(size_t *at_pos, git_index *index, const char *path);
+
+/**
+ * Find the first position of any entries matching a prefix. To find the first position
+ * of a path inside a given folder, suffix the prefix with a '/'.
+ *
+ * @param at_pos the address to which the position of the index entry is written (optional)
+ * @param index an existing index object
+ * @param prefix the prefix to search for
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_index_find_prefix(size_t *at_pos, git_index *index, const char *prefix);
+
+/**@}*/
+
+/** @name Conflict Index Entry Functions
+ *
+ * These functions work on conflict index entries specifically (ie, stages 1-3)
+ */
+/**@{*/
+
+/**
+ * Add or update index entries to represent a conflict. Any staged
+ * entries that exist at the given paths will be removed.
+ *
+ * The entries are the entries from the tree included in the merge. Any
+ * entry may be null to indicate that that file was not present in the
+ * trees during the merge. For example, ancestor_entry may be NULL to
+ * indicate that a file was added in both branches and must be resolved.
+ *
+ * @param index an existing index object
+ * @param ancestor_entry the entry data for the ancestor of the conflict
+ * @param our_entry the entry data for our side of the merge conflict
+ * @param their_entry the entry data for their side of the merge conflict
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_index_conflict_add(
+ git_index *index,
+ const git_index_entry *ancestor_entry,
+ const git_index_entry *our_entry,
+ const git_index_entry *their_entry);
+
+/**
+ * Get the index entries that represent a conflict of a single file.
+ *
+ * The entries are not modifiable and should not be freed. Because the
+ * `git_index_entry` struct is a publicly defined struct, you should
+ * be able to make your own permanent copy of the data if necessary.
+ *
+ * @param ancestor_out Pointer to store the ancestor entry
+ * @param our_out Pointer to store the our entry
+ * @param their_out Pointer to store the their entry
+ * @param index an existing index object
+ * @param path path to search
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_index_conflict_get(
+ const git_index_entry **ancestor_out,
+ const git_index_entry **our_out,
+ const git_index_entry **their_out,
+ git_index *index,
+ const char *path);
+
+/**
+ * Removes the index entries that represent a conflict of a single file.
+ *
+ * @param index an existing index object
+ * @param path path to remove conflicts for
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_index_conflict_remove(git_index *index, const char *path);
+
+/**
+ * Remove all conflicts in the index (entries with a stage greater than 0).
+ *
+ * @param index an existing index object
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_index_conflict_cleanup(git_index *index);
+
+/**
+ * Determine if the index contains entries representing file conflicts.
+ *
+ * @param index An existing index object.
+ * @return 1 if at least one conflict is found, 0 otherwise.
+ */
+GIT_EXTERN(int) git_index_has_conflicts(const git_index *index);
+
+/**
+ * Create an iterator for the conflicts in the index.
+ *
+ * The index must not be modified while iterating; the results are undefined.
+ *
+ * @param iterator_out The newly created conflict iterator
+ * @param index The index to scan
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_index_conflict_iterator_new(
+ git_index_conflict_iterator **iterator_out,
+ git_index *index);
+
+/**
+ * Returns the current conflict (ancestor, ours and theirs entry) and
+ * advance the iterator internally to the next value.
+ *
+ * @param ancestor_out Pointer to store the ancestor side of the conflict
+ * @param our_out Pointer to store our side of the conflict
+ * @param their_out Pointer to store their side of the conflict
+ * @param iterator The conflict iterator.
+ * @return 0 (no error), GIT_ITEROVER (iteration is done) or an error code
+ * (negative value)
+ */
+GIT_EXTERN(int) git_index_conflict_next(
+ const git_index_entry **ancestor_out,
+ const git_index_entry **our_out,
+ const git_index_entry **their_out,
+ git_index_conflict_iterator *iterator);
+
+/**
+ * Frees a `git_index_conflict_iterator`.
+ *
+ * @param iterator pointer to the iterator
+ */
+GIT_EXTERN(void) git_index_conflict_iterator_free(
+ git_index_conflict_iterator *iterator);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/indexer.h b/include/git2/indexer.h
new file mode 100644
index 0000000..630eef9
--- /dev/null
+++ b/include/git2/indexer.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef _INCLUDE_git_indexer_h__
+#define _INCLUDE_git_indexer_h__
+
+#include "common.h"
+#include "types.h"
+#include "oid.h"
+
+GIT_BEGIN_DECL
+
+/** A git indexer object */
+typedef struct git_indexer git_indexer;
+
+/**
+ * This structure is used to provide callers information about the
+ * progress of indexing a packfile, either directly or part of a
+ * fetch or clone that downloads a packfile.
+ */
+typedef struct git_indexer_progress {
+ /** number of objects in the packfile being indexed */
+ unsigned int total_objects;
+
+ /** received objects that have been hashed */
+ unsigned int indexed_objects;
+
+ /** received_objects: objects which have been downloaded */
+ unsigned int received_objects;
+
+ /**
+ * locally-available objects that have been injected in order
+ * to fix a thin pack
+ */
+ unsigned int local_objects;
+
+ /** number of deltas in the packfile being indexed */
+ unsigned int total_deltas;
+
+ /** received deltas that have been indexed */
+ unsigned int indexed_deltas;
+
+ /** size of the packfile received up to now */
+ size_t received_bytes;
+} git_indexer_progress;
+
+/**
+ * Type for progress callbacks during indexing. Return a value less
+ * than zero to cancel the indexing or download.
+ *
+ * @param stats Structure containing information about the state of the transfer
+ * @param payload Payload provided by caller
+ */
+typedef int GIT_CALLBACK(git_indexer_progress_cb)(const git_indexer_progress *stats, void *payload);
+
+/**
+ * Options for indexer configuration
+ */
+typedef struct git_indexer_options {
+ unsigned int version;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ /** permissions to use creating packfile or 0 for defaults */
+ unsigned int mode;
+
+ /**
+ * object database from which to read base objects when
+ * fixing thin packs. This can be NULL if there are no thin
+ * packs; if a thin pack is encountered, an error will be
+ * returned if there are bases missing.
+ */
+ git_odb *odb;
+#endif
+
+ /** progress_cb function to call with progress information */
+ git_indexer_progress_cb progress_cb;
+
+ /** progress_cb_payload payload for the progress callback */
+ void *progress_cb_payload;
+
+ /** Do connectivity checks for the received pack */
+ unsigned char verify;
+} git_indexer_options;
+
+#define GIT_INDEXER_OPTIONS_VERSION 1
+#define GIT_INDEXER_OPTIONS_INIT { GIT_INDEXER_OPTIONS_VERSION }
+
+/**
+ * Initializes a `git_indexer_options` with default values. Equivalent to
+ * creating an instance with GIT_INDEXER_OPTIONS_INIT.
+ *
+ * @param opts the `git_indexer_options` struct to initialize.
+ * @param version Version of struct; pass `GIT_INDEXER_OPTIONS_VERSION`
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_indexer_options_init(
+ git_indexer_options *opts,
+ unsigned int version);
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+/**
+ * Create a new indexer instance
+ *
+ * @param out where to store the indexer instance
+ * @param path to the directory where the packfile should be stored
+ * @param oid_type the oid type to use for objects
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_indexer_new(
+ git_indexer **out,
+ const char *path,
+ git_oid_t oid_type,
+ git_indexer_options *opts);
+#else
+/**
+ * Create a new indexer instance
+ *
+ * @param out where to store the indexer instance
+ * @param path to the directory where the packfile should be stored
+ * @param mode permissions to use creating packfile or 0 for defaults
+ * @param odb object database from which to read base objects when
+ * fixing thin packs. Pass NULL if no thin pack is expected (an error
+ * will be returned if there are bases missing)
+ * @param opts Optional structure containing additional options. See
+ * `git_indexer_options` above.
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_indexer_new(
+ git_indexer **out,
+ const char *path,
+ unsigned int mode,
+ git_odb *odb,
+ git_indexer_options *opts);
+#endif
+
+/**
+ * Add data to the indexer
+ *
+ * @param idx the indexer
+ * @param data the data to add
+ * @param size the size of the data in bytes
+ * @param stats stat storage
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_indexer_append(git_indexer *idx, const void *data, size_t size, git_indexer_progress *stats);
+
+/**
+ * Finalize the pack and index
+ *
+ * Resolve any pending deltas and write out the index file
+ *
+ * @param idx the indexer
+ * @param stats Stat storage.
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_indexer_commit(git_indexer *idx, git_indexer_progress *stats);
+
+#ifndef GIT_DEPRECATE_HARD
+/**
+ * Get the packfile's hash
+ *
+ * A packfile's name is derived from the sorted hashing of all object
+ * names. This is only correct after the index has been finalized.
+ *
+ * @deprecated use git_indexer_name
+ * @param idx the indexer instance
+ * @return the packfile's hash
+ */
+GIT_EXTERN(const git_oid *) git_indexer_hash(const git_indexer *idx);
+#endif
+
+/**
+ * Get the unique name for the resulting packfile.
+ *
+ * The packfile's name is derived from the packfile's content.
+ * This is only correct after the index has been finalized.
+ *
+ * @param idx the indexer instance
+ * @return a NUL terminated string for the packfile name
+ */
+GIT_EXTERN(const char *) git_indexer_name(const git_indexer *idx);
+
+/**
+ * Free the indexer and its resources
+ *
+ * @param idx the indexer to free
+ */
+GIT_EXTERN(void) git_indexer_free(git_indexer *idx);
+
+GIT_END_DECL
+
+#endif
diff --git a/include/git2/mailmap.h b/include/git2/mailmap.h
new file mode 100644
index 0000000..7c3f60f
--- /dev/null
+++ b/include/git2/mailmap.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_mailmap_h__
+#define INCLUDE_git_mailmap_h__
+
+#include "common.h"
+#include "types.h"
+#include "buffer.h"
+
+/**
+ * @file git2/mailmap.h
+ * @brief Mailmap parsing routines
+ * @defgroup git_mailmap Git mailmap routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Allocate a new mailmap object.
+ *
+ * This object is empty, so you'll have to add a mailmap file before you can do
+ * anything with it. The mailmap must be freed with 'git_mailmap_free'.
+ *
+ * @param out pointer to store the new mailmap
+ * @return 0 on success, or an error code
+ */
+GIT_EXTERN(int) git_mailmap_new(git_mailmap **out);
+
+/**
+ * Free the mailmap and its associated memory.
+ *
+ * @param mm the mailmap to free
+ */
+GIT_EXTERN(void) git_mailmap_free(git_mailmap *mm);
+
+/**
+ * Add a single entry to the given mailmap object. If the entry already exists,
+ * it will be replaced with the new entry.
+ *
+ * @param mm mailmap to add the entry to
+ * @param real_name the real name to use, or NULL
+ * @param real_email the real email to use, or NULL
+ * @param replace_name the name to replace, or NULL
+ * @param replace_email the email to replace
+ * @return 0 on success, or an error code
+ */
+GIT_EXTERN(int) git_mailmap_add_entry(
+ git_mailmap *mm, const char *real_name, const char *real_email,
+ const char *replace_name, const char *replace_email);
+
+/**
+ * Create a new mailmap instance containing a single mailmap file
+ *
+ * @param out pointer to store the new mailmap
+ * @param buf buffer to parse the mailmap from
+ * @param len the length of the input buffer
+ * @return 0 on success, or an error code
+ */
+GIT_EXTERN(int) git_mailmap_from_buffer(
+ git_mailmap **out, const char *buf, size_t len);
+
+/**
+ * Create a new mailmap instance from a repository, loading mailmap files based
+ * on the repository's configuration.
+ *
+ * Mailmaps are loaded in the following order:
+ * 1. '.mailmap' in the root of the repository's working directory, if present.
+ * 2. The blob object identified by the 'mailmap.blob' config entry, if set.
+ * [NOTE: 'mailmap.blob' defaults to 'HEAD:.mailmap' in bare repositories]
+ * 3. The path in the 'mailmap.file' config entry, if set.
+ *
+ * @param out pointer to store the new mailmap
+ * @param repo repository to load mailmap information from
+ * @return 0 on success, or an error code
+ */
+GIT_EXTERN(int) git_mailmap_from_repository(
+ git_mailmap **out, git_repository *repo);
+
+/**
+ * Resolve a name and email to the corresponding real name and email.
+ *
+ * The lifetime of the strings are tied to `mm`, `name`, and `email` parameters.
+ *
+ * @param real_name pointer to store the real name
+ * @param real_email pointer to store the real email
+ * @param mm the mailmap to perform a lookup with (may be NULL)
+ * @param name the name to look up
+ * @param email the email to look up
+ * @return 0 on success, or an error code
+ */
+GIT_EXTERN(int) git_mailmap_resolve(
+ const char **real_name, const char **real_email,
+ const git_mailmap *mm, const char *name, const char *email);
+
+/**
+ * Resolve a signature to use real names and emails with a mailmap.
+ *
+ * Call `git_signature_free()` to free the data.
+ *
+ * @param out new signature
+ * @param mm mailmap to resolve with
+ * @param sig signature to resolve
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_mailmap_resolve_signature(
+ git_signature **out, const git_mailmap *mm, const git_signature *sig);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/merge.h b/include/git2/merge.h
new file mode 100644
index 0000000..fcce559
--- /dev/null
+++ b/include/git2/merge.h
@@ -0,0 +1,626 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_merge_h__
+#define INCLUDE_git_merge_h__
+
+#include "common.h"
+#include "types.h"
+#include "oid.h"
+#include "oidarray.h"
+#include "checkout.h"
+#include "index.h"
+#include "annotated_commit.h"
+
+/**
+ * @file git2/merge.h
+ * @brief Git merge routines
+ * @defgroup git_merge Git merge routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * The file inputs to `git_merge_file`. Callers should populate the
+ * `git_merge_file_input` structure with descriptions of the files in
+ * each side of the conflict for use in producing the merge file.
+ */
+typedef struct {
+ unsigned int version;
+
+ /** Pointer to the contents of the file. */
+ const char *ptr;
+
+ /** Size of the contents pointed to in `ptr`. */
+ size_t size;
+
+ /** File name of the conflicted file, or `NULL` to not merge the path. */
+ const char *path;
+
+ /** File mode of the conflicted file, or `0` to not merge the mode. */
+ unsigned int mode;
+} git_merge_file_input;
+
+#define GIT_MERGE_FILE_INPUT_VERSION 1
+#define GIT_MERGE_FILE_INPUT_INIT {GIT_MERGE_FILE_INPUT_VERSION}
+
+/**
+ * Initializes a `git_merge_file_input` with default values. Equivalent to
+ * creating an instance with GIT_MERGE_FILE_INPUT_INIT.
+ *
+ * @param opts the `git_merge_file_input` instance to initialize.
+ * @param version the version of the struct; you should pass
+ * `GIT_MERGE_FILE_INPUT_VERSION` here.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_merge_file_input_init(
+ git_merge_file_input *opts,
+ unsigned int version);
+
+/**
+ * Flags for `git_merge` options. A combination of these flags can be
+ * passed in via the `flags` value in the `git_merge_options`.
+ */
+typedef enum {
+ /**
+ * Detect renames that occur between the common ancestor and the "ours"
+ * side or the common ancestor and the "theirs" side. This will enable
+ * the ability to merge between a modified and renamed file.
+ */
+ GIT_MERGE_FIND_RENAMES = (1 << 0),
+
+ /**
+ * If a conflict occurs, exit immediately instead of attempting to
+ * continue resolving conflicts. The merge operation will fail with
+ * GIT_EMERGECONFLICT and no index will be returned.
+ */
+ GIT_MERGE_FAIL_ON_CONFLICT = (1 << 1),
+
+ /**
+ * Do not write the REUC extension on the generated index
+ */
+ GIT_MERGE_SKIP_REUC = (1 << 2),
+
+ /**
+ * If the commits being merged have multiple merge bases, do not build
+ * a recursive merge base (by merging the multiple merge bases),
+ * instead simply use the first base. This flag provides a similar
+ * merge base to `git-merge-resolve`.
+ */
+ GIT_MERGE_NO_RECURSIVE = (1 << 3),
+
+ /**
+ * Treat this merge as if it is to produce the virtual base
+ * of a recursive merge. This will ensure that there are
+ * no conflicts, any conflicting regions will keep conflict
+ * markers in the merge result.
+ */
+ GIT_MERGE_VIRTUAL_BASE = (1 << 4)
+} git_merge_flag_t;
+
+/**
+ * Merge file favor options for `git_merge_options` instruct the file-level
+ * merging functionality how to deal with conflicting regions of the files.
+ */
+typedef enum {
+ /**
+ * When a region of a file is changed in both branches, a conflict
+ * will be recorded in the index so that `git_checkout` can produce
+ * a merge file with conflict markers in the working directory.
+ * This is the default.
+ */
+ GIT_MERGE_FILE_FAVOR_NORMAL = 0,
+
+ /**
+ * When a region of a file is changed in both branches, the file
+ * created in the index will contain the "ours" side of any conflicting
+ * region. The index will not record a conflict.
+ */
+ GIT_MERGE_FILE_FAVOR_OURS = 1,
+
+ /**
+ * When a region of a file is changed in both branches, the file
+ * created in the index will contain the "theirs" side of any conflicting
+ * region. The index will not record a conflict.
+ */
+ GIT_MERGE_FILE_FAVOR_THEIRS = 2,
+
+ /**
+ * When a region of a file is changed in both branches, the file
+ * created in the index will contain each unique line from each side,
+ * which has the result of combining both files. The index will not
+ * record a conflict.
+ */
+ GIT_MERGE_FILE_FAVOR_UNION = 3
+} git_merge_file_favor_t;
+
+/**
+ * File merging flags
+ */
+typedef enum {
+ /** Defaults */
+ GIT_MERGE_FILE_DEFAULT = 0,
+
+ /** Create standard conflicted merge files */
+ GIT_MERGE_FILE_STYLE_MERGE = (1 << 0),
+
+ /** Create diff3-style files */
+ GIT_MERGE_FILE_STYLE_DIFF3 = (1 << 1),
+
+ /** Condense non-alphanumeric regions for simplified diff file */
+ GIT_MERGE_FILE_SIMPLIFY_ALNUM = (1 << 2),
+
+ /** Ignore all whitespace */
+ GIT_MERGE_FILE_IGNORE_WHITESPACE = (1 << 3),
+
+ /** Ignore changes in amount of whitespace */
+ GIT_MERGE_FILE_IGNORE_WHITESPACE_CHANGE = (1 << 4),
+
+ /** Ignore whitespace at end of line */
+ GIT_MERGE_FILE_IGNORE_WHITESPACE_EOL = (1 << 5),
+
+ /** Use the "patience diff" algorithm */
+ GIT_MERGE_FILE_DIFF_PATIENCE = (1 << 6),
+
+ /** Take extra time to find minimal diff */
+ GIT_MERGE_FILE_DIFF_MINIMAL = (1 << 7),
+
+ /** Create zdiff3 ("zealous diff3")-style files */
+ GIT_MERGE_FILE_STYLE_ZDIFF3 = (1 << 8),
+
+ /**
+ * Do not produce file conflicts when common regions have
+ * changed; keep the conflict markers in the file and accept
+ * that as the merge result.
+ */
+ GIT_MERGE_FILE_ACCEPT_CONFLICTS = (1 << 9)
+} git_merge_file_flag_t;
+
+#define GIT_MERGE_CONFLICT_MARKER_SIZE 7
+
+/**
+ * Options for merging a file
+ */
+typedef struct {
+ unsigned int version;
+
+ /**
+ * Label for the ancestor file side of the conflict which will be prepended
+ * to labels in diff3-format merge files.
+ */
+ const char *ancestor_label;
+
+ /**
+ * Label for our file side of the conflict which will be prepended
+ * to labels in merge files.
+ */
+ const char *our_label;
+
+ /**
+ * Label for their file side of the conflict which will be prepended
+ * to labels in merge files.
+ */
+ const char *their_label;
+
+ /** The file to favor in region conflicts. */
+ git_merge_file_favor_t favor;
+
+ /** see `git_merge_file_flag_t` above */
+ uint32_t flags;
+
+ /** The size of conflict markers (eg, "<<<<<<<"). Default is
+ * GIT_MERGE_CONFLICT_MARKER_SIZE. */
+ unsigned short marker_size;
+} git_merge_file_options;
+
+#define GIT_MERGE_FILE_OPTIONS_VERSION 1
+#define GIT_MERGE_FILE_OPTIONS_INIT {GIT_MERGE_FILE_OPTIONS_VERSION}
+
+/**
+ * Initialize git_merge_file_options structure
+ *
+ * Initializes a `git_merge_file_options` with default values. Equivalent to
+ * creating an instance with `GIT_MERGE_FILE_OPTIONS_INIT`.
+ *
+ * @param opts The `git_merge_file_options` struct to initialize.
+ * @param version The struct version; pass `GIT_MERGE_FILE_OPTIONS_VERSION`.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_merge_file_options_init(git_merge_file_options *opts, unsigned int version);
+
+/**
+ * Information about file-level merging
+ */
+typedef struct {
+ /**
+ * True if the output was automerged, false if the output contains
+ * conflict markers.
+ */
+ unsigned int automergeable;
+
+ /**
+ * The path that the resultant merge file should use, or NULL if a
+ * filename conflict would occur.
+ */
+ const char *path;
+
+ /** The mode that the resultant merge file should use. */
+ unsigned int mode;
+
+ /** The contents of the merge. */
+ const char *ptr;
+
+ /** The length of the merge contents. */
+ size_t len;
+} git_merge_file_result;
+
+/**
+ * Merging options
+ */
+typedef struct {
+ unsigned int version;
+
+ /** See `git_merge_flag_t` above */
+ uint32_t flags;
+
+ /**
+ * Similarity to consider a file renamed (default 50). If
+ * `GIT_MERGE_FIND_RENAMES` is enabled, added files will be compared
+ * with deleted files to determine their similarity. Files that are
+ * more similar than the rename threshold (percentage-wise) will be
+ * treated as a rename.
+ */
+ unsigned int rename_threshold;
+
+ /**
+ * Maximum similarity sources to examine for renames (default 200).
+ * If the number of rename candidates (add / delete pairs) is greater
+ * than this value, inexact rename detection is aborted.
+ *
+ * This setting overrides the `merge.renameLimit` configuration value.
+ */
+ unsigned int target_limit;
+
+ /** Pluggable similarity metric; pass NULL to use internal metric */
+ git_diff_similarity_metric *metric;
+
+ /**
+ * Maximum number of times to merge common ancestors to build a
+ * virtual merge base when faced with criss-cross merges. When this
+ * limit is reached, the next ancestor will simply be used instead of
+ * attempting to merge it. The default is unlimited.
+ */
+ unsigned int recursion_limit;
+
+ /**
+ * Default merge driver to be used when both sides of a merge have
+ * changed. The default is the `text` driver.
+ */
+ const char *default_driver;
+
+ /**
+ * Flags for handling conflicting content, to be used with the standard
+ * (`text`) merge driver.
+ */
+ git_merge_file_favor_t file_favor;
+
+ /** see `git_merge_file_flag_t` above */
+ uint32_t file_flags;
+} git_merge_options;
+
+#define GIT_MERGE_OPTIONS_VERSION 1
+#define GIT_MERGE_OPTIONS_INIT { \
+ GIT_MERGE_OPTIONS_VERSION, GIT_MERGE_FIND_RENAMES }
+
+/**
+ * Initialize git_merge_options structure
+ *
+ * Initializes a `git_merge_options` with default values. Equivalent to
+ * creating an instance with `GIT_MERGE_OPTIONS_INIT`.
+ *
+ * @param opts The `git_merge_options` struct to initialize.
+ * @param version The struct version; pass `GIT_MERGE_OPTIONS_VERSION`.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_merge_options_init(git_merge_options *opts, unsigned int version);
+
+/**
+ * The results of `git_merge_analysis` indicate the merge opportunities.
+ */
+typedef enum {
+ /** No merge is possible. (Unused.) */
+ GIT_MERGE_ANALYSIS_NONE = 0,
+
+ /**
+ * A "normal" merge; both HEAD and the given merge input have diverged
+ * from their common ancestor. The divergent commits must be merged.
+ */
+ GIT_MERGE_ANALYSIS_NORMAL = (1 << 0),
+
+ /**
+ * All given merge inputs are reachable from HEAD, meaning the
+ * repository is up-to-date and no merge needs to be performed.
+ */
+ GIT_MERGE_ANALYSIS_UP_TO_DATE = (1 << 1),
+
+ /**
+ * The given merge input is a fast-forward from HEAD and no merge
+ * needs to be performed. Instead, the client can check out the
+ * given merge input.
+ */
+ GIT_MERGE_ANALYSIS_FASTFORWARD = (1 << 2),
+
+ /**
+ * The HEAD of the current repository is "unborn" and does not point to
+ * a valid commit. No merge can be performed, but the caller may wish
+ * to simply set HEAD to the target commit(s).
+ */
+ GIT_MERGE_ANALYSIS_UNBORN = (1 << 3)
+} git_merge_analysis_t;
+
+/**
+ * The user's stated preference for merges.
+ */
+typedef enum {
+ /**
+ * No configuration was found that suggests a preferred behavior for
+ * merge.
+ */
+ GIT_MERGE_PREFERENCE_NONE = 0,
+
+ /**
+ * There is a `merge.ff=false` configuration setting, suggesting that
+ * the user does not want to allow a fast-forward merge.
+ */
+ GIT_MERGE_PREFERENCE_NO_FASTFORWARD = (1 << 0),
+
+ /**
+ * There is a `merge.ff=only` configuration setting, suggesting that
+ * the user only wants fast-forward merges.
+ */
+ GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY = (1 << 1)
+} git_merge_preference_t;
+
+/**
+ * Analyzes the given branch(es) and determines the opportunities for
+ * merging them into the HEAD of the repository.
+ *
+ * @param analysis_out analysis enumeration that the result is written into
+ * @param preference_out One of the `git_merge_preference_t` flag.
+ * @param repo the repository to merge
+ * @param their_heads the heads to merge into
+ * @param their_heads_len the number of heads to merge
+ * @return 0 on success or error code
+ */
+GIT_EXTERN(int) git_merge_analysis(
+ git_merge_analysis_t *analysis_out,
+ git_merge_preference_t *preference_out,
+ git_repository *repo,
+ const git_annotated_commit **their_heads,
+ size_t their_heads_len);
+
+/**
+ * Analyzes the given branch(es) and determines the opportunities for
+ * merging them into a reference.
+ *
+ * @param analysis_out analysis enumeration that the result is written into
+ * @param preference_out One of the `git_merge_preference_t` flag.
+ * @param repo the repository to merge
+ * @param our_ref the reference to perform the analysis from
+ * @param their_heads the heads to merge into
+ * @param their_heads_len the number of heads to merge
+ * @return 0 on success or error code
+ */
+GIT_EXTERN(int) git_merge_analysis_for_ref(
+ git_merge_analysis_t *analysis_out,
+ git_merge_preference_t *preference_out,
+ git_repository *repo,
+ git_reference *our_ref,
+ const git_annotated_commit **their_heads,
+ size_t their_heads_len);
+
+/**
+ * Find a merge base between two commits
+ *
+ * @param out the OID of a merge base between 'one' and 'two'
+ * @param repo the repository where the commits exist
+ * @param one one of the commits
+ * @param two the other commit
+ * @return 0 on success, GIT_ENOTFOUND if not found or error code
+ */
+GIT_EXTERN(int) git_merge_base(
+ git_oid *out,
+ git_repository *repo,
+ const git_oid *one,
+ const git_oid *two);
+
+/**
+ * Find merge bases between two commits
+ *
+ * @param out array in which to store the resulting ids
+ * @param repo the repository where the commits exist
+ * @param one one of the commits
+ * @param two the other commit
+ * @return 0 on success, GIT_ENOTFOUND if not found or error code
+ */
+GIT_EXTERN(int) git_merge_bases(
+ git_oidarray *out,
+ git_repository *repo,
+ const git_oid *one,
+ const git_oid *two);
+
+/**
+ * Find a merge base given a list of commits
+ *
+ * @param out the OID of a merge base considering all the commits
+ * @param repo the repository where the commits exist
+ * @param length The number of commits in the provided `input_array`
+ * @param input_array oids of the commits
+ * @return Zero on success; GIT_ENOTFOUND or -1 on failure.
+ */
+GIT_EXTERN(int) git_merge_base_many(
+ git_oid *out,
+ git_repository *repo,
+ size_t length,
+ const git_oid input_array[]);
+
+/**
+ * Find all merge bases given a list of commits
+ *
+ * @param out array in which to store the resulting ids
+ * @param repo the repository where the commits exist
+ * @param length The number of commits in the provided `input_array`
+ * @param input_array oids of the commits
+ * @return Zero on success; GIT_ENOTFOUND or -1 on failure.
+ */
+GIT_EXTERN(int) git_merge_bases_many(
+ git_oidarray *out,
+ git_repository *repo,
+ size_t length,
+ const git_oid input_array[]);
+
+/**
+ * Find a merge base in preparation for an octopus merge
+ *
+ * @param out the OID of a merge base considering all the commits
+ * @param repo the repository where the commits exist
+ * @param length The number of commits in the provided `input_array`
+ * @param input_array oids of the commits
+ * @return Zero on success; GIT_ENOTFOUND or -1 on failure.
+ */
+GIT_EXTERN(int) git_merge_base_octopus(
+ git_oid *out,
+ git_repository *repo,
+ size_t length,
+ const git_oid input_array[]);
+
+/**
+ * Merge two files as they exist in the in-memory data structures, using
+ * the given common ancestor as the baseline, producing a
+ * `git_merge_file_result` that reflects the merge result. The
+ * `git_merge_file_result` must be freed with `git_merge_file_result_free`.
+ *
+ * Note that this function does not reference a repository and any
+ * configuration must be passed as `git_merge_file_options`.
+ *
+ * @param out The git_merge_file_result to be filled in
+ * @param ancestor The contents of the ancestor file
+ * @param ours The contents of the file in "our" side
+ * @param theirs The contents of the file in "their" side
+ * @param opts The merge file options or `NULL` for defaults
+ * @return 0 on success or error code
+ */
+GIT_EXTERN(int) git_merge_file(
+ git_merge_file_result *out,
+ const git_merge_file_input *ancestor,
+ const git_merge_file_input *ours,
+ const git_merge_file_input *theirs,
+ const git_merge_file_options *opts);
+
+/**
+ * Merge two files as they exist in the index, using the given common
+ * ancestor as the baseline, producing a `git_merge_file_result` that
+ * reflects the merge result. The `git_merge_file_result` must be freed with
+ * `git_merge_file_result_free`.
+ *
+ * @param out The git_merge_file_result to be filled in
+ * @param repo The repository
+ * @param ancestor The index entry for the ancestor file (stage level 1)
+ * @param ours The index entry for our file (stage level 2)
+ * @param theirs The index entry for their file (stage level 3)
+ * @param opts The merge file options or NULL
+ * @return 0 on success or error code
+ */
+GIT_EXTERN(int) git_merge_file_from_index(
+ git_merge_file_result *out,
+ git_repository *repo,
+ const git_index_entry *ancestor,
+ const git_index_entry *ours,
+ const git_index_entry *theirs,
+ const git_merge_file_options *opts);
+
+/**
+ * Frees a `git_merge_file_result`.
+ *
+ * @param result The result to free or `NULL`
+ */
+GIT_EXTERN(void) git_merge_file_result_free(git_merge_file_result *result);
+
+/**
+ * Merge two trees, producing a `git_index` that reflects the result of
+ * the merge. The index may be written as-is to the working directory
+ * or checked out. If the index is to be converted to a tree, the caller
+ * should resolve any conflicts that arose as part of the merge.
+ *
+ * The returned index must be freed explicitly with `git_index_free`.
+ *
+ * @param out pointer to store the index result in
+ * @param repo repository that contains the given trees
+ * @param ancestor_tree the common ancestor between the trees (or null if none)
+ * @param our_tree the tree that reflects the destination tree
+ * @param their_tree the tree to merge in to `our_tree`
+ * @param opts the merge tree options (or null for defaults)
+ * @return 0 on success or error code
+ */
+GIT_EXTERN(int) git_merge_trees(
+ git_index **out,
+ git_repository *repo,
+ const git_tree *ancestor_tree,
+ const git_tree *our_tree,
+ const git_tree *their_tree,
+ const git_merge_options *opts);
+
+/**
+ * Merge two commits, producing a `git_index` that reflects the result of
+ * the merge. The index may be written as-is to the working directory
+ * or checked out. If the index is to be converted to a tree, the caller
+ * should resolve any conflicts that arose as part of the merge.
+ *
+ * The returned index must be freed explicitly with `git_index_free`.
+ *
+ * @param out pointer to store the index result in
+ * @param repo repository that contains the given trees
+ * @param our_commit the commit that reflects the destination tree
+ * @param their_commit the commit to merge in to `our_commit`
+ * @param opts the merge tree options (or null for defaults)
+ * @return 0 on success or error code
+ */
+GIT_EXTERN(int) git_merge_commits(
+ git_index **out,
+ git_repository *repo,
+ const git_commit *our_commit,
+ const git_commit *their_commit,
+ const git_merge_options *opts);
+
+/**
+ * Merges the given commit(s) into HEAD, writing the results into the working
+ * directory. Any changes are staged for commit and any conflicts are written
+ * to the index. Callers should inspect the repository's index after this
+ * completes, resolve any conflicts and prepare a commit.
+ *
+ * For compatibility with git, the repository is put into a merging
+ * state. Once the commit is done (or if the user wishes to abort),
+ * you should clear this state by calling
+ * `git_repository_state_cleanup()`.
+ *
+ * @param repo the repository to merge
+ * @param their_heads the heads to merge into
+ * @param their_heads_len the number of heads to merge
+ * @param merge_opts merge options
+ * @param checkout_opts checkout options
+ * @return 0 on success or error code
+ */
+GIT_EXTERN(int) git_merge(
+ git_repository *repo,
+ const git_annotated_commit **their_heads,
+ size_t their_heads_len,
+ const git_merge_options *merge_opts,
+ const git_checkout_options *checkout_opts);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/message.h b/include/git2/message.h
new file mode 100644
index 0000000..cd3ddf7
--- /dev/null
+++ b/include/git2/message.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_message_h__
+#define INCLUDE_git_message_h__
+
+#include "common.h"
+#include "buffer.h"
+
+/**
+ * @file git2/message.h
+ * @brief Git message management routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Clean up excess whitespace and make sure there is a trailing newline in the message.
+ *
+ * Optionally, it can remove lines which start with the comment character.
+ *
+ * @param out The user-allocated git_buf which will be filled with the
+ * cleaned up message.
+ *
+ * @param message The message to be prettified.
+ *
+ * @param strip_comments Non-zero to remove comment lines, 0 to leave them in.
+ *
+ * @param comment_char Comment character. Lines starting with this character
+ * are considered to be comments and removed if `strip_comments` is non-zero.
+ *
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_message_prettify(git_buf *out, const char *message, int strip_comments, char comment_char);
+
+/**
+ * Represents a single git message trailer.
+ */
+typedef struct {
+ const char *key;
+ const char *value;
+} git_message_trailer;
+
+/**
+ * Represents an array of git message trailers.
+ *
+ * Struct members under the private comment are private, subject to change
+ * and should not be used by callers.
+ */
+typedef struct {
+ git_message_trailer *trailers;
+ size_t count;
+
+ /* private */
+ char *_trailer_block;
+} git_message_trailer_array;
+
+/**
+ * Parse trailers out of a message, filling the array pointed to by +arr+.
+ *
+ * Trailers are key/value pairs in the last paragraph of a message, not
+ * including any patches or conflicts that may be present.
+ *
+ * @param arr A pre-allocated git_message_trailer_array struct to be filled in
+ * with any trailers found during parsing.
+ * @param message The message to be parsed
+ * @return 0 on success, or non-zero on error.
+ */
+GIT_EXTERN(int) git_message_trailers(git_message_trailer_array *arr, const char *message);
+
+/**
+ * Clean's up any allocated memory in the git_message_trailer_array filled by
+ * a call to git_message_trailers.
+ *
+ * @param arr The trailer to free.
+ */
+GIT_EXTERN(void) git_message_trailer_array_free(git_message_trailer_array *arr);
+
+/** @} */
+GIT_END_DECL
+
+#endif /* INCLUDE_git_message_h__ */
diff --git a/include/git2/net.h b/include/git2/net.h
new file mode 100644
index 0000000..8103eaf
--- /dev/null
+++ b/include/git2/net.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_net_h__
+#define INCLUDE_git_net_h__
+
+#include "common.h"
+#include "oid.h"
+#include "types.h"
+
+/**
+ * @file git2/net.h
+ * @brief Git networking declarations
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+#define GIT_DEFAULT_PORT "9418"
+
+/**
+ * Direction of the connection.
+ *
+ * We need this because we need to know whether we should call
+ * git-upload-pack or git-receive-pack on the remote end when get_refs
+ * gets called.
+ */
+typedef enum {
+ GIT_DIRECTION_FETCH = 0,
+ GIT_DIRECTION_PUSH = 1
+} git_direction;
+
+/**
+ * Description of a reference advertised by a remote server, given out
+ * on `ls` calls.
+ */
+struct git_remote_head {
+ int local; /* available locally */
+ git_oid oid;
+ git_oid loid;
+ char *name;
+ /**
+ * If the server send a symref mapping for this ref, this will
+ * point to the target.
+ */
+ char *symref_target;
+};
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/notes.h b/include/git2/notes.h
new file mode 100644
index 0000000..c135881
--- /dev/null
+++ b/include/git2/notes.h
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_note_h__
+#define INCLUDE_git_note_h__
+
+#include "oid.h"
+
+/**
+ * @file git2/notes.h
+ * @brief Git notes management routines
+ * @defgroup git_note Git notes management routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Callback for git_note_foreach.
+ *
+ * Receives:
+ * - blob_id: Oid of the blob containing the message
+ * - annotated_object_id: Oid of the git object being annotated
+ * - payload: Payload data passed to `git_note_foreach`
+ */
+typedef int GIT_CALLBACK(git_note_foreach_cb)(
+ const git_oid *blob_id, const git_oid *annotated_object_id, void *payload);
+
+/**
+ * note iterator
+ */
+typedef struct git_iterator git_note_iterator;
+
+/**
+ * Creates a new iterator for notes
+ *
+ * The iterator must be freed manually by the user.
+ *
+ * @param out pointer to the iterator
+ * @param repo repository where to look up the note
+ * @param notes_ref canonical name of the reference to use (optional); defaults to
+ * "refs/notes/commits"
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_note_iterator_new(
+ git_note_iterator **out,
+ git_repository *repo,
+ const char *notes_ref);
+
+/**
+ * Creates a new iterator for notes from a commit
+ *
+ * The iterator must be freed manually by the user.
+ *
+ * @param out pointer to the iterator
+ * @param notes_commit a pointer to the notes commit object
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_note_commit_iterator_new(
+ git_note_iterator **out,
+ git_commit *notes_commit);
+
+/**
+ * Frees an git_note_iterator
+ *
+ * @param it pointer to the iterator
+ */
+GIT_EXTERN(void) git_note_iterator_free(git_note_iterator *it);
+
+/**
+ * Return the current item (note_id and annotated_id) and advance the iterator
+ * internally to the next value
+ *
+ * @param note_id id of blob containing the message
+ * @param annotated_id id of the git object being annotated
+ * @param it pointer to the iterator
+ *
+ * @return 0 (no error), GIT_ITEROVER (iteration is done) or an error code
+ * (negative value)
+ */
+GIT_EXTERN(int) git_note_next(
+ git_oid *note_id,
+ git_oid *annotated_id,
+ git_note_iterator *it);
+
+
+/**
+ * Read the note for an object
+ *
+ * The note must be freed manually by the user.
+ *
+ * @param out pointer to the read note; NULL in case of error
+ * @param repo repository where to look up the note
+ * @param notes_ref canonical name of the reference to use (optional); defaults to
+ * "refs/notes/commits"
+ * @param oid OID of the git object to read the note from
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_note_read(
+ git_note **out,
+ git_repository *repo,
+ const char *notes_ref,
+ const git_oid *oid);
+
+
+/**
+ * Read the note for an object from a note commit
+ *
+ * The note must be freed manually by the user.
+ *
+ * @param out pointer to the read note; NULL in case of error
+ * @param repo repository where to look up the note
+ * @param notes_commit a pointer to the notes commit object
+ * @param oid OID of the git object to read the note from
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_note_commit_read(
+ git_note **out,
+ git_repository *repo,
+ git_commit *notes_commit,
+ const git_oid *oid);
+
+/**
+ * Get the note author
+ *
+ * @param note the note
+ * @return the author
+ */
+GIT_EXTERN(const git_signature *) git_note_author(const git_note *note);
+
+/**
+ * Get the note committer
+ *
+ * @param note the note
+ * @return the committer
+ */
+GIT_EXTERN(const git_signature *) git_note_committer(const git_note *note);
+
+
+/**
+ * Get the note message
+ *
+ * @param note the note
+ * @return the note message
+ */
+GIT_EXTERN(const char *) git_note_message(const git_note *note);
+
+
+/**
+ * Get the note object's id
+ *
+ * @param note the note
+ * @return the note object's id
+ */
+GIT_EXTERN(const git_oid *) git_note_id(const git_note *note);
+
+/**
+ * Add a note for an object
+ *
+ * @param out pointer to store the OID (optional); NULL in case of error
+ * @param repo repository where to store the note
+ * @param notes_ref canonical name of the reference to use (optional);
+ * defaults to "refs/notes/commits"
+ * @param author signature of the notes commit author
+ * @param committer signature of the notes commit committer
+ * @param oid OID of the git object to decorate
+ * @param note Content of the note to add for object oid
+ * @param force Overwrite existing note
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_note_create(
+ git_oid *out,
+ git_repository *repo,
+ const char *notes_ref,
+ const git_signature *author,
+ const git_signature *committer,
+ const git_oid *oid,
+ const char *note,
+ int force);
+
+/**
+ * Add a note for an object from a commit
+ *
+ * This function will create a notes commit for a given object,
+ * the commit is a dangling commit, no reference is created.
+ *
+ * @param notes_commit_out pointer to store the commit (optional);
+ * NULL in case of error
+ * @param notes_blob_out a point to the id of a note blob (optional)
+ * @param repo repository where the note will live
+ * @param parent Pointer to parent note
+ * or NULL if this shall start a new notes tree
+ * @param author signature of the notes commit author
+ * @param committer signature of the notes commit committer
+ * @param oid OID of the git object to decorate
+ * @param note Content of the note to add for object oid
+ * @param allow_note_overwrite Overwrite existing note
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_note_commit_create(
+ git_oid *notes_commit_out,
+ git_oid *notes_blob_out,
+ git_repository *repo,
+ git_commit *parent,
+ const git_signature *author,
+ const git_signature *committer,
+ const git_oid *oid,
+ const char *note,
+ int allow_note_overwrite);
+
+/**
+ * Remove the note for an object
+ *
+ * @param repo repository where the note lives
+ * @param notes_ref canonical name of the reference to use (optional);
+ * defaults to "refs/notes/commits"
+ * @param author signature of the notes commit author
+ * @param committer signature of the notes commit committer
+ * @param oid OID of the git object to remove the note from
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_note_remove(
+ git_repository *repo,
+ const char *notes_ref,
+ const git_signature *author,
+ const git_signature *committer,
+ const git_oid *oid);
+
+/**
+ * Remove the note for an object
+ *
+ * @param notes_commit_out pointer to store the new notes commit (optional);
+ * NULL in case of error.
+ * When removing a note a new tree containing all notes
+ * sans the note to be removed is created and a new commit
+ * pointing to that tree is also created.
+ * In the case where the resulting tree is an empty tree
+ * a new commit pointing to this empty tree will be returned.
+ * @param repo repository where the note lives
+ * @param notes_commit a pointer to the notes commit object
+ * @param author signature of the notes commit author
+ * @param committer signature of the notes commit committer
+ * @param oid OID of the git object to remove the note from
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_note_commit_remove(
+ git_oid *notes_commit_out,
+ git_repository *repo,
+ git_commit *notes_commit,
+ const git_signature *author,
+ const git_signature *committer,
+ const git_oid *oid);
+
+/**
+ * Free a git_note object
+ *
+ * @param note git_note object
+ */
+GIT_EXTERN(void) git_note_free(git_note *note);
+
+/**
+ * Get the default notes reference for a repository
+ *
+ * @param out buffer in which to store the name of the default notes reference
+ * @param repo The Git repository
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_note_default_ref(git_buf *out, git_repository *repo);
+
+/**
+ * Loop over all the notes within a specified namespace
+ * and issue a callback for each one.
+ *
+ * @param repo Repository where to find the notes.
+ *
+ * @param notes_ref Reference to read from (optional); defaults to
+ * "refs/notes/commits".
+ *
+ * @param note_cb Callback to invoke per found annotation. Return non-zero
+ * to stop looping.
+ *
+ * @param payload Extra parameter to callback function.
+ *
+ * @return 0 on success, non-zero callback return value, or error code
+ */
+GIT_EXTERN(int) git_note_foreach(
+ git_repository *repo,
+ const char *notes_ref,
+ git_note_foreach_cb note_cb,
+ void *payload);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/object.h b/include/git2/object.h
new file mode 100644
index 0000000..6384aaa
--- /dev/null
+++ b/include/git2/object.h
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_object_h__
+#define INCLUDE_git_object_h__
+
+#include "common.h"
+#include "types.h"
+#include "oid.h"
+#include "buffer.h"
+
+/**
+ * @file git2/object.h
+ * @brief Git revision object management routines
+ * @defgroup git_object Git revision object management routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+#define GIT_OBJECT_SIZE_MAX UINT64_MAX
+
+/**
+ * Lookup a reference to one of the objects in a repository.
+ *
+ * The generated reference is owned by the repository and
+ * should be closed with the `git_object_free` method
+ * instead of free'd manually.
+ *
+ * The 'type' parameter must match the type of the object
+ * in the odb; the method will fail otherwise.
+ * The special value 'GIT_OBJECT_ANY' may be passed to let
+ * the method guess the object's type.
+ *
+ * @param object pointer to the looked-up object
+ * @param repo the repository to look up the object
+ * @param id the unique identifier for the object
+ * @param type the type of the object
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_object_lookup(
+ git_object **object,
+ git_repository *repo,
+ const git_oid *id,
+ git_object_t type);
+
+/**
+ * Lookup a reference to one of the objects in a repository,
+ * given a prefix of its identifier (short id).
+ *
+ * The object obtained will be so that its identifier
+ * matches the first 'len' hexadecimal characters
+ * (packets of 4 bits) of the given 'id'.
+ * 'len' must be at least GIT_OID_MINPREFIXLEN, and
+ * long enough to identify a unique object matching
+ * the prefix; otherwise the method will fail.
+ *
+ * The generated reference is owned by the repository and
+ * should be closed with the `git_object_free` method
+ * instead of free'd manually.
+ *
+ * The 'type' parameter must match the type of the object
+ * in the odb; the method will fail otherwise.
+ * The special value 'GIT_OBJECT_ANY' may be passed to let
+ * the method guess the object's type.
+ *
+ * @param object_out pointer where to store the looked-up object
+ * @param repo the repository to look up the object
+ * @param id a short identifier for the object
+ * @param len the length of the short identifier
+ * @param type the type of the object
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_object_lookup_prefix(
+ git_object **object_out,
+ git_repository *repo,
+ const git_oid *id,
+ size_t len,
+ git_object_t type);
+
+
+/**
+ * Lookup an object that represents a tree entry.
+ *
+ * @param out buffer that receives a pointer to the object (which must be freed
+ * by the caller)
+ * @param treeish root object that can be peeled to a tree
+ * @param path relative path from the root object to the desired object
+ * @param type type of object desired
+ * @return 0 on success, or an error code
+ */
+GIT_EXTERN(int) git_object_lookup_bypath(
+ git_object **out,
+ const git_object *treeish,
+ const char *path,
+ git_object_t type);
+
+/**
+ * Get the id (SHA1) of a repository object
+ *
+ * @param obj the repository object
+ * @return the SHA1 id
+ */
+GIT_EXTERN(const git_oid *) git_object_id(const git_object *obj);
+
+/**
+ * Get a short abbreviated OID string for the object
+ *
+ * This starts at the "core.abbrev" length (default 7 characters) and
+ * iteratively extends to a longer string if that length is ambiguous.
+ * The result will be unambiguous (at least until new objects are added to
+ * the repository).
+ *
+ * @param out Buffer to write string into
+ * @param obj The object to get an ID for
+ * @return 0 on success, <0 for error
+ */
+GIT_EXTERN(int) git_object_short_id(git_buf *out, const git_object *obj);
+
+/**
+ * Get the object type of an object
+ *
+ * @param obj the repository object
+ * @return the object's type
+ */
+GIT_EXTERN(git_object_t) git_object_type(const git_object *obj);
+
+/**
+ * Get the repository that owns this object
+ *
+ * Freeing or calling `git_repository_close` on the
+ * returned pointer will invalidate the actual object.
+ *
+ * Any other operation may be run on the repository without
+ * affecting the object.
+ *
+ * @param obj the object
+ * @return the repository who owns this object
+ */
+GIT_EXTERN(git_repository *) git_object_owner(const git_object *obj);
+
+/**
+ * Close an open object
+ *
+ * This method instructs the library to close an existing
+ * object; note that git_objects are owned and cached by the repository
+ * so the object may or may not be freed after this library call,
+ * depending on how aggressive is the caching mechanism used
+ * by the repository.
+ *
+ * IMPORTANT:
+ * It *is* necessary to call this method when you stop using
+ * an object. Failure to do so will cause a memory leak.
+ *
+ * @param object the object to close
+ */
+GIT_EXTERN(void) git_object_free(git_object *object);
+
+/**
+ * Convert an object type to its string representation.
+ *
+ * The result is a pointer to a string in static memory and
+ * should not be free()'ed.
+ *
+ * @param type object type to convert.
+ * @return the corresponding string representation.
+ */
+GIT_EXTERN(const char *) git_object_type2string(git_object_t type);
+
+/**
+ * Convert a string object type representation to it's git_object_t.
+ *
+ * @param str the string to convert.
+ * @return the corresponding git_object_t.
+ */
+GIT_EXTERN(git_object_t) git_object_string2type(const char *str);
+
+/**
+ * Determine if the given git_object_t is a valid loose object type.
+ *
+ * @param type object type to test.
+ * @return true if the type represents a valid loose object type,
+ * false otherwise.
+ */
+GIT_EXTERN(int) git_object_typeisloose(git_object_t type);
+
+/**
+ * Recursively peel an object until an object of the specified type is met.
+ *
+ * If the query cannot be satisfied due to the object model,
+ * GIT_EINVALIDSPEC will be returned (e.g. trying to peel a blob to a
+ * tree).
+ *
+ * If you pass `GIT_OBJECT_ANY` as the target type, then the object will
+ * be peeled until the type changes. A tag will be peeled until the
+ * referenced object is no longer a tag, and a commit will be peeled
+ * to a tree. Any other object type will return GIT_EINVALIDSPEC.
+ *
+ * If peeling a tag we discover an object which cannot be peeled to
+ * the target type due to the object model, GIT_EPEEL will be
+ * returned.
+ *
+ * You must free the returned object.
+ *
+ * @param peeled Pointer to the peeled git_object
+ * @param object The object to be processed
+ * @param target_type The type of the requested object (a GIT_OBJECT_ value)
+ * @return 0 on success, GIT_EINVALIDSPEC, GIT_EPEEL, or an error code
+ */
+GIT_EXTERN(int) git_object_peel(
+ git_object **peeled,
+ const git_object *object,
+ git_object_t target_type);
+
+/**
+ * Create an in-memory copy of a Git object. The copy must be
+ * explicitly free'd or it will leak.
+ *
+ * @param dest Pointer to store the copy of the object
+ * @param source Original object to copy
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_object_dup(git_object **dest, git_object *source);
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+/**
+ * Analyzes a buffer of raw object content and determines its validity.
+ * Tree, commit, and tag objects will be parsed and ensured that they
+ * are valid, parseable content. (Blobs are always valid by definition.)
+ * An error message will be set with an informative message if the object
+ * is not valid.
+ *
+ * @warning This function is experimental and its signature may change in
+ * the future.
+ *
+ * @param valid Output pointer to set with validity of the object content
+ * @param buf The contents to validate
+ * @param len The length of the buffer
+ * @param object_type The type of the object in the buffer
+ * @param oid_type The object ID type for the OIDs in the given buffer
+ * @return 0 on success or an error code
+ */
+GIT_EXTERN(int) git_object_rawcontent_is_valid(
+ int *valid,
+ const char *buf,
+ size_t len,
+ git_object_t object_type,
+ git_oid_t oid_type);
+#else
+/**
+ * Analyzes a buffer of raw object content and determines its validity.
+ * Tree, commit, and tag objects will be parsed and ensured that they
+ * are valid, parseable content. (Blobs are always valid by definition.)
+ * An error message will be set with an informative message if the object
+ * is not valid.
+ *
+ * @warning This function is experimental and its signature may change in
+ * the future.
+ *
+ * @param valid Output pointer to set with validity of the object content
+ * @param buf The contents to validate
+ * @param len The length of the buffer
+ * @param object_type The type of the object in the buffer
+ * @return 0 on success or an error code
+ */
+GIT_EXTERN(int) git_object_rawcontent_is_valid(
+ int *valid,
+ const char *buf,
+ size_t len,
+ git_object_t object_type);
+#endif
+
+/** @} */
+GIT_END_DECL
+
+#endif
diff --git a/include/git2/odb.h b/include/git2/odb.h
new file mode 100644
index 0000000..c7d6a89
--- /dev/null
+++ b/include/git2/odb.h
@@ -0,0 +1,654 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_odb_h__
+#define INCLUDE_git_odb_h__
+
+#include "common.h"
+#include "types.h"
+#include "oid.h"
+#include "oidarray.h"
+#include "indexer.h"
+
+/**
+ * @file git2/odb.h
+ * @brief Git object database routines
+ * @defgroup git_odb Git object database routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/** Flags controlling the behavior of ODB lookup operations */
+typedef enum {
+ /**
+ * Don't call `git_odb_refresh` if the lookup fails. Useful when doing
+ * a batch of lookup operations for objects that may legitimately not
+ * exist. When using this flag, you may wish to manually call
+ * `git_odb_refresh` before processing a batch of objects.
+ */
+ GIT_ODB_LOOKUP_NO_REFRESH = (1 << 0)
+} git_odb_lookup_flags_t;
+
+/**
+ * Function type for callbacks from git_odb_foreach.
+ */
+typedef int GIT_CALLBACK(git_odb_foreach_cb)(const git_oid *id, void *payload);
+
+/** Options for configuring a loose object backend. */
+typedef struct {
+ unsigned int version; /**< version for the struct */
+
+ /**
+ * Type of object IDs to use for this object database, or
+ * 0 for default (currently SHA1).
+ */
+ git_oid_t oid_type;
+} git_odb_options;
+
+/* The current version of the diff options structure */
+#define GIT_ODB_OPTIONS_VERSION 1
+
+/* Stack initializer for odb options. Alternatively use
+ * `git_odb_options_init` programmatic initialization.
+ */
+#define GIT_ODB_OPTIONS_INIT { GIT_ODB_OPTIONS_VERSION }
+
+/**
+ * Create a new object database with no backends.
+ *
+ * Before the ODB can be used for read/writing, a custom database
+ * backend must be manually added using `git_odb_add_backend()`
+ *
+ * @param out location to store the database pointer, if opened.
+ * Set to NULL if the open failed.
+ * @param opts the options for this object database or NULL for defaults
+ * @return 0 or an error code
+ */
+#ifdef GIT_EXPERIMENTAL_SHA256
+GIT_EXTERN(int) git_odb_new(git_odb **out, const git_odb_options *opts);
+#else
+GIT_EXTERN(int) git_odb_new(git_odb **out);
+#endif
+
+/**
+ * Create a new object database and automatically add
+ * the two default backends:
+ *
+ * - git_odb_backend_loose: read and write loose object files
+ * from disk, assuming `objects_dir` as the Objects folder
+ *
+ * - git_odb_backend_pack: read objects from packfiles,
+ * assuming `objects_dir` as the Objects folder which
+ * contains a 'pack/' folder with the corresponding data
+ *
+ * @param out location to store the database pointer, if opened.
+ * Set to NULL if the open failed.
+ * @param objects_dir path of the backends' "objects" directory.
+ * @param opts the options for this object database or NULL for defaults
+ * @return 0 or an error code
+ */
+#ifdef GIT_EXPERIMENTAL_SHA256
+GIT_EXTERN(int) git_odb_open(
+ git_odb **out,
+ const char *objects_dir,
+ const git_odb_options *opts);
+#else
+GIT_EXTERN(int) git_odb_open(git_odb **out, const char *objects_dir);
+#endif
+
+/**
+ * Add an on-disk alternate to an existing Object DB.
+ *
+ * Note that the added path must point to an `objects`, not
+ * to a full repository, to use it as an alternate store.
+ *
+ * Alternate backends are always checked for objects *after*
+ * all the main backends have been exhausted.
+ *
+ * Writing is disabled on alternate backends.
+ *
+ * @param odb database to add the backend to
+ * @param path path to the objects folder for the alternate
+ * @return 0 on success, error code otherwise
+ */
+GIT_EXTERN(int) git_odb_add_disk_alternate(git_odb *odb, const char *path);
+
+/**
+ * Close an open object database.
+ *
+ * @param db database pointer to close. If NULL no action is taken.
+ */
+GIT_EXTERN(void) git_odb_free(git_odb *db);
+
+/**
+ * Read an object from the database.
+ *
+ * This method queries all available ODB backends
+ * trying to read the given OID.
+ *
+ * The returned object is reference counted and
+ * internally cached, so it should be closed
+ * by the user once it's no longer in use.
+ *
+ * @param out pointer where to store the read object
+ * @param db database to search for the object in.
+ * @param id identity of the object to read.
+ * @return 0 if the object was read, GIT_ENOTFOUND if the object is
+ * not in the database.
+ */
+GIT_EXTERN(int) git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id);
+
+/**
+ * Read an object from the database, given a prefix
+ * of its identifier.
+ *
+ * This method queries all available ODB backends
+ * trying to match the 'len' first hexadecimal
+ * characters of the 'short_id'.
+ * The remaining (GIT_OID_SHA1_HEXSIZE-len)*4 bits of
+ * 'short_id' must be 0s.
+ * 'len' must be at least GIT_OID_MINPREFIXLEN,
+ * and the prefix must be long enough to identify
+ * a unique object in all the backends; the
+ * method will fail otherwise.
+ *
+ * The returned object is reference counted and
+ * internally cached, so it should be closed
+ * by the user once it's no longer in use.
+ *
+ * @param out pointer where to store the read object
+ * @param db database to search for the object in.
+ * @param short_id a prefix of the id of the object to read.
+ * @param len the length of the prefix
+ * @return 0 if the object was read, GIT_ENOTFOUND if the object is not in the
+ * database. GIT_EAMBIGUOUS if the prefix is ambiguous
+ * (several objects match the prefix)
+ */
+GIT_EXTERN(int) git_odb_read_prefix(git_odb_object **out, git_odb *db, const git_oid *short_id, size_t len);
+
+/**
+ * Read the header of an object from the database, without
+ * reading its full contents.
+ *
+ * The header includes the length and the type of an object.
+ *
+ * Note that most backends do not support reading only the header
+ * of an object, so the whole object will be read and then the
+ * header will be returned.
+ *
+ * @param len_out pointer where to store the length
+ * @param type_out pointer where to store the type
+ * @param db database to search for the object in.
+ * @param id identity of the object to read.
+ * @return 0 if the object was read, GIT_ENOTFOUND if the object is not
+ * in the database.
+ */
+GIT_EXTERN(int) git_odb_read_header(size_t *len_out, git_object_t *type_out, git_odb *db, const git_oid *id);
+
+/**
+ * Determine if the given object can be found in the object database.
+ *
+ * @param db database to be searched for the given object.
+ * @param id the object to search for.
+ * @return 1 if the object was found, 0 otherwise
+ */
+GIT_EXTERN(int) git_odb_exists(git_odb *db, const git_oid *id);
+
+/**
+ * Determine if the given object can be found in the object database, with
+ * extended options.
+ *
+ * @param db database to be searched for the given object.
+ * @param id the object to search for.
+ * @param flags flags affecting the lookup (see `git_odb_lookup_flags_t`)
+ * @return 1 if the object was found, 0 otherwise
+ */
+GIT_EXTERN(int) git_odb_exists_ext(git_odb *db, const git_oid *id, unsigned int flags);
+
+/**
+ * Determine if an object can be found in the object database by an
+ * abbreviated object ID.
+ *
+ * @param out The full OID of the found object if just one is found.
+ * @param db The database to be searched for the given object.
+ * @param short_id A prefix of the id of the object to read.
+ * @param len The length of the prefix.
+ * @return 0 if found, GIT_ENOTFOUND if not found, GIT_EAMBIGUOUS if multiple
+ * matches were found, other value < 0 if there was a read error.
+ */
+GIT_EXTERN(int) git_odb_exists_prefix(
+ git_oid *out, git_odb *db, const git_oid *short_id, size_t len);
+
+/**
+ * The information about object IDs to query in `git_odb_expand_ids`,
+ * which will be populated upon return.
+ */
+typedef struct git_odb_expand_id {
+ /** The object ID to expand */
+ git_oid id;
+
+ /**
+ * The length of the object ID (in nibbles, or packets of 4 bits; the
+ * number of hex characters)
+ * */
+ unsigned short length;
+
+ /**
+ * The (optional) type of the object to search for; leave as `0` or set
+ * to `GIT_OBJECT_ANY` to query for any object matching the ID.
+ */
+ git_object_t type;
+} git_odb_expand_id;
+
+/**
+ * Determine if one or more objects can be found in the object database
+ * by their abbreviated object ID and type.
+ *
+ * The given array will be updated in place: for each abbreviated ID that is
+ * unique in the database, and of the given type (if specified),
+ * the full object ID, object ID length (`GIT_OID_SHA1_HEXSIZE`) and type will be
+ * written back to the array. For IDs that are not found (or are ambiguous),
+ * the array entry will be zeroed.
+ *
+ * Note that since this function operates on multiple objects, the
+ * underlying database will not be asked to be reloaded if an object is
+ * not found (which is unlike other object database operations.)
+ *
+ * @param db The database to be searched for the given objects.
+ * @param ids An array of short object IDs to search for
+ * @param count The length of the `ids` array
+ * @return 0 on success or an error code on failure
+ */
+GIT_EXTERN(int) git_odb_expand_ids(
+ git_odb *db,
+ git_odb_expand_id *ids,
+ size_t count);
+
+/**
+ * Refresh the object database to load newly added files.
+ *
+ * If the object databases have changed on disk while the library
+ * is running, this function will force a reload of the underlying
+ * indexes.
+ *
+ * Use this function when you're confident that an external
+ * application has tampered with the ODB.
+ *
+ * NOTE that it is not necessary to call this function at all. The
+ * library will automatically attempt to refresh the ODB
+ * when a lookup fails, to see if the looked up object exists
+ * on disk but hasn't been loaded yet.
+ *
+ * @param db database to refresh
+ * @return 0 on success, error code otherwise
+ */
+GIT_EXTERN(int) git_odb_refresh(struct git_odb *db);
+
+/**
+ * List all objects available in the database
+ *
+ * The callback will be called for each object available in the
+ * database. Note that the objects are likely to be returned in the index
+ * order, which would make accessing the objects in that order inefficient.
+ * Return a non-zero value from the callback to stop looping.
+ *
+ * @param db database to use
+ * @param cb the callback to call for each object
+ * @param payload data to pass to the callback
+ * @return 0 on success, non-zero callback return value, or error code
+ */
+GIT_EXTERN(int) git_odb_foreach(git_odb *db, git_odb_foreach_cb cb, void *payload);
+
+/**
+ * Write an object directly into the ODB
+ *
+ * This method writes a full object straight into the ODB.
+ * For most cases, it is preferred to write objects through a write
+ * stream, which is both faster and less memory intensive, specially
+ * for big objects.
+ *
+ * This method is provided for compatibility with custom backends
+ * which are not able to support streaming writes
+ *
+ * @param out pointer to store the OID result of the write
+ * @param odb object database where to store the object
+ * @param data buffer with the data to store
+ * @param len size of the buffer
+ * @param type type of the data to store
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_odb_write(git_oid *out, git_odb *odb, const void *data, size_t len, git_object_t type);
+
+/**
+ * Open a stream to write an object into the ODB
+ *
+ * The type and final length of the object must be specified
+ * when opening the stream.
+ *
+ * The returned stream will be of type `GIT_STREAM_WRONLY`, and it
+ * won't be effective until `git_odb_stream_finalize_write` is called
+ * and returns without an error
+ *
+ * The stream must always be freed when done with `git_odb_stream_free` or
+ * will leak memory.
+ *
+ * @see git_odb_stream
+ *
+ * @param out pointer where to store the stream
+ * @param db object database where the stream will write
+ * @param size final size of the object that will be written
+ * @param type type of the object that will be written
+ * @return 0 if the stream was created; error code otherwise
+ */
+GIT_EXTERN(int) git_odb_open_wstream(git_odb_stream **out, git_odb *db, git_object_size_t size, git_object_t type);
+
+/**
+ * Write to an odb stream
+ *
+ * This method will fail if the total number of received bytes exceeds the
+ * size declared with `git_odb_open_wstream()`
+ *
+ * @param stream the stream
+ * @param buffer the data to write
+ * @param len the buffer's length
+ * @return 0 if the write succeeded, error code otherwise
+ */
+GIT_EXTERN(int) git_odb_stream_write(git_odb_stream *stream, const char *buffer, size_t len);
+
+/**
+ * Finish writing to an odb stream
+ *
+ * The object will take its final name and will be available to the
+ * odb.
+ *
+ * This method will fail if the total number of received bytes
+ * differs from the size declared with `git_odb_open_wstream()`
+ *
+ * @param out pointer to store the resulting object's id
+ * @param stream the stream
+ * @return 0 on success, an error code otherwise
+ */
+GIT_EXTERN(int) git_odb_stream_finalize_write(git_oid *out, git_odb_stream *stream);
+
+/**
+ * Read from an odb stream
+ *
+ * Most backends don't implement streaming reads
+ *
+ * @param stream the stream
+ * @param buffer a user-allocated buffer to store the data in.
+ * @param len the buffer's length
+ * @return 0 if the read succeeded, error code otherwise
+ */
+GIT_EXTERN(int) git_odb_stream_read(git_odb_stream *stream, char *buffer, size_t len);
+
+/**
+ * Free an odb stream
+ *
+ * @param stream the stream to free
+ */
+GIT_EXTERN(void) git_odb_stream_free(git_odb_stream *stream);
+
+/**
+ * Open a stream to read an object from the ODB
+ *
+ * Note that most backends do *not* support streaming reads
+ * because they store their objects as compressed/delta'ed blobs.
+ *
+ * It's recommended to use `git_odb_read` instead, which is
+ * assured to work on all backends.
+ *
+ * The returned stream will be of type `GIT_STREAM_RDONLY` and
+ * will have the following methods:
+ *
+ * - stream->read: read `n` bytes from the stream
+ * - stream->free: free the stream
+ *
+ * The stream must always be free'd or will leak memory.
+ *
+ * @see git_odb_stream
+ *
+ * @param out pointer where to store the stream
+ * @param len pointer where to store the length of the object
+ * @param type pointer where to store the type of the object
+ * @param db object database where the stream will read from
+ * @param oid oid of the object the stream will read from
+ * @return 0 if the stream was created, error code otherwise
+ */
+GIT_EXTERN(int) git_odb_open_rstream(
+ git_odb_stream **out,
+ size_t *len,
+ git_object_t *type,
+ git_odb *db,
+ const git_oid *oid);
+
+/**
+ * Open a stream for writing a pack file to the ODB.
+ *
+ * If the ODB layer understands pack files, then the given
+ * packfile will likely be streamed directly to disk (and a
+ * corresponding index created). If the ODB layer does not
+ * understand pack files, the objects will be stored in whatever
+ * format the ODB layer uses.
+ *
+ * @see git_odb_writepack
+ *
+ * @param out pointer to the writepack functions
+ * @param db object database where the stream will read from
+ * @param progress_cb function to call with progress information.
+ * Be aware that this is called inline with network and indexing operations,
+ * so performance may be affected.
+ * @param progress_payload payload for the progress callback
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_odb_write_pack(
+ git_odb_writepack **out,
+ git_odb *db,
+ git_indexer_progress_cb progress_cb,
+ void *progress_payload);
+
+/**
+ * Write a `multi-pack-index` file from all the `.pack` files in the ODB.
+ *
+ * If the ODB layer understands pack files, then this will create a file called
+ * `multi-pack-index` next to the `.pack` and `.idx` files, which will contain
+ * an index of all objects stored in `.pack` files. This will allow for
+ * O(log n) lookup for n objects (regardless of how many packfiles there
+ * exist).
+ *
+ * @param db object database where the `multi-pack-index` file will be written.
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_odb_write_multi_pack_index(
+ git_odb *db);
+
+/**
+ * Determine the object-ID (sha1 or sha256 hash) of a data buffer
+ *
+ * The resulting OID will be the identifier for the data buffer as if
+ * the data buffer it were to written to the ODB.
+ *
+ * @param out the resulting object-ID.
+ * @param data data to hash
+ * @param len size of the data
+ * @param object_type of the data to hash
+ * @param oid_type the oid type to hash to
+ * @return 0 or an error code
+ */
+#ifdef GIT_EXPERIMENTAL_SHA256
+GIT_EXTERN(int) git_odb_hash(
+ git_oid *out,
+ const void *data,
+ size_t len,
+ git_object_t object_type,
+ git_oid_t oid_type);
+#else
+GIT_EXTERN(int) git_odb_hash(git_oid *out, const void *data, size_t len, git_object_t type);
+#endif
+
+/**
+ * Read a file from disk and fill a git_oid with the object id
+ * that the file would have if it were written to the Object
+ * Database as an object of the given type (w/o applying filters).
+ * Similar functionality to git.git's `git hash-object` without
+ * the `-w` flag, however, with the --no-filters flag.
+ * If you need filters, see git_repository_hashfile.
+ *
+ * @param out oid structure the result is written into.
+ * @param path file to read and determine object id for
+ * @param object_type of the data to hash
+ * @param oid_type the oid type to hash to
+ * @return 0 or an error code
+ */
+#ifdef GIT_EXPERIMENTAL_SHA256
+GIT_EXTERN(int) git_odb_hashfile(
+ git_oid *out,
+ const char *path,
+ git_object_t object_type,
+ git_oid_t oid_type);
+#else
+GIT_EXTERN(int) git_odb_hashfile(git_oid *out, const char *path, git_object_t type);
+#endif
+
+/**
+ * Create a copy of an odb_object
+ *
+ * The returned copy must be manually freed with `git_odb_object_free`.
+ * Note that because of an implementation detail, the returned copy will be
+ * the same pointer as `source`: the object is internally refcounted, so the
+ * copy still needs to be freed twice.
+ *
+ * @param dest pointer where to store the copy
+ * @param source object to copy
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_odb_object_dup(git_odb_object **dest, git_odb_object *source);
+
+/**
+ * Close an ODB object
+ *
+ * This method must always be called once a `git_odb_object` is no
+ * longer needed, otherwise memory will leak.
+ *
+ * @param object object to close
+ */
+GIT_EXTERN(void) git_odb_object_free(git_odb_object *object);
+
+/**
+ * Return the OID of an ODB object
+ *
+ * This is the OID from which the object was read from
+ *
+ * @param object the object
+ * @return a pointer to the OID
+ */
+GIT_EXTERN(const git_oid *) git_odb_object_id(git_odb_object *object);
+
+/**
+ * Return the data of an ODB object
+ *
+ * This is the uncompressed, raw data as read from the ODB,
+ * without the leading header.
+ *
+ * This pointer is owned by the object and shall not be free'd.
+ *
+ * @param object the object
+ * @return a pointer to the data
+ */
+GIT_EXTERN(const void *) git_odb_object_data(git_odb_object *object);
+
+/**
+ * Return the size of an ODB object
+ *
+ * This is the real size of the `data` buffer, not the
+ * actual size of the object.
+ *
+ * @param object the object
+ * @return the size
+ */
+GIT_EXTERN(size_t) git_odb_object_size(git_odb_object *object);
+
+/**
+ * Return the type of an ODB object
+ *
+ * @param object the object
+ * @return the type
+ */
+GIT_EXTERN(git_object_t) git_odb_object_type(git_odb_object *object);
+
+/**
+ * Add a custom backend to an existing Object DB
+ *
+ * The backends are checked in relative ordering, based on the
+ * value of the `priority` parameter.
+ *
+ * Read <sys/odb_backend.h> for more information.
+ *
+ * @param odb database to add the backend to
+ * @param backend pointer to a git_odb_backend instance
+ * @param priority Value for ordering the backends queue
+ * @return 0 on success, error code otherwise
+ */
+GIT_EXTERN(int) git_odb_add_backend(git_odb *odb, git_odb_backend *backend, int priority);
+
+/**
+ * Add a custom backend to an existing Object DB; this
+ * backend will work as an alternate.
+ *
+ * Alternate backends are always checked for objects *after*
+ * all the main backends have been exhausted.
+ *
+ * The backends are checked in relative ordering, based on the
+ * value of the `priority` parameter.
+ *
+ * Writing is disabled on alternate backends.
+ *
+ * Read <sys/odb_backend.h> for more information.
+ *
+ * @param odb database to add the backend to
+ * @param backend pointer to a git_odb_backend instance
+ * @param priority Value for ordering the backends queue
+ * @return 0 on success, error code otherwise
+ */
+GIT_EXTERN(int) git_odb_add_alternate(git_odb *odb, git_odb_backend *backend, int priority);
+
+/**
+ * Get the number of ODB backend objects
+ *
+ * @param odb object database
+ * @return number of backends in the ODB
+ */
+GIT_EXTERN(size_t) git_odb_num_backends(git_odb *odb);
+
+/**
+ * Lookup an ODB backend object by index
+ *
+ * @param out output pointer to ODB backend at pos
+ * @param odb object database
+ * @param pos index into object database backend list
+ * @return 0 on success, GIT_ENOTFOUND if pos is invalid, other errors < 0
+ */
+GIT_EXTERN(int) git_odb_get_backend(git_odb_backend **out, git_odb *odb, size_t pos);
+
+/**
+ * Set the git commit-graph for the ODB.
+ *
+ * After a successful call, the ownership of the cgraph parameter will be
+ * transferred to libgit2, and the caller should not free it.
+ *
+ * The commit-graph can also be unset by explicitly passing NULL as the cgraph
+ * parameter.
+ *
+ * @param odb object database
+ * @param cgraph the git commit-graph
+ * @return 0 on success; error code otherwise
+ */
+GIT_EXTERN(int) git_odb_set_commit_graph(git_odb *odb, git_commit_graph *cgraph);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/odb_backend.h b/include/git2/odb_backend.h
new file mode 100644
index 0000000..12dd0fd
--- /dev/null
+++ b/include/git2/odb_backend.h
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_odb_backend_h__
+#define INCLUDE_git_odb_backend_h__
+
+#include "common.h"
+#include "types.h"
+#include "indexer.h"
+
+/**
+ * @file git2/backend.h
+ * @brief Git custom backend functions
+ * @defgroup git_odb Git object database routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/*
+ * Constructors for in-box ODB backends.
+ */
+
+/** Options for configuring a packfile object backend. */
+typedef struct {
+ unsigned int version; /**< version for the struct */
+
+ /**
+ * Type of object IDs to use for this object database, or
+ * 0 for default (currently SHA1).
+ */
+ git_oid_t oid_type;
+} git_odb_backend_pack_options;
+
+/* The current version of the diff options structure */
+#define GIT_ODB_BACKEND_PACK_OPTIONS_VERSION 1
+
+/* Stack initializer for odb pack backend options. Alternatively use
+ * `git_odb_backend_pack_options_init` programmatic initialization.
+ */
+#define GIT_ODB_BACKEND_PACK_OPTIONS_INIT \
+ { GIT_ODB_BACKEND_PACK_OPTIONS_VERSION }
+
+/**
+ * Create a backend for the packfiles.
+ *
+ * @param out location to store the odb backend pointer
+ * @param objects_dir the Git repository's objects directory
+ *
+ * @return 0 or an error code
+ */
+#ifdef GIT_EXPERIMENTAL_SHA256
+GIT_EXTERN(int) git_odb_backend_pack(
+ git_odb_backend **out,
+ const char *objects_dir,
+ const git_odb_backend_pack_options *opts);
+#else
+GIT_EXTERN(int) git_odb_backend_pack(
+ git_odb_backend **out,
+ const char *objects_dir);
+#endif
+
+/**
+ * Create a backend out of a single packfile
+ *
+ * This can be useful for inspecting the contents of a single
+ * packfile.
+ *
+ * @param out location to store the odb backend pointer
+ * @param index_file path to the packfile's .idx file
+ *
+ * @return 0 or an error code
+ */
+#ifdef GIT_EXPERIMENTAL_SHA256
+GIT_EXTERN(int) git_odb_backend_one_pack(
+ git_odb_backend **out,
+ const char *index_file,
+ const git_odb_backend_pack_options *opts);
+#else
+GIT_EXTERN(int) git_odb_backend_one_pack(
+ git_odb_backend **out,
+ const char *index_file);
+#endif
+
+typedef enum {
+ GIT_ODB_BACKEND_LOOSE_FSYNC = (1 << 0)
+} git_odb_backend_loose_flag_t;
+
+/** Options for configuring a loose object backend. */
+typedef struct {
+ unsigned int version; /**< version for the struct */
+
+ /** A combination of the `git_odb_backend_loose_flag_t` types. */
+ uint32_t flags;
+
+ /**
+ * zlib compression level to use (0-9), where 1 is the fastest
+ * at the expense of larger files, and 9 produces the best
+ * compression at the expense of speed. 0 indicates that no
+ * compression should be performed. -1 is the default (currently
+ * optimizing for speed).
+ */
+ int compression_level;
+
+ /** Permissions to use creating a directory or 0 for defaults */
+ unsigned int dir_mode;
+
+ /** Permissions to use creating a file or 0 for defaults */
+ unsigned int file_mode;
+
+ /**
+ * Type of object IDs to use for this object database, or
+ * 0 for default (currently SHA1).
+ */
+ git_oid_t oid_type;
+} git_odb_backend_loose_options;
+
+/* The current version of the diff options structure */
+#define GIT_ODB_BACKEND_LOOSE_OPTIONS_VERSION 1
+
+/* Stack initializer for odb loose backend options. Alternatively use
+ * `git_odb_backend_loose_options_init` programmatic initialization.
+ */
+#define GIT_ODB_BACKEND_LOOSE_OPTIONS_INIT \
+ { GIT_ODB_BACKEND_LOOSE_OPTIONS_VERSION, 0, -1 }
+
+/**
+ * Create a backend for loose objects
+ *
+ * @param out location to store the odb backend pointer
+ * @param objects_dir the Git repository's objects directory
+ * @param opts options for the loose object backend or NULL
+ *
+ * @return 0 or an error code
+ */
+#ifdef GIT_EXPERIMENTAL_SHA256
+GIT_EXTERN(int) git_odb_backend_loose(
+ git_odb_backend **out,
+ const char *objects_dir,
+ git_odb_backend_loose_options *opts);
+#else
+GIT_EXTERN(int) git_odb_backend_loose(
+ git_odb_backend **out,
+ const char *objects_dir,
+ int compression_level,
+ int do_fsync,
+ unsigned int dir_mode,
+ unsigned int file_mode);
+#endif
+
+/** Streaming mode */
+typedef enum {
+ GIT_STREAM_RDONLY = (1 << 1),
+ GIT_STREAM_WRONLY = (1 << 2),
+ GIT_STREAM_RW = (GIT_STREAM_RDONLY | GIT_STREAM_WRONLY)
+} git_odb_stream_t;
+
+/**
+ * A stream to read/write from a backend.
+ *
+ * This represents a stream of data being written to or read from a
+ * backend. When writing, the frontend functions take care of
+ * calculating the object's id and all `finalize_write` needs to do is
+ * store the object with the id it is passed.
+ */
+struct git_odb_stream {
+ git_odb_backend *backend;
+ unsigned int mode;
+ void *hash_ctx;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_oid_t oid_type;
+#endif
+
+ git_object_size_t declared_size;
+ git_object_size_t received_bytes;
+
+ /**
+ * Write at most `len` bytes into `buffer` and advance the stream.
+ */
+ int GIT_CALLBACK(read)(git_odb_stream *stream, char *buffer, size_t len);
+
+ /**
+ * Write `len` bytes from `buffer` into the stream.
+ */
+ int GIT_CALLBACK(write)(git_odb_stream *stream, const char *buffer, size_t len);
+
+ /**
+ * Store the contents of the stream as an object with the id
+ * specified in `oid`.
+ *
+ * This method might not be invoked if:
+ * - an error occurs earlier with the `write` callback,
+ * - the object referred to by `oid` already exists in any backend, or
+ * - the final number of received bytes differs from the size declared
+ * with `git_odb_open_wstream()`
+ */
+ int GIT_CALLBACK(finalize_write)(git_odb_stream *stream, const git_oid *oid);
+
+ /**
+ * Free the stream's memory.
+ *
+ * This method might be called without a call to `finalize_write` if
+ * an error occurs or if the object is already present in the ODB.
+ */
+ void GIT_CALLBACK(free)(git_odb_stream *stream);
+};
+
+/** A stream to write a pack file to the ODB */
+struct git_odb_writepack {
+ git_odb_backend *backend;
+
+ int GIT_CALLBACK(append)(git_odb_writepack *writepack, const void *data, size_t size, git_indexer_progress *stats);
+ int GIT_CALLBACK(commit)(git_odb_writepack *writepack, git_indexer_progress *stats);
+ void GIT_CALLBACK(free)(git_odb_writepack *writepack);
+};
+
+GIT_END_DECL
+
+#endif
diff --git a/include/git2/oid.h b/include/git2/oid.h
new file mode 100644
index 0000000..35b43ef
--- /dev/null
+++ b/include/git2/oid.h
@@ -0,0 +1,373 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_oid_h__
+#define INCLUDE_git_oid_h__
+
+#include "common.h"
+#include "types.h"
+#include "experimental.h"
+
+/**
+ * @file git2/oid.h
+ * @brief Git object id routines
+ * @defgroup git_oid Git object id routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/** The type of object id. */
+typedef enum {
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ GIT_OID_SHA1 = 1, /**< SHA1 */
+ GIT_OID_SHA256 = 2 /**< SHA256 */
+#else
+ GIT_OID_SHA1 = 1 /**< SHA1 */
+#endif
+
+} git_oid_t;
+
+/*
+ * SHA1 is currently the only supported object ID type.
+ */
+
+/** SHA1 is currently libgit2's default oid type. */
+#define GIT_OID_DEFAULT GIT_OID_SHA1
+
+/** Size (in bytes) of a raw/binary sha1 oid */
+#define GIT_OID_SHA1_SIZE 20
+/** Size (in bytes) of a hex formatted sha1 oid */
+#define GIT_OID_SHA1_HEXSIZE (GIT_OID_SHA1_SIZE * 2)
+
+/**
+ * The binary representation of the null sha1 object ID.
+ */
+#ifndef GIT_EXPERIMENTAL_SHA256
+# define GIT_OID_SHA1_ZERO { { 0 } }
+#else
+# define GIT_OID_SHA1_ZERO { GIT_OID_SHA1, { 0 } }
+#endif
+
+/**
+ * The string representation of the null sha1 object ID.
+ */
+#define GIT_OID_SHA1_HEXZERO "0000000000000000000000000000000000000000"
+
+/*
+ * Experimental SHA256 support is a breaking change to the API.
+ * This exists for application compatibility testing.
+ */
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+
+/** Size (in bytes) of a raw/binary sha256 oid */
+# define GIT_OID_SHA256_SIZE 32
+/** Size (in bytes) of a hex formatted sha256 oid */
+# define GIT_OID_SHA256_HEXSIZE (GIT_OID_SHA256_SIZE * 2)
+
+/**
+ * The binary representation of the null sha256 object ID.
+ */
+# define GIT_OID_SHA256_ZERO { GIT_OID_SHA256, { 0 } }
+
+/**
+ * The string representation of the null sha256 object ID.
+ */
+# define GIT_OID_SHA256_HEXZERO "0000000000000000000000000000000000000000000000000000000000000000"
+
+#endif
+
+/* Maximum possible object ID size in raw / hex string format. */
+#ifndef GIT_EXPERIMENTAL_SHA256
+# define GIT_OID_MAX_SIZE GIT_OID_SHA1_SIZE
+# define GIT_OID_MAX_HEXSIZE GIT_OID_SHA1_HEXSIZE
+#else
+# define GIT_OID_MAX_SIZE GIT_OID_SHA256_SIZE
+# define GIT_OID_MAX_HEXSIZE GIT_OID_SHA256_HEXSIZE
+#endif
+
+/** Minimum length (in number of hex characters,
+ * i.e. packets of 4 bits) of an oid prefix */
+#define GIT_OID_MINPREFIXLEN 4
+
+/** Unique identity of any object (commit, tree, blob, tag). */
+typedef struct git_oid {
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ /** type of object id */
+ unsigned char type;
+#endif
+
+ /** raw binary formatted id */
+ unsigned char id[GIT_OID_MAX_SIZE];
+} git_oid;
+
+/**
+ * Parse a hex formatted object id into a git_oid.
+ *
+ * The appropriate number of bytes for the given object ID type will
+ * be read from the string - 40 bytes for SHA1, 64 bytes for SHA256.
+ * The given string need not be NUL terminated.
+ *
+ * @param out oid structure the result is written into.
+ * @param str input hex string; must be pointing at the start of
+ * the hex sequence and have at least the number of bytes
+ * needed for an oid encoded in hex (40 bytes for sha1,
+ * 256 bytes for sha256).
+ * @param type the type of object id
+ * @return 0 or an error code
+ */
+#ifdef GIT_EXPERIMENTAL_SHA256
+GIT_EXTERN(int) git_oid_fromstr(git_oid *out, const char *str, git_oid_t type);
+#else
+GIT_EXTERN(int) git_oid_fromstr(git_oid *out, const char *str);
+#endif
+
+/**
+ * Parse a hex formatted NUL-terminated string into a git_oid.
+ *
+ * @param out oid structure the result is written into.
+ * @param str input hex string; must be null-terminated.
+ * @param type the type of object id
+ * @return 0 or an error code
+ */
+#ifdef GIT_EXPERIMENTAL_SHA256
+GIT_EXTERN(int) git_oid_fromstrp(git_oid *out, const char *str, git_oid_t type);
+#else
+GIT_EXTERN(int) git_oid_fromstrp(git_oid *out, const char *str);
+#endif
+
+/**
+ * Parse N characters of a hex formatted object id into a git_oid.
+ *
+ * If N is odd, the last byte's high nibble will be read in and the
+ * low nibble set to zero.
+ *
+ * @param out oid structure the result is written into.
+ * @param str input hex string of at least size `length`
+ * @param length length of the input string
+ * @param type the type of object id
+ * @return 0 or an error code
+ */
+#ifdef GIT_EXPERIMENTAL_SHA256
+GIT_EXTERN(int) git_oid_fromstrn(git_oid *out, const char *str, size_t length, git_oid_t type);
+#else
+GIT_EXTERN(int) git_oid_fromstrn(git_oid *out, const char *str, size_t length);
+#endif
+
+/**
+ * Copy an already raw oid into a git_oid structure.
+ *
+ * @param out oid structure the result is written into.
+ * @param raw the raw input bytes to be copied.
+ * @return 0 on success or error code
+ */
+#ifdef GIT_EXPERIMENTAL_SHA256
+GIT_EXTERN(int) git_oid_fromraw(git_oid *out, const unsigned char *raw, git_oid_t type);
+#else
+GIT_EXTERN(int) git_oid_fromraw(git_oid *out, const unsigned char *raw);
+#endif
+
+/**
+ * Format a git_oid into a hex string.
+ *
+ * @param out output hex string; must be pointing at the start of
+ * the hex sequence and have at least the number of bytes
+ * needed for an oid encoded in hex (40 bytes for SHA1,
+ * 64 bytes for SHA256). Only the oid digits are written;
+ * a '\\0' terminator must be added by the caller if it is
+ * required.
+ * @param id oid structure to format.
+ * @return 0 on success or error code
+ */
+GIT_EXTERN(int) git_oid_fmt(char *out, const git_oid *id);
+
+/**
+ * Format a git_oid into a partial hex string.
+ *
+ * @param out output hex string; you say how many bytes to write.
+ * If the number of bytes is > GIT_OID_SHA1_HEXSIZE, extra bytes
+ * will be zeroed; if not, a '\0' terminator is NOT added.
+ * @param n number of characters to write into out string
+ * @param id oid structure to format.
+ * @return 0 on success or error code
+ */
+GIT_EXTERN(int) git_oid_nfmt(char *out, size_t n, const git_oid *id);
+
+/**
+ * Format a git_oid into a loose-object path string.
+ *
+ * The resulting string is "aa/...", where "aa" is the first two
+ * hex digits of the oid and "..." is the remaining 38 digits.
+ *
+ * @param out output hex string; must be pointing at the start of
+ * the hex sequence and have at least the number of bytes
+ * needed for an oid encoded in hex (41 bytes for SHA1,
+ * 65 bytes for SHA256). Only the oid digits are written;
+ * a '\\0' terminator must be added by the caller if it
+ * is required.
+ * @param id oid structure to format.
+ * @return 0 on success, non-zero callback return value, or error code
+ */
+GIT_EXTERN(int) git_oid_pathfmt(char *out, const git_oid *id);
+
+/**
+ * Format a git_oid into a statically allocated c-string.
+ *
+ * The c-string is owned by the library and should not be freed
+ * by the user. If libgit2 is built with thread support, the string
+ * will be stored in TLS (i.e. one buffer per thread) to allow for
+ * concurrent calls of the function.
+ *
+ * @param oid The oid structure to format
+ * @return the c-string or NULL on failure
+ */
+GIT_EXTERN(char *) git_oid_tostr_s(const git_oid *oid);
+
+/**
+ * Format a git_oid into a buffer as a hex format c-string.
+ *
+ * If the buffer is smaller than the size of a hex-formatted oid string
+ * plus an additional byte (GIT_OID_SHA_HEXSIZE + 1 for SHA1 or
+ * GIT_OID_SHA256_HEXSIZE + 1 for SHA256), then the resulting
+ * oid c-string will be truncated to n-1 characters (but will still be
+ * NUL-byte terminated).
+ *
+ * If there are any input parameter errors (out == NULL, n == 0, oid ==
+ * NULL), then a pointer to an empty string is returned, so that the
+ * return value can always be printed.
+ *
+ * @param out the buffer into which the oid string is output.
+ * @param n the size of the out buffer.
+ * @param id the oid structure to format.
+ * @return the out buffer pointer, assuming no input parameter
+ * errors, otherwise a pointer to an empty string.
+ */
+GIT_EXTERN(char *) git_oid_tostr(char *out, size_t n, const git_oid *id);
+
+/**
+ * Copy an oid from one structure to another.
+ *
+ * @param out oid structure the result is written into.
+ * @param src oid structure to copy from.
+ * @return 0 on success or error code
+ */
+GIT_EXTERN(int) git_oid_cpy(git_oid *out, const git_oid *src);
+
+/**
+ * Compare two oid structures.
+ *
+ * @param a first oid structure.
+ * @param b second oid structure.
+ * @return <0, 0, >0 if a < b, a == b, a > b.
+ */
+GIT_EXTERN(int) git_oid_cmp(const git_oid *a, const git_oid *b);
+
+/**
+ * Compare two oid structures for equality
+ *
+ * @param a first oid structure.
+ * @param b second oid structure.
+ * @return true if equal, false otherwise
+ */
+GIT_EXTERN(int) git_oid_equal(const git_oid *a, const git_oid *b);
+
+/**
+ * Compare the first 'len' hexadecimal characters (packets of 4 bits)
+ * of two oid structures.
+ *
+ * @param a first oid structure.
+ * @param b second oid structure.
+ * @param len the number of hex chars to compare
+ * @return 0 in case of a match
+ */
+GIT_EXTERN(int) git_oid_ncmp(const git_oid *a, const git_oid *b, size_t len);
+
+/**
+ * Check if an oid equals an hex formatted object id.
+ *
+ * @param id oid structure.
+ * @param str input hex string of an object id.
+ * @return 0 in case of a match, -1 otherwise.
+ */
+GIT_EXTERN(int) git_oid_streq(const git_oid *id, const char *str);
+
+/**
+ * Compare an oid to an hex formatted object id.
+ *
+ * @param id oid structure.
+ * @param str input hex string of an object id.
+ * @return -1 if str is not valid, <0 if id sorts before str,
+ * 0 if id matches str, >0 if id sorts after str.
+ */
+GIT_EXTERN(int) git_oid_strcmp(const git_oid *id, const char *str);
+
+/**
+ * Check is an oid is all zeros.
+ *
+ * @return 1 if all zeros, 0 otherwise.
+ */
+GIT_EXTERN(int) git_oid_is_zero(const git_oid *id);
+
+/**
+ * OID Shortener object
+ */
+typedef struct git_oid_shorten git_oid_shorten;
+
+/**
+ * Create a new OID shortener.
+ *
+ * The OID shortener is used to process a list of OIDs
+ * in text form and return the shortest length that would
+ * uniquely identify all of them.
+ *
+ * E.g. look at the result of `git log --abbrev`.
+ *
+ * @param min_length The minimal length for all identifiers,
+ * which will be used even if shorter OIDs would still
+ * be unique.
+ * @return a `git_oid_shorten` instance, NULL if OOM
+ */
+GIT_EXTERN(git_oid_shorten *) git_oid_shorten_new(size_t min_length);
+
+/**
+ * Add a new OID to set of shortened OIDs and calculate
+ * the minimal length to uniquely identify all the OIDs in
+ * the set.
+ *
+ * The OID is expected to be a 40-char hexadecimal string.
+ * The OID is owned by the user and will not be modified
+ * or freed.
+ *
+ * For performance reasons, there is a hard-limit of how many
+ * OIDs can be added to a single set (around ~32000, assuming
+ * a mostly randomized distribution), which should be enough
+ * for any kind of program, and keeps the algorithm fast and
+ * memory-efficient.
+ *
+ * Attempting to add more than those OIDs will result in a
+ * GIT_ERROR_INVALID error
+ *
+ * @param os a `git_oid_shorten` instance
+ * @param text_id an OID in text form
+ * @return the minimal length to uniquely identify all OIDs
+ * added so far to the set; or an error code (<0) if an
+ * error occurs.
+ */
+GIT_EXTERN(int) git_oid_shorten_add(git_oid_shorten *os, const char *text_id);
+
+/**
+ * Free an OID shortener instance
+ *
+ * @param os a `git_oid_shorten` instance
+ */
+GIT_EXTERN(void) git_oid_shorten_free(git_oid_shorten *os);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/oidarray.h b/include/git2/oidarray.h
new file mode 100644
index 0000000..94fc58d
--- /dev/null
+++ b/include/git2/oidarray.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_oidarray_h__
+#define INCLUDE_git_oidarray_h__
+
+#include "common.h"
+#include "oid.h"
+
+GIT_BEGIN_DECL
+
+/** Array of object ids */
+typedef struct git_oidarray {
+ git_oid *ids;
+ size_t count;
+} git_oidarray;
+
+/**
+ * Free the object IDs contained in an oid_array. This method should
+ * be called on `git_oidarray` objects that were provided by the
+ * library. Not doing so will result in a memory leak.
+ *
+ * This does not free the `git_oidarray` itself, since the library will
+ * never allocate that object directly itself.
+ *
+ * @param array git_oidarray from which to free oid data
+ */
+GIT_EXTERN(void) git_oidarray_dispose(git_oidarray *array);
+
+/** @} */
+GIT_END_DECL
+
+#endif
+
diff --git a/include/git2/pack.h b/include/git2/pack.h
new file mode 100644
index 0000000..0f6bd2a
--- /dev/null
+++ b/include/git2/pack.h
@@ -0,0 +1,267 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_pack_h__
+#define INCLUDE_git_pack_h__
+
+#include "common.h"
+#include "oid.h"
+#include "indexer.h"
+
+/**
+ * @file git2/pack.h
+ * @brief Git pack management routines
+ *
+ * Packing objects
+ * ---------------
+ *
+ * Creation of packfiles requires two steps:
+ *
+ * - First, insert all the objects you want to put into the packfile
+ * using `git_packbuilder_insert` and `git_packbuilder_insert_tree`.
+ * It's important to add the objects in recency order ("in the order
+ * that they are 'reachable' from head").
+ *
+ * "ANY order will give you a working pack, ... [but it is] the thing
+ * that gives packs good locality. It keeps the objects close to the
+ * head (whether they are old or new, but they are _reachable_ from the
+ * head) at the head of the pack. So packs actually have absolutely
+ * _wonderful_ IO patterns." - Linus Torvalds
+ * git.git/Documentation/technical/pack-heuristics.txt
+ *
+ * - Second, use `git_packbuilder_write` or `git_packbuilder_foreach` to
+ * write the resulting packfile.
+ *
+ * libgit2 will take care of the delta ordering and generation.
+ * `git_packbuilder_set_threads` can be used to adjust the number of
+ * threads used for the process.
+ *
+ * See tests/pack/packbuilder.c for an example.
+ *
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Stages that are reported by the packbuilder progress callback.
+ */
+typedef enum {
+ GIT_PACKBUILDER_ADDING_OBJECTS = 0,
+ GIT_PACKBUILDER_DELTAFICATION = 1
+} git_packbuilder_stage_t;
+
+/**
+ * Initialize a new packbuilder
+ *
+ * @param out The new packbuilder object
+ * @param repo The repository
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_packbuilder_new(git_packbuilder **out, git_repository *repo);
+
+/**
+ * Set number of threads to spawn
+ *
+ * By default, libgit2 won't spawn any threads at all;
+ * when set to 0, libgit2 will autodetect the number of
+ * CPUs.
+ *
+ * @param pb The packbuilder
+ * @param n Number of threads to spawn
+ * @return number of actual threads to be used
+ */
+GIT_EXTERN(unsigned int) git_packbuilder_set_threads(git_packbuilder *pb, unsigned int n);
+
+/**
+ * Insert a single object
+ *
+ * For an optimal pack it's mandatory to insert objects in recency order,
+ * commits followed by trees and blobs.
+ *
+ * @param pb The packbuilder
+ * @param id The oid of the commit
+ * @param name The name; might be NULL
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_packbuilder_insert(git_packbuilder *pb, const git_oid *id, const char *name);
+
+/**
+ * Insert a root tree object
+ *
+ * This will add the tree as well as all referenced trees and blobs.
+ *
+ * @param pb The packbuilder
+ * @param id The oid of the root tree
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_packbuilder_insert_tree(git_packbuilder *pb, const git_oid *id);
+
+/**
+ * Insert a commit object
+ *
+ * This will add a commit as well as the completed referenced tree.
+ *
+ * @param pb The packbuilder
+ * @param id The oid of the commit
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_packbuilder_insert_commit(git_packbuilder *pb, const git_oid *id);
+
+/**
+ * Insert objects as given by the walk
+ *
+ * Those commits and all objects they reference will be inserted into
+ * the packbuilder.
+ *
+ * @param pb the packbuilder
+ * @param walk the revwalk to use to fill the packbuilder
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_packbuilder_insert_walk(git_packbuilder *pb, git_revwalk *walk);
+
+/**
+ * Recursively insert an object and its referenced objects
+ *
+ * Insert the object as well as any object it references.
+ *
+ * @param pb the packbuilder
+ * @param id the id of the root object to insert
+ * @param name optional name for the object
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_packbuilder_insert_recur(git_packbuilder *pb, const git_oid *id, const char *name);
+
+/**
+ * Write the contents of the packfile to an in-memory buffer
+ *
+ * The contents of the buffer will become a valid packfile, even though there
+ * will be no attached index
+ *
+ * @param buf Buffer where to write the packfile
+ * @param pb The packbuilder
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_packbuilder_write_buf(git_buf *buf, git_packbuilder *pb);
+
+/**
+ * Write the new pack and corresponding index file to path.
+ *
+ * @param pb The packbuilder
+ * @param path Path to the directory where the packfile and index should be stored, or NULL for default location
+ * @param mode permissions to use creating a packfile or 0 for defaults
+ * @param progress_cb function to call with progress information from the indexer (optional)
+ * @param progress_cb_payload payload for the progress callback (optional)
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_packbuilder_write(
+ git_packbuilder *pb,
+ const char *path,
+ unsigned int mode,
+ git_indexer_progress_cb progress_cb,
+ void *progress_cb_payload);
+
+#ifndef GIT_DEPRECATE_HARD
+/**
+ * Get the packfile's hash
+ *
+ * A packfile's name is derived from the sorted hashing of all object
+ * names. This is only correct after the packfile has been written.
+ *
+ * @deprecated use git_packbuilder_name
+ * @param pb The packbuilder object
+ * @return 0 or an error code
+ */
+GIT_EXTERN(const git_oid *) git_packbuilder_hash(git_packbuilder *pb);
+#endif
+
+/**
+ * Get the unique name for the resulting packfile.
+ *
+ * The packfile's name is derived from the packfile's content.
+ * This is only correct after the packfile has been written.
+ *
+ * @param pb the packbuilder instance
+ * @return a NUL terminated string for the packfile name
+ */
+GIT_EXTERN(const char *) git_packbuilder_name(git_packbuilder *pb);
+
+/**
+ * Callback used to iterate over packed objects
+ *
+ * @see git_packbuilder_foreach
+ *
+ * @param buf A pointer to the object's data
+ * @param size The size of the underlying object
+ * @param payload Payload passed to git_packbuilder_foreach
+ * @return non-zero to terminate the iteration
+ */
+typedef int GIT_CALLBACK(git_packbuilder_foreach_cb)(void *buf, size_t size, void *payload);
+
+/**
+ * Create the new pack and pass each object to the callback
+ *
+ * @param pb the packbuilder
+ * @param cb the callback to call with each packed object's buffer
+ * @param payload the callback's data
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_packbuilder_foreach(git_packbuilder *pb, git_packbuilder_foreach_cb cb, void *payload);
+
+/**
+ * Get the total number of objects the packbuilder will write out
+ *
+ * @param pb the packbuilder
+ * @return the number of objects in the packfile
+ */
+GIT_EXTERN(size_t) git_packbuilder_object_count(git_packbuilder *pb);
+
+/**
+ * Get the number of objects the packbuilder has already written out
+ *
+ * @param pb the packbuilder
+ * @return the number of objects which have already been written
+ */
+GIT_EXTERN(size_t) git_packbuilder_written(git_packbuilder *pb);
+
+/** Packbuilder progress notification function */
+typedef int GIT_CALLBACK(git_packbuilder_progress)(
+ int stage,
+ uint32_t current,
+ uint32_t total,
+ void *payload);
+
+/**
+ * Set the callbacks for a packbuilder
+ *
+ * @param pb The packbuilder object
+ * @param progress_cb Function to call with progress information during
+ * pack building. Be aware that this is called inline with pack building
+ * operations, so performance may be affected.
+ * @param progress_cb_payload Payload for progress callback.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_packbuilder_set_callbacks(
+ git_packbuilder *pb,
+ git_packbuilder_progress progress_cb,
+ void *progress_cb_payload);
+
+/**
+ * Free the packbuilder and all associated data
+ *
+ * @param pb The packbuilder
+ */
+GIT_EXTERN(void) git_packbuilder_free(git_packbuilder *pb);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/patch.h b/include/git2/patch.h
new file mode 100644
index 0000000..9cf758a
--- /dev/null
+++ b/include/git2/patch.h
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_patch_h__
+#define INCLUDE_git_patch_h__
+
+#include "common.h"
+#include "types.h"
+#include "oid.h"
+#include "diff.h"
+
+/**
+ * @file git2/patch.h
+ * @brief Patch handling routines.
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * The diff patch is used to store all the text diffs for a delta.
+ *
+ * You can easily loop over the content of patches and get information about
+ * them.
+ */
+typedef struct git_patch git_patch;
+
+/**
+ * Get the repository associated with this patch. May be NULL.
+ *
+ * @param patch the patch
+ * @return a pointer to the repository
+ */
+GIT_EXTERN(git_repository *) git_patch_owner(const git_patch *patch);
+
+/**
+ * Return a patch for an entry in the diff list.
+ *
+ * The `git_patch` is a newly created object contains the text diffs
+ * for the delta. You have to call `git_patch_free()` when you are
+ * done with it. You can use the patch object to loop over all the hunks
+ * and lines in the diff of the one delta.
+ *
+ * For an unchanged file or a binary file, no `git_patch` will be
+ * created, the output will be set to NULL, and the `binary` flag will be
+ * set true in the `git_diff_delta` structure.
+ *
+ * It is okay to pass NULL for either of the output parameters; if you pass
+ * NULL for the `git_patch`, then the text diff will not be calculated.
+ *
+ * @param out Output parameter for the delta patch object
+ * @param diff Diff list object
+ * @param idx Index into diff list
+ * @return 0 on success, other value < 0 on error
+ */
+GIT_EXTERN(int) git_patch_from_diff(
+ git_patch **out, git_diff *diff, size_t idx);
+
+/**
+ * Directly generate a patch from the difference between two blobs.
+ *
+ * This is just like `git_diff_blobs()` except it generates a patch object
+ * for the difference instead of directly making callbacks. You can use the
+ * standard `git_patch` accessor functions to read the patch data, and
+ * you must call `git_patch_free()` on the patch when done.
+ *
+ * @param out The generated patch; NULL on error
+ * @param old_blob Blob for old side of diff, or NULL for empty blob
+ * @param old_as_path Treat old blob as if it had this filename; can be NULL
+ * @param new_blob Blob for new side of diff, or NULL for empty blob
+ * @param new_as_path Treat new blob as if it had this filename; can be NULL
+ * @param opts Options for diff, or NULL for default options
+ * @return 0 on success or error code < 0
+ */
+GIT_EXTERN(int) git_patch_from_blobs(
+ git_patch **out,
+ const git_blob *old_blob,
+ const char *old_as_path,
+ const git_blob *new_blob,
+ const char *new_as_path,
+ const git_diff_options *opts);
+
+/**
+ * Directly generate a patch from the difference between a blob and a buffer.
+ *
+ * This is just like `git_diff_blob_to_buffer()` except it generates a patch
+ * object for the difference instead of directly making callbacks. You can
+ * use the standard `git_patch` accessor functions to read the patch
+ * data, and you must call `git_patch_free()` on the patch when done.
+ *
+ * @param out The generated patch; NULL on error
+ * @param old_blob Blob for old side of diff, or NULL for empty blob
+ * @param old_as_path Treat old blob as if it had this filename; can be NULL
+ * @param buffer Raw data for new side of diff, or NULL for empty
+ * @param buffer_len Length of raw data for new side of diff
+ * @param buffer_as_path Treat buffer as if it had this filename; can be NULL
+ * @param opts Options for diff, or NULL for default options
+ * @return 0 on success or error code < 0
+ */
+GIT_EXTERN(int) git_patch_from_blob_and_buffer(
+ git_patch **out,
+ const git_blob *old_blob,
+ const char *old_as_path,
+ const void *buffer,
+ size_t buffer_len,
+ const char *buffer_as_path,
+ const git_diff_options *opts);
+
+/**
+ * Directly generate a patch from the difference between two buffers.
+ *
+ * This is just like `git_diff_buffers()` except it generates a patch
+ * object for the difference instead of directly making callbacks. You can
+ * use the standard `git_patch` accessor functions to read the patch
+ * data, and you must call `git_patch_free()` on the patch when done.
+ *
+ * @param out The generated patch; NULL on error
+ * @param old_buffer Raw data for old side of diff, or NULL for empty
+ * @param old_len Length of the raw data for old side of the diff
+ * @param old_as_path Treat old buffer as if it had this filename; can be NULL
+ * @param new_buffer Raw data for new side of diff, or NULL for empty
+ * @param new_len Length of raw data for new side of diff
+ * @param new_as_path Treat buffer as if it had this filename; can be NULL
+ * @param opts Options for diff, or NULL for default options
+ * @return 0 on success or error code < 0
+ */
+GIT_EXTERN(int) git_patch_from_buffers(
+ git_patch **out,
+ const void *old_buffer,
+ size_t old_len,
+ const char *old_as_path,
+ const void *new_buffer,
+ size_t new_len,
+ const char *new_as_path,
+ const git_diff_options *opts);
+
+/**
+ * Free a git_patch object.
+ *
+ * @param patch The patch to free.
+ */
+GIT_EXTERN(void) git_patch_free(git_patch *patch);
+
+/**
+ * Get the delta associated with a patch. This delta points to internal
+ * data and you do not have to release it when you are done with it.
+ *
+ * @param patch The patch in which to get the delta.
+ * @return The delta associated with the patch.
+ */
+GIT_EXTERN(const git_diff_delta *) git_patch_get_delta(const git_patch *patch);
+
+/**
+ * Get the number of hunks in a patch
+ *
+ * @param patch The patch in which to get the number of hunks.
+ * @return The number of hunks of the patch.
+ */
+GIT_EXTERN(size_t) git_patch_num_hunks(const git_patch *patch);
+
+/**
+ * Get line counts of each type in a patch.
+ *
+ * This helps imitate a diff --numstat type of output. For that purpose,
+ * you only need the `total_additions` and `total_deletions` values, but we
+ * include the `total_context` line count in case you want the total number
+ * of lines of diff output that will be generated.
+ *
+ * All outputs are optional. Pass NULL if you don't need a particular count.
+ *
+ * @param total_context Count of context lines in output, can be NULL.
+ * @param total_additions Count of addition lines in output, can be NULL.
+ * @param total_deletions Count of deletion lines in output, can be NULL.
+ * @param patch The git_patch object
+ * @return 0 on success, <0 on error
+ */
+GIT_EXTERN(int) git_patch_line_stats(
+ size_t *total_context,
+ size_t *total_additions,
+ size_t *total_deletions,
+ const git_patch *patch);
+
+/**
+ * Get the information about a hunk in a patch
+ *
+ * Given a patch and a hunk index into the patch, this returns detailed
+ * information about that hunk. Any of the output pointers can be passed
+ * as NULL if you don't care about that particular piece of information.
+ *
+ * @param out Output pointer to git_diff_hunk of hunk
+ * @param lines_in_hunk Output count of total lines in this hunk
+ * @param patch Input pointer to patch object
+ * @param hunk_idx Input index of hunk to get information about
+ * @return 0 on success, GIT_ENOTFOUND if hunk_idx out of range, <0 on error
+ */
+GIT_EXTERN(int) git_patch_get_hunk(
+ const git_diff_hunk **out,
+ size_t *lines_in_hunk,
+ git_patch *patch,
+ size_t hunk_idx);
+
+/**
+ * Get the number of lines in a hunk.
+ *
+ * @param patch The git_patch object
+ * @param hunk_idx Index of the hunk
+ * @return Number of lines in hunk or GIT_ENOTFOUND if invalid hunk index
+ */
+GIT_EXTERN(int) git_patch_num_lines_in_hunk(
+ const git_patch *patch,
+ size_t hunk_idx);
+
+/**
+ * Get data about a line in a hunk of a patch.
+ *
+ * Given a patch, a hunk index, and a line index in the hunk, this
+ * will return a lot of details about that line. If you pass a hunk
+ * index larger than the number of hunks or a line index larger than
+ * the number of lines in the hunk, this will return -1.
+ *
+ * @param out The git_diff_line data for this line
+ * @param patch The patch to look in
+ * @param hunk_idx The index of the hunk
+ * @param line_of_hunk The index of the line in the hunk
+ * @return 0 on success, <0 on failure
+ */
+GIT_EXTERN(int) git_patch_get_line_in_hunk(
+ const git_diff_line **out,
+ git_patch *patch,
+ size_t hunk_idx,
+ size_t line_of_hunk);
+
+/**
+ * Look up size of patch diff data in bytes
+ *
+ * This returns the raw size of the patch data. This only includes the
+ * actual data from the lines of the diff, not the file or hunk headers.
+ *
+ * If you pass `include_context` as true (non-zero), this will be the size
+ * of all of the diff output; if you pass it as false (zero), this will
+ * only include the actual changed lines (as if `context_lines` was 0).
+ *
+ * @param patch A git_patch representing changes to one file
+ * @param include_context Include context lines in size if non-zero
+ * @param include_hunk_headers Include hunk header lines if non-zero
+ * @param include_file_headers Include file header lines if non-zero
+ * @return The number of bytes of data
+ */
+GIT_EXTERN(size_t) git_patch_size(
+ git_patch *patch,
+ int include_context,
+ int include_hunk_headers,
+ int include_file_headers);
+
+/**
+ * Serialize the patch to text via callback.
+ *
+ * Returning a non-zero value from the callback will terminate the iteration
+ * and return that value to the caller.
+ *
+ * @param patch A git_patch representing changes to one file
+ * @param print_cb Callback function to output lines of the patch. Will be
+ * called for file headers, hunk headers, and diff lines.
+ * @param payload Reference pointer that will be passed to your callbacks.
+ * @return 0 on success, non-zero callback return value, or error code
+ */
+GIT_EXTERN(int) git_patch_print(
+ git_patch *patch,
+ git_diff_line_cb print_cb,
+ void *payload);
+
+/**
+ * Get the content of a patch as a single diff text.
+ *
+ * @param out The git_buf to be filled in
+ * @param patch A git_patch representing changes to one file
+ * @return 0 on success, <0 on failure.
+ */
+GIT_EXTERN(int) git_patch_to_buf(
+ git_buf *out,
+ git_patch *patch);
+
+GIT_END_DECL
+
+/**@}*/
+
+#endif
diff --git a/include/git2/pathspec.h b/include/git2/pathspec.h
new file mode 100644
index 0000000..acbd5cd
--- /dev/null
+++ b/include/git2/pathspec.h
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_pathspec_h__
+#define INCLUDE_git_pathspec_h__
+
+#include "common.h"
+#include "types.h"
+#include "strarray.h"
+#include "diff.h"
+
+GIT_BEGIN_DECL
+
+/**
+ * Compiled pathspec
+ */
+typedef struct git_pathspec git_pathspec;
+
+/**
+ * List of filenames matching a pathspec
+ */
+typedef struct git_pathspec_match_list git_pathspec_match_list;
+
+/**
+ * Options controlling how pathspec match should be executed
+ */
+typedef enum {
+ GIT_PATHSPEC_DEFAULT = 0,
+
+ /**
+ * GIT_PATHSPEC_IGNORE_CASE forces match to ignore case; otherwise
+ * match will use native case sensitivity of platform filesystem
+ */
+ GIT_PATHSPEC_IGNORE_CASE = (1u << 0),
+
+ /**
+ * GIT_PATHSPEC_USE_CASE forces case sensitive match; otherwise
+ * match will use native case sensitivity of platform filesystem
+ */
+ GIT_PATHSPEC_USE_CASE = (1u << 1),
+
+ /**
+ * GIT_PATHSPEC_NO_GLOB disables glob patterns and just uses simple
+ * string comparison for matching
+ */
+ GIT_PATHSPEC_NO_GLOB = (1u << 2),
+
+ /**
+ * GIT_PATHSPEC_NO_MATCH_ERROR means the match functions return error
+ * code GIT_ENOTFOUND if no matches are found; otherwise no matches is
+ * still success (return 0) but `git_pathspec_match_list_entrycount`
+ * will indicate 0 matches.
+ */
+ GIT_PATHSPEC_NO_MATCH_ERROR = (1u << 3),
+
+ /**
+ * GIT_PATHSPEC_FIND_FAILURES means that the `git_pathspec_match_list`
+ * should track which patterns matched which files so that at the end of
+ * the match we can identify patterns that did not match any files.
+ */
+ GIT_PATHSPEC_FIND_FAILURES = (1u << 4),
+
+ /**
+ * GIT_PATHSPEC_FAILURES_ONLY means that the `git_pathspec_match_list`
+ * does not need to keep the actual matching filenames. Use this to
+ * just test if there were any matches at all or in combination with
+ * GIT_PATHSPEC_FIND_FAILURES to validate a pathspec.
+ */
+ GIT_PATHSPEC_FAILURES_ONLY = (1u << 5)
+} git_pathspec_flag_t;
+
+/**
+ * Compile a pathspec
+ *
+ * @param out Output of the compiled pathspec
+ * @param pathspec A git_strarray of the paths to match
+ * @return 0 on success, <0 on failure
+ */
+GIT_EXTERN(int) git_pathspec_new(
+ git_pathspec **out, const git_strarray *pathspec);
+
+/**
+ * Free a pathspec
+ *
+ * @param ps The compiled pathspec
+ */
+GIT_EXTERN(void) git_pathspec_free(git_pathspec *ps);
+
+/**
+ * Try to match a path against a pathspec
+ *
+ * Unlike most of the other pathspec matching functions, this will not
+ * fall back on the native case-sensitivity for your platform. You must
+ * explicitly pass flags to control case sensitivity or else this will
+ * fall back on being case sensitive.
+ *
+ * @param ps The compiled pathspec
+ * @param flags Combination of git_pathspec_flag_t options to control match
+ * @param path The pathname to attempt to match
+ * @return 1 is path matches spec, 0 if it does not
+ */
+GIT_EXTERN(int) git_pathspec_matches_path(
+ const git_pathspec *ps, uint32_t flags, const char *path);
+
+/**
+ * Match a pathspec against the working directory of a repository.
+ *
+ * This matches the pathspec against the current files in the working
+ * directory of the repository. It is an error to invoke this on a bare
+ * repo. This handles git ignores (i.e. ignored files will not be
+ * considered to match the `pathspec` unless the file is tracked in the
+ * index).
+ *
+ * If `out` is not NULL, this returns a `git_patchspec_match_list`. That
+ * contains the list of all matched filenames (unless you pass the
+ * `GIT_PATHSPEC_FAILURES_ONLY` flag) and may also contain the list of
+ * pathspecs with no match (if you used the `GIT_PATHSPEC_FIND_FAILURES`
+ * flag). You must call `git_pathspec_match_list_free()` on this object.
+ *
+ * @param out Output list of matches; pass NULL to just get return value
+ * @param repo The repository in which to match; bare repo is an error
+ * @param flags Combination of git_pathspec_flag_t options to control match
+ * @param ps Pathspec to be matched
+ * @return 0 on success, -1 on error, GIT_ENOTFOUND if no matches and
+ * the GIT_PATHSPEC_NO_MATCH_ERROR flag was given
+ */
+GIT_EXTERN(int) git_pathspec_match_workdir(
+ git_pathspec_match_list **out,
+ git_repository *repo,
+ uint32_t flags,
+ git_pathspec *ps);
+
+/**
+ * Match a pathspec against entries in an index.
+ *
+ * This matches the pathspec against the files in the repository index.
+ *
+ * NOTE: At the moment, the case sensitivity of this match is controlled
+ * by the current case-sensitivity of the index object itself and the
+ * USE_CASE and IGNORE_CASE flags will have no effect. This behavior will
+ * be corrected in a future release.
+ *
+ * If `out` is not NULL, this returns a `git_patchspec_match_list`. That
+ * contains the list of all matched filenames (unless you pass the
+ * `GIT_PATHSPEC_FAILURES_ONLY` flag) and may also contain the list of
+ * pathspecs with no match (if you used the `GIT_PATHSPEC_FIND_FAILURES`
+ * flag). You must call `git_pathspec_match_list_free()` on this object.
+ *
+ * @param out Output list of matches; pass NULL to just get return value
+ * @param index The index to match against
+ * @param flags Combination of git_pathspec_flag_t options to control match
+ * @param ps Pathspec to be matched
+ * @return 0 on success, -1 on error, GIT_ENOTFOUND if no matches and
+ * the GIT_PATHSPEC_NO_MATCH_ERROR flag is used
+ */
+GIT_EXTERN(int) git_pathspec_match_index(
+ git_pathspec_match_list **out,
+ git_index *index,
+ uint32_t flags,
+ git_pathspec *ps);
+
+/**
+ * Match a pathspec against files in a tree.
+ *
+ * This matches the pathspec against the files in the given tree.
+ *
+ * If `out` is not NULL, this returns a `git_patchspec_match_list`. That
+ * contains the list of all matched filenames (unless you pass the
+ * `GIT_PATHSPEC_FAILURES_ONLY` flag) and may also contain the list of
+ * pathspecs with no match (if you used the `GIT_PATHSPEC_FIND_FAILURES`
+ * flag). You must call `git_pathspec_match_list_free()` on this object.
+ *
+ * @param out Output list of matches; pass NULL to just get return value
+ * @param tree The root-level tree to match against
+ * @param flags Combination of git_pathspec_flag_t options to control match
+ * @param ps Pathspec to be matched
+ * @return 0 on success, -1 on error, GIT_ENOTFOUND if no matches and
+ * the GIT_PATHSPEC_NO_MATCH_ERROR flag is used
+ */
+GIT_EXTERN(int) git_pathspec_match_tree(
+ git_pathspec_match_list **out,
+ git_tree *tree,
+ uint32_t flags,
+ git_pathspec *ps);
+
+/**
+ * Match a pathspec against files in a diff list.
+ *
+ * This matches the pathspec against the files in the given diff list.
+ *
+ * If `out` is not NULL, this returns a `git_patchspec_match_list`. That
+ * contains the list of all matched filenames (unless you pass the
+ * `GIT_PATHSPEC_FAILURES_ONLY` flag) and may also contain the list of
+ * pathspecs with no match (if you used the `GIT_PATHSPEC_FIND_FAILURES`
+ * flag). You must call `git_pathspec_match_list_free()` on this object.
+ *
+ * @param out Output list of matches; pass NULL to just get return value
+ * @param diff A generated diff list
+ * @param flags Combination of git_pathspec_flag_t options to control match
+ * @param ps Pathspec to be matched
+ * @return 0 on success, -1 on error, GIT_ENOTFOUND if no matches and
+ * the GIT_PATHSPEC_NO_MATCH_ERROR flag is used
+ */
+GIT_EXTERN(int) git_pathspec_match_diff(
+ git_pathspec_match_list **out,
+ git_diff *diff,
+ uint32_t flags,
+ git_pathspec *ps);
+
+/**
+ * Free memory associates with a git_pathspec_match_list
+ *
+ * @param m The git_pathspec_match_list to be freed
+ */
+GIT_EXTERN(void) git_pathspec_match_list_free(git_pathspec_match_list *m);
+
+/**
+ * Get the number of items in a match list.
+ *
+ * @param m The git_pathspec_match_list object
+ * @return Number of items in match list
+ */
+GIT_EXTERN(size_t) git_pathspec_match_list_entrycount(
+ const git_pathspec_match_list *m);
+
+/**
+ * Get a matching filename by position.
+ *
+ * This routine cannot be used if the match list was generated by
+ * `git_pathspec_match_diff`. If so, it will always return NULL.
+ *
+ * @param m The git_pathspec_match_list object
+ * @param pos The index into the list
+ * @return The filename of the match
+ */
+GIT_EXTERN(const char *) git_pathspec_match_list_entry(
+ const git_pathspec_match_list *m, size_t pos);
+
+/**
+ * Get a matching diff delta by position.
+ *
+ * This routine can only be used if the match list was generated by
+ * `git_pathspec_match_diff`. Otherwise it will always return NULL.
+ *
+ * @param m The git_pathspec_match_list object
+ * @param pos The index into the list
+ * @return The filename of the match
+ */
+GIT_EXTERN(const git_diff_delta *) git_pathspec_match_list_diff_entry(
+ const git_pathspec_match_list *m, size_t pos);
+
+/**
+ * Get the number of pathspec items that did not match.
+ *
+ * This will be zero unless you passed GIT_PATHSPEC_FIND_FAILURES when
+ * generating the git_pathspec_match_list.
+ *
+ * @param m The git_pathspec_match_list object
+ * @return Number of items in original pathspec that had no matches
+ */
+GIT_EXTERN(size_t) git_pathspec_match_list_failed_entrycount(
+ const git_pathspec_match_list *m);
+
+/**
+ * Get an original pathspec string that had no matches.
+ *
+ * This will be return NULL for positions out of range.
+ *
+ * @param m The git_pathspec_match_list object
+ * @param pos The index into the failed items
+ * @return The pathspec pattern that didn't match anything
+ */
+GIT_EXTERN(const char *) git_pathspec_match_list_failed_entry(
+ const git_pathspec_match_list *m, size_t pos);
+
+GIT_END_DECL
+#endif
diff --git a/include/git2/proxy.h b/include/git2/proxy.h
new file mode 100644
index 0000000..cfc0c64
--- /dev/null
+++ b/include/git2/proxy.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_proxy_h__
+#define INCLUDE_git_proxy_h__
+
+#include "common.h"
+
+#include "cert.h"
+#include "credential.h"
+
+GIT_BEGIN_DECL
+
+/**
+ * The type of proxy to use.
+ */
+typedef enum {
+ /**
+ * Do not attempt to connect through a proxy
+ *
+ * If built against libcurl, it itself may attempt to connect
+ * to a proxy if the environment variables specify it.
+ */
+ GIT_PROXY_NONE,
+ /**
+ * Try to auto-detect the proxy from the git configuration.
+ */
+ GIT_PROXY_AUTO,
+ /**
+ * Connect via the URL given in the options
+ */
+ GIT_PROXY_SPECIFIED
+} git_proxy_t;
+
+/**
+ * Options for connecting through a proxy
+ *
+ * Note that not all types may be supported, depending on the platform
+ * and compilation options.
+ */
+typedef struct {
+ unsigned int version;
+
+ /**
+ * The type of proxy to use, by URL, auto-detect.
+ */
+ git_proxy_t type;
+
+ /**
+ * The URL of the proxy.
+ */
+ const char *url;
+
+ /**
+ * This will be called if the remote host requires
+ * authentication in order to connect to it.
+ *
+ * Returning GIT_PASSTHROUGH will make libgit2 behave as
+ * though this field isn't set.
+ */
+ git_credential_acquire_cb credentials;
+
+ /**
+ * If cert verification fails, this will be called to let the
+ * user make the final decision of whether to allow the
+ * connection to proceed. Returns 0 to allow the connection
+ * or a negative value to indicate an error.
+ */
+ git_transport_certificate_check_cb certificate_check;
+
+ /**
+ * Payload to be provided to the credentials and certificate
+ * check callbacks.
+ */
+ void *payload;
+} git_proxy_options;
+
+#define GIT_PROXY_OPTIONS_VERSION 1
+#define GIT_PROXY_OPTIONS_INIT {GIT_PROXY_OPTIONS_VERSION}
+
+/**
+ * Initialize git_proxy_options structure
+ *
+ * Initializes a `git_proxy_options` with default values. Equivalent to
+ * creating an instance with `GIT_PROXY_OPTIONS_INIT`.
+ *
+ * @param opts The `git_proxy_options` struct to initialize.
+ * @param version The struct version; pass `GIT_PROXY_OPTIONS_VERSION`.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_proxy_options_init(git_proxy_options *opts, unsigned int version);
+
+GIT_END_DECL
+
+#endif
diff --git a/include/git2/rebase.h b/include/git2/rebase.h
new file mode 100644
index 0000000..b1ac71f
--- /dev/null
+++ b/include/git2/rebase.h
@@ -0,0 +1,399 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_rebase_h__
+#define INCLUDE_git_rebase_h__
+
+#include "common.h"
+#include "types.h"
+#include "oid.h"
+#include "annotated_commit.h"
+#include "merge.h"
+#include "checkout.h"
+#include "commit.h"
+
+/**
+ * @file git2/rebase.h
+ * @brief Git rebase routines
+ * @defgroup git_rebase Git merge routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Rebase options
+ *
+ * Use to tell the rebase machinery how to operate.
+ */
+typedef struct {
+ unsigned int version;
+
+ /**
+ * Used by `git_rebase_init`, this will instruct other clients working
+ * on this rebase that you want a quiet rebase experience, which they
+ * may choose to provide in an application-specific manner. This has no
+ * effect upon libgit2 directly, but is provided for interoperability
+ * between Git tools.
+ */
+ int quiet;
+
+ /**
+ * Used by `git_rebase_init`, this will begin an in-memory rebase,
+ * which will allow callers to step through the rebase operations and
+ * commit the rebased changes, but will not rewind HEAD or update the
+ * repository to be in a rebasing state. This will not interfere with
+ * the working directory (if there is one).
+ */
+ int inmemory;
+
+ /**
+ * Used by `git_rebase_finish`, this is the name of the notes reference
+ * used to rewrite notes for rebased commits when finishing the rebase;
+ * if NULL, the contents of the configuration option `notes.rewriteRef`
+ * is examined, unless the configuration option `notes.rewrite.rebase`
+ * is set to false. If `notes.rewriteRef` is also NULL, notes will
+ * not be rewritten.
+ */
+ const char *rewrite_notes_ref;
+
+ /**
+ * Options to control how trees are merged during `git_rebase_next`.
+ */
+ git_merge_options merge_options;
+
+ /**
+ * Options to control how files are written during `git_rebase_init`,
+ * `git_rebase_next` and `git_rebase_abort`. Note that a minimum
+ * strategy of `GIT_CHECKOUT_SAFE` is defaulted in `init` and `next`,
+ * and a minimum strategy of `GIT_CHECKOUT_FORCE` is defaulted in
+ * `abort` to match git semantics.
+ */
+ git_checkout_options checkout_options;
+
+ /**
+ * Optional callback that allows users to override commit
+ * creation in `git_rebase_commit`. If specified, users can
+ * create their own commit and provide the commit ID, which
+ * may be useful for signing commits or otherwise customizing
+ * the commit creation.
+ *
+ * If this callback returns `GIT_PASSTHROUGH`, then
+ * `git_rebase_commit` will continue to create the commit.
+ */
+ git_commit_create_cb commit_create_cb;
+
+#ifdef GIT_DEPRECATE_HARD
+ void *reserved;
+#else
+ /**
+ * If provided, this will be called with the commit content, allowing
+ * a signature to be added to the rebase commit. Can be skipped with
+ * GIT_PASSTHROUGH. If GIT_PASSTHROUGH is returned, a commit will be made
+ * without a signature.
+ *
+ * This field is only used when performing git_rebase_commit.
+ *
+ * This callback is not invoked if a `git_commit_create_cb` is
+ * specified.
+ *
+ * This callback is deprecated; users should provide a
+ * creation callback as `commit_create_cb` that produces a
+ * commit buffer, signs it, and commits it.
+ */
+ int (*signing_cb)(git_buf *, git_buf *, const char *, void *);
+#endif
+
+ /**
+ * This will be passed to each of the callbacks in this struct
+ * as the last parameter.
+ */
+ void *payload;
+} git_rebase_options;
+
+/**
+ * Type of rebase operation in-progress after calling `git_rebase_next`.
+ */
+typedef enum {
+ /**
+ * The given commit is to be cherry-picked. The client should commit
+ * the changes and continue if there are no conflicts.
+ */
+ GIT_REBASE_OPERATION_PICK = 0,
+
+ /**
+ * The given commit is to be cherry-picked, but the client should prompt
+ * the user to provide an updated commit message.
+ */
+ GIT_REBASE_OPERATION_REWORD,
+
+ /**
+ * The given commit is to be cherry-picked, but the client should stop
+ * to allow the user to edit the changes before committing them.
+ */
+ GIT_REBASE_OPERATION_EDIT,
+
+ /**
+ * The given commit is to be squashed into the previous commit. The
+ * commit message will be merged with the previous message.
+ */
+ GIT_REBASE_OPERATION_SQUASH,
+
+ /**
+ * The given commit is to be squashed into the previous commit. The
+ * commit message from this commit will be discarded.
+ */
+ GIT_REBASE_OPERATION_FIXUP,
+
+ /**
+ * No commit will be cherry-picked. The client should run the given
+ * command and (if successful) continue.
+ */
+ GIT_REBASE_OPERATION_EXEC
+} git_rebase_operation_t;
+
+#define GIT_REBASE_OPTIONS_VERSION 1
+#define GIT_REBASE_OPTIONS_INIT \
+ { GIT_REBASE_OPTIONS_VERSION, 0, 0, NULL, GIT_MERGE_OPTIONS_INIT, \
+ GIT_CHECKOUT_OPTIONS_INIT, NULL, NULL }
+
+/** Indicates that a rebase operation is not (yet) in progress. */
+#define GIT_REBASE_NO_OPERATION SIZE_MAX
+
+/**
+ * A rebase operation
+ *
+ * Describes a single instruction/operation to be performed during the
+ * rebase.
+ */
+typedef struct {
+ /** The type of rebase operation. */
+ git_rebase_operation_t type;
+
+ /**
+ * The commit ID being cherry-picked. This will be populated for
+ * all operations except those of type `GIT_REBASE_OPERATION_EXEC`.
+ */
+ const git_oid id;
+
+ /**
+ * The executable the user has requested be run. This will only
+ * be populated for operations of type `GIT_REBASE_OPERATION_EXEC`.
+ */
+ const char *exec;
+} git_rebase_operation;
+
+/**
+ * Initialize git_rebase_options structure
+ *
+ * Initializes a `git_rebase_options` with default values. Equivalent to
+ * creating an instance with `GIT_REBASE_OPTIONS_INIT`.
+ *
+ * @param opts The `git_rebase_options` struct to initialize.
+ * @param version The struct version; pass `GIT_REBASE_OPTIONS_VERSION`.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_rebase_options_init(
+ git_rebase_options *opts,
+ unsigned int version);
+
+/**
+ * Initializes a rebase operation to rebase the changes in `branch`
+ * relative to `upstream` onto another branch. To begin the rebase
+ * process, call `git_rebase_next`. When you have finished with this
+ * object, call `git_rebase_free`.
+ *
+ * @param out Pointer to store the rebase object
+ * @param repo The repository to perform the rebase
+ * @param branch The terminal commit to rebase, or NULL to rebase the
+ * current branch
+ * @param upstream The commit to begin rebasing from, or NULL to rebase all
+ * reachable commits
+ * @param onto The branch to rebase onto, or NULL to rebase onto the given
+ * upstream
+ * @param opts Options to specify how rebase is performed, or NULL
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_rebase_init(
+ git_rebase **out,
+ git_repository *repo,
+ const git_annotated_commit *branch,
+ const git_annotated_commit *upstream,
+ const git_annotated_commit *onto,
+ const git_rebase_options *opts);
+
+/**
+ * Opens an existing rebase that was previously started by either an
+ * invocation of `git_rebase_init` or by another client.
+ *
+ * @param out Pointer to store the rebase object
+ * @param repo The repository that has a rebase in-progress
+ * @param opts Options to specify how rebase is performed
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_rebase_open(
+ git_rebase **out,
+ git_repository *repo,
+ const git_rebase_options *opts);
+
+/**
+ * Gets the original `HEAD` ref name for merge rebases.
+ *
+ * @param rebase The in-progress rebase.
+ * @return The original `HEAD` ref name
+ */
+GIT_EXTERN(const char *) git_rebase_orig_head_name(git_rebase *rebase);
+
+/**
+ * Gets the original `HEAD` id for merge rebases.
+ *
+ * @param rebase The in-progress rebase.
+ * @return The original `HEAD` id
+ */
+GIT_EXTERN(const git_oid *) git_rebase_orig_head_id(git_rebase *rebase);
+
+/**
+ * Gets the `onto` ref name for merge rebases.
+ *
+ * @param rebase The in-progress rebase.
+ * @return The `onto` ref name
+ */
+GIT_EXTERN(const char *) git_rebase_onto_name(git_rebase *rebase);
+
+/**
+ * Gets the `onto` id for merge rebases.
+ *
+ * @param rebase The in-progress rebase.
+ * @return The `onto` id
+ */
+GIT_EXTERN(const git_oid *) git_rebase_onto_id(git_rebase *rebase);
+
+/**
+ * Gets the count of rebase operations that are to be applied.
+ *
+ * @param rebase The in-progress rebase
+ * @return The number of rebase operations in total
+ */
+GIT_EXTERN(size_t) git_rebase_operation_entrycount(git_rebase *rebase);
+
+/**
+ * Gets the index of the rebase operation that is currently being applied.
+ * If the first operation has not yet been applied (because you have
+ * called `init` but not yet `next`) then this returns
+ * `GIT_REBASE_NO_OPERATION`.
+ *
+ * @param rebase The in-progress rebase
+ * @return The index of the rebase operation currently being applied.
+ */
+GIT_EXTERN(size_t) git_rebase_operation_current(git_rebase *rebase);
+
+/**
+ * Gets the rebase operation specified by the given index.
+ *
+ * @param rebase The in-progress rebase
+ * @param idx The index of the rebase operation to retrieve
+ * @return The rebase operation or NULL if `idx` was out of bounds
+ */
+GIT_EXTERN(git_rebase_operation *) git_rebase_operation_byindex(
+ git_rebase *rebase,
+ size_t idx);
+
+/**
+ * Performs the next rebase operation and returns the information about it.
+ * If the operation is one that applies a patch (which is any operation except
+ * GIT_REBASE_OPERATION_EXEC) then the patch will be applied and the index and
+ * working directory will be updated with the changes. If there are conflicts,
+ * you will need to address those before committing the changes.
+ *
+ * @param operation Pointer to store the rebase operation that is to be performed next
+ * @param rebase The rebase in progress
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_rebase_next(
+ git_rebase_operation **operation,
+ git_rebase *rebase);
+
+/**
+ * Gets the index produced by the last operation, which is the result
+ * of `git_rebase_next` and which will be committed by the next
+ * invocation of `git_rebase_commit`. This is useful for resolving
+ * conflicts in an in-memory rebase before committing them. You must
+ * call `git_index_free` when you are finished with this.
+ *
+ * This is only applicable for in-memory rebases; for rebases within
+ * a working directory, the changes were applied to the repository's
+ * index.
+ *
+ * @param index The result index of the last operation.
+ * @param rebase The in-progress rebase.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_rebase_inmemory_index(
+ git_index **index,
+ git_rebase *rebase);
+
+/**
+ * Commits the current patch. You must have resolved any conflicts that
+ * were introduced during the patch application from the `git_rebase_next`
+ * invocation.
+ *
+ * @param id Pointer in which to store the OID of the newly created commit
+ * @param rebase The rebase that is in-progress
+ * @param author The author of the updated commit, or NULL to keep the
+ * author from the original commit
+ * @param committer The committer of the rebase
+ * @param message_encoding The encoding for the message in the commit,
+ * represented with a standard encoding name. If message is NULL,
+ * this should also be NULL, and the encoding from the original
+ * commit will be maintained. If message is specified, this may be
+ * NULL to indicate that "UTF-8" is to be used.
+ * @param message The message for this commit, or NULL to use the message
+ * from the original commit.
+ * @return Zero on success, GIT_EUNMERGED if there are unmerged changes in
+ * the index, GIT_EAPPLIED if the current commit has already
+ * been applied to the upstream and there is nothing to commit,
+ * -1 on failure.
+ */
+GIT_EXTERN(int) git_rebase_commit(
+ git_oid *id,
+ git_rebase *rebase,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *message_encoding,
+ const char *message);
+
+/**
+ * Aborts a rebase that is currently in progress, resetting the repository
+ * and working directory to their state before rebase began.
+ *
+ * @param rebase The rebase that is in-progress
+ * @return Zero on success; GIT_ENOTFOUND if a rebase is not in progress,
+ * -1 on other errors.
+ */
+GIT_EXTERN(int) git_rebase_abort(git_rebase *rebase);
+
+/**
+ * Finishes a rebase that is currently in progress once all patches have
+ * been applied.
+ *
+ * @param rebase The rebase that is in-progress
+ * @param signature The identity that is finishing the rebase (optional)
+ * @return Zero on success; -1 on error
+ */
+GIT_EXTERN(int) git_rebase_finish(
+ git_rebase *rebase,
+ const git_signature *signature);
+
+/**
+ * Frees the `git_rebase` object.
+ *
+ * @param rebase The rebase object
+ */
+GIT_EXTERN(void) git_rebase_free(git_rebase *rebase);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/refdb.h b/include/git2/refdb.h
new file mode 100644
index 0000000..c4849ab
--- /dev/null
+++ b/include/git2/refdb.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_refdb_h__
+#define INCLUDE_git_refdb_h__
+
+#include "common.h"
+#include "types.h"
+#include "oid.h"
+#include "refs.h"
+
+/**
+ * @file git2/refdb.h
+ * @brief Git custom refs backend functions
+ * @defgroup git_refdb Git custom refs backend API
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Create a new reference database with no backends.
+ *
+ * Before the Ref DB can be used for read/writing, a custom database
+ * backend must be manually set using `git_refdb_set_backend()`
+ *
+ * @param out location to store the database pointer, if opened.
+ * Set to NULL if the open failed.
+ * @param repo the repository
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_refdb_new(git_refdb **out, git_repository *repo);
+
+/**
+ * Create a new reference database and automatically add
+ * the default backends:
+ *
+ * - git_refdb_dir: read and write loose and packed refs
+ * from disk, assuming the repository dir as the folder
+ *
+ * @param out location to store the database pointer, if opened.
+ * Set to NULL if the open failed.
+ * @param repo the repository
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_refdb_open(git_refdb **out, git_repository *repo);
+
+/**
+ * Suggests that the given refdb compress or optimize its references.
+ * This mechanism is implementation specific. For on-disk reference
+ * databases, for example, this may pack all loose references.
+ *
+ * @param refdb The reference database to optimize.
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_refdb_compress(git_refdb *refdb);
+
+/**
+ * Close an open reference database.
+ *
+ * @param refdb reference database pointer or NULL
+ */
+GIT_EXTERN(void) git_refdb_free(git_refdb *refdb);
+
+/** @} */
+GIT_END_DECL
+
+#endif
diff --git a/include/git2/reflog.h b/include/git2/reflog.h
new file mode 100644
index 0000000..ec365c1
--- /dev/null
+++ b/include/git2/reflog.h
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_reflog_h__
+#define INCLUDE_git_reflog_h__
+
+#include "common.h"
+#include "types.h"
+#include "oid.h"
+
+/**
+ * @file git2/reflog.h
+ * @brief Git reflog management routines
+ * @defgroup git_reflog Git reflog management routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Read the reflog for the given reference
+ *
+ * If there is no reflog file for the given
+ * reference yet, an empty reflog object will
+ * be returned.
+ *
+ * The reflog must be freed manually by using
+ * git_reflog_free().
+ *
+ * @param out pointer to reflog
+ * @param repo the repository
+ * @param name reference to look up
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_reflog_read(git_reflog **out, git_repository *repo, const char *name);
+
+/**
+ * Write an existing in-memory reflog object back to disk
+ * using an atomic file lock.
+ *
+ * @param reflog an existing reflog object
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_reflog_write(git_reflog *reflog);
+
+/**
+ * Add a new entry to the in-memory reflog.
+ *
+ * `msg` is optional and can be NULL.
+ *
+ * @param reflog an existing reflog object
+ * @param id the OID the reference is now pointing to
+ * @param committer the signature of the committer
+ * @param msg the reflog message
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_reflog_append(git_reflog *reflog, const git_oid *id, const git_signature *committer, const char *msg);
+
+/**
+ * Rename a reflog
+ *
+ * The reflog to be renamed is expected to already exist
+ *
+ * The new name will be checked for validity.
+ * See `git_reference_create_symbolic()` for rules about valid names.
+ *
+ * @param repo the repository
+ * @param old_name the old name of the reference
+ * @param name the new name of the reference
+ * @return 0 on success, GIT_EINVALIDSPEC or an error code
+ */
+GIT_EXTERN(int) git_reflog_rename(git_repository *repo, const char *old_name, const char *name);
+
+/**
+ * Delete the reflog for the given reference
+ *
+ * @param repo the repository
+ * @param name the reflog to delete
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_reflog_delete(git_repository *repo, const char *name);
+
+/**
+ * Get the number of log entries in a reflog
+ *
+ * @param reflog the previously loaded reflog
+ * @return the number of log entries
+ */
+GIT_EXTERN(size_t) git_reflog_entrycount(git_reflog *reflog);
+
+/**
+ * Lookup an entry by its index
+ *
+ * Requesting the reflog entry with an index of 0 (zero) will
+ * return the most recently created entry.
+ *
+ * @param reflog a previously loaded reflog
+ * @param idx the position of the entry to lookup. Should be greater than or
+ * equal to 0 (zero) and less than `git_reflog_entrycount()`.
+ * @return the entry; NULL if not found
+ */
+GIT_EXTERN(const git_reflog_entry *) git_reflog_entry_byindex(const git_reflog *reflog, size_t idx);
+
+/**
+ * Remove an entry from the reflog by its index
+ *
+ * To ensure there's no gap in the log history, set `rewrite_previous_entry`
+ * param value to 1. When deleting entry `n`, member old_oid of entry `n-1`
+ * (if any) will be updated with the value of member new_oid of entry `n+1`.
+ *
+ * @param reflog a previously loaded reflog.
+ *
+ * @param idx the position of the entry to remove. Should be greater than or
+ * equal to 0 (zero) and less than `git_reflog_entrycount()`.
+ *
+ * @param rewrite_previous_entry 1 to rewrite the history; 0 otherwise.
+ *
+ * @return 0 on success, GIT_ENOTFOUND if the entry doesn't exist
+ * or an error code.
+ */
+GIT_EXTERN(int) git_reflog_drop(
+ git_reflog *reflog,
+ size_t idx,
+ int rewrite_previous_entry);
+
+/**
+ * Get the old oid
+ *
+ * @param entry a reflog entry
+ * @return the old oid
+ */
+GIT_EXTERN(const git_oid *) git_reflog_entry_id_old(const git_reflog_entry *entry);
+
+/**
+ * Get the new oid
+ *
+ * @param entry a reflog entry
+ * @return the new oid at this time
+ */
+GIT_EXTERN(const git_oid *) git_reflog_entry_id_new(const git_reflog_entry *entry);
+
+/**
+ * Get the committer of this entry
+ *
+ * @param entry a reflog entry
+ * @return the committer
+ */
+GIT_EXTERN(const git_signature *) git_reflog_entry_committer(const git_reflog_entry *entry);
+
+/**
+ * Get the log message
+ *
+ * @param entry a reflog entry
+ * @return the log msg
+ */
+GIT_EXTERN(const char *) git_reflog_entry_message(const git_reflog_entry *entry);
+
+/**
+ * Free the reflog
+ *
+ * @param reflog reflog to free
+ */
+GIT_EXTERN(void) git_reflog_free(git_reflog *reflog);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/refs.h b/include/git2/refs.h
new file mode 100644
index 0000000..06f8bb9
--- /dev/null
+++ b/include/git2/refs.h
@@ -0,0 +1,771 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_refs_h__
+#define INCLUDE_git_refs_h__
+
+#include "common.h"
+#include "types.h"
+#include "oid.h"
+#include "strarray.h"
+
+/**
+ * @file git2/refs.h
+ * @brief Git reference management routines
+ * @defgroup git_reference Git reference management routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Lookup a reference by name in a repository.
+ *
+ * The returned reference must be freed by the user.
+ *
+ * The name will be checked for validity.
+ * See `git_reference_symbolic_create()` for rules about valid names.
+ *
+ * @param out pointer to the looked-up reference
+ * @param repo the repository to look up the reference
+ * @param name the long name for the reference (e.g. HEAD, refs/heads/master, refs/tags/v0.1.0, ...)
+ * @return 0 on success, GIT_ENOTFOUND, GIT_EINVALIDSPEC or an error code.
+ */
+GIT_EXTERN(int) git_reference_lookup(git_reference **out, git_repository *repo, const char *name);
+
+/**
+ * Lookup a reference by name and resolve immediately to OID.
+ *
+ * This function provides a quick way to resolve a reference name straight
+ * through to the object id that it refers to. This avoids having to
+ * allocate or free any `git_reference` objects for simple situations.
+ *
+ * The name will be checked for validity.
+ * See `git_reference_symbolic_create()` for rules about valid names.
+ *
+ * @param out Pointer to oid to be filled in
+ * @param repo The repository in which to look up the reference
+ * @param name The long name for the reference (e.g. HEAD, refs/heads/master, refs/tags/v0.1.0, ...)
+ * @return 0 on success, GIT_ENOTFOUND, GIT_EINVALIDSPEC or an error code.
+ */
+GIT_EXTERN(int) git_reference_name_to_id(
+ git_oid *out, git_repository *repo, const char *name);
+
+/**
+ * Lookup a reference by DWIMing its short name
+ *
+ * Apply the git precedence rules to the given shorthand to determine
+ * which reference the user is referring to.
+ *
+ * @param out pointer in which to store the reference
+ * @param repo the repository in which to look
+ * @param shorthand the short name for the reference
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_reference_dwim(git_reference **out, git_repository *repo, const char *shorthand);
+
+/**
+ * Conditionally create a new symbolic reference.
+ *
+ * A symbolic reference is a reference name that refers to another
+ * reference name. If the other name moves, the symbolic name will move,
+ * too. As a simple example, the "HEAD" reference might refer to
+ * "refs/heads/master" while on the "master" branch of a repository.
+ *
+ * The symbolic reference will be created in the repository and written to
+ * the disk. The generated reference object must be freed by the user.
+ *
+ * Valid reference names must follow one of two patterns:
+ *
+ * 1. Top-level names must contain only capital letters and underscores,
+ * and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD").
+ * 2. Names prefixed with "refs/" can be almost anything. You must avoid
+ * the characters '~', '^', ':', '\\', '?', '[', and '*', and the
+ * sequences ".." and "@{" which have special meaning to revparse.
+ *
+ * This function will return an error if a reference already exists with the
+ * given name unless `force` is true, in which case it will be overwritten.
+ *
+ * The message for the reflog will be ignored if the reference does
+ * not belong in the standard set (HEAD, branches and remote-tracking
+ * branches) and it does not have a reflog.
+ *
+ * It will return GIT_EMODIFIED if the reference's value at the time
+ * of updating does not match the one passed through `current_value`
+ * (i.e. if the ref has changed since the user read it).
+ *
+ * If `current_value` is all zeros, this function will return GIT_EMODIFIED
+ * if the ref already exists.
+ *
+ * @param out Pointer to the newly created reference
+ * @param repo Repository where that reference will live
+ * @param name The name of the reference
+ * @param target The target of the reference
+ * @param force Overwrite existing references
+ * @param current_value The expected value of the reference when updating
+ * @param log_message The one line long message to be appended to the reflog
+ * @return 0 on success, GIT_EEXISTS, GIT_EINVALIDSPEC, GIT_EMODIFIED or an error code
+ */
+GIT_EXTERN(int) git_reference_symbolic_create_matching(git_reference **out, git_repository *repo, const char *name, const char *target, int force, const char *current_value, const char *log_message);
+
+/**
+ * Create a new symbolic reference.
+ *
+ * A symbolic reference is a reference name that refers to another
+ * reference name. If the other name moves, the symbolic name will move,
+ * too. As a simple example, the "HEAD" reference might refer to
+ * "refs/heads/master" while on the "master" branch of a repository.
+ *
+ * The symbolic reference will be created in the repository and written to
+ * the disk. The generated reference object must be freed by the user.
+ *
+ * Valid reference names must follow one of two patterns:
+ *
+ * 1. Top-level names must contain only capital letters and underscores,
+ * and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD").
+ * 2. Names prefixed with "refs/" can be almost anything. You must avoid
+ * the characters '~', '^', ':', '\\', '?', '[', and '*', and the
+ * sequences ".." and "@{" which have special meaning to revparse.
+ *
+ * This function will return an error if a reference already exists with the
+ * given name unless `force` is true, in which case it will be overwritten.
+ *
+ * The message for the reflog will be ignored if the reference does
+ * not belong in the standard set (HEAD, branches and remote-tracking
+ * branches) and it does not have a reflog.
+ *
+ * @param out Pointer to the newly created reference
+ * @param repo Repository where that reference will live
+ * @param name The name of the reference
+ * @param target The target of the reference
+ * @param force Overwrite existing references
+ * @param log_message The one line long message to be appended to the reflog
+ * @return 0 on success, GIT_EEXISTS, GIT_EINVALIDSPEC or an error code
+ */
+GIT_EXTERN(int) git_reference_symbolic_create(git_reference **out, git_repository *repo, const char *name, const char *target, int force, const char *log_message);
+
+/**
+ * Create a new direct reference.
+ *
+ * A direct reference (also called an object id reference) refers directly
+ * to a specific object id (a.k.a. OID or SHA) in the repository. The id
+ * permanently refers to the object (although the reference itself can be
+ * moved). For example, in libgit2 the direct ref "refs/tags/v0.17.0"
+ * refers to OID 5b9fac39d8a76b9139667c26a63e6b3f204b3977.
+ *
+ * The direct reference will be created in the repository and written to
+ * the disk. The generated reference object must be freed by the user.
+ *
+ * Valid reference names must follow one of two patterns:
+ *
+ * 1. Top-level names must contain only capital letters and underscores,
+ * and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD").
+ * 2. Names prefixed with "refs/" can be almost anything. You must avoid
+ * the characters '~', '^', ':', '\\', '?', '[', and '*', and the
+ * sequences ".." and "@{" which have special meaning to revparse.
+ *
+ * This function will return an error if a reference already exists with the
+ * given name unless `force` is true, in which case it will be overwritten.
+ *
+ * The message for the reflog will be ignored if the reference does
+ * not belong in the standard set (HEAD, branches and remote-tracking
+ * branches) and it does not have a reflog.
+ *
+ * @param out Pointer to the newly created reference
+ * @param repo Repository where that reference will live
+ * @param name The name of the reference
+ * @param id The object id pointed to by the reference.
+ * @param force Overwrite existing references
+ * @param log_message The one line long message to be appended to the reflog
+ * @return 0 on success, GIT_EEXISTS, GIT_EINVALIDSPEC or an error code
+ */
+GIT_EXTERN(int) git_reference_create(git_reference **out, git_repository *repo, const char *name, const git_oid *id, int force, const char *log_message);
+
+/**
+ * Conditionally create new direct reference
+ *
+ * A direct reference (also called an object id reference) refers directly
+ * to a specific object id (a.k.a. OID or SHA) in the repository. The id
+ * permanently refers to the object (although the reference itself can be
+ * moved). For example, in libgit2 the direct ref "refs/tags/v0.17.0"
+ * refers to OID 5b9fac39d8a76b9139667c26a63e6b3f204b3977.
+ *
+ * The direct reference will be created in the repository and written to
+ * the disk. The generated reference object must be freed by the user.
+ *
+ * Valid reference names must follow one of two patterns:
+ *
+ * 1. Top-level names must contain only capital letters and underscores,
+ * and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD").
+ * 2. Names prefixed with "refs/" can be almost anything. You must avoid
+ * the characters '~', '^', ':', '\\', '?', '[', and '*', and the
+ * sequences ".." and "@{" which have special meaning to revparse.
+ *
+ * This function will return an error if a reference already exists with the
+ * given name unless `force` is true, in which case it will be overwritten.
+ *
+ * The message for the reflog will be ignored if the reference does
+ * not belong in the standard set (HEAD, branches and remote-tracking
+ * branches) and it does not have a reflog.
+ *
+ * It will return GIT_EMODIFIED if the reference's value at the time
+ * of updating does not match the one passed through `current_id`
+ * (i.e. if the ref has changed since the user read it).
+ *
+ * @param out Pointer to the newly created reference
+ * @param repo Repository where that reference will live
+ * @param name The name of the reference
+ * @param id The object id pointed to by the reference.
+ * @param force Overwrite existing references
+ * @param current_id The expected value of the reference at the time of update
+ * @param log_message The one line long message to be appended to the reflog
+ * @return 0 on success, GIT_EMODIFIED if the value of the reference
+ * has changed, GIT_EEXISTS, GIT_EINVALIDSPEC or an error code
+ */
+GIT_EXTERN(int) git_reference_create_matching(git_reference **out, git_repository *repo, const char *name, const git_oid *id, int force, const git_oid *current_id, const char *log_message);
+
+/**
+ * Get the OID pointed to by a direct reference.
+ *
+ * Only available if the reference is direct (i.e. an object id reference,
+ * not a symbolic one).
+ *
+ * To find the OID of a symbolic ref, call `git_reference_resolve()` and
+ * then this function (or maybe use `git_reference_name_to_id()` to
+ * directly resolve a reference name all the way through to an OID).
+ *
+ * @param ref The reference
+ * @return a pointer to the oid if available, NULL otherwise
+ */
+GIT_EXTERN(const git_oid *) git_reference_target(const git_reference *ref);
+
+/**
+ * Return the peeled OID target of this reference.
+ *
+ * This peeled OID only applies to direct references that point to
+ * a hard Tag object: it is the result of peeling such Tag.
+ *
+ * @param ref The reference
+ * @return a pointer to the oid if available, NULL otherwise
+ */
+GIT_EXTERN(const git_oid *) git_reference_target_peel(const git_reference *ref);
+
+/**
+ * Get full name to the reference pointed to by a symbolic reference.
+ *
+ * Only available if the reference is symbolic.
+ *
+ * @param ref The reference
+ * @return a pointer to the name if available, NULL otherwise
+ */
+GIT_EXTERN(const char *) git_reference_symbolic_target(const git_reference *ref);
+
+/**
+ * Get the type of a reference.
+ *
+ * Either direct (GIT_REFERENCE_DIRECT) or symbolic (GIT_REFERENCE_SYMBOLIC)
+ *
+ * @param ref The reference
+ * @return the type
+ */
+GIT_EXTERN(git_reference_t) git_reference_type(const git_reference *ref);
+
+/**
+ * Get the full name of a reference.
+ *
+ * See `git_reference_symbolic_create()` for rules about valid names.
+ *
+ * @param ref The reference
+ * @return the full name for the ref
+ */
+GIT_EXTERN(const char *) git_reference_name(const git_reference *ref);
+
+/**
+ * Resolve a symbolic reference to a direct reference.
+ *
+ * This method iteratively peels a symbolic reference until it resolves to
+ * a direct reference to an OID.
+ *
+ * The peeled reference is returned in the `resolved_ref` argument, and
+ * must be freed manually once it's no longer needed.
+ *
+ * If a direct reference is passed as an argument, a copy of that
+ * reference is returned. This copy must be manually freed too.
+ *
+ * @param out Pointer to the peeled reference
+ * @param ref The reference
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_reference_resolve(git_reference **out, const git_reference *ref);
+
+/**
+ * Get the repository where a reference resides.
+ *
+ * @param ref The reference
+ * @return a pointer to the repo
+ */
+GIT_EXTERN(git_repository *) git_reference_owner(const git_reference *ref);
+
+/**
+ * Create a new reference with the same name as the given reference but a
+ * different symbolic target. The reference must be a symbolic reference,
+ * otherwise this will fail.
+ *
+ * The new reference will be written to disk, overwriting the given reference.
+ *
+ * The target name will be checked for validity.
+ * See `git_reference_symbolic_create()` for rules about valid names.
+ *
+ * The message for the reflog will be ignored if the reference does
+ * not belong in the standard set (HEAD, branches and remote-tracking
+ * branches) and it does not have a reflog.
+ *
+ * @param out Pointer to the newly created reference
+ * @param ref The reference
+ * @param target The new target for the reference
+ * @param log_message The one line long message to be appended to the reflog
+ * @return 0 on success, GIT_EINVALIDSPEC or an error code
+ */
+GIT_EXTERN(int) git_reference_symbolic_set_target(
+ git_reference **out,
+ git_reference *ref,
+ const char *target,
+ const char *log_message);
+
+/**
+ * Conditionally create a new reference with the same name as the given reference but a
+ * different OID target. The reference must be a direct reference, otherwise
+ * this will fail.
+ *
+ * The new reference will be written to disk, overwriting the given reference.
+ *
+ * @param out Pointer to the newly created reference
+ * @param ref The reference
+ * @param id The new target OID for the reference
+ * @param log_message The one line long message to be appended to the reflog
+ * @return 0 on success, GIT_EMODIFIED if the value of the reference
+ * has changed since it was read, or an error code
+ */
+GIT_EXTERN(int) git_reference_set_target(
+ git_reference **out,
+ git_reference *ref,
+ const git_oid *id,
+ const char *log_message);
+
+/**
+ * Rename an existing reference.
+ *
+ * This method works for both direct and symbolic references.
+ *
+ * The new name will be checked for validity.
+ * See `git_reference_symbolic_create()` for rules about valid names.
+ *
+ * If the `force` flag is not enabled, and there's already
+ * a reference with the given name, the renaming will fail.
+ *
+ * IMPORTANT:
+ * The user needs to write a proper reflog entry if the
+ * reflog is enabled for the repository. We only rename
+ * the reflog if it exists.
+ *
+ * @param ref The reference to rename
+ * @param new_name The new name for the reference
+ * @param force Overwrite an existing reference
+ * @param log_message The one line long message to be appended to the reflog
+ * @return 0 on success, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code
+ *
+ */
+GIT_EXTERN(int) git_reference_rename(
+ git_reference **new_ref,
+ git_reference *ref,
+ const char *new_name,
+ int force,
+ const char *log_message);
+
+/**
+ * Delete an existing reference.
+ *
+ * This method works for both direct and symbolic references. The reference
+ * will be immediately removed on disk but the memory will not be freed.
+ * Callers must call `git_reference_free`.
+ *
+ * This function will return an error if the reference has changed
+ * from the time it was looked up.
+ *
+ * @param ref The reference to remove
+ * @return 0, GIT_EMODIFIED or an error code
+ */
+GIT_EXTERN(int) git_reference_delete(git_reference *ref);
+
+/**
+ * Delete an existing reference by name
+ *
+ * This method removes the named reference from the repository without
+ * looking at its old value.
+ *
+ * @param name The reference to remove
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_reference_remove(git_repository *repo, const char *name);
+
+/**
+ * Fill a list with all the references that can be found in a repository.
+ *
+ * The string array will be filled with the names of all references; these
+ * values are owned by the user and should be free'd manually when no
+ * longer needed, using `git_strarray_free()`.
+ *
+ * @param array Pointer to a git_strarray structure where
+ * the reference names will be stored
+ * @param repo Repository where to find the refs
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_reference_list(git_strarray *array, git_repository *repo);
+
+/**
+ * Callback used to iterate over references
+ *
+ * @see git_reference_foreach
+ *
+ * @param reference The reference object
+ * @param payload Payload passed to git_reference_foreach
+ * @return non-zero to terminate the iteration
+ */
+typedef int GIT_CALLBACK(git_reference_foreach_cb)(git_reference *reference, void *payload);
+
+/**
+ * Callback used to iterate over reference names
+ *
+ * @see git_reference_foreach_name
+ *
+ * @param name The reference name
+ * @param payload Payload passed to git_reference_foreach_name
+ * @return non-zero to terminate the iteration
+ */
+typedef int GIT_CALLBACK(git_reference_foreach_name_cb)(const char *name, void *payload);
+
+/**
+ * Perform a callback on each reference in the repository.
+ *
+ * The `callback` function will be called for each reference in the
+ * repository, receiving the reference object and the `payload` value
+ * passed to this method. Returning a non-zero value from the callback
+ * will terminate the iteration.
+ *
+ * Note that the callback function is responsible to call `git_reference_free`
+ * on each reference passed to it.
+ *
+ * @param repo Repository where to find the refs
+ * @param callback Function which will be called for every listed ref
+ * @param payload Additional data to pass to the callback
+ * @return 0 on success, non-zero callback return value, or error code
+ */
+GIT_EXTERN(int) git_reference_foreach(
+ git_repository *repo,
+ git_reference_foreach_cb callback,
+ void *payload);
+
+/**
+ * Perform a callback on the fully-qualified name of each reference.
+ *
+ * The `callback` function will be called for each reference in the
+ * repository, receiving the name of the reference and the `payload` value
+ * passed to this method. Returning a non-zero value from the callback
+ * will terminate the iteration.
+ *
+ * @param repo Repository where to find the refs
+ * @param callback Function which will be called for every listed ref name
+ * @param payload Additional data to pass to the callback
+ * @return 0 on success, non-zero callback return value, or error code
+ */
+GIT_EXTERN(int) git_reference_foreach_name(
+ git_repository *repo,
+ git_reference_foreach_name_cb callback,
+ void *payload);
+
+/**
+ * Create a copy of an existing reference.
+ *
+ * Call `git_reference_free` to free the data.
+ *
+ * @param dest pointer where to store the copy
+ * @param source object to copy
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_reference_dup(git_reference **dest, git_reference *source);
+
+/**
+ * Free the given reference.
+ *
+ * @param ref git_reference
+ */
+GIT_EXTERN(void) git_reference_free(git_reference *ref);
+
+/**
+ * Compare two references.
+ *
+ * @param ref1 The first git_reference
+ * @param ref2 The second git_reference
+ * @return 0 if the same, else a stable but meaningless ordering.
+ */
+GIT_EXTERN(int) git_reference_cmp(
+ const git_reference *ref1,
+ const git_reference *ref2);
+
+/**
+ * Create an iterator for the repo's references
+ *
+ * @param out pointer in which to store the iterator
+ * @param repo the repository
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_reference_iterator_new(
+ git_reference_iterator **out,
+ git_repository *repo);
+
+/**
+ * Create an iterator for the repo's references that match the
+ * specified glob
+ *
+ * @param out pointer in which to store the iterator
+ * @param repo the repository
+ * @param glob the glob to match against the reference names
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_reference_iterator_glob_new(
+ git_reference_iterator **out,
+ git_repository *repo,
+ const char *glob);
+
+/**
+ * Get the next reference
+ *
+ * @param out pointer in which to store the reference
+ * @param iter the iterator
+ * @return 0, GIT_ITEROVER if there are no more; or an error code
+ */
+GIT_EXTERN(int) git_reference_next(git_reference **out, git_reference_iterator *iter);
+
+/**
+ * Get the next reference's name
+ *
+ * This function is provided for convenience in case only the names
+ * are interesting as it avoids the allocation of the `git_reference`
+ * object which `git_reference_next()` needs.
+ *
+ * @param out pointer in which to store the string
+ * @param iter the iterator
+ * @return 0, GIT_ITEROVER if there are no more; or an error code
+ */
+GIT_EXTERN(int) git_reference_next_name(const char **out, git_reference_iterator *iter);
+
+/**
+ * Free the iterator and its associated resources
+ *
+ * @param iter the iterator to free
+ */
+GIT_EXTERN(void) git_reference_iterator_free(git_reference_iterator *iter);
+
+/**
+ * Perform a callback on each reference in the repository whose name
+ * matches the given pattern.
+ *
+ * This function acts like `git_reference_foreach()` with an additional
+ * pattern match being applied to the reference name before issuing the
+ * callback function. See that function for more information.
+ *
+ * The pattern is matched using fnmatch or "glob" style where a '*' matches
+ * any sequence of letters, a '?' matches any letter, and square brackets
+ * can be used to define character ranges (such as "[0-9]" for digits).
+ *
+ * @param repo Repository where to find the refs
+ * @param glob Pattern to match (fnmatch-style) against reference name.
+ * @param callback Function which will be called for every listed ref
+ * @param payload Additional data to pass to the callback
+ * @return 0 on success, GIT_EUSER on non-zero callback, or error code
+ */
+GIT_EXTERN(int) git_reference_foreach_glob(
+ git_repository *repo,
+ const char *glob,
+ git_reference_foreach_name_cb callback,
+ void *payload);
+
+/**
+ * Check if a reflog exists for the specified reference.
+ *
+ * @param repo the repository
+ * @param refname the reference's name
+ * @return 0 when no reflog can be found, 1 when it exists;
+ * otherwise an error code.
+ */
+GIT_EXTERN(int) git_reference_has_log(git_repository *repo, const char *refname);
+
+/**
+ * Ensure there is a reflog for a particular reference.
+ *
+ * Make sure that successive updates to the reference will append to
+ * its log.
+ *
+ * @param repo the repository
+ * @param refname the reference's name
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_reference_ensure_log(git_repository *repo, const char *refname);
+
+/**
+ * Check if a reference is a local branch.
+ *
+ * @param ref A git reference
+ *
+ * @return 1 when the reference lives in the refs/heads
+ * namespace; 0 otherwise.
+ */
+GIT_EXTERN(int) git_reference_is_branch(const git_reference *ref);
+
+/**
+ * Check if a reference is a remote tracking branch
+ *
+ * @param ref A git reference
+ *
+ * @return 1 when the reference lives in the refs/remotes
+ * namespace; 0 otherwise.
+ */
+GIT_EXTERN(int) git_reference_is_remote(const git_reference *ref);
+
+/**
+ * Check if a reference is a tag
+ *
+ * @param ref A git reference
+ *
+ * @return 1 when the reference lives in the refs/tags
+ * namespace; 0 otherwise.
+ */
+GIT_EXTERN(int) git_reference_is_tag(const git_reference *ref);
+
+/**
+ * Check if a reference is a note
+ *
+ * @param ref A git reference
+ *
+ * @return 1 when the reference lives in the refs/notes
+ * namespace; 0 otherwise.
+ */
+GIT_EXTERN(int) git_reference_is_note(const git_reference *ref);
+
+/**
+ * Normalization options for reference lookup
+ */
+typedef enum {
+ /**
+ * No particular normalization.
+ */
+ GIT_REFERENCE_FORMAT_NORMAL = 0u,
+
+ /**
+ * Control whether one-level refnames are accepted
+ * (i.e., refnames that do not contain multiple /-separated
+ * components). Those are expected to be written only using
+ * uppercase letters and underscore (FETCH_HEAD, ...)
+ */
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL = (1u << 0),
+
+ /**
+ * Interpret the provided name as a reference pattern for a
+ * refspec (as used with remote repositories). If this option
+ * is enabled, the name is allowed to contain a single * (<star>)
+ * in place of a one full pathname component
+ * (e.g., foo/<star>/bar but not foo/bar<star>).
+ */
+ GIT_REFERENCE_FORMAT_REFSPEC_PATTERN = (1u << 1),
+
+ /**
+ * Interpret the name as part of a refspec in shorthand form
+ * so the `ONELEVEL` naming rules aren't enforced and 'master'
+ * becomes a valid name.
+ */
+ GIT_REFERENCE_FORMAT_REFSPEC_SHORTHAND = (1u << 2)
+} git_reference_format_t;
+
+/**
+ * Normalize reference name and check validity.
+ *
+ * This will normalize the reference name by removing any leading slash
+ * '/' characters and collapsing runs of adjacent slashes between name
+ * components into a single slash.
+ *
+ * Once normalized, if the reference name is valid, it will be returned in
+ * the user allocated buffer.
+ *
+ * See `git_reference_symbolic_create()` for rules about valid names.
+ *
+ * @param buffer_out User allocated buffer to store normalized name
+ * @param buffer_size Size of buffer_out
+ * @param name Reference name to be checked.
+ * @param flags Flags to constrain name validation rules - see the
+ * GIT_REFERENCE_FORMAT constants above.
+ * @return 0 on success, GIT_EBUFS if buffer is too small, GIT_EINVALIDSPEC
+ * or an error code.
+ */
+GIT_EXTERN(int) git_reference_normalize_name(
+ char *buffer_out,
+ size_t buffer_size,
+ const char *name,
+ unsigned int flags);
+
+/**
+ * Recursively peel reference until object of the specified type is found.
+ *
+ * The retrieved `peeled` object is owned by the repository
+ * and should be closed with the `git_object_free` method.
+ *
+ * If you pass `GIT_OBJECT_ANY` as the target type, then the object
+ * will be peeled until a non-tag object is met.
+ *
+ * @param out Pointer to the peeled git_object
+ * @param ref The reference to be processed
+ * @param type The type of the requested object (GIT_OBJECT_COMMIT,
+ * GIT_OBJECT_TAG, GIT_OBJECT_TREE, GIT_OBJECT_BLOB or GIT_OBJECT_ANY).
+ * @return 0 on success, GIT_EAMBIGUOUS, GIT_ENOTFOUND or an error code
+ */
+GIT_EXTERN(int) git_reference_peel(
+ git_object **out,
+ const git_reference *ref,
+ git_object_t type);
+
+/**
+ * Ensure the reference name is well-formed.
+ *
+ * Valid reference names must follow one of two patterns:
+ *
+ * 1. Top-level names must contain only capital letters and underscores,
+ * and must begin and end with a letter. (e.g. "HEAD", "ORIG_HEAD").
+ * 2. Names prefixed with "refs/" can be almost anything. You must avoid
+ * the characters '~', '^', ':', '\\', '?', '[', and '*', and the
+ * sequences ".." and "@{" which have special meaning to revparse.
+ *
+ * @param valid output pointer to set with validity of given reference name
+ * @param refname name to be checked.
+ * @return 0 on success or an error code
+ */
+GIT_EXTERN(int) git_reference_name_is_valid(int *valid, const char *refname);
+
+/**
+ * Get the reference's short name
+ *
+ * This will transform the reference name into a name "human-readable"
+ * version. If no shortname is appropriate, it will return the full
+ * name.
+ *
+ * The memory is owned by the reference and must not be freed.
+ *
+ * @param ref a reference
+ * @return the human-readable version of the name
+ */
+GIT_EXTERN(const char *) git_reference_shorthand(const git_reference *ref);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/refspec.h b/include/git2/refspec.h
new file mode 100644
index 0000000..eaf7747
--- /dev/null
+++ b/include/git2/refspec.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_refspec_h__
+#define INCLUDE_git_refspec_h__
+
+#include "common.h"
+#include "types.h"
+#include "net.h"
+#include "buffer.h"
+
+/**
+ * @file git2/refspec.h
+ * @brief Git refspec attributes
+ * @defgroup git_refspec Git refspec attributes
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Parse a given refspec string
+ *
+ * @param refspec a pointer to hold the refspec handle
+ * @param input the refspec string
+ * @param is_fetch is this a refspec for a fetch
+ * @return 0 if the refspec string could be parsed, -1 otherwise
+ */
+GIT_EXTERN(int) git_refspec_parse(git_refspec **refspec, const char *input, int is_fetch);
+
+/**
+ * Free a refspec object which has been created by git_refspec_parse
+ *
+ * @param refspec the refspec object
+ */
+GIT_EXTERN(void) git_refspec_free(git_refspec *refspec);
+
+/**
+ * Get the source specifier
+ *
+ * @param refspec the refspec
+ * @return the refspec's source specifier
+ */
+GIT_EXTERN(const char *) git_refspec_src(const git_refspec *refspec);
+
+/**
+ * Get the destination specifier
+ *
+ * @param refspec the refspec
+ * @return the refspec's destination specifier
+ */
+GIT_EXTERN(const char *) git_refspec_dst(const git_refspec *refspec);
+
+/**
+ * Get the refspec's string
+ *
+ * @param refspec the refspec
+ * @returns the refspec's original string
+ */
+GIT_EXTERN(const char *) git_refspec_string(const git_refspec *refspec);
+
+/**
+ * Get the force update setting
+ *
+ * @param refspec the refspec
+ * @return 1 if force update has been set, 0 otherwise
+ */
+GIT_EXTERN(int) git_refspec_force(const git_refspec *refspec);
+
+/**
+ * Get the refspec's direction.
+ *
+ * @param spec refspec
+ * @return GIT_DIRECTION_FETCH or GIT_DIRECTION_PUSH
+ */
+GIT_EXTERN(git_direction) git_refspec_direction(const git_refspec *spec);
+
+/**
+ * Check if a refspec's source descriptor matches a reference
+ *
+ * @param refspec the refspec
+ * @param refname the name of the reference to check
+ * @return 1 if the refspec matches, 0 otherwise
+ */
+GIT_EXTERN(int) git_refspec_src_matches(const git_refspec *refspec, const char *refname);
+
+/**
+ * Check if a refspec's destination descriptor matches a reference
+ *
+ * @param refspec the refspec
+ * @param refname the name of the reference to check
+ * @return 1 if the refspec matches, 0 otherwise
+ */
+GIT_EXTERN(int) git_refspec_dst_matches(const git_refspec *refspec, const char *refname);
+
+/**
+ * Transform a reference to its target following the refspec's rules
+ *
+ * @param out where to store the target name
+ * @param spec the refspec
+ * @param name the name of the reference to transform
+ * @return 0, GIT_EBUFS or another error
+ */
+GIT_EXTERN(int) git_refspec_transform(git_buf *out, const git_refspec *spec, const char *name);
+
+/**
+ * Transform a target reference to its source reference following the refspec's rules
+ *
+ * @param out where to store the source reference name
+ * @param spec the refspec
+ * @param name the name of the reference to transform
+ * @return 0, GIT_EBUFS or another error
+ */
+GIT_EXTERN(int) git_refspec_rtransform(git_buf *out, const git_refspec *spec, const char *name);
+
+GIT_END_DECL
+
+#endif
diff --git a/include/git2/remote.h b/include/git2/remote.h
new file mode 100644
index 0000000..e9065b2
--- /dev/null
+++ b/include/git2/remote.h
@@ -0,0 +1,1173 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_remote_h__
+#define INCLUDE_git_remote_h__
+
+#include "common.h"
+#include "repository.h"
+#include "refspec.h"
+#include "net.h"
+#include "indexer.h"
+#include "strarray.h"
+#include "transport.h"
+#include "pack.h"
+#include "proxy.h"
+
+/**
+ * @file git2/remote.h
+ * @brief Git remote management functions
+ * @defgroup git_remote remote management functions
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Add a remote with the default fetch refspec to the repository's configuration.
+ *
+ * @param out the resulting remote
+ * @param repo the repository in which to create the remote
+ * @param name the remote's name
+ * @param url the remote's url
+ * @return 0, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code
+ */
+GIT_EXTERN(int) git_remote_create(
+ git_remote **out,
+ git_repository *repo,
+ const char *name,
+ const char *url);
+
+/**
+ * Remote redirection settings; whether redirects to another host
+ * are permitted. By default, git will follow a redirect on the
+ * initial request (`/info/refs`), but not subsequent requests.
+ */
+typedef enum {
+ /**
+ * Do not follow any off-site redirects at any stage of
+ * the fetch or push.
+ */
+ GIT_REMOTE_REDIRECT_NONE = (1 << 0),
+
+ /**
+ * Allow off-site redirects only upon the initial request.
+ * This is the default.
+ */
+ GIT_REMOTE_REDIRECT_INITIAL = (1 << 1),
+
+ /**
+ * Allow redirects at any stage in the fetch or push.
+ */
+ GIT_REMOTE_REDIRECT_ALL = (1 << 2)
+} git_remote_redirect_t;
+
+/**
+ * Remote creation options flags
+ */
+typedef enum {
+ /** Ignore the repository apply.insteadOf configuration */
+ GIT_REMOTE_CREATE_SKIP_INSTEADOF = (1 << 0),
+
+ /** Don't build a fetchspec from the name if none is set */
+ GIT_REMOTE_CREATE_SKIP_DEFAULT_FETCHSPEC = (1 << 1)
+} git_remote_create_flags;
+
+/**
+ * Remote creation options structure
+ *
+ * Initialize with `GIT_REMOTE_CREATE_OPTIONS_INIT`. Alternatively, you can
+ * use `git_remote_create_options_init`.
+ *
+ */
+typedef struct git_remote_create_options {
+ unsigned int version;
+
+ /**
+ * The repository that should own the remote.
+ * Setting this to NULL results in a detached remote.
+ */
+ git_repository *repository;
+
+ /**
+ * The remote's name.
+ * Setting this to NULL results in an in-memory/anonymous remote.
+ */
+ const char *name;
+
+ /** The fetchspec the remote should use. */
+ const char *fetchspec;
+
+ /** Additional flags for the remote. See git_remote_create_flags. */
+ unsigned int flags;
+} git_remote_create_options;
+
+#define GIT_REMOTE_CREATE_OPTIONS_VERSION 1
+#define GIT_REMOTE_CREATE_OPTIONS_INIT {GIT_REMOTE_CREATE_OPTIONS_VERSION}
+
+/**
+ * Initialize git_remote_create_options structure
+ *
+ * Initializes a `git_remote_create_options` with default values. Equivalent to
+ * creating an instance with `GIT_REMOTE_CREATE_OPTIONS_INIT`.
+ *
+ * @param opts The `git_remote_create_options` struct to initialize.
+ * @param version The struct version; pass `GIT_REMOTE_CREATE_OPTIONS_VERSION`.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_remote_create_options_init(
+ git_remote_create_options *opts,
+ unsigned int version);
+
+/**
+ * Create a remote, with options.
+ *
+ * This function allows more fine-grained control over the remote creation.
+ *
+ * Passing NULL as the opts argument will result in a detached remote.
+ *
+ * @param out the resulting remote
+ * @param url the remote's url
+ * @param opts the remote creation options
+ * @return 0, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code
+ */
+GIT_EXTERN(int) git_remote_create_with_opts(
+ git_remote **out,
+ const char *url,
+ const git_remote_create_options *opts);
+
+/**
+ * Add a remote with the provided fetch refspec (or default if NULL) to the repository's
+ * configuration.
+ *
+ * @param out the resulting remote
+ * @param repo the repository in which to create the remote
+ * @param name the remote's name
+ * @param url the remote's url
+ * @param fetch the remote fetch value
+ * @return 0, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code
+ */
+GIT_EXTERN(int) git_remote_create_with_fetchspec(
+ git_remote **out,
+ git_repository *repo,
+ const char *name,
+ const char *url,
+ const char *fetch);
+
+/**
+ * Create an anonymous remote
+ *
+ * Create a remote with the given url in-memory. You can use this when
+ * you have a URL instead of a remote's name.
+ *
+ * @param out pointer to the new remote objects
+ * @param repo the associated repository
+ * @param url the remote repository's URL
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_remote_create_anonymous(
+ git_remote **out,
+ git_repository *repo,
+ const char *url);
+
+/**
+ * Create a remote without a connected local repo
+ *
+ * Create a remote with the given url in-memory. You can use this when
+ * you have a URL instead of a remote's name.
+ *
+ * Contrasted with git_remote_create_anonymous, a detached remote
+ * will not consider any repo configuration values (such as insteadof url
+ * substitutions).
+ *
+ * @param out pointer to the new remote objects
+ * @param url the remote repository's URL
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_remote_create_detached(
+ git_remote **out,
+ const char *url);
+
+/**
+ * Get the information for a particular remote
+ *
+ * The name will be checked for validity.
+ * See `git_tag_create()` for rules about valid names.
+ *
+ * @param out pointer to the new remote object
+ * @param repo the associated repository
+ * @param name the remote's name
+ * @return 0, GIT_ENOTFOUND, GIT_EINVALIDSPEC or an error code
+ */
+GIT_EXTERN(int) git_remote_lookup(git_remote **out, git_repository *repo, const char *name);
+
+/**
+ * Create a copy of an existing remote. All internal strings are also
+ * duplicated. Callbacks are not duplicated.
+ *
+ * Call `git_remote_free` to free the data.
+ *
+ * @param dest pointer where to store the copy
+ * @param source object to copy
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_remote_dup(git_remote **dest, git_remote *source);
+
+/**
+ * Get the remote's repository
+ *
+ * @param remote the remote
+ * @return a pointer to the repository
+ */
+GIT_EXTERN(git_repository *) git_remote_owner(const git_remote *remote);
+
+/**
+ * Get the remote's name
+ *
+ * @param remote the remote
+ * @return a pointer to the name or NULL for in-memory remotes
+ */
+GIT_EXTERN(const char *) git_remote_name(const git_remote *remote);
+
+/**
+ * Get the remote's url
+ *
+ * If url.*.insteadOf has been configured for this URL, it will
+ * return the modified URL. If `git_remote_set_instance_pushurl`
+ * has been called for this remote, then that URL will be returned.
+ *
+ * @param remote the remote
+ * @return a pointer to the url
+ */
+GIT_EXTERN(const char *) git_remote_url(const git_remote *remote);
+
+/**
+ * Get the remote's url for pushing.
+ *
+ * If url.*.pushInsteadOf has been configured for this URL, it
+ * will return the modified URL. If `git_remote_set_instance_pushurl`
+ * has been called for this remote, then that URL will be returned.
+ *
+ * @param remote the remote
+ * @return a pointer to the url or NULL if no special url for pushing is set
+ */
+GIT_EXTERN(const char *) git_remote_pushurl(const git_remote *remote);
+
+/**
+ * Set the remote's url in the configuration
+ *
+ * Remote objects already in memory will not be affected. This assumes
+ * the common case of a single-url remote and will otherwise return an error.
+ *
+ * @param repo the repository in which to perform the change
+ * @param remote the remote's name
+ * @param url the url to set
+ * @return 0 or an error value
+ */
+GIT_EXTERN(int) git_remote_set_url(git_repository *repo, const char *remote, const char *url);
+
+/**
+ * Set the remote's url for pushing in the configuration.
+ *
+ * Remote objects already in memory will not be affected. This assumes
+ * the common case of a single-url remote and will otherwise return an error.
+ *
+ *
+ * @param repo the repository in which to perform the change
+ * @param remote the remote's name
+ * @param url the url to set
+ * @return 0, or an error code
+ */
+GIT_EXTERN(int) git_remote_set_pushurl(git_repository *repo, const char *remote, const char *url);
+
+/**
+ * Set the url for this particular url instance. The URL in the
+ * configuration will be ignored, and will not be changed.
+ *
+ * @param remote the remote's name
+ * @param url the url to set
+ * @return 0 or an error value
+ */
+GIT_EXTERN(int) git_remote_set_instance_url(git_remote *remote, const char *url);
+
+/**
+ * Set the push url for this particular url instance. The URL in the
+ * configuration will be ignored, and will not be changed.
+ *
+ * @param remote the remote's name
+ * @param url the url to set
+ * @return 0 or an error value
+ */
+GIT_EXTERN(int) git_remote_set_instance_pushurl(git_remote *remote, const char *url);
+
+/**
+ * Add a fetch refspec to the remote's configuration
+ *
+ * Add the given refspec to the fetch list in the configuration. No
+ * loaded remote instances will be affected.
+ *
+ * @param repo the repository in which to change the configuration
+ * @param remote the name of the remote to change
+ * @param refspec the new fetch refspec
+ * @return 0, GIT_EINVALIDSPEC if refspec is invalid or an error value
+ */
+GIT_EXTERN(int) git_remote_add_fetch(git_repository *repo, const char *remote, const char *refspec);
+
+/**
+ * Get the remote's list of fetch refspecs
+ *
+ * The memory is owned by the user and should be freed with
+ * `git_strarray_free`.
+ *
+ * @param array pointer to the array in which to store the strings
+ * @param remote the remote to query
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_remote_get_fetch_refspecs(git_strarray *array, const git_remote *remote);
+
+/**
+ * Add a push refspec to the remote's configuration
+ *
+ * Add the given refspec to the push list in the configuration. No
+ * loaded remote instances will be affected.
+ *
+ * @param repo the repository in which to change the configuration
+ * @param remote the name of the remote to change
+ * @param refspec the new push refspec
+ * @return 0, GIT_EINVALIDSPEC if refspec is invalid or an error value
+ */
+GIT_EXTERN(int) git_remote_add_push(git_repository *repo, const char *remote, const char *refspec);
+
+/**
+ * Get the remote's list of push refspecs
+ *
+ * The memory is owned by the user and should be freed with
+ * `git_strarray_free`.
+ *
+ * @param array pointer to the array in which to store the strings
+ * @param remote the remote to query
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_remote_get_push_refspecs(git_strarray *array, const git_remote *remote);
+
+/**
+ * Get the number of refspecs for a remote
+ *
+ * @param remote the remote
+ * @return the amount of refspecs configured in this remote
+ */
+GIT_EXTERN(size_t) git_remote_refspec_count(const git_remote *remote);
+
+/**
+ * Get a refspec from the remote
+ *
+ * @param remote the remote to query
+ * @param n the refspec to get
+ * @return the nth refspec
+ */
+GIT_EXTERN(const git_refspec *)git_remote_get_refspec(const git_remote *remote, size_t n);
+
+/**
+ * Get the remote repository's reference advertisement list
+ *
+ * Get the list of references with which the server responds to a new
+ * connection.
+ *
+ * The remote (or more exactly its transport) must have connected to
+ * the remote repository. This list is available as soon as the
+ * connection to the remote is initiated and it remains available
+ * after disconnecting.
+ *
+ * The memory belongs to the remote. The pointer will be valid as long
+ * as a new connection is not initiated, but it is recommended that
+ * you make a copy in order to make use of the data.
+ *
+ * @param out pointer to the array
+ * @param size the number of remote heads
+ * @param remote the remote
+ * @return 0 on success, or an error code
+ */
+GIT_EXTERN(int) git_remote_ls(const git_remote_head ***out, size_t *size, git_remote *remote);
+
+/**
+ * Check whether the remote is connected
+ *
+ * Check whether the remote's underlying transport is connected to the
+ * remote host.
+ *
+ * @param remote the remote
+ * @return 1 if it's connected, 0 otherwise.
+ */
+GIT_EXTERN(int) git_remote_connected(const git_remote *remote);
+
+/**
+ * Cancel the operation
+ *
+ * At certain points in its operation, the network code checks whether
+ * the operation has been cancelled and if so stops the operation.
+ *
+ * @param remote the remote
+ * @return 0 on success, or an error code
+ */
+GIT_EXTERN(int) git_remote_stop(git_remote *remote);
+
+/**
+ * Disconnect from the remote
+ *
+ * Close the connection to the remote.
+ *
+ * @param remote the remote to disconnect from
+ * @return 0 on success, or an error code
+ */
+GIT_EXTERN(int) git_remote_disconnect(git_remote *remote);
+
+/**
+ * Free the memory associated with a remote
+ *
+ * This also disconnects from the remote, if the connection
+ * has not been closed yet (using git_remote_disconnect).
+ *
+ * @param remote the remote to free
+ */
+GIT_EXTERN(void) git_remote_free(git_remote *remote);
+
+/**
+ * Get a list of the configured remotes for a repo
+ *
+ * The string array must be freed by the user.
+ *
+ * @param out a string array which receives the names of the remotes
+ * @param repo the repository to query
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_remote_list(git_strarray *out, git_repository *repo);
+
+/**
+ * Argument to the completion callback which tells it which operation
+ * finished.
+ */
+typedef enum git_remote_completion_t {
+ GIT_REMOTE_COMPLETION_DOWNLOAD,
+ GIT_REMOTE_COMPLETION_INDEXING,
+ GIT_REMOTE_COMPLETION_ERROR
+} git_remote_completion_t;
+
+/** Push network progress notification function */
+typedef int GIT_CALLBACK(git_push_transfer_progress_cb)(
+ unsigned int current,
+ unsigned int total,
+ size_t bytes,
+ void *payload);
+
+/**
+ * Represents an update which will be performed on the remote during push
+ */
+typedef struct {
+ /**
+ * The source name of the reference
+ */
+ char *src_refname;
+ /**
+ * The name of the reference to update on the server
+ */
+ char *dst_refname;
+ /**
+ * The current target of the reference
+ */
+ git_oid src;
+ /**
+ * The new target for the reference
+ */
+ git_oid dst;
+} git_push_update;
+
+/**
+ * Callback used to inform of upcoming updates.
+ *
+ * @param updates an array containing the updates which will be sent
+ * as commands to the destination.
+ * @param len number of elements in `updates`
+ * @param payload Payload provided by the caller
+ */
+typedef int GIT_CALLBACK(git_push_negotiation)(const git_push_update **updates, size_t len, void *payload);
+
+/**
+ * Callback used to inform of the update status from the remote.
+ *
+ * Called for each updated reference on push. If `status` is
+ * not `NULL`, the update was rejected by the remote server
+ * and `status` contains the reason given.
+ *
+ * @param refname refname specifying to the remote ref
+ * @param status status message sent from the remote
+ * @param data data provided by the caller
+ * @return 0 on success, otherwise an error
+ */
+typedef int GIT_CALLBACK(git_push_update_reference_cb)(const char *refname, const char *status, void *data);
+
+#ifndef GIT_DEPRECATE_HARD
+/**
+ * Callback to resolve URLs before connecting to remote
+ *
+ * If you return GIT_PASSTHROUGH, you don't need to write anything to
+ * url_resolved.
+ *
+ * @param url_resolved The buffer to write the resolved URL to
+ * @param url The URL to resolve
+ * @param direction GIT_DIRECTION_FETCH or GIT_DIRECTION_PUSH
+ * @param payload Payload provided by the caller
+ * @return 0 on success, GIT_PASSTHROUGH or an error
+ * @deprecated Use `git_remote_set_instance_url`
+ */
+typedef int GIT_CALLBACK(git_url_resolve_cb)(git_buf *url_resolved, const char *url, int direction, void *payload);
+#endif
+
+/**
+ * Callback invoked immediately before we attempt to connect to the
+ * given url. Callers may change the URL before the connection by
+ * calling `git_remote_set_instance_url` in the callback.
+ *
+ * @param remote The remote to be connected
+ * @param direction GIT_DIRECTION_FETCH or GIT_DIRECTION_PUSH
+ * @param payload Payload provided by the caller
+ * @return 0 on success, or an error
+ */
+typedef int GIT_CALLBACK(git_remote_ready_cb)(git_remote *remote, int direction, void *payload);
+
+/**
+ * The callback settings structure
+ *
+ * Set the callbacks to be called by the remote when informing the user
+ * about the progress of the network operations.
+ */
+struct git_remote_callbacks {
+ unsigned int version; /**< The version */
+
+ /**
+ * Textual progress from the remote. Text send over the
+ * progress side-band will be passed to this function (this is
+ * the 'counting objects' output).
+ */
+ git_transport_message_cb sideband_progress;
+
+ /**
+ * Completion is called when different parts of the download
+ * process are done (currently unused).
+ */
+ int GIT_CALLBACK(completion)(git_remote_completion_t type, void *data);
+
+ /**
+ * This will be called if the remote host requires
+ * authentication in order to connect to it.
+ *
+ * Returning GIT_PASSTHROUGH will make libgit2 behave as
+ * though this field isn't set.
+ */
+ git_credential_acquire_cb credentials;
+
+ /**
+ * If cert verification fails, this will be called to let the
+ * user make the final decision of whether to allow the
+ * connection to proceed. Returns 0 to allow the connection
+ * or a negative value to indicate an error.
+ */
+ git_transport_certificate_check_cb certificate_check;
+
+ /**
+ * During the download of new data, this will be regularly
+ * called with the current count of progress done by the
+ * indexer.
+ */
+ git_indexer_progress_cb transfer_progress;
+
+ /**
+ * Each time a reference is updated locally, this function
+ * will be called with information about it.
+ */
+ int GIT_CALLBACK(update_tips)(const char *refname, const git_oid *a, const git_oid *b, void *data);
+
+ /**
+ * Function to call with progress information during pack
+ * building. Be aware that this is called inline with pack
+ * building operations, so performance may be affected.
+ */
+ git_packbuilder_progress pack_progress;
+
+ /**
+ * Function to call with progress information during the
+ * upload portion of a push. Be aware that this is called
+ * inline with pack building operations, so performance may be
+ * affected.
+ */
+ git_push_transfer_progress_cb push_transfer_progress;
+
+ /**
+ * See documentation of git_push_update_reference_cb
+ */
+ git_push_update_reference_cb push_update_reference;
+
+ /**
+ * Called once between the negotiation step and the upload. It
+ * provides information about what updates will be performed.
+ */
+ git_push_negotiation push_negotiation;
+
+ /**
+ * Create the transport to use for this operation. Leave NULL
+ * to auto-detect.
+ */
+ git_transport_cb transport;
+
+ /**
+ * Callback when the remote is ready to connect.
+ */
+ git_remote_ready_cb remote_ready;
+
+ /**
+ * This will be passed to each of the callbacks in this struct
+ * as the last parameter.
+ */
+ void *payload;
+
+#ifdef GIT_DEPRECATE_HARD
+ void *reserved;
+#else
+ /**
+ * Resolve URL before connecting to remote.
+ * The returned URL will be used to connect to the remote instead.
+ *
+ * This callback is deprecated; users should use
+ * git_remote_ready_cb and configure the instance URL instead.
+ */
+ git_url_resolve_cb resolve_url;
+#endif
+};
+
+#define GIT_REMOTE_CALLBACKS_VERSION 1
+#define GIT_REMOTE_CALLBACKS_INIT {GIT_REMOTE_CALLBACKS_VERSION}
+
+/**
+ * Initializes a `git_remote_callbacks` with default values. Equivalent to
+ * creating an instance with GIT_REMOTE_CALLBACKS_INIT.
+ *
+ * @param opts the `git_remote_callbacks` struct to initialize
+ * @param version Version of struct; pass `GIT_REMOTE_CALLBACKS_VERSION`
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_remote_init_callbacks(
+ git_remote_callbacks *opts,
+ unsigned int version);
+
+/** Acceptable prune settings when fetching */
+typedef enum {
+ /**
+ * Use the setting from the configuration
+ */
+ GIT_FETCH_PRUNE_UNSPECIFIED,
+ /**
+ * Force pruning on
+ */
+ GIT_FETCH_PRUNE,
+ /**
+ * Force pruning off
+ */
+ GIT_FETCH_NO_PRUNE
+} git_fetch_prune_t;
+
+/**
+ * Automatic tag following option
+ *
+ * Lets us select the --tags option to use.
+ */
+typedef enum {
+ /**
+ * Use the setting from the configuration.
+ */
+ GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED = 0,
+ /**
+ * Ask the server for tags pointing to objects we're already
+ * downloading.
+ */
+ GIT_REMOTE_DOWNLOAD_TAGS_AUTO,
+ /**
+ * Don't ask for any tags beyond the refspecs.
+ */
+ GIT_REMOTE_DOWNLOAD_TAGS_NONE,
+ /**
+ * Ask for the all the tags.
+ */
+ GIT_REMOTE_DOWNLOAD_TAGS_ALL
+} git_remote_autotag_option_t;
+
+/** Constants for fetch depth (shallowness of fetch). */
+typedef enum {
+ /** The fetch is "full" (not shallow). This is the default. */
+ GIT_FETCH_DEPTH_FULL = 0,
+
+ /** The fetch should "unshallow" and fetch missing data. */
+ GIT_FETCH_DEPTH_UNSHALLOW = 2147483647
+} git_fetch_depth_t;
+
+/**
+ * Fetch options structure.
+ *
+ * Zero out for defaults. Initialize with `GIT_FETCH_OPTIONS_INIT` macro to
+ * correctly set the `version` field. E.g.
+ *
+ * git_fetch_options opts = GIT_FETCH_OPTIONS_INIT;
+ */
+typedef struct {
+ int version;
+
+ /**
+ * Callbacks to use for this fetch operation
+ */
+ git_remote_callbacks callbacks;
+
+ /**
+ * Whether to perform a prune after the fetch
+ */
+ git_fetch_prune_t prune;
+
+ /**
+ * Whether to write the results to FETCH_HEAD. Defaults to
+ * on. Leave this default in order to behave like git.
+ */
+ int update_fetchhead;
+
+ /**
+ * Determines how to behave regarding tags on the remote, such
+ * as auto-downloading tags for objects we're downloading or
+ * downloading all of them.
+ *
+ * The default is to auto-follow tags.
+ */
+ git_remote_autotag_option_t download_tags;
+
+ /**
+ * Proxy options to use, by default no proxy is used.
+ */
+ git_proxy_options proxy_opts;
+
+ /**
+ * Depth of the fetch to perform, or `GIT_FETCH_DEPTH_FULL`
+ * (or `0`) for full history, or `GIT_FETCH_DEPTH_UNSHALLOW`
+ * to "unshallow" a shallow repository.
+ *
+ * The default is full (`GIT_FETCH_DEPTH_FULL` or `0`).
+ */
+ int depth;
+
+ /**
+ * Whether to allow off-site redirects. If this is not
+ * specified, the `http.followRedirects` configuration setting
+ * will be consulted.
+ */
+ git_remote_redirect_t follow_redirects;
+
+ /**
+ * Extra headers for this fetch operation
+ */
+ git_strarray custom_headers;
+} git_fetch_options;
+
+#define GIT_FETCH_OPTIONS_VERSION 1
+#define GIT_FETCH_OPTIONS_INIT { GIT_FETCH_OPTIONS_VERSION, GIT_REMOTE_CALLBACKS_INIT, GIT_FETCH_PRUNE_UNSPECIFIED, 1, \
+ GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED, GIT_PROXY_OPTIONS_INIT }
+
+/**
+ * Initialize git_fetch_options structure
+ *
+ * Initializes a `git_fetch_options` with default values. Equivalent to
+ * creating an instance with `GIT_FETCH_OPTIONS_INIT`.
+ *
+ * @param opts The `git_fetch_options` struct to initialize.
+ * @param version The struct version; pass `GIT_FETCH_OPTIONS_VERSION`.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_fetch_options_init(
+ git_fetch_options *opts,
+ unsigned int version);
+
+
+/**
+ * Controls the behavior of a git_push object.
+ */
+typedef struct {
+ unsigned int version;
+
+ /**
+ * If the transport being used to push to the remote requires the creation
+ * of a pack file, this controls the number of worker threads used by
+ * the packbuilder when creating that pack file to be sent to the remote.
+ *
+ * If set to 0, the packbuilder will auto-detect the number of threads
+ * to create. The default value is 1.
+ */
+ unsigned int pb_parallelism;
+
+ /**
+ * Callbacks to use for this push operation
+ */
+ git_remote_callbacks callbacks;
+
+ /**
+ * Proxy options to use, by default no proxy is used.
+ */
+ git_proxy_options proxy_opts;
+
+ /**
+ * Whether to allow off-site redirects. If this is not
+ * specified, the `http.followRedirects` configuration setting
+ * will be consulted.
+ */
+ git_remote_redirect_t follow_redirects;
+
+ /**
+ * Extra headers for this push operation
+ */
+ git_strarray custom_headers;
+} git_push_options;
+
+#define GIT_PUSH_OPTIONS_VERSION 1
+#define GIT_PUSH_OPTIONS_INIT { GIT_PUSH_OPTIONS_VERSION, 1, GIT_REMOTE_CALLBACKS_INIT, GIT_PROXY_OPTIONS_INIT }
+
+/**
+ * Initialize git_push_options structure
+ *
+ * Initializes a `git_push_options` with default values. Equivalent to
+ * creating an instance with `GIT_PUSH_OPTIONS_INIT`.
+ *
+ * @param opts The `git_push_options` struct to initialize.
+ * @param version The struct version; pass `GIT_PUSH_OPTIONS_VERSION`.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_push_options_init(
+ git_push_options *opts,
+ unsigned int version);
+
+/**
+ * Remote creation options structure
+ *
+ * Initialize with `GIT_REMOTE_CREATE_OPTIONS_INIT`. Alternatively, you can
+ * use `git_remote_create_options_init`.
+ *
+ */
+typedef struct {
+ unsigned int version;
+
+ /** Callbacks to use for this connection */
+ git_remote_callbacks callbacks;
+
+ /** HTTP Proxy settings */
+ git_proxy_options proxy_opts;
+
+ /**
+ * Whether to allow off-site redirects. If this is not
+ * specified, the `http.followRedirects` configuration setting
+ * will be consulted.
+ */
+ git_remote_redirect_t follow_redirects;
+
+ /** Extra HTTP headers to use in this connection */
+ git_strarray custom_headers;
+} git_remote_connect_options;
+
+#define GIT_REMOTE_CONNECT_OPTIONS_VERSION 1
+#define GIT_REMOTE_CONNECT_OPTIONS_INIT { \
+ GIT_REMOTE_CONNECT_OPTIONS_VERSION, \
+ GIT_REMOTE_CALLBACKS_INIT, \
+ GIT_PROXY_OPTIONS_INIT }
+
+/**
+ * Initialize git_remote_connect_options structure.
+ *
+ * Initializes a `git_remote_connect_options` with default values.
+ * Equivalent to creating an instance with
+ * `GIT_REMOTE_CONNECT_OPTIONS_INIT`.
+ *
+ * @param opts The `git_remote_connect_options` struct to initialize.
+ * @param version The struct version; pass `GIT_REMOTE_CONNECT_OPTIONS_VERSION`.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_remote_connect_options_init(
+ git_remote_connect_options *opts,
+ unsigned int version);
+
+/**
+ * Open a connection to a remote.
+ *
+ * The transport is selected based on the URL; the direction argument
+ * is due to a limitation of the git protocol which starts up a
+ * specific binary which can only do the one or the other.
+ *
+ * @param remote the remote to connect to
+ * @param direction GIT_DIRECTION_FETCH if you want to fetch or
+ * GIT_DIRECTION_PUSH if you want to push
+ * @param callbacks the callbacks to use for this connection
+ * @param proxy_opts proxy settings
+ * @param custom_headers extra HTTP headers to use in this connection
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_remote_connect(
+ git_remote *remote,
+ git_direction direction,
+ const git_remote_callbacks *callbacks,
+ const git_proxy_options *proxy_opts,
+ const git_strarray *custom_headers);
+
+/**
+ * Open a connection to a remote with extended options.
+ *
+ * The transport is selected based on the URL; the direction argument
+ * is due to a limitation of the git protocol which starts up a
+ * specific binary which can only do the one or the other.
+ *
+ * The given options structure will form the defaults for connection
+ * options and callback setup. Callers may override these defaults
+ * by specifying `git_fetch_options` or `git_push_options` in
+ * subsequent calls.
+ *
+ * @param remote the remote to connect to
+ * @param direction GIT_DIRECTION_FETCH if you want to fetch or
+ * GIT_DIRECTION_PUSH if you want to push
+ * @param opts the remote connection options
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_remote_connect_ext(
+ git_remote *remote,
+ git_direction direction,
+ const git_remote_connect_options *opts);
+
+/**
+ * Download and index the packfile.
+ *
+ * Connect to the remote if it hasn't been done yet, negotiate with
+ * the remote git which objects are missing, download and index the
+ * packfile.
+ *
+ * The .idx file will be created and both it and the packfile with be
+ * renamed to their final name.
+ *
+ * If options are specified and this remote is already connected then
+ * the existing remote connection options will be discarded and the
+ * remote will now use the new options.
+ *
+ * @param remote the remote
+ * @param refspecs the refspecs to use for this negotiation and
+ * download. Use NULL or an empty array to use the base refspecs
+ * @param opts the options to use for this fetch or NULL
+ * @return 0 or an error code
+ */
+ GIT_EXTERN(int) git_remote_download(
+ git_remote *remote,
+ const git_strarray *refspecs,
+ const git_fetch_options *opts);
+
+/**
+ * Create a packfile and send it to the server
+ *
+ * Connect to the remote if it hasn't been done yet, negotiate with
+ * the remote git which objects are missing, create a packfile with
+ * the missing objects and send it.
+ *
+ * If options are specified and this remote is already connected then
+ * the existing remote connection options will be discarded and the
+ * remote will now use the new options.
+ *
+ * @param remote the remote
+ * @param refspecs the refspecs to use for this negotiation and
+ * upload. Use NULL or an empty array to use the base refspecs
+ * @param opts the options to use for this push
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_remote_upload(
+ git_remote *remote,
+ const git_strarray *refspecs,
+ const git_push_options *opts);
+
+/**
+ * Update the tips to the new state.
+ *
+ * If callbacks are not specified then the callbacks specified to
+ * `git_remote_connect` will be used (if it was called).
+ *
+ * @param remote the remote to update
+ * @param reflog_message The message to insert into the reflogs. If
+ * NULL and fetching, the default is "fetch <name>", where <name> is
+ * the name of the remote (or its url, for in-memory remotes). This
+ * parameter is ignored when pushing.
+ * @param callbacks pointer to the callback structure to use or NULL
+ * @param update_fetchhead whether to write to FETCH_HEAD. Pass 1 to behave like git.
+ * @param download_tags what the behaviour for downloading tags is for this fetch. This is
+ * ignored for push. This must be the same value passed to `git_remote_download()`.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_remote_update_tips(
+ git_remote *remote,
+ const git_remote_callbacks *callbacks,
+ int update_fetchhead,
+ git_remote_autotag_option_t download_tags,
+ const char *reflog_message);
+
+/**
+ * Download new data and update tips.
+ *
+ * Convenience function to connect to a remote, download the data,
+ * disconnect and update the remote-tracking branches.
+ *
+ * If options are specified and this remote is already connected then
+ * the existing remote connection options will be discarded and the
+ * remote will now use the new options.
+ *
+ * @param remote the remote to fetch from
+ * @param refspecs the refspecs to use for this fetch. Pass NULL or an
+ * empty array to use the base refspecs.
+ * @param opts options to use for this fetch or NULL
+ * @param reflog_message The message to insert into the reflogs. If NULL, the
+ * default is "fetch"
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_remote_fetch(
+ git_remote *remote,
+ const git_strarray *refspecs,
+ const git_fetch_options *opts,
+ const char *reflog_message);
+
+/**
+ * Prune tracking refs that are no longer present on remote.
+ *
+ * If callbacks are not specified then the callbacks specified to
+ * `git_remote_connect` will be used (if it was called).
+ *
+ * @param remote the remote to prune
+ * @param callbacks callbacks to use for this prune
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_remote_prune(
+ git_remote *remote,
+ const git_remote_callbacks *callbacks);
+
+/**
+ * Perform a push.
+ *
+ * If options are specified and this remote is already connected then
+ * the existing remote connection options will be discarded and the
+ * remote will now use the new options.
+ *
+ * @param remote the remote to push to
+ * @param refspecs the refspecs to use for pushing. If NULL or an empty
+ * array, the configured refspecs will be used
+ * @param opts options to use for this push
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_remote_push(
+ git_remote *remote,
+ const git_strarray *refspecs,
+ const git_push_options *opts);
+
+/**
+ * Get the statistics structure that is filled in by the fetch operation.
+ */
+GIT_EXTERN(const git_indexer_progress *) git_remote_stats(git_remote *remote);
+
+/**
+ * Retrieve the tag auto-follow setting
+ *
+ * @param remote the remote to query
+ * @return the auto-follow setting
+ */
+GIT_EXTERN(git_remote_autotag_option_t) git_remote_autotag(const git_remote *remote);
+
+/**
+ * Set the remote's tag following setting.
+ *
+ * The change will be made in the configuration. No loaded remotes
+ * will be affected.
+ *
+ * @param repo the repository in which to make the change
+ * @param remote the name of the remote
+ * @param value the new value to take.
+ * @return 0, or an error code.
+ */
+GIT_EXTERN(int) git_remote_set_autotag(git_repository *repo, const char *remote, git_remote_autotag_option_t value);
+
+/**
+ * Retrieve the ref-prune setting
+ *
+ * @param remote the remote to query
+ * @return the ref-prune setting
+ */
+GIT_EXTERN(int) git_remote_prune_refs(const git_remote *remote);
+
+/**
+ * Give the remote a new name
+ *
+ * All remote-tracking branches and configuration settings
+ * for the remote are updated.
+ *
+ * The new name will be checked for validity.
+ * See `git_tag_create()` for rules about valid names.
+ *
+ * No loaded instances of a the remote with the old name will change
+ * their name or their list of refspecs.
+ *
+ * @param problems non-default refspecs cannot be renamed and will be
+ * stored here for further processing by the caller. Always free this
+ * strarray on successful return.
+ * @param repo the repository in which to rename
+ * @param name the current name of the remote
+ * @param new_name the new name the remote should bear
+ * @return 0, GIT_EINVALIDSPEC, GIT_EEXISTS or an error code
+ */
+GIT_EXTERN(int) git_remote_rename(
+ git_strarray *problems,
+ git_repository *repo,
+ const char *name,
+ const char *new_name);
+
+/**
+ * Ensure the remote name is well-formed.
+ *
+ * @param valid output pointer to set with validity of given remote name
+ * @param remote_name name to be checked.
+ * @return 0 on success or an error code
+ */
+GIT_EXTERN(int) git_remote_name_is_valid(int *valid, const char *remote_name);
+
+/**
+* Delete an existing persisted remote.
+*
+* All remote-tracking branches and configuration settings
+* for the remote will be removed.
+*
+* @param repo the repository in which to act
+* @param name the name of the remote to delete
+* @return 0 on success, or an error code.
+*/
+GIT_EXTERN(int) git_remote_delete(git_repository *repo, const char *name);
+
+/**
+ * Retrieve the name of the remote's default branch
+ *
+ * The default branch of a repository is the branch which HEAD points
+ * to. If the remote does not support reporting this information
+ * directly, it performs the guess as git does; that is, if there are
+ * multiple branches which point to the same commit, the first one is
+ * chosen. If the master branch is a candidate, it wins.
+ *
+ * This function must only be called after connecting.
+ *
+ * @param out the buffer in which to store the reference name
+ * @param remote the remote
+ * @return 0, GIT_ENOTFOUND if the remote does not have any references
+ * or none of them point to HEAD's commit, or an error message.
+ */
+GIT_EXTERN(int) git_remote_default_branch(git_buf *out, git_remote *remote);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/repository.h b/include/git2/repository.h
new file mode 100644
index 0000000..6ec2ac8
--- /dev/null
+++ b/include/git2/repository.h
@@ -0,0 +1,983 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_repository_h__
+#define INCLUDE_git_repository_h__
+
+#include "common.h"
+#include "types.h"
+#include "oid.h"
+#include "buffer.h"
+
+/**
+ * @file git2/repository.h
+ * @brief Git repository management routines
+ * @defgroup git_repository Git repository management routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Open a git repository.
+ *
+ * The 'path' argument must point to either a git repository
+ * folder, or an existing work dir.
+ *
+ * The method will automatically detect if 'path' is a normal
+ * or bare repository or fail is 'path' is neither.
+ *
+ * @param out pointer to the repo which will be opened
+ * @param path the path to the repository
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_repository_open(git_repository **out, const char *path);
+/**
+ * Open working tree as a repository
+ *
+ * Open the working directory of the working tree as a normal
+ * repository that can then be worked on.
+ *
+ * @param out Output pointer containing opened repository
+ * @param wt Working tree to open
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_repository_open_from_worktree(git_repository **out, git_worktree *wt);
+
+/**
+ * Create a "fake" repository to wrap an object database
+ *
+ * Create a repository object to wrap an object database to be used
+ * with the API when all you have is an object database. This doesn't
+ * have any paths associated with it, so use with care.
+ *
+ * @param out pointer to the repo
+ * @param odb the object database to wrap
+ * @param oid_type the oid type of the object database
+ * @return 0 or an error code
+ */
+#ifdef GIT_EXPERIMENTAL_SHA256
+GIT_EXTERN(int) git_repository_wrap_odb(
+ git_repository **out,
+ git_odb *odb,
+ git_oid_t oid_type);
+#else
+GIT_EXTERN(int) git_repository_wrap_odb(
+ git_repository **out,
+ git_odb *odb);
+#endif
+
+/**
+ * Look for a git repository and copy its path in the given buffer.
+ * The lookup start from base_path and walk across parent directories
+ * if nothing has been found. The lookup ends when the first repository
+ * is found, or when reaching a directory referenced in ceiling_dirs
+ * or when the filesystem changes (in case across_fs is true).
+ *
+ * The method will automatically detect if the repository is bare
+ * (if there is a repository).
+ *
+ * @param out A pointer to a user-allocated git_buf which will contain
+ * the found path.
+ *
+ * @param start_path The base path where the lookup starts.
+ *
+ * @param across_fs If true, then the lookup will not stop when a
+ * filesystem device change is detected while exploring parent directories.
+ *
+ * @param ceiling_dirs A GIT_PATH_LIST_SEPARATOR separated list of
+ * absolute symbolic link free paths. The lookup will stop when any
+ * of this paths is reached. Note that the lookup always performs on
+ * start_path no matter start_path appears in ceiling_dirs ceiling_dirs
+ * might be NULL (which is equivalent to an empty string)
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_repository_discover(
+ git_buf *out,
+ const char *start_path,
+ int across_fs,
+ const char *ceiling_dirs);
+
+/**
+ * Option flags for `git_repository_open_ext`.
+ */
+typedef enum {
+ /**
+ * Only open the repository if it can be immediately found in the
+ * start_path. Do not walk up from the start_path looking at parent
+ * directories.
+ */
+ GIT_REPOSITORY_OPEN_NO_SEARCH = (1 << 0),
+
+ /**
+ * Unless this flag is set, open will not continue searching across
+ * filesystem boundaries (i.e. when `st_dev` changes from the `stat`
+ * system call). For example, searching in a user's home directory at
+ * "/home/user/source/" will not return "/.git/" as the found repo if
+ * "/" is a different filesystem than "/home".
+ */
+ GIT_REPOSITORY_OPEN_CROSS_FS = (1 << 1),
+
+ /**
+ * Open repository as a bare repo regardless of core.bare config, and
+ * defer loading config file for faster setup.
+ * Unlike `git_repository_open_bare`, this can follow gitlinks.
+ */
+ GIT_REPOSITORY_OPEN_BARE = (1 << 2),
+
+ /**
+ * Do not check for a repository by appending /.git to the start_path;
+ * only open the repository if start_path itself points to the git
+ * directory.
+ */
+ GIT_REPOSITORY_OPEN_NO_DOTGIT = (1 << 3),
+
+ /**
+ * Find and open a git repository, respecting the environment variables
+ * used by the git command-line tools.
+ * If set, `git_repository_open_ext` will ignore the other flags and
+ * the `ceiling_dirs` argument, and will allow a NULL `path` to use
+ * `GIT_DIR` or search from the current directory.
+ * The search for a repository will respect $GIT_CEILING_DIRECTORIES and
+ * $GIT_DISCOVERY_ACROSS_FILESYSTEM. The opened repository will
+ * respect $GIT_INDEX_FILE, $GIT_NAMESPACE, $GIT_OBJECT_DIRECTORY, and
+ * $GIT_ALTERNATE_OBJECT_DIRECTORIES.
+ * In the future, this flag will also cause `git_repository_open_ext`
+ * to respect $GIT_WORK_TREE and $GIT_COMMON_DIR; currently,
+ * `git_repository_open_ext` with this flag will error out if either
+ * $GIT_WORK_TREE or $GIT_COMMON_DIR is set.
+ */
+ GIT_REPOSITORY_OPEN_FROM_ENV = (1 << 4)
+} git_repository_open_flag_t;
+
+/**
+ * Find and open a repository with extended controls.
+ *
+ * @param out Pointer to the repo which will be opened. This can
+ * actually be NULL if you only want to use the error code to
+ * see if a repo at this path could be opened.
+ * @param path Path to open as git repository. If the flags
+ * permit "searching", then this can be a path to a subdirectory
+ * inside the working directory of the repository. May be NULL if
+ * flags is GIT_REPOSITORY_OPEN_FROM_ENV.
+ * @param flags A combination of the GIT_REPOSITORY_OPEN flags above.
+ * @param ceiling_dirs A GIT_PATH_LIST_SEPARATOR delimited list of path
+ * prefixes at which the search for a containing repository should
+ * terminate.
+ * @return 0 on success, GIT_ENOTFOUND if no repository could be found,
+ * or -1 if there was a repository but open failed for some reason
+ * (such as repo corruption or system errors).
+ */
+GIT_EXTERN(int) git_repository_open_ext(
+ git_repository **out,
+ const char *path,
+ unsigned int flags,
+ const char *ceiling_dirs);
+
+/**
+ * Open a bare repository on the serverside.
+ *
+ * This is a fast open for bare repositories that will come in handy
+ * if you're e.g. hosting git repositories and need to access them
+ * efficiently
+ *
+ * @param out Pointer to the repo which will be opened.
+ * @param bare_path Direct path to the bare repository
+ * @return 0 on success, or an error code
+ */
+GIT_EXTERN(int) git_repository_open_bare(git_repository **out, const char *bare_path);
+
+/**
+ * Free a previously allocated repository
+ *
+ * Note that after a repository is free'd, all the objects it has spawned
+ * will still exist until they are manually closed by the user
+ * with `git_object_free`, but accessing any of the attributes of
+ * an object without a backing repository will result in undefined
+ * behavior
+ *
+ * @param repo repository handle to close. If NULL nothing occurs.
+ */
+GIT_EXTERN(void) git_repository_free(git_repository *repo);
+
+/**
+ * Creates a new Git repository in the given folder.
+ *
+ * TODO:
+ * - Reinit the repository
+ *
+ * @param out pointer to the repo which will be created or reinitialized
+ * @param path the path to the repository
+ * @param is_bare if true, a Git repository without a working directory is
+ * created at the pointed path. If false, provided path will be
+ * considered as the working directory into which the .git directory
+ * will be created.
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_repository_init(
+ git_repository **out,
+ const char *path,
+ unsigned is_bare);
+
+/**
+ * Option flags for `git_repository_init_ext`.
+ *
+ * These flags configure extra behaviors to `git_repository_init_ext`.
+ * In every case, the default behavior is the zero value (i.e. flag is
+ * not set). Just OR the flag values together for the `flags` parameter
+ * when initializing a new repo.
+ */
+typedef enum {
+ /**
+ * Create a bare repository with no working directory.
+ */
+ GIT_REPOSITORY_INIT_BARE = (1u << 0),
+
+ /**
+ * Return an GIT_EEXISTS error if the repo_path appears to already be
+ * an git repository.
+ */
+ GIT_REPOSITORY_INIT_NO_REINIT = (1u << 1),
+
+ /**
+ * Normally a "/.git/" will be appended to the repo path for
+ * non-bare repos (if it is not already there), but passing this flag
+ * prevents that behavior.
+ */
+ GIT_REPOSITORY_INIT_NO_DOTGIT_DIR = (1u << 2),
+
+ /**
+ * Make the repo_path (and workdir_path) as needed. Init is always willing
+ * to create the ".git" directory even without this flag. This flag tells
+ * init to create the trailing component of the repo and workdir paths
+ * as needed.
+ */
+ GIT_REPOSITORY_INIT_MKDIR = (1u << 3),
+
+ /**
+ * Recursively make all components of the repo and workdir paths as
+ * necessary.
+ */
+ GIT_REPOSITORY_INIT_MKPATH = (1u << 4),
+
+ /**
+ * libgit2 normally uses internal templates to initialize a new repo.
+ * This flags enables external templates, looking the "template_path" from
+ * the options if set, or the `init.templatedir` global config if not,
+ * or falling back on "/usr/share/git-core/templates" if it exists.
+ */
+ GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE = (1u << 5),
+
+ /**
+ * If an alternate workdir is specified, use relative paths for the gitdir
+ * and core.worktree.
+ */
+ GIT_REPOSITORY_INIT_RELATIVE_GITLINK = (1u << 6)
+} git_repository_init_flag_t;
+
+/**
+ * Mode options for `git_repository_init_ext`.
+ *
+ * Set the mode field of the `git_repository_init_options` structure
+ * either to the custom mode that you would like, or to one of the
+ * defined modes.
+ */
+typedef enum {
+ /**
+ * Use permissions configured by umask - the default.
+ */
+ GIT_REPOSITORY_INIT_SHARED_UMASK = 0,
+
+ /**
+ * Use "--shared=group" behavior, chmod'ing the new repo to be group
+ * writable and "g+sx" for sticky group assignment.
+ */
+ GIT_REPOSITORY_INIT_SHARED_GROUP = 0002775,
+
+ /**
+ * Use "--shared=all" behavior, adding world readability.
+ */
+ GIT_REPOSITORY_INIT_SHARED_ALL = 0002777
+} git_repository_init_mode_t;
+
+/**
+ * Extended options structure for `git_repository_init_ext`.
+ *
+ * This contains extra options for `git_repository_init_ext` that enable
+ * additional initialization features.
+ */
+typedef struct {
+ unsigned int version;
+
+ /**
+ * Combination of GIT_REPOSITORY_INIT flags above.
+ */
+ uint32_t flags;
+
+ /**
+ * Set to one of the standard GIT_REPOSITORY_INIT_SHARED_... constants
+ * above, or to a custom value that you would like.
+ */
+ uint32_t mode;
+
+ /**
+ * The path to the working dir or NULL for default (i.e. repo_path parent
+ * on non-bare repos). IF THIS IS RELATIVE PATH, IT WILL BE EVALUATED
+ * RELATIVE TO THE REPO_PATH. If this is not the "natural" working
+ * directory, a .git gitlink file will be created here linking to the
+ * repo_path.
+ */
+ const char *workdir_path;
+
+ /**
+ * If set, this will be used to initialize the "description" file in the
+ * repository, instead of using the template content.
+ */
+ const char *description;
+
+ /**
+ * When GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE is set, this contains
+ * the path to use for the template directory. If this is NULL, the config
+ * or default directory options will be used instead.
+ */
+ const char *template_path;
+
+ /**
+ * The name of the head to point HEAD at. If NULL, then this will be
+ * treated as "master" and the HEAD ref will be set to "refs/heads/master".
+ * If this begins with "refs/" it will be used verbatim;
+ * otherwise "refs/heads/" will be prefixed.
+ */
+ const char *initial_head;
+
+ /**
+ * If this is non-NULL, then after the rest of the repository
+ * initialization is completed, an "origin" remote will be added
+ * pointing to this URL.
+ */
+ const char *origin_url;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ /**
+ *
+ * Type of object IDs to use for this repository, or 0 for
+ * default (currently SHA1).
+ */
+ git_oid_t oid_type;
+#endif
+} git_repository_init_options;
+
+#define GIT_REPOSITORY_INIT_OPTIONS_VERSION 1
+#define GIT_REPOSITORY_INIT_OPTIONS_INIT {GIT_REPOSITORY_INIT_OPTIONS_VERSION}
+
+/**
+ * Initialize git_repository_init_options structure
+ *
+ * Initializes a `git_repository_init_options` with default values. Equivalent to
+ * creating an instance with `GIT_REPOSITORY_INIT_OPTIONS_INIT`.
+ *
+ * @param opts The `git_repository_init_options` struct to initialize.
+ * @param version The struct version; pass `GIT_REPOSITORY_INIT_OPTIONS_VERSION`.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_repository_init_options_init(
+ git_repository_init_options *opts,
+ unsigned int version);
+
+/**
+ * Create a new Git repository in the given folder with extended controls.
+ *
+ * This will initialize a new git repository (creating the repo_path
+ * if requested by flags) and working directory as needed. It will
+ * auto-detect the case sensitivity of the file system and if the
+ * file system supports file mode bits correctly.
+ *
+ * @param out Pointer to the repo which will be created or reinitialized.
+ * @param repo_path The path to the repository.
+ * @param opts Pointer to git_repository_init_options struct.
+ * @return 0 or an error code on failure.
+ */
+GIT_EXTERN(int) git_repository_init_ext(
+ git_repository **out,
+ const char *repo_path,
+ git_repository_init_options *opts);
+
+/**
+ * Retrieve and resolve the reference pointed at by HEAD.
+ *
+ * The returned `git_reference` will be owned by caller and
+ * `git_reference_free()` must be called when done with it to release the
+ * allocated memory and prevent a leak.
+ *
+ * @param out pointer to the reference which will be retrieved
+ * @param repo a repository object
+ *
+ * @return 0 on success, GIT_EUNBORNBRANCH when HEAD points to a non existing
+ * branch, GIT_ENOTFOUND when HEAD is missing; an error code otherwise
+ */
+GIT_EXTERN(int) git_repository_head(git_reference **out, git_repository *repo);
+
+/**
+ * Retrieve the referenced HEAD for the worktree
+ *
+ * @param out pointer to the reference which will be retrieved
+ * @param repo a repository object
+ * @param name name of the worktree to retrieve HEAD for
+ * @return 0 when successful, error-code otherwise
+ */
+GIT_EXTERN(int) git_repository_head_for_worktree(git_reference **out, git_repository *repo,
+ const char *name);
+
+/**
+ * Check if a repository's HEAD is detached
+ *
+ * A repository's HEAD is detached when it points directly to a commit
+ * instead of a branch.
+ *
+ * @param repo Repo to test
+ * @return 1 if HEAD is detached, 0 if it's not; error code if there
+ * was an error.
+ */
+GIT_EXTERN(int) git_repository_head_detached(git_repository *repo);
+
+/**
+ * Check if a worktree's HEAD is detached
+ *
+ * A worktree's HEAD is detached when it points directly to a
+ * commit instead of a branch.
+ *
+ * @param repo a repository object
+ * @param name name of the worktree to retrieve HEAD for
+ * @return 1 if HEAD is detached, 0 if its not; error code if
+ * there was an error
+ */
+GIT_EXTERN(int) git_repository_head_detached_for_worktree(git_repository *repo,
+ const char *name);
+
+/**
+ * Check if the current branch is unborn
+ *
+ * An unborn branch is one named from HEAD but which doesn't exist in
+ * the refs namespace, because it doesn't have any commit to point to.
+ *
+ * @param repo Repo to test
+ * @return 1 if the current branch is unborn, 0 if it's not; error
+ * code if there was an error
+ */
+GIT_EXTERN(int) git_repository_head_unborn(git_repository *repo);
+
+/**
+ * Check if a repository is empty
+ *
+ * An empty repository has just been initialized and contains no references
+ * apart from HEAD, which must be pointing to the unborn master branch,
+ * or the branch specified for the repository in the `init.defaultBranch`
+ * configuration variable.
+ *
+ * @param repo Repo to test
+ * @return 1 if the repository is empty, 0 if it isn't, error code
+ * if the repository is corrupted
+ */
+GIT_EXTERN(int) git_repository_is_empty(git_repository *repo);
+
+/**
+ * List of items which belong to the git repository layout
+ */
+typedef enum {
+ GIT_REPOSITORY_ITEM_GITDIR,
+ GIT_REPOSITORY_ITEM_WORKDIR,
+ GIT_REPOSITORY_ITEM_COMMONDIR,
+ GIT_REPOSITORY_ITEM_INDEX,
+ GIT_REPOSITORY_ITEM_OBJECTS,
+ GIT_REPOSITORY_ITEM_REFS,
+ GIT_REPOSITORY_ITEM_PACKED_REFS,
+ GIT_REPOSITORY_ITEM_REMOTES,
+ GIT_REPOSITORY_ITEM_CONFIG,
+ GIT_REPOSITORY_ITEM_INFO,
+ GIT_REPOSITORY_ITEM_HOOKS,
+ GIT_REPOSITORY_ITEM_LOGS,
+ GIT_REPOSITORY_ITEM_MODULES,
+ GIT_REPOSITORY_ITEM_WORKTREES,
+ GIT_REPOSITORY_ITEM__LAST
+} git_repository_item_t;
+
+/**
+ * Get the location of a specific repository file or directory
+ *
+ * This function will retrieve the path of a specific repository
+ * item. It will thereby honor things like the repository's
+ * common directory, gitdir, etc. In case a file path cannot
+ * exist for a given item (e.g. the working directory of a bare
+ * repository), GIT_ENOTFOUND is returned.
+ *
+ * @param out Buffer to store the path at
+ * @param repo Repository to get path for
+ * @param item The repository item for which to retrieve the path
+ * @return 0, GIT_ENOTFOUND if the path cannot exist or an error code
+ */
+GIT_EXTERN(int) git_repository_item_path(git_buf *out, const git_repository *repo, git_repository_item_t item);
+
+/**
+ * Get the path of this repository
+ *
+ * This is the path of the `.git` folder for normal repositories,
+ * or of the repository itself for bare repositories.
+ *
+ * @param repo A repository object
+ * @return the path to the repository
+ */
+GIT_EXTERN(const char *) git_repository_path(const git_repository *repo);
+
+/**
+ * Get the path of the working directory for this repository
+ *
+ * If the repository is bare, this function will always return
+ * NULL.
+ *
+ * @param repo A repository object
+ * @return the path to the working dir, if it exists
+ */
+GIT_EXTERN(const char *) git_repository_workdir(const git_repository *repo);
+
+/**
+ * Get the path of the shared common directory for this repository.
+ *
+ * If the repository is bare, it is the root directory for the repository.
+ * If the repository is a worktree, it is the parent repo's gitdir.
+ * Otherwise, it is the gitdir.
+ *
+ * @param repo A repository object
+ * @return the path to the common dir
+ */
+GIT_EXTERN(const char *) git_repository_commondir(const git_repository *repo);
+
+/**
+ * Set the path to the working directory for this repository
+ *
+ * The working directory doesn't need to be the same one
+ * that contains the `.git` folder for this repository.
+ *
+ * If this repository is bare, setting its working directory
+ * will turn it into a normal repository, capable of performing
+ * all the common workdir operations (checkout, status, index
+ * manipulation, etc).
+ *
+ * @param repo A repository object
+ * @param workdir The path to a working directory
+ * @param update_gitlink Create/update gitlink in workdir and set config
+ * "core.worktree" (if workdir is not the parent of the .git directory)
+ * @return 0, or an error code
+ */
+GIT_EXTERN(int) git_repository_set_workdir(
+ git_repository *repo, const char *workdir, int update_gitlink);
+
+/**
+ * Check if a repository is bare
+ *
+ * @param repo Repo to test
+ * @return 1 if the repository is bare, 0 otherwise.
+ */
+GIT_EXTERN(int) git_repository_is_bare(const git_repository *repo);
+
+/**
+ * Check if a repository is a linked work tree
+ *
+ * @param repo Repo to test
+ * @return 1 if the repository is a linked work tree, 0 otherwise.
+ */
+GIT_EXTERN(int) git_repository_is_worktree(const git_repository *repo);
+
+/**
+ * Get the configuration file for this repository.
+ *
+ * If a configuration file has not been set, the default
+ * config set for the repository will be returned, including
+ * global and system configurations (if they are available).
+ *
+ * The configuration file must be freed once it's no longer
+ * being used by the user.
+ *
+ * @param out Pointer to store the loaded configuration
+ * @param repo A repository object
+ * @return 0, or an error code
+ */
+GIT_EXTERN(int) git_repository_config(git_config **out, git_repository *repo);
+
+/**
+ * Get a snapshot of the repository's configuration
+ *
+ * Convenience function to take a snapshot from the repository's
+ * configuration. The contents of this snapshot will not change,
+ * even if the underlying config files are modified.
+ *
+ * The configuration file must be freed once it's no longer
+ * being used by the user.
+ *
+ * @param out Pointer to store the loaded configuration
+ * @param repo the repository
+ * @return 0, or an error code
+ */
+GIT_EXTERN(int) git_repository_config_snapshot(git_config **out, git_repository *repo);
+
+/**
+ * Get the Object Database for this repository.
+ *
+ * If a custom ODB has not been set, the default
+ * database for the repository will be returned (the one
+ * located in `.git/objects`).
+ *
+ * The ODB must be freed once it's no longer being used by
+ * the user.
+ *
+ * @param out Pointer to store the loaded ODB
+ * @param repo A repository object
+ * @return 0, or an error code
+ */
+GIT_EXTERN(int) git_repository_odb(git_odb **out, git_repository *repo);
+
+/**
+ * Get the Reference Database Backend for this repository.
+ *
+ * If a custom refsdb has not been set, the default database for
+ * the repository will be returned (the one that manipulates loose
+ * and packed references in the `.git` directory).
+ *
+ * The refdb must be freed once it's no longer being used by
+ * the user.
+ *
+ * @param out Pointer to store the loaded refdb
+ * @param repo A repository object
+ * @return 0, or an error code
+ */
+GIT_EXTERN(int) git_repository_refdb(git_refdb **out, git_repository *repo);
+
+/**
+ * Get the Index file for this repository.
+ *
+ * If a custom index has not been set, the default
+ * index for the repository will be returned (the one
+ * located in `.git/index`).
+ *
+ * The index must be freed once it's no longer being used by
+ * the user.
+ *
+ * @param out Pointer to store the loaded index
+ * @param repo A repository object
+ * @return 0, or an error code
+ */
+GIT_EXTERN(int) git_repository_index(git_index **out, git_repository *repo);
+
+/**
+ * Retrieve git's prepared message
+ *
+ * Operations such as git revert/cherry-pick/merge with the -n option
+ * stop just short of creating a commit with the changes and save
+ * their prepared message in .git/MERGE_MSG so the next git-commit
+ * execution can present it to the user for them to amend if they
+ * wish.
+ *
+ * Use this function to get the contents of this file. Don't forget to
+ * remove the file after you create the commit.
+ *
+ * @param out git_buf to write data into
+ * @param repo Repository to read prepared message from
+ * @return 0, GIT_ENOTFOUND if no message exists or an error code
+ */
+GIT_EXTERN(int) git_repository_message(git_buf *out, git_repository *repo);
+
+/**
+ * Remove git's prepared message.
+ *
+ * Remove the message that `git_repository_message` retrieves.
+ *
+ * @param repo Repository to remove prepared message from.
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_repository_message_remove(git_repository *repo);
+
+/**
+ * Remove all the metadata associated with an ongoing command like merge,
+ * revert, cherry-pick, etc. For example: MERGE_HEAD, MERGE_MSG, etc.
+ *
+ * @param repo A repository object
+ * @return 0 on success, or error
+ */
+GIT_EXTERN(int) git_repository_state_cleanup(git_repository *repo);
+
+/**
+ * Callback used to iterate over each FETCH_HEAD entry
+ *
+ * @see git_repository_fetchhead_foreach
+ *
+ * @param ref_name The reference name
+ * @param remote_url The remote URL
+ * @param oid The reference target OID
+ * @param is_merge Was the reference the result of a merge
+ * @param payload Payload passed to git_repository_fetchhead_foreach
+ * @return non-zero to terminate the iteration
+ */
+typedef int GIT_CALLBACK(git_repository_fetchhead_foreach_cb)(const char *ref_name,
+ const char *remote_url,
+ const git_oid *oid,
+ unsigned int is_merge,
+ void *payload);
+
+/**
+ * Invoke 'callback' for each entry in the given FETCH_HEAD file.
+ *
+ * Return a non-zero value from the callback to stop the loop.
+ *
+ * @param repo A repository object
+ * @param callback Callback function
+ * @param payload Pointer to callback data (optional)
+ * @return 0 on success, non-zero callback return value, GIT_ENOTFOUND if
+ * there is no FETCH_HEAD file, or other error code.
+ */
+GIT_EXTERN(int) git_repository_fetchhead_foreach(
+ git_repository *repo,
+ git_repository_fetchhead_foreach_cb callback,
+ void *payload);
+
+/**
+ * Callback used to iterate over each MERGE_HEAD entry
+ *
+ * @see git_repository_mergehead_foreach
+ *
+ * @param oid The merge OID
+ * @param payload Payload passed to git_repository_mergehead_foreach
+ * @return non-zero to terminate the iteration
+ */
+typedef int GIT_CALLBACK(git_repository_mergehead_foreach_cb)(const git_oid *oid,
+ void *payload);
+
+/**
+ * If a merge is in progress, invoke 'callback' for each commit ID in the
+ * MERGE_HEAD file.
+ *
+ * Return a non-zero value from the callback to stop the loop.
+ *
+ * @param repo A repository object
+ * @param callback Callback function
+ * @param payload Pointer to callback data (optional)
+ * @return 0 on success, non-zero callback return value, GIT_ENOTFOUND if
+ * there is no MERGE_HEAD file, or other error code.
+ */
+GIT_EXTERN(int) git_repository_mergehead_foreach(
+ git_repository *repo,
+ git_repository_mergehead_foreach_cb callback,
+ void *payload);
+
+/**
+ * Calculate hash of file using repository filtering rules.
+ *
+ * If you simply want to calculate the hash of a file on disk with no filters,
+ * you can just use the `git_odb_hashfile()` API. However, if you want to
+ * hash a file in the repository and you want to apply filtering rules (e.g.
+ * crlf filters) before generating the SHA, then use this function.
+ *
+ * Note: if the repository has `core.safecrlf` set to fail and the
+ * filtering triggers that failure, then this function will return an
+ * error and not calculate the hash of the file.
+ *
+ * @param out Output value of calculated SHA
+ * @param repo Repository pointer
+ * @param path Path to file on disk whose contents should be hashed. This
+ * may be an absolute path or a relative path, in which case it
+ * will be treated as a path within the working directory.
+ * @param type The object type to hash as (e.g. GIT_OBJECT_BLOB)
+ * @param as_path The path to use to look up filtering rules. If this is
+ * an empty string then no filters will be applied when
+ * calculating the hash. If this is `NULL` and the `path`
+ * parameter is a file within the repository's working
+ * directory, then the `path` will be used.
+ * @return 0 on success, or an error code
+ */
+GIT_EXTERN(int) git_repository_hashfile(
+ git_oid *out,
+ git_repository *repo,
+ const char *path,
+ git_object_t type,
+ const char *as_path);
+
+/**
+ * Make the repository HEAD point to the specified reference.
+ *
+ * If the provided reference points to a Tree or a Blob, the HEAD is
+ * unaltered and -1 is returned.
+ *
+ * If the provided reference points to a branch, the HEAD will point
+ * to that branch, staying attached, or become attached if it isn't yet.
+ * If the branch doesn't exist yet, no error will be return. The HEAD
+ * will then be attached to an unborn branch.
+ *
+ * Otherwise, the HEAD will be detached and will directly point to
+ * the Commit.
+ *
+ * @param repo Repository pointer
+ * @param refname Canonical name of the reference the HEAD should point at
+ * @return 0 on success, or an error code
+ */
+GIT_EXTERN(int) git_repository_set_head(
+ git_repository *repo,
+ const char *refname);
+
+/**
+ * Make the repository HEAD directly point to the Commit.
+ *
+ * If the provided committish cannot be found in the repository, the HEAD
+ * is unaltered and GIT_ENOTFOUND is returned.
+ *
+ * If the provided committish cannot be peeled into a commit, the HEAD
+ * is unaltered and -1 is returned.
+ *
+ * Otherwise, the HEAD will eventually be detached and will directly point to
+ * the peeled Commit.
+ *
+ * @param repo Repository pointer
+ * @param committish Object id of the Commit the HEAD should point to
+ * @return 0 on success, or an error code
+ */
+GIT_EXTERN(int) git_repository_set_head_detached(
+ git_repository *repo,
+ const git_oid *committish);
+
+/**
+ * Make the repository HEAD directly point to the Commit.
+ *
+ * This behaves like `git_repository_set_head_detached()` but takes an
+ * annotated commit, which lets you specify which extended sha syntax
+ * string was specified by a user, allowing for more exact reflog
+ * messages.
+ *
+ * See the documentation for `git_repository_set_head_detached()`.
+ *
+ * @see git_repository_set_head_detached
+ */
+GIT_EXTERN(int) git_repository_set_head_detached_from_annotated(
+ git_repository *repo,
+ const git_annotated_commit *committish);
+
+/**
+ * Detach the HEAD.
+ *
+ * If the HEAD is already detached and points to a Commit, 0 is returned.
+ *
+ * If the HEAD is already detached and points to a Tag, the HEAD is
+ * updated into making it point to the peeled Commit, and 0 is returned.
+ *
+ * If the HEAD is already detached and points to a non committish, the HEAD is
+ * unaltered, and -1 is returned.
+ *
+ * Otherwise, the HEAD will be detached and point to the peeled Commit.
+ *
+ * @param repo Repository pointer
+ * @return 0 on success, GIT_EUNBORNBRANCH when HEAD points to a non existing
+ * branch or an error code
+ */
+GIT_EXTERN(int) git_repository_detach_head(
+ git_repository *repo);
+
+/**
+ * Repository state
+ *
+ * These values represent possible states for the repository to be in,
+ * based on the current operation which is ongoing.
+ */
+typedef enum {
+ GIT_REPOSITORY_STATE_NONE,
+ GIT_REPOSITORY_STATE_MERGE,
+ GIT_REPOSITORY_STATE_REVERT,
+ GIT_REPOSITORY_STATE_REVERT_SEQUENCE,
+ GIT_REPOSITORY_STATE_CHERRYPICK,
+ GIT_REPOSITORY_STATE_CHERRYPICK_SEQUENCE,
+ GIT_REPOSITORY_STATE_BISECT,
+ GIT_REPOSITORY_STATE_REBASE,
+ GIT_REPOSITORY_STATE_REBASE_INTERACTIVE,
+ GIT_REPOSITORY_STATE_REBASE_MERGE,
+ GIT_REPOSITORY_STATE_APPLY_MAILBOX,
+ GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE
+} git_repository_state_t;
+
+/**
+ * Determines the status of a git repository - ie, whether an operation
+ * (merge, cherry-pick, etc) is in progress.
+ *
+ * @param repo Repository pointer
+ * @return The state of the repository
+ */
+GIT_EXTERN(int) git_repository_state(git_repository *repo);
+
+/**
+ * Sets the active namespace for this Git Repository
+ *
+ * This namespace affects all reference operations for the repo.
+ * See `man gitnamespaces`
+ *
+ * @param repo The repo
+ * @param nmspace The namespace. This should not include the refs
+ * folder, e.g. to namespace all references under `refs/namespaces/foo/`,
+ * use `foo` as the namespace.
+ * @return 0 on success, -1 on error
+ */
+GIT_EXTERN(int) git_repository_set_namespace(git_repository *repo, const char *nmspace);
+
+/**
+ * Get the currently active namespace for this repository
+ *
+ * @param repo The repo
+ * @return the active namespace, or NULL if there isn't one
+ */
+GIT_EXTERN(const char *) git_repository_get_namespace(git_repository *repo);
+
+
+/**
+ * Determine if the repository was a shallow clone
+ *
+ * @param repo The repository
+ * @return 1 if shallow, zero if not
+ */
+GIT_EXTERN(int) git_repository_is_shallow(git_repository *repo);
+
+/**
+ * Retrieve the configured identity to use for reflogs
+ *
+ * The memory is owned by the repository and must not be freed by the
+ * user.
+ *
+ * @param name where to store the pointer to the name
+ * @param email where to store the pointer to the email
+ * @param repo the repository
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_repository_ident(const char **name, const char **email, const git_repository *repo);
+
+/**
+ * Set the identity to be used for writing reflogs
+ *
+ * If both are set, this name and email will be used to write to the
+ * reflog. Pass NULL to unset. When unset, the identity will be taken
+ * from the repository's configuration.
+ *
+ * @param repo the repository to configure
+ * @param name the name to use for the reflog entries
+ * @param email the email to use for the reflog entries
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_repository_set_ident(git_repository *repo, const char *name, const char *email);
+
+/**
+ * Gets the object type used by this repository.
+ *
+ * @param repo the repository
+ * @return the object id type
+ */
+GIT_EXTERN(git_oid_t) git_repository_oid_type(git_repository *repo);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/reset.h b/include/git2/reset.h
new file mode 100644
index 0000000..b2ee2ba
--- /dev/null
+++ b/include/git2/reset.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_reset_h__
+#define INCLUDE_git_reset_h__
+
+#include "common.h"
+#include "types.h"
+#include "strarray.h"
+#include "checkout.h"
+
+/**
+ * @file git2/reset.h
+ * @brief Git reset management routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Kinds of reset operation
+ */
+typedef enum {
+ GIT_RESET_SOFT = 1, /**< Move the head to the given commit */
+ GIT_RESET_MIXED = 2, /**< SOFT plus reset index to the commit */
+ GIT_RESET_HARD = 3 /**< MIXED plus changes in working tree discarded */
+} git_reset_t;
+
+/**
+ * Sets the current head to the specified commit oid and optionally
+ * resets the index and working tree to match.
+ *
+ * SOFT reset means the Head will be moved to the commit.
+ *
+ * MIXED reset will trigger a SOFT reset, plus the index will be replaced
+ * with the content of the commit tree.
+ *
+ * HARD reset will trigger a MIXED reset and the working directory will be
+ * replaced with the content of the index. (Untracked and ignored files
+ * will be left alone, however.)
+ *
+ * TODO: Implement remaining kinds of resets.
+ *
+ * @param repo Repository where to perform the reset operation.
+ *
+ * @param target Committish to which the Head should be moved to. This object
+ * must belong to the given `repo` and can either be a git_commit or a
+ * git_tag. When a git_tag is being passed, it should be dereferenceable
+ * to a git_commit which oid will be used as the target of the branch.
+ *
+ * @param reset_type Kind of reset operation to perform.
+ *
+ * @param checkout_opts Optional checkout options to be used for a HARD reset.
+ * The checkout_strategy field will be overridden (based on reset_type).
+ * This parameter can be used to propagate notify and progress callbacks.
+ *
+ * @return 0 on success or an error code
+ */
+GIT_EXTERN(int) git_reset(
+ git_repository *repo,
+ const git_object *target,
+ git_reset_t reset_type,
+ const git_checkout_options *checkout_opts);
+
+/**
+ * Sets the current head to the specified commit oid and optionally
+ * resets the index and working tree to match.
+ *
+ * This behaves like `git_reset()` but takes an annotated commit,
+ * which lets you specify which extended sha syntax string was
+ * specified by a user, allowing for more exact reflog messages.
+ *
+ * See the documentation for `git_reset()`.
+ *
+ * @see git_reset
+ */
+GIT_EXTERN(int) git_reset_from_annotated(
+ git_repository *repo,
+ const git_annotated_commit *commit,
+ git_reset_t reset_type,
+ const git_checkout_options *checkout_opts);
+
+/**
+ * Updates some entries in the index from the target commit tree.
+ *
+ * The scope of the updated entries is determined by the paths
+ * being passed in the `pathspec` parameters.
+ *
+ * Passing a NULL `target` will result in removing
+ * entries in the index matching the provided pathspecs.
+ *
+ * @param repo Repository where to perform the reset operation.
+ *
+ * @param target The committish which content will be used to reset the content
+ * of the index.
+ *
+ * @param pathspecs List of pathspecs to operate on.
+ *
+ * @return 0 on success or an error code < 0
+ */
+GIT_EXTERN(int) git_reset_default(
+ git_repository *repo,
+ const git_object *target,
+ const git_strarray* pathspecs);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/revert.h b/include/git2/revert.h
new file mode 100644
index 0000000..331e90d
--- /dev/null
+++ b/include/git2/revert.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_revert_h__
+#define INCLUDE_git_revert_h__
+
+#include "common.h"
+#include "types.h"
+#include "merge.h"
+
+/**
+ * @file git2/revert.h
+ * @brief Git revert routines
+ * @defgroup git_revert Git revert routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Options for revert
+ */
+typedef struct {
+ unsigned int version;
+
+ /** For merge commits, the "mainline" is treated as the parent. */
+ unsigned int mainline;
+
+ git_merge_options merge_opts; /**< Options for the merging */
+ git_checkout_options checkout_opts; /**< Options for the checkout */
+} git_revert_options;
+
+#define GIT_REVERT_OPTIONS_VERSION 1
+#define GIT_REVERT_OPTIONS_INIT {GIT_REVERT_OPTIONS_VERSION, 0, GIT_MERGE_OPTIONS_INIT, GIT_CHECKOUT_OPTIONS_INIT}
+
+/**
+ * Initialize git_revert_options structure
+ *
+ * Initializes a `git_revert_options` with default values. Equivalent to
+ * creating an instance with `GIT_REVERT_OPTIONS_INIT`.
+ *
+ * @param opts The `git_revert_options` struct to initialize.
+ * @param version The struct version; pass `GIT_REVERT_OPTIONS_VERSION`.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_revert_options_init(
+ git_revert_options *opts,
+ unsigned int version);
+
+/**
+ * Reverts the given commit against the given "our" commit, producing an
+ * index that reflects the result of the revert.
+ *
+ * The returned index must be freed explicitly with `git_index_free`.
+ *
+ * @param out pointer to store the index result in
+ * @param repo the repository that contains the given commits
+ * @param revert_commit the commit to revert
+ * @param our_commit the commit to revert against (eg, HEAD)
+ * @param mainline the parent of the revert commit, if it is a merge
+ * @param merge_options the merge options (or null for defaults)
+ * @return zero on success, -1 on failure.
+ */
+GIT_EXTERN(int) git_revert_commit(
+ git_index **out,
+ git_repository *repo,
+ git_commit *revert_commit,
+ git_commit *our_commit,
+ unsigned int mainline,
+ const git_merge_options *merge_options);
+
+/**
+ * Reverts the given commit, producing changes in the index and working directory.
+ *
+ * @param repo the repository to revert
+ * @param commit the commit to revert
+ * @param given_opts the revert options (or null for defaults)
+ * @return zero on success, -1 on failure.
+ */
+GIT_EXTERN(int) git_revert(
+ git_repository *repo,
+ git_commit *commit,
+ const git_revert_options *given_opts);
+
+/** @} */
+GIT_END_DECL
+#endif
+
diff --git a/include/git2/revparse.h b/include/git2/revparse.h
new file mode 100644
index 0000000..51ea2dc
--- /dev/null
+++ b/include/git2/revparse.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_revparse_h__
+#define INCLUDE_git_revparse_h__
+
+#include "common.h"
+#include "types.h"
+
+/**
+ * @file git2/revparse.h
+ * @brief Git revision parsing routines
+ * @defgroup git_revparse Git revision parsing routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Find a single object, as specified by a revision string.
+ *
+ * See `man gitrevisions`, or
+ * http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions for
+ * information on the syntax accepted.
+ *
+ * The returned object should be released with `git_object_free` when no
+ * longer needed.
+ *
+ * @param out pointer to output object
+ * @param repo the repository to search in
+ * @param spec the textual specification for an object
+ * @return 0 on success, GIT_ENOTFOUND, GIT_EAMBIGUOUS, GIT_EINVALIDSPEC or an error code
+ */
+GIT_EXTERN(int) git_revparse_single(
+ git_object **out, git_repository *repo, const char *spec);
+
+/**
+ * Find a single object and intermediate reference by a revision string.
+ *
+ * See `man gitrevisions`, or
+ * http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions for
+ * information on the syntax accepted.
+ *
+ * In some cases (`@{<-n>}` or `<branchname>@{upstream}`), the expression may
+ * point to an intermediate reference. When such expressions are being passed
+ * in, `reference_out` will be valued as well.
+ *
+ * The returned object should be released with `git_object_free` and the
+ * returned reference with `git_reference_free` when no longer needed.
+ *
+ * @param object_out pointer to output object
+ * @param reference_out pointer to output reference or NULL
+ * @param repo the repository to search in
+ * @param spec the textual specification for an object
+ * @return 0 on success, GIT_ENOTFOUND, GIT_EAMBIGUOUS, GIT_EINVALIDSPEC
+ * or an error code
+ */
+GIT_EXTERN(int) git_revparse_ext(
+ git_object **object_out,
+ git_reference **reference_out,
+ git_repository *repo,
+ const char *spec);
+
+/**
+ * Revparse flags. These indicate the intended behavior of the spec passed to
+ * git_revparse.
+ */
+typedef enum {
+ /** The spec targeted a single object. */
+ GIT_REVSPEC_SINGLE = 1 << 0,
+ /** The spec targeted a range of commits. */
+ GIT_REVSPEC_RANGE = 1 << 1,
+ /** The spec used the '...' operator, which invokes special semantics. */
+ GIT_REVSPEC_MERGE_BASE = 1 << 2
+} git_revspec_t;
+
+/**
+ * Git Revision Spec: output of a `git_revparse` operation
+ */
+typedef struct {
+ /** The left element of the revspec; must be freed by the user */
+ git_object *from;
+ /** The right element of the revspec; must be freed by the user */
+ git_object *to;
+ /** The intent of the revspec (i.e. `git_revspec_mode_t` flags) */
+ unsigned int flags;
+} git_revspec;
+
+/**
+ * Parse a revision string for `from`, `to`, and intent.
+ *
+ * See `man gitrevisions` or
+ * http://git-scm.com/docs/git-rev-parse.html#_specifying_revisions for
+ * information on the syntax accepted.
+ *
+ * @param revspec Pointer to an user-allocated git_revspec struct where
+ * the result of the rev-parse will be stored
+ * @param repo the repository to search in
+ * @param spec the rev-parse spec to parse
+ * @return 0 on success, GIT_INVALIDSPEC, GIT_ENOTFOUND, GIT_EAMBIGUOUS or an error code
+ */
+GIT_EXTERN(int) git_revparse(
+ git_revspec *revspec,
+ git_repository *repo,
+ const char *spec);
+
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/revwalk.h b/include/git2/revwalk.h
new file mode 100644
index 0000000..4aa9f5b
--- /dev/null
+++ b/include/git2/revwalk.h
@@ -0,0 +1,302 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_revwalk_h__
+#define INCLUDE_git_revwalk_h__
+
+#include "common.h"
+#include "types.h"
+#include "oid.h"
+
+/**
+ * @file git2/revwalk.h
+ * @brief Git revision traversal routines
+ * @defgroup git_revwalk Git revision traversal routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Flags to specify the sorting which a revwalk should perform.
+ */
+typedef enum {
+ /**
+ * Sort the output with the same default method from `git`: reverse
+ * chronological order. This is the default sorting for new walkers.
+ */
+ GIT_SORT_NONE = 0,
+
+ /**
+ * Sort the repository contents in topological order (no parents before
+ * all of its children are shown); this sorting mode can be combined
+ * with time sorting to produce `git`'s `--date-order``.
+ */
+ GIT_SORT_TOPOLOGICAL = 1 << 0,
+
+ /**
+ * Sort the repository contents by commit time;
+ * this sorting mode can be combined with
+ * topological sorting.
+ */
+ GIT_SORT_TIME = 1 << 1,
+
+ /**
+ * Iterate through the repository contents in reverse
+ * order; this sorting mode can be combined with
+ * any of the above.
+ */
+ GIT_SORT_REVERSE = 1 << 2
+} git_sort_t;
+
+/**
+ * Allocate a new revision walker to iterate through a repo.
+ *
+ * This revision walker uses a custom memory pool and an internal
+ * commit cache, so it is relatively expensive to allocate.
+ *
+ * For maximum performance, this revision walker should be
+ * reused for different walks.
+ *
+ * This revision walker is *not* thread safe: it may only be
+ * used to walk a repository on a single thread; however,
+ * it is possible to have several revision walkers in
+ * several different threads walking the same repository.
+ *
+ * @param out pointer to the new revision walker
+ * @param repo the repo to walk through
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_revwalk_new(git_revwalk **out, git_repository *repo);
+
+/**
+ * Reset the revision walker for reuse.
+ *
+ * This will clear all the pushed and hidden commits, and
+ * leave the walker in a blank state (just like at
+ * creation) ready to receive new commit pushes and
+ * start a new walk.
+ *
+ * The revision walk is automatically reset when a walk
+ * is over.
+ *
+ * @param walker handle to reset.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_revwalk_reset(git_revwalk *walker);
+
+/**
+ * Add a new root for the traversal
+ *
+ * The pushed commit will be marked as one of the roots from which to
+ * start the walk. This commit may not be walked if it or a child is
+ * hidden.
+ *
+ * At least one commit must be pushed onto the walker before a walk
+ * can be started.
+ *
+ * The given id must belong to a committish on the walked
+ * repository.
+ *
+ * @param walk the walker being used for the traversal.
+ * @param id the oid of the commit to start from.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_revwalk_push(git_revwalk *walk, const git_oid *id);
+
+/**
+ * Push matching references
+ *
+ * The OIDs pointed to by the references that match the given glob
+ * pattern will be pushed to the revision walker.
+ *
+ * A leading 'refs/' is implied if not present as well as a trailing
+ * '/\*' if the glob lacks '?', '\*' or '['.
+ *
+ * Any references matching this glob which do not point to a
+ * committish will be ignored.
+ *
+ * @param walk the walker being used for the traversal
+ * @param glob the glob pattern references should match
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_revwalk_push_glob(git_revwalk *walk, const char *glob);
+
+/**
+ * Push the repository's HEAD
+ *
+ * @param walk the walker being used for the traversal
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_revwalk_push_head(git_revwalk *walk);
+
+/**
+ * Mark a commit (and its ancestors) uninteresting for the output.
+ *
+ * The given id must belong to a committish on the walked
+ * repository.
+ *
+ * The resolved commit and all its parents will be hidden from the
+ * output on the revision walk.
+ *
+ * @param walk the walker being used for the traversal.
+ * @param commit_id the oid of commit that will be ignored during the traversal
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_revwalk_hide(git_revwalk *walk, const git_oid *commit_id);
+
+/**
+ * Hide matching references.
+ *
+ * The OIDs pointed to by the references that match the given glob
+ * pattern and their ancestors will be hidden from the output on the
+ * revision walk.
+ *
+ * A leading 'refs/' is implied if not present as well as a trailing
+ * '/\*' if the glob lacks '?', '\*' or '['.
+ *
+ * Any references matching this glob which do not point to a
+ * committish will be ignored.
+ *
+ * @param walk the walker being used for the traversal
+ * @param glob the glob pattern references should match
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_revwalk_hide_glob(git_revwalk *walk, const char *glob);
+
+/**
+ * Hide the repository's HEAD
+ *
+ * @param walk the walker being used for the traversal
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_revwalk_hide_head(git_revwalk *walk);
+
+/**
+ * Push the OID pointed to by a reference
+ *
+ * The reference must point to a committish.
+ *
+ * @param walk the walker being used for the traversal
+ * @param refname the reference to push
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_revwalk_push_ref(git_revwalk *walk, const char *refname);
+
+/**
+ * Hide the OID pointed to by a reference
+ *
+ * The reference must point to a committish.
+ *
+ * @param walk the walker being used for the traversal
+ * @param refname the reference to hide
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_revwalk_hide_ref(git_revwalk *walk, const char *refname);
+
+/**
+ * Get the next commit from the revision walk.
+ *
+ * The initial call to this method is *not* blocking when
+ * iterating through a repo with a time-sorting mode.
+ *
+ * Iterating with Topological or inverted modes makes the initial
+ * call blocking to preprocess the commit list, but this block should be
+ * mostly unnoticeable on most repositories (topological preprocessing
+ * times at 0.3s on the git.git repo).
+ *
+ * The revision walker is reset when the walk is over.
+ *
+ * @param out Pointer where to store the oid of the next commit
+ * @param walk the walker to pop the commit from.
+ * @return 0 if the next commit was found;
+ * GIT_ITEROVER if there are no commits left to iterate
+ */
+GIT_EXTERN(int) git_revwalk_next(git_oid *out, git_revwalk *walk);
+
+/**
+ * Change the sorting mode when iterating through the
+ * repository's contents.
+ *
+ * Changing the sorting mode resets the walker.
+ *
+ * @param walk the walker being used for the traversal.
+ * @param sort_mode combination of GIT_SORT_XXX flags
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_revwalk_sorting(git_revwalk *walk, unsigned int sort_mode);
+
+/**
+ * Push and hide the respective endpoints of the given range.
+ *
+ * The range should be of the form
+ * <commit>..<commit>
+ * where each <commit> is in the form accepted by 'git_revparse_single'.
+ * The left-hand commit will be hidden and the right-hand commit pushed.
+ *
+ * @param walk the walker being used for the traversal
+ * @param range the range
+ * @return 0 or an error code
+ *
+ */
+GIT_EXTERN(int) git_revwalk_push_range(git_revwalk *walk, const char *range);
+
+/**
+ * Simplify the history by first-parent
+ *
+ * No parents other than the first for each commit will be enqueued.
+ *
+ * @param walk The revision walker.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_revwalk_simplify_first_parent(git_revwalk *walk);
+
+
+/**
+ * Free a revision walker previously allocated.
+ *
+ * @param walk traversal handle to close. If NULL nothing occurs.
+ */
+GIT_EXTERN(void) git_revwalk_free(git_revwalk *walk);
+
+/**
+ * Return the repository on which this walker
+ * is operating.
+ *
+ * @param walk the revision walker
+ * @return the repository being walked
+ */
+GIT_EXTERN(git_repository *) git_revwalk_repository(git_revwalk *walk);
+
+/**
+ * This is a callback function that user can provide to hide a
+ * commit and its parents. If the callback function returns non-zero value,
+ * then this commit and its parents will be hidden.
+ *
+ * @param commit_id oid of Commit
+ * @param payload User-specified pointer to data to be passed as data payload
+ * @return non-zero to hide the commmit and it parent.
+ */
+typedef int GIT_CALLBACK(git_revwalk_hide_cb)(
+ const git_oid *commit_id,
+ void *payload);
+
+/**
+ * Adds, changes or removes a callback function to hide a commit and its parents
+ *
+ * @param walk the revision walker
+ * @param hide_cb callback function to hide a commit and its parents
+ * @param payload data payload to be passed to callback function
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_revwalk_add_hide_cb(
+ git_revwalk *walk,
+ git_revwalk_hide_cb hide_cb,
+ void *payload);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/signature.h b/include/git2/signature.h
new file mode 100644
index 0000000..849998e
--- /dev/null
+++ b/include/git2/signature.h
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_signature_h__
+#define INCLUDE_git_signature_h__
+
+#include "common.h"
+#include "types.h"
+
+/**
+ * @file git2/signature.h
+ * @brief Git signature creation
+ * @defgroup git_signature Git signature creation
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Create a new action signature.
+ *
+ * Call `git_signature_free()` to free the data.
+ *
+ * Note: angle brackets ('<' and '>') characters are not allowed
+ * to be used in either the `name` or the `email` parameter.
+ *
+ * @param out new signature, in case of error NULL
+ * @param name name of the person
+ * @param email email of the person
+ * @param time time (in seconds from epoch) when the action happened
+ * @param offset timezone offset (in minutes) for the time
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_signature_new(git_signature **out, const char *name, const char *email, git_time_t time, int offset);
+
+/**
+ * Create a new action signature with a timestamp of 'now'.
+ *
+ * Call `git_signature_free()` to free the data.
+ *
+ * @param out new signature, in case of error NULL
+ * @param name name of the person
+ * @param email email of the person
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_signature_now(git_signature **out, const char *name, const char *email);
+
+/**
+ * Create a new action signature with default user and now timestamp.
+ *
+ * This looks up the user.name and user.email from the configuration and
+ * uses the current time as the timestamp, and creates a new signature
+ * based on that information. It will return GIT_ENOTFOUND if either the
+ * user.name or user.email are not set.
+ *
+ * @param out new signature
+ * @param repo repository pointer
+ * @return 0 on success, GIT_ENOTFOUND if config is missing, or error code
+ */
+GIT_EXTERN(int) git_signature_default(git_signature **out, git_repository *repo);
+
+/**
+ * Create a new signature by parsing the given buffer, which is
+ * expected to be in the format "Real Name <email> timestamp tzoffset",
+ * where `timestamp` is the number of seconds since the Unix epoch and
+ * `tzoffset` is the timezone offset in `hhmm` format (note the lack
+ * of a colon separator).
+ *
+ * @param out new signature
+ * @param buf signature string
+ * @return 0 on success, GIT_EINVALID if the signature is not parseable, or an error code
+ */
+GIT_EXTERN(int) git_signature_from_buffer(git_signature **out, const char *buf);
+
+/**
+ * Create a copy of an existing signature. All internal strings are also
+ * duplicated.
+ *
+ * Call `git_signature_free()` to free the data.
+ *
+ * @param dest pointer where to store the copy
+ * @param sig signature to duplicate
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_signature_dup(git_signature **dest, const git_signature *sig);
+
+/**
+ * Free an existing signature.
+ *
+ * Because the signature is not an opaque structure, it is legal to free it
+ * manually, but be sure to free the "name" and "email" strings in addition
+ * to the structure itself.
+ *
+ * @param sig signature to free
+ */
+GIT_EXTERN(void) git_signature_free(git_signature *sig);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/stash.h b/include/git2/stash.h
new file mode 100644
index 0000000..dcfc013
--- /dev/null
+++ b/include/git2/stash.h
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_stash_h__
+#define INCLUDE_git_stash_h__
+
+#include "common.h"
+#include "types.h"
+#include "checkout.h"
+
+/**
+ * @file git2/stash.h
+ * @brief Git stash management routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Stash flags
+ */
+typedef enum {
+ /**
+ * No option, default
+ */
+ GIT_STASH_DEFAULT = 0,
+
+ /**
+ * All changes already added to the index are left intact in
+ * the working directory
+ */
+ GIT_STASH_KEEP_INDEX = (1 << 0),
+
+ /**
+ * All untracked files are also stashed and then cleaned up
+ * from the working directory
+ */
+ GIT_STASH_INCLUDE_UNTRACKED = (1 << 1),
+
+ /**
+ * All ignored files are also stashed and then cleaned up from
+ * the working directory
+ */
+ GIT_STASH_INCLUDE_IGNORED = (1 << 2),
+
+ /**
+ * All changes in the index and working directory are left intact
+ */
+ GIT_STASH_KEEP_ALL = (1 << 3)
+} git_stash_flags;
+
+/**
+ * Save the local modifications to a new stash.
+ *
+ * @param out Object id of the commit containing the stashed state.
+ * This commit is also the target of the direct reference refs/stash.
+ * @param repo The owning repository.
+ * @param stasher The identity of the person performing the stashing.
+ * @param message Optional description along with the stashed state.
+ * @param flags Flags to control the stashing process. (see GIT_STASH_* above)
+ * @return 0 on success, GIT_ENOTFOUND where there's nothing to stash,
+ * or error code.
+ */
+GIT_EXTERN(int) git_stash_save(
+ git_oid *out,
+ git_repository *repo,
+ const git_signature *stasher,
+ const char *message,
+ uint32_t flags);
+
+/**
+ * Stash save options structure
+ *
+ * Initialize with `GIT_STASH_SAVE_OPTIONS_INIT`. Alternatively, you can
+ * use `git_stash_save_options_init`.
+ *
+ */
+typedef struct git_stash_save_options {
+ unsigned int version;
+
+ /** Flags to control the stashing process. (see GIT_STASH_* above) */
+ uint32_t flags;
+
+ /** The identity of the person performing the stashing. */
+ const git_signature *stasher;
+
+ /** Optional description along with the stashed state. */
+ const char *message;
+
+ /** Optional paths that control which files are stashed. */
+ git_strarray paths;
+} git_stash_save_options;
+
+#define GIT_STASH_SAVE_OPTIONS_VERSION 1
+#define GIT_STASH_SAVE_OPTIONS_INIT { GIT_STASH_SAVE_OPTIONS_VERSION }
+
+/**
+ * Initialize git_stash_save_options structure
+ *
+ * Initializes a `git_stash_save_options` with default values. Equivalent to
+ * creating an instance with `GIT_STASH_SAVE_OPTIONS_INIT`.
+ *
+ * @param opts The `git_stash_save_options` struct to initialize.
+ * @param version The struct version; pass `GIT_STASH_SAVE_OPTIONS_VERSION`.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_stash_save_options_init(
+ git_stash_save_options *opts, unsigned int version);
+
+/**
+ * Save the local modifications to a new stash, with options.
+ *
+ * @param out Object id of the commit containing the stashed state.
+ * This commit is also the target of the direct reference refs/stash.
+ * @param repo The owning repository.
+ * @param opts The stash options.
+ * @return 0 on success, GIT_ENOTFOUND where there's nothing to stash,
+ * or error code.
+ */
+GIT_EXTERN(int) git_stash_save_with_opts(
+ git_oid *out,
+ git_repository *repo,
+ const git_stash_save_options *opts);
+
+/** Stash application flags. */
+typedef enum {
+ GIT_STASH_APPLY_DEFAULT = 0,
+
+ /* Try to reinstate not only the working tree's changes,
+ * but also the index's changes.
+ */
+ GIT_STASH_APPLY_REINSTATE_INDEX = (1 << 0)
+} git_stash_apply_flags;
+
+/** Stash apply progression states */
+typedef enum {
+ GIT_STASH_APPLY_PROGRESS_NONE = 0,
+
+ /** Loading the stashed data from the object database. */
+ GIT_STASH_APPLY_PROGRESS_LOADING_STASH,
+
+ /** The stored index is being analyzed. */
+ GIT_STASH_APPLY_PROGRESS_ANALYZE_INDEX,
+
+ /** The modified files are being analyzed. */
+ GIT_STASH_APPLY_PROGRESS_ANALYZE_MODIFIED,
+
+ /** The untracked and ignored files are being analyzed. */
+ GIT_STASH_APPLY_PROGRESS_ANALYZE_UNTRACKED,
+
+ /** The untracked files are being written to disk. */
+ GIT_STASH_APPLY_PROGRESS_CHECKOUT_UNTRACKED,
+
+ /** The modified files are being written to disk. */
+ GIT_STASH_APPLY_PROGRESS_CHECKOUT_MODIFIED,
+
+ /** The stash was applied successfully. */
+ GIT_STASH_APPLY_PROGRESS_DONE
+} git_stash_apply_progress_t;
+
+/**
+ * Stash application progress notification function.
+ * Return 0 to continue processing, or a negative value to
+ * abort the stash application.
+ */
+typedef int GIT_CALLBACK(git_stash_apply_progress_cb)(
+ git_stash_apply_progress_t progress,
+ void *payload);
+
+/**
+ * Stash application options structure
+ *
+ * Initialize with `GIT_STASH_APPLY_OPTIONS_INIT`. Alternatively, you can
+ * use `git_stash_apply_options_init`.
+ *
+ */
+typedef struct git_stash_apply_options {
+ unsigned int version;
+
+ /** See `git_stash_apply_flags`, above. */
+ uint32_t flags;
+
+ /** Options to use when writing files to the working directory. */
+ git_checkout_options checkout_options;
+
+ /** Optional callback to notify the consumer of application progress. */
+ git_stash_apply_progress_cb progress_cb;
+ void *progress_payload;
+} git_stash_apply_options;
+
+#define GIT_STASH_APPLY_OPTIONS_VERSION 1
+#define GIT_STASH_APPLY_OPTIONS_INIT { \
+ GIT_STASH_APPLY_OPTIONS_VERSION, \
+ GIT_STASH_APPLY_DEFAULT, \
+ GIT_CHECKOUT_OPTIONS_INIT }
+
+/**
+ * Initialize git_stash_apply_options structure
+ *
+ * Initializes a `git_stash_apply_options` with default values. Equivalent to
+ * creating an instance with `GIT_STASH_APPLY_OPTIONS_INIT`.
+ *
+ * @param opts The `git_stash_apply_options` struct to initialize.
+ * @param version The struct version; pass `GIT_STASH_APPLY_OPTIONS_VERSION`.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_stash_apply_options_init(
+ git_stash_apply_options *opts, unsigned int version);
+
+/**
+ * Apply a single stashed state from the stash list.
+ *
+ * If local changes in the working directory conflict with changes in the
+ * stash then GIT_EMERGECONFLICT will be returned. In this case, the index
+ * will always remain unmodified and all files in the working directory will
+ * remain unmodified. However, if you are restoring untracked files or
+ * ignored files and there is a conflict when applying the modified files,
+ * then those files will remain in the working directory.
+ *
+ * If passing the GIT_STASH_APPLY_REINSTATE_INDEX flag and there would be
+ * conflicts when reinstating the index, the function will return
+ * GIT_EMERGECONFLICT and both the working directory and index will be left
+ * unmodified.
+ *
+ * Note that a minimum checkout strategy of `GIT_CHECKOUT_SAFE` is implied.
+ *
+ * @param repo The owning repository.
+ * @param index The position within the stash list. 0 points to the
+ * most recent stashed state.
+ * @param options Optional options to control how stashes are applied.
+ *
+ * @return 0 on success, GIT_ENOTFOUND if there's no stashed state for the
+ * given index, GIT_EMERGECONFLICT if changes exist in the working
+ * directory, or an error code
+ */
+GIT_EXTERN(int) git_stash_apply(
+ git_repository *repo,
+ size_t index,
+ const git_stash_apply_options *options);
+
+/**
+ * This is a callback function you can provide to iterate over all the
+ * stashed states that will be invoked per entry.
+ *
+ * @param index The position within the stash list. 0 points to the
+ * most recent stashed state.
+ * @param message The stash message.
+ * @param stash_id The commit oid of the stashed state.
+ * @param payload Extra parameter to callback function.
+ * @return 0 to continue iterating or non-zero to stop.
+ */
+typedef int GIT_CALLBACK(git_stash_cb)(
+ size_t index,
+ const char *message,
+ const git_oid *stash_id,
+ void *payload);
+
+/**
+ * Loop over all the stashed states and issue a callback for each one.
+ *
+ * If the callback returns a non-zero value, this will stop looping.
+ *
+ * @param repo Repository where to find the stash.
+ *
+ * @param callback Callback to invoke per found stashed state. The most
+ * recent stash state will be enumerated first.
+ *
+ * @param payload Extra parameter to callback function.
+ *
+ * @return 0 on success, non-zero callback return value, or error code.
+ */
+GIT_EXTERN(int) git_stash_foreach(
+ git_repository *repo,
+ git_stash_cb callback,
+ void *payload);
+
+/**
+ * Remove a single stashed state from the stash list.
+ *
+ * @param repo The owning repository.
+ *
+ * @param index The position within the stash list. 0 points to the
+ * most recent stashed state.
+ *
+ * @return 0 on success, GIT_ENOTFOUND if there's no stashed state for the given
+ * index, or error code.
+ */
+GIT_EXTERN(int) git_stash_drop(
+ git_repository *repo,
+ size_t index);
+
+/**
+ * Apply a single stashed state from the stash list and remove it from the list
+ * if successful.
+ *
+ * @param repo The owning repository.
+ * @param index The position within the stash list. 0 points to the
+ * most recent stashed state.
+ * @param options Optional options to control how stashes are applied.
+ *
+ * @return 0 on success, GIT_ENOTFOUND if there's no stashed state for the given
+ * index, or error code. (see git_stash_apply() above for details)
+*/
+GIT_EXTERN(int) git_stash_pop(
+ git_repository *repo,
+ size_t index,
+ const git_stash_apply_options *options);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/status.h b/include/git2/status.h
new file mode 100644
index 0000000..bb28e87
--- /dev/null
+++ b/include/git2/status.h
@@ -0,0 +1,452 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_status_h__
+#define INCLUDE_git_status_h__
+
+#include "common.h"
+#include "types.h"
+#include "strarray.h"
+#include "diff.h"
+
+/**
+ * @file git2/status.h
+ * @brief Git file status routines
+ * @defgroup git_status Git file status routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Status flags for a single file.
+ *
+ * A combination of these values will be returned to indicate the status of
+ * a file. Status compares the working directory, the index, and the
+ * current HEAD of the repository. The `GIT_STATUS_INDEX` set of flags
+ * represents the status of file in the index relative to the HEAD, and the
+ * `GIT_STATUS_WT` set of flags represent the status of the file in the
+ * working directory relative to the index.
+ */
+typedef enum {
+ GIT_STATUS_CURRENT = 0,
+
+ GIT_STATUS_INDEX_NEW = (1u << 0),
+ GIT_STATUS_INDEX_MODIFIED = (1u << 1),
+ GIT_STATUS_INDEX_DELETED = (1u << 2),
+ GIT_STATUS_INDEX_RENAMED = (1u << 3),
+ GIT_STATUS_INDEX_TYPECHANGE = (1u << 4),
+
+ GIT_STATUS_WT_NEW = (1u << 7),
+ GIT_STATUS_WT_MODIFIED = (1u << 8),
+ GIT_STATUS_WT_DELETED = (1u << 9),
+ GIT_STATUS_WT_TYPECHANGE = (1u << 10),
+ GIT_STATUS_WT_RENAMED = (1u << 11),
+ GIT_STATUS_WT_UNREADABLE = (1u << 12),
+
+ GIT_STATUS_IGNORED = (1u << 14),
+ GIT_STATUS_CONFLICTED = (1u << 15)
+} git_status_t;
+
+/**
+ * Function pointer to receive status on individual files
+ *
+ * `path` is the relative path to the file from the root of the repository.
+ *
+ * `status_flags` is a combination of `git_status_t` values that apply.
+ *
+ * `payload` is the value you passed to the foreach function as payload.
+ */
+typedef int GIT_CALLBACK(git_status_cb)(
+ const char *path, unsigned int status_flags, void *payload);
+
+/**
+ * Select the files on which to report status.
+ *
+ * With `git_status_foreach_ext`, this will control which changes get
+ * callbacks. With `git_status_list_new`, these will control which
+ * changes are included in the list.
+ */
+typedef enum {
+ /**
+ * The default. This roughly matches `git status --porcelain` regarding
+ * which files are included and in what order.
+ */
+ GIT_STATUS_SHOW_INDEX_AND_WORKDIR = 0,
+
+ /**
+ * Only gives status based on HEAD to index comparison, not looking at
+ * working directory changes.
+ */
+ GIT_STATUS_SHOW_INDEX_ONLY = 1,
+
+ /**
+ * Only gives status based on index to working directory comparison,
+ * not comparing the index to the HEAD.
+ */
+ GIT_STATUS_SHOW_WORKDIR_ONLY = 2
+} git_status_show_t;
+
+/**
+ * Flags to control status callbacks
+ *
+ * Calling `git_status_foreach()` is like calling the extended version
+ * with: GIT_STATUS_OPT_INCLUDE_IGNORED, GIT_STATUS_OPT_INCLUDE_UNTRACKED,
+ * and GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS. Those options are bundled
+ * together as `GIT_STATUS_OPT_DEFAULTS` if you want them as a baseline.
+ */
+typedef enum {
+ /**
+ * Says that callbacks should be made on untracked files.
+ * These will only be made if the workdir files are included in the status
+ * "show" option.
+ */
+ GIT_STATUS_OPT_INCLUDE_UNTRACKED = (1u << 0),
+
+ /**
+ * Says that ignored files get callbacks.
+ * Again, these callbacks will only be made if the workdir files are
+ * included in the status "show" option.
+ */
+ GIT_STATUS_OPT_INCLUDE_IGNORED = (1u << 1),
+
+ /**
+ * Indicates that callback should be made even on unmodified files.
+ */
+ GIT_STATUS_OPT_INCLUDE_UNMODIFIED = (1u << 2),
+
+ /**
+ * Indicates that submodules should be skipped.
+ * This only applies if there are no pending typechanges to the submodule
+ * (either from or to another type).
+ */
+ GIT_STATUS_OPT_EXCLUDE_SUBMODULES = (1u << 3),
+
+ /**
+ * Indicates that all files in untracked directories should be included.
+ * Normally if an entire directory is new, then just the top-level
+ * directory is included (with a trailing slash on the entry name).
+ * This flag says to include all of the individual files in the directory
+ * instead.
+ */
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS = (1u << 4),
+
+ /**
+ * Indicates that the given path should be treated as a literal path,
+ * and not as a pathspec pattern.
+ */
+ GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH = (1u << 5),
+
+ /**
+ * Indicates that the contents of ignored directories should be included
+ * in the status. This is like doing `git ls-files -o -i --exclude-standard`
+ * with core git.
+ */
+ GIT_STATUS_OPT_RECURSE_IGNORED_DIRS = (1u << 6),
+
+ /**
+ * Indicates that rename detection should be processed between the head and
+ * the index and enables the GIT_STATUS_INDEX_RENAMED as a possible status
+ * flag.
+ */
+ GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX = (1u << 7),
+
+ /**
+ * Indicates that rename detection should be run between the index and the
+ * working directory and enabled GIT_STATUS_WT_RENAMED as a possible status
+ * flag.
+ */
+ GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR = (1u << 8),
+
+ /**
+ * Overrides the native case sensitivity for the file system and forces
+ * the output to be in case-sensitive order.
+ */
+ GIT_STATUS_OPT_SORT_CASE_SENSITIVELY = (1u << 9),
+
+ /**
+ * Overrides the native case sensitivity for the file system and forces
+ * the output to be in case-insensitive order.
+ */
+ GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY = (1u << 10),
+
+ /**
+ * Iindicates that rename detection should include rewritten files.
+ */
+ GIT_STATUS_OPT_RENAMES_FROM_REWRITES = (1u << 11),
+
+ /**
+ * Bypasses the default status behavior of doing a "soft" index reload
+ * (i.e. reloading the index data if the file on disk has been modified
+ * outside libgit2).
+ */
+ GIT_STATUS_OPT_NO_REFRESH = (1u << 12),
+
+ /**
+ * Tells libgit2 to refresh the stat cache in the index for files that are
+ * unchanged but have out of date stat einformation in the index.
+ * It will result in less work being done on subsequent calls to get status.
+ * This is mutually exclusive with the NO_REFRESH option.
+ */
+ GIT_STATUS_OPT_UPDATE_INDEX = (1u << 13),
+
+ /**
+ * Normally files that cannot be opened or read are ignored as
+ * these are often transient files; this option will return
+ * unreadable files as `GIT_STATUS_WT_UNREADABLE`.
+ */
+ GIT_STATUS_OPT_INCLUDE_UNREADABLE = (1u << 14),
+
+ /**
+ * Unreadable files will be detected and given the status
+ * untracked instead of unreadable.
+ */
+ GIT_STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED = (1u << 15)
+} git_status_opt_t;
+
+#define GIT_STATUS_OPT_DEFAULTS \
+ (GIT_STATUS_OPT_INCLUDE_IGNORED | \
+ GIT_STATUS_OPT_INCLUDE_UNTRACKED | \
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS)
+
+/**
+ * Options to control how `git_status_foreach_ext()` will issue callbacks.
+ *
+ * Initialize with `GIT_STATUS_OPTIONS_INIT`. Alternatively, you can
+ * use `git_status_options_init`.
+ *
+ */
+typedef struct {
+ /**
+ * The struct version; pass `GIT_STATUS_OPTIONS_VERSION`.
+ */
+ unsigned int version;
+
+ /**
+ * The `show` value is one of the `git_status_show_t` constants that
+ * control which files to scan and in what order. The default is
+ * `GIT_STATUS_SHOW_INDEX_AND_WORKDIR`.
+ */
+ git_status_show_t show;
+
+ /**
+ * The `flags` value is an OR'ed combination of the
+ * `git_status_opt_t` values above. The default is
+ * `GIT_STATUS_OPT_DEFAULTS`, which matches git's default
+ * behavior.
+ */
+ unsigned int flags;
+
+ /**
+ * The `pathspec` is an array of path patterns to match (using
+ * fnmatch-style matching), or just an array of paths to match
+ * exactly if `GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH` is specified
+ * in the flags.
+ */
+ git_strarray pathspec;
+
+ /**
+ * The `baseline` is the tree to be used for comparison to the
+ * working directory and index; defaults to HEAD.
+ */
+ git_tree *baseline;
+
+ /**
+ * Threshold above which similar files will be considered renames.
+ * This is equivalent to the -M option. Defaults to 50.
+ */
+ uint16_t rename_threshold;
+} git_status_options;
+
+#define GIT_STATUS_OPTIONS_VERSION 1
+#define GIT_STATUS_OPTIONS_INIT {GIT_STATUS_OPTIONS_VERSION}
+
+/**
+ * Initialize git_status_options structure
+ *
+ * Initializes a `git_status_options` with default values. Equivalent to
+ * creating an instance with `GIT_STATUS_OPTIONS_INIT`.
+ *
+ * @param opts The `git_status_options` struct to initialize.
+ * @param version The struct version; pass `GIT_STATUS_OPTIONS_VERSION`.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_status_options_init(
+ git_status_options *opts,
+ unsigned int version);
+
+/**
+ * A status entry, providing the differences between the file as it exists
+ * in HEAD and the index, and providing the differences between the index
+ * and the working directory.
+ *
+ * The `status` value provides the status flags for this file.
+ *
+ * The `head_to_index` value provides detailed information about the
+ * differences between the file in HEAD and the file in the index.
+ *
+ * The `index_to_workdir` value provides detailed information about the
+ * differences between the file in the index and the file in the
+ * working directory.
+ */
+typedef struct {
+ git_status_t status;
+ git_diff_delta *head_to_index;
+ git_diff_delta *index_to_workdir;
+} git_status_entry;
+
+
+/**
+ * Gather file statuses and run a callback for each one.
+ *
+ * The callback is passed the path of the file, the status (a combination of
+ * the `git_status_t` values above) and the `payload` data pointer passed
+ * into this function.
+ *
+ * If the callback returns a non-zero value, this function will stop looping
+ * and return that value to caller.
+ *
+ * @param repo A repository object
+ * @param callback The function to call on each file
+ * @param payload Pointer to pass through to callback function
+ * @return 0 on success, non-zero callback return value, or error code
+ */
+GIT_EXTERN(int) git_status_foreach(
+ git_repository *repo,
+ git_status_cb callback,
+ void *payload);
+
+/**
+ * Gather file status information and run callbacks as requested.
+ *
+ * This is an extended version of the `git_status_foreach()` API that
+ * allows for more granular control over which paths will be processed and
+ * in what order. See the `git_status_options` structure for details
+ * about the additional controls that this makes available.
+ *
+ * Note that if a `pathspec` is given in the `git_status_options` to filter
+ * the status, then the results from rename detection (if you enable it) may
+ * not be accurate. To do rename detection properly, this must be called
+ * with no `pathspec` so that all files can be considered.
+ *
+ * @param repo Repository object
+ * @param opts Status options structure
+ * @param callback The function to call on each file
+ * @param payload Pointer to pass through to callback function
+ * @return 0 on success, non-zero callback return value, or error code
+ */
+GIT_EXTERN(int) git_status_foreach_ext(
+ git_repository *repo,
+ const git_status_options *opts,
+ git_status_cb callback,
+ void *payload);
+
+/**
+ * Get file status for a single file.
+ *
+ * This tries to get status for the filename that you give. If no files
+ * match that name (in either the HEAD, index, or working directory), this
+ * returns GIT_ENOTFOUND.
+ *
+ * If the name matches multiple files (for example, if the `path` names a
+ * directory or if running on a case- insensitive filesystem and yet the
+ * HEAD has two entries that both match the path), then this returns
+ * GIT_EAMBIGUOUS because it cannot give correct results.
+ *
+ * This does not do any sort of rename detection. Renames require a set of
+ * targets and because of the path filtering, there is not enough
+ * information to check renames correctly. To check file status with rename
+ * detection, there is no choice but to do a full `git_status_list_new` and
+ * scan through looking for the path that you are interested in.
+ *
+ * @param status_flags Output combination of git_status_t values for file
+ * @param repo A repository object
+ * @param path The exact path to retrieve status for relative to the
+ * repository working directory
+ * @return 0 on success, GIT_ENOTFOUND if the file is not found in the HEAD,
+ * index, and work tree, GIT_EAMBIGUOUS if `path` matches multiple files
+ * or if it refers to a folder, and -1 on other errors.
+ */
+GIT_EXTERN(int) git_status_file(
+ unsigned int *status_flags,
+ git_repository *repo,
+ const char *path);
+
+/**
+ * Gather file status information and populate the `git_status_list`.
+ *
+ * Note that if a `pathspec` is given in the `git_status_options` to filter
+ * the status, then the results from rename detection (if you enable it) may
+ * not be accurate. To do rename detection properly, this must be called
+ * with no `pathspec` so that all files can be considered.
+ *
+ * @param out Pointer to store the status results in
+ * @param repo Repository object
+ * @param opts Status options structure
+ * @return 0 on success or error code
+ */
+GIT_EXTERN(int) git_status_list_new(
+ git_status_list **out,
+ git_repository *repo,
+ const git_status_options *opts);
+
+/**
+ * Gets the count of status entries in this list.
+ *
+ * If there are no changes in status (at least according the options given
+ * when the status list was created), this can return 0.
+ *
+ * @param statuslist Existing status list object
+ * @return the number of status entries
+ */
+GIT_EXTERN(size_t) git_status_list_entrycount(
+ git_status_list *statuslist);
+
+/**
+ * Get a pointer to one of the entries in the status list.
+ *
+ * The entry is not modifiable and should not be freed.
+ *
+ * @param statuslist Existing status list object
+ * @param idx Position of the entry
+ * @return Pointer to the entry; NULL if out of bounds
+ */
+GIT_EXTERN(const git_status_entry *) git_status_byindex(
+ git_status_list *statuslist,
+ size_t idx);
+
+/**
+ * Free an existing status list
+ *
+ * @param statuslist Existing status list object
+ */
+GIT_EXTERN(void) git_status_list_free(
+ git_status_list *statuslist);
+
+/**
+ * Test if the ignore rules apply to a given file.
+ *
+ * This function checks the ignore rules to see if they would apply to the
+ * given file. This indicates if the file would be ignored regardless of
+ * whether the file is already in the index or committed to the repository.
+ *
+ * One way to think of this is if you were to do "git add ." on the
+ * directory containing the file, would it be added or not?
+ *
+ * @param ignored Boolean returning 0 if the file is not ignored, 1 if it is
+ * @param repo A repository object
+ * @param path The file to check ignores for, rooted at the repo's workdir.
+ * @return 0 if ignore rules could be processed for the file (regardless
+ * of whether it exists or not), or an error < 0 if they could not.
+ */
+GIT_EXTERN(int) git_status_should_ignore(
+ int *ignored,
+ git_repository *repo,
+ const char *path);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/stdint.h b/include/git2/stdint.h
new file mode 100644
index 0000000..6950427
--- /dev/null
+++ b/include/git2/stdint.h
@@ -0,0 +1,247 @@
+// ISO C9x compliant stdint.h for Microsoft Visual Studio
+// Based on ISO/IEC 9899:TC2 Committee draft (May 6, 2005) WG14/N1124
+//
+// Copyright (c) 2006-2008 Alexander Chemeris
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice,
+// this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the distribution.
+//
+// 3. The name of the author may be used to endorse or promote products
+// derived from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+// OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+// WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+// ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#ifdef _MSC_VER // [
+
+#ifndef _MSC_STDINT_H_ // [
+#define _MSC_STDINT_H_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif
+
+#include <limits.h>
+
+// For Visual Studio 6 in C++ mode and for many Visual Studio versions when
+// compiling for ARM we should wrap <wchar.h> include with 'extern "C++" {}'
+// or compiler give many errors like this:
+// error C2733: second C linkage of overloaded function 'wmemchr' not allowed
+#ifdef __cplusplus
+extern "C" {
+#endif
+# include <wchar.h>
+#ifdef __cplusplus
+}
+#endif
+
+// Define _W64 macros to mark types changing their size, like intptr_t.
+#ifndef _W64
+# if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300
+# define _W64 __w64
+# else
+# define _W64
+# endif
+#endif
+
+
+// 7.18.1 Integer types
+
+// 7.18.1.1 Exact-width integer types
+
+// Visual Studio 6 and Embedded Visual C++ 4 doesn't
+// realize that, e.g. char has the same size as __int8
+// so we give up on __intX for them.
+#if (_MSC_VER < 1300)
+ typedef signed char int8_t;
+ typedef signed short int16_t;
+ typedef signed int int32_t;
+ typedef unsigned char uint8_t;
+ typedef unsigned short uint16_t;
+ typedef unsigned int uint32_t;
+#else
+ typedef signed __int8 int8_t;
+ typedef signed __int16 int16_t;
+ typedef signed __int32 int32_t;
+ typedef unsigned __int8 uint8_t;
+ typedef unsigned __int16 uint16_t;
+ typedef unsigned __int32 uint32_t;
+#endif
+typedef signed __int64 int64_t;
+typedef unsigned __int64 uint64_t;
+
+
+// 7.18.1.2 Minimum-width integer types
+typedef int8_t int_least8_t;
+typedef int16_t int_least16_t;
+typedef int32_t int_least32_t;
+typedef int64_t int_least64_t;
+typedef uint8_t uint_least8_t;
+typedef uint16_t uint_least16_t;
+typedef uint32_t uint_least32_t;
+typedef uint64_t uint_least64_t;
+
+// 7.18.1.3 Fastest minimum-width integer types
+typedef int8_t int_fast8_t;
+typedef int16_t int_fast16_t;
+typedef int32_t int_fast32_t;
+typedef int64_t int_fast64_t;
+typedef uint8_t uint_fast8_t;
+typedef uint16_t uint_fast16_t;
+typedef uint32_t uint_fast32_t;
+typedef uint64_t uint_fast64_t;
+
+// 7.18.1.4 Integer types capable of holding object pointers
+#ifdef _WIN64 // [
+ typedef signed __int64 intptr_t;
+ typedef unsigned __int64 uintptr_t;
+#else // _WIN64 ][
+ typedef _W64 signed int intptr_t;
+ typedef _W64 unsigned int uintptr_t;
+#endif // _WIN64 ]
+
+// 7.18.1.5 Greatest-width integer types
+typedef int64_t intmax_t;
+typedef uint64_t uintmax_t;
+
+
+// 7.18.2 Limits of specified-width integer types
+
+#if !defined(__cplusplus) || defined(__STDC_LIMIT_MACROS) // [ See footnote 220 at page 257 and footnote 221 at page 259
+
+// 7.18.2.1 Limits of exact-width integer types
+#define INT8_MIN ((int8_t)_I8_MIN)
+#define INT8_MAX _I8_MAX
+#define INT16_MIN ((int16_t)_I16_MIN)
+#define INT16_MAX _I16_MAX
+#define INT32_MIN ((int32_t)_I32_MIN)
+#define INT32_MAX _I32_MAX
+#define INT64_MIN ((int64_t)_I64_MIN)
+#define INT64_MAX _I64_MAX
+#define UINT8_MAX _UI8_MAX
+#define UINT16_MAX _UI16_MAX
+#define UINT32_MAX _UI32_MAX
+#define UINT64_MAX _UI64_MAX
+
+// 7.18.2.2 Limits of minimum-width integer types
+#define INT_LEAST8_MIN INT8_MIN
+#define INT_LEAST8_MAX INT8_MAX
+#define INT_LEAST16_MIN INT16_MIN
+#define INT_LEAST16_MAX INT16_MAX
+#define INT_LEAST32_MIN INT32_MIN
+#define INT_LEAST32_MAX INT32_MAX
+#define INT_LEAST64_MIN INT64_MIN
+#define INT_LEAST64_MAX INT64_MAX
+#define UINT_LEAST8_MAX UINT8_MAX
+#define UINT_LEAST16_MAX UINT16_MAX
+#define UINT_LEAST32_MAX UINT32_MAX
+#define UINT_LEAST64_MAX UINT64_MAX
+
+// 7.18.2.3 Limits of fastest minimum-width integer types
+#define INT_FAST8_MIN INT8_MIN
+#define INT_FAST8_MAX INT8_MAX
+#define INT_FAST16_MIN INT16_MIN
+#define INT_FAST16_MAX INT16_MAX
+#define INT_FAST32_MIN INT32_MIN
+#define INT_FAST32_MAX INT32_MAX
+#define INT_FAST64_MIN INT64_MIN
+#define INT_FAST64_MAX INT64_MAX
+#define UINT_FAST8_MAX UINT8_MAX
+#define UINT_FAST16_MAX UINT16_MAX
+#define UINT_FAST32_MAX UINT32_MAX
+#define UINT_FAST64_MAX UINT64_MAX
+
+// 7.18.2.4 Limits of integer types capable of holding object pointers
+#ifdef _WIN64 // [
+# define INTPTR_MIN INT64_MIN
+# define INTPTR_MAX INT64_MAX
+# define UINTPTR_MAX UINT64_MAX
+#else // _WIN64 ][
+# define INTPTR_MIN INT32_MIN
+# define INTPTR_MAX INT32_MAX
+# define UINTPTR_MAX UINT32_MAX
+#endif // _WIN64 ]
+
+// 7.18.2.5 Limits of greatest-width integer types
+#define INTMAX_MIN INT64_MIN
+#define INTMAX_MAX INT64_MAX
+#define UINTMAX_MAX UINT64_MAX
+
+// 7.18.3 Limits of other integer types
+
+#ifdef _WIN64 // [
+# define PTRDIFF_MIN _I64_MIN
+# define PTRDIFF_MAX _I64_MAX
+#else // _WIN64 ][
+# define PTRDIFF_MIN _I32_MIN
+# define PTRDIFF_MAX _I32_MAX
+#endif // _WIN64 ]
+
+#define SIG_ATOMIC_MIN INT_MIN
+#define SIG_ATOMIC_MAX INT_MAX
+
+#ifndef SIZE_MAX // [
+# ifdef _WIN64 // [
+# define SIZE_MAX _UI64_MAX
+# else // _WIN64 ][
+# define SIZE_MAX _UI32_MAX
+# endif // _WIN64 ]
+#endif // SIZE_MAX ]
+
+// WCHAR_MIN and WCHAR_MAX are also defined in <wchar.h>
+#ifndef WCHAR_MIN // [
+# define WCHAR_MIN 0
+#endif // WCHAR_MIN ]
+#ifndef WCHAR_MAX // [
+# define WCHAR_MAX _UI16_MAX
+#endif // WCHAR_MAX ]
+
+#define WINT_MIN 0
+#define WINT_MAX _UI16_MAX
+
+#endif // __STDC_LIMIT_MACROS ]
+
+
+// 7.18.4 Limits of other integer types
+
+#if !defined(__cplusplus) || defined(__STDC_CONSTANT_MACROS) // [ See footnote 224 at page 260
+
+// 7.18.4.1 Macros for minimum-width integer constants
+
+#define INT8_C(val) val##i8
+#define INT16_C(val) val##i16
+#define INT32_C(val) val##i32
+#define INT64_C(val) val##i64
+
+#define UINT8_C(val) val##ui8
+#define UINT16_C(val) val##ui16
+#define UINT32_C(val) val##ui32
+#define UINT64_C(val) val##ui64
+
+// 7.18.4.2 Macros for greatest-width integer constants
+#define INTMAX_C INT64_C
+#define UINTMAX_C UINT64_C
+
+#endif // __STDC_CONSTANT_MACROS ]
+
+
+#endif // _MSC_STDINT_H_ ]
+
+#endif // _MSC_VER ] \ No newline at end of file
diff --git a/include/git2/strarray.h b/include/git2/strarray.h
new file mode 100644
index 0000000..03d93f8
--- /dev/null
+++ b/include/git2/strarray.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_strarray_h__
+#define INCLUDE_git_strarray_h__
+
+#include "common.h"
+
+/**
+ * @file git2/strarray.h
+ * @brief Git string array routines
+ * @defgroup git_strarray Git string array routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/** Array of strings */
+typedef struct git_strarray {
+ char **strings;
+ size_t count;
+} git_strarray;
+
+/**
+ * Free the strings contained in a string array. This method should
+ * be called on `git_strarray` objects that were provided by the
+ * library. Not doing so, will result in a memory leak.
+ *
+ * This does not free the `git_strarray` itself, since the library will
+ * never allocate that object directly itself.
+ *
+ * @param array The git_strarray that contains strings to free
+ */
+GIT_EXTERN(void) git_strarray_dispose(git_strarray *array);
+
+/** @} */
+GIT_END_DECL
+
+#endif
+
diff --git a/include/git2/submodule.h b/include/git2/submodule.h
new file mode 100644
index 0000000..2082966
--- /dev/null
+++ b/include/git2/submodule.h
@@ -0,0 +1,668 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_submodule_h__
+#define INCLUDE_git_submodule_h__
+
+#include "common.h"
+#include "types.h"
+#include "oid.h"
+#include "remote.h"
+#include "checkout.h"
+
+/**
+ * @file git2/submodule.h
+ * @brief Git submodule management utilities
+ *
+ * Submodule support in libgit2 builds a list of known submodules and keeps
+ * it in the repository. The list is built from the .gitmodules file, the
+ * .git/config file, the index, and the HEAD tree. Items in the working
+ * directory that look like submodules (i.e. a git repo) but are not
+ * mentioned in those places won't be tracked.
+ *
+ * @defgroup git_submodule Git submodule management routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Return codes for submodule status.
+ *
+ * A combination of these flags will be returned to describe the status of a
+ * submodule. Depending on the "ignore" property of the submodule, some of
+ * the flags may never be returned because they indicate changes that are
+ * supposed to be ignored.
+ *
+ * Submodule info is contained in 4 places: the HEAD tree, the index, config
+ * files (both .git/config and .gitmodules), and the working directory. Any
+ * or all of those places might be missing information about the submodule
+ * depending on what state the repo is in. We consider all four places to
+ * build the combination of status flags.
+ *
+ * There are four values that are not really status, but give basic info
+ * about what sources of submodule data are available. These will be
+ * returned even if ignore is set to "ALL".
+ *
+ * * IN_HEAD - superproject head contains submodule
+ * * IN_INDEX - superproject index contains submodule
+ * * IN_CONFIG - superproject gitmodules has submodule
+ * * IN_WD - superproject workdir has submodule
+ *
+ * The following values will be returned so long as ignore is not "ALL".
+ *
+ * * INDEX_ADDED - in index, not in head
+ * * INDEX_DELETED - in head, not in index
+ * * INDEX_MODIFIED - index and head don't match
+ * * WD_UNINITIALIZED - workdir contains empty directory
+ * * WD_ADDED - in workdir, not index
+ * * WD_DELETED - in index, not workdir
+ * * WD_MODIFIED - index and workdir head don't match
+ *
+ * The following can only be returned if ignore is "NONE" or "UNTRACKED".
+ *
+ * * WD_INDEX_MODIFIED - submodule workdir index is dirty
+ * * WD_WD_MODIFIED - submodule workdir has modified files
+ *
+ * Lastly, the following will only be returned for ignore "NONE".
+ *
+ * * WD_UNTRACKED - wd contains untracked files
+ */
+typedef enum {
+ GIT_SUBMODULE_STATUS_IN_HEAD = (1u << 0),
+ GIT_SUBMODULE_STATUS_IN_INDEX = (1u << 1),
+ GIT_SUBMODULE_STATUS_IN_CONFIG = (1u << 2),
+ GIT_SUBMODULE_STATUS_IN_WD = (1u << 3),
+ GIT_SUBMODULE_STATUS_INDEX_ADDED = (1u << 4),
+ GIT_SUBMODULE_STATUS_INDEX_DELETED = (1u << 5),
+ GIT_SUBMODULE_STATUS_INDEX_MODIFIED = (1u << 6),
+ GIT_SUBMODULE_STATUS_WD_UNINITIALIZED = (1u << 7),
+ GIT_SUBMODULE_STATUS_WD_ADDED = (1u << 8),
+ GIT_SUBMODULE_STATUS_WD_DELETED = (1u << 9),
+ GIT_SUBMODULE_STATUS_WD_MODIFIED = (1u << 10),
+ GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED = (1u << 11),
+ GIT_SUBMODULE_STATUS_WD_WD_MODIFIED = (1u << 12),
+ GIT_SUBMODULE_STATUS_WD_UNTRACKED = (1u << 13)
+} git_submodule_status_t;
+
+#define GIT_SUBMODULE_STATUS__IN_FLAGS 0x000Fu
+#define GIT_SUBMODULE_STATUS__INDEX_FLAGS 0x0070u
+#define GIT_SUBMODULE_STATUS__WD_FLAGS 0x3F80u
+
+#define GIT_SUBMODULE_STATUS_IS_UNMODIFIED(S) \
+ (((S) & ~GIT_SUBMODULE_STATUS__IN_FLAGS) == 0)
+
+#define GIT_SUBMODULE_STATUS_IS_INDEX_UNMODIFIED(S) \
+ (((S) & GIT_SUBMODULE_STATUS__INDEX_FLAGS) == 0)
+
+#define GIT_SUBMODULE_STATUS_IS_WD_UNMODIFIED(S) \
+ (((S) & (GIT_SUBMODULE_STATUS__WD_FLAGS & \
+ ~GIT_SUBMODULE_STATUS_WD_UNINITIALIZED)) == 0)
+
+#define GIT_SUBMODULE_STATUS_IS_WD_DIRTY(S) \
+ (((S) & (GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED | \
+ GIT_SUBMODULE_STATUS_WD_WD_MODIFIED | \
+ GIT_SUBMODULE_STATUS_WD_UNTRACKED)) != 0)
+
+/**
+ * Function pointer to receive each submodule
+ *
+ * @param sm git_submodule currently being visited
+ * @param name name of the submodule
+ * @param payload value you passed to the foreach function as payload
+ * @return 0 on success or error code
+ */
+typedef int GIT_CALLBACK(git_submodule_cb)(
+ git_submodule *sm, const char *name, void *payload);
+
+/**
+ * Submodule update options structure
+ *
+ * Initialize with `GIT_SUBMODULE_UPDATE_OPTIONS_INIT`. Alternatively, you can
+ * use `git_submodule_update_options_init`.
+ *
+ */
+typedef struct git_submodule_update_options {
+ unsigned int version;
+
+ /**
+ * These options are passed to the checkout step. To disable
+ * checkout, set the `checkout_strategy` to
+ * `GIT_CHECKOUT_NONE`. Generally you will want the use
+ * GIT_CHECKOUT_SAFE to update files in the working
+ * directory.
+ */
+ git_checkout_options checkout_opts;
+
+ /**
+ * Options which control the fetch, including callbacks.
+ *
+ * The callbacks to use for reporting fetch progress, and for acquiring
+ * credentials in the event they are needed.
+ */
+ git_fetch_options fetch_opts;
+
+ /**
+ * Allow fetching from the submodule's default remote if the target
+ * commit isn't found. Enabled by default.
+ */
+ int allow_fetch;
+} git_submodule_update_options;
+
+#define GIT_SUBMODULE_UPDATE_OPTIONS_VERSION 1
+#define GIT_SUBMODULE_UPDATE_OPTIONS_INIT \
+ { GIT_SUBMODULE_UPDATE_OPTIONS_VERSION, \
+ { GIT_CHECKOUT_OPTIONS_VERSION, GIT_CHECKOUT_SAFE }, \
+ GIT_FETCH_OPTIONS_INIT, 1 }
+
+/**
+ * Initialize git_submodule_update_options structure
+ *
+ * Initializes a `git_submodule_update_options` with default values. Equivalent to
+ * creating an instance with `GIT_SUBMODULE_UPDATE_OPTIONS_INIT`.
+ *
+ * @param opts The `git_submodule_update_options` struct to initialize.
+ * @param version The struct version; pass `GIT_SUBMODULE_UPDATE_OPTIONS_VERSION`.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_submodule_update_options_init(
+ git_submodule_update_options *opts, unsigned int version);
+
+/**
+ * Update a submodule. This will clone a missing submodule and
+ * checkout the subrepository to the commit specified in the index of
+ * the containing repository. If the submodule repository doesn't contain
+ * the target commit (e.g. because fetchRecurseSubmodules isn't set), then
+ * the submodule is fetched using the fetch options supplied in options.
+ *
+ * @param submodule Submodule object
+ * @param init If the submodule is not initialized, setting this flag to true
+ * will initialize the submodule before updating. Otherwise, this will
+ * return an error if attempting to update an uninitialized repository.
+ * but setting this to true forces them to be updated.
+ * @param options configuration options for the update. If NULL, the
+ * function works as though GIT_SUBMODULE_UPDATE_OPTIONS_INIT was passed.
+ * @return 0 on success, any non-zero return value from a callback
+ * function, or a negative value to indicate an error (use
+ * `git_error_last` for a detailed error message).
+ */
+GIT_EXTERN(int) git_submodule_update(git_submodule *submodule, int init, git_submodule_update_options *options);
+
+/**
+ * Lookup submodule information by name or path.
+ *
+ * Given either the submodule name or path (they are usually the same), this
+ * returns a structure describing the submodule.
+ *
+ * There are two expected error scenarios:
+ *
+ * - The submodule is not mentioned in the HEAD, the index, and the config,
+ * but does "exist" in the working directory (i.e. there is a subdirectory
+ * that appears to be a Git repository). In this case, this function
+ * returns GIT_EEXISTS to indicate a sub-repository exists but not in a
+ * state where a git_submodule can be instantiated.
+ * - The submodule is not mentioned in the HEAD, index, or config and the
+ * working directory doesn't contain a value git repo at that path.
+ * There may or may not be anything else at that path, but nothing that
+ * looks like a submodule. In this case, this returns GIT_ENOTFOUND.
+ *
+ * You must call `git_submodule_free` when done with the submodule.
+ *
+ * @param out Output ptr to submodule; pass NULL to just get return code
+ * @param repo The parent repository
+ * @param name The name of or path to the submodule; trailing slashes okay
+ * @return 0 on success, GIT_ENOTFOUND if submodule does not exist,
+ * GIT_EEXISTS if a repository is found in working directory only,
+ * -1 on other errors.
+ */
+GIT_EXTERN(int) git_submodule_lookup(
+ git_submodule **out,
+ git_repository *repo,
+ const char *name);
+
+/**
+ * Create an in-memory copy of a submodule. The copy must be explicitly
+ * free'd or it will leak.
+ *
+ * @param out Pointer to store the copy of the submodule.
+ * @param source Original submodule to copy.
+ * @return 0
+ */
+GIT_EXTERN(int) git_submodule_dup(git_submodule **out, git_submodule *source);
+
+/**
+ * Release a submodule
+ *
+ * @param submodule Submodule object
+ */
+GIT_EXTERN(void) git_submodule_free(git_submodule *submodule);
+
+/**
+ * Iterate over all tracked submodules of a repository.
+ *
+ * See the note on `git_submodule` above. This iterates over the tracked
+ * submodules as described therein.
+ *
+ * If you are concerned about items in the working directory that look like
+ * submodules but are not tracked, the diff API will generate a diff record
+ * for workdir items that look like submodules but are not tracked, showing
+ * them as added in the workdir. Also, the status API will treat the entire
+ * subdirectory of a contained git repo as a single GIT_STATUS_WT_NEW item.
+ *
+ * @param repo The repository
+ * @param callback Function to be called with the name of each submodule.
+ * Return a non-zero value to terminate the iteration.
+ * @param payload Extra data to pass to callback
+ * @return 0 on success, -1 on error, or non-zero return value of callback
+ */
+GIT_EXTERN(int) git_submodule_foreach(
+ git_repository *repo,
+ git_submodule_cb callback,
+ void *payload);
+
+/**
+ * Set up a new git submodule for checkout.
+ *
+ * This does "git submodule add" up to the fetch and checkout of the
+ * submodule contents. It preps a new submodule, creates an entry in
+ * .gitmodules and creates an empty initialized repository either at the
+ * given path in the working directory or in .git/modules with a gitlink
+ * from the working directory to the new repo.
+ *
+ * To fully emulate "git submodule add" call this function, then open the
+ * submodule repo and perform the clone step as needed (if you don't need
+ * anything custom see `git_submodule_add_clone()`). Lastly, call
+ * `git_submodule_add_finalize()` to wrap up adding the new submodule and
+ * .gitmodules to the index to be ready to commit.
+ *
+ * You must call `git_submodule_free` on the submodule object when done.
+ *
+ * @param out The newly created submodule ready to open for clone
+ * @param repo The repository in which you want to create the submodule
+ * @param url URL for the submodule's remote
+ * @param path Path at which the submodule should be created
+ * @param use_gitlink Should workdir contain a gitlink to the repo in
+ * .git/modules vs. repo directly in workdir.
+ * @return 0 on success, GIT_EEXISTS if submodule already exists,
+ * -1 on other errors.
+ */
+GIT_EXTERN(int) git_submodule_add_setup(
+ git_submodule **out,
+ git_repository *repo,
+ const char *url,
+ const char *path,
+ int use_gitlink);
+
+/**
+ * Perform the clone step for a newly created submodule.
+ *
+ * This performs the necessary `git_clone` to setup a newly-created submodule.
+ *
+ * @param out The newly created repository object. Optional.
+ * @param submodule The submodule currently waiting for its clone.
+ * @param opts The options to use.
+ *
+ * @return 0 on success, -1 on other errors (see git_clone).
+ */
+GIT_EXTERN(int) git_submodule_clone(
+ git_repository **out,
+ git_submodule *submodule,
+ const git_submodule_update_options *opts);
+
+/**
+ * Resolve the setup of a new git submodule.
+ *
+ * This should be called on a submodule once you have called add setup
+ * and done the clone of the submodule. This adds the .gitmodules file
+ * and the newly cloned submodule to the index to be ready to be committed
+ * (but doesn't actually do the commit).
+ *
+ * @param submodule The submodule to finish adding.
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_submodule_add_finalize(git_submodule *submodule);
+
+/**
+ * Add current submodule HEAD commit to index of superproject.
+ *
+ * @param submodule The submodule to add to the index
+ * @param write_index Boolean if this should immediately write the index
+ * file. If you pass this as false, you will have to get the
+ * git_index and explicitly call `git_index_write()` on it to
+ * save the change.
+ * @return 0 on success, <0 on failure
+ */
+GIT_EXTERN(int) git_submodule_add_to_index(
+ git_submodule *submodule,
+ int write_index);
+
+/**
+ * Get the containing repository for a submodule.
+ *
+ * This returns a pointer to the repository that contains the submodule.
+ * This is a just a reference to the repository that was passed to the
+ * original `git_submodule_lookup()` call, so if that repository has been
+ * freed, then this may be a dangling reference.
+ *
+ * @param submodule Pointer to submodule object
+ * @return Pointer to `git_repository`
+ */
+GIT_EXTERN(git_repository *) git_submodule_owner(git_submodule *submodule);
+
+/**
+ * Get the name of submodule.
+ *
+ * @param submodule Pointer to submodule object
+ * @return Pointer to the submodule name
+ */
+GIT_EXTERN(const char *) git_submodule_name(git_submodule *submodule);
+
+/**
+ * Get the path to the submodule.
+ *
+ * The path is almost always the same as the submodule name, but the
+ * two are actually not required to match.
+ *
+ * @param submodule Pointer to submodule object
+ * @return Pointer to the submodule path
+ */
+GIT_EXTERN(const char *) git_submodule_path(git_submodule *submodule);
+
+/**
+ * Get the URL for the submodule.
+ *
+ * @param submodule Pointer to submodule object
+ * @return Pointer to the submodule url
+ */
+GIT_EXTERN(const char *) git_submodule_url(git_submodule *submodule);
+
+/**
+ * Resolve a submodule url relative to the given repository.
+ *
+ * @param out buffer to store the absolute submodule url in
+ * @param repo Pointer to repository object
+ * @param url Relative url
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_submodule_resolve_url(git_buf *out, git_repository *repo, const char *url);
+
+/**
+* Get the branch for the submodule.
+*
+* @param submodule Pointer to submodule object
+* @return Pointer to the submodule branch
+*/
+GIT_EXTERN(const char *) git_submodule_branch(git_submodule *submodule);
+
+/**
+ * Set the branch for the submodule in the configuration
+ *
+ * After calling this, you may wish to call `git_submodule_sync()` to
+ * write the changes to the checked out submodule repository.
+ *
+ * @param repo the repository to affect
+ * @param name the name of the submodule to configure
+ * @param branch Branch that should be used for the submodule
+ * @return 0 on success, <0 on failure
+ */
+GIT_EXTERN(int) git_submodule_set_branch(git_repository *repo, const char *name, const char *branch);
+
+/**
+ * Set the URL for the submodule in the configuration
+ *
+ *
+ * After calling this, you may wish to call `git_submodule_sync()` to
+ * write the changes to the checked out submodule repository.
+ *
+ * @param repo the repository to affect
+ * @param name the name of the submodule to configure
+ * @param url URL that should be used for the submodule
+ * @return 0 on success, <0 on failure
+ */
+GIT_EXTERN(int) git_submodule_set_url(git_repository *repo, const char *name, const char *url);
+
+/**
+ * Get the OID for the submodule in the index.
+ *
+ * @param submodule Pointer to submodule object
+ * @return Pointer to git_oid or NULL if submodule is not in index.
+ */
+GIT_EXTERN(const git_oid *) git_submodule_index_id(git_submodule *submodule);
+
+/**
+ * Get the OID for the submodule in the current HEAD tree.
+ *
+ * @param submodule Pointer to submodule object
+ * @return Pointer to git_oid or NULL if submodule is not in the HEAD.
+ */
+GIT_EXTERN(const git_oid *) git_submodule_head_id(git_submodule *submodule);
+
+/**
+ * Get the OID for the submodule in the current working directory.
+ *
+ * This returns the OID that corresponds to looking up 'HEAD' in the checked
+ * out submodule. If there are pending changes in the index or anything
+ * else, this won't notice that. You should call `git_submodule_status()`
+ * for a more complete picture about the state of the working directory.
+ *
+ * @param submodule Pointer to submodule object
+ * @return Pointer to git_oid or NULL if submodule is not checked out.
+ */
+GIT_EXTERN(const git_oid *) git_submodule_wd_id(git_submodule *submodule);
+
+/**
+ * Get the ignore rule that will be used for the submodule.
+ *
+ * These values control the behavior of `git_submodule_status()` for this
+ * submodule. There are four ignore values:
+ *
+ * - **GIT_SUBMODULE_IGNORE_NONE** will consider any change to the contents
+ * of the submodule from a clean checkout to be dirty, including the
+ * addition of untracked files. This is the default if unspecified.
+ * - **GIT_SUBMODULE_IGNORE_UNTRACKED** examines the contents of the
+ * working tree (i.e. call `git_status_foreach()` on the submodule) but
+ * UNTRACKED files will not count as making the submodule dirty.
+ * - **GIT_SUBMODULE_IGNORE_DIRTY** means to only check if the HEAD of the
+ * submodule has moved for status. This is fast since it does not need to
+ * scan the working tree of the submodule at all.
+ * - **GIT_SUBMODULE_IGNORE_ALL** means not to open the submodule repo.
+ * The working directory will be consider clean so long as there is a
+ * checked out version present.
+ *
+ * @param submodule The submodule to check
+ * @return The current git_submodule_ignore_t valyue what will be used for
+ * this submodule.
+ */
+GIT_EXTERN(git_submodule_ignore_t) git_submodule_ignore(
+ git_submodule *submodule);
+
+/**
+ * Set the ignore rule for the submodule in the configuration
+ *
+ * This does not affect any currently-loaded instances.
+ *
+ * @param repo the repository to affect
+ * @param name the name of the submdule
+ * @param ignore The new value for the ignore rule
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_submodule_set_ignore(
+ git_repository *repo,
+ const char *name,
+ git_submodule_ignore_t ignore);
+
+/**
+ * Get the update rule that will be used for the submodule.
+ *
+ * This value controls the behavior of the `git submodule update` command.
+ * There are four useful values documented with `git_submodule_update_t`.
+ *
+ * @param submodule The submodule to check
+ * @return The current git_submodule_update_t value that will be used
+ * for this submodule.
+ */
+GIT_EXTERN(git_submodule_update_t) git_submodule_update_strategy(
+ git_submodule *submodule);
+
+/**
+ * Set the update rule for the submodule in the configuration
+ *
+ * This setting won't affect any existing instances.
+ *
+ * @param repo the repository to affect
+ * @param name the name of the submodule to configure
+ * @param update The new value to use
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_submodule_set_update(
+ git_repository *repo,
+ const char *name,
+ git_submodule_update_t update);
+
+/**
+ * Read the fetchRecurseSubmodules rule for a submodule.
+ *
+ * This accesses the submodule.<name>.fetchRecurseSubmodules value for
+ * the submodule that controls fetching behavior for the submodule.
+ *
+ * Note that at this time, libgit2 does not honor this setting and the
+ * fetch functionality current ignores submodules.
+ *
+ * @return 0 if fetchRecurseSubmodules is false, 1 if true
+ */
+GIT_EXTERN(git_submodule_recurse_t) git_submodule_fetch_recurse_submodules(
+ git_submodule *submodule);
+
+/**
+ * Set the fetchRecurseSubmodules rule for a submodule in the configuration
+ *
+ * This setting won't affect any existing instances.
+ *
+ * @param repo the repository to affect
+ * @param name the submodule to configure
+ * @param fetch_recurse_submodules Boolean value
+ * @return old value for fetchRecurseSubmodules
+ */
+GIT_EXTERN(int) git_submodule_set_fetch_recurse_submodules(
+ git_repository *repo,
+ const char *name,
+ git_submodule_recurse_t fetch_recurse_submodules);
+
+/**
+ * Copy submodule info into ".git/config" file.
+ *
+ * Just like "git submodule init", this copies information about the
+ * submodule into ".git/config". You can use the accessor functions
+ * above to alter the in-memory git_submodule object and control what
+ * is written to the config, overriding what is in .gitmodules.
+ *
+ * @param submodule The submodule to write into the superproject config
+ * @param overwrite By default, existing entries will not be overwritten,
+ * but setting this to true forces them to be updated.
+ * @return 0 on success, <0 on failure.
+ */
+GIT_EXTERN(int) git_submodule_init(git_submodule *submodule, int overwrite);
+
+/**
+ * Set up the subrepository for a submodule in preparation for clone.
+ *
+ * This function can be called to init and set up a submodule
+ * repository from a submodule in preparation to clone it from
+ * its remote.
+ *
+ * @param out Output pointer to the created git repository.
+ * @param sm The submodule to create a new subrepository from.
+ * @param use_gitlink Should the workdir contain a gitlink to
+ * the repo in .git/modules vs. repo directly in workdir.
+ * @return 0 on success, <0 on failure.
+ */
+GIT_EXTERN(int) git_submodule_repo_init(
+ git_repository **out,
+ const git_submodule *sm,
+ int use_gitlink);
+
+/**
+ * Copy submodule remote info into submodule repo.
+ *
+ * This copies the information about the submodules URL into the checked out
+ * submodule config, acting like "git submodule sync". This is useful if
+ * you have altered the URL for the submodule (or it has been altered by a
+ * fetch of upstream changes) and you need to update your local repo.
+ *
+ * @param submodule The submodule to copy.
+ * @return 0 or an error code.
+ */
+GIT_EXTERN(int) git_submodule_sync(git_submodule *submodule);
+
+/**
+ * Open the repository for a submodule.
+ *
+ * This is a newly opened repository object. The caller is responsible for
+ * calling `git_repository_free()` on it when done. Multiple calls to this
+ * function will return distinct `git_repository` objects. This will only
+ * work if the submodule is checked out into the working directory.
+ *
+ * @param repo Pointer to the submodule repo which was opened
+ * @param submodule Submodule to be opened
+ * @return 0 on success, <0 if submodule repo could not be opened.
+ */
+GIT_EXTERN(int) git_submodule_open(
+ git_repository **repo,
+ git_submodule *submodule);
+
+/**
+ * Reread submodule info from config, index, and HEAD.
+ *
+ * Call this to reread cached submodule information for this submodule if
+ * you have reason to believe that it has changed.
+ *
+ * @param submodule The submodule to reload
+ * @param force Force reload even if the data doesn't seem out of date
+ * @return 0 on success, <0 on error
+ */
+GIT_EXTERN(int) git_submodule_reload(git_submodule *submodule, int force);
+
+/**
+ * Get the status for a submodule.
+ *
+ * This looks at a submodule and tries to determine the status. It
+ * will return a combination of the `GIT_SUBMODULE_STATUS` values above.
+ * How deeply it examines the working directory to do this will depend
+ * on the `git_submodule_ignore_t` value for the submodule.
+ *
+ * @param status Combination of `GIT_SUBMODULE_STATUS` flags
+ * @param repo the repository in which to look
+ * @param name name of the submodule
+ * @param ignore the ignore rules to follow
+ * @return 0 on success, <0 on error
+ */
+GIT_EXTERN(int) git_submodule_status(
+ unsigned int *status,
+ git_repository *repo,
+ const char *name,
+ git_submodule_ignore_t ignore);
+
+/**
+ * Get the locations of submodule information.
+ *
+ * This is a bit like a very lightweight version of `git_submodule_status`.
+ * It just returns a made of the first four submodule status values (i.e.
+ * the ones like GIT_SUBMODULE_STATUS_IN_HEAD, etc) that tell you where the
+ * submodule data comes from (i.e. the HEAD commit, gitmodules file, etc.).
+ * This can be useful if you want to know if the submodule is present in the
+ * working directory at this point in time, etc.
+ *
+ * @param location_status Combination of first four `GIT_SUBMODULE_STATUS` flags
+ * @param submodule Submodule for which to get status
+ * @return 0 on success, <0 on error
+ */
+GIT_EXTERN(int) git_submodule_location(
+ unsigned int *location_status,
+ git_submodule *submodule);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/sys/alloc.h b/include/git2/sys/alloc.h
new file mode 100644
index 0000000..e7f85b8
--- /dev/null
+++ b/include/git2/sys/alloc.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_sys_git_alloc_h__
+#define INCLUDE_sys_git_alloc_h__
+
+#include "git2/common.h"
+
+GIT_BEGIN_DECL
+
+/**
+ * An instance for a custom memory allocator
+ *
+ * Setting the pointers of this structure allows the developer to implement
+ * custom memory allocators. The global memory allocator can be set by using
+ * "GIT_OPT_SET_ALLOCATOR" with the `git_libgit2_opts` function. Keep in mind
+ * that all fields need to be set to a proper function.
+ */
+typedef struct {
+ /** Allocate `n` bytes of memory */
+ void * GIT_CALLBACK(gmalloc)(size_t n, const char *file, int line);
+
+ /**
+ * This function shall deallocate the old object `ptr` and return a
+ * pointer to a new object that has the size specified by `size`. In
+ * case `ptr` is `NULL`, a new array shall be allocated.
+ */
+ void * GIT_CALLBACK(grealloc)(void *ptr, size_t size, const char *file, int line);
+
+ /**
+ * This function shall free the memory pointed to by `ptr`. In case
+ * `ptr` is `NULL`, this shall be a no-op.
+ */
+ void GIT_CALLBACK(gfree)(void *ptr);
+} git_allocator;
+
+/**
+ * Initialize the allocator structure to use the `stdalloc` pointer.
+ *
+ * Set up the structure so that all of its members are using the standard
+ * "stdalloc" allocator functions. The structure can then be used with
+ * `git_allocator_setup`.
+ *
+ * @param allocator The allocator that is to be initialized.
+ * @return An error code or 0.
+ */
+int git_stdalloc_init_allocator(git_allocator *allocator);
+
+/**
+ * Initialize the allocator structure to use the `crtdbg` pointer.
+ *
+ * Set up the structure so that all of its members are using the "crtdbg"
+ * allocator functions. Note that this allocator is only available on Windows
+ * platforms and only if libgit2 is being compiled with "-DMSVC_CRTDBG".
+ *
+ * @param allocator The allocator that is to be initialized.
+ * @return An error code or 0.
+ */
+int git_win32_crtdbg_init_allocator(git_allocator *allocator);
+
+GIT_END_DECL
+
+#endif
diff --git a/include/git2/sys/commit.h b/include/git2/sys/commit.h
new file mode 100644
index 0000000..ba67106
--- /dev/null
+++ b/include/git2/sys/commit.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_sys_git_commit_h__
+#define INCLUDE_sys_git_commit_h__
+
+#include "git2/common.h"
+#include "git2/types.h"
+#include "git2/oid.h"
+
+/**
+ * @file git2/sys/commit.h
+ * @brief Low-level Git commit creation
+ * @defgroup git_backend Git custom backend APIs
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Create new commit in the repository from a list of `git_oid` values.
+ *
+ * See documentation for `git_commit_create()` for information about the
+ * parameters, as the meaning is identical excepting that `tree` and
+ * `parents` now take `git_oid`. This is a dangerous API in that nor
+ * the `tree`, neither the `parents` list of `git_oid`s are checked for
+ * validity.
+ *
+ * @see git_commit_create
+ */
+GIT_EXTERN(int) git_commit_create_from_ids(
+ git_oid *id,
+ git_repository *repo,
+ const char *update_ref,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *message_encoding,
+ const char *message,
+ const git_oid *tree,
+ size_t parent_count,
+ const git_oid *parents[]);
+
+/**
+ * Callback function to return parents for commit.
+ *
+ * This is invoked with the count of the number of parents processed so far
+ * along with the user supplied payload. This should return a git_oid of
+ * the next parent or NULL if all parents have been provided.
+ */
+typedef const git_oid * GIT_CALLBACK(git_commit_parent_callback)(size_t idx, void *payload);
+
+/**
+ * Create a new commit in the repository with an callback to supply parents.
+ *
+ * See documentation for `git_commit_create()` for information about the
+ * parameters, as the meaning is identical excepting that `tree` takes a
+ * `git_oid` and doesn't check for validity, and `parent_cb` is invoked
+ * with `parent_payload` and should return `git_oid` values or NULL to
+ * indicate that all parents are accounted for.
+ *
+ * @see git_commit_create
+ */
+GIT_EXTERN(int) git_commit_create_from_callback(
+ git_oid *id,
+ git_repository *repo,
+ const char *update_ref,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *message_encoding,
+ const char *message,
+ const git_oid *tree,
+ git_commit_parent_callback parent_cb,
+ void *parent_payload);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/sys/commit_graph.h b/include/git2/sys/commit_graph.h
new file mode 100644
index 0000000..06e045f
--- /dev/null
+++ b/include/git2/sys/commit_graph.h
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_sys_git_commit_graph_h__
+#define INCLUDE_sys_git_commit_graph_h__
+
+#include "git2/common.h"
+#include "git2/types.h"
+
+/**
+ * @file git2/sys/commit_graph.h
+ * @brief Git commit-graph
+ * @defgroup git_commit_graph Git commit-graph APIs
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Opens a `git_commit_graph` from a path to an objects directory.
+ *
+ * This finds, opens, and validates the `commit-graph` file.
+ *
+ * @param cgraph_out the `git_commit_graph` struct to initialize.
+ * @param objects_dir the path to a git objects directory.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_commit_graph_open(
+ git_commit_graph **cgraph_out,
+ const char *objects_dir
+#ifdef GIT_EXPERIMENTAL_SHA256
+ , git_oid_t oid_type
+#endif
+ );
+
+/**
+ * Frees commit-graph data. This should only be called when memory allocated
+ * using `git_commit_graph_open` is not returned to libgit2 because it was not
+ * associated with the ODB through a successful call to
+ * `git_odb_set_commit_graph`.
+ *
+ * @param cgraph the commit-graph object to free. If NULL, no action is taken.
+ */
+GIT_EXTERN(void) git_commit_graph_free(git_commit_graph *cgraph);
+
+/**
+ * Create a new writer for `commit-graph` files.
+ *
+ * @param out Location to store the writer pointer.
+ * @param objects_info_dir The `objects/info` directory.
+ * The `commit-graph` file will be written in this directory.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_commit_graph_writer_new(
+ git_commit_graph_writer **out,
+ const char *objects_info_dir
+#ifdef GIT_EXPERIMENTAL_SHA256
+ , git_oid_t oid_type
+#endif
+ );
+
+/**
+ * Free the commit-graph writer and its resources.
+ *
+ * @param w The writer to free. If NULL no action is taken.
+ */
+GIT_EXTERN(void) git_commit_graph_writer_free(git_commit_graph_writer *w);
+
+/**
+ * Add an `.idx` file (associated to a packfile) to the writer.
+ *
+ * @param w The writer.
+ * @param repo The repository that owns the `.idx` file.
+ * @param idx_path The path of an `.idx` file.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_commit_graph_writer_add_index_file(
+ git_commit_graph_writer *w,
+ git_repository *repo,
+ const char *idx_path);
+
+/**
+ * Add a revwalk to the writer. This will add all the commits from the revwalk
+ * to the commit-graph.
+ *
+ * @param w The writer.
+ * @param walk The git_revwalk.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_commit_graph_writer_add_revwalk(
+ git_commit_graph_writer *w,
+ git_revwalk *walk);
+
+
+/**
+ * The strategy to use when adding a new set of commits to a pre-existing
+ * commit-graph chain.
+ */
+typedef enum {
+ /**
+ * Do not split commit-graph files. The other split strategy-related option
+ * fields are ignored.
+ */
+ GIT_COMMIT_GRAPH_SPLIT_STRATEGY_SINGLE_FILE = 0
+} git_commit_graph_split_strategy_t;
+
+/**
+ * Options structure for
+ * `git_commit_graph_writer_commit`/`git_commit_graph_writer_dump`.
+ *
+ * Initialize with `GIT_COMMIT_GRAPH_WRITER_OPTIONS_INIT`. Alternatively, you
+ * can use `git_commit_graph_writer_options_init`.
+ */
+typedef struct {
+ unsigned int version;
+
+ /**
+ * The strategy to use when adding new commits to a pre-existing commit-graph
+ * chain.
+ */
+ git_commit_graph_split_strategy_t split_strategy;
+
+ /**
+ * The number of commits in level N is less than X times the number of
+ * commits in level N + 1. Default is 2.
+ */
+ float size_multiple;
+
+ /**
+ * The number of commits in level N + 1 is more than C commits.
+ * Default is 64000.
+ */
+ size_t max_commits;
+} git_commit_graph_writer_options;
+
+#define GIT_COMMIT_GRAPH_WRITER_OPTIONS_VERSION 1
+#define GIT_COMMIT_GRAPH_WRITER_OPTIONS_INIT { \
+ GIT_COMMIT_GRAPH_WRITER_OPTIONS_VERSION \
+ }
+
+/**
+ * Initialize git_commit_graph_writer_options structure
+ *
+ * Initializes a `git_commit_graph_writer_options` with default values. Equivalent to
+ * creating an instance with `GIT_COMMIT_GRAPH_WRITER_OPTIONS_INIT`.
+ *
+ * @param opts The `git_commit_graph_writer_options` struct to initialize.
+ * @param version The struct version; pass `GIT_COMMIT_GRAPH_WRITER_OPTIONS_VERSION`.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_commit_graph_writer_options_init(
+ git_commit_graph_writer_options *opts,
+ unsigned int version);
+
+/**
+ * Write a `commit-graph` file to a file.
+ *
+ * @param w The writer
+ * @param opts Pointer to git_commit_graph_writer_options struct.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_commit_graph_writer_commit(
+ git_commit_graph_writer *w,
+ git_commit_graph_writer_options *opts);
+
+/**
+ * Dump the contents of the `commit-graph` to an in-memory buffer.
+ *
+ * @param buffer Buffer where to store the contents of the `commit-graph`.
+ * @param w The writer.
+ * @param opts Pointer to git_commit_graph_writer_options struct.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_commit_graph_writer_dump(
+ git_buf *buffer,
+ git_commit_graph_writer *w,
+ git_commit_graph_writer_options *opts);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/sys/config.h b/include/git2/sys/config.h
new file mode 100644
index 0000000..0a9005e
--- /dev/null
+++ b/include/git2/sys/config.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_sys_git_config_backend_h__
+#define INCLUDE_sys_git_config_backend_h__
+
+#include "git2/common.h"
+#include "git2/types.h"
+#include "git2/config.h"
+
+/**
+ * @file git2/sys/config.h
+ * @brief Git config backend routines
+ * @defgroup git_backend Git custom backend APIs
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Every iterator must have this struct as its first element, so the
+ * API can talk to it. You'd define your iterator as
+ *
+ * struct my_iterator {
+ * git_config_iterator parent;
+ * ...
+ * }
+ *
+ * and assign `iter->parent.backend` to your `git_config_backend`.
+ */
+struct git_config_iterator {
+ git_config_backend *backend;
+ unsigned int flags;
+
+ /**
+ * Return the current entry and advance the iterator. The
+ * memory belongs to the library.
+ */
+ int GIT_CALLBACK(next)(git_config_entry **entry, git_config_iterator *iter);
+
+ /**
+ * Free the iterator
+ */
+ void GIT_CALLBACK(free)(git_config_iterator *iter);
+};
+
+/**
+ * Generic backend that implements the interface to
+ * access a configuration file
+ */
+struct git_config_backend {
+ unsigned int version;
+ /** True if this backend is for a snapshot */
+ int readonly;
+ struct git_config *cfg;
+
+ /* Open means open the file/database and parse if necessary */
+ int GIT_CALLBACK(open)(struct git_config_backend *, git_config_level_t level, const git_repository *repo);
+ int GIT_CALLBACK(get)(struct git_config_backend *, const char *key, git_config_entry **entry);
+ int GIT_CALLBACK(set)(struct git_config_backend *, const char *key, const char *value);
+ int GIT_CALLBACK(set_multivar)(git_config_backend *cfg, const char *name, const char *regexp, const char *value);
+ int GIT_CALLBACK(del)(struct git_config_backend *, const char *key);
+ int GIT_CALLBACK(del_multivar)(struct git_config_backend *, const char *key, const char *regexp);
+ int GIT_CALLBACK(iterator)(git_config_iterator **, struct git_config_backend *);
+ /** Produce a read-only version of this backend */
+ int GIT_CALLBACK(snapshot)(struct git_config_backend **, struct git_config_backend *);
+ /**
+ * Lock this backend.
+ *
+ * Prevent any writes to the data store backing this
+ * backend. Any updates must not be visible to any other
+ * readers.
+ */
+ int GIT_CALLBACK(lock)(struct git_config_backend *);
+ /**
+ * Unlock the data store backing this backend. If success is
+ * true, the changes should be committed, otherwise rolled
+ * back.
+ */
+ int GIT_CALLBACK(unlock)(struct git_config_backend *, int success);
+ void GIT_CALLBACK(free)(struct git_config_backend *);
+};
+#define GIT_CONFIG_BACKEND_VERSION 1
+#define GIT_CONFIG_BACKEND_INIT {GIT_CONFIG_BACKEND_VERSION}
+
+/**
+ * Initializes a `git_config_backend` with default values. Equivalent to
+ * creating an instance with GIT_CONFIG_BACKEND_INIT.
+ *
+ * @param backend the `git_config_backend` struct to initialize.
+ * @param version Version of struct; pass `GIT_CONFIG_BACKEND_VERSION`
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_config_init_backend(
+ git_config_backend *backend,
+ unsigned int version);
+
+/**
+ * Add a generic config file instance to an existing config
+ *
+ * Note that the configuration object will free the file
+ * automatically.
+ *
+ * Further queries on this config object will access each
+ * of the config file instances in order (instances with
+ * a higher priority level will be accessed first).
+ *
+ * @param cfg the configuration to add the file to
+ * @param file the configuration file (backend) to add
+ * @param level the priority level of the backend
+ * @param repo optional repository to allow parsing of
+ * conditional includes
+ * @param force if a config file already exists for the given
+ * priority level, replace it
+ * @return 0 on success, GIT_EEXISTS when adding more than one file
+ * for a given priority level (and force_replace set to 0), or error code
+ */
+GIT_EXTERN(int) git_config_add_backend(
+ git_config *cfg,
+ git_config_backend *file,
+ git_config_level_t level,
+ const git_repository *repo,
+ int force);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/sys/cred.h b/include/git2/sys/cred.h
new file mode 100644
index 0000000..4d2a59a
--- /dev/null
+++ b/include/git2/sys/cred.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_sys_git_cred_h__
+#define INCLUDE_sys_git_cred_h__
+
+/* These declarations have moved. */
+#ifndef GIT_DEPRECATE_HARD
+# include "git2/sys/credential.h"
+#endif
+
+#endif
diff --git a/include/git2/sys/credential.h b/include/git2/sys/credential.h
new file mode 100644
index 0000000..bb4c9f9
--- /dev/null
+++ b/include/git2/sys/credential.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_sys_git_credential_h__
+#define INCLUDE_sys_git_credential_h__
+
+#include "git2/common.h"
+#include "git2/credential.h"
+
+/**
+ * @file git2/sys/cred.h
+ * @brief Git credentials low-level implementation
+ * @defgroup git_credential Git credentials low-level implementation
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * The base structure for all credential types
+ */
+struct git_credential {
+ git_credential_t credtype; /**< A type of credential */
+
+ /** The deallocator for this type of credentials */
+ void GIT_CALLBACK(free)(git_credential *cred);
+};
+
+/** A plaintext username and password */
+struct git_credential_userpass_plaintext {
+ git_credential parent; /**< The parent credential */
+ char *username; /**< The username to authenticate as */
+ char *password; /**< The password to use */
+};
+
+/** Username-only credential information */
+struct git_credential_username {
+ git_credential parent; /**< The parent credential */
+ char username[1]; /**< The username to authenticate as */
+};
+
+/**
+ * A ssh key from disk
+ */
+struct git_credential_ssh_key {
+ git_credential parent; /**< The parent credential */
+ char *username; /**< The username to authenticate as */
+ char *publickey; /**< The path to a public key */
+ char *privatekey; /**< The path to a private key */
+ char *passphrase; /**< Passphrase to decrypt the private key */
+};
+
+/**
+ * Keyboard-interactive based ssh authentication
+ */
+struct git_credential_ssh_interactive {
+ git_credential parent; /**< The parent credential */
+ char *username; /**< The username to authenticate as */
+
+ /**
+ * Callback used for authentication.
+ */
+ git_credential_ssh_interactive_cb prompt_callback;
+
+ void *payload; /**< Payload passed to prompt_callback */
+};
+
+/**
+ * A key with a custom signature function
+ */
+struct git_credential_ssh_custom {
+ git_credential parent; /**< The parent credential */
+ char *username; /**< The username to authenticate as */
+ char *publickey; /**< The public key data */
+ size_t publickey_len; /**< Length of the public key */
+
+ /**
+ * Callback used to sign the data.
+ */
+ git_credential_sign_cb sign_callback;
+
+ void *payload; /**< Payload passed to prompt_callback */
+};
+
+GIT_END_DECL
+
+#endif
diff --git a/include/git2/sys/diff.h b/include/git2/sys/diff.h
new file mode 100644
index 0000000..aefd7b9
--- /dev/null
+++ b/include/git2/sys/diff.h
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_sys_git_diff_h__
+#define INCLUDE_sys_git_diff_h__
+
+#include "git2/common.h"
+#include "git2/types.h"
+#include "git2/oid.h"
+#include "git2/diff.h"
+#include "git2/status.h"
+
+/**
+ * @file git2/sys/diff.h
+ * @brief Low-level Git diff utilities
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Diff print callback that writes to a git_buf.
+ *
+ * This function is provided not for you to call it directly, but instead
+ * so you can use it as a function pointer to the `git_diff_print` or
+ * `git_patch_print` APIs. When using those APIs, you specify a callback
+ * to actually handle the diff and/or patch data.
+ *
+ * Use this callback to easily write that data to a `git_buf` buffer. You
+ * must pass a `git_buf *` value as the payload to the `git_diff_print`
+ * and/or `git_patch_print` function. The data will be appended to the
+ * buffer (after any existing content).
+ */
+GIT_EXTERN(int) git_diff_print_callback__to_buf(
+ const git_diff_delta *delta,
+ const git_diff_hunk *hunk,
+ const git_diff_line *line,
+ void *payload); /**< payload must be a `git_buf *` */
+
+/**
+ * Diff print callback that writes to stdio FILE handle.
+ *
+ * This function is provided not for you to call it directly, but instead
+ * so you can use it as a function pointer to the `git_diff_print` or
+ * `git_patch_print` APIs. When using those APIs, you specify a callback
+ * to actually handle the diff and/or patch data.
+ *
+ * Use this callback to easily write that data to a stdio FILE handle. You
+ * must pass a `FILE *` value (such as `stdout` or `stderr` or the return
+ * value from `fopen()`) as the payload to the `git_diff_print`
+ * and/or `git_patch_print` function. If you pass NULL, this will write
+ * data to `stdout`.
+ */
+GIT_EXTERN(int) git_diff_print_callback__to_file_handle(
+ const git_diff_delta *delta,
+ const git_diff_hunk *hunk,
+ const git_diff_line *line,
+ void *payload); /**< payload must be a `FILE *` */
+
+
+/**
+ * Performance data from diffing
+ */
+typedef struct {
+ unsigned int version;
+ size_t stat_calls; /**< Number of stat() calls performed */
+ size_t oid_calculations; /**< Number of ID calculations */
+} git_diff_perfdata;
+
+#define GIT_DIFF_PERFDATA_VERSION 1
+#define GIT_DIFF_PERFDATA_INIT {GIT_DIFF_PERFDATA_VERSION,0,0}
+
+/**
+ * Get performance data for a diff object.
+ *
+ * @param out Structure to be filled with diff performance data
+ * @param diff Diff to read performance data from
+ * @return 0 for success, <0 for error
+ */
+GIT_EXTERN(int) git_diff_get_perfdata(
+ git_diff_perfdata *out, const git_diff *diff);
+
+/**
+ * Get performance data for diffs from a git_status_list
+ */
+GIT_EXTERN(int) git_status_list_get_perfdata(
+ git_diff_perfdata *out, const git_status_list *status);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/sys/email.h b/include/git2/sys/email.h
new file mode 100644
index 0000000..6f4a286
--- /dev/null
+++ b/include/git2/sys/email.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_sys_git_email_h__
+#define INCLUDE_sys_git_email_h__
+
+/**
+ * @file git2/sys/email.h
+ * @brief Advanced git email creation routines
+ * @defgroup git_email Advanced git email creation routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Create a diff for a commit in mbox format for sending via email.
+ *
+ * @param out buffer to store the e-mail patch in
+ * @param diff the changes to include in the email
+ * @param patch_idx the patch index
+ * @param patch_count the total number of patches that will be included
+ * @param commit_id the commit id for this change
+ * @param summary the commit message for this change
+ * @param body optional text to include above the diffstat
+ * @param author the person who authored this commit
+ * @param opts email creation options
+ */
+GIT_EXTERN(int) git_email_create_from_diff(
+ git_buf *out,
+ git_diff *diff,
+ size_t patch_idx,
+ size_t patch_count,
+ const git_oid *commit_id,
+ const char *summary,
+ const char *body,
+ const git_signature *author,
+ const git_email_create_options *opts);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/sys/filter.h b/include/git2/sys/filter.h
new file mode 100644
index 0000000..b375941
--- /dev/null
+++ b/include/git2/sys/filter.h
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_sys_git_filter_h__
+#define INCLUDE_sys_git_filter_h__
+
+#include "git2/filter.h"
+
+/**
+ * @file git2/sys/filter.h
+ * @brief Git filter backend and plugin routines
+ * @defgroup git_backend Git custom backend APIs
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Look up a filter by name
+ *
+ * @param name The name of the filter
+ * @return Pointer to the filter object or NULL if not found
+ */
+GIT_EXTERN(git_filter *) git_filter_lookup(const char *name);
+
+#define GIT_FILTER_CRLF "crlf"
+#define GIT_FILTER_IDENT "ident"
+
+/**
+ * This is priority that the internal CRLF filter will be registered with
+ */
+#define GIT_FILTER_CRLF_PRIORITY 0
+
+/**
+ * This is priority that the internal ident filter will be registered with
+ */
+#define GIT_FILTER_IDENT_PRIORITY 100
+
+/**
+ * This is priority to use with a custom filter to imitate a core Git
+ * filter driver, so that it will be run last on checkout and first on
+ * checkin. You do not have to use this, but it helps compatibility.
+ */
+#define GIT_FILTER_DRIVER_PRIORITY 200
+
+/**
+ * Create a new empty filter list
+ *
+ * Normally you won't use this because `git_filter_list_load` will create
+ * the filter list for you, but you can use this in combination with the
+ * `git_filter_lookup` and `git_filter_list_push` functions to assemble
+ * your own chains of filters.
+ */
+GIT_EXTERN(int) git_filter_list_new(
+ git_filter_list **out,
+ git_repository *repo,
+ git_filter_mode_t mode,
+ uint32_t options);
+
+/**
+ * Add a filter to a filter list with the given payload.
+ *
+ * Normally you won't have to do this because the filter list is created
+ * by calling the "check" function on registered filters when the filter
+ * attributes are set, but this does allow more direct manipulation of
+ * filter lists when desired.
+ *
+ * Note that normally the "check" function can set up a payload for the
+ * filter. Using this function, you can either pass in a payload if you
+ * know the expected payload format, or you can pass NULL. Some filters
+ * may fail with a NULL payload. Good luck!
+ */
+GIT_EXTERN(int) git_filter_list_push(
+ git_filter_list *fl, git_filter *filter, void *payload);
+
+/**
+ * Look up how many filters are in the list
+ *
+ * We will attempt to apply all of these filters to any data passed in,
+ * but note that the filter apply action still has the option of skipping
+ * data that is passed in (for example, the CRLF filter will skip data
+ * that appears to be binary).
+ *
+ * @param fl A filter list
+ * @return The number of filters in the list
+ */
+GIT_EXTERN(size_t) git_filter_list_length(const git_filter_list *fl);
+
+/**
+ * A filter source represents a file/blob to be processed
+ */
+typedef struct git_filter_source git_filter_source;
+
+/**
+ * Get the repository that the source data is coming from.
+ */
+GIT_EXTERN(git_repository *) git_filter_source_repo(const git_filter_source *src);
+
+/**
+ * Get the path that the source data is coming from.
+ */
+GIT_EXTERN(const char *) git_filter_source_path(const git_filter_source *src);
+
+/**
+ * Get the file mode of the source file
+ * If the mode is unknown, this will return 0
+ */
+GIT_EXTERN(uint16_t) git_filter_source_filemode(const git_filter_source *src);
+
+/**
+ * Get the OID of the source
+ * If the OID is unknown (often the case with GIT_FILTER_CLEAN) then
+ * this will return NULL.
+ */
+GIT_EXTERN(const git_oid *) git_filter_source_id(const git_filter_source *src);
+
+/**
+ * Get the git_filter_mode_t to be used
+ */
+GIT_EXTERN(git_filter_mode_t) git_filter_source_mode(const git_filter_source *src);
+
+/**
+ * Get the combination git_filter_flag_t options to be applied
+ */
+GIT_EXTERN(uint32_t) git_filter_source_flags(const git_filter_source *src);
+
+/**
+ * Initialize callback on filter
+ *
+ * Specified as `filter.initialize`, this is an optional callback invoked
+ * before a filter is first used. It will be called once at most.
+ *
+ * If non-NULL, the filter's `initialize` callback will be invoked right
+ * before the first use of the filter, so you can defer expensive
+ * initialization operations (in case libgit2 is being used in a way that
+ * doesn't need the filter).
+ */
+typedef int GIT_CALLBACK(git_filter_init_fn)(git_filter *self);
+
+/**
+ * Shutdown callback on filter
+ *
+ * Specified as `filter.shutdown`, this is an optional callback invoked
+ * when the filter is unregistered or when libgit2 is shutting down. It
+ * will be called once at most and should release resources as needed.
+ * This may be called even if the `initialize` callback was not made.
+ *
+ * Typically this function will free the `git_filter` object itself.
+ */
+typedef void GIT_CALLBACK(git_filter_shutdown_fn)(git_filter *self);
+
+/**
+ * Callback to decide if a given source needs this filter
+ *
+ * Specified as `filter.check`, this is an optional callback that checks
+ * if filtering is needed for a given source.
+ *
+ * It should return 0 if the filter should be applied (i.e. success),
+ * GIT_PASSTHROUGH if the filter should not be applied, or an error code
+ * to fail out of the filter processing pipeline and return to the caller.
+ *
+ * The `attr_values` will be set to the values of any attributes given in
+ * the filter definition. See `git_filter` below for more detail.
+ *
+ * The `payload` will be a pointer to a reference payload for the filter.
+ * This will start as NULL, but `check` can assign to this pointer for
+ * later use by the `stream` callback. Note that the value should be heap
+ * allocated (not stack), so that it doesn't go away before the `stream`
+ * callback can use it. If a filter allocates and assigns a value to the
+ * `payload`, it will need a `cleanup` callback to free the payload.
+ */
+typedef int GIT_CALLBACK(git_filter_check_fn)(
+ git_filter *self,
+ void **payload, /* NULL on entry, may be set */
+ const git_filter_source *src,
+ const char **attr_values);
+
+#ifndef GIT_DEPRECATE_HARD
+/**
+ * Callback to actually perform the data filtering
+ *
+ * Specified as `filter.apply`, this is the callback that actually filters
+ * data. If it successfully writes the output, it should return 0. Like
+ * `check`, it can return GIT_PASSTHROUGH to indicate that the filter
+ * doesn't want to run. Other error codes will stop filter processing and
+ * return to the caller.
+ *
+ * The `payload` value will refer to any payload that was set by the
+ * `check` callback. It may be read from or written to as needed.
+ *
+ * @deprecated use git_filter_stream_fn
+ */
+typedef int GIT_CALLBACK(git_filter_apply_fn)(
+ git_filter *self,
+ void **payload, /* may be read and/or set */
+ git_buf *to,
+ const git_buf *from,
+ const git_filter_source *src);
+#endif
+
+/**
+ * Callback to perform the data filtering.
+ *
+ * Specified as `filter.stream`, this is a callback that filters data
+ * in a streaming manner. This function will provide a
+ * `git_writestream` that will the original data will be written to;
+ * with that data, the `git_writestream` will then perform the filter
+ * translation and stream the filtered data out to the `next` location.
+ */
+typedef int GIT_CALLBACK(git_filter_stream_fn)(
+ git_writestream **out,
+ git_filter *self,
+ void **payload,
+ const git_filter_source *src,
+ git_writestream *next);
+
+/**
+ * Callback to clean up after filtering has been applied
+ *
+ * Specified as `filter.cleanup`, this is an optional callback invoked
+ * after the filter has been applied. If the `check`, `apply`, or
+ * `stream` callbacks allocated a `payload` to keep per-source filter
+ * state, use this callback to free that payload and release resources
+ * as required.
+ */
+typedef void GIT_CALLBACK(git_filter_cleanup_fn)(
+ git_filter *self,
+ void *payload);
+
+/**
+ * Filter structure used to register custom filters.
+ *
+ * To associate extra data with a filter, allocate extra data and put the
+ * `git_filter` struct at the start of your data buffer, then cast the
+ * `self` pointer to your larger structure when your callback is invoked.
+ */
+struct git_filter {
+ /** The `version` field should be set to `GIT_FILTER_VERSION`. */
+ unsigned int version;
+
+ /**
+ * A whitespace-separated list of attribute names to check for this
+ * filter (e.g. "eol crlf text"). If the attribute name is bare, it
+ * will be simply loaded and passed to the `check` callback. If it
+ * has a value (i.e. "name=value"), the attribute must match that
+ * value for the filter to be applied. The value may be a wildcard
+ * (eg, "name=*"), in which case the filter will be invoked for any
+ * value for the given attribute name. See the attribute parameter
+ * of the `check` callback for the attribute value that was specified.
+ */
+ const char *attributes;
+
+ /** Called when the filter is first used for any file. */
+ git_filter_init_fn initialize;
+
+ /** Called when the filter is removed or unregistered from the system. */
+ git_filter_shutdown_fn shutdown;
+
+ /**
+ * Called to determine whether the filter should be invoked for a
+ * given file. If this function returns `GIT_PASSTHROUGH` then the
+ * `stream` or `apply` functions will not be invoked and the
+ * contents will be passed through unmodified.
+ */
+ git_filter_check_fn check;
+
+#ifdef GIT_DEPRECATE_HARD
+ void *reserved;
+#else
+ /**
+ * Provided for backward compatibility; this will apply the
+ * filter to the given contents in a `git_buf`. Callers should
+ * provide a `stream` function instead.
+ */
+ git_filter_apply_fn apply;
+#endif
+
+ /**
+ * Called to apply the filter, this function will provide a
+ * `git_writestream` that will the original data will be
+ * written to; with that data, the `git_writestream` will then
+ * perform the filter translation and stream the filtered data
+ * out to the `next` location.
+ */
+ git_filter_stream_fn stream;
+
+ /** Called when the system is done filtering for a file. */
+ git_filter_cleanup_fn cleanup;
+};
+
+#define GIT_FILTER_VERSION 1
+#define GIT_FILTER_INIT {GIT_FILTER_VERSION}
+
+/**
+ * Initializes a `git_filter` with default values. Equivalent to
+ * creating an instance with GIT_FILTER_INIT.
+ *
+ * @param filter the `git_filter` struct to initialize.
+ * @param version Version the struct; pass `GIT_FILTER_VERSION`
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_filter_init(git_filter *filter, unsigned int version);
+
+/**
+ * Register a filter under a given name with a given priority.
+ *
+ * As mentioned elsewhere, the initialize callback will not be invoked
+ * immediately. It is deferred until the filter is used in some way.
+ *
+ * A filter's attribute checks and `check` and `stream` (or `apply`)
+ * callbacks will be issued in order of `priority` on smudge (to
+ * workdir), and in reverse order of `priority` on clean (to odb).
+ *
+ * Two filters are preregistered with libgit2:
+ * - GIT_FILTER_CRLF with priority 0
+ * - GIT_FILTER_IDENT with priority 100
+ *
+ * Currently the filter registry is not thread safe, so any registering or
+ * deregistering of filters must be done outside of any possible usage of
+ * the filters (i.e. during application setup or shutdown).
+ *
+ * @param name A name by which the filter can be referenced. Attempting
+ * to register with an in-use name will return GIT_EEXISTS.
+ * @param filter The filter definition. This pointer will be stored as is
+ * by libgit2 so it must be a durable allocation (either static
+ * or on the heap).
+ * @param priority The priority for filter application
+ * @return 0 on successful registry, error code <0 on failure
+ */
+GIT_EXTERN(int) git_filter_register(
+ const char *name, git_filter *filter, int priority);
+
+/**
+ * Remove the filter with the given name
+ *
+ * Attempting to remove the builtin libgit2 filters is not permitted and
+ * will return an error.
+ *
+ * Currently the filter registry is not thread safe, so any registering or
+ * deregistering of filters must be done outside of any possible usage of
+ * the filters (i.e. during application setup or shutdown).
+ *
+ * @param name The name under which the filter was registered
+ * @return 0 on success, error code <0 on failure
+ */
+GIT_EXTERN(int) git_filter_unregister(const char *name);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/sys/hashsig.h b/include/git2/sys/hashsig.h
new file mode 100644
index 0000000..09c19ae
--- /dev/null
+++ b/include/git2/sys/hashsig.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_sys_hashsig_h__
+#define INCLUDE_sys_hashsig_h__
+
+#include "git2/common.h"
+
+GIT_BEGIN_DECL
+
+/**
+ * Similarity signature of arbitrary text content based on line hashes
+ */
+typedef struct git_hashsig git_hashsig;
+
+/**
+ * Options for hashsig computation
+ *
+ * The options GIT_HASHSIG_NORMAL, GIT_HASHSIG_IGNORE_WHITESPACE,
+ * GIT_HASHSIG_SMART_WHITESPACE are exclusive and should not be combined.
+ */
+typedef enum {
+ /**
+ * Use all data
+ */
+ GIT_HASHSIG_NORMAL = 0,
+
+ /**
+ * Ignore whitespace
+ */
+ GIT_HASHSIG_IGNORE_WHITESPACE = (1 << 0),
+
+ /**
+ * Ignore \r and all space after \n
+ */
+ GIT_HASHSIG_SMART_WHITESPACE = (1 << 1),
+
+ /**
+ * Allow hashing of small files
+ */
+ GIT_HASHSIG_ALLOW_SMALL_FILES = (1 << 2)
+} git_hashsig_option_t;
+
+/**
+ * Compute a similarity signature for a text buffer
+ *
+ * If you have passed the option GIT_HASHSIG_IGNORE_WHITESPACE, then the
+ * whitespace will be removed from the buffer while it is being processed,
+ * modifying the buffer in place. Sorry about that!
+ *
+ * @param out The computed similarity signature.
+ * @param buf The input buffer.
+ * @param buflen The input buffer size.
+ * @param opts The signature computation options (see above).
+ * @return 0 on success, GIT_EBUFS if the buffer doesn't contain enough data to
+ * compute a valid signature (unless GIT_HASHSIG_ALLOW_SMALL_FILES is set), or
+ * error code.
+ */
+GIT_EXTERN(int) git_hashsig_create(
+ git_hashsig **out,
+ const char *buf,
+ size_t buflen,
+ git_hashsig_option_t opts);
+
+/**
+ * Compute a similarity signature for a text file
+ *
+ * This walks through the file, only loading a maximum of 4K of file data at
+ * a time. Otherwise, it acts just like `git_hashsig_create`.
+ *
+ * @param out The computed similarity signature.
+ * @param path The path to the input file.
+ * @param opts The signature computation options (see above).
+ * @return 0 on success, GIT_EBUFS if the buffer doesn't contain enough data to
+ * compute a valid signature (unless GIT_HASHSIG_ALLOW_SMALL_FILES is set), or
+ * error code.
+ */
+GIT_EXTERN(int) git_hashsig_create_fromfile(
+ git_hashsig **out,
+ const char *path,
+ git_hashsig_option_t opts);
+
+/**
+ * Release memory for a content similarity signature
+ *
+ * @param sig The similarity signature to free.
+ */
+GIT_EXTERN(void) git_hashsig_free(git_hashsig *sig);
+
+/**
+ * Measure similarity score between two similarity signatures
+ *
+ * @param a The first similarity signature to compare.
+ * @param b The second similarity signature to compare.
+ * @return [0 to 100] on success as the similarity score, or error code.
+ */
+GIT_EXTERN(int) git_hashsig_compare(
+ const git_hashsig *a,
+ const git_hashsig *b);
+
+GIT_END_DECL
+
+#endif
diff --git a/include/git2/sys/index.h b/include/git2/sys/index.h
new file mode 100644
index 0000000..1f6d93f
--- /dev/null
+++ b/include/git2/sys/index.h
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_sys_git_index_h__
+#define INCLUDE_sys_git_index_h__
+
+#include "git2/common.h"
+#include "git2/types.h"
+
+/**
+ * @file git2/sys/index.h
+ * @brief Low-level Git index manipulation routines
+ * @defgroup git_backend Git custom backend APIs
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/** Representation of a rename conflict entry in the index. */
+typedef struct git_index_name_entry {
+ char *ancestor;
+ char *ours;
+ char *theirs;
+} git_index_name_entry;
+
+/** Representation of a resolve undo entry in the index. */
+typedef struct git_index_reuc_entry {
+ uint32_t mode[3];
+ git_oid oid[3];
+ char *path;
+} git_index_reuc_entry;
+
+/** @name Conflict Name entry functions
+ *
+ * These functions work on rename conflict entries.
+ */
+/**@{*/
+
+/**
+ * Get the count of filename conflict entries currently in the index.
+ *
+ * @param index an existing index object
+ * @return integer of count of current filename conflict entries
+ */
+GIT_EXTERN(size_t) git_index_name_entrycount(git_index *index);
+
+/**
+ * Get a filename conflict entry from the index.
+ *
+ * The returned entry is read-only and should not be modified
+ * or freed by the caller.
+ *
+ * @param index an existing index object
+ * @param n the position of the entry
+ * @return a pointer to the filename conflict entry; NULL if out of bounds
+ */
+GIT_EXTERN(const git_index_name_entry *) git_index_name_get_byindex(
+ git_index *index, size_t n);
+
+/**
+ * Record the filenames involved in a rename conflict.
+ *
+ * @param index an existing index object
+ * @param ancestor the path of the file as it existed in the ancestor
+ * @param ours the path of the file as it existed in our tree
+ * @param theirs the path of the file as it existed in their tree
+ */
+GIT_EXTERN(int) git_index_name_add(git_index *index,
+ const char *ancestor, const char *ours, const char *theirs);
+
+/**
+ * Remove all filename conflict entries.
+ *
+ * @param index an existing index object
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_index_name_clear(git_index *index);
+
+/**@}*/
+
+/** @name Resolve Undo (REUC) index entry manipulation.
+ *
+ * These functions work on the Resolve Undo index extension and contains
+ * data about the original files that led to a merge conflict.
+ */
+/**@{*/
+
+/**
+ * Get the count of resolve undo entries currently in the index.
+ *
+ * @param index an existing index object
+ * @return integer of count of current resolve undo entries
+ */
+GIT_EXTERN(size_t) git_index_reuc_entrycount(git_index *index);
+
+/**
+ * Finds the resolve undo entry that points to the given path in the Git
+ * index.
+ *
+ * @param at_pos the address to which the position of the reuc entry is written (optional)
+ * @param index an existing index object
+ * @param path path to search
+ * @return 0 if found, < 0 otherwise (GIT_ENOTFOUND)
+ */
+GIT_EXTERN(int) git_index_reuc_find(size_t *at_pos, git_index *index, const char *path);
+
+/**
+ * Get a resolve undo entry from the index.
+ *
+ * The returned entry is read-only and should not be modified
+ * or freed by the caller.
+ *
+ * @param index an existing index object
+ * @param path path to search
+ * @return the resolve undo entry; NULL if not found
+ */
+GIT_EXTERN(const git_index_reuc_entry *) git_index_reuc_get_bypath(git_index *index, const char *path);
+
+/**
+ * Get a resolve undo entry from the index.
+ *
+ * The returned entry is read-only and should not be modified
+ * or freed by the caller.
+ *
+ * @param index an existing index object
+ * @param n the position of the entry
+ * @return a pointer to the resolve undo entry; NULL if out of bounds
+ */
+GIT_EXTERN(const git_index_reuc_entry *) git_index_reuc_get_byindex(git_index *index, size_t n);
+
+/**
+ * Adds a resolve undo entry for a file based on the given parameters.
+ *
+ * The resolve undo entry contains the OIDs of files that were involved
+ * in a merge conflict after the conflict has been resolved. This allows
+ * conflicts to be re-resolved later.
+ *
+ * If there exists a resolve undo entry for the given path in the index,
+ * it will be removed.
+ *
+ * This method will fail in bare index instances.
+ *
+ * @param index an existing index object
+ * @param path filename to add
+ * @param ancestor_mode mode of the ancestor file
+ * @param ancestor_id oid of the ancestor file
+ * @param our_mode mode of our file
+ * @param our_id oid of our file
+ * @param their_mode mode of their file
+ * @param their_id oid of their file
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_index_reuc_add(git_index *index, const char *path,
+ int ancestor_mode, const git_oid *ancestor_id,
+ int our_mode, const git_oid *our_id,
+ int their_mode, const git_oid *their_id);
+
+/**
+ * Remove an resolve undo entry from the index
+ *
+ * @param index an existing index object
+ * @param n position of the resolve undo entry to remove
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_index_reuc_remove(git_index *index, size_t n);
+
+/**
+ * Remove all resolve undo entries from the index
+ *
+ * @param index an existing index object
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_index_reuc_clear(git_index *index);
+
+/**@}*/
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/sys/mempack.h b/include/git2/sys/mempack.h
new file mode 100644
index 0000000..17da590
--- /dev/null
+++ b/include/git2/sys/mempack.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_sys_git_odb_mempack_h__
+#define INCLUDE_sys_git_odb_mempack_h__
+
+#include "git2/common.h"
+#include "git2/types.h"
+#include "git2/oid.h"
+#include "git2/odb.h"
+#include "git2/buffer.h"
+
+/**
+ * @file git2/sys/mempack.h
+ * @brief Custom ODB backend that permits packing objects in-memory
+ * @defgroup git_backend Git custom backend APIs
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Instantiate a new mempack backend.
+ *
+ * The backend must be added to an existing ODB with the highest
+ * priority.
+ *
+ * git_mempack_new(&mempacker);
+ * git_repository_odb(&odb, repository);
+ * git_odb_add_backend(odb, mempacker, 999);
+ *
+ * Once the backend has been loaded, all writes to the ODB will
+ * instead be queued in memory, and can be finalized with
+ * `git_mempack_dump`.
+ *
+ * Subsequent reads will also be served from the in-memory store
+ * to ensure consistency, until the memory store is dumped.
+ *
+ * @param out Pointer where to store the ODB backend
+ * @return 0 on success; error code otherwise
+ */
+GIT_EXTERN(int) git_mempack_new(git_odb_backend **out);
+
+/**
+ * Dump all the queued in-memory writes to a packfile.
+ *
+ * The contents of the packfile will be stored in the given buffer.
+ * It is the caller's responsibility to ensure that the generated
+ * packfile is available to the repository (e.g. by writing it
+ * to disk, or doing something crazy like distributing it across
+ * several copies of the repository over a network).
+ *
+ * Once the generated packfile is available to the repository,
+ * call `git_mempack_reset` to cleanup the memory store.
+ *
+ * Calling `git_mempack_reset` before the packfile has been
+ * written to disk will result in an inconsistent repository
+ * (the objects in the memory store won't be accessible).
+ *
+ * @param pack Buffer where to store the raw packfile
+ * @param repo The active repository where the backend is loaded
+ * @param backend The mempack backend
+ * @return 0 on success; error code otherwise
+ */
+GIT_EXTERN(int) git_mempack_dump(git_buf *pack, git_repository *repo, git_odb_backend *backend);
+
+/**
+ * Reset the memory packer by clearing all the queued objects.
+ *
+ * This assumes that `git_mempack_dump` has been called before to
+ * store all the queued objects into a single packfile.
+ *
+ * Alternatively, call `reset` without a previous dump to "undo"
+ * all the recently written objects, giving transaction-like
+ * semantics to the Git repository.
+ *
+ * @param backend The mempack backend
+ * @return 0 on success; error code otherwise
+ */
+GIT_EXTERN(int) git_mempack_reset(git_odb_backend *backend);
+
+GIT_END_DECL
+
+#endif
diff --git a/include/git2/sys/merge.h b/include/git2/sys/merge.h
new file mode 100644
index 0000000..ef4bc5a
--- /dev/null
+++ b/include/git2/sys/merge.h
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_sys_git_merge_h__
+#define INCLUDE_sys_git_merge_h__
+
+#include "git2/common.h"
+#include "git2/types.h"
+#include "git2/index.h"
+#include "git2/merge.h"
+
+/**
+ * @file git2/sys/merge.h
+ * @brief Git merge driver backend and plugin routines
+ * @defgroup git_merge Git merge driver APIs
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+typedef struct git_merge_driver git_merge_driver;
+
+/**
+ * Look up a merge driver by name
+ *
+ * @param name The name of the merge driver
+ * @return Pointer to the merge driver object or NULL if not found
+ */
+GIT_EXTERN(git_merge_driver *) git_merge_driver_lookup(const char *name);
+
+#define GIT_MERGE_DRIVER_TEXT "text"
+#define GIT_MERGE_DRIVER_BINARY "binary"
+#define GIT_MERGE_DRIVER_UNION "union"
+
+/**
+ * A merge driver source represents the file to be merged
+ */
+typedef struct git_merge_driver_source git_merge_driver_source;
+
+/** Get the repository that the source data is coming from. */
+GIT_EXTERN(git_repository *) git_merge_driver_source_repo(
+ const git_merge_driver_source *src);
+
+/** Gets the ancestor of the file to merge. */
+GIT_EXTERN(const git_index_entry *) git_merge_driver_source_ancestor(
+ const git_merge_driver_source *src);
+
+/** Gets the ours side of the file to merge. */
+GIT_EXTERN(const git_index_entry *) git_merge_driver_source_ours(
+ const git_merge_driver_source *src);
+
+/** Gets the theirs side of the file to merge. */
+GIT_EXTERN(const git_index_entry *) git_merge_driver_source_theirs(
+ const git_merge_driver_source *src);
+
+/** Gets the merge file options that the merge was invoked with */
+GIT_EXTERN(const git_merge_file_options *) git_merge_driver_source_file_options(
+ const git_merge_driver_source *src);
+
+
+/**
+ * Initialize callback on merge driver
+ *
+ * Specified as `driver.initialize`, this is an optional callback invoked
+ * before a merge driver is first used. It will be called once at most
+ * per library lifetime.
+ *
+ * If non-NULL, the merge driver's `initialize` callback will be invoked
+ * right before the first use of the driver, so you can defer expensive
+ * initialization operations (in case libgit2 is being used in a way that
+ * doesn't need the merge driver).
+ */
+typedef int GIT_CALLBACK(git_merge_driver_init_fn)(git_merge_driver *self);
+
+/**
+ * Shutdown callback on merge driver
+ *
+ * Specified as `driver.shutdown`, this is an optional callback invoked
+ * when the merge driver is unregistered or when libgit2 is shutting down.
+ * It will be called once at most and should release resources as needed.
+ * This may be called even if the `initialize` callback was not made.
+ *
+ * Typically this function will free the `git_merge_driver` object itself.
+ */
+typedef void GIT_CALLBACK(git_merge_driver_shutdown_fn)(git_merge_driver *self);
+
+/**
+ * Callback to perform the merge.
+ *
+ * Specified as `driver.apply`, this is the callback that actually does the
+ * merge. If it can successfully perform a merge, it should populate
+ * `path_out` with a pointer to the filename to accept, `mode_out` with
+ * the resultant mode, and `merged_out` with the buffer of the merged file
+ * and then return 0. If the driver returns `GIT_PASSTHROUGH`, then the
+ * default merge driver should instead be run. It can also return
+ * `GIT_EMERGECONFLICT` if the driver is not able to produce a merge result,
+ * and the file will remain conflicted. Any other errors will fail and
+ * return to the caller.
+ *
+ * The `filter_name` contains the name of the filter that was invoked, as
+ * specified by the file's attributes.
+ *
+ * The `src` contains the data about the file to be merged.
+ */
+typedef int GIT_CALLBACK(git_merge_driver_apply_fn)(
+ git_merge_driver *self,
+ const char **path_out,
+ uint32_t *mode_out,
+ git_buf *merged_out,
+ const char *filter_name,
+ const git_merge_driver_source *src);
+
+/**
+ * Merge driver structure used to register custom merge drivers.
+ *
+ * To associate extra data with a driver, allocate extra data and put the
+ * `git_merge_driver` struct at the start of your data buffer, then cast
+ * the `self` pointer to your larger structure when your callback is invoked.
+ */
+struct git_merge_driver {
+ /** The `version` should be set to `GIT_MERGE_DRIVER_VERSION`. */
+ unsigned int version;
+
+ /** Called when the merge driver is first used for any file. */
+ git_merge_driver_init_fn initialize;
+
+ /** Called when the merge driver is unregistered from the system. */
+ git_merge_driver_shutdown_fn shutdown;
+
+ /**
+ * Called to merge the contents of a conflict. If this function
+ * returns `GIT_PASSTHROUGH` then the default (`text`) merge driver
+ * will instead be invoked. If this function returns
+ * `GIT_EMERGECONFLICT` then the file will remain conflicted.
+ */
+ git_merge_driver_apply_fn apply;
+};
+
+#define GIT_MERGE_DRIVER_VERSION 1
+
+/**
+ * Register a merge driver under a given name.
+ *
+ * As mentioned elsewhere, the initialize callback will not be invoked
+ * immediately. It is deferred until the driver is used in some way.
+ *
+ * Currently the merge driver registry is not thread safe, so any
+ * registering or deregistering of merge drivers must be done outside of
+ * any possible usage of the drivers (i.e. during application setup or
+ * shutdown).
+ *
+ * @param name The name of this driver to match an attribute. Attempting
+ * to register with an in-use name will return GIT_EEXISTS.
+ * @param driver The merge driver definition. This pointer will be stored
+ * as is by libgit2 so it must be a durable allocation (either
+ * static or on the heap).
+ * @return 0 on successful registry, error code <0 on failure
+ */
+GIT_EXTERN(int) git_merge_driver_register(
+ const char *name, git_merge_driver *driver);
+
+/**
+ * Remove the merge driver with the given name.
+ *
+ * Attempting to remove the builtin libgit2 merge drivers is not permitted
+ * and will return an error.
+ *
+ * Currently the merge driver registry is not thread safe, so any
+ * registering or deregistering of drivers must be done outside of any
+ * possible usage of the drivers (i.e. during application setup or shutdown).
+ *
+ * @param name The name under which the merge driver was registered
+ * @return 0 on success, error code <0 on failure
+ */
+GIT_EXTERN(int) git_merge_driver_unregister(const char *name);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/sys/midx.h b/include/git2/sys/midx.h
new file mode 100644
index 0000000..3a87484
--- /dev/null
+++ b/include/git2/sys/midx.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_sys_git_midx_h__
+#define INCLUDE_sys_git_midx_h__
+
+#include "git2/common.h"
+#include "git2/types.h"
+
+/**
+ * @file git2/midx.h
+ * @brief Git multi-pack-index routines
+ * @defgroup git_midx Git multi-pack-index routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Create a new writer for `multi-pack-index` files.
+ *
+ * @param out location to store the writer pointer.
+ * @param pack_dir the directory where the `.pack` and `.idx` files are. The
+ * `multi-pack-index` file will be written in this directory, too.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_midx_writer_new(
+ git_midx_writer **out,
+ const char *pack_dir
+#ifdef GIT_EXPERIMENTAL_SHA256
+ , git_oid_t oid_type
+#endif
+ );
+
+/**
+ * Free the multi-pack-index writer and its resources.
+ *
+ * @param w the writer to free. If NULL no action is taken.
+ */
+GIT_EXTERN(void) git_midx_writer_free(git_midx_writer *w);
+
+/**
+ * Add an `.idx` file to the writer.
+ *
+ * @param w the writer
+ * @param idx_path the path of an `.idx` file.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_midx_writer_add(
+ git_midx_writer *w,
+ const char *idx_path);
+
+/**
+ * Write a `multi-pack-index` file to a file.
+ *
+ * @param w the writer
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_midx_writer_commit(
+ git_midx_writer *w);
+
+/**
+ * Dump the contents of the `multi-pack-index` to an in-memory buffer.
+ *
+ * @param midx Buffer where to store the contents of the `multi-pack-index`.
+ * @param w the writer
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_midx_writer_dump(
+ git_buf *midx,
+ git_midx_writer *w);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/sys/odb_backend.h b/include/git2/sys/odb_backend.h
new file mode 100644
index 0000000..c42abd3
--- /dev/null
+++ b/include/git2/sys/odb_backend.h
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_sys_git_odb_backend_h__
+#define INCLUDE_sys_git_odb_backend_h__
+
+#include "git2/common.h"
+#include "git2/types.h"
+#include "git2/oid.h"
+#include "git2/odb.h"
+
+/**
+ * @file git2/sys/backend.h
+ * @brief Git custom backend implementors functions
+ * @defgroup git_backend Git custom backend APIs
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * An instance for a custom backend
+ */
+struct git_odb_backend {
+ unsigned int version;
+ git_odb *odb;
+
+ /* read and read_prefix each return to libgit2 a buffer which
+ * will be freed later. The buffer should be allocated using
+ * the function git_odb_backend_data_alloc to ensure that libgit2
+ * can safely free it later. */
+ int GIT_CALLBACK(read)(
+ void **, size_t *, git_object_t *, git_odb_backend *, const git_oid *);
+
+ /* To find a unique object given a prefix of its oid. The oid given
+ * must be so that the remaining (GIT_OID_SHA1_HEXSIZE - len)*4 bits are 0s.
+ */
+ int GIT_CALLBACK(read_prefix)(
+ git_oid *, void **, size_t *, git_object_t *,
+ git_odb_backend *, const git_oid *, size_t);
+
+ int GIT_CALLBACK(read_header)(
+ size_t *, git_object_t *, git_odb_backend *, const git_oid *);
+
+ /**
+ * Write an object into the backend. The id of the object has
+ * already been calculated and is passed in.
+ */
+ int GIT_CALLBACK(write)(
+ git_odb_backend *, const git_oid *, const void *, size_t, git_object_t);
+
+ int GIT_CALLBACK(writestream)(
+ git_odb_stream **, git_odb_backend *, git_object_size_t, git_object_t);
+
+ int GIT_CALLBACK(readstream)(
+ git_odb_stream **, size_t *, git_object_t *,
+ git_odb_backend *, const git_oid *);
+
+ int GIT_CALLBACK(exists)(
+ git_odb_backend *, const git_oid *);
+
+ int GIT_CALLBACK(exists_prefix)(
+ git_oid *, git_odb_backend *, const git_oid *, size_t);
+
+ /**
+ * If the backend implements a refreshing mechanism, it should be exposed
+ * through this endpoint. Each call to `git_odb_refresh()` will invoke it.
+ *
+ * The odb layer will automatically call this when needed on failed
+ * lookups (ie. `exists()`, `read()`, `read_header()`).
+ */
+ int GIT_CALLBACK(refresh)(git_odb_backend *);
+
+ int GIT_CALLBACK(foreach)(
+ git_odb_backend *, git_odb_foreach_cb cb, void *payload);
+
+ int GIT_CALLBACK(writepack)(
+ git_odb_writepack **, git_odb_backend *, git_odb *odb,
+ git_indexer_progress_cb progress_cb, void *progress_payload);
+
+ /**
+ * If the backend supports pack files, this will create a
+ * `multi-pack-index` file which will contain an index of all objects
+ * across all the `.pack` files.
+ */
+ int GIT_CALLBACK(writemidx)(git_odb_backend *);
+
+ /**
+ * "Freshens" an already existing object, updating its last-used
+ * time. This occurs when `git_odb_write` was called, but the
+ * object already existed (and will not be re-written). The
+ * underlying implementation may want to update last-used timestamps.
+ *
+ * If callers implement this, they should return `0` if the object
+ * exists and was freshened, and non-zero otherwise.
+ */
+ int GIT_CALLBACK(freshen)(git_odb_backend *, const git_oid *);
+
+ /**
+ * Frees any resources held by the odb (including the `git_odb_backend`
+ * itself). An odb backend implementation must provide this function.
+ */
+ void GIT_CALLBACK(free)(git_odb_backend *);
+};
+
+#define GIT_ODB_BACKEND_VERSION 1
+#define GIT_ODB_BACKEND_INIT {GIT_ODB_BACKEND_VERSION}
+
+/**
+ * Initializes a `git_odb_backend` with default values. Equivalent to
+ * creating an instance with GIT_ODB_BACKEND_INIT.
+ *
+ * @param backend the `git_odb_backend` struct to initialize.
+ * @param version Version the struct; pass `GIT_ODB_BACKEND_VERSION`
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_odb_init_backend(
+ git_odb_backend *backend,
+ unsigned int version);
+
+/**
+ * Allocate data for an ODB object. Custom ODB backends may use this
+ * to provide data back to the ODB from their read function. This
+ * memory should not be freed once it is returned to libgit2. If a
+ * custom ODB uses this function but encounters an error and does not
+ * return this data to libgit2, then they should use the corresponding
+ * git_odb_backend_data_free function.
+ *
+ * @param backend the ODB backend that is allocating this memory
+ * @param len the number of bytes to allocate
+ * @return the allocated buffer on success or NULL if out of memory
+ */
+GIT_EXTERN(void *) git_odb_backend_data_alloc(git_odb_backend *backend, size_t len);
+
+/**
+ * Frees custom allocated ODB data. This should only be called when
+ * memory allocated using git_odb_backend_data_alloc is not returned
+ * to libgit2 because the backend encountered an error in the read
+ * function after allocation and did not return this data to libgit2.
+ *
+ * @param backend the ODB backend that is freeing this memory
+ * @param data the buffer to free
+ */
+GIT_EXTERN(void) git_odb_backend_data_free(git_odb_backend *backend, void *data);
+
+
+/*
+ * Users can avoid deprecated functions by defining `GIT_DEPRECATE_HARD`.
+ */
+#ifndef GIT_DEPRECATE_HARD
+
+/**
+ * Allocate memory for an ODB object from a custom backend. This is
+ * an alias of `git_odb_backend_data_alloc` and is preserved for
+ * backward compatibility.
+ *
+ * This function is deprecated, but there is no plan to remove this
+ * function at this time.
+ *
+ * @deprecated git_odb_backend_data_alloc
+ * @see git_odb_backend_data_alloc
+ */
+GIT_EXTERN(void *) git_odb_backend_malloc(git_odb_backend *backend, size_t len);
+
+#endif
+
+GIT_END_DECL
+
+#endif
diff --git a/include/git2/sys/openssl.h b/include/git2/sys/openssl.h
new file mode 100644
index 0000000..b41c55c
--- /dev/null
+++ b/include/git2/sys/openssl.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_openssl_h__
+#define INCLUDE_git_openssl_h__
+
+#include "git2/common.h"
+
+GIT_BEGIN_DECL
+
+/**
+ * Initialize the OpenSSL locks
+ *
+ * OpenSSL requires the application to determine how it performs
+ * locking.
+ *
+ * This is a last-resort convenience function which libgit2 provides for
+ * allocating and initializing the locks as well as setting the
+ * locking function to use the system's native locking functions.
+ *
+ * The locking function will be cleared and the memory will be freed
+ * when you call git_threads_sutdown().
+ *
+ * If your programming language has an OpenSSL package/bindings, it
+ * likely sets up locking. You should very strongly prefer that over
+ * this function.
+ *
+ * @return 0 on success, -1 if there are errors or if libgit2 was not
+ * built with OpenSSL and threading support.
+ */
+GIT_EXTERN(int) git_openssl_set_locking(void);
+
+GIT_END_DECL
+#endif
+
diff --git a/include/git2/sys/path.h b/include/git2/sys/path.h
new file mode 100644
index 0000000..2a0c7e0
--- /dev/null
+++ b/include/git2/sys/path.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_sys_git_path_h__
+#define INCLUDE_sys_git_path_h__
+
+#include "git2/common.h"
+
+GIT_BEGIN_DECL
+
+/**
+ * The kinds of git-specific files we know about.
+ *
+ * The order needs to stay the same to not break the `gitfiles`
+ * array in path.c
+ */
+typedef enum {
+ /** Check for the .gitignore file */
+ GIT_PATH_GITFILE_GITIGNORE,
+ /** Check for the .gitmodules file */
+ GIT_PATH_GITFILE_GITMODULES,
+ /** Check for the .gitattributes file */
+ GIT_PATH_GITFILE_GITATTRIBUTES
+} git_path_gitfile;
+
+/**
+ * The kinds of checks to perform according to which filesystem we are trying to
+ * protect.
+ */
+typedef enum {
+ /** Do both NTFS- and HFS-specific checks */
+ GIT_PATH_FS_GENERIC,
+ /** Do NTFS-specific checks only */
+ GIT_PATH_FS_NTFS,
+ /** Do HFS-specific checks only */
+ GIT_PATH_FS_HFS
+} git_path_fs;
+
+/**
+ * Check whether a path component corresponds to a .git$SUFFIX
+ * file.
+ *
+ * As some filesystems do special things to filenames when
+ * writing files to disk, you cannot always do a plain string
+ * comparison to verify whether a file name matches an expected
+ * path or not. This function can do the comparison for you,
+ * depending on the filesystem you're on.
+ *
+ * @param path the path component to check
+ * @param pathlen the length of `path` that is to be checked
+ * @param gitfile which file to check against
+ * @param fs which filesystem-specific checks to use
+ * @return 0 in case the file does not match, a positive value if
+ * it does; -1 in case of an error
+ */
+GIT_EXTERN(int) git_path_is_gitfile(const char *path, size_t pathlen, git_path_gitfile gitfile, git_path_fs fs);
+
+GIT_END_DECL
+
+#endif /* INCLUDE_sys_git_path */
diff --git a/include/git2/sys/refdb_backend.h b/include/git2/sys/refdb_backend.h
new file mode 100644
index 0000000..c31e26d
--- /dev/null
+++ b/include/git2/sys/refdb_backend.h
@@ -0,0 +1,361 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_sys_git_refdb_backend_h__
+#define INCLUDE_sys_git_refdb_backend_h__
+
+#include "git2/common.h"
+#include "git2/types.h"
+#include "git2/oid.h"
+
+/**
+ * @file git2/refdb_backend.h
+ * @brief Git custom refs backend functions
+ * @defgroup git_refdb_backend Git custom refs backend API
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+
+/**
+ * Every backend's iterator must have a pointer to itself as the first
+ * element, so the API can talk to it. You'd define your iterator as
+ *
+ * struct my_iterator {
+ * git_reference_iterator parent;
+ * ...
+ * }
+ *
+ * and assign `iter->parent.backend` to your `git_refdb_backend`.
+ */
+struct git_reference_iterator {
+ git_refdb *db;
+
+ /**
+ * Return the current reference and advance the iterator.
+ */
+ int GIT_CALLBACK(next)(
+ git_reference **ref,
+ git_reference_iterator *iter);
+
+ /**
+ * Return the name of the current reference and advance the iterator
+ */
+ int GIT_CALLBACK(next_name)(
+ const char **ref_name,
+ git_reference_iterator *iter);
+
+ /**
+ * Free the iterator
+ */
+ void GIT_CALLBACK(free)(
+ git_reference_iterator *iter);
+};
+
+/** An instance for a custom backend */
+struct git_refdb_backend {
+ unsigned int version; /**< The backend API version */
+
+ /**
+ * Queries the refdb backend for the existence of a reference.
+ *
+ * A refdb implementation must provide this function.
+ *
+ * @arg exists The implementation shall set this to `0` if a ref does
+ * not exist, otherwise to `1`.
+ * @arg ref_name The reference's name that should be checked for
+ * existence.
+ * @return `0` on success, a negative error value code.
+ */
+ int GIT_CALLBACK(exists)(
+ int *exists,
+ git_refdb_backend *backend,
+ const char *ref_name);
+
+ /**
+ * Queries the refdb backend for a given reference.
+ *
+ * A refdb implementation must provide this function.
+ *
+ * @arg out The implementation shall set this to the allocated
+ * reference, if it could be found, otherwise to `NULL`.
+ * @arg ref_name The reference's name that should be checked for
+ * existence.
+ * @return `0` on success, `GIT_ENOTFOUND` if the reference does
+ * exist, otherwise a negative error code.
+ */
+ int GIT_CALLBACK(lookup)(
+ git_reference **out,
+ git_refdb_backend *backend,
+ const char *ref_name);
+
+ /**
+ * Allocate an iterator object for the backend.
+ *
+ * A refdb implementation must provide this function.
+ *
+ * @arg out The implementation shall set this to the allocated
+ * reference iterator. A custom structure may be used with an
+ * embedded `git_reference_iterator` structure. Both `next`
+ * and `next_name` functions of `git_reference_iterator` need
+ * to be populated.
+ * @arg glob A pattern to filter references by. If given, the iterator
+ * shall only return references that match the glob when
+ * passed to `wildmatch`.
+ * @return `0` on success, otherwise a negative error code.
+ */
+ int GIT_CALLBACK(iterator)(
+ git_reference_iterator **iter,
+ struct git_refdb_backend *backend,
+ const char *glob);
+
+ /**
+ * Writes the given reference to the refdb.
+ *
+ * A refdb implementation must provide this function.
+ *
+ * @arg ref The reference to persist. May either be a symbolic or
+ * direct reference.
+ * @arg force Whether to write the reference if a reference with the
+ * same name already exists.
+ * @arg who The person updating the reference. Shall be used to create
+ * a reflog entry.
+ * @arg message The message detailing what kind of reference update is
+ * performed. Shall be used to create a reflog entry.
+ * @arg old If not `NULL` and `force` is not set, then the
+ * implementation needs to ensure that the reference is currently at
+ * the given OID before writing the new value. If both `old`
+ * and `old_target` are `NULL`, then the reference should not
+ * exist at the point of writing.
+ * @arg old_target If not `NULL` and `force` is not set, then the
+ * implementation needs to ensure that the symbolic
+ * reference is currently at the given target before
+ * writing the new value. If both `old` and
+ * `old_target` are `NULL`, then the reference should
+ * not exist at the point of writing.
+ * @return `0` on success, otherwise a negative error code.
+ */
+ int GIT_CALLBACK(write)(git_refdb_backend *backend,
+ const git_reference *ref, int force,
+ const git_signature *who, const char *message,
+ const git_oid *old, const char *old_target);
+
+ /**
+ * Rename a reference in the refdb.
+ *
+ * A refdb implementation must provide this function.
+ *
+ * @arg out The implementation shall set this to the newly created
+ * reference or `NULL` on error.
+ * @arg old_name The current name of the reference that is to be renamed.
+ * @arg new_name The new name that the old reference shall be renamed to.
+ * @arg force Whether to write the reference if a reference with the
+ * target name already exists.
+ * @arg who The person updating the reference. Shall be used to create
+ * a reflog entry.
+ * @arg message The message detailing what kind of reference update is
+ * performed. Shall be used to create a reflog entry.
+ * @return `0` on success, otherwise a negative error code.
+ */
+ int GIT_CALLBACK(rename)(
+ git_reference **out, git_refdb_backend *backend,
+ const char *old_name, const char *new_name, int force,
+ const git_signature *who, const char *message);
+
+ /**
+ * Deletes the given reference from the refdb.
+ *
+ * If it exists, its reflog should be deleted as well.
+ *
+ * A refdb implementation must provide this function.
+ *
+ * @arg ref_name The name of the reference name that shall be deleted.
+ * @arg old_id If not `NULL` and `force` is not set, then the
+ * implementation needs to ensure that the reference is currently at
+ * the given OID before writing the new value.
+ * @arg old_target If not `NULL` and `force` is not set, then the
+ * implementation needs to ensure that the symbolic
+ * reference is currently at the given target before
+ * writing the new value.
+ * @return `0` on success, otherwise a negative error code.
+ */
+ int GIT_CALLBACK(del)(git_refdb_backend *backend, const char *ref_name, const git_oid *old_id, const char *old_target);
+
+ /**
+ * Suggests that the given refdb compress or optimize its references.
+ *
+ * This mechanism is implementation specific. For on-disk reference
+ * databases, this may pack all loose references.
+ *
+ * A refdb implementation may provide this function; if it is not
+ * provided, nothing will be done.
+ *
+ * @return `0` on success a negative error code otherwise
+ */
+ int GIT_CALLBACK(compress)(git_refdb_backend *backend);
+
+ /**
+ * Query whether a particular reference has a log (may be empty)
+ *
+ * Shall return 1 if it has a reflog, 0 it it doesn't and negative in
+ * case an error occurred.
+ *
+ * A refdb implementation must provide this function.
+ *
+ * @return `0` on success, `1` if the reflog for the given reference
+ * exists, a negative error code otherwise
+ */
+ int GIT_CALLBACK(has_log)(git_refdb_backend *backend, const char *refname);
+
+ /**
+ * Make sure a particular reference will have a reflog which
+ * will be appended to on writes.
+ *
+ * A refdb implementation must provide this function.
+ *
+ * @return `0` on success, a negative error code otherwise
+ */
+ int GIT_CALLBACK(ensure_log)(git_refdb_backend *backend, const char *refname);
+
+ /**
+ * Frees any resources held by the refdb (including the `git_refdb_backend`
+ * itself).
+ *
+ * A refdb backend implementation must provide this function.
+ */
+ void GIT_CALLBACK(free)(git_refdb_backend *backend);
+
+ /**
+ * Read the reflog for the given reference name.
+ *
+ * A refdb implementation must provide this function.
+ *
+ * @return `0` on success, a negative error code otherwise
+ */
+ int GIT_CALLBACK(reflog_read)(git_reflog **out, git_refdb_backend *backend, const char *name);
+
+ /**
+ * Write a reflog to disk.
+ *
+ * A refdb implementation must provide this function.
+ *
+ * @arg reflog The complete reference log for a given reference. Note
+ * that this may contain entries that have already been
+ * written to disk.
+ * @return `0` on success, a negative error code otherwise
+ */
+ int GIT_CALLBACK(reflog_write)(git_refdb_backend *backend, git_reflog *reflog);
+
+ /**
+ * Rename a reflog.
+ *
+ * A refdb implementation must provide this function.
+ *
+ * @arg old_name The name of old reference whose reflog shall be renamed from.
+ * @arg new_name The name of new reference whose reflog shall be renamed to.
+ * @return `0` on success, a negative error code otherwise
+ */
+ int GIT_CALLBACK(reflog_rename)(git_refdb_backend *_backend, const char *old_name, const char *new_name);
+
+ /**
+ * Remove a reflog.
+ *
+ * A refdb implementation must provide this function.
+ *
+ * @arg name The name of the reference whose reflog shall be deleted.
+ * @return `0` on success, a negative error code otherwise
+ */
+ int GIT_CALLBACK(reflog_delete)(git_refdb_backend *backend, const char *name);
+
+ /**
+ * Lock a reference.
+ *
+ * A refdb implementation may provide this function; if it is not
+ * provided, the transaction API will fail to work.
+ *
+ * @arg payload_out Opaque parameter that will be passed verbosely to
+ * `unlock`.
+ * @arg refname Reference that shall be locked.
+ * @return `0` on success, a negative error code otherwise
+ */
+ int GIT_CALLBACK(lock)(void **payload_out, git_refdb_backend *backend, const char *refname);
+
+ /**
+ * Unlock a reference.
+ *
+ * Only one of target or symbolic_target will be set.
+ * `success` will be true if the reference should be update, false if
+ * the lock must be discarded.
+ *
+ * A refdb implementation must provide this function if a `lock`
+ * implementation is provided.
+ *
+ * @arg payload The payload returned by `lock`.
+ * @arg success `1` if a reference should be updated, `2` if
+ * a reference should be deleted, `0` if the lock must be
+ * discarded.
+ * @arg update_reflog `1` in case the reflog should be updated, `0`
+ * otherwise.
+ * @arg ref The reference which should be unlocked.
+ * @arg who The person updating the reference. Shall be used to create
+ * a reflog entry in case `update_reflog` is set.
+ * @arg message The message detailing what kind of reference update is
+ * performed. Shall be used to create a reflog entry in
+ * case `update_reflog` is set.
+ * @return `0` on success, a negative error code otherwise
+ */
+ int GIT_CALLBACK(unlock)(git_refdb_backend *backend, void *payload, int success, int update_reflog,
+ const git_reference *ref, const git_signature *sig, const char *message);
+};
+
+#define GIT_REFDB_BACKEND_VERSION 1
+#define GIT_REFDB_BACKEND_INIT {GIT_REFDB_BACKEND_VERSION}
+
+/**
+ * Initializes a `git_refdb_backend` with default values. Equivalent to
+ * creating an instance with GIT_REFDB_BACKEND_INIT.
+ *
+ * @param backend the `git_refdb_backend` struct to initialize
+ * @param version Version of struct; pass `GIT_REFDB_BACKEND_VERSION`
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_refdb_init_backend(
+ git_refdb_backend *backend,
+ unsigned int version);
+
+/**
+ * Constructors for default filesystem-based refdb backend
+ *
+ * Under normal usage, this is called for you when the repository is
+ * opened / created, but you can use this to explicitly construct a
+ * filesystem refdb backend for a repository.
+ *
+ * @param backend_out Output pointer to the git_refdb_backend object
+ * @param repo Git repository to access
+ * @return 0 on success, <0 error code on failure
+ */
+GIT_EXTERN(int) git_refdb_backend_fs(
+ git_refdb_backend **backend_out,
+ git_repository *repo);
+
+/**
+ * Sets the custom backend to an existing reference DB
+ *
+ * The `git_refdb` will take ownership of the `git_refdb_backend` so you
+ * should NOT free it after calling this function.
+ *
+ * @param refdb database to add the backend to
+ * @param backend pointer to a git_refdb_backend instance
+ * @return 0 on success; error code otherwise
+ */
+GIT_EXTERN(int) git_refdb_set_backend(
+ git_refdb *refdb,
+ git_refdb_backend *backend);
+
+GIT_END_DECL
+
+#endif
diff --git a/include/git2/sys/reflog.h b/include/git2/sys/reflog.h
new file mode 100644
index 0000000..c9d0041
--- /dev/null
+++ b/include/git2/sys/reflog.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_sys_git_reflog_h__
+#define INCLUDE_sys_git_reflog_h__
+
+#include "git2/common.h"
+#include "git2/types.h"
+#include "git2/oid.h"
+
+GIT_BEGIN_DECL
+
+GIT_EXTERN(git_reflog_entry *) git_reflog_entry__alloc(void);
+GIT_EXTERN(void) git_reflog_entry__free(git_reflog_entry *entry);
+
+GIT_END_DECL
+
+#endif
diff --git a/include/git2/sys/refs.h b/include/git2/sys/refs.h
new file mode 100644
index 0000000..d2ce2e0
--- /dev/null
+++ b/include/git2/sys/refs.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_sys_git_refdb_h__
+#define INCLUDE_sys_git_refdb_h__
+
+#include "git2/common.h"
+#include "git2/types.h"
+#include "git2/oid.h"
+
+/**
+ * @file git2/sys/refs.h
+ * @brief Low-level Git ref creation
+ * @defgroup git_backend Git custom backend APIs
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Create a new direct reference from an OID.
+ *
+ * @param name the reference name
+ * @param oid the object id for a direct reference
+ * @param peel the first non-tag object's OID, or NULL
+ * @return the created git_reference or NULL on error
+ */
+GIT_EXTERN(git_reference *) git_reference__alloc(
+ const char *name,
+ const git_oid *oid,
+ const git_oid *peel);
+
+/**
+ * Create a new symbolic reference.
+ *
+ * @param name the reference name
+ * @param target the target for a symbolic reference
+ * @return the created git_reference or NULL on error
+ */
+GIT_EXTERN(git_reference *) git_reference__alloc_symbolic(
+ const char *name,
+ const char *target);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/sys/remote.h b/include/git2/sys/remote.h
new file mode 100644
index 0000000..0eae923
--- /dev/null
+++ b/include/git2/sys/remote.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_sys_git_remote_h
+#define INCLUDE_sys_git_remote_h
+
+#include "git2/remote.h"
+
+/**
+ * @file git2/sys/remote.h
+ * @brief Low-level remote functionality for custom transports
+ * @defgroup git_remote Low-level remote functionality
+ * @ingroup Git
+ * @{
+*/
+
+GIT_BEGIN_DECL
+
+typedef enum {
+ /** Remote supports fetching an advertised object by ID. */
+ GIT_REMOTE_CAPABILITY_TIP_OID = (1 << 0),
+
+ /** Remote supports fetching an individual reachable object. */
+ GIT_REMOTE_CAPABILITY_REACHABLE_OID = (1 << 1),
+} git_remote_capability_t;
+
+/**
+ * Disposes libgit2-initialized fields from a git_remote_connect_options.
+ * This should only be used for git_remote_connect_options returned by
+ * git_transport_remote_connect_options.
+ *
+ * Note that this does not free the `git_remote_connect_options` itself, just
+ * the memory pointed to by it.
+ *
+ * @param opts The `git_remote_connect_options` struct to dispose.
+ */
+GIT_EXTERN(void) git_remote_connect_options_dispose(
+ git_remote_connect_options *opts);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/sys/repository.h b/include/git2/sys/repository.h
new file mode 100644
index 0000000..892be66
--- /dev/null
+++ b/include/git2/sys/repository.h
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_sys_git_repository_h__
+#define INCLUDE_sys_git_repository_h__
+
+#include "git2/common.h"
+#include "git2/types.h"
+
+/**
+ * @file git2/sys/repository.h
+ * @brief Git repository custom implementation routines
+ * @defgroup git_backend Git custom backend APIs
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Create a new repository with neither backends nor config object
+ *
+ * Note that this is only useful if you wish to associate the repository
+ * with a non-filesystem-backed object database and config store.
+ *
+ * Caveats: since this repository has no physical location, some systems
+ * can fail to function properly: locations under $GIT_DIR, $GIT_COMMON_DIR,
+ * or $GIT_INFO_DIR are impacted.
+ *
+ * @param out The blank repository
+ * @return 0 on success, or an error code
+ */
+GIT_EXTERN(int) git_repository_new(git_repository **out);
+
+/**
+ * Reset all the internal state in a repository.
+ *
+ * This will free all the mapped memory and internal objects
+ * of the repository and leave it in a "blank" state.
+ *
+ * There's no need to call this function directly unless you're
+ * trying to aggressively cleanup the repo before its
+ * deallocation. `git_repository_free` already performs this operation
+ * before deallocating the repo.
+ *
+ * @param repo The repository to clean up
+ * @return 0 on success, or an error code
+ */
+GIT_EXTERN(int) git_repository__cleanup(git_repository *repo);
+
+/**
+ * Update the filesystem config settings for an open repository
+ *
+ * When a repository is initialized, config values are set based on the
+ * properties of the filesystem that the repository is on, such as
+ * "core.ignorecase", "core.filemode", "core.symlinks", etc. If the
+ * repository is moved to a new filesystem, these properties may no
+ * longer be correct and API calls may not behave as expected. This
+ * call reruns the phase of repository initialization that sets those
+ * properties to compensate for the current filesystem of the repo.
+ *
+ * @param repo A repository object
+ * @param recurse_submodules Should submodules be updated recursively
+ * @return 0 on success, < 0 on error
+ */
+GIT_EXTERN(int) git_repository_reinit_filesystem(
+ git_repository *repo,
+ int recurse_submodules);
+
+/**
+ * Set the configuration file for this repository
+ *
+ * This configuration file will be used for all configuration
+ * queries involving this repository.
+ *
+ * The repository will keep a reference to the config file;
+ * the user must still free the config after setting it
+ * to the repository, or it will leak.
+ *
+ * @param repo A repository object
+ * @param config A Config object
+ * @return 0 on success, or an error code
+ */
+GIT_EXTERN(int) git_repository_set_config(git_repository *repo, git_config *config);
+
+/**
+ * Set the Object Database for this repository
+ *
+ * The ODB will be used for all object-related operations
+ * involving this repository.
+ *
+ * The repository will keep a reference to the ODB; the user
+ * must still free the ODB object after setting it to the
+ * repository, or it will leak.
+ *
+ * @param repo A repository object
+ * @param odb An ODB object
+ * @return 0 on success, or an error code
+ */
+GIT_EXTERN(int) git_repository_set_odb(git_repository *repo, git_odb *odb);
+
+/**
+ * Set the Reference Database Backend for this repository
+ *
+ * The refdb will be used for all reference related operations
+ * involving this repository.
+ *
+ * The repository will keep a reference to the refdb; the user
+ * must still free the refdb object after setting it to the
+ * repository, or it will leak.
+ *
+ * @param repo A repository object
+ * @param refdb An refdb object
+ * @return 0 on success, or an error code
+ */
+GIT_EXTERN(int) git_repository_set_refdb(git_repository *repo, git_refdb *refdb);
+
+/**
+ * Set the index file for this repository
+ *
+ * This index will be used for all index-related operations
+ * involving this repository.
+ *
+ * The repository will keep a reference to the index file;
+ * the user must still free the index after setting it
+ * to the repository, or it will leak.
+ *
+ * @param repo A repository object
+ * @param index An index object
+ * @return 0 on success, or an error code
+ */
+GIT_EXTERN(int) git_repository_set_index(git_repository *repo, git_index *index);
+
+/**
+ * Set a repository to be bare.
+ *
+ * Clear the working directory and set core.bare to true. You may also
+ * want to call `git_repository_set_index(repo, NULL)` since a bare repo
+ * typically does not have an index, but this function will not do that
+ * for you.
+ *
+ * @param repo Repo to make bare
+ * @return 0 on success, <0 on failure
+ */
+GIT_EXTERN(int) git_repository_set_bare(git_repository *repo);
+
+/**
+ * Load and cache all submodules.
+ *
+ * Because the `.gitmodules` file is unstructured, loading submodules is an
+ * O(N) operation. Any operation (such as `git_rebase_init`) that requires
+ * accessing all submodules is O(N^2) in the number of submodules, if it
+ * has to look each one up individually. This function loads all submodules
+ * and caches them so that subsequent calls to `git_submodule_lookup` are O(1).
+ *
+ * @param repo the repository whose submodules will be cached.
+ */
+GIT_EXTERN(int) git_repository_submodule_cache_all(
+ git_repository *repo);
+
+/**
+ * Clear the submodule cache.
+ *
+ * Clear the submodule cache populated by `git_repository_submodule_cache_all`.
+ * If there is no cache, do nothing.
+ *
+ * The cache incorporates data from the repository's configuration, as well
+ * as the state of the working tree, the index, and HEAD. So any time any
+ * of these has changed, the cache might become invalid.
+ *
+ * @param repo the repository whose submodule cache will be cleared
+ */
+GIT_EXTERN(int) git_repository_submodule_cache_clear(
+ git_repository *repo);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/sys/stream.h b/include/git2/sys/stream.h
new file mode 100644
index 0000000..3d28d09
--- /dev/null
+++ b/include/git2/sys/stream.h
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_sys_git_stream_h__
+#define INCLUDE_sys_git_stream_h__
+
+#include "git2/common.h"
+#include "git2/types.h"
+#include "git2/proxy.h"
+
+GIT_BEGIN_DECL
+
+#define GIT_STREAM_VERSION 1
+
+/**
+ * Every stream must have this struct as its first element, so the
+ * API can talk to it. You'd define your stream as
+ *
+ * struct my_stream {
+ * git_stream parent;
+ * ...
+ * }
+ *
+ * and fill the functions
+ */
+typedef struct git_stream {
+ int version;
+
+ int encrypted : 1,
+ proxy_support : 1;
+
+ /**
+ * Timeout for read and write operations; can be set to `0` to
+ * block indefinitely.
+ */
+ int timeout;
+
+ /**
+ * Timeout to connect to the remote server; can be set to `0`
+ * to use the system defaults. This can be shorter than the
+ * system default - often 75 seconds - but cannot be longer.
+ */
+ int connect_timeout;
+
+ int GIT_CALLBACK(connect)(struct git_stream *);
+ int GIT_CALLBACK(certificate)(git_cert **, struct git_stream *);
+ int GIT_CALLBACK(set_proxy)(struct git_stream *, const git_proxy_options *proxy_opts);
+ ssize_t GIT_CALLBACK(read)(struct git_stream *, void *, size_t);
+ ssize_t GIT_CALLBACK(write)(struct git_stream *, const char *, size_t, int);
+ int GIT_CALLBACK(close)(struct git_stream *);
+ void GIT_CALLBACK(free)(struct git_stream *);
+} git_stream;
+
+typedef struct {
+ /** The `version` field should be set to `GIT_STREAM_VERSION`. */
+ int version;
+
+ /**
+ * Called to create a new connection to a given host.
+ *
+ * @param out The created stream
+ * @param host The hostname to connect to; may be a hostname or
+ * IP address
+ * @param port The port to connect to; may be a port number or
+ * service name
+ * @return 0 or an error code
+ */
+ int GIT_CALLBACK(init)(git_stream **out, const char *host, const char *port);
+
+ /**
+ * Called to create a new connection on top of the given stream. If
+ * this is a TLS stream, then this function may be used to proxy a
+ * TLS stream over an HTTP CONNECT session. If this is unset, then
+ * HTTP CONNECT proxies will not be supported.
+ *
+ * @param out The created stream
+ * @param in An existing stream to add TLS to
+ * @param host The hostname that the stream is connected to,
+ * for certificate validation
+ * @return 0 or an error code
+ */
+ int GIT_CALLBACK(wrap)(git_stream **out, git_stream *in, const char *host);
+} git_stream_registration;
+
+/**
+ * The type of stream to register.
+ */
+typedef enum {
+ /** A standard (non-TLS) socket. */
+ GIT_STREAM_STANDARD = 1,
+
+ /** A TLS-encrypted socket. */
+ GIT_STREAM_TLS = 2
+} git_stream_t;
+
+/**
+ * Register stream constructors for the library to use
+ *
+ * If a registration structure is already set, it will be overwritten.
+ * Pass `NULL` in order to deregister the current constructor and return
+ * to the system defaults.
+ *
+ * The type parameter may be a bitwise AND of types.
+ *
+ * @param type the type or types of stream to register
+ * @param registration the registration data
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_stream_register(
+ git_stream_t type, git_stream_registration *registration);
+
+#ifndef GIT_DEPRECATE_HARD
+
+/** @name Deprecated TLS Stream Registration Functions
+ *
+ * These functions are retained for backward compatibility. The newer
+ * versions of these values should be preferred in all new code.
+ *
+ * There is no plan to remove these backward compatibility values at
+ * this time.
+ */
+/**@{*/
+
+/**
+ * @deprecated Provide a git_stream_registration to git_stream_register
+ * @see git_stream_registration
+ */
+typedef int GIT_CALLBACK(git_stream_cb)(git_stream **out, const char *host, const char *port);
+
+/**
+ * Register a TLS stream constructor for the library to use. This stream
+ * will not support HTTP CONNECT proxies. This internally calls
+ * `git_stream_register` and is preserved for backward compatibility.
+ *
+ * This function is deprecated, but there is no plan to remove this
+ * function at this time.
+ *
+ * @deprecated Provide a git_stream_registration to git_stream_register
+ * @see git_stream_register
+ */
+GIT_EXTERN(int) git_stream_register_tls(git_stream_cb ctor);
+
+/**@}*/
+
+#endif
+
+GIT_END_DECL
+
+#endif
diff --git a/include/git2/sys/transport.h b/include/git2/sys/transport.h
new file mode 100644
index 0000000..370ca45
--- /dev/null
+++ b/include/git2/sys/transport.h
@@ -0,0 +1,465 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_sys_git_transport_h
+#define INCLUDE_sys_git_transport_h
+
+#include "git2/net.h"
+#include "git2/oidarray.h"
+#include "git2/proxy.h"
+#include "git2/remote.h"
+#include "git2/strarray.h"
+#include "git2/transport.h"
+#include "git2/types.h"
+
+/**
+ * @file git2/sys/transport.h
+ * @brief Git custom transport registration interfaces and functions
+ * @defgroup git_transport Git custom transport registration
+ * @ingroup Git
+ * @{
+ */
+
+GIT_BEGIN_DECL
+
+typedef struct {
+ const git_remote_head * const *refs;
+ size_t refs_len;
+ git_oid *shallow_roots;
+ size_t shallow_roots_len;
+ int depth;
+} git_fetch_negotiation;
+
+struct git_transport {
+ unsigned int version; /**< The struct version */
+
+ /**
+ * Connect the transport to the remote repository, using the given
+ * direction.
+ */
+ int GIT_CALLBACK(connect)(
+ git_transport *transport,
+ const char *url,
+ int direction,
+ const git_remote_connect_options *connect_opts);
+
+ /**
+ * Resets the connect options for the given transport. This
+ * is useful for updating settings or callbacks for an already
+ * connected transport.
+ */
+ int GIT_CALLBACK(set_connect_opts)(
+ git_transport *transport,
+ const git_remote_connect_options *connect_opts);
+
+ /**
+ * Gets the capabilities for this remote repository.
+ *
+ * This function may be called after a successful call to
+ * `connect()`.
+ */
+ int GIT_CALLBACK(capabilities)(
+ unsigned int *capabilities,
+ git_transport *transport);
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ /**
+ * Gets the object type for the remote repository.
+ *
+ * This function may be called after a successful call to
+ * `connect()`.
+ */
+ int GIT_CALLBACK(oid_type)(
+ git_oid_t *object_type,
+ git_transport *transport);
+#endif
+
+ /**
+ * Get the list of available references in the remote repository.
+ *
+ * This function may be called after a successful call to
+ * `connect()`. The array returned is owned by the transport and
+ * must be kept valid until the next call to one of its functions.
+ */
+ int GIT_CALLBACK(ls)(
+ const git_remote_head ***out,
+ size_t *size,
+ git_transport *transport);
+
+ /** Executes the push whose context is in the git_push object. */
+ int GIT_CALLBACK(push)(
+ git_transport *transport,
+ git_push *push);
+
+ /**
+ * Negotiate a fetch with the remote repository.
+ *
+ * This function may be called after a successful call to `connect()`,
+ * when the direction is GIT_DIRECTION_FETCH. The function performs a
+ * negotiation to calculate the `wants` list for the fetch.
+ */
+ int GIT_CALLBACK(negotiate_fetch)(
+ git_transport *transport,
+ git_repository *repo,
+ const git_fetch_negotiation *fetch_data);
+
+ /**
+ * Return the shallow roots of the remote.
+ *
+ * This function may be called after a successful call to
+ * `negotiate_fetch`.
+ */
+ int GIT_CALLBACK(shallow_roots)(
+ git_oidarray *out,
+ git_transport *transport);
+
+ /**
+ * Start downloading the packfile from the remote repository.
+ *
+ * This function may be called after a successful call to
+ * negotiate_fetch(), when the direction is GIT_DIRECTION_FETCH.
+ */
+ int GIT_CALLBACK(download_pack)(
+ git_transport *transport,
+ git_repository *repo,
+ git_indexer_progress *stats);
+
+ /** Checks to see if the transport is connected */
+ int GIT_CALLBACK(is_connected)(git_transport *transport);
+
+ /** Cancels any outstanding transport operation */
+ void GIT_CALLBACK(cancel)(git_transport *transport);
+
+ /**
+ * Close the connection to the remote repository.
+ *
+ * This function is the reverse of connect() -- it terminates the
+ * connection to the remote end.
+ */
+ int GIT_CALLBACK(close)(git_transport *transport);
+
+ /** Frees/destructs the git_transport object. */
+ void GIT_CALLBACK(free)(git_transport *transport);
+};
+
+#define GIT_TRANSPORT_VERSION 1
+#define GIT_TRANSPORT_INIT {GIT_TRANSPORT_VERSION}
+
+/**
+ * Initializes a `git_transport` with default values. Equivalent to
+ * creating an instance with GIT_TRANSPORT_INIT.
+ *
+ * @param opts the `git_transport` struct to initialize
+ * @param version Version of struct; pass `GIT_TRANSPORT_VERSION`
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_transport_init(
+ git_transport *opts,
+ unsigned int version);
+
+/**
+ * Function to use to create a transport from a URL. The transport database
+ * is scanned to find a transport that implements the scheme of the URI (i.e.
+ * git:// or http://) and a transport object is returned to the caller.
+ *
+ * @param out The newly created transport (out)
+ * @param owner The git_remote which will own this transport
+ * @param url The URL to connect to
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_transport_new(git_transport **out, git_remote *owner, const char *url);
+
+/**
+ * Create an ssh transport with custom git command paths
+ *
+ * This is a factory function suitable for setting as the transport
+ * callback in a remote (or for a clone in the options).
+ *
+ * The payload argument must be a strarray pointer with the paths for
+ * the `git-upload-pack` and `git-receive-pack` at index 0 and 1.
+ *
+ * @param out the resulting transport
+ * @param owner the owning remote
+ * @param payload a strarray with the paths
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_transport_ssh_with_paths(git_transport **out, git_remote *owner, void *payload);
+
+/**
+ * Add a custom transport definition, to be used in addition to the built-in
+ * set of transports that come with libgit2.
+ *
+ * The caller is responsible for synchronizing calls to git_transport_register
+ * and git_transport_unregister with other calls to the library that
+ * instantiate transports.
+ *
+ * @param prefix The scheme (ending in "://") to match, i.e. "git://"
+ * @param cb The callback used to create an instance of the transport
+ * @param param A fixed parameter to pass to cb at creation time
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_transport_register(
+ const char *prefix,
+ git_transport_cb cb,
+ void *param);
+
+/**
+ * Unregister a custom transport definition which was previously registered
+ * with git_transport_register.
+ *
+ * The caller is responsible for synchronizing calls to git_transport_register
+ * and git_transport_unregister with other calls to the library that
+ * instantiate transports.
+ *
+ * @param prefix From the previous call to git_transport_register
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_transport_unregister(
+ const char *prefix);
+
+/* Transports which come with libgit2 (match git_transport_cb). The expected
+ * value for "param" is listed in-line below. */
+
+/**
+ * Create an instance of the dummy transport.
+ *
+ * @param out The newly created transport (out)
+ * @param owner The git_remote which will own this transport
+ * @param payload You must pass NULL for this parameter.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_transport_dummy(
+ git_transport **out,
+ git_remote *owner,
+ /* NULL */ void *payload);
+
+/**
+ * Create an instance of the local transport.
+ *
+ * @param out The newly created transport (out)
+ * @param owner The git_remote which will own this transport
+ * @param payload You must pass NULL for this parameter.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_transport_local(
+ git_transport **out,
+ git_remote *owner,
+ /* NULL */ void *payload);
+
+/**
+ * Create an instance of the smart transport.
+ *
+ * @param out The newly created transport (out)
+ * @param owner The git_remote which will own this transport
+ * @param payload A pointer to a git_smart_subtransport_definition
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_transport_smart(
+ git_transport **out,
+ git_remote *owner,
+ /* (git_smart_subtransport_definition *) */ void *payload);
+
+/**
+ * Call the certificate check for this transport.
+ *
+ * @param transport a smart transport
+ * @param cert the certificate to pass to the caller
+ * @param valid whether we believe the certificate is valid
+ * @param hostname the hostname we connected to
+ * @return the return value of the callback: 0 for no error, GIT_PASSTHROUGH
+ * to indicate that there is no callback registered (or the callback
+ * refused to validate the certificate and callers should behave as
+ * if no callback was set), or < 0 for an error
+ */
+GIT_EXTERN(int) git_transport_smart_certificate_check(git_transport *transport, git_cert *cert, int valid, const char *hostname);
+
+/**
+ * Call the credentials callback for this transport
+ *
+ * @param out the pointer where the creds are to be stored
+ * @param transport a smart transport
+ * @param user the user we saw on the url (if any)
+ * @param methods available methods for authentication
+ * @return the return value of the callback: 0 for no error, GIT_PASSTHROUGH
+ * to indicate that there is no callback registered (or the callback
+ * refused to provide credentials and callers should behave as if no
+ * callback was set), or < 0 for an error
+ */
+GIT_EXTERN(int) git_transport_smart_credentials(git_credential **out, git_transport *transport, const char *user, int methods);
+
+/**
+ * Get a copy of the remote connect options
+ *
+ * All data is copied and must be freed by the caller by calling
+ * `git_remote_connect_options_dispose`.
+ *
+ * @param out options struct to fill
+ * @param transport the transport to extract the data from.
+ */
+GIT_EXTERN(int) git_transport_remote_connect_options(
+ git_remote_connect_options *out,
+ git_transport *transport);
+
+/*
+ *** End of base transport interface ***
+ *** Begin interface for subtransports for the smart transport ***
+ */
+
+/** Actions that the smart transport can ask a subtransport to perform */
+typedef enum {
+ GIT_SERVICE_UPLOADPACK_LS = 1,
+ GIT_SERVICE_UPLOADPACK = 2,
+ GIT_SERVICE_RECEIVEPACK_LS = 3,
+ GIT_SERVICE_RECEIVEPACK = 4
+} git_smart_service_t;
+
+typedef struct git_smart_subtransport git_smart_subtransport;
+typedef struct git_smart_subtransport_stream git_smart_subtransport_stream;
+
+/**
+ * A stream used by the smart transport to read and write data
+ * from a subtransport.
+ *
+ * This provides a customization point in case you need to
+ * support some other communication method.
+ */
+struct git_smart_subtransport_stream {
+ git_smart_subtransport *subtransport; /**< The owning subtransport */
+
+ /**
+ * Read available data from the stream.
+ *
+ * The implementation may read less than requested.
+ */
+ int GIT_CALLBACK(read)(
+ git_smart_subtransport_stream *stream,
+ char *buffer,
+ size_t buf_size,
+ size_t *bytes_read);
+
+ /**
+ * Write data to the stream
+ *
+ * The implementation must write all data or return an error.
+ */
+ int GIT_CALLBACK(write)(
+ git_smart_subtransport_stream *stream,
+ const char *buffer,
+ size_t len);
+
+ /** Free the stream */
+ void GIT_CALLBACK(free)(
+ git_smart_subtransport_stream *stream);
+};
+
+/**
+ * An implementation of a subtransport which carries data for the
+ * smart transport
+ */
+struct git_smart_subtransport {
+ /**
+ * Setup a subtransport stream for the requested action.
+ */
+ int GIT_CALLBACK(action)(
+ git_smart_subtransport_stream **out,
+ git_smart_subtransport *transport,
+ const char *url,
+ git_smart_service_t action);
+
+ /**
+ * Close the subtransport.
+ *
+ * Subtransports are guaranteed a call to close() between
+ * calls to action(), except for the following two "natural" progressions
+ * of actions against a constant URL:
+ *
+ * - UPLOADPACK_LS -> UPLOADPACK
+ * - RECEIVEPACK_LS -> RECEIVEPACK
+ */
+ int GIT_CALLBACK(close)(git_smart_subtransport *transport);
+
+ /** Free the subtransport */
+ void GIT_CALLBACK(free)(git_smart_subtransport *transport);
+};
+
+/** A function which creates a new subtransport for the smart transport */
+typedef int GIT_CALLBACK(git_smart_subtransport_cb)(
+ git_smart_subtransport **out,
+ git_transport *owner,
+ void *param);
+
+/**
+ * Definition for a "subtransport"
+ *
+ * The smart transport knows how to speak the git protocol, but it has no
+ * knowledge of how to establish a connection between it and another endpoint,
+ * or how to move data back and forth. For this, a subtransport interface is
+ * declared, and the smart transport delegates this work to the subtransports.
+ *
+ * Three subtransports are provided by libgit2: ssh, git, http(s).
+ *
+ * Subtransports can either be RPC = 0 (persistent connection) or RPC = 1
+ * (request/response). The smart transport handles the differences in its own
+ * logic. The git subtransport is RPC = 0, while http is RPC = 1.
+ */
+typedef struct git_smart_subtransport_definition {
+ /** The function to use to create the git_smart_subtransport */
+ git_smart_subtransport_cb callback;
+
+ /**
+ * True if the protocol is stateless; false otherwise. For example,
+ * http:// is stateless, but git:// is not.
+ */
+ unsigned rpc;
+
+ /** User-specified parameter passed to the callback */
+ void *param;
+} git_smart_subtransport_definition;
+
+/* Smart transport subtransports that come with libgit2 */
+
+/**
+ * Create an instance of the http subtransport.
+ *
+ * This subtransport also supports https.
+ *
+ * @param out The newly created subtransport
+ * @param owner The smart transport to own this subtransport
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_smart_subtransport_http(
+ git_smart_subtransport **out,
+ git_transport *owner,
+ void *param);
+
+/**
+ * Create an instance of the git subtransport.
+ *
+ * @param out The newly created subtransport
+ * @param owner The smart transport to own this subtransport
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_smart_subtransport_git(
+ git_smart_subtransport **out,
+ git_transport *owner,
+ void *param);
+
+/**
+ * Create an instance of the ssh subtransport.
+ *
+ * @param out The newly created subtransport
+ * @param owner The smart transport to own this subtransport
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_smart_subtransport_ssh(
+ git_smart_subtransport **out,
+ git_transport *owner,
+ void *param);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/tag.h b/include/git2/tag.h
new file mode 100644
index 0000000..9830536
--- /dev/null
+++ b/include/git2/tag.h
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_tag_h__
+#define INCLUDE_git_tag_h__
+
+#include "common.h"
+#include "types.h"
+#include "oid.h"
+#include "object.h"
+#include "strarray.h"
+
+/**
+ * @file git2/tag.h
+ * @brief Git tag parsing routines
+ * @defgroup git_tag Git tag management
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Lookup a tag object from the repository.
+ *
+ * @param out pointer to the looked up tag
+ * @param repo the repo to use when locating the tag.
+ * @param id identity of the tag to locate.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_tag_lookup(
+ git_tag **out, git_repository *repo, const git_oid *id);
+
+/**
+ * Lookup a tag object from the repository,
+ * given a prefix of its identifier (short id).
+ *
+ * @see git_object_lookup_prefix
+ *
+ * @param out pointer to the looked up tag
+ * @param repo the repo to use when locating the tag.
+ * @param id identity of the tag to locate.
+ * @param len the length of the short identifier
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_tag_lookup_prefix(
+ git_tag **out, git_repository *repo, const git_oid *id, size_t len);
+
+/**
+ * Close an open tag
+ *
+ * You can no longer use the git_tag pointer after this call.
+ *
+ * IMPORTANT: You MUST call this method when you are through with a tag to
+ * release memory. Failure to do so will cause a memory leak.
+ *
+ * @param tag the tag to close
+ */
+GIT_EXTERN(void) git_tag_free(git_tag *tag);
+
+/**
+ * Get the id of a tag.
+ *
+ * @param tag a previously loaded tag.
+ * @return object identity for the tag.
+ */
+GIT_EXTERN(const git_oid *) git_tag_id(const git_tag *tag);
+
+/**
+ * Get the repository that contains the tag.
+ *
+ * @param tag A previously loaded tag.
+ * @return Repository that contains this tag.
+ */
+GIT_EXTERN(git_repository *) git_tag_owner(const git_tag *tag);
+
+/**
+ * Get the tagged object of a tag
+ *
+ * This method performs a repository lookup for the
+ * given object and returns it
+ *
+ * @param target_out pointer where to store the target
+ * @param tag a previously loaded tag.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_tag_target(git_object **target_out, const git_tag *tag);
+
+/**
+ * Get the OID of the tagged object of a tag
+ *
+ * @param tag a previously loaded tag.
+ * @return pointer to the OID
+ */
+GIT_EXTERN(const git_oid *) git_tag_target_id(const git_tag *tag);
+
+/**
+ * Get the type of a tag's tagged object
+ *
+ * @param tag a previously loaded tag.
+ * @return type of the tagged object
+ */
+GIT_EXTERN(git_object_t) git_tag_target_type(const git_tag *tag);
+
+/**
+ * Get the name of a tag
+ *
+ * @param tag a previously loaded tag.
+ * @return name of the tag
+ */
+GIT_EXTERN(const char *) git_tag_name(const git_tag *tag);
+
+/**
+ * Get the tagger (author) of a tag
+ *
+ * @param tag a previously loaded tag.
+ * @return reference to the tag's author or NULL when unspecified
+ */
+GIT_EXTERN(const git_signature *) git_tag_tagger(const git_tag *tag);
+
+/**
+ * Get the message of a tag
+ *
+ * @param tag a previously loaded tag.
+ * @return message of the tag or NULL when unspecified
+ */
+GIT_EXTERN(const char *) git_tag_message(const git_tag *tag);
+
+
+/**
+ * Create a new tag in the repository from an object
+ *
+ * A new reference will also be created pointing to
+ * this tag object. If `force` is true and a reference
+ * already exists with the given name, it'll be replaced.
+ *
+ * The message will not be cleaned up. This can be achieved
+ * through `git_message_prettify()`.
+ *
+ * The tag name will be checked for validity. You must avoid
+ * the characters '~', '^', ':', '\\', '?', '[', and '*', and the
+ * sequences ".." and "@{" which have special meaning to revparse.
+ *
+ * @param oid Pointer where to store the OID of the
+ * newly created tag. If the tag already exists, this parameter
+ * will be the oid of the existing tag, and the function will
+ * return a GIT_EEXISTS error code.
+ *
+ * @param repo Repository where to store the tag
+ *
+ * @param tag_name Name for the tag; this name is validated
+ * for consistency. It should also not conflict with an
+ * already existing tag name
+ *
+ * @param target Object to which this tag points. This object
+ * must belong to the given `repo`.
+ *
+ * @param tagger Signature of the tagger for this tag, and
+ * of the tagging time
+ *
+ * @param message Full message for this tag
+ *
+ * @param force Overwrite existing references
+ *
+ * @return 0 on success, GIT_EINVALIDSPEC or an error code
+ * A tag object is written to the ODB, and a proper reference
+ * is written in the /refs/tags folder, pointing to it
+ */
+GIT_EXTERN(int) git_tag_create(
+ git_oid *oid,
+ git_repository *repo,
+ const char *tag_name,
+ const git_object *target,
+ const git_signature *tagger,
+ const char *message,
+ int force);
+
+/**
+ * Create a new tag in the object database pointing to a git_object
+ *
+ * The message will not be cleaned up. This can be achieved
+ * through `git_message_prettify()`.
+ *
+ * @param oid Pointer where to store the OID of the
+ * newly created tag
+ *
+ * @param repo Repository where to store the tag
+ *
+ * @param tag_name Name for the tag
+ *
+ * @param target Object to which this tag points. This object
+ * must belong to the given `repo`.
+ *
+ * @param tagger Signature of the tagger for this tag, and
+ * of the tagging time
+ *
+ * @param message Full message for this tag
+ *
+ * @return 0 on success or an error code
+ */
+GIT_EXTERN(int) git_tag_annotation_create(
+ git_oid *oid,
+ git_repository *repo,
+ const char *tag_name,
+ const git_object *target,
+ const git_signature *tagger,
+ const char *message);
+
+/**
+ * Create a new tag in the repository from a buffer
+ *
+ * @param oid Pointer where to store the OID of the newly created tag
+ * @param repo Repository where to store the tag
+ * @param buffer Raw tag data
+ * @param force Overwrite existing tags
+ * @return 0 on success; error code otherwise
+ */
+GIT_EXTERN(int) git_tag_create_from_buffer(
+ git_oid *oid,
+ git_repository *repo,
+ const char *buffer,
+ int force);
+
+/**
+ * Create a new lightweight tag pointing at a target object
+ *
+ * A new direct reference will be created pointing to
+ * this target object. If `force` is true and a reference
+ * already exists with the given name, it'll be replaced.
+ *
+ * The tag name will be checked for validity.
+ * See `git_tag_create()` for rules about valid names.
+ *
+ * @param oid Pointer where to store the OID of the provided
+ * target object. If the tag already exists, this parameter
+ * will be filled with the oid of the existing pointed object
+ * and the function will return a GIT_EEXISTS error code.
+ *
+ * @param repo Repository where to store the lightweight tag
+ *
+ * @param tag_name Name for the tag; this name is validated
+ * for consistency. It should also not conflict with an
+ * already existing tag name
+ *
+ * @param target Object to which this tag points. This object
+ * must belong to the given `repo`.
+ *
+ * @param force Overwrite existing references
+ *
+ * @return 0 on success, GIT_EINVALIDSPEC or an error code
+ * A proper reference is written in the /refs/tags folder,
+ * pointing to the provided target object
+ */
+GIT_EXTERN(int) git_tag_create_lightweight(
+ git_oid *oid,
+ git_repository *repo,
+ const char *tag_name,
+ const git_object *target,
+ int force);
+
+/**
+ * Delete an existing tag reference.
+ *
+ * The tag name will be checked for validity.
+ * See `git_tag_create()` for rules about valid names.
+ *
+ * @param repo Repository where lives the tag
+ *
+ * @param tag_name Name of the tag to be deleted;
+ * this name is validated for consistency.
+ *
+ * @return 0 on success, GIT_EINVALIDSPEC or an error code
+ */
+GIT_EXTERN(int) git_tag_delete(
+ git_repository *repo,
+ const char *tag_name);
+
+/**
+ * Fill a list with all the tags in the Repository
+ *
+ * The string array will be filled with the names of the
+ * matching tags; these values are owned by the user and
+ * should be free'd manually when no longer needed, using
+ * `git_strarray_free`.
+ *
+ * @param tag_names Pointer to a git_strarray structure where
+ * the tag names will be stored
+ * @param repo Repository where to find the tags
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_tag_list(
+ git_strarray *tag_names,
+ git_repository *repo);
+
+/**
+ * Fill a list with all the tags in the Repository
+ * which name match a defined pattern
+ *
+ * If an empty pattern is provided, all the tags
+ * will be returned.
+ *
+ * The string array will be filled with the names of the
+ * matching tags; these values are owned by the user and
+ * should be free'd manually when no longer needed, using
+ * `git_strarray_free`.
+ *
+ * @param tag_names Pointer to a git_strarray structure where
+ * the tag names will be stored
+ * @param pattern Standard fnmatch pattern
+ * @param repo Repository where to find the tags
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_tag_list_match(
+ git_strarray *tag_names,
+ const char *pattern,
+ git_repository *repo);
+
+/**
+ * Callback used to iterate over tag names
+ *
+ * @see git_tag_foreach
+ *
+ * @param name The tag name
+ * @param oid The tag's OID
+ * @param payload Payload passed to git_tag_foreach
+ * @return non-zero to terminate the iteration
+ */
+typedef int GIT_CALLBACK(git_tag_foreach_cb)(const char *name, git_oid *oid, void *payload);
+
+/**
+ * Call callback `cb' for each tag in the repository
+ *
+ * @param repo Repository
+ * @param callback Callback function
+ * @param payload Pointer to callback data (optional)
+ */
+GIT_EXTERN(int) git_tag_foreach(
+ git_repository *repo,
+ git_tag_foreach_cb callback,
+ void *payload);
+
+
+/**
+ * Recursively peel a tag until a non tag git_object is found
+ *
+ * The retrieved `tag_target` object is owned by the repository
+ * and should be closed with the `git_object_free` method.
+ *
+ * @param tag_target_out Pointer to the peeled git_object
+ * @param tag The tag to be processed
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_tag_peel(
+ git_object **tag_target_out,
+ const git_tag *tag);
+
+/**
+ * Create an in-memory copy of a tag. The copy must be explicitly
+ * free'd or it will leak.
+ *
+ * @param out Pointer to store the copy of the tag
+ * @param source Original tag to copy
+ * @return 0
+ */
+GIT_EXTERN(int) git_tag_dup(git_tag **out, git_tag *source);
+
+/**
+ * Determine whether a tag name is valid, meaning that (when prefixed
+ * with `refs/tags/`) that it is a valid reference name, and that any
+ * additional tag name restrictions are imposed (eg, it cannot start
+ * with a `-`).
+ *
+ * @param valid output pointer to set with validity of given tag name
+ * @param name a tag name to test
+ * @return 0 on success or an error code
+ */
+GIT_EXTERN(int) git_tag_name_is_valid(int *valid, const char *name);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/trace.h b/include/git2/trace.h
new file mode 100644
index 0000000..8cee3a9
--- /dev/null
+++ b/include/git2/trace.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_trace_h__
+#define INCLUDE_git_trace_h__
+
+#include "common.h"
+#include "types.h"
+
+/**
+ * @file git2/trace.h
+ * @brief Git tracing configuration routines
+ * @defgroup git_trace Git tracing configuration routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Available tracing levels. When tracing is set to a particular level,
+ * callers will be provided tracing at the given level and all lower levels.
+ */
+typedef enum {
+ /** No tracing will be performed. */
+ GIT_TRACE_NONE = 0,
+
+ /** Severe errors that may impact the program's execution */
+ GIT_TRACE_FATAL = 1,
+
+ /** Errors that do not impact the program's execution */
+ GIT_TRACE_ERROR = 2,
+
+ /** Warnings that suggest abnormal data */
+ GIT_TRACE_WARN = 3,
+
+ /** Informational messages about program execution */
+ GIT_TRACE_INFO = 4,
+
+ /** Detailed data that allows for debugging */
+ GIT_TRACE_DEBUG = 5,
+
+ /** Exceptionally detailed debugging data */
+ GIT_TRACE_TRACE = 6
+} git_trace_level_t;
+
+/**
+ * An instance for a tracing function
+ */
+typedef void GIT_CALLBACK(git_trace_cb)(git_trace_level_t level, const char *msg);
+
+/**
+ * Sets the system tracing configuration to the specified level with the
+ * specified callback. When system events occur at a level equal to, or
+ * lower than, the given level they will be reported to the given callback.
+ *
+ * @param level Level to set tracing to
+ * @param cb Function to call with trace data
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_trace_set(git_trace_level_t level, git_trace_cb cb);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/transaction.h b/include/git2/transaction.h
new file mode 100644
index 0000000..4938570
--- /dev/null
+++ b/include/git2/transaction.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_transaction_h__
+#define INCLUDE_git_transaction_h__
+
+#include "common.h"
+#include "types.h"
+
+/**
+ * @file git2/transaction.h
+ * @brief Git transactional reference routines
+ * @defgroup git_transaction Git transactional reference routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Create a new transaction object
+ *
+ * This does not lock anything, but sets up the transaction object to
+ * know from which repository to lock.
+ *
+ * @param out the resulting transaction
+ * @param repo the repository in which to lock
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_transaction_new(git_transaction **out, git_repository *repo);
+
+/**
+ * Lock a reference
+ *
+ * Lock the specified reference. This is the first step to updating a
+ * reference.
+ *
+ * @param tx the transaction
+ * @param refname the reference to lock
+ * @return 0 or an error message
+ */
+GIT_EXTERN(int) git_transaction_lock_ref(git_transaction *tx, const char *refname);
+
+/**
+ * Set the target of a reference
+ *
+ * Set the target of the specified reference. This reference must be
+ * locked.
+ *
+ * @param tx the transaction
+ * @param refname reference to update
+ * @param target target to set the reference to
+ * @param sig signature to use in the reflog; pass NULL to read the identity from the config
+ * @param msg message to use in the reflog
+ * @return 0, GIT_ENOTFOUND if the reference is not among the locked ones, or an error code
+ */
+GIT_EXTERN(int) git_transaction_set_target(git_transaction *tx, const char *refname, const git_oid *target, const git_signature *sig, const char *msg);
+
+/**
+ * Set the target of a reference
+ *
+ * Set the target of the specified reference. This reference must be
+ * locked.
+ *
+ * @param tx the transaction
+ * @param refname reference to update
+ * @param target target to set the reference to
+ * @param sig signature to use in the reflog; pass NULL to read the identity from the config
+ * @param msg message to use in the reflog
+ * @return 0, GIT_ENOTFOUND if the reference is not among the locked ones, or an error code
+ */
+GIT_EXTERN(int) git_transaction_set_symbolic_target(git_transaction *tx, const char *refname, const char *target, const git_signature *sig, const char *msg);
+
+/**
+ * Set the reflog of a reference
+ *
+ * Set the specified reference's reflog. If this is combined with
+ * setting the target, that update won't be written to the reflog.
+ *
+ * @param tx the transaction
+ * @param refname the reference whose reflog to set
+ * @param reflog the reflog as it should be written out
+ * @return 0, GIT_ENOTFOUND if the reference is not among the locked ones, or an error code
+ */
+GIT_EXTERN(int) git_transaction_set_reflog(git_transaction *tx, const char *refname, const git_reflog *reflog);
+
+/**
+ * Remove a reference
+ *
+ * @param tx the transaction
+ * @param refname the reference to remove
+ * @return 0, GIT_ENOTFOUND if the reference is not among the locked ones, or an error code
+ */
+GIT_EXTERN(int) git_transaction_remove(git_transaction *tx, const char *refname);
+
+/**
+ * Commit the changes from the transaction
+ *
+ * Perform the changes that have been queued. The updates will be made
+ * one by one, and the first failure will stop the processing.
+ *
+ * @param tx the transaction
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_transaction_commit(git_transaction *tx);
+
+/**
+ * Free the resources allocated by this transaction
+ *
+ * If any references remain locked, they will be unlocked without any
+ * changes made to them.
+ *
+ * @param tx the transaction
+ */
+GIT_EXTERN(void) git_transaction_free(git_transaction *tx);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/include/git2/transport.h b/include/git2/transport.h
new file mode 100644
index 0000000..5a27de9
--- /dev/null
+++ b/include/git2/transport.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_transport_h__
+#define INCLUDE_git_transport_h__
+
+#include "indexer.h"
+#include "net.h"
+#include "types.h"
+#include "cert.h"
+#include "credential.h"
+
+/**
+ * @file git2/transport.h
+ * @brief Git transport interfaces and functions
+ * @defgroup git_transport interfaces and functions
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Callback for messages received by the transport.
+ *
+ * Return a negative value to cancel the network operation.
+ *
+ * @param str The message from the transport
+ * @param len The length of the message
+ * @param payload Payload provided by the caller
+ */
+typedef int GIT_CALLBACK(git_transport_message_cb)(const char *str, int len, void *payload);
+
+/** Signature of a function which creates a transport */
+typedef int GIT_CALLBACK(git_transport_cb)(git_transport **out, git_remote *owner, void *param);
+
+/** @} */
+GIT_END_DECL
+
+#endif
diff --git a/include/git2/tree.h b/include/git2/tree.h
new file mode 100644
index 0000000..ce0a609
--- /dev/null
+++ b/include/git2/tree.h
@@ -0,0 +1,475 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_tree_h__
+#define INCLUDE_git_tree_h__
+
+#include "common.h"
+#include "types.h"
+#include "oid.h"
+#include "object.h"
+
+/**
+ * @file git2/tree.h
+ * @brief Git tree parsing, loading routines
+ * @defgroup git_tree Git tree parsing, loading routines
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Lookup a tree object from the repository.
+ *
+ * @param out Pointer to the looked up tree
+ * @param repo The repo to use when locating the tree.
+ * @param id Identity of the tree to locate.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_tree_lookup(
+ git_tree **out, git_repository *repo, const git_oid *id);
+
+/**
+ * Lookup a tree object from the repository,
+ * given a prefix of its identifier (short id).
+ *
+ * @see git_object_lookup_prefix
+ *
+ * @param out pointer to the looked up tree
+ * @param repo the repo to use when locating the tree.
+ * @param id identity of the tree to locate.
+ * @param len the length of the short identifier
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_tree_lookup_prefix(
+ git_tree **out,
+ git_repository *repo,
+ const git_oid *id,
+ size_t len);
+
+/**
+ * Close an open tree
+ *
+ * You can no longer use the git_tree pointer after this call.
+ *
+ * IMPORTANT: You MUST call this method when you stop using a tree to
+ * release memory. Failure to do so will cause a memory leak.
+ *
+ * @param tree The tree to close
+ */
+GIT_EXTERN(void) git_tree_free(git_tree *tree);
+
+/**
+ * Get the id of a tree.
+ *
+ * @param tree a previously loaded tree.
+ * @return object identity for the tree.
+ */
+GIT_EXTERN(const git_oid *) git_tree_id(const git_tree *tree);
+
+/**
+ * Get the repository that contains the tree.
+ *
+ * @param tree A previously loaded tree.
+ * @return Repository that contains this tree.
+ */
+GIT_EXTERN(git_repository *) git_tree_owner(const git_tree *tree);
+
+/**
+ * Get the number of entries listed in a tree
+ *
+ * @param tree a previously loaded tree.
+ * @return the number of entries in the tree
+ */
+GIT_EXTERN(size_t) git_tree_entrycount(const git_tree *tree);
+
+/**
+ * Lookup a tree entry by its filename
+ *
+ * This returns a git_tree_entry that is owned by the git_tree. You don't
+ * have to free it, but you must not use it after the git_tree is released.
+ *
+ * @param tree a previously loaded tree.
+ * @param filename the filename of the desired entry
+ * @return the tree entry; NULL if not found
+ */
+GIT_EXTERN(const git_tree_entry *) git_tree_entry_byname(
+ const git_tree *tree, const char *filename);
+
+/**
+ * Lookup a tree entry by its position in the tree
+ *
+ * This returns a git_tree_entry that is owned by the git_tree. You don't
+ * have to free it, but you must not use it after the git_tree is released.
+ *
+ * @param tree a previously loaded tree.
+ * @param idx the position in the entry list
+ * @return the tree entry; NULL if not found
+ */
+GIT_EXTERN(const git_tree_entry *) git_tree_entry_byindex(
+ const git_tree *tree, size_t idx);
+
+/**
+ * Lookup a tree entry by SHA value.
+ *
+ * This returns a git_tree_entry that is owned by the git_tree. You don't
+ * have to free it, but you must not use it after the git_tree is released.
+ *
+ * Warning: this must examine every entry in the tree, so it is not fast.
+ *
+ * @param tree a previously loaded tree.
+ * @param id the sha being looked for
+ * @return the tree entry; NULL if not found
+ */
+GIT_EXTERN(const git_tree_entry *) git_tree_entry_byid(
+ const git_tree *tree, const git_oid *id);
+
+/**
+ * Retrieve a tree entry contained in a tree or in any of its subtrees,
+ * given its relative path.
+ *
+ * Unlike the other lookup functions, the returned tree entry is owned by
+ * the user and must be freed explicitly with `git_tree_entry_free()`.
+ *
+ * @param out Pointer where to store the tree entry
+ * @param root Previously loaded tree which is the root of the relative path
+ * @param path Path to the contained entry
+ * @return 0 on success; GIT_ENOTFOUND if the path does not exist
+ */
+GIT_EXTERN(int) git_tree_entry_bypath(
+ git_tree_entry **out,
+ const git_tree *root,
+ const char *path);
+
+/**
+ * Duplicate a tree entry
+ *
+ * Create a copy of a tree entry. The returned copy is owned by the user,
+ * and must be freed explicitly with `git_tree_entry_free()`.
+ *
+ * @param dest pointer where to store the copy
+ * @param source tree entry to duplicate
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_tree_entry_dup(git_tree_entry **dest, const git_tree_entry *source);
+
+/**
+ * Free a user-owned tree entry
+ *
+ * IMPORTANT: This function is only needed for tree entries owned by the
+ * user, such as the ones returned by `git_tree_entry_dup()` or
+ * `git_tree_entry_bypath()`.
+ *
+ * @param entry The entry to free
+ */
+GIT_EXTERN(void) git_tree_entry_free(git_tree_entry *entry);
+
+/**
+ * Get the filename of a tree entry
+ *
+ * @param entry a tree entry
+ * @return the name of the file
+ */
+GIT_EXTERN(const char *) git_tree_entry_name(const git_tree_entry *entry);
+
+/**
+ * Get the id of the object pointed by the entry
+ *
+ * @param entry a tree entry
+ * @return the oid of the object
+ */
+GIT_EXTERN(const git_oid *) git_tree_entry_id(const git_tree_entry *entry);
+
+/**
+ * Get the type of the object pointed by the entry
+ *
+ * @param entry a tree entry
+ * @return the type of the pointed object
+ */
+GIT_EXTERN(git_object_t) git_tree_entry_type(const git_tree_entry *entry);
+
+/**
+ * Get the UNIX file attributes of a tree entry
+ *
+ * @param entry a tree entry
+ * @return filemode as an integer
+ */
+GIT_EXTERN(git_filemode_t) git_tree_entry_filemode(const git_tree_entry *entry);
+
+/**
+ * Get the raw UNIX file attributes of a tree entry
+ *
+ * This function does not perform any normalization and is only useful
+ * if you need to be able to recreate the original tree object.
+ *
+ * @param entry a tree entry
+ * @return filemode as an integer
+ */
+
+GIT_EXTERN(git_filemode_t) git_tree_entry_filemode_raw(const git_tree_entry *entry);
+/**
+ * Compare two tree entries
+ *
+ * @param e1 first tree entry
+ * @param e2 second tree entry
+ * @return <0 if e1 is before e2, 0 if e1 == e2, >0 if e1 is after e2
+ */
+GIT_EXTERN(int) git_tree_entry_cmp(const git_tree_entry *e1, const git_tree_entry *e2);
+
+/**
+ * Convert a tree entry to the git_object it points to.
+ *
+ * You must call `git_object_free()` on the object when you are done with it.
+ *
+ * @param object_out pointer to the converted object
+ * @param repo repository where to lookup the pointed object
+ * @param entry a tree entry
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_tree_entry_to_object(
+ git_object **object_out,
+ git_repository *repo,
+ const git_tree_entry *entry);
+
+/**
+ * Create a new tree builder.
+ *
+ * The tree builder can be used to create or modify trees in memory and
+ * write them as tree objects to the database.
+ *
+ * If the `source` parameter is not NULL, the tree builder will be
+ * initialized with the entries of the given tree.
+ *
+ * If the `source` parameter is NULL, the tree builder will start with no
+ * entries and will have to be filled manually.
+ *
+ * @param out Pointer where to store the tree builder
+ * @param repo Repository in which to store the object
+ * @param source Source tree to initialize the builder (optional)
+ * @return 0 on success; error code otherwise
+ */
+GIT_EXTERN(int) git_treebuilder_new(
+ git_treebuilder **out, git_repository *repo, const git_tree *source);
+
+/**
+ * Clear all the entries in the builder
+ *
+ * @param bld Builder to clear
+ * @return 0 on success; error code otherwise
+ */
+GIT_EXTERN(int) git_treebuilder_clear(git_treebuilder *bld);
+
+/**
+ * Get the number of entries listed in a treebuilder
+ *
+ * @param bld a previously loaded treebuilder.
+ * @return the number of entries in the treebuilder
+ */
+GIT_EXTERN(size_t) git_treebuilder_entrycount(git_treebuilder *bld);
+
+/**
+ * Free a tree builder
+ *
+ * This will clear all the entries and free to builder.
+ * Failing to free the builder after you're done using it
+ * will result in a memory leak
+ *
+ * @param bld Builder to free
+ */
+GIT_EXTERN(void) git_treebuilder_free(git_treebuilder *bld);
+
+/**
+ * Get an entry from the builder from its filename
+ *
+ * The returned entry is owned by the builder and should
+ * not be freed manually.
+ *
+ * @param bld Tree builder
+ * @param filename Name of the entry
+ * @return pointer to the entry; NULL if not found
+ */
+GIT_EXTERN(const git_tree_entry *) git_treebuilder_get(
+ git_treebuilder *bld, const char *filename);
+
+/**
+ * Add or update an entry to the builder
+ *
+ * Insert a new entry for `filename` in the builder with the
+ * given attributes.
+ *
+ * If an entry named `filename` already exists, its attributes
+ * will be updated with the given ones.
+ *
+ * The optional pointer `out` can be used to retrieve a pointer to the
+ * newly created/updated entry. Pass NULL if you do not need it. The
+ * pointer may not be valid past the next operation in this
+ * builder. Duplicate the entry if you want to keep it.
+ *
+ * By default the entry that you are inserting will be checked for
+ * validity; that it exists in the object database and is of the
+ * correct type. If you do not want this behavior, set the
+ * `GIT_OPT_ENABLE_STRICT_OBJECT_CREATION` library option to false.
+ *
+ * @param out Pointer to store the entry (optional)
+ * @param bld Tree builder
+ * @param filename Filename of the entry
+ * @param id SHA1 oid of the entry
+ * @param filemode Folder attributes of the entry. This parameter must
+ * be valued with one of the following entries: 0040000, 0100644,
+ * 0100755, 0120000 or 0160000.
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_treebuilder_insert(
+ const git_tree_entry **out,
+ git_treebuilder *bld,
+ const char *filename,
+ const git_oid *id,
+ git_filemode_t filemode);
+
+/**
+ * Remove an entry from the builder by its filename
+ *
+ * @param bld Tree builder
+ * @param filename Filename of the entry to remove
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_treebuilder_remove(
+ git_treebuilder *bld, const char *filename);
+
+/**
+ * Callback for git_treebuilder_filter
+ *
+ * The return value is treated as a boolean, with zero indicating that the
+ * entry should be left alone and any non-zero value meaning that the
+ * entry should be removed from the treebuilder list (i.e. filtered out).
+ */
+typedef int GIT_CALLBACK(git_treebuilder_filter_cb)(
+ const git_tree_entry *entry, void *payload);
+
+/**
+ * Selectively remove entries in the tree
+ *
+ * The `filter` callback will be called for each entry in the tree with a
+ * pointer to the entry and the provided `payload`; if the callback returns
+ * non-zero, the entry will be filtered (removed from the builder).
+ *
+ * @param bld Tree builder
+ * @param filter Callback to filter entries
+ * @param payload Extra data to pass to filter callback
+ * @return 0 on success, non-zero callback return value, or error code
+ */
+GIT_EXTERN(int) git_treebuilder_filter(
+ git_treebuilder *bld,
+ git_treebuilder_filter_cb filter,
+ void *payload);
+
+/**
+ * Write the contents of the tree builder as a tree object
+ *
+ * The tree builder will be written to the given `repo`, and its
+ * identifying SHA1 hash will be stored in the `id` pointer.
+ *
+ * @param id Pointer to store the OID of the newly written tree
+ * @param bld Tree builder to write
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_treebuilder_write(
+ git_oid *id, git_treebuilder *bld);
+
+/** Callback for the tree traversal method */
+typedef int GIT_CALLBACK(git_treewalk_cb)(
+ const char *root, const git_tree_entry *entry, void *payload);
+
+/** Tree traversal modes */
+typedef enum {
+ GIT_TREEWALK_PRE = 0, /* Pre-order */
+ GIT_TREEWALK_POST = 1 /* Post-order */
+} git_treewalk_mode;
+
+/**
+ * Traverse the entries in a tree and its subtrees in post or pre order.
+ *
+ * The entries will be traversed in the specified order, children subtrees
+ * will be automatically loaded as required, and the `callback` will be
+ * called once per entry with the current (relative) root for the entry and
+ * the entry data itself.
+ *
+ * If the callback returns a positive value, the passed entry will be
+ * skipped on the traversal (in pre mode). A negative value stops the walk.
+ *
+ * @param tree The tree to walk
+ * @param mode Traversal mode (pre or post-order)
+ * @param callback Function to call on each tree entry
+ * @param payload Opaque pointer to be passed on each callback
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_tree_walk(
+ const git_tree *tree,
+ git_treewalk_mode mode,
+ git_treewalk_cb callback,
+ void *payload);
+
+/**
+ * Create an in-memory copy of a tree. The copy must be explicitly
+ * free'd or it will leak.
+ *
+ * @param out Pointer to store the copy of the tree
+ * @param source Original tree to copy
+ * @return 0
+ */
+GIT_EXTERN(int) git_tree_dup(git_tree **out, git_tree *source);
+
+/**
+ * The kind of update to perform
+ */
+typedef enum {
+ /** Update or insert an entry at the specified path */
+ GIT_TREE_UPDATE_UPSERT,
+ /** Remove an entry from the specified path */
+ GIT_TREE_UPDATE_REMOVE
+} git_tree_update_t;
+
+/**
+ * An action to perform during the update of a tree
+ */
+typedef struct {
+ /** Update action. If it's an removal, only the path is looked at */
+ git_tree_update_t action;
+ /** The entry's id */
+ git_oid id;
+ /** The filemode/kind of object */
+ git_filemode_t filemode;
+ /** The full path from the root tree */
+ const char *path;
+} git_tree_update;
+
+/**
+ * Create a tree based on another one with the specified modifications
+ *
+ * Given the `baseline` perform the changes described in the list of
+ * `updates` and create a new tree.
+ *
+ * This function is optimized for common file/directory addition, removal and
+ * replacement in trees. It is much more efficient than reading the tree into a
+ * `git_index` and modifying that, but in exchange it is not as flexible.
+ *
+ * Deleting and adding the same entry is undefined behaviour, changing
+ * a tree to a blob or viceversa is not supported.
+ *
+ * @param out id of the new tree
+ * @param repo the repository in which to create the tree, must be the
+ * same as for `baseline`
+ * @param baseline the tree to base these changes on
+ * @param nupdates the number of elements in the update list
+ * @param updates the list of updates to perform
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_tree_create_updated(git_oid *out, git_repository *repo, git_tree *baseline, size_t nupdates, const git_tree_update *updates);
+
+/** @} */
+
+GIT_END_DECL
+#endif
diff --git a/include/git2/types.h b/include/git2/types.h
new file mode 100644
index 0000000..d4b033d
--- /dev/null
+++ b/include/git2/types.h
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_types_h__
+#define INCLUDE_git_types_h__
+
+#include "common.h"
+
+/**
+ * @file git2/types.h
+ * @brief libgit2 base & compatibility types
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * Cross-platform compatibility types for off_t / time_t
+ *
+ * NOTE: This needs to be in a public header so that both the library
+ * implementation and client applications both agree on the same types.
+ * Otherwise we get undefined behavior.
+ *
+ * Use the "best" types that each platform provides. Currently we truncate
+ * these intermediate representations for compatibility with the git ABI, but
+ * if and when it changes to support 64 bit types, our code will naturally
+ * adapt.
+ * NOTE: These types should match those that are returned by our internal
+ * stat() functions, for all platforms.
+ */
+#include <sys/types.h>
+#ifdef __amigaos4__
+#include <stdint.h>
+#endif
+
+#if defined(_MSC_VER)
+
+typedef __int64 git_off_t;
+typedef __time64_t git_time_t;
+
+#elif defined(__MINGW32__)
+
+typedef off64_t git_off_t;
+typedef __time64_t git_time_t;
+
+#elif defined(__HAIKU__)
+
+typedef __haiku_std_int64 git_off_t;
+typedef __haiku_std_int64 git_time_t;
+
+#else /* POSIX */
+
+/*
+ * Note: Can't use off_t since if a client program includes <sys/types.h>
+ * before us (directly or indirectly), they'll get 32 bit off_t in their client
+ * app, even though /we/ define _FILE_OFFSET_BITS=64.
+ */
+typedef int64_t git_off_t;
+typedef int64_t git_time_t; /**< time in seconds from epoch */
+
+#endif
+
+/** The maximum size of an object */
+typedef uint64_t git_object_size_t;
+
+#include "buffer.h"
+#include "oid.h"
+
+/** Basic type (loose or packed) of any Git object. */
+typedef enum {
+ GIT_OBJECT_ANY = -2, /**< Object can be any of the following */
+ GIT_OBJECT_INVALID = -1, /**< Object is invalid. */
+ GIT_OBJECT_COMMIT = 1, /**< A commit object. */
+ GIT_OBJECT_TREE = 2, /**< A tree (directory listing) object. */
+ GIT_OBJECT_BLOB = 3, /**< A file revision object. */
+ GIT_OBJECT_TAG = 4, /**< An annotated tag object. */
+ GIT_OBJECT_OFS_DELTA = 6, /**< A delta, base is given by an offset. */
+ GIT_OBJECT_REF_DELTA = 7 /**< A delta, base is given by object id. */
+} git_object_t;
+
+/** An open object database handle. */
+typedef struct git_odb git_odb;
+
+/** A custom backend in an ODB */
+typedef struct git_odb_backend git_odb_backend;
+
+/** An object read from the ODB */
+typedef struct git_odb_object git_odb_object;
+
+/** A stream to read/write from the ODB */
+typedef struct git_odb_stream git_odb_stream;
+
+/** A stream to write a packfile to the ODB */
+typedef struct git_odb_writepack git_odb_writepack;
+
+/** a writer for multi-pack-index files. */
+typedef struct git_midx_writer git_midx_writer;
+
+/** An open refs database handle. */
+typedef struct git_refdb git_refdb;
+
+/** A custom backend for refs */
+typedef struct git_refdb_backend git_refdb_backend;
+
+/** A git commit-graph */
+typedef struct git_commit_graph git_commit_graph;
+
+/** a writer for commit-graph files. */
+typedef struct git_commit_graph_writer git_commit_graph_writer;
+
+/**
+ * Representation of an existing git repository,
+ * including all its object contents
+ */
+typedef struct git_repository git_repository;
+
+/** Representation of a working tree */
+typedef struct git_worktree git_worktree;
+
+/** Representation of a generic object in a repository */
+typedef struct git_object git_object;
+
+/** Representation of an in-progress walk through the commits in a repo */
+typedef struct git_revwalk git_revwalk;
+
+/** Parsed representation of a tag object. */
+typedef struct git_tag git_tag;
+
+/** In-memory representation of a blob object. */
+typedef struct git_blob git_blob;
+
+/** Parsed representation of a commit object. */
+typedef struct git_commit git_commit;
+
+/** Representation of each one of the entries in a tree object. */
+typedef struct git_tree_entry git_tree_entry;
+
+/** Representation of a tree object. */
+typedef struct git_tree git_tree;
+
+/** Constructor for in-memory trees */
+typedef struct git_treebuilder git_treebuilder;
+
+/** Memory representation of an index file. */
+typedef struct git_index git_index;
+
+/** An iterator for entries in the index. */
+typedef struct git_index_iterator git_index_iterator;
+
+/** An iterator for conflicts in the index. */
+typedef struct git_index_conflict_iterator git_index_conflict_iterator;
+
+/** Memory representation of a set of config files */
+typedef struct git_config git_config;
+
+/** Interface to access a configuration file */
+typedef struct git_config_backend git_config_backend;
+
+/** Representation of a reference log entry */
+typedef struct git_reflog_entry git_reflog_entry;
+
+/** Representation of a reference log */
+typedef struct git_reflog git_reflog;
+
+/** Representation of a git note */
+typedef struct git_note git_note;
+
+/** Representation of a git packbuilder */
+typedef struct git_packbuilder git_packbuilder;
+
+/** Time in a signature */
+typedef struct git_time {
+ git_time_t time; /**< time in seconds from epoch */
+ int offset; /**< timezone offset, in minutes */
+ char sign; /**< indicator for questionable '-0000' offsets in signature */
+} git_time;
+
+/** An action signature (e.g. for committers, taggers, etc) */
+typedef struct git_signature {
+ char *name; /**< full name of the author */
+ char *email; /**< email of the author */
+ git_time when; /**< time when the action happened */
+} git_signature;
+
+/** In-memory representation of a reference. */
+typedef struct git_reference git_reference;
+
+/** Iterator for references */
+typedef struct git_reference_iterator git_reference_iterator;
+
+/** Transactional interface to references */
+typedef struct git_transaction git_transaction;
+
+/** Annotated commits, the input to merge and rebase. */
+typedef struct git_annotated_commit git_annotated_commit;
+
+/** Representation of a status collection */
+typedef struct git_status_list git_status_list;
+
+/** Representation of a rebase */
+typedef struct git_rebase git_rebase;
+
+/** Basic type of any Git reference. */
+typedef enum {
+ GIT_REFERENCE_INVALID = 0, /**< Invalid reference */
+ GIT_REFERENCE_DIRECT = 1, /**< A reference that points at an object id */
+ GIT_REFERENCE_SYMBOLIC = 2, /**< A reference that points at another reference */
+ GIT_REFERENCE_ALL = GIT_REFERENCE_DIRECT | GIT_REFERENCE_SYMBOLIC
+} git_reference_t;
+
+/** Basic type of any Git branch. */
+typedef enum {
+ GIT_BRANCH_LOCAL = 1,
+ GIT_BRANCH_REMOTE = 2,
+ GIT_BRANCH_ALL = GIT_BRANCH_LOCAL|GIT_BRANCH_REMOTE
+} git_branch_t;
+
+/** Valid modes for index and tree entries. */
+typedef enum {
+ GIT_FILEMODE_UNREADABLE = 0000000,
+ GIT_FILEMODE_TREE = 0040000,
+ GIT_FILEMODE_BLOB = 0100644,
+ GIT_FILEMODE_BLOB_EXECUTABLE = 0100755,
+ GIT_FILEMODE_LINK = 0120000,
+ GIT_FILEMODE_COMMIT = 0160000
+} git_filemode_t;
+
+/**
+ * A refspec specifies the mapping between remote and local reference
+ * names when fetch or pushing.
+ */
+typedef struct git_refspec git_refspec;
+
+/**
+ * Git's idea of a remote repository. A remote can be anonymous (in
+ * which case it does not have backing configuration entries).
+ */
+typedef struct git_remote git_remote;
+
+/**
+ * Interface which represents a transport to communicate with a
+ * remote.
+ */
+typedef struct git_transport git_transport;
+
+/**
+ * Preparation for a push operation. Can be used to configure what to
+ * push and the level of parallelism of the packfile builder.
+ */
+typedef struct git_push git_push;
+
+/* documentation in the definition */
+typedef struct git_remote_head git_remote_head;
+typedef struct git_remote_callbacks git_remote_callbacks;
+
+/**
+ * Parent type for `git_cert_hostkey` and `git_cert_x509`.
+ */
+typedef struct git_cert git_cert;
+
+/**
+ * Opaque structure representing a submodule.
+ */
+typedef struct git_submodule git_submodule;
+
+/**
+ * Submodule update values
+ *
+ * These values represent settings for the `submodule.$name.update`
+ * configuration value which says how to handle `git submodule update` for
+ * this submodule. The value is usually set in the ".gitmodules" file and
+ * copied to ".git/config" when the submodule is initialized.
+ *
+ * You can override this setting on a per-submodule basis with
+ * `git_submodule_set_update()` and write the changed value to disk using
+ * `git_submodule_save()`. If you have overwritten the value, you can
+ * revert it by passing `GIT_SUBMODULE_UPDATE_RESET` to the set function.
+ *
+ * The values are:
+ *
+ * - GIT_SUBMODULE_UPDATE_CHECKOUT: the default; when a submodule is
+ * updated, checkout the new detached HEAD to the submodule directory.
+ * - GIT_SUBMODULE_UPDATE_REBASE: update by rebasing the current checked
+ * out branch onto the commit from the superproject.
+ * - GIT_SUBMODULE_UPDATE_MERGE: update by merging the commit in the
+ * superproject into the current checkout out branch of the submodule.
+ * - GIT_SUBMODULE_UPDATE_NONE: do not update this submodule even when
+ * the commit in the superproject is updated.
+ * - GIT_SUBMODULE_UPDATE_DEFAULT: not used except as static initializer
+ * when we don't want any particular update rule to be specified.
+ */
+typedef enum {
+ GIT_SUBMODULE_UPDATE_CHECKOUT = 1,
+ GIT_SUBMODULE_UPDATE_REBASE = 2,
+ GIT_SUBMODULE_UPDATE_MERGE = 3,
+ GIT_SUBMODULE_UPDATE_NONE = 4,
+
+ GIT_SUBMODULE_UPDATE_DEFAULT = 0
+} git_submodule_update_t;
+
+/**
+ * Submodule ignore values
+ *
+ * These values represent settings for the `submodule.$name.ignore`
+ * configuration value which says how deeply to look at the working
+ * directory when getting submodule status.
+ *
+ * You can override this value in memory on a per-submodule basis with
+ * `git_submodule_set_ignore()` and can write the changed value to disk
+ * with `git_submodule_save()`. If you have overwritten the value, you
+ * can revert to the on disk value by using `GIT_SUBMODULE_IGNORE_RESET`.
+ *
+ * The values are:
+ *
+ * - GIT_SUBMODULE_IGNORE_UNSPECIFIED: use the submodule's configuration
+ * - GIT_SUBMODULE_IGNORE_NONE: don't ignore any change - i.e. even an
+ * untracked file, will mark the submodule as dirty. Ignored files are
+ * still ignored, of course.
+ * - GIT_SUBMODULE_IGNORE_UNTRACKED: ignore untracked files; only changes
+ * to tracked files, or the index or the HEAD commit will matter.
+ * - GIT_SUBMODULE_IGNORE_DIRTY: ignore changes in the working directory,
+ * only considering changes if the HEAD of submodule has moved from the
+ * value in the superproject.
+ * - GIT_SUBMODULE_IGNORE_ALL: never check if the submodule is dirty
+ * - GIT_SUBMODULE_IGNORE_DEFAULT: not used except as static initializer
+ * when we don't want any particular ignore rule to be specified.
+ */
+typedef enum {
+ GIT_SUBMODULE_IGNORE_UNSPECIFIED = -1, /**< use the submodule's configuration */
+
+ GIT_SUBMODULE_IGNORE_NONE = 1, /**< any change or untracked == dirty */
+ GIT_SUBMODULE_IGNORE_UNTRACKED = 2, /**< dirty if tracked files change */
+ GIT_SUBMODULE_IGNORE_DIRTY = 3, /**< only dirty if HEAD moved */
+ GIT_SUBMODULE_IGNORE_ALL = 4 /**< never dirty */
+} git_submodule_ignore_t;
+
+/**
+ * Options for submodule recurse.
+ *
+ * Represent the value of `submodule.$name.fetchRecurseSubmodules`
+ *
+ * * GIT_SUBMODULE_RECURSE_NO - do no recurse into submodules
+ * * GIT_SUBMODULE_RECURSE_YES - recurse into submodules
+ * * GIT_SUBMODULE_RECURSE_ONDEMAND - recurse into submodules only when
+ * commit not already in local clone
+ */
+typedef enum {
+ GIT_SUBMODULE_RECURSE_NO = 0,
+ GIT_SUBMODULE_RECURSE_YES = 1,
+ GIT_SUBMODULE_RECURSE_ONDEMAND = 2
+} git_submodule_recurse_t;
+
+typedef struct git_writestream git_writestream;
+
+/** A type to write in a streaming fashion, for example, for filters. */
+struct git_writestream {
+ int GIT_CALLBACK(write)(git_writestream *stream, const char *buffer, size_t len);
+ int GIT_CALLBACK(close)(git_writestream *stream);
+ void GIT_CALLBACK(free)(git_writestream *stream);
+};
+
+/** Representation of .mailmap file state. */
+typedef struct git_mailmap git_mailmap;
+
+/** @} */
+GIT_END_DECL
+
+#endif
diff --git a/include/git2/version.h b/include/git2/version.h
new file mode 100644
index 0000000..d6aba3b
--- /dev/null
+++ b/include/git2/version.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_version_h__
+#define INCLUDE_git_version_h__
+
+/**
+ * The version string for libgit2. This string follows semantic
+ * versioning (v2) guidelines.
+ */
+#define LIBGIT2_VERSION "1.7.2"
+
+/** The major version number for this version of libgit2. */
+#define LIBGIT2_VER_MAJOR 1
+
+/** The minor version number for this version of libgit2. */
+#define LIBGIT2_VER_MINOR 7
+
+/** The revision ("teeny") version number for this version of libgit2. */
+#define LIBGIT2_VER_REVISION 2
+
+/** The Windows DLL patch number for this version of libgit2. */
+#define LIBGIT2_VER_PATCH 0
+
+/**
+ * The prerelease string for this version of libgit2. For development
+ * (nightly) builds, this will be "alpha". For prereleases, this will be
+ * a prerelease name like "beta" or "rc1". For final releases, this will
+ * be `NULL`.
+ */
+#define LIBGIT2_VER_PRERELEASE NULL
+
+/** The library ABI soversion for this version of libgit2. */
+#define LIBGIT2_SOVERSION "1.7"
+
+#endif
diff --git a/include/git2/worktree.h b/include/git2/worktree.h
new file mode 100644
index 0000000..9193eaf
--- /dev/null
+++ b/include/git2/worktree.h
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git_worktree_h__
+#define INCLUDE_git_worktree_h__
+
+#include "common.h"
+#include "buffer.h"
+#include "types.h"
+#include "strarray.h"
+
+/**
+ * @file git2/worktrees.h
+ * @brief Git worktree related functions
+ * @defgroup git_commit Git worktree related functions
+ * @ingroup Git
+ * @{
+ */
+GIT_BEGIN_DECL
+
+/**
+ * List names of linked working trees
+ *
+ * The returned list should be released with `git_strarray_free`
+ * when no longer needed.
+ *
+ * @param out pointer to the array of working tree names
+ * @param repo the repo to use when listing working trees
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_worktree_list(git_strarray *out, git_repository *repo);
+
+/**
+ * Lookup a working tree by its name for a given repository
+ *
+ * @param out Output pointer to looked up worktree or `NULL`
+ * @param repo The repository containing worktrees
+ * @param name Name of the working tree to look up
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_worktree_lookup(git_worktree **out, git_repository *repo, const char *name);
+
+/**
+ * Open a worktree of a given repository
+ *
+ * If a repository is not the main tree but a worktree, this
+ * function will look up the worktree inside the parent
+ * repository and create a new `git_worktree` structure.
+ *
+ * @param out Out-pointer for the newly allocated worktree
+ * @param repo Repository to look up worktree for
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_worktree_open_from_repository(git_worktree **out, git_repository *repo);
+
+/**
+ * Free a previously allocated worktree
+ *
+ * @param wt worktree handle to close. If NULL nothing occurs.
+ */
+GIT_EXTERN(void) git_worktree_free(git_worktree *wt);
+
+/**
+ * Check if worktree is valid
+ *
+ * A valid worktree requires both the git data structures inside
+ * the linked parent repository and the linked working copy to be
+ * present.
+ *
+ * @param wt Worktree to check
+ * @return 0 when worktree is valid, error-code otherwise
+ */
+GIT_EXTERN(int) git_worktree_validate(const git_worktree *wt);
+
+/**
+ * Worktree add options structure
+ *
+ * Initialize with `GIT_WORKTREE_ADD_OPTIONS_INIT`. Alternatively, you can
+ * use `git_worktree_add_options_init`.
+ *
+ */
+typedef struct git_worktree_add_options {
+ unsigned int version;
+
+ int lock; /**< lock newly created worktree */
+ git_reference *ref; /**< reference to use for the new worktree HEAD */
+
+ /**
+ * Options for the checkout.
+ */
+ git_checkout_options checkout_options;
+} git_worktree_add_options;
+
+#define GIT_WORKTREE_ADD_OPTIONS_VERSION 1
+#define GIT_WORKTREE_ADD_OPTIONS_INIT {GIT_WORKTREE_ADD_OPTIONS_VERSION,0,NULL,GIT_CHECKOUT_OPTIONS_INIT}
+
+/**
+ * Initialize git_worktree_add_options structure
+ *
+ * Initializes a `git_worktree_add_options` with default values. Equivalent to
+ * creating an instance with `GIT_WORKTREE_ADD_OPTIONS_INIT`.
+ *
+ * @param opts The `git_worktree_add_options` struct to initialize.
+ * @param version The struct version; pass `GIT_WORKTREE_ADD_OPTIONS_VERSION`.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_worktree_add_options_init(git_worktree_add_options *opts,
+ unsigned int version);
+
+/**
+ * Add a new working tree
+ *
+ * Add a new working tree for the repository, that is create the
+ * required data structures inside the repository and check out
+ * the current HEAD at `path`
+ *
+ * @param out Output pointer containing new working tree
+ * @param repo Repository to create working tree for
+ * @param name Name of the working tree
+ * @param path Path to create working tree at
+ * @param opts Options to modify default behavior. May be NULL
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_worktree_add(git_worktree **out, git_repository *repo,
+ const char *name, const char *path,
+ const git_worktree_add_options *opts);
+
+/**
+ * Lock worktree if not already locked
+ *
+ * Lock a worktree, optionally specifying a reason why the linked
+ * working tree is being locked.
+ *
+ * @param wt Worktree to lock
+ * @param reason Reason why the working tree is being locked
+ * @return 0 on success, non-zero otherwise
+ */
+GIT_EXTERN(int) git_worktree_lock(git_worktree *wt, const char *reason);
+
+/**
+ * Unlock a locked worktree
+ *
+ * @param wt Worktree to unlock
+ * @return 0 on success, 1 if worktree was not locked, error-code
+ * otherwise
+ */
+GIT_EXTERN(int) git_worktree_unlock(git_worktree *wt);
+
+/**
+ * Check if worktree is locked
+ *
+ * A worktree may be locked if the linked working tree is stored
+ * on a portable device which is not available.
+ *
+ * @param reason Buffer to store reason in. If NULL no reason is stored.
+ * @param wt Worktree to check
+ * @return 0 when the working tree not locked, a value greater
+ * than zero if it is locked, less than zero if there was an
+ * error
+ */
+GIT_EXTERN(int) git_worktree_is_locked(git_buf *reason, const git_worktree *wt);
+
+/**
+ * Retrieve the name of the worktree
+ *
+ * @param wt Worktree to get the name for
+ * @return The worktree's name. The pointer returned is valid for the
+ * lifetime of the git_worktree
+ */
+GIT_EXTERN(const char *) git_worktree_name(const git_worktree *wt);
+
+/**
+ * Retrieve the filesystem path for the worktree
+ *
+ * @param wt Worktree to get the path for
+ * @return The worktree's filesystem path. The pointer returned
+ * is valid for the lifetime of the git_worktree.
+ */
+GIT_EXTERN(const char *) git_worktree_path(const git_worktree *wt);
+
+/**
+ * Flags which can be passed to git_worktree_prune to alter its
+ * behavior.
+ */
+typedef enum {
+ /* Prune working tree even if working tree is valid */
+ GIT_WORKTREE_PRUNE_VALID = 1u << 0,
+ /* Prune working tree even if it is locked */
+ GIT_WORKTREE_PRUNE_LOCKED = 1u << 1,
+ /* Prune checked out working tree */
+ GIT_WORKTREE_PRUNE_WORKING_TREE = 1u << 2
+} git_worktree_prune_t;
+
+/**
+ * Worktree prune options structure
+ *
+ * Initialize with `GIT_WORKTREE_PRUNE_OPTIONS_INIT`. Alternatively, you can
+ * use `git_worktree_prune_options_init`.
+ *
+ */
+typedef struct git_worktree_prune_options {
+ unsigned int version;
+
+ /** A combination of `git_worktree_prune_t` */
+ uint32_t flags;
+} git_worktree_prune_options;
+
+#define GIT_WORKTREE_PRUNE_OPTIONS_VERSION 1
+#define GIT_WORKTREE_PRUNE_OPTIONS_INIT {GIT_WORKTREE_PRUNE_OPTIONS_VERSION,0}
+
+/**
+ * Initialize git_worktree_prune_options structure
+ *
+ * Initializes a `git_worktree_prune_options` with default values. Equivalent to
+ * creating an instance with `GIT_WORKTREE_PRUNE_OPTIONS_INIT`.
+ *
+ * @param opts The `git_worktree_prune_options` struct to initialize.
+ * @param version The struct version; pass `GIT_WORKTREE_PRUNE_OPTIONS_VERSION`.
+ * @return Zero on success; -1 on failure.
+ */
+GIT_EXTERN(int) git_worktree_prune_options_init(
+ git_worktree_prune_options *opts,
+ unsigned int version);
+
+/**
+ * Is the worktree prunable with the given options?
+ *
+ * A worktree is not prunable in the following scenarios:
+ *
+ * - the worktree is linking to a valid on-disk worktree. The
+ * `valid` member will cause this check to be ignored.
+ * - the worktree is locked. The `locked` flag will cause this
+ * check to be ignored.
+ *
+ * If the worktree is not valid and not locked or if the above
+ * flags have been passed in, this function will return a
+ * positive value. If the worktree is not prunable, an error
+ * message will be set (visible in `giterr_last`) with details about
+ * why.
+ *
+ * @param wt Worktree to check.
+ * @param opts The prunable options.
+ * @return 1 if the worktree is prunable, 0 otherwise, or an error code.
+ */
+GIT_EXTERN(int) git_worktree_is_prunable(git_worktree *wt,
+ git_worktree_prune_options *opts);
+
+/**
+ * Prune working tree
+ *
+ * Prune the working tree, that is remove the git data
+ * structures on disk. The repository will only be pruned of
+ * `git_worktree_is_prunable` succeeds.
+ *
+ * @param wt Worktree to prune
+ * @param opts Specifies which checks to override. See
+ * `git_worktree_is_prunable`. May be NULL
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_worktree_prune(git_worktree *wt,
+ git_worktree_prune_options *opts);
+
+/** @} */
+GIT_END_DECL
+#endif
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..ed0ab4f
--- /dev/null
+++ b/package.json
@@ -0,0 +1,7 @@
+{
+ "name": "libgit2",
+ "version": "1.7.2",
+ "repo": "https://github.com/libgit2/libgit2",
+ "description": " A cross-platform, linkable library implementation of Git that you can use in your application.",
+ "install": "mkdir build && cd build && cmake .. && cmake --build ."
+}
diff --git a/script/backport.sh b/script/backport.sh
new file mode 100755
index 0000000..3c2f899
--- /dev/null
+++ b/script/backport.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+if test $# -eq 0
+then
+ echo "USAGE: $0 <#PR> [<#PR>...]"
+ exit
+fi
+
+commits=
+
+for pr in $*
+do
+ mergecommit=$(git rev-parse ":/Merge pull request #$pr" || exit 1)
+ mergebase=$(git merge-base "$mergecommit"^1 "$mergecommit"^2 || exit 1)
+
+ commits="$commits $(git rev-list --reverse "$mergecommit"^2 ^"$mergebase")"
+done
+
+echo "Cherry-picking the following commits:"
+git rev-list --no-walk --oneline $commits
+echo
+
+git cherry-pick $commits
diff --git a/script/leaks.sh b/script/leaks.sh
new file mode 100755
index 0000000..efeead5
--- /dev/null
+++ b/script/leaks.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+export MallocStackLogging=1
+export MallocScribble=1
+export MallocLogFile=/dev/null
+export CLAR_AT_EXIT="leaks -quiet \$PPID"
+exec "$@"
diff --git a/script/release.py b/script/release.py
new file mode 100755
index 0000000..1a240de
--- /dev/null
+++ b/script/release.py
@@ -0,0 +1,191 @@
+#!/usr/bin/env python
+
+from collections import namedtuple
+
+import argparse
+import base64
+import copy
+import json
+import subprocess
+import sys
+import urllib.parse
+import urllib.request
+import urllib.error
+
+class Error(Exception):
+ pass
+
+class Version(object):
+ def __init__(self, version):
+ versions = version.split(sep='.')
+ if len(versions) < 2 or len(versions) > 3:
+ raise Error("Invalid version string '{}'".format(version))
+ self.major = int(versions[0])
+ self.minor = int(versions[1])
+ self.revision = int(versions[2]) if len(versions) == 3 else 0
+
+ def __str__(self):
+ return '{}.{}.{}'.format(self.major, self.minor, self.revision)
+
+ def __eq__(self, other):
+ return self.major == other.major and self.minor == other.minor and self.revision == other.revision
+
+def verify_version(version):
+ expected = {
+ 'VERSION': [ '"{}"'.format(version), None ],
+ 'VER_MAJOR': [ str(version.major), None ],
+ 'VER_MINOR': [ str(version.minor), None ],
+ 'VER_REVISION': [ str(version.revision), None ],
+ 'VER_PATCH': [ '0', None ],
+ 'SOVERSION': [ '"{}.{}"'.format(version.major, version.minor), None ],
+ }
+
+ # Parse CMakeLists
+ with open('CMakeLists.txt') as f:
+ for line in f.readlines():
+ if line.startswith('project(libgit2 VERSION "{}"'.format(version)):
+ break
+ else:
+ raise Error("cmake: invalid project definition")
+
+ # Parse version.h
+ with open('include/git2/version.h') as f:
+ lines = f.readlines()
+
+ for key in expected.keys():
+ define = '#define LIBGIT2_{} '.format(key)
+ for line in lines:
+ if line.startswith(define):
+ expected[key][1] = line[len(define):].strip()
+ break
+ else:
+ raise Error("version.h: missing define for '{}'".format(key))
+
+ for k, v in expected.items():
+ if v[0] != v[1]:
+ raise Error("version.h: define '{}' does not match (got '{}', expected '{}')".format(k, v[0], v[1]))
+
+ with open('package.json') as f:
+ pkg = json.load(f)
+
+ try:
+ pkg_version = Version(pkg["version"])
+ except KeyError as err:
+ raise Error("package.json: missing the field {}".format(err))
+
+ if pkg_version != version:
+ raise Error("package.json: version does not match (got '{}', expected '{}')".format(pkg_version, version))
+
+def generate_relnotes(tree, version):
+ with open('docs/changelog.md') as f:
+ lines = f.readlines()
+
+ if not lines[0].startswith('v'):
+ raise Error("changelog.md: missing section for v{}".format(version))
+ try:
+ v = Version(lines[0][1:].strip())
+ except:
+ raise Error("changelog.md: invalid version string {}".format(lines[0].strip()))
+ if v != version:
+ raise Error("changelog.md: changelog version doesn't match (got {}, expected {})".format(v, version))
+ if not lines[1].startswith('----'):
+ raise Error("changelog.md: missing version header")
+ if lines[2] != '\n':
+ raise Error("changelog.md: missing newline after version header")
+
+ for i, line in enumerate(lines[3:]):
+ if not line.startswith('v'):
+ continue
+ try:
+ Version(line[1:].strip())
+ break
+ except:
+ continue
+ else:
+ raise Error("changelog.md: cannot find section header of preceding release")
+
+ return ''.join(lines[3:i + 3]).strip()
+
+def git(*args):
+ process = subprocess.run([ 'git', *args ], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ if process.returncode != 0:
+ raise Error('Failed executing git {}: {}'.format(' '.join(args), process.stderr.decode()))
+ return process.stdout
+
+def post(url, data, contenttype, user, password):
+ request = urllib.request.Request(url, data=data)
+ request.add_header('Accept', 'application/json')
+ request.add_header('Content-Type', contenttype)
+ request.add_header('Content-Length', len(data))
+ request.add_header('Authorization', 'Basic ' + base64.b64encode('{}:{}'.format(user, password).encode()).decode())
+
+ try:
+ response = urllib.request.urlopen(request)
+ if response.getcode() != 201:
+ raise Error("POST to '{}' failed: {}".format(url, response.reason))
+ except urllib.error.URLError as e:
+ raise Error("POST to '{}' failed: {}".format(url, e))
+ data = json.load(response)
+
+ return data
+
+def generate_asset(version, tree, archive_format):
+ Asset = namedtuple('Asset', ['name', 'label', 'mimetype', 'data'])
+ mimetype = 'application/{}'.format('gzip' if archive_format == 'tar.gz' else 'zip')
+ return Asset(
+ "libgit2-{}.{}".format(version, archive_format), "Release sources: libgit2-{}.{}".format(version, archive_format), mimetype,
+ git('archive', '--format', archive_format, '--prefix', 'libgit2-{}/'.format(version), tree)
+ )
+
+def release(args):
+ params = {
+ "tag_name": 'v' + str(args.version),
+ "name": 'libgit2 v' + str(args.version),
+ "target_commitish": git('rev-parse', args.tree).decode().strip(),
+ "body": generate_relnotes(args.tree, args.version),
+ }
+ assets = [
+ generate_asset(args.version, args.tree, 'tar.gz'),
+ generate_asset(args.version, args.tree, 'zip'),
+ ]
+
+ if args.dryrun:
+ for k, v in params.items():
+ print('{}: {}'.format(k, v))
+ for asset in assets:
+ print('asset: name={}, label={}, mimetype={}, bytes={}'.format(asset.name, asset.label, asset.mimetype, len(asset.data)))
+ return
+
+ try:
+ url = 'https://api.github.com/repos/{}/releases'.format(args.repository)
+ response = post(url, json.dumps(params).encode(), 'application/json', args.user, args.password)
+ except Error as e:
+ raise Error('Could not create release: ' + str(e))
+
+ for asset in assets:
+ try:
+ url = list(urllib.parse.urlparse(response['upload_url'].split('{?')[0]))
+ url[4] = urllib.parse.urlencode({ 'name': asset.name, 'label': asset.label })
+ post(urllib.parse.urlunparse(url), asset.data, asset.mimetype, args.user, args.password)
+ except Error as e:
+ raise Error('Could not upload asset: ' + str(e))
+
+def main():
+ parser = argparse.ArgumentParser(description='Create a libgit2 release')
+ parser.add_argument('--tree', default='HEAD', help='tree to create release for (default: HEAD)')
+ parser.add_argument('--dryrun', action='store_true', help='generate release, but do not post it')
+ parser.add_argument('--repository', default='libgit2/libgit2', help='GitHub repository to create repository in')
+ parser.add_argument('--user', help='user to authenticate as')
+ parser.add_argument('--password', help='password to authenticate with')
+ parser.add_argument('version', type=Version, help='version of the new release')
+ args = parser.parse_args()
+
+ verify_version(args.version)
+ release(args)
+
+if __name__ == '__main__':
+ try:
+ main()
+ except Error as e:
+ print(e)
+ sys.exit(1)
diff --git a/script/sanitizers.supp b/script/sanitizers.supp
new file mode 100644
index 0000000..4e0e9be
--- /dev/null
+++ b/script/sanitizers.supp
@@ -0,0 +1,4 @@
+[undefined]
+# This library allows unaligned access on Intel-like processors. Prevent UBSan
+# from complaining about that.
+fun:sha1_compression_states
diff --git a/script/thread-sanitizer.supp b/script/thread-sanitizer.supp
new file mode 100644
index 0000000..97d2304
--- /dev/null
+++ b/script/thread-sanitizer.supp
@@ -0,0 +1,26 @@
+# In attr_file_free, the locks are acquired in the opposite order in which they
+# are normally acquired. This is probably something worth fixing to have a
+# consistent lock hierarchy that is easy to understand.
+deadlock:attr_cache_lock
+
+# git_mwindow_file_register has the possibility of evicting some files from the
+# global cache. In order to avoid races and closing files that are currently
+# being accessed, before evicting any file it will attempt to acquire that
+# file's lock. Finally, git_mwindow_file_register is typically called with a
+# file lock held, because the caller will use the fd in the mwf immediately
+# after registering it. This causes ThreadSanitizer to observe different orders
+# of acquisition of the mutex (which implies a possibility of a deadlock),
+# _but_ since the files are added to the cache after other files have been
+# evicted, there cannot be a case where mwf A is trying to be registered while
+# evicting mwf B concurrently and viceversa: at most one of them can be present
+# in the cache.
+deadlock:git_mwindow_file_register
+
+# When invoking the time/timezone functions from git_signature_now(), they
+# access libc methods that need to be instrumented to correctly analyze the
+# data races.
+called_from_lib:libc.so.6
+
+# TODO(#5592): Investigate and fix this. It can be triggered by the `thread`
+# test suite.
+race:git_filter_list__load_ext
diff --git a/script/user_model.c b/script/user_model.c
new file mode 100644
index 0000000..4942527
--- /dev/null
+++ b/script/user_model.c
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+void *realloc(void *ptr, size_t size);
+void *memmove(void *dest, const void *src, size_t n);
+size_t strlen(const char *s);
+
+typedef struct va_list_str *va_list;
+
+typedef struct git_vector {
+ void **contents;
+ size_t length;
+} git_vector;
+
+typedef struct git_buf {
+ char *ptr;
+ size_t asize, size;
+} git_buf;
+
+int git_vector_insert(git_vector *v, void *element)
+{
+ if (!v)
+ __coverity_panic__();
+
+ v->contents = realloc(v->contents, ++v->length);
+ if (!v->contents)
+ __coverity_panic__();
+ v->contents[v->length] = element;
+
+ return 0;
+}
+
+int git_buf_len(const struct git_buf *buf)
+{
+ return strlen(buf->ptr);
+}
+
+int git_buf_vprintf(git_buf *buf, const char *format, va_list ap)
+{
+ char ch, *s;
+ size_t len;
+
+ __coverity_string_null_sink__(format);
+ __coverity_string_size_sink__(format);
+
+ ch = *format;
+ ch = *(char *)ap;
+
+ buf->ptr = __coverity_alloc__(len);
+ __coverity_writeall__(buf->ptr);
+ buf->size = len;
+
+ return 0;
+}
+
+int git_buf_put(git_buf *buf, const char *data, size_t len)
+{
+ buf->ptr = __coverity_alloc__(buf->size + len + 1);
+ memmove(buf->ptr + buf->size, data, len);
+ buf->size += len;
+ buf->ptr[buf->size + len] = 0;
+ return 0;
+}
+
+int git_buf_set(git_buf *buf, const void *data, size_t len)
+{
+ buf->ptr = __coverity_alloc__(len + 1);
+ memmove(buf->ptr, data, len);
+ buf->size = len + 1;
+ return 0;
+}
+
+void clar__fail(
+ const char *file,
+ int line,
+ const char *error,
+ const char *description,
+ int should_abort)
+{
+ if (should_abort)
+ __coverity_panic__();
+}
+
+void clar__assert(
+ int condition,
+ const char *file,
+ int line,
+ const char *error,
+ const char *description,
+ int should_abort)
+{
+ if (!condition && should_abort)
+ __coverity_panic__();
+}
diff --git a/script/user_nodefs.h b/script/user_nodefs.h
new file mode 100644
index 0000000..b6e2df3
--- /dev/null
+++ b/script/user_nodefs.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#nodef GIT_ERROR_CHECK_ALLOC(ptr) if (ptr == NULL) { __coverity_panic__(); }
+#nodef GIT_ERROR_CHECK_ALLOC_BUF(buf) if (buf == NULL || git_buf_oom(buf)) { __coverity_panic__(); }
+
+#nodef GITERR_CHECK_ALLOC_ADD(out, one, two) \
+ if (GIT_ADD_SIZET_OVERFLOW(out, one, two)) { __coverity_panic__(); }
+
+#nodef GITERR_CHECK_ALLOC_ADD3(out, one, two, three) \
+ if (GIT_ADD_SIZET_OVERFLOW(out, one, two) || \
+ GIT_ADD_SIZET_OVERFLOW(out, *(out), three)) { __coverity_panic__(); }
+
+#nodef GITERR_CHECK_ALLOC_ADD4(out, one, two, three, four) \
+ if (GIT_ADD_SIZET_OVERFLOW(out, one, two) || \
+ GIT_ADD_SIZET_OVERFLOW(out, *(out), three) || \
+ GIT_ADD_SIZET_OVERFLOW(out, *(out), four)) { __coverity_panic__(); }
+
+#nodef GITERR_CHECK_ALLOC_MULTIPLY(out, nelem, elsize) \
+ if (GIT_MULTIPLY_SIZET_OVERFLOW(out, nelem, elsize)) { __coverity_panic__(); }
+
+#nodef GIT_ERROR_CHECK_VERSION(S,V,N) if (git_error__check_version(S,V,N) < 0) { __coverity_panic__(); }
+
+#nodef LOOKS_LIKE_DRIVE_PREFIX(S) (strlen(S) >= 2 && git__isalpha((S)[0]) && (S)[1] == ':')
+
+#nodef git_vector_foreach(v, iter, elem) \
+ for ((iter) = 0; (v)->contents != NULL && (iter) < (v)->length && ((elem) = (v)->contents[(iter)], 1); (iter)++ )
+
+#nodef git_vector_rforeach(v, iter, elem) \
+ for ((iter) = (v)->length - 1; (v)->contents != NULL && (iter) < SIZE_MAX && ((elem) = (v)->contents[(iter)], 1); (iter)-- )
diff --git a/script/valgrind.sh b/script/valgrind.sh
new file mode 100755
index 0000000..b5deed2
--- /dev/null
+++ b/script/valgrind.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+exec valgrind --leak-check=full --show-reachable=yes --error-exitcode=125 --num-callers=50 --suppressions="$(dirname "${BASH_SOURCE[0]}")/valgrind.supp" "$@"
diff --git a/script/valgrind.supp b/script/valgrind.supp
new file mode 100644
index 0000000..8c4549f
--- /dev/null
+++ b/script/valgrind.supp
@@ -0,0 +1,244 @@
+{
+ ignore-zlib-errors-cond
+ Memcheck:Cond
+ obj:*libz.so*
+}
+
+{
+ ignore-giterror-set-leak
+ Memcheck:Leak
+ ...
+ fun:giterror_set
+}
+
+{
+ ignore-git-global-state-leak
+ Memcheck:Leak
+ ...
+ fun:git__global_state
+}
+
+{
+ ignore-openssl-ssl-leak
+ Memcheck:Leak
+ ...
+ obj:*libssl.so*
+ ...
+}
+
+{
+ ignore-openssl-crypto-leak
+ Memcheck:Leak
+ ...
+ obj:*libcrypto.so*
+ ...
+}
+
+{
+ ignore-openssl-crypto-cond
+ Memcheck:Cond
+ obj:*libcrypto.so*
+ ...
+}
+
+{
+ ignore-openssl-init-leak
+ Memcheck:Leak
+ ...
+ fun:git_openssl_stream_global_init
+ ...
+}
+
+{
+ ignore-openssl-legacy-init-leak
+ Memcheck:Leak
+ ...
+ fun:OPENSSL_init_ssl__legacy
+ ...
+}
+
+{
+ ignore-openssl-malloc-leak
+ Memcheck:Leak
+ ...
+ fun:git_openssl_malloc
+ ...
+}
+
+{
+ ignore-openssl-realloc-leak
+ Memcheck:Leak
+ ...
+ fun:git_openssl_realloc
+ ...
+}
+
+{
+ ignore-glibc-getaddrinfo-cache
+ Memcheck:Leak
+ ...
+ fun:__check_pf
+}
+
+{
+ ignore-curl-global-init
+ Memcheck:Leak
+ ...
+ fun:curl_global_init
+}
+
+{
+ ignore-libssh2-init
+ Memcheck:Leak
+ ...
+ fun:gcry_control
+ fun:libssh2_init
+ ...
+}
+
+{
+ ignore-libssh2-session-create
+ Memcheck:Leak
+ ...
+ fun:_git_ssh_session_create
+ ...
+}
+
+{
+ ignore-libssh2-setup-conn
+ Memcheck:Leak
+ ...
+ fun:_git_ssh_setup_conn
+ ...
+}
+
+{
+ ignore-libssh2-gcrypt-control-leak
+ Memcheck:Leak
+ ...
+ fun:gcry_control
+ obj:*libssh2.so*
+}
+
+{
+ ignore-libssh2-gcrypt-mpinew-leak
+ Memcheck:Leak
+ ...
+ fun:gcry_mpi_new
+ obj:*libssh2.so*
+}
+
+{
+ ignore-libssh2-gcrypt-mpiscan-leak
+ Memcheck:Leak
+ ...
+ fun:gcry_mpi_scan
+ obj:*libssh2.so*
+ ...
+}
+
+{
+ ignore-libssh2-gcrypt-randomize-leak
+ Memcheck:Leak
+ ...
+ fun:gcry_randomize
+ obj:*libssh2.so*
+}
+
+{
+ ignore-libssh2-gcrypt-sexpfindtoken-leak
+ Memcheck:Leak
+ ...
+ fun:gcry_sexp_find_token
+ obj:*libssh2.so*
+}
+
+{
+ ignore-libssh2-gcrypt-pksign-leak
+ Memcheck:Leak
+ ...
+ fun:gcry_pk_sign
+ obj:*libssh2.so*
+}
+
+{
+ ignore-libssh2-gcrypt-session-handshake
+ Memcheck:Leak
+ ...
+ obj:*libssh2.so*
+ obj:*libssh2.so*
+ fun:libssh2_session_handshake
+ ...
+}
+
+{
+ ignore-openssl-undefined-in-read
+ Memcheck:Cond
+ ...
+ obj:*libssl.so*
+ ...
+ fun:openssl_read
+ ...
+}
+
+{
+ ignore-openssl-undefined-in-connect
+ Memcheck:Cond
+ ...
+ obj:*libssl.so*
+ ...
+ fun:openssl_connect
+ ...
+}
+
+{
+ ignore-libssh2-rsa-sha1-sign
+ Memcheck:Leak
+ ...
+ obj:*libgcrypt.so*
+ fun:_libssh2_rsa_sha1_sign
+ ...
+}
+
+{
+ ignore-libssh2-kexinit
+ Memcheck:Leak
+ ...
+ obj:*libssh2.so*
+ fun:kexinit
+ ...
+}
+
+{
+ ignore-noai6ai_cached-double-free
+ Memcheck:Free
+ fun:free
+ fun:__libc_freeres
+ ...
+ fun:exit
+ ...
+}
+
+{
+ ignore-libcrypto-uninitialized-read-for-entropy
+ Memcheck:Value8
+ ...
+ obj:*libcrypto.so*
+ ...
+}
+
+{
+ ignore-dlopen-leak
+ Memcheck:Leak
+ ...
+ fun:dlopen
+ ...
+}
+
+{
+ ignore-dlopen-leak
+ Memcheck:Leak
+ ...
+ fun:_dlerror_run
+ ...
+}
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..8525acd
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,214 @@
+# The main libgit2 source tree: this CMakeLists.txt identifies platform
+# support and includes the subprojects that make up core libgit2 support.
+
+#
+# Optional build configuration settings
+#
+
+if(DEPRECATE_HARD)
+ add_definitions(-DGIT_DEPRECATE_HARD)
+endif()
+
+if(USE_LEAK_CHECKER STREQUAL "valgrind")
+ add_definitions(-DVALGRIND)
+endif()
+
+#
+# Optional debugging functionality
+#
+
+if(DEBUG_POOL)
+ set(GIT_DEBUG_POOL 1)
+endif()
+add_feature_info(debugpool GIT_DEBUG_POOL "debug pool allocator")
+
+if(DEBUG_STRICT_ALLOC)
+ set(GIT_DEBUG_STRICT_ALLOC 1)
+endif()
+add_feature_info(debugalloc GIT_DEBUG_STRICT_ALLOC "debug strict allocators")
+
+if(DEBUG_STRICT_OPEN)
+ set(GIT_DEBUG_STRICT_OPEN 1)
+endif()
+add_feature_info(debugopen GIT_DEBUG_STRICT_OPEN "path validation in open")
+
+#
+# Optional feature enablement
+#
+
+include(SelectGSSAPI)
+include(SelectHTTPSBackend)
+include(SelectHashes)
+include(SelectHTTPParser)
+include(SelectRegex)
+include(SelectXdiff)
+include(SelectSSH)
+include(SelectZlib)
+
+#
+# Platform support
+#
+
+# futimes/futimens
+
+if(HAVE_FUTIMENS)
+ set(GIT_USE_FUTIMENS 1)
+endif ()
+add_feature_info(futimens GIT_USE_FUTIMENS "futimens support")
+
+# qsort
+
+# old-style FreeBSD qsort_r() has the 'context' parameter as the first argument
+# of the comparison function:
+check_prototype_definition_safe(qsort_r
+ "void (qsort_r)(void *base, size_t nmemb, size_t size, void *context, int (*compar)(void *, const void *, const void *))"
+ "" "stdlib.h" GIT_QSORT_BSD)
+
+# GNU or POSIX qsort_r() has the 'context' parameter as the last argument of the
+# comparison function:
+check_prototype_definition_safe(qsort_r
+ "void (qsort_r)(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *, void *), void *context)"
+ "" "stdlib.h" GIT_QSORT_GNU)
+
+# C11 qsort_s() has the 'context' parameter as the last argument of the
+# comparison function, and returns an error status:
+check_prototype_definition_safe(qsort_s
+ "errno_t (qsort_s)(void *base, rsize_t nmemb, rsize_t size, int (*compar)(const void *, const void *, void *), void *context)"
+ "0" "stdlib.h" GIT_QSORT_C11)
+
+# MSC qsort_s() has the 'context' parameter as the first argument of the
+# comparison function, and as the last argument of qsort_s():
+check_prototype_definition_safe(qsort_s
+ "void (qsort_s)(void *base, size_t num, size_t width, int (*compare )(void *, const void *, const void *), void *context)"
+ "" "stdlib.h" GIT_QSORT_MSC)
+
+# random / entropy data
+
+check_symbol_exists(getentropy unistd.h GIT_RAND_GETENTROPY)
+check_symbol_exists(getloadavg stdlib.h GIT_RAND_GETLOADAVG)
+
+# poll
+
+if(WIN32)
+ set(GIT_IO_WSAPOLL 1)
+else()
+ check_symbol_exists(poll poll.h GIT_IO_POLL)
+ check_symbol_exists(select sys/select.h GIT_IO_SELECT)
+endif()
+
+# determine architecture of the machine
+
+if(CMAKE_SIZEOF_VOID_P EQUAL 8)
+ set(GIT_ARCH_64 1)
+elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
+ set(GIT_ARCH_32 1)
+elseif(CMAKE_SIZEOF_VOID_P)
+ message(FATAL_ERROR "Unsupported architecture (pointer size is ${CMAKE_SIZEOF_VOID_P} bytes)")
+else()
+ message(FATAL_ERROR "Unsupported architecture (CMAKE_SIZEOF_VOID_P is unset)")
+endif()
+
+# nanosecond mtime/ctime support
+
+if(USE_NSEC)
+ set(GIT_USE_NSEC 1)
+endif()
+
+# high-resolution stat support
+
+if(HAVE_STRUCT_STAT_ST_MTIM)
+ set(GIT_USE_STAT_MTIM 1)
+elseif(HAVE_STRUCT_STAT_ST_MTIMESPEC)
+ set(GIT_USE_STAT_MTIMESPEC 1)
+elseif(HAVE_STRUCT_STAT_ST_MTIME_NSEC)
+ set(GIT_USE_STAT_MTIME_NSEC 1)
+endif()
+
+# realtime support
+
+check_library_exists(rt clock_gettime "time.h" NEED_LIBRT)
+if(NEED_LIBRT)
+ list(APPEND LIBGIT2_SYSTEM_LIBS rt)
+ list(APPEND LIBGIT2_PC_LIBS "-lrt")
+endif()
+
+# platform libraries
+
+if(WIN32)
+ list(APPEND LIBGIT2_SYSTEM_LIBS ws2_32)
+endif()
+
+if(CMAKE_SYSTEM_NAME MATCHES "(Solaris|SunOS)")
+ list(APPEND LIBGIT2_SYSTEM_LIBS socket nsl)
+ list(APPEND LIBGIT2_PC_LIBS "-lsocket" "-lnsl")
+endif()
+
+if(CMAKE_SYSTEM_NAME MATCHES "Haiku")
+ list(APPEND LIBGIT2_SYSTEM_LIBS gnu network)
+ list(APPEND LIBGIT2_PC_LIBS "-lgnu -lnetwork")
+endif()
+
+if(AMIGA)
+ add_definitions(-DNO_ADDRINFO -DNO_READDIR_R -DNO_MMAP)
+endif()
+
+# threads
+
+if(USE_THREADS)
+ if(NOT WIN32)
+ find_package(Threads REQUIRED)
+ list(APPEND LIBGIT2_SYSTEM_LIBS ${CMAKE_THREAD_LIBS_INIT})
+ list(APPEND LIBGIT2_PC_LIBS ${CMAKE_THREAD_LIBS_INIT})
+ endif()
+
+ set(GIT_THREADS 1)
+endif()
+add_feature_info(threadsafe USE_THREADS "threadsafe support")
+
+#
+# Optional bundled features
+#
+
+# ntlmclient
+if(USE_NTLMCLIENT)
+ set(GIT_NTLM 1)
+ add_subdirectory("${PROJECT_SOURCE_DIR}/deps/ntlmclient" "${PROJECT_BINARY_DIR}/deps/ntlmclient")
+ list(APPEND LIBGIT2_DEPENDENCY_INCLUDES "${PROJECT_SOURCE_DIR}/deps/ntlmclient")
+ list(APPEND LIBGIT2_DEPENDENCY_OBJECTS "$<TARGET_OBJECTS:ntlmclient>")
+endif()
+add_feature_info(ntlmclient GIT_NTLM "NTLM authentication support for Unix")
+
+#
+# Optional external dependencies
+
+# iconv
+if(USE_ICONV)
+ find_package(Iconv)
+endif()
+if(ICONV_FOUND)
+ set(GIT_USE_ICONV 1)
+ list(APPEND LIBGIT2_SYSTEM_INCLUDES ${ICONV_INCLUDE_DIR})
+ list(APPEND LIBGIT2_SYSTEM_LIBS ${ICONV_LIBRARIES})
+ list(APPEND LIBGIT2_PC_LIBS ${ICONV_LIBRARIES})
+endif()
+add_feature_info(iconv GIT_USE_ICONV "iconv encoding conversion support")
+
+#
+# Include child projects
+#
+
+add_subdirectory(libgit2)
+add_subdirectory(util)
+
+if(BUILD_CLI)
+ add_subdirectory(cli)
+endif()
+
+# re-export these to the root so that peer projects (tests, fuzzers,
+# examples) can use them
+set(LIBGIT2_INCLUDES ${LIBGIT2_INCLUDES} PARENT_SCOPE)
+set(LIBGIT2_OBJECTS ${LIBGIT2_OBJECTS} PARENT_SCOPE)
+set(LIBGIT2_DEPENDENCY_INCLUDES ${LIBGIT2_DEPENDENCY_INCLUDES} PARENT_SCOPE)
+set(LIBGIT2_DEPENDENCY_OBJECTS ${LIBGIT2_DEPENDENCY_OBJECTS} PARENT_SCOPE)
+set(LIBGIT2_SYSTEM_INCLUDES ${LIBGIT2_SYSTEM_INCLUDES} PARENT_SCOPE)
+set(LIBGIT2_SYSTEM_LIBS ${LIBGIT2_SYSTEM_LIBS} PARENT_SCOPE)
diff --git a/src/README.md b/src/README.md
new file mode 100644
index 0000000..10b86c1
--- /dev/null
+++ b/src/README.md
@@ -0,0 +1,12 @@
+# libgit2 sources
+
+This is the source that makes up the core of libgit2 and its related
+projects.
+
+* `cli`
+ A git-compatible command-line interface that uses libgit2.
+* `libgit2`
+ This is the libgit2 project, a cross-platform, linkable library
+ implementation of Git that you can use in your application.
+* `util`
+ A shared utility library for these projects.
diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt
new file mode 100644
index 0000000..84b6c19
--- /dev/null
+++ b/src/cli/CMakeLists.txt
@@ -0,0 +1,56 @@
+set(CLI_INCLUDES
+ "${libgit2_BINARY_DIR}/src/util"
+ "${libgit2_BINARY_DIR}/include"
+ "${libgit2_SOURCE_DIR}/src/util"
+ "${libgit2_SOURCE_DIR}/src/cli"
+ "${libgit2_SOURCE_DIR}/include"
+ "${LIBGIT2_DEPENDENCY_INCLUDES}")
+
+if(WIN32 AND NOT CYGWIN)
+ file(GLOB CLI_SRC_OS win32/*.c)
+ list(SORT CLI_SRC_OS)
+else()
+ file(GLOB CLI_SRC_OS unix/*.c)
+ list(SORT CLI_SRC_OS)
+endif()
+
+file(GLOB CLI_SRC_C *.c *.h)
+list(SORT CLI_SRC_C)
+
+#
+# The CLI currently needs to be statically linked against libgit2 because
+# the utility library uses libgit2's thread-local error buffers. TODO:
+# remove this dependency and allow us to dynamically link against libgit2.
+#
+
+if(BUILD_CLI STREQUAL "dynamic")
+ set(CLI_LIBGIT2_LIBRARY libgit2package)
+else()
+ set(CLI_LIBGIT2_OBJECTS $<TARGET_OBJECTS:libgit2>)
+endif()
+
+#
+# Compile and link the CLI
+#
+
+add_executable(git2_cli ${CLI_SRC_C} ${CLI_SRC_OS} ${CLI_OBJECTS}
+ $<TARGET_OBJECTS:util>
+ ${CLI_LIBGIT2_OBJECTS}
+ ${LIBGIT2_DEPENDENCY_OBJECTS})
+target_link_libraries(git2_cli ${CLI_LIBGIT2_LIBRARY} ${LIBGIT2_SYSTEM_LIBS})
+
+set_target_properties(git2_cli PROPERTIES C_STANDARD 90)
+set_target_properties(git2_cli PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${libgit2_BINARY_DIR})
+set_target_properties(git2_cli PROPERTIES OUTPUT_NAME ${LIBGIT2_FILENAME})
+
+ide_split_sources(git2_cli)
+
+target_include_directories(git2_cli PRIVATE ${CLI_INCLUDES})
+
+if(MSVC_IDE)
+ # Precompiled headers
+ set_target_properties(git2_cli PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h")
+ set_source_files_properties(win32/precompiled.c COMPILE_FLAGS "/Ycprecompiled.h")
+endif()
+
+install(TARGETS git2_cli RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
diff --git a/src/cli/README.md b/src/cli/README.md
new file mode 100644
index 0000000..3087c39
--- /dev/null
+++ b/src/cli/README.md
@@ -0,0 +1,26 @@
+# cli
+
+A git-compatible command-line interface that uses libgit2.
+
+## Adding commands
+
+1. Individual commands have a `main`-like top-level entrypoint. For example:
+
+ ```c
+ int cmd_help(int argc, char **argv)
+ ```
+
+ Although this is the same signature as `main`, commands are not built as
+ individual standalone executables, they'll be linked into the main cli.
+ (Though there may be an option for command executables to be built as
+ standalone executables in the future.)
+
+2. Commands are prototyped in `cmd.h` and added to `main.c`'s list of
+ commands (`cli_cmds[]`). Commands should be specified with their name,
+ entrypoint and a brief description that can be printed in `git help`.
+ This is done because commands are linked into the main cli.
+
+3. Commands should accept a `--help` option that displays their help
+ information. This will be shown when a user runs `<command> --help` and
+ when a user runs `help <command>`.
+
diff --git a/src/cli/cli.h b/src/cli/cli.h
new file mode 100644
index 0000000..7dede67
--- /dev/null
+++ b/src/cli/cli.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef CLI_cli_h__
+#define CLI_cli_h__
+
+#define PROGRAM_NAME "git2"
+
+#include "git2_util.h"
+
+#include "error.h"
+#include "opt.h"
+#include "opt_usage.h"
+#include "sighandler.h"
+
+#endif /* CLI_cli_h__ */
diff --git a/src/cli/cmd.c b/src/cli/cmd.c
new file mode 100644
index 0000000..2a7e71c
--- /dev/null
+++ b/src/cli/cmd.c
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "cli.h"
+#include "cmd.h"
+
+const cli_cmd_spec *cli_cmd_spec_byname(const char *name)
+{
+ const cli_cmd_spec *cmd;
+
+ for (cmd = cli_cmds; cmd->name; cmd++) {
+ if (!strcmp(cmd->name, name))
+ return cmd;
+ }
+
+ return NULL;
+}
diff --git a/src/cli/cmd.h b/src/cli/cmd.h
new file mode 100644
index 0000000..8b1a1b3
--- /dev/null
+++ b/src/cli/cmd.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef CLI_cmd_h__
+#define CLI_cmd_h__
+
+/* Command definitions */
+typedef struct {
+ const char *name;
+ int (*fn)(int argc, char **argv);
+ const char *desc;
+} cli_cmd_spec;
+
+/* Options that are common to all commands (eg --help, --git-dir) */
+extern const cli_opt_spec cli_common_opts[];
+
+/* All the commands supported by the CLI */
+extern const cli_cmd_spec cli_cmds[];
+
+/* Find a command by name */
+extern const cli_cmd_spec *cli_cmd_spec_byname(const char *name);
+
+/* Commands */
+extern int cmd_cat_file(int argc, char **argv);
+extern int cmd_clone(int argc, char **argv);
+extern int cmd_hash_object(int argc, char **argv);
+extern int cmd_help(int argc, char **argv);
+
+#endif /* CLI_cmd_h__ */
diff --git a/src/cli/cmd_cat_file.c b/src/cli/cmd_cat_file.c
new file mode 100644
index 0000000..fb53a72
--- /dev/null
+++ b/src/cli/cmd_cat_file.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include <git2.h>
+#include "cli.h"
+#include "cmd.h"
+
+#define COMMAND_NAME "cat-file"
+
+typedef enum {
+ DISPLAY_CONTENT = 0,
+ DISPLAY_EXISTS,
+ DISPLAY_PRETTY,
+ DISPLAY_SIZE,
+ DISPLAY_TYPE
+} display_t;
+
+static int show_help;
+static int display = DISPLAY_CONTENT;
+static char *type_name, *object_spec;
+
+static const cli_opt_spec opts[] = {
+ { CLI_OPT_TYPE_SWITCH, "help", 0, &show_help, 1,
+ CLI_OPT_USAGE_HIDDEN | CLI_OPT_USAGE_STOP_PARSING, NULL,
+ "display help about the " COMMAND_NAME " command" },
+
+ { CLI_OPT_TYPE_SWITCH, NULL, 't', &display, DISPLAY_TYPE,
+ CLI_OPT_USAGE_REQUIRED, NULL, "display the type of the object" },
+ { CLI_OPT_TYPE_SWITCH, NULL, 's', &display, DISPLAY_SIZE,
+ CLI_OPT_USAGE_CHOICE, NULL, "display the size of the object" },
+ { CLI_OPT_TYPE_SWITCH, NULL, 'e', &display, DISPLAY_EXISTS,
+ CLI_OPT_USAGE_CHOICE, NULL, "displays nothing unless the object is corrupt" },
+ { CLI_OPT_TYPE_SWITCH, NULL, 'p', &display, DISPLAY_PRETTY,
+ CLI_OPT_USAGE_CHOICE, NULL, "pretty-print the object" },
+ { CLI_OPT_TYPE_ARG, "type", 0, &type_name, 0,
+ CLI_OPT_USAGE_CHOICE, "type", "the type of object to display" },
+ { CLI_OPT_TYPE_ARG, "object", 0, &object_spec, 0,
+ CLI_OPT_USAGE_REQUIRED, "object", "the object to display" },
+ { 0 },
+};
+
+static void print_help(void)
+{
+ cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts);
+ printf("\n");
+
+ printf("Display the content for the given object in the repository.\n");
+ printf("\n");
+
+ printf("Options:\n");
+
+ cli_opt_help_fprint(stdout, opts);
+}
+
+static int print_odb(git_object *object, display_t display)
+{
+ git_odb *odb = NULL;
+ git_odb_object *odb_object = NULL;
+ const unsigned char *content;
+ git_object_size_t size;
+ int ret = 0;
+
+ /*
+ * Our parsed blobs retain the raw content; all other objects are
+ * parsed into a working representation. To get the raw content,
+ * we need to do an ODB lookup. (Thankfully, this should be cached
+ * in-memory from our last call.)
+ */
+ if (git_object_type(object) == GIT_OBJECT_BLOB) {
+ content = git_blob_rawcontent((git_blob *)object);
+ size = git_blob_rawsize((git_blob *)object);
+ } else {
+ if (git_repository_odb(&odb, git_object_owner(object)) < 0 ||
+ git_odb_read(&odb_object, odb, git_object_id(object)) < 0) {
+ ret = cli_error_git();
+ goto done;
+ }
+
+ content = git_odb_object_data(odb_object);
+ size = git_odb_object_size(odb_object);
+ }
+
+ switch (display) {
+ case DISPLAY_SIZE:
+ if (printf("%" PRIu64 "\n", size) < 0)
+ ret = cli_error_os();
+ break;
+ case DISPLAY_CONTENT:
+ if (p_write(fileno(stdout), content, (size_t)size) < 0)
+ ret = cli_error_os();
+ break;
+ default:
+ GIT_ASSERT(0);
+ }
+
+done:
+ git_odb_object_free(odb_object);
+ git_odb_free(odb);
+ return ret;
+}
+
+static int print_type(git_object *object)
+{
+ if (printf("%s\n", git_object_type2string(git_object_type(object))) < 0)
+ return cli_error_os();
+
+ return 0;
+}
+
+static int print_pretty(git_object *object)
+{
+ const git_tree_entry *entry;
+ size_t i, count;
+
+ /*
+ * Only trees are stored in an unreadable format and benefit from
+ * pretty-printing.
+ */
+ if (git_object_type(object) != GIT_OBJECT_TREE)
+ return print_odb(object, DISPLAY_CONTENT);
+
+ for (i = 0, count = git_tree_entrycount((git_tree *)object); i < count; i++) {
+ entry = git_tree_entry_byindex((git_tree *)object, i);
+
+ if (printf("%06o %s %s\t%s\n",
+ git_tree_entry_filemode_raw(entry),
+ git_object_type2string(git_tree_entry_type(entry)),
+ git_oid_tostr_s(git_tree_entry_id(entry)),
+ git_tree_entry_name(entry)) < 0)
+ return cli_error_os();
+ }
+
+ return 0;
+}
+
+int cmd_cat_file(int argc, char **argv)
+{
+ git_repository *repo = NULL;
+ git_object *object = NULL;
+ git_object_t type;
+ cli_opt invalid_opt;
+ int giterr, ret = 0;
+
+ if (cli_opt_parse(&invalid_opt, opts, argv + 1, argc - 1, CLI_OPT_PARSE_GNU))
+ return cli_opt_usage_error(COMMAND_NAME, opts, &invalid_opt);
+
+ if (show_help) {
+ print_help();
+ return 0;
+ }
+
+ if (git_repository_open_ext(&repo, ".", GIT_REPOSITORY_OPEN_FROM_ENV, NULL) < 0)
+ return cli_error_git();
+
+ if ((giterr = git_revparse_single(&object, repo, object_spec)) < 0) {
+ if (display == DISPLAY_EXISTS && giterr == GIT_ENOTFOUND)
+ ret = 1;
+ else
+ ret = cli_error_git();
+
+ goto done;
+ }
+
+ if (type_name) {
+ git_object *peeled;
+
+ if ((type = git_object_string2type(type_name)) == GIT_OBJECT_INVALID) {
+ ret = cli_error_usage("invalid object type '%s'", type_name);
+ goto done;
+ }
+
+ if (git_object_peel(&peeled, object, type) < 0) {
+ ret = cli_error_git();
+ goto done;
+ }
+
+ git_object_free(object);
+ object = peeled;
+ }
+
+ switch (display) {
+ case DISPLAY_EXISTS:
+ ret = 0;
+ break;
+ case DISPLAY_TYPE:
+ ret = print_type(object);
+ break;
+ case DISPLAY_PRETTY:
+ ret = print_pretty(object);
+ break;
+ default:
+ ret = print_odb(object, display);
+ break;
+ }
+
+done:
+ git_object_free(object);
+ git_repository_free(repo);
+ return ret;
+}
diff --git a/src/cli/cmd_clone.c b/src/cli/cmd_clone.c
new file mode 100644
index 0000000..e477625
--- /dev/null
+++ b/src/cli/cmd_clone.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include <stdio.h>
+#include <git2.h>
+#include "cli.h"
+#include "cmd.h"
+#include "error.h"
+#include "sighandler.h"
+#include "progress.h"
+
+#include "fs_path.h"
+#include "futils.h"
+
+#define COMMAND_NAME "clone"
+
+static char *branch, *remote_path, *local_path, *depth;
+static int show_help, quiet, checkout = 1, bare;
+static bool local_path_exists;
+static cli_progress progress = CLI_PROGRESS_INIT;
+
+static const cli_opt_spec opts[] = {
+ { CLI_OPT_TYPE_SWITCH, "help", 0, &show_help, 1,
+ CLI_OPT_USAGE_HIDDEN | CLI_OPT_USAGE_STOP_PARSING, NULL,
+ "display help about the " COMMAND_NAME " command" },
+
+ { CLI_OPT_TYPE_SWITCH, "quiet", 'q', &quiet, 1,
+ CLI_OPT_USAGE_DEFAULT, NULL, "display the type of the object" },
+ { CLI_OPT_TYPE_SWITCH, "no-checkout", 'n', &checkout, 0,
+ CLI_OPT_USAGE_DEFAULT, NULL, "don't checkout HEAD" },
+ { CLI_OPT_TYPE_SWITCH, "bare", 0, &bare, 1,
+ CLI_OPT_USAGE_DEFAULT, NULL, "don't create a working directory" },
+ { CLI_OPT_TYPE_VALUE, "branch", 'b', &branch, 0,
+ CLI_OPT_USAGE_DEFAULT, "name", "branch to check out" },
+ { CLI_OPT_TYPE_VALUE, "depth", 0, &depth, 0,
+ CLI_OPT_USAGE_DEFAULT, "depth", "commit depth to check out " },
+ { CLI_OPT_TYPE_LITERAL },
+ { CLI_OPT_TYPE_ARG, "repository", 0, &remote_path, 0,
+ CLI_OPT_USAGE_REQUIRED, "repository", "repository path" },
+ { CLI_OPT_TYPE_ARG, "directory", 0, &local_path, 0,
+ CLI_OPT_USAGE_DEFAULT, "directory", "directory to clone into" },
+ { 0 }
+};
+
+static void print_help(void)
+{
+ cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts);
+ printf("\n");
+
+ printf("Clone a repository into a new directory.\n");
+ printf("\n");
+
+ printf("Options:\n");
+
+ cli_opt_help_fprint(stdout, opts);
+}
+
+static char *compute_local_path(const char *orig_path)
+{
+ const char *slash;
+ char *local_path;
+
+ if ((slash = strrchr(orig_path, '/')) == NULL &&
+ (slash = strrchr(orig_path, '\\')) == NULL)
+ local_path = git__strdup(orig_path);
+ else
+ local_path = git__strdup(slash + 1);
+
+ return local_path;
+}
+
+static int compute_depth(const char *depth)
+{
+ int64_t i;
+ const char *endptr;
+
+ if (!depth)
+ return 0;
+
+ if (git__strntol64(&i, depth, strlen(depth), &endptr, 10) < 0 || i < 0 || i > INT_MAX || *endptr) {
+ fprintf(stderr, "fatal: depth '%s' is not valid.\n", depth);
+ exit(128);
+ }
+
+ return (int)i;
+}
+
+static bool validate_local_path(const char *path)
+{
+ if (!git_fs_path_exists(path))
+ return false;
+
+ if (!git_fs_path_isdir(path) || !git_fs_path_is_empty_dir(path)) {
+ fprintf(stderr, "fatal: destination path '%s' already exists and is not an empty directory.\n",
+ path);
+ exit(128);
+ }
+
+ return true;
+}
+
+static void cleanup(void)
+{
+ int rmdir_flags = GIT_RMDIR_REMOVE_FILES;
+
+ cli_progress_abort(&progress);
+
+ if (local_path_exists)
+ rmdir_flags |= GIT_RMDIR_SKIP_ROOT;
+
+ if (!git_fs_path_isdir(local_path))
+ return;
+
+ git_futils_rmdir_r(local_path, NULL, rmdir_flags);
+}
+
+static void interrupt_cleanup(void)
+{
+ cleanup();
+ exit(130);
+}
+
+int cmd_clone(int argc, char **argv)
+{
+ git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT;
+ git_repository *repo = NULL;
+ cli_opt invalid_opt;
+ char *computed_path = NULL;
+ int ret = 0;
+
+ if (cli_opt_parse(&invalid_opt, opts, argv + 1, argc - 1, CLI_OPT_PARSE_GNU))
+ return cli_opt_usage_error(COMMAND_NAME, opts, &invalid_opt);
+
+ if (show_help) {
+ print_help();
+ return 0;
+ }
+
+ if (!remote_path) {
+ ret = cli_error_usage("you must specify a repository to clone");
+ goto done;
+ }
+
+ clone_opts.bare = !!bare;
+ clone_opts.checkout_branch = branch;
+ clone_opts.fetch_opts.depth = compute_depth(depth);
+
+ if (!checkout)
+ clone_opts.checkout_opts.checkout_strategy = GIT_CHECKOUT_NONE;
+
+ if (!local_path)
+ local_path = computed_path = compute_local_path(remote_path);
+
+ local_path_exists = validate_local_path(local_path);
+
+ cli_sighandler_set_interrupt(interrupt_cleanup);
+
+ if (!local_path_exists &&
+ git_futils_mkdir(local_path, 0777, 0) < 0) {
+ ret = cli_error_git();
+ goto done;
+ }
+
+ if (!quiet) {
+ clone_opts.fetch_opts.callbacks.sideband_progress = cli_progress_fetch_sideband;
+ clone_opts.fetch_opts.callbacks.transfer_progress = cli_progress_fetch_transfer;
+ clone_opts.fetch_opts.callbacks.payload = &progress;
+
+ clone_opts.checkout_opts.progress_cb = cli_progress_checkout;
+ clone_opts.checkout_opts.progress_payload = &progress;
+
+ printf("Cloning into '%s'...\n", local_path);
+ }
+
+ if (git_clone(&repo, remote_path, local_path, &clone_opts) < 0) {
+ cleanup();
+ ret = cli_error_git();
+ goto done;
+ }
+
+ cli_progress_finish(&progress);
+
+done:
+ cli_progress_dispose(&progress);
+ git__free(computed_path);
+ git_repository_free(repo);
+ return ret;
+}
diff --git a/src/cli/cmd_hash_object.c b/src/cli/cmd_hash_object.c
new file mode 100644
index 0000000..93b980d
--- /dev/null
+++ b/src/cli/cmd_hash_object.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include <git2.h>
+#include "cli.h"
+#include "cmd.h"
+
+#include "futils.h"
+
+#define COMMAND_NAME "hash-object"
+
+static int show_help;
+static char *type_name;
+static int write_object, read_stdin, literally;
+static char **filenames;
+
+static const cli_opt_spec opts[] = {
+ { CLI_OPT_TYPE_SWITCH, "help", 0, &show_help, 1,
+ CLI_OPT_USAGE_HIDDEN | CLI_OPT_USAGE_STOP_PARSING, NULL,
+ "display help about the " COMMAND_NAME " command" },
+
+ { CLI_OPT_TYPE_VALUE, NULL, 't', &type_name, 0,
+ CLI_OPT_USAGE_DEFAULT, "type", "the type of object to hash (default: \"blob\")" },
+ { CLI_OPT_TYPE_SWITCH, NULL, 'w', &write_object, 1,
+ CLI_OPT_USAGE_DEFAULT, NULL, "write the object to the object database" },
+ { CLI_OPT_TYPE_SWITCH, "literally", 0, &literally, 1,
+ CLI_OPT_USAGE_DEFAULT, NULL, "do not validate the object contents" },
+ { CLI_OPT_TYPE_SWITCH, "stdin", 0, &read_stdin, 1,
+ CLI_OPT_USAGE_REQUIRED, NULL, "read content from stdin" },
+ { CLI_OPT_TYPE_ARGS, "file", 0, &filenames, 0,
+ CLI_OPT_USAGE_CHOICE, "file", "the file (or files) to read and hash" },
+ { 0 },
+};
+
+static void print_help(void)
+{
+ cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts);
+ printf("\n");
+
+ printf("Compute the object ID for a given file and optionally write that file\nto the object database.\n");
+ printf("\n");
+
+ printf("Options:\n");
+
+ cli_opt_help_fprint(stdout, opts);
+}
+
+static int hash_buf(
+ git_odb *odb,
+ git_str *buf,
+ git_object_t object_type,
+ git_oid_t oid_type)
+{
+ git_oid oid;
+
+ if (!literally) {
+ int valid = 0;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ if (git_object_rawcontent_is_valid(&valid, buf->ptr, buf->size, object_type, oid_type) < 0 || !valid)
+ return cli_error_git();
+#else
+ GIT_UNUSED(oid_type);
+
+ if (git_object_rawcontent_is_valid(&valid, buf->ptr, buf->size, object_type) < 0 || !valid)
+ return cli_error_git();
+#endif
+ }
+
+ if (write_object) {
+ if (git_odb_write(&oid, odb, buf->ptr, buf->size, object_type) < 0)
+ return cli_error_git();
+ } else {
+#ifdef GIT_EXPERIMENTAL_SHA256
+ if (git_odb_hash(&oid, buf->ptr, buf->size, object_type, GIT_OID_SHA1) < 0)
+ return cli_error_git();
+#else
+ if (git_odb_hash(&oid, buf->ptr, buf->size, object_type) < 0)
+ return cli_error_git();
+#endif
+ }
+
+ if (printf("%s\n", git_oid_tostr_s(&oid)) < 0)
+ return cli_error_os();
+
+ return 0;
+}
+
+int cmd_hash_object(int argc, char **argv)
+{
+ git_repository *repo = NULL;
+ git_odb *odb = NULL;
+ git_oid_t oid_type;
+ git_str buf = GIT_STR_INIT;
+ cli_opt invalid_opt;
+ git_object_t object_type = GIT_OBJECT_BLOB;
+ char **filename;
+ int ret = 0;
+
+ if (cli_opt_parse(&invalid_opt, opts, argv + 1, argc - 1, CLI_OPT_PARSE_GNU))
+ return cli_opt_usage_error(COMMAND_NAME, opts, &invalid_opt);
+
+ if (show_help) {
+ print_help();
+ return 0;
+ }
+
+ if (type_name && (object_type = git_object_string2type(type_name)) == GIT_OBJECT_INVALID)
+ return cli_error_usage("invalid object type '%s'", type_name);
+
+ if (write_object &&
+ (git_repository_open_ext(&repo, ".", GIT_REPOSITORY_OPEN_FROM_ENV, NULL) < 0 ||
+ git_repository_odb(&odb, repo) < 0)) {
+ ret = cli_error_git();
+ goto done;
+ }
+
+ oid_type = git_repository_oid_type(repo);
+
+ /*
+ * TODO: we're reading blobs, we shouldn't pull them all into main
+ * memory, we should just stream them into the odb instead.
+ * (Or create a `git_odb_writefile` API.)
+ */
+ if (read_stdin) {
+ if (git_futils_readbuffer_fd_full(&buf, fileno(stdin)) < 0) {
+ ret = cli_error_git();
+ goto done;
+ }
+
+ if ((ret = hash_buf(odb, &buf, object_type, oid_type)) != 0)
+ goto done;
+ } else {
+ for (filename = filenames; *filename; filename++) {
+ if (git_futils_readbuffer(&buf, *filename) < 0) {
+ ret = cli_error_git();
+ goto done;
+ }
+
+ if ((ret = hash_buf(odb, &buf, object_type, oid_type)) != 0)
+ goto done;
+ }
+ }
+
+done:
+ git_str_dispose(&buf);
+ git_odb_free(odb);
+ git_repository_free(repo);
+ return ret;
+}
diff --git a/src/cli/cmd_help.c b/src/cli/cmd_help.c
new file mode 100644
index 0000000..7ee9822
--- /dev/null
+++ b/src/cli/cmd_help.c
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include <stdio.h>
+#include <git2.h>
+#include "cli.h"
+#include "cmd.h"
+
+#define COMMAND_NAME "help"
+
+static char *command;
+static int show_help;
+
+static const cli_opt_spec opts[] = {
+ { CLI_OPT_TYPE_SWITCH, "help", 0, &show_help, 1,
+ CLI_OPT_USAGE_HIDDEN, NULL, "display help about the help command" },
+ { CLI_OPT_TYPE_ARG, "command", 0, &command, 0,
+ CLI_OPT_USAGE_DEFAULT, "command", "the command to show help for" },
+ { 0 },
+};
+
+static int print_help(void)
+{
+ cli_opt_usage_fprint(stdout, PROGRAM_NAME, COMMAND_NAME, opts);
+ printf("\n");
+
+ printf("Display help information about %s. If a command is specified, help\n", PROGRAM_NAME);
+ printf("about that command will be shown. Otherwise, general information about\n");
+ printf("%s will be shown, including the commands available.\n", PROGRAM_NAME);
+
+ return 0;
+}
+
+static int print_commands(void)
+{
+ const cli_cmd_spec *cmd;
+
+ cli_opt_usage_fprint(stdout, PROGRAM_NAME, NULL, cli_common_opts);
+ printf("\n");
+
+ printf("These are the %s commands available:\n\n", PROGRAM_NAME);
+
+ for (cmd = cli_cmds; cmd->name; cmd++)
+ printf(" %-11s %s\n", cmd->name, cmd->desc);
+
+ printf("\nSee '%s help <command>' for more information on a specific command.\n", PROGRAM_NAME);
+
+ return 0;
+}
+
+int cmd_help(int argc, char **argv)
+{
+ char *fake_args[2];
+ const cli_cmd_spec *cmd;
+ cli_opt invalid_opt;
+
+ if (cli_opt_parse(&invalid_opt, opts, argv + 1, argc - 1, CLI_OPT_PARSE_GNU))
+ return cli_opt_usage_error(COMMAND_NAME, opts, &invalid_opt);
+
+ /* Show the meta-help */
+ if (show_help)
+ return print_help();
+
+ /* We were not asked to show help for a specific command. */
+ if (!command)
+ return print_commands();
+
+ /*
+ * If we were asked for help for a command (eg, `help <command>`),
+ * delegate back to that command's `--help` option. This lets
+ * commands own their help. Emulate the command-line arguments
+ * that would invoke `<command> --help` and invoke that command.
+ */
+ fake_args[0] = command;
+ fake_args[1] = "--help";
+
+ if ((cmd = cli_cmd_spec_byname(command)) == NULL)
+ return cli_error("'%s' is not a %s command. See '%s help'.",
+ command, PROGRAM_NAME, PROGRAM_NAME);
+
+ return cmd->fn(2, fake_args);
+}
diff --git a/src/cli/error.h b/src/cli/error.h
new file mode 100644
index 0000000..cce7a54
--- /dev/null
+++ b/src/cli/error.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef CLI_error_h__
+#define CLI_error_h__
+
+#include "cli.h"
+#include <stdio.h>
+
+#define CLI_EXIT_OK 0
+#define CLI_EXIT_ERROR 1
+#define CLI_EXIT_OS 128
+#define CLI_EXIT_GIT 128
+#define CLI_EXIT_USAGE 129
+
+#define cli_error__print(fmt) do { \
+ va_list ap; \
+ va_start(ap, fmt); \
+ fprintf(stderr, "%s: ", PROGRAM_NAME); \
+ vfprintf(stderr, fmt, ap); \
+ fprintf(stderr, "\n"); \
+ va_end(ap); \
+ } while(0)
+
+GIT_INLINE(int) cli_error(const char *fmt, ...)
+{
+ cli_error__print(fmt);
+ return CLI_EXIT_ERROR;
+}
+
+GIT_INLINE(int) cli_error_usage(const char *fmt, ...)
+{
+ cli_error__print(fmt);
+ return CLI_EXIT_USAGE;
+}
+
+GIT_INLINE(int) cli_error_git(void)
+{
+ const git_error *err = git_error_last();
+ fprintf(stderr, "%s: %s\n", PROGRAM_NAME,
+ err ? err->message : "unknown error");
+ return CLI_EXIT_GIT;
+}
+
+#define cli_error_os() (perror(PROGRAM_NAME), CLI_EXIT_OS)
+
+#endif /* CLI_error_h__ */
diff --git a/src/cli/main.c b/src/cli/main.c
new file mode 100644
index 0000000..cbfc50e
--- /dev/null
+++ b/src/cli/main.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include <stdio.h>
+#include <git2.h>
+#include "cli.h"
+#include "cmd.h"
+
+static int show_help = 0;
+static int show_version = 0;
+static char *command = NULL;
+static char **args = NULL;
+
+const cli_opt_spec cli_common_opts[] = {
+ { CLI_OPT_TYPE_SWITCH, "help", 0, &show_help, 1,
+ CLI_OPT_USAGE_DEFAULT, NULL, "display help information" },
+ { CLI_OPT_TYPE_SWITCH, "version", 0, &show_version, 1,
+ CLI_OPT_USAGE_DEFAULT, NULL, "display the version" },
+ { CLI_OPT_TYPE_ARG, "command", 0, &command, 0,
+ CLI_OPT_USAGE_REQUIRED, "command", "the command to run" },
+ { CLI_OPT_TYPE_ARGS, "args", 0, &args, 0,
+ CLI_OPT_USAGE_DEFAULT, "args", "arguments for the command" },
+ { 0 }
+};
+
+const cli_cmd_spec cli_cmds[] = {
+ { "cat-file", cmd_cat_file, "Display an object in the repository" },
+ { "clone", cmd_clone, "Clone a repository into a new directory" },
+ { "hash-object", cmd_hash_object, "Hash a raw object and product its object ID" },
+ { "help", cmd_help, "Display help information" },
+ { NULL }
+};
+
+int main(int argc, char **argv)
+{
+ const cli_cmd_spec *cmd;
+ cli_opt_parser optparser;
+ cli_opt opt;
+ char *help_args[3] = { NULL };
+ int help_args_len;
+ int args_len = 0;
+ int ret = 0;
+
+ if (git_libgit2_init() < 0) {
+ cli_error("failed to initialize libgit2");
+ exit(CLI_EXIT_GIT);
+ }
+
+ cli_opt_parser_init(&optparser, cli_common_opts, argv + 1, argc - 1, CLI_OPT_PARSE_GNU);
+
+ /* Parse the top-level (common) options and command information */
+ while (cli_opt_parser_next(&opt, &optparser)) {
+ if (!opt.spec) {
+ cli_opt_status_fprint(stderr, PROGRAM_NAME, &opt);
+ cli_opt_usage_fprint(stderr, PROGRAM_NAME, NULL, cli_common_opts);
+ ret = CLI_EXIT_USAGE;
+ goto done;
+ }
+
+ /*
+ * When we see a command, stop parsing and capture the
+ * remaining arguments as args for the command itself.
+ */
+ if (command) {
+ args = &argv[optparser.idx];
+ args_len = (int)(argc - optparser.idx);
+ break;
+ }
+ }
+
+ if (show_version) {
+ printf("%s version %s\n", PROGRAM_NAME, LIBGIT2_VERSION);
+ goto done;
+ }
+
+ /*
+ * If `--help <command>` is specified, delegate to that command's
+ * `--help` option. If no command is specified, run the `help`
+ * command. Do this by updating the args to emulate that behavior.
+ */
+ if (!command || show_help) {
+ help_args[0] = command ? (char *)command : "help";
+ help_args[1] = command ? "--help" : NULL;
+ help_args_len = command ? 2 : 1;
+
+ command = help_args[0];
+ args = help_args;
+ args_len = help_args_len;
+ }
+
+ if ((cmd = cli_cmd_spec_byname(command)) == NULL) {
+ ret = cli_error("'%s' is not a %s command. See '%s help'.",
+ command, PROGRAM_NAME, PROGRAM_NAME);
+ goto done;
+ }
+
+ ret = cmd->fn(args_len, args);
+
+done:
+ git_libgit2_shutdown();
+ return ret;
+}
diff --git a/src/cli/opt.c b/src/cli/opt.c
new file mode 100644
index 0000000..62a3430
--- /dev/null
+++ b/src/cli/opt.c
@@ -0,0 +1,669 @@
+/*
+ * Copyright (c), Edward Thomson <ethomson@edwardthomson.com>
+ * All rights reserved.
+ *
+ * This file is part of adopt, distributed under the MIT license.
+ * For full terms and conditions, see the included LICENSE file.
+ *
+ * THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT.
+ *
+ * This file was produced by using the `rename.pl` script included with
+ * adopt. The command-line specified was:
+ *
+ * ./rename.pl cli_opt --filename=opt --include=cli.h --inline=GIT_INLINE --header-guard=CLI_opt_h__ --lowercase-status --without-usage
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <limits.h>
+#include <assert.h>
+
+#include "cli.h"
+#include "opt.h"
+
+#ifdef _WIN32
+# include <windows.h>
+#else
+# include <fcntl.h>
+# include <sys/ioctl.h>
+#endif
+
+#ifdef _MSC_VER
+# define alloca _alloca
+#endif
+
+#define spec_is_option_type(x) \
+ ((x)->type == CLI_OPT_TYPE_BOOL || \
+ (x)->type == CLI_OPT_TYPE_SWITCH || \
+ (x)->type == CLI_OPT_TYPE_VALUE)
+
+GIT_INLINE(const cli_opt_spec *) spec_for_long(
+ int *is_negated,
+ int *has_value,
+ const char **value,
+ const cli_opt_parser *parser,
+ const char *arg)
+{
+ const cli_opt_spec *spec;
+ char *eql;
+ size_t eql_pos;
+
+ eql = strchr(arg, '=');
+ eql_pos = (eql = strchr(arg, '=')) ? (size_t)(eql - arg) : strlen(arg);
+
+ for (spec = parser->specs; spec->type; ++spec) {
+ /* Handle -- (everything after this is literal) */
+ if (spec->type == CLI_OPT_TYPE_LITERAL && arg[0] == '\0')
+ return spec;
+
+ /* Handle --no-option arguments for bool types */
+ if (spec->type == CLI_OPT_TYPE_BOOL &&
+ strncmp(arg, "no-", 3) == 0 &&
+ strcmp(arg + 3, spec->name) == 0) {
+ *is_negated = 1;
+ return spec;
+ }
+
+ /* Handle the typical --option arguments */
+ if (spec_is_option_type(spec) &&
+ spec->name &&
+ strcmp(arg, spec->name) == 0)
+ return spec;
+
+ /* Handle --option=value arguments */
+ if (spec->type == CLI_OPT_TYPE_VALUE &&
+ eql &&
+ strncmp(arg, spec->name, eql_pos) == 0 &&
+ spec->name[eql_pos] == '\0') {
+ *has_value = 1;
+ *value = arg[eql_pos + 1] ? &arg[eql_pos + 1] : NULL;
+ return spec;
+ }
+ }
+
+ return NULL;
+}
+
+GIT_INLINE(const cli_opt_spec *) spec_for_short(
+ const char **value,
+ const cli_opt_parser *parser,
+ const char *arg)
+{
+ const cli_opt_spec *spec;
+
+ for (spec = parser->specs; spec->type; ++spec) {
+ /* Handle -svalue short options with a value */
+ if (spec->type == CLI_OPT_TYPE_VALUE &&
+ arg[0] == spec->alias &&
+ arg[1] != '\0') {
+ *value = &arg[1];
+ return spec;
+ }
+
+ /* Handle typical -s short options */
+ if (arg[0] == spec->alias) {
+ *value = NULL;
+ return spec;
+ }
+ }
+
+ return NULL;
+}
+
+GIT_INLINE(const cli_opt_spec *) spec_for_arg(cli_opt_parser *parser)
+{
+ const cli_opt_spec *spec;
+ size_t args = 0;
+
+ for (spec = parser->specs; spec->type; ++spec) {
+ if (spec->type == CLI_OPT_TYPE_ARG) {
+ if (args == parser->arg_idx) {
+ parser->arg_idx++;
+ return spec;
+ }
+
+ args++;
+ }
+
+ if (spec->type == CLI_OPT_TYPE_ARGS && args == parser->arg_idx)
+ return spec;
+ }
+
+ return NULL;
+}
+
+GIT_INLINE(int) spec_is_choice(const cli_opt_spec *spec)
+{
+ return ((spec + 1)->type &&
+ ((spec + 1)->usage & CLI_OPT_USAGE_CHOICE));
+}
+
+/*
+ * If we have a choice with switches and bare arguments, and we see
+ * the switch, then we no longer expect the bare argument.
+ */
+GIT_INLINE(void) consume_choices(const cli_opt_spec *spec, cli_opt_parser *parser)
+{
+ /* back up to the beginning of the choices */
+ while (spec->type && (spec->usage & CLI_OPT_USAGE_CHOICE))
+ --spec;
+
+ if (!spec_is_choice(spec))
+ return;
+
+ do {
+ if (spec->type == CLI_OPT_TYPE_ARG)
+ parser->arg_idx++;
+ ++spec;
+ } while(spec->type && (spec->usage & CLI_OPT_USAGE_CHOICE));
+}
+
+static cli_opt_status_t parse_long(cli_opt *opt, cli_opt_parser *parser)
+{
+ const cli_opt_spec *spec;
+ char *arg = parser->args[parser->idx++];
+ const char *value = NULL;
+ int is_negated = 0, has_value = 0;
+
+ opt->arg = arg;
+
+ if ((spec = spec_for_long(&is_negated, &has_value, &value, parser, &arg[2])) == NULL) {
+ opt->spec = NULL;
+ opt->status = CLI_OPT_STATUS_UNKNOWN_OPTION;
+ goto done;
+ }
+
+ opt->spec = spec;
+
+ /* Future options parsed as literal */
+ if (spec->type == CLI_OPT_TYPE_LITERAL)
+ parser->in_literal = 1;
+
+ /* --bool or --no-bool */
+ else if (spec->type == CLI_OPT_TYPE_BOOL && spec->value)
+ *((int *)spec->value) = !is_negated;
+
+ /* --accumulate */
+ else if (spec->type == CLI_OPT_TYPE_ACCUMULATOR && spec->value)
+ *((int *)spec->value) += spec->switch_value ? spec->switch_value : 1;
+
+ /* --switch */
+ else if (spec->type == CLI_OPT_TYPE_SWITCH && spec->value)
+ *((int *)spec->value) = spec->switch_value;
+
+ /* Parse values as "--foo=bar" or "--foo bar" */
+ else if (spec->type == CLI_OPT_TYPE_VALUE) {
+ if (has_value)
+ opt->value = (char *)value;
+ else if ((parser->idx + 1) <= parser->args_len)
+ opt->value = parser->args[parser->idx++];
+
+ if (spec->value)
+ *((char **)spec->value) = opt->value;
+ }
+
+ /* Required argument was not provided */
+ if (spec->type == CLI_OPT_TYPE_VALUE &&
+ !opt->value &&
+ !(spec->usage & CLI_OPT_USAGE_VALUE_OPTIONAL))
+ opt->status = CLI_OPT_STATUS_MISSING_VALUE;
+ else
+ opt->status = CLI_OPT_STATUS_OK;
+
+ consume_choices(opt->spec, parser);
+
+done:
+ return opt->status;
+}
+
+static cli_opt_status_t parse_short(cli_opt *opt, cli_opt_parser *parser)
+{
+ const cli_opt_spec *spec;
+ char *arg = parser->args[parser->idx++];
+ const char *value;
+
+ opt->arg = arg;
+
+ if ((spec = spec_for_short(&value, parser, &arg[1 + parser->in_short])) == NULL) {
+ opt->spec = NULL;
+ opt->status = CLI_OPT_STATUS_UNKNOWN_OPTION;
+ goto done;
+ }
+
+ opt->spec = spec;
+
+ if (spec->type == CLI_OPT_TYPE_BOOL && spec->value)
+ *((int *)spec->value) = 1;
+
+ else if (spec->type == CLI_OPT_TYPE_ACCUMULATOR && spec->value)
+ *((int *)spec->value) += spec->switch_value ? spec->switch_value : 1;
+
+ else if (spec->type == CLI_OPT_TYPE_SWITCH && spec->value)
+ *((int *)spec->value) = spec->switch_value;
+
+ /* Parse values as "-ifoo" or "-i foo" */
+ else if (spec->type == CLI_OPT_TYPE_VALUE) {
+ if (value)
+ opt->value = (char *)value;
+ else if ((parser->idx + 1) <= parser->args_len)
+ opt->value = parser->args[parser->idx++];
+
+ if (spec->value)
+ *((char **)spec->value) = opt->value;
+ }
+
+ /*
+ * Handle compressed short arguments, like "-fbcd"; see if there's
+ * another character after the one we processed. If not, advance
+ * the parser index.
+ */
+ if (spec->type != CLI_OPT_TYPE_VALUE && arg[2 + parser->in_short] != '\0') {
+ parser->in_short++;
+ parser->idx--;
+ } else {
+ parser->in_short = 0;
+ }
+
+ /* Required argument was not provided */
+ if (spec->type == CLI_OPT_TYPE_VALUE && !opt->value)
+ opt->status = CLI_OPT_STATUS_MISSING_VALUE;
+ else
+ opt->status = CLI_OPT_STATUS_OK;
+
+ consume_choices(opt->spec, parser);
+
+done:
+ return opt->status;
+}
+
+static cli_opt_status_t parse_arg(cli_opt *opt, cli_opt_parser *parser)
+{
+ const cli_opt_spec *spec = spec_for_arg(parser);
+
+ opt->spec = spec;
+ opt->arg = parser->args[parser->idx];
+
+ if (!spec) {
+ parser->idx++;
+ opt->status = CLI_OPT_STATUS_UNKNOWN_OPTION;
+ } else if (spec->type == CLI_OPT_TYPE_ARGS) {
+ if (spec->value)
+ *((char ***)spec->value) = &parser->args[parser->idx];
+
+ /*
+ * We have started a list of arguments; the remainder of
+ * given arguments need not be examined.
+ */
+ parser->in_args = (parser->args_len - parser->idx);
+ parser->idx = parser->args_len;
+ opt->args_len = parser->in_args;
+ opt->status = CLI_OPT_STATUS_OK;
+ } else {
+ if (spec->value)
+ *((char **)spec->value) = parser->args[parser->idx];
+
+ parser->idx++;
+ opt->status = CLI_OPT_STATUS_OK;
+ }
+
+ return opt->status;
+}
+
+static int support_gnu_style(unsigned int flags)
+{
+ if ((flags & CLI_OPT_PARSE_FORCE_GNU) != 0)
+ return 1;
+
+ if ((flags & CLI_OPT_PARSE_GNU) == 0)
+ return 0;
+
+ /* TODO: Windows */
+#if defined(_WIN32) && defined(UNICODE)
+ if (_wgetenv(L"POSIXLY_CORRECT") != NULL)
+ return 0;
+#else
+ if (getenv("POSIXLY_CORRECT") != NULL)
+ return 0;
+#endif
+
+ return 1;
+}
+
+void cli_opt_parser_init(
+ cli_opt_parser *parser,
+ const cli_opt_spec specs[],
+ char **args,
+ size_t args_len,
+ unsigned int flags)
+{
+ assert(parser);
+
+ memset(parser, 0x0, sizeof(cli_opt_parser));
+
+ parser->specs = specs;
+ parser->args = args;
+ parser->args_len = args_len;
+ parser->flags = flags;
+
+ parser->needs_sort = support_gnu_style(flags);
+}
+
+GIT_INLINE(const cli_opt_spec *) spec_for_sort(
+ int *needs_value,
+ const cli_opt_parser *parser,
+ const char *arg)
+{
+ int is_negated, has_value = 0;
+ const char *value;
+ const cli_opt_spec *spec = NULL;
+ size_t idx = 0;
+
+ *needs_value = 0;
+
+ if (strncmp(arg, "--", 2) == 0) {
+ spec = spec_for_long(&is_negated, &has_value, &value, parser, &arg[2]);
+ *needs_value = !has_value;
+ }
+
+ else if (strncmp(arg, "-", 1) == 0) {
+ spec = spec_for_short(&value, parser, &arg[1]);
+
+ /*
+ * Advance through compressed short arguments to see if
+ * the last one has a value, eg "-xvffilename".
+ */
+ while (spec && !value && arg[1 + ++idx] != '\0')
+ spec = spec_for_short(&value, parser, &arg[1 + idx]);
+
+ *needs_value = (value == NULL);
+ }
+
+ return spec;
+}
+
+/*
+ * Some parsers allow for handling arguments like "file1 --help file2";
+ * this is done by re-sorting the arguments in-place; emulate that.
+ */
+static int sort_gnu_style(cli_opt_parser *parser)
+{
+ size_t i, j, insert_idx = parser->idx, offset;
+ const cli_opt_spec *spec;
+ char *option, *value;
+ int needs_value, changed = 0;
+
+ parser->needs_sort = 0;
+
+ for (i = parser->idx; i < parser->args_len; i++) {
+ spec = spec_for_sort(&needs_value, parser, parser->args[i]);
+
+ /* Not a "-" or "--" prefixed option. No change. */
+ if (!spec)
+ continue;
+
+ /* A "--" alone means remaining args are literal. */
+ if (spec->type == CLI_OPT_TYPE_LITERAL)
+ break;
+
+ option = parser->args[i];
+
+ /*
+ * If the argument is a value type and doesn't already
+ * have a value (eg "--foo=bar" or "-fbar") then we need
+ * to copy the next argument as its value.
+ */
+ if (spec->type == CLI_OPT_TYPE_VALUE && needs_value) {
+ /*
+ * A required value is not provided; set parser
+ * index to this value so that we fail on it.
+ */
+ if (i + 1 >= parser->args_len) {
+ parser->idx = i;
+ return 1;
+ }
+
+ value = parser->args[i + 1];
+ offset = 1;
+ } else {
+ value = NULL;
+ offset = 0;
+ }
+
+ /* Caller error if args[0] is an option. */
+ if (i == 0)
+ return 0;
+
+ /* Shift args up one (or two) and insert the option */
+ for (j = i; j > insert_idx; j--)
+ parser->args[j + offset] = parser->args[j - 1];
+
+ parser->args[insert_idx] = option;
+
+ if (value)
+ parser->args[insert_idx + 1] = value;
+
+ insert_idx += (1 + offset);
+ i += offset;
+
+ changed = 1;
+ }
+
+ return changed;
+}
+
+cli_opt_status_t cli_opt_parser_next(cli_opt *opt, cli_opt_parser *parser)
+{
+ assert(opt && parser);
+
+ memset(opt, 0x0, sizeof(cli_opt));
+
+ if (parser->idx >= parser->args_len) {
+ opt->args_len = parser->in_args;
+ return CLI_OPT_STATUS_DONE;
+ }
+
+ /* Handle options in long form, those beginning with "--" */
+ if (strncmp(parser->args[parser->idx], "--", 2) == 0 &&
+ !parser->in_short &&
+ !parser->in_literal)
+ return parse_long(opt, parser);
+
+ /* Handle options in short form, those beginning with "-" */
+ else if (parser->in_short ||
+ (strncmp(parser->args[parser->idx], "-", 1) == 0 &&
+ !parser->in_literal))
+ return parse_short(opt, parser);
+
+ /*
+ * We've reached the first "bare" argument. In POSIX mode, all
+ * remaining items on the command line are arguments. In GNU
+ * mode, there may be long or short options after this. Sort any
+ * options up to this position then re-parse the current position.
+ */
+ if (parser->needs_sort && sort_gnu_style(parser))
+ return cli_opt_parser_next(opt, parser);
+
+ return parse_arg(opt, parser);
+}
+
+GIT_INLINE(int) spec_included(const cli_opt_spec **specs, const cli_opt_spec *spec)
+{
+ const cli_opt_spec **i;
+
+ for (i = specs; *i; ++i) {
+ if (spec == *i)
+ return 1;
+ }
+
+ return 0;
+}
+
+static cli_opt_status_t validate_required(
+ cli_opt *opt,
+ const cli_opt_spec specs[],
+ const cli_opt_spec **given_specs)
+{
+ const cli_opt_spec *spec, *required;
+ int given;
+
+ /*
+ * Iterate over the possible specs to identify requirements and
+ * ensure that those have been given on the command-line.
+ * Note that we can have required *choices*, where one in a
+ * list of choices must be specified.
+ */
+ for (spec = specs, required = NULL, given = 0; spec->type; ++spec) {
+ if (!required && (spec->usage & CLI_OPT_USAGE_REQUIRED)) {
+ required = spec;
+ given = 0;
+ } else if (!required) {
+ continue;
+ }
+
+ if (!given)
+ given = spec_included(given_specs, spec);
+
+ /*
+ * Validate the requirement unless we're in a required
+ * choice. In that case, keep the required state and
+ * validate at the end of the choice list.
+ */
+ if (!spec_is_choice(spec)) {
+ if (!given) {
+ opt->spec = required;
+ opt->status = CLI_OPT_STATUS_MISSING_ARGUMENT;
+ break;
+ }
+
+ required = NULL;
+ given = 0;
+ }
+ }
+
+ return opt->status;
+}
+
+cli_opt_status_t cli_opt_parse(
+ cli_opt *opt,
+ const cli_opt_spec specs[],
+ char **args,
+ size_t args_len,
+ unsigned int flags)
+{
+ cli_opt_parser parser;
+ const cli_opt_spec **given_specs;
+ size_t given_idx = 0;
+
+ cli_opt_parser_init(&parser, specs, args, args_len, flags);
+
+ given_specs = alloca(sizeof(const cli_opt_spec *) * (args_len + 1));
+
+ while (cli_opt_parser_next(opt, &parser)) {
+ if (opt->status != CLI_OPT_STATUS_OK &&
+ opt->status != CLI_OPT_STATUS_DONE)
+ return opt->status;
+
+ if ((opt->spec->usage & CLI_OPT_USAGE_STOP_PARSING))
+ return (opt->status = CLI_OPT_STATUS_DONE);
+
+ given_specs[given_idx++] = opt->spec;
+ }
+
+ given_specs[given_idx] = NULL;
+
+ return validate_required(opt, specs, given_specs);
+}
+
+static int spec_name_fprint(FILE *file, const cli_opt_spec *spec)
+{
+ int error;
+
+ if (spec->type == CLI_OPT_TYPE_ARG)
+ error = fprintf(file, "%s", spec->value_name);
+ else if (spec->type == CLI_OPT_TYPE_ARGS)
+ error = fprintf(file, "%s", spec->value_name);
+ else if (spec->alias && !(spec->usage & CLI_OPT_USAGE_SHOW_LONG))
+ error = fprintf(file, "-%c", spec->alias);
+ else
+ error = fprintf(file, "--%s", spec->name);
+
+ return error;
+}
+
+int cli_opt_status_fprint(
+ FILE *file,
+ const char *command,
+ const cli_opt *opt)
+{
+ const cli_opt_spec *choice;
+ int error;
+
+ if (command && (error = fprintf(file, "%s: ", command)) < 0)
+ return error;
+
+ switch (opt->status) {
+ case CLI_OPT_STATUS_DONE:
+ error = fprintf(file, "finished processing arguments (no error)\n");
+ break;
+ case CLI_OPT_STATUS_OK:
+ error = fprintf(file, "no error\n");
+ break;
+ case CLI_OPT_STATUS_UNKNOWN_OPTION:
+ error = fprintf(file, "unknown option: %s\n", opt->arg);
+ break;
+ case CLI_OPT_STATUS_MISSING_VALUE:
+ if ((error = fprintf(file, "argument '")) < 0 ||
+ (error = spec_name_fprint(file, opt->spec)) < 0 ||
+ (error = fprintf(file, "' requires a value.\n")) < 0)
+ break;
+ break;
+ case CLI_OPT_STATUS_MISSING_ARGUMENT:
+ if (spec_is_choice(opt->spec)) {
+ int is_choice = 1;
+
+ if (spec_is_choice((opt->spec)+1))
+ error = fprintf(file, "one of");
+ else
+ error = fprintf(file, "either");
+
+ if (error < 0)
+ break;
+
+ for (choice = opt->spec; is_choice; ++choice) {
+ is_choice = spec_is_choice(choice);
+
+ if (!is_choice)
+ error = fprintf(file, " or");
+ else if (choice != opt->spec)
+ error = fprintf(file, ",");
+
+ if ((error < 0) ||
+ (error = fprintf(file, " '")) < 0 ||
+ (error = spec_name_fprint(file, choice)) < 0 ||
+ (error = fprintf(file, "'")) < 0)
+ break;
+
+ if (!spec_is_choice(choice))
+ break;
+ }
+
+ if ((error < 0) ||
+ (error = fprintf(file, " is required.\n")) < 0)
+ break;
+ } else {
+ if ((error = fprintf(file, "argument '")) < 0 ||
+ (error = spec_name_fprint(file, opt->spec)) < 0 ||
+ (error = fprintf(file, "' is required.\n")) < 0)
+ break;
+ }
+
+ break;
+ default:
+ error = fprintf(file, "unknown status: %d\n", opt->status);
+ break;
+ }
+
+ return error;
+}
+
diff --git a/src/cli/opt.h b/src/cli/opt.h
new file mode 100644
index 0000000..6c1d460
--- /dev/null
+++ b/src/cli/opt.h
@@ -0,0 +1,349 @@
+/*
+ * Copyright (c), Edward Thomson <ethomson@edwardthomson.com>
+ * All rights reserved.
+ *
+ * This file is part of adopt, distributed under the MIT license.
+ * For full terms and conditions, see the included LICENSE file.
+ *
+ * THIS FILE IS AUTOMATICALLY GENERATED; DO NOT EDIT.
+ *
+ * This file was produced by using the `rename.pl` script included with
+ * adopt. The command-line specified was:
+ *
+ * ./rename.pl cli_opt --filename=opt --include=cli.h --inline=GIT_INLINE --header-guard=CLI_opt_h__ --lowercase-status --without-usage
+ */
+
+#ifndef CLI_opt_h__
+#define CLI_opt_h__
+
+#include <stdio.h>
+#include <stdint.h>
+
+/**
+ * The type of argument to be parsed.
+ */
+typedef enum {
+ CLI_OPT_TYPE_NONE = 0,
+
+ /**
+ * An option that, when specified, sets a given value to true.
+ * This is useful for options like "--debug". A negation
+ * option (beginning with "no-") is implicitly specified; for
+ * example "--no-debug". The `value` pointer in the returned
+ * option will be set to `1` when this is specified, and set to
+ * `0` when the negation "no-" option is specified.
+ */
+ CLI_OPT_TYPE_BOOL,
+
+ /**
+ * An option that, when specified, sets the given `value` pointer
+ * to the specified `switch_value`. This is useful for booleans
+ * where you do not want the implicit negation that comes with an
+ * `CLI_OPT_TYPE_BOOL`, or for switches that multiplex a value, like
+ * setting a mode. For example, `--read` may set the `value` to
+ * `MODE_READ` and `--write` may set the `value` to `MODE_WRITE`.
+ */
+ CLI_OPT_TYPE_SWITCH,
+
+ /**
+ * An option that, when specified, increments the given
+ * `value` by the given `switch_value`. This can be specified
+ * multiple times to continue to increment the `value`.
+ * (For example, "-vvv" to set verbosity to 3.)
+ */
+ CLI_OPT_TYPE_ACCUMULATOR,
+
+ /**
+ * An option that takes a value, for example `-n value`,
+ * `-nvalue`, `--name value` or `--name=value`.
+ */
+ CLI_OPT_TYPE_VALUE,
+
+ /**
+ * A bare "--" that indicates that arguments following this are
+ * literal. This allows callers to specify things that might
+ * otherwise look like options, for example to operate on a file
+ * named "-rf" then you can invoke "program -- -rf" to treat
+ * "-rf" as an argument not an option.
+ */
+ CLI_OPT_TYPE_LITERAL,
+
+ /**
+ * A single argument, not an option. When options are exhausted,
+ * arguments will be matches in the order that they're specified
+ * in the spec list. For example, if two `CLI_OPT_TYPE_ARGS` are
+ * specified, `input_file` and `output_file`, then the first bare
+ * argument on the command line will be `input_file` and the
+ * second will be `output_file`.
+ */
+ CLI_OPT_TYPE_ARG,
+
+ /**
+ * A collection of arguments. This is useful when you want to take
+ * a list of arguments, for example, multiple paths. When specified,
+ * the value will be set to the first argument in the list.
+ */
+ CLI_OPT_TYPE_ARGS,
+} cli_opt_type_t;
+
+/**
+ * Additional information about an option, including parsing
+ * restrictions and usage information to be displayed to the end-user.
+ */
+typedef enum {
+ /** Defaults for the argument. */
+ CLI_OPT_USAGE_DEFAULT = 0,
+
+ /** This argument is required. */
+ CLI_OPT_USAGE_REQUIRED = (1u << 0),
+
+ /**
+ * This is a multiple choice argument, combined with the previous
+ * argument. For example, when the previous argument is `-f` and
+ * this optional is applied to an argument of type `-b` then one
+ * of `-f` or `-b` may be specified.
+ */
+ CLI_OPT_USAGE_CHOICE = (1u << 1),
+
+ /**
+ * This argument short-circuits the remainder of parsing.
+ * Useful for arguments like `--help`.
+ */
+ CLI_OPT_USAGE_STOP_PARSING = (1u << 2),
+
+ /** The argument's value is optional ("-n" or "-n foo") */
+ CLI_OPT_USAGE_VALUE_OPTIONAL = (1u << 3),
+
+ /** This argument should not be displayed in usage. */
+ CLI_OPT_USAGE_HIDDEN = (1u << 4),
+
+ /** In usage, show the long format instead of the abbreviated format. */
+ CLI_OPT_USAGE_SHOW_LONG = (1u << 5),
+} cli_opt_usage_t;
+
+typedef enum {
+ /** Default parsing behavior. */
+ CLI_OPT_PARSE_DEFAULT = 0,
+
+ /**
+ * Parse with GNU `getopt_long` style behavior, where options can
+ * be intermixed with arguments at any position (for example,
+ * "file1 --help file2".) Like `getopt_long`, this can mutate the
+ * arguments given.
+ */
+ CLI_OPT_PARSE_GNU = (1u << 0),
+
+ /**
+ * Force GNU `getopt_long` style behavior; the `POSIXLY_CORRECT`
+ * environment variable is ignored.
+ */
+ CLI_OPT_PARSE_FORCE_GNU = (1u << 1),
+} cli_opt_flag_t;
+
+/** Specification for an available option. */
+typedef struct cli_opt_spec {
+ /** Type of option expected. */
+ cli_opt_type_t type;
+
+ /** Name of the long option. */
+ const char *name;
+
+ /** The alias is the short (one-character) option alias. */
+ const char alias;
+
+ /**
+ * If this spec is of type `CLI_OPT_TYPE_BOOL`, this is a pointer
+ * to an `int` that will be set to `1` if the option is specified.
+ *
+ * If this spec is of type `CLI_OPT_TYPE_SWITCH`, this is a pointer
+ * to an `int` that will be set to the opt's `switch_value` (below)
+ * when this option is specified.
+ *
+ * If this spec is of type `CLI_OPT_TYPE_ACCUMULATOR`, this is a
+ * pointer to an `int` that will be incremented by the opt's
+ * `switch_value` (below). If no `switch_value` is provided then
+ * the value will be incremented by 1.
+ *
+ * If this spec is of type `CLI_OPT_TYPE_VALUE`,
+ * `CLI_OPT_TYPE_VALUE_OPTIONAL`, or `CLI_OPT_TYPE_ARG`, this is
+ * a pointer to a `char *` that will be set to the value
+ * specified on the command line.
+ *
+ * If this spec is of type `CLI_OPT_TYPE_ARGS`, this is a pointer
+ * to a `char **` that will be set to the remaining values
+ * specified on the command line.
+ */
+ void *value;
+
+ /**
+ * If this spec is of type `CLI_OPT_TYPE_SWITCH`, this is the value
+ * to set in the option's `value` pointer when it is specified. If
+ * this spec is of type `CLI_OPT_TYPE_ACCUMULATOR`, this is the value
+ * to increment in the option's `value` pointer when it is
+ * specified. This is ignored for other opt types.
+ */
+ int switch_value;
+
+ /**
+ * Optional usage flags that change parsing behavior and how
+ * usage information is shown to the end-user.
+ */
+ uint32_t usage;
+
+ /**
+ * The name of the value, provided when creating usage information.
+ * This is required only for the functions that display usage
+ * information and only when a spec is of type `CLI_OPT_TYPE_VALUE,
+ * `CLI_OPT_TYPE_ARG` or `CLI_OPT_TYPE_ARGS``.
+ */
+ const char *value_name;
+
+ /**
+ * Optional short description of the option to display to the
+ * end-user. This is only used when creating usage information.
+ */
+ const char *help;
+} cli_opt_spec;
+
+/** Return value for `cli_opt_parser_next`. */
+typedef enum {
+ /** Parsing is complete; there are no more arguments. */
+ CLI_OPT_STATUS_DONE = 0,
+
+ /**
+ * This argument was parsed correctly; the `opt` structure is
+ * populated and the value pointer has been set.
+ */
+ CLI_OPT_STATUS_OK = 1,
+
+ /**
+ * The argument could not be parsed correctly, it does not match
+ * any of the specifications provided.
+ */
+ CLI_OPT_STATUS_UNKNOWN_OPTION = 2,
+
+ /**
+ * The argument matched a spec of type `CLI_OPT_VALUE`, but no value
+ * was provided.
+ */
+ CLI_OPT_STATUS_MISSING_VALUE = 3,
+
+ /** A required argument was not provided. */
+ CLI_OPT_STATUS_MISSING_ARGUMENT = 4,
+} cli_opt_status_t;
+
+/** An option provided on the command-line. */
+typedef struct cli_opt {
+ /** The status of parsing the most recent argument. */
+ cli_opt_status_t status;
+
+ /**
+ * The specification that was provided on the command-line, or
+ * `NULL` if the argument did not match an `cli_opt_spec`.
+ */
+ const cli_opt_spec *spec;
+
+ /**
+ * The argument as it was specified on the command-line, including
+ * dashes, eg, `-f` or `--foo`.
+ */
+ char *arg;
+
+ /**
+ * If the spec is of type `CLI_OPT_VALUE` or `CLI_OPT_VALUE_OPTIONAL`,
+ * this is the value provided to the argument.
+ */
+ char *value;
+
+ /**
+ * If the argument is of type `CLI_OPT_ARGS`, this is the number of
+ * arguments remaining. This value is persisted even when parsing
+ * is complete and `status` == `CLI_OPT_STATUS_DONE`.
+ */
+ size_t args_len;
+} cli_opt;
+
+/* The internal parser state. Callers should not modify this structure. */
+typedef struct cli_opt_parser {
+ const cli_opt_spec *specs;
+ char **args;
+ size_t args_len;
+ unsigned int flags;
+
+ /* Parser state */
+ size_t idx;
+ size_t arg_idx;
+ size_t in_args;
+ size_t in_short;
+ int needs_sort : 1,
+ in_literal : 1;
+} cli_opt_parser;
+
+/**
+ * Parses all the command-line arguments and updates all the options using
+ * the pointers provided. Parsing stops on any invalid argument and
+ * information about the failure will be provided in the opt argument.
+ *
+ * This is the simplest way to parse options; it handles the initialization
+ * (`parser_init`) and looping (`parser_next`).
+ *
+ * @param opt The The `cli_opt` information that failed parsing
+ * @param specs A NULL-terminated array of `cli_opt_spec`s that can be parsed
+ * @param args The arguments that will be parsed
+ * @param args_len The length of arguments to be parsed
+ * @param flags The `cli_opt_flag_t flags for parsing
+ */
+cli_opt_status_t cli_opt_parse(
+ cli_opt *opt,
+ const cli_opt_spec specs[],
+ char **args,
+ size_t args_len,
+ unsigned int flags);
+
+/**
+ * Initializes a parser that parses the given arguments according to the
+ * given specifications.
+ *
+ * @param parser The `cli_opt_parser` that will be initialized
+ * @param specs A NULL-terminated array of `cli_opt_spec`s that can be parsed
+ * @param args The arguments that will be parsed
+ * @param args_len The length of arguments to be parsed
+ * @param flags The `cli_opt_flag_t flags for parsing
+ */
+void cli_opt_parser_init(
+ cli_opt_parser *parser,
+ const cli_opt_spec specs[],
+ char **args,
+ size_t args_len,
+ unsigned int flags);
+
+/**
+ * Parses the next command-line argument and places the information about
+ * the argument into the given `opt` data.
+ *
+ * @param opt The `cli_opt` information parsed from the argument
+ * @param parser An `cli_opt_parser` that has been initialized with
+ * `cli_opt_parser_init`
+ * @return true if the caller should continue iterating, or 0 if there are
+ * no arguments left to process.
+ */
+cli_opt_status_t cli_opt_parser_next(
+ cli_opt *opt,
+ cli_opt_parser *parser);
+
+/**
+ * Prints the status after parsing the most recent argument. This is
+ * useful for printing an error message when an unknown argument was
+ * specified, or when an argument was specified without a value.
+ *
+ * @param file The file to print information to
+ * @param command The name of the command to use when printing (optional)
+ * @param opt The option that failed to parse
+ * @return 0 on success, -1 on failure
+ */
+int cli_opt_status_fprint(
+ FILE *file,
+ const char *command,
+ const cli_opt *opt);
+
+#endif /* CLI_opt_h__ */
diff --git a/src/cli/opt_usage.c b/src/cli/opt_usage.c
new file mode 100644
index 0000000..478b416
--- /dev/null
+++ b/src/cli/opt_usage.c
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "cli.h"
+#include "str.h"
+
+static int print_spec_name(git_str *out, const cli_opt_spec *spec)
+{
+ if (spec->type == CLI_OPT_TYPE_VALUE && spec->alias &&
+ !(spec->usage & CLI_OPT_USAGE_VALUE_OPTIONAL) &&
+ !(spec->usage & CLI_OPT_USAGE_SHOW_LONG))
+ return git_str_printf(out, "-%c <%s>", spec->alias, spec->value_name);
+ if (spec->type == CLI_OPT_TYPE_VALUE && spec->alias &&
+ !(spec->usage & CLI_OPT_USAGE_SHOW_LONG))
+ return git_str_printf(out, "-%c [<%s>]", spec->alias, spec->value_name);
+ if (spec->type == CLI_OPT_TYPE_VALUE &&
+ !(spec->usage & CLI_OPT_USAGE_VALUE_OPTIONAL))
+ return git_str_printf(out, "--%s[=<%s>]", spec->name, spec->value_name);
+ if (spec->type == CLI_OPT_TYPE_VALUE)
+ return git_str_printf(out, "--%s=<%s>", spec->name, spec->value_name);
+ if (spec->type == CLI_OPT_TYPE_ARG)
+ return git_str_printf(out, "<%s>", spec->value_name);
+ if (spec->type == CLI_OPT_TYPE_ARGS)
+ return git_str_printf(out, "<%s>...", spec->value_name);
+ if (spec->type == CLI_OPT_TYPE_LITERAL)
+ return git_str_printf(out, "--");
+ if (spec->alias && !(spec->usage & CLI_OPT_USAGE_SHOW_LONG))
+ return git_str_printf(out, "-%c", spec->alias);
+ if (spec->name)
+ return git_str_printf(out, "--%s", spec->name);
+
+ GIT_ASSERT(0);
+}
+
+/*
+ * This is similar to adopt's function, but modified to understand
+ * that we have a command ("git") and a "subcommand" ("checkout").
+ * It also understands a terminal's line length and wrap appropriately,
+ * using a `git_str` for storage.
+ */
+int cli_opt_usage_fprint(
+ FILE *file,
+ const char *command,
+ const char *subcommand,
+ const cli_opt_spec specs[])
+{
+ git_str usage = GIT_BUF_INIT, opt = GIT_BUF_INIT;
+ const cli_opt_spec *spec;
+ size_t i, prefixlen, linelen;
+ bool choice = false, next_choice = false, optional = false;
+ int error;
+
+ /* TODO: query actual console width. */
+ int console_width = 80;
+
+ if ((error = git_str_printf(&usage, "usage: %s", command)) < 0)
+ goto done;
+
+ if (subcommand &&
+ (error = git_str_printf(&usage, " %s", subcommand)) < 0)
+ goto done;
+
+ linelen = git_str_len(&usage);
+ prefixlen = linelen + 1;
+
+ for (spec = specs; spec->type; ++spec) {
+ if (!choice)
+ optional = !(spec->usage & CLI_OPT_USAGE_REQUIRED);
+
+ next_choice = !!((spec + 1)->usage & CLI_OPT_USAGE_CHOICE);
+
+ if (spec->usage & CLI_OPT_USAGE_HIDDEN)
+ continue;
+
+ if (choice)
+ git_str_putc(&opt, '|');
+ else
+ git_str_clear(&opt);
+
+ if (optional && !choice)
+ git_str_putc(&opt, '[');
+ if (!optional && !choice && next_choice)
+ git_str_putc(&opt, '(');
+
+ if ((error = print_spec_name(&opt, spec)) < 0)
+ goto done;
+
+ if (!optional && choice && !next_choice)
+ git_str_putc(&opt, ')');
+ else if (optional && !next_choice)
+ git_str_putc(&opt, ']');
+
+ if ((choice = next_choice))
+ continue;
+
+ if (git_str_oom(&opt)) {
+ error = -1;
+ goto done;
+ }
+
+ if (linelen > prefixlen &&
+ console_width > 0 &&
+ linelen + git_str_len(&opt) + 1 > (size_t)console_width) {
+ git_str_putc(&usage, '\n');
+
+ for (i = 0; i < prefixlen; i++)
+ git_str_putc(&usage, ' ');
+
+ linelen = prefixlen;
+ } else {
+ git_str_putc(&usage, ' ');
+ linelen += git_str_len(&opt) + 1;
+ }
+
+ git_str_puts(&usage, git_str_cstr(&opt));
+
+ if (git_str_oom(&usage)) {
+ error = -1;
+ goto done;
+ }
+ }
+
+ error = fprintf(file, "%s\n", git_str_cstr(&usage));
+
+done:
+ error = (error < 0) ? -1 : 0;
+
+ git_str_dispose(&usage);
+ git_str_dispose(&opt);
+ return error;
+}
+
+int cli_opt_usage_error(
+ const char *subcommand,
+ const cli_opt_spec specs[],
+ const cli_opt *invalid_opt)
+{
+ cli_opt_status_fprint(stderr, PROGRAM_NAME, invalid_opt);
+ cli_opt_usage_fprint(stderr, PROGRAM_NAME, subcommand, specs);
+ return CLI_EXIT_USAGE;
+}
+
+int cli_opt_help_fprint(
+ FILE *file,
+ const cli_opt_spec specs[])
+{
+ git_str help = GIT_BUF_INIT;
+ const cli_opt_spec *spec;
+ int error = 0;
+
+ /* Display required arguments first */
+ for (spec = specs; spec->type; ++spec) {
+ if (! (spec->usage & CLI_OPT_USAGE_REQUIRED) ||
+ (spec->usage & CLI_OPT_USAGE_HIDDEN))
+ continue;
+
+ git_str_printf(&help, " ");
+
+ if ((error = print_spec_name(&help, spec)) < 0)
+ goto done;
+
+ git_str_printf(&help, ": %s\n", spec->help);
+ }
+
+ /* Display the remaining arguments */
+ for (spec = specs; spec->type; ++spec) {
+ if ((spec->usage & CLI_OPT_USAGE_REQUIRED) ||
+ (spec->usage & CLI_OPT_USAGE_HIDDEN))
+ continue;
+
+ git_str_printf(&help, " ");
+
+ if ((error = print_spec_name(&help, spec)) < 0)
+ goto done;
+
+ git_str_printf(&help, ": %s\n", spec->help);
+
+ }
+
+ if (git_str_oom(&help) ||
+ p_write(fileno(file), help.ptr, help.size) < 0)
+ error = -1;
+
+done:
+ error = (error < 0) ? -1 : 0;
+
+ git_str_dispose(&help);
+ return error;
+}
+
diff --git a/src/cli/opt_usage.h b/src/cli/opt_usage.h
new file mode 100644
index 0000000..c752494
--- /dev/null
+++ b/src/cli/opt_usage.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef CLI_opt_usage_h__
+#define CLI_opt_usage_h__
+
+/**
+ * Prints usage information to the given file handle.
+ *
+ * @param file The file to print information to
+ * @param command The name of the command to use when printing
+ * @param subcommand The name of the subcommand (eg "checkout") to use when printing, or NULL to skip
+ * @param specs The specifications allowed by the command
+ * @return 0 on success, -1 on failure
+ */
+int cli_opt_usage_fprint(
+ FILE *file,
+ const char *command,
+ const char *subcommand,
+ const cli_opt_spec specs[]);
+
+int cli_opt_usage_error(
+ const char *subcommand,
+ const cli_opt_spec specs[],
+ const cli_opt *invalid_opt);
+
+int cli_opt_help_fprint(
+ FILE *file,
+ const cli_opt_spec specs[]);
+
+#endif /* CLI_opt_usage_h__ */
diff --git a/src/cli/progress.c b/src/cli/progress.c
new file mode 100644
index 0000000..ddfbafb
--- /dev/null
+++ b/src/cli/progress.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdint.h>
+
+#include "progress.h"
+#include "error.h"
+
+/*
+ * Show updates to the percentage and number of objects received
+ * separately from the throughput to give an accurate progress while
+ * avoiding too much noise on the screen. (In milliseconds.)
+ */
+#define PROGRESS_UPDATE_TIME 60
+#define THROUGHPUT_UPDATE_TIME 500
+
+#define is_nl(c) ((c) == '\r' || (c) == '\n')
+
+#define return_os_error(msg) do { \
+ git_error_set(GIT_ERROR_OS, "%s", msg); return -1; } while(0)
+
+GIT_INLINE(size_t) no_nl_len(const char *str, size_t len)
+{
+ size_t i = 0;
+
+ while (i < len && !is_nl(str[i]))
+ i++;
+
+ return i;
+}
+
+GIT_INLINE(size_t) nl_len(bool *has_nl, const char *str, size_t len)
+{
+ size_t i = no_nl_len(str, len);
+
+ *has_nl = false;
+
+ while (i < len && is_nl(str[i])) {
+ *has_nl = true;
+ i++;
+ }
+
+ return i;
+}
+
+static int progress_write(cli_progress *progress, bool force, git_str *line)
+{
+ bool has_nl;
+ size_t no_nl = no_nl_len(line->ptr, line->size);
+ size_t nl = nl_len(&has_nl, line->ptr + no_nl, line->size - no_nl);
+ uint64_t now = git_time_monotonic();
+ size_t i;
+
+ /* Avoid spamming the console with progress updates */
+ if (!force && line->ptr[line->size - 1] != '\n' && progress->last_update) {
+ if (now - progress->last_update < PROGRESS_UPDATE_TIME) {
+ git_str_clear(&progress->deferred);
+ git_str_put(&progress->deferred, line->ptr, line->size);
+ return git_str_oom(&progress->deferred) ? -1 : 0;
+ }
+ }
+
+ /*
+ * If there's something on this line already (eg, a progress line
+ * with only a trailing `\r` that we'll print over) then we need
+ * to really print over it in case we're writing a shorter line.
+ */
+ if (printf("%.*s", (int)no_nl, line->ptr) < 0)
+ return_os_error("could not print status");
+
+ if (progress->onscreen.size) {
+ for (i = no_nl; i < progress->onscreen.size; i++) {
+ if (printf(" ") < 0)
+ return_os_error("could not print status");
+ }
+ }
+
+ if (printf("%.*s", (int)nl, line->ptr + no_nl) < 0 ||
+ fflush(stdout) != 0)
+ return_os_error("could not print status");
+
+ git_str_clear(&progress->onscreen);
+
+ if (line->ptr[line->size - 1] == '\n') {
+ progress->last_update = 0;
+ } else {
+ git_str_put(&progress->onscreen, line->ptr, line->size);
+ progress->last_update = now;
+ }
+
+ git_str_clear(&progress->deferred);
+ return git_str_oom(&progress->onscreen) ? -1 : 0;
+}
+
+static int progress_printf(cli_progress *progress, bool force, const char *fmt, ...)
+ GIT_FORMAT_PRINTF(3, 4);
+
+int progress_printf(cli_progress *progress, bool force, const char *fmt, ...)
+{
+ git_str buf = GIT_BUF_INIT;
+ va_list ap;
+ int error;
+
+ va_start(ap, fmt);
+ error = git_str_vprintf(&buf, fmt, ap);
+ va_end(ap);
+
+ if (error < 0)
+ return error;
+
+ error = progress_write(progress, force, &buf);
+
+ git_str_dispose(&buf);
+ return error;
+}
+
+static int progress_complete(cli_progress *progress)
+{
+ if (progress->deferred.size)
+ progress_write(progress, true, &progress->deferred);
+
+ if (progress->onscreen.size)
+ if (printf("\n") < 0)
+ return_os_error("could not print status");
+
+ git_str_clear(&progress->deferred);
+ git_str_clear(&progress->onscreen);
+ progress->last_update = 0;
+ progress->action_start = 0;
+ progress->action_finish = 0;
+
+ return 0;
+}
+
+GIT_INLINE(int) percent(size_t completed, size_t total)
+{
+ if (total == 0)
+ return (completed == 0) ? 100 : 0;
+
+ return (int)(((double)completed / (double)total) * 100);
+}
+
+int cli_progress_fetch_sideband(const char *str, int len, void *payload)
+{
+ cli_progress *progress = (cli_progress *)payload;
+ size_t remain;
+
+ if (len <= 0)
+ return 0;
+
+ /* Accumulate the sideband data, then print it line-at-a-time. */
+ if (git_str_put(&progress->sideband, str, len) < 0)
+ return -1;
+
+ str = progress->sideband.ptr;
+ remain = progress->sideband.size;
+
+ while (remain) {
+ bool has_nl;
+ size_t line_len = nl_len(&has_nl, str, remain);
+
+ if (!has_nl)
+ break;
+
+ if (line_len < INT_MAX) {
+ int error = progress_printf(progress, true,
+ "remote: %.*s", (int)line_len, str);
+
+ if (error < 0)
+ return error;
+ }
+
+ str += line_len;
+ remain -= line_len;
+ }
+
+ git_str_consume_bytes(&progress->sideband, (progress->sideband.size - remain));
+
+ return 0;
+}
+
+static int fetch_receiving(
+ cli_progress *progress,
+ const git_indexer_progress *stats)
+{
+ char *recv_units[] = { "B", "KiB", "MiB", "GiB", "TiB", NULL };
+ char *rate_units[] = { "B/s", "KiB/s", "MiB/s", "GiB/s", "TiB/s", NULL };
+ uint64_t now, elapsed;
+
+ double recv_len, rate;
+ size_t recv_unit_idx = 0, rate_unit_idx = 0;
+ bool done = (stats->received_objects == stats->total_objects);
+
+ if (!progress->action_start)
+ progress->action_start = git_time_monotonic();
+
+ if (done && progress->action_finish)
+ now = progress->action_finish;
+ else if (done)
+ progress->action_finish = now = git_time_monotonic();
+ else
+ now = git_time_monotonic();
+
+ if (progress->throughput_update &&
+ now - progress->throughput_update < THROUGHPUT_UPDATE_TIME) {
+ elapsed = progress->throughput_update -
+ progress->action_start;
+ recv_len = progress->throughput_bytes;
+ } else {
+ elapsed = now - progress->action_start;
+ recv_len = (double)stats->received_bytes;
+
+ progress->throughput_update = now;
+ progress->throughput_bytes = recv_len;
+ }
+
+ rate = elapsed ? recv_len / elapsed : 0;
+
+ while (recv_len > 1024 && recv_units[recv_unit_idx+1]) {
+ recv_len /= 1024;
+ recv_unit_idx++;
+ }
+
+ while (rate > 1024 && rate_units[rate_unit_idx+1]) {
+ rate /= 1024;
+ rate_unit_idx++;
+ }
+
+ return progress_printf(progress, false,
+ "Receiving objects: %3d%% (%d/%d), %.2f %s | %.2f %s%s\r",
+ percent(stats->received_objects, stats->total_objects),
+ stats->received_objects,
+ stats->total_objects,
+ recv_len, recv_units[recv_unit_idx],
+ rate, rate_units[rate_unit_idx],
+ done ? ", done." : "");
+}
+
+static int fetch_resolving(
+ cli_progress *progress,
+ const git_indexer_progress *stats)
+{
+ bool done = (stats->indexed_deltas == stats->total_deltas);
+
+ return progress_printf(progress, false,
+ "Resolving deltas: %3d%% (%d/%d)%s\r",
+ percent(stats->indexed_deltas, stats->total_deltas),
+ stats->indexed_deltas, stats->total_deltas,
+ done ? ", done." : "");
+}
+
+int cli_progress_fetch_transfer(const git_indexer_progress *stats, void *payload)
+{
+ cli_progress *progress = (cli_progress *)payload;
+ int error = 0;
+
+ switch (progress->action) {
+ case CLI_PROGRESS_NONE:
+ progress->action = CLI_PROGRESS_RECEIVING;
+ /* fall through */
+
+ case CLI_PROGRESS_RECEIVING:
+ if ((error = fetch_receiving(progress, stats)) < 0)
+ break;
+
+ /*
+ * Upgrade from receiving to resolving; do this after the
+ * final call to cli_progress_fetch_receiving (above) to
+ * ensure that we've printed a final "done" string after
+ * any sideband data.
+ */
+ if (!stats->indexed_deltas)
+ break;
+
+ progress_complete(progress);
+ progress->action = CLI_PROGRESS_RESOLVING;
+ /* fall through */
+
+ case CLI_PROGRESS_RESOLVING:
+ error = fetch_resolving(progress, stats);
+ break;
+
+ default:
+ /* should not be reached */
+ GIT_ASSERT(!"unexpected progress state");
+ }
+
+ return error;
+}
+
+void cli_progress_checkout(
+ const char *path,
+ size_t completed_steps,
+ size_t total_steps,
+ void *payload)
+{
+ cli_progress *progress = (cli_progress *)payload;
+ bool done = (completed_steps == total_steps);
+
+ GIT_UNUSED(path);
+
+ if (progress->action != CLI_PROGRESS_CHECKING_OUT) {
+ progress_complete(progress);
+ progress->action = CLI_PROGRESS_CHECKING_OUT;
+ }
+
+ progress_printf(progress, false,
+ "Checking out files: %3d%% (%" PRIuZ "/%" PRIuZ ")%s\r",
+ percent(completed_steps, total_steps),
+ completed_steps, total_steps,
+ done ? ", done." : "");
+}
+
+int cli_progress_abort(cli_progress *progress)
+{
+ if (progress->onscreen.size > 0 && printf("\n") < 0)
+ return_os_error("could not print status");
+
+ return 0;
+}
+
+int cli_progress_finish(cli_progress *progress)
+{
+ int error = progress->action ? progress_complete(progress) : 0;
+
+ progress->action = 0;
+ return error;
+}
+
+void cli_progress_dispose(cli_progress *progress)
+{
+ if (progress == NULL)
+ return;
+
+ git_str_dispose(&progress->sideband);
+ git_str_dispose(&progress->onscreen);
+ git_str_dispose(&progress->deferred);
+
+ memset(progress, 0, sizeof(cli_progress));
+}
diff --git a/src/cli/progress.h b/src/cli/progress.h
new file mode 100644
index 0000000..886fef8
--- /dev/null
+++ b/src/cli/progress.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef CLI_progress_h__
+#define CLI_progress_h__
+
+#include <git2.h>
+#include "str.h"
+
+/*
+ * A general purpose set of progress printing functions. An individual
+ * `cli_progress` object is capable of displaying progress for a single
+ * function, even if that function displays multiple pieces of progress
+ * (like `git_clone`). `cli_progress_finish` should be called after
+ * any function invocation to re-set state.
+ */
+
+typedef enum {
+ CLI_PROGRESS_NONE,
+ CLI_PROGRESS_RECEIVING,
+ CLI_PROGRESS_RESOLVING,
+ CLI_PROGRESS_CHECKING_OUT
+} cli_progress_t;
+
+typedef struct {
+ cli_progress_t action;
+
+ /* Actions may time themselves (eg fetch) but are not required to */
+ uint64_t action_start;
+ uint64_t action_finish;
+
+ /* Last console update, avoid too frequent updates. */
+ uint64_t last_update;
+
+ /* Accumulators for partial output and deferred updates. */
+ git_str sideband;
+ git_str onscreen;
+ git_str deferred;
+
+ /* Last update about throughput */
+ uint64_t throughput_update;
+ double throughput_bytes;
+} cli_progress;
+
+#define CLI_PROGRESS_INIT { 0 }
+
+/**
+ * Prints sideband data from fetch to the console. Suitable for a
+ * `sideband_progress` callback for `git_fetch_options`.
+ *
+ * @param str The sideband string
+ * @param len The length of the sideband string
+ * @param payload A pointer to the cli_progress
+ * @return 0 on success, -1 on failure
+ */
+extern int cli_progress_fetch_sideband(
+ const char *str,
+ int len,
+ void *payload);
+
+/**
+ * Prints fetch transfer statistics to the console. Suitable for a
+ * `transfer_progress` callback for `git_fetch_options`.
+ *
+ * @param stats The indexer stats
+ * @param payload A pointer to the cli_progress
+ * @return 0 on success, -1 on failure
+ */
+extern int cli_progress_fetch_transfer(
+ const git_indexer_progress *stats,
+ void *payload);
+
+/**
+ * Prints checkout progress to the console. Suitable for a
+ * `progress_cb` callback for `git_checkout_options`.
+ *
+ * @param path The path being written
+ * @param completed_steps The completed checkout steps
+ * @param total_steps The total number of checkout steps
+ * @param payload A pointer to the cli_progress
+ */
+extern void cli_progress_checkout(
+ const char *path,
+ size_t completed_steps,
+ size_t total_steps,
+ void *payload);
+
+/**
+ * Stop displaying progress quickly; suitable for stopping an application
+ * quickly. Does not display any lines that were buffered, just gets the
+ * console back to a sensible place.
+ *
+ * @param progress The progress information
+ * @return 0 on success, -1 on failure
+ */
+extern int cli_progress_abort(cli_progress *progress);
+
+/**
+ * Finishes displaying progress; flushes any buffered output.
+ *
+ * @param progress The progress information
+ * @return 0 on success, -1 on failure
+ */
+extern int cli_progress_finish(cli_progress *progress);
+
+/**
+ * Disposes the progress information.
+ *
+ * @param progress The progress information
+ */
+extern void cli_progress_dispose(cli_progress *progress);
+
+#endif /* CLI_progress_h__ */
diff --git a/src/cli/sighandler.h b/src/cli/sighandler.h
new file mode 100644
index 0000000..877223e
--- /dev/null
+++ b/src/cli/sighandler.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef CLI_sighandler_h__
+#define CLI_sighandler_h__
+
+/**
+ * Sets up a signal handler that will run when the process is interrupted
+ * (via SIGINT on POSIX or Control-C or Control-Break on Windows).
+ *
+ * @param handler The function to run on interrupt
+ * @return 0 on success, -1 on failure
+ */
+int cli_sighandler_set_interrupt(void (*handler)(void));
+
+#endif /* CLI_sighandler_h__ */
diff --git a/src/cli/unix/sighandler.c b/src/cli/unix/sighandler.c
new file mode 100644
index 0000000..6b4982d
--- /dev/null
+++ b/src/cli/unix/sighandler.c
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include <stdint.h>
+#include <signal.h>
+#include "git2_util.h"
+#include "cli.h"
+
+static void (*interrupt_handler)(void) = NULL;
+
+static void interrupt_proxy(int signal)
+{
+ GIT_UNUSED(signal);
+ interrupt_handler();
+}
+
+int cli_sighandler_set_interrupt(void (*handler)(void))
+{
+ void (*result)(int);
+
+ if ((interrupt_handler = handler) != NULL)
+ result = signal(SIGINT, interrupt_proxy);
+ else
+ result = signal(SIGINT, SIG_DFL);
+
+ if (result == SIG_ERR) {
+ git_error_set(GIT_ERROR_OS, "could not set signal handler");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/cli/win32/precompiled.c b/src/cli/win32/precompiled.c
new file mode 100644
index 0000000..5f656a4
--- /dev/null
+++ b/src/cli/win32/precompiled.c
@@ -0,0 +1 @@
+#include "precompiled.h"
diff --git a/src/cli/win32/precompiled.h b/src/cli/win32/precompiled.h
new file mode 100644
index 0000000..b0309b8
--- /dev/null
+++ b/src/cli/win32/precompiled.h
@@ -0,0 +1,3 @@
+#include <git2.h>
+
+#include "cli.h"
diff --git a/src/cli/win32/sighandler.c b/src/cli/win32/sighandler.c
new file mode 100644
index 0000000..cc0b646
--- /dev/null
+++ b/src/cli/win32/sighandler.c
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "git2_util.h"
+#include <windows.h>
+
+#include "cli.h"
+
+static void (*interrupt_handler)(void) = NULL;
+
+static BOOL WINAPI interrupt_proxy(DWORD signal)
+{
+ GIT_UNUSED(signal);
+ interrupt_handler();
+ return TRUE;
+}
+
+int cli_sighandler_set_interrupt(void (*handler)(void))
+{
+ BOOL result;
+
+ if ((interrupt_handler = handler) != NULL)
+ result = SetConsoleCtrlHandler(interrupt_proxy, FALSE);
+ else
+ result = SetConsoleCtrlHandler(NULL, FALSE);
+
+ if (!result) {
+ git_error_set(GIT_ERROR_OS, "could not set control control handler");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/libgit2/CMakeLists.txt b/src/libgit2/CMakeLists.txt
new file mode 100644
index 0000000..876a703
--- /dev/null
+++ b/src/libgit2/CMakeLists.txt
@@ -0,0 +1,122 @@
+# libgit2: the shared library: this CMakeLists.txt compiles the core
+# git library functionality.
+
+add_library(libgit2 OBJECT)
+set_target_properties(libgit2 PROPERTIES C_STANDARD 90)
+set_target_properties(libgit2 PROPERTIES C_EXTENSIONS OFF)
+
+include(PkgBuildConfig)
+
+set(LIBGIT2_INCLUDES
+ "${PROJECT_BINARY_DIR}/src/util"
+ "${PROJECT_BINARY_DIR}/include"
+ "${PROJECT_SOURCE_DIR}/src/libgit2"
+ "${PROJECT_SOURCE_DIR}/src/util"
+ "${PROJECT_SOURCE_DIR}/include")
+
+# Collect sourcefiles
+file(GLOB SRC_H
+ "${PROJECT_SOURCE_DIR}/include/git2.h"
+ "${PROJECT_SOURCE_DIR}/include/git2/*.h"
+ "${PROJECT_SOURCE_DIR}/include/git2/sys/*.h")
+list(SORT SRC_H)
+target_sources(libgit2 PRIVATE ${SRC_H})
+
+file(GLOB SRC_GIT2 *.c *.h
+ streams/*.c streams/*.h
+ transports/*.c transports/*.h)
+list(SORT SRC_GIT2)
+target_sources(libgit2 PRIVATE ${SRC_GIT2})
+
+if(WIN32 AND NOT CYGWIN)
+ # Add resource information on Windows
+ set(SRC_RC "git2.rc")
+endif()
+
+if(APPLE)
+ # The old Secure Transport API has been deprecated in macOS 10.15.
+ set_source_files_properties(streams/stransport.c PROPERTIES COMPILE_FLAGS -Wno-deprecated)
+endif()
+
+ide_split_sources(libgit2)
+list(APPEND LIBGIT2_OBJECTS $<TARGET_OBJECTS:util> $<TARGET_OBJECTS:libgit2> ${LIBGIT2_DEPENDENCY_OBJECTS})
+list(APPEND LIBGIT2_INCLUDES ${LIBGIT2_DEPENDENCY_INCLUDES})
+
+target_include_directories(libgit2 PRIVATE ${LIBGIT2_INCLUDES} ${LIBGIT2_DEPENDENCY_INCLUDES} PUBLIC ${PROJECT_SOURCE_DIR}/include)
+target_include_directories(libgit2 SYSTEM PRIVATE ${LIBGIT2_SYSTEM_INCLUDES})
+
+set(LIBGIT2_INCLUDES ${LIBGIT2_INCLUDES} PARENT_SCOPE)
+set(LIBGIT2_OBJECTS ${LIBGIT2_OBJECTS} PARENT_SCOPE)
+set(LIBGIT2_DEPENDENCY_INCLUDES ${LIBGIT2_DEPENDENCY_INCLUDES} PARENT_SCOPE)
+set(LIBGIT2_DEPENDENCY_OBJECTS ${LIBGIT2_DEPENDENCY_OBJECTS} PARENT_SCOPE)
+set(LIBGIT2_SYSTEM_INCLUDES ${LIBGIT2_SYSTEM_INCLUDES} PARENT_SCOPE)
+set(LIBGIT2_SYSTEM_LIBS ${LIBGIT2_SYSTEM_LIBS} PARENT_SCOPE)
+
+#
+# Compile and link libgit2
+#
+
+add_library(libgit2package ${SRC_RC} ${LIBGIT2_OBJECTS})
+target_link_libraries(libgit2package ${LIBGIT2_SYSTEM_LIBS})
+target_include_directories(libgit2package SYSTEM PRIVATE ${LIBGIT2_INCLUDES})
+
+set_target_properties(libgit2package PROPERTIES C_STANDARD 90)
+set_target_properties(libgit2package PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
+set_target_properties(libgit2package PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
+set_target_properties(libgit2package PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
+
+# Workaround for Cmake bug #0011240 (see http://public.kitware.com/Bug/view.php?id=11240)
+# Win64+MSVC+static libs = linker error
+if(MSVC AND GIT_ARCH_64 AND NOT BUILD_SHARED_LIBS)
+ set_target_properties(libgit2package PROPERTIES STATIC_LIBRARY_FLAGS "/MACHINE:x64")
+endif()
+
+ide_split_sources(libgit2package)
+
+if(SONAME)
+ set_target_properties(libgit2package PROPERTIES VERSION ${libgit2_VERSION})
+ set_target_properties(libgit2package PROPERTIES SOVERSION "${libgit2_VERSION_MAJOR}.${libgit2_VERSION_MINOR}")
+ if(LIBGIT2_FILENAME)
+ target_compile_definitions(libgit2package PRIVATE LIBGIT2_FILENAME=\"${LIBGIT2_FILENAME}\")
+ set_target_properties(libgit2package PROPERTIES OUTPUT_NAME ${LIBGIT2_FILENAME})
+ elseif(DEFINED LIBGIT2_PREFIX)
+ set_target_properties(libgit2package PROPERTIES PREFIX "${LIBGIT2_PREFIX}")
+ endif()
+endif()
+
+pkg_build_config(NAME "lib${LIBGIT2_FILENAME}"
+ VERSION ${libgit2_VERSION}
+ DESCRIPTION "The git library, take 2"
+ LIBS_SELF ${LIBGIT2_FILENAME}
+ PRIVATE_LIBS ${LIBGIT2_PC_LIBS}
+ REQUIRES ${LIBGIT2_PC_REQUIRES})
+
+if(MSVC_IDE)
+ # Precompiled headers
+ set_target_properties(libgit2package PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h")
+ set_source_files_properties(win32/precompiled.c COMPILE_FLAGS "/Ycprecompiled.h")
+endif()
+
+# support experimental features and functionality
+
+configure_file(experimental.h.in "${PROJECT_BINARY_DIR}/include/git2/experimental.h")
+
+# translate filenames in the git2.h so that they match the install directory
+# (allows for side-by-side installs of libgit2 and libgit2-experimental.)
+
+FILE(READ "${PROJECT_SOURCE_DIR}/include/git2.h" LIBGIT2_INCLUDE)
+STRING(REGEX REPLACE "#include \"git2\/" "#include \"${LIBGIT2_FILENAME}/" LIBGIT2_INCLUDE "${LIBGIT2_INCLUDE}")
+FILE(WRITE "${PROJECT_BINARY_DIR}/include/${LIBGIT2_FILENAME}.h" ${LIBGIT2_INCLUDE})
+
+# Install
+
+install(TARGETS libgit2package
+ RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+ LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+ ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
+install(DIRECTORY ${PROJECT_SOURCE_DIR}/include/git2/
+ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${LIBGIT2_FILENAME}")
+install(FILES ${PROJECT_BINARY_DIR}/include/git2/experimental.h
+ DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/${LIBGIT2_FILENAME}")
+install(FILES "${PROJECT_BINARY_DIR}/include/${LIBGIT2_FILENAME}.h"
+ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR})
diff --git a/src/libgit2/annotated_commit.c b/src/libgit2/annotated_commit.c
new file mode 100644
index 0000000..c5c8ace
--- /dev/null
+++ b/src/libgit2/annotated_commit.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "annotated_commit.h"
+
+#include "refs.h"
+#include "cache.h"
+
+#include "git2/commit.h"
+#include "git2/refs.h"
+#include "git2/repository.h"
+#include "git2/annotated_commit.h"
+#include "git2/revparse.h"
+#include "git2/tree.h"
+#include "git2/index.h"
+
+static int annotated_commit_init(
+ git_annotated_commit **out,
+ git_commit *commit,
+ const char *description)
+{
+ git_annotated_commit *annotated_commit;
+ int error = 0;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(commit);
+
+ *out = NULL;
+
+ annotated_commit = git__calloc(1, sizeof(git_annotated_commit));
+ GIT_ERROR_CHECK_ALLOC(annotated_commit);
+
+ annotated_commit->type = GIT_ANNOTATED_COMMIT_REAL;
+
+ if ((error = git_commit_dup(&annotated_commit->commit, commit)) < 0)
+ goto done;
+
+ git_oid_tostr(annotated_commit->id_str, GIT_OID_MAX_HEXSIZE + 1,
+ git_commit_id(commit));
+
+ if (!description)
+ description = annotated_commit->id_str;
+
+ annotated_commit->description = git__strdup(description);
+ GIT_ERROR_CHECK_ALLOC(annotated_commit->description);
+
+done:
+ if (!error)
+ *out = annotated_commit;
+
+ return error;
+}
+
+static int annotated_commit_init_from_id(
+ git_annotated_commit **out,
+ git_repository *repo,
+ const git_oid *id,
+ const char *description)
+{
+ git_commit *commit = NULL;
+ int error = 0;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(id);
+
+ *out = NULL;
+
+ if ((error = git_commit_lookup(&commit, repo, id)) < 0)
+ goto done;
+
+ error = annotated_commit_init(out, commit, description);
+
+done:
+ git_commit_free(commit);
+ return error;
+}
+
+int git_annotated_commit_lookup(
+ git_annotated_commit **out,
+ git_repository *repo,
+ const git_oid *id)
+{
+ return annotated_commit_init_from_id(out, repo, id, NULL);
+}
+
+int git_annotated_commit_from_commit(
+ git_annotated_commit **out,
+ git_commit *commit)
+{
+ return annotated_commit_init(out, commit, NULL);
+}
+
+int git_annotated_commit_from_revspec(
+ git_annotated_commit **out,
+ git_repository *repo,
+ const char *revspec)
+{
+ git_object *obj, *commit;
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(revspec);
+
+ if ((error = git_revparse_single(&obj, repo, revspec)) < 0)
+ return error;
+
+ if ((error = git_object_peel(&commit, obj, GIT_OBJECT_COMMIT))) {
+ git_object_free(obj);
+ return error;
+ }
+
+ error = annotated_commit_init(out, (git_commit *)commit, revspec);
+
+ git_object_free(obj);
+ git_object_free(commit);
+
+ return error;
+}
+
+int git_annotated_commit_from_ref(
+ git_annotated_commit **out,
+ git_repository *repo,
+ const git_reference *ref)
+{
+ git_object *peeled;
+ int error = 0;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(ref);
+
+ *out = NULL;
+
+ if ((error = git_reference_peel(&peeled, ref, GIT_OBJECT_COMMIT)) < 0)
+ return error;
+
+ error = annotated_commit_init_from_id(out,
+ repo,
+ git_object_id(peeled),
+ git_reference_name(ref));
+
+ if (!error) {
+ (*out)->ref_name = git__strdup(git_reference_name(ref));
+ GIT_ERROR_CHECK_ALLOC((*out)->ref_name);
+ }
+
+ git_object_free(peeled);
+ return error;
+}
+
+int git_annotated_commit_from_head(
+ git_annotated_commit **out,
+ git_repository *repo)
+{
+ git_reference *head;
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+
+ *out = NULL;
+
+ if ((error = git_reference_lookup(&head, repo, GIT_HEAD_FILE)) < 0)
+ return -1;
+
+ error = git_annotated_commit_from_ref(out, repo, head);
+
+ git_reference_free(head);
+ return error;
+}
+
+int git_annotated_commit_from_fetchhead(
+ git_annotated_commit **out,
+ git_repository *repo,
+ const char *branch_name,
+ const char *remote_url,
+ const git_oid *id)
+{
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(branch_name);
+ GIT_ASSERT_ARG(remote_url);
+ GIT_ASSERT_ARG(id);
+
+ if (annotated_commit_init_from_id(out, repo, id, branch_name) < 0)
+ return -1;
+
+ (*out)->ref_name = git__strdup(branch_name);
+ GIT_ERROR_CHECK_ALLOC((*out)->ref_name);
+
+ (*out)->remote_url = git__strdup(remote_url);
+ GIT_ERROR_CHECK_ALLOC((*out)->remote_url);
+
+ return 0;
+}
+
+
+const git_oid *git_annotated_commit_id(
+ const git_annotated_commit *annotated_commit)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(annotated_commit, NULL);
+ return git_commit_id(annotated_commit->commit);
+}
+
+const char *git_annotated_commit_ref(
+ const git_annotated_commit *annotated_commit)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(annotated_commit, NULL);
+ return annotated_commit->ref_name;
+}
+
+void git_annotated_commit_free(git_annotated_commit *annotated_commit)
+{
+ if (annotated_commit == NULL)
+ return;
+
+ switch (annotated_commit->type) {
+ case GIT_ANNOTATED_COMMIT_REAL:
+ git_commit_free(annotated_commit->commit);
+ git_tree_free(annotated_commit->tree);
+ git__free((char *)annotated_commit->description);
+ git__free((char *)annotated_commit->ref_name);
+ git__free((char *)annotated_commit->remote_url);
+ break;
+ case GIT_ANNOTATED_COMMIT_VIRTUAL:
+ git_index_free(annotated_commit->index);
+ git_array_clear(annotated_commit->parents);
+ break;
+ default:
+ abort();
+ }
+
+ git__free(annotated_commit);
+}
diff --git a/src/libgit2/annotated_commit.h b/src/libgit2/annotated_commit.h
new file mode 100644
index 0000000..1f805fe
--- /dev/null
+++ b/src/libgit2/annotated_commit.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_annotated_commit_h__
+#define INCLUDE_annotated_commit_h__
+
+#include "common.h"
+
+#include "oidarray.h"
+
+#include "git2/oid.h"
+
+typedef enum {
+ GIT_ANNOTATED_COMMIT_REAL = 1,
+ GIT_ANNOTATED_COMMIT_VIRTUAL = 2
+} git_annotated_commit_t;
+
+/**
+ * Internal structure for merge inputs. An annotated commit is generally
+ * "real" and backed by an actual commit in the repository, but merge will
+ * internally create "virtual" commits that are in-memory intermediate
+ * commits backed by an index.
+ */
+struct git_annotated_commit {
+ git_annotated_commit_t type;
+
+ /* real commit */
+ git_commit *commit;
+ git_tree *tree;
+
+ /* virtual commit structure */
+ git_index *index;
+ git_array_oid_t parents;
+
+ /* how this commit was looked up */
+ const char *description;
+
+ const char *ref_name;
+ const char *remote_url;
+
+ char id_str[GIT_OID_MAX_HEXSIZE + 1];
+};
+
+extern int git_annotated_commit_from_head(git_annotated_commit **out,
+ git_repository *repo);
+extern int git_annotated_commit_from_commit(git_annotated_commit **out,
+ git_commit *commit);
+
+#endif
diff --git a/src/libgit2/apply.c b/src/libgit2/apply.c
new file mode 100644
index 0000000..6b55b81
--- /dev/null
+++ b/src/libgit2/apply.c
@@ -0,0 +1,899 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "git2/apply.h"
+#include "git2/patch.h"
+#include "git2/filter.h"
+#include "git2/blob.h"
+#include "git2/index.h"
+#include "git2/checkout.h"
+#include "git2/repository.h"
+#include "array.h"
+#include "patch.h"
+#include "futils.h"
+#include "delta.h"
+#include "zstream.h"
+#include "reader.h"
+#include "index.h"
+#include "repository.h"
+#include "apply.h"
+
+typedef struct {
+ /* The lines that we allocate ourself are allocated out of the pool.
+ * (Lines may have been allocated out of the diff.)
+ */
+ git_pool pool;
+ git_vector lines;
+} patch_image;
+
+static int apply_err(const char *fmt, ...) GIT_FORMAT_PRINTF(1, 2);
+static int apply_err(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ git_error_vset(GIT_ERROR_PATCH, fmt, ap);
+ va_end(ap);
+
+ return GIT_EAPPLYFAIL;
+}
+
+static void patch_line_init(
+ git_diff_line *out,
+ const char *in,
+ size_t in_len,
+ size_t in_offset)
+{
+ out->content = in;
+ out->content_len = in_len;
+ out->content_offset = in_offset;
+}
+
+#define PATCH_IMAGE_INIT { GIT_POOL_INIT, GIT_VECTOR_INIT }
+
+static int patch_image_init_fromstr(
+ patch_image *out, const char *in, size_t in_len)
+{
+ git_diff_line *line;
+ const char *start, *end;
+
+ memset(out, 0x0, sizeof(patch_image));
+
+ if (git_pool_init(&out->pool, sizeof(git_diff_line)) < 0)
+ return -1;
+
+ if (!in_len)
+ return 0;
+
+ for (start = in; start < in + in_len; start = end) {
+ end = memchr(start, '\n', in_len - (start - in));
+
+ if (end == NULL)
+ end = in + in_len;
+
+ else if (end < in + in_len)
+ end++;
+
+ line = git_pool_mallocz(&out->pool, 1);
+ GIT_ERROR_CHECK_ALLOC(line);
+
+ if (git_vector_insert(&out->lines, line) < 0)
+ return -1;
+
+ patch_line_init(line, start, (end - start), (start - in));
+ }
+
+ return 0;
+}
+
+static void patch_image_free(patch_image *image)
+{
+ if (image == NULL)
+ return;
+
+ git_pool_clear(&image->pool);
+ git_vector_free(&image->lines);
+}
+
+static bool match_hunk(
+ patch_image *image,
+ patch_image *preimage,
+ size_t linenum)
+{
+ bool match = 0;
+ size_t i;
+
+ /* Ensure this hunk is within the image boundaries. */
+ if (git_vector_length(&preimage->lines) + linenum >
+ git_vector_length(&image->lines))
+ return 0;
+
+ match = 1;
+
+ /* Check exact match. */
+ for (i = 0; i < git_vector_length(&preimage->lines); i++) {
+ git_diff_line *preimage_line = git_vector_get(&preimage->lines, i);
+ git_diff_line *image_line = git_vector_get(&image->lines, linenum + i);
+
+ if (preimage_line->content_len != image_line->content_len ||
+ memcmp(preimage_line->content, image_line->content, image_line->content_len) != 0) {
+ match = 0;
+ break;
+ }
+ }
+
+ return match;
+}
+
+static bool find_hunk_linenum(
+ size_t *out,
+ patch_image *image,
+ patch_image *preimage,
+ size_t linenum)
+{
+ size_t max = git_vector_length(&image->lines);
+ bool match;
+
+ if (linenum > max)
+ linenum = max;
+
+ match = match_hunk(image, preimage, linenum);
+
+ *out = linenum;
+ return match;
+}
+
+static int update_hunk(
+ patch_image *image,
+ size_t linenum,
+ patch_image *preimage,
+ patch_image *postimage)
+{
+ size_t postlen = git_vector_length(&postimage->lines);
+ size_t prelen = git_vector_length(&preimage->lines);
+ size_t i;
+ int error = 0;
+
+ if (postlen > prelen)
+ error = git_vector_insert_null(
+ &image->lines, linenum, (postlen - prelen));
+ else if (prelen > postlen)
+ error = git_vector_remove_range(
+ &image->lines, linenum, (prelen - postlen));
+
+ if (error) {
+ git_error_set_oom();
+ return -1;
+ }
+
+ for (i = 0; i < git_vector_length(&postimage->lines); i++) {
+ image->lines.contents[linenum + i] =
+ git_vector_get(&postimage->lines, i);
+ }
+
+ return 0;
+}
+
+typedef struct {
+ git_apply_options opts;
+ size_t skipped_new_lines;
+ size_t skipped_old_lines;
+} apply_hunks_ctx;
+
+static int apply_hunk(
+ patch_image *image,
+ git_patch *patch,
+ git_patch_hunk *hunk,
+ apply_hunks_ctx *ctx)
+{
+ patch_image preimage = PATCH_IMAGE_INIT, postimage = PATCH_IMAGE_INIT;
+ size_t line_num, i;
+ int error = 0;
+
+ if (ctx->opts.hunk_cb) {
+ error = ctx->opts.hunk_cb(&hunk->hunk, ctx->opts.payload);
+
+ if (error) {
+ if (error > 0) {
+ ctx->skipped_new_lines += hunk->hunk.new_lines;
+ ctx->skipped_old_lines += hunk->hunk.old_lines;
+ error = 0;
+ }
+
+ goto done;
+ }
+ }
+
+ for (i = 0; i < hunk->line_count; i++) {
+ size_t linenum = hunk->line_start + i;
+ git_diff_line *line = git_array_get(patch->lines, linenum), *prev;
+
+ if (!line) {
+ error = apply_err("preimage does not contain line %"PRIuZ, linenum);
+ goto done;
+ }
+
+ switch (line->origin) {
+ case GIT_DIFF_LINE_CONTEXT_EOFNL:
+ case GIT_DIFF_LINE_DEL_EOFNL:
+ case GIT_DIFF_LINE_ADD_EOFNL:
+ prev = i ? git_array_get(patch->lines, linenum - 1) : NULL;
+ if (prev && prev->content[prev->content_len - 1] == '\n')
+ prev->content_len -= 1;
+ break;
+ case GIT_DIFF_LINE_CONTEXT:
+ if ((error = git_vector_insert(&preimage.lines, line)) < 0 ||
+ (error = git_vector_insert(&postimage.lines, line)) < 0)
+ goto done;
+ break;
+ case GIT_DIFF_LINE_DELETION:
+ if ((error = git_vector_insert(&preimage.lines, line)) < 0)
+ goto done;
+ break;
+ case GIT_DIFF_LINE_ADDITION:
+ if ((error = git_vector_insert(&postimage.lines, line)) < 0)
+ goto done;
+ break;
+ }
+ }
+
+ if (hunk->hunk.new_start) {
+ line_num = hunk->hunk.new_start -
+ ctx->skipped_new_lines +
+ ctx->skipped_old_lines -
+ 1;
+ } else {
+ line_num = 0;
+ }
+
+ if (!find_hunk_linenum(&line_num, image, &preimage, line_num)) {
+ error = apply_err("hunk at line %d did not apply",
+ hunk->hunk.new_start);
+ goto done;
+ }
+
+ error = update_hunk(image, line_num, &preimage, &postimage);
+
+done:
+ patch_image_free(&preimage);
+ patch_image_free(&postimage);
+
+ return error;
+}
+
+static int apply_hunks(
+ git_str *out,
+ const char *source,
+ size_t source_len,
+ git_patch *patch,
+ apply_hunks_ctx *ctx)
+{
+ git_patch_hunk *hunk;
+ git_diff_line *line;
+ patch_image image;
+ size_t i;
+ int error = 0;
+
+ if ((error = patch_image_init_fromstr(&image, source, source_len)) < 0)
+ goto done;
+
+ git_array_foreach(patch->hunks, i, hunk) {
+ if ((error = apply_hunk(&image, patch, hunk, ctx)) < 0)
+ goto done;
+ }
+
+ git_vector_foreach(&image.lines, i, line)
+ git_str_put(out, line->content, line->content_len);
+
+done:
+ patch_image_free(&image);
+
+ return error;
+}
+
+static int apply_binary_delta(
+ git_str *out,
+ const char *source,
+ size_t source_len,
+ git_diff_binary_file *binary_file)
+{
+ git_str inflated = GIT_STR_INIT;
+ int error = 0;
+
+ /* no diff means identical contents */
+ if (binary_file->datalen == 0)
+ return git_str_put(out, source, source_len);
+
+ error = git_zstream_inflatebuf(&inflated,
+ binary_file->data, binary_file->datalen);
+
+ if (!error && inflated.size != binary_file->inflatedlen) {
+ error = apply_err("inflated delta does not match expected length");
+ git_str_dispose(out);
+ }
+
+ if (error < 0)
+ goto done;
+
+ if (binary_file->type == GIT_DIFF_BINARY_DELTA) {
+ void *data;
+ size_t data_len;
+
+ error = git_delta_apply(&data, &data_len, (void *)source, source_len,
+ (void *)inflated.ptr, inflated.size);
+
+ out->ptr = data;
+ out->size = data_len;
+ out->asize = data_len;
+ }
+ else if (binary_file->type == GIT_DIFF_BINARY_LITERAL) {
+ git_str_swap(out, &inflated);
+ }
+ else {
+ error = apply_err("unknown binary delta type");
+ goto done;
+ }
+
+done:
+ git_str_dispose(&inflated);
+ return error;
+}
+
+static int apply_binary(
+ git_str *out,
+ const char *source,
+ size_t source_len,
+ git_patch *patch)
+{
+ git_str reverse = GIT_STR_INIT;
+ int error = 0;
+
+ if (!patch->binary.contains_data) {
+ error = apply_err("patch does not contain binary data");
+ goto done;
+ }
+
+ if (!patch->binary.old_file.datalen && !patch->binary.new_file.datalen)
+ goto done;
+
+ /* first, apply the new_file delta to the given source */
+ if ((error = apply_binary_delta(out, source, source_len,
+ &patch->binary.new_file)) < 0)
+ goto done;
+
+ /* second, apply the old_file delta to sanity check the result */
+ if ((error = apply_binary_delta(&reverse, out->ptr, out->size,
+ &patch->binary.old_file)) < 0)
+ goto done;
+
+ /* Verify that the resulting file with the reverse patch applied matches the source file */
+ if (source_len != reverse.size ||
+ (source_len && memcmp(source, reverse.ptr, source_len) != 0)) {
+ error = apply_err("binary patch did not apply cleanly");
+ goto done;
+ }
+
+done:
+ if (error < 0)
+ git_str_dispose(out);
+
+ git_str_dispose(&reverse);
+ return error;
+}
+
+int git_apply__patch(
+ git_str *contents_out,
+ char **filename_out,
+ unsigned int *mode_out,
+ const char *source,
+ size_t source_len,
+ git_patch *patch,
+ const git_apply_options *given_opts)
+{
+ apply_hunks_ctx ctx = { GIT_APPLY_OPTIONS_INIT };
+ char *filename = NULL;
+ unsigned int mode = 0;
+ int error = 0;
+
+ GIT_ASSERT_ARG(contents_out);
+ GIT_ASSERT_ARG(filename_out);
+ GIT_ASSERT_ARG(mode_out);
+ GIT_ASSERT_ARG(source || !source_len);
+ GIT_ASSERT_ARG(patch);
+
+ if (given_opts)
+ memcpy(&ctx.opts, given_opts, sizeof(git_apply_options));
+
+ *filename_out = NULL;
+ *mode_out = 0;
+
+ if (patch->delta->status != GIT_DELTA_DELETED) {
+ const git_diff_file *newfile = &patch->delta->new_file;
+
+ filename = git__strdup(newfile->path);
+ mode = newfile->mode ?
+ newfile->mode : GIT_FILEMODE_BLOB;
+ }
+
+ if (patch->delta->flags & GIT_DIFF_FLAG_BINARY)
+ error = apply_binary(contents_out, source, source_len, patch);
+ else if (patch->hunks.size)
+ error = apply_hunks(contents_out, source, source_len, patch, &ctx);
+ else
+ error = git_str_put(contents_out, source, source_len);
+
+ if (error)
+ goto done;
+
+ if (patch->delta->status == GIT_DELTA_DELETED &&
+ git_str_len(contents_out) > 0) {
+ error = apply_err("removal patch leaves file contents");
+ goto done;
+ }
+
+ *filename_out = filename;
+ *mode_out = mode;
+
+done:
+ if (error < 0)
+ git__free(filename);
+
+ return error;
+}
+
+static int apply_one(
+ git_repository *repo,
+ git_reader *preimage_reader,
+ git_index *preimage,
+ git_reader *postimage_reader,
+ git_index *postimage,
+ git_diff *diff,
+ git_strmap *removed_paths,
+ size_t i,
+ const git_apply_options *opts)
+{
+ git_patch *patch = NULL;
+ git_str pre_contents = GIT_STR_INIT, post_contents = GIT_STR_INIT;
+ const git_diff_delta *delta;
+ char *filename = NULL;
+ unsigned int mode;
+ git_oid pre_id, post_id;
+ git_filemode_t pre_filemode;
+ git_index_entry pre_entry, post_entry;
+ bool skip_preimage = false;
+ int error;
+
+ if ((error = git_patch_from_diff(&patch, diff, i)) < 0)
+ goto done;
+
+ delta = git_patch_get_delta(patch);
+
+ if (opts->delta_cb) {
+ error = opts->delta_cb(delta, opts->payload);
+
+ if (error) {
+ if (error > 0)
+ error = 0;
+
+ goto done;
+ }
+ }
+
+ /*
+ * Ensure that the file has not been deleted or renamed if we're
+ * applying a modification delta.
+ */
+ if (delta->status != GIT_DELTA_RENAMED &&
+ delta->status != GIT_DELTA_ADDED) {
+ if (git_strmap_exists(removed_paths, delta->old_file.path)) {
+ error = apply_err("path '%s' has been renamed or deleted", delta->old_file.path);
+ goto done;
+ }
+ }
+
+ /*
+ * We may be applying a second delta to an already seen file. If so,
+ * use the already modified data in the postimage instead of the
+ * content from the index or working directory. (Don't do this in
+ * the case of a rename, which must be specified before additional
+ * deltas since we apply deltas to the target filename.)
+ */
+ if (delta->status != GIT_DELTA_RENAMED) {
+ if ((error = git_reader_read(&pre_contents, &pre_id, &pre_filemode,
+ postimage_reader, delta->old_file.path)) == 0) {
+ skip_preimage = true;
+ } else if (error == GIT_ENOTFOUND) {
+ git_error_clear();
+ error = 0;
+ } else {
+ goto done;
+ }
+ }
+
+ if (!skip_preimage && delta->status != GIT_DELTA_ADDED) {
+ error = git_reader_read(&pre_contents, &pre_id, &pre_filemode,
+ preimage_reader, delta->old_file.path);
+
+ /* ENOTFOUND means the preimage was not found; apply failed. */
+ if (error == GIT_ENOTFOUND)
+ error = GIT_EAPPLYFAIL;
+
+ /* When applying to BOTH, the index did not match the workdir. */
+ if (error == GIT_READER_MISMATCH)
+ error = apply_err("%s: does not match index", delta->old_file.path);
+
+ if (error < 0)
+ goto done;
+
+ /*
+ * We need to populate the preimage data structure with the
+ * contents that we are using as the preimage for this file.
+ * This allows us to apply patches to files that have been
+ * modified in the working directory. During checkout,
+ * we will use this expected preimage as the baseline, and
+ * limit checkout to only the paths affected by patch
+ * application. (Without this, we would fail to write the
+ * postimage contents to any file that had been modified
+ * from HEAD on-disk, even if the patch application succeeded.)
+ * Use the contents from the delta where available - some
+ * fields may not be available, like the old file mode (eg in
+ * an exact rename situation) so trust the patch parsing to
+ * validate and use the preimage data in that case.
+ */
+ if (preimage) {
+ memset(&pre_entry, 0, sizeof(git_index_entry));
+ pre_entry.path = delta->old_file.path;
+ pre_entry.mode = delta->old_file.mode ? delta->old_file.mode : pre_filemode;
+ git_oid_cpy(&pre_entry.id, &pre_id);
+
+ if ((error = git_index_add(preimage, &pre_entry)) < 0)
+ goto done;
+ }
+ }
+
+ if (delta->status != GIT_DELTA_DELETED) {
+ if ((error = git_apply__patch(&post_contents, &filename, &mode,
+ pre_contents.ptr, pre_contents.size, patch, opts)) < 0 ||
+ (error = git_blob_create_from_buffer(&post_id, repo,
+ post_contents.ptr, post_contents.size)) < 0)
+ goto done;
+
+ memset(&post_entry, 0, sizeof(git_index_entry));
+ post_entry.path = filename;
+ post_entry.mode = mode;
+ git_oid_cpy(&post_entry.id, &post_id);
+
+ if ((error = git_index_add(postimage, &post_entry)) < 0)
+ goto done;
+ }
+
+ if (delta->status == GIT_DELTA_RENAMED ||
+ delta->status == GIT_DELTA_DELETED)
+ error = git_strmap_set(removed_paths, delta->old_file.path, (char *) delta->old_file.path);
+
+ if (delta->status == GIT_DELTA_RENAMED ||
+ delta->status == GIT_DELTA_ADDED)
+ git_strmap_delete(removed_paths, delta->new_file.path);
+
+done:
+ git_str_dispose(&pre_contents);
+ git_str_dispose(&post_contents);
+ git__free(filename);
+ git_patch_free(patch);
+
+ return error;
+}
+
+static int apply_deltas(
+ git_repository *repo,
+ git_reader *pre_reader,
+ git_index *preimage,
+ git_reader *post_reader,
+ git_index *postimage,
+ git_diff *diff,
+ const git_apply_options *opts)
+{
+ git_strmap *removed_paths;
+ size_t i;
+ int error = 0;
+
+ if (git_strmap_new(&removed_paths) < 0)
+ return -1;
+
+ for (i = 0; i < git_diff_num_deltas(diff); i++) {
+ if ((error = apply_one(repo, pre_reader, preimage, post_reader, postimage, diff, removed_paths, i, opts)) < 0)
+ goto done;
+ }
+
+done:
+ git_strmap_free(removed_paths);
+ return error;
+}
+
+int git_apply_to_tree(
+ git_index **out,
+ git_repository *repo,
+ git_tree *preimage,
+ git_diff *diff,
+ const git_apply_options *given_opts)
+{
+ git_index *postimage = NULL;
+ git_reader *pre_reader = NULL, *post_reader = NULL;
+ git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
+ const git_diff_delta *delta;
+ size_t i;
+ int error = 0;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(preimage);
+ GIT_ASSERT_ARG(diff);
+
+ *out = NULL;
+
+ if (given_opts)
+ memcpy(&opts, given_opts, sizeof(git_apply_options));
+
+ if ((error = git_reader_for_tree(&pre_reader, preimage)) < 0)
+ goto done;
+
+ /*
+ * put the current tree into the postimage as-is - the diff will
+ * replace any entries contained therein
+ */
+ if ((error = git_index__new(&postimage, repo->oid_type)) < 0 ||
+ (error = git_index_read_tree(postimage, preimage)) < 0 ||
+ (error = git_reader_for_index(&post_reader, repo, postimage)) < 0)
+ goto done;
+
+ /*
+ * Remove the old paths from the index before applying diffs -
+ * we need to do a full pass to remove them before adding deltas,
+ * in order to handle rename situations.
+ */
+ for (i = 0; i < git_diff_num_deltas(diff); i++) {
+ delta = git_diff_get_delta(diff, i);
+
+ if (delta->status == GIT_DELTA_DELETED ||
+ delta->status == GIT_DELTA_RENAMED) {
+ if ((error = git_index_remove(postimage,
+ delta->old_file.path, 0)) < 0)
+ goto done;
+ }
+ }
+
+ if ((error = apply_deltas(repo, pre_reader, NULL, post_reader, postimage, diff, &opts)) < 0)
+ goto done;
+
+ *out = postimage;
+
+done:
+ if (error < 0)
+ git_index_free(postimage);
+
+ git_reader_free(pre_reader);
+ git_reader_free(post_reader);
+
+ return error;
+}
+
+static int git_apply__to_workdir(
+ git_repository *repo,
+ git_diff *diff,
+ git_index *preimage,
+ git_index *postimage,
+ git_apply_location_t location,
+ git_apply_options *opts)
+{
+ git_vector paths = GIT_VECTOR_INIT;
+ git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
+ const git_diff_delta *delta;
+ size_t i;
+ int error;
+
+ GIT_UNUSED(opts);
+
+ /*
+ * Limit checkout to the paths affected by the diff; this ensures
+ * that other modifications in the working directory are unaffected.
+ */
+ if ((error = git_vector_init(&paths, git_diff_num_deltas(diff), NULL)) < 0)
+ goto done;
+
+ for (i = 0; i < git_diff_num_deltas(diff); i++) {
+ delta = git_diff_get_delta(diff, i);
+
+ if ((error = git_vector_insert(&paths, (void *)delta->old_file.path)) < 0)
+ goto done;
+
+ if (strcmp(delta->old_file.path, delta->new_file.path) &&
+ (error = git_vector_insert(&paths, (void *)delta->new_file.path)) < 0)
+ goto done;
+ }
+
+ checkout_opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
+ checkout_opts.checkout_strategy |= GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH;
+ checkout_opts.checkout_strategy |= GIT_CHECKOUT_DONT_WRITE_INDEX;
+
+ if (location == GIT_APPLY_LOCATION_WORKDIR)
+ checkout_opts.checkout_strategy |= GIT_CHECKOUT_DONT_UPDATE_INDEX;
+
+ checkout_opts.paths.strings = (char **)paths.contents;
+ checkout_opts.paths.count = paths.length;
+
+ checkout_opts.baseline_index = preimage;
+
+ error = git_checkout_index(repo, postimage, &checkout_opts);
+
+done:
+ git_vector_free(&paths);
+ return error;
+}
+
+static int git_apply__to_index(
+ git_repository *repo,
+ git_diff *diff,
+ git_index *preimage,
+ git_index *postimage,
+ git_apply_options *opts)
+{
+ git_index *index = NULL;
+ const git_diff_delta *delta;
+ const git_index_entry *entry;
+ size_t i;
+ int error;
+
+ GIT_UNUSED(preimage);
+ GIT_UNUSED(opts);
+
+ if ((error = git_repository_index(&index, repo)) < 0)
+ goto done;
+
+ /* Remove deleted (or renamed) paths from the index. */
+ for (i = 0; i < git_diff_num_deltas(diff); i++) {
+ delta = git_diff_get_delta(diff, i);
+
+ if (delta->status == GIT_DELTA_DELETED ||
+ delta->status == GIT_DELTA_RENAMED) {
+ if ((error = git_index_remove(index, delta->old_file.path, 0)) < 0)
+ goto done;
+ }
+ }
+
+ /* Then add the changes back to the index. */
+ for (i = 0; i < git_index_entrycount(postimage); i++) {
+ entry = git_index_get_byindex(postimage, i);
+
+ if ((error = git_index_add(index, entry)) < 0)
+ goto done;
+ }
+
+done:
+ git_index_free(index);
+ return error;
+}
+
+int git_apply_options_init(git_apply_options *opts, unsigned int version)
+{
+ GIT_ASSERT_ARG(opts);
+
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_apply_options, GIT_APPLY_OPTIONS_INIT);
+ return 0;
+}
+
+/*
+ * Handle the three application options ("locations"):
+ *
+ * GIT_APPLY_LOCATION_WORKDIR: the default, emulates `git apply`.
+ * Applies the diff only to the workdir items and ignores the index
+ * entirely.
+ *
+ * GIT_APPLY_LOCATION_INDEX: emulates `git apply --cached`.
+ * Applies the diff only to the index items and ignores the workdir
+ * completely.
+ *
+ * GIT_APPLY_LOCATION_BOTH: emulates `git apply --index`.
+ * Applies the diff to both the index items and the working directory
+ * items.
+ */
+
+int git_apply(
+ git_repository *repo,
+ git_diff *diff,
+ git_apply_location_t location,
+ const git_apply_options *given_opts)
+{
+ git_indexwriter indexwriter = GIT_INDEXWRITER_INIT;
+ git_index *index = NULL, *preimage = NULL, *postimage = NULL;
+ git_reader *pre_reader = NULL, *post_reader = NULL;
+ git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
+ int error = GIT_EINVALID;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(diff);
+
+ GIT_ERROR_CHECK_VERSION(
+ given_opts, GIT_APPLY_OPTIONS_VERSION, "git_apply_options");
+
+ if (given_opts)
+ memcpy(&opts, given_opts, sizeof(git_apply_options));
+
+ /*
+ * by default, we apply a patch directly to the working directory;
+ * in `--cached` or `--index` mode, we apply to the contents already
+ * in the index.
+ */
+ switch (location) {
+ case GIT_APPLY_LOCATION_BOTH:
+ error = git_reader_for_workdir(&pre_reader, repo, true);
+ break;
+ case GIT_APPLY_LOCATION_INDEX:
+ error = git_reader_for_index(&pre_reader, repo, NULL);
+ break;
+ case GIT_APPLY_LOCATION_WORKDIR:
+ error = git_reader_for_workdir(&pre_reader, repo, false);
+ break;
+ default:
+ GIT_ASSERT(false);
+ }
+
+ if (error < 0)
+ goto done;
+
+ /*
+ * Build the preimage and postimage (differences). Note that
+ * this is not the complete preimage or postimage, it only
+ * contains the files affected by the patch. We want to avoid
+ * having the full repo index, so we will limit our checkout
+ * to only write these files that were affected by the diff.
+ */
+ if ((error = git_index__new(&preimage, repo->oid_type)) < 0 ||
+ (error = git_index__new(&postimage, repo->oid_type)) < 0 ||
+ (error = git_reader_for_index(&post_reader, repo, postimage)) < 0)
+ goto done;
+
+ if (!(opts.flags & GIT_APPLY_CHECK))
+ if ((error = git_repository_index(&index, repo)) < 0 ||
+ (error = git_indexwriter_init(&indexwriter, index)) < 0)
+ goto done;
+
+ if ((error = apply_deltas(repo, pre_reader, preimage, post_reader, postimage, diff, &opts)) < 0)
+ goto done;
+
+ if ((opts.flags & GIT_APPLY_CHECK))
+ goto done;
+
+ switch (location) {
+ case GIT_APPLY_LOCATION_BOTH:
+ error = git_apply__to_workdir(repo, diff, preimage, postimage, location, &opts);
+ break;
+ case GIT_APPLY_LOCATION_INDEX:
+ error = git_apply__to_index(repo, diff, preimage, postimage, &opts);
+ break;
+ case GIT_APPLY_LOCATION_WORKDIR:
+ error = git_apply__to_workdir(repo, diff, preimage, postimage, location, &opts);
+ break;
+ default:
+ GIT_ASSERT(false);
+ }
+
+ if (error < 0)
+ goto done;
+
+ error = git_indexwriter_commit(&indexwriter);
+
+done:
+ git_indexwriter_cleanup(&indexwriter);
+ git_index_free(postimage);
+ git_index_free(preimage);
+ git_index_free(index);
+ git_reader_free(pre_reader);
+ git_reader_free(post_reader);
+
+ return error;
+}
diff --git a/src/libgit2/apply.h b/src/libgit2/apply.h
new file mode 100644
index 0000000..e990a71
--- /dev/null
+++ b/src/libgit2/apply.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_apply_h__
+#define INCLUDE_apply_h__
+
+#include "common.h"
+
+#include "git2/patch.h"
+#include "git2/apply.h"
+#include "str.h"
+
+extern int git_apply__patch(
+ git_str *out,
+ char **filename,
+ unsigned int *mode,
+ const char *source,
+ size_t source_len,
+ git_patch *patch,
+ const git_apply_options *opts);
+
+#endif
diff --git a/src/libgit2/attr.c b/src/libgit2/attr.c
new file mode 100644
index 0000000..1623b1d
--- /dev/null
+++ b/src/libgit2/attr.c
@@ -0,0 +1,700 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "attr.h"
+
+#include "repository.h"
+#include "sysdir.h"
+#include "config.h"
+#include "attr_file.h"
+#include "ignore.h"
+#include "git2/oid.h"
+#include <ctype.h>
+
+const char *git_attr__true = "[internal]__TRUE__";
+const char *git_attr__false = "[internal]__FALSE__";
+const char *git_attr__unset = "[internal]__UNSET__";
+
+git_attr_value_t git_attr_value(const char *attr)
+{
+ if (attr == NULL || attr == git_attr__unset)
+ return GIT_ATTR_VALUE_UNSPECIFIED;
+
+ if (attr == git_attr__true)
+ return GIT_ATTR_VALUE_TRUE;
+
+ if (attr == git_attr__false)
+ return GIT_ATTR_VALUE_FALSE;
+
+ return GIT_ATTR_VALUE_STRING;
+}
+
+static int collect_attr_files(
+ git_repository *repo,
+ git_attr_session *attr_session,
+ git_attr_options *opts,
+ const char *path,
+ git_vector *files);
+
+static void release_attr_files(git_vector *files);
+
+int git_attr_get_ext(
+ const char **value,
+ git_repository *repo,
+ git_attr_options *opts,
+ const char *pathname,
+ const char *name)
+{
+ int error;
+ git_attr_path path;
+ git_vector files = GIT_VECTOR_INIT;
+ size_t i, j;
+ git_attr_file *file;
+ git_attr_name attr;
+ git_attr_rule *rule;
+ git_dir_flag dir_flag = GIT_DIR_FLAG_UNKNOWN;
+
+ GIT_ASSERT_ARG(value);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
+ GIT_ERROR_CHECK_VERSION(opts, GIT_ATTR_OPTIONS_VERSION, "git_attr_options");
+
+ *value = NULL;
+
+ if (git_repository_is_bare(repo))
+ dir_flag = GIT_DIR_FLAG_FALSE;
+
+ if (git_attr_path__init(&path, pathname, git_repository_workdir(repo), dir_flag) < 0)
+ return -1;
+
+ if ((error = collect_attr_files(repo, NULL, opts, pathname, &files)) < 0)
+ goto cleanup;
+
+ memset(&attr, 0, sizeof(attr));
+ attr.name = name;
+ attr.name_hash = git_attr_file__name_hash(name);
+
+ git_vector_foreach(&files, i, file) {
+
+ git_attr_file__foreach_matching_rule(file, &path, j, rule) {
+ size_t pos;
+
+ if (!git_vector_bsearch(&pos, &rule->assigns, &attr)) {
+ *value = ((git_attr_assignment *)git_vector_get(
+ &rule->assigns, pos))->value;
+ goto cleanup;
+ }
+ }
+ }
+
+cleanup:
+ release_attr_files(&files);
+ git_attr_path__free(&path);
+
+ return error;
+}
+
+int git_attr_get(
+ const char **value,
+ git_repository *repo,
+ uint32_t flags,
+ const char *pathname,
+ const char *name)
+{
+ git_attr_options opts = GIT_ATTR_OPTIONS_INIT;
+
+ opts.flags = flags;
+
+ return git_attr_get_ext(value, repo, &opts, pathname, name);
+}
+
+
+typedef struct {
+ git_attr_name name;
+ git_attr_assignment *found;
+} attr_get_many_info;
+
+int git_attr_get_many_with_session(
+ const char **values,
+ git_repository *repo,
+ git_attr_session *attr_session,
+ git_attr_options *opts,
+ const char *pathname,
+ size_t num_attr,
+ const char **names)
+{
+ int error;
+ git_attr_path path;
+ git_vector files = GIT_VECTOR_INIT;
+ size_t i, j, k;
+ git_attr_file *file;
+ git_attr_rule *rule;
+ attr_get_many_info *info = NULL;
+ size_t num_found = 0;
+ git_dir_flag dir_flag = GIT_DIR_FLAG_UNKNOWN;
+
+ if (!num_attr)
+ return 0;
+
+ GIT_ASSERT_ARG(values);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(pathname);
+ GIT_ASSERT_ARG(names);
+ GIT_ERROR_CHECK_VERSION(opts, GIT_ATTR_OPTIONS_VERSION, "git_attr_options");
+
+ if (git_repository_is_bare(repo))
+ dir_flag = GIT_DIR_FLAG_FALSE;
+
+ if (git_attr_path__init(&path, pathname, git_repository_workdir(repo), dir_flag) < 0)
+ return -1;
+
+ if ((error = collect_attr_files(repo, attr_session, opts, pathname, &files)) < 0)
+ goto cleanup;
+
+ info = git__calloc(num_attr, sizeof(attr_get_many_info));
+ GIT_ERROR_CHECK_ALLOC(info);
+
+ git_vector_foreach(&files, i, file) {
+
+ git_attr_file__foreach_matching_rule(file, &path, j, rule) {
+
+ for (k = 0; k < num_attr; k++) {
+ size_t pos;
+
+ if (info[k].found != NULL) /* already found assignment */
+ continue;
+
+ if (!info[k].name.name) {
+ info[k].name.name = names[k];
+ info[k].name.name_hash = git_attr_file__name_hash(names[k]);
+ }
+
+ if (!git_vector_bsearch(&pos, &rule->assigns, &info[k].name)) {
+ info[k].found = (git_attr_assignment *)
+ git_vector_get(&rule->assigns, pos);
+ values[k] = info[k].found->value;
+
+ if (++num_found == num_attr)
+ goto cleanup;
+ }
+ }
+ }
+ }
+
+ for (k = 0; k < num_attr; k++) {
+ if (!info[k].found)
+ values[k] = NULL;
+ }
+
+cleanup:
+ release_attr_files(&files);
+ git_attr_path__free(&path);
+ git__free(info);
+
+ return error;
+}
+
+int git_attr_get_many(
+ const char **values,
+ git_repository *repo,
+ uint32_t flags,
+ const char *pathname,
+ size_t num_attr,
+ const char **names)
+{
+ git_attr_options opts = GIT_ATTR_OPTIONS_INIT;
+
+ opts.flags = flags;
+
+ return git_attr_get_many_with_session(
+ values, repo, NULL, &opts, pathname, num_attr, names);
+}
+
+int git_attr_get_many_ext(
+ const char **values,
+ git_repository *repo,
+ git_attr_options *opts,
+ const char *pathname,
+ size_t num_attr,
+ const char **names)
+{
+ return git_attr_get_many_with_session(
+ values, repo, NULL, opts, pathname, num_attr, names);
+}
+
+int git_attr_foreach(
+ git_repository *repo,
+ uint32_t flags,
+ const char *pathname,
+ int (*callback)(const char *name, const char *value, void *payload),
+ void *payload)
+{
+ git_attr_options opts = GIT_ATTR_OPTIONS_INIT;
+
+ opts.flags = flags;
+
+ return git_attr_foreach_ext(repo, &opts, pathname, callback, payload);
+}
+
+int git_attr_foreach_ext(
+ git_repository *repo,
+ git_attr_options *opts,
+ const char *pathname,
+ int (*callback)(const char *name, const char *value, void *payload),
+ void *payload)
+{
+ int error;
+ git_attr_path path;
+ git_vector files = GIT_VECTOR_INIT;
+ size_t i, j, k;
+ git_attr_file *file;
+ git_attr_rule *rule;
+ git_attr_assignment *assign;
+ git_strmap *seen = NULL;
+ git_dir_flag dir_flag = GIT_DIR_FLAG_UNKNOWN;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(callback);
+ GIT_ERROR_CHECK_VERSION(opts, GIT_ATTR_OPTIONS_VERSION, "git_attr_options");
+
+ if (git_repository_is_bare(repo))
+ dir_flag = GIT_DIR_FLAG_FALSE;
+
+ if (git_attr_path__init(&path, pathname, git_repository_workdir(repo), dir_flag) < 0)
+ return -1;
+
+ if ((error = collect_attr_files(repo, NULL, opts, pathname, &files)) < 0 ||
+ (error = git_strmap_new(&seen)) < 0)
+ goto cleanup;
+
+ git_vector_foreach(&files, i, file) {
+
+ git_attr_file__foreach_matching_rule(file, &path, j, rule) {
+
+ git_vector_foreach(&rule->assigns, k, assign) {
+ /* skip if higher priority assignment was already seen */
+ if (git_strmap_exists(seen, assign->name))
+ continue;
+
+ if ((error = git_strmap_set(seen, assign->name, assign)) < 0)
+ goto cleanup;
+
+ error = callback(assign->name, assign->value, payload);
+ if (error) {
+ git_error_set_after_callback(error);
+ goto cleanup;
+ }
+ }
+ }
+ }
+
+cleanup:
+ git_strmap_free(seen);
+ release_attr_files(&files);
+ git_attr_path__free(&path);
+
+ return error;
+}
+
+static int preload_attr_source(
+ git_repository *repo,
+ git_attr_session *attr_session,
+ git_attr_file_source *source)
+{
+ int error;
+ git_attr_file *preload = NULL;
+
+ if (!source)
+ return 0;
+
+ error = git_attr_cache__get(&preload, repo, attr_session, source,
+ git_attr_file__parse_buffer, true);
+
+ if (!error)
+ git_attr_file__free(preload);
+
+ return error;
+}
+
+GIT_INLINE(int) preload_attr_file(
+ git_repository *repo,
+ git_attr_session *attr_session,
+ const char *base,
+ const char *filename)
+{
+ git_attr_file_source source = { GIT_ATTR_FILE_SOURCE_FILE };
+
+ if (!filename)
+ return 0;
+
+ source.base = base;
+ source.filename = filename;
+
+ return preload_attr_source(repo, attr_session, &source);
+}
+
+static int system_attr_file(
+ git_str *out,
+ git_attr_session *attr_session)
+{
+ int error;
+
+ if (!attr_session) {
+ error = git_sysdir_find_system_file(out, GIT_ATTR_FILE_SYSTEM);
+
+ if (error == GIT_ENOTFOUND)
+ git_error_clear();
+
+ return error;
+ }
+
+ if (!attr_session->init_sysdir) {
+ error = git_sysdir_find_system_file(&attr_session->sysdir, GIT_ATTR_FILE_SYSTEM);
+
+ if (error == GIT_ENOTFOUND)
+ git_error_clear();
+ else if (error)
+ return error;
+
+ attr_session->init_sysdir = 1;
+ }
+
+ if (attr_session->sysdir.size == 0)
+ return GIT_ENOTFOUND;
+
+ /* We can safely provide a git_str with no allocation (asize == 0) to
+ * a consumer. This allows them to treat this as a regular `git_str`,
+ * but their call to `git_str_dispose` will not attempt to free it.
+ */
+ git_str_attach_notowned(
+ out, attr_session->sysdir.ptr, attr_session->sysdir.size);
+ return 0;
+}
+
+static int attr_setup(
+ git_repository *repo,
+ git_attr_session *attr_session,
+ git_attr_options *opts)
+{
+ git_str system = GIT_STR_INIT, info = GIT_STR_INIT;
+ git_attr_file_source index_source = { GIT_ATTR_FILE_SOURCE_INDEX, NULL, GIT_ATTR_FILE, NULL };
+ git_attr_file_source head_source = { GIT_ATTR_FILE_SOURCE_HEAD, NULL, GIT_ATTR_FILE, NULL };
+ git_attr_file_source commit_source = { GIT_ATTR_FILE_SOURCE_COMMIT, NULL, GIT_ATTR_FILE, NULL };
+ git_index *idx = NULL;
+ const char *workdir;
+ int error = 0;
+
+ if (attr_session && attr_session->init_setup)
+ return 0;
+
+ if ((error = git_attr_cache__init(repo)) < 0)
+ return error;
+
+ /*
+ * Preload attribute files that could contain macros so the
+ * definitions will be available for later file parsing.
+ */
+
+ if ((error = system_attr_file(&system, attr_session)) < 0 ||
+ (error = preload_attr_file(repo, attr_session, NULL, system.ptr)) < 0) {
+ if (error != GIT_ENOTFOUND)
+ goto out;
+
+ error = 0;
+ }
+
+ if ((error = preload_attr_file(repo, attr_session, NULL,
+ git_repository_attr_cache(repo)->cfg_attr_file)) < 0)
+ goto out;
+
+ if ((error = git_repository__item_path(&info, repo, GIT_REPOSITORY_ITEM_INFO)) < 0 ||
+ (error = preload_attr_file(repo, attr_session, info.ptr, GIT_ATTR_FILE_INREPO)) < 0) {
+ if (error != GIT_ENOTFOUND)
+ goto out;
+
+ error = 0;
+ }
+
+ if ((workdir = git_repository_workdir(repo)) != NULL &&
+ (error = preload_attr_file(repo, attr_session, workdir, GIT_ATTR_FILE)) < 0)
+ goto out;
+
+ if ((error = git_repository_index__weakptr(&idx, repo)) < 0 ||
+ (error = preload_attr_source(repo, attr_session, &index_source)) < 0)
+ goto out;
+
+ if ((opts && (opts->flags & GIT_ATTR_CHECK_INCLUDE_HEAD) != 0) &&
+ (error = preload_attr_source(repo, attr_session, &head_source)) < 0)
+ goto out;
+
+ if ((opts && (opts->flags & GIT_ATTR_CHECK_INCLUDE_COMMIT) != 0)) {
+#ifndef GIT_DEPRECATE_HARD
+ if (opts->commit_id)
+ commit_source.commit_id = opts->commit_id;
+ else
+#endif
+ commit_source.commit_id = &opts->attr_commit_id;
+
+ if ((error = preload_attr_source(repo, attr_session, &commit_source)) < 0)
+ goto out;
+ }
+
+ if (attr_session)
+ attr_session->init_setup = 1;
+
+out:
+ git_str_dispose(&system);
+ git_str_dispose(&info);
+
+ return error;
+}
+
+int git_attr_add_macro(
+ git_repository *repo,
+ const char *name,
+ const char *values)
+{
+ int error;
+ git_attr_rule *macro = NULL;
+ git_pool *pool;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
+
+ if ((error = git_attr_cache__init(repo)) < 0)
+ return error;
+
+ macro = git__calloc(1, sizeof(git_attr_rule));
+ GIT_ERROR_CHECK_ALLOC(macro);
+
+ pool = &git_repository_attr_cache(repo)->pool;
+
+ macro->match.pattern = git_pool_strdup(pool, name);
+ GIT_ERROR_CHECK_ALLOC(macro->match.pattern);
+
+ macro->match.length = strlen(macro->match.pattern);
+ macro->match.flags = GIT_ATTR_FNMATCH_MACRO;
+
+ error = git_attr_assignment__parse(repo, pool, &macro->assigns, &values);
+
+ if (!error)
+ error = git_attr_cache__insert_macro(repo, macro);
+
+ if (error < 0)
+ git_attr_rule__free(macro);
+
+ return error;
+}
+
+typedef struct {
+ git_repository *repo;
+ git_attr_session *attr_session;
+ git_attr_options *opts;
+ const char *workdir;
+ git_index *index;
+ git_vector *files;
+} attr_walk_up_info;
+
+static int attr_decide_sources(
+ uint32_t flags,
+ bool has_wd,
+ bool has_index,
+ git_attr_file_source_t *srcs)
+{
+ int count = 0;
+
+ switch (flags & 0x03) {
+ case GIT_ATTR_CHECK_FILE_THEN_INDEX:
+ if (has_wd)
+ srcs[count++] = GIT_ATTR_FILE_SOURCE_FILE;
+ if (has_index)
+ srcs[count++] = GIT_ATTR_FILE_SOURCE_INDEX;
+ break;
+ case GIT_ATTR_CHECK_INDEX_THEN_FILE:
+ if (has_index)
+ srcs[count++] = GIT_ATTR_FILE_SOURCE_INDEX;
+ if (has_wd)
+ srcs[count++] = GIT_ATTR_FILE_SOURCE_FILE;
+ break;
+ case GIT_ATTR_CHECK_INDEX_ONLY:
+ if (has_index)
+ srcs[count++] = GIT_ATTR_FILE_SOURCE_INDEX;
+ break;
+ }
+
+ if ((flags & GIT_ATTR_CHECK_INCLUDE_HEAD) != 0)
+ srcs[count++] = GIT_ATTR_FILE_SOURCE_HEAD;
+
+ if ((flags & GIT_ATTR_CHECK_INCLUDE_COMMIT) != 0)
+ srcs[count++] = GIT_ATTR_FILE_SOURCE_COMMIT;
+
+ return count;
+}
+
+static int push_attr_source(
+ git_repository *repo,
+ git_attr_session *attr_session,
+ git_vector *list,
+ git_attr_file_source *source,
+ bool allow_macros)
+{
+ int error = 0;
+ git_attr_file *file = NULL;
+
+ error = git_attr_cache__get(&file, repo, attr_session,
+ source,
+ git_attr_file__parse_buffer,
+ allow_macros);
+
+ if (error < 0)
+ return error;
+
+ if (file != NULL) {
+ if ((error = git_vector_insert(list, file)) < 0)
+ git_attr_file__free(file);
+ }
+
+ return error;
+}
+
+GIT_INLINE(int) push_attr_file(
+ git_repository *repo,
+ git_attr_session *attr_session,
+ git_vector *list,
+ const char *base,
+ const char *filename)
+{
+ git_attr_file_source source = { GIT_ATTR_FILE_SOURCE_FILE, base, filename };
+ return push_attr_source(repo, attr_session, list, &source, true);
+}
+
+static int push_one_attr(void *ref, const char *path)
+{
+ attr_walk_up_info *info = (attr_walk_up_info *)ref;
+ git_attr_file_source_t src[GIT_ATTR_FILE_NUM_SOURCES];
+ int error = 0, n_src, i;
+ bool allow_macros;
+
+ n_src = attr_decide_sources(info->opts ? info->opts->flags : 0,
+ info->workdir != NULL,
+ info->index != NULL,
+ src);
+
+ allow_macros = info->workdir ? !strcmp(info->workdir, path) : false;
+
+ for (i = 0; !error && i < n_src; ++i) {
+ git_attr_file_source source = { src[i], path, GIT_ATTR_FILE };
+
+ if (src[i] == GIT_ATTR_FILE_SOURCE_COMMIT && info->opts) {
+#ifndef GIT_DEPRECATE_HARD
+ if (info->opts->commit_id)
+ source.commit_id = info->opts->commit_id;
+ else
+#endif
+ source.commit_id = &info->opts->attr_commit_id;
+ }
+
+ error = push_attr_source(info->repo, info->attr_session, info->files,
+ &source, allow_macros);
+ }
+
+ return error;
+}
+
+static void release_attr_files(git_vector *files)
+{
+ size_t i;
+ git_attr_file *file;
+
+ git_vector_foreach(files, i, file) {
+ git_attr_file__free(file);
+ files->contents[i] = NULL;
+ }
+ git_vector_free(files);
+}
+
+static int collect_attr_files(
+ git_repository *repo,
+ git_attr_session *attr_session,
+ git_attr_options *opts,
+ const char *path,
+ git_vector *files)
+{
+ int error = 0;
+ git_str dir = GIT_STR_INIT, attrfile = GIT_STR_INIT;
+ const char *workdir = git_repository_workdir(repo);
+ attr_walk_up_info info = { NULL };
+
+ GIT_ASSERT(!git_fs_path_is_absolute(path));
+
+ if ((error = attr_setup(repo, attr_session, opts)) < 0)
+ return error;
+
+ /* Resolve path in a non-bare repo */
+ if (workdir != NULL) {
+ if (!(error = git_repository_workdir_path(&dir, repo, path)))
+ error = git_fs_path_find_dir(&dir);
+ }
+ else {
+ error = git_fs_path_dirname_r(&dir, path);
+ }
+
+ if (error < 0)
+ goto cleanup;
+
+ /* in precedence order highest to lowest:
+ * - $GIT_DIR/info/attributes
+ * - path components with .gitattributes
+ * - config core.attributesfile
+ * - $GIT_PREFIX/etc/gitattributes
+ */
+
+ if ((error = git_repository__item_path(&attrfile, repo, GIT_REPOSITORY_ITEM_INFO)) < 0 ||
+ (error = push_attr_file(repo, attr_session, files, attrfile.ptr, GIT_ATTR_FILE_INREPO)) < 0) {
+ if (error != GIT_ENOTFOUND)
+ goto cleanup;
+ }
+
+ info.repo = repo;
+ info.attr_session = attr_session;
+ info.opts = opts;
+ info.workdir = workdir;
+ if (git_repository_index__weakptr(&info.index, repo) < 0)
+ git_error_clear(); /* no error even if there is no index */
+ info.files = files;
+
+ if (!strcmp(dir.ptr, "."))
+ error = push_one_attr(&info, "");
+ else
+ error = git_fs_path_walk_up(&dir, workdir, push_one_attr, &info);
+
+ if (error < 0)
+ goto cleanup;
+
+ if (git_repository_attr_cache(repo)->cfg_attr_file != NULL) {
+ error = push_attr_file(repo, attr_session, files, NULL, git_repository_attr_cache(repo)->cfg_attr_file);
+ if (error < 0)
+ goto cleanup;
+ }
+
+ if (!opts || (opts->flags & GIT_ATTR_CHECK_NO_SYSTEM) == 0) {
+ error = system_attr_file(&dir, attr_session);
+
+ if (!error)
+ error = push_attr_file(repo, attr_session, files, NULL, dir.ptr);
+ else if (error == GIT_ENOTFOUND)
+ error = 0;
+ }
+
+ cleanup:
+ if (error < 0)
+ release_attr_files(files);
+ git_str_dispose(&attrfile);
+ git_str_dispose(&dir);
+
+ return error;
+}
diff --git a/src/libgit2/attr.h b/src/libgit2/attr.h
new file mode 100644
index 0000000..9775652
--- /dev/null
+++ b/src/libgit2/attr.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_attr_h__
+#define INCLUDE_attr_h__
+
+#include "common.h"
+
+#include "attr_file.h"
+#include "attrcache.h"
+
+#endif
diff --git a/src/libgit2/attr_file.c b/src/libgit2/attr_file.c
new file mode 100644
index 0000000..afa8ec7
--- /dev/null
+++ b/src/libgit2/attr_file.c
@@ -0,0 +1,1027 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "attr_file.h"
+
+#include "repository.h"
+#include "filebuf.h"
+#include "attrcache.h"
+#include "git2/blob.h"
+#include "git2/tree.h"
+#include "blob.h"
+#include "index.h"
+#include "wildmatch.h"
+#include <ctype.h>
+
+static void attr_file_free(git_attr_file *file)
+{
+ bool unlock = !git_mutex_lock(&file->lock);
+ git_attr_file__clear_rules(file, false);
+ git_pool_clear(&file->pool);
+ if (unlock)
+ git_mutex_unlock(&file->lock);
+ git_mutex_free(&file->lock);
+
+ git__memzero(file, sizeof(*file));
+ git__free(file);
+}
+
+int git_attr_file__new(
+ git_attr_file **out,
+ git_attr_file_entry *entry,
+ git_attr_file_source *source)
+{
+ git_attr_file *attrs = git__calloc(1, sizeof(git_attr_file));
+ GIT_ERROR_CHECK_ALLOC(attrs);
+
+ if (git_mutex_init(&attrs->lock) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to initialize lock");
+ goto on_error;
+ }
+
+ if (git_pool_init(&attrs->pool, 1) < 0)
+ goto on_error;
+
+ GIT_REFCOUNT_INC(attrs);
+ attrs->entry = entry;
+ memcpy(&attrs->source, source, sizeof(git_attr_file_source));
+ *out = attrs;
+ return 0;
+
+on_error:
+ git__free(attrs);
+ return -1;
+}
+
+int git_attr_file__clear_rules(git_attr_file *file, bool need_lock)
+{
+ unsigned int i;
+ git_attr_rule *rule;
+
+ if (need_lock && git_mutex_lock(&file->lock) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to lock attribute file");
+ return -1;
+ }
+
+ git_vector_foreach(&file->rules, i, rule)
+ git_attr_rule__free(rule);
+ git_vector_free(&file->rules);
+
+ if (need_lock)
+ git_mutex_unlock(&file->lock);
+
+ return 0;
+}
+
+void git_attr_file__free(git_attr_file *file)
+{
+ if (!file)
+ return;
+ GIT_REFCOUNT_DEC(file, attr_file_free);
+}
+
+static int attr_file_oid_from_index(
+ git_oid *oid, git_repository *repo, const char *path)
+{
+ int error;
+ git_index *idx;
+ size_t pos;
+ const git_index_entry *entry;
+
+ if ((error = git_repository_index__weakptr(&idx, repo)) < 0 ||
+ (error = git_index__find_pos(&pos, idx, path, 0, 0)) < 0)
+ return error;
+
+ if (!(entry = git_index_get_byindex(idx, pos)))
+ return GIT_ENOTFOUND;
+
+ *oid = entry->id;
+ return 0;
+}
+
+int git_attr_file__load(
+ git_attr_file **out,
+ git_repository *repo,
+ git_attr_session *attr_session,
+ git_attr_file_entry *entry,
+ git_attr_file_source *source,
+ git_attr_file_parser parser,
+ bool allow_macros)
+{
+ int error = 0;
+ git_commit *commit = NULL;
+ git_tree *tree = NULL;
+ git_tree_entry *tree_entry = NULL;
+ git_blob *blob = NULL;
+ git_str content = GIT_STR_INIT;
+ const char *content_str;
+ git_attr_file *file;
+ struct stat st;
+ bool nonexistent = false;
+ int bom_offset;
+ git_str_bom_t bom;
+ git_oid id;
+ git_object_size_t blobsize;
+
+ *out = NULL;
+
+ switch (source->type) {
+ case GIT_ATTR_FILE_SOURCE_MEMORY:
+ /* in-memory attribute file doesn't need data */
+ break;
+ case GIT_ATTR_FILE_SOURCE_INDEX: {
+ if ((error = attr_file_oid_from_index(&id, repo, entry->path)) < 0 ||
+ (error = git_blob_lookup(&blob, repo, &id)) < 0)
+ return error;
+
+ /* Do not assume that data straight from the ODB is NULL-terminated;
+ * copy the contents of a file to a buffer to work on */
+ blobsize = git_blob_rawsize(blob);
+
+ GIT_ERROR_CHECK_BLOBSIZE(blobsize);
+ git_str_put(&content, git_blob_rawcontent(blob), (size_t)blobsize);
+ break;
+ }
+ case GIT_ATTR_FILE_SOURCE_FILE: {
+ int fd = -1;
+
+ /* For open or read errors, pretend that we got ENOTFOUND. */
+ /* TODO: issue warning when warning API is available */
+
+ if (p_stat(entry->fullpath, &st) < 0 ||
+ S_ISDIR(st.st_mode) ||
+ (fd = git_futils_open_ro(entry->fullpath)) < 0 ||
+ (error = git_futils_readbuffer_fd(&content, fd, (size_t)st.st_size)) < 0)
+ nonexistent = true;
+
+ if (fd >= 0)
+ p_close(fd);
+
+ break;
+ }
+ case GIT_ATTR_FILE_SOURCE_HEAD:
+ case GIT_ATTR_FILE_SOURCE_COMMIT: {
+ if (source->type == GIT_ATTR_FILE_SOURCE_COMMIT) {
+ if ((error = git_commit_lookup(&commit, repo, source->commit_id)) < 0 ||
+ (error = git_commit_tree(&tree, commit)) < 0)
+ goto cleanup;
+ } else {
+ if ((error = git_repository_head_tree(&tree, repo)) < 0)
+ goto cleanup;
+ }
+
+ if ((error = git_tree_entry_bypath(&tree_entry, tree, entry->path)) < 0) {
+ /*
+ * If the attributes file does not exist, we can
+ * cache an empty file for this commit to prevent
+ * needless future lookups.
+ */
+ if (error == GIT_ENOTFOUND) {
+ error = 0;
+ break;
+ }
+
+ goto cleanup;
+ }
+
+ if ((error = git_blob_lookup(&blob, repo, git_tree_entry_id(tree_entry))) < 0)
+ goto cleanup;
+
+ /*
+ * Do not assume that data straight from the ODB is NULL-terminated;
+ * copy the contents of a file to a buffer to work on.
+ */
+ blobsize = git_blob_rawsize(blob);
+
+ GIT_ERROR_CHECK_BLOBSIZE(blobsize);
+ if ((error = git_str_put(&content,
+ git_blob_rawcontent(blob), (size_t)blobsize)) < 0)
+ goto cleanup;
+
+ break;
+ }
+ default:
+ git_error_set(GIT_ERROR_INVALID, "unknown file source %d", source->type);
+ return -1;
+ }
+
+ if ((error = git_attr_file__new(&file, entry, source)) < 0)
+ goto cleanup;
+
+ /* advance over a UTF8 BOM */
+ content_str = git_str_cstr(&content);
+ bom_offset = git_str_detect_bom(&bom, &content);
+
+ if (bom == GIT_STR_BOM_UTF8)
+ content_str += bom_offset;
+
+ /* store the key of the attr_reader; don't bother with cache
+ * invalidation during the same attr reader session.
+ */
+ if (attr_session)
+ file->session_key = attr_session->key;
+
+ if (parser && (error = parser(repo, file, content_str, allow_macros)) < 0) {
+ git_attr_file__free(file);
+ goto cleanup;
+ }
+
+ /* write cache breakers */
+ if (nonexistent)
+ file->nonexistent = 1;
+ else if (source->type == GIT_ATTR_FILE_SOURCE_INDEX)
+ git_oid_cpy(&file->cache_data.oid, git_blob_id(blob));
+ else if (source->type == GIT_ATTR_FILE_SOURCE_HEAD)
+ git_oid_cpy(&file->cache_data.oid, git_tree_id(tree));
+ else if (source->type == GIT_ATTR_FILE_SOURCE_COMMIT)
+ git_oid_cpy(&file->cache_data.oid, git_tree_id(tree));
+ else if (source->type == GIT_ATTR_FILE_SOURCE_FILE)
+ git_futils_filestamp_set_from_stat(&file->cache_data.stamp, &st);
+ /* else always cacheable */
+
+ *out = file;
+
+cleanup:
+ git_blob_free(blob);
+ git_tree_entry_free(tree_entry);
+ git_tree_free(tree);
+ git_commit_free(commit);
+ git_str_dispose(&content);
+
+ return error;
+}
+
+int git_attr_file__out_of_date(
+ git_repository *repo,
+ git_attr_session *attr_session,
+ git_attr_file *file,
+ git_attr_file_source *source)
+{
+ if (!file)
+ return 1;
+
+ /* we are never out of date if we just created this data in the same
+ * attr_session; otherwise, nonexistent files must be invalidated
+ */
+ if (attr_session && attr_session->key == file->session_key)
+ return 0;
+ else if (file->nonexistent)
+ return 1;
+
+ switch (file->source.type) {
+ case GIT_ATTR_FILE_SOURCE_MEMORY:
+ return 0;
+
+ case GIT_ATTR_FILE_SOURCE_FILE:
+ return git_futils_filestamp_check(
+ &file->cache_data.stamp, file->entry->fullpath);
+
+ case GIT_ATTR_FILE_SOURCE_INDEX: {
+ int error;
+ git_oid id;
+
+ if ((error = attr_file_oid_from_index(
+ &id, repo, file->entry->path)) < 0)
+ return error;
+
+ return (git_oid__cmp(&file->cache_data.oid, &id) != 0);
+ }
+
+ case GIT_ATTR_FILE_SOURCE_HEAD: {
+ git_tree *tree = NULL;
+ int error = git_repository_head_tree(&tree, repo);
+
+ if (error < 0)
+ return error;
+
+ error = (git_oid__cmp(&file->cache_data.oid, git_tree_id(tree)) != 0);
+
+ git_tree_free(tree);
+ return error;
+ }
+
+ case GIT_ATTR_FILE_SOURCE_COMMIT: {
+ git_commit *commit = NULL;
+ git_tree *tree = NULL;
+ int error;
+
+ if ((error = git_commit_lookup(&commit, repo, source->commit_id)) < 0)
+ return error;
+
+ error = git_commit_tree(&tree, commit);
+ git_commit_free(commit);
+
+ if (error < 0)
+ return error;
+
+ error = (git_oid__cmp(&file->cache_data.oid, git_tree_id(tree)) != 0);
+
+ git_tree_free(tree);
+ return error;
+ }
+
+ default:
+ git_error_set(GIT_ERROR_INVALID, "invalid file type %d", file->source.type);
+ return -1;
+ }
+}
+
+static int sort_by_hash_and_name(const void *a_raw, const void *b_raw);
+static void git_attr_rule__clear(git_attr_rule *rule);
+static bool parse_optimized_patterns(
+ git_attr_fnmatch *spec,
+ git_pool *pool,
+ const char *pattern);
+
+int git_attr_file__parse_buffer(
+ git_repository *repo, git_attr_file *attrs, const char *data, bool allow_macros)
+{
+ const char *scan = data, *context = NULL;
+ git_attr_rule *rule = NULL;
+ int error = 0;
+
+ /* If subdir file path, convert context for file paths */
+ if (attrs->entry && git_fs_path_root(attrs->entry->path) < 0 &&
+ !git__suffixcmp(attrs->entry->path, "/" GIT_ATTR_FILE))
+ context = attrs->entry->path;
+
+ if (git_mutex_lock(&attrs->lock) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to lock attribute file");
+ return -1;
+ }
+
+ while (!error && *scan) {
+ /* Allocate rule if needed, otherwise re-use previous rule */
+ if (!rule) {
+ rule = git__calloc(1, sizeof(*rule));
+ GIT_ERROR_CHECK_ALLOC(rule);
+ } else
+ git_attr_rule__clear(rule);
+
+ rule->match.flags = GIT_ATTR_FNMATCH_ALLOWNEG | GIT_ATTR_FNMATCH_ALLOWMACRO;
+
+ /* Parse the next "pattern attr attr attr" line */
+ if ((error = git_attr_fnmatch__parse(&rule->match, &attrs->pool, context, &scan)) < 0 ||
+ (error = git_attr_assignment__parse(repo, &attrs->pool, &rule->assigns, &scan)) < 0)
+ {
+ if (error != GIT_ENOTFOUND)
+ goto out;
+ error = 0;
+ continue;
+ }
+
+ if (rule->match.flags & GIT_ATTR_FNMATCH_MACRO) {
+ /* TODO: warning if macro found in file below repo root */
+ if (!allow_macros)
+ continue;
+ if ((error = git_attr_cache__insert_macro(repo, rule)) < 0)
+ goto out;
+ } else if ((error = git_vector_insert(&attrs->rules, rule)) < 0)
+ goto out;
+
+ rule = NULL;
+ }
+
+out:
+ git_mutex_unlock(&attrs->lock);
+ git_attr_rule__free(rule);
+
+ return error;
+}
+
+uint32_t git_attr_file__name_hash(const char *name)
+{
+ uint32_t h = 5381;
+ int c;
+
+ GIT_ASSERT_ARG(name);
+
+ while ((c = (int)*name++) != 0)
+ h = ((h << 5) + h) + c;
+ return h;
+}
+
+int git_attr_file__lookup_one(
+ git_attr_file *file,
+ git_attr_path *path,
+ const char *attr,
+ const char **value)
+{
+ size_t i;
+ git_attr_name name;
+ git_attr_rule *rule;
+
+ *value = NULL;
+
+ name.name = attr;
+ name.name_hash = git_attr_file__name_hash(attr);
+
+ git_attr_file__foreach_matching_rule(file, path, i, rule) {
+ size_t pos;
+
+ if (!git_vector_bsearch(&pos, &rule->assigns, &name)) {
+ *value = ((git_attr_assignment *)
+ git_vector_get(&rule->assigns, pos))->value;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+int git_attr_file__load_standalone(git_attr_file **out, const char *path)
+{
+ git_str content = GIT_STR_INIT;
+ git_attr_file_source source = { GIT_ATTR_FILE_SOURCE_FILE };
+ git_attr_file *file = NULL;
+ int error;
+
+ if ((error = git_futils_readbuffer(&content, path)) < 0)
+ goto out;
+
+ /*
+ * Because the cache entry is allocated from the file's own pool, we
+ * don't have to free it - freeing file+pool will free cache entry, too.
+ */
+
+ if ((error = git_attr_file__new(&file, NULL, &source)) < 0 ||
+ (error = git_attr_file__parse_buffer(NULL, file, content.ptr, true)) < 0 ||
+ (error = git_attr_cache__alloc_file_entry(&file->entry, NULL, NULL, path, &file->pool)) < 0)
+ goto out;
+
+ *out = file;
+out:
+ if (error < 0)
+ git_attr_file__free(file);
+ git_str_dispose(&content);
+
+ return error;
+}
+
+bool git_attr_fnmatch__match(
+ git_attr_fnmatch *match,
+ git_attr_path *path)
+{
+ const char *relpath = path->path;
+ const char *filename;
+ int flags = 0;
+
+ /*
+ * If the rule was generated in a subdirectory, we must only
+ * use it for paths inside that directory. We can thus return
+ * a non-match if the prefixes don't match.
+ */
+ if (match->containing_dir) {
+ if (match->flags & GIT_ATTR_FNMATCH_ICASE) {
+ if (git__strncasecmp(path->path, match->containing_dir, match->containing_dir_length))
+ return 0;
+ } else {
+ if (git__prefixcmp(path->path, match->containing_dir))
+ return 0;
+ }
+
+ relpath += match->containing_dir_length;
+ }
+
+ if (match->flags & GIT_ATTR_FNMATCH_ICASE)
+ flags |= WM_CASEFOLD;
+
+ if (match->flags & GIT_ATTR_FNMATCH_FULLPATH) {
+ filename = relpath;
+ flags |= WM_PATHNAME;
+ } else {
+ filename = path->basename;
+ }
+
+ if ((match->flags & GIT_ATTR_FNMATCH_DIRECTORY) && !path->is_dir) {
+ bool samename;
+
+ /*
+ * for attribute checks or checks at the root of this match's
+ * containing_dir (or root of the repository if no containing_dir),
+ * do not match.
+ */
+ if (!(match->flags & GIT_ATTR_FNMATCH_IGNORE) ||
+ path->basename == relpath)
+ return false;
+
+ /* fail match if this is a file with same name as ignored folder */
+ samename = (match->flags & GIT_ATTR_FNMATCH_ICASE) ?
+ !strcasecmp(match->pattern, relpath) :
+ !strcmp(match->pattern, relpath);
+
+ if (samename)
+ return false;
+
+ return (wildmatch(match->pattern, relpath, flags) == WM_MATCH);
+ }
+
+ return (wildmatch(match->pattern, filename, flags) == WM_MATCH);
+}
+
+bool git_attr_rule__match(
+ git_attr_rule *rule,
+ git_attr_path *path)
+{
+ bool matched = git_attr_fnmatch__match(&rule->match, path);
+
+ if (rule->match.flags & GIT_ATTR_FNMATCH_NEGATIVE)
+ matched = !matched;
+
+ return matched;
+}
+
+git_attr_assignment *git_attr_rule__lookup_assignment(
+ git_attr_rule *rule, const char *name)
+{
+ size_t pos;
+ git_attr_name key;
+ key.name = name;
+ key.name_hash = git_attr_file__name_hash(name);
+
+ if (git_vector_bsearch(&pos, &rule->assigns, &key))
+ return NULL;
+
+ return git_vector_get(&rule->assigns, pos);
+}
+
+int git_attr_path__init(
+ git_attr_path *info,
+ const char *path,
+ const char *base,
+ git_dir_flag dir_flag)
+{
+ ssize_t root;
+
+ /* build full path as best we can */
+ git_str_init(&info->full, 0);
+
+ if (git_fs_path_join_unrooted(&info->full, path, base, &root) < 0)
+ return -1;
+
+ info->path = info->full.ptr + root;
+
+ /* remove trailing slashes */
+ while (info->full.size > 0) {
+ if (info->full.ptr[info->full.size - 1] != '/')
+ break;
+ info->full.size--;
+ }
+ info->full.ptr[info->full.size] = '\0';
+
+ /* skip leading slashes in path */
+ while (*info->path == '/')
+ info->path++;
+
+ /* find trailing basename component */
+ info->basename = strrchr(info->path, '/');
+ if (info->basename)
+ info->basename++;
+ if (!info->basename || !*info->basename)
+ info->basename = info->path;
+
+ switch (dir_flag)
+ {
+ case GIT_DIR_FLAG_FALSE:
+ info->is_dir = 0;
+ break;
+
+ case GIT_DIR_FLAG_TRUE:
+ info->is_dir = 1;
+ break;
+
+ case GIT_DIR_FLAG_UNKNOWN:
+ default:
+ info->is_dir = (int)git_fs_path_isdir(info->full.ptr);
+ break;
+ }
+
+ return 0;
+}
+
+void git_attr_path__free(git_attr_path *info)
+{
+ git_str_dispose(&info->full);
+ info->path = NULL;
+ info->basename = NULL;
+}
+
+/*
+ * From gitattributes(5):
+ *
+ * Patterns have the following format:
+ *
+ * - A blank line matches no files, so it can serve as a separator for
+ * readability.
+ *
+ * - A line starting with # serves as a comment.
+ *
+ * - An optional prefix ! which negates the pattern; any matching file
+ * excluded by a previous pattern will become included again. If a negated
+ * pattern matches, this will override lower precedence patterns sources.
+ *
+ * - If the pattern ends with a slash, it is removed for the purpose of the
+ * following description, but it would only find a match with a directory. In
+ * other words, foo/ will match a directory foo and paths underneath it, but
+ * will not match a regular file or a symbolic link foo (this is consistent
+ * with the way how pathspec works in general in git).
+ *
+ * - If the pattern does not contain a slash /, git treats it as a shell glob
+ * pattern and checks for a match against the pathname without leading
+ * directories.
+ *
+ * - Otherwise, git treats the pattern as a shell glob suitable for consumption
+ * by fnmatch(3) with the FNM_PATHNAME flag: wildcards in the pattern will
+ * not match a / in the pathname. For example, "Documentation/\*.html" matches
+ * "Documentation/git.html" but not "Documentation/ppc/ppc.html". A leading
+ * slash matches the beginning of the pathname; for example, "/\*.c" matches
+ * "cat-file.c" but not "mozilla-sha1/sha1.c".
+ */
+
+/*
+ * Determine the length of trailing spaces. Escaped spaces do not count as
+ * trailing whitespace.
+ */
+static size_t trailing_space_length(const char *p, size_t len)
+{
+ size_t n, i;
+ for (n = len; n; n--) {
+ if (p[n-1] != ' ' && p[n-1] != '\t')
+ break;
+
+ /*
+ * Count escape-characters before space. In case where it's an
+ * even number of escape characters, then the escape char itself
+ * is escaped and the whitespace is an unescaped whitespace.
+ * Otherwise, the last escape char is not escaped and the
+ * whitespace in an escaped whitespace.
+ */
+ i = n;
+ while (i > 1 && p[i-2] == '\\')
+ i--;
+ if ((n - i) % 2)
+ break;
+ }
+ return len - n;
+}
+
+static size_t unescape_spaces(char *str)
+{
+ char *scan, *pos = str;
+ bool escaped = false;
+
+ if (!str)
+ return 0;
+
+ for (scan = str; *scan; scan++) {
+ if (!escaped && *scan == '\\') {
+ escaped = true;
+ continue;
+ }
+
+ /* Only insert the escape character for escaped non-spaces */
+ if (escaped && !git__isspace(*scan))
+ *pos++ = '\\';
+
+ *pos++ = *scan;
+ escaped = false;
+ }
+
+ if (pos != scan)
+ *pos = '\0';
+
+ return (pos - str);
+}
+
+/*
+ * This will return 0 if the spec was filled out,
+ * GIT_ENOTFOUND if the fnmatch does not require matching, or
+ * another error code there was an actual problem.
+ */
+int git_attr_fnmatch__parse(
+ git_attr_fnmatch *spec,
+ git_pool *pool,
+ const char *context,
+ const char **base)
+{
+ const char *pattern, *scan;
+ int slash_count, allow_space;
+ bool escaped;
+
+ GIT_ASSERT_ARG(spec);
+ GIT_ASSERT_ARG(base && *base);
+
+ if (parse_optimized_patterns(spec, pool, *base))
+ return 0;
+
+ spec->flags = (spec->flags & GIT_ATTR_FNMATCH__INCOMING);
+ allow_space = ((spec->flags & GIT_ATTR_FNMATCH_ALLOWSPACE) != 0);
+
+ pattern = *base;
+
+ while (!allow_space && git__isspace(*pattern))
+ pattern++;
+
+ if (!*pattern || *pattern == '#' || *pattern == '\n' ||
+ (*pattern == '\r' && *(pattern + 1) == '\n')) {
+ *base = git__next_line(pattern);
+ return GIT_ENOTFOUND;
+ }
+
+ if (*pattern == '[' && (spec->flags & GIT_ATTR_FNMATCH_ALLOWMACRO) != 0) {
+ if (strncmp(pattern, "[attr]", 6) == 0) {
+ spec->flags = spec->flags | GIT_ATTR_FNMATCH_MACRO;
+ pattern += 6;
+ }
+ /* else a character range like [a-e]* which is accepted */
+ }
+
+ if (*pattern == '!' && (spec->flags & GIT_ATTR_FNMATCH_ALLOWNEG) != 0) {
+ spec->flags = spec->flags | GIT_ATTR_FNMATCH_NEGATIVE;
+ pattern++;
+ }
+
+ slash_count = 0;
+ escaped = false;
+ /* Scan until a non-escaped whitespace. */
+ for (scan = pattern; *scan != '\0'; ++scan) {
+ char c = *scan;
+
+ if (c == '\\' && !escaped) {
+ escaped = true;
+ continue;
+ } else if (git__isspace(c) && !escaped) {
+ if (!allow_space || (c != ' ' && c != '\t' && c != '\r'))
+ break;
+ } else if (c == '/') {
+ spec->flags = spec->flags | GIT_ATTR_FNMATCH_FULLPATH;
+ slash_count++;
+
+ if (slash_count == 1 && pattern == scan)
+ pattern++;
+ } else if (git__iswildcard(c) && !escaped) {
+ /* remember if we see an unescaped wildcard in pattern */
+ spec->flags = spec->flags | GIT_ATTR_FNMATCH_HASWILD;
+ }
+
+ escaped = false;
+ }
+
+ *base = scan;
+
+ if ((spec->length = scan - pattern) == 0)
+ return GIT_ENOTFOUND;
+
+ /*
+ * Remove one trailing \r in case this is a CRLF delimited
+ * file, in the case of Icon\r\r\n, we still leave the first
+ * \r there to match against.
+ */
+ if (pattern[spec->length - 1] == '\r')
+ if (--spec->length == 0)
+ return GIT_ENOTFOUND;
+
+ /* Remove trailing spaces. */
+ spec->length -= trailing_space_length(pattern, spec->length);
+
+ if (spec->length == 0)
+ return GIT_ENOTFOUND;
+
+ if (pattern[spec->length - 1] == '/') {
+ spec->length--;
+ spec->flags = spec->flags | GIT_ATTR_FNMATCH_DIRECTORY;
+ if (--slash_count <= 0)
+ spec->flags = spec->flags & ~GIT_ATTR_FNMATCH_FULLPATH;
+ }
+
+ if (context) {
+ char *slash = strrchr(context, '/');
+ size_t len;
+ if (slash) {
+ /* include the slash for easier matching */
+ len = slash - context + 1;
+ spec->containing_dir = git_pool_strndup(pool, context, len);
+ spec->containing_dir_length = len;
+ }
+ }
+
+ spec->pattern = git_pool_strndup(pool, pattern, spec->length);
+
+ if (!spec->pattern) {
+ *base = git__next_line(pattern);
+ return -1;
+ } else {
+ /* strip '\' that might have been used for internal whitespace */
+ spec->length = unescape_spaces(spec->pattern);
+ }
+
+ return 0;
+}
+
+static bool parse_optimized_patterns(
+ git_attr_fnmatch *spec,
+ git_pool *pool,
+ const char *pattern)
+{
+ if (!pattern[1] && (pattern[0] == '*' || pattern[0] == '.')) {
+ spec->flags = GIT_ATTR_FNMATCH_MATCH_ALL;
+ spec->pattern = git_pool_strndup(pool, pattern, 1);
+ spec->length = 1;
+
+ return true;
+ }
+
+ return false;
+}
+
+static int sort_by_hash_and_name(const void *a_raw, const void *b_raw)
+{
+ const git_attr_name *a = a_raw;
+ const git_attr_name *b = b_raw;
+
+ if (b->name_hash < a->name_hash)
+ return 1;
+ else if (b->name_hash > a->name_hash)
+ return -1;
+ else
+ return strcmp(b->name, a->name);
+}
+
+static void git_attr_assignment__free(git_attr_assignment *assign)
+{
+ /* name and value are stored in a git_pool associated with the
+ * git_attr_file, so they do not need to be freed here
+ */
+ assign->name = NULL;
+ assign->value = NULL;
+ git__free(assign);
+}
+
+static int merge_assignments(void **old_raw, void *new_raw)
+{
+ git_attr_assignment **old = (git_attr_assignment **)old_raw;
+ git_attr_assignment *new = (git_attr_assignment *)new_raw;
+
+ GIT_REFCOUNT_DEC(*old, git_attr_assignment__free);
+ *old = new;
+ return GIT_EEXISTS;
+}
+
+int git_attr_assignment__parse(
+ git_repository *repo,
+ git_pool *pool,
+ git_vector *assigns,
+ const char **base)
+{
+ int error;
+ const char *scan = *base;
+ git_attr_assignment *assign = NULL;
+
+ GIT_ASSERT_ARG(assigns && !assigns->length);
+
+ git_vector_set_cmp(assigns, sort_by_hash_and_name);
+
+ while (*scan && *scan != '\n') {
+ const char *name_start, *value_start;
+
+ /* skip leading blanks */
+ while (git__isspace(*scan) && *scan != '\n') scan++;
+
+ /* allocate assign if needed */
+ if (!assign) {
+ assign = git__calloc(1, sizeof(git_attr_assignment));
+ GIT_ERROR_CHECK_ALLOC(assign);
+ GIT_REFCOUNT_INC(assign);
+ }
+
+ assign->name_hash = 5381;
+ assign->value = git_attr__true;
+
+ /* look for magic name prefixes */
+ if (*scan == '-') {
+ assign->value = git_attr__false;
+ scan++;
+ } else if (*scan == '!') {
+ assign->value = git_attr__unset; /* explicit unspecified state */
+ scan++;
+ } else if (*scan == '#') /* comment rest of line */
+ break;
+
+ /* find the name */
+ name_start = scan;
+ while (*scan && !git__isspace(*scan) && *scan != '=') {
+ assign->name_hash =
+ ((assign->name_hash << 5) + assign->name_hash) + *scan;
+ scan++;
+ }
+ if (scan == name_start) {
+ /* must have found lone prefix (" - ") or leading = ("=foo")
+ * or end of buffer -- advance until whitespace and continue
+ */
+ while (*scan && !git__isspace(*scan)) scan++;
+ continue;
+ }
+
+ /* allocate permanent storage for name */
+ assign->name = git_pool_strndup(pool, name_start, scan - name_start);
+ GIT_ERROR_CHECK_ALLOC(assign->name);
+
+ /* if there is an equals sign, find the value */
+ if (*scan == '=') {
+ for (value_start = ++scan; *scan && !git__isspace(*scan); ++scan);
+
+ /* if we found a value, allocate permanent storage for it */
+ if (scan > value_start) {
+ assign->value = git_pool_strndup(pool, value_start, scan - value_start);
+ GIT_ERROR_CHECK_ALLOC(assign->value);
+ }
+ }
+
+ /* expand macros (if given a repo with a macro cache) */
+ if (repo != NULL && assign->value == git_attr__true) {
+ git_attr_rule *macro =
+ git_attr_cache__lookup_macro(repo, assign->name);
+
+ if (macro != NULL) {
+ unsigned int i;
+ git_attr_assignment *massign;
+
+ git_vector_foreach(&macro->assigns, i, massign) {
+ GIT_REFCOUNT_INC(massign);
+
+ error = git_vector_insert_sorted(
+ assigns, massign, &merge_assignments);
+ if (error < 0 && error != GIT_EEXISTS) {
+ git_attr_assignment__free(assign);
+ return error;
+ }
+ }
+ }
+ }
+
+ /* insert allocated assign into vector */
+ error = git_vector_insert_sorted(assigns, assign, &merge_assignments);
+ if (error < 0 && error != GIT_EEXISTS)
+ return error;
+
+ /* clear assign since it is now "owned" by the vector */
+ assign = NULL;
+ }
+
+ if (assign != NULL)
+ git_attr_assignment__free(assign);
+
+ *base = git__next_line(scan);
+
+ return (assigns->length == 0) ? GIT_ENOTFOUND : 0;
+}
+
+static void git_attr_rule__clear(git_attr_rule *rule)
+{
+ unsigned int i;
+ git_attr_assignment *assign;
+
+ if (!rule)
+ return;
+
+ if (!(rule->match.flags & GIT_ATTR_FNMATCH_IGNORE)) {
+ git_vector_foreach(&rule->assigns, i, assign)
+ GIT_REFCOUNT_DEC(assign, git_attr_assignment__free);
+ git_vector_free(&rule->assigns);
+ }
+
+ /* match.pattern is stored in a git_pool, so no need to free */
+ rule->match.pattern = NULL;
+ rule->match.length = 0;
+}
+
+void git_attr_rule__free(git_attr_rule *rule)
+{
+ git_attr_rule__clear(rule);
+ git__free(rule);
+}
+
+int git_attr_session__init(git_attr_session *session, git_repository *repo)
+{
+ GIT_ASSERT_ARG(repo);
+
+ memset(session, 0, sizeof(*session));
+ session->key = git_atomic32_inc(&repo->attr_session_key);
+
+ return 0;
+}
+
+void git_attr_session__free(git_attr_session *session)
+{
+ if (!session)
+ return;
+
+ git_str_dispose(&session->sysdir);
+ git_str_dispose(&session->tmp);
+
+ memset(session, 0, sizeof(git_attr_session));
+}
diff --git a/src/libgit2/attr_file.h b/src/libgit2/attr_file.h
new file mode 100644
index 0000000..08630d1
--- /dev/null
+++ b/src/libgit2/attr_file.h
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_attr_file_h__
+#define INCLUDE_attr_file_h__
+
+#include "common.h"
+
+#include "git2/oid.h"
+#include "git2/attr.h"
+#include "vector.h"
+#include "pool.h"
+#include "str.h"
+#include "futils.h"
+
+#define GIT_ATTR_FILE ".gitattributes"
+#define GIT_ATTR_FILE_INREPO "attributes"
+#define GIT_ATTR_FILE_SYSTEM "gitattributes"
+#define GIT_ATTR_FILE_XDG "attributes"
+
+#define GIT_ATTR_FNMATCH_NEGATIVE (1U << 0)
+#define GIT_ATTR_FNMATCH_DIRECTORY (1U << 1)
+#define GIT_ATTR_FNMATCH_FULLPATH (1U << 2)
+#define GIT_ATTR_FNMATCH_MACRO (1U << 3)
+#define GIT_ATTR_FNMATCH_IGNORE (1U << 4)
+#define GIT_ATTR_FNMATCH_HASWILD (1U << 5)
+#define GIT_ATTR_FNMATCH_ALLOWSPACE (1U << 6)
+#define GIT_ATTR_FNMATCH_ICASE (1U << 7)
+#define GIT_ATTR_FNMATCH_MATCH_ALL (1U << 8)
+#define GIT_ATTR_FNMATCH_ALLOWNEG (1U << 9)
+#define GIT_ATTR_FNMATCH_ALLOWMACRO (1U << 10)
+
+#define GIT_ATTR_FNMATCH__INCOMING \
+ (GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG | GIT_ATTR_FNMATCH_ALLOWMACRO)
+
+typedef enum {
+ GIT_ATTR_FILE_SOURCE_MEMORY = 0,
+ GIT_ATTR_FILE_SOURCE_FILE = 1,
+ GIT_ATTR_FILE_SOURCE_INDEX = 2,
+ GIT_ATTR_FILE_SOURCE_HEAD = 3,
+ GIT_ATTR_FILE_SOURCE_COMMIT = 4,
+
+ GIT_ATTR_FILE_NUM_SOURCES = 5
+} git_attr_file_source_t;
+
+typedef struct {
+ /* The source location for the attribute file. */
+ git_attr_file_source_t type;
+
+ /*
+ * The filename of the attribute file to read (relative to the
+ * given base path).
+ */
+ const char *base;
+ const char *filename;
+
+ /*
+ * The commit ID when the given source type is a commit (or NULL
+ * for the repository's HEAD commit.)
+ */
+ git_oid *commit_id;
+} git_attr_file_source;
+
+extern const char *git_attr__true;
+extern const char *git_attr__false;
+extern const char *git_attr__unset;
+
+typedef struct {
+ char *pattern;
+ size_t length;
+ char *containing_dir;
+ size_t containing_dir_length;
+ unsigned int flags;
+} git_attr_fnmatch;
+
+typedef struct {
+ git_attr_fnmatch match;
+ git_vector assigns; /* vector of <git_attr_assignment*> */
+} git_attr_rule;
+
+typedef struct {
+ git_refcount unused;
+ const char *name;
+ uint32_t name_hash;
+} git_attr_name;
+
+typedef struct {
+ git_refcount rc; /* for macros */
+ char *name;
+ uint32_t name_hash;
+ const char *value;
+} git_attr_assignment;
+
+typedef struct git_attr_file_entry git_attr_file_entry;
+
+typedef struct {
+ git_refcount rc;
+ git_mutex lock;
+ git_attr_file_entry *entry;
+ git_attr_file_source source;
+ git_vector rules; /* vector of <rule*> or <fnmatch*> */
+ git_pool pool;
+ unsigned int nonexistent:1;
+ int session_key;
+ union {
+ git_oid oid;
+ git_futils_filestamp stamp;
+ } cache_data;
+} git_attr_file;
+
+struct git_attr_file_entry {
+ git_attr_file *file[GIT_ATTR_FILE_NUM_SOURCES];
+ const char *path; /* points into fullpath */
+ char fullpath[GIT_FLEX_ARRAY];
+};
+
+typedef struct {
+ git_str full;
+ char *path;
+ char *basename;
+ int is_dir;
+} git_attr_path;
+
+/* A git_attr_session can provide an "instance" of reading, to prevent cache
+ * invalidation during a single operation instance (like checkout).
+ */
+
+typedef struct {
+ int key;
+ unsigned int init_setup:1,
+ init_sysdir:1;
+ git_str sysdir;
+ git_str tmp;
+} git_attr_session;
+
+extern int git_attr_session__init(git_attr_session *attr_session, git_repository *repo);
+extern void git_attr_session__free(git_attr_session *session);
+
+extern int git_attr_get_many_with_session(
+ const char **values_out,
+ git_repository *repo,
+ git_attr_session *attr_session,
+ git_attr_options *opts,
+ const char *path,
+ size_t num_attr,
+ const char **names);
+
+typedef int (*git_attr_file_parser)(
+ git_repository *repo,
+ git_attr_file *file,
+ const char *data,
+ bool allow_macros);
+
+/*
+ * git_attr_file API
+ */
+
+int git_attr_file__new(
+ git_attr_file **out,
+ git_attr_file_entry *entry,
+ git_attr_file_source *source);
+
+void git_attr_file__free(git_attr_file *file);
+
+int git_attr_file__load(
+ git_attr_file **out,
+ git_repository *repo,
+ git_attr_session *attr_session,
+ git_attr_file_entry *ce,
+ git_attr_file_source *source,
+ git_attr_file_parser parser,
+ bool allow_macros);
+
+int git_attr_file__load_standalone(
+ git_attr_file **out, const char *path);
+
+int git_attr_file__out_of_date(
+ git_repository *repo, git_attr_session *session, git_attr_file *file, git_attr_file_source *source);
+
+int git_attr_file__parse_buffer(
+ git_repository *repo, git_attr_file *attrs, const char *data, bool allow_macros);
+
+int git_attr_file__clear_rules(
+ git_attr_file *file, bool need_lock);
+
+int git_attr_file__lookup_one(
+ git_attr_file *file,
+ git_attr_path *path,
+ const char *attr,
+ const char **value);
+
+/* loop over rules in file from bottom to top */
+#define git_attr_file__foreach_matching_rule(file, path, iter, rule) \
+ git_vector_rforeach(&(file)->rules, (iter), (rule)) \
+ if (git_attr_rule__match((rule), (path)))
+
+uint32_t git_attr_file__name_hash(const char *name);
+
+
+/*
+ * other utilities
+ */
+
+extern int git_attr_fnmatch__parse(
+ git_attr_fnmatch *spec,
+ git_pool *pool,
+ const char *source,
+ const char **base);
+
+extern bool git_attr_fnmatch__match(
+ git_attr_fnmatch *rule,
+ git_attr_path *path);
+
+extern void git_attr_rule__free(git_attr_rule *rule);
+
+extern bool git_attr_rule__match(
+ git_attr_rule *rule,
+ git_attr_path *path);
+
+extern git_attr_assignment *git_attr_rule__lookup_assignment(
+ git_attr_rule *rule, const char *name);
+
+typedef enum { GIT_DIR_FLAG_TRUE = 1, GIT_DIR_FLAG_FALSE = 0, GIT_DIR_FLAG_UNKNOWN = -1 } git_dir_flag;
+
+extern int git_attr_path__init(
+ git_attr_path *out,
+ const char *path,
+ const char *base,
+ git_dir_flag is_dir);
+extern void git_attr_path__free(git_attr_path *info);
+
+extern int git_attr_assignment__parse(
+ git_repository *repo, /* needed to expand macros */
+ git_pool *pool,
+ git_vector *assigns,
+ const char **scan);
+
+#endif
diff --git a/src/libgit2/attrcache.c b/src/libgit2/attrcache.c
new file mode 100644
index 0000000..405944e
--- /dev/null
+++ b/src/libgit2/attrcache.c
@@ -0,0 +1,478 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "attrcache.h"
+
+#include "repository.h"
+#include "attr_file.h"
+#include "config.h"
+#include "sysdir.h"
+#include "ignore.h"
+#include "path.h"
+
+GIT_INLINE(int) attr_cache_lock(git_attr_cache *cache)
+{
+ GIT_UNUSED(cache); /* avoid warning if threading is off */
+
+ if (git_mutex_lock(&cache->lock) < 0) {
+ git_error_set(GIT_ERROR_OS, "unable to get attr cache lock");
+ return -1;
+ }
+ return 0;
+}
+
+GIT_INLINE(void) attr_cache_unlock(git_attr_cache *cache)
+{
+ GIT_UNUSED(cache); /* avoid warning if threading is off */
+ git_mutex_unlock(&cache->lock);
+}
+
+GIT_INLINE(git_attr_file_entry *) attr_cache_lookup_entry(
+ git_attr_cache *cache, const char *path)
+{
+ return git_strmap_get(cache->files, path);
+}
+
+int git_attr_cache__alloc_file_entry(
+ git_attr_file_entry **out,
+ git_repository *repo,
+ const char *base,
+ const char *path,
+ git_pool *pool)
+{
+ git_str fullpath_str = GIT_STR_INIT;
+ size_t baselen = 0, pathlen = strlen(path);
+ size_t cachesize = sizeof(git_attr_file_entry) + pathlen + 1;
+ git_attr_file_entry *ce;
+
+ if (base != NULL && git_fs_path_root(path) < 0) {
+ baselen = strlen(base);
+ cachesize += baselen;
+
+ if (baselen && base[baselen - 1] != '/')
+ cachesize++;
+ }
+
+ ce = git_pool_mallocz(pool, cachesize);
+ GIT_ERROR_CHECK_ALLOC(ce);
+
+ if (baselen) {
+ memcpy(ce->fullpath, base, baselen);
+
+ if (base[baselen - 1] != '/')
+ ce->fullpath[baselen++] = '/';
+ }
+ memcpy(&ce->fullpath[baselen], path, pathlen);
+
+ fullpath_str.ptr = ce->fullpath;
+ fullpath_str.size = pathlen + baselen;
+
+ if (git_path_validate_str_length(repo, &fullpath_str) < 0)
+ return -1;
+
+ ce->path = &ce->fullpath[baselen];
+ *out = ce;
+
+ return 0;
+}
+
+/* call with attrcache locked */
+static int attr_cache_make_entry(
+ git_attr_file_entry **out, git_repository *repo, const char *path)
+{
+ git_attr_cache *cache = git_repository_attr_cache(repo);
+ git_attr_file_entry *entry = NULL;
+ int error;
+
+ if ((error = git_attr_cache__alloc_file_entry(&entry, repo,
+ git_repository_workdir(repo), path, &cache->pool)) < 0)
+ return error;
+
+ if ((error = git_strmap_set(cache->files, entry->path, entry)) < 0)
+ return error;
+
+ *out = entry;
+ return error;
+}
+
+/* insert entry or replace existing if we raced with another thread */
+static int attr_cache_upsert(git_attr_cache *cache, git_attr_file *file)
+{
+ git_attr_file_entry *entry;
+ git_attr_file *old;
+
+ if (attr_cache_lock(cache) < 0)
+ return -1;
+
+ entry = attr_cache_lookup_entry(cache, file->entry->path);
+
+ GIT_REFCOUNT_OWN(file, entry);
+ GIT_REFCOUNT_INC(file);
+
+ /*
+ * Replace the existing value if another thread has
+ * created it in the meantime.
+ */
+ old = git_atomic_swap(entry->file[file->source.type], file);
+
+ if (old) {
+ GIT_REFCOUNT_OWN(old, NULL);
+ git_attr_file__free(old);
+ }
+
+ attr_cache_unlock(cache);
+ return 0;
+}
+
+static int attr_cache_remove(git_attr_cache *cache, git_attr_file *file)
+{
+ int error = 0;
+ git_attr_file_entry *entry;
+ git_attr_file *oldfile = NULL;
+
+ if (!file)
+ return 0;
+
+ if ((error = attr_cache_lock(cache)) < 0)
+ return error;
+
+ if ((entry = attr_cache_lookup_entry(cache, file->entry->path)) != NULL)
+ oldfile = git_atomic_compare_and_swap(&entry->file[file->source.type], file, NULL);
+
+ attr_cache_unlock(cache);
+
+ if (oldfile == file) {
+ GIT_REFCOUNT_OWN(file, NULL);
+ git_attr_file__free(file);
+ }
+
+ return error;
+}
+
+/* Look up cache entry and file.
+ * - If entry is not present, create it while the cache is locked.
+ * - If file is present, increment refcount before returning it, so the
+ * cache can be unlocked and it won't go away.
+ */
+static int attr_cache_lookup(
+ git_attr_file **out_file,
+ git_attr_file_entry **out_entry,
+ git_repository *repo,
+ git_attr_session *attr_session,
+ git_attr_file_source *source)
+{
+ int error = 0;
+ git_str path = GIT_STR_INIT;
+ const char *wd = git_repository_workdir(repo);
+ const char *filename;
+ git_attr_cache *cache = git_repository_attr_cache(repo);
+ git_attr_file_entry *entry = NULL;
+ git_attr_file *file = NULL;
+
+ /* join base and path as needed */
+ if (source->base != NULL && git_fs_path_root(source->filename) < 0) {
+ git_str *p = attr_session ? &attr_session->tmp : &path;
+
+ if (git_str_joinpath(p, source->base, source->filename) < 0 ||
+ git_path_validate_str_length(repo, p) < 0)
+ return -1;
+
+ filename = p->ptr;
+ } else {
+ filename = source->filename;
+ }
+
+ if (wd && !git__prefixcmp(filename, wd))
+ filename += strlen(wd);
+
+ /* check cache for existing entry */
+ if ((error = attr_cache_lock(cache)) < 0)
+ goto cleanup;
+
+ entry = attr_cache_lookup_entry(cache, filename);
+
+ if (!entry) {
+ error = attr_cache_make_entry(&entry, repo, filename);
+ } else if (entry->file[source->type] != NULL) {
+ file = entry->file[source->type];
+ GIT_REFCOUNT_INC(file);
+ }
+
+ attr_cache_unlock(cache);
+
+cleanup:
+ *out_file = file;
+ *out_entry = entry;
+
+ git_str_dispose(&path);
+ return error;
+}
+
+int git_attr_cache__get(
+ git_attr_file **out,
+ git_repository *repo,
+ git_attr_session *attr_session,
+ git_attr_file_source *source,
+ git_attr_file_parser parser,
+ bool allow_macros)
+{
+ int error = 0;
+ git_attr_cache *cache = git_repository_attr_cache(repo);
+ git_attr_file_entry *entry = NULL;
+ git_attr_file *file = NULL, *updated = NULL;
+
+ if ((error = attr_cache_lookup(&file, &entry, repo, attr_session, source)) < 0)
+ return error;
+
+ /* load file if we don't have one or if existing one is out of date */
+ if (!file ||
+ (error = git_attr_file__out_of_date(repo, attr_session, file, source)) > 0)
+ error = git_attr_file__load(&updated, repo, attr_session,
+ entry, source, parser,
+ allow_macros);
+
+ /* if we loaded the file, insert into and/or update cache */
+ if (updated) {
+ if ((error = attr_cache_upsert(cache, updated)) < 0) {
+ git_attr_file__free(updated);
+ } else {
+ git_attr_file__free(file); /* offset incref from lookup */
+ file = updated;
+ }
+ }
+
+ /* if file could not be loaded */
+ if (error < 0) {
+ /* remove existing entry */
+ if (file) {
+ attr_cache_remove(cache, file);
+ git_attr_file__free(file); /* offset incref from lookup */
+ file = NULL;
+ }
+ /* no error if file simply doesn't exist */
+ if (error == GIT_ENOTFOUND) {
+ git_error_clear();
+ error = 0;
+ }
+ }
+
+ *out = file;
+ return error;
+}
+
+bool git_attr_cache__is_cached(
+ git_repository *repo,
+ git_attr_file_source_t source_type,
+ const char *filename)
+{
+ git_attr_cache *cache = git_repository_attr_cache(repo);
+ git_attr_file_entry *entry;
+ git_strmap *files;
+
+ if (!cache || !(files = cache->files))
+ return false;
+
+ if ((entry = git_strmap_get(files, filename)) == NULL)
+ return false;
+
+ return entry && (entry->file[source_type] != NULL);
+}
+
+
+static int attr_cache__lookup_path(
+ char **out, git_config *cfg, const char *key, const char *fallback)
+{
+ git_str buf = GIT_STR_INIT;
+ int error;
+ git_config_entry *entry = NULL;
+
+ *out = NULL;
+
+ if ((error = git_config__lookup_entry(&entry, cfg, key, false)) < 0)
+ return error;
+
+ if (entry) {
+ const char *cfgval = entry->value;
+
+ /* expand leading ~/ as needed */
+ if (cfgval && cfgval[0] == '~' && cfgval[1] == '/') {
+ if (! (error = git_sysdir_expand_homedir_file(&buf, &cfgval[2])))
+ *out = git_str_detach(&buf);
+ } else if (cfgval) {
+ *out = git__strdup(cfgval);
+ }
+ }
+ else if (!git_sysdir_find_xdg_file(&buf, fallback)) {
+ *out = git_str_detach(&buf);
+ }
+
+ git_config_entry_free(entry);
+ git_str_dispose(&buf);
+
+ return error;
+}
+
+static void attr_cache__free(git_attr_cache *cache)
+{
+ bool unlock;
+
+ if (!cache)
+ return;
+
+ unlock = (attr_cache_lock(cache) == 0);
+
+ if (cache->files != NULL) {
+ git_attr_file_entry *entry;
+ git_attr_file *file;
+ int i;
+
+ git_strmap_foreach_value(cache->files, entry, {
+ for (i = 0; i < GIT_ATTR_FILE_NUM_SOURCES; ++i) {
+ if ((file = git_atomic_swap(entry->file[i], NULL)) != NULL) {
+ GIT_REFCOUNT_OWN(file, NULL);
+ git_attr_file__free(file);
+ }
+ }
+ });
+ git_strmap_free(cache->files);
+ }
+
+ if (cache->macros != NULL) {
+ git_attr_rule *rule;
+
+ git_strmap_foreach_value(cache->macros, rule, {
+ git_attr_rule__free(rule);
+ });
+ git_strmap_free(cache->macros);
+ }
+
+ git_pool_clear(&cache->pool);
+
+ git__free(cache->cfg_attr_file);
+ cache->cfg_attr_file = NULL;
+
+ git__free(cache->cfg_excl_file);
+ cache->cfg_excl_file = NULL;
+
+ if (unlock)
+ attr_cache_unlock(cache);
+ git_mutex_free(&cache->lock);
+
+ git__free(cache);
+}
+
+int git_attr_cache__init(git_repository *repo)
+{
+ int ret = 0;
+ git_attr_cache *cache = git_repository_attr_cache(repo);
+ git_config *cfg = NULL;
+
+ if (cache)
+ return 0;
+
+ cache = git__calloc(1, sizeof(git_attr_cache));
+ GIT_ERROR_CHECK_ALLOC(cache);
+
+ /* set up lock */
+ if (git_mutex_init(&cache->lock) < 0) {
+ git_error_set(GIT_ERROR_OS, "unable to initialize lock for attr cache");
+ git__free(cache);
+ return -1;
+ }
+
+ if ((ret = git_repository_config_snapshot(&cfg, repo)) < 0)
+ goto cancel;
+
+ /* cache config settings for attributes and ignores */
+ ret = attr_cache__lookup_path(
+ &cache->cfg_attr_file, cfg, GIT_ATTR_CONFIG, GIT_ATTR_FILE_XDG);
+ if (ret < 0)
+ goto cancel;
+
+ ret = attr_cache__lookup_path(
+ &cache->cfg_excl_file, cfg, GIT_IGNORE_CONFIG, GIT_IGNORE_FILE_XDG);
+ if (ret < 0)
+ goto cancel;
+
+ /* allocate hashtable for attribute and ignore file contents,
+ * hashtable for attribute macros, and string pool
+ */
+ if ((ret = git_strmap_new(&cache->files)) < 0 ||
+ (ret = git_strmap_new(&cache->macros)) < 0 ||
+ (ret = git_pool_init(&cache->pool, 1)) < 0)
+ goto cancel;
+
+ if (git_atomic_compare_and_swap(&repo->attrcache, NULL, cache) != NULL)
+ goto cancel; /* raced with another thread, free this but no error */
+
+ git_config_free(cfg);
+
+ /* insert default macros */
+ return git_attr_add_macro(repo, "binary", "-diff -merge -text -crlf");
+
+cancel:
+ attr_cache__free(cache);
+ git_config_free(cfg);
+ return ret;
+}
+
+int git_attr_cache_flush(git_repository *repo)
+{
+ git_attr_cache *cache;
+
+ /* this could be done less expensively, but for now, we'll just free
+ * the entire attrcache and let the next use reinitialize it...
+ */
+ if (repo && (cache = git_atomic_swap(repo->attrcache, NULL)) != NULL)
+ attr_cache__free(cache);
+
+ return 0;
+}
+
+int git_attr_cache__insert_macro(git_repository *repo, git_attr_rule *macro)
+{
+ git_attr_cache *cache = git_repository_attr_cache(repo);
+ git_attr_rule *preexisting;
+ bool locked = false;
+ int error = 0;
+
+ /*
+ * Callers assume that if we return success, that the
+ * macro will have been adopted by the attributes cache.
+ * Thus, we have to free the macro here if it's not being
+ * added to the cache.
+ *
+ * TODO: generate warning log if (macro->assigns.length == 0)
+ */
+ if (macro->assigns.length == 0) {
+ git_attr_rule__free(macro);
+ goto out;
+ }
+
+ if ((error = attr_cache_lock(cache)) < 0)
+ goto out;
+ locked = true;
+
+ if ((preexisting = git_strmap_get(cache->macros, macro->match.pattern)) != NULL)
+ git_attr_rule__free(preexisting);
+
+ if ((error = git_strmap_set(cache->macros, macro->match.pattern, macro)) < 0)
+ goto out;
+
+out:
+ if (locked)
+ attr_cache_unlock(cache);
+ return error;
+}
+
+git_attr_rule *git_attr_cache__lookup_macro(
+ git_repository *repo, const char *name)
+{
+ git_strmap *macros = git_repository_attr_cache(repo)->macros;
+
+ return git_strmap_get(macros, name);
+}
diff --git a/src/libgit2/attrcache.h b/src/libgit2/attrcache.h
new file mode 100644
index 0000000..b13e0e8
--- /dev/null
+++ b/src/libgit2/attrcache.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_attrcache_h__
+#define INCLUDE_attrcache_h__
+
+#include "common.h"
+
+#include "attr_file.h"
+#include "strmap.h"
+
+#define GIT_ATTR_CONFIG "core.attributesfile"
+#define GIT_IGNORE_CONFIG "core.excludesfile"
+
+typedef struct {
+ char *cfg_attr_file; /* cached value of core.attributesfile */
+ char *cfg_excl_file; /* cached value of core.excludesfile */
+ git_strmap *files; /* hash path to git_attr_cache_entry records */
+ git_strmap *macros; /* hash name to vector<git_attr_assignment> */
+ git_mutex lock;
+ git_pool pool;
+} git_attr_cache;
+
+extern int git_attr_cache__init(git_repository *repo);
+
+/* get file - loading and reload as needed */
+extern int git_attr_cache__get(
+ git_attr_file **file,
+ git_repository *repo,
+ git_attr_session *attr_session,
+ git_attr_file_source *source,
+ git_attr_file_parser parser,
+ bool allow_macros);
+
+extern bool git_attr_cache__is_cached(
+ git_repository *repo,
+ git_attr_file_source_t source_type,
+ const char *filename);
+
+extern int git_attr_cache__alloc_file_entry(
+ git_attr_file_entry **out,
+ git_repository *repo,
+ const char *base,
+ const char *path,
+ git_pool *pool);
+
+extern int git_attr_cache__insert_macro(
+ git_repository *repo, git_attr_rule *macro);
+
+extern git_attr_rule *git_attr_cache__lookup_macro(
+ git_repository *repo, const char *name);
+
+#endif
diff --git a/src/libgit2/blame.c b/src/libgit2/blame.c
new file mode 100644
index 0000000..d93dd5e
--- /dev/null
+++ b/src/libgit2/blame.c
@@ -0,0 +1,566 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "blame.h"
+
+#include "git2/commit.h"
+#include "git2/revparse.h"
+#include "git2/revwalk.h"
+#include "git2/tree.h"
+#include "git2/diff.h"
+#include "git2/blob.h"
+#include "git2/signature.h"
+#include "git2/mailmap.h"
+#include "util.h"
+#include "repository.h"
+#include "blame_git.h"
+
+
+static int hunk_byfinalline_search_cmp(const void *key, const void *entry)
+{
+ git_blame_hunk *hunk = (git_blame_hunk*)entry;
+
+ size_t lineno = *(size_t*)key;
+ size_t lines_in_hunk = hunk->lines_in_hunk;
+ size_t final_start_line_number = hunk->final_start_line_number;
+
+ if (lineno < final_start_line_number)
+ return -1;
+ if (lineno >= final_start_line_number + lines_in_hunk)
+ return 1;
+ return 0;
+}
+
+static int paths_cmp(const void *a, const void *b) { return git__strcmp((char*)a, (char*)b); }
+static int hunk_cmp(const void *_a, const void *_b)
+{
+ git_blame_hunk *a = (git_blame_hunk*)_a,
+ *b = (git_blame_hunk*)_b;
+
+ if (a->final_start_line_number > b->final_start_line_number)
+ return 1;
+ else if (a->final_start_line_number < b->final_start_line_number)
+ return -1;
+ else
+ return 0;
+}
+
+static bool hunk_ends_at_or_before_line(git_blame_hunk *hunk, size_t line)
+{
+ return line >= (hunk->final_start_line_number + hunk->lines_in_hunk - 1);
+}
+
+static bool hunk_starts_at_or_after_line(git_blame_hunk *hunk, size_t line)
+{
+ return line <= hunk->final_start_line_number;
+}
+
+static git_blame_hunk *new_hunk(
+ size_t start,
+ size_t lines,
+ size_t orig_start,
+ const char *path,
+ git_blame *blame)
+{
+ git_blame_hunk *hunk = git__calloc(1, sizeof(git_blame_hunk));
+ if (!hunk) return NULL;
+
+ hunk->lines_in_hunk = lines;
+ hunk->final_start_line_number = start;
+ hunk->orig_start_line_number = orig_start;
+ hunk->orig_path = path ? git__strdup(path) : NULL;
+ git_oid_clear(&hunk->orig_commit_id, blame->repository->oid_type);
+ git_oid_clear(&hunk->final_commit_id, blame->repository->oid_type);
+
+ return hunk;
+}
+
+static void free_hunk(git_blame_hunk *hunk)
+{
+ git__free((void*)hunk->orig_path);
+ git_signature_free(hunk->final_signature);
+ git_signature_free(hunk->orig_signature);
+ git__free(hunk);
+}
+
+static git_blame_hunk *dup_hunk(git_blame_hunk *hunk, git_blame *blame)
+{
+ git_blame_hunk *newhunk = new_hunk(
+ hunk->final_start_line_number,
+ hunk->lines_in_hunk,
+ hunk->orig_start_line_number,
+ hunk->orig_path,
+ blame);
+
+ if (!newhunk)
+ return NULL;
+
+ git_oid_cpy(&newhunk->orig_commit_id, &hunk->orig_commit_id);
+ git_oid_cpy(&newhunk->final_commit_id, &hunk->final_commit_id);
+ newhunk->boundary = hunk->boundary;
+
+ if (git_signature_dup(&newhunk->final_signature, hunk->final_signature) < 0 ||
+ git_signature_dup(&newhunk->orig_signature, hunk->orig_signature) < 0) {
+ free_hunk(newhunk);
+ return NULL;
+ }
+
+ return newhunk;
+}
+
+/* Starting with the hunk that includes start_line, shift all following hunks'
+ * final_start_line by shift_by lines */
+static void shift_hunks_by(git_vector *v, size_t start_line, int shift_by)
+{
+ size_t i;
+
+ if (!git_vector_bsearch2(&i, v, hunk_byfinalline_search_cmp, &start_line)) {
+ for (; i < v->length; i++) {
+ git_blame_hunk *hunk = (git_blame_hunk*)v->contents[i];
+ hunk->final_start_line_number += shift_by;
+ }
+ }
+}
+
+git_blame *git_blame__alloc(
+ git_repository *repo,
+ git_blame_options opts,
+ const char *path)
+{
+ git_blame *gbr = git__calloc(1, sizeof(git_blame));
+ if (!gbr)
+ return NULL;
+
+ gbr->repository = repo;
+ gbr->options = opts;
+
+ if (git_vector_init(&gbr->hunks, 8, hunk_cmp) < 0 ||
+ git_vector_init(&gbr->paths, 8, paths_cmp) < 0 ||
+ (gbr->path = git__strdup(path)) == NULL ||
+ git_vector_insert(&gbr->paths, git__strdup(path)) < 0)
+ {
+ git_blame_free(gbr);
+ return NULL;
+ }
+
+ if (opts.flags & GIT_BLAME_USE_MAILMAP &&
+ git_mailmap_from_repository(&gbr->mailmap, repo) < 0) {
+ git_blame_free(gbr);
+ return NULL;
+ }
+
+ return gbr;
+}
+
+void git_blame_free(git_blame *blame)
+{
+ size_t i;
+ git_blame_hunk *hunk;
+
+ if (!blame) return;
+
+ git_vector_foreach(&blame->hunks, i, hunk)
+ free_hunk(hunk);
+ git_vector_free(&blame->hunks);
+
+ git_vector_free_deep(&blame->paths);
+
+ git_array_clear(blame->line_index);
+
+ git_mailmap_free(blame->mailmap);
+
+ git__free(blame->path);
+ git_blob_free(blame->final_blob);
+ git__free(blame);
+}
+
+uint32_t git_blame_get_hunk_count(git_blame *blame)
+{
+ GIT_ASSERT_ARG(blame);
+ return (uint32_t)blame->hunks.length;
+}
+
+const git_blame_hunk *git_blame_get_hunk_byindex(git_blame *blame, uint32_t index)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(blame, NULL);
+ return (git_blame_hunk*)git_vector_get(&blame->hunks, index);
+}
+
+const git_blame_hunk *git_blame_get_hunk_byline(git_blame *blame, size_t lineno)
+{
+ size_t i, new_lineno = lineno;
+
+ GIT_ASSERT_ARG_WITH_RETVAL(blame, NULL);
+
+ if (!git_vector_bsearch2(&i, &blame->hunks, hunk_byfinalline_search_cmp, &new_lineno)) {
+ return git_blame_get_hunk_byindex(blame, (uint32_t)i);
+ }
+
+ return NULL;
+}
+
+static int normalize_options(
+ git_blame_options *out,
+ const git_blame_options *in,
+ git_repository *repo)
+{
+ git_blame_options dummy = GIT_BLAME_OPTIONS_INIT;
+ if (!in) in = &dummy;
+
+ memcpy(out, in, sizeof(git_blame_options));
+
+ /* No newest_commit => HEAD */
+ if (git_oid_is_zero(&out->newest_commit)) {
+ if (git_reference_name_to_id(&out->newest_commit, repo, "HEAD") < 0) {
+ return -1;
+ }
+ }
+
+ /* min_line 0 really means 1 */
+ if (!out->min_line) out->min_line = 1;
+ /* max_line 0 really means N, but we don't know N yet */
+
+ /* Fix up option implications */
+ if (out->flags & GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES)
+ out->flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES;
+ if (out->flags & GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES)
+ out->flags |= GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES;
+ if (out->flags & GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES)
+ out->flags |= GIT_BLAME_TRACK_COPIES_SAME_FILE;
+
+ return 0;
+}
+
+static git_blame_hunk *split_hunk_in_vector(
+ git_vector *vec,
+ git_blame_hunk *hunk,
+ size_t rel_line,
+ bool return_new,
+ git_blame *blame)
+{
+ size_t new_line_count;
+ git_blame_hunk *nh;
+
+ /* Don't split if already at a boundary */
+ if (rel_line <= 0 ||
+ rel_line >= hunk->lines_in_hunk)
+ {
+ return hunk;
+ }
+
+ new_line_count = hunk->lines_in_hunk - rel_line;
+ nh = new_hunk(hunk->final_start_line_number + rel_line,
+ new_line_count, hunk->orig_start_line_number + rel_line,
+ hunk->orig_path, blame);
+
+ if (!nh)
+ return NULL;
+
+ git_oid_cpy(&nh->final_commit_id, &hunk->final_commit_id);
+ git_oid_cpy(&nh->orig_commit_id, &hunk->orig_commit_id);
+
+ /* Adjust hunk that was split */
+ hunk->lines_in_hunk -= new_line_count;
+ git_vector_insert_sorted(vec, nh, NULL);
+ {
+ git_blame_hunk *ret = return_new ? nh : hunk;
+ return ret;
+ }
+}
+
+/*
+ * Construct a list of char indices for where lines begin
+ * Adapted from core git:
+ * https://github.com/gitster/git/blob/be5c9fb9049ed470e7005f159bb923a5f4de1309/builtin/blame.c#L1760-L1789
+ */
+static int index_blob_lines(git_blame *blame)
+{
+ const char *buf = blame->final_buf;
+ size_t len = blame->final_buf_size;
+ int num = 0, incomplete = 0, bol = 1;
+ size_t *i;
+
+ if (len && buf[len-1] != '\n')
+ incomplete++; /* incomplete line at the end */
+ while (len--) {
+ if (bol) {
+ i = git_array_alloc(blame->line_index);
+ GIT_ERROR_CHECK_ALLOC(i);
+ *i = buf - blame->final_buf;
+ bol = 0;
+ }
+ if (*buf++ == '\n') {
+ num++;
+ bol = 1;
+ }
+ }
+ i = git_array_alloc(blame->line_index);
+ GIT_ERROR_CHECK_ALLOC(i);
+ *i = buf - blame->final_buf;
+ blame->num_lines = num + incomplete;
+ return blame->num_lines;
+}
+
+static git_blame_hunk *hunk_from_entry(git_blame__entry *e, git_blame *blame)
+{
+ git_blame_hunk *h = new_hunk(
+ e->lno+1, e->num_lines, e->s_lno+1, e->suspect->path,
+ blame);
+
+ if (!h)
+ return NULL;
+
+ git_oid_cpy(&h->final_commit_id, git_commit_id(e->suspect->commit));
+ git_oid_cpy(&h->orig_commit_id, git_commit_id(e->suspect->commit));
+ git_commit_author_with_mailmap(
+ &h->final_signature, e->suspect->commit, blame->mailmap);
+ git_signature_dup(&h->orig_signature, h->final_signature);
+ h->boundary = e->is_boundary ? 1 : 0;
+ return h;
+}
+
+static int load_blob(git_blame *blame)
+{
+ int error;
+
+ if (blame->final_blob) return 0;
+
+ error = git_commit_lookup(&blame->final, blame->repository, &blame->options.newest_commit);
+ if (error < 0)
+ goto cleanup;
+ error = git_object_lookup_bypath((git_object**)&blame->final_blob,
+ (git_object*)blame->final, blame->path, GIT_OBJECT_BLOB);
+
+cleanup:
+ return error;
+}
+
+static int blame_internal(git_blame *blame)
+{
+ int error;
+ git_blame__entry *ent = NULL;
+ git_blame__origin *o;
+
+ if ((error = load_blob(blame)) < 0 ||
+ (error = git_blame__get_origin(&o, blame, blame->final, blame->path)) < 0)
+ goto cleanup;
+
+ if (git_blob_rawsize(blame->final_blob) > SIZE_MAX) {
+ git_error_set(GIT_ERROR_NOMEMORY, "blob is too large to blame");
+ error = -1;
+ goto cleanup;
+ }
+
+ blame->final_buf = git_blob_rawcontent(blame->final_blob);
+ blame->final_buf_size = (size_t)git_blob_rawsize(blame->final_blob);
+
+ ent = git__calloc(1, sizeof(git_blame__entry));
+ GIT_ERROR_CHECK_ALLOC(ent);
+
+ ent->num_lines = index_blob_lines(blame);
+ ent->lno = blame->options.min_line - 1;
+ ent->num_lines = ent->num_lines - blame->options.min_line + 1;
+ if (blame->options.max_line > 0)
+ ent->num_lines = blame->options.max_line - blame->options.min_line + 1;
+ ent->s_lno = ent->lno;
+ ent->suspect = o;
+
+ blame->ent = ent;
+
+ error = git_blame__like_git(blame, blame->options.flags);
+
+cleanup:
+ for (ent = blame->ent; ent; ) {
+ git_blame__entry *e = ent->next;
+ git_blame_hunk *h = hunk_from_entry(ent, blame);
+
+ git_vector_insert(&blame->hunks, h);
+
+ git_blame__free_entry(ent);
+ ent = e;
+ }
+
+ return error;
+}
+
+/*******************************************************************************
+ * File blaming
+ ******************************************************************************/
+
+int git_blame_file(
+ git_blame **out,
+ git_repository *repo,
+ const char *path,
+ git_blame_options *options)
+{
+ int error = -1;
+ git_blame_options normOptions = GIT_BLAME_OPTIONS_INIT;
+ git_blame *blame = NULL;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(path);
+
+ if ((error = normalize_options(&normOptions, options, repo)) < 0)
+ goto on_error;
+
+ blame = git_blame__alloc(repo, normOptions, path);
+ GIT_ERROR_CHECK_ALLOC(blame);
+
+ if ((error = load_blob(blame)) < 0)
+ goto on_error;
+
+ if ((error = blame_internal(blame)) < 0)
+ goto on_error;
+
+ *out = blame;
+ return 0;
+
+on_error:
+ git_blame_free(blame);
+ return error;
+}
+
+/*******************************************************************************
+ * Buffer blaming
+ *******************************************************************************/
+
+static bool hunk_is_bufferblame(git_blame_hunk *hunk)
+{
+ return hunk && git_oid_is_zero(&hunk->final_commit_id);
+}
+
+static int buffer_hunk_cb(
+ const git_diff_delta *delta,
+ const git_diff_hunk *hunk,
+ void *payload)
+{
+ git_blame *blame = (git_blame*)payload;
+ uint32_t wedge_line;
+
+ GIT_UNUSED(delta);
+
+ wedge_line = (hunk->old_lines == 0) ? hunk->new_start : hunk->old_start;
+ blame->current_diff_line = wedge_line;
+
+ blame->current_hunk = (git_blame_hunk*)git_blame_get_hunk_byline(blame, wedge_line);
+ if (!blame->current_hunk) {
+ /* Line added at the end of the file */
+ blame->current_hunk = new_hunk(wedge_line, 0, wedge_line,
+ blame->path, blame);
+ GIT_ERROR_CHECK_ALLOC(blame->current_hunk);
+
+ git_vector_insert(&blame->hunks, blame->current_hunk);
+ } else if (!hunk_starts_at_or_after_line(blame->current_hunk, wedge_line)){
+ /* If this hunk doesn't start between existing hunks, split a hunk up so it does */
+ blame->current_hunk = split_hunk_in_vector(&blame->hunks, blame->current_hunk,
+ wedge_line - blame->current_hunk->orig_start_line_number, true,
+ blame);
+ GIT_ERROR_CHECK_ALLOC(blame->current_hunk);
+ }
+
+ return 0;
+}
+
+static int ptrs_equal_cmp(const void *a, const void *b) { return a<b ? -1 : a>b ? 1 : 0; }
+static int buffer_line_cb(
+ const git_diff_delta *delta,
+ const git_diff_hunk *hunk,
+ const git_diff_line *line,
+ void *payload)
+{
+ git_blame *blame = (git_blame*)payload;
+
+ GIT_UNUSED(delta);
+ GIT_UNUSED(hunk);
+ GIT_UNUSED(line);
+
+ if (line->origin == GIT_DIFF_LINE_ADDITION) {
+ if (hunk_is_bufferblame(blame->current_hunk) &&
+ hunk_ends_at_or_before_line(blame->current_hunk, blame->current_diff_line)) {
+ /* Append to the current buffer-blame hunk */
+ blame->current_hunk->lines_in_hunk++;
+ shift_hunks_by(&blame->hunks, blame->current_diff_line+1, 1);
+ } else {
+ /* Create a new buffer-blame hunk with this line */
+ shift_hunks_by(&blame->hunks, blame->current_diff_line, 1);
+ blame->current_hunk = new_hunk(blame->current_diff_line, 1, 0, blame->path, blame);
+ GIT_ERROR_CHECK_ALLOC(blame->current_hunk);
+
+ git_vector_insert_sorted(&blame->hunks, blame->current_hunk, NULL);
+ }
+ blame->current_diff_line++;
+ }
+
+ if (line->origin == GIT_DIFF_LINE_DELETION) {
+ /* Trim the line from the current hunk; remove it if it's now empty */
+ size_t shift_base = blame->current_diff_line + blame->current_hunk->lines_in_hunk+1;
+
+ if (--(blame->current_hunk->lines_in_hunk) == 0) {
+ size_t i;
+ shift_base--;
+ if (!git_vector_search2(&i, &blame->hunks, ptrs_equal_cmp, blame->current_hunk)) {
+ git_vector_remove(&blame->hunks, i);
+ free_hunk(blame->current_hunk);
+ blame->current_hunk = (git_blame_hunk*)git_blame_get_hunk_byindex(blame, (uint32_t)i);
+ }
+ }
+ shift_hunks_by(&blame->hunks, shift_base, -1);
+ }
+ return 0;
+}
+
+int git_blame_buffer(
+ git_blame **out,
+ git_blame *reference,
+ const char *buffer,
+ size_t buffer_len)
+{
+ git_blame *blame;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ size_t i;
+ git_blame_hunk *hunk;
+
+ diffopts.context_lines = 0;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(reference);
+ GIT_ASSERT_ARG(buffer && buffer_len);
+
+ blame = git_blame__alloc(reference->repository, reference->options, reference->path);
+ GIT_ERROR_CHECK_ALLOC(blame);
+
+ /* Duplicate all of the hunk structures in the reference blame */
+ git_vector_foreach(&reference->hunks, i, hunk) {
+ git_blame_hunk *h = dup_hunk(hunk, blame);
+ GIT_ERROR_CHECK_ALLOC(h);
+
+ git_vector_insert(&blame->hunks, h);
+ }
+
+ /* Diff to the reference blob */
+ git_diff_blob_to_buffer(reference->final_blob, blame->path,
+ buffer, buffer_len, blame->path, &diffopts,
+ NULL, NULL, buffer_hunk_cb, buffer_line_cb, blame);
+
+ *out = blame;
+ return 0;
+}
+
+int git_blame_options_init(git_blame_options *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_blame_options, GIT_BLAME_OPTIONS_INIT);
+ return 0;
+}
+
+#ifndef GIT_DEPRECATE_HARD
+int git_blame_init_options(git_blame_options *opts, unsigned int version)
+{
+ return git_blame_options_init(opts, version);
+}
+#endif
diff --git a/src/libgit2/blame.h b/src/libgit2/blame.h
new file mode 100644
index 0000000..4141e2b
--- /dev/null
+++ b/src/libgit2/blame.h
@@ -0,0 +1,95 @@
+#ifndef INCLUDE_blame_h__
+#define INCLUDE_blame_h__
+
+#include "common.h"
+
+#include "git2/blame.h"
+#include "vector.h"
+#include "diff.h"
+#include "array.h"
+#include "git2/oid.h"
+
+/*
+ * One blob in a commit that is being suspected
+ */
+typedef struct git_blame__origin {
+ int refcnt;
+ struct git_blame__origin *previous;
+ git_commit *commit;
+ git_blob *blob;
+ char path[GIT_FLEX_ARRAY];
+} git_blame__origin;
+
+/*
+ * Each group of lines is described by a git_blame__entry; it can be split
+ * as we pass blame to the parents. They form a linked list in the
+ * scoreboard structure, sorted by the target line number.
+ */
+typedef struct git_blame__entry {
+ struct git_blame__entry *prev;
+ struct git_blame__entry *next;
+
+ /* the first line of this group in the final image;
+ * internally all line numbers are 0 based.
+ */
+ size_t lno;
+
+ /* how many lines this group has */
+ size_t num_lines;
+
+ /* the commit that introduced this group into the final image */
+ git_blame__origin *suspect;
+
+ /* true if the suspect is truly guilty; false while we have not
+ * checked if the group came from one of its parents.
+ */
+ bool guilty;
+
+ /* true if the entry has been scanned for copies in the current parent
+ */
+ bool scanned;
+
+ /* the line number of the first line of this group in the
+ * suspect's file; internally all line numbers are 0 based.
+ */
+ size_t s_lno;
+
+ /* how significant this entry is -- cached to avoid
+ * scanning the lines over and over.
+ */
+ unsigned score;
+
+ /* Whether this entry has been tracked to a boundary commit.
+ */
+ bool is_boundary;
+} git_blame__entry;
+
+struct git_blame {
+ char *path;
+ git_repository *repository;
+ git_mailmap *mailmap;
+ git_blame_options options;
+
+ git_vector hunks;
+ git_vector paths;
+
+ git_blob *final_blob;
+ git_array_t(size_t) line_index;
+
+ size_t current_diff_line;
+ git_blame_hunk *current_hunk;
+
+ /* Scoreboard fields */
+ git_commit *final;
+ git_blame__entry *ent;
+ int num_lines;
+ const char *final_buf;
+ size_t final_buf_size;
+};
+
+git_blame *git_blame__alloc(
+ git_repository *repo,
+ git_blame_options opts,
+ const char *path);
+
+#endif
diff --git a/src/libgit2/blame_git.c b/src/libgit2/blame_git.c
new file mode 100644
index 0000000..69897b3
--- /dev/null
+++ b/src/libgit2/blame_git.c
@@ -0,0 +1,684 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "blame_git.h"
+
+#include "commit.h"
+#include "blob.h"
+#include "diff_xdiff.h"
+
+/*
+ * Origin is refcounted and usually we keep the blob contents to be
+ * reused.
+ */
+static git_blame__origin *origin_incref(git_blame__origin *o)
+{
+ if (o)
+ o->refcnt++;
+ return o;
+}
+
+static void origin_decref(git_blame__origin *o)
+{
+ if (o && --o->refcnt <= 0) {
+ if (o->previous)
+ origin_decref(o->previous);
+ git_blob_free(o->blob);
+ git_commit_free(o->commit);
+ git__free(o);
+ }
+}
+
+/* Given a commit and a path in it, create a new origin structure. */
+static int make_origin(git_blame__origin **out, git_commit *commit, const char *path)
+{
+ git_blame__origin *o;
+ git_object *blob;
+ size_t path_len = strlen(path), alloc_len;
+ int error = 0;
+
+ if ((error = git_object_lookup_bypath(&blob, (git_object*)commit,
+ path, GIT_OBJECT_BLOB)) < 0)
+ return error;
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, sizeof(*o), path_len);
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 1);
+ o = git__calloc(1, alloc_len);
+ GIT_ERROR_CHECK_ALLOC(o);
+
+ o->commit = commit;
+ o->blob = (git_blob *) blob;
+ o->refcnt = 1;
+ strcpy(o->path, path);
+
+ *out = o;
+
+ return 0;
+}
+
+/* Locate an existing origin or create a new one. */
+int git_blame__get_origin(
+ git_blame__origin **out,
+ git_blame *blame,
+ git_commit *commit,
+ const char *path)
+{
+ git_blame__entry *e;
+
+ for (e = blame->ent; e; e = e->next) {
+ if (e->suspect->commit == commit && !strcmp(e->suspect->path, path)) {
+ *out = origin_incref(e->suspect);
+ }
+ }
+ return make_origin(out, commit, path);
+}
+
+typedef struct blame_chunk_cb_data {
+ git_blame *blame;
+ git_blame__origin *target;
+ git_blame__origin *parent;
+ long tlno;
+ long plno;
+}blame_chunk_cb_data;
+
+static bool same_suspect(git_blame__origin *a, git_blame__origin *b)
+{
+ if (a == b)
+ return true;
+ if (git_oid_cmp(git_commit_id(a->commit), git_commit_id(b->commit)))
+ return false;
+ return 0 == strcmp(a->path, b->path);
+}
+
+/* find the line number of the last line the target is suspected for */
+static bool find_last_in_target(size_t *out, git_blame *blame, git_blame__origin *target)
+{
+ git_blame__entry *e;
+ size_t last_in_target = 0;
+ bool found = false;
+
+ *out = 0;
+
+ for (e=blame->ent; e; e=e->next) {
+ if (e->guilty || !same_suspect(e->suspect, target))
+ continue;
+ if (last_in_target < e->s_lno + e->num_lines) {
+ found = true;
+ last_in_target = e->s_lno + e->num_lines;
+ }
+ }
+
+ *out = last_in_target;
+ return found;
+}
+
+/*
+ * It is known that lines between tlno to same came from parent, and e
+ * has an overlap with that range. it also is known that parent's
+ * line plno corresponds to e's line tlno.
+ *
+ * <---- e ----->
+ * <------> (entirely within)
+ * <------------> (extends past)
+ * <------------> (starts before)
+ * <------------------> (entirely encloses)
+ *
+ * Split e into potentially three parts; before this chunk, the chunk
+ * to be blamed for the parent, and after that portion.
+ */
+static void split_overlap(git_blame__entry *split, git_blame__entry *e,
+ size_t tlno, size_t plno, size_t same, git_blame__origin *parent)
+{
+ size_t chunk_end_lno;
+
+ if (e->s_lno < tlno) {
+ /* there is a pre-chunk part not blamed on the parent */
+ split[0].suspect = origin_incref(e->suspect);
+ split[0].lno = e->lno;
+ split[0].s_lno = e->s_lno;
+ split[0].num_lines = tlno - e->s_lno;
+ split[1].lno = e->lno + tlno - e->s_lno;
+ split[1].s_lno = plno;
+ } else {
+ split[1].lno = e->lno;
+ split[1].s_lno = plno + (e->s_lno - tlno);
+ }
+
+ if (same < e->s_lno + e->num_lines) {
+ /* there is a post-chunk part not blamed on parent */
+ split[2].suspect = origin_incref(e->suspect);
+ split[2].lno = e->lno + (same - e->s_lno);
+ split[2].s_lno = e->s_lno + (same - e->s_lno);
+ split[2].num_lines = e->s_lno + e->num_lines - same;
+ chunk_end_lno = split[2].lno;
+ } else {
+ chunk_end_lno = e->lno + e->num_lines;
+ }
+ split[1].num_lines = chunk_end_lno - split[1].lno;
+
+ /*
+ * if it turns out there is nothing to blame the parent for, forget about
+ * the splitting. !split[1].suspect signals this.
+ */
+ if (split[1].num_lines < 1)
+ return;
+ split[1].suspect = origin_incref(parent);
+}
+
+/*
+ * Link in a new blame entry to the scoreboard. Entries that cover the same
+ * line range have been removed from the scoreboard previously.
+ */
+static void add_blame_entry(git_blame *blame, git_blame__entry *e)
+{
+ git_blame__entry *ent, *prev = NULL;
+
+ origin_incref(e->suspect);
+
+ for (ent = blame->ent; ent && ent->lno < e->lno; ent = ent->next)
+ prev = ent;
+
+ /* prev, if not NULL, is the last one that is below e */
+ e->prev = prev;
+ if (prev) {
+ e->next = prev->next;
+ prev->next = e;
+ } else {
+ e->next = blame->ent;
+ blame->ent = e;
+ }
+ if (e->next)
+ e->next->prev = e;
+}
+
+/*
+ * src typically is on-stack; we want to copy the information in it to
+ * a malloced blame_entry that is already on the linked list of the scoreboard.
+ * The origin of dst loses a refcnt while the origin of src gains one.
+ */
+static void dup_entry(git_blame__entry *dst, git_blame__entry *src)
+{
+ git_blame__entry *p, *n;
+
+ p = dst->prev;
+ n = dst->next;
+ origin_incref(src->suspect);
+ origin_decref(dst->suspect);
+ memcpy(dst, src, sizeof(*src));
+ dst->prev = p;
+ dst->next = n;
+ dst->score = 0;
+}
+
+/*
+ * split_overlap() divided an existing blame e into up to three parts in split.
+ * Adjust the linked list of blames in the scoreboard to reflect the split.
+ */
+static int split_blame(git_blame *blame, git_blame__entry *split, git_blame__entry *e)
+{
+ git_blame__entry *new_entry;
+
+ if (split[0].suspect && split[2].suspect) {
+ /* The first part (reuse storage for the existing entry e */
+ dup_entry(e, &split[0]);
+
+ /* The last part -- me */
+ new_entry = git__malloc(sizeof(*new_entry));
+ GIT_ERROR_CHECK_ALLOC(new_entry);
+ memcpy(new_entry, &(split[2]), sizeof(git_blame__entry));
+ add_blame_entry(blame, new_entry);
+
+ /* ... and the middle part -- parent */
+ new_entry = git__malloc(sizeof(*new_entry));
+ GIT_ERROR_CHECK_ALLOC(new_entry);
+ memcpy(new_entry, &(split[1]), sizeof(git_blame__entry));
+ add_blame_entry(blame, new_entry);
+ } else if (!split[0].suspect && !split[2].suspect) {
+ /*
+ * The parent covers the entire area; reuse storage for e and replace it
+ * with the parent
+ */
+ dup_entry(e, &split[1]);
+ } else if (split[0].suspect) {
+ /* me and then parent */
+ dup_entry(e, &split[0]);
+ new_entry = git__malloc(sizeof(*new_entry));
+ GIT_ERROR_CHECK_ALLOC(new_entry);
+ memcpy(new_entry, &(split[1]), sizeof(git_blame__entry));
+ add_blame_entry(blame, new_entry);
+ } else {
+ /* parent and then me */
+ dup_entry(e, &split[1]);
+ new_entry = git__malloc(sizeof(*new_entry));
+ GIT_ERROR_CHECK_ALLOC(new_entry);
+ memcpy(new_entry, &(split[2]), sizeof(git_blame__entry));
+ add_blame_entry(blame, new_entry);
+ }
+
+ return 0;
+}
+
+/*
+ * After splitting the blame, the origins used by the on-stack blame_entry
+ * should lose one refcnt each.
+ */
+static void decref_split(git_blame__entry *split)
+{
+ int i;
+ for (i=0; i<3; i++)
+ origin_decref(split[i].suspect);
+}
+
+/*
+ * Helper for blame_chunk(). blame_entry e is known to overlap with the patch
+ * hunk; split it and pass blame to the parent.
+ */
+static int blame_overlap(
+ git_blame *blame,
+ git_blame__entry *e,
+ size_t tlno,
+ size_t plno,
+ size_t same,
+ git_blame__origin *parent)
+{
+ git_blame__entry split[3] = {{0}};
+
+ split_overlap(split, e, tlno, plno, same, parent);
+ if (split[1].suspect)
+ if (split_blame(blame, split, e) < 0)
+ return -1;
+ decref_split(split);
+
+ return 0;
+}
+
+/*
+ * Process one hunk from the patch between the current suspect for blame_entry
+ * e and its parent. Find and split the overlap, and pass blame to the
+ * overlapping part to the parent.
+ */
+static int blame_chunk(
+ git_blame *blame,
+ size_t tlno,
+ size_t plno,
+ size_t same,
+ git_blame__origin *target,
+ git_blame__origin *parent)
+{
+ git_blame__entry *e;
+
+ for (e = blame->ent; e; e = e->next) {
+ if (e->guilty || !same_suspect(e->suspect, target))
+ continue;
+ if (same <= e->s_lno)
+ continue;
+ if (tlno < e->s_lno + e->num_lines) {
+ if (blame_overlap(blame, e, tlno, plno, same, parent) < 0)
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int my_emit(
+ long start_a, long count_a,
+ long start_b, long count_b,
+ void *cb_data)
+{
+ blame_chunk_cb_data *d = (blame_chunk_cb_data *)cb_data;
+
+ if (blame_chunk(d->blame, d->tlno, d->plno, start_b, d->target, d->parent) < 0)
+ return -1;
+ d->plno = start_a + count_a;
+ d->tlno = start_b + count_b;
+
+ return 0;
+}
+
+static void trim_common_tail(mmfile_t *a, mmfile_t *b, long ctx)
+{
+ const int blk = 1024;
+ long trimmed = 0, recovered = 0;
+ char *ap = a->ptr + a->size;
+ char *bp = b->ptr + b->size;
+ long smaller = (long)((a->size < b->size) ? a->size : b->size);
+
+ if (ctx)
+ return;
+
+ while (blk + trimmed <= smaller && !memcmp(ap - blk, bp - blk, blk)) {
+ trimmed += blk;
+ ap -= blk;
+ bp -= blk;
+ }
+
+ while (recovered < trimmed)
+ if (ap[recovered++] == '\n')
+ break;
+ a->size -= trimmed - recovered;
+ b->size -= trimmed - recovered;
+}
+
+static int diff_hunks(mmfile_t file_a, mmfile_t file_b, void *cb_data, git_blame_options *options)
+{
+ xdemitconf_t xecfg = {0};
+ xdemitcb_t ecb = {0};
+ xpparam_t xpp = {0};
+
+ if (options->flags & GIT_BLAME_IGNORE_WHITESPACE)
+ xpp.flags |= XDF_IGNORE_WHITESPACE;
+
+ xecfg.hunk_func = my_emit;
+ ecb.priv = cb_data;
+
+ trim_common_tail(&file_a, &file_b, 0);
+
+ if (file_a.size > GIT_XDIFF_MAX_SIZE ||
+ file_b.size > GIT_XDIFF_MAX_SIZE) {
+ git_error_set(GIT_ERROR_INVALID, "file too large to blame");
+ return -1;
+ }
+
+ return xdl_diff(&file_a, &file_b, &xpp, &xecfg, &ecb);
+}
+
+static void fill_origin_blob(git_blame__origin *o, mmfile_t *file)
+{
+ memset(file, 0, sizeof(*file));
+ if (o->blob) {
+ file->ptr = (char*)git_blob_rawcontent(o->blob);
+ file->size = (long)git_blob_rawsize(o->blob);
+ }
+}
+
+static int pass_blame_to_parent(
+ git_blame *blame,
+ git_blame__origin *target,
+ git_blame__origin *parent)
+{
+ size_t last_in_target;
+ mmfile_t file_p, file_o;
+ blame_chunk_cb_data d = { blame, target, parent, 0, 0 };
+
+ if (!find_last_in_target(&last_in_target, blame, target))
+ return 1; /* nothing remains for this target */
+
+ fill_origin_blob(parent, &file_p);
+ fill_origin_blob(target, &file_o);
+
+ if (diff_hunks(file_p, file_o, &d, &blame->options) < 0)
+ return -1;
+
+ /* The reset (i.e. anything after tlno) are the same as the parent */
+ if (blame_chunk(blame, d.tlno, d.plno, last_in_target, target, parent) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int paths_on_dup(void **old, void *new)
+{
+ GIT_UNUSED(old);
+ git__free(new);
+ return -1;
+}
+
+static git_blame__origin *find_origin(
+ git_blame *blame,
+ git_commit *parent,
+ git_blame__origin *origin)
+{
+ git_blame__origin *porigin = NULL;
+ git_diff *difflist = NULL;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_tree *otree=NULL, *ptree=NULL;
+
+ /* Get the trees from this commit and its parent */
+ if (0 != git_commit_tree(&otree, origin->commit) ||
+ 0 != git_commit_tree(&ptree, parent))
+ goto cleanup;
+
+ /* Configure the diff */
+ diffopts.context_lines = 0;
+ diffopts.flags = GIT_DIFF_SKIP_BINARY_CHECK;
+
+ /* Check to see if files we're interested have changed */
+ diffopts.pathspec.count = blame->paths.length;
+ diffopts.pathspec.strings = (char**)blame->paths.contents;
+ if (0 != git_diff_tree_to_tree(&difflist, blame->repository, ptree, otree, &diffopts))
+ goto cleanup;
+
+ if (!git_diff_num_deltas(difflist)) {
+ /* No changes; copy data */
+ git_blame__get_origin(&porigin, blame, parent, origin->path);
+ } else {
+ git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
+ int i;
+
+ /* Generate a full diff between the two trees */
+ git_diff_free(difflist);
+ diffopts.pathspec.count = 0;
+ if (0 != git_diff_tree_to_tree(&difflist, blame->repository, ptree, otree, &diffopts))
+ goto cleanup;
+
+ /* Let diff find renames */
+ findopts.flags = GIT_DIFF_FIND_RENAMES;
+ if (0 != git_diff_find_similar(difflist, &findopts))
+ goto cleanup;
+
+ /* Find one that matches */
+ for (i=0; i<(int)git_diff_num_deltas(difflist); i++) {
+ const git_diff_delta *delta = git_diff_get_delta(difflist, i);
+
+ if (!git_vector_bsearch(NULL, &blame->paths, delta->new_file.path))
+ {
+ git_vector_insert_sorted(&blame->paths, (void*)git__strdup(delta->old_file.path),
+ paths_on_dup);
+ make_origin(&porigin, parent, delta->old_file.path);
+ }
+ }
+ }
+
+cleanup:
+ git_diff_free(difflist);
+ git_tree_free(otree);
+ git_tree_free(ptree);
+ return porigin;
+}
+
+/*
+ * The blobs of origin and porigin exactly match, so everything origin is
+ * suspected for can be blamed on the parent.
+ */
+static int pass_whole_blame(git_blame *blame,
+ git_blame__origin *origin, git_blame__origin *porigin)
+{
+ git_blame__entry *e;
+
+ if (!porigin->blob &&
+ git_object_lookup((git_object**)&porigin->blob, blame->repository,
+ git_blob_id(origin->blob), GIT_OBJECT_BLOB) < 0)
+ return -1;
+ for (e=blame->ent; e; e=e->next) {
+ if (!same_suspect(e->suspect, origin))
+ continue;
+ origin_incref(porigin);
+ origin_decref(e->suspect);
+ e->suspect = porigin;
+ }
+
+ return 0;
+}
+
+static int pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt)
+{
+ git_commit *commit = origin->commit;
+ int i, num_parents;
+ git_blame__origin *sg_buf[16];
+ git_blame__origin *porigin, **sg_origin = sg_buf;
+ int ret, error = 0;
+
+ num_parents = git_commit_parentcount(commit);
+ if (!git_oid_cmp(git_commit_id(commit), &blame->options.oldest_commit))
+ /* Stop at oldest specified commit */
+ num_parents = 0;
+ else if (opt & GIT_BLAME_FIRST_PARENT && num_parents > 1)
+ /* Limit search to the first parent */
+ num_parents = 1;
+
+ if (!num_parents) {
+ git_oid_cpy(&blame->options.oldest_commit, git_commit_id(commit));
+ goto finish;
+ } else if (num_parents < (int)ARRAY_SIZE(sg_buf))
+ memset(sg_buf, 0, sizeof(sg_buf));
+ else {
+ sg_origin = git__calloc(num_parents, sizeof(*sg_origin));
+ GIT_ERROR_CHECK_ALLOC(sg_origin);
+ }
+
+ for (i=0; i<num_parents; i++) {
+ git_commit *p;
+ int j, same;
+
+ if (sg_origin[i])
+ continue;
+
+ if ((error = git_commit_parent(&p, origin->commit, i)) < 0)
+ goto finish;
+ porigin = find_origin(blame, p, origin);
+
+ if (!porigin) {
+ /*
+ * We only have to decrement the parent's
+ * reference count when no porigin has
+ * been created, as otherwise the commit
+ * is assigned to the created object.
+ */
+ git_commit_free(p);
+ continue;
+ }
+ if (porigin->blob && origin->blob &&
+ !git_oid_cmp(git_blob_id(porigin->blob), git_blob_id(origin->blob))) {
+ error = pass_whole_blame(blame, origin, porigin);
+ origin_decref(porigin);
+ goto finish;
+ }
+ for (j = same = 0; j<i; j++)
+ if (sg_origin[j] &&
+ !git_oid_cmp(git_blob_id(sg_origin[j]->blob), git_blob_id(porigin->blob))) {
+ same = 1;
+ break;
+ }
+ if (!same)
+ sg_origin[i] = porigin;
+ else
+ origin_decref(porigin);
+ }
+
+ /* Standard blame */
+ for (i=0; i<num_parents; i++) {
+ git_blame__origin *porigin = sg_origin[i];
+ if (!porigin)
+ continue;
+ if (!origin->previous) {
+ origin_incref(porigin);
+ origin->previous = porigin;
+ }
+
+ if ((ret = pass_blame_to_parent(blame, origin, porigin)) != 0) {
+ if (ret < 0)
+ error = -1;
+
+ goto finish;
+ }
+ }
+
+ /* TODO: optionally find moves in parents' files */
+
+ /* TODO: optionally find copies in parents' files */
+
+finish:
+ for (i=0; i<num_parents; i++)
+ if (sg_origin[i])
+ origin_decref(sg_origin[i]);
+ if (sg_origin != sg_buf)
+ git__free(sg_origin);
+ return error;
+}
+
+/*
+ * If two blame entries that are next to each other came from
+ * contiguous lines in the same origin (i.e. <commit, path> pair),
+ * merge them together.
+ */
+static void coalesce(git_blame *blame)
+{
+ git_blame__entry *ent, *next;
+
+ for (ent=blame->ent; ent && (next = ent->next); ent = next) {
+ if (same_suspect(ent->suspect, next->suspect) &&
+ ent->guilty == next->guilty &&
+ ent->s_lno + ent->num_lines == next->s_lno)
+ {
+ ent->num_lines += next->num_lines;
+ ent->next = next->next;
+ if (ent->next)
+ ent->next->prev = ent;
+ origin_decref(next->suspect);
+ git__free(next);
+ ent->score = 0;
+ next = ent; /* again */
+ }
+ }
+}
+
+int git_blame__like_git(git_blame *blame, uint32_t opt)
+{
+ int error = 0;
+
+ while (true) {
+ git_blame__entry *ent;
+ git_blame__origin *suspect = NULL;
+
+ /* Find a suspect to break down */
+ for (ent = blame->ent; !suspect && ent; ent = ent->next)
+ if (!ent->guilty)
+ suspect = ent->suspect;
+ if (!suspect)
+ break;
+
+ /* We'll use this suspect later in the loop, so hold on to it for now. */
+ origin_incref(suspect);
+
+ if ((error = pass_blame(blame, suspect, opt)) < 0)
+ break;
+
+ /* Take responsibility for the remaining entries */
+ for (ent = blame->ent; ent; ent = ent->next) {
+ if (same_suspect(ent->suspect, suspect)) {
+ ent->guilty = true;
+ ent->is_boundary = !git_oid_cmp(
+ git_commit_id(suspect->commit),
+ &blame->options.oldest_commit);
+ }
+ }
+ origin_decref(suspect);
+ }
+
+ if (!error)
+ coalesce(blame);
+
+ return error;
+}
+
+void git_blame__free_entry(git_blame__entry *ent)
+{
+ if (!ent) return;
+ origin_decref(ent->suspect);
+ git__free(ent);
+}
diff --git a/src/libgit2/blame_git.h b/src/libgit2/blame_git.h
new file mode 100644
index 0000000..48b85a2
--- /dev/null
+++ b/src/libgit2/blame_git.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_blame_git__
+#define INCLUDE_blame_git__
+
+#include "common.h"
+
+#include "blame.h"
+
+int git_blame__get_origin(
+ git_blame__origin **out,
+ git_blame *sb,
+ git_commit *commit,
+ const char *path);
+void git_blame__free_entry(git_blame__entry *ent);
+int git_blame__like_git(git_blame *sb, uint32_t flags);
+
+#endif
diff --git a/src/libgit2/blob.c b/src/libgit2/blob.c
new file mode 100644
index 0000000..5cfd747
--- /dev/null
+++ b/src/libgit2/blob.c
@@ -0,0 +1,530 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "blob.h"
+
+#include "git2/common.h"
+#include "git2/object.h"
+#include "git2/repository.h"
+#include "git2/odb_backend.h"
+
+#include "buf.h"
+#include "filebuf.h"
+#include "filter.h"
+
+const void *git_blob_rawcontent(const git_blob *blob)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(blob, NULL);
+
+ if (blob->raw)
+ return blob->data.raw.data;
+ else
+ return git_odb_object_data(blob->data.odb);
+}
+
+git_object_size_t git_blob_rawsize(const git_blob *blob)
+{
+ GIT_ASSERT_ARG(blob);
+
+ if (blob->raw)
+ return blob->data.raw.size;
+ else
+ return (git_object_size_t)git_odb_object_size(blob->data.odb);
+}
+
+int git_blob__getbuf(git_str *buffer, git_blob *blob)
+{
+ git_object_size_t size = git_blob_rawsize(blob);
+
+ GIT_ERROR_CHECK_BLOBSIZE(size);
+ return git_str_set(buffer, git_blob_rawcontent(blob), (size_t)size);
+}
+
+void git_blob__free(void *_blob)
+{
+ git_blob *blob = (git_blob *) _blob;
+ if (!blob->raw)
+ git_odb_object_free(blob->data.odb);
+ git__free(blob);
+}
+
+int git_blob__parse_raw(void *_blob, const char *data, size_t size, git_oid_t oid_type)
+{
+ git_blob *blob = (git_blob *) _blob;
+
+ GIT_ASSERT_ARG(blob);
+ GIT_UNUSED(oid_type);
+
+ blob->raw = 1;
+ blob->data.raw.data = data;
+ blob->data.raw.size = size;
+ return 0;
+}
+
+int git_blob__parse(void *_blob, git_odb_object *odb_obj, git_oid_t oid_type)
+{
+ git_blob *blob = (git_blob *) _blob;
+
+ GIT_ASSERT_ARG(blob);
+ GIT_UNUSED(oid_type);
+
+ git_cached_obj_incref((git_cached_obj *)odb_obj);
+ blob->raw = 0;
+ blob->data.odb = odb_obj;
+ return 0;
+}
+
+int git_blob_create_from_buffer(
+ git_oid *id, git_repository *repo, const void *buffer, size_t len)
+{
+ int error;
+ git_odb *odb;
+ git_odb_stream *stream;
+
+ GIT_ASSERT_ARG(id);
+ GIT_ASSERT_ARG(repo);
+
+ if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||
+ (error = git_odb_open_wstream(&stream, odb, len, GIT_OBJECT_BLOB)) < 0)
+ return error;
+
+ if ((error = git_odb_stream_write(stream, buffer, len)) == 0)
+ error = git_odb_stream_finalize_write(id, stream);
+
+ git_odb_stream_free(stream);
+ return error;
+}
+
+static int write_file_stream(
+ git_oid *id, git_odb *odb, const char *path, git_object_size_t file_size)
+{
+ int fd, error;
+ char buffer[GIT_BUFSIZE_FILEIO];
+ git_odb_stream *stream = NULL;
+ ssize_t read_len = -1;
+ git_object_size_t written = 0;
+
+ if ((error = git_odb_open_wstream(
+ &stream, odb, file_size, GIT_OBJECT_BLOB)) < 0)
+ return error;
+
+ if ((fd = git_futils_open_ro(path)) < 0) {
+ git_odb_stream_free(stream);
+ return -1;
+ }
+
+ while (!error && (read_len = p_read(fd, buffer, sizeof(buffer))) > 0) {
+ error = git_odb_stream_write(stream, buffer, read_len);
+ written += read_len;
+ }
+
+ p_close(fd);
+
+ if (written != file_size || read_len < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to read file into stream");
+ error = -1;
+ }
+
+ if (!error)
+ error = git_odb_stream_finalize_write(id, stream);
+
+ git_odb_stream_free(stream);
+ return error;
+}
+
+static int write_file_filtered(
+ git_oid *id,
+ git_object_size_t *size,
+ git_odb *odb,
+ const char *full_path,
+ git_filter_list *fl,
+ git_repository* repo)
+{
+ int error;
+ git_str tgt = GIT_STR_INIT;
+
+ error = git_filter_list__apply_to_file(&tgt, fl, repo, full_path);
+
+ /* Write the file to disk if it was properly filtered */
+ if (!error) {
+ *size = tgt.size;
+
+ error = git_odb_write(id, odb, tgt.ptr, tgt.size, GIT_OBJECT_BLOB);
+ }
+
+ git_str_dispose(&tgt);
+ return error;
+}
+
+static int write_symlink(
+ git_oid *id, git_odb *odb, const char *path, size_t link_size)
+{
+ char *link_data;
+ ssize_t read_len;
+ int error;
+
+ link_data = git__malloc(link_size);
+ GIT_ERROR_CHECK_ALLOC(link_data);
+
+ read_len = p_readlink(path, link_data, link_size);
+ if (read_len != (ssize_t)link_size) {
+ git_error_set(GIT_ERROR_OS, "failed to create blob: cannot read symlink '%s'", path);
+ git__free(link_data);
+ return -1;
+ }
+
+ error = git_odb_write(id, odb, (void *)link_data, link_size, GIT_OBJECT_BLOB);
+ git__free(link_data);
+ return error;
+}
+
+int git_blob__create_from_paths(
+ git_oid *id,
+ struct stat *out_st,
+ git_repository *repo,
+ const char *content_path,
+ const char *hint_path,
+ mode_t hint_mode,
+ bool try_load_filters)
+{
+ int error;
+ struct stat st;
+ git_odb *odb = NULL;
+ git_object_size_t size;
+ mode_t mode;
+ git_str path = GIT_STR_INIT;
+
+ GIT_ASSERT_ARG(hint_path || !try_load_filters);
+
+ if (!content_path) {
+ if (git_repository_workdir_path(&path, repo, hint_path) < 0)
+ return -1;
+
+ content_path = path.ptr;
+ }
+
+ if ((error = git_fs_path_lstat(content_path, &st)) < 0 ||
+ (error = git_repository_odb(&odb, repo)) < 0)
+ goto done;
+
+ if (S_ISDIR(st.st_mode)) {
+ git_error_set(GIT_ERROR_ODB, "cannot create blob from '%s': it is a directory", content_path);
+ error = GIT_EDIRECTORY;
+ goto done;
+ }
+
+ if (out_st)
+ memcpy(out_st, &st, sizeof(st));
+
+ size = st.st_size;
+ mode = hint_mode ? hint_mode : st.st_mode;
+
+ if (S_ISLNK(mode)) {
+ error = write_symlink(id, odb, content_path, (size_t)size);
+ } else {
+ git_filter_list *fl = NULL;
+
+ if (try_load_filters)
+ /* Load the filters for writing this file to the ODB */
+ error = git_filter_list_load(
+ &fl, repo, NULL, hint_path,
+ GIT_FILTER_TO_ODB, GIT_FILTER_DEFAULT);
+
+ if (error < 0)
+ /* well, that didn't work */;
+ else if (fl == NULL)
+ /* No filters need to be applied to the document: we can stream
+ * directly from disk */
+ error = write_file_stream(id, odb, content_path, size);
+ else {
+ /* We need to apply one or more filters */
+ error = write_file_filtered(id, &size, odb, content_path, fl, repo);
+
+ git_filter_list_free(fl);
+ }
+
+ /*
+ * TODO: eventually support streaming filtered files, for files
+ * which are bigger than a given threshold. This is not a priority
+ * because applying a filter in streaming mode changes the final
+ * size of the blob, and without knowing its final size, the blob
+ * cannot be written in stream mode to the ODB.
+ *
+ * The plan is to do streaming writes to a tempfile on disk and then
+ * opening streaming that file to the ODB, using
+ * `write_file_stream`.
+ *
+ * CAREFULLY DESIGNED APIS YO
+ */
+ }
+
+done:
+ git_odb_free(odb);
+ git_str_dispose(&path);
+
+ return error;
+}
+
+int git_blob_create_from_workdir(
+ git_oid *id, git_repository *repo, const char *path)
+{
+ return git_blob__create_from_paths(id, NULL, repo, NULL, path, 0, true);
+}
+
+int git_blob_create_from_disk(
+ git_oid *id, git_repository *repo, const char *path)
+{
+ int error;
+ git_str full_path = GIT_STR_INIT;
+ const char *workdir, *hintpath = NULL;
+
+ if ((error = git_fs_path_prettify(&full_path, path, NULL)) < 0) {
+ git_str_dispose(&full_path);
+ return error;
+ }
+
+ workdir = git_repository_workdir(repo);
+
+ if (workdir && !git__prefixcmp(full_path.ptr, workdir))
+ hintpath = full_path.ptr + strlen(workdir);
+
+ error = git_blob__create_from_paths(
+ id, NULL, repo, git_str_cstr(&full_path), hintpath, 0, !!hintpath);
+
+ git_str_dispose(&full_path);
+ return error;
+}
+
+typedef struct {
+ git_writestream parent;
+ git_filebuf fbuf;
+ git_repository *repo;
+ char *hintpath;
+} blob_writestream;
+
+static int blob_writestream_close(git_writestream *_stream)
+{
+ blob_writestream *stream = (blob_writestream *) _stream;
+
+ git_filebuf_cleanup(&stream->fbuf);
+ return 0;
+}
+
+static void blob_writestream_free(git_writestream *_stream)
+{
+ blob_writestream *stream = (blob_writestream *) _stream;
+
+ git_filebuf_cleanup(&stream->fbuf);
+ git__free(stream->hintpath);
+ git__free(stream);
+}
+
+static int blob_writestream_write(git_writestream *_stream, const char *buffer, size_t len)
+{
+ blob_writestream *stream = (blob_writestream *) _stream;
+
+ return git_filebuf_write(&stream->fbuf, buffer, len);
+}
+
+int git_blob_create_from_stream(git_writestream **out, git_repository *repo, const char *hintpath)
+{
+ int error;
+ git_str path = GIT_STR_INIT;
+ blob_writestream *stream;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+
+ stream = git__calloc(1, sizeof(blob_writestream));
+ GIT_ERROR_CHECK_ALLOC(stream);
+
+ if (hintpath) {
+ stream->hintpath = git__strdup(hintpath);
+ GIT_ERROR_CHECK_ALLOC(stream->hintpath);
+ }
+
+ stream->repo = repo;
+ stream->parent.write = blob_writestream_write;
+ stream->parent.close = blob_writestream_close;
+ stream->parent.free = blob_writestream_free;
+
+ if ((error = git_repository__item_path(&path, repo, GIT_REPOSITORY_ITEM_OBJECTS)) < 0
+ || (error = git_str_joinpath(&path, path.ptr, "streamed")) < 0)
+ goto cleanup;
+
+ if ((error = git_filebuf_open_withsize(&stream->fbuf, git_str_cstr(&path), GIT_FILEBUF_TEMPORARY,
+ 0666, 2 * 1024 * 1024)) < 0)
+ goto cleanup;
+
+ *out = (git_writestream *) stream;
+
+cleanup:
+ if (error < 0)
+ blob_writestream_free((git_writestream *) stream);
+
+ git_str_dispose(&path);
+ return error;
+}
+
+int git_blob_create_from_stream_commit(git_oid *out, git_writestream *_stream)
+{
+ int error;
+ blob_writestream *stream = (blob_writestream *) _stream;
+
+ /*
+ * We can make this more officient by avoiding writing to
+ * disk, but for now let's re-use the helper functions we
+ * have.
+ */
+ if ((error = git_filebuf_flush(&stream->fbuf)) < 0)
+ goto cleanup;
+
+ error = git_blob__create_from_paths(out, NULL, stream->repo, stream->fbuf.path_lock,
+ stream->hintpath, 0, !!stream->hintpath);
+
+cleanup:
+ blob_writestream_free(_stream);
+ return error;
+
+}
+
+int git_blob_is_binary(const git_blob *blob)
+{
+ git_str content = GIT_STR_INIT;
+ git_object_size_t size;
+
+ GIT_ASSERT_ARG(blob);
+
+ size = git_blob_rawsize(blob);
+
+ git_str_attach_notowned(&content, git_blob_rawcontent(blob),
+ (size_t)min(size, GIT_FILTER_BYTES_TO_CHECK_NUL));
+ return git_str_is_binary(&content);
+}
+
+int git_blob_data_is_binary(const char *str, size_t len)
+{
+ git_str content = GIT_STR_INIT;
+
+ git_str_attach_notowned(&content, str, len);
+
+ return git_str_is_binary(&content);
+}
+
+int git_blob_filter_options_init(
+ git_blob_filter_options *opts,
+ unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(opts, version,
+ git_blob_filter_options, GIT_BLOB_FILTER_OPTIONS_INIT);
+ return 0;
+}
+
+int git_blob_filter(
+ git_buf *out,
+ git_blob *blob,
+ const char *path,
+ git_blob_filter_options *given_opts)
+{
+ git_blob_filter_options opts = GIT_BLOB_FILTER_OPTIONS_INIT;
+ git_filter_options filter_opts = GIT_FILTER_OPTIONS_INIT;
+ git_filter_list *fl = NULL;
+ int error = 0;
+
+ GIT_ASSERT_ARG(blob);
+ GIT_ASSERT_ARG(path);
+ GIT_ASSERT_ARG(out);
+
+ GIT_ERROR_CHECK_VERSION(
+ given_opts, GIT_BLOB_FILTER_OPTIONS_VERSION, "git_blob_filter_options");
+
+ if (given_opts != NULL)
+ memcpy(&opts, given_opts, sizeof(git_blob_filter_options));
+
+ if ((opts.flags & GIT_BLOB_FILTER_CHECK_FOR_BINARY) != 0 &&
+ git_blob_is_binary(blob))
+ return 0;
+
+ if ((opts.flags & GIT_BLOB_FILTER_NO_SYSTEM_ATTRIBUTES) != 0)
+ filter_opts.flags |= GIT_FILTER_NO_SYSTEM_ATTRIBUTES;
+
+ if ((opts.flags & GIT_BLOB_FILTER_ATTRIBUTES_FROM_HEAD) != 0)
+ filter_opts.flags |= GIT_FILTER_ATTRIBUTES_FROM_HEAD;
+
+ if ((opts.flags & GIT_BLOB_FILTER_ATTRIBUTES_FROM_COMMIT) != 0) {
+ filter_opts.flags |= GIT_FILTER_ATTRIBUTES_FROM_COMMIT;
+
+#ifndef GIT_DEPRECATE_HARD
+ if (opts.commit_id)
+ git_oid_cpy(&filter_opts.attr_commit_id, opts.commit_id);
+ else
+#endif
+ git_oid_cpy(&filter_opts.attr_commit_id, &opts.attr_commit_id);
+ }
+
+ if (!(error = git_filter_list_load_ext(
+ &fl, git_blob_owner(blob), blob, path,
+ GIT_FILTER_TO_WORKTREE, &filter_opts))) {
+
+ error = git_filter_list_apply_to_blob(out, fl, blob);
+
+ git_filter_list_free(fl);
+ }
+
+ return error;
+}
+
+/* Deprecated functions */
+
+#ifndef GIT_DEPRECATE_HARD
+int git_blob_create_frombuffer(
+ git_oid *id, git_repository *repo, const void *buffer, size_t len)
+{
+ return git_blob_create_from_buffer(id, repo, buffer, len);
+}
+
+int git_blob_create_fromworkdir(git_oid *id, git_repository *repo, const char *relative_path)
+{
+ return git_blob_create_from_workdir(id, repo, relative_path);
+}
+
+int git_blob_create_fromdisk(git_oid *id, git_repository *repo, const char *path)
+{
+ return git_blob_create_from_disk(id, repo, path);
+}
+
+int git_blob_create_fromstream(
+ git_writestream **out,
+ git_repository *repo,
+ const char *hintpath)
+{
+ return git_blob_create_from_stream(out, repo, hintpath);
+}
+
+int git_blob_create_fromstream_commit(
+ git_oid *out,
+ git_writestream *stream)
+{
+ return git_blob_create_from_stream_commit(out, stream);
+}
+
+int git_blob_filtered_content(
+ git_buf *out,
+ git_blob *blob,
+ const char *path,
+ int check_for_binary_data)
+{
+ git_blob_filter_options opts = GIT_BLOB_FILTER_OPTIONS_INIT;
+
+ if (check_for_binary_data)
+ opts.flags |= GIT_BLOB_FILTER_CHECK_FOR_BINARY;
+ else
+ opts.flags &= ~GIT_BLOB_FILTER_CHECK_FOR_BINARY;
+
+ return git_blob_filter(out, blob, path, &opts);
+}
+#endif
diff --git a/src/libgit2/blob.h b/src/libgit2/blob.h
new file mode 100644
index 0000000..d6c9dd9
--- /dev/null
+++ b/src/libgit2/blob.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_blob_h__
+#define INCLUDE_blob_h__
+
+#include "common.h"
+
+#include "git2/blob.h"
+#include "repository.h"
+#include "odb.h"
+#include "futils.h"
+
+struct git_blob {
+ git_object object;
+
+ union {
+ git_odb_object *odb;
+ struct {
+ const char *data;
+ git_object_size_t size;
+ } raw;
+ } data;
+ unsigned int raw:1;
+};
+
+#define GIT_ERROR_CHECK_BLOBSIZE(n) \
+ do { \
+ if (!git__is_sizet(n)) { \
+ git_error_set(GIT_ERROR_NOMEMORY, "blob contents too large to fit in memory"); \
+ return -1; \
+ } \
+ } while(0)
+
+void git_blob__free(void *blob);
+int git_blob__parse(void *blob, git_odb_object *obj, git_oid_t oid_type);
+int git_blob__parse_raw(void *blob, const char *data, size_t size, git_oid_t oid_type);
+int git_blob__getbuf(git_str *buffer, git_blob *blob);
+
+extern int git_blob__create_from_paths(
+ git_oid *out_oid,
+ struct stat *out_st,
+ git_repository *repo,
+ const char *full_path,
+ const char *hint_path,
+ mode_t hint_mode,
+ bool apply_filters);
+
+#endif
diff --git a/src/libgit2/branch.c b/src/libgit2/branch.c
new file mode 100644
index 0000000..9a31c9c
--- /dev/null
+++ b/src/libgit2/branch.c
@@ -0,0 +1,823 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "branch.h"
+
+#include "buf.h"
+#include "commit.h"
+#include "tag.h"
+#include "config.h"
+#include "refspec.h"
+#include "refs.h"
+#include "remote.h"
+#include "annotated_commit.h"
+#include "worktree.h"
+
+#include "git2/branch.h"
+
+static int retrieve_branch_reference(
+ git_reference **branch_reference_out,
+ git_repository *repo,
+ const char *branch_name,
+ bool is_remote)
+{
+ git_reference *branch = NULL;
+ int error = 0;
+ char *prefix;
+ git_str ref_name = GIT_STR_INIT;
+
+ prefix = is_remote ? GIT_REFS_REMOTES_DIR : GIT_REFS_HEADS_DIR;
+
+ if ((error = git_str_joinpath(&ref_name, prefix, branch_name)) < 0)
+ /* OOM */;
+ else if ((error = git_reference_lookup(&branch, repo, ref_name.ptr)) < 0)
+ git_error_set(
+ GIT_ERROR_REFERENCE, "cannot locate %s branch '%s'",
+ is_remote ? "remote-tracking" : "local", branch_name);
+
+ *branch_reference_out = branch; /* will be NULL on error */
+
+ git_str_dispose(&ref_name);
+ return error;
+}
+
+static int not_a_local_branch(const char *reference_name)
+{
+ git_error_set(
+ GIT_ERROR_INVALID,
+ "reference '%s' is not a local branch.", reference_name);
+ return -1;
+}
+
+static bool branch_name_is_valid(const char *branch_name)
+{
+ /*
+ * Discourage branch name starting with dash,
+ * https://github.com/git/git/commit/6348624010888b
+ * and discourage HEAD as branch name,
+ * https://github.com/git/git/commit/a625b092cc5994
+ */
+ return branch_name[0] != '-' && git__strcmp(branch_name, "HEAD");
+}
+
+static int create_branch(
+ git_reference **ref_out,
+ git_repository *repository,
+ const char *branch_name,
+ const git_commit *commit,
+ const char *from,
+ int force)
+{
+ int is_unmovable_head = 0;
+ git_reference *branch = NULL;
+ git_str canonical_branch_name = GIT_STR_INIT,
+ log_message = GIT_STR_INIT;
+ int error = -1;
+ int bare = git_repository_is_bare(repository);
+
+ GIT_ASSERT_ARG(branch_name);
+ GIT_ASSERT_ARG(commit);
+ GIT_ASSERT_ARG(ref_out);
+ GIT_ASSERT_ARG(git_commit_owner(commit) == repository);
+
+ if (!branch_name_is_valid(branch_name)) {
+ git_error_set(GIT_ERROR_REFERENCE, "'%s' is not a valid branch name", branch_name);
+ error = -1;
+ goto cleanup;
+ }
+
+ if (force && !bare && git_branch_lookup(&branch, repository, branch_name, GIT_BRANCH_LOCAL) == 0) {
+ error = git_branch_is_head(branch);
+ git_reference_free(branch);
+ branch = NULL;
+
+ if (error < 0)
+ goto cleanup;
+
+ is_unmovable_head = error;
+ }
+
+ if (is_unmovable_head && force) {
+ git_error_set(GIT_ERROR_REFERENCE, "cannot force update branch '%s' as it is "
+ "the current HEAD of the repository.", branch_name);
+ error = -1;
+ goto cleanup;
+ }
+
+ if (git_str_joinpath(&canonical_branch_name, GIT_REFS_HEADS_DIR, branch_name) < 0)
+ goto cleanup;
+
+ if (git_str_printf(&log_message, "branch: Created from %s", from) < 0)
+ goto cleanup;
+
+ error = git_reference_create(&branch, repository,
+ git_str_cstr(&canonical_branch_name), git_commit_id(commit), force,
+ git_str_cstr(&log_message));
+
+ if (!error)
+ *ref_out = branch;
+
+cleanup:
+ git_str_dispose(&canonical_branch_name);
+ git_str_dispose(&log_message);
+ return error;
+}
+
+int git_branch_create(
+ git_reference **ref_out,
+ git_repository *repository,
+ const char *branch_name,
+ const git_commit *commit,
+ int force)
+{
+ char commit_id[GIT_OID_MAX_HEXSIZE + 1];
+
+ git_oid_tostr(commit_id, GIT_OID_MAX_HEXSIZE + 1, git_commit_id(commit));
+ return create_branch(ref_out, repository, branch_name, commit, commit_id, force);
+}
+
+int git_branch_create_from_annotated(
+ git_reference **ref_out,
+ git_repository *repository,
+ const char *branch_name,
+ const git_annotated_commit *commit,
+ int force)
+{
+ return create_branch(ref_out,
+ repository, branch_name, commit->commit, commit->description, force);
+}
+
+static int branch_is_checked_out(git_repository *worktree, void *payload)
+{
+ git_reference *branch = (git_reference *) payload;
+ git_reference *head = NULL;
+ int error;
+
+ if (git_repository_is_bare(worktree))
+ return 0;
+
+ if ((error = git_reference_lookup(&head, worktree, GIT_HEAD_FILE)) < 0) {
+ if (error == GIT_ENOTFOUND)
+ error = 0;
+ goto out;
+ }
+
+ if (git_reference_type(head) != GIT_REFERENCE_SYMBOLIC)
+ goto out;
+
+ error = !git__strcmp(head->target.symbolic, branch->name);
+
+out:
+ git_reference_free(head);
+ return error;
+}
+
+int git_branch_is_checked_out(const git_reference *branch)
+{
+ GIT_ASSERT_ARG(branch);
+
+ if (!git_reference_is_branch(branch))
+ return 0;
+ return git_repository_foreach_worktree(git_reference_owner(branch),
+ branch_is_checked_out, (void *)branch) == 1;
+}
+
+int git_branch_delete(git_reference *branch)
+{
+ int is_head;
+ git_str config_section = GIT_STR_INIT;
+ int error = -1;
+
+ GIT_ASSERT_ARG(branch);
+
+ if (!git_reference_is_branch(branch) && !git_reference_is_remote(branch)) {
+ git_error_set(GIT_ERROR_INVALID, "reference '%s' is not a valid branch.",
+ git_reference_name(branch));
+ return GIT_ENOTFOUND;
+ }
+
+ if ((is_head = git_branch_is_head(branch)) < 0)
+ return is_head;
+
+ if (is_head) {
+ git_error_set(GIT_ERROR_REFERENCE, "cannot delete branch '%s' as it is "
+ "the current HEAD of the repository.", git_reference_name(branch));
+ return -1;
+ }
+
+ if (git_reference_is_branch(branch) && git_branch_is_checked_out(branch)) {
+ git_error_set(GIT_ERROR_REFERENCE, "Cannot delete branch '%s' as it is "
+ "the current HEAD of a linked repository.", git_reference_name(branch));
+ return -1;
+ }
+
+ if (git_str_join(&config_section, '.', "branch",
+ git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR)) < 0)
+ goto on_error;
+
+ if (git_config_rename_section(
+ git_reference_owner(branch), git_str_cstr(&config_section), NULL) < 0)
+ goto on_error;
+
+ error = git_reference_delete(branch);
+
+on_error:
+ git_str_dispose(&config_section);
+ return error;
+}
+
+typedef struct {
+ git_reference_iterator *iter;
+ unsigned int flags;
+} branch_iter;
+
+int git_branch_next(git_reference **out, git_branch_t *out_type, git_branch_iterator *_iter)
+{
+ branch_iter *iter = (branch_iter *) _iter;
+ git_reference *ref;
+ int error;
+
+ while ((error = git_reference_next(&ref, iter->iter)) == 0) {
+ if ((iter->flags & GIT_BRANCH_LOCAL) &&
+ !git__prefixcmp(ref->name, GIT_REFS_HEADS_DIR)) {
+ *out = ref;
+ *out_type = GIT_BRANCH_LOCAL;
+
+ return 0;
+ } else if ((iter->flags & GIT_BRANCH_REMOTE) &&
+ !git__prefixcmp(ref->name, GIT_REFS_REMOTES_DIR)) {
+ *out = ref;
+ *out_type = GIT_BRANCH_REMOTE;
+
+ return 0;
+ } else {
+ git_reference_free(ref);
+ }
+ }
+
+ return error;
+}
+
+int git_branch_iterator_new(
+ git_branch_iterator **out,
+ git_repository *repo,
+ git_branch_t list_flags)
+{
+ branch_iter *iter;
+
+ iter = git__calloc(1, sizeof(branch_iter));
+ GIT_ERROR_CHECK_ALLOC(iter);
+
+ iter->flags = list_flags;
+
+ if (git_reference_iterator_new(&iter->iter, repo) < 0) {
+ git__free(iter);
+ return -1;
+ }
+
+ *out = (git_branch_iterator *) iter;
+
+ return 0;
+}
+
+void git_branch_iterator_free(git_branch_iterator *_iter)
+{
+ branch_iter *iter = (branch_iter *) _iter;
+
+ if (iter == NULL)
+ return;
+
+ git_reference_iterator_free(iter->iter);
+ git__free(iter);
+}
+
+int git_branch_move(
+ git_reference **out,
+ git_reference *branch,
+ const char *new_branch_name,
+ int force)
+{
+ git_str new_reference_name = GIT_STR_INIT,
+ old_config_section = GIT_STR_INIT,
+ new_config_section = GIT_STR_INIT,
+ log_message = GIT_STR_INIT;
+ int error;
+
+ GIT_ASSERT_ARG(branch);
+ GIT_ASSERT_ARG(new_branch_name);
+
+ if (!git_reference_is_branch(branch))
+ return not_a_local_branch(git_reference_name(branch));
+
+ if ((error = git_str_joinpath(&new_reference_name, GIT_REFS_HEADS_DIR, new_branch_name)) < 0)
+ goto done;
+
+ if ((error = git_str_printf(&log_message, "branch: renamed %s to %s",
+ git_reference_name(branch), git_str_cstr(&new_reference_name))) < 0)
+ goto done;
+
+ /* first update ref then config so failure won't trash config */
+
+ error = git_reference_rename(
+ out, branch, git_str_cstr(&new_reference_name), force,
+ git_str_cstr(&log_message));
+ if (error < 0)
+ goto done;
+
+ git_str_join(&old_config_section, '.', "branch",
+ git_reference_name(branch) + strlen(GIT_REFS_HEADS_DIR));
+ git_str_join(&new_config_section, '.', "branch", new_branch_name);
+
+ error = git_config_rename_section(
+ git_reference_owner(branch),
+ git_str_cstr(&old_config_section),
+ git_str_cstr(&new_config_section));
+
+done:
+ git_str_dispose(&new_reference_name);
+ git_str_dispose(&old_config_section);
+ git_str_dispose(&new_config_section);
+ git_str_dispose(&log_message);
+
+ return error;
+}
+
+int git_branch_lookup(
+ git_reference **ref_out,
+ git_repository *repo,
+ const char *branch_name,
+ git_branch_t branch_type)
+{
+ int error = -1;
+
+ GIT_ASSERT_ARG(ref_out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(branch_name);
+
+ switch (branch_type) {
+ case GIT_BRANCH_LOCAL:
+ case GIT_BRANCH_REMOTE:
+ error = retrieve_branch_reference(ref_out, repo, branch_name, branch_type == GIT_BRANCH_REMOTE);
+ break;
+ case GIT_BRANCH_ALL:
+ error = retrieve_branch_reference(ref_out, repo, branch_name, false);
+ if (error == GIT_ENOTFOUND)
+ error = retrieve_branch_reference(ref_out, repo, branch_name, true);
+ break;
+ default:
+ GIT_ASSERT(false);
+ }
+ return error;
+}
+
+int git_branch_name(
+ const char **out,
+ const git_reference *ref)
+{
+ const char *branch_name;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(ref);
+
+ branch_name = ref->name;
+
+ if (git_reference_is_branch(ref)) {
+ branch_name += strlen(GIT_REFS_HEADS_DIR);
+ } else if (git_reference_is_remote(ref)) {
+ branch_name += strlen(GIT_REFS_REMOTES_DIR);
+ } else {
+ git_error_set(GIT_ERROR_INVALID,
+ "reference '%s' is neither a local nor a remote branch.", ref->name);
+ return -1;
+ }
+ *out = branch_name;
+ return 0;
+}
+
+static int retrieve_upstream_configuration(
+ git_str *out,
+ const git_config *config,
+ const char *canonical_branch_name,
+ const char *format)
+{
+ git_str buf = GIT_STR_INIT;
+ int error;
+
+ if (git_str_printf(&buf, format,
+ canonical_branch_name + strlen(GIT_REFS_HEADS_DIR)) < 0)
+ return -1;
+
+ error = git_config__get_string_buf(out, config, git_str_cstr(&buf));
+ git_str_dispose(&buf);
+ return error;
+}
+
+int git_branch_upstream_name(
+ git_buf *out,
+ git_repository *repo,
+ const char *refname)
+{
+ GIT_BUF_WRAP_PRIVATE(out, git_branch__upstream_name, repo, refname);
+}
+
+int git_branch__upstream_name(
+ git_str *out,
+ git_repository *repo,
+ const char *refname)
+{
+ git_str remote_name = GIT_STR_INIT;
+ git_str merge_name = GIT_STR_INIT;
+ git_str buf = GIT_STR_INIT;
+ int error = -1;
+ git_remote *remote = NULL;
+ const git_refspec *refspec;
+ git_config *config;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(refname);
+
+ if (!git_reference__is_branch(refname))
+ return not_a_local_branch(refname);
+
+ if ((error = git_repository_config_snapshot(&config, repo)) < 0)
+ return error;
+
+ if ((error = retrieve_upstream_configuration(
+ &remote_name, config, refname, "branch.%s.remote")) < 0)
+ goto cleanup;
+
+ if ((error = retrieve_upstream_configuration(
+ &merge_name, config, refname, "branch.%s.merge")) < 0)
+ goto cleanup;
+
+ if (git_str_len(&remote_name) == 0 || git_str_len(&merge_name) == 0) {
+ git_error_set(GIT_ERROR_REFERENCE,
+ "branch '%s' does not have an upstream", refname);
+ error = GIT_ENOTFOUND;
+ goto cleanup;
+ }
+
+ if (strcmp(".", git_str_cstr(&remote_name)) != 0) {
+ if ((error = git_remote_lookup(&remote, repo, git_str_cstr(&remote_name))) < 0)
+ goto cleanup;
+
+ refspec = git_remote__matching_refspec(remote, git_str_cstr(&merge_name));
+ if (!refspec) {
+ error = GIT_ENOTFOUND;
+ goto cleanup;
+ }
+
+ if (git_refspec__transform(&buf, refspec, git_str_cstr(&merge_name)) < 0)
+ goto cleanup;
+ } else
+ if (git_str_set(&buf, git_str_cstr(&merge_name), git_str_len(&merge_name)) < 0)
+ goto cleanup;
+
+ git_str_swap(out, &buf);
+
+cleanup:
+ git_config_free(config);
+ git_remote_free(remote);
+ git_str_dispose(&remote_name);
+ git_str_dispose(&merge_name);
+ git_str_dispose(&buf);
+ return error;
+}
+
+static int git_branch_upstream_with_format(
+ git_str *out,
+ git_repository *repo,
+ const char *refname,
+ const char *format,
+ const char *format_name)
+{
+ git_config *cfg;
+ int error;
+
+ if (!git_reference__is_branch(refname))
+ return not_a_local_branch(refname);
+
+ if ((error = git_repository_config__weakptr(&cfg, repo)) < 0 ||
+ (error = retrieve_upstream_configuration(out, cfg, refname, format)) < 0)
+ return error;
+
+ if (git_str_len(out) == 0) {
+ git_error_set(GIT_ERROR_REFERENCE, "branch '%s' does not have an upstream %s", refname, format_name);
+ error = GIT_ENOTFOUND;
+ }
+
+ return error;
+}
+
+int git_branch_upstream_remote(
+ git_buf *out,
+ git_repository *repo,
+ const char *refname)
+{
+ GIT_BUF_WRAP_PRIVATE(out, git_branch__upstream_remote, repo, refname);
+}
+
+int git_branch__upstream_remote(
+ git_str *out,
+ git_repository *repo,
+ const char *refname)
+{
+ return git_branch_upstream_with_format(out, repo, refname, "branch.%s.remote", "remote");
+}
+
+int git_branch_upstream_merge(
+ git_buf *out,
+ git_repository *repo,
+ const char *refname)
+{
+ GIT_BUF_WRAP_PRIVATE(out, git_branch__upstream_merge, repo, refname);
+}
+
+int git_branch__upstream_merge(
+ git_str *out,
+ git_repository *repo,
+ const char *refname)
+{
+ return git_branch_upstream_with_format(out, repo, refname, "branch.%s.merge", "merge");
+}
+
+int git_branch_remote_name(
+ git_buf *out,
+ git_repository *repo,
+ const char *refname)
+{
+ GIT_BUF_WRAP_PRIVATE(out, git_branch__remote_name, repo, refname);
+}
+
+int git_branch__remote_name(
+ git_str *out,
+ git_repository *repo,
+ const char *refname)
+{
+ git_strarray remote_list = {0};
+ size_t i;
+ git_remote *remote;
+ const git_refspec *fetchspec;
+ int error = 0;
+ char *remote_name = NULL;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(refname);
+
+ /* Verify that this is a remote branch */
+ if (!git_reference__is_remote(refname)) {
+ git_error_set(GIT_ERROR_INVALID, "reference '%s' is not a remote branch.",
+ refname);
+ error = GIT_ERROR;
+ goto cleanup;
+ }
+
+ /* Get the remotes */
+ if ((error = git_remote_list(&remote_list, repo)) < 0)
+ goto cleanup;
+
+ /* Find matching remotes */
+ for (i = 0; i < remote_list.count; i++) {
+ if ((error = git_remote_lookup(&remote, repo, remote_list.strings[i])) < 0)
+ continue;
+
+ fetchspec = git_remote__matching_dst_refspec(remote, refname);
+ if (fetchspec) {
+ /* If we have not already set out yet, then set
+ * it to the matching remote name. Otherwise
+ * multiple remotes match this reference, and it
+ * is ambiguous. */
+ if (!remote_name) {
+ remote_name = remote_list.strings[i];
+ } else {
+ git_remote_free(remote);
+
+ git_error_set(GIT_ERROR_REFERENCE,
+ "reference '%s' is ambiguous", refname);
+ error = GIT_EAMBIGUOUS;
+ goto cleanup;
+ }
+ }
+
+ git_remote_free(remote);
+ }
+
+ if (remote_name) {
+ git_str_clear(out);
+ error = git_str_puts(out, remote_name);
+ } else {
+ git_error_set(GIT_ERROR_REFERENCE,
+ "could not determine remote for '%s'", refname);
+ error = GIT_ENOTFOUND;
+ }
+
+cleanup:
+ if (error < 0)
+ git_str_dispose(out);
+
+ git_strarray_dispose(&remote_list);
+ return error;
+}
+
+int git_branch_upstream(
+ git_reference **tracking_out,
+ const git_reference *branch)
+{
+ int error;
+ git_str tracking_name = GIT_STR_INIT;
+
+ if ((error = git_branch__upstream_name(&tracking_name,
+ git_reference_owner(branch), git_reference_name(branch))) < 0)
+ return error;
+
+ error = git_reference_lookup(
+ tracking_out,
+ git_reference_owner(branch),
+ git_str_cstr(&tracking_name));
+
+ git_str_dispose(&tracking_name);
+ return error;
+}
+
+static int unset_upstream(git_config *config, const char *shortname)
+{
+ git_str buf = GIT_STR_INIT;
+
+ if (git_str_printf(&buf, "branch.%s.remote", shortname) < 0)
+ return -1;
+
+ if (git_config_delete_entry(config, git_str_cstr(&buf)) < 0)
+ goto on_error;
+
+ git_str_clear(&buf);
+ if (git_str_printf(&buf, "branch.%s.merge", shortname) < 0)
+ goto on_error;
+
+ if (git_config_delete_entry(config, git_str_cstr(&buf)) < 0)
+ goto on_error;
+
+ git_str_dispose(&buf);
+ return 0;
+
+on_error:
+ git_str_dispose(&buf);
+ return -1;
+}
+
+int git_branch_set_upstream(git_reference *branch, const char *branch_name)
+{
+ git_str key = GIT_STR_INIT, remote_name = GIT_STR_INIT, merge_refspec = GIT_STR_INIT;
+ git_reference *upstream;
+ git_repository *repo;
+ git_remote *remote = NULL;
+ git_config *config;
+ const char *refname, *shortname;
+ int local, error;
+ const git_refspec *fetchspec;
+
+ refname = git_reference_name(branch);
+ if (!git_reference__is_branch(refname))
+ return not_a_local_branch(refname);
+
+ if (git_repository_config__weakptr(&config, git_reference_owner(branch)) < 0)
+ return -1;
+
+ shortname = refname + strlen(GIT_REFS_HEADS_DIR);
+
+ /* We're unsetting, delegate and bail-out */
+ if (branch_name == NULL)
+ return unset_upstream(config, shortname);
+
+ repo = git_reference_owner(branch);
+
+ /* First we need to resolve name to a branch */
+ if (git_branch_lookup(&upstream, repo, branch_name, GIT_BRANCH_LOCAL) == 0)
+ local = 1;
+ else if (git_branch_lookup(&upstream, repo, branch_name, GIT_BRANCH_REMOTE) == 0)
+ local = 0;
+ else {
+ git_error_set(GIT_ERROR_REFERENCE,
+ "cannot set upstream for branch '%s'", shortname);
+ return GIT_ENOTFOUND;
+ }
+
+ /*
+ * If it's a local-tracking branch, its remote is "." (as "the local
+ * repository"), and the branch name is simply the refname.
+ * Otherwise we need to figure out what the remote-tracking branch's
+ * name on the remote is and use that.
+ */
+ if (local)
+ error = git_str_puts(&remote_name, ".");
+ else
+ error = git_branch__remote_name(&remote_name, repo, git_reference_name(upstream));
+
+ if (error < 0)
+ goto on_error;
+
+ /* Update the upstream branch config with the new name */
+ if (git_str_printf(&key, "branch.%s.remote", shortname) < 0)
+ goto on_error;
+
+ if (git_config_set_string(config, git_str_cstr(&key), git_str_cstr(&remote_name)) < 0)
+ goto on_error;
+
+ if (local) {
+ /* A local branch uses the upstream refname directly */
+ if (git_str_puts(&merge_refspec, git_reference_name(upstream)) < 0)
+ goto on_error;
+ } else {
+ /* We transform the upstream branch name according to the remote's refspecs */
+ if (git_remote_lookup(&remote, repo, git_str_cstr(&remote_name)) < 0)
+ goto on_error;
+
+ fetchspec = git_remote__matching_dst_refspec(remote, git_reference_name(upstream));
+ if (!fetchspec || git_refspec__rtransform(&merge_refspec, fetchspec, git_reference_name(upstream)) < 0)
+ goto on_error;
+
+ git_remote_free(remote);
+ remote = NULL;
+ }
+
+ /* Update the merge branch config with the refspec */
+ git_str_clear(&key);
+ if (git_str_printf(&key, "branch.%s.merge", shortname) < 0)
+ goto on_error;
+
+ if (git_config_set_string(config, git_str_cstr(&key), git_str_cstr(&merge_refspec)) < 0)
+ goto on_error;
+
+ git_reference_free(upstream);
+ git_str_dispose(&key);
+ git_str_dispose(&remote_name);
+ git_str_dispose(&merge_refspec);
+
+ return 0;
+
+on_error:
+ git_reference_free(upstream);
+ git_str_dispose(&key);
+ git_str_dispose(&remote_name);
+ git_str_dispose(&merge_refspec);
+ git_remote_free(remote);
+
+ return -1;
+}
+
+int git_branch_is_head(
+ const git_reference *branch)
+{
+ git_reference *head;
+ bool is_same = false;
+ int error;
+
+ GIT_ASSERT_ARG(branch);
+
+ if (!git_reference_is_branch(branch))
+ return false;
+
+ error = git_repository_head(&head, git_reference_owner(branch));
+
+ if (error == GIT_EUNBORNBRANCH || error == GIT_ENOTFOUND)
+ return false;
+
+ if (error < 0)
+ return -1;
+
+ is_same = strcmp(
+ git_reference_name(branch),
+ git_reference_name(head)) == 0;
+
+ git_reference_free(head);
+
+ return is_same;
+}
+
+int git_branch_name_is_valid(int *valid, const char *name)
+{
+ git_str ref_name = GIT_STR_INIT;
+ int error = 0;
+
+ GIT_ASSERT(valid);
+
+ *valid = 0;
+
+ if (!name || !branch_name_is_valid(name))
+ goto done;
+
+ if ((error = git_str_puts(&ref_name, GIT_REFS_HEADS_DIR)) < 0 ||
+ (error = git_str_puts(&ref_name, name)) < 0)
+ goto done;
+
+ error = git_reference_name_is_valid(valid, ref_name.ptr);
+
+done:
+ git_str_dispose(&ref_name);
+ return error;
+}
diff --git a/src/libgit2/branch.h b/src/libgit2/branch.h
new file mode 100644
index 0000000..b4db42a
--- /dev/null
+++ b/src/libgit2/branch.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_branch_h__
+#define INCLUDE_branch_h__
+
+#include "common.h"
+
+#include "str.h"
+
+int git_branch__remote_name(
+ git_str *out,
+ git_repository *repo,
+ const char *refname);
+int git_branch__upstream_remote(
+ git_str *out,
+ git_repository *repo,
+ const char *refname);
+int git_branch__upstream_merge(
+ git_str *out,
+ git_repository *repo,
+ const char *refname);
+int git_branch__upstream_name(
+ git_str *tracking_name,
+ git_repository *repo,
+ const char *canonical_branch_name);
+
+#endif
diff --git a/src/libgit2/buf.c b/src/libgit2/buf.c
new file mode 100644
index 0000000..652f5dd
--- /dev/null
+++ b/src/libgit2/buf.c
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "buf.h"
+#include "common.h"
+
+int git_buf_sanitize(git_buf *buf)
+{
+ GIT_ASSERT_ARG(buf);
+
+ if (buf->reserved > 0)
+ buf->ptr[0] = '\0';
+ else
+ buf->ptr = git_str__initstr;
+
+ buf->size = 0;
+ return 0;
+}
+
+int git_buf_tostr(git_str *out, git_buf *buf)
+{
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(buf);
+
+ if (git_buf_sanitize(buf) < 0)
+ return -1;
+
+ out->ptr = buf->ptr;
+ out->asize = buf->reserved;
+ out->size = buf->size;
+
+ buf->ptr = git_str__initstr;
+ buf->reserved = 0;
+ buf->size = 0;
+
+ return 0;
+}
+
+int git_buf_fromstr(git_buf *out, git_str *str)
+{
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(str);
+
+ out->ptr = str->ptr;
+ out->reserved = str->asize;
+ out->size = str->size;
+
+ str->ptr = git_str__initstr;
+ str->asize = 0;
+ str->size = 0;
+
+ return 0;
+}
+
+void git_buf_dispose(git_buf *buf)
+{
+ if (!buf)
+ return;
+
+ if (buf->ptr != git_str__initstr)
+ git__free(buf->ptr);
+
+ buf->ptr = git_str__initstr;
+ buf->reserved = 0;
+ buf->size = 0;
+}
+
+#ifndef GIT_DEPRECATE_HARD
+int git_buf_grow(git_buf *buffer, size_t target_size)
+{
+ char *newptr;
+
+ if (buffer->reserved >= target_size)
+ return 0;
+
+ if (buffer->ptr == git_str__initstr)
+ newptr = git__malloc(target_size);
+ else
+ newptr = git__realloc(buffer->ptr, target_size);
+
+ if (!newptr)
+ return -1;
+
+ buffer->ptr = newptr;
+ buffer->reserved = target_size;
+ return 0;
+}
+
+int git_buf_set(git_buf *buffer, const void *data, size_t datalen)
+{
+ size_t alloclen;
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, datalen, 1);
+
+ if (git_buf_grow(buffer, alloclen) < 0)
+ return -1;
+
+ memmove(buffer->ptr, data, datalen);
+ buffer->size = datalen;
+ buffer->ptr[buffer->size] = '\0';
+
+ return 0;
+}
+
+int git_buf_is_binary(const git_buf *buf)
+{
+ git_str str = GIT_STR_INIT_CONST(buf->ptr, buf->size);
+ return git_str_is_binary(&str);
+}
+
+int git_buf_contains_nul(const git_buf *buf)
+{
+ git_str str = GIT_STR_INIT_CONST(buf->ptr, buf->size);
+ return git_str_contains_nul(&str);
+}
+
+void git_buf_free(git_buf *buffer)
+{
+ git_buf_dispose(buffer);
+}
+
+#endif
diff --git a/src/libgit2/buf.h b/src/libgit2/buf.h
new file mode 100644
index 0000000..4bc7f27
--- /dev/null
+++ b/src/libgit2/buf.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_buf_h__
+#define INCLUDE_buf_h__
+
+#include "git2/buffer.h"
+#include "common.h"
+
+/*
+ * Adapts a private API that takes a `git_str` into a public API that
+ * takes a `git_buf`.
+ */
+
+#define GIT_BUF_WRAP_PRIVATE(buf, fn, ...) \
+ { \
+ git_str str = GIT_STR_INIT; \
+ int error; \
+ if ((error = git_buf_tostr(&str, buf)) == 0 && \
+ (error = fn(&str, __VA_ARGS__)) == 0) \
+ error = git_buf_fromstr(buf, &str); \
+ git_str_dispose(&str); \
+ return error; \
+}
+
+/**
+ * "Sanitizes" a buffer from user input. This simply ensures that the
+ * `git_buf` has nice defaults if the user didn't set the members to
+ * anything, so that if we return early we don't leave it populated
+ * with nonsense.
+ */
+extern int git_buf_sanitize(git_buf *from_user);
+
+/**
+ * Populate a `git_str` from a `git_buf` for passing to libgit2 internal
+ * functions. Sanitizes the given `git_buf` before proceeding. The
+ * `git_buf` will no longer point to this memory.
+ */
+extern int git_buf_tostr(git_str *out, git_buf *buf);
+
+/**
+ * Populate a `git_buf` from a `git_str` for returning to a user.
+ * The `git_str` will no longer point to this memory.
+ */
+extern int git_buf_fromstr(git_buf *out, git_str *str);
+
+#endif
diff --git a/src/libgit2/cache.c b/src/libgit2/cache.c
new file mode 100644
index 0000000..2f68e35
--- /dev/null
+++ b/src/libgit2/cache.c
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "cache.h"
+
+#include "repository.h"
+#include "commit.h"
+#include "thread.h"
+#include "util.h"
+#include "odb.h"
+#include "object.h"
+#include "git2/oid.h"
+
+bool git_cache__enabled = true;
+ssize_t git_cache__max_storage = (256 * 1024 * 1024);
+git_atomic_ssize git_cache__current_storage = {0};
+
+static size_t git_cache__max_object_size[8] = {
+ 0, /* GIT_OBJECT__EXT1 */
+ 4096, /* GIT_OBJECT_COMMIT */
+ 4096, /* GIT_OBJECT_TREE */
+ 0, /* GIT_OBJECT_BLOB */
+ 4096, /* GIT_OBJECT_TAG */
+ 0, /* GIT_OBJECT__EXT2 */
+ 0, /* GIT_OBJECT_OFS_DELTA */
+ 0 /* GIT_OBJECT_REF_DELTA */
+};
+
+int git_cache_set_max_object_size(git_object_t type, size_t size)
+{
+ if (type < 0 || (size_t)type >= ARRAY_SIZE(git_cache__max_object_size)) {
+ git_error_set(GIT_ERROR_INVALID, "type out of range");
+ return -1;
+ }
+
+ git_cache__max_object_size[type] = size;
+ return 0;
+}
+
+int git_cache_init(git_cache *cache)
+{
+ memset(cache, 0, sizeof(*cache));
+
+ if ((git_oidmap_new(&cache->map)) < 0)
+ return -1;
+
+ if (git_rwlock_init(&cache->lock)) {
+ git_error_set(GIT_ERROR_OS, "failed to initialize cache rwlock");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* called with lock */
+static void clear_cache(git_cache *cache)
+{
+ git_cached_obj *evict = NULL;
+
+ if (git_cache_size(cache) == 0)
+ return;
+
+ git_oidmap_foreach_value(cache->map, evict, {
+ git_cached_obj_decref(evict);
+ });
+
+ git_oidmap_clear(cache->map);
+ git_atomic_ssize_add(&git_cache__current_storage, -cache->used_memory);
+ cache->used_memory = 0;
+}
+
+void git_cache_clear(git_cache *cache)
+{
+ if (git_rwlock_wrlock(&cache->lock) < 0)
+ return;
+
+ clear_cache(cache);
+
+ git_rwlock_wrunlock(&cache->lock);
+}
+
+void git_cache_dispose(git_cache *cache)
+{
+ git_cache_clear(cache);
+ git_oidmap_free(cache->map);
+ git_rwlock_free(&cache->lock);
+ git__memzero(cache, sizeof(*cache));
+}
+
+/* Called with lock */
+static void cache_evict_entries(git_cache *cache)
+{
+ size_t evict_count = git_cache_size(cache) / 2048, i;
+ ssize_t evicted_memory = 0;
+
+ if (evict_count < 8)
+ evict_count = 8;
+
+ /* do not infinite loop if there's not enough entries to evict */
+ if (evict_count > git_cache_size(cache)) {
+ clear_cache(cache);
+ return;
+ }
+
+ i = 0;
+ while (evict_count > 0) {
+ git_cached_obj *evict;
+ const git_oid *key;
+
+ if (git_oidmap_iterate((void **) &evict, cache->map, &i, &key) == GIT_ITEROVER)
+ break;
+
+ evict_count--;
+ evicted_memory += evict->size;
+ git_oidmap_delete(cache->map, key);
+ git_cached_obj_decref(evict);
+ }
+
+ cache->used_memory -= evicted_memory;
+ git_atomic_ssize_add(&git_cache__current_storage, -evicted_memory);
+}
+
+static bool cache_should_store(git_object_t object_type, size_t object_size)
+{
+ size_t max_size = git_cache__max_object_size[object_type];
+ return git_cache__enabled && object_size < max_size;
+}
+
+static void *cache_get(git_cache *cache, const git_oid *oid, unsigned int flags)
+{
+ git_cached_obj *entry;
+
+ if (!git_cache__enabled || git_rwlock_rdlock(&cache->lock) < 0)
+ return NULL;
+
+ if ((entry = git_oidmap_get(cache->map, oid)) != NULL) {
+ if (flags && entry->flags != flags) {
+ entry = NULL;
+ } else {
+ git_cached_obj_incref(entry);
+ }
+ }
+
+ git_rwlock_rdunlock(&cache->lock);
+
+ return entry;
+}
+
+static void *cache_store(git_cache *cache, git_cached_obj *entry)
+{
+ git_cached_obj *stored_entry;
+
+ git_cached_obj_incref(entry);
+
+ if (!git_cache__enabled && cache->used_memory > 0) {
+ git_cache_clear(cache);
+ return entry;
+ }
+
+ if (!cache_should_store(entry->type, entry->size))
+ return entry;
+
+ if (git_rwlock_wrlock(&cache->lock) < 0)
+ return entry;
+
+ /* soften the load on the cache */
+ if (git_atomic_ssize_get(&git_cache__current_storage) > git_cache__max_storage)
+ cache_evict_entries(cache);
+
+ /* not found */
+ if ((stored_entry = git_oidmap_get(cache->map, &entry->oid)) == NULL) {
+ if (git_oidmap_set(cache->map, &entry->oid, entry) == 0) {
+ git_cached_obj_incref(entry);
+ cache->used_memory += entry->size;
+ git_atomic_ssize_add(&git_cache__current_storage, (ssize_t)entry->size);
+ }
+ }
+ /* found */
+ else {
+ if (stored_entry->flags == entry->flags) {
+ git_cached_obj_decref(entry);
+ git_cached_obj_incref(stored_entry);
+ entry = stored_entry;
+ } else if (stored_entry->flags == GIT_CACHE_STORE_RAW &&
+ entry->flags == GIT_CACHE_STORE_PARSED) {
+ if (git_oidmap_set(cache->map, &entry->oid, entry) == 0) {
+ git_cached_obj_decref(stored_entry);
+ git_cached_obj_incref(entry);
+ } else {
+ git_cached_obj_decref(entry);
+ git_cached_obj_incref(stored_entry);
+ entry = stored_entry;
+ }
+ } else {
+ /* NO OP */
+ }
+ }
+
+ git_rwlock_wrunlock(&cache->lock);
+ return entry;
+}
+
+void *git_cache_store_raw(git_cache *cache, git_odb_object *entry)
+{
+ entry->cached.flags = GIT_CACHE_STORE_RAW;
+ return cache_store(cache, (git_cached_obj *)entry);
+}
+
+void *git_cache_store_parsed(git_cache *cache, git_object *entry)
+{
+ entry->cached.flags = GIT_CACHE_STORE_PARSED;
+ return cache_store(cache, (git_cached_obj *)entry);
+}
+
+git_odb_object *git_cache_get_raw(git_cache *cache, const git_oid *oid)
+{
+ return cache_get(cache, oid, GIT_CACHE_STORE_RAW);
+}
+
+git_object *git_cache_get_parsed(git_cache *cache, const git_oid *oid)
+{
+ return cache_get(cache, oid, GIT_CACHE_STORE_PARSED);
+}
+
+void *git_cache_get_any(git_cache *cache, const git_oid *oid)
+{
+ return cache_get(cache, oid, GIT_CACHE_STORE_ANY);
+}
+
+void git_cached_obj_decref(void *_obj)
+{
+ git_cached_obj *obj = _obj;
+
+ if (git_atomic32_dec(&obj->refcount) == 0) {
+ switch (obj->flags) {
+ case GIT_CACHE_STORE_RAW:
+ git_odb_object__free(_obj);
+ break;
+
+ case GIT_CACHE_STORE_PARSED:
+ git_object__free(_obj);
+ break;
+
+ default:
+ git__free(_obj);
+ break;
+ }
+ }
+}
diff --git a/src/libgit2/cache.h b/src/libgit2/cache.h
new file mode 100644
index 0000000..42c4fa8
--- /dev/null
+++ b/src/libgit2/cache.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_cache_h__
+#define INCLUDE_cache_h__
+
+#include "common.h"
+
+#include "git2/common.h"
+#include "git2/oid.h"
+#include "git2/odb.h"
+
+#include "thread.h"
+#include "oidmap.h"
+
+enum {
+ GIT_CACHE_STORE_ANY = 0,
+ GIT_CACHE_STORE_RAW = 1,
+ GIT_CACHE_STORE_PARSED = 2
+};
+
+typedef struct {
+ git_oid oid;
+ int16_t type; /* git_object_t value */
+ uint16_t flags; /* GIT_CACHE_STORE value */
+ size_t size;
+ git_atomic32 refcount;
+} git_cached_obj;
+
+typedef struct {
+ git_oidmap *map;
+ git_rwlock lock;
+ ssize_t used_memory;
+} git_cache;
+
+extern bool git_cache__enabled;
+extern ssize_t git_cache__max_storage;
+extern git_atomic_ssize git_cache__current_storage;
+
+int git_cache_set_max_object_size(git_object_t type, size_t size);
+
+int git_cache_init(git_cache *cache);
+void git_cache_dispose(git_cache *cache);
+void git_cache_clear(git_cache *cache);
+
+void *git_cache_store_raw(git_cache *cache, git_odb_object *entry);
+void *git_cache_store_parsed(git_cache *cache, git_object *entry);
+
+git_odb_object *git_cache_get_raw(git_cache *cache, const git_oid *oid);
+git_object *git_cache_get_parsed(git_cache *cache, const git_oid *oid);
+void *git_cache_get_any(git_cache *cache, const git_oid *oid);
+
+GIT_INLINE(size_t) git_cache_size(git_cache *cache)
+{
+ return (size_t)git_oidmap_size(cache->map);
+}
+
+GIT_INLINE(void) git_cached_obj_incref(void *_obj)
+{
+ git_cached_obj *obj = _obj;
+ git_atomic32_inc(&obj->refcount);
+}
+
+void git_cached_obj_decref(void *_obj);
+
+#endif
diff --git a/src/libgit2/checkout.c b/src/libgit2/checkout.c
new file mode 100644
index 0000000..6a46431
--- /dev/null
+++ b/src/libgit2/checkout.c
@@ -0,0 +1,2813 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "checkout.h"
+
+#include "git2/repository.h"
+#include "git2/refs.h"
+#include "git2/tree.h"
+#include "git2/blob.h"
+#include "git2/config.h"
+#include "git2/diff.h"
+#include "git2/submodule.h"
+#include "git2/sys/index.h"
+#include "git2/sys/filter.h"
+#include "git2/merge.h"
+
+#include "refs.h"
+#include "repository.h"
+#include "index.h"
+#include "filter.h"
+#include "blob.h"
+#include "diff.h"
+#include "diff_generate.h"
+#include "pathspec.h"
+#include "diff_xdiff.h"
+#include "fs_path.h"
+#include "attr.h"
+#include "pool.h"
+#include "strmap.h"
+#include "path.h"
+
+/* See docs/checkout-internals.md for more information */
+
+enum {
+ CHECKOUT_ACTION__NONE = 0,
+ CHECKOUT_ACTION__REMOVE = 1,
+ CHECKOUT_ACTION__UPDATE_BLOB = 2,
+ CHECKOUT_ACTION__UPDATE_SUBMODULE = 4,
+ CHECKOUT_ACTION__CONFLICT = 8,
+ CHECKOUT_ACTION__REMOVE_CONFLICT = 16,
+ CHECKOUT_ACTION__UPDATE_CONFLICT = 32,
+ CHECKOUT_ACTION__MAX = 32,
+ CHECKOUT_ACTION__REMOVE_AND_UPDATE =
+ (CHECKOUT_ACTION__UPDATE_BLOB | CHECKOUT_ACTION__REMOVE)
+};
+
+typedef struct {
+ git_repository *repo;
+ git_iterator *target;
+ git_diff *diff;
+ git_checkout_options opts;
+ bool opts_free_baseline;
+ char *pfx;
+ git_index *index;
+ git_pool pool;
+ git_vector removes;
+ git_vector remove_conflicts;
+ git_vector update_conflicts;
+ git_vector *update_reuc;
+ git_vector *update_names;
+ git_str target_path;
+ size_t target_len;
+ git_str tmp;
+ unsigned int strategy;
+ int can_symlink;
+ int respect_filemode;
+ bool reload_submodules;
+ size_t total_steps;
+ size_t completed_steps;
+ git_checkout_perfdata perfdata;
+ git_strmap *mkdir_map;
+ git_attr_session attr_session;
+} checkout_data;
+
+typedef struct {
+ const git_index_entry *ancestor;
+ const git_index_entry *ours;
+ const git_index_entry *theirs;
+
+ unsigned int name_collision:1,
+ directoryfile:1,
+ one_to_two:1,
+ binary:1,
+ submodule:1;
+} checkout_conflictdata;
+
+static int checkout_notify(
+ checkout_data *data,
+ git_checkout_notify_t why,
+ const git_diff_delta *delta,
+ const git_index_entry *wditem)
+{
+ git_diff_file wdfile;
+ const git_diff_file *baseline = NULL, *target = NULL, *workdir = NULL;
+ const char *path = NULL;
+
+ if (!data->opts.notify_cb ||
+ (why & data->opts.notify_flags) == 0)
+ return 0;
+
+ if (wditem) {
+ memset(&wdfile, 0, sizeof(wdfile));
+
+ git_oid_cpy(&wdfile.id, &wditem->id);
+ wdfile.path = wditem->path;
+ wdfile.size = wditem->file_size;
+ wdfile.flags = GIT_DIFF_FLAG_VALID_ID;
+ wdfile.mode = wditem->mode;
+
+ workdir = &wdfile;
+
+ path = wditem->path;
+ }
+
+ if (delta) {
+ switch (delta->status) {
+ case GIT_DELTA_UNMODIFIED:
+ case GIT_DELTA_MODIFIED:
+ case GIT_DELTA_TYPECHANGE:
+ default:
+ baseline = &delta->old_file;
+ target = &delta->new_file;
+ break;
+ case GIT_DELTA_ADDED:
+ case GIT_DELTA_IGNORED:
+ case GIT_DELTA_UNTRACKED:
+ case GIT_DELTA_UNREADABLE:
+ target = &delta->new_file;
+ break;
+ case GIT_DELTA_DELETED:
+ baseline = &delta->old_file;
+ break;
+ }
+
+ path = delta->old_file.path;
+ }
+
+ {
+ int error = data->opts.notify_cb(
+ why, path, baseline, target, workdir, data->opts.notify_payload);
+
+ return git_error_set_after_callback_function(
+ error, "git_checkout notification");
+ }
+}
+
+GIT_INLINE(bool) is_workdir_base_or_new(
+ const git_oid *workdir_id,
+ const git_diff_file *baseitem,
+ const git_diff_file *newitem)
+{
+ return (git_oid__cmp(&baseitem->id, workdir_id) == 0 ||
+ git_oid__cmp(&newitem->id, workdir_id) == 0);
+}
+
+GIT_INLINE(bool) is_filemode_changed(git_filemode_t a, git_filemode_t b, int respect_filemode)
+{
+ /* If core.filemode = false, ignore links in the repository and executable bit changes */
+ if (!respect_filemode) {
+ if (a == S_IFLNK)
+ a = GIT_FILEMODE_BLOB;
+ if (b == S_IFLNK)
+ b = GIT_FILEMODE_BLOB;
+
+ a &= ~0111;
+ b &= ~0111;
+ }
+
+ return (a != b);
+}
+
+static bool checkout_is_workdir_modified(
+ checkout_data *data,
+ const git_diff_file *baseitem,
+ const git_diff_file *newitem,
+ const git_index_entry *wditem)
+{
+ git_oid oid;
+ const git_index_entry *ie;
+
+ /* handle "modified" submodule */
+ if (wditem->mode == GIT_FILEMODE_COMMIT) {
+ git_submodule *sm;
+ unsigned int sm_status = 0;
+ const git_oid *sm_oid = NULL;
+ bool rval = false;
+
+ if (git_submodule_lookup(&sm, data->repo, wditem->path) < 0) {
+ git_error_clear();
+ return true;
+ }
+
+ if (git_submodule_status(&sm_status, data->repo, wditem->path, GIT_SUBMODULE_IGNORE_UNSPECIFIED) < 0 ||
+ GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status))
+ rval = true;
+ else if ((sm_oid = git_submodule_wd_id(sm)) == NULL)
+ rval = false;
+ else
+ rval = (git_oid__cmp(&baseitem->id, sm_oid) != 0);
+
+ git_submodule_free(sm);
+ return rval;
+ }
+
+ /*
+ * Look at the cache to decide if the workdir is modified: if the
+ * cache contents match the workdir contents, then we do not need
+ * to examine the working directory directly, instead we can
+ * examine the cache to see if _it_ has been modified. This allows
+ * us to avoid touching the disk.
+ */
+ ie = git_index_get_bypath(data->index, wditem->path, 0);
+
+ if (ie != NULL &&
+ !git_index_entry_newer_than_index(ie, data->index) &&
+ git_index_time_eq(&wditem->mtime, &ie->mtime) &&
+ wditem->file_size == ie->file_size &&
+ !is_filemode_changed(wditem->mode, ie->mode, data->respect_filemode)) {
+
+ /* The workdir is modified iff the index entry is modified */
+ return !is_workdir_base_or_new(&ie->id, baseitem, newitem) ||
+ is_filemode_changed(baseitem->mode, ie->mode, data->respect_filemode);
+ }
+
+ /* depending on where base is coming from, we may or may not know
+ * the actual size of the data, so we can't rely on this shortcut.
+ */
+ if (baseitem->size && wditem->file_size != baseitem->size)
+ return true;
+
+ /* if the workdir item is a directory, it cannot be a modified file */
+ if (S_ISDIR(wditem->mode))
+ return false;
+
+ if (is_filemode_changed(baseitem->mode, wditem->mode, data->respect_filemode))
+ return true;
+
+ if (git_diff__oid_for_entry(&oid, data->diff, wditem, wditem->mode, NULL) < 0)
+ return false;
+
+ /* Allow the checkout if the workdir is not modified *or* if the checkout
+ * target's contents are already in the working directory.
+ */
+ return !is_workdir_base_or_new(&oid, baseitem, newitem);
+}
+
+#define CHECKOUT_ACTION_IF(FLAG,YES,NO) \
+ ((data->strategy & GIT_CHECKOUT_##FLAG) ? CHECKOUT_ACTION__##YES : CHECKOUT_ACTION__##NO)
+
+static int checkout_action_common(
+ int *action,
+ checkout_data *data,
+ const git_diff_delta *delta,
+ const git_index_entry *wd)
+{
+ git_checkout_notify_t notify = GIT_CHECKOUT_NOTIFY_NONE;
+
+ if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0)
+ *action = (*action & ~CHECKOUT_ACTION__REMOVE);
+
+ if ((*action & CHECKOUT_ACTION__UPDATE_BLOB) != 0) {
+ if (S_ISGITLINK(delta->new_file.mode))
+ *action = (*action & ~CHECKOUT_ACTION__UPDATE_BLOB) |
+ CHECKOUT_ACTION__UPDATE_SUBMODULE;
+
+ /* to "update" a symlink, we must remove the old one first */
+ if (delta->new_file.mode == GIT_FILEMODE_LINK && wd != NULL)
+ *action |= CHECKOUT_ACTION__REMOVE;
+
+ /* if the file is on disk and doesn't match our mode, force update */
+ if (wd &&
+ GIT_PERMS_IS_EXEC(wd->mode) != GIT_PERMS_IS_EXEC(delta->new_file.mode))
+ *action |= CHECKOUT_ACTION__REMOVE;
+
+ notify = GIT_CHECKOUT_NOTIFY_UPDATED;
+ }
+
+ if ((*action & CHECKOUT_ACTION__CONFLICT) != 0)
+ notify = GIT_CHECKOUT_NOTIFY_CONFLICT;
+
+ return checkout_notify(data, notify, delta, wd);
+}
+
+static int checkout_action_no_wd(
+ int *action,
+ checkout_data *data,
+ const git_diff_delta *delta)
+{
+ int error = 0;
+
+ *action = CHECKOUT_ACTION__NONE;
+
+ switch (delta->status) {
+ case GIT_DELTA_UNMODIFIED: /* case 12 */
+ error = checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, NULL);
+ if (error)
+ return error;
+ *action = CHECKOUT_ACTION_IF(RECREATE_MISSING, UPDATE_BLOB, NONE);
+ break;
+ case GIT_DELTA_ADDED: /* case 2 or 28 (and 5 but not really) */
+ *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
+ break;
+ case GIT_DELTA_MODIFIED: /* case 13 (and 35 but not really) */
+ *action = CHECKOUT_ACTION_IF(RECREATE_MISSING, UPDATE_BLOB, CONFLICT);
+ break;
+ case GIT_DELTA_TYPECHANGE: /* case 21 (B->T) and 28 (T->B)*/
+ if (delta->new_file.mode == GIT_FILEMODE_TREE)
+ *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
+ break;
+ case GIT_DELTA_DELETED: /* case 8 or 25 */
+ *action = CHECKOUT_ACTION_IF(SAFE, REMOVE, NONE);
+ break;
+ default: /* impossible */
+ break;
+ }
+
+ return checkout_action_common(action, data, delta, NULL);
+}
+
+static int checkout_target_fullpath(
+ git_str **out, checkout_data *data, const char *path)
+{
+ git_str_truncate(&data->target_path, data->target_len);
+
+ if (path && git_str_puts(&data->target_path, path) < 0)
+ return -1;
+
+ if (git_path_validate_str_length(data->repo, &data->target_path) < 0)
+ return -1;
+
+ *out = &data->target_path;
+
+ return 0;
+}
+
+static bool wd_item_is_removable(
+ checkout_data *data, const git_index_entry *wd)
+{
+ git_str *full;
+
+ if (wd->mode != GIT_FILEMODE_TREE)
+ return true;
+
+ if (checkout_target_fullpath(&full, data, wd->path) < 0)
+ return false;
+
+ return !full || !git_fs_path_contains(full, DOT_GIT);
+}
+
+static int checkout_queue_remove(checkout_data *data, const char *path)
+{
+ char *copy = git_pool_strdup(&data->pool, path);
+ GIT_ERROR_CHECK_ALLOC(copy);
+ return git_vector_insert(&data->removes, copy);
+}
+
+/* note that this advances the iterator over the wd item */
+static int checkout_action_wd_only(
+ checkout_data *data,
+ git_iterator *workdir,
+ const git_index_entry **wditem,
+ git_vector *pathspec)
+{
+ int error = 0;
+ bool remove = false;
+ git_checkout_notify_t notify = GIT_CHECKOUT_NOTIFY_NONE;
+ const git_index_entry *wd = *wditem;
+
+ if (!git_pathspec__match(
+ pathspec, wd->path,
+ (data->strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) != 0,
+ git_iterator_ignore_case(workdir), NULL, NULL)) {
+
+ if (wd->mode == GIT_FILEMODE_TREE)
+ return git_iterator_advance_into(wditem, workdir);
+ else
+ return git_iterator_advance(wditem, workdir);
+ }
+
+ /* check if item is tracked in the index but not in the checkout diff */
+ if (data->index != NULL) {
+ size_t pos;
+
+ error = git_index__find_pos(
+ &pos, data->index, wd->path, 0, GIT_INDEX_STAGE_ANY);
+
+ if (wd->mode != GIT_FILEMODE_TREE) {
+ if (!error) { /* found by git_index__find_pos call */
+ notify = GIT_CHECKOUT_NOTIFY_DIRTY;
+ remove = ((data->strategy & GIT_CHECKOUT_FORCE) != 0);
+ } else if (error != GIT_ENOTFOUND)
+ return error;
+ else
+ error = 0; /* git_index__find_pos does not set error msg */
+ } else {
+ /* for tree entries, we have to see if there are any index
+ * entries that are contained inside that tree
+ */
+ const git_index_entry *e = git_index_get_byindex(data->index, pos);
+
+ if (e != NULL && data->diff->pfxcomp(e->path, wd->path) == 0)
+ return git_iterator_advance_into(wditem, workdir);
+ }
+ }
+
+ if (notify != GIT_CHECKOUT_NOTIFY_NONE) {
+ /* if we found something in the index, notify and advance */
+ if ((error = checkout_notify(data, notify, NULL, wd)) != 0)
+ return error;
+
+ if (remove && wd_item_is_removable(data, wd))
+ error = checkout_queue_remove(data, wd->path);
+
+ if (!error)
+ error = git_iterator_advance(wditem, workdir);
+ } else {
+ /* untracked or ignored - can't know which until we advance through */
+ bool over = false, removable = wd_item_is_removable(data, wd);
+ git_iterator_status_t untracked_state;
+
+ /* copy the entry for issuing notification callback later */
+ git_index_entry saved_wd = *wd;
+ git_str_sets(&data->tmp, wd->path);
+ saved_wd.path = data->tmp.ptr;
+
+ error = git_iterator_advance_over(
+ wditem, &untracked_state, workdir);
+ if (error == GIT_ITEROVER)
+ over = true;
+ else if (error < 0)
+ return error;
+
+ if (untracked_state == GIT_ITERATOR_STATUS_IGNORED) {
+ notify = GIT_CHECKOUT_NOTIFY_IGNORED;
+ remove = ((data->strategy & GIT_CHECKOUT_REMOVE_IGNORED) != 0);
+ } else {
+ notify = GIT_CHECKOUT_NOTIFY_UNTRACKED;
+ remove = ((data->strategy & GIT_CHECKOUT_REMOVE_UNTRACKED) != 0);
+ }
+
+ if ((error = checkout_notify(data, notify, NULL, &saved_wd)) != 0)
+ return error;
+
+ if (remove && removable)
+ error = checkout_queue_remove(data, saved_wd.path);
+
+ if (!error && over) /* restore ITEROVER if needed */
+ error = GIT_ITEROVER;
+ }
+
+ return error;
+}
+
+static bool submodule_is_config_only(
+ checkout_data *data,
+ const char *path)
+{
+ git_submodule *sm = NULL;
+ unsigned int sm_loc = 0;
+ bool rval = false;
+
+ if (git_submodule_lookup(&sm, data->repo, path) < 0)
+ return true;
+
+ if (git_submodule_location(&sm_loc, sm) < 0 ||
+ sm_loc == GIT_SUBMODULE_STATUS_IN_CONFIG)
+ rval = true;
+
+ git_submodule_free(sm);
+
+ return rval;
+}
+
+static bool checkout_is_empty_dir(checkout_data *data, const char *path)
+{
+ git_str *fullpath;
+
+ if (checkout_target_fullpath(&fullpath, data, path) < 0)
+ return false;
+
+ return git_fs_path_is_empty_dir(fullpath->ptr);
+}
+
+static int checkout_action_with_wd(
+ int *action,
+ checkout_data *data,
+ const git_diff_delta *delta,
+ git_iterator *workdir,
+ const git_index_entry *wd)
+{
+ *action = CHECKOUT_ACTION__NONE;
+
+ switch (delta->status) {
+ case GIT_DELTA_UNMODIFIED: /* case 14/15 or 33 */
+ if (checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd)) {
+ GIT_ERROR_CHECK_ERROR(
+ checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, wd) );
+ *action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, NONE);
+ }
+ break;
+ case GIT_DELTA_ADDED: /* case 3, 4 or 6 */
+ if (git_iterator_current_is_ignored(workdir))
+ *action = CHECKOUT_ACTION_IF(DONT_OVERWRITE_IGNORED, CONFLICT, UPDATE_BLOB);
+ else
+ *action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, CONFLICT);
+ break;
+ case GIT_DELTA_DELETED: /* case 9 or 10 (or 26 but not really) */
+ if (checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd))
+ *action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT);
+ else
+ *action = CHECKOUT_ACTION_IF(SAFE, REMOVE, NONE);
+ break;
+ case GIT_DELTA_MODIFIED: /* case 16, 17, 18 (or 36 but not really) */
+ if (wd->mode != GIT_FILEMODE_COMMIT &&
+ checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd))
+ *action = CHECKOUT_ACTION_IF(FORCE, UPDATE_BLOB, CONFLICT);
+ else
+ *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
+ break;
+ case GIT_DELTA_TYPECHANGE: /* case 22, 23, 29, 30 */
+ if (delta->old_file.mode == GIT_FILEMODE_TREE) {
+ if (wd->mode == GIT_FILEMODE_TREE)
+ /* either deleting items in old tree will delete the wd dir,
+ * or we'll get a conflict when we attempt blob update...
+ */
+ *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
+ else if (wd->mode == GIT_FILEMODE_COMMIT) {
+ /* workdir is possibly a "phantom" submodule - treat as a
+ * tree if the only submodule info came from the config
+ */
+ if (submodule_is_config_only(data, wd->path))
+ *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
+ else
+ *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT);
+ } else
+ *action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT);
+ }
+ else if (checkout_is_workdir_modified(data, &delta->old_file, &delta->new_file, wd))
+ *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT);
+ else
+ *action = CHECKOUT_ACTION_IF(SAFE, REMOVE_AND_UPDATE, NONE);
+
+ /* don't update if the typechange is to a tree */
+ if (delta->new_file.mode == GIT_FILEMODE_TREE)
+ *action = (*action & ~CHECKOUT_ACTION__UPDATE_BLOB);
+ break;
+ default: /* impossible */
+ break;
+ }
+
+ return checkout_action_common(action, data, delta, wd);
+}
+
+static int checkout_action_with_wd_blocker(
+ int *action,
+ checkout_data *data,
+ const git_diff_delta *delta,
+ const git_index_entry *wd)
+{
+ *action = CHECKOUT_ACTION__NONE;
+
+ switch (delta->status) {
+ case GIT_DELTA_UNMODIFIED:
+ /* should show delta as dirty / deleted */
+ GIT_ERROR_CHECK_ERROR(
+ checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, wd) );
+ *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, NONE);
+ break;
+ case GIT_DELTA_ADDED:
+ case GIT_DELTA_MODIFIED:
+ *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT);
+ break;
+ case GIT_DELTA_DELETED:
+ *action = CHECKOUT_ACTION_IF(FORCE, REMOVE, CONFLICT);
+ break;
+ case GIT_DELTA_TYPECHANGE:
+ /* not 100% certain about this... */
+ *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT);
+ break;
+ default: /* impossible */
+ break;
+ }
+
+ return checkout_action_common(action, data, delta, wd);
+}
+
+static int checkout_action_with_wd_dir(
+ int *action,
+ checkout_data *data,
+ const git_diff_delta *delta,
+ git_iterator *workdir,
+ const git_index_entry *wd)
+{
+ *action = CHECKOUT_ACTION__NONE;
+
+ switch (delta->status) {
+ case GIT_DELTA_UNMODIFIED: /* case 19 or 24 (or 34 but not really) */
+ GIT_ERROR_CHECK_ERROR(
+ checkout_notify(data, GIT_CHECKOUT_NOTIFY_DIRTY, delta, NULL));
+ GIT_ERROR_CHECK_ERROR(
+ checkout_notify(data, GIT_CHECKOUT_NOTIFY_UNTRACKED, NULL, wd));
+ *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, NONE);
+ break;
+ case GIT_DELTA_ADDED:/* case 4 (and 7 for dir) */
+ case GIT_DELTA_MODIFIED: /* case 20 (or 37 but not really) */
+ if (delta->old_file.mode == GIT_FILEMODE_COMMIT)
+ /* expected submodule (and maybe found one) */;
+ else if (delta->new_file.mode != GIT_FILEMODE_TREE)
+ *action = git_iterator_current_is_ignored(workdir) ?
+ CHECKOUT_ACTION_IF(DONT_OVERWRITE_IGNORED, CONFLICT, REMOVE_AND_UPDATE) :
+ CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT);
+ break;
+ case GIT_DELTA_DELETED: /* case 11 (and 27 for dir) */
+ if (delta->old_file.mode != GIT_FILEMODE_TREE)
+ GIT_ERROR_CHECK_ERROR(
+ checkout_notify(data, GIT_CHECKOUT_NOTIFY_UNTRACKED, NULL, wd));
+ break;
+ case GIT_DELTA_TYPECHANGE: /* case 24 or 31 */
+ if (delta->old_file.mode == GIT_FILEMODE_TREE) {
+ /* For typechange from dir, remove dir and add blob, but it is
+ * not safe to remove dir if it contains modified files.
+ * However, safely removing child files will remove the parent
+ * directory if is it left empty, so we can defer removing the
+ * dir and it will succeed if no children are left.
+ */
+ *action = CHECKOUT_ACTION_IF(SAFE, UPDATE_BLOB, NONE);
+ }
+ else if (delta->new_file.mode != GIT_FILEMODE_TREE)
+ /* For typechange to dir, dir is already created so no action */
+ *action = CHECKOUT_ACTION_IF(FORCE, REMOVE_AND_UPDATE, CONFLICT);
+ break;
+ default: /* impossible */
+ break;
+ }
+
+ return checkout_action_common(action, data, delta, wd);
+}
+
+static int checkout_action_with_wd_dir_empty(
+ int *action,
+ checkout_data *data,
+ const git_diff_delta *delta)
+{
+ int error = checkout_action_no_wd(action, data, delta);
+
+ /* We can always safely remove an empty directory. */
+ if (error == 0 && *action != CHECKOUT_ACTION__NONE)
+ *action |= CHECKOUT_ACTION__REMOVE;
+
+ return error;
+}
+
+static int checkout_action(
+ int *action,
+ checkout_data *data,
+ git_diff_delta *delta,
+ git_iterator *workdir,
+ const git_index_entry **wditem,
+ git_vector *pathspec)
+{
+ int cmp = -1, error;
+ int (*strcomp)(const char *, const char *) = data->diff->strcomp;
+ int (*pfxcomp)(const char *str, const char *pfx) = data->diff->pfxcomp;
+ int (*advance)(const git_index_entry **, git_iterator *) = NULL;
+
+ /* move workdir iterator to follow along with deltas */
+
+ while (1) {
+ const git_index_entry *wd = *wditem;
+
+ if (!wd)
+ return checkout_action_no_wd(action, data, delta);
+
+ cmp = strcomp(wd->path, delta->old_file.path);
+
+ /* 1. wd before delta ("a/a" before "a/b")
+ * 2. wd prefixes delta & should expand ("a/" before "a/b")
+ * 3. wd prefixes delta & cannot expand ("a/b" before "a/b/c")
+ * 4. wd equals delta ("a/b" and "a/b")
+ * 5. wd after delta & delta prefixes wd ("a/b/c" after "a/b/" or "a/b")
+ * 6. wd after delta ("a/c" after "a/b")
+ */
+
+ if (cmp < 0) {
+ cmp = pfxcomp(delta->old_file.path, wd->path);
+
+ if (cmp == 0) {
+ if (wd->mode == GIT_FILEMODE_TREE) {
+ /* case 2 - entry prefixed by workdir tree */
+ error = git_iterator_advance_into(wditem, workdir);
+ if (error < 0 && error != GIT_ITEROVER)
+ goto done;
+ continue;
+ }
+
+ /* case 3 maybe - wd contains non-dir where dir expected */
+ if (delta->old_file.path[strlen(wd->path)] == '/') {
+ error = checkout_action_with_wd_blocker(
+ action, data, delta, wd);
+ advance = git_iterator_advance;
+ goto done;
+ }
+ }
+
+ /* case 1 - handle wd item (if it matches pathspec) */
+ error = checkout_action_wd_only(data, workdir, wditem, pathspec);
+ if (error && error != GIT_ITEROVER)
+ goto done;
+ continue;
+ }
+
+ if (cmp == 0) {
+ /* case 4 */
+ error = checkout_action_with_wd(action, data, delta, workdir, wd);
+ advance = git_iterator_advance;
+ goto done;
+ }
+
+ cmp = pfxcomp(wd->path, delta->old_file.path);
+
+ if (cmp == 0) { /* case 5 */
+ if (wd->path[strlen(delta->old_file.path)] != '/')
+ return checkout_action_no_wd(action, data, delta);
+
+ if (delta->status == GIT_DELTA_TYPECHANGE) {
+ if (delta->old_file.mode == GIT_FILEMODE_TREE) {
+ error = checkout_action_with_wd(action, data, delta, workdir, wd);
+ advance = git_iterator_advance_into;
+ goto done;
+ }
+
+ if (delta->new_file.mode == GIT_FILEMODE_TREE ||
+ delta->new_file.mode == GIT_FILEMODE_COMMIT ||
+ delta->old_file.mode == GIT_FILEMODE_COMMIT)
+ {
+ error = checkout_action_with_wd(action, data, delta, workdir, wd);
+ advance = git_iterator_advance;
+ goto done;
+ }
+ }
+
+ return checkout_is_empty_dir(data, wd->path) ?
+ checkout_action_with_wd_dir_empty(action, data, delta) :
+ checkout_action_with_wd_dir(action, data, delta, workdir, wd);
+ }
+
+ /* case 6 - wd is after delta */
+ return checkout_action_no_wd(action, data, delta);
+ }
+
+done:
+ if (!error && advance != NULL &&
+ (error = advance(wditem, workdir)) < 0) {
+ *wditem = NULL;
+ if (error == GIT_ITEROVER)
+ error = 0;
+ }
+
+ return error;
+}
+
+static int checkout_remaining_wd_items(
+ checkout_data *data,
+ git_iterator *workdir,
+ const git_index_entry *wd,
+ git_vector *spec)
+{
+ int error = 0;
+
+ while (wd && !error)
+ error = checkout_action_wd_only(data, workdir, &wd, spec);
+
+ if (error == GIT_ITEROVER)
+ error = 0;
+
+ return error;
+}
+
+GIT_INLINE(int) checkout_idxentry_cmp(
+ const git_index_entry *a,
+ const git_index_entry *b)
+{
+ if (!a && !b)
+ return 0;
+ else if (!a && b)
+ return -1;
+ else if(a && !b)
+ return 1;
+ else
+ return strcmp(a->path, b->path);
+}
+
+static int checkout_conflictdata_cmp(const void *a, const void *b)
+{
+ const checkout_conflictdata *ca = a;
+ const checkout_conflictdata *cb = b;
+ int diff;
+
+ if ((diff = checkout_idxentry_cmp(ca->ancestor, cb->ancestor)) == 0 &&
+ (diff = checkout_idxentry_cmp(ca->ours, cb->theirs)) == 0)
+ diff = checkout_idxentry_cmp(ca->theirs, cb->theirs);
+
+ return diff;
+}
+
+static int checkout_conflictdata_empty(
+ const git_vector *conflicts, size_t idx, void *payload)
+{
+ checkout_conflictdata *conflict;
+
+ GIT_UNUSED(payload);
+
+ if ((conflict = git_vector_get(conflicts, idx)) == NULL)
+ return -1;
+
+ if (conflict->ancestor || conflict->ours || conflict->theirs)
+ return 0;
+
+ git__free(conflict);
+ return 1;
+}
+
+GIT_INLINE(bool) conflict_pathspec_match(
+ checkout_data *data,
+ git_iterator *workdir,
+ git_vector *pathspec,
+ const git_index_entry *ancestor,
+ const git_index_entry *ours,
+ const git_index_entry *theirs)
+{
+ /* if the pathspec matches ours *or* theirs, proceed */
+ if (ours && git_pathspec__match(pathspec, ours->path,
+ (data->strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) != 0,
+ git_iterator_ignore_case(workdir), NULL, NULL))
+ return true;
+
+ if (theirs && git_pathspec__match(pathspec, theirs->path,
+ (data->strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) != 0,
+ git_iterator_ignore_case(workdir), NULL, NULL))
+ return true;
+
+ if (ancestor && git_pathspec__match(pathspec, ancestor->path,
+ (data->strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH) != 0,
+ git_iterator_ignore_case(workdir), NULL, NULL))
+ return true;
+
+ return false;
+}
+
+GIT_INLINE(int) checkout_conflict_detect_submodule(checkout_conflictdata *conflict)
+{
+ conflict->submodule = ((conflict->ancestor && S_ISGITLINK(conflict->ancestor->mode)) ||
+ (conflict->ours && S_ISGITLINK(conflict->ours->mode)) ||
+ (conflict->theirs && S_ISGITLINK(conflict->theirs->mode)));
+ return 0;
+}
+
+GIT_INLINE(int) checkout_conflict_detect_binary(git_repository *repo, checkout_conflictdata *conflict)
+{
+ git_blob *ancestor_blob = NULL, *our_blob = NULL, *their_blob = NULL;
+ int error = 0;
+
+ if (conflict->submodule)
+ return 0;
+
+ if (conflict->ancestor) {
+ if ((error = git_blob_lookup(&ancestor_blob, repo, &conflict->ancestor->id)) < 0)
+ goto done;
+
+ conflict->binary = git_blob_is_binary(ancestor_blob);
+ }
+
+ if (!conflict->binary && conflict->ours) {
+ if ((error = git_blob_lookup(&our_blob, repo, &conflict->ours->id)) < 0)
+ goto done;
+
+ conflict->binary = git_blob_is_binary(our_blob);
+ }
+
+ if (!conflict->binary && conflict->theirs) {
+ if ((error = git_blob_lookup(&their_blob, repo, &conflict->theirs->id)) < 0)
+ goto done;
+
+ conflict->binary = git_blob_is_binary(their_blob);
+ }
+
+done:
+ git_blob_free(ancestor_blob);
+ git_blob_free(our_blob);
+ git_blob_free(their_blob);
+
+ return error;
+}
+
+static int checkout_conflict_append_update(
+ const git_index_entry *ancestor,
+ const git_index_entry *ours,
+ const git_index_entry *theirs,
+ void *payload)
+{
+ checkout_data *data = payload;
+ checkout_conflictdata *conflict;
+ int error;
+
+ conflict = git__calloc(1, sizeof(checkout_conflictdata));
+ GIT_ERROR_CHECK_ALLOC(conflict);
+
+ conflict->ancestor = ancestor;
+ conflict->ours = ours;
+ conflict->theirs = theirs;
+
+ if ((error = checkout_conflict_detect_submodule(conflict)) < 0 ||
+ (error = checkout_conflict_detect_binary(data->repo, conflict)) < 0)
+ {
+ git__free(conflict);
+ return error;
+ }
+
+ if (git_vector_insert(&data->update_conflicts, conflict))
+ return -1;
+
+ return 0;
+}
+
+static int checkout_conflicts_foreach(
+ checkout_data *data,
+ git_index *index,
+ git_iterator *workdir,
+ git_vector *pathspec,
+ int (*cb)(const git_index_entry *, const git_index_entry *, const git_index_entry *, void *),
+ void *payload)
+{
+ git_index_conflict_iterator *iterator = NULL;
+ const git_index_entry *ancestor, *ours, *theirs;
+ int error = 0;
+
+ if ((error = git_index_conflict_iterator_new(&iterator, index)) < 0)
+ goto done;
+
+ /* Collect the conflicts */
+ while ((error = git_index_conflict_next(&ancestor, &ours, &theirs, iterator)) == 0) {
+ if (!conflict_pathspec_match(data, workdir, pathspec, ancestor, ours, theirs))
+ continue;
+
+ if ((error = cb(ancestor, ours, theirs, payload)) < 0)
+ goto done;
+ }
+
+ if (error == GIT_ITEROVER)
+ error = 0;
+
+done:
+ git_index_conflict_iterator_free(iterator);
+
+ return error;
+}
+
+static int checkout_conflicts_load(checkout_data *data, git_iterator *workdir, git_vector *pathspec)
+{
+ git_index *index;
+
+ /* Only write conflicts from sources that have them: indexes. */
+ if ((index = git_iterator_index(data->target)) == NULL)
+ return 0;
+
+ data->update_conflicts._cmp = checkout_conflictdata_cmp;
+
+ if (checkout_conflicts_foreach(data, index, workdir, pathspec, checkout_conflict_append_update, data) < 0)
+ return -1;
+
+ /* Collect the REUC and NAME entries */
+ data->update_reuc = &index->reuc;
+ data->update_names = &index->names;
+
+ return 0;
+}
+
+GIT_INLINE(int) checkout_conflicts_cmp_entry(
+ const char *path,
+ const git_index_entry *entry)
+{
+ return strcmp((const char *)path, entry->path);
+}
+
+static int checkout_conflicts_cmp_ancestor(const void *p, const void *c)
+{
+ const char *path = p;
+ const checkout_conflictdata *conflict = c;
+
+ if (!conflict->ancestor)
+ return 1;
+
+ return checkout_conflicts_cmp_entry(path, conflict->ancestor);
+}
+
+static checkout_conflictdata *checkout_conflicts_search_ancestor(
+ checkout_data *data,
+ const char *path)
+{
+ size_t pos;
+
+ if (git_vector_bsearch2(&pos, &data->update_conflicts, checkout_conflicts_cmp_ancestor, path) < 0)
+ return NULL;
+
+ return git_vector_get(&data->update_conflicts, pos);
+}
+
+static checkout_conflictdata *checkout_conflicts_search_branch(
+ checkout_data *data,
+ const char *path)
+{
+ checkout_conflictdata *conflict;
+ size_t i;
+
+ git_vector_foreach(&data->update_conflicts, i, conflict) {
+ int cmp = -1;
+
+ if (conflict->ancestor)
+ break;
+
+ if (conflict->ours)
+ cmp = checkout_conflicts_cmp_entry(path, conflict->ours);
+ else if (conflict->theirs)
+ cmp = checkout_conflicts_cmp_entry(path, conflict->theirs);
+
+ if (cmp == 0)
+ return conflict;
+ }
+
+ return NULL;
+}
+
+static int checkout_conflicts_load_byname_entry(
+ checkout_conflictdata **ancestor_out,
+ checkout_conflictdata **ours_out,
+ checkout_conflictdata **theirs_out,
+ checkout_data *data,
+ const git_index_name_entry *name_entry)
+{
+ checkout_conflictdata *ancestor, *ours = NULL, *theirs = NULL;
+ int error = 0;
+
+ *ancestor_out = NULL;
+ *ours_out = NULL;
+ *theirs_out = NULL;
+
+ if (!name_entry->ancestor) {
+ git_error_set(GIT_ERROR_INDEX, "a NAME entry exists without an ancestor");
+ error = -1;
+ goto done;
+ }
+
+ if (!name_entry->ours && !name_entry->theirs) {
+ git_error_set(GIT_ERROR_INDEX, "a NAME entry exists without an ours or theirs");
+ error = -1;
+ goto done;
+ }
+
+ if ((ancestor = checkout_conflicts_search_ancestor(data,
+ name_entry->ancestor)) == NULL) {
+ git_error_set(GIT_ERROR_INDEX,
+ "a NAME entry referenced ancestor entry '%s' which does not exist in the main index",
+ name_entry->ancestor);
+ error = -1;
+ goto done;
+ }
+
+ if (name_entry->ours) {
+ if (strcmp(name_entry->ancestor, name_entry->ours) == 0)
+ ours = ancestor;
+ else if ((ours = checkout_conflicts_search_branch(data, name_entry->ours)) == NULL ||
+ ours->ours == NULL) {
+ git_error_set(GIT_ERROR_INDEX,
+ "a NAME entry referenced our entry '%s' which does not exist in the main index",
+ name_entry->ours);
+ error = -1;
+ goto done;
+ }
+ }
+
+ if (name_entry->theirs) {
+ if (strcmp(name_entry->ancestor, name_entry->theirs) == 0)
+ theirs = ancestor;
+ else if (name_entry->ours && strcmp(name_entry->ours, name_entry->theirs) == 0)
+ theirs = ours;
+ else if ((theirs = checkout_conflicts_search_branch(data, name_entry->theirs)) == NULL ||
+ theirs->theirs == NULL) {
+ git_error_set(GIT_ERROR_INDEX,
+ "a NAME entry referenced their entry '%s' which does not exist in the main index",
+ name_entry->theirs);
+ error = -1;
+ goto done;
+ }
+ }
+
+ *ancestor_out = ancestor;
+ *ours_out = ours;
+ *theirs_out = theirs;
+
+done:
+ return error;
+}
+
+static int checkout_conflicts_coalesce_renames(
+ checkout_data *data)
+{
+ git_index *index;
+ const git_index_name_entry *name_entry;
+ checkout_conflictdata *ancestor_conflict, *our_conflict, *their_conflict;
+ size_t i, names;
+ int error = 0;
+
+ if ((index = git_iterator_index(data->target)) == NULL)
+ return 0;
+
+ /* Juggle entries based on renames */
+ names = git_index_name_entrycount(index);
+
+ for (i = 0; i < names; i++) {
+ name_entry = git_index_name_get_byindex(index, i);
+
+ if ((error = checkout_conflicts_load_byname_entry(
+ &ancestor_conflict, &our_conflict, &their_conflict,
+ data, name_entry)) < 0)
+ goto done;
+
+ if (our_conflict && our_conflict != ancestor_conflict) {
+ ancestor_conflict->ours = our_conflict->ours;
+ our_conflict->ours = NULL;
+
+ if (our_conflict->theirs)
+ our_conflict->name_collision = 1;
+
+ if (our_conflict->name_collision)
+ ancestor_conflict->name_collision = 1;
+ }
+
+ if (their_conflict && their_conflict != ancestor_conflict) {
+ ancestor_conflict->theirs = their_conflict->theirs;
+ their_conflict->theirs = NULL;
+
+ if (their_conflict->ours)
+ their_conflict->name_collision = 1;
+
+ if (their_conflict->name_collision)
+ ancestor_conflict->name_collision = 1;
+ }
+
+ if (our_conflict && our_conflict != ancestor_conflict &&
+ their_conflict && their_conflict != ancestor_conflict)
+ ancestor_conflict->one_to_two = 1;
+ }
+
+ git_vector_remove_matching(
+ &data->update_conflicts, checkout_conflictdata_empty, NULL);
+
+done:
+ return error;
+}
+
+static int checkout_conflicts_mark_directoryfile(
+ checkout_data *data)
+{
+ git_index *index;
+ checkout_conflictdata *conflict;
+ const git_index_entry *entry;
+ size_t i, j, len;
+ const char *path;
+ int prefixed, error = 0;
+
+ if ((index = git_iterator_index(data->target)) == NULL)
+ return 0;
+
+ len = git_index_entrycount(index);
+
+ /* Find d/f conflicts */
+ git_vector_foreach(&data->update_conflicts, i, conflict) {
+ if ((conflict->ours && conflict->theirs) ||
+ (!conflict->ours && !conflict->theirs))
+ continue;
+
+ path = conflict->ours ?
+ conflict->ours->path : conflict->theirs->path;
+
+ if ((error = git_index_find(&j, index, path)) < 0) {
+ if (error == GIT_ENOTFOUND)
+ git_error_set(GIT_ERROR_INDEX,
+ "index inconsistency, could not find entry for expected conflict '%s'", path);
+
+ goto done;
+ }
+
+ for (; j < len; j++) {
+ if ((entry = git_index_get_byindex(index, j)) == NULL) {
+ git_error_set(GIT_ERROR_INDEX,
+ "index inconsistency, truncated index while loading expected conflict '%s'", path);
+ error = -1;
+ goto done;
+ }
+
+ prefixed = git_fs_path_equal_or_prefixed(path, entry->path, NULL);
+
+ if (prefixed == GIT_FS_PATH_EQUAL)
+ continue;
+
+ if (prefixed == GIT_FS_PATH_PREFIX)
+ conflict->directoryfile = 1;
+
+ break;
+ }
+ }
+
+done:
+ return error;
+}
+
+static int checkout_get_update_conflicts(
+ checkout_data *data,
+ git_iterator *workdir,
+ git_vector *pathspec)
+{
+ int error = 0;
+
+ if (data->strategy & GIT_CHECKOUT_SKIP_UNMERGED)
+ return 0;
+
+ if ((error = checkout_conflicts_load(data, workdir, pathspec)) < 0 ||
+ (error = checkout_conflicts_coalesce_renames(data)) < 0 ||
+ (error = checkout_conflicts_mark_directoryfile(data)) < 0)
+ goto done;
+
+done:
+ return error;
+}
+
+static int checkout_conflict_append_remove(
+ const git_index_entry *ancestor,
+ const git_index_entry *ours,
+ const git_index_entry *theirs,
+ void *payload)
+{
+ checkout_data *data = payload;
+ const char *name;
+
+ GIT_ASSERT_ARG(ancestor || ours || theirs);
+
+ if (ancestor)
+ name = git__strdup(ancestor->path);
+ else if (ours)
+ name = git__strdup(ours->path);
+ else if (theirs)
+ name = git__strdup(theirs->path);
+ else
+ abort();
+
+ GIT_ERROR_CHECK_ALLOC(name);
+
+ return git_vector_insert(&data->remove_conflicts, (char *)name);
+}
+
+static int checkout_get_remove_conflicts(
+ checkout_data *data,
+ git_iterator *workdir,
+ git_vector *pathspec)
+{
+ if ((data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) != 0)
+ return 0;
+
+ return checkout_conflicts_foreach(data, data->index, workdir, pathspec, checkout_conflict_append_remove, data);
+}
+
+static int checkout_verify_paths(
+ git_repository *repo,
+ int action,
+ git_diff_delta *delta)
+{
+ unsigned int flags = GIT_PATH_REJECT_WORKDIR_DEFAULTS;
+
+ if (action & CHECKOUT_ACTION__REMOVE) {
+ if (!git_path_is_valid(repo, delta->old_file.path, delta->old_file.mode, flags)) {
+ git_error_set(GIT_ERROR_CHECKOUT, "cannot remove invalid path '%s'", delta->old_file.path);
+ return -1;
+ }
+ }
+
+ if (action & ~CHECKOUT_ACTION__REMOVE) {
+ if (!git_path_is_valid(repo, delta->new_file.path, delta->new_file.mode, flags)) {
+ git_error_set(GIT_ERROR_CHECKOUT, "cannot checkout to invalid path '%s'", delta->new_file.path);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int checkout_get_actions(
+ uint32_t **actions_ptr,
+ size_t **counts_ptr,
+ checkout_data *data,
+ git_iterator *workdir)
+{
+ int error = 0, act;
+ const git_index_entry *wditem;
+ git_vector pathspec = GIT_VECTOR_INIT, *deltas;
+ git_pool pathpool;
+ git_diff_delta *delta;
+ size_t i, *counts = NULL;
+ uint32_t *actions = NULL;
+
+ if (git_pool_init(&pathpool, 1) < 0)
+ return -1;
+
+ if (data->opts.paths.count > 0 &&
+ git_pathspec__vinit(&pathspec, &data->opts.paths, &pathpool) < 0)
+ return -1;
+
+ if ((error = git_iterator_current(&wditem, workdir)) < 0 &&
+ error != GIT_ITEROVER)
+ goto fail;
+
+ deltas = &data->diff->deltas;
+
+ *counts_ptr = counts = git__calloc(CHECKOUT_ACTION__MAX+1, sizeof(size_t));
+ *actions_ptr = actions = git__calloc(
+ deltas->length ? deltas->length : 1, sizeof(uint32_t));
+ if (!counts || !actions) {
+ error = -1;
+ goto fail;
+ }
+
+ git_vector_foreach(deltas, i, delta) {
+ if ((error = checkout_action(&act, data, delta, workdir, &wditem, &pathspec)) == 0)
+ error = checkout_verify_paths(data->repo, act, delta);
+
+ if (error != 0)
+ goto fail;
+
+ actions[i] = act;
+
+ if (act & CHECKOUT_ACTION__REMOVE)
+ counts[CHECKOUT_ACTION__REMOVE]++;
+ if (act & CHECKOUT_ACTION__UPDATE_BLOB)
+ counts[CHECKOUT_ACTION__UPDATE_BLOB]++;
+ if (act & CHECKOUT_ACTION__UPDATE_SUBMODULE)
+ counts[CHECKOUT_ACTION__UPDATE_SUBMODULE]++;
+ if (act & CHECKOUT_ACTION__CONFLICT)
+ counts[CHECKOUT_ACTION__CONFLICT]++;
+ }
+
+ error = checkout_remaining_wd_items(data, workdir, wditem, &pathspec);
+ if (error)
+ goto fail;
+
+ counts[CHECKOUT_ACTION__REMOVE] += data->removes.length;
+
+ if (counts[CHECKOUT_ACTION__CONFLICT] > 0 &&
+ (data->strategy & GIT_CHECKOUT_ALLOW_CONFLICTS) == 0) {
+ git_error_set(GIT_ERROR_CHECKOUT, "%"PRIuZ" %s checkout",
+ counts[CHECKOUT_ACTION__CONFLICT],
+ counts[CHECKOUT_ACTION__CONFLICT] == 1 ?
+ "conflict prevents" : "conflicts prevent");
+ error = GIT_ECONFLICT;
+ goto fail;
+ }
+
+
+ if ((error = checkout_get_remove_conflicts(data, workdir, &pathspec)) < 0 ||
+ (error = checkout_get_update_conflicts(data, workdir, &pathspec)) < 0)
+ goto fail;
+
+ counts[CHECKOUT_ACTION__REMOVE_CONFLICT] = git_vector_length(&data->remove_conflicts);
+ counts[CHECKOUT_ACTION__UPDATE_CONFLICT] = git_vector_length(&data->update_conflicts);
+
+ git_pathspec__vfree(&pathspec);
+ git_pool_clear(&pathpool);
+
+ return 0;
+
+fail:
+ *counts_ptr = NULL;
+ git__free(counts);
+ *actions_ptr = NULL;
+ git__free(actions);
+
+ git_pathspec__vfree(&pathspec);
+ git_pool_clear(&pathpool);
+
+ return error;
+}
+
+static bool should_remove_existing(checkout_data *data)
+{
+ int ignorecase;
+
+ if (git_repository__configmap_lookup(&ignorecase, data->repo, GIT_CONFIGMAP_IGNORECASE) < 0) {
+ ignorecase = 0;
+ }
+
+ return (ignorecase &&
+ (data->strategy & GIT_CHECKOUT_DONT_REMOVE_EXISTING) == 0);
+}
+
+#define MKDIR_NORMAL \
+ GIT_MKDIR_PATH | GIT_MKDIR_VERIFY_DIR
+#define MKDIR_REMOVE_EXISTING \
+ MKDIR_NORMAL | GIT_MKDIR_REMOVE_FILES | GIT_MKDIR_REMOVE_SYMLINKS
+
+static int checkout_mkdir(
+ checkout_data *data,
+ const char *path,
+ const char *base,
+ mode_t mode,
+ unsigned int flags)
+{
+ struct git_futils_mkdir_options mkdir_opts = {0};
+ int error;
+
+ mkdir_opts.dir_map = data->mkdir_map;
+ mkdir_opts.pool = &data->pool;
+
+ error = git_futils_mkdir_relative(
+ path, base, mode, flags, &mkdir_opts);
+
+ data->perfdata.mkdir_calls += mkdir_opts.perfdata.mkdir_calls;
+ data->perfdata.stat_calls += mkdir_opts.perfdata.stat_calls;
+ data->perfdata.chmod_calls += mkdir_opts.perfdata.chmod_calls;
+
+ return error;
+}
+
+static int mkpath2file(
+ checkout_data *data, const char *path, unsigned int mode)
+{
+ struct stat st;
+ bool remove_existing = should_remove_existing(data);
+ unsigned int flags =
+ (remove_existing ? MKDIR_REMOVE_EXISTING : MKDIR_NORMAL) |
+ GIT_MKDIR_SKIP_LAST;
+ int error;
+
+ if ((error = checkout_mkdir(
+ data, path, data->opts.target_directory, mode, flags)) < 0)
+ return error;
+
+ if (remove_existing) {
+ data->perfdata.stat_calls++;
+
+ if (p_lstat(path, &st) == 0) {
+
+ /* Some file, symlink or folder already exists at this name.
+ * We would have removed it in remove_the_old unless we're on
+ * a case inensitive filesystem (or the user has asked us not
+ * to). Remove the similarly named file to write the new.
+ */
+ error = git_futils_rmdir_r(path, NULL, GIT_RMDIR_REMOVE_FILES);
+ } else if (errno != ENOENT) {
+ git_error_set(GIT_ERROR_OS, "failed to stat '%s'", path);
+ return GIT_EEXISTS;
+ } else {
+ git_error_clear();
+ }
+ }
+
+ return error;
+}
+
+struct checkout_stream {
+ git_writestream base;
+ const char *path;
+ int fd;
+ int open;
+};
+
+static int checkout_stream_write(
+ git_writestream *s, const char *buffer, size_t len)
+{
+ struct checkout_stream *stream = (struct checkout_stream *)s;
+ int ret;
+
+ if ((ret = p_write(stream->fd, buffer, len)) < 0)
+ git_error_set(GIT_ERROR_OS, "could not write to '%s'", stream->path);
+
+ return ret;
+}
+
+static int checkout_stream_close(git_writestream *s)
+{
+ struct checkout_stream *stream = (struct checkout_stream *)s;
+
+ GIT_ASSERT_ARG(stream);
+ GIT_ASSERT_ARG(stream->open);
+
+ stream->open = 0;
+ return p_close(stream->fd);
+}
+
+static void checkout_stream_free(git_writestream *s)
+{
+ GIT_UNUSED(s);
+}
+
+static int blob_content_to_file(
+ checkout_data *data,
+ struct stat *st,
+ git_blob *blob,
+ const char *path,
+ const char *hint_path,
+ mode_t entry_filemode)
+{
+ int flags = data->opts.file_open_flags;
+ mode_t file_mode = data->opts.file_mode ?
+ data->opts.file_mode : entry_filemode;
+ git_filter_session filter_session = GIT_FILTER_SESSION_INIT;
+ struct checkout_stream writer;
+ mode_t mode;
+ git_filter_list *fl = NULL;
+ int fd;
+ int error = 0;
+
+ GIT_ASSERT(hint_path != NULL);
+
+ if ((error = mkpath2file(data, path, data->opts.dir_mode)) < 0)
+ return error;
+
+ if (flags <= 0)
+ flags = O_CREAT | O_TRUNC | O_WRONLY;
+ if (!(mode = file_mode))
+ mode = GIT_FILEMODE_BLOB;
+
+ if ((fd = p_open(path, flags, mode)) < 0) {
+ git_error_set(GIT_ERROR_OS, "could not open '%s' for writing", path);
+ return fd;
+ }
+
+ filter_session.attr_session = &data->attr_session;
+ filter_session.temp_buf = &data->tmp;
+
+ if (!data->opts.disable_filters &&
+ (error = git_filter_list__load(
+ &fl, data->repo, blob, hint_path,
+ GIT_FILTER_TO_WORKTREE, &filter_session))) {
+ p_close(fd);
+ return error;
+ }
+
+ /* setup the writer */
+ memset(&writer, 0, sizeof(struct checkout_stream));
+ writer.base.write = checkout_stream_write;
+ writer.base.close = checkout_stream_close;
+ writer.base.free = checkout_stream_free;
+ writer.path = path;
+ writer.fd = fd;
+ writer.open = 1;
+
+ error = git_filter_list_stream_blob(fl, blob, &writer.base);
+
+ GIT_ASSERT(writer.open == 0);
+
+ git_filter_list_free(fl);
+
+ if (error < 0)
+ return error;
+
+ if (st) {
+ data->perfdata.stat_calls++;
+
+ if ((error = p_stat(path, st)) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to stat '%s'", path);
+ return error;
+ }
+
+ st->st_mode = entry_filemode;
+ }
+
+ return 0;
+}
+
+static int blob_content_to_link(
+ checkout_data *data,
+ struct stat *st,
+ git_blob *blob,
+ const char *path)
+{
+ git_str linktarget = GIT_STR_INIT;
+ int error;
+
+ if ((error = mkpath2file(data, path, data->opts.dir_mode)) < 0)
+ return error;
+
+ if ((error = git_blob__getbuf(&linktarget, blob)) < 0)
+ return error;
+
+ if (data->can_symlink) {
+ if ((error = p_symlink(git_str_cstr(&linktarget), path)) < 0)
+ git_error_set(GIT_ERROR_OS, "could not create symlink %s", path);
+ } else {
+ error = git_futils_fake_symlink(git_str_cstr(&linktarget), path);
+ }
+
+ if (!error) {
+ data->perfdata.stat_calls++;
+
+ if ((error = p_lstat(path, st)) < 0)
+ git_error_set(GIT_ERROR_CHECKOUT, "could not stat symlink %s", path);
+
+ st->st_mode = GIT_FILEMODE_LINK;
+ }
+
+ git_str_dispose(&linktarget);
+
+ return error;
+}
+
+static int checkout_update_index(
+ checkout_data *data,
+ const git_diff_file *file,
+ struct stat *st)
+{
+ git_index_entry entry;
+
+ if (!data->index)
+ return 0;
+
+ memset(&entry, 0, sizeof(entry));
+ entry.path = (char *)file->path; /* cast to prevent warning */
+ git_index_entry__init_from_stat(&entry, st, true);
+ git_oid_cpy(&entry.id, &file->id);
+
+ return git_index_add(data->index, &entry);
+}
+
+static int checkout_submodule_update_index(
+ checkout_data *data,
+ const git_diff_file *file)
+{
+ git_str *fullpath;
+ struct stat st;
+
+ /* update the index unless prevented */
+ if ((data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) != 0)
+ return 0;
+
+ if (checkout_target_fullpath(&fullpath, data, file->path) < 0)
+ return -1;
+
+ data->perfdata.stat_calls++;
+ if (p_stat(fullpath->ptr, &st) < 0) {
+ git_error_set(
+ GIT_ERROR_CHECKOUT, "could not stat submodule %s\n", file->path);
+ return GIT_ENOTFOUND;
+ }
+
+ st.st_mode = GIT_FILEMODE_COMMIT;
+
+ return checkout_update_index(data, file, &st);
+}
+
+static int checkout_submodule(
+ checkout_data *data,
+ const git_diff_file *file)
+{
+ bool remove_existing = should_remove_existing(data);
+ int error = 0;
+
+ /* Until submodules are supported, UPDATE_ONLY means do nothing here */
+ if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0)
+ return 0;
+
+ if ((error = checkout_mkdir(
+ data,
+ file->path, data->opts.target_directory, data->opts.dir_mode,
+ remove_existing ? MKDIR_REMOVE_EXISTING : MKDIR_NORMAL)) < 0)
+ return error;
+
+ if ((error = git_submodule_lookup(NULL, data->repo, file->path)) < 0) {
+ /* I've observed repos with submodules in the tree that do not
+ * have a .gitmodules - core Git just makes an empty directory
+ */
+ if (error == GIT_ENOTFOUND) {
+ git_error_clear();
+ return checkout_submodule_update_index(data, file);
+ }
+
+ return error;
+ }
+
+ /* TODO: Support checkout_strategy options. Two circumstances:
+ * 1 - submodule already checked out, but we need to move the HEAD
+ * to the new OID, or
+ * 2 - submodule not checked out and we should recursively check it out
+ *
+ * Checkout will not execute a pull on the submodule, but a clone
+ * command should probably be able to. Do we need a submodule callback?
+ */
+
+ return checkout_submodule_update_index(data, file);
+}
+
+static void report_progress(
+ checkout_data *data,
+ const char *path)
+{
+ if (data->opts.progress_cb)
+ data->opts.progress_cb(
+ path, data->completed_steps, data->total_steps,
+ data->opts.progress_payload);
+}
+
+static int checkout_safe_for_update_only(
+ checkout_data *data, const char *path, mode_t expected_mode)
+{
+ struct stat st;
+
+ data->perfdata.stat_calls++;
+
+ if (p_lstat(path, &st) < 0) {
+ /* if doesn't exist, then no error and no update */
+ if (errno == ENOENT || errno == ENOTDIR)
+ return 0;
+
+ /* otherwise, stat error and no update */
+ git_error_set(GIT_ERROR_OS, "failed to stat '%s'", path);
+ return -1;
+ }
+
+ /* only safe for update if this is the same type of file */
+ if ((st.st_mode & ~0777) == (expected_mode & ~0777))
+ return 1;
+
+ return 0;
+}
+
+static int checkout_write_content(
+ checkout_data *data,
+ const git_oid *oid,
+ const char *full_path,
+ const char *hint_path,
+ unsigned int mode,
+ struct stat *st)
+{
+ int error = 0;
+ git_blob *blob;
+
+ if ((error = git_blob_lookup(&blob, data->repo, oid)) < 0)
+ return error;
+
+ if (S_ISLNK(mode))
+ error = blob_content_to_link(data, st, blob, full_path);
+ else
+ error = blob_content_to_file(data, st, blob, full_path, hint_path, mode);
+
+ git_blob_free(blob);
+
+ /* if we try to create the blob and an existing directory blocks it from
+ * being written, then there must have been a typechange conflict in a
+ * parent directory - suppress the error and try to continue.
+ */
+ if ((data->strategy & GIT_CHECKOUT_ALLOW_CONFLICTS) != 0 &&
+ (error == GIT_ENOTFOUND || error == GIT_EEXISTS))
+ {
+ git_error_clear();
+ error = 0;
+ }
+
+ return error;
+}
+
+static int checkout_blob(
+ checkout_data *data,
+ const git_diff_file *file)
+{
+ git_str *fullpath;
+ struct stat st;
+ int error = 0;
+
+ if (checkout_target_fullpath(&fullpath, data, file->path) < 0)
+ return -1;
+
+ if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0) {
+ int rval = checkout_safe_for_update_only(
+ data, fullpath->ptr, file->mode);
+
+ if (rval <= 0)
+ return rval;
+ }
+
+ error = checkout_write_content(
+ data, &file->id, fullpath->ptr, file->path, file->mode, &st);
+
+ /* update the index unless prevented */
+ if (!error && (data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0)
+ error = checkout_update_index(data, file, &st);
+
+ /* update the submodule data if this was a new .gitmodules file */
+ if (!error && strcmp(file->path, ".gitmodules") == 0)
+ data->reload_submodules = true;
+
+ return error;
+}
+
+static int checkout_remove_the_old(
+ unsigned int *actions,
+ checkout_data *data)
+{
+ int error = 0;
+ git_diff_delta *delta;
+ const char *str;
+ size_t i;
+ git_str *fullpath;
+ uint32_t flg = GIT_RMDIR_EMPTY_PARENTS |
+ GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_REMOVE_BLOCKERS;
+
+ if (data->opts.checkout_strategy & GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES)
+ flg |= GIT_RMDIR_SKIP_NONEMPTY;
+
+ if (checkout_target_fullpath(&fullpath, data, NULL) < 0)
+ return -1;
+
+ git_vector_foreach(&data->diff->deltas, i, delta) {
+ if (actions[i] & CHECKOUT_ACTION__REMOVE) {
+ error = git_futils_rmdir_r(
+ delta->old_file.path, fullpath->ptr, flg);
+
+ if (error < 0)
+ return error;
+
+ data->completed_steps++;
+ report_progress(data, delta->old_file.path);
+
+ if ((actions[i] & CHECKOUT_ACTION__UPDATE_BLOB) == 0 &&
+ (data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0 &&
+ data->index != NULL)
+ {
+ (void)git_index_remove(data->index, delta->old_file.path, 0);
+ }
+ }
+ }
+
+ git_vector_foreach(&data->removes, i, str) {
+ error = git_futils_rmdir_r(str, fullpath->ptr, flg);
+ if (error < 0)
+ return error;
+
+ data->completed_steps++;
+ report_progress(data, str);
+
+ if ((data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0 &&
+ data->index != NULL)
+ {
+ if (str[strlen(str) - 1] == '/')
+ (void)git_index_remove_directory(data->index, str, 0);
+ else
+ (void)git_index_remove(data->index, str, 0);
+ }
+ }
+
+ return 0;
+}
+
+static int checkout_create_the_new(
+ unsigned int *actions,
+ checkout_data *data)
+{
+ int error = 0;
+ git_diff_delta *delta;
+ size_t i;
+
+ git_vector_foreach(&data->diff->deltas, i, delta) {
+ if (actions[i] & CHECKOUT_ACTION__UPDATE_BLOB && !S_ISLNK(delta->new_file.mode)) {
+ if ((error = checkout_blob(data, &delta->new_file)) < 0)
+ return error;
+ data->completed_steps++;
+ report_progress(data, delta->new_file.path);
+ }
+ }
+
+ git_vector_foreach(&data->diff->deltas, i, delta) {
+ if (actions[i] & CHECKOUT_ACTION__UPDATE_BLOB && S_ISLNK(delta->new_file.mode)) {
+ if ((error = checkout_blob(data, &delta->new_file)) < 0)
+ return error;
+ data->completed_steps++;
+ report_progress(data, delta->new_file.path);
+ }
+ }
+
+ return 0;
+}
+
+static int checkout_create_submodules(
+ unsigned int *actions,
+ checkout_data *data)
+{
+ git_diff_delta *delta;
+ size_t i;
+
+ git_vector_foreach(&data->diff->deltas, i, delta) {
+ if (actions[i] & CHECKOUT_ACTION__UPDATE_SUBMODULE) {
+ int error = checkout_submodule(data, &delta->new_file);
+ if (error < 0)
+ return error;
+
+ data->completed_steps++;
+ report_progress(data, delta->new_file.path);
+ }
+ }
+
+ return 0;
+}
+
+static int checkout_lookup_head_tree(git_tree **out, git_repository *repo)
+{
+ int error = 0;
+ git_reference *ref = NULL;
+ git_object *head;
+
+ if (!(error = git_repository_head(&ref, repo)) &&
+ !(error = git_reference_peel(&head, ref, GIT_OBJECT_TREE)))
+ *out = (git_tree *)head;
+
+ git_reference_free(ref);
+
+ return error;
+}
+
+
+static int conflict_entry_name(
+ git_str *out,
+ const char *side_name,
+ const char *filename)
+{
+ if (git_str_puts(out, side_name) < 0 ||
+ git_str_putc(out, ':') < 0 ||
+ git_str_puts(out, filename) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int checkout_path_suffixed(git_str *path, const char *suffix)
+{
+ size_t path_len;
+ int i = 0, error = 0;
+
+ if ((error = git_str_putc(path, '~')) < 0 || (error = git_str_puts(path, suffix)) < 0)
+ return -1;
+
+ path_len = git_str_len(path);
+
+ while (git_fs_path_exists(git_str_cstr(path)) && i < INT_MAX) {
+ git_str_truncate(path, path_len);
+
+ if ((error = git_str_putc(path, '_')) < 0 ||
+ (error = git_str_printf(path, "%d", i)) < 0)
+ return error;
+
+ i++;
+ }
+
+ if (i == INT_MAX) {
+ git_str_truncate(path, path_len);
+
+ git_error_set(GIT_ERROR_CHECKOUT, "could not write '%s': working directory file exists", path->ptr);
+ return GIT_EEXISTS;
+ }
+
+ return 0;
+}
+
+static int checkout_write_entry(
+ checkout_data *data,
+ checkout_conflictdata *conflict,
+ const git_index_entry *side)
+{
+ const char *hint_path = NULL, *suffix;
+ git_str *fullpath;
+ struct stat st;
+ int error;
+
+ GIT_ASSERT(side == conflict->ours || side == conflict->theirs);
+
+ if (checkout_target_fullpath(&fullpath, data, side->path) < 0)
+ return -1;
+
+ if ((conflict->name_collision || conflict->directoryfile) &&
+ (data->strategy & GIT_CHECKOUT_USE_OURS) == 0 &&
+ (data->strategy & GIT_CHECKOUT_USE_THEIRS) == 0) {
+
+ if (side == conflict->ours)
+ suffix = data->opts.our_label ? data->opts.our_label :
+ "ours";
+ else
+ suffix = data->opts.their_label ? data->opts.their_label :
+ "theirs";
+
+ if (checkout_path_suffixed(fullpath, suffix) < 0)
+ return -1;
+ }
+
+ hint_path = side->path;
+
+ if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0 &&
+ (error = checkout_safe_for_update_only(data, fullpath->ptr, side->mode)) <= 0)
+ return error;
+
+ if (!S_ISGITLINK(side->mode))
+ return checkout_write_content(data,
+ &side->id, fullpath->ptr, hint_path, side->mode, &st);
+
+ return 0;
+}
+
+static int checkout_write_entries(
+ checkout_data *data,
+ checkout_conflictdata *conflict)
+{
+ int error = 0;
+
+ if ((error = checkout_write_entry(data, conflict, conflict->ours)) >= 0)
+ error = checkout_write_entry(data, conflict, conflict->theirs);
+
+ return error;
+}
+
+static int checkout_merge_path(
+ git_str *out,
+ checkout_data *data,
+ checkout_conflictdata *conflict,
+ git_merge_file_result *result)
+{
+ const char *our_label_raw, *their_label_raw, *suffix;
+ int error = 0;
+
+ if ((error = git_str_joinpath(out, data->opts.target_directory, result->path)) < 0 ||
+ (error = git_path_validate_str_length(data->repo, out)) < 0)
+ return error;
+
+ /* Most conflicts simply use the filename in the index */
+ if (!conflict->name_collision)
+ return 0;
+
+ /* Rename 2->1 conflicts need the branch name appended */
+ our_label_raw = data->opts.our_label ? data->opts.our_label : "ours";
+ their_label_raw = data->opts.their_label ? data->opts.their_label : "theirs";
+ suffix = strcmp(result->path, conflict->ours->path) == 0 ? our_label_raw : their_label_raw;
+
+ if ((error = checkout_path_suffixed(out, suffix)) < 0)
+ return error;
+
+ return 0;
+}
+
+static int checkout_write_merge(
+ checkout_data *data,
+ checkout_conflictdata *conflict)
+{
+ git_str our_label = GIT_STR_INIT, their_label = GIT_STR_INIT,
+ path_suffixed = GIT_STR_INIT, path_workdir = GIT_STR_INIT,
+ in_data = GIT_STR_INIT, out_data = GIT_STR_INIT;
+ git_merge_file_options opts = GIT_MERGE_FILE_OPTIONS_INIT;
+ git_merge_file_result result = {0};
+ git_filebuf output = GIT_FILEBUF_INIT;
+ git_filter_list *fl = NULL;
+ git_filter_session filter_session = GIT_FILTER_SESSION_INIT;
+ int error = 0;
+
+ if (data->opts.checkout_strategy & GIT_CHECKOUT_CONFLICT_STYLE_DIFF3)
+ opts.flags |= GIT_MERGE_FILE_STYLE_DIFF3;
+
+ if (data->opts.checkout_strategy & GIT_CHECKOUT_CONFLICT_STYLE_ZDIFF3)
+ opts.flags |= GIT_MERGE_FILE_STYLE_ZDIFF3;
+
+ opts.ancestor_label = data->opts.ancestor_label ?
+ data->opts.ancestor_label : "ancestor";
+ opts.our_label = data->opts.our_label ?
+ data->opts.our_label : "ours";
+ opts.their_label = data->opts.their_label ?
+ data->opts.their_label : "theirs";
+
+ /* If all the paths are identical, decorate the diff3 file with the branch
+ * names. Otherwise, append branch_name:path.
+ */
+ if (conflict->ours && conflict->theirs &&
+ strcmp(conflict->ours->path, conflict->theirs->path) != 0) {
+
+ if ((error = conflict_entry_name(
+ &our_label, opts.our_label, conflict->ours->path)) < 0 ||
+ (error = conflict_entry_name(
+ &their_label, opts.their_label, conflict->theirs->path)) < 0)
+ goto done;
+
+ opts.our_label = git_str_cstr(&our_label);
+ opts.their_label = git_str_cstr(&their_label);
+ }
+
+ if ((error = git_merge_file_from_index(&result, data->repo,
+ conflict->ancestor, conflict->ours, conflict->theirs, &opts)) < 0)
+ goto done;
+
+ if (result.path == NULL || result.mode == 0) {
+ git_error_set(GIT_ERROR_CHECKOUT, "could not merge contents of file");
+ error = GIT_ECONFLICT;
+ goto done;
+ }
+
+ if ((error = checkout_merge_path(&path_workdir, data, conflict, &result)) < 0)
+ goto done;
+
+ if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0 &&
+ (error = checkout_safe_for_update_only(data, git_str_cstr(&path_workdir), result.mode)) <= 0)
+ goto done;
+
+ if (!data->opts.disable_filters) {
+ in_data.ptr = (char *)result.ptr;
+ in_data.size = result.len;
+
+ filter_session.attr_session = &data->attr_session;
+ filter_session.temp_buf = &data->tmp;
+
+ if ((error = git_filter_list__load(
+ &fl, data->repo, NULL, result.path,
+ GIT_FILTER_TO_WORKTREE, &filter_session)) < 0 ||
+ (error = git_filter_list__convert_buf(&out_data, fl, &in_data)) < 0)
+ goto done;
+ } else {
+ out_data.ptr = (char *)result.ptr;
+ out_data.size = result.len;
+ }
+
+ if ((error = mkpath2file(data, path_workdir.ptr, data->opts.dir_mode)) < 0 ||
+ (error = git_filebuf_open(&output, git_str_cstr(&path_workdir), GIT_FILEBUF_DO_NOT_BUFFER, result.mode)) < 0 ||
+ (error = git_filebuf_write(&output, out_data.ptr, out_data.size)) < 0 ||
+ (error = git_filebuf_commit(&output)) < 0)
+ goto done;
+
+done:
+ git_filter_list_free(fl);
+
+ git_str_dispose(&out_data);
+ git_str_dispose(&our_label);
+ git_str_dispose(&their_label);
+
+ git_merge_file_result_free(&result);
+ git_str_dispose(&path_workdir);
+ git_str_dispose(&path_suffixed);
+
+ return error;
+}
+
+static int checkout_conflict_add(
+ checkout_data *data,
+ const git_index_entry *conflict)
+{
+ int error = git_index_remove(data->index, conflict->path, 0);
+
+ if (error == GIT_ENOTFOUND)
+ git_error_clear();
+ else if (error < 0)
+ return error;
+
+ return git_index_add(data->index, conflict);
+}
+
+static int checkout_conflict_update_index(
+ checkout_data *data,
+ checkout_conflictdata *conflict)
+{
+ int error = 0;
+
+ if (conflict->ancestor)
+ error = checkout_conflict_add(data, conflict->ancestor);
+
+ if (!error && conflict->ours)
+ error = checkout_conflict_add(data, conflict->ours);
+
+ if (!error && conflict->theirs)
+ error = checkout_conflict_add(data, conflict->theirs);
+
+ return error;
+}
+
+static int checkout_create_conflicts(checkout_data *data)
+{
+ checkout_conflictdata *conflict;
+ size_t i;
+ int error = 0;
+
+ git_vector_foreach(&data->update_conflicts, i, conflict) {
+
+ /* Both deleted: nothing to do */
+ if (conflict->ours == NULL && conflict->theirs == NULL)
+ error = 0;
+
+ else if ((data->strategy & GIT_CHECKOUT_USE_OURS) &&
+ conflict->ours)
+ error = checkout_write_entry(data, conflict, conflict->ours);
+ else if ((data->strategy & GIT_CHECKOUT_USE_THEIRS) &&
+ conflict->theirs)
+ error = checkout_write_entry(data, conflict, conflict->theirs);
+
+ /* Ignore the other side of name collisions. */
+ else if ((data->strategy & GIT_CHECKOUT_USE_OURS) &&
+ !conflict->ours && conflict->name_collision)
+ error = 0;
+ else if ((data->strategy & GIT_CHECKOUT_USE_THEIRS) &&
+ !conflict->theirs && conflict->name_collision)
+ error = 0;
+
+ /* For modify/delete, name collisions and d/f conflicts, write
+ * the file (potentially with the name mangled.
+ */
+ else if (conflict->ours != NULL && conflict->theirs == NULL)
+ error = checkout_write_entry(data, conflict, conflict->ours);
+ else if (conflict->ours == NULL && conflict->theirs != NULL)
+ error = checkout_write_entry(data, conflict, conflict->theirs);
+
+ /* Add/add conflicts and rename 1->2 conflicts, write the
+ * ours/theirs sides (potentially name mangled).
+ */
+ else if (conflict->one_to_two)
+ error = checkout_write_entries(data, conflict);
+
+ /* If all sides are links, write the ours side */
+ else if (S_ISLNK(conflict->ours->mode) &&
+ S_ISLNK(conflict->theirs->mode))
+ error = checkout_write_entry(data, conflict, conflict->ours);
+ /* Link/file conflicts, write the file side */
+ else if (S_ISLNK(conflict->ours->mode))
+ error = checkout_write_entry(data, conflict, conflict->theirs);
+ else if (S_ISLNK(conflict->theirs->mode))
+ error = checkout_write_entry(data, conflict, conflict->ours);
+
+ /* If any side is a gitlink, do nothing. */
+ else if (conflict->submodule)
+ error = 0;
+
+ /* If any side is binary, write the ours side */
+ else if (conflict->binary)
+ error = checkout_write_entry(data, conflict, conflict->ours);
+
+ else if (!error)
+ error = checkout_write_merge(data, conflict);
+
+ /* Update the index extensions (REUC and NAME) if we're checking
+ * out a different index. (Otherwise just leave them there.)
+ */
+ if (!error && (data->strategy & GIT_CHECKOUT_DONT_UPDATE_INDEX) == 0)
+ error = checkout_conflict_update_index(data, conflict);
+
+ if (error)
+ break;
+
+ data->completed_steps++;
+ report_progress(data,
+ conflict->ours ? conflict->ours->path :
+ (conflict->theirs ? conflict->theirs->path : conflict->ancestor->path));
+ }
+
+ return error;
+}
+
+static int checkout_remove_conflicts(checkout_data *data)
+{
+ const char *conflict;
+ size_t i;
+
+ git_vector_foreach(&data->remove_conflicts, i, conflict) {
+ if (git_index_conflict_remove(data->index, conflict) < 0)
+ return -1;
+
+ data->completed_steps++;
+ }
+
+ return 0;
+}
+
+static int checkout_extensions_update_index(checkout_data *data)
+{
+ const git_index_reuc_entry *reuc_entry;
+ const git_index_name_entry *name_entry;
+ size_t i;
+ int error = 0;
+
+ if ((data->strategy & GIT_CHECKOUT_UPDATE_ONLY) != 0)
+ return 0;
+
+ if (data->update_reuc) {
+ git_vector_foreach(data->update_reuc, i, reuc_entry) {
+ if ((error = git_index_reuc_add(data->index, reuc_entry->path,
+ reuc_entry->mode[0], &reuc_entry->oid[0],
+ reuc_entry->mode[1], &reuc_entry->oid[1],
+ reuc_entry->mode[2], &reuc_entry->oid[2])) < 0)
+ goto done;
+ }
+ }
+
+ if (data->update_names) {
+ git_vector_foreach(data->update_names, i, name_entry) {
+ if ((error = git_index_name_add(data->index, name_entry->ancestor,
+ name_entry->ours, name_entry->theirs)) < 0)
+ goto done;
+ }
+ }
+
+done:
+ return error;
+}
+
+static void checkout_data_clear(checkout_data *data)
+{
+ if (data->opts_free_baseline) {
+ git_tree_free(data->opts.baseline);
+ data->opts.baseline = NULL;
+ }
+
+ git_vector_free(&data->removes);
+ git_pool_clear(&data->pool);
+
+ git_vector_free_deep(&data->remove_conflicts);
+ git_vector_free_deep(&data->update_conflicts);
+
+ git__free(data->pfx);
+ data->pfx = NULL;
+
+ git_str_dispose(&data->target_path);
+ git_str_dispose(&data->tmp);
+
+ git_index_free(data->index);
+ data->index = NULL;
+
+ git_strmap_free(data->mkdir_map);
+ data->mkdir_map = NULL;
+
+ git_attr_session__free(&data->attr_session);
+}
+
+static int validate_target_directory(checkout_data *data)
+{
+ int error;
+
+ if ((error = git_path_validate_length(data->repo, data->opts.target_directory)) < 0)
+ return error;
+
+ if (git_fs_path_isdir(data->opts.target_directory))
+ return 0;
+
+ error = checkout_mkdir(data, data->opts.target_directory, NULL,
+ GIT_DIR_MODE, GIT_MKDIR_VERIFY_DIR);
+
+ return error;
+}
+
+static int checkout_data_init(
+ checkout_data *data,
+ git_iterator *target,
+ const git_checkout_options *proposed)
+{
+ int error = 0;
+ git_repository *repo = git_iterator_owner(target);
+
+ memset(data, 0, sizeof(*data));
+
+ if (!repo) {
+ git_error_set(GIT_ERROR_CHECKOUT, "cannot checkout nothing");
+ return -1;
+ }
+
+ if ((!proposed || !proposed->target_directory) &&
+ (error = git_repository__ensure_not_bare(repo, "checkout")) < 0)
+ return error;
+
+ data->repo = repo;
+ data->target = target;
+
+ GIT_ERROR_CHECK_VERSION(
+ proposed, GIT_CHECKOUT_OPTIONS_VERSION, "git_checkout_options");
+
+ if (!proposed)
+ GIT_INIT_STRUCTURE(&data->opts, GIT_CHECKOUT_OPTIONS_VERSION);
+ else
+ memmove(&data->opts, proposed, sizeof(git_checkout_options));
+
+ if (!data->opts.target_directory)
+ data->opts.target_directory = git_repository_workdir(repo);
+ else if ((error = validate_target_directory(data)) < 0)
+ goto cleanup;
+
+ if ((error = git_repository_index(&data->index, data->repo)) < 0)
+ goto cleanup;
+
+ /* refresh config and index content unless NO_REFRESH is given */
+ if ((data->opts.checkout_strategy & GIT_CHECKOUT_NO_REFRESH) == 0) {
+ git_config *cfg;
+
+ if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
+ goto cleanup;
+
+ /* Reload the repository index (unless we're checking out the
+ * index; then it has the changes we're trying to check out
+ * and those should not be overwritten.)
+ */
+ if (data->index != git_iterator_index(target)) {
+ if (data->opts.checkout_strategy & GIT_CHECKOUT_FORCE) {
+ /* When forcing, we can blindly re-read the index */
+ if ((error = git_index_read(data->index, false)) < 0)
+ goto cleanup;
+ } else {
+ /*
+ * When not being forced, we need to check for unresolved
+ * conflicts and unsaved changes in the index before
+ * proceeding.
+ */
+ if (git_index_has_conflicts(data->index)) {
+ error = GIT_ECONFLICT;
+ git_error_set(GIT_ERROR_CHECKOUT,
+ "unresolved conflicts exist in the index");
+ goto cleanup;
+ }
+
+ if ((error = git_index_read_safely(data->index)) < 0)
+ goto cleanup;
+ }
+
+ /* clean conflict data in the current index */
+ git_index_name_clear(data->index);
+ git_index_reuc_clear(data->index);
+ }
+ }
+
+ /* if you are forcing, allow all safe updates, plus recreate missing */
+ if ((data->opts.checkout_strategy & GIT_CHECKOUT_FORCE) != 0)
+ data->opts.checkout_strategy |= GIT_CHECKOUT_SAFE |
+ GIT_CHECKOUT_RECREATE_MISSING;
+
+ /* if the repository does not actually have an index file, then this
+ * is an initial checkout (perhaps from clone), so we allow safe updates
+ */
+ if (!data->index->on_disk &&
+ (data->opts.checkout_strategy & GIT_CHECKOUT_SAFE) != 0)
+ data->opts.checkout_strategy |= GIT_CHECKOUT_RECREATE_MISSING;
+
+ data->strategy = data->opts.checkout_strategy;
+
+ /* opts->disable_filters is false by default */
+
+ if (!data->opts.dir_mode)
+ data->opts.dir_mode = GIT_DIR_MODE;
+
+ if (!data->opts.file_open_flags)
+ data->opts.file_open_flags = O_CREAT | O_TRUNC | O_WRONLY;
+
+ data->pfx = git_pathspec_prefix(&data->opts.paths);
+
+ if ((error = git_repository__configmap_lookup(
+ &data->can_symlink, repo, GIT_CONFIGMAP_SYMLINKS)) < 0)
+ goto cleanup;
+
+ if ((error = git_repository__configmap_lookup(
+ &data->respect_filemode, repo, GIT_CONFIGMAP_FILEMODE)) < 0)
+ goto cleanup;
+
+ if (!data->opts.baseline && !data->opts.baseline_index) {
+ data->opts_free_baseline = true;
+ error = 0;
+
+ /* if we don't have an index, this is an initial checkout and
+ * should be against an empty baseline
+ */
+ if (data->index->on_disk)
+ error = checkout_lookup_head_tree(&data->opts.baseline, repo);
+
+ if (error == GIT_EUNBORNBRANCH) {
+ error = 0;
+ git_error_clear();
+ }
+
+ if (error < 0)
+ goto cleanup;
+ }
+
+ if ((data->opts.checkout_strategy &
+ (GIT_CHECKOUT_CONFLICT_STYLE_MERGE | GIT_CHECKOUT_CONFLICT_STYLE_DIFF3)) == 0) {
+ git_config_entry *conflict_style = NULL;
+ git_config *cfg = NULL;
+
+ if ((error = git_repository_config__weakptr(&cfg, repo)) < 0 ||
+ (error = git_config_get_entry(&conflict_style, cfg, "merge.conflictstyle")) < 0 ||
+ error == GIT_ENOTFOUND)
+ ;
+ else if (error)
+ goto cleanup;
+ else if (strcmp(conflict_style->value, "merge") == 0)
+ data->opts.checkout_strategy |= GIT_CHECKOUT_CONFLICT_STYLE_MERGE;
+ else if (strcmp(conflict_style->value, "diff3") == 0)
+ data->opts.checkout_strategy |= GIT_CHECKOUT_CONFLICT_STYLE_DIFF3;
+ else if (strcmp(conflict_style->value, "zdiff3") == 0)
+ data->opts.checkout_strategy |= GIT_CHECKOUT_CONFLICT_STYLE_ZDIFF3;
+ else {
+ git_error_set(GIT_ERROR_CHECKOUT, "unknown style '%s' given for 'merge.conflictstyle'",
+ conflict_style->value);
+ error = -1;
+ git_config_entry_free(conflict_style);
+ goto cleanup;
+ }
+ git_config_entry_free(conflict_style);
+ }
+
+ if ((error = git_pool_init(&data->pool, 1)) < 0 ||
+ (error = git_vector_init(&data->removes, 0, git__strcmp_cb)) < 0 ||
+ (error = git_vector_init(&data->remove_conflicts, 0, NULL)) < 0 ||
+ (error = git_vector_init(&data->update_conflicts, 0, NULL)) < 0 ||
+ (error = git_str_puts(&data->target_path, data->opts.target_directory)) < 0 ||
+ (error = git_fs_path_to_dir(&data->target_path)) < 0 ||
+ (error = git_strmap_new(&data->mkdir_map)) < 0)
+ goto cleanup;
+
+ data->target_len = git_str_len(&data->target_path);
+
+ git_attr_session__init(&data->attr_session, data->repo);
+
+cleanup:
+ if (error < 0)
+ checkout_data_clear(data);
+
+ return error;
+}
+
+#define CHECKOUT_INDEX_DONT_WRITE_MASK \
+ (GIT_CHECKOUT_DONT_UPDATE_INDEX | GIT_CHECKOUT_DONT_WRITE_INDEX)
+
+GIT_INLINE(void) setup_pathspecs(
+ git_iterator_options *iter_opts,
+ const git_checkout_options *checkout_opts)
+{
+ if (checkout_opts &&
+ (checkout_opts->checkout_strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH)) {
+ iter_opts->pathlist.count = checkout_opts->paths.count;
+ iter_opts->pathlist.strings = checkout_opts->paths.strings;
+ }
+}
+
+int git_checkout_iterator(
+ git_iterator *target,
+ git_index *index,
+ const git_checkout_options *opts)
+{
+ int error = 0;
+ git_iterator *baseline = NULL, *workdir = NULL;
+ git_iterator_options baseline_opts = GIT_ITERATOR_OPTIONS_INIT,
+ workdir_opts = GIT_ITERATOR_OPTIONS_INIT;
+ checkout_data data = {0};
+ git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
+ uint32_t *actions = NULL;
+ size_t *counts = NULL;
+
+ /* initialize structures and options */
+ error = checkout_data_init(&data, target, opts);
+ if (error < 0)
+ return error;
+
+ diff_opts.flags =
+ GIT_DIFF_INCLUDE_UNMODIFIED |
+ GIT_DIFF_INCLUDE_UNREADABLE |
+ GIT_DIFF_INCLUDE_UNTRACKED |
+ GIT_DIFF_RECURSE_UNTRACKED_DIRS | /* needed to match baseline */
+ GIT_DIFF_INCLUDE_IGNORED |
+ GIT_DIFF_INCLUDE_TYPECHANGE |
+ GIT_DIFF_INCLUDE_TYPECHANGE_TREES |
+ GIT_DIFF_SKIP_BINARY_CHECK |
+ GIT_DIFF_INCLUDE_CASECHANGE;
+ if (data.opts.checkout_strategy & GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH)
+ diff_opts.flags |= GIT_DIFF_DISABLE_PATHSPEC_MATCH;
+ if (data.opts.paths.count > 0)
+ diff_opts.pathspec = data.opts.paths;
+
+ /* set up iterators */
+
+ workdir_opts.flags = git_iterator_ignore_case(target) ?
+ GIT_ITERATOR_IGNORE_CASE : GIT_ITERATOR_DONT_IGNORE_CASE;
+ workdir_opts.flags |= GIT_ITERATOR_DONT_AUTOEXPAND;
+ workdir_opts.start = data.pfx;
+ workdir_opts.end = data.pfx;
+
+ setup_pathspecs(&workdir_opts, opts);
+
+ if ((error = git_iterator_reset_range(target, data.pfx, data.pfx)) < 0 ||
+ (error = git_iterator_for_workdir_ext(
+ &workdir, data.repo, data.opts.target_directory, index, NULL,
+ &workdir_opts)) < 0)
+ goto cleanup;
+
+ baseline_opts.flags = git_iterator_ignore_case(target) ?
+ GIT_ITERATOR_IGNORE_CASE : GIT_ITERATOR_DONT_IGNORE_CASE;
+ baseline_opts.start = data.pfx;
+ baseline_opts.end = data.pfx;
+
+ setup_pathspecs(&baseline_opts, opts);
+
+ if (data.opts.baseline_index) {
+ if ((error = git_iterator_for_index(
+ &baseline, git_index_owner(data.opts.baseline_index),
+ data.opts.baseline_index, &baseline_opts)) < 0)
+ goto cleanup;
+ } else {
+ if ((error = git_iterator_for_tree(
+ &baseline, data.opts.baseline, &baseline_opts)) < 0)
+ goto cleanup;
+ }
+
+ /* Should not have case insensitivity mismatch */
+ GIT_ASSERT(git_iterator_ignore_case(workdir) == git_iterator_ignore_case(baseline));
+
+ /* Generate baseline-to-target diff which will include an entry for
+ * every possible update that might need to be made.
+ */
+ if ((error = git_diff__from_iterators(
+ &data.diff, data.repo, baseline, target, &diff_opts)) < 0)
+ goto cleanup;
+
+ /* Loop through diff (and working directory iterator) building a list of
+ * actions to be taken, plus look for conflicts and send notifications,
+ * then loop through conflicts.
+ */
+ if ((error = checkout_get_actions(&actions, &counts, &data, workdir)) != 0)
+ goto cleanup;
+
+ if (data.strategy & GIT_CHECKOUT_DRY_RUN)
+ goto cleanup;
+
+ data.total_steps = counts[CHECKOUT_ACTION__REMOVE] +
+ counts[CHECKOUT_ACTION__REMOVE_CONFLICT] +
+ counts[CHECKOUT_ACTION__UPDATE_BLOB] +
+ counts[CHECKOUT_ACTION__UPDATE_SUBMODULE] +
+ counts[CHECKOUT_ACTION__UPDATE_CONFLICT];
+
+ report_progress(&data, NULL); /* establish 0 baseline */
+
+ /* To deal with some order dependencies, perform remaining checkout
+ * in three passes: removes, then update blobs, then update submodules.
+ */
+ if (counts[CHECKOUT_ACTION__REMOVE] > 0 &&
+ (error = checkout_remove_the_old(actions, &data)) < 0)
+ goto cleanup;
+
+ if (counts[CHECKOUT_ACTION__REMOVE_CONFLICT] > 0 &&
+ (error = checkout_remove_conflicts(&data)) < 0)
+ goto cleanup;
+
+ if (counts[CHECKOUT_ACTION__UPDATE_BLOB] > 0 &&
+ (error = checkout_create_the_new(actions, &data)) < 0)
+ goto cleanup;
+
+ if (counts[CHECKOUT_ACTION__UPDATE_SUBMODULE] > 0 &&
+ (error = checkout_create_submodules(actions, &data)) < 0)
+ goto cleanup;
+
+ if (counts[CHECKOUT_ACTION__UPDATE_CONFLICT] > 0 &&
+ (error = checkout_create_conflicts(&data)) < 0)
+ goto cleanup;
+
+ if (data.index != git_iterator_index(target) &&
+ (error = checkout_extensions_update_index(&data)) < 0)
+ goto cleanup;
+
+ GIT_ASSERT(data.completed_steps == data.total_steps);
+
+ if (data.opts.perfdata_cb)
+ data.opts.perfdata_cb(&data.perfdata, data.opts.perfdata_payload);
+
+cleanup:
+ if (!error && data.index != NULL &&
+ (data.strategy & CHECKOUT_INDEX_DONT_WRITE_MASK) == 0)
+ error = git_index_write(data.index);
+
+ git_diff_free(data.diff);
+ git_iterator_free(workdir);
+ git_iterator_free(baseline);
+ git__free(actions);
+ git__free(counts);
+ checkout_data_clear(&data);
+
+ return error;
+}
+
+int git_checkout_index(
+ git_repository *repo,
+ git_index *index,
+ const git_checkout_options *opts)
+{
+ git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
+ int error, owned = 0;
+ git_iterator *index_i;
+
+ if (!index && !repo) {
+ git_error_set(GIT_ERROR_CHECKOUT,
+ "must provide either repository or index to checkout");
+ return -1;
+ }
+
+ if (index && repo &&
+ git_index_owner(index) &&
+ git_index_owner(index) != repo) {
+ git_error_set(GIT_ERROR_CHECKOUT,
+ "index to checkout does not match repository");
+ return -1;
+ } else if(index && repo && !git_index_owner(index)) {
+ GIT_REFCOUNT_OWN(index, repo);
+ owned = 1;
+ }
+
+ if (!repo)
+ repo = git_index_owner(index);
+
+ if (!index && (error = git_repository_index__weakptr(&index, repo)) < 0)
+ return error;
+ GIT_REFCOUNT_INC(index);
+
+ setup_pathspecs(&iter_opts, opts);
+
+ if (!(error = git_iterator_for_index(&index_i, repo, index, &iter_opts)))
+ error = git_checkout_iterator(index_i, index, opts);
+
+ if (owned)
+ GIT_REFCOUNT_OWN(index, NULL);
+
+ git_iterator_free(index_i);
+ git_index_free(index);
+
+ return error;
+}
+
+int git_checkout_tree(
+ git_repository *repo,
+ const git_object *treeish,
+ const git_checkout_options *opts)
+{
+ int error;
+ git_index *index;
+ git_tree *tree = NULL;
+ git_iterator *tree_i = NULL;
+ git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
+
+ if (!treeish && !repo) {
+ git_error_set(GIT_ERROR_CHECKOUT,
+ "must provide either repository or tree to checkout");
+ return -1;
+ }
+ if (treeish && repo && git_object_owner(treeish) != repo) {
+ git_error_set(GIT_ERROR_CHECKOUT,
+ "object to checkout does not match repository");
+ return -1;
+ }
+
+ if (!repo)
+ repo = git_object_owner(treeish);
+
+ if (treeish) {
+ if (git_object_peel((git_object **)&tree, treeish, GIT_OBJECT_TREE) < 0) {
+ git_error_set(
+ GIT_ERROR_CHECKOUT, "provided object cannot be peeled to a tree");
+ return -1;
+ }
+ }
+ else {
+ if ((error = checkout_lookup_head_tree(&tree, repo)) < 0) {
+ if (error != GIT_EUNBORNBRANCH)
+ git_error_set(
+ GIT_ERROR_CHECKOUT,
+ "HEAD could not be peeled to a tree and no treeish given");
+ return error;
+ }
+ }
+
+ if ((error = git_repository_index(&index, repo)) < 0)
+ return error;
+
+ setup_pathspecs(&iter_opts, opts);
+
+ if (!(error = git_iterator_for_tree(&tree_i, tree, &iter_opts)))
+ error = git_checkout_iterator(tree_i, index, opts);
+
+ git_iterator_free(tree_i);
+ git_index_free(index);
+ git_tree_free(tree);
+
+ return error;
+}
+
+int git_checkout_head(
+ git_repository *repo,
+ const git_checkout_options *opts)
+{
+ GIT_ASSERT_ARG(repo);
+
+ return git_checkout_tree(repo, NULL, opts);
+}
+
+int git_checkout_options_init(git_checkout_options *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_checkout_options, GIT_CHECKOUT_OPTIONS_INIT);
+ return 0;
+}
+
+#ifndef GIT_DEPRECATE_HARD
+int git_checkout_init_options(git_checkout_options *opts, unsigned int version)
+{
+ return git_checkout_options_init(opts, version);
+}
+#endif
diff --git a/src/libgit2/checkout.h b/src/libgit2/checkout.h
new file mode 100644
index 0000000..517fbf3
--- /dev/null
+++ b/src/libgit2/checkout.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_checkout_h__
+#define INCLUDE_checkout_h__
+
+#include "common.h"
+
+#include "git2/checkout.h"
+#include "iterator.h"
+
+#define GIT_CHECKOUT__NOTIFY_CONFLICT_TREE (1u << 12)
+
+/**
+ * Update the working directory to match the target iterator. The
+ * expected baseline value can be passed in via the checkout options
+ * or else will default to the HEAD commit.
+ */
+extern int git_checkout_iterator(
+ git_iterator *target,
+ git_index *index,
+ const git_checkout_options *opts);
+
+#endif
diff --git a/src/libgit2/cherrypick.c b/src/libgit2/cherrypick.c
new file mode 100644
index 0000000..3ef42d5
--- /dev/null
+++ b/src/libgit2/cherrypick.c
@@ -0,0 +1,242 @@
+/*
+* Copyright (C) the libgit2 contributors. All rights reserved.
+*
+* This file is part of libgit2, distributed under the GNU GPL v2 with
+* a Linking Exception. For full terms see the included COPYING file.
+*/
+
+#include "common.h"
+
+#include "repository.h"
+#include "filebuf.h"
+#include "merge.h"
+#include "vector.h"
+#include "index.h"
+
+#include "git2/types.h"
+#include "git2/merge.h"
+#include "git2/cherrypick.h"
+#include "git2/commit.h"
+#include "git2/sys/commit.h"
+
+#define GIT_CHERRYPICK_FILE_MODE 0666
+
+static int write_cherrypick_head(
+ git_repository *repo,
+ const char *commit_oidstr)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ git_str file_path = GIT_STR_INIT;
+ int error = 0;
+
+ if ((error = git_str_joinpath(&file_path, repo->gitdir, GIT_CHERRYPICK_HEAD_FILE)) >= 0 &&
+ (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_CREATE_LEADING_DIRS, GIT_CHERRYPICK_FILE_MODE)) >= 0 &&
+ (error = git_filebuf_printf(&file, "%s\n", commit_oidstr)) >= 0)
+ error = git_filebuf_commit(&file);
+
+ if (error < 0)
+ git_filebuf_cleanup(&file);
+
+ git_str_dispose(&file_path);
+
+ return error;
+}
+
+static int write_merge_msg(
+ git_repository *repo,
+ const char *commit_msg)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ git_str file_path = GIT_STR_INIT;
+ int error = 0;
+
+ if ((error = git_str_joinpath(&file_path, repo->gitdir, GIT_MERGE_MSG_FILE)) < 0 ||
+ (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_CREATE_LEADING_DIRS, GIT_CHERRYPICK_FILE_MODE)) < 0 ||
+ (error = git_filebuf_printf(&file, "%s", commit_msg)) < 0)
+ goto cleanup;
+
+ error = git_filebuf_commit(&file);
+
+cleanup:
+ if (error < 0)
+ git_filebuf_cleanup(&file);
+
+ git_str_dispose(&file_path);
+
+ return error;
+}
+
+static int cherrypick_normalize_opts(
+ git_repository *repo,
+ git_cherrypick_options *opts,
+ const git_cherrypick_options *given,
+ const char *their_label)
+{
+ int error = 0;
+ unsigned int default_checkout_strategy = GIT_CHECKOUT_SAFE |
+ GIT_CHECKOUT_ALLOW_CONFLICTS;
+
+ GIT_UNUSED(repo);
+
+ if (given != NULL)
+ memcpy(opts, given, sizeof(git_cherrypick_options));
+ else {
+ git_cherrypick_options default_opts = GIT_CHERRYPICK_OPTIONS_INIT;
+ memcpy(opts, &default_opts, sizeof(git_cherrypick_options));
+ }
+
+ if (!opts->checkout_opts.checkout_strategy)
+ opts->checkout_opts.checkout_strategy = default_checkout_strategy;
+
+ if (!opts->checkout_opts.our_label)
+ opts->checkout_opts.our_label = "HEAD";
+
+ if (!opts->checkout_opts.their_label)
+ opts->checkout_opts.their_label = their_label;
+
+ return error;
+}
+
+static int cherrypick_state_cleanup(git_repository *repo)
+{
+ const char *state_files[] = { GIT_CHERRYPICK_HEAD_FILE, GIT_MERGE_MSG_FILE };
+
+ return git_repository__cleanup_files(repo, state_files, ARRAY_SIZE(state_files));
+}
+
+static int cherrypick_seterr(git_commit *commit, const char *fmt)
+{
+ char commit_oidstr[GIT_OID_MAX_HEXSIZE + 1];
+
+ git_error_set(GIT_ERROR_CHERRYPICK, fmt,
+ git_oid_tostr(commit_oidstr, GIT_OID_MAX_HEXSIZE + 1, git_commit_id(commit)));
+
+ return -1;
+}
+
+int git_cherrypick_commit(
+ git_index **out,
+ git_repository *repo,
+ git_commit *cherrypick_commit,
+ git_commit *our_commit,
+ unsigned int mainline,
+ const git_merge_options *merge_opts)
+{
+ git_commit *parent_commit = NULL;
+ git_tree *parent_tree = NULL, *our_tree = NULL, *cherrypick_tree = NULL;
+ int parent = 0, error = 0;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(cherrypick_commit);
+ GIT_ASSERT_ARG(our_commit);
+
+ if (git_commit_parentcount(cherrypick_commit) > 1) {
+ if (!mainline)
+ return cherrypick_seterr(cherrypick_commit,
+ "mainline branch is not specified but %s is a merge commit");
+
+ parent = mainline;
+ } else {
+ if (mainline)
+ return cherrypick_seterr(cherrypick_commit,
+ "mainline branch specified but %s is not a merge commit");
+
+ parent = git_commit_parentcount(cherrypick_commit);
+ }
+
+ if (parent &&
+ ((error = git_commit_parent(&parent_commit, cherrypick_commit, (parent - 1))) < 0 ||
+ (error = git_commit_tree(&parent_tree, parent_commit)) < 0))
+ goto done;
+
+ if ((error = git_commit_tree(&cherrypick_tree, cherrypick_commit)) < 0 ||
+ (error = git_commit_tree(&our_tree, our_commit)) < 0)
+ goto done;
+
+ error = git_merge_trees(out, repo, parent_tree, our_tree, cherrypick_tree, merge_opts);
+
+done:
+ git_tree_free(parent_tree);
+ git_tree_free(our_tree);
+ git_tree_free(cherrypick_tree);
+ git_commit_free(parent_commit);
+
+ return error;
+}
+
+int git_cherrypick(
+ git_repository *repo,
+ git_commit *commit,
+ const git_cherrypick_options *given_opts)
+{
+ git_cherrypick_options opts;
+ git_reference *our_ref = NULL;
+ git_commit *our_commit = NULL;
+ char commit_oidstr[GIT_OID_MAX_HEXSIZE + 1];
+ const char *commit_msg, *commit_summary;
+ git_str their_label = GIT_STR_INIT;
+ git_index *index = NULL;
+ git_indexwriter indexwriter = GIT_INDEXWRITER_INIT;
+ int error = 0;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(commit);
+
+ GIT_ERROR_CHECK_VERSION(given_opts, GIT_CHERRYPICK_OPTIONS_VERSION, "git_cherrypick_options");
+
+ if ((error = git_repository__ensure_not_bare(repo, "cherry-pick")) < 0)
+ return error;
+
+ if ((commit_msg = git_commit_message(commit)) == NULL ||
+ (commit_summary = git_commit_summary(commit)) == NULL) {
+ error = -1;
+ goto on_error;
+ }
+
+ git_oid_nfmt(commit_oidstr, sizeof(commit_oidstr), git_commit_id(commit));
+
+ if ((error = write_merge_msg(repo, commit_msg)) < 0 ||
+ (error = git_str_printf(&their_label, "%.7s... %s", commit_oidstr, commit_summary)) < 0 ||
+ (error = cherrypick_normalize_opts(repo, &opts, given_opts, git_str_cstr(&their_label))) < 0 ||
+ (error = git_indexwriter_init_for_operation(&indexwriter, repo, &opts.checkout_opts.checkout_strategy)) < 0 ||
+ (error = write_cherrypick_head(repo, commit_oidstr)) < 0 ||
+ (error = git_repository_head(&our_ref, repo)) < 0 ||
+ (error = git_reference_peel((git_object **)&our_commit, our_ref, GIT_OBJECT_COMMIT)) < 0 ||
+ (error = git_cherrypick_commit(&index, repo, commit, our_commit, opts.mainline, &opts.merge_opts)) < 0 ||
+ (error = git_merge__check_result(repo, index)) < 0 ||
+ (error = git_merge__append_conflicts_to_merge_msg(repo, index)) < 0 ||
+ (error = git_checkout_index(repo, index, &opts.checkout_opts)) < 0 ||
+ (error = git_indexwriter_commit(&indexwriter)) < 0)
+ goto on_error;
+
+ goto done;
+
+on_error:
+ cherrypick_state_cleanup(repo);
+
+done:
+ git_indexwriter_cleanup(&indexwriter);
+ git_index_free(index);
+ git_commit_free(our_commit);
+ git_reference_free(our_ref);
+ git_str_dispose(&their_label);
+
+ return error;
+}
+
+int git_cherrypick_options_init(
+ git_cherrypick_options *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_cherrypick_options, GIT_CHERRYPICK_OPTIONS_INIT);
+ return 0;
+}
+
+#ifndef GIT_DEPRECATE_HARD
+int git_cherrypick_init_options(
+ git_cherrypick_options *opts, unsigned int version)
+{
+ return git_cherrypick_options_init(opts, version);
+}
+#endif
diff --git a/src/libgit2/clone.c b/src/libgit2/clone.c
new file mode 100644
index 0000000..fca0ca0
--- /dev/null
+++ b/src/libgit2/clone.c
@@ -0,0 +1,686 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "clone.h"
+
+#include "git2/clone.h"
+#include "git2/remote.h"
+#include "git2/revparse.h"
+#include "git2/branch.h"
+#include "git2/config.h"
+#include "git2/checkout.h"
+#include "git2/commit.h"
+#include "git2/tree.h"
+
+#include "remote.h"
+#include "futils.h"
+#include "refs.h"
+#include "fs_path.h"
+#include "repository.h"
+#include "odb.h"
+
+static int clone_local_into(git_repository *repo, git_remote *remote, const git_fetch_options *fetch_opts, const git_checkout_options *co_opts, const char *branch, int link);
+
+static int create_branch(
+ git_reference **branch,
+ git_repository *repo,
+ const git_oid *target,
+ const char *name,
+ const char *log_message)
+{
+ git_commit *head_obj = NULL;
+ git_reference *branch_ref = NULL;
+ git_str refname = GIT_STR_INIT;
+ int error;
+
+ /* Find the target commit */
+ if ((error = git_commit_lookup(&head_obj, repo, target)) < 0)
+ return error;
+
+ /* Create the new branch */
+ if ((error = git_str_printf(&refname, GIT_REFS_HEADS_DIR "%s", name)) < 0)
+ return error;
+
+ error = git_reference_create(&branch_ref, repo, git_str_cstr(&refname), target, 0, log_message);
+ git_str_dispose(&refname);
+ git_commit_free(head_obj);
+
+ if (!error)
+ *branch = branch_ref;
+ else
+ git_reference_free(branch_ref);
+
+ return error;
+}
+
+static int setup_tracking_config(
+ git_repository *repo,
+ const char *branch_name,
+ const char *remote_name,
+ const char *merge_target)
+{
+ git_config *cfg;
+ git_str remote_key = GIT_STR_INIT, merge_key = GIT_STR_INIT;
+ int error = -1;
+
+ if (git_repository_config__weakptr(&cfg, repo) < 0)
+ return -1;
+
+ if (git_str_printf(&remote_key, "branch.%s.remote", branch_name) < 0)
+ goto cleanup;
+
+ if (git_str_printf(&merge_key, "branch.%s.merge", branch_name) < 0)
+ goto cleanup;
+
+ if (git_config_set_string(cfg, git_str_cstr(&remote_key), remote_name) < 0)
+ goto cleanup;
+
+ if (git_config_set_string(cfg, git_str_cstr(&merge_key), merge_target) < 0)
+ goto cleanup;
+
+ error = 0;
+
+cleanup:
+ git_str_dispose(&remote_key);
+ git_str_dispose(&merge_key);
+ return error;
+}
+
+static int create_tracking_branch(
+ git_reference **branch,
+ git_repository *repo,
+ const git_oid *target,
+ const char *branch_name,
+ const char *log_message)
+{
+ int error;
+
+ if ((error = create_branch(branch, repo, target, branch_name, log_message)) < 0)
+ return error;
+
+ return setup_tracking_config(
+ repo,
+ branch_name,
+ GIT_REMOTE_ORIGIN,
+ git_reference_name(*branch));
+}
+
+static int update_head_to_new_branch(
+ git_repository *repo,
+ const git_oid *target,
+ const char *name,
+ const char *reflog_message)
+{
+ git_reference *tracking_branch = NULL;
+ int error;
+
+ if (!git__prefixcmp(name, GIT_REFS_HEADS_DIR))
+ name += strlen(GIT_REFS_HEADS_DIR);
+
+ error = create_tracking_branch(&tracking_branch, repo, target, name,
+ reflog_message);
+
+ if (!error)
+ error = git_repository_set_head(
+ repo, git_reference_name(tracking_branch));
+
+ git_reference_free(tracking_branch);
+
+ /* if it already existed, then the user's refspec created it for us, ignore it' */
+ if (error == GIT_EEXISTS)
+ error = 0;
+
+ return error;
+}
+
+static int update_head_to_default(git_repository *repo)
+{
+ git_str initialbranch = GIT_STR_INIT;
+ const char *branch_name;
+ int error = 0;
+
+ if ((error = git_repository_initialbranch(&initialbranch, repo)) < 0)
+ goto done;
+
+ if (git__prefixcmp(initialbranch.ptr, GIT_REFS_HEADS_DIR) != 0) {
+ git_error_set(GIT_ERROR_INVALID, "invalid initial branch '%s'", initialbranch.ptr);
+ error = -1;
+ goto done;
+ }
+
+ branch_name = initialbranch.ptr + strlen(GIT_REFS_HEADS_DIR);
+
+ error = setup_tracking_config(repo, branch_name, GIT_REMOTE_ORIGIN,
+ initialbranch.ptr);
+
+done:
+ git_str_dispose(&initialbranch);
+ return error;
+}
+
+static int update_remote_head(
+ git_repository *repo,
+ git_remote *remote,
+ git_str *target,
+ const char *reflog_message)
+{
+ git_refspec *refspec;
+ git_reference *remote_head = NULL;
+ git_str remote_head_name = GIT_STR_INIT;
+ git_str remote_branch_name = GIT_STR_INIT;
+ int error;
+
+ /* Determine the remote tracking ref name from the local branch */
+ refspec = git_remote__matching_refspec(remote, git_str_cstr(target));
+
+ if (refspec == NULL) {
+ git_error_set(GIT_ERROR_NET, "the remote's default branch does not fit the refspec configuration");
+ error = GIT_EINVALIDSPEC;
+ goto cleanup;
+ }
+
+ if ((error = git_refspec__transform(
+ &remote_branch_name,
+ refspec,
+ git_str_cstr(target))) < 0)
+ goto cleanup;
+
+ if ((error = git_str_printf(&remote_head_name,
+ "%s%s/%s",
+ GIT_REFS_REMOTES_DIR,
+ git_remote_name(remote),
+ GIT_HEAD_FILE)) < 0)
+ goto cleanup;
+
+ error = git_reference_symbolic_create(
+ &remote_head,
+ repo,
+ git_str_cstr(&remote_head_name),
+ git_str_cstr(&remote_branch_name),
+ true,
+ reflog_message);
+
+cleanup:
+ git_reference_free(remote_head);
+ git_str_dispose(&remote_branch_name);
+ git_str_dispose(&remote_head_name);
+ return error;
+}
+
+static int update_head_to_remote(
+ git_repository *repo,
+ git_remote *remote,
+ const char *reflog_message)
+{
+ int error = 0;
+ size_t refs_len;
+ const git_remote_head *remote_head, **refs;
+ const git_oid *remote_head_id;
+ git_str branch = GIT_STR_INIT;
+
+ if ((error = git_remote_ls(&refs, &refs_len, remote)) < 0)
+ return error;
+
+ /* We cloned an empty repository or one with an unborn HEAD */
+ if (refs_len == 0 || strcmp(refs[0]->name, GIT_HEAD_FILE))
+ return update_head_to_default(repo);
+
+ /* We know we have HEAD, let's see where it points */
+ remote_head = refs[0];
+ GIT_ASSERT(remote_head);
+
+ remote_head_id = &remote_head->oid;
+
+ error = git_remote__default_branch(&branch, remote);
+ if (error == GIT_ENOTFOUND) {
+ error = git_repository_set_head_detached(
+ repo, remote_head_id);
+ goto cleanup;
+ }
+
+ if ((error = update_remote_head(repo, remote, &branch, reflog_message)) < 0)
+ goto cleanup;
+
+ error = update_head_to_new_branch(
+ repo,
+ remote_head_id,
+ git_str_cstr(&branch),
+ reflog_message);
+
+cleanup:
+ git_str_dispose(&branch);
+
+ return error;
+}
+
+static int update_head_to_branch(
+ git_repository *repo,
+ git_remote *remote,
+ const char *branch,
+ const char *reflog_message)
+{
+ int retcode;
+ git_str remote_branch_name = GIT_STR_INIT;
+ git_reference *remote_ref = NULL;
+ git_str default_branch = GIT_STR_INIT;
+
+ GIT_ASSERT_ARG(remote);
+ GIT_ASSERT_ARG(branch);
+
+ if ((retcode = git_str_printf(&remote_branch_name, GIT_REFS_REMOTES_DIR "%s/%s",
+ git_remote_name(remote), branch)) < 0 )
+ goto cleanup;
+
+ if ((retcode = git_reference_lookup(&remote_ref, repo, git_str_cstr(&remote_branch_name))) < 0)
+ goto cleanup;
+
+ if ((retcode = update_head_to_new_branch(repo, git_reference_target(remote_ref), branch,
+ reflog_message)) < 0)
+ goto cleanup;
+
+ retcode = git_remote__default_branch(&default_branch, remote);
+
+ if (retcode == GIT_ENOTFOUND)
+ retcode = 0;
+ else if (retcode)
+ goto cleanup;
+
+ if (!git_remote__matching_refspec(remote, git_str_cstr(&default_branch)))
+ goto cleanup;
+
+ retcode = update_remote_head(repo, remote, &default_branch, reflog_message);
+
+cleanup:
+ git_reference_free(remote_ref);
+ git_str_dispose(&remote_branch_name);
+ git_str_dispose(&default_branch);
+ return retcode;
+}
+
+static int default_repository_create(git_repository **out, const char *path, int bare, void *payload)
+{
+ GIT_UNUSED(payload);
+
+ return git_repository_init(out, path, bare);
+}
+
+static int default_remote_create(
+ git_remote **out,
+ git_repository *repo,
+ const char *name,
+ const char *url,
+ void *payload)
+{
+ GIT_UNUSED(payload);
+
+ return git_remote_create(out, repo, name, url);
+}
+
+/*
+ * submodules?
+ */
+
+static int create_and_configure_origin(
+ git_remote **out,
+ git_repository *repo,
+ const char *url,
+ const git_clone_options *options)
+{
+ int error;
+ git_remote *origin = NULL;
+ char buf[GIT_PATH_MAX];
+ git_remote_create_cb remote_create = options->remote_cb;
+ void *payload = options->remote_cb_payload;
+
+ /* If the path exists and is a dir, the url should be the absolute path */
+ if (git_fs_path_root(url) < 0 && git_fs_path_exists(url) && git_fs_path_isdir(url)) {
+ if (p_realpath(url, buf) == NULL)
+ return -1;
+
+ url = buf;
+ }
+
+ if (!remote_create) {
+ remote_create = default_remote_create;
+ payload = NULL;
+ }
+
+ if ((error = remote_create(&origin, repo, "origin", url, payload)) < 0)
+ goto on_error;
+
+ *out = origin;
+ return 0;
+
+on_error:
+ git_remote_free(origin);
+ return error;
+}
+
+static bool should_checkout(
+ git_repository *repo,
+ bool is_bare,
+ const git_checkout_options *opts)
+{
+ if (is_bare)
+ return false;
+
+ if (!opts)
+ return false;
+
+ if (opts->checkout_strategy == GIT_CHECKOUT_NONE)
+ return false;
+
+ return !git_repository_head_unborn(repo);
+}
+
+static int checkout_branch(git_repository *repo, git_remote *remote, const git_checkout_options *co_opts, const char *branch, const char *reflog_message)
+{
+ int error;
+
+ if (branch)
+ error = update_head_to_branch(repo, remote, branch, reflog_message);
+ /* Point HEAD to the same ref as the remote's head */
+ else
+ error = update_head_to_remote(repo, remote, reflog_message);
+
+ if (!error && should_checkout(repo, git_repository_is_bare(repo), co_opts))
+ error = git_checkout_head(repo, co_opts);
+
+ return error;
+}
+
+static int clone_into(
+ git_repository *repo,
+ git_remote *_remote,
+ const git_fetch_options *opts,
+ const git_checkout_options *co_opts,
+ const char *branch)
+{
+ int error;
+ git_str reflog_message = GIT_STR_INIT;
+ git_remote_connect_options connect_opts = GIT_REMOTE_CONNECT_OPTIONS_INIT;
+ git_fetch_options fetch_opts;
+ git_remote *remote;
+ git_oid_t oid_type;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(_remote);
+
+ if (!git_repository_is_empty(repo)) {
+ git_error_set(GIT_ERROR_INVALID, "the repository is not empty");
+ return -1;
+ }
+
+ if ((error = git_remote_dup(&remote, _remote)) < 0)
+ return error;
+
+ memcpy(&fetch_opts, opts, sizeof(git_fetch_options));
+ fetch_opts.update_fetchhead = 0;
+
+ if (!opts->depth)
+ fetch_opts.download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL;
+
+ if ((error = git_remote_connect_options__from_fetch_opts(&connect_opts, remote, &fetch_opts)) < 0)
+ goto cleanup;
+
+ git_str_printf(&reflog_message, "clone: from %s", git_remote_url(remote));
+
+ /*
+ * Connect to the server so that we can identify the remote
+ * object format.
+ */
+
+ if ((error = git_remote_connect_ext(remote, GIT_DIRECTION_FETCH,
+ &connect_opts)) < 0)
+ goto cleanup;
+
+ if ((error = git_remote_oid_type(&oid_type, remote)) < 0 ||
+ (error = git_repository__set_objectformat(repo, oid_type)) < 0)
+ goto cleanup;
+
+ if ((error = git_remote_fetch(remote, NULL, &fetch_opts, git_str_cstr(&reflog_message))) != 0)
+ goto cleanup;
+
+ error = checkout_branch(repo, remote, co_opts, branch, git_str_cstr(&reflog_message));
+
+cleanup:
+ git_remote_free(remote);
+ git_remote_connect_options_dispose(&connect_opts);
+ git_str_dispose(&reflog_message);
+
+ return error;
+}
+
+int git_clone__should_clone_local(const char *url_or_path, git_clone_local_t local)
+{
+ git_str fromurl = GIT_STR_INIT;
+ const char *path = url_or_path;
+ bool is_url, is_local;
+
+ if (local == GIT_CLONE_NO_LOCAL)
+ return 0;
+
+ if ((is_url = git_fs_path_is_local_file_url(url_or_path)) != 0) {
+ if (git_fs_path_fromurl(&fromurl, url_or_path) < 0) {
+ is_local = -1;
+ goto done;
+ }
+
+ path = fromurl.ptr;
+ }
+
+ is_local = (!is_url || local != GIT_CLONE_LOCAL_AUTO) &&
+ git_fs_path_isdir(path);
+
+done:
+ git_str_dispose(&fromurl);
+ return is_local;
+}
+
+static int git__clone(
+ git_repository **out,
+ const char *url,
+ const char *local_path,
+ const git_clone_options *_options,
+ int use_existing)
+{
+ int error = 0;
+ git_repository *repo = NULL;
+ git_remote *origin;
+ git_clone_options options = GIT_CLONE_OPTIONS_INIT;
+ uint32_t rmdir_flags = GIT_RMDIR_REMOVE_FILES;
+ git_repository_create_cb repository_cb;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(url);
+ GIT_ASSERT_ARG(local_path);
+
+ if (_options)
+ memcpy(&options, _options, sizeof(git_clone_options));
+
+ GIT_ERROR_CHECK_VERSION(&options, GIT_CLONE_OPTIONS_VERSION, "git_clone_options");
+
+ /* Only clone to a new directory or an empty directory */
+ if (git_fs_path_exists(local_path) && !use_existing && !git_fs_path_is_empty_dir(local_path)) {
+ git_error_set(GIT_ERROR_INVALID,
+ "'%s' exists and is not an empty directory", local_path);
+ return GIT_EEXISTS;
+ }
+
+ /* Only remove the root directory on failure if we create it */
+ if (git_fs_path_exists(local_path))
+ rmdir_flags |= GIT_RMDIR_SKIP_ROOT;
+
+ if (options.repository_cb)
+ repository_cb = options.repository_cb;
+ else
+ repository_cb = default_repository_create;
+
+ if ((error = repository_cb(&repo, local_path, options.bare, options.repository_cb_payload)) < 0)
+ return error;
+
+ if (!(error = create_and_configure_origin(&origin, repo, url, &options))) {
+ int clone_local = git_clone__should_clone_local(url, options.local);
+ int link = options.local != GIT_CLONE_LOCAL_NO_LINKS;
+
+ if (clone_local == 1)
+ error = clone_local_into(
+ repo, origin, &options.fetch_opts, &options.checkout_opts,
+ options.checkout_branch, link);
+ else if (clone_local == 0)
+ error = clone_into(
+ repo, origin, &options.fetch_opts, &options.checkout_opts,
+ options.checkout_branch);
+ else
+ error = -1;
+
+ git_remote_free(origin);
+ }
+
+ if (error != 0) {
+ git_error_state last_error = {0};
+ git_error_state_capture(&last_error, error);
+
+ git_repository_free(repo);
+ repo = NULL;
+
+ (void)git_futils_rmdir_r(local_path, NULL, rmdir_flags);
+
+ git_error_state_restore(&last_error);
+ }
+
+ *out = repo;
+ return error;
+}
+
+int git_clone(
+ git_repository **out,
+ const char *url,
+ const char *local_path,
+ const git_clone_options *_options)
+{
+ return git__clone(out, url, local_path, _options, 0);
+}
+
+int git_clone__submodule(
+ git_repository **out,
+ const char *url,
+ const char *local_path,
+ const git_clone_options *_options)
+{
+ return git__clone(out, url, local_path, _options, 1);
+}
+
+int git_clone_options_init(git_clone_options *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_clone_options, GIT_CLONE_OPTIONS_INIT);
+ return 0;
+}
+
+#ifndef GIT_DEPRECATE_HARD
+int git_clone_init_options(git_clone_options *opts, unsigned int version)
+{
+ return git_clone_options_init(opts, version);
+}
+#endif
+
+static bool can_link(const char *src, const char *dst, int link)
+{
+#ifdef GIT_WIN32
+ GIT_UNUSED(src);
+ GIT_UNUSED(dst);
+ GIT_UNUSED(link);
+ return false;
+#else
+
+ struct stat st_src, st_dst;
+
+ if (!link)
+ return false;
+
+ if (p_stat(src, &st_src) < 0)
+ return false;
+
+ if (p_stat(dst, &st_dst) < 0)
+ return false;
+
+ return st_src.st_dev == st_dst.st_dev;
+#endif
+}
+
+static int clone_local_into(git_repository *repo, git_remote *remote, const git_fetch_options *fetch_opts, const git_checkout_options *co_opts, const char *branch, int link)
+{
+ int error, flags;
+ git_repository *src;
+ git_str src_odb = GIT_STR_INIT, dst_odb = GIT_STR_INIT, src_path = GIT_STR_INIT;
+ git_str reflog_message = GIT_STR_INIT;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(remote);
+
+ if (!git_repository_is_empty(repo)) {
+ git_error_set(GIT_ERROR_INVALID, "the repository is not empty");
+ return -1;
+ }
+
+ /*
+ * Let's figure out what path we should use for the source
+ * repo, if it's not rooted, the path should be relative to
+ * the repository's worktree/gitdir.
+ */
+ if ((error = git_fs_path_from_url_or_path(&src_path, git_remote_url(remote))) < 0)
+ return error;
+
+ /* Copy .git/objects/ from the source to the target */
+ if ((error = git_repository_open(&src, git_str_cstr(&src_path))) < 0) {
+ git_str_dispose(&src_path);
+ return error;
+ }
+
+ if (git_repository__item_path(&src_odb, src, GIT_REPOSITORY_ITEM_OBJECTS) < 0 ||
+ git_repository__item_path(&dst_odb, repo, GIT_REPOSITORY_ITEM_OBJECTS) < 0) {
+ error = -1;
+ goto cleanup;
+ }
+
+ flags = 0;
+ if (can_link(git_repository_path(src), git_repository_path(repo), link))
+ flags |= GIT_CPDIR_LINK_FILES;
+
+ error = git_futils_cp_r(git_str_cstr(&src_odb), git_str_cstr(&dst_odb),
+ flags, GIT_OBJECT_DIR_MODE);
+
+ /*
+ * can_link() doesn't catch all variations, so if we hit an
+ * error and did want to link, let's try again without trying
+ * to link.
+ */
+ if (error < 0 && link) {
+ flags &= ~GIT_CPDIR_LINK_FILES;
+ error = git_futils_cp_r(git_str_cstr(&src_odb), git_str_cstr(&dst_odb),
+ flags, GIT_OBJECT_DIR_MODE);
+ }
+
+ if (error < 0)
+ goto cleanup;
+
+ git_str_printf(&reflog_message, "clone: from %s", git_remote_url(remote));
+
+ if ((error = git_remote_fetch(remote, NULL, fetch_opts, git_str_cstr(&reflog_message))) != 0)
+ goto cleanup;
+
+ error = checkout_branch(repo, remote, co_opts, branch, git_str_cstr(&reflog_message));
+
+cleanup:
+ git_str_dispose(&reflog_message);
+ git_str_dispose(&src_path);
+ git_str_dispose(&src_odb);
+ git_str_dispose(&dst_odb);
+ git_repository_free(src);
+ return error;
+}
diff --git a/src/libgit2/clone.h b/src/libgit2/clone.h
new file mode 100644
index 0000000..7d73cab
--- /dev/null
+++ b/src/libgit2/clone.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_clone_h__
+#define INCLUDE_clone_h__
+
+#include "common.h"
+
+#include "git2/clone.h"
+
+extern int git_clone__submodule(git_repository **out,
+ const char *url, const char *local_path,
+ const git_clone_options *_options);
+
+extern int git_clone__should_clone_local(const char *url, git_clone_local_t local);
+
+#endif
diff --git a/src/libgit2/commit.c b/src/libgit2/commit.c
new file mode 100644
index 0000000..f7be73a
--- /dev/null
+++ b/src/libgit2/commit.c
@@ -0,0 +1,1099 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "commit.h"
+
+#include "git2/common.h"
+#include "git2/object.h"
+#include "git2/repository.h"
+#include "git2/signature.h"
+#include "git2/mailmap.h"
+#include "git2/sys/commit.h"
+
+#include "buf.h"
+#include "odb.h"
+#include "commit.h"
+#include "signature.h"
+#include "refs.h"
+#include "object.h"
+#include "array.h"
+#include "oidarray.h"
+#include "grafts.h"
+
+void git_commit__free(void *_commit)
+{
+ git_commit *commit = _commit;
+
+ git_array_clear(commit->parent_ids);
+
+ git_signature_free(commit->author);
+ git_signature_free(commit->committer);
+
+ git__free(commit->raw_header);
+ git__free(commit->raw_message);
+ git__free(commit->message_encoding);
+ git__free(commit->summary);
+ git__free(commit->body);
+
+ git__free(commit);
+}
+
+static int git_commit__create_buffer_internal(
+ git_str *out,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *message_encoding,
+ const char *message,
+ const git_oid *tree,
+ git_array_oid_t *parents)
+{
+ size_t i = 0;
+ const git_oid *parent;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(tree);
+
+ if (git_object__write_oid_header(out, "tree ", tree) < 0)
+ goto on_error;
+
+ for (i = 0; i < git_array_size(*parents); i++) {
+ parent = git_array_get(*parents, i);
+ if (git_object__write_oid_header(out, "parent ", parent) < 0)
+ goto on_error;
+ }
+
+ git_signature__writebuf(out, "author ", author);
+ git_signature__writebuf(out, "committer ", committer);
+
+ if (message_encoding != NULL)
+ git_str_printf(out, "encoding %s\n", message_encoding);
+
+ git_str_putc(out, '\n');
+
+ if (git_str_puts(out, message) < 0)
+ goto on_error;
+
+ return 0;
+
+on_error:
+ git_str_dispose(out);
+ return -1;
+}
+
+static int validate_tree_and_parents(git_array_oid_t *parents, git_repository *repo, const git_oid *tree,
+ git_commit_parent_callback parent_cb, void *parent_payload,
+ const git_oid *current_id, bool validate)
+{
+ size_t i;
+ int error;
+ git_oid *parent_cpy;
+ const git_oid *parent;
+
+ if (validate && !git_object__is_valid(repo, tree, GIT_OBJECT_TREE))
+ return -1;
+
+ i = 0;
+ while ((parent = parent_cb(i, parent_payload)) != NULL) {
+ if (validate && !git_object__is_valid(repo, parent, GIT_OBJECT_COMMIT)) {
+ error = -1;
+ goto on_error;
+ }
+
+ parent_cpy = git_array_alloc(*parents);
+ GIT_ERROR_CHECK_ALLOC(parent_cpy);
+
+ git_oid_cpy(parent_cpy, parent);
+ i++;
+ }
+
+ if (current_id && (parents->size == 0 || git_oid_cmp(current_id, git_array_get(*parents, 0)))) {
+ git_error_set(GIT_ERROR_OBJECT, "failed to create commit: current tip is not the first parent");
+ error = GIT_EMODIFIED;
+ goto on_error;
+ }
+
+ return 0;
+
+on_error:
+ git_array_clear(*parents);
+ return error;
+}
+
+static int git_commit__create_internal(
+ git_oid *id,
+ git_repository *repo,
+ const char *update_ref,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *message_encoding,
+ const char *message,
+ const git_oid *tree,
+ git_commit_parent_callback parent_cb,
+ void *parent_payload,
+ bool validate)
+{
+ int error;
+ git_odb *odb;
+ git_reference *ref = NULL;
+ git_str buf = GIT_STR_INIT;
+ const git_oid *current_id = NULL;
+ git_array_oid_t parents = GIT_ARRAY_INIT;
+
+ if (update_ref) {
+ error = git_reference_lookup_resolved(&ref, repo, update_ref, 10);
+ if (error < 0 && error != GIT_ENOTFOUND)
+ return error;
+ }
+ git_error_clear();
+
+ if (ref)
+ current_id = git_reference_target(ref);
+
+ if ((error = validate_tree_and_parents(&parents, repo, tree, parent_cb, parent_payload, current_id, validate)) < 0)
+ goto cleanup;
+
+ error = git_commit__create_buffer_internal(&buf, author, committer,
+ message_encoding, message, tree,
+ &parents);
+
+ if (error < 0)
+ goto cleanup;
+
+ if (git_repository_odb__weakptr(&odb, repo) < 0)
+ goto cleanup;
+
+ if (git_odb__freshen(odb, tree) < 0)
+ goto cleanup;
+
+ if (git_odb_write(id, odb, buf.ptr, buf.size, GIT_OBJECT_COMMIT) < 0)
+ goto cleanup;
+
+
+ if (update_ref != NULL) {
+ error = git_reference__update_for_commit(
+ repo, ref, update_ref, id, "commit");
+ goto cleanup;
+ }
+
+cleanup:
+ git_array_clear(parents);
+ git_reference_free(ref);
+ git_str_dispose(&buf);
+ return error;
+}
+
+int git_commit_create_from_callback(
+ git_oid *id,
+ git_repository *repo,
+ const char *update_ref,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *message_encoding,
+ const char *message,
+ const git_oid *tree,
+ git_commit_parent_callback parent_cb,
+ void *parent_payload)
+{
+ return git_commit__create_internal(
+ id, repo, update_ref, author, committer, message_encoding, message,
+ tree, parent_cb, parent_payload, true);
+}
+
+typedef struct {
+ size_t total;
+ va_list args;
+} commit_parent_varargs;
+
+static const git_oid *commit_parent_from_varargs(size_t curr, void *payload)
+{
+ commit_parent_varargs *data = payload;
+ const git_commit *commit;
+ if (curr >= data->total)
+ return NULL;
+ commit = va_arg(data->args, const git_commit *);
+ return commit ? git_commit_id(commit) : NULL;
+}
+
+int git_commit_create_v(
+ git_oid *id,
+ git_repository *repo,
+ const char *update_ref,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *message_encoding,
+ const char *message,
+ const git_tree *tree,
+ size_t parent_count,
+ ...)
+{
+ int error = 0;
+ commit_parent_varargs data;
+
+ GIT_ASSERT_ARG(tree);
+ GIT_ASSERT_ARG(git_tree_owner(tree) == repo);
+
+ data.total = parent_count;
+ va_start(data.args, parent_count);
+
+ error = git_commit__create_internal(
+ id, repo, update_ref, author, committer,
+ message_encoding, message, git_tree_id(tree),
+ commit_parent_from_varargs, &data, false);
+
+ va_end(data.args);
+ return error;
+}
+
+typedef struct {
+ size_t total;
+ const git_oid **parents;
+} commit_parent_oids;
+
+static const git_oid *commit_parent_from_ids(size_t curr, void *payload)
+{
+ commit_parent_oids *data = payload;
+ return (curr < data->total) ? data->parents[curr] : NULL;
+}
+
+int git_commit_create_from_ids(
+ git_oid *id,
+ git_repository *repo,
+ const char *update_ref,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *message_encoding,
+ const char *message,
+ const git_oid *tree,
+ size_t parent_count,
+ const git_oid *parents[])
+{
+ commit_parent_oids data = { parent_count, parents };
+
+ return git_commit__create_internal(
+ id, repo, update_ref, author, committer,
+ message_encoding, message, tree,
+ commit_parent_from_ids, &data, true);
+}
+
+typedef struct {
+ size_t total;
+ const git_commit **parents;
+ git_repository *repo;
+} commit_parent_data;
+
+static const git_oid *commit_parent_from_array(size_t curr, void *payload)
+{
+ commit_parent_data *data = payload;
+ const git_commit *commit;
+ if (curr >= data->total)
+ return NULL;
+ commit = data->parents[curr];
+ if (git_commit_owner(commit) != data->repo)
+ return NULL;
+ return git_commit_id(commit);
+}
+
+int git_commit_create(
+ git_oid *id,
+ git_repository *repo,
+ const char *update_ref,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *message_encoding,
+ const char *message,
+ const git_tree *tree,
+ size_t parent_count,
+ const git_commit *parents[])
+{
+ commit_parent_data data = { parent_count, parents, repo };
+
+ GIT_ASSERT_ARG(tree);
+ GIT_ASSERT_ARG(git_tree_owner(tree) == repo);
+
+ return git_commit__create_internal(
+ id, repo, update_ref, author, committer,
+ message_encoding, message, git_tree_id(tree),
+ commit_parent_from_array, &data, false);
+}
+
+static const git_oid *commit_parent_for_amend(size_t curr, void *payload)
+{
+ const git_commit *commit_to_amend = payload;
+ if (curr >= git_array_size(commit_to_amend->parent_ids))
+ return NULL;
+ return git_array_get(commit_to_amend->parent_ids, curr);
+}
+
+int git_commit_amend(
+ git_oid *id,
+ const git_commit *commit_to_amend,
+ const char *update_ref,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *message_encoding,
+ const char *message,
+ const git_tree *tree)
+{
+ git_repository *repo;
+ git_oid tree_id;
+ git_reference *ref;
+ int error;
+
+ GIT_ASSERT_ARG(id);
+ GIT_ASSERT_ARG(commit_to_amend);
+
+ repo = git_commit_owner(commit_to_amend);
+
+ if (!author)
+ author = git_commit_author(commit_to_amend);
+ if (!committer)
+ committer = git_commit_committer(commit_to_amend);
+ if (!message_encoding)
+ message_encoding = git_commit_message_encoding(commit_to_amend);
+ if (!message)
+ message = git_commit_message(commit_to_amend);
+
+ if (!tree) {
+ git_tree *old_tree;
+ GIT_ERROR_CHECK_ERROR( git_commit_tree(&old_tree, commit_to_amend) );
+ git_oid_cpy(&tree_id, git_tree_id(old_tree));
+ git_tree_free(old_tree);
+ } else {
+ GIT_ASSERT_ARG(git_tree_owner(tree) == repo);
+ git_oid_cpy(&tree_id, git_tree_id(tree));
+ }
+
+ if (update_ref) {
+ if ((error = git_reference_lookup_resolved(&ref, repo, update_ref, 5)) < 0)
+ return error;
+
+ if (git_oid_cmp(git_commit_id(commit_to_amend), git_reference_target(ref))) {
+ git_reference_free(ref);
+ git_error_set(GIT_ERROR_REFERENCE, "commit to amend is not the tip of the given branch");
+ return -1;
+ }
+ }
+
+ error = git_commit__create_internal(
+ id, repo, NULL, author, committer, message_encoding, message,
+ &tree_id, commit_parent_for_amend, (void *)commit_to_amend, false);
+
+ if (!error && update_ref) {
+ error = git_reference__update_for_commit(
+ repo, ref, NULL, id, "commit");
+ git_reference_free(ref);
+ }
+
+ return error;
+}
+
+static int commit_parse(
+ git_commit *commit,
+ const char *data,
+ size_t size,
+ git_commit__parse_options *opts)
+{
+ const char *buffer_start = data, *buffer;
+ const char *buffer_end = buffer_start + size;
+ git_oid parent_id;
+ size_t header_len;
+ git_signature dummy_sig;
+ int error;
+
+ GIT_ASSERT_ARG(commit);
+ GIT_ASSERT_ARG(data);
+ GIT_ASSERT_ARG(opts);
+
+ buffer = buffer_start;
+
+ /* Allocate for one, which will allow not to realloc 90% of the time */
+ git_array_init_to_size(commit->parent_ids, 1);
+ GIT_ERROR_CHECK_ARRAY(commit->parent_ids);
+
+ /* The tree is always the first field */
+ if (!(opts->flags & GIT_COMMIT_PARSE_QUICK)) {
+ if (git_object__parse_oid_header(&commit->tree_id,
+ &buffer, buffer_end, "tree ",
+ opts->oid_type) < 0)
+ goto bad_buffer;
+ } else {
+ size_t tree_len = strlen("tree ") + git_oid_hexsize(opts->oid_type) + 1;
+
+ if (buffer + tree_len > buffer_end)
+ goto bad_buffer;
+ buffer += tree_len;
+ }
+
+ while (git_object__parse_oid_header(&parent_id,
+ &buffer, buffer_end, "parent ",
+ opts->oid_type) == 0) {
+ git_oid *new_id = git_array_alloc(commit->parent_ids);
+ GIT_ERROR_CHECK_ALLOC(new_id);
+
+ git_oid_cpy(new_id, &parent_id);
+ }
+
+ if (!opts || !(opts->flags & GIT_COMMIT_PARSE_QUICK)) {
+ commit->author = git__malloc(sizeof(git_signature));
+ GIT_ERROR_CHECK_ALLOC(commit->author);
+
+ if ((error = git_signature__parse(commit->author, &buffer, buffer_end, "author ", '\n')) < 0)
+ return error;
+ }
+
+ /* Some tools create multiple author fields, ignore the extra ones */
+ while (!git__prefixncmp(buffer, buffer_end - buffer, "author ")) {
+ if ((error = git_signature__parse(&dummy_sig, &buffer, buffer_end, "author ", '\n')) < 0)
+ return error;
+
+ git__free(dummy_sig.name);
+ git__free(dummy_sig.email);
+ }
+
+ /* Always parse the committer; we need the commit time */
+ commit->committer = git__malloc(sizeof(git_signature));
+ GIT_ERROR_CHECK_ALLOC(commit->committer);
+
+ if ((error = git_signature__parse(commit->committer, &buffer, buffer_end, "committer ", '\n')) < 0)
+ return error;
+
+ if (opts && opts->flags & GIT_COMMIT_PARSE_QUICK)
+ return 0;
+
+ /* Parse add'l header entries */
+ while (buffer < buffer_end) {
+ const char *eoln = buffer;
+ if (buffer[-1] == '\n' && buffer[0] == '\n')
+ break;
+
+ while (eoln < buffer_end && *eoln != '\n')
+ ++eoln;
+
+ if (git__prefixncmp(buffer, buffer_end - buffer, "encoding ") == 0) {
+ buffer += strlen("encoding ");
+
+ commit->message_encoding = git__strndup(buffer, eoln - buffer);
+ GIT_ERROR_CHECK_ALLOC(commit->message_encoding);
+ }
+
+ if (eoln < buffer_end && *eoln == '\n')
+ ++eoln;
+ buffer = eoln;
+ }
+
+ header_len = buffer - buffer_start;
+ commit->raw_header = git__strndup(buffer_start, header_len);
+ GIT_ERROR_CHECK_ALLOC(commit->raw_header);
+
+ /* point "buffer" to data after header, +1 for the final LF */
+ buffer = buffer_start + header_len + 1;
+
+ /* extract commit message */
+ if (buffer <= buffer_end)
+ commit->raw_message = git__strndup(buffer, buffer_end - buffer);
+ else
+ commit->raw_message = git__strdup("");
+ GIT_ERROR_CHECK_ALLOC(commit->raw_message);
+
+ return 0;
+
+bad_buffer:
+ git_error_set(GIT_ERROR_OBJECT, "failed to parse bad commit object");
+ return GIT_EINVALID;
+}
+
+int git_commit__parse(
+ void *commit,
+ git_odb_object *odb_obj,
+ git_oid_t oid_type)
+{
+ git_commit__parse_options parse_options = {0};
+ parse_options.oid_type = oid_type;
+
+ return git_commit__parse_ext(commit, odb_obj, &parse_options);
+}
+
+int git_commit__parse_raw(
+ void *commit,
+ const char *data,
+ size_t size,
+ git_oid_t oid_type)
+{
+ git_commit__parse_options parse_options = {0};
+ parse_options.oid_type = oid_type;
+
+ return commit_parse(commit, data, size, &parse_options);
+}
+
+static int assign_commit_parents_from_graft(git_commit *commit, git_commit_graft *graft) {
+ size_t idx;
+ git_oid *oid;
+
+ git_array_clear(commit->parent_ids);
+ git_array_init_to_size(commit->parent_ids, git_array_size(graft->parents));
+ git_array_foreach(graft->parents, idx, oid) {
+ git_oid *id = git_array_alloc(commit->parent_ids);
+ GIT_ERROR_CHECK_ALLOC(id);
+
+ git_oid_cpy(id, oid);
+ }
+
+ return 0;
+}
+
+int git_commit__parse_ext(
+ git_commit *commit,
+ git_odb_object *odb_obj,
+ git_commit__parse_options *parse_opts)
+{
+ git_repository *repo = git_object_owner((git_object *)commit);
+ git_commit_graft *graft;
+ int error;
+
+ if ((error = commit_parse(commit, git_odb_object_data(odb_obj),
+ git_odb_object_size(odb_obj), parse_opts)) < 0)
+ return error;
+
+ /* Perform necessary grafts */
+ if (git_grafts_get(&graft, repo->grafts, git_odb_object_id(odb_obj)) != 0 &&
+ git_grafts_get(&graft, repo->shallow_grafts, git_odb_object_id(odb_obj)) != 0)
+ return 0;
+
+ return assign_commit_parents_from_graft(commit, graft);
+}
+
+#define GIT_COMMIT_GETTER(_rvalue, _name, _return, _invalid) \
+ _rvalue git_commit_##_name(const git_commit *commit) \
+ {\
+ GIT_ASSERT_ARG_WITH_RETVAL(commit, _invalid); \
+ return _return; \
+ }
+
+GIT_COMMIT_GETTER(const git_signature *, author, commit->author, NULL)
+GIT_COMMIT_GETTER(const git_signature *, committer, commit->committer, NULL)
+GIT_COMMIT_GETTER(const char *, message_raw, commit->raw_message, NULL)
+GIT_COMMIT_GETTER(const char *, message_encoding, commit->message_encoding, NULL)
+GIT_COMMIT_GETTER(const char *, raw_header, commit->raw_header, NULL)
+GIT_COMMIT_GETTER(git_time_t, time, commit->committer->when.time, INT64_MIN)
+GIT_COMMIT_GETTER(int, time_offset, commit->committer->when.offset, -1)
+GIT_COMMIT_GETTER(unsigned int, parentcount, (unsigned int)git_array_size(commit->parent_ids), 0)
+GIT_COMMIT_GETTER(const git_oid *, tree_id, &commit->tree_id, NULL)
+
+const char *git_commit_message(const git_commit *commit)
+{
+ const char *message;
+
+ GIT_ASSERT_ARG_WITH_RETVAL(commit, NULL);
+
+ message = commit->raw_message;
+
+ /* trim leading newlines from raw message */
+ while (*message && *message == '\n')
+ ++message;
+
+ return message;
+}
+
+const char *git_commit_summary(git_commit *commit)
+{
+ git_str summary = GIT_STR_INIT;
+ const char *msg, *space, *next;
+ bool space_contains_newline = false;
+
+ GIT_ASSERT_ARG_WITH_RETVAL(commit, NULL);
+
+ if (!commit->summary) {
+ for (msg = git_commit_message(commit), space = NULL; *msg; ++msg) {
+ char next_character = msg[0];
+ /* stop processing at the end of the first paragraph */
+ if (next_character == '\n') {
+ if (!msg[1])
+ break;
+ if (msg[1] == '\n')
+ break;
+ /* stop processing if next line contains only whitespace */
+ next = msg + 1;
+ while (*next && git__isspace_nonlf(*next)) {
+ ++next;
+ }
+ if (!*next || *next == '\n')
+ break;
+ }
+ /* record the beginning of contiguous whitespace runs */
+ if (git__isspace(next_character)) {
+ if(space == NULL) {
+ space = msg;
+ space_contains_newline = false;
+ }
+ space_contains_newline |= next_character == '\n';
+ }
+ /* the next character is non-space */
+ else {
+ /* process any recorded whitespace */
+ if (space) {
+ if(space_contains_newline)
+ git_str_putc(&summary, ' '); /* if the space contains a newline, collapse to ' ' */
+ else
+ git_str_put(&summary, space, (msg - space)); /* otherwise copy it */
+ space = NULL;
+ }
+ /* copy the next character */
+ git_str_putc(&summary, next_character);
+ }
+ }
+
+ commit->summary = git_str_detach(&summary);
+ if (!commit->summary)
+ commit->summary = git__strdup("");
+ }
+
+ return commit->summary;
+}
+
+const char *git_commit_body(git_commit *commit)
+{
+ const char *msg, *end;
+
+ GIT_ASSERT_ARG_WITH_RETVAL(commit, NULL);
+
+ if (!commit->body) {
+ /* search for end of summary */
+ for (msg = git_commit_message(commit); *msg; ++msg)
+ if (msg[0] == '\n' && (!msg[1] || msg[1] == '\n'))
+ break;
+
+ /* trim leading and trailing whitespace */
+ for (; *msg; ++msg)
+ if (!git__isspace(*msg))
+ break;
+ for (end = msg + strlen(msg) - 1; msg <= end; --end)
+ if (!git__isspace(*end))
+ break;
+
+ if (*msg)
+ commit->body = git__strndup(msg, end - msg + 1);
+ }
+
+ return commit->body;
+}
+
+int git_commit_tree(git_tree **tree_out, const git_commit *commit)
+{
+ GIT_ASSERT_ARG(commit);
+ return git_tree_lookup(tree_out, commit->object.repo, &commit->tree_id);
+}
+
+const git_oid *git_commit_parent_id(
+ const git_commit *commit, unsigned int n)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(commit, NULL);
+
+ return git_array_get(commit->parent_ids, n);
+}
+
+int git_commit_parent(
+ git_commit **parent, const git_commit *commit, unsigned int n)
+{
+ const git_oid *parent_id;
+ GIT_ASSERT_ARG(commit);
+
+ parent_id = git_commit_parent_id(commit, n);
+ if (parent_id == NULL) {
+ git_error_set(GIT_ERROR_INVALID, "parent %u does not exist", n);
+ return GIT_ENOTFOUND;
+ }
+
+ return git_commit_lookup(parent, commit->object.repo, parent_id);
+}
+
+int git_commit_nth_gen_ancestor(
+ git_commit **ancestor,
+ const git_commit *commit,
+ unsigned int n)
+{
+ git_commit *current, *parent = NULL;
+ int error;
+
+ GIT_ASSERT_ARG(ancestor);
+ GIT_ASSERT_ARG(commit);
+
+ if (git_commit_dup(&current, (git_commit *)commit) < 0)
+ return -1;
+
+ if (n == 0) {
+ *ancestor = current;
+ return 0;
+ }
+
+ while (n--) {
+ error = git_commit_parent(&parent, current, 0);
+
+ git_commit_free(current);
+
+ if (error < 0)
+ return error;
+
+ current = parent;
+ }
+
+ *ancestor = parent;
+ return 0;
+}
+
+int git_commit_header_field(
+ git_buf *out,
+ const git_commit *commit,
+ const char *field)
+{
+ GIT_BUF_WRAP_PRIVATE(out, git_commit__header_field, commit, field);
+}
+
+int git_commit__header_field(
+ git_str *out,
+ const git_commit *commit,
+ const char *field)
+{
+ const char *eol, *buf = commit->raw_header;
+
+ git_str_clear(out);
+
+ while ((eol = strchr(buf, '\n'))) {
+ /* We can skip continuations here */
+ if (buf[0] == ' ') {
+ buf = eol + 1;
+ continue;
+ }
+
+ /* Skip until we find the field we're after */
+ if (git__prefixcmp(buf, field)) {
+ buf = eol + 1;
+ continue;
+ }
+
+ buf += strlen(field);
+ /* Check that we're not matching a prefix but the field itself */
+ if (buf[0] != ' ') {
+ buf = eol + 1;
+ continue;
+ }
+
+ buf++; /* skip the SP */
+
+ git_str_put(out, buf, eol - buf);
+ if (git_str_oom(out))
+ goto oom;
+
+ /* If the next line starts with SP, it's multi-line, we must continue */
+ while (eol[1] == ' ') {
+ git_str_putc(out, '\n');
+ buf = eol + 2;
+ eol = strchr(buf, '\n');
+ if (!eol)
+ goto malformed;
+
+ git_str_put(out, buf, eol - buf);
+ }
+
+ if (git_str_oom(out))
+ goto oom;
+
+ return 0;
+ }
+
+ git_error_set(GIT_ERROR_OBJECT, "no such field '%s'", field);
+ return GIT_ENOTFOUND;
+
+malformed:
+ git_error_set(GIT_ERROR_OBJECT, "malformed header");
+ return -1;
+oom:
+ git_error_set_oom();
+ return -1;
+}
+
+int git_commit_extract_signature(
+ git_buf *signature_out,
+ git_buf *signed_data_out,
+ git_repository *repo,
+ git_oid *commit_id,
+ const char *field)
+{
+ git_str signature = GIT_STR_INIT, signed_data = GIT_STR_INIT;
+ int error;
+
+ if ((error = git_buf_tostr(&signature, signature_out)) < 0 ||
+ (error = git_buf_tostr(&signed_data, signed_data_out)) < 0 ||
+ (error = git_commit__extract_signature(&signature, &signed_data, repo, commit_id, field)) < 0 ||
+ (error = git_buf_fromstr(signature_out, &signature)) < 0 ||
+ (error = git_buf_fromstr(signed_data_out, &signed_data)) < 0)
+ goto done;
+
+done:
+ git_str_dispose(&signature);
+ git_str_dispose(&signed_data);
+ return error;
+}
+
+int git_commit__extract_signature(
+ git_str *signature,
+ git_str *signed_data,
+ git_repository *repo,
+ git_oid *commit_id,
+ const char *field)
+{
+ git_odb_object *obj;
+ git_odb *odb;
+ const char *buf;
+ const char *h, *eol;
+ int error;
+
+ git_str_clear(signature);
+ git_str_clear(signed_data);
+
+ if (!field)
+ field = "gpgsig";
+
+ if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
+ return error;
+
+ if ((error = git_odb_read(&obj, odb, commit_id)) < 0)
+ return error;
+
+ if (obj->cached.type != GIT_OBJECT_COMMIT) {
+ git_error_set(GIT_ERROR_INVALID, "the requested type does not match the type in the ODB");
+ error = GIT_ENOTFOUND;
+ goto cleanup;
+ }
+
+ buf = git_odb_object_data(obj);
+
+ while ((h = strchr(buf, '\n')) && h[1] != '\0') {
+ h++;
+ if (git__prefixcmp(buf, field)) {
+ if (git_str_put(signed_data, buf, h - buf) < 0)
+ return -1;
+
+ buf = h;
+ continue;
+ }
+
+ h = buf;
+ h += strlen(field);
+ eol = strchr(h, '\n');
+ if (h[0] != ' ') {
+ buf = h;
+ continue;
+ }
+ if (!eol)
+ goto malformed;
+
+ h++; /* skip the SP */
+
+ git_str_put(signature, h, eol - h);
+ if (git_str_oom(signature))
+ goto oom;
+
+ /* If the next line starts with SP, it's multi-line, we must continue */
+ while (eol[1] == ' ') {
+ git_str_putc(signature, '\n');
+ h = eol + 2;
+ eol = strchr(h, '\n');
+ if (!eol)
+ goto malformed;
+
+ git_str_put(signature, h, eol - h);
+ }
+
+ if (git_str_oom(signature))
+ goto oom;
+
+ error = git_str_puts(signed_data, eol+1);
+ git_odb_object_free(obj);
+ return error;
+ }
+
+ git_error_set(GIT_ERROR_OBJECT, "this commit is not signed");
+ error = GIT_ENOTFOUND;
+ goto cleanup;
+
+malformed:
+ git_error_set(GIT_ERROR_OBJECT, "malformed header");
+ error = -1;
+ goto cleanup;
+oom:
+ git_error_set_oom();
+ error = -1;
+ goto cleanup;
+
+cleanup:
+ git_odb_object_free(obj);
+ git_str_clear(signature);
+ git_str_clear(signed_data);
+ return error;
+}
+
+int git_commit_create_buffer(
+ git_buf *out,
+ git_repository *repo,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *message_encoding,
+ const char *message,
+ const git_tree *tree,
+ size_t parent_count,
+ const git_commit *parents[])
+{
+ GIT_BUF_WRAP_PRIVATE(out, git_commit__create_buffer, repo,
+ author, committer, message_encoding, message,
+ tree, parent_count, parents);
+}
+
+int git_commit__create_buffer(
+ git_str *out,
+ git_repository *repo,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *message_encoding,
+ const char *message,
+ const git_tree *tree,
+ size_t parent_count,
+ const git_commit *parents[])
+{
+ int error;
+ commit_parent_data data = { parent_count, parents, repo };
+ git_array_oid_t parents_arr = GIT_ARRAY_INIT;
+ const git_oid *tree_id;
+
+ GIT_ASSERT_ARG(tree);
+ GIT_ASSERT_ARG(git_tree_owner(tree) == repo);
+
+ tree_id = git_tree_id(tree);
+
+ if ((error = validate_tree_and_parents(&parents_arr, repo, tree_id, commit_parent_from_array, &data, NULL, true)) < 0)
+ return error;
+
+ error = git_commit__create_buffer_internal(
+ out, author, committer,
+ message_encoding, message, tree_id,
+ &parents_arr);
+
+ git_array_clear(parents_arr);
+ return error;
+}
+
+/**
+ * Append to 'out' properly marking continuations when there's a newline in 'content'
+ */
+static int format_header_field(git_str *out, const char *field, const char *content)
+{
+ const char *lf;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(field);
+ GIT_ASSERT_ARG(content);
+
+ git_str_puts(out, field);
+ git_str_putc(out, ' ');
+
+ while ((lf = strchr(content, '\n')) != NULL) {
+ git_str_put(out, content, lf - content);
+ git_str_puts(out, "\n ");
+ content = lf + 1;
+ }
+
+ git_str_puts(out, content);
+ git_str_putc(out, '\n');
+
+ return git_str_oom(out) ? -1 : 0;
+}
+
+static const git_oid *commit_parent_from_commit(size_t n, void *payload)
+{
+ const git_commit *commit = (const git_commit *) payload;
+
+ return git_array_get(commit->parent_ids, n);
+
+}
+
+int git_commit_create_with_signature(
+ git_oid *out,
+ git_repository *repo,
+ const char *commit_content,
+ const char *signature,
+ const char *signature_field)
+{
+ git_odb *odb;
+ int error = 0;
+ const char *field;
+ const char *header_end;
+ git_str commit = GIT_STR_INIT;
+ git_commit *parsed;
+ git_array_oid_t parents = GIT_ARRAY_INIT;
+ git_commit__parse_options parse_opts = {0};
+
+ parse_opts.oid_type = repo->oid_type;
+
+ /* The first step is to verify that all the tree and parents exist */
+ parsed = git__calloc(1, sizeof(git_commit));
+ GIT_ERROR_CHECK_ALLOC(parsed);
+ if (commit_parse(parsed, commit_content, strlen(commit_content), &parse_opts) < 0) {
+ error = -1;
+ goto cleanup;
+ }
+
+ if ((error = validate_tree_and_parents(&parents, repo, &parsed->tree_id, commit_parent_from_commit, parsed, NULL, true)) < 0)
+ goto cleanup;
+
+ git_array_clear(parents);
+
+ /* Then we start appending by identifying the end of the commit header */
+ header_end = strstr(commit_content, "\n\n");
+ if (!header_end) {
+ git_error_set(GIT_ERROR_INVALID, "malformed commit contents");
+ error = -1;
+ goto cleanup;
+ }
+
+ /* The header ends after the first LF */
+ header_end++;
+ git_str_put(&commit, commit_content, header_end - commit_content);
+
+ if (signature != NULL) {
+ field = signature_field ? signature_field : "gpgsig";
+
+ if ((error = format_header_field(&commit, field, signature)) < 0)
+ goto cleanup;
+ }
+
+ git_str_puts(&commit, header_end);
+
+ if (git_str_oom(&commit))
+ return -1;
+
+ if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
+ goto cleanup;
+
+ if ((error = git_odb_write(out, odb, commit.ptr, commit.size, GIT_OBJECT_COMMIT)) < 0)
+ goto cleanup;
+
+cleanup:
+ git_commit__free(parsed);
+ git_str_dispose(&commit);
+ return error;
+}
+
+int git_commit_committer_with_mailmap(
+ git_signature **out, const git_commit *commit, const git_mailmap *mailmap)
+{
+ return git_mailmap_resolve_signature(out, mailmap, commit->committer);
+}
+
+int git_commit_author_with_mailmap(
+ git_signature **out, const git_commit *commit, const git_mailmap *mailmap)
+{
+ return git_mailmap_resolve_signature(out, mailmap, commit->author);
+}
diff --git a/src/libgit2/commit.h b/src/libgit2/commit.h
new file mode 100644
index 0000000..c25fee3
--- /dev/null
+++ b/src/libgit2/commit.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_commit_h__
+#define INCLUDE_commit_h__
+
+#include "common.h"
+
+#include "git2/commit.h"
+#include "tree.h"
+#include "repository.h"
+#include "array.h"
+
+#include <time.h>
+
+struct git_commit {
+ git_object object;
+
+ git_array_t(git_oid) parent_ids;
+ git_oid tree_id;
+
+ git_signature *author;
+ git_signature *committer;
+
+ char *message_encoding;
+ char *raw_message;
+ char *raw_header;
+
+ char *summary;
+ char *body;
+};
+
+typedef struct {
+ git_oid_t oid_type;
+ unsigned int flags;
+} git_commit__parse_options;
+
+typedef enum {
+ /** Only parse parents and committer info */
+ GIT_COMMIT_PARSE_QUICK = (1 << 0)
+} git_commit__parse_flags;
+
+int git_commit__header_field(
+ git_str *out,
+ const git_commit *commit,
+ const char *field);
+
+int git_commit__extract_signature(
+ git_str *signature,
+ git_str *signed_data,
+ git_repository *repo,
+ git_oid *commit_id,
+ const char *field);
+
+int git_commit__create_buffer(
+ git_str *out,
+ git_repository *repo,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *message_encoding,
+ const char *message,
+ const git_tree *tree,
+ size_t parent_count,
+ const git_commit *parents[]);
+
+int git_commit__parse(
+ void *commit,
+ git_odb_object *obj,
+ git_oid_t oid_type);
+
+int git_commit__parse_raw(
+ void *commit,
+ const char *data,
+ size_t size,
+ git_oid_t oid_type);
+
+int git_commit__parse_ext(
+ git_commit *commit,
+ git_odb_object *odb_obj,
+ git_commit__parse_options *parse_opts);
+
+void git_commit__free(void *commit);
+
+#endif
diff --git a/src/libgit2/commit_graph.c b/src/libgit2/commit_graph.c
new file mode 100644
index 0000000..4edd711
--- /dev/null
+++ b/src/libgit2/commit_graph.c
@@ -0,0 +1,1309 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "commit_graph.h"
+
+#include "array.h"
+#include "buf.h"
+#include "filebuf.h"
+#include "futils.h"
+#include "hash.h"
+#include "oidarray.h"
+#include "oidmap.h"
+#include "pack.h"
+#include "repository.h"
+#include "revwalk.h"
+
+#define GIT_COMMIT_GRAPH_MISSING_PARENT 0x70000000
+#define GIT_COMMIT_GRAPH_GENERATION_NUMBER_MAX 0x3FFFFFFF
+#define GIT_COMMIT_GRAPH_GENERATION_NUMBER_INFINITY 0xFFFFFFFF
+
+#define COMMIT_GRAPH_SIGNATURE 0x43475048 /* "CGPH" */
+#define COMMIT_GRAPH_VERSION 1
+#define COMMIT_GRAPH_OBJECT_ID_VERSION 1
+struct git_commit_graph_header {
+ uint32_t signature;
+ uint8_t version;
+ uint8_t object_id_version;
+ uint8_t chunks;
+ uint8_t base_graph_files;
+};
+
+#define COMMIT_GRAPH_OID_FANOUT_ID 0x4f494446 /* "OIDF" */
+#define COMMIT_GRAPH_OID_LOOKUP_ID 0x4f49444c /* "OIDL" */
+#define COMMIT_GRAPH_COMMIT_DATA_ID 0x43444154 /* "CDAT" */
+#define COMMIT_GRAPH_EXTRA_EDGE_LIST_ID 0x45444745 /* "EDGE" */
+#define COMMIT_GRAPH_BLOOM_FILTER_INDEX_ID 0x42494458 /* "BIDX" */
+#define COMMIT_GRAPH_BLOOM_FILTER_DATA_ID 0x42444154 /* "BDAT" */
+
+struct git_commit_graph_chunk {
+ off64_t offset;
+ size_t length;
+};
+
+typedef git_array_t(size_t) parent_index_array_t;
+
+struct packed_commit {
+ size_t index;
+ git_oid sha1;
+ git_oid tree_oid;
+ uint32_t generation;
+ git_time_t commit_time;
+ git_array_oid_t parents;
+ parent_index_array_t parent_indices;
+};
+
+static void packed_commit_free(struct packed_commit *p)
+{
+ if (!p)
+ return;
+
+ git_array_clear(p->parents);
+ git_array_clear(p->parent_indices);
+ git__free(p);
+}
+
+static struct packed_commit *packed_commit_new(git_commit *commit)
+{
+ unsigned int i, parentcount = git_commit_parentcount(commit);
+ struct packed_commit *p = git__calloc(1, sizeof(struct packed_commit));
+ if (!p)
+ goto cleanup;
+
+ git_array_init_to_size(p->parents, parentcount);
+ if (parentcount && !p->parents.ptr)
+ goto cleanup;
+
+ if (git_oid_cpy(&p->sha1, git_commit_id(commit)) < 0)
+ goto cleanup;
+ if (git_oid_cpy(&p->tree_oid, git_commit_tree_id(commit)) < 0)
+ goto cleanup;
+ p->commit_time = git_commit_time(commit);
+
+ for (i = 0; i < parentcount; ++i) {
+ git_oid *parent_id = git_array_alloc(p->parents);
+ if (!parent_id)
+ goto cleanup;
+ if (git_oid_cpy(parent_id, git_commit_parent_id(commit, i)) < 0)
+ goto cleanup;
+ }
+
+ return p;
+
+cleanup:
+ packed_commit_free(p);
+ return NULL;
+}
+
+typedef int (*commit_graph_write_cb)(const char *buf, size_t size, void *cb_data);
+
+static int commit_graph_error(const char *message)
+{
+ git_error_set(GIT_ERROR_ODB, "invalid commit-graph file - %s", message);
+ return -1;
+}
+
+static int commit_graph_parse_oid_fanout(
+ git_commit_graph_file *file,
+ const unsigned char *data,
+ struct git_commit_graph_chunk *chunk_oid_fanout)
+{
+ uint32_t i, nr;
+ if (chunk_oid_fanout->offset == 0)
+ return commit_graph_error("missing OID Fanout chunk");
+ if (chunk_oid_fanout->length == 0)
+ return commit_graph_error("empty OID Fanout chunk");
+ if (chunk_oid_fanout->length != 256 * 4)
+ return commit_graph_error("OID Fanout chunk has wrong length");
+
+ file->oid_fanout = (const uint32_t *)(data + chunk_oid_fanout->offset);
+ nr = 0;
+ for (i = 0; i < 256; ++i) {
+ uint32_t n = ntohl(file->oid_fanout[i]);
+ if (n < nr)
+ return commit_graph_error("index is non-monotonic");
+ nr = n;
+ }
+ file->num_commits = nr;
+ return 0;
+}
+
+static int commit_graph_parse_oid_lookup(
+ git_commit_graph_file *file,
+ const unsigned char *data,
+ struct git_commit_graph_chunk *chunk_oid_lookup)
+{
+ uint32_t i;
+ unsigned char *oid, *prev_oid, zero_oid[GIT_OID_MAX_SIZE] = {0};
+ size_t oid_size;
+
+ oid_size = git_oid_size(file->oid_type);
+
+ if (chunk_oid_lookup->offset == 0)
+ return commit_graph_error("missing OID Lookup chunk");
+ if (chunk_oid_lookup->length == 0)
+ return commit_graph_error("empty OID Lookup chunk");
+ if (chunk_oid_lookup->length != file->num_commits * oid_size)
+ return commit_graph_error("OID Lookup chunk has wrong length");
+
+ file->oid_lookup = oid = (unsigned char *)(data + chunk_oid_lookup->offset);
+ prev_oid = zero_oid;
+ for (i = 0; i < file->num_commits; ++i, oid += oid_size) {
+ if (git_oid_raw_cmp(prev_oid, oid, oid_size) >= 0)
+ return commit_graph_error("OID Lookup index is non-monotonic");
+ prev_oid = oid;
+ }
+
+ return 0;
+}
+
+static int commit_graph_parse_commit_data(
+ git_commit_graph_file *file,
+ const unsigned char *data,
+ struct git_commit_graph_chunk *chunk_commit_data)
+{
+ size_t oid_size = git_oid_size(file->oid_type);
+
+ if (chunk_commit_data->offset == 0)
+ return commit_graph_error("missing Commit Data chunk");
+ if (chunk_commit_data->length == 0)
+ return commit_graph_error("empty Commit Data chunk");
+ if (chunk_commit_data->length != file->num_commits * (oid_size + 16))
+ return commit_graph_error("Commit Data chunk has wrong length");
+
+ file->commit_data = data + chunk_commit_data->offset;
+
+ return 0;
+}
+
+static int commit_graph_parse_extra_edge_list(
+ git_commit_graph_file *file,
+ const unsigned char *data,
+ struct git_commit_graph_chunk *chunk_extra_edge_list)
+{
+ if (chunk_extra_edge_list->length == 0)
+ return 0;
+ if (chunk_extra_edge_list->length % 4 != 0)
+ return commit_graph_error("malformed Extra Edge List chunk");
+
+ file->extra_edge_list = data + chunk_extra_edge_list->offset;
+ file->num_extra_edge_list = chunk_extra_edge_list->length / 4;
+
+ return 0;
+}
+
+int git_commit_graph_file_parse(
+ git_commit_graph_file *file,
+ const unsigned char *data,
+ size_t size)
+{
+ struct git_commit_graph_header *hdr;
+ const unsigned char *chunk_hdr;
+ struct git_commit_graph_chunk *last_chunk;
+ uint32_t i;
+ uint64_t last_chunk_offset, chunk_offset, trailer_offset;
+ size_t checksum_size;
+ int error;
+ struct git_commit_graph_chunk chunk_oid_fanout = {0}, chunk_oid_lookup = {0},
+ chunk_commit_data = {0}, chunk_extra_edge_list = {0},
+ chunk_unsupported = {0};
+
+ GIT_ASSERT_ARG(file);
+
+ checksum_size = git_oid_size(file->oid_type);
+
+ if (size < sizeof(struct git_commit_graph_header) + checksum_size)
+ return commit_graph_error("commit-graph is too short");
+
+ hdr = ((struct git_commit_graph_header *)data);
+
+ if (hdr->signature != htonl(COMMIT_GRAPH_SIGNATURE) || hdr->version != COMMIT_GRAPH_VERSION
+ || hdr->object_id_version != COMMIT_GRAPH_OBJECT_ID_VERSION) {
+ return commit_graph_error("unsupported commit-graph version");
+ }
+ if (hdr->chunks == 0)
+ return commit_graph_error("no chunks in commit-graph");
+
+ /*
+ * The very first chunk's offset should be after the header, all the chunk
+ * headers, and a special zero chunk.
+ */
+ last_chunk_offset = sizeof(struct git_commit_graph_header) + (1 + hdr->chunks) * 12;
+ trailer_offset = size - checksum_size;
+
+ if (trailer_offset < last_chunk_offset)
+ return commit_graph_error("wrong commit-graph size");
+ memcpy(file->checksum, (data + trailer_offset), checksum_size);
+
+ chunk_hdr = data + sizeof(struct git_commit_graph_header);
+ last_chunk = NULL;
+ for (i = 0; i < hdr->chunks; ++i, chunk_hdr += 12) {
+ chunk_offset = ((uint64_t)ntohl(*((uint32_t *)(chunk_hdr + 4)))) << 32
+ | ((uint64_t)ntohl(*((uint32_t *)(chunk_hdr + 8))));
+ if (chunk_offset < last_chunk_offset)
+ return commit_graph_error("chunks are non-monotonic");
+ if (chunk_offset >= trailer_offset)
+ return commit_graph_error("chunks extend beyond the trailer");
+ if (last_chunk != NULL)
+ last_chunk->length = (size_t)(chunk_offset - last_chunk_offset);
+ last_chunk_offset = chunk_offset;
+
+ switch (ntohl(*((uint32_t *)(chunk_hdr + 0)))) {
+ case COMMIT_GRAPH_OID_FANOUT_ID:
+ chunk_oid_fanout.offset = last_chunk_offset;
+ last_chunk = &chunk_oid_fanout;
+ break;
+
+ case COMMIT_GRAPH_OID_LOOKUP_ID:
+ chunk_oid_lookup.offset = last_chunk_offset;
+ last_chunk = &chunk_oid_lookup;
+ break;
+
+ case COMMIT_GRAPH_COMMIT_DATA_ID:
+ chunk_commit_data.offset = last_chunk_offset;
+ last_chunk = &chunk_commit_data;
+ break;
+
+ case COMMIT_GRAPH_EXTRA_EDGE_LIST_ID:
+ chunk_extra_edge_list.offset = last_chunk_offset;
+ last_chunk = &chunk_extra_edge_list;
+ break;
+
+ case COMMIT_GRAPH_BLOOM_FILTER_INDEX_ID:
+ case COMMIT_GRAPH_BLOOM_FILTER_DATA_ID:
+ chunk_unsupported.offset = last_chunk_offset;
+ last_chunk = &chunk_unsupported;
+ break;
+
+ default:
+ return commit_graph_error("unrecognized chunk ID");
+ }
+ }
+ last_chunk->length = (size_t)(trailer_offset - last_chunk_offset);
+
+ error = commit_graph_parse_oid_fanout(file, data, &chunk_oid_fanout);
+ if (error < 0)
+ return error;
+ error = commit_graph_parse_oid_lookup(file, data, &chunk_oid_lookup);
+ if (error < 0)
+ return error;
+ error = commit_graph_parse_commit_data(file, data, &chunk_commit_data);
+ if (error < 0)
+ return error;
+ error = commit_graph_parse_extra_edge_list(file, data, &chunk_extra_edge_list);
+ if (error < 0)
+ return error;
+
+ return 0;
+}
+
+int git_commit_graph_new(
+ git_commit_graph **cgraph_out,
+ const char *objects_dir,
+ bool open_file,
+ git_oid_t oid_type)
+{
+ git_commit_graph *cgraph = NULL;
+ int error = 0;
+
+ GIT_ASSERT_ARG(cgraph_out);
+ GIT_ASSERT_ARG(objects_dir);
+ GIT_ASSERT_ARG(oid_type);
+
+ cgraph = git__calloc(1, sizeof(git_commit_graph));
+ GIT_ERROR_CHECK_ALLOC(cgraph);
+
+ cgraph->oid_type = oid_type;
+
+ error = git_str_joinpath(&cgraph->filename, objects_dir, "info/commit-graph");
+ if (error < 0)
+ goto error;
+
+ if (open_file) {
+ error = git_commit_graph_file_open(&cgraph->file,
+ git_str_cstr(&cgraph->filename), oid_type);
+
+ if (error < 0)
+ goto error;
+
+ cgraph->checked = 1;
+ }
+
+ *cgraph_out = cgraph;
+ return 0;
+
+error:
+ git_commit_graph_free(cgraph);
+ return error;
+}
+
+int git_commit_graph_validate(git_commit_graph *cgraph) {
+ unsigned char checksum[GIT_HASH_MAX_SIZE];
+ git_hash_algorithm_t checksum_type;
+ size_t checksum_size, trailer_offset;
+
+ checksum_type = git_oid_algorithm(cgraph->oid_type);
+ checksum_size = git_hash_size(checksum_type);
+ trailer_offset = cgraph->file->graph_map.len - checksum_size;
+
+ if (cgraph->file->graph_map.len < checksum_size)
+ return commit_graph_error("map length too small");
+
+ if (git_hash_buf(checksum, cgraph->file->graph_map.data, trailer_offset, checksum_type) < 0)
+ return commit_graph_error("could not calculate signature");
+ if (memcmp(checksum, cgraph->file->checksum, checksum_size) != 0)
+ return commit_graph_error("index signature mismatch");
+
+ return 0;
+}
+
+int git_commit_graph_open(
+ git_commit_graph **cgraph_out,
+ const char *objects_dir
+#ifdef GIT_EXPERIMENTAL_SHA256
+ , git_oid_t oid_type
+#endif
+ )
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ git_oid_t oid_type = GIT_OID_SHA1;
+#endif
+ int error;
+
+ error = git_commit_graph_new(cgraph_out, objects_dir, true,
+ oid_type);
+
+ if (!error)
+ return git_commit_graph_validate(*cgraph_out);
+
+ return error;
+}
+
+int git_commit_graph_file_open(
+ git_commit_graph_file **file_out,
+ const char *path,
+ git_oid_t oid_type)
+{
+ git_commit_graph_file *file;
+ git_file fd = -1;
+ size_t cgraph_size;
+ struct stat st;
+ int error;
+
+ /* TODO: properly open the file without access time using O_NOATIME */
+ fd = git_futils_open_ro(path);
+ if (fd < 0)
+ return fd;
+
+ if (p_fstat(fd, &st) < 0) {
+ p_close(fd);
+ git_error_set(GIT_ERROR_ODB, "commit-graph file not found - '%s'", path);
+ return GIT_ENOTFOUND;
+ }
+
+ if (!S_ISREG(st.st_mode) || !git__is_sizet(st.st_size)) {
+ p_close(fd);
+ git_error_set(GIT_ERROR_ODB, "invalid pack index '%s'", path);
+ return GIT_ENOTFOUND;
+ }
+ cgraph_size = (size_t)st.st_size;
+
+ file = git__calloc(1, sizeof(git_commit_graph_file));
+ GIT_ERROR_CHECK_ALLOC(file);
+
+ file->oid_type = oid_type;
+
+ error = git_futils_mmap_ro(&file->graph_map, fd, 0, cgraph_size);
+ p_close(fd);
+ if (error < 0) {
+ git_commit_graph_file_free(file);
+ return error;
+ }
+
+ if ((error = git_commit_graph_file_parse(file, file->graph_map.data, cgraph_size)) < 0) {
+ git_commit_graph_file_free(file);
+ return error;
+ }
+
+ *file_out = file;
+ return 0;
+}
+
+int git_commit_graph_get_file(
+ git_commit_graph_file **file_out,
+ git_commit_graph *cgraph)
+{
+ if (!cgraph->checked) {
+ int error = 0;
+ git_commit_graph_file *result = NULL;
+
+ /* We only check once, no matter the result. */
+ cgraph->checked = 1;
+
+ /* Best effort */
+ error = git_commit_graph_file_open(&result,
+ git_str_cstr(&cgraph->filename), cgraph->oid_type);
+
+ if (error < 0)
+ return error;
+
+ cgraph->file = result;
+ }
+ if (!cgraph->file)
+ return GIT_ENOTFOUND;
+
+ *file_out = cgraph->file;
+ return 0;
+}
+
+void git_commit_graph_refresh(git_commit_graph *cgraph)
+{
+ if (!cgraph->checked)
+ return;
+
+ if (cgraph->file
+ && git_commit_graph_file_needs_refresh(cgraph->file, git_str_cstr(&cgraph->filename))) {
+ /* We just free the commit graph. The next time it is requested, it will be
+ * re-loaded. */
+ git_commit_graph_file_free(cgraph->file);
+ cgraph->file = NULL;
+ }
+ /* Force a lazy re-check next time it is needed. */
+ cgraph->checked = 0;
+}
+
+static int git_commit_graph_entry_get_byindex(
+ git_commit_graph_entry *e,
+ const git_commit_graph_file *file,
+ size_t pos)
+{
+ const unsigned char *commit_data;
+ size_t oid_size = git_oid_size(file->oid_type);
+
+ GIT_ASSERT_ARG(e);
+ GIT_ASSERT_ARG(file);
+
+ if (pos >= file->num_commits) {
+ git_error_set(GIT_ERROR_INVALID, "commit index %zu does not exist", pos);
+ return GIT_ENOTFOUND;
+ }
+
+ commit_data = file->commit_data + pos * (oid_size + 4 * sizeof(uint32_t));
+ git_oid__fromraw(&e->tree_oid, commit_data, file->oid_type);
+ e->parent_indices[0] = ntohl(*((uint32_t *)(commit_data + oid_size)));
+ e->parent_indices[1] = ntohl(
+ *((uint32_t *)(commit_data + oid_size + sizeof(uint32_t))));
+ e->parent_count = (e->parent_indices[0] != GIT_COMMIT_GRAPH_MISSING_PARENT)
+ + (e->parent_indices[1] != GIT_COMMIT_GRAPH_MISSING_PARENT);
+ e->generation = ntohl(*((uint32_t *)(commit_data + oid_size + 2 * sizeof(uint32_t))));
+ e->commit_time = ntohl(*((uint32_t *)(commit_data + oid_size + 3 * sizeof(uint32_t))));
+
+ e->commit_time |= (e->generation & UINT64_C(0x3)) << UINT64_C(32);
+ e->generation >>= 2u;
+ if (e->parent_indices[1] & 0x80000000u) {
+ uint32_t extra_edge_list_pos = e->parent_indices[1] & 0x7fffffff;
+
+ /* Make sure we're not being sent out of bounds */
+ if (extra_edge_list_pos >= file->num_extra_edge_list) {
+ git_error_set(GIT_ERROR_INVALID,
+ "commit %u does not exist",
+ extra_edge_list_pos);
+ return GIT_ENOTFOUND;
+ }
+
+ e->extra_parents_index = extra_edge_list_pos;
+ while (extra_edge_list_pos < file->num_extra_edge_list
+ && (ntohl(*(
+ (uint32_t *)(file->extra_edge_list
+ + extra_edge_list_pos * sizeof(uint32_t))))
+ & 0x80000000u)
+ == 0) {
+ extra_edge_list_pos++;
+ e->parent_count++;
+ }
+ }
+
+ git_oid__fromraw(&e->sha1, &file->oid_lookup[pos * oid_size], file->oid_type);
+ return 0;
+}
+
+bool git_commit_graph_file_needs_refresh(const git_commit_graph_file *file, const char *path)
+{
+ git_file fd = -1;
+ struct stat st;
+ ssize_t bytes_read;
+ unsigned char checksum[GIT_HASH_MAX_SIZE];
+ size_t checksum_size = git_oid_size(file->oid_type);
+
+ /* TODO: properly open the file without access time using O_NOATIME */
+ fd = git_futils_open_ro(path);
+ if (fd < 0)
+ return true;
+
+ if (p_fstat(fd, &st) < 0) {
+ p_close(fd);
+ return true;
+ }
+
+ if (!S_ISREG(st.st_mode) || !git__is_sizet(st.st_size)
+ || (size_t)st.st_size != file->graph_map.len) {
+ p_close(fd);
+ return true;
+ }
+
+ bytes_read = p_pread(fd, checksum, checksum_size, st.st_size - checksum_size);
+ p_close(fd);
+ if (bytes_read != (ssize_t)checksum_size)
+ return true;
+
+ return (memcmp(checksum, file->checksum, checksum_size) != 0);
+}
+
+int git_commit_graph_entry_find(
+ git_commit_graph_entry *e,
+ const git_commit_graph_file *file,
+ const git_oid *short_oid,
+ size_t len)
+{
+ int pos, found = 0;
+ uint32_t hi, lo;
+ const unsigned char *current = NULL;
+ size_t oid_size, oid_hexsize;
+
+ GIT_ASSERT_ARG(e);
+ GIT_ASSERT_ARG(file);
+ GIT_ASSERT_ARG(short_oid);
+
+ oid_size = git_oid_size(file->oid_type);
+ oid_hexsize = git_oid_hexsize(file->oid_type);
+
+ hi = ntohl(file->oid_fanout[(int)short_oid->id[0]]);
+ lo = ((short_oid->id[0] == 0x0) ? 0 : ntohl(file->oid_fanout[(int)short_oid->id[0] - 1]));
+
+ pos = git_pack__lookup_id(file->oid_lookup, oid_size, lo, hi,
+ short_oid->id, file->oid_type);
+
+ if (pos >= 0) {
+ /* An object matching exactly the oid was found */
+ found = 1;
+ current = file->oid_lookup + (pos * oid_size);
+ } else {
+ /* No object was found */
+ /* pos refers to the object with the "closest" oid to short_oid */
+ pos = -1 - pos;
+ if (pos < (int)file->num_commits) {
+ current = file->oid_lookup + (pos * oid_size);
+
+ if (!git_oid_raw_ncmp(short_oid->id, current, len))
+ found = 1;
+ }
+ }
+
+ if (found && len != oid_hexsize && pos + 1 < (int)file->num_commits) {
+ /* Check for ambiguousity */
+ const unsigned char *next = current + oid_size;
+
+ if (!git_oid_raw_ncmp(short_oid->id, next, len))
+ found = 2;
+ }
+
+ if (!found)
+ return git_odb__error_notfound(
+ "failed to find offset for commit-graph index entry", short_oid, len);
+ if (found > 1)
+ return git_odb__error_ambiguous(
+ "found multiple offsets for commit-graph index entry");
+
+ return git_commit_graph_entry_get_byindex(e, file, pos);
+}
+
+int git_commit_graph_entry_parent(
+ git_commit_graph_entry *parent,
+ const git_commit_graph_file *file,
+ const git_commit_graph_entry *entry,
+ size_t n)
+{
+ GIT_ASSERT_ARG(parent);
+ GIT_ASSERT_ARG(file);
+
+ if (n >= entry->parent_count) {
+ git_error_set(GIT_ERROR_INVALID, "parent index %zu does not exist", n);
+ return GIT_ENOTFOUND;
+ }
+
+ if (n == 0 || (n == 1 && entry->parent_count == 2))
+ return git_commit_graph_entry_get_byindex(parent, file, entry->parent_indices[n]);
+
+ return git_commit_graph_entry_get_byindex(
+ parent,
+ file,
+ ntohl(
+ *(uint32_t *)(file->extra_edge_list
+ + (entry->extra_parents_index + n - 1)
+ * sizeof(uint32_t)))
+ & 0x7fffffff);
+}
+
+int git_commit_graph_file_close(git_commit_graph_file *file)
+{
+ GIT_ASSERT_ARG(file);
+
+ if (file->graph_map.data)
+ git_futils_mmap_free(&file->graph_map);
+
+ return 0;
+}
+
+void git_commit_graph_free(git_commit_graph *cgraph)
+{
+ if (!cgraph)
+ return;
+
+ git_str_dispose(&cgraph->filename);
+ git_commit_graph_file_free(cgraph->file);
+ git__free(cgraph);
+}
+
+void git_commit_graph_file_free(git_commit_graph_file *file)
+{
+ if (!file)
+ return;
+
+ git_commit_graph_file_close(file);
+ git__free(file);
+}
+
+static int packed_commit__cmp(const void *a_, const void *b_)
+{
+ const struct packed_commit *a = a_;
+ const struct packed_commit *b = b_;
+ return git_oid_cmp(&a->sha1, &b->sha1);
+}
+
+int git_commit_graph_writer_new(
+ git_commit_graph_writer **out,
+ const char *objects_info_dir
+#ifdef GIT_EXPERIMENTAL_SHA256
+ , git_oid_t oid_type
+#endif
+ )
+{
+ git_commit_graph_writer *w;
+
+#ifndef GIT_EXPERIMENTAL_SHA256
+ git_oid_t oid_type = GIT_OID_SHA1;
+#endif
+
+ GIT_ASSERT_ARG(out && objects_info_dir && oid_type);
+
+ w = git__calloc(1, sizeof(git_commit_graph_writer));
+ GIT_ERROR_CHECK_ALLOC(w);
+
+ w->oid_type = oid_type;
+
+ if (git_str_sets(&w->objects_info_dir, objects_info_dir) < 0) {
+ git__free(w);
+ return -1;
+ }
+
+ if (git_vector_init(&w->commits, 0, packed_commit__cmp) < 0) {
+ git_str_dispose(&w->objects_info_dir);
+ git__free(w);
+ return -1;
+ }
+
+ *out = w;
+ return 0;
+}
+
+void git_commit_graph_writer_free(git_commit_graph_writer *w)
+{
+ struct packed_commit *packed_commit;
+ size_t i;
+
+ if (!w)
+ return;
+
+ git_vector_foreach (&w->commits, i, packed_commit)
+ packed_commit_free(packed_commit);
+ git_vector_free(&w->commits);
+ git_str_dispose(&w->objects_info_dir);
+ git__free(w);
+}
+
+struct object_entry_cb_state {
+ git_repository *repo;
+ git_odb *db;
+ git_vector *commits;
+};
+
+static int object_entry__cb(const git_oid *id, void *data)
+{
+ struct object_entry_cb_state *state = (struct object_entry_cb_state *)data;
+ git_commit *commit = NULL;
+ struct packed_commit *packed_commit = NULL;
+ size_t header_len;
+ git_object_t header_type;
+ int error = 0;
+
+ error = git_odb_read_header(&header_len, &header_type, state->db, id);
+ if (error < 0)
+ return error;
+
+ if (header_type != GIT_OBJECT_COMMIT)
+ return 0;
+
+ error = git_commit_lookup(&commit, state->repo, id);
+ if (error < 0)
+ return error;
+
+ packed_commit = packed_commit_new(commit);
+ git_commit_free(commit);
+ GIT_ERROR_CHECK_ALLOC(packed_commit);
+
+ error = git_vector_insert(state->commits, packed_commit);
+ if (error < 0) {
+ packed_commit_free(packed_commit);
+ return error;
+ }
+
+ return 0;
+}
+
+int git_commit_graph_writer_add_index_file(
+ git_commit_graph_writer *w,
+ git_repository *repo,
+ const char *idx_path)
+{
+ int error;
+ struct git_pack_file *p = NULL;
+ struct object_entry_cb_state state = {0};
+ state.repo = repo;
+ state.commits = &w->commits;
+
+ error = git_repository_odb(&state.db, repo);
+ if (error < 0)
+ goto cleanup;
+
+ /* TODO: SHA256 */
+ error = git_mwindow_get_pack(&p, idx_path, 0);
+ if (error < 0)
+ goto cleanup;
+
+ error = git_pack_foreach_entry(p, object_entry__cb, &state);
+ if (error < 0)
+ goto cleanup;
+
+cleanup:
+ if (p)
+ git_mwindow_put_pack(p);
+ git_odb_free(state.db);
+ return error;
+}
+
+int git_commit_graph_writer_add_revwalk(git_commit_graph_writer *w, git_revwalk *walk)
+{
+ int error;
+ git_oid id;
+ git_repository *repo = git_revwalk_repository(walk);
+ git_commit *commit;
+ struct packed_commit *packed_commit;
+
+ while ((git_revwalk_next(&id, walk)) == 0) {
+ error = git_commit_lookup(&commit, repo, &id);
+ if (error < 0)
+ return error;
+
+ packed_commit = packed_commit_new(commit);
+ git_commit_free(commit);
+ GIT_ERROR_CHECK_ALLOC(packed_commit);
+
+ error = git_vector_insert(&w->commits, packed_commit);
+ if (error < 0) {
+ packed_commit_free(packed_commit);
+ return error;
+ }
+ }
+
+ return 0;
+}
+
+enum generation_number_commit_state {
+ GENERATION_NUMBER_COMMIT_STATE_UNVISITED = 0,
+ GENERATION_NUMBER_COMMIT_STATE_ADDED = 1,
+ GENERATION_NUMBER_COMMIT_STATE_EXPANDED = 2,
+ GENERATION_NUMBER_COMMIT_STATE_VISITED = 3
+};
+
+static int compute_generation_numbers(git_vector *commits)
+{
+ git_array_t(size_t) index_stack = GIT_ARRAY_INIT;
+ size_t i, j;
+ size_t *parent_idx;
+ enum generation_number_commit_state *commit_states = NULL;
+ struct packed_commit *child_packed_commit;
+ git_oidmap *packed_commit_map = NULL;
+ int error = 0;
+
+ /* First populate the parent indices fields */
+ error = git_oidmap_new(&packed_commit_map);
+ if (error < 0)
+ goto cleanup;
+ git_vector_foreach (commits, i, child_packed_commit) {
+ child_packed_commit->index = i;
+ error = git_oidmap_set(
+ packed_commit_map, &child_packed_commit->sha1, child_packed_commit);
+ if (error < 0)
+ goto cleanup;
+ }
+
+ git_vector_foreach (commits, i, child_packed_commit) {
+ size_t parent_i, *parent_idx_ptr;
+ struct packed_commit *parent_packed_commit;
+ git_oid *parent_id;
+ git_array_init_to_size(
+ child_packed_commit->parent_indices,
+ git_array_size(child_packed_commit->parents));
+ if (git_array_size(child_packed_commit->parents)
+ && !child_packed_commit->parent_indices.ptr) {
+ error = -1;
+ goto cleanup;
+ }
+ git_array_foreach (child_packed_commit->parents, parent_i, parent_id) {
+ parent_packed_commit = git_oidmap_get(packed_commit_map, parent_id);
+ if (!parent_packed_commit) {
+ git_error_set(GIT_ERROR_ODB,
+ "parent commit %s not found in commit graph",
+ git_oid_tostr_s(parent_id));
+ error = GIT_ENOTFOUND;
+ goto cleanup;
+ }
+ parent_idx_ptr = git_array_alloc(child_packed_commit->parent_indices);
+ if (!parent_idx_ptr) {
+ error = -1;
+ goto cleanup;
+ }
+ *parent_idx_ptr = parent_packed_commit->index;
+ }
+ }
+
+ /*
+ * We copy all the commits to the stack and then during visitation,
+ * each node can be added up to two times to the stack.
+ */
+ git_array_init_to_size(index_stack, 3 * git_vector_length(commits));
+ if (!index_stack.ptr) {
+ error = -1;
+ goto cleanup;
+ }
+
+ commit_states = (enum generation_number_commit_state *)git__calloc(
+ git_vector_length(commits), sizeof(enum generation_number_commit_state));
+ if (!commit_states) {
+ error = -1;
+ goto cleanup;
+ }
+
+ /*
+ * Perform a Post-Order traversal so that all parent nodes are fully
+ * visited before the child node.
+ */
+ git_vector_foreach (commits, i, child_packed_commit)
+ *(size_t *)git_array_alloc(index_stack) = i;
+
+ while (git_array_size(index_stack)) {
+ size_t *index_ptr = git_array_pop(index_stack);
+ i = *index_ptr;
+ child_packed_commit = git_vector_get(commits, i);
+
+ if (commit_states[i] == GENERATION_NUMBER_COMMIT_STATE_VISITED) {
+ /* This commit has already been fully visited. */
+ continue;
+ }
+ if (commit_states[i] == GENERATION_NUMBER_COMMIT_STATE_EXPANDED) {
+ /* All of the commits parents have been visited. */
+ child_packed_commit->generation = 0;
+ git_array_foreach (child_packed_commit->parent_indices, j, parent_idx) {
+ struct packed_commit *parent = git_vector_get(commits, *parent_idx);
+ if (child_packed_commit->generation < parent->generation)
+ child_packed_commit->generation = parent->generation;
+ }
+ if (child_packed_commit->generation
+ < GIT_COMMIT_GRAPH_GENERATION_NUMBER_MAX) {
+ ++child_packed_commit->generation;
+ }
+ commit_states[i] = GENERATION_NUMBER_COMMIT_STATE_VISITED;
+ continue;
+ }
+
+ /*
+ * This is the first time we see this commit. We need
+ * to visit all its parents before we can fully visit
+ * it.
+ */
+ if (git_array_size(child_packed_commit->parent_indices) == 0) {
+ /*
+ * Special case: if the commit has no parents, there's
+ * no need to add it to the stack just to immediately
+ * remove it.
+ */
+ commit_states[i] = GENERATION_NUMBER_COMMIT_STATE_VISITED;
+ child_packed_commit->generation = 1;
+ continue;
+ }
+
+ /*
+ * Add this current commit again so that it is visited
+ * again once all its children have been visited.
+ */
+ *(size_t *)git_array_alloc(index_stack) = i;
+ git_array_foreach (child_packed_commit->parent_indices, j, parent_idx) {
+ if (commit_states[*parent_idx]
+ != GENERATION_NUMBER_COMMIT_STATE_UNVISITED) {
+ /* This commit has already been considered. */
+ continue;
+ }
+
+ commit_states[*parent_idx] = GENERATION_NUMBER_COMMIT_STATE_ADDED;
+ *(size_t *)git_array_alloc(index_stack) = *parent_idx;
+ }
+ commit_states[i] = GENERATION_NUMBER_COMMIT_STATE_EXPANDED;
+ }
+
+cleanup:
+ git_oidmap_free(packed_commit_map);
+ git__free(commit_states);
+ git_array_clear(index_stack);
+
+ return error;
+}
+
+static int write_offset(off64_t offset, commit_graph_write_cb write_cb, void *cb_data)
+{
+ int error;
+ uint32_t word;
+
+ word = htonl((uint32_t)((offset >> 32) & 0xffffffffu));
+ error = write_cb((const char *)&word, sizeof(word), cb_data);
+ if (error < 0)
+ return error;
+ word = htonl((uint32_t)((offset >> 0) & 0xffffffffu));
+ error = write_cb((const char *)&word, sizeof(word), cb_data);
+ if (error < 0)
+ return error;
+
+ return 0;
+}
+
+static int write_chunk_header(
+ int chunk_id,
+ off64_t offset,
+ commit_graph_write_cb write_cb,
+ void *cb_data)
+{
+ uint32_t word = htonl(chunk_id);
+ int error = write_cb((const char *)&word, sizeof(word), cb_data);
+ if (error < 0)
+ return error;
+ return write_offset(offset, write_cb, cb_data);
+}
+
+static int commit_graph_write_buf(const char *buf, size_t size, void *data)
+{
+ git_str *b = (git_str *)data;
+ return git_str_put(b, buf, size);
+}
+
+struct commit_graph_write_hash_context {
+ commit_graph_write_cb write_cb;
+ void *cb_data;
+ git_hash_ctx *ctx;
+};
+
+static int commit_graph_write_hash(const char *buf, size_t size, void *data)
+{
+ struct commit_graph_write_hash_context *ctx = data;
+ int error;
+
+ error = git_hash_update(ctx->ctx, buf, size);
+ if (error < 0)
+ return error;
+
+ return ctx->write_cb(buf, size, ctx->cb_data);
+}
+
+static void packed_commit_free_dup(void *packed_commit)
+{
+ packed_commit_free(packed_commit);
+}
+
+static int commit_graph_write(
+ git_commit_graph_writer *w,
+ commit_graph_write_cb write_cb,
+ void *cb_data)
+{
+ int error = 0;
+ size_t i;
+ struct packed_commit *packed_commit;
+ struct git_commit_graph_header hdr = {0};
+ uint32_t oid_fanout_count;
+ uint32_t extra_edge_list_count;
+ uint32_t oid_fanout[256];
+ off64_t offset;
+ git_str oid_lookup = GIT_STR_INIT, commit_data = GIT_STR_INIT,
+ extra_edge_list = GIT_STR_INIT;
+ unsigned char checksum[GIT_HASH_MAX_SIZE];
+ git_hash_algorithm_t checksum_type;
+ size_t checksum_size, oid_size;
+ git_hash_ctx ctx;
+ struct commit_graph_write_hash_context hash_cb_data = {0};
+
+ hdr.signature = htonl(COMMIT_GRAPH_SIGNATURE);
+ hdr.version = COMMIT_GRAPH_VERSION;
+ hdr.object_id_version = COMMIT_GRAPH_OBJECT_ID_VERSION;
+ hdr.chunks = 0;
+ hdr.base_graph_files = 0;
+ hash_cb_data.write_cb = write_cb;
+ hash_cb_data.cb_data = cb_data;
+ hash_cb_data.ctx = &ctx;
+
+ oid_size = git_oid_size(w->oid_type);
+ checksum_type = git_oid_algorithm(w->oid_type);
+ checksum_size = git_hash_size(checksum_type);
+
+ error = git_hash_ctx_init(&ctx, checksum_type);
+ if (error < 0)
+ return error;
+ cb_data = &hash_cb_data;
+ write_cb = commit_graph_write_hash;
+
+ /* Sort the commits. */
+ git_vector_sort(&w->commits);
+ git_vector_uniq(&w->commits, packed_commit_free_dup);
+ error = compute_generation_numbers(&w->commits);
+ if (error < 0)
+ goto cleanup;
+
+ /* Fill the OID Fanout table. */
+ oid_fanout_count = 0;
+ for (i = 0; i < 256; i++) {
+ while (oid_fanout_count < git_vector_length(&w->commits) &&
+ (packed_commit = (struct packed_commit *)git_vector_get(&w->commits, oid_fanout_count)) &&
+ packed_commit->sha1.id[0] <= i)
+ ++oid_fanout_count;
+ oid_fanout[i] = htonl(oid_fanout_count);
+ }
+
+ /* Fill the OID Lookup table. */
+ git_vector_foreach (&w->commits, i, packed_commit) {
+ error = git_str_put(&oid_lookup,
+ (const char *)&packed_commit->sha1.id,
+ oid_size);
+
+ if (error < 0)
+ goto cleanup;
+ }
+
+ /* Fill the Commit Data and Extra Edge List tables. */
+ extra_edge_list_count = 0;
+ git_vector_foreach (&w->commits, i, packed_commit) {
+ uint64_t commit_time;
+ uint32_t generation;
+ uint32_t word;
+ size_t *packed_index;
+ unsigned int parentcount = (unsigned int)git_array_size(packed_commit->parents);
+
+ error = git_str_put(&commit_data,
+ (const char *)&packed_commit->tree_oid.id,
+ oid_size);
+
+ if (error < 0)
+ goto cleanup;
+
+ if (parentcount == 0) {
+ word = htonl(GIT_COMMIT_GRAPH_MISSING_PARENT);
+ } else {
+ packed_index = git_array_get(packed_commit->parent_indices, 0);
+ word = htonl((uint32_t)*packed_index);
+ }
+ error = git_str_put(&commit_data, (const char *)&word, sizeof(word));
+ if (error < 0)
+ goto cleanup;
+
+ if (parentcount < 2) {
+ word = htonl(GIT_COMMIT_GRAPH_MISSING_PARENT);
+ } else if (parentcount == 2) {
+ packed_index = git_array_get(packed_commit->parent_indices, 1);
+ word = htonl((uint32_t)*packed_index);
+ } else {
+ word = htonl(0x80000000u | extra_edge_list_count);
+ }
+ error = git_str_put(&commit_data, (const char *)&word, sizeof(word));
+ if (error < 0)
+ goto cleanup;
+
+ if (parentcount > 2) {
+ unsigned int parent_i;
+ for (parent_i = 1; parent_i < parentcount; ++parent_i) {
+ packed_index = git_array_get(
+ packed_commit->parent_indices, parent_i);
+ word = htonl((uint32_t)(*packed_index | (parent_i + 1 == parentcount ? 0x80000000u : 0)));
+
+ error = git_str_put(&extra_edge_list,
+ (const char *)&word,
+ sizeof(word));
+ if (error < 0)
+ goto cleanup;
+ }
+ extra_edge_list_count += parentcount - 1;
+ }
+
+ generation = packed_commit->generation;
+ commit_time = (uint64_t)packed_commit->commit_time;
+ if (generation > GIT_COMMIT_GRAPH_GENERATION_NUMBER_MAX)
+ generation = GIT_COMMIT_GRAPH_GENERATION_NUMBER_MAX;
+ word = ntohl((uint32_t)((generation << 2) | (((uint32_t)(commit_time >> 32)) & 0x3) ));
+ error = git_str_put(&commit_data, (const char *)&word, sizeof(word));
+ if (error < 0)
+ goto cleanup;
+ word = ntohl((uint32_t)(commit_time & 0xfffffffful));
+ error = git_str_put(&commit_data, (const char *)&word, sizeof(word));
+ if (error < 0)
+ goto cleanup;
+ }
+
+ /* Write the header. */
+ hdr.chunks = 3;
+ if (git_str_len(&extra_edge_list) > 0)
+ hdr.chunks++;
+ error = write_cb((const char *)&hdr, sizeof(hdr), cb_data);
+ if (error < 0)
+ goto cleanup;
+
+ /* Write the chunk headers. */
+ offset = sizeof(hdr) + (hdr.chunks + 1) * 12;
+ error = write_chunk_header(COMMIT_GRAPH_OID_FANOUT_ID, offset, write_cb, cb_data);
+ if (error < 0)
+ goto cleanup;
+ offset += sizeof(oid_fanout);
+ error = write_chunk_header(COMMIT_GRAPH_OID_LOOKUP_ID, offset, write_cb, cb_data);
+ if (error < 0)
+ goto cleanup;
+ offset += git_str_len(&oid_lookup);
+ error = write_chunk_header(COMMIT_GRAPH_COMMIT_DATA_ID, offset, write_cb, cb_data);
+ if (error < 0)
+ goto cleanup;
+ offset += git_str_len(&commit_data);
+ if (git_str_len(&extra_edge_list) > 0) {
+ error = write_chunk_header(
+ COMMIT_GRAPH_EXTRA_EDGE_LIST_ID, offset, write_cb, cb_data);
+ if (error < 0)
+ goto cleanup;
+ offset += git_str_len(&extra_edge_list);
+ }
+ error = write_chunk_header(0, offset, write_cb, cb_data);
+ if (error < 0)
+ goto cleanup;
+
+ /* Write all the chunks. */
+ error = write_cb((const char *)oid_fanout, sizeof(oid_fanout), cb_data);
+ if (error < 0)
+ goto cleanup;
+ error = write_cb(git_str_cstr(&oid_lookup), git_str_len(&oid_lookup), cb_data);
+ if (error < 0)
+ goto cleanup;
+ error = write_cb(git_str_cstr(&commit_data), git_str_len(&commit_data), cb_data);
+ if (error < 0)
+ goto cleanup;
+ error = write_cb(git_str_cstr(&extra_edge_list), git_str_len(&extra_edge_list), cb_data);
+ if (error < 0)
+ goto cleanup;
+
+ /* Finalize the checksum and write the trailer. */
+ error = git_hash_final(checksum, &ctx);
+ if (error < 0)
+ goto cleanup;
+ error = write_cb((char *)checksum, checksum_size, cb_data);
+ if (error < 0)
+ goto cleanup;
+
+cleanup:
+ git_str_dispose(&oid_lookup);
+ git_str_dispose(&commit_data);
+ git_str_dispose(&extra_edge_list);
+ git_hash_ctx_cleanup(&ctx);
+ return error;
+}
+
+static int commit_graph_write_filebuf(const char *buf, size_t size, void *data)
+{
+ git_filebuf *f = (git_filebuf *)data;
+ return git_filebuf_write(f, buf, size);
+}
+
+int git_commit_graph_writer_options_init(
+ git_commit_graph_writer_options *opts,
+ unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts,
+ version,
+ git_commit_graph_writer_options,
+ GIT_COMMIT_GRAPH_WRITER_OPTIONS_INIT);
+ return 0;
+}
+
+int git_commit_graph_writer_commit(
+ git_commit_graph_writer *w,
+ git_commit_graph_writer_options *opts)
+{
+ int error;
+ int filebuf_flags = GIT_FILEBUF_DO_NOT_BUFFER;
+ git_str commit_graph_path = GIT_STR_INIT;
+ git_filebuf output = GIT_FILEBUF_INIT;
+
+ /* TODO: support options and fill in defaults. */
+ GIT_UNUSED(opts);
+
+ error = git_str_joinpath(
+ &commit_graph_path, git_str_cstr(&w->objects_info_dir), "commit-graph");
+ if (error < 0)
+ return error;
+
+ if (git_repository__fsync_gitdir)
+ filebuf_flags |= GIT_FILEBUF_FSYNC;
+ error = git_filebuf_open(&output, git_str_cstr(&commit_graph_path), filebuf_flags, 0644);
+ git_str_dispose(&commit_graph_path);
+ if (error < 0)
+ return error;
+
+ error = commit_graph_write(w, commit_graph_write_filebuf, &output);
+ if (error < 0) {
+ git_filebuf_cleanup(&output);
+ return error;
+ }
+
+ return git_filebuf_commit(&output);
+}
+
+int git_commit_graph_writer_dump(
+ git_buf *cgraph,
+ git_commit_graph_writer *w,
+ git_commit_graph_writer_options *opts)
+{
+ GIT_BUF_WRAP_PRIVATE(cgraph, git_commit_graph__writer_dump, w, opts);
+}
+
+int git_commit_graph__writer_dump(
+ git_str *cgraph,
+ git_commit_graph_writer *w,
+ git_commit_graph_writer_options *opts)
+{
+ /* TODO: support options. */
+ GIT_UNUSED(opts);
+ return commit_graph_write(w, commit_graph_write_buf, cgraph);
+}
diff --git a/src/libgit2/commit_graph.h b/src/libgit2/commit_graph.h
new file mode 100644
index 0000000..ecf4379
--- /dev/null
+++ b/src/libgit2/commit_graph.h
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_commit_graph_h__
+#define INCLUDE_commit_graph_h__
+
+#include "common.h"
+
+#include "git2/types.h"
+#include "git2/sys/commit_graph.h"
+
+#include "map.h"
+#include "vector.h"
+#include "oid.h"
+#include "hash.h"
+
+/**
+ * A commit-graph file.
+ *
+ * This file contains metadata about commits, particularly the generation
+ * number for each one. This can help speed up graph operations without
+ * requiring a full graph traversal.
+ *
+ * Support for this feature was added in git 2.19.
+ */
+typedef struct git_commit_graph_file {
+ git_map graph_map;
+
+ /* The type of object IDs in the commit graph file. */
+ git_oid_t oid_type;
+
+ /* The OID Fanout table. */
+ const uint32_t *oid_fanout;
+ /* The total number of commits in the graph. */
+ uint32_t num_commits;
+
+ /* The OID Lookup table. */
+ unsigned char *oid_lookup;
+
+ /*
+ * The Commit Data table. Each entry contains the OID of the commit followed
+ * by two 8-byte fields in network byte order:
+ * - The indices of the first two parents (32 bits each).
+ * - The generation number (first 30 bits) and commit time in seconds since
+ * UNIX epoch (34 bits).
+ */
+ const unsigned char *commit_data;
+
+ /*
+ * The Extra Edge List table. Each 4-byte entry is a network byte order index
+ * of one of the i-th (i > 0) parents of commits in the `commit_data` table,
+ * when the commit has more than 2 parents.
+ */
+ const unsigned char *extra_edge_list;
+ /* The number of entries in the Extra Edge List table. Each entry is 4 bytes wide. */
+ size_t num_extra_edge_list;
+
+ /* The trailer of the file. Contains the SHA1-checksum of the whole file. */
+ unsigned char checksum[GIT_HASH_SHA1_SIZE];
+} git_commit_graph_file;
+
+/**
+ * An entry in the commit-graph file. Provides a subset of the information that
+ * can be obtained from the commit header.
+ */
+typedef struct git_commit_graph_entry {
+ /* The generation number of the commit within the graph */
+ size_t generation;
+
+ /* Time in seconds from UNIX epoch. */
+ git_time_t commit_time;
+
+ /* The number of parents of the commit. */
+ size_t parent_count;
+
+ /*
+ * The indices of the parent commits within the Commit Data table. The value
+ * of `GIT_COMMIT_GRAPH_MISSING_PARENT` indicates that no parent is in that
+ * position.
+ */
+ size_t parent_indices[2];
+
+ /* The index within the Extra Edge List of any parent after the first two. */
+ size_t extra_parents_index;
+
+ /* The object ID of the root tree of the commit. */
+ git_oid tree_oid;
+
+ /* The object ID hash of the requested commit. */
+ git_oid sha1;
+} git_commit_graph_entry;
+
+/* A wrapper for git_commit_graph_file to enable lazy loading in the ODB. */
+struct git_commit_graph {
+ /* The path to the commit-graph file. Something like ".git/objects/info/commit-graph". */
+ git_str filename;
+
+ /* The underlying commit-graph file. */
+ git_commit_graph_file *file;
+
+ /* The object ID types in the commit graph. */
+ git_oid_t oid_type;
+
+ /* Whether the commit-graph file was already checked for validity. */
+ bool checked;
+};
+
+/** Create a new commit-graph, optionally opening the underlying file. */
+int git_commit_graph_new(
+ git_commit_graph **cgraph_out,
+ const char *objects_dir,
+ bool open_file,
+ git_oid_t oid_type);
+
+/** Validate the checksum of a commit graph */
+int git_commit_graph_validate(git_commit_graph *cgraph);
+
+/** Open and validate a commit-graph file. */
+int git_commit_graph_file_open(
+ git_commit_graph_file **file_out,
+ const char *path,
+ git_oid_t oid_type);
+
+/*
+ * Attempt to get the git_commit_graph's commit-graph file. This object is
+ * still owned by the git_commit_graph. If the repository does not contain a commit graph,
+ * it will return GIT_ENOTFOUND.
+ *
+ * This function is not thread-safe.
+ */
+int git_commit_graph_get_file(git_commit_graph_file **file_out, git_commit_graph *cgraph);
+
+/* Marks the commit-graph file as needing a refresh. */
+void git_commit_graph_refresh(git_commit_graph *cgraph);
+
+/*
+ * A writer for `commit-graph` files.
+ */
+struct git_commit_graph_writer {
+ /*
+ * The path of the `objects/info` directory where the `commit-graph` will be
+ * stored.
+ */
+ git_str objects_info_dir;
+
+ /* The object ID type of the commit graph. */
+ git_oid_t oid_type;
+
+ /* The list of packed commits. */
+ git_vector commits;
+};
+
+int git_commit_graph__writer_dump(
+ git_str *cgraph,
+ git_commit_graph_writer *w,
+ git_commit_graph_writer_options *opts);
+
+/*
+ * Returns whether the git_commit_graph_file needs to be reloaded since the
+ * contents of the commit-graph file have changed on disk.
+ */
+bool git_commit_graph_file_needs_refresh(
+ const git_commit_graph_file *file, const char *path);
+
+int git_commit_graph_entry_find(
+ git_commit_graph_entry *e,
+ const git_commit_graph_file *file,
+ const git_oid *short_oid,
+ size_t len);
+int git_commit_graph_entry_parent(
+ git_commit_graph_entry *parent,
+ const git_commit_graph_file *file,
+ const git_commit_graph_entry *entry,
+ size_t n);
+int git_commit_graph_file_close(git_commit_graph_file *cgraph);
+void git_commit_graph_file_free(git_commit_graph_file *cgraph);
+
+/* This is exposed for use in the fuzzers. */
+int git_commit_graph_file_parse(
+ git_commit_graph_file *file,
+ const unsigned char *data,
+ size_t size);
+
+#endif
diff --git a/src/libgit2/commit_list.c b/src/libgit2/commit_list.c
new file mode 100644
index 0000000..7f00c48
--- /dev/null
+++ b/src/libgit2/commit_list.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "commit_list.h"
+
+#include "revwalk.h"
+#include "pool.h"
+#include "odb.h"
+#include "commit.h"
+
+int git_commit_list_generation_cmp(const void *a, const void *b)
+{
+ uint32_t generation_a = ((git_commit_list_node *) a)->generation;
+ uint32_t generation_b = ((git_commit_list_node *) b)->generation;
+
+ if (!generation_a || !generation_b) {
+ /* Fall back to comparing by timestamps if at least one commit lacks a generation. */
+ return git_commit_list_time_cmp(a, b);
+ }
+
+ if (generation_a < generation_b)
+ return 1;
+ if (generation_a > generation_b)
+ return -1;
+
+ return 0;
+}
+
+int git_commit_list_time_cmp(const void *a, const void *b)
+{
+ int64_t time_a = ((git_commit_list_node *) a)->time;
+ int64_t time_b = ((git_commit_list_node *) b)->time;
+
+ if (time_a < time_b)
+ return 1;
+ if (time_a > time_b)
+ return -1;
+
+ return 0;
+}
+
+git_commit_list *git_commit_list_create(git_commit_list_node *item, git_commit_list *next) {
+ git_commit_list *new_list = git__malloc(sizeof(git_commit_list));
+ if (new_list != NULL) {
+ new_list->item = item;
+ new_list->next = next;
+ }
+ return new_list;
+}
+
+git_commit_list *git_commit_list_insert(git_commit_list_node *item, git_commit_list **list_p)
+{
+ git_commit_list *new_list = git_commit_list_create(item, *list_p);
+ *list_p = new_list;
+ return new_list;
+}
+
+git_commit_list *git_commit_list_insert_by_date(git_commit_list_node *item, git_commit_list **list_p)
+{
+ git_commit_list **pp = list_p;
+ git_commit_list *p;
+
+ while ((p = *pp) != NULL) {
+ if (git_commit_list_time_cmp(p->item, item) > 0)
+ break;
+
+ pp = &p->next;
+ }
+
+ return git_commit_list_insert(item, pp);
+}
+
+git_commit_list_node *git_commit_list_alloc_node(git_revwalk *walk)
+{
+ return (git_commit_list_node *)git_pool_mallocz(&walk->commit_pool, 1);
+}
+
+static git_commit_list_node **alloc_parents(
+ git_revwalk *walk, git_commit_list_node *commit, size_t n_parents)
+{
+ size_t bytes;
+
+ if (n_parents <= PARENTS_PER_COMMIT)
+ return (git_commit_list_node **)((char *)commit + sizeof(git_commit_list_node));
+
+ if (git__multiply_sizet_overflow(&bytes, n_parents, sizeof(git_commit_list_node *)))
+ return NULL;
+
+ return (git_commit_list_node **)git_pool_malloc(&walk->commit_pool, bytes);
+}
+
+
+void git_commit_list_free(git_commit_list **list_p)
+{
+ git_commit_list *list = *list_p;
+
+ if (list == NULL)
+ return;
+
+ while (list) {
+ git_commit_list *temp = list;
+ list = temp->next;
+ git__free(temp);
+ }
+
+ *list_p = NULL;
+}
+
+git_commit_list_node *git_commit_list_pop(git_commit_list **stack)
+{
+ git_commit_list *top = *stack;
+ git_commit_list_node *item = top ? top->item : NULL;
+
+ if (top) {
+ *stack = top->next;
+ git__free(top);
+ }
+ return item;
+}
+
+static int commit_quick_parse(
+ git_revwalk *walk,
+ git_commit_list_node *node,
+ git_odb_object *obj)
+{
+ git_oid *parent_oid;
+ git_commit *commit;
+ git_commit__parse_options parse_opts = {
+ walk->repo->oid_type,
+ GIT_COMMIT_PARSE_QUICK
+ };
+ size_t i;
+
+ commit = git__calloc(1, sizeof(*commit));
+ GIT_ERROR_CHECK_ALLOC(commit);
+ commit->object.repo = walk->repo;
+
+ if (git_commit__parse_ext(commit, obj, &parse_opts) < 0) {
+ git__free(commit);
+ return -1;
+ }
+
+ if (!git__is_uint16(git_array_size(commit->parent_ids))) {
+ git__free(commit);
+ git_error_set(GIT_ERROR_INVALID, "commit has more than 2^16 parents");
+ return -1;
+ }
+
+ node->generation = 0;
+ node->time = commit->committer->when.time;
+ node->out_degree = (uint16_t) git_array_size(commit->parent_ids);
+ node->parents = alloc_parents(walk, node, node->out_degree);
+ GIT_ERROR_CHECK_ALLOC(node->parents);
+
+ git_array_foreach(commit->parent_ids, i, parent_oid) {
+ node->parents[i] = git_revwalk__commit_lookup(walk, parent_oid);
+ }
+
+ git_commit__free(commit);
+
+ node->parsed = 1;
+
+ return 0;
+}
+
+int git_commit_list_parse(git_revwalk *walk, git_commit_list_node *commit)
+{
+ git_odb_object *obj;
+ git_commit_graph_file *cgraph_file = NULL;
+ int error;
+
+ if (commit->parsed)
+ return 0;
+
+ /* Let's try to use the commit graph first. */
+ git_odb__get_commit_graph_file(&cgraph_file, walk->odb);
+ if (cgraph_file) {
+ git_commit_graph_entry e;
+
+ error = git_commit_graph_entry_find(&e, cgraph_file,
+ &commit->oid, git_oid_size(walk->repo->oid_type));
+
+ if (error == 0 && git__is_uint16(e.parent_count)) {
+ size_t i;
+ commit->generation = (uint32_t)e.generation;
+ commit->time = e.commit_time;
+ commit->out_degree = (uint16_t)e.parent_count;
+ commit->parents = alloc_parents(walk, commit, commit->out_degree);
+ GIT_ERROR_CHECK_ALLOC(commit->parents);
+
+ for (i = 0; i < commit->out_degree; ++i) {
+ git_commit_graph_entry parent;
+ error = git_commit_graph_entry_parent(&parent, cgraph_file, &e, i);
+ if (error < 0)
+ return error;
+ commit->parents[i] = git_revwalk__commit_lookup(walk, &parent.sha1);
+ }
+ commit->parsed = 1;
+ return 0;
+ }
+ }
+
+ if ((error = git_odb_read(&obj, walk->odb, &commit->oid)) < 0)
+ return error;
+
+ if (obj->cached.type != GIT_OBJECT_COMMIT) {
+ git_error_set(GIT_ERROR_INVALID, "object is no commit object");
+ error = -1;
+ } else
+ error = commit_quick_parse(walk, commit, obj);
+
+ git_odb_object_free(obj);
+ return error;
+}
+
diff --git a/src/libgit2/commit_list.h b/src/libgit2/commit_list.h
new file mode 100644
index 0000000..e2dbd2a
--- /dev/null
+++ b/src/libgit2/commit_list.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_commit_list_h__
+#define INCLUDE_commit_list_h__
+
+#include "common.h"
+
+#include "git2/oid.h"
+
+#define PARENT1 (1 << 0)
+#define PARENT2 (1 << 1)
+#define RESULT (1 << 2)
+#define STALE (1 << 3)
+#define ALL_FLAGS (PARENT1 | PARENT2 | STALE | RESULT)
+
+#define PARENTS_PER_COMMIT 2
+#define COMMIT_ALLOC \
+ (sizeof(git_commit_list_node) + PARENTS_PER_COMMIT * sizeof(git_commit_list_node *))
+
+#define FLAG_BITS 4
+
+typedef struct git_commit_list_node {
+ git_oid oid;
+ int64_t time;
+ uint32_t generation;
+ unsigned int seen:1,
+ uninteresting:1,
+ topo_delay:1,
+ parsed:1,
+ added:1,
+ flags : FLAG_BITS;
+
+ uint16_t in_degree;
+ uint16_t out_degree;
+
+ struct git_commit_list_node **parents;
+} git_commit_list_node;
+
+typedef struct git_commit_list {
+ git_commit_list_node *item;
+ struct git_commit_list *next;
+} git_commit_list;
+
+git_commit_list_node *git_commit_list_alloc_node(git_revwalk *walk);
+int git_commit_list_generation_cmp(const void *a, const void *b);
+int git_commit_list_time_cmp(const void *a, const void *b);
+void git_commit_list_free(git_commit_list **list_p);
+git_commit_list *git_commit_list_create(git_commit_list_node *item, git_commit_list *next);
+git_commit_list *git_commit_list_insert(git_commit_list_node *item, git_commit_list **list_p);
+git_commit_list *git_commit_list_insert_by_date(git_commit_list_node *item, git_commit_list **list_p);
+int git_commit_list_parse(git_revwalk *walk, git_commit_list_node *commit);
+git_commit_list_node *git_commit_list_pop(git_commit_list **stack);
+
+#endif
diff --git a/src/libgit2/common.h b/src/libgit2/common.h
new file mode 100644
index 0000000..bb9ec5a
--- /dev/null
+++ b/src/libgit2/common.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_common_h__
+#define INCLUDE_common_h__
+
+#include "git2_util.h"
+#include "errors.h"
+
+/*
+* Include the declarations for deprecated functions; this ensures
+* that they're decorated with the proper extern/visibility attributes.
+*/
+#include "git2/deprecated.h"
+
+#include "posix.h"
+
+/**
+ * Initialize a structure with a version.
+ */
+GIT_INLINE(void) git__init_structure(void *structure, size_t len, unsigned int version)
+{
+ memset(structure, 0, len);
+ *((int*)structure) = version;
+}
+#define GIT_INIT_STRUCTURE(S,V) git__init_structure(S, sizeof(*S), V)
+
+#define GIT_INIT_STRUCTURE_FROM_TEMPLATE(PTR,VERSION,TYPE,TPL) do { \
+ TYPE _tmpl = TPL; \
+ GIT_ERROR_CHECK_VERSION(&(VERSION), _tmpl.version, #TYPE); \
+ memcpy((PTR), &_tmpl, sizeof(_tmpl)); } while (0)
+
+/**
+ * Check a versioned structure for validity
+ */
+GIT_INLINE(int) git_error__check_version(const void *structure, unsigned int expected_max, const char *name)
+{
+ unsigned int actual;
+
+ if (!structure)
+ return 0;
+
+ actual = *(const unsigned int*)structure;
+ if (actual > 0 && actual <= expected_max)
+ return 0;
+
+ git_error_set(GIT_ERROR_INVALID, "invalid version %d on %s", actual, name);
+ return -1;
+}
+#define GIT_ERROR_CHECK_VERSION(S,V,N) if (git_error__check_version(S,V,N) < 0) return -1
+
+#endif
diff --git a/src/libgit2/config.c b/src/libgit2/config.c
new file mode 100644
index 0000000..23a8f9f
--- /dev/null
+++ b/src/libgit2/config.c
@@ -0,0 +1,1576 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "config.h"
+
+#include "git2/config.h"
+#include "git2/sys/config.h"
+
+#include "buf.h"
+#include "config_backend.h"
+#include "regexp.h"
+#include "sysdir.h"
+#include "transaction.h"
+#include "vector.h"
+#if GIT_WIN32
+# include <windows.h>
+#endif
+
+#include <ctype.h>
+
+void git_config_entry_free(git_config_entry *entry)
+{
+ if (!entry)
+ return;
+
+ entry->free(entry);
+}
+
+typedef struct {
+ git_refcount rc;
+
+ git_config_backend *backend;
+ git_config_level_t level;
+} backend_internal;
+
+static void backend_internal_free(backend_internal *internal)
+{
+ git_config_backend *backend;
+
+ backend = internal->backend;
+ backend->free(backend);
+ git__free(internal);
+}
+
+static void config_free(git_config *cfg)
+{
+ size_t i;
+ backend_internal *internal;
+
+ for (i = 0; i < cfg->backends.length; ++i) {
+ internal = git_vector_get(&cfg->backends, i);
+ GIT_REFCOUNT_DEC(internal, backend_internal_free);
+ }
+
+ git_vector_free(&cfg->backends);
+
+ git__memzero(cfg, sizeof(*cfg));
+ git__free(cfg);
+}
+
+void git_config_free(git_config *cfg)
+{
+ if (cfg == NULL)
+ return;
+
+ GIT_REFCOUNT_DEC(cfg, config_free);
+}
+
+static int config_backend_cmp(const void *a, const void *b)
+{
+ const backend_internal *bk_a = (const backend_internal *)(a);
+ const backend_internal *bk_b = (const backend_internal *)(b);
+
+ return bk_b->level - bk_a->level;
+}
+
+int git_config_new(git_config **out)
+{
+ git_config *cfg;
+
+ cfg = git__malloc(sizeof(git_config));
+ GIT_ERROR_CHECK_ALLOC(cfg);
+
+ memset(cfg, 0x0, sizeof(git_config));
+
+ if (git_vector_init(&cfg->backends, 3, config_backend_cmp) < 0) {
+ git__free(cfg);
+ return -1;
+ }
+
+ *out = cfg;
+ GIT_REFCOUNT_INC(cfg);
+ return 0;
+}
+
+int git_config_add_file_ondisk(
+ git_config *cfg,
+ const char *path,
+ git_config_level_t level,
+ const git_repository *repo,
+ int force)
+{
+ git_config_backend *file = NULL;
+ struct stat st;
+ int res;
+
+ GIT_ASSERT_ARG(cfg);
+ GIT_ASSERT_ARG(path);
+
+ res = p_stat(path, &st);
+ if (res < 0 && errno != ENOENT && errno != ENOTDIR) {
+ git_error_set(GIT_ERROR_CONFIG, "failed to stat '%s'", path);
+ return -1;
+ }
+
+ if (git_config_backend_from_file(&file, path) < 0)
+ return -1;
+
+ if ((res = git_config_add_backend(cfg, file, level, repo, force)) < 0) {
+ /*
+ * free manually; the file is not owned by the config
+ * instance yet and will not be freed on cleanup
+ */
+ file->free(file);
+ return res;
+ }
+
+ return 0;
+}
+
+int git_config_open_ondisk(git_config **out, const char *path)
+{
+ int error;
+ git_config *config;
+
+ *out = NULL;
+
+ if (git_config_new(&config) < 0)
+ return -1;
+
+ if ((error = git_config_add_file_ondisk(config, path, GIT_CONFIG_LEVEL_LOCAL, NULL, 0)) < 0)
+ git_config_free(config);
+ else
+ *out = config;
+
+ return error;
+}
+
+int git_config_snapshot(git_config **out, git_config *in)
+{
+ int error = 0;
+ size_t i;
+ backend_internal *internal;
+ git_config *config;
+
+ *out = NULL;
+
+ if (git_config_new(&config) < 0)
+ return -1;
+
+ git_vector_foreach(&in->backends, i, internal) {
+ git_config_backend *b;
+
+ if ((error = internal->backend->snapshot(&b, internal->backend)) < 0)
+ break;
+
+ if ((error = git_config_add_backend(config, b, internal->level, NULL, 0)) < 0) {
+ b->free(b);
+ break;
+ }
+ }
+
+ if (error < 0)
+ git_config_free(config);
+ else
+ *out = config;
+
+ return error;
+}
+
+static int find_backend_by_level(
+ backend_internal **out,
+ const git_config *cfg,
+ git_config_level_t level)
+{
+ int pos = -1;
+ backend_internal *internal;
+ size_t i;
+
+ /* when passing GIT_CONFIG_HIGHEST_LEVEL, the idea is to get the config backend
+ * which has the highest level. As config backends are stored in a vector
+ * sorted by decreasing order of level, getting the backend at position 0
+ * will do the job.
+ */
+ if (level == GIT_CONFIG_HIGHEST_LEVEL) {
+ pos = 0;
+ } else {
+ git_vector_foreach(&cfg->backends, i, internal) {
+ if (internal->level == level)
+ pos = (int)i;
+ }
+ }
+
+ if (pos == -1) {
+ git_error_set(GIT_ERROR_CONFIG,
+ "no configuration exists for the given level '%i'", (int)level);
+ return GIT_ENOTFOUND;
+ }
+
+ *out = git_vector_get(&cfg->backends, pos);
+
+ return 0;
+}
+
+static int duplicate_level(void **old_raw, void *new_raw)
+{
+ backend_internal **old = (backend_internal **)old_raw;
+
+ GIT_UNUSED(new_raw);
+
+ git_error_set(GIT_ERROR_CONFIG, "there already exists a configuration for the given level (%i)", (int)(*old)->level);
+ return GIT_EEXISTS;
+}
+
+static void try_remove_existing_backend(
+ git_config *cfg,
+ git_config_level_t level)
+{
+ int pos = -1;
+ backend_internal *internal;
+ size_t i;
+
+ git_vector_foreach(&cfg->backends, i, internal) {
+ if (internal->level == level)
+ pos = (int)i;
+ }
+
+ if (pos == -1)
+ return;
+
+ internal = git_vector_get(&cfg->backends, pos);
+
+ if (git_vector_remove(&cfg->backends, pos) < 0)
+ return;
+
+ GIT_REFCOUNT_DEC(internal, backend_internal_free);
+}
+
+static int git_config__add_internal(
+ git_config *cfg,
+ backend_internal *internal,
+ git_config_level_t level,
+ int force)
+{
+ int result;
+
+ /* delete existing config backend for level if it exists */
+ if (force)
+ try_remove_existing_backend(cfg, level);
+
+ if ((result = git_vector_insert_sorted(&cfg->backends,
+ internal, &duplicate_level)) < 0)
+ return result;
+
+ git_vector_sort(&cfg->backends);
+ internal->backend->cfg = cfg;
+
+ GIT_REFCOUNT_INC(internal);
+
+ return 0;
+}
+
+int git_config_open_global(git_config **cfg_out, git_config *cfg)
+{
+ if (!git_config_open_level(cfg_out, cfg, GIT_CONFIG_LEVEL_XDG))
+ return 0;
+
+ return git_config_open_level(cfg_out, cfg, GIT_CONFIG_LEVEL_GLOBAL);
+}
+
+int git_config_open_level(
+ git_config **cfg_out,
+ const git_config *cfg_parent,
+ git_config_level_t level)
+{
+ git_config *cfg;
+ backend_internal *internal;
+ int res;
+
+ if ((res = find_backend_by_level(&internal, cfg_parent, level)) < 0)
+ return res;
+
+ if ((res = git_config_new(&cfg)) < 0)
+ return res;
+
+ if ((res = git_config__add_internal(cfg, internal, level, true)) < 0) {
+ git_config_free(cfg);
+ return res;
+ }
+
+ *cfg_out = cfg;
+
+ return 0;
+}
+
+int git_config_add_backend(
+ git_config *cfg,
+ git_config_backend *backend,
+ git_config_level_t level,
+ const git_repository *repo,
+ int force)
+{
+ backend_internal *internal;
+ int result;
+
+ GIT_ASSERT_ARG(cfg);
+ GIT_ASSERT_ARG(backend);
+
+ GIT_ERROR_CHECK_VERSION(backend, GIT_CONFIG_BACKEND_VERSION, "git_config_backend");
+
+ if ((result = backend->open(backend, level, repo)) < 0)
+ return result;
+
+ internal = git__malloc(sizeof(backend_internal));
+ GIT_ERROR_CHECK_ALLOC(internal);
+
+ memset(internal, 0x0, sizeof(backend_internal));
+
+ internal->backend = backend;
+ internal->level = level;
+
+ if ((result = git_config__add_internal(cfg, internal, level, force)) < 0) {
+ git__free(internal);
+ return result;
+ }
+
+ return 0;
+}
+
+/*
+ * Loop over all the variables
+ */
+
+typedef struct {
+ git_config_iterator parent;
+ git_config_iterator *current;
+ const git_config *cfg;
+ git_regexp regex;
+ size_t i;
+} all_iter;
+
+static int find_next_backend(size_t *out, const git_config *cfg, size_t i)
+{
+ backend_internal *internal;
+
+ for (; i > 0; --i) {
+ internal = git_vector_get(&cfg->backends, i - 1);
+ if (!internal || !internal->backend)
+ continue;
+
+ *out = i;
+ return 0;
+ }
+
+ return -1;
+}
+
+static int all_iter_next(git_config_entry **entry, git_config_iterator *_iter)
+{
+ all_iter *iter = (all_iter *) _iter;
+ backend_internal *internal;
+ git_config_backend *backend;
+ size_t i;
+ int error = 0;
+
+ if (iter->current != NULL &&
+ (error = iter->current->next(entry, iter->current)) == 0) {
+ return 0;
+ }
+
+ if (error < 0 && error != GIT_ITEROVER)
+ return error;
+
+ do {
+ if (find_next_backend(&i, iter->cfg, iter->i) < 0)
+ return GIT_ITEROVER;
+
+ internal = git_vector_get(&iter->cfg->backends, i - 1);
+ backend = internal->backend;
+ iter->i = i - 1;
+
+ if (iter->current)
+ iter->current->free(iter->current);
+
+ iter->current = NULL;
+ error = backend->iterator(&iter->current, backend);
+ if (error == GIT_ENOTFOUND)
+ continue;
+
+ if (error < 0)
+ return error;
+
+ error = iter->current->next(entry, iter->current);
+ /* If this backend is empty, then keep going */
+ if (error == GIT_ITEROVER)
+ continue;
+
+ return error;
+
+ } while(1);
+
+ return GIT_ITEROVER;
+}
+
+static int all_iter_glob_next(git_config_entry **entry, git_config_iterator *_iter)
+{
+ int error;
+ all_iter *iter = (all_iter *) _iter;
+
+ /*
+ * We use the "normal" function to grab the next one across
+ * backends and then apply the regex
+ */
+ while ((error = all_iter_next(entry, _iter)) == 0) {
+ /* skip non-matching keys if regexp was provided */
+ if (git_regexp_match(&iter->regex, (*entry)->name) != 0)
+ continue;
+
+ /* and simply return if we like the entry's name */
+ return 0;
+ }
+
+ return error;
+}
+
+static void all_iter_free(git_config_iterator *_iter)
+{
+ all_iter *iter = (all_iter *) _iter;
+
+ if (iter->current)
+ iter->current->free(iter->current);
+
+ git__free(iter);
+}
+
+static void all_iter_glob_free(git_config_iterator *_iter)
+{
+ all_iter *iter = (all_iter *) _iter;
+
+ git_regexp_dispose(&iter->regex);
+ all_iter_free(_iter);
+}
+
+int git_config_iterator_new(git_config_iterator **out, const git_config *cfg)
+{
+ all_iter *iter;
+
+ iter = git__calloc(1, sizeof(all_iter));
+ GIT_ERROR_CHECK_ALLOC(iter);
+
+ iter->parent.free = all_iter_free;
+ iter->parent.next = all_iter_next;
+
+ iter->i = cfg->backends.length;
+ iter->cfg = cfg;
+
+ *out = (git_config_iterator *) iter;
+
+ return 0;
+}
+
+int git_config_iterator_glob_new(git_config_iterator **out, const git_config *cfg, const char *regexp)
+{
+ all_iter *iter;
+ int result;
+
+ if (regexp == NULL)
+ return git_config_iterator_new(out, cfg);
+
+ iter = git__calloc(1, sizeof(all_iter));
+ GIT_ERROR_CHECK_ALLOC(iter);
+
+ if ((result = git_regexp_compile(&iter->regex, regexp, 0)) < 0) {
+ git__free(iter);
+ return -1;
+ }
+
+ iter->parent.next = all_iter_glob_next;
+ iter->parent.free = all_iter_glob_free;
+ iter->i = cfg->backends.length;
+ iter->cfg = cfg;
+
+ *out = (git_config_iterator *) iter;
+
+ return 0;
+}
+
+int git_config_foreach(
+ const git_config *cfg, git_config_foreach_cb cb, void *payload)
+{
+ return git_config_foreach_match(cfg, NULL, cb, payload);
+}
+
+int git_config_backend_foreach_match(
+ git_config_backend *backend,
+ const char *regexp,
+ git_config_foreach_cb cb,
+ void *payload)
+{
+ git_config_entry *entry;
+ git_config_iterator *iter;
+ git_regexp regex;
+ int error = 0;
+
+ GIT_ASSERT_ARG(backend);
+ GIT_ASSERT_ARG(cb);
+
+ if (regexp && git_regexp_compile(&regex, regexp, 0) < 0)
+ return -1;
+
+ if ((error = backend->iterator(&iter, backend)) < 0) {
+ iter = NULL;
+ return -1;
+ }
+
+ while (!(iter->next(&entry, iter) < 0)) {
+ /* skip non-matching keys if regexp was provided */
+ if (regexp && git_regexp_match(&regex, entry->name) != 0)
+ continue;
+
+ /* abort iterator on non-zero return value */
+ if ((error = cb(entry, payload)) != 0) {
+ git_error_set_after_callback(error);
+ break;
+ }
+ }
+
+ if (regexp != NULL)
+ git_regexp_dispose(&regex);
+
+ iter->free(iter);
+
+ return error;
+}
+
+int git_config_foreach_match(
+ const git_config *cfg,
+ const char *regexp,
+ git_config_foreach_cb cb,
+ void *payload)
+{
+ int error;
+ git_config_iterator *iter;
+ git_config_entry *entry;
+
+ if ((error = git_config_iterator_glob_new(&iter, cfg, regexp)) < 0)
+ return error;
+
+ while (!(error = git_config_next(&entry, iter))) {
+ if ((error = cb(entry, payload)) != 0) {
+ git_error_set_after_callback(error);
+ break;
+ }
+ }
+
+ git_config_iterator_free(iter);
+
+ if (error == GIT_ITEROVER)
+ error = 0;
+
+ return error;
+}
+
+/**************
+ * Setters
+ **************/
+
+typedef enum {
+ BACKEND_USE_SET,
+ BACKEND_USE_DELETE
+} backend_use;
+
+static const char *uses[] = {
+ "set",
+ "delete"
+};
+
+static int get_backend_for_use(git_config_backend **out,
+ git_config *cfg, const char *name, backend_use use)
+{
+ size_t i;
+ backend_internal *backend;
+
+ *out = NULL;
+
+ if (git_vector_length(&cfg->backends) == 0) {
+ git_error_set(GIT_ERROR_CONFIG,
+ "cannot %s value for '%s' when no config backends exist",
+ uses[use], name);
+ return GIT_ENOTFOUND;
+ }
+
+ git_vector_foreach(&cfg->backends, i, backend) {
+ if (!backend->backend->readonly) {
+ *out = backend->backend;
+ return 0;
+ }
+ }
+
+ git_error_set(GIT_ERROR_CONFIG,
+ "cannot %s value for '%s' when all config backends are readonly",
+ uses[use], name);
+ return GIT_ENOTFOUND;
+}
+
+int git_config_delete_entry(git_config *cfg, const char *name)
+{
+ git_config_backend *backend;
+
+ if (get_backend_for_use(&backend, cfg, name, BACKEND_USE_DELETE) < 0)
+ return GIT_ENOTFOUND;
+
+ return backend->del(backend, name);
+}
+
+int git_config_set_int64(git_config *cfg, const char *name, int64_t value)
+{
+ char str_value[32]; /* All numbers should fit in here */
+ p_snprintf(str_value, sizeof(str_value), "%" PRId64, value);
+ return git_config_set_string(cfg, name, str_value);
+}
+
+int git_config_set_int32(git_config *cfg, const char *name, int32_t value)
+{
+ return git_config_set_int64(cfg, name, (int64_t)value);
+}
+
+int git_config_set_bool(git_config *cfg, const char *name, int value)
+{
+ return git_config_set_string(cfg, name, value ? "true" : "false");
+}
+
+int git_config_set_string(git_config *cfg, const char *name, const char *value)
+{
+ int error;
+ git_config_backend *backend;
+
+ if (!value) {
+ git_error_set(GIT_ERROR_CONFIG, "the value to set cannot be NULL");
+ return -1;
+ }
+
+ if (get_backend_for_use(&backend, cfg, name, BACKEND_USE_SET) < 0)
+ return GIT_ENOTFOUND;
+
+ error = backend->set(backend, name, value);
+
+ if (!error && GIT_REFCOUNT_OWNER(cfg) != NULL)
+ git_repository__configmap_lookup_cache_clear(GIT_REFCOUNT_OWNER(cfg));
+
+ return error;
+}
+
+int git_config__update_entry(
+ git_config *config,
+ const char *key,
+ const char *value,
+ bool overwrite_existing,
+ bool only_if_existing)
+{
+ int error = 0;
+ git_config_entry *ce = NULL;
+
+ if ((error = git_config__lookup_entry(&ce, config, key, false)) < 0)
+ return error;
+
+ if (!ce && only_if_existing) /* entry doesn't exist */
+ return 0;
+ if (ce && !overwrite_existing) /* entry would be overwritten */
+ return 0;
+ if (value && ce && ce->value && !strcmp(ce->value, value)) /* no change */
+ return 0;
+ if (!value && (!ce || !ce->value)) /* asked to delete absent entry */
+ return 0;
+
+ if (!value)
+ error = git_config_delete_entry(config, key);
+ else
+ error = git_config_set_string(config, key, value);
+
+ git_config_entry_free(ce);
+ return error;
+}
+
+/***********
+ * Getters
+ ***********/
+
+static int config_error_notfound(const char *name)
+{
+ git_error_set(GIT_ERROR_CONFIG, "config value '%s' was not found", name);
+ return GIT_ENOTFOUND;
+}
+
+enum {
+ GET_ALL_ERRORS = 0,
+ GET_NO_MISSING = 1,
+ GET_NO_ERRORS = 2
+};
+
+static int get_entry(
+ git_config_entry **out,
+ const git_config *cfg,
+ const char *name,
+ bool normalize_name,
+ int want_errors)
+{
+ int res = GIT_ENOTFOUND;
+ const char *key = name;
+ char *normalized = NULL;
+ size_t i;
+ backend_internal *internal;
+
+ *out = NULL;
+
+ if (normalize_name) {
+ if ((res = git_config__normalize_name(name, &normalized)) < 0)
+ goto cleanup;
+ key = normalized;
+ }
+
+ res = GIT_ENOTFOUND;
+ git_vector_foreach(&cfg->backends, i, internal) {
+ if (!internal || !internal->backend)
+ continue;
+
+ res = internal->backend->get(internal->backend, key, out);
+ if (res != GIT_ENOTFOUND)
+ break;
+ }
+
+ git__free(normalized);
+
+cleanup:
+ if (res == GIT_ENOTFOUND)
+ res = (want_errors > GET_ALL_ERRORS) ? 0 : config_error_notfound(name);
+ else if (res && (want_errors == GET_NO_ERRORS)) {
+ git_error_clear();
+ res = 0;
+ }
+
+ return res;
+}
+
+int git_config_get_entry(
+ git_config_entry **out, const git_config *cfg, const char *name)
+{
+ return get_entry(out, cfg, name, true, GET_ALL_ERRORS);
+}
+
+int git_config__lookup_entry(
+ git_config_entry **out,
+ const git_config *cfg,
+ const char *key,
+ bool no_errors)
+{
+ return get_entry(
+ out, cfg, key, false, no_errors ? GET_NO_ERRORS : GET_NO_MISSING);
+}
+
+int git_config_get_mapped(
+ int *out,
+ const git_config *cfg,
+ const char *name,
+ const git_configmap *maps,
+ size_t map_n)
+{
+ git_config_entry *entry;
+ int ret;
+
+ if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0)
+ return ret;
+
+ ret = git_config_lookup_map_value(out, maps, map_n, entry->value);
+ git_config_entry_free(entry);
+
+ return ret;
+}
+
+int git_config_get_int64(int64_t *out, const git_config *cfg, const char *name)
+{
+ git_config_entry *entry;
+ int ret;
+
+ if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0)
+ return ret;
+
+ ret = git_config_parse_int64(out, entry->value);
+ git_config_entry_free(entry);
+
+ return ret;
+}
+
+int git_config_get_int32(int32_t *out, const git_config *cfg, const char *name)
+{
+ git_config_entry *entry;
+ int ret;
+
+ if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0)
+ return ret;
+
+ ret = git_config_parse_int32(out, entry->value);
+ git_config_entry_free(entry);
+
+ return ret;
+}
+
+int git_config_get_bool(int *out, const git_config *cfg, const char *name)
+{
+ git_config_entry *entry;
+ int ret;
+
+ if ((ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0)
+ return ret;
+
+ ret = git_config_parse_bool(out, entry->value);
+ git_config_entry_free(entry);
+
+ return ret;
+}
+
+static int is_readonly(const git_config *cfg)
+{
+ size_t i;
+ backend_internal *internal;
+
+ git_vector_foreach(&cfg->backends, i, internal) {
+ if (!internal || !internal->backend)
+ continue;
+
+ if (!internal->backend->readonly)
+ return 0;
+ }
+
+ return 1;
+}
+
+static int git_config__parse_path(git_str *out, const char *value)
+{
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(value);
+
+ if (value[0] == '~') {
+ if (value[1] != '\0' && value[1] != '/') {
+ git_error_set(GIT_ERROR_CONFIG, "retrieving a homedir by name is not supported");
+ return -1;
+ }
+
+ return git_sysdir_expand_homedir_file(out, value[1] ? &value[2] : NULL);
+ }
+
+ return git_str_sets(out, value);
+}
+
+int git_config_parse_path(git_buf *out, const char *value)
+{
+ GIT_BUF_WRAP_PRIVATE(out, git_config__parse_path, value);
+}
+
+int git_config_get_path(
+ git_buf *out,
+ const git_config *cfg,
+ const char *name)
+{
+ GIT_BUF_WRAP_PRIVATE(out, git_config__get_path, cfg, name);
+}
+
+int git_config__get_path(
+ git_str *out,
+ const git_config *cfg,
+ const char *name)
+{
+ git_config_entry *entry;
+ int error;
+
+ if ((error = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS)) < 0)
+ return error;
+
+ error = git_config__parse_path(out, entry->value);
+ git_config_entry_free(entry);
+
+ return error;
+}
+
+int git_config_get_string(
+ const char **out, const git_config *cfg, const char *name)
+{
+ git_config_entry *entry;
+ int ret;
+
+ if (!is_readonly(cfg)) {
+ git_error_set(GIT_ERROR_CONFIG, "get_string called on a live config object");
+ return -1;
+ }
+
+ ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS);
+ *out = !ret ? (entry->value ? entry->value : "") : NULL;
+
+ git_config_entry_free(entry);
+
+ return ret;
+}
+
+int git_config_get_string_buf(
+ git_buf *out, const git_config *cfg, const char *name)
+{
+ GIT_BUF_WRAP_PRIVATE(out, git_config__get_string_buf, cfg, name);
+}
+
+int git_config__get_string_buf(
+ git_str *out, const git_config *cfg, const char *name)
+{
+ git_config_entry *entry;
+ int ret;
+ const char *str;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(cfg);
+
+ ret = get_entry(&entry, cfg, name, true, GET_ALL_ERRORS);
+ str = !ret ? (entry->value ? entry->value : "") : NULL;
+
+ if (str)
+ ret = git_str_puts(out, str);
+
+ git_config_entry_free(entry);
+
+ return ret;
+}
+
+char *git_config__get_string_force(
+ const git_config *cfg, const char *key, const char *fallback_value)
+{
+ git_config_entry *entry;
+ char *ret;
+
+ get_entry(&entry, cfg, key, false, GET_NO_ERRORS);
+ ret = (entry && entry->value) ? git__strdup(entry->value) : fallback_value ? git__strdup(fallback_value) : NULL;
+ git_config_entry_free(entry);
+
+ return ret;
+}
+
+int git_config__get_bool_force(
+ const git_config *cfg, const char *key, int fallback_value)
+{
+ int val = fallback_value;
+ git_config_entry *entry;
+
+ get_entry(&entry, cfg, key, false, GET_NO_ERRORS);
+
+ if (entry && git_config_parse_bool(&val, entry->value) < 0)
+ git_error_clear();
+
+ git_config_entry_free(entry);
+ return val;
+}
+
+int git_config__get_int_force(
+ const git_config *cfg, const char *key, int fallback_value)
+{
+ int32_t val = (int32_t)fallback_value;
+ git_config_entry *entry;
+
+ get_entry(&entry, cfg, key, false, GET_NO_ERRORS);
+
+ if (entry && git_config_parse_int32(&val, entry->value) < 0)
+ git_error_clear();
+
+ git_config_entry_free(entry);
+ return (int)val;
+}
+
+int git_config_get_multivar_foreach(
+ const git_config *cfg, const char *name, const char *regexp,
+ git_config_foreach_cb cb, void *payload)
+{
+ int err, found;
+ git_config_iterator *iter;
+ git_config_entry *entry;
+
+ if ((err = git_config_multivar_iterator_new(&iter, cfg, name, regexp)) < 0)
+ return err;
+
+ found = 0;
+ while ((err = iter->next(&entry, iter)) == 0) {
+ found = 1;
+
+ if ((err = cb(entry, payload)) != 0) {
+ git_error_set_after_callback(err);
+ break;
+ }
+ }
+
+ iter->free(iter);
+ if (err == GIT_ITEROVER)
+ err = 0;
+
+ if (found == 0 && err == 0)
+ err = config_error_notfound(name);
+
+ return err;
+}
+
+typedef struct {
+ git_config_iterator parent;
+ git_config_iterator *iter;
+ char *name;
+ git_regexp regex;
+ int have_regex;
+} multivar_iter;
+
+static int multivar_iter_next(git_config_entry **entry, git_config_iterator *_iter)
+{
+ multivar_iter *iter = (multivar_iter *) _iter;
+ int error = 0;
+
+ while ((error = iter->iter->next(entry, iter->iter)) == 0) {
+ if (git__strcmp(iter->name, (*entry)->name))
+ continue;
+
+ if (!iter->have_regex)
+ return 0;
+
+ if (git_regexp_match(&iter->regex, (*entry)->value) == 0)
+ return 0;
+ }
+
+ return error;
+}
+
+static void multivar_iter_free(git_config_iterator *_iter)
+{
+ multivar_iter *iter = (multivar_iter *) _iter;
+
+ iter->iter->free(iter->iter);
+
+ git__free(iter->name);
+ if (iter->have_regex)
+ git_regexp_dispose(&iter->regex);
+ git__free(iter);
+}
+
+int git_config_multivar_iterator_new(git_config_iterator **out, const git_config *cfg, const char *name, const char *regexp)
+{
+ multivar_iter *iter = NULL;
+ git_config_iterator *inner = NULL;
+ int error;
+
+ if ((error = git_config_iterator_new(&inner, cfg)) < 0)
+ return error;
+
+ iter = git__calloc(1, sizeof(multivar_iter));
+ GIT_ERROR_CHECK_ALLOC(iter);
+
+ if ((error = git_config__normalize_name(name, &iter->name)) < 0)
+ goto on_error;
+
+ if (regexp != NULL) {
+ if ((error = git_regexp_compile(&iter->regex, regexp, 0)) < 0)
+ goto on_error;
+
+ iter->have_regex = 1;
+ }
+
+ iter->iter = inner;
+ iter->parent.free = multivar_iter_free;
+ iter->parent.next = multivar_iter_next;
+
+ *out = (git_config_iterator *) iter;
+
+ return 0;
+
+on_error:
+
+ inner->free(inner);
+ git__free(iter);
+ return error;
+}
+
+int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value)
+{
+ git_config_backend *backend;
+
+ if (get_backend_for_use(&backend, cfg, name, BACKEND_USE_DELETE) < 0)
+ return GIT_ENOTFOUND;
+
+ return backend->set_multivar(backend, name, regexp, value);
+}
+
+int git_config_delete_multivar(git_config *cfg, const char *name, const char *regexp)
+{
+ git_config_backend *backend;
+
+ if (get_backend_for_use(&backend, cfg, name, BACKEND_USE_DELETE) < 0)
+ return GIT_ENOTFOUND;
+
+ return backend->del_multivar(backend, name, regexp);
+}
+
+int git_config_next(git_config_entry **entry, git_config_iterator *iter)
+{
+ return iter->next(entry, iter);
+}
+
+void git_config_iterator_free(git_config_iterator *iter)
+{
+ if (iter == NULL)
+ return;
+
+ iter->free(iter);
+}
+
+int git_config_find_global(git_buf *path)
+{
+ GIT_BUF_WRAP_PRIVATE(path, git_sysdir_find_global_file, GIT_CONFIG_FILENAME_GLOBAL);
+}
+
+int git_config__find_global(git_str *path)
+{
+ return git_sysdir_find_global_file(path, GIT_CONFIG_FILENAME_GLOBAL);
+}
+
+int git_config_find_xdg(git_buf *path)
+{
+ GIT_BUF_WRAP_PRIVATE(path, git_sysdir_find_xdg_file, GIT_CONFIG_FILENAME_XDG);
+}
+
+int git_config__find_xdg(git_str *path)
+{
+ return git_sysdir_find_xdg_file(path, GIT_CONFIG_FILENAME_XDG);
+}
+
+int git_config_find_system(git_buf *path)
+{
+ GIT_BUF_WRAP_PRIVATE(path, git_sysdir_find_system_file, GIT_CONFIG_FILENAME_SYSTEM);
+}
+
+int git_config__find_system(git_str *path)
+{
+ return git_sysdir_find_system_file(path, GIT_CONFIG_FILENAME_SYSTEM);
+}
+
+int git_config_find_programdata(git_buf *path)
+{
+ git_str str = GIT_STR_INIT;
+ int error;
+
+ if ((error = git_buf_tostr(&str, path)) == 0 &&
+ (error = git_config__find_programdata(&str)) == 0)
+ error = git_buf_fromstr(path, &str);
+
+ git_str_dispose(&str);
+ return error;
+}
+
+int git_config__find_programdata(git_str *path)
+{
+ git_fs_path_owner_t owner_level =
+ GIT_FS_PATH_OWNER_CURRENT_USER |
+ GIT_FS_PATH_OWNER_ADMINISTRATOR;
+ bool is_safe;
+ int error;
+
+ if ((error = git_sysdir_find_programdata_file(path, GIT_CONFIG_FILENAME_PROGRAMDATA)) < 0)
+ return error;
+
+ if (git_fs_path_owner_is(&is_safe, path->ptr, owner_level) < 0)
+ return -1;
+
+ if (!is_safe) {
+ git_error_set(GIT_ERROR_CONFIG, "programdata path has invalid ownership");
+ return -1;
+ }
+
+ return 0;
+}
+
+int git_config__global_location(git_str *buf)
+{
+ const git_str *paths;
+ const char *sep, *start;
+
+ if (git_sysdir_get(&paths, GIT_SYSDIR_GLOBAL) < 0)
+ return -1;
+
+ /* no paths, so give up */
+ if (!paths || !git_str_len(paths))
+ return -1;
+
+ /* find unescaped separator or end of string */
+ for (sep = start = git_str_cstr(paths); *sep; ++sep) {
+ if (*sep == GIT_PATH_LIST_SEPARATOR &&
+ (sep <= start || sep[-1] != '\\'))
+ break;
+ }
+
+ if (git_str_set(buf, start, (size_t)(sep - start)) < 0)
+ return -1;
+
+ return git_str_joinpath(buf, buf->ptr, GIT_CONFIG_FILENAME_GLOBAL);
+}
+
+int git_config_open_default(git_config **out)
+{
+ int error;
+ git_config *cfg = NULL;
+ git_str buf = GIT_STR_INIT;
+
+ if ((error = git_config_new(&cfg)) < 0)
+ return error;
+
+ if (!git_config__find_global(&buf) ||
+ !git_config__global_location(&buf)) {
+ error = git_config_add_file_ondisk(cfg, buf.ptr,
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0);
+ }
+
+ if (!error && !git_config__find_xdg(&buf))
+ error = git_config_add_file_ondisk(cfg, buf.ptr,
+ GIT_CONFIG_LEVEL_XDG, NULL, 0);
+
+ if (!error && !git_config__find_system(&buf))
+ error = git_config_add_file_ondisk(cfg, buf.ptr,
+ GIT_CONFIG_LEVEL_SYSTEM, NULL, 0);
+
+ if (!error && !git_config__find_programdata(&buf))
+ error = git_config_add_file_ondisk(cfg, buf.ptr,
+ GIT_CONFIG_LEVEL_PROGRAMDATA, NULL, 0);
+
+ git_str_dispose(&buf);
+
+ if (error) {
+ git_config_free(cfg);
+ cfg = NULL;
+ }
+
+ *out = cfg;
+
+ return error;
+}
+
+int git_config_lock(git_transaction **out, git_config *cfg)
+{
+ int error;
+ git_config_backend *backend;
+ backend_internal *internal;
+
+ GIT_ASSERT_ARG(cfg);
+
+ internal = git_vector_get(&cfg->backends, 0);
+ if (!internal || !internal->backend) {
+ git_error_set(GIT_ERROR_CONFIG, "cannot lock; the config has no backends");
+ return -1;
+ }
+ backend = internal->backend;
+
+ if ((error = backend->lock(backend)) < 0)
+ return error;
+
+ return git_transaction_config_new(out, cfg);
+}
+
+int git_config_unlock(git_config *cfg, int commit)
+{
+ git_config_backend *backend;
+ backend_internal *internal;
+
+ GIT_ASSERT_ARG(cfg);
+
+ internal = git_vector_get(&cfg->backends, 0);
+ if (!internal || !internal->backend) {
+ git_error_set(GIT_ERROR_CONFIG, "cannot lock; the config has no backends");
+ return -1;
+ }
+
+ backend = internal->backend;
+
+ return backend->unlock(backend, commit);
+}
+
+/***********
+ * Parsers
+ ***********/
+
+int git_config_lookup_map_value(
+ int *out,
+ const git_configmap *maps,
+ size_t map_n,
+ const char *value)
+{
+ size_t i;
+
+ for (i = 0; i < map_n; ++i) {
+ const git_configmap *m = maps + i;
+
+ switch (m->type) {
+ case GIT_CONFIGMAP_FALSE:
+ case GIT_CONFIGMAP_TRUE: {
+ int bool_val;
+
+ if (git_config_parse_bool(&bool_val, value) == 0 &&
+ bool_val == (int)m->type) {
+ *out = m->map_value;
+ return 0;
+ }
+ break;
+ }
+
+ case GIT_CONFIGMAP_INT32:
+ if (git_config_parse_int32(out, value) == 0)
+ return 0;
+ break;
+
+ case GIT_CONFIGMAP_STRING:
+ if (value && strcasecmp(value, m->str_match) == 0) {
+ *out = m->map_value;
+ return 0;
+ }
+ break;
+ }
+ }
+
+ git_error_set(GIT_ERROR_CONFIG, "failed to map '%s'", value);
+ return -1;
+}
+
+int git_config_lookup_map_enum(git_configmap_t *type_out, const char **str_out,
+ const git_configmap *maps, size_t map_n, int enum_val)
+{
+ size_t i;
+
+ for (i = 0; i < map_n; i++) {
+ const git_configmap *m = &maps[i];
+
+ if (m->map_value != enum_val)
+ continue;
+
+ *type_out = m->type;
+ *str_out = m->str_match;
+ return 0;
+ }
+
+ git_error_set(GIT_ERROR_CONFIG, "invalid enum value");
+ return GIT_ENOTFOUND;
+}
+
+int git_config_parse_bool(int *out, const char *value)
+{
+ if (git__parse_bool(out, value) == 0)
+ return 0;
+
+ if (git_config_parse_int32(out, value) == 0) {
+ *out = !!(*out);
+ return 0;
+ }
+
+ git_error_set(GIT_ERROR_CONFIG, "failed to parse '%s' as a boolean value", value);
+ return -1;
+}
+
+int git_config_parse_int64(int64_t *out, const char *value)
+{
+ const char *num_end;
+ int64_t num;
+
+ if (!value || git__strntol64(&num, value, strlen(value), &num_end, 0) < 0)
+ goto fail_parse;
+
+ switch (*num_end) {
+ case 'g':
+ case 'G':
+ num *= 1024;
+ /* fallthrough */
+
+ case 'm':
+ case 'M':
+ num *= 1024;
+ /* fallthrough */
+
+ case 'k':
+ case 'K':
+ num *= 1024;
+
+ /* check that that there are no more characters after the
+ * given modifier suffix */
+ if (num_end[1] != '\0')
+ return -1;
+
+ /* fallthrough */
+
+ case '\0':
+ *out = num;
+ return 0;
+
+ default:
+ goto fail_parse;
+ }
+
+fail_parse:
+ git_error_set(GIT_ERROR_CONFIG, "failed to parse '%s' as an integer", value ? value : "(null)");
+ return -1;
+}
+
+int git_config_parse_int32(int32_t *out, const char *value)
+{
+ int64_t tmp;
+ int32_t truncate;
+
+ if (git_config_parse_int64(&tmp, value) < 0)
+ goto fail_parse;
+
+ truncate = tmp & 0xFFFFFFFF;
+ if (truncate != tmp)
+ goto fail_parse;
+
+ *out = truncate;
+ return 0;
+
+fail_parse:
+ git_error_set(GIT_ERROR_CONFIG, "failed to parse '%s' as a 32-bit integer", value ? value : "(null)");
+ return -1;
+}
+
+static int normalize_section(char *start, char *end)
+{
+ char *scan;
+
+ if (start == end)
+ return GIT_EINVALIDSPEC;
+
+ /* Validate and downcase range */
+ for (scan = start; *scan; ++scan) {
+ if (end && scan >= end)
+ break;
+ if (isalnum(*scan))
+ *scan = (char)git__tolower(*scan);
+ else if (*scan != '-' || scan == start)
+ return GIT_EINVALIDSPEC;
+ }
+
+ if (scan == start)
+ return GIT_EINVALIDSPEC;
+
+ return 0;
+}
+
+
+/* Take something the user gave us and make it nice for our hash function */
+int git_config__normalize_name(const char *in, char **out)
+{
+ char *name, *fdot, *ldot;
+
+ GIT_ASSERT_ARG(in);
+ GIT_ASSERT_ARG(out);
+
+ name = git__strdup(in);
+ GIT_ERROR_CHECK_ALLOC(name);
+
+ fdot = strchr(name, '.');
+ ldot = strrchr(name, '.');
+
+ if (fdot == NULL || fdot == name || ldot == NULL || !ldot[1])
+ goto invalid;
+
+ /* Validate and downcase up to first dot and after last dot */
+ if (normalize_section(name, fdot) < 0 ||
+ normalize_section(ldot + 1, NULL) < 0)
+ goto invalid;
+
+ /* If there is a middle range, make sure it doesn't have newlines */
+ while (fdot < ldot)
+ if (*fdot++ == '\n')
+ goto invalid;
+
+ *out = name;
+ return 0;
+
+invalid:
+ git__free(name);
+ git_error_set(GIT_ERROR_CONFIG, "invalid config item name '%s'", in);
+ return GIT_EINVALIDSPEC;
+}
+
+struct rename_data {
+ git_config *config;
+ git_str *name;
+ size_t old_len;
+};
+
+static int rename_config_entries_cb(
+ const git_config_entry *entry,
+ void *payload)
+{
+ int error = 0;
+ struct rename_data *data = (struct rename_data *)payload;
+ size_t base_len = git_str_len(data->name);
+
+ if (base_len > 0 &&
+ !(error = git_str_puts(data->name, entry->name + data->old_len)))
+ {
+ error = git_config_set_string(
+ data->config, git_str_cstr(data->name), entry->value);
+
+ git_str_truncate(data->name, base_len);
+ }
+
+ if (!error)
+ error = git_config_delete_entry(data->config, entry->name);
+
+ return error;
+}
+
+int git_config_rename_section(
+ git_repository *repo,
+ const char *old_section_name,
+ const char *new_section_name)
+{
+ git_config *config;
+ git_str pattern = GIT_STR_INIT, replace = GIT_STR_INIT;
+ int error = 0;
+ struct rename_data data;
+
+ git_str_puts_escape_regex(&pattern, old_section_name);
+
+ if ((error = git_str_puts(&pattern, "\\..+")) < 0)
+ goto cleanup;
+
+ if ((error = git_repository_config__weakptr(&config, repo)) < 0)
+ goto cleanup;
+
+ data.config = config;
+ data.name = &replace;
+ data.old_len = strlen(old_section_name) + 1;
+
+ if ((error = git_str_join(&replace, '.', new_section_name, "")) < 0)
+ goto cleanup;
+
+ if (new_section_name != NULL &&
+ (error = normalize_section(replace.ptr, strchr(replace.ptr, '.'))) < 0)
+ {
+ git_error_set(
+ GIT_ERROR_CONFIG, "invalid config section '%s'", new_section_name);
+ goto cleanup;
+ }
+
+ error = git_config_foreach_match(
+ config, git_str_cstr(&pattern), rename_config_entries_cb, &data);
+
+cleanup:
+ git_str_dispose(&pattern);
+ git_str_dispose(&replace);
+
+ return error;
+}
+
+int git_config_init_backend(git_config_backend *backend, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ backend, version, git_config_backend, GIT_CONFIG_BACKEND_INIT);
+ return 0;
+}
diff --git a/src/libgit2/config.h b/src/libgit2/config.h
new file mode 100644
index 0000000..01b84b1
--- /dev/null
+++ b/src/libgit2/config.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_config_h__
+#define INCLUDE_config_h__
+
+#include "common.h"
+
+#include "git2.h"
+#include "git2/config.h"
+#include "vector.h"
+#include "repository.h"
+
+#define GIT_CONFIG_FILENAME_PROGRAMDATA "config"
+#define GIT_CONFIG_FILENAME_SYSTEM "gitconfig"
+#define GIT_CONFIG_FILENAME_GLOBAL ".gitconfig"
+#define GIT_CONFIG_FILENAME_XDG "config"
+
+#define GIT_CONFIG_FILENAME_INREPO "config"
+#define GIT_CONFIG_FILE_MODE 0666
+
+struct git_config {
+ git_refcount rc;
+ git_vector backends;
+};
+
+extern int git_config__global_location(git_str *buf);
+
+extern int git_config__find_global(git_str *path);
+extern int git_config__find_xdg(git_str *path);
+extern int git_config__find_system(git_str *path);
+extern int git_config__find_programdata(git_str *path);
+
+extern int git_config_rename_section(
+ git_repository *repo,
+ const char *old_section_name, /* eg "branch.dummy" */
+ const char *new_section_name); /* NULL to drop the old section */
+
+extern int git_config__normalize_name(const char *in, char **out);
+
+/* internal only: does not normalize key and sets out to NULL if not found */
+extern int git_config__lookup_entry(
+ git_config_entry **out,
+ const git_config *cfg,
+ const char *key,
+ bool no_errors);
+
+/* internal only: update and/or delete entry string with constraints */
+extern int git_config__update_entry(
+ git_config *cfg,
+ const char *key,
+ const char *value,
+ bool overwrite_existing,
+ bool only_if_existing);
+
+int git_config__get_path(
+ git_str *out,
+ const git_config *cfg,
+ const char *name);
+
+int git_config__get_string_buf(
+ git_str *out, const git_config *cfg, const char *name);
+
+/*
+ * Lookup functions that cannot fail. These functions look up a config
+ * value and return a fallback value if the value is missing or if any
+ * failures occur while trying to access the value.
+ */
+
+extern char *git_config__get_string_force(
+ const git_config *cfg, const char *key, const char *fallback_value);
+
+extern int git_config__get_bool_force(
+ const git_config *cfg, const char *key, int fallback_value);
+
+extern int git_config__get_int_force(
+ const git_config *cfg, const char *key, int fallback_value);
+
+/* API for repository configmap-style lookups from config - not cached, but
+ * uses configmap value maps and fallbacks
+ */
+extern int git_config__configmap_lookup(
+ int *out, git_config *config, git_configmap_item item);
+
+/**
+ * The opposite of git_config_lookup_map_value, we take an enum value
+ * and map it to the string or bool value on the config.
+ */
+int git_config_lookup_map_enum(git_configmap_t *type_out,
+ const char **str_out, const git_configmap *maps,
+ size_t map_n, int enum_val);
+
+/**
+ * Unlock the backend with the highest priority
+ *
+ * Unlocking will allow other writers to update the configuration
+ * file. Optionally, any changes performed since the lock will be
+ * applied to the configuration.
+ *
+ * @param cfg the configuration
+ * @param commit boolean which indicates whether to commit any changes
+ * done since locking
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_config_unlock(git_config *cfg, int commit);
+
+#endif
diff --git a/src/libgit2/config_backend.h b/src/libgit2/config_backend.h
new file mode 100644
index 0000000..dbb1905
--- /dev/null
+++ b/src/libgit2/config_backend.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_config_file_h__
+#define INCLUDE_config_file_h__
+
+#include "common.h"
+
+#include "git2/sys/config.h"
+#include "git2/config.h"
+
+/**
+ * Create a configuration file backend for ondisk files
+ *
+ * These are the normal `.gitconfig` files that Core Git
+ * processes. Note that you first have to add this file to a
+ * configuration object before you can query it for configuration
+ * variables.
+ *
+ * @param out the new backend
+ * @param path where the config file is located
+ */
+extern int git_config_backend_from_file(git_config_backend **out, const char *path);
+
+/**
+ * Create a readonly configuration file backend from another backend
+ *
+ * This copies the complete contents of the source backend to the
+ * new backend. The new backend will be completely read-only and
+ * cannot be modified.
+ *
+ * @param out the new snapshotted backend
+ * @param source the backend to copy
+ */
+extern int git_config_backend_snapshot(git_config_backend **out, git_config_backend *source);
+
+/**
+ * Create an in-memory configuration file backend
+ *
+ * @param out the new backend
+ * @param cfg the configuration that is to be parsed
+ * @param len the length of the string pointed to by `cfg`
+ */
+extern int git_config_backend_from_string(git_config_backend **out, const char *cfg, size_t len);
+
+GIT_INLINE(int) git_config_backend_open(git_config_backend *cfg, unsigned int level, const git_repository *repo)
+{
+ return cfg->open(cfg, level, repo);
+}
+
+GIT_INLINE(void) git_config_backend_free(git_config_backend *cfg)
+{
+ if (cfg)
+ cfg->free(cfg);
+}
+
+GIT_INLINE(int) git_config_backend_get_string(
+ git_config_entry **out, git_config_backend *cfg, const char *name)
+{
+ return cfg->get(cfg, name, out);
+}
+
+GIT_INLINE(int) git_config_backend_set_string(
+ git_config_backend *cfg, const char *name, const char *value)
+{
+ return cfg->set(cfg, name, value);
+}
+
+GIT_INLINE(int) git_config_backend_delete(
+ git_config_backend *cfg, const char *name)
+{
+ return cfg->del(cfg, name);
+}
+
+GIT_INLINE(int) git_config_backend_foreach(
+ git_config_backend *cfg,
+ int (*fn)(const git_config_entry *entry, void *data),
+ void *data)
+{
+ return git_config_backend_foreach_match(cfg, NULL, fn, data);
+}
+
+GIT_INLINE(int) git_config_backend_lock(git_config_backend *cfg)
+{
+ return cfg->lock(cfg);
+}
+
+GIT_INLINE(int) git_config_backend_unlock(git_config_backend *cfg, int success)
+{
+ return cfg->unlock(cfg, success);
+}
+
+#endif
diff --git a/src/libgit2/config_cache.c b/src/libgit2/config_cache.c
new file mode 100644
index 0000000..4bb91f5
--- /dev/null
+++ b/src/libgit2/config_cache.c
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+
+#include "futils.h"
+#include "repository.h"
+#include "config.h"
+#include "git2/config.h"
+#include "vector.h"
+#include "filter.h"
+
+struct map_data {
+ const char *name;
+ git_configmap *maps;
+ size_t map_count;
+ int default_value;
+};
+
+/*
+ * core.eol
+ * Sets the line ending type to use in the working directory for
+ * files that have the text property set. Alternatives are lf, crlf
+ * and native, which uses the platform's native line ending. The default
+ * value is native. See gitattributes(5) for more information on
+ * end-of-line conversion.
+ */
+static git_configmap _configmap_eol[] = {
+ {GIT_CONFIGMAP_FALSE, NULL, GIT_EOL_UNSET},
+ {GIT_CONFIGMAP_STRING, "lf", GIT_EOL_LF},
+ {GIT_CONFIGMAP_STRING, "crlf", GIT_EOL_CRLF},
+ {GIT_CONFIGMAP_STRING, "native", GIT_EOL_NATIVE}
+};
+
+/*
+ * core.autocrlf
+ * Setting this variable to "true" is almost the same as setting
+ * the text attribute to "auto" on all files except that text files are
+ * not guaranteed to be normalized: files that contain CRLF in the
+ * repository will not be touched. Use this setting if you want to have
+ * CRLF line endings in your working directory even though the repository
+ * does not have normalized line endings. This variable can be set to input,
+ * in which case no output conversion is performed.
+ */
+static git_configmap _configmap_autocrlf[] = {
+ {GIT_CONFIGMAP_FALSE, NULL, GIT_AUTO_CRLF_FALSE},
+ {GIT_CONFIGMAP_TRUE, NULL, GIT_AUTO_CRLF_TRUE},
+ {GIT_CONFIGMAP_STRING, "input", GIT_AUTO_CRLF_INPUT}
+};
+
+static git_configmap _configmap_safecrlf[] = {
+ {GIT_CONFIGMAP_FALSE, NULL, GIT_SAFE_CRLF_FALSE},
+ {GIT_CONFIGMAP_TRUE, NULL, GIT_SAFE_CRLF_FAIL},
+ {GIT_CONFIGMAP_STRING, "warn", GIT_SAFE_CRLF_WARN}
+};
+
+static git_configmap _configmap_logallrefupdates[] = {
+ {GIT_CONFIGMAP_FALSE, NULL, GIT_LOGALLREFUPDATES_FALSE},
+ {GIT_CONFIGMAP_TRUE, NULL, GIT_LOGALLREFUPDATES_TRUE},
+ {GIT_CONFIGMAP_STRING, "always", GIT_LOGALLREFUPDATES_ALWAYS},
+};
+
+/*
+ * Generic map for integer values
+ */
+static git_configmap _configmap_int[] = {
+ {GIT_CONFIGMAP_INT32, NULL, 0},
+};
+
+static struct map_data _configmaps[] = {
+ {"core.autocrlf", _configmap_autocrlf, ARRAY_SIZE(_configmap_autocrlf), GIT_AUTO_CRLF_DEFAULT},
+ {"core.eol", _configmap_eol, ARRAY_SIZE(_configmap_eol), GIT_EOL_DEFAULT},
+ {"core.symlinks", NULL, 0, GIT_SYMLINKS_DEFAULT },
+ {"core.ignorecase", NULL, 0, GIT_IGNORECASE_DEFAULT },
+ {"core.filemode", NULL, 0, GIT_FILEMODE_DEFAULT },
+ {"core.ignorestat", NULL, 0, GIT_IGNORESTAT_DEFAULT },
+ {"core.trustctime", NULL, 0, GIT_TRUSTCTIME_DEFAULT },
+ {"core.abbrev", _configmap_int, 1, GIT_ABBREV_DEFAULT },
+ {"core.precomposeunicode", NULL, 0, GIT_PRECOMPOSE_DEFAULT },
+ {"core.safecrlf", _configmap_safecrlf, ARRAY_SIZE(_configmap_safecrlf), GIT_SAFE_CRLF_DEFAULT},
+ {"core.logallrefupdates", _configmap_logallrefupdates, ARRAY_SIZE(_configmap_logallrefupdates), GIT_LOGALLREFUPDATES_DEFAULT},
+ {"core.protecthfs", NULL, 0, GIT_PROTECTHFS_DEFAULT },
+ {"core.protectntfs", NULL, 0, GIT_PROTECTNTFS_DEFAULT },
+ {"core.fsyncobjectfiles", NULL, 0, GIT_FSYNCOBJECTFILES_DEFAULT },
+ {"core.longpaths", NULL, 0, GIT_LONGPATHS_DEFAULT },
+};
+
+int git_config__configmap_lookup(int *out, git_config *config, git_configmap_item item)
+{
+ int error = 0;
+ struct map_data *data = &_configmaps[(int)item];
+ git_config_entry *entry;
+
+ if ((error = git_config__lookup_entry(&entry, config, data->name, false)) < 0)
+ return error;
+
+ if (!entry)
+ *out = data->default_value;
+ else if (data->maps)
+ error = git_config_lookup_map_value(
+ out, data->maps, data->map_count, entry->value);
+ else
+ error = git_config_parse_bool(out, entry->value);
+
+ git_config_entry_free(entry);
+ return error;
+}
+
+int git_repository__configmap_lookup(int *out, git_repository *repo, git_configmap_item item)
+{
+ intptr_t value = (intptr_t)git_atomic_load(repo->configmap_cache[(int)item]);
+
+ *out = (int)value;
+
+ if (value == GIT_CONFIGMAP_NOT_CACHED) {
+ git_config *config;
+ intptr_t oldval = value;
+ int error;
+
+ if ((error = git_repository_config__weakptr(&config, repo)) < 0 ||
+ (error = git_config__configmap_lookup(out, config, item)) < 0)
+ return error;
+
+ value = *out;
+ git_atomic_compare_and_swap(&repo->configmap_cache[(int)item], (void *)oldval, (void *)value);
+ }
+
+ return 0;
+}
+
+void git_repository__configmap_lookup_cache_clear(git_repository *repo)
+{
+ int i;
+
+ for (i = 0; i < GIT_CONFIGMAP_CACHE_MAX; ++i)
+ repo->configmap_cache[i] = GIT_CONFIGMAP_NOT_CACHED;
+}
+
diff --git a/src/libgit2/config_entries.c b/src/libgit2/config_entries.c
new file mode 100644
index 0000000..66aae09
--- /dev/null
+++ b/src/libgit2/config_entries.c
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "config_entries.h"
+
+typedef struct config_entry_list {
+ struct config_entry_list *next;
+ struct config_entry_list *last;
+ git_config_entry *entry;
+} config_entry_list;
+
+typedef struct {
+ git_config_entry *entry;
+ bool multivar;
+} config_entry_map_head;
+
+typedef struct config_entries_iterator {
+ git_config_iterator parent;
+ git_config_entries *entries;
+ config_entry_list *head;
+} config_entries_iterator;
+
+struct git_config_entries {
+ git_refcount rc;
+ git_strmap *map;
+ config_entry_list *list;
+};
+
+int git_config_entries_new(git_config_entries **out)
+{
+ git_config_entries *entries;
+ int error;
+
+ entries = git__calloc(1, sizeof(git_config_entries));
+ GIT_ERROR_CHECK_ALLOC(entries);
+ GIT_REFCOUNT_INC(entries);
+
+ if ((error = git_strmap_new(&entries->map)) < 0)
+ git__free(entries);
+ else
+ *out = entries;
+
+ return error;
+}
+
+int git_config_entries_dup_entry(git_config_entries *entries, const git_config_entry *entry)
+{
+ git_config_entry *duplicated;
+ int error;
+
+ duplicated = git__calloc(1, sizeof(git_config_entry));
+ GIT_ERROR_CHECK_ALLOC(duplicated);
+
+ duplicated->name = git__strdup(entry->name);
+ GIT_ERROR_CHECK_ALLOC(duplicated->name);
+
+ if (entry->value) {
+ duplicated->value = git__strdup(entry->value);
+ GIT_ERROR_CHECK_ALLOC(duplicated->value);
+ }
+ duplicated->level = entry->level;
+ duplicated->include_depth = entry->include_depth;
+
+ if ((error = git_config_entries_append(entries, duplicated)) < 0)
+ goto out;
+
+out:
+ if (error && duplicated) {
+ git__free((char *) duplicated->name);
+ git__free((char *) duplicated->value);
+ git__free(duplicated);
+ }
+ return error;
+}
+
+int git_config_entries_dup(git_config_entries **out, git_config_entries *entries)
+{
+ git_config_entries *result = NULL;
+ config_entry_list *head;
+ int error;
+
+ if ((error = git_config_entries_new(&result)) < 0)
+ goto out;
+
+ for (head = entries->list; head; head = head->next)
+ if ((git_config_entries_dup_entry(result, head->entry)) < 0)
+ goto out;
+
+ *out = result;
+ result = NULL;
+
+out:
+ git_config_entries_free(result);
+ return error;
+}
+
+void git_config_entries_incref(git_config_entries *entries)
+{
+ GIT_REFCOUNT_INC(entries);
+}
+
+static void config_entries_free(git_config_entries *entries)
+{
+ config_entry_list *list = NULL, *next;
+ config_entry_map_head *head;
+
+ git_strmap_foreach_value(entries->map, head,
+ git__free((char *) head->entry->name); git__free(head)
+ );
+ git_strmap_free(entries->map);
+
+ list = entries->list;
+ while (list != NULL) {
+ next = list->next;
+ git__free((char *) list->entry->value);
+ git__free(list->entry);
+ git__free(list);
+ list = next;
+ }
+
+ git__free(entries);
+}
+
+void git_config_entries_free(git_config_entries *entries)
+{
+ if (entries)
+ GIT_REFCOUNT_DEC(entries, config_entries_free);
+}
+
+int git_config_entries_append(git_config_entries *entries, git_config_entry *entry)
+{
+ config_entry_list *list_head;
+ config_entry_map_head *map_head;
+
+ if ((map_head = git_strmap_get(entries->map, entry->name)) != NULL) {
+ map_head->multivar = true;
+ /*
+ * This is a micro-optimization for configuration files
+ * with a lot of same keys. As for multivars the entry's
+ * key will be the same for all entries, we can just free
+ * all except the first entry's name and just re-use it.
+ */
+ git__free((char *) entry->name);
+ entry->name = map_head->entry->name;
+ } else {
+ map_head = git__calloc(1, sizeof(*map_head));
+ if ((git_strmap_set(entries->map, entry->name, map_head)) < 0)
+ return -1;
+ }
+ map_head->entry = entry;
+
+ list_head = git__calloc(1, sizeof(config_entry_list));
+ GIT_ERROR_CHECK_ALLOC(list_head);
+ list_head->entry = entry;
+
+ if (entries->list)
+ entries->list->last->next = list_head;
+ else
+ entries->list = list_head;
+ entries->list->last = list_head;
+
+ return 0;
+}
+
+int git_config_entries_get(git_config_entry **out, git_config_entries *entries, const char *key)
+{
+ config_entry_map_head *entry;
+ if ((entry = git_strmap_get(entries->map, key)) == NULL)
+ return GIT_ENOTFOUND;
+ *out = entry->entry;
+ return 0;
+}
+
+int git_config_entries_get_unique(git_config_entry **out, git_config_entries *entries, const char *key)
+{
+ config_entry_map_head *entry;
+
+ if ((entry = git_strmap_get(entries->map, key)) == NULL)
+ return GIT_ENOTFOUND;
+
+ if (entry->multivar) {
+ git_error_set(GIT_ERROR_CONFIG, "entry is not unique due to being a multivar");
+ return -1;
+ }
+
+ if (entry->entry->include_depth) {
+ git_error_set(GIT_ERROR_CONFIG, "entry is not unique due to being included");
+ return -1;
+ }
+
+ *out = entry->entry;
+
+ return 0;
+}
+
+static void config_iterator_free(git_config_iterator *iter)
+{
+ config_entries_iterator *it = (config_entries_iterator *) iter;
+ git_config_entries_free(it->entries);
+ git__free(it);
+}
+
+static int config_iterator_next(
+ git_config_entry **entry,
+ git_config_iterator *iter)
+{
+ config_entries_iterator *it = (config_entries_iterator *) iter;
+
+ if (!it->head)
+ return GIT_ITEROVER;
+
+ *entry = it->head->entry;
+ it->head = it->head->next;
+
+ return 0;
+}
+
+int git_config_entries_iterator_new(git_config_iterator **out, git_config_entries *entries)
+{
+ config_entries_iterator *it;
+
+ it = git__calloc(1, sizeof(config_entries_iterator));
+ GIT_ERROR_CHECK_ALLOC(it);
+ it->parent.next = config_iterator_next;
+ it->parent.free = config_iterator_free;
+ it->head = entries->list;
+ it->entries = entries;
+
+ git_config_entries_incref(entries);
+ *out = &it->parent;
+
+ return 0;
+}
diff --git a/src/libgit2/config_entries.h b/src/libgit2/config_entries.h
new file mode 100644
index 0000000..832379e
--- /dev/null
+++ b/src/libgit2/config_entries.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+
+#include "git2/sys/config.h"
+#include "config.h"
+
+typedef struct git_config_entries git_config_entries;
+
+int git_config_entries_new(git_config_entries **out);
+int git_config_entries_dup(git_config_entries **out, git_config_entries *entries);
+int git_config_entries_dup_entry(git_config_entries *entries, const git_config_entry *entry);
+void git_config_entries_incref(git_config_entries *entries);
+void git_config_entries_free(git_config_entries *entries);
+/* Add or append the new config option */
+int git_config_entries_append(git_config_entries *entries, git_config_entry *entry);
+int git_config_entries_get(git_config_entry **out, git_config_entries *entries, const char *key);
+int git_config_entries_get_unique(git_config_entry **out, git_config_entries *entries, const char *key);
+int git_config_entries_iterator_new(git_config_iterator **out, git_config_entries *entries);
diff --git a/src/libgit2/config_file.c b/src/libgit2/config_file.c
new file mode 100644
index 0000000..716924d
--- /dev/null
+++ b/src/libgit2/config_file.c
@@ -0,0 +1,1200 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "config.h"
+
+#include "git2/config.h"
+#include "git2/sys/config.h"
+
+#include "array.h"
+#include "str.h"
+#include "config_backend.h"
+#include "config_entries.h"
+#include "config_parse.h"
+#include "filebuf.h"
+#include "regexp.h"
+#include "sysdir.h"
+#include "wildmatch.h"
+#include "hash.h"
+
+/* Max depth for [include] directives */
+#define MAX_INCLUDE_DEPTH 10
+
+typedef struct config_file {
+ git_futils_filestamp stamp;
+ unsigned char checksum[GIT_HASH_SHA256_SIZE];
+ char *path;
+ git_array_t(struct config_file) includes;
+} config_file;
+
+typedef struct {
+ git_config_backend parent;
+ git_mutex values_mutex;
+ git_config_entries *entries;
+ const git_repository *repo;
+ git_config_level_t level;
+
+ git_array_t(git_config_parser) readers;
+
+ bool locked;
+ git_filebuf locked_buf;
+ git_str locked_content;
+
+ config_file file;
+} config_file_backend;
+
+typedef struct {
+ const git_repository *repo;
+ config_file *file;
+ git_config_entries *entries;
+ git_config_level_t level;
+ unsigned int depth;
+} config_file_parse_data;
+
+static int config_file_read(git_config_entries *entries, const git_repository *repo, config_file *file, git_config_level_t level, int depth);
+static int config_file_read_buffer(git_config_entries *entries, const git_repository *repo, config_file *file, git_config_level_t level, int depth, const char *buf, size_t buflen);
+static int config_file_write(config_file_backend *cfg, const char *orig_key, const char *key, const git_regexp *preg, const char *value);
+static char *escape_value(const char *ptr);
+
+/**
+ * Take the current values map from the backend and increase its
+ * refcount. This is its own function to make sure we use the mutex to
+ * avoid the map pointer from changing under us.
+ */
+static int config_file_entries_take(git_config_entries **out, config_file_backend *b)
+{
+ int error;
+
+ if ((error = git_mutex_lock(&b->values_mutex)) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to lock config backend");
+ return error;
+ }
+
+ git_config_entries_incref(b->entries);
+ *out = b->entries;
+
+ git_mutex_unlock(&b->values_mutex);
+
+ return 0;
+}
+
+static void config_file_clear(config_file *file)
+{
+ config_file *include;
+ uint32_t i;
+
+ if (file == NULL)
+ return;
+
+ git_array_foreach(file->includes, i, include) {
+ config_file_clear(include);
+ }
+ git_array_clear(file->includes);
+
+ git__free(file->path);
+}
+
+static int config_file_open(git_config_backend *cfg, git_config_level_t level, const git_repository *repo)
+{
+ config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent);
+ int res;
+
+ b->level = level;
+ b->repo = repo;
+
+ if ((res = git_config_entries_new(&b->entries)) < 0)
+ return res;
+
+ if (!git_fs_path_exists(b->file.path))
+ return 0;
+
+ /*
+ * git silently ignores configuration files that are not
+ * readable. We emulate that behavior. This is particularly
+ * important for sandboxed applications on macOS where the
+ * git configuration files may not be readable.
+ */
+ if (p_access(b->file.path, R_OK) < 0)
+ return GIT_ENOTFOUND;
+
+ if (res < 0 || (res = config_file_read(b->entries, repo, &b->file, level, 0)) < 0) {
+ git_config_entries_free(b->entries);
+ b->entries = NULL;
+ }
+
+ return res;
+}
+
+static int config_file_is_modified(int *modified, config_file *file)
+{
+ config_file *include;
+ git_str buf = GIT_STR_INIT;
+ unsigned char checksum[GIT_HASH_SHA256_SIZE];
+ uint32_t i;
+ int error = 0;
+
+ *modified = 0;
+
+ if (!git_futils_filestamp_check(&file->stamp, file->path))
+ goto check_includes;
+
+ if ((error = git_futils_readbuffer(&buf, file->path)) < 0)
+ goto out;
+
+ if ((error = git_hash_buf(checksum, buf.ptr, buf.size, GIT_HASH_ALGORITHM_SHA256)) < 0)
+ goto out;
+
+ if (memcmp(checksum, file->checksum, GIT_HASH_SHA256_SIZE) != 0) {
+ *modified = 1;
+ goto out;
+ }
+
+check_includes:
+ git_array_foreach(file->includes, i, include) {
+ if ((error = config_file_is_modified(modified, include)) < 0 || *modified)
+ goto out;
+ }
+
+out:
+ git_str_dispose(&buf);
+
+ return error;
+}
+
+static void config_file_clear_includes(config_file_backend *cfg)
+{
+ config_file *include;
+ uint32_t i;
+
+ git_array_foreach(cfg->file.includes, i, include)
+ config_file_clear(include);
+ git_array_clear(cfg->file.includes);
+}
+
+static int config_file_set_entries(git_config_backend *cfg, git_config_entries *entries)
+{
+ config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent);
+ git_config_entries *old = NULL;
+ int error;
+
+ if (b->parent.readonly) {
+ git_error_set(GIT_ERROR_CONFIG, "this backend is read-only");
+ return -1;
+ }
+
+ if ((error = git_mutex_lock(&b->values_mutex)) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to lock config backend");
+ goto out;
+ }
+
+ old = b->entries;
+ b->entries = entries;
+
+ git_mutex_unlock(&b->values_mutex);
+
+out:
+ git_config_entries_free(old);
+ return error;
+}
+
+static int config_file_refresh_from_buffer(git_config_backend *cfg, const char *buf, size_t buflen)
+{
+ config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent);
+ git_config_entries *entries = NULL;
+ int error;
+
+ config_file_clear_includes(b);
+
+ if ((error = git_config_entries_new(&entries)) < 0 ||
+ (error = config_file_read_buffer(entries, b->repo, &b->file,
+ b->level, 0, buf, buflen)) < 0 ||
+ (error = config_file_set_entries(cfg, entries)) < 0)
+ goto out;
+
+ entries = NULL;
+out:
+ git_config_entries_free(entries);
+ return error;
+}
+
+static int config_file_refresh(git_config_backend *cfg)
+{
+ config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent);
+ git_config_entries *entries = NULL;
+ int error, modified;
+
+ if (cfg->readonly)
+ return 0;
+
+ if ((error = config_file_is_modified(&modified, &b->file)) < 0 && error != GIT_ENOTFOUND)
+ goto out;
+
+ if (!modified)
+ return 0;
+
+ config_file_clear_includes(b);
+
+ if ((error = git_config_entries_new(&entries)) < 0 ||
+ (error = config_file_read(entries, b->repo, &b->file, b->level, 0)) < 0 ||
+ (error = config_file_set_entries(cfg, entries)) < 0)
+ goto out;
+
+ entries = NULL;
+out:
+ git_config_entries_free(entries);
+
+ return (error == GIT_ENOTFOUND) ? 0 : error;
+}
+
+static void config_file_free(git_config_backend *_backend)
+{
+ config_file_backend *backend = GIT_CONTAINER_OF(_backend, config_file_backend, parent);
+
+ if (backend == NULL)
+ return;
+
+ config_file_clear(&backend->file);
+ git_config_entries_free(backend->entries);
+ git_mutex_free(&backend->values_mutex);
+ git__free(backend);
+}
+
+static int config_file_iterator(
+ git_config_iterator **iter,
+ struct git_config_backend *backend)
+{
+ config_file_backend *b = GIT_CONTAINER_OF(backend, config_file_backend, parent);
+ git_config_entries *dupped = NULL, *entries = NULL;
+ int error;
+
+ if ((error = config_file_refresh(backend)) < 0 ||
+ (error = config_file_entries_take(&entries, b)) < 0 ||
+ (error = git_config_entries_dup(&dupped, entries)) < 0 ||
+ (error = git_config_entries_iterator_new(iter, dupped)) < 0)
+ goto out;
+
+out:
+ /* Let iterator delete duplicated entries when it's done */
+ git_config_entries_free(entries);
+ git_config_entries_free(dupped);
+ return error;
+}
+
+static int config_file_snapshot(git_config_backend **out, git_config_backend *backend)
+{
+ return git_config_backend_snapshot(out, backend);
+}
+
+static int config_file_set(git_config_backend *cfg, const char *name, const char *value)
+{
+ config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent);
+ git_config_entries *entries;
+ git_config_entry *existing;
+ char *key, *esc_value = NULL;
+ int error;
+
+ if ((error = git_config__normalize_name(name, &key)) < 0)
+ return error;
+
+ if ((error = config_file_entries_take(&entries, b)) < 0)
+ return error;
+
+ /* Check whether we'd be modifying an included or multivar key */
+ if ((error = git_config_entries_get_unique(&existing, entries, key)) < 0) {
+ if (error != GIT_ENOTFOUND)
+ goto out;
+ error = 0;
+ } else if ((!existing->value && !value) ||
+ (existing->value && value && !strcmp(existing->value, value))) {
+ /* don't update if old and new values already match */
+ error = 0;
+ goto out;
+ }
+
+ /* No early returns due to sanity checks, let's write it out and refresh */
+ if (value) {
+ esc_value = escape_value(value);
+ GIT_ERROR_CHECK_ALLOC(esc_value);
+ }
+
+ if ((error = config_file_write(b, name, key, NULL, esc_value)) < 0)
+ goto out;
+
+out:
+ git_config_entries_free(entries);
+ git__free(esc_value);
+ git__free(key);
+ return error;
+}
+
+/* release the map containing the entry as an equivalent to freeing it */
+static void config_file_entry_free(git_config_entry *entry)
+{
+ git_config_entries *entries = (git_config_entries *) entry->payload;
+ git_config_entries_free(entries);
+}
+
+/*
+ * Internal function that actually gets the value in string form
+ */
+static int config_file_get(git_config_backend *cfg, const char *key, git_config_entry **out)
+{
+ config_file_backend *h = GIT_CONTAINER_OF(cfg, config_file_backend, parent);
+ git_config_entries *entries = NULL;
+ git_config_entry *entry;
+ int error = 0;
+
+ if (!h->parent.readonly && ((error = config_file_refresh(cfg)) < 0))
+ return error;
+
+ if ((error = config_file_entries_take(&entries, h)) < 0)
+ return error;
+
+ if ((error = (git_config_entries_get(&entry, entries, key))) < 0) {
+ git_config_entries_free(entries);
+ return error;
+ }
+
+ entry->free = config_file_entry_free;
+ entry->payload = entries;
+ *out = entry;
+
+ return 0;
+}
+
+static int config_file_set_multivar(
+ git_config_backend *cfg, const char *name, const char *regexp, const char *value)
+{
+ config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent);
+ git_regexp preg;
+ int result;
+ char *key;
+
+ GIT_ASSERT_ARG(regexp);
+
+ if ((result = git_config__normalize_name(name, &key)) < 0)
+ return result;
+
+ if ((result = git_regexp_compile(&preg, regexp, 0)) < 0)
+ goto out;
+
+ /* If we do have it, set call config_file_write() and reload */
+ if ((result = config_file_write(b, name, key, &preg, value)) < 0)
+ goto out;
+
+out:
+ git__free(key);
+ git_regexp_dispose(&preg);
+
+ return result;
+}
+
+static int config_file_delete(git_config_backend *cfg, const char *name)
+{
+ config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent);
+ git_config_entries *entries = NULL;
+ git_config_entry *entry;
+ char *key = NULL;
+ int error;
+
+ if ((error = git_config__normalize_name(name, &key)) < 0)
+ goto out;
+
+ if ((error = config_file_entries_take(&entries, b)) < 0)
+ goto out;
+
+ /* Check whether we'd be modifying an included or multivar key */
+ if ((error = git_config_entries_get_unique(&entry, entries, key)) < 0) {
+ if (error == GIT_ENOTFOUND)
+ git_error_set(GIT_ERROR_CONFIG, "could not find key '%s' to delete", name);
+ goto out;
+ }
+
+ if ((error = config_file_write(b, name, entry->name, NULL, NULL)) < 0)
+ goto out;
+
+out:
+ git_config_entries_free(entries);
+ git__free(key);
+ return error;
+}
+
+static int config_file_delete_multivar(git_config_backend *cfg, const char *name, const char *regexp)
+{
+ config_file_backend *b = GIT_CONTAINER_OF(cfg, config_file_backend, parent);
+ git_config_entries *entries = NULL;
+ git_config_entry *entry = NULL;
+ git_regexp preg = GIT_REGEX_INIT;
+ char *key = NULL;
+ int result;
+
+ if ((result = git_config__normalize_name(name, &key)) < 0)
+ goto out;
+
+ if ((result = config_file_entries_take(&entries, b)) < 0)
+ goto out;
+
+ if ((result = git_config_entries_get(&entry, entries, key)) < 0) {
+ if (result == GIT_ENOTFOUND)
+ git_error_set(GIT_ERROR_CONFIG, "could not find key '%s' to delete", name);
+ goto out;
+ }
+
+ if ((result = git_regexp_compile(&preg, regexp, 0)) < 0)
+ goto out;
+
+ if ((result = config_file_write(b, name, key, &preg, NULL)) < 0)
+ goto out;
+
+out:
+ git_config_entries_free(entries);
+ git__free(key);
+ git_regexp_dispose(&preg);
+ return result;
+}
+
+static int config_file_lock(git_config_backend *_cfg)
+{
+ config_file_backend *cfg = GIT_CONTAINER_OF(_cfg, config_file_backend, parent);
+ int error;
+
+ if ((error = git_filebuf_open(&cfg->locked_buf, cfg->file.path, 0, GIT_CONFIG_FILE_MODE)) < 0)
+ return error;
+
+ error = git_futils_readbuffer(&cfg->locked_content, cfg->file.path);
+ if (error < 0 && error != GIT_ENOTFOUND) {
+ git_filebuf_cleanup(&cfg->locked_buf);
+ return error;
+ }
+
+ cfg->locked = true;
+ return 0;
+
+}
+
+static int config_file_unlock(git_config_backend *_cfg, int success)
+{
+ config_file_backend *cfg = GIT_CONTAINER_OF(_cfg, config_file_backend, parent);
+ int error = 0;
+
+ if (success) {
+ git_filebuf_write(&cfg->locked_buf, cfg->locked_content.ptr, cfg->locked_content.size);
+ error = git_filebuf_commit(&cfg->locked_buf);
+ }
+
+ git_filebuf_cleanup(&cfg->locked_buf);
+ git_str_dispose(&cfg->locked_content);
+ cfg->locked = false;
+
+ return error;
+}
+
+int git_config_backend_from_file(git_config_backend **out, const char *path)
+{
+ config_file_backend *backend;
+
+ backend = git__calloc(1, sizeof(config_file_backend));
+ GIT_ERROR_CHECK_ALLOC(backend);
+
+ backend->parent.version = GIT_CONFIG_BACKEND_VERSION;
+ git_mutex_init(&backend->values_mutex);
+
+ backend->file.path = git__strdup(path);
+ GIT_ERROR_CHECK_ALLOC(backend->file.path);
+ git_array_init(backend->file.includes);
+
+ backend->parent.open = config_file_open;
+ backend->parent.get = config_file_get;
+ backend->parent.set = config_file_set;
+ backend->parent.set_multivar = config_file_set_multivar;
+ backend->parent.del = config_file_delete;
+ backend->parent.del_multivar = config_file_delete_multivar;
+ backend->parent.iterator = config_file_iterator;
+ backend->parent.snapshot = config_file_snapshot;
+ backend->parent.lock = config_file_lock;
+ backend->parent.unlock = config_file_unlock;
+ backend->parent.free = config_file_free;
+
+ *out = (git_config_backend *)backend;
+
+ return 0;
+}
+
+static int included_path(git_str *out, const char *dir, const char *path)
+{
+ /* From the user's home */
+ if (path[0] == '~' && path[1] == '/')
+ return git_sysdir_expand_homedir_file(out, &path[1]);
+
+ return git_fs_path_join_unrooted(out, path, dir, NULL);
+}
+
+/* Escape the values to write them to the file */
+static char *escape_value(const char *ptr)
+{
+ git_str buf;
+ size_t len;
+ const char *esc;
+
+ GIT_ASSERT_ARG_WITH_RETVAL(ptr, NULL);
+
+ len = strlen(ptr);
+ if (!len)
+ return git__calloc(1, sizeof(char));
+
+ if (git_str_init(&buf, len) < 0)
+ return NULL;
+
+ while (*ptr != '\0') {
+ if ((esc = strchr(git_config_escaped, *ptr)) != NULL) {
+ git_str_putc(&buf, '\\');
+ git_str_putc(&buf, git_config_escapes[esc - git_config_escaped]);
+ } else {
+ git_str_putc(&buf, *ptr);
+ }
+ ptr++;
+ }
+
+ if (git_str_oom(&buf))
+ return NULL;
+
+ return git_str_detach(&buf);
+}
+
+static int parse_include(config_file_parse_data *parse_data, const char *file)
+{
+ config_file *include;
+ git_str path = GIT_STR_INIT;
+ char *dir;
+ int result;
+
+ if (!file)
+ return 0;
+
+ if ((result = git_fs_path_dirname_r(&path, parse_data->file->path)) < 0)
+ return result;
+
+ dir = git_str_detach(&path);
+ result = included_path(&path, dir, file);
+ git__free(dir);
+
+ if (result < 0)
+ return result;
+
+ include = git_array_alloc(parse_data->file->includes);
+ GIT_ERROR_CHECK_ALLOC(include);
+ memset(include, 0, sizeof(*include));
+ git_array_init(include->includes);
+ include->path = git_str_detach(&path);
+
+ result = config_file_read(parse_data->entries, parse_data->repo, include,
+ parse_data->level, parse_data->depth+1);
+
+ if (result == GIT_ENOTFOUND) {
+ git_error_clear();
+ result = 0;
+ }
+
+ return result;
+}
+
+static int do_match_gitdir(
+ int *matches,
+ const git_repository *repo,
+ const char *cfg_file,
+ const char *condition,
+ bool case_insensitive)
+{
+ git_str pattern = GIT_STR_INIT, gitdir = GIT_STR_INIT;
+ int error;
+
+ if (condition[0] == '.' && git_fs_path_is_dirsep(condition[1])) {
+ git_fs_path_dirname_r(&pattern, cfg_file);
+ git_str_joinpath(&pattern, pattern.ptr, condition + 2);
+ } else if (condition[0] == '~' && git_fs_path_is_dirsep(condition[1]))
+ git_sysdir_expand_homedir_file(&pattern, condition + 1);
+ else if (!git_fs_path_is_absolute(condition))
+ git_str_joinpath(&pattern, "**", condition);
+ else
+ git_str_sets(&pattern, condition);
+
+ if (git_fs_path_is_dirsep(condition[strlen(condition) - 1]))
+ git_str_puts(&pattern, "**");
+
+ if (git_str_oom(&pattern)) {
+ error = -1;
+ goto out;
+ }
+
+ if ((error = git_repository__item_path(&gitdir, repo, GIT_REPOSITORY_ITEM_GITDIR)) < 0)
+ goto out;
+
+ if (git_fs_path_is_dirsep(gitdir.ptr[gitdir.size - 1]))
+ git_str_truncate(&gitdir, gitdir.size - 1);
+
+ *matches = wildmatch(pattern.ptr, gitdir.ptr,
+ WM_PATHNAME | (case_insensitive ? WM_CASEFOLD : 0)) == WM_MATCH;
+out:
+ git_str_dispose(&pattern);
+ git_str_dispose(&gitdir);
+ return error;
+}
+
+static int conditional_match_gitdir(
+ int *matches,
+ const git_repository *repo,
+ const char *cfg_file,
+ const char *value)
+{
+ return do_match_gitdir(matches, repo, cfg_file, value, false);
+}
+
+static int conditional_match_gitdir_i(
+ int *matches,
+ const git_repository *repo,
+ const char *cfg_file,
+ const char *value)
+{
+ return do_match_gitdir(matches, repo, cfg_file, value, true);
+}
+
+static int conditional_match_onbranch(
+ int *matches,
+ const git_repository *repo,
+ const char *cfg_file,
+ const char *condition)
+{
+ git_str reference = GIT_STR_INIT, buf = GIT_STR_INIT;
+ int error;
+
+ GIT_UNUSED(cfg_file);
+
+ /*
+ * NOTE: you cannot use `git_repository_head` here. Looking up the
+ * HEAD reference will create the ODB, which causes us to read the
+ * repo's config for keys like core.precomposeUnicode. As we're
+ * just parsing the config right now, though, this would result in
+ * an endless recursion.
+ */
+
+ if ((error = git_str_joinpath(&buf, git_repository_path(repo), GIT_HEAD_FILE)) < 0 ||
+ (error = git_futils_readbuffer(&reference, buf.ptr)) < 0)
+ goto out;
+ git_str_rtrim(&reference);
+
+ if (git__strncmp(reference.ptr, GIT_SYMREF, strlen(GIT_SYMREF)))
+ goto out;
+ git_str_consume(&reference, reference.ptr + strlen(GIT_SYMREF));
+
+ if (git__strncmp(reference.ptr, GIT_REFS_HEADS_DIR, strlen(GIT_REFS_HEADS_DIR)))
+ goto out;
+ git_str_consume(&reference, reference.ptr + strlen(GIT_REFS_HEADS_DIR));
+
+ /*
+ * If the condition ends with a '/', then we should treat it as if
+ * it had '**' appended.
+ */
+ if ((error = git_str_sets(&buf, condition)) < 0)
+ goto out;
+ if (git_fs_path_is_dirsep(condition[strlen(condition) - 1]) &&
+ (error = git_str_puts(&buf, "**")) < 0)
+ goto out;
+
+ *matches = wildmatch(buf.ptr, reference.ptr, WM_PATHNAME) == WM_MATCH;
+out:
+ git_str_dispose(&reference);
+ git_str_dispose(&buf);
+
+ return error;
+
+}
+
+static const struct {
+ const char *prefix;
+ int (*matches)(int *matches, const git_repository *repo, const char *cfg, const char *value);
+} conditions[] = {
+ { "gitdir:", conditional_match_gitdir },
+ { "gitdir/i:", conditional_match_gitdir_i },
+ { "onbranch:", conditional_match_onbranch }
+};
+
+static int parse_conditional_include(config_file_parse_data *parse_data, const char *section, const char *file)
+{
+ char *condition;
+ size_t section_len, i;
+ int error = 0, matches;
+
+ if (!parse_data->repo || !file)
+ return 0;
+
+ section_len = strlen(section);
+
+ /*
+ * We checked that the string starts with `includeIf.` and ends
+ * in `.path` to get here. Make sure it consists of more.
+ */
+ if (section_len < CONST_STRLEN("includeIf.") + CONST_STRLEN(".path"))
+ return 0;
+
+ condition = git__substrdup(section + CONST_STRLEN("includeIf."),
+ section_len - CONST_STRLEN("includeIf.") - CONST_STRLEN(".path"));
+
+ GIT_ERROR_CHECK_ALLOC(condition);
+
+ for (i = 0; i < ARRAY_SIZE(conditions); i++) {
+ if (git__prefixcmp(condition, conditions[i].prefix))
+ continue;
+
+ if ((error = conditions[i].matches(&matches,
+ parse_data->repo,
+ parse_data->file->path,
+ condition + strlen(conditions[i].prefix))) < 0)
+ break;
+
+ if (matches)
+ error = parse_include(parse_data, file);
+
+ break;
+ }
+
+ git__free(condition);
+ return error;
+}
+
+static int read_on_variable(
+ git_config_parser *reader,
+ const char *current_section,
+ const char *var_name,
+ const char *var_value,
+ const char *line,
+ size_t line_len,
+ void *data)
+{
+ config_file_parse_data *parse_data = (config_file_parse_data *)data;
+ git_str buf = GIT_STR_INIT;
+ git_config_entry *entry;
+ const char *c;
+ int result = 0;
+
+ GIT_UNUSED(reader);
+ GIT_UNUSED(line);
+ GIT_UNUSED(line_len);
+
+ if (current_section) {
+ /* TODO: Once warnings lang, we should likely warn
+ * here. Git appears to warn in most cases if it sees
+ * un-namespaced config options.
+ */
+ git_str_puts(&buf, current_section);
+ git_str_putc(&buf, '.');
+ }
+
+ for (c = var_name; *c; c++)
+ git_str_putc(&buf, git__tolower(*c));
+
+ if (git_str_oom(&buf))
+ return -1;
+
+ entry = git__calloc(1, sizeof(git_config_entry));
+ GIT_ERROR_CHECK_ALLOC(entry);
+ entry->name = git_str_detach(&buf);
+ entry->value = var_value ? git__strdup(var_value) : NULL;
+ entry->level = parse_data->level;
+ entry->include_depth = parse_data->depth;
+
+ if ((result = git_config_entries_append(parse_data->entries, entry)) < 0)
+ return result;
+
+ result = 0;
+
+ /* Add or append the new config option */
+ if (!git__strcmp(entry->name, "include.path"))
+ result = parse_include(parse_data, entry->value);
+ else if (!git__prefixcmp(entry->name, "includeif.") &&
+ !git__suffixcmp(entry->name, ".path"))
+ result = parse_conditional_include(parse_data, entry->name, entry->value);
+
+ return result;
+}
+
+static int config_file_read_buffer(
+ git_config_entries *entries,
+ const git_repository *repo,
+ config_file *file,
+ git_config_level_t level,
+ int depth,
+ const char *buf,
+ size_t buflen)
+{
+ config_file_parse_data parse_data;
+ git_config_parser reader;
+ int error;
+
+ if (depth >= MAX_INCLUDE_DEPTH) {
+ git_error_set(GIT_ERROR_CONFIG, "maximum config include depth reached");
+ return -1;
+ }
+
+ /* Initialize the reading position */
+ reader.path = file->path;
+ git_parse_ctx_init(&reader.ctx, buf, buflen);
+
+ /* If the file is empty, there's nothing for us to do */
+ if (!reader.ctx.content || *reader.ctx.content == '\0') {
+ error = 0;
+ goto out;
+ }
+
+ parse_data.repo = repo;
+ parse_data.file = file;
+ parse_data.entries = entries;
+ parse_data.level = level;
+ parse_data.depth = depth;
+
+ error = git_config_parse(&reader, NULL, read_on_variable, NULL, NULL, &parse_data);
+
+out:
+ return error;
+}
+
+static int config_file_read(
+ git_config_entries *entries,
+ const git_repository *repo,
+ config_file *file,
+ git_config_level_t level,
+ int depth)
+{
+ git_str contents = GIT_STR_INIT;
+ struct stat st;
+ int error;
+
+ if (p_stat(file->path, &st) < 0) {
+ error = git_fs_path_set_error(errno, file->path, "stat");
+ goto out;
+ }
+
+ if ((error = git_futils_readbuffer(&contents, file->path)) < 0)
+ goto out;
+
+ git_futils_filestamp_set_from_stat(&file->stamp, &st);
+ if ((error = git_hash_buf(file->checksum, contents.ptr, contents.size, GIT_HASH_ALGORITHM_SHA256)) < 0)
+ goto out;
+
+ if ((error = config_file_read_buffer(entries, repo, file, level, depth,
+ contents.ptr, contents.size)) < 0)
+ goto out;
+
+out:
+ git_str_dispose(&contents);
+ return error;
+}
+
+static int write_section(git_str *fbuf, const char *key)
+{
+ int result;
+ const char *dot;
+ git_str buf = GIT_STR_INIT;
+
+ /* All of this just for [section "subsection"] */
+ dot = strchr(key, '.');
+ git_str_putc(&buf, '[');
+ if (dot == NULL) {
+ git_str_puts(&buf, key);
+ } else {
+ char *escaped;
+ git_str_put(&buf, key, dot - key);
+ escaped = escape_value(dot + 1);
+ GIT_ERROR_CHECK_ALLOC(escaped);
+ git_str_printf(&buf, " \"%s\"", escaped);
+ git__free(escaped);
+ }
+ git_str_puts(&buf, "]\n");
+
+ if (git_str_oom(&buf))
+ return -1;
+
+ result = git_str_put(fbuf, git_str_cstr(&buf), buf.size);
+ git_str_dispose(&buf);
+
+ return result;
+}
+
+static const char *quotes_for_value(const char *value)
+{
+ const char *ptr;
+
+ if (value[0] == ' ' || value[0] == '\0')
+ return "\"";
+
+ for (ptr = value; *ptr; ++ptr) {
+ if (*ptr == ';' || *ptr == '#')
+ return "\"";
+ }
+
+ if (ptr[-1] == ' ')
+ return "\"";
+
+ return "";
+}
+
+struct write_data {
+ git_str *buf;
+ git_str buffered_comment;
+ unsigned int in_section : 1,
+ preg_replaced : 1;
+ const char *orig_section;
+ const char *section;
+ const char *orig_name;
+ const char *name;
+ const git_regexp *preg;
+ const char *value;
+};
+
+static int write_line_to(git_str *buf, const char *line, size_t line_len)
+{
+ int result = git_str_put(buf, line, line_len);
+
+ if (!result && line_len && line[line_len-1] != '\n')
+ result = git_str_printf(buf, "\n");
+
+ return result;
+}
+
+static int write_line(struct write_data *write_data, const char *line, size_t line_len)
+{
+ return write_line_to(write_data->buf, line, line_len);
+}
+
+static int write_value(struct write_data *write_data)
+{
+ const char *q;
+ int result;
+
+ q = quotes_for_value(write_data->value);
+ result = git_str_printf(write_data->buf,
+ "\t%s = %s%s%s\n", write_data->orig_name, q, write_data->value, q);
+
+ /* If we are updating a single name/value, we're done. Setting `value`
+ * to `NULL` will prevent us from trying to write it again later (in
+ * `write_on_section`) if we see the same section repeated.
+ */
+ if (!write_data->preg)
+ write_data->value = NULL;
+
+ return result;
+}
+
+static int write_on_section(
+ git_config_parser *reader,
+ const char *current_section,
+ const char *line,
+ size_t line_len,
+ void *data)
+{
+ struct write_data *write_data = (struct write_data *)data;
+ int result = 0;
+
+ GIT_UNUSED(reader);
+
+ /* If we were previously in the correct section (but aren't anymore)
+ * and haven't written our value (for a simple name/value set, not
+ * a multivar), then append it to the end of the section before writing
+ * the new one.
+ */
+ if (write_data->in_section && !write_data->preg && write_data->value)
+ result = write_value(write_data);
+
+ write_data->in_section = strcmp(current_section, write_data->section) == 0;
+
+ /*
+ * If there were comments just before this section, dump them as well.
+ */
+ if (!result) {
+ result = git_str_put(write_data->buf, write_data->buffered_comment.ptr, write_data->buffered_comment.size);
+ git_str_clear(&write_data->buffered_comment);
+ }
+
+ if (!result)
+ result = write_line(write_data, line, line_len);
+
+ return result;
+}
+
+static int write_on_variable(
+ git_config_parser *reader,
+ const char *current_section,
+ const char *var_name,
+ const char *var_value,
+ const char *line,
+ size_t line_len,
+ void *data)
+{
+ struct write_data *write_data = (struct write_data *)data;
+ bool has_matched = false;
+ int error;
+
+ GIT_UNUSED(reader);
+ GIT_UNUSED(current_section);
+
+ /*
+ * If there were comments just before this variable, let's dump them as well.
+ */
+ if ((error = git_str_put(write_data->buf, write_data->buffered_comment.ptr, write_data->buffered_comment.size)) < 0)
+ return error;
+
+ git_str_clear(&write_data->buffered_comment);
+
+ /* See if we are to update this name/value pair; first examine name */
+ if (write_data->in_section &&
+ strcasecmp(write_data->name, var_name) == 0)
+ has_matched = true;
+
+ /* If we have a regex to match the value, see if it matches */
+ if (has_matched && write_data->preg != NULL)
+ has_matched = (git_regexp_match(write_data->preg, var_value) == 0);
+
+ /* If this isn't the name/value we're looking for, simply dump the
+ * existing data back out and continue on.
+ */
+ if (!has_matched)
+ return write_line(write_data, line, line_len);
+
+ write_data->preg_replaced = 1;
+
+ /* If value is NULL, we are deleting this value; write nothing. */
+ if (!write_data->value)
+ return 0;
+
+ return write_value(write_data);
+}
+
+static int write_on_comment(git_config_parser *reader, const char *line, size_t line_len, void *data)
+{
+ struct write_data *write_data;
+
+ GIT_UNUSED(reader);
+
+ write_data = (struct write_data *)data;
+ return write_line_to(&write_data->buffered_comment, line, line_len);
+}
+
+static int write_on_eof(
+ git_config_parser *reader, const char *current_section, void *data)
+{
+ struct write_data *write_data = (struct write_data *)data;
+ int result = 0;
+
+ GIT_UNUSED(reader);
+
+ /*
+ * If we've buffered comments when reaching EOF, make sure to dump them.
+ */
+ if ((result = git_str_put(write_data->buf, write_data->buffered_comment.ptr, write_data->buffered_comment.size)) < 0)
+ return result;
+
+ /* If we are at the EOF and have not written our value (again, for a
+ * simple name/value set, not a multivar) then we have never seen the
+ * section in question and should create a new section and write the
+ * value.
+ */
+ if ((!write_data->preg || !write_data->preg_replaced) && write_data->value) {
+ /* write the section header unless we're already in it */
+ if (!current_section || strcmp(current_section, write_data->section))
+ result = write_section(write_data->buf, write_data->orig_section);
+
+ if (!result)
+ result = write_value(write_data);
+ }
+
+ return result;
+}
+
+/*
+ * This is pretty much the parsing, except we write out anything we don't have
+ */
+static int config_file_write(
+ config_file_backend *cfg,
+ const char *orig_key,
+ const char *key,
+ const git_regexp *preg,
+ const char *value)
+
+{
+ char *orig_section = NULL, *section = NULL, *orig_name, *name, *ldot;
+ git_str buf = GIT_STR_INIT, contents = GIT_STR_INIT;
+ git_config_parser parser = GIT_CONFIG_PARSER_INIT;
+ git_filebuf file = GIT_FILEBUF_INIT;
+ struct write_data write_data;
+ int error;
+
+ memset(&write_data, 0, sizeof(write_data));
+
+ if (cfg->locked) {
+ error = git_str_puts(&contents, git_str_cstr(&cfg->locked_content) == NULL ? "" : git_str_cstr(&cfg->locked_content));
+ } else {
+ if ((error = git_filebuf_open(&file, cfg->file.path,
+ GIT_FILEBUF_HASH_SHA256,
+ GIT_CONFIG_FILE_MODE)) < 0)
+ goto done;
+
+ /* We need to read in our own config file */
+ error = git_futils_readbuffer(&contents, cfg->file.path);
+ }
+ if (error < 0 && error != GIT_ENOTFOUND)
+ goto done;
+
+ if ((git_config_parser_init(&parser, cfg->file.path, contents.ptr, contents.size)) < 0)
+ goto done;
+
+ ldot = strrchr(key, '.');
+ name = ldot + 1;
+ section = git__strndup(key, ldot - key);
+ GIT_ERROR_CHECK_ALLOC(section);
+
+ ldot = strrchr(orig_key, '.');
+ orig_name = ldot + 1;
+ orig_section = git__strndup(orig_key, ldot - orig_key);
+ GIT_ERROR_CHECK_ALLOC(orig_section);
+
+ write_data.buf = &buf;
+ write_data.orig_section = orig_section;
+ write_data.section = section;
+ write_data.orig_name = orig_name;
+ write_data.name = name;
+ write_data.preg = preg;
+ write_data.value = value;
+
+ if ((error = git_config_parse(&parser, write_on_section, write_on_variable,
+ write_on_comment, write_on_eof, &write_data)) < 0)
+ goto done;
+
+ if (cfg->locked) {
+ size_t len = buf.asize;
+ /* Update our copy with the modified contents */
+ git_str_dispose(&cfg->locked_content);
+ git_str_attach(&cfg->locked_content, git_str_detach(&buf), len);
+ } else {
+ git_filebuf_write(&file, git_str_cstr(&buf), git_str_len(&buf));
+
+ if ((error = git_filebuf_commit(&file)) < 0)
+ goto done;
+
+ if ((error = config_file_refresh_from_buffer(&cfg->parent, buf.ptr, buf.size)) < 0)
+ goto done;
+ }
+
+done:
+ git__free(section);
+ git__free(orig_section);
+ git_str_dispose(&write_data.buffered_comment);
+ git_str_dispose(&buf);
+ git_str_dispose(&contents);
+ git_filebuf_cleanup(&file);
+ git_config_parser_dispose(&parser);
+
+ return error;
+}
diff --git a/src/libgit2/config_mem.c b/src/libgit2/config_mem.c
new file mode 100644
index 0000000..560229c
--- /dev/null
+++ b/src/libgit2/config_mem.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "config.h"
+
+#include "config_backend.h"
+#include "config_parse.h"
+#include "config_entries.h"
+
+typedef struct {
+ git_config_backend parent;
+ git_config_entries *entries;
+ git_str cfg;
+} config_memory_backend;
+
+typedef struct {
+ git_config_entries *entries;
+ git_config_level_t level;
+} config_memory_parse_data;
+
+static int config_error_readonly(void)
+{
+ git_error_set(GIT_ERROR_CONFIG, "this backend is read-only");
+ return -1;
+}
+
+static int read_variable_cb(
+ git_config_parser *reader,
+ const char *current_section,
+ const char *var_name,
+ const char *var_value,
+ const char *line,
+ size_t line_len,
+ void *payload)
+{
+ config_memory_parse_data *parse_data = (config_memory_parse_data *) payload;
+ git_str buf = GIT_STR_INIT;
+ git_config_entry *entry;
+ const char *c;
+ int result;
+
+ GIT_UNUSED(reader);
+ GIT_UNUSED(line);
+ GIT_UNUSED(line_len);
+
+ if (current_section) {
+ /* TODO: Once warnings land, we should likely warn
+ * here. Git appears to warn in most cases if it sees
+ * un-namespaced config options.
+ */
+ git_str_puts(&buf, current_section);
+ git_str_putc(&buf, '.');
+ }
+
+ for (c = var_name; *c; c++)
+ git_str_putc(&buf, git__tolower(*c));
+
+ if (git_str_oom(&buf))
+ return -1;
+
+ entry = git__calloc(1, sizeof(git_config_entry));
+ GIT_ERROR_CHECK_ALLOC(entry);
+ entry->name = git_str_detach(&buf);
+ entry->value = var_value ? git__strdup(var_value) : NULL;
+ entry->level = parse_data->level;
+ entry->include_depth = 0;
+
+ if ((result = git_config_entries_append(parse_data->entries, entry)) < 0)
+ return result;
+
+ return result;
+}
+
+static int config_memory_open(git_config_backend *backend, git_config_level_t level, const git_repository *repo)
+{
+ config_memory_backend *memory_backend = (config_memory_backend *) backend;
+ git_config_parser parser = GIT_PARSE_CTX_INIT;
+ config_memory_parse_data parse_data;
+ int error;
+
+ GIT_UNUSED(repo);
+
+ if ((error = git_config_parser_init(&parser, "in-memory", memory_backend->cfg.ptr,
+ memory_backend->cfg.size)) < 0)
+ goto out;
+ parse_data.entries = memory_backend->entries;
+ parse_data.level = level;
+
+ if ((error = git_config_parse(&parser, NULL, read_variable_cb, NULL, NULL, &parse_data)) < 0)
+ goto out;
+
+out:
+ git_config_parser_dispose(&parser);
+ return error;
+}
+
+static int config_memory_get(git_config_backend *backend, const char *key, git_config_entry **out)
+{
+ config_memory_backend *memory_backend = (config_memory_backend *) backend;
+ return git_config_entries_get(out, memory_backend->entries, key);
+}
+
+static int config_memory_iterator(
+ git_config_iterator **iter,
+ git_config_backend *backend)
+{
+ config_memory_backend *memory_backend = (config_memory_backend *) backend;
+ git_config_entries *entries;
+ int error;
+
+ if ((error = git_config_entries_dup(&entries, memory_backend->entries)) < 0)
+ goto out;
+
+ if ((error = git_config_entries_iterator_new(iter, entries)) < 0)
+ goto out;
+
+out:
+ /* Let iterator delete duplicated entries when it's done */
+ git_config_entries_free(entries);
+ return error;
+}
+
+static int config_memory_set(git_config_backend *backend, const char *name, const char *value)
+{
+ GIT_UNUSED(backend);
+ GIT_UNUSED(name);
+ GIT_UNUSED(value);
+ return config_error_readonly();
+}
+
+static int config_memory_set_multivar(
+ git_config_backend *backend, const char *name, const char *regexp, const char *value)
+{
+ GIT_UNUSED(backend);
+ GIT_UNUSED(name);
+ GIT_UNUSED(regexp);
+ GIT_UNUSED(value);
+ return config_error_readonly();
+}
+
+static int config_memory_delete(git_config_backend *backend, const char *name)
+{
+ GIT_UNUSED(backend);
+ GIT_UNUSED(name);
+ return config_error_readonly();
+}
+
+static int config_memory_delete_multivar(git_config_backend *backend, const char *name, const char *regexp)
+{
+ GIT_UNUSED(backend);
+ GIT_UNUSED(name);
+ GIT_UNUSED(regexp);
+ return config_error_readonly();
+}
+
+static int config_memory_lock(git_config_backend *backend)
+{
+ GIT_UNUSED(backend);
+ return config_error_readonly();
+}
+
+static int config_memory_unlock(git_config_backend *backend, int success)
+{
+ GIT_UNUSED(backend);
+ GIT_UNUSED(success);
+ return config_error_readonly();
+}
+
+static void config_memory_free(git_config_backend *_backend)
+{
+ config_memory_backend *backend = (config_memory_backend *)_backend;
+
+ if (backend == NULL)
+ return;
+
+ git_config_entries_free(backend->entries);
+ git_str_dispose(&backend->cfg);
+ git__free(backend);
+}
+
+int git_config_backend_from_string(git_config_backend **out, const char *cfg, size_t len)
+{
+ config_memory_backend *backend;
+
+ backend = git__calloc(1, sizeof(config_memory_backend));
+ GIT_ERROR_CHECK_ALLOC(backend);
+
+ if (git_config_entries_new(&backend->entries) < 0) {
+ git__free(backend);
+ return -1;
+ }
+
+ if (git_str_set(&backend->cfg, cfg, len) < 0) {
+ git_config_entries_free(backend->entries);
+ git__free(backend);
+ return -1;
+ }
+
+ backend->parent.version = GIT_CONFIG_BACKEND_VERSION;
+ backend->parent.readonly = 1;
+ backend->parent.open = config_memory_open;
+ backend->parent.get = config_memory_get;
+ backend->parent.set = config_memory_set;
+ backend->parent.set_multivar = config_memory_set_multivar;
+ backend->parent.del = config_memory_delete;
+ backend->parent.del_multivar = config_memory_delete_multivar;
+ backend->parent.iterator = config_memory_iterator;
+ backend->parent.lock = config_memory_lock;
+ backend->parent.unlock = config_memory_unlock;
+ backend->parent.snapshot = git_config_backend_snapshot;
+ backend->parent.free = config_memory_free;
+
+ *out = (git_config_backend *)backend;
+
+ return 0;
+}
diff --git a/src/libgit2/config_parse.c b/src/libgit2/config_parse.c
new file mode 100644
index 0000000..0693136
--- /dev/null
+++ b/src/libgit2/config_parse.c
@@ -0,0 +1,580 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "config_parse.h"
+
+#include <ctype.h>
+
+const char *git_config_escapes = "ntb\"\\";
+const char *git_config_escaped = "\n\t\b\"\\";
+
+static void set_parse_error(git_config_parser *reader, int col, const char *error_str)
+{
+ if (col)
+ git_error_set(GIT_ERROR_CONFIG,
+ "failed to parse config file: %s (in %s:%"PRIuZ", column %d)",
+ error_str, reader->path, reader->ctx.line_num, col);
+ else
+ git_error_set(GIT_ERROR_CONFIG,
+ "failed to parse config file: %s (in %s:%"PRIuZ")",
+ error_str, reader->path, reader->ctx.line_num);
+}
+
+
+GIT_INLINE(int) config_keychar(int c)
+{
+ return isalnum(c) || c == '-';
+}
+
+static int strip_comments(char *line, int in_quotes)
+{
+ int quote_count = in_quotes, backslash_count = 0;
+ char *ptr;
+
+ for (ptr = line; *ptr; ++ptr) {
+ if (ptr[0] == '"' && ((ptr > line && ptr[-1] != '\\') || ptr == line))
+ quote_count++;
+
+ if ((ptr[0] == ';' || ptr[0] == '#') &&
+ (quote_count % 2) == 0 &&
+ (backslash_count % 2) == 0) {
+ ptr[0] = '\0';
+ break;
+ }
+
+ if (ptr[0] == '\\')
+ backslash_count++;
+ else
+ backslash_count = 0;
+ }
+
+ /* skip any space at the end */
+ while (ptr > line && git__isspace(ptr[-1])) {
+ ptr--;
+ }
+ ptr[0] = '\0';
+
+ return quote_count;
+}
+
+
+static int parse_subsection_header(git_config_parser *reader, const char *line, size_t pos, const char *base_name, char **section_name)
+{
+ int c, rpos;
+ const char *first_quote, *last_quote;
+ const char *line_start = line;
+ git_str buf = GIT_STR_INIT;
+ size_t quoted_len, alloc_len, base_name_len = strlen(base_name);
+
+ /* Skip any additional whitespace before our section name */
+ while (git__isspace(line[pos]))
+ pos++;
+
+ /* We should be at the first quotation mark. */
+ if (line[pos] != '"') {
+ set_parse_error(reader, 0, "missing quotation marks in section header");
+ goto end_error;
+ }
+
+ first_quote = &line[pos];
+ last_quote = strrchr(line, '"');
+ quoted_len = last_quote - first_quote;
+
+ if ((last_quote - line) > INT_MAX) {
+ set_parse_error(reader, 0, "invalid section header, line too long");
+ goto end_error;
+ }
+
+ if (quoted_len == 0) {
+ set_parse_error(reader, 0, "missing closing quotation mark in section header");
+ goto end_error;
+ }
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, base_name_len, quoted_len);
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2);
+
+ if (git_str_grow(&buf, alloc_len) < 0 ||
+ git_str_printf(&buf, "%s.", base_name) < 0)
+ goto end_error;
+
+ rpos = 0;
+
+ line = first_quote;
+ c = line[++rpos];
+
+ /*
+ * At the end of each iteration, whatever is stored in c will be
+ * added to the string. In case of error, jump to out
+ */
+ do {
+
+ switch (c) {
+ case 0:
+ set_parse_error(reader, 0, "unexpected end-of-line in section header");
+ goto end_error;
+
+ case '"':
+ goto end_parse;
+
+ case '\\':
+ c = line[++rpos];
+
+ if (c == 0) {
+ set_parse_error(reader, rpos, "unexpected end-of-line in section header");
+ goto end_error;
+ }
+
+ default:
+ break;
+ }
+
+ git_str_putc(&buf, (char)c);
+ c = line[++rpos];
+ } while (line + rpos < last_quote);
+
+end_parse:
+ if (git_str_oom(&buf))
+ goto end_error;
+
+ if (line[rpos] != '"' || line[rpos + 1] != ']') {
+ set_parse_error(reader, rpos, "unexpected text after closing quotes");
+ git_str_dispose(&buf);
+ return -1;
+ }
+
+ *section_name = git_str_detach(&buf);
+ return (int)(&line[rpos + 2] - line_start); /* rpos is at the closing quote */
+
+end_error:
+ git_str_dispose(&buf);
+
+ return -1;
+}
+
+static int parse_section_header(git_config_parser *reader, char **section_out)
+{
+ char *name, *name_end;
+ int name_length, c, pos;
+ int result;
+ char *line;
+ size_t line_len;
+
+ git_parse_advance_ws(&reader->ctx);
+ line = git__strndup(reader->ctx.line, reader->ctx.line_len);
+ if (line == NULL)
+ return -1;
+
+ /* find the end of the variable's name */
+ name_end = strrchr(line, ']');
+ if (name_end == NULL) {
+ git__free(line);
+ set_parse_error(reader, 0, "missing ']' in section header");
+ return -1;
+ }
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&line_len, (size_t)(name_end - line), 1);
+ name = git__malloc(line_len);
+ GIT_ERROR_CHECK_ALLOC(name);
+
+ name_length = 0;
+ pos = 0;
+
+ /* Make sure we were given a section header */
+ c = line[pos++];
+ GIT_ASSERT(c == '[');
+
+ c = line[pos++];
+
+ do {
+ if (git__isspace(c)){
+ name[name_length] = '\0';
+ result = parse_subsection_header(reader, line, pos, name, section_out);
+ git__free(line);
+ git__free(name);
+ return result;
+ }
+
+ if (!config_keychar(c) && c != '.') {
+ set_parse_error(reader, pos, "unexpected character in header");
+ goto fail_parse;
+ }
+
+ name[name_length++] = (char)git__tolower(c);
+
+ } while ((c = line[pos++]) != ']');
+
+ if (line[pos - 1] != ']') {
+ set_parse_error(reader, pos, "unexpected end of file");
+ goto fail_parse;
+ }
+
+ git__free(line);
+
+ name[name_length] = 0;
+ *section_out = name;
+
+ return pos;
+
+fail_parse:
+ git__free(line);
+ git__free(name);
+ return -1;
+}
+
+static int skip_bom(git_parse_ctx *parser)
+{
+ git_str buf = GIT_STR_INIT_CONST(parser->content, parser->content_len);
+ git_str_bom_t bom;
+ int bom_offset = git_str_detect_bom(&bom, &buf);
+
+ if (bom == GIT_STR_BOM_UTF8)
+ git_parse_advance_chars(parser, bom_offset);
+
+ /* TODO: reference implementation is pretty stupid with BoM */
+
+ return 0;
+}
+
+/*
+ (* basic types *)
+ digit = "0".."9"
+ integer = digit { digit }
+ alphabet = "a".."z" + "A" .. "Z"
+
+ section_char = alphabet | "." | "-"
+ extension_char = (* any character except newline *)
+ any_char = (* any character *)
+ variable_char = "alphabet" | "-"
+
+
+ (* actual grammar *)
+ config = { section }
+
+ section = header { definition }
+
+ header = "[" section [subsection | subsection_ext] "]"
+
+ subsection = "." section
+ subsection_ext = "\"" extension "\""
+
+ section = section_char { section_char }
+ extension = extension_char { extension_char }
+
+ definition = variable_name ["=" variable_value] "\n"
+
+ variable_name = variable_char { variable_char }
+ variable_value = string | boolean | integer
+
+ string = quoted_string | plain_string
+ quoted_string = "\"" plain_string "\""
+ plain_string = { any_char }
+
+ boolean = boolean_true | boolean_false
+ boolean_true = "yes" | "1" | "true" | "on"
+ boolean_false = "no" | "0" | "false" | "off"
+*/
+
+/* '\"' -> '"' etc */
+static int unescape_line(
+ char **out, bool *is_multi, const char *ptr, int quote_count)
+{
+ char *str, *fixed, *esc;
+ size_t ptr_len = strlen(ptr), alloc_len;
+
+ *is_multi = false;
+
+ if (GIT_ADD_SIZET_OVERFLOW(&alloc_len, ptr_len, 1) ||
+ (str = git__malloc(alloc_len)) == NULL) {
+ return -1;
+ }
+
+ fixed = str;
+
+ while (*ptr != '\0') {
+ if (*ptr == '"') {
+ quote_count++;
+ } else if (*ptr != '\\') {
+ *fixed++ = *ptr;
+ } else {
+ /* backslash, check the next char */
+ ptr++;
+ /* if we're at the end, it's a multiline, so keep the backslash */
+ if (*ptr == '\0') {
+ *is_multi = true;
+ goto done;
+ }
+ if ((esc = strchr(git_config_escapes, *ptr)) != NULL) {
+ *fixed++ = git_config_escaped[esc - git_config_escapes];
+ } else {
+ git__free(str);
+ git_error_set(GIT_ERROR_CONFIG, "invalid escape at %s", ptr);
+ return -1;
+ }
+ }
+ ptr++;
+ }
+
+done:
+ *fixed = '\0';
+ *out = str;
+
+ return 0;
+}
+
+static int parse_multiline_variable(git_config_parser *reader, git_str *value, int in_quotes, size_t *line_len)
+{
+ int quote_count;
+ bool multiline = true;
+
+ while (multiline) {
+ char *line = NULL, *proc_line = NULL;
+ int error;
+
+ /* Check that the next line exists */
+ git_parse_advance_line(&reader->ctx);
+ line = git__strndup(reader->ctx.line, reader->ctx.line_len);
+ GIT_ERROR_CHECK_ALLOC(line);
+ if (GIT_ADD_SIZET_OVERFLOW(line_len, *line_len, reader->ctx.line_len)) {
+ error = -1;
+ goto out;
+ }
+
+ /*
+ * We've reached the end of the file, there is no continuation.
+ * (this is not an error).
+ */
+ if (line[0] == '\0') {
+ error = 0;
+ goto out;
+ }
+
+ /* If it was just a comment, pretend it didn't exist */
+ quote_count = strip_comments(line, in_quotes);
+ if (line[0] == '\0')
+ goto next;
+
+ if ((error = unescape_line(&proc_line, &multiline,
+ line, in_quotes)) < 0)
+ goto out;
+
+ /* Add this line to the multiline var */
+ if ((error = git_str_puts(value, proc_line)) < 0)
+ goto out;
+
+next:
+ git__free(line);
+ git__free(proc_line);
+ in_quotes = quote_count;
+ continue;
+
+out:
+ git__free(line);
+ git__free(proc_line);
+ return error;
+ }
+
+ return 0;
+}
+
+GIT_INLINE(bool) is_namechar(char c)
+{
+ return isalnum(c) || c == '-';
+}
+
+static int parse_name(
+ char **name, const char **value, git_config_parser *reader, const char *line)
+{
+ const char *name_end = line, *value_start;
+
+ *name = NULL;
+ *value = NULL;
+
+ while (*name_end && is_namechar(*name_end))
+ name_end++;
+
+ if (line == name_end) {
+ set_parse_error(reader, 0, "invalid configuration key");
+ return -1;
+ }
+
+ value_start = name_end;
+
+ while (*value_start && git__isspace(*value_start))
+ value_start++;
+
+ if (*value_start == '=') {
+ *value = value_start + 1;
+ } else if (*value_start) {
+ set_parse_error(reader, 0, "invalid configuration key");
+ return -1;
+ }
+
+ if ((*name = git__strndup(line, name_end - line)) == NULL)
+ return -1;
+
+ return 0;
+}
+
+static int parse_variable(git_config_parser *reader, char **var_name, char **var_value, size_t *line_len)
+{
+ const char *value_start = NULL;
+ char *line = NULL, *name = NULL, *value = NULL;
+ int quote_count, error;
+ bool multiline;
+
+ *var_name = NULL;
+ *var_value = NULL;
+
+ git_parse_advance_ws(&reader->ctx);
+ line = git__strndup(reader->ctx.line, reader->ctx.line_len);
+ GIT_ERROR_CHECK_ALLOC(line);
+
+ quote_count = strip_comments(line, 0);
+
+ if ((error = parse_name(&name, &value_start, reader, line)) < 0)
+ goto out;
+
+ /*
+ * Now, let's try to parse the value
+ */
+ if (value_start != NULL) {
+ while (git__isspace(value_start[0]))
+ value_start++;
+
+ if ((error = unescape_line(&value, &multiline, value_start, 0)) < 0)
+ goto out;
+
+ if (multiline) {
+ git_str multi_value = GIT_STR_INIT;
+ git_str_attach(&multi_value, value, 0);
+ value = NULL;
+
+ if (parse_multiline_variable(reader, &multi_value, quote_count % 2, line_len) < 0 ||
+ git_str_oom(&multi_value)) {
+ error = -1;
+ git_str_dispose(&multi_value);
+ goto out;
+ }
+
+ value = git_str_detach(&multi_value);
+ }
+ }
+
+ *var_name = name;
+ *var_value = value;
+ name = NULL;
+ value = NULL;
+
+out:
+ git__free(name);
+ git__free(value);
+ git__free(line);
+ return error;
+}
+
+int git_config_parser_init(git_config_parser *out, const char *path, const char *data, size_t datalen)
+{
+ out->path = path;
+ return git_parse_ctx_init(&out->ctx, data, datalen);
+}
+
+void git_config_parser_dispose(git_config_parser *parser)
+{
+ git_parse_ctx_clear(&parser->ctx);
+}
+
+int git_config_parse(
+ git_config_parser *parser,
+ git_config_parser_section_cb on_section,
+ git_config_parser_variable_cb on_variable,
+ git_config_parser_comment_cb on_comment,
+ git_config_parser_eof_cb on_eof,
+ void *payload)
+{
+ git_parse_ctx *ctx;
+ char *current_section = NULL, *var_name = NULL, *var_value = NULL;
+ int result = 0;
+
+ ctx = &parser->ctx;
+
+ skip_bom(ctx);
+
+ for (; ctx->remain_len > 0; git_parse_advance_line(ctx)) {
+ const char *line_start;
+ size_t line_len;
+ char c;
+
+ restart:
+ line_start = ctx->line;
+ line_len = ctx->line_len;
+
+ /*
+ * Get either first non-whitespace character or, if that does
+ * not exist, the first whitespace character. This is required
+ * to preserve whitespaces when writing back the file.
+ */
+ if (git_parse_peek(&c, ctx, GIT_PARSE_PEEK_SKIP_WHITESPACE) < 0 &&
+ git_parse_peek(&c, ctx, 0) < 0)
+ continue;
+
+ switch (c) {
+ case '[': /* section header, new section begins */
+ git__free(current_section);
+ current_section = NULL;
+
+ result = parse_section_header(parser, &current_section);
+ if (result < 0)
+ break;
+
+ git_parse_advance_chars(ctx, result);
+
+ if (on_section)
+ result = on_section(parser, current_section, line_start, line_len, payload);
+ /*
+ * After we've parsed the section header we may not be
+ * done with the line. If there's still data in there,
+ * run the next loop with the rest of the current line
+ * instead of moving forward.
+ */
+
+ if (!git_parse_peek(&c, ctx, GIT_PARSE_PEEK_SKIP_WHITESPACE))
+ goto restart;
+
+ break;
+
+ case '\n': /* comment or whitespace-only */
+ case '\r':
+ case ' ':
+ case '\t':
+ case ';':
+ case '#':
+ if (on_comment) {
+ result = on_comment(parser, line_start, line_len, payload);
+ }
+ break;
+
+ default: /* assume variable declaration */
+ if ((result = parse_variable(parser, &var_name, &var_value, &line_len)) == 0 && on_variable) {
+ result = on_variable(parser, current_section, var_name, var_value, line_start, line_len, payload);
+ git__free(var_name);
+ git__free(var_value);
+ }
+
+ break;
+ }
+
+ if (result < 0)
+ goto out;
+ }
+
+ if (on_eof)
+ result = on_eof(parser, current_section, payload);
+
+out:
+ git__free(current_section);
+ return result;
+}
diff --git a/src/libgit2/config_parse.h b/src/libgit2/config_parse.h
new file mode 100644
index 0000000..b791d32
--- /dev/null
+++ b/src/libgit2/config_parse.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_config_parse_h__
+#define INCLUDE_config_parse_h__
+
+#include "common.h"
+
+#include "array.h"
+#include "futils.h"
+#include "oid.h"
+#include "parse.h"
+
+extern const char *git_config_escapes;
+extern const char *git_config_escaped;
+
+typedef struct {
+ const char *path;
+ git_parse_ctx ctx;
+} git_config_parser;
+
+#define GIT_CONFIG_PARSER_INIT { NULL, GIT_PARSE_CTX_INIT }
+
+typedef int (*git_config_parser_section_cb)(
+ git_config_parser *parser,
+ const char *current_section,
+ const char *line,
+ size_t line_len,
+ void *payload);
+
+typedef int (*git_config_parser_variable_cb)(
+ git_config_parser *parser,
+ const char *current_section,
+ const char *var_name,
+ const char *var_value,
+ const char *line,
+ size_t line_len,
+ void *payload);
+
+typedef int (*git_config_parser_comment_cb)(
+ git_config_parser *parser,
+ const char *line,
+ size_t line_len,
+ void *payload);
+
+typedef int (*git_config_parser_eof_cb)(
+ git_config_parser *parser,
+ const char *current_section,
+ void *payload);
+
+int git_config_parser_init(git_config_parser *out, const char *path, const char *data, size_t datalen);
+void git_config_parser_dispose(git_config_parser *parser);
+
+int git_config_parse(
+ git_config_parser *parser,
+ git_config_parser_section_cb on_section,
+ git_config_parser_variable_cb on_variable,
+ git_config_parser_comment_cb on_comment,
+ git_config_parser_eof_cb on_eof,
+ void *payload);
+
+#endif
diff --git a/src/libgit2/config_snapshot.c b/src/libgit2/config_snapshot.c
new file mode 100644
index 0000000..e295d2f
--- /dev/null
+++ b/src/libgit2/config_snapshot.c
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "config_backend.h"
+
+#include "config.h"
+#include "config_entries.h"
+
+typedef struct {
+ git_config_backend parent;
+ git_mutex values_mutex;
+ git_config_entries *entries;
+ git_config_backend *source;
+} config_snapshot_backend;
+
+static int config_error_readonly(void)
+{
+ git_error_set(GIT_ERROR_CONFIG, "this backend is read-only");
+ return -1;
+}
+
+static int config_snapshot_iterator(
+ git_config_iterator **iter,
+ struct git_config_backend *backend)
+{
+ config_snapshot_backend *b = GIT_CONTAINER_OF(backend, config_snapshot_backend, parent);
+ git_config_entries *entries = NULL;
+ int error;
+
+ if ((error = git_config_entries_dup(&entries, b->entries)) < 0 ||
+ (error = git_config_entries_iterator_new(iter, entries)) < 0)
+ goto out;
+
+out:
+ /* Let iterator delete duplicated entries when it's done */
+ git_config_entries_free(entries);
+ return error;
+}
+
+/* release the map containing the entry as an equivalent to freeing it */
+static void config_snapshot_entry_free(git_config_entry *entry)
+{
+ git_config_entries *entries = (git_config_entries *) entry->payload;
+ git_config_entries_free(entries);
+}
+
+static int config_snapshot_get(git_config_backend *cfg, const char *key, git_config_entry **out)
+{
+ config_snapshot_backend *b = GIT_CONTAINER_OF(cfg, config_snapshot_backend, parent);
+ git_config_entries *entries = NULL;
+ git_config_entry *entry;
+ int error = 0;
+
+ if (git_mutex_lock(&b->values_mutex) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to lock config backend");
+ return -1;
+ }
+
+ entries = b->entries;
+ git_config_entries_incref(entries);
+ git_mutex_unlock(&b->values_mutex);
+
+ if ((error = (git_config_entries_get(&entry, entries, key))) < 0) {
+ git_config_entries_free(entries);
+ return error;
+ }
+
+ entry->free = config_snapshot_entry_free;
+ entry->payload = entries;
+ *out = entry;
+
+ return 0;
+}
+
+static int config_snapshot_set(git_config_backend *cfg, const char *name, const char *value)
+{
+ GIT_UNUSED(cfg);
+ GIT_UNUSED(name);
+ GIT_UNUSED(value);
+
+ return config_error_readonly();
+}
+
+static int config_snapshot_set_multivar(
+ git_config_backend *cfg, const char *name, const char *regexp, const char *value)
+{
+ GIT_UNUSED(cfg);
+ GIT_UNUSED(name);
+ GIT_UNUSED(regexp);
+ GIT_UNUSED(value);
+
+ return config_error_readonly();
+}
+
+static int config_snapshot_delete_multivar(git_config_backend *cfg, const char *name, const char *regexp)
+{
+ GIT_UNUSED(cfg);
+ GIT_UNUSED(name);
+ GIT_UNUSED(regexp);
+
+ return config_error_readonly();
+}
+
+static int config_snapshot_delete(git_config_backend *cfg, const char *name)
+{
+ GIT_UNUSED(cfg);
+ GIT_UNUSED(name);
+
+ return config_error_readonly();
+}
+
+static int config_snapshot_lock(git_config_backend *_cfg)
+{
+ GIT_UNUSED(_cfg);
+
+ return config_error_readonly();
+}
+
+static int config_snapshot_unlock(git_config_backend *_cfg, int success)
+{
+ GIT_UNUSED(_cfg);
+ GIT_UNUSED(success);
+
+ return config_error_readonly();
+}
+
+static void config_snapshot_free(git_config_backend *_backend)
+{
+ config_snapshot_backend *backend = GIT_CONTAINER_OF(_backend, config_snapshot_backend, parent);
+
+ if (backend == NULL)
+ return;
+
+ git_config_entries_free(backend->entries);
+ git_mutex_free(&backend->values_mutex);
+ git__free(backend);
+}
+
+static int config_snapshot_open(git_config_backend *cfg, git_config_level_t level, const git_repository *repo)
+{
+ config_snapshot_backend *b = GIT_CONTAINER_OF(cfg, config_snapshot_backend, parent);
+ git_config_entries *entries = NULL;
+ git_config_iterator *it = NULL;
+ git_config_entry *entry;
+ int error;
+
+ /* We're just copying data, don't care about the level or repo*/
+ GIT_UNUSED(level);
+ GIT_UNUSED(repo);
+
+ if ((error = git_config_entries_new(&entries)) < 0 ||
+ (error = b->source->iterator(&it, b->source)) < 0)
+ goto out;
+
+ while ((error = git_config_next(&entry, it)) == 0)
+ if ((error = git_config_entries_dup_entry(entries, entry)) < 0)
+ goto out;
+
+ if (error < 0) {
+ if (error != GIT_ITEROVER)
+ goto out;
+ error = 0;
+ }
+
+ b->entries = entries;
+
+out:
+ git_config_iterator_free(it);
+ if (error)
+ git_config_entries_free(entries);
+ return error;
+}
+
+int git_config_backend_snapshot(git_config_backend **out, git_config_backend *source)
+{
+ config_snapshot_backend *backend;
+
+ backend = git__calloc(1, sizeof(config_snapshot_backend));
+ GIT_ERROR_CHECK_ALLOC(backend);
+
+ backend->parent.version = GIT_CONFIG_BACKEND_VERSION;
+ git_mutex_init(&backend->values_mutex);
+
+ backend->source = source;
+
+ backend->parent.readonly = 1;
+ backend->parent.version = GIT_CONFIG_BACKEND_VERSION;
+ backend->parent.open = config_snapshot_open;
+ backend->parent.get = config_snapshot_get;
+ backend->parent.set = config_snapshot_set;
+ backend->parent.set_multivar = config_snapshot_set_multivar;
+ backend->parent.snapshot = git_config_backend_snapshot;
+ backend->parent.del = config_snapshot_delete;
+ backend->parent.del_multivar = config_snapshot_delete_multivar;
+ backend->parent.iterator = config_snapshot_iterator;
+ backend->parent.lock = config_snapshot_lock;
+ backend->parent.unlock = config_snapshot_unlock;
+ backend->parent.free = config_snapshot_free;
+
+ *out = &backend->parent;
+
+ return 0;
+}
diff --git a/src/libgit2/crlf.c b/src/libgit2/crlf.c
new file mode 100644
index 0000000..1e1f1e8
--- /dev/null
+++ b/src/libgit2/crlf.c
@@ -0,0 +1,426 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+
+#include "git2/attr.h"
+#include "git2/blob.h"
+#include "git2/index.h"
+#include "git2/sys/filter.h"
+
+#include "buf.h"
+#include "futils.h"
+#include "hash.h"
+#include "filter.h"
+#include "repository.h"
+
+typedef enum {
+ GIT_CRLF_UNDEFINED,
+ GIT_CRLF_BINARY,
+ GIT_CRLF_TEXT,
+ GIT_CRLF_TEXT_INPUT,
+ GIT_CRLF_TEXT_CRLF,
+ GIT_CRLF_AUTO,
+ GIT_CRLF_AUTO_INPUT,
+ GIT_CRLF_AUTO_CRLF
+} git_crlf_t;
+
+struct crlf_attrs {
+ int attr_action; /* the .gitattributes setting */
+ int crlf_action; /* the core.autocrlf setting */
+
+ int auto_crlf;
+ int safe_crlf;
+ int core_eol;
+};
+
+struct crlf_filter {
+ git_filter f;
+};
+
+static git_crlf_t check_crlf(const char *value)
+{
+ if (GIT_ATTR_IS_TRUE(value))
+ return GIT_CRLF_TEXT;
+ else if (GIT_ATTR_IS_FALSE(value))
+ return GIT_CRLF_BINARY;
+ else if (GIT_ATTR_IS_UNSPECIFIED(value))
+ ;
+ else if (strcmp(value, "input") == 0)
+ return GIT_CRLF_TEXT_INPUT;
+ else if (strcmp(value, "auto") == 0)
+ return GIT_CRLF_AUTO;
+
+ return GIT_CRLF_UNDEFINED;
+}
+
+static git_configmap_value check_eol(const char *value)
+{
+ if (GIT_ATTR_IS_UNSPECIFIED(value))
+ ;
+ else if (strcmp(value, "lf") == 0)
+ return GIT_EOL_LF;
+ else if (strcmp(value, "crlf") == 0)
+ return GIT_EOL_CRLF;
+
+ return GIT_EOL_UNSET;
+}
+
+static int has_cr_in_index(const git_filter_source *src)
+{
+ git_repository *repo = git_filter_source_repo(src);
+ const char *path = git_filter_source_path(src);
+ git_index *index;
+ const git_index_entry *entry;
+ git_blob *blob;
+ const void *blobcontent;
+ git_object_size_t blobsize;
+ bool found_cr;
+
+ if (!path)
+ return false;
+
+ if (git_repository_index__weakptr(&index, repo) < 0) {
+ git_error_clear();
+ return false;
+ }
+
+ if (!(entry = git_index_get_bypath(index, path, 0)) &&
+ !(entry = git_index_get_bypath(index, path, 1)))
+ return false;
+
+ if (!S_ISREG(entry->mode)) /* don't crlf filter non-blobs */
+ return true;
+
+ if (git_blob_lookup(&blob, repo, &entry->id) < 0)
+ return false;
+
+ blobcontent = git_blob_rawcontent(blob);
+ blobsize = git_blob_rawsize(blob);
+ if (!git__is_sizet(blobsize))
+ blobsize = (size_t)-1;
+
+ found_cr = (blobcontent != NULL &&
+ blobsize > 0 &&
+ memchr(blobcontent, '\r', (size_t)blobsize) != NULL);
+
+ git_blob_free(blob);
+ return found_cr;
+}
+
+static int text_eol_is_crlf(struct crlf_attrs *ca)
+{
+ if (ca->auto_crlf == GIT_AUTO_CRLF_TRUE)
+ return 1;
+ else if (ca->auto_crlf == GIT_AUTO_CRLF_INPUT)
+ return 0;
+
+ if (ca->core_eol == GIT_EOL_CRLF)
+ return 1;
+ if (ca->core_eol == GIT_EOL_UNSET && GIT_EOL_NATIVE == GIT_EOL_CRLF)
+ return 1;
+
+ return 0;
+}
+
+static git_configmap_value output_eol(struct crlf_attrs *ca)
+{
+ switch (ca->crlf_action) {
+ case GIT_CRLF_BINARY:
+ return GIT_EOL_UNSET;
+ case GIT_CRLF_TEXT_CRLF:
+ return GIT_EOL_CRLF;
+ case GIT_CRLF_TEXT_INPUT:
+ return GIT_EOL_LF;
+ case GIT_CRLF_UNDEFINED:
+ case GIT_CRLF_AUTO_CRLF:
+ return GIT_EOL_CRLF;
+ case GIT_CRLF_AUTO_INPUT:
+ return GIT_EOL_LF;
+ case GIT_CRLF_TEXT:
+ case GIT_CRLF_AUTO:
+ return text_eol_is_crlf(ca) ? GIT_EOL_CRLF : GIT_EOL_LF;
+ }
+
+ /* TODO: warn when available */
+ return ca->core_eol;
+}
+
+GIT_INLINE(int) check_safecrlf(
+ struct crlf_attrs *ca,
+ const git_filter_source *src,
+ git_str_text_stats *stats)
+{
+ const char *filename = git_filter_source_path(src);
+
+ if (!ca->safe_crlf)
+ return 0;
+
+ if (output_eol(ca) == GIT_EOL_LF) {
+ /*
+ * CRLFs would not be restored by checkout:
+ * check if we'd remove CRLFs
+ */
+ if (stats->crlf) {
+ if (ca->safe_crlf == GIT_SAFE_CRLF_WARN) {
+ /* TODO: issue a warning when available */
+ } else {
+ if (filename && *filename)
+ git_error_set(
+ GIT_ERROR_FILTER, "CRLF would be replaced by LF in '%s'",
+ filename);
+ else
+ git_error_set(
+ GIT_ERROR_FILTER, "CRLF would be replaced by LF");
+
+ return -1;
+ }
+ }
+ } else if (output_eol(ca) == GIT_EOL_CRLF) {
+ /*
+ * CRLFs would be added by checkout:
+ * check if we have "naked" LFs
+ */
+ if (stats->crlf != stats->lf) {
+ if (ca->safe_crlf == GIT_SAFE_CRLF_WARN) {
+ /* TODO: issue a warning when available */
+ } else {
+ if (filename && *filename)
+ git_error_set(
+ GIT_ERROR_FILTER, "LF would be replaced by CRLF in '%s'",
+ filename);
+ else
+ git_error_set(
+ GIT_ERROR_FILTER, "LF would be replaced by CRLF");
+
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int crlf_apply_to_odb(
+ struct crlf_attrs *ca,
+ git_str *to,
+ const git_str *from,
+ const git_filter_source *src)
+{
+ git_str_text_stats stats;
+ bool is_binary;
+ int error;
+
+ /* Binary attribute? Empty file? Nothing to do */
+ if (ca->crlf_action == GIT_CRLF_BINARY || from->size == 0)
+ return GIT_PASSTHROUGH;
+
+ is_binary = git_str_gather_text_stats(&stats, from, false);
+
+ /* Heuristics to see if we can skip the conversion.
+ * Straight from Core Git.
+ */
+ if (ca->crlf_action == GIT_CRLF_AUTO ||
+ ca->crlf_action == GIT_CRLF_AUTO_INPUT ||
+ ca->crlf_action == GIT_CRLF_AUTO_CRLF) {
+
+ if (is_binary)
+ return GIT_PASSTHROUGH;
+
+ /*
+ * If the file in the index has any CR in it, do not convert.
+ * This is the new safer autocrlf handling.
+ */
+ if (has_cr_in_index(src))
+ return GIT_PASSTHROUGH;
+ }
+
+ if ((error = check_safecrlf(ca, src, &stats)) < 0)
+ return error;
+
+ /* If there are no CR characters to filter out, then just pass */
+ if (!stats.crlf)
+ return GIT_PASSTHROUGH;
+
+ /* Actually drop the carriage returns */
+ return git_str_crlf_to_lf(to, from);
+}
+
+static int crlf_apply_to_workdir(
+ struct crlf_attrs *ca,
+ git_str *to,
+ const git_str *from)
+{
+ git_str_text_stats stats;
+ bool is_binary;
+
+ /* Empty file? Nothing to do. */
+ if (git_str_len(from) == 0 || output_eol(ca) != GIT_EOL_CRLF)
+ return GIT_PASSTHROUGH;
+
+ is_binary = git_str_gather_text_stats(&stats, from, false);
+
+ /* If there are no LFs, or all LFs are part of a CRLF, nothing to do */
+ if (stats.lf == 0 || stats.lf == stats.crlf)
+ return GIT_PASSTHROUGH;
+
+ if (ca->crlf_action == GIT_CRLF_AUTO ||
+ ca->crlf_action == GIT_CRLF_AUTO_INPUT ||
+ ca->crlf_action == GIT_CRLF_AUTO_CRLF) {
+
+ /* If we have any existing CR or CRLF line endings, do nothing */
+ if (stats.cr > 0)
+ return GIT_PASSTHROUGH;
+
+ /* Don't filter binary files */
+ if (is_binary)
+ return GIT_PASSTHROUGH;
+ }
+
+ return git_str_lf_to_crlf(to, from);
+}
+
+static int convert_attrs(
+ struct crlf_attrs *ca,
+ const char **attr_values,
+ const git_filter_source *src)
+{
+ int error;
+
+ memset(ca, 0, sizeof(struct crlf_attrs));
+
+ if ((error = git_repository__configmap_lookup(&ca->auto_crlf,
+ git_filter_source_repo(src), GIT_CONFIGMAP_AUTO_CRLF)) < 0 ||
+ (error = git_repository__configmap_lookup(&ca->safe_crlf,
+ git_filter_source_repo(src), GIT_CONFIGMAP_SAFE_CRLF)) < 0 ||
+ (error = git_repository__configmap_lookup(&ca->core_eol,
+ git_filter_source_repo(src), GIT_CONFIGMAP_EOL)) < 0)
+ return error;
+
+ /* downgrade FAIL to WARN if ALLOW_UNSAFE option is used */
+ if ((git_filter_source_flags(src) & GIT_FILTER_ALLOW_UNSAFE) &&
+ ca->safe_crlf == GIT_SAFE_CRLF_FAIL)
+ ca->safe_crlf = GIT_SAFE_CRLF_WARN;
+
+ if (attr_values) {
+ /* load the text attribute */
+ ca->crlf_action = check_crlf(attr_values[2]); /* text */
+
+ if (ca->crlf_action == GIT_CRLF_UNDEFINED)
+ ca->crlf_action = check_crlf(attr_values[0]); /* crlf */
+
+ if (ca->crlf_action != GIT_CRLF_BINARY) {
+ /* load the eol attribute */
+ int eol_attr = check_eol(attr_values[1]);
+
+ if (ca->crlf_action == GIT_CRLF_AUTO && eol_attr == GIT_EOL_LF)
+ ca->crlf_action = GIT_CRLF_AUTO_INPUT;
+ else if (ca->crlf_action == GIT_CRLF_AUTO && eol_attr == GIT_EOL_CRLF)
+ ca->crlf_action = GIT_CRLF_AUTO_CRLF;
+ else if (eol_attr == GIT_EOL_LF)
+ ca->crlf_action = GIT_CRLF_TEXT_INPUT;
+ else if (eol_attr == GIT_EOL_CRLF)
+ ca->crlf_action = GIT_CRLF_TEXT_CRLF;
+ }
+
+ ca->attr_action = ca->crlf_action;
+ } else {
+ ca->crlf_action = GIT_CRLF_UNDEFINED;
+ }
+
+ if (ca->crlf_action == GIT_CRLF_TEXT)
+ ca->crlf_action = text_eol_is_crlf(ca) ? GIT_CRLF_TEXT_CRLF : GIT_CRLF_TEXT_INPUT;
+ if (ca->crlf_action == GIT_CRLF_UNDEFINED && ca->auto_crlf == GIT_AUTO_CRLF_FALSE)
+ ca->crlf_action = GIT_CRLF_BINARY;
+ if (ca->crlf_action == GIT_CRLF_UNDEFINED && ca->auto_crlf == GIT_AUTO_CRLF_TRUE)
+ ca->crlf_action = GIT_CRLF_AUTO_CRLF;
+ if (ca->crlf_action == GIT_CRLF_UNDEFINED && ca->auto_crlf == GIT_AUTO_CRLF_INPUT)
+ ca->crlf_action = GIT_CRLF_AUTO_INPUT;
+
+ return 0;
+}
+
+static int crlf_check(
+ git_filter *self,
+ void **payload, /* points to NULL ptr on entry, may be set */
+ const git_filter_source *src,
+ const char **attr_values)
+{
+ struct crlf_attrs ca;
+
+ GIT_UNUSED(self);
+
+ convert_attrs(&ca, attr_values, src);
+
+ if (ca.crlf_action == GIT_CRLF_BINARY)
+ return GIT_PASSTHROUGH;
+
+ *payload = git__malloc(sizeof(ca));
+ GIT_ERROR_CHECK_ALLOC(*payload);
+ memcpy(*payload, &ca, sizeof(ca));
+
+ return 0;
+}
+
+static int crlf_apply(
+ git_filter *self,
+ void **payload, /* may be read and/or set */
+ git_str *to,
+ const git_str *from,
+ const git_filter_source *src)
+{
+ int error = 0;
+
+ /* initialize payload in case `check` was bypassed */
+ if (!*payload) {
+ if ((error = crlf_check(self, payload, src, NULL)) < 0)
+ return error;
+ }
+
+ if (git_filter_source_mode(src) == GIT_FILTER_SMUDGE)
+ error = crlf_apply_to_workdir(*payload, to, from);
+ else
+ error = crlf_apply_to_odb(*payload, to, from, src);
+
+ return error;
+}
+
+static int crlf_stream(
+ git_writestream **out,
+ git_filter *self,
+ void **payload,
+ const git_filter_source *src,
+ git_writestream *next)
+{
+ return git_filter_buffered_stream_new(out,
+ self, crlf_apply, NULL, payload, src, next);
+}
+
+static void crlf_cleanup(
+ git_filter *self,
+ void *payload)
+{
+ GIT_UNUSED(self);
+ git__free(payload);
+}
+
+git_filter *git_crlf_filter_new(void)
+{
+ struct crlf_filter *f = git__calloc(1, sizeof(struct crlf_filter));
+ if (f == NULL)
+ return NULL;
+
+ f->f.version = GIT_FILTER_VERSION;
+ f->f.attributes = "crlf eol text";
+ f->f.initialize = NULL;
+ f->f.shutdown = git_filter_free;
+ f->f.check = crlf_check;
+ f->f.stream = crlf_stream;
+ f->f.cleanup = crlf_cleanup;
+
+ return (git_filter *)f;
+}
diff --git a/src/libgit2/delta.c b/src/libgit2/delta.c
new file mode 100644
index 0000000..2d2c5fa
--- /dev/null
+++ b/src/libgit2/delta.c
@@ -0,0 +1,628 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "delta.h"
+
+/* maximum hash entry list for the same hash bucket */
+#define HASH_LIMIT 64
+
+#define RABIN_SHIFT 23
+#define RABIN_WINDOW 16
+
+static const unsigned int T[256] = {
+ 0x00000000, 0xab59b4d1, 0x56b369a2, 0xfdeadd73, 0x063f6795, 0xad66d344,
+ 0x508c0e37, 0xfbd5bae6, 0x0c7ecf2a, 0xa7277bfb, 0x5acda688, 0xf1941259,
+ 0x0a41a8bf, 0xa1181c6e, 0x5cf2c11d, 0xf7ab75cc, 0x18fd9e54, 0xb3a42a85,
+ 0x4e4ef7f6, 0xe5174327, 0x1ec2f9c1, 0xb59b4d10, 0x48719063, 0xe32824b2,
+ 0x1483517e, 0xbfdae5af, 0x423038dc, 0xe9698c0d, 0x12bc36eb, 0xb9e5823a,
+ 0x440f5f49, 0xef56eb98, 0x31fb3ca8, 0x9aa28879, 0x6748550a, 0xcc11e1db,
+ 0x37c45b3d, 0x9c9defec, 0x6177329f, 0xca2e864e, 0x3d85f382, 0x96dc4753,
+ 0x6b369a20, 0xc06f2ef1, 0x3bba9417, 0x90e320c6, 0x6d09fdb5, 0xc6504964,
+ 0x2906a2fc, 0x825f162d, 0x7fb5cb5e, 0xd4ec7f8f, 0x2f39c569, 0x846071b8,
+ 0x798aaccb, 0xd2d3181a, 0x25786dd6, 0x8e21d907, 0x73cb0474, 0xd892b0a5,
+ 0x23470a43, 0x881ebe92, 0x75f463e1, 0xdeadd730, 0x63f67950, 0xc8afcd81,
+ 0x354510f2, 0x9e1ca423, 0x65c91ec5, 0xce90aa14, 0x337a7767, 0x9823c3b6,
+ 0x6f88b67a, 0xc4d102ab, 0x393bdfd8, 0x92626b09, 0x69b7d1ef, 0xc2ee653e,
+ 0x3f04b84d, 0x945d0c9c, 0x7b0be704, 0xd05253d5, 0x2db88ea6, 0x86e13a77,
+ 0x7d348091, 0xd66d3440, 0x2b87e933, 0x80de5de2, 0x7775282e, 0xdc2c9cff,
+ 0x21c6418c, 0x8a9ff55d, 0x714a4fbb, 0xda13fb6a, 0x27f92619, 0x8ca092c8,
+ 0x520d45f8, 0xf954f129, 0x04be2c5a, 0xafe7988b, 0x5432226d, 0xff6b96bc,
+ 0x02814bcf, 0xa9d8ff1e, 0x5e738ad2, 0xf52a3e03, 0x08c0e370, 0xa39957a1,
+ 0x584ced47, 0xf3155996, 0x0eff84e5, 0xa5a63034, 0x4af0dbac, 0xe1a96f7d,
+ 0x1c43b20e, 0xb71a06df, 0x4ccfbc39, 0xe79608e8, 0x1a7cd59b, 0xb125614a,
+ 0x468e1486, 0xedd7a057, 0x103d7d24, 0xbb64c9f5, 0x40b17313, 0xebe8c7c2,
+ 0x16021ab1, 0xbd5bae60, 0x6cb54671, 0xc7ecf2a0, 0x3a062fd3, 0x915f9b02,
+ 0x6a8a21e4, 0xc1d39535, 0x3c394846, 0x9760fc97, 0x60cb895b, 0xcb923d8a,
+ 0x3678e0f9, 0x9d215428, 0x66f4eece, 0xcdad5a1f, 0x3047876c, 0x9b1e33bd,
+ 0x7448d825, 0xdf116cf4, 0x22fbb187, 0x89a20556, 0x7277bfb0, 0xd92e0b61,
+ 0x24c4d612, 0x8f9d62c3, 0x7836170f, 0xd36fa3de, 0x2e857ead, 0x85dcca7c,
+ 0x7e09709a, 0xd550c44b, 0x28ba1938, 0x83e3ade9, 0x5d4e7ad9, 0xf617ce08,
+ 0x0bfd137b, 0xa0a4a7aa, 0x5b711d4c, 0xf028a99d, 0x0dc274ee, 0xa69bc03f,
+ 0x5130b5f3, 0xfa690122, 0x0783dc51, 0xacda6880, 0x570fd266, 0xfc5666b7,
+ 0x01bcbbc4, 0xaae50f15, 0x45b3e48d, 0xeeea505c, 0x13008d2f, 0xb85939fe,
+ 0x438c8318, 0xe8d537c9, 0x153feaba, 0xbe665e6b, 0x49cd2ba7, 0xe2949f76,
+ 0x1f7e4205, 0xb427f6d4, 0x4ff24c32, 0xe4abf8e3, 0x19412590, 0xb2189141,
+ 0x0f433f21, 0xa41a8bf0, 0x59f05683, 0xf2a9e252, 0x097c58b4, 0xa225ec65,
+ 0x5fcf3116, 0xf49685c7, 0x033df00b, 0xa86444da, 0x558e99a9, 0xfed72d78,
+ 0x0502979e, 0xae5b234f, 0x53b1fe3c, 0xf8e84aed, 0x17bea175, 0xbce715a4,
+ 0x410dc8d7, 0xea547c06, 0x1181c6e0, 0xbad87231, 0x4732af42, 0xec6b1b93,
+ 0x1bc06e5f, 0xb099da8e, 0x4d7307fd, 0xe62ab32c, 0x1dff09ca, 0xb6a6bd1b,
+ 0x4b4c6068, 0xe015d4b9, 0x3eb80389, 0x95e1b758, 0x680b6a2b, 0xc352defa,
+ 0x3887641c, 0x93ded0cd, 0x6e340dbe, 0xc56db96f, 0x32c6cca3, 0x999f7872,
+ 0x6475a501, 0xcf2c11d0, 0x34f9ab36, 0x9fa01fe7, 0x624ac294, 0xc9137645,
+ 0x26459ddd, 0x8d1c290c, 0x70f6f47f, 0xdbaf40ae, 0x207afa48, 0x8b234e99,
+ 0x76c993ea, 0xdd90273b, 0x2a3b52f7, 0x8162e626, 0x7c883b55, 0xd7d18f84,
+ 0x2c043562, 0x875d81b3, 0x7ab75cc0, 0xd1eee811
+};
+
+static const unsigned int U[256] = {
+ 0x00000000, 0x7eb5200d, 0x5633f4cb, 0x2886d4c6, 0x073e5d47, 0x798b7d4a,
+ 0x510da98c, 0x2fb88981, 0x0e7cba8e, 0x70c99a83, 0x584f4e45, 0x26fa6e48,
+ 0x0942e7c9, 0x77f7c7c4, 0x5f711302, 0x21c4330f, 0x1cf9751c, 0x624c5511,
+ 0x4aca81d7, 0x347fa1da, 0x1bc7285b, 0x65720856, 0x4df4dc90, 0x3341fc9d,
+ 0x1285cf92, 0x6c30ef9f, 0x44b63b59, 0x3a031b54, 0x15bb92d5, 0x6b0eb2d8,
+ 0x4388661e, 0x3d3d4613, 0x39f2ea38, 0x4747ca35, 0x6fc11ef3, 0x11743efe,
+ 0x3eccb77f, 0x40799772, 0x68ff43b4, 0x164a63b9, 0x378e50b6, 0x493b70bb,
+ 0x61bda47d, 0x1f088470, 0x30b00df1, 0x4e052dfc, 0x6683f93a, 0x1836d937,
+ 0x250b9f24, 0x5bbebf29, 0x73386bef, 0x0d8d4be2, 0x2235c263, 0x5c80e26e,
+ 0x740636a8, 0x0ab316a5, 0x2b7725aa, 0x55c205a7, 0x7d44d161, 0x03f1f16c,
+ 0x2c4978ed, 0x52fc58e0, 0x7a7a8c26, 0x04cfac2b, 0x73e5d470, 0x0d50f47d,
+ 0x25d620bb, 0x5b6300b6, 0x74db8937, 0x0a6ea93a, 0x22e87dfc, 0x5c5d5df1,
+ 0x7d996efe, 0x032c4ef3, 0x2baa9a35, 0x551fba38, 0x7aa733b9, 0x041213b4,
+ 0x2c94c772, 0x5221e77f, 0x6f1ca16c, 0x11a98161, 0x392f55a7, 0x479a75aa,
+ 0x6822fc2b, 0x1697dc26, 0x3e1108e0, 0x40a428ed, 0x61601be2, 0x1fd53bef,
+ 0x3753ef29, 0x49e6cf24, 0x665e46a5, 0x18eb66a8, 0x306db26e, 0x4ed89263,
+ 0x4a173e48, 0x34a21e45, 0x1c24ca83, 0x6291ea8e, 0x4d29630f, 0x339c4302,
+ 0x1b1a97c4, 0x65afb7c9, 0x446b84c6, 0x3adea4cb, 0x1258700d, 0x6ced5000,
+ 0x4355d981, 0x3de0f98c, 0x15662d4a, 0x6bd30d47, 0x56ee4b54, 0x285b6b59,
+ 0x00ddbf9f, 0x7e689f92, 0x51d01613, 0x2f65361e, 0x07e3e2d8, 0x7956c2d5,
+ 0x5892f1da, 0x2627d1d7, 0x0ea10511, 0x7014251c, 0x5facac9d, 0x21198c90,
+ 0x099f5856, 0x772a785b, 0x4c921c31, 0x32273c3c, 0x1aa1e8fa, 0x6414c8f7,
+ 0x4bac4176, 0x3519617b, 0x1d9fb5bd, 0x632a95b0, 0x42eea6bf, 0x3c5b86b2,
+ 0x14dd5274, 0x6a687279, 0x45d0fbf8, 0x3b65dbf5, 0x13e30f33, 0x6d562f3e,
+ 0x506b692d, 0x2ede4920, 0x06589de6, 0x78edbdeb, 0x5755346a, 0x29e01467,
+ 0x0166c0a1, 0x7fd3e0ac, 0x5e17d3a3, 0x20a2f3ae, 0x08242768, 0x76910765,
+ 0x59298ee4, 0x279caee9, 0x0f1a7a2f, 0x71af5a22, 0x7560f609, 0x0bd5d604,
+ 0x235302c2, 0x5de622cf, 0x725eab4e, 0x0ceb8b43, 0x246d5f85, 0x5ad87f88,
+ 0x7b1c4c87, 0x05a96c8a, 0x2d2fb84c, 0x539a9841, 0x7c2211c0, 0x029731cd,
+ 0x2a11e50b, 0x54a4c506, 0x69998315, 0x172ca318, 0x3faa77de, 0x411f57d3,
+ 0x6ea7de52, 0x1012fe5f, 0x38942a99, 0x46210a94, 0x67e5399b, 0x19501996,
+ 0x31d6cd50, 0x4f63ed5d, 0x60db64dc, 0x1e6e44d1, 0x36e89017, 0x485db01a,
+ 0x3f77c841, 0x41c2e84c, 0x69443c8a, 0x17f11c87, 0x38499506, 0x46fcb50b,
+ 0x6e7a61cd, 0x10cf41c0, 0x310b72cf, 0x4fbe52c2, 0x67388604, 0x198da609,
+ 0x36352f88, 0x48800f85, 0x6006db43, 0x1eb3fb4e, 0x238ebd5d, 0x5d3b9d50,
+ 0x75bd4996, 0x0b08699b, 0x24b0e01a, 0x5a05c017, 0x728314d1, 0x0c3634dc,
+ 0x2df207d3, 0x534727de, 0x7bc1f318, 0x0574d315, 0x2acc5a94, 0x54797a99,
+ 0x7cffae5f, 0x024a8e52, 0x06852279, 0x78300274, 0x50b6d6b2, 0x2e03f6bf,
+ 0x01bb7f3e, 0x7f0e5f33, 0x57888bf5, 0x293dabf8, 0x08f998f7, 0x764cb8fa,
+ 0x5eca6c3c, 0x207f4c31, 0x0fc7c5b0, 0x7172e5bd, 0x59f4317b, 0x27411176,
+ 0x1a7c5765, 0x64c97768, 0x4c4fa3ae, 0x32fa83a3, 0x1d420a22, 0x63f72a2f,
+ 0x4b71fee9, 0x35c4dee4, 0x1400edeb, 0x6ab5cde6, 0x42331920, 0x3c86392d,
+ 0x133eb0ac, 0x6d8b90a1, 0x450d4467, 0x3bb8646a
+};
+
+struct index_entry {
+ const unsigned char *ptr;
+ unsigned int val;
+ struct index_entry *next;
+};
+
+struct git_delta_index {
+ unsigned long memsize;
+ const void *src_buf;
+ size_t src_size;
+ unsigned int hash_mask;
+ struct index_entry *hash[GIT_FLEX_ARRAY];
+};
+
+static int lookup_index_alloc(
+ void **out, unsigned long *out_len, size_t entries, size_t hash_count)
+{
+ size_t entries_len, hash_len, index_len;
+
+ GIT_ERROR_CHECK_ALLOC_MULTIPLY(&entries_len, entries, sizeof(struct index_entry));
+ GIT_ERROR_CHECK_ALLOC_MULTIPLY(&hash_len, hash_count, sizeof(struct index_entry *));
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&index_len, sizeof(struct git_delta_index), entries_len);
+ GIT_ERROR_CHECK_ALLOC_ADD(&index_len, index_len, hash_len);
+
+ if (!git__is_ulong(index_len)) {
+ git_error_set(GIT_ERROR_NOMEMORY, "overly large delta");
+ return -1;
+ }
+
+ *out = git__malloc(index_len);
+ GIT_ERROR_CHECK_ALLOC(*out);
+
+ *out_len = (unsigned long)index_len;
+ return 0;
+}
+
+int git_delta_index_init(
+ git_delta_index **out, const void *buf, size_t bufsize)
+{
+ unsigned int i, hsize, hmask, entries, prev_val, *hash_count;
+ const unsigned char *data, *buffer = buf;
+ struct git_delta_index *index;
+ struct index_entry *entry, **hash;
+ void *mem;
+ unsigned long memsize;
+
+ *out = NULL;
+
+ if (!buf || !bufsize)
+ return 0;
+
+ /* Determine index hash size. Note that indexing skips the
+ first byte to allow for optimizing the rabin polynomial
+ initialization in create_delta(). */
+ entries = (unsigned int)(bufsize - 1) / RABIN_WINDOW;
+ if (bufsize >= 0xffffffffUL) {
+ /*
+ * Current delta format can't encode offsets into
+ * reference buffer with more than 32 bits.
+ */
+ entries = 0xfffffffeU / RABIN_WINDOW;
+ }
+ hsize = entries / 4;
+ for (i = 4; i < 31 && (1u << i) < hsize; i++);
+ hsize = 1 << i;
+ hmask = hsize - 1;
+
+ if (lookup_index_alloc(&mem, &memsize, entries, hsize) < 0)
+ return -1;
+
+ index = mem;
+ mem = index->hash;
+ hash = mem;
+ mem = hash + hsize;
+ entry = mem;
+
+ index->memsize = memsize;
+ index->src_buf = buf;
+ index->src_size = bufsize;
+ index->hash_mask = hmask;
+ memset(hash, 0, hsize * sizeof(*hash));
+
+ /* allocate an array to count hash entries */
+ hash_count = git__calloc(hsize, sizeof(*hash_count));
+ if (!hash_count) {
+ git__free(index);
+ return -1;
+ }
+
+ /* then populate the index */
+ prev_val = ~0;
+ for (data = buffer + entries * RABIN_WINDOW - RABIN_WINDOW;
+ data >= buffer;
+ data -= RABIN_WINDOW) {
+ unsigned int val = 0;
+ for (i = 1; i <= RABIN_WINDOW; i++)
+ val = ((val << 8) | data[i]) ^ T[val >> RABIN_SHIFT];
+ if (val == prev_val) {
+ /* keep the lowest of consecutive identical blocks */
+ entry[-1].ptr = data + RABIN_WINDOW;
+ } else {
+ prev_val = val;
+ i = val & hmask;
+ entry->ptr = data + RABIN_WINDOW;
+ entry->val = val;
+ entry->next = hash[i];
+ hash[i] = entry++;
+ hash_count[i]++;
+ }
+ }
+
+ /*
+ * Determine a limit on the number of entries in the same hash
+ * bucket. This guard us against patological data sets causing
+ * really bad hash distribution with most entries in the same hash
+ * bucket that would bring us to O(m*n) computing costs (m and n
+ * corresponding to reference and target buffer sizes).
+ *
+ * Make sure none of the hash buckets has more entries than
+ * we're willing to test. Otherwise we cull the entry list
+ * uniformly to still preserve a good repartition across
+ * the reference buffer.
+ */
+ for (i = 0; i < hsize; i++) {
+ if (hash_count[i] < HASH_LIMIT)
+ continue;
+
+ entry = hash[i];
+ do {
+ struct index_entry *keep = entry;
+ int skip = hash_count[i] / HASH_LIMIT / 2;
+ do {
+ entry = entry->next;
+ } while(--skip && entry);
+ keep->next = entry;
+ } while (entry);
+ }
+ git__free(hash_count);
+
+ *out = index;
+ return 0;
+}
+
+void git_delta_index_free(git_delta_index *index)
+{
+ git__free(index);
+}
+
+size_t git_delta_index_size(git_delta_index *index)
+{
+ GIT_ASSERT_ARG(index);
+
+ return index->memsize;
+}
+
+/*
+ * The maximum size for any opcode sequence, including the initial header
+ * plus rabin window plus biggest copy.
+ */
+#define MAX_OP_SIZE (5 + 5 + 1 + RABIN_WINDOW + 7)
+
+int git_delta_create_from_index(
+ void **out,
+ size_t *out_len,
+ const struct git_delta_index *index,
+ const void *trg_buf,
+ size_t trg_size,
+ size_t max_size)
+{
+ unsigned int i, bufpos, bufsize, moff, msize, val;
+ int inscnt;
+ const unsigned char *ref_data, *ref_top, *data, *top;
+ unsigned char *buf;
+
+ *out = NULL;
+ *out_len = 0;
+
+ if (!trg_buf || !trg_size)
+ return 0;
+
+ if (index->src_size > UINT_MAX ||
+ trg_size > UINT_MAX ||
+ max_size > (UINT_MAX - MAX_OP_SIZE - 1)) {
+ git_error_set(GIT_ERROR_INVALID, "buffer sizes too large for delta processing");
+ return -1;
+ }
+
+ bufpos = 0;
+ bufsize = 8192;
+ if (max_size && bufsize >= max_size)
+ bufsize = (unsigned int)(max_size + MAX_OP_SIZE + 1);
+ buf = git__malloc(bufsize);
+ GIT_ERROR_CHECK_ALLOC(buf);
+
+ /* store reference buffer size */
+ i = (unsigned int)index->src_size;
+ while (i >= 0x80) {
+ buf[bufpos++] = i | 0x80;
+ i >>= 7;
+ }
+ buf[bufpos++] = i;
+
+ /* store target buffer size */
+ i = (unsigned int)trg_size;
+ while (i >= 0x80) {
+ buf[bufpos++] = i | 0x80;
+ i >>= 7;
+ }
+ buf[bufpos++] = i;
+
+ ref_data = index->src_buf;
+ ref_top = ref_data + index->src_size;
+ data = trg_buf;
+ top = (const unsigned char *) trg_buf + trg_size;
+
+ bufpos++;
+ val = 0;
+ for (i = 0; i < RABIN_WINDOW && data < top; i++, data++) {
+ buf[bufpos++] = *data;
+ val = ((val << 8) | *data) ^ T[val >> RABIN_SHIFT];
+ }
+ inscnt = i;
+
+ moff = 0;
+ msize = 0;
+ while (data < top) {
+ if (msize < 4096) {
+ struct index_entry *entry;
+ val ^= U[data[-RABIN_WINDOW]];
+ val = ((val << 8) | *data) ^ T[val >> RABIN_SHIFT];
+ i = val & index->hash_mask;
+ for (entry = index->hash[i]; entry; entry = entry->next) {
+ const unsigned char *ref = entry->ptr;
+ const unsigned char *src = data;
+ unsigned int ref_size = (unsigned int)(ref_top - ref);
+ if (entry->val != val)
+ continue;
+ if (ref_size > (unsigned int)(top - src))
+ ref_size = (unsigned int)(top - src);
+ if (ref_size <= msize)
+ break;
+ while (ref_size-- && *src++ == *ref)
+ ref++;
+ if (msize < (unsigned int)(ref - entry->ptr)) {
+ /* this is our best match so far */
+ msize = (unsigned int)(ref - entry->ptr);
+ moff = (unsigned int)(entry->ptr - ref_data);
+ if (msize >= 4096) /* good enough */
+ break;
+ }
+ }
+ }
+
+ if (msize < 4) {
+ if (!inscnt)
+ bufpos++;
+ buf[bufpos++] = *data++;
+ inscnt++;
+ if (inscnt == 0x7f) {
+ buf[bufpos - inscnt - 1] = inscnt;
+ inscnt = 0;
+ }
+ msize = 0;
+ } else {
+ unsigned int left;
+ unsigned char *op;
+
+ if (inscnt) {
+ while (moff && ref_data[moff-1] == data[-1]) {
+ /* we can match one byte back */
+ msize++;
+ moff--;
+ data--;
+ bufpos--;
+ if (--inscnt)
+ continue;
+ bufpos--; /* remove count slot */
+ inscnt--; /* make it -1 */
+ break;
+ }
+ buf[bufpos - inscnt - 1] = inscnt;
+ inscnt = 0;
+ }
+
+ /* A copy op is currently limited to 64KB (pack v2) */
+ left = (msize < 0x10000) ? 0 : (msize - 0x10000);
+ msize -= left;
+
+ op = buf + bufpos++;
+ i = 0x80;
+
+ if (moff & 0x000000ff)
+ buf[bufpos++] = moff >> 0, i |= 0x01;
+ if (moff & 0x0000ff00)
+ buf[bufpos++] = moff >> 8, i |= 0x02;
+ if (moff & 0x00ff0000)
+ buf[bufpos++] = moff >> 16, i |= 0x04;
+ if (moff & 0xff000000)
+ buf[bufpos++] = moff >> 24, i |= 0x08;
+
+ if (msize & 0x00ff)
+ buf[bufpos++] = msize >> 0, i |= 0x10;
+ if (msize & 0xff00)
+ buf[bufpos++] = msize >> 8, i |= 0x20;
+
+ *op = i;
+
+ data += msize;
+ moff += msize;
+ msize = left;
+
+ if (msize < 4096) {
+ int j;
+ val = 0;
+ for (j = -RABIN_WINDOW; j < 0; j++)
+ val = ((val << 8) | data[j])
+ ^ T[val >> RABIN_SHIFT];
+ }
+ }
+
+ if (bufpos >= bufsize - MAX_OP_SIZE) {
+ void *tmp = buf;
+ bufsize = bufsize * 3 / 2;
+ if (max_size && bufsize >= max_size)
+ bufsize = (unsigned int)(max_size + MAX_OP_SIZE + 1);
+ if (max_size && bufpos > max_size)
+ break;
+ buf = git__realloc(buf, bufsize);
+ if (!buf) {
+ git__free(tmp);
+ return -1;
+ }
+ }
+ }
+
+ if (inscnt)
+ buf[bufpos - inscnt - 1] = inscnt;
+
+ if (max_size && bufpos > max_size) {
+ git_error_set(GIT_ERROR_NOMEMORY, "delta would be larger than maximum size");
+ git__free(buf);
+ return GIT_EBUFS;
+ }
+
+ *out_len = bufpos;
+ *out = buf;
+ return 0;
+}
+
+/*
+* Delta application was heavily cribbed from BinaryDelta.java in JGit, which
+* itself was heavily cribbed from <code>patch-delta.c</code> in the
+* GIT project. The original delta patching code was written by
+* Nicolas Pitre <nico@cam.org>.
+*/
+
+static int hdr_sz(
+ size_t *size,
+ const unsigned char **delta,
+ const unsigned char *end)
+{
+ const unsigned char *d = *delta;
+ size_t r = 0;
+ unsigned int c, shift = 0;
+
+ do {
+ if (d == end) {
+ git_error_set(GIT_ERROR_INVALID, "truncated delta");
+ return -1;
+ }
+
+ c = *d++;
+ r |= (c & 0x7f) << shift;
+ shift += 7;
+ } while (c & 0x80);
+ *delta = d;
+ *size = r;
+ return 0;
+}
+
+int git_delta_read_header(
+ size_t *base_out,
+ size_t *result_out,
+ const unsigned char *delta,
+ size_t delta_len)
+{
+ const unsigned char *delta_end = delta + delta_len;
+ if ((hdr_sz(base_out, &delta, delta_end) < 0) ||
+ (hdr_sz(result_out, &delta, delta_end) < 0))
+ return -1;
+ return 0;
+}
+
+#define DELTA_HEADER_BUFFER_LEN 16
+int git_delta_read_header_fromstream(
+ size_t *base_sz, size_t *res_sz, git_packfile_stream *stream)
+{
+ static const size_t buffer_len = DELTA_HEADER_BUFFER_LEN;
+ unsigned char buffer[DELTA_HEADER_BUFFER_LEN];
+ const unsigned char *delta, *delta_end;
+ size_t len;
+ ssize_t read;
+
+ len = read = 0;
+ while (len < buffer_len) {
+ read = git_packfile_stream_read(stream, &buffer[len], buffer_len - len);
+
+ if (read == 0)
+ break;
+
+ if (read == GIT_EBUFS)
+ continue;
+
+ len += read;
+ }
+
+ delta = buffer;
+ delta_end = delta + len;
+ if ((hdr_sz(base_sz, &delta, delta_end) < 0) ||
+ (hdr_sz(res_sz, &delta, delta_end) < 0))
+ return -1;
+
+ return 0;
+}
+
+int git_delta_apply(
+ void **out,
+ size_t *out_len,
+ const unsigned char *base,
+ size_t base_len,
+ const unsigned char *delta,
+ size_t delta_len)
+{
+ const unsigned char *delta_end = delta + delta_len;
+ size_t base_sz, res_sz, alloc_sz;
+ unsigned char *res_dp;
+
+ *out = NULL;
+ *out_len = 0;
+
+ /*
+ * Check that the base size matches the data we were given;
+ * if not we would underflow while accessing data from the
+ * base object, resulting in data corruption or segfault.
+ */
+ if ((hdr_sz(&base_sz, &delta, delta_end) < 0) || (base_sz != base_len)) {
+ git_error_set(GIT_ERROR_INVALID, "failed to apply delta: base size does not match given data");
+ return -1;
+ }
+
+ if (hdr_sz(&res_sz, &delta, delta_end) < 0) {
+ git_error_set(GIT_ERROR_INVALID, "failed to apply delta: base size does not match given data");
+ return -1;
+ }
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_sz, res_sz, 1);
+ res_dp = git__malloc(alloc_sz);
+ GIT_ERROR_CHECK_ALLOC(res_dp);
+
+ res_dp[res_sz] = '\0';
+ *out = res_dp;
+ *out_len = res_sz;
+
+ while (delta < delta_end) {
+ unsigned char cmd = *delta++;
+ if (cmd & 0x80) {
+ /* cmd is a copy instruction; copy from the base. */
+ size_t off = 0, len = 0, end;
+
+#define ADD_DELTA(o, shift) { if (delta < delta_end) (o) |= ((unsigned) *delta++ << shift); else goto fail; }
+ if (cmd & 0x01) ADD_DELTA(off, 0UL);
+ if (cmd & 0x02) ADD_DELTA(off, 8UL);
+ if (cmd & 0x04) ADD_DELTA(off, 16UL);
+ if (cmd & 0x08) ADD_DELTA(off, 24UL);
+
+ if (cmd & 0x10) ADD_DELTA(len, 0UL);
+ if (cmd & 0x20) ADD_DELTA(len, 8UL);
+ if (cmd & 0x40) ADD_DELTA(len, 16UL);
+ if (!len) len = 0x10000;
+#undef ADD_DELTA
+
+ if (GIT_ADD_SIZET_OVERFLOW(&end, off, len) ||
+ base_len < end || res_sz < len)
+ goto fail;
+
+ memcpy(res_dp, base + off, len);
+ res_dp += len;
+ res_sz -= len;
+
+ } else if (cmd) {
+ /*
+ * cmd is a literal insert instruction; copy from
+ * the delta stream itself.
+ */
+ if (delta_end - delta < cmd || res_sz < cmd)
+ goto fail;
+ memcpy(res_dp, delta, cmd);
+ delta += cmd;
+ res_dp += cmd;
+ res_sz -= cmd;
+
+ } else {
+ /* cmd == 0 is reserved for future encodings. */
+ goto fail;
+ }
+ }
+
+ if (delta != delta_end || res_sz)
+ goto fail;
+ return 0;
+
+fail:
+ git__free(*out);
+
+ *out = NULL;
+ *out_len = 0;
+
+ git_error_set(GIT_ERROR_INVALID, "failed to apply delta");
+ return -1;
+}
diff --git a/src/libgit2/delta.h b/src/libgit2/delta.h
new file mode 100644
index 0000000..f619873
--- /dev/null
+++ b/src/libgit2/delta.h
@@ -0,0 +1,136 @@
+/*
+ * diff-delta code taken from git.git. See diff-delta.c for details.
+ *
+ */
+#ifndef INCLUDE_git_delta_h__
+#define INCLUDE_git_delta_h__
+
+#include "common.h"
+
+#include "pack.h"
+
+typedef struct git_delta_index git_delta_index;
+
+/*
+ * git_delta_index_init: compute index data from given buffer
+ *
+ * This returns a pointer to a struct delta_index that should be passed to
+ * subsequent create_delta() calls, or to free_delta_index(). A NULL pointer
+ * is returned on failure. The given buffer must not be freed nor altered
+ * before free_delta_index() is called. The returned pointer must be freed
+ * using free_delta_index().
+ */
+extern int git_delta_index_init(
+ git_delta_index **out, const void *buf, size_t bufsize);
+
+/*
+ * Free the index created by git_delta_index_init()
+ */
+extern void git_delta_index_free(git_delta_index *index);
+
+/*
+ * Returns memory usage of delta index.
+ */
+extern size_t git_delta_index_size(git_delta_index *index);
+
+/*
+ * create_delta: create a delta from given index for the given buffer
+ *
+ * This function may be called multiple times with different buffers using
+ * the same delta_index pointer. If max_delta_size is non-zero and the
+ * resulting delta is to be larger than max_delta_size then NULL is returned.
+ * On success, a non-NULL pointer to the buffer with the delta data is
+ * returned and *delta_size is updated with its size. The returned buffer
+ * must be freed by the caller.
+ */
+extern int git_delta_create_from_index(
+ void **out,
+ size_t *out_size,
+ const struct git_delta_index *index,
+ const void *buf,
+ size_t bufsize,
+ size_t max_delta_size);
+
+/*
+ * diff_delta: create a delta from source buffer to target buffer
+ *
+ * If max_delta_size is non-zero and the resulting delta is to be larger
+ * than max_delta_size then GIT_EBUFS is returned. On success, a non-NULL
+ * pointer to the buffer with the delta data is returned and *delta_size is
+ * updated with its size. The returned buffer must be freed by the caller.
+ */
+GIT_INLINE(int) git_delta(
+ void **out, size_t *out_len,
+ const void *src_buf, size_t src_bufsize,
+ const void *trg_buf, size_t trg_bufsize,
+ size_t max_delta_size)
+{
+ git_delta_index *index;
+ int error = 0;
+
+ *out = NULL;
+ *out_len = 0;
+
+ if ((error = git_delta_index_init(&index, src_buf, src_bufsize)) < 0)
+ return error;
+
+ if (index) {
+ error = git_delta_create_from_index(out, out_len,
+ index, trg_buf, trg_bufsize, max_delta_size);
+
+ git_delta_index_free(index);
+ }
+
+ return error;
+}
+
+/* the smallest possible delta size is 4 bytes */
+#define GIT_DELTA_SIZE_MIN 4
+
+/**
+* Apply a git binary delta to recover the original content.
+* The caller is responsible for freeing the returned buffer.
+*
+* @param out the output buffer
+* @param out_len the length of the output buffer
+* @param base the base to copy from during copy instructions.
+* @param base_len number of bytes available at base.
+* @param delta the delta to execute copy/insert instructions from.
+* @param delta_len total number of bytes in the delta.
+* @return 0 on success or an error code
+*/
+extern int git_delta_apply(
+ void **out,
+ size_t *out_len,
+ const unsigned char *base,
+ size_t base_len,
+ const unsigned char *delta,
+ size_t delta_len);
+
+/**
+* Read the header of a git binary delta.
+*
+* @param base_out pointer to store the base size field.
+* @param result_out pointer to store the result size field.
+* @param delta the delta to execute copy/insert instructions from.
+* @param delta_len total number of bytes in the delta.
+* @return 0 on success or an error code
+*/
+extern int git_delta_read_header(
+ size_t *base_out,
+ size_t *result_out,
+ const unsigned char *delta,
+ size_t delta_len);
+
+/**
+ * Read the header of a git binary delta
+ *
+ * This variant reads just enough from the packfile stream to read the
+ * delta header.
+ */
+extern int git_delta_read_header_fromstream(
+ size_t *base_out,
+ size_t *result_out,
+ git_packfile_stream *stream);
+
+#endif
diff --git a/src/libgit2/describe.c b/src/libgit2/describe.c
new file mode 100644
index 0000000..0445347
--- /dev/null
+++ b/src/libgit2/describe.c
@@ -0,0 +1,912 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+
+#include "git2/describe.h"
+#include "git2/diff.h"
+#include "git2/status.h"
+
+#include "buf.h"
+#include "commit.h"
+#include "commit_list.h"
+#include "oidmap.h"
+#include "refs.h"
+#include "repository.h"
+#include "revwalk.h"
+#include "strarray.h"
+#include "tag.h"
+#include "vector.h"
+#include "wildmatch.h"
+
+/* Ported from https://github.com/git/git/blob/89dde7882f71f846ccd0359756d27bebc31108de/builtin/describe.c */
+
+struct commit_name {
+ git_tag *tag;
+ unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */
+ unsigned name_checked:1;
+ git_oid sha1;
+ char *path;
+
+ /* Khash workaround. They original key has to still be reachable */
+ git_oid peeled;
+};
+
+static void *oidmap_value_bykey(git_oidmap *map, const git_oid *key)
+{
+ return git_oidmap_get(map, key);
+}
+
+static struct commit_name *find_commit_name(
+ git_oidmap *names,
+ const git_oid *peeled)
+{
+ return (struct commit_name *)(oidmap_value_bykey(names, peeled));
+}
+
+static int replace_name(
+ git_tag **tag,
+ git_repository *repo,
+ struct commit_name *e,
+ unsigned int prio,
+ const git_oid *sha1)
+{
+ git_time_t e_time = 0, t_time = 0;
+
+ if (!e || e->prio < prio)
+ return 1;
+
+ if (e->prio == 2 && prio == 2) {
+ /* Multiple annotated tags point to the same commit.
+ * Select one to keep based upon their tagger date.
+ */
+ git_tag *t = NULL;
+
+ if (!e->tag) {
+ if (git_tag_lookup(&t, repo, &e->sha1) < 0)
+ return 1;
+ e->tag = t;
+ }
+
+ if (git_tag_lookup(&t, repo, sha1) < 0)
+ return 0;
+
+ *tag = t;
+
+ if (e->tag->tagger)
+ e_time = e->tag->tagger->when.time;
+
+ if (t->tagger)
+ t_time = t->tagger->when.time;
+
+ if (e_time < t_time)
+ return 1;
+ }
+
+ return 0;
+}
+
+static int add_to_known_names(
+ git_repository *repo,
+ git_oidmap *names,
+ const char *path,
+ const git_oid *peeled,
+ unsigned int prio,
+ const git_oid *sha1)
+{
+ struct commit_name *e = find_commit_name(names, peeled);
+ bool found = (e != NULL);
+
+ git_tag *tag = NULL;
+ if (replace_name(&tag, repo, e, prio, sha1)) {
+ if (!found) {
+ e = git__malloc(sizeof(struct commit_name));
+ GIT_ERROR_CHECK_ALLOC(e);
+
+ e->path = NULL;
+ e->tag = NULL;
+ }
+
+ if (e->tag)
+ git_tag_free(e->tag);
+ e->tag = tag;
+ e->prio = prio;
+ e->name_checked = 0;
+ git_oid_cpy(&e->sha1, sha1);
+ git__free(e->path);
+ e->path = git__strdup(path);
+ git_oid_cpy(&e->peeled, peeled);
+
+ if (!found && git_oidmap_set(names, &e->peeled, e) < 0)
+ return -1;
+ }
+ else
+ git_tag_free(tag);
+
+ return 0;
+}
+
+static int retrieve_peeled_tag_or_object_oid(
+ git_oid *peeled_out,
+ git_oid *ref_target_out,
+ git_repository *repo,
+ const char *refname)
+{
+ git_reference *ref;
+ git_object *peeled = NULL;
+ int error;
+
+ if ((error = git_reference_lookup_resolved(&ref, repo, refname, -1)) < 0)
+ return error;
+
+ if ((error = git_reference_peel(&peeled, ref, GIT_OBJECT_ANY)) < 0)
+ goto cleanup;
+
+ git_oid_cpy(ref_target_out, git_reference_target(ref));
+ git_oid_cpy(peeled_out, git_object_id(peeled));
+
+ if (git_oid_cmp(ref_target_out, peeled_out) != 0)
+ error = 1; /* The reference was pointing to a annotated tag */
+ else
+ error = 0; /* Any other object */
+
+cleanup:
+ git_reference_free(ref);
+ git_object_free(peeled);
+ return error;
+}
+
+struct git_describe_result {
+ int dirty;
+ int exact_match;
+ int fallback_to_id;
+ git_oid commit_id;
+ git_repository *repo;
+ struct commit_name *name;
+ struct possible_tag *tag;
+};
+
+struct get_name_data
+{
+ git_describe_options *opts;
+ git_repository *repo;
+ git_oidmap *names;
+ git_describe_result *result;
+};
+
+static int commit_name_dup(struct commit_name **out, struct commit_name *in)
+{
+ struct commit_name *name;
+
+ name = git__malloc(sizeof(struct commit_name));
+ GIT_ERROR_CHECK_ALLOC(name);
+
+ memcpy(name, in, sizeof(struct commit_name));
+ name->tag = NULL;
+ name->path = NULL;
+
+ if (in->tag && git_tag_dup(&name->tag, in->tag) < 0)
+ return -1;
+
+ name->path = git__strdup(in->path);
+ GIT_ERROR_CHECK_ALLOC(name->path);
+
+ *out = name;
+ return 0;
+}
+
+static int get_name(const char *refname, void *payload)
+{
+ struct get_name_data *data;
+ bool is_tag, is_annotated, all;
+ git_oid peeled, sha1;
+ unsigned int prio;
+ int error = 0;
+
+ data = (struct get_name_data *)payload;
+ is_tag = !git__prefixcmp(refname, GIT_REFS_TAGS_DIR);
+ all = data->opts->describe_strategy == GIT_DESCRIBE_ALL;
+
+ /* Reject anything outside refs/tags/ unless --all */
+ if (!all && !is_tag)
+ return 0;
+
+ /* Accept only tags that match the pattern, if given */
+ if (data->opts->pattern && (!is_tag || wildmatch(data->opts->pattern,
+ refname + strlen(GIT_REFS_TAGS_DIR), 0)))
+ return 0;
+
+ /* Is it annotated? */
+ if ((error = retrieve_peeled_tag_or_object_oid(
+ &peeled, &sha1, data->repo, refname)) < 0)
+ return error;
+
+ is_annotated = error;
+
+ /*
+ * By default, we only use annotated tags, but with --tags
+ * we fall back to lightweight ones (even without --tags,
+ * we still remember lightweight ones, only to give hints
+ * in an error message). --all allows any refs to be used.
+ */
+ if (is_annotated)
+ prio = 2;
+ else if (is_tag)
+ prio = 1;
+ else
+ prio = 0;
+
+ add_to_known_names(data->repo, data->names,
+ all ? refname + strlen(GIT_REFS_DIR) : refname + strlen(GIT_REFS_TAGS_DIR),
+ &peeled, prio, &sha1);
+ return 0;
+}
+
+struct possible_tag {
+ struct commit_name *name;
+ int depth;
+ int found_order;
+ unsigned flag_within;
+};
+
+static int possible_tag_dup(struct possible_tag **out, struct possible_tag *in)
+{
+ struct possible_tag *tag;
+ int error;
+
+ tag = git__malloc(sizeof(struct possible_tag));
+ GIT_ERROR_CHECK_ALLOC(tag);
+
+ memcpy(tag, in, sizeof(struct possible_tag));
+ tag->name = NULL;
+
+ if ((error = commit_name_dup(&tag->name, in->name)) < 0) {
+ git__free(tag);
+ *out = NULL;
+ return error;
+ }
+
+ *out = tag;
+ return 0;
+}
+
+static int compare_pt(const void *a_, const void *b_)
+{
+ struct possible_tag *a = (struct possible_tag *)a_;
+ struct possible_tag *b = (struct possible_tag *)b_;
+ if (a->depth != b->depth)
+ return a->depth - b->depth;
+ if (a->found_order != b->found_order)
+ return a->found_order - b->found_order;
+ return 0;
+}
+
+#define SEEN (1u << 0)
+
+static unsigned long finish_depth_computation(
+ git_pqueue *list,
+ git_revwalk *walk,
+ struct possible_tag *best)
+{
+ unsigned long seen_commits = 0;
+ int error, i;
+
+ while (git_pqueue_size(list) > 0) {
+ git_commit_list_node *c = git_pqueue_pop(list);
+ seen_commits++;
+ if (c->flags & best->flag_within) {
+ size_t index = 0;
+ while (git_pqueue_size(list) > index) {
+ git_commit_list_node *i = git_pqueue_get(list, index);
+ if (!(i->flags & best->flag_within))
+ break;
+ index++;
+ }
+ if (index > git_pqueue_size(list))
+ break;
+ } else
+ best->depth++;
+ for (i = 0; i < c->out_degree; i++) {
+ git_commit_list_node *p = c->parents[i];
+ if ((error = git_commit_list_parse(walk, p)) < 0)
+ return error;
+ if (!(p->flags & SEEN))
+ if ((error = git_pqueue_insert(list, p)) < 0)
+ return error;
+ p->flags |= c->flags;
+ }
+ }
+ return seen_commits;
+}
+
+static int display_name(git_str *buf, git_repository *repo, struct commit_name *n)
+{
+ if (n->prio == 2 && !n->tag) {
+ if (git_tag_lookup(&n->tag, repo, &n->sha1) < 0) {
+ git_error_set(GIT_ERROR_TAG, "annotated tag '%s' not available", n->path);
+ return -1;
+ }
+ }
+
+ if (n->tag && !n->name_checked) {
+ if (!git_tag_name(n->tag)) {
+ git_error_set(GIT_ERROR_TAG, "annotated tag '%s' has no embedded name", n->path);
+ return -1;
+ }
+
+ /* TODO: Cope with warnings
+ if (strcmp(n->tag->tag, all ? n->path + 5 : n->path))
+ warning(_("tag '%s' is really '%s' here"), n->tag->tag, n->path);
+ */
+
+ n->name_checked = 1;
+ }
+
+ if (n->tag)
+ git_str_printf(buf, "%s", git_tag_name(n->tag));
+ else
+ git_str_printf(buf, "%s", n->path);
+
+ return 0;
+}
+
+static int find_unique_abbrev_size(
+ int *out,
+ git_repository *repo,
+ const git_oid *oid_in,
+ unsigned int abbreviated_size)
+{
+ size_t size = abbreviated_size;
+ git_odb *odb;
+ git_oid dummy;
+ size_t hexsize;
+ int error;
+
+ if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
+ return error;
+
+ hexsize = git_oid_hexsize(repo->oid_type);
+
+ while (size < hexsize) {
+ if ((error = git_odb_exists_prefix(&dummy, odb, oid_in, size)) == 0) {
+ *out = (int) size;
+ return 0;
+ }
+
+ /* If the error wasn't that it's not unique, then it's a proper error */
+ if (error != GIT_EAMBIGUOUS)
+ return error;
+
+ /* Try again with a larger size */
+ size++;
+ }
+
+ /* If we didn't find any shorter prefix, we have to do the whole thing */
+ *out = (int)hexsize;
+
+ return 0;
+}
+
+static int show_suffix(
+ git_str *buf,
+ int depth,
+ git_repository *repo,
+ const git_oid *id,
+ unsigned int abbrev_size)
+{
+ int error, size = 0;
+
+ char hex_oid[GIT_OID_MAX_HEXSIZE];
+
+ if ((error = find_unique_abbrev_size(&size, repo, id, abbrev_size)) < 0)
+ return error;
+
+ git_oid_fmt(hex_oid, id);
+
+ git_str_printf(buf, "-%d-g", depth);
+
+ git_str_put(buf, hex_oid, size);
+
+ return git_str_oom(buf) ? -1 : 0;
+}
+
+#define MAX_CANDIDATES_TAGS FLAG_BITS - 1
+
+static int describe_not_found(const git_oid *oid, const char *message_format) {
+ char oid_str[GIT_OID_MAX_HEXSIZE + 1];
+ git_oid_tostr(oid_str, sizeof(oid_str), oid);
+
+ git_error_set(GIT_ERROR_DESCRIBE, message_format, oid_str);
+ return GIT_ENOTFOUND;
+}
+
+static int describe(
+ struct get_name_data *data,
+ git_commit *commit)
+{
+ struct commit_name *n;
+ struct possible_tag *best;
+ bool all, tags;
+ git_revwalk *walk = NULL;
+ git_pqueue list;
+ git_commit_list_node *cmit, *gave_up_on = NULL;
+ git_vector all_matches = GIT_VECTOR_INIT;
+ unsigned int match_cnt = 0, annotated_cnt = 0, cur_match;
+ unsigned long seen_commits = 0; /* TODO: Check long */
+ unsigned int unannotated_cnt = 0;
+ int error;
+
+ if (git_vector_init(&all_matches, MAX_CANDIDATES_TAGS, compare_pt) < 0)
+ return -1;
+
+ if ((error = git_pqueue_init(&list, 0, 2, git_commit_list_time_cmp)) < 0)
+ goto cleanup;
+
+ all = data->opts->describe_strategy == GIT_DESCRIBE_ALL;
+ tags = data->opts->describe_strategy == GIT_DESCRIBE_TAGS;
+
+ git_oid_cpy(&data->result->commit_id, git_commit_id(commit));
+
+ n = find_commit_name(data->names, git_commit_id(commit));
+ if (n && (tags || all || n->prio == 2)) {
+ /*
+ * Exact match to an existing ref.
+ */
+ data->result->exact_match = 1;
+ if ((error = commit_name_dup(&data->result->name, n)) < 0)
+ goto cleanup;
+
+ goto cleanup;
+ }
+
+ if (!data->opts->max_candidates_tags) {
+ error = describe_not_found(
+ git_commit_id(commit),
+ "cannot describe - no tag exactly matches '%s'");
+
+ goto cleanup;
+ }
+
+ if ((error = git_revwalk_new(&walk, git_commit_owner(commit))) < 0)
+ goto cleanup;
+
+ if ((cmit = git_revwalk__commit_lookup(walk, git_commit_id(commit))) == NULL)
+ goto cleanup;
+
+ if ((error = git_commit_list_parse(walk, cmit)) < 0)
+ goto cleanup;
+
+ cmit->flags = SEEN;
+
+ if ((error = git_pqueue_insert(&list, cmit)) < 0)
+ goto cleanup;
+
+ while (git_pqueue_size(&list) > 0)
+ {
+ int i;
+
+ git_commit_list_node *c = (git_commit_list_node *)git_pqueue_pop(&list);
+ seen_commits++;
+
+ n = find_commit_name(data->names, &c->oid);
+
+ if (n) {
+ if (!tags && !all && n->prio < 2) {
+ unannotated_cnt++;
+ } else if (match_cnt < data->opts->max_candidates_tags) {
+ struct possible_tag *t = git__malloc(sizeof(struct commit_name));
+ GIT_ERROR_CHECK_ALLOC(t);
+ if ((error = git_vector_insert(&all_matches, t)) < 0)
+ goto cleanup;
+
+ match_cnt++;
+
+ t->name = n;
+ t->depth = seen_commits - 1;
+ t->flag_within = 1u << match_cnt;
+ t->found_order = match_cnt;
+ c->flags |= t->flag_within;
+ if (n->prio == 2)
+ annotated_cnt++;
+ }
+ else {
+ gave_up_on = c;
+ break;
+ }
+ }
+
+ for (cur_match = 0; cur_match < match_cnt; cur_match++) {
+ struct possible_tag *t = git_vector_get(&all_matches, cur_match);
+ if (!(c->flags & t->flag_within))
+ t->depth++;
+ }
+
+ if (annotated_cnt && (git_pqueue_size(&list) == 0)) {
+ /*
+ if (debug) {
+ char oid_str[GIT_OID_MAX_HEXSIZE + 1];
+ git_oid_tostr(oid_str, sizeof(oid_str), &c->oid);
+
+ fprintf(stderr, "finished search at %s\n", oid_str);
+ }
+ */
+ break;
+ }
+ for (i = 0; i < c->out_degree; i++) {
+ git_commit_list_node *p = c->parents[i];
+ if ((error = git_commit_list_parse(walk, p)) < 0)
+ goto cleanup;
+ if (!(p->flags & SEEN))
+ if ((error = git_pqueue_insert(&list, p)) < 0)
+ goto cleanup;
+ p->flags |= c->flags;
+
+ if (data->opts->only_follow_first_parent)
+ break;
+ }
+ }
+
+ if (!match_cnt) {
+ if (data->opts->show_commit_oid_as_fallback) {
+ data->result->fallback_to_id = 1;
+ git_oid_cpy(&data->result->commit_id, &cmit->oid);
+
+ goto cleanup;
+ }
+ if (unannotated_cnt) {
+ error = describe_not_found(git_commit_id(commit),
+ "cannot describe - "
+ "no annotated tags can describe '%s'; "
+ "however, there were unannotated tags.");
+ goto cleanup;
+ }
+ else {
+ error = describe_not_found(git_commit_id(commit),
+ "cannot describe - "
+ "no tags can describe '%s'.");
+ goto cleanup;
+ }
+ }
+
+ git_vector_sort(&all_matches);
+
+ best = (struct possible_tag *)git_vector_get(&all_matches, 0);
+
+ if (gave_up_on) {
+ if ((error = git_pqueue_insert(&list, gave_up_on)) < 0)
+ goto cleanup;
+ seen_commits--;
+ }
+ if ((error = finish_depth_computation(
+ &list, walk, best)) < 0)
+ goto cleanup;
+
+ seen_commits += error;
+ if ((error = possible_tag_dup(&data->result->tag, best)) < 0)
+ goto cleanup;
+
+ /*
+ {
+ static const char *prio_names[] = {
+ "head", "lightweight", "annotated",
+ };
+
+ char oid_str[GIT_OID_MAX_HEXSIZE + 1];
+
+ if (debug) {
+ for (cur_match = 0; cur_match < match_cnt; cur_match++) {
+ struct possible_tag *t = (struct possible_tag *)git_vector_get(&all_matches, cur_match);
+ fprintf(stderr, " %-11s %8d %s\n",
+ prio_names[t->name->prio],
+ t->depth, t->name->path);
+ }
+ fprintf(stderr, "traversed %lu commits\n", seen_commits);
+ if (gave_up_on) {
+ git_oid_tostr(oid_str, sizeof(oid_str), &gave_up_on->oid);
+ fprintf(stderr,
+ "more than %i tags found; listed %i most recent\n"
+ "gave up search at %s\n",
+ data->opts->max_candidates_tags, data->opts->max_candidates_tags,
+ oid_str);
+ }
+ }
+ }
+ */
+
+ git_oid_cpy(&data->result->commit_id, &cmit->oid);
+
+cleanup:
+ {
+ size_t i;
+ struct possible_tag *match;
+ git_vector_foreach(&all_matches, i, match) {
+ git__free(match);
+ }
+ }
+ git_vector_free(&all_matches);
+ git_pqueue_free(&list);
+ git_revwalk_free(walk);
+ return error;
+}
+
+static int normalize_options(
+ git_describe_options *dst,
+ const git_describe_options *src)
+{
+ git_describe_options default_options = GIT_DESCRIBE_OPTIONS_INIT;
+ if (!src) src = &default_options;
+
+ *dst = *src;
+
+ if (dst->max_candidates_tags > GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS)
+ dst->max_candidates_tags = GIT_DESCRIBE_DEFAULT_MAX_CANDIDATES_TAGS;
+
+ return 0;
+}
+
+int git_describe_commit(
+ git_describe_result **result,
+ git_object *committish,
+ git_describe_options *opts)
+{
+ struct get_name_data data;
+ struct commit_name *name;
+ git_commit *commit;
+ int error = -1;
+ git_describe_options normalized;
+
+ GIT_ASSERT_ARG(result);
+ GIT_ASSERT_ARG(committish);
+
+ data.result = git__calloc(1, sizeof(git_describe_result));
+ GIT_ERROR_CHECK_ALLOC(data.result);
+ data.result->repo = git_object_owner(committish);
+
+ data.repo = git_object_owner(committish);
+
+ if ((error = normalize_options(&normalized, opts)) < 0)
+ return error;
+
+ GIT_ERROR_CHECK_VERSION(
+ &normalized,
+ GIT_DESCRIBE_OPTIONS_VERSION,
+ "git_describe_options");
+ data.opts = &normalized;
+
+ if ((error = git_oidmap_new(&data.names)) < 0)
+ return error;
+
+ /** TODO: contains to be implemented */
+
+ if ((error = git_object_peel((git_object **)(&commit), committish, GIT_OBJECT_COMMIT)) < 0)
+ goto cleanup;
+
+ if ((error = git_reference_foreach_name(
+ git_object_owner(committish),
+ get_name, &data)) < 0)
+ goto cleanup;
+
+ if (git_oidmap_size(data.names) == 0 && !normalized.show_commit_oid_as_fallback) {
+ git_error_set(GIT_ERROR_DESCRIBE, "cannot describe - "
+ "no reference found, cannot describe anything.");
+ error = -1;
+ goto cleanup;
+ }
+
+ if ((error = describe(&data, commit)) < 0)
+ goto cleanup;
+
+cleanup:
+ git_commit_free(commit);
+
+ git_oidmap_foreach_value(data.names, name, {
+ git_tag_free(name->tag);
+ git__free(name->path);
+ git__free(name);
+ });
+
+ git_oidmap_free(data.names);
+
+ if (error < 0)
+ git_describe_result_free(data.result);
+ else
+ *result = data.result;
+
+ return error;
+}
+
+int git_describe_workdir(
+ git_describe_result **out,
+ git_repository *repo,
+ git_describe_options *opts)
+{
+ int error;
+ git_oid current_id;
+ git_status_list *status = NULL;
+ git_status_options status_opts = GIT_STATUS_OPTIONS_INIT;
+ git_describe_result *result = NULL;
+ git_object *commit;
+
+ if ((error = git_reference_name_to_id(&current_id, repo, GIT_HEAD_FILE)) < 0)
+ return error;
+
+ if ((error = git_object_lookup(&commit, repo, &current_id, GIT_OBJECT_COMMIT)) < 0)
+ return error;
+
+ /* The first step is to perform a describe of HEAD, so we can leverage this */
+ if ((error = git_describe_commit(&result, commit, opts)) < 0)
+ goto out;
+
+ if ((error = git_status_list_new(&status, repo, &status_opts)) < 0)
+ goto out;
+
+
+ if (git_status_list_entrycount(status) > 0)
+ result->dirty = 1;
+
+out:
+ git_object_free(commit);
+ git_status_list_free(status);
+
+ if (error < 0)
+ git_describe_result_free(result);
+ else
+ *out = result;
+
+ return error;
+}
+
+static int normalize_format_options(
+ git_describe_format_options *dst,
+ const git_describe_format_options *src)
+{
+ if (!src) {
+ git_describe_format_options_init(dst, GIT_DESCRIBE_FORMAT_OPTIONS_VERSION);
+ return 0;
+ }
+
+ memcpy(dst, src, sizeof(git_describe_format_options));
+ return 0;
+}
+
+static int git_describe__format(
+ git_str *out,
+ const git_describe_result *result,
+ const git_describe_format_options *given)
+{
+ int error;
+ git_repository *repo;
+ struct commit_name *name;
+ git_describe_format_options opts;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(result);
+
+ GIT_ERROR_CHECK_VERSION(given, GIT_DESCRIBE_FORMAT_OPTIONS_VERSION, "git_describe_format_options");
+ normalize_format_options(&opts, given);
+
+ if (opts.always_use_long_format && opts.abbreviated_size == 0) {
+ git_error_set(GIT_ERROR_DESCRIBE, "cannot describe - "
+ "'always_use_long_format' is incompatible with a zero"
+ "'abbreviated_size'");
+ return -1;
+ }
+
+
+ repo = result->repo;
+
+ /* If we did find an exact match, then it's the easier method */
+ if (result->exact_match) {
+ name = result->name;
+ if ((error = display_name(out, repo, name)) < 0)
+ return error;
+
+ if (opts.always_use_long_format) {
+ const git_oid *id = name->tag ? git_tag_target_id(name->tag) : &result->commit_id;
+ if ((error = show_suffix(out, 0, repo, id, opts.abbreviated_size)) < 0)
+ return error;
+ }
+
+ if (result->dirty && opts.dirty_suffix)
+ git_str_puts(out, opts.dirty_suffix);
+
+ return git_str_oom(out) ? -1 : 0;
+ }
+
+ /* If we didn't find *any* tags, we fall back to the commit's id */
+ if (result->fallback_to_id) {
+ char hex_oid[GIT_OID_MAX_HEXSIZE + 1] = {0};
+ int size = 0;
+
+ if ((error = find_unique_abbrev_size(
+ &size, repo, &result->commit_id, opts.abbreviated_size)) < 0)
+ return -1;
+
+ git_oid_fmt(hex_oid, &result->commit_id);
+ git_str_put(out, hex_oid, size);
+
+ if (result->dirty && opts.dirty_suffix)
+ git_str_puts(out, opts.dirty_suffix);
+
+ return git_str_oom(out) ? -1 : 0;
+ }
+
+ /* Lastly, if we found a matching tag, we show that */
+ name = result->tag->name;
+
+ if ((error = display_name(out, repo, name)) < 0)
+ return error;
+
+ if (opts.abbreviated_size) {
+ if ((error = show_suffix(out, result->tag->depth, repo,
+ &result->commit_id, opts.abbreviated_size)) < 0)
+ return error;
+ }
+
+ if (result->dirty && opts.dirty_suffix) {
+ git_str_puts(out, opts.dirty_suffix);
+ }
+
+ return git_str_oom(out) ? -1 : 0;
+}
+
+int git_describe_format(
+ git_buf *out,
+ const git_describe_result *result,
+ const git_describe_format_options *given)
+{
+ GIT_BUF_WRAP_PRIVATE(out, git_describe__format, result, given);
+}
+
+void git_describe_result_free(git_describe_result *result)
+{
+ if (result == NULL)
+ return;
+
+ if (result->name) {
+ git_tag_free(result->name->tag);
+ git__free(result->name->path);
+ git__free(result->name);
+ }
+
+ if (result->tag) {
+ git_tag_free(result->tag->name->tag);
+ git__free(result->tag->name->path);
+ git__free(result->tag->name);
+ git__free(result->tag);
+ }
+
+ git__free(result);
+}
+
+int git_describe_options_init(git_describe_options *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_describe_options, GIT_DESCRIBE_OPTIONS_INIT);
+ return 0;
+}
+
+#ifndef GIT_DEPRECATE_HARD
+int git_describe_init_options(git_describe_options *opts, unsigned int version)
+{
+ return git_describe_options_init(opts, version);
+}
+#endif
+
+int git_describe_format_options_init(git_describe_format_options *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_describe_format_options, GIT_DESCRIBE_FORMAT_OPTIONS_INIT);
+ return 0;
+}
+
+#ifndef GIT_DEPRECATE_HARD
+int git_describe_init_format_options(git_describe_format_options *opts, unsigned int version)
+{
+ return git_describe_format_options_init(opts, version);
+}
+#endif
diff --git a/src/libgit2/diff.c b/src/libgit2/diff.c
new file mode 100644
index 0000000..db12ccd
--- /dev/null
+++ b/src/libgit2/diff.c
@@ -0,0 +1,402 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "diff.h"
+
+#include "common.h"
+#include "buf.h"
+#include "patch.h"
+#include "email.h"
+#include "commit.h"
+#include "index.h"
+#include "diff_generate.h"
+
+#include "git2/version.h"
+#include "git2/email.h"
+
+struct patch_id_args {
+ git_diff *diff;
+ git_hash_ctx ctx;
+ git_oid result;
+ git_oid_t oid_type;
+ int first_file;
+};
+
+GIT_INLINE(const char *) diff_delta__path(const git_diff_delta *delta)
+{
+ const char *str = delta->old_file.path;
+
+ if (!str ||
+ delta->status == GIT_DELTA_ADDED ||
+ delta->status == GIT_DELTA_RENAMED ||
+ delta->status == GIT_DELTA_COPIED)
+ str = delta->new_file.path;
+
+ return str;
+}
+
+int git_diff_delta__cmp(const void *a, const void *b)
+{
+ const git_diff_delta *da = a, *db = b;
+ int val = strcmp(diff_delta__path(da), diff_delta__path(db));
+ return val ? val : ((int)da->status - (int)db->status);
+}
+
+int git_diff_delta__casecmp(const void *a, const void *b)
+{
+ const git_diff_delta *da = a, *db = b;
+ int val = strcasecmp(diff_delta__path(da), diff_delta__path(db));
+ return val ? val : ((int)da->status - (int)db->status);
+}
+
+int git_diff__entry_cmp(const void *a, const void *b)
+{
+ const git_index_entry *entry_a = a;
+ const git_index_entry *entry_b = b;
+
+ return strcmp(entry_a->path, entry_b->path);
+}
+
+int git_diff__entry_icmp(const void *a, const void *b)
+{
+ const git_index_entry *entry_a = a;
+ const git_index_entry *entry_b = b;
+
+ return strcasecmp(entry_a->path, entry_b->path);
+}
+
+void git_diff_free(git_diff *diff)
+{
+ if (!diff)
+ return;
+
+ GIT_REFCOUNT_DEC(diff, diff->free_fn);
+}
+
+void git_diff_addref(git_diff *diff)
+{
+ GIT_REFCOUNT_INC(diff);
+}
+
+size_t git_diff_num_deltas(const git_diff *diff)
+{
+ GIT_ASSERT_ARG(diff);
+ return diff->deltas.length;
+}
+
+size_t git_diff_num_deltas_of_type(const git_diff *diff, git_delta_t type)
+{
+ size_t i, count = 0;
+ const git_diff_delta *delta;
+
+ GIT_ASSERT_ARG(diff);
+
+ git_vector_foreach(&diff->deltas, i, delta) {
+ count += (delta->status == type);
+ }
+
+ return count;
+}
+
+const git_diff_delta *git_diff_get_delta(const git_diff *diff, size_t idx)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(diff, NULL);
+ return git_vector_get(&diff->deltas, idx);
+}
+
+int git_diff_is_sorted_icase(const git_diff *diff)
+{
+ return (diff->opts.flags & GIT_DIFF_IGNORE_CASE) != 0;
+}
+
+int git_diff_get_perfdata(git_diff_perfdata *out, const git_diff *diff)
+{
+ GIT_ASSERT_ARG(out);
+ GIT_ERROR_CHECK_VERSION(out, GIT_DIFF_PERFDATA_VERSION, "git_diff_perfdata");
+ out->stat_calls = diff->perf.stat_calls;
+ out->oid_calculations = diff->perf.oid_calculations;
+ return 0;
+}
+
+int git_diff_foreach(
+ git_diff *diff,
+ git_diff_file_cb file_cb,
+ git_diff_binary_cb binary_cb,
+ git_diff_hunk_cb hunk_cb,
+ git_diff_line_cb data_cb,
+ void *payload)
+{
+ int error = 0;
+ git_diff_delta *delta;
+ size_t idx;
+
+ GIT_ASSERT_ARG(diff);
+
+ git_vector_foreach(&diff->deltas, idx, delta) {
+ git_patch *patch;
+
+ /* check flags against patch status */
+ if (git_diff_delta__should_skip(&diff->opts, delta))
+ continue;
+
+ if ((error = git_patch_from_diff(&patch, diff, idx)) != 0)
+ break;
+
+ error = git_patch__invoke_callbacks(patch, file_cb, binary_cb,
+ hunk_cb, data_cb, payload);
+ git_patch_free(patch);
+
+ if (error)
+ break;
+ }
+
+ return error;
+}
+
+#ifndef GIT_DEPRECATE_HARD
+
+int git_diff_format_email(
+ git_buf *out,
+ git_diff *diff,
+ const git_diff_format_email_options *opts)
+{
+ git_email_create_options email_create_opts = GIT_EMAIL_CREATE_OPTIONS_INIT;
+ git_str email = GIT_STR_INIT;
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(diff);
+ GIT_ASSERT_ARG(opts && opts->summary && opts->id && opts->author);
+
+ GIT_ERROR_CHECK_VERSION(opts,
+ GIT_DIFF_FORMAT_EMAIL_OPTIONS_VERSION,
+ "git_format_email_options");
+
+ /* This is a `git_buf` special case; subsequent calls append. */
+ email.ptr = out->ptr;
+ email.asize = out->reserved;
+ email.size = out->size;
+
+ out->ptr = git_str__initstr;
+ out->reserved = 0;
+ out->size = 0;
+
+ if ((opts->flags & GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER) != 0)
+ email_create_opts.subject_prefix = "";
+
+ error = git_email__append_from_diff(&email, diff, opts->patch_no,
+ opts->total_patches, opts->id, opts->summary, opts->body,
+ opts->author, &email_create_opts);
+
+ if (error < 0)
+ goto done;
+
+ error = git_buf_fromstr(out, &email);
+
+done:
+ git_str_dispose(&email);
+ return error;
+}
+
+int git_diff_commit_as_email(
+ git_buf *out,
+ git_repository *repo,
+ git_commit *commit,
+ size_t patch_no,
+ size_t total_patches,
+ uint32_t flags,
+ const git_diff_options *diff_opts)
+{
+ git_diff *diff = NULL;
+ git_email_create_options opts = GIT_EMAIL_CREATE_OPTIONS_INIT;
+ const git_oid *commit_id;
+ const char *summary, *body;
+ const git_signature *author;
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(commit);
+
+ commit_id = git_commit_id(commit);
+ summary = git_commit_summary(commit);
+ body = git_commit_body(commit);
+ author = git_commit_author(commit);
+
+ if ((flags & GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER) != 0)
+ opts.subject_prefix = "";
+
+ if ((error = git_diff__commit(&diff, repo, commit, diff_opts)) < 0)
+ return error;
+
+ error = git_email_create_from_diff(out, diff, patch_no, total_patches, commit_id, summary, body, author, &opts);
+
+ git_diff_free(diff);
+ return error;
+}
+
+int git_diff_init_options(git_diff_options *opts, unsigned int version)
+{
+ return git_diff_options_init(opts, version);
+}
+
+int git_diff_find_init_options(
+ git_diff_find_options *opts, unsigned int version)
+{
+ return git_diff_find_options_init(opts, version);
+}
+
+int git_diff_format_email_options_init(
+ git_diff_format_email_options *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_diff_format_email_options,
+ GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT);
+ return 0;
+}
+
+int git_diff_format_email_init_options(
+ git_diff_format_email_options *opts, unsigned int version)
+{
+ return git_diff_format_email_options_init(opts, version);
+}
+
+#endif
+
+int git_diff_options_init(git_diff_options *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_diff_options, GIT_DIFF_OPTIONS_INIT);
+ return 0;
+}
+
+int git_diff_find_options_init(
+ git_diff_find_options *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_diff_find_options, GIT_DIFF_FIND_OPTIONS_INIT);
+ return 0;
+}
+
+static int flush_hunk(git_oid *result, struct patch_id_args *args)
+{
+ git_hash_ctx *ctx = &args->ctx;
+ git_oid hash;
+ unsigned short carry = 0;
+ size_t i;
+ int error;
+
+ if ((error = git_hash_final(hash.id, ctx)) < 0 ||
+ (error = git_hash_init(ctx)) < 0)
+ return error;
+
+ for (i = 0; i < git_oid_size(args->oid_type); i++) {
+ carry += result->id[i] + hash.id[i];
+ result->id[i] = (unsigned char)carry;
+ carry >>= 8;
+ }
+
+ return 0;
+}
+
+static void strip_spaces(git_str *buf)
+{
+ char *src = buf->ptr, *dst = buf->ptr;
+ char c;
+ size_t len = 0;
+
+ while ((c = *src++) != '\0') {
+ if (!git__isspace(c)) {
+ *dst++ = c;
+ len++;
+ }
+ }
+
+ git_str_truncate(buf, len);
+}
+
+static int diff_patchid_print_callback_to_buf(
+ const git_diff_delta *delta,
+ const git_diff_hunk *hunk,
+ const git_diff_line *line,
+ void *payload)
+{
+ struct patch_id_args *args = (struct patch_id_args *) payload;
+ git_str buf = GIT_STR_INIT;
+ int error = 0;
+
+ if (line->origin == GIT_DIFF_LINE_CONTEXT_EOFNL ||
+ line->origin == GIT_DIFF_LINE_ADD_EOFNL ||
+ line->origin == GIT_DIFF_LINE_DEL_EOFNL)
+ goto out;
+
+ if ((error = git_diff_print_callback__to_buf(delta, hunk,
+ line, &buf)) < 0)
+ goto out;
+
+ strip_spaces(&buf);
+
+ if (line->origin == GIT_DIFF_LINE_FILE_HDR &&
+ !args->first_file &&
+ (error = flush_hunk(&args->result, args) < 0))
+ goto out;
+
+ if ((error = git_hash_update(&args->ctx, buf.ptr, buf.size)) < 0)
+ goto out;
+
+ if (line->origin == GIT_DIFF_LINE_FILE_HDR && args->first_file)
+ args->first_file = 0;
+
+out:
+ git_str_dispose(&buf);
+ return error;
+}
+
+int git_diff_patchid_options_init(git_diff_patchid_options *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_diff_patchid_options, GIT_DIFF_PATCHID_OPTIONS_INIT);
+ return 0;
+}
+
+int git_diff_patchid(git_oid *out, git_diff *diff, git_diff_patchid_options *opts)
+{
+ struct patch_id_args args;
+ git_hash_algorithm_t algorithm;
+ int error;
+
+ GIT_ERROR_CHECK_VERSION(
+ opts, GIT_DIFF_PATCHID_OPTIONS_VERSION, "git_diff_patchid_options");
+
+ algorithm = git_oid_algorithm(diff->opts.oid_type);
+
+ memset(&args, 0, sizeof(args));
+ args.diff = diff;
+ args.first_file = 1;
+ args.oid_type = diff->opts.oid_type;
+ if ((error = git_hash_ctx_init(&args.ctx, algorithm)) < 0)
+ goto out;
+
+ if ((error = git_diff_print(diff,
+ GIT_DIFF_FORMAT_PATCH_ID,
+ diff_patchid_print_callback_to_buf,
+ &args)) < 0)
+ goto out;
+
+ if ((error = (flush_hunk(&args.result, &args))) < 0)
+ goto out;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ args.result.type = diff->opts.oid_type;
+#endif
+
+ git_oid_cpy(out, &args.result);
+
+out:
+ git_hash_ctx_cleanup(&args.ctx);
+ return error;
+}
diff --git a/src/libgit2/diff.h b/src/libgit2/diff.h
new file mode 100644
index 0000000..f21b276
--- /dev/null
+++ b/src/libgit2/diff.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_diff_h__
+#define INCLUDE_diff_h__
+
+#include "common.h"
+
+#include "git2/diff.h"
+#include "git2/patch.h"
+#include "git2/sys/diff.h"
+#include "git2/oid.h"
+
+#include "vector.h"
+#include "iterator.h"
+#include "repository.h"
+#include "pool.h"
+#include "odb.h"
+
+#define DIFF_OLD_PREFIX_DEFAULT "a/"
+#define DIFF_NEW_PREFIX_DEFAULT "b/"
+
+typedef enum {
+ GIT_DIFF_TYPE_UNKNOWN = 0,
+ GIT_DIFF_TYPE_GENERATED = 1,
+ GIT_DIFF_TYPE_PARSED = 2
+} git_diff_origin_t;
+
+struct git_diff {
+ git_refcount rc;
+ git_repository *repo;
+ git_attr_session attrsession;
+ git_diff_origin_t type;
+ git_diff_options opts;
+ git_vector deltas; /* vector of git_diff_delta */
+ git_pool pool;
+ git_iterator_t old_src;
+ git_iterator_t new_src;
+ git_diff_perfdata perf;
+
+ int (*strcomp)(const char *, const char *);
+ int (*strncomp)(const char *, const char *, size_t);
+ int (*pfxcomp)(const char *str, const char *pfx);
+ int (*entrycomp)(const void *a, const void *b);
+
+ int (*patch_fn)(git_patch **out, git_diff *diff, size_t idx);
+ void (*free_fn)(git_diff *diff);
+};
+
+extern int git_diff_delta__format_file_header(
+ git_str *out,
+ const git_diff_delta *delta,
+ const char *oldpfx,
+ const char *newpfx,
+ int oid_strlen,
+ bool print_index);
+
+extern int git_diff_delta__cmp(const void *a, const void *b);
+extern int git_diff_delta__casecmp(const void *a, const void *b);
+
+extern int git_diff__entry_cmp(const void *a, const void *b);
+extern int git_diff__entry_icmp(const void *a, const void *b);
+
+#endif
diff --git a/src/libgit2/diff_driver.c b/src/libgit2/diff_driver.c
new file mode 100644
index 0000000..5f25fdb
--- /dev/null
+++ b/src/libgit2/diff_driver.c
@@ -0,0 +1,522 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "diff_driver.h"
+
+#include "git2/attr.h"
+
+#include "common.h"
+#include "diff.h"
+#include "strmap.h"
+#include "map.h"
+#include "config.h"
+#include "regexp.h"
+#include "repository.h"
+
+typedef enum {
+ DIFF_DRIVER_AUTO = 0,
+ DIFF_DRIVER_BINARY = 1,
+ DIFF_DRIVER_TEXT = 2,
+ DIFF_DRIVER_PATTERNLIST = 3
+} git_diff_driver_t;
+
+typedef struct {
+ git_regexp re;
+ int flags;
+} git_diff_driver_pattern;
+
+enum {
+ REG_NEGATE = (1 << 15) /* get out of the way of existing flags */
+};
+
+/* data for finding function context for a given file type */
+struct git_diff_driver {
+ git_diff_driver_t type;
+ uint32_t binary_flags;
+ uint32_t other_flags;
+ git_array_t(git_diff_driver_pattern) fn_patterns;
+ git_regexp word_pattern;
+ char name[GIT_FLEX_ARRAY];
+};
+
+#include "userdiff.h"
+
+struct git_diff_driver_registry {
+ git_strmap *drivers;
+};
+
+#define FORCE_DIFFABLE (GIT_DIFF_FORCE_TEXT | GIT_DIFF_FORCE_BINARY)
+
+static git_diff_driver diff_driver_auto = { DIFF_DRIVER_AUTO, 0, 0 };
+static git_diff_driver diff_driver_binary = { DIFF_DRIVER_BINARY, GIT_DIFF_FORCE_BINARY, 0 };
+static git_diff_driver diff_driver_text = { DIFF_DRIVER_TEXT, GIT_DIFF_FORCE_TEXT, 0 };
+
+git_diff_driver_registry *git_diff_driver_registry_new(void)
+{
+ git_diff_driver_registry *reg =
+ git__calloc(1, sizeof(git_diff_driver_registry));
+ if (!reg)
+ return NULL;
+
+ if (git_strmap_new(&reg->drivers) < 0) {
+ git_diff_driver_registry_free(reg);
+ return NULL;
+ }
+
+ return reg;
+}
+
+void git_diff_driver_registry_free(git_diff_driver_registry *reg)
+{
+ git_diff_driver *drv;
+
+ if (!reg)
+ return;
+
+ git_strmap_foreach_value(reg->drivers, drv, git_diff_driver_free(drv));
+ git_strmap_free(reg->drivers);
+ git__free(reg);
+}
+
+static int diff_driver_add_patterns(
+ git_diff_driver *drv, const char *regex_str, int regex_flags)
+{
+ int error = 0;
+ const char *scan, *end;
+ git_diff_driver_pattern *pat = NULL;
+ git_str buf = GIT_STR_INIT;
+
+ for (scan = regex_str; scan; scan = end) {
+ /* get pattern to fill in */
+ if ((pat = git_array_alloc(drv->fn_patterns)) == NULL) {
+ return -1;
+ }
+
+ pat->flags = regex_flags;
+ if (*scan == '!') {
+ pat->flags |= REG_NEGATE;
+ ++scan;
+ }
+
+ if ((end = strchr(scan, '\n')) != NULL) {
+ error = git_str_set(&buf, scan, end - scan);
+ end++;
+ } else {
+ error = git_str_sets(&buf, scan);
+ }
+ if (error < 0)
+ break;
+
+ if ((error = git_regexp_compile(&pat->re, buf.ptr, regex_flags)) != 0) {
+ /*
+ * TODO: issue a warning
+ */
+ }
+ }
+
+ if (error && pat != NULL)
+ (void)git_array_pop(drv->fn_patterns); /* release last item */
+ git_str_dispose(&buf);
+
+ /* We want to ignore bad patterns, so return success regardless */
+ return 0;
+}
+
+static int diff_driver_xfuncname(const git_config_entry *entry, void *payload)
+{
+ return diff_driver_add_patterns(payload, entry->value, 0);
+}
+
+static int diff_driver_funcname(const git_config_entry *entry, void *payload)
+{
+ return diff_driver_add_patterns(payload, entry->value, 0);
+}
+
+static git_diff_driver_registry *git_repository_driver_registry(
+ git_repository *repo)
+{
+ git_diff_driver_registry *reg = git_atomic_load(repo->diff_drivers), *newreg;
+ if (reg)
+ return reg;
+
+ newreg = git_diff_driver_registry_new();
+ if (!newreg) {
+ git_error_set(GIT_ERROR_REPOSITORY, "unable to create diff driver registry");
+ return newreg;
+ }
+ reg = git_atomic_compare_and_swap(&repo->diff_drivers, NULL, newreg);
+ if (!reg) {
+ reg = newreg;
+ } else {
+ /* if we race, free losing allocation */
+ git_diff_driver_registry_free(newreg);
+ }
+ return reg;
+}
+
+static int diff_driver_alloc(
+ git_diff_driver **out, size_t *namelen_out, const char *name)
+{
+ git_diff_driver *driver;
+ size_t driverlen = sizeof(git_diff_driver),
+ namelen = strlen(name),
+ alloclen;
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, driverlen, namelen);
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
+
+ driver = git__calloc(1, alloclen);
+ GIT_ERROR_CHECK_ALLOC(driver);
+
+ memcpy(driver->name, name, namelen);
+
+ *out = driver;
+
+ if (namelen_out)
+ *namelen_out = namelen;
+
+ return 0;
+}
+
+static int git_diff_driver_builtin(
+ git_diff_driver **out,
+ git_diff_driver_registry *reg,
+ const char *driver_name)
+{
+ git_diff_driver_definition *ddef = NULL;
+ git_diff_driver *drv = NULL;
+ int error = 0;
+ size_t idx;
+
+ for (idx = 0; idx < ARRAY_SIZE(builtin_defs); ++idx) {
+ if (!strcasecmp(driver_name, builtin_defs[idx].name)) {
+ ddef = &builtin_defs[idx];
+ break;
+ }
+ }
+ if (!ddef)
+ goto done;
+
+ if ((error = diff_driver_alloc(&drv, NULL, ddef->name)) < 0)
+ goto done;
+
+ drv->type = DIFF_DRIVER_PATTERNLIST;
+
+ if (ddef->fns &&
+ (error = diff_driver_add_patterns(
+ drv, ddef->fns, ddef->flags)) < 0)
+ goto done;
+
+ if (ddef->words &&
+ (error = git_regexp_compile(&drv->word_pattern, ddef->words, ddef->flags)) < 0)
+ goto done;
+
+ if ((error = git_strmap_set(reg->drivers, drv->name, drv)) < 0)
+ goto done;
+
+done:
+ if (error && drv)
+ git_diff_driver_free(drv);
+ else
+ *out = drv;
+
+ return error;
+}
+
+static int git_diff_driver_load(
+ git_diff_driver **out, git_repository *repo, const char *driver_name)
+{
+ int error = 0;
+ git_diff_driver_registry *reg;
+ git_diff_driver *drv;
+ size_t namelen;
+ git_config *cfg = NULL;
+ git_str name = GIT_STR_INIT;
+ git_config_entry *ce = NULL;
+ bool found_driver = false;
+
+ if ((reg = git_repository_driver_registry(repo)) == NULL)
+ return -1;
+
+ if ((drv = git_strmap_get(reg->drivers, driver_name)) != NULL) {
+ *out = drv;
+ return 0;
+ }
+
+ if ((error = diff_driver_alloc(&drv, &namelen, driver_name)) < 0)
+ goto done;
+
+ drv->type = DIFF_DRIVER_AUTO;
+
+ /* if you can't read config for repo, just use default driver */
+ if (git_repository_config_snapshot(&cfg, repo) < 0) {
+ git_error_clear();
+ goto done;
+ }
+
+ if ((error = git_str_printf(&name, "diff.%s.binary", driver_name)) < 0)
+ goto done;
+
+ switch (git_config__get_bool_force(cfg, name.ptr, -1)) {
+ case true:
+ /* if diff.<driver>.binary is true, just return the binary driver */
+ *out = &diff_driver_binary;
+ goto done;
+ case false:
+ /* if diff.<driver>.binary is false, force binary checks off */
+ /* but still may have custom function context patterns, etc. */
+ drv->binary_flags = GIT_DIFF_FORCE_TEXT;
+ found_driver = true;
+ break;
+ default:
+ /* diff.<driver>.binary unspecified or "auto", so just continue */
+ break;
+ }
+
+ /* TODO: warn if diff.<name>.command or diff.<name>.textconv are set */
+
+ git_str_truncate(&name, namelen + strlen("diff.."));
+ if ((error = git_str_PUTS(&name, "xfuncname")) < 0)
+ goto done;
+
+ if ((error = git_config_get_multivar_foreach(
+ cfg, name.ptr, NULL, diff_driver_xfuncname, drv)) < 0) {
+ if (error != GIT_ENOTFOUND)
+ goto done;
+ git_error_clear(); /* no diff.<driver>.xfuncname, so just continue */
+ }
+
+ git_str_truncate(&name, namelen + strlen("diff.."));
+ if ((error = git_str_PUTS(&name, "funcname")) < 0)
+ goto done;
+
+ if ((error = git_config_get_multivar_foreach(
+ cfg, name.ptr, NULL, diff_driver_funcname, drv)) < 0) {
+ if (error != GIT_ENOTFOUND)
+ goto done;
+ git_error_clear(); /* no diff.<driver>.funcname, so just continue */
+ }
+
+ /* if we found any patterns, set driver type to use correct callback */
+ if (git_array_size(drv->fn_patterns) > 0) {
+ drv->type = DIFF_DRIVER_PATTERNLIST;
+ found_driver = true;
+ }
+
+ git_str_truncate(&name, namelen + strlen("diff.."));
+ if ((error = git_str_PUTS(&name, "wordregex")) < 0)
+ goto done;
+
+ if ((error = git_config__lookup_entry(&ce, cfg, name.ptr, false)) < 0)
+ goto done;
+ if (!ce || !ce->value)
+ /* no diff.<driver>.wordregex, so just continue */;
+ else if (!(error = git_regexp_compile(&drv->word_pattern, ce->value, 0)))
+ found_driver = true;
+ else {
+ /* TODO: warn about bad regex instead of failure */
+ goto done;
+ }
+
+ /* TODO: look up diff.<driver>.algorithm to turn on minimal / patience
+ * diff in drv->other_flags
+ */
+
+ /* if no driver config found at all, fall back on AUTO driver */
+ if (!found_driver)
+ goto done;
+
+ /* store driver in registry */
+ if ((error = git_strmap_set(reg->drivers, drv->name, drv)) < 0)
+ goto done;
+
+ *out = drv;
+
+done:
+ git_config_entry_free(ce);
+ git_str_dispose(&name);
+ git_config_free(cfg);
+
+ if (!*out) {
+ int error2 = git_diff_driver_builtin(out, reg, driver_name);
+ if (!error)
+ error = error2;
+ }
+
+ if (drv && drv != *out)
+ git_diff_driver_free(drv);
+
+ return error;
+}
+
+int git_diff_driver_lookup(
+ git_diff_driver **out, git_repository *repo,
+ git_attr_session *attrsession, const char *path)
+{
+ int error = 0;
+ const char *values[1], *attrs[] = { "diff" };
+
+ GIT_ASSERT_ARG(out);
+ *out = NULL;
+
+ if (!repo || !path || !strlen(path))
+ /* just use the auto value */;
+ else if ((error = git_attr_get_many_with_session(values, repo,
+ attrsession, 0, path, 1, attrs)) < 0)
+ /* return error below */;
+
+ else if (GIT_ATTR_IS_UNSPECIFIED(values[0]))
+ /* just use the auto value */;
+ else if (GIT_ATTR_IS_FALSE(values[0]))
+ *out = &diff_driver_binary;
+ else if (GIT_ATTR_IS_TRUE(values[0]))
+ *out = &diff_driver_text;
+
+ /* otherwise look for driver information in config and build driver */
+ else if ((error = git_diff_driver_load(out, repo, values[0])) < 0) {
+ if (error == GIT_ENOTFOUND) {
+ error = 0;
+ git_error_clear();
+ }
+ }
+
+ if (!*out)
+ *out = &diff_driver_auto;
+
+ return error;
+}
+
+void git_diff_driver_free(git_diff_driver *driver)
+{
+ git_diff_driver_pattern *pat;
+
+ if (!driver)
+ return;
+
+ while ((pat = git_array_pop(driver->fn_patterns)) != NULL)
+ git_regexp_dispose(&pat->re);
+ git_array_clear(driver->fn_patterns);
+
+ git_regexp_dispose(&driver->word_pattern);
+
+ git__free(driver);
+}
+
+void git_diff_driver_update_options(
+ uint32_t *option_flags, git_diff_driver *driver)
+{
+ if ((*option_flags & FORCE_DIFFABLE) == 0)
+ *option_flags |= driver->binary_flags;
+
+ *option_flags |= driver->other_flags;
+}
+
+int git_diff_driver_content_is_binary(
+ git_diff_driver *driver, const char *content, size_t content_len)
+{
+ git_str search = GIT_STR_INIT;
+
+ GIT_UNUSED(driver);
+
+ git_str_attach_notowned(&search, content,
+ min(content_len, GIT_FILTER_BYTES_TO_CHECK_NUL));
+
+ /* TODO: provide encoding / binary detection callbacks that can
+ * be UTF-8 aware, etc. For now, instead of trying to be smart,
+ * let's just use the simple NUL-byte detection that core git uses.
+ */
+
+ /* previously was: if (git_str_is_binary(&search)) */
+ if (git_str_contains_nul(&search))
+ return 1;
+
+ return 0;
+}
+
+static int diff_context_line__simple(
+ git_diff_driver *driver, git_str *line)
+{
+ char firstch = line->ptr[0];
+ GIT_UNUSED(driver);
+ return (git__isalpha(firstch) || firstch == '_' || firstch == '$');
+}
+
+static int diff_context_line__pattern_match(
+ git_diff_driver *driver, git_str *line)
+{
+ size_t i, maxi = git_array_size(driver->fn_patterns);
+ git_regmatch pmatch[2];
+
+ for (i = 0; i < maxi; ++i) {
+ git_diff_driver_pattern *pat = git_array_get(driver->fn_patterns, i);
+
+ if (!git_regexp_search(&pat->re, line->ptr, 2, pmatch)) {
+ if (pat->flags & REG_NEGATE)
+ return false;
+
+ /* use pmatch data to trim line data */
+ i = (pmatch[1].start >= 0) ? 1 : 0;
+ git_str_consume(line, git_str_cstr(line) + pmatch[i].start);
+ git_str_truncate(line, pmatch[i].end - pmatch[i].start);
+ git_str_rtrim(line);
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static long diff_context_find(
+ const char *line,
+ long line_len,
+ char *out,
+ long out_size,
+ void *payload)
+{
+ git_diff_find_context_payload *ctxt = payload;
+
+ if (git_str_set(&ctxt->line, line, (size_t)line_len) < 0)
+ return -1;
+ git_str_rtrim(&ctxt->line);
+
+ if (!ctxt->line.size)
+ return -1;
+
+ if (!ctxt->match_line || !ctxt->match_line(ctxt->driver, &ctxt->line))
+ return -1;
+
+ if (out_size > (long)ctxt->line.size)
+ out_size = (long)ctxt->line.size;
+ memcpy(out, ctxt->line.ptr, (size_t)out_size);
+
+ return out_size;
+}
+
+void git_diff_find_context_init(
+ git_diff_find_context_fn *findfn_out,
+ git_diff_find_context_payload *payload_out,
+ git_diff_driver *driver)
+{
+ *findfn_out = driver ? diff_context_find : NULL;
+
+ memset(payload_out, 0, sizeof(*payload_out));
+ if (driver) {
+ payload_out->driver = driver;
+ payload_out->match_line = (driver->type == DIFF_DRIVER_PATTERNLIST) ?
+ diff_context_line__pattern_match : diff_context_line__simple;
+ git_str_init(&payload_out->line, 0);
+ }
+}
+
+void git_diff_find_context_clear(git_diff_find_context_payload *payload)
+{
+ if (payload) {
+ git_str_dispose(&payload->line);
+ payload->driver = NULL;
+ }
+}
diff --git a/src/libgit2/diff_driver.h b/src/libgit2/diff_driver.h
new file mode 100644
index 0000000..03711e8
--- /dev/null
+++ b/src/libgit2/diff_driver.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_diff_driver_h__
+#define INCLUDE_diff_driver_h__
+
+#include "common.h"
+
+#include "attr_file.h"
+#include "str.h"
+
+typedef struct git_diff_driver_registry git_diff_driver_registry;
+
+git_diff_driver_registry *git_diff_driver_registry_new(void);
+void git_diff_driver_registry_free(git_diff_driver_registry *);
+
+typedef struct git_diff_driver git_diff_driver;
+
+int git_diff_driver_lookup(git_diff_driver **, git_repository *,
+ git_attr_session *attrsession, const char *);
+void git_diff_driver_free(git_diff_driver *);
+
+/* diff option flags to force off and on for this driver */
+void git_diff_driver_update_options(uint32_t *option_flags, git_diff_driver *);
+
+/* returns -1 meaning "unknown", 0 meaning not binary, 1 meaning binary */
+int git_diff_driver_content_is_binary(
+ git_diff_driver *, const char *content, size_t content_len);
+
+typedef long (*git_diff_find_context_fn)(
+ const char *, long, char *, long, void *);
+
+typedef int (*git_diff_find_context_line)(
+ git_diff_driver *, git_str *);
+
+typedef struct {
+ git_diff_driver *driver;
+ git_diff_find_context_line match_line;
+ git_str line;
+} git_diff_find_context_payload;
+
+void git_diff_find_context_init(
+ git_diff_find_context_fn *findfn_out,
+ git_diff_find_context_payload *payload_out,
+ git_diff_driver *driver);
+
+void git_diff_find_context_clear(git_diff_find_context_payload *);
+
+#endif
diff --git a/src/libgit2/diff_file.c b/src/libgit2/diff_file.c
new file mode 100644
index 0000000..a792834
--- /dev/null
+++ b/src/libgit2/diff_file.c
@@ -0,0 +1,490 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "diff_file.h"
+
+#include "git2/blob.h"
+#include "git2/submodule.h"
+#include "diff.h"
+#include "diff_generate.h"
+#include "odb.h"
+#include "futils.h"
+#include "filter.h"
+
+#define DIFF_MAX_FILESIZE 0x20000000
+
+static bool diff_file_content_binary_by_size(git_diff_file_content *fc)
+{
+ /* if we have diff opts, check max_size vs file size */
+ if ((fc->file->flags & DIFF_FLAGS_KNOWN_BINARY) == 0 &&
+ fc->opts_max_size > 0 &&
+ fc->file->size > fc->opts_max_size)
+ fc->file->flags |= GIT_DIFF_FLAG_BINARY;
+
+ return ((fc->file->flags & GIT_DIFF_FLAG_BINARY) != 0);
+}
+
+static void diff_file_content_binary_by_content(git_diff_file_content *fc)
+{
+ if ((fc->file->flags & DIFF_FLAGS_KNOWN_BINARY) != 0)
+ return;
+
+ switch (git_diff_driver_content_is_binary(
+ fc->driver, fc->map.data, fc->map.len)) {
+ case 0: fc->file->flags |= GIT_DIFF_FLAG_NOT_BINARY; break;
+ case 1: fc->file->flags |= GIT_DIFF_FLAG_BINARY; break;
+ default: break;
+ }
+}
+
+static int diff_file_content_init_common(
+ git_diff_file_content *fc, const git_diff_options *opts)
+{
+ fc->opts_flags = opts ? opts->flags : GIT_DIFF_NORMAL;
+
+ if (opts && opts->max_size >= 0)
+ fc->opts_max_size = opts->max_size ?
+ opts->max_size : DIFF_MAX_FILESIZE;
+
+ if (fc->src == GIT_ITERATOR_EMPTY)
+ fc->src = GIT_ITERATOR_TREE;
+
+ if (!fc->driver &&
+ git_diff_driver_lookup(&fc->driver, fc->repo,
+ NULL, fc->file->path) < 0)
+ return -1;
+
+ /* give driver a chance to modify options */
+ git_diff_driver_update_options(&fc->opts_flags, fc->driver);
+
+ /* make sure file is conceivable mmap-able */
+ if ((size_t)fc->file->size != fc->file->size)
+ fc->file->flags |= GIT_DIFF_FLAG_BINARY;
+ /* check if user is forcing text diff the file */
+ else if (fc->opts_flags & GIT_DIFF_FORCE_TEXT) {
+ fc->file->flags &= ~GIT_DIFF_FLAG_BINARY;
+ fc->file->flags |= GIT_DIFF_FLAG_NOT_BINARY;
+ }
+ /* check if user is forcing binary diff the file */
+ else if (fc->opts_flags & GIT_DIFF_FORCE_BINARY) {
+ fc->file->flags &= ~GIT_DIFF_FLAG_NOT_BINARY;
+ fc->file->flags |= GIT_DIFF_FLAG_BINARY;
+ }
+
+ diff_file_content_binary_by_size(fc);
+
+ if ((fc->flags & GIT_DIFF_FLAG__NO_DATA) != 0) {
+ fc->flags |= GIT_DIFF_FLAG__LOADED;
+ fc->map.len = 0;
+ fc->map.data = "";
+ }
+
+ if ((fc->flags & GIT_DIFF_FLAG__LOADED) != 0)
+ diff_file_content_binary_by_content(fc);
+
+ return 0;
+}
+
+int git_diff_file_content__init_from_diff(
+ git_diff_file_content *fc,
+ git_diff *diff,
+ git_diff_delta *delta,
+ bool use_old)
+{
+ bool has_data = true;
+
+ memset(fc, 0, sizeof(*fc));
+ fc->repo = diff->repo;
+ fc->file = use_old ? &delta->old_file : &delta->new_file;
+ fc->src = use_old ? diff->old_src : diff->new_src;
+
+ if (git_diff_driver_lookup(&fc->driver, fc->repo,
+ &diff->attrsession, fc->file->path) < 0)
+ return -1;
+
+ switch (delta->status) {
+ case GIT_DELTA_ADDED:
+ has_data = !use_old; break;
+ case GIT_DELTA_DELETED:
+ has_data = use_old; break;
+ case GIT_DELTA_UNTRACKED:
+ has_data = (use_old == (diff->opts.flags & GIT_DIFF_REVERSE)) &&
+ (diff->opts.flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) != 0;
+ break;
+ case GIT_DELTA_UNREADABLE:
+ case GIT_DELTA_MODIFIED:
+ case GIT_DELTA_COPIED:
+ case GIT_DELTA_RENAMED:
+ break;
+ default:
+ has_data = false;
+ break;
+ }
+
+ if (!has_data)
+ fc->flags |= GIT_DIFF_FLAG__NO_DATA;
+
+ return diff_file_content_init_common(fc, &diff->opts);
+}
+
+int git_diff_file_content__init_from_src(
+ git_diff_file_content *fc,
+ git_repository *repo,
+ const git_diff_options *opts,
+ const git_diff_file_content_src *src,
+ git_diff_file *as_file)
+{
+ memset(fc, 0, sizeof(*fc));
+ fc->repo = repo;
+ fc->file = as_file;
+
+ if (!src->blob && !src->buf) {
+ fc->flags |= GIT_DIFF_FLAG__NO_DATA;
+ git_oid_clear(&fc->file->id, opts->oid_type);
+ } else {
+ fc->flags |= GIT_DIFF_FLAG__LOADED;
+ fc->file->flags |= GIT_DIFF_FLAG_VALID_ID;
+ fc->file->mode = GIT_FILEMODE_BLOB;
+
+ if (src->blob) {
+ git_blob_dup((git_blob **)&fc->blob, (git_blob *) src->blob);
+ fc->file->size = git_blob_rawsize(src->blob);
+ git_oid_cpy(&fc->file->id, git_blob_id(src->blob));
+ fc->file->id_abbrev = (uint16_t)git_oid_hexsize(repo->oid_type);
+
+ fc->map.len = (size_t)fc->file->size;
+ fc->map.data = (char *)git_blob_rawcontent(src->blob);
+
+ fc->flags |= GIT_DIFF_FLAG__FREE_BLOB;
+ } else {
+ int error;
+ if ((error = git_odb__hash(&fc->file->id, src->buf, src->buflen, GIT_OBJECT_BLOB, opts->oid_type)) < 0)
+ return error;
+ fc->file->size = src->buflen;
+ fc->file->id_abbrev = (uint16_t)git_oid_hexsize(opts->oid_type);
+
+ fc->map.len = src->buflen;
+ fc->map.data = (char *)src->buf;
+ }
+ }
+
+ return diff_file_content_init_common(fc, opts);
+}
+
+static int diff_file_content_commit_to_str(
+ git_diff_file_content *fc, bool check_status)
+{
+ char oid[GIT_OID_MAX_HEXSIZE+1];
+ git_str content = GIT_STR_INIT;
+ const char *status = "";
+
+ if (check_status) {
+ int error = 0;
+ git_submodule *sm = NULL;
+ unsigned int sm_status = 0;
+ const git_oid *sm_head;
+
+ if ((error = git_submodule_lookup(&sm, fc->repo, fc->file->path)) < 0) {
+ /* GIT_EEXISTS means a "submodule" that has not been git added */
+ if (error == GIT_EEXISTS) {
+ git_error_clear();
+ error = 0;
+ }
+ return error;
+ }
+
+ if ((error = git_submodule_status(&sm_status, fc->repo, fc->file->path, GIT_SUBMODULE_IGNORE_UNSPECIFIED)) < 0) {
+ git_submodule_free(sm);
+ return error;
+ }
+
+ /* update OID if we didn't have it previously */
+ if ((fc->file->flags & GIT_DIFF_FLAG_VALID_ID) == 0 &&
+ ((sm_head = git_submodule_wd_id(sm)) != NULL ||
+ (sm_head = git_submodule_head_id(sm)) != NULL))
+ {
+ git_oid_cpy(&fc->file->id, sm_head);
+ fc->file->flags |= GIT_DIFF_FLAG_VALID_ID;
+ }
+
+ if (GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status))
+ status = "-dirty";
+
+ git_submodule_free(sm);
+ }
+
+ git_oid_tostr(oid, sizeof(oid), &fc->file->id);
+ if (git_str_printf(&content, "Subproject commit %s%s\n", oid, status) < 0)
+ return -1;
+
+ fc->map.len = git_str_len(&content);
+ fc->map.data = git_str_detach(&content);
+ fc->flags |= GIT_DIFF_FLAG__FREE_DATA;
+
+ return 0;
+}
+
+static int diff_file_content_load_blob(
+ git_diff_file_content *fc,
+ git_diff_options *opts)
+{
+ int error = 0;
+ git_odb_object *odb_obj = NULL;
+
+ if (git_oid_is_zero(&fc->file->id))
+ return 0;
+
+ if (fc->file->mode == GIT_FILEMODE_COMMIT)
+ return diff_file_content_commit_to_str(fc, false);
+
+ /* if we don't know size, try to peek at object header first */
+ if (!fc->file->size) {
+ if ((error = git_diff_file__resolve_zero_size(
+ fc->file, &odb_obj, fc->repo)) < 0)
+ return error;
+ }
+
+ if ((opts->flags & GIT_DIFF_SHOW_BINARY) == 0 &&
+ diff_file_content_binary_by_size(fc))
+ return 0;
+
+ if (odb_obj != NULL) {
+ error = git_object__from_odb_object(
+ (git_object **)&fc->blob, fc->repo, odb_obj, GIT_OBJECT_BLOB);
+ git_odb_object_free(odb_obj);
+ } else {
+ error = git_blob_lookup(
+ (git_blob **)&fc->blob, fc->repo, &fc->file->id);
+ }
+
+ if (!error) {
+ fc->flags |= GIT_DIFF_FLAG__FREE_BLOB;
+ fc->map.data = (void *)git_blob_rawcontent(fc->blob);
+ fc->map.len = (size_t)git_blob_rawsize(fc->blob);
+ }
+
+ return error;
+}
+
+static int diff_file_content_load_workdir_symlink_fake(
+ git_diff_file_content *fc, git_str *path)
+{
+ git_str target = GIT_STR_INIT;
+ int error;
+
+ if ((error = git_futils_readbuffer(&target, path->ptr)) < 0)
+ return error;
+
+ fc->map.len = git_str_len(&target);
+ fc->map.data = git_str_detach(&target);
+ fc->flags |= GIT_DIFF_FLAG__FREE_DATA;
+
+ git_str_dispose(&target);
+ return error;
+}
+
+static int diff_file_content_load_workdir_symlink(
+ git_diff_file_content *fc, git_str *path)
+{
+ ssize_t alloc_len, read_len;
+ int symlink_supported, error;
+
+ if ((error = git_repository__configmap_lookup(
+ &symlink_supported, fc->repo, GIT_CONFIGMAP_SYMLINKS)) < 0)
+ return -1;
+
+ if (!symlink_supported)
+ return diff_file_content_load_workdir_symlink_fake(fc, path);
+
+ /* link path on disk could be UTF-16, so prepare a buffer that is
+ * big enough to handle some UTF-8 data expansion
+ */
+ alloc_len = (ssize_t)(fc->file->size * 2) + 1;
+
+ fc->map.data = git__calloc(alloc_len, sizeof(char));
+ GIT_ERROR_CHECK_ALLOC(fc->map.data);
+
+ fc->flags |= GIT_DIFF_FLAG__FREE_DATA;
+
+ read_len = p_readlink(git_str_cstr(path), fc->map.data, alloc_len);
+ if (read_len < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to read symlink '%s'", fc->file->path);
+ return -1;
+ }
+
+ fc->map.len = read_len;
+ return 0;
+}
+
+static int diff_file_content_load_workdir_file(
+ git_diff_file_content *fc,
+ git_str *path,
+ git_diff_options *diff_opts)
+{
+ int error = 0;
+ git_filter_list *fl = NULL;
+ git_file fd = git_futils_open_ro(git_str_cstr(path));
+ git_str raw = GIT_STR_INIT;
+ git_object_size_t new_file_size = 0;
+
+ if (fd < 0)
+ return fd;
+
+ error = git_futils_filesize(&new_file_size, fd);
+
+ if (error < 0)
+ goto cleanup;
+
+ if (!(fc->file->flags & GIT_DIFF_FLAG_VALID_SIZE)) {
+ fc->file->size = new_file_size;
+ fc->file->flags |= GIT_DIFF_FLAG_VALID_SIZE;
+ } else if (fc->file->size != new_file_size) {
+ git_error_set(GIT_ERROR_FILESYSTEM, "file changed before we could read it");
+ error = -1;
+ goto cleanup;
+ }
+
+ /* if file is empty, don't attempt to mmap or readbuffer */
+ if (fc->file->size == 0) {
+ fc->map.len = 0;
+ fc->map.data = git_str__initstr;
+ goto cleanup;
+ }
+
+ if ((diff_opts->flags & GIT_DIFF_SHOW_BINARY) == 0 &&
+ diff_file_content_binary_by_size(fc))
+ goto cleanup;
+
+ if ((error = git_filter_list_load(
+ &fl, fc->repo, NULL, fc->file->path,
+ GIT_FILTER_TO_ODB, GIT_FILTER_ALLOW_UNSAFE)) < 0)
+ goto cleanup;
+
+ /* if there are no filters, try to mmap the file */
+ if (fl == NULL) {
+ if (!(error = git_futils_mmap_ro(
+ &fc->map, fd, 0, (size_t)fc->file->size))) {
+ fc->flags |= GIT_DIFF_FLAG__UNMAP_DATA;
+ goto cleanup;
+ }
+
+ /* if mmap failed, fall through to try readbuffer below */
+ git_error_clear();
+ }
+
+ if (!(error = git_futils_readbuffer_fd(&raw, fd, (size_t)fc->file->size))) {
+ git_str out = GIT_STR_INIT;
+
+ error = git_filter_list__convert_buf(&out, fl, &raw);
+
+ if (!error) {
+ fc->map.len = out.size;
+ fc->map.data = out.ptr;
+ fc->flags |= GIT_DIFF_FLAG__FREE_DATA;
+ }
+ }
+
+cleanup:
+ git_filter_list_free(fl);
+ p_close(fd);
+
+ return error;
+}
+
+static int diff_file_content_load_workdir(
+ git_diff_file_content *fc,
+ git_diff_options *diff_opts)
+{
+ int error = 0;
+ git_str path = GIT_STR_INIT;
+
+ if (fc->file->mode == GIT_FILEMODE_COMMIT)
+ return diff_file_content_commit_to_str(fc, true);
+
+ if (fc->file->mode == GIT_FILEMODE_TREE)
+ return 0;
+
+ if (git_repository_workdir_path(&path, fc->repo, fc->file->path) < 0)
+ return -1;
+
+ if (S_ISLNK(fc->file->mode))
+ error = diff_file_content_load_workdir_symlink(fc, &path);
+ else
+ error = diff_file_content_load_workdir_file(fc, &path, diff_opts);
+
+ /* once data is loaded, update OID if we didn't have it previously */
+ if (!error && (fc->file->flags & GIT_DIFF_FLAG_VALID_ID) == 0) {
+ error = git_odb__hash(
+ &fc->file->id, fc->map.data, fc->map.len,
+ GIT_OBJECT_BLOB, diff_opts->oid_type);
+ fc->file->flags |= GIT_DIFF_FLAG_VALID_ID;
+ }
+
+ git_str_dispose(&path);
+ return error;
+}
+
+int git_diff_file_content__load(
+ git_diff_file_content *fc,
+ git_diff_options *diff_opts)
+{
+ int error = 0;
+
+ if ((fc->flags & GIT_DIFF_FLAG__LOADED) != 0)
+ return 0;
+
+ if ((fc->file->flags & GIT_DIFF_FLAG_BINARY) != 0 &&
+ (diff_opts->flags & GIT_DIFF_SHOW_BINARY) == 0)
+ return 0;
+
+ if (fc->src == GIT_ITERATOR_WORKDIR)
+ error = diff_file_content_load_workdir(fc, diff_opts);
+ else
+ error = diff_file_content_load_blob(fc, diff_opts);
+ if (error)
+ return error;
+
+ fc->flags |= GIT_DIFF_FLAG__LOADED;
+
+ diff_file_content_binary_by_content(fc);
+
+ return 0;
+}
+
+void git_diff_file_content__unload(git_diff_file_content *fc)
+{
+ if ((fc->flags & GIT_DIFF_FLAG__LOADED) == 0)
+ return;
+
+ if (fc->flags & GIT_DIFF_FLAG__FREE_DATA) {
+ git__free(fc->map.data);
+ fc->map.data = "";
+ fc->map.len = 0;
+ fc->flags &= ~GIT_DIFF_FLAG__FREE_DATA;
+ }
+ else if (fc->flags & GIT_DIFF_FLAG__UNMAP_DATA) {
+ git_futils_mmap_free(&fc->map);
+ fc->map.data = "";
+ fc->map.len = 0;
+ fc->flags &= ~GIT_DIFF_FLAG__UNMAP_DATA;
+ }
+
+ if (fc->flags & GIT_DIFF_FLAG__FREE_BLOB) {
+ git_blob_free((git_blob *)fc->blob);
+ fc->blob = NULL;
+ fc->flags &= ~GIT_DIFF_FLAG__FREE_BLOB;
+ }
+
+ fc->flags &= ~GIT_DIFF_FLAG__LOADED;
+}
+
+void git_diff_file_content__clear(git_diff_file_content *fc)
+{
+ git_diff_file_content__unload(fc);
+
+ /* for now, nothing else to do */
+}
diff --git a/src/libgit2/diff_file.h b/src/libgit2/diff_file.h
new file mode 100644
index 0000000..8d743e8
--- /dev/null
+++ b/src/libgit2/diff_file.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_diff_file_h__
+#define INCLUDE_diff_file_h__
+
+#include "common.h"
+
+#include "diff.h"
+#include "diff_driver.h"
+#include "map.h"
+
+/* expanded information for one side of a delta */
+typedef struct {
+ git_repository *repo;
+ git_diff_file *file;
+ git_diff_driver *driver;
+ uint32_t flags;
+ uint32_t opts_flags;
+ git_object_size_t opts_max_size;
+ git_iterator_t src;
+ const git_blob *blob;
+ git_map map;
+} git_diff_file_content;
+
+extern int git_diff_file_content__init_from_diff(
+ git_diff_file_content *fc,
+ git_diff *diff,
+ git_diff_delta *delta,
+ bool use_old);
+
+typedef struct {
+ const git_blob *blob;
+ const void *buf;
+ size_t buflen;
+ const char *as_path;
+} git_diff_file_content_src;
+
+#define GIT_DIFF_FILE_CONTENT_SRC__BLOB(BLOB,PATH) { (BLOB),NULL,0,(PATH) }
+#define GIT_DIFF_FILE_CONTENT_SRC__BUF(BUF,LEN,PATH) { NULL,(BUF),(LEN),(PATH) }
+
+extern int git_diff_file_content__init_from_src(
+ git_diff_file_content *fc,
+ git_repository *repo,
+ const git_diff_options *opts,
+ const git_diff_file_content_src *src,
+ git_diff_file *as_file);
+
+/* this loads the blob/file-on-disk as needed */
+extern int git_diff_file_content__load(
+ git_diff_file_content *fc,
+ git_diff_options *diff_opts);
+
+/* this releases the blob/file-in-memory */
+extern void git_diff_file_content__unload(git_diff_file_content *fc);
+
+/* this unloads and also releases any other resources */
+extern void git_diff_file_content__clear(git_diff_file_content *fc);
+
+#endif
diff --git a/src/libgit2/diff_generate.c b/src/libgit2/diff_generate.c
new file mode 100644
index 0000000..78fe510
--- /dev/null
+++ b/src/libgit2/diff_generate.c
@@ -0,0 +1,1750 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "diff_generate.h"
+
+#include "diff.h"
+#include "patch_generate.h"
+#include "futils.h"
+#include "config.h"
+#include "attr_file.h"
+#include "filter.h"
+#include "pathspec.h"
+#include "index.h"
+#include "odb.h"
+#include "submodule.h"
+
+#define DIFF_FLAG_IS_SET(DIFF,FLAG) \
+ (((DIFF)->base.opts.flags & (FLAG)) != 0)
+#define DIFF_FLAG_ISNT_SET(DIFF,FLAG) \
+ (((DIFF)->base.opts.flags & (FLAG)) == 0)
+#define DIFF_FLAG_SET(DIFF,FLAG,VAL) (DIFF)->base.opts.flags = \
+ (VAL) ? ((DIFF)->base.opts.flags | (FLAG)) : \
+ ((DIFF)->base.opts.flags & ~(FLAG))
+
+typedef struct {
+ struct git_diff base;
+
+ git_vector pathspec;
+
+ uint32_t diffcaps;
+ bool index_updated;
+} git_diff_generated;
+
+static git_diff_delta *diff_delta__alloc(
+ git_diff_generated *diff,
+ git_delta_t status,
+ const char *path)
+{
+ git_diff_delta *delta = git__calloc(1, sizeof(git_diff_delta));
+ if (!delta)
+ return NULL;
+
+ delta->old_file.path = git_pool_strdup(&diff->base.pool, path);
+ if (delta->old_file.path == NULL) {
+ git__free(delta);
+ return NULL;
+ }
+
+ delta->new_file.path = delta->old_file.path;
+
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) {
+ switch (status) {
+ case GIT_DELTA_ADDED: status = GIT_DELTA_DELETED; break;
+ case GIT_DELTA_DELETED: status = GIT_DELTA_ADDED; break;
+ default: break; /* leave other status values alone */
+ }
+ }
+ delta->status = status;
+
+ git_oid_clear(&delta->old_file.id, diff->base.opts.oid_type);
+ git_oid_clear(&delta->new_file.id, diff->base.opts.oid_type);
+
+ return delta;
+}
+
+static int diff_insert_delta(
+ git_diff_generated *diff,
+ git_diff_delta *delta,
+ const char *matched_pathspec)
+{
+ int error = 0;
+
+ if (diff->base.opts.notify_cb) {
+ error = diff->base.opts.notify_cb(
+ &diff->base, delta, matched_pathspec, diff->base.opts.payload);
+
+ if (error) {
+ git__free(delta);
+
+ if (error > 0) /* positive value means to skip this delta */
+ return 0;
+ else /* negative value means to cancel diff */
+ return git_error_set_after_callback_function(error, "git_diff");
+ }
+ }
+
+ if ((error = git_vector_insert(&diff->base.deltas, delta)) < 0)
+ git__free(delta);
+
+ return error;
+}
+
+static bool diff_pathspec_match(
+ const char **matched_pathspec,
+ git_diff_generated *diff,
+ const git_index_entry *entry)
+{
+ bool disable_pathspec_match =
+ DIFF_FLAG_IS_SET(diff, GIT_DIFF_DISABLE_PATHSPEC_MATCH);
+
+ /* If we're disabling fnmatch, then the iterator has already applied
+ * the filters to the files for us and we don't have to do anything.
+ * However, this only applies to *files* - the iterator will include
+ * directories that we need to recurse into when not autoexpanding,
+ * so we still need to apply the pathspec match to directories.
+ */
+ if ((S_ISLNK(entry->mode) || S_ISREG(entry->mode)) &&
+ disable_pathspec_match) {
+ *matched_pathspec = entry->path;
+ return true;
+ }
+
+ return git_pathspec__match(
+ &diff->pathspec, entry->path, disable_pathspec_match,
+ DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE),
+ matched_pathspec, NULL);
+}
+
+static void diff_delta__flag_known_size(git_diff_file *file)
+{
+ /*
+ * If we don't know the ID, that can only come from the workdir
+ * iterator, which means we *do* know the file size. This is a
+ * leaky abstraction, but alas. Otherwise, we test against the
+ * empty blob id.
+ */
+ if (file->size ||
+ !(file->flags & GIT_DIFF_FLAG_VALID_ID) ||
+ git_oid_equal(&file->id, &git_oid__empty_blob_sha1))
+ file->flags |= GIT_DIFF_FLAG_VALID_SIZE;
+}
+
+static void diff_delta__flag_known_sizes(git_diff_delta *delta)
+{
+ diff_delta__flag_known_size(&delta->old_file);
+ diff_delta__flag_known_size(&delta->new_file);
+}
+
+static int diff_delta__from_one(
+ git_diff_generated *diff,
+ git_delta_t status,
+ const git_index_entry *oitem,
+ const git_index_entry *nitem)
+{
+ const git_index_entry *entry = nitem;
+ bool has_old = false;
+ git_diff_delta *delta;
+ git_oid_t oid_type;
+ const char *matched_pathspec;
+
+ GIT_ASSERT_ARG((oitem != NULL) ^ (nitem != NULL));
+
+ oid_type = diff->base.opts.oid_type;
+
+ if (oitem) {
+ entry = oitem;
+ has_old = true;
+ }
+
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE))
+ has_old = !has_old;
+
+ if ((entry->flags & GIT_INDEX_ENTRY_VALID) != 0)
+ return 0;
+
+ if (status == GIT_DELTA_IGNORED &&
+ DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_IGNORED))
+ return 0;
+
+ if (status == GIT_DELTA_UNTRACKED &&
+ DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNTRACKED))
+ return 0;
+
+ if (status == GIT_DELTA_UNREADABLE &&
+ DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNREADABLE))
+ return 0;
+
+ if (!diff_pathspec_match(&matched_pathspec, diff, entry))
+ return 0;
+
+ delta = diff_delta__alloc(diff, status, entry->path);
+ GIT_ERROR_CHECK_ALLOC(delta);
+
+ /* This fn is just for single-sided diffs */
+ GIT_ASSERT(status != GIT_DELTA_MODIFIED);
+ delta->nfiles = 1;
+
+ git_oid_clear(&delta->old_file.id, diff->base.opts.oid_type);
+ git_oid_clear(&delta->new_file.id, diff->base.opts.oid_type);
+
+ if (has_old) {
+ delta->old_file.mode = entry->mode;
+ delta->old_file.size = entry->file_size;
+ delta->old_file.flags |= GIT_DIFF_FLAG_EXISTS;
+ git_oid_cpy(&delta->old_file.id, &entry->id);
+ git_oid_clear(&delta->new_file.id, oid_type);
+ delta->old_file.id_abbrev = (uint16_t)git_oid_hexsize(oid_type);
+ } else /* ADDED, IGNORED, UNTRACKED */ {
+ delta->new_file.mode = entry->mode;
+ delta->new_file.size = entry->file_size;
+ delta->new_file.flags |= GIT_DIFF_FLAG_EXISTS;
+ git_oid_clear(&delta->old_file.id, oid_type);
+ git_oid_cpy(&delta->new_file.id, &entry->id);
+ delta->new_file.id_abbrev = (uint16_t)git_oid_hexsize(oid_type);
+ }
+
+ delta->old_file.flags |= GIT_DIFF_FLAG_VALID_ID;
+
+ if (has_old || !git_oid_is_zero(&delta->new_file.id))
+ delta->new_file.flags |= GIT_DIFF_FLAG_VALID_ID;
+
+ diff_delta__flag_known_sizes(delta);
+
+ return diff_insert_delta(diff, delta, matched_pathspec);
+}
+
+static int diff_delta__from_two(
+ git_diff_generated *diff,
+ git_delta_t status,
+ const git_index_entry *old_entry,
+ uint32_t old_mode,
+ const git_index_entry *new_entry,
+ uint32_t new_mode,
+ const git_oid *new_id,
+ const char *matched_pathspec)
+{
+ const git_oid *old_id = &old_entry->id;
+ git_diff_delta *delta;
+ const char *canonical_path = old_entry->path;
+ git_oid_t oid_type;
+
+ oid_type = diff->base.opts.oid_type;
+
+ if (status == GIT_DELTA_UNMODIFIED &&
+ DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_UNMODIFIED))
+ return 0;
+
+ if (!new_id)
+ new_id = &new_entry->id;
+
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) {
+ uint32_t temp_mode = old_mode;
+ const git_index_entry *temp_entry = old_entry;
+ const git_oid *temp_id = old_id;
+
+ old_entry = new_entry;
+ new_entry = temp_entry;
+ old_mode = new_mode;
+ new_mode = temp_mode;
+ old_id = new_id;
+ new_id = temp_id;
+ }
+
+ delta = diff_delta__alloc(diff, status, canonical_path);
+ GIT_ERROR_CHECK_ALLOC(delta);
+ delta->nfiles = 2;
+
+ if (!git_index_entry_is_conflict(old_entry)) {
+ delta->old_file.size = old_entry->file_size;
+ delta->old_file.mode = old_mode;
+ git_oid_cpy(&delta->old_file.id, old_id);
+ delta->old_file.id_abbrev = (uint16_t)git_oid_hexsize(oid_type);
+ delta->old_file.flags |= GIT_DIFF_FLAG_VALID_ID |
+ GIT_DIFF_FLAG_EXISTS;
+ }
+
+ if (!git_index_entry_is_conflict(new_entry)) {
+ git_oid_cpy(&delta->new_file.id, new_id);
+ delta->new_file.id_abbrev = (uint16_t)git_oid_hexsize(oid_type);
+ delta->new_file.size = new_entry->file_size;
+ delta->new_file.mode = new_mode;
+ delta->old_file.flags |= GIT_DIFF_FLAG_EXISTS;
+ delta->new_file.flags |= GIT_DIFF_FLAG_EXISTS;
+
+ if (!git_oid_is_zero(&new_entry->id))
+ delta->new_file.flags |= GIT_DIFF_FLAG_VALID_ID;
+ }
+
+ diff_delta__flag_known_sizes(delta);
+
+ return diff_insert_delta(diff, delta, matched_pathspec);
+}
+
+static git_diff_delta *diff_delta__last_for_item(
+ git_diff_generated *diff,
+ const git_index_entry *item)
+{
+ git_diff_delta *delta = git_vector_last(&diff->base.deltas);
+ if (!delta)
+ return NULL;
+
+ switch (delta->status) {
+ case GIT_DELTA_UNMODIFIED:
+ case GIT_DELTA_DELETED:
+ if (git_oid__cmp(&delta->old_file.id, &item->id) == 0)
+ return delta;
+ break;
+ case GIT_DELTA_ADDED:
+ if (git_oid__cmp(&delta->new_file.id, &item->id) == 0)
+ return delta;
+ break;
+ case GIT_DELTA_UNREADABLE:
+ case GIT_DELTA_UNTRACKED:
+ if (diff->base.strcomp(delta->new_file.path, item->path) == 0 &&
+ git_oid__cmp(&delta->new_file.id, &item->id) == 0)
+ return delta;
+ break;
+ case GIT_DELTA_MODIFIED:
+ if (git_oid__cmp(&delta->old_file.id, &item->id) == 0 ||
+ (delta->new_file.mode == item->mode &&
+ git_oid__cmp(&delta->new_file.id, &item->id) == 0))
+ return delta;
+ break;
+ default:
+ break;
+ }
+
+ return NULL;
+}
+
+static char *diff_strdup_prefix(git_pool *pool, const char *prefix)
+{
+ size_t len = strlen(prefix);
+
+ /* append '/' at end if needed */
+ if (len > 0 && prefix[len - 1] != '/')
+ return git_pool_strcat(pool, prefix, "/");
+ else
+ return git_pool_strndup(pool, prefix, len + 1);
+}
+
+GIT_INLINE(const char *) diff_delta__i2w_path(const git_diff_delta *delta)
+{
+ return delta->old_file.path ?
+ delta->old_file.path : delta->new_file.path;
+}
+
+static int diff_delta_i2w_cmp(const void *a, const void *b)
+{
+ const git_diff_delta *da = a, *db = b;
+ int val = strcmp(diff_delta__i2w_path(da), diff_delta__i2w_path(db));
+ return val ? val : ((int)da->status - (int)db->status);
+}
+
+static int diff_delta_i2w_casecmp(const void *a, const void *b)
+{
+ const git_diff_delta *da = a, *db = b;
+ int val = strcasecmp(diff_delta__i2w_path(da), diff_delta__i2w_path(db));
+ return val ? val : ((int)da->status - (int)db->status);
+}
+
+bool git_diff_delta__should_skip(
+ const git_diff_options *opts, const git_diff_delta *delta)
+{
+ uint32_t flags = opts ? opts->flags : 0;
+
+ if (delta->status == GIT_DELTA_UNMODIFIED &&
+ (flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
+ return true;
+
+ if (delta->status == GIT_DELTA_IGNORED &&
+ (flags & GIT_DIFF_INCLUDE_IGNORED) == 0)
+ return true;
+
+ if (delta->status == GIT_DELTA_UNTRACKED &&
+ (flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0)
+ return true;
+
+ if (delta->status == GIT_DELTA_UNREADABLE &&
+ (flags & GIT_DIFF_INCLUDE_UNREADABLE) == 0)
+ return true;
+
+ return false;
+}
+
+
+static const char *diff_mnemonic_prefix(
+ git_iterator_t type, bool left_side)
+{
+ const char *pfx = "";
+
+ switch (type) {
+ case GIT_ITERATOR_EMPTY: pfx = "c"; break;
+ case GIT_ITERATOR_TREE: pfx = "c"; break;
+ case GIT_ITERATOR_INDEX: pfx = "i"; break;
+ case GIT_ITERATOR_WORKDIR: pfx = "w"; break;
+ case GIT_ITERATOR_FS: pfx = left_side ? "1" : "2"; break;
+ default: break;
+ }
+
+ /* note: without a deeper look at pathspecs, there is no easy way
+ * to get the (o)bject / (w)ork tree mnemonics working...
+ */
+
+ return pfx;
+}
+
+static void diff_set_ignore_case(git_diff *diff, bool ignore_case)
+{
+ if (!ignore_case) {
+ diff->opts.flags &= ~GIT_DIFF_IGNORE_CASE;
+
+ diff->strcomp = git__strcmp;
+ diff->strncomp = git__strncmp;
+ diff->pfxcomp = git__prefixcmp;
+ diff->entrycomp = git_diff__entry_cmp;
+
+ git_vector_set_cmp(&diff->deltas, git_diff_delta__cmp);
+ } else {
+ diff->opts.flags |= GIT_DIFF_IGNORE_CASE;
+
+ diff->strcomp = git__strcasecmp;
+ diff->strncomp = git__strncasecmp;
+ diff->pfxcomp = git__prefixcmp_icase;
+ diff->entrycomp = git_diff__entry_icmp;
+
+ git_vector_set_cmp(&diff->deltas, git_diff_delta__casecmp);
+ }
+
+ git_vector_sort(&diff->deltas);
+}
+
+static void diff_generated_free(git_diff *d)
+{
+ git_diff_generated *diff = (git_diff_generated *)d;
+
+ git_attr_session__free(&diff->base.attrsession);
+ git_vector_free_deep(&diff->base.deltas);
+
+ git_pathspec__vfree(&diff->pathspec);
+ git_pool_clear(&diff->base.pool);
+
+ git__memzero(diff, sizeof(*diff));
+ git__free(diff);
+}
+
+static git_diff_generated *diff_generated_alloc(
+ git_repository *repo,
+ git_iterator *old_iter,
+ git_iterator *new_iter)
+{
+ git_diff_generated *diff;
+ git_diff_options dflt = GIT_DIFF_OPTIONS_INIT;
+
+ GIT_ASSERT_ARG_WITH_RETVAL(repo, NULL);
+ GIT_ASSERT_ARG_WITH_RETVAL(old_iter, NULL);
+ GIT_ASSERT_ARG_WITH_RETVAL(new_iter, NULL);
+
+ if ((diff = git__calloc(1, sizeof(git_diff_generated))) == NULL)
+ return NULL;
+
+ GIT_REFCOUNT_INC(&diff->base);
+ diff->base.type = GIT_DIFF_TYPE_GENERATED;
+ diff->base.repo = repo;
+ diff->base.old_src = old_iter->type;
+ diff->base.new_src = new_iter->type;
+ diff->base.patch_fn = git_patch_generated_from_diff;
+ diff->base.free_fn = diff_generated_free;
+ git_attr_session__init(&diff->base.attrsession, repo);
+ memcpy(&diff->base.opts, &dflt, sizeof(git_diff_options));
+
+ if (git_pool_init(&diff->base.pool, 1) < 0 ||
+ git_vector_init(&diff->base.deltas, 0, git_diff_delta__cmp) < 0) {
+ git_diff_free(&diff->base);
+ return NULL;
+ }
+
+ /* Use case-insensitive compare if either iterator has
+ * the ignore_case bit set */
+ diff_set_ignore_case(
+ &diff->base,
+ git_iterator_ignore_case(old_iter) ||
+ git_iterator_ignore_case(new_iter));
+
+ return diff;
+}
+
+static int diff_generated_apply_options(
+ git_diff_generated *diff,
+ const git_diff_options *opts)
+{
+ git_config *cfg = NULL;
+ git_repository *repo = diff->base.repo;
+ git_pool *pool = &diff->base.pool;
+ int val;
+
+ if (opts) {
+ /* copy user options (except case sensitivity info from iterators) */
+ bool icase = DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE);
+ memcpy(&diff->base.opts, opts, sizeof(diff->base.opts));
+ DIFF_FLAG_SET(diff, GIT_DIFF_IGNORE_CASE, icase);
+
+ /* initialize pathspec from options */
+ if (git_pathspec__vinit(&diff->pathspec, &opts->pathspec, pool) < 0)
+ return -1;
+ }
+
+ if (!diff->base.opts.oid_type) {
+ diff->base.opts.oid_type = repo->oid_type;
+ } else if (diff->base.opts.oid_type != repo->oid_type) {
+ git_error_set(GIT_ERROR_INVALID,
+ "specified object ID type does not match repository object ID type");
+ return -1;
+ }
+
+ /* flag INCLUDE_TYPECHANGE_TREES implies INCLUDE_TYPECHANGE */
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES))
+ diff->base.opts.flags |= GIT_DIFF_INCLUDE_TYPECHANGE;
+
+ /* flag INCLUDE_UNTRACKED_CONTENT implies INCLUDE_UNTRACKED */
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_SHOW_UNTRACKED_CONTENT))
+ diff->base.opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
+
+ /* load config values that affect diff behavior */
+ if ((val = git_repository_config_snapshot(&cfg, repo)) < 0)
+ return val;
+
+ if (!git_config__configmap_lookup(&val, cfg, GIT_CONFIGMAP_SYMLINKS) && val)
+ diff->diffcaps |= GIT_DIFFCAPS_HAS_SYMLINKS;
+
+ if (!git_config__configmap_lookup(&val, cfg, GIT_CONFIGMAP_IGNORESTAT) && val)
+ diff->diffcaps |= GIT_DIFFCAPS_IGNORE_STAT;
+
+ if ((diff->base.opts.flags & GIT_DIFF_IGNORE_FILEMODE) == 0 &&
+ !git_config__configmap_lookup(&val, cfg, GIT_CONFIGMAP_FILEMODE) && val)
+ diff->diffcaps |= GIT_DIFFCAPS_TRUST_MODE_BITS;
+
+ if (!git_config__configmap_lookup(&val, cfg, GIT_CONFIGMAP_TRUSTCTIME) && val)
+ diff->diffcaps |= GIT_DIFFCAPS_TRUST_CTIME;
+
+ /* Don't set GIT_DIFFCAPS_USE_DEV - compile time option in core git */
+
+ /* If not given explicit `opts`, check `diff.xyz` configs */
+ if (!opts) {
+ int context = git_config__get_int_force(cfg, "diff.context", 3);
+ diff->base.opts.context_lines = context >= 0 ? (uint32_t)context : 3;
+
+ /* add other defaults here */
+ }
+
+ /* Reverse src info if diff is reversed */
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) {
+ git_iterator_t tmp_src = diff->base.old_src;
+ diff->base.old_src = diff->base.new_src;
+ diff->base.new_src = tmp_src;
+ }
+
+ /* Unset UPDATE_INDEX unless diffing workdir and index */
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_UPDATE_INDEX) &&
+ (!(diff->base.old_src == GIT_ITERATOR_WORKDIR ||
+ diff->base.new_src == GIT_ITERATOR_WORKDIR) ||
+ !(diff->base.old_src == GIT_ITERATOR_INDEX ||
+ diff->base.new_src == GIT_ITERATOR_INDEX)))
+ diff->base.opts.flags &= ~GIT_DIFF_UPDATE_INDEX;
+
+ /* if ignore_submodules not explicitly set, check diff config */
+ if (diff->base.opts.ignore_submodules <= 0) {
+ git_config_entry *entry;
+ git_config__lookup_entry(&entry, cfg, "diff.ignoresubmodules", true);
+
+ if (entry && git_submodule_parse_ignore(
+ &diff->base.opts.ignore_submodules, entry->value) < 0)
+ git_error_clear();
+ git_config_entry_free(entry);
+ }
+
+ /* if either prefix is not set, figure out appropriate value */
+ if (!diff->base.opts.old_prefix || !diff->base.opts.new_prefix) {
+ const char *use_old = DIFF_OLD_PREFIX_DEFAULT;
+ const char *use_new = DIFF_NEW_PREFIX_DEFAULT;
+
+ if (git_config__get_bool_force(cfg, "diff.noprefix", 0))
+ use_old = use_new = "";
+ else if (git_config__get_bool_force(cfg, "diff.mnemonicprefix", 0)) {
+ use_old = diff_mnemonic_prefix(diff->base.old_src, true);
+ use_new = diff_mnemonic_prefix(diff->base.new_src, false);
+ }
+
+ if (!diff->base.opts.old_prefix)
+ diff->base.opts.old_prefix = use_old;
+ if (!diff->base.opts.new_prefix)
+ diff->base.opts.new_prefix = use_new;
+ }
+
+ /* strdup prefix from pool so we're not dependent on external data */
+ diff->base.opts.old_prefix = diff_strdup_prefix(pool, diff->base.opts.old_prefix);
+ diff->base.opts.new_prefix = diff_strdup_prefix(pool, diff->base.opts.new_prefix);
+
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_REVERSE)) {
+ const char *tmp_prefix = diff->base.opts.old_prefix;
+ diff->base.opts.old_prefix = diff->base.opts.new_prefix;
+ diff->base.opts.new_prefix = tmp_prefix;
+ }
+
+ git_config_free(cfg);
+
+ /* check strdup results for error */
+ return (!diff->base.opts.old_prefix || !diff->base.opts.new_prefix) ? -1 : 0;
+}
+
+int git_diff__oid_for_file(
+ git_oid *out,
+ git_diff *diff,
+ const char *path,
+ uint16_t mode,
+ git_object_size_t size)
+{
+ git_index_entry entry;
+
+ if (size > UINT32_MAX) {
+ git_error_set(GIT_ERROR_NOMEMORY, "file size overflow (for 32-bits) on '%s'", path);
+ return -1;
+ }
+
+ memset(&entry, 0, sizeof(entry));
+ entry.mode = mode;
+ entry.file_size = (uint32_t)size;
+ entry.path = (char *)path;
+ git_oid_clear(&entry.id, diff->opts.oid_type);
+
+ return git_diff__oid_for_entry(out, diff, &entry, mode, NULL);
+}
+
+int git_diff__oid_for_entry(
+ git_oid *out,
+ git_diff *d,
+ const git_index_entry *src,
+ uint16_t mode,
+ const git_oid *update_match)
+{
+ git_diff_generated *diff;
+ git_str full_path = GIT_STR_INIT;
+ git_index_entry entry = *src;
+ git_filter_list *fl = NULL;
+ int error = 0;
+
+ GIT_ASSERT(d->type == GIT_DIFF_TYPE_GENERATED);
+ diff = (git_diff_generated *)d;
+
+ git_oid_clear(out, diff->base.opts.oid_type);
+
+ if (git_repository_workdir_path(&full_path, diff->base.repo, entry.path) < 0)
+ return -1;
+
+ if (!mode) {
+ struct stat st;
+
+ diff->base.perf.stat_calls++;
+
+ if (p_stat(full_path.ptr, &st) < 0) {
+ error = git_fs_path_set_error(errno, entry.path, "stat");
+ git_str_dispose(&full_path);
+ return error;
+ }
+
+ git_index_entry__init_from_stat(&entry,
+ &st, (diff->diffcaps & GIT_DIFFCAPS_TRUST_MODE_BITS) != 0);
+ }
+
+ /* calculate OID for file if possible */
+ if (S_ISGITLINK(mode)) {
+ git_submodule *sm;
+
+ if (!git_submodule_lookup(&sm, diff->base.repo, entry.path)) {
+ const git_oid *sm_oid = git_submodule_wd_id(sm);
+ if (sm_oid)
+ git_oid_cpy(out, sm_oid);
+ git_submodule_free(sm);
+ } else {
+ /* if submodule lookup failed probably just in an intermediate
+ * state where some init hasn't happened, so ignore the error
+ */
+ git_error_clear();
+ }
+ } else if (S_ISLNK(mode)) {
+ error = git_odb__hashlink(out, full_path.ptr,
+ diff->base.opts.oid_type);
+ diff->base.perf.oid_calculations++;
+ } else if (!git__is_sizet(entry.file_size)) {
+ git_error_set(GIT_ERROR_NOMEMORY, "file size overflow (for 32-bits) on '%s'",
+ entry.path);
+ error = -1;
+ } else if (!(error = git_filter_list_load(&fl,
+ diff->base.repo, NULL, entry.path,
+ GIT_FILTER_TO_ODB, GIT_FILTER_ALLOW_UNSAFE)))
+ {
+ int fd = git_futils_open_ro(full_path.ptr);
+ if (fd < 0)
+ error = fd;
+ else {
+ error = git_odb__hashfd_filtered(
+ out, fd, (size_t)entry.file_size,
+ GIT_OBJECT_BLOB, diff->base.opts.oid_type,
+ fl);
+ p_close(fd);
+ diff->base.perf.oid_calculations++;
+ }
+
+ git_filter_list_free(fl);
+ }
+
+ /* update index for entry if requested */
+ if (!error && update_match && git_oid_equal(out, update_match)) {
+ git_index *idx;
+ git_index_entry updated_entry;
+
+ memcpy(&updated_entry, &entry, sizeof(git_index_entry));
+ updated_entry.mode = mode;
+ git_oid_cpy(&updated_entry.id, out);
+
+ if (!(error = git_repository_index__weakptr(&idx,
+ diff->base.repo))) {
+ error = git_index_add(idx, &updated_entry);
+ diff->index_updated = true;
+ }
+ }
+
+ git_str_dispose(&full_path);
+ return error;
+}
+
+typedef struct {
+ git_repository *repo;
+ git_iterator *old_iter;
+ git_iterator *new_iter;
+ const git_index_entry *oitem;
+ const git_index_entry *nitem;
+ git_strmap *submodule_cache;
+ bool submodule_cache_initialized;
+} diff_in_progress;
+
+#define MODE_BITS_MASK 0000777
+
+static int maybe_modified_submodule(
+ git_delta_t *status,
+ git_oid *found_oid,
+ git_diff_generated *diff,
+ diff_in_progress *info)
+{
+ int error = 0;
+ git_submodule *sub;
+ unsigned int sm_status = 0;
+ git_submodule_ignore_t ign = diff->base.opts.ignore_submodules;
+ git_strmap *submodule_cache = NULL;
+
+ *status = GIT_DELTA_UNMODIFIED;
+
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES) ||
+ ign == GIT_SUBMODULE_IGNORE_ALL)
+ return 0;
+
+ if (diff->base.repo->submodule_cache != NULL) {
+ submodule_cache = diff->base.repo->submodule_cache;
+ } else {
+ if (!info->submodule_cache_initialized) {
+ info->submodule_cache_initialized = true;
+ /*
+ * Try to cache the submodule information to avoid having to parse it for
+ * every submodule. It is okay if it fails, the cache will still be NULL
+ * and the submodules will be attempted to be looked up individually.
+ */
+ git_submodule_cache_init(&info->submodule_cache, diff->base.repo);
+ }
+ submodule_cache = info->submodule_cache;
+ }
+
+ if ((error = git_submodule__lookup_with_cache(
+ &sub, diff->base.repo, info->nitem->path, submodule_cache)) < 0) {
+
+ /* GIT_EEXISTS means dir with .git in it was found - ignore it */
+ if (error == GIT_EEXISTS) {
+ git_error_clear();
+ error = 0;
+ }
+ return error;
+ }
+
+ if (ign <= 0 && git_submodule_ignore(sub) == GIT_SUBMODULE_IGNORE_ALL)
+ /* ignore it */;
+ else if ((error = git_submodule__status(
+ &sm_status, NULL, NULL, found_oid, sub, ign)) < 0)
+ /* return error below */;
+
+ /* check IS_WD_UNMODIFIED because this case is only used
+ * when the new side of the diff is the working directory
+ */
+ else if (!GIT_SUBMODULE_STATUS_IS_WD_UNMODIFIED(sm_status))
+ *status = GIT_DELTA_MODIFIED;
+
+ /* now that we have a HEAD OID, check if HEAD moved */
+ else if ((sm_status & GIT_SUBMODULE_STATUS_IN_WD) != 0 &&
+ !git_oid_equal(&info->oitem->id, found_oid))
+ *status = GIT_DELTA_MODIFIED;
+
+ git_submodule_free(sub);
+ return error;
+}
+
+static int maybe_modified(
+ git_diff_generated *diff,
+ diff_in_progress *info)
+{
+ git_oid noid;
+ git_delta_t status = GIT_DELTA_MODIFIED;
+ const git_index_entry *oitem = info->oitem;
+ const git_index_entry *nitem = info->nitem;
+ unsigned int omode = oitem->mode;
+ unsigned int nmode = nitem->mode;
+ bool new_is_workdir = (info->new_iter->type == GIT_ITERATOR_WORKDIR);
+ bool modified_uncertain = false;
+ const char *matched_pathspec;
+ int error = 0;
+
+ git_oid_clear(&noid, diff->base.opts.oid_type);
+
+ if (!diff_pathspec_match(&matched_pathspec, diff, oitem))
+ return 0;
+
+ /* on platforms with no symlinks, preserve mode of existing symlinks */
+ if (S_ISLNK(omode) && S_ISREG(nmode) && new_is_workdir &&
+ !(diff->diffcaps & GIT_DIFFCAPS_HAS_SYMLINKS))
+ nmode = omode;
+
+ /* on platforms with no execmode, just preserve old mode */
+ if (!(diff->diffcaps & GIT_DIFFCAPS_TRUST_MODE_BITS) &&
+ (nmode & MODE_BITS_MASK) != (omode & MODE_BITS_MASK) &&
+ new_is_workdir)
+ nmode = (nmode & ~MODE_BITS_MASK) | (omode & MODE_BITS_MASK);
+
+ /* if one side is a conflict, mark the whole delta as conflicted */
+ if (git_index_entry_is_conflict(oitem) ||
+ git_index_entry_is_conflict(nitem)) {
+ status = GIT_DELTA_CONFLICTED;
+
+ /* support "assume unchanged" (poorly, b/c we still stat everything) */
+ } else if ((oitem->flags & GIT_INDEX_ENTRY_VALID) != 0) {
+ status = GIT_DELTA_UNMODIFIED;
+
+ /* support "skip worktree" index bit */
+ } else if ((oitem->flags_extended & GIT_INDEX_ENTRY_SKIP_WORKTREE) != 0) {
+ status = GIT_DELTA_UNMODIFIED;
+
+ /* if basic type of file changed, then split into delete and add */
+ } else if (GIT_MODE_TYPE(omode) != GIT_MODE_TYPE(nmode)) {
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE)) {
+ status = GIT_DELTA_TYPECHANGE;
+ }
+
+ else if (nmode == GIT_FILEMODE_UNREADABLE) {
+ if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem, NULL)))
+ error = diff_delta__from_one(diff, GIT_DELTA_UNREADABLE, NULL, nitem);
+ return error;
+ }
+
+ else {
+ if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem, NULL)))
+ error = diff_delta__from_one(diff, GIT_DELTA_ADDED, NULL, nitem);
+ return error;
+ }
+
+ /* if oids and modes match (and are valid), then file is unmodified */
+ } else if (git_oid_equal(&oitem->id, &nitem->id) &&
+ omode == nmode &&
+ !git_oid_is_zero(&oitem->id)) {
+ status = GIT_DELTA_UNMODIFIED;
+
+ /* if we have an unknown OID and a workdir iterator, then check some
+ * circumstances that can accelerate things or need special handling
+ */
+ } else if (git_oid_is_zero(&nitem->id) && new_is_workdir) {
+ bool use_ctime =
+ ((diff->diffcaps & GIT_DIFFCAPS_TRUST_CTIME) != 0);
+ git_index *index = git_iterator_index(info->new_iter);
+
+ status = GIT_DELTA_UNMODIFIED;
+
+ if (S_ISGITLINK(nmode)) {
+ if ((error = maybe_modified_submodule(&status, &noid, diff, info)) < 0)
+ return error;
+ }
+
+ /* if the stat data looks different, then mark modified - this just
+ * means that the OID will be recalculated below to confirm change
+ */
+ else if (omode != nmode || oitem->file_size != nitem->file_size) {
+ status = GIT_DELTA_MODIFIED;
+ modified_uncertain =
+ (oitem->file_size <= 0 && nitem->file_size > 0);
+ }
+ else if (!git_index_time_eq(&oitem->mtime, &nitem->mtime) ||
+ (use_ctime && !git_index_time_eq(&oitem->ctime, &nitem->ctime)) ||
+ oitem->ino != nitem->ino ||
+ oitem->uid != nitem->uid ||
+ oitem->gid != nitem->gid ||
+ git_index_entry_newer_than_index(nitem, index))
+ {
+ status = GIT_DELTA_MODIFIED;
+ modified_uncertain = true;
+ }
+
+ /* if mode is GITLINK and submodules are ignored, then skip */
+ } else if (S_ISGITLINK(nmode) &&
+ DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_SUBMODULES)) {
+ status = GIT_DELTA_UNMODIFIED;
+ }
+
+ /* if we got here and decided that the files are modified, but we
+ * haven't calculated the OID of the new item, then calculate it now
+ */
+ if (modified_uncertain && git_oid_is_zero(&nitem->id)) {
+ const git_oid *update_check =
+ DIFF_FLAG_IS_SET(diff, GIT_DIFF_UPDATE_INDEX) && omode == nmode ?
+ &oitem->id : NULL;
+
+ if ((error = git_diff__oid_for_entry(
+ &noid, &diff->base, nitem, nmode, update_check)) < 0)
+ return error;
+
+ /* if oid matches, then mark unmodified (except submodules, where
+ * the filesystem content may be modified even if the oid still
+ * matches between the index and the workdir HEAD)
+ */
+ if (omode == nmode && !S_ISGITLINK(omode) &&
+ git_oid_equal(&oitem->id, &noid))
+ status = GIT_DELTA_UNMODIFIED;
+ }
+
+ /* If we want case changes, then break this into a delete of the old
+ * and an add of the new so that consumers can act accordingly (eg,
+ * checkout will update the case on disk.)
+ */
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE) &&
+ DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_CASECHANGE) &&
+ strcmp(oitem->path, nitem->path) != 0) {
+
+ if (!(error = diff_delta__from_one(diff, GIT_DELTA_DELETED, oitem, NULL)))
+ error = diff_delta__from_one(diff, GIT_DELTA_ADDED, NULL, nitem);
+
+ return error;
+ }
+
+ return diff_delta__from_two(
+ diff, status, oitem, omode, nitem, nmode,
+ git_oid_is_zero(&noid) ? NULL : &noid, matched_pathspec);
+}
+
+static bool entry_is_prefixed(
+ git_diff_generated *diff,
+ const git_index_entry *item,
+ const git_index_entry *prefix_item)
+{
+ size_t pathlen;
+
+ if (!item || diff->base.pfxcomp(item->path, prefix_item->path) != 0)
+ return false;
+
+ pathlen = strlen(prefix_item->path);
+
+ return (prefix_item->path[pathlen - 1] == '/' ||
+ item->path[pathlen] == '\0' ||
+ item->path[pathlen] == '/');
+}
+
+static int iterator_current(
+ const git_index_entry **entry,
+ git_iterator *iterator)
+{
+ int error;
+
+ if ((error = git_iterator_current(entry, iterator)) == GIT_ITEROVER) {
+ *entry = NULL;
+ error = 0;
+ }
+
+ return error;
+}
+
+static int iterator_advance(
+ const git_index_entry **entry,
+ git_iterator *iterator)
+{
+ const git_index_entry *prev_entry = *entry;
+ int cmp, error;
+
+ /* if we're looking for conflicts, we only want to report
+ * one conflict for each file, instead of all three sides.
+ * so if this entry is a conflict for this file, and the
+ * previous one was a conflict for the same file, skip it.
+ */
+ while ((error = git_iterator_advance(entry, iterator)) == 0) {
+ if (!(iterator->flags & GIT_ITERATOR_INCLUDE_CONFLICTS) ||
+ !git_index_entry_is_conflict(prev_entry) ||
+ !git_index_entry_is_conflict(*entry))
+ break;
+
+ cmp = (iterator->flags & GIT_ITERATOR_IGNORE_CASE) ?
+ strcasecmp(prev_entry->path, (*entry)->path) :
+ strcmp(prev_entry->path, (*entry)->path);
+
+ if (cmp)
+ break;
+ }
+
+ if (error == GIT_ITEROVER) {
+ *entry = NULL;
+ error = 0;
+ }
+
+ return error;
+}
+
+static int iterator_advance_into(
+ const git_index_entry **entry,
+ git_iterator *iterator)
+{
+ int error;
+
+ if ((error = git_iterator_advance_into(entry, iterator)) == GIT_ITEROVER) {
+ *entry = NULL;
+ error = 0;
+ }
+
+ return error;
+}
+
+static int iterator_advance_over(
+ const git_index_entry **entry,
+ git_iterator_status_t *status,
+ git_iterator *iterator)
+{
+ int error = git_iterator_advance_over(entry, status, iterator);
+
+ if (error == GIT_ITEROVER) {
+ *entry = NULL;
+ error = 0;
+ }
+
+ return error;
+}
+
+static int handle_unmatched_new_item(
+ git_diff_generated *diff, diff_in_progress *info)
+{
+ int error = 0;
+ const git_index_entry *nitem = info->nitem;
+ git_delta_t delta_type = GIT_DELTA_UNTRACKED;
+ bool contains_oitem;
+
+ /* check if this is a prefix of the other side */
+ contains_oitem = entry_is_prefixed(diff, info->oitem, nitem);
+
+ /* update delta_type if this item is conflicted */
+ if (git_index_entry_is_conflict(nitem))
+ delta_type = GIT_DELTA_CONFLICTED;
+
+ /* update delta_type if this item is ignored */
+ else if (git_iterator_current_is_ignored(info->new_iter))
+ delta_type = GIT_DELTA_IGNORED;
+
+ if (nitem->mode == GIT_FILEMODE_TREE) {
+ bool recurse_into_dir = contains_oitem;
+
+ /* check if user requests recursion into this type of dir */
+ recurse_into_dir = contains_oitem ||
+ (delta_type == GIT_DELTA_UNTRACKED &&
+ DIFF_FLAG_IS_SET(diff, GIT_DIFF_RECURSE_UNTRACKED_DIRS)) ||
+ (delta_type == GIT_DELTA_IGNORED &&
+ DIFF_FLAG_IS_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS));
+
+ /* do not advance into directories that contain a .git file */
+ if (recurse_into_dir && !contains_oitem) {
+ git_str *full = NULL;
+ if (git_iterator_current_workdir_path(&full, info->new_iter) < 0)
+ return -1;
+ if (full && git_fs_path_contains(full, DOT_GIT)) {
+ /* TODO: warning if not a valid git repository */
+ recurse_into_dir = false;
+ }
+ }
+
+ /* still have to look into untracked directories to match core git -
+ * with no untracked files, directory is treated as ignored
+ */
+ if (!recurse_into_dir &&
+ delta_type == GIT_DELTA_UNTRACKED &&
+ DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS))
+ {
+ git_diff_delta *last;
+ git_iterator_status_t untracked_state;
+
+ /* attempt to insert record for this directory */
+ if ((error = diff_delta__from_one(diff, delta_type, NULL, nitem)) != 0)
+ return error;
+
+ /* if delta wasn't created (because of rules), just skip ahead */
+ last = diff_delta__last_for_item(diff, nitem);
+ if (!last)
+ return iterator_advance(&info->nitem, info->new_iter);
+
+ /* iterate into dir looking for an actual untracked file */
+ if ((error = iterator_advance_over(
+ &info->nitem, &untracked_state, info->new_iter)) < 0)
+ return error;
+
+ /* if we found nothing that matched our pathlist filter, exclude */
+ if (untracked_state == GIT_ITERATOR_STATUS_FILTERED) {
+ git_vector_pop(&diff->base.deltas);
+ git__free(last);
+ }
+
+ /* if we found nothing or just ignored items, update the record */
+ if (untracked_state == GIT_ITERATOR_STATUS_IGNORED ||
+ untracked_state == GIT_ITERATOR_STATUS_EMPTY) {
+ last->status = GIT_DELTA_IGNORED;
+
+ /* remove the record if we don't want ignored records */
+ if (DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_INCLUDE_IGNORED)) {
+ git_vector_pop(&diff->base.deltas);
+ git__free(last);
+ }
+ }
+
+ return 0;
+ }
+
+ /* try to advance into directory if necessary */
+ if (recurse_into_dir) {
+ error = iterator_advance_into(&info->nitem, info->new_iter);
+
+ /* if directory is empty, can't advance into it, so skip it */
+ if (error == GIT_ENOTFOUND) {
+ git_error_clear();
+ error = iterator_advance(&info->nitem, info->new_iter);
+ }
+
+ return error;
+ }
+ }
+
+ else if (delta_type == GIT_DELTA_IGNORED &&
+ DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_IGNORED_DIRS) &&
+ git_iterator_current_tree_is_ignored(info->new_iter))
+ /* item contained in ignored directory, so skip over it */
+ return iterator_advance(&info->nitem, info->new_iter);
+
+ else if (info->new_iter->type != GIT_ITERATOR_WORKDIR) {
+ if (delta_type != GIT_DELTA_CONFLICTED)
+ delta_type = GIT_DELTA_ADDED;
+ }
+
+ else if (nitem->mode == GIT_FILEMODE_COMMIT) {
+ /* ignore things that are not actual submodules */
+ if (git_submodule_lookup(NULL, info->repo, nitem->path) != 0) {
+ git_error_clear();
+ delta_type = GIT_DELTA_IGNORED;
+
+ /* if this contains a tracked item, treat as normal TREE */
+ if (contains_oitem) {
+ error = iterator_advance_into(&info->nitem, info->new_iter);
+ if (error != GIT_ENOTFOUND)
+ return error;
+
+ git_error_clear();
+ return iterator_advance(&info->nitem, info->new_iter);
+ }
+ }
+ }
+
+ else if (nitem->mode == GIT_FILEMODE_UNREADABLE) {
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED))
+ delta_type = GIT_DELTA_UNTRACKED;
+ else
+ delta_type = GIT_DELTA_UNREADABLE;
+ }
+
+ /* Actually create the record for this item if necessary */
+ if ((error = diff_delta__from_one(diff, delta_type, NULL, nitem)) != 0)
+ return error;
+
+ /* If user requested TYPECHANGE records, then check for that instead of
+ * just generating an ADDED/UNTRACKED record
+ */
+ if (delta_type != GIT_DELTA_IGNORED &&
+ DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES) &&
+ contains_oitem)
+ {
+ /* this entry was prefixed with a tree - make TYPECHANGE */
+ git_diff_delta *last = diff_delta__last_for_item(diff, nitem);
+ if (last) {
+ last->status = GIT_DELTA_TYPECHANGE;
+ last->old_file.mode = GIT_FILEMODE_TREE;
+ }
+ }
+
+ return iterator_advance(&info->nitem, info->new_iter);
+}
+
+static int handle_unmatched_old_item(
+ git_diff_generated *diff, diff_in_progress *info)
+{
+ git_delta_t delta_type = GIT_DELTA_DELETED;
+ int error;
+
+ /* update delta_type if this item is conflicted */
+ if (git_index_entry_is_conflict(info->oitem))
+ delta_type = GIT_DELTA_CONFLICTED;
+
+ if ((error = diff_delta__from_one(diff, delta_type, info->oitem, NULL)) < 0)
+ return error;
+
+ /* if we are generating TYPECHANGE records then check for that
+ * instead of just generating a DELETE record
+ */
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_INCLUDE_TYPECHANGE_TREES) &&
+ entry_is_prefixed(diff, info->nitem, info->oitem))
+ {
+ /* this entry has become a tree! convert to TYPECHANGE */
+ git_diff_delta *last = diff_delta__last_for_item(diff, info->oitem);
+ if (last) {
+ last->status = GIT_DELTA_TYPECHANGE;
+ last->new_file.mode = GIT_FILEMODE_TREE;
+ }
+
+ /* If new_iter is a workdir iterator, then this situation
+ * will certainly be followed by a series of untracked items.
+ * Unless RECURSE_UNTRACKED_DIRS is set, skip over them...
+ */
+ if (S_ISDIR(info->nitem->mode) &&
+ DIFF_FLAG_ISNT_SET(diff, GIT_DIFF_RECURSE_UNTRACKED_DIRS))
+ return iterator_advance(&info->nitem, info->new_iter);
+ }
+
+ return iterator_advance(&info->oitem, info->old_iter);
+}
+
+static int handle_matched_item(
+ git_diff_generated *diff, diff_in_progress *info)
+{
+ int error = 0;
+
+ if ((error = maybe_modified(diff, info)) < 0)
+ return error;
+
+ if (!(error = iterator_advance(&info->oitem, info->old_iter)))
+ error = iterator_advance(&info->nitem, info->new_iter);
+
+ return error;
+}
+
+int git_diff__from_iterators(
+ git_diff **out,
+ git_repository *repo,
+ git_iterator *old_iter,
+ git_iterator *new_iter,
+ const git_diff_options *opts)
+{
+ git_diff_generated *diff;
+ diff_in_progress info = {0};
+ int error = 0;
+
+ *out = NULL;
+
+ diff = diff_generated_alloc(repo, old_iter, new_iter);
+ GIT_ERROR_CHECK_ALLOC(diff);
+
+ info.repo = repo;
+ info.old_iter = old_iter;
+ info.new_iter = new_iter;
+
+ /* make iterators have matching icase behavior */
+ if (DIFF_FLAG_IS_SET(diff, GIT_DIFF_IGNORE_CASE)) {
+ if ((error = git_iterator_set_ignore_case(old_iter, true)) < 0 ||
+ (error = git_iterator_set_ignore_case(new_iter, true)) < 0)
+ goto cleanup;
+ }
+
+ /* finish initialization */
+ if ((error = diff_generated_apply_options(diff, opts)) < 0)
+ goto cleanup;
+
+ if ((error = iterator_current(&info.oitem, old_iter)) < 0 ||
+ (error = iterator_current(&info.nitem, new_iter)) < 0)
+ goto cleanup;
+
+ /* run iterators building diffs */
+ while (!error && (info.oitem || info.nitem)) {
+ int cmp;
+
+ /* report progress */
+ if (opts && opts->progress_cb) {
+ if ((error = opts->progress_cb(&diff->base,
+ info.oitem ? info.oitem->path : NULL,
+ info.nitem ? info.nitem->path : NULL,
+ opts->payload)))
+ break;
+ }
+
+ cmp = info.oitem ?
+ (info.nitem ? diff->base.entrycomp(info.oitem, info.nitem) : -1) : 1;
+
+ /* create DELETED records for old items not matched in new */
+ if (cmp < 0)
+ error = handle_unmatched_old_item(diff, &info);
+
+ /* create ADDED, TRACKED, or IGNORED records for new items not
+ * matched in old (and/or descend into directories as needed)
+ */
+ else if (cmp > 0)
+ error = handle_unmatched_new_item(diff, &info);
+
+ /* otherwise item paths match, so create MODIFIED record
+ * (or ADDED and DELETED pair if type changed)
+ */
+ else
+ error = handle_matched_item(diff, &info);
+ }
+
+ diff->base.perf.stat_calls +=
+ old_iter->stat_calls + new_iter->stat_calls;
+
+cleanup:
+ if (!error)
+ *out = &diff->base;
+ else
+ git_diff_free(&diff->base);
+ if (info.submodule_cache)
+ git_submodule_cache_free(info.submodule_cache);
+
+ return error;
+}
+
+static int diff_prepare_iterator_opts(char **prefix, git_iterator_options *a, int aflags,
+ git_iterator_options *b, int bflags,
+ const git_diff_options *opts)
+{
+ GIT_ERROR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options");
+
+ *prefix = NULL;
+
+ if (opts && (opts->flags & GIT_DIFF_DISABLE_PATHSPEC_MATCH)) {
+ a->pathlist.strings = opts->pathspec.strings;
+ a->pathlist.count = opts->pathspec.count;
+ b->pathlist.strings = opts->pathspec.strings;
+ b->pathlist.count = opts->pathspec.count;
+ } else if (opts) {
+ *prefix = git_pathspec_prefix(&opts->pathspec);
+ GIT_ERROR_CHECK_ALLOC(prefix);
+ }
+
+ a->flags = aflags;
+ b->flags = bflags;
+ a->start = b->start = *prefix;
+ a->end = b->end = *prefix;
+
+ return 0;
+}
+
+int git_diff_tree_to_tree(
+ git_diff **out,
+ git_repository *repo,
+ git_tree *old_tree,
+ git_tree *new_tree,
+ const git_diff_options *opts)
+{
+ git_iterator_flag_t iflag = GIT_ITERATOR_DONT_IGNORE_CASE;
+ git_iterator_options a_opts = GIT_ITERATOR_OPTIONS_INIT,
+ b_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_iterator *a = NULL, *b = NULL;
+ git_diff *diff = NULL;
+ char *prefix = NULL;
+ int error = 0;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+
+ *out = NULL;
+
+ /* for tree to tree diff, be case sensitive even if the index is
+ * currently case insensitive, unless the user explicitly asked
+ * for case insensitivity
+ */
+ if (opts && (opts->flags & GIT_DIFF_IGNORE_CASE) != 0)
+ iflag = GIT_ITERATOR_IGNORE_CASE;
+
+ if ((error = diff_prepare_iterator_opts(&prefix, &a_opts, iflag, &b_opts, iflag, opts)) < 0 ||
+ (error = git_iterator_for_tree(&a, old_tree, &a_opts)) < 0 ||
+ (error = git_iterator_for_tree(&b, new_tree, &b_opts)) < 0 ||
+ (error = git_diff__from_iterators(&diff, repo, a, b, opts)) < 0)
+ goto out;
+
+ *out = diff;
+ diff = NULL;
+out:
+ git_iterator_free(a);
+ git_iterator_free(b);
+ git_diff_free(diff);
+ git__free(prefix);
+
+ return error;
+}
+
+static int diff_load_index(git_index **index, git_repository *repo)
+{
+ int error = git_repository_index__weakptr(index, repo);
+
+ /* reload the repository index when user did not pass one in */
+ if (!error && git_index_read(*index, false) < 0)
+ git_error_clear();
+
+ return error;
+}
+
+int git_diff_tree_to_index(
+ git_diff **out,
+ git_repository *repo,
+ git_tree *old_tree,
+ git_index *index,
+ const git_diff_options *opts)
+{
+ git_iterator_flag_t iflag = GIT_ITERATOR_DONT_IGNORE_CASE |
+ GIT_ITERATOR_INCLUDE_CONFLICTS;
+ git_iterator_options a_opts = GIT_ITERATOR_OPTIONS_INIT,
+ b_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_iterator *a = NULL, *b = NULL;
+ git_diff *diff = NULL;
+ char *prefix = NULL;
+ bool index_ignore_case = false;
+ int error = 0;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+
+ *out = NULL;
+
+ if (!index && (error = diff_load_index(&index, repo)) < 0)
+ return error;
+
+ index_ignore_case = index->ignore_case;
+
+ if ((error = diff_prepare_iterator_opts(&prefix, &a_opts, iflag, &b_opts, iflag, opts)) < 0 ||
+ (error = git_iterator_for_tree(&a, old_tree, &a_opts)) < 0 ||
+ (error = git_iterator_for_index(&b, repo, index, &b_opts)) < 0 ||
+ (error = git_diff__from_iterators(&diff, repo, a, b, opts)) < 0)
+ goto out;
+
+ /* if index is in case-insensitive order, re-sort deltas to match */
+ if (index_ignore_case)
+ diff_set_ignore_case(diff, true);
+
+ *out = diff;
+ diff = NULL;
+out:
+ git_iterator_free(a);
+ git_iterator_free(b);
+ git_diff_free(diff);
+ git__free(prefix);
+
+ return error;
+}
+
+int git_diff_index_to_workdir(
+ git_diff **out,
+ git_repository *repo,
+ git_index *index,
+ const git_diff_options *opts)
+{
+ git_iterator_options a_opts = GIT_ITERATOR_OPTIONS_INIT,
+ b_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_iterator *a = NULL, *b = NULL;
+ git_diff *diff = NULL;
+ char *prefix = NULL;
+ int error = 0;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+
+ *out = NULL;
+
+ if (!index && (error = diff_load_index(&index, repo)) < 0)
+ return error;
+
+ if ((error = diff_prepare_iterator_opts(&prefix, &a_opts, GIT_ITERATOR_INCLUDE_CONFLICTS,
+ &b_opts, GIT_ITERATOR_DONT_AUTOEXPAND, opts)) < 0 ||
+ (error = git_iterator_for_index(&a, repo, index, &a_opts)) < 0 ||
+ (error = git_iterator_for_workdir(&b, repo, index, NULL, &b_opts)) < 0 ||
+ (error = git_diff__from_iterators(&diff, repo, a, b, opts)) < 0)
+ goto out;
+
+ if ((diff->opts.flags & GIT_DIFF_UPDATE_INDEX) && ((git_diff_generated *)diff)->index_updated)
+ if ((error = git_index_write(index)) < 0)
+ goto out;
+
+ *out = diff;
+ diff = NULL;
+out:
+ git_iterator_free(a);
+ git_iterator_free(b);
+ git_diff_free(diff);
+ git__free(prefix);
+
+ return error;
+}
+
+int git_diff_tree_to_workdir(
+ git_diff **out,
+ git_repository *repo,
+ git_tree *old_tree,
+ const git_diff_options *opts)
+{
+ git_iterator_options a_opts = GIT_ITERATOR_OPTIONS_INIT,
+ b_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_iterator *a = NULL, *b = NULL;
+ git_diff *diff = NULL;
+ char *prefix = NULL;
+ git_index *index;
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+
+ *out = NULL;
+
+ if ((error = diff_prepare_iterator_opts(&prefix, &a_opts, 0,
+ &b_opts, GIT_ITERATOR_DONT_AUTOEXPAND, opts) < 0) ||
+ (error = git_repository_index__weakptr(&index, repo)) < 0 ||
+ (error = git_iterator_for_tree(&a, old_tree, &a_opts)) < 0 ||
+ (error = git_iterator_for_workdir(&b, repo, index, old_tree, &b_opts)) < 0 ||
+ (error = git_diff__from_iterators(&diff, repo, a, b, opts)) < 0)
+ goto out;
+
+ *out = diff;
+ diff = NULL;
+out:
+ git_iterator_free(a);
+ git_iterator_free(b);
+ git_diff_free(diff);
+ git__free(prefix);
+
+ return error;
+}
+
+int git_diff_tree_to_workdir_with_index(
+ git_diff **out,
+ git_repository *repo,
+ git_tree *tree,
+ const git_diff_options *opts)
+{
+ git_diff *d1 = NULL, *d2 = NULL;
+ git_index *index = NULL;
+ int error = 0;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+
+ *out = NULL;
+
+ if ((error = diff_load_index(&index, repo)) < 0)
+ return error;
+
+ if (!(error = git_diff_tree_to_index(&d1, repo, tree, index, opts)) &&
+ !(error = git_diff_index_to_workdir(&d2, repo, index, opts)))
+ error = git_diff_merge(d1, d2);
+
+ git_diff_free(d2);
+
+ if (error) {
+ git_diff_free(d1);
+ d1 = NULL;
+ }
+
+ *out = d1;
+ return error;
+}
+
+int git_diff_index_to_index(
+ git_diff **out,
+ git_repository *repo,
+ git_index *old_index,
+ git_index *new_index,
+ const git_diff_options *opts)
+{
+ git_iterator_options a_opts = GIT_ITERATOR_OPTIONS_INIT,
+ b_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_iterator *a = NULL, *b = NULL;
+ git_diff *diff = NULL;
+ char *prefix = NULL;
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(old_index);
+ GIT_ASSERT_ARG(new_index);
+
+ *out = NULL;
+
+ if ((error = diff_prepare_iterator_opts(&prefix, &a_opts, GIT_ITERATOR_DONT_IGNORE_CASE,
+ &b_opts, GIT_ITERATOR_DONT_IGNORE_CASE, opts) < 0) ||
+ (error = git_iterator_for_index(&a, repo, old_index, &a_opts)) < 0 ||
+ (error = git_iterator_for_index(&b, repo, new_index, &b_opts)) < 0 ||
+ (error = git_diff__from_iterators(&diff, repo, a, b, opts)) < 0)
+ goto out;
+
+ /* if index is in case-insensitive order, re-sort deltas to match */
+ if (old_index->ignore_case || new_index->ignore_case)
+ diff_set_ignore_case(diff, true);
+
+ *out = diff;
+ diff = NULL;
+out:
+ git_iterator_free(a);
+ git_iterator_free(b);
+ git_diff_free(diff);
+ git__free(prefix);
+
+ return error;
+}
+
+int git_diff__paired_foreach(
+ git_diff *head2idx,
+ git_diff *idx2wd,
+ int (*cb)(git_diff_delta *h2i, git_diff_delta *i2w, void *payload),
+ void *payload)
+{
+ int cmp, error = 0;
+ git_diff_delta *h2i, *i2w;
+ size_t i, j, i_max, j_max;
+ int (*strcomp)(const char *, const char *) = git__strcmp;
+ bool h2i_icase, i2w_icase, icase_mismatch;
+
+ i_max = head2idx ? head2idx->deltas.length : 0;
+ j_max = idx2wd ? idx2wd->deltas.length : 0;
+ if (!i_max && !j_max)
+ return 0;
+
+ /* At some point, tree-to-index diffs will probably never ignore case,
+ * even if that isn't true now. Index-to-workdir diffs may or may not
+ * ignore case, but the index filename for the idx2wd diff should
+ * still be using the canonical case-preserving name.
+ *
+ * Therefore the main thing we need to do here is make sure the diffs
+ * are traversed in a compatible order. To do this, we temporarily
+ * resort a mismatched diff to get the order correct.
+ *
+ * In order to traverse renames in the index->workdir, we need to
+ * ensure that we compare the index name on both sides, so we
+ * always sort by the old name in the i2w list.
+ */
+ h2i_icase = head2idx != NULL && git_diff_is_sorted_icase(head2idx);
+ i2w_icase = idx2wd != NULL && git_diff_is_sorted_icase(idx2wd);
+
+ icase_mismatch =
+ (head2idx != NULL && idx2wd != NULL && h2i_icase != i2w_icase);
+
+ if (icase_mismatch && h2i_icase) {
+ git_vector_set_cmp(&head2idx->deltas, git_diff_delta__cmp);
+ git_vector_sort(&head2idx->deltas);
+ }
+
+ if (i2w_icase && !icase_mismatch) {
+ strcomp = git__strcasecmp;
+
+ git_vector_set_cmp(&idx2wd->deltas, diff_delta_i2w_casecmp);
+ git_vector_sort(&idx2wd->deltas);
+ } else if (idx2wd != NULL) {
+ git_vector_set_cmp(&idx2wd->deltas, diff_delta_i2w_cmp);
+ git_vector_sort(&idx2wd->deltas);
+ }
+
+ for (i = 0, j = 0; i < i_max || j < j_max; ) {
+ h2i = head2idx ? GIT_VECTOR_GET(&head2idx->deltas, i) : NULL;
+ i2w = idx2wd ? GIT_VECTOR_GET(&idx2wd->deltas, j) : NULL;
+
+ cmp = !i2w ? -1 : !h2i ? 1 :
+ strcomp(h2i->new_file.path, i2w->old_file.path);
+
+ if (cmp < 0) {
+ i++; i2w = NULL;
+ } else if (cmp > 0) {
+ j++; h2i = NULL;
+ } else {
+ i++; j++;
+ }
+
+ if ((error = cb(h2i, i2w, payload)) != 0) {
+ git_error_set_after_callback(error);
+ break;
+ }
+ }
+
+ /* restore case-insensitive delta sort */
+ if (icase_mismatch && h2i_icase) {
+ git_vector_set_cmp(&head2idx->deltas, git_diff_delta__casecmp);
+ git_vector_sort(&head2idx->deltas);
+ }
+
+ /* restore idx2wd sort by new path */
+ if (idx2wd != NULL) {
+ git_vector_set_cmp(&idx2wd->deltas,
+ i2w_icase ? git_diff_delta__casecmp : git_diff_delta__cmp);
+ git_vector_sort(&idx2wd->deltas);
+ }
+
+ return error;
+}
+
+int git_diff__commit(
+ git_diff **out,
+ git_repository *repo,
+ const git_commit *commit,
+ const git_diff_options *opts)
+{
+ git_commit *parent = NULL;
+ git_diff *commit_diff = NULL;
+ git_tree *old_tree = NULL, *new_tree = NULL;
+ size_t parents;
+ int error = 0;
+
+ *out = NULL;
+
+ if ((parents = git_commit_parentcount(commit)) > 1) {
+ char commit_oidstr[GIT_OID_MAX_HEXSIZE + 1];
+
+ error = -1;
+ git_error_set(GIT_ERROR_INVALID, "commit %s is a merge commit",
+ git_oid_tostr(commit_oidstr, GIT_OID_MAX_HEXSIZE + 1, git_commit_id(commit)));
+ goto on_error;
+ }
+
+ if (parents > 0)
+ if ((error = git_commit_parent(&parent, commit, 0)) < 0 ||
+ (error = git_commit_tree(&old_tree, parent)) < 0)
+ goto on_error;
+
+ if ((error = git_commit_tree(&new_tree, commit)) < 0 ||
+ (error = git_diff_tree_to_tree(&commit_diff, repo, old_tree, new_tree, opts)) < 0)
+ goto on_error;
+
+ *out = commit_diff;
+
+on_error:
+ git_tree_free(new_tree);
+ git_tree_free(old_tree);
+ git_commit_free(parent);
+
+ return error;
+}
+
diff --git a/src/libgit2/diff_generate.h b/src/libgit2/diff_generate.h
new file mode 100644
index 0000000..b782f29
--- /dev/null
+++ b/src/libgit2/diff_generate.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_diff_generate_h__
+#define INCLUDE_diff_generate_h__
+
+#include "common.h"
+
+#include "diff.h"
+#include "pool.h"
+#include "index.h"
+
+enum {
+ GIT_DIFFCAPS_HAS_SYMLINKS = (1 << 0), /* symlinks on platform? */
+ GIT_DIFFCAPS_IGNORE_STAT = (1 << 1), /* use stat? */
+ GIT_DIFFCAPS_TRUST_MODE_BITS = (1 << 2), /* use st_mode? */
+ GIT_DIFFCAPS_TRUST_CTIME = (1 << 3), /* use st_ctime? */
+ GIT_DIFFCAPS_USE_DEV = (1 << 4) /* use st_dev? */
+};
+
+#define DIFF_FLAGS_KNOWN_BINARY (GIT_DIFF_FLAG_BINARY|GIT_DIFF_FLAG_NOT_BINARY)
+#define DIFF_FLAGS_NOT_BINARY (GIT_DIFF_FLAG_NOT_BINARY|GIT_DIFF_FLAG__NO_DATA)
+
+enum {
+ GIT_DIFF_FLAG__FREE_PATH = (1 << 7), /* `path` is allocated memory */
+ GIT_DIFF_FLAG__FREE_DATA = (1 << 8), /* internal file data is allocated */
+ GIT_DIFF_FLAG__UNMAP_DATA = (1 << 9), /* internal file data is mmap'ed */
+ GIT_DIFF_FLAG__NO_DATA = (1 << 10), /* file data should not be loaded */
+ GIT_DIFF_FLAG__FREE_BLOB = (1 << 11), /* release the blob when done */
+ GIT_DIFF_FLAG__LOADED = (1 << 12), /* file data has been loaded */
+
+ GIT_DIFF_FLAG__TO_DELETE = (1 << 16), /* delete entry during rename det. */
+ GIT_DIFF_FLAG__TO_SPLIT = (1 << 17), /* split entry during rename det. */
+ GIT_DIFF_FLAG__IS_RENAME_TARGET = (1 << 18),
+ GIT_DIFF_FLAG__IS_RENAME_SOURCE = (1 << 19),
+ GIT_DIFF_FLAG__HAS_SELF_SIMILARITY = (1 << 20)
+};
+
+#define GIT_DIFF_FLAG__CLEAR_INTERNAL(F) (F) = ((F) & 0x00FFFF)
+
+#define GIT_DIFF__VERBOSE (1 << 30)
+
+extern void git_diff_addref(git_diff *diff);
+
+extern bool git_diff_delta__should_skip(
+ const git_diff_options *opts, const git_diff_delta *delta);
+
+extern int git_diff__from_iterators(
+ git_diff **diff_ptr,
+ git_repository *repo,
+ git_iterator *old_iter,
+ git_iterator *new_iter,
+ const git_diff_options *opts);
+
+extern int git_diff__commit(
+ git_diff **diff, git_repository *repo, const git_commit *commit, const git_diff_options *opts);
+
+extern int git_diff__paired_foreach(
+ git_diff *idx2head,
+ git_diff *wd2idx,
+ int (*cb)(git_diff_delta *i2h, git_diff_delta *w2i, void *payload),
+ void *payload);
+
+/* Merge two `git_diff`s according to the callback given by `cb`. */
+
+typedef git_diff_delta *(*git_diff__merge_cb)(
+ const git_diff_delta *left,
+ const git_diff_delta *right,
+ git_pool *pool);
+
+extern int git_diff__merge(
+ git_diff *onto, const git_diff *from, git_diff__merge_cb cb);
+
+extern git_diff_delta *git_diff__merge_like_cgit(
+ const git_diff_delta *a,
+ const git_diff_delta *b,
+ git_pool *pool);
+
+/* Duplicate a `git_diff_delta` out of the `git_pool` */
+extern git_diff_delta *git_diff__delta_dup(
+ const git_diff_delta *d, git_pool *pool);
+
+extern int git_diff__oid_for_file(
+ git_oid *out,
+ git_diff *diff,
+ const char *path,
+ uint16_t mode,
+ git_object_size_t size);
+
+extern int git_diff__oid_for_entry(
+ git_oid *out,
+ git_diff *diff,
+ const git_index_entry *src,
+ uint16_t mode,
+ const git_oid *update_match);
+
+/*
+ * Sometimes a git_diff_file will have a zero size; this attempts to
+ * fill in the size without loading the blob if possible. If that is
+ * not possible, then it will return the git_odb_object that had to be
+ * loaded and the caller can use it or dispose of it as needed.
+ */
+GIT_INLINE(int) git_diff_file__resolve_zero_size(
+ git_diff_file *file, git_odb_object **odb_obj, git_repository *repo)
+{
+ int error;
+ git_odb *odb;
+ size_t len;
+ git_object_t type;
+
+ if ((error = git_repository_odb(&odb, repo)) < 0)
+ return error;
+
+ error = git_odb__read_header_or_object(
+ odb_obj, &len, &type, odb, &file->id);
+
+ git_odb_free(odb);
+
+ if (!error) {
+ file->size = (git_object_size_t)len;
+ file->flags |= GIT_DIFF_FLAG_VALID_SIZE;
+ }
+
+ return error;
+}
+
+#endif
diff --git a/src/libgit2/diff_parse.c b/src/libgit2/diff_parse.c
new file mode 100644
index 0000000..0460396
--- /dev/null
+++ b/src/libgit2/diff_parse.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "diff_parse.h"
+
+#include "diff.h"
+#include "patch.h"
+#include "patch_parse.h"
+
+static void diff_parsed_free(git_diff *d)
+{
+ git_diff_parsed *diff = (git_diff_parsed *)d;
+ git_patch *patch;
+ size_t i;
+
+ git_vector_foreach(&diff->patches, i, patch)
+ git_patch_free(patch);
+
+ git_vector_free(&diff->patches);
+
+ git_vector_free(&diff->base.deltas);
+ git_pool_clear(&diff->base.pool);
+
+ git__memzero(diff, sizeof(*diff));
+ git__free(diff);
+}
+
+static git_diff_parsed *diff_parsed_alloc(git_oid_t oid_type)
+{
+ git_diff_parsed *diff;
+
+ if ((diff = git__calloc(1, sizeof(git_diff_parsed))) == NULL)
+ return NULL;
+
+ GIT_REFCOUNT_INC(&diff->base);
+ diff->base.type = GIT_DIFF_TYPE_PARSED;
+ diff->base.strcomp = git__strcmp;
+ diff->base.strncomp = git__strncmp;
+ diff->base.pfxcomp = git__prefixcmp;
+ diff->base.entrycomp = git_diff__entry_cmp;
+ diff->base.patch_fn = git_patch_parsed_from_diff;
+ diff->base.free_fn = diff_parsed_free;
+
+ if (git_diff_options_init(&diff->base.opts, GIT_DIFF_OPTIONS_VERSION) < 0) {
+ git__free(diff);
+ return NULL;
+ }
+
+ diff->base.opts.flags &= ~GIT_DIFF_IGNORE_CASE;
+ diff->base.opts.oid_type = oid_type;
+
+ if (git_pool_init(&diff->base.pool, 1) < 0 ||
+ git_vector_init(&diff->patches, 0, NULL) < 0 ||
+ git_vector_init(&diff->base.deltas, 0, git_diff_delta__cmp) < 0) {
+ git_diff_free(&diff->base);
+ return NULL;
+ }
+
+ git_vector_set_cmp(&diff->base.deltas, git_diff_delta__cmp);
+
+ return diff;
+}
+
+int git_diff_from_buffer(
+ git_diff **out,
+ const char *content,
+ size_t content_len
+#ifdef GIT_EXPERIMENTAL_SHA256
+ , git_diff_parse_options *opts
+#endif
+ )
+{
+ git_diff_parsed *diff;
+ git_patch *patch;
+ git_patch_parse_ctx *ctx = NULL;
+ git_patch_options patch_opts = GIT_PATCH_OPTIONS_INIT;
+ git_oid_t oid_type;
+ int error = 0;
+
+ *out = NULL;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ oid_type = (opts && opts->oid_type) ? opts->oid_type :
+ GIT_OID_DEFAULT;
+#else
+ oid_type = GIT_OID_DEFAULT;
+#endif
+
+ patch_opts.oid_type = oid_type;
+
+ diff = diff_parsed_alloc(oid_type);
+ GIT_ERROR_CHECK_ALLOC(diff);
+
+ ctx = git_patch_parse_ctx_init(content, content_len, &patch_opts);
+ GIT_ERROR_CHECK_ALLOC(ctx);
+
+ while (ctx->parse_ctx.remain_len) {
+ if ((error = git_patch_parse(&patch, ctx)) < 0)
+ break;
+
+ git_vector_insert(&diff->patches, patch);
+ git_vector_insert(&diff->base.deltas, patch->delta);
+ }
+
+ if (error == GIT_ENOTFOUND && git_vector_length(&diff->patches) > 0) {
+ git_error_clear();
+ error = 0;
+ }
+
+ git_patch_parse_ctx_free(ctx);
+
+ if (error < 0)
+ git_diff_free(&diff->base);
+ else
+ *out = &diff->base;
+
+ return error;
+}
+
diff --git a/src/libgit2/diff_parse.h b/src/libgit2/diff_parse.h
new file mode 100644
index 0000000..8767821
--- /dev/null
+++ b/src/libgit2/diff_parse.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_diff_parse_h__
+#define INCLUDE_diff_parse_h__
+
+#include "common.h"
+
+#include "diff.h"
+
+typedef struct {
+ struct git_diff base;
+
+ git_vector patches;
+} git_diff_parsed;
+
+#endif
diff --git a/src/libgit2/diff_print.c b/src/libgit2/diff_print.c
new file mode 100644
index 0000000..32c9368
--- /dev/null
+++ b/src/libgit2/diff_print.c
@@ -0,0 +1,849 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+
+#include "buf.h"
+#include "diff.h"
+#include "diff_file.h"
+#include "patch_generate.h"
+#include "futils.h"
+#include "zstream.h"
+#include "blob.h"
+#include "delta.h"
+#include "git2/sys/diff.h"
+
+typedef struct {
+ git_diff_format_t format;
+ git_diff_line_cb print_cb;
+ void *payload;
+
+ git_str *buf;
+ git_diff_line line;
+
+ const char *old_prefix;
+ const char *new_prefix;
+ uint32_t flags;
+ int id_strlen;
+ git_oid_t oid_type;
+
+ int (*strcomp)(const char *, const char *);
+} diff_print_info;
+
+static int diff_print_info_init__common(
+ diff_print_info *pi,
+ git_str *out,
+ git_repository *repo,
+ git_diff_format_t format,
+ git_diff_line_cb cb,
+ void *payload)
+{
+ pi->format = format;
+ pi->print_cb = cb;
+ pi->payload = payload;
+ pi->buf = out;
+
+ GIT_ASSERT(pi->oid_type);
+
+ if (!pi->id_strlen) {
+ if (!repo)
+ pi->id_strlen = GIT_ABBREV_DEFAULT;
+ else if (git_repository__configmap_lookup(&pi->id_strlen, repo, GIT_CONFIGMAP_ABBREV) < 0)
+ return -1;
+ }
+
+ if (pi->id_strlen > 0 &&
+ (size_t)pi->id_strlen > git_oid_hexsize(pi->oid_type))
+ pi->id_strlen = (int)git_oid_hexsize(pi->oid_type);
+
+ memset(&pi->line, 0, sizeof(pi->line));
+ pi->line.old_lineno = -1;
+ pi->line.new_lineno = -1;
+ pi->line.num_lines = 1;
+
+ return 0;
+}
+
+static int diff_print_info_init_fromdiff(
+ diff_print_info *pi,
+ git_str *out,
+ git_diff *diff,
+ git_diff_format_t format,
+ git_diff_line_cb cb,
+ void *payload)
+{
+ git_repository *repo = diff ? diff->repo : NULL;
+
+ memset(pi, 0, sizeof(diff_print_info));
+
+ if (diff) {
+ pi->flags = diff->opts.flags;
+ pi->oid_type = diff->opts.oid_type;
+ pi->id_strlen = diff->opts.id_abbrev;
+ pi->old_prefix = diff->opts.old_prefix;
+ pi->new_prefix = diff->opts.new_prefix;
+
+ pi->strcomp = diff->strcomp;
+ }
+
+ return diff_print_info_init__common(pi, out, repo, format, cb, payload);
+}
+
+static int diff_print_info_init_frompatch(
+ diff_print_info *pi,
+ git_str *out,
+ git_patch *patch,
+ git_diff_format_t format,
+ git_diff_line_cb cb,
+ void *payload)
+{
+ GIT_ASSERT_ARG(patch);
+
+ memset(pi, 0, sizeof(diff_print_info));
+
+ pi->flags = patch->diff_opts.flags;
+ pi->oid_type = patch->diff_opts.oid_type;
+ pi->id_strlen = patch->diff_opts.id_abbrev;
+ pi->old_prefix = patch->diff_opts.old_prefix;
+ pi->new_prefix = patch->diff_opts.new_prefix;
+
+ return diff_print_info_init__common(pi, out, patch->repo, format, cb, payload);
+}
+
+static char diff_pick_suffix(int mode)
+{
+ if (S_ISDIR(mode))
+ return '/';
+ else if (GIT_PERMS_IS_EXEC(mode)) /* -V536 */
+ /* in git, modes are very regular, so we must have 0100755 mode */
+ return '*';
+ else
+ return ' ';
+}
+
+char git_diff_status_char(git_delta_t status)
+{
+ char code;
+
+ switch (status) {
+ case GIT_DELTA_ADDED: code = 'A'; break;
+ case GIT_DELTA_DELETED: code = 'D'; break;
+ case GIT_DELTA_MODIFIED: code = 'M'; break;
+ case GIT_DELTA_RENAMED: code = 'R'; break;
+ case GIT_DELTA_COPIED: code = 'C'; break;
+ case GIT_DELTA_IGNORED: code = 'I'; break;
+ case GIT_DELTA_UNTRACKED: code = '?'; break;
+ case GIT_DELTA_TYPECHANGE: code = 'T'; break;
+ case GIT_DELTA_UNREADABLE: code = 'X'; break;
+ default: code = ' '; break;
+ }
+
+ return code;
+}
+
+static int diff_print_one_name_only(
+ const git_diff_delta *delta, float progress, void *data)
+{
+ diff_print_info *pi = data;
+ git_str *out = pi->buf;
+
+ GIT_UNUSED(progress);
+
+ if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 &&
+ delta->status == GIT_DELTA_UNMODIFIED)
+ return 0;
+
+ git_str_clear(out);
+ git_str_puts(out, delta->new_file.path);
+ git_str_putc(out, '\n');
+ if (git_str_oom(out))
+ return -1;
+
+ pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
+ pi->line.content = git_str_cstr(out);
+ pi->line.content_len = git_str_len(out);
+
+ return pi->print_cb(delta, NULL, &pi->line, pi->payload);
+}
+
+static int diff_print_one_name_status(
+ const git_diff_delta *delta, float progress, void *data)
+{
+ diff_print_info *pi = data;
+ git_str *out = pi->buf;
+ char old_suffix, new_suffix, code = git_diff_status_char(delta->status);
+ int(*strcomp)(const char *, const char *) = pi->strcomp ?
+ pi->strcomp : git__strcmp;
+
+ GIT_UNUSED(progress);
+
+ if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 && code == ' ')
+ return 0;
+
+ old_suffix = diff_pick_suffix(delta->old_file.mode);
+ new_suffix = diff_pick_suffix(delta->new_file.mode);
+
+ git_str_clear(out);
+
+ if (delta->old_file.path != delta->new_file.path &&
+ strcomp(delta->old_file.path,delta->new_file.path) != 0)
+ git_str_printf(out, "%c\t%s%c %s%c\n", code,
+ delta->old_file.path, old_suffix, delta->new_file.path, new_suffix);
+ else if (delta->old_file.mode != delta->new_file.mode &&
+ delta->old_file.mode != 0 && delta->new_file.mode != 0)
+ git_str_printf(out, "%c\t%s%c %s%c\n", code,
+ delta->old_file.path, old_suffix, delta->new_file.path, new_suffix);
+ else if (old_suffix != ' ')
+ git_str_printf(out, "%c\t%s%c\n", code, delta->old_file.path, old_suffix);
+ else
+ git_str_printf(out, "%c\t%s\n", code, delta->old_file.path);
+ if (git_str_oom(out))
+ return -1;
+
+ pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
+ pi->line.content = git_str_cstr(out);
+ pi->line.content_len = git_str_len(out);
+
+ return pi->print_cb(delta, NULL, &pi->line, pi->payload);
+}
+
+static int diff_print_one_raw(
+ const git_diff_delta *delta, float progress, void *data)
+{
+ diff_print_info *pi = data;
+ git_str *out = pi->buf;
+ int id_abbrev;
+ char code = git_diff_status_char(delta->status);
+ char start_oid[GIT_OID_MAX_HEXSIZE + 1],
+ end_oid[GIT_OID_MAX_HEXSIZE + 1];
+ size_t oid_hexsize;
+ bool id_is_abbrev;
+
+ GIT_UNUSED(progress);
+
+ if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 && code == ' ')
+ return 0;
+
+ git_str_clear(out);
+
+ id_abbrev = delta->old_file.mode ? delta->old_file.id_abbrev :
+ delta->new_file.id_abbrev;
+
+ if (pi->id_strlen > id_abbrev) {
+ git_error_set(GIT_ERROR_PATCH,
+ "the patch input contains %d id characters (cannot print %d)",
+ id_abbrev, pi->id_strlen);
+ return -1;
+ }
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ GIT_ASSERT(delta->old_file.id.type == delta->new_file.id.type);
+ oid_hexsize = git_oid_hexsize(delta->old_file.id.type);
+#else
+ oid_hexsize = GIT_OID_SHA1_HEXSIZE;
+#endif
+
+ id_is_abbrev = (pi->id_strlen > 0 &&
+ (size_t)pi->id_strlen <= oid_hexsize);
+
+ git_oid_tostr(start_oid, pi->id_strlen + 1, &delta->old_file.id);
+ git_oid_tostr(end_oid, pi->id_strlen + 1, &delta->new_file.id);
+
+ git_str_printf(out,
+ id_is_abbrev ? ":%06o %06o %s... %s... %c" : ":%06o %06o %s %s %c",
+ delta->old_file.mode, delta->new_file.mode, start_oid, end_oid, code);
+
+ if (delta->similarity > 0)
+ git_str_printf(out, "%03u", delta->similarity);
+
+ if (delta->old_file.path != delta->new_file.path)
+ git_str_printf(
+ out, "\t%s %s\n", delta->old_file.path, delta->new_file.path);
+ else
+ git_str_printf(
+ out, "\t%s\n", delta->old_file.path ?
+ delta->old_file.path : delta->new_file.path);
+
+ if (git_str_oom(out))
+ return -1;
+
+ pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
+ pi->line.content = git_str_cstr(out);
+ pi->line.content_len = git_str_len(out);
+
+ return pi->print_cb(delta, NULL, &pi->line, pi->payload);
+}
+
+static int diff_print_modes(
+ git_str *out, const git_diff_delta *delta)
+{
+ git_str_printf(out, "old mode %o\n", delta->old_file.mode);
+ git_str_printf(out, "new mode %o\n", delta->new_file.mode);
+
+ return git_str_oom(out) ? -1 : 0;
+}
+
+static int diff_print_oid_range(
+ git_str *out, const git_diff_delta *delta, int id_strlen,
+ bool print_index)
+{
+ char start_oid[GIT_OID_MAX_HEXSIZE + 1],
+ end_oid[GIT_OID_MAX_HEXSIZE + 1];
+
+ if (delta->old_file.mode &&
+ id_strlen > delta->old_file.id_abbrev) {
+ git_error_set(GIT_ERROR_PATCH,
+ "the patch input contains %d id characters (cannot print %d)",
+ delta->old_file.id_abbrev, id_strlen);
+ return -1;
+ }
+
+ if ((delta->new_file.mode &&
+ id_strlen > delta->new_file.id_abbrev)) {
+ git_error_set(GIT_ERROR_PATCH,
+ "the patch input contains %d id characters (cannot print %d)",
+ delta->new_file.id_abbrev, id_strlen);
+ return -1;
+ }
+
+ git_oid_tostr(start_oid, id_strlen + 1, &delta->old_file.id);
+ git_oid_tostr(end_oid, id_strlen + 1, &delta->new_file.id);
+
+ if (delta->old_file.mode == delta->new_file.mode) {
+ if (print_index)
+ git_str_printf(out, "index %s..%s %o\n",
+ start_oid, end_oid, delta->old_file.mode);
+ } else {
+ if (delta->old_file.mode == 0)
+ git_str_printf(out, "new file mode %o\n", delta->new_file.mode);
+ else if (delta->new_file.mode == 0)
+ git_str_printf(out, "deleted file mode %o\n", delta->old_file.mode);
+ else
+ diff_print_modes(out, delta);
+
+ if (print_index)
+ git_str_printf(out, "index %s..%s\n", start_oid, end_oid);
+ }
+
+ return git_str_oom(out) ? -1 : 0;
+}
+
+static int diff_delta_format_path(
+ git_str *out, const char *prefix, const char *filename)
+{
+ if (!filename) {
+ /* don't prefix "/dev/null" */
+ return git_str_puts(out, "/dev/null");
+ }
+
+ if (git_str_joinpath(out, prefix, filename) < 0)
+ return -1;
+
+ return git_str_quote(out);
+}
+
+static int diff_delta_format_with_paths(
+ git_str *out,
+ const git_diff_delta *delta,
+ const char *template,
+ const char *oldpath,
+ const char *newpath)
+{
+ if (git_oid_is_zero(&delta->old_file.id))
+ oldpath = "/dev/null";
+
+ if (git_oid_is_zero(&delta->new_file.id))
+ newpath = "/dev/null";
+
+ return git_str_printf(out, template, oldpath, newpath);
+}
+
+static int diff_delta_format_similarity_header(
+ git_str *out,
+ const git_diff_delta *delta)
+{
+ git_str old_path = GIT_STR_INIT, new_path = GIT_STR_INIT;
+ const char *type;
+ int error = 0;
+
+ if (delta->similarity > 100) {
+ git_error_set(GIT_ERROR_PATCH, "invalid similarity %d", delta->similarity);
+ error = -1;
+ goto done;
+ }
+
+ GIT_ASSERT(delta->status == GIT_DELTA_RENAMED || delta->status == GIT_DELTA_COPIED);
+ if (delta->status == GIT_DELTA_RENAMED)
+ type = "rename";
+ else
+ type = "copy";
+
+ if ((error = git_str_puts(&old_path, delta->old_file.path)) < 0 ||
+ (error = git_str_puts(&new_path, delta->new_file.path)) < 0 ||
+ (error = git_str_quote(&old_path)) < 0 ||
+ (error = git_str_quote(&new_path)) < 0)
+ goto done;
+
+ git_str_printf(out,
+ "similarity index %d%%\n"
+ "%s from %s\n"
+ "%s to %s\n",
+ delta->similarity,
+ type, old_path.ptr,
+ type, new_path.ptr);
+
+ if (git_str_oom(out))
+ error = -1;
+
+done:
+ git_str_dispose(&old_path);
+ git_str_dispose(&new_path);
+
+ return error;
+}
+
+static bool delta_is_unchanged(const git_diff_delta *delta)
+{
+ if (git_oid_is_zero(&delta->old_file.id) &&
+ git_oid_is_zero(&delta->new_file.id))
+ return true;
+
+ if (delta->old_file.mode == GIT_FILEMODE_COMMIT ||
+ delta->new_file.mode == GIT_FILEMODE_COMMIT)
+ return false;
+
+ if (git_oid_equal(&delta->old_file.id, &delta->new_file.id))
+ return true;
+
+ return false;
+}
+
+int git_diff_delta__format_file_header(
+ git_str *out,
+ const git_diff_delta *delta,
+ const char *oldpfx,
+ const char *newpfx,
+ int id_strlen,
+ bool print_index)
+{
+ git_str old_path = GIT_STR_INIT, new_path = GIT_STR_INIT;
+ bool unchanged = delta_is_unchanged(delta);
+ int error = 0;
+
+ if (!oldpfx)
+ oldpfx = DIFF_OLD_PREFIX_DEFAULT;
+ if (!newpfx)
+ newpfx = DIFF_NEW_PREFIX_DEFAULT;
+ if (!id_strlen)
+ id_strlen = GIT_ABBREV_DEFAULT;
+
+ if ((error = diff_delta_format_path(
+ &old_path, oldpfx, delta->old_file.path)) < 0 ||
+ (error = diff_delta_format_path(
+ &new_path, newpfx, delta->new_file.path)) < 0)
+ goto done;
+
+ git_str_clear(out);
+
+ git_str_printf(out, "diff --git %s %s\n",
+ old_path.ptr, new_path.ptr);
+
+ if (unchanged && delta->old_file.mode != delta->new_file.mode)
+ diff_print_modes(out, delta);
+
+ if (delta->status == GIT_DELTA_RENAMED ||
+ (delta->status == GIT_DELTA_COPIED && unchanged)) {
+ if ((error = diff_delta_format_similarity_header(out, delta)) < 0)
+ goto done;
+ }
+
+ if (!unchanged) {
+ if ((error = diff_print_oid_range(out, delta,
+ id_strlen, print_index)) < 0)
+ goto done;
+
+ if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0)
+ diff_delta_format_with_paths(out, delta,
+ "--- %s\n+++ %s\n", old_path.ptr, new_path.ptr);
+ }
+
+ if (git_str_oom(out))
+ error = -1;
+
+done:
+ git_str_dispose(&old_path);
+ git_str_dispose(&new_path);
+
+ return error;
+}
+
+static int format_binary(
+ diff_print_info *pi,
+ git_diff_binary_t type,
+ const char *data,
+ size_t datalen,
+ size_t inflatedlen)
+{
+ const char *typename = type == GIT_DIFF_BINARY_DELTA ?
+ "delta" : "literal";
+ const char *scan, *end;
+
+ git_str_printf(pi->buf, "%s %" PRIuZ "\n", typename, inflatedlen);
+ pi->line.num_lines++;
+
+ for (scan = data, end = data + datalen; scan < end; ) {
+ size_t chunk_len = end - scan;
+ if (chunk_len > 52)
+ chunk_len = 52;
+
+ if (chunk_len <= 26)
+ git_str_putc(pi->buf, (char)chunk_len + 'A' - 1);
+ else
+ git_str_putc(pi->buf, (char)chunk_len - 26 + 'a' - 1);
+
+ git_str_encode_base85(pi->buf, scan, chunk_len);
+ git_str_putc(pi->buf, '\n');
+
+ if (git_str_oom(pi->buf))
+ return -1;
+
+ scan += chunk_len;
+ pi->line.num_lines++;
+ }
+ git_str_putc(pi->buf, '\n');
+
+ if (git_str_oom(pi->buf))
+ return -1;
+
+ return 0;
+}
+
+static int diff_print_patch_file_binary_noshow(
+ diff_print_info *pi, git_diff_delta *delta,
+ const char *old_pfx, const char *new_pfx)
+{
+ git_str old_path = GIT_STR_INIT, new_path = GIT_STR_INIT;
+ int error;
+
+ if ((error = diff_delta_format_path(&old_path, old_pfx, delta->old_file.path)) < 0 ||
+ (error = diff_delta_format_path(&new_path, new_pfx, delta->new_file.path)) < 0 ||
+ (error = diff_delta_format_with_paths(pi->buf, delta, "Binary files %s and %s differ\n",
+ old_path.ptr, new_path.ptr)) < 0)
+ goto done;
+
+ pi->line.num_lines = 1;
+
+done:
+ git_str_dispose(&old_path);
+ git_str_dispose(&new_path);
+ return error;
+}
+
+static int diff_print_patch_file_binary(
+ diff_print_info *pi, git_diff_delta *delta,
+ const char *old_pfx, const char *new_pfx,
+ const git_diff_binary *binary)
+{
+ size_t pre_binary_size;
+ int error;
+
+ if (delta->status == GIT_DELTA_UNMODIFIED)
+ return 0;
+
+ if ((pi->flags & GIT_DIFF_SHOW_BINARY) == 0 || !binary->contains_data)
+ return diff_print_patch_file_binary_noshow(
+ pi, delta, old_pfx, new_pfx);
+
+ pre_binary_size = pi->buf->size;
+ git_str_printf(pi->buf, "GIT binary patch\n");
+ pi->line.num_lines++;
+
+ if ((error = format_binary(pi, binary->new_file.type, binary->new_file.data,
+ binary->new_file.datalen, binary->new_file.inflatedlen)) < 0 ||
+ (error = format_binary(pi, binary->old_file.type, binary->old_file.data,
+ binary->old_file.datalen, binary->old_file.inflatedlen)) < 0) {
+ if (error == GIT_EBUFS) {
+ git_error_clear();
+ git_str_truncate(pi->buf, pre_binary_size);
+
+ return diff_print_patch_file_binary_noshow(
+ pi, delta, old_pfx, new_pfx);
+ }
+ }
+
+ pi->line.num_lines++;
+ return error;
+}
+
+static int diff_print_patch_file(
+ const git_diff_delta *delta, float progress, void *data)
+{
+ int error;
+ diff_print_info *pi = data;
+ const char *oldpfx =
+ pi->old_prefix ? pi->old_prefix : DIFF_OLD_PREFIX_DEFAULT;
+ const char *newpfx =
+ pi->new_prefix ? pi->new_prefix : DIFF_NEW_PREFIX_DEFAULT;
+
+ bool binary = (delta->flags & GIT_DIFF_FLAG_BINARY) ||
+ (pi->flags & GIT_DIFF_FORCE_BINARY);
+ bool show_binary = !!(pi->flags & GIT_DIFF_SHOW_BINARY);
+ int id_strlen = pi->id_strlen;
+ bool print_index = (pi->format != GIT_DIFF_FORMAT_PATCH_ID);
+
+ if (binary && show_binary)
+ id_strlen = delta->old_file.id_abbrev ? delta->old_file.id_abbrev :
+ delta->new_file.id_abbrev;
+
+ GIT_UNUSED(progress);
+
+ if (S_ISDIR(delta->new_file.mode) ||
+ delta->status == GIT_DELTA_UNMODIFIED ||
+ delta->status == GIT_DELTA_IGNORED ||
+ delta->status == GIT_DELTA_UNREADABLE ||
+ (delta->status == GIT_DELTA_UNTRACKED &&
+ (pi->flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) == 0))
+ return 0;
+
+ if ((error = git_diff_delta__format_file_header(pi->buf, delta, oldpfx, newpfx,
+ id_strlen, print_index)) < 0)
+ return error;
+
+ pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
+ pi->line.content = git_str_cstr(pi->buf);
+ pi->line.content_len = git_str_len(pi->buf);
+
+ return pi->print_cb(delta, NULL, &pi->line, pi->payload);
+}
+
+static int diff_print_patch_binary(
+ const git_diff_delta *delta,
+ const git_diff_binary *binary,
+ void *data)
+{
+ diff_print_info *pi = data;
+ const char *old_pfx =
+ pi->old_prefix ? pi->old_prefix : DIFF_OLD_PREFIX_DEFAULT;
+ const char *new_pfx =
+ pi->new_prefix ? pi->new_prefix : DIFF_NEW_PREFIX_DEFAULT;
+ int error;
+
+ git_str_clear(pi->buf);
+
+ if ((error = diff_print_patch_file_binary(
+ pi, (git_diff_delta *)delta, old_pfx, new_pfx, binary)) < 0)
+ return error;
+
+ pi->line.origin = GIT_DIFF_LINE_BINARY;
+ pi->line.content = git_str_cstr(pi->buf);
+ pi->line.content_len = git_str_len(pi->buf);
+
+ return pi->print_cb(delta, NULL, &pi->line, pi->payload);
+}
+
+static int diff_print_patch_hunk(
+ const git_diff_delta *d,
+ const git_diff_hunk *h,
+ void *data)
+{
+ diff_print_info *pi = data;
+
+ if (S_ISDIR(d->new_file.mode))
+ return 0;
+
+ pi->line.origin = GIT_DIFF_LINE_HUNK_HDR;
+ pi->line.content = h->header;
+ pi->line.content_len = h->header_len;
+
+ return pi->print_cb(d, h, &pi->line, pi->payload);
+}
+
+static int diff_print_patch_line(
+ const git_diff_delta *delta,
+ const git_diff_hunk *hunk,
+ const git_diff_line *line,
+ void *data)
+{
+ diff_print_info *pi = data;
+
+ if (S_ISDIR(delta->new_file.mode))
+ return 0;
+
+ return pi->print_cb(delta, hunk, line, pi->payload);
+}
+
+/* print a git_diff to an output callback */
+int git_diff_print(
+ git_diff *diff,
+ git_diff_format_t format,
+ git_diff_line_cb print_cb,
+ void *payload)
+{
+ int error;
+ git_str buf = GIT_STR_INIT;
+ diff_print_info pi;
+ git_diff_file_cb print_file = NULL;
+ git_diff_binary_cb print_binary = NULL;
+ git_diff_hunk_cb print_hunk = NULL;
+ git_diff_line_cb print_line = NULL;
+
+ switch (format) {
+ case GIT_DIFF_FORMAT_PATCH:
+ print_file = diff_print_patch_file;
+ print_binary = diff_print_patch_binary;
+ print_hunk = diff_print_patch_hunk;
+ print_line = diff_print_patch_line;
+ break;
+ case GIT_DIFF_FORMAT_PATCH_ID:
+ print_file = diff_print_patch_file;
+ print_binary = diff_print_patch_binary;
+ print_line = diff_print_patch_line;
+ break;
+ case GIT_DIFF_FORMAT_PATCH_HEADER:
+ print_file = diff_print_patch_file;
+ break;
+ case GIT_DIFF_FORMAT_RAW:
+ print_file = diff_print_one_raw;
+ break;
+ case GIT_DIFF_FORMAT_NAME_ONLY:
+ print_file = diff_print_one_name_only;
+ break;
+ case GIT_DIFF_FORMAT_NAME_STATUS:
+ print_file = diff_print_one_name_status;
+ break;
+ default:
+ git_error_set(GIT_ERROR_INVALID, "unknown diff output format (%d)", format);
+ return -1;
+ }
+
+ if ((error = diff_print_info_init_fromdiff(&pi, &buf, diff, format, print_cb, payload)) < 0)
+ goto out;
+
+ if ((error = git_diff_foreach(diff, print_file, print_binary, print_hunk, print_line, &pi)) != 0) {
+ git_error_set_after_callback_function(error, "git_diff_print");
+ goto out;
+ }
+
+out:
+ git_str_dispose(&buf);
+ return error;
+}
+
+int git_diff_print_callback__to_buf(
+ const git_diff_delta *delta,
+ const git_diff_hunk *hunk,
+ const git_diff_line *line,
+ void *payload)
+{
+ git_str *output = payload;
+ GIT_UNUSED(delta); GIT_UNUSED(hunk);
+
+ if (!output) {
+ git_error_set(GIT_ERROR_INVALID, "buffer pointer must be provided");
+ return -1;
+ }
+
+ if (line->origin == GIT_DIFF_LINE_ADDITION ||
+ line->origin == GIT_DIFF_LINE_DELETION ||
+ line->origin == GIT_DIFF_LINE_CONTEXT)
+ git_str_putc(output, line->origin);
+
+ return git_str_put(output, line->content, line->content_len);
+}
+
+int git_diff_print_callback__to_file_handle(
+ const git_diff_delta *delta,
+ const git_diff_hunk *hunk,
+ const git_diff_line *line,
+ void *payload)
+{
+ FILE *fp = payload ? payload : stdout;
+ int error;
+
+ GIT_UNUSED(delta);
+ GIT_UNUSED(hunk);
+
+ if (line->origin == GIT_DIFF_LINE_CONTEXT ||
+ line->origin == GIT_DIFF_LINE_ADDITION ||
+ line->origin == GIT_DIFF_LINE_DELETION) {
+ while ((error = fputc(line->origin, fp)) == EINTR)
+ continue;
+ if (error) {
+ git_error_set(GIT_ERROR_OS, "could not write status");
+ return -1;
+ }
+ }
+
+ if (fwrite(line->content, line->content_len, 1, fp) != 1) {
+ git_error_set(GIT_ERROR_OS, "could not write line");
+ return -1;
+ }
+
+ return 0;
+}
+
+/* print a git_diff to a git_str */
+int git_diff_to_buf(git_buf *out, git_diff *diff, git_diff_format_t format)
+{
+ git_str str = GIT_STR_INIT;
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(diff);
+
+ if ((error = git_buf_tostr(&str, out)) < 0 ||
+ (error = git_diff_print(diff, format, git_diff_print_callback__to_buf, &str)) < 0)
+ goto done;
+
+ error = git_buf_fromstr(out, &str);
+
+done:
+ git_str_dispose(&str);
+ return error;
+}
+
+/* print a git_patch to an output callback */
+int git_patch_print(
+ git_patch *patch,
+ git_diff_line_cb print_cb,
+ void *payload)
+{
+ git_str temp = GIT_STR_INIT;
+ diff_print_info pi;
+ int error;
+
+ GIT_ASSERT_ARG(patch);
+ GIT_ASSERT_ARG(print_cb);
+
+ if ((error = diff_print_info_init_frompatch(&pi, &temp, patch,
+ GIT_DIFF_FORMAT_PATCH, print_cb, payload)) < 0)
+ goto out;
+
+ if ((error = git_patch__invoke_callbacks(patch, diff_print_patch_file, diff_print_patch_binary,
+ diff_print_patch_hunk, diff_print_patch_line, &pi)) < 0) {
+ git_error_set_after_callback_function(error, "git_patch_print");
+ goto out;
+ }
+
+out:
+ git_str_dispose(&temp);
+ return error;
+}
+
+/* print a git_patch to a git_str */
+int git_patch_to_buf(git_buf *out, git_patch *patch)
+{
+ GIT_BUF_WRAP_PRIVATE(out, git_patch__to_buf, patch);
+}
+
+int git_patch__to_buf(git_str *out, git_patch *patch)
+{
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(patch);
+
+ return git_patch_print(patch, git_diff_print_callback__to_buf, out);
+}
diff --git a/src/libgit2/diff_stats.c b/src/libgit2/diff_stats.c
new file mode 100644
index 0000000..2599398
--- /dev/null
+++ b/src/libgit2/diff_stats.c
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "diff_stats.h"
+
+#include "buf.h"
+#include "common.h"
+#include "vector.h"
+#include "diff.h"
+#include "patch_generate.h"
+
+#define DIFF_RENAME_FILE_SEPARATOR " => "
+#define STATS_FULL_MIN_SCALE 7
+
+typedef struct {
+ size_t insertions;
+ size_t deletions;
+} diff_file_stats;
+
+struct git_diff_stats {
+ git_diff *diff;
+ diff_file_stats *filestats;
+
+ size_t files_changed;
+ size_t insertions;
+ size_t deletions;
+ size_t renames;
+
+ size_t max_name;
+ size_t max_filestat;
+ int max_digits;
+};
+
+static int digits_for_value(size_t val)
+{
+ int count = 1;
+ size_t placevalue = 10;
+
+ while (val >= placevalue) {
+ ++count;
+ placevalue *= 10;
+ }
+
+ return count;
+}
+
+static int diff_file_stats_full_to_buf(
+ git_str *out,
+ const git_diff_delta *delta,
+ const diff_file_stats *filestat,
+ const git_diff_stats *stats,
+ size_t width)
+{
+ const char *old_path = NULL, *new_path = NULL, *adddel_path = NULL;
+ size_t padding;
+ git_object_size_t old_size, new_size;
+
+ old_path = delta->old_file.path;
+ new_path = delta->new_file.path;
+ old_size = delta->old_file.size;
+ new_size = delta->new_file.size;
+
+ if (old_path && new_path && strcmp(old_path, new_path) != 0) {
+ size_t common_dirlen;
+ int error;
+
+ padding = stats->max_name - strlen(old_path) - strlen(new_path);
+
+ if ((common_dirlen = git_fs_path_common_dirlen(old_path, new_path)) &&
+ common_dirlen <= INT_MAX) {
+ error = git_str_printf(out, " %.*s{%s"DIFF_RENAME_FILE_SEPARATOR"%s}",
+ (int) common_dirlen, old_path,
+ old_path + common_dirlen,
+ new_path + common_dirlen);
+ } else {
+ error = git_str_printf(out, " %s" DIFF_RENAME_FILE_SEPARATOR "%s",
+ old_path, new_path);
+ }
+
+ if (error < 0)
+ goto on_error;
+ } else {
+ adddel_path = new_path ? new_path : old_path;
+ if (git_str_printf(out, " %s", adddel_path) < 0)
+ goto on_error;
+
+ padding = stats->max_name - strlen(adddel_path);
+
+ if (stats->renames > 0)
+ padding += strlen(DIFF_RENAME_FILE_SEPARATOR);
+ }
+
+ if (git_str_putcn(out, ' ', padding) < 0 ||
+ git_str_puts(out, " | ") < 0)
+ goto on_error;
+
+ if (delta->flags & GIT_DIFF_FLAG_BINARY) {
+ if (git_str_printf(out,
+ "Bin %" PRId64 " -> %" PRId64 " bytes", old_size, new_size) < 0)
+ goto on_error;
+ }
+ else {
+ if (git_str_printf(out,
+ "%*" PRIuZ, stats->max_digits,
+ filestat->insertions + filestat->deletions) < 0)
+ goto on_error;
+
+ if (filestat->insertions || filestat->deletions) {
+ if (git_str_putc(out, ' ') < 0)
+ goto on_error;
+
+ if (!width) {
+ if (git_str_putcn(out, '+', filestat->insertions) < 0 ||
+ git_str_putcn(out, '-', filestat->deletions) < 0)
+ goto on_error;
+ } else {
+ size_t total = filestat->insertions + filestat->deletions;
+ size_t full = (total * width + stats->max_filestat / 2) /
+ stats->max_filestat;
+ size_t plus = full * filestat->insertions / total;
+ size_t minus = full - plus;
+
+ if (git_str_putcn(out, '+', max(plus, 1)) < 0 ||
+ git_str_putcn(out, '-', max(minus, 1)) < 0)
+ goto on_error;
+ }
+ }
+ }
+
+ git_str_putc(out, '\n');
+
+on_error:
+ return (git_str_oom(out) ? -1 : 0);
+}
+
+static int diff_file_stats_number_to_buf(
+ git_str *out,
+ const git_diff_delta *delta,
+ const diff_file_stats *filestats)
+{
+ int error;
+ const char *path = delta->new_file.path;
+
+ if (delta->flags & GIT_DIFF_FLAG_BINARY)
+ error = git_str_printf(out, "%-8c" "%-8c" "%s\n", '-', '-', path);
+ else
+ error = git_str_printf(out, "%-8" PRIuZ "%-8" PRIuZ "%s\n",
+ filestats->insertions, filestats->deletions, path);
+
+ return error;
+}
+
+static int diff_file_stats_summary_to_buf(
+ git_str *out,
+ const git_diff_delta *delta)
+{
+ if (delta->old_file.mode != delta->new_file.mode) {
+ if (delta->old_file.mode == 0) {
+ git_str_printf(out, " create mode %06o %s\n",
+ delta->new_file.mode, delta->new_file.path);
+ }
+ else if (delta->new_file.mode == 0) {
+ git_str_printf(out, " delete mode %06o %s\n",
+ delta->old_file.mode, delta->old_file.path);
+ }
+ else {
+ git_str_printf(out, " mode change %06o => %06o %s\n",
+ delta->old_file.mode, delta->new_file.mode, delta->new_file.path);
+ }
+ }
+
+ return 0;
+}
+
+int git_diff_get_stats(
+ git_diff_stats **out,
+ git_diff *diff)
+{
+ size_t i, deltas;
+ size_t total_insertions = 0, total_deletions = 0;
+ git_diff_stats *stats = NULL;
+ int error = 0;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(diff);
+
+ stats = git__calloc(1, sizeof(git_diff_stats));
+ GIT_ERROR_CHECK_ALLOC(stats);
+
+ deltas = git_diff_num_deltas(diff);
+
+ stats->filestats = git__calloc(deltas, sizeof(diff_file_stats));
+ if (!stats->filestats) {
+ git__free(stats);
+ return -1;
+ }
+
+ stats->diff = diff;
+ GIT_REFCOUNT_INC(diff);
+
+ for (i = 0; i < deltas && !error; ++i) {
+ git_patch *patch = NULL;
+ size_t add = 0, remove = 0, namelen;
+ const git_diff_delta *delta;
+
+ if ((error = git_patch_from_diff(&patch, diff, i)) < 0)
+ break;
+
+ /* keep a count of renames because it will affect formatting */
+ delta = patch->delta;
+
+ /* TODO ugh */
+ namelen = strlen(delta->new_file.path);
+ if (delta->old_file.path && strcmp(delta->old_file.path, delta->new_file.path) != 0) {
+ namelen += strlen(delta->old_file.path);
+ stats->renames++;
+ }
+
+ /* and, of course, count the line stats */
+ error = git_patch_line_stats(NULL, &add, &remove, patch);
+
+ git_patch_free(patch);
+
+ stats->filestats[i].insertions = add;
+ stats->filestats[i].deletions = remove;
+
+ total_insertions += add;
+ total_deletions += remove;
+
+ if (stats->max_name < namelen)
+ stats->max_name = namelen;
+ if (stats->max_filestat < add + remove)
+ stats->max_filestat = add + remove;
+ }
+
+ stats->files_changed = deltas;
+ stats->insertions = total_insertions;
+ stats->deletions = total_deletions;
+ stats->max_digits = digits_for_value(stats->max_filestat + 1);
+
+ if (error < 0) {
+ git_diff_stats_free(stats);
+ stats = NULL;
+ }
+
+ *out = stats;
+ return error;
+}
+
+size_t git_diff_stats_files_changed(
+ const git_diff_stats *stats)
+{
+ GIT_ASSERT_ARG(stats);
+
+ return stats->files_changed;
+}
+
+size_t git_diff_stats_insertions(
+ const git_diff_stats *stats)
+{
+ GIT_ASSERT_ARG(stats);
+
+ return stats->insertions;
+}
+
+size_t git_diff_stats_deletions(
+ const git_diff_stats *stats)
+{
+ GIT_ASSERT_ARG(stats);
+
+ return stats->deletions;
+}
+
+int git_diff_stats_to_buf(
+ git_buf *out,
+ const git_diff_stats *stats,
+ git_diff_stats_format_t format,
+ size_t width)
+{
+ GIT_BUF_WRAP_PRIVATE(out, git_diff__stats_to_buf, stats, format, width);
+}
+
+int git_diff__stats_to_buf(
+ git_str *out,
+ const git_diff_stats *stats,
+ git_diff_stats_format_t format,
+ size_t width)
+{
+ int error = 0;
+ size_t i;
+ const git_diff_delta *delta;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(stats);
+
+ if (format & GIT_DIFF_STATS_NUMBER) {
+ for (i = 0; i < stats->files_changed; ++i) {
+ if ((delta = git_diff_get_delta(stats->diff, i)) == NULL)
+ continue;
+
+ error = diff_file_stats_number_to_buf(
+ out, delta, &stats->filestats[i]);
+ if (error < 0)
+ return error;
+ }
+ }
+
+ if (format & GIT_DIFF_STATS_FULL) {
+ if (width > 0) {
+ if (width > stats->max_name + stats->max_digits + 5)
+ width -= (stats->max_name + stats->max_digits + 5);
+ if (width < STATS_FULL_MIN_SCALE)
+ width = STATS_FULL_MIN_SCALE;
+ }
+ if (width > stats->max_filestat)
+ width = 0;
+
+ for (i = 0; i < stats->files_changed; ++i) {
+ if ((delta = git_diff_get_delta(stats->diff, i)) == NULL)
+ continue;
+
+ error = diff_file_stats_full_to_buf(
+ out, delta, &stats->filestats[i], stats, width);
+ if (error < 0)
+ return error;
+ }
+ }
+
+ if (format & GIT_DIFF_STATS_FULL || format & GIT_DIFF_STATS_SHORT) {
+ git_str_printf(
+ out, " %" PRIuZ " file%s changed",
+ stats->files_changed, stats->files_changed != 1 ? "s" : "");
+
+ if (stats->insertions || stats->deletions == 0)
+ git_str_printf(
+ out, ", %" PRIuZ " insertion%s(+)",
+ stats->insertions, stats->insertions != 1 ? "s" : "");
+
+ if (stats->deletions || stats->insertions == 0)
+ git_str_printf(
+ out, ", %" PRIuZ " deletion%s(-)",
+ stats->deletions, stats->deletions != 1 ? "s" : "");
+
+ git_str_putc(out, '\n');
+
+ if (git_str_oom(out))
+ return -1;
+ }
+
+ if (format & GIT_DIFF_STATS_INCLUDE_SUMMARY) {
+ for (i = 0; i < stats->files_changed; ++i) {
+ if ((delta = git_diff_get_delta(stats->diff, i)) == NULL)
+ continue;
+
+ error = diff_file_stats_summary_to_buf(out, delta);
+ if (error < 0)
+ return error;
+ }
+ }
+
+ return error;
+}
+
+void git_diff_stats_free(git_diff_stats *stats)
+{
+ if (stats == NULL)
+ return;
+
+ git_diff_free(stats->diff); /* bumped refcount in constructor */
+ git__free(stats->filestats);
+ git__free(stats);
+}
diff --git a/src/libgit2/diff_stats.h b/src/libgit2/diff_stats.h
new file mode 100644
index 0000000..c71862b
--- /dev/null
+++ b/src/libgit2/diff_stats.h
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_diff_stats_h__
+#define INCLUDE_diff_stats_h__
+
+#include "common.h"
+
+int git_diff__stats_to_buf(
+ git_str *out,
+ const git_diff_stats *stats,
+ git_diff_stats_format_t format,
+ size_t width);
+
+#endif
diff --git a/src/libgit2/diff_tform.c b/src/libgit2/diff_tform.c
new file mode 100644
index 0000000..4a156c7
--- /dev/null
+++ b/src/libgit2/diff_tform.c
@@ -0,0 +1,1125 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "diff_tform.h"
+
+#include "git2/config.h"
+#include "git2/blob.h"
+#include "git2/sys/hashsig.h"
+
+#include "diff.h"
+#include "diff_generate.h"
+#include "fs_path.h"
+#include "futils.h"
+#include "config.h"
+
+git_diff_delta *git_diff__delta_dup(
+ const git_diff_delta *d, git_pool *pool)
+{
+ git_diff_delta *delta = git__malloc(sizeof(git_diff_delta));
+ if (!delta)
+ return NULL;
+
+ memcpy(delta, d, sizeof(git_diff_delta));
+ GIT_DIFF_FLAG__CLEAR_INTERNAL(delta->flags);
+
+ if (d->old_file.path != NULL) {
+ delta->old_file.path = git_pool_strdup(pool, d->old_file.path);
+ if (delta->old_file.path == NULL)
+ goto fail;
+ }
+
+ if (d->new_file.path != d->old_file.path && d->new_file.path != NULL) {
+ delta->new_file.path = git_pool_strdup(pool, d->new_file.path);
+ if (delta->new_file.path == NULL)
+ goto fail;
+ } else {
+ delta->new_file.path = delta->old_file.path;
+ }
+
+ return delta;
+
+fail:
+ git__free(delta);
+ return NULL;
+}
+
+git_diff_delta *git_diff__merge_like_cgit(
+ const git_diff_delta *a,
+ const git_diff_delta *b,
+ git_pool *pool)
+{
+ git_diff_delta *dup;
+
+ /* Emulate C git for merging two diffs (a la 'git diff <sha>').
+ *
+ * When C git does a diff between the work dir and a tree, it actually
+ * diffs with the index but uses the workdir contents. This emulates
+ * those choices so we can emulate the type of diff.
+ *
+ * We have three file descriptions here, let's call them:
+ * f1 = a->old_file
+ * f2 = a->new_file AND b->old_file
+ * f3 = b->new_file
+ */
+
+ /* If one of the diffs is a conflict, just dup it */
+ if (b->status == GIT_DELTA_CONFLICTED)
+ return git_diff__delta_dup(b, pool);
+ if (a->status == GIT_DELTA_CONFLICTED)
+ return git_diff__delta_dup(a, pool);
+
+ /* if f2 == f3 or f2 is deleted, then just dup the 'a' diff */
+ if (b->status == GIT_DELTA_UNMODIFIED || a->status == GIT_DELTA_DELETED)
+ return git_diff__delta_dup(a, pool);
+
+ /* otherwise, base this diff on the 'b' diff */
+ if ((dup = git_diff__delta_dup(b, pool)) == NULL)
+ return NULL;
+
+ /* If 'a' status is uninteresting, then we're done */
+ if (a->status == GIT_DELTA_UNMODIFIED ||
+ a->status == GIT_DELTA_UNTRACKED ||
+ a->status == GIT_DELTA_UNREADABLE)
+ return dup;
+
+ GIT_ASSERT_WITH_RETVAL(b->status != GIT_DELTA_UNMODIFIED, NULL);
+
+ /* A cgit exception is that the diff of a file that is only in the
+ * index (i.e. not in HEAD nor workdir) is given as empty.
+ */
+ if (dup->status == GIT_DELTA_DELETED) {
+ if (a->status == GIT_DELTA_ADDED) {
+ dup->status = GIT_DELTA_UNMODIFIED;
+ dup->nfiles = 2;
+ }
+ /* else don't overwrite DELETE status */
+ } else {
+ dup->status = a->status;
+ dup->nfiles = a->nfiles;
+ }
+
+ git_oid_cpy(&dup->old_file.id, &a->old_file.id);
+ dup->old_file.mode = a->old_file.mode;
+ dup->old_file.size = a->old_file.size;
+ dup->old_file.flags = a->old_file.flags;
+
+ return dup;
+}
+
+int git_diff__merge(
+ git_diff *onto, const git_diff *from, git_diff__merge_cb cb)
+{
+ int error = 0;
+ git_pool onto_pool;
+ git_vector onto_new;
+ git_diff_delta *delta;
+ bool ignore_case, reversed;
+ unsigned int i, j;
+
+ GIT_ASSERT_ARG(onto);
+ GIT_ASSERT_ARG(from);
+
+ if (!from->deltas.length)
+ return 0;
+
+ ignore_case = ((onto->opts.flags & GIT_DIFF_IGNORE_CASE) != 0);
+ reversed = ((onto->opts.flags & GIT_DIFF_REVERSE) != 0);
+
+ if (ignore_case != ((from->opts.flags & GIT_DIFF_IGNORE_CASE) != 0) ||
+ reversed != ((from->opts.flags & GIT_DIFF_REVERSE) != 0)) {
+ git_error_set(GIT_ERROR_INVALID,
+ "attempt to merge diffs created with conflicting options");
+ return -1;
+ }
+
+ if (git_vector_init(&onto_new, onto->deltas.length, git_diff_delta__cmp) < 0 ||
+ git_pool_init(&onto_pool, 1) < 0)
+ return -1;
+
+ for (i = 0, j = 0; i < onto->deltas.length || j < from->deltas.length; ) {
+ git_diff_delta *o = GIT_VECTOR_GET(&onto->deltas, i);
+ const git_diff_delta *f = GIT_VECTOR_GET(&from->deltas, j);
+ int cmp = !f ? -1 : !o ? 1 :
+ STRCMP_CASESELECT(ignore_case, o->old_file.path, f->old_file.path);
+
+ if (cmp < 0) {
+ delta = git_diff__delta_dup(o, &onto_pool);
+ i++;
+ } else if (cmp > 0) {
+ delta = git_diff__delta_dup(f, &onto_pool);
+ j++;
+ } else {
+ const git_diff_delta *left = reversed ? f : o;
+ const git_diff_delta *right = reversed ? o : f;
+
+ delta = cb(left, right, &onto_pool);
+ i++;
+ j++;
+ }
+
+ /* the ignore rules for the target may not match the source
+ * or the result of a merged delta could be skippable...
+ */
+ if (delta && git_diff_delta__should_skip(&onto->opts, delta)) {
+ git__free(delta);
+ continue;
+ }
+
+ if ((error = !delta ? -1 : git_vector_insert(&onto_new, delta)) < 0)
+ break;
+ }
+
+ if (!error) {
+ git_vector_swap(&onto->deltas, &onto_new);
+ git_pool_swap(&onto->pool, &onto_pool);
+
+ if ((onto->opts.flags & GIT_DIFF_REVERSE) != 0)
+ onto->old_src = from->old_src;
+ else
+ onto->new_src = from->new_src;
+
+ /* prefix strings also come from old pool, so recreate those.*/
+ onto->opts.old_prefix =
+ git_pool_strdup_safe(&onto->pool, onto->opts.old_prefix);
+ onto->opts.new_prefix =
+ git_pool_strdup_safe(&onto->pool, onto->opts.new_prefix);
+ }
+
+ git_vector_free_deep(&onto_new);
+ git_pool_clear(&onto_pool);
+
+ return error;
+}
+
+int git_diff_merge(git_diff *onto, const git_diff *from)
+{
+ return git_diff__merge(onto, from, git_diff__merge_like_cgit);
+}
+
+int git_diff_find_similar__hashsig_for_file(
+ void **out, const git_diff_file *f, const char *path, void *p)
+{
+ git_hashsig_option_t opt = (git_hashsig_option_t)(intptr_t)p;
+
+ GIT_UNUSED(f);
+ return git_hashsig_create_fromfile((git_hashsig **)out, path, opt);
+}
+
+int git_diff_find_similar__hashsig_for_buf(
+ void **out, const git_diff_file *f, const char *buf, size_t len, void *p)
+{
+ git_hashsig_option_t opt = (git_hashsig_option_t)(intptr_t)p;
+
+ GIT_UNUSED(f);
+ return git_hashsig_create((git_hashsig **)out, buf, len, opt);
+}
+
+void git_diff_find_similar__hashsig_free(void *sig, void *payload)
+{
+ GIT_UNUSED(payload);
+ git_hashsig_free(sig);
+}
+
+int git_diff_find_similar__calc_similarity(
+ int *score, void *siga, void *sigb, void *payload)
+{
+ int error;
+
+ GIT_UNUSED(payload);
+ error = git_hashsig_compare(siga, sigb);
+ if (error < 0)
+ return error;
+
+ *score = error;
+ return 0;
+}
+
+#define DEFAULT_THRESHOLD 50
+#define DEFAULT_BREAK_REWRITE_THRESHOLD 60
+#define DEFAULT_RENAME_LIMIT 1000
+
+static int normalize_find_opts(
+ git_diff *diff,
+ git_diff_find_options *opts,
+ const git_diff_find_options *given)
+{
+ git_config *cfg = NULL;
+ git_hashsig_option_t hashsig_opts;
+
+ GIT_ERROR_CHECK_VERSION(given, GIT_DIFF_FIND_OPTIONS_VERSION, "git_diff_find_options");
+
+ if (diff->repo != NULL &&
+ git_repository_config__weakptr(&cfg, diff->repo) < 0)
+ return -1;
+
+ if (given)
+ memcpy(opts, given, sizeof(*opts));
+
+ if (!given ||
+ (given->flags & GIT_DIFF_FIND_ALL) == GIT_DIFF_FIND_BY_CONFIG)
+ {
+ if (cfg) {
+ char *rule =
+ git_config__get_string_force(cfg, "diff.renames", "true");
+ int boolval;
+
+ if (!git__parse_bool(&boolval, rule) && !boolval)
+ /* don't set FIND_RENAMES if bool value is false */;
+ else if (!strcasecmp(rule, "copies") || !strcasecmp(rule, "copy"))
+ opts->flags |= GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES;
+ else
+ opts->flags |= GIT_DIFF_FIND_RENAMES;
+
+ git__free(rule);
+ } else {
+ /* set default flag */
+ opts->flags |= GIT_DIFF_FIND_RENAMES;
+ }
+ }
+
+ /* some flags imply others */
+
+ if (opts->flags & GIT_DIFF_FIND_EXACT_MATCH_ONLY) {
+ /* if we are only looking for exact matches, then don't turn
+ * MODIFIED items into ADD/DELETE pairs because it's too picky
+ */
+ opts->flags &= ~(GIT_DIFF_FIND_REWRITES | GIT_DIFF_BREAK_REWRITES);
+
+ /* similarly, don't look for self-rewrites to split */
+ opts->flags &= ~GIT_DIFF_FIND_RENAMES_FROM_REWRITES;
+ }
+
+ if (opts->flags & GIT_DIFF_FIND_RENAMES_FROM_REWRITES)
+ opts->flags |= GIT_DIFF_FIND_RENAMES;
+
+ if (opts->flags & GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED)
+ opts->flags |= GIT_DIFF_FIND_COPIES;
+
+ if (opts->flags & GIT_DIFF_BREAK_REWRITES)
+ opts->flags |= GIT_DIFF_FIND_REWRITES;
+
+#define USE_DEFAULT(X) ((X) == 0 || (X) > 100)
+
+ if (USE_DEFAULT(opts->rename_threshold))
+ opts->rename_threshold = DEFAULT_THRESHOLD;
+
+ if (USE_DEFAULT(opts->rename_from_rewrite_threshold))
+ opts->rename_from_rewrite_threshold = DEFAULT_THRESHOLD;
+
+ if (USE_DEFAULT(opts->copy_threshold))
+ opts->copy_threshold = DEFAULT_THRESHOLD;
+
+ if (USE_DEFAULT(opts->break_rewrite_threshold))
+ opts->break_rewrite_threshold = DEFAULT_BREAK_REWRITE_THRESHOLD;
+
+#undef USE_DEFAULT
+
+ if (!opts->rename_limit) {
+ if (cfg) {
+ opts->rename_limit = git_config__get_int_force(
+ cfg, "diff.renamelimit", DEFAULT_RENAME_LIMIT);
+ }
+
+ if (opts->rename_limit <= 0)
+ opts->rename_limit = DEFAULT_RENAME_LIMIT;
+ }
+
+ /* assign the internal metric with whitespace flag as payload */
+ if (!opts->metric) {
+ opts->metric = git__malloc(sizeof(git_diff_similarity_metric));
+ GIT_ERROR_CHECK_ALLOC(opts->metric);
+
+ opts->metric->file_signature = git_diff_find_similar__hashsig_for_file;
+ opts->metric->buffer_signature = git_diff_find_similar__hashsig_for_buf;
+ opts->metric->free_signature = git_diff_find_similar__hashsig_free;
+ opts->metric->similarity = git_diff_find_similar__calc_similarity;
+
+ if (opts->flags & GIT_DIFF_FIND_IGNORE_WHITESPACE)
+ hashsig_opts = GIT_HASHSIG_IGNORE_WHITESPACE;
+ else if (opts->flags & GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE)
+ hashsig_opts = GIT_HASHSIG_NORMAL;
+ else
+ hashsig_opts = GIT_HASHSIG_SMART_WHITESPACE;
+ hashsig_opts |= GIT_HASHSIG_ALLOW_SMALL_FILES;
+ opts->metric->payload = (void *)hashsig_opts;
+ }
+
+ return 0;
+}
+
+static int insert_delete_side_of_split(
+ git_diff *diff, git_vector *onto, const git_diff_delta *delta)
+{
+ /* make new record for DELETED side of split */
+ git_diff_delta *deleted = git_diff__delta_dup(delta, &diff->pool);
+ GIT_ERROR_CHECK_ALLOC(deleted);
+
+ deleted->status = GIT_DELTA_DELETED;
+ deleted->nfiles = 1;
+ memset(&deleted->new_file, 0, sizeof(deleted->new_file));
+ deleted->new_file.path = deleted->old_file.path;
+ deleted->new_file.flags |= GIT_DIFF_FLAG_VALID_ID;
+ git_oid_clear(&deleted->new_file.id, diff->opts.oid_type);
+
+ return git_vector_insert(onto, deleted);
+}
+
+static int apply_splits_and_deletes(
+ git_diff *diff, size_t expected_size, bool actually_split)
+{
+ git_vector onto = GIT_VECTOR_INIT;
+ size_t i;
+ git_diff_delta *delta;
+
+ if (git_vector_init(&onto, expected_size, diff->deltas._cmp) < 0)
+ return -1;
+
+ /* build new delta list without TO_DELETE and splitting TO_SPLIT */
+ git_vector_foreach(&diff->deltas, i, delta) {
+ if ((delta->flags & GIT_DIFF_FLAG__TO_DELETE) != 0)
+ continue;
+
+ if ((delta->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0 && actually_split) {
+ delta->similarity = 0;
+
+ if (insert_delete_side_of_split(diff, &onto, delta) < 0)
+ goto on_error;
+
+ if (diff->new_src == GIT_ITERATOR_WORKDIR)
+ delta->status = GIT_DELTA_UNTRACKED;
+ else
+ delta->status = GIT_DELTA_ADDED;
+ delta->nfiles = 1;
+ memset(&delta->old_file, 0, sizeof(delta->old_file));
+ delta->old_file.path = delta->new_file.path;
+ delta->old_file.flags |= GIT_DIFF_FLAG_VALID_ID;
+ git_oid_clear(&delta->old_file.id, diff->opts.oid_type);
+ }
+
+ /* clean up delta before inserting into new list */
+ GIT_DIFF_FLAG__CLEAR_INTERNAL(delta->flags);
+
+ if (delta->status != GIT_DELTA_COPIED &&
+ delta->status != GIT_DELTA_RENAMED &&
+ (delta->status != GIT_DELTA_MODIFIED || actually_split))
+ delta->similarity = 0;
+
+ /* insert into new list */
+ if (git_vector_insert(&onto, delta) < 0)
+ goto on_error;
+ }
+
+ /* cannot return an error past this point */
+
+ /* free deltas from old list that didn't make it to the new one */
+ git_vector_foreach(&diff->deltas, i, delta) {
+ if ((delta->flags & GIT_DIFF_FLAG__TO_DELETE) != 0)
+ git__free(delta);
+ }
+
+ /* swap new delta list into place */
+ git_vector_swap(&diff->deltas, &onto);
+ git_vector_free(&onto);
+ git_vector_sort(&diff->deltas);
+
+ return 0;
+
+on_error:
+ git_vector_free_deep(&onto);
+
+ return -1;
+}
+
+GIT_INLINE(git_diff_file *) similarity_get_file(git_diff *diff, size_t idx)
+{
+ git_diff_delta *delta = git_vector_get(&diff->deltas, idx / 2);
+ return (idx & 1) ? &delta->new_file : &delta->old_file;
+}
+
+typedef struct {
+ size_t idx;
+ git_iterator_t src;
+ git_repository *repo;
+ git_diff_file *file;
+ git_str data;
+ git_odb_object *odb_obj;
+ git_blob *blob;
+} similarity_info;
+
+static int similarity_init(
+ similarity_info *info, git_diff *diff, size_t file_idx)
+{
+ info->idx = file_idx;
+ info->src = (file_idx & 1) ? diff->new_src : diff->old_src;
+ info->repo = diff->repo;
+ info->file = similarity_get_file(diff, file_idx);
+ info->odb_obj = NULL;
+ info->blob = NULL;
+ git_str_init(&info->data, 0);
+
+ if ((info->file->flags & GIT_DIFF_FLAG_VALID_SIZE) ||
+ info->src == GIT_ITERATOR_WORKDIR)
+ return 0;
+
+ return git_diff_file__resolve_zero_size(
+ info->file, &info->odb_obj, info->repo);
+}
+
+static int similarity_sig(
+ similarity_info *info,
+ const git_diff_find_options *opts,
+ void **cache)
+{
+ int error = 0;
+ git_diff_file *file = info->file;
+
+ if (info->src == GIT_ITERATOR_WORKDIR) {
+ if ((error = git_repository_workdir_path(
+ &info->data, info->repo, file->path)) < 0)
+ return error;
+
+ /* if path is not a regular file, just skip this item */
+ if (!git_fs_path_isfile(info->data.ptr))
+ return 0;
+
+ /* TODO: apply wd-to-odb filters to file data if necessary */
+
+ error = opts->metric->file_signature(
+ &cache[info->idx], info->file,
+ info->data.ptr, opts->metric->payload);
+ } else {
+ /* if we didn't initially know the size, we might have an odb_obj
+ * around from earlier, so convert that, otherwise load the blob now
+ */
+ if (info->odb_obj != NULL)
+ error = git_object__from_odb_object(
+ (git_object **)&info->blob, info->repo,
+ info->odb_obj, GIT_OBJECT_BLOB);
+ else
+ error = git_blob_lookup(&info->blob, info->repo, &file->id);
+
+ if (error < 0) {
+ /* if lookup fails, just skip this item in similarity calc */
+ git_error_clear();
+ } else {
+ size_t sz;
+
+ /* index size may not be actual blob size if filtered */
+ if (file->size != git_blob_rawsize(info->blob))
+ file->size = git_blob_rawsize(info->blob);
+
+ sz = git__is_sizet(file->size) ? (size_t)file->size : (size_t)-1;
+
+ error = opts->metric->buffer_signature(
+ &cache[info->idx], info->file,
+ git_blob_rawcontent(info->blob), sz, opts->metric->payload);
+ }
+ }
+
+ return error;
+}
+
+static void similarity_unload(similarity_info *info)
+{
+ if (info->odb_obj)
+ git_odb_object_free(info->odb_obj);
+
+ if (info->blob)
+ git_blob_free(info->blob);
+ else
+ git_str_dispose(&info->data);
+}
+
+#define FLAG_SET(opts,flag_name) (((opts)->flags & flag_name) != 0)
+
+/* - score < 0 means files cannot be compared
+ * - score >= 100 means files are exact match
+ * - score == 0 means files are completely different
+ */
+static int similarity_measure(
+ int *score,
+ git_diff *diff,
+ const git_diff_find_options *opts,
+ void **cache,
+ size_t a_idx,
+ size_t b_idx)
+{
+ git_diff_file *a_file = similarity_get_file(diff, a_idx);
+ git_diff_file *b_file = similarity_get_file(diff, b_idx);
+ bool exact_match = FLAG_SET(opts, GIT_DIFF_FIND_EXACT_MATCH_ONLY);
+ int error = 0;
+ similarity_info a_info, b_info;
+
+ *score = -1;
+
+ /* don't try to compare things that aren't files */
+ if (!GIT_MODE_ISBLOB(a_file->mode) || !GIT_MODE_ISBLOB(b_file->mode))
+ return 0;
+
+ /* if exact match is requested, force calculation of missing OIDs now */
+ if (exact_match) {
+ if (git_oid_is_zero(&a_file->id) &&
+ diff->old_src == GIT_ITERATOR_WORKDIR &&
+ !git_diff__oid_for_file(&a_file->id,
+ diff, a_file->path, a_file->mode, a_file->size))
+ a_file->flags |= GIT_DIFF_FLAG_VALID_ID;
+
+ if (git_oid_is_zero(&b_file->id) &&
+ diff->new_src == GIT_ITERATOR_WORKDIR &&
+ !git_diff__oid_for_file(&b_file->id,
+ diff, b_file->path, b_file->mode, b_file->size))
+ b_file->flags |= GIT_DIFF_FLAG_VALID_ID;
+ }
+
+ /* check OID match as a quick test */
+ if (git_oid__cmp(&a_file->id, &b_file->id) == 0) {
+ *score = 100;
+ return 0;
+ }
+
+ /* don't calculate signatures if we are doing exact match */
+ if (exact_match) {
+ *score = 0;
+ return 0;
+ }
+
+ memset(&a_info, 0, sizeof(a_info));
+ memset(&b_info, 0, sizeof(b_info));
+
+ /* set up similarity data (will try to update missing file sizes) */
+ if (!cache[a_idx] && (error = similarity_init(&a_info, diff, a_idx)) < 0)
+ return error;
+ if (!cache[b_idx] && (error = similarity_init(&b_info, diff, b_idx)) < 0)
+ goto cleanup;
+
+ /* check if file sizes are nowhere near each other */
+ if (a_file->size > 127 &&
+ b_file->size > 127 &&
+ (a_file->size > (b_file->size << 3) ||
+ b_file->size > (a_file->size << 3)))
+ goto cleanup;
+
+ /* update signature cache if needed */
+ if (!cache[a_idx]) {
+ if ((error = similarity_sig(&a_info, opts, cache)) < 0)
+ goto cleanup;
+ }
+ if (!cache[b_idx]) {
+ if ((error = similarity_sig(&b_info, opts, cache)) < 0)
+ goto cleanup;
+ }
+
+ /* calculate similarity provided that the metric choose to process
+ * both the a and b files (some may not if file is too big, etc).
+ */
+ if (cache[a_idx] && cache[b_idx])
+ error = opts->metric->similarity(
+ score, cache[a_idx], cache[b_idx], opts->metric->payload);
+
+cleanup:
+ similarity_unload(&a_info);
+ similarity_unload(&b_info);
+
+ return error;
+}
+
+static int calc_self_similarity(
+ git_diff *diff,
+ const git_diff_find_options *opts,
+ size_t delta_idx,
+ void **cache)
+{
+ int error, similarity = -1;
+ git_diff_delta *delta = GIT_VECTOR_GET(&diff->deltas, delta_idx);
+
+ if ((delta->flags & GIT_DIFF_FLAG__HAS_SELF_SIMILARITY) != 0)
+ return 0;
+
+ error = similarity_measure(
+ &similarity, diff, opts, cache, 2 * delta_idx, 2 * delta_idx + 1);
+ if (error < 0)
+ return error;
+
+ if (similarity >= 0) {
+ delta->similarity = (uint16_t)similarity;
+ delta->flags |= GIT_DIFF_FLAG__HAS_SELF_SIMILARITY;
+ }
+
+ return 0;
+}
+
+static bool is_rename_target(
+ git_diff *diff,
+ const git_diff_find_options *opts,
+ size_t delta_idx,
+ void **cache)
+{
+ git_diff_delta *delta = GIT_VECTOR_GET(&diff->deltas, delta_idx);
+
+ /* skip things that aren't plain blobs */
+ if (!GIT_MODE_ISBLOB(delta->new_file.mode))
+ return false;
+
+ /* only consider ADDED, RENAMED, COPIED, and split MODIFIED as
+ * targets; maybe include UNTRACKED if requested.
+ */
+ switch (delta->status) {
+ case GIT_DELTA_UNMODIFIED:
+ case GIT_DELTA_DELETED:
+ case GIT_DELTA_IGNORED:
+ case GIT_DELTA_CONFLICTED:
+ return false;
+
+ case GIT_DELTA_MODIFIED:
+ if (!FLAG_SET(opts, GIT_DIFF_FIND_REWRITES) &&
+ !FLAG_SET(opts, GIT_DIFF_FIND_RENAMES_FROM_REWRITES))
+ return false;
+
+ if (calc_self_similarity(diff, opts, delta_idx, cache) < 0)
+ return false;
+
+ if (FLAG_SET(opts, GIT_DIFF_BREAK_REWRITES) &&
+ delta->similarity < opts->break_rewrite_threshold) {
+ delta->flags |= GIT_DIFF_FLAG__TO_SPLIT;
+ break;
+ }
+ if (FLAG_SET(opts, GIT_DIFF_FIND_RENAMES_FROM_REWRITES) &&
+ delta->similarity < opts->rename_from_rewrite_threshold) {
+ delta->flags |= GIT_DIFF_FLAG__TO_SPLIT;
+ break;
+ }
+
+ return false;
+
+ case GIT_DELTA_UNTRACKED:
+ if (!FLAG_SET(opts, GIT_DIFF_FIND_FOR_UNTRACKED))
+ return false;
+ break;
+
+ default: /* all other status values should be checked */
+ break;
+ }
+
+ delta->flags |= GIT_DIFF_FLAG__IS_RENAME_TARGET;
+ return true;
+}
+
+static bool is_rename_source(
+ git_diff *diff,
+ const git_diff_find_options *opts,
+ size_t delta_idx,
+ void **cache)
+{
+ git_diff_delta *delta = GIT_VECTOR_GET(&diff->deltas, delta_idx);
+
+ /* skip things that aren't blobs */
+ if (!GIT_MODE_ISBLOB(delta->old_file.mode))
+ return false;
+
+ switch (delta->status) {
+ case GIT_DELTA_ADDED:
+ case GIT_DELTA_UNTRACKED:
+ case GIT_DELTA_UNREADABLE:
+ case GIT_DELTA_IGNORED:
+ case GIT_DELTA_CONFLICTED:
+ return false;
+
+ case GIT_DELTA_DELETED:
+ case GIT_DELTA_TYPECHANGE:
+ break;
+
+ case GIT_DELTA_UNMODIFIED:
+ if (!FLAG_SET(opts, GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED))
+ return false;
+ if (FLAG_SET(opts, GIT_DIFF_FIND_REMOVE_UNMODIFIED))
+ delta->flags |= GIT_DIFF_FLAG__TO_DELETE;
+ break;
+
+ default: /* MODIFIED, RENAMED, COPIED */
+ /* if we're finding copies, this could be a source */
+ if (FLAG_SET(opts, GIT_DIFF_FIND_COPIES))
+ break;
+
+ /* otherwise, this is only a source if we can split it */
+ if (!FLAG_SET(opts, GIT_DIFF_FIND_REWRITES) &&
+ !FLAG_SET(opts, GIT_DIFF_FIND_RENAMES_FROM_REWRITES))
+ return false;
+
+ if (calc_self_similarity(diff, opts, delta_idx, cache) < 0)
+ return false;
+
+ if (FLAG_SET(opts, GIT_DIFF_BREAK_REWRITES) &&
+ delta->similarity < opts->break_rewrite_threshold) {
+ delta->flags |= GIT_DIFF_FLAG__TO_SPLIT;
+ break;
+ }
+
+ if (FLAG_SET(opts, GIT_DIFF_FIND_RENAMES_FROM_REWRITES) &&
+ delta->similarity < opts->rename_from_rewrite_threshold)
+ break;
+
+ return false;
+ }
+
+ delta->flags |= GIT_DIFF_FLAG__IS_RENAME_SOURCE;
+ return true;
+}
+
+GIT_INLINE(bool) delta_is_split(git_diff_delta *delta)
+{
+ return (delta->status == GIT_DELTA_TYPECHANGE ||
+ (delta->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0);
+}
+
+GIT_INLINE(bool) delta_is_new_only(git_diff_delta *delta)
+{
+ return (delta->status == GIT_DELTA_ADDED ||
+ delta->status == GIT_DELTA_UNTRACKED ||
+ delta->status == GIT_DELTA_UNREADABLE ||
+ delta->status == GIT_DELTA_IGNORED);
+}
+
+GIT_INLINE(void) delta_make_rename(
+ git_diff_delta *to, const git_diff_delta *from, uint16_t similarity)
+{
+ to->status = GIT_DELTA_RENAMED;
+ to->similarity = similarity;
+ to->nfiles = 2;
+ memcpy(&to->old_file, &from->old_file, sizeof(to->old_file));
+ to->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
+}
+
+typedef struct {
+ size_t idx;
+ uint16_t similarity;
+} diff_find_match;
+
+int git_diff_find_similar(
+ git_diff *diff,
+ const git_diff_find_options *given_opts)
+{
+ size_t s, t;
+ int error = 0, result;
+ uint16_t similarity;
+ git_diff_delta *src, *tgt;
+ git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
+ size_t num_deltas, num_srcs = 0, num_tgts = 0;
+ size_t tried_srcs = 0, tried_tgts = 0;
+ size_t num_rewrites = 0, num_updates = 0, num_bumped = 0;
+ size_t sigcache_size;
+ void **sigcache = NULL; /* cache of similarity metric file signatures */
+ diff_find_match *tgt2src = NULL;
+ diff_find_match *src2tgt = NULL;
+ diff_find_match *tgt2src_copy = NULL;
+ diff_find_match *best_match;
+ git_diff_file swap;
+
+ GIT_ASSERT_ARG(diff);
+
+ if ((error = normalize_find_opts(diff, &opts, given_opts)) < 0)
+ return error;
+
+ num_deltas = diff->deltas.length;
+
+ /* TODO: maybe abort if deltas.length > rename_limit ??? */
+ if (!num_deltas || !git__is_uint32(num_deltas))
+ goto cleanup;
+
+ /* No flags set; nothing to do */
+ if ((opts.flags & GIT_DIFF_FIND_ALL) == 0)
+ goto cleanup;
+
+ GIT_ERROR_CHECK_ALLOC_MULTIPLY(&sigcache_size, num_deltas, 2);
+ sigcache = git__calloc(sigcache_size, sizeof(void *));
+ GIT_ERROR_CHECK_ALLOC(sigcache);
+
+ /* Label rename sources and targets
+ *
+ * This will also set self-similarity scores for MODIFIED files and
+ * mark them for splitting if break-rewrites is enabled
+ */
+ git_vector_foreach(&diff->deltas, t, tgt) {
+ if (is_rename_source(diff, &opts, t, sigcache))
+ ++num_srcs;
+
+ if (is_rename_target(diff, &opts, t, sigcache))
+ ++num_tgts;
+
+ if ((tgt->flags & GIT_DIFF_FLAG__TO_SPLIT) != 0)
+ num_rewrites++;
+ }
+
+ /* if there are no candidate srcs or tgts, we're done */
+ if (!num_srcs || !num_tgts)
+ goto cleanup;
+
+ src2tgt = git__calloc(num_deltas, sizeof(diff_find_match));
+ GIT_ERROR_CHECK_ALLOC(src2tgt);
+ tgt2src = git__calloc(num_deltas, sizeof(diff_find_match));
+ GIT_ERROR_CHECK_ALLOC(tgt2src);
+
+ if (FLAG_SET(&opts, GIT_DIFF_FIND_COPIES)) {
+ tgt2src_copy = git__calloc(num_deltas, sizeof(diff_find_match));
+ GIT_ERROR_CHECK_ALLOC(tgt2src_copy);
+ }
+
+ /*
+ * Find best-fit matches for rename / copy candidates
+ */
+
+find_best_matches:
+ tried_tgts = num_bumped = 0;
+
+ git_vector_foreach(&diff->deltas, t, tgt) {
+ /* skip things that are not rename targets */
+ if ((tgt->flags & GIT_DIFF_FLAG__IS_RENAME_TARGET) == 0)
+ continue;
+
+ tried_srcs = 0;
+
+ git_vector_foreach(&diff->deltas, s, src) {
+ /* skip things that are not rename sources */
+ if ((src->flags & GIT_DIFF_FLAG__IS_RENAME_SOURCE) == 0)
+ continue;
+
+ /* calculate similarity for this pair and find best match */
+ if (s == t)
+ result = -1; /* don't measure self-similarity here */
+ else if ((error = similarity_measure(
+ &result, diff, &opts, sigcache, 2 * s, 2 * t + 1)) < 0)
+ goto cleanup;
+
+ if (result < 0)
+ continue;
+ similarity = (uint16_t)result;
+
+ /* is this a better rename? */
+ if (tgt2src[t].similarity < similarity &&
+ src2tgt[s].similarity < similarity)
+ {
+ /* eject old mapping */
+ if (src2tgt[s].similarity > 0) {
+ tgt2src[src2tgt[s].idx].similarity = 0;
+ num_bumped++;
+ }
+ if (tgt2src[t].similarity > 0) {
+ src2tgt[tgt2src[t].idx].similarity = 0;
+ num_bumped++;
+ }
+
+ /* write new mapping */
+ tgt2src[t].idx = s;
+ tgt2src[t].similarity = similarity;
+ src2tgt[s].idx = t;
+ src2tgt[s].similarity = similarity;
+ }
+
+ /* keep best absolute match for copies */
+ if (tgt2src_copy != NULL &&
+ tgt2src_copy[t].similarity < similarity)
+ {
+ tgt2src_copy[t].idx = s;
+ tgt2src_copy[t].similarity = similarity;
+ }
+
+ if (++tried_srcs >= num_srcs)
+ break;
+
+ /* cap on maximum targets we'll examine (per "tgt" file) */
+ if (tried_srcs > opts.rename_limit)
+ break;
+ }
+
+ if (++tried_tgts >= num_tgts)
+ break;
+ }
+
+ if (num_bumped > 0) /* try again if we bumped some items */
+ goto find_best_matches;
+
+ /*
+ * Rewrite the diffs with renames / copies
+ */
+
+ git_vector_foreach(&diff->deltas, t, tgt) {
+ /* skip things that are not rename targets */
+ if ((tgt->flags & GIT_DIFF_FLAG__IS_RENAME_TARGET) == 0)
+ continue;
+
+ /* check if this delta was the target of a similarity */
+ if (tgt2src[t].similarity)
+ best_match = &tgt2src[t];
+ else if (tgt2src_copy && tgt2src_copy[t].similarity)
+ best_match = &tgt2src_copy[t];
+ else
+ continue;
+
+ s = best_match->idx;
+ src = GIT_VECTOR_GET(&diff->deltas, s);
+
+ /* possible scenarios:
+ * 1. from DELETE to ADD/UNTRACK/IGNORE = RENAME
+ * 2. from DELETE to SPLIT/TYPECHANGE = RENAME + DELETE
+ * 3. from SPLIT/TYPECHANGE to ADD/UNTRACK/IGNORE = ADD + RENAME
+ * 4. from SPLIT/TYPECHANGE to SPLIT/TYPECHANGE = RENAME + SPLIT
+ * 5. from OTHER to ADD/UNTRACK/IGNORE = OTHER + COPY
+ */
+
+ if (src->status == GIT_DELTA_DELETED) {
+
+ if (delta_is_new_only(tgt)) {
+
+ if (best_match->similarity < opts.rename_threshold)
+ continue;
+
+ delta_make_rename(tgt, src, best_match->similarity);
+
+ src->flags |= GIT_DIFF_FLAG__TO_DELETE;
+ num_rewrites++;
+ } else {
+ GIT_ASSERT(delta_is_split(tgt));
+
+ if (best_match->similarity < opts.rename_from_rewrite_threshold)
+ continue;
+
+ memcpy(&swap, &tgt->old_file, sizeof(swap));
+
+ delta_make_rename(tgt, src, best_match->similarity);
+ num_rewrites--;
+
+ GIT_ASSERT(src->status == GIT_DELTA_DELETED);
+ memcpy(&src->old_file, &swap, sizeof(src->old_file));
+ memset(&src->new_file, 0, sizeof(src->new_file));
+ src->new_file.path = src->old_file.path;
+ src->new_file.flags |= GIT_DIFF_FLAG_VALID_ID;
+ git_oid_clear(&src->new_file.id, diff->opts.oid_type);
+
+ num_updates++;
+
+ if (src2tgt[t].similarity > 0 && src2tgt[t].idx > t) {
+ /* what used to be at src t is now at src s */
+ tgt2src[src2tgt[t].idx].idx = s;
+ }
+ }
+ }
+
+ else if (delta_is_split(src)) {
+
+ if (delta_is_new_only(tgt)) {
+
+ if (best_match->similarity < opts.rename_threshold)
+ continue;
+
+ delta_make_rename(tgt, src, best_match->similarity);
+
+ src->status = (diff->new_src == GIT_ITERATOR_WORKDIR) ?
+ GIT_DELTA_UNTRACKED : GIT_DELTA_ADDED;
+ src->nfiles = 1;
+ memset(&src->old_file, 0, sizeof(src->old_file));
+ src->old_file.path = src->new_file.path;
+ src->old_file.flags |= GIT_DIFF_FLAG_VALID_ID;
+ git_oid_clear(&src->old_file.id, diff->opts.oid_type);
+
+ src->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
+ num_rewrites--;
+
+ num_updates++;
+ } else {
+ GIT_ASSERT(delta_is_split(src));
+
+ if (best_match->similarity < opts.rename_from_rewrite_threshold)
+ continue;
+
+ memcpy(&swap, &tgt->old_file, sizeof(swap));
+
+ delta_make_rename(tgt, src, best_match->similarity);
+ num_rewrites--;
+ num_updates++;
+
+ memcpy(&src->old_file, &swap, sizeof(src->old_file));
+
+ /* if we've just swapped the new element into the correct
+ * place, clear the SPLIT and RENAME_TARGET flags
+ */
+ if (tgt2src[s].idx == t &&
+ tgt2src[s].similarity >
+ opts.rename_from_rewrite_threshold) {
+ src->status = GIT_DELTA_RENAMED;
+ src->similarity = tgt2src[s].similarity;
+ tgt2src[s].similarity = 0;
+ src->flags &= ~(GIT_DIFF_FLAG__TO_SPLIT | GIT_DIFF_FLAG__IS_RENAME_TARGET);
+ num_rewrites--;
+ }
+ /* otherwise, if we just overwrote a source, update mapping */
+ else if (src2tgt[t].similarity > 0 && src2tgt[t].idx > t) {
+ /* what used to be at src t is now at src s */
+ tgt2src[src2tgt[t].idx].idx = s;
+ }
+
+ num_updates++;
+ }
+ }
+
+ else if (FLAG_SET(&opts, GIT_DIFF_FIND_COPIES)) {
+ if (tgt2src_copy[t].similarity < opts.copy_threshold)
+ continue;
+
+ /* always use best possible source for copy */
+ best_match = &tgt2src_copy[t];
+ src = GIT_VECTOR_GET(&diff->deltas, best_match->idx);
+
+ if (delta_is_split(tgt)) {
+ error = insert_delete_side_of_split(diff, &diff->deltas, tgt);
+ if (error < 0)
+ goto cleanup;
+ num_rewrites--;
+ }
+
+ if (!delta_is_split(tgt) && !delta_is_new_only(tgt))
+ continue;
+
+ tgt->status = GIT_DELTA_COPIED;
+ tgt->similarity = best_match->similarity;
+ tgt->nfiles = 2;
+ memcpy(&tgt->old_file, &src->old_file, sizeof(tgt->old_file));
+ tgt->flags &= ~GIT_DIFF_FLAG__TO_SPLIT;
+
+ num_updates++;
+ }
+ }
+
+ /*
+ * Actually split and delete entries as needed
+ */
+
+ if (num_rewrites > 0 || num_updates > 0)
+ error = apply_splits_and_deletes(
+ diff, diff->deltas.length - num_rewrites,
+ FLAG_SET(&opts, GIT_DIFF_BREAK_REWRITES) &&
+ !FLAG_SET(&opts, GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY));
+
+cleanup:
+ git__free(tgt2src);
+ git__free(src2tgt);
+ git__free(tgt2src_copy);
+
+ if (sigcache) {
+ for (t = 0; t < num_deltas * 2; ++t) {
+ if (sigcache[t] != NULL)
+ opts.metric->free_signature(sigcache[t], opts.metric->payload);
+ }
+ git__free(sigcache);
+ }
+
+ if (!given_opts || !given_opts->metric)
+ git__free(opts.metric);
+
+ return error;
+}
+
+#undef FLAG_SET
diff --git a/src/libgit2/diff_tform.h b/src/libgit2/diff_tform.h
new file mode 100644
index 0000000..7abb8b3
--- /dev/null
+++ b/src/libgit2/diff_tform.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_diff_tform_h__
+#define INCLUDE_diff_tform_h__
+
+#include "common.h"
+
+#include "diff_file.h"
+
+extern int git_diff_find_similar__hashsig_for_file(
+ void **out, const git_diff_file *f, const char *path, void *p);
+
+extern int git_diff_find_similar__hashsig_for_buf(
+ void **out, const git_diff_file *f, const char *buf, size_t len, void *p);
+
+extern void git_diff_find_similar__hashsig_free(void *sig, void *payload);
+
+extern int git_diff_find_similar__calc_similarity(
+ int *score, void *siga, void *sigb, void *payload);
+
+#endif
diff --git a/src/libgit2/diff_xdiff.c b/src/libgit2/diff_xdiff.c
new file mode 100644
index 0000000..5f56c52
--- /dev/null
+++ b/src/libgit2/diff_xdiff.c
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "diff_xdiff.h"
+
+#include "git2/errors.h"
+#include "diff.h"
+#include "diff_driver.h"
+#include "patch_generate.h"
+#include "utf8.h"
+
+static int git_xdiff_scan_int(const char **str, int *value)
+{
+ const char *scan = *str;
+ int v = 0, digits = 0;
+ /* find next digit */
+ for (scan = *str; *scan && !git__isdigit(*scan); scan++);
+ /* parse next number */
+ for (; git__isdigit(*scan); scan++, digits++)
+ v = (v * 10) + (*scan - '0');
+ *str = scan;
+ *value = v;
+ return (digits > 0) ? 0 : -1;
+}
+
+static int git_xdiff_parse_hunk(git_diff_hunk *hunk, const char *header)
+{
+ /* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */
+ if (*header != '@')
+ goto fail;
+ if (git_xdiff_scan_int(&header, &hunk->old_start) < 0)
+ goto fail;
+ if (*header == ',') {
+ if (git_xdiff_scan_int(&header, &hunk->old_lines) < 0)
+ goto fail;
+ } else
+ hunk->old_lines = 1;
+ if (git_xdiff_scan_int(&header, &hunk->new_start) < 0)
+ goto fail;
+ if (*header == ',') {
+ if (git_xdiff_scan_int(&header, &hunk->new_lines) < 0)
+ goto fail;
+ } else
+ hunk->new_lines = 1;
+ if (hunk->old_start < 0 || hunk->new_start < 0)
+ goto fail;
+
+ return 0;
+
+fail:
+ git_error_set(GIT_ERROR_INVALID, "malformed hunk header from xdiff");
+ return -1;
+}
+
+typedef struct {
+ git_xdiff_output *xo;
+ git_patch_generated *patch;
+ git_diff_hunk hunk;
+ int old_lineno, new_lineno;
+ mmfile_t xd_old_data, xd_new_data;
+} git_xdiff_info;
+
+static int diff_update_lines(
+ git_xdiff_info *info,
+ git_diff_line *line,
+ const char *content,
+ size_t content_len)
+{
+ const char *scan = content, *scan_end = content + content_len;
+
+ for (line->num_lines = 0; scan < scan_end; ++scan)
+ if (*scan == '\n')
+ ++line->num_lines;
+
+ line->content = content;
+ line->content_len = content_len;
+
+ /* expect " "/"-"/"+", then data */
+ switch (line->origin) {
+ case GIT_DIFF_LINE_ADDITION:
+ case GIT_DIFF_LINE_DEL_EOFNL:
+ line->old_lineno = -1;
+ line->new_lineno = info->new_lineno;
+ info->new_lineno += (int)line->num_lines;
+ break;
+ case GIT_DIFF_LINE_DELETION:
+ case GIT_DIFF_LINE_ADD_EOFNL:
+ line->old_lineno = info->old_lineno;
+ line->new_lineno = -1;
+ info->old_lineno += (int)line->num_lines;
+ break;
+ case GIT_DIFF_LINE_CONTEXT:
+ case GIT_DIFF_LINE_CONTEXT_EOFNL:
+ line->old_lineno = info->old_lineno;
+ line->new_lineno = info->new_lineno;
+ info->old_lineno += (int)line->num_lines;
+ info->new_lineno += (int)line->num_lines;
+ break;
+ default:
+ git_error_set(GIT_ERROR_INVALID, "unknown diff line origin %02x",
+ (unsigned int)line->origin);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int git_xdiff_cb(void *priv, mmbuffer_t *bufs, int len)
+{
+ git_xdiff_info *info = priv;
+ git_patch_generated *patch = info->patch;
+ const git_diff_delta *delta = patch->base.delta;
+ git_patch_generated_output *output = &info->xo->output;
+ git_diff_line line;
+ size_t buffer_len;
+
+ if (len == 1) {
+ output->error = git_xdiff_parse_hunk(&info->hunk, bufs[0].ptr);
+ if (output->error < 0)
+ return output->error;
+
+ info->hunk.header_len = bufs[0].size;
+ if (info->hunk.header_len >= sizeof(info->hunk.header))
+ info->hunk.header_len = sizeof(info->hunk.header) - 1;
+
+ /* Sanitize the hunk header in case there is invalid Unicode */
+ buffer_len = git_utf8_valid_buf_length(bufs[0].ptr, info->hunk.header_len);
+ /* Sanitizing the hunk header may delete the newline, so add it back again if there is room */
+ if (buffer_len < info->hunk.header_len) {
+ bufs[0].ptr[buffer_len] = '\n';
+ buffer_len += 1;
+ info->hunk.header_len = buffer_len;
+ }
+
+ memcpy(info->hunk.header, bufs[0].ptr, info->hunk.header_len);
+ info->hunk.header[info->hunk.header_len] = '\0';
+
+ if (output->hunk_cb != NULL &&
+ (output->error = output->hunk_cb(
+ delta, &info->hunk, output->payload)))
+ return output->error;
+
+ info->old_lineno = info->hunk.old_start;
+ info->new_lineno = info->hunk.new_start;
+ }
+
+ if (len == 2 || len == 3) {
+ /* expect " "/"-"/"+", then data */
+ line.origin =
+ (*bufs[0].ptr == '+') ? GIT_DIFF_LINE_ADDITION :
+ (*bufs[0].ptr == '-') ? GIT_DIFF_LINE_DELETION :
+ GIT_DIFF_LINE_CONTEXT;
+
+ if (line.origin == GIT_DIFF_LINE_ADDITION)
+ line.content_offset = bufs[1].ptr - info->xd_new_data.ptr;
+ else if (line.origin == GIT_DIFF_LINE_DELETION)
+ line.content_offset = bufs[1].ptr - info->xd_old_data.ptr;
+ else
+ line.content_offset = -1;
+
+ output->error = diff_update_lines(
+ info, &line, bufs[1].ptr, bufs[1].size);
+
+ if (!output->error && output->data_cb != NULL)
+ output->error = output->data_cb(
+ delta, &info->hunk, &line, output->payload);
+ }
+
+ if (len == 3 && !output->error) {
+ /* If we have a '+' and a third buf, then we have added a line
+ * without a newline and the old code had one, so DEL_EOFNL.
+ * If we have a '-' and a third buf, then we have removed a line
+ * with out a newline but added a blank line, so ADD_EOFNL.
+ */
+ line.origin =
+ (*bufs[0].ptr == '+') ? GIT_DIFF_LINE_DEL_EOFNL :
+ (*bufs[0].ptr == '-') ? GIT_DIFF_LINE_ADD_EOFNL :
+ GIT_DIFF_LINE_CONTEXT_EOFNL;
+
+ line.content_offset = -1;
+
+ output->error = diff_update_lines(
+ info, &line, bufs[2].ptr, bufs[2].size);
+
+ if (!output->error && output->data_cb != NULL)
+ output->error = output->data_cb(
+ delta, &info->hunk, &line, output->payload);
+ }
+
+ return output->error;
+}
+
+static int git_xdiff(git_patch_generated_output *output, git_patch_generated *patch)
+{
+ git_xdiff_output *xo = (git_xdiff_output *)output;
+ git_xdiff_info info;
+ git_diff_find_context_payload findctxt;
+
+ memset(&info, 0, sizeof(info));
+ info.patch = patch;
+ info.xo = xo;
+
+ xo->callback.priv = &info;
+
+ git_diff_find_context_init(
+ &xo->config.find_func, &findctxt, git_patch_generated_driver(patch));
+ xo->config.find_func_priv = &findctxt;
+
+ if (xo->config.find_func != NULL)
+ xo->config.flags |= XDL_EMIT_FUNCNAMES;
+ else
+ xo->config.flags &= ~XDL_EMIT_FUNCNAMES;
+
+ /* TODO: check ofile.opts_flags to see if driver-specific per-file
+ * updates are needed to xo->params.flags
+ */
+
+ if (git_patch_generated_old_data(&info.xd_old_data.ptr, &info.xd_old_data.size, patch) < 0 ||
+ git_patch_generated_new_data(&info.xd_new_data.ptr, &info.xd_new_data.size, patch) < 0)
+ return -1;
+
+ xdl_diff(&info.xd_old_data, &info.xd_new_data,
+ &xo->params, &xo->config, &xo->callback);
+
+ git_diff_find_context_clear(&findctxt);
+
+ return xo->output.error;
+}
+
+void git_xdiff_init(git_xdiff_output *xo, const git_diff_options *opts)
+{
+ uint32_t flags = opts ? opts->flags : 0;
+
+ xo->output.diff_cb = git_xdiff;
+
+ xo->config.ctxlen = opts ? opts->context_lines : 3;
+ xo->config.interhunkctxlen = opts ? opts->interhunk_lines : 0;
+
+ if (flags & GIT_DIFF_IGNORE_WHITESPACE)
+ xo->params.flags |= XDF_WHITESPACE_FLAGS;
+ if (flags & GIT_DIFF_IGNORE_WHITESPACE_CHANGE)
+ xo->params.flags |= XDF_IGNORE_WHITESPACE_CHANGE;
+ if (flags & GIT_DIFF_IGNORE_WHITESPACE_EOL)
+ xo->params.flags |= XDF_IGNORE_WHITESPACE_AT_EOL;
+ if (flags & GIT_DIFF_INDENT_HEURISTIC)
+ xo->params.flags |= XDF_INDENT_HEURISTIC;
+
+ if (flags & GIT_DIFF_PATIENCE)
+ xo->params.flags |= XDF_PATIENCE_DIFF;
+ if (flags & GIT_DIFF_MINIMAL)
+ xo->params.flags |= XDF_NEED_MINIMAL;
+
+ if (flags & GIT_DIFF_IGNORE_BLANK_LINES)
+ xo->params.flags |= XDF_IGNORE_BLANK_LINES;
+
+ xo->callback.out_line = git_xdiff_cb;
+}
diff --git a/src/libgit2/diff_xdiff.h b/src/libgit2/diff_xdiff.h
new file mode 100644
index 0000000..327dc7c
--- /dev/null
+++ b/src/libgit2/diff_xdiff.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_diff_xdiff_h__
+#define INCLUDE_diff_xdiff_h__
+
+#include "common.h"
+
+#include "diff.h"
+#include "xdiff.h"
+#include "patch_generate.h"
+
+/* xdiff cannot cope with large files. these files should not be passed to
+ * xdiff. callers should treat these large files as binary.
+ */
+#define GIT_XDIFF_MAX_SIZE (INT64_C(1024) * 1024 * 1023)
+
+/* A git_xdiff_output is a git_patch_generate_output with extra fields
+ * necessary to use libxdiff. Calling git_xdiff_init() will set the diff_cb
+ * field of the output to use xdiff to generate the diffs.
+ */
+typedef struct {
+ git_patch_generated_output output;
+
+ xdemitconf_t config;
+ xpparam_t params;
+ xdemitcb_t callback;
+} git_xdiff_output;
+
+void git_xdiff_init(git_xdiff_output *xo, const git_diff_options *opts);
+
+#endif
diff --git a/src/libgit2/email.c b/src/libgit2/email.c
new file mode 100644
index 0000000..8a10a12
--- /dev/null
+++ b/src/libgit2/email.c
@@ -0,0 +1,316 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "email.h"
+
+#include "common.h"
+#include "buf.h"
+#include "diff_generate.h"
+#include "diff_stats.h"
+#include "patch.h"
+#include "date.h"
+
+#include "git2/email.h"
+#include "git2/patch.h"
+#include "git2/version.h"
+
+/*
+ * Git uses a "magic" timestamp to indicate that an email message
+ * is from `git format-patch` (or our equivalent).
+ */
+#define EMAIL_TIMESTAMP "Mon Sep 17 00:00:00 2001"
+
+GIT_INLINE(int) include_prefix(
+ size_t patch_count,
+ git_email_create_options *opts)
+{
+ return ((!opts->subject_prefix || *opts->subject_prefix) ||
+ (opts->flags & GIT_EMAIL_CREATE_ALWAYS_NUMBER) != 0 ||
+ opts->reroll_number ||
+ (patch_count > 1 && !(opts->flags & GIT_EMAIL_CREATE_OMIT_NUMBERS)));
+}
+
+static int append_prefix(
+ git_str *out,
+ size_t patch_idx,
+ size_t patch_count,
+ git_email_create_options *opts)
+{
+ const char *subject_prefix = opts->subject_prefix ?
+ opts->subject_prefix : "PATCH";
+
+ git_str_putc(out, '[');
+
+ if (*subject_prefix)
+ git_str_puts(out, subject_prefix);
+
+ if (opts->reroll_number) {
+ if (*subject_prefix)
+ git_str_putc(out, ' ');
+
+ git_str_printf(out, "v%" PRIuZ, opts->reroll_number);
+ }
+
+ if ((opts->flags & GIT_EMAIL_CREATE_ALWAYS_NUMBER) != 0 ||
+ (patch_count > 1 && !(opts->flags & GIT_EMAIL_CREATE_OMIT_NUMBERS))) {
+ size_t start_number = opts->start_number ?
+ opts->start_number : 1;
+
+ if (*subject_prefix || opts->reroll_number)
+ git_str_putc(out, ' ');
+
+ git_str_printf(out, "%" PRIuZ "/%" PRIuZ,
+ patch_idx + (start_number - 1),
+ patch_count + (start_number - 1));
+ }
+
+ git_str_puts(out, "]");
+
+ return git_str_oom(out) ? -1 : 0;
+}
+
+static int append_date(
+ git_str *out,
+ const git_time *date)
+{
+ int error;
+
+ if ((error = git_str_printf(out, "Date: ")) == 0 &&
+ (error = git_date_rfc2822_fmt(out, date->time, date->offset)) == 0)
+ error = git_str_putc(out, '\n');
+
+ return error;
+}
+
+static int append_subject(
+ git_str *out,
+ size_t patch_idx,
+ size_t patch_count,
+ const char *summary,
+ git_email_create_options *opts)
+{
+ bool prefix = include_prefix(patch_count, opts);
+ size_t summary_len = summary ? strlen(summary) : 0;
+ int error;
+
+ if (summary_len) {
+ const char *nl = strchr(summary, '\n');
+
+ if (nl)
+ summary_len = (nl - summary);
+ }
+
+ if ((error = git_str_puts(out, "Subject: ")) < 0)
+ return error;
+
+ if (prefix &&
+ (error = append_prefix(out, patch_idx, patch_count, opts)) < 0)
+ return error;
+
+ if (prefix && summary_len && (error = git_str_putc(out, ' ')) < 0)
+ return error;
+
+ if (summary_len &&
+ (error = git_str_put(out, summary, summary_len)) < 0)
+ return error;
+
+ return git_str_putc(out, '\n');
+}
+
+static int append_header(
+ git_str *out,
+ size_t patch_idx,
+ size_t patch_count,
+ const git_oid *commit_id,
+ const char *summary,
+ const git_signature *author,
+ git_email_create_options *opts)
+{
+ char id[GIT_OID_MAX_HEXSIZE + 1];
+ int error;
+
+ git_oid_tostr(id, GIT_OID_MAX_HEXSIZE + 1, commit_id);
+
+ if ((error = git_str_printf(out, "From %s %s\n", id, EMAIL_TIMESTAMP)) < 0 ||
+ (error = git_str_printf(out, "From: %s <%s>\n", author->name, author->email)) < 0 ||
+ (error = append_date(out, &author->when)) < 0 ||
+ (error = append_subject(out, patch_idx, patch_count, summary, opts)) < 0)
+ return error;
+
+ if ((error = git_str_putc(out, '\n')) < 0)
+ return error;
+
+ return 0;
+}
+
+static int append_body(git_str *out, const char *body)
+{
+ size_t body_len;
+ int error;
+
+ if (!body)
+ return 0;
+
+ body_len = strlen(body);
+
+ if ((error = git_str_puts(out, body)) < 0)
+ return error;
+
+ if (body_len && body[body_len - 1] != '\n')
+ error = git_str_putc(out, '\n');
+
+ return error;
+}
+
+static int append_diffstat(git_str *out, git_diff *diff)
+{
+ git_diff_stats *stats = NULL;
+ unsigned int format_flags;
+ int error;
+
+ format_flags = GIT_DIFF_STATS_FULL | GIT_DIFF_STATS_INCLUDE_SUMMARY;
+
+ if ((error = git_diff_get_stats(&stats, diff)) == 0 &&
+ (error = git_diff__stats_to_buf(out, stats, format_flags, 0)) == 0)
+ error = git_str_putc(out, '\n');
+
+ git_diff_stats_free(stats);
+ return error;
+}
+
+static int append_patches(git_str *out, git_diff *diff)
+{
+ size_t i, deltas;
+ int error = 0;
+
+ deltas = git_diff_num_deltas(diff);
+
+ for (i = 0; i < deltas; ++i) {
+ git_patch *patch = NULL;
+
+ if ((error = git_patch_from_diff(&patch, diff, i)) >= 0)
+ error = git_patch__to_buf(out, patch);
+
+ git_patch_free(patch);
+
+ if (error < 0)
+ break;
+ }
+
+ return error;
+}
+
+int git_email__append_from_diff(
+ git_str *out,
+ git_diff *diff,
+ size_t patch_idx,
+ size_t patch_count,
+ const git_oid *commit_id,
+ const char *summary,
+ const char *body,
+ const git_signature *author,
+ const git_email_create_options *given_opts)
+{
+ git_email_create_options opts = GIT_EMAIL_CREATE_OPTIONS_INIT;
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(diff);
+ GIT_ASSERT_ARG(!patch_idx || patch_idx <= patch_count);
+ GIT_ASSERT_ARG(commit_id);
+ GIT_ASSERT_ARG(author);
+
+ GIT_ERROR_CHECK_VERSION(given_opts,
+ GIT_EMAIL_CREATE_OPTIONS_VERSION,
+ "git_email_create_options");
+
+ if (given_opts)
+ memcpy(&opts, given_opts, sizeof(git_email_create_options));
+
+ if ((error = append_header(out, patch_idx, patch_count, commit_id, summary, author, &opts)) == 0 &&
+ (error = append_body(out, body)) == 0 &&
+ (error = git_str_puts(out, "---\n")) == 0 &&
+ (error = append_diffstat(out, diff)) == 0 &&
+ (error = append_patches(out, diff)) == 0)
+ error = git_str_puts(out, "--\nlibgit2 " LIBGIT2_VERSION "\n\n");
+
+ return error;
+}
+
+int git_email_create_from_diff(
+ git_buf *out,
+ git_diff *diff,
+ size_t patch_idx,
+ size_t patch_count,
+ const git_oid *commit_id,
+ const char *summary,
+ const char *body,
+ const git_signature *author,
+ const git_email_create_options *given_opts)
+{
+ git_str email = GIT_STR_INIT;
+ int error;
+
+ git_buf_tostr(&email, out);
+
+ error = git_email__append_from_diff(&email, diff, patch_idx,
+ patch_count, commit_id, summary, body, author,
+ given_opts);
+
+ if (error == 0)
+ error = git_buf_fromstr(out, &email);
+
+ git_str_dispose(&email);
+ return error;
+}
+
+int git_email_create_from_commit(
+ git_buf *out,
+ git_commit *commit,
+ const git_email_create_options *given_opts)
+{
+ git_email_create_options opts = GIT_EMAIL_CREATE_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ git_repository *repo;
+ git_diff_options *diff_opts;
+ git_diff_find_options *find_opts;
+ const git_signature *author;
+ const char *summary, *body;
+ const git_oid *commit_id;
+ int error = -1;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(commit);
+
+ GIT_ERROR_CHECK_VERSION(given_opts,
+ GIT_EMAIL_CREATE_OPTIONS_VERSION,
+ "git_email_create_options");
+
+ if (given_opts)
+ memcpy(&opts, given_opts, sizeof(git_email_create_options));
+
+ repo = git_commit_owner(commit);
+ author = git_commit_author(commit);
+ summary = git_commit_summary(commit);
+ body = git_commit_body(commit);
+ commit_id = git_commit_id(commit);
+ diff_opts = &opts.diff_opts;
+ find_opts = &opts.diff_find_opts;
+
+ if ((error = git_diff__commit(&diff, repo, commit, diff_opts)) < 0)
+ goto done;
+
+ if ((opts.flags & GIT_EMAIL_CREATE_NO_RENAMES) == 0 &&
+ (error = git_diff_find_similar(diff, find_opts)) < 0)
+ goto done;
+
+ error = git_email_create_from_diff(out, diff, 1, 1, commit_id, summary, body, author, &opts);
+
+done:
+ git_diff_free(diff);
+ return error;
+}
diff --git a/src/libgit2/email.h b/src/libgit2/email.h
new file mode 100644
index 0000000..083e56d
--- /dev/null
+++ b/src/libgit2/email.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_email_h__
+#define INCLUDE_email_h__
+
+#include "common.h"
+
+#include "git2/email.h"
+
+extern int git_email__append_from_diff(
+ git_str *out,
+ git_diff *diff,
+ size_t patch_idx,
+ size_t patch_count,
+ const git_oid *commit_id,
+ const char *summary,
+ const char *body,
+ const git_signature *author,
+ const git_email_create_options *given_opts);
+
+#endif
diff --git a/src/libgit2/errors.c b/src/libgit2/errors.c
new file mode 100644
index 0000000..2e58948
--- /dev/null
+++ b/src/libgit2/errors.c
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+
+#include "threadstate.h"
+#include "posix.h"
+#include "str.h"
+#include "libgit2.h"
+
+/********************************************
+ * New error handling
+ ********************************************/
+
+static git_error oom_error = {
+ "Out of memory",
+ GIT_ERROR_NOMEMORY
+};
+
+static git_error uninitialized_error = {
+ "libgit2 has not been initialized; you must call git_libgit2_init",
+ GIT_ERROR_INVALID
+};
+
+static git_error tlsdata_error = {
+ "thread-local data initialization failure",
+ GIT_ERROR
+};
+
+static void set_error_from_buffer(int error_class)
+{
+ git_threadstate *threadstate = git_threadstate_get();
+ git_error *error;
+ git_str *buf;
+
+ if (!threadstate)
+ return;
+
+ error = &threadstate->error_t;
+ buf = &threadstate->error_buf;
+
+ error->message = buf->ptr;
+ error->klass = error_class;
+
+ threadstate->last_error = error;
+}
+
+static void set_error(int error_class, char *string)
+{
+ git_threadstate *threadstate = git_threadstate_get();
+ git_str *buf;
+
+ if (!threadstate)
+ return;
+
+ buf = &threadstate->error_buf;
+
+ git_str_clear(buf);
+
+ if (string) {
+ git_str_puts(buf, string);
+ git__free(string);
+ }
+
+ set_error_from_buffer(error_class);
+}
+
+void git_error_set_oom(void)
+{
+ git_threadstate *threadstate = git_threadstate_get();
+
+ if (!threadstate)
+ return;
+
+ threadstate->last_error = &oom_error;
+}
+
+void git_error_set(int error_class, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ git_error_vset(error_class, fmt, ap);
+ va_end(ap);
+}
+
+void git_error_vset(int error_class, const char *fmt, va_list ap)
+{
+#ifdef GIT_WIN32
+ DWORD win32_error_code = (error_class == GIT_ERROR_OS) ? GetLastError() : 0;
+#endif
+
+ git_threadstate *threadstate = git_threadstate_get();
+ int error_code = (error_class == GIT_ERROR_OS) ? errno : 0;
+ git_str *buf;
+
+ if (!threadstate)
+ return;
+
+ buf = &threadstate->error_buf;
+
+ git_str_clear(buf);
+
+ if (fmt) {
+ git_str_vprintf(buf, fmt, ap);
+ if (error_class == GIT_ERROR_OS)
+ git_str_PUTS(buf, ": ");
+ }
+
+ if (error_class == GIT_ERROR_OS) {
+#ifdef GIT_WIN32
+ char *win32_error = git_win32_get_error_message(win32_error_code);
+ if (win32_error) {
+ git_str_puts(buf, win32_error);
+ git__free(win32_error);
+
+ SetLastError(0);
+ }
+ else
+#endif
+ if (error_code)
+ git_str_puts(buf, strerror(error_code));
+
+ if (error_code)
+ errno = 0;
+ }
+
+ if (!git_str_oom(buf))
+ set_error_from_buffer(error_class);
+}
+
+int git_error_set_str(int error_class, const char *string)
+{
+ git_threadstate *threadstate = git_threadstate_get();
+ git_str *buf;
+
+ GIT_ASSERT_ARG(string);
+
+ if (!threadstate)
+ return -1;
+
+ buf = &threadstate->error_buf;
+
+ git_str_clear(buf);
+ git_str_puts(buf, string);
+
+ if (git_str_oom(buf))
+ return -1;
+
+ set_error_from_buffer(error_class);
+ return 0;
+}
+
+void git_error_clear(void)
+{
+ git_threadstate *threadstate = git_threadstate_get();
+
+ if (!threadstate)
+ return;
+
+ if (threadstate->last_error != NULL) {
+ set_error(0, NULL);
+ threadstate->last_error = NULL;
+ }
+
+ errno = 0;
+#ifdef GIT_WIN32
+ SetLastError(0);
+#endif
+}
+
+const git_error *git_error_last(void)
+{
+ git_threadstate *threadstate;
+
+ /* If the library is not initialized, return a static error. */
+ if (!git_libgit2_init_count())
+ return &uninitialized_error;
+
+ if ((threadstate = git_threadstate_get()) == NULL)
+ return &tlsdata_error;
+
+ return threadstate->last_error;
+}
+
+int git_error_state_capture(git_error_state *state, int error_code)
+{
+ git_threadstate *threadstate = git_threadstate_get();
+ git_error *error;
+ git_str *error_buf;
+
+ if (!threadstate)
+ return -1;
+
+ error = threadstate->last_error;
+ error_buf = &threadstate->error_buf;
+
+ memset(state, 0, sizeof(git_error_state));
+
+ if (!error_code)
+ return 0;
+
+ state->error_code = error_code;
+ state->oom = (error == &oom_error);
+
+ if (error) {
+ state->error_msg.klass = error->klass;
+
+ if (state->oom)
+ state->error_msg.message = oom_error.message;
+ else
+ state->error_msg.message = git_str_detach(error_buf);
+ }
+
+ git_error_clear();
+ return error_code;
+}
+
+int git_error_state_restore(git_error_state *state)
+{
+ int ret = 0;
+
+ git_error_clear();
+
+ if (state && state->error_msg.message) {
+ if (state->oom)
+ git_error_set_oom();
+ else
+ set_error(state->error_msg.klass, state->error_msg.message);
+
+ ret = state->error_code;
+ memset(state, 0, sizeof(git_error_state));
+ }
+
+ return ret;
+}
+
+void git_error_state_free(git_error_state *state)
+{
+ if (!state)
+ return;
+
+ if (!state->oom)
+ git__free(state->error_msg.message);
+
+ memset(state, 0, sizeof(git_error_state));
+}
+
+int git_error_system_last(void)
+{
+#ifdef GIT_WIN32
+ return GetLastError();
+#else
+ return errno;
+#endif
+}
+
+void git_error_system_set(int code)
+{
+#ifdef GIT_WIN32
+ SetLastError(code);
+#else
+ errno = code;
+#endif
+}
+
+/* Deprecated error values and functions */
+
+#ifndef GIT_DEPRECATE_HARD
+const git_error *giterr_last(void)
+{
+ return git_error_last();
+}
+
+void giterr_clear(void)
+{
+ git_error_clear();
+}
+
+void giterr_set_str(int error_class, const char *string)
+{
+ git_error_set_str(error_class, string);
+}
+
+void giterr_set_oom(void)
+{
+ git_error_set_oom();
+}
+#endif
diff --git a/src/libgit2/errors.h b/src/libgit2/errors.h
new file mode 100644
index 0000000..772c7ba
--- /dev/null
+++ b/src/libgit2/errors.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_errors_h__
+#define INCLUDE_errors_h__
+
+#include "common.h"
+
+/*
+ * `vprintf`-style formatting for the error message for this thread.
+ */
+void git_error_vset(int error_class, const char *fmt, va_list ap);
+
+/**
+ * Set error message for user callback if needed.
+ *
+ * If the error code in non-zero and no error message is set, this
+ * sets a generic error message.
+ *
+ * @return This always returns the `error_code` parameter.
+ */
+GIT_INLINE(int) git_error_set_after_callback_function(
+ int error_code, const char *action)
+{
+ if (error_code) {
+ const git_error *e = git_error_last();
+ if (!e || !e->message)
+ git_error_set(e ? e->klass : GIT_ERROR_CALLBACK,
+ "%s callback returned %d", action, error_code);
+ }
+ return error_code;
+}
+
+#ifdef GIT_WIN32
+#define git_error_set_after_callback(code) \
+ git_error_set_after_callback_function((code), __FUNCTION__)
+#else
+#define git_error_set_after_callback(code) \
+ git_error_set_after_callback_function((code), __func__)
+#endif
+
+/**
+ * Gets the system error code for this thread.
+ */
+int git_error_system_last(void);
+
+/**
+ * Sets the system error code for this thread.
+ */
+void git_error_system_set(int code);
+
+/**
+ * Structure to preserve libgit2 error state
+ */
+typedef struct {
+ int error_code;
+ unsigned int oom : 1;
+ git_error error_msg;
+} git_error_state;
+
+/**
+ * Capture current error state to restore later, returning error code.
+ * If `error_code` is zero, this does not clear the current error state.
+ * You must either restore this error state, or free it.
+ */
+extern int git_error_state_capture(git_error_state *state, int error_code);
+
+/**
+ * Restore error state to a previous value, returning saved error code.
+ */
+extern int git_error_state_restore(git_error_state *state);
+
+/** Free an error state. */
+extern void git_error_state_free(git_error_state *state);
+
+#endif
diff --git a/src/libgit2/experimental.h.in b/src/libgit2/experimental.h.in
new file mode 100644
index 0000000..25fb14b
--- /dev/null
+++ b/src/libgit2/experimental.h.in
@@ -0,0 +1,13 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_experimental_h__
+#define INCLUDE_experimental_h__
+
+#cmakedefine GIT_EXPERIMENTAL_SHA256 1
+
+#endif
diff --git a/src/libgit2/fetch.c b/src/libgit2/fetch.c
new file mode 100644
index 0000000..d74abb4
--- /dev/null
+++ b/src/libgit2/fetch.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "fetch.h"
+
+#include "git2/oid.h"
+#include "git2/refs.h"
+#include "git2/revwalk.h"
+#include "git2/transport.h"
+#include "git2/sys/remote.h"
+
+#include "oid.h"
+#include "remote.h"
+#include "refspec.h"
+#include "pack.h"
+#include "repository.h"
+#include "refs.h"
+#include "transports/smart.h"
+
+static int maybe_want(git_remote *remote, git_remote_head *head, git_refspec *tagspec, git_remote_autotag_option_t tagopt)
+{
+ int match = 0, valid;
+
+ if (git_reference_name_is_valid(&valid, head->name) < 0)
+ return -1;
+
+ if (!valid)
+ return 0;
+
+ if (tagopt == GIT_REMOTE_DOWNLOAD_TAGS_ALL) {
+ /*
+ * If tagopt is --tags, always request tags
+ * in addition to the remote's refspecs
+ */
+ if (git_refspec_src_matches(tagspec, head->name))
+ match = 1;
+ }
+
+ if (!match && git_remote__matching_refspec(remote, head->name))
+ match = 1;
+
+ if (!match)
+ return 0;
+
+ return git_vector_insert(&remote->refs, head);
+}
+
+static int mark_local(git_remote *remote)
+{
+ git_remote_head *head;
+ git_odb *odb;
+ size_t i;
+
+ if (git_repository_odb__weakptr(&odb, remote->repo) < 0)
+ return -1;
+
+ git_vector_foreach(&remote->refs, i, head) {
+ /* If we have the object, mark it so we don't ask for it.
+ However if we are unshallowing, we need to ask for it
+ even though the head exists locally. */
+ if (remote->nego.depth != INT_MAX && git_odb_exists(odb, &head->oid))
+ head->local = 1;
+ else
+ remote->need_pack = 1;
+ }
+
+ return 0;
+}
+
+static int maybe_want_oid(git_remote *remote, git_refspec *spec)
+{
+ git_remote_head *oid_head;
+
+ oid_head = git__calloc(1, sizeof(git_remote_head));
+ GIT_ERROR_CHECK_ALLOC(oid_head);
+
+ git_oid__fromstr(&oid_head->oid, spec->src, remote->repo->oid_type);
+
+ if (spec->dst) {
+ oid_head->name = git__strdup(spec->dst);
+ GIT_ERROR_CHECK_ALLOC(oid_head->name);
+ }
+
+ if (git_vector_insert(&remote->local_heads, oid_head) < 0 ||
+ git_vector_insert(&remote->refs, oid_head) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int filter_wants(git_remote *remote, const git_fetch_options *opts)
+{
+ git_remote_head **heads;
+ git_refspec tagspec, head, *spec;
+ int error = 0;
+ size_t i, heads_len;
+ unsigned int remote_caps;
+ unsigned int oid_mask = GIT_REMOTE_CAPABILITY_TIP_OID |
+ GIT_REMOTE_CAPABILITY_REACHABLE_OID;
+ git_remote_autotag_option_t tagopt = remote->download_tags;
+
+ if (opts && opts->download_tags != GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED)
+ tagopt = opts->download_tags;
+
+ git_vector_clear(&remote->refs);
+ if ((error = git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true)) < 0)
+ return error;
+
+ /*
+ * The fetch refspec can be NULL, and what this means is that the
+ * user didn't specify one. This is fine, as it means that we're
+ * not interested in any particular branch but just the remote's
+ * HEAD, which will be stored in FETCH_HEAD after the fetch.
+ */
+ if (remote->active_refspecs.length == 0) {
+ if ((error = git_refspec__parse(&head, "HEAD", true)) < 0)
+ goto cleanup;
+
+ error = git_refspec__dwim_one(&remote->active_refspecs, &head, &remote->refs);
+ git_refspec__dispose(&head);
+
+ if (error < 0)
+ goto cleanup;
+ }
+
+ if ((error = git_remote_ls((const git_remote_head ***)&heads, &heads_len, remote)) < 0 ||
+ (error = git_remote_capabilities(&remote_caps, remote)) < 0)
+ goto cleanup;
+
+ /* Handle remote heads */
+ for (i = 0; i < heads_len; i++) {
+ if ((error = maybe_want(remote, heads[i], &tagspec, tagopt)) < 0)
+ goto cleanup;
+ }
+
+ /* Handle explicitly specified OID specs */
+ git_vector_foreach(&remote->active_refspecs, i, spec) {
+ if (!git_oid__is_hexstr(spec->src, remote->repo->oid_type))
+ continue;
+
+ if (!(remote_caps & oid_mask)) {
+ git_error_set(GIT_ERROR_INVALID, "cannot fetch a specific object from the remote repository");
+ error = -1;
+ goto cleanup;
+ }
+
+ if ((error = maybe_want_oid(remote, spec)) < 0)
+ goto cleanup;
+ }
+
+ error = mark_local(remote);
+
+cleanup:
+ git_refspec__dispose(&tagspec);
+
+ return error;
+}
+
+/*
+ * In this first version, we push all our refs in and start sending
+ * them out. When we get an ACK we hide that commit and continue
+ * traversing until we're done
+ */
+int git_fetch_negotiate(git_remote *remote, const git_fetch_options *opts)
+{
+ git_transport *t = remote->transport;
+ int error;
+
+ remote->need_pack = 0;
+
+ if (opts) {
+ GIT_ASSERT_ARG(opts->depth >= 0);
+ remote->nego.depth = opts->depth;
+ }
+
+ if (filter_wants(remote, opts) < 0)
+ return -1;
+
+ /* Don't try to negotiate when we don't want anything */
+ if (!remote->need_pack)
+ return 0;
+
+ /*
+ * Now we have everything set up so we can start tell the
+ * server what we want and what we have.
+ */
+ remote->nego.refs = (const git_remote_head * const *)remote->refs.contents;
+ remote->nego.refs_len = remote->refs.length;
+
+ if (git_repository__shallow_roots(&remote->nego.shallow_roots,
+ &remote->nego.shallow_roots_len,
+ remote->repo) < 0)
+ return -1;
+
+ error = t->negotiate_fetch(t,
+ remote->repo,
+ &remote->nego);
+
+ git__free(remote->nego.shallow_roots);
+
+ return error;
+}
+
+int git_fetch_download_pack(git_remote *remote)
+{
+ git_oidarray shallow_roots = { NULL };
+ git_transport *t = remote->transport;
+ int error;
+
+ if (!remote->need_pack)
+ return 0;
+
+ if ((error = t->download_pack(t, remote->repo, &remote->stats)) != 0 ||
+ (error = t->shallow_roots(&shallow_roots, t)) != 0)
+ return error;
+
+ error = git_repository__shallow_roots_write(remote->repo, &shallow_roots);
+
+ git_oidarray_dispose(&shallow_roots);
+ return error;
+}
+
+int git_fetch_options_init(git_fetch_options *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_fetch_options, GIT_FETCH_OPTIONS_INIT);
+ return 0;
+}
+
+#ifndef GIT_DEPRECATE_HARD
+int git_fetch_init_options(git_fetch_options *opts, unsigned int version)
+{
+ return git_fetch_options_init(opts, version);
+}
+#endif
diff --git a/src/libgit2/fetch.h b/src/libgit2/fetch.h
new file mode 100644
index 0000000..493366d
--- /dev/null
+++ b/src/libgit2/fetch.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_fetch_h__
+#define INCLUDE_fetch_h__
+
+#include "common.h"
+
+#include "git2/remote.h"
+
+int git_fetch_negotiate(git_remote *remote, const git_fetch_options *opts);
+
+int git_fetch_download_pack(git_remote *remote);
+
+int git_fetch_setup_walk(git_revwalk **out, git_repository *repo);
+
+#endif
diff --git a/src/libgit2/fetchhead.c b/src/libgit2/fetchhead.c
new file mode 100644
index 0000000..2f276e5
--- /dev/null
+++ b/src/libgit2/fetchhead.c
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "fetchhead.h"
+
+#include "git2/types.h"
+#include "git2/oid.h"
+
+#include "str.h"
+#include "futils.h"
+#include "filebuf.h"
+#include "refs.h"
+#include "net.h"
+#include "repository.h"
+
+int git_fetchhead_ref_cmp(const void *a, const void *b)
+{
+ const git_fetchhead_ref *one = (const git_fetchhead_ref *)a;
+ const git_fetchhead_ref *two = (const git_fetchhead_ref *)b;
+
+ if (one->is_merge && !two->is_merge)
+ return -1;
+ if (two->is_merge && !one->is_merge)
+ return 1;
+
+ if (one->ref_name && two->ref_name)
+ return strcmp(one->ref_name, two->ref_name);
+ else if (one->ref_name)
+ return -1;
+ else if (two->ref_name)
+ return 1;
+
+ return 0;
+}
+
+static char *sanitized_remote_url(const char *remote_url)
+{
+ git_net_url url = GIT_NET_URL_INIT;
+ char *sanitized = NULL;
+ int error;
+
+ if (git_net_url_parse(&url, remote_url) == 0) {
+ git_str buf = GIT_STR_INIT;
+
+ git__free(url.username);
+ git__free(url.password);
+ url.username = url.password = NULL;
+
+ if ((error = git_net_url_fmt(&buf, &url)) < 0)
+ goto fallback;
+
+ sanitized = git_str_detach(&buf);
+ }
+
+fallback:
+ if (!sanitized)
+ sanitized = git__strdup(remote_url);
+
+ git_net_url_dispose(&url);
+ return sanitized;
+}
+
+int git_fetchhead_ref_create(
+ git_fetchhead_ref **out,
+ git_oid *oid,
+ unsigned int is_merge,
+ const char *ref_name,
+ const char *remote_url)
+{
+ git_fetchhead_ref *fetchhead_ref;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(oid);
+
+ *out = NULL;
+
+ fetchhead_ref = git__malloc(sizeof(git_fetchhead_ref));
+ GIT_ERROR_CHECK_ALLOC(fetchhead_ref);
+
+ memset(fetchhead_ref, 0x0, sizeof(git_fetchhead_ref));
+
+ git_oid_cpy(&fetchhead_ref->oid, oid);
+ fetchhead_ref->is_merge = is_merge;
+
+ if (ref_name) {
+ fetchhead_ref->ref_name = git__strdup(ref_name);
+ GIT_ERROR_CHECK_ALLOC(fetchhead_ref->ref_name);
+ }
+
+ if (remote_url) {
+ fetchhead_ref->remote_url = sanitized_remote_url(remote_url);
+ GIT_ERROR_CHECK_ALLOC(fetchhead_ref->remote_url);
+ }
+
+ *out = fetchhead_ref;
+
+ return 0;
+}
+
+static int fetchhead_ref_write(
+ git_filebuf *file,
+ git_fetchhead_ref *fetchhead_ref)
+{
+ char oid[GIT_OID_MAX_HEXSIZE + 1];
+ const char *type, *name;
+ int head = 0;
+
+ GIT_ASSERT_ARG(file);
+ GIT_ASSERT_ARG(fetchhead_ref);
+
+ git_oid_tostr(oid, GIT_OID_MAX_HEXSIZE + 1, &fetchhead_ref->oid);
+
+ if (git__prefixcmp(fetchhead_ref->ref_name, GIT_REFS_HEADS_DIR) == 0) {
+ type = "branch ";
+ name = fetchhead_ref->ref_name + strlen(GIT_REFS_HEADS_DIR);
+ } else if(git__prefixcmp(fetchhead_ref->ref_name,
+ GIT_REFS_TAGS_DIR) == 0) {
+ type = "tag ";
+ name = fetchhead_ref->ref_name + strlen(GIT_REFS_TAGS_DIR);
+ } else if (!git__strcmp(fetchhead_ref->ref_name, GIT_HEAD_FILE)) {
+ head = 1;
+ } else {
+ type = "";
+ name = fetchhead_ref->ref_name;
+ }
+
+ if (head)
+ return git_filebuf_printf(file, "%s\t\t%s\n", oid, fetchhead_ref->remote_url);
+
+ return git_filebuf_printf(file, "%s\t%s\t%s'%s' of %s\n",
+ oid,
+ (fetchhead_ref->is_merge) ? "" : "not-for-merge",
+ type,
+ name,
+ fetchhead_ref->remote_url);
+}
+
+int git_fetchhead_write(git_repository *repo, git_vector *fetchhead_refs)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ git_str path = GIT_STR_INIT;
+ unsigned int i;
+ git_fetchhead_ref *fetchhead_ref;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(fetchhead_refs);
+
+ if (git_str_joinpath(&path, repo->gitdir, GIT_FETCH_HEAD_FILE) < 0)
+ return -1;
+
+ if (git_filebuf_open(&file, path.ptr, GIT_FILEBUF_APPEND, GIT_REFS_FILE_MODE) < 0) {
+ git_str_dispose(&path);
+ return -1;
+ }
+
+ git_str_dispose(&path);
+
+ git_vector_sort(fetchhead_refs);
+
+ git_vector_foreach(fetchhead_refs, i, fetchhead_ref)
+ fetchhead_ref_write(&file, fetchhead_ref);
+
+ return git_filebuf_commit(&file);
+}
+
+static int fetchhead_ref_parse(
+ git_oid *oid,
+ unsigned int *is_merge,
+ git_str *ref_name,
+ const char **remote_url,
+ char *line,
+ size_t line_num,
+ git_oid_t oid_type)
+{
+ char *oid_str, *is_merge_str, *desc, *name = NULL;
+ const char *type = NULL;
+ int error = 0;
+
+ *remote_url = NULL;
+
+ if (!*line) {
+ git_error_set(GIT_ERROR_FETCHHEAD,
+ "empty line in FETCH_HEAD line %"PRIuZ, line_num);
+ return -1;
+ }
+
+ /* Compat with old git clients that wrote FETCH_HEAD like a loose ref. */
+ if ((oid_str = git__strsep(&line, "\t")) == NULL) {
+ oid_str = line;
+ line += strlen(line);
+
+ *is_merge = 1;
+ }
+
+ if (strlen(oid_str) != git_oid_hexsize(oid_type)) {
+ git_error_set(GIT_ERROR_FETCHHEAD,
+ "invalid object ID in FETCH_HEAD line %"PRIuZ, line_num);
+ return -1;
+ }
+
+ if (git_oid__fromstr(oid, oid_str, oid_type) < 0) {
+ const git_error *oid_err = git_error_last();
+ const char *err_msg = oid_err ? oid_err->message : "invalid object ID";
+
+ git_error_set(GIT_ERROR_FETCHHEAD, "%s in FETCH_HEAD line %"PRIuZ,
+ err_msg, line_num);
+ return -1;
+ }
+
+ /* Parse new data from newer git clients */
+ if (*line) {
+ if ((is_merge_str = git__strsep(&line, "\t")) == NULL) {
+ git_error_set(GIT_ERROR_FETCHHEAD,
+ "invalid description data in FETCH_HEAD line %"PRIuZ, line_num);
+ return -1;
+ }
+
+ if (*is_merge_str == '\0')
+ *is_merge = 1;
+ else if (strcmp(is_merge_str, "not-for-merge") == 0)
+ *is_merge = 0;
+ else {
+ git_error_set(GIT_ERROR_FETCHHEAD,
+ "invalid for-merge entry in FETCH_HEAD line %"PRIuZ, line_num);
+ return -1;
+ }
+
+ if ((desc = line) == NULL) {
+ git_error_set(GIT_ERROR_FETCHHEAD,
+ "invalid description in FETCH_HEAD line %"PRIuZ, line_num);
+ return -1;
+ }
+
+ if (git__prefixcmp(desc, "branch '") == 0) {
+ type = GIT_REFS_HEADS_DIR;
+ name = desc + 8;
+ } else if (git__prefixcmp(desc, "tag '") == 0) {
+ type = GIT_REFS_TAGS_DIR;
+ name = desc + 5;
+ } else if (git__prefixcmp(desc, "'") == 0)
+ name = desc + 1;
+
+ if (name) {
+ if ((desc = strstr(name, "' ")) == NULL ||
+ git__prefixcmp(desc, "' of ") != 0) {
+ git_error_set(GIT_ERROR_FETCHHEAD,
+ "invalid description in FETCH_HEAD line %"PRIuZ, line_num);
+ return -1;
+ }
+
+ *desc = '\0';
+ desc += 5;
+ }
+
+ *remote_url = desc;
+ }
+
+ git_str_clear(ref_name);
+
+ if (type)
+ git_str_join(ref_name, '/', type, name);
+ else if(name)
+ git_str_puts(ref_name, name);
+
+ return error;
+}
+
+int git_repository_fetchhead_foreach(
+ git_repository *repo,
+ git_repository_fetchhead_foreach_cb cb,
+ void *payload)
+{
+ git_str path = GIT_STR_INIT, file = GIT_STR_INIT, name = GIT_STR_INIT;
+ const char *ref_name;
+ git_oid oid;
+ const char *remote_url;
+ unsigned int is_merge = 0;
+ char *buffer, *line;
+ size_t line_num = 0;
+ int error = 0;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(cb);
+
+ if (git_str_joinpath(&path, repo->gitdir, GIT_FETCH_HEAD_FILE) < 0)
+ return -1;
+
+ if ((error = git_futils_readbuffer(&file, git_str_cstr(&path))) < 0)
+ goto done;
+
+ buffer = file.ptr;
+
+ while ((line = git__strsep(&buffer, "\n")) != NULL) {
+ ++line_num;
+
+ if ((error = fetchhead_ref_parse(&oid, &is_merge, &name,
+ &remote_url, line, line_num,
+ repo->oid_type)) < 0)
+ goto done;
+
+ if (git_str_len(&name) > 0)
+ ref_name = git_str_cstr(&name);
+ else
+ ref_name = NULL;
+
+ error = cb(ref_name, remote_url, &oid, is_merge, payload);
+ if (error) {
+ git_error_set_after_callback(error);
+ goto done;
+ }
+ }
+
+ if (*buffer) {
+ git_error_set(GIT_ERROR_FETCHHEAD, "no EOL at line %"PRIuZ, line_num+1);
+ error = -1;
+ goto done;
+ }
+
+done:
+ git_str_dispose(&file);
+ git_str_dispose(&path);
+ git_str_dispose(&name);
+
+ return error;
+}
+
+void git_fetchhead_ref_free(git_fetchhead_ref *fetchhead_ref)
+{
+ if (fetchhead_ref == NULL)
+ return;
+
+ git__free(fetchhead_ref->remote_url);
+ git__free(fetchhead_ref->ref_name);
+ git__free(fetchhead_ref);
+}
+
diff --git a/src/libgit2/fetchhead.h b/src/libgit2/fetchhead.h
new file mode 100644
index 0000000..9e51710
--- /dev/null
+++ b/src/libgit2/fetchhead.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_fetchhead_h__
+#define INCLUDE_fetchhead_h__
+
+#include "common.h"
+
+#include "oid.h"
+#include "vector.h"
+
+typedef struct git_fetchhead_ref {
+ git_oid oid;
+ unsigned int is_merge;
+ char *ref_name;
+ char *remote_url;
+} git_fetchhead_ref;
+
+int git_fetchhead_ref_create(
+ git_fetchhead_ref **fetchhead_ref_out,
+ git_oid *oid,
+ unsigned int is_merge,
+ const char *ref_name,
+ const char *remote_url);
+
+int git_fetchhead_ref_cmp(const void *a, const void *b);
+
+int git_fetchhead_write(git_repository *repo, git_vector *fetchhead_refs);
+
+void git_fetchhead_ref_free(git_fetchhead_ref *fetchhead_ref);
+
+#endif
diff --git a/src/libgit2/filter.c b/src/libgit2/filter.c
new file mode 100644
index 0000000..80a3cae
--- /dev/null
+++ b/src/libgit2/filter.c
@@ -0,0 +1,1221 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "filter.h"
+
+#include "buf.h"
+#include "common.h"
+#include "futils.h"
+#include "hash.h"
+#include "repository.h"
+#include "runtime.h"
+#include "git2/sys/filter.h"
+#include "git2/config.h"
+#include "blob.h"
+#include "attr_file.h"
+#include "array.h"
+#include "path.h"
+
+struct git_filter_source {
+ git_repository *repo;
+ const char *path;
+ git_oid oid; /* zero if unknown (which is likely) */
+ uint16_t filemode; /* zero if unknown */
+ git_filter_mode_t mode;
+ git_filter_options options;
+};
+
+typedef struct {
+ const char *filter_name;
+ git_filter *filter;
+ void *payload;
+} git_filter_entry;
+
+struct git_filter_list {
+ git_array_t(git_filter_entry) filters;
+ git_filter_source source;
+ git_str *temp_buf;
+ char path[GIT_FLEX_ARRAY];
+};
+
+typedef struct {
+ char *filter_name;
+ git_filter *filter;
+ int priority;
+ int initialized;
+ size_t nattrs, nmatches;
+ char *attrdata;
+ const char *attrs[GIT_FLEX_ARRAY];
+} git_filter_def;
+
+static int filter_def_priority_cmp(const void *a, const void *b)
+{
+ int pa = ((const git_filter_def *)a)->priority;
+ int pb = ((const git_filter_def *)b)->priority;
+ return (pa < pb) ? -1 : (pa > pb) ? 1 : 0;
+}
+
+struct git_filter_registry {
+ git_rwlock lock;
+ git_vector filters;
+};
+
+static struct git_filter_registry filter_registry;
+
+static void git_filter_global_shutdown(void);
+
+
+static int filter_def_scan_attrs(
+ git_str *attrs, size_t *nattr, size_t *nmatch, const char *attr_str)
+{
+ const char *start, *scan = attr_str;
+ int has_eq;
+
+ *nattr = *nmatch = 0;
+
+ if (!scan)
+ return 0;
+
+ while (*scan) {
+ while (git__isspace(*scan)) scan++;
+
+ for (start = scan, has_eq = 0; *scan && !git__isspace(*scan); ++scan) {
+ if (*scan == '=')
+ has_eq = 1;
+ }
+
+ if (scan > start) {
+ (*nattr)++;
+ if (has_eq || *start == '-' || *start == '+' || *start == '!')
+ (*nmatch)++;
+
+ if (has_eq)
+ git_str_putc(attrs, '=');
+ git_str_put(attrs, start, scan - start);
+ git_str_putc(attrs, '\0');
+ }
+ }
+
+ return 0;
+}
+
+static void filter_def_set_attrs(git_filter_def *fdef)
+{
+ char *scan = fdef->attrdata;
+ size_t i;
+
+ for (i = 0; i < fdef->nattrs; ++i) {
+ const char *name, *value;
+
+ switch (*scan) {
+ case '=':
+ name = scan + 1;
+ for (scan++; *scan != '='; scan++) /* find '=' */;
+ *scan++ = '\0';
+ value = scan;
+ break;
+ case '-':
+ name = scan + 1; value = git_attr__false; break;
+ case '+':
+ name = scan + 1; value = git_attr__true; break;
+ case '!':
+ name = scan + 1; value = git_attr__unset; break;
+ default:
+ name = scan; value = NULL; break;
+ }
+
+ fdef->attrs[i] = name;
+ fdef->attrs[i + fdef->nattrs] = value;
+
+ scan += strlen(scan) + 1;
+ }
+}
+
+static int filter_def_name_key_check(const void *key, const void *fdef)
+{
+ const char *name =
+ fdef ? ((const git_filter_def *)fdef)->filter_name : NULL;
+ return name ? git__strcmp(key, name) : -1;
+}
+
+static int filter_def_filter_key_check(const void *key, const void *fdef)
+{
+ const void *filter = fdef ? ((const git_filter_def *)fdef)->filter : NULL;
+ return (key == filter) ? 0 : -1;
+}
+
+/* Note: callers must lock the registry before calling this function */
+static int filter_registry_insert(
+ const char *name, git_filter *filter, int priority)
+{
+ git_filter_def *fdef;
+ size_t nattr = 0, nmatch = 0, alloc_len;
+ git_str attrs = GIT_STR_INIT;
+
+ if (filter_def_scan_attrs(&attrs, &nattr, &nmatch, filter->attributes) < 0)
+ return -1;
+
+ GIT_ERROR_CHECK_ALLOC_MULTIPLY(&alloc_len, nattr, 2);
+ GIT_ERROR_CHECK_ALLOC_MULTIPLY(&alloc_len, alloc_len, sizeof(char *));
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, sizeof(git_filter_def));
+
+ fdef = git__calloc(1, alloc_len);
+ GIT_ERROR_CHECK_ALLOC(fdef);
+
+ fdef->filter_name = git__strdup(name);
+ GIT_ERROR_CHECK_ALLOC(fdef->filter_name);
+
+ fdef->filter = filter;
+ fdef->priority = priority;
+ fdef->nattrs = nattr;
+ fdef->nmatches = nmatch;
+ fdef->attrdata = git_str_detach(&attrs);
+
+ filter_def_set_attrs(fdef);
+
+ if (git_vector_insert(&filter_registry.filters, fdef) < 0) {
+ git__free(fdef->filter_name);
+ git__free(fdef->attrdata);
+ git__free(fdef);
+ return -1;
+ }
+
+ git_vector_sort(&filter_registry.filters);
+ return 0;
+}
+
+int git_filter_global_init(void)
+{
+ git_filter *crlf = NULL, *ident = NULL;
+ int error = 0;
+
+ if (git_rwlock_init(&filter_registry.lock) < 0)
+ return -1;
+
+ if ((error = git_vector_init(&filter_registry.filters, 2,
+ filter_def_priority_cmp)) < 0)
+ goto done;
+
+ if ((crlf = git_crlf_filter_new()) == NULL ||
+ filter_registry_insert(
+ GIT_FILTER_CRLF, crlf, GIT_FILTER_CRLF_PRIORITY) < 0 ||
+ (ident = git_ident_filter_new()) == NULL ||
+ filter_registry_insert(
+ GIT_FILTER_IDENT, ident, GIT_FILTER_IDENT_PRIORITY) < 0)
+ error = -1;
+
+ if (!error)
+ error = git_runtime_shutdown_register(git_filter_global_shutdown);
+
+done:
+ if (error) {
+ git_filter_free(crlf);
+ git_filter_free(ident);
+ }
+
+ return error;
+}
+
+static void git_filter_global_shutdown(void)
+{
+ size_t pos;
+ git_filter_def *fdef;
+
+ if (git_rwlock_wrlock(&filter_registry.lock) < 0)
+ return;
+
+ git_vector_foreach(&filter_registry.filters, pos, fdef) {
+ if (fdef->filter && fdef->filter->shutdown) {
+ fdef->filter->shutdown(fdef->filter);
+ fdef->initialized = false;
+ }
+
+ git__free(fdef->filter_name);
+ git__free(fdef->attrdata);
+ git__free(fdef);
+ }
+
+ git_vector_free(&filter_registry.filters);
+
+ git_rwlock_wrunlock(&filter_registry.lock);
+ git_rwlock_free(&filter_registry.lock);
+}
+
+/* Note: callers must lock the registry before calling this function */
+static int filter_registry_find(size_t *pos, const char *name)
+{
+ return git_vector_search2(
+ pos, &filter_registry.filters, filter_def_name_key_check, name);
+}
+
+/* Note: callers must lock the registry before calling this function */
+static git_filter_def *filter_registry_lookup(size_t *pos, const char *name)
+{
+ git_filter_def *fdef = NULL;
+
+ if (!filter_registry_find(pos, name))
+ fdef = git_vector_get(&filter_registry.filters, *pos);
+
+ return fdef;
+}
+
+
+int git_filter_register(
+ const char *name, git_filter *filter, int priority)
+{
+ int error;
+
+ GIT_ASSERT_ARG(name);
+ GIT_ASSERT_ARG(filter);
+
+ if (git_rwlock_wrlock(&filter_registry.lock) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to lock filter registry");
+ return -1;
+ }
+
+ if (!filter_registry_find(NULL, name)) {
+ git_error_set(
+ GIT_ERROR_FILTER, "attempt to reregister existing filter '%s'", name);
+ error = GIT_EEXISTS;
+ goto done;
+ }
+
+ error = filter_registry_insert(name, filter, priority);
+
+done:
+ git_rwlock_wrunlock(&filter_registry.lock);
+ return error;
+}
+
+int git_filter_unregister(const char *name)
+{
+ size_t pos;
+ git_filter_def *fdef;
+ int error = 0;
+
+ GIT_ASSERT_ARG(name);
+
+ /* cannot unregister default filters */
+ if (!strcmp(GIT_FILTER_CRLF, name) || !strcmp(GIT_FILTER_IDENT, name)) {
+ git_error_set(GIT_ERROR_FILTER, "cannot unregister filter '%s'", name);
+ return -1;
+ }
+
+ if (git_rwlock_wrlock(&filter_registry.lock) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to lock filter registry");
+ return -1;
+ }
+
+ if ((fdef = filter_registry_lookup(&pos, name)) == NULL) {
+ git_error_set(GIT_ERROR_FILTER, "cannot find filter '%s' to unregister", name);
+ error = GIT_ENOTFOUND;
+ goto done;
+ }
+
+ git_vector_remove(&filter_registry.filters, pos);
+
+ if (fdef->initialized && fdef->filter && fdef->filter->shutdown) {
+ fdef->filter->shutdown(fdef->filter);
+ fdef->initialized = false;
+ }
+
+ git__free(fdef->filter_name);
+ git__free(fdef->attrdata);
+ git__free(fdef);
+
+done:
+ git_rwlock_wrunlock(&filter_registry.lock);
+ return error;
+}
+
+static int filter_initialize(git_filter_def *fdef)
+{
+ int error = 0;
+
+ if (!fdef->initialized && fdef->filter && fdef->filter->initialize) {
+ if ((error = fdef->filter->initialize(fdef->filter)) < 0)
+ return error;
+ }
+
+ fdef->initialized = true;
+ return 0;
+}
+
+git_filter *git_filter_lookup(const char *name)
+{
+ size_t pos;
+ git_filter_def *fdef;
+ git_filter *filter = NULL;
+
+ if (git_rwlock_rdlock(&filter_registry.lock) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to lock filter registry");
+ return NULL;
+ }
+
+ if ((fdef = filter_registry_lookup(&pos, name)) == NULL ||
+ (!fdef->initialized && filter_initialize(fdef) < 0))
+ goto done;
+
+ filter = fdef->filter;
+
+done:
+ git_rwlock_rdunlock(&filter_registry.lock);
+ return filter;
+}
+
+void git_filter_free(git_filter *filter)
+{
+ git__free(filter);
+}
+
+git_repository *git_filter_source_repo(const git_filter_source *src)
+{
+ return src->repo;
+}
+
+const char *git_filter_source_path(const git_filter_source *src)
+{
+ return src->path;
+}
+
+uint16_t git_filter_source_filemode(const git_filter_source *src)
+{
+ return src->filemode;
+}
+
+const git_oid *git_filter_source_id(const git_filter_source *src)
+{
+ return git_oid_is_zero(&src->oid) ? NULL : &src->oid;
+}
+
+git_filter_mode_t git_filter_source_mode(const git_filter_source *src)
+{
+ return src->mode;
+}
+
+uint32_t git_filter_source_flags(const git_filter_source *src)
+{
+ return src->options.flags;
+}
+
+static int filter_list_new(
+ git_filter_list **out, const git_filter_source *src)
+{
+ git_filter_list *fl = NULL;
+ size_t pathlen = src->path ? strlen(src->path) : 0, alloclen;
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_filter_list), pathlen);
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
+
+ fl = git__calloc(1, alloclen);
+ GIT_ERROR_CHECK_ALLOC(fl);
+
+ if (src->path)
+ memcpy(fl->path, src->path, pathlen);
+ fl->source.repo = src->repo;
+ fl->source.path = fl->path;
+ fl->source.mode = src->mode;
+
+ memcpy(&fl->source.options, &src->options, sizeof(git_filter_options));
+
+ *out = fl;
+ return 0;
+}
+
+static int filter_list_check_attributes(
+ const char ***out,
+ git_repository *repo,
+ git_filter_session *filter_session,
+ git_filter_def *fdef,
+ const git_filter_source *src)
+{
+ const char **strs = git__calloc(fdef->nattrs, sizeof(const char *));
+ git_attr_options attr_opts = GIT_ATTR_OPTIONS_INIT;
+ size_t i;
+ int error;
+
+ GIT_ERROR_CHECK_ALLOC(strs);
+
+ if ((src->options.flags & GIT_FILTER_NO_SYSTEM_ATTRIBUTES) != 0)
+ attr_opts.flags |= GIT_ATTR_CHECK_NO_SYSTEM;
+
+ if ((src->options.flags & GIT_FILTER_ATTRIBUTES_FROM_HEAD) != 0)
+ attr_opts.flags |= GIT_ATTR_CHECK_INCLUDE_HEAD;
+
+ if ((src->options.flags & GIT_FILTER_ATTRIBUTES_FROM_COMMIT) != 0) {
+ attr_opts.flags |= GIT_ATTR_CHECK_INCLUDE_COMMIT;
+
+#ifndef GIT_DEPRECATE_HARD
+ if (src->options.commit_id)
+ git_oid_cpy(&attr_opts.attr_commit_id, src->options.commit_id);
+ else
+#endif
+ git_oid_cpy(&attr_opts.attr_commit_id, &src->options.attr_commit_id);
+ }
+
+ error = git_attr_get_many_with_session(
+ strs, repo, filter_session->attr_session, &attr_opts, src->path, fdef->nattrs, fdef->attrs);
+
+ /* if no values were found but no matches are needed, it's okay! */
+ if (error == GIT_ENOTFOUND && !fdef->nmatches) {
+ git_error_clear();
+ git__free((void *)strs);
+ return 0;
+ }
+
+ for (i = 0; !error && i < fdef->nattrs; ++i) {
+ const char *want = fdef->attrs[fdef->nattrs + i];
+ git_attr_value_t want_type, found_type;
+
+ if (!want)
+ continue;
+
+ want_type = git_attr_value(want);
+ found_type = git_attr_value(strs[i]);
+
+ if (want_type != found_type)
+ error = GIT_ENOTFOUND;
+ else if (want_type == GIT_ATTR_VALUE_STRING &&
+ strcmp(want, strs[i]) &&
+ strcmp(want, "*"))
+ error = GIT_ENOTFOUND;
+ }
+
+ if (error)
+ git__free((void *)strs);
+ else
+ *out = strs;
+
+ return error;
+}
+
+int git_filter_list_new(
+ git_filter_list **out,
+ git_repository *repo,
+ git_filter_mode_t mode,
+ uint32_t flags)
+{
+ git_filter_source src = { 0 };
+ src.repo = repo;
+ src.path = NULL;
+ src.mode = mode;
+ src.options.flags = flags;
+ return filter_list_new(out, &src);
+}
+
+int git_filter_list__load(
+ git_filter_list **filters,
+ git_repository *repo,
+ git_blob *blob, /* can be NULL */
+ const char *path,
+ git_filter_mode_t mode,
+ git_filter_session *filter_session)
+{
+ int error = 0;
+ git_filter_list *fl = NULL;
+ git_filter_source src = { 0 };
+ git_filter_entry *fe;
+ size_t idx;
+ git_filter_def *fdef;
+
+ if (git_rwlock_rdlock(&filter_registry.lock) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to lock filter registry");
+ return -1;
+ }
+
+ src.repo = repo;
+ src.path = path;
+ src.mode = mode;
+
+ memcpy(&src.options, &filter_session->options, sizeof(git_filter_options));
+
+ if (blob)
+ git_oid_cpy(&src.oid, git_blob_id(blob));
+
+ git_vector_foreach(&filter_registry.filters, idx, fdef) {
+ const char **values = NULL;
+ void *payload = NULL;
+
+ if (!fdef || !fdef->filter)
+ continue;
+
+ if (fdef->nattrs > 0) {
+ error = filter_list_check_attributes(
+ &values, repo,
+ filter_session, fdef, &src);
+
+ if (error == GIT_ENOTFOUND) {
+ error = 0;
+ continue;
+ } else if (error < 0)
+ break;
+ }
+
+ if (!fdef->initialized && (error = filter_initialize(fdef)) < 0)
+ break;
+
+ if (fdef->filter->check)
+ error = fdef->filter->check(
+ fdef->filter, &payload, &src, values);
+
+ git__free((void *)values);
+
+ if (error == GIT_PASSTHROUGH)
+ error = 0;
+ else if (error < 0)
+ break;
+ else {
+ if (!fl) {
+ if ((error = filter_list_new(&fl, &src)) < 0)
+ break;
+
+ fl->temp_buf = filter_session->temp_buf;
+ }
+
+ fe = git_array_alloc(fl->filters);
+ GIT_ERROR_CHECK_ALLOC(fe);
+
+ fe->filter = fdef->filter;
+ fe->filter_name = fdef->filter_name;
+ fe->payload = payload;
+ }
+ }
+
+ git_rwlock_rdunlock(&filter_registry.lock);
+
+ if (error && fl != NULL) {
+ git_array_clear(fl->filters);
+ git__free(fl);
+ fl = NULL;
+ }
+
+ *filters = fl;
+ return error;
+}
+
+int git_filter_list_load_ext(
+ git_filter_list **filters,
+ git_repository *repo,
+ git_blob *blob, /* can be NULL */
+ const char *path,
+ git_filter_mode_t mode,
+ git_filter_options *opts)
+{
+ git_filter_session filter_session = GIT_FILTER_SESSION_INIT;
+
+ if (opts)
+ memcpy(&filter_session.options, opts, sizeof(git_filter_options));
+
+ return git_filter_list__load(
+ filters, repo, blob, path, mode, &filter_session);
+}
+
+int git_filter_list_load(
+ git_filter_list **filters,
+ git_repository *repo,
+ git_blob *blob, /* can be NULL */
+ const char *path,
+ git_filter_mode_t mode,
+ uint32_t flags)
+{
+ git_filter_session filter_session = GIT_FILTER_SESSION_INIT;
+
+ filter_session.options.flags = flags;
+
+ return git_filter_list__load(
+ filters, repo, blob, path, mode, &filter_session);
+}
+
+void git_filter_list_free(git_filter_list *fl)
+{
+ uint32_t i;
+
+ if (!fl)
+ return;
+
+ for (i = 0; i < git_array_size(fl->filters); ++i) {
+ git_filter_entry *fe = git_array_get(fl->filters, i);
+ if (fe->filter->cleanup)
+ fe->filter->cleanup(fe->filter, fe->payload);
+ }
+
+ git_array_clear(fl->filters);
+ git__free(fl);
+}
+
+int git_filter_list_contains(
+ git_filter_list *fl,
+ const char *name)
+{
+ size_t i;
+
+ GIT_ASSERT_ARG(name);
+
+ if (!fl)
+ return 0;
+
+ for (i = 0; i < fl->filters.size; i++) {
+ if (strcmp(fl->filters.ptr[i].filter_name, name) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+int git_filter_list_push(
+ git_filter_list *fl, git_filter *filter, void *payload)
+{
+ int error = 0;
+ size_t pos;
+ git_filter_def *fdef = NULL;
+ git_filter_entry *fe;
+
+ GIT_ASSERT_ARG(fl);
+ GIT_ASSERT_ARG(filter);
+
+ if (git_rwlock_rdlock(&filter_registry.lock) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to lock filter registry");
+ return -1;
+ }
+
+ if (git_vector_search2(
+ &pos, &filter_registry.filters,
+ filter_def_filter_key_check, filter) == 0)
+ fdef = git_vector_get(&filter_registry.filters, pos);
+
+ git_rwlock_rdunlock(&filter_registry.lock);
+
+ if (fdef == NULL) {
+ git_error_set(GIT_ERROR_FILTER, "cannot use an unregistered filter");
+ return -1;
+ }
+
+ if (!fdef->initialized && (error = filter_initialize(fdef)) < 0)
+ return error;
+
+ fe = git_array_alloc(fl->filters);
+ GIT_ERROR_CHECK_ALLOC(fe);
+ fe->filter = filter;
+ fe->payload = payload;
+
+ return 0;
+}
+
+size_t git_filter_list_length(const git_filter_list *fl)
+{
+ return fl ? git_array_size(fl->filters) : 0;
+}
+
+struct buf_stream {
+ git_writestream parent;
+ git_str *target;
+ bool complete;
+};
+
+static int buf_stream_write(
+ git_writestream *s, const char *buffer, size_t len)
+{
+ struct buf_stream *buf_stream = (struct buf_stream *)s;
+ GIT_ASSERT_ARG(buf_stream);
+ GIT_ASSERT(buf_stream->complete == 0);
+
+ return git_str_put(buf_stream->target, buffer, len);
+}
+
+static int buf_stream_close(git_writestream *s)
+{
+ struct buf_stream *buf_stream = (struct buf_stream *)s;
+ GIT_ASSERT_ARG(buf_stream);
+
+ GIT_ASSERT(buf_stream->complete == 0);
+ buf_stream->complete = 1;
+
+ return 0;
+}
+
+static void buf_stream_free(git_writestream *s)
+{
+ GIT_UNUSED(s);
+}
+
+static void buf_stream_init(struct buf_stream *writer, git_str *target)
+{
+ memset(writer, 0, sizeof(struct buf_stream));
+
+ writer->parent.write = buf_stream_write;
+ writer->parent.close = buf_stream_close;
+ writer->parent.free = buf_stream_free;
+ writer->target = target;
+
+ git_str_clear(target);
+}
+
+int git_filter_list_apply_to_buffer(
+ git_buf *out,
+ git_filter_list *filters,
+ const char *in,
+ size_t in_len)
+{
+ GIT_BUF_WRAP_PRIVATE(out, git_filter_list__apply_to_buffer, filters, in, in_len);
+}
+
+int git_filter_list__apply_to_buffer(
+ git_str *out,
+ git_filter_list *filters,
+ const char *in,
+ size_t in_len)
+{
+ struct buf_stream writer;
+ int error;
+
+ buf_stream_init(&writer, out);
+
+ if ((error = git_filter_list_stream_buffer(filters,
+ in, in_len, &writer.parent)) < 0)
+ return error;
+
+ GIT_ASSERT(writer.complete);
+ return error;
+}
+
+int git_filter_list__convert_buf(
+ git_str *out,
+ git_filter_list *filters,
+ git_str *in)
+{
+ int error;
+
+ if (!filters || git_filter_list_length(filters) == 0) {
+ git_str_swap(out, in);
+ git_str_dispose(in);
+ return 0;
+ }
+
+ error = git_filter_list__apply_to_buffer(out, filters,
+ in->ptr, in->size);
+
+ if (!error)
+ git_str_dispose(in);
+
+ return error;
+}
+
+int git_filter_list_apply_to_file(
+ git_buf *out,
+ git_filter_list *filters,
+ git_repository *repo,
+ const char *path)
+{
+ GIT_BUF_WRAP_PRIVATE(out, git_filter_list__apply_to_file, filters, repo, path);
+}
+
+int git_filter_list__apply_to_file(
+ git_str *out,
+ git_filter_list *filters,
+ git_repository *repo,
+ const char *path)
+{
+ struct buf_stream writer;
+ int error;
+
+ buf_stream_init(&writer, out);
+
+ if ((error = git_filter_list_stream_file(
+ filters, repo, path, &writer.parent)) < 0)
+ return error;
+
+ GIT_ASSERT(writer.complete);
+ return error;
+}
+
+static int buf_from_blob(git_str *out, git_blob *blob)
+{
+ git_object_size_t rawsize = git_blob_rawsize(blob);
+
+ if (!git__is_sizet(rawsize)) {
+ git_error_set(GIT_ERROR_OS, "blob is too large to filter");
+ return -1;
+ }
+
+ git_str_attach_notowned(out, git_blob_rawcontent(blob), (size_t)rawsize);
+ return 0;
+}
+
+int git_filter_list_apply_to_blob(
+ git_buf *out,
+ git_filter_list *filters,
+ git_blob *blob)
+{
+ GIT_BUF_WRAP_PRIVATE(out, git_filter_list__apply_to_blob, filters, blob);
+}
+
+int git_filter_list__apply_to_blob(
+ git_str *out,
+ git_filter_list *filters,
+ git_blob *blob)
+{
+ struct buf_stream writer;
+ int error;
+
+ buf_stream_init(&writer, out);
+
+ if ((error = git_filter_list_stream_blob(
+ filters, blob, &writer.parent)) < 0)
+ return error;
+
+ GIT_ASSERT(writer.complete);
+ return error;
+}
+
+struct buffered_stream {
+ git_writestream parent;
+ git_filter *filter;
+ int (*write_fn)(git_filter *, void **, git_str *, const git_str *, const git_filter_source *);
+ int (*legacy_write_fn)(git_filter *, void **, git_buf *, const git_buf *, const git_filter_source *);
+ const git_filter_source *source;
+ void **payload;
+ git_str input;
+ git_str temp_buf;
+ git_str *output;
+ git_writestream *target;
+};
+
+static int buffered_stream_write(
+ git_writestream *s, const char *buffer, size_t len)
+{
+ struct buffered_stream *buffered_stream = (struct buffered_stream *)s;
+ GIT_ASSERT_ARG(buffered_stream);
+
+ return git_str_put(&buffered_stream->input, buffer, len);
+}
+
+#ifndef GIT_DEPRECATE_HARD
+# define BUF_TO_STRUCT(b, s) \
+ (b)->ptr = (s)->ptr; \
+ (b)->size = (s)->size; \
+ (b)->reserved = (s)->asize;
+# define STRUCT_TO_BUF(s, b) \
+ (s)->ptr = (b)->ptr; \
+ (s)->size = (b)->size; \
+ (s)->asize = (b)->reserved;
+#endif
+
+static int buffered_stream_close(git_writestream *s)
+{
+ struct buffered_stream *buffered_stream = (struct buffered_stream *)s;
+ git_str *writebuf;
+ git_error_state error_state = {0};
+ int error;
+
+ GIT_ASSERT_ARG(buffered_stream);
+
+#ifndef GIT_DEPRECATE_HARD
+ if (buffered_stream->write_fn == NULL) {
+ git_buf legacy_output = GIT_BUF_INIT,
+ legacy_input = GIT_BUF_INIT;
+
+ BUF_TO_STRUCT(&legacy_output, buffered_stream->output);
+ BUF_TO_STRUCT(&legacy_input, &buffered_stream->input);
+
+ error = buffered_stream->legacy_write_fn(
+ buffered_stream->filter,
+ buffered_stream->payload,
+ &legacy_output,
+ &legacy_input,
+ buffered_stream->source);
+
+ STRUCT_TO_BUF(buffered_stream->output, &legacy_output);
+ STRUCT_TO_BUF(&buffered_stream->input, &legacy_input);
+ } else
+#endif
+ error = buffered_stream->write_fn(
+ buffered_stream->filter,
+ buffered_stream->payload,
+ buffered_stream->output,
+ &buffered_stream->input,
+ buffered_stream->source);
+
+ if (error == GIT_PASSTHROUGH) {
+ writebuf = &buffered_stream->input;
+ } else if (error == 0) {
+ writebuf = buffered_stream->output;
+ } else {
+ /* close stream before erroring out taking care
+ * to preserve the original error */
+ git_error_state_capture(&error_state, error);
+ buffered_stream->target->close(buffered_stream->target);
+ git_error_state_restore(&error_state);
+ return error;
+ }
+
+ if ((error = buffered_stream->target->write(
+ buffered_stream->target, writebuf->ptr, writebuf->size)) == 0)
+ error = buffered_stream->target->close(buffered_stream->target);
+
+ return error;
+}
+
+static void buffered_stream_free(git_writestream *s)
+{
+ struct buffered_stream *buffered_stream = (struct buffered_stream *)s;
+
+ if (buffered_stream) {
+ git_str_dispose(&buffered_stream->input);
+ git_str_dispose(&buffered_stream->temp_buf);
+ git__free(buffered_stream);
+ }
+}
+
+int git_filter_buffered_stream_new(
+ git_writestream **out,
+ git_filter *filter,
+ int (*write_fn)(git_filter *, void **, git_str *, const git_str *, const git_filter_source *),
+ git_str *temp_buf,
+ void **payload,
+ const git_filter_source *source,
+ git_writestream *target)
+{
+ struct buffered_stream *buffered_stream = git__calloc(1, sizeof(struct buffered_stream));
+ GIT_ERROR_CHECK_ALLOC(buffered_stream);
+
+ buffered_stream->parent.write = buffered_stream_write;
+ buffered_stream->parent.close = buffered_stream_close;
+ buffered_stream->parent.free = buffered_stream_free;
+ buffered_stream->filter = filter;
+ buffered_stream->write_fn = write_fn;
+ buffered_stream->output = temp_buf ? temp_buf : &buffered_stream->temp_buf;
+ buffered_stream->payload = payload;
+ buffered_stream->source = source;
+ buffered_stream->target = target;
+
+ if (temp_buf)
+ git_str_clear(temp_buf);
+
+ *out = (git_writestream *)buffered_stream;
+ return 0;
+}
+
+#ifndef GIT_DEPRECATE_HARD
+static int buffered_legacy_stream_new(
+ git_writestream **out,
+ git_filter *filter,
+ int (*legacy_write_fn)(git_filter *, void **, git_buf *, const git_buf *, const git_filter_source *),
+ git_str *temp_buf,
+ void **payload,
+ const git_filter_source *source,
+ git_writestream *target)
+{
+ struct buffered_stream *buffered_stream = git__calloc(1, sizeof(struct buffered_stream));
+ GIT_ERROR_CHECK_ALLOC(buffered_stream);
+
+ buffered_stream->parent.write = buffered_stream_write;
+ buffered_stream->parent.close = buffered_stream_close;
+ buffered_stream->parent.free = buffered_stream_free;
+ buffered_stream->filter = filter;
+ buffered_stream->legacy_write_fn = legacy_write_fn;
+ buffered_stream->output = temp_buf ? temp_buf : &buffered_stream->temp_buf;
+ buffered_stream->payload = payload;
+ buffered_stream->source = source;
+ buffered_stream->target = target;
+
+ if (temp_buf)
+ git_str_clear(temp_buf);
+
+ *out = (git_writestream *)buffered_stream;
+ return 0;
+}
+#endif
+
+static int setup_stream(
+ git_writestream **out,
+ git_filter_entry *fe,
+ git_filter_list *filters,
+ git_writestream *last_stream)
+{
+#ifndef GIT_DEPRECATE_HARD
+ GIT_ASSERT(fe->filter->stream || fe->filter->apply);
+
+ /*
+ * If necessary, create a stream that proxies the traditional
+ * application.
+ */
+ if (!fe->filter->stream) {
+ /* Create a stream that proxies the one-shot apply */
+ return buffered_legacy_stream_new(out,
+ fe->filter, fe->filter->apply, filters->temp_buf,
+ &fe->payload, &filters->source, last_stream);
+ }
+#endif
+
+ GIT_ASSERT(fe->filter->stream);
+ return fe->filter->stream(out, fe->filter,
+ &fe->payload, &filters->source, last_stream);
+}
+
+static int stream_list_init(
+ git_writestream **out,
+ git_vector *streams,
+ git_filter_list *filters,
+ git_writestream *target)
+{
+ git_writestream *last_stream = target;
+ size_t i;
+ int error = 0;
+
+ *out = NULL;
+
+ if (!filters) {
+ *out = target;
+ return 0;
+ }
+
+ /* Create filters last to first to get the chaining direction */
+ for (i = 0; i < git_array_size(filters->filters); ++i) {
+ size_t filter_idx = (filters->source.mode == GIT_FILTER_TO_WORKTREE) ?
+ git_array_size(filters->filters) - 1 - i : i;
+
+ git_filter_entry *fe = git_array_get(filters->filters, filter_idx);
+ git_writestream *filter_stream;
+
+ error = setup_stream(&filter_stream, fe, filters, last_stream);
+
+ if (error < 0)
+ goto out;
+
+ git_vector_insert(streams, filter_stream);
+ last_stream = filter_stream;
+ }
+
+out:
+ if (error)
+ last_stream->close(last_stream);
+ else
+ *out = last_stream;
+
+ return error;
+}
+
+static void filter_streams_free(git_vector *streams)
+{
+ git_writestream *stream;
+ size_t i;
+
+ git_vector_foreach(streams, i, stream)
+ stream->free(stream);
+ git_vector_free(streams);
+}
+
+int git_filter_list_stream_file(
+ git_filter_list *filters,
+ git_repository *repo,
+ const char *path,
+ git_writestream *target)
+{
+ char buf[GIT_BUFSIZE_FILTERIO];
+ git_str abspath = GIT_STR_INIT;
+ const char *base = repo ? git_repository_workdir(repo) : NULL;
+ git_vector filter_streams = GIT_VECTOR_INIT;
+ git_writestream *stream_start;
+ ssize_t readlen;
+ int fd = -1, error, initialized = 0;
+
+ if ((error = stream_list_init(
+ &stream_start, &filter_streams, filters, target)) < 0 ||
+ (error = git_fs_path_join_unrooted(&abspath, path, base, NULL)) < 0 ||
+ (error = git_path_validate_str_length(repo, &abspath)) < 0)
+ goto done;
+
+ initialized = 1;
+
+ if ((fd = git_futils_open_ro(abspath.ptr)) < 0) {
+ error = fd;
+ goto done;
+ }
+
+ while ((readlen = p_read(fd, buf, sizeof(buf))) > 0) {
+ if ((error = stream_start->write(stream_start, buf, readlen)) < 0)
+ goto done;
+ }
+
+ if (readlen < 0)
+ error = -1;
+
+done:
+ if (initialized)
+ error |= stream_start->close(stream_start);
+
+ if (fd >= 0)
+ p_close(fd);
+ filter_streams_free(&filter_streams);
+ git_str_dispose(&abspath);
+ return error;
+}
+
+int git_filter_list_stream_buffer(
+ git_filter_list *filters,
+ const char *buffer,
+ size_t len,
+ git_writestream *target)
+{
+ git_vector filter_streams = GIT_VECTOR_INIT;
+ git_writestream *stream_start;
+ int error, initialized = 0;
+
+ if ((error = stream_list_init(&stream_start, &filter_streams, filters, target)) < 0)
+ goto out;
+ initialized = 1;
+
+ if ((error = stream_start->write(stream_start, buffer, len)) < 0)
+ goto out;
+
+out:
+ if (initialized)
+ error |= stream_start->close(stream_start);
+
+ filter_streams_free(&filter_streams);
+ return error;
+}
+
+int git_filter_list_stream_blob(
+ git_filter_list *filters,
+ git_blob *blob,
+ git_writestream *target)
+{
+ git_str in = GIT_STR_INIT;
+
+ if (buf_from_blob(&in, blob) < 0)
+ return -1;
+
+ if (filters)
+ git_oid_cpy(&filters->source.oid, git_blob_id(blob));
+
+ return git_filter_list_stream_buffer(filters, in.ptr, in.size, target);
+}
+
+int git_filter_init(git_filter *filter, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(filter, version, git_filter, GIT_FILTER_INIT);
+ return 0;
+}
+
+#ifndef GIT_DEPRECATE_HARD
+
+int git_filter_list_stream_data(
+ git_filter_list *filters,
+ git_buf *data,
+ git_writestream *target)
+{
+ return git_filter_list_stream_buffer(filters, data->ptr, data->size, target);
+}
+
+int git_filter_list_apply_to_data(
+ git_buf *tgt, git_filter_list *filters, git_buf *src)
+{
+ return git_filter_list_apply_to_buffer(tgt, filters, src->ptr, src->size);
+}
+
+#endif
diff --git a/src/libgit2/filter.h b/src/libgit2/filter.h
new file mode 100644
index 0000000..58cb4b4
--- /dev/null
+++ b/src/libgit2/filter.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_filter_h__
+#define INCLUDE_filter_h__
+
+#include "common.h"
+
+#include "attr_file.h"
+#include "git2/filter.h"
+#include "git2/sys/filter.h"
+
+/* Amount of file to examine for NUL byte when checking binary-ness */
+#define GIT_FILTER_BYTES_TO_CHECK_NUL 8000
+
+typedef struct {
+ git_filter_options options;
+ git_attr_session *attr_session;
+ git_str *temp_buf;
+} git_filter_session;
+
+#define GIT_FILTER_SESSION_INIT {GIT_FILTER_OPTIONS_INIT, 0}
+
+extern int git_filter_global_init(void);
+
+extern void git_filter_free(git_filter *filter);
+
+extern int git_filter_list__load(
+ git_filter_list **filters,
+ git_repository *repo,
+ git_blob *blob, /* can be NULL */
+ const char *path,
+ git_filter_mode_t mode,
+ git_filter_session *filter_session);
+
+int git_filter_list__apply_to_buffer(
+ git_str *out,
+ git_filter_list *filters,
+ const char *in,
+ size_t in_len);
+int git_filter_list__apply_to_file(
+ git_str *out,
+ git_filter_list *filters,
+ git_repository *repo,
+ const char *path);
+int git_filter_list__apply_to_blob(
+ git_str *out,
+ git_filter_list *filters,
+ git_blob *blob);
+
+/*
+ * The given input buffer will be converted to the given output buffer.
+ * The input buffer will be freed (_if_ it was allocated).
+ */
+extern int git_filter_list__convert_buf(
+ git_str *out,
+ git_filter_list *filters,
+ git_str *in);
+
+extern int git_filter_list__apply_to_file(
+ git_str *out,
+ git_filter_list *filters,
+ git_repository *repo,
+ const char *path);
+
+/*
+ * Available filters
+ */
+
+extern git_filter *git_crlf_filter_new(void);
+extern git_filter *git_ident_filter_new(void);
+
+extern int git_filter_buffered_stream_new(
+ git_writestream **out,
+ git_filter *filter,
+ int (*write_fn)(git_filter *, void **, git_str *, const git_str *, const git_filter_source *),
+ git_str *temp_buf,
+ void **payload,
+ const git_filter_source *source,
+ git_writestream *target);
+
+#endif
diff --git a/src/libgit2/git2.rc b/src/libgit2/git2.rc
new file mode 100644
index 0000000..d273afd
--- /dev/null
+++ b/src/libgit2/git2.rc
@@ -0,0 +1,59 @@
+#include <winver.h>
+#include "../../include/git2/version.h"
+
+#ifndef LIBGIT2_FILENAME
+# ifdef __GNUC__
+# define LIBGIT2_FILENAME git2
+# else
+# define LIBGIT2_FILENAME "git2"
+# endif
+#endif
+
+#ifndef LIBGIT2_COMMENTS
+# define LIBGIT2_COMMENTS "For more information visit http://libgit2.github.com/"
+#endif
+
+#ifdef __GNUC__
+# define _STR(x) #x
+# define STR(x) _STR(x)
+#else
+# define STR(x) x
+#endif
+
+#ifdef __GNUC__
+VS_VERSION_INFO VERSIONINFO
+#else
+VS_VERSION_INFO VERSIONINFO MOVEABLE IMPURE LOADONCALL DISCARDABLE
+#endif
+ FILEVERSION LIBGIT2_VER_MAJOR,LIBGIT2_VER_MINOR,LIBGIT2_VER_REVISION,LIBGIT2_VER_PATCH
+ PRODUCTVERSION LIBGIT2_VER_MAJOR,LIBGIT2_VER_MINOR,LIBGIT2_VER_REVISION,LIBGIT2_VER_PATCH
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+#ifdef _DEBUG
+ FILEFLAGS VS_FF_DEBUG
+#else
+ FILEFLAGS 0
+#endif
+ FILEOS VOS_NT_WINDOWS32
+ FILETYPE VFT_DLL
+ FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904E4"
+ //language ID = U.S. English, char set = Windows, Multilingual
+ BEGIN
+ VALUE "FileDescription", "libgit2 - the Git linkable library\0"
+ VALUE "FileVersion", LIBGIT2_VERSION "\0"
+ VALUE "InternalName", STR(LIBGIT2_FILENAME) ".dll\0"
+ VALUE "LegalCopyright", "Copyright (C) the libgit2 contributors. All rights reserved.\0"
+ VALUE "OriginalFilename", STR(LIBGIT2_FILENAME) ".dll\0"
+ VALUE "ProductName", "libgit2\0"
+ VALUE "ProductVersion", LIBGIT2_VERSION "\0"
+ VALUE "Comments", LIBGIT2_COMMENTS "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x0409, 1252
+ END
+END
diff --git a/src/libgit2/grafts.c b/src/libgit2/grafts.c
new file mode 100644
index 0000000..1d9373a
--- /dev/null
+++ b/src/libgit2/grafts.c
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "grafts.h"
+
+#include "futils.h"
+#include "oid.h"
+#include "oidarray.h"
+#include "parse.h"
+
+struct git_grafts {
+ /* Map of `git_commit_graft`s */
+ git_oidmap *commits;
+
+ /* Type of object IDs */
+ git_oid_t oid_type;
+
+ /* File backing the graft. NULL if it's an in-memory graft */
+ char *path;
+ unsigned char path_checksum[GIT_HASH_SHA256_SIZE];
+};
+
+int git_grafts_new(git_grafts **out, git_oid_t oid_type)
+{
+ git_grafts *grafts;
+
+ GIT_ASSERT_ARG(out && oid_type);
+
+ grafts = git__calloc(1, sizeof(*grafts));
+ GIT_ERROR_CHECK_ALLOC(grafts);
+
+ if ((git_oidmap_new(&grafts->commits)) < 0) {
+ git__free(grafts);
+ return -1;
+ }
+
+ grafts->oid_type = oid_type;
+
+ *out = grafts;
+ return 0;
+}
+
+int git_grafts_open(
+ git_grafts **out,
+ const char *path,
+ git_oid_t oid_type)
+{
+ git_grafts *grafts = NULL;
+ int error;
+
+ GIT_ASSERT_ARG(out && path && oid_type);
+
+ if ((error = git_grafts_new(&grafts, oid_type)) < 0)
+ goto error;
+
+ grafts->path = git__strdup(path);
+ GIT_ERROR_CHECK_ALLOC(grafts->path);
+
+ if ((error = git_grafts_refresh(grafts)) < 0)
+ goto error;
+
+ *out = grafts;
+
+error:
+ if (error < 0)
+ git_grafts_free(grafts);
+
+ return error;
+}
+
+int git_grafts_open_or_refresh(
+ git_grafts **out,
+ const char *path,
+ git_oid_t oid_type)
+{
+ GIT_ASSERT_ARG(out && path && oid_type);
+
+ return *out ? git_grafts_refresh(*out) : git_grafts_open(out, path, oid_type);
+}
+
+void git_grafts_free(git_grafts *grafts)
+{
+ if (!grafts)
+ return;
+ git__free(grafts->path);
+ git_grafts_clear(grafts);
+ git_oidmap_free(grafts->commits);
+ git__free(grafts);
+}
+
+void git_grafts_clear(git_grafts *grafts)
+{
+ git_commit_graft *graft;
+
+ if (!grafts)
+ return;
+
+ git_oidmap_foreach_value(grafts->commits, graft, {
+ git__free(graft->parents.ptr);
+ git__free(graft);
+ });
+
+ git_oidmap_clear(grafts->commits);
+}
+
+int git_grafts_refresh(git_grafts *grafts)
+{
+ git_str contents = GIT_STR_INIT;
+ int error, updated = 0;
+
+ GIT_ASSERT_ARG(grafts);
+
+ if (!grafts->path)
+ return 0;
+
+ if ((error = git_futils_readbuffer_updated(&contents, grafts->path,
+ grafts->path_checksum, &updated)) < 0) {
+
+ if (error == GIT_ENOTFOUND) {
+ git_grafts_clear(grafts);
+ error = 0;
+ }
+
+ goto cleanup;
+ }
+
+ if (!updated) {
+ goto cleanup;
+ }
+
+ if ((error = git_grafts_parse(grafts, contents.ptr, contents.size)) < 0)
+ goto cleanup;
+
+cleanup:
+ git_str_dispose(&contents);
+ return error;
+}
+
+int git_grafts_parse(git_grafts *grafts, const char *buf, size_t len)
+{
+ git_array_oid_t parents = GIT_ARRAY_INIT;
+ git_parse_ctx parser;
+ int error;
+
+ git_grafts_clear(grafts);
+
+ if ((error = git_parse_ctx_init(&parser, buf, len)) < 0)
+ goto error;
+
+ for (; parser.remain_len; git_parse_advance_line(&parser)) {
+ git_oid graft_oid;
+
+ if ((error = git_parse_advance_oid(&graft_oid, &parser, grafts->oid_type)) < 0) {
+ git_error_set(GIT_ERROR_GRAFTS, "invalid graft OID at line %" PRIuZ, parser.line_num);
+ goto error;
+ }
+
+ while (parser.line_len && git_parse_advance_expected(&parser, "\n", 1) != 0) {
+ git_oid *id = git_array_alloc(parents);
+ GIT_ERROR_CHECK_ALLOC(id);
+
+ if ((error = git_parse_advance_expected(&parser, " ", 1)) < 0 ||
+ (error = git_parse_advance_oid(id, &parser, grafts->oid_type)) < 0) {
+ git_error_set(GIT_ERROR_GRAFTS, "invalid parent OID at line %" PRIuZ, parser.line_num);
+ goto error;
+ }
+ }
+
+ if ((error = git_grafts_add(grafts, &graft_oid, parents)) < 0)
+ goto error;
+
+ git_array_clear(parents);
+ }
+
+error:
+ git_array_clear(parents);
+ return error;
+}
+
+int git_grafts_add(git_grafts *grafts, const git_oid *oid, git_array_oid_t parents)
+{
+ git_commit_graft *graft;
+ git_oid *parent_oid;
+ int error;
+ size_t i;
+
+ GIT_ASSERT_ARG(grafts && oid);
+
+ graft = git__calloc(1, sizeof(*graft));
+ GIT_ERROR_CHECK_ALLOC(graft);
+
+ git_array_init_to_size(graft->parents, git_array_size(parents));
+ git_array_foreach(parents, i, parent_oid) {
+ git_oid *id = git_array_alloc(graft->parents);
+ GIT_ERROR_CHECK_ALLOC(id);
+
+ git_oid_cpy(id, parent_oid);
+ }
+ git_oid_cpy(&graft->oid, oid);
+
+ if ((error = git_grafts_remove(grafts, &graft->oid)) < 0 && error != GIT_ENOTFOUND)
+ goto cleanup;
+
+ if ((error = git_oidmap_set(grafts->commits, &graft->oid, graft)) < 0)
+ goto cleanup;
+
+ return 0;
+
+cleanup:
+ git_array_clear(graft->parents);
+ git__free(graft);
+ return error;
+}
+
+int git_grafts_remove(git_grafts *grafts, const git_oid *oid)
+{
+ git_commit_graft *graft;
+ int error;
+
+ GIT_ASSERT_ARG(grafts && oid);
+
+ if ((graft = git_oidmap_get(grafts->commits, oid)) == NULL)
+ return GIT_ENOTFOUND;
+
+ if ((error = git_oidmap_delete(grafts->commits, oid)) < 0)
+ return error;
+
+ git__free(graft->parents.ptr);
+ git__free(graft);
+
+ return 0;
+}
+
+int git_grafts_get(git_commit_graft **out, git_grafts *grafts, const git_oid *oid)
+{
+ GIT_ASSERT_ARG(out && grafts && oid);
+ if ((*out = git_oidmap_get(grafts->commits, oid)) == NULL)
+ return GIT_ENOTFOUND;
+ return 0;
+}
+
+int git_grafts_oids(git_oid **out, size_t *out_len, git_grafts *grafts)
+{
+ git_array_oid_t array = GIT_ARRAY_INIT;
+ const git_oid *oid;
+ size_t existing, i = 0;
+
+ GIT_ASSERT_ARG(out && grafts);
+
+ if ((existing = git_oidmap_size(grafts->commits)) > 0)
+ git_array_init_to_size(array, existing);
+
+ while (git_oidmap_iterate(NULL, grafts->commits, &i, &oid) == 0) {
+ git_oid *cpy = git_array_alloc(array);
+ GIT_ERROR_CHECK_ALLOC(cpy);
+ git_oid_cpy(cpy, oid);
+ }
+
+ *out = array.ptr;
+ *out_len = array.size;
+
+ return 0;
+}
+
+size_t git_grafts_size(git_grafts *grafts)
+{
+ return git_oidmap_size(grafts->commits);
+}
diff --git a/src/libgit2/grafts.h b/src/libgit2/grafts.h
new file mode 100644
index 0000000..394867f
--- /dev/null
+++ b/src/libgit2/grafts.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_graft_h__
+#define INCLUDE_graft_h__
+
+#include "common.h"
+#include "oidarray.h"
+#include "oidmap.h"
+
+/** graft commit */
+typedef struct {
+ git_oid oid;
+ git_array_oid_t parents;
+} git_commit_graft;
+
+typedef struct git_grafts git_grafts;
+
+int git_grafts_new(git_grafts **out, git_oid_t oid_type);
+int git_grafts_open(git_grafts **out, const char *path, git_oid_t oid_type);
+int git_grafts_open_or_refresh(git_grafts **out, const char *path, git_oid_t oid_type);
+void git_grafts_free(git_grafts *grafts);
+void git_grafts_clear(git_grafts *grafts);
+
+int git_grafts_refresh(git_grafts *grafts);
+int git_grafts_parse(git_grafts *grafts, const char *buf, size_t len);
+int git_grafts_add(git_grafts *grafts, const git_oid *oid, git_array_oid_t parents);
+int git_grafts_remove(git_grafts *grafts, const git_oid *oid);
+int git_grafts_get(git_commit_graft **out, git_grafts *grafts, const git_oid *oid);
+int git_grafts_oids(git_oid **out, size_t *out_len, git_grafts *grafts);
+size_t git_grafts_size(git_grafts *grafts);
+
+#endif
diff --git a/src/libgit2/graph.c b/src/libgit2/graph.c
new file mode 100644
index 0000000..35e914f
--- /dev/null
+++ b/src/libgit2/graph.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+
+#include "revwalk.h"
+#include "merge.h"
+#include "git2/graph.h"
+
+static int interesting(git_pqueue *list, git_commit_list *roots)
+{
+ unsigned int i;
+
+ for (i = 0; i < git_pqueue_size(list); i++) {
+ git_commit_list_node *commit = git_pqueue_get(list, i);
+ if ((commit->flags & STALE) == 0)
+ return 1;
+ }
+
+ while(roots) {
+ if ((roots->item->flags & STALE) == 0)
+ return 1;
+ roots = roots->next;
+ }
+
+ return 0;
+}
+
+static int mark_parents(git_revwalk *walk, git_commit_list_node *one,
+ git_commit_list_node *two)
+{
+ unsigned int i;
+ git_commit_list *roots = NULL;
+ git_pqueue list;
+
+ /* if the commit is repeated, we have a our merge base already */
+ if (one == two) {
+ one->flags |= PARENT1 | PARENT2 | RESULT;
+ return 0;
+ }
+
+ if (git_pqueue_init(&list, 0, 2, git_commit_list_generation_cmp) < 0)
+ return -1;
+
+ if (git_commit_list_parse(walk, one) < 0)
+ goto on_error;
+ one->flags |= PARENT1;
+ if (git_pqueue_insert(&list, one) < 0)
+ goto on_error;
+
+ if (git_commit_list_parse(walk, two) < 0)
+ goto on_error;
+ two->flags |= PARENT2;
+ if (git_pqueue_insert(&list, two) < 0)
+ goto on_error;
+
+ /* as long as there are non-STALE commits */
+ while (interesting(&list, roots)) {
+ git_commit_list_node *commit = git_pqueue_pop(&list);
+ unsigned int flags;
+
+ if (commit == NULL)
+ break;
+
+ flags = commit->flags & (PARENT1 | PARENT2 | STALE);
+ if (flags == (PARENT1 | PARENT2)) {
+ if (!(commit->flags & RESULT))
+ commit->flags |= RESULT;
+ /* we mark the parents of a merge stale */
+ flags |= STALE;
+ }
+
+ for (i = 0; i < commit->out_degree; i++) {
+ git_commit_list_node *p = commit->parents[i];
+ if ((p->flags & flags) == flags)
+ continue;
+
+ if (git_commit_list_parse(walk, p) < 0)
+ goto on_error;
+
+ p->flags |= flags;
+ if (git_pqueue_insert(&list, p) < 0)
+ goto on_error;
+ }
+
+ /* Keep track of root commits, to make sure the path gets marked */
+ if (commit->out_degree == 0) {
+ if (git_commit_list_insert(commit, &roots) == NULL)
+ goto on_error;
+ }
+ }
+
+ git_commit_list_free(&roots);
+ git_pqueue_free(&list);
+ return 0;
+
+on_error:
+ git_commit_list_free(&roots);
+ git_pqueue_free(&list);
+ return -1;
+}
+
+
+static int ahead_behind(git_commit_list_node *one, git_commit_list_node *two,
+ size_t *ahead, size_t *behind)
+{
+ git_commit_list_node *commit;
+ git_pqueue pq;
+ int error = 0, i;
+ *ahead = 0;
+ *behind = 0;
+
+ if (git_pqueue_init(&pq, 0, 2, git_commit_list_time_cmp) < 0)
+ return -1;
+
+ if ((error = git_pqueue_insert(&pq, one)) < 0 ||
+ (error = git_pqueue_insert(&pq, two)) < 0)
+ goto done;
+
+ while ((commit = git_pqueue_pop(&pq)) != NULL) {
+ if (commit->flags & RESULT ||
+ (commit->flags & (PARENT1 | PARENT2)) == (PARENT1 | PARENT2))
+ continue;
+ else if (commit->flags & PARENT1)
+ (*ahead)++;
+ else if (commit->flags & PARENT2)
+ (*behind)++;
+
+ for (i = 0; i < commit->out_degree; i++) {
+ git_commit_list_node *p = commit->parents[i];
+ if ((error = git_pqueue_insert(&pq, p)) < 0)
+ goto done;
+ }
+ commit->flags |= RESULT;
+ }
+
+done:
+ git_pqueue_free(&pq);
+ return error;
+}
+
+int git_graph_ahead_behind(size_t *ahead, size_t *behind, git_repository *repo,
+ const git_oid *local, const git_oid *upstream)
+{
+ git_revwalk *walk;
+ git_commit_list_node *commit_u, *commit_l;
+
+ if (git_revwalk_new(&walk, repo) < 0)
+ return -1;
+
+ commit_u = git_revwalk__commit_lookup(walk, upstream);
+ if (commit_u == NULL)
+ goto on_error;
+
+ commit_l = git_revwalk__commit_lookup(walk, local);
+ if (commit_l == NULL)
+ goto on_error;
+
+ if (mark_parents(walk, commit_l, commit_u) < 0)
+ goto on_error;
+ if (ahead_behind(commit_l, commit_u, ahead, behind) < 0)
+ goto on_error;
+
+ git_revwalk_free(walk);
+
+ return 0;
+
+on_error:
+ git_revwalk_free(walk);
+ return -1;
+}
+
+int git_graph_descendant_of(git_repository *repo, const git_oid *commit, const git_oid *ancestor)
+{
+ if (git_oid_equal(commit, ancestor))
+ return 0;
+
+ return git_graph_reachable_from_any(repo, ancestor, commit, 1);
+}
+
+int git_graph_reachable_from_any(
+ git_repository *repo,
+ const git_oid *commit_id,
+ const git_oid descendant_array[],
+ size_t length)
+{
+ git_revwalk *walk = NULL;
+ git_vector list;
+ git_commit_list *result = NULL;
+ git_commit_list_node *commit;
+ size_t i;
+ uint32_t minimum_generation = 0xffffffff;
+ int error = 0;
+
+ if (!length)
+ return 0;
+
+ for (i = 0; i < length; ++i) {
+ if (git_oid_equal(commit_id, &descendant_array[i]))
+ return 1;
+ }
+
+ if ((error = git_vector_init(&list, length + 1, NULL)) < 0)
+ return error;
+
+ if ((error = git_revwalk_new(&walk, repo)) < 0)
+ goto done;
+
+ for (i = 0; i < length; i++) {
+ commit = git_revwalk__commit_lookup(walk, &descendant_array[i]);
+ if (commit == NULL) {
+ error = -1;
+ goto done;
+ }
+
+ git_vector_insert(&list, commit);
+ if (minimum_generation > commit->generation)
+ minimum_generation = commit->generation;
+ }
+
+ commit = git_revwalk__commit_lookup(walk, commit_id);
+ if (commit == NULL) {
+ error = -1;
+ goto done;
+ }
+
+ if (minimum_generation > commit->generation)
+ minimum_generation = commit->generation;
+
+ if ((error = git_merge__bases_many(&result, walk, commit, &list, minimum_generation)) < 0)
+ goto done;
+
+ if (result) {
+ error = git_oid_equal(commit_id, &result->item->oid);
+ } else {
+ /* No merge-base found, it's not a descendant */
+ error = 0;
+ }
+
+done:
+ git_commit_list_free(&result);
+ git_vector_free(&list);
+ git_revwalk_free(walk);
+ return error;
+}
diff --git a/src/libgit2/hashsig.c b/src/libgit2/hashsig.c
new file mode 100644
index 0000000..6b4fb83
--- /dev/null
+++ b/src/libgit2/hashsig.c
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+
+#include "git2/sys/hashsig.h"
+#include "futils.h"
+#include "util.h"
+
+typedef uint32_t hashsig_t;
+typedef uint64_t hashsig_state;
+
+#define HASHSIG_SCALE 100
+
+#define HASHSIG_MAX_RUN 80
+#define HASHSIG_HASH_START INT64_C(0x012345678ABCDEF0)
+#define HASHSIG_HASH_SHIFT 5
+
+#define HASHSIG_HASH_MIX(S,CH) \
+ (S) = ((S) << HASHSIG_HASH_SHIFT) - (S) + (hashsig_state)(CH)
+
+#define HASHSIG_HEAP_SIZE ((1 << 7) - 1)
+#define HASHSIG_HEAP_MIN_SIZE 4
+
+typedef int (*hashsig_cmp)(const void *a, const void *b, void *);
+
+typedef struct {
+ int size, asize;
+ hashsig_cmp cmp;
+ hashsig_t values[HASHSIG_HEAP_SIZE];
+} hashsig_heap;
+
+struct git_hashsig {
+ hashsig_heap mins;
+ hashsig_heap maxs;
+ size_t lines;
+ git_hashsig_option_t opt;
+};
+
+#define HEAP_LCHILD_OF(I) (((I)<<1)+1)
+#define HEAP_RCHILD_OF(I) (((I)<<1)+2)
+#define HEAP_PARENT_OF(I) (((I)-1)>>1)
+
+static void hashsig_heap_init(hashsig_heap *h, hashsig_cmp cmp)
+{
+ h->size = 0;
+ h->asize = HASHSIG_HEAP_SIZE;
+ h->cmp = cmp;
+}
+
+static int hashsig_cmp_max(const void *a, const void *b, void *payload)
+{
+ hashsig_t av = *(const hashsig_t *)a, bv = *(const hashsig_t *)b;
+ GIT_UNUSED(payload);
+ return (av < bv) ? -1 : (av > bv) ? 1 : 0;
+}
+
+static int hashsig_cmp_min(const void *a, const void *b, void *payload)
+{
+ hashsig_t av = *(const hashsig_t *)a, bv = *(const hashsig_t *)b;
+ GIT_UNUSED(payload);
+ return (av > bv) ? -1 : (av < bv) ? 1 : 0;
+}
+
+static void hashsig_heap_up(hashsig_heap *h, int el)
+{
+ int parent_el = HEAP_PARENT_OF(el);
+
+ while (el > 0 && h->cmp(&h->values[parent_el], &h->values[el], NULL) > 0) {
+ hashsig_t t = h->values[el];
+ h->values[el] = h->values[parent_el];
+ h->values[parent_el] = t;
+
+ el = parent_el;
+ parent_el = HEAP_PARENT_OF(el);
+ }
+}
+
+static void hashsig_heap_down(hashsig_heap *h, int el)
+{
+ hashsig_t v, lv, rv;
+
+ /* 'el < h->size / 2' tests if el is bottom row of heap */
+
+ while (el < h->size / 2) {
+ int lel = HEAP_LCHILD_OF(el), rel = HEAP_RCHILD_OF(el), swapel;
+
+ v = h->values[el];
+ lv = h->values[lel];
+ rv = h->values[rel];
+
+ if (h->cmp(&v, &lv, NULL) < 0 && h->cmp(&v, &rv, NULL) < 0)
+ break;
+
+ swapel = (h->cmp(&lv, &rv, NULL) < 0) ? lel : rel;
+
+ h->values[el] = h->values[swapel];
+ h->values[swapel] = v;
+
+ el = swapel;
+ }
+}
+
+static void hashsig_heap_sort(hashsig_heap *h)
+{
+ /* only need to do this at the end for signature comparison */
+ git__qsort_r(h->values, h->size, sizeof(hashsig_t), h->cmp, NULL);
+}
+
+static void hashsig_heap_insert(hashsig_heap *h, hashsig_t val)
+{
+ /* if heap is not full, insert new element */
+ if (h->size < h->asize) {
+ h->values[h->size++] = val;
+ hashsig_heap_up(h, h->size - 1);
+ }
+
+ /* if heap is full, pop top if new element should replace it */
+ else if (h->cmp(&val, &h->values[0], NULL) > 0) {
+ h->size--;
+ h->values[0] = h->values[h->size];
+ hashsig_heap_down(h, 0);
+ }
+
+}
+
+typedef struct {
+ int use_ignores;
+ uint8_t ignore_ch[256];
+} hashsig_in_progress;
+
+static int hashsig_in_progress_init(
+ hashsig_in_progress *prog, git_hashsig *sig)
+{
+ int i;
+
+ /* no more than one can be set */
+ GIT_ASSERT(!(sig->opt & GIT_HASHSIG_IGNORE_WHITESPACE) ||
+ !(sig->opt & GIT_HASHSIG_SMART_WHITESPACE));
+
+ if (sig->opt & GIT_HASHSIG_IGNORE_WHITESPACE) {
+ for (i = 0; i < 256; ++i)
+ prog->ignore_ch[i] = git__isspace_nonlf(i);
+ prog->use_ignores = 1;
+ } else if (sig->opt & GIT_HASHSIG_SMART_WHITESPACE) {
+ for (i = 0; i < 256; ++i)
+ prog->ignore_ch[i] = git__isspace(i);
+ prog->use_ignores = 1;
+ } else {
+ memset(prog, 0, sizeof(*prog));
+ }
+
+ return 0;
+}
+
+static int hashsig_add_hashes(
+ git_hashsig *sig,
+ const uint8_t *data,
+ size_t size,
+ hashsig_in_progress *prog)
+{
+ const uint8_t *scan = data, *end = data + size;
+ hashsig_state state = HASHSIG_HASH_START;
+ int use_ignores = prog->use_ignores, len;
+ uint8_t ch;
+
+ while (scan < end) {
+ state = HASHSIG_HASH_START;
+
+ for (len = 0; scan < end && len < HASHSIG_MAX_RUN; ) {
+ ch = *scan;
+
+ if (use_ignores)
+ for (; scan < end && git__isspace_nonlf(ch); ch = *scan)
+ ++scan;
+ else if (sig->opt &
+ (GIT_HASHSIG_IGNORE_WHITESPACE | GIT_HASHSIG_SMART_WHITESPACE))
+ for (; scan < end && ch == '\r'; ch = *scan)
+ ++scan;
+
+ /* peek at next character to decide what to do next */
+ if (sig->opt & GIT_HASHSIG_SMART_WHITESPACE)
+ use_ignores = (ch == '\n');
+
+ if (scan >= end)
+ break;
+ ++scan;
+
+ /* check run terminator */
+ if (ch == '\n' || ch == '\0') {
+ sig->lines++;
+ break;
+ }
+
+ ++len;
+ HASHSIG_HASH_MIX(state, ch);
+ }
+
+ if (len > 0) {
+ hashsig_heap_insert(&sig->mins, (hashsig_t)state);
+ hashsig_heap_insert(&sig->maxs, (hashsig_t)state);
+
+ while (scan < end && (*scan == '\n' || !*scan))
+ ++scan;
+ }
+ }
+
+ prog->use_ignores = use_ignores;
+
+ return 0;
+}
+
+static int hashsig_finalize_hashes(git_hashsig *sig)
+{
+ if (sig->mins.size < HASHSIG_HEAP_MIN_SIZE &&
+ !(sig->opt & GIT_HASHSIG_ALLOW_SMALL_FILES)) {
+ git_error_set(GIT_ERROR_INVALID,
+ "file too small for similarity signature calculation");
+ return GIT_EBUFS;
+ }
+
+ hashsig_heap_sort(&sig->mins);
+ hashsig_heap_sort(&sig->maxs);
+
+ return 0;
+}
+
+static git_hashsig *hashsig_alloc(git_hashsig_option_t opts)
+{
+ git_hashsig *sig = git__calloc(1, sizeof(git_hashsig));
+ if (!sig)
+ return NULL;
+
+ hashsig_heap_init(&sig->mins, hashsig_cmp_min);
+ hashsig_heap_init(&sig->maxs, hashsig_cmp_max);
+ sig->opt = opts;
+
+ return sig;
+}
+
+int git_hashsig_create(
+ git_hashsig **out,
+ const char *buf,
+ size_t buflen,
+ git_hashsig_option_t opts)
+{
+ int error;
+ hashsig_in_progress prog;
+ git_hashsig *sig = hashsig_alloc(opts);
+ GIT_ERROR_CHECK_ALLOC(sig);
+
+ if ((error = hashsig_in_progress_init(&prog, sig)) < 0)
+ return error;
+
+ error = hashsig_add_hashes(sig, (const uint8_t *)buf, buflen, &prog);
+
+ if (!error)
+ error = hashsig_finalize_hashes(sig);
+
+ if (!error)
+ *out = sig;
+ else
+ git_hashsig_free(sig);
+
+ return error;
+}
+
+int git_hashsig_create_fromfile(
+ git_hashsig **out,
+ const char *path,
+ git_hashsig_option_t opts)
+{
+ uint8_t buf[0x1000];
+ ssize_t buflen = 0;
+ int error = 0, fd;
+ hashsig_in_progress prog;
+ git_hashsig *sig = hashsig_alloc(opts);
+ GIT_ERROR_CHECK_ALLOC(sig);
+
+ if ((fd = git_futils_open_ro(path)) < 0) {
+ git__free(sig);
+ return fd;
+ }
+
+ if ((error = hashsig_in_progress_init(&prog, sig)) < 0) {
+ p_close(fd);
+ return error;
+ }
+
+ while (!error) {
+ if ((buflen = p_read(fd, buf, sizeof(buf))) <= 0) {
+ if ((error = (int)buflen) < 0)
+ git_error_set(GIT_ERROR_OS,
+ "read error on '%s' calculating similarity hashes", path);
+ break;
+ }
+
+ error = hashsig_add_hashes(sig, buf, buflen, &prog);
+ }
+
+ p_close(fd);
+
+ if (!error)
+ error = hashsig_finalize_hashes(sig);
+
+ if (!error)
+ *out = sig;
+ else
+ git_hashsig_free(sig);
+
+ return error;
+}
+
+void git_hashsig_free(git_hashsig *sig)
+{
+ git__free(sig);
+}
+
+static int hashsig_heap_compare(const hashsig_heap *a, const hashsig_heap *b)
+{
+ int matches = 0, i, j, cmp;
+
+ GIT_ASSERT_WITH_RETVAL(a->cmp == b->cmp, 0);
+
+ /* hash heaps are sorted - just look for overlap vs total */
+
+ for (i = 0, j = 0; i < a->size && j < b->size; ) {
+ cmp = a->cmp(&a->values[i], &b->values[j], NULL);
+
+ if (cmp < 0)
+ ++i;
+ else if (cmp > 0)
+ ++j;
+ else {
+ ++i; ++j; ++matches;
+ }
+ }
+
+ return HASHSIG_SCALE * (matches * 2) / (a->size + b->size);
+}
+
+int git_hashsig_compare(const git_hashsig *a, const git_hashsig *b)
+{
+ /* if we have no elements in either file then each file is either
+ * empty or blank. if we're ignoring whitespace then the files are
+ * similar, otherwise they're dissimilar.
+ */
+ if (a->mins.size == 0 && b->mins.size == 0) {
+ if ((!a->lines && !b->lines) ||
+ (a->opt & GIT_HASHSIG_IGNORE_WHITESPACE))
+ return HASHSIG_SCALE;
+ else
+ return 0;
+ }
+
+ /* if we have fewer than the maximum number of elements, then just use
+ * one array since the two arrays will be the same
+ */
+ if (a->mins.size < HASHSIG_HEAP_SIZE) {
+ return hashsig_heap_compare(&a->mins, &b->mins);
+ } else {
+ int mins, maxs;
+
+ if ((mins = hashsig_heap_compare(&a->mins, &b->mins)) < 0)
+ return mins;
+ if ((maxs = hashsig_heap_compare(&a->maxs, &b->maxs)) < 0)
+ return maxs;
+
+ return (mins + maxs) / 2;
+ }
+}
diff --git a/src/libgit2/ident.c b/src/libgit2/ident.c
new file mode 100644
index 0000000..97110c6
--- /dev/null
+++ b/src/libgit2/ident.c
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+
+#include "git2/sys/filter.h"
+#include "filter.h"
+#include "str.h"
+
+static int ident_find_id(
+ const char **id_start, const char **id_end, const char *start, size_t len)
+{
+ const char *end = start + len, *found = NULL;
+
+ while (len > 3 && (found = memchr(start, '$', len)) != NULL) {
+ size_t remaining = (size_t)(end - found) - 1;
+ if (remaining < 3)
+ return GIT_ENOTFOUND;
+
+ start = found + 1;
+ len = remaining;
+
+ if (start[0] == 'I' && start[1] == 'd')
+ break;
+ }
+
+ if (len < 3 || !found)
+ return GIT_ENOTFOUND;
+ *id_start = found;
+
+ if ((found = memchr(start + 2, '$', len - 2)) == NULL)
+ return GIT_ENOTFOUND;
+
+ *id_end = found + 1;
+ return 0;
+}
+
+static int ident_insert_id(
+ git_str *to, const git_str *from, const git_filter_source *src)
+{
+ char oid[GIT_OID_MAX_HEXSIZE + 1];
+ const char *id_start, *id_end, *from_end = from->ptr + from->size;
+ size_t need_size;
+
+ /* replace $Id$ with blob id */
+
+ if (!git_filter_source_id(src))
+ return GIT_PASSTHROUGH;
+
+ git_oid_tostr(oid, sizeof(oid), git_filter_source_id(src));
+
+ if (ident_find_id(&id_start, &id_end, from->ptr, from->size) < 0)
+ return GIT_PASSTHROUGH;
+
+ need_size = (size_t)(id_start - from->ptr) +
+ 5 /* "$Id: " */ + GIT_OID_MAX_HEXSIZE + 2 /* " $" */ +
+ (size_t)(from_end - id_end);
+
+ if (git_str_grow(to, need_size) < 0)
+ return -1;
+
+ git_str_set(to, from->ptr, (size_t)(id_start - from->ptr));
+ git_str_put(to, "$Id: ", 5);
+ git_str_puts(to, oid);
+ git_str_put(to, " $", 2);
+ git_str_put(to, id_end, (size_t)(from_end - id_end));
+
+ return git_str_oom(to) ? -1 : 0;
+}
+
+static int ident_remove_id(
+ git_str *to, const git_str *from)
+{
+ const char *id_start, *id_end, *from_end = from->ptr + from->size;
+ size_t need_size;
+
+ if (ident_find_id(&id_start, &id_end, from->ptr, from->size) < 0)
+ return GIT_PASSTHROUGH;
+
+ need_size = (size_t)(id_start - from->ptr) +
+ 4 /* "$Id$" */ + (size_t)(from_end - id_end);
+
+ if (git_str_grow(to, need_size) < 0)
+ return -1;
+
+ git_str_set(to, from->ptr, (size_t)(id_start - from->ptr));
+ git_str_put(to, "$Id$", 4);
+ git_str_put(to, id_end, (size_t)(from_end - id_end));
+
+ return git_str_oom(to) ? -1 : 0;
+}
+
+static int ident_apply(
+ git_filter *self,
+ void **payload,
+ git_str *to,
+ const git_str *from,
+ const git_filter_source *src)
+{
+ GIT_UNUSED(self); GIT_UNUSED(payload);
+
+ /* Don't filter binary files */
+ if (git_str_is_binary(from))
+ return GIT_PASSTHROUGH;
+
+ if (git_filter_source_mode(src) == GIT_FILTER_SMUDGE)
+ return ident_insert_id(to, from, src);
+ else
+ return ident_remove_id(to, from);
+}
+
+static int ident_stream(
+ git_writestream **out,
+ git_filter *self,
+ void **payload,
+ const git_filter_source *src,
+ git_writestream *next)
+{
+ return git_filter_buffered_stream_new(out,
+ self, ident_apply, NULL, payload, src, next);
+}
+
+git_filter *git_ident_filter_new(void)
+{
+ git_filter *f = git__calloc(1, sizeof(git_filter));
+ if (f == NULL)
+ return NULL;
+
+ f->version = GIT_FILTER_VERSION;
+ f->attributes = "+ident"; /* apply to files with ident attribute set */
+ f->shutdown = git_filter_free;
+ f->stream = ident_stream;
+
+ return f;
+}
diff --git a/src/libgit2/idxmap.c b/src/libgit2/idxmap.c
new file mode 100644
index 0000000..bc23608
--- /dev/null
+++ b/src/libgit2/idxmap.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "idxmap.h"
+
+#define kmalloc git__malloc
+#define kcalloc git__calloc
+#define krealloc git__realloc
+#define kreallocarray git__reallocarray
+#define kfree git__free
+#include "khash.h"
+
+__KHASH_TYPE(idx, const git_index_entry *, git_index_entry *)
+__KHASH_TYPE(idxicase, const git_index_entry *, git_index_entry *)
+
+/* This is __ac_X31_hash_string but with tolower and it takes the entry's stage into account */
+static kh_inline khint_t idxentry_hash(const git_index_entry *e)
+{
+ const char *s = e->path;
+ khint_t h = (khint_t)git__tolower(*s);
+ if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)git__tolower(*s);
+ return h + GIT_INDEX_ENTRY_STAGE(e);
+}
+
+#define idxentry_equal(a, b) (GIT_INDEX_ENTRY_STAGE(a) == GIT_INDEX_ENTRY_STAGE(b) && strcmp(a->path, b->path) == 0)
+#define idxentry_icase_equal(a, b) (GIT_INDEX_ENTRY_STAGE(a) == GIT_INDEX_ENTRY_STAGE(b) && strcasecmp(a->path, b->path) == 0)
+
+__KHASH_IMPL(idx, static kh_inline, const git_index_entry *, git_index_entry *, 1, idxentry_hash, idxentry_equal)
+__KHASH_IMPL(idxicase, static kh_inline, const git_index_entry *, git_index_entry *, 1, idxentry_hash, idxentry_icase_equal)
+
+int git_idxmap_new(git_idxmap **out)
+{
+ *out = kh_init(idx);
+ GIT_ERROR_CHECK_ALLOC(*out);
+
+ return 0;
+}
+
+int git_idxmap_icase_new(git_idxmap_icase **out)
+{
+ *out = kh_init(idxicase);
+ GIT_ERROR_CHECK_ALLOC(*out);
+
+ return 0;
+}
+
+void git_idxmap_free(git_idxmap *map)
+{
+ kh_destroy(idx, map);
+}
+
+void git_idxmap_icase_free(git_idxmap_icase *map)
+{
+ kh_destroy(idxicase, map);
+}
+
+void git_idxmap_clear(git_idxmap *map)
+{
+ kh_clear(idx, map);
+}
+
+void git_idxmap_icase_clear(git_idxmap_icase *map)
+{
+ kh_clear(idxicase, map);
+}
+
+int git_idxmap_resize(git_idxmap *map, size_t size)
+{
+ if (!git__is_uint32(size) ||
+ kh_resize(idx, map, (khiter_t)size) < 0) {
+ git_error_set_oom();
+ return -1;
+ }
+ return 0;
+}
+
+int git_idxmap_icase_resize(git_idxmap_icase *map, size_t size)
+{
+ if (!git__is_uint32(size) ||
+ kh_resize(idxicase, map, (khiter_t)size) < 0) {
+ git_error_set_oom();
+ return -1;
+ }
+ return 0;
+}
+
+void *git_idxmap_get(git_idxmap *map, const git_index_entry *key)
+{
+ size_t idx = kh_get(idx, map, key);
+ if (idx == kh_end(map) || !kh_exist(map, idx))
+ return NULL;
+ return kh_val(map, idx);
+}
+
+int git_idxmap_set(git_idxmap *map, const git_index_entry *key, void *value)
+{
+ size_t idx;
+ int rval;
+
+ idx = kh_put(idx, map, key, &rval);
+ if (rval < 0)
+ return -1;
+
+ if (rval == 0)
+ kh_key(map, idx) = key;
+
+ kh_val(map, idx) = value;
+
+ return 0;
+}
+
+int git_idxmap_icase_set(git_idxmap_icase *map, const git_index_entry *key, void *value)
+{
+ size_t idx;
+ int rval;
+
+ idx = kh_put(idxicase, map, key, &rval);
+ if (rval < 0)
+ return -1;
+
+ if (rval == 0)
+ kh_key(map, idx) = key;
+
+ kh_val(map, idx) = value;
+
+ return 0;
+}
+
+void *git_idxmap_icase_get(git_idxmap_icase *map, const git_index_entry *key)
+{
+ size_t idx = kh_get(idxicase, map, key);
+ if (idx == kh_end(map) || !kh_exist(map, idx))
+ return NULL;
+ return kh_val(map, idx);
+}
+
+int git_idxmap_delete(git_idxmap *map, const git_index_entry *key)
+{
+ khiter_t idx = kh_get(idx, map, key);
+ if (idx == kh_end(map))
+ return GIT_ENOTFOUND;
+ kh_del(idx, map, idx);
+ return 0;
+}
+
+int git_idxmap_icase_delete(git_idxmap_icase *map, const git_index_entry *key)
+{
+ khiter_t idx = kh_get(idxicase, map, key);
+ if (idx == kh_end(map))
+ return GIT_ENOTFOUND;
+ kh_del(idxicase, map, idx);
+ return 0;
+}
diff --git a/src/libgit2/idxmap.h b/src/libgit2/idxmap.h
new file mode 100644
index 0000000..76170ef
--- /dev/null
+++ b/src/libgit2/idxmap.h
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_idxmap_h__
+#define INCLUDE_idxmap_h__
+
+#include "common.h"
+
+#include "git2/index.h"
+
+/** A map with `git_index_entry`s as key. */
+typedef struct kh_idx_s git_idxmap;
+/** A map with case-insensitive `git_index_entry`s as key */
+typedef struct kh_idxicase_s git_idxmap_icase;
+
+/**
+ * Allocate a new index entry map.
+ *
+ * @param out Pointer to the map that shall be allocated.
+ * @return 0 on success, an error code if allocation has failed.
+ */
+int git_idxmap_new(git_idxmap **out);
+
+/**
+ * Allocate a new case-insensitive index entry map.
+ *
+ * @param out Pointer to the map that shall be allocated.
+ * @return 0 on success, an error code if allocation has failed.
+ */
+int git_idxmap_icase_new(git_idxmap_icase **out);
+
+/**
+ * Free memory associated with the map.
+ *
+ * Note that this function will _not_ free values added to this
+ * map.
+ *
+ * @param map Pointer to the map that is to be free'd. May be
+ * `NULL`.
+ */
+void git_idxmap_free(git_idxmap *map);
+
+/**
+ * Free memory associated with the map.
+ *
+ * Note that this function will _not_ free values added to this
+ * map.
+ *
+ * @param map Pointer to the map that is to be free'd. May be
+ * `NULL`.
+ */
+void git_idxmap_icase_free(git_idxmap_icase *map);
+
+/**
+ * Clear all entries from the map.
+ *
+ * This function will remove all entries from the associated map.
+ * Memory associated with it will not be released, though.
+ *
+ * @param map Pointer to the map that shall be cleared. May be
+ * `NULL`.
+ */
+void git_idxmap_clear(git_idxmap *map);
+
+/**
+ * Clear all entries from the map.
+ *
+ * This function will remove all entries from the associated map.
+ * Memory associated with it will not be released, though.
+ *
+ * @param map Pointer to the map that shall be cleared. May be
+ * `NULL`.
+ */
+void git_idxmap_icase_clear(git_idxmap_icase *map);
+
+/**
+ * Resize the map by allocating more memory.
+ *
+ * @param map map that shall be resized
+ * @param size count of entries that the map shall hold
+ * @return `0` if the map was successfully resized, a negative
+ * error code otherwise
+ */
+int git_idxmap_resize(git_idxmap *map, size_t size);
+
+/**
+ * Resize the map by allocating more memory.
+ *
+ * @param map map that shall be resized
+ * @param size count of entries that the map shall hold
+ * @return `0` if the map was successfully resized, a negative
+ * error code otherwise
+ */
+int git_idxmap_icase_resize(git_idxmap_icase *map, size_t size);
+
+/**
+ * Return value associated with the given key.
+ *
+ * @param map map to search key in
+ * @param key key to search for; the index entry will be searched
+ * for by its case-sensitive path
+ * @return value associated with the given key or NULL if the key was not found
+ */
+void *git_idxmap_get(git_idxmap *map, const git_index_entry *key);
+
+/**
+ * Return value associated with the given key.
+ *
+ * @param map map to search key in
+ * @param key key to search for; the index entry will be searched
+ * for by its case-insensitive path
+ * @return value associated with the given key or NULL if the key was not found
+ */
+void *git_idxmap_icase_get(git_idxmap_icase *map, const git_index_entry *key);
+
+/**
+ * Set the entry for key to value.
+ *
+ * If the map has no corresponding entry for the given key, a new
+ * entry will be created with the given value. If an entry exists
+ * already, its value will be updated to match the given value.
+ *
+ * @param map map to create new entry in
+ * @param key key to set
+ * @param value value to associate the key with; may be NULL
+ * @return zero if the key was successfully set, a negative error
+ * code otherwise
+ */
+int git_idxmap_set(git_idxmap *map, const git_index_entry *key, void *value);
+
+/**
+ * Set the entry for key to value.
+ *
+ * If the map has no corresponding entry for the given key, a new
+ * entry will be created with the given value. If an entry exists
+ * already, its value will be updated to match the given value.
+ *
+ * @param map map to create new entry in
+ * @param key key to set
+ * @param value value to associate the key with; may be NULL
+ * @return zero if the key was successfully set, a negative error
+ * code otherwise
+ */
+int git_idxmap_icase_set(git_idxmap_icase *map, const git_index_entry *key, void *value);
+
+/**
+ * Delete an entry from the map.
+ *
+ * Delete the given key and its value from the map. If no such
+ * key exists, this will do nothing.
+ *
+ * @param map map to delete key in
+ * @param key key to delete
+ * @return `0` if the key has been deleted, GIT_ENOTFOUND if no
+ * such key was found, a negative code in case of an
+ * error
+ */
+int git_idxmap_delete(git_idxmap *map, const git_index_entry *key);
+
+/**
+ * Delete an entry from the map.
+ *
+ * Delete the given key and its value from the map. If no such
+ * key exists, this will do nothing.
+ *
+ * @param map map to delete key in
+ * @param key key to delete
+ * @return `0` if the key has been deleted, GIT_ENOTFOUND if no
+ * such key was found, a negative code in case of an
+ * error
+ */
+int git_idxmap_icase_delete(git_idxmap_icase *map, const git_index_entry *key);
+
+#endif
diff --git a/src/libgit2/ignore.c b/src/libgit2/ignore.c
new file mode 100644
index 0000000..cee58d7
--- /dev/null
+++ b/src/libgit2/ignore.c
@@ -0,0 +1,652 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "ignore.h"
+
+#include "git2/ignore.h"
+#include "common.h"
+#include "attrcache.h"
+#include "fs_path.h"
+#include "config.h"
+#include "wildmatch.h"
+#include "path.h"
+
+#define GIT_IGNORE_INTERNAL "[internal]exclude"
+
+#define GIT_IGNORE_DEFAULT_RULES ".\n..\n.git\n"
+
+/**
+ * A negative ignore pattern can negate a positive one without
+ * wildcards if it is a basename only and equals the basename of
+ * the positive pattern. Thus
+ *
+ * foo/bar
+ * !bar
+ *
+ * would result in foo/bar being unignored again while
+ *
+ * moo/foo/bar
+ * !foo/bar
+ *
+ * would do nothing. The reverse also holds true: a positive
+ * basename pattern can be negated by unignoring the basename in
+ * subdirectories. Thus
+ *
+ * bar
+ * !foo/bar
+ *
+ * would result in foo/bar being unignored again. As with the
+ * first case,
+ *
+ * foo/bar
+ * !moo/foo/bar
+ *
+ * would do nothing, again.
+ */
+static int does_negate_pattern(git_attr_fnmatch *rule, git_attr_fnmatch *neg)
+{
+ int (*cmp)(const char *, const char *, size_t);
+ git_attr_fnmatch *longer, *shorter;
+ char *p;
+
+ if ((rule->flags & GIT_ATTR_FNMATCH_NEGATIVE) != 0
+ || (neg->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0)
+ return false;
+
+ if (neg->flags & GIT_ATTR_FNMATCH_ICASE)
+ cmp = git__strncasecmp;
+ else
+ cmp = git__strncmp;
+
+ /* If lengths match we need to have an exact match */
+ if (rule->length == neg->length) {
+ return cmp(rule->pattern, neg->pattern, rule->length) == 0;
+ } else if (rule->length < neg->length) {
+ shorter = rule;
+ longer = neg;
+ } else {
+ shorter = neg;
+ longer = rule;
+ }
+
+ /* Otherwise, we need to check if the shorter
+ * rule is a basename only (that is, it contains
+ * no path separator) and, if so, if it
+ * matches the tail of the longer rule */
+ p = longer->pattern + longer->length - shorter->length;
+
+ if (p[-1] != '/')
+ return false;
+ if (memchr(shorter->pattern, '/', shorter->length) != NULL)
+ return false;
+
+ return cmp(p, shorter->pattern, shorter->length) == 0;
+}
+
+/**
+ * A negative ignore can only unignore a file which is given explicitly before, thus
+ *
+ * foo
+ * !foo/bar
+ *
+ * does not unignore 'foo/bar' as it's not in the list. However
+ *
+ * foo/<star>
+ * !foo/bar
+ *
+ * does unignore 'foo/bar', as it is contained within the 'foo/<star>' rule.
+ */
+static int does_negate_rule(int *out, git_vector *rules, git_attr_fnmatch *match)
+{
+ int error = 0, wildmatch_flags, effective_flags;
+ size_t i;
+ git_attr_fnmatch *rule;
+ char *path;
+ git_str buf = GIT_STR_INIT;
+
+ *out = 0;
+
+ wildmatch_flags = WM_PATHNAME;
+ if (match->flags & GIT_ATTR_FNMATCH_ICASE)
+ wildmatch_flags |= WM_CASEFOLD;
+
+ /* path of the file relative to the workdir, so we match the rules in subdirs */
+ if (match->containing_dir) {
+ git_str_puts(&buf, match->containing_dir);
+ }
+ if (git_str_puts(&buf, match->pattern) < 0)
+ return -1;
+
+ path = git_str_detach(&buf);
+
+ git_vector_foreach(rules, i, rule) {
+ if (!(rule->flags & GIT_ATTR_FNMATCH_HASWILD)) {
+ if (does_negate_pattern(rule, match)) {
+ error = 0;
+ *out = 1;
+ goto out;
+ }
+ else
+ continue;
+ }
+
+ git_str_clear(&buf);
+ if (rule->containing_dir)
+ git_str_puts(&buf, rule->containing_dir);
+ git_str_puts(&buf, rule->pattern);
+
+ if (git_str_oom(&buf))
+ goto out;
+
+ /*
+ * if rule isn't for full path we match without PATHNAME flag
+ * as lines like *.txt should match something like dir/test.txt
+ * requiring * to also match /
+ */
+ effective_flags = wildmatch_flags;
+ if (!(rule->flags & GIT_ATTR_FNMATCH_FULLPATH))
+ effective_flags &= ~WM_PATHNAME;
+
+ /* if we found a match, we want to keep this rule */
+ if ((wildmatch(git_str_cstr(&buf), path, effective_flags)) == WM_MATCH) {
+ *out = 1;
+ error = 0;
+ goto out;
+ }
+ }
+
+ error = 0;
+
+out:
+ git__free(path);
+ git_str_dispose(&buf);
+ return error;
+}
+
+static int parse_ignore_file(
+ git_repository *repo, git_attr_file *attrs, const char *data, bool allow_macros)
+{
+ int error = 0;
+ int ignore_case = false;
+ const char *scan = data, *context = NULL;
+ git_attr_fnmatch *match = NULL;
+
+ GIT_UNUSED(allow_macros);
+
+ if (git_repository__configmap_lookup(&ignore_case, repo, GIT_CONFIGMAP_IGNORECASE) < 0)
+ git_error_clear();
+
+ /* if subdir file path, convert context for file paths */
+ if (attrs->entry &&
+ git_fs_path_root(attrs->entry->path) < 0 &&
+ !git__suffixcmp(attrs->entry->path, "/" GIT_IGNORE_FILE))
+ context = attrs->entry->path;
+
+ if (git_mutex_lock(&attrs->lock) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to lock ignore file");
+ return -1;
+ }
+
+ while (!error && *scan) {
+ int valid_rule = 1;
+
+ if (!match && !(match = git__calloc(1, sizeof(*match)))) {
+ error = -1;
+ break;
+ }
+
+ match->flags =
+ GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG;
+
+ if (!(error = git_attr_fnmatch__parse(
+ match, &attrs->pool, context, &scan)))
+ {
+ match->flags |= GIT_ATTR_FNMATCH_IGNORE;
+
+ if (ignore_case)
+ match->flags |= GIT_ATTR_FNMATCH_ICASE;
+
+ scan = git__next_line(scan);
+
+ /*
+ * If a negative match doesn't actually do anything,
+ * throw it away. As we cannot always verify whether a
+ * rule containing wildcards negates another rule, we
+ * do not optimize away these rules, though.
+ * */
+ if (match->flags & GIT_ATTR_FNMATCH_NEGATIVE
+ && !(match->flags & GIT_ATTR_FNMATCH_HASWILD))
+ error = does_negate_rule(&valid_rule, &attrs->rules, match);
+
+ if (!error && valid_rule)
+ error = git_vector_insert(&attrs->rules, match);
+ }
+
+ if (error != 0 || !valid_rule) {
+ match->pattern = NULL;
+
+ if (error == GIT_ENOTFOUND)
+ error = 0;
+ } else {
+ match = NULL; /* vector now "owns" the match */
+ }
+ }
+
+ git_mutex_unlock(&attrs->lock);
+ git__free(match);
+
+ return error;
+}
+
+static int push_ignore_file(
+ git_ignores *ignores,
+ git_vector *which_list,
+ const char *base,
+ const char *filename)
+{
+ git_attr_file_source source = { GIT_ATTR_FILE_SOURCE_FILE, base, filename };
+ git_attr_file *file = NULL;
+ int error = 0;
+
+ error = git_attr_cache__get(&file, ignores->repo, NULL, &source, parse_ignore_file, false);
+
+ if (error < 0)
+ return error;
+
+ if (file != NULL) {
+ if ((error = git_vector_insert(which_list, file)) < 0)
+ git_attr_file__free(file);
+ }
+
+ return error;
+}
+
+static int push_one_ignore(void *payload, const char *path)
+{
+ git_ignores *ign = payload;
+ ign->depth++;
+ return push_ignore_file(ign, &ign->ign_path, path, GIT_IGNORE_FILE);
+}
+
+static int get_internal_ignores(git_attr_file **out, git_repository *repo)
+{
+ git_attr_file_source source = { GIT_ATTR_FILE_SOURCE_MEMORY, NULL, GIT_IGNORE_INTERNAL };
+ int error;
+
+ if ((error = git_attr_cache__init(repo)) < 0)
+ return error;
+
+ error = git_attr_cache__get(out, repo, NULL, &source, NULL, false);
+
+ /* if internal rules list is empty, insert default rules */
+ if (!error && !(*out)->rules.length)
+ error = parse_ignore_file(repo, *out, GIT_IGNORE_DEFAULT_RULES, false);
+
+ return error;
+}
+
+int git_ignore__for_path(
+ git_repository *repo,
+ const char *path,
+ git_ignores *ignores)
+{
+ int error = 0;
+ const char *workdir = git_repository_workdir(repo);
+ git_str infopath = GIT_STR_INIT;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(ignores);
+ GIT_ASSERT_ARG(path);
+
+ memset(ignores, 0, sizeof(*ignores));
+ ignores->repo = repo;
+
+ /* Read the ignore_case flag */
+ if ((error = git_repository__configmap_lookup(
+ &ignores->ignore_case, repo, GIT_CONFIGMAP_IGNORECASE)) < 0)
+ goto cleanup;
+
+ if ((error = git_attr_cache__init(repo)) < 0)
+ goto cleanup;
+
+ /* given a unrooted path in a non-bare repo, resolve it */
+ if (workdir && git_fs_path_root(path) < 0) {
+ git_str local = GIT_STR_INIT;
+
+ if ((error = git_fs_path_dirname_r(&local, path)) < 0 ||
+ (error = git_fs_path_resolve_relative(&local, 0)) < 0 ||
+ (error = git_fs_path_to_dir(&local)) < 0 ||
+ (error = git_str_joinpath(&ignores->dir, workdir, local.ptr)) < 0 ||
+ (error = git_path_validate_str_length(repo, &ignores->dir)) < 0) {
+ /* Nothing, we just want to stop on the first error */
+ }
+
+ git_str_dispose(&local);
+ } else {
+ if (!(error = git_str_joinpath(&ignores->dir, path, "")))
+ error = git_path_validate_str_length(NULL, &ignores->dir);
+ }
+
+ if (error < 0)
+ goto cleanup;
+
+ if (workdir && !git__prefixcmp(ignores->dir.ptr, workdir))
+ ignores->dir_root = strlen(workdir);
+
+ /* set up internals */
+ if ((error = get_internal_ignores(&ignores->ign_internal, repo)) < 0)
+ goto cleanup;
+
+ /* load .gitignore up the path */
+ if (workdir != NULL) {
+ error = git_fs_path_walk_up(
+ &ignores->dir, workdir, push_one_ignore, ignores);
+ if (error < 0)
+ goto cleanup;
+ }
+
+ /* load .git/info/exclude if possible */
+ if ((error = git_repository__item_path(&infopath, repo, GIT_REPOSITORY_ITEM_INFO)) < 0 ||
+ (error = push_ignore_file(ignores, &ignores->ign_global, infopath.ptr, GIT_IGNORE_FILE_INREPO)) < 0) {
+ if (error != GIT_ENOTFOUND)
+ goto cleanup;
+ error = 0;
+ }
+
+ /* load core.excludesfile */
+ if (git_repository_attr_cache(repo)->cfg_excl_file != NULL)
+ error = push_ignore_file(
+ ignores, &ignores->ign_global, NULL,
+ git_repository_attr_cache(repo)->cfg_excl_file);
+
+cleanup:
+ git_str_dispose(&infopath);
+ if (error < 0)
+ git_ignore__free(ignores);
+
+ return error;
+}
+
+int git_ignore__push_dir(git_ignores *ign, const char *dir)
+{
+ if (git_str_joinpath(&ign->dir, ign->dir.ptr, dir) < 0)
+ return -1;
+
+ ign->depth++;
+
+ return push_ignore_file(
+ ign, &ign->ign_path, ign->dir.ptr, GIT_IGNORE_FILE);
+}
+
+int git_ignore__pop_dir(git_ignores *ign)
+{
+ if (ign->ign_path.length > 0) {
+ git_attr_file *file = git_vector_last(&ign->ign_path);
+ const char *start = file->entry->path, *end;
+
+ /* - ign->dir looks something like "/home/user/a/b/" (or "a/b/c/d/")
+ * - file->path looks something like "a/b/.gitignore
+ *
+ * We are popping the last directory off ign->dir. We also want
+ * to remove the file from the vector if the popped directory
+ * matches the ignore path. We need to test if the "a/b" part of
+ * the file key matches the path we are about to pop.
+ */
+
+ if ((end = strrchr(start, '/')) != NULL) {
+ size_t dirlen = (end - start) + 1;
+ const char *relpath = ign->dir.ptr + ign->dir_root;
+ size_t pathlen = ign->dir.size - ign->dir_root;
+
+ if (pathlen == dirlen && !memcmp(relpath, start, dirlen)) {
+ git_vector_pop(&ign->ign_path);
+ git_attr_file__free(file);
+ }
+ }
+ }
+
+ if (--ign->depth > 0) {
+ git_str_rtruncate_at_char(&ign->dir, '/');
+ git_fs_path_to_dir(&ign->dir);
+ }
+
+ return 0;
+}
+
+void git_ignore__free(git_ignores *ignores)
+{
+ unsigned int i;
+ git_attr_file *file;
+
+ git_attr_file__free(ignores->ign_internal);
+
+ git_vector_foreach(&ignores->ign_path, i, file) {
+ git_attr_file__free(file);
+ ignores->ign_path.contents[i] = NULL;
+ }
+ git_vector_free(&ignores->ign_path);
+
+ git_vector_foreach(&ignores->ign_global, i, file) {
+ git_attr_file__free(file);
+ ignores->ign_global.contents[i] = NULL;
+ }
+ git_vector_free(&ignores->ign_global);
+
+ git_str_dispose(&ignores->dir);
+}
+
+static bool ignore_lookup_in_rules(
+ int *ignored, git_attr_file *file, git_attr_path *path)
+{
+ size_t j;
+ git_attr_fnmatch *match;
+
+ git_vector_rforeach(&file->rules, j, match) {
+ if (match->flags & GIT_ATTR_FNMATCH_DIRECTORY &&
+ path->is_dir == GIT_DIR_FLAG_FALSE)
+ continue;
+ if (git_attr_fnmatch__match(match, path)) {
+ *ignored = ((match->flags & GIT_ATTR_FNMATCH_NEGATIVE) == 0) ?
+ GIT_IGNORE_TRUE : GIT_IGNORE_FALSE;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+int git_ignore__lookup(
+ int *out, git_ignores *ignores, const char *pathname, git_dir_flag dir_flag)
+{
+ size_t i;
+ git_attr_file *file;
+ git_attr_path path;
+
+ *out = GIT_IGNORE_NOTFOUND;
+
+ if (git_attr_path__init(
+ &path, pathname, git_repository_workdir(ignores->repo), dir_flag) < 0)
+ return -1;
+
+ /* first process builtins - success means path was found */
+ if (ignore_lookup_in_rules(out, ignores->ign_internal, &path))
+ goto cleanup;
+
+ /* next process files in the path.
+ * this process has to process ignores in reverse order
+ * to ensure correct prioritization of rules
+ */
+ git_vector_rforeach(&ignores->ign_path, i, file) {
+ if (ignore_lookup_in_rules(out, file, &path))
+ goto cleanup;
+ }
+
+ /* last process global ignores */
+ git_vector_foreach(&ignores->ign_global, i, file) {
+ if (ignore_lookup_in_rules(out, file, &path))
+ goto cleanup;
+ }
+
+cleanup:
+ git_attr_path__free(&path);
+ return 0;
+}
+
+int git_ignore_add_rule(git_repository *repo, const char *rules)
+{
+ int error;
+ git_attr_file *ign_internal = NULL;
+
+ if ((error = get_internal_ignores(&ign_internal, repo)) < 0)
+ return error;
+
+ error = parse_ignore_file(repo, ign_internal, rules, false);
+ git_attr_file__free(ign_internal);
+
+ return error;
+}
+
+int git_ignore_clear_internal_rules(git_repository *repo)
+{
+ int error;
+ git_attr_file *ign_internal;
+
+ if ((error = get_internal_ignores(&ign_internal, repo)) < 0)
+ return error;
+
+ if (!(error = git_attr_file__clear_rules(ign_internal, true)))
+ error = parse_ignore_file(
+ repo, ign_internal, GIT_IGNORE_DEFAULT_RULES, false);
+
+ git_attr_file__free(ign_internal);
+ return error;
+}
+
+int git_ignore_path_is_ignored(
+ int *ignored,
+ git_repository *repo,
+ const char *pathname)
+{
+ int error;
+ const char *workdir;
+ git_attr_path path;
+ git_ignores ignores;
+ unsigned int i;
+ git_attr_file *file;
+ git_dir_flag dir_flag = GIT_DIR_FLAG_UNKNOWN;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(ignored);
+ GIT_ASSERT_ARG(pathname);
+
+ workdir = git_repository_workdir(repo);
+
+ memset(&path, 0, sizeof(path));
+ memset(&ignores, 0, sizeof(ignores));
+
+ if (!git__suffixcmp(pathname, "/"))
+ dir_flag = GIT_DIR_FLAG_TRUE;
+ else if (git_repository_is_bare(repo))
+ dir_flag = GIT_DIR_FLAG_FALSE;
+
+ if ((error = git_attr_path__init(&path, pathname, workdir, dir_flag)) < 0 ||
+ (error = git_ignore__for_path(repo, path.path, &ignores)) < 0)
+ goto cleanup;
+
+ while (1) {
+ /* first process builtins - success means path was found */
+ if (ignore_lookup_in_rules(ignored, ignores.ign_internal, &path))
+ goto cleanup;
+
+ /* next process files in the path */
+ git_vector_foreach(&ignores.ign_path, i, file) {
+ if (ignore_lookup_in_rules(ignored, file, &path))
+ goto cleanup;
+ }
+
+ /* last process global ignores */
+ git_vector_foreach(&ignores.ign_global, i, file) {
+ if (ignore_lookup_in_rules(ignored, file, &path))
+ goto cleanup;
+ }
+
+ /* move up one directory */
+ if (path.basename == path.path)
+ break;
+ path.basename[-1] = '\0';
+ while (path.basename > path.path && *path.basename != '/')
+ path.basename--;
+ if (path.basename > path.path)
+ path.basename++;
+ path.is_dir = 1;
+
+ if ((error = git_ignore__pop_dir(&ignores)) < 0)
+ break;
+ }
+
+ *ignored = 0;
+
+cleanup:
+ git_attr_path__free(&path);
+ git_ignore__free(&ignores);
+ return error;
+}
+
+int git_ignore__check_pathspec_for_exact_ignores(
+ git_repository *repo,
+ git_vector *vspec,
+ bool no_fnmatch)
+{
+ int error = 0;
+ size_t i;
+ git_attr_fnmatch *match;
+ int ignored;
+ git_str path = GIT_STR_INIT;
+ const char *filename;
+ git_index *idx;
+
+ if ((error = git_repository__ensure_not_bare(
+ repo, "validate pathspec")) < 0 ||
+ (error = git_repository_index(&idx, repo)) < 0)
+ return error;
+
+ git_vector_foreach(vspec, i, match) {
+ /* skip wildcard matches (if they are being used) */
+ if ((match->flags & GIT_ATTR_FNMATCH_HASWILD) != 0 &&
+ !no_fnmatch)
+ continue;
+
+ filename = match->pattern;
+
+ /* if file is already in the index, it's fine */
+ if (git_index_get_bypath(idx, filename, 0) != NULL)
+ continue;
+
+ if ((error = git_repository_workdir_path(&path, repo, filename)) < 0)
+ break;
+
+ /* is there a file on disk that matches this exactly? */
+ if (!git_fs_path_isfile(path.ptr))
+ continue;
+
+ /* is that file ignored? */
+ if ((error = git_ignore_path_is_ignored(&ignored, repo, filename)) < 0)
+ break;
+
+ if (ignored) {
+ git_error_set(GIT_ERROR_INVALID, "pathspec contains ignored file '%s'",
+ filename);
+ error = GIT_EINVALIDSPEC;
+ break;
+ }
+ }
+
+ git_index_free(idx);
+ git_str_dispose(&path);
+
+ return error;
+}
diff --git a/src/libgit2/ignore.h b/src/libgit2/ignore.h
new file mode 100644
index 0000000..aa5ca62
--- /dev/null
+++ b/src/libgit2/ignore.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_ignore_h__
+#define INCLUDE_ignore_h__
+
+#include "common.h"
+
+#include "repository.h"
+#include "vector.h"
+#include "attr_file.h"
+
+#define GIT_IGNORE_FILE ".gitignore"
+#define GIT_IGNORE_FILE_INREPO "exclude"
+#define GIT_IGNORE_FILE_XDG "ignore"
+
+/* The git_ignores structure maintains three sets of ignores:
+ * - internal ignores
+ * - per directory ignores
+ * - global ignores (at lower priority than the others)
+ * As you traverse from one directory to another, you can push and pop
+ * directories onto git_ignores list efficiently.
+ */
+typedef struct {
+ git_repository *repo;
+ git_str dir; /* current directory reflected in ign_path */
+ git_attr_file *ign_internal;
+ git_vector ign_path;
+ git_vector ign_global;
+ size_t dir_root; /* offset in dir to repo root */
+ int ignore_case;
+ int depth;
+} git_ignores;
+
+extern int git_ignore__for_path(
+ git_repository *repo, const char *path, git_ignores *ign);
+
+extern int git_ignore__push_dir(git_ignores *ign, const char *dir);
+
+extern int git_ignore__pop_dir(git_ignores *ign);
+
+extern void git_ignore__free(git_ignores *ign);
+
+enum {
+ GIT_IGNORE_UNCHECKED = -2,
+ GIT_IGNORE_NOTFOUND = -1,
+ GIT_IGNORE_FALSE = 0,
+ GIT_IGNORE_TRUE = 1
+};
+
+extern int git_ignore__lookup(int *out, git_ignores *ign, const char *path, git_dir_flag dir_flag);
+
+/* command line Git sometimes generates an error message if given a
+ * pathspec that contains an exact match to an ignored file (provided
+ * --force isn't also given). This makes it easy to check it that has
+ * happened. Returns GIT_EINVALIDSPEC if the pathspec contains ignored
+ * exact matches (that are not already present in the index).
+ */
+extern int git_ignore__check_pathspec_for_exact_ignores(
+ git_repository *repo, git_vector *pathspec, bool no_fnmatch);
+
+#endif
diff --git a/src/libgit2/index.c b/src/libgit2/index.c
new file mode 100644
index 0000000..ccb3823
--- /dev/null
+++ b/src/libgit2/index.c
@@ -0,0 +1,3972 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "index.h"
+
+#include <stddef.h>
+
+#include "repository.h"
+#include "tree.h"
+#include "tree-cache.h"
+#include "hash.h"
+#include "iterator.h"
+#include "pathspec.h"
+#include "ignore.h"
+#include "blob.h"
+#include "idxmap.h"
+#include "diff.h"
+#include "varint.h"
+#include "path.h"
+
+#include "git2/odb.h"
+#include "git2/oid.h"
+#include "git2/blob.h"
+#include "git2/config.h"
+#include "git2/sys/index.h"
+
+static int index_apply_to_wd_diff(git_index *index, int action, const git_strarray *paths,
+ unsigned int flags,
+ git_index_matched_path_cb cb, void *payload);
+
+static const size_t INDEX_HEADER_SIZE = 12;
+
+static const unsigned int INDEX_VERSION_NUMBER_DEFAULT = 2;
+static const unsigned int INDEX_VERSION_NUMBER_LB = 2;
+static const unsigned int INDEX_VERSION_NUMBER_EXT = 3;
+static const unsigned int INDEX_VERSION_NUMBER_COMP = 4;
+static const unsigned int INDEX_VERSION_NUMBER_UB = 4;
+
+static const unsigned int INDEX_HEADER_SIG = 0x44495243;
+static const char INDEX_EXT_TREECACHE_SIG[] = {'T', 'R', 'E', 'E'};
+static const char INDEX_EXT_UNMERGED_SIG[] = {'R', 'E', 'U', 'C'};
+static const char INDEX_EXT_CONFLICT_NAME_SIG[] = {'N', 'A', 'M', 'E'};
+
+#define INDEX_OWNER(idx) ((git_repository *)(GIT_REFCOUNT_OWNER(idx)))
+
+struct index_header {
+ uint32_t signature;
+ uint32_t version;
+ uint32_t entry_count;
+};
+
+struct index_extension {
+ char signature[4];
+ uint32_t extension_size;
+};
+
+struct entry_time {
+ uint32_t seconds;
+ uint32_t nanoseconds;
+};
+
+struct entry_common {
+ struct entry_time ctime;
+ struct entry_time mtime;
+ uint32_t dev;
+ uint32_t ino;
+ uint32_t mode;
+ uint32_t uid;
+ uint32_t gid;
+ uint32_t file_size;
+};
+
+#define entry_short(oid_size) \
+ struct { \
+ struct entry_common common; \
+ unsigned char oid[oid_size]; \
+ uint16_t flags; \
+ char path[1]; /* arbitrary length */ \
+ }
+
+#define entry_long(oid_size) \
+ struct { \
+ struct entry_common common; \
+ unsigned char oid[oid_size]; \
+ uint16_t flags; \
+ uint16_t flags_extended; \
+ char path[1]; /* arbitrary length */ \
+ }
+
+typedef entry_short(GIT_OID_SHA1_SIZE) index_entry_short_sha1;
+typedef entry_long(GIT_OID_SHA1_SIZE) index_entry_long_sha1;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+typedef entry_short(GIT_OID_SHA256_SIZE) index_entry_short_sha256;
+typedef entry_long(GIT_OID_SHA256_SIZE) index_entry_long_sha256;
+#endif
+
+#undef entry_short
+#undef entry_long
+
+struct entry_srch_key {
+ const char *path;
+ size_t pathlen;
+ int stage;
+};
+
+struct entry_internal {
+ git_index_entry entry;
+ size_t pathlen;
+ char path[GIT_FLEX_ARRAY];
+};
+
+struct reuc_entry_internal {
+ git_index_reuc_entry entry;
+ size_t pathlen;
+ char path[GIT_FLEX_ARRAY];
+};
+
+bool git_index__enforce_unsaved_safety = false;
+
+/* local declarations */
+static int read_extension(size_t *read_len, git_index *index, size_t checksum_size, const char *buffer, size_t buffer_size);
+static int read_header(struct index_header *dest, const void *buffer);
+
+static int parse_index(git_index *index, const char *buffer, size_t buffer_size);
+static bool is_index_extended(git_index *index);
+static int write_index(unsigned char checksum[GIT_HASH_MAX_SIZE], size_t *checksum_size, git_index *index, git_filebuf *file);
+
+static void index_entry_free(git_index_entry *entry);
+static void index_entry_reuc_free(git_index_reuc_entry *reuc);
+
+GIT_INLINE(int) index_map_set(git_idxmap *map, git_index_entry *e, bool ignore_case)
+{
+ if (ignore_case)
+ return git_idxmap_icase_set((git_idxmap_icase *) map, e, e);
+ else
+ return git_idxmap_set(map, e, e);
+}
+
+GIT_INLINE(int) index_map_delete(git_idxmap *map, git_index_entry *e, bool ignore_case)
+{
+ if (ignore_case)
+ return git_idxmap_icase_delete((git_idxmap_icase *) map, e);
+ else
+ return git_idxmap_delete(map, e);
+}
+
+GIT_INLINE(int) index_map_resize(git_idxmap *map, size_t count, bool ignore_case)
+{
+ if (ignore_case)
+ return git_idxmap_icase_resize((git_idxmap_icase *) map, count);
+ else
+ return git_idxmap_resize(map, count);
+}
+
+int git_index_entry_srch(const void *key, const void *array_member)
+{
+ const struct entry_srch_key *srch_key = key;
+ const struct entry_internal *entry = array_member;
+ int cmp;
+ size_t len1, len2, len;
+
+ len1 = srch_key->pathlen;
+ len2 = entry->pathlen;
+ len = len1 < len2 ? len1 : len2;
+
+ cmp = memcmp(srch_key->path, entry->path, len);
+ if (cmp)
+ return cmp;
+ if (len1 < len2)
+ return -1;
+ if (len1 > len2)
+ return 1;
+
+ if (srch_key->stage != GIT_INDEX_STAGE_ANY)
+ return srch_key->stage - GIT_INDEX_ENTRY_STAGE(&entry->entry);
+
+ return 0;
+}
+
+int git_index_entry_isrch(const void *key, const void *array_member)
+{
+ const struct entry_srch_key *srch_key = key;
+ const struct entry_internal *entry = array_member;
+ int cmp;
+ size_t len1, len2, len;
+
+ len1 = srch_key->pathlen;
+ len2 = entry->pathlen;
+ len = len1 < len2 ? len1 : len2;
+
+ cmp = strncasecmp(srch_key->path, entry->path, len);
+
+ if (cmp)
+ return cmp;
+ if (len1 < len2)
+ return -1;
+ if (len1 > len2)
+ return 1;
+
+ if (srch_key->stage != GIT_INDEX_STAGE_ANY)
+ return srch_key->stage - GIT_INDEX_ENTRY_STAGE(&entry->entry);
+
+ return 0;
+}
+
+static int index_entry_srch_path(const void *path, const void *array_member)
+{
+ const git_index_entry *entry = array_member;
+
+ return strcmp((const char *)path, entry->path);
+}
+
+static int index_entry_isrch_path(const void *path, const void *array_member)
+{
+ const git_index_entry *entry = array_member;
+
+ return strcasecmp((const char *)path, entry->path);
+}
+
+int git_index_entry_cmp(const void *a, const void *b)
+{
+ int diff;
+ const git_index_entry *entry_a = a;
+ const git_index_entry *entry_b = b;
+
+ diff = strcmp(entry_a->path, entry_b->path);
+
+ if (diff == 0)
+ diff = (GIT_INDEX_ENTRY_STAGE(entry_a) - GIT_INDEX_ENTRY_STAGE(entry_b));
+
+ return diff;
+}
+
+int git_index_entry_icmp(const void *a, const void *b)
+{
+ int diff;
+ const git_index_entry *entry_a = a;
+ const git_index_entry *entry_b = b;
+
+ diff = strcasecmp(entry_a->path, entry_b->path);
+
+ if (diff == 0)
+ diff = (GIT_INDEX_ENTRY_STAGE(entry_a) - GIT_INDEX_ENTRY_STAGE(entry_b));
+
+ return diff;
+}
+
+static int conflict_name_cmp(const void *a, const void *b)
+{
+ const git_index_name_entry *name_a = a;
+ const git_index_name_entry *name_b = b;
+
+ if (name_a->ancestor && !name_b->ancestor)
+ return 1;
+
+ if (!name_a->ancestor && name_b->ancestor)
+ return -1;
+
+ if (name_a->ancestor)
+ return strcmp(name_a->ancestor, name_b->ancestor);
+
+ if (!name_a->ours || !name_b->ours)
+ return 0;
+
+ return strcmp(name_a->ours, name_b->ours);
+}
+
+/**
+ * TODO: enable this when resolving case insensitive conflicts
+ */
+#if 0
+static int conflict_name_icmp(const void *a, const void *b)
+{
+ const git_index_name_entry *name_a = a;
+ const git_index_name_entry *name_b = b;
+
+ if (name_a->ancestor && !name_b->ancestor)
+ return 1;
+
+ if (!name_a->ancestor && name_b->ancestor)
+ return -1;
+
+ if (name_a->ancestor)
+ return strcasecmp(name_a->ancestor, name_b->ancestor);
+
+ if (!name_a->ours || !name_b->ours)
+ return 0;
+
+ return strcasecmp(name_a->ours, name_b->ours);
+}
+#endif
+
+static int reuc_srch(const void *key, const void *array_member)
+{
+ const git_index_reuc_entry *reuc = array_member;
+
+ return strcmp(key, reuc->path);
+}
+
+static int reuc_isrch(const void *key, const void *array_member)
+{
+ const git_index_reuc_entry *reuc = array_member;
+
+ return strcasecmp(key, reuc->path);
+}
+
+static int reuc_cmp(const void *a, const void *b)
+{
+ const git_index_reuc_entry *info_a = a;
+ const git_index_reuc_entry *info_b = b;
+
+ return strcmp(info_a->path, info_b->path);
+}
+
+static int reuc_icmp(const void *a, const void *b)
+{
+ const git_index_reuc_entry *info_a = a;
+ const git_index_reuc_entry *info_b = b;
+
+ return strcasecmp(info_a->path, info_b->path);
+}
+
+static void index_entry_reuc_free(git_index_reuc_entry *reuc)
+{
+ git__free(reuc);
+}
+
+static void index_entry_free(git_index_entry *entry)
+{
+ if (!entry)
+ return;
+
+ memset(&entry->id, 0, sizeof(entry->id));
+ git__free(entry);
+}
+
+unsigned int git_index__create_mode(unsigned int mode)
+{
+ if (S_ISLNK(mode))
+ return S_IFLNK;
+
+ if (S_ISDIR(mode) || (mode & S_IFMT) == (S_IFLNK | S_IFDIR))
+ return (S_IFLNK | S_IFDIR);
+
+ return S_IFREG | GIT_PERMS_CANONICAL(mode);
+}
+
+static unsigned int index_merge_mode(
+ git_index *index, git_index_entry *existing, unsigned int mode)
+{
+ if (index->no_symlinks && S_ISREG(mode) &&
+ existing && S_ISLNK(existing->mode))
+ return existing->mode;
+
+ if (index->distrust_filemode && S_ISREG(mode))
+ return (existing && S_ISREG(existing->mode)) ?
+ existing->mode : git_index__create_mode(0666);
+
+ return git_index__create_mode(mode);
+}
+
+GIT_INLINE(int) index_find_in_entries(
+ size_t *out, git_vector *entries, git_vector_cmp entry_srch,
+ const char *path, size_t path_len, int stage)
+{
+ struct entry_srch_key srch_key;
+ srch_key.path = path;
+ srch_key.pathlen = !path_len ? strlen(path) : path_len;
+ srch_key.stage = stage;
+ return git_vector_bsearch2(out, entries, entry_srch, &srch_key);
+}
+
+GIT_INLINE(int) index_find(
+ size_t *out, git_index *index,
+ const char *path, size_t path_len, int stage)
+{
+ git_vector_sort(&index->entries);
+
+ return index_find_in_entries(
+ out, &index->entries, index->entries_search, path, path_len, stage);
+}
+
+void git_index__set_ignore_case(git_index *index, bool ignore_case)
+{
+ index->ignore_case = ignore_case;
+
+ if (ignore_case) {
+ index->entries_cmp_path = git__strcasecmp_cb;
+ index->entries_search = git_index_entry_isrch;
+ index->entries_search_path = index_entry_isrch_path;
+ index->reuc_search = reuc_isrch;
+ } else {
+ index->entries_cmp_path = git__strcmp_cb;
+ index->entries_search = git_index_entry_srch;
+ index->entries_search_path = index_entry_srch_path;
+ index->reuc_search = reuc_srch;
+ }
+
+ git_vector_set_cmp(&index->entries,
+ ignore_case ? git_index_entry_icmp : git_index_entry_cmp);
+ git_vector_sort(&index->entries);
+
+ git_vector_set_cmp(&index->reuc, ignore_case ? reuc_icmp : reuc_cmp);
+ git_vector_sort(&index->reuc);
+}
+
+int git_index__open(
+ git_index **index_out,
+ const char *index_path,
+ git_oid_t oid_type)
+{
+ git_index *index;
+ int error = -1;
+
+ GIT_ASSERT_ARG(index_out);
+
+ index = git__calloc(1, sizeof(git_index));
+ GIT_ERROR_CHECK_ALLOC(index);
+
+ index->oid_type = oid_type;
+
+ if (git_pool_init(&index->tree_pool, 1) < 0)
+ goto fail;
+
+ if (index_path != NULL) {
+ index->index_file_path = git__strdup(index_path);
+ if (!index->index_file_path)
+ goto fail;
+
+ /* Check if index file is stored on disk already */
+ if (git_fs_path_exists(index->index_file_path) == true)
+ index->on_disk = 1;
+ }
+
+ if (git_vector_init(&index->entries, 32, git_index_entry_cmp) < 0 ||
+ git_idxmap_new(&index->entries_map) < 0 ||
+ git_vector_init(&index->names, 8, conflict_name_cmp) < 0 ||
+ git_vector_init(&index->reuc, 8, reuc_cmp) < 0 ||
+ git_vector_init(&index->deleted, 8, git_index_entry_cmp) < 0)
+ goto fail;
+
+ index->entries_cmp_path = git__strcmp_cb;
+ index->entries_search = git_index_entry_srch;
+ index->entries_search_path = index_entry_srch_path;
+ index->reuc_search = reuc_srch;
+ index->version = INDEX_VERSION_NUMBER_DEFAULT;
+
+ if (index_path != NULL && (error = git_index_read(index, true)) < 0)
+ goto fail;
+
+ *index_out = index;
+ GIT_REFCOUNT_INC(index);
+
+ return 0;
+
+fail:
+ git_pool_clear(&index->tree_pool);
+ git_index_free(index);
+ return error;
+}
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+int git_index_open(git_index **index_out, const char *index_path, git_oid_t oid_type)
+{
+ return git_index__open(index_out, index_path, oid_type);
+}
+#else
+int git_index_open(git_index **index_out, const char *index_path)
+{
+ return git_index__open(index_out, index_path, GIT_OID_SHA1);
+}
+#endif
+
+int git_index__new(git_index **out, git_oid_t oid_type)
+{
+ return git_index__open(out, NULL, oid_type);
+}
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+int git_index_new(git_index **out, git_oid_t oid_type)
+{
+ return git_index__new(out, oid_type);
+}
+#else
+int git_index_new(git_index **out)
+{
+ return git_index__new(out, GIT_OID_SHA1);
+}
+#endif
+
+static void index_free(git_index *index)
+{
+ /* index iterators increment the refcount of the index, so if we
+ * get here then there should be no outstanding iterators.
+ */
+ if (git_atomic32_get(&index->readers))
+ return;
+
+ git_index_clear(index);
+ git_idxmap_free(index->entries_map);
+ git_vector_free(&index->entries);
+ git_vector_free(&index->names);
+ git_vector_free(&index->reuc);
+ git_vector_free(&index->deleted);
+
+ git__free(index->index_file_path);
+
+ git__memzero(index, sizeof(*index));
+ git__free(index);
+}
+
+void git_index_free(git_index *index)
+{
+ if (index == NULL)
+ return;
+
+ GIT_REFCOUNT_DEC(index, index_free);
+}
+
+/* call with locked index */
+static void index_free_deleted(git_index *index)
+{
+ int readers = (int)git_atomic32_get(&index->readers);
+ size_t i;
+
+ if (readers > 0 || !index->deleted.length)
+ return;
+
+ for (i = 0; i < index->deleted.length; ++i) {
+ git_index_entry *ie = git_atomic_swap(index->deleted.contents[i], NULL);
+ index_entry_free(ie);
+ }
+
+ git_vector_clear(&index->deleted);
+}
+
+/* call with locked index */
+static int index_remove_entry(git_index *index, size_t pos)
+{
+ int error = 0;
+ git_index_entry *entry = git_vector_get(&index->entries, pos);
+
+ if (entry != NULL) {
+ git_tree_cache_invalidate_path(index->tree, entry->path);
+ index_map_delete(index->entries_map, entry, index->ignore_case);
+ }
+
+ error = git_vector_remove(&index->entries, pos);
+
+ if (!error) {
+ if (git_atomic32_get(&index->readers) > 0) {
+ error = git_vector_insert(&index->deleted, entry);
+ } else {
+ index_entry_free(entry);
+ }
+
+ index->dirty = 1;
+ }
+
+ return error;
+}
+
+int git_index_clear(git_index *index)
+{
+ int error = 0;
+
+ GIT_ASSERT_ARG(index);
+
+ index->dirty = 1;
+ index->tree = NULL;
+ git_pool_clear(&index->tree_pool);
+
+ git_idxmap_clear(index->entries_map);
+ while (!error && index->entries.length > 0)
+ error = index_remove_entry(index, index->entries.length - 1);
+
+ if (error)
+ goto done;
+
+ index_free_deleted(index);
+
+ if ((error = git_index_name_clear(index)) < 0 ||
+ (error = git_index_reuc_clear(index)) < 0)
+ goto done;
+
+ git_futils_filestamp_set(&index->stamp, NULL);
+
+done:
+ return error;
+}
+
+static int create_index_error(int error, const char *msg)
+{
+ git_error_set_str(GIT_ERROR_INDEX, msg);
+ return error;
+}
+
+int git_index_set_caps(git_index *index, int caps)
+{
+ unsigned int old_ignore_case;
+
+ GIT_ASSERT_ARG(index);
+
+ old_ignore_case = index->ignore_case;
+
+ if (caps == GIT_INDEX_CAPABILITY_FROM_OWNER) {
+ git_repository *repo = INDEX_OWNER(index);
+ int val;
+
+ if (!repo)
+ return create_index_error(
+ -1, "cannot access repository to set index caps");
+
+ if (!git_repository__configmap_lookup(&val, repo, GIT_CONFIGMAP_IGNORECASE))
+ index->ignore_case = (val != 0);
+ if (!git_repository__configmap_lookup(&val, repo, GIT_CONFIGMAP_FILEMODE))
+ index->distrust_filemode = (val == 0);
+ if (!git_repository__configmap_lookup(&val, repo, GIT_CONFIGMAP_SYMLINKS))
+ index->no_symlinks = (val == 0);
+ }
+ else {
+ index->ignore_case = ((caps & GIT_INDEX_CAPABILITY_IGNORE_CASE) != 0);
+ index->distrust_filemode = ((caps & GIT_INDEX_CAPABILITY_NO_FILEMODE) != 0);
+ index->no_symlinks = ((caps & GIT_INDEX_CAPABILITY_NO_SYMLINKS) != 0);
+ }
+
+ if (old_ignore_case != index->ignore_case) {
+ git_index__set_ignore_case(index, (bool)index->ignore_case);
+ }
+
+ return 0;
+}
+
+int git_index_caps(const git_index *index)
+{
+ return ((index->ignore_case ? GIT_INDEX_CAPABILITY_IGNORE_CASE : 0) |
+ (index->distrust_filemode ? GIT_INDEX_CAPABILITY_NO_FILEMODE : 0) |
+ (index->no_symlinks ? GIT_INDEX_CAPABILITY_NO_SYMLINKS : 0));
+}
+
+#ifndef GIT_DEPRECATE_HARD
+const git_oid *git_index_checksum(git_index *index)
+{
+ return (git_oid *)index->checksum;
+}
+#endif
+
+/**
+ * Returns 1 for changed, 0 for not changed and <0 for errors
+ */
+static int compare_checksum(git_index *index)
+{
+ int fd;
+ ssize_t bytes_read;
+ unsigned char checksum[GIT_HASH_MAX_SIZE];
+ size_t checksum_size = git_oid_size(index->oid_type);
+
+ if ((fd = p_open(index->index_file_path, O_RDONLY)) < 0)
+ return fd;
+
+ if (p_lseek(fd, (0 - (ssize_t)checksum_size), SEEK_END) < 0) {
+ p_close(fd);
+ git_error_set(GIT_ERROR_OS, "failed to seek to end of file");
+ return -1;
+ }
+
+ bytes_read = p_read(fd, checksum, checksum_size);
+ p_close(fd);
+
+ if (bytes_read < (ssize_t)checksum_size)
+ return -1;
+
+ return !!memcmp(checksum, index->checksum, checksum_size);
+}
+
+int git_index_read(git_index *index, int force)
+{
+ int error = 0, updated;
+ git_str buffer = GIT_STR_INIT;
+ git_futils_filestamp stamp = index->stamp;
+
+ if (!index->index_file_path)
+ return create_index_error(-1,
+ "failed to read index: The index is in-memory only");
+
+ index->on_disk = git_fs_path_exists(index->index_file_path);
+
+ if (!index->on_disk) {
+ if (force && (error = git_index_clear(index)) < 0)
+ return error;
+
+ index->dirty = 0;
+ return 0;
+ }
+
+ if ((updated = git_futils_filestamp_check(&stamp, index->index_file_path) < 0) ||
+ ((updated = compare_checksum(index)) < 0)) {
+ git_error_set(
+ GIT_ERROR_INDEX,
+ "failed to read index: '%s' no longer exists",
+ index->index_file_path);
+ return updated;
+ }
+
+ if (!updated && !force)
+ return 0;
+
+ error = git_futils_readbuffer(&buffer, index->index_file_path);
+ if (error < 0)
+ return error;
+
+ index->tree = NULL;
+ git_pool_clear(&index->tree_pool);
+
+ error = git_index_clear(index);
+
+ if (!error)
+ error = parse_index(index, buffer.ptr, buffer.size);
+
+ if (!error) {
+ git_futils_filestamp_set(&index->stamp, &stamp);
+ index->dirty = 0;
+ }
+
+ git_str_dispose(&buffer);
+ return error;
+}
+
+int git_index_read_safely(git_index *index)
+{
+ if (git_index__enforce_unsaved_safety && index->dirty) {
+ git_error_set(GIT_ERROR_INDEX,
+ "the index has unsaved changes that would be overwritten by this operation");
+ return GIT_EINDEXDIRTY;
+ }
+
+ return git_index_read(index, false);
+}
+
+static bool is_racy_entry(git_index *index, const git_index_entry *entry)
+{
+ /* Git special-cases submodules in the check */
+ if (S_ISGITLINK(entry->mode))
+ return false;
+
+ return git_index_entry_newer_than_index(entry, index);
+}
+
+/*
+ * Force the next diff to take a look at those entries which have the
+ * same timestamp as the current index.
+ */
+static int truncate_racily_clean(git_index *index)
+{
+ size_t i;
+ int error;
+ git_index_entry *entry;
+ git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ git_vector paths = GIT_VECTOR_INIT;
+ git_diff_delta *delta;
+
+ /* Nothing to do if there's no repo to talk about */
+ if (!INDEX_OWNER(index))
+ return 0;
+
+ /* If there's no workdir, we can't know where to even check */
+ if (!git_repository_workdir(INDEX_OWNER(index)))
+ return 0;
+
+ diff_opts.flags |= GIT_DIFF_INCLUDE_TYPECHANGE | GIT_DIFF_IGNORE_SUBMODULES | GIT_DIFF_DISABLE_PATHSPEC_MATCH;
+ git_vector_foreach(&index->entries, i, entry) {
+ if ((entry->flags_extended & GIT_INDEX_ENTRY_UPTODATE) == 0 &&
+ is_racy_entry(index, entry))
+ git_vector_insert(&paths, (char *)entry->path);
+ }
+
+ if (paths.length == 0)
+ goto done;
+
+ diff_opts.pathspec.count = paths.length;
+ diff_opts.pathspec.strings = (char **)paths.contents;
+
+ if ((error = git_diff_index_to_workdir(&diff, INDEX_OWNER(index), index, &diff_opts)) < 0)
+ return error;
+
+ git_vector_foreach(&diff->deltas, i, delta) {
+ entry = (git_index_entry *)git_index_get_bypath(index, delta->old_file.path, 0);
+
+ /* Ensure that we have a stage 0 for this file (ie, it's not a
+ * conflict), otherwise smudging it is quite pointless.
+ */
+ if (entry) {
+ entry->file_size = 0;
+ index->dirty = 1;
+ }
+ }
+
+done:
+ git_diff_free(diff);
+ git_vector_free(&paths);
+ return 0;
+}
+
+unsigned git_index_version(git_index *index)
+{
+ GIT_ASSERT_ARG(index);
+
+ return index->version;
+}
+
+int git_index_set_version(git_index *index, unsigned int version)
+{
+ GIT_ASSERT_ARG(index);
+
+ if (version < INDEX_VERSION_NUMBER_LB ||
+ version > INDEX_VERSION_NUMBER_UB) {
+ git_error_set(GIT_ERROR_INDEX, "invalid version number");
+ return -1;
+ }
+
+ index->version = version;
+
+ return 0;
+}
+
+int git_index_write(git_index *index)
+{
+ git_indexwriter writer = GIT_INDEXWRITER_INIT;
+ int error;
+
+ truncate_racily_clean(index);
+
+ if ((error = git_indexwriter_init(&writer, index)) == 0 &&
+ (error = git_indexwriter_commit(&writer)) == 0)
+ index->dirty = 0;
+
+ git_indexwriter_cleanup(&writer);
+
+ return error;
+}
+
+const char *git_index_path(const git_index *index)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(index, NULL);
+ return index->index_file_path;
+}
+
+int git_index_write_tree(git_oid *oid, git_index *index)
+{
+ git_repository *repo;
+
+ GIT_ASSERT_ARG(oid);
+ GIT_ASSERT_ARG(index);
+
+ repo = INDEX_OWNER(index);
+
+ if (repo == NULL)
+ return create_index_error(-1, "Failed to write tree. "
+ "the index file is not backed up by an existing repository");
+
+ return git_tree__write_index(oid, index, repo);
+}
+
+int git_index_write_tree_to(
+ git_oid *oid, git_index *index, git_repository *repo)
+{
+ GIT_ASSERT_ARG(oid);
+ GIT_ASSERT_ARG(index);
+ GIT_ASSERT_ARG(repo);
+
+ return git_tree__write_index(oid, index, repo);
+}
+
+size_t git_index_entrycount(const git_index *index)
+{
+ GIT_ASSERT_ARG(index);
+
+ return index->entries.length;
+}
+
+const git_index_entry *git_index_get_byindex(
+ git_index *index, size_t n)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(index, NULL);
+
+ git_vector_sort(&index->entries);
+ return git_vector_get(&index->entries, n);
+}
+
+const git_index_entry *git_index_get_bypath(
+ git_index *index, const char *path, int stage)
+{
+ git_index_entry key = {{ 0 }};
+ git_index_entry *value;
+
+ GIT_ASSERT_ARG_WITH_RETVAL(index, NULL);
+
+ key.path = path;
+ GIT_INDEX_ENTRY_STAGE_SET(&key, stage);
+
+ if (index->ignore_case)
+ value = git_idxmap_icase_get((git_idxmap_icase *) index->entries_map, &key);
+ else
+ value = git_idxmap_get(index->entries_map, &key);
+
+ if (!value) {
+ git_error_set(GIT_ERROR_INDEX, "index does not contain '%s'", path);
+ return NULL;
+ }
+
+ return value;
+}
+
+void git_index_entry__init_from_stat(
+ git_index_entry *entry, struct stat *st, bool trust_mode)
+{
+ entry->ctime.seconds = (int32_t)st->st_ctime;
+ entry->mtime.seconds = (int32_t)st->st_mtime;
+#if defined(GIT_USE_NSEC)
+ entry->mtime.nanoseconds = st->st_mtime_nsec;
+ entry->ctime.nanoseconds = st->st_ctime_nsec;
+#endif
+ entry->dev = st->st_rdev;
+ entry->ino = st->st_ino;
+ entry->mode = (!trust_mode && S_ISREG(st->st_mode)) ?
+ git_index__create_mode(0666) : git_index__create_mode(st->st_mode);
+ entry->uid = st->st_uid;
+ entry->gid = st->st_gid;
+ entry->file_size = (uint32_t)st->st_size;
+}
+
+static void index_entry_adjust_namemask(
+ git_index_entry *entry,
+ size_t path_length)
+{
+ entry->flags &= ~GIT_INDEX_ENTRY_NAMEMASK;
+
+ if (path_length < GIT_INDEX_ENTRY_NAMEMASK)
+ entry->flags |= path_length & GIT_INDEX_ENTRY_NAMEMASK;
+ else
+ entry->flags |= GIT_INDEX_ENTRY_NAMEMASK;
+}
+
+/* When `from_workdir` is true, we will validate the paths to avoid placing
+ * paths that are invalid for the working directory on the current filesystem
+ * (eg, on Windows, we will disallow `GIT~1`, `AUX`, `COM1`, etc). This
+ * function will *always* prevent `.git` and directory traversal `../` from
+ * being added to the index.
+ */
+static int index_entry_create(
+ git_index_entry **out,
+ git_repository *repo,
+ const char *path,
+ struct stat *st,
+ bool from_workdir)
+{
+ size_t pathlen = strlen(path), alloclen;
+ struct entry_internal *entry;
+ unsigned int path_valid_flags = GIT_PATH_REJECT_INDEX_DEFAULTS;
+ uint16_t mode = 0;
+
+ /* always reject placing `.git` in the index and directory traversal.
+ * when requested, disallow platform-specific filenames and upgrade to
+ * the platform-specific `.git` tests (eg, `git~1`, etc).
+ */
+ if (from_workdir)
+ path_valid_flags |= GIT_PATH_REJECT_WORKDIR_DEFAULTS;
+ if (st)
+ mode = st->st_mode;
+
+ if (!git_path_is_valid(repo, path, mode, path_valid_flags)) {
+ git_error_set(GIT_ERROR_INDEX, "invalid path: '%s'", path);
+ return -1;
+ }
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, sizeof(struct entry_internal), pathlen);
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
+ entry = git__calloc(1, alloclen);
+ GIT_ERROR_CHECK_ALLOC(entry);
+
+ entry->pathlen = pathlen;
+ memcpy(entry->path, path, pathlen);
+ entry->entry.path = entry->path;
+
+ *out = (git_index_entry *)entry;
+ return 0;
+}
+
+static int index_entry_init(
+ git_index_entry **entry_out,
+ git_index *index,
+ const char *rel_path)
+{
+ int error = 0;
+ git_index_entry *entry = NULL;
+ git_str path = GIT_STR_INIT;
+ struct stat st;
+ git_oid oid;
+ git_repository *repo;
+
+ if (INDEX_OWNER(index) == NULL)
+ return create_index_error(-1,
+ "could not initialize index entry. "
+ "Index is not backed up by an existing repository.");
+
+ /*
+ * FIXME: this is duplicated with the work in
+ * git_blob__create_from_paths. It should accept an optional stat
+ * structure so we can pass in the one we have to do here.
+ */
+ repo = INDEX_OWNER(index);
+ if (git_repository__ensure_not_bare(repo, "create blob from file") < 0)
+ return GIT_EBAREREPO;
+
+ if (git_repository_workdir_path(&path, repo, rel_path) < 0)
+ return -1;
+
+ error = git_fs_path_lstat(path.ptr, &st);
+ git_str_dispose(&path);
+
+ if (error < 0)
+ return error;
+
+ if (index_entry_create(&entry, INDEX_OWNER(index), rel_path, &st, true) < 0)
+ return -1;
+
+ /* write the blob to disk and get the oid and stat info */
+ error = git_blob__create_from_paths(
+ &oid, &st, INDEX_OWNER(index), NULL, rel_path, 0, true);
+
+ if (error < 0) {
+ index_entry_free(entry);
+ return error;
+ }
+
+ entry->id = oid;
+ git_index_entry__init_from_stat(entry, &st, !index->distrust_filemode);
+
+ *entry_out = (git_index_entry *)entry;
+ return 0;
+}
+
+static git_index_reuc_entry *reuc_entry_alloc(const char *path)
+{
+ size_t pathlen = strlen(path),
+ structlen = sizeof(struct reuc_entry_internal),
+ alloclen;
+ struct reuc_entry_internal *entry;
+
+ if (GIT_ADD_SIZET_OVERFLOW(&alloclen, structlen, pathlen) ||
+ GIT_ADD_SIZET_OVERFLOW(&alloclen, alloclen, 1))
+ return NULL;
+
+ entry = git__calloc(1, alloclen);
+ if (!entry)
+ return NULL;
+
+ entry->pathlen = pathlen;
+ memcpy(entry->path, path, pathlen);
+ entry->entry.path = entry->path;
+
+ return (git_index_reuc_entry *)entry;
+}
+
+static int index_entry_reuc_init(git_index_reuc_entry **reuc_out,
+ const char *path,
+ int ancestor_mode, const git_oid *ancestor_oid,
+ int our_mode, const git_oid *our_oid,
+ int their_mode, const git_oid *their_oid)
+{
+ git_index_reuc_entry *reuc = NULL;
+
+ GIT_ASSERT_ARG(reuc_out);
+ GIT_ASSERT_ARG(path);
+
+ *reuc_out = reuc = reuc_entry_alloc(path);
+ GIT_ERROR_CHECK_ALLOC(reuc);
+
+ if ((reuc->mode[0] = ancestor_mode) > 0) {
+ GIT_ASSERT(ancestor_oid);
+ git_oid_cpy(&reuc->oid[0], ancestor_oid);
+ }
+
+ if ((reuc->mode[1] = our_mode) > 0) {
+ GIT_ASSERT(our_oid);
+ git_oid_cpy(&reuc->oid[1], our_oid);
+ }
+
+ if ((reuc->mode[2] = their_mode) > 0) {
+ GIT_ASSERT(their_oid);
+ git_oid_cpy(&reuc->oid[2], their_oid);
+ }
+
+ return 0;
+}
+
+static void index_entry_cpy(
+ git_index_entry *tgt,
+ const git_index_entry *src)
+{
+ const char *tgt_path = tgt->path;
+ memcpy(tgt, src, sizeof(*tgt));
+ tgt->path = tgt_path;
+}
+
+static int index_entry_dup(
+ git_index_entry **out,
+ git_index *index,
+ const git_index_entry *src)
+{
+ if (index_entry_create(out, INDEX_OWNER(index), src->path, NULL, false) < 0)
+ return -1;
+
+ index_entry_cpy(*out, src);
+ return 0;
+}
+
+static void index_entry_cpy_nocache(
+ git_index_entry *tgt,
+ const git_index_entry *src)
+{
+ git_oid_cpy(&tgt->id, &src->id);
+ tgt->mode = src->mode;
+ tgt->flags = src->flags;
+ tgt->flags_extended = (src->flags_extended & GIT_INDEX_ENTRY_EXTENDED_FLAGS);
+}
+
+static int index_entry_dup_nocache(
+ git_index_entry **out,
+ git_index *index,
+ const git_index_entry *src)
+{
+ if (index_entry_create(out, INDEX_OWNER(index), src->path, NULL, false) < 0)
+ return -1;
+
+ index_entry_cpy_nocache(*out, src);
+ return 0;
+}
+
+static int has_file_name(git_index *index,
+ const git_index_entry *entry, size_t pos, int ok_to_replace)
+{
+ size_t len = strlen(entry->path);
+ int stage = GIT_INDEX_ENTRY_STAGE(entry);
+ const char *name = entry->path;
+
+ while (pos < index->entries.length) {
+ struct entry_internal *p = index->entries.contents[pos++];
+
+ if (len >= p->pathlen)
+ break;
+ if (memcmp(name, p->path, len))
+ break;
+ if (GIT_INDEX_ENTRY_STAGE(&p->entry) != stage)
+ continue;
+ if (p->path[len] != '/')
+ continue;
+ if (!ok_to_replace)
+ return -1;
+
+ if (index_remove_entry(index, --pos) < 0)
+ break;
+ }
+ return 0;
+}
+
+/*
+ * Do we have another file with a pathname that is a proper
+ * subset of the name we're trying to add?
+ */
+static int has_dir_name(git_index *index,
+ const git_index_entry *entry, int ok_to_replace)
+{
+ int stage = GIT_INDEX_ENTRY_STAGE(entry);
+ const char *name = entry->path;
+ const char *slash = name + strlen(name);
+
+ for (;;) {
+ size_t len, pos;
+
+ for (;;) {
+ slash--;
+
+ if (slash <= entry->path)
+ return 0;
+
+ if (*slash == '/')
+ break;
+ }
+ len = slash - name;
+
+ if (!index_find(&pos, index, name, len, stage)) {
+ if (!ok_to_replace)
+ return -1;
+
+ if (index_remove_entry(index, pos) < 0)
+ break;
+ continue;
+ }
+
+ /*
+ * Trivial optimization: if we find an entry that
+ * already matches the sub-directory, then we know
+ * we're ok, and we can exit.
+ */
+ for (; pos < index->entries.length; ++pos) {
+ struct entry_internal *p = index->entries.contents[pos];
+
+ if (p->pathlen <= len ||
+ p->path[len] != '/' ||
+ memcmp(p->path, name, len))
+ break; /* not our subdirectory */
+
+ if (GIT_INDEX_ENTRY_STAGE(&p->entry) == stage)
+ return 0;
+ }
+ }
+
+ return 0;
+}
+
+static int check_file_directory_collision(git_index *index,
+ git_index_entry *entry, size_t pos, int ok_to_replace)
+{
+ if (has_file_name(index, entry, pos, ok_to_replace) < 0 ||
+ has_dir_name(index, entry, ok_to_replace) < 0) {
+ git_error_set(GIT_ERROR_INDEX,
+ "'%s' appears as both a file and a directory", entry->path);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int canonicalize_directory_path(
+ git_index *index,
+ git_index_entry *entry,
+ git_index_entry *existing)
+{
+ const git_index_entry *match, *best = NULL;
+ char *search, *sep;
+ size_t pos, search_len, best_len;
+
+ if (!index->ignore_case)
+ return 0;
+
+ /* item already exists in the index, simply re-use the existing case */
+ if (existing) {
+ memcpy((char *)entry->path, existing->path, strlen(existing->path));
+ return 0;
+ }
+
+ /* nothing to do */
+ if (strchr(entry->path, '/') == NULL)
+ return 0;
+
+ if ((search = git__strdup(entry->path)) == NULL)
+ return -1;
+
+ /* starting at the parent directory and descending to the root, find the
+ * common parent directory.
+ */
+ while (!best && (sep = strrchr(search, '/'))) {
+ sep[1] = '\0';
+
+ search_len = strlen(search);
+
+ git_vector_bsearch2(
+ &pos, &index->entries, index->entries_search_path, search);
+
+ while ((match = git_vector_get(&index->entries, pos))) {
+ if (GIT_INDEX_ENTRY_STAGE(match) != 0) {
+ /* conflicts do not contribute to canonical paths */
+ } else if (strncmp(search, match->path, search_len) == 0) {
+ /* prefer an exact match to the input filename */
+ best = match;
+ best_len = search_len;
+ break;
+ } else if (strncasecmp(search, match->path, search_len) == 0) {
+ /* continue walking, there may be a path with an exact
+ * (case sensitive) match later in the index, but use this
+ * as the best match until that happens.
+ */
+ if (!best) {
+ best = match;
+ best_len = search_len;
+ }
+ } else {
+ break;
+ }
+
+ pos++;
+ }
+
+ sep[0] = '\0';
+ }
+
+ if (best)
+ memcpy((char *)entry->path, best->path, best_len);
+
+ git__free(search);
+ return 0;
+}
+
+static int index_no_dups(void **old, void *new)
+{
+ const git_index_entry *entry = new;
+ GIT_UNUSED(old);
+ git_error_set(GIT_ERROR_INDEX, "'%s' appears multiple times at stage %d",
+ entry->path, GIT_INDEX_ENTRY_STAGE(entry));
+ return GIT_EEXISTS;
+}
+
+static void index_existing_and_best(
+ git_index_entry **existing,
+ size_t *existing_position,
+ git_index_entry **best,
+ git_index *index,
+ const git_index_entry *entry)
+{
+ git_index_entry *e;
+ size_t pos;
+ int error;
+
+ error = index_find(&pos,
+ index, entry->path, 0, GIT_INDEX_ENTRY_STAGE(entry));
+
+ if (error == 0) {
+ *existing = index->entries.contents[pos];
+ *existing_position = pos;
+ *best = index->entries.contents[pos];
+ return;
+ }
+
+ *existing = NULL;
+ *existing_position = 0;
+ *best = NULL;
+
+ if (GIT_INDEX_ENTRY_STAGE(entry) == 0) {
+ for (; pos < index->entries.length; pos++) {
+ int (*strcomp)(const char *a, const char *b) =
+ index->ignore_case ? git__strcasecmp : git__strcmp;
+
+ e = index->entries.contents[pos];
+
+ if (strcomp(entry->path, e->path) != 0)
+ break;
+
+ if (GIT_INDEX_ENTRY_STAGE(e) == GIT_INDEX_STAGE_ANCESTOR) {
+ *best = e;
+ continue;
+ } else {
+ *best = e;
+ break;
+ }
+ }
+ }
+}
+
+/* index_insert takes ownership of the new entry - if it can't insert
+ * it, then it will return an error **and also free the entry**. When
+ * it replaces an existing entry, it will update the entry_ptr with the
+ * actual entry in the index (and free the passed in one).
+ *
+ * trust_path is whether we use the given path, or whether (on case
+ * insensitive systems only) we try to canonicalize the given path to
+ * be within an existing directory.
+ *
+ * trust_mode is whether we trust the mode in entry_ptr.
+ *
+ * trust_id is whether we trust the id or it should be validated.
+ */
+static int index_insert(
+ git_index *index,
+ git_index_entry **entry_ptr,
+ int replace,
+ bool trust_path,
+ bool trust_mode,
+ bool trust_id)
+{
+ git_index_entry *existing, *best, *entry;
+ size_t path_length, position;
+ int error;
+
+ GIT_ASSERT_ARG(index);
+ GIT_ASSERT_ARG(entry_ptr);
+
+ entry = *entry_ptr;
+
+ /* Make sure that the path length flag is correct */
+ path_length = ((struct entry_internal *)entry)->pathlen;
+ index_entry_adjust_namemask(entry, path_length);
+
+ /* This entry is now up-to-date and should not be checked for raciness */
+ entry->flags_extended |= GIT_INDEX_ENTRY_UPTODATE;
+
+ git_vector_sort(&index->entries);
+
+ /*
+ * Look if an entry with this path already exists, either staged, or (if
+ * this entry is a regular staged item) as the "ours" side of a conflict.
+ */
+ index_existing_and_best(&existing, &position, &best, index, entry);
+
+ /* Update the file mode */
+ entry->mode = trust_mode ?
+ git_index__create_mode(entry->mode) :
+ index_merge_mode(index, best, entry->mode);
+
+ /* Canonicalize the directory name */
+ if (!trust_path && (error = canonicalize_directory_path(index, entry, best)) < 0)
+ goto out;
+
+ /* Ensure that the given id exists (unless it's a submodule) */
+ if (!trust_id && INDEX_OWNER(index) &&
+ (entry->mode & GIT_FILEMODE_COMMIT) != GIT_FILEMODE_COMMIT) {
+
+ if (!git_object__is_valid(INDEX_OWNER(index), &entry->id,
+ git_object__type_from_filemode(entry->mode))) {
+ error = -1;
+ goto out;
+ }
+ }
+
+ /* Look for tree / blob name collisions, removing conflicts if requested */
+ if ((error = check_file_directory_collision(index, entry, position, replace)) < 0)
+ goto out;
+
+ /*
+ * If we are replacing an existing item, overwrite the existing entry
+ * and return it in place of the passed in one.
+ */
+ if (existing) {
+ if (replace) {
+ index_entry_cpy(existing, entry);
+
+ if (trust_path)
+ memcpy((char *)existing->path, entry->path, strlen(entry->path));
+ }
+
+ index_entry_free(entry);
+ *entry_ptr = existing;
+ } else {
+ /*
+ * If replace is not requested or no existing entry exists, insert
+ * at the sorted position. (Since we re-sort after each insert to
+ * check for dups, this is actually cheaper in the long run.)
+ */
+ if ((error = git_vector_insert_sorted(&index->entries, entry, index_no_dups)) < 0 ||
+ (error = index_map_set(index->entries_map, entry, index->ignore_case)) < 0)
+ goto out;
+ }
+
+ index->dirty = 1;
+
+out:
+ if (error < 0) {
+ index_entry_free(*entry_ptr);
+ *entry_ptr = NULL;
+ }
+
+ return error;
+}
+
+static int index_conflict_to_reuc(git_index *index, const char *path)
+{
+ const git_index_entry *conflict_entries[3];
+ int ancestor_mode, our_mode, their_mode;
+ git_oid const *ancestor_oid, *our_oid, *their_oid;
+ int ret;
+
+ if ((ret = git_index_conflict_get(&conflict_entries[0],
+ &conflict_entries[1], &conflict_entries[2], index, path)) < 0)
+ return ret;
+
+ ancestor_mode = conflict_entries[0] == NULL ? 0 : conflict_entries[0]->mode;
+ our_mode = conflict_entries[1] == NULL ? 0 : conflict_entries[1]->mode;
+ their_mode = conflict_entries[2] == NULL ? 0 : conflict_entries[2]->mode;
+
+ ancestor_oid = conflict_entries[0] == NULL ? NULL : &conflict_entries[0]->id;
+ our_oid = conflict_entries[1] == NULL ? NULL : &conflict_entries[1]->id;
+ their_oid = conflict_entries[2] == NULL ? NULL : &conflict_entries[2]->id;
+
+ if ((ret = git_index_reuc_add(index, path, ancestor_mode, ancestor_oid,
+ our_mode, our_oid, their_mode, their_oid)) >= 0)
+ ret = git_index_conflict_remove(index, path);
+
+ return ret;
+}
+
+GIT_INLINE(bool) is_file_or_link(const int filemode)
+{
+ return (filemode == GIT_FILEMODE_BLOB ||
+ filemode == GIT_FILEMODE_BLOB_EXECUTABLE ||
+ filemode == GIT_FILEMODE_LINK);
+}
+
+GIT_INLINE(bool) valid_filemode(const int filemode)
+{
+ return (is_file_or_link(filemode) || filemode == GIT_FILEMODE_COMMIT);
+}
+
+int git_index_add_from_buffer(
+ git_index *index, const git_index_entry *source_entry,
+ const void *buffer, size_t len)
+{
+ git_index_entry *entry = NULL;
+ int error = 0;
+ git_oid id;
+
+ GIT_ASSERT_ARG(index);
+ GIT_ASSERT_ARG(source_entry && source_entry->path);
+
+ if (INDEX_OWNER(index) == NULL)
+ return create_index_error(-1,
+ "could not initialize index entry. "
+ "Index is not backed up by an existing repository.");
+
+ if (!is_file_or_link(source_entry->mode)) {
+ git_error_set(GIT_ERROR_INDEX, "invalid filemode");
+ return -1;
+ }
+
+ if (len > UINT32_MAX) {
+ git_error_set(GIT_ERROR_INDEX, "buffer is too large");
+ return -1;
+ }
+
+ if (index_entry_dup(&entry, index, source_entry) < 0)
+ return -1;
+
+ error = git_blob_create_from_buffer(&id, INDEX_OWNER(index), buffer, len);
+ if (error < 0) {
+ index_entry_free(entry);
+ return error;
+ }
+
+ git_oid_cpy(&entry->id, &id);
+ entry->file_size = (uint32_t)len;
+
+ if ((error = index_insert(index, &entry, 1, true, true, true)) < 0)
+ return error;
+
+ /* Adding implies conflict was resolved, move conflict entries to REUC */
+ if ((error = index_conflict_to_reuc(index, entry->path)) < 0 && error != GIT_ENOTFOUND)
+ return error;
+
+ git_tree_cache_invalidate_path(index->tree, entry->path);
+ return 0;
+}
+
+static int add_repo_as_submodule(git_index_entry **out, git_index *index, const char *path)
+{
+ git_repository *sub;
+ git_str abspath = GIT_STR_INIT;
+ git_repository *repo = INDEX_OWNER(index);
+ git_reference *head;
+ git_index_entry *entry;
+ struct stat st;
+ int error;
+
+ if ((error = git_repository_workdir_path(&abspath, repo, path)) < 0)
+ return error;
+
+ if ((error = p_stat(abspath.ptr, &st)) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to stat repository dir");
+ return -1;
+ }
+
+ if (index_entry_create(&entry, INDEX_OWNER(index), path, &st, true) < 0)
+ return -1;
+
+ git_index_entry__init_from_stat(entry, &st, !index->distrust_filemode);
+
+ if ((error = git_repository_open(&sub, abspath.ptr)) < 0)
+ return error;
+
+ if ((error = git_repository_head(&head, sub)) < 0)
+ return error;
+
+ git_oid_cpy(&entry->id, git_reference_target(head));
+ entry->mode = GIT_FILEMODE_COMMIT;
+
+ git_reference_free(head);
+ git_repository_free(sub);
+ git_str_dispose(&abspath);
+
+ *out = entry;
+ return 0;
+}
+
+int git_index_add_bypath(git_index *index, const char *path)
+{
+ git_index_entry *entry = NULL;
+ int ret;
+
+ GIT_ASSERT_ARG(index);
+ GIT_ASSERT_ARG(path);
+
+ if ((ret = index_entry_init(&entry, index, path)) == 0)
+ ret = index_insert(index, &entry, 1, false, false, true);
+
+ /* If we were given a directory, let's see if it's a submodule */
+ if (ret < 0 && ret != GIT_EDIRECTORY)
+ return ret;
+
+ if (ret == GIT_EDIRECTORY) {
+ git_submodule *sm;
+ git_error_state err;
+
+ git_error_state_capture(&err, ret);
+
+ ret = git_submodule_lookup(&sm, INDEX_OWNER(index), path);
+ if (ret == GIT_ENOTFOUND)
+ return git_error_state_restore(&err);
+
+ git_error_state_free(&err);
+
+ /*
+ * EEXISTS means that there is a repository at that path, but it's not known
+ * as a submodule. We add its HEAD as an entry and don't register it.
+ */
+ if (ret == GIT_EEXISTS) {
+ if ((ret = add_repo_as_submodule(&entry, index, path)) < 0)
+ return ret;
+
+ if ((ret = index_insert(index, &entry, 1, false, false, true)) < 0)
+ return ret;
+ } else if (ret < 0) {
+ return ret;
+ } else {
+ ret = git_submodule_add_to_index(sm, false);
+ git_submodule_free(sm);
+ return ret;
+ }
+ }
+
+ /* Adding implies conflict was resolved, move conflict entries to REUC */
+ if ((ret = index_conflict_to_reuc(index, path)) < 0 && ret != GIT_ENOTFOUND)
+ return ret;
+
+ git_tree_cache_invalidate_path(index->tree, entry->path);
+ return 0;
+}
+
+int git_index_remove_bypath(git_index *index, const char *path)
+{
+ int ret;
+
+ GIT_ASSERT_ARG(index);
+ GIT_ASSERT_ARG(path);
+
+ if (((ret = git_index_remove(index, path, 0)) < 0 &&
+ ret != GIT_ENOTFOUND) ||
+ ((ret = index_conflict_to_reuc(index, path)) < 0 &&
+ ret != GIT_ENOTFOUND))
+ return ret;
+
+ if (ret == GIT_ENOTFOUND)
+ git_error_clear();
+
+ return 0;
+}
+
+int git_index__fill(git_index *index, const git_vector *source_entries)
+{
+ const git_index_entry *source_entry = NULL;
+ int error = 0;
+ size_t i;
+
+ GIT_ASSERT_ARG(index);
+
+ if (!source_entries->length)
+ return 0;
+
+ if (git_vector_size_hint(&index->entries, source_entries->length) < 0 ||
+ index_map_resize(index->entries_map, (size_t)(source_entries->length * 1.3),
+ index->ignore_case) < 0)
+ return -1;
+
+ git_vector_foreach(source_entries, i, source_entry) {
+ git_index_entry *entry = NULL;
+
+ if ((error = index_entry_dup(&entry, index, source_entry)) < 0)
+ break;
+
+ index_entry_adjust_namemask(entry, ((struct entry_internal *)entry)->pathlen);
+ entry->flags_extended |= GIT_INDEX_ENTRY_UPTODATE;
+ entry->mode = git_index__create_mode(entry->mode);
+
+ if ((error = git_vector_insert(&index->entries, entry)) < 0)
+ break;
+
+ if ((error = index_map_set(index->entries_map, entry, index->ignore_case)) < 0)
+ break;
+
+ index->dirty = 1;
+ }
+
+ if (!error)
+ git_vector_sort(&index->entries);
+
+ return error;
+}
+
+
+int git_index_add(git_index *index, const git_index_entry *source_entry)
+{
+ git_index_entry *entry = NULL;
+ int ret;
+
+ GIT_ASSERT_ARG(index);
+ GIT_ASSERT_ARG(source_entry && source_entry->path);
+
+ if (!valid_filemode(source_entry->mode)) {
+ git_error_set(GIT_ERROR_INDEX, "invalid entry mode");
+ return -1;
+ }
+
+ if ((ret = index_entry_dup(&entry, index, source_entry)) < 0 ||
+ (ret = index_insert(index, &entry, 1, true, true, false)) < 0)
+ return ret;
+
+ git_tree_cache_invalidate_path(index->tree, entry->path);
+ return 0;
+}
+
+int git_index_remove(git_index *index, const char *path, int stage)
+{
+ int error;
+ size_t position;
+ git_index_entry remove_key = {{ 0 }};
+
+ remove_key.path = path;
+ GIT_INDEX_ENTRY_STAGE_SET(&remove_key, stage);
+
+ index_map_delete(index->entries_map, &remove_key, index->ignore_case);
+
+ if (index_find(&position, index, path, 0, stage) < 0) {
+ git_error_set(
+ GIT_ERROR_INDEX, "index does not contain %s at stage %d", path, stage);
+ error = GIT_ENOTFOUND;
+ } else {
+ error = index_remove_entry(index, position);
+ }
+
+ return error;
+}
+
+int git_index_remove_directory(git_index *index, const char *dir, int stage)
+{
+ git_str pfx = GIT_STR_INIT;
+ int error = 0;
+ size_t pos;
+ git_index_entry *entry;
+
+ if (!(error = git_str_sets(&pfx, dir)) &&
+ !(error = git_fs_path_to_dir(&pfx)))
+ index_find(&pos, index, pfx.ptr, pfx.size, GIT_INDEX_STAGE_ANY);
+
+ while (!error) {
+ entry = git_vector_get(&index->entries, pos);
+ if (!entry || git__prefixcmp(entry->path, pfx.ptr) != 0)
+ break;
+
+ if (GIT_INDEX_ENTRY_STAGE(entry) != stage) {
+ ++pos;
+ continue;
+ }
+
+ error = index_remove_entry(index, pos);
+
+ /* removed entry at 'pos' so we don't need to increment */
+ }
+
+ git_str_dispose(&pfx);
+
+ return error;
+}
+
+int git_index_find_prefix(size_t *at_pos, git_index *index, const char *prefix)
+{
+ int error = 0;
+ size_t pos;
+ const git_index_entry *entry;
+
+ index_find(&pos, index, prefix, strlen(prefix), GIT_INDEX_STAGE_ANY);
+ entry = git_vector_get(&index->entries, pos);
+ if (!entry || git__prefixcmp(entry->path, prefix) != 0)
+ error = GIT_ENOTFOUND;
+
+ if (!error && at_pos)
+ *at_pos = pos;
+
+ return error;
+}
+
+int git_index__find_pos(
+ size_t *out, git_index *index, const char *path, size_t path_len, int stage)
+{
+ GIT_ASSERT_ARG(index);
+ GIT_ASSERT_ARG(path);
+ return index_find(out, index, path, path_len, stage);
+}
+
+int git_index_find(size_t *at_pos, git_index *index, const char *path)
+{
+ size_t pos;
+
+ GIT_ASSERT_ARG(index);
+ GIT_ASSERT_ARG(path);
+
+ if (git_vector_bsearch2(
+ &pos, &index->entries, index->entries_search_path, path) < 0) {
+ git_error_set(GIT_ERROR_INDEX, "index does not contain %s", path);
+ return GIT_ENOTFOUND;
+ }
+
+ /* Since our binary search only looked at path, we may be in the
+ * middle of a list of stages.
+ */
+ for (; pos > 0; --pos) {
+ const git_index_entry *prev = git_vector_get(&index->entries, pos - 1);
+
+ if (index->entries_cmp_path(prev->path, path) != 0)
+ break;
+ }
+
+ if (at_pos)
+ *at_pos = pos;
+
+ return 0;
+}
+
+int git_index_conflict_add(git_index *index,
+ const git_index_entry *ancestor_entry,
+ const git_index_entry *our_entry,
+ const git_index_entry *their_entry)
+{
+ git_index_entry *entries[3] = { 0 };
+ unsigned short i;
+ int ret = 0;
+
+ GIT_ASSERT_ARG(index);
+
+ if ((ancestor_entry &&
+ (ret = index_entry_dup(&entries[0], index, ancestor_entry)) < 0) ||
+ (our_entry &&
+ (ret = index_entry_dup(&entries[1], index, our_entry)) < 0) ||
+ (their_entry &&
+ (ret = index_entry_dup(&entries[2], index, their_entry)) < 0))
+ goto on_error;
+
+ /* Validate entries */
+ for (i = 0; i < 3; i++) {
+ if (entries[i] && !valid_filemode(entries[i]->mode)) {
+ git_error_set(GIT_ERROR_INDEX, "invalid filemode for stage %d entry",
+ i + 1);
+ ret = -1;
+ goto on_error;
+ }
+ }
+
+ /* Remove existing index entries for each path */
+ for (i = 0; i < 3; i++) {
+ if (entries[i] == NULL)
+ continue;
+
+ if ((ret = git_index_remove(index, entries[i]->path, 0)) != 0) {
+ if (ret != GIT_ENOTFOUND)
+ goto on_error;
+
+ git_error_clear();
+ ret = 0;
+ }
+ }
+
+ /* Add the conflict entries */
+ for (i = 0; i < 3; i++) {
+ if (entries[i] == NULL)
+ continue;
+
+ /* Make sure stage is correct */
+ GIT_INDEX_ENTRY_STAGE_SET(entries[i], i + 1);
+
+ if ((ret = index_insert(index, &entries[i], 1, true, true, false)) < 0)
+ goto on_error;
+
+ entries[i] = NULL; /* don't free if later entry fails */
+ }
+
+ return 0;
+
+on_error:
+ for (i = 0; i < 3; i++) {
+ if (entries[i] != NULL)
+ index_entry_free(entries[i]);
+ }
+
+ return ret;
+}
+
+static int index_conflict__get_byindex(
+ const git_index_entry **ancestor_out,
+ const git_index_entry **our_out,
+ const git_index_entry **their_out,
+ git_index *index,
+ size_t n)
+{
+ const git_index_entry *conflict_entry;
+ const char *path = NULL;
+ size_t count;
+ int stage, len = 0;
+
+ GIT_ASSERT_ARG(ancestor_out);
+ GIT_ASSERT_ARG(our_out);
+ GIT_ASSERT_ARG(their_out);
+ GIT_ASSERT_ARG(index);
+
+ *ancestor_out = NULL;
+ *our_out = NULL;
+ *their_out = NULL;
+
+ for (count = git_index_entrycount(index); n < count; ++n) {
+ conflict_entry = git_vector_get(&index->entries, n);
+
+ if (path && index->entries_cmp_path(conflict_entry->path, path) != 0)
+ break;
+
+ stage = GIT_INDEX_ENTRY_STAGE(conflict_entry);
+ path = conflict_entry->path;
+
+ switch (stage) {
+ case 3:
+ *their_out = conflict_entry;
+ len++;
+ break;
+ case 2:
+ *our_out = conflict_entry;
+ len++;
+ break;
+ case 1:
+ *ancestor_out = conflict_entry;
+ len++;
+ break;
+ default:
+ break;
+ };
+ }
+
+ return len;
+}
+
+int git_index_conflict_get(
+ const git_index_entry **ancestor_out,
+ const git_index_entry **our_out,
+ const git_index_entry **their_out,
+ git_index *index,
+ const char *path)
+{
+ size_t pos;
+ int len = 0;
+
+ GIT_ASSERT_ARG(ancestor_out);
+ GIT_ASSERT_ARG(our_out);
+ GIT_ASSERT_ARG(their_out);
+ GIT_ASSERT_ARG(index);
+ GIT_ASSERT_ARG(path);
+
+ *ancestor_out = NULL;
+ *our_out = NULL;
+ *their_out = NULL;
+
+ if (git_index_find(&pos, index, path) < 0)
+ return GIT_ENOTFOUND;
+
+ if ((len = index_conflict__get_byindex(
+ ancestor_out, our_out, their_out, index, pos)) < 0)
+ return len;
+ else if (len == 0)
+ return GIT_ENOTFOUND;
+
+ return 0;
+}
+
+static int index_conflict_remove(git_index *index, const char *path)
+{
+ size_t pos = 0;
+ git_index_entry *conflict_entry;
+ int error = 0;
+
+ if (path != NULL && git_index_find(&pos, index, path) < 0)
+ return GIT_ENOTFOUND;
+
+ while ((conflict_entry = git_vector_get(&index->entries, pos)) != NULL) {
+
+ if (path != NULL &&
+ index->entries_cmp_path(conflict_entry->path, path) != 0)
+ break;
+
+ if (GIT_INDEX_ENTRY_STAGE(conflict_entry) == 0) {
+ pos++;
+ continue;
+ }
+
+ if ((error = index_remove_entry(index, pos)) < 0)
+ break;
+ }
+
+ return error;
+}
+
+int git_index_conflict_remove(git_index *index, const char *path)
+{
+ GIT_ASSERT_ARG(index);
+ GIT_ASSERT_ARG(path);
+ return index_conflict_remove(index, path);
+}
+
+int git_index_conflict_cleanup(git_index *index)
+{
+ GIT_ASSERT_ARG(index);
+ return index_conflict_remove(index, NULL);
+}
+
+int git_index_has_conflicts(const git_index *index)
+{
+ size_t i;
+ git_index_entry *entry;
+
+ GIT_ASSERT_ARG(index);
+
+ git_vector_foreach(&index->entries, i, entry) {
+ if (GIT_INDEX_ENTRY_STAGE(entry) > 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+int git_index_iterator_new(
+ git_index_iterator **iterator_out,
+ git_index *index)
+{
+ git_index_iterator *it;
+ int error;
+
+ GIT_ASSERT_ARG(iterator_out);
+ GIT_ASSERT_ARG(index);
+
+ it = git__calloc(1, sizeof(git_index_iterator));
+ GIT_ERROR_CHECK_ALLOC(it);
+
+ if ((error = git_index_snapshot_new(&it->snap, index)) < 0) {
+ git__free(it);
+ return error;
+ }
+
+ it->index = index;
+
+ *iterator_out = it;
+ return 0;
+}
+
+int git_index_iterator_next(
+ const git_index_entry **out,
+ git_index_iterator *it)
+{
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(it);
+
+ if (it->cur >= git_vector_length(&it->snap))
+ return GIT_ITEROVER;
+
+ *out = (git_index_entry *)git_vector_get(&it->snap, it->cur++);
+ return 0;
+}
+
+void git_index_iterator_free(git_index_iterator *it)
+{
+ if (it == NULL)
+ return;
+
+ git_index_snapshot_release(&it->snap, it->index);
+ git__free(it);
+}
+
+int git_index_conflict_iterator_new(
+ git_index_conflict_iterator **iterator_out,
+ git_index *index)
+{
+ git_index_conflict_iterator *it = NULL;
+
+ GIT_ASSERT_ARG(iterator_out);
+ GIT_ASSERT_ARG(index);
+
+ it = git__calloc(1, sizeof(git_index_conflict_iterator));
+ GIT_ERROR_CHECK_ALLOC(it);
+
+ it->index = index;
+
+ *iterator_out = it;
+ return 0;
+}
+
+int git_index_conflict_next(
+ const git_index_entry **ancestor_out,
+ const git_index_entry **our_out,
+ const git_index_entry **their_out,
+ git_index_conflict_iterator *iterator)
+{
+ const git_index_entry *entry;
+ int len;
+
+ GIT_ASSERT_ARG(ancestor_out);
+ GIT_ASSERT_ARG(our_out);
+ GIT_ASSERT_ARG(their_out);
+ GIT_ASSERT_ARG(iterator);
+
+ *ancestor_out = NULL;
+ *our_out = NULL;
+ *their_out = NULL;
+
+ while (iterator->cur < iterator->index->entries.length) {
+ entry = git_index_get_byindex(iterator->index, iterator->cur);
+
+ if (git_index_entry_is_conflict(entry)) {
+ if ((len = index_conflict__get_byindex(
+ ancestor_out,
+ our_out,
+ their_out,
+ iterator->index,
+ iterator->cur)) < 0)
+ return len;
+
+ iterator->cur += len;
+ return 0;
+ }
+
+ iterator->cur++;
+ }
+
+ return GIT_ITEROVER;
+}
+
+void git_index_conflict_iterator_free(git_index_conflict_iterator *iterator)
+{
+ if (iterator == NULL)
+ return;
+
+ git__free(iterator);
+}
+
+size_t git_index_name_entrycount(git_index *index)
+{
+ GIT_ASSERT_ARG(index);
+ return index->names.length;
+}
+
+const git_index_name_entry *git_index_name_get_byindex(
+ git_index *index, size_t n)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(index, NULL);
+
+ git_vector_sort(&index->names);
+ return git_vector_get(&index->names, n);
+}
+
+static void index_name_entry_free(git_index_name_entry *ne)
+{
+ if (!ne)
+ return;
+ git__free(ne->ancestor);
+ git__free(ne->ours);
+ git__free(ne->theirs);
+ git__free(ne);
+}
+
+int git_index_name_add(git_index *index,
+ const char *ancestor, const char *ours, const char *theirs)
+{
+ git_index_name_entry *conflict_name;
+
+ GIT_ASSERT_ARG((ancestor && ours) || (ancestor && theirs) || (ours && theirs));
+
+ conflict_name = git__calloc(1, sizeof(git_index_name_entry));
+ GIT_ERROR_CHECK_ALLOC(conflict_name);
+
+ if ((ancestor && !(conflict_name->ancestor = git__strdup(ancestor))) ||
+ (ours && !(conflict_name->ours = git__strdup(ours))) ||
+ (theirs && !(conflict_name->theirs = git__strdup(theirs))) ||
+ git_vector_insert(&index->names, conflict_name) < 0)
+ {
+ index_name_entry_free(conflict_name);
+ return -1;
+ }
+
+ index->dirty = 1;
+ return 0;
+}
+
+int git_index_name_clear(git_index *index)
+{
+ size_t i;
+ git_index_name_entry *conflict_name;
+
+ GIT_ASSERT_ARG(index);
+
+ git_vector_foreach(&index->names, i, conflict_name)
+ index_name_entry_free(conflict_name);
+
+ git_vector_clear(&index->names);
+
+ index->dirty = 1;
+
+ return 0;
+}
+
+size_t git_index_reuc_entrycount(git_index *index)
+{
+ GIT_ASSERT_ARG(index);
+ return index->reuc.length;
+}
+
+static int index_reuc_on_dup(void **old, void *new)
+{
+ index_entry_reuc_free(*old);
+ *old = new;
+ return GIT_EEXISTS;
+}
+
+static int index_reuc_insert(
+ git_index *index,
+ git_index_reuc_entry *reuc)
+{
+ int res;
+
+ GIT_ASSERT_ARG(index);
+ GIT_ASSERT_ARG(reuc && reuc->path != NULL);
+ GIT_ASSERT(git_vector_is_sorted(&index->reuc));
+
+ res = git_vector_insert_sorted(&index->reuc, reuc, &index_reuc_on_dup);
+ index->dirty = 1;
+
+ return res == GIT_EEXISTS ? 0 : res;
+}
+
+int git_index_reuc_add(git_index *index, const char *path,
+ int ancestor_mode, const git_oid *ancestor_oid,
+ int our_mode, const git_oid *our_oid,
+ int their_mode, const git_oid *their_oid)
+{
+ git_index_reuc_entry *reuc = NULL;
+ int error = 0;
+
+ GIT_ASSERT_ARG(index);
+ GIT_ASSERT_ARG(path);
+
+ if ((error = index_entry_reuc_init(&reuc, path, ancestor_mode,
+ ancestor_oid, our_mode, our_oid, their_mode, their_oid)) < 0 ||
+ (error = index_reuc_insert(index, reuc)) < 0)
+ index_entry_reuc_free(reuc);
+
+ return error;
+}
+
+int git_index_reuc_find(size_t *at_pos, git_index *index, const char *path)
+{
+ return git_vector_bsearch2(at_pos, &index->reuc, index->reuc_search, path);
+}
+
+const git_index_reuc_entry *git_index_reuc_get_bypath(
+ git_index *index, const char *path)
+{
+ size_t pos;
+
+ GIT_ASSERT_ARG_WITH_RETVAL(index, NULL);
+ GIT_ASSERT_ARG_WITH_RETVAL(path, NULL);
+
+ if (!index->reuc.length)
+ return NULL;
+
+ GIT_ASSERT_WITH_RETVAL(git_vector_is_sorted(&index->reuc), NULL);
+
+ if (git_index_reuc_find(&pos, index, path) < 0)
+ return NULL;
+
+ return git_vector_get(&index->reuc, pos);
+}
+
+const git_index_reuc_entry *git_index_reuc_get_byindex(
+ git_index *index, size_t n)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(index, NULL);
+ GIT_ASSERT_WITH_RETVAL(git_vector_is_sorted(&index->reuc), NULL);
+
+ return git_vector_get(&index->reuc, n);
+}
+
+int git_index_reuc_remove(git_index *index, size_t position)
+{
+ int error;
+ git_index_reuc_entry *reuc;
+
+ GIT_ASSERT_ARG(index);
+ GIT_ASSERT(git_vector_is_sorted(&index->reuc));
+
+ reuc = git_vector_get(&index->reuc, position);
+ error = git_vector_remove(&index->reuc, position);
+
+ if (!error)
+ index_entry_reuc_free(reuc);
+
+ index->dirty = 1;
+ return error;
+}
+
+int git_index_reuc_clear(git_index *index)
+{
+ size_t i;
+
+ GIT_ASSERT_ARG(index);
+
+ for (i = 0; i < index->reuc.length; ++i)
+ index_entry_reuc_free(git_atomic_swap(index->reuc.contents[i], NULL));
+
+ git_vector_clear(&index->reuc);
+
+ index->dirty = 1;
+
+ return 0;
+}
+
+static int index_error_invalid(const char *message)
+{
+ git_error_set(GIT_ERROR_INDEX, "invalid data in index - %s", message);
+ return -1;
+}
+
+static int read_reuc(git_index *index, const char *buffer, size_t size)
+{
+ const char *endptr;
+ size_t oid_size = git_oid_size(index->oid_type);
+ size_t len;
+ int i;
+
+ /* If called multiple times, the vector might already be initialized */
+ if (index->reuc._alloc_size == 0 &&
+ git_vector_init(&index->reuc, 16, reuc_cmp) < 0)
+ return -1;
+
+ while (size) {
+ git_index_reuc_entry *lost;
+
+ len = p_strnlen(buffer, size) + 1;
+ if (size <= len)
+ return index_error_invalid("reading reuc entries");
+
+ lost = reuc_entry_alloc(buffer);
+ GIT_ERROR_CHECK_ALLOC(lost);
+
+ size -= len;
+ buffer += len;
+
+ /* read 3 ASCII octal numbers for stage entries */
+ for (i = 0; i < 3; i++) {
+ int64_t tmp;
+
+ if (git__strntol64(&tmp, buffer, size, &endptr, 8) < 0 ||
+ !endptr || endptr == buffer || *endptr ||
+ tmp < 0 || tmp > UINT32_MAX) {
+ index_entry_reuc_free(lost);
+ return index_error_invalid("reading reuc entry stage");
+ }
+
+ lost->mode[i] = (uint32_t)tmp;
+
+ len = (endptr + 1) - buffer;
+ if (size <= len) {
+ index_entry_reuc_free(lost);
+ return index_error_invalid("reading reuc entry stage");
+ }
+
+ size -= len;
+ buffer += len;
+ }
+
+ /* read up to 3 OIDs for stage entries */
+ for (i = 0; i < 3; i++) {
+ if (!lost->mode[i])
+ continue;
+ if (size < oid_size) {
+ index_entry_reuc_free(lost);
+ return index_error_invalid("reading reuc entry oid");
+ }
+
+ if (git_oid__fromraw(&lost->oid[i], (const unsigned char *) buffer, index->oid_type) < 0)
+ return -1;
+
+ size -= oid_size;
+ buffer += oid_size;
+ }
+
+ /* entry was read successfully - insert into reuc vector */
+ if (git_vector_insert(&index->reuc, lost) < 0)
+ return -1;
+ }
+
+ /* entries are guaranteed to be sorted on-disk */
+ git_vector_set_sorted(&index->reuc, true);
+
+ return 0;
+}
+
+
+static int read_conflict_names(git_index *index, const char *buffer, size_t size)
+{
+ size_t len;
+
+ /* This gets called multiple times, the vector might already be initialized */
+ if (index->names._alloc_size == 0 &&
+ git_vector_init(&index->names, 16, conflict_name_cmp) < 0)
+ return -1;
+
+#define read_conflict_name(ptr) \
+ len = p_strnlen(buffer, size) + 1; \
+ if (size < len) { \
+ index_error_invalid("reading conflict name entries"); \
+ goto out_err; \
+ } \
+ if (len == 1) \
+ ptr = NULL; \
+ else { \
+ ptr = git__malloc(len); \
+ GIT_ERROR_CHECK_ALLOC(ptr); \
+ memcpy(ptr, buffer, len); \
+ } \
+ \
+ buffer += len; \
+ size -= len;
+
+ while (size) {
+ git_index_name_entry *conflict_name = git__calloc(1, sizeof(git_index_name_entry));
+ GIT_ERROR_CHECK_ALLOC(conflict_name);
+
+ read_conflict_name(conflict_name->ancestor);
+ read_conflict_name(conflict_name->ours);
+ read_conflict_name(conflict_name->theirs);
+
+ if (git_vector_insert(&index->names, conflict_name) < 0)
+ goto out_err;
+
+ continue;
+
+out_err:
+ git__free(conflict_name->ancestor);
+ git__free(conflict_name->ours);
+ git__free(conflict_name->theirs);
+ git__free(conflict_name);
+ return -1;
+ }
+
+#undef read_conflict_name
+
+ /* entries are guaranteed to be sorted on-disk */
+ git_vector_set_sorted(&index->names, true);
+
+ return 0;
+}
+
+GIT_INLINE(size_t) index_entry_path_offset(
+ git_oid_t oid_type,
+ uint32_t flags)
+{
+ if (oid_type == GIT_OID_SHA1)
+ return (flags & GIT_INDEX_ENTRY_EXTENDED) ?
+ offsetof(index_entry_long_sha1, path) :
+ offsetof(index_entry_short_sha1, path);
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ else if (oid_type == GIT_OID_SHA256)
+ return (flags & GIT_INDEX_ENTRY_EXTENDED) ?
+ offsetof(index_entry_long_sha256, path) :
+ offsetof(index_entry_short_sha256, path);
+#endif
+
+ git_error_set(GIT_ERROR_INTERNAL, "invalid oid type");
+ return 0;
+}
+
+GIT_INLINE(size_t) index_entry_flags_offset(git_oid_t oid_type)
+{
+ if (oid_type == GIT_OID_SHA1)
+ return offsetof(index_entry_long_sha1, flags_extended);
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ else if (oid_type == GIT_OID_SHA256)
+ return offsetof(index_entry_long_sha256, flags_extended);
+#endif
+
+ git_error_set(GIT_ERROR_INTERNAL, "invalid oid type");
+ return 0;
+}
+
+static size_t index_entry_size(
+ size_t path_len,
+ size_t varint_len,
+ git_oid_t oid_type,
+ uint32_t flags)
+{
+ size_t offset, size;
+
+ if (!(offset = index_entry_path_offset(oid_type, flags)))
+ return 0;
+
+ if (varint_len) {
+ if (GIT_ADD_SIZET_OVERFLOW(&size, offset, path_len) ||
+ GIT_ADD_SIZET_OVERFLOW(&size, size, 1) ||
+ GIT_ADD_SIZET_OVERFLOW(&size, size, varint_len))
+ return 0;
+ } else {
+ if (GIT_ADD_SIZET_OVERFLOW(&size, offset, path_len) ||
+ GIT_ADD_SIZET_OVERFLOW(&size, size, 8))
+ return 0;
+
+ size &= ~7;
+ }
+
+ return size;
+}
+
+static int read_entry(
+ git_index_entry **out,
+ size_t *out_size,
+ git_index *index,
+ size_t checksum_size,
+ const void *buffer,
+ size_t buffer_size,
+ const char *last)
+{
+ size_t path_length, path_offset, entry_size;
+ const char *path_ptr;
+ struct entry_common *source_common;
+ index_entry_short_sha1 source_sha1;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ index_entry_short_sha256 source_sha256;
+#endif
+ git_index_entry entry = {{0}};
+ bool compressed = index->version >= INDEX_VERSION_NUMBER_COMP;
+ char *tmp_path = NULL;
+
+ size_t minimal_entry_size = index_entry_path_offset(index->oid_type, 0);
+
+ if (checksum_size + minimal_entry_size > buffer_size)
+ return -1;
+
+ /* buffer is not guaranteed to be aligned */
+ switch (index->oid_type) {
+ case GIT_OID_SHA1:
+ source_common = &source_sha1.common;
+ memcpy(&source_sha1, buffer, sizeof(source_sha1));
+ break;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ case GIT_OID_SHA256:
+ source_common = &source_sha256.common;
+ memcpy(&source_sha256, buffer, sizeof(source_sha256));
+ break;
+#endif
+ default:
+ GIT_ASSERT(!"invalid oid type");
+ }
+
+ entry.ctime.seconds = (git_time_t)ntohl(source_common->ctime.seconds);
+ entry.ctime.nanoseconds = ntohl(source_common->ctime.nanoseconds);
+ entry.mtime.seconds = (git_time_t)ntohl(source_common->mtime.seconds);
+ entry.mtime.nanoseconds = ntohl(source_common->mtime.nanoseconds);
+ entry.dev = ntohl(source_common->dev);
+ entry.ino = ntohl(source_common->ino);
+ entry.mode = ntohl(source_common->mode);
+ entry.uid = ntohl(source_common->uid);
+ entry.gid = ntohl(source_common->gid);
+ entry.file_size = ntohl(source_common->file_size);
+
+ switch (index->oid_type) {
+ case GIT_OID_SHA1:
+ if (git_oid__fromraw(&entry.id, source_sha1.oid,
+ GIT_OID_SHA1) < 0)
+ return -1;
+ entry.flags = ntohs(source_sha1.flags);
+ break;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ case GIT_OID_SHA256:
+ if (git_oid__fromraw(&entry.id, source_sha256.oid,
+ GIT_OID_SHA256) < 0)
+ return -1;
+ entry.flags = ntohs(source_sha256.flags);
+ break;
+#endif
+ default:
+ GIT_ASSERT(!"invalid oid type");
+ }
+
+ if (!(path_offset = index_entry_path_offset(index->oid_type, entry.flags)))
+ return -1;
+
+
+ if (entry.flags & GIT_INDEX_ENTRY_EXTENDED) {
+ uint16_t flags_raw;
+ size_t flags_offset;
+
+ if (!(flags_offset = index_entry_flags_offset(index->oid_type)))
+ return -1;
+
+ memcpy(&flags_raw, (const char *)buffer + flags_offset, sizeof(flags_raw));
+ flags_raw = ntohs(flags_raw);
+
+ memcpy(&entry.flags_extended, &flags_raw, sizeof(flags_raw));
+ path_ptr = (const char *)buffer + path_offset;
+ } else {
+ path_ptr = (const char *)buffer + path_offset;
+ }
+
+ if (!compressed) {
+ path_length = entry.flags & GIT_INDEX_ENTRY_NAMEMASK;
+
+ /* if this is a very long string, we must find its
+ * real length without overflowing */
+ if (path_length == 0xFFF) {
+ const char *path_end;
+
+ path_end = memchr(path_ptr, '\0', buffer_size);
+ if (path_end == NULL)
+ return index_error_invalid("invalid path name");
+
+ path_length = path_end - path_ptr;
+ }
+
+ entry_size = index_entry_size(path_length, 0, index->oid_type, entry.flags);
+ entry.path = (char *)path_ptr;
+ } else {
+ size_t varint_len, last_len, prefix_len, suffix_len, path_len;
+ uintmax_t strip_len;
+
+ strip_len = git_decode_varint((const unsigned char *)path_ptr, &varint_len);
+ last_len = strlen(last);
+
+ if (varint_len == 0 || last_len < strip_len)
+ return index_error_invalid("incorrect prefix length");
+
+ prefix_len = last_len - (size_t)strip_len;
+ suffix_len = strlen(path_ptr + varint_len);
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&path_len, prefix_len, suffix_len);
+ GIT_ERROR_CHECK_ALLOC_ADD(&path_len, path_len, 1);
+
+ if (path_len > GIT_PATH_MAX)
+ return index_error_invalid("unreasonable path length");
+
+ tmp_path = git__malloc(path_len);
+ GIT_ERROR_CHECK_ALLOC(tmp_path);
+
+ memcpy(tmp_path, last, prefix_len);
+ memcpy(tmp_path + prefix_len, path_ptr + varint_len, suffix_len + 1);
+
+ entry_size = index_entry_size(suffix_len, varint_len, index->oid_type, entry.flags);
+ entry.path = tmp_path;
+ }
+
+ if (entry_size == 0)
+ return -1;
+
+ if (checksum_size + entry_size > buffer_size) {
+ git_error_set(GIT_ERROR_INTERNAL, "invalid index checksum");
+ return -1;
+ }
+
+ if (index_entry_dup(out, index, &entry) < 0) {
+ git__free(tmp_path);
+ return -1;
+ }
+
+ git__free(tmp_path);
+ *out_size = entry_size;
+ return 0;
+}
+
+static int read_header(struct index_header *dest, const void *buffer)
+{
+ const struct index_header *source = buffer;
+
+ dest->signature = ntohl(source->signature);
+ if (dest->signature != INDEX_HEADER_SIG)
+ return index_error_invalid("incorrect header signature");
+
+ dest->version = ntohl(source->version);
+ if (dest->version < INDEX_VERSION_NUMBER_LB ||
+ dest->version > INDEX_VERSION_NUMBER_UB)
+ return index_error_invalid("incorrect header version");
+
+ dest->entry_count = ntohl(source->entry_count);
+ return 0;
+}
+
+static int read_extension(size_t *read_len, git_index *index, size_t checksum_size, const char *buffer, size_t buffer_size)
+{
+ struct index_extension dest;
+ size_t total_size;
+
+ /* buffer is not guaranteed to be aligned */
+ memcpy(&dest, buffer, sizeof(struct index_extension));
+ dest.extension_size = ntohl(dest.extension_size);
+
+ total_size = dest.extension_size + sizeof(struct index_extension);
+
+ if (dest.extension_size > total_size ||
+ buffer_size < total_size ||
+ buffer_size - total_size < checksum_size) {
+ index_error_invalid("extension is truncated");
+ return -1;
+ }
+
+ /* optional extension */
+ if (dest.signature[0] >= 'A' && dest.signature[0] <= 'Z') {
+ /* tree cache */
+ if (memcmp(dest.signature, INDEX_EXT_TREECACHE_SIG, 4) == 0) {
+ if (git_tree_cache_read(&index->tree, buffer + 8, dest.extension_size, index->oid_type, &index->tree_pool) < 0)
+ return -1;
+ } else if (memcmp(dest.signature, INDEX_EXT_UNMERGED_SIG, 4) == 0) {
+ if (read_reuc(index, buffer + 8, dest.extension_size) < 0)
+ return -1;
+ } else if (memcmp(dest.signature, INDEX_EXT_CONFLICT_NAME_SIG, 4) == 0) {
+ if (read_conflict_names(index, buffer + 8, dest.extension_size) < 0)
+ return -1;
+ }
+ /* else, unsupported extension. We cannot parse this, but we can skip
+ * it by returning `total_size */
+ } else {
+ /* we cannot handle non-ignorable extensions;
+ * in fact they aren't even defined in the standard */
+ git_error_set(GIT_ERROR_INDEX, "unsupported mandatory extension: '%.4s'", dest.signature);
+ return -1;
+ }
+
+ *read_len = total_size;
+
+ return 0;
+}
+
+static int parse_index(git_index *index, const char *buffer, size_t buffer_size)
+{
+ int error = 0;
+ unsigned int i;
+ struct index_header header = { 0 };
+ unsigned char checksum[GIT_HASH_MAX_SIZE];
+ size_t checksum_size = git_hash_size(git_oid_algorithm(index->oid_type));
+ const char *last = NULL;
+ const char *empty = "";
+
+#define seek_forward(_increase) { \
+ if (_increase >= buffer_size) { \
+ error = index_error_invalid("ran out of data while parsing"); \
+ goto done; } \
+ buffer += _increase; \
+ buffer_size -= _increase;\
+}
+
+ if (buffer_size < INDEX_HEADER_SIZE + checksum_size)
+ return index_error_invalid("insufficient buffer space");
+
+ /*
+ * Precalculate the hash of the files's contents -- we'll match
+ * it to the provided checksum in the footer.
+ */
+ git_hash_buf(checksum, buffer, buffer_size - checksum_size,
+ git_oid_algorithm(index->oid_type));
+
+ /* Parse header */
+ if ((error = read_header(&header, buffer)) < 0)
+ return error;
+
+ index->version = header.version;
+ if (index->version >= INDEX_VERSION_NUMBER_COMP)
+ last = empty;
+
+ seek_forward(INDEX_HEADER_SIZE);
+
+ GIT_ASSERT(!index->entries.length);
+
+ if ((error = index_map_resize(index->entries_map, header.entry_count, index->ignore_case)) < 0)
+ return error;
+
+ /* Parse all the entries */
+ for (i = 0; i < header.entry_count && buffer_size > checksum_size; ++i) {
+ git_index_entry *entry = NULL;
+ size_t entry_size;
+
+ if ((error = read_entry(&entry, &entry_size, index, checksum_size, buffer, buffer_size, last)) < 0) {
+ error = index_error_invalid("invalid entry");
+ goto done;
+ }
+
+ if ((error = git_vector_insert(&index->entries, entry)) < 0) {
+ index_entry_free(entry);
+ goto done;
+ }
+
+ if ((error = index_map_set(index->entries_map, entry, index->ignore_case)) < 0) {
+ index_entry_free(entry);
+ goto done;
+ }
+ error = 0;
+
+ if (index->version >= INDEX_VERSION_NUMBER_COMP)
+ last = entry->path;
+
+ seek_forward(entry_size);
+ }
+
+ if (i != header.entry_count) {
+ error = index_error_invalid("header entries changed while parsing");
+ goto done;
+ }
+
+ /* There's still space for some extensions! */
+ while (buffer_size > checksum_size) {
+ size_t extension_size;
+
+ if ((error = read_extension(&extension_size, index, checksum_size, buffer, buffer_size)) < 0) {
+ goto done;
+ }
+
+ seek_forward(extension_size);
+ }
+
+ if (buffer_size != checksum_size) {
+ error = index_error_invalid(
+ "buffer size does not match index footer size");
+ goto done;
+ }
+
+ /*
+ * SHA-1 or SHA-256 (depending on the repository's object format)
+ * over the content of the index file before this checksum.
+ */
+ if (memcmp(checksum, buffer, checksum_size) != 0) {
+ error = index_error_invalid(
+ "calculated checksum does not match expected");
+ goto done;
+ }
+
+ memcpy(index->checksum, checksum, checksum_size);
+
+#undef seek_forward
+
+ /* Entries are stored case-sensitively on disk, so re-sort now if
+ * in-memory index is supposed to be case-insensitive
+ */
+ git_vector_set_sorted(&index->entries, !index->ignore_case);
+ git_vector_sort(&index->entries);
+
+ index->dirty = 0;
+done:
+ return error;
+}
+
+static bool is_index_extended(git_index *index)
+{
+ size_t i, extended;
+ git_index_entry *entry;
+
+ extended = 0;
+
+ git_vector_foreach(&index->entries, i, entry) {
+ entry->flags &= ~GIT_INDEX_ENTRY_EXTENDED;
+ if (entry->flags_extended & GIT_INDEX_ENTRY_EXTENDED_FLAGS) {
+ extended++;
+ entry->flags |= GIT_INDEX_ENTRY_EXTENDED;
+ }
+ }
+
+ return (extended > 0);
+}
+
+static int write_disk_entry(
+ git_index *index,
+ git_filebuf *file,
+ git_index_entry *entry,
+ const char *last)
+{
+ void *mem = NULL;
+ struct entry_common *ondisk_common;
+ size_t path_len, path_offset, disk_size;
+ int varint_len = 0;
+ char *path;
+ const char *path_start = entry->path;
+ size_t same_len = 0;
+
+ index_entry_short_sha1 ondisk_sha1;
+ index_entry_long_sha1 ondisk_ext_sha1;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ index_entry_short_sha256 ondisk_sha256;
+ index_entry_long_sha256 ondisk_ext_sha256;
+#endif
+
+ switch (index->oid_type) {
+ case GIT_OID_SHA1:
+ ondisk_common = &ondisk_sha1.common;
+ break;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ case GIT_OID_SHA256:
+ ondisk_common = &ondisk_sha256.common;
+ break;
+#endif
+ default:
+ GIT_ASSERT(!"invalid oid type");
+ }
+
+ path_len = ((struct entry_internal *)entry)->pathlen;
+
+ if (last) {
+ const char *last_c = last;
+
+ while (*path_start == *last_c) {
+ if (!*path_start || !*last_c)
+ break;
+ ++path_start;
+ ++last_c;
+ ++same_len;
+ }
+ path_len -= same_len;
+ varint_len = git_encode_varint(NULL, 0, strlen(last) - same_len);
+ }
+
+ disk_size = index_entry_size(path_len, varint_len, index->oid_type, entry->flags);
+
+ if (!disk_size || git_filebuf_reserve(file, &mem, disk_size) < 0)
+ return -1;
+
+ memset(mem, 0x0, disk_size);
+
+ /**
+ * Yes, we have to truncate.
+ *
+ * The on-disk format for Index entries clearly defines
+ * the time and size fields to be 4 bytes each -- so even if
+ * we store these values with 8 bytes on-memory, they must
+ * be truncated to 4 bytes before writing to disk.
+ *
+ * In 2038 I will be either too dead or too rich to care about this
+ */
+ ondisk_common->ctime.seconds = htonl((uint32_t)entry->ctime.seconds);
+ ondisk_common->mtime.seconds = htonl((uint32_t)entry->mtime.seconds);
+ ondisk_common->ctime.nanoseconds = htonl(entry->ctime.nanoseconds);
+ ondisk_common->mtime.nanoseconds = htonl(entry->mtime.nanoseconds);
+ ondisk_common->dev = htonl(entry->dev);
+ ondisk_common->ino = htonl(entry->ino);
+ ondisk_common->mode = htonl(entry->mode);
+ ondisk_common->uid = htonl(entry->uid);
+ ondisk_common->gid = htonl(entry->gid);
+ ondisk_common->file_size = htonl((uint32_t)entry->file_size);
+
+ switch (index->oid_type) {
+ case GIT_OID_SHA1:
+ git_oid_raw_cpy(ondisk_sha1.oid, entry->id.id, GIT_OID_SHA1_SIZE);
+ ondisk_sha1.flags = htons(entry->flags);
+ break;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ case GIT_OID_SHA256:
+ git_oid_raw_cpy(ondisk_sha256.oid, entry->id.id, GIT_OID_SHA256_SIZE);
+ ondisk_sha256.flags = htons(entry->flags);
+ break;
+#endif
+ default:
+ GIT_ASSERT(!"invalid oid type");
+ }
+
+ path_offset = index_entry_path_offset(index->oid_type, entry->flags);
+
+ if (entry->flags & GIT_INDEX_ENTRY_EXTENDED) {
+ struct entry_common *ondisk_ext;
+ uint16_t flags_extended = htons(entry->flags_extended &
+ GIT_INDEX_ENTRY_EXTENDED_FLAGS);
+
+ switch (index->oid_type) {
+ case GIT_OID_SHA1:
+ memcpy(&ondisk_ext_sha1, &ondisk_sha1,
+ sizeof(index_entry_short_sha1));
+ ondisk_ext_sha1.flags_extended = flags_extended;
+ ondisk_ext = &ondisk_ext_sha1.common;
+ break;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ case GIT_OID_SHA256:
+ memcpy(&ondisk_ext_sha256, &ondisk_sha256,
+ sizeof(index_entry_short_sha256));
+ ondisk_ext_sha256.flags_extended = flags_extended;
+ ondisk_ext = &ondisk_ext_sha256.common;
+ break;
+#endif
+ default:
+ GIT_ASSERT(!"invalid oid type");
+ }
+
+ memcpy(mem, ondisk_ext, path_offset);
+ } else {
+ switch (index->oid_type) {
+ case GIT_OID_SHA1:
+ memcpy(mem, &ondisk_sha1, path_offset);
+ break;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ case GIT_OID_SHA256:
+ memcpy(mem, &ondisk_sha256, path_offset);
+ break;
+#endif
+ default:
+ GIT_ASSERT(!"invalid oid type");
+ }
+ }
+
+ path = (char *)mem + path_offset;
+ disk_size -= path_offset;
+
+ if (last) {
+ varint_len = git_encode_varint((unsigned char *) path,
+ disk_size, strlen(last) - same_len);
+ GIT_ASSERT(varint_len > 0);
+
+ path += varint_len;
+ disk_size -= varint_len;
+
+ /*
+ * If using path compression, we are not allowed
+ * to have additional trailing NULs.
+ */
+ GIT_ASSERT(disk_size == path_len + 1);
+ } else {
+ /*
+ * If no path compression is used, we do have
+ * NULs as padding. As such, simply assert that
+ * we have enough space left to write the path.
+ */
+ GIT_ASSERT(disk_size > path_len);
+ }
+
+ memcpy(path, path_start, path_len + 1);
+
+ return 0;
+}
+
+static int write_entries(git_index *index, git_filebuf *file)
+{
+ int error = 0;
+ size_t i;
+ git_vector case_sorted = GIT_VECTOR_INIT, *entries = NULL;
+ git_index_entry *entry;
+ const char *last = NULL;
+
+ /* If index->entries is sorted case-insensitively, then we need
+ * to re-sort it case-sensitively before writing */
+ if (index->ignore_case) {
+ if ((error = git_vector_dup(&case_sorted, &index->entries, git_index_entry_cmp)) < 0)
+ goto done;
+
+ git_vector_sort(&case_sorted);
+ entries = &case_sorted;
+ } else {
+ entries = &index->entries;
+ }
+
+ if (index->version >= INDEX_VERSION_NUMBER_COMP)
+ last = "";
+
+ git_vector_foreach(entries, i, entry) {
+ if ((error = write_disk_entry(index, file, entry, last)) < 0)
+ break;
+ if (index->version >= INDEX_VERSION_NUMBER_COMP)
+ last = entry->path;
+ }
+
+done:
+ git_vector_free(&case_sorted);
+ return error;
+}
+
+static int write_extension(git_filebuf *file, struct index_extension *header, git_str *data)
+{
+ struct index_extension ondisk;
+
+ memset(&ondisk, 0x0, sizeof(struct index_extension));
+ memcpy(&ondisk, header, 4);
+ ondisk.extension_size = htonl(header->extension_size);
+
+ git_filebuf_write(file, &ondisk, sizeof(struct index_extension));
+ return git_filebuf_write(file, data->ptr, data->size);
+}
+
+static int create_name_extension_data(git_str *name_buf, git_index_name_entry *conflict_name)
+{
+ int error = 0;
+
+ if (conflict_name->ancestor == NULL)
+ error = git_str_put(name_buf, "\0", 1);
+ else
+ error = git_str_put(name_buf, conflict_name->ancestor, strlen(conflict_name->ancestor) + 1);
+
+ if (error != 0)
+ goto on_error;
+
+ if (conflict_name->ours == NULL)
+ error = git_str_put(name_buf, "\0", 1);
+ else
+ error = git_str_put(name_buf, conflict_name->ours, strlen(conflict_name->ours) + 1);
+
+ if (error != 0)
+ goto on_error;
+
+ if (conflict_name->theirs == NULL)
+ error = git_str_put(name_buf, "\0", 1);
+ else
+ error = git_str_put(name_buf, conflict_name->theirs, strlen(conflict_name->theirs) + 1);
+
+on_error:
+ return error;
+}
+
+static int write_name_extension(git_index *index, git_filebuf *file)
+{
+ git_str name_buf = GIT_STR_INIT;
+ git_vector *out = &index->names;
+ git_index_name_entry *conflict_name;
+ struct index_extension extension;
+ size_t i;
+ int error = 0;
+
+ git_vector_foreach(out, i, conflict_name) {
+ if ((error = create_name_extension_data(&name_buf, conflict_name)) < 0)
+ goto done;
+ }
+
+ memset(&extension, 0x0, sizeof(struct index_extension));
+ memcpy(&extension.signature, INDEX_EXT_CONFLICT_NAME_SIG, 4);
+ extension.extension_size = (uint32_t)name_buf.size;
+
+ error = write_extension(file, &extension, &name_buf);
+
+ git_str_dispose(&name_buf);
+
+done:
+ return error;
+}
+
+static int create_reuc_extension_data(git_str *reuc_buf, git_index *index, git_index_reuc_entry *reuc)
+{
+ size_t oid_size = git_oid_size(index->oid_type);
+ int i;
+ int error = 0;
+
+ if ((error = git_str_put(reuc_buf, reuc->path, strlen(reuc->path) + 1)) < 0)
+ return error;
+
+ for (i = 0; i < 3; i++) {
+ if ((error = git_str_printf(reuc_buf, "%o", reuc->mode[i])) < 0 ||
+ (error = git_str_put(reuc_buf, "\0", 1)) < 0)
+ return error;
+ }
+
+ for (i = 0; i < 3; i++) {
+ if (reuc->mode[i] && (error = git_str_put(reuc_buf, (char *)&reuc->oid[i].id, oid_size)) < 0)
+ return error;
+ }
+
+ return 0;
+}
+
+static int write_reuc_extension(git_index *index, git_filebuf *file)
+{
+ git_str reuc_buf = GIT_STR_INIT;
+ git_vector *out = &index->reuc;
+ git_index_reuc_entry *reuc;
+ struct index_extension extension;
+ size_t i;
+ int error = 0;
+
+ git_vector_foreach(out, i, reuc) {
+ if ((error = create_reuc_extension_data(&reuc_buf, index, reuc)) < 0)
+ goto done;
+ }
+
+ memset(&extension, 0x0, sizeof(struct index_extension));
+ memcpy(&extension.signature, INDEX_EXT_UNMERGED_SIG, 4);
+ extension.extension_size = (uint32_t)reuc_buf.size;
+
+ error = write_extension(file, &extension, &reuc_buf);
+
+ git_str_dispose(&reuc_buf);
+
+done:
+ return error;
+}
+
+static int write_tree_extension(git_index *index, git_filebuf *file)
+{
+ struct index_extension extension;
+ git_str buf = GIT_STR_INIT;
+ int error;
+
+ if (index->tree == NULL)
+ return 0;
+
+ if ((error = git_tree_cache_write(&buf, index->tree)) < 0)
+ return error;
+
+ memset(&extension, 0x0, sizeof(struct index_extension));
+ memcpy(&extension.signature, INDEX_EXT_TREECACHE_SIG, 4);
+ extension.extension_size = (uint32_t)buf.size;
+
+ error = write_extension(file, &extension, &buf);
+
+ git_str_dispose(&buf);
+
+ return error;
+}
+
+static void clear_uptodate(git_index *index)
+{
+ git_index_entry *entry;
+ size_t i;
+
+ git_vector_foreach(&index->entries, i, entry)
+ entry->flags_extended &= ~GIT_INDEX_ENTRY_UPTODATE;
+}
+
+static int write_index(
+ unsigned char checksum[GIT_HASH_MAX_SIZE],
+ size_t *checksum_size,
+ git_index *index,
+ git_filebuf *file)
+{
+ struct index_header header;
+ bool is_extended;
+ uint32_t index_version_number;
+
+ GIT_ASSERT_ARG(index);
+ GIT_ASSERT_ARG(file);
+
+ GIT_ASSERT(index->oid_type);
+
+ *checksum_size = git_hash_size(git_oid_algorithm(index->oid_type));
+
+ if (index->version <= INDEX_VERSION_NUMBER_EXT) {
+ is_extended = is_index_extended(index);
+ index_version_number = is_extended ? INDEX_VERSION_NUMBER_EXT : INDEX_VERSION_NUMBER_LB;
+ } else {
+ index_version_number = index->version;
+ }
+
+ header.signature = htonl(INDEX_HEADER_SIG);
+ header.version = htonl(index_version_number);
+ header.entry_count = htonl((uint32_t)index->entries.length);
+
+ if (git_filebuf_write(file, &header, sizeof(struct index_header)) < 0)
+ return -1;
+
+ if (write_entries(index, file) < 0)
+ return -1;
+
+ /* write the tree cache extension */
+ if (index->tree != NULL && write_tree_extension(index, file) < 0)
+ return -1;
+
+ /* write the rename conflict extension */
+ if (index->names.length > 0 && write_name_extension(index, file) < 0)
+ return -1;
+
+ /* write the reuc extension */
+ if (index->reuc.length > 0 && write_reuc_extension(index, file) < 0)
+ return -1;
+
+ /* get out the hash for all the contents we've appended to the file */
+ git_filebuf_hash(checksum, file);
+
+ /* write it at the end of the file */
+ if (git_filebuf_write(file, checksum, *checksum_size) < 0)
+ return -1;
+
+ /* file entries are no longer up to date */
+ clear_uptodate(index);
+
+ return 0;
+}
+
+int git_index_entry_stage(const git_index_entry *entry)
+{
+ return GIT_INDEX_ENTRY_STAGE(entry);
+}
+
+int git_index_entry_is_conflict(const git_index_entry *entry)
+{
+ return (GIT_INDEX_ENTRY_STAGE(entry) > 0);
+}
+
+typedef struct read_tree_data {
+ git_index *index;
+ git_vector *old_entries;
+ git_vector *new_entries;
+ git_vector_cmp entry_cmp;
+ git_tree_cache *tree;
+} read_tree_data;
+
+static int read_tree_cb(
+ const char *root, const git_tree_entry *tentry, void *payload)
+{
+ read_tree_data *data = payload;
+ git_index_entry *entry = NULL, *old_entry;
+ git_str path = GIT_STR_INIT;
+ size_t pos;
+
+ if (git_tree_entry__is_tree(tentry))
+ return 0;
+
+ if (git_str_joinpath(&path, root, tentry->filename) < 0)
+ return -1;
+
+ if (index_entry_create(&entry, INDEX_OWNER(data->index), path.ptr, NULL, false) < 0)
+ return -1;
+
+ entry->mode = tentry->attr;
+ git_oid_cpy(&entry->id, git_tree_entry_id(tentry));
+
+ /* look for corresponding old entry and copy data to new entry */
+ if (data->old_entries != NULL &&
+ !index_find_in_entries(
+ &pos, data->old_entries, data->entry_cmp, path.ptr, 0, 0) &&
+ (old_entry = git_vector_get(data->old_entries, pos)) != NULL &&
+ entry->mode == old_entry->mode &&
+ git_oid_equal(&entry->id, &old_entry->id))
+ {
+ index_entry_cpy(entry, old_entry);
+ entry->flags_extended = 0;
+ }
+
+ index_entry_adjust_namemask(entry, path.size);
+ git_str_dispose(&path);
+
+ if (git_vector_insert(data->new_entries, entry) < 0) {
+ index_entry_free(entry);
+ return -1;
+ }
+
+ return 0;
+}
+
+int git_index_read_tree(git_index *index, const git_tree *tree)
+{
+ int error = 0;
+ git_vector entries = GIT_VECTOR_INIT;
+ git_idxmap *entries_map;
+ read_tree_data data;
+ size_t i;
+ git_index_entry *e;
+
+ if (git_idxmap_new(&entries_map) < 0)
+ return -1;
+
+ git_vector_set_cmp(&entries, index->entries._cmp); /* match sort */
+
+ data.index = index;
+ data.old_entries = &index->entries;
+ data.new_entries = &entries;
+ data.entry_cmp = index->entries_search;
+
+ index->tree = NULL;
+ git_pool_clear(&index->tree_pool);
+
+ git_vector_sort(&index->entries);
+
+ if ((error = git_tree_walk(tree, GIT_TREEWALK_POST, read_tree_cb, &data)) < 0)
+ goto cleanup;
+
+ if ((error = index_map_resize(entries_map, entries.length, index->ignore_case)) < 0)
+ goto cleanup;
+
+ git_vector_foreach(&entries, i, e) {
+ if ((error = index_map_set(entries_map, e, index->ignore_case)) < 0) {
+ git_error_set(GIT_ERROR_INDEX, "failed to insert entry into map");
+ return error;
+ }
+ }
+
+ error = 0;
+
+ git_vector_sort(&entries);
+
+ if ((error = git_index_clear(index)) < 0) {
+ /* well, this isn't good */;
+ } else {
+ git_vector_swap(&entries, &index->entries);
+ entries_map = git_atomic_swap(index->entries_map, entries_map);
+ }
+
+ index->dirty = 1;
+
+cleanup:
+ git_vector_free(&entries);
+ git_idxmap_free(entries_map);
+ if (error < 0)
+ return error;
+
+ error = git_tree_cache_read_tree(&index->tree, tree, index->oid_type, &index->tree_pool);
+
+ return error;
+}
+
+static int git_index_read_iterator(
+ git_index *index,
+ git_iterator *new_iterator,
+ size_t new_length_hint)
+{
+ git_vector new_entries = GIT_VECTOR_INIT,
+ remove_entries = GIT_VECTOR_INIT;
+ git_idxmap *new_entries_map = NULL;
+ git_iterator *index_iterator = NULL;
+ git_iterator_options opts = GIT_ITERATOR_OPTIONS_INIT;
+ const git_index_entry *old_entry, *new_entry;
+ git_index_entry *entry;
+ size_t i;
+ int error;
+
+ GIT_ASSERT((new_iterator->flags & GIT_ITERATOR_DONT_IGNORE_CASE));
+
+ if ((error = git_vector_init(&new_entries, new_length_hint, index->entries._cmp)) < 0 ||
+ (error = git_vector_init(&remove_entries, index->entries.length, NULL)) < 0 ||
+ (error = git_idxmap_new(&new_entries_map)) < 0)
+ goto done;
+
+ if (new_length_hint && (error = index_map_resize(new_entries_map, new_length_hint,
+ index->ignore_case)) < 0)
+ goto done;
+
+ opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE |
+ GIT_ITERATOR_INCLUDE_CONFLICTS;
+
+ if ((error = git_iterator_for_index(&index_iterator,
+ git_index_owner(index), index, &opts)) < 0 ||
+ ((error = git_iterator_current(&old_entry, index_iterator)) < 0 &&
+ error != GIT_ITEROVER) ||
+ ((error = git_iterator_current(&new_entry, new_iterator)) < 0 &&
+ error != GIT_ITEROVER))
+ goto done;
+
+ while (true) {
+ git_index_entry
+ *dup_entry = NULL,
+ *add_entry = NULL,
+ *remove_entry = NULL;
+ int diff;
+
+ error = 0;
+
+ if (old_entry && new_entry)
+ diff = git_index_entry_cmp(old_entry, new_entry);
+ else if (!old_entry && new_entry)
+ diff = 1;
+ else if (old_entry && !new_entry)
+ diff = -1;
+ else
+ break;
+
+ if (diff < 0) {
+ remove_entry = (git_index_entry *)old_entry;
+ } else if (diff > 0) {
+ dup_entry = (git_index_entry *)new_entry;
+ } else {
+ /* Path and stage are equal, if the OID is equal, keep it to
+ * keep the stat cache data.
+ */
+ if (git_oid_equal(&old_entry->id, &new_entry->id) &&
+ old_entry->mode == new_entry->mode) {
+ add_entry = (git_index_entry *)old_entry;
+ } else {
+ dup_entry = (git_index_entry *)new_entry;
+ remove_entry = (git_index_entry *)old_entry;
+ }
+ }
+
+ if (dup_entry) {
+ if ((error = index_entry_dup_nocache(&add_entry, index, dup_entry)) < 0)
+ goto done;
+
+ index_entry_adjust_namemask(add_entry,
+ ((struct entry_internal *)add_entry)->pathlen);
+ }
+
+ /* invalidate this path in the tree cache if this is new (to
+ * invalidate the parent trees)
+ */
+ if (dup_entry && !remove_entry && index->tree)
+ git_tree_cache_invalidate_path(index->tree, dup_entry->path);
+
+ if (add_entry) {
+ if ((error = git_vector_insert(&new_entries, add_entry)) == 0)
+ error = index_map_set(new_entries_map, add_entry,
+ index->ignore_case);
+ }
+
+ if (remove_entry && error >= 0)
+ error = git_vector_insert(&remove_entries, remove_entry);
+
+ if (error < 0) {
+ git_error_set(GIT_ERROR_INDEX, "failed to insert entry");
+ goto done;
+ }
+
+ if (diff <= 0) {
+ if ((error = git_iterator_advance(&old_entry, index_iterator)) < 0 &&
+ error != GIT_ITEROVER)
+ goto done;
+ }
+
+ if (diff >= 0) {
+ if ((error = git_iterator_advance(&new_entry, new_iterator)) < 0 &&
+ error != GIT_ITEROVER)
+ goto done;
+ }
+ }
+
+ if ((error = git_index_name_clear(index)) < 0 ||
+ (error = git_index_reuc_clear(index)) < 0)
+ goto done;
+
+ git_vector_swap(&new_entries, &index->entries);
+ new_entries_map = git_atomic_swap(index->entries_map, new_entries_map);
+
+ git_vector_foreach(&remove_entries, i, entry) {
+ if (index->tree)
+ git_tree_cache_invalidate_path(index->tree, entry->path);
+
+ index_entry_free(entry);
+ }
+
+ clear_uptodate(index);
+
+ index->dirty = 1;
+ error = 0;
+
+done:
+ git_idxmap_free(new_entries_map);
+ git_vector_free(&new_entries);
+ git_vector_free(&remove_entries);
+ git_iterator_free(index_iterator);
+ return error;
+}
+
+int git_index_read_index(
+ git_index *index,
+ const git_index *new_index)
+{
+ git_iterator *new_iterator = NULL;
+ git_iterator_options opts = GIT_ITERATOR_OPTIONS_INIT;
+ int error;
+
+ opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE |
+ GIT_ITERATOR_INCLUDE_CONFLICTS;
+
+ if ((error = git_iterator_for_index(&new_iterator,
+ git_index_owner(new_index), (git_index *)new_index, &opts)) < 0 ||
+ (error = git_index_read_iterator(index, new_iterator,
+ new_index->entries.length)) < 0)
+ goto done;
+
+done:
+ git_iterator_free(new_iterator);
+ return error;
+}
+
+git_repository *git_index_owner(const git_index *index)
+{
+ return INDEX_OWNER(index);
+}
+
+enum {
+ INDEX_ACTION_NONE = 0,
+ INDEX_ACTION_UPDATE = 1,
+ INDEX_ACTION_REMOVE = 2,
+ INDEX_ACTION_ADDALL = 3
+};
+
+int git_index_add_all(
+ git_index *index,
+ const git_strarray *paths,
+ unsigned int flags,
+ git_index_matched_path_cb cb,
+ void *payload)
+{
+ int error;
+ git_repository *repo;
+ git_pathspec ps;
+ bool no_fnmatch = (flags & GIT_INDEX_ADD_DISABLE_PATHSPEC_MATCH) != 0;
+
+ GIT_ASSERT_ARG(index);
+
+ repo = INDEX_OWNER(index);
+ if ((error = git_repository__ensure_not_bare(repo, "index add all")) < 0)
+ return error;
+
+ if ((error = git_pathspec__init(&ps, paths)) < 0)
+ return error;
+
+ /* optionally check that pathspec doesn't mention any ignored files */
+ if ((flags & GIT_INDEX_ADD_CHECK_PATHSPEC) != 0 &&
+ (flags & GIT_INDEX_ADD_FORCE) == 0 &&
+ (error = git_ignore__check_pathspec_for_exact_ignores(
+ repo, &ps.pathspec, no_fnmatch)) < 0)
+ goto cleanup;
+
+ error = index_apply_to_wd_diff(index, INDEX_ACTION_ADDALL, paths, flags, cb, payload);
+
+ if (error)
+ git_error_set_after_callback(error);
+
+cleanup:
+ git_pathspec__clear(&ps);
+
+ return error;
+}
+
+struct foreach_diff_data {
+ git_index *index;
+ const git_pathspec *pathspec;
+ unsigned int flags;
+ git_index_matched_path_cb cb;
+ void *payload;
+};
+
+static int apply_each_file(const git_diff_delta *delta, float progress, void *payload)
+{
+ struct foreach_diff_data *data = payload;
+ const char *match, *path;
+ int error = 0;
+
+ GIT_UNUSED(progress);
+
+ path = delta->old_file.path;
+
+ /* We only want those which match the pathspecs */
+ if (!git_pathspec__match(
+ &data->pathspec->pathspec, path, false, (bool)data->index->ignore_case,
+ &match, NULL))
+ return 0;
+
+ if (data->cb)
+ error = data->cb(path, match, data->payload);
+
+ if (error > 0) /* skip this entry */
+ return 0;
+ if (error < 0) /* actual error */
+ return error;
+
+ /* If the workdir item does not exist, remove it from the index. */
+ if ((delta->new_file.flags & GIT_DIFF_FLAG_EXISTS) == 0)
+ error = git_index_remove_bypath(data->index, path);
+ else
+ error = git_index_add_bypath(data->index, delta->new_file.path);
+
+ return error;
+}
+
+static int index_apply_to_wd_diff(git_index *index, int action, const git_strarray *paths,
+ unsigned int flags,
+ git_index_matched_path_cb cb, void *payload)
+{
+ int error;
+ git_diff *diff;
+ git_pathspec ps;
+ git_repository *repo;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ struct foreach_diff_data data = {
+ index,
+ NULL,
+ flags,
+ cb,
+ payload,
+ };
+
+ GIT_ASSERT_ARG(index);
+ GIT_ASSERT_ARG(action == INDEX_ACTION_UPDATE || action == INDEX_ACTION_ADDALL);
+
+ repo = INDEX_OWNER(index);
+
+ if (!repo) {
+ return create_index_error(-1,
+ "cannot run update; the index is not backed up by a repository.");
+ }
+
+ /*
+ * We do the matching ourselves instead of passing the list to
+ * diff because we want to tell the callback which one
+ * matched, which we do not know if we ask diff to filter for us.
+ */
+ if ((error = git_pathspec__init(&ps, paths)) < 0)
+ return error;
+
+ opts.flags = GIT_DIFF_INCLUDE_TYPECHANGE;
+ if (action == INDEX_ACTION_ADDALL) {
+ opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED |
+ GIT_DIFF_RECURSE_UNTRACKED_DIRS;
+
+ if (flags == GIT_INDEX_ADD_FORCE)
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED |
+ GIT_DIFF_RECURSE_IGNORED_DIRS;
+ }
+
+ if ((error = git_diff_index_to_workdir(&diff, repo, index, &opts)) < 0)
+ goto cleanup;
+
+ data.pathspec = &ps;
+ error = git_diff_foreach(diff, apply_each_file, NULL, NULL, NULL, &data);
+ git_diff_free(diff);
+
+ if (error) /* make sure error is set if callback stopped iteration */
+ git_error_set_after_callback(error);
+
+cleanup:
+ git_pathspec__clear(&ps);
+ return error;
+}
+
+static int index_apply_to_all(
+ git_index *index,
+ int action,
+ const git_strarray *paths,
+ git_index_matched_path_cb cb,
+ void *payload)
+{
+ int error = 0;
+ size_t i;
+ git_pathspec ps;
+ const char *match;
+ git_str path = GIT_STR_INIT;
+
+ GIT_ASSERT_ARG(index);
+
+ if ((error = git_pathspec__init(&ps, paths)) < 0)
+ return error;
+
+ git_vector_sort(&index->entries);
+
+ for (i = 0; !error && i < index->entries.length; ++i) {
+ git_index_entry *entry = git_vector_get(&index->entries, i);
+
+ /* check if path actually matches */
+ if (!git_pathspec__match(
+ &ps.pathspec, entry->path, false, (bool)index->ignore_case,
+ &match, NULL))
+ continue;
+
+ /* issue notification callback if requested */
+ if (cb && (error = cb(entry->path, match, payload)) != 0) {
+ if (error > 0) { /* return > 0 means skip this one */
+ error = 0;
+ continue;
+ }
+ if (error < 0) /* return < 0 means abort */
+ break;
+ }
+
+ /* index manipulation may alter entry, so don't depend on it */
+ if ((error = git_str_sets(&path, entry->path)) < 0)
+ break;
+
+ switch (action) {
+ case INDEX_ACTION_NONE:
+ break;
+ case INDEX_ACTION_UPDATE:
+ error = git_index_add_bypath(index, path.ptr);
+
+ if (error == GIT_ENOTFOUND) {
+ git_error_clear();
+
+ error = git_index_remove_bypath(index, path.ptr);
+
+ if (!error) /* back up foreach if we removed this */
+ i--;
+ }
+ break;
+ case INDEX_ACTION_REMOVE:
+ if (!(error = git_index_remove_bypath(index, path.ptr)))
+ i--; /* back up foreach if we removed this */
+ break;
+ default:
+ git_error_set(GIT_ERROR_INVALID, "unknown index action %d", action);
+ error = -1;
+ break;
+ }
+ }
+
+ git_str_dispose(&path);
+ git_pathspec__clear(&ps);
+
+ return error;
+}
+
+int git_index_remove_all(
+ git_index *index,
+ const git_strarray *pathspec,
+ git_index_matched_path_cb cb,
+ void *payload)
+{
+ int error = index_apply_to_all(
+ index, INDEX_ACTION_REMOVE, pathspec, cb, payload);
+
+ if (error) /* make sure error is set if callback stopped iteration */
+ git_error_set_after_callback(error);
+
+ return error;
+}
+
+int git_index_update_all(
+ git_index *index,
+ const git_strarray *pathspec,
+ git_index_matched_path_cb cb,
+ void *payload)
+{
+ int error = index_apply_to_wd_diff(index, INDEX_ACTION_UPDATE, pathspec, 0, cb, payload);
+ if (error) /* make sure error is set if callback stopped iteration */
+ git_error_set_after_callback(error);
+
+ return error;
+}
+
+int git_index_snapshot_new(git_vector *snap, git_index *index)
+{
+ int error;
+
+ GIT_REFCOUNT_INC(index);
+
+ git_atomic32_inc(&index->readers);
+ git_vector_sort(&index->entries);
+
+ error = git_vector_dup(snap, &index->entries, index->entries._cmp);
+
+ if (error < 0)
+ git_index_snapshot_release(snap, index);
+
+ return error;
+}
+
+void git_index_snapshot_release(git_vector *snap, git_index *index)
+{
+ git_vector_free(snap);
+
+ git_atomic32_dec(&index->readers);
+
+ git_index_free(index);
+}
+
+int git_index_snapshot_find(
+ size_t *out, git_vector *entries, git_vector_cmp entry_srch,
+ const char *path, size_t path_len, int stage)
+{
+ return index_find_in_entries(out, entries, entry_srch, path, path_len, stage);
+}
+
+int git_indexwriter_init(
+ git_indexwriter *writer,
+ git_index *index)
+{
+ int filebuf_hash, error;
+
+ GIT_REFCOUNT_INC(index);
+
+ writer->index = index;
+
+ filebuf_hash = git_filebuf_hash_flags(git_oid_algorithm(index->oid_type));
+ GIT_ASSERT(filebuf_hash);
+
+ if (!index->index_file_path)
+ return create_index_error(-1,
+ "failed to write index: The index is in-memory only");
+
+ if ((error = git_filebuf_open(&writer->file,
+ index->index_file_path,
+ git_filebuf_hash_flags(filebuf_hash),
+ GIT_INDEX_FILE_MODE)) < 0) {
+ if (error == GIT_ELOCKED)
+ git_error_set(GIT_ERROR_INDEX, "the index is locked; this might be due to a concurrent or crashed process");
+
+ return error;
+ }
+
+ writer->should_write = 1;
+
+ return 0;
+}
+
+int git_indexwriter_init_for_operation(
+ git_indexwriter *writer,
+ git_repository *repo,
+ unsigned int *checkout_strategy)
+{
+ git_index *index;
+ int error;
+
+ if ((error = git_repository_index__weakptr(&index, repo)) < 0 ||
+ (error = git_indexwriter_init(writer, index)) < 0)
+ return error;
+
+ writer->should_write = (*checkout_strategy & GIT_CHECKOUT_DONT_WRITE_INDEX) == 0;
+ *checkout_strategy |= GIT_CHECKOUT_DONT_WRITE_INDEX;
+
+ return 0;
+}
+
+int git_indexwriter_commit(git_indexwriter *writer)
+{
+ unsigned char checksum[GIT_HASH_MAX_SIZE];
+ size_t checksum_size;
+ int error;
+
+ if (!writer->should_write)
+ return 0;
+
+ git_vector_sort(&writer->index->entries);
+ git_vector_sort(&writer->index->reuc);
+
+ if ((error = write_index(checksum, &checksum_size, writer->index, &writer->file)) < 0) {
+ git_indexwriter_cleanup(writer);
+ return error;
+ }
+
+ if ((error = git_filebuf_commit(&writer->file)) < 0)
+ return error;
+
+ if ((error = git_futils_filestamp_check(
+ &writer->index->stamp, writer->index->index_file_path)) < 0) {
+ git_error_set(GIT_ERROR_OS, "could not read index timestamp");
+ return -1;
+ }
+
+ writer->index->dirty = 0;
+ writer->index->on_disk = 1;
+ memcpy(writer->index->checksum, checksum, checksum_size);
+
+ git_index_free(writer->index);
+ writer->index = NULL;
+
+ return 0;
+}
+
+void git_indexwriter_cleanup(git_indexwriter *writer)
+{
+ git_filebuf_cleanup(&writer->file);
+
+ git_index_free(writer->index);
+ writer->index = NULL;
+}
+
+/* Deprecated functions */
+
+#ifndef GIT_DEPRECATE_HARD
+int git_index_add_frombuffer(
+ git_index *index, const git_index_entry *source_entry,
+ const void *buffer, size_t len)
+{
+ return git_index_add_from_buffer(index, source_entry, buffer, len);
+}
+#endif
diff --git a/src/libgit2/index.h b/src/libgit2/index.h
new file mode 100644
index 0000000..53c2997
--- /dev/null
+++ b/src/libgit2/index.h
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_index_h__
+#define INCLUDE_index_h__
+
+#include "common.h"
+
+#include "futils.h"
+#include "filebuf.h"
+#include "vector.h"
+#include "idxmap.h"
+#include "tree-cache.h"
+#include "git2/odb.h"
+#include "git2/index.h"
+
+#define GIT_INDEX_FILE "index"
+#define GIT_INDEX_FILE_MODE 0666
+
+extern bool git_index__enforce_unsaved_safety;
+
+struct git_index {
+ git_refcount rc;
+
+ char *index_file_path;
+ git_futils_filestamp stamp;
+ unsigned char checksum[GIT_HASH_MAX_SIZE];
+
+ git_vector entries;
+ git_idxmap *entries_map;
+
+ git_vector deleted; /* deleted entries if readers > 0 */
+ git_atomic32 readers; /* number of active iterators */
+
+ git_oid_t oid_type;
+
+ unsigned int on_disk:1;
+ unsigned int ignore_case:1;
+ unsigned int distrust_filemode:1;
+ unsigned int no_symlinks:1;
+ unsigned int dirty:1; /* whether we have unsaved changes */
+
+ git_tree_cache *tree;
+ git_pool tree_pool;
+
+ git_vector names;
+ git_vector reuc;
+
+ git_vector_cmp entries_cmp_path;
+ git_vector_cmp entries_search;
+ git_vector_cmp entries_search_path;
+ git_vector_cmp reuc_search;
+
+ unsigned int version;
+};
+
+struct git_index_iterator {
+ git_index *index;
+ git_vector snap;
+ size_t cur;
+};
+
+struct git_index_conflict_iterator {
+ git_index *index;
+ size_t cur;
+};
+
+extern void git_index_entry__init_from_stat(
+ git_index_entry *entry, struct stat *st, bool trust_mode);
+
+/* Index entry comparison functions for array sorting */
+extern int git_index_entry_cmp(const void *a, const void *b);
+extern int git_index_entry_icmp(const void *a, const void *b);
+
+/* Index entry search functions for search using a search spec */
+extern int git_index_entry_srch(const void *a, const void *b);
+extern int git_index_entry_isrch(const void *a, const void *b);
+
+/* Index time handling functions */
+GIT_INLINE(bool) git_index_time_eq(const git_index_time *one, const git_index_time *two)
+{
+ if (one->seconds != two->seconds)
+ return false;
+
+#ifdef GIT_USE_NSEC
+ if (one->nanoseconds != two->nanoseconds)
+ return false;
+#endif
+
+ return true;
+}
+
+/*
+ * Test if the given index time is newer than the given existing index entry.
+ * If the timestamps are exactly equivalent, then the given index time is
+ * considered "racily newer" than the existing index entry.
+ */
+GIT_INLINE(bool) git_index_entry_newer_than_index(
+ const git_index_entry *entry, git_index *index)
+{
+ /* If we never read the index, we can't have this race either */
+ if (!index || index->stamp.mtime.tv_sec == 0)
+ return false;
+
+ /* If the timestamp is the same or newer than the index, it's racy */
+#if defined(GIT_USE_NSEC)
+ if ((int32_t)index->stamp.mtime.tv_sec < entry->mtime.seconds)
+ return true;
+ else if ((int32_t)index->stamp.mtime.tv_sec > entry->mtime.seconds)
+ return false;
+ else
+ return (uint32_t)index->stamp.mtime.tv_nsec <= entry->mtime.nanoseconds;
+#else
+ return ((int32_t)index->stamp.mtime.tv_sec) <= entry->mtime.seconds;
+#endif
+}
+
+/* Search index for `path`, returning GIT_ENOTFOUND if it does not exist
+ * (but not setting an error message).
+ *
+ * `at_pos` is set to the position where it is or would be inserted.
+ * Pass `path_len` as strlen of path or 0 to call strlen internally.
+ */
+extern int git_index__find_pos(
+ size_t *at_pos, git_index *index, const char *path, size_t path_len, int stage);
+
+extern int git_index__fill(git_index *index, const git_vector *source_entries);
+
+extern void git_index__set_ignore_case(git_index *index, bool ignore_case);
+
+extern unsigned int git_index__create_mode(unsigned int mode);
+
+GIT_INLINE(const git_futils_filestamp *) git_index__filestamp(git_index *index)
+{
+ return &index->stamp;
+}
+
+GIT_INLINE(unsigned char *) git_index__checksum(git_index *index)
+{
+ return index->checksum;
+}
+
+/* SHA256-aware internal functions */
+
+extern int git_index__new(
+ git_index **index_out,
+ git_oid_t oid_type);
+
+extern int git_index__open(
+ git_index **index_out,
+ const char *index_path,
+ git_oid_t oid_type);
+
+/* Copy the current entries vector *and* increment the index refcount.
+ * Call `git_index__release_snapshot` when done.
+ */
+extern int git_index_snapshot_new(git_vector *snap, git_index *index);
+extern void git_index_snapshot_release(git_vector *snap, git_index *index);
+
+/* Allow searching in a snapshot; entries must already be sorted! */
+extern int git_index_snapshot_find(
+ size_t *at_pos, git_vector *snap, git_vector_cmp entry_srch,
+ const char *path, size_t path_len, int stage);
+
+/* Replace an index with a new index */
+int git_index_read_index(git_index *index, const git_index *new_index);
+
+GIT_INLINE(int) git_index_is_dirty(git_index *index)
+{
+ return index->dirty;
+}
+
+extern int git_index_read_safely(git_index *index);
+
+typedef struct {
+ git_index *index;
+ git_filebuf file;
+ unsigned int should_write:1;
+} git_indexwriter;
+
+#define GIT_INDEXWRITER_INIT { NULL, GIT_FILEBUF_INIT }
+
+/* Lock the index for eventual writing. */
+extern int git_indexwriter_init(git_indexwriter *writer, git_index *index);
+
+/* Lock the index for eventual writing by a repository operation: a merge,
+ * revert, cherry-pick or a rebase. Note that the given checkout strategy
+ * will be updated for the operation's use so that checkout will not write
+ * the index.
+ */
+extern int git_indexwriter_init_for_operation(
+ git_indexwriter *writer,
+ git_repository *repo,
+ unsigned int *checkout_strategy);
+
+/* Write the index and unlock it. */
+extern int git_indexwriter_commit(git_indexwriter *writer);
+
+/* Cleanup an index writing session, unlocking the file (if it is still
+ * locked and freeing any data structures.
+ */
+extern void git_indexwriter_cleanup(git_indexwriter *writer);
+
+#endif
diff --git a/src/libgit2/indexer.c b/src/libgit2/indexer.c
new file mode 100644
index 0000000..e559a19
--- /dev/null
+++ b/src/libgit2/indexer.c
@@ -0,0 +1,1483 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "indexer.h"
+
+#include "git2/indexer.h"
+#include "git2/object.h"
+
+#include "commit.h"
+#include "tree.h"
+#include "tag.h"
+#include "pack.h"
+#include "mwindow.h"
+#include "posix.h"
+#include "pack.h"
+#include "filebuf.h"
+#include "oid.h"
+#include "oidarray.h"
+#include "oidmap.h"
+#include "zstream.h"
+#include "object.h"
+
+size_t git_indexer__max_objects = UINT32_MAX;
+
+#define UINT31_MAX (0x7FFFFFFF)
+
+struct entry {
+ git_oid oid;
+ uint32_t crc;
+ uint32_t offset;
+ uint64_t offset_long;
+};
+
+struct git_indexer {
+ unsigned int parsed_header :1,
+ pack_committed :1,
+ have_stream :1,
+ have_delta :1,
+ do_fsync :1,
+ do_verify :1;
+ git_oid_t oid_type;
+ struct git_pack_header hdr;
+ struct git_pack_file *pack;
+ unsigned int mode;
+ off64_t off;
+ off64_t entry_start;
+ git_object_t entry_type;
+ git_str entry_data;
+ git_packfile_stream stream;
+ size_t nr_objects;
+ git_vector objects;
+ git_vector deltas;
+ unsigned int fanout[256];
+ git_hash_ctx hash_ctx;
+ unsigned char checksum[GIT_HASH_MAX_SIZE];
+ char name[(GIT_HASH_MAX_SIZE * 2) + 1];
+ git_indexer_progress_cb progress_cb;
+ void *progress_payload;
+ char objbuf[8*1024];
+
+ /* OIDs referenced from pack objects. Used for verification. */
+ git_oidmap *expected_oids;
+
+ /* Needed to look up objects which we want to inject to fix a thin pack */
+ git_odb *odb;
+
+ /* Fields for calculating the packfile trailer (hash of everything before it) */
+ char inbuf[GIT_HASH_MAX_SIZE];
+ size_t inbuf_len;
+ git_hash_ctx trailer;
+};
+
+struct delta_info {
+ off64_t delta_off;
+};
+
+#ifndef GIT_DEPRECATE_HARD
+const git_oid *git_indexer_hash(const git_indexer *idx)
+{
+ return (git_oid *)idx->checksum;
+}
+#endif
+
+const char *git_indexer_name(const git_indexer *idx)
+{
+ return idx->name;
+}
+
+static int parse_header(struct git_pack_header *hdr, struct git_pack_file *pack)
+{
+ int error;
+ git_map map;
+
+ if ((error = p_mmap(&map, sizeof(*hdr), GIT_PROT_READ, GIT_MAP_SHARED, pack->mwf.fd, 0)) < 0)
+ return error;
+
+ memcpy(hdr, map.data, sizeof(*hdr));
+ p_munmap(&map);
+
+ /* Verify we recognize this pack file format. */
+ if (hdr->hdr_signature != ntohl(PACK_SIGNATURE)) {
+ git_error_set(GIT_ERROR_INDEXER, "wrong pack signature");
+ return -1;
+ }
+
+ if (!pack_version_ok(hdr->hdr_version)) {
+ git_error_set(GIT_ERROR_INDEXER, "wrong pack version");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int objects_cmp(const void *a, const void *b)
+{
+ const struct entry *entrya = a;
+ const struct entry *entryb = b;
+
+ return git_oid__cmp(&entrya->oid, &entryb->oid);
+}
+
+int git_indexer_options_init(git_indexer_options *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_indexer_options, GIT_INDEXER_OPTIONS_INIT);
+ return 0;
+}
+
+#ifndef GIT_DEPRECATE_HARD
+int git_indexer_init_options(git_indexer_options *opts, unsigned int version)
+{
+ return git_indexer_options_init(opts, version);
+}
+#endif
+
+GIT_INLINE(git_hash_algorithm_t) indexer_hash_algorithm(git_indexer *idx)
+{
+ switch (idx->oid_type) {
+ case GIT_OID_SHA1:
+ return GIT_HASH_ALGORITHM_SHA1;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ case GIT_OID_SHA256:
+ return GIT_HASH_ALGORITHM_SHA256;
+#endif
+ }
+
+ return GIT_HASH_ALGORITHM_NONE;
+}
+
+static int indexer_new(
+ git_indexer **out,
+ const char *prefix,
+ git_oid_t oid_type,
+ unsigned int mode,
+ git_odb *odb,
+ git_indexer_options *in_opts)
+{
+ git_indexer_options opts = GIT_INDEXER_OPTIONS_INIT;
+ git_indexer *idx;
+ git_str path = GIT_STR_INIT, tmp_path = GIT_STR_INIT;
+ static const char suff[] = "/pack";
+ git_hash_algorithm_t checksum_type;
+ int error, fd = -1;
+
+ if (in_opts)
+ memcpy(&opts, in_opts, sizeof(opts));
+
+ idx = git__calloc(1, sizeof(git_indexer));
+ GIT_ERROR_CHECK_ALLOC(idx);
+ idx->oid_type = oid_type;
+ idx->odb = odb;
+ idx->progress_cb = opts.progress_cb;
+ idx->progress_payload = opts.progress_cb_payload;
+ idx->mode = mode ? mode : GIT_PACK_FILE_MODE;
+ git_str_init(&idx->entry_data, 0);
+
+ checksum_type = indexer_hash_algorithm(idx);
+
+ if ((error = git_hash_ctx_init(&idx->hash_ctx, checksum_type)) < 0 ||
+ (error = git_hash_ctx_init(&idx->trailer, checksum_type)) < 0 ||
+ (error = git_oidmap_new(&idx->expected_oids)) < 0)
+ goto cleanup;
+
+ idx->do_verify = opts.verify;
+
+ if (git_repository__fsync_gitdir)
+ idx->do_fsync = 1;
+
+ error = git_str_joinpath(&path, prefix, suff);
+ if (error < 0)
+ goto cleanup;
+
+ fd = git_futils_mktmp(&tmp_path, git_str_cstr(&path), idx->mode);
+ git_str_dispose(&path);
+ if (fd < 0)
+ goto cleanup;
+
+ error = git_packfile_alloc(&idx->pack, git_str_cstr(&tmp_path), oid_type);
+ git_str_dispose(&tmp_path);
+
+ if (error < 0)
+ goto cleanup;
+
+ idx->pack->mwf.fd = fd;
+ if ((error = git_mwindow_file_register(&idx->pack->mwf)) < 0)
+ goto cleanup;
+
+ *out = idx;
+ return 0;
+
+cleanup:
+ if (fd != -1)
+ p_close(fd);
+
+ if (git_str_len(&tmp_path) > 0)
+ p_unlink(git_str_cstr(&tmp_path));
+
+ if (idx->pack != NULL)
+ p_unlink(idx->pack->pack_name);
+
+ git_str_dispose(&path);
+ git_str_dispose(&tmp_path);
+ git__free(idx);
+ return -1;
+}
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+int git_indexer_new(
+ git_indexer **out,
+ const char *prefix,
+ git_oid_t oid_type,
+ git_indexer_options *opts)
+{
+ return indexer_new(
+ out,
+ prefix,
+ oid_type,
+ opts ? opts->mode : 0,
+ opts ? opts->odb : NULL,
+ opts);
+}
+#else
+int git_indexer_new(
+ git_indexer **out,
+ const char *prefix,
+ unsigned int mode,
+ git_odb *odb,
+ git_indexer_options *opts)
+{
+ return indexer_new(out, prefix, GIT_OID_SHA1, mode, odb, opts);
+}
+#endif
+
+void git_indexer__set_fsync(git_indexer *idx, int do_fsync)
+{
+ idx->do_fsync = !!do_fsync;
+}
+
+/* Try to store the delta so we can try to resolve it later */
+static int store_delta(git_indexer *idx)
+{
+ struct delta_info *delta;
+
+ delta = git__calloc(1, sizeof(struct delta_info));
+ GIT_ERROR_CHECK_ALLOC(delta);
+ delta->delta_off = idx->entry_start;
+
+ if (git_vector_insert(&idx->deltas, delta) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int hash_header(git_hash_ctx *ctx, off64_t len, git_object_t type)
+{
+ char buffer[64];
+ size_t hdrlen;
+ int error;
+
+ if ((error = git_odb__format_object_header(&hdrlen,
+ buffer, sizeof(buffer), (size_t)len, type)) < 0)
+ return error;
+
+ return git_hash_update(ctx, buffer, hdrlen);
+}
+
+static int hash_object_stream(git_indexer*idx, git_packfile_stream *stream)
+{
+ ssize_t read;
+
+ GIT_ASSERT_ARG(idx);
+ GIT_ASSERT_ARG(stream);
+
+ do {
+ if ((read = git_packfile_stream_read(stream, idx->objbuf, sizeof(idx->objbuf))) < 0)
+ break;
+
+ if (idx->do_verify)
+ git_str_put(&idx->entry_data, idx->objbuf, read);
+
+ git_hash_update(&idx->hash_ctx, idx->objbuf, read);
+ } while (read > 0);
+
+ if (read < 0)
+ return (int)read;
+
+ return 0;
+}
+
+/* In order to create the packfile stream, we need to skip over the delta base description */
+static int advance_delta_offset(git_indexer *idx, git_object_t type)
+{
+ git_mwindow *w = NULL;
+
+ GIT_ASSERT_ARG(type == GIT_OBJECT_REF_DELTA || type == GIT_OBJECT_OFS_DELTA);
+
+ if (type == GIT_OBJECT_REF_DELTA) {
+ idx->off += git_oid_size(idx->oid_type);
+ } else {
+ off64_t base_off;
+ int error = get_delta_base(&base_off, idx->pack, &w, &idx->off, type, idx->entry_start);
+ git_mwindow_close(&w);
+ if (error < 0)
+ return error;
+ }
+
+ return 0;
+}
+
+/* Read from the stream and discard any output */
+static int read_object_stream(git_indexer *idx, git_packfile_stream *stream)
+{
+ ssize_t read;
+
+ GIT_ASSERT_ARG(stream);
+
+ do {
+ read = git_packfile_stream_read(stream, idx->objbuf, sizeof(idx->objbuf));
+ } while (read > 0);
+
+ if (read < 0)
+ return (int)read;
+
+ return 0;
+}
+
+static int crc_object(uint32_t *crc_out, git_mwindow_file *mwf, off64_t start, off64_t size)
+{
+ void *ptr;
+ uint32_t crc;
+ unsigned int left, len;
+ git_mwindow *w = NULL;
+
+ crc = crc32(0L, Z_NULL, 0);
+ while (size) {
+ ptr = git_mwindow_open(mwf, &w, start, (size_t)size, &left);
+ if (ptr == NULL)
+ return -1;
+
+ len = min(left, (unsigned int)size);
+ crc = crc32(crc, ptr, len);
+ size -= len;
+ start += len;
+ git_mwindow_close(&w);
+ }
+
+ *crc_out = htonl(crc);
+ return 0;
+}
+
+static int add_expected_oid(git_indexer *idx, const git_oid *oid)
+{
+ /*
+ * If we know about that object because it is stored in our ODB or
+ * because we have already processed it as part of our pack file, we do
+ * not have to expect it.
+ */
+ if ((!idx->odb || !git_odb_exists(idx->odb, oid)) &&
+ !git_oidmap_exists(idx->pack->idx_cache, oid) &&
+ !git_oidmap_exists(idx->expected_oids, oid)) {
+ git_oid *dup = git__malloc(sizeof(*oid));
+ GIT_ERROR_CHECK_ALLOC(dup);
+ git_oid_cpy(dup, oid);
+ return git_oidmap_set(idx->expected_oids, dup, dup);
+ }
+
+ return 0;
+}
+
+static int check_object_connectivity(git_indexer *idx, const git_rawobj *obj)
+{
+ git_object *object;
+ git_oid *expected;
+ int error = 0;
+
+ if (obj->type != GIT_OBJECT_BLOB &&
+ obj->type != GIT_OBJECT_TREE &&
+ obj->type != GIT_OBJECT_COMMIT &&
+ obj->type != GIT_OBJECT_TAG)
+ return 0;
+
+ if (git_object__from_raw(&object, obj->data, obj->len, obj->type, idx->oid_type) < 0) {
+ /*
+ * parse_raw returns EINVALID on invalid data; downgrade
+ * that to a normal -1 error code.
+ */
+ error = -1;
+ goto out;
+ }
+
+ if ((expected = git_oidmap_get(idx->expected_oids, &object->cached.oid)) != NULL) {
+ git_oidmap_delete(idx->expected_oids, &object->cached.oid);
+ git__free(expected);
+ }
+
+ /*
+ * Check whether this is a known object. If so, we can just continue as
+ * we assume that the ODB has a complete graph.
+ */
+ if (idx->odb && git_odb_exists(idx->odb, &object->cached.oid))
+ return 0;
+
+ switch (obj->type) {
+ case GIT_OBJECT_TREE:
+ {
+ git_tree *tree = (git_tree *) object;
+ git_tree_entry *entry;
+ size_t i;
+
+ git_array_foreach(tree->entries, i, entry)
+ if (add_expected_oid(idx, &entry->oid) < 0)
+ goto out;
+
+ break;
+ }
+ case GIT_OBJECT_COMMIT:
+ {
+ git_commit *commit = (git_commit *) object;
+ git_oid *parent_oid;
+ size_t i;
+
+ git_array_foreach(commit->parent_ids, i, parent_oid)
+ if (add_expected_oid(idx, parent_oid) < 0)
+ goto out;
+
+ if (add_expected_oid(idx, &commit->tree_id) < 0)
+ goto out;
+
+ break;
+ }
+ case GIT_OBJECT_TAG:
+ {
+ git_tag *tag = (git_tag *) object;
+
+ if (add_expected_oid(idx, &tag->target) < 0)
+ goto out;
+
+ break;
+ }
+ case GIT_OBJECT_BLOB:
+ default:
+ break;
+ }
+
+out:
+ git_object_free(object);
+
+ return error;
+}
+
+static int store_object(git_indexer *idx)
+{
+ int i, error;
+ git_oid oid;
+ struct entry *entry;
+ off64_t entry_size;
+ struct git_pack_entry *pentry;
+ off64_t entry_start = idx->entry_start;
+
+ entry = git__calloc(1, sizeof(*entry));
+ GIT_ERROR_CHECK_ALLOC(entry);
+
+ pentry = git__calloc(1, sizeof(struct git_pack_entry));
+ GIT_ERROR_CHECK_ALLOC(pentry);
+
+ if (git_hash_final(oid.id, &idx->hash_ctx)) {
+ git__free(pentry);
+ goto on_error;
+ }
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ oid.type = idx->oid_type;
+#endif
+
+ entry_size = idx->off - entry_start;
+ if (entry_start > UINT31_MAX) {
+ entry->offset = UINT32_MAX;
+ entry->offset_long = entry_start;
+ } else {
+ entry->offset = (uint32_t)entry_start;
+ }
+
+ if (idx->do_verify) {
+ git_rawobj rawobj = {
+ idx->entry_data.ptr,
+ idx->entry_data.size,
+ idx->entry_type
+ };
+
+ if ((error = check_object_connectivity(idx, &rawobj)) < 0)
+ goto on_error;
+ }
+
+ git_oid_cpy(&pentry->id, &oid);
+ pentry->offset = entry_start;
+
+ if (git_oidmap_exists(idx->pack->idx_cache, &pentry->id)) {
+ const char *idstr = git_oid_tostr_s(&pentry->id);
+
+ if (!idstr)
+ git_error_set(GIT_ERROR_INDEXER, "failed to parse object id");
+ else
+ git_error_set(GIT_ERROR_INDEXER, "duplicate object %s found in pack", idstr);
+
+ git__free(pentry);
+ goto on_error;
+ }
+
+ if ((error = git_oidmap_set(idx->pack->idx_cache, &pentry->id, pentry)) < 0) {
+ git__free(pentry);
+ git_error_set_oom();
+ goto on_error;
+ }
+
+ git_oid_cpy(&entry->oid, &oid);
+
+ if (crc_object(&entry->crc, &idx->pack->mwf, entry_start, entry_size) < 0)
+ goto on_error;
+
+ /* Add the object to the list */
+ if (git_vector_insert(&idx->objects, entry) < 0)
+ goto on_error;
+
+ for (i = oid.id[0]; i < 256; ++i) {
+ idx->fanout[i]++;
+ }
+
+ return 0;
+
+on_error:
+ git__free(entry);
+
+ return -1;
+}
+
+GIT_INLINE(bool) has_entry(git_indexer *idx, git_oid *id)
+{
+ return git_oidmap_exists(idx->pack->idx_cache, id);
+}
+
+static int save_entry(git_indexer *idx, struct entry *entry, struct git_pack_entry *pentry, off64_t entry_start)
+{
+ int i;
+
+ if (entry_start > UINT31_MAX) {
+ entry->offset = UINT32_MAX;
+ entry->offset_long = entry_start;
+ } else {
+ entry->offset = (uint32_t)entry_start;
+ }
+
+ pentry->offset = entry_start;
+
+ if (git_oidmap_exists(idx->pack->idx_cache, &pentry->id) ||
+ git_oidmap_set(idx->pack->idx_cache, &pentry->id, pentry) < 0) {
+ git_error_set(GIT_ERROR_INDEXER, "cannot insert object into pack");
+ return -1;
+ }
+
+ /* Add the object to the list */
+ if (git_vector_insert(&idx->objects, entry) < 0)
+ return -1;
+
+ for (i = entry->oid.id[0]; i < 256; ++i) {
+ idx->fanout[i]++;
+ }
+
+ return 0;
+}
+
+static int hash_and_save(git_indexer *idx, git_rawobj *obj, off64_t entry_start)
+{
+ git_oid oid;
+ size_t entry_size;
+ struct entry *entry;
+ struct git_pack_entry *pentry = NULL;
+
+ entry = git__calloc(1, sizeof(*entry));
+ GIT_ERROR_CHECK_ALLOC(entry);
+
+ if (git_odb__hashobj(&oid, obj, idx->oid_type) < 0) {
+ git_error_set(GIT_ERROR_INDEXER, "failed to hash object");
+ goto on_error;
+ }
+
+ pentry = git__calloc(1, sizeof(struct git_pack_entry));
+ GIT_ERROR_CHECK_ALLOC(pentry);
+
+ git_oid_cpy(&pentry->id, &oid);
+ git_oid_cpy(&entry->oid, &oid);
+ entry->crc = crc32(0L, Z_NULL, 0);
+
+ entry_size = (size_t)(idx->off - entry_start);
+ if (crc_object(&entry->crc, &idx->pack->mwf, entry_start, entry_size) < 0)
+ goto on_error;
+
+ return save_entry(idx, entry, pentry, entry_start);
+
+on_error:
+ git__free(pentry);
+ git__free(entry);
+ git__free(obj->data);
+ return -1;
+}
+
+static int do_progress_callback(git_indexer *idx, git_indexer_progress *stats)
+{
+ if (idx->progress_cb)
+ return git_error_set_after_callback_function(
+ idx->progress_cb(stats, idx->progress_payload),
+ "indexer progress");
+ return 0;
+}
+
+/* Hash everything but the checksum trailer */
+static void hash_partially(git_indexer *idx, const uint8_t *data, size_t size)
+{
+ size_t to_expell, to_keep;
+ size_t oid_size = git_oid_size(idx->oid_type);
+
+ if (size == 0)
+ return;
+
+ /*
+ * Easy case, dump the buffer and the data minus the trailing
+ * checksum (SHA1 or SHA256).
+ */
+ if (size >= oid_size) {
+ git_hash_update(&idx->trailer, idx->inbuf, idx->inbuf_len);
+ git_hash_update(&idx->trailer, data, size - oid_size);
+
+ data += size - oid_size;
+ memcpy(idx->inbuf, data, oid_size);
+ idx->inbuf_len = oid_size;
+ return;
+ }
+
+ /* We can just append */
+ if (idx->inbuf_len + size <= oid_size) {
+ memcpy(idx->inbuf + idx->inbuf_len, data, size);
+ idx->inbuf_len += size;
+ return;
+ }
+
+ /* We need to partially drain the buffer and then append */
+ to_keep = oid_size - size;
+ to_expell = idx->inbuf_len - to_keep;
+
+ git_hash_update(&idx->trailer, idx->inbuf, to_expell);
+
+ memmove(idx->inbuf, idx->inbuf + to_expell, to_keep);
+ memcpy(idx->inbuf + to_keep, data, size);
+ idx->inbuf_len += size - to_expell;
+}
+
+#if defined(NO_MMAP) || !defined(GIT_WIN32)
+
+static int write_at(git_indexer *idx, const void *data, off64_t offset, size_t size)
+{
+ size_t remaining_size = size;
+ const char *ptr = (const char *)data;
+
+ /* Handle data size larger that ssize_t */
+ while (remaining_size > 0) {
+ ssize_t nb;
+ HANDLE_EINTR(nb, p_pwrite(idx->pack->mwf.fd, (void *)ptr,
+ remaining_size, offset));
+ if (nb <= 0)
+ return -1;
+
+ ptr += nb;
+ offset += nb;
+ remaining_size -= nb;
+ }
+
+ return 0;
+}
+
+static int append_to_pack(git_indexer *idx, const void *data, size_t size)
+{
+ if (write_at(idx, data, idx->pack->mwf.size, size) < 0) {
+ git_error_set(GIT_ERROR_OS, "cannot extend packfile '%s'", idx->pack->pack_name);
+ return -1;
+ }
+
+ return 0;
+}
+
+#else
+
+/*
+ * Windows may keep different views to a networked file for the mmap- and
+ * open-accessed versions of a file, so any writes done through
+ * `write(2)`/`pwrite(2)` may not be reflected on the data that `mmap(2)` is
+ * able to read.
+ */
+
+static int write_at(git_indexer *idx, const void *data, off64_t offset, size_t size)
+{
+ git_file fd = idx->pack->mwf.fd;
+ size_t mmap_alignment;
+ size_t page_offset;
+ off64_t page_start;
+ unsigned char *map_data;
+ git_map map;
+ int error;
+
+ GIT_ASSERT_ARG(data);
+ GIT_ASSERT_ARG(size);
+
+ if ((error = git__mmap_alignment(&mmap_alignment)) < 0)
+ return error;
+
+ /* the offset needs to be at the mmap boundary for the platform */
+ page_offset = offset % mmap_alignment;
+ page_start = offset - page_offset;
+
+ if ((error = p_mmap(&map, page_offset + size, GIT_PROT_WRITE, GIT_MAP_SHARED, fd, page_start)) < 0)
+ return error;
+
+ map_data = (unsigned char *)map.data;
+ memcpy(map_data + page_offset, data, size);
+ p_munmap(&map);
+
+ return 0;
+}
+
+static int append_to_pack(git_indexer *idx, const void *data, size_t size)
+{
+ off64_t new_size;
+ size_t mmap_alignment;
+ size_t page_offset;
+ off64_t page_start;
+ off64_t current_size = idx->pack->mwf.size;
+ int error;
+
+ if (!size)
+ return 0;
+
+ if ((error = git__mmap_alignment(&mmap_alignment)) < 0)
+ return error;
+
+ /* Write a single byte to force the file system to allocate space now or
+ * report an error, since we can't report errors when writing using mmap.
+ * Round the size up to the nearest page so that we only need to perform file
+ * I/O when we add a page, instead of whenever we write even a single byte. */
+ new_size = current_size + size;
+ page_offset = new_size % mmap_alignment;
+ page_start = new_size - page_offset;
+
+ if (p_pwrite(idx->pack->mwf.fd, data, 1, page_start + mmap_alignment - 1) < 0) {
+ git_error_set(GIT_ERROR_OS, "cannot extend packfile '%s'", idx->pack->pack_name);
+ return -1;
+ }
+
+ return write_at(idx, data, idx->pack->mwf.size, size);
+}
+
+#endif
+
+static int read_stream_object(git_indexer *idx, git_indexer_progress *stats)
+{
+ git_packfile_stream *stream = &idx->stream;
+ off64_t entry_start = idx->off;
+ size_t oid_size, entry_size;
+ git_object_t type;
+ git_mwindow *w = NULL;
+ int error;
+
+ oid_size = git_oid_size(idx->oid_type);
+
+ if (idx->pack->mwf.size <= idx->off + (long long)oid_size)
+ return GIT_EBUFS;
+
+ if (!idx->have_stream) {
+ error = git_packfile_unpack_header(&entry_size, &type, idx->pack, &w, &idx->off);
+ if (error == GIT_EBUFS) {
+ idx->off = entry_start;
+ return error;
+ }
+ if (error < 0)
+ return error;
+
+ git_mwindow_close(&w);
+ idx->entry_start = entry_start;
+ git_hash_init(&idx->hash_ctx);
+ git_str_clear(&idx->entry_data);
+
+ if (type == GIT_OBJECT_REF_DELTA || type == GIT_OBJECT_OFS_DELTA) {
+ error = advance_delta_offset(idx, type);
+ if (error == GIT_EBUFS) {
+ idx->off = entry_start;
+ return error;
+ }
+ if (error < 0)
+ return error;
+
+ idx->have_delta = 1;
+ } else {
+ idx->have_delta = 0;
+
+ error = hash_header(&idx->hash_ctx, entry_size, type);
+ if (error < 0)
+ return error;
+ }
+
+ idx->have_stream = 1;
+ idx->entry_type = type;
+
+ error = git_packfile_stream_open(stream, idx->pack, idx->off);
+ if (error < 0)
+ return error;
+ }
+
+ if (idx->have_delta) {
+ error = read_object_stream(idx, stream);
+ } else {
+ error = hash_object_stream(idx, stream);
+ }
+
+ idx->off = stream->curpos;
+ if (error == GIT_EBUFS)
+ return error;
+
+ /* We want to free the stream reasorces no matter what here */
+ idx->have_stream = 0;
+ git_packfile_stream_dispose(stream);
+
+ if (error < 0)
+ return error;
+
+ if (idx->have_delta) {
+ error = store_delta(idx);
+ } else {
+ error = store_object(idx);
+ }
+
+ if (error < 0)
+ return error;
+
+ if (!idx->have_delta) {
+ stats->indexed_objects++;
+ }
+ stats->received_objects++;
+
+ if ((error = do_progress_callback(idx, stats)) != 0)
+ return error;
+
+ return 0;
+}
+
+int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_indexer_progress *stats)
+{
+ int error = -1;
+ struct git_pack_header *hdr = &idx->hdr;
+ git_mwindow_file *mwf = &idx->pack->mwf;
+
+ GIT_ASSERT_ARG(idx);
+ GIT_ASSERT_ARG(data);
+ GIT_ASSERT_ARG(stats);
+
+ if ((error = append_to_pack(idx, data, size)) < 0)
+ return error;
+
+ hash_partially(idx, data, (int)size);
+
+ /* Make sure we set the new size of the pack */
+ idx->pack->mwf.size += size;
+
+ if (!idx->parsed_header) {
+ unsigned int total_objects;
+
+ if ((unsigned)idx->pack->mwf.size < sizeof(struct git_pack_header))
+ return 0;
+
+ if ((error = parse_header(&idx->hdr, idx->pack)) < 0)
+ return error;
+
+ idx->parsed_header = 1;
+ idx->nr_objects = ntohl(hdr->hdr_entries);
+ idx->off = sizeof(struct git_pack_header);
+
+ if (idx->nr_objects <= git_indexer__max_objects) {
+ total_objects = (unsigned int)idx->nr_objects;
+ } else {
+ git_error_set(GIT_ERROR_INDEXER, "too many objects");
+ return -1;
+ }
+
+ if (git_oidmap_new(&idx->pack->idx_cache) < 0)
+ return -1;
+
+ idx->pack->has_cache = 1;
+ if (git_vector_init(&idx->objects, total_objects, objects_cmp) < 0)
+ return -1;
+
+ if (git_vector_init(&idx->deltas, total_objects / 2, NULL) < 0)
+ return -1;
+
+ stats->received_objects = 0;
+ stats->local_objects = 0;
+ stats->total_deltas = 0;
+ stats->indexed_deltas = 0;
+ stats->indexed_objects = 0;
+ stats->total_objects = total_objects;
+
+ if ((error = do_progress_callback(idx, stats)) != 0)
+ return error;
+ }
+
+ /* Now that we have data in the pack, let's try to parse it */
+
+ /* As the file grows any windows we try to use will be out of date */
+ if ((error = git_mwindow_free_all(mwf)) < 0)
+ goto on_error;
+
+ while (stats->indexed_objects < idx->nr_objects) {
+ if ((error = read_stream_object(idx, stats)) != 0) {
+ if (error == GIT_EBUFS)
+ break;
+ else
+ goto on_error;
+ }
+ }
+
+ return 0;
+
+on_error:
+ git_mwindow_free_all(mwf);
+ return error;
+}
+
+static int index_path(git_str *path, git_indexer *idx, const char *suffix)
+{
+ const char prefix[] = "pack-";
+ size_t slash = (size_t)path->size;
+
+ /* search backwards for '/' */
+ while (slash > 0 && path->ptr[slash - 1] != '/')
+ slash--;
+
+ if (git_str_grow(path, slash + 1 + strlen(prefix) +
+ git_oid_hexsize(idx->oid_type) + strlen(suffix) + 1) < 0)
+ return -1;
+
+ git_str_truncate(path, slash);
+ git_str_puts(path, prefix);
+ git_str_puts(path, idx->name);
+ git_str_puts(path, suffix);
+
+ return git_str_oom(path) ? -1 : 0;
+}
+
+/**
+ * Rewind the packfile by the trailer, as we might need to fix the
+ * packfile by injecting objects at the tail and must overwrite it.
+ */
+static int seek_back_trailer(git_indexer *idx)
+{
+ idx->pack->mwf.size -= git_oid_size(idx->oid_type);
+ return git_mwindow_free_all(&idx->pack->mwf);
+}
+
+static int inject_object(git_indexer *idx, git_oid *id)
+{
+ git_odb_object *obj = NULL;
+ struct entry *entry = NULL;
+ struct git_pack_entry *pentry = NULL;
+ unsigned char empty_checksum[GIT_HASH_MAX_SIZE] = {0};
+ unsigned char hdr[64];
+ git_str buf = GIT_STR_INIT;
+ off64_t entry_start;
+ const void *data;
+ size_t len, hdr_len;
+ size_t checksum_size;
+ int error;
+
+ checksum_size = git_hash_size(indexer_hash_algorithm(idx));
+
+ if ((error = seek_back_trailer(idx)) < 0)
+ goto cleanup;
+
+ entry_start = idx->pack->mwf.size;
+
+ if ((error = git_odb_read(&obj, idx->odb, id)) < 0) {
+ git_error_set(GIT_ERROR_INDEXER, "missing delta bases");
+ goto cleanup;
+ }
+
+ data = git_odb_object_data(obj);
+ len = git_odb_object_size(obj);
+
+ entry = git__calloc(1, sizeof(*entry));
+ GIT_ERROR_CHECK_ALLOC(entry);
+
+ entry->crc = crc32(0L, Z_NULL, 0);
+
+ /* Write out the object header */
+ if ((error = git_packfile__object_header(&hdr_len, hdr, len, git_odb_object_type(obj))) < 0 ||
+ (error = append_to_pack(idx, hdr, hdr_len)) < 0)
+ goto cleanup;
+
+ idx->pack->mwf.size += hdr_len;
+ entry->crc = crc32(entry->crc, hdr, (uInt)hdr_len);
+
+ if ((error = git_zstream_deflatebuf(&buf, data, len)) < 0)
+ goto cleanup;
+
+ /* And then the compressed object */
+ if ((error = append_to_pack(idx, buf.ptr, buf.size)) < 0)
+ goto cleanup;
+
+ idx->pack->mwf.size += buf.size;
+ entry->crc = htonl(crc32(entry->crc, (unsigned char *)buf.ptr, (uInt)buf.size));
+ git_str_dispose(&buf);
+
+ /* Write a fake trailer so the pack functions play ball */
+
+ if ((error = append_to_pack(idx, empty_checksum, checksum_size)) < 0)
+ goto cleanup;
+
+ idx->pack->mwf.size += git_oid_size(idx->oid_type);
+
+ pentry = git__calloc(1, sizeof(struct git_pack_entry));
+ GIT_ERROR_CHECK_ALLOC(pentry);
+
+ git_oid_cpy(&pentry->id, id);
+ git_oid_cpy(&entry->oid, id);
+ idx->off = entry_start + hdr_len + len;
+
+ error = save_entry(idx, entry, pentry, entry_start);
+
+cleanup:
+ if (error) {
+ git__free(entry);
+ git__free(pentry);
+ }
+
+ git_odb_object_free(obj);
+ return error;
+}
+
+static int fix_thin_pack(git_indexer *idx, git_indexer_progress *stats)
+{
+ int error, found_ref_delta = 0;
+ unsigned int i;
+ struct delta_info *delta;
+ size_t size;
+ git_object_t type;
+ git_mwindow *w = NULL;
+ off64_t curpos = 0;
+ unsigned char *base_info;
+ unsigned int left = 0;
+ git_oid base;
+
+ GIT_ASSERT(git_vector_length(&idx->deltas) > 0);
+
+ if (idx->odb == NULL) {
+ git_error_set(GIT_ERROR_INDEXER, "cannot fix a thin pack without an ODB");
+ return -1;
+ }
+
+ /* Loop until we find the first REF delta */
+ git_vector_foreach(&idx->deltas, i, delta) {
+ if (!delta)
+ continue;
+
+ curpos = delta->delta_off;
+ error = git_packfile_unpack_header(&size, &type, idx->pack, &w, &curpos);
+ if (error < 0)
+ return error;
+
+ if (type == GIT_OBJECT_REF_DELTA) {
+ found_ref_delta = 1;
+ break;
+ }
+ }
+
+ if (!found_ref_delta) {
+ git_error_set(GIT_ERROR_INDEXER, "no REF_DELTA found, cannot inject object");
+ return -1;
+ }
+
+ /* curpos now points to the base information, which is an OID */
+ base_info = git_mwindow_open(&idx->pack->mwf, &w, curpos, git_oid_size(idx->oid_type), &left);
+ if (base_info == NULL) {
+ git_error_set(GIT_ERROR_INDEXER, "failed to map delta information");
+ return -1;
+ }
+
+ git_oid__fromraw(&base, base_info, idx->oid_type);
+ git_mwindow_close(&w);
+
+ if (has_entry(idx, &base))
+ return 0;
+
+ if (inject_object(idx, &base) < 0)
+ return -1;
+
+ stats->local_objects++;
+
+ return 0;
+}
+
+static int resolve_deltas(git_indexer *idx, git_indexer_progress *stats)
+{
+ unsigned int i;
+ int error;
+ struct delta_info *delta;
+ int progressed = 0, non_null = 0, progress_cb_result;
+
+ while (idx->deltas.length > 0) {
+ progressed = 0;
+ non_null = 0;
+ git_vector_foreach(&idx->deltas, i, delta) {
+ git_rawobj obj = {0};
+
+ if (!delta)
+ continue;
+
+ non_null = 1;
+ idx->off = delta->delta_off;
+ if ((error = git_packfile_unpack(&obj, idx->pack, &idx->off)) < 0) {
+ if (error == GIT_PASSTHROUGH) {
+ /* We have not seen the base object, we'll try again later. */
+ continue;
+ }
+ return -1;
+ }
+
+ if (idx->do_verify && check_object_connectivity(idx, &obj) < 0)
+ /* TODO: error? continue? */
+ continue;
+
+ if (hash_and_save(idx, &obj, delta->delta_off) < 0)
+ continue;
+
+ git__free(obj.data);
+ stats->indexed_objects++;
+ stats->indexed_deltas++;
+ progressed = 1;
+ if ((progress_cb_result = do_progress_callback(idx, stats)) < 0)
+ return progress_cb_result;
+
+ /* remove from the list */
+ git_vector_set(NULL, &idx->deltas, i, NULL);
+ git__free(delta);
+ }
+
+ /* if none were actually set, we're done */
+ if (!non_null)
+ break;
+
+ if (!progressed && (fix_thin_pack(idx, stats) < 0)) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int update_header_and_rehash(git_indexer *idx, git_indexer_progress *stats)
+{
+ void *ptr;
+ size_t chunk = 1024*1024;
+ off64_t hashed = 0;
+ git_mwindow *w = NULL;
+ git_mwindow_file *mwf;
+ unsigned int left;
+
+ mwf = &idx->pack->mwf;
+
+ git_hash_init(&idx->trailer);
+
+
+ /* Update the header to include the number of local objects we injected */
+ idx->hdr.hdr_entries = htonl(stats->total_objects + stats->local_objects);
+ if (write_at(idx, &idx->hdr, 0, sizeof(struct git_pack_header)) < 0)
+ return -1;
+
+ /*
+ * We now use the same technique as before to determine the
+ * hash. We keep reading up to the end and let
+ * hash_partially() keep the existing trailer out of the
+ * calculation.
+ */
+ if (git_mwindow_free_all(mwf) < 0)
+ return -1;
+
+ idx->inbuf_len = 0;
+ while (hashed < mwf->size) {
+ ptr = git_mwindow_open(mwf, &w, hashed, chunk, &left);
+ if (ptr == NULL)
+ return -1;
+
+ hash_partially(idx, ptr, left);
+ hashed += left;
+
+ git_mwindow_close(&w);
+ }
+
+ return 0;
+}
+
+int git_indexer_commit(git_indexer *idx, git_indexer_progress *stats)
+{
+ git_mwindow *w = NULL;
+ unsigned int i, long_offsets = 0, left;
+ int error;
+ struct git_pack_idx_header hdr;
+ git_str filename = GIT_STR_INIT;
+ struct entry *entry;
+ unsigned char checksum[GIT_HASH_MAX_SIZE];
+ git_filebuf index_file = {0};
+ void *packfile_trailer;
+ size_t checksum_size;
+ int filebuf_hash;
+ bool mismatch;
+
+ if (!idx->parsed_header) {
+ git_error_set(GIT_ERROR_INDEXER, "incomplete pack header");
+ return -1;
+ }
+
+ checksum_size = git_hash_size(indexer_hash_algorithm(idx));
+ filebuf_hash = git_filebuf_hash_flags(indexer_hash_algorithm(idx));
+ GIT_ASSERT(checksum_size);
+
+ /* Test for this before resolve_deltas(), as it plays with idx->off */
+ if (idx->off + (ssize_t)checksum_size < idx->pack->mwf.size) {
+ git_error_set(GIT_ERROR_INDEXER, "unexpected data at the end of the pack");
+ return -1;
+ }
+ if (idx->off + (ssize_t)checksum_size > idx->pack->mwf.size) {
+ git_error_set(GIT_ERROR_INDEXER, "missing trailer at the end of the pack");
+ return -1;
+ }
+
+ packfile_trailer = git_mwindow_open(&idx->pack->mwf, &w, idx->pack->mwf.size - checksum_size, checksum_size, &left);
+ if (packfile_trailer == NULL) {
+ git_mwindow_close(&w);
+ goto on_error;
+ }
+
+ /* Compare the packfile trailer as it was sent to us and what we calculated */
+ git_hash_final(checksum, &idx->trailer);
+ mismatch = !!memcmp(checksum, packfile_trailer, checksum_size);
+ git_mwindow_close(&w);
+
+ if (mismatch) {
+ git_error_set(GIT_ERROR_INDEXER, "packfile trailer mismatch");
+ return -1;
+ }
+
+ /* Freeze the number of deltas */
+ stats->total_deltas = stats->total_objects - stats->indexed_objects;
+
+ if ((error = resolve_deltas(idx, stats)) < 0)
+ return error;
+
+ if (stats->indexed_objects != stats->total_objects) {
+ git_error_set(GIT_ERROR_INDEXER, "early EOF");
+ return -1;
+ }
+
+ if (stats->local_objects > 0) {
+ if (update_header_and_rehash(idx, stats) < 0)
+ return -1;
+
+ git_hash_final(checksum, &idx->trailer);
+ write_at(idx, checksum, idx->pack->mwf.size - checksum_size, checksum_size);
+ }
+
+ /*
+ * Is the resulting graph fully connected or are we still
+ * missing some objects? In the second case, we can
+ * bail out due to an incomplete and thus corrupt
+ * packfile.
+ */
+ if (git_oidmap_size(idx->expected_oids) > 0) {
+ git_error_set(GIT_ERROR_INDEXER, "packfile is missing %"PRIuZ" objects",
+ git_oidmap_size(idx->expected_oids));
+ return -1;
+ }
+
+ git_vector_sort(&idx->objects);
+
+ /* Use the trailer hash as the pack file name to ensure
+ * files with different contents have different names */
+ memcpy(idx->checksum, checksum, checksum_size);
+ if (git_hash_fmt(idx->name, checksum, checksum_size) < 0)
+ return -1;
+
+ git_str_sets(&filename, idx->pack->pack_name);
+ git_str_shorten(&filename, strlen("pack"));
+ git_str_puts(&filename, "idx");
+ if (git_str_oom(&filename))
+ return -1;
+
+ if (git_filebuf_open(&index_file, filename.ptr,
+ filebuf_hash | (idx->do_fsync ? GIT_FILEBUF_FSYNC : 0),
+ idx->mode) < 0)
+ goto on_error;
+
+ /* Write out the header */
+ hdr.idx_signature = htonl(PACK_IDX_SIGNATURE);
+ hdr.idx_version = htonl(2);
+ git_filebuf_write(&index_file, &hdr, sizeof(hdr));
+
+ /* Write out the fanout table */
+ for (i = 0; i < 256; ++i) {
+ uint32_t n = htonl(idx->fanout[i]);
+ git_filebuf_write(&index_file, &n, sizeof(n));
+ }
+
+ /* Write out the object names (SHA-1 hashes) */
+ git_vector_foreach(&idx->objects, i, entry) {
+ git_filebuf_write(&index_file, &entry->oid.id, git_oid_size(idx->oid_type));
+ }
+
+ /* Write out the CRC32 values */
+ git_vector_foreach(&idx->objects, i, entry) {
+ git_filebuf_write(&index_file, &entry->crc, sizeof(uint32_t));
+ }
+
+ /* Write out the offsets */
+ git_vector_foreach(&idx->objects, i, entry) {
+ uint32_t n;
+
+ if (entry->offset == UINT32_MAX)
+ n = htonl(0x80000000 | long_offsets++);
+ else
+ n = htonl(entry->offset);
+
+ git_filebuf_write(&index_file, &n, sizeof(uint32_t));
+ }
+
+ /* Write out the long offsets */
+ git_vector_foreach(&idx->objects, i, entry) {
+ uint32_t split[2];
+
+ if (entry->offset != UINT32_MAX)
+ continue;
+
+ split[0] = htonl(entry->offset_long >> 32);
+ split[1] = htonl(entry->offset_long & 0xffffffff);
+
+ git_filebuf_write(&index_file, &split, sizeof(uint32_t) * 2);
+ }
+
+ /* Write out the packfile trailer to the index */
+ if (git_filebuf_write(&index_file, checksum, checksum_size) < 0)
+ goto on_error;
+
+ /* Write out the hash of the idx */
+ if (git_filebuf_hash(checksum, &index_file) < 0)
+ goto on_error;
+
+ git_filebuf_write(&index_file, checksum, checksum_size);
+
+ /* Figure out what the final name should be */
+ if (index_path(&filename, idx, ".idx") < 0)
+ goto on_error;
+
+ /* Commit file */
+ if (git_filebuf_commit_at(&index_file, filename.ptr) < 0)
+ goto on_error;
+
+ if (git_mwindow_free_all(&idx->pack->mwf) < 0)
+ goto on_error;
+
+#if !defined(NO_MMAP) && defined(GIT_WIN32)
+ /*
+ * Some non-Windows remote filesystems fail when truncating files if the
+ * file permissions change after opening the file (done by p_mkstemp).
+ *
+ * Truncation is only needed when mmap is used to undo rounding up to next
+ * page_size in append_to_pack.
+ */
+ if (p_ftruncate(idx->pack->mwf.fd, idx->pack->mwf.size) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to truncate pack file '%s'", idx->pack->pack_name);
+ return -1;
+ }
+#endif
+
+ if (idx->do_fsync && p_fsync(idx->pack->mwf.fd) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to fsync packfile");
+ goto on_error;
+ }
+
+ /* We need to close the descriptor here so Windows doesn't choke on commit_at */
+ if (p_close(idx->pack->mwf.fd) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to close packfile");
+ goto on_error;
+ }
+
+ idx->pack->mwf.fd = -1;
+
+ if (index_path(&filename, idx, ".pack") < 0)
+ goto on_error;
+
+ /* And don't forget to rename the packfile to its new place. */
+ if (p_rename(idx->pack->pack_name, git_str_cstr(&filename)) < 0)
+ goto on_error;
+
+ /* And fsync the parent directory if we're asked to. */
+ if (idx->do_fsync &&
+ git_futils_fsync_parent(git_str_cstr(&filename)) < 0)
+ goto on_error;
+
+ idx->pack_committed = 1;
+
+ git_str_dispose(&filename);
+ return 0;
+
+on_error:
+ git_mwindow_free_all(&idx->pack->mwf);
+ git_filebuf_cleanup(&index_file);
+ git_str_dispose(&filename);
+ return -1;
+}
+
+void git_indexer_free(git_indexer *idx)
+{
+ const git_oid *key;
+ git_oid *value;
+ size_t iter;
+
+ if (idx == NULL)
+ return;
+
+ if (idx->have_stream)
+ git_packfile_stream_dispose(&idx->stream);
+
+ git_vector_free_deep(&idx->objects);
+
+ if (idx->pack->idx_cache) {
+ struct git_pack_entry *pentry;
+ git_oidmap_foreach_value(idx->pack->idx_cache, pentry, {
+ git__free(pentry);
+ });
+
+ git_oidmap_free(idx->pack->idx_cache);
+ }
+
+ git_vector_free_deep(&idx->deltas);
+
+ git_packfile_free(idx->pack, !idx->pack_committed);
+
+ iter = 0;
+ while (git_oidmap_iterate((void **) &value, idx->expected_oids, &iter, &key) == 0)
+ git__free(value);
+
+ git_hash_ctx_cleanup(&idx->trailer);
+ git_hash_ctx_cleanup(&idx->hash_ctx);
+ git_str_dispose(&idx->entry_data);
+ git_oidmap_free(idx->expected_oids);
+ git__free(idx);
+}
diff --git a/src/libgit2/indexer.h b/src/libgit2/indexer.h
new file mode 100644
index 0000000..8ee6115
--- /dev/null
+++ b/src/libgit2/indexer.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_indexer_h__
+#define INCLUDE_indexer_h__
+
+#include "common.h"
+
+#include "git2/indexer.h"
+
+extern void git_indexer__set_fsync(git_indexer *idx, int do_fsync);
+
+#endif
diff --git a/src/libgit2/iterator.c b/src/libgit2/iterator.c
new file mode 100644
index 0000000..95ded10
--- /dev/null
+++ b/src/libgit2/iterator.c
@@ -0,0 +1,2456 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "iterator.h"
+
+#include "tree.h"
+#include "index.h"
+#include "path.h"
+
+#define GIT_ITERATOR_FIRST_ACCESS (1 << 15)
+#define GIT_ITERATOR_HONOR_IGNORES (1 << 16)
+#define GIT_ITERATOR_IGNORE_DOT_GIT (1 << 17)
+
+#define iterator__flag(I,F) ((((git_iterator *)(I))->flags & GIT_ITERATOR_ ## F) != 0)
+#define iterator__ignore_case(I) iterator__flag(I,IGNORE_CASE)
+#define iterator__include_trees(I) iterator__flag(I,INCLUDE_TREES)
+#define iterator__dont_autoexpand(I) iterator__flag(I,DONT_AUTOEXPAND)
+#define iterator__do_autoexpand(I) !iterator__flag(I,DONT_AUTOEXPAND)
+#define iterator__include_conflicts(I) iterator__flag(I,INCLUDE_CONFLICTS)
+#define iterator__has_been_accessed(I) iterator__flag(I,FIRST_ACCESS)
+#define iterator__honor_ignores(I) iterator__flag(I,HONOR_IGNORES)
+#define iterator__ignore_dot_git(I) iterator__flag(I,IGNORE_DOT_GIT)
+#define iterator__descend_symlinks(I) iterator__flag(I,DESCEND_SYMLINKS)
+
+
+static void iterator_set_ignore_case(git_iterator *iter, bool ignore_case)
+{
+ if (ignore_case)
+ iter->flags |= GIT_ITERATOR_IGNORE_CASE;
+ else
+ iter->flags &= ~GIT_ITERATOR_IGNORE_CASE;
+
+ iter->strcomp = ignore_case ? git__strcasecmp : git__strcmp;
+ iter->strncomp = ignore_case ? git__strncasecmp : git__strncmp;
+ iter->prefixcomp = ignore_case ? git__prefixcmp_icase : git__prefixcmp;
+ iter->entry_srch = ignore_case ? git_index_entry_isrch : git_index_entry_srch;
+
+ git_vector_set_cmp(&iter->pathlist, (git_vector_cmp)iter->strcomp);
+}
+
+static int iterator_range_init(
+ git_iterator *iter, const char *start, const char *end)
+{
+ if (start && *start) {
+ iter->start = git__strdup(start);
+ GIT_ERROR_CHECK_ALLOC(iter->start);
+
+ iter->start_len = strlen(iter->start);
+ }
+
+ if (end && *end) {
+ iter->end = git__strdup(end);
+ GIT_ERROR_CHECK_ALLOC(iter->end);
+
+ iter->end_len = strlen(iter->end);
+ }
+
+ iter->started = (iter->start == NULL);
+ iter->ended = false;
+
+ return 0;
+}
+
+static void iterator_range_free(git_iterator *iter)
+{
+ if (iter->start) {
+ git__free(iter->start);
+ iter->start = NULL;
+ iter->start_len = 0;
+ }
+
+ if (iter->end) {
+ git__free(iter->end);
+ iter->end = NULL;
+ iter->end_len = 0;
+ }
+}
+
+static int iterator_reset_range(
+ git_iterator *iter, const char *start, const char *end)
+{
+ iterator_range_free(iter);
+ return iterator_range_init(iter, start, end);
+}
+
+static int iterator_pathlist_init(git_iterator *iter, git_strarray *pathlist)
+{
+ size_t i;
+
+ if (git_vector_init(&iter->pathlist, pathlist->count, NULL) < 0)
+ return -1;
+
+ for (i = 0; i < pathlist->count; i++) {
+ if (!pathlist->strings[i])
+ continue;
+
+ if (git_vector_insert(&iter->pathlist, pathlist->strings[i]) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+static int iterator_init_common(
+ git_iterator *iter,
+ git_repository *repo,
+ git_index *index,
+ git_iterator_options *given_opts)
+{
+ static git_iterator_options default_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_iterator_options *options = given_opts ? given_opts : &default_opts;
+ bool ignore_case;
+ int precompose;
+ int error;
+
+ iter->repo = repo;
+ iter->index = index;
+ iter->flags = options->flags;
+
+ if ((iter->flags & GIT_ITERATOR_IGNORE_CASE) != 0) {
+ ignore_case = true;
+ } else if ((iter->flags & GIT_ITERATOR_DONT_IGNORE_CASE) != 0) {
+ ignore_case = false;
+ } else if (repo) {
+ git_index *index;
+
+ if ((error = git_repository_index__weakptr(&index, iter->repo)) < 0)
+ return error;
+
+ ignore_case = !!index->ignore_case;
+
+ if (ignore_case == 1)
+ iter->flags |= GIT_ITERATOR_IGNORE_CASE;
+ else
+ iter->flags |= GIT_ITERATOR_DONT_IGNORE_CASE;
+ } else {
+ ignore_case = false;
+ }
+
+ /* try to look up precompose and set flag if appropriate */
+ if (repo &&
+ (iter->flags & GIT_ITERATOR_PRECOMPOSE_UNICODE) == 0 &&
+ (iter->flags & GIT_ITERATOR_DONT_PRECOMPOSE_UNICODE) == 0) {
+
+ if (git_repository__configmap_lookup(&precompose, repo, GIT_CONFIGMAP_PRECOMPOSE) < 0)
+ git_error_clear();
+ else if (precompose)
+ iter->flags |= GIT_ITERATOR_PRECOMPOSE_UNICODE;
+ }
+
+ if ((iter->flags & GIT_ITERATOR_DONT_AUTOEXPAND))
+ iter->flags |= GIT_ITERATOR_INCLUDE_TREES;
+
+ if ((error = iterator_range_init(iter, options->start, options->end)) < 0 ||
+ (error = iterator_pathlist_init(iter, &options->pathlist)) < 0)
+ return error;
+
+ iterator_set_ignore_case(iter, ignore_case);
+ return 0;
+}
+
+static void iterator_clear(git_iterator *iter)
+{
+ iter->started = false;
+ iter->ended = false;
+ iter->stat_calls = 0;
+ iter->pathlist_walk_idx = 0;
+ iter->flags &= ~GIT_ITERATOR_FIRST_ACCESS;
+}
+
+GIT_INLINE(bool) iterator_has_started(
+ git_iterator *iter, const char *path, bool is_submodule)
+{
+ size_t path_len;
+
+ if (iter->start == NULL || iter->started == true)
+ return true;
+
+ /* the starting path is generally a prefix - we have started once we
+ * are prefixed by this path
+ */
+ iter->started = (iter->prefixcomp(path, iter->start) >= 0);
+
+ if (iter->started)
+ return true;
+
+ path_len = strlen(path);
+
+ /* if, however, we are a submodule, then we support `start` being
+ * suffixed with a `/` for crazy legacy reasons. match `submod`
+ * with a start path of `submod/`.
+ */
+ if (is_submodule && iter->start_len && path_len == iter->start_len - 1 &&
+ iter->start[iter->start_len-1] == '/')
+ return true;
+
+ /* if, however, our current path is a directory, and our starting path
+ * is _beneath_ that directory, then recurse into the directory (even
+ * though we have not yet "started")
+ */
+ if (path_len > 0 && path[path_len-1] == '/' &&
+ iter->strncomp(path, iter->start, path_len) == 0)
+ return true;
+
+ return false;
+}
+
+GIT_INLINE(bool) iterator_has_ended(git_iterator *iter, const char *path)
+{
+ if (iter->end == NULL)
+ return false;
+ else if (iter->ended)
+ return true;
+
+ iter->ended = (iter->prefixcomp(path, iter->end) > 0);
+ return iter->ended;
+}
+
+/* walker for the index and tree iterator that allows it to walk the sorted
+ * pathlist entries alongside sorted iterator entries.
+ */
+static bool iterator_pathlist_next_is(git_iterator *iter, const char *path)
+{
+ char *p;
+ size_t path_len, p_len, cmp_len, i;
+ int cmp;
+
+ if (iter->pathlist.length == 0)
+ return true;
+
+ git_vector_sort(&iter->pathlist);
+
+ path_len = strlen(path);
+
+ /* for comparison, drop the trailing slash on the current '/' */
+ if (path_len && path[path_len-1] == '/')
+ path_len--;
+
+ for (i = iter->pathlist_walk_idx; i < iter->pathlist.length; i++) {
+ p = iter->pathlist.contents[i];
+ p_len = strlen(p);
+
+ if (p_len && p[p_len-1] == '/')
+ p_len--;
+
+ cmp_len = min(path_len, p_len);
+
+ /* see if the pathlist entry is a prefix of this path */
+ cmp = iter->strncomp(p, path, cmp_len);
+
+ /* prefix match - see if there's an exact match, or if we were
+ * given a path that matches the directory
+ */
+ if (cmp == 0) {
+ /* if this pathlist entry is not suffixed with a '/' then
+ * it matches a path that is a file or a directory.
+ * (eg, pathlist = "foo" and path is "foo" or "foo/" or
+ * "foo/something")
+ */
+ if (p[cmp_len] == '\0' &&
+ (path[cmp_len] == '\0' || path[cmp_len] == '/'))
+ return true;
+
+ /* if this pathlist entry _is_ suffixed with a '/' then
+ * it matches only paths that are directories.
+ * (eg, pathlist = "foo/" and path is "foo/" or "foo/something")
+ */
+ if (p[cmp_len] == '/' && path[cmp_len] == '/')
+ return true;
+ }
+
+ /* this pathlist entry sorts before the given path, try the next */
+ else if (cmp < 0) {
+ iter->pathlist_walk_idx++;
+ continue;
+ }
+
+ /* this pathlist sorts after the given path, no match. */
+ else if (cmp > 0) {
+ break;
+ }
+ }
+
+ return false;
+}
+
+typedef enum {
+ ITERATOR_PATHLIST_NONE = 0,
+ ITERATOR_PATHLIST_IS_FILE = 1,
+ ITERATOR_PATHLIST_IS_DIR = 2,
+ ITERATOR_PATHLIST_IS_PARENT = 3,
+ ITERATOR_PATHLIST_FULL = 4
+} iterator_pathlist_search_t;
+
+static iterator_pathlist_search_t iterator_pathlist_search(
+ git_iterator *iter, const char *path, size_t path_len)
+{
+ const char *p;
+ size_t idx;
+ int error;
+
+ if (iter->pathlist.length == 0)
+ return ITERATOR_PATHLIST_FULL;
+
+ git_vector_sort(&iter->pathlist);
+
+ error = git_vector_bsearch2(&idx, &iter->pathlist,
+ (git_vector_cmp)iter->strcomp, path);
+
+ /* the given path was found in the pathlist. since the pathlist only
+ * matches directories when they're suffixed with a '/', analyze the
+ * path string to determine whether it's a directory or not.
+ */
+ if (error == 0) {
+ if (path_len && path[path_len-1] == '/')
+ return ITERATOR_PATHLIST_IS_DIR;
+
+ return ITERATOR_PATHLIST_IS_FILE;
+ }
+
+ /* at this point, the path we're examining may be a directory (though we
+ * don't know that yet, since we're avoiding a stat unless it's necessary)
+ * so walk the pathlist looking for the given path with a '/' after it,
+ */
+ while ((p = git_vector_get(&iter->pathlist, idx)) != NULL) {
+ if (iter->prefixcomp(p, path) != 0)
+ break;
+
+ /* an exact match would have been matched by the bsearch above */
+ GIT_ASSERT_WITH_RETVAL(p[path_len], ITERATOR_PATHLIST_NONE);
+
+ /* is this a literal directory entry (eg `foo/`) or a file beneath */
+ if (p[path_len] == '/') {
+ return (p[path_len+1] == '\0') ?
+ ITERATOR_PATHLIST_IS_DIR :
+ ITERATOR_PATHLIST_IS_PARENT;
+ }
+
+ if (p[path_len] > '/')
+ break;
+
+ idx++;
+ }
+
+ return ITERATOR_PATHLIST_NONE;
+}
+
+/* Empty iterator */
+
+static int empty_iterator_noop(const git_index_entry **e, git_iterator *i)
+{
+ GIT_UNUSED(i);
+
+ if (e)
+ *e = NULL;
+
+ return GIT_ITEROVER;
+}
+
+static int empty_iterator_advance_over(
+ const git_index_entry **e,
+ git_iterator_status_t *s,
+ git_iterator *i)
+{
+ *s = GIT_ITERATOR_STATUS_EMPTY;
+ return empty_iterator_noop(e, i);
+}
+
+static int empty_iterator_reset(git_iterator *i)
+{
+ GIT_UNUSED(i);
+ return 0;
+}
+
+static void empty_iterator_free(git_iterator *i)
+{
+ GIT_UNUSED(i);
+}
+
+typedef struct {
+ git_iterator base;
+ git_iterator_callbacks cb;
+} empty_iterator;
+
+int git_iterator_for_nothing(
+ git_iterator **out,
+ git_iterator_options *options)
+{
+ empty_iterator *iter;
+
+ static git_iterator_callbacks callbacks = {
+ empty_iterator_noop,
+ empty_iterator_noop,
+ empty_iterator_noop,
+ empty_iterator_advance_over,
+ empty_iterator_reset,
+ empty_iterator_free
+ };
+
+ *out = NULL;
+
+ iter = git__calloc(1, sizeof(empty_iterator));
+ GIT_ERROR_CHECK_ALLOC(iter);
+
+ iter->base.type = GIT_ITERATOR_EMPTY;
+ iter->base.cb = &callbacks;
+ iter->base.flags = options->flags;
+
+ *out = &iter->base;
+ return 0;
+}
+
+/* Tree iterator */
+
+typedef struct {
+ git_tree_entry *tree_entry;
+ const char *parent_path;
+} tree_iterator_entry;
+
+typedef struct {
+ git_tree *tree;
+
+ /* path to this particular frame (folder) */
+ git_str path;
+
+ /* a sorted list of the entries for this frame (folder), these are
+ * actually pointers to the iterator's entry pool.
+ */
+ git_vector entries;
+ tree_iterator_entry *current;
+
+ size_t next_idx;
+
+ /* on case insensitive iterations, we also have an array of other
+ * paths that were case insensitively equal to this one, and their
+ * tree objects. we have coalesced the tree entries into this frame.
+ * a child `tree_iterator_entry` will contain a pointer to its actual
+ * parent path.
+ */
+ git_vector similar_trees;
+ git_array_t(git_str) similar_paths;
+} tree_iterator_frame;
+
+typedef struct {
+ git_iterator base;
+ git_tree *root;
+ git_array_t(tree_iterator_frame) frames;
+
+ git_index_entry entry;
+ git_str entry_path;
+
+ /* a pool of entries to reduce the number of allocations */
+ git_pool entry_pool;
+} tree_iterator;
+
+GIT_INLINE(tree_iterator_frame *) tree_iterator_parent_frame(
+ tree_iterator *iter)
+{
+ return iter->frames.size > 1 ?
+ &iter->frames.ptr[iter->frames.size-2] : NULL;
+}
+
+GIT_INLINE(tree_iterator_frame *) tree_iterator_current_frame(
+ tree_iterator *iter)
+{
+ return iter->frames.size ? &iter->frames.ptr[iter->frames.size-1] : NULL;
+}
+
+GIT_INLINE(int) tree_entry_cmp(
+ const git_tree_entry *a, const git_tree_entry *b, bool icase)
+{
+ return git_fs_path_cmp(
+ a->filename, a->filename_len, a->attr == GIT_FILEMODE_TREE,
+ b->filename, b->filename_len, b->attr == GIT_FILEMODE_TREE,
+ icase ? git__strncasecmp : git__strncmp);
+}
+
+GIT_INLINE(int) tree_iterator_entry_cmp_icase(
+ const void *ptr_a, const void *ptr_b)
+{
+ const tree_iterator_entry *a = (const tree_iterator_entry *)ptr_a;
+ const tree_iterator_entry *b = (const tree_iterator_entry *)ptr_b;
+
+ return tree_entry_cmp(a->tree_entry, b->tree_entry, true);
+}
+
+static int tree_iterator_entry_sort_icase(const void *ptr_a, const void *ptr_b)
+{
+ const tree_iterator_entry *a = (const tree_iterator_entry *)ptr_a;
+ const tree_iterator_entry *b = (const tree_iterator_entry *)ptr_b;
+
+ int c = tree_entry_cmp(a->tree_entry, b->tree_entry, true);
+
+ /* stabilize the sort order for filenames that are (case insensitively)
+ * the same by examining the parent path (case sensitively) before
+ * falling back to a case sensitive sort of the filename.
+ */
+ if (!c && a->parent_path != b->parent_path)
+ c = git__strcmp(a->parent_path, b->parent_path);
+
+ if (!c)
+ c = tree_entry_cmp(a->tree_entry, b->tree_entry, false);
+
+ return c;
+}
+
+static int tree_iterator_compute_path(
+ git_str *out,
+ tree_iterator_entry *entry)
+{
+ git_str_clear(out);
+
+ if (entry->parent_path)
+ git_str_joinpath(out, entry->parent_path, entry->tree_entry->filename);
+ else
+ git_str_puts(out, entry->tree_entry->filename);
+
+ if (git_tree_entry__is_tree(entry->tree_entry))
+ git_str_putc(out, '/');
+
+ if (git_str_oom(out))
+ return -1;
+
+ return 0;
+}
+
+static int tree_iterator_frame_init(
+ tree_iterator *iter,
+ git_tree *tree,
+ tree_iterator_entry *frame_entry)
+{
+ tree_iterator_frame *new_frame = NULL;
+ tree_iterator_entry *new_entry;
+ git_tree *dup = NULL;
+ git_tree_entry *tree_entry;
+ git_vector_cmp cmp;
+ size_t i;
+ int error = 0;
+
+ new_frame = git_array_alloc(iter->frames);
+ GIT_ERROR_CHECK_ALLOC(new_frame);
+
+ if ((error = git_tree_dup(&dup, tree)) < 0)
+ goto done;
+
+ memset(new_frame, 0x0, sizeof(tree_iterator_frame));
+ new_frame->tree = dup;
+
+ if (frame_entry &&
+ (error = tree_iterator_compute_path(&new_frame->path, frame_entry)) < 0)
+ goto done;
+
+ cmp = iterator__ignore_case(&iter->base) ?
+ tree_iterator_entry_sort_icase : NULL;
+
+ if ((error = git_vector_init(&new_frame->entries,
+ dup->entries.size, cmp)) < 0)
+ goto done;
+
+ git_array_foreach(dup->entries, i, tree_entry) {
+ if ((new_entry = git_pool_malloc(&iter->entry_pool, 1)) == NULL) {
+ git_error_set_oom();
+ error = -1;
+ goto done;
+ }
+
+ new_entry->tree_entry = tree_entry;
+ new_entry->parent_path = new_frame->path.ptr;
+
+ if ((error = git_vector_insert(&new_frame->entries, new_entry)) < 0)
+ goto done;
+ }
+
+ git_vector_set_sorted(&new_frame->entries,
+ !iterator__ignore_case(&iter->base));
+
+done:
+ if (error < 0) {
+ git_tree_free(dup);
+ git_array_pop(iter->frames);
+ }
+
+ return error;
+}
+
+GIT_INLINE(tree_iterator_entry *) tree_iterator_current_entry(
+ tree_iterator_frame *frame)
+{
+ return frame->current;
+}
+
+GIT_INLINE(int) tree_iterator_frame_push_neighbors(
+ tree_iterator *iter,
+ tree_iterator_frame *parent_frame,
+ tree_iterator_frame *frame,
+ const char *filename)
+{
+ tree_iterator_entry *entry, *new_entry;
+ git_tree *tree = NULL;
+ git_tree_entry *tree_entry;
+ git_str *path;
+ size_t new_size, i;
+ int error = 0;
+
+ while (parent_frame->next_idx < parent_frame->entries.length) {
+ entry = parent_frame->entries.contents[parent_frame->next_idx];
+
+ if (strcasecmp(filename, entry->tree_entry->filename) != 0)
+ break;
+
+ if ((error = git_tree_lookup(&tree,
+ iter->base.repo, &entry->tree_entry->oid)) < 0)
+ break;
+
+ if (git_vector_insert(&parent_frame->similar_trees, tree) < 0)
+ break;
+
+ path = git_array_alloc(parent_frame->similar_paths);
+ GIT_ERROR_CHECK_ALLOC(path);
+
+ memset(path, 0, sizeof(git_str));
+
+ if ((error = tree_iterator_compute_path(path, entry)) < 0)
+ break;
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&new_size,
+ frame->entries.length, tree->entries.size);
+ git_vector_size_hint(&frame->entries, new_size);
+
+ git_array_foreach(tree->entries, i, tree_entry) {
+ new_entry = git_pool_malloc(&iter->entry_pool, 1);
+ GIT_ERROR_CHECK_ALLOC(new_entry);
+
+ new_entry->tree_entry = tree_entry;
+ new_entry->parent_path = path->ptr;
+
+ if ((error = git_vector_insert(&frame->entries, new_entry)) < 0)
+ break;
+ }
+
+ if (error)
+ break;
+
+ parent_frame->next_idx++;
+ }
+
+ return error;
+}
+
+GIT_INLINE(int) tree_iterator_frame_push(
+ tree_iterator *iter, tree_iterator_entry *entry)
+{
+ tree_iterator_frame *parent_frame, *frame;
+ git_tree *tree = NULL;
+ int error;
+
+ if ((error = git_tree_lookup(&tree,
+ iter->base.repo, &entry->tree_entry->oid)) < 0 ||
+ (error = tree_iterator_frame_init(iter, tree, entry)) < 0)
+ goto done;
+
+ parent_frame = tree_iterator_parent_frame(iter);
+ frame = tree_iterator_current_frame(iter);
+
+ /* if we're case insensitive, then we may have another directory that
+ * is (case insensitively) equal to this one. coalesce those children
+ * into this tree.
+ */
+ if (iterator__ignore_case(&iter->base))
+ error = tree_iterator_frame_push_neighbors(iter,
+ parent_frame, frame, entry->tree_entry->filename);
+
+done:
+ git_tree_free(tree);
+ return error;
+}
+
+static int tree_iterator_frame_pop(tree_iterator *iter)
+{
+ tree_iterator_frame *frame;
+ git_str *buf = NULL;
+ git_tree *tree;
+ size_t i;
+
+ GIT_ASSERT(iter->frames.size);
+
+ frame = git_array_pop(iter->frames);
+
+ git_vector_free(&frame->entries);
+ git_tree_free(frame->tree);
+
+ do {
+ buf = git_array_pop(frame->similar_paths);
+ git_str_dispose(buf);
+ } while (buf != NULL);
+
+ git_array_clear(frame->similar_paths);
+
+ git_vector_foreach(&frame->similar_trees, i, tree)
+ git_tree_free(tree);
+
+ git_vector_free(&frame->similar_trees);
+
+ git_str_dispose(&frame->path);
+
+ return 0;
+}
+
+static int tree_iterator_current(
+ const git_index_entry **out, git_iterator *i)
+{
+ tree_iterator *iter = (tree_iterator *)i;
+
+ if (!iterator__has_been_accessed(i))
+ return iter->base.cb->advance(out, i);
+
+ if (!iter->frames.size) {
+ *out = NULL;
+ return GIT_ITEROVER;
+ }
+
+ *out = &iter->entry;
+ return 0;
+}
+
+static void tree_iterator_set_current(
+ tree_iterator *iter,
+ tree_iterator_frame *frame,
+ tree_iterator_entry *entry)
+{
+ git_tree_entry *tree_entry = entry->tree_entry;
+
+ frame->current = entry;
+
+ memset(&iter->entry, 0x0, sizeof(git_index_entry));
+
+ iter->entry.mode = tree_entry->attr;
+ iter->entry.path = iter->entry_path.ptr;
+ git_oid_cpy(&iter->entry.id, &tree_entry->oid);
+}
+
+static int tree_iterator_advance(const git_index_entry **out, git_iterator *i)
+{
+ tree_iterator *iter = (tree_iterator *)i;
+ int error = 0;
+
+ iter->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
+
+ /* examine tree entries until we find the next one to return */
+ while (true) {
+ tree_iterator_entry *prev_entry, *entry;
+ tree_iterator_frame *frame;
+ bool is_tree;
+
+ if ((frame = tree_iterator_current_frame(iter)) == NULL) {
+ error = GIT_ITEROVER;
+ break;
+ }
+
+ /* no more entries in this frame. pop the frame out */
+ if (frame->next_idx == frame->entries.length) {
+ if ((error = tree_iterator_frame_pop(iter)) < 0)
+ break;
+
+ continue;
+ }
+
+ /* we may have coalesced the contents of case-insensitively same-named
+ * directories, so do the sort now.
+ */
+ if (frame->next_idx == 0 && !git_vector_is_sorted(&frame->entries))
+ git_vector_sort(&frame->entries);
+
+ /* we have more entries in the current frame, that's our next entry */
+ prev_entry = tree_iterator_current_entry(frame);
+ entry = frame->entries.contents[frame->next_idx];
+ frame->next_idx++;
+
+ /* we can have collisions when iterating case insensitively. (eg,
+ * 'A/a' and 'a/A'). squash this one if it's already been seen.
+ */
+ if (iterator__ignore_case(&iter->base) &&
+ prev_entry &&
+ tree_iterator_entry_cmp_icase(prev_entry, entry) == 0)
+ continue;
+
+ if ((error = tree_iterator_compute_path(&iter->entry_path, entry)) < 0)
+ break;
+
+ /* if this path is before our start, advance over this entry */
+ if (!iterator_has_started(&iter->base, iter->entry_path.ptr, false))
+ continue;
+
+ /* if this path is after our end, stop */
+ if (iterator_has_ended(&iter->base, iter->entry_path.ptr)) {
+ error = GIT_ITEROVER;
+ break;
+ }
+
+ /* if we have a list of paths we're interested in, examine it */
+ if (!iterator_pathlist_next_is(&iter->base, iter->entry_path.ptr))
+ continue;
+
+ is_tree = git_tree_entry__is_tree(entry->tree_entry);
+
+ /* if we are *not* including trees then advance over this entry */
+ if (is_tree && !iterator__include_trees(iter)) {
+
+ /* if we've found a tree (and are not returning it to the caller)
+ * and we are autoexpanding, then we want to return the first
+ * child. push the new directory and advance.
+ */
+ if (iterator__do_autoexpand(iter)) {
+ if ((error = tree_iterator_frame_push(iter, entry)) < 0)
+ break;
+ }
+
+ continue;
+ }
+
+ tree_iterator_set_current(iter, frame, entry);
+
+ /* if we are autoexpanding, then push this as a new frame, so that
+ * the next call to `advance` will dive into this directory.
+ */
+ if (is_tree && iterator__do_autoexpand(iter))
+ error = tree_iterator_frame_push(iter, entry);
+
+ break;
+ }
+
+ if (out)
+ *out = (error == 0) ? &iter->entry : NULL;
+
+ return error;
+}
+
+static int tree_iterator_advance_into(
+ const git_index_entry **out, git_iterator *i)
+{
+ tree_iterator *iter = (tree_iterator *)i;
+ tree_iterator_frame *frame;
+ tree_iterator_entry *prev_entry;
+ int error;
+
+ if (out)
+ *out = NULL;
+
+ if ((frame = tree_iterator_current_frame(iter)) == NULL)
+ return GIT_ITEROVER;
+
+ /* get the last seen entry */
+ prev_entry = tree_iterator_current_entry(frame);
+
+ /* it's legal to call advance_into when auto-expand is on. in this case,
+ * we will have pushed a new (empty) frame on to the stack for this
+ * new directory. since it's empty, its current_entry should be null.
+ */
+ GIT_ASSERT(iterator__do_autoexpand(i) ^ (prev_entry != NULL));
+
+ if (prev_entry) {
+ if (!git_tree_entry__is_tree(prev_entry->tree_entry))
+ return 0;
+
+ if ((error = tree_iterator_frame_push(iter, prev_entry)) < 0)
+ return error;
+ }
+
+ /* we've advanced into the directory in question, let advance
+ * find the first entry
+ */
+ return tree_iterator_advance(out, i);
+}
+
+static int tree_iterator_advance_over(
+ const git_index_entry **out,
+ git_iterator_status_t *status,
+ git_iterator *i)
+{
+ *status = GIT_ITERATOR_STATUS_NORMAL;
+ return git_iterator_advance(out, i);
+}
+
+static void tree_iterator_clear(tree_iterator *iter)
+{
+ while (iter->frames.size)
+ tree_iterator_frame_pop(iter);
+
+ git_array_clear(iter->frames);
+
+ git_pool_clear(&iter->entry_pool);
+ git_str_clear(&iter->entry_path);
+
+ iterator_clear(&iter->base);
+}
+
+static int tree_iterator_init(tree_iterator *iter)
+{
+ int error;
+
+ if ((error = git_pool_init(&iter->entry_pool, sizeof(tree_iterator_entry))) < 0 ||
+ (error = tree_iterator_frame_init(iter, iter->root, NULL)) < 0)
+ return error;
+
+ iter->base.flags &= ~GIT_ITERATOR_FIRST_ACCESS;
+
+ return 0;
+}
+
+static int tree_iterator_reset(git_iterator *i)
+{
+ tree_iterator *iter = (tree_iterator *)i;
+
+ tree_iterator_clear(iter);
+ return tree_iterator_init(iter);
+}
+
+static void tree_iterator_free(git_iterator *i)
+{
+ tree_iterator *iter = (tree_iterator *)i;
+
+ tree_iterator_clear(iter);
+
+ git_tree_free(iter->root);
+ git_str_dispose(&iter->entry_path);
+}
+
+int git_iterator_for_tree(
+ git_iterator **out,
+ git_tree *tree,
+ git_iterator_options *options)
+{
+ tree_iterator *iter;
+ int error;
+
+ static git_iterator_callbacks callbacks = {
+ tree_iterator_current,
+ tree_iterator_advance,
+ tree_iterator_advance_into,
+ tree_iterator_advance_over,
+ tree_iterator_reset,
+ tree_iterator_free
+ };
+
+ *out = NULL;
+
+ if (tree == NULL)
+ return git_iterator_for_nothing(out, options);
+
+ iter = git__calloc(1, sizeof(tree_iterator));
+ GIT_ERROR_CHECK_ALLOC(iter);
+
+ iter->base.type = GIT_ITERATOR_TREE;
+ iter->base.cb = &callbacks;
+
+ if ((error = iterator_init_common(&iter->base,
+ git_tree_owner(tree), NULL, options)) < 0 ||
+ (error = git_tree_dup(&iter->root, tree)) < 0 ||
+ (error = tree_iterator_init(iter)) < 0)
+ goto on_error;
+
+ *out = &iter->base;
+ return 0;
+
+on_error:
+ git_iterator_free(&iter->base);
+ return error;
+}
+
+int git_iterator_current_tree_entry(
+ const git_tree_entry **tree_entry, git_iterator *i)
+{
+ tree_iterator *iter;
+ tree_iterator_frame *frame;
+ tree_iterator_entry *entry;
+
+ GIT_ASSERT(i->type == GIT_ITERATOR_TREE);
+
+ iter = (tree_iterator *)i;
+
+ frame = tree_iterator_current_frame(iter);
+ entry = tree_iterator_current_entry(frame);
+
+ *tree_entry = entry->tree_entry;
+ return 0;
+}
+
+int git_iterator_current_parent_tree(
+ const git_tree **parent_tree, git_iterator *i, size_t depth)
+{
+ tree_iterator *iter;
+ tree_iterator_frame *frame;
+
+ GIT_ASSERT(i->type == GIT_ITERATOR_TREE);
+
+ iter = (tree_iterator *)i;
+
+ GIT_ASSERT(depth < iter->frames.size);
+ frame = &iter->frames.ptr[iter->frames.size-depth-1];
+
+ *parent_tree = frame->tree;
+ return 0;
+}
+
+/* Filesystem iterator */
+
+typedef struct {
+ struct stat st;
+ size_t path_len;
+ iterator_pathlist_search_t match;
+ git_oid id;
+ char path[GIT_FLEX_ARRAY];
+} filesystem_iterator_entry;
+
+typedef struct {
+ git_vector entries;
+ git_pool entry_pool;
+ size_t next_idx;
+
+ size_t path_len;
+ int is_ignored;
+} filesystem_iterator_frame;
+
+typedef struct {
+ git_iterator base;
+ char *root;
+ size_t root_len;
+
+ unsigned int dirload_flags;
+
+ git_tree *tree;
+ git_index *index;
+ git_vector index_snapshot;
+
+ git_oid_t oid_type;
+
+ git_array_t(filesystem_iterator_frame) frames;
+ git_ignores ignores;
+
+ /* info about the current entry */
+ git_index_entry entry;
+ git_str current_path;
+ int current_is_ignored;
+
+ /* temporary buffer for advance_over */
+ git_str tmp_buf;
+} filesystem_iterator;
+
+
+GIT_INLINE(filesystem_iterator_frame *) filesystem_iterator_parent_frame(
+ filesystem_iterator *iter)
+{
+ return iter->frames.size > 1 ?
+ &iter->frames.ptr[iter->frames.size-2] : NULL;
+}
+
+GIT_INLINE(filesystem_iterator_frame *) filesystem_iterator_current_frame(
+ filesystem_iterator *iter)
+{
+ return iter->frames.size ? &iter->frames.ptr[iter->frames.size-1] : NULL;
+}
+
+GIT_INLINE(filesystem_iterator_entry *) filesystem_iterator_current_entry(
+ filesystem_iterator_frame *frame)
+{
+ return frame->next_idx == 0 ?
+ NULL : frame->entries.contents[frame->next_idx-1];
+}
+
+static int filesystem_iterator_entry_cmp(const void *_a, const void *_b)
+{
+ const filesystem_iterator_entry *a = (const filesystem_iterator_entry *)_a;
+ const filesystem_iterator_entry *b = (const filesystem_iterator_entry *)_b;
+
+ return git__strcmp(a->path, b->path);
+}
+
+static int filesystem_iterator_entry_cmp_icase(const void *_a, const void *_b)
+{
+ const filesystem_iterator_entry *a = (const filesystem_iterator_entry *)_a;
+ const filesystem_iterator_entry *b = (const filesystem_iterator_entry *)_b;
+
+ return git__strcasecmp(a->path, b->path);
+}
+
+#define FILESYSTEM_MAX_DEPTH 100
+
+/**
+ * Figure out if an entry is a submodule.
+ *
+ * We consider it a submodule if the path is listed as a submodule in
+ * either the tree or the index.
+ */
+static int filesystem_iterator_is_submodule(
+ bool *out, filesystem_iterator *iter, const char *path, size_t path_len)
+{
+ bool is_submodule = false;
+ int error;
+
+ *out = false;
+
+ /* first see if this path is a submodule in HEAD */
+ if (iter->tree) {
+ git_tree_entry *entry;
+
+ error = git_tree_entry_bypath(&entry, iter->tree, path);
+
+ if (error < 0 && error != GIT_ENOTFOUND)
+ return error;
+
+ if (!error) {
+ is_submodule = (entry->attr == GIT_FILEMODE_COMMIT);
+ git_tree_entry_free(entry);
+ }
+ }
+
+ if (!is_submodule && iter->base.index) {
+ size_t pos;
+
+ error = git_index_snapshot_find(&pos,
+ &iter->index_snapshot, iter->base.entry_srch, path, path_len, 0);
+
+ if (error < 0 && error != GIT_ENOTFOUND)
+ return error;
+
+ if (!error) {
+ git_index_entry *e = git_vector_get(&iter->index_snapshot, pos);
+ is_submodule = (e->mode == GIT_FILEMODE_COMMIT);
+ }
+ }
+
+ *out = is_submodule;
+ return 0;
+}
+
+static void filesystem_iterator_frame_push_ignores(
+ filesystem_iterator *iter,
+ filesystem_iterator_entry *frame_entry,
+ filesystem_iterator_frame *new_frame)
+{
+ filesystem_iterator_frame *previous_frame;
+ const char *path = frame_entry ? frame_entry->path : "";
+
+ if (!iterator__honor_ignores(&iter->base))
+ return;
+
+ if (git_ignore__lookup(&new_frame->is_ignored,
+ &iter->ignores, path, GIT_DIR_FLAG_TRUE) < 0) {
+ git_error_clear();
+ new_frame->is_ignored = GIT_IGNORE_NOTFOUND;
+ }
+
+ /* if this is not the top level directory... */
+ if (frame_entry) {
+ const char *relative_path;
+
+ previous_frame = filesystem_iterator_parent_frame(iter);
+
+ /* push new ignores for files in this directory */
+ relative_path = frame_entry->path + previous_frame->path_len;
+
+ /* inherit ignored from parent if no rule specified */
+ if (new_frame->is_ignored <= GIT_IGNORE_NOTFOUND)
+ new_frame->is_ignored = previous_frame->is_ignored;
+
+ git_ignore__push_dir(&iter->ignores, relative_path);
+ }
+}
+
+static void filesystem_iterator_frame_pop_ignores(
+ filesystem_iterator *iter)
+{
+ if (iterator__honor_ignores(&iter->base))
+ git_ignore__pop_dir(&iter->ignores);
+}
+
+GIT_INLINE(bool) filesystem_iterator_examine_path(
+ bool *is_dir_out,
+ iterator_pathlist_search_t *match_out,
+ filesystem_iterator *iter,
+ filesystem_iterator_entry *frame_entry,
+ const char *path,
+ size_t path_len)
+{
+ bool is_dir = 0;
+ iterator_pathlist_search_t match = ITERATOR_PATHLIST_FULL;
+
+ *is_dir_out = false;
+ *match_out = ITERATOR_PATHLIST_NONE;
+
+ if (iter->base.start_len) {
+ int cmp = iter->base.strncomp(path, iter->base.start, path_len);
+
+ /* we haven't stat'ed `path` yet, so we don't yet know if it's a
+ * directory or not. special case if the current path may be a
+ * directory that matches the start prefix.
+ */
+ if (cmp == 0) {
+ if (iter->base.start[path_len] == '/')
+ is_dir = true;
+
+ else if (iter->base.start[path_len] != '\0')
+ cmp = -1;
+ }
+
+ if (cmp < 0)
+ return false;
+ }
+
+ if (iter->base.end_len) {
+ int cmp = iter->base.strncomp(path, iter->base.end, iter->base.end_len);
+
+ if (cmp > 0)
+ return false;
+ }
+
+ /* if we have a pathlist that we're limiting to, examine this path now
+ * to avoid a `stat` if we're not interested in the path.
+ */
+ if (iter->base.pathlist.length) {
+ /* if our parent was explicitly included, so too are we */
+ if (frame_entry && frame_entry->match != ITERATOR_PATHLIST_IS_PARENT)
+ match = ITERATOR_PATHLIST_FULL;
+ else
+ match = iterator_pathlist_search(&iter->base, path, path_len);
+
+ if (match == ITERATOR_PATHLIST_NONE)
+ return false;
+
+ /* Ensure that the pathlist entry lines up with what we expected */
+ if (match == ITERATOR_PATHLIST_IS_DIR ||
+ match == ITERATOR_PATHLIST_IS_PARENT)
+ is_dir = true;
+ }
+
+ *is_dir_out = is_dir;
+ *match_out = match;
+ return true;
+}
+
+GIT_INLINE(bool) filesystem_iterator_is_dot_git(
+ filesystem_iterator *iter, const char *path, size_t path_len)
+{
+ size_t len;
+
+ if (!iterator__ignore_dot_git(&iter->base))
+ return false;
+
+ if ((len = path_len) < 4)
+ return false;
+
+ if (path[len - 1] == '/')
+ len--;
+
+ if (git__tolower(path[len - 1]) != 't' ||
+ git__tolower(path[len - 2]) != 'i' ||
+ git__tolower(path[len - 3]) != 'g' ||
+ git__tolower(path[len - 4]) != '.')
+ return false;
+
+ return (len == 4 || path[len - 5] == '/');
+}
+
+static int filesystem_iterator_entry_hash(
+ filesystem_iterator *iter,
+ filesystem_iterator_entry *entry)
+{
+ git_str fullpath = GIT_STR_INIT;
+ int error;
+
+ if (S_ISDIR(entry->st.st_mode)) {
+ memset(&entry->id, 0, git_oid_size(iter->oid_type));
+ return 0;
+ }
+
+ if (iter->base.type == GIT_ITERATOR_WORKDIR)
+ return git_repository_hashfile(&entry->id,
+ iter->base.repo, entry->path, GIT_OBJECT_BLOB, NULL);
+
+ if (!(error = git_str_joinpath(&fullpath, iter->root, entry->path)) &&
+ !(error = git_path_validate_str_length(iter->base.repo, &fullpath)))
+ error = git_odb__hashfile(&entry->id, fullpath.ptr, GIT_OBJECT_BLOB, iter->oid_type);
+
+ git_str_dispose(&fullpath);
+ return error;
+}
+
+static int filesystem_iterator_entry_init(
+ filesystem_iterator_entry **out,
+ filesystem_iterator *iter,
+ filesystem_iterator_frame *frame,
+ const char *path,
+ size_t path_len,
+ struct stat *statbuf,
+ iterator_pathlist_search_t pathlist_match)
+{
+ filesystem_iterator_entry *entry;
+ size_t entry_size;
+ int error = 0;
+
+ *out = NULL;
+
+ /* Make sure to append two bytes, one for the path's null
+ * termination, one for a possible trailing '/' for folders.
+ */
+ GIT_ERROR_CHECK_ALLOC_ADD(&entry_size,
+ sizeof(filesystem_iterator_entry), path_len);
+ GIT_ERROR_CHECK_ALLOC_ADD(&entry_size, entry_size, 2);
+
+ entry = git_pool_malloc(&frame->entry_pool, entry_size);
+ GIT_ERROR_CHECK_ALLOC(entry);
+
+ entry->path_len = path_len;
+ entry->match = pathlist_match;
+ memcpy(entry->path, path, path_len);
+ memcpy(&entry->st, statbuf, sizeof(struct stat));
+
+ /* Suffix directory paths with a '/' */
+ if (S_ISDIR(entry->st.st_mode))
+ entry->path[entry->path_len++] = '/';
+
+ entry->path[entry->path_len] = '\0';
+
+ if (iter->base.flags & GIT_ITERATOR_INCLUDE_HASH)
+ error = filesystem_iterator_entry_hash(iter, entry);
+
+ if (!error)
+ *out = entry;
+
+ return error;
+}
+
+static int filesystem_iterator_frame_push(
+ filesystem_iterator *iter,
+ filesystem_iterator_entry *frame_entry)
+{
+ filesystem_iterator_frame *new_frame = NULL;
+ git_fs_path_diriter diriter = GIT_FS_PATH_DIRITER_INIT;
+ git_str root = GIT_STR_INIT;
+ const char *path;
+ filesystem_iterator_entry *entry;
+ struct stat statbuf;
+ size_t path_len;
+ int error;
+
+ if (iter->frames.size == FILESYSTEM_MAX_DEPTH) {
+ git_error_set(GIT_ERROR_REPOSITORY,
+ "directory nesting too deep (%"PRIuZ")", iter->frames.size);
+ return -1;
+ }
+
+ new_frame = git_array_alloc(iter->frames);
+ GIT_ERROR_CHECK_ALLOC(new_frame);
+
+ memset(new_frame, 0, sizeof(filesystem_iterator_frame));
+
+ if (frame_entry)
+ git_str_joinpath(&root, iter->root, frame_entry->path);
+ else
+ git_str_puts(&root, iter->root);
+
+ if (git_str_oom(&root) ||
+ git_path_validate_str_length(iter->base.repo, &root) < 0) {
+ error = -1;
+ goto done;
+ }
+
+ new_frame->path_len = frame_entry ? frame_entry->path_len : 0;
+
+ /* Any error here is equivalent to the dir not existing, skip over it */
+ if ((error = git_fs_path_diriter_init(
+ &diriter, root.ptr, iter->dirload_flags)) < 0) {
+ error = GIT_ENOTFOUND;
+ goto done;
+ }
+
+ if ((error = git_vector_init(&new_frame->entries, 64,
+ iterator__ignore_case(&iter->base) ?
+ filesystem_iterator_entry_cmp_icase :
+ filesystem_iterator_entry_cmp)) < 0)
+ goto done;
+
+ if ((error = git_pool_init(&new_frame->entry_pool, 1)) < 0)
+ goto done;
+
+ /* check if this directory is ignored */
+ filesystem_iterator_frame_push_ignores(iter, frame_entry, new_frame);
+
+ while ((error = git_fs_path_diriter_next(&diriter)) == 0) {
+ iterator_pathlist_search_t pathlist_match = ITERATOR_PATHLIST_FULL;
+ git_str path_str = GIT_STR_INIT;
+ bool dir_expected = false;
+
+ if ((error = git_fs_path_diriter_fullpath(&path, &path_len, &diriter)) < 0)
+ goto done;
+
+ path_str.ptr = (char *)path;
+ path_str.size = path_len;
+
+ if ((error = git_path_validate_str_length(iter->base.repo, &path_str)) < 0)
+ goto done;
+
+ GIT_ASSERT(path_len > iter->root_len);
+
+ /* remove the prefix if requested */
+ path += iter->root_len;
+ path_len -= iter->root_len;
+
+ /* examine start / end and the pathlist to see if this path is in it.
+ * note that since we haven't yet stat'ed the path, we cannot know
+ * whether it's a directory yet or not, so this can give us an
+ * expected type (S_IFDIR or S_IFREG) that we should examine)
+ */
+ if (!filesystem_iterator_examine_path(&dir_expected, &pathlist_match,
+ iter, frame_entry, path, path_len))
+ continue;
+
+ /* TODO: don't need to stat if assume unchanged for this path and
+ * we have an index, we can just copy the data out of it.
+ */
+
+ if ((error = git_fs_path_diriter_stat(&statbuf, &diriter)) < 0) {
+ /* file was removed between readdir and lstat */
+ if (error == GIT_ENOTFOUND)
+ continue;
+
+ /* treat the file as unreadable */
+ memset(&statbuf, 0, sizeof(statbuf));
+ statbuf.st_mode = GIT_FILEMODE_UNREADABLE;
+
+ error = 0;
+ }
+
+ iter->base.stat_calls++;
+
+ /* Ignore wacky things in the filesystem */
+ if (!S_ISDIR(statbuf.st_mode) &&
+ !S_ISREG(statbuf.st_mode) &&
+ !S_ISLNK(statbuf.st_mode) &&
+ statbuf.st_mode != GIT_FILEMODE_UNREADABLE)
+ continue;
+
+ if (filesystem_iterator_is_dot_git(iter, path, path_len))
+ continue;
+
+ /* convert submodules to GITLINK and remove trailing slashes */
+ if (S_ISDIR(statbuf.st_mode)) {
+ bool submodule = false;
+
+ if ((error = filesystem_iterator_is_submodule(&submodule,
+ iter, path, path_len)) < 0)
+ goto done;
+
+ if (submodule)
+ statbuf.st_mode = GIT_FILEMODE_COMMIT;
+ }
+
+ /* Ensure that the pathlist entry lines up with what we expected */
+ else if (dir_expected)
+ continue;
+
+ if ((error = filesystem_iterator_entry_init(&entry,
+ iter, new_frame, path, path_len, &statbuf, pathlist_match)) < 0)
+ goto done;
+
+ git_vector_insert(&new_frame->entries, entry);
+ }
+
+ if (error == GIT_ITEROVER)
+ error = 0;
+
+ /* sort now that directory suffix is added */
+ git_vector_sort(&new_frame->entries);
+
+done:
+ if (error < 0)
+ git_array_pop(iter->frames);
+
+ git_str_dispose(&root);
+ git_fs_path_diriter_free(&diriter);
+ return error;
+}
+
+GIT_INLINE(int) filesystem_iterator_frame_pop(filesystem_iterator *iter)
+{
+ filesystem_iterator_frame *frame;
+
+ GIT_ASSERT(iter->frames.size);
+
+ frame = git_array_pop(iter->frames);
+ filesystem_iterator_frame_pop_ignores(iter);
+
+ git_pool_clear(&frame->entry_pool);
+ git_vector_free(&frame->entries);
+
+ return 0;
+}
+
+static void filesystem_iterator_set_current(
+ filesystem_iterator *iter,
+ filesystem_iterator_entry *entry)
+{
+ /*
+ * Index entries are limited to 32 bit timestamps. We can safely
+ * cast this since workdir times are only used in the cache; any
+ * mismatch will cause a hash recomputation which is unfortunate
+ * but affects only people who set their filetimes to 2038.
+ * (Same with the file size.)
+ */
+ iter->entry.ctime.seconds = (int32_t)entry->st.st_ctime;
+ iter->entry.mtime.seconds = (int32_t)entry->st.st_mtime;
+
+#if defined(GIT_USE_NSEC)
+ iter->entry.ctime.nanoseconds = entry->st.st_ctime_nsec;
+ iter->entry.mtime.nanoseconds = entry->st.st_mtime_nsec;
+#else
+ iter->entry.ctime.nanoseconds = 0;
+ iter->entry.mtime.nanoseconds = 0;
+#endif
+
+ iter->entry.dev = entry->st.st_dev;
+ iter->entry.ino = entry->st.st_ino;
+ iter->entry.mode = git_futils_canonical_mode(entry->st.st_mode);
+ iter->entry.uid = entry->st.st_uid;
+ iter->entry.gid = entry->st.st_gid;
+ iter->entry.file_size = (uint32_t)entry->st.st_size;
+
+ if (iter->base.flags & GIT_ITERATOR_INCLUDE_HASH)
+ git_oid_cpy(&iter->entry.id, &entry->id);
+ else
+ git_oid_clear(&iter->entry.id, iter->oid_type);
+
+ iter->entry.path = entry->path;
+
+ iter->current_is_ignored = GIT_IGNORE_UNCHECKED;
+}
+
+static int filesystem_iterator_current(
+ const git_index_entry **out, git_iterator *i)
+{
+ filesystem_iterator *iter = GIT_CONTAINER_OF(i, filesystem_iterator, base);
+
+ if (!iterator__has_been_accessed(i))
+ return iter->base.cb->advance(out, i);
+
+ if (!iter->frames.size) {
+ *out = NULL;
+ return GIT_ITEROVER;
+ }
+
+ *out = &iter->entry;
+ return 0;
+}
+
+static int filesystem_iterator_is_dir(
+ bool *is_dir,
+ const filesystem_iterator *iter,
+ const filesystem_iterator_entry *entry)
+{
+ struct stat st;
+ git_str fullpath = GIT_STR_INIT;
+ int error = 0;
+
+ if (S_ISDIR(entry->st.st_mode)) {
+ *is_dir = 1;
+ goto done;
+ }
+
+ if (!iterator__descend_symlinks(iter) || !S_ISLNK(entry->st.st_mode)) {
+ *is_dir = 0;
+ goto done;
+ }
+
+ if ((error = git_str_joinpath(&fullpath, iter->root, entry->path)) < 0 ||
+ (error = git_path_validate_str_length(iter->base.repo, &fullpath)) < 0 ||
+ (error = p_stat(fullpath.ptr, &st)) < 0)
+ goto done;
+
+ *is_dir = S_ISDIR(st.st_mode);
+
+done:
+ git_str_dispose(&fullpath);
+ return error;
+}
+
+static int filesystem_iterator_advance(
+ const git_index_entry **out, git_iterator *i)
+{
+ filesystem_iterator *iter = GIT_CONTAINER_OF(i, filesystem_iterator, base);
+ bool is_dir;
+ int error = 0;
+
+ iter->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
+
+ /* examine filesystem entries until we find the next one to return */
+ while (true) {
+ filesystem_iterator_frame *frame;
+ filesystem_iterator_entry *entry;
+
+ if ((frame = filesystem_iterator_current_frame(iter)) == NULL) {
+ error = GIT_ITEROVER;
+ break;
+ }
+
+ /* no more entries in this frame. pop the frame out */
+ if (frame->next_idx == frame->entries.length) {
+ filesystem_iterator_frame_pop(iter);
+ continue;
+ }
+
+ /* we have more entries in the current frame, that's our next entry */
+ entry = frame->entries.contents[frame->next_idx];
+ frame->next_idx++;
+
+ if ((error = filesystem_iterator_is_dir(&is_dir, iter, entry)) < 0)
+ break;
+
+ if (is_dir) {
+ if (iterator__do_autoexpand(iter)) {
+ error = filesystem_iterator_frame_push(iter, entry);
+
+ /* may get GIT_ENOTFOUND due to races or permission problems
+ * that we want to quietly swallow
+ */
+ if (error == GIT_ENOTFOUND)
+ continue;
+ else if (error < 0)
+ break;
+ }
+
+ if (!iterator__include_trees(iter))
+ continue;
+ }
+
+ filesystem_iterator_set_current(iter, entry);
+ break;
+ }
+
+ if (out)
+ *out = (error == 0) ? &iter->entry : NULL;
+
+ return error;
+}
+
+static int filesystem_iterator_advance_into(
+ const git_index_entry **out, git_iterator *i)
+{
+ filesystem_iterator *iter = GIT_CONTAINER_OF(i, filesystem_iterator, base);
+ filesystem_iterator_frame *frame;
+ filesystem_iterator_entry *prev_entry;
+ int error;
+
+ if (out)
+ *out = NULL;
+
+ if ((frame = filesystem_iterator_current_frame(iter)) == NULL)
+ return GIT_ITEROVER;
+
+ /* get the last seen entry */
+ prev_entry = filesystem_iterator_current_entry(frame);
+
+ /* it's legal to call advance_into when auto-expand is on. in this case,
+ * we will have pushed a new (empty) frame on to the stack for this
+ * new directory. since it's empty, its current_entry should be null.
+ */
+ GIT_ASSERT(iterator__do_autoexpand(i) ^ (prev_entry != NULL));
+
+ if (prev_entry) {
+ if (prev_entry->st.st_mode != GIT_FILEMODE_COMMIT &&
+ !S_ISDIR(prev_entry->st.st_mode))
+ return 0;
+
+ if ((error = filesystem_iterator_frame_push(iter, prev_entry)) < 0)
+ return error;
+ }
+
+ /* we've advanced into the directory in question, let advance
+ * find the first entry
+ */
+ return filesystem_iterator_advance(out, i);
+}
+
+int git_iterator_current_workdir_path(git_str **out, git_iterator *i)
+{
+ filesystem_iterator *iter = GIT_CONTAINER_OF(i, filesystem_iterator, base);
+ const git_index_entry *entry;
+
+ if (i->type != GIT_ITERATOR_FS &&
+ i->type != GIT_ITERATOR_WORKDIR) {
+ *out = NULL;
+ return 0;
+ }
+
+ git_str_truncate(&iter->current_path, iter->root_len);
+
+ if (git_iterator_current(&entry, i) < 0 ||
+ git_str_puts(&iter->current_path, entry->path) < 0)
+ return -1;
+
+ *out = &iter->current_path;
+ return 0;
+}
+
+GIT_INLINE(git_dir_flag) entry_dir_flag(git_index_entry *entry)
+{
+#if defined(GIT_WIN32) && !defined(__MINGW32__)
+ return (entry && entry->mode) ?
+ (S_ISDIR(entry->mode) ? GIT_DIR_FLAG_TRUE : GIT_DIR_FLAG_FALSE) :
+ GIT_DIR_FLAG_UNKNOWN;
+#else
+ GIT_UNUSED(entry);
+ return GIT_DIR_FLAG_UNKNOWN;
+#endif
+}
+
+static void filesystem_iterator_update_ignored(filesystem_iterator *iter)
+{
+ filesystem_iterator_frame *frame;
+ git_dir_flag dir_flag = entry_dir_flag(&iter->entry);
+
+ if (git_ignore__lookup(&iter->current_is_ignored,
+ &iter->ignores, iter->entry.path, dir_flag) < 0) {
+ git_error_clear();
+ iter->current_is_ignored = GIT_IGNORE_NOTFOUND;
+ }
+
+ /* use ignore from containing frame stack */
+ if (iter->current_is_ignored <= GIT_IGNORE_NOTFOUND) {
+ frame = filesystem_iterator_current_frame(iter);
+ iter->current_is_ignored = frame->is_ignored;
+ }
+}
+
+GIT_INLINE(bool) filesystem_iterator_current_is_ignored(
+ filesystem_iterator *iter)
+{
+ if (iter->current_is_ignored == GIT_IGNORE_UNCHECKED)
+ filesystem_iterator_update_ignored(iter);
+
+ return (iter->current_is_ignored == GIT_IGNORE_TRUE);
+}
+
+bool git_iterator_current_is_ignored(git_iterator *i)
+{
+ filesystem_iterator *iter = NULL;
+
+ if (i->type != GIT_ITERATOR_WORKDIR)
+ return false;
+
+ iter = GIT_CONTAINER_OF(i, filesystem_iterator, base);
+
+ return filesystem_iterator_current_is_ignored(iter);
+}
+
+bool git_iterator_current_tree_is_ignored(git_iterator *i)
+{
+ filesystem_iterator *iter = GIT_CONTAINER_OF(i, filesystem_iterator, base);
+ filesystem_iterator_frame *frame;
+
+ if (i->type != GIT_ITERATOR_WORKDIR)
+ return false;
+
+ frame = filesystem_iterator_current_frame(iter);
+ return (frame->is_ignored == GIT_IGNORE_TRUE);
+}
+
+static int filesystem_iterator_advance_over(
+ const git_index_entry **out,
+ git_iterator_status_t *status,
+ git_iterator *i)
+{
+ filesystem_iterator *iter = GIT_CONTAINER_OF(i, filesystem_iterator, base);
+ filesystem_iterator_frame *current_frame;
+ filesystem_iterator_entry *current_entry;
+ const git_index_entry *entry = NULL;
+ const char *base;
+ int error = 0;
+
+ *out = NULL;
+ *status = GIT_ITERATOR_STATUS_NORMAL;
+
+ GIT_ASSERT(iterator__has_been_accessed(i));
+
+ current_frame = filesystem_iterator_current_frame(iter);
+ GIT_ASSERT(current_frame);
+
+ current_entry = filesystem_iterator_current_entry(current_frame);
+ GIT_ASSERT(current_entry);
+
+ if ((error = git_iterator_current(&entry, i)) < 0)
+ return error;
+
+ if (!S_ISDIR(entry->mode)) {
+ if (filesystem_iterator_current_is_ignored(iter))
+ *status = GIT_ITERATOR_STATUS_IGNORED;
+
+ return filesystem_iterator_advance(out, i);
+ }
+
+ git_str_clear(&iter->tmp_buf);
+ if ((error = git_str_puts(&iter->tmp_buf, entry->path)) < 0)
+ return error;
+
+ base = iter->tmp_buf.ptr;
+
+ /* scan inside the directory looking for files. if we find nothing,
+ * we will remain EMPTY. if we find any ignored item, upgrade EMPTY to
+ * IGNORED. if we find a real actual item, upgrade all the way to NORMAL
+ * and then stop.
+ *
+ * however, if we're here looking for a pathlist item (but are not
+ * actually in the pathlist ourselves) then start at FILTERED instead of
+ * EMPTY. callers then know that this path was not something they asked
+ * about.
+ */
+ *status = current_entry->match == ITERATOR_PATHLIST_IS_PARENT ?
+ GIT_ITERATOR_STATUS_FILTERED : GIT_ITERATOR_STATUS_EMPTY;
+
+ while (entry && !iter->base.prefixcomp(entry->path, base)) {
+ if (filesystem_iterator_current_is_ignored(iter)) {
+ /* if we found an explicitly ignored item, then update from
+ * EMPTY to IGNORED
+ */
+ *status = GIT_ITERATOR_STATUS_IGNORED;
+ } else if (S_ISDIR(entry->mode)) {
+ error = filesystem_iterator_advance_into(&entry, i);
+
+ if (!error)
+ continue;
+
+ /* this directory disappeared, ignore it */
+ else if (error == GIT_ENOTFOUND)
+ error = 0;
+
+ /* a real error occurred */
+ else
+ break;
+ } else {
+ /* we found a non-ignored item, treat parent as untracked */
+ *status = GIT_ITERATOR_STATUS_NORMAL;
+ break;
+ }
+
+ if ((error = git_iterator_advance(&entry, i)) < 0)
+ break;
+ }
+
+ /* wrap up scan back to base directory */
+ while (entry && !iter->base.prefixcomp(entry->path, base)) {
+ if ((error = git_iterator_advance(&entry, i)) < 0)
+ break;
+ }
+
+ if (!error)
+ *out = entry;
+
+ return error;
+}
+
+static void filesystem_iterator_clear(filesystem_iterator *iter)
+{
+ while (iter->frames.size)
+ filesystem_iterator_frame_pop(iter);
+
+ git_array_clear(iter->frames);
+ git_ignore__free(&iter->ignores);
+
+ git_str_dispose(&iter->tmp_buf);
+
+ iterator_clear(&iter->base);
+}
+
+static int filesystem_iterator_init(filesystem_iterator *iter)
+{
+ int error;
+
+ if (iterator__honor_ignores(&iter->base) &&
+ (error = git_ignore__for_path(iter->base.repo,
+ ".gitignore", &iter->ignores)) < 0)
+ return error;
+
+ if ((error = filesystem_iterator_frame_push(iter, NULL)) < 0)
+ return error;
+
+ iter->base.flags &= ~GIT_ITERATOR_FIRST_ACCESS;
+
+ return 0;
+}
+
+static int filesystem_iterator_reset(git_iterator *i)
+{
+ filesystem_iterator *iter = GIT_CONTAINER_OF(i, filesystem_iterator, base);
+
+ filesystem_iterator_clear(iter);
+ return filesystem_iterator_init(iter);
+}
+
+static void filesystem_iterator_free(git_iterator *i)
+{
+ filesystem_iterator *iter = GIT_CONTAINER_OF(i, filesystem_iterator, base);
+ git__free(iter->root);
+ git_str_dispose(&iter->current_path);
+ git_tree_free(iter->tree);
+ if (iter->index)
+ git_index_snapshot_release(&iter->index_snapshot, iter->index);
+ filesystem_iterator_clear(iter);
+}
+
+static int iterator_for_filesystem(
+ git_iterator **out,
+ git_repository *repo,
+ const char *root,
+ git_index *index,
+ git_tree *tree,
+ git_iterator_t type,
+ git_iterator_options *options)
+{
+ filesystem_iterator *iter;
+ size_t root_len;
+ int error;
+
+ static git_iterator_callbacks callbacks = {
+ filesystem_iterator_current,
+ filesystem_iterator_advance,
+ filesystem_iterator_advance_into,
+ filesystem_iterator_advance_over,
+ filesystem_iterator_reset,
+ filesystem_iterator_free
+ };
+
+ *out = NULL;
+
+ if (root == NULL)
+ return git_iterator_for_nothing(out, options);
+
+ iter = git__calloc(1, sizeof(filesystem_iterator));
+ GIT_ERROR_CHECK_ALLOC(iter);
+
+ iter->base.type = type;
+ iter->base.cb = &callbacks;
+
+ root_len = strlen(root);
+
+ iter->root = git__malloc(root_len+2);
+ GIT_ERROR_CHECK_ALLOC(iter->root);
+
+ memcpy(iter->root, root, root_len);
+
+ if (root_len == 0 || root[root_len-1] != '/') {
+ iter->root[root_len] = '/';
+ root_len++;
+ }
+ iter->root[root_len] = '\0';
+ iter->root_len = root_len;
+
+ if ((error = git_str_puts(&iter->current_path, iter->root)) < 0)
+ goto on_error;
+
+ if ((error = iterator_init_common(&iter->base, repo, index, options)) < 0)
+ goto on_error;
+
+ if (tree && (error = git_tree_dup(&iter->tree, tree)) < 0)
+ goto on_error;
+
+ if (index &&
+ (error = git_index_snapshot_new(&iter->index_snapshot, index)) < 0)
+ goto on_error;
+
+ iter->index = index;
+ iter->dirload_flags =
+ (iterator__ignore_case(&iter->base) ?
+ GIT_FS_PATH_DIR_IGNORE_CASE : 0) |
+ (iterator__flag(&iter->base, PRECOMPOSE_UNICODE) ?
+ GIT_FS_PATH_DIR_PRECOMPOSE_UNICODE : 0);
+
+ iter->oid_type = options->oid_type;
+
+ if ((error = filesystem_iterator_init(iter)) < 0)
+ goto on_error;
+
+ *out = &iter->base;
+ return 0;
+
+on_error:
+ git_iterator_free(&iter->base);
+ return error;
+}
+
+int git_iterator_for_filesystem(
+ git_iterator **out,
+ const char *root,
+ git_iterator_options *given_opts)
+{
+ git_iterator_options options = GIT_ITERATOR_OPTIONS_INIT;
+
+ if (given_opts)
+ memcpy(&options, given_opts, sizeof(git_iterator_options));
+
+ return iterator_for_filesystem(out,
+ NULL, root, NULL, NULL, GIT_ITERATOR_FS, &options);
+}
+
+int git_iterator_for_workdir_ext(
+ git_iterator **out,
+ git_repository *repo,
+ const char *repo_workdir,
+ git_index *index,
+ git_tree *tree,
+ git_iterator_options *given_opts)
+{
+ git_iterator_options options = GIT_ITERATOR_OPTIONS_INIT;
+
+ if (!repo_workdir) {
+ if (git_repository__ensure_not_bare(repo, "scan working directory") < 0)
+ return GIT_EBAREREPO;
+
+ repo_workdir = git_repository_workdir(repo);
+ }
+
+ /* upgrade to a workdir iterator, adding necessary internal flags */
+ if (given_opts)
+ memcpy(&options, given_opts, sizeof(git_iterator_options));
+
+ options.flags |= GIT_ITERATOR_HONOR_IGNORES |
+ GIT_ITERATOR_IGNORE_DOT_GIT;
+
+ if (!options.oid_type)
+ options.oid_type = repo->oid_type;
+ else if (options.oid_type != repo->oid_type)
+ git_error_set(GIT_ERROR_INVALID,
+ "specified object ID type does not match repository object ID type");
+
+ return iterator_for_filesystem(out,
+ repo, repo_workdir, index, tree, GIT_ITERATOR_WORKDIR, &options);
+}
+
+
+/* Index iterator */
+
+
+typedef struct {
+ git_iterator base;
+ git_vector entries;
+ size_t next_idx;
+
+ /* the pseudotree entry */
+ git_index_entry tree_entry;
+ git_str tree_buf;
+ bool skip_tree;
+
+ const git_index_entry *entry;
+} index_iterator;
+
+static int index_iterator_current(
+ const git_index_entry **out, git_iterator *i)
+{
+ index_iterator *iter = (index_iterator *)i;
+
+ if (!iterator__has_been_accessed(i))
+ return iter->base.cb->advance(out, i);
+
+ if (iter->entry == NULL) {
+ *out = NULL;
+ return GIT_ITEROVER;
+ }
+
+ *out = iter->entry;
+ return 0;
+}
+
+static bool index_iterator_create_pseudotree(
+ const git_index_entry **out,
+ index_iterator *iter,
+ const char *path)
+{
+ const char *prev_path, *relative_path, *dirsep;
+ size_t common_len;
+
+ prev_path = iter->entry ? iter->entry->path : "";
+
+ /* determine if the new path is in a different directory from the old */
+ common_len = git_fs_path_common_dirlen(prev_path, path);
+ relative_path = path + common_len;
+
+ if ((dirsep = strchr(relative_path, '/')) == NULL)
+ return false;
+
+ git_str_clear(&iter->tree_buf);
+ git_str_put(&iter->tree_buf, path, (dirsep - path) + 1);
+
+ iter->tree_entry.mode = GIT_FILEMODE_TREE;
+ iter->tree_entry.path = iter->tree_buf.ptr;
+
+ *out = &iter->tree_entry;
+ return true;
+}
+
+static int index_iterator_skip_pseudotree(index_iterator *iter)
+{
+ GIT_ASSERT(iterator__has_been_accessed(&iter->base));
+ GIT_ASSERT(S_ISDIR(iter->entry->mode));
+
+ while (true) {
+ const git_index_entry *next_entry = NULL;
+
+ if (++iter->next_idx >= iter->entries.length)
+ return GIT_ITEROVER;
+
+ next_entry = iter->entries.contents[iter->next_idx];
+
+ if (iter->base.strncomp(iter->tree_buf.ptr, next_entry->path,
+ iter->tree_buf.size) != 0)
+ break;
+ }
+
+ iter->skip_tree = false;
+ return 0;
+}
+
+static int index_iterator_advance(
+ const git_index_entry **out, git_iterator *i)
+{
+ index_iterator *iter = GIT_CONTAINER_OF(i, index_iterator, base);
+ const git_index_entry *entry = NULL;
+ bool is_submodule;
+ int error = 0;
+
+ iter->base.flags |= GIT_ITERATOR_FIRST_ACCESS;
+
+ while (true) {
+ if (iter->next_idx >= iter->entries.length) {
+ error = GIT_ITEROVER;
+ break;
+ }
+
+ /* we were not asked to expand this pseudotree. advance over it. */
+ if (iter->skip_tree) {
+ index_iterator_skip_pseudotree(iter);
+ continue;
+ }
+
+ entry = iter->entries.contents[iter->next_idx];
+ is_submodule = S_ISGITLINK(entry->mode);
+
+ if (!iterator_has_started(&iter->base, entry->path, is_submodule)) {
+ iter->next_idx++;
+ continue;
+ }
+
+ if (iterator_has_ended(&iter->base, entry->path)) {
+ error = GIT_ITEROVER;
+ break;
+ }
+
+ /* if we have a list of paths we're interested in, examine it */
+ if (!iterator_pathlist_next_is(&iter->base, entry->path)) {
+ iter->next_idx++;
+ continue;
+ }
+
+ /* if this is a conflict, skip it unless we're including conflicts */
+ if (git_index_entry_is_conflict(entry) &&
+ !iterator__include_conflicts(&iter->base)) {
+ iter->next_idx++;
+ continue;
+ }
+
+ /* we've found what will be our next _file_ entry. but if we are
+ * returning trees entries, we may need to return a pseudotree
+ * entry that will contain this. don't advance over this entry,
+ * though, we still need to return it on the next `advance`.
+ */
+ if (iterator__include_trees(&iter->base) &&
+ index_iterator_create_pseudotree(&entry, iter, entry->path)) {
+
+ /* Note whether this pseudo tree should be expanded or not */
+ iter->skip_tree = iterator__dont_autoexpand(&iter->base);
+ break;
+ }
+
+ iter->next_idx++;
+ break;
+ }
+
+ iter->entry = (error == 0) ? entry : NULL;
+
+ if (out)
+ *out = iter->entry;
+
+ return error;
+}
+
+static int index_iterator_advance_into(
+ const git_index_entry **out, git_iterator *i)
+{
+ index_iterator *iter = GIT_CONTAINER_OF(i, index_iterator, base);
+
+ if (! S_ISDIR(iter->tree_entry.mode)) {
+ if (out)
+ *out = NULL;
+
+ return 0;
+ }
+
+ iter->skip_tree = false;
+ return index_iterator_advance(out, i);
+}
+
+static int index_iterator_advance_over(
+ const git_index_entry **out,
+ git_iterator_status_t *status,
+ git_iterator *i)
+{
+ index_iterator *iter = GIT_CONTAINER_OF(i, index_iterator, base);
+ const git_index_entry *entry;
+ int error;
+
+ if ((error = index_iterator_current(&entry, i)) < 0)
+ return error;
+
+ if (S_ISDIR(entry->mode))
+ index_iterator_skip_pseudotree(iter);
+
+ *status = GIT_ITERATOR_STATUS_NORMAL;
+ return index_iterator_advance(out, i);
+}
+
+static void index_iterator_clear(index_iterator *iter)
+{
+ iterator_clear(&iter->base);
+}
+
+static int index_iterator_init(index_iterator *iter)
+{
+ iter->base.flags &= ~GIT_ITERATOR_FIRST_ACCESS;
+ iter->next_idx = 0;
+ iter->skip_tree = false;
+ return 0;
+}
+
+static int index_iterator_reset(git_iterator *i)
+{
+ index_iterator *iter = GIT_CONTAINER_OF(i, index_iterator, base);
+
+ index_iterator_clear(iter);
+ return index_iterator_init(iter);
+}
+
+static void index_iterator_free(git_iterator *i)
+{
+ index_iterator *iter = GIT_CONTAINER_OF(i, index_iterator, base);
+
+ git_index_snapshot_release(&iter->entries, iter->base.index);
+ git_str_dispose(&iter->tree_buf);
+}
+
+int git_iterator_for_index(
+ git_iterator **out,
+ git_repository *repo,
+ git_index *index,
+ git_iterator_options *options)
+{
+ index_iterator *iter;
+ int error;
+
+ static git_iterator_callbacks callbacks = {
+ index_iterator_current,
+ index_iterator_advance,
+ index_iterator_advance_into,
+ index_iterator_advance_over,
+ index_iterator_reset,
+ index_iterator_free
+ };
+
+ *out = NULL;
+
+ if (index == NULL)
+ return git_iterator_for_nothing(out, options);
+
+ iter = git__calloc(1, sizeof(index_iterator));
+ GIT_ERROR_CHECK_ALLOC(iter);
+
+ iter->base.type = GIT_ITERATOR_INDEX;
+ iter->base.cb = &callbacks;
+
+ if ((error = iterator_init_common(&iter->base, repo, index, options)) < 0 ||
+ (error = git_index_snapshot_new(&iter->entries, index)) < 0 ||
+ (error = index_iterator_init(iter)) < 0)
+ goto on_error;
+
+ git_vector_set_cmp(&iter->entries, iterator__ignore_case(&iter->base) ?
+ git_index_entry_icmp : git_index_entry_cmp);
+ git_vector_sort(&iter->entries);
+
+ *out = &iter->base;
+ return 0;
+
+on_error:
+ git_iterator_free(&iter->base);
+ return error;
+}
+
+
+/* Iterator API */
+
+int git_iterator_reset_range(
+ git_iterator *i, const char *start, const char *end)
+{
+ if (iterator_reset_range(i, start, end) < 0)
+ return -1;
+
+ return i->cb->reset(i);
+}
+
+int git_iterator_set_ignore_case(git_iterator *i, bool ignore_case)
+{
+ GIT_ASSERT(!iterator__has_been_accessed(i));
+ iterator_set_ignore_case(i, ignore_case);
+ return 0;
+}
+
+void git_iterator_free(git_iterator *iter)
+{
+ if (iter == NULL)
+ return;
+
+ iter->cb->free(iter);
+
+ git_vector_free(&iter->pathlist);
+ git__free(iter->start);
+ git__free(iter->end);
+
+ memset(iter, 0, sizeof(*iter));
+
+ git__free(iter);
+}
+
+int git_iterator_foreach(
+ git_iterator *iterator,
+ git_iterator_foreach_cb cb,
+ void *data)
+{
+ const git_index_entry *iterator_item;
+ int error = 0;
+
+ if ((error = git_iterator_current(&iterator_item, iterator)) < 0)
+ goto done;
+
+ if ((error = cb(iterator_item, data)) != 0)
+ goto done;
+
+ while (true) {
+ if ((error = git_iterator_advance(&iterator_item, iterator)) < 0)
+ goto done;
+
+ if ((error = cb(iterator_item, data)) != 0)
+ goto done;
+ }
+
+done:
+ if (error == GIT_ITEROVER)
+ error = 0;
+
+ return error;
+}
+
+int git_iterator_walk(
+ git_iterator **iterators,
+ size_t cnt,
+ git_iterator_walk_cb cb,
+ void *data)
+{
+ const git_index_entry **iterator_item; /* next in each iterator */
+ const git_index_entry **cur_items; /* current path in each iter */
+ const git_index_entry *first_match;
+ size_t i, j;
+ int error = 0;
+
+ iterator_item = git__calloc(cnt, sizeof(git_index_entry *));
+ cur_items = git__calloc(cnt, sizeof(git_index_entry *));
+
+ GIT_ERROR_CHECK_ALLOC(iterator_item);
+ GIT_ERROR_CHECK_ALLOC(cur_items);
+
+ /* Set up the iterators */
+ for (i = 0; i < cnt; i++) {
+ error = git_iterator_current(&iterator_item[i], iterators[i]);
+
+ if (error < 0 && error != GIT_ITEROVER)
+ goto done;
+ }
+
+ while (true) {
+ for (i = 0; i < cnt; i++)
+ cur_items[i] = NULL;
+
+ first_match = NULL;
+
+ /* Find the next path(s) to consume from each iterator */
+ for (i = 0; i < cnt; i++) {
+ if (iterator_item[i] == NULL)
+ continue;
+
+ if (first_match == NULL) {
+ first_match = iterator_item[i];
+ cur_items[i] = iterator_item[i];
+ } else {
+ int path_diff = git_index_entry_cmp(iterator_item[i], first_match);
+
+ if (path_diff < 0) {
+ /* Found an index entry that sorts before the one we're
+ * looking at. Forget that we've seen the other and
+ * look at the other iterators for this path.
+ */
+ for (j = 0; j < i; j++)
+ cur_items[j] = NULL;
+
+ first_match = iterator_item[i];
+ cur_items[i] = iterator_item[i];
+ } else if (path_diff == 0) {
+ cur_items[i] = iterator_item[i];
+ }
+ }
+ }
+
+ if (first_match == NULL)
+ break;
+
+ if ((error = cb(cur_items, data)) != 0)
+ goto done;
+
+ /* Advance each iterator that participated */
+ for (i = 0; i < cnt; i++) {
+ if (cur_items[i] == NULL)
+ continue;
+
+ error = git_iterator_advance(&iterator_item[i], iterators[i]);
+
+ if (error < 0 && error != GIT_ITEROVER)
+ goto done;
+ }
+ }
+
+done:
+ git__free((git_index_entry **)iterator_item);
+ git__free((git_index_entry **)cur_items);
+
+ if (error == GIT_ITEROVER)
+ error = 0;
+
+ return error;
+}
diff --git a/src/libgit2/iterator.h b/src/libgit2/iterator.h
new file mode 100644
index 0000000..7963ce7
--- /dev/null
+++ b/src/libgit2/iterator.h
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_iterator_h__
+#define INCLUDE_iterator_h__
+
+#include "common.h"
+
+#include "git2/index.h"
+#include "vector.h"
+#include "str.h"
+#include "ignore.h"
+
+typedef struct git_iterator git_iterator;
+
+typedef enum {
+ GIT_ITERATOR_EMPTY = 0,
+ GIT_ITERATOR_TREE = 1,
+ GIT_ITERATOR_INDEX = 2,
+ GIT_ITERATOR_WORKDIR = 3,
+ GIT_ITERATOR_FS = 4
+} git_iterator_t;
+
+typedef enum {
+ /** ignore case for entry sort order */
+ GIT_ITERATOR_IGNORE_CASE = (1u << 0),
+ /** force case sensitivity for entry sort order */
+ GIT_ITERATOR_DONT_IGNORE_CASE = (1u << 1),
+ /** return tree items in addition to blob items */
+ GIT_ITERATOR_INCLUDE_TREES = (1u << 2),
+ /** don't flatten trees, requiring advance_into (implies INCLUDE_TREES) */
+ GIT_ITERATOR_DONT_AUTOEXPAND = (1u << 3),
+ /** convert precomposed unicode to decomposed unicode */
+ GIT_ITERATOR_PRECOMPOSE_UNICODE = (1u << 4),
+ /** never convert precomposed unicode to decomposed unicode */
+ GIT_ITERATOR_DONT_PRECOMPOSE_UNICODE = (1u << 5),
+ /** include conflicts */
+ GIT_ITERATOR_INCLUDE_CONFLICTS = (1u << 6),
+ /** descend into symlinked directories */
+ GIT_ITERATOR_DESCEND_SYMLINKS = (1u << 7),
+ /** hash files in workdir or filesystem iterators */
+ GIT_ITERATOR_INCLUDE_HASH = (1u << 8)
+} git_iterator_flag_t;
+
+typedef enum {
+ GIT_ITERATOR_STATUS_NORMAL = 0,
+ GIT_ITERATOR_STATUS_IGNORED = 1,
+ GIT_ITERATOR_STATUS_EMPTY = 2,
+ GIT_ITERATOR_STATUS_FILTERED = 3
+} git_iterator_status_t;
+
+typedef struct {
+ const char *start;
+ const char *end;
+
+ /* paths to include in the iterator (literal). if set, any paths not
+ * listed here will be excluded from iteration.
+ */
+ git_strarray pathlist;
+
+ /* flags, from above */
+ unsigned int flags;
+
+ /* oid type - necessary for non-workdir filesystem iterators */
+ git_oid_t oid_type;
+} git_iterator_options;
+
+#define GIT_ITERATOR_OPTIONS_INIT {0}
+
+typedef struct {
+ int (*current)(const git_index_entry **, git_iterator *);
+ int (*advance)(const git_index_entry **, git_iterator *);
+ int (*advance_into)(const git_index_entry **, git_iterator *);
+ int (*advance_over)(
+ const git_index_entry **, git_iterator_status_t *, git_iterator *);
+ int (*reset)(git_iterator *);
+ void (*free)(git_iterator *);
+} git_iterator_callbacks;
+
+struct git_iterator {
+ git_iterator_t type;
+ git_iterator_callbacks *cb;
+
+ git_repository *repo;
+ git_index *index;
+
+ char *start;
+ size_t start_len;
+
+ char *end;
+ size_t end_len;
+
+ bool started;
+ bool ended;
+ git_vector pathlist;
+ size_t pathlist_walk_idx;
+ int (*strcomp)(const char *a, const char *b);
+ int (*strncomp)(const char *a, const char *b, size_t n);
+ int (*prefixcomp)(const char *str, const char *prefix);
+ int (*entry_srch)(const void *key, const void *array_member);
+ size_t stat_calls;
+ unsigned int flags;
+};
+
+extern int git_iterator_for_nothing(
+ git_iterator **out,
+ git_iterator_options *options);
+
+/* tree iterators will match the ignore_case value from the index of the
+ * repository, unless you override with a non-zero flag value
+ */
+extern int git_iterator_for_tree(
+ git_iterator **out,
+ git_tree *tree,
+ git_iterator_options *options);
+
+/* index iterators will take the ignore_case value from the index; the
+ * ignore_case flags are not used
+ */
+extern int git_iterator_for_index(
+ git_iterator **out,
+ git_repository *repo,
+ git_index *index,
+ git_iterator_options *options);
+
+extern int git_iterator_for_workdir_ext(
+ git_iterator **out,
+ git_repository *repo,
+ const char *repo_workdir,
+ git_index *index,
+ git_tree *tree,
+ git_iterator_options *options);
+
+/* workdir iterators will match the ignore_case value from the index of the
+ * repository, unless you override with a non-zero flag value
+ */
+GIT_INLINE(int) git_iterator_for_workdir(
+ git_iterator **out,
+ git_repository *repo,
+ git_index *index,
+ git_tree *tree,
+ git_iterator_options *options)
+{
+ return git_iterator_for_workdir_ext(out, repo, NULL, index, tree, options);
+}
+
+/* for filesystem iterators, you have to explicitly pass in the ignore_case
+ * behavior that you desire
+ */
+extern int git_iterator_for_filesystem(
+ git_iterator **out,
+ const char *root,
+ git_iterator_options *options);
+
+extern void git_iterator_free(git_iterator *iter);
+
+/* Return a git_index_entry structure for the current value the iterator
+ * is looking at or NULL if the iterator is at the end.
+ *
+ * The entry may noy be fully populated. Tree iterators will only have a
+ * value mode, OID, and path. Workdir iterators will not have an OID (but
+ * you can use `git_iterator_current_oid()` to calculate it on demand).
+ *
+ * You do not need to free the entry. It is still "owned" by the iterator.
+ * Once you call `git_iterator_advance()` then the old entry is no longer
+ * guaranteed to be valid - it may be freed or just overwritten in place.
+ */
+GIT_INLINE(int) git_iterator_current(
+ const git_index_entry **entry, git_iterator *iter)
+{
+ return iter->cb->current(entry, iter);
+}
+
+/**
+ * Advance to the next item for the iterator.
+ *
+ * If GIT_ITERATOR_INCLUDE_TREES is set, this may be a tree item. If
+ * GIT_ITERATOR_DONT_AUTOEXPAND is set, calling this again when on a tree
+ * item will skip over all the items under that tree.
+ */
+GIT_INLINE(int) git_iterator_advance(
+ const git_index_entry **entry, git_iterator *iter)
+{
+ return iter->cb->advance(entry, iter);
+}
+
+/**
+ * Iterate into a tree item (when GIT_ITERATOR_DONT_AUTOEXPAND is set).
+ *
+ * git_iterator_advance() steps through all items being iterated over
+ * (either with or without trees, depending on GIT_ITERATOR_INCLUDE_TREES),
+ * but if GIT_ITERATOR_DONT_AUTOEXPAND is set, it will skip to the next
+ * sibling of a tree instead of going to the first child of the tree. In
+ * that case, use this function to advance to the first child of the tree.
+ *
+ * If the current item is not a tree, this is a no-op.
+ *
+ * For filesystem and working directory iterators, a tree (i.e. directory)
+ * can be empty. In that case, this function returns GIT_ENOTFOUND and
+ * does not advance. That can't happen for tree and index iterators.
+ */
+GIT_INLINE(int) git_iterator_advance_into(
+ const git_index_entry **entry, git_iterator *iter)
+{
+ return iter->cb->advance_into(entry, iter);
+}
+
+/* Advance over a directory and check if it contains no files or just
+ * ignored files.
+ *
+ * In a tree or the index, all directories will contain files, but in the
+ * working directory it is possible to have an empty directory tree or a
+ * tree that only contains ignored files. Many Git operations treat these
+ * cases specially. This advances over a directory (presumably an
+ * untracked directory) but checks during the scan if there are any files
+ * and any non-ignored files.
+ */
+GIT_INLINE(int) git_iterator_advance_over(
+ const git_index_entry **entry,
+ git_iterator_status_t *status,
+ git_iterator *iter)
+{
+ return iter->cb->advance_over(entry, status, iter);
+}
+
+/**
+ * Go back to the start of the iteration.
+ */
+GIT_INLINE(int) git_iterator_reset(git_iterator *iter)
+{
+ return iter->cb->reset(iter);
+}
+
+/**
+ * Go back to the start of the iteration after updating the `start` and
+ * `end` pathname boundaries of the iteration.
+ */
+extern int git_iterator_reset_range(
+ git_iterator *iter, const char *start, const char *end);
+
+GIT_INLINE(git_iterator_t) git_iterator_type(git_iterator *iter)
+{
+ return iter->type;
+}
+
+GIT_INLINE(git_repository *) git_iterator_owner(git_iterator *iter)
+{
+ return iter->repo;
+}
+
+GIT_INLINE(git_index *) git_iterator_index(git_iterator *iter)
+{
+ return iter->index;
+}
+
+GIT_INLINE(git_iterator_flag_t) git_iterator_flags(git_iterator *iter)
+{
+ return iter->flags;
+}
+
+GIT_INLINE(bool) git_iterator_ignore_case(git_iterator *iter)
+{
+ return ((iter->flags & GIT_ITERATOR_IGNORE_CASE) != 0);
+}
+
+extern int git_iterator_set_ignore_case(
+ git_iterator *iter, bool ignore_case);
+
+extern int git_iterator_current_tree_entry(
+ const git_tree_entry **entry_out, git_iterator *iter);
+
+extern int git_iterator_current_parent_tree(
+ const git_tree **tree_out, git_iterator *iter, size_t depth);
+
+extern bool git_iterator_current_is_ignored(git_iterator *iter);
+
+extern bool git_iterator_current_tree_is_ignored(git_iterator *iter);
+
+/**
+ * Get full path of the current item from a workdir iterator. This will
+ * return NULL for a non-workdir iterator. The git_str is still owned by
+ * the iterator; this is exposed just for efficiency.
+ */
+extern int git_iterator_current_workdir_path(
+ git_str **path, git_iterator *iter);
+
+/**
+ * Retrieve the index stored in the iterator.
+ *
+ * Only implemented for the workdir and index iterators.
+ */
+extern git_index *git_iterator_index(git_iterator *iter);
+
+typedef int (*git_iterator_foreach_cb)(
+ const git_index_entry *entry,
+ void *data);
+
+/**
+ * Walk the given iterator and invoke the callback for each path
+ * contained in the iterator.
+ */
+extern int git_iterator_foreach(
+ git_iterator *iterator,
+ git_iterator_foreach_cb cb,
+ void *data);
+
+typedef int (*git_iterator_walk_cb)(
+ const git_index_entry **entries,
+ void *data);
+
+/**
+ * Walk the given iterators in lock-step. The given callback will be
+ * called for each unique path, with the index entry in each iterator
+ * (or NULL if the given iterator does not contain that path).
+ */
+extern int git_iterator_walk(
+ git_iterator **iterators,
+ size_t cnt,
+ git_iterator_walk_cb cb,
+ void *data);
+
+#endif
diff --git a/src/libgit2/libgit2.c b/src/libgit2/libgit2.c
new file mode 100644
index 0000000..ce28714
--- /dev/null
+++ b/src/libgit2/libgit2.c
@@ -0,0 +1,483 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "libgit2.h"
+
+#include <git2.h>
+#include "alloc.h"
+#include "buf.h"
+#include "cache.h"
+#include "common.h"
+#include "filter.h"
+#include "grafts.h"
+#include "hash.h"
+#include "index.h"
+#include "merge_driver.h"
+#include "pool.h"
+#include "mwindow.h"
+#include "object.h"
+#include "odb.h"
+#include "rand.h"
+#include "refs.h"
+#include "runtime.h"
+#include "sysdir.h"
+#include "thread.h"
+#include "threadstate.h"
+#include "git2/global.h"
+#include "streams/registry.h"
+#include "streams/mbedtls.h"
+#include "streams/openssl.h"
+#include "streams/socket.h"
+#include "transports/smart.h"
+#include "transports/http.h"
+#include "transports/ssh.h"
+
+#ifdef GIT_WIN32
+# include "win32/w32_leakcheck.h"
+#endif
+
+/* Declarations for tuneable settings */
+extern size_t git_mwindow__window_size;
+extern size_t git_mwindow__mapped_limit;
+extern size_t git_mwindow__file_limit;
+extern size_t git_indexer__max_objects;
+extern bool git_disable_pack_keep_file_checks;
+extern int git_odb__packed_priority;
+extern int git_odb__loose_priority;
+extern int git_socket_stream__connect_timeout;
+extern int git_socket_stream__timeout;
+
+char *git__user_agent;
+char *git__ssl_ciphers;
+
+static void libgit2_settings_global_shutdown(void)
+{
+ git__free(git__user_agent);
+ git__free(git__ssl_ciphers);
+ git_repository__free_extensions();
+}
+
+static int git_libgit2_settings_global_init(void)
+{
+ return git_runtime_shutdown_register(libgit2_settings_global_shutdown);
+}
+
+int git_libgit2_init(void)
+{
+ static git_runtime_init_fn init_fns[] = {
+#ifdef GIT_WIN32
+ git_win32_leakcheck_global_init,
+#endif
+ git_allocator_global_init,
+ git_threadstate_global_init,
+ git_threads_global_init,
+ git_rand_global_init,
+ git_hash_global_init,
+ git_sysdir_global_init,
+ git_filter_global_init,
+ git_merge_driver_global_init,
+ git_transport_ssh_global_init,
+ git_stream_registry_global_init,
+ git_socket_stream_global_init,
+ git_openssl_stream_global_init,
+ git_mbedtls_stream_global_init,
+ git_mwindow_global_init,
+ git_pool_global_init,
+ git_libgit2_settings_global_init
+ };
+
+ return git_runtime_init(init_fns, ARRAY_SIZE(init_fns));
+}
+
+int git_libgit2_init_count(void)
+{
+ return git_runtime_init_count();
+}
+
+int git_libgit2_shutdown(void)
+{
+ return git_runtime_shutdown();
+}
+
+int git_libgit2_version(int *major, int *minor, int *rev)
+{
+ *major = LIBGIT2_VER_MAJOR;
+ *minor = LIBGIT2_VER_MINOR;
+ *rev = LIBGIT2_VER_REVISION;
+
+ return 0;
+}
+
+const char *git_libgit2_prerelease(void)
+{
+ return LIBGIT2_VER_PRERELEASE;
+}
+
+int git_libgit2_features(void)
+{
+ return 0
+#ifdef GIT_THREADS
+ | GIT_FEATURE_THREADS
+#endif
+#ifdef GIT_HTTPS
+ | GIT_FEATURE_HTTPS
+#endif
+#if defined(GIT_SSH)
+ | GIT_FEATURE_SSH
+#endif
+#if defined(GIT_USE_NSEC)
+ | GIT_FEATURE_NSEC
+#endif
+ ;
+}
+
+static int config_level_to_sysdir(int *out, int config_level)
+{
+ switch (config_level) {
+ case GIT_CONFIG_LEVEL_SYSTEM:
+ *out = GIT_SYSDIR_SYSTEM;
+ return 0;
+ case GIT_CONFIG_LEVEL_XDG:
+ *out = GIT_SYSDIR_XDG;
+ return 0;
+ case GIT_CONFIG_LEVEL_GLOBAL:
+ *out = GIT_SYSDIR_GLOBAL;
+ return 0;
+ case GIT_CONFIG_LEVEL_PROGRAMDATA:
+ *out = GIT_SYSDIR_PROGRAMDATA;
+ return 0;
+ default:
+ break;
+ }
+
+ git_error_set(
+ GIT_ERROR_INVALID, "invalid config path selector %d", config_level);
+ return -1;
+}
+
+const char *git_libgit2__user_agent(void)
+{
+ return git__user_agent;
+}
+
+const char *git_libgit2__ssl_ciphers(void)
+{
+ return git__ssl_ciphers;
+}
+
+int git_libgit2_opts(int key, ...)
+{
+ int error = 0;
+ va_list ap;
+
+ va_start(ap, key);
+
+ switch (key) {
+ case GIT_OPT_SET_MWINDOW_SIZE:
+ git_mwindow__window_size = va_arg(ap, size_t);
+ break;
+
+ case GIT_OPT_GET_MWINDOW_SIZE:
+ *(va_arg(ap, size_t *)) = git_mwindow__window_size;
+ break;
+
+ case GIT_OPT_SET_MWINDOW_MAPPED_LIMIT:
+ git_mwindow__mapped_limit = va_arg(ap, size_t);
+ break;
+
+ case GIT_OPT_GET_MWINDOW_MAPPED_LIMIT:
+ *(va_arg(ap, size_t *)) = git_mwindow__mapped_limit;
+ break;
+
+ case GIT_OPT_SET_MWINDOW_FILE_LIMIT:
+ git_mwindow__file_limit = va_arg(ap, size_t);
+ break;
+
+ case GIT_OPT_GET_MWINDOW_FILE_LIMIT:
+ *(va_arg(ap, size_t *)) = git_mwindow__file_limit;
+ break;
+
+ case GIT_OPT_GET_SEARCH_PATH:
+ {
+ int sysdir = va_arg(ap, int);
+ git_buf *out = va_arg(ap, git_buf *);
+ git_str str = GIT_STR_INIT;
+ const git_str *tmp;
+ int level;
+
+ if ((error = git_buf_tostr(&str, out)) < 0 ||
+ (error = config_level_to_sysdir(&level, sysdir)) < 0 ||
+ (error = git_sysdir_get(&tmp, level)) < 0 ||
+ (error = git_str_put(&str, tmp->ptr, tmp->size)) < 0)
+ break;
+
+ error = git_buf_fromstr(out, &str);
+ }
+ break;
+
+ case GIT_OPT_SET_SEARCH_PATH:
+ {
+ int level;
+
+ if ((error = config_level_to_sysdir(&level, va_arg(ap, int))) >= 0)
+ error = git_sysdir_set(level, va_arg(ap, const char *));
+ }
+ break;
+
+ case GIT_OPT_SET_CACHE_OBJECT_LIMIT:
+ {
+ git_object_t type = (git_object_t)va_arg(ap, int);
+ size_t size = va_arg(ap, size_t);
+ error = git_cache_set_max_object_size(type, size);
+ break;
+ }
+
+ case GIT_OPT_SET_CACHE_MAX_SIZE:
+ git_cache__max_storage = va_arg(ap, ssize_t);
+ break;
+
+ case GIT_OPT_ENABLE_CACHING:
+ git_cache__enabled = (va_arg(ap, int) != 0);
+ break;
+
+ case GIT_OPT_GET_CACHED_MEMORY:
+ *(va_arg(ap, ssize_t *)) = git_cache__current_storage.val;
+ *(va_arg(ap, ssize_t *)) = git_cache__max_storage;
+ break;
+
+ case GIT_OPT_GET_TEMPLATE_PATH:
+ {
+ git_buf *out = va_arg(ap, git_buf *);
+ git_str str = GIT_STR_INIT;
+ const git_str *tmp;
+
+ if ((error = git_buf_tostr(&str, out)) < 0 ||
+ (error = git_sysdir_get(&tmp, GIT_SYSDIR_TEMPLATE)) < 0 ||
+ (error = git_str_put(&str, tmp->ptr, tmp->size)) < 0)
+ break;
+
+ error = git_buf_fromstr(out, &str);
+ }
+ break;
+
+ case GIT_OPT_SET_TEMPLATE_PATH:
+ error = git_sysdir_set(GIT_SYSDIR_TEMPLATE, va_arg(ap, const char *));
+ break;
+
+ case GIT_OPT_SET_SSL_CERT_LOCATIONS:
+#ifdef GIT_OPENSSL
+ {
+ const char *file = va_arg(ap, const char *);
+ const char *path = va_arg(ap, const char *);
+ error = git_openssl__set_cert_location(file, path);
+ }
+#elif defined(GIT_MBEDTLS)
+ {
+ const char *file = va_arg(ap, const char *);
+ const char *path = va_arg(ap, const char *);
+ error = git_mbedtls__set_cert_location(file, path);
+ }
+#else
+ git_error_set(GIT_ERROR_SSL, "TLS backend doesn't support certificate locations");
+ error = -1;
+#endif
+ break;
+ case GIT_OPT_SET_USER_AGENT:
+ git__free(git__user_agent);
+ git__user_agent = git__strdup(va_arg(ap, const char *));
+ if (!git__user_agent) {
+ git_error_set_oom();
+ error = -1;
+ }
+
+ break;
+
+ case GIT_OPT_ENABLE_STRICT_OBJECT_CREATION:
+ git_object__strict_input_validation = (va_arg(ap, int) != 0);
+ break;
+
+ case GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION:
+ git_reference__enable_symbolic_ref_target_validation = (va_arg(ap, int) != 0);
+ break;
+
+ case GIT_OPT_SET_SSL_CIPHERS:
+#if (GIT_OPENSSL || GIT_MBEDTLS)
+ {
+ git__free(git__ssl_ciphers);
+ git__ssl_ciphers = git__strdup(va_arg(ap, const char *));
+ if (!git__ssl_ciphers) {
+ git_error_set_oom();
+ error = -1;
+ }
+ }
+#else
+ git_error_set(GIT_ERROR_SSL, "TLS backend doesn't support custom ciphers");
+ error = -1;
+#endif
+ break;
+
+ case GIT_OPT_GET_USER_AGENT:
+ {
+ git_buf *out = va_arg(ap, git_buf *);
+ git_str str = GIT_STR_INIT;
+
+ if ((error = git_buf_tostr(&str, out)) < 0 ||
+ (error = git_str_puts(&str, git__user_agent)) < 0)
+ break;
+
+ error = git_buf_fromstr(out, &str);
+ }
+ break;
+
+ case GIT_OPT_ENABLE_OFS_DELTA:
+ git_smart__ofs_delta_enabled = (va_arg(ap, int) != 0);
+ break;
+
+ case GIT_OPT_ENABLE_FSYNC_GITDIR:
+ git_repository__fsync_gitdir = (va_arg(ap, int) != 0);
+ break;
+
+ case GIT_OPT_GET_WINDOWS_SHAREMODE:
+#ifdef GIT_WIN32
+ *(va_arg(ap, unsigned long *)) = git_win32__createfile_sharemode;
+#endif
+ break;
+
+ case GIT_OPT_SET_WINDOWS_SHAREMODE:
+#ifdef GIT_WIN32
+ git_win32__createfile_sharemode = va_arg(ap, unsigned long);
+#endif
+ break;
+
+ case GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION:
+ git_odb__strict_hash_verification = (va_arg(ap, int) != 0);
+ break;
+
+ case GIT_OPT_SET_ALLOCATOR:
+ error = git_allocator_setup(va_arg(ap, git_allocator *));
+ break;
+
+ case GIT_OPT_ENABLE_UNSAVED_INDEX_SAFETY:
+ git_index__enforce_unsaved_safety = (va_arg(ap, int) != 0);
+ break;
+
+ case GIT_OPT_SET_PACK_MAX_OBJECTS:
+ git_indexer__max_objects = va_arg(ap, size_t);
+ break;
+
+ case GIT_OPT_GET_PACK_MAX_OBJECTS:
+ *(va_arg(ap, size_t *)) = git_indexer__max_objects;
+ break;
+
+ case GIT_OPT_DISABLE_PACK_KEEP_FILE_CHECKS:
+ git_disable_pack_keep_file_checks = (va_arg(ap, int) != 0);
+ break;
+
+ case GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE:
+ git_http__expect_continue = (va_arg(ap, int) != 0);
+ break;
+
+ case GIT_OPT_SET_ODB_PACKED_PRIORITY:
+ git_odb__packed_priority = va_arg(ap, int);
+ break;
+
+ case GIT_OPT_SET_ODB_LOOSE_PRIORITY:
+ git_odb__loose_priority = va_arg(ap, int);
+ break;
+
+ case GIT_OPT_SET_EXTENSIONS:
+ {
+ const char **extensions = va_arg(ap, const char **);
+ size_t len = va_arg(ap, size_t);
+ error = git_repository__set_extensions(extensions, len);
+ }
+ break;
+
+ case GIT_OPT_GET_EXTENSIONS:
+ {
+ git_strarray *out = va_arg(ap, git_strarray *);
+ char **extensions;
+ size_t len;
+
+ if ((error = git_repository__extensions(&extensions, &len)) < 0)
+ break;
+
+ out->strings = extensions;
+ out->count = len;
+ }
+ break;
+
+ case GIT_OPT_GET_OWNER_VALIDATION:
+ *(va_arg(ap, int *)) = git_repository__validate_ownership;
+ break;
+
+ case GIT_OPT_SET_OWNER_VALIDATION:
+ git_repository__validate_ownership = (va_arg(ap, int) != 0);
+ break;
+
+ case GIT_OPT_GET_HOMEDIR:
+ {
+ git_buf *out = va_arg(ap, git_buf *);
+ git_str str = GIT_STR_INIT;
+ const git_str *tmp;
+
+ if ((error = git_buf_tostr(&str, out)) < 0 ||
+ (error = git_sysdir_get(&tmp, GIT_SYSDIR_HOME)) < 0 ||
+ (error = git_str_put(&str, tmp->ptr, tmp->size)) < 0)
+ break;
+
+ error = git_buf_fromstr(out, &str);
+ }
+ break;
+
+ case GIT_OPT_SET_HOMEDIR:
+ error = git_sysdir_set(GIT_SYSDIR_HOME, va_arg(ap, const char *));
+ break;
+
+ case GIT_OPT_GET_SERVER_CONNECT_TIMEOUT:
+ *(va_arg(ap, int *)) = git_socket_stream__connect_timeout;
+ break;
+
+ case GIT_OPT_SET_SERVER_CONNECT_TIMEOUT:
+ {
+ int timeout = va_arg(ap, int);
+
+ if (timeout < 0) {
+ git_error_set(GIT_ERROR_INVALID, "invalid connect timeout");
+ error = -1;
+ } else {
+ git_socket_stream__connect_timeout = timeout;
+ }
+ }
+ break;
+
+ case GIT_OPT_GET_SERVER_TIMEOUT:
+ *(va_arg(ap, int *)) = git_socket_stream__timeout;
+ break;
+
+ case GIT_OPT_SET_SERVER_TIMEOUT:
+ {
+ int timeout = va_arg(ap, int);
+
+ if (timeout < 0) {
+ git_error_set(GIT_ERROR_INVALID, "invalid timeout");
+ error = -1;
+ } else {
+ git_socket_stream__timeout = timeout;
+ }
+ }
+ break;
+
+ default:
+ git_error_set(GIT_ERROR_INVALID, "invalid option key");
+ error = -1;
+ }
+
+ va_end(ap);
+
+ return error;
+}
diff --git a/src/libgit2/libgit2.h b/src/libgit2/libgit2.h
new file mode 100644
index 0000000..a898367
--- /dev/null
+++ b/src/libgit2/libgit2.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_libgit2_h__
+#define INCLUDE_libgit2_h__
+
+extern int git_libgit2_init_count(void);
+
+extern const char *git_libgit2__user_agent(void);
+extern const char *git_libgit2__ssl_ciphers(void);
+
+#endif
diff --git a/src/libgit2/mailmap.c b/src/libgit2/mailmap.c
new file mode 100644
index 0000000..4336fe3
--- /dev/null
+++ b/src/libgit2/mailmap.c
@@ -0,0 +1,500 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "mailmap.h"
+
+#include "common.h"
+#include "config.h"
+#include "fs_path.h"
+#include "repository.h"
+#include "signature.h"
+#include "git2/config.h"
+#include "git2/revparse.h"
+#include "blob.h"
+#include "parse.h"
+#include "path.h"
+
+#define MM_FILE ".mailmap"
+#define MM_FILE_CONFIG "mailmap.file"
+#define MM_BLOB_CONFIG "mailmap.blob"
+#define MM_BLOB_DEFAULT "HEAD:" MM_FILE
+
+static void mailmap_entry_free(git_mailmap_entry *entry)
+{
+ if (!entry)
+ return;
+
+ git__free(entry->real_name);
+ git__free(entry->real_email);
+ git__free(entry->replace_name);
+ git__free(entry->replace_email);
+ git__free(entry);
+}
+
+/*
+ * First we sort by replace_email, then replace_name (if present).
+ * Entries with names are greater than entries without.
+ */
+static int mailmap_entry_cmp(const void *a_raw, const void *b_raw)
+{
+ const git_mailmap_entry *a = (const git_mailmap_entry *)a_raw;
+ const git_mailmap_entry *b = (const git_mailmap_entry *)b_raw;
+ int cmp;
+
+ GIT_ASSERT_ARG(a && a->replace_email);
+ GIT_ASSERT_ARG(b && b->replace_email);
+
+ cmp = git__strcmp(a->replace_email, b->replace_email);
+ if (cmp)
+ return cmp;
+
+ /* NULL replace_names are less than not-NULL ones */
+ if (a->replace_name == NULL || b->replace_name == NULL)
+ return (int)(a->replace_name != NULL) - (int)(b->replace_name != NULL);
+
+ return git__strcmp(a->replace_name, b->replace_name);
+}
+
+/* Replace the old entry with the new on duplicate. */
+static int mailmap_entry_replace(void **old_raw, void *new_raw)
+{
+ mailmap_entry_free((git_mailmap_entry *)*old_raw);
+ *old_raw = new_raw;
+ return GIT_EEXISTS;
+}
+
+/* Check if we're at the end of line, w/ comments */
+static bool is_eol(git_parse_ctx *ctx)
+{
+ char c;
+ return git_parse_peek(&c, ctx, GIT_PARSE_PEEK_SKIP_WHITESPACE) < 0 || c == '#';
+}
+
+static int advance_until(
+ const char **start, size_t *len, git_parse_ctx *ctx, char needle)
+{
+ *start = ctx->line;
+ while (ctx->line_len > 0 && *ctx->line != '#' && *ctx->line != needle)
+ git_parse_advance_chars(ctx, 1);
+
+ if (ctx->line_len == 0 || *ctx->line == '#')
+ return -1; /* end of line */
+
+ *len = ctx->line - *start;
+ git_parse_advance_chars(ctx, 1); /* advance past needle */
+ return 0;
+}
+
+/*
+ * Parse a single entry from a mailmap file.
+ *
+ * The output git_strs will be non-owning, and should be copied before being
+ * persisted.
+ */
+static int parse_mailmap_entry(
+ git_str *real_name, git_str *real_email,
+ git_str *replace_name, git_str *replace_email,
+ git_parse_ctx *ctx)
+{
+ const char *start;
+ size_t len;
+
+ git_str_clear(real_name);
+ git_str_clear(real_email);
+ git_str_clear(replace_name);
+ git_str_clear(replace_email);
+
+ git_parse_advance_ws(ctx);
+ if (is_eol(ctx))
+ return -1; /* blank line */
+
+ /* Parse the real name */
+ if (advance_until(&start, &len, ctx, '<') < 0)
+ return -1;
+
+ git_str_attach_notowned(real_name, start, len);
+ git_str_rtrim(real_name);
+
+ /*
+ * If this is the last email in the line, this is the email to replace,
+ * otherwise, it's the real email.
+ */
+ if (advance_until(&start, &len, ctx, '>') < 0)
+ return -1;
+
+ /* If we aren't at the end of the line, parse a second name and email */
+ if (!is_eol(ctx)) {
+ git_str_attach_notowned(real_email, start, len);
+
+ git_parse_advance_ws(ctx);
+ if (advance_until(&start, &len, ctx, '<') < 0)
+ return -1;
+ git_str_attach_notowned(replace_name, start, len);
+ git_str_rtrim(replace_name);
+
+ if (advance_until(&start, &len, ctx, '>') < 0)
+ return -1;
+ }
+
+ git_str_attach_notowned(replace_email, start, len);
+
+ if (!is_eol(ctx))
+ return -1;
+
+ return 0;
+}
+
+int git_mailmap_new(git_mailmap **out)
+{
+ int error;
+ git_mailmap *mm = git__calloc(1, sizeof(git_mailmap));
+ GIT_ERROR_CHECK_ALLOC(mm);
+
+ error = git_vector_init(&mm->entries, 0, mailmap_entry_cmp);
+ if (error < 0) {
+ git__free(mm);
+ return error;
+ }
+ *out = mm;
+ return 0;
+}
+
+void git_mailmap_free(git_mailmap *mm)
+{
+ size_t idx;
+ git_mailmap_entry *entry;
+ if (!mm)
+ return;
+
+ git_vector_foreach(&mm->entries, idx, entry)
+ mailmap_entry_free(entry);
+
+ git_vector_free(&mm->entries);
+ git__free(mm);
+}
+
+static int mailmap_add_entry_unterminated(
+ git_mailmap *mm,
+ const char *real_name, size_t real_name_size,
+ const char *real_email, size_t real_email_size,
+ const char *replace_name, size_t replace_name_size,
+ const char *replace_email, size_t replace_email_size)
+{
+ int error;
+ git_mailmap_entry *entry = git__calloc(1, sizeof(git_mailmap_entry));
+ GIT_ERROR_CHECK_ALLOC(entry);
+
+ GIT_ASSERT_ARG(mm);
+ GIT_ASSERT_ARG(replace_email && *replace_email);
+
+ if (real_name_size > 0) {
+ entry->real_name = git__substrdup(real_name, real_name_size);
+ GIT_ERROR_CHECK_ALLOC(entry->real_name);
+ }
+ if (real_email_size > 0) {
+ entry->real_email = git__substrdup(real_email, real_email_size);
+ GIT_ERROR_CHECK_ALLOC(entry->real_email);
+ }
+ if (replace_name_size > 0) {
+ entry->replace_name = git__substrdup(replace_name, replace_name_size);
+ GIT_ERROR_CHECK_ALLOC(entry->replace_name);
+ }
+ entry->replace_email = git__substrdup(replace_email, replace_email_size);
+ GIT_ERROR_CHECK_ALLOC(entry->replace_email);
+
+ error = git_vector_insert_sorted(&mm->entries, entry, mailmap_entry_replace);
+ if (error == GIT_EEXISTS)
+ error = GIT_OK;
+ else if (error < 0)
+ mailmap_entry_free(entry);
+
+ return error;
+}
+
+int git_mailmap_add_entry(
+ git_mailmap *mm, const char *real_name, const char *real_email,
+ const char *replace_name, const char *replace_email)
+{
+ return mailmap_add_entry_unterminated(
+ mm,
+ real_name, real_name ? strlen(real_name) : 0,
+ real_email, real_email ? strlen(real_email) : 0,
+ replace_name, replace_name ? strlen(replace_name) : 0,
+ replace_email, strlen(replace_email));
+}
+
+static int mailmap_add_buffer(git_mailmap *mm, const char *buf, size_t len)
+{
+ int error = 0;
+ git_parse_ctx ctx;
+
+ /* Scratch buffers containing the real parsed names & emails */
+ git_str real_name = GIT_STR_INIT;
+ git_str real_email = GIT_STR_INIT;
+ git_str replace_name = GIT_STR_INIT;
+ git_str replace_email = GIT_STR_INIT;
+
+ /* Buffers may not contain '\0's. */
+ if (memchr(buf, '\0', len) != NULL)
+ return -1;
+
+ git_parse_ctx_init(&ctx, buf, len);
+
+ /* Run the parser */
+ while (ctx.remain_len > 0) {
+ error = parse_mailmap_entry(
+ &real_name, &real_email, &replace_name, &replace_email, &ctx);
+ if (error < 0) {
+ error = 0; /* Skip lines which don't contain a valid entry */
+ git_parse_advance_line(&ctx);
+ continue; /* TODO: warn */
+ }
+
+ /* NOTE: Can't use add_entry(...) as our buffers aren't terminated */
+ error = mailmap_add_entry_unterminated(
+ mm, real_name.ptr, real_name.size, real_email.ptr, real_email.size,
+ replace_name.ptr, replace_name.size, replace_email.ptr, replace_email.size);
+ if (error < 0)
+ goto cleanup;
+
+ error = 0;
+ }
+
+cleanup:
+ git_str_dispose(&real_name);
+ git_str_dispose(&real_email);
+ git_str_dispose(&replace_name);
+ git_str_dispose(&replace_email);
+ return error;
+}
+
+int git_mailmap_from_buffer(git_mailmap **out, const char *data, size_t len)
+{
+ int error = git_mailmap_new(out);
+ if (error < 0)
+ return error;
+
+ error = mailmap_add_buffer(*out, data, len);
+ if (error < 0) {
+ git_mailmap_free(*out);
+ *out = NULL;
+ }
+ return error;
+}
+
+static int mailmap_add_blob(
+ git_mailmap *mm, git_repository *repo, const char *rev)
+{
+ git_object *object = NULL;
+ git_blob *blob = NULL;
+ git_str content = GIT_STR_INIT;
+ int error;
+
+ GIT_ASSERT_ARG(mm);
+ GIT_ASSERT_ARG(repo);
+
+ error = git_revparse_single(&object, repo, rev);
+ if (error < 0)
+ goto cleanup;
+
+ error = git_object_peel((git_object **)&blob, object, GIT_OBJECT_BLOB);
+ if (error < 0)
+ goto cleanup;
+
+ error = git_blob__getbuf(&content, blob);
+ if (error < 0)
+ goto cleanup;
+
+ error = mailmap_add_buffer(mm, content.ptr, content.size);
+ if (error < 0)
+ goto cleanup;
+
+cleanup:
+ git_str_dispose(&content);
+ git_blob_free(blob);
+ git_object_free(object);
+ return error;
+}
+
+static int mailmap_add_file_ondisk(
+ git_mailmap *mm, const char *path, git_repository *repo)
+{
+ const char *base = repo ? git_repository_workdir(repo) : NULL;
+ git_str fullpath = GIT_STR_INIT;
+ git_str content = GIT_STR_INIT;
+ int error;
+
+ error = git_fs_path_join_unrooted(&fullpath, path, base, NULL);
+ if (error < 0)
+ goto cleanup;
+
+ error = git_path_validate_str_length(repo, &fullpath);
+ if (error < 0)
+ goto cleanup;
+
+ error = git_futils_readbuffer(&content, fullpath.ptr);
+ if (error < 0)
+ goto cleanup;
+
+ error = mailmap_add_buffer(mm, content.ptr, content.size);
+ if (error < 0)
+ goto cleanup;
+
+cleanup:
+ git_str_dispose(&fullpath);
+ git_str_dispose(&content);
+ return error;
+}
+
+/* NOTE: Only expose with an error return, currently never errors */
+static void mailmap_add_from_repository(git_mailmap *mm, git_repository *repo)
+{
+ git_config *config = NULL;
+ git_str rev_buf = GIT_STR_INIT;
+ git_str path_buf = GIT_STR_INIT;
+ const char *rev = NULL;
+ const char *path = NULL;
+
+ /* If we're in a bare repo, default blob to 'HEAD:.mailmap' */
+ if (repo->is_bare)
+ rev = MM_BLOB_DEFAULT;
+
+ /* Try to load 'mailmap.file' and 'mailmap.blob' cfgs from the repo */
+ if (git_repository_config(&config, repo) == 0) {
+ if (git_config__get_string_buf(&rev_buf, config, MM_BLOB_CONFIG) == 0)
+ rev = rev_buf.ptr;
+ if (git_config__get_path(&path_buf, config, MM_FILE_CONFIG) == 0)
+ path = path_buf.ptr;
+ }
+
+ /*
+ * Load mailmap files in order, overriding previous entries with new ones.
+ * 1. The '.mailmap' file in the repository's workdir root,
+ * 2. The blob described by the 'mailmap.blob' config (default HEAD:.mailmap),
+ * 3. The file described by the 'mailmap.file' config.
+ *
+ * We ignore errors from these loads, as these files may not exist, or may
+ * contain invalid information, and we don't want to report that error.
+ *
+ * XXX: Warn?
+ */
+ if (!repo->is_bare)
+ mailmap_add_file_ondisk(mm, MM_FILE, repo);
+ if (rev != NULL)
+ mailmap_add_blob(mm, repo, rev);
+ if (path != NULL)
+ mailmap_add_file_ondisk(mm, path, repo);
+
+ git_str_dispose(&rev_buf);
+ git_str_dispose(&path_buf);
+ git_config_free(config);
+}
+
+int git_mailmap_from_repository(git_mailmap **out, git_repository *repo)
+{
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+
+ if ((error = git_mailmap_new(out)) < 0)
+ return error;
+
+ mailmap_add_from_repository(*out, repo);
+ return 0;
+}
+
+const git_mailmap_entry *git_mailmap_entry_lookup(
+ const git_mailmap *mm, const char *name, const char *email)
+{
+ int error;
+ ssize_t fallback = -1;
+ size_t idx;
+ git_mailmap_entry *entry;
+
+ /* The lookup needle we want to use only sets the replace_email. */
+ git_mailmap_entry needle = { NULL };
+ needle.replace_email = (char *)email;
+
+ GIT_ASSERT_ARG_WITH_RETVAL(email, NULL);
+
+ if (!mm)
+ return NULL;
+
+ /*
+ * We want to find the place to start looking. so we do a binary search for
+ * the "fallback" nameless entry. If we find it, we advance past it and record
+ * the index.
+ */
+ error = git_vector_bsearch(&idx, (git_vector *)&mm->entries, &needle);
+ if (error >= 0)
+ fallback = idx++;
+ else if (error != GIT_ENOTFOUND)
+ return NULL;
+
+ /* do a linear search for an exact match */
+ for (; idx < git_vector_length(&mm->entries); ++idx) {
+ entry = git_vector_get(&mm->entries, idx);
+
+ if (git__strcmp(entry->replace_email, email))
+ break; /* it's a different email, so we're done looking */
+
+ /* should be specific */
+ GIT_ASSERT_WITH_RETVAL(entry->replace_name, NULL);
+ if (!name || !git__strcmp(entry->replace_name, name))
+ return entry;
+ }
+
+ if (fallback < 0)
+ return NULL; /* no fallback */
+ return git_vector_get(&mm->entries, fallback);
+}
+
+int git_mailmap_resolve(
+ const char **real_name, const char **real_email,
+ const git_mailmap *mailmap,
+ const char *name, const char *email)
+{
+ const git_mailmap_entry *entry = NULL;
+
+ GIT_ASSERT(name);
+ GIT_ASSERT(email);
+
+ *real_name = name;
+ *real_email = email;
+
+ if ((entry = git_mailmap_entry_lookup(mailmap, name, email))) {
+ if (entry->real_name)
+ *real_name = entry->real_name;
+ if (entry->real_email)
+ *real_email = entry->real_email;
+ }
+ return 0;
+}
+
+int git_mailmap_resolve_signature(
+ git_signature **out, const git_mailmap *mailmap, const git_signature *sig)
+{
+ const char *name = NULL;
+ const char *email = NULL;
+ int error;
+
+ if (!sig)
+ return 0;
+
+ error = git_mailmap_resolve(&name, &email, mailmap, sig->name, sig->email);
+ if (error < 0)
+ return error;
+
+ error = git_signature_new(out, name, email, sig->when.time, sig->when.offset);
+ if (error < 0)
+ return error;
+
+ /* Copy over the sign, as git_signature_new doesn't let you pass it. */
+ (*out)->when.sign = sig->when.sign;
+ return 0;
+}
diff --git a/src/libgit2/mailmap.h b/src/libgit2/mailmap.h
new file mode 100644
index 0000000..2c9736a
--- /dev/null
+++ b/src/libgit2/mailmap.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_mailmap_h__
+#define INCLUDE_mailmap_h__
+
+#include "git2/mailmap.h"
+#include "vector.h"
+
+/*
+ * A mailmap is stored as a sorted vector of 'git_mailmap_entry's. These entries
+ * are sorted first by 'replace_email', and then by 'replace_name'. NULL
+ * replace_names are ordered first.
+ *
+ * Looking up a name and email in the mailmap is done with a binary search.
+ */
+struct git_mailmap {
+ git_vector entries;
+};
+
+/* Single entry parsed from a mailmap */
+typedef struct git_mailmap_entry {
+ char *real_name; /**< the real name (may be NULL) */
+ char *real_email; /**< the real email (may be NULL) */
+ char *replace_name; /**< the name to replace (may be NULL) */
+ char *replace_email; /**< the email to replace */
+} git_mailmap_entry;
+
+const git_mailmap_entry *git_mailmap_entry_lookup(
+ const git_mailmap *mm, const char *name, const char *email);
+
+#endif
diff --git a/src/libgit2/merge.c b/src/libgit2/merge.c
new file mode 100644
index 0000000..0114e4b
--- /dev/null
+++ b/src/libgit2/merge.c
@@ -0,0 +1,3440 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "merge.h"
+
+#include "posix.h"
+#include "str.h"
+#include "repository.h"
+#include "revwalk.h"
+#include "commit_list.h"
+#include "fs_path.h"
+#include "refs.h"
+#include "object.h"
+#include "iterator.h"
+#include "refs.h"
+#include "diff.h"
+#include "diff_generate.h"
+#include "diff_tform.h"
+#include "checkout.h"
+#include "tree.h"
+#include "blob.h"
+#include "oid.h"
+#include "index.h"
+#include "filebuf.h"
+#include "config.h"
+#include "oidarray.h"
+#include "annotated_commit.h"
+#include "commit.h"
+#include "oidarray.h"
+#include "merge_driver.h"
+#include "oidmap.h"
+#include "array.h"
+
+#include "git2/types.h"
+#include "git2/repository.h"
+#include "git2/object.h"
+#include "git2/commit.h"
+#include "git2/merge.h"
+#include "git2/refs.h"
+#include "git2/reset.h"
+#include "git2/checkout.h"
+#include "git2/signature.h"
+#include "git2/config.h"
+#include "git2/tree.h"
+#include "git2/oidarray.h"
+#include "git2/annotated_commit.h"
+#include "git2/sys/index.h"
+#include "git2/sys/hashsig.h"
+
+#define GIT_MERGE_INDEX_ENTRY_EXISTS(X) ((X).mode != 0)
+#define GIT_MERGE_INDEX_ENTRY_ISFILE(X) S_ISREG((X).mode)
+
+
+typedef enum {
+ TREE_IDX_ANCESTOR = 0,
+ TREE_IDX_OURS = 1,
+ TREE_IDX_THEIRS = 2
+} merge_tree_index_t;
+
+/* Tracks D/F conflicts */
+struct merge_diff_df_data {
+ const char *df_path;
+ const char *prev_path;
+ git_merge_diff *prev_conflict;
+};
+
+/*
+ * This acts as a negative cache entry marker. In case we've tried to calculate
+ * similarity metrics for a given blob already but `git_hashsig` determined
+ * that it's too small in order to have a meaningful hash signature, we will
+ * insert the address of this marker instead of `NULL`. Like this, we can
+ * easily check whether we have checked a gien entry already and skip doing the
+ * calculation again and again.
+ */
+static int cache_invalid_marker;
+
+/* Merge base computation */
+
+static int merge_bases_many(git_commit_list **out, git_revwalk **walk_out, git_repository *repo, size_t length, const git_oid input_array[])
+{
+ git_revwalk *walk = NULL;
+ git_vector list;
+ git_commit_list *result = NULL;
+ git_commit_list_node *commit;
+ int error = -1;
+ unsigned int i;
+
+ if (length < 2) {
+ git_error_set(GIT_ERROR_INVALID, "at least two commits are required to find an ancestor");
+ return -1;
+ }
+
+ if (git_vector_init(&list, length - 1, NULL) < 0)
+ return -1;
+
+ if (git_revwalk_new(&walk, repo) < 0)
+ goto on_error;
+
+ for (i = 1; i < length; i++) {
+ commit = git_revwalk__commit_lookup(walk, &input_array[i]);
+ if (commit == NULL)
+ goto on_error;
+
+ git_vector_insert(&list, commit);
+ }
+
+ commit = git_revwalk__commit_lookup(walk, &input_array[0]);
+ if (commit == NULL)
+ goto on_error;
+
+ if (git_merge__bases_many(&result, walk, commit, &list, 0) < 0)
+ goto on_error;
+
+ if (!result) {
+ git_error_set(GIT_ERROR_MERGE, "no merge base found");
+ error = GIT_ENOTFOUND;
+ goto on_error;
+ }
+
+ *out = result;
+ *walk_out = walk;
+
+ git_vector_free(&list);
+ return 0;
+
+on_error:
+ git_vector_free(&list);
+ git_revwalk_free(walk);
+ return error;
+}
+
+int git_merge_base_many(git_oid *out, git_repository *repo, size_t length, const git_oid input_array[])
+{
+ git_revwalk *walk;
+ git_commit_list *result = NULL;
+ int error = 0;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(input_array);
+
+ if ((error = merge_bases_many(&result, &walk, repo, length, input_array)) < 0)
+ return error;
+
+ git_oid_cpy(out, &result->item->oid);
+
+ git_commit_list_free(&result);
+ git_revwalk_free(walk);
+
+ return 0;
+}
+
+int git_merge_bases_many(git_oidarray *out, git_repository *repo, size_t length, const git_oid input_array[])
+{
+ git_revwalk *walk;
+ git_commit_list *list, *result = NULL;
+ int error = 0;
+ git_array_oid_t array;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(input_array);
+
+ if ((error = merge_bases_many(&result, &walk, repo, length, input_array)) < 0)
+ return error;
+
+ git_array_init(array);
+
+ list = result;
+ while (list) {
+ git_oid *id = git_array_alloc(array);
+ if (id == NULL) {
+ error = -1;
+ goto cleanup;
+ }
+
+ git_oid_cpy(id, &list->item->oid);
+ list = list->next;
+ }
+
+ git_oidarray__from_array(out, &array);
+
+cleanup:
+ git_commit_list_free(&result);
+ git_revwalk_free(walk);
+
+ return error;
+}
+
+int git_merge_base_octopus(git_oid *out, git_repository *repo, size_t length, const git_oid input_array[])
+{
+ git_oid result;
+ unsigned int i;
+ int error = -1;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(input_array);
+
+ if (length < 2) {
+ git_error_set(GIT_ERROR_INVALID, "at least two commits are required to find an ancestor");
+ return -1;
+ }
+
+ result = input_array[0];
+ for (i = 1; i < length; i++) {
+ error = git_merge_base(&result, repo, &result, &input_array[i]);
+ if (error < 0)
+ return error;
+ }
+
+ *out = result;
+
+ return 0;
+}
+
+static int merge_bases(git_commit_list **out, git_revwalk **walk_out, git_repository *repo, const git_oid *one, const git_oid *two)
+{
+ git_revwalk *walk;
+ git_vector list;
+ git_commit_list *result = NULL;
+ git_commit_list_node *commit;
+ void *contents[1];
+
+ if (git_revwalk_new(&walk, repo) < 0)
+ return -1;
+
+ commit = git_revwalk__commit_lookup(walk, two);
+ if (commit == NULL)
+ goto on_error;
+
+ /* This is just one value, so we can do it on the stack */
+ memset(&list, 0x0, sizeof(git_vector));
+ contents[0] = commit;
+ list.length = 1;
+ list.contents = contents;
+
+ commit = git_revwalk__commit_lookup(walk, one);
+ if (commit == NULL)
+ goto on_error;
+
+ if (git_merge__bases_many(&result, walk, commit, &list, 0) < 0)
+ goto on_error;
+
+ if (!result) {
+ git_revwalk_free(walk);
+ git_error_set(GIT_ERROR_MERGE, "no merge base found");
+ return GIT_ENOTFOUND;
+ }
+
+ *out = result;
+ *walk_out = walk;
+
+ return 0;
+
+on_error:
+ git_revwalk_free(walk);
+ return -1;
+
+}
+
+int git_merge_base(git_oid *out, git_repository *repo, const git_oid *one, const git_oid *two)
+{
+ int error;
+ git_revwalk *walk;
+ git_commit_list *result;
+
+ if ((error = merge_bases(&result, &walk, repo, one, two)) < 0)
+ return error;
+
+ git_oid_cpy(out, &result->item->oid);
+ git_commit_list_free(&result);
+ git_revwalk_free(walk);
+
+ return 0;
+}
+
+int git_merge_bases(git_oidarray *out, git_repository *repo, const git_oid *one, const git_oid *two)
+{
+ int error;
+ git_revwalk *walk;
+ git_commit_list *result, *list;
+ git_array_oid_t array;
+
+ git_array_init(array);
+
+ if ((error = merge_bases(&result, &walk, repo, one, two)) < 0)
+ return error;
+
+ list = result;
+ while (list) {
+ git_oid *id = git_array_alloc(array);
+ if (id == NULL)
+ goto on_error;
+
+ git_oid_cpy(id, &list->item->oid);
+ list = list->next;
+ }
+
+ git_oidarray__from_array(out, &array);
+ git_commit_list_free(&result);
+ git_revwalk_free(walk);
+
+ return 0;
+
+on_error:
+ git_commit_list_free(&result);
+ git_revwalk_free(walk);
+ return -1;
+}
+
+static int interesting(git_pqueue *list)
+{
+ size_t i;
+
+ for (i = 0; i < git_pqueue_size(list); i++) {
+ git_commit_list_node *commit = git_pqueue_get(list, i);
+ if ((commit->flags & STALE) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
+static int clear_commit_marks_1(git_commit_list **plist,
+ git_commit_list_node *commit, unsigned int mark)
+{
+ while (commit) {
+ unsigned int i;
+
+ if (!(mark & commit->flags))
+ return 0;
+
+ commit->flags &= ~mark;
+
+ for (i = 1; i < commit->out_degree; i++) {
+ git_commit_list_node *p = commit->parents[i];
+ if (git_commit_list_insert(p, plist) == NULL)
+ return -1;
+ }
+
+ commit = commit->out_degree ? commit->parents[0] : NULL;
+ }
+
+ return 0;
+}
+
+static int clear_commit_marks_many(git_vector *commits, unsigned int mark)
+{
+ git_commit_list *list = NULL;
+ git_commit_list_node *c;
+ unsigned int i;
+
+ git_vector_foreach(commits, i, c) {
+ if (git_commit_list_insert(c, &list) == NULL)
+ return -1;
+ }
+
+ while (list)
+ if (clear_commit_marks_1(&list, git_commit_list_pop(&list), mark) < 0)
+ return -1;
+ return 0;
+}
+
+static int clear_commit_marks(git_commit_list_node *commit, unsigned int mark)
+{
+ git_commit_list *list = NULL;
+ if (git_commit_list_insert(commit, &list) == NULL)
+ return -1;
+ while (list)
+ if (clear_commit_marks_1(&list, git_commit_list_pop(&list), mark) < 0)
+ return -1;
+ return 0;
+}
+
+static int paint_down_to_common(
+ git_commit_list **out,
+ git_revwalk *walk,
+ git_commit_list_node *one,
+ git_vector *twos,
+ uint32_t minimum_generation)
+{
+ git_pqueue list;
+ git_commit_list *result = NULL;
+ git_commit_list_node *two;
+
+ int error;
+ unsigned int i;
+
+ if (git_pqueue_init(&list, 0, twos->length * 2, git_commit_list_generation_cmp) < 0)
+ return -1;
+
+ one->flags |= PARENT1;
+ if (git_pqueue_insert(&list, one) < 0)
+ return -1;
+
+ git_vector_foreach(twos, i, two) {
+ if (git_commit_list_parse(walk, two) < 0)
+ return -1;
+
+ two->flags |= PARENT2;
+ if (git_pqueue_insert(&list, two) < 0)
+ return -1;
+ }
+
+ /* as long as there are non-STALE commits */
+ while (interesting(&list)) {
+ git_commit_list_node *commit = git_pqueue_pop(&list);
+ int flags;
+
+ if (commit == NULL)
+ break;
+
+ flags = commit->flags & (PARENT1 | PARENT2 | STALE);
+ if (flags == (PARENT1 | PARENT2)) {
+ if (!(commit->flags & RESULT)) {
+ commit->flags |= RESULT;
+ if (git_commit_list_insert(commit, &result) == NULL)
+ return -1;
+ }
+ /* we mark the parents of a merge stale */
+ flags |= STALE;
+ }
+
+ for (i = 0; i < commit->out_degree; i++) {
+ git_commit_list_node *p = commit->parents[i];
+ if ((p->flags & flags) == flags)
+ continue;
+ if (p->generation < minimum_generation)
+ continue;
+
+ if ((error = git_commit_list_parse(walk, p)) < 0)
+ return error;
+
+ p->flags |= flags;
+ if (git_pqueue_insert(&list, p) < 0)
+ return -1;
+ }
+ }
+
+ git_pqueue_free(&list);
+ *out = result;
+ return 0;
+}
+
+static int remove_redundant(git_revwalk *walk, git_vector *commits, uint32_t minimum_generation)
+{
+ git_vector work = GIT_VECTOR_INIT;
+ unsigned char *redundant;
+ unsigned int *filled_index;
+ unsigned int i, j;
+ int error = 0;
+
+ redundant = git__calloc(commits->length, 1);
+ GIT_ERROR_CHECK_ALLOC(redundant);
+ filled_index = git__calloc((commits->length - 1), sizeof(unsigned int));
+ GIT_ERROR_CHECK_ALLOC(filled_index);
+
+ for (i = 0; i < commits->length; ++i) {
+ if ((error = git_commit_list_parse(walk, commits->contents[i])) < 0)
+ goto done;
+ }
+
+ for (i = 0; i < commits->length; ++i) {
+ git_commit_list *common = NULL;
+ git_commit_list_node *commit = commits->contents[i];
+
+ if (redundant[i])
+ continue;
+
+ git_vector_clear(&work);
+
+ for (j = 0; j < commits->length; j++) {
+ if (i == j || redundant[j])
+ continue;
+
+ filled_index[work.length] = j;
+ if ((error = git_vector_insert(&work, commits->contents[j])) < 0)
+ goto done;
+ }
+
+ error = paint_down_to_common(&common, walk, commit, &work, minimum_generation);
+ if (error < 0)
+ goto done;
+
+ if (commit->flags & PARENT2)
+ redundant[i] = 1;
+
+ for (j = 0; j < work.length; j++) {
+ git_commit_list_node *w = work.contents[j];
+ if (w->flags & PARENT1)
+ redundant[filled_index[j]] = 1;
+ }
+
+ git_commit_list_free(&common);
+
+ if ((error = clear_commit_marks(commit, ALL_FLAGS)) < 0 ||
+ (error = clear_commit_marks_many(&work, ALL_FLAGS)) < 0)
+ goto done;
+ }
+
+ for (i = 0; i < commits->length; ++i) {
+ if (redundant[i])
+ commits->contents[i] = NULL;
+ }
+
+done:
+ git__free(redundant);
+ git__free(filled_index);
+ git_vector_free(&work);
+ return error;
+}
+
+int git_merge__bases_many(
+ git_commit_list **out,
+ git_revwalk *walk,
+ git_commit_list_node *one,
+ git_vector *twos,
+ uint32_t minimum_generation)
+{
+ int error;
+ unsigned int i;
+ git_commit_list_node *two;
+ git_commit_list *result = NULL, *tmp = NULL;
+
+ /* If there's only the one commit, there can be no merge bases */
+ if (twos->length == 0) {
+ *out = NULL;
+ return 0;
+ }
+
+ /* if the commit is repeated, we have a our merge base already */
+ git_vector_foreach(twos, i, two) {
+ if (one == two)
+ return git_commit_list_insert(one, out) ? 0 : -1;
+ }
+
+ if (git_commit_list_parse(walk, one) < 0)
+ return -1;
+
+ error = paint_down_to_common(&result, walk, one, twos, minimum_generation);
+ if (error < 0)
+ return error;
+
+ /* filter out any stale commits in the results */
+ tmp = result;
+ result = NULL;
+
+ while (tmp) {
+ git_commit_list_node *c = git_commit_list_pop(&tmp);
+ if (!(c->flags & STALE))
+ if (git_commit_list_insert_by_date(c, &result) == NULL)
+ return -1;
+ }
+
+ /*
+ * more than one merge base -- see if there are redundant merge
+ * bases and remove them
+ */
+ if (result && result->next) {
+ git_vector redundant = GIT_VECTOR_INIT;
+
+ while (result)
+ git_vector_insert(&redundant, git_commit_list_pop(&result));
+
+ if ((error = clear_commit_marks(one, ALL_FLAGS)) < 0 ||
+ (error = clear_commit_marks_many(twos, ALL_FLAGS)) < 0 ||
+ (error = remove_redundant(walk, &redundant, minimum_generation)) < 0) {
+ git_vector_free(&redundant);
+ return error;
+ }
+
+ git_vector_foreach(&redundant, i, two) {
+ if (two != NULL)
+ git_commit_list_insert_by_date(two, &result);
+ }
+
+ git_vector_free(&redundant);
+ }
+
+ *out = result;
+ return 0;
+}
+
+int git_repository_mergehead_foreach(
+ git_repository *repo,
+ git_repository_mergehead_foreach_cb cb,
+ void *payload)
+{
+ git_str merge_head_path = GIT_STR_INIT, merge_head_file = GIT_STR_INIT;
+ char *buffer, *line;
+ size_t line_num = 1;
+ git_oid oid;
+ int error = 0;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(cb);
+
+ if ((error = git_str_joinpath(&merge_head_path, repo->gitdir,
+ GIT_MERGE_HEAD_FILE)) < 0)
+ return error;
+
+ if ((error = git_futils_readbuffer(&merge_head_file,
+ git_str_cstr(&merge_head_path))) < 0)
+ goto cleanup;
+
+ buffer = merge_head_file.ptr;
+
+ while ((line = git__strsep(&buffer, "\n")) != NULL) {
+ if (strlen(line) != git_oid_hexsize(repo->oid_type)) {
+ git_error_set(GIT_ERROR_INVALID, "unable to parse OID - invalid length");
+ error = -1;
+ goto cleanup;
+ }
+
+ if ((error = git_oid__fromstr(&oid, line, repo->oid_type)) < 0)
+ goto cleanup;
+
+ if ((error = cb(&oid, payload)) != 0) {
+ git_error_set_after_callback(error);
+ goto cleanup;
+ }
+
+ ++line_num;
+ }
+
+ if (*buffer) {
+ git_error_set(GIT_ERROR_MERGE, "no EOL at line %"PRIuZ, line_num);
+ error = -1;
+ goto cleanup;
+ }
+
+cleanup:
+ git_str_dispose(&merge_head_path);
+ git_str_dispose(&merge_head_file);
+
+ return error;
+}
+
+GIT_INLINE(int) index_entry_cmp(const git_index_entry *a, const git_index_entry *b)
+{
+ int value = 0;
+
+ if (a->path == NULL)
+ return (b->path == NULL) ? 0 : 1;
+
+ if ((value = a->mode - b->mode) == 0 &&
+ (value = git_oid__cmp(&a->id, &b->id)) == 0)
+ value = strcmp(a->path, b->path);
+
+ return value;
+}
+
+/* Conflict resolution */
+
+static int merge_conflict_resolve_trivial(
+ int *resolved,
+ git_merge_diff_list *diff_list,
+ const git_merge_diff *conflict)
+{
+ int ours_empty, theirs_empty;
+ int ours_changed, theirs_changed, ours_theirs_differ;
+ git_index_entry const *result = NULL;
+ int error = 0;
+
+ GIT_ASSERT_ARG(resolved);
+ GIT_ASSERT_ARG(diff_list);
+ GIT_ASSERT_ARG(conflict);
+
+ *resolved = 0;
+
+ if (conflict->type == GIT_MERGE_DIFF_DIRECTORY_FILE ||
+ conflict->type == GIT_MERGE_DIFF_RENAMED_ADDED)
+ return 0;
+
+ if (conflict->our_status == GIT_DELTA_RENAMED ||
+ conflict->their_status == GIT_DELTA_RENAMED)
+ return 0;
+
+ ours_empty = !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry);
+ theirs_empty = !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry);
+
+ ours_changed = (conflict->our_status != GIT_DELTA_UNMODIFIED);
+ theirs_changed = (conflict->their_status != GIT_DELTA_UNMODIFIED);
+ ours_theirs_differ = ours_changed && theirs_changed &&
+ index_entry_cmp(&conflict->our_entry, &conflict->their_entry);
+
+ /*
+ * Note: with only one ancestor, some cases are not distinct:
+ *
+ * 16: ancest:anc1/anc2, head:anc1, remote:anc2 = result:no merge
+ * 3: ancest:(empty)^, head:head, remote:(empty) = result:no merge
+ * 2: ancest:(empty)^, head:(empty), remote:remote = result:no merge
+ *
+ * Note that the two cases that take D/F conflicts into account
+ * specifically do not need to be explicitly tested, as D/F conflicts
+ * would fail the *empty* test:
+ *
+ * 3ALT: ancest:(empty)+, head:head, remote:*empty* = result:head
+ * 2ALT: ancest:(empty)+, head:*empty*, remote:remote = result:remote
+ *
+ * Note that many of these cases need not be explicitly tested, as
+ * they simply degrade to "all different" cases (eg, 11):
+ *
+ * 4: ancest:(empty)^, head:head, remote:remote = result:no merge
+ * 7: ancest:ancest+, head:(empty), remote:remote = result:no merge
+ * 9: ancest:ancest+, head:head, remote:(empty) = result:no merge
+ * 11: ancest:ancest+, head:head, remote:remote = result:no merge
+ */
+
+ /* 5ALT: ancest:*, head:head, remote:head = result:head */
+ if (ours_changed && !ours_empty && !ours_theirs_differ)
+ result = &conflict->our_entry;
+ /* 6: ancest:ancest+, head:(empty), remote:(empty) = result:no merge */
+ else if (ours_changed && ours_empty && theirs_empty)
+ *resolved = 0;
+ /* 8: ancest:ancest^, head:(empty), remote:ancest = result:no merge */
+ else if (ours_empty && !theirs_changed)
+ *resolved = 0;
+ /* 10: ancest:ancest^, head:ancest, remote:(empty) = result:no merge */
+ else if (!ours_changed && theirs_empty)
+ *resolved = 0;
+ /* 13: ancest:ancest+, head:head, remote:ancest = result:head */
+ else if (ours_changed && !theirs_changed)
+ result = &conflict->our_entry;
+ /* 14: ancest:ancest+, head:ancest, remote:remote = result:remote */
+ else if (!ours_changed && theirs_changed)
+ result = &conflict->their_entry;
+ else
+ *resolved = 0;
+
+ if (result != NULL &&
+ GIT_MERGE_INDEX_ENTRY_EXISTS(*result) &&
+ (error = git_vector_insert(&diff_list->staged, (void *)result)) >= 0)
+ *resolved = 1;
+
+ /* Note: trivial resolution does not update the REUC. */
+
+ return error;
+}
+
+static int merge_conflict_resolve_one_removed(
+ int *resolved,
+ git_merge_diff_list *diff_list,
+ const git_merge_diff *conflict)
+{
+ int ours_empty, theirs_empty;
+ int ours_changed, theirs_changed;
+ int error = 0;
+
+ GIT_ASSERT_ARG(resolved);
+ GIT_ASSERT_ARG(diff_list);
+ GIT_ASSERT_ARG(conflict);
+
+ *resolved = 0;
+
+ if (conflict->type == GIT_MERGE_DIFF_DIRECTORY_FILE ||
+ conflict->type == GIT_MERGE_DIFF_RENAMED_ADDED)
+ return 0;
+
+ ours_empty = !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry);
+ theirs_empty = !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry);
+
+ ours_changed = (conflict->our_status != GIT_DELTA_UNMODIFIED);
+ theirs_changed = (conflict->their_status != GIT_DELTA_UNMODIFIED);
+
+ /* Removed in both */
+ if (ours_changed && ours_empty && theirs_empty)
+ *resolved = 1;
+ /* Removed in ours */
+ else if (ours_empty && !theirs_changed)
+ *resolved = 1;
+ /* Removed in theirs */
+ else if (!ours_changed && theirs_empty)
+ *resolved = 1;
+
+ if (*resolved)
+ git_vector_insert(&diff_list->resolved, (git_merge_diff *)conflict);
+
+ return error;
+}
+
+static int merge_conflict_resolve_one_renamed(
+ int *resolved,
+ git_merge_diff_list *diff_list,
+ const git_merge_diff *conflict)
+{
+ int ours_renamed, theirs_renamed;
+ int ours_changed, theirs_changed;
+ git_index_entry *merged;
+ int error = 0;
+
+ GIT_ASSERT_ARG(resolved);
+ GIT_ASSERT_ARG(diff_list);
+ GIT_ASSERT_ARG(conflict);
+
+ *resolved = 0;
+
+ if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ||
+ !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry))
+ return 0;
+
+ ours_renamed = (conflict->our_status == GIT_DELTA_RENAMED);
+ theirs_renamed = (conflict->their_status == GIT_DELTA_RENAMED);
+
+ if (!ours_renamed && !theirs_renamed)
+ return 0;
+
+ /* Reject one file in a 2->1 conflict */
+ if (conflict->type == GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1 ||
+ conflict->type == GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2 ||
+ conflict->type == GIT_MERGE_DIFF_RENAMED_ADDED)
+ return 0;
+
+ ours_changed = (git_oid__cmp(&conflict->ancestor_entry.id, &conflict->our_entry.id) != 0) ||
+ (conflict->ancestor_entry.mode != conflict->our_entry.mode);
+
+ theirs_changed = (git_oid__cmp(&conflict->ancestor_entry.id, &conflict->their_entry.id) != 0) ||
+ (conflict->ancestor_entry.mode != conflict->their_entry.mode);
+
+ /* if both are modified (and not to a common target) require a merge */
+ if (ours_changed && theirs_changed &&
+ git_oid__cmp(&conflict->our_entry.id, &conflict->their_entry.id) != 0)
+ return 0;
+
+ if ((merged = git_pool_malloc(&diff_list->pool, sizeof(git_index_entry))) == NULL)
+ return -1;
+
+ if (ours_changed)
+ memcpy(merged, &conflict->our_entry, sizeof(git_index_entry));
+ else
+ memcpy(merged, &conflict->their_entry, sizeof(git_index_entry));
+
+ if (ours_renamed)
+ merged->path = conflict->our_entry.path;
+ else
+ merged->path = conflict->their_entry.path;
+
+ *resolved = 1;
+
+ git_vector_insert(&diff_list->staged, merged);
+ git_vector_insert(&diff_list->resolved, (git_merge_diff *)conflict);
+
+ return error;
+}
+
+static bool merge_conflict_can_resolve_contents(
+ const git_merge_diff *conflict)
+{
+ if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ||
+ !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry))
+ return false;
+
+ /* Reject D/F conflicts */
+ if (conflict->type == GIT_MERGE_DIFF_DIRECTORY_FILE)
+ return false;
+
+ /* Reject submodules. */
+ if (S_ISGITLINK(conflict->ancestor_entry.mode) ||
+ S_ISGITLINK(conflict->our_entry.mode) ||
+ S_ISGITLINK(conflict->their_entry.mode))
+ return false;
+
+ /* Reject link/file conflicts. */
+ if ((S_ISLNK(conflict->ancestor_entry.mode) ^
+ S_ISLNK(conflict->our_entry.mode)) ||
+ (S_ISLNK(conflict->ancestor_entry.mode) ^
+ S_ISLNK(conflict->their_entry.mode)))
+ return false;
+
+ /* Reject name conflicts */
+ if (conflict->type == GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1 ||
+ conflict->type == GIT_MERGE_DIFF_RENAMED_ADDED)
+ return false;
+
+ if ((conflict->our_status & GIT_DELTA_RENAMED) == GIT_DELTA_RENAMED &&
+ (conflict->their_status & GIT_DELTA_RENAMED) == GIT_DELTA_RENAMED &&
+ strcmp(conflict->ancestor_entry.path, conflict->their_entry.path) != 0)
+ return false;
+
+ return true;
+}
+
+static int merge_conflict_invoke_driver(
+ git_index_entry **out,
+ const char *name,
+ git_merge_driver *driver,
+ git_merge_diff_list *diff_list,
+ git_merge_driver_source *src)
+{
+ git_index_entry *result;
+ git_buf buf = {0};
+ const char *path;
+ uint32_t mode;
+ git_odb *odb = NULL;
+ git_oid oid;
+ int error;
+
+ *out = NULL;
+
+ if ((error = driver->apply(driver, &path, &mode, &buf, name, src)) < 0 ||
+ (error = git_repository_odb(&odb, src->repo)) < 0 ||
+ (error = git_odb_write(&oid, odb, buf.ptr, buf.size, GIT_OBJECT_BLOB)) < 0)
+ goto done;
+
+ result = git_pool_mallocz(&diff_list->pool, sizeof(git_index_entry));
+ GIT_ERROR_CHECK_ALLOC(result);
+
+ git_oid_cpy(&result->id, &oid);
+ result->mode = mode;
+ result->file_size = (uint32_t)buf.size;
+
+ result->path = git_pool_strdup(&diff_list->pool, path);
+ GIT_ERROR_CHECK_ALLOC(result->path);
+
+ *out = result;
+
+done:
+ git_buf_dispose(&buf);
+ git_odb_free(odb);
+
+ return error;
+}
+
+static int merge_conflict_resolve_contents(
+ int *resolved,
+ git_merge_diff_list *diff_list,
+ const git_merge_diff *conflict,
+ const git_merge_options *merge_opts,
+ const git_merge_file_options *file_opts)
+{
+ git_merge_driver_source source = {0};
+ git_merge_file_result result = {0};
+ git_merge_driver *driver;
+ git_merge_driver__builtin builtin = {{0}};
+ git_index_entry *merge_result;
+ git_odb *odb = NULL;
+ const char *name;
+ bool fallback = false;
+ int error;
+
+ GIT_ASSERT_ARG(resolved);
+ GIT_ASSERT_ARG(diff_list);
+ GIT_ASSERT_ARG(conflict);
+
+ *resolved = 0;
+
+ if (!merge_conflict_can_resolve_contents(conflict))
+ return 0;
+
+ source.repo = diff_list->repo;
+ source.default_driver = merge_opts->default_driver;
+ source.file_opts = file_opts;
+ source.ancestor = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) ?
+ &conflict->ancestor_entry : NULL;
+ source.ours = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ?
+ &conflict->our_entry : NULL;
+ source.theirs = GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ?
+ &conflict->their_entry : NULL;
+
+ if (file_opts->favor != GIT_MERGE_FILE_FAVOR_NORMAL) {
+ /* if the user requested a particular type of resolution (via the
+ * favor flag) then let that override the gitattributes and use
+ * the builtin driver.
+ */
+ name = "text";
+ builtin.base.apply = git_merge_driver__builtin_apply;
+ builtin.favor = file_opts->favor;
+
+ driver = &builtin.base;
+ } else {
+ /* find the merge driver for this file */
+ if ((error = git_merge_driver_for_source(&name, &driver, &source)) < 0)
+ goto done;
+
+ if (driver == NULL)
+ fallback = true;
+ }
+
+ if (driver) {
+ error = merge_conflict_invoke_driver(&merge_result, name, driver,
+ diff_list, &source);
+
+ if (error == GIT_PASSTHROUGH)
+ fallback = true;
+ }
+
+ if (fallback) {
+ error = merge_conflict_invoke_driver(&merge_result, "text",
+ &git_merge_driver__text.base, diff_list, &source);
+ }
+
+ if (error < 0) {
+ if (error == GIT_EMERGECONFLICT)
+ error = 0;
+
+ goto done;
+ }
+
+ git_vector_insert(&diff_list->staged, merge_result);
+ git_vector_insert(&diff_list->resolved, (git_merge_diff *)conflict);
+
+ *resolved = 1;
+
+done:
+ git_merge_file_result_free(&result);
+ git_odb_free(odb);
+
+ return error;
+}
+
+static int merge_conflict_resolve(
+ int *out,
+ git_merge_diff_list *diff_list,
+ const git_merge_diff *conflict,
+ const git_merge_options *merge_opts,
+ const git_merge_file_options *file_opts)
+{
+ int resolved = 0;
+ int error = 0;
+
+ *out = 0;
+
+ if ((error = merge_conflict_resolve_trivial(
+ &resolved, diff_list, conflict)) < 0)
+ goto done;
+
+ if (!resolved && (error = merge_conflict_resolve_one_removed(
+ &resolved, diff_list, conflict)) < 0)
+ goto done;
+
+ if (!resolved && (error = merge_conflict_resolve_one_renamed(
+ &resolved, diff_list, conflict)) < 0)
+ goto done;
+
+ if (!resolved && (error = merge_conflict_resolve_contents(
+ &resolved, diff_list, conflict, merge_opts, file_opts)) < 0)
+ goto done;
+
+ *out = resolved;
+
+done:
+ return error;
+}
+
+/* Rename detection and coalescing */
+
+struct merge_diff_similarity {
+ unsigned char similarity;
+ size_t other_idx;
+};
+
+static int index_entry_similarity_calc(
+ void **out,
+ git_repository *repo,
+ git_index_entry *entry,
+ const git_merge_options *opts)
+{
+ git_blob *blob;
+ git_diff_file diff_file;
+ git_object_size_t blobsize;
+ int error;
+
+ if (*out || *out == &cache_invalid_marker)
+ return 0;
+
+ *out = NULL;
+
+ git_oid_clear(&diff_file.id, repo->oid_type);
+
+ if ((error = git_blob_lookup(&blob, repo, &entry->id)) < 0)
+ return error;
+
+ git_oid_cpy(&diff_file.id, &entry->id);
+ diff_file.path = entry->path;
+ diff_file.size = entry->file_size;
+ diff_file.mode = entry->mode;
+ diff_file.flags = 0;
+
+ blobsize = git_blob_rawsize(blob);
+
+ /* file too big for rename processing */
+ if (!git__is_sizet(blobsize))
+ return 0;
+
+ error = opts->metric->buffer_signature(out, &diff_file,
+ git_blob_rawcontent(blob), (size_t)blobsize,
+ opts->metric->payload);
+ if (error == GIT_EBUFS)
+ *out = &cache_invalid_marker;
+
+ git_blob_free(blob);
+
+ return error;
+}
+
+static int index_entry_similarity_inexact(
+ git_repository *repo,
+ git_index_entry *a,
+ size_t a_idx,
+ git_index_entry *b,
+ size_t b_idx,
+ void **cache,
+ const git_merge_options *opts)
+{
+ int score = 0;
+ int error = 0;
+
+ if (!GIT_MODE_ISBLOB(a->mode) || !GIT_MODE_ISBLOB(b->mode))
+ return 0;
+
+ /* update signature cache if needed */
+ if ((error = index_entry_similarity_calc(&cache[a_idx], repo, a, opts)) < 0 ||
+ (error = index_entry_similarity_calc(&cache[b_idx], repo, b, opts)) < 0)
+ return error;
+
+ /* some metrics may not wish to process this file (too big / too small) */
+ if (cache[a_idx] == &cache_invalid_marker || cache[b_idx] == &cache_invalid_marker)
+ return 0;
+
+ /* compare signatures */
+ if (opts->metric->similarity(&score, cache[a_idx], cache[b_idx], opts->metric->payload) < 0)
+ return -1;
+
+ /* clip score */
+ if (score < 0)
+ score = 0;
+ else if (score > 100)
+ score = 100;
+
+ return score;
+}
+
+/* Tracks deletes by oid for merge_diff_mark_similarity_exact(). This is a
+* non-shrinking queue where next_pos is the next position to dequeue.
+*/
+typedef struct {
+ git_array_t(size_t) arr;
+ size_t next_pos;
+ size_t first_entry;
+} deletes_by_oid_queue;
+
+static void deletes_by_oid_free(git_oidmap *map) {
+ deletes_by_oid_queue *queue;
+
+ if (!map)
+ return;
+
+ git_oidmap_foreach_value(map, queue, {
+ git_array_clear(queue->arr);
+ });
+ git_oidmap_free(map);
+}
+
+static int deletes_by_oid_enqueue(git_oidmap *map, git_pool *pool, const git_oid *id, size_t idx)
+{
+ deletes_by_oid_queue *queue;
+ size_t *array_entry;
+
+ if ((queue = git_oidmap_get(map, id)) == NULL) {
+ queue = git_pool_malloc(pool, sizeof(deletes_by_oid_queue));
+ GIT_ERROR_CHECK_ALLOC(queue);
+
+ git_array_init(queue->arr);
+ queue->next_pos = 0;
+ queue->first_entry = idx;
+
+ if (git_oidmap_set(map, id, queue) < 0)
+ return -1;
+ } else {
+ array_entry = git_array_alloc(queue->arr);
+ GIT_ERROR_CHECK_ALLOC(array_entry);
+ *array_entry = idx;
+ }
+
+ return 0;
+}
+
+static int deletes_by_oid_dequeue(size_t *idx, git_oidmap *map, const git_oid *id)
+{
+ deletes_by_oid_queue *queue;
+ size_t *array_entry;
+
+ if ((queue = git_oidmap_get(map, id)) == NULL)
+ return GIT_ENOTFOUND;
+
+ if (queue->next_pos == 0) {
+ *idx = queue->first_entry;
+ } else {
+ array_entry = git_array_get(queue->arr, queue->next_pos - 1);
+ if (array_entry == NULL)
+ return GIT_ENOTFOUND;
+
+ *idx = *array_entry;
+ }
+
+ queue->next_pos++;
+ return 0;
+}
+
+static int merge_diff_mark_similarity_exact(
+ git_merge_diff_list *diff_list,
+ struct merge_diff_similarity *similarity_ours,
+ struct merge_diff_similarity *similarity_theirs)
+{
+ size_t i, j;
+ git_merge_diff *conflict_src, *conflict_tgt;
+ git_oidmap *ours_deletes_by_oid = NULL, *theirs_deletes_by_oid = NULL;
+ int error = 0;
+
+ if (git_oidmap_new(&ours_deletes_by_oid) < 0 ||
+ git_oidmap_new(&theirs_deletes_by_oid) < 0) {
+ error = -1;
+ goto done;
+ }
+
+ /* Build a map of object ids to conflicts */
+ git_vector_foreach(&diff_list->conflicts, i, conflict_src) {
+ /* Items can be the source of a rename iff they have an item in the
+ * ancestor slot and lack an item in the ours or theirs slot. */
+ if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->ancestor_entry))
+ continue;
+
+ if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->our_entry)) {
+ error = deletes_by_oid_enqueue(ours_deletes_by_oid, &diff_list->pool, &conflict_src->ancestor_entry.id, i);
+ if (error < 0)
+ goto done;
+ }
+
+ if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->their_entry)) {
+ error = deletes_by_oid_enqueue(theirs_deletes_by_oid, &diff_list->pool, &conflict_src->ancestor_entry.id, i);
+ if (error < 0)
+ goto done;
+ }
+ }
+
+ git_vector_foreach(&diff_list->conflicts, j, conflict_tgt) {
+ if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->ancestor_entry))
+ continue;
+
+ if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->our_entry)) {
+ if (deletes_by_oid_dequeue(&i, ours_deletes_by_oid, &conflict_tgt->our_entry.id) == 0) {
+ similarity_ours[i].similarity = 100;
+ similarity_ours[i].other_idx = j;
+
+ similarity_ours[j].similarity = 100;
+ similarity_ours[j].other_idx = i;
+ }
+ }
+
+ if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->their_entry)) {
+ if (deletes_by_oid_dequeue(&i, theirs_deletes_by_oid, &conflict_tgt->their_entry.id) == 0) {
+ similarity_theirs[i].similarity = 100;
+ similarity_theirs[i].other_idx = j;
+
+ similarity_theirs[j].similarity = 100;
+ similarity_theirs[j].other_idx = i;
+ }
+ }
+ }
+
+done:
+ deletes_by_oid_free(ours_deletes_by_oid);
+ deletes_by_oid_free(theirs_deletes_by_oid);
+
+ return error;
+}
+
+static int merge_diff_mark_similarity_inexact(
+ git_repository *repo,
+ git_merge_diff_list *diff_list,
+ struct merge_diff_similarity *similarity_ours,
+ struct merge_diff_similarity *similarity_theirs,
+ void **cache,
+ const git_merge_options *opts)
+{
+ size_t i, j;
+ git_merge_diff *conflict_src, *conflict_tgt;
+ int similarity;
+
+ git_vector_foreach(&diff_list->conflicts, i, conflict_src) {
+ /* Items can be the source of a rename iff they have an item in the
+ * ancestor slot and lack an item in the ours or theirs slot. */
+ if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->ancestor_entry) ||
+ (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->our_entry) &&
+ GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->their_entry)))
+ continue;
+
+ git_vector_foreach(&diff_list->conflicts, j, conflict_tgt) {
+ size_t our_idx = diff_list->conflicts.length + j;
+ size_t their_idx = (diff_list->conflicts.length * 2) + j;
+
+ if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->ancestor_entry))
+ continue;
+
+ if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->our_entry) &&
+ !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->our_entry)) {
+ similarity = index_entry_similarity_inexact(repo, &conflict_src->ancestor_entry, i, &conflict_tgt->our_entry, our_idx, cache, opts);
+
+ if (similarity == GIT_EBUFS)
+ continue;
+ else if (similarity < 0)
+ return similarity;
+
+ if (similarity > similarity_ours[i].similarity &&
+ similarity > similarity_ours[j].similarity) {
+ /* Clear previous best similarity */
+ if (similarity_ours[i].similarity > 0)
+ similarity_ours[similarity_ours[i].other_idx].similarity = 0;
+
+ if (similarity_ours[j].similarity > 0)
+ similarity_ours[similarity_ours[j].other_idx].similarity = 0;
+
+ similarity_ours[i].similarity = similarity;
+ similarity_ours[i].other_idx = j;
+
+ similarity_ours[j].similarity = similarity;
+ similarity_ours[j].other_idx = i;
+ }
+ }
+
+ if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_tgt->their_entry) &&
+ !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict_src->their_entry)) {
+ similarity = index_entry_similarity_inexact(repo, &conflict_src->ancestor_entry, i, &conflict_tgt->their_entry, their_idx, cache, opts);
+
+ if (similarity > similarity_theirs[i].similarity &&
+ similarity > similarity_theirs[j].similarity) {
+ /* Clear previous best similarity */
+ if (similarity_theirs[i].similarity > 0)
+ similarity_theirs[similarity_theirs[i].other_idx].similarity = 0;
+
+ if (similarity_theirs[j].similarity > 0)
+ similarity_theirs[similarity_theirs[j].other_idx].similarity = 0;
+
+ similarity_theirs[i].similarity = similarity;
+ similarity_theirs[i].other_idx = j;
+
+ similarity_theirs[j].similarity = similarity;
+ similarity_theirs[j].other_idx = i;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Rename conflicts:
+ *
+ * Ancestor Ours Theirs
+ *
+ * 0a A A A No rename
+ * b A A* A No rename (ours was rewritten)
+ * c A A A* No rename (theirs rewritten)
+ * 1a A A B[A] Rename or rename/edit
+ * b A B[A] A (automergeable)
+ * 2 A B[A] B[A] Both renamed (automergeable)
+ * 3a A B[A] Rename/delete
+ * b A B[A] (same)
+ * 4a A B[A] B Rename/add [B~ours B~theirs]
+ * b A B B[A] (same)
+ * 5 A B[A] C[A] Both renamed ("1 -> 2")
+ * 6 A C[A] Both renamed ("2 -> 1")
+ * B C[B] [C~ours C~theirs] (automergeable)
+ */
+static void merge_diff_mark_rename_conflict(
+ git_merge_diff_list *diff_list,
+ struct merge_diff_similarity *similarity_ours,
+ bool ours_renamed,
+ size_t ours_source_idx,
+ struct merge_diff_similarity *similarity_theirs,
+ bool theirs_renamed,
+ size_t theirs_source_idx,
+ git_merge_diff *target,
+ const git_merge_options *opts)
+{
+ git_merge_diff *ours_source = NULL, *theirs_source = NULL;
+
+ if (ours_renamed)
+ ours_source = diff_list->conflicts.contents[ours_source_idx];
+
+ if (theirs_renamed)
+ theirs_source = diff_list->conflicts.contents[theirs_source_idx];
+
+ /* Detect 2->1 conflicts */
+ if (ours_renamed && theirs_renamed) {
+ /* Both renamed to the same target name. */
+ if (ours_source_idx == theirs_source_idx)
+ ours_source->type = GIT_MERGE_DIFF_BOTH_RENAMED;
+ else {
+ ours_source->type = GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1;
+ theirs_source->type = GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1;
+ }
+ } else if (ours_renamed) {
+ /* If our source was also renamed in theirs, this is a 1->2 */
+ if (similarity_theirs[ours_source_idx].similarity >= opts->rename_threshold)
+ ours_source->type = GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2;
+
+ else if (GIT_MERGE_INDEX_ENTRY_EXISTS(target->their_entry)) {
+ ours_source->type = GIT_MERGE_DIFF_RENAMED_ADDED;
+ target->type = GIT_MERGE_DIFF_RENAMED_ADDED;
+ }
+
+ else if (!GIT_MERGE_INDEX_ENTRY_EXISTS(ours_source->their_entry))
+ ours_source->type = GIT_MERGE_DIFF_RENAMED_DELETED;
+
+ else if (ours_source->type == GIT_MERGE_DIFF_MODIFIED_DELETED)
+ ours_source->type = GIT_MERGE_DIFF_RENAMED_MODIFIED;
+ } else if (theirs_renamed) {
+ /* If their source was also renamed in ours, this is a 1->2 */
+ if (similarity_ours[theirs_source_idx].similarity >= opts->rename_threshold)
+ theirs_source->type = GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2;
+
+ else if (GIT_MERGE_INDEX_ENTRY_EXISTS(target->our_entry)) {
+ theirs_source->type = GIT_MERGE_DIFF_RENAMED_ADDED;
+ target->type = GIT_MERGE_DIFF_RENAMED_ADDED;
+ }
+
+ else if (!GIT_MERGE_INDEX_ENTRY_EXISTS(theirs_source->our_entry))
+ theirs_source->type = GIT_MERGE_DIFF_RENAMED_DELETED;
+
+ else if (theirs_source->type == GIT_MERGE_DIFF_MODIFIED_DELETED)
+ theirs_source->type = GIT_MERGE_DIFF_RENAMED_MODIFIED;
+ }
+}
+
+GIT_INLINE(void) merge_diff_coalesce_rename(
+ git_index_entry *source_entry,
+ git_delta_t *source_status,
+ git_index_entry *target_entry,
+ git_delta_t *target_status)
+{
+ /* Coalesce the rename target into the rename source. */
+ memcpy(source_entry, target_entry, sizeof(git_index_entry));
+ *source_status = GIT_DELTA_RENAMED;
+
+ memset(target_entry, 0x0, sizeof(git_index_entry));
+ *target_status = GIT_DELTA_UNMODIFIED;
+}
+
+static void merge_diff_list_coalesce_renames(
+ git_merge_diff_list *diff_list,
+ struct merge_diff_similarity *similarity_ours,
+ struct merge_diff_similarity *similarity_theirs,
+ const git_merge_options *opts)
+{
+ size_t i;
+ bool ours_renamed = 0, theirs_renamed = 0;
+ size_t ours_source_idx = 0, theirs_source_idx = 0;
+ git_merge_diff *ours_source, *theirs_source, *target;
+
+ for (i = 0; i < diff_list->conflicts.length; i++) {
+ target = diff_list->conflicts.contents[i];
+
+ ours_renamed = 0;
+ theirs_renamed = 0;
+
+ if (GIT_MERGE_INDEX_ENTRY_EXISTS(target->our_entry) &&
+ similarity_ours[i].similarity >= opts->rename_threshold) {
+ ours_source_idx = similarity_ours[i].other_idx;
+
+ ours_source = diff_list->conflicts.contents[ours_source_idx];
+
+ merge_diff_coalesce_rename(
+ &ours_source->our_entry,
+ &ours_source->our_status,
+ &target->our_entry,
+ &target->our_status);
+
+ similarity_ours[ours_source_idx].similarity = 0;
+ similarity_ours[i].similarity = 0;
+
+ ours_renamed = 1;
+ }
+
+ /* insufficient to determine direction */
+ if (GIT_MERGE_INDEX_ENTRY_EXISTS(target->their_entry) &&
+ similarity_theirs[i].similarity >= opts->rename_threshold) {
+ theirs_source_idx = similarity_theirs[i].other_idx;
+
+ theirs_source = diff_list->conflicts.contents[theirs_source_idx];
+
+ merge_diff_coalesce_rename(
+ &theirs_source->their_entry,
+ &theirs_source->their_status,
+ &target->their_entry,
+ &target->their_status);
+
+ similarity_theirs[theirs_source_idx].similarity = 0;
+ similarity_theirs[i].similarity = 0;
+
+ theirs_renamed = 1;
+ }
+
+ merge_diff_mark_rename_conflict(diff_list,
+ similarity_ours, ours_renamed, ours_source_idx,
+ similarity_theirs, theirs_renamed, theirs_source_idx,
+ target, opts);
+ }
+}
+
+static int merge_diff_empty(const git_vector *conflicts, size_t idx, void *p)
+{
+ git_merge_diff *conflict = conflicts->contents[idx];
+
+ GIT_UNUSED(p);
+
+ return (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) &&
+ !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) &&
+ !GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry));
+}
+
+static void merge_diff_list_count_candidates(
+ git_merge_diff_list *diff_list,
+ size_t *src_count,
+ size_t *tgt_count)
+{
+ git_merge_diff *entry;
+ size_t i;
+
+ *src_count = 0;
+ *tgt_count = 0;
+
+ git_vector_foreach(&diff_list->conflicts, i, entry) {
+ if (GIT_MERGE_INDEX_ENTRY_EXISTS(entry->ancestor_entry) &&
+ (!GIT_MERGE_INDEX_ENTRY_EXISTS(entry->our_entry) ||
+ !GIT_MERGE_INDEX_ENTRY_EXISTS(entry->their_entry)))
+ (*src_count)++;
+ else if (!GIT_MERGE_INDEX_ENTRY_EXISTS(entry->ancestor_entry))
+ (*tgt_count)++;
+ }
+}
+
+int git_merge_diff_list__find_renames(
+ git_repository *repo,
+ git_merge_diff_list *diff_list,
+ const git_merge_options *opts)
+{
+ struct merge_diff_similarity *similarity_ours, *similarity_theirs;
+ void **cache = NULL;
+ size_t cache_size = 0;
+ size_t src_count, tgt_count, i;
+ int error = 0;
+
+ GIT_ASSERT_ARG(diff_list);
+ GIT_ASSERT_ARG(opts);
+
+ if ((opts->flags & GIT_MERGE_FIND_RENAMES) == 0 ||
+ !diff_list->conflicts.length)
+ return 0;
+
+ similarity_ours = git__calloc(diff_list->conflicts.length,
+ sizeof(struct merge_diff_similarity));
+ GIT_ERROR_CHECK_ALLOC(similarity_ours);
+
+ similarity_theirs = git__calloc(diff_list->conflicts.length,
+ sizeof(struct merge_diff_similarity));
+ GIT_ERROR_CHECK_ALLOC(similarity_theirs);
+
+ /* Calculate similarity between items that were deleted from the ancestor
+ * and added in the other branch.
+ */
+ if ((error = merge_diff_mark_similarity_exact(diff_list, similarity_ours, similarity_theirs)) < 0)
+ goto done;
+
+ if (opts->rename_threshold < 100 && diff_list->conflicts.length <= opts->target_limit) {
+ GIT_ERROR_CHECK_ALLOC_MULTIPLY(&cache_size, diff_list->conflicts.length, 3);
+ cache = git__calloc(cache_size, sizeof(void *));
+ GIT_ERROR_CHECK_ALLOC(cache);
+
+ merge_diff_list_count_candidates(diff_list, &src_count, &tgt_count);
+
+ if (src_count > opts->target_limit || tgt_count > opts->target_limit) {
+ /* TODO: report! */
+ } else {
+ if ((error = merge_diff_mark_similarity_inexact(
+ repo, diff_list, similarity_ours, similarity_theirs, cache, opts)) < 0)
+ goto done;
+ }
+ }
+
+ /* For entries that are appropriately similar, merge the new name's entry
+ * into the old name.
+ */
+ merge_diff_list_coalesce_renames(diff_list, similarity_ours, similarity_theirs, opts);
+
+ /* And remove any entries that were merged and are now empty. */
+ git_vector_remove_matching(&diff_list->conflicts, merge_diff_empty, NULL);
+
+done:
+ if (cache != NULL) {
+ for (i = 0; i < cache_size; ++i) {
+ if (cache[i] != NULL && cache[i] != &cache_invalid_marker)
+ opts->metric->free_signature(cache[i], opts->metric->payload);
+ }
+
+ git__free(cache);
+ }
+
+ git__free(similarity_ours);
+ git__free(similarity_theirs);
+
+ return error;
+}
+
+/* Directory/file conflict handling */
+
+GIT_INLINE(const char *) merge_diff_path(
+ const git_merge_diff *conflict)
+{
+ if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry))
+ return conflict->ancestor_entry.path;
+ else if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry))
+ return conflict->our_entry.path;
+ else if (GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry))
+ return conflict->their_entry.path;
+
+ return NULL;
+}
+
+GIT_INLINE(bool) merge_diff_any_side_added_or_modified(
+ const git_merge_diff *conflict)
+{
+ if (conflict->our_status == GIT_DELTA_ADDED ||
+ conflict->our_status == GIT_DELTA_MODIFIED ||
+ conflict->their_status == GIT_DELTA_ADDED ||
+ conflict->their_status == GIT_DELTA_MODIFIED)
+ return true;
+
+ return false;
+}
+
+GIT_INLINE(bool) path_is_prefixed(const char *parent, const char *child)
+{
+ size_t child_len = strlen(child);
+ size_t parent_len = strlen(parent);
+
+ if (child_len < parent_len ||
+ strncmp(parent, child, parent_len) != 0)
+ return 0;
+
+ return (child[parent_len] == '/');
+}
+
+GIT_INLINE(int) merge_diff_detect_df_conflict(
+ struct merge_diff_df_data *df_data,
+ git_merge_diff *conflict)
+{
+ const char *cur_path = merge_diff_path(conflict);
+
+ /* Determine if this is a D/F conflict or the child of one */
+ if (df_data->df_path &&
+ path_is_prefixed(df_data->df_path, cur_path))
+ conflict->type = GIT_MERGE_DIFF_DF_CHILD;
+ else if(df_data->df_path)
+ df_data->df_path = NULL;
+ else if (df_data->prev_path &&
+ merge_diff_any_side_added_or_modified(df_data->prev_conflict) &&
+ merge_diff_any_side_added_or_modified(conflict) &&
+ path_is_prefixed(df_data->prev_path, cur_path)) {
+ conflict->type = GIT_MERGE_DIFF_DF_CHILD;
+
+ df_data->prev_conflict->type = GIT_MERGE_DIFF_DIRECTORY_FILE;
+ df_data->df_path = df_data->prev_path;
+ }
+
+ df_data->prev_path = cur_path;
+ df_data->prev_conflict = conflict;
+
+ return 0;
+}
+
+/* Conflict handling */
+
+GIT_INLINE(int) merge_diff_detect_type(
+ git_merge_diff *conflict)
+{
+ if (conflict->our_status == GIT_DELTA_ADDED &&
+ conflict->their_status == GIT_DELTA_ADDED)
+ conflict->type = GIT_MERGE_DIFF_BOTH_ADDED;
+ else if (conflict->our_status == GIT_DELTA_MODIFIED &&
+ conflict->their_status == GIT_DELTA_MODIFIED)
+ conflict->type = GIT_MERGE_DIFF_BOTH_MODIFIED;
+ else if (conflict->our_status == GIT_DELTA_DELETED &&
+ conflict->their_status == GIT_DELTA_DELETED)
+ conflict->type = GIT_MERGE_DIFF_BOTH_DELETED;
+ else if (conflict->our_status == GIT_DELTA_MODIFIED &&
+ conflict->their_status == GIT_DELTA_DELETED)
+ conflict->type = GIT_MERGE_DIFF_MODIFIED_DELETED;
+ else if (conflict->our_status == GIT_DELTA_DELETED &&
+ conflict->their_status == GIT_DELTA_MODIFIED)
+ conflict->type = GIT_MERGE_DIFF_MODIFIED_DELETED;
+ else
+ conflict->type = GIT_MERGE_DIFF_NONE;
+
+ return 0;
+}
+
+GIT_INLINE(int) index_entry_dup_pool(
+ git_index_entry *out,
+ git_pool *pool,
+ const git_index_entry *src)
+{
+ if (src != NULL) {
+ memcpy(out, src, sizeof(git_index_entry));
+ if ((out->path = git_pool_strdup(pool, src->path)) == NULL)
+ return -1;
+ }
+
+ return 0;
+}
+
+GIT_INLINE(int) merge_delta_type_from_index_entries(
+ const git_index_entry *ancestor,
+ const git_index_entry *other)
+{
+ if (ancestor == NULL && other == NULL)
+ return GIT_DELTA_UNMODIFIED;
+ else if (ancestor == NULL && other != NULL)
+ return GIT_DELTA_ADDED;
+ else if (ancestor != NULL && other == NULL)
+ return GIT_DELTA_DELETED;
+ else if (S_ISDIR(ancestor->mode) ^ S_ISDIR(other->mode))
+ return GIT_DELTA_TYPECHANGE;
+ else if(S_ISLNK(ancestor->mode) ^ S_ISLNK(other->mode))
+ return GIT_DELTA_TYPECHANGE;
+ else if (git_oid__cmp(&ancestor->id, &other->id) ||
+ ancestor->mode != other->mode)
+ return GIT_DELTA_MODIFIED;
+
+ return GIT_DELTA_UNMODIFIED;
+}
+
+static git_merge_diff *merge_diff_from_index_entries(
+ git_merge_diff_list *diff_list,
+ const git_index_entry **entries)
+{
+ git_merge_diff *conflict;
+ git_pool *pool = &diff_list->pool;
+
+ if ((conflict = git_pool_mallocz(pool, sizeof(git_merge_diff))) == NULL)
+ return NULL;
+
+ if (index_entry_dup_pool(&conflict->ancestor_entry, pool, entries[TREE_IDX_ANCESTOR]) < 0 ||
+ index_entry_dup_pool(&conflict->our_entry, pool, entries[TREE_IDX_OURS]) < 0 ||
+ index_entry_dup_pool(&conflict->their_entry, pool, entries[TREE_IDX_THEIRS]) < 0)
+ return NULL;
+
+ conflict->our_status = merge_delta_type_from_index_entries(
+ entries[TREE_IDX_ANCESTOR], entries[TREE_IDX_OURS]);
+ conflict->their_status = merge_delta_type_from_index_entries(
+ entries[TREE_IDX_ANCESTOR], entries[TREE_IDX_THEIRS]);
+
+ return conflict;
+}
+
+/* Merge trees */
+
+static int merge_diff_list_insert_conflict(
+ git_merge_diff_list *diff_list,
+ struct merge_diff_df_data *merge_df_data,
+ const git_index_entry *tree_items[3])
+{
+ git_merge_diff *conflict;
+
+ if ((conflict = merge_diff_from_index_entries(diff_list, tree_items)) == NULL ||
+ merge_diff_detect_type(conflict) < 0 ||
+ merge_diff_detect_df_conflict(merge_df_data, conflict) < 0 ||
+ git_vector_insert(&diff_list->conflicts, conflict) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int merge_diff_list_insert_unmodified(
+ git_merge_diff_list *diff_list,
+ const git_index_entry *tree_items[3])
+{
+ int error = 0;
+ git_index_entry *entry;
+
+ entry = git_pool_malloc(&diff_list->pool, sizeof(git_index_entry));
+ GIT_ERROR_CHECK_ALLOC(entry);
+
+ if ((error = index_entry_dup_pool(entry, &diff_list->pool, tree_items[0])) >= 0)
+ error = git_vector_insert(&diff_list->staged, entry);
+
+ return error;
+}
+
+struct merge_diff_find_data {
+ git_merge_diff_list *diff_list;
+ struct merge_diff_df_data df_data;
+};
+
+static int queue_difference(const git_index_entry **entries, void *data)
+{
+ struct merge_diff_find_data *find_data = data;
+ bool item_modified = false;
+ size_t i;
+
+ if (!entries[0] || !entries[1] || !entries[2]) {
+ item_modified = true;
+ } else {
+ for (i = 1; i < 3; i++) {
+ if (index_entry_cmp(entries[0], entries[i]) != 0) {
+ item_modified = true;
+ break;
+ }
+ }
+ }
+
+ return item_modified ?
+ merge_diff_list_insert_conflict(
+ find_data->diff_list, &find_data->df_data, entries) :
+ merge_diff_list_insert_unmodified(find_data->diff_list, entries);
+}
+
+int git_merge_diff_list__find_differences(
+ git_merge_diff_list *diff_list,
+ git_iterator *ancestor_iter,
+ git_iterator *our_iter,
+ git_iterator *their_iter)
+{
+ git_iterator *iterators[3] = { ancestor_iter, our_iter, their_iter };
+ struct merge_diff_find_data find_data = { diff_list };
+
+ return git_iterator_walk(iterators, 3, queue_difference, &find_data);
+}
+
+git_merge_diff_list *git_merge_diff_list__alloc(git_repository *repo)
+{
+ git_merge_diff_list *diff_list = git__calloc(1, sizeof(git_merge_diff_list));
+
+ if (diff_list == NULL)
+ return NULL;
+
+ diff_list->repo = repo;
+
+
+ if (git_pool_init(&diff_list->pool, 1) < 0 ||
+ git_vector_init(&diff_list->staged, 0, NULL) < 0 ||
+ git_vector_init(&diff_list->conflicts, 0, NULL) < 0 ||
+ git_vector_init(&diff_list->resolved, 0, NULL) < 0) {
+ git_merge_diff_list__free(diff_list);
+ return NULL;
+ }
+
+ return diff_list;
+}
+
+void git_merge_diff_list__free(git_merge_diff_list *diff_list)
+{
+ if (!diff_list)
+ return;
+
+ git_vector_free(&diff_list->staged);
+ git_vector_free(&diff_list->conflicts);
+ git_vector_free(&diff_list->resolved);
+ git_pool_clear(&diff_list->pool);
+ git__free(diff_list);
+}
+
+static int merge_normalize_opts(
+ git_repository *repo,
+ git_merge_options *opts,
+ const git_merge_options *given)
+{
+ git_config *cfg = NULL;
+ git_config_entry *entry = NULL;
+ int error = 0;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(opts);
+
+ if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
+ return error;
+
+ if (given != NULL) {
+ memcpy(opts, given, sizeof(git_merge_options));
+ } else {
+ git_merge_options init = GIT_MERGE_OPTIONS_INIT;
+ memcpy(opts, &init, sizeof(init));
+ }
+
+ if ((opts->flags & GIT_MERGE_FIND_RENAMES) && !opts->rename_threshold)
+ opts->rename_threshold = GIT_MERGE_DEFAULT_RENAME_THRESHOLD;
+
+ if (given && given->default_driver) {
+ opts->default_driver = git__strdup(given->default_driver);
+ GIT_ERROR_CHECK_ALLOC(opts->default_driver);
+ } else {
+ error = git_config_get_entry(&entry, cfg, "merge.default");
+
+ if (error == 0) {
+ opts->default_driver = git__strdup(entry->value);
+ GIT_ERROR_CHECK_ALLOC(opts->default_driver);
+ } else if (error == GIT_ENOTFOUND) {
+ error = 0;
+ } else {
+ goto done;
+ }
+ }
+
+ if (!opts->target_limit) {
+ int limit = git_config__get_int_force(cfg, "merge.renamelimit", 0);
+
+ if (!limit)
+ limit = git_config__get_int_force(cfg, "diff.renamelimit", 0);
+
+ opts->target_limit = (limit <= 0) ?
+ GIT_MERGE_DEFAULT_TARGET_LIMIT : (unsigned int)limit;
+ }
+
+ /* assign the internal metric with whitespace flag as payload */
+ if (!opts->metric) {
+ opts->metric = git__malloc(sizeof(git_diff_similarity_metric));
+ GIT_ERROR_CHECK_ALLOC(opts->metric);
+
+ opts->metric->file_signature = git_diff_find_similar__hashsig_for_file;
+ opts->metric->buffer_signature = git_diff_find_similar__hashsig_for_buf;
+ opts->metric->free_signature = git_diff_find_similar__hashsig_free;
+ opts->metric->similarity = git_diff_find_similar__calc_similarity;
+ opts->metric->payload = (void *)GIT_HASHSIG_SMART_WHITESPACE;
+ }
+
+done:
+ git_config_entry_free(entry);
+ return error;
+}
+
+
+static int merge_index_insert_reuc(
+ git_index *index,
+ size_t idx,
+ const git_index_entry *entry)
+{
+ const git_index_reuc_entry *reuc;
+ int mode[3] = { 0, 0, 0 };
+ git_oid const *oid[3] = { NULL, NULL, NULL };
+ size_t i;
+
+ if (!GIT_MERGE_INDEX_ENTRY_EXISTS(*entry))
+ return 0;
+
+ if ((reuc = git_index_reuc_get_bypath(index, entry->path)) != NULL) {
+ for (i = 0; i < 3; i++) {
+ mode[i] = reuc->mode[i];
+ oid[i] = &reuc->oid[i];
+ }
+ }
+
+ mode[idx] = entry->mode;
+ oid[idx] = &entry->id;
+
+ return git_index_reuc_add(index, entry->path,
+ mode[0], oid[0], mode[1], oid[1], mode[2], oid[2]);
+}
+
+static int index_update_reuc(git_index *index, git_merge_diff_list *diff_list)
+{
+ int error;
+ size_t i;
+ git_merge_diff *conflict;
+
+ /* Add each entry in the resolved conflict to the REUC independently, since
+ * the paths may differ due to renames. */
+ git_vector_foreach(&diff_list->resolved, i, conflict) {
+ const git_index_entry *ancestor =
+ GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) ?
+ &conflict->ancestor_entry : NULL;
+
+ const git_index_entry *ours =
+ GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ?
+ &conflict->our_entry : NULL;
+
+ const git_index_entry *theirs =
+ GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ?
+ &conflict->their_entry : NULL;
+
+ if (ancestor != NULL &&
+ (error = merge_index_insert_reuc(index, TREE_IDX_ANCESTOR, ancestor)) < 0)
+ return error;
+
+ if (ours != NULL &&
+ (error = merge_index_insert_reuc(index, TREE_IDX_OURS, ours)) < 0)
+ return error;
+
+ if (theirs != NULL &&
+ (error = merge_index_insert_reuc(index, TREE_IDX_THEIRS, theirs)) < 0)
+ return error;
+ }
+
+ return 0;
+}
+
+static int index_from_diff_list(
+ git_index **out,
+ git_merge_diff_list *diff_list,
+ git_oid_t oid_type,
+ bool skip_reuc)
+{
+ git_index *index;
+ size_t i;
+ git_merge_diff *conflict;
+ int error = 0;
+
+ *out = NULL;
+
+ if ((error = git_index__new(&index, oid_type)) < 0)
+ return error;
+
+ if ((error = git_index__fill(index, &diff_list->staged)) < 0)
+ goto on_error;
+
+ git_vector_foreach(&diff_list->conflicts, i, conflict) {
+ const git_index_entry *ancestor =
+ GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry) ?
+ &conflict->ancestor_entry : NULL;
+
+ const git_index_entry *ours =
+ GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ?
+ &conflict->our_entry : NULL;
+
+ const git_index_entry *theirs =
+ GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ?
+ &conflict->their_entry : NULL;
+
+ if ((error = git_index_conflict_add(index, ancestor, ours, theirs)) < 0)
+ goto on_error;
+ }
+
+ /* Add each rename entry to the rename portion of the index. */
+ git_vector_foreach(&diff_list->conflicts, i, conflict) {
+ const char *ancestor_path, *our_path, *their_path;
+
+ if (!GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->ancestor_entry))
+ continue;
+
+ ancestor_path = conflict->ancestor_entry.path;
+
+ our_path =
+ GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->our_entry) ?
+ conflict->our_entry.path : NULL;
+
+ their_path =
+ GIT_MERGE_INDEX_ENTRY_EXISTS(conflict->their_entry) ?
+ conflict->their_entry.path : NULL;
+
+ if ((our_path && strcmp(ancestor_path, our_path) != 0) ||
+ (their_path && strcmp(ancestor_path, their_path) != 0)) {
+ if ((error = git_index_name_add(index, ancestor_path, our_path, their_path)) < 0)
+ goto on_error;
+ }
+ }
+
+ if (!skip_reuc) {
+ if ((error = index_update_reuc(index, diff_list)) < 0)
+ goto on_error;
+ }
+
+ *out = index;
+ return 0;
+
+on_error:
+ git_index_free(index);
+ return error;
+}
+
+static git_iterator *iterator_given_or_empty(git_iterator **empty, git_iterator *given)
+{
+ git_iterator_options opts = GIT_ITERATOR_OPTIONS_INIT;
+
+ if (given)
+ return given;
+
+ opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ if (git_iterator_for_nothing(empty, &opts) < 0)
+ return NULL;
+
+ return *empty;
+}
+
+int git_merge__iterators(
+ git_index **out,
+ git_repository *repo,
+ git_iterator *ancestor_iter,
+ git_iterator *our_iter,
+ git_iterator *theirs_iter,
+ const git_merge_options *given_opts)
+{
+ git_iterator *empty_ancestor = NULL,
+ *empty_ours = NULL,
+ *empty_theirs = NULL;
+ git_merge_diff_list *diff_list;
+ git_merge_options opts;
+ git_merge_file_options file_opts = GIT_MERGE_FILE_OPTIONS_INIT;
+ git_merge_diff *conflict;
+ git_vector changes;
+ size_t i;
+ int error = 0;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+
+ *out = NULL;
+
+ GIT_ERROR_CHECK_VERSION(
+ given_opts, GIT_MERGE_OPTIONS_VERSION, "git_merge_options");
+
+ if ((error = merge_normalize_opts(repo, &opts, given_opts)) < 0)
+ return error;
+
+ file_opts.favor = opts.file_favor;
+ file_opts.flags = opts.file_flags;
+
+ /* use the git-inspired labels when virtual base building */
+ if (opts.flags & GIT_MERGE_VIRTUAL_BASE) {
+ file_opts.ancestor_label = "merged common ancestors";
+ file_opts.our_label = "Temporary merge branch 1";
+ file_opts.their_label = "Temporary merge branch 2";
+ file_opts.flags |= GIT_MERGE_FILE_ACCEPT_CONFLICTS;
+ file_opts.marker_size = GIT_MERGE_CONFLICT_MARKER_SIZE + 2;
+ }
+
+ diff_list = git_merge_diff_list__alloc(repo);
+ GIT_ERROR_CHECK_ALLOC(diff_list);
+
+ ancestor_iter = iterator_given_or_empty(&empty_ancestor, ancestor_iter);
+ our_iter = iterator_given_or_empty(&empty_ours, our_iter);
+ theirs_iter = iterator_given_or_empty(&empty_theirs, theirs_iter);
+
+ if ((error = git_merge_diff_list__find_differences(
+ diff_list, ancestor_iter, our_iter, theirs_iter)) < 0 ||
+ (error = git_merge_diff_list__find_renames(repo, diff_list, &opts)) < 0)
+ goto done;
+
+ memcpy(&changes, &diff_list->conflicts, sizeof(git_vector));
+ git_vector_clear(&diff_list->conflicts);
+
+ git_vector_foreach(&changes, i, conflict) {
+ int resolved = 0;
+
+ if ((error = merge_conflict_resolve(
+ &resolved, diff_list, conflict, &opts, &file_opts)) < 0)
+ goto done;
+
+ if (!resolved) {
+ if ((opts.flags & GIT_MERGE_FAIL_ON_CONFLICT)) {
+ git_error_set(GIT_ERROR_MERGE, "merge conflicts exist");
+ error = GIT_EMERGECONFLICT;
+ goto done;
+ }
+
+ git_vector_insert(&diff_list->conflicts, conflict);
+ }
+ }
+
+ error = index_from_diff_list(out, diff_list, repo->oid_type,
+ (opts.flags & GIT_MERGE_SKIP_REUC));
+
+done:
+ if (!given_opts || !given_opts->metric)
+ git__free(opts.metric);
+
+ git__free((char *)opts.default_driver);
+
+ git_merge_diff_list__free(diff_list);
+ git_iterator_free(empty_ancestor);
+ git_iterator_free(empty_ours);
+ git_iterator_free(empty_theirs);
+
+ return error;
+}
+
+int git_merge_trees(
+ git_index **out,
+ git_repository *repo,
+ const git_tree *ancestor_tree,
+ const git_tree *our_tree,
+ const git_tree *their_tree,
+ const git_merge_options *merge_opts)
+{
+ git_iterator *ancestor_iter = NULL, *our_iter = NULL, *their_iter = NULL;
+ git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+
+ /* if one side is treesame to the ancestor, take the other side */
+ if (ancestor_tree && merge_opts && (merge_opts->flags & GIT_MERGE_SKIP_REUC)) {
+ const git_tree *result = NULL;
+ const git_oid *ancestor_tree_id = git_tree_id(ancestor_tree);
+
+ if (our_tree && !git_oid_cmp(ancestor_tree_id, git_tree_id(our_tree)))
+ result = their_tree;
+ else if (their_tree && !git_oid_cmp(ancestor_tree_id, git_tree_id(their_tree)))
+ result = our_tree;
+
+ if (result) {
+ if ((error = git_index__new(out, repo->oid_type)) == 0)
+ error = git_index_read_tree(*out, result);
+
+ return error;
+ }
+ }
+
+ iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ if ((error = git_iterator_for_tree(
+ &ancestor_iter, (git_tree *)ancestor_tree, &iter_opts)) < 0 ||
+ (error = git_iterator_for_tree(
+ &our_iter, (git_tree *)our_tree, &iter_opts)) < 0 ||
+ (error = git_iterator_for_tree(
+ &their_iter, (git_tree *)their_tree, &iter_opts)) < 0)
+ goto done;
+
+ error = git_merge__iterators(
+ out, repo, ancestor_iter, our_iter, their_iter, merge_opts);
+
+done:
+ git_iterator_free(ancestor_iter);
+ git_iterator_free(our_iter);
+ git_iterator_free(their_iter);
+
+ return error;
+}
+
+static int merge_annotated_commits(
+ git_index **index_out,
+ git_annotated_commit **base_out,
+ git_repository *repo,
+ git_annotated_commit *our_commit,
+ git_annotated_commit *their_commit,
+ size_t recursion_level,
+ const git_merge_options *opts);
+
+GIT_INLINE(int) insert_head_ids(
+ git_array_oid_t *ids,
+ const git_annotated_commit *annotated_commit)
+{
+ git_oid *id;
+ size_t i;
+
+ if (annotated_commit->type == GIT_ANNOTATED_COMMIT_REAL) {
+ id = git_array_alloc(*ids);
+ GIT_ERROR_CHECK_ALLOC(id);
+
+ git_oid_cpy(id, git_commit_id(annotated_commit->commit));
+ } else {
+ for (i = 0; i < annotated_commit->parents.size; i++) {
+ id = git_array_alloc(*ids);
+ GIT_ERROR_CHECK_ALLOC(id);
+
+ git_oid_cpy(id, &annotated_commit->parents.ptr[i]);
+ }
+ }
+
+ return 0;
+}
+
+static int create_virtual_base(
+ git_annotated_commit **out,
+ git_repository *repo,
+ git_annotated_commit *one,
+ git_annotated_commit *two,
+ const git_merge_options *opts,
+ size_t recursion_level)
+{
+ git_annotated_commit *result = NULL;
+ git_index *index = NULL;
+ git_merge_options virtual_opts = GIT_MERGE_OPTIONS_INIT;
+
+ /* Conflicts in the merge base creation do not propagate to conflicts
+ * in the result; the conflicted base will act as the common ancestor.
+ */
+ if (opts)
+ memcpy(&virtual_opts, opts, sizeof(git_merge_options));
+
+ virtual_opts.flags &= ~GIT_MERGE_FAIL_ON_CONFLICT;
+ virtual_opts.flags |= GIT_MERGE_VIRTUAL_BASE;
+
+ if ((merge_annotated_commits(&index, NULL, repo, one, two,
+ recursion_level + 1, &virtual_opts)) < 0)
+ return -1;
+
+ result = git__calloc(1, sizeof(git_annotated_commit));
+ GIT_ERROR_CHECK_ALLOC(result);
+ result->type = GIT_ANNOTATED_COMMIT_VIRTUAL;
+ result->index = index;
+
+ if (insert_head_ids(&result->parents, one) < 0 ||
+ insert_head_ids(&result->parents, two) < 0) {
+ git_annotated_commit_free(result);
+ return -1;
+ }
+
+ *out = result;
+ return 0;
+}
+
+static int compute_base(
+ git_annotated_commit **out,
+ git_repository *repo,
+ const git_annotated_commit *one,
+ const git_annotated_commit *two,
+ const git_merge_options *given_opts,
+ size_t recursion_level)
+{
+ git_array_oid_t head_ids = GIT_ARRAY_INIT;
+ git_oidarray bases = {0};
+ git_annotated_commit *base = NULL, *other = NULL, *new_base = NULL;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+ size_t i, base_count;
+ int error;
+
+ *out = NULL;
+
+ if (given_opts)
+ memcpy(&opts, given_opts, sizeof(git_merge_options));
+
+ /* With more than two commits, merge_bases_many finds the base of
+ * the first commit and a hypothetical merge of the others. Since
+ * "one" may itself be a virtual commit, which insert_head_ids
+ * substitutes multiple ancestors for, it needs to be added
+ * after "two" which is always a single real commit.
+ */
+ if ((error = insert_head_ids(&head_ids, two)) < 0 ||
+ (error = insert_head_ids(&head_ids, one)) < 0 ||
+ (error = git_merge_bases_many(&bases, repo,
+ head_ids.size, head_ids.ptr)) < 0)
+ goto done;
+
+ base_count = (opts.flags & GIT_MERGE_NO_RECURSIVE) ? 0 : bases.count;
+
+ if (base_count)
+ git_oidarray__reverse(&bases);
+
+ if ((error = git_annotated_commit_lookup(&base, repo, &bases.ids[0])) < 0)
+ goto done;
+
+ for (i = 1; i < base_count; i++) {
+ recursion_level++;
+
+ if (opts.recursion_limit && recursion_level > opts.recursion_limit)
+ break;
+
+ if ((error = git_annotated_commit_lookup(&other, repo,
+ &bases.ids[i])) < 0 ||
+ (error = create_virtual_base(&new_base, repo, base, other, &opts,
+ recursion_level)) < 0)
+ goto done;
+
+ git_annotated_commit_free(base);
+ git_annotated_commit_free(other);
+
+ base = new_base;
+ new_base = NULL;
+ other = NULL;
+ }
+
+done:
+ if (error == 0)
+ *out = base;
+ else
+ git_annotated_commit_free(base);
+
+ git_annotated_commit_free(other);
+ git_annotated_commit_free(new_base);
+ git_oidarray_dispose(&bases);
+ git_array_clear(head_ids);
+ return error;
+}
+
+static int iterator_for_annotated_commit(
+ git_iterator **out,
+ git_annotated_commit *commit)
+{
+ git_iterator_options opts = GIT_ITERATOR_OPTIONS_INIT;
+ int error;
+
+ opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ if (commit == NULL) {
+ error = git_iterator_for_nothing(out, &opts);
+ } else if (commit->type == GIT_ANNOTATED_COMMIT_VIRTUAL) {
+ error = git_iterator_for_index(out, git_index_owner(commit->index), commit->index, &opts);
+ } else {
+ if (!commit->tree &&
+ (error = git_commit_tree(&commit->tree, commit->commit)) < 0)
+ goto done;
+
+ error = git_iterator_for_tree(out, commit->tree, &opts);
+ }
+
+done:
+ return error;
+}
+
+static int merge_annotated_commits(
+ git_index **index_out,
+ git_annotated_commit **base_out,
+ git_repository *repo,
+ git_annotated_commit *ours,
+ git_annotated_commit *theirs,
+ size_t recursion_level,
+ const git_merge_options *opts)
+{
+ git_annotated_commit *base = NULL;
+ git_iterator *base_iter = NULL, *our_iter = NULL, *their_iter = NULL;
+ int error;
+
+ if ((error = compute_base(&base, repo, ours, theirs, opts,
+ recursion_level)) < 0) {
+
+ if (error != GIT_ENOTFOUND)
+ goto done;
+
+ git_error_clear();
+ }
+
+ if ((error = iterator_for_annotated_commit(&base_iter, base)) < 0 ||
+ (error = iterator_for_annotated_commit(&our_iter, ours)) < 0 ||
+ (error = iterator_for_annotated_commit(&their_iter, theirs)) < 0 ||
+ (error = git_merge__iterators(index_out, repo, base_iter, our_iter,
+ their_iter, opts)) < 0)
+ goto done;
+
+ if (base_out) {
+ *base_out = base;
+ base = NULL;
+ }
+
+done:
+ git_annotated_commit_free(base);
+ git_iterator_free(base_iter);
+ git_iterator_free(our_iter);
+ git_iterator_free(their_iter);
+ return error;
+}
+
+
+int git_merge_commits(
+ git_index **out,
+ git_repository *repo,
+ const git_commit *our_commit,
+ const git_commit *their_commit,
+ const git_merge_options *opts)
+{
+ git_annotated_commit *ours = NULL, *theirs = NULL, *base = NULL;
+ int error = 0;
+
+ if ((error = git_annotated_commit_from_commit(&ours, (git_commit *)our_commit)) < 0 ||
+ (error = git_annotated_commit_from_commit(&theirs, (git_commit *)their_commit)) < 0)
+ goto done;
+
+ error = merge_annotated_commits(out, &base, repo, ours, theirs, 0, opts);
+
+done:
+ git_annotated_commit_free(ours);
+ git_annotated_commit_free(theirs);
+ git_annotated_commit_free(base);
+ return error;
+}
+
+/* Merge setup / cleanup */
+
+static int write_merge_head(
+ git_repository *repo,
+ const git_annotated_commit *heads[],
+ size_t heads_len)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ git_str file_path = GIT_STR_INIT;
+ size_t i;
+ int error = 0;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(heads);
+
+ if ((error = git_str_joinpath(&file_path, repo->gitdir, GIT_MERGE_HEAD_FILE)) < 0 ||
+ (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_CREATE_LEADING_DIRS, GIT_MERGE_FILE_MODE)) < 0)
+ goto cleanup;
+
+ for (i = 0; i < heads_len; i++) {
+ if ((error = git_filebuf_printf(&file, "%s\n", heads[i]->id_str)) < 0)
+ goto cleanup;
+ }
+
+ error = git_filebuf_commit(&file);
+
+cleanup:
+ if (error < 0)
+ git_filebuf_cleanup(&file);
+
+ git_str_dispose(&file_path);
+
+ return error;
+}
+
+static int write_merge_mode(git_repository *repo)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ git_str file_path = GIT_STR_INIT;
+ int error = 0;
+
+ GIT_ASSERT_ARG(repo);
+
+ if ((error = git_str_joinpath(&file_path, repo->gitdir, GIT_MERGE_MODE_FILE)) < 0 ||
+ (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_CREATE_LEADING_DIRS, GIT_MERGE_FILE_MODE)) < 0)
+ goto cleanup;
+
+ if ((error = git_filebuf_write(&file, "no-ff", 5)) < 0)
+ goto cleanup;
+
+ error = git_filebuf_commit(&file);
+
+cleanup:
+ if (error < 0)
+ git_filebuf_cleanup(&file);
+
+ git_str_dispose(&file_path);
+
+ return error;
+}
+
+struct merge_msg_entry {
+ const git_annotated_commit *merge_head;
+ bool written;
+};
+
+static int msg_entry_is_branch(
+ const struct merge_msg_entry *entry,
+ git_vector *entries)
+{
+ GIT_UNUSED(entries);
+
+ return (entry->written == 0 &&
+ entry->merge_head->remote_url == NULL &&
+ entry->merge_head->ref_name != NULL &&
+ git__strncmp(GIT_REFS_HEADS_DIR, entry->merge_head->ref_name, strlen(GIT_REFS_HEADS_DIR)) == 0);
+}
+
+static int msg_entry_is_tracking(
+ const struct merge_msg_entry *entry,
+ git_vector *entries)
+{
+ GIT_UNUSED(entries);
+
+ return (entry->written == 0 &&
+ entry->merge_head->remote_url == NULL &&
+ entry->merge_head->ref_name != NULL &&
+ git__strncmp(GIT_REFS_REMOTES_DIR, entry->merge_head->ref_name, strlen(GIT_REFS_REMOTES_DIR)) == 0);
+}
+
+static int msg_entry_is_tag(
+ const struct merge_msg_entry *entry,
+ git_vector *entries)
+{
+ GIT_UNUSED(entries);
+
+ return (entry->written == 0 &&
+ entry->merge_head->remote_url == NULL &&
+ entry->merge_head->ref_name != NULL &&
+ git__strncmp(GIT_REFS_TAGS_DIR, entry->merge_head->ref_name, strlen(GIT_REFS_TAGS_DIR)) == 0);
+}
+
+static int msg_entry_is_remote(
+ const struct merge_msg_entry *entry,
+ git_vector *entries)
+{
+ if (entry->written == 0 &&
+ entry->merge_head->remote_url != NULL &&
+ entry->merge_head->ref_name != NULL &&
+ git__strncmp(GIT_REFS_HEADS_DIR, entry->merge_head->ref_name, strlen(GIT_REFS_HEADS_DIR)) == 0)
+ {
+ struct merge_msg_entry *existing;
+
+ /* Match only branches from the same remote */
+ if (entries->length == 0)
+ return 1;
+
+ existing = git_vector_get(entries, 0);
+
+ return (git__strcmp(existing->merge_head->remote_url,
+ entry->merge_head->remote_url) == 0);
+ }
+
+ return 0;
+}
+
+static int msg_entry_is_oid(
+ const struct merge_msg_entry *merge_msg_entry)
+{
+ return (merge_msg_entry->written == 0 &&
+ merge_msg_entry->merge_head->ref_name == NULL &&
+ merge_msg_entry->merge_head->remote_url == NULL);
+}
+
+static int merge_msg_entry_written(
+ const struct merge_msg_entry *merge_msg_entry)
+{
+ return (merge_msg_entry->written == 1);
+}
+
+static int merge_msg_entries(
+ git_vector *v,
+ const struct merge_msg_entry *entries,
+ size_t len,
+ int (*match)(const struct merge_msg_entry *entry, git_vector *entries))
+{
+ size_t i;
+ int matches, total = 0;
+
+ git_vector_clear(v);
+
+ for (i = 0; i < len; i++) {
+ if ((matches = match(&entries[i], v)) < 0)
+ return matches;
+ else if (!matches)
+ continue;
+
+ git_vector_insert(v, (struct merge_msg_entry *)&entries[i]);
+ total++;
+ }
+
+ return total;
+}
+
+static int merge_msg_write_entries(
+ git_filebuf *file,
+ git_vector *entries,
+ const char *item_name,
+ const char *item_plural_name,
+ size_t ref_name_skip,
+ const char *source,
+ char sep)
+{
+ struct merge_msg_entry *entry;
+ size_t i;
+ int error = 0;
+
+ if (entries->length == 0)
+ return 0;
+
+ if (sep && (error = git_filebuf_printf(file, "%c ", sep)) < 0)
+ goto done;
+
+ if ((error = git_filebuf_printf(file, "%s ",
+ (entries->length == 1) ? item_name : item_plural_name)) < 0)
+ goto done;
+
+ git_vector_foreach(entries, i, entry) {
+ if (i > 0 &&
+ (error = git_filebuf_printf(file, "%s", (i == entries->length - 1) ? " and " : ", ")) < 0)
+ goto done;
+
+ if ((error = git_filebuf_printf(file, "'%s'", entry->merge_head->ref_name + ref_name_skip)) < 0)
+ goto done;
+
+ entry->written = 1;
+ }
+
+ if (source)
+ error = git_filebuf_printf(file, " of %s", source);
+
+done:
+ return error;
+}
+
+static int merge_msg_write_branches(
+ git_filebuf *file,
+ git_vector *entries,
+ char sep)
+{
+ return merge_msg_write_entries(file, entries,
+ "branch", "branches", strlen(GIT_REFS_HEADS_DIR), NULL, sep);
+}
+
+static int merge_msg_write_tracking(
+ git_filebuf *file,
+ git_vector *entries,
+ char sep)
+{
+ return merge_msg_write_entries(file, entries,
+ "remote-tracking branch", "remote-tracking branches", 0, NULL, sep);
+}
+
+static int merge_msg_write_tags(
+ git_filebuf *file,
+ git_vector *entries,
+ char sep)
+{
+ return merge_msg_write_entries(file, entries,
+ "tag", "tags", strlen(GIT_REFS_TAGS_DIR), NULL, sep);
+}
+
+static int merge_msg_write_remotes(
+ git_filebuf *file,
+ git_vector *entries,
+ char sep)
+{
+ const char *source;
+
+ if (entries->length == 0)
+ return 0;
+
+ source = ((struct merge_msg_entry *)entries->contents[0])->merge_head->remote_url;
+
+ return merge_msg_write_entries(file, entries,
+ "branch", "branches", strlen(GIT_REFS_HEADS_DIR), source, sep);
+}
+
+static int write_merge_msg(
+ git_repository *repo,
+ const git_annotated_commit *heads[],
+ size_t heads_len)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ git_str file_path = GIT_STR_INIT;
+ struct merge_msg_entry *entries;
+ git_vector matching = GIT_VECTOR_INIT;
+ size_t i;
+ char sep = 0;
+ int error = 0;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(heads);
+
+ entries = git__calloc(heads_len, sizeof(struct merge_msg_entry));
+ GIT_ERROR_CHECK_ALLOC(entries);
+
+ if (git_vector_init(&matching, heads_len, NULL) < 0) {
+ git__free(entries);
+ return -1;
+ }
+
+ for (i = 0; i < heads_len; i++)
+ entries[i].merge_head = heads[i];
+
+ if ((error = git_str_joinpath(&file_path, repo->gitdir, GIT_MERGE_MSG_FILE)) < 0 ||
+ (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_CREATE_LEADING_DIRS, GIT_MERGE_FILE_MODE)) < 0 ||
+ (error = git_filebuf_write(&file, "Merge ", 6)) < 0)
+ goto cleanup;
+
+ /*
+ * This is to emulate the format of MERGE_MSG by core git.
+ *
+ * Core git will write all the commits specified by OID, in the order
+ * provided, until the first named branch or tag is reached, at which
+ * point all branches will be written in the order provided, then all
+ * tags, then all remote tracking branches and finally all commits that
+ * were specified by OID that were not already written.
+ *
+ * Yes. Really.
+ */
+ for (i = 0; i < heads_len; i++) {
+ if (!msg_entry_is_oid(&entries[i]))
+ break;
+
+ if ((error = git_filebuf_printf(&file,
+ "%scommit '%s'", (i > 0) ? "; " : "",
+ entries[i].merge_head->id_str)) < 0)
+ goto cleanup;
+
+ entries[i].written = 1;
+ }
+
+ if (i)
+ sep = ';';
+
+ if ((error = merge_msg_entries(&matching, entries, heads_len, msg_entry_is_branch)) < 0 ||
+ (error = merge_msg_write_branches(&file, &matching, sep)) < 0)
+ goto cleanup;
+
+ if (matching.length)
+ sep =',';
+
+ if ((error = merge_msg_entries(&matching, entries, heads_len, msg_entry_is_tracking)) < 0 ||
+ (error = merge_msg_write_tracking(&file, &matching, sep)) < 0)
+ goto cleanup;
+
+ if (matching.length)
+ sep =',';
+
+ if ((error = merge_msg_entries(&matching, entries, heads_len, msg_entry_is_tag)) < 0 ||
+ (error = merge_msg_write_tags(&file, &matching, sep)) < 0)
+ goto cleanup;
+
+ if (matching.length)
+ sep =',';
+
+ /* We should never be called with multiple remote branches, but handle
+ * it in case we are... */
+ while ((error = merge_msg_entries(&matching, entries, heads_len, msg_entry_is_remote)) > 0) {
+ if ((error = merge_msg_write_remotes(&file, &matching, sep)) < 0)
+ goto cleanup;
+
+ if (matching.length)
+ sep =',';
+ }
+
+ if (error < 0)
+ goto cleanup;
+
+ for (i = 0; i < heads_len; i++) {
+ if (merge_msg_entry_written(&entries[i]))
+ continue;
+
+ if ((error = git_filebuf_printf(&file, "; commit '%s'",
+ entries[i].merge_head->id_str)) < 0)
+ goto cleanup;
+ }
+
+ if ((error = git_filebuf_printf(&file, "\n")) < 0 ||
+ (error = git_filebuf_commit(&file)) < 0)
+ goto cleanup;
+
+cleanup:
+ if (error < 0)
+ git_filebuf_cleanup(&file);
+
+ git_str_dispose(&file_path);
+
+ git_vector_free(&matching);
+ git__free(entries);
+
+ return error;
+}
+
+int git_merge__setup(
+ git_repository *repo,
+ const git_annotated_commit *our_head,
+ const git_annotated_commit *heads[],
+ size_t heads_len)
+{
+ int error = 0;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(our_head);
+ GIT_ASSERT_ARG(heads);
+
+ if ((error = git_repository__set_orig_head(repo, git_annotated_commit_id(our_head))) == 0 &&
+ (error = write_merge_head(repo, heads, heads_len)) == 0 &&
+ (error = write_merge_mode(repo)) == 0) {
+ error = write_merge_msg(repo, heads, heads_len);
+ }
+
+ return error;
+}
+
+/* Merge branches */
+
+static int merge_ancestor_head(
+ git_annotated_commit **ancestor_head,
+ git_repository *repo,
+ const git_annotated_commit *our_head,
+ const git_annotated_commit **their_heads,
+ size_t their_heads_len)
+{
+ git_oid *oids, ancestor_oid;
+ size_t i, alloc_len;
+ int error = 0;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(our_head);
+ GIT_ASSERT_ARG(their_heads);
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, their_heads_len, 1);
+ oids = git__calloc(alloc_len, sizeof(git_oid));
+ GIT_ERROR_CHECK_ALLOC(oids);
+
+ git_oid_cpy(&oids[0], git_commit_id(our_head->commit));
+
+ for (i = 0; i < their_heads_len; i++)
+ git_oid_cpy(&oids[i + 1], git_annotated_commit_id(their_heads[i]));
+
+ if ((error = git_merge_base_many(&ancestor_oid, repo, their_heads_len + 1, oids)) < 0)
+ goto on_error;
+
+ error = git_annotated_commit_lookup(ancestor_head, repo, &ancestor_oid);
+
+on_error:
+ git__free(oids);
+ return error;
+}
+
+static const char *merge_their_label(const char *branchname)
+{
+ const char *slash;
+
+ if ((slash = strrchr(branchname, '/')) == NULL)
+ return branchname;
+
+ if (*(slash+1) == '\0')
+ return "theirs";
+
+ return slash+1;
+}
+
+static int merge_normalize_checkout_opts(
+ git_checkout_options *out,
+ git_repository *repo,
+ const git_checkout_options *given_checkout_opts,
+ unsigned int checkout_strategy,
+ git_annotated_commit *ancestor,
+ const git_annotated_commit *our_head,
+ const git_annotated_commit **their_heads,
+ size_t their_heads_len)
+{
+ git_checkout_options default_checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
+ int error = 0;
+
+ GIT_UNUSED(repo);
+
+ if (given_checkout_opts != NULL)
+ memcpy(out, given_checkout_opts, sizeof(git_checkout_options));
+ else
+ memcpy(out, &default_checkout_opts, sizeof(git_checkout_options));
+
+ out->checkout_strategy = checkout_strategy;
+
+ if (!out->ancestor_label) {
+ if (ancestor && ancestor->type == GIT_ANNOTATED_COMMIT_REAL)
+ out->ancestor_label = git_commit_summary(ancestor->commit);
+ else if (ancestor)
+ out->ancestor_label = "merged common ancestors";
+ else
+ out->ancestor_label = "empty base";
+ }
+
+ if (!out->our_label) {
+ if (our_head && our_head->ref_name)
+ out->our_label = our_head->ref_name;
+ else
+ out->our_label = "ours";
+ }
+
+ if (!out->their_label) {
+ if (their_heads_len == 1 && their_heads[0]->ref_name)
+ out->their_label = merge_their_label(their_heads[0]->ref_name);
+ else if (their_heads_len == 1)
+ out->their_label = their_heads[0]->id_str;
+ else
+ out->their_label = "theirs";
+ }
+
+ return error;
+}
+
+static int merge_check_index(size_t *conflicts, git_repository *repo, git_index *index_new, git_vector *merged_paths)
+{
+ git_tree *head_tree = NULL;
+ git_index *index_repo = NULL;
+ git_iterator *iter_repo = NULL, *iter_new = NULL;
+ git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_diff *staged_diff_list = NULL, *index_diff_list = NULL;
+ git_diff_delta *delta;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_vector staged_paths = GIT_VECTOR_INIT;
+ size_t i;
+ int error = 0;
+
+ GIT_UNUSED(merged_paths);
+
+ *conflicts = 0;
+
+ /* No staged changes may exist unless the change staged is identical to
+ * the result of the merge. This allows one to apply to merge manually,
+ * then run merge. Any other staged change would be overwritten by
+ * a reset merge.
+ */
+ if ((error = git_repository_head_tree(&head_tree, repo)) < 0 ||
+ (error = git_repository_index(&index_repo, repo)) < 0 ||
+ (error = git_diff_tree_to_index(&staged_diff_list, repo, head_tree, index_repo, &opts)) < 0)
+ goto done;
+
+ if (staged_diff_list->deltas.length == 0)
+ goto done;
+
+ git_vector_foreach(&staged_diff_list->deltas, i, delta) {
+ if ((error = git_vector_insert(&staged_paths, (char *)delta->new_file.path)) < 0)
+ goto done;
+ }
+
+ iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+ iter_opts.pathlist.strings = (char **)staged_paths.contents;
+ iter_opts.pathlist.count = staged_paths.length;
+
+ if ((error = git_iterator_for_index(&iter_repo, repo, index_repo, &iter_opts)) < 0 ||
+ (error = git_iterator_for_index(&iter_new, repo, index_new, &iter_opts)) < 0 ||
+ (error = git_diff__from_iterators(&index_diff_list, repo, iter_repo, iter_new, &opts)) < 0)
+ goto done;
+
+ *conflicts = index_diff_list->deltas.length;
+
+done:
+ git_tree_free(head_tree);
+ git_index_free(index_repo);
+ git_iterator_free(iter_repo);
+ git_iterator_free(iter_new);
+ git_diff_free(staged_diff_list);
+ git_diff_free(index_diff_list);
+ git_vector_free(&staged_paths);
+
+ return error;
+}
+
+static int merge_check_workdir(size_t *conflicts, git_repository *repo, git_index *index_new, git_vector *merged_paths)
+{
+ git_diff *wd_diff_list = NULL;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ int error = 0;
+
+ GIT_UNUSED(index_new);
+
+ *conflicts = 0;
+
+ /* We need to have merged at least 1 file for the possibility to exist to
+ * have conflicts with the workdir. Passing 0 as the pathspec count parameter
+ * will consider all files in the working directory, that is, we may detect
+ * a conflict if there were untracked files in the workdir prior to starting
+ * the merge. This typically happens when cherry-picking a commit whose
+ * changes have already been applied.
+ */
+ if (merged_paths->length == 0)
+ return 0;
+
+ opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
+
+ /* Workdir changes may exist iff they do not conflict with changes that
+ * will be applied by the merge (including conflicts). Ensure that there
+ * are no changes in the workdir to these paths.
+ */
+ opts.flags |= GIT_DIFF_DISABLE_PATHSPEC_MATCH;
+ opts.pathspec.count = merged_paths->length;
+ opts.pathspec.strings = (char **)merged_paths->contents;
+ opts.ignore_submodules = GIT_SUBMODULE_IGNORE_ALL;
+
+ if ((error = git_diff_index_to_workdir(&wd_diff_list, repo, NULL, &opts)) < 0)
+ goto done;
+
+ *conflicts = wd_diff_list->deltas.length;
+
+done:
+ git_diff_free(wd_diff_list);
+
+ return error;
+}
+
+int git_merge__check_result(git_repository *repo, git_index *index_new)
+{
+ git_tree *head_tree = NULL;
+ git_iterator *iter_head = NULL, *iter_new = NULL;
+ git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_diff *merged_list = NULL;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_delta *delta;
+ git_vector paths = GIT_VECTOR_INIT;
+ size_t i, index_conflicts = 0, wd_conflicts = 0, conflicts;
+ const git_index_entry *e;
+ int error = 0;
+
+ iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ if ((error = git_repository_head_tree(&head_tree, repo)) < 0 ||
+ (error = git_iterator_for_tree(&iter_head, head_tree, &iter_opts)) < 0 ||
+ (error = git_iterator_for_index(&iter_new, repo, index_new, &iter_opts)) < 0 ||
+ (error = git_diff__from_iterators(&merged_list, repo, iter_head, iter_new, &opts)) < 0)
+ goto done;
+
+ git_vector_foreach(&merged_list->deltas, i, delta) {
+ if ((error = git_vector_insert(&paths, (char *)delta->new_file.path)) < 0)
+ goto done;
+ }
+
+ for (i = 0; i < git_index_entrycount(index_new); i++) {
+ e = git_index_get_byindex(index_new, i);
+
+ if (git_index_entry_is_conflict(e) &&
+ (git_vector_last(&paths) == NULL ||
+ strcmp(git_vector_last(&paths), e->path) != 0)) {
+
+ if ((error = git_vector_insert(&paths, (char *)e->path)) < 0)
+ goto done;
+ }
+ }
+
+ /* Make sure the index and workdir state do not prevent merging */
+ if ((error = merge_check_index(&index_conflicts, repo, index_new, &paths)) < 0 ||
+ (error = merge_check_workdir(&wd_conflicts, repo, index_new, &paths)) < 0)
+ goto done;
+
+ if ((conflicts = index_conflicts + wd_conflicts) > 0) {
+ git_error_set(GIT_ERROR_MERGE, "%" PRIuZ " uncommitted change%s would be overwritten by merge",
+ conflicts, (conflicts != 1) ? "s" : "");
+ error = GIT_ECONFLICT;
+ }
+
+done:
+ git_vector_free(&paths);
+ git_tree_free(head_tree);
+ git_iterator_free(iter_head);
+ git_iterator_free(iter_new);
+ git_diff_free(merged_list);
+
+ return error;
+}
+
+int git_merge__append_conflicts_to_merge_msg(
+ git_repository *repo,
+ git_index *index)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ git_str file_path = GIT_STR_INIT;
+ const char *last = NULL;
+ size_t i;
+ int error;
+
+ if (!git_index_has_conflicts(index))
+ return 0;
+
+ if ((error = git_str_joinpath(&file_path, repo->gitdir, GIT_MERGE_MSG_FILE)) < 0 ||
+ (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_APPEND, GIT_MERGE_FILE_MODE)) < 0)
+ goto cleanup;
+
+ git_filebuf_printf(&file, "\n#Conflicts:\n");
+
+ for (i = 0; i < git_index_entrycount(index); i++) {
+ const git_index_entry *e = git_index_get_byindex(index, i);
+
+ if (!git_index_entry_is_conflict(e))
+ continue;
+
+ if (last == NULL || strcmp(e->path, last) != 0)
+ git_filebuf_printf(&file, "#\t%s\n", e->path);
+
+ last = e->path;
+ }
+
+ error = git_filebuf_commit(&file);
+
+cleanup:
+ if (error < 0)
+ git_filebuf_cleanup(&file);
+
+ git_str_dispose(&file_path);
+
+ return error;
+}
+
+static int merge_state_cleanup(git_repository *repo)
+{
+ const char *state_files[] = {
+ GIT_MERGE_HEAD_FILE,
+ GIT_MERGE_MODE_FILE,
+ GIT_MERGE_MSG_FILE,
+ };
+
+ return git_repository__cleanup_files(repo, state_files, ARRAY_SIZE(state_files));
+}
+
+static int merge_heads(
+ git_annotated_commit **ancestor_head_out,
+ git_annotated_commit **our_head_out,
+ git_repository *repo,
+ git_reference *our_ref,
+ const git_annotated_commit **their_heads,
+ size_t their_heads_len)
+{
+ git_annotated_commit *ancestor_head = NULL, *our_head = NULL;
+ int error = 0;
+
+ *ancestor_head_out = NULL;
+ *our_head_out = NULL;
+
+ if ((error = git_annotated_commit_from_ref(&our_head, repo, our_ref)) < 0)
+ goto done;
+
+ if ((error = merge_ancestor_head(&ancestor_head, repo, our_head, their_heads, their_heads_len)) < 0) {
+ if (error != GIT_ENOTFOUND)
+ goto done;
+
+ git_error_clear();
+ error = 0;
+ }
+
+ *ancestor_head_out = ancestor_head;
+ *our_head_out = our_head;
+
+done:
+ if (error < 0) {
+ git_annotated_commit_free(ancestor_head);
+ git_annotated_commit_free(our_head);
+ }
+
+ return error;
+}
+
+static int merge_preference(git_merge_preference_t *out, git_repository *repo)
+{
+ git_config *config;
+ const char *value;
+ int bool_value, error = 0;
+
+ *out = GIT_MERGE_PREFERENCE_NONE;
+
+ if ((error = git_repository_config_snapshot(&config, repo)) < 0)
+ goto done;
+
+ if ((error = git_config_get_string(&value, config, "merge.ff")) < 0) {
+ if (error == GIT_ENOTFOUND) {
+ git_error_clear();
+ error = 0;
+ }
+
+ goto done;
+ }
+
+ if (git_config_parse_bool(&bool_value, value) == 0) {
+ if (!bool_value)
+ *out |= GIT_MERGE_PREFERENCE_NO_FASTFORWARD;
+ } else {
+ if (strcasecmp(value, "only") == 0)
+ *out |= GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY;
+ }
+
+done:
+ git_config_free(config);
+ return error;
+}
+
+int git_merge_analysis_for_ref(
+ git_merge_analysis_t *analysis_out,
+ git_merge_preference_t *preference_out,
+ git_repository *repo,
+ git_reference *our_ref,
+ const git_annotated_commit **their_heads,
+ size_t their_heads_len)
+{
+ git_annotated_commit *ancestor_head = NULL, *our_head = NULL;
+ int error = 0;
+ bool unborn;
+
+ GIT_ASSERT_ARG(analysis_out);
+ GIT_ASSERT_ARG(preference_out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(their_heads && their_heads_len > 0);
+
+ if (their_heads_len != 1) {
+ git_error_set(GIT_ERROR_MERGE, "can only merge a single branch");
+ error = -1;
+ goto done;
+ }
+
+ *analysis_out = GIT_MERGE_ANALYSIS_NONE;
+
+ if ((error = merge_preference(preference_out, repo)) < 0)
+ goto done;
+
+ if ((error = git_reference__is_unborn_head(&unborn, our_ref, repo)) < 0)
+ goto done;
+
+ if (unborn) {
+ *analysis_out |= GIT_MERGE_ANALYSIS_FASTFORWARD | GIT_MERGE_ANALYSIS_UNBORN;
+ error = 0;
+ goto done;
+ }
+
+ if ((error = merge_heads(&ancestor_head, &our_head, repo, our_ref, their_heads, their_heads_len)) < 0)
+ goto done;
+
+ /* We're up-to-date if we're trying to merge our own common ancestor. */
+ if (ancestor_head && git_oid_equal(
+ git_annotated_commit_id(ancestor_head), git_annotated_commit_id(their_heads[0])))
+ *analysis_out |= GIT_MERGE_ANALYSIS_UP_TO_DATE;
+
+ /* We're fastforwardable if we're our own common ancestor. */
+ else if (ancestor_head && git_oid_equal(
+ git_annotated_commit_id(ancestor_head), git_annotated_commit_id(our_head)))
+ *analysis_out |= GIT_MERGE_ANALYSIS_FASTFORWARD | GIT_MERGE_ANALYSIS_NORMAL;
+
+ /* Otherwise, just a normal merge is possible. */
+ else
+ *analysis_out |= GIT_MERGE_ANALYSIS_NORMAL;
+
+done:
+ git_annotated_commit_free(ancestor_head);
+ git_annotated_commit_free(our_head);
+ return error;
+}
+
+int git_merge_analysis(
+ git_merge_analysis_t *analysis_out,
+ git_merge_preference_t *preference_out,
+ git_repository *repo,
+ const git_annotated_commit **their_heads,
+ size_t their_heads_len)
+{
+ git_reference *head_ref = NULL;
+ int error = 0;
+
+ if ((error = git_reference_lookup(&head_ref, repo, GIT_HEAD_FILE)) < 0) {
+ git_error_set(GIT_ERROR_MERGE, "failed to lookup HEAD reference");
+ return error;
+ }
+
+ error = git_merge_analysis_for_ref(analysis_out, preference_out, repo, head_ref, their_heads, their_heads_len);
+
+ git_reference_free(head_ref);
+
+ return error;
+}
+
+int git_merge(
+ git_repository *repo,
+ const git_annotated_commit **their_heads,
+ size_t their_heads_len,
+ const git_merge_options *merge_opts,
+ const git_checkout_options *given_checkout_opts)
+{
+ git_reference *our_ref = NULL;
+ git_checkout_options checkout_opts;
+ git_annotated_commit *our_head = NULL, *base = NULL;
+ git_index *repo_index = NULL, *index = NULL;
+ git_indexwriter indexwriter = GIT_INDEXWRITER_INIT;
+ unsigned int checkout_strategy;
+ int error = 0;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(their_heads && their_heads_len > 0);
+
+ if (their_heads_len != 1) {
+ git_error_set(GIT_ERROR_MERGE, "can only merge a single branch");
+ return -1;
+ }
+
+ if ((error = git_repository__ensure_not_bare(repo, "merge")) < 0)
+ goto done;
+
+ checkout_strategy = given_checkout_opts ?
+ given_checkout_opts->checkout_strategy :
+ GIT_CHECKOUT_SAFE;
+
+ if ((error = git_indexwriter_init_for_operation(&indexwriter, repo,
+ &checkout_strategy)) < 0)
+ goto done;
+
+ if ((error = git_repository_index(&repo_index, repo) < 0) ||
+ (error = git_index_read(repo_index, 0) < 0))
+ goto done;
+
+ /* Write the merge setup files to the repository. */
+ if ((error = git_annotated_commit_from_head(&our_head, repo)) < 0 ||
+ (error = git_merge__setup(repo, our_head, their_heads,
+ their_heads_len)) < 0)
+ goto done;
+
+ /* TODO: octopus */
+
+ if ((error = merge_annotated_commits(&index, &base, repo, our_head,
+ (git_annotated_commit *)their_heads[0], 0, merge_opts)) < 0 ||
+ (error = git_merge__check_result(repo, index)) < 0 ||
+ (error = git_merge__append_conflicts_to_merge_msg(repo, index)) < 0)
+ goto done;
+
+ /* check out the merge results */
+
+ if ((error = merge_normalize_checkout_opts(&checkout_opts, repo,
+ given_checkout_opts, checkout_strategy,
+ base, our_head, their_heads, their_heads_len)) < 0 ||
+ (error = git_checkout_index(repo, index, &checkout_opts)) < 0)
+ goto done;
+
+ error = git_indexwriter_commit(&indexwriter);
+
+done:
+ if (error < 0)
+ merge_state_cleanup(repo);
+
+ git_indexwriter_cleanup(&indexwriter);
+ git_index_free(index);
+ git_annotated_commit_free(our_head);
+ git_annotated_commit_free(base);
+ git_reference_free(our_ref);
+ git_index_free(repo_index);
+
+ return error;
+}
+
+int git_merge_options_init(git_merge_options *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_merge_options, GIT_MERGE_OPTIONS_INIT);
+ return 0;
+}
+
+#ifndef GIT_DEPRECATE_HARD
+int git_merge_init_options(git_merge_options *opts, unsigned int version)
+{
+ return git_merge_options_init(opts, version);
+}
+#endif
+
+int git_merge_file_input_init(git_merge_file_input *input, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ input, version, git_merge_file_input, GIT_MERGE_FILE_INPUT_INIT);
+ return 0;
+}
+
+#ifndef GIT_DEPRECATE_HARD
+int git_merge_file_init_input(git_merge_file_input *input, unsigned int version)
+{
+ return git_merge_file_input_init(input, version);
+}
+#endif
+
+int git_merge_file_options_init(
+ git_merge_file_options *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_merge_file_options, GIT_MERGE_FILE_OPTIONS_INIT);
+ return 0;
+}
+
+#ifndef GIT_DEPRECATE_HARD
+int git_merge_file_init_options(
+ git_merge_file_options *opts, unsigned int version)
+{
+ return git_merge_file_options_init(opts, version);
+}
+#endif
diff --git a/src/libgit2/merge.h b/src/libgit2/merge.h
new file mode 100644
index 0000000..2393290
--- /dev/null
+++ b/src/libgit2/merge.h
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_merge_h__
+#define INCLUDE_merge_h__
+
+#include "common.h"
+
+#include "vector.h"
+#include "commit_list.h"
+#include "pool.h"
+#include "iterator.h"
+
+#include "git2/types.h"
+#include "git2/merge.h"
+#include "git2/sys/merge.h"
+
+#define GIT_MERGE_MSG_FILE "MERGE_MSG"
+#define GIT_MERGE_MODE_FILE "MERGE_MODE"
+#define GIT_MERGE_FILE_MODE 0666
+
+#define GIT_MERGE_DEFAULT_RENAME_THRESHOLD 50
+#define GIT_MERGE_DEFAULT_TARGET_LIMIT 1000
+
+/** Types of changes when files are merged from branch to branch. */
+typedef enum {
+ /* No conflict - a change only occurs in one branch. */
+ GIT_MERGE_DIFF_NONE = 0,
+
+ /* Occurs when a file is modified in both branches. */
+ GIT_MERGE_DIFF_BOTH_MODIFIED = (1 << 0),
+
+ /* Occurs when a file is added in both branches. */
+ GIT_MERGE_DIFF_BOTH_ADDED = (1 << 1),
+
+ /* Occurs when a file is deleted in both branches. */
+ GIT_MERGE_DIFF_BOTH_DELETED = (1 << 2),
+
+ /* Occurs when a file is modified in one branch and deleted in the other. */
+ GIT_MERGE_DIFF_MODIFIED_DELETED = (1 << 3),
+
+ /* Occurs when a file is renamed in one branch and modified in the other. */
+ GIT_MERGE_DIFF_RENAMED_MODIFIED = (1 << 4),
+
+ /* Occurs when a file is renamed in one branch and deleted in the other. */
+ GIT_MERGE_DIFF_RENAMED_DELETED = (1 << 5),
+
+ /* Occurs when a file is renamed in one branch and a file with the same
+ * name is added in the other. Eg, A->B and new file B. Core git calls
+ * this a "rename/delete". */
+ GIT_MERGE_DIFF_RENAMED_ADDED = (1 << 6),
+
+ /* Occurs when both a file is renamed to the same name in the ours and
+ * theirs branches. Eg, A->B and A->B in both. Automergeable. */
+ GIT_MERGE_DIFF_BOTH_RENAMED = (1 << 7),
+
+ /* Occurs when a file is renamed to different names in the ours and theirs
+ * branches. Eg, A->B and A->C. */
+ GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2 = (1 << 8),
+
+ /* Occurs when two files are renamed to the same name in the ours and
+ * theirs branches. Eg, A->C and B->C. */
+ GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1 = (1 << 9),
+
+ /* Occurs when an item at a path in one branch is a directory, and an
+ * item at the same path in a different branch is a file. */
+ GIT_MERGE_DIFF_DIRECTORY_FILE = (1 << 10),
+
+ /* The child of a folder that is in a directory/file conflict. */
+ GIT_MERGE_DIFF_DF_CHILD = (1 << 11)
+} git_merge_diff_t;
+
+typedef struct {
+ git_repository *repo;
+ git_pool pool;
+
+ /* Vector of git_index_entry that represent the merged items that
+ * have been staged, either because only one side changed, or because
+ * the two changes were non-conflicting and mergeable. These items
+ * will be written as staged entries in the main index.
+ */
+ git_vector staged;
+
+ /* Vector of git_merge_diff entries that represent the conflicts that
+ * have not been automerged. These items will be written to high-stage
+ * entries in the main index.
+ */
+ git_vector conflicts;
+
+ /* Vector of git_merge_diff that have been automerged. These items
+ * will be written to the REUC when the index is produced.
+ */
+ git_vector resolved;
+} git_merge_diff_list;
+
+/**
+ * Description of changes to one file across three trees.
+ */
+typedef struct {
+ git_merge_diff_t type;
+
+ git_index_entry ancestor_entry;
+
+ git_index_entry our_entry;
+ git_delta_t our_status;
+
+ git_index_entry their_entry;
+ git_delta_t their_status;
+
+} git_merge_diff;
+
+int git_merge__bases_many(
+ git_commit_list **out,
+ git_revwalk *walk,
+ git_commit_list_node *one,
+ git_vector *twos,
+ uint32_t minimum_generation);
+
+/*
+ * Three-way tree differencing
+ */
+
+git_merge_diff_list *git_merge_diff_list__alloc(git_repository *repo);
+
+int git_merge_diff_list__find_differences(
+ git_merge_diff_list *merge_diff_list,
+ git_iterator *ancestor_iterator,
+ git_iterator *ours_iter,
+ git_iterator *theirs_iter);
+
+int git_merge_diff_list__find_renames(git_repository *repo, git_merge_diff_list *merge_diff_list, const git_merge_options *opts);
+
+void git_merge_diff_list__free(git_merge_diff_list *diff_list);
+
+/* Merge metadata setup */
+
+int git_merge__setup(
+ git_repository *repo,
+ const git_annotated_commit *our_head,
+ const git_annotated_commit *heads[],
+ size_t heads_len);
+
+int git_merge__iterators(
+ git_index **out,
+ git_repository *repo,
+ git_iterator *ancestor_iter,
+ git_iterator *our_iter,
+ git_iterator *their_iter,
+ const git_merge_options *given_opts);
+
+int git_merge__check_result(git_repository *repo, git_index *index_new);
+
+int git_merge__append_conflicts_to_merge_msg(git_repository *repo, git_index *index);
+
+/* Merge files */
+
+GIT_INLINE(const char *) git_merge_file__best_path(
+ const char *ancestor,
+ const char *ours,
+ const char *theirs)
+{
+ if (!ancestor) {
+ if (ours && theirs && strcmp(ours, theirs) == 0)
+ return ours;
+
+ return NULL;
+ }
+
+ if (ours && strcmp(ancestor, ours) == 0)
+ return theirs;
+ else if(theirs && strcmp(ancestor, theirs) == 0)
+ return ours;
+
+ return NULL;
+}
+
+GIT_INLINE(uint32_t) git_merge_file__best_mode(
+ uint32_t ancestor, uint32_t ours, uint32_t theirs)
+{
+ /*
+ * If ancestor didn't exist and either ours or theirs is executable,
+ * assume executable. Otherwise, if any mode changed from the ancestor,
+ * use that one.
+ */
+ if (!ancestor) {
+ if (ours == GIT_FILEMODE_BLOB_EXECUTABLE ||
+ theirs == GIT_FILEMODE_BLOB_EXECUTABLE)
+ return GIT_FILEMODE_BLOB_EXECUTABLE;
+
+ return GIT_FILEMODE_BLOB;
+ } else if (ours && theirs) {
+ if (ancestor == ours)
+ return theirs;
+
+ return ours;
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/src/libgit2/merge_driver.c b/src/libgit2/merge_driver.c
new file mode 100644
index 0000000..19b35ac
--- /dev/null
+++ b/src/libgit2/merge_driver.c
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "merge_driver.h"
+
+#include "vector.h"
+#include "runtime.h"
+#include "merge.h"
+#include "git2/merge.h"
+#include "git2/sys/merge.h"
+
+static const char *merge_driver_name__text = "text";
+static const char *merge_driver_name__union = "union";
+static const char *merge_driver_name__binary = "binary";
+
+struct merge_driver_registry {
+ git_rwlock lock;
+ git_vector drivers;
+};
+
+typedef struct {
+ git_merge_driver *driver;
+ int initialized;
+ char name[GIT_FLEX_ARRAY];
+} git_merge_driver_entry;
+
+static struct merge_driver_registry merge_driver_registry;
+
+static void git_merge_driver_global_shutdown(void);
+
+git_repository *git_merge_driver_source_repo(
+ const git_merge_driver_source *src)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(src, NULL);
+ return src->repo;
+}
+
+const git_index_entry *git_merge_driver_source_ancestor(
+ const git_merge_driver_source *src)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(src, NULL);
+ return src->ancestor;
+}
+
+const git_index_entry *git_merge_driver_source_ours(
+ const git_merge_driver_source *src)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(src, NULL);
+ return src->ours;
+}
+
+const git_index_entry *git_merge_driver_source_theirs(
+ const git_merge_driver_source *src)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(src, NULL);
+ return src->theirs;
+}
+
+const git_merge_file_options *git_merge_driver_source_file_options(
+ const git_merge_driver_source *src)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(src, NULL);
+ return src->file_opts;
+}
+
+int git_merge_driver__builtin_apply(
+ git_merge_driver *self,
+ const char **path_out,
+ uint32_t *mode_out,
+ git_buf *merged_out,
+ const char *filter_name,
+ const git_merge_driver_source *src)
+{
+ git_merge_driver__builtin *driver = (git_merge_driver__builtin *)self;
+ git_merge_file_options file_opts = GIT_MERGE_FILE_OPTIONS_INIT;
+ git_merge_file_result result = {0};
+ int error;
+
+ GIT_UNUSED(filter_name);
+
+ if (src->file_opts)
+ memcpy(&file_opts, src->file_opts, sizeof(git_merge_file_options));
+
+ if (driver->favor)
+ file_opts.favor = driver->favor;
+
+ if ((error = git_merge_file_from_index(&result, src->repo,
+ src->ancestor, src->ours, src->theirs, &file_opts)) < 0)
+ goto done;
+
+ if (!result.automergeable &&
+ !(file_opts.flags & GIT_MERGE_FILE_ACCEPT_CONFLICTS)) {
+ error = GIT_EMERGECONFLICT;
+ goto done;
+ }
+
+ *path_out = git_merge_file__best_path(
+ src->ancestor ? src->ancestor->path : NULL,
+ src->ours ? src->ours->path : NULL,
+ src->theirs ? src->theirs->path : NULL);
+
+ *mode_out = git_merge_file__best_mode(
+ src->ancestor ? src->ancestor->mode : 0,
+ src->ours ? src->ours->mode : 0,
+ src->theirs ? src->theirs->mode : 0);
+
+ merged_out->ptr = (char *)result.ptr;
+ merged_out->size = result.len;
+ merged_out->reserved = 0;
+ result.ptr = NULL;
+
+done:
+ git_merge_file_result_free(&result);
+ return error;
+}
+
+static int merge_driver_binary_apply(
+ git_merge_driver *self,
+ const char **path_out,
+ uint32_t *mode_out,
+ git_buf *merged_out,
+ const char *filter_name,
+ const git_merge_driver_source *src)
+{
+ GIT_UNUSED(self);
+ GIT_UNUSED(path_out);
+ GIT_UNUSED(mode_out);
+ GIT_UNUSED(merged_out);
+ GIT_UNUSED(filter_name);
+ GIT_UNUSED(src);
+
+ return GIT_EMERGECONFLICT;
+}
+
+static int merge_driver_entry_cmp(const void *a, const void *b)
+{
+ const git_merge_driver_entry *entry_a = a;
+ const git_merge_driver_entry *entry_b = b;
+
+ return strcmp(entry_a->name, entry_b->name);
+}
+
+static int merge_driver_entry_search(const void *a, const void *b)
+{
+ const char *name_a = a;
+ const git_merge_driver_entry *entry_b = b;
+
+ return strcmp(name_a, entry_b->name);
+}
+
+git_merge_driver__builtin git_merge_driver__text = {
+ {
+ GIT_MERGE_DRIVER_VERSION,
+ NULL,
+ NULL,
+ git_merge_driver__builtin_apply,
+ },
+ GIT_MERGE_FILE_FAVOR_NORMAL
+};
+
+git_merge_driver__builtin git_merge_driver__union = {
+ {
+ GIT_MERGE_DRIVER_VERSION,
+ NULL,
+ NULL,
+ git_merge_driver__builtin_apply,
+ },
+ GIT_MERGE_FILE_FAVOR_UNION
+};
+
+git_merge_driver git_merge_driver__binary = {
+ GIT_MERGE_DRIVER_VERSION,
+ NULL,
+ NULL,
+ merge_driver_binary_apply
+};
+
+/* Note: callers must lock the registry before calling this function */
+static int merge_driver_registry_insert(
+ const char *name, git_merge_driver *driver)
+{
+ git_merge_driver_entry *entry;
+
+ entry = git__calloc(1, sizeof(git_merge_driver_entry) + strlen(name) + 1);
+ GIT_ERROR_CHECK_ALLOC(entry);
+
+ strcpy(entry->name, name);
+ entry->driver = driver;
+
+ return git_vector_insert_sorted(
+ &merge_driver_registry.drivers, entry, NULL);
+}
+
+int git_merge_driver_global_init(void)
+{
+ int error;
+
+ if (git_rwlock_init(&merge_driver_registry.lock) < 0)
+ return -1;
+
+ if ((error = git_vector_init(&merge_driver_registry.drivers, 3,
+ merge_driver_entry_cmp)) < 0)
+ goto done;
+
+ if ((error = merge_driver_registry_insert(
+ merge_driver_name__text, &git_merge_driver__text.base)) < 0 ||
+ (error = merge_driver_registry_insert(
+ merge_driver_name__union, &git_merge_driver__union.base)) < 0 ||
+ (error = merge_driver_registry_insert(
+ merge_driver_name__binary, &git_merge_driver__binary)) < 0)
+ goto done;
+
+ error = git_runtime_shutdown_register(git_merge_driver_global_shutdown);
+
+done:
+ if (error < 0)
+ git_vector_free_deep(&merge_driver_registry.drivers);
+
+ return error;
+}
+
+static void git_merge_driver_global_shutdown(void)
+{
+ git_merge_driver_entry *entry;
+ size_t i;
+
+ if (git_rwlock_wrlock(&merge_driver_registry.lock) < 0)
+ return;
+
+ git_vector_foreach(&merge_driver_registry.drivers, i, entry) {
+ if (entry->driver->shutdown)
+ entry->driver->shutdown(entry->driver);
+
+ git__free(entry);
+ }
+
+ git_vector_free(&merge_driver_registry.drivers);
+
+ git_rwlock_wrunlock(&merge_driver_registry.lock);
+ git_rwlock_free(&merge_driver_registry.lock);
+}
+
+/* Note: callers must lock the registry before calling this function */
+static int merge_driver_registry_find(size_t *pos, const char *name)
+{
+ return git_vector_search2(pos, &merge_driver_registry.drivers,
+ merge_driver_entry_search, name);
+}
+
+/* Note: callers must lock the registry before calling this function */
+static git_merge_driver_entry *merge_driver_registry_lookup(
+ size_t *pos, const char *name)
+{
+ git_merge_driver_entry *entry = NULL;
+
+ if (!merge_driver_registry_find(pos, name))
+ entry = git_vector_get(&merge_driver_registry.drivers, *pos);
+
+ return entry;
+}
+
+int git_merge_driver_register(const char *name, git_merge_driver *driver)
+{
+ int error;
+
+ GIT_ASSERT_ARG(name);
+ GIT_ASSERT_ARG(driver);
+
+ if (git_rwlock_wrlock(&merge_driver_registry.lock) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to lock merge driver registry");
+ return -1;
+ }
+
+ if (!merge_driver_registry_find(NULL, name)) {
+ git_error_set(GIT_ERROR_MERGE, "attempt to reregister existing driver '%s'",
+ name);
+ error = GIT_EEXISTS;
+ goto done;
+ }
+
+ error = merge_driver_registry_insert(name, driver);
+
+done:
+ git_rwlock_wrunlock(&merge_driver_registry.lock);
+ return error;
+}
+
+int git_merge_driver_unregister(const char *name)
+{
+ git_merge_driver_entry *entry;
+ size_t pos;
+ int error = 0;
+
+ if (git_rwlock_wrlock(&merge_driver_registry.lock) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to lock merge driver registry");
+ return -1;
+ }
+
+ if ((entry = merge_driver_registry_lookup(&pos, name)) == NULL) {
+ git_error_set(GIT_ERROR_MERGE, "cannot find merge driver '%s' to unregister",
+ name);
+ error = GIT_ENOTFOUND;
+ goto done;
+ }
+
+ git_vector_remove(&merge_driver_registry.drivers, pos);
+
+ if (entry->initialized && entry->driver->shutdown) {
+ entry->driver->shutdown(entry->driver);
+ entry->initialized = false;
+ }
+
+ git__free(entry);
+
+done:
+ git_rwlock_wrunlock(&merge_driver_registry.lock);
+ return error;
+}
+
+git_merge_driver *git_merge_driver_lookup(const char *name)
+{
+ git_merge_driver_entry *entry;
+ size_t pos;
+ int error;
+
+ /* If we've decided the merge driver to use internally - and not
+ * based on user configuration (in merge_driver_name_for_path)
+ * then we can use a hardcoded name to compare instead of bothering
+ * to take a lock and look it up in the vector.
+ */
+ if (name == merge_driver_name__text)
+ return &git_merge_driver__text.base;
+ else if (name == merge_driver_name__binary)
+ return &git_merge_driver__binary;
+
+ if (git_rwlock_rdlock(&merge_driver_registry.lock) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to lock merge driver registry");
+ return NULL;
+ }
+
+ entry = merge_driver_registry_lookup(&pos, name);
+
+ git_rwlock_rdunlock(&merge_driver_registry.lock);
+
+ if (entry == NULL) {
+ git_error_set(GIT_ERROR_MERGE, "cannot use an unregistered filter");
+ return NULL;
+ }
+
+ if (!entry->initialized) {
+ if (entry->driver->initialize &&
+ (error = entry->driver->initialize(entry->driver)) < 0)
+ return NULL;
+
+ entry->initialized = 1;
+ }
+
+ return entry->driver;
+}
+
+static int merge_driver_name_for_path(
+ const char **out,
+ git_repository *repo,
+ const char *path,
+ const char *default_driver)
+{
+ const char *value;
+ int error;
+
+ *out = NULL;
+
+ if ((error = git_attr_get(&value, repo, 0, path, "merge")) < 0)
+ return error;
+
+ /* set: use the built-in 3-way merge driver ("text") */
+ if (GIT_ATTR_IS_TRUE(value))
+ *out = merge_driver_name__text;
+
+ /* unset: do not merge ("binary") */
+ else if (GIT_ATTR_IS_FALSE(value))
+ *out = merge_driver_name__binary;
+
+ else if (GIT_ATTR_IS_UNSPECIFIED(value) && default_driver)
+ *out = default_driver;
+
+ else if (GIT_ATTR_IS_UNSPECIFIED(value))
+ *out = merge_driver_name__text;
+
+ else
+ *out = value;
+
+ return 0;
+}
+
+
+GIT_INLINE(git_merge_driver *) merge_driver_lookup_with_wildcard(
+ const char *name)
+{
+ git_merge_driver *driver = git_merge_driver_lookup(name);
+
+ if (driver == NULL)
+ driver = git_merge_driver_lookup("*");
+
+ return driver;
+}
+
+int git_merge_driver_for_source(
+ const char **name_out,
+ git_merge_driver **driver_out,
+ const git_merge_driver_source *src)
+{
+ const char *path, *driver_name;
+ int error = 0;
+
+ path = git_merge_file__best_path(
+ src->ancestor ? src->ancestor->path : NULL,
+ src->ours ? src->ours->path : NULL,
+ src->theirs ? src->theirs->path : NULL);
+
+ if ((error = merge_driver_name_for_path(
+ &driver_name, src->repo, path, src->default_driver)) < 0)
+ return error;
+
+ *name_out = driver_name;
+ *driver_out = merge_driver_lookup_with_wildcard(driver_name);
+ return error;
+}
+
diff --git a/src/libgit2/merge_driver.h b/src/libgit2/merge_driver.h
new file mode 100644
index 0000000..6b7da52
--- /dev/null
+++ b/src/libgit2/merge_driver.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_merge_driver_h__
+#define INCLUDE_merge_driver_h__
+
+#include "common.h"
+
+#include "git2/merge.h"
+#include "git2/index.h"
+#include "git2/sys/merge.h"
+
+struct git_merge_driver_source {
+ git_repository *repo;
+ const char *default_driver;
+ const git_merge_file_options *file_opts;
+
+ const git_index_entry *ancestor;
+ const git_index_entry *ours;
+ const git_index_entry *theirs;
+};
+
+typedef struct git_merge_driver__builtin {
+ git_merge_driver base;
+ git_merge_file_favor_t favor;
+} git_merge_driver__builtin;
+
+extern int git_merge_driver_global_init(void);
+
+extern int git_merge_driver_for_path(
+ char **name_out,
+ git_merge_driver **driver_out,
+ git_repository *repo,
+ const char *path);
+
+/* Merge driver configuration */
+extern int git_merge_driver_for_source(
+ const char **name_out,
+ git_merge_driver **driver_out,
+ const git_merge_driver_source *src);
+
+extern int git_merge_driver__builtin_apply(
+ git_merge_driver *self,
+ const char **path_out,
+ uint32_t *mode_out,
+ git_buf *merged_out,
+ const char *filter_name,
+ const git_merge_driver_source *src);
+
+/* Merge driver for text files, performs a standard three-way merge */
+extern git_merge_driver__builtin git_merge_driver__text;
+
+/* Merge driver for union-style merging */
+extern git_merge_driver__builtin git_merge_driver__union;
+
+/* Merge driver for unmergeable (binary) files: always produces conflicts */
+extern git_merge_driver git_merge_driver__binary;
+
+#endif
diff --git a/src/libgit2/merge_file.c b/src/libgit2/merge_file.c
new file mode 100644
index 0000000..ffe83cf
--- /dev/null
+++ b/src/libgit2/merge_file.c
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+
+#include "repository.h"
+#include "posix.h"
+#include "futils.h"
+#include "index.h"
+#include "diff_xdiff.h"
+#include "merge.h"
+
+#include "git2/repository.h"
+#include "git2/object.h"
+#include "git2/index.h"
+#include "git2/merge.h"
+
+/* only examine the first 8000 bytes for binaryness.
+ * https://github.com/git/git/blob/77bd3ea9f54f1584147b594abc04c26ca516d987/xdiff-interface.c#L197
+ */
+#define GIT_MERGE_FILE_BINARY_SIZE 8000
+
+#define GIT_MERGE_FILE_SIDE_EXISTS(X) ((X)->mode != 0)
+
+static int merge_file_input_from_index(
+ git_merge_file_input *input_out,
+ git_odb_object **odb_object_out,
+ git_odb *odb,
+ const git_index_entry *entry)
+{
+ int error = 0;
+
+ GIT_ASSERT_ARG(input_out);
+ GIT_ASSERT_ARG(odb_object_out);
+ GIT_ASSERT_ARG(odb);
+ GIT_ASSERT_ARG(entry);
+
+ if ((error = git_odb_read(odb_object_out, odb, &entry->id)) < 0)
+ goto done;
+
+ input_out->path = entry->path;
+ input_out->mode = entry->mode;
+ input_out->ptr = (char *)git_odb_object_data(*odb_object_out);
+ input_out->size = git_odb_object_size(*odb_object_out);
+
+done:
+ return error;
+}
+
+static void merge_file_normalize_opts(
+ git_merge_file_options *out,
+ const git_merge_file_options *given_opts)
+{
+ if (given_opts)
+ memcpy(out, given_opts, sizeof(git_merge_file_options));
+ else {
+ git_merge_file_options default_opts = GIT_MERGE_FILE_OPTIONS_INIT;
+ memcpy(out, &default_opts, sizeof(git_merge_file_options));
+ }
+}
+
+static int merge_file__xdiff(
+ git_merge_file_result *out,
+ const git_merge_file_input *ancestor,
+ const git_merge_file_input *ours,
+ const git_merge_file_input *theirs,
+ const git_merge_file_options *given_opts)
+{
+ xmparam_t xmparam;
+ mmfile_t ancestor_mmfile = {0}, our_mmfile = {0}, their_mmfile = {0};
+ mmbuffer_t mmbuffer;
+ git_merge_file_options options = GIT_MERGE_FILE_OPTIONS_INIT;
+ const char *path;
+ int xdl_result;
+ int error = 0;
+
+ memset(out, 0x0, sizeof(git_merge_file_result));
+
+ merge_file_normalize_opts(&options, given_opts);
+
+ memset(&xmparam, 0x0, sizeof(xmparam_t));
+
+ if (ours->size > LONG_MAX ||
+ theirs->size > LONG_MAX ||
+ (ancestor && ancestor->size > LONG_MAX)) {
+ git_error_set(GIT_ERROR_MERGE, "failed to merge files");
+ error = -1;
+ goto done;
+ }
+
+ if (ancestor) {
+ xmparam.ancestor = (options.ancestor_label) ?
+ options.ancestor_label : ancestor->path;
+ ancestor_mmfile.ptr = (char *)ancestor->ptr;
+ ancestor_mmfile.size = (long)ancestor->size;
+ }
+
+ xmparam.file1 = (options.our_label) ?
+ options.our_label : ours->path;
+ our_mmfile.ptr = (char *)ours->ptr;
+ our_mmfile.size = (long)ours->size;
+
+ xmparam.file2 = (options.their_label) ?
+ options.their_label : theirs->path;
+ their_mmfile.ptr = (char *)theirs->ptr;
+ their_mmfile.size = (long)theirs->size;
+
+ if (options.favor == GIT_MERGE_FILE_FAVOR_OURS)
+ xmparam.favor = XDL_MERGE_FAVOR_OURS;
+ else if (options.favor == GIT_MERGE_FILE_FAVOR_THEIRS)
+ xmparam.favor = XDL_MERGE_FAVOR_THEIRS;
+ else if (options.favor == GIT_MERGE_FILE_FAVOR_UNION)
+ xmparam.favor = XDL_MERGE_FAVOR_UNION;
+
+ xmparam.level = (options.flags & GIT_MERGE_FILE_SIMPLIFY_ALNUM) ?
+ XDL_MERGE_ZEALOUS_ALNUM : XDL_MERGE_ZEALOUS;
+
+ if (options.flags & GIT_MERGE_FILE_STYLE_DIFF3)
+ xmparam.style = XDL_MERGE_DIFF3;
+ if (options.flags & GIT_MERGE_FILE_STYLE_ZDIFF3)
+ xmparam.style = XDL_MERGE_ZEALOUS_DIFF3;
+
+ if (options.flags & GIT_MERGE_FILE_IGNORE_WHITESPACE)
+ xmparam.xpp.flags |= XDF_IGNORE_WHITESPACE;
+ if (options.flags & GIT_MERGE_FILE_IGNORE_WHITESPACE_CHANGE)
+ xmparam.xpp.flags |= XDF_IGNORE_WHITESPACE_CHANGE;
+ if (options.flags & GIT_MERGE_FILE_IGNORE_WHITESPACE_EOL)
+ xmparam.xpp.flags |= XDF_IGNORE_WHITESPACE_AT_EOL;
+
+ if (options.flags & GIT_MERGE_FILE_DIFF_PATIENCE)
+ xmparam.xpp.flags |= XDF_PATIENCE_DIFF;
+
+ if (options.flags & GIT_MERGE_FILE_DIFF_MINIMAL)
+ xmparam.xpp.flags |= XDF_NEED_MINIMAL;
+
+ xmparam.marker_size = options.marker_size;
+
+ if ((xdl_result = xdl_merge(&ancestor_mmfile, &our_mmfile,
+ &their_mmfile, &xmparam, &mmbuffer)) < 0) {
+ git_error_set(GIT_ERROR_MERGE, "failed to merge files");
+ error = -1;
+ goto done;
+ }
+
+ path = git_merge_file__best_path(
+ ancestor ? ancestor->path : NULL,
+ ours->path,
+ theirs->path);
+
+ if (path != NULL && (out->path = git__strdup(path)) == NULL) {
+ error = -1;
+ goto done;
+ }
+
+ out->automergeable = (xdl_result == 0);
+ out->ptr = (const char *)mmbuffer.ptr;
+ out->len = mmbuffer.size;
+ out->mode = git_merge_file__best_mode(
+ ancestor ? ancestor->mode : 0,
+ ours->mode,
+ theirs->mode);
+
+done:
+ if (error < 0)
+ git_merge_file_result_free(out);
+
+ return error;
+}
+
+static bool merge_file__is_binary(const git_merge_file_input *file)
+{
+ size_t len = file ? file->size : 0;
+
+ if (len > GIT_XDIFF_MAX_SIZE)
+ return true;
+ if (len > GIT_MERGE_FILE_BINARY_SIZE)
+ len = GIT_MERGE_FILE_BINARY_SIZE;
+
+ return len ? (memchr(file->ptr, 0, len) != NULL) : false;
+}
+
+static int merge_file__binary(
+ git_merge_file_result *out,
+ const git_merge_file_input *ours,
+ const git_merge_file_input *theirs,
+ const git_merge_file_options *given_opts)
+{
+ const git_merge_file_input *favored = NULL;
+
+ memset(out, 0x0, sizeof(git_merge_file_result));
+
+ if (given_opts && given_opts->favor == GIT_MERGE_FILE_FAVOR_OURS)
+ favored = ours;
+ else if (given_opts && given_opts->favor == GIT_MERGE_FILE_FAVOR_THEIRS)
+ favored = theirs;
+ else
+ goto done;
+
+ if ((out->path = git__strdup(favored->path)) == NULL ||
+ (out->ptr = git__malloc(favored->size)) == NULL)
+ goto done;
+
+ memcpy((char *)out->ptr, favored->ptr, favored->size);
+ out->len = favored->size;
+ out->mode = favored->mode;
+ out->automergeable = 1;
+
+done:
+ return 0;
+}
+
+static int merge_file__from_inputs(
+ git_merge_file_result *out,
+ const git_merge_file_input *ancestor,
+ const git_merge_file_input *ours,
+ const git_merge_file_input *theirs,
+ const git_merge_file_options *given_opts)
+{
+ if (merge_file__is_binary(ancestor) ||
+ merge_file__is_binary(ours) ||
+ merge_file__is_binary(theirs))
+ return merge_file__binary(out, ours, theirs, given_opts);
+
+ return merge_file__xdiff(out, ancestor, ours, theirs, given_opts);
+}
+
+static git_merge_file_input *git_merge_file__normalize_inputs(
+ git_merge_file_input *out,
+ const git_merge_file_input *given)
+{
+ memcpy(out, given, sizeof(git_merge_file_input));
+
+ if (!out->path)
+ out->path = "file.txt";
+
+ if (!out->mode)
+ out->mode = 0100644;
+
+ return out;
+}
+
+int git_merge_file(
+ git_merge_file_result *out,
+ const git_merge_file_input *ancestor,
+ const git_merge_file_input *ours,
+ const git_merge_file_input *theirs,
+ const git_merge_file_options *options)
+{
+ git_merge_file_input inputs[3] = { {0} };
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(ours);
+ GIT_ASSERT_ARG(theirs);
+
+ memset(out, 0x0, sizeof(git_merge_file_result));
+
+ if (ancestor)
+ ancestor = git_merge_file__normalize_inputs(&inputs[0], ancestor);
+
+ ours = git_merge_file__normalize_inputs(&inputs[1], ours);
+ theirs = git_merge_file__normalize_inputs(&inputs[2], theirs);
+
+ return merge_file__from_inputs(out, ancestor, ours, theirs, options);
+}
+
+int git_merge_file_from_index(
+ git_merge_file_result *out,
+ git_repository *repo,
+ const git_index_entry *ancestor,
+ const git_index_entry *ours,
+ const git_index_entry *theirs,
+ const git_merge_file_options *options)
+{
+ git_merge_file_input *ancestor_ptr = NULL,
+ ancestor_input = {0}, our_input = {0}, their_input = {0};
+ git_odb *odb = NULL;
+ git_odb_object *odb_object[3] = { 0 };
+ int error = 0;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(ours);
+ GIT_ASSERT_ARG(theirs);
+
+ memset(out, 0x0, sizeof(git_merge_file_result));
+
+ if ((error = git_repository_odb(&odb, repo)) < 0)
+ goto done;
+
+ if (ancestor) {
+ if ((error = merge_file_input_from_index(
+ &ancestor_input, &odb_object[0], odb, ancestor)) < 0)
+ goto done;
+
+ ancestor_ptr = &ancestor_input;
+ }
+
+ if ((error = merge_file_input_from_index(&our_input, &odb_object[1], odb, ours)) < 0 ||
+ (error = merge_file_input_from_index(&their_input, &odb_object[2], odb, theirs)) < 0)
+ goto done;
+
+ error = merge_file__from_inputs(out,
+ ancestor_ptr, &our_input, &their_input, options);
+
+done:
+ git_odb_object_free(odb_object[0]);
+ git_odb_object_free(odb_object[1]);
+ git_odb_object_free(odb_object[2]);
+ git_odb_free(odb);
+
+ return error;
+}
+
+void git_merge_file_result_free(git_merge_file_result *result)
+{
+ if (result == NULL)
+ return;
+
+ git__free((char *)result->path);
+ git__free((char *)result->ptr);
+}
diff --git a/src/libgit2/message.c b/src/libgit2/message.c
new file mode 100644
index 0000000..ec0103a
--- /dev/null
+++ b/src/libgit2/message.c
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "buf.h"
+
+#include "git2/message.h"
+
+static size_t line_length_without_trailing_spaces(const char *line, size_t len)
+{
+ while (len) {
+ unsigned char c = line[len - 1];
+ if (!git__isspace(c))
+ break;
+ len--;
+ }
+
+ return len;
+}
+
+/* Greatly inspired from git.git "stripspace" */
+/* see https://github.com/git/git/blob/497215d8811ac7b8955693ceaad0899ecd894ed2/builtin/stripspace.c#L4-67 */
+static int git_message__prettify(
+ git_str *message_out,
+ const char *message,
+ int strip_comments,
+ char comment_char)
+{
+ const size_t message_len = strlen(message);
+
+ int consecutive_empty_lines = 0;
+ size_t i, line_length, rtrimmed_line_length;
+ char *next_newline;
+
+ for (i = 0; i < strlen(message); i += line_length) {
+ next_newline = memchr(message + i, '\n', message_len - i);
+
+ if (next_newline != NULL) {
+ line_length = next_newline - (message + i) + 1;
+ } else {
+ line_length = message_len - i;
+ }
+
+ if (strip_comments && line_length && message[i] == comment_char)
+ continue;
+
+ rtrimmed_line_length = line_length_without_trailing_spaces(message + i, line_length);
+
+ if (!rtrimmed_line_length) {
+ consecutive_empty_lines++;
+ continue;
+ }
+
+ if (consecutive_empty_lines > 0 && message_out->size > 0)
+ git_str_putc(message_out, '\n');
+
+ consecutive_empty_lines = 0;
+ git_str_put(message_out, message + i, rtrimmed_line_length);
+ git_str_putc(message_out, '\n');
+ }
+
+ return git_str_oom(message_out) ? -1 : 0;
+}
+
+int git_message_prettify(
+ git_buf *message_out,
+ const char *message,
+ int strip_comments,
+ char comment_char)
+{
+ GIT_BUF_WRAP_PRIVATE(message_out, git_message__prettify, message, strip_comments, comment_char);
+}
diff --git a/src/libgit2/midx.c b/src/libgit2/midx.c
new file mode 100644
index 0000000..d73a1da
--- /dev/null
+++ b/src/libgit2/midx.c
@@ -0,0 +1,928 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "midx.h"
+
+#include "array.h"
+#include "buf.h"
+#include "filebuf.h"
+#include "futils.h"
+#include "hash.h"
+#include "odb.h"
+#include "pack.h"
+#include "fs_path.h"
+#include "repository.h"
+#include "str.h"
+
+#define MIDX_SIGNATURE 0x4d494458 /* "MIDX" */
+#define MIDX_VERSION 1
+#define MIDX_OBJECT_ID_VERSION 1
+struct git_midx_header {
+ uint32_t signature;
+ uint8_t version;
+ uint8_t object_id_version;
+ uint8_t chunks;
+ uint8_t base_midx_files;
+ uint32_t packfiles;
+};
+
+#define MIDX_PACKFILE_NAMES_ID 0x504e414d /* "PNAM" */
+#define MIDX_OID_FANOUT_ID 0x4f494446 /* "OIDF" */
+#define MIDX_OID_LOOKUP_ID 0x4f49444c /* "OIDL" */
+#define MIDX_OBJECT_OFFSETS_ID 0x4f4f4646 /* "OOFF" */
+#define MIDX_OBJECT_LARGE_OFFSETS_ID 0x4c4f4646 /* "LOFF" */
+
+struct git_midx_chunk {
+ off64_t offset;
+ size_t length;
+};
+
+typedef int (*midx_write_cb)(const char *buf, size_t size, void *cb_data);
+
+static int midx_error(const char *message)
+{
+ git_error_set(GIT_ERROR_ODB, "invalid multi-pack-index file - %s", message);
+ return -1;
+}
+
+static int midx_parse_packfile_names(
+ git_midx_file *idx,
+ const unsigned char *data,
+ uint32_t packfiles,
+ struct git_midx_chunk *chunk)
+{
+ int error;
+ uint32_t i;
+ char *packfile_name = (char *)(data + chunk->offset);
+ size_t chunk_size = chunk->length, len;
+ if (chunk->offset == 0)
+ return midx_error("missing Packfile Names chunk");
+ if (chunk->length == 0)
+ return midx_error("empty Packfile Names chunk");
+ if ((error = git_vector_init(&idx->packfile_names, packfiles, git__strcmp_cb)) < 0)
+ return error;
+ for (i = 0; i < packfiles; ++i) {
+ len = p_strnlen(packfile_name, chunk_size);
+ if (len == 0)
+ return midx_error("empty packfile name");
+ if (len + 1 > chunk_size)
+ return midx_error("unterminated packfile name");
+ git_vector_insert(&idx->packfile_names, packfile_name);
+ if (i && strcmp(git_vector_get(&idx->packfile_names, i - 1), packfile_name) >= 0)
+ return midx_error("packfile names are not sorted");
+ if (strlen(packfile_name) <= strlen(".idx") || git__suffixcmp(packfile_name, ".idx") != 0)
+ return midx_error("non-.idx packfile name");
+ if (strchr(packfile_name, '/') != NULL || strchr(packfile_name, '\\') != NULL)
+ return midx_error("non-local packfile");
+ packfile_name += len + 1;
+ chunk_size -= len + 1;
+ }
+ return 0;
+}
+
+static int midx_parse_oid_fanout(
+ git_midx_file *idx,
+ const unsigned char *data,
+ struct git_midx_chunk *chunk_oid_fanout)
+{
+ uint32_t i, nr;
+ if (chunk_oid_fanout->offset == 0)
+ return midx_error("missing OID Fanout chunk");
+ if (chunk_oid_fanout->length == 0)
+ return midx_error("empty OID Fanout chunk");
+ if (chunk_oid_fanout->length != 256 * 4)
+ return midx_error("OID Fanout chunk has wrong length");
+
+ idx->oid_fanout = (const uint32_t *)(data + chunk_oid_fanout->offset);
+ nr = 0;
+ for (i = 0; i < 256; ++i) {
+ uint32_t n = ntohl(idx->oid_fanout[i]);
+ if (n < nr)
+ return midx_error("index is non-monotonic");
+ nr = n;
+ }
+ idx->num_objects = nr;
+ return 0;
+}
+
+static int midx_parse_oid_lookup(
+ git_midx_file *idx,
+ const unsigned char *data,
+ struct git_midx_chunk *chunk_oid_lookup)
+{
+ size_t oid_size = git_oid_size(idx->oid_type);
+
+ if (chunk_oid_lookup->offset == 0)
+ return midx_error("missing OID Lookup chunk");
+ if (chunk_oid_lookup->length == 0)
+ return midx_error("empty OID Lookup chunk");
+ if (chunk_oid_lookup->length != idx->num_objects * oid_size)
+ return midx_error("OID Lookup chunk has wrong length");
+
+ idx->oid_lookup = (unsigned char *)(data + chunk_oid_lookup->offset);
+
+ return 0;
+}
+
+static int midx_parse_object_offsets(
+ git_midx_file *idx,
+ const unsigned char *data,
+ struct git_midx_chunk *chunk_object_offsets)
+{
+ if (chunk_object_offsets->offset == 0)
+ return midx_error("missing Object Offsets chunk");
+ if (chunk_object_offsets->length == 0)
+ return midx_error("empty Object Offsets chunk");
+ if (chunk_object_offsets->length != idx->num_objects * 8)
+ return midx_error("Object Offsets chunk has wrong length");
+
+ idx->object_offsets = data + chunk_object_offsets->offset;
+
+ return 0;
+}
+
+static int midx_parse_object_large_offsets(
+ git_midx_file *idx,
+ const unsigned char *data,
+ struct git_midx_chunk *chunk_object_large_offsets)
+{
+ if (chunk_object_large_offsets->length == 0)
+ return 0;
+ if (chunk_object_large_offsets->length % 8 != 0)
+ return midx_error("malformed Object Large Offsets chunk");
+
+ idx->object_large_offsets = data + chunk_object_large_offsets->offset;
+ idx->num_object_large_offsets = chunk_object_large_offsets->length / 8;
+
+ return 0;
+}
+
+int git_midx_parse(
+ git_midx_file *idx,
+ const unsigned char *data,
+ size_t size)
+{
+ struct git_midx_header *hdr;
+ const unsigned char *chunk_hdr;
+ struct git_midx_chunk *last_chunk;
+ uint32_t i;
+ off64_t last_chunk_offset, chunk_offset, trailer_offset;
+ size_t checksum_size, oid_size;
+ int error;
+ struct git_midx_chunk chunk_packfile_names = {0},
+ chunk_oid_fanout = {0},
+ chunk_oid_lookup = {0},
+ chunk_object_offsets = {0},
+ chunk_object_large_offsets = {0},
+ chunk_unknown = {0};
+
+ GIT_ASSERT_ARG(idx);
+
+ oid_size = git_oid_size(idx->oid_type);
+
+ if (size < sizeof(struct git_midx_header) + oid_size)
+ return midx_error("multi-pack index is too short");
+
+ hdr = ((struct git_midx_header *)data);
+
+ if (hdr->signature != htonl(MIDX_SIGNATURE) ||
+ hdr->version != MIDX_VERSION ||
+ hdr->object_id_version != MIDX_OBJECT_ID_VERSION) {
+ return midx_error("unsupported multi-pack index version");
+ }
+ if (hdr->chunks == 0)
+ return midx_error("no chunks in multi-pack index");
+
+ /*
+ * The very first chunk's offset should be after the header, all the chunk
+ * headers, and a special zero chunk.
+ */
+ last_chunk_offset =
+ sizeof(struct git_midx_header) +
+ (1 + hdr->chunks) * 12;
+
+ checksum_size = oid_size;
+ trailer_offset = size - checksum_size;
+
+ if (trailer_offset < last_chunk_offset)
+ return midx_error("wrong index size");
+ memcpy(idx->checksum, data + trailer_offset, checksum_size);
+
+ chunk_hdr = data + sizeof(struct git_midx_header);
+ last_chunk = NULL;
+ for (i = 0; i < hdr->chunks; ++i, chunk_hdr += 12) {
+ uint32_t chunk_id = ntohl(*((uint32_t *)(chunk_hdr + 0)));
+ uint64_t high_offset = ((uint64_t)ntohl(*((uint32_t *)(chunk_hdr + 4)))) & 0xffffffffu;
+ uint64_t low_offset = ((uint64_t)ntohl(*((uint32_t *)(chunk_hdr + 8)))) & 0xffffffffu;
+
+ if (high_offset >= INT32_MAX)
+ return midx_error("chunk offset out of range");
+ chunk_offset = (off64_t)(high_offset << 32 | low_offset);
+ if (chunk_offset < last_chunk_offset)
+ return midx_error("chunks are non-monotonic");
+ if (chunk_offset >= trailer_offset)
+ return midx_error("chunks extend beyond the trailer");
+ if (last_chunk != NULL)
+ last_chunk->length = (size_t)(chunk_offset - last_chunk_offset);
+ last_chunk_offset = chunk_offset;
+
+ switch (chunk_id) {
+ case MIDX_PACKFILE_NAMES_ID:
+ chunk_packfile_names.offset = last_chunk_offset;
+ last_chunk = &chunk_packfile_names;
+ break;
+
+ case MIDX_OID_FANOUT_ID:
+ chunk_oid_fanout.offset = last_chunk_offset;
+ last_chunk = &chunk_oid_fanout;
+ break;
+
+ case MIDX_OID_LOOKUP_ID:
+ chunk_oid_lookup.offset = last_chunk_offset;
+ last_chunk = &chunk_oid_lookup;
+ break;
+
+ case MIDX_OBJECT_OFFSETS_ID:
+ chunk_object_offsets.offset = last_chunk_offset;
+ last_chunk = &chunk_object_offsets;
+ break;
+
+ case MIDX_OBJECT_LARGE_OFFSETS_ID:
+ chunk_object_large_offsets.offset = last_chunk_offset;
+ last_chunk = &chunk_object_large_offsets;
+ break;
+
+ default:
+ chunk_unknown.offset = last_chunk_offset;
+ last_chunk = &chunk_unknown;
+ break;
+ }
+ }
+ last_chunk->length = (size_t)(trailer_offset - last_chunk_offset);
+
+ error = midx_parse_packfile_names(
+ idx, data, ntohl(hdr->packfiles), &chunk_packfile_names);
+ if (error < 0)
+ return error;
+ error = midx_parse_oid_fanout(idx, data, &chunk_oid_fanout);
+ if (error < 0)
+ return error;
+ error = midx_parse_oid_lookup(idx, data, &chunk_oid_lookup);
+ if (error < 0)
+ return error;
+ error = midx_parse_object_offsets(idx, data, &chunk_object_offsets);
+ if (error < 0)
+ return error;
+ error = midx_parse_object_large_offsets(idx, data, &chunk_object_large_offsets);
+ if (error < 0)
+ return error;
+
+ return 0;
+}
+
+int git_midx_open(
+ git_midx_file **idx_out,
+ const char *path,
+ git_oid_t oid_type)
+{
+ git_midx_file *idx;
+ git_file fd = -1;
+ size_t idx_size;
+ struct stat st;
+ int error;
+
+ GIT_ASSERT_ARG(idx_out && path && oid_type);
+
+ /* TODO: properly open the file without access time using O_NOATIME */
+ fd = git_futils_open_ro(path);
+ if (fd < 0)
+ return fd;
+
+ if (p_fstat(fd, &st) < 0) {
+ p_close(fd);
+ git_error_set(GIT_ERROR_ODB, "multi-pack-index file not found - '%s'", path);
+ return -1;
+ }
+
+ if (!S_ISREG(st.st_mode) || !git__is_sizet(st.st_size)) {
+ p_close(fd);
+ git_error_set(GIT_ERROR_ODB, "invalid pack index '%s'", path);
+ return -1;
+ }
+ idx_size = (size_t)st.st_size;
+
+ idx = git__calloc(1, sizeof(git_midx_file));
+ GIT_ERROR_CHECK_ALLOC(idx);
+
+ idx->oid_type = oid_type;
+
+ error = git_str_sets(&idx->filename, path);
+ if (error < 0)
+ return error;
+
+ error = git_futils_mmap_ro(&idx->index_map, fd, 0, idx_size);
+ p_close(fd);
+ if (error < 0) {
+ git_midx_free(idx);
+ return error;
+ }
+
+ if ((error = git_midx_parse(idx, idx->index_map.data, idx_size)) < 0) {
+ git_midx_free(idx);
+ return error;
+ }
+
+ *idx_out = idx;
+ return 0;
+}
+
+bool git_midx_needs_refresh(
+ const git_midx_file *idx,
+ const char *path)
+{
+ git_file fd = -1;
+ struct stat st;
+ ssize_t bytes_read;
+ unsigned char checksum[GIT_HASH_MAX_SIZE];
+ size_t checksum_size;
+
+ /* TODO: properly open the file without access time using O_NOATIME */
+ fd = git_futils_open_ro(path);
+ if (fd < 0)
+ return true;
+
+ if (p_fstat(fd, &st) < 0) {
+ p_close(fd);
+ return true;
+ }
+
+ if (!S_ISREG(st.st_mode) ||
+ !git__is_sizet(st.st_size) ||
+ (size_t)st.st_size != idx->index_map.len) {
+ p_close(fd);
+ return true;
+ }
+
+ checksum_size = git_oid_size(idx->oid_type);
+ bytes_read = p_pread(fd, checksum, checksum_size, st.st_size - checksum_size);
+ p_close(fd);
+
+ if (bytes_read != (ssize_t)checksum_size)
+ return true;
+
+ return (memcmp(checksum, idx->checksum, checksum_size) != 0);
+}
+
+int git_midx_entry_find(
+ git_midx_entry *e,
+ git_midx_file *idx,
+ const git_oid *short_oid,
+ size_t len)
+{
+ int pos, found = 0;
+ size_t pack_index, oid_size, oid_hexsize;
+ uint32_t hi, lo;
+ unsigned char *current = NULL;
+ const unsigned char *object_offset;
+ off64_t offset;
+
+ GIT_ASSERT_ARG(idx);
+
+ oid_size = git_oid_size(idx->oid_type);
+ oid_hexsize = git_oid_hexsize(idx->oid_type);
+
+ hi = ntohl(idx->oid_fanout[(int)short_oid->id[0]]);
+ lo = ((short_oid->id[0] == 0x0) ? 0 : ntohl(idx->oid_fanout[(int)short_oid->id[0] - 1]));
+
+ pos = git_pack__lookup_id(idx->oid_lookup, oid_size, lo, hi, short_oid->id, idx->oid_type);
+
+ if (pos >= 0) {
+ /* An object matching exactly the oid was found */
+ found = 1;
+ current = idx->oid_lookup + (pos * oid_size);
+ } else {
+ /* No object was found */
+ /* pos refers to the object with the "closest" oid to short_oid */
+ pos = -1 - pos;
+ if (pos < (int)idx->num_objects) {
+ current = idx->oid_lookup + (pos * oid_size);
+
+ if (!git_oid_raw_ncmp(short_oid->id, current, len))
+ found = 1;
+ }
+ }
+
+ if (found && len != oid_hexsize && pos + 1 < (int)idx->num_objects) {
+ /* Check for ambiguousity */
+ const unsigned char *next = current + oid_size;
+
+ if (!git_oid_raw_ncmp(short_oid->id, next, len))
+ found = 2;
+ }
+
+ if (!found)
+ return git_odb__error_notfound("failed to find offset for multi-pack index entry", short_oid, len);
+ if (found > 1)
+ return git_odb__error_ambiguous("found multiple offsets for multi-pack index entry");
+
+ object_offset = idx->object_offsets + pos * 8;
+ offset = ntohl(*((uint32_t *)(object_offset + 4)));
+ if (idx->object_large_offsets && offset & 0x80000000) {
+ uint32_t object_large_offsets_pos = (uint32_t) (offset ^ 0x80000000);
+ const unsigned char *object_large_offsets_index = idx->object_large_offsets;
+
+ /* Make sure we're not being sent out of bounds */
+ if (object_large_offsets_pos >= idx->num_object_large_offsets)
+ return git_odb__error_notfound("invalid index into the object large offsets table", short_oid, len);
+
+ object_large_offsets_index += 8 * object_large_offsets_pos;
+
+ offset = (((uint64_t)ntohl(*((uint32_t *)(object_large_offsets_index + 0)))) << 32) |
+ ntohl(*((uint32_t *)(object_large_offsets_index + 4)));
+ }
+ pack_index = ntohl(*((uint32_t *)(object_offset + 0)));
+ if (pack_index >= git_vector_length(&idx->packfile_names))
+ return midx_error("invalid index into the packfile names table");
+ e->pack_index = pack_index;
+ e->offset = offset;
+ git_oid__fromraw(&e->sha1, current, idx->oid_type);
+ return 0;
+}
+
+int git_midx_foreach_entry(
+ git_midx_file *idx,
+ git_odb_foreach_cb cb,
+ void *data)
+{
+ git_oid oid;
+ size_t oid_size, i;
+ int error;
+
+ GIT_ASSERT_ARG(idx);
+
+ oid_size = git_oid_size(idx->oid_type);
+
+ for (i = 0; i < idx->num_objects; ++i) {
+ if ((error = git_oid__fromraw(&oid, &idx->oid_lookup[i * oid_size], idx->oid_type)) < 0)
+ return error;
+
+ if ((error = cb(&oid, data)) != 0)
+ return git_error_set_after_callback(error);
+ }
+
+ return error;
+}
+
+int git_midx_close(git_midx_file *idx)
+{
+ GIT_ASSERT_ARG(idx);
+
+ if (idx->index_map.data)
+ git_futils_mmap_free(&idx->index_map);
+
+ git_vector_free(&idx->packfile_names);
+
+ return 0;
+}
+
+void git_midx_free(git_midx_file *idx)
+{
+ if (!idx)
+ return;
+
+ git_str_dispose(&idx->filename);
+ git_midx_close(idx);
+ git__free(idx);
+}
+
+static int packfile__cmp(const void *a_, const void *b_)
+{
+ const struct git_pack_file *a = a_;
+ const struct git_pack_file *b = b_;
+
+ return strcmp(a->pack_name, b->pack_name);
+}
+
+int git_midx_writer_new(
+ git_midx_writer **out,
+ const char *pack_dir
+#ifdef GIT_EXPERIMENTAL_SHA256
+ , git_oid_t oid_type
+#endif
+ )
+{
+ git_midx_writer *w;
+
+#ifndef GIT_EXPERIMENTAL_SHA256
+ git_oid_t oid_type = GIT_OID_SHA1;
+#endif
+
+ GIT_ASSERT_ARG(out && pack_dir && oid_type);
+
+ w = git__calloc(1, sizeof(git_midx_writer));
+ GIT_ERROR_CHECK_ALLOC(w);
+
+ if (git_str_sets(&w->pack_dir, pack_dir) < 0) {
+ git__free(w);
+ return -1;
+ }
+ git_fs_path_squash_slashes(&w->pack_dir);
+
+ if (git_vector_init(&w->packs, 0, packfile__cmp) < 0) {
+ git_str_dispose(&w->pack_dir);
+ git__free(w);
+ return -1;
+ }
+
+ w->oid_type = oid_type;
+
+ *out = w;
+ return 0;
+}
+
+void git_midx_writer_free(git_midx_writer *w)
+{
+ struct git_pack_file *p;
+ size_t i;
+
+ if (!w)
+ return;
+
+ git_vector_foreach (&w->packs, i, p)
+ git_mwindow_put_pack(p);
+ git_vector_free(&w->packs);
+ git_str_dispose(&w->pack_dir);
+ git__free(w);
+}
+
+int git_midx_writer_add(
+ git_midx_writer *w,
+ const char *idx_path)
+{
+ git_str idx_path_buf = GIT_STR_INIT;
+ int error;
+ struct git_pack_file *p;
+
+ error = git_fs_path_prettify(&idx_path_buf, idx_path, git_str_cstr(&w->pack_dir));
+ if (error < 0)
+ return error;
+
+ /* TODO: SHA256 */
+ error = git_mwindow_get_pack(&p, git_str_cstr(&idx_path_buf), 0);
+ git_str_dispose(&idx_path_buf);
+ if (error < 0)
+ return error;
+
+ error = git_vector_insert(&w->packs, p);
+ if (error < 0) {
+ git_mwindow_put_pack(p);
+ return error;
+ }
+
+ return 0;
+}
+
+typedef git_array_t(git_midx_entry) object_entry_array_t;
+
+struct object_entry_cb_state {
+ uint32_t pack_index;
+ object_entry_array_t *object_entries_array;
+};
+
+static int object_entry__cb(const git_oid *oid, off64_t offset, void *data)
+{
+ struct object_entry_cb_state *state = (struct object_entry_cb_state *)data;
+
+ git_midx_entry *entry = git_array_alloc(*state->object_entries_array);
+ GIT_ERROR_CHECK_ALLOC(entry);
+
+ git_oid_cpy(&entry->sha1, oid);
+ entry->offset = offset;
+ entry->pack_index = state->pack_index;
+
+ return 0;
+}
+
+static int object_entry__cmp(const void *a_, const void *b_)
+{
+ const git_midx_entry *a = (const git_midx_entry *)a_;
+ const git_midx_entry *b = (const git_midx_entry *)b_;
+
+ return git_oid_cmp(&a->sha1, &b->sha1);
+}
+
+static int write_offset(off64_t offset, midx_write_cb write_cb, void *cb_data)
+{
+ int error;
+ uint32_t word;
+
+ word = htonl((uint32_t)((offset >> 32) & 0xffffffffu));
+ error = write_cb((const char *)&word, sizeof(word), cb_data);
+ if (error < 0)
+ return error;
+ word = htonl((uint32_t)((offset >> 0) & 0xffffffffu));
+ error = write_cb((const char *)&word, sizeof(word), cb_data);
+ if (error < 0)
+ return error;
+
+ return 0;
+}
+
+static int write_chunk_header(int chunk_id, off64_t offset, midx_write_cb write_cb, void *cb_data)
+{
+ uint32_t word = htonl(chunk_id);
+ int error = write_cb((const char *)&word, sizeof(word), cb_data);
+ if (error < 0)
+ return error;
+ return write_offset(offset, write_cb, cb_data);
+
+ return 0;
+}
+
+static int midx_write_buf(const char *buf, size_t size, void *data)
+{
+ git_str *b = (git_str *)data;
+ return git_str_put(b, buf, size);
+}
+
+struct midx_write_hash_context {
+ midx_write_cb write_cb;
+ void *cb_data;
+ git_hash_ctx *ctx;
+};
+
+static int midx_write_hash(const char *buf, size_t size, void *data)
+{
+ struct midx_write_hash_context *ctx = (struct midx_write_hash_context *)data;
+ int error;
+
+ error = git_hash_update(ctx->ctx, buf, size);
+ if (error < 0)
+ return error;
+
+ return ctx->write_cb(buf, size, ctx->cb_data);
+}
+
+static int midx_write(
+ git_midx_writer *w,
+ midx_write_cb write_cb,
+ void *cb_data)
+{
+ int error = 0;
+ size_t i;
+ struct git_pack_file *p;
+ struct git_midx_header hdr = {0};
+ uint32_t oid_fanout_count;
+ uint32_t object_large_offsets_count;
+ uint32_t oid_fanout[256];
+ off64_t offset;
+ git_str packfile_names = GIT_STR_INIT,
+ oid_lookup = GIT_STR_INIT,
+ object_offsets = GIT_STR_INIT,
+ object_large_offsets = GIT_STR_INIT;
+ unsigned char checksum[GIT_HASH_MAX_SIZE];
+ size_t checksum_size, oid_size;
+ git_midx_entry *entry;
+ object_entry_array_t object_entries_array = GIT_ARRAY_INIT;
+ git_vector object_entries = GIT_VECTOR_INIT;
+ git_hash_ctx ctx;
+ git_hash_algorithm_t checksum_type;
+ struct midx_write_hash_context hash_cb_data = {0};
+
+ hdr.signature = htonl(MIDX_SIGNATURE);
+ hdr.version = MIDX_VERSION;
+ hdr.object_id_version = MIDX_OBJECT_ID_VERSION;
+ hdr.base_midx_files = 0;
+
+ hash_cb_data.write_cb = write_cb;
+ hash_cb_data.cb_data = cb_data;
+ hash_cb_data.ctx = &ctx;
+
+ oid_size = git_oid_size(w->oid_type);
+
+ GIT_ASSERT((checksum_type = git_oid_algorithm(w->oid_type)));
+ checksum_size = git_hash_size(checksum_type);
+
+ if ((error = git_hash_ctx_init(&ctx, checksum_type)) < 0)
+ return error;
+
+ cb_data = &hash_cb_data;
+ write_cb = midx_write_hash;
+
+ git_vector_sort(&w->packs);
+ git_vector_foreach (&w->packs, i, p) {
+ git_str relative_index = GIT_STR_INIT;
+ struct object_entry_cb_state state = {0};
+ size_t path_len;
+
+ state.pack_index = (uint32_t)i;
+ state.object_entries_array = &object_entries_array;
+
+ error = git_str_sets(&relative_index, p->pack_name);
+ if (error < 0)
+ goto cleanup;
+ error = git_fs_path_make_relative(&relative_index, git_str_cstr(&w->pack_dir));
+ if (error < 0) {
+ git_str_dispose(&relative_index);
+ goto cleanup;
+ }
+ path_len = git_str_len(&relative_index);
+ if (path_len <= strlen(".pack") || git__suffixcmp(git_str_cstr(&relative_index), ".pack") != 0) {
+ git_str_dispose(&relative_index);
+ git_error_set(GIT_ERROR_INVALID, "invalid packfile name: '%s'", p->pack_name);
+ error = -1;
+ goto cleanup;
+ }
+ path_len -= strlen(".pack");
+
+ git_str_put(&packfile_names, git_str_cstr(&relative_index), path_len);
+ git_str_puts(&packfile_names, ".idx");
+ git_str_putc(&packfile_names, '\0');
+ git_str_dispose(&relative_index);
+
+ error = git_pack_foreach_entry_offset(p, object_entry__cb, &state);
+ if (error < 0)
+ goto cleanup;
+ }
+
+ /* Sort the object entries. */
+ error = git_vector_init(&object_entries, git_array_size(object_entries_array), object_entry__cmp);
+ if (error < 0)
+ goto cleanup;
+ git_array_foreach (object_entries_array, i, entry) {
+ if ((error = git_vector_set(NULL, &object_entries, i, entry)) < 0)
+ goto cleanup;
+ }
+ git_vector_set_sorted(&object_entries, 0);
+ git_vector_sort(&object_entries);
+ git_vector_uniq(&object_entries, NULL);
+
+ /* Pad the packfile names so it is a multiple of four. */
+ while (git_str_len(&packfile_names) & 3)
+ git_str_putc(&packfile_names, '\0');
+
+ /* Fill the OID Fanout table. */
+ oid_fanout_count = 0;
+ for (i = 0; i < 256; i++) {
+ while (oid_fanout_count < git_vector_length(&object_entries) &&
+ ((const git_midx_entry *)git_vector_get(&object_entries, oid_fanout_count))->sha1.id[0] <= i)
+ ++oid_fanout_count;
+ oid_fanout[i] = htonl(oid_fanout_count);
+ }
+
+ /* Fill the OID Lookup table. */
+ git_vector_foreach (&object_entries, i, entry) {
+ error = git_str_put(&oid_lookup,
+ (char *)&entry->sha1.id, oid_size);
+
+ if (error < 0)
+ goto cleanup;
+ }
+
+ /* Fill the Object Offsets and Object Large Offsets tables. */
+ object_large_offsets_count = 0;
+ git_vector_foreach (&object_entries, i, entry) {
+ uint32_t word;
+
+ word = htonl((uint32_t)entry->pack_index);
+ error = git_str_put(&object_offsets, (const char *)&word, sizeof(word));
+ if (error < 0)
+ goto cleanup;
+ if (entry->offset >= 0x80000000l) {
+ word = htonl(0x80000000u | object_large_offsets_count++);
+ if ((error = write_offset(entry->offset, midx_write_buf, &object_large_offsets)) < 0)
+ goto cleanup;
+ } else {
+ word = htonl((uint32_t)entry->offset & 0x7fffffffu);
+ }
+
+ error = git_str_put(&object_offsets, (const char *)&word, sizeof(word));
+ if (error < 0)
+ goto cleanup;
+ }
+
+ /* Write the header. */
+ hdr.packfiles = htonl((uint32_t)git_vector_length(&w->packs));
+ hdr.chunks = 4;
+ if (git_str_len(&object_large_offsets) > 0)
+ hdr.chunks++;
+ error = write_cb((const char *)&hdr, sizeof(hdr), cb_data);
+ if (error < 0)
+ goto cleanup;
+
+ /* Write the chunk headers. */
+ offset = sizeof(hdr) + (hdr.chunks + 1) * 12;
+ error = write_chunk_header(MIDX_PACKFILE_NAMES_ID, offset, write_cb, cb_data);
+ if (error < 0)
+ goto cleanup;
+ offset += git_str_len(&packfile_names);
+ error = write_chunk_header(MIDX_OID_FANOUT_ID, offset, write_cb, cb_data);
+ if (error < 0)
+ goto cleanup;
+ offset += sizeof(oid_fanout);
+ error = write_chunk_header(MIDX_OID_LOOKUP_ID, offset, write_cb, cb_data);
+ if (error < 0)
+ goto cleanup;
+ offset += git_str_len(&oid_lookup);
+ error = write_chunk_header(MIDX_OBJECT_OFFSETS_ID, offset, write_cb, cb_data);
+ if (error < 0)
+ goto cleanup;
+ offset += git_str_len(&object_offsets);
+ if (git_str_len(&object_large_offsets) > 0) {
+ error = write_chunk_header(MIDX_OBJECT_LARGE_OFFSETS_ID, offset, write_cb, cb_data);
+ if (error < 0)
+ goto cleanup;
+ offset += git_str_len(&object_large_offsets);
+ }
+ error = write_chunk_header(0, offset, write_cb, cb_data);
+ if (error < 0)
+ goto cleanup;
+
+ /* Write all the chunks. */
+ error = write_cb(git_str_cstr(&packfile_names), git_str_len(&packfile_names), cb_data);
+ if (error < 0)
+ goto cleanup;
+ error = write_cb((const char *)oid_fanout, sizeof(oid_fanout), cb_data);
+ if (error < 0)
+ goto cleanup;
+ error = write_cb(git_str_cstr(&oid_lookup), git_str_len(&oid_lookup), cb_data);
+ if (error < 0)
+ goto cleanup;
+ error = write_cb(git_str_cstr(&object_offsets), git_str_len(&object_offsets), cb_data);
+ if (error < 0)
+ goto cleanup;
+ error = write_cb(git_str_cstr(&object_large_offsets), git_str_len(&object_large_offsets), cb_data);
+ if (error < 0)
+ goto cleanup;
+
+ /* Finalize the checksum and write the trailer. */
+ error = git_hash_final(checksum, &ctx);
+ if (error < 0)
+ goto cleanup;
+ error = write_cb((char *)checksum, checksum_size, cb_data);
+ if (error < 0)
+ goto cleanup;
+
+cleanup:
+ git_array_clear(object_entries_array);
+ git_vector_free(&object_entries);
+ git_str_dispose(&packfile_names);
+ git_str_dispose(&oid_lookup);
+ git_str_dispose(&object_offsets);
+ git_str_dispose(&object_large_offsets);
+ git_hash_ctx_cleanup(&ctx);
+ return error;
+}
+
+static int midx_write_filebuf(const char *buf, size_t size, void *data)
+{
+ git_filebuf *f = (git_filebuf *)data;
+ return git_filebuf_write(f, buf, size);
+}
+
+int git_midx_writer_commit(
+ git_midx_writer *w)
+{
+ int error;
+ int filebuf_flags = GIT_FILEBUF_DO_NOT_BUFFER;
+ git_str midx_path = GIT_STR_INIT;
+ git_filebuf output = GIT_FILEBUF_INIT;
+
+ error = git_str_joinpath(&midx_path, git_str_cstr(&w->pack_dir), "multi-pack-index");
+ if (error < 0)
+ return error;
+
+ if (git_repository__fsync_gitdir)
+ filebuf_flags |= GIT_FILEBUF_FSYNC;
+ error = git_filebuf_open(&output, git_str_cstr(&midx_path), filebuf_flags, 0644);
+ git_str_dispose(&midx_path);
+ if (error < 0)
+ return error;
+
+ error = midx_write(w, midx_write_filebuf, &output);
+ if (error < 0) {
+ git_filebuf_cleanup(&output);
+ return error;
+ }
+
+ return git_filebuf_commit(&output);
+}
+
+int git_midx_writer_dump(
+ git_buf *midx,
+ git_midx_writer *w)
+{
+ git_str str = GIT_STR_INIT;
+ int error;
+
+ if ((error = git_buf_tostr(&str, midx)) < 0 ||
+ (error = midx_write(w, midx_write_buf, &str)) == 0)
+ error = git_buf_fromstr(midx, &str);
+
+ git_str_dispose(&str);
+ return error;
+}
diff --git a/src/libgit2/midx.h b/src/libgit2/midx.h
new file mode 100644
index 0000000..5107f69
--- /dev/null
+++ b/src/libgit2/midx.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_midx_h__
+#define INCLUDE_midx_h__
+
+#include "common.h"
+
+#include <ctype.h>
+
+#include "git2/sys/midx.h"
+
+#include "map.h"
+#include "mwindow.h"
+#include "odb.h"
+#include "oid.h"
+
+/*
+ * A multi-pack-index file.
+ *
+ * This file contains a merged index for multiple independent .pack files. This
+ * can help speed up locating objects without requiring a garbage collection
+ * cycle to create a single .pack file.
+ *
+ * Support for this feature was added in git 2.21, and requires the
+ * `core.multiPackIndex` config option to be set.
+ */
+typedef struct git_midx_file {
+ git_map index_map;
+
+ /* The table of Packfile Names. */
+ git_vector packfile_names;
+
+ /* The OID Fanout table. */
+ const uint32_t *oid_fanout;
+ /* The total number of objects in the index. */
+ uint32_t num_objects;
+
+ /* The OID Lookup table. */
+ unsigned char *oid_lookup;
+
+ /* The Object Offsets table. Each entry has two 4-byte fields with the pack index and the offset. */
+ const unsigned char *object_offsets;
+
+ /* The Object Large Offsets table. */
+ const unsigned char *object_large_offsets;
+ /* The number of entries in the Object Large Offsets table. Each entry has an 8-byte with an offset */
+ size_t num_object_large_offsets;
+
+ /*
+ * The trailer of the file. Contains the checksum of the whole
+ * file, in the repository's object format hash.
+ */
+ unsigned char checksum[GIT_HASH_MAX_SIZE];
+
+ /* The type of object IDs in the midx. */
+ git_oid_t oid_type;
+
+ /* something like ".git/objects/pack/multi-pack-index". */
+ git_str filename;
+} git_midx_file;
+
+/*
+ * An entry in the multi-pack-index file. Similar in purpose to git_pack_entry.
+ */
+typedef struct git_midx_entry {
+ /* The index within idx->packfile_names where the packfile name can be found. */
+ size_t pack_index;
+ /* The offset within the .pack file where the requested object is found. */
+ off64_t offset;
+ /* The SHA-1 hash of the requested object. */
+ git_oid sha1;
+} git_midx_entry;
+
+/*
+ * A writer for `multi-pack-index` files.
+ */
+struct git_midx_writer {
+ /*
+ * The path of the directory where the .pack/.idx files are stored. The
+ * `multi-pack-index` file will be written to the same directory.
+ */
+ git_str pack_dir;
+
+ /* The list of `git_pack_file`s. */
+ git_vector packs;
+
+ /* The object ID type of the writer. */
+ git_oid_t oid_type;
+};
+
+int git_midx_open(
+ git_midx_file **idx_out,
+ const char *path,
+ git_oid_t oid_type);
+bool git_midx_needs_refresh(
+ const git_midx_file *idx,
+ const char *path);
+int git_midx_entry_find(
+ git_midx_entry *e,
+ git_midx_file *idx,
+ const git_oid *short_oid,
+ size_t len);
+int git_midx_foreach_entry(
+ git_midx_file *idx,
+ git_odb_foreach_cb cb,
+ void *data);
+int git_midx_close(git_midx_file *idx);
+void git_midx_free(git_midx_file *idx);
+
+/* This is exposed for use in the fuzzers. */
+int git_midx_parse(
+ git_midx_file *idx,
+ const unsigned char *data,
+ size_t size);
+
+#endif
diff --git a/src/libgit2/mwindow.c b/src/libgit2/mwindow.c
new file mode 100644
index 0000000..b8295d9
--- /dev/null
+++ b/src/libgit2/mwindow.c
@@ -0,0 +1,544 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "mwindow.h"
+
+#include "vector.h"
+#include "futils.h"
+#include "map.h"
+#include "runtime.h"
+#include "strmap.h"
+#include "pack.h"
+
+#define DEFAULT_WINDOW_SIZE \
+ (sizeof(void*) >= 8 \
+ ? 1 * 1024 * 1024 * 1024 \
+ : 32 * 1024 * 1024)
+
+#define DEFAULT_MAPPED_LIMIT \
+ ((1024 * 1024) * (sizeof(void*) >= 8 ? UINT64_C(8192) : UINT64_C(256)))
+
+/* default is unlimited */
+#define DEFAULT_FILE_LIMIT 0
+
+size_t git_mwindow__window_size = DEFAULT_WINDOW_SIZE;
+size_t git_mwindow__mapped_limit = DEFAULT_MAPPED_LIMIT;
+size_t git_mwindow__file_limit = DEFAULT_FILE_LIMIT;
+
+/* Mutex to control access to `git_mwindow__mem_ctl` and `git__pack_cache`. */
+git_mutex git__mwindow_mutex;
+
+/* Whenever you want to read or modify this, grab `git__mwindow_mutex` */
+git_mwindow_ctl git_mwindow__mem_ctl;
+
+/* Global list of mwindow files, to open packs once across repos */
+git_strmap *git__pack_cache = NULL;
+
+static void git_mwindow_global_shutdown(void)
+{
+ git_strmap *tmp = git__pack_cache;
+
+ git_mutex_free(&git__mwindow_mutex);
+
+ git__pack_cache = NULL;
+ git_strmap_free(tmp);
+}
+
+int git_mwindow_global_init(void)
+{
+ int error;
+
+ GIT_ASSERT(!git__pack_cache);
+
+ if ((error = git_mutex_init(&git__mwindow_mutex)) < 0 ||
+ (error = git_strmap_new(&git__pack_cache)) < 0)
+ return error;
+
+ return git_runtime_shutdown_register(git_mwindow_global_shutdown);
+}
+
+int git_mwindow_get_pack(
+ struct git_pack_file **out,
+ const char *path,
+ git_oid_t oid_type)
+{
+ struct git_pack_file *pack;
+ char *packname;
+ int error;
+
+ if ((error = git_packfile__name(&packname, path)) < 0)
+ return error;
+
+ if (git_mutex_lock(&git__mwindow_mutex) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to lock mwindow mutex");
+ return -1;
+ }
+
+ pack = git_strmap_get(git__pack_cache, packname);
+ git__free(packname);
+
+ if (pack != NULL) {
+ git_atomic32_inc(&pack->refcount);
+ git_mutex_unlock(&git__mwindow_mutex);
+ *out = pack;
+ return 0;
+ }
+
+ /* If we didn't find it, we need to create it */
+ if ((error = git_packfile_alloc(&pack, path, oid_type)) < 0) {
+ git_mutex_unlock(&git__mwindow_mutex);
+ return error;
+ }
+
+ git_atomic32_inc(&pack->refcount);
+
+ error = git_strmap_set(git__pack_cache, pack->pack_name, pack);
+ git_mutex_unlock(&git__mwindow_mutex);
+ if (error < 0) {
+ git_packfile_free(pack, false);
+ return error;
+ }
+
+ *out = pack;
+ return 0;
+}
+
+int git_mwindow_put_pack(struct git_pack_file *pack)
+{
+ int count, error;
+ struct git_pack_file *pack_to_delete = NULL;
+
+ if ((error = git_mutex_lock(&git__mwindow_mutex)) < 0)
+ return error;
+
+ /* put before get would be a corrupted state */
+ GIT_ASSERT(git__pack_cache);
+
+ /* if we cannot find it, the state is corrupted */
+ GIT_ASSERT(git_strmap_exists(git__pack_cache, pack->pack_name));
+
+ count = git_atomic32_dec(&pack->refcount);
+ if (count == 0) {
+ git_strmap_delete(git__pack_cache, pack->pack_name);
+ pack_to_delete = pack;
+ }
+ git_mutex_unlock(&git__mwindow_mutex);
+ git_packfile_free(pack_to_delete, false);
+
+ return 0;
+}
+
+/*
+ * Free all the windows in a sequence, typically because we're done
+ * with the file. Needs to hold the git__mwindow_mutex.
+ */
+static int git_mwindow_free_all_locked(git_mwindow_file *mwf)
+{
+ git_mwindow_ctl *ctl = &git_mwindow__mem_ctl;
+ size_t i;
+
+ /*
+ * Remove these windows from the global list
+ */
+ for (i = 0; i < ctl->windowfiles.length; ++i){
+ if (git_vector_get(&ctl->windowfiles, i) == mwf) {
+ git_vector_remove(&ctl->windowfiles, i);
+ break;
+ }
+ }
+
+ if (ctl->windowfiles.length == 0) {
+ git_vector_free(&ctl->windowfiles);
+ ctl->windowfiles.contents = NULL;
+ }
+
+ while (mwf->windows) {
+ git_mwindow *w = mwf->windows;
+ GIT_ASSERT(w->inuse_cnt == 0);
+
+ ctl->mapped -= w->window_map.len;
+ ctl->open_windows--;
+
+ git_futils_mmap_free(&w->window_map);
+
+ mwf->windows = w->next;
+ git__free(w);
+ }
+
+ return 0;
+}
+
+int git_mwindow_free_all(git_mwindow_file *mwf)
+{
+ int error;
+
+ if (git_mutex_lock(&git__mwindow_mutex)) {
+ git_error_set(GIT_ERROR_THREAD, "unable to lock mwindow mutex");
+ return -1;
+ }
+
+ error = git_mwindow_free_all_locked(mwf);
+
+ git_mutex_unlock(&git__mwindow_mutex);
+
+ return error;
+}
+
+/*
+ * Check if a window 'win' contains both the address 'offset' and 'extra'.
+ *
+ * 'extra' is the size of the hash we're using as we always want to make sure
+ * that it's contained.
+ */
+int git_mwindow_contains(git_mwindow *win, off64_t offset, off64_t extra)
+{
+ off64_t win_off = win->offset;
+ return win_off <= offset
+ && (offset + extra) <= (off64_t)(win_off + win->window_map.len);
+}
+
+#define GIT_MWINDOW__LRU -1
+#define GIT_MWINDOW__MRU 1
+
+/*
+ * Find the least- or most-recently-used window in a file that is not currently
+ * being used. The 'only_unused' flag controls whether the caller requires the
+ * file to only have unused windows. If '*out_window' is non-null, it is used as
+ * a starting point for the comparison.
+ *
+ * Returns whether such a window was found in the file.
+ */
+static bool git_mwindow_scan_recently_used(
+ git_mwindow_file *mwf,
+ git_mwindow **out_window,
+ git_mwindow **out_last,
+ bool only_unused,
+ int comparison_sign)
+{
+ git_mwindow *w, *w_last;
+ git_mwindow *lru_window = NULL, *lru_last = NULL;
+ bool found = false;
+
+ GIT_ASSERT_ARG(mwf);
+ GIT_ASSERT_ARG(out_window);
+
+ lru_window = *out_window;
+ if (out_last)
+ lru_last = *out_last;
+
+ for (w_last = NULL, w = mwf->windows; w; w_last = w, w = w->next) {
+ if (w->inuse_cnt) {
+ if (only_unused)
+ return false;
+ /* This window is currently being used. Skip it. */
+ continue;
+ }
+
+ /*
+ * If the current one is more (or less) recent than the last one,
+ * store it in the output parameter. If lru_window is NULL,
+ * it's the first loop, so store it as well.
+ */
+ if (!lru_window || (comparison_sign * w->last_used) > lru_window->last_used) {
+ lru_window = w;
+ lru_last = w_last;
+ found = true;
+ }
+ }
+
+ if (!found)
+ return false;
+
+ *out_window = lru_window;
+ if (out_last)
+ *out_last = lru_last;
+ return true;
+}
+
+/*
+ * Close the least recently used window (that is currently not being used) out
+ * of all the files. Called under lock from new_window_locked.
+ */
+static int git_mwindow_close_lru_window_locked(void)
+{
+ git_mwindow_ctl *ctl = &git_mwindow__mem_ctl;
+ git_mwindow_file *cur;
+ size_t i;
+ git_mwindow *lru_window = NULL, *lru_last = NULL, **list = NULL;
+
+ git_vector_foreach(&ctl->windowfiles, i, cur) {
+ if (git_mwindow_scan_recently_used(
+ cur, &lru_window, &lru_last, false, GIT_MWINDOW__LRU)) {
+ list = &cur->windows;
+ }
+ }
+
+ if (!lru_window) {
+ git_error_set(GIT_ERROR_OS, "failed to close memory window; couldn't find LRU");
+ return -1;
+ }
+
+ ctl->mapped -= lru_window->window_map.len;
+ git_futils_mmap_free(&lru_window->window_map);
+
+ if (lru_last)
+ lru_last->next = lru_window->next;
+ else
+ *list = lru_window->next;
+
+ git__free(lru_window);
+ ctl->open_windows--;
+
+ return 0;
+}
+
+/*
+ * Finds the file that does not have any open windows AND whose
+ * most-recently-used window is the least-recently used one across all
+ * currently open files.
+ *
+ * Called under lock from new_window_locked.
+ */
+static int git_mwindow_find_lru_file_locked(git_mwindow_file **out)
+{
+ git_mwindow_ctl *ctl = &git_mwindow__mem_ctl;
+ git_mwindow_file *lru_file = NULL, *current_file = NULL;
+ git_mwindow *lru_window = NULL;
+ size_t i;
+
+ git_vector_foreach(&ctl->windowfiles, i, current_file) {
+ git_mwindow *mru_window = NULL;
+ if (!git_mwindow_scan_recently_used(
+ current_file, &mru_window, NULL, true, GIT_MWINDOW__MRU)) {
+ continue;
+ }
+ if (!lru_window || lru_window->last_used > mru_window->last_used) {
+ lru_window = mru_window;
+ lru_file = current_file;
+ }
+ }
+
+ if (!lru_file) {
+ git_error_set(GIT_ERROR_OS, "failed to close memory window file; couldn't find LRU");
+ return -1;
+ }
+
+ *out = lru_file;
+ return 0;
+}
+
+/* This gets called under lock from git_mwindow_open */
+static git_mwindow *new_window_locked(
+ git_file fd,
+ off64_t size,
+ off64_t offset)
+{
+ git_mwindow_ctl *ctl = &git_mwindow__mem_ctl;
+ size_t walign = git_mwindow__window_size / 2;
+ off64_t len;
+ git_mwindow *w;
+
+ w = git__calloc(1, sizeof(*w));
+
+ if (w == NULL)
+ return NULL;
+
+ w->offset = (offset / walign) * walign;
+
+ len = size - w->offset;
+ if (len > (off64_t)git_mwindow__window_size)
+ len = (off64_t)git_mwindow__window_size;
+
+ ctl->mapped += (size_t)len;
+
+ while (git_mwindow__mapped_limit < ctl->mapped &&
+ git_mwindow_close_lru_window_locked() == 0) /* nop */;
+
+ /*
+ * We treat `mapped_limit` as a soft limit. If we can't find a
+ * window to close and are above the limit, we still mmap the new
+ * window.
+ */
+
+ if (git_futils_mmap_ro(&w->window_map, fd, w->offset, (size_t)len) < 0) {
+ /*
+ * The first error might be down to memory fragmentation even if
+ * we're below our soft limits, so free up what we can and try again.
+ */
+
+ while (git_mwindow_close_lru_window_locked() == 0)
+ /* nop */;
+
+ if (git_futils_mmap_ro(&w->window_map, fd, w->offset, (size_t)len) < 0) {
+ git__free(w);
+ return NULL;
+ }
+ }
+
+ ctl->mmap_calls++;
+ ctl->open_windows++;
+
+ if (ctl->mapped > ctl->peak_mapped)
+ ctl->peak_mapped = ctl->mapped;
+
+ if (ctl->open_windows > ctl->peak_open_windows)
+ ctl->peak_open_windows = ctl->open_windows;
+
+ return w;
+}
+
+/*
+ * Open a new window, closing the least recenty used until we have
+ * enough space. Don't forget to add it to your list
+ */
+unsigned char *git_mwindow_open(
+ git_mwindow_file *mwf,
+ git_mwindow **cursor,
+ off64_t offset,
+ size_t extra,
+ unsigned int *left)
+{
+ git_mwindow_ctl *ctl = &git_mwindow__mem_ctl;
+ git_mwindow *w = *cursor;
+
+ if (git_mutex_lock(&git__mwindow_mutex)) {
+ git_error_set(GIT_ERROR_THREAD, "unable to lock mwindow mutex");
+ return NULL;
+ }
+
+ if (!w || !(git_mwindow_contains(w, offset, extra))) {
+ if (w) {
+ w->inuse_cnt--;
+ }
+
+ for (w = mwf->windows; w; w = w->next) {
+ if (git_mwindow_contains(w, offset, extra))
+ break;
+ }
+
+ /*
+ * If there isn't a suitable window, we need to create a new
+ * one.
+ */
+ if (!w) {
+ w = new_window_locked(mwf->fd, mwf->size, offset);
+ if (w == NULL) {
+ git_mutex_unlock(&git__mwindow_mutex);
+ return NULL;
+ }
+ w->next = mwf->windows;
+ mwf->windows = w;
+ }
+ }
+
+ /* If we changed w, store it in the cursor */
+ if (w != *cursor) {
+ w->last_used = ctl->used_ctr++;
+ w->inuse_cnt++;
+ *cursor = w;
+ }
+
+ offset -= w->offset;
+
+ if (left)
+ *left = (unsigned int)(w->window_map.len - offset);
+
+ git_mutex_unlock(&git__mwindow_mutex);
+ return (unsigned char *) w->window_map.data + offset;
+}
+
+int git_mwindow_file_register(git_mwindow_file *mwf)
+{
+ git_vector closed_files = GIT_VECTOR_INIT;
+ git_mwindow_ctl *ctl = &git_mwindow__mem_ctl;
+ int error;
+ size_t i;
+ git_mwindow_file *closed_file = NULL;
+
+ if (git_mutex_lock(&git__mwindow_mutex)) {
+ git_error_set(GIT_ERROR_THREAD, "unable to lock mwindow mutex");
+ return -1;
+ }
+
+ if (ctl->windowfiles.length == 0 &&
+ (error = git_vector_init(&ctl->windowfiles, 8, NULL)) < 0) {
+ git_mutex_unlock(&git__mwindow_mutex);
+ goto cleanup;
+ }
+
+ if (git_mwindow__file_limit) {
+ git_mwindow_file *lru_file;
+ while (git_mwindow__file_limit <= ctl->windowfiles.length &&
+ git_mwindow_find_lru_file_locked(&lru_file) == 0) {
+ if ((error = git_vector_insert(&closed_files, lru_file)) < 0) {
+ /*
+ * Exceeding the file limit seems preferable to being open to
+ * data races that can end up corrupting the heap.
+ */
+ break;
+ }
+ git_mwindow_free_all_locked(lru_file);
+ }
+ }
+
+ error = git_vector_insert(&ctl->windowfiles, mwf);
+ git_mutex_unlock(&git__mwindow_mutex);
+ if (error < 0)
+ goto cleanup;
+
+ /*
+ * Once we have released the global windowfiles lock, we can close each
+ * individual file. Before doing so, acquire that file's lock to avoid
+ * closing a file that is currently being used.
+ */
+ git_vector_foreach(&closed_files, i, closed_file) {
+ error = git_mutex_lock(&closed_file->lock);
+ if (error < 0)
+ continue;
+ p_close(closed_file->fd);
+ closed_file->fd = -1;
+ git_mutex_unlock(&closed_file->lock);
+ }
+
+cleanup:
+ git_vector_free(&closed_files);
+ return error;
+}
+
+void git_mwindow_file_deregister(git_mwindow_file *mwf)
+{
+ git_mwindow_ctl *ctl = &git_mwindow__mem_ctl;
+ git_mwindow_file *cur;
+ size_t i;
+
+ if (git_mutex_lock(&git__mwindow_mutex))
+ return;
+
+ git_vector_foreach(&ctl->windowfiles, i, cur) {
+ if (cur == mwf) {
+ git_vector_remove(&ctl->windowfiles, i);
+ git_mutex_unlock(&git__mwindow_mutex);
+ return;
+ }
+ }
+ git_mutex_unlock(&git__mwindow_mutex);
+}
+
+void git_mwindow_close(git_mwindow **window)
+{
+ git_mwindow *w = *window;
+ if (w) {
+ if (git_mutex_lock(&git__mwindow_mutex)) {
+ git_error_set(GIT_ERROR_THREAD, "unable to lock mwindow mutex");
+ return;
+ }
+
+ w->inuse_cnt--;
+ git_mutex_unlock(&git__mwindow_mutex);
+ *window = NULL;
+ }
+}
diff --git a/src/libgit2/mwindow.h b/src/libgit2/mwindow.h
new file mode 100644
index 0000000..8e6df26
--- /dev/null
+++ b/src/libgit2/mwindow.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_mwindow__
+#define INCLUDE_mwindow__
+
+#include "common.h"
+
+#include "map.h"
+#include "vector.h"
+
+typedef struct git_mwindow {
+ struct git_mwindow *next;
+ git_map window_map;
+ off64_t offset;
+ size_t last_used;
+ size_t inuse_cnt;
+} git_mwindow;
+
+typedef struct git_mwindow_file {
+ git_mutex lock; /* protects updates to fd */
+ git_mwindow *windows;
+ int fd;
+ off64_t size;
+} git_mwindow_file;
+
+typedef struct git_mwindow_ctl {
+ size_t mapped;
+ unsigned int open_windows;
+ unsigned int mmap_calls;
+ unsigned int peak_open_windows;
+ size_t peak_mapped;
+ size_t used_ctr;
+ git_vector windowfiles;
+} git_mwindow_ctl;
+
+int git_mwindow_contains(git_mwindow *win, off64_t offset, off64_t extra);
+int git_mwindow_free_all(git_mwindow_file *mwf); /* locks */
+unsigned char *git_mwindow_open(git_mwindow_file *mwf, git_mwindow **cursor, off64_t offset, size_t extra, unsigned int *left);
+int git_mwindow_file_register(git_mwindow_file *mwf);
+void git_mwindow_file_deregister(git_mwindow_file *mwf);
+void git_mwindow_close(git_mwindow **w_cursor);
+
+extern int git_mwindow_global_init(void);
+
+struct git_pack_file; /* just declaration to avoid cyclical includes */
+int git_mwindow_get_pack(
+ struct git_pack_file **out,
+ const char *path,
+ git_oid_t oid_type);
+int git_mwindow_put_pack(struct git_pack_file *pack);
+
+#endif
diff --git a/src/libgit2/notes.c b/src/libgit2/notes.c
new file mode 100644
index 0000000..13ca382
--- /dev/null
+++ b/src/libgit2/notes.c
@@ -0,0 +1,810 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "notes.h"
+
+#include "buf.h"
+#include "refs.h"
+#include "config.h"
+#include "iterator.h"
+#include "signature.h"
+#include "blob.h"
+
+static int note_error_notfound(void)
+{
+ git_error_set(GIT_ERROR_INVALID, "note could not be found");
+ return GIT_ENOTFOUND;
+}
+
+static int find_subtree_in_current_level(
+ git_tree **out,
+ git_repository *repo,
+ git_tree *parent,
+ const char *annotated_object_sha,
+ int fanout)
+{
+ size_t i;
+ const git_tree_entry *entry;
+
+ *out = NULL;
+
+ if (parent == NULL)
+ return note_error_notfound();
+
+ for (i = 0; i < git_tree_entrycount(parent); i++) {
+ entry = git_tree_entry_byindex(parent, i);
+
+ if (!git__ishex(git_tree_entry_name(entry)))
+ continue;
+
+ if (S_ISDIR(git_tree_entry_filemode(entry))
+ && strlen(git_tree_entry_name(entry)) == 2
+ && !strncmp(git_tree_entry_name(entry), annotated_object_sha + fanout, 2))
+ return git_tree_lookup(out, repo, git_tree_entry_id(entry));
+
+ /* Not a DIR, so do we have an already existing blob? */
+ if (!strcmp(git_tree_entry_name(entry), annotated_object_sha + fanout))
+ return GIT_EEXISTS;
+ }
+
+ return note_error_notfound();
+}
+
+static int find_subtree_r(git_tree **out, git_tree *root,
+ git_repository *repo, const char *target, int *fanout)
+{
+ int error;
+ git_tree *subtree = NULL;
+
+ *out = NULL;
+
+ error = find_subtree_in_current_level(&subtree, repo, root, target, *fanout);
+ if (error == GIT_EEXISTS)
+ return git_tree_lookup(out, repo, git_tree_id(root));
+
+ if (error < 0)
+ return error;
+
+ *fanout += 2;
+ error = find_subtree_r(out, subtree, repo, target, fanout);
+ git_tree_free(subtree);
+
+ return error;
+}
+
+static int find_blob(git_oid *blob, git_tree *tree, const char *target)
+{
+ size_t i;
+ const git_tree_entry *entry;
+
+ for (i=0; i<git_tree_entrycount(tree); i++) {
+ entry = git_tree_entry_byindex(tree, i);
+
+ if (!strcmp(git_tree_entry_name(entry), target)) {
+ /* found matching note object - return */
+
+ git_oid_cpy(blob, git_tree_entry_id(entry));
+ return 0;
+ }
+ }
+
+ return note_error_notfound();
+}
+
+static int tree_write(
+ git_tree **out,
+ git_repository *repo,
+ git_tree *source_tree,
+ const git_oid *object_oid,
+ const char *treeentry_name,
+ unsigned int attributes)
+{
+ int error;
+ git_treebuilder *tb = NULL;
+ const git_tree_entry *entry;
+ git_oid tree_oid;
+
+ if ((error = git_treebuilder_new(&tb, repo, source_tree)) < 0)
+ goto cleanup;
+
+ if (object_oid) {
+ if ((error = git_treebuilder_insert(
+ &entry, tb, treeentry_name, object_oid, attributes)) < 0)
+ goto cleanup;
+ } else {
+ if ((error = git_treebuilder_remove(tb, treeentry_name)) < 0)
+ goto cleanup;
+ }
+
+ if ((error = git_treebuilder_write(&tree_oid, tb)) < 0)
+ goto cleanup;
+
+ error = git_tree_lookup(out, repo, &tree_oid);
+
+cleanup:
+ git_treebuilder_free(tb);
+ return error;
+}
+
+static int manipulate_note_in_tree_r(
+ git_tree **out,
+ git_repository *repo,
+ git_tree *parent,
+ git_oid *note_oid,
+ const char *annotated_object_sha,
+ int fanout,
+ int (*note_exists_cb)(
+ git_tree **out,
+ git_repository *repo,
+ git_tree *parent,
+ git_oid *note_oid,
+ const char *annotated_object_sha,
+ int fanout,
+ int current_error),
+ int (*note_notfound_cb)(
+ git_tree **out,
+ git_repository *repo,
+ git_tree *parent,
+ git_oid *note_oid,
+ const char *annotated_object_sha,
+ int fanout,
+ int current_error))
+{
+ int error;
+ git_tree *subtree = NULL, *new = NULL;
+ char subtree_name[3];
+
+ error = find_subtree_in_current_level(
+ &subtree, repo, parent, annotated_object_sha, fanout);
+
+ if (error == GIT_EEXISTS) {
+ error = note_exists_cb(
+ out, repo, parent, note_oid, annotated_object_sha, fanout, error);
+ goto cleanup;
+ }
+
+ if (error == GIT_ENOTFOUND) {
+ error = note_notfound_cb(
+ out, repo, parent, note_oid, annotated_object_sha, fanout, error);
+ goto cleanup;
+ }
+
+ if (error < 0)
+ goto cleanup;
+
+ /* An existing fanout has been found, let's dig deeper */
+ error = manipulate_note_in_tree_r(
+ &new, repo, subtree, note_oid, annotated_object_sha,
+ fanout + 2, note_exists_cb, note_notfound_cb);
+
+ if (error < 0)
+ goto cleanup;
+
+ strncpy(subtree_name, annotated_object_sha + fanout, 2);
+ subtree_name[2] = '\0';
+
+ error = tree_write(out, repo, parent, git_tree_id(new),
+ subtree_name, GIT_FILEMODE_TREE);
+
+
+cleanup:
+ git_tree_free(new);
+ git_tree_free(subtree);
+ return error;
+}
+
+static int remove_note_in_tree_eexists_cb(
+ git_tree **out,
+ git_repository *repo,
+ git_tree *parent,
+ git_oid *note_oid,
+ const char *annotated_object_sha,
+ int fanout,
+ int current_error)
+{
+ GIT_UNUSED(note_oid);
+ GIT_UNUSED(current_error);
+
+ return tree_write(out, repo, parent, NULL, annotated_object_sha + fanout, 0);
+}
+
+static int remove_note_in_tree_enotfound_cb(
+ git_tree **out,
+ git_repository *repo,
+ git_tree *parent,
+ git_oid *note_oid,
+ const char *annotated_object_sha,
+ int fanout,
+ int current_error)
+{
+ GIT_UNUSED(out);
+ GIT_UNUSED(repo);
+ GIT_UNUSED(parent);
+ GIT_UNUSED(note_oid);
+ GIT_UNUSED(fanout);
+
+ git_error_set(GIT_ERROR_REPOSITORY, "object '%s' has no note", annotated_object_sha);
+ return current_error;
+}
+
+static int insert_note_in_tree_eexists_cb(git_tree **out,
+ git_repository *repo,
+ git_tree *parent,
+ git_oid *note_oid,
+ const char *annotated_object_sha,
+ int fanout,
+ int current_error)
+{
+ GIT_UNUSED(out);
+ GIT_UNUSED(repo);
+ GIT_UNUSED(parent);
+ GIT_UNUSED(note_oid);
+ GIT_UNUSED(fanout);
+
+ git_error_set(GIT_ERROR_REPOSITORY, "note for '%s' exists already", annotated_object_sha);
+ return current_error;
+}
+
+static int insert_note_in_tree_enotfound_cb(git_tree **out,
+ git_repository *repo,
+ git_tree *parent,
+ git_oid *note_oid,
+ const char *annotated_object_sha,
+ int fanout,
+ int current_error)
+{
+ GIT_UNUSED(current_error);
+
+ /* No existing fanout at this level, insert in place */
+ return tree_write(
+ out,
+ repo,
+ parent,
+ note_oid,
+ annotated_object_sha + fanout,
+ GIT_FILEMODE_BLOB);
+}
+
+static int note_write(
+ git_oid *notes_commit_out,
+ git_oid *notes_blob_out,
+ git_repository *repo,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *notes_ref,
+ const char *note,
+ git_tree *commit_tree,
+ const char *target,
+ git_commit **parents,
+ int allow_note_overwrite)
+{
+ int error;
+ git_oid oid;
+ git_tree *tree = NULL;
+
+ /* TODO: should we apply filters? */
+ /* create note object */
+ if ((error = git_blob_create_from_buffer(&oid, repo, note, strlen(note))) < 0)
+ goto cleanup;
+
+ if ((error = manipulate_note_in_tree_r(
+ &tree, repo, commit_tree, &oid, target, 0,
+ allow_note_overwrite ? insert_note_in_tree_enotfound_cb : insert_note_in_tree_eexists_cb,
+ insert_note_in_tree_enotfound_cb)) < 0)
+ goto cleanup;
+
+ if (notes_blob_out)
+ git_oid_cpy(notes_blob_out, &oid);
+
+
+ error = git_commit_create(&oid, repo, notes_ref, author, committer,
+ NULL, GIT_NOTES_DEFAULT_MSG_ADD,
+ tree, *parents == NULL ? 0 : 1, (const git_commit **) parents);
+
+ if (notes_commit_out)
+ git_oid_cpy(notes_commit_out, &oid);
+
+cleanup:
+ git_tree_free(tree);
+ return error;
+}
+
+static int note_new(
+ git_note **out,
+ git_oid *note_oid,
+ git_commit *commit,
+ git_blob *blob)
+{
+ git_note *note = NULL;
+ git_object_size_t blobsize;
+
+ note = git__malloc(sizeof(git_note));
+ GIT_ERROR_CHECK_ALLOC(note);
+
+ git_oid_cpy(&note->id, note_oid);
+
+ if (git_signature_dup(&note->author, git_commit_author(commit)) < 0 ||
+ git_signature_dup(&note->committer, git_commit_committer(commit)) < 0)
+ return -1;
+
+ blobsize = git_blob_rawsize(blob);
+ GIT_ERROR_CHECK_BLOBSIZE(blobsize);
+
+ note->message = git__strndup(git_blob_rawcontent(blob), (size_t)blobsize);
+ GIT_ERROR_CHECK_ALLOC(note->message);
+
+ *out = note;
+ return 0;
+}
+
+static int note_lookup(
+ git_note **out,
+ git_repository *repo,
+ git_commit *commit,
+ git_tree *tree,
+ const char *target)
+{
+ int error, fanout = 0;
+ git_oid oid;
+ git_blob *blob = NULL;
+ git_note *note = NULL;
+ git_tree *subtree = NULL;
+
+ if ((error = find_subtree_r(&subtree, tree, repo, target, &fanout)) < 0)
+ goto cleanup;
+
+ if ((error = find_blob(&oid, subtree, target + fanout)) < 0)
+ goto cleanup;
+
+ if ((error = git_blob_lookup(&blob, repo, &oid)) < 0)
+ goto cleanup;
+
+ if ((error = note_new(&note, &oid, commit, blob)) < 0)
+ goto cleanup;
+
+ *out = note;
+
+cleanup:
+ git_tree_free(subtree);
+ git_blob_free(blob);
+ return error;
+}
+
+static int note_remove(
+ git_oid *notes_commit_out,
+ git_repository *repo,
+ const git_signature *author, const git_signature *committer,
+ const char *notes_ref, git_tree *tree,
+ const char *target, git_commit **parents)
+{
+ int error;
+ git_tree *tree_after_removal = NULL;
+ git_oid oid;
+
+ if ((error = manipulate_note_in_tree_r(
+ &tree_after_removal, repo, tree, NULL, target, 0,
+ remove_note_in_tree_eexists_cb, remove_note_in_tree_enotfound_cb)) < 0)
+ goto cleanup;
+
+ error = git_commit_create(&oid, repo, notes_ref, author, committer,
+ NULL, GIT_NOTES_DEFAULT_MSG_RM,
+ tree_after_removal,
+ *parents == NULL ? 0 : 1,
+ (const git_commit **) parents);
+
+ if (error < 0)
+ goto cleanup;
+
+ if (notes_commit_out)
+ git_oid_cpy(notes_commit_out, &oid);
+
+cleanup:
+ git_tree_free(tree_after_removal);
+ return error;
+}
+
+static int note_get_default_ref(git_str *out, git_repository *repo)
+{
+ git_config *cfg;
+ int error;
+
+ if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
+ return error;
+
+ error = git_config__get_string_buf(out, cfg, "core.notesref");
+
+ if (error == GIT_ENOTFOUND)
+ error = git_str_puts(out, GIT_NOTES_DEFAULT_REF);
+
+ return error;
+}
+
+static int normalize_namespace(git_str *out, git_repository *repo, const char *notes_ref)
+{
+ if (notes_ref)
+ return git_str_puts(out, notes_ref);
+
+ return note_get_default_ref(out, repo);
+}
+
+static int retrieve_note_commit(
+ git_commit **commit_out,
+ git_str *notes_ref_out,
+ git_repository *repo,
+ const char *notes_ref)
+{
+ int error;
+ git_oid oid;
+
+ if ((error = normalize_namespace(notes_ref_out, repo, notes_ref)) < 0)
+ return error;
+
+ if ((error = git_reference_name_to_id(&oid, repo, notes_ref_out->ptr)) < 0)
+ return error;
+
+ if (git_commit_lookup(commit_out, repo, &oid) < 0)
+ return error;
+
+ return 0;
+}
+
+int git_note_commit_read(
+ git_note **out,
+ git_repository *repo,
+ git_commit *notes_commit,
+ const git_oid *oid)
+{
+ int error;
+ git_tree *tree = NULL;
+ char target[GIT_OID_MAX_HEXSIZE + 1];
+
+ git_oid_tostr(target, sizeof(target), oid);
+
+ if ((error = git_commit_tree(&tree, notes_commit)) < 0)
+ goto cleanup;
+
+ error = note_lookup(out, repo, notes_commit, tree, target);
+
+cleanup:
+ git_tree_free(tree);
+ return error;
+}
+
+int git_note_read(git_note **out, git_repository *repo,
+ const char *notes_ref_in, const git_oid *oid)
+{
+ int error;
+ git_str notes_ref = GIT_STR_INIT;
+ git_commit *commit = NULL;
+
+ error = retrieve_note_commit(&commit, &notes_ref, repo, notes_ref_in);
+
+ if (error < 0)
+ goto cleanup;
+
+ error = git_note_commit_read(out, repo, commit, oid);
+
+cleanup:
+ git_str_dispose(&notes_ref);
+ git_commit_free(commit);
+ return error;
+}
+
+int git_note_commit_create(
+ git_oid *notes_commit_out,
+ git_oid *notes_blob_out,
+ git_repository *repo,
+ git_commit *parent,
+ const git_signature *author,
+ const git_signature *committer,
+ const git_oid *oid,
+ const char *note,
+ int allow_note_overwrite)
+{
+ int error;
+ git_tree *tree = NULL;
+ char target[GIT_OID_MAX_HEXSIZE + 1];
+
+ git_oid_tostr(target, sizeof(target), oid);
+
+ if (parent != NULL && (error = git_commit_tree(&tree, parent)) < 0)
+ goto cleanup;
+
+ error = note_write(notes_commit_out, notes_blob_out, repo, author,
+ committer, NULL, note, tree, target, &parent, allow_note_overwrite);
+
+ if (error < 0)
+ goto cleanup;
+
+cleanup:
+ git_tree_free(tree);
+ return error;
+}
+
+int git_note_create(
+ git_oid *out,
+ git_repository *repo,
+ const char *notes_ref_in,
+ const git_signature *author,
+ const git_signature *committer,
+ const git_oid *oid,
+ const char *note,
+ int allow_note_overwrite)
+{
+ int error;
+ git_str notes_ref = GIT_STR_INIT;
+ git_commit *existing_notes_commit = NULL;
+ git_reference *ref = NULL;
+ git_oid notes_blob_oid, notes_commit_oid;
+
+ error = retrieve_note_commit(&existing_notes_commit, &notes_ref,
+ repo, notes_ref_in);
+
+ if (error < 0 && error != GIT_ENOTFOUND)
+ goto cleanup;
+
+ error = git_note_commit_create(&notes_commit_oid,
+ &notes_blob_oid,
+ repo, existing_notes_commit, author,
+ committer, oid, note,
+ allow_note_overwrite);
+ if (error < 0)
+ goto cleanup;
+
+ error = git_reference_create(&ref, repo, notes_ref.ptr,
+ &notes_commit_oid, 1, NULL);
+
+ if (out != NULL)
+ git_oid_cpy(out, &notes_blob_oid);
+
+cleanup:
+ git_str_dispose(&notes_ref);
+ git_commit_free(existing_notes_commit);
+ git_reference_free(ref);
+ return error;
+}
+
+int git_note_commit_remove(
+ git_oid *notes_commit_out,
+ git_repository *repo,
+ git_commit *notes_commit,
+ const git_signature *author,
+ const git_signature *committer,
+ const git_oid *oid)
+{
+ int error;
+ git_tree *tree = NULL;
+ char target[GIT_OID_MAX_HEXSIZE + 1];
+
+ git_oid_tostr(target, sizeof(target), oid);
+
+ if ((error = git_commit_tree(&tree, notes_commit)) < 0)
+ goto cleanup;
+
+ error = note_remove(notes_commit_out,
+ repo, author, committer, NULL, tree, target, &notes_commit);
+
+cleanup:
+ git_tree_free(tree);
+ return error;
+}
+
+int git_note_remove(git_repository *repo, const char *notes_ref_in,
+ const git_signature *author, const git_signature *committer,
+ const git_oid *oid)
+{
+ int error;
+ git_str notes_ref_target = GIT_STR_INIT;
+ git_commit *existing_notes_commit = NULL;
+ git_oid new_notes_commit;
+ git_reference *notes_ref = NULL;
+
+ error = retrieve_note_commit(&existing_notes_commit, &notes_ref_target,
+ repo, notes_ref_in);
+
+ if (error < 0)
+ goto cleanup;
+
+ error = git_note_commit_remove(&new_notes_commit, repo,
+ existing_notes_commit, author, committer, oid);
+ if (error < 0)
+ goto cleanup;
+
+ error = git_reference_create(&notes_ref, repo, notes_ref_target.ptr,
+ &new_notes_commit, 1, NULL);
+
+cleanup:
+ git_str_dispose(&notes_ref_target);
+ git_reference_free(notes_ref);
+ git_commit_free(existing_notes_commit);
+ return error;
+}
+
+int git_note_default_ref(git_buf *out, git_repository *repo)
+{
+ GIT_BUF_WRAP_PRIVATE(out, note_get_default_ref, repo);
+}
+
+const git_signature *git_note_committer(const git_note *note)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(note, NULL);
+ return note->committer;
+}
+
+const git_signature *git_note_author(const git_note *note)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(note, NULL);
+ return note->author;
+}
+
+const char *git_note_message(const git_note *note)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(note, NULL);
+ return note->message;
+}
+
+const git_oid *git_note_id(const git_note *note)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(note, NULL);
+ return &note->id;
+}
+
+void git_note_free(git_note *note)
+{
+ if (note == NULL)
+ return;
+
+ git_signature_free(note->committer);
+ git_signature_free(note->author);
+ git__free(note->message);
+ git__free(note);
+}
+
+static int process_entry_path(
+ git_oid *annotated_object_id,
+ git_note_iterator *it,
+ const char *entry_path)
+{
+ int error = 0;
+ size_t i = 0, j = 0, len;
+ git_str buf = GIT_STR_INIT;
+
+ if ((error = git_str_puts(&buf, entry_path)) < 0)
+ goto cleanup;
+
+ len = git_str_len(&buf);
+
+ while (i < len) {
+ if (buf.ptr[i] == '/') {
+ i++;
+ continue;
+ }
+
+ if (git__fromhex(buf.ptr[i]) < 0) {
+ /* This is not a note entry */
+ goto cleanup;
+ }
+
+ if (i != j)
+ buf.ptr[j] = buf.ptr[i];
+
+ i++;
+ j++;
+ }
+
+ buf.ptr[j] = '\0';
+ buf.size = j;
+
+ if (j != git_oid_hexsize(it->repo->oid_type)) {
+ /* This is not a note entry */
+ goto cleanup;
+ }
+
+ error = git_oid__fromstr(annotated_object_id, buf.ptr, it->repo->oid_type);
+
+cleanup:
+ git_str_dispose(&buf);
+ return error;
+}
+
+int git_note_foreach(
+ git_repository *repo,
+ const char *notes_ref,
+ git_note_foreach_cb note_cb,
+ void *payload)
+{
+ int error;
+ git_note_iterator *iter = NULL;
+ git_oid note_id, annotated_id;
+
+ if ((error = git_note_iterator_new(&iter, repo, notes_ref)) < 0)
+ return error;
+
+ while (!(error = git_note_next(&note_id, &annotated_id, iter))) {
+ if ((error = note_cb(&note_id, &annotated_id, payload)) != 0) {
+ git_error_set_after_callback(error);
+ break;
+ }
+ }
+
+ if (error == GIT_ITEROVER)
+ error = 0;
+
+ git_note_iterator_free(iter);
+ return error;
+}
+
+void git_note_iterator_free(git_note_iterator *it)
+{
+ if (it == NULL)
+ return;
+
+ git_iterator_free(it);
+}
+
+int git_note_commit_iterator_new(
+ git_note_iterator **it,
+ git_commit *notes_commit)
+{
+ int error;
+ git_tree *tree;
+
+ if ((error = git_commit_tree(&tree, notes_commit)) < 0)
+ goto cleanup;
+
+ if ((error = git_iterator_for_tree(it, tree, NULL)) < 0)
+ git_iterator_free(*it);
+
+cleanup:
+ git_tree_free(tree);
+
+ return error;
+}
+
+int git_note_iterator_new(
+ git_note_iterator **it,
+ git_repository *repo,
+ const char *notes_ref_in)
+{
+ int error;
+ git_commit *commit = NULL;
+ git_str notes_ref = GIT_STR_INIT;
+
+ error = retrieve_note_commit(&commit, &notes_ref, repo, notes_ref_in);
+ if (error < 0)
+ goto cleanup;
+
+ error = git_note_commit_iterator_new(it, commit);
+
+cleanup:
+ git_str_dispose(&notes_ref);
+ git_commit_free(commit);
+
+ return error;
+}
+
+int git_note_next(
+ git_oid *note_id,
+ git_oid *annotated_id,
+ git_note_iterator *it)
+{
+ int error;
+ const git_index_entry *item;
+
+ if ((error = git_iterator_current(&item, it)) < 0)
+ return error;
+
+ git_oid_cpy(note_id, &item->id);
+
+ if ((error = process_entry_path(annotated_id, it, item->path)) < 0)
+ return error;
+
+ if ((error = git_iterator_advance(NULL, it)) < 0 && error != GIT_ITEROVER)
+ return error;
+
+ return 0;
+}
diff --git a/src/libgit2/notes.h b/src/libgit2/notes.h
new file mode 100644
index 0000000..2168e45
--- /dev/null
+++ b/src/libgit2/notes.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_note_h__
+#define INCLUDE_note_h__
+
+#include "common.h"
+
+#include "git2/oid.h"
+#include "git2/types.h"
+
+#define GIT_NOTES_DEFAULT_REF "refs/notes/commits"
+
+#define GIT_NOTES_DEFAULT_MSG_ADD \
+ "Notes added by 'git_note_create' from libgit2"
+
+#define GIT_NOTES_DEFAULT_MSG_RM \
+ "Notes removed by 'git_note_remove' from libgit2"
+
+struct git_note {
+ git_oid id;
+
+ git_signature *author;
+ git_signature *committer;
+
+ char *message;
+};
+
+#endif
diff --git a/src/libgit2/object.c b/src/libgit2/object.c
new file mode 100644
index 0000000..5fab77e
--- /dev/null
+++ b/src/libgit2/object.c
@@ -0,0 +1,691 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "object.h"
+
+#include "git2/object.h"
+
+#include "repository.h"
+
+#include "buf.h"
+#include "commit.h"
+#include "hash.h"
+#include "tree.h"
+#include "blob.h"
+#include "oid.h"
+#include "tag.h"
+
+bool git_object__strict_input_validation = true;
+
+size_t git_object__size(git_object_t type);
+
+typedef struct {
+ const char *str; /* type name string */
+ size_t size; /* size in bytes of the object structure */
+
+ int (*parse)(void *self, git_odb_object *obj, git_oid_t oid_type);
+ int (*parse_raw)(void *self, const char *data, size_t size, git_oid_t oid_type);
+ void (*free)(void *self);
+} git_object_def;
+
+static git_object_def git_objects_table[] = {
+ /* 0 = GIT_OBJECT__EXT1 */
+ { "", 0, NULL, NULL, NULL },
+
+ /* 1 = GIT_OBJECT_COMMIT */
+ { "commit", sizeof(git_commit), git_commit__parse, git_commit__parse_raw, git_commit__free },
+
+ /* 2 = GIT_OBJECT_TREE */
+ { "tree", sizeof(git_tree), git_tree__parse, git_tree__parse_raw, git_tree__free },
+
+ /* 3 = GIT_OBJECT_BLOB */
+ { "blob", sizeof(git_blob), git_blob__parse, git_blob__parse_raw, git_blob__free },
+
+ /* 4 = GIT_OBJECT_TAG */
+ { "tag", sizeof(git_tag), git_tag__parse, git_tag__parse_raw, git_tag__free },
+
+ /* 5 = GIT_OBJECT__EXT2 */
+ { "", 0, NULL, NULL, NULL },
+ /* 6 = GIT_OBJECT_OFS_DELTA */
+ { "OFS_DELTA", 0, NULL, NULL, NULL },
+ /* 7 = GIT_OBJECT_REF_DELTA */
+ { "REF_DELTA", 0, NULL, NULL, NULL },
+};
+
+int git_object__from_raw(
+ git_object **object_out,
+ const char *data,
+ size_t size,
+ git_object_t object_type,
+ git_oid_t oid_type)
+{
+ git_object_def *def;
+ git_object *object;
+ size_t object_size;
+ int error;
+
+ GIT_ASSERT_ARG(object_out);
+ *object_out = NULL;
+
+ /* Validate type match */
+ if (object_type != GIT_OBJECT_BLOB &&
+ object_type != GIT_OBJECT_TREE &&
+ object_type != GIT_OBJECT_COMMIT &&
+ object_type != GIT_OBJECT_TAG) {
+ git_error_set(GIT_ERROR_INVALID, "the requested type is invalid");
+ return GIT_ENOTFOUND;
+ }
+
+ if ((object_size = git_object__size(object_type)) == 0) {
+ git_error_set(GIT_ERROR_INVALID, "the requested type is invalid");
+ return GIT_ENOTFOUND;
+ }
+
+ /* Allocate and initialize base object */
+ object = git__calloc(1, object_size);
+ GIT_ERROR_CHECK_ALLOC(object);
+ object->cached.flags = GIT_CACHE_STORE_PARSED;
+ object->cached.type = object_type;
+ if ((error = git_odb__hash(&object->cached.oid, data, size, object_type, oid_type)) < 0)
+ return error;
+
+ /* Parse raw object data */
+ def = &git_objects_table[object_type];
+ GIT_ASSERT(def->free && def->parse_raw);
+
+ if ((error = def->parse_raw(object, data, size, oid_type)) < 0) {
+ def->free(object);
+ return error;
+ }
+
+ git_cached_obj_incref(object);
+ *object_out = object;
+
+ return 0;
+}
+
+int git_object__init_from_odb_object(
+ git_object **object_out,
+ git_repository *repo,
+ git_odb_object *odb_obj,
+ git_object_t type)
+{
+ size_t object_size;
+ git_object *object = NULL;
+
+ GIT_ASSERT_ARG(object_out);
+ *object_out = NULL;
+
+ /* Validate type match */
+ if (type != GIT_OBJECT_ANY && type != odb_obj->cached.type) {
+ git_error_set(GIT_ERROR_INVALID,
+ "the requested type does not match the type in the ODB");
+ return GIT_ENOTFOUND;
+ }
+
+ if ((object_size = git_object__size(odb_obj->cached.type)) == 0) {
+ git_error_set(GIT_ERROR_INVALID, "the requested type is invalid");
+ return GIT_ENOTFOUND;
+ }
+
+ /* Allocate and initialize base object */
+ object = git__calloc(1, object_size);
+ GIT_ERROR_CHECK_ALLOC(object);
+
+ git_oid_cpy(&object->cached.oid, &odb_obj->cached.oid);
+ object->cached.type = odb_obj->cached.type;
+ object->cached.size = odb_obj->cached.size;
+ object->repo = repo;
+
+ *object_out = object;
+ return 0;
+}
+
+int git_object__from_odb_object(
+ git_object **object_out,
+ git_repository *repo,
+ git_odb_object *odb_obj,
+ git_object_t type)
+{
+ int error;
+ git_object_def *def;
+ git_object *object = NULL;
+
+ if ((error = git_object__init_from_odb_object(&object, repo, odb_obj, type)) < 0)
+ return error;
+
+ /* Parse raw object data */
+ def = &git_objects_table[odb_obj->cached.type];
+ GIT_ASSERT(def->free && def->parse);
+
+ if ((error = def->parse(object, odb_obj, repo->oid_type)) < 0) {
+ /*
+ * parse returns EINVALID on invalid data; downgrade
+ * that to a normal -1 error code.
+ */
+ def->free(object);
+ return -1;
+ }
+
+ *object_out = git_cache_store_parsed(&repo->objects, object);
+ return 0;
+}
+
+void git_object__free(void *obj)
+{
+ git_object_t type = ((git_object *)obj)->cached.type;
+
+ if (type < 0 || ((size_t)type) >= ARRAY_SIZE(git_objects_table) ||
+ !git_objects_table[type].free)
+ git__free(obj);
+ else
+ git_objects_table[type].free(obj);
+}
+
+int git_object_lookup_prefix(
+ git_object **object_out,
+ git_repository *repo,
+ const git_oid *id,
+ size_t len,
+ git_object_t type)
+{
+ git_object *object = NULL;
+ git_odb *odb = NULL;
+ git_odb_object *odb_obj = NULL;
+ size_t oid_hexsize;
+ int error = 0;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(object_out);
+ GIT_ASSERT_ARG(id);
+
+ if (len < GIT_OID_MINPREFIXLEN) {
+ git_error_set(GIT_ERROR_OBJECT, "ambiguous lookup - OID prefix is too short");
+ return GIT_EAMBIGUOUS;
+ }
+
+ error = git_repository_odb__weakptr(&odb, repo);
+ if (error < 0)
+ return error;
+
+ oid_hexsize = git_oid_hexsize(repo->oid_type);
+
+ if (len > oid_hexsize)
+ len = oid_hexsize;
+
+ if (len == oid_hexsize) {
+ git_cached_obj *cached = NULL;
+
+ /* We want to match the full id : we can first look up in the cache,
+ * since there is no need to check for non ambiguousity
+ */
+ cached = git_cache_get_any(&repo->objects, id);
+ if (cached != NULL) {
+ if (cached->flags == GIT_CACHE_STORE_PARSED) {
+ object = (git_object *)cached;
+
+ if (type != GIT_OBJECT_ANY && type != object->cached.type) {
+ git_object_free(object);
+ git_error_set(GIT_ERROR_INVALID,
+ "the requested type does not match the type in the ODB");
+ return GIT_ENOTFOUND;
+ }
+
+ *object_out = object;
+ return 0;
+ } else if (cached->flags == GIT_CACHE_STORE_RAW) {
+ odb_obj = (git_odb_object *)cached;
+ } else {
+ GIT_ASSERT(!"Wrong caching type in the global object cache");
+ }
+ } else {
+ /* Object was not found in the cache, let's explore the backends.
+ * We could just use git_odb_read_unique_short_oid,
+ * it is the same cost for packed and loose object backends,
+ * but it may be much more costly for sqlite and hiredis.
+ */
+ error = git_odb_read(&odb_obj, odb, id);
+ }
+ } else {
+ git_oid short_oid;
+
+ git_oid_clear(&short_oid, repo->oid_type);
+ git_oid__cpy_prefix(&short_oid, id, len);
+
+ /* If len < GIT_OID_SHA1_HEXSIZE (a strict short oid was given), we have
+ * 2 options :
+ * - We always search in the cache first. If we find that short oid is
+ * ambiguous, we can stop. But in all the other cases, we must then
+ * explore all the backends (to find an object if there was match,
+ * or to check that oid is not ambiguous if we have found 1 match in
+ * the cache)
+ * - We never explore the cache, go right to exploring the backends
+ * We chose the latter : we explore directly the backends.
+ */
+ error = git_odb_read_prefix(&odb_obj, odb, &short_oid, len);
+ }
+
+ if (error < 0)
+ return error;
+
+ GIT_ASSERT(odb_obj);
+ error = git_object__from_odb_object(object_out, repo, odb_obj, type);
+
+ git_odb_object_free(odb_obj);
+
+ return error;
+}
+
+int git_object_lookup(git_object **object_out, git_repository *repo, const git_oid *id, git_object_t type) {
+ return git_object_lookup_prefix(object_out,
+ repo, id, git_oid_hexsize(repo->oid_type), type);
+}
+
+void git_object_free(git_object *object)
+{
+ if (object == NULL)
+ return;
+
+ git_cached_obj_decref(object);
+}
+
+const git_oid *git_object_id(const git_object *obj)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(obj, NULL);
+ return &obj->cached.oid;
+}
+
+git_object_t git_object_type(const git_object *obj)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(obj, GIT_OBJECT_INVALID);
+ return obj->cached.type;
+}
+
+git_repository *git_object_owner(const git_object *obj)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(obj, NULL);
+ return obj->repo;
+}
+
+const char *git_object_type2string(git_object_t type)
+{
+ if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table))
+ return "";
+
+ return git_objects_table[type].str;
+}
+
+git_object_t git_object_string2type(const char *str)
+{
+ if (!str)
+ return GIT_OBJECT_INVALID;
+
+ return git_object_stringn2type(str, strlen(str));
+}
+
+git_object_t git_object_stringn2type(const char *str, size_t len)
+{
+ size_t i;
+
+ if (!str || !len || !*str)
+ return GIT_OBJECT_INVALID;
+
+ for (i = 0; i < ARRAY_SIZE(git_objects_table); i++)
+ if (*git_objects_table[i].str &&
+ !git__prefixncmp(str, len, git_objects_table[i].str))
+ return (git_object_t)i;
+
+ return GIT_OBJECT_INVALID;
+}
+
+int git_object_typeisloose(git_object_t type)
+{
+ if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table))
+ return 0;
+
+ return (git_objects_table[type].size > 0) ? 1 : 0;
+}
+
+size_t git_object__size(git_object_t type)
+{
+ if (type < 0 || ((size_t) type) >= ARRAY_SIZE(git_objects_table))
+ return 0;
+
+ return git_objects_table[type].size;
+}
+
+static int dereference_object(git_object **dereferenced, git_object *obj)
+{
+ git_object_t type = git_object_type(obj);
+
+ switch (type) {
+ case GIT_OBJECT_COMMIT:
+ return git_commit_tree((git_tree **)dereferenced, (git_commit*)obj);
+
+ case GIT_OBJECT_TAG:
+ return git_tag_target(dereferenced, (git_tag*)obj);
+
+ case GIT_OBJECT_BLOB:
+ case GIT_OBJECT_TREE:
+ return GIT_EPEEL;
+
+ default:
+ return GIT_EINVALIDSPEC;
+ }
+}
+
+static int peel_error(int error, const git_oid *oid, git_object_t type)
+{
+ const char *type_name;
+ char hex_oid[GIT_OID_MAX_HEXSIZE + 1];
+
+ type_name = git_object_type2string(type);
+
+ git_oid_nfmt(hex_oid, GIT_OID_MAX_HEXSIZE + 1, oid);
+
+ git_error_set(GIT_ERROR_OBJECT, "the git_object of id '%s' can not be "
+ "successfully peeled into a %s (git_object_t=%i).", hex_oid, type_name, type);
+
+ return error;
+}
+
+static int check_type_combination(git_object_t type, git_object_t target)
+{
+ if (type == target)
+ return 0;
+
+ switch (type) {
+ case GIT_OBJECT_BLOB:
+ case GIT_OBJECT_TREE:
+ /* a blob or tree can never be peeled to anything but themselves */
+ return GIT_EINVALIDSPEC;
+ break;
+ case GIT_OBJECT_COMMIT:
+ /* a commit can only be peeled to a tree */
+ if (target != GIT_OBJECT_TREE && target != GIT_OBJECT_ANY)
+ return GIT_EINVALIDSPEC;
+ break;
+ case GIT_OBJECT_TAG:
+ /* a tag may point to anything, so we let anything through */
+ break;
+ default:
+ return GIT_EINVALIDSPEC;
+ }
+
+ return 0;
+}
+
+int git_object_peel(
+ git_object **peeled,
+ const git_object *object,
+ git_object_t target_type)
+{
+ git_object *source, *deref = NULL;
+ int error;
+
+ GIT_ASSERT_ARG(object);
+ GIT_ASSERT_ARG(peeled);
+
+ GIT_ASSERT_ARG(target_type == GIT_OBJECT_TAG ||
+ target_type == GIT_OBJECT_COMMIT ||
+ target_type == GIT_OBJECT_TREE ||
+ target_type == GIT_OBJECT_BLOB ||
+ target_type == GIT_OBJECT_ANY);
+
+ if ((error = check_type_combination(git_object_type(object), target_type)) < 0)
+ return peel_error(error, git_object_id(object), target_type);
+
+ if (git_object_type(object) == target_type)
+ return git_object_dup(peeled, (git_object *)object);
+
+ source = (git_object *)object;
+
+ while (!(error = dereference_object(&deref, source))) {
+
+ if (source != object)
+ git_object_free(source);
+
+ if (git_object_type(deref) == target_type) {
+ *peeled = deref;
+ return 0;
+ }
+
+ if (target_type == GIT_OBJECT_ANY &&
+ git_object_type(deref) != git_object_type(object))
+ {
+ *peeled = deref;
+ return 0;
+ }
+
+ source = deref;
+ deref = NULL;
+ }
+
+ if (source != object)
+ git_object_free(source);
+
+ git_object_free(deref);
+
+ if (error)
+ error = peel_error(error, git_object_id(object), target_type);
+
+ return error;
+}
+
+int git_object_dup(git_object **dest, git_object *source)
+{
+ git_cached_obj_incref(source);
+ *dest = source;
+ return 0;
+}
+
+int git_object_lookup_bypath(
+ git_object **out,
+ const git_object *treeish,
+ const char *path,
+ git_object_t type)
+{
+ int error = -1;
+ git_tree *tree = NULL;
+ git_tree_entry *entry = NULL;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(treeish);
+ GIT_ASSERT_ARG(path);
+
+ if ((error = git_object_peel((git_object**)&tree, treeish, GIT_OBJECT_TREE)) < 0 ||
+ (error = git_tree_entry_bypath(&entry, tree, path)) < 0)
+ {
+ goto cleanup;
+ }
+
+ if (type != GIT_OBJECT_ANY && git_tree_entry_type(entry) != type)
+ {
+ git_error_set(GIT_ERROR_OBJECT,
+ "object at path '%s' is not of the asked-for type %d",
+ path, type);
+ error = GIT_EINVALIDSPEC;
+ goto cleanup;
+ }
+
+ error = git_tree_entry_to_object(out, git_object_owner(treeish), entry);
+
+cleanup:
+ git_tree_entry_free(entry);
+ git_tree_free(tree);
+ return error;
+}
+
+static int git_object__short_id(git_str *out, const git_object *obj)
+{
+ git_repository *repo;
+ git_oid id;
+ git_odb *odb;
+ size_t oid_hexsize;
+ int len = GIT_ABBREV_DEFAULT, error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(obj);
+
+ repo = git_object_owner(obj);
+
+ git_oid_clear(&id, repo->oid_type);
+ oid_hexsize = git_oid_hexsize(repo->oid_type);
+
+ if ((error = git_repository__configmap_lookup(&len, repo, GIT_CONFIGMAP_ABBREV)) < 0)
+ return error;
+
+ if (len < 0 || (size_t)len > oid_hexsize) {
+ git_error_set(GIT_ERROR_CONFIG, "invalid oid abbreviation setting: '%d'", len);
+ return -1;
+ }
+
+ if ((error = git_repository_odb(&odb, repo)) < 0)
+ return error;
+
+ while ((size_t)len < oid_hexsize) {
+ /* set up short oid */
+ memcpy(&id.id, &obj->cached.oid.id, (len + 1) / 2);
+ if (len & 1)
+ id.id[len / 2] &= 0xf0;
+
+ error = git_odb_exists_prefix(NULL, odb, &id, len);
+ if (error != GIT_EAMBIGUOUS)
+ break;
+
+ git_error_clear();
+ len++;
+ }
+
+ if (!error && !(error = git_str_grow(out, len + 1))) {
+ git_oid_tostr(out->ptr, len + 1, &id);
+ out->size = len;
+ }
+
+ git_odb_free(odb);
+
+ return error;
+}
+
+int git_object_short_id(git_buf *out, const git_object *obj)
+{
+ GIT_BUF_WRAP_PRIVATE(out, git_object__short_id, obj);
+}
+
+bool git_object__is_valid(
+ git_repository *repo, const git_oid *id, git_object_t expected_type)
+{
+ git_odb *odb;
+ git_object_t actual_type;
+ size_t len;
+ int error;
+
+ if (!git_object__strict_input_validation)
+ return true;
+
+ if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||
+ (error = git_odb_read_header(&len, &actual_type, odb, id)) < 0)
+ return false;
+
+ if (expected_type != GIT_OBJECT_ANY && expected_type != actual_type) {
+ git_error_set(GIT_ERROR_INVALID,
+ "the requested type does not match the type in the ODB");
+ return false;
+ }
+
+ return true;
+}
+
+int git_object_rawcontent_is_valid(
+ int *valid,
+ const char *buf,
+ size_t len,
+ git_object_t object_type
+#ifdef GIT_EXPERIMENTAL_SHA256
+ , git_oid_t oid_type
+#endif
+ )
+{
+ git_object *obj = NULL;
+ int error;
+
+#ifndef GIT_EXPERIMENTAL_SHA256
+ git_oid_t oid_type = GIT_OID_SHA1;
+#endif
+
+ GIT_ASSERT_ARG(valid);
+ GIT_ASSERT_ARG(buf);
+
+ /* Blobs are always valid; don't bother parsing. */
+ if (object_type == GIT_OBJECT_BLOB) {
+ *valid = 1;
+ return 0;
+ }
+
+ error = git_object__from_raw(&obj, buf, len, object_type, oid_type);
+ git_object_free(obj);
+
+ if (error == 0) {
+ *valid = 1;
+ return 0;
+ } else if (error == GIT_EINVALID) {
+ *valid = 0;
+ return 0;
+ }
+
+ return error;
+}
+
+int git_object__parse_oid_header(
+ git_oid *oid,
+ const char **buffer_out,
+ const char *buffer_end,
+ const char *header,
+ git_oid_t oid_type)
+{
+ const size_t sha_len = git_oid_hexsize(oid_type);
+ const size_t header_len = strlen(header);
+
+ const char *buffer = *buffer_out;
+
+ if (buffer + (header_len + sha_len + 1) > buffer_end)
+ return -1;
+
+ if (memcmp(buffer, header, header_len) != 0)
+ return -1;
+
+ if (buffer[header_len + sha_len] != '\n')
+ return -1;
+
+ if (git_oid__fromstr(oid, buffer + header_len, oid_type) < 0)
+ return -1;
+
+ *buffer_out = buffer + (header_len + sha_len + 1);
+
+ return 0;
+}
+
+int git_object__write_oid_header(
+ git_str *buf,
+ const char *header,
+ const git_oid *oid)
+{
+ size_t hex_size = git_oid_hexsize(git_oid_type(oid));
+ char hex_oid[GIT_OID_MAX_HEXSIZE];
+
+ if (!hex_size) {
+ git_error_set(GIT_ERROR_INVALID, "unknown type");
+ return -1;
+ }
+
+ git_oid_fmt(hex_oid, oid);
+ git_str_puts(buf, header);
+ git_str_put(buf, hex_oid, hex_size);
+ git_str_putc(buf, '\n');
+
+ return git_str_oom(buf) ? -1 : 0;
+}
diff --git a/src/libgit2/object.h b/src/libgit2/object.h
new file mode 100644
index 0000000..b6c604c
--- /dev/null
+++ b/src/libgit2/object.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_object_h__
+#define INCLUDE_object_h__
+
+#include "common.h"
+
+#include "repository.h"
+
+#define GIT_OBJECT_SIZE_MAX UINT64_MAX
+
+extern bool git_object__strict_input_validation;
+
+/** Base git object for inheritance */
+struct git_object {
+ git_cached_obj cached;
+ git_repository *repo;
+};
+
+/* fully free the object; internal method, DO NOT EXPORT */
+void git_object__free(void *object);
+
+/*
+ * Parse object from raw data. Note that the resulting object is
+ * tied to the lifetime of the data, as some objects simply point
+ * into it.
+ */
+int git_object__from_raw(
+ git_object **object_out,
+ const char *data,
+ size_t size,
+ git_object_t object_type,
+ git_oid_t oid_type);
+
+int git_object__init_from_odb_object(
+ git_object **object_out,
+ git_repository *repo,
+ git_odb_object *odb_obj,
+ git_object_t type);
+
+int git_object__from_odb_object(
+ git_object **object_out,
+ git_repository *repo,
+ git_odb_object *odb_obj,
+ git_object_t type);
+
+int git_object__resolve_to_type(git_object **obj, git_object_t type);
+
+git_object_t git_object_stringn2type(const char *str, size_t len);
+
+int git_object__parse_oid_header(
+ git_oid *oid,
+ const char **buffer_out,
+ const char *buffer_end,
+ const char *header,
+ git_oid_t oid_type);
+
+int git_object__write_oid_header(
+ git_str *buf,
+ const char *header,
+ const git_oid *oid);
+
+bool git_object__is_valid(
+ git_repository *repo, const git_oid *id, git_object_t expected_type);
+
+GIT_INLINE(git_object_t) git_object__type_from_filemode(git_filemode_t mode)
+{
+ switch (mode) {
+ case GIT_FILEMODE_TREE:
+ return GIT_OBJECT_TREE;
+ case GIT_FILEMODE_COMMIT:
+ return GIT_OBJECT_COMMIT;
+ case GIT_FILEMODE_BLOB:
+ case GIT_FILEMODE_BLOB_EXECUTABLE:
+ case GIT_FILEMODE_LINK:
+ return GIT_OBJECT_BLOB;
+ default:
+ return GIT_OBJECT_INVALID;
+ }
+}
+
+#endif
diff --git a/src/libgit2/object_api.c b/src/libgit2/object_api.c
new file mode 100644
index 0000000..d45abd5
--- /dev/null
+++ b/src/libgit2/object_api.c
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+
+#include "git2/object.h"
+
+#include "repository.h"
+#include "commit.h"
+#include "tree.h"
+#include "blob.h"
+#include "tag.h"
+
+/**
+ * Commit
+ */
+int git_commit_lookup(git_commit **out, git_repository *repo, const git_oid *id)
+{
+ return git_object_lookup((git_object **)out, repo, id, GIT_OBJECT_COMMIT);
+}
+
+int git_commit_lookup_prefix(git_commit **out, git_repository *repo, const git_oid *id, size_t len)
+{
+ return git_object_lookup_prefix((git_object **)out, repo, id, len, GIT_OBJECT_COMMIT);
+}
+
+void git_commit_free(git_commit *obj)
+{
+ git_object_free((git_object *)obj);
+}
+
+const git_oid *git_commit_id(const git_commit *obj)
+{
+ return git_object_id((const git_object *)obj);
+}
+
+git_repository *git_commit_owner(const git_commit *obj)
+{
+ return git_object_owner((const git_object *)obj);
+}
+
+int git_commit_dup(git_commit **out, git_commit *obj)
+{
+ return git_object_dup((git_object **)out, (git_object *)obj);
+}
+
+/**
+ * Tree
+ */
+int git_tree_lookup(git_tree **out, git_repository *repo, const git_oid *id)
+{
+ return git_object_lookup((git_object **)out, repo, id, GIT_OBJECT_TREE);
+}
+
+int git_tree_lookup_prefix(git_tree **out, git_repository *repo, const git_oid *id, size_t len)
+{
+ return git_object_lookup_prefix((git_object **)out, repo, id, len, GIT_OBJECT_TREE);
+}
+
+void git_tree_free(git_tree *obj)
+{
+ git_object_free((git_object *)obj);
+}
+
+const git_oid *git_tree_id(const git_tree *obj)
+{
+ return git_object_id((const git_object *)obj);
+}
+
+git_repository *git_tree_owner(const git_tree *obj)
+{
+ return git_object_owner((const git_object *)obj);
+}
+
+int git_tree_dup(git_tree **out, git_tree *obj)
+{
+ return git_object_dup((git_object **)out, (git_object *)obj);
+}
+
+/**
+ * Tag
+ */
+int git_tag_lookup(git_tag **out, git_repository *repo, const git_oid *id)
+{
+ return git_object_lookup((git_object **)out, repo, id, GIT_OBJECT_TAG);
+}
+
+int git_tag_lookup_prefix(git_tag **out, git_repository *repo, const git_oid *id, size_t len)
+{
+ return git_object_lookup_prefix((git_object **)out, repo, id, len, GIT_OBJECT_TAG);
+}
+
+void git_tag_free(git_tag *obj)
+{
+ git_object_free((git_object *)obj);
+}
+
+const git_oid *git_tag_id(const git_tag *obj)
+{
+ return git_object_id((const git_object *)obj);
+}
+
+git_repository *git_tag_owner(const git_tag *obj)
+{
+ return git_object_owner((const git_object *)obj);
+}
+
+int git_tag_dup(git_tag **out, git_tag *obj)
+{
+ return git_object_dup((git_object **)out, (git_object *)obj);
+}
+
+/**
+ * Blob
+ */
+int git_blob_lookup(git_blob **out, git_repository *repo, const git_oid *id)
+{
+ return git_object_lookup((git_object **)out, repo, id, GIT_OBJECT_BLOB);
+}
+
+int git_blob_lookup_prefix(git_blob **out, git_repository *repo, const git_oid *id, size_t len)
+{
+ return git_object_lookup_prefix((git_object **)out, repo, id, len, GIT_OBJECT_BLOB);
+}
+
+void git_blob_free(git_blob *obj)
+{
+ git_object_free((git_object *)obj);
+}
+
+const git_oid *git_blob_id(const git_blob *obj)
+{
+ return git_object_id((const git_object *)obj);
+}
+
+git_repository *git_blob_owner(const git_blob *obj)
+{
+ return git_object_owner((const git_object *)obj);
+}
+
+int git_blob_dup(git_blob **out, git_blob *obj)
+{
+ return git_object_dup((git_object **)out, (git_object *)obj);
+}
diff --git a/src/libgit2/odb.c b/src/libgit2/odb.c
new file mode 100644
index 0000000..fec1e45
--- /dev/null
+++ b/src/libgit2/odb.c
@@ -0,0 +1,2000 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "odb.h"
+
+#include <zlib.h>
+#include "git2/object.h"
+#include "git2/sys/odb_backend.h"
+#include "futils.h"
+#include "hash.h"
+#include "delta.h"
+#include "filter.h"
+#include "repository.h"
+#include "blob.h"
+#include "oid.h"
+
+#include "git2/odb_backend.h"
+#include "git2/oid.h"
+#include "git2/oidarray.h"
+
+#define GIT_ALTERNATES_FILE "info/alternates"
+
+#define GIT_ALTERNATES_MAX_DEPTH 5
+
+/*
+ * We work under the assumption that most objects for long-running
+ * operations will be packed
+ */
+int git_odb__loose_priority = GIT_ODB_DEFAULT_LOOSE_PRIORITY;
+int git_odb__packed_priority = GIT_ODB_DEFAULT_PACKED_PRIORITY;
+
+bool git_odb__strict_hash_verification = true;
+
+typedef struct
+{
+ git_odb_backend *backend;
+ int priority;
+ bool is_alternate;
+ ino_t disk_inode;
+} backend_internal;
+
+static git_cache *odb_cache(git_odb *odb)
+{
+ git_repository *owner = GIT_REFCOUNT_OWNER(odb);
+ if (owner != NULL) {
+ return &owner->objects;
+ }
+
+ return &odb->own_cache;
+}
+
+static int odb_otype_fast(git_object_t *type_p, git_odb *db, const git_oid *id);
+static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_depth);
+static int error_null_oid(int error, const char *message);
+
+static git_object_t odb_hardcoded_type(const git_oid *id)
+{
+ if (!git_oid_cmp(id, &git_oid__empty_tree_sha1))
+ return GIT_OBJECT_TREE;
+
+ return GIT_OBJECT_INVALID;
+}
+
+static int odb_read_hardcoded(bool *found, git_rawobj *raw, const git_oid *id)
+{
+ git_object_t type;
+
+ *found = false;
+
+ if ((type = odb_hardcoded_type(id)) == GIT_OBJECT_INVALID)
+ return 0;
+
+ raw->type = type;
+ raw->len = 0;
+ raw->data = git__calloc(1, sizeof(uint8_t));
+ GIT_ERROR_CHECK_ALLOC(raw->data);
+
+ *found = true;
+ return 0;
+}
+
+int git_odb__format_object_header(
+ size_t *written,
+ char *hdr,
+ size_t hdr_size,
+ git_object_size_t obj_len,
+ git_object_t obj_type)
+{
+ const char *type_str = git_object_type2string(obj_type);
+ int hdr_max = (hdr_size > INT_MAX-2) ? (INT_MAX-2) : (int)hdr_size;
+ int len;
+
+ len = p_snprintf(hdr, hdr_max, "%s %"PRId64, type_str, (int64_t)obj_len);
+
+ if (len < 0 || len >= hdr_max) {
+ git_error_set(GIT_ERROR_OS, "object header creation failed");
+ return -1;
+ }
+
+ *written = (size_t)(len + 1);
+ return 0;
+}
+
+int git_odb__hashobj(git_oid *id, git_rawobj *obj, git_oid_t oid_type)
+{
+ git_str_vec vec[2];
+ char header[64];
+ size_t hdrlen;
+ git_hash_algorithm_t algorithm;
+ int error;
+
+ GIT_ASSERT_ARG(id);
+ GIT_ASSERT_ARG(obj);
+
+ if (!git_object_typeisloose(obj->type)) {
+ git_error_set(GIT_ERROR_INVALID, "invalid object type");
+ return -1;
+ }
+
+ if (!(algorithm = git_oid_algorithm(oid_type))) {
+ git_error_set(GIT_ERROR_INVALID, "unknown oid type");
+ return -1;
+ }
+
+ if (!obj->data && obj->len != 0) {
+ git_error_set(GIT_ERROR_INVALID, "invalid object");
+ return -1;
+ }
+
+ if ((error = git_odb__format_object_header(&hdrlen,
+ header, sizeof(header), obj->len, obj->type)) < 0)
+ return error;
+
+ vec[0].data = header;
+ vec[0].len = hdrlen;
+ vec[1].data = obj->data;
+ vec[1].len = obj->len;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ id->type = oid_type;
+#endif
+
+ return git_hash_vec(id->id, vec, 2, algorithm);
+}
+
+
+static git_odb_object *odb_object__alloc(const git_oid *oid, git_rawobj *source)
+{
+ git_odb_object *object = git__calloc(1, sizeof(git_odb_object));
+
+ if (object != NULL) {
+ git_oid_cpy(&object->cached.oid, oid);
+ object->cached.type = source->type;
+ object->cached.size = source->len;
+ object->buffer = source->data;
+ }
+
+ return object;
+}
+
+void git_odb_object__free(void *object)
+{
+ if (object != NULL) {
+ git__free(((git_odb_object *)object)->buffer);
+ git__free(object);
+ }
+}
+
+const git_oid *git_odb_object_id(git_odb_object *object)
+{
+ return &object->cached.oid;
+}
+
+const void *git_odb_object_data(git_odb_object *object)
+{
+ return object->buffer;
+}
+
+size_t git_odb_object_size(git_odb_object *object)
+{
+ return object->cached.size;
+}
+
+git_object_t git_odb_object_type(git_odb_object *object)
+{
+ return object->cached.type;
+}
+
+int git_odb_object_dup(git_odb_object **dest, git_odb_object *source)
+{
+ git_cached_obj_incref(source);
+ *dest = source;
+ return 0;
+}
+
+void git_odb_object_free(git_odb_object *object)
+{
+ if (object == NULL)
+ return;
+
+ git_cached_obj_decref(object);
+}
+
+int git_odb__hashfd(
+ git_oid *out,
+ git_file fd,
+ size_t size,
+ git_object_t object_type,
+ git_oid_t oid_type)
+{
+ size_t hdr_len;
+ char hdr[64], buffer[GIT_BUFSIZE_FILEIO];
+ git_hash_ctx ctx;
+ git_hash_algorithm_t algorithm;
+ ssize_t read_len = 0;
+ int error = 0;
+
+ if (!git_object_typeisloose(object_type)) {
+ git_error_set(GIT_ERROR_INVALID, "invalid object type for hash");
+ return -1;
+ }
+
+ if (!(algorithm = git_oid_algorithm(oid_type))) {
+ git_error_set(GIT_ERROR_INVALID, "unknown oid type");
+ return -1;
+ }
+
+ if ((error = git_hash_ctx_init(&ctx, algorithm)) < 0)
+ return error;
+
+ if ((error = git_odb__format_object_header(&hdr_len, hdr,
+ sizeof(hdr), size, object_type)) < 0)
+ goto done;
+
+ if ((error = git_hash_update(&ctx, hdr, hdr_len)) < 0)
+ goto done;
+
+ while (size > 0 && (read_len = p_read(fd, buffer, sizeof(buffer))) > 0) {
+ if ((error = git_hash_update(&ctx, buffer, read_len)) < 0)
+ goto done;
+
+ size -= read_len;
+ }
+
+ /* If p_read returned an error code, the read obviously failed.
+ * If size is not zero, the file was truncated after we originally
+ * stat'd it, so we consider this a read failure too */
+ if (read_len < 0 || size > 0) {
+ git_error_set(GIT_ERROR_OS, "error reading file for hashing");
+ error = -1;
+
+ goto done;
+ }
+
+ error = git_hash_final(out->id, &ctx);
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ out->type = oid_type;
+#endif
+
+done:
+ git_hash_ctx_cleanup(&ctx);
+ return error;
+}
+
+int git_odb__hashfd_filtered(
+ git_oid *out,
+ git_file fd,
+ size_t size,
+ git_object_t object_type,
+ git_oid_t oid_type,
+ git_filter_list *fl)
+{
+ int error;
+ git_str raw = GIT_STR_INIT;
+
+ if (!fl)
+ return git_odb__hashfd(out, fd, size, object_type, oid_type);
+
+ /* size of data is used in header, so we have to read the whole file
+ * into memory to apply filters before beginning to calculate the hash
+ */
+
+ if (!(error = git_futils_readbuffer_fd(&raw, fd, size))) {
+ git_str post = GIT_STR_INIT;
+
+ error = git_filter_list__convert_buf(&post, fl, &raw);
+
+ if (!error)
+ error = git_odb__hash(out, post.ptr, post.size, object_type, oid_type);
+
+ git_str_dispose(&post);
+ }
+
+ return error;
+}
+
+int git_odb__hashlink(git_oid *out, const char *path, git_oid_t oid_type)
+{
+ struct stat st;
+ int size;
+ int result;
+
+ if (git_fs_path_lstat(path, &st) < 0)
+ return -1;
+
+ if (!git__is_int(st.st_size) || (int)st.st_size < 0) {
+ git_error_set(GIT_ERROR_FILESYSTEM, "file size overflow for 32-bit systems");
+ return -1;
+ }
+
+ size = (int)st.st_size;
+
+ if (S_ISLNK(st.st_mode)) {
+ char *link_data;
+ int read_len;
+ size_t alloc_size;
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_size, size, 1);
+ link_data = git__malloc(alloc_size);
+ GIT_ERROR_CHECK_ALLOC(link_data);
+
+ read_len = p_readlink(path, link_data, size);
+ if (read_len == -1) {
+ git_error_set(GIT_ERROR_OS, "failed to read symlink data for '%s'", path);
+ git__free(link_data);
+ return -1;
+ }
+ GIT_ASSERT(read_len <= size);
+ link_data[read_len] = '\0';
+
+ result = git_odb__hash(out, link_data, read_len, GIT_OBJECT_BLOB, oid_type);
+ git__free(link_data);
+ } else {
+ int fd = git_futils_open_ro(path);
+ if (fd < 0)
+ return -1;
+ result = git_odb__hashfd(out, fd, size, GIT_OBJECT_BLOB, oid_type);
+ p_close(fd);
+ }
+
+ return result;
+}
+
+int git_odb__hashfile(
+ git_oid *out,
+ const char *path,
+ git_object_t object_type,
+ git_oid_t oid_type)
+{
+ uint64_t size;
+ int fd, error = 0;
+
+ if ((fd = git_futils_open_ro(path)) < 0)
+ return fd;
+
+ if ((error = git_futils_filesize(&size, fd)) < 0)
+ goto done;
+
+ if (!git__is_sizet(size)) {
+ git_error_set(GIT_ERROR_OS, "file size overflow for 32-bit systems");
+ error = -1;
+ goto done;
+ }
+
+ error = git_odb__hashfd(out, fd, (size_t)size, object_type, oid_type);
+
+done:
+ p_close(fd);
+ return error;
+}
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+int git_odb_hashfile(
+ git_oid *out,
+ const char *path,
+ git_object_t object_type,
+ git_oid_t oid_type)
+{
+ return git_odb__hashfile(out, path, object_type, oid_type);
+}
+#else
+int git_odb_hashfile(
+ git_oid *out,
+ const char *path,
+ git_object_t object_type)
+{
+ return git_odb__hashfile(out, path, object_type, GIT_OID_SHA1);
+}
+#endif
+
+int git_odb__hash(
+ git_oid *id,
+ const void *data,
+ size_t len,
+ git_object_t object_type,
+ git_oid_t oid_type)
+{
+ git_rawobj raw;
+
+ GIT_ASSERT_ARG(id);
+
+ raw.data = (void *)data;
+ raw.len = len;
+ raw.type = object_type;
+
+ return git_odb__hashobj(id, &raw, oid_type);
+}
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+int git_odb_hash(
+ git_oid *out,
+ const void *data,
+ size_t len,
+ git_object_t object_type,
+ git_oid_t oid_type)
+{
+ return git_odb__hash(out, data, len, object_type, oid_type);
+}
+#else
+int git_odb_hash(
+ git_oid *out,
+ const void *data,
+ size_t len,
+ git_object_t type)
+{
+ return git_odb__hash(out, data, len, type, GIT_OID_SHA1);
+}
+#endif
+
+/**
+ * FAKE WSTREAM
+ */
+
+typedef struct {
+ git_odb_stream stream;
+ char *buffer;
+ size_t size, written;
+ git_object_t type;
+} fake_wstream;
+
+static int fake_wstream__fwrite(git_odb_stream *_stream, const git_oid *oid)
+{
+ fake_wstream *stream = (fake_wstream *)_stream;
+ return _stream->backend->write(_stream->backend, oid, stream->buffer, stream->size, stream->type);
+}
+
+static int fake_wstream__write(git_odb_stream *_stream, const char *data, size_t len)
+{
+ fake_wstream *stream = (fake_wstream *)_stream;
+
+ GIT_ASSERT(stream->written + len <= stream->size);
+
+ memcpy(stream->buffer + stream->written, data, len);
+ stream->written += len;
+ return 0;
+}
+
+static void fake_wstream__free(git_odb_stream *_stream)
+{
+ fake_wstream *stream = (fake_wstream *)_stream;
+
+ git__free(stream->buffer);
+ git__free(stream);
+}
+
+static int init_fake_wstream(git_odb_stream **stream_p, git_odb_backend *backend, git_object_size_t size, git_object_t type)
+{
+ fake_wstream *stream;
+ size_t blobsize;
+
+ GIT_ERROR_CHECK_BLOBSIZE(size);
+ blobsize = (size_t)size;
+
+ stream = git__calloc(1, sizeof(fake_wstream));
+ GIT_ERROR_CHECK_ALLOC(stream);
+
+ stream->size = blobsize;
+ stream->type = type;
+ stream->buffer = git__malloc(blobsize);
+ if (stream->buffer == NULL) {
+ git__free(stream);
+ return -1;
+ }
+
+ stream->stream.backend = backend;
+ stream->stream.read = NULL; /* read only */
+ stream->stream.write = &fake_wstream__write;
+ stream->stream.finalize_write = &fake_wstream__fwrite;
+ stream->stream.free = &fake_wstream__free;
+ stream->stream.mode = GIT_STREAM_WRONLY;
+
+ *stream_p = (git_odb_stream *)stream;
+ return 0;
+}
+
+/***********************************************************
+ *
+ * OBJECT DATABASE PUBLIC API
+ *
+ * Public calls for the ODB functionality
+ *
+ ***********************************************************/
+
+static int backend_sort_cmp(const void *a, const void *b)
+{
+ const backend_internal *backend_a = (const backend_internal *)(a);
+ const backend_internal *backend_b = (const backend_internal *)(b);
+
+ if (backend_b->priority == backend_a->priority) {
+ if (backend_a->is_alternate)
+ return -1;
+ if (backend_b->is_alternate)
+ return 1;
+ return 0;
+ }
+ return (backend_b->priority - backend_a->priority);
+}
+
+static void normalize_options(
+ git_odb_options *opts,
+ const git_odb_options *given_opts)
+{
+ git_odb_options init = GIT_ODB_OPTIONS_INIT;
+
+ if (given_opts)
+ memcpy(opts, given_opts, sizeof(git_odb_options));
+ else
+ memcpy(opts, &init, sizeof(git_odb_options));
+
+ if (!opts->oid_type)
+ opts->oid_type = GIT_OID_DEFAULT;
+}
+
+int git_odb__new(git_odb **out, const git_odb_options *opts)
+{
+ git_odb *db = git__calloc(1, sizeof(*db));
+ GIT_ERROR_CHECK_ALLOC(db);
+
+ normalize_options(&db->options, opts);
+
+ if (git_mutex_init(&db->lock) < 0) {
+ git__free(db);
+ return -1;
+ }
+ if (git_cache_init(&db->own_cache) < 0) {
+ git_mutex_free(&db->lock);
+ git__free(db);
+ return -1;
+ }
+ if (git_vector_init(&db->backends, 4, backend_sort_cmp) < 0) {
+ git_cache_dispose(&db->own_cache);
+ git_mutex_free(&db->lock);
+ git__free(db);
+ return -1;
+ }
+
+ *out = db;
+ GIT_REFCOUNT_INC(db);
+ return 0;
+}
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+int git_odb_new(git_odb **out, const git_odb_options *opts)
+{
+ return git_odb__new(out, opts);
+}
+#else
+int git_odb_new(git_odb **out)
+{
+ return git_odb__new(out, NULL);
+}
+#endif
+
+static int add_backend_internal(
+ git_odb *odb, git_odb_backend *backend,
+ int priority, bool is_alternate, ino_t disk_inode)
+{
+ backend_internal *internal;
+
+ GIT_ASSERT_ARG(odb);
+ GIT_ASSERT_ARG(backend);
+
+ GIT_ERROR_CHECK_VERSION(backend, GIT_ODB_BACKEND_VERSION, "git_odb_backend");
+
+ /* Check if the backend is already owned by another ODB */
+ GIT_ASSERT(!backend->odb || backend->odb == odb);
+
+ internal = git__malloc(sizeof(backend_internal));
+ GIT_ERROR_CHECK_ALLOC(internal);
+
+ internal->backend = backend;
+ internal->priority = priority;
+ internal->is_alternate = is_alternate;
+ internal->disk_inode = disk_inode;
+
+ if (git_mutex_lock(&odb->lock) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ return -1;
+ }
+ if (git_vector_insert(&odb->backends, internal) < 0) {
+ git_mutex_unlock(&odb->lock);
+ git__free(internal);
+ return -1;
+ }
+ git_vector_sort(&odb->backends);
+ internal->backend->odb = odb;
+ git_mutex_unlock(&odb->lock);
+ return 0;
+}
+
+int git_odb_add_backend(git_odb *odb, git_odb_backend *backend, int priority)
+{
+ return add_backend_internal(odb, backend, priority, false, 0);
+}
+
+int git_odb_add_alternate(git_odb *odb, git_odb_backend *backend, int priority)
+{
+ return add_backend_internal(odb, backend, priority, true, 0);
+}
+
+size_t git_odb_num_backends(git_odb *odb)
+{
+ size_t length;
+ bool locked = true;
+
+ GIT_ASSERT_ARG(odb);
+
+ if (git_mutex_lock(&odb->lock) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ locked = false;
+ }
+ length = odb->backends.length;
+ if (locked)
+ git_mutex_unlock(&odb->lock);
+ return length;
+}
+
+static int git_odb__error_unsupported_in_backend(const char *action)
+{
+ git_error_set(GIT_ERROR_ODB,
+ "cannot %s - unsupported in the loaded odb backends", action);
+ return -1;
+}
+
+
+int git_odb_get_backend(git_odb_backend **out, git_odb *odb, size_t pos)
+{
+ backend_internal *internal;
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(odb);
+
+
+ if ((error = git_mutex_lock(&odb->lock)) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ return error;
+ }
+ internal = git_vector_get(&odb->backends, pos);
+
+ if (!internal || !internal->backend) {
+ git_mutex_unlock(&odb->lock);
+
+ git_error_set(GIT_ERROR_ODB, "no ODB backend loaded at index %" PRIuZ, pos);
+ return GIT_ENOTFOUND;
+ }
+ *out = internal->backend;
+ git_mutex_unlock(&odb->lock);
+
+ return 0;
+}
+
+int git_odb__add_default_backends(
+ git_odb *db, const char *objects_dir,
+ bool as_alternates, int alternate_depth)
+{
+ size_t i = 0;
+ struct stat st;
+ ino_t inode;
+ git_odb_backend *loose, *packed;
+ git_odb_backend_loose_options loose_opts = GIT_ODB_BACKEND_LOOSE_OPTIONS_INIT;
+ git_odb_backend_pack_options pack_opts = GIT_ODB_BACKEND_PACK_OPTIONS_INIT;
+
+ /* TODO: inodes are not really relevant on Win32, so we need to find
+ * a cross-platform workaround for this */
+#ifdef GIT_WIN32
+ GIT_UNUSED(i);
+ GIT_UNUSED(&st);
+
+ inode = 0;
+#else
+ if (p_stat(objects_dir, &st) < 0) {
+ if (as_alternates)
+ /* this should warn */
+ return 0;
+
+ git_error_set(GIT_ERROR_ODB, "failed to load object database in '%s'", objects_dir);
+ return -1;
+ }
+
+ inode = st.st_ino;
+
+ if (git_mutex_lock(&db->lock) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ return -1;
+ }
+ for (i = 0; i < db->backends.length; ++i) {
+ backend_internal *backend = git_vector_get(&db->backends, i);
+ if (backend->disk_inode == inode) {
+ git_mutex_unlock(&db->lock);
+ return 0;
+ }
+ }
+ git_mutex_unlock(&db->lock);
+#endif
+
+ if (db->do_fsync)
+ loose_opts.flags |= GIT_ODB_BACKEND_LOOSE_FSYNC;
+
+ loose_opts.oid_type = db->options.oid_type;
+ pack_opts.oid_type = db->options.oid_type;
+
+ /* add the loose object backend */
+ if (git_odb__backend_loose(&loose, objects_dir, &loose_opts) < 0 ||
+ add_backend_internal(db, loose, git_odb__loose_priority, as_alternates, inode) < 0)
+ return -1;
+
+ /* add the packed file backend */
+#ifdef GIT_EXPERIMENTAL_SHA256
+ if (git_odb_backend_pack(&packed, objects_dir, &pack_opts) < 0)
+ return -1;
+#else
+ GIT_UNUSED(pack_opts);
+
+ if (git_odb_backend_pack(&packed, objects_dir) < 0)
+ return -1;
+#endif
+
+ if (add_backend_internal(db, packed, git_odb__packed_priority, as_alternates, inode) < 0)
+ return -1;
+
+ if (git_mutex_lock(&db->lock) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ return -1;
+ }
+ if (!db->cgraph &&
+ git_commit_graph_new(&db->cgraph, objects_dir, false, db->options.oid_type) < 0) {
+ git_mutex_unlock(&db->lock);
+ return -1;
+ }
+ git_mutex_unlock(&db->lock);
+
+ return load_alternates(db, objects_dir, alternate_depth);
+}
+
+static int load_alternates(git_odb *odb, const char *objects_dir, int alternate_depth)
+{
+ git_str alternates_path = GIT_STR_INIT;
+ git_str alternates_buf = GIT_STR_INIT;
+ char *buffer;
+ const char *alternate;
+ int result = 0;
+
+ /* Git reports an error, we just ignore anything deeper */
+ if (alternate_depth > GIT_ALTERNATES_MAX_DEPTH)
+ return 0;
+
+ if (git_str_joinpath(&alternates_path, objects_dir, GIT_ALTERNATES_FILE) < 0)
+ return -1;
+
+ if (git_fs_path_exists(alternates_path.ptr) == false) {
+ git_str_dispose(&alternates_path);
+ return 0;
+ }
+
+ if (git_futils_readbuffer(&alternates_buf, alternates_path.ptr) < 0) {
+ git_str_dispose(&alternates_path);
+ return -1;
+ }
+
+ buffer = (char *)alternates_buf.ptr;
+
+ /* add each alternate as a new backend; one alternate per line */
+ while ((alternate = git__strtok(&buffer, "\r\n")) != NULL) {
+ if (*alternate == '\0' || *alternate == '#')
+ continue;
+
+ /*
+ * Relative path: build based on the current `objects`
+ * folder. However, relative paths are only allowed in
+ * the current repository.
+ */
+ if (*alternate == '.' && !alternate_depth) {
+ if ((result = git_str_joinpath(&alternates_path, objects_dir, alternate)) < 0)
+ break;
+ alternate = git_str_cstr(&alternates_path);
+ }
+
+ if ((result = git_odb__add_default_backends(odb, alternate, true, alternate_depth + 1)) < 0)
+ break;
+ }
+
+ git_str_dispose(&alternates_path);
+ git_str_dispose(&alternates_buf);
+
+ return result;
+}
+
+int git_odb_add_disk_alternate(git_odb *odb, const char *path)
+{
+ return git_odb__add_default_backends(odb, path, true, 0);
+}
+
+int git_odb_set_commit_graph(git_odb *odb, git_commit_graph *cgraph)
+{
+ int error = 0;
+
+ GIT_ASSERT_ARG(odb);
+
+ if ((error = git_mutex_lock(&odb->lock)) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the db lock");
+ return error;
+ }
+ git_commit_graph_free(odb->cgraph);
+ odb->cgraph = cgraph;
+ git_mutex_unlock(&odb->lock);
+
+ return error;
+}
+
+int git_odb__open(
+ git_odb **out,
+ const char *objects_dir,
+ const git_odb_options *opts)
+{
+ git_odb *db;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(objects_dir);
+
+ *out = NULL;
+
+ if (git_odb__new(&db, opts) < 0)
+ return -1;
+
+ if (git_odb__add_default_backends(db, objects_dir, 0, 0) < 0) {
+ git_odb_free(db);
+ return -1;
+ }
+
+ *out = db;
+ return 0;
+}
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+
+int git_odb_open(
+ git_odb **out,
+ const char *objects_dir,
+ const git_odb_options *opts)
+{
+ return git_odb__open(out, objects_dir, opts);
+}
+
+#else
+
+int git_odb_open(git_odb **out, const char *objects_dir)
+{
+ return git_odb__open(out, objects_dir, NULL);
+}
+
+#endif
+
+int git_odb__set_caps(git_odb *odb, int caps)
+{
+ if (caps == GIT_ODB_CAP_FROM_OWNER) {
+ git_repository *repo = GIT_REFCOUNT_OWNER(odb);
+ int val;
+
+ if (!repo) {
+ git_error_set(GIT_ERROR_ODB, "cannot access repository to set odb caps");
+ return -1;
+ }
+
+ if (!git_repository__configmap_lookup(&val, repo, GIT_CONFIGMAP_FSYNCOBJECTFILES))
+ odb->do_fsync = !!val;
+ }
+
+ return 0;
+}
+
+static void odb_free(git_odb *db)
+{
+ size_t i;
+ bool locked = true;
+
+ if (git_mutex_lock(&db->lock) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ locked = false;
+ }
+ for (i = 0; i < db->backends.length; ++i) {
+ backend_internal *internal = git_vector_get(&db->backends, i);
+ git_odb_backend *backend = internal->backend;
+
+ backend->free(backend);
+
+ git__free(internal);
+ }
+ if (locked)
+ git_mutex_unlock(&db->lock);
+
+ git_commit_graph_free(db->cgraph);
+ git_vector_free(&db->backends);
+ git_cache_dispose(&db->own_cache);
+ git_mutex_free(&db->lock);
+
+ git__memzero(db, sizeof(*db));
+ git__free(db);
+}
+
+void git_odb_free(git_odb *db)
+{
+ if (db == NULL)
+ return;
+
+ GIT_REFCOUNT_DEC(db, odb_free);
+}
+
+static int odb_exists_1(
+ git_odb *db,
+ const git_oid *id,
+ bool only_refreshed)
+{
+ size_t i;
+ bool found = false;
+ int error;
+
+ if ((error = git_mutex_lock(&db->lock)) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ return error;
+ }
+ for (i = 0; i < db->backends.length && !found; ++i) {
+ backend_internal *internal = git_vector_get(&db->backends, i);
+ git_odb_backend *b = internal->backend;
+
+ if (only_refreshed && !b->refresh)
+ continue;
+
+ if (b->exists != NULL)
+ found = (bool)b->exists(b, id);
+ }
+ git_mutex_unlock(&db->lock);
+
+ return (int)found;
+}
+
+int git_odb__get_commit_graph_file(git_commit_graph_file **out, git_odb *db)
+{
+ int error = 0;
+ git_commit_graph_file *result = NULL;
+
+ if ((error = git_mutex_lock(&db->lock)) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the db lock");
+ return error;
+ }
+ if (!db->cgraph) {
+ error = GIT_ENOTFOUND;
+ goto done;
+ }
+ error = git_commit_graph_get_file(&result, db->cgraph);
+ if (error)
+ goto done;
+ *out = result;
+
+done:
+ git_mutex_unlock(&db->lock);
+ return error;
+}
+
+static int odb_freshen_1(
+ git_odb *db,
+ const git_oid *id,
+ bool only_refreshed)
+{
+ size_t i;
+ bool found = false;
+ int error;
+
+ if ((error = git_mutex_lock(&db->lock)) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ return error;
+ }
+ for (i = 0; i < db->backends.length && !found; ++i) {
+ backend_internal *internal = git_vector_get(&db->backends, i);
+ git_odb_backend *b = internal->backend;
+
+ if (only_refreshed && !b->refresh)
+ continue;
+
+ if (b->freshen != NULL)
+ found = !b->freshen(b, id);
+ else if (b->exists != NULL)
+ found = b->exists(b, id);
+ }
+ git_mutex_unlock(&db->lock);
+
+ return (int)found;
+}
+
+int git_odb__freshen(git_odb *db, const git_oid *id)
+{
+ GIT_ASSERT_ARG(db);
+ GIT_ASSERT_ARG(id);
+
+ if (odb_freshen_1(db, id, false))
+ return 1;
+
+ if (!git_odb_refresh(db))
+ return odb_freshen_1(db, id, true);
+
+ /* Failed to refresh, hence not found */
+ return 0;
+}
+
+int git_odb_exists(git_odb *db, const git_oid *id)
+{
+ return git_odb_exists_ext(db, id, 0);
+}
+
+int git_odb_exists_ext(git_odb *db, const git_oid *id, unsigned int flags)
+{
+ git_odb_object *object;
+
+ GIT_ASSERT_ARG(db);
+ GIT_ASSERT_ARG(id);
+
+ if (git_oid_is_zero(id))
+ return 0;
+
+ if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) {
+ git_odb_object_free(object);
+ return 1;
+ }
+
+ if (odb_exists_1(db, id, false))
+ return 1;
+
+ if (!(flags & GIT_ODB_LOOKUP_NO_REFRESH) && !git_odb_refresh(db))
+ return odb_exists_1(db, id, true);
+
+ /* Failed to refresh, hence not found */
+ return 0;
+}
+
+static int odb_exists_prefix_1(git_oid *out, git_odb *db,
+ const git_oid *key, size_t len, bool only_refreshed)
+{
+ size_t i;
+ int error = GIT_ENOTFOUND, num_found = 0;
+ git_oid last_found = GIT_OID_NONE, found;
+
+ if ((error = git_mutex_lock(&db->lock)) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ return error;
+ }
+ error = GIT_ENOTFOUND;
+ for (i = 0; i < db->backends.length; ++i) {
+ backend_internal *internal = git_vector_get(&db->backends, i);
+ git_odb_backend *b = internal->backend;
+
+ if (only_refreshed && !b->refresh)
+ continue;
+
+ if (!b->exists_prefix)
+ continue;
+
+ error = b->exists_prefix(&found, b, key, len);
+ if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH)
+ continue;
+ if (error) {
+ git_mutex_unlock(&db->lock);
+ return error;
+ }
+
+ /* make sure found item doesn't introduce ambiguity */
+ if (num_found) {
+ if (git_oid__cmp(&last_found, &found)) {
+ git_mutex_unlock(&db->lock);
+ return git_odb__error_ambiguous("multiple matches for prefix");
+ }
+ } else {
+ git_oid_cpy(&last_found, &found);
+ num_found++;
+ }
+ }
+ git_mutex_unlock(&db->lock);
+
+ if (!num_found)
+ return GIT_ENOTFOUND;
+
+ if (out)
+ git_oid_cpy(out, &last_found);
+
+ return 0;
+}
+
+int git_odb_exists_prefix(
+ git_oid *out, git_odb *db, const git_oid *short_id, size_t len)
+{
+ int error;
+ git_oid key = GIT_OID_NONE;
+
+ GIT_ASSERT_ARG(db);
+ GIT_ASSERT_ARG(short_id);
+
+ if (len < GIT_OID_MINPREFIXLEN)
+ return git_odb__error_ambiguous("prefix length too short");
+
+ if (len >= git_oid_hexsize(db->options.oid_type)) {
+ if (git_odb_exists(db, short_id)) {
+ if (out)
+ git_oid_cpy(out, short_id);
+ return 0;
+ } else {
+ return git_odb__error_notfound(
+ "no match for id prefix", short_id, len);
+ }
+ }
+
+ git_oid__cpy_prefix(&key, short_id, len);
+
+ error = odb_exists_prefix_1(out, db, &key, len, false);
+
+ if (error == GIT_ENOTFOUND && !git_odb_refresh(db))
+ error = odb_exists_prefix_1(out, db, &key, len, true);
+
+ if (error == GIT_ENOTFOUND)
+ return git_odb__error_notfound("no match for id prefix", &key, len);
+
+ return error;
+}
+
+int git_odb_expand_ids(
+ git_odb *db,
+ git_odb_expand_id *ids,
+ size_t count)
+{
+ size_t hex_size, i;
+
+ GIT_ASSERT_ARG(db);
+ GIT_ASSERT_ARG(ids);
+
+ hex_size = git_oid_hexsize(db->options.oid_type);
+
+ for (i = 0; i < count; i++) {
+ git_odb_expand_id *query = &ids[i];
+ int error = GIT_EAMBIGUOUS;
+
+ if (!query->type)
+ query->type = GIT_OBJECT_ANY;
+
+ /* if we have a short OID, expand it first */
+ if (query->length >= GIT_OID_MINPREFIXLEN && query->length < hex_size) {
+ git_oid actual_id;
+
+ error = odb_exists_prefix_1(&actual_id, db, &query->id, query->length, false);
+ if (!error) {
+ git_oid_cpy(&query->id, &actual_id);
+ query->length = (unsigned short)hex_size;
+ }
+ }
+
+ /*
+ * now we ought to have a 40-char OID, either because we've expanded it
+ * or because the user passed a full OID. Ensure its type is right.
+ */
+ if (query->length >= hex_size) {
+ git_object_t actual_type;
+
+ error = odb_otype_fast(&actual_type, db, &query->id);
+ if (!error) {
+ if (query->type != GIT_OBJECT_ANY && query->type != actual_type)
+ error = GIT_ENOTFOUND;
+ else
+ query->type = actual_type;
+ }
+ }
+
+ switch (error) {
+ /* no errors, so we've successfully expanded the OID */
+ case 0:
+ continue;
+
+ /* the object is missing or ambiguous */
+ case GIT_ENOTFOUND:
+ case GIT_EAMBIGUOUS:
+ git_oid_clear(&query->id, db->options.oid_type);
+ query->length = 0;
+ query->type = 0;
+ break;
+
+ /* something went very wrong with the ODB; bail hard */
+ default:
+ return error;
+ }
+ }
+
+ git_error_clear();
+ return 0;
+}
+
+int git_odb_read_header(size_t *len_p, git_object_t *type_p, git_odb *db, const git_oid *id)
+{
+ int error;
+ git_odb_object *object = NULL;
+
+ error = git_odb__read_header_or_object(&object, len_p, type_p, db, id);
+
+ if (object)
+ git_odb_object_free(object);
+
+ return error;
+}
+
+static int odb_read_header_1(
+ size_t *len_p, git_object_t *type_p, git_odb *db,
+ const git_oid *id, bool only_refreshed)
+{
+ size_t i;
+ git_object_t ht;
+ bool passthrough = false;
+ int error;
+
+ if (!only_refreshed && (ht = odb_hardcoded_type(id)) != GIT_OBJECT_INVALID) {
+ *type_p = ht;
+ *len_p = 0;
+ return 0;
+ }
+
+ if ((error = git_mutex_lock(&db->lock)) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ return error;
+ }
+ for (i = 0; i < db->backends.length; ++i) {
+ backend_internal *internal = git_vector_get(&db->backends, i);
+ git_odb_backend *b = internal->backend;
+
+ if (only_refreshed && !b->refresh)
+ continue;
+
+ if (!b->read_header) {
+ passthrough = true;
+ continue;
+ }
+
+ error = b->read_header(len_p, type_p, b, id);
+
+ switch (error) {
+ case GIT_PASSTHROUGH:
+ passthrough = true;
+ break;
+ case GIT_ENOTFOUND:
+ break;
+ default:
+ git_mutex_unlock(&db->lock);
+ return error;
+ }
+ }
+ git_mutex_unlock(&db->lock);
+
+ return passthrough ? GIT_PASSTHROUGH : GIT_ENOTFOUND;
+}
+
+int git_odb__read_header_or_object(
+ git_odb_object **out, size_t *len_p, git_object_t *type_p,
+ git_odb *db, const git_oid *id)
+{
+ int error = GIT_ENOTFOUND;
+ git_odb_object *object;
+
+ GIT_ASSERT_ARG(db);
+ GIT_ASSERT_ARG(id);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(len_p);
+ GIT_ASSERT_ARG(type_p);
+
+ *out = NULL;
+
+ if (git_oid_is_zero(id))
+ return error_null_oid(GIT_ENOTFOUND, "cannot read object");
+
+ if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) {
+ *len_p = object->cached.size;
+ *type_p = object->cached.type;
+ *out = object;
+ return 0;
+ }
+
+ error = odb_read_header_1(len_p, type_p, db, id, false);
+
+ if (error == GIT_ENOTFOUND && !git_odb_refresh(db))
+ error = odb_read_header_1(len_p, type_p, db, id, true);
+
+ if (error == GIT_ENOTFOUND)
+ return git_odb__error_notfound("cannot read header for", id, git_oid_hexsize(db->options.oid_type));
+
+ /* we found the header; return early */
+ if (!error)
+ return 0;
+
+ if (error == GIT_PASSTHROUGH) {
+ /*
+ * no backend has header-reading functionality
+ * so try using `git_odb_read` instead
+ */
+ error = git_odb_read(&object, db, id);
+ if (!error) {
+ *len_p = object->cached.size;
+ *type_p = object->cached.type;
+ *out = object;
+ }
+ }
+
+ return error;
+}
+
+static int odb_read_1(
+ git_odb_object **out,
+ git_odb *db,
+ const git_oid *id,
+ bool only_refreshed)
+{
+ size_t i;
+ git_rawobj raw;
+ git_odb_object *object;
+ git_oid hashed;
+ bool found = false;
+ int error = 0;
+
+ if (!only_refreshed) {
+ if ((error = odb_read_hardcoded(&found, &raw, id)) < 0)
+ return error;
+ }
+
+ if ((error = git_mutex_lock(&db->lock)) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ return error;
+ }
+ for (i = 0; i < db->backends.length && !found; ++i) {
+ backend_internal *internal = git_vector_get(&db->backends, i);
+ git_odb_backend *b = internal->backend;
+
+ if (only_refreshed && !b->refresh)
+ continue;
+
+ if (b->read != NULL) {
+ error = b->read(&raw.data, &raw.len, &raw.type, b, id);
+ if (error == GIT_PASSTHROUGH || error == GIT_ENOTFOUND)
+ continue;
+
+ if (error < 0) {
+ git_mutex_unlock(&db->lock);
+ return error;
+ }
+
+ found = true;
+ }
+ }
+ git_mutex_unlock(&db->lock);
+
+ if (!found)
+ return GIT_ENOTFOUND;
+
+ if (git_odb__strict_hash_verification) {
+ if ((error = git_odb__hash(&hashed, raw.data, raw.len, raw.type, db->options.oid_type)) < 0)
+ goto out;
+
+ if (!git_oid_equal(id, &hashed)) {
+ error = git_odb__error_mismatch(id, &hashed);
+ goto out;
+ }
+ }
+
+ git_error_clear();
+ if ((object = odb_object__alloc(id, &raw)) == NULL) {
+ error = -1;
+ goto out;
+ }
+
+ *out = git_cache_store_raw(odb_cache(db), object);
+
+out:
+ if (error)
+ git__free(raw.data);
+ return error;
+}
+
+int git_odb_read(git_odb_object **out, git_odb *db, const git_oid *id)
+{
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(db);
+ GIT_ASSERT_ARG(id);
+
+ if (git_oid_is_zero(id))
+ return error_null_oid(GIT_ENOTFOUND, "cannot read object");
+
+ *out = git_cache_get_raw(odb_cache(db), id);
+ if (*out != NULL)
+ return 0;
+
+ error = odb_read_1(out, db, id, false);
+
+ if (error == GIT_ENOTFOUND && !git_odb_refresh(db))
+ error = odb_read_1(out, db, id, true);
+
+ if (error == GIT_ENOTFOUND)
+ return git_odb__error_notfound("no match for id", id, git_oid_hexsize(git_oid_type(id)));
+
+ return error;
+}
+
+static int odb_otype_fast(git_object_t *type_p, git_odb *db, const git_oid *id)
+{
+ git_odb_object *object;
+ size_t _unused;
+ int error;
+
+ if (git_oid_is_zero(id))
+ return error_null_oid(GIT_ENOTFOUND, "cannot get object type");
+
+ if ((object = git_cache_get_raw(odb_cache(db), id)) != NULL) {
+ *type_p = object->cached.type;
+ git_odb_object_free(object);
+ return 0;
+ }
+
+ error = odb_read_header_1(&_unused, type_p, db, id, false);
+
+ if (error == GIT_PASSTHROUGH) {
+ error = odb_read_1(&object, db, id, false);
+ if (!error)
+ *type_p = object->cached.type;
+ git_odb_object_free(object);
+ }
+
+ return error;
+}
+
+static int read_prefix_1(git_odb_object **out, git_odb *db,
+ const git_oid *key, size_t len, bool only_refreshed)
+{
+ size_t i;
+ int error = 0;
+ git_oid found_full_oid = GIT_OID_NONE;
+ git_rawobj raw = {0};
+ void *data = NULL;
+ bool found = false;
+ git_odb_object *object;
+
+ if ((error = git_mutex_lock(&db->lock)) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ return error;
+ }
+ for (i = 0; i < db->backends.length; ++i) {
+ backend_internal *internal = git_vector_get(&db->backends, i);
+ git_odb_backend *b = internal->backend;
+
+ if (only_refreshed && !b->refresh)
+ continue;
+
+ if (b->read_prefix != NULL) {
+ git_oid full_oid;
+ error = b->read_prefix(&full_oid, &raw.data, &raw.len, &raw.type, b, key, len);
+
+ if (error == GIT_ENOTFOUND || error == GIT_PASSTHROUGH) {
+ error = 0;
+ continue;
+ }
+
+ if (error) {
+ git_mutex_unlock(&db->lock);
+ goto out;
+ }
+
+ git__free(data);
+ data = raw.data;
+
+ if (found && git_oid__cmp(&full_oid, &found_full_oid)) {
+ git_str buf = GIT_STR_INIT;
+ const char *idstr;
+
+ if ((idstr = git_oid_tostr_s(&full_oid)) == NULL) {
+ git_str_puts(&buf, "failed to parse object id");
+ } else {
+ git_str_printf(&buf, "multiple matches for prefix: %s", idstr);
+
+ if ((idstr = git_oid_tostr_s(&found_full_oid)) != NULL)
+ git_str_printf(&buf, " %s", idstr);
+ }
+
+ error = git_odb__error_ambiguous(buf.ptr);
+ git_str_dispose(&buf);
+ git_mutex_unlock(&db->lock);
+ goto out;
+ }
+
+ found_full_oid = full_oid;
+ found = true;
+ }
+ }
+ git_mutex_unlock(&db->lock);
+
+ if (!found)
+ return GIT_ENOTFOUND;
+
+ if (git_odb__strict_hash_verification) {
+ git_oid hash;
+
+ if ((error = git_odb__hash(&hash, raw.data, raw.len, raw.type, db->options.oid_type)) < 0)
+ goto out;
+
+ if (!git_oid_equal(&found_full_oid, &hash)) {
+ error = git_odb__error_mismatch(&found_full_oid, &hash);
+ goto out;
+ }
+ }
+
+ if ((object = odb_object__alloc(&found_full_oid, &raw)) == NULL) {
+ error = -1;
+ goto out;
+ }
+
+ *out = git_cache_store_raw(odb_cache(db), object);
+
+out:
+ if (error)
+ git__free(raw.data);
+
+ return error;
+}
+
+int git_odb_read_prefix(
+ git_odb_object **out, git_odb *db, const git_oid *short_id, size_t len)
+{
+ git_oid key = GIT_OID_NONE;
+ size_t hex_size;
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(db);
+
+ hex_size = git_oid_hexsize(db->options.oid_type);
+
+ if (len < GIT_OID_MINPREFIXLEN)
+ return git_odb__error_ambiguous("prefix length too short");
+
+ if (len > hex_size)
+ len = hex_size;
+
+ if (len == hex_size) {
+ *out = git_cache_get_raw(odb_cache(db), short_id);
+ if (*out != NULL)
+ return 0;
+ }
+
+ git_oid__cpy_prefix(&key, short_id, len);
+
+ error = read_prefix_1(out, db, &key, len, false);
+
+ if (error == GIT_ENOTFOUND && !git_odb_refresh(db))
+ error = read_prefix_1(out, db, &key, len, true);
+
+ if (error == GIT_ENOTFOUND)
+ return git_odb__error_notfound("no match for prefix", &key, len);
+
+ return error;
+}
+
+int git_odb_foreach(git_odb *db, git_odb_foreach_cb cb, void *payload)
+{
+ unsigned int i;
+ git_vector backends = GIT_VECTOR_INIT;
+ backend_internal *internal;
+ int error = 0;
+
+ /* Make a copy of the backends vector to invoke the callback without holding the lock. */
+ if ((error = git_mutex_lock(&db->lock)) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ goto cleanup;
+ }
+ error = git_vector_dup(&backends, &db->backends, NULL);
+ git_mutex_unlock(&db->lock);
+
+ if (error < 0)
+ goto cleanup;
+
+ git_vector_foreach(&backends, i, internal) {
+ git_odb_backend *b = internal->backend;
+ error = b->foreach(b, cb, payload);
+ if (error != 0)
+ goto cleanup;
+ }
+
+cleanup:
+ git_vector_free(&backends);
+
+ return error;
+}
+
+int git_odb_write(
+ git_oid *oid, git_odb *db, const void *data, size_t len, git_object_t type)
+{
+ size_t i;
+ int error;
+ git_odb_stream *stream;
+
+ GIT_ASSERT_ARG(oid);
+ GIT_ASSERT_ARG(db);
+
+ if ((error = git_odb__hash(oid, data, len, type, db->options.oid_type)) < 0)
+ return error;
+
+ if (git_oid_is_zero(oid))
+ return error_null_oid(GIT_EINVALID, "cannot write object");
+
+ if (git_odb__freshen(db, oid))
+ return 0;
+
+ if ((error = git_mutex_lock(&db->lock)) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ return error;
+ }
+ for (i = 0, error = GIT_ERROR; i < db->backends.length && error < 0; ++i) {
+ backend_internal *internal = git_vector_get(&db->backends, i);
+ git_odb_backend *b = internal->backend;
+
+ /* we don't write in alternates! */
+ if (internal->is_alternate)
+ continue;
+
+ if (b->write != NULL)
+ error = b->write(b, oid, data, len, type);
+ }
+ git_mutex_unlock(&db->lock);
+
+ if (!error || error == GIT_PASSTHROUGH)
+ return 0;
+
+ /* if no backends were able to write the object directly, we try a
+ * streaming write to the backends; just write the whole object into the
+ * stream in one push
+ */
+ if ((error = git_odb_open_wstream(&stream, db, len, type)) != 0)
+ return error;
+
+ if ((error = stream->write(stream, data, len)) == 0)
+ error = stream->finalize_write(stream, oid);
+
+ git_odb_stream_free(stream);
+ return error;
+}
+
+static int hash_header(git_hash_ctx *ctx, git_object_size_t size, git_object_t type)
+{
+ char header[64];
+ size_t hdrlen;
+ int error;
+
+ if ((error = git_odb__format_object_header(&hdrlen,
+ header, sizeof(header), size, type)) < 0)
+ return error;
+
+ return git_hash_update(ctx, header, hdrlen);
+}
+
+int git_odb_open_wstream(
+ git_odb_stream **stream, git_odb *db, git_object_size_t size, git_object_t type)
+{
+ size_t i, writes = 0;
+ int error = GIT_ERROR;
+ git_hash_ctx *ctx = NULL;
+
+ GIT_ASSERT_ARG(stream);
+ GIT_ASSERT_ARG(db);
+
+ if ((error = git_mutex_lock(&db->lock)) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ return error;
+ }
+ error = GIT_ERROR;
+ for (i = 0; i < db->backends.length && error < 0; ++i) {
+ backend_internal *internal = git_vector_get(&db->backends, i);
+ git_odb_backend *b = internal->backend;
+
+ /* we don't write in alternates! */
+ if (internal->is_alternate)
+ continue;
+
+ if (b->writestream != NULL) {
+ ++writes;
+ error = b->writestream(stream, b, size, type);
+ } else if (b->write != NULL) {
+ ++writes;
+ error = init_fake_wstream(stream, b, size, type);
+ }
+ }
+ git_mutex_unlock(&db->lock);
+
+ if (error < 0) {
+ if (error == GIT_PASSTHROUGH)
+ error = 0;
+ else if (!writes)
+ error = git_odb__error_unsupported_in_backend("write object");
+
+ goto done;
+ }
+
+ ctx = git__malloc(sizeof(git_hash_ctx));
+ GIT_ERROR_CHECK_ALLOC(ctx);
+
+ if ((error = git_hash_ctx_init(ctx, git_oid_algorithm(db->options.oid_type))) < 0 ||
+ (error = hash_header(ctx, size, type)) < 0)
+ goto done;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ (*stream)->oid_type = db->options.oid_type;
+#endif
+ (*stream)->hash_ctx = ctx;
+ (*stream)->declared_size = size;
+ (*stream)->received_bytes = 0;
+
+done:
+ if (error)
+ git__free(ctx);
+ return error;
+}
+
+static int git_odb_stream__invalid_length(
+ const git_odb_stream *stream,
+ const char *action)
+{
+ git_error_set(GIT_ERROR_ODB,
+ "cannot %s - "
+ "Invalid length. %"PRId64" was expected. The "
+ "total size of the received chunks amounts to %"PRId64".",
+ action, stream->declared_size, stream->received_bytes);
+
+ return -1;
+}
+
+int git_odb_stream_write(git_odb_stream *stream, const char *buffer, size_t len)
+{
+ git_hash_update(stream->hash_ctx, buffer, len);
+
+ stream->received_bytes += len;
+
+ if (stream->received_bytes > stream->declared_size)
+ return git_odb_stream__invalid_length(stream,
+ "stream_write()");
+
+ return stream->write(stream, buffer, len);
+}
+
+int git_odb_stream_finalize_write(git_oid *out, git_odb_stream *stream)
+{
+ if (stream->received_bytes != stream->declared_size)
+ return git_odb_stream__invalid_length(stream,
+ "stream_finalize_write()");
+
+ git_hash_final(out->id, stream->hash_ctx);
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ out->type = stream->oid_type;
+#endif
+
+ if (git_odb__freshen(stream->backend->odb, out))
+ return 0;
+
+ return stream->finalize_write(stream, out);
+}
+
+int git_odb_stream_read(git_odb_stream *stream, char *buffer, size_t len)
+{
+ return stream->read(stream, buffer, len);
+}
+
+void git_odb_stream_free(git_odb_stream *stream)
+{
+ if (stream == NULL)
+ return;
+
+ git_hash_ctx_cleanup(stream->hash_ctx);
+ git__free(stream->hash_ctx);
+ stream->free(stream);
+}
+
+int git_odb_open_rstream(
+ git_odb_stream **stream,
+ size_t *len,
+ git_object_t *type,
+ git_odb *db,
+ const git_oid *oid)
+{
+ size_t i, reads = 0;
+ int error = GIT_ERROR;
+
+ GIT_ASSERT_ARG(stream);
+ GIT_ASSERT_ARG(db);
+
+ if ((error = git_mutex_lock(&db->lock)) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ return error;
+ }
+ error = GIT_ERROR;
+ for (i = 0; i < db->backends.length && error < 0; ++i) {
+ backend_internal *internal = git_vector_get(&db->backends, i);
+ git_odb_backend *b = internal->backend;
+
+ if (b->readstream != NULL) {
+ ++reads;
+ error = b->readstream(stream, len, type, b, oid);
+ }
+ }
+ git_mutex_unlock(&db->lock);
+
+ if (error == GIT_PASSTHROUGH)
+ error = 0;
+ if (error < 0 && !reads)
+ error = git_odb__error_unsupported_in_backend("read object streamed");
+
+ return error;
+}
+
+int git_odb_write_pack(struct git_odb_writepack **out, git_odb *db, git_indexer_progress_cb progress_cb, void *progress_payload)
+{
+ size_t i, writes = 0;
+ int error = GIT_ERROR;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(db);
+
+ if ((error = git_mutex_lock(&db->lock)) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ return error;
+ }
+ error = GIT_ERROR;
+ for (i = 0; i < db->backends.length && error < 0; ++i) {
+ backend_internal *internal = git_vector_get(&db->backends, i);
+ git_odb_backend *b = internal->backend;
+
+ /* we don't write in alternates! */
+ if (internal->is_alternate)
+ continue;
+
+ if (b->writepack != NULL) {
+ ++writes;
+ error = b->writepack(out, b, db, progress_cb, progress_payload);
+ }
+ }
+ git_mutex_unlock(&db->lock);
+
+ if (error == GIT_PASSTHROUGH)
+ error = 0;
+ if (error < 0 && !writes)
+ error = git_odb__error_unsupported_in_backend("write pack");
+
+ return error;
+}
+
+int git_odb_write_multi_pack_index(git_odb *db)
+{
+ size_t i, writes = 0;
+ int error = GIT_ERROR;
+
+ GIT_ASSERT_ARG(db);
+
+ for (i = 0; i < db->backends.length && error < 0; ++i) {
+ backend_internal *internal = git_vector_get(&db->backends, i);
+ git_odb_backend *b = internal->backend;
+
+ /* we don't write in alternates! */
+ if (internal->is_alternate)
+ continue;
+
+ if (b->writemidx != NULL) {
+ ++writes;
+ error = b->writemidx(b);
+ }
+ }
+
+ if (error == GIT_PASSTHROUGH)
+ error = 0;
+ if (error < 0 && !writes)
+ error = git_odb__error_unsupported_in_backend("write multi-pack-index");
+
+ return error;
+}
+
+void *git_odb_backend_data_alloc(git_odb_backend *backend, size_t len)
+{
+ GIT_UNUSED(backend);
+ return git__malloc(len);
+}
+
+#ifndef GIT_DEPRECATE_HARD
+void *git_odb_backend_malloc(git_odb_backend *backend, size_t len)
+{
+ return git_odb_backend_data_alloc(backend, len);
+}
+#endif
+
+void git_odb_backend_data_free(git_odb_backend *backend, void *data)
+{
+ GIT_UNUSED(backend);
+ git__free(data);
+}
+
+int git_odb_refresh(struct git_odb *db)
+{
+ size_t i;
+ int error;
+
+ GIT_ASSERT_ARG(db);
+
+ if ((error = git_mutex_lock(&db->lock)) < 0) {
+ git_error_set(GIT_ERROR_ODB, "failed to acquire the odb lock");
+ return error;
+ }
+ for (i = 0; i < db->backends.length; ++i) {
+ backend_internal *internal = git_vector_get(&db->backends, i);
+ git_odb_backend *b = internal->backend;
+
+ if (b->refresh != NULL) {
+ int error = b->refresh(b);
+ if (error < 0) {
+ git_mutex_unlock(&db->lock);
+ return error;
+ }
+ }
+ }
+ if (db->cgraph)
+ git_commit_graph_refresh(db->cgraph);
+ git_mutex_unlock(&db->lock);
+
+ return 0;
+}
+
+int git_odb__error_mismatch(const git_oid *expected, const git_oid *actual)
+{
+ char expected_oid[GIT_OID_MAX_HEXSIZE + 1],
+ actual_oid[GIT_OID_MAX_HEXSIZE + 1];
+
+ git_oid_tostr(expected_oid, git_oid_hexsize(git_oid_type(expected)) + 1, expected);
+ git_oid_tostr(actual_oid, git_oid_hexsize(git_oid_type(actual)) + 1, actual);
+
+ git_error_set(GIT_ERROR_ODB, "object hash mismatch - expected %s but got %s",
+ expected_oid, actual_oid);
+
+ return GIT_EMISMATCH;
+}
+
+int git_odb__error_notfound(
+ const char *message, const git_oid *oid, size_t oid_len)
+{
+ if (oid != NULL) {
+ char oid_str[GIT_OID_MAX_HEXSIZE + 1];
+ git_oid_tostr(oid_str, oid_len+1, oid);
+ git_error_set(GIT_ERROR_ODB, "object not found - %s (%.*s)",
+ message, (int) oid_len, oid_str);
+ } else
+ git_error_set(GIT_ERROR_ODB, "object not found - %s", message);
+
+ return GIT_ENOTFOUND;
+}
+
+static int error_null_oid(int error, const char *message)
+{
+ git_error_set(GIT_ERROR_ODB, "odb: %s: null OID cannot exist", message);
+ return error;
+}
+
+int git_odb__error_ambiguous(const char *message)
+{
+ git_error_set(GIT_ERROR_ODB, "ambiguous OID prefix - %s", message);
+ return GIT_EAMBIGUOUS;
+}
+
+int git_odb_init_backend(git_odb_backend *backend, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ backend, version, git_odb_backend, GIT_ODB_BACKEND_INIT);
+ return 0;
+}
diff --git a/src/libgit2/odb.h b/src/libgit2/odb.h
new file mode 100644
index 0000000..7a712e2
--- /dev/null
+++ b/src/libgit2/odb.h
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_odb_h__
+#define INCLUDE_odb_h__
+
+#include "common.h"
+
+#include "git2/odb.h"
+#include "git2/odb_backend.h"
+#include "git2/oid.h"
+#include "git2/types.h"
+#include "git2/sys/commit_graph.h"
+
+#include "cache.h"
+#include "commit_graph.h"
+#include "filter.h"
+#include "posix.h"
+#include "vector.h"
+
+#define GIT_OBJECTS_DIR "objects/"
+#define GIT_OBJECT_DIR_MODE 0777
+#define GIT_OBJECT_FILE_MODE 0444
+
+#define GIT_ODB_DEFAULT_LOOSE_PRIORITY 1
+#define GIT_ODB_DEFAULT_PACKED_PRIORITY 2
+
+extern bool git_odb__strict_hash_verification;
+
+/* DO NOT EXPORT */
+typedef struct {
+ void *data; /**< Raw, decompressed object data. */
+ size_t len; /**< Total number of bytes in data. */
+ git_object_t type; /**< Type of this object. */
+} git_rawobj;
+
+/* EXPORT */
+struct git_odb_object {
+ git_cached_obj cached;
+ void *buffer;
+};
+
+/* EXPORT */
+struct git_odb {
+ git_refcount rc;
+ git_mutex lock; /* protects backends */
+ git_odb_options options;
+ git_vector backends;
+ git_cache own_cache;
+ git_commit_graph *cgraph;
+ unsigned int do_fsync :1;
+};
+
+typedef enum {
+ GIT_ODB_CAP_FROM_OWNER = -1
+} git_odb_cap_t;
+
+/*
+ * Set the capabilities for the object database.
+ */
+int git_odb__set_caps(git_odb *odb, int caps);
+
+/*
+ * Add the default loose and packed backends for a database.
+ */
+int git_odb__add_default_backends(
+ git_odb *db, const char *objects_dir,
+ bool as_alternates, int alternate_depth);
+
+/*
+ * Hash a git_rawobj internally.
+ * The `git_rawobj` is supposed to be previously initialized
+ */
+int git_odb__hashobj(git_oid *id, git_rawobj *obj, git_oid_t oid_type);
+
+/*
+ * Format the object header such as it would appear in the on-disk object
+ */
+int git_odb__format_object_header(size_t *out_len, char *hdr, size_t hdr_size, git_object_size_t obj_len, git_object_t obj_type);
+
+/*
+ * Hash an open file descriptor.
+ * This is a performance call when the contents of a fd need to be hashed,
+ * but the fd is already open and we have the size of the contents.
+ *
+ * Saves us some `stat` calls.
+ *
+ * The fd is never closed, not even on error. It must be opened and closed
+ * by the caller
+ */
+int git_odb__hashfd(
+ git_oid *out,
+ git_file fd,
+ size_t size,
+ git_object_t object_type,
+ git_oid_t oid_type);
+
+/*
+ * Hash an open file descriptor applying an array of filters
+ * Acts just like git_odb__hashfd with the addition of filters...
+ */
+int git_odb__hashfd_filtered(
+ git_oid *out,
+ git_file fd,
+ size_t len,
+ git_object_t object_type,
+ git_oid_t oid_type,
+ git_filter_list *fl);
+
+/*
+ * Hash a `path`, assuming it could be a POSIX symlink: if the path is a
+ * symlink, then the raw contents of the symlink will be hashed. Otherwise,
+ * this will fallback to `git_odb__hashfd`.
+ *
+ * The hash type for this call is always `GIT_OBJECT_BLOB` because
+ * symlinks may only point to blobs.
+ */
+int git_odb__hashlink(git_oid *out, const char *path, git_oid_t oid_type);
+
+/**
+ * Generate a GIT_EMISMATCH error for the ODB.
+ */
+int git_odb__error_mismatch(
+ const git_oid *expected, const git_oid *actual);
+
+/*
+ * Generate a GIT_ENOTFOUND error for the ODB.
+ */
+int git_odb__error_notfound(
+ const char *message, const git_oid *oid, size_t oid_len);
+
+/*
+ * Generate a GIT_EAMBIGUOUS error for the ODB.
+ */
+int git_odb__error_ambiguous(const char *message);
+
+/*
+ * Attempt to read object header or just return whole object if it could
+ * not be read.
+ */
+int git_odb__read_header_or_object(
+ git_odb_object **out, size_t *len_p, git_object_t *type_p,
+ git_odb *db, const git_oid *id);
+
+/*
+ * Attempt to get the ODB's commit-graph file. This object is still owned by
+ * the ODB. If the repository does not contain a commit-graph, it will return
+ * GIT_ENOTFOUND.
+ */
+int git_odb__get_commit_graph_file(git_commit_graph_file **out, git_odb *odb);
+
+/* freshen an entry in the object database */
+int git_odb__freshen(git_odb *db, const git_oid *id);
+
+/* fully free the object; internal method, DO NOT EXPORT */
+void git_odb_object__free(void *object);
+
+/* SHA256 support */
+
+int git_odb__new(git_odb **out, const git_odb_options *opts);
+
+int git_odb__open(
+ git_odb **out,
+ const char *objects_dir,
+ const git_odb_options *opts);
+
+int git_odb__hash(
+ git_oid *out,
+ const void *data,
+ size_t len,
+ git_object_t object_type,
+ git_oid_t oid_type);
+
+int git_odb__hashfile(
+ git_oid *out,
+ const char *path,
+ git_object_t object_type,
+ git_oid_t oid_type);
+
+GIT_EXTERN(int) git_odb__backend_loose(
+ git_odb_backend **out,
+ const char *objects_dir,
+ git_odb_backend_loose_options *opts);
+
+#endif
diff --git a/src/libgit2/odb_loose.c b/src/libgit2/odb_loose.c
new file mode 100644
index 0000000..51195d3
--- /dev/null
+++ b/src/libgit2/odb_loose.c
@@ -0,0 +1,1240 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+
+#include <zlib.h>
+#include "git2/object.h"
+#include "git2/sys/odb_backend.h"
+#include "futils.h"
+#include "hash.h"
+#include "odb.h"
+#include "delta.h"
+#include "filebuf.h"
+#include "object.h"
+#include "zstream.h"
+
+#include "git2/odb_backend.h"
+#include "git2/types.h"
+
+/* maximum possible header length */
+#define MAX_HEADER_LEN 64
+
+typedef struct { /* object header data */
+ git_object_t type; /* object type */
+ size_t size; /* object size */
+} obj_hdr;
+
+typedef struct {
+ git_odb_stream stream;
+ git_filebuf fbuf;
+} loose_writestream;
+
+typedef struct {
+ git_odb_stream stream;
+ git_map map;
+ char start[MAX_HEADER_LEN];
+ size_t start_len;
+ size_t start_read;
+ git_zstream zstream;
+} loose_readstream;
+
+typedef struct loose_backend {
+ git_odb_backend parent;
+
+ git_odb_backend_loose_options options;
+ size_t oid_hexsize;
+
+ size_t objects_dirlen;
+ char objects_dir[GIT_FLEX_ARRAY];
+} loose_backend;
+
+/* State structure for exploring directories,
+ * in order to locate objects matching a short oid.
+ */
+typedef struct {
+ loose_backend *backend;
+
+ size_t dir_len;
+
+ /* Hex formatted oid to match (and its length) */
+ unsigned char short_oid[GIT_OID_MAX_HEXSIZE];
+ size_t short_oid_len;
+
+ /* Number of matching objects found so far */
+ int found;
+
+ /* Hex formatted oid of the object found */
+ unsigned char res_oid[GIT_OID_MAX_HEXSIZE];
+} loose_locate_object_state;
+
+
+/***********************************************************
+ *
+ * MISCELLANEOUS HELPER FUNCTIONS
+ *
+ ***********************************************************/
+
+static int object_file_name(
+ git_str *name, const loose_backend *be, const git_oid *id)
+{
+ /* append loose object filename: aa/aaa... (41 bytes plus NUL) */
+ size_t path_size = be->oid_hexsize + 1;
+
+ git_str_set(name, be->objects_dir, be->objects_dirlen);
+ git_fs_path_to_dir(name);
+
+ if (git_str_grow_by(name, path_size + 1) < 0)
+ return -1;
+
+ git_oid_pathfmt(name->ptr + name->size, id);
+ name->size += path_size;
+ name->ptr[name->size] = '\0';
+
+ return 0;
+}
+
+static int object_mkdir(const git_str *name, const loose_backend *be)
+{
+ return git_futils_mkdir_relative(
+ name->ptr + be->objects_dirlen,
+ be->objects_dir,
+ be->options.dir_mode,
+ GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST | GIT_MKDIR_VERIFY_DIR, NULL);
+}
+
+static int parse_header_packlike(
+ obj_hdr *out, size_t *out_len, const unsigned char *data, size_t len)
+{
+ unsigned long c;
+ size_t shift, size, used = 0;
+
+ if (len == 0)
+ goto on_error;
+
+ c = data[used++];
+ out->type = (c >> 4) & 7;
+
+ size = c & 15;
+ shift = 4;
+ while (c & 0x80) {
+ if (len <= used)
+ goto on_error;
+
+ if (sizeof(size_t) * 8 <= shift)
+ goto on_error;
+
+ c = data[used++];
+ size += (c & 0x7f) << shift;
+ shift += 7;
+ }
+
+ out->size = size;
+
+ if (out_len)
+ *out_len = used;
+
+ return 0;
+
+on_error:
+ git_error_set(GIT_ERROR_OBJECT, "failed to parse loose object: invalid header");
+ return -1;
+}
+
+static int parse_header(
+ obj_hdr *out,
+ size_t *out_len,
+ const unsigned char *_data,
+ size_t data_len)
+{
+ const char *data = (char *)_data;
+ size_t i, typename_len, size_idx, size_len;
+ int64_t size;
+
+ *out_len = 0;
+
+ /* find the object type name */
+ for (i = 0, typename_len = 0; i < data_len; i++, typename_len++) {
+ if (data[i] == ' ')
+ break;
+ }
+
+ if (typename_len == data_len)
+ goto on_error;
+
+ out->type = git_object_stringn2type(data, typename_len);
+
+ size_idx = typename_len + 1;
+ for (i = size_idx, size_len = 0; i < data_len; i++, size_len++) {
+ if (data[i] == '\0')
+ break;
+ }
+
+ if (i == data_len)
+ goto on_error;
+
+ if (git__strntol64(&size, &data[size_idx], size_len, NULL, 10) < 0 ||
+ size < 0)
+ goto on_error;
+
+ if ((uint64_t)size > SIZE_MAX) {
+ git_error_set(GIT_ERROR_OBJECT, "object is larger than available memory");
+ return -1;
+ }
+
+ out->size = (size_t)size;
+
+ if (GIT_ADD_SIZET_OVERFLOW(out_len, i, 1))
+ goto on_error;
+
+ return 0;
+
+on_error:
+ git_error_set(GIT_ERROR_OBJECT, "failed to parse loose object: invalid header");
+ return -1;
+}
+
+static int is_zlib_compressed_data(unsigned char *data, size_t data_len)
+{
+ unsigned int w;
+
+ if (data_len < 2)
+ return 0;
+
+ w = ((unsigned int)(data[0]) << 8) + data[1];
+ return (data[0] & 0x8F) == 0x08 && !(w % 31);
+}
+
+/***********************************************************
+ *
+ * ODB OBJECT READING & WRITING
+ *
+ * Backend for the public API; read headers and full objects
+ * from the ODB. Write raw data to the ODB.
+ *
+ ***********************************************************/
+
+
+/*
+ * At one point, there was a loose object format that was intended to
+ * mimic the format used in pack-files. This was to allow easy copying
+ * of loose object data into packs. This format is no longer used, but
+ * we must still read it.
+ */
+static int read_loose_packlike(git_rawobj *out, git_str *obj)
+{
+ git_str body = GIT_STR_INIT;
+ const unsigned char *obj_data;
+ obj_hdr hdr;
+ size_t obj_len, head_len, alloc_size;
+ int error;
+
+ obj_data = (unsigned char *)obj->ptr;
+ obj_len = obj->size;
+
+ /*
+ * read the object header, which is an (uncompressed)
+ * binary encoding of the object type and size.
+ */
+ if ((error = parse_header_packlike(&hdr, &head_len, obj_data, obj_len)) < 0)
+ goto done;
+
+ if (!git_object_typeisloose(hdr.type) || head_len > obj_len) {
+ git_error_set(GIT_ERROR_ODB, "failed to inflate loose object");
+ error = -1;
+ goto done;
+ }
+
+ obj_data += head_len;
+ obj_len -= head_len;
+
+ /*
+ * allocate a buffer and inflate the data into it
+ */
+ if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, hdr.size, 1) ||
+ git_str_init(&body, alloc_size) < 0) {
+ error = -1;
+ goto done;
+ }
+
+ if ((error = git_zstream_inflatebuf(&body, obj_data, obj_len)) < 0)
+ goto done;
+
+ out->len = hdr.size;
+ out->type = hdr.type;
+ out->data = git_str_detach(&body);
+
+done:
+ git_str_dispose(&body);
+ return error;
+}
+
+static int read_loose_standard(git_rawobj *out, git_str *obj)
+{
+ git_zstream zstream = GIT_ZSTREAM_INIT;
+ unsigned char head[MAX_HEADER_LEN], *body = NULL;
+ size_t decompressed, head_len, body_len, alloc_size;
+ obj_hdr hdr;
+ int error;
+
+ if ((error = git_zstream_init(&zstream, GIT_ZSTREAM_INFLATE)) < 0 ||
+ (error = git_zstream_set_input(&zstream, git_str_cstr(obj), git_str_len(obj))) < 0)
+ goto done;
+
+ decompressed = sizeof(head);
+
+ /*
+ * inflate the initial part of the compressed buffer in order to
+ * parse the header; read the largest header possible, then push the
+ * remainder into the body buffer.
+ */
+ if ((error = git_zstream_get_output(head, &decompressed, &zstream)) < 0 ||
+ (error = parse_header(&hdr, &head_len, head, decompressed)) < 0)
+ goto done;
+
+ if (!git_object_typeisloose(hdr.type)) {
+ git_error_set(GIT_ERROR_ODB, "failed to inflate disk object");
+ error = -1;
+ goto done;
+ }
+
+ /*
+ * allocate a buffer and inflate the object data into it
+ * (including the initial sequence in the head buffer).
+ */
+ if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, hdr.size, 1) ||
+ (body = git__calloc(1, alloc_size)) == NULL) {
+ error = -1;
+ goto done;
+ }
+
+ GIT_ASSERT(decompressed >= head_len);
+ body_len = decompressed - head_len;
+
+ if (body_len)
+ memcpy(body, head + head_len, body_len);
+
+ decompressed = hdr.size - body_len;
+ if ((error = git_zstream_get_output(body + body_len, &decompressed, &zstream)) < 0)
+ goto done;
+
+ if (!git_zstream_done(&zstream)) {
+ git_error_set(GIT_ERROR_ZLIB, "failed to finish zlib inflation: stream aborted prematurely");
+ error = -1;
+ goto done;
+ }
+
+ body[hdr.size] = '\0';
+
+ out->data = body;
+ out->len = hdr.size;
+ out->type = hdr.type;
+
+done:
+ if (error < 0)
+ git__free(body);
+
+ git_zstream_free(&zstream);
+ return error;
+}
+
+static int read_loose(git_rawobj *out, git_str *loc)
+{
+ int error;
+ git_str obj = GIT_STR_INIT;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(loc);
+
+ if (git_str_oom(loc))
+ return -1;
+
+ out->data = NULL;
+ out->len = 0;
+ out->type = GIT_OBJECT_INVALID;
+
+ if ((error = git_futils_readbuffer(&obj, loc->ptr)) < 0)
+ goto done;
+
+ if (!is_zlib_compressed_data((unsigned char *)obj.ptr, obj.size))
+ error = read_loose_packlike(out, &obj);
+ else
+ error = read_loose_standard(out, &obj);
+
+done:
+ git_str_dispose(&obj);
+ return error;
+}
+
+static int read_header_loose_packlike(
+ git_rawobj *out, const unsigned char *data, size_t len)
+{
+ obj_hdr hdr;
+ size_t header_len;
+ int error;
+
+ if ((error = parse_header_packlike(&hdr, &header_len, data, len)) < 0)
+ return error;
+
+ out->len = hdr.size;
+ out->type = hdr.type;
+
+ return error;
+}
+
+static int read_header_loose_standard(
+ git_rawobj *out, const unsigned char *data, size_t len)
+{
+ git_zstream zs = GIT_ZSTREAM_INIT;
+ obj_hdr hdr = {0};
+ unsigned char inflated[MAX_HEADER_LEN] = {0};
+ size_t header_len, inflated_len = sizeof(inflated);
+ int error;
+
+ if ((error = git_zstream_init(&zs, GIT_ZSTREAM_INFLATE)) < 0 ||
+ (error = git_zstream_set_input(&zs, data, len)) < 0 ||
+ (error = git_zstream_get_output_chunk(inflated, &inflated_len, &zs)) < 0 ||
+ (error = parse_header(&hdr, &header_len, inflated, inflated_len)) < 0)
+ goto done;
+
+ out->len = hdr.size;
+ out->type = hdr.type;
+
+done:
+ git_zstream_free(&zs);
+ return error;
+}
+
+static int read_header_loose(git_rawobj *out, git_str *loc)
+{
+ unsigned char obj[1024];
+ ssize_t obj_len;
+ int fd, error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(loc);
+
+ if (git_str_oom(loc))
+ return -1;
+
+ out->data = NULL;
+
+ if ((error = fd = git_futils_open_ro(loc->ptr)) < 0)
+ goto done;
+
+ if ((obj_len = p_read(fd, obj, sizeof(obj))) < 0) {
+ error = (int)obj_len;
+ goto done;
+ }
+
+ if (!is_zlib_compressed_data(obj, (size_t)obj_len))
+ error = read_header_loose_packlike(out, obj, (size_t)obj_len);
+ else
+ error = read_header_loose_standard(out, obj, (size_t)obj_len);
+
+ if (!error && !git_object_typeisloose(out->type)) {
+ git_error_set(GIT_ERROR_ZLIB, "failed to read loose object header");
+ error = -1;
+ goto done;
+ }
+
+done:
+ if (fd >= 0)
+ p_close(fd);
+ return error;
+}
+
+static int locate_object(
+ git_str *object_location,
+ loose_backend *backend,
+ const git_oid *oid)
+{
+ int error = object_file_name(object_location, backend, oid);
+
+ if (!error && !git_fs_path_exists(object_location->ptr))
+ return GIT_ENOTFOUND;
+
+ return error;
+}
+
+/* Explore an entry of a directory and see if it matches a short oid */
+static int fn_locate_object_short_oid(void *state, git_str *pathbuf) {
+ loose_locate_object_state *sstate = (loose_locate_object_state *)state;
+ size_t hex_size = sstate->backend->oid_hexsize;
+
+ if (git_str_len(pathbuf) - sstate->dir_len != hex_size - 2) {
+ /* Entry cannot be an object. Continue to next entry */
+ return 0;
+ }
+
+ if (git_fs_path_isdir(pathbuf->ptr) == false) {
+ /* We are already in the directory matching the 2 first hex characters,
+ * compare the first ncmp characters of the oids */
+ if (!memcmp(sstate->short_oid + 2,
+ (unsigned char *)pathbuf->ptr + sstate->dir_len,
+ sstate->short_oid_len - 2)) {
+
+ if (!sstate->found) {
+ sstate->res_oid[0] = sstate->short_oid[0];
+ sstate->res_oid[1] = sstate->short_oid[1];
+ memcpy(sstate->res_oid + 2,
+ pathbuf->ptr+sstate->dir_len,
+ hex_size - 2);
+ }
+ sstate->found++;
+ }
+ }
+
+ if (sstate->found > 1)
+ return GIT_EAMBIGUOUS;
+
+ return 0;
+}
+
+/* Locate an object matching a given short oid */
+static int locate_object_short_oid(
+ git_str *object_location,
+ git_oid *res_oid,
+ loose_backend *backend,
+ const git_oid *short_oid,
+ size_t len)
+{
+ char *objects_dir = backend->objects_dir;
+ size_t dir_len = strlen(objects_dir), alloc_len;
+ loose_locate_object_state state;
+ int error;
+
+ /* prealloc memory for OBJ_DIR/xx/xx..38x..xx */
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, dir_len, backend->oid_hexsize);
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 3);
+ if (git_str_grow(object_location, alloc_len) < 0)
+ return -1;
+
+ git_str_set(object_location, objects_dir, dir_len);
+ git_fs_path_to_dir(object_location);
+
+ /* save adjusted position at end of dir so it can be restored later */
+ dir_len = git_str_len(object_location);
+
+ /* Convert raw oid to hex formatted oid */
+ git_oid_fmt((char *)state.short_oid, short_oid);
+
+ /* Explore OBJ_DIR/xx/ where xx is the beginning of hex formatted short oid */
+ if (git_str_put(object_location, (char *)state.short_oid, 3) < 0)
+ return -1;
+ object_location->ptr[object_location->size - 1] = '/';
+
+ /* Check that directory exists */
+ if (git_fs_path_isdir(object_location->ptr) == false)
+ return git_odb__error_notfound("no matching loose object for prefix",
+ short_oid, len);
+
+ state.backend = backend;
+ state.dir_len = git_str_len(object_location);
+ state.short_oid_len = len;
+ state.found = 0;
+
+ /* Explore directory to find a unique object matching short_oid */
+ error = git_fs_path_direach(
+ object_location, 0, fn_locate_object_short_oid, &state);
+ if (error < 0 && error != GIT_EAMBIGUOUS)
+ return error;
+
+ if (!state.found)
+ return git_odb__error_notfound("no matching loose object for prefix",
+ short_oid, len);
+
+ if (state.found > 1)
+ return git_odb__error_ambiguous("multiple matches in loose objects");
+
+ /* Convert obtained hex formatted oid to raw */
+ error = git_oid__fromstr(res_oid, (char *)state.res_oid, backend->options.oid_type);
+ if (error)
+ return error;
+
+ /* Update the location according to the oid obtained */
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, dir_len, backend->oid_hexsize);
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2);
+
+ git_str_truncate(object_location, dir_len);
+ if (git_str_grow(object_location, alloc_len) < 0)
+ return -1;
+
+ git_oid_pathfmt(object_location->ptr + dir_len, res_oid);
+
+ object_location->size += backend->oid_hexsize + 1;
+ object_location->ptr[object_location->size] = '\0';
+
+ return 0;
+}
+
+/***********************************************************
+ *
+ * LOOSE BACKEND PUBLIC API
+ *
+ * Implement the git_odb_backend API calls
+ *
+ ***********************************************************/
+
+static int loose_backend__read_header(size_t *len_p, git_object_t *type_p, git_odb_backend *backend, const git_oid *oid)
+{
+ git_str object_path = GIT_STR_INIT;
+ git_rawobj raw;
+ int error;
+
+ GIT_ASSERT_ARG(backend);
+ GIT_ASSERT_ARG(oid);
+
+ raw.len = 0;
+ raw.type = GIT_OBJECT_INVALID;
+
+ if (locate_object(&object_path, (loose_backend *)backend, oid) < 0) {
+ error = git_odb__error_notfound("no matching loose object",
+ oid, ((struct loose_backend *)backend)->oid_hexsize);
+ } else if ((error = read_header_loose(&raw, &object_path)) == 0) {
+ *len_p = raw.len;
+ *type_p = raw.type;
+ }
+
+ git_str_dispose(&object_path);
+
+ return error;
+}
+
+static int loose_backend__read(void **buffer_p, size_t *len_p, git_object_t *type_p, git_odb_backend *backend, const git_oid *oid)
+{
+ git_str object_path = GIT_STR_INIT;
+ git_rawobj raw;
+ int error = 0;
+
+ GIT_ASSERT_ARG(backend);
+ GIT_ASSERT_ARG(oid);
+
+ if (locate_object(&object_path, (loose_backend *)backend, oid) < 0) {
+ error = git_odb__error_notfound("no matching loose object",
+ oid, ((struct loose_backend *)backend)->oid_hexsize);
+ } else if ((error = read_loose(&raw, &object_path)) == 0) {
+ *buffer_p = raw.data;
+ *len_p = raw.len;
+ *type_p = raw.type;
+ }
+
+ git_str_dispose(&object_path);
+
+ return error;
+}
+
+static int loose_backend__read_prefix(
+ git_oid *out_oid,
+ void **buffer_p,
+ size_t *len_p,
+ git_object_t *type_p,
+ git_odb_backend *_backend,
+ const git_oid *short_oid,
+ size_t len)
+{
+ struct loose_backend *backend = (struct loose_backend *)_backend;
+ int error = 0;
+
+ GIT_ASSERT_ARG(len >= GIT_OID_MINPREFIXLEN &&
+ len <= backend->oid_hexsize);
+
+ if (len == backend->oid_hexsize) {
+ /* We can fall back to regular read method */
+ error = loose_backend__read(buffer_p, len_p, type_p, _backend, short_oid);
+ if (!error)
+ git_oid_cpy(out_oid, short_oid);
+ } else {
+ git_str object_path = GIT_STR_INIT;
+ git_rawobj raw;
+
+ GIT_ASSERT_ARG(backend && short_oid);
+
+ if ((error = locate_object_short_oid(&object_path, out_oid,
+ (loose_backend *)backend, short_oid, len)) == 0 &&
+ (error = read_loose(&raw, &object_path)) == 0)
+ {
+ *buffer_p = raw.data;
+ *len_p = raw.len;
+ *type_p = raw.type;
+ }
+
+ git_str_dispose(&object_path);
+ }
+
+ return error;
+}
+
+static int loose_backend__exists(git_odb_backend *backend, const git_oid *oid)
+{
+ git_str object_path = GIT_STR_INIT;
+ int error;
+
+ GIT_ASSERT_ARG(backend);
+ GIT_ASSERT_ARG(oid);
+
+ error = locate_object(&object_path, (loose_backend *)backend, oid);
+
+ git_str_dispose(&object_path);
+
+ return !error;
+}
+
+static int loose_backend__exists_prefix(
+ git_oid *out, git_odb_backend *backend, const git_oid *short_id, size_t len)
+{
+ git_str object_path = GIT_STR_INIT;
+ int error;
+
+ GIT_ASSERT_ARG(backend);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(short_id);
+ GIT_ASSERT_ARG(len >= GIT_OID_MINPREFIXLEN);
+
+ error = locate_object_short_oid(
+ &object_path, out, (loose_backend *)backend, short_id, len);
+
+ git_str_dispose(&object_path);
+
+ return error;
+}
+
+struct foreach_state {
+ struct loose_backend *backend;
+ size_t dir_len;
+ git_odb_foreach_cb cb;
+ void *data;
+};
+
+GIT_INLINE(int) filename_to_oid(struct loose_backend *backend, git_oid *oid, const char *ptr)
+{
+ int v;
+ size_t i = 0;
+
+ if (strlen(ptr) != backend->oid_hexsize + 1)
+ return -1;
+
+ if (ptr[2] != '/') {
+ return -1;
+ }
+
+ v = (git__fromhex(ptr[i]) << 4) | git__fromhex(ptr[i+1]);
+ if (v < 0)
+ return -1;
+
+ oid->id[0] = (unsigned char) v;
+
+ ptr += 3;
+ for (i = 0; i < backend->oid_hexsize - 2; i += 2) {
+ v = (git__fromhex(ptr[i]) << 4) | git__fromhex(ptr[i + 1]);
+ if (v < 0)
+ return -1;
+
+ oid->id[1 + i/2] = (unsigned char) v;
+ }
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ oid->type = backend->options.oid_type;
+#endif
+
+ return 0;
+}
+
+static int foreach_object_dir_cb(void *_state, git_str *path)
+{
+ git_oid oid;
+ struct foreach_state *state = (struct foreach_state *) _state;
+
+ if (filename_to_oid(state->backend, &oid, path->ptr + state->dir_len) < 0)
+ return 0;
+
+ return git_error_set_after_callback_function(
+ state->cb(&oid, state->data), "git_odb_foreach");
+}
+
+static int foreach_cb(void *_state, git_str *path)
+{
+ struct foreach_state *state = (struct foreach_state *) _state;
+
+ /* non-dir is some stray file, ignore it */
+ if (!git_fs_path_isdir(git_str_cstr(path)))
+ return 0;
+
+ return git_fs_path_direach(path, 0, foreach_object_dir_cb, state);
+}
+
+static int loose_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb cb, void *data)
+{
+ char *objects_dir;
+ int error;
+ git_str buf = GIT_STR_INIT;
+ struct foreach_state state;
+ loose_backend *backend = (loose_backend *) _backend;
+
+ GIT_ASSERT_ARG(backend);
+ GIT_ASSERT_ARG(cb);
+
+ objects_dir = backend->objects_dir;
+
+ git_str_sets(&buf, objects_dir);
+ git_fs_path_to_dir(&buf);
+ if (git_str_oom(&buf))
+ return -1;
+
+ memset(&state, 0, sizeof(state));
+ state.backend = backend;
+ state.cb = cb;
+ state.data = data;
+ state.dir_len = git_str_len(&buf);
+
+ error = git_fs_path_direach(&buf, 0, foreach_cb, &state);
+
+ git_str_dispose(&buf);
+
+ return error;
+}
+
+static int loose_backend__writestream_finalize(git_odb_stream *_stream, const git_oid *oid)
+{
+ loose_writestream *stream = (loose_writestream *)_stream;
+ loose_backend *backend = (loose_backend *)_stream->backend;
+ git_str final_path = GIT_STR_INIT;
+ int error = 0;
+
+ if (object_file_name(&final_path, backend, oid) < 0 ||
+ object_mkdir(&final_path, backend) < 0)
+ error = -1;
+ else
+ error = git_filebuf_commit_at(
+ &stream->fbuf, final_path.ptr);
+
+ git_str_dispose(&final_path);
+
+ return error;
+}
+
+static int loose_backend__writestream_write(git_odb_stream *_stream, const char *data, size_t len)
+{
+ loose_writestream *stream = (loose_writestream *)_stream;
+ return git_filebuf_write(&stream->fbuf, data, len);
+}
+
+static void loose_backend__writestream_free(git_odb_stream *_stream)
+{
+ loose_writestream *stream = (loose_writestream *)_stream;
+
+ git_filebuf_cleanup(&stream->fbuf);
+ git__free(stream);
+}
+
+static int filebuf_flags(loose_backend *backend)
+{
+ int flags = GIT_FILEBUF_TEMPORARY |
+ (backend->options.compression_level << GIT_FILEBUF_DEFLATE_SHIFT);
+
+ if ((backend->options.flags & GIT_ODB_BACKEND_LOOSE_FSYNC) ||
+ git_repository__fsync_gitdir)
+ flags |= GIT_FILEBUF_FSYNC;
+
+ return flags;
+}
+
+static int loose_backend__writestream(git_odb_stream **stream_out, git_odb_backend *_backend, git_object_size_t length, git_object_t type)
+{
+ loose_backend *backend;
+ loose_writestream *stream = NULL;
+ char hdr[MAX_HEADER_LEN];
+ git_str tmp_path = GIT_STR_INIT;
+ size_t hdrlen;
+ int error;
+
+ GIT_ASSERT_ARG(_backend);
+
+ backend = (loose_backend *)_backend;
+ *stream_out = NULL;
+
+ if ((error = git_odb__format_object_header(&hdrlen,
+ hdr, sizeof(hdr), length, type)) < 0)
+ return error;
+
+ stream = git__calloc(1, sizeof(loose_writestream));
+ GIT_ERROR_CHECK_ALLOC(stream);
+
+ stream->stream.backend = _backend;
+ stream->stream.read = NULL; /* read only */
+ stream->stream.write = &loose_backend__writestream_write;
+ stream->stream.finalize_write = &loose_backend__writestream_finalize;
+ stream->stream.free = &loose_backend__writestream_free;
+ stream->stream.mode = GIT_STREAM_WRONLY;
+
+ if (git_str_joinpath(&tmp_path, backend->objects_dir, "tmp_object") < 0 ||
+ git_filebuf_open(&stream->fbuf, tmp_path.ptr, filebuf_flags(backend),
+ backend->options.file_mode) < 0 ||
+ stream->stream.write((git_odb_stream *)stream, hdr, hdrlen) < 0)
+ {
+ git_filebuf_cleanup(&stream->fbuf);
+ git__free(stream);
+ stream = NULL;
+ }
+ git_str_dispose(&tmp_path);
+ *stream_out = (git_odb_stream *)stream;
+
+ return !stream ? -1 : 0;
+}
+
+static int loose_backend__readstream_read(
+ git_odb_stream *_stream,
+ char *buffer,
+ size_t buffer_len)
+{
+ loose_readstream *stream = (loose_readstream *)_stream;
+ size_t start_remain = stream->start_len - stream->start_read;
+ int total = 0, error;
+
+ buffer_len = min(buffer_len, INT_MAX);
+
+ /*
+ * if we read more than just the header in the initial read, play
+ * that back for the caller.
+ */
+ if (start_remain && buffer_len) {
+ size_t chunk = min(start_remain, buffer_len);
+ memcpy(buffer, stream->start + stream->start_read, chunk);
+
+ buffer += chunk;
+ stream->start_read += chunk;
+
+ total += (int)chunk;
+ buffer_len -= chunk;
+ }
+
+ if (buffer_len) {
+ size_t chunk = buffer_len;
+
+ if ((error = git_zstream_get_output(buffer, &chunk, &stream->zstream)) < 0)
+ return error;
+
+ total += (int)chunk;
+ }
+
+ return (int)total;
+}
+
+static void loose_backend__readstream_free(git_odb_stream *_stream)
+{
+ loose_readstream *stream = (loose_readstream *)_stream;
+
+ git_futils_mmap_free(&stream->map);
+ git_zstream_free(&stream->zstream);
+ git__free(stream);
+}
+
+static int loose_backend__readstream_packlike(
+ obj_hdr *hdr,
+ loose_readstream *stream)
+{
+ const unsigned char *data;
+ size_t data_len, head_len;
+ int error;
+
+ data = stream->map.data;
+ data_len = stream->map.len;
+
+ /*
+ * read the object header, which is an (uncompressed)
+ * binary encoding of the object type and size.
+ */
+ if ((error = parse_header_packlike(hdr, &head_len, data, data_len)) < 0)
+ return error;
+
+ if (!git_object_typeisloose(hdr->type)) {
+ git_error_set(GIT_ERROR_ODB, "failed to inflate loose object");
+ return -1;
+ }
+
+ return git_zstream_set_input(&stream->zstream,
+ data + head_len, data_len - head_len);
+}
+
+static int loose_backend__readstream_standard(
+ obj_hdr *hdr,
+ loose_readstream *stream)
+{
+ unsigned char head[MAX_HEADER_LEN];
+ size_t init, head_len;
+ int error;
+
+ if ((error = git_zstream_set_input(&stream->zstream,
+ stream->map.data, stream->map.len)) < 0)
+ return error;
+
+ init = sizeof(head);
+
+ /*
+ * inflate the initial part of the compressed buffer in order to
+ * parse the header; read the largest header possible, then store
+ * it in the `start` field of the stream object.
+ */
+ if ((error = git_zstream_get_output(head, &init, &stream->zstream)) < 0 ||
+ (error = parse_header(hdr, &head_len, head, init)) < 0)
+ return error;
+
+ if (!git_object_typeisloose(hdr->type)) {
+ git_error_set(GIT_ERROR_ODB, "failed to inflate disk object");
+ return -1;
+ }
+
+ if (init > head_len) {
+ stream->start_len = init - head_len;
+ memcpy(stream->start, head + head_len, init - head_len);
+ }
+
+ return 0;
+}
+
+static int loose_backend__readstream(
+ git_odb_stream **stream_out,
+ size_t *len_out,
+ git_object_t *type_out,
+ git_odb_backend *_backend,
+ const git_oid *oid)
+{
+ loose_backend *backend;
+ loose_readstream *stream = NULL;
+ git_hash_ctx *hash_ctx = NULL;
+ git_str object_path = GIT_STR_INIT;
+ git_hash_algorithm_t algorithm;
+ obj_hdr hdr;
+ int error = 0;
+
+ GIT_ASSERT_ARG(stream_out);
+ GIT_ASSERT_ARG(len_out);
+ GIT_ASSERT_ARG(type_out);
+ GIT_ASSERT_ARG(_backend);
+ GIT_ASSERT_ARG(oid);
+
+ backend = (loose_backend *)_backend;
+ *stream_out = NULL;
+ *len_out = 0;
+ *type_out = GIT_OBJECT_INVALID;
+
+ if (locate_object(&object_path, backend, oid) < 0) {
+ error = git_odb__error_notfound("no matching loose object",
+ oid, backend->oid_hexsize);
+ goto done;
+ }
+
+ stream = git__calloc(1, sizeof(loose_readstream));
+ GIT_ERROR_CHECK_ALLOC(stream);
+
+ hash_ctx = git__malloc(sizeof(git_hash_ctx));
+ GIT_ERROR_CHECK_ALLOC(hash_ctx);
+
+ algorithm = git_oid_algorithm(backend->options.oid_type);
+
+ if ((error = git_hash_ctx_init(hash_ctx, algorithm)) < 0 ||
+ (error = git_futils_mmap_ro_file(&stream->map, object_path.ptr)) < 0 ||
+ (error = git_zstream_init(&stream->zstream, GIT_ZSTREAM_INFLATE)) < 0)
+ goto done;
+
+ /* check for a packlike loose object */
+ if (!is_zlib_compressed_data(stream->map.data, stream->map.len))
+ error = loose_backend__readstream_packlike(&hdr, stream);
+ else
+ error = loose_backend__readstream_standard(&hdr, stream);
+
+ if (error < 0)
+ goto done;
+
+ stream->stream.backend = _backend;
+ stream->stream.hash_ctx = hash_ctx;
+ stream->stream.read = &loose_backend__readstream_read;
+ stream->stream.free = &loose_backend__readstream_free;
+
+ *stream_out = (git_odb_stream *)stream;
+ *len_out = hdr.size;
+ *type_out = hdr.type;
+
+done:
+ if (error < 0) {
+ if (stream) {
+ git_futils_mmap_free(&stream->map);
+ git_zstream_free(&stream->zstream);
+ git__free(stream);
+ }
+ if (hash_ctx) {
+ git_hash_ctx_cleanup(hash_ctx);
+ git__free(hash_ctx);
+ }
+ }
+
+ git_str_dispose(&object_path);
+ return error;
+}
+
+static int loose_backend__write(git_odb_backend *_backend, const git_oid *oid, const void *data, size_t len, git_object_t type)
+{
+ int error = 0;
+ git_str final_path = GIT_STR_INIT;
+ char header[MAX_HEADER_LEN];
+ size_t header_len;
+ git_filebuf fbuf = GIT_FILEBUF_INIT;
+ loose_backend *backend;
+
+ backend = (loose_backend *)_backend;
+
+ /* prepare the header for the file */
+ if ((error = git_odb__format_object_header(&header_len,
+ header, sizeof(header), len, type)) < 0)
+ goto cleanup;
+
+ if (git_str_joinpath(&final_path, backend->objects_dir, "tmp_object") < 0 ||
+ git_filebuf_open(&fbuf, final_path.ptr, filebuf_flags(backend),
+ backend->options.file_mode) < 0)
+ {
+ error = -1;
+ goto cleanup;
+ }
+
+ git_filebuf_write(&fbuf, header, header_len);
+ git_filebuf_write(&fbuf, data, len);
+
+ if (object_file_name(&final_path, backend, oid) < 0 ||
+ object_mkdir(&final_path, backend) < 0 ||
+ git_filebuf_commit_at(&fbuf, final_path.ptr) < 0)
+ error = -1;
+
+cleanup:
+ if (error < 0)
+ git_filebuf_cleanup(&fbuf);
+ git_str_dispose(&final_path);
+ return error;
+}
+
+static int loose_backend__freshen(
+ git_odb_backend *_backend,
+ const git_oid *oid)
+{
+ loose_backend *backend = (loose_backend *)_backend;
+ git_str path = GIT_STR_INIT;
+ int error;
+
+ if (object_file_name(&path, backend, oid) < 0)
+ return -1;
+
+ error = git_futils_touch(path.ptr, NULL);
+ git_str_dispose(&path);
+
+ return error;
+}
+
+static void loose_backend__free(git_odb_backend *_backend)
+{
+ git__free(_backend);
+}
+
+static void normalize_options(
+ git_odb_backend_loose_options *opts,
+ const git_odb_backend_loose_options *given_opts)
+{
+ git_odb_backend_loose_options init = GIT_ODB_BACKEND_LOOSE_OPTIONS_INIT;
+
+ if (given_opts)
+ memcpy(opts, given_opts, sizeof(git_odb_backend_loose_options));
+ else
+ memcpy(opts, &init, sizeof(git_odb_backend_loose_options));
+
+ if (opts->compression_level < 0)
+ opts->compression_level = Z_BEST_SPEED;
+
+ if (opts->dir_mode == 0)
+ opts->dir_mode = GIT_OBJECT_DIR_MODE;
+
+ if (opts->file_mode == 0)
+ opts->file_mode = GIT_OBJECT_FILE_MODE;
+
+ if (opts->oid_type == 0)
+ opts->oid_type = GIT_OID_DEFAULT;
+}
+
+int git_odb__backend_loose(
+ git_odb_backend **backend_out,
+ const char *objects_dir,
+ git_odb_backend_loose_options *opts)
+{
+ loose_backend *backend;
+ size_t objects_dirlen, alloclen;
+
+ GIT_ASSERT_ARG(backend_out);
+ GIT_ASSERT_ARG(objects_dir);
+
+ objects_dirlen = strlen(objects_dir);
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, sizeof(loose_backend), objects_dirlen);
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 2);
+ backend = git__calloc(1, alloclen);
+ GIT_ERROR_CHECK_ALLOC(backend);
+
+ backend->parent.version = GIT_ODB_BACKEND_VERSION;
+ backend->objects_dirlen = objects_dirlen;
+ memcpy(backend->objects_dir, objects_dir, objects_dirlen);
+
+ if (backend->objects_dir[backend->objects_dirlen - 1] != '/')
+ backend->objects_dir[backend->objects_dirlen++] = '/';
+
+ normalize_options(&backend->options, opts);
+ backend->oid_hexsize = git_oid_hexsize(backend->options.oid_type);
+
+ backend->parent.read = &loose_backend__read;
+ backend->parent.write = &loose_backend__write;
+ backend->parent.read_prefix = &loose_backend__read_prefix;
+ backend->parent.read_header = &loose_backend__read_header;
+ backend->parent.writestream = &loose_backend__writestream;
+ backend->parent.readstream = &loose_backend__readstream;
+ backend->parent.exists = &loose_backend__exists;
+ backend->parent.exists_prefix = &loose_backend__exists_prefix;
+ backend->parent.foreach = &loose_backend__foreach;
+ backend->parent.freshen = &loose_backend__freshen;
+ backend->parent.free = &loose_backend__free;
+
+ *backend_out = (git_odb_backend *)backend;
+ return 0;
+}
+
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+int git_odb_backend_loose(
+ git_odb_backend **backend_out,
+ const char *objects_dir,
+ git_odb_backend_loose_options *opts)
+{
+ return git_odb__backend_loose(backend_out, objects_dir, opts);
+}
+#else
+int git_odb_backend_loose(
+ git_odb_backend **backend_out,
+ const char *objects_dir,
+ int compression_level,
+ int do_fsync,
+ unsigned int dir_mode,
+ unsigned int file_mode)
+{
+ git_odb_backend_loose_flag_t flags = 0;
+ git_odb_backend_loose_options opts = GIT_ODB_BACKEND_LOOSE_OPTIONS_INIT;
+
+ if (do_fsync)
+ flags |= GIT_ODB_BACKEND_LOOSE_FSYNC;
+
+ opts.flags = flags;
+ opts.compression_level = compression_level;
+ opts.dir_mode = dir_mode;
+ opts.file_mode = file_mode;
+ opts.oid_type = GIT_OID_DEFAULT;
+
+ return git_odb__backend_loose(backend_out, objects_dir, &opts);
+}
+#endif
diff --git a/src/libgit2/odb_mempack.c b/src/libgit2/odb_mempack.c
new file mode 100644
index 0000000..6f27f45
--- /dev/null
+++ b/src/libgit2/odb_mempack.c
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+
+#include "buf.h"
+#include "futils.h"
+#include "hash.h"
+#include "odb.h"
+#include "array.h"
+#include "oidmap.h"
+#include "pack-objects.h"
+
+#include "git2/odb_backend.h"
+#include "git2/object.h"
+#include "git2/types.h"
+#include "git2/pack.h"
+#include "git2/sys/odb_backend.h"
+#include "git2/sys/mempack.h"
+
+struct memobject {
+ git_oid oid;
+ size_t len;
+ git_object_t type;
+ char data[GIT_FLEX_ARRAY];
+};
+
+struct memory_packer_db {
+ git_odb_backend parent;
+ git_oidmap *objects;
+ git_array_t(struct memobject *) commits;
+};
+
+static int impl__write(git_odb_backend *_backend, const git_oid *oid, const void *data, size_t len, git_object_t type)
+{
+ struct memory_packer_db *db = (struct memory_packer_db *)_backend;
+ struct memobject *obj = NULL;
+ size_t alloc_len;
+
+ if (git_oidmap_exists(db->objects, oid))
+ return 0;
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, sizeof(struct memobject), len);
+ obj = git__malloc(alloc_len);
+ GIT_ERROR_CHECK_ALLOC(obj);
+
+ memcpy(obj->data, data, len);
+ git_oid_cpy(&obj->oid, oid);
+ obj->len = len;
+ obj->type = type;
+
+ if (git_oidmap_set(db->objects, &obj->oid, obj) < 0)
+ return -1;
+
+ if (type == GIT_OBJECT_COMMIT) {
+ struct memobject **store = git_array_alloc(db->commits);
+ GIT_ERROR_CHECK_ALLOC(store);
+ *store = obj;
+ }
+
+ return 0;
+}
+
+static int impl__exists(git_odb_backend *backend, const git_oid *oid)
+{
+ struct memory_packer_db *db = (struct memory_packer_db *)backend;
+
+ return git_oidmap_exists(db->objects, oid);
+}
+
+static int impl__read(void **buffer_p, size_t *len_p, git_object_t *type_p, git_odb_backend *backend, const git_oid *oid)
+{
+ struct memory_packer_db *db = (struct memory_packer_db *)backend;
+ struct memobject *obj;
+
+ if ((obj = git_oidmap_get(db->objects, oid)) == NULL)
+ return GIT_ENOTFOUND;
+
+ *len_p = obj->len;
+ *type_p = obj->type;
+ *buffer_p = git__malloc(obj->len);
+ GIT_ERROR_CHECK_ALLOC(*buffer_p);
+
+ memcpy(*buffer_p, obj->data, obj->len);
+ return 0;
+}
+
+static int impl__read_header(size_t *len_p, git_object_t *type_p, git_odb_backend *backend, const git_oid *oid)
+{
+ struct memory_packer_db *db = (struct memory_packer_db *)backend;
+ struct memobject *obj;
+
+ if ((obj = git_oidmap_get(db->objects, oid)) == NULL)
+ return GIT_ENOTFOUND;
+
+ *len_p = obj->len;
+ *type_p = obj->type;
+ return 0;
+}
+
+static int git_mempack__dump(
+ git_str *pack,
+ git_repository *repo,
+ git_odb_backend *_backend)
+{
+ struct memory_packer_db *db = (struct memory_packer_db *)_backend;
+ git_packbuilder *packbuilder;
+ uint32_t i;
+ int err = -1;
+
+ if (git_packbuilder_new(&packbuilder, repo) < 0)
+ return -1;
+
+ git_packbuilder_set_threads(packbuilder, 0);
+
+ for (i = 0; i < db->commits.size; ++i) {
+ struct memobject *commit = db->commits.ptr[i];
+
+ err = git_packbuilder_insert_commit(packbuilder, &commit->oid);
+ if (err < 0)
+ goto cleanup;
+ }
+
+ err = git_packbuilder__write_buf(pack, packbuilder);
+
+cleanup:
+ git_packbuilder_free(packbuilder);
+ return err;
+}
+
+int git_mempack_dump(
+ git_buf *pack,
+ git_repository *repo,
+ git_odb_backend *_backend)
+{
+ GIT_BUF_WRAP_PRIVATE(pack, git_mempack__dump, repo, _backend);
+}
+
+int git_mempack_reset(git_odb_backend *_backend)
+{
+ struct memory_packer_db *db = (struct memory_packer_db *)_backend;
+ struct memobject *object = NULL;
+
+ git_oidmap_foreach_value(db->objects, object, {
+ git__free(object);
+ });
+
+ git_array_clear(db->commits);
+
+ git_oidmap_clear(db->objects);
+
+ return 0;
+}
+
+static void impl__free(git_odb_backend *_backend)
+{
+ struct memory_packer_db *db = (struct memory_packer_db *)_backend;
+
+ git_mempack_reset(_backend);
+ git_oidmap_free(db->objects);
+ git__free(db);
+}
+
+int git_mempack_new(git_odb_backend **out)
+{
+ struct memory_packer_db *db;
+
+ GIT_ASSERT_ARG(out);
+
+ db = git__calloc(1, sizeof(struct memory_packer_db));
+ GIT_ERROR_CHECK_ALLOC(db);
+
+ if (git_oidmap_new(&db->objects) < 0)
+ return -1;
+
+ db->parent.version = GIT_ODB_BACKEND_VERSION;
+ db->parent.read = &impl__read;
+ db->parent.write = &impl__write;
+ db->parent.read_header = &impl__read_header;
+ db->parent.exists = &impl__exists;
+ db->parent.free = &impl__free;
+
+ *out = (git_odb_backend *)db;
+ return 0;
+}
diff --git a/src/libgit2/odb_pack.c b/src/libgit2/odb_pack.c
new file mode 100644
index 0000000..fc533ae
--- /dev/null
+++ b/src/libgit2/odb_pack.c
@@ -0,0 +1,986 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+
+#include <zlib.h>
+#include "git2/repository.h"
+#include "git2/indexer.h"
+#include "git2/sys/odb_backend.h"
+#include "delta.h"
+#include "futils.h"
+#include "hash.h"
+#include "midx.h"
+#include "mwindow.h"
+#include "odb.h"
+#include "pack.h"
+
+#include "git2/odb_backend.h"
+
+/* re-freshen pack files no more than every 2 seconds */
+#define FRESHEN_FREQUENCY 2
+
+struct pack_backend {
+ git_odb_backend parent;
+ git_odb_backend_pack_options opts;
+ git_midx_file *midx;
+ git_vector midx_packs;
+ git_vector packs;
+ struct git_pack_file *last_found;
+ char *pack_folder;
+};
+
+struct pack_writepack {
+ struct git_odb_writepack parent;
+ git_indexer *indexer;
+};
+
+/**
+ * The wonderful tale of a Packed Object lookup query
+ * ===================================================
+ * A riveting and epic story of epicness and ASCII
+ * art, presented by yours truly,
+ * Sir Vicent of Marti
+ *
+ *
+ * Chapter 1: Once upon a time...
+ * Initialization of the Pack Backend
+ * --------------------------------------------------
+ *
+ * # git_odb_backend_pack
+ * | Creates the pack backend structure, initializes the
+ * | callback pointers to our default read() and exist() methods,
+ * | and tries to find the `pack` folder, if it exists. ODBs without a `pack`
+ * | folder are ignored altogether. If there is a `pack` folder, it tries to
+ * | preload all the known packfiles in the ODB.
+ * |
+ * |-# pack_backend__refresh
+ * | The `multi-pack-index` is loaded if it exists and is valid.
+ * | Then we run a `dirent` callback through every file in the pack folder,
+ * | even those present in `multi-pack-index`. The unindexed packfiles are
+ * | then sorted according to a sorting callback.
+ * |
+ * |-# refresh_multi_pack_index
+ * | Detect the presence of the `multi-pack-index` file. If it needs to be
+ * | refreshed, frees the old copy and tries to load the new one, together
+ * | with all the packfiles it indexes. If the process fails, fall back to
+ * | the old behavior, as if the `multi-pack-index` file was not there.
+ * |
+ * |-# packfile_load__cb
+ * | | This callback is called from `dirent` with every single file
+ * | | inside the pack folder. We find the packs by actually locating
+ * | | their index (ends in ".idx"). From that index, we verify that
+ * | | the corresponding packfile exists and is valid, and if so, we
+ * | | add it to the pack list.
+ * | |
+ * | # git_mwindow_get_pack
+ * | Make sure that there's a packfile to back this index, and store
+ * | some very basic information regarding the packfile itself,
+ * | such as the full path, the size, and the modification time.
+ * | We don't actually open the packfile to check for internal consistency.
+ * |
+ * |-# packfile_sort__cb
+ * Sort all the preloaded packs according to some specific criteria:
+ * we prioritize the "newer" packs because it's more likely they
+ * contain the objects we are looking for, and we prioritize local
+ * packs over remote ones.
+ *
+ *
+ *
+ * Chapter 2: To be, or not to be...
+ * A standard packed `exist` query for an OID
+ * --------------------------------------------------
+ *
+ * # pack_backend__exists / pack_backend__exists_prefix
+ * | Check if the given oid (or an oid prefix) exists in any of the
+ * | packs that have been loaded for our ODB.
+ * |
+ * |-# pack_entry_find / pack_entry_find_prefix
+ * | If there is a multi-pack-index present, search the oid in that
+ * | index first. If it is not found there, iterate through all the unindexed
+ * | packs that have been preloaded (starting by the pack where the latest
+ * | object was found) to try to find the OID in one of them.
+ * |
+ * |-# git_midx_entry_find
+ * | Search for the oid in the multi-pack-index. See
+ * | <https://github.com/git/git/blob/master/Documentation/technical/pack-format.txt>
+ * | for specifics on the multi-pack-index format and how do we find
+ * | entries in it.
+ * |
+ * |-# git_pack_entry_find
+ * | Check the index of an individual unindexed pack to see if the
+ * | OID can be found. If we can find the offset to that inside of the
+ * | index, that means the object is contained inside of the packfile and
+ * | we can stop searching. Before returning, we verify that the
+ * | packfile behind the index we are searching still exists on disk.
+ * |
+ * |-# pack_entry_find_offset
+ * | Mmap the actual index file to disk if it hasn't been opened
+ * | yet, and run a binary search through it to find the OID.
+ * | See <https://github.com/git/git/blob/master/Documentation/technical/pack-format.txt>
+ * | for specifics on the Packfile Index format and how do we find
+ * | entries in it.
+ * |
+ * |-# pack_index_open
+ * | Guess the name of the index based on the full path to the
+ * | packfile, open it and verify its contents. Only if the index
+ * | has not been opened already.
+ * |
+ * |-# pack_index_check
+ * Mmap the index file and do a quick run through the header
+ * to guess the index version (right now we support v1 and v2),
+ * and to verify that the size of the index makes sense.
+ *
+ *
+ *
+ * Chapter 3: The neverending story...
+ * A standard packed `lookup` query for an OID
+ * --------------------------------------------------
+ *
+ * # pack_backend__read / pack_backend__read_prefix
+ * | Check if the given oid (or an oid prefix) exists in any of the
+ * | packs that have been loaded for our ODB. If it does, open the packfile and
+ * | read from it.
+ * |
+ * |-# git_packfile_unpack
+ * Armed with a packfile and the offset within it, we can finally unpack
+ * the object pointed at by the oid. This involves mmapping part of
+ * the `.pack` file, and uncompressing the object within it (if it is
+ * stored in the undelfitied representation), or finding a base object and
+ * applying some deltas to its uncompressed representation (if it is stored
+ * in the deltified representation). See
+ * <https://github.com/git/git/blob/master/Documentation/technical/pack-format.txt>
+ * for specifics on the Packfile format and how do we read from it.
+ *
+ */
+
+
+/***********************************************************
+ *
+ * FORWARD DECLARATIONS
+ *
+ ***********************************************************/
+
+static int packfile_sort__cb(const void *a_, const void *b_);
+
+static int packfile_load__cb(void *_data, git_str *path);
+
+static int packfile_byname_search_cmp(const void *path, const void *pack_entry);
+
+static int pack_entry_find(struct git_pack_entry *e,
+ struct pack_backend *backend, const git_oid *oid);
+
+/* Can find the offset of an object given
+ * a prefix of an identifier.
+ * Sets GIT_EAMBIGUOUS if short oid is ambiguous.
+ * This method assumes that len is between
+ * GIT_OID_MINPREFIXLEN and the hexsize for the hash type.
+ */
+static int pack_entry_find_prefix(
+ struct git_pack_entry *e,
+ struct pack_backend *backend,
+ const git_oid *short_oid,
+ size_t len);
+
+
+
+/***********************************************************
+ *
+ * PACK WINDOW MANAGEMENT
+ *
+ ***********************************************************/
+
+static int packfile_byname_search_cmp(const void *path_, const void *p_)
+{
+ const git_str *path = (const git_str *)path_;
+ const struct git_pack_file *p = (const struct git_pack_file *)p_;
+
+ return strncmp(p->pack_name, git_str_cstr(path), git_str_len(path));
+}
+
+static int packfile_sort__cb(const void *a_, const void *b_)
+{
+ const struct git_pack_file *a = a_;
+ const struct git_pack_file *b = b_;
+ int st;
+
+ /*
+ * Local packs tend to contain objects specific to our
+ * variant of the project than remote ones. In addition,
+ * remote ones could be on a network mounted filesystem.
+ * Favor local ones for these reasons.
+ */
+ st = a->pack_local - b->pack_local;
+ if (st)
+ return -st;
+
+ /*
+ * Younger packs tend to contain more recent objects,
+ * and more recent objects tend to get accessed more
+ * often.
+ */
+ if (a->mtime < b->mtime)
+ return 1;
+ else if (a->mtime == b->mtime)
+ return 0;
+
+ return -1;
+}
+
+
+static int packfile_load__cb(void *data, git_str *path)
+{
+ struct pack_backend *backend = data;
+ struct git_pack_file *pack;
+ const char *path_str = git_str_cstr(path);
+ git_str index_prefix = GIT_STR_INIT;
+ size_t cmp_len = git_str_len(path);
+ int error;
+
+ if (cmp_len <= strlen(".idx") || git__suffixcmp(path_str, ".idx") != 0)
+ return 0; /* not an index */
+
+ cmp_len -= strlen(".idx");
+ git_str_attach_notowned(&index_prefix, path_str, cmp_len);
+
+ if (git_vector_search2(NULL, &backend->midx_packs, packfile_byname_search_cmp, &index_prefix) == 0)
+ return 0;
+ if (git_vector_search2(NULL, &backend->packs, packfile_byname_search_cmp, &index_prefix) == 0)
+ return 0;
+
+ error = git_mwindow_get_pack(&pack, path->ptr, backend->opts.oid_type);
+
+ /* ignore missing .pack file as git does */
+ if (error == GIT_ENOTFOUND) {
+ git_error_clear();
+ return 0;
+ }
+
+ if (!error)
+ error = git_vector_insert(&backend->packs, pack);
+
+ return error;
+
+}
+
+static int pack_entry_find(struct git_pack_entry *e, struct pack_backend *backend, const git_oid *oid)
+{
+ struct git_pack_file *last_found = backend->last_found, *p;
+ git_midx_entry midx_entry;
+ size_t oid_hexsize = git_oid_hexsize(backend->opts.oid_type);
+ size_t i;
+
+ if (backend->midx &&
+ git_midx_entry_find(&midx_entry, backend->midx, oid, oid_hexsize) == 0 &&
+ midx_entry.pack_index < git_vector_length(&backend->midx_packs)) {
+ e->offset = midx_entry.offset;
+ git_oid_cpy(&e->id, &midx_entry.sha1);
+ e->p = git_vector_get(&backend->midx_packs, midx_entry.pack_index);
+ return 0;
+ }
+
+ if (last_found &&
+ git_pack_entry_find(e, last_found, oid, oid_hexsize) == 0)
+ return 0;
+
+ git_vector_foreach(&backend->packs, i, p) {
+ if (p == last_found)
+ continue;
+
+ if (git_pack_entry_find(e, p, oid, oid_hexsize) == 0) {
+ backend->last_found = p;
+ return 0;
+ }
+ }
+
+ return git_odb__error_notfound(
+ "failed to find pack entry", oid, oid_hexsize);
+}
+
+static int pack_entry_find_prefix(
+ struct git_pack_entry *e,
+ struct pack_backend *backend,
+ const git_oid *short_oid,
+ size_t len)
+{
+ int error;
+ size_t i;
+ git_oid found_full_oid;
+ bool found = false;
+ struct git_pack_file *last_found = backend->last_found, *p;
+ git_midx_entry midx_entry;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_oid_clear(&found_full_oid, short_oid->type);
+#else
+ git_oid_clear(&found_full_oid, GIT_OID_SHA1);
+#endif
+
+ if (backend->midx) {
+ error = git_midx_entry_find(&midx_entry, backend->midx, short_oid, len);
+ if (error == GIT_EAMBIGUOUS)
+ return error;
+ if (!error && midx_entry.pack_index < git_vector_length(&backend->midx_packs)) {
+ e->offset = midx_entry.offset;
+ git_oid_cpy(&e->id, &midx_entry.sha1);
+ e->p = git_vector_get(&backend->midx_packs, midx_entry.pack_index);
+ git_oid_cpy(&found_full_oid, &e->id);
+ found = true;
+ }
+ }
+
+ if (last_found) {
+ error = git_pack_entry_find(e, last_found, short_oid, len);
+ if (error == GIT_EAMBIGUOUS)
+ return error;
+ if (!error) {
+ if (found && git_oid_cmp(&e->id, &found_full_oid))
+ return git_odb__error_ambiguous("found multiple pack entries");
+ git_oid_cpy(&found_full_oid, &e->id);
+ found = true;
+ }
+ }
+
+ git_vector_foreach(&backend->packs, i, p) {
+ if (p == last_found)
+ continue;
+
+ error = git_pack_entry_find(e, p, short_oid, len);
+ if (error == GIT_EAMBIGUOUS)
+ return error;
+ if (!error) {
+ if (found && git_oid_cmp(&e->id, &found_full_oid))
+ return git_odb__error_ambiguous("found multiple pack entries");
+ git_oid_cpy(&found_full_oid, &e->id);
+ found = true;
+ backend->last_found = p;
+ }
+ }
+
+ if (!found)
+ return git_odb__error_notfound("no matching pack entry for prefix",
+ short_oid, len);
+ else
+ return 0;
+}
+
+/***********************************************************
+ *
+ * MULTI-PACK-INDEX SUPPORT
+ *
+ * Functions needed to support the multi-pack-index.
+ *
+ ***********************************************************/
+
+/*
+ * Remove the multi-pack-index, and move all midx_packs to packs.
+ */
+static int remove_multi_pack_index(struct pack_backend *backend)
+{
+ size_t i, j = git_vector_length(&backend->packs);
+ struct pack_backend *p;
+ int error = git_vector_size_hint(
+ &backend->packs,
+ j + git_vector_length(&backend->midx_packs));
+ if (error < 0)
+ return error;
+
+ git_vector_foreach(&backend->midx_packs, i, p)
+ git_vector_set(NULL, &backend->packs, j++, p);
+ git_vector_clear(&backend->midx_packs);
+
+ git_midx_free(backend->midx);
+ backend->midx = NULL;
+
+ return 0;
+}
+
+/*
+ * Loads a single .pack file referred to by the multi-pack-index. These must
+ * match the order in which they are declared in the multi-pack-index file,
+ * since these files are referred to by their index.
+ */
+static int process_multi_pack_index_pack(
+ struct pack_backend *backend,
+ size_t i,
+ const char *packfile_name)
+{
+ int error;
+ struct git_pack_file *pack;
+ size_t found_position;
+ git_str pack_path = GIT_STR_INIT, index_prefix = GIT_STR_INIT;
+
+ error = git_str_joinpath(&pack_path, backend->pack_folder, packfile_name);
+ if (error < 0)
+ return error;
+
+ /* This is ensured by midx_parse_packfile_name() */
+ if (git_str_len(&pack_path) <= strlen(".idx") || git__suffixcmp(git_str_cstr(&pack_path), ".idx") != 0)
+ return git_odb__error_notfound("midx file contained a non-index", NULL, 0);
+
+ git_str_attach_notowned(&index_prefix, git_str_cstr(&pack_path), git_str_len(&pack_path) - strlen(".idx"));
+
+ if (git_vector_search2(&found_position, &backend->packs, packfile_byname_search_cmp, &index_prefix) == 0) {
+ /* Pack was found in the packs list. Moving it to the midx_packs list. */
+ git_str_dispose(&pack_path);
+ git_vector_set(NULL, &backend->midx_packs, i, git_vector_get(&backend->packs, found_position));
+ git_vector_remove(&backend->packs, found_position);
+ return 0;
+ }
+
+ /* Pack was not found. Allocate a new one. */
+ error = git_mwindow_get_pack(
+ &pack,
+ git_str_cstr(&pack_path),
+ backend->opts.oid_type);
+ git_str_dispose(&pack_path);
+ if (error < 0)
+ return error;
+
+ git_vector_set(NULL, &backend->midx_packs, i, pack);
+ return 0;
+}
+
+/*
+ * Reads the multi-pack-index. If this fails for whatever reason, the
+ * multi-pack-index object is freed, and all the packfiles that are related to
+ * it are moved to the unindexed packfiles vector.
+ */
+static int refresh_multi_pack_index(struct pack_backend *backend)
+{
+ int error;
+ git_str midx_path = GIT_STR_INIT;
+ const char *packfile_name;
+ size_t i;
+
+ error = git_str_joinpath(&midx_path, backend->pack_folder, "multi-pack-index");
+ if (error < 0)
+ return error;
+
+ /*
+ * Check whether the multi-pack-index has changed. If it has, close any
+ * old multi-pack-index and move all the packfiles to the unindexed
+ * packs. This is done to prevent losing any open packfiles in case
+ * refreshing the new multi-pack-index fails, or the file is deleted.
+ */
+ if (backend->midx) {
+ if (!git_midx_needs_refresh(backend->midx, git_str_cstr(&midx_path))) {
+ git_str_dispose(&midx_path);
+ return 0;
+ }
+ error = remove_multi_pack_index(backend);
+ if (error < 0) {
+ git_str_dispose(&midx_path);
+ return error;
+ }
+ }
+
+ error = git_midx_open(&backend->midx, git_str_cstr(&midx_path),
+ backend->opts.oid_type);
+
+ git_str_dispose(&midx_path);
+ if (error < 0)
+ return error;
+
+ git_vector_resize_to(&backend->midx_packs, git_vector_length(&backend->midx->packfile_names));
+
+ git_vector_foreach(&backend->midx->packfile_names, i, packfile_name) {
+ error = process_multi_pack_index_pack(backend, i, packfile_name);
+ if (error < 0) {
+ /*
+ * Something failed during reading multi-pack-index.
+ * Restore the state of backend as if the
+ * multi-pack-index was never there, and move all
+ * packfiles that have been processed so far to the
+ * unindexed packs.
+ */
+ git_vector_resize_to(&backend->midx_packs, i);
+ remove_multi_pack_index(backend);
+ return error;
+ }
+ }
+
+ return 0;
+}
+
+/***********************************************************
+ *
+ * PACKED BACKEND PUBLIC API
+ *
+ * Implement the git_odb_backend API calls
+ *
+ ***********************************************************/
+static int pack_backend__refresh(git_odb_backend *backend_)
+{
+ int error;
+ struct stat st;
+ git_str path = GIT_STR_INIT;
+ struct pack_backend *backend = (struct pack_backend *)backend_;
+
+ if (backend->pack_folder == NULL)
+ return 0;
+
+ if (p_stat(backend->pack_folder, &st) < 0 || !S_ISDIR(st.st_mode))
+ return git_odb__error_notfound("failed to refresh packfiles", NULL, 0);
+
+ if (refresh_multi_pack_index(backend) < 0) {
+ /*
+ * It is okay if this fails. We will just not use the
+ * multi-pack-index in this case.
+ */
+ git_error_clear();
+ }
+
+ /* reload all packs */
+ git_str_sets(&path, backend->pack_folder);
+ error = git_fs_path_direach(&path, 0, packfile_load__cb, backend);
+
+ git_str_dispose(&path);
+ git_vector_sort(&backend->packs);
+
+ return error;
+}
+
+static int pack_backend__read_header(
+ size_t *len_p, git_object_t *type_p,
+ struct git_odb_backend *backend, const git_oid *oid)
+{
+ struct git_pack_entry e;
+ int error;
+
+ GIT_ASSERT_ARG(len_p);
+ GIT_ASSERT_ARG(type_p);
+ GIT_ASSERT_ARG(backend);
+ GIT_ASSERT_ARG(oid);
+
+ if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < 0)
+ return error;
+
+ return git_packfile_resolve_header(len_p, type_p, e.p, e.offset);
+}
+
+static int pack_backend__freshen(
+ git_odb_backend *backend, const git_oid *oid)
+{
+ struct git_pack_entry e;
+ time_t now;
+ int error;
+
+ if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < 0)
+ return error;
+
+ now = time(NULL);
+
+ if (e.p->last_freshen > now - FRESHEN_FREQUENCY)
+ return 0;
+
+ if ((error = git_futils_touch(e.p->pack_name, &now)) < 0)
+ return error;
+
+ e.p->last_freshen = now;
+ return 0;
+}
+
+static int pack_backend__read(
+ void **buffer_p, size_t *len_p, git_object_t *type_p,
+ git_odb_backend *backend, const git_oid *oid)
+{
+ struct git_pack_entry e;
+ git_rawobj raw = {NULL};
+ int error;
+
+ if ((error = pack_entry_find(&e, (struct pack_backend *)backend, oid)) < 0 ||
+ (error = git_packfile_unpack(&raw, e.p, &e.offset)) < 0)
+ return error;
+
+ *buffer_p = raw.data;
+ *len_p = raw.len;
+ *type_p = raw.type;
+
+ return 0;
+}
+
+static int pack_backend__read_prefix(
+ git_oid *out_oid,
+ void **buffer_p,
+ size_t *len_p,
+ git_object_t *type_p,
+ git_odb_backend *_backend,
+ const git_oid *short_oid,
+ size_t len)
+{
+ struct pack_backend *backend = (struct pack_backend *)_backend;
+ int error = 0;
+
+ if (len < GIT_OID_MINPREFIXLEN)
+ error = git_odb__error_ambiguous("prefix length too short");
+
+ else if (len >= git_oid_hexsize(backend->opts.oid_type)) {
+ /* We can fall back to regular read method */
+ error = pack_backend__read(buffer_p, len_p, type_p, _backend, short_oid);
+ if (!error)
+ git_oid_cpy(out_oid, short_oid);
+ } else {
+ struct git_pack_entry e;
+ git_rawobj raw = {NULL};
+
+ if ((error = pack_entry_find_prefix(&e,
+ backend, short_oid, len)) == 0 &&
+ (error = git_packfile_unpack(&raw, e.p, &e.offset)) == 0)
+ {
+ *buffer_p = raw.data;
+ *len_p = raw.len;
+ *type_p = raw.type;
+ git_oid_cpy(out_oid, &e.id);
+ }
+ }
+
+ return error;
+}
+
+static int pack_backend__exists(git_odb_backend *backend, const git_oid *oid)
+{
+ struct git_pack_entry e;
+ return pack_entry_find(&e, (struct pack_backend *)backend, oid) == 0;
+}
+
+static int pack_backend__exists_prefix(
+ git_oid *out, git_odb_backend *backend, const git_oid *short_id, size_t len)
+{
+ int error;
+ struct pack_backend *pb = (struct pack_backend *)backend;
+ struct git_pack_entry e = {0};
+
+ error = pack_entry_find_prefix(&e, pb, short_id, len);
+ git_oid_cpy(out, &e.id);
+ return error;
+}
+
+static int pack_backend__foreach(git_odb_backend *_backend, git_odb_foreach_cb cb, void *data)
+{
+ int error;
+ struct git_pack_file *p;
+ struct pack_backend *backend;
+ unsigned int i;
+
+ GIT_ASSERT_ARG(_backend);
+ GIT_ASSERT_ARG(cb);
+
+ backend = (struct pack_backend *)_backend;
+
+ /* Make sure we know about the packfiles */
+ if ((error = pack_backend__refresh(_backend)) != 0)
+ return error;
+
+ if (backend->midx && (error = git_midx_foreach_entry(backend->midx, cb, data)) != 0)
+ return error;
+ git_vector_foreach(&backend->packs, i, p) {
+ if ((error = git_pack_foreach_entry(p, cb, data)) != 0)
+ return error;
+ }
+
+ return 0;
+}
+
+static int pack_backend__writepack_append(struct git_odb_writepack *_writepack, const void *data, size_t size, git_indexer_progress *stats)
+{
+ struct pack_writepack *writepack = (struct pack_writepack *)_writepack;
+
+ GIT_ASSERT_ARG(writepack);
+
+ return git_indexer_append(writepack->indexer, data, size, stats);
+}
+
+static int pack_backend__writepack_commit(struct git_odb_writepack *_writepack, git_indexer_progress *stats)
+{
+ struct pack_writepack *writepack = (struct pack_writepack *)_writepack;
+
+ GIT_ASSERT_ARG(writepack);
+
+ return git_indexer_commit(writepack->indexer, stats);
+}
+
+static void pack_backend__writepack_free(struct git_odb_writepack *_writepack)
+{
+ struct pack_writepack *writepack;
+
+ if (!_writepack)
+ return;
+
+ writepack = (struct pack_writepack *)_writepack;
+
+ git_indexer_free(writepack->indexer);
+ git__free(writepack);
+}
+
+static int pack_backend__writepack(struct git_odb_writepack **out,
+ git_odb_backend *_backend,
+ git_odb *odb,
+ git_indexer_progress_cb progress_cb,
+ void *progress_payload)
+{
+ git_indexer_options opts = GIT_INDEXER_OPTIONS_INIT;
+ struct pack_backend *backend;
+ struct pack_writepack *writepack;
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(_backend);
+
+ *out = NULL;
+
+ opts.progress_cb = progress_cb;
+ opts.progress_cb_payload = progress_payload;
+
+ backend = (struct pack_backend *)_backend;
+
+ writepack = git__calloc(1, sizeof(struct pack_writepack));
+ GIT_ERROR_CHECK_ALLOC(writepack);
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ opts.odb = odb;
+
+ error = git_indexer_new(&writepack->indexer,
+ backend->pack_folder,
+ backend->opts.oid_type,
+ &opts);
+#else
+ error = git_indexer_new(&writepack->indexer,
+ backend->pack_folder, 0, odb, &opts);
+#endif
+
+ if (error < 0)
+ return -1;
+
+ writepack->parent.backend = _backend;
+ writepack->parent.append = pack_backend__writepack_append;
+ writepack->parent.commit = pack_backend__writepack_commit;
+ writepack->parent.free = pack_backend__writepack_free;
+
+ *out = (git_odb_writepack *)writepack;
+
+ return 0;
+}
+
+static int get_idx_path(
+ git_str *idx_path,
+ struct pack_backend *backend,
+ struct git_pack_file *p)
+{
+ size_t path_len;
+ int error;
+
+ error = git_fs_path_prettify(idx_path, p->pack_name, backend->pack_folder);
+ if (error < 0)
+ return error;
+ path_len = git_str_len(idx_path);
+ if (path_len <= strlen(".pack") || git__suffixcmp(git_str_cstr(idx_path), ".pack") != 0)
+ return git_odb__error_notfound("packfile does not end in .pack", NULL, 0);
+ path_len -= strlen(".pack");
+ error = git_str_splice(idx_path, path_len, strlen(".pack"), ".idx", strlen(".idx"));
+ if (error < 0)
+ return error;
+
+ return 0;
+}
+
+static int pack_backend__writemidx(git_odb_backend *_backend)
+{
+ struct pack_backend *backend;
+ git_midx_writer *w = NULL;
+ struct git_pack_file *p;
+ size_t i;
+ int error = 0;
+
+ GIT_ASSERT_ARG(_backend);
+
+ backend = (struct pack_backend *)_backend;
+
+ error = git_midx_writer_new(&w, backend->pack_folder
+#ifdef GIT_EXPERIMENTAL_SHA256
+ , backend->opts.oid_type
+#endif
+ );
+
+ if (error < 0)
+ return error;
+
+ git_vector_foreach(&backend->midx_packs, i, p) {
+ git_str idx_path = GIT_STR_INIT;
+ error = get_idx_path(&idx_path, backend, p);
+ if (error < 0)
+ goto cleanup;
+ error = git_midx_writer_add(w, git_str_cstr(&idx_path));
+ git_str_dispose(&idx_path);
+ if (error < 0)
+ goto cleanup;
+ }
+ git_vector_foreach(&backend->packs, i, p) {
+ git_str idx_path = GIT_STR_INIT;
+ error = get_idx_path(&idx_path, backend, p);
+ if (error < 0)
+ goto cleanup;
+ error = git_midx_writer_add(w, git_str_cstr(&idx_path));
+ git_str_dispose(&idx_path);
+ if (error < 0)
+ goto cleanup;
+ }
+
+ /*
+ * Invalidate the previous midx before writing the new one.
+ */
+ error = remove_multi_pack_index(backend);
+ if (error < 0)
+ goto cleanup;
+ error = git_midx_writer_commit(w);
+ if (error < 0)
+ goto cleanup;
+ error = refresh_multi_pack_index(backend);
+
+cleanup:
+ git_midx_writer_free(w);
+ return error;
+}
+
+static void pack_backend__free(git_odb_backend *_backend)
+{
+ struct pack_backend *backend;
+ struct git_pack_file *p;
+ size_t i;
+
+ if (!_backend)
+ return;
+
+ backend = (struct pack_backend *)_backend;
+
+ git_vector_foreach(&backend->midx_packs, i, p)
+ git_mwindow_put_pack(p);
+ git_vector_foreach(&backend->packs, i, p)
+ git_mwindow_put_pack(p);
+
+ git_midx_free(backend->midx);
+ git_vector_free(&backend->midx_packs);
+ git_vector_free(&backend->packs);
+ git__free(backend->pack_folder);
+ git__free(backend);
+}
+
+static int pack_backend__alloc(
+ struct pack_backend **out,
+ size_t initial_size,
+ const git_odb_backend_pack_options *opts)
+{
+ struct pack_backend *backend = git__calloc(1, sizeof(struct pack_backend));
+ GIT_ERROR_CHECK_ALLOC(backend);
+
+ if (git_vector_init(&backend->midx_packs, 0, NULL) < 0) {
+ git__free(backend);
+ return -1;
+ }
+
+ if (git_vector_init(&backend->packs, initial_size, packfile_sort__cb) < 0) {
+ git_vector_free(&backend->midx_packs);
+ git__free(backend);
+ return -1;
+ }
+
+ if (opts)
+ memcpy(&backend->opts, opts, sizeof(git_odb_backend_pack_options));
+
+ if (!backend->opts.oid_type)
+ backend->opts.oid_type = GIT_OID_DEFAULT;
+
+ backend->parent.version = GIT_ODB_BACKEND_VERSION;
+
+ backend->parent.read = &pack_backend__read;
+ backend->parent.read_prefix = &pack_backend__read_prefix;
+ backend->parent.read_header = &pack_backend__read_header;
+ backend->parent.exists = &pack_backend__exists;
+ backend->parent.exists_prefix = &pack_backend__exists_prefix;
+ backend->parent.refresh = &pack_backend__refresh;
+ backend->parent.foreach = &pack_backend__foreach;
+ backend->parent.writepack = &pack_backend__writepack;
+ backend->parent.writemidx = &pack_backend__writemidx;
+ backend->parent.freshen = &pack_backend__freshen;
+ backend->parent.free = &pack_backend__free;
+
+ *out = backend;
+ return 0;
+}
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+int git_odb_backend_one_pack(
+ git_odb_backend **backend_out,
+ const char *idx,
+ const git_odb_backend_pack_options *opts)
+#else
+int git_odb_backend_one_pack(
+ git_odb_backend **backend_out,
+ const char *idx)
+#endif
+{
+ struct pack_backend *backend = NULL;
+ struct git_pack_file *packfile = NULL;
+
+#ifndef GIT_EXPERIMENTAL_SHA256
+ git_odb_backend_pack_options *opts = NULL;
+#endif
+
+ git_oid_t oid_type = opts ? opts->oid_type : 0;
+
+ if (pack_backend__alloc(&backend, 1, opts) < 0)
+ return -1;
+
+ if (git_mwindow_get_pack(&packfile, idx, oid_type) < 0 ||
+ git_vector_insert(&backend->packs, packfile) < 0) {
+ pack_backend__free((git_odb_backend *)backend);
+ return -1;
+ }
+
+ *backend_out = (git_odb_backend *)backend;
+ return 0;
+}
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+int git_odb_backend_pack(
+ git_odb_backend **backend_out,
+ const char *objects_dir,
+ const git_odb_backend_pack_options *opts)
+#else
+int git_odb_backend_pack(
+ git_odb_backend **backend_out,
+ const char *objects_dir)
+#endif
+{
+ int error = 0;
+ struct pack_backend *backend = NULL;
+ git_str path = GIT_STR_INIT;
+
+#ifndef GIT_EXPERIMENTAL_SHA256
+ git_odb_backend_pack_options *opts = NULL;
+#endif
+
+ if (pack_backend__alloc(&backend, 8, opts) < 0)
+ return -1;
+
+ if (!(error = git_str_joinpath(&path, objects_dir, "pack")) &&
+ git_fs_path_isdir(git_str_cstr(&path))) {
+ backend->pack_folder = git_str_detach(&path);
+ error = pack_backend__refresh((git_odb_backend *)backend);
+ }
+
+ if (error < 0) {
+ pack_backend__free((git_odb_backend *)backend);
+ backend = NULL;
+ }
+
+ *backend_out = (git_odb_backend *)backend;
+
+ git_str_dispose(&path);
+
+ return error;
+}
diff --git a/src/libgit2/offmap.c b/src/libgit2/offmap.c
new file mode 100644
index 0000000..be9eb66
--- /dev/null
+++ b/src/libgit2/offmap.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "offmap.h"
+
+#define kmalloc git__malloc
+#define kcalloc git__calloc
+#define krealloc git__realloc
+#define kreallocarray git__reallocarray
+#define kfree git__free
+#include "khash.h"
+
+__KHASH_TYPE(off, off64_t, void *)
+
+__KHASH_IMPL(off, static kh_inline, off64_t, void *, 1, kh_int64_hash_func, kh_int64_hash_equal)
+
+
+int git_offmap_new(git_offmap **out)
+{
+ *out = kh_init(off);
+ GIT_ERROR_CHECK_ALLOC(*out);
+
+ return 0;
+}
+
+void git_offmap_free(git_offmap *map)
+{
+ kh_destroy(off, map);
+}
+
+void git_offmap_clear(git_offmap *map)
+{
+ kh_clear(off, map);
+}
+
+size_t git_offmap_size(git_offmap *map)
+{
+ return kh_size(map);
+}
+
+void *git_offmap_get(git_offmap *map, const off64_t key)
+{
+ size_t idx = kh_get(off, map, key);
+ if (idx == kh_end(map) || !kh_exist(map, idx))
+ return NULL;
+ return kh_val(map, idx);
+}
+
+int git_offmap_set(git_offmap *map, const off64_t key, void *value)
+{
+ size_t idx;
+ int rval;
+
+ idx = kh_put(off, map, key, &rval);
+ if (rval < 0)
+ return -1;
+
+ if (rval == 0)
+ kh_key(map, idx) = key;
+
+ kh_val(map, idx) = value;
+
+ return 0;
+}
+
+int git_offmap_delete(git_offmap *map, const off64_t key)
+{
+ khiter_t idx = kh_get(off, map, key);
+ if (idx == kh_end(map))
+ return GIT_ENOTFOUND;
+ kh_del(off, map, idx);
+ return 0;
+}
+
+int git_offmap_exists(git_offmap *map, const off64_t key)
+{
+ return kh_get(off, map, key) != kh_end(map);
+}
+
+int git_offmap_iterate(void **value, git_offmap *map, size_t *iter, off64_t *key)
+{
+ size_t i = *iter;
+
+ while (i < map->n_buckets && !kh_exist(map, i))
+ i++;
+
+ if (i >= map->n_buckets)
+ return GIT_ITEROVER;
+
+ if (key)
+ *key = kh_key(map, i);
+ if (value)
+ *value = kh_value(map, i);
+ *iter = ++i;
+
+ return 0;
+}
diff --git a/src/libgit2/offmap.h b/src/libgit2/offmap.h
new file mode 100644
index 0000000..81c459b
--- /dev/null
+++ b/src/libgit2/offmap.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2012 the libgit2 contributors
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_offmap_h__
+#define INCLUDE_offmap_h__
+
+#include "common.h"
+
+#include "git2/types.h"
+
+/** A map with `off64_t`s as key. */
+typedef struct kh_off_s git_offmap;
+
+/**
+ * Allocate a new `off64_t` map.
+ *
+ * @param out Pointer to the map that shall be allocated.
+ * @return 0 on success, an error code if allocation has failed.
+ */
+int git_offmap_new(git_offmap **out);
+
+/**
+ * Free memory associated with the map.
+ *
+ * Note that this function will _not_ free values added to this
+ * map.
+ *
+ * @param map Pointer to the map that is to be free'd. May be
+ * `NULL`.
+ */
+void git_offmap_free(git_offmap *map);
+
+/**
+ * Clear all entries from the map.
+ *
+ * This function will remove all entries from the associated map.
+ * Memory associated with it will not be released, though.
+ *
+ * @param map Pointer to the map that shall be cleared. May be
+ * `NULL`.
+ */
+void git_offmap_clear(git_offmap *map);
+
+/**
+ * Return the number of elements in the map.
+ *
+ * @parameter map map containing the elements
+ * @return number of elements in the map
+ */
+size_t git_offmap_size(git_offmap *map);
+
+/**
+ * Return value associated with the given key.
+ *
+ * @param map map to search key in
+ * @param key key to search for
+ * @return value associated with the given key or NULL if the key was not found
+ */
+void *git_offmap_get(git_offmap *map, const off64_t key);
+
+/**
+ * Set the entry for key to value.
+ *
+ * If the map has no corresponding entry for the given key, a new
+ * entry will be created with the given value. If an entry exists
+ * already, its value will be updated to match the given value.
+ *
+ * @param map map to create new entry in
+ * @param key key to set
+ * @param value value to associate the key with; may be NULL
+ * @return zero if the key was successfully set, a negative error
+ * code otherwise
+ */
+int git_offmap_set(git_offmap *map, const off64_t key, void *value);
+
+/**
+ * Delete an entry from the map.
+ *
+ * Delete the given key and its value from the map. If no such
+ * key exists, this will do nothing.
+ *
+ * @param map map to delete key in
+ * @param key key to delete
+ * @return `0` if the key has been deleted, GIT_ENOTFOUND if no
+ * such key was found, a negative code in case of an
+ * error
+ */
+int git_offmap_delete(git_offmap *map, const off64_t key);
+
+/**
+ * Check whether a key exists in the given map.
+ *
+ * @param map map to query for the key
+ * @param key key to search for
+ * @return 0 if the key has not been found, 1 otherwise
+ */
+int git_offmap_exists(git_offmap *map, const off64_t key);
+
+/**
+ * Iterate over entries of the map.
+ *
+ * This functions allows to iterate over all key-value entries of
+ * the map. The current position is stored in the `iter` variable
+ * and should be initialized to `0` before the first call to this
+ * function.
+ *
+ * @param map map to iterate over
+ * @param value pointer to the variable where to store the current
+ * value. May be NULL.
+ * @param iter iterator storing the current position. Initialize
+ * with zero previous to the first call.
+ * @param key pointer to the variable where to store the current
+ * key. May be NULL.
+ * @return `0` if the next entry was correctly retrieved.
+ * GIT_ITEROVER if no entries are left. A negative error
+ * code otherwise.
+ */
+int git_offmap_iterate(void **value, git_offmap *map, size_t *iter, off64_t *key);
+
+#define git_offmap_foreach(h, kvar, vvar, code) { size_t __i = 0; \
+ while (git_offmap_iterate((void **) &(vvar), h, &__i, &(kvar)) == 0) { \
+ code; \
+ } }
+
+#define git_offmap_foreach_value(h, vvar, code) { size_t __i = 0; \
+ while (git_offmap_iterate((void **) &(vvar), h, &__i, NULL) == 0) { \
+ code; \
+ } }
+
+#endif
diff --git a/src/libgit2/oid.c b/src/libgit2/oid.c
new file mode 100644
index 0000000..631a566
--- /dev/null
+++ b/src/libgit2/oid.c
@@ -0,0 +1,508 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "oid.h"
+
+#include "git2/oid.h"
+#include "repository.h"
+#include "threadstate.h"
+#include <string.h>
+#include <limits.h>
+
+const git_oid git_oid__empty_blob_sha1 =
+ GIT_OID_INIT(GIT_OID_SHA1,
+ { 0xe6, 0x9d, 0xe2, 0x9b, 0xb2, 0xd1, 0xd6, 0x43, 0x4b, 0x8b,
+ 0x29, 0xae, 0x77, 0x5a, 0xd8, 0xc2, 0xe4, 0x8c, 0x53, 0x91 });
+const git_oid git_oid__empty_tree_sha1 =
+ GIT_OID_INIT(GIT_OID_SHA1,
+ { 0x4b, 0x82, 0x5d, 0xc6, 0x42, 0xcb, 0x6e, 0xb9, 0xa0, 0x60,
+ 0xe5, 0x4b, 0xf8, 0xd6, 0x92, 0x88, 0xfb, 0xee, 0x49, 0x04 });
+
+static int oid_error_invalid(const char *msg)
+{
+ git_error_set(GIT_ERROR_INVALID, "unable to parse OID - %s", msg);
+ return -1;
+}
+
+int git_oid__fromstrn(
+ git_oid *out,
+ const char *str,
+ size_t length,
+ git_oid_t type)
+{
+ size_t size, p;
+ int v;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(str);
+
+ if (!(size = git_oid_size(type)))
+ return oid_error_invalid("unknown type");
+
+ if (!length)
+ return oid_error_invalid("too short");
+
+ if (length > git_oid_hexsize(type))
+ return oid_error_invalid("too long");
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ out->type = type;
+#endif
+ memset(out->id, 0, size);
+
+ for (p = 0; p < length; p++) {
+ v = git__fromhex(str[p]);
+ if (v < 0)
+ return oid_error_invalid("contains invalid characters");
+
+ out->id[p / 2] |= (unsigned char)(v << (p % 2 ? 0 : 4));
+ }
+
+ return 0;
+}
+
+int git_oid__fromstrp(git_oid *out, const char *str, git_oid_t type)
+{
+ return git_oid__fromstrn(out, str, strlen(str), type);
+}
+
+int git_oid__fromstr(git_oid *out, const char *str, git_oid_t type)
+{
+ return git_oid__fromstrn(out, str, git_oid_hexsize(type), type);
+}
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+int git_oid_fromstrn(
+ git_oid *out,
+ const char *str,
+ size_t length,
+ git_oid_t type)
+{
+ return git_oid__fromstrn(out, str, length, type);
+}
+
+int git_oid_fromstrp(git_oid *out, const char *str, git_oid_t type)
+{
+ return git_oid_fromstrn(out, str, strlen(str), type);
+}
+
+int git_oid_fromstr(git_oid *out, const char *str, git_oid_t type)
+{
+ return git_oid_fromstrn(out, str, git_oid_hexsize(type), type);
+}
+#else
+int git_oid_fromstrn(
+ git_oid *out,
+ const char *str,
+ size_t length)
+{
+ return git_oid__fromstrn(out, str, length, GIT_OID_SHA1);
+}
+
+int git_oid_fromstrp(git_oid *out, const char *str)
+{
+ return git_oid__fromstrn(out, str, strlen(str), GIT_OID_SHA1);
+}
+
+int git_oid_fromstr(git_oid *out, const char *str)
+{
+ return git_oid__fromstrn(out, str, GIT_OID_SHA1_HEXSIZE, GIT_OID_SHA1);
+}
+#endif
+
+int git_oid_nfmt(char *str, size_t n, const git_oid *oid)
+{
+ size_t hex_size;
+
+ if (!oid) {
+ memset(str, 0, n);
+ return 0;
+ }
+
+ if (!(hex_size = git_oid_hexsize(git_oid_type(oid))))
+ return oid_error_invalid("unknown type");
+
+ if (n > hex_size) {
+ memset(&str[hex_size], 0, n - hex_size);
+ n = hex_size;
+ }
+
+ git_oid_fmt_substr(str, oid, 0, n);
+ return 0;
+}
+
+int git_oid_fmt(char *str, const git_oid *oid)
+{
+ return git_oid_nfmt(str, git_oid_hexsize(git_oid_type(oid)), oid);
+}
+
+int git_oid_pathfmt(char *str, const git_oid *oid)
+{
+ size_t hex_size;
+
+ if (!(hex_size = git_oid_hexsize(git_oid_type(oid))))
+ return oid_error_invalid("unknown type");
+
+ git_oid_fmt_substr(str, oid, 0, 2);
+ str[2] = '/';
+ git_oid_fmt_substr(&str[3], oid, 2, (hex_size - 2));
+ return 0;
+}
+
+char *git_oid_tostr_s(const git_oid *oid)
+{
+ git_threadstate *threadstate = git_threadstate_get();
+ char *str;
+
+ if (!threadstate)
+ return NULL;
+
+ str = threadstate->oid_fmt;
+ git_oid_nfmt(str, git_oid_hexsize(git_oid_type(oid)) + 1, oid);
+ return str;
+}
+
+char *git_oid_allocfmt(const git_oid *oid)
+{
+ size_t hex_size = git_oid_hexsize(git_oid_type(oid));
+ char *str = git__malloc(hex_size + 1);
+
+ if (!hex_size || !str)
+ return NULL;
+
+ if (git_oid_nfmt(str, hex_size + 1, oid) < 0) {
+ git__free(str);
+ return NULL;
+ }
+
+ return str;
+}
+
+char *git_oid_tostr(char *out, size_t n, const git_oid *oid)
+{
+ size_t hex_size;
+
+ if (!out || n == 0)
+ return "";
+
+ hex_size = oid ? git_oid_hexsize(git_oid_type(oid)) : 0;
+
+ if (n > hex_size + 1)
+ n = hex_size + 1;
+
+ git_oid_nfmt(out, n - 1, oid); /* allow room for terminating NUL */
+ out[n - 1] = '\0';
+
+ return out;
+}
+
+int git_oid__fromraw(git_oid *out, const unsigned char *raw, git_oid_t type)
+{
+ size_t size;
+
+ if (!(size = git_oid_size(type)))
+ return oid_error_invalid("unknown type");
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ out->type = type;
+#endif
+ memcpy(out->id, raw, size);
+ return 0;
+}
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+int git_oid_fromraw(git_oid *out, const unsigned char *raw, git_oid_t type)
+{
+ return git_oid__fromraw(out, raw, type);
+}
+#else
+int git_oid_fromraw(git_oid *out, const unsigned char *raw)
+{
+ return git_oid__fromraw(out, raw, GIT_OID_SHA1);
+}
+#endif
+
+int git_oid_cpy(git_oid *out, const git_oid *src)
+{
+ size_t size;
+
+ if (!(size = git_oid_size(git_oid_type(src))))
+ return oid_error_invalid("unknown type");
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ out->type = src->type;
+#endif
+
+ return git_oid_raw_cpy(out->id, src->id, size);
+}
+
+int git_oid_cmp(const git_oid *a, const git_oid *b)
+{
+ return git_oid__cmp(a, b);
+}
+
+int git_oid_equal(const git_oid *a, const git_oid *b)
+{
+ return (git_oid__cmp(a, b) == 0);
+}
+
+int git_oid_ncmp(const git_oid *oid_a, const git_oid *oid_b, size_t len)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ if (oid_a->type != oid_b->type)
+ return oid_a->type - oid_b->type;
+#endif
+
+ return git_oid_raw_ncmp(oid_a->id, oid_b->id, len);
+}
+
+int git_oid_strcmp(const git_oid *oid_a, const char *str)
+{
+ const unsigned char *a;
+ unsigned char strval;
+ long size = (long)git_oid_size(git_oid_type(oid_a));
+ int hexval;
+
+ for (a = oid_a->id; *str && (a - oid_a->id) < size; ++a) {
+ if ((hexval = git__fromhex(*str++)) < 0)
+ return -1;
+ strval = (unsigned char)(hexval << 4);
+ if (*str) {
+ if ((hexval = git__fromhex(*str++)) < 0)
+ return -1;
+ strval |= hexval;
+ }
+ if (*a != strval)
+ return (*a - strval);
+ }
+
+ return 0;
+}
+
+int git_oid_streq(const git_oid *oid_a, const char *str)
+{
+ return git_oid_strcmp(oid_a, str) == 0 ? 0 : -1;
+}
+
+int git_oid_is_zero(const git_oid *oid_a)
+{
+ const unsigned char *a = oid_a->id;
+ size_t size = git_oid_size(git_oid_type(oid_a)), i;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ if (!oid_a->type)
+ return 1;
+ else if (!size)
+ return 0;
+#endif
+
+ for (i = 0; i < size; ++i, ++a)
+ if (*a != 0)
+ return 0;
+ return 1;
+}
+
+#ifndef GIT_DEPRECATE_HARD
+int git_oid_iszero(const git_oid *oid_a)
+{
+ return git_oid_is_zero(oid_a);
+}
+#endif
+
+typedef short node_index;
+
+typedef union {
+ const char *tail;
+ node_index children[16];
+} trie_node;
+
+struct git_oid_shorten {
+ trie_node *nodes;
+ size_t node_count, size;
+ int min_length, full;
+};
+
+static int resize_trie(git_oid_shorten *self, size_t new_size)
+{
+ self->nodes = git__reallocarray(self->nodes, new_size, sizeof(trie_node));
+ GIT_ERROR_CHECK_ALLOC(self->nodes);
+
+ if (new_size > self->size) {
+ memset(&self->nodes[self->size], 0x0, (new_size - self->size) * sizeof(trie_node));
+ }
+
+ self->size = new_size;
+ return 0;
+}
+
+static trie_node *push_leaf(git_oid_shorten *os, node_index idx, int push_at, const char *oid)
+{
+ trie_node *node, *leaf;
+ node_index idx_leaf;
+
+ if (os->node_count >= os->size) {
+ if (resize_trie(os, os->size * 2) < 0)
+ return NULL;
+ }
+
+ idx_leaf = (node_index)os->node_count++;
+
+ if (os->node_count == SHRT_MAX) {
+ os->full = 1;
+ return NULL;
+ }
+
+ node = &os->nodes[idx];
+ node->children[push_at] = -idx_leaf;
+
+ leaf = &os->nodes[idx_leaf];
+ leaf->tail = oid;
+
+ return node;
+}
+
+git_oid_shorten *git_oid_shorten_new(size_t min_length)
+{
+ git_oid_shorten *os;
+
+ GIT_ASSERT_ARG_WITH_RETVAL((size_t)((int)min_length) == min_length, NULL);
+
+ os = git__calloc(1, sizeof(git_oid_shorten));
+ if (os == NULL)
+ return NULL;
+
+ if (resize_trie(os, 16) < 0) {
+ git__free(os);
+ return NULL;
+ }
+
+ os->node_count = 1;
+ os->min_length = (int)min_length;
+
+ return os;
+}
+
+void git_oid_shorten_free(git_oid_shorten *os)
+{
+ if (os == NULL)
+ return;
+
+ git__free(os->nodes);
+ git__free(os);
+}
+
+
+/*
+ * What wizardry is this?
+ *
+ * This is just a memory-optimized trie: basically a very fancy
+ * 16-ary tree, which is used to store the prefixes of the OID
+ * strings.
+ *
+ * Read more: http://en.wikipedia.org/wiki/Trie
+ *
+ * Magic that happens in this method:
+ *
+ * - Each node in the trie is an union, so it can work both as
+ * a normal node, or as a leaf.
+ *
+ * - Each normal node points to 16 children (one for each possible
+ * character in the oid). This is *not* stored in an array of
+ * pointers, because in a 64-bit arch this would be sucking
+ * 16*sizeof(void*) = 128 bytes of memory per node, which is
+ * insane. What we do is store Node Indexes, and use these indexes
+ * to look up each node in the om->index array. These indexes are
+ * signed shorts, so this limits the amount of unique OIDs that
+ * fit in the structure to about 20000 (assuming a more or less uniform
+ * distribution).
+ *
+ * - All the nodes in om->index array are stored contiguously in
+ * memory, and each of them is 32 bytes, so we fit 2x nodes per
+ * cache line. Convenient for speed.
+ *
+ * - To differentiate the leafs from the normal nodes, we store all
+ * the indexes towards a leaf as a negative index (indexes to normal
+ * nodes are positives). When we find that one of the children for
+ * a node has a negative value, that means it's going to be a leaf.
+ * This reduces the amount of indexes we have by two, but also reduces
+ * the size of each node by 1-4 bytes (the amount we would need to
+ * add a `is_leaf` field): this is good because it allows the nodes
+ * to fit cleanly in cache lines.
+ *
+ * - Once we reach an empty children, instead of continuing to insert
+ * new nodes for each remaining character of the OID, we store a pointer
+ * to the tail in the leaf; if the leaf is reached again, we turn it
+ * into a normal node and use the tail to create a new leaf.
+ *
+ * This is a pretty good balance between performance and memory usage.
+ */
+int git_oid_shorten_add(git_oid_shorten *os, const char *text_oid)
+{
+ int i;
+ bool is_leaf;
+ node_index idx;
+
+ if (os->full) {
+ git_error_set(GIT_ERROR_INVALID, "unable to shorten OID - OID set full");
+ return -1;
+ }
+
+ if (text_oid == NULL)
+ return os->min_length;
+
+ idx = 0;
+ is_leaf = false;
+
+ for (i = 0; i < GIT_OID_SHA1_HEXSIZE; ++i) {
+ int c = git__fromhex(text_oid[i]);
+ trie_node *node;
+
+ if (c == -1) {
+ git_error_set(GIT_ERROR_INVALID, "unable to shorten OID - invalid hex value");
+ return -1;
+ }
+
+ node = &os->nodes[idx];
+
+ if (is_leaf) {
+ const char *tail;
+
+ tail = node->tail;
+ node->tail = NULL;
+
+ node = push_leaf(os, idx, git__fromhex(tail[0]), &tail[1]);
+ if (node == NULL) {
+ if (os->full)
+ git_error_set(GIT_ERROR_INVALID, "unable to shorten OID - OID set full");
+ return -1;
+ }
+ }
+
+ if (node->children[c] == 0) {
+ if (push_leaf(os, idx, c, &text_oid[i + 1]) == NULL) {
+ if (os->full)
+ git_error_set(GIT_ERROR_INVALID, "unable to shorten OID - OID set full");
+ return -1;
+ }
+ break;
+ }
+
+ idx = node->children[c];
+ is_leaf = false;
+
+ if (idx < 0) {
+ node->children[c] = idx = -idx;
+ is_leaf = true;
+ }
+ }
+
+ if (++i > os->min_length)
+ os->min_length = i;
+
+ return os->min_length;
+}
+
diff --git a/src/libgit2/oid.h b/src/libgit2/oid.h
new file mode 100644
index 0000000..7b6b09d
--- /dev/null
+++ b/src/libgit2/oid.h
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_oid_h__
+#define INCLUDE_oid_h__
+
+#include "common.h"
+
+#include "git2/experimental.h"
+#include "git2/oid.h"
+#include "hash.h"
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+# define GIT_OID_NONE { 0, { 0 } }
+# define GIT_OID_INIT(type, ...) { type, __VA_ARGS__ }
+#else
+# define GIT_OID_NONE { { 0 } }
+# define GIT_OID_INIT(type, ...) { __VA_ARGS__ }
+#endif
+
+extern const git_oid git_oid__empty_blob_sha1;
+extern const git_oid git_oid__empty_tree_sha1;
+
+GIT_INLINE(git_oid_t) git_oid_type(const git_oid *oid)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ return oid->type;
+#else
+ GIT_UNUSED(oid);
+ return GIT_OID_SHA1;
+#endif
+}
+
+GIT_INLINE(size_t) git_oid_size(git_oid_t type)
+{
+ switch (type) {
+ case GIT_OID_SHA1:
+ return GIT_OID_SHA1_SIZE;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ case GIT_OID_SHA256:
+ return GIT_OID_SHA256_SIZE;
+#endif
+
+ }
+
+ return 0;
+}
+
+GIT_INLINE(size_t) git_oid_hexsize(git_oid_t type)
+{
+ switch (type) {
+ case GIT_OID_SHA1:
+ return GIT_OID_SHA1_HEXSIZE;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ case GIT_OID_SHA256:
+ return GIT_OID_SHA256_HEXSIZE;
+#endif
+
+ }
+
+ return 0;
+}
+
+GIT_INLINE(const char *) git_oid_type_name(git_oid_t type)
+{
+ switch (type) {
+ case GIT_OID_SHA1:
+ return "sha1";
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ case GIT_OID_SHA256:
+ return "sha256";
+#endif
+ }
+
+ return "unknown";
+}
+
+GIT_INLINE(git_oid_t) git_oid_type_fromstr(const char *name)
+{
+ if (strcmp(name, "sha1") == 0)
+ return GIT_OID_SHA1;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ if (strcmp(name, "sha256") == 0)
+ return GIT_OID_SHA256;
+#endif
+
+ return 0;
+}
+
+GIT_INLINE(git_oid_t) git_oid_type_fromstrn(const char *name, size_t len)
+{
+ if (len == CONST_STRLEN("sha1") && strncmp(name, "sha1", len) == 0)
+ return GIT_OID_SHA1;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ if (len == CONST_STRLEN("sha256") && strncmp(name, "sha256", len) == 0)
+ return GIT_OID_SHA256;
+#endif
+
+ return 0;
+}
+
+GIT_INLINE(git_hash_algorithm_t) git_oid_algorithm(git_oid_t type)
+{
+ switch (type) {
+ case GIT_OID_SHA1:
+ return GIT_HASH_ALGORITHM_SHA1;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ case GIT_OID_SHA256:
+ return GIT_HASH_ALGORITHM_SHA256;
+#endif
+
+ }
+
+ return 0;
+}
+
+/**
+ * Format a git_oid into a newly allocated c-string.
+ *
+ * The c-string is owned by the caller and needs to be manually freed.
+ *
+ * @param id the oid structure to format
+ * @return the c-string; NULL if memory is exhausted. Caller must
+ * deallocate the string with git__free().
+ */
+char *git_oid_allocfmt(const git_oid *id);
+
+/**
+ * Format the requested nibbles of an object id.
+ *
+ * @param str the string to write into
+ * @param oid the oid structure to format
+ * @param start the starting number of nibbles
+ * @param count the number of nibbles to format
+ */
+GIT_INLINE(void) git_oid_fmt_substr(
+ char *str,
+ const git_oid *oid,
+ size_t start,
+ size_t count)
+{
+ static char hex[] = "0123456789abcdef";
+ size_t i, end = start + count, min = start / 2, max = end / 2;
+
+ if (start & 1)
+ *str++ = hex[oid->id[min++] & 0x0f];
+
+ for (i = min; i < max; i++) {
+ *str++ = hex[oid->id[i] >> 4];
+ *str++ = hex[oid->id[i] & 0x0f];
+ }
+
+ if (end & 1)
+ *str++ = hex[oid->id[i] >> 4];
+}
+
+GIT_INLINE(int) git_oid_raw_ncmp(
+ const unsigned char *sha1,
+ const unsigned char *sha2,
+ size_t len)
+{
+ if (len > GIT_OID_MAX_HEXSIZE)
+ len = GIT_OID_MAX_HEXSIZE;
+
+ while (len > 1) {
+ if (*sha1 != *sha2)
+ return 1;
+ sha1++;
+ sha2++;
+ len -= 2;
+ };
+
+ if (len)
+ if ((*sha1 ^ *sha2) & 0xf0)
+ return 1;
+
+ return 0;
+}
+
+GIT_INLINE(int) git_oid_raw_cmp(
+ const unsigned char *sha1,
+ const unsigned char *sha2,
+ size_t size)
+{
+ return memcmp(sha1, sha2, size);
+}
+
+GIT_INLINE(int) git_oid_raw_cpy(
+ unsigned char *dst,
+ const unsigned char *src,
+ size_t size)
+{
+ memcpy(dst, src, size);
+ return 0;
+}
+
+/*
+ * Compare two oid structures.
+ *
+ * @param a first oid structure.
+ * @param b second oid structure.
+ * @return <0, 0, >0 if a < b, a == b, a > b.
+ */
+GIT_INLINE(int) git_oid__cmp(const git_oid *a, const git_oid *b)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ if (a->type != b->type)
+ return a->type - b->type;
+
+ return git_oid_raw_cmp(a->id, b->id, git_oid_size(a->type));
+#else
+ return git_oid_raw_cmp(a->id, b->id, git_oid_size(GIT_OID_SHA1));
+#endif
+}
+
+GIT_INLINE(void) git_oid__cpy_prefix(
+ git_oid *out, const git_oid *id, size_t len)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ out->type = id->type;
+#endif
+
+ memcpy(&out->id, id->id, (len + 1) / 2);
+
+ if (len & 1)
+ out->id[len / 2] &= 0xF0;
+}
+
+GIT_INLINE(bool) git_oid__is_hexstr(const char *str, git_oid_t type)
+{
+ size_t i;
+
+ for (i = 0; str[i] != '\0'; i++) {
+ if (git__fromhex(str[i]) < 0)
+ return false;
+ }
+
+ return (i == git_oid_hexsize(type));
+}
+
+GIT_INLINE(void) git_oid_clear(git_oid *out, git_oid_t type)
+{
+ memset(out->id, 0, git_oid_size(type));
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ out->type = type;
+#endif
+}
+
+/* SHA256 support */
+
+int git_oid__fromstr(git_oid *out, const char *str, git_oid_t type);
+
+int git_oid__fromstrp(git_oid *out, const char *str, git_oid_t type);
+
+int git_oid__fromstrn(
+ git_oid *out,
+ const char *str,
+ size_t length,
+ git_oid_t type);
+
+int git_oid__fromraw(git_oid *out, const unsigned char *raw, git_oid_t type);
+
+#endif
diff --git a/src/libgit2/oidarray.c b/src/libgit2/oidarray.c
new file mode 100644
index 0000000..37f6775
--- /dev/null
+++ b/src/libgit2/oidarray.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "oidarray.h"
+
+#include "git2/oidarray.h"
+#include "array.h"
+
+void git_oidarray_dispose(git_oidarray *arr)
+{
+ git__free(arr->ids);
+}
+
+void git_oidarray__from_array(git_oidarray *out, const git_array_oid_t *array)
+{
+ out->count = array->size;
+ out->ids = array->ptr;
+}
+
+void git_oidarray__to_array(git_array_oid_t *out, const git_oidarray *array)
+{
+ out->ptr = array->ids;
+ out->size = array->count;
+ out->asize = array->count;
+}
+
+void git_oidarray__reverse(git_oidarray *arr)
+{
+ size_t i;
+ git_oid tmp;
+
+ for (i = 0; i < arr->count / 2; i++) {
+ git_oid_cpy(&tmp, &arr->ids[i]);
+ git_oid_cpy(&arr->ids[i], &arr->ids[(arr->count-1)-i]);
+ git_oid_cpy(&arr->ids[(arr->count-1)-i], &tmp);
+ }
+}
+
+int git_oidarray__add(git_array_oid_t *arr, git_oid *id)
+{
+ git_oid *add, *iter;
+ size_t i;
+
+ git_array_foreach(*arr, i, iter) {
+ if (git_oid_cmp(iter, id) == 0)
+ return 0;
+ }
+
+ if ((add = git_array_alloc(*arr)) == NULL)
+ return -1;
+
+ git_oid_cpy(add, id);
+ return 0;
+}
+
+bool git_oidarray__remove(git_array_oid_t *arr, git_oid *id)
+{
+ bool found = false;
+ size_t remain, i;
+ git_oid *iter;
+
+ git_array_foreach(*arr, i, iter) {
+ if (git_oid_cmp(iter, id) == 0) {
+ arr->size--;
+ remain = arr->size - i;
+
+ if (remain > 0)
+ memmove(&arr->ptr[i], &arr->ptr[i+1], remain * sizeof(git_oid));
+
+ found = true;
+ break;
+ }
+ }
+
+ return found;
+}
+
+#ifndef GIT_DEPRECATE_HARD
+
+void git_oidarray_free(git_oidarray *arr)
+{
+ git_oidarray_dispose(arr);
+}
+
+#endif
diff --git a/src/libgit2/oidarray.h b/src/libgit2/oidarray.h
new file mode 100644
index 0000000..8f1543a
--- /dev/null
+++ b/src/libgit2/oidarray.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_oidarray_h__
+#define INCLUDE_oidarray_h__
+
+#include "common.h"
+
+#include "git2/oidarray.h"
+#include "array.h"
+
+typedef git_array_t(git_oid) git_array_oid_t;
+
+extern void git_oidarray__reverse(git_oidarray *arr);
+extern void git_oidarray__from_array(git_oidarray *out, const git_array_oid_t *array);
+extern void git_oidarray__to_array(git_array_oid_t *out, const git_oidarray *array);
+
+int git_oidarray__add(git_array_oid_t *arr, git_oid *id);
+bool git_oidarray__remove(git_array_oid_t *arr, git_oid *id);
+
+#endif
diff --git a/src/libgit2/oidmap.c b/src/libgit2/oidmap.c
new file mode 100644
index 0000000..eaf9fa0
--- /dev/null
+++ b/src/libgit2/oidmap.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "oidmap.h"
+
+#define kmalloc git__malloc
+#define kcalloc git__calloc
+#define krealloc git__realloc
+#define kreallocarray git__reallocarray
+#define kfree git__free
+#include "khash.h"
+
+__KHASH_TYPE(oid, const git_oid *, void *)
+
+GIT_INLINE(khint_t) git_oidmap_hash(const git_oid *oid)
+{
+ khint_t h;
+ memcpy(&h, oid->id, sizeof(khint_t));
+ return h;
+}
+
+__KHASH_IMPL(oid, static kh_inline, const git_oid *, void *, 1, git_oidmap_hash, git_oid_equal)
+
+int git_oidmap_new(git_oidmap **out)
+{
+ *out = kh_init(oid);
+ GIT_ERROR_CHECK_ALLOC(*out);
+
+ return 0;
+}
+
+void git_oidmap_free(git_oidmap *map)
+{
+ kh_destroy(oid, map);
+}
+
+void git_oidmap_clear(git_oidmap *map)
+{
+ kh_clear(oid, map);
+}
+
+size_t git_oidmap_size(git_oidmap *map)
+{
+ return kh_size(map);
+}
+
+void *git_oidmap_get(git_oidmap *map, const git_oid *key)
+{
+ size_t idx = kh_get(oid, map, key);
+ if (idx == kh_end(map) || !kh_exist(map, idx))
+ return NULL;
+ return kh_val(map, idx);
+}
+
+int git_oidmap_set(git_oidmap *map, const git_oid *key, void *value)
+{
+ size_t idx;
+ int rval;
+
+ idx = kh_put(oid, map, key, &rval);
+ if (rval < 0)
+ return -1;
+
+ if (rval == 0)
+ kh_key(map, idx) = key;
+
+ kh_val(map, idx) = value;
+
+ return 0;
+}
+
+int git_oidmap_delete(git_oidmap *map, const git_oid *key)
+{
+ khiter_t idx = kh_get(oid, map, key);
+ if (idx == kh_end(map))
+ return GIT_ENOTFOUND;
+ kh_del(oid, map, idx);
+ return 0;
+}
+
+int git_oidmap_exists(git_oidmap *map, const git_oid *key)
+{
+ return kh_get(oid, map, key) != kh_end(map);
+}
+
+int git_oidmap_iterate(void **value, git_oidmap *map, size_t *iter, const git_oid **key)
+{
+ size_t i = *iter;
+
+ while (i < map->n_buckets && !kh_exist(map, i))
+ i++;
+
+ if (i >= map->n_buckets)
+ return GIT_ITEROVER;
+
+ if (key)
+ *key = kh_key(map, i);
+ if (value)
+ *value = kh_value(map, i);
+ *iter = ++i;
+
+ return 0;
+}
diff --git a/src/libgit2/oidmap.h b/src/libgit2/oidmap.h
new file mode 100644
index 0000000..b748f72
--- /dev/null
+++ b/src/libgit2/oidmap.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_oidmap_h__
+#define INCLUDE_oidmap_h__
+
+#include "common.h"
+
+#include "git2/oid.h"
+
+/** A map with `git_oid`s as key. */
+typedef struct kh_oid_s git_oidmap;
+
+/**
+ * Allocate a new OID map.
+ *
+ * @param out Pointer to the map that shall be allocated.
+ * @return 0 on success, an error code if allocation has failed.
+ */
+int git_oidmap_new(git_oidmap **out);
+
+/**
+ * Free memory associated with the map.
+ *
+ * Note that this function will _not_ free values added to this
+ * map.
+ *
+ * @param map Pointer to the map that is to be free'd. May be
+ * `NULL`.
+ */
+void git_oidmap_free(git_oidmap *map);
+
+/**
+ * Clear all entries from the map.
+ *
+ * This function will remove all entries from the associated map.
+ * Memory associated with it will not be released, though.
+ *
+ * @param map Pointer to the map that shall be cleared. May be
+ * `NULL`.
+ */
+void git_oidmap_clear(git_oidmap *map);
+
+/**
+ * Return the number of elements in the map.
+ *
+ * @parameter map map containing the elements
+ * @return number of elements in the map
+ */
+size_t git_oidmap_size(git_oidmap *map);
+
+/**
+ * Return value associated with the given key.
+ *
+ * @param map map to search key in
+ * @param key key to search for
+ * @return value associated with the given key or NULL if the key was not found
+ */
+void *git_oidmap_get(git_oidmap *map, const git_oid *key);
+
+/**
+ * Set the entry for key to value.
+ *
+ * If the map has no corresponding entry for the given key, a new
+ * entry will be created with the given value. If an entry exists
+ * already, its value will be updated to match the given value.
+ *
+ * @param map map to create new entry in
+ * @param key key to set
+ * @param value value to associate the key with; may be NULL
+ * @return zero if the key was successfully set, a negative error
+ * code otherwise
+ */
+int git_oidmap_set(git_oidmap *map, const git_oid *key, void *value);
+
+/**
+ * Delete an entry from the map.
+ *
+ * Delete the given key and its value from the map. If no such
+ * key exists, this will do nothing.
+ *
+ * @param map map to delete key in
+ * @param key key to delete
+ * @return `0` if the key has been deleted, GIT_ENOTFOUND if no
+ * such key was found, a negative code in case of an
+ * error
+ */
+int git_oidmap_delete(git_oidmap *map, const git_oid *key);
+
+/**
+ * Check whether a key exists in the given map.
+ *
+ * @param map map to query for the key
+ * @param key key to search for
+ * @return 0 if the key has not been found, 1 otherwise
+ */
+int git_oidmap_exists(git_oidmap *map, const git_oid *key);
+
+/**
+ * Iterate over entries of the map.
+ *
+ * This functions allows to iterate over all key-value entries of
+ * the map. The current position is stored in the `iter` variable
+ * and should be initialized to `0` before the first call to this
+ * function.
+ *
+ * @param map map to iterate over
+ * @param value pointer to the variable where to store the current
+ * value. May be NULL.
+ * @param iter iterator storing the current position. Initialize
+ * with zero previous to the first call.
+ * @param key pointer to the variable where to store the current
+ * key. May be NULL.
+ * @return `0` if the next entry was correctly retrieved.
+ * GIT_ITEROVER if no entries are left. A negative error
+ * code otherwise.
+ */
+int git_oidmap_iterate(void **value, git_oidmap *map, size_t *iter, const git_oid **key);
+
+#define git_oidmap_foreach_value(h, vvar, code) { size_t __i = 0; \
+ while (git_oidmap_iterate((void **) &(vvar), h, &__i, NULL) == 0) { \
+ code; \
+ } }
+
+#endif
diff --git a/src/libgit2/pack-objects.c b/src/libgit2/pack-objects.c
new file mode 100644
index 0000000..b2d80cb
--- /dev/null
+++ b/src/libgit2/pack-objects.c
@@ -0,0 +1,1839 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "pack-objects.h"
+
+#include "buf.h"
+#include "zstream.h"
+#include "delta.h"
+#include "iterator.h"
+#include "pack.h"
+#include "thread.h"
+#include "tree.h"
+#include "util.h"
+#include "revwalk.h"
+#include "commit_list.h"
+
+#include "git2/pack.h"
+#include "git2/commit.h"
+#include "git2/tag.h"
+#include "git2/indexer.h"
+#include "git2/config.h"
+
+struct unpacked {
+ git_pobject *object;
+ void *data;
+ struct git_delta_index *index;
+ size_t depth;
+};
+
+struct tree_walk_context {
+ git_packbuilder *pb;
+ git_str buf;
+};
+
+struct pack_write_context {
+ git_indexer *indexer;
+ git_indexer_progress *stats;
+};
+
+struct walk_object {
+ git_oid id;
+ unsigned int uninteresting:1,
+ seen:1;
+};
+
+#ifdef GIT_THREADS
+# define GIT_PACKBUILDER__MUTEX_OP(pb, mtx, op) git_mutex_##op(&(pb)->mtx)
+#else
+# define GIT_PACKBUILDER__MUTEX_OP(pb, mtx, op) git__noop()
+#endif
+
+#define git_packbuilder__cache_lock(pb) GIT_PACKBUILDER__MUTEX_OP(pb, cache_mutex, lock)
+#define git_packbuilder__cache_unlock(pb) GIT_PACKBUILDER__MUTEX_OP(pb, cache_mutex, unlock)
+#define git_packbuilder__progress_lock(pb) GIT_PACKBUILDER__MUTEX_OP(pb, progress_mutex, lock)
+#define git_packbuilder__progress_unlock(pb) GIT_PACKBUILDER__MUTEX_OP(pb, progress_mutex, unlock)
+
+/* The minimal interval between progress updates (in seconds). */
+#define MIN_PROGRESS_UPDATE_INTERVAL 0.5
+
+/* Size of the buffer to feed to zlib */
+#define COMPRESS_BUFLEN (1024 * 1024)
+
+static unsigned name_hash(const char *name)
+{
+ unsigned c, hash = 0;
+
+ if (!name)
+ return 0;
+
+ /*
+ * This effectively just creates a sortable number from the
+ * last sixteen non-whitespace characters. Last characters
+ * count "most", so things that end in ".c" sort together.
+ */
+ while ((c = *name++) != 0) {
+ if (git__isspace(c))
+ continue;
+ hash = (hash >> 2) + (c << 24);
+ }
+ return hash;
+}
+
+static int packbuilder_config(git_packbuilder *pb)
+{
+ git_config *config;
+ int ret = 0;
+ int64_t val;
+
+ if ((ret = git_repository_config_snapshot(&config, pb->repo)) < 0)
+ return ret;
+
+#define config_get(KEY,DST,DFLT) do { \
+ ret = git_config_get_int64(&val, config, KEY); \
+ if (!ret) { \
+ if (!git__is_sizet(val)) { \
+ git_error_set(GIT_ERROR_CONFIG, \
+ "configuration value '%s' is too large", KEY); \
+ ret = -1; \
+ goto out; \
+ } \
+ (DST) = (size_t)val; \
+ } else if (ret == GIT_ENOTFOUND) { \
+ (DST) = (DFLT); \
+ ret = 0; \
+ } else if (ret < 0) goto out; } while (0)
+
+ config_get("pack.deltaCacheSize", pb->max_delta_cache_size,
+ GIT_PACK_DELTA_CACHE_SIZE);
+ config_get("pack.deltaCacheLimit", pb->cache_max_small_delta_size,
+ GIT_PACK_DELTA_CACHE_LIMIT);
+ config_get("pack.deltaCacheSize", pb->big_file_threshold,
+ GIT_PACK_BIG_FILE_THRESHOLD);
+ config_get("pack.windowMemory", pb->window_memory_limit, 0);
+
+#undef config_get
+
+out:
+ git_config_free(config);
+
+ return ret;
+}
+
+int git_packbuilder_new(git_packbuilder **out, git_repository *repo)
+{
+ git_hash_algorithm_t hash_algorithm;
+ git_packbuilder *pb;
+
+ *out = NULL;
+
+ pb = git__calloc(1, sizeof(*pb));
+ GIT_ERROR_CHECK_ALLOC(pb);
+
+ pb->oid_type = repo->oid_type;
+
+ hash_algorithm = git_oid_algorithm(pb->oid_type);
+ GIT_ASSERT(hash_algorithm);
+
+ if (git_oidmap_new(&pb->object_ix) < 0 ||
+ git_oidmap_new(&pb->walk_objects) < 0 ||
+ git_pool_init(&pb->object_pool, sizeof(struct walk_object)) < 0)
+ goto on_error;
+
+ pb->repo = repo;
+ pb->nr_threads = 1; /* do not spawn any thread by default */
+
+ if (git_hash_ctx_init(&pb->ctx, hash_algorithm) < 0 ||
+ git_zstream_init(&pb->zstream, GIT_ZSTREAM_DEFLATE) < 0 ||
+ git_repository_odb(&pb->odb, repo) < 0 ||
+ packbuilder_config(pb) < 0)
+ goto on_error;
+
+#ifdef GIT_THREADS
+
+ if (git_mutex_init(&pb->cache_mutex) ||
+ git_mutex_init(&pb->progress_mutex) ||
+ git_cond_init(&pb->progress_cond))
+ {
+ git_error_set(GIT_ERROR_OS, "failed to initialize packbuilder mutex");
+ goto on_error;
+ }
+
+#endif
+
+ *out = pb;
+ return 0;
+
+on_error:
+ git_packbuilder_free(pb);
+ return -1;
+}
+
+unsigned int git_packbuilder_set_threads(git_packbuilder *pb, unsigned int n)
+{
+ GIT_ASSERT_ARG(pb);
+
+#ifdef GIT_THREADS
+ pb->nr_threads = n;
+#else
+ GIT_UNUSED(n);
+ GIT_ASSERT(pb->nr_threads == 1);
+#endif
+
+ return pb->nr_threads;
+}
+
+static int rehash(git_packbuilder *pb)
+{
+ git_pobject *po;
+ size_t i;
+
+ git_oidmap_clear(pb->object_ix);
+
+ for (i = 0, po = pb->object_list; i < pb->nr_objects; i++, po++) {
+ if (git_oidmap_set(pb->object_ix, &po->id, po) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+int git_packbuilder_insert(git_packbuilder *pb, const git_oid *oid,
+ const char *name)
+{
+ git_pobject *po;
+ size_t newsize;
+ int ret;
+
+ GIT_ASSERT_ARG(pb);
+ GIT_ASSERT_ARG(oid);
+
+ /* If the object already exists in the hash table, then we don't
+ * have any work to do */
+ if (git_oidmap_exists(pb->object_ix, oid))
+ return 0;
+
+ if (pb->nr_objects >= pb->nr_alloc) {
+ GIT_ERROR_CHECK_ALLOC_ADD(&newsize, pb->nr_alloc, 1024);
+ GIT_ERROR_CHECK_ALLOC_MULTIPLY(&newsize, newsize / 2, 3);
+
+ if (!git__is_uint32(newsize)) {
+ git_error_set(GIT_ERROR_NOMEMORY, "packfile too large to fit in memory.");
+ return -1;
+ }
+
+ pb->nr_alloc = newsize;
+
+ pb->object_list = git__reallocarray(pb->object_list,
+ pb->nr_alloc, sizeof(*po));
+ GIT_ERROR_CHECK_ALLOC(pb->object_list);
+
+ if (rehash(pb) < 0)
+ return -1;
+ }
+
+ po = pb->object_list + pb->nr_objects;
+ memset(po, 0x0, sizeof(*po));
+
+ if ((ret = git_odb_read_header(&po->size, &po->type, pb->odb, oid)) < 0)
+ return ret;
+
+ pb->nr_objects++;
+ git_oid_cpy(&po->id, oid);
+ po->hash = name_hash(name);
+
+ if (git_oidmap_set(pb->object_ix, &po->id, po) < 0) {
+ git_error_set_oom();
+ return -1;
+ }
+
+ pb->done = false;
+
+ if (pb->progress_cb) {
+ uint64_t current_time = git_time_monotonic();
+ uint64_t elapsed = current_time - pb->last_progress_report_time;
+
+ if (elapsed >= MIN_PROGRESS_UPDATE_INTERVAL) {
+ pb->last_progress_report_time = current_time;
+
+ ret = pb->progress_cb(
+ GIT_PACKBUILDER_ADDING_OBJECTS,
+ pb->nr_objects, 0, pb->progress_cb_payload);
+
+ if (ret)
+ return git_error_set_after_callback(ret);
+ }
+ }
+
+ return 0;
+}
+
+static int get_delta(void **out, git_odb *odb, git_pobject *po)
+{
+ git_odb_object *src = NULL, *trg = NULL;
+ size_t delta_size;
+ void *delta_buf;
+ int error;
+
+ *out = NULL;
+
+ if (git_odb_read(&src, odb, &po->delta->id) < 0 ||
+ git_odb_read(&trg, odb, &po->id) < 0)
+ goto on_error;
+
+ error = git_delta(&delta_buf, &delta_size,
+ git_odb_object_data(src), git_odb_object_size(src),
+ git_odb_object_data(trg), git_odb_object_size(trg),
+ 0);
+
+ if (error < 0 && error != GIT_EBUFS)
+ goto on_error;
+
+ if (error == GIT_EBUFS || delta_size != po->delta_size) {
+ git_error_set(GIT_ERROR_INVALID, "delta size changed");
+ goto on_error;
+ }
+
+ *out = delta_buf;
+
+ git_odb_object_free(src);
+ git_odb_object_free(trg);
+ return 0;
+
+on_error:
+ git_odb_object_free(src);
+ git_odb_object_free(trg);
+ return -1;
+}
+
+static int write_object(
+ git_packbuilder *pb,
+ git_pobject *po,
+ int (*write_cb)(void *buf, size_t size, void *cb_data),
+ void *cb_data)
+{
+ git_odb_object *obj = NULL;
+ git_object_t type;
+ unsigned char hdr[10], *zbuf = NULL;
+ void *data = NULL;
+ size_t hdr_len, zbuf_len = COMPRESS_BUFLEN, data_len, oid_size;
+ int error;
+
+ oid_size = git_oid_size(pb->oid_type);
+
+ /*
+ * If we have a delta base, let's use the delta to save space.
+ * Otherwise load the whole object. 'data' ends up pointing to
+ * whatever data we want to put into the packfile.
+ */
+ if (po->delta) {
+ if (po->delta_data)
+ data = po->delta_data;
+ else if ((error = get_delta(&data, pb->odb, po)) < 0)
+ goto done;
+
+ data_len = po->delta_size;
+ type = GIT_OBJECT_REF_DELTA;
+ } else {
+ if ((error = git_odb_read(&obj, pb->odb, &po->id)) < 0)
+ goto done;
+
+ data = (void *)git_odb_object_data(obj);
+ data_len = git_odb_object_size(obj);
+ type = git_odb_object_type(obj);
+ }
+
+ /* Write header */
+ if ((error = git_packfile__object_header(&hdr_len, hdr, data_len, type)) < 0 ||
+ (error = write_cb(hdr, hdr_len, cb_data)) < 0 ||
+ (error = git_hash_update(&pb->ctx, hdr, hdr_len)) < 0)
+ goto done;
+
+ if (type == GIT_OBJECT_REF_DELTA) {
+ if ((error = write_cb(po->delta->id.id, oid_size, cb_data)) < 0 ||
+ (error = git_hash_update(&pb->ctx, po->delta->id.id, oid_size)) < 0)
+ goto done;
+ }
+
+ /* Write data */
+ if (po->z_delta_size) {
+ data_len = po->z_delta_size;
+
+ if ((error = write_cb(data, data_len, cb_data)) < 0 ||
+ (error = git_hash_update(&pb->ctx, data, data_len)) < 0)
+ goto done;
+ } else {
+ zbuf = git__malloc(zbuf_len);
+ GIT_ERROR_CHECK_ALLOC(zbuf);
+
+ git_zstream_reset(&pb->zstream);
+
+ if ((error = git_zstream_set_input(&pb->zstream, data, data_len)) < 0)
+ goto done;
+
+ while (!git_zstream_done(&pb->zstream)) {
+ if ((error = git_zstream_get_output(zbuf, &zbuf_len, &pb->zstream)) < 0 ||
+ (error = write_cb(zbuf, zbuf_len, cb_data)) < 0 ||
+ (error = git_hash_update(&pb->ctx, zbuf, zbuf_len)) < 0)
+ goto done;
+
+ zbuf_len = COMPRESS_BUFLEN; /* reuse buffer */
+ }
+ }
+
+ /*
+ * If po->delta is true, data is a delta and it is our
+ * responsibility to free it (otherwise it's a git_object's
+ * data). We set po->delta_data to NULL in case we got the
+ * data from there instead of get_delta(). If we didn't,
+ * there's no harm.
+ */
+ if (po->delta) {
+ git__free(data);
+ po->delta_data = NULL;
+ }
+
+ pb->nr_written++;
+
+done:
+ git__free(zbuf);
+ git_odb_object_free(obj);
+ return error;
+}
+
+enum write_one_status {
+ WRITE_ONE_SKIP = -1, /* already written */
+ WRITE_ONE_BREAK = 0, /* writing this will bust the limit; not written */
+ WRITE_ONE_WRITTEN = 1, /* normal */
+ WRITE_ONE_RECURSIVE = 2 /* already scheduled to be written */
+};
+
+static int write_one(
+ enum write_one_status *status,
+ git_packbuilder *pb,
+ git_pobject *po,
+ int (*write_cb)(void *buf, size_t size, void *cb_data),
+ void *cb_data)
+{
+ int error;
+
+ if (po->recursing) {
+ *status = WRITE_ONE_RECURSIVE;
+ return 0;
+ } else if (po->written) {
+ *status = WRITE_ONE_SKIP;
+ return 0;
+ }
+
+ if (po->delta) {
+ po->recursing = 1;
+
+ if ((error = write_one(status, pb, po->delta, write_cb, cb_data)) < 0)
+ return error;
+
+ /* we cannot depend on this one */
+ if (*status == WRITE_ONE_RECURSIVE)
+ po->delta = NULL;
+ }
+
+ *status = WRITE_ONE_WRITTEN;
+ po->written = 1;
+ po->recursing = 0;
+
+ return write_object(pb, po, write_cb, cb_data);
+}
+
+GIT_INLINE(void) add_to_write_order(git_pobject **wo, size_t *endp,
+ git_pobject *po)
+{
+ if (po->filled)
+ return;
+ wo[(*endp)++] = po;
+ po->filled = 1;
+}
+
+static void add_descendants_to_write_order(git_pobject **wo, size_t *endp,
+ git_pobject *po)
+{
+ int add_to_order = 1;
+ while (po) {
+ if (add_to_order) {
+ git_pobject *s;
+ /* add this node... */
+ add_to_write_order(wo, endp, po);
+ /* all its siblings... */
+ for (s = po->delta_sibling; s; s = s->delta_sibling) {
+ add_to_write_order(wo, endp, s);
+ }
+ }
+ /* drop down a level to add left subtree nodes if possible */
+ if (po->delta_child) {
+ add_to_order = 1;
+ po = po->delta_child;
+ } else {
+ add_to_order = 0;
+ /* our sibling might have some children, it is next */
+ if (po->delta_sibling) {
+ po = po->delta_sibling;
+ continue;
+ }
+ /* go back to our parent node */
+ po = po->delta;
+ while (po && !po->delta_sibling) {
+ /* we're on the right side of a subtree, keep
+ * going up until we can go right again */
+ po = po->delta;
+ }
+ if (!po) {
+ /* done- we hit our original root node */
+ return;
+ }
+ /* pass it off to sibling at this level */
+ po = po->delta_sibling;
+ }
+ };
+}
+
+static void add_family_to_write_order(git_pobject **wo, size_t *endp,
+ git_pobject *po)
+{
+ git_pobject *root;
+
+ for (root = po; root->delta; root = root->delta)
+ ; /* nothing */
+ add_descendants_to_write_order(wo, endp, root);
+}
+
+static int cb_tag_foreach(const char *name, git_oid *oid, void *data)
+{
+ git_packbuilder *pb = data;
+ git_pobject *po;
+
+ GIT_UNUSED(name);
+
+ if ((po = git_oidmap_get(pb->object_ix, oid)) == NULL)
+ return 0;
+
+ po->tagged = 1;
+
+ /* TODO: peel objects */
+
+ return 0;
+}
+
+static int compute_write_order(git_pobject ***out, git_packbuilder *pb)
+{
+ size_t i, wo_end, last_untagged;
+ git_pobject **wo;
+
+ *out = NULL;
+
+ if (!pb->nr_objects)
+ return 0;
+
+ if ((wo = git__mallocarray(pb->nr_objects, sizeof(*wo))) == NULL)
+ return -1;
+
+ for (i = 0; i < pb->nr_objects; i++) {
+ git_pobject *po = pb->object_list + i;
+ po->tagged = 0;
+ po->filled = 0;
+ po->delta_child = NULL;
+ po->delta_sibling = NULL;
+ }
+
+ /*
+ * Fully connect delta_child/delta_sibling network.
+ * Make sure delta_sibling is sorted in the original
+ * recency order.
+ */
+ for (i = pb->nr_objects; i > 0;) {
+ git_pobject *po = &pb->object_list[--i];
+ if (!po->delta)
+ continue;
+ /* Mark me as the first child */
+ po->delta_sibling = po->delta->delta_child;
+ po->delta->delta_child = po;
+ }
+
+ /*
+ * Mark objects that are at the tip of tags.
+ */
+ if (git_tag_foreach(pb->repo, &cb_tag_foreach, pb) < 0) {
+ git__free(wo);
+ return -1;
+ }
+
+ /*
+ * Give the objects in the original recency order until
+ * we see a tagged tip.
+ */
+ for (i = wo_end = 0; i < pb->nr_objects; i++) {
+ git_pobject *po = pb->object_list + i;
+ if (po->tagged)
+ break;
+ add_to_write_order(wo, &wo_end, po);
+ }
+ last_untagged = i;
+
+ /*
+ * Then fill all the tagged tips.
+ */
+ for (; i < pb->nr_objects; i++) {
+ git_pobject *po = pb->object_list + i;
+ if (po->tagged)
+ add_to_write_order(wo, &wo_end, po);
+ }
+
+ /*
+ * And then all remaining commits and tags.
+ */
+ for (i = last_untagged; i < pb->nr_objects; i++) {
+ git_pobject *po = pb->object_list + i;
+ if (po->type != GIT_OBJECT_COMMIT &&
+ po->type != GIT_OBJECT_TAG)
+ continue;
+ add_to_write_order(wo, &wo_end, po);
+ }
+
+ /*
+ * And then all the trees.
+ */
+ for (i = last_untagged; i < pb->nr_objects; i++) {
+ git_pobject *po = pb->object_list + i;
+ if (po->type != GIT_OBJECT_TREE)
+ continue;
+ add_to_write_order(wo, &wo_end, po);
+ }
+
+ /*
+ * Finally all the rest in really tight order
+ */
+ for (i = last_untagged; i < pb->nr_objects; i++) {
+ git_pobject *po = pb->object_list + i;
+ if (!po->filled)
+ add_family_to_write_order(wo, &wo_end, po);
+ }
+
+ if (wo_end != pb->nr_objects) {
+ git__free(wo);
+ git_error_set(GIT_ERROR_INVALID, "invalid write order");
+ return -1;
+ }
+
+ *out = wo;
+ return 0;
+}
+
+static int write_pack(git_packbuilder *pb,
+ int (*write_cb)(void *buf, size_t size, void *cb_data),
+ void *cb_data)
+{
+ git_pobject **write_order;
+ git_pobject *po;
+ enum write_one_status status;
+ struct git_pack_header ph;
+ git_oid entry_oid;
+ size_t i = 0;
+ int error;
+
+ if ((error = compute_write_order(&write_order, pb)) < 0)
+ return error;
+
+ if (!git__is_uint32(pb->nr_objects)) {
+ git_error_set(GIT_ERROR_INVALID, "too many objects");
+ error = -1;
+ goto done;
+ }
+
+ /* Write pack header */
+ ph.hdr_signature = htonl(PACK_SIGNATURE);
+ ph.hdr_version = htonl(PACK_VERSION);
+ ph.hdr_entries = htonl(pb->nr_objects);
+
+ if ((error = write_cb(&ph, sizeof(ph), cb_data)) < 0 ||
+ (error = git_hash_update(&pb->ctx, &ph, sizeof(ph))) < 0)
+ goto done;
+
+ pb->nr_remaining = pb->nr_objects;
+ do {
+ pb->nr_written = 0;
+ for ( ; i < pb->nr_objects; ++i) {
+ po = write_order[i];
+
+ if ((error = write_one(&status, pb, po, write_cb, cb_data)) < 0)
+ goto done;
+ }
+
+ pb->nr_remaining -= pb->nr_written;
+ } while (pb->nr_remaining && i < pb->nr_objects);
+
+ if ((error = git_hash_final(entry_oid.id, &pb->ctx)) < 0)
+ goto done;
+
+ error = write_cb(entry_oid.id, git_oid_size(pb->oid_type), cb_data);
+
+done:
+ /* if callback cancelled writing, we must still free delta_data */
+ for ( ; i < pb->nr_objects; ++i) {
+ po = write_order[i];
+ if (po->delta_data) {
+ git__free(po->delta_data);
+ po->delta_data = NULL;
+ }
+ }
+
+ git__free(write_order);
+ return error;
+}
+
+static int write_pack_buf(void *buf, size_t size, void *data)
+{
+ git_str *b = (git_str *)data;
+ return git_str_put(b, buf, size);
+}
+
+static int type_size_sort(const void *_a, const void *_b)
+{
+ const git_pobject *a = (git_pobject *)_a;
+ const git_pobject *b = (git_pobject *)_b;
+
+ if (a->type > b->type)
+ return -1;
+ if (a->type < b->type)
+ return 1;
+ if (a->hash > b->hash)
+ return -1;
+ if (a->hash < b->hash)
+ return 1;
+ /*
+ * TODO
+ *
+ if (a->preferred_base > b->preferred_base)
+ return -1;
+ if (a->preferred_base < b->preferred_base)
+ return 1;
+ */
+ if (a->size > b->size)
+ return -1;
+ if (a->size < b->size)
+ return 1;
+ return a < b ? -1 : (a > b); /* newest first */
+}
+
+static int delta_cacheable(
+ git_packbuilder *pb,
+ size_t src_size,
+ size_t trg_size,
+ size_t delta_size)
+{
+ size_t new_size;
+
+ if (git__add_sizet_overflow(&new_size, pb->delta_cache_size, delta_size))
+ return 0;
+
+ if (pb->max_delta_cache_size && new_size > pb->max_delta_cache_size)
+ return 0;
+
+ if (delta_size < pb->cache_max_small_delta_size)
+ return 1;
+
+ /* cache delta, if objects are large enough compared to delta size */
+ if ((src_size >> 20) + (trg_size >> 21) > (delta_size >> 10))
+ return 1;
+
+ return 0;
+}
+
+static int try_delta(git_packbuilder *pb, struct unpacked *trg,
+ struct unpacked *src, size_t max_depth,
+ size_t *mem_usage, int *ret)
+{
+ git_pobject *trg_object = trg->object;
+ git_pobject *src_object = src->object;
+ git_odb_object *obj;
+ size_t trg_size, src_size, delta_size, sizediff, max_size, sz;
+ size_t ref_depth;
+ void *delta_buf;
+
+ /* Don't bother doing diffs between different types */
+ if (trg_object->type != src_object->type) {
+ *ret = -1;
+ return 0;
+ }
+
+ *ret = 0;
+
+ /* TODO: support reuse-delta */
+
+ /* Let's not bust the allowed depth. */
+ if (src->depth >= max_depth)
+ return 0;
+
+ /* Now some size filtering heuristics. */
+ trg_size = trg_object->size;
+ if (!trg_object->delta) {
+ max_size = trg_size/2 - 20;
+ ref_depth = 1;
+ } else {
+ max_size = trg_object->delta_size;
+ ref_depth = trg->depth;
+ }
+
+ max_size = (uint64_t)max_size * (max_depth - src->depth) /
+ (max_depth - ref_depth + 1);
+ if (max_size == 0)
+ return 0;
+
+ src_size = src_object->size;
+ sizediff = src_size < trg_size ? trg_size - src_size : 0;
+ if (sizediff >= max_size)
+ return 0;
+ if (trg_size < src_size / 32)
+ return 0;
+
+ /* Load data if not already done */
+ if (!trg->data) {
+ if (git_odb_read(&obj, pb->odb, &trg_object->id) < 0)
+ return -1;
+
+ sz = git_odb_object_size(obj);
+ trg->data = git__malloc(sz);
+ GIT_ERROR_CHECK_ALLOC(trg->data);
+ memcpy(trg->data, git_odb_object_data(obj), sz);
+
+ git_odb_object_free(obj);
+
+ if (sz != trg_size) {
+ git_error_set(GIT_ERROR_INVALID,
+ "inconsistent target object length");
+ return -1;
+ }
+
+ *mem_usage += sz;
+ }
+ if (!src->data) {
+ size_t obj_sz;
+
+ if (git_odb_read(&obj, pb->odb, &src_object->id) < 0 ||
+ !git__is_ulong(obj_sz = git_odb_object_size(obj)))
+ return -1;
+
+ sz = obj_sz;
+ src->data = git__malloc(sz);
+ GIT_ERROR_CHECK_ALLOC(src->data);
+ memcpy(src->data, git_odb_object_data(obj), sz);
+
+ git_odb_object_free(obj);
+
+ if (sz != src_size) {
+ git_error_set(GIT_ERROR_INVALID,
+ "inconsistent source object length");
+ return -1;
+ }
+
+ *mem_usage += sz;
+ }
+ if (!src->index) {
+ if (git_delta_index_init(&src->index, src->data, src_size) < 0)
+ return 0; /* suboptimal pack - out of memory */
+
+ *mem_usage += git_delta_index_size(src->index);
+ }
+
+ if (git_delta_create_from_index(&delta_buf, &delta_size, src->index, trg->data, trg_size,
+ max_size) < 0)
+ return 0;
+
+ if (trg_object->delta) {
+ /* Prefer only shallower same-sized deltas. */
+ if (delta_size == trg_object->delta_size &&
+ src->depth + 1 >= trg->depth) {
+ git__free(delta_buf);
+ return 0;
+ }
+ }
+
+ GIT_ASSERT(git_packbuilder__cache_lock(pb) == 0);
+
+ if (trg_object->delta_data) {
+ git__free(trg_object->delta_data);
+ GIT_ASSERT(pb->delta_cache_size >= trg_object->delta_size);
+ pb->delta_cache_size -= trg_object->delta_size;
+ trg_object->delta_data = NULL;
+ }
+ if (delta_cacheable(pb, src_size, trg_size, delta_size)) {
+ bool overflow = git__add_sizet_overflow(
+ &pb->delta_cache_size, pb->delta_cache_size, delta_size);
+
+ GIT_ASSERT(git_packbuilder__cache_unlock(pb) == 0);
+
+ if (overflow) {
+ git__free(delta_buf);
+ return -1;
+ }
+
+ trg_object->delta_data = git__realloc(delta_buf, delta_size);
+ GIT_ERROR_CHECK_ALLOC(trg_object->delta_data);
+ } else {
+ /* create delta when writing the pack */
+ GIT_ASSERT(git_packbuilder__cache_unlock(pb) == 0);
+ git__free(delta_buf);
+ }
+
+ trg_object->delta = src_object;
+ trg_object->delta_size = delta_size;
+ trg->depth = src->depth + 1;
+
+ *ret = 1;
+ return 0;
+}
+
+static size_t check_delta_limit(git_pobject *me, size_t n)
+{
+ git_pobject *child = me->delta_child;
+ size_t m = n;
+
+ while (child) {
+ size_t c = check_delta_limit(child, n + 1);
+ if (m < c)
+ m = c;
+ child = child->delta_sibling;
+ }
+ return m;
+}
+
+static size_t free_unpacked(struct unpacked *n)
+{
+ size_t freed_mem = 0;
+
+ if (n->index) {
+ freed_mem += git_delta_index_size(n->index);
+ git_delta_index_free(n->index);
+ }
+ n->index = NULL;
+
+ if (n->data) {
+ freed_mem += n->object->size;
+ git__free(n->data);
+ n->data = NULL;
+ }
+ n->object = NULL;
+ n->depth = 0;
+ return freed_mem;
+}
+
+static int report_delta_progress(
+ git_packbuilder *pb, uint32_t count, bool force)
+{
+ int ret;
+
+ if (pb->progress_cb) {
+ uint64_t current_time = git_time_monotonic();
+ uint64_t elapsed = current_time - pb->last_progress_report_time;
+
+ if (force || elapsed >= MIN_PROGRESS_UPDATE_INTERVAL) {
+ pb->last_progress_report_time = current_time;
+
+ ret = pb->progress_cb(
+ GIT_PACKBUILDER_DELTAFICATION,
+ count, pb->nr_objects, pb->progress_cb_payload);
+
+ if (ret)
+ return git_error_set_after_callback(ret);
+ }
+ }
+
+ return 0;
+}
+
+static int find_deltas(git_packbuilder *pb, git_pobject **list,
+ size_t *list_size, size_t window, size_t depth)
+{
+ git_pobject *po;
+ git_str zbuf = GIT_STR_INIT;
+ struct unpacked *array;
+ size_t idx = 0, count = 0;
+ size_t mem_usage = 0;
+ size_t i;
+ int error = -1;
+
+ array = git__calloc(window, sizeof(struct unpacked));
+ GIT_ERROR_CHECK_ALLOC(array);
+
+ for (;;) {
+ struct unpacked *n = array + idx;
+ size_t max_depth, j, best_base = SIZE_MAX;
+
+ GIT_ASSERT(git_packbuilder__progress_lock(pb) == 0);
+ if (!*list_size) {
+ GIT_ASSERT(git_packbuilder__progress_unlock(pb) == 0);
+ break;
+ }
+
+ pb->nr_deltified += 1;
+ report_delta_progress(pb, pb->nr_deltified, false);
+
+ po = *list++;
+ (*list_size)--;
+ GIT_ASSERT(git_packbuilder__progress_unlock(pb) == 0);
+
+ mem_usage -= free_unpacked(n);
+ n->object = po;
+
+ while (pb->window_memory_limit &&
+ mem_usage > pb->window_memory_limit &&
+ count > 1) {
+ size_t tail = (idx + window - count) % window;
+ mem_usage -= free_unpacked(array + tail);
+ count--;
+ }
+
+ /*
+ * If the current object is at pack edge, take the depth the
+ * objects that depend on the current object into account
+ * otherwise they would become too deep.
+ */
+ max_depth = depth;
+ if (po->delta_child) {
+ size_t delta_limit = check_delta_limit(po, 0);
+
+ if (delta_limit > max_depth)
+ goto next;
+
+ max_depth -= delta_limit;
+ }
+
+ j = window;
+ while (--j > 0) {
+ int ret;
+ size_t other_idx = idx + j;
+ struct unpacked *m;
+
+ if (other_idx >= window)
+ other_idx -= window;
+
+ m = array + other_idx;
+ if (!m->object)
+ break;
+
+ if (try_delta(pb, n, m, max_depth, &mem_usage, &ret) < 0)
+ goto on_error;
+ if (ret < 0)
+ break;
+ else if (ret > 0)
+ best_base = other_idx;
+ }
+
+ /*
+ * If we decided to cache the delta data, then it is best
+ * to compress it right away. First because we have to do
+ * it anyway, and doing it here while we're threaded will
+ * save a lot of time in the non threaded write phase,
+ * as well as allow for caching more deltas within
+ * the same cache size limit.
+ * ...
+ * But only if not writing to stdout, since in that case
+ * the network is most likely throttling writes anyway,
+ * and therefore it is best to go to the write phase ASAP
+ * instead, as we can afford spending more time compressing
+ * between writes at that moment.
+ */
+ if (po->delta_data) {
+ if (git_zstream_deflatebuf(&zbuf, po->delta_data, po->delta_size) < 0)
+ goto on_error;
+
+ git__free(po->delta_data);
+ po->delta_data = git__malloc(zbuf.size);
+ GIT_ERROR_CHECK_ALLOC(po->delta_data);
+
+ memcpy(po->delta_data, zbuf.ptr, zbuf.size);
+ po->z_delta_size = zbuf.size;
+ git_str_clear(&zbuf);
+
+ GIT_ASSERT(git_packbuilder__cache_lock(pb) == 0);
+ pb->delta_cache_size -= po->delta_size;
+ pb->delta_cache_size += po->z_delta_size;
+ GIT_ASSERT(git_packbuilder__cache_unlock(pb) == 0);
+ }
+
+ /*
+ * If we made n a delta, and if n is already at max
+ * depth, leaving it in the window is pointless. we
+ * should evict it first.
+ */
+ if (po->delta && max_depth <= n->depth)
+ continue;
+
+ /*
+ * Move the best delta base up in the window, after the
+ * currently deltified object, to keep it longer. It will
+ * be the first base object to be attempted next.
+ */
+ if (po->delta) {
+ struct unpacked swap = array[best_base];
+ size_t dist = (window + idx - best_base) % window;
+ size_t dst = best_base;
+ while (dist--) {
+ size_t src = (dst + 1) % window;
+ array[dst] = array[src];
+ dst = src;
+ }
+ array[dst] = swap;
+ }
+
+ next:
+ idx++;
+ if (count + 1 < window)
+ count++;
+ if (idx >= window)
+ idx = 0;
+ }
+ error = 0;
+
+on_error:
+ for (i = 0; i < window; ++i) {
+ git__free(array[i].index);
+ git__free(array[i].data);
+ }
+ git__free(array);
+ git_str_dispose(&zbuf);
+
+ return error;
+}
+
+#ifdef GIT_THREADS
+
+struct thread_params {
+ git_thread thread;
+ git_packbuilder *pb;
+
+ git_pobject **list;
+
+ git_cond cond;
+ git_mutex mutex;
+
+ size_t list_size;
+ size_t remaining;
+
+ size_t window;
+ size_t depth;
+ size_t working;
+ size_t data_ready;
+};
+
+static void *threaded_find_deltas(void *arg)
+{
+ struct thread_params *me = arg;
+
+ while (me->remaining) {
+ if (find_deltas(me->pb, me->list, &me->remaining,
+ me->window, me->depth) < 0) {
+ ; /* TODO */
+ }
+
+ GIT_ASSERT_WITH_RETVAL(git_packbuilder__progress_lock(me->pb) == 0, NULL);
+ me->working = 0;
+ git_cond_signal(&me->pb->progress_cond);
+ GIT_ASSERT_WITH_RETVAL(git_packbuilder__progress_unlock(me->pb) == 0, NULL);
+
+ if (git_mutex_lock(&me->mutex)) {
+ git_error_set(GIT_ERROR_THREAD, "unable to lock packfile condition mutex");
+ return NULL;
+ }
+
+ while (!me->data_ready)
+ git_cond_wait(&me->cond, &me->mutex);
+
+ /*
+ * We must not set ->data_ready before we wait on the
+ * condition because the main thread may have set it to 1
+ * before we get here. In order to be sure that new
+ * work is available if we see 1 in ->data_ready, it
+ * was initialized to 0 before this thread was spawned
+ * and we reset it to 0 right away.
+ */
+ me->data_ready = 0;
+ git_mutex_unlock(&me->mutex);
+ }
+ /* leave ->working 1 so that this doesn't get more work assigned */
+ return NULL;
+}
+
+static int ll_find_deltas(git_packbuilder *pb, git_pobject **list,
+ size_t list_size, size_t window, size_t depth)
+{
+ struct thread_params *p;
+ size_t i;
+ int ret, active_threads = 0;
+
+ if (!pb->nr_threads)
+ pb->nr_threads = git__online_cpus();
+
+ if (pb->nr_threads <= 1) {
+ find_deltas(pb, list, &list_size, window, depth);
+ return 0;
+ }
+
+ p = git__mallocarray(pb->nr_threads, sizeof(*p));
+ GIT_ERROR_CHECK_ALLOC(p);
+
+ /* Partition the work among the threads */
+ for (i = 0; i < pb->nr_threads; ++i) {
+ size_t sub_size = list_size / (pb->nr_threads - i);
+
+ /* don't use too small segments or no deltas will be found */
+ if (sub_size < 2*window && i+1 < pb->nr_threads)
+ sub_size = 0;
+
+ p[i].pb = pb;
+ p[i].window = window;
+ p[i].depth = depth;
+ p[i].working = 1;
+ p[i].data_ready = 0;
+
+ /* try to split chunks on "path" boundaries */
+ while (sub_size && sub_size < list_size &&
+ list[sub_size]->hash &&
+ list[sub_size]->hash == list[sub_size-1]->hash)
+ sub_size++;
+
+ p[i].list = list;
+ p[i].list_size = sub_size;
+ p[i].remaining = sub_size;
+
+ list += sub_size;
+ list_size -= sub_size;
+ }
+
+ /* Start work threads */
+ for (i = 0; i < pb->nr_threads; ++i) {
+ if (!p[i].list_size)
+ continue;
+
+ git_mutex_init(&p[i].mutex);
+ git_cond_init(&p[i].cond);
+
+ ret = git_thread_create(&p[i].thread,
+ threaded_find_deltas, &p[i]);
+ if (ret) {
+ git_error_set(GIT_ERROR_THREAD, "unable to create thread");
+ return -1;
+ }
+ active_threads++;
+ }
+
+ /*
+ * Now let's wait for work completion. Each time a thread is done
+ * with its work, we steal half of the remaining work from the
+ * thread with the largest number of unprocessed objects and give
+ * it to that newly idle thread. This ensure good load balancing
+ * until the remaining object list segments are simply too short
+ * to be worth splitting anymore.
+ */
+ while (active_threads) {
+ struct thread_params *target = NULL;
+ struct thread_params *victim = NULL;
+ size_t sub_size = 0;
+
+ /* Start by locating a thread that has transitioned its
+ * 'working' flag from 1 -> 0. This indicates that it is
+ * ready to receive more work using our work-stealing
+ * algorithm. */
+ GIT_ASSERT(git_packbuilder__progress_lock(pb) == 0);
+ for (;;) {
+ for (i = 0; !target && i < pb->nr_threads; i++)
+ if (!p[i].working)
+ target = &p[i];
+ if (target)
+ break;
+ git_cond_wait(&pb->progress_cond, &pb->progress_mutex);
+ }
+
+ /* At this point we hold the progress lock and have located
+ * a thread to receive more work. We still need to locate a
+ * thread from which to steal work (the victim). */
+ for (i = 0; i < pb->nr_threads; i++)
+ if (p[i].remaining > 2*window &&
+ (!victim || victim->remaining < p[i].remaining))
+ victim = &p[i];
+
+ if (victim) {
+ sub_size = victim->remaining / 2;
+ list = victim->list + victim->list_size - sub_size;
+ while (sub_size && list[0]->hash &&
+ list[0]->hash == list[-1]->hash) {
+ list++;
+ sub_size--;
+ }
+ if (!sub_size) {
+ /*
+ * It is possible for some "paths" to have
+ * so many objects that no hash boundary
+ * might be found. Let's just steal the
+ * exact half in that case.
+ */
+ sub_size = victim->remaining / 2;
+ list -= sub_size;
+ }
+ target->list = list;
+ victim->list_size -= sub_size;
+ victim->remaining -= sub_size;
+ }
+ target->list_size = sub_size;
+ target->remaining = sub_size;
+ target->working = 1;
+ GIT_ASSERT(git_packbuilder__progress_unlock(pb) == 0);
+
+ if (git_mutex_lock(&target->mutex)) {
+ git_error_set(GIT_ERROR_THREAD, "unable to lock packfile condition mutex");
+ git__free(p);
+ return -1;
+ }
+
+ target->data_ready = 1;
+ git_cond_signal(&target->cond);
+ git_mutex_unlock(&target->mutex);
+
+ if (!sub_size) {
+ git_thread_join(&target->thread, NULL);
+ git_cond_free(&target->cond);
+ git_mutex_free(&target->mutex);
+ active_threads--;
+ }
+ }
+
+ git__free(p);
+ return 0;
+}
+
+#else
+#define ll_find_deltas(pb, l, ls, w, d) find_deltas(pb, l, &ls, w, d)
+#endif
+
+int git_packbuilder__prepare(git_packbuilder *pb)
+{
+ git_pobject **delta_list;
+ size_t i, n = 0;
+
+ if (pb->nr_objects == 0 || pb->done)
+ return 0; /* nothing to do */
+
+ /*
+ * Although we do not report progress during deltafication, we
+ * at least report that we are in the deltafication stage
+ */
+ if (pb->progress_cb)
+ pb->progress_cb(GIT_PACKBUILDER_DELTAFICATION, 0, pb->nr_objects, pb->progress_cb_payload);
+
+ delta_list = git__mallocarray(pb->nr_objects, sizeof(*delta_list));
+ GIT_ERROR_CHECK_ALLOC(delta_list);
+
+ for (i = 0; i < pb->nr_objects; ++i) {
+ git_pobject *po = pb->object_list + i;
+
+ /* Make sure the item is within our size limits */
+ if (po->size < 50 || po->size > pb->big_file_threshold)
+ continue;
+
+ delta_list[n++] = po;
+ }
+
+ if (n > 1) {
+ git__tsort((void **)delta_list, n, type_size_sort);
+ if (ll_find_deltas(pb, delta_list, n,
+ GIT_PACK_WINDOW + 1,
+ GIT_PACK_DEPTH) < 0) {
+ git__free(delta_list);
+ return -1;
+ }
+ }
+
+ report_delta_progress(pb, pb->nr_objects, true);
+
+ pb->done = true;
+ git__free(delta_list);
+ return 0;
+}
+
+#define PREPARE_PACK if (git_packbuilder__prepare(pb) < 0) { return -1; }
+
+int git_packbuilder_foreach(git_packbuilder *pb, int (*cb)(void *buf, size_t size, void *payload), void *payload)
+{
+ PREPARE_PACK;
+ return write_pack(pb, cb, payload);
+}
+
+int git_packbuilder__write_buf(git_str *buf, git_packbuilder *pb)
+{
+ PREPARE_PACK;
+
+ return write_pack(pb, &write_pack_buf, buf);
+}
+
+int git_packbuilder_write_buf(git_buf *buf, git_packbuilder *pb)
+{
+ GIT_BUF_WRAP_PRIVATE(buf, git_packbuilder__write_buf, pb);
+}
+
+static int write_cb(void *buf, size_t len, void *payload)
+{
+ struct pack_write_context *ctx = payload;
+ return git_indexer_append(ctx->indexer, buf, len, ctx->stats);
+}
+
+int git_packbuilder_write(
+ git_packbuilder *pb,
+ const char *path,
+ unsigned int mode,
+ git_indexer_progress_cb progress_cb,
+ void *progress_cb_payload)
+{
+ int error = -1;
+ git_str object_path = GIT_STR_INIT;
+ git_indexer_options opts = GIT_INDEXER_OPTIONS_INIT;
+ git_indexer *indexer = NULL;
+ git_indexer_progress stats;
+ struct pack_write_context ctx;
+ int t;
+
+ PREPARE_PACK;
+
+ if (path == NULL) {
+ if ((error = git_repository__item_path(&object_path, pb->repo, GIT_REPOSITORY_ITEM_OBJECTS)) < 0)
+ goto cleanup;
+ if ((error = git_str_joinpath(&object_path, git_str_cstr(&object_path), "pack")) < 0)
+ goto cleanup;
+ path = git_str_cstr(&object_path);
+ }
+
+ opts.progress_cb = progress_cb;
+ opts.progress_cb_payload = progress_cb_payload;
+
+ /* TODO: SHA256 */
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ opts.mode = mode;
+ opts.odb = pb->odb;
+
+ error = git_indexer_new(&indexer, path, GIT_OID_SHA1, &opts);
+#else
+ error = git_indexer_new(&indexer, path, mode, pb->odb, &opts);
+#endif
+
+ if (error < 0)
+ goto cleanup;
+
+ if (!git_repository__configmap_lookup(&t, pb->repo, GIT_CONFIGMAP_FSYNCOBJECTFILES) && t)
+ git_indexer__set_fsync(indexer, 1);
+
+ ctx.indexer = indexer;
+ ctx.stats = &stats;
+
+ if ((error = git_packbuilder_foreach(pb, write_cb, &ctx)) < 0)
+ goto cleanup;
+
+ if ((error = git_indexer_commit(indexer, &stats)) < 0)
+ goto cleanup;
+
+#ifndef GIT_DEPRECATE_HARD
+ git_oid_cpy(&pb->pack_oid, git_indexer_hash(indexer));
+#endif
+
+ pb->pack_name = git__strdup(git_indexer_name(indexer));
+ GIT_ERROR_CHECK_ALLOC(pb->pack_name);
+
+cleanup:
+ git_indexer_free(indexer);
+ git_str_dispose(&object_path);
+ return error;
+}
+
+#undef PREPARE_PACK
+
+#ifndef GIT_DEPRECATE_HARD
+const git_oid *git_packbuilder_hash(git_packbuilder *pb)
+{
+ return &pb->pack_oid;
+}
+#endif
+
+const char *git_packbuilder_name(git_packbuilder *pb)
+{
+ return pb->pack_name;
+}
+
+
+static int cb_tree_walk(
+ const char *root, const git_tree_entry *entry, void *payload)
+{
+ int error;
+ struct tree_walk_context *ctx = payload;
+
+ /* A commit inside a tree represents a submodule commit and should be skipped. */
+ if (git_tree_entry_type(entry) == GIT_OBJECT_COMMIT)
+ return 0;
+
+ if (!(error = git_str_sets(&ctx->buf, root)) &&
+ !(error = git_str_puts(&ctx->buf, git_tree_entry_name(entry))))
+ error = git_packbuilder_insert(
+ ctx->pb, git_tree_entry_id(entry), git_str_cstr(&ctx->buf));
+
+ return error;
+}
+
+int git_packbuilder_insert_commit(git_packbuilder *pb, const git_oid *oid)
+{
+ git_commit *commit;
+
+ if (git_commit_lookup(&commit, pb->repo, oid) < 0 ||
+ git_packbuilder_insert(pb, oid, NULL) < 0)
+ return -1;
+
+ if (git_packbuilder_insert_tree(pb, git_commit_tree_id(commit)) < 0)
+ return -1;
+
+ git_commit_free(commit);
+ return 0;
+}
+
+int git_packbuilder_insert_tree(git_packbuilder *pb, const git_oid *oid)
+{
+ int error;
+ git_tree *tree = NULL;
+ struct tree_walk_context context = { pb, GIT_STR_INIT };
+
+ if (!(error = git_tree_lookup(&tree, pb->repo, oid)) &&
+ !(error = git_packbuilder_insert(pb, oid, NULL)))
+ error = git_tree_walk(tree, GIT_TREEWALK_PRE, cb_tree_walk, &context);
+
+ git_tree_free(tree);
+ git_str_dispose(&context.buf);
+ return error;
+}
+
+int git_packbuilder_insert_recur(git_packbuilder *pb, const git_oid *id, const char *name)
+{
+ git_object *obj;
+ int error;
+
+ GIT_ASSERT_ARG(pb);
+ GIT_ASSERT_ARG(id);
+
+ if ((error = git_object_lookup(&obj, pb->repo, id, GIT_OBJECT_ANY)) < 0)
+ return error;
+
+ switch (git_object_type(obj)) {
+ case GIT_OBJECT_BLOB:
+ error = git_packbuilder_insert(pb, id, name);
+ break;
+ case GIT_OBJECT_TREE:
+ error = git_packbuilder_insert_tree(pb, id);
+ break;
+ case GIT_OBJECT_COMMIT:
+ error = git_packbuilder_insert_commit(pb, id);
+ break;
+ case GIT_OBJECT_TAG:
+ if ((error = git_packbuilder_insert(pb, id, name)) < 0)
+ goto cleanup;
+ error = git_packbuilder_insert_recur(pb, git_tag_target_id((git_tag *) obj), NULL);
+ break;
+
+ default:
+ git_error_set(GIT_ERROR_INVALID, "unknown object type");
+ error = -1;
+ }
+
+cleanup:
+ git_object_free(obj);
+ return error;
+}
+
+size_t git_packbuilder_object_count(git_packbuilder *pb)
+{
+ return pb->nr_objects;
+}
+
+size_t git_packbuilder_written(git_packbuilder *pb)
+{
+ return pb->nr_written;
+}
+
+static int lookup_walk_object(struct walk_object **out, git_packbuilder *pb, const git_oid *id)
+{
+ struct walk_object *obj;
+
+ obj = git_pool_mallocz(&pb->object_pool, 1);
+ if (!obj) {
+ git_error_set_oom();
+ return -1;
+ }
+
+ git_oid_cpy(&obj->id, id);
+
+ *out = obj;
+ return 0;
+}
+
+static int retrieve_object(struct walk_object **out, git_packbuilder *pb, const git_oid *id)
+{
+ struct walk_object *obj;
+ int error;
+
+ if ((obj = git_oidmap_get(pb->walk_objects, id)) == NULL) {
+ if ((error = lookup_walk_object(&obj, pb, id)) < 0)
+ return error;
+
+ if ((error = git_oidmap_set(pb->walk_objects, &obj->id, obj)) < 0)
+ return error;
+ }
+
+ *out = obj;
+ return 0;
+}
+
+static int mark_blob_uninteresting(git_packbuilder *pb, const git_oid *id)
+{
+ int error;
+ struct walk_object *obj;
+
+ if ((error = retrieve_object(&obj, pb, id)) < 0)
+ return error;
+
+ obj->uninteresting = 1;
+
+ return 0;
+}
+
+static int mark_tree_uninteresting(git_packbuilder *pb, const git_oid *id)
+{
+ struct walk_object *obj;
+ git_tree *tree;
+ int error;
+ size_t i;
+
+ if ((error = retrieve_object(&obj, pb, id)) < 0)
+ return error;
+
+ if (obj->uninteresting)
+ return 0;
+
+ obj->uninteresting = 1;
+
+ if ((error = git_tree_lookup(&tree, pb->repo, id)) < 0)
+ return error;
+
+ for (i = 0; i < git_tree_entrycount(tree); i++) {
+ const git_tree_entry *entry = git_tree_entry_byindex(tree, i);
+ const git_oid *entry_id = git_tree_entry_id(entry);
+ switch (git_tree_entry_type(entry)) {
+ case GIT_OBJECT_TREE:
+ if ((error = mark_tree_uninteresting(pb, entry_id)) < 0)
+ goto cleanup;
+ break;
+ case GIT_OBJECT_BLOB:
+ if ((error = mark_blob_uninteresting(pb, entry_id)) < 0)
+ goto cleanup;
+ break;
+ default:
+ /* it's a submodule or something unknown, we don't want it */
+ ;
+ }
+ }
+
+cleanup:
+ git_tree_free(tree);
+ return error;
+}
+
+/*
+ * Mark the edges of the graph uninteresting. Since we start from a
+ * git_revwalk, the commits are already uninteresting, but we need to
+ * mark the trees and blobs.
+ */
+static int mark_edges_uninteresting(git_packbuilder *pb, git_commit_list *commits)
+{
+ int error;
+ git_commit_list *list;
+ git_commit *commit;
+
+ for (list = commits; list; list = list->next) {
+ if (!list->item->uninteresting)
+ continue;
+
+ if ((error = git_commit_lookup(&commit, pb->repo, &list->item->oid)) < 0)
+ return error;
+
+ error = mark_tree_uninteresting(pb, git_commit_tree_id(commit));
+ git_commit_free(commit);
+
+ if (error < 0)
+ return error;
+ }
+
+ return 0;
+}
+
+static int pack_objects_insert_tree(git_packbuilder *pb, git_tree *tree)
+{
+ size_t i;
+ int error;
+ git_tree *subtree;
+ struct walk_object *obj;
+ const char *name;
+
+ if ((error = retrieve_object(&obj, pb, git_tree_id(tree))) < 0)
+ return error;
+
+ if (obj->seen || obj->uninteresting)
+ return 0;
+
+ obj->seen = 1;
+
+ if ((error = git_packbuilder_insert(pb, &obj->id, NULL)))
+ return error;
+
+ for (i = 0; i < git_tree_entrycount(tree); i++) {
+ const git_tree_entry *entry = git_tree_entry_byindex(tree, i);
+ const git_oid *entry_id = git_tree_entry_id(entry);
+ switch (git_tree_entry_type(entry)) {
+ case GIT_OBJECT_TREE:
+ if ((error = git_tree_lookup(&subtree, pb->repo, entry_id)) < 0)
+ return error;
+
+ error = pack_objects_insert_tree(pb, subtree);
+ git_tree_free(subtree);
+
+ if (error < 0)
+ return error;
+
+ break;
+ case GIT_OBJECT_BLOB:
+ if ((error = retrieve_object(&obj, pb, entry_id)) < 0)
+ return error;
+ if (obj->uninteresting)
+ continue;
+ name = git_tree_entry_name(entry);
+ if ((error = git_packbuilder_insert(pb, entry_id, name)) < 0)
+ return error;
+ break;
+ default:
+ /* it's a submodule or something unknown, we don't want it */
+ ;
+ }
+ }
+
+
+ return error;
+}
+
+static int pack_objects_insert_commit(git_packbuilder *pb, struct walk_object *obj)
+{
+ int error;
+ git_commit *commit = NULL;
+ git_tree *tree = NULL;
+
+ obj->seen = 1;
+
+ if ((error = git_packbuilder_insert(pb, &obj->id, NULL)) < 0)
+ return error;
+
+ if ((error = git_commit_lookup(&commit, pb->repo, &obj->id)) < 0)
+ return error;
+
+ if ((error = git_tree_lookup(&tree, pb->repo, git_commit_tree_id(commit))) < 0)
+ goto cleanup;
+
+ if ((error = pack_objects_insert_tree(pb, tree)) < 0)
+ goto cleanup;
+
+cleanup:
+ git_commit_free(commit);
+ git_tree_free(tree);
+ return error;
+}
+
+int git_packbuilder_insert_walk(git_packbuilder *pb, git_revwalk *walk)
+{
+ int error;
+ git_oid id;
+ struct walk_object *obj;
+
+ GIT_ASSERT_ARG(pb);
+ GIT_ASSERT_ARG(walk);
+
+ if ((error = mark_edges_uninteresting(pb, walk->user_input)) < 0)
+ return error;
+
+ /*
+ * TODO: git marks the parents of the edges
+ * uninteresting. This may provide a speed advantage, but does
+ * seem to assume the remote does not have a single-commit
+ * history on the other end.
+ */
+
+ /* walk down each tree up to the blobs and insert them, stopping when uninteresting */
+ while ((error = git_revwalk_next(&id, walk)) == 0) {
+ if ((error = retrieve_object(&obj, pb, &id)) < 0)
+ return error;
+
+ if (obj->seen || obj->uninteresting)
+ continue;
+
+ if ((error = pack_objects_insert_commit(pb, obj)) < 0)
+ return error;
+ }
+
+ if (error == GIT_ITEROVER)
+ error = 0;
+
+ return error;
+}
+
+int git_packbuilder_set_callbacks(git_packbuilder *pb, git_packbuilder_progress progress_cb, void *progress_cb_payload)
+{
+ if (!pb)
+ return -1;
+
+ pb->progress_cb = progress_cb;
+ pb->progress_cb_payload = progress_cb_payload;
+
+ return 0;
+}
+
+void git_packbuilder_free(git_packbuilder *pb)
+{
+ if (pb == NULL)
+ return;
+
+#ifdef GIT_THREADS
+
+ git_mutex_free(&pb->cache_mutex);
+ git_mutex_free(&pb->progress_mutex);
+ git_cond_free(&pb->progress_cond);
+
+#endif
+
+ if (pb->odb)
+ git_odb_free(pb->odb);
+
+ if (pb->object_ix)
+ git_oidmap_free(pb->object_ix);
+
+ if (pb->object_list)
+ git__free(pb->object_list);
+
+ git_oidmap_free(pb->walk_objects);
+ git_pool_clear(&pb->object_pool);
+
+ git_hash_ctx_cleanup(&pb->ctx);
+ git_zstream_free(&pb->zstream);
+
+ git__free(pb->pack_name);
+
+ git__free(pb);
+}
diff --git a/src/libgit2/pack-objects.h b/src/libgit2/pack-objects.h
new file mode 100644
index 0000000..bbc8b94
--- /dev/null
+++ b/src/libgit2/pack-objects.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_pack_objects_h__
+#define INCLUDE_pack_objects_h__
+
+#include "common.h"
+
+#include "str.h"
+#include "hash.h"
+#include "oidmap.h"
+#include "zstream.h"
+#include "pool.h"
+#include "indexer.h"
+
+#include "git2/oid.h"
+#include "git2/pack.h"
+
+#define GIT_PACK_WINDOW 10 /* number of objects to possibly delta against */
+#define GIT_PACK_DEPTH 50 /* max delta depth */
+#define GIT_PACK_DELTA_CACHE_SIZE (256 * 1024 * 1024)
+#define GIT_PACK_DELTA_CACHE_LIMIT 1000
+#define GIT_PACK_BIG_FILE_THRESHOLD (512 * 1024 * 1024)
+
+typedef struct git_pobject {
+ git_oid id;
+ git_object_t type;
+ off64_t offset;
+
+ size_t size;
+
+ unsigned int hash; /* name hint hash */
+
+ struct git_pobject *delta; /* delta base object */
+ struct git_pobject *delta_child; /* deltified objects who bases me */
+ struct git_pobject *delta_sibling; /* other deltified objects
+ * who uses the same base as
+ * me */
+
+ void *delta_data;
+ size_t delta_size;
+ size_t z_delta_size;
+
+ unsigned int written:1,
+ recursing:1,
+ tagged:1,
+ filled:1;
+} git_pobject;
+
+struct git_packbuilder {
+ git_repository *repo; /* associated repository */
+ git_odb *odb; /* associated object database */
+
+ git_oid_t oid_type;
+
+ git_hash_ctx ctx;
+ git_zstream zstream;
+
+ uint32_t nr_objects,
+ nr_deltified,
+ nr_written,
+ nr_remaining;
+
+ size_t nr_alloc;
+
+ git_pobject *object_list;
+
+ git_oidmap *object_ix;
+
+ git_oidmap *walk_objects;
+ git_pool object_pool;
+
+#ifndef GIT_DEPRECATE_HARD
+ git_oid pack_oid; /* hash of written pack */
+#endif
+ char *pack_name; /* name of written pack */
+
+ /* synchronization objects */
+ git_mutex cache_mutex;
+ git_mutex progress_mutex;
+ git_cond progress_cond;
+
+ /* configs */
+ size_t delta_cache_size;
+ size_t max_delta_cache_size;
+ size_t cache_max_small_delta_size;
+ size_t big_file_threshold;
+ size_t window_memory_limit;
+
+ unsigned int nr_threads; /* nr of threads to use */
+
+ git_packbuilder_progress progress_cb;
+ void *progress_cb_payload;
+
+ /* the time progress was last reported, in millisecond ticks */
+ uint64_t last_progress_report_time;
+
+ bool done;
+};
+
+int git_packbuilder__write_buf(git_str *buf, git_packbuilder *pb);
+int git_packbuilder__prepare(git_packbuilder *pb);
+
+
+#endif
diff --git a/src/libgit2/pack.c b/src/libgit2/pack.c
new file mode 100644
index 0000000..eff7398
--- /dev/null
+++ b/src/libgit2/pack.c
@@ -0,0 +1,1658 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "pack.h"
+
+#include "delta.h"
+#include "futils.h"
+#include "mwindow.h"
+#include "odb.h"
+#include "oid.h"
+#include "oidarray.h"
+
+/* Option to bypass checking existence of '.keep' files */
+bool git_disable_pack_keep_file_checks = false;
+
+static int packfile_open_locked(struct git_pack_file *p);
+static off64_t nth_packed_object_offset_locked(struct git_pack_file *p, uint32_t n);
+static int packfile_unpack_compressed(
+ git_rawobj *obj,
+ struct git_pack_file *p,
+ git_mwindow **w_curs,
+ off64_t *curpos,
+ size_t size,
+ git_object_t type);
+
+/* Can find the offset of an object given
+ * a prefix of an identifier.
+ * Throws GIT_EAMBIGUOUSOIDPREFIX if short oid
+ * is ambiguous within the pack.
+ * This method assumes that len is between
+ * GIT_OID_MINPREFIXLEN and the oid type's hexsize.
+ */
+static int pack_entry_find_offset(
+ off64_t *offset_out,
+ git_oid *found_oid,
+ struct git_pack_file *p,
+ const git_oid *short_oid,
+ size_t len);
+
+static int packfile_error(const char *message)
+{
+ git_error_set(GIT_ERROR_ODB, "invalid pack file - %s", message);
+ return -1;
+}
+
+/********************
+ * Delta base cache
+ ********************/
+
+static git_pack_cache_entry *new_cache_object(git_rawobj *source)
+{
+ git_pack_cache_entry *e = git__calloc(1, sizeof(git_pack_cache_entry));
+ if (!e)
+ return NULL;
+
+ git_atomic32_inc(&e->refcount);
+ memcpy(&e->raw, source, sizeof(git_rawobj));
+
+ return e;
+}
+
+static void free_cache_object(void *o)
+{
+ git_pack_cache_entry *e = (git_pack_cache_entry *)o;
+
+ if (e != NULL) {
+ git__free(e->raw.data);
+ git__free(e);
+ }
+}
+
+static void cache_free(git_pack_cache *cache)
+{
+ git_pack_cache_entry *entry;
+
+ if (cache->entries) {
+ git_offmap_foreach_value(cache->entries, entry, {
+ free_cache_object(entry);
+ });
+
+ git_offmap_free(cache->entries);
+ cache->entries = NULL;
+ }
+}
+
+static int cache_init(git_pack_cache *cache)
+{
+ if (git_offmap_new(&cache->entries) < 0)
+ return -1;
+
+ cache->memory_limit = GIT_PACK_CACHE_MEMORY_LIMIT;
+
+ if (git_mutex_init(&cache->lock)) {
+ git_error_set(GIT_ERROR_OS, "failed to initialize pack cache mutex");
+
+ git__free(cache->entries);
+ cache->entries = NULL;
+
+ return -1;
+ }
+
+ return 0;
+}
+
+static git_pack_cache_entry *cache_get(git_pack_cache *cache, off64_t offset)
+{
+ git_pack_cache_entry *entry;
+
+ if (git_mutex_lock(&cache->lock) < 0)
+ return NULL;
+
+ if ((entry = git_offmap_get(cache->entries, offset)) != NULL) {
+ git_atomic32_inc(&entry->refcount);
+ entry->last_usage = cache->use_ctr++;
+ }
+ git_mutex_unlock(&cache->lock);
+
+ return entry;
+}
+
+/* Run with the cache lock held */
+static void free_lowest_entry(git_pack_cache *cache)
+{
+ off64_t offset;
+ git_pack_cache_entry *entry;
+
+ git_offmap_foreach(cache->entries, offset, entry, {
+ if (entry && git_atomic32_get(&entry->refcount) == 0) {
+ cache->memory_used -= entry->raw.len;
+ git_offmap_delete(cache->entries, offset);
+ free_cache_object(entry);
+ }
+ });
+}
+
+static int cache_add(
+ git_pack_cache_entry **cached_out,
+ git_pack_cache *cache,
+ git_rawobj *base,
+ off64_t offset)
+{
+ git_pack_cache_entry *entry;
+ int exists;
+
+ if (base->len > GIT_PACK_CACHE_SIZE_LIMIT)
+ return -1;
+
+ entry = new_cache_object(base);
+ if (entry) {
+ if (git_mutex_lock(&cache->lock) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to lock cache");
+ git__free(entry);
+ return -1;
+ }
+ /* Add it to the cache if nobody else has */
+ exists = git_offmap_exists(cache->entries, offset);
+ if (!exists) {
+ while (cache->memory_used + base->len > cache->memory_limit)
+ free_lowest_entry(cache);
+
+ git_offmap_set(cache->entries, offset, entry);
+ cache->memory_used += entry->raw.len;
+
+ *cached_out = entry;
+ }
+ git_mutex_unlock(&cache->lock);
+ /* Somebody beat us to adding it into the cache */
+ if (exists) {
+ git__free(entry);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+/***********************************************************
+ *
+ * PACK INDEX METHODS
+ *
+ ***********************************************************/
+
+static void pack_index_free(struct git_pack_file *p)
+{
+ if (p->ids) {
+ git__free(p->ids);
+ p->ids = NULL;
+ }
+ if (p->index_map.data) {
+ git_futils_mmap_free(&p->index_map);
+ p->index_map.data = NULL;
+ }
+}
+
+/* Run with the packfile lock held */
+static int pack_index_check_locked(const char *path, struct git_pack_file *p)
+{
+ struct git_pack_idx_header *hdr;
+ uint32_t version, nr = 0, i, *index;
+ void *idx_map;
+ size_t idx_size;
+ struct stat st;
+ int error;
+
+ /* TODO: properly open the file without access time using O_NOATIME */
+ git_file fd = git_futils_open_ro(path);
+ if (fd < 0)
+ return fd;
+
+ if (p_fstat(fd, &st) < 0) {
+ p_close(fd);
+ git_error_set(GIT_ERROR_OS, "unable to stat pack index '%s'", path);
+ return -1;
+ }
+
+ if (!S_ISREG(st.st_mode) ||
+ !git__is_sizet(st.st_size) ||
+ (idx_size = (size_t)st.st_size) < (size_t)((4 * 256) + (p->oid_size * 2))) {
+ p_close(fd);
+ git_error_set(GIT_ERROR_ODB, "invalid pack index '%s'", path);
+ return -1;
+ }
+
+ error = git_futils_mmap_ro(&p->index_map, fd, 0, idx_size);
+
+ p_close(fd);
+
+ if (error < 0)
+ return error;
+
+ hdr = idx_map = p->index_map.data;
+
+ if (hdr->idx_signature == htonl(PACK_IDX_SIGNATURE)) {
+ version = ntohl(hdr->idx_version);
+
+ if (version < 2 || version > 2) {
+ git_futils_mmap_free(&p->index_map);
+ return packfile_error("unsupported index version");
+ }
+
+ } else {
+ version = 1;
+ }
+
+ index = idx_map;
+
+ if (version > 1)
+ index += 2; /* skip index header */
+
+ for (i = 0; i < 256; i++) {
+ uint32_t n = ntohl(index[i]);
+ if (n < nr) {
+ git_futils_mmap_free(&p->index_map);
+ return packfile_error("index is non-monotonic");
+ }
+ nr = n;
+ }
+
+ if (version == 1) {
+ /*
+ * Total size:
+ * - 256 index entries 4 bytes each
+ * - 24/36-byte entries * nr (20/32 byte SHA + 4-byte offset)
+ * - 20/32-byte SHA of the packfile
+ * - 20/32-byte SHA file checksum
+ */
+ if (idx_size != (4 * 256 + ((uint64_t) nr * (p->oid_size + 4)) + (p->oid_size * 2))) {
+ git_futils_mmap_free(&p->index_map);
+ return packfile_error("index is corrupted");
+ }
+ } else if (version == 2) {
+ /*
+ * Minimum size:
+ * - 8 bytes of header
+ * - 256 index entries 4 bytes each
+ * - 20/32-byte SHA entry * nr
+ * - 4-byte crc entry * nr
+ * - 4-byte offset entry * nr
+ * - 20/32-byte SHA of the packfile
+ * - 20/32-byte SHA file checksum
+ * And after the 4-byte offset table might be a
+ * variable sized table containing 8-byte entries
+ * for offsets larger than 2^31.
+ */
+ uint64_t min_size = 8 + (4 * 256) + ((uint64_t)nr * (p->oid_size + 4 + 4)) + (p->oid_size * 2);
+ uint64_t max_size = min_size;
+
+ if (nr)
+ max_size += (nr - 1)*8;
+
+ if (idx_size < min_size || idx_size > max_size) {
+ git_futils_mmap_free(&p->index_map);
+ return packfile_error("wrong index size");
+ }
+ }
+
+ p->num_objects = nr;
+ p->index_version = version;
+ return 0;
+}
+
+/* Run with the packfile lock held */
+static int pack_index_open_locked(struct git_pack_file *p)
+{
+ int error = 0;
+ size_t name_len;
+ git_str idx_name = GIT_STR_INIT;
+
+ if (p->index_version > -1)
+ goto cleanup;
+
+ /* checked by git_pack_file alloc */
+ name_len = strlen(p->pack_name);
+ GIT_ASSERT(name_len > strlen(".pack"));
+
+ if ((error = git_str_init(&idx_name, name_len)) < 0)
+ goto cleanup;
+
+ git_str_put(&idx_name, p->pack_name, name_len - strlen(".pack"));
+ git_str_puts(&idx_name, ".idx");
+ if (git_str_oom(&idx_name)) {
+ error = -1;
+ goto cleanup;
+ }
+
+ if (p->index_version == -1)
+ error = pack_index_check_locked(idx_name.ptr, p);
+
+cleanup:
+ git_str_dispose(&idx_name);
+
+ return error;
+}
+
+static unsigned char *pack_window_open(
+ struct git_pack_file *p,
+ git_mwindow **w_cursor,
+ off64_t offset,
+ unsigned int *left)
+{
+ unsigned char *pack_data = NULL;
+
+ if (git_mutex_lock(&p->lock) < 0) {
+ git_error_set(GIT_ERROR_THREAD, "unable to lock packfile");
+ return NULL;
+ }
+ if (git_mutex_lock(&p->mwf.lock) < 0) {
+ git_mutex_unlock(&p->lock);
+ git_error_set(GIT_ERROR_THREAD, "unable to lock packfile");
+ return NULL;
+ }
+
+ if (p->mwf.fd == -1 && packfile_open_locked(p) < 0)
+ goto cleanup;
+
+ /* Since packfiles end in a hash of their content and it's
+ * pointless to ask for an offset into the middle of that
+ * hash, and the pack_window_contains function above wouldn't match
+ * don't allow an offset too close to the end of the file.
+ *
+ * Don't allow a negative offset, as that means we've wrapped
+ * around.
+ */
+ if (offset > (p->mwf.size - p->oid_size))
+ goto cleanup;
+ if (offset < 0)
+ goto cleanup;
+
+ pack_data = git_mwindow_open(&p->mwf, w_cursor, offset, p->oid_size, left);
+
+cleanup:
+ git_mutex_unlock(&p->mwf.lock);
+ git_mutex_unlock(&p->lock);
+ return pack_data;
+ }
+
+/*
+ * The per-object header is a pretty dense thing, which is
+ * - first byte: low four bits are "size",
+ * then three bits of "type",
+ * with the high bit being "size continues".
+ * - each byte afterwards: low seven bits are size continuation,
+ * with the high bit being "size continues"
+ */
+int git_packfile__object_header(size_t *out, unsigned char *hdr, size_t size, git_object_t type)
+{
+ unsigned char *hdr_base;
+ unsigned char c;
+
+ GIT_ASSERT_ARG(type >= GIT_OBJECT_COMMIT && type <= GIT_OBJECT_REF_DELTA);
+
+ /* TODO: add support for chunked objects; see git.git 6c0d19b1 */
+
+ c = (unsigned char)((type << 4) | (size & 15));
+ size >>= 4;
+ hdr_base = hdr;
+
+ while (size) {
+ *hdr++ = c | 0x80;
+ c = size & 0x7f;
+ size >>= 7;
+ }
+ *hdr++ = c;
+
+ *out = (hdr - hdr_base);
+ return 0;
+}
+
+
+static int packfile_unpack_header1(
+ unsigned long *usedp,
+ size_t *sizep,
+ git_object_t *type,
+ const unsigned char *buf,
+ unsigned long len)
+{
+ unsigned shift;
+ unsigned long size, c;
+ unsigned long used = 0;
+
+ c = buf[used++];
+ *type = (c >> 4) & 7;
+ size = c & 15;
+ shift = 4;
+ while (c & 0x80) {
+ if (len <= used) {
+ git_error_set(GIT_ERROR_ODB, "buffer too small");
+ return GIT_EBUFS;
+ }
+
+ if (bitsizeof(long) <= shift) {
+ *usedp = 0;
+ git_error_set(GIT_ERROR_ODB, "packfile corrupted");
+ return -1;
+ }
+
+ c = buf[used++];
+ size += (c & 0x7f) << shift;
+ shift += 7;
+ }
+
+ *sizep = (size_t)size;
+ *usedp = used;
+ return 0;
+}
+
+int git_packfile_unpack_header(
+ size_t *size_p,
+ git_object_t *type_p,
+ struct git_pack_file *p,
+ git_mwindow **w_curs,
+ off64_t *curpos)
+{
+ unsigned char *base;
+ unsigned int left;
+ unsigned long used;
+ int error;
+
+ if ((error = git_mutex_lock(&p->lock)) < 0)
+ return error;
+ if ((error = git_mutex_lock(&p->mwf.lock)) < 0) {
+ git_mutex_unlock(&p->lock);
+ return error;
+ }
+
+ if (p->mwf.fd == -1 && (error = packfile_open_locked(p)) < 0) {
+ git_mutex_unlock(&p->lock);
+ git_mutex_unlock(&p->mwf.lock);
+ return error;
+ }
+
+ /* pack_window_open() assures us we have [base, base + oid_size)
+ * available as a range that we can look at at. (It's actually
+ * the hash size that is assured.) With our object header
+ * encoding the maximum deflated object size is 2^137, which is
+ * just insane, so we know won't exceed what we have been given.
+ */
+ base = git_mwindow_open(&p->mwf, w_curs, *curpos, p->oid_size, &left);
+ git_mutex_unlock(&p->lock);
+ git_mutex_unlock(&p->mwf.lock);
+ if (base == NULL)
+ return GIT_EBUFS;
+
+ error = packfile_unpack_header1(&used, size_p, type_p, base, left);
+ git_mwindow_close(w_curs);
+ if (error == GIT_EBUFS)
+ return error;
+ else if (error < 0)
+ return packfile_error("header length is zero");
+
+ *curpos += used;
+ return 0;
+}
+
+int git_packfile_resolve_header(
+ size_t *size_p,
+ git_object_t *type_p,
+ struct git_pack_file *p,
+ off64_t offset)
+{
+ git_mwindow *w_curs = NULL;
+ off64_t curpos = offset;
+ size_t size;
+ git_object_t type;
+ off64_t base_offset;
+ int error;
+
+ error = git_mutex_lock(&p->lock);
+ if (error < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to lock packfile reader");
+ return error;
+ }
+ error = git_mutex_lock(&p->mwf.lock);
+ if (error < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to lock packfile reader");
+ git_mutex_unlock(&p->lock);
+ return error;
+ }
+
+ if (p->mwf.fd == -1 && (error = packfile_open_locked(p)) < 0) {
+ git_mutex_unlock(&p->mwf.lock);
+ git_mutex_unlock(&p->lock);
+ return error;
+ }
+ git_mutex_unlock(&p->mwf.lock);
+ git_mutex_unlock(&p->lock);
+
+ error = git_packfile_unpack_header(&size, &type, p, &w_curs, &curpos);
+ if (error < 0)
+ return error;
+
+ if (type == GIT_OBJECT_OFS_DELTA || type == GIT_OBJECT_REF_DELTA) {
+ size_t base_size;
+ git_packfile_stream stream;
+
+ error = get_delta_base(&base_offset, p, &w_curs, &curpos, type, offset);
+ git_mwindow_close(&w_curs);
+
+ if (error < 0)
+ return error;
+
+ if ((error = git_packfile_stream_open(&stream, p, curpos)) < 0)
+ return error;
+ error = git_delta_read_header_fromstream(&base_size, size_p, &stream);
+ git_packfile_stream_dispose(&stream);
+ if (error < 0)
+ return error;
+ } else {
+ *size_p = size;
+ base_offset = 0;
+ }
+
+ while (type == GIT_OBJECT_OFS_DELTA || type == GIT_OBJECT_REF_DELTA) {
+ curpos = base_offset;
+ error = git_packfile_unpack_header(&size, &type, p, &w_curs, &curpos);
+ if (error < 0)
+ return error;
+ if (type != GIT_OBJECT_OFS_DELTA && type != GIT_OBJECT_REF_DELTA)
+ break;
+
+ error = get_delta_base(&base_offset, p, &w_curs, &curpos, type, base_offset);
+ git_mwindow_close(&w_curs);
+
+ if (error < 0)
+ return error;
+ }
+ *type_p = type;
+
+ return error;
+}
+
+#define SMALL_STACK_SIZE 64
+
+/**
+ * Generate the chain of dependencies which we need to get to the
+ * object at `off`. `chain` is used a stack, popping gives the right
+ * order to apply deltas on. If an object is found in the pack's base
+ * cache, we stop calculating there.
+ */
+static int pack_dependency_chain(git_dependency_chain *chain_out,
+ git_pack_cache_entry **cached_out, off64_t *cached_off,
+ struct pack_chain_elem *small_stack, size_t *stack_sz,
+ struct git_pack_file *p, off64_t obj_offset)
+{
+ git_dependency_chain chain = GIT_ARRAY_INIT;
+ git_mwindow *w_curs = NULL;
+ off64_t curpos = obj_offset, base_offset;
+ int error = 0, use_heap = 0;
+ size_t size, elem_pos;
+ git_object_t type;
+
+ elem_pos = 0;
+ while (true) {
+ struct pack_chain_elem *elem;
+ git_pack_cache_entry *cached = NULL;
+
+ /* if we have a base cached, we can stop here instead */
+ if ((cached = cache_get(&p->bases, obj_offset)) != NULL) {
+ *cached_out = cached;
+ *cached_off = obj_offset;
+ break;
+ }
+
+ /* if we run out of space on the small stack, use the array */
+ if (elem_pos == SMALL_STACK_SIZE) {
+ git_array_init_to_size(chain, elem_pos);
+ GIT_ERROR_CHECK_ARRAY(chain);
+ memcpy(chain.ptr, small_stack, elem_pos * sizeof(struct pack_chain_elem));
+ chain.size = elem_pos;
+ use_heap = 1;
+ }
+
+ curpos = obj_offset;
+ if (!use_heap) {
+ elem = &small_stack[elem_pos];
+ } else {
+ elem = git_array_alloc(chain);
+ if (!elem) {
+ error = -1;
+ goto on_error;
+ }
+ }
+
+ elem->base_key = obj_offset;
+
+ error = git_packfile_unpack_header(&size, &type, p, &w_curs, &curpos);
+ if (error < 0)
+ goto on_error;
+
+ elem->offset = curpos;
+ elem->size = size;
+ elem->type = type;
+ elem->base_key = obj_offset;
+
+ if (type != GIT_OBJECT_OFS_DELTA && type != GIT_OBJECT_REF_DELTA)
+ break;
+
+ error = get_delta_base(&base_offset, p, &w_curs, &curpos, type, obj_offset);
+ git_mwindow_close(&w_curs);
+
+ if (error < 0)
+ goto on_error;
+
+ /* we need to pass the pos *after* the delta-base bit */
+ elem->offset = curpos;
+
+ /* go through the loop again, but with the new object */
+ obj_offset = base_offset;
+ elem_pos++;
+ }
+
+
+ *stack_sz = elem_pos + 1;
+ *chain_out = chain;
+ return error;
+
+on_error:
+ git_array_clear(chain);
+ return error;
+}
+
+int git_packfile_unpack(
+ git_rawobj *obj,
+ struct git_pack_file *p,
+ off64_t *obj_offset)
+{
+ git_mwindow *w_curs = NULL;
+ off64_t curpos = *obj_offset;
+ int error, free_base = 0;
+ git_dependency_chain chain = GIT_ARRAY_INIT;
+ struct pack_chain_elem *elem = NULL, *stack;
+ git_pack_cache_entry *cached = NULL;
+ struct pack_chain_elem small_stack[SMALL_STACK_SIZE];
+ size_t stack_size = 0, elem_pos, alloclen;
+ git_object_t base_type;
+
+ error = git_mutex_lock(&p->lock);
+ if (error < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to lock packfile reader");
+ return error;
+ }
+ error = git_mutex_lock(&p->mwf.lock);
+ if (error < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to lock packfile reader");
+ git_mutex_unlock(&p->lock);
+ return error;
+ }
+
+ if (p->mwf.fd == -1)
+ error = packfile_open_locked(p);
+ git_mutex_unlock(&p->mwf.lock);
+ git_mutex_unlock(&p->lock);
+ if (error < 0)
+ return error;
+
+ /*
+ * TODO: optionally check the CRC on the packfile
+ */
+
+ error = pack_dependency_chain(&chain, &cached, obj_offset, small_stack, &stack_size, p, *obj_offset);
+ if (error < 0)
+ return error;
+
+ obj->data = NULL;
+ obj->len = 0;
+ obj->type = GIT_OBJECT_INVALID;
+
+ /* let's point to the right stack */
+ stack = chain.ptr ? chain.ptr : small_stack;
+
+ elem_pos = stack_size;
+ if (cached) {
+ memcpy(obj, &cached->raw, sizeof(git_rawobj));
+ base_type = obj->type;
+ elem_pos--; /* stack_size includes the base, which isn't actually there */
+ } else {
+ elem = &stack[--elem_pos];
+ base_type = elem->type;
+ }
+
+ switch (base_type) {
+ case GIT_OBJECT_COMMIT:
+ case GIT_OBJECT_TREE:
+ case GIT_OBJECT_BLOB:
+ case GIT_OBJECT_TAG:
+ if (!cached) {
+ curpos = elem->offset;
+ error = packfile_unpack_compressed(obj, p, &w_curs, &curpos, elem->size, elem->type);
+ git_mwindow_close(&w_curs);
+ base_type = elem->type;
+ }
+ if (error < 0)
+ goto cleanup;
+ break;
+ case GIT_OBJECT_OFS_DELTA:
+ case GIT_OBJECT_REF_DELTA:
+ error = packfile_error("dependency chain ends in a delta");
+ goto cleanup;
+ default:
+ error = packfile_error("invalid packfile type in header");
+ goto cleanup;
+ }
+
+ /*
+ * Finding the object we want a cached base element is
+ * problematic, as we need to make sure we don't accidentally
+ * give the caller the cached object, which it would then feel
+ * free to free, so we need to copy the data.
+ */
+ if (cached && stack_size == 1) {
+ void *data = obj->data;
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, obj->len, 1);
+ obj->data = git__malloc(alloclen);
+ GIT_ERROR_CHECK_ALLOC(obj->data);
+
+ memcpy(obj->data, data, obj->len + 1);
+ git_atomic32_dec(&cached->refcount);
+ goto cleanup;
+ }
+
+ /* we now apply each consecutive delta until we run out */
+ while (elem_pos > 0 && !error) {
+ git_rawobj base, delta;
+
+ /*
+ * We can now try to add the base to the cache, as
+ * long as it's not already the cached one.
+ */
+ if (!cached)
+ free_base = !!cache_add(&cached, &p->bases, obj, elem->base_key);
+
+ elem = &stack[elem_pos - 1];
+ curpos = elem->offset;
+ error = packfile_unpack_compressed(&delta, p, &w_curs, &curpos, elem->size, elem->type);
+ git_mwindow_close(&w_curs);
+
+ if (error < 0) {
+ /* We have transferred ownership of the data to the cache. */
+ obj->data = NULL;
+ break;
+ }
+
+ /* the current object becomes the new base, on which we apply the delta */
+ base = *obj;
+ obj->data = NULL;
+ obj->len = 0;
+ obj->type = GIT_OBJECT_INVALID;
+
+ error = git_delta_apply(&obj->data, &obj->len, base.data, base.len, delta.data, delta.len);
+ obj->type = base_type;
+
+ /*
+ * We usually don't want to free the base at this
+ * point, as we put it into the cache in the previous
+ * iteration. free_base lets us know that we got the
+ * base object directly from the packfile, so we can free it.
+ */
+ git__free(delta.data);
+ if (free_base) {
+ free_base = 0;
+ git__free(base.data);
+ }
+
+ if (cached) {
+ git_atomic32_dec(&cached->refcount);
+ cached = NULL;
+ }
+
+ if (error < 0)
+ break;
+
+ elem_pos--;
+ }
+
+cleanup:
+ if (error < 0) {
+ git__free(obj->data);
+ if (cached)
+ git_atomic32_dec(&cached->refcount);
+ }
+
+ if (elem)
+ *obj_offset = curpos;
+
+ git_array_clear(chain);
+ return error;
+}
+
+int git_packfile_stream_open(git_packfile_stream *obj, struct git_pack_file *p, off64_t curpos)
+{
+ memset(obj, 0, sizeof(git_packfile_stream));
+ obj->curpos = curpos;
+ obj->p = p;
+
+ if (git_zstream_init(&obj->zstream, GIT_ZSTREAM_INFLATE) < 0) {
+ git_error_set(GIT_ERROR_ZLIB, "failed to init packfile stream");
+ return -1;
+ }
+
+ return 0;
+}
+
+ssize_t git_packfile_stream_read(git_packfile_stream *obj, void *buffer, size_t len)
+{
+ unsigned int window_len;
+ unsigned char *in;
+ int error;
+
+ if (obj->done)
+ return 0;
+
+ if ((in = pack_window_open(obj->p, &obj->mw, obj->curpos, &window_len)) == NULL)
+ return GIT_EBUFS;
+
+ if ((error = git_zstream_set_input(&obj->zstream, in, window_len)) < 0 ||
+ (error = git_zstream_get_output_chunk(buffer, &len, &obj->zstream)) < 0) {
+ git_mwindow_close(&obj->mw);
+ git_error_set(GIT_ERROR_ZLIB, "error reading from the zlib stream");
+ return -1;
+ }
+
+ git_mwindow_close(&obj->mw);
+
+ obj->curpos += window_len - obj->zstream.in_len;
+
+ if (git_zstream_eos(&obj->zstream))
+ obj->done = 1;
+
+ /* If we didn't write anything out but we're not done, we need more data */
+ if (!len && !git_zstream_eos(&obj->zstream))
+ return GIT_EBUFS;
+
+ return len;
+
+}
+
+void git_packfile_stream_dispose(git_packfile_stream *obj)
+{
+ git_zstream_free(&obj->zstream);
+}
+
+static int packfile_unpack_compressed(
+ git_rawobj *obj,
+ struct git_pack_file *p,
+ git_mwindow **mwindow,
+ off64_t *position,
+ size_t size,
+ git_object_t type)
+{
+ git_zstream zstream = GIT_ZSTREAM_INIT;
+ size_t buffer_len, total = 0;
+ char *data = NULL;
+ int error;
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&buffer_len, size, 1);
+ data = git__calloc(1, buffer_len);
+ GIT_ERROR_CHECK_ALLOC(data);
+
+ if ((error = git_zstream_init(&zstream, GIT_ZSTREAM_INFLATE)) < 0) {
+ git_error_set(GIT_ERROR_ZLIB, "failed to init zlib stream on unpack");
+ goto out;
+ }
+
+ do {
+ size_t bytes = buffer_len - total;
+ unsigned int window_len, consumed;
+ unsigned char *in;
+
+ if ((in = pack_window_open(p, mwindow, *position, &window_len)) == NULL) {
+ error = -1;
+ goto out;
+ }
+
+ if ((error = git_zstream_set_input(&zstream, in, window_len)) < 0 ||
+ (error = git_zstream_get_output_chunk(data + total, &bytes, &zstream)) < 0) {
+ git_mwindow_close(mwindow);
+ goto out;
+ }
+
+ git_mwindow_close(mwindow);
+
+ consumed = window_len - (unsigned int)zstream.in_len;
+
+ if (!bytes && !consumed) {
+ git_error_set(GIT_ERROR_ZLIB, "error inflating zlib stream");
+ error = -1;
+ goto out;
+ }
+
+ *position += consumed;
+ total += bytes;
+ } while (!git_zstream_eos(&zstream));
+
+ if (total != size || !git_zstream_eos(&zstream)) {
+ git_error_set(GIT_ERROR_ZLIB, "error inflating zlib stream");
+ error = -1;
+ goto out;
+ }
+
+ obj->type = type;
+ obj->len = size;
+ obj->data = data;
+
+out:
+ git_zstream_free(&zstream);
+ if (error)
+ git__free(data);
+
+ return error;
+}
+
+/*
+ * curpos is where the data starts, delta_obj_offset is the where the
+ * header starts
+ */
+int get_delta_base(
+ off64_t *delta_base_out,
+ struct git_pack_file *p,
+ git_mwindow **w_curs,
+ off64_t *curpos,
+ git_object_t type,
+ off64_t delta_obj_offset)
+{
+ unsigned int left = 0;
+ unsigned char *base_info;
+ off64_t base_offset;
+ git_oid unused;
+
+ GIT_ASSERT_ARG(delta_base_out);
+
+ base_info = pack_window_open(p, w_curs, *curpos, &left);
+ /* Assumption: the only reason this would fail is because the file is too small */
+ if (base_info == NULL)
+ return GIT_EBUFS;
+ /* pack_window_open() assured us we have
+ * [base_info, base_info + oid_size) as a range that we can look
+ * at without walking off the end of the mapped window. Its
+ * actually the hash size that is assured. An OFS_DELTA longer
+ * than the hash size is stupid, as then a REF_DELTA would be
+ * smaller to store.
+ */
+ if (type == GIT_OBJECT_OFS_DELTA) {
+ unsigned used = 0;
+ unsigned char c = base_info[used++];
+ size_t unsigned_base_offset = c & 127;
+ while (c & 128) {
+ if (left <= used)
+ return GIT_EBUFS;
+ unsigned_base_offset += 1;
+ if (!unsigned_base_offset || MSB(unsigned_base_offset, 7))
+ return packfile_error("overflow");
+ c = base_info[used++];
+ unsigned_base_offset = (unsigned_base_offset << 7) + (c & 127);
+ }
+ if (unsigned_base_offset == 0 || (size_t)delta_obj_offset <= unsigned_base_offset)
+ return packfile_error("out of bounds");
+ base_offset = delta_obj_offset - unsigned_base_offset;
+ *curpos += used;
+ } else if (type == GIT_OBJECT_REF_DELTA) {
+ git_oid base_oid;
+ git_oid__fromraw(&base_oid, base_info, p->oid_type);
+
+ /* If we have the cooperative cache, search in it first */
+ if (p->has_cache) {
+ struct git_pack_entry *entry;
+
+ if ((entry = git_oidmap_get(p->idx_cache, &base_oid)) != NULL) {
+ if (entry->offset == 0)
+ return packfile_error("delta offset is zero");
+
+ *curpos += p->oid_size;
+ *delta_base_out = entry->offset;
+ return 0;
+ } else {
+ /* If we're building an index, don't try to find the pack
+ * entry; we just haven't seen it yet. We'll make
+ * progress again in the next loop.
+ */
+ return GIT_PASSTHROUGH;
+ }
+ }
+
+ /* The base entry _must_ be in the same pack */
+ if (pack_entry_find_offset(&base_offset, &unused, p, &base_oid, p->oid_hexsize) < 0)
+ return packfile_error("base entry delta is not in the same pack");
+ *curpos += p->oid_size;
+ } else
+ return packfile_error("unknown object type");
+
+ if (base_offset == 0)
+ return packfile_error("delta offset is zero");
+
+ *delta_base_out = base_offset;
+ return 0;
+}
+
+/***********************************************************
+ *
+ * PACKFILE METHODS
+ *
+ ***********************************************************/
+
+void git_packfile_free(struct git_pack_file *p, bool unlink_packfile)
+{
+ bool locked = true;
+
+ if (!p)
+ return;
+
+ cache_free(&p->bases);
+
+ if (git_mutex_lock(&p->lock) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to lock packfile");
+ locked = false;
+ }
+ if (p->mwf.fd >= 0) {
+ git_mwindow_free_all(&p->mwf);
+ p_close(p->mwf.fd);
+ p->mwf.fd = -1;
+ }
+ if (locked)
+ git_mutex_unlock(&p->lock);
+
+ if (unlink_packfile)
+ p_unlink(p->pack_name);
+
+ pack_index_free(p);
+
+ git__free(p->bad_object_ids);
+
+ git_mutex_free(&p->bases.lock);
+ git_mutex_free(&p->mwf.lock);
+ git_mutex_free(&p->lock);
+ git__free(p);
+}
+
+/* Run with the packfile and mwf locks held */
+static int packfile_open_locked(struct git_pack_file *p)
+{
+ struct stat st;
+ struct git_pack_header hdr;
+ unsigned char checksum[GIT_OID_MAX_SIZE];
+ unsigned char *idx_checksum;
+
+ if (pack_index_open_locked(p) < 0)
+ return git_odb__error_notfound("failed to open packfile", NULL, 0);
+
+ if (p->mwf.fd >= 0)
+ return 0;
+
+ /* TODO: open with noatime */
+ p->mwf.fd = git_futils_open_ro(p->pack_name);
+ if (p->mwf.fd < 0)
+ goto cleanup;
+
+ if (p_fstat(p->mwf.fd, &st) < 0) {
+ git_error_set(GIT_ERROR_OS, "could not stat packfile");
+ goto cleanup;
+ }
+
+ /* If we created the struct before we had the pack we lack size. */
+ if (!p->mwf.size) {
+ if (!S_ISREG(st.st_mode))
+ goto cleanup;
+ p->mwf.size = (off64_t)st.st_size;
+ } else if (p->mwf.size != st.st_size)
+ goto cleanup;
+
+#if 0
+ /* We leave these file descriptors open with sliding mmap;
+ * there is no point keeping them open across exec(), though.
+ */
+ fd_flag = fcntl(p->mwf.fd, F_GETFD, 0);
+ if (fd_flag < 0)
+ goto cleanup;
+
+ fd_flag |= FD_CLOEXEC;
+ if (fcntl(p->pack_fd, F_SETFD, fd_flag) == -1)
+ goto cleanup;
+#endif
+
+ /* Verify we recognize this pack file format. */
+ if (p_read(p->mwf.fd, &hdr, sizeof(hdr)) < 0 ||
+ hdr.hdr_signature != htonl(PACK_SIGNATURE) ||
+ !pack_version_ok(hdr.hdr_version))
+ goto cleanup;
+
+ /* Verify the pack matches its index. */
+ if (p->num_objects != ntohl(hdr.hdr_entries) ||
+ p_pread(p->mwf.fd, checksum, p->oid_size, p->mwf.size - p->oid_size) < 0)
+ goto cleanup;
+
+ idx_checksum = ((unsigned char *)p->index_map.data) +
+ p->index_map.len - (p->oid_size * 2);
+
+ if (git_oid_raw_cmp(checksum, idx_checksum, p->oid_size) != 0)
+ goto cleanup;
+
+ if (git_mwindow_file_register(&p->mwf) < 0)
+ goto cleanup;
+
+ return 0;
+
+cleanup:
+ git_error_set(GIT_ERROR_OS, "invalid packfile '%s'", p->pack_name);
+
+ if (p->mwf.fd >= 0)
+ p_close(p->mwf.fd);
+ p->mwf.fd = -1;
+
+ return -1;
+}
+
+int git_packfile__name(char **out, const char *path)
+{
+ size_t path_len;
+ git_str buf = GIT_STR_INIT;
+
+ path_len = strlen(path);
+
+ if (path_len < strlen(".idx"))
+ return git_odb__error_notfound("invalid packfile path", NULL, 0);
+
+ if (git_str_printf(&buf, "%.*s.pack", (int)(path_len - strlen(".idx")), path) < 0)
+ return -1;
+
+ *out = git_str_detach(&buf);
+ return 0;
+}
+
+int git_packfile_alloc(
+ struct git_pack_file **pack_out,
+ const char *path,
+ git_oid_t oid_type)
+{
+ struct stat st;
+ struct git_pack_file *p;
+ size_t path_len = path ? strlen(path) : 0, alloc_len;
+
+ *pack_out = NULL;
+
+ if (path_len < strlen(".idx"))
+ return git_odb__error_notfound("invalid packfile path", NULL, 0);
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, sizeof(*p), path_len);
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2);
+
+ p = git__calloc(1, alloc_len);
+ GIT_ERROR_CHECK_ALLOC(p);
+
+ memcpy(p->pack_name, path, path_len + 1);
+
+ /*
+ * Make sure a corresponding .pack file exists and that
+ * the index looks sane.
+ */
+ if (git__suffixcmp(path, ".idx") == 0) {
+ size_t root_len = path_len - strlen(".idx");
+
+ if (!git_disable_pack_keep_file_checks) {
+ memcpy(p->pack_name + root_len, ".keep", sizeof(".keep"));
+ if (git_fs_path_exists(p->pack_name) == true)
+ p->pack_keep = 1;
+ }
+
+ memcpy(p->pack_name + root_len, ".pack", sizeof(".pack"));
+ }
+
+ if (p_stat(p->pack_name, &st) < 0 || !S_ISREG(st.st_mode)) {
+ git__free(p);
+ return git_odb__error_notfound("packfile not found", NULL, 0);
+ }
+
+ /* ok, it looks sane as far as we can check without
+ * actually mapping the pack file.
+ */
+ p->mwf.fd = -1;
+ p->mwf.size = st.st_size;
+ p->pack_local = 1;
+ p->mtime = (git_time_t)st.st_mtime;
+ p->index_version = -1;
+ p->oid_type = oid_type ? oid_type : GIT_OID_DEFAULT;
+ p->oid_size = (unsigned int)git_oid_size(p->oid_type);
+ p->oid_hexsize = (unsigned int)git_oid_hexsize(p->oid_type);
+
+ if (git_mutex_init(&p->lock) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to initialize packfile mutex");
+ git__free(p);
+ return -1;
+ }
+
+ if (git_mutex_init(&p->mwf.lock) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to initialize packfile window mutex");
+ git_mutex_free(&p->lock);
+ git__free(p);
+ return -1;
+ }
+
+ if (cache_init(&p->bases) < 0) {
+ git_mutex_free(&p->mwf.lock);
+ git_mutex_free(&p->lock);
+ git__free(p);
+ return -1;
+ }
+
+ *pack_out = p;
+
+ return 0;
+}
+
+/***********************************************************
+ *
+ * PACKFILE ENTRY SEARCH INTERNALS
+ *
+ ***********************************************************/
+
+static off64_t nth_packed_object_offset_locked(struct git_pack_file *p, uint32_t n)
+{
+ const unsigned char *index, *end;
+ uint32_t off32;
+
+ index = p->index_map.data;
+ end = index + p->index_map.len;
+ index += 4 * 256;
+ if (p->index_version == 1)
+ return ntohl(*((uint32_t *)(index + (p->oid_size + 4) * (size_t) n)));
+
+ index += 8 + (size_t) p->num_objects * (p->oid_size + 4);
+ off32 = ntohl(*((uint32_t *)(index + 4 * n)));
+ if (!(off32 & 0x80000000))
+ return off32;
+ index += (size_t) p->num_objects * 4 + (off32 & 0x7fffffff) * 8;
+
+ /* Make sure we're not being sent out of bounds */
+ if (index >= end - 8)
+ return -1;
+
+ return (((uint64_t)ntohl(*((uint32_t *)(index + 0)))) << 32) |
+ ntohl(*((uint32_t *)(index + 4)));
+}
+
+static int git__memcmp4(const void *a, const void *b) {
+ return memcmp(a, b, 4);
+}
+
+int git_pack_foreach_entry(
+ struct git_pack_file *p,
+ git_odb_foreach_cb cb,
+ void *data)
+{
+ const unsigned char *index, *current;
+ uint32_t i;
+ int error = 0;
+ git_array_oid_t oids = GIT_ARRAY_INIT;
+ git_oid *oid;
+
+ if (git_mutex_lock(&p->lock) < 0)
+ return packfile_error("failed to get lock for git_pack_foreach_entry");
+
+ if ((error = pack_index_open_locked(p)) < 0) {
+ git_mutex_unlock(&p->lock);
+ return error;
+ }
+
+ if (!p->index_map.data) {
+ git_error_set(GIT_ERROR_INTERNAL, "internal error: p->index_map.data == NULL");
+ git_mutex_unlock(&p->lock);
+ return -1;
+ }
+
+ index = p->index_map.data;
+
+ if (p->index_version > 1)
+ index += 8;
+
+ index += 4 * 256;
+
+ if (p->ids == NULL) {
+ git_vector offsets, oids;
+
+ if ((error = git_vector_init(&oids, p->num_objects, NULL))) {
+ git_mutex_unlock(&p->lock);
+ return error;
+ }
+
+ if ((error = git_vector_init(&offsets, p->num_objects, git__memcmp4))) {
+ git_mutex_unlock(&p->lock);
+ return error;
+ }
+
+ if (p->index_version > 1) {
+ const unsigned char *off = index +
+ (p->oid_size + 4) * p->num_objects;
+
+ for (i = 0; i < p->num_objects; i++)
+ git_vector_insert(&offsets, (void*)&off[4 * i]);
+
+ git_vector_sort(&offsets);
+ git_vector_foreach(&offsets, i, current)
+ git_vector_insert(&oids, (void*)&index[5 * (current - off)]);
+ } else {
+ for (i = 0; i < p->num_objects; i++)
+ git_vector_insert(&offsets, (void*)&index[(p->oid_size + 4) * i]);
+ git_vector_sort(&offsets);
+ git_vector_foreach(&offsets, i, current)
+ git_vector_insert(&oids, (void*)&current[4]);
+ }
+
+ git_vector_free(&offsets);
+ p->ids = (unsigned char **)git_vector_detach(NULL, NULL, &oids);
+ }
+
+ /*
+ * We need to copy the OIDs to another array before we
+ * relinquish the lock to avoid races. We can also take
+ * this opportunity to put them into normal form.
+ */
+ git_array_init_to_size(oids, p->num_objects);
+ if (!oids.ptr) {
+ git_mutex_unlock(&p->lock);
+ git_array_clear(oids);
+ GIT_ERROR_CHECK_ARRAY(oids);
+ }
+ for (i = 0; i < p->num_objects; i++) {
+ oid = git_array_alloc(oids);
+ if (!oid) {
+ git_mutex_unlock(&p->lock);
+ git_array_clear(oids);
+ GIT_ERROR_CHECK_ALLOC(oid);
+ }
+ git_oid__fromraw(oid, p->ids[i], p->oid_type);
+ }
+
+ git_mutex_unlock(&p->lock);
+
+ git_array_foreach(oids, i, oid) {
+ if ((error = cb(oid, data)) != 0) {
+ git_error_set_after_callback(error);
+ break;
+ }
+ }
+
+ git_array_clear(oids);
+ return error;
+}
+
+int git_pack_foreach_entry_offset(
+ struct git_pack_file *p,
+ git_pack_foreach_entry_offset_cb cb,
+ void *data)
+{
+ const unsigned char *index;
+ off64_t current_offset;
+ git_oid current_oid;
+ uint32_t i;
+ int error = 0;
+
+ if (git_mutex_lock(&p->lock) < 0)
+ return packfile_error("failed to get lock for git_pack_foreach_entry_offset");
+
+ index = p->index_map.data;
+ if (index == NULL) {
+ if ((error = pack_index_open_locked(p)) < 0)
+ goto cleanup;
+
+ if (!p->index_map.data) {
+ git_error_set(GIT_ERROR_INTERNAL, "internal error: p->index_map.data == NULL");
+ goto cleanup;
+ }
+
+ index = p->index_map.data;
+ }
+
+ if (p->index_version > 1)
+ index += 8;
+
+ index += 4 * 256;
+
+ /* all offsets should have been validated by pack_index_check_locked */
+ if (p->index_version > 1) {
+ const unsigned char *offsets = index +
+ (p->oid_size + 4) * p->num_objects;
+ const unsigned char *large_offset_ptr;
+ const unsigned char *large_offsets = index +
+ (p->oid_size + 8) * p->num_objects;
+ const unsigned char *large_offsets_end = ((const unsigned char *)p->index_map.data) + p->index_map.len - p->oid_size;
+
+ for (i = 0; i < p->num_objects; i++) {
+ current_offset = ntohl(*(const uint32_t *)(offsets + 4 * i));
+ if (current_offset & 0x80000000) {
+ large_offset_ptr = large_offsets + (current_offset & 0x7fffffff) * 8;
+ if (large_offset_ptr >= large_offsets_end) {
+ error = packfile_error("invalid large offset");
+ goto cleanup;
+ }
+ current_offset = (((off64_t)ntohl(*((uint32_t *)(large_offset_ptr + 0)))) << 32) |
+ ntohl(*((uint32_t *)(large_offset_ptr + 4)));
+ }
+
+ git_oid__fromraw(&current_oid, (index + p->oid_size * i), p->oid_type);
+ if ((error = cb(&current_oid, current_offset, data)) != 0) {
+ error = git_error_set_after_callback(error);
+ goto cleanup;
+ }
+ }
+ } else {
+ for (i = 0; i < p->num_objects; i++) {
+ current_offset = ntohl(*(const uint32_t *)(index + (p->oid_size + 4) * i));
+ git_oid__fromraw(&current_oid, (index + (p->oid_size + 4) * i + 4), p->oid_type);
+ if ((error = cb(&current_oid, current_offset, data)) != 0) {
+ error = git_error_set_after_callback(error);
+ goto cleanup;
+ }
+ }
+ }
+
+cleanup:
+ git_mutex_unlock(&p->lock);
+ return error;
+}
+
+int git_pack__lookup_id(
+ const void *oid_lookup_table,
+ size_t stride,
+ unsigned lo,
+ unsigned hi,
+ const unsigned char *oid_prefix,
+ const git_oid_t oid_type)
+{
+ const unsigned char *base = oid_lookup_table;
+ size_t oid_size = git_oid_size(oid_type);
+
+ while (lo < hi) {
+ unsigned mi = (lo + hi) / 2;
+ int cmp = git_oid_raw_cmp(base + mi * stride, oid_prefix, oid_size);
+
+ if (!cmp)
+ return mi;
+
+ if (cmp > 0)
+ hi = mi;
+ else
+ lo = mi+1;
+ }
+
+ return -((int)lo)-1;
+}
+
+static int pack_entry_find_offset(
+ off64_t *offset_out,
+ git_oid *found_oid,
+ struct git_pack_file *p,
+ const git_oid *short_oid,
+ size_t len)
+{
+ const uint32_t *level1_ofs;
+ const unsigned char *index;
+ unsigned hi, lo, stride;
+ int pos, found = 0;
+ off64_t offset;
+ const unsigned char *current = 0;
+ int error = 0;
+
+ *offset_out = 0;
+
+ if (git_mutex_lock(&p->lock) < 0)
+ return packfile_error("failed to get lock for pack_entry_find_offset");
+
+ if ((error = pack_index_open_locked(p)) < 0)
+ goto cleanup;
+
+ if (!p->index_map.data) {
+ git_error_set(GIT_ERROR_INTERNAL, "internal error: p->index_map.data == NULL");
+ goto cleanup;
+ }
+
+ index = p->index_map.data;
+ level1_ofs = p->index_map.data;
+
+ if (p->index_version > 1) {
+ level1_ofs += 2;
+ index += 8;
+ }
+
+ index += 4 * 256;
+ hi = ntohl(level1_ofs[(int)short_oid->id[0]]);
+ lo = ((short_oid->id[0] == 0x0) ? 0 : ntohl(level1_ofs[(int)short_oid->id[0] - 1]));
+
+ if (p->index_version > 1) {
+ stride = p->oid_size;
+ } else {
+ stride = p->oid_size + 4;
+ index += 4;
+ }
+
+#ifdef INDEX_DEBUG_LOOKUP
+ printf("%02x%02x%02x... lo %u hi %u nr %d\n",
+ short_oid->id[0], short_oid->id[1], short_oid->id[2], lo, hi, p->num_objects);
+#endif
+
+ pos = git_pack__lookup_id(index, stride, lo, hi,
+ short_oid->id, p->oid_type);
+
+ if (pos >= 0) {
+ /* An object matching exactly the oid was found */
+ found = 1;
+ current = index + pos * stride;
+ } else {
+ /* No object was found */
+ /* pos refers to the object with the "closest" oid to short_oid */
+ pos = - 1 - pos;
+ if (pos < (int)p->num_objects) {
+ current = index + pos * stride;
+
+ if (!git_oid_raw_ncmp(short_oid->id, current, len))
+ found = 1;
+ }
+ }
+
+ if (found &&
+ len != p->oid_hexsize &&
+ pos + 1 < (int)p->num_objects) {
+ /* Check for ambiguousity */
+ const unsigned char *next = current + stride;
+
+ if (!git_oid_raw_ncmp(short_oid->id, next, len)) {
+ found = 2;
+ }
+ }
+
+ if (!found) {
+ error = git_odb__error_notfound("failed to find offset for pack entry", short_oid, len);
+ goto cleanup;
+ }
+ if (found > 1) {
+ error = git_odb__error_ambiguous("found multiple offsets for pack entry");
+ goto cleanup;
+ }
+
+ if ((offset = nth_packed_object_offset_locked(p, pos)) < 0) {
+ git_error_set(GIT_ERROR_ODB, "packfile index is corrupt");
+ error = -1;
+ goto cleanup;
+ }
+
+ *offset_out = offset;
+ git_oid__fromraw(found_oid, current, p->oid_type);
+
+#ifdef INDEX_DEBUG_LOOKUP
+ {
+ char hex_sha1[p->oid_hexsize + 1];
+ git_oid_fmt(hex_sha1, found_oid);
+ hex_sha1[p->oid_hexsize] = '\0';
+ printf("found lo=%d %s\n", lo, hex_sha1);
+ }
+#endif
+
+cleanup:
+ git_mutex_unlock(&p->lock);
+ return error;
+}
+
+int git_pack_entry_find(
+ struct git_pack_entry *e,
+ struct git_pack_file *p,
+ const git_oid *short_oid,
+ size_t len)
+{
+ off64_t offset;
+ git_oid found_oid;
+ int error;
+
+ GIT_ASSERT_ARG(p);
+
+ if (len == p->oid_hexsize && p->num_bad_objects) {
+ unsigned i;
+ for (i = 0; i < p->num_bad_objects; i++)
+ if (git_oid__cmp(short_oid, &p->bad_object_ids[i]) == 0)
+ return packfile_error("bad object found in packfile");
+ }
+
+ error = pack_entry_find_offset(&offset, &found_oid, p, short_oid, len);
+ if (error < 0)
+ return error;
+
+ error = git_mutex_lock(&p->lock);
+ if (error < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to lock packfile reader");
+ return error;
+ }
+ error = git_mutex_lock(&p->mwf.lock);
+ if (error < 0) {
+ git_mutex_unlock(&p->lock);
+ git_error_set(GIT_ERROR_OS, "failed to lock packfile reader");
+ return error;
+ }
+
+ /* we found a unique entry in the index;
+ * make sure the packfile backing the index
+ * still exists on disk */
+ if (p->mwf.fd == -1)
+ error = packfile_open_locked(p);
+ git_mutex_unlock(&p->mwf.lock);
+ git_mutex_unlock(&p->lock);
+ if (error < 0)
+ return error;
+
+ e->offset = offset;
+ e->p = p;
+
+ git_oid_cpy(&e->id, &found_oid);
+ return 0;
+}
diff --git a/src/libgit2/pack.h b/src/libgit2/pack.h
new file mode 100644
index 0000000..1a9eb14
--- /dev/null
+++ b/src/libgit2/pack.h
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_pack_h__
+#define INCLUDE_pack_h__
+
+#include "common.h"
+
+#include "git2/oid.h"
+
+#include "array.h"
+#include "map.h"
+#include "mwindow.h"
+#include "odb.h"
+#include "offmap.h"
+#include "oidmap.h"
+#include "zstream.h"
+#include "oid.h"
+
+/**
+ * Function type for callbacks from git_pack_foreach_entry_offset.
+ */
+typedef int git_pack_foreach_entry_offset_cb(
+ const git_oid *id,
+ off64_t offset,
+ void *payload);
+
+#define GIT_PACK_FILE_MODE 0444
+
+#define PACK_SIGNATURE 0x5041434b /* "PACK" */
+#define PACK_VERSION 2
+#define pack_version_ok(v) ((v) == htonl(2))
+struct git_pack_header {
+ uint32_t hdr_signature;
+ uint32_t hdr_version;
+ uint32_t hdr_entries;
+};
+
+/*
+ * The first four bytes of index formats later than version 1 should
+ * start with this signature, as all older git binaries would find this
+ * value illegal and abort reading the file.
+ *
+ * This is the case because the number of objects in a packfile
+ * cannot exceed 1,431,660,000 as every object would need at least
+ * 3 bytes of data and the overall packfile cannot exceed 4 GiB with
+ * version 1 of the index file due to the offsets limited to 32 bits.
+ * Clearly the signature exceeds this maximum.
+ *
+ * Very old git binaries will also compare the first 4 bytes to the
+ * next 4 bytes in the index and abort with a "non-monotonic index"
+ * error if the second 4 byte word is smaller than the first 4
+ * byte word. This would be true in the proposed future index
+ * format as idx_signature would be greater than idx_version.
+ */
+
+#define PACK_IDX_SIGNATURE 0xff744f63 /* "\377tOc" */
+
+struct git_pack_idx_header {
+ uint32_t idx_signature;
+ uint32_t idx_version;
+};
+
+typedef struct git_pack_cache_entry {
+ size_t last_usage; /* enough? */
+ git_atomic32 refcount;
+ git_rawobj raw;
+} git_pack_cache_entry;
+
+struct pack_chain_elem {
+ off64_t base_key;
+ off64_t offset;
+ size_t size;
+ git_object_t type;
+};
+
+typedef git_array_t(struct pack_chain_elem) git_dependency_chain;
+
+#define GIT_PACK_CACHE_MEMORY_LIMIT 16 * 1024 * 1024
+#define GIT_PACK_CACHE_SIZE_LIMIT 1024 * 1024 /* don't bother caching anything over 1MB */
+
+typedef struct {
+ size_t memory_used;
+ size_t memory_limit;
+ size_t use_ctr;
+ git_mutex lock;
+ git_offmap *entries;
+} git_pack_cache;
+
+struct git_pack_file {
+ git_mwindow_file mwf;
+ git_map index_map;
+ git_mutex lock; /* protect updates to index_map */
+ git_atomic32 refcount;
+
+ uint32_t num_objects;
+ uint32_t num_bad_objects;
+ git_oid *bad_object_ids; /* array of git_oid */
+
+ git_oid_t oid_type;
+ unsigned oid_hexsize:7,
+ oid_size:6,
+ pack_local:1,
+ pack_keep:1,
+ has_cache:1;
+
+ int index_version;
+ git_time_t mtime;
+ git_oidmap *idx_cache;
+ unsigned char **ids;
+
+ git_pack_cache bases; /* delta base cache */
+
+ time_t last_freshen; /* last time the packfile was freshened */
+
+ /* something like ".git/objects/pack/xxxxx.pack" */
+ char pack_name[GIT_FLEX_ARRAY]; /* more */
+};
+
+/**
+ * Return the position where an OID (or a prefix) would be inserted within
+ * the OID Lookup Table of an .idx file. This performs binary search
+ * between the lo and hi indices.
+ *
+ * The stride parameter is provided because .idx files version 1 store the
+ * OIDs interleaved with the 4-byte file offsets of the objects within the
+ * .pack file (stride = oid_size + 4), whereas files with version 2 store
+ * them in a contiguous flat array (stride = oid_size).
+ */
+int git_pack__lookup_id(
+ const void *id_lookup_table,
+ size_t stride,
+ unsigned lo,
+ unsigned hi,
+ const unsigned char *id_prefix,
+ const git_oid_t oid_type);
+
+struct git_pack_entry {
+ off64_t offset;
+ git_oid id;
+ struct git_pack_file *p;
+};
+
+typedef struct git_packfile_stream {
+ off64_t curpos;
+ int done;
+ git_zstream zstream;
+ struct git_pack_file *p;
+ git_mwindow *mw;
+} git_packfile_stream;
+
+int git_packfile__object_header(size_t *out, unsigned char *hdr, size_t size, git_object_t type);
+
+int git_packfile__name(char **out, const char *path);
+
+int git_packfile_unpack_header(
+ size_t *size_p,
+ git_object_t *type_p,
+ struct git_pack_file *p,
+ git_mwindow **w_curs,
+ off64_t *curpos);
+
+int git_packfile_resolve_header(
+ size_t *size_p,
+ git_object_t *type_p,
+ struct git_pack_file *p,
+ off64_t offset);
+
+int git_packfile_unpack(git_rawobj *obj, struct git_pack_file *p, off64_t *obj_offset);
+
+int git_packfile_stream_open(git_packfile_stream *obj, struct git_pack_file *p, off64_t curpos);
+ssize_t git_packfile_stream_read(git_packfile_stream *obj, void *buffer, size_t len);
+void git_packfile_stream_dispose(git_packfile_stream *obj);
+
+int get_delta_base(
+ off64_t *delta_base_out,
+ struct git_pack_file *p,
+ git_mwindow **w_curs,
+ off64_t *curpos,
+ git_object_t type,
+ off64_t delta_obj_offset);
+
+void git_packfile_free(struct git_pack_file *p, bool unlink_packfile);
+int git_packfile_alloc(
+ struct git_pack_file **pack_out,
+ const char *path,
+ git_oid_t oid_type);
+
+int git_pack_entry_find(
+ struct git_pack_entry *e,
+ struct git_pack_file *p,
+ const git_oid *short_id,
+ size_t len);
+int git_pack_foreach_entry(
+ struct git_pack_file *p,
+ git_odb_foreach_cb cb,
+ void *data);
+/**
+ * Similar to git_pack_foreach_entry, but:
+ * - It also provides the offset of the object within the
+ * packfile.
+ * - It does not sort the objects in any order.
+ * - It retains the lock while invoking the callback.
+ */
+int git_pack_foreach_entry_offset(
+ struct git_pack_file *p,
+ git_pack_foreach_entry_offset_cb cb,
+ void *data);
+
+#endif
diff --git a/src/libgit2/parse.c b/src/libgit2/parse.c
new file mode 100644
index 0000000..9eb86a3
--- /dev/null
+++ b/src/libgit2/parse.c
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#include "parse.h"
+#include "oid.h"
+
+int git_parse_ctx_init(git_parse_ctx *ctx, const char *content, size_t content_len)
+{
+ if (content && content_len) {
+ ctx->content = content;
+ ctx->content_len = content_len;
+ } else {
+ ctx->content = "";
+ ctx->content_len = 0;
+ }
+
+ ctx->remain = ctx->content;
+ ctx->remain_len = ctx->content_len;
+ ctx->line = ctx->remain;
+ ctx->line_len = git__linenlen(ctx->line, ctx->remain_len);
+ ctx->line_num = 1;
+
+ return 0;
+}
+
+void git_parse_ctx_clear(git_parse_ctx *ctx)
+{
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->content = "";
+}
+
+void git_parse_advance_line(git_parse_ctx *ctx)
+{
+ ctx->line += ctx->line_len;
+ ctx->remain_len -= ctx->line_len;
+ ctx->line_len = git__linenlen(ctx->line, ctx->remain_len);
+ ctx->line_num++;
+}
+
+void git_parse_advance_chars(git_parse_ctx *ctx, size_t char_cnt)
+{
+ ctx->line += char_cnt;
+ ctx->remain_len -= char_cnt;
+ ctx->line_len -= char_cnt;
+}
+
+int git_parse_advance_expected(
+ git_parse_ctx *ctx,
+ const char *expected,
+ size_t expected_len)
+{
+ if (ctx->line_len < expected_len)
+ return -1;
+
+ if (memcmp(ctx->line, expected, expected_len) != 0)
+ return -1;
+
+ git_parse_advance_chars(ctx, expected_len);
+ return 0;
+}
+
+int git_parse_advance_ws(git_parse_ctx *ctx)
+{
+ int ret = -1;
+
+ while (ctx->line_len > 0 &&
+ ctx->line[0] != '\n' &&
+ git__isspace(ctx->line[0])) {
+ ctx->line++;
+ ctx->line_len--;
+ ctx->remain_len--;
+ ret = 0;
+ }
+
+ return ret;
+}
+
+int git_parse_advance_nl(git_parse_ctx *ctx)
+{
+ if (ctx->line_len != 1 || ctx->line[0] != '\n')
+ return -1;
+
+ git_parse_advance_line(ctx);
+ return 0;
+}
+
+int git_parse_advance_digit(int64_t *out, git_parse_ctx *ctx, int base)
+{
+ const char *end;
+ int ret;
+
+ if (ctx->line_len < 1 || !git__isdigit(ctx->line[0]))
+ return -1;
+
+ if ((ret = git__strntol64(out, ctx->line, ctx->line_len, &end, base)) < 0)
+ return -1;
+
+ git_parse_advance_chars(ctx, (end - ctx->line));
+ return 0;
+}
+
+int git_parse_advance_oid(git_oid *out, git_parse_ctx *ctx, git_oid_t oid_type)
+{
+ size_t oid_hexsize = git_oid_hexsize(oid_type);
+ GIT_ASSERT(oid_hexsize);
+
+ if (ctx->line_len < oid_hexsize)
+ return -1;
+ if ((git_oid__fromstrn(out, ctx->line, oid_hexsize, oid_type)) < 0)
+ return -1;
+ git_parse_advance_chars(ctx, oid_hexsize);
+ return 0;
+}
+
+int git_parse_peek(char *out, git_parse_ctx *ctx, int flags)
+{
+ size_t remain = ctx->line_len;
+ const char *ptr = ctx->line;
+
+ while (remain) {
+ char c = *ptr;
+
+ if ((flags & GIT_PARSE_PEEK_SKIP_WHITESPACE) &&
+ git__isspace(c)) {
+ remain--;
+ ptr++;
+ continue;
+ }
+
+ *out = c;
+ return 0;
+ }
+
+ return -1;
+}
diff --git a/src/libgit2/parse.h b/src/libgit2/parse.h
new file mode 100644
index 0000000..beef1de
--- /dev/null
+++ b/src/libgit2/parse.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_parse_h__
+#define INCLUDE_parse_h__
+
+#include "common.h"
+
+typedef struct {
+ /* Original content buffer */
+ const char *content;
+ size_t content_len;
+
+ /* The remaining (unparsed) buffer */
+ const char *remain;
+ size_t remain_len;
+
+ const char *line;
+ size_t line_len;
+ size_t line_num;
+} git_parse_ctx;
+
+#define GIT_PARSE_CTX_INIT { 0 }
+
+int git_parse_ctx_init(git_parse_ctx *ctx, const char *content, size_t content_len);
+void git_parse_ctx_clear(git_parse_ctx *ctx);
+
+#define git_parse_ctx_contains_s(ctx, str) \
+ git_parse_ctx_contains(ctx, str, sizeof(str) - 1)
+
+GIT_INLINE(bool) git_parse_ctx_contains(
+ git_parse_ctx *ctx, const char *str, size_t len)
+{
+ return (ctx->line_len >= len && memcmp(ctx->line, str, len) == 0);
+}
+
+void git_parse_advance_line(git_parse_ctx *ctx);
+void git_parse_advance_chars(git_parse_ctx *ctx, size_t char_cnt);
+int git_parse_advance_expected(
+ git_parse_ctx *ctx,
+ const char *expected,
+ size_t expected_len);
+
+#define git_parse_advance_expected_str(ctx, str) \
+ git_parse_advance_expected(ctx, str, strlen(str))
+
+int git_parse_advance_ws(git_parse_ctx *ctx);
+int git_parse_advance_nl(git_parse_ctx *ctx);
+int git_parse_advance_digit(int64_t *out, git_parse_ctx *ctx, int base);
+int git_parse_advance_oid(git_oid *out, git_parse_ctx *ctx, git_oid_t oid_type);
+
+enum GIT_PARSE_PEEK_FLAGS {
+ GIT_PARSE_PEEK_SKIP_WHITESPACE = (1 << 0)
+};
+
+int git_parse_peek(char *out, git_parse_ctx *ctx, int flags);
+
+#endif
diff --git a/src/libgit2/patch.c b/src/libgit2/patch.c
new file mode 100644
index 0000000..a30546f
--- /dev/null
+++ b/src/libgit2/patch.c
@@ -0,0 +1,230 @@
+/*
+* Copyright (C) the libgit2 contributors. All rights reserved.
+*
+* This file is part of libgit2, distributed under the GNU GPL v2 with
+* a Linking Exception. For full terms see the included COPYING file.
+*/
+
+#include "patch.h"
+
+#include "git2/patch.h"
+#include "diff.h"
+
+int git_patch__invoke_callbacks(
+ git_patch *patch,
+ git_diff_file_cb file_cb,
+ git_diff_binary_cb binary_cb,
+ git_diff_hunk_cb hunk_cb,
+ git_diff_line_cb line_cb,
+ void *payload)
+{
+ int error = 0;
+ uint32_t i, j;
+
+ if (file_cb)
+ error = file_cb(patch->delta, 0, payload);
+
+ if (error)
+ return error;
+
+ if ((patch->delta->flags & GIT_DIFF_FLAG_BINARY) != 0) {
+ if (binary_cb)
+ error = binary_cb(patch->delta, &patch->binary, payload);
+
+ return error;
+ }
+
+ if (!hunk_cb && !line_cb)
+ return error;
+
+ for (i = 0; !error && i < git_array_size(patch->hunks); ++i) {
+ git_patch_hunk *h = git_array_get(patch->hunks, i);
+
+ if (hunk_cb)
+ error = hunk_cb(patch->delta, &h->hunk, payload);
+
+ if (!line_cb)
+ continue;
+
+ for (j = 0; !error && j < h->line_count; ++j) {
+ git_diff_line *l =
+ git_array_get(patch->lines, h->line_start + j);
+
+ error = line_cb(patch->delta, &h->hunk, l, payload);
+ }
+ }
+
+ return error;
+}
+
+size_t git_patch_size(
+ git_patch *patch,
+ int include_context,
+ int include_hunk_headers,
+ int include_file_headers)
+{
+ size_t out;
+
+ GIT_ASSERT_ARG(patch);
+
+ out = patch->content_size;
+
+ if (!include_context)
+ out -= patch->context_size;
+
+ if (include_hunk_headers)
+ out += patch->header_size;
+
+ if (include_file_headers) {
+ git_str file_header = GIT_STR_INIT;
+
+ if (git_diff_delta__format_file_header(
+ &file_header, patch->delta, NULL, NULL, 0, true) < 0)
+ git_error_clear();
+ else
+ out += git_str_len(&file_header);
+
+ git_str_dispose(&file_header);
+ }
+
+ return out;
+}
+
+int git_patch_line_stats(
+ size_t *total_ctxt,
+ size_t *total_adds,
+ size_t *total_dels,
+ const git_patch *patch)
+{
+ size_t totals[3], idx;
+
+ memset(totals, 0, sizeof(totals));
+
+ for (idx = 0; idx < git_array_size(patch->lines); ++idx) {
+ git_diff_line *line = git_array_get(patch->lines, idx);
+ if (!line)
+ continue;
+
+ switch (line->origin) {
+ case GIT_DIFF_LINE_CONTEXT: totals[0]++; break;
+ case GIT_DIFF_LINE_ADDITION: totals[1]++; break;
+ case GIT_DIFF_LINE_DELETION: totals[2]++; break;
+ default:
+ /* diff --stat and --numstat don't count EOFNL marks because
+ * they will always be paired with a ADDITION or DELETION line.
+ */
+ break;
+ }
+ }
+
+ if (total_ctxt)
+ *total_ctxt = totals[0];
+ if (total_adds)
+ *total_adds = totals[1];
+ if (total_dels)
+ *total_dels = totals[2];
+
+ return 0;
+}
+
+const git_diff_delta *git_patch_get_delta(const git_patch *patch)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(patch, NULL);
+ return patch->delta;
+}
+
+size_t git_patch_num_hunks(const git_patch *patch)
+{
+ GIT_ASSERT_ARG(patch);
+ return git_array_size(patch->hunks);
+}
+
+static int patch_error_outofrange(const char *thing)
+{
+ git_error_set(GIT_ERROR_INVALID, "patch %s index out of range", thing);
+ return GIT_ENOTFOUND;
+}
+
+int git_patch_get_hunk(
+ const git_diff_hunk **out,
+ size_t *lines_in_hunk,
+ git_patch *patch,
+ size_t hunk_idx)
+{
+ git_patch_hunk *hunk;
+ GIT_ASSERT_ARG(patch);
+
+ hunk = git_array_get(patch->hunks, hunk_idx);
+
+ if (!hunk) {
+ if (out) *out = NULL;
+ if (lines_in_hunk) *lines_in_hunk = 0;
+ return patch_error_outofrange("hunk");
+ }
+
+ if (out) *out = &hunk->hunk;
+ if (lines_in_hunk) *lines_in_hunk = hunk->line_count;
+ return 0;
+}
+
+int git_patch_num_lines_in_hunk(const git_patch *patch, size_t hunk_idx)
+{
+ git_patch_hunk *hunk;
+ GIT_ASSERT_ARG(patch);
+
+ if (!(hunk = git_array_get(patch->hunks, hunk_idx)))
+ return patch_error_outofrange("hunk");
+ return (int)hunk->line_count;
+}
+
+int git_patch_get_line_in_hunk(
+ const git_diff_line **out,
+ git_patch *patch,
+ size_t hunk_idx,
+ size_t line_of_hunk)
+{
+ git_patch_hunk *hunk;
+ git_diff_line *line;
+
+ GIT_ASSERT_ARG(patch);
+
+ if (!(hunk = git_array_get(patch->hunks, hunk_idx))) {
+ if (out) *out = NULL;
+ return patch_error_outofrange("hunk");
+ }
+
+ if (line_of_hunk >= hunk->line_count ||
+ !(line = git_array_get(
+ patch->lines, hunk->line_start + line_of_hunk))) {
+ if (out) *out = NULL;
+ return patch_error_outofrange("line");
+ }
+
+ if (out) *out = line;
+ return 0;
+}
+
+git_repository *git_patch_owner(const git_patch *patch)
+{
+ return patch->repo;
+}
+
+int git_patch_from_diff(git_patch **out, git_diff *diff, size_t idx)
+{
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(diff);
+ GIT_ASSERT_ARG(diff->patch_fn);
+ return diff->patch_fn(out, diff, idx);
+}
+
+static void git_patch__free(git_patch *patch)
+{
+ if (patch->free_fn)
+ patch->free_fn(patch);
+}
+
+void git_patch_free(git_patch *patch)
+{
+ if (patch)
+ GIT_REFCOUNT_DEC(patch, git_patch__free);
+}
diff --git a/src/libgit2/patch.h b/src/libgit2/patch.h
new file mode 100644
index 0000000..86328e8
--- /dev/null
+++ b/src/libgit2/patch.h
@@ -0,0 +1,75 @@
+/*
+* Copyright (C) the libgit2 contributors. All rights reserved.
+*
+* This file is part of libgit2, distributed under the GNU GPL v2 with
+* a Linking Exception. For full terms see the included COPYING file.
+*/
+#ifndef INCLUDE_patch_h__
+#define INCLUDE_patch_h__
+
+#include "common.h"
+
+#include "git2/patch.h"
+#include "array.h"
+
+/* cached information about a hunk in a patch */
+typedef struct git_patch_hunk {
+ git_diff_hunk hunk;
+ size_t line_start;
+ size_t line_count;
+} git_patch_hunk;
+
+struct git_patch {
+ git_refcount rc;
+
+ git_repository *repo; /* may be null */
+
+ git_diff_options diff_opts;
+
+ git_diff_delta *delta;
+ git_diff_binary binary;
+ git_array_t(git_patch_hunk) hunks;
+ git_array_t(git_diff_line) lines;
+
+ size_t header_size;
+ size_t content_size;
+ size_t context_size;
+
+ void (*free_fn)(git_patch *patch);
+};
+
+extern int git_patch__invoke_callbacks(
+ git_patch *patch,
+ git_diff_file_cb file_cb,
+ git_diff_binary_cb binary_cb,
+ git_diff_hunk_cb hunk_cb,
+ git_diff_line_cb line_cb,
+ void *payload);
+
+extern int git_patch_line_stats(
+ size_t *total_ctxt,
+ size_t *total_adds,
+ size_t *total_dels,
+ const git_patch *patch);
+
+/** Options for parsing patch files. */
+typedef struct {
+ /**
+ * The length of the prefix (in path segments) for the filenames.
+ * This prefix will be removed when looking for files. The default is 1.
+ */
+ uint32_t prefix_len;
+
+ /**
+ * The type of object IDs in the patch file. The default is
+ * `GIT_OID_DEFAULT`.
+ */
+ git_oid_t oid_type;
+} git_patch_options;
+
+#define GIT_PATCH_OPTIONS_INIT { 1, GIT_OID_DEFAULT }
+
+extern int git_patch__to_buf(git_str *out, git_patch *patch);
+extern void git_patch_free(git_patch *patch);
+
+#endif
diff --git a/src/libgit2/patch_generate.c b/src/libgit2/patch_generate.c
new file mode 100644
index 0000000..079bc53
--- /dev/null
+++ b/src/libgit2/patch_generate.c
@@ -0,0 +1,934 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "patch_generate.h"
+
+#include "git2/blob.h"
+#include "diff.h"
+#include "diff_generate.h"
+#include "diff_file.h"
+#include "diff_driver.h"
+#include "diff_xdiff.h"
+#include "delta.h"
+#include "zstream.h"
+#include "futils.h"
+
+static void diff_output_init(
+ git_patch_generated_output *, const git_diff_options *, git_diff_file_cb,
+ git_diff_binary_cb, git_diff_hunk_cb, git_diff_line_cb, void*);
+
+static void diff_output_to_patch(
+ git_patch_generated_output *, git_patch_generated *);
+
+static void patch_generated_free(git_patch *p)
+{
+ git_patch_generated *patch = (git_patch_generated *)p;
+
+ git_array_clear(patch->base.lines);
+ git_array_clear(patch->base.hunks);
+
+ git__free((char *)patch->base.binary.old_file.data);
+ git__free((char *)patch->base.binary.new_file.data);
+
+ git_diff_file_content__clear(&patch->ofile);
+ git_diff_file_content__clear(&patch->nfile);
+
+ git_diff_free(patch->diff); /* decrements refcount */
+ patch->diff = NULL;
+
+ git_pool_clear(&patch->flattened);
+
+ git__free((char *)patch->base.diff_opts.old_prefix);
+ git__free((char *)patch->base.diff_opts.new_prefix);
+
+ if (patch->flags & GIT_PATCH_GENERATED_ALLOCATED)
+ git__free(patch);
+}
+
+static void patch_generated_update_binary(git_patch_generated *patch)
+{
+ if ((patch->base.delta->flags & DIFF_FLAGS_KNOWN_BINARY) != 0)
+ return;
+
+ if ((patch->ofile.file->flags & GIT_DIFF_FLAG_BINARY) != 0 ||
+ (patch->nfile.file->flags & GIT_DIFF_FLAG_BINARY) != 0)
+ patch->base.delta->flags |= GIT_DIFF_FLAG_BINARY;
+
+ else if (patch->ofile.file->size > GIT_XDIFF_MAX_SIZE ||
+ patch->nfile.file->size > GIT_XDIFF_MAX_SIZE)
+ patch->base.delta->flags |= GIT_DIFF_FLAG_BINARY;
+
+ else if ((patch->ofile.file->flags & DIFF_FLAGS_NOT_BINARY) != 0 &&
+ (patch->nfile.file->flags & DIFF_FLAGS_NOT_BINARY) != 0)
+ patch->base.delta->flags |= GIT_DIFF_FLAG_NOT_BINARY;
+}
+
+static void patch_generated_init_common(git_patch_generated *patch)
+{
+ patch->base.free_fn = patch_generated_free;
+
+ patch_generated_update_binary(patch);
+
+ patch->flags |= GIT_PATCH_GENERATED_INITIALIZED;
+
+ if (patch->diff)
+ git_diff_addref(patch->diff);
+}
+
+static int patch_generated_normalize_options(
+ git_diff_options *out,
+ const git_diff_options *opts,
+ git_repository *repo)
+{
+ if (opts) {
+ GIT_ERROR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options");
+ memcpy(out, opts, sizeof(git_diff_options));
+ } else {
+ git_diff_options default_opts = GIT_DIFF_OPTIONS_INIT;
+ memcpy(out, &default_opts, sizeof(git_diff_options));
+ }
+
+ if (repo && opts && opts->oid_type && repo->oid_type != opts->oid_type) {
+ /*
+ * This limitation feels unnecessary - we should consider
+ * allowing users to generate diffs with a different object
+ * ID format than the repository.
+ */
+ git_error_set(GIT_ERROR_INVALID,
+ "specified object ID type does not match repository object ID type");
+ return -1;
+ } else if (repo) {
+ out->oid_type = repo->oid_type;
+ } else if (opts && opts->oid_type) {
+ out->oid_type = opts->oid_type;
+ } else {
+ out->oid_type = GIT_OID_DEFAULT;
+ }
+
+ out->old_prefix = opts && opts->old_prefix ?
+ git__strdup(opts->old_prefix) :
+ git__strdup(DIFF_OLD_PREFIX_DEFAULT);
+
+ out->new_prefix = opts && opts->new_prefix ?
+ git__strdup(opts->new_prefix) :
+ git__strdup(DIFF_NEW_PREFIX_DEFAULT);
+
+ GIT_ERROR_CHECK_ALLOC(out->old_prefix);
+ GIT_ERROR_CHECK_ALLOC(out->new_prefix);
+
+ return 0;
+}
+
+static int patch_generated_init(
+ git_patch_generated *patch, git_diff *diff, size_t delta_index)
+{
+ int error = 0;
+
+ memset(patch, 0, sizeof(*patch));
+
+ patch->diff = diff;
+ patch->base.repo = diff->repo;
+ patch->base.delta = git_vector_get(&diff->deltas, delta_index);
+ patch->delta_index = delta_index;
+
+ if ((error = patch_generated_normalize_options(
+ &patch->base.diff_opts, &diff->opts, diff->repo)) < 0 ||
+ (error = git_diff_file_content__init_from_diff(
+ &patch->ofile, diff, patch->base.delta, true)) < 0 ||
+ (error = git_diff_file_content__init_from_diff(
+ &patch->nfile, diff, patch->base.delta, false)) < 0)
+ return error;
+
+ patch_generated_init_common(patch);
+
+ return 0;
+}
+
+static int patch_generated_alloc_from_diff(
+ git_patch_generated **out, git_diff *diff, size_t delta_index)
+{
+ int error;
+ git_patch_generated *patch = git__calloc(1, sizeof(git_patch_generated));
+ GIT_ERROR_CHECK_ALLOC(patch);
+
+ if (!(error = patch_generated_init(patch, diff, delta_index))) {
+ patch->flags |= GIT_PATCH_GENERATED_ALLOCATED;
+ GIT_REFCOUNT_INC(&patch->base);
+ } else {
+ git__free(patch);
+ patch = NULL;
+ }
+
+ *out = patch;
+ return error;
+}
+
+GIT_INLINE(bool) should_skip_binary(git_patch_generated *patch, git_diff_file *file)
+{
+ if ((patch->base.diff_opts.flags & GIT_DIFF_SHOW_BINARY) != 0)
+ return false;
+
+ return (file->flags & GIT_DIFF_FLAG_BINARY) != 0;
+}
+
+static bool patch_generated_diffable(git_patch_generated *patch)
+{
+ size_t olen, nlen;
+
+ if (patch->base.delta->status == GIT_DELTA_UNMODIFIED)
+ return false;
+
+ /* if we've determined this to be binary (and we are not showing binary
+ * data) then we have skipped loading the map data. instead, query the
+ * file data itself.
+ */
+ if ((patch->base.delta->flags & GIT_DIFF_FLAG_BINARY) != 0 &&
+ (patch->base.diff_opts.flags & GIT_DIFF_SHOW_BINARY) == 0) {
+ olen = (size_t)patch->ofile.file->size;
+ nlen = (size_t)patch->nfile.file->size;
+ } else {
+ olen = patch->ofile.map.len;
+ nlen = patch->nfile.map.len;
+ }
+
+ /* if both sides are empty, files are identical */
+ if (!olen && !nlen)
+ return false;
+
+ /* otherwise, check the file sizes and the oid */
+ return (olen != nlen ||
+ !git_oid_equal(&patch->ofile.file->id, &patch->nfile.file->id));
+}
+
+static int patch_generated_load(git_patch_generated *patch, git_patch_generated_output *output)
+{
+ int error = 0;
+ bool incomplete_data;
+
+ if ((patch->flags & GIT_PATCH_GENERATED_LOADED) != 0)
+ return 0;
+
+ /* if no hunk and data callbacks and user doesn't care if data looks
+ * binary, then there is no need to actually load the data
+ */
+ if ((patch->ofile.opts_flags & GIT_DIFF_SKIP_BINARY_CHECK) != 0 &&
+ output && !output->binary_cb && !output->hunk_cb && !output->data_cb)
+ return 0;
+
+ incomplete_data =
+ (((patch->ofile.flags & GIT_DIFF_FLAG__NO_DATA) != 0 ||
+ (patch->ofile.file->flags & GIT_DIFF_FLAG_VALID_ID) != 0) &&
+ ((patch->nfile.flags & GIT_DIFF_FLAG__NO_DATA) != 0 ||
+ (patch->nfile.file->flags & GIT_DIFF_FLAG_VALID_ID) != 0));
+
+ if ((error = git_diff_file_content__load(
+ &patch->ofile, &patch->base.diff_opts)) < 0 ||
+ (error = git_diff_file_content__load(
+ &patch->nfile, &patch->base.diff_opts)) < 0 ||
+ should_skip_binary(patch, patch->nfile.file))
+ goto cleanup;
+
+ /* if previously missing an oid, and now that we have it the two sides
+ * are the same (and not submodules), update MODIFIED -> UNMODIFIED
+ */
+ if (incomplete_data &&
+ patch->ofile.file->mode == patch->nfile.file->mode &&
+ patch->ofile.file->mode != GIT_FILEMODE_COMMIT &&
+ git_oid_equal(&patch->ofile.file->id, &patch->nfile.file->id) &&
+ patch->base.delta->status == GIT_DELTA_MODIFIED) /* not RENAMED/COPIED! */
+ patch->base.delta->status = GIT_DELTA_UNMODIFIED;
+
+cleanup:
+ patch_generated_update_binary(patch);
+
+ if (!error) {
+ if (patch_generated_diffable(patch))
+ patch->flags |= GIT_PATCH_GENERATED_DIFFABLE;
+
+ patch->flags |= GIT_PATCH_GENERATED_LOADED;
+ }
+
+ return error;
+}
+
+static int patch_generated_invoke_file_callback(
+ git_patch_generated *patch, git_patch_generated_output *output)
+{
+ float progress = patch->diff ?
+ ((float)patch->delta_index / patch->diff->deltas.length) : 1.0f;
+
+ if (!output->file_cb)
+ return 0;
+
+ return git_error_set_after_callback_function(
+ output->file_cb(patch->base.delta, progress, output->payload),
+ "git_patch");
+}
+
+static int create_binary(
+ git_diff_binary_t *out_type,
+ char **out_data,
+ size_t *out_datalen,
+ size_t *out_inflatedlen,
+ const char *a_data,
+ size_t a_datalen,
+ const char *b_data,
+ size_t b_datalen)
+{
+ git_str deflate = GIT_STR_INIT, delta = GIT_STR_INIT;
+ size_t delta_data_len = 0;
+ int error;
+
+ /* The git_delta function accepts unsigned long only */
+ if (!git__is_ulong(a_datalen) || !git__is_ulong(b_datalen))
+ return GIT_EBUFS;
+
+ if ((error = git_zstream_deflatebuf(&deflate, b_data, b_datalen)) < 0)
+ goto done;
+
+ /* The git_delta function accepts unsigned long only */
+ if (!git__is_ulong(deflate.size)) {
+ error = GIT_EBUFS;
+ goto done;
+ }
+
+ if (a_datalen && b_datalen) {
+ void *delta_data;
+
+ error = git_delta(&delta_data, &delta_data_len,
+ a_data, a_datalen,
+ b_data, b_datalen,
+ deflate.size);
+
+ if (error == 0) {
+ error = git_zstream_deflatebuf(
+ &delta, delta_data, delta_data_len);
+
+ git__free(delta_data);
+ } else if (error == GIT_EBUFS) {
+ error = 0;
+ }
+
+ if (error < 0)
+ goto done;
+ }
+
+ if (delta.size && delta.size < deflate.size) {
+ *out_type = GIT_DIFF_BINARY_DELTA;
+ *out_datalen = delta.size;
+ *out_data = git_str_detach(&delta);
+ *out_inflatedlen = delta_data_len;
+ } else {
+ *out_type = GIT_DIFF_BINARY_LITERAL;
+ *out_datalen = deflate.size;
+ *out_data = git_str_detach(&deflate);
+ *out_inflatedlen = b_datalen;
+ }
+
+done:
+ git_str_dispose(&deflate);
+ git_str_dispose(&delta);
+
+ return error;
+}
+
+static int diff_binary(git_patch_generated_output *output, git_patch_generated *patch)
+{
+ git_diff_binary binary = {0};
+ const char *old_data = patch->ofile.map.data;
+ const char *new_data = patch->nfile.map.data;
+ size_t old_len = patch->ofile.map.len,
+ new_len = patch->nfile.map.len;
+ int error;
+
+ /* Only load contents if the user actually wants to diff
+ * binary files. */
+ if (patch->base.diff_opts.flags & GIT_DIFF_SHOW_BINARY) {
+ binary.contains_data = 1;
+
+ /* Create the old->new delta (as the "new" side of the patch),
+ * and the new->old delta (as the "old" side)
+ */
+ if ((error = create_binary(&binary.old_file.type,
+ (char **)&binary.old_file.data,
+ &binary.old_file.datalen,
+ &binary.old_file.inflatedlen,
+ new_data, new_len, old_data, old_len)) < 0 ||
+ (error = create_binary(&binary.new_file.type,
+ (char **)&binary.new_file.data,
+ &binary.new_file.datalen,
+ &binary.new_file.inflatedlen,
+ old_data, old_len, new_data, new_len)) < 0)
+ return error;
+ }
+
+ error = git_error_set_after_callback_function(
+ output->binary_cb(patch->base.delta, &binary, output->payload),
+ "git_patch");
+
+ git__free((char *) binary.old_file.data);
+ git__free((char *) binary.new_file.data);
+
+ return error;
+}
+
+static int patch_generated_create(
+ git_patch_generated *patch,
+ git_patch_generated_output *output)
+{
+ int error = 0;
+
+ if ((patch->flags & GIT_PATCH_GENERATED_DIFFED) != 0)
+ return 0;
+
+ /* if we are not looking at the binary or text data, don't do the diff */
+ if (!output->binary_cb && !output->hunk_cb && !output->data_cb)
+ return 0;
+
+ if ((patch->flags & GIT_PATCH_GENERATED_LOADED) == 0 &&
+ (error = patch_generated_load(patch, output)) < 0)
+ return error;
+
+ if ((patch->flags & GIT_PATCH_GENERATED_DIFFABLE) == 0)
+ return 0;
+
+ if ((patch->base.delta->flags & GIT_DIFF_FLAG_BINARY) != 0) {
+ if (output->binary_cb)
+ error = diff_binary(output, patch);
+ }
+ else {
+ if (output->diff_cb)
+ error = output->diff_cb(output, patch);
+ }
+
+ patch->flags |= GIT_PATCH_GENERATED_DIFFED;
+ return error;
+}
+
+static int diff_required(git_diff *diff, const char *action)
+{
+ if (diff)
+ return 0;
+ git_error_set(GIT_ERROR_INVALID, "must provide valid diff to %s", action);
+ return -1;
+}
+
+typedef struct {
+ git_patch_generated patch;
+ git_diff_delta delta;
+ char paths[GIT_FLEX_ARRAY];
+} patch_generated_with_delta;
+
+static int diff_single_generate(patch_generated_with_delta *pd, git_xdiff_output *xo)
+{
+ int error = 0;
+ git_patch_generated *patch = &pd->patch;
+ bool has_old = ((patch->ofile.flags & GIT_DIFF_FLAG__NO_DATA) == 0);
+ bool has_new = ((patch->nfile.flags & GIT_DIFF_FLAG__NO_DATA) == 0);
+
+ pd->delta.status = has_new ?
+ (has_old ? GIT_DELTA_MODIFIED : GIT_DELTA_ADDED) :
+ (has_old ? GIT_DELTA_DELETED : GIT_DELTA_UNTRACKED);
+
+ if (git_oid_equal(&patch->nfile.file->id, &patch->ofile.file->id))
+ pd->delta.status = GIT_DELTA_UNMODIFIED;
+
+ patch->base.delta = &pd->delta;
+
+ patch_generated_init_common(patch);
+
+ if (pd->delta.status == GIT_DELTA_UNMODIFIED &&
+ !(patch->ofile.opts_flags & GIT_DIFF_INCLUDE_UNMODIFIED)) {
+
+ /* Even empty patches are flagged as binary, and even though
+ * there's no difference, we flag this as "containing data"
+ * (the data is known to be empty, as opposed to wholly unknown).
+ */
+ if (patch->base.diff_opts.flags & GIT_DIFF_SHOW_BINARY)
+ patch->base.binary.contains_data = 1;
+
+ return error;
+ }
+
+ error = patch_generated_invoke_file_callback(patch, (git_patch_generated_output *)xo);
+
+ if (!error)
+ error = patch_generated_create(patch, (git_patch_generated_output *)xo);
+
+ return error;
+}
+
+static int patch_generated_from_sources(
+ patch_generated_with_delta *pd,
+ git_xdiff_output *xo,
+ git_diff_file_content_src *oldsrc,
+ git_diff_file_content_src *newsrc,
+ const git_diff_options *given_opts)
+{
+ int error = 0;
+ git_repository *repo =
+ oldsrc->blob ? git_blob_owner(oldsrc->blob) :
+ newsrc->blob ? git_blob_owner(newsrc->blob) : NULL;
+ git_diff_file *lfile = &pd->delta.old_file, *rfile = &pd->delta.new_file;
+ git_diff_file_content *ldata = &pd->patch.ofile, *rdata = &pd->patch.nfile;
+ git_diff_options *opts = &pd->patch.base.diff_opts;
+
+ if ((error = patch_generated_normalize_options(opts, given_opts, repo)) < 0)
+ return error;
+
+ if ((opts->flags & GIT_DIFF_REVERSE) != 0) {
+ void *tmp = lfile; lfile = rfile; rfile = tmp;
+ tmp = ldata; ldata = rdata; rdata = tmp;
+ }
+
+ pd->patch.base.delta = &pd->delta;
+
+ if (!oldsrc->as_path) {
+ if (newsrc->as_path)
+ oldsrc->as_path = newsrc->as_path;
+ else
+ oldsrc->as_path = newsrc->as_path = "file";
+ }
+ else if (!newsrc->as_path)
+ newsrc->as_path = oldsrc->as_path;
+
+ lfile->path = oldsrc->as_path;
+ rfile->path = newsrc->as_path;
+
+ if ((error = git_diff_file_content__init_from_src(
+ ldata, repo, opts, oldsrc, lfile)) < 0 ||
+ (error = git_diff_file_content__init_from_src(
+ rdata, repo, opts, newsrc, rfile)) < 0)
+ return error;
+
+ return diff_single_generate(pd, xo);
+}
+
+static int patch_generated_with_delta_alloc(
+ patch_generated_with_delta **out,
+ const char **old_path,
+ const char **new_path)
+{
+ patch_generated_with_delta *pd;
+ size_t old_len = *old_path ? strlen(*old_path) : 0;
+ size_t new_len = *new_path ? strlen(*new_path) : 0;
+ size_t alloc_len;
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, sizeof(*pd), old_len);
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, new_len);
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 2);
+
+ *out = pd = git__calloc(1, alloc_len);
+ GIT_ERROR_CHECK_ALLOC(pd);
+
+ pd->patch.flags = GIT_PATCH_GENERATED_ALLOCATED;
+
+ if (*old_path) {
+ memcpy(&pd->paths[0], *old_path, old_len);
+ *old_path = &pd->paths[0];
+ } else if (*new_path)
+ *old_path = &pd->paths[old_len + 1];
+
+ if (*new_path) {
+ memcpy(&pd->paths[old_len + 1], *new_path, new_len);
+ *new_path = &pd->paths[old_len + 1];
+ } else if (*old_path)
+ *new_path = &pd->paths[0];
+
+ return 0;
+}
+
+static int diff_from_sources(
+ git_diff_file_content_src *oldsrc,
+ git_diff_file_content_src *newsrc,
+ const git_diff_options *opts,
+ git_diff_file_cb file_cb,
+ git_diff_binary_cb binary_cb,
+ git_diff_hunk_cb hunk_cb,
+ git_diff_line_cb data_cb,
+ void *payload)
+{
+ int error = 0;
+ patch_generated_with_delta pd;
+ git_xdiff_output xo;
+
+ memset(&xo, 0, sizeof(xo));
+ diff_output_init(
+ &xo.output, opts, file_cb, binary_cb, hunk_cb, data_cb, payload);
+ git_xdiff_init(&xo, opts);
+
+ memset(&pd, 0, sizeof(pd));
+
+ error = patch_generated_from_sources(&pd, &xo, oldsrc, newsrc, opts);
+
+ git_patch_free(&pd.patch.base);
+
+ return error;
+}
+
+static int patch_from_sources(
+ git_patch **out,
+ git_diff_file_content_src *oldsrc,
+ git_diff_file_content_src *newsrc,
+ const git_diff_options *opts)
+{
+ int error = 0;
+ patch_generated_with_delta *pd;
+ git_xdiff_output xo;
+
+ GIT_ASSERT_ARG(out);
+ *out = NULL;
+
+ if ((error = patch_generated_with_delta_alloc(
+ &pd, &oldsrc->as_path, &newsrc->as_path)) < 0)
+ return error;
+
+ memset(&xo, 0, sizeof(xo));
+ diff_output_to_patch(&xo.output, &pd->patch);
+ git_xdiff_init(&xo, opts);
+
+ if (!(error = patch_generated_from_sources(pd, &xo, oldsrc, newsrc, opts)))
+ *out = (git_patch *)pd;
+ else
+ git_patch_free((git_patch *)pd);
+
+ return error;
+}
+
+int git_diff_blobs(
+ const git_blob *old_blob,
+ const char *old_path,
+ const git_blob *new_blob,
+ const char *new_path,
+ const git_diff_options *opts,
+ git_diff_file_cb file_cb,
+ git_diff_binary_cb binary_cb,
+ git_diff_hunk_cb hunk_cb,
+ git_diff_line_cb data_cb,
+ void *payload)
+{
+ git_diff_file_content_src osrc =
+ GIT_DIFF_FILE_CONTENT_SRC__BLOB(old_blob, old_path);
+ git_diff_file_content_src nsrc =
+ GIT_DIFF_FILE_CONTENT_SRC__BLOB(new_blob, new_path);
+ return diff_from_sources(
+ &osrc, &nsrc, opts, file_cb, binary_cb, hunk_cb, data_cb, payload);
+}
+
+int git_patch_from_blobs(
+ git_patch **out,
+ const git_blob *old_blob,
+ const char *old_path,
+ const git_blob *new_blob,
+ const char *new_path,
+ const git_diff_options *opts)
+{
+ git_diff_file_content_src osrc =
+ GIT_DIFF_FILE_CONTENT_SRC__BLOB(old_blob, old_path);
+ git_diff_file_content_src nsrc =
+ GIT_DIFF_FILE_CONTENT_SRC__BLOB(new_blob, new_path);
+ return patch_from_sources(out, &osrc, &nsrc, opts);
+}
+
+int git_diff_blob_to_buffer(
+ const git_blob *old_blob,
+ const char *old_path,
+ const char *buf,
+ size_t buflen,
+ const char *buf_path,
+ const git_diff_options *opts,
+ git_diff_file_cb file_cb,
+ git_diff_binary_cb binary_cb,
+ git_diff_hunk_cb hunk_cb,
+ git_diff_line_cb data_cb,
+ void *payload)
+{
+ git_diff_file_content_src osrc =
+ GIT_DIFF_FILE_CONTENT_SRC__BLOB(old_blob, old_path);
+ git_diff_file_content_src nsrc =
+ GIT_DIFF_FILE_CONTENT_SRC__BUF(buf, buflen, buf_path);
+ return diff_from_sources(
+ &osrc, &nsrc, opts, file_cb, binary_cb, hunk_cb, data_cb, payload);
+}
+
+int git_patch_from_blob_and_buffer(
+ git_patch **out,
+ const git_blob *old_blob,
+ const char *old_path,
+ const void *buf,
+ size_t buflen,
+ const char *buf_path,
+ const git_diff_options *opts)
+{
+ git_diff_file_content_src osrc =
+ GIT_DIFF_FILE_CONTENT_SRC__BLOB(old_blob, old_path);
+ git_diff_file_content_src nsrc =
+ GIT_DIFF_FILE_CONTENT_SRC__BUF(buf, buflen, buf_path);
+ return patch_from_sources(out, &osrc, &nsrc, opts);
+}
+
+int git_diff_buffers(
+ const void *old_buf,
+ size_t old_len,
+ const char *old_path,
+ const void *new_buf,
+ size_t new_len,
+ const char *new_path,
+ const git_diff_options *opts,
+ git_diff_file_cb file_cb,
+ git_diff_binary_cb binary_cb,
+ git_diff_hunk_cb hunk_cb,
+ git_diff_line_cb data_cb,
+ void *payload)
+{
+ git_diff_file_content_src osrc =
+ GIT_DIFF_FILE_CONTENT_SRC__BUF(old_buf, old_len, old_path);
+ git_diff_file_content_src nsrc =
+ GIT_DIFF_FILE_CONTENT_SRC__BUF(new_buf, new_len, new_path);
+ return diff_from_sources(
+ &osrc, &nsrc, opts, file_cb, binary_cb, hunk_cb, data_cb, payload);
+}
+
+int git_patch_from_buffers(
+ git_patch **out,
+ const void *old_buf,
+ size_t old_len,
+ const char *old_path,
+ const void *new_buf,
+ size_t new_len,
+ const char *new_path,
+ const git_diff_options *opts)
+{
+ git_diff_file_content_src osrc =
+ GIT_DIFF_FILE_CONTENT_SRC__BUF(old_buf, old_len, old_path);
+ git_diff_file_content_src nsrc =
+ GIT_DIFF_FILE_CONTENT_SRC__BUF(new_buf, new_len, new_path);
+ return patch_from_sources(out, &osrc, &nsrc, opts);
+}
+
+int git_patch_generated_from_diff(
+ git_patch **patch_ptr, git_diff *diff, size_t idx)
+{
+ int error = 0;
+ git_xdiff_output xo;
+ git_diff_delta *delta = NULL;
+ git_patch_generated *patch = NULL;
+
+ if (patch_ptr) *patch_ptr = NULL;
+
+ if (diff_required(diff, "git_patch_from_diff") < 0)
+ return -1;
+
+ delta = git_vector_get(&diff->deltas, idx);
+ if (!delta) {
+ git_error_set(GIT_ERROR_INVALID, "index out of range for delta in diff");
+ return GIT_ENOTFOUND;
+ }
+
+ if (git_diff_delta__should_skip(&diff->opts, delta))
+ return 0;
+
+ /* don't load the patch data unless we need it for binary check */
+ if (!patch_ptr &&
+ ((delta->flags & DIFF_FLAGS_KNOWN_BINARY) != 0 ||
+ (diff->opts.flags & GIT_DIFF_SKIP_BINARY_CHECK) != 0))
+ return 0;
+
+ if ((error = patch_generated_alloc_from_diff(&patch, diff, idx)) < 0)
+ return error;
+
+ memset(&xo, 0, sizeof(xo));
+ diff_output_to_patch(&xo.output, patch);
+ git_xdiff_init(&xo, &diff->opts);
+
+ error = patch_generated_invoke_file_callback(patch, &xo.output);
+
+ if (!error)
+ error = patch_generated_create(patch, &xo.output);
+
+ if (!error) {
+ /* TODO: if cumulative diff size is < 0.5 total size, flatten patch */
+ /* TODO: and unload the file content */
+ }
+
+ if (error || !patch_ptr)
+ git_patch_free(&patch->base);
+ else
+ *patch_ptr = &patch->base;
+
+ return error;
+}
+
+git_diff_driver *git_patch_generated_driver(git_patch_generated *patch)
+{
+ /* ofile driver is representative for whole patch */
+ return patch->ofile.driver;
+}
+
+int git_patch_generated_old_data(
+ char **ptr, long *len, git_patch_generated *patch)
+{
+ if (patch->ofile.map.len > LONG_MAX ||
+ patch->ofile.map.len > GIT_XDIFF_MAX_SIZE) {
+ git_error_set(GIT_ERROR_INVALID, "files too large for diff");
+ return -1;
+ }
+
+ *ptr = patch->ofile.map.data;
+ *len = (long)patch->ofile.map.len;
+
+ return 0;
+}
+
+int git_patch_generated_new_data(
+ char **ptr, long *len, git_patch_generated *patch)
+{
+ if (patch->ofile.map.len > LONG_MAX ||
+ patch->ofile.map.len > GIT_XDIFF_MAX_SIZE) {
+ git_error_set(GIT_ERROR_INVALID, "files too large for diff");
+ return -1;
+ }
+
+ *ptr = patch->nfile.map.data;
+ *len = (long)patch->nfile.map.len;
+
+ return 0;
+}
+
+static int patch_generated_file_cb(
+ const git_diff_delta *delta,
+ float progress,
+ void *payload)
+{
+ GIT_UNUSED(delta); GIT_UNUSED(progress); GIT_UNUSED(payload);
+ return 0;
+}
+
+static int patch_generated_binary_cb(
+ const git_diff_delta *delta,
+ const git_diff_binary *binary,
+ void *payload)
+{
+ git_patch *patch = payload;
+
+ GIT_UNUSED(delta);
+
+ memcpy(&patch->binary, binary, sizeof(git_diff_binary));
+
+ if (binary->old_file.data) {
+ patch->binary.old_file.data = git__malloc(binary->old_file.datalen);
+ GIT_ERROR_CHECK_ALLOC(patch->binary.old_file.data);
+
+ memcpy((char *)patch->binary.old_file.data,
+ binary->old_file.data, binary->old_file.datalen);
+ }
+
+ if (binary->new_file.data) {
+ patch->binary.new_file.data = git__malloc(binary->new_file.datalen);
+ GIT_ERROR_CHECK_ALLOC(patch->binary.new_file.data);
+
+ memcpy((char *)patch->binary.new_file.data,
+ binary->new_file.data, binary->new_file.datalen);
+ }
+
+ return 0;
+}
+
+static int git_patch_hunk_cb(
+ const git_diff_delta *delta,
+ const git_diff_hunk *hunk_,
+ void *payload)
+{
+ git_patch_generated *patch = payload;
+ git_patch_hunk *hunk;
+
+ GIT_UNUSED(delta);
+
+ hunk = git_array_alloc(patch->base.hunks);
+ GIT_ERROR_CHECK_ALLOC(hunk);
+
+ memcpy(&hunk->hunk, hunk_, sizeof(hunk->hunk));
+
+ patch->base.header_size += hunk_->header_len;
+
+ hunk->line_start = git_array_size(patch->base.lines);
+ hunk->line_count = 0;
+
+ return 0;
+}
+
+static int patch_generated_line_cb(
+ const git_diff_delta *delta,
+ const git_diff_hunk *hunk_,
+ const git_diff_line *line_,
+ void *payload)
+{
+ git_patch_generated *patch = payload;
+ git_patch_hunk *hunk;
+ git_diff_line *line;
+
+ GIT_UNUSED(delta);
+ GIT_UNUSED(hunk_);
+
+ hunk = git_array_last(patch->base.hunks);
+ GIT_ASSERT(hunk); /* programmer error if no hunk is available */
+
+ line = git_array_alloc(patch->base.lines);
+ GIT_ERROR_CHECK_ALLOC(line);
+
+ memcpy(line, line_, sizeof(*line));
+
+ /* do some bookkeeping so we can provide old/new line numbers */
+
+ patch->base.content_size += line->content_len;
+
+ if (line->origin == GIT_DIFF_LINE_ADDITION ||
+ line->origin == GIT_DIFF_LINE_DELETION)
+ patch->base.content_size += 1;
+ else if (line->origin == GIT_DIFF_LINE_CONTEXT) {
+ patch->base.content_size += 1;
+ patch->base.context_size += line->content_len + 1;
+ } else if (line->origin == GIT_DIFF_LINE_CONTEXT_EOFNL)
+ patch->base.context_size += line->content_len;
+
+ hunk->line_count++;
+
+ return 0;
+}
+
+static void diff_output_init(
+ git_patch_generated_output *out,
+ const git_diff_options *opts,
+ git_diff_file_cb file_cb,
+ git_diff_binary_cb binary_cb,
+ git_diff_hunk_cb hunk_cb,
+ git_diff_line_cb data_cb,
+ void *payload)
+{
+ GIT_UNUSED(opts);
+
+ memset(out, 0, sizeof(*out));
+
+ out->file_cb = file_cb;
+ out->binary_cb = binary_cb;
+ out->hunk_cb = hunk_cb;
+ out->data_cb = data_cb;
+ out->payload = payload;
+}
+
+static void diff_output_to_patch(
+ git_patch_generated_output *out, git_patch_generated *patch)
+{
+ diff_output_init(
+ out,
+ NULL,
+ patch_generated_file_cb,
+ patch_generated_binary_cb,
+ git_patch_hunk_cb,
+ patch_generated_line_cb,
+ patch);
+}
diff --git a/src/libgit2/patch_generate.h b/src/libgit2/patch_generate.h
new file mode 100644
index 0000000..56e3e9d
--- /dev/null
+++ b/src/libgit2/patch_generate.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_patch_generate_h__
+#define INCLUDE_patch_generate_h__
+
+#include "common.h"
+
+#include "diff.h"
+#include "diff_file.h"
+#include "patch.h"
+
+enum {
+ GIT_PATCH_GENERATED_ALLOCATED = (1 << 0),
+ GIT_PATCH_GENERATED_INITIALIZED = (1 << 1),
+ GIT_PATCH_GENERATED_LOADED = (1 << 2),
+ /* the two sides are different */
+ GIT_PATCH_GENERATED_DIFFABLE = (1 << 3),
+ /* the difference between the two sides has been computed */
+ GIT_PATCH_GENERATED_DIFFED = (1 << 4),
+ GIT_PATCH_GENERATED_FLATTENED = (1 << 5)
+};
+
+struct git_patch_generated {
+ struct git_patch base;
+
+ git_diff *diff; /* for refcount purposes, maybe NULL for blob diffs */
+ size_t delta_index;
+ git_diff_file_content ofile;
+ git_diff_file_content nfile;
+ uint32_t flags;
+ git_pool flattened;
+};
+
+typedef struct git_patch_generated git_patch_generated;
+
+extern git_diff_driver *git_patch_generated_driver(git_patch_generated *);
+
+extern int git_patch_generated_old_data(
+ char **, long *, git_patch_generated *);
+extern int git_patch_generated_new_data(
+ char **, long *, git_patch_generated *);
+extern int git_patch_generated_from_diff(
+ git_patch **, git_diff *, size_t);
+
+typedef struct git_patch_generated_output git_patch_generated_output;
+
+struct git_patch_generated_output {
+ /* these callbacks are issued with the diff data */
+ git_diff_file_cb file_cb;
+ git_diff_binary_cb binary_cb;
+ git_diff_hunk_cb hunk_cb;
+ git_diff_line_cb data_cb;
+ void *payload;
+
+ /* this records the actual error in cases where it may be obscured */
+ int error;
+
+ /* this callback is used to do the diff and drive the other callbacks.
+ * see diff_xdiff.h for how to use this in practice for now.
+ */
+ int (*diff_cb)(git_patch_generated_output *output,
+ git_patch_generated *patch);
+};
+
+#endif
diff --git a/src/libgit2/patch_parse.c b/src/libgit2/patch_parse.c
new file mode 100644
index 0000000..c069155
--- /dev/null
+++ b/src/libgit2/patch_parse.c
@@ -0,0 +1,1239 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "patch_parse.h"
+
+#include "git2/patch.h"
+#include "patch.h"
+#include "diff_parse.h"
+#include "fs_path.h"
+
+typedef struct {
+ git_patch base;
+
+ git_patch_parse_ctx *ctx;
+
+ /* the paths from the `diff --git` header, these will be used if this is not
+ * a rename (and rename paths are specified) or if no `+++`/`---` line specify
+ * the paths.
+ */
+ char *header_old_path, *header_new_path;
+
+ /* renamed paths are precise and are not prefixed */
+ char *rename_old_path, *rename_new_path;
+
+ /* the paths given in `---` and `+++` lines */
+ char *old_path, *new_path;
+
+ /* the prefixes from the old/new paths */
+ char *old_prefix, *new_prefix;
+} git_patch_parsed;
+
+static int git_parse_err(const char *fmt, ...) GIT_FORMAT_PRINTF(1, 2);
+static int git_parse_err(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ git_error_vset(GIT_ERROR_PATCH, fmt, ap);
+ va_end(ap);
+
+ return -1;
+}
+
+static size_t header_path_len(git_patch_parse_ctx *ctx)
+{
+ bool inquote = 0;
+ bool quoted = git_parse_ctx_contains_s(&ctx->parse_ctx, "\"");
+ size_t len;
+
+ for (len = quoted; len < ctx->parse_ctx.line_len; len++) {
+ if (!quoted && git__isspace(ctx->parse_ctx.line[len]))
+ break;
+ else if (quoted && !inquote && ctx->parse_ctx.line[len] == '"') {
+ len++;
+ break;
+ }
+
+ inquote = (!inquote && ctx->parse_ctx.line[len] == '\\');
+ }
+
+ return len;
+}
+
+static int parse_header_path_buf(git_str *path, git_patch_parse_ctx *ctx, size_t path_len)
+{
+ int error;
+
+ if ((error = git_str_put(path, ctx->parse_ctx.line, path_len)) < 0)
+ return error;
+
+ git_parse_advance_chars(&ctx->parse_ctx, path_len);
+
+ git_str_rtrim(path);
+
+ if (path->size > 0 && path->ptr[0] == '"' &&
+ (error = git_str_unquote(path)) < 0)
+ return error;
+
+ git_fs_path_squash_slashes(path);
+
+ if (!path->size)
+ return git_parse_err("patch contains empty path at line %"PRIuZ,
+ ctx->parse_ctx.line_num);
+
+ return 0;
+}
+
+static int parse_header_path(char **out, git_patch_parse_ctx *ctx)
+{
+ git_str path = GIT_STR_INIT;
+ int error;
+
+ if ((error = parse_header_path_buf(&path, ctx, header_path_len(ctx))) < 0)
+ goto out;
+ *out = git_str_detach(&path);
+
+out:
+ git_str_dispose(&path);
+ return error;
+}
+
+static int parse_header_git_oldpath(
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
+{
+ git_str old_path = GIT_STR_INIT;
+ int error;
+
+ if (patch->old_path) {
+ error = git_parse_err("patch contains duplicate old path at line %"PRIuZ,
+ ctx->parse_ctx.line_num);
+ goto out;
+ }
+
+ if ((error = parse_header_path_buf(&old_path, ctx, ctx->parse_ctx.line_len - 1)) < 0)
+ goto out;
+
+ patch->old_path = git_str_detach(&old_path);
+
+out:
+ git_str_dispose(&old_path);
+ return error;
+}
+
+static int parse_header_git_newpath(
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
+{
+ git_str new_path = GIT_STR_INIT;
+ int error;
+
+ if (patch->new_path) {
+ error = git_parse_err("patch contains duplicate new path at line %"PRIuZ,
+ ctx->parse_ctx.line_num);
+ goto out;
+ }
+
+ if ((error = parse_header_path_buf(&new_path, ctx, ctx->parse_ctx.line_len - 1)) < 0)
+ goto out;
+ patch->new_path = git_str_detach(&new_path);
+
+out:
+ git_str_dispose(&new_path);
+ return error;
+}
+
+static int parse_header_mode(uint16_t *mode, git_patch_parse_ctx *ctx)
+{
+ int64_t m;
+
+ if ((git_parse_advance_digit(&m, &ctx->parse_ctx, 8)) < 0)
+ return git_parse_err("invalid file mode at line %"PRIuZ, ctx->parse_ctx.line_num);
+
+ if (m > UINT16_MAX)
+ return -1;
+
+ *mode = (uint16_t)m;
+
+ return 0;
+}
+
+static int parse_header_oid(
+ git_oid *oid,
+ uint16_t *oid_len,
+ git_patch_parse_ctx *ctx)
+{
+ size_t hexsize, len;
+
+ hexsize = git_oid_hexsize(ctx->opts.oid_type);
+
+ for (len = 0;
+ len < ctx->parse_ctx.line_len && len < hexsize;
+ len++) {
+ if (!git__isxdigit(ctx->parse_ctx.line[len]))
+ break;
+ }
+
+ if (len < GIT_OID_MINPREFIXLEN || len > hexsize ||
+ git_oid__fromstrn(oid, ctx->parse_ctx.line, len, ctx->opts.oid_type) < 0)
+ return git_parse_err("invalid hex formatted object id at line %"PRIuZ,
+ ctx->parse_ctx.line_num);
+
+ git_parse_advance_chars(&ctx->parse_ctx, len);
+
+ *oid_len = (uint16_t)len;
+
+ return 0;
+}
+
+static int parse_header_git_index(
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
+{
+ char c;
+
+ if (parse_header_oid(&patch->base.delta->old_file.id,
+ &patch->base.delta->old_file.id_abbrev, ctx) < 0 ||
+ git_parse_advance_expected_str(&ctx->parse_ctx, "..") < 0 ||
+ parse_header_oid(&patch->base.delta->new_file.id,
+ &patch->base.delta->new_file.id_abbrev, ctx) < 0)
+ return -1;
+
+ if (git_parse_peek(&c, &ctx->parse_ctx, 0) == 0 && c == ' ') {
+ uint16_t mode = 0;
+
+ git_parse_advance_chars(&ctx->parse_ctx, 1);
+
+ if (parse_header_mode(&mode, ctx) < 0)
+ return -1;
+
+ if (!patch->base.delta->new_file.mode)
+ patch->base.delta->new_file.mode = mode;
+
+ if (!patch->base.delta->old_file.mode)
+ patch->base.delta->old_file.mode = mode;
+ }
+
+ return 0;
+}
+
+static int parse_header_git_oldmode(
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
+{
+ return parse_header_mode(&patch->base.delta->old_file.mode, ctx);
+}
+
+static int parse_header_git_newmode(
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
+{
+ return parse_header_mode(&patch->base.delta->new_file.mode, ctx);
+}
+
+static int parse_header_git_deletedfilemode(
+ git_patch_parsed *patch,
+ git_patch_parse_ctx *ctx)
+{
+ git__free((char *)patch->base.delta->new_file.path);
+
+ patch->base.delta->new_file.path = NULL;
+ patch->base.delta->status = GIT_DELTA_DELETED;
+ patch->base.delta->nfiles = 1;
+
+ return parse_header_mode(&patch->base.delta->old_file.mode, ctx);
+}
+
+static int parse_header_git_newfilemode(
+ git_patch_parsed *patch,
+ git_patch_parse_ctx *ctx)
+{
+ git__free((char *)patch->base.delta->old_file.path);
+
+ patch->base.delta->old_file.path = NULL;
+ patch->base.delta->status = GIT_DELTA_ADDED;
+ patch->base.delta->nfiles = 1;
+
+ return parse_header_mode(&patch->base.delta->new_file.mode, ctx);
+}
+
+static int parse_header_rename(
+ char **out,
+ git_patch_parse_ctx *ctx)
+{
+ git_str path = GIT_STR_INIT;
+
+ if (parse_header_path_buf(&path, ctx, header_path_len(ctx)) < 0)
+ return -1;
+
+ /* Note: the `rename from` and `rename to` lines include the literal
+ * filename. They do *not* include the prefix. (Who needs consistency?)
+ */
+ *out = git_str_detach(&path);
+ return 0;
+}
+
+static int parse_header_renamefrom(
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
+{
+ patch->base.delta->status = GIT_DELTA_RENAMED;
+ return parse_header_rename(&patch->rename_old_path, ctx);
+}
+
+static int parse_header_renameto(
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
+{
+ patch->base.delta->status = GIT_DELTA_RENAMED;
+ return parse_header_rename(&patch->rename_new_path, ctx);
+}
+
+static int parse_header_copyfrom(
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
+{
+ patch->base.delta->status = GIT_DELTA_COPIED;
+ return parse_header_rename(&patch->rename_old_path, ctx);
+}
+
+static int parse_header_copyto(
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
+{
+ patch->base.delta->status = GIT_DELTA_COPIED;
+ return parse_header_rename(&patch->rename_new_path, ctx);
+}
+
+static int parse_header_percent(uint16_t *out, git_patch_parse_ctx *ctx)
+{
+ int64_t val;
+
+ if (git_parse_advance_digit(&val, &ctx->parse_ctx, 10) < 0)
+ return -1;
+
+ if (git_parse_advance_expected_str(&ctx->parse_ctx, "%") < 0)
+ return -1;
+
+ if (val < 0 || val > 100)
+ return -1;
+
+ *out = (uint16_t)val;
+ return 0;
+}
+
+static int parse_header_similarity(
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
+{
+ if (parse_header_percent(&patch->base.delta->similarity, ctx) < 0)
+ return git_parse_err("invalid similarity percentage at line %"PRIuZ,
+ ctx->parse_ctx.line_num);
+
+ return 0;
+}
+
+static int parse_header_dissimilarity(
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
+{
+ uint16_t dissimilarity;
+
+ if (parse_header_percent(&dissimilarity, ctx) < 0)
+ return git_parse_err("invalid similarity percentage at line %"PRIuZ,
+ ctx->parse_ctx.line_num);
+
+ patch->base.delta->similarity = 100 - dissimilarity;
+
+ return 0;
+}
+
+static int parse_header_start(git_patch_parsed *patch, git_patch_parse_ctx *ctx)
+{
+ if (parse_header_path(&patch->header_old_path, ctx) < 0)
+ return git_parse_err("corrupt old path in git diff header at line %"PRIuZ,
+ ctx->parse_ctx.line_num);
+
+ if (git_parse_advance_ws(&ctx->parse_ctx) < 0 ||
+ parse_header_path(&patch->header_new_path, ctx) < 0)
+ return git_parse_err("corrupt new path in git diff header at line %"PRIuZ,
+ ctx->parse_ctx.line_num);
+
+ /*
+ * We cannot expect to be able to always parse paths correctly at this
+ * point. Due to the possibility of unquoted names, whitespaces in
+ * filenames and custom prefixes we have to allow that, though, and just
+ * proceed here. We then hope for the "---" and "+++" lines to fix that
+ * for us.
+ */
+ if (!git_parse_ctx_contains(&ctx->parse_ctx, "\n", 1) &&
+ !git_parse_ctx_contains(&ctx->parse_ctx, "\r\n", 2)) {
+ git_parse_advance_chars(&ctx->parse_ctx, ctx->parse_ctx.line_len - 1);
+
+ git__free(patch->header_old_path);
+ patch->header_old_path = NULL;
+ git__free(patch->header_new_path);
+ patch->header_new_path = NULL;
+ }
+
+ return 0;
+}
+
+typedef enum {
+ STATE_START,
+
+ STATE_DIFF,
+ STATE_FILEMODE,
+ STATE_MODE,
+ STATE_INDEX,
+ STATE_PATH,
+
+ STATE_SIMILARITY,
+ STATE_RENAME,
+ STATE_COPY,
+
+ STATE_END
+} parse_header_state;
+
+typedef struct {
+ const char *str;
+ parse_header_state expected_state;
+ parse_header_state next_state;
+ int(*fn)(git_patch_parsed *, git_patch_parse_ctx *);
+} parse_header_transition;
+
+static const parse_header_transition transitions[] = {
+ /* Start */
+ { "diff --git " , STATE_START, STATE_DIFF, parse_header_start },
+
+ { "deleted file mode " , STATE_DIFF, STATE_FILEMODE, parse_header_git_deletedfilemode },
+ { "new file mode " , STATE_DIFF, STATE_FILEMODE, parse_header_git_newfilemode },
+ { "old mode " , STATE_DIFF, STATE_MODE, parse_header_git_oldmode },
+ { "new mode " , STATE_MODE, STATE_END, parse_header_git_newmode },
+
+ { "index " , STATE_FILEMODE, STATE_INDEX, parse_header_git_index },
+ { "index " , STATE_DIFF, STATE_INDEX, parse_header_git_index },
+ { "index " , STATE_END, STATE_INDEX, parse_header_git_index },
+
+ { "--- " , STATE_DIFF, STATE_PATH, parse_header_git_oldpath },
+ { "--- " , STATE_INDEX, STATE_PATH, parse_header_git_oldpath },
+ { "--- " , STATE_FILEMODE, STATE_PATH, parse_header_git_oldpath },
+ { "+++ " , STATE_PATH, STATE_END, parse_header_git_newpath },
+ { "GIT binary patch" , STATE_INDEX, STATE_END, NULL },
+ { "Binary files " , STATE_INDEX, STATE_END, NULL },
+
+ { "similarity index " , STATE_END, STATE_SIMILARITY, parse_header_similarity },
+ { "similarity index " , STATE_DIFF, STATE_SIMILARITY, parse_header_similarity },
+ { "dissimilarity index ", STATE_DIFF, STATE_SIMILARITY, parse_header_dissimilarity },
+ { "rename from " , STATE_SIMILARITY, STATE_RENAME, parse_header_renamefrom },
+ { "rename old " , STATE_SIMILARITY, STATE_RENAME, parse_header_renamefrom },
+ { "copy from " , STATE_SIMILARITY, STATE_COPY, parse_header_copyfrom },
+ { "rename to " , STATE_RENAME, STATE_END, parse_header_renameto },
+ { "rename new " , STATE_RENAME, STATE_END, parse_header_renameto },
+ { "copy to " , STATE_COPY, STATE_END, parse_header_copyto },
+
+ /* Next patch */
+ { "diff --git " , STATE_END, 0, NULL },
+ { "@@ -" , STATE_END, 0, NULL },
+ { "-- " , STATE_INDEX, 0, NULL },
+ { "-- " , STATE_END, 0, NULL },
+};
+
+static int parse_header_git(
+ git_patch_parsed *patch,
+ git_patch_parse_ctx *ctx)
+{
+ size_t i;
+ int error = 0;
+ parse_header_state state = STATE_START;
+
+ /* Parse remaining header lines */
+ for (; ctx->parse_ctx.remain_len > 0; git_parse_advance_line(&ctx->parse_ctx)) {
+ bool found = false;
+
+ if (ctx->parse_ctx.line_len == 0 || ctx->parse_ctx.line[ctx->parse_ctx.line_len - 1] != '\n')
+ break;
+
+ for (i = 0; i < ARRAY_SIZE(transitions); i++) {
+ const parse_header_transition *transition = &transitions[i];
+ size_t op_len = strlen(transition->str);
+
+ if (transition->expected_state != state ||
+ git__prefixcmp(ctx->parse_ctx.line, transition->str) != 0)
+ continue;
+
+ state = transition->next_state;
+
+ /* Do not advance if this is the patch separator */
+ if (transition->fn == NULL)
+ goto done;
+
+ git_parse_advance_chars(&ctx->parse_ctx, op_len);
+
+ if ((error = transition->fn(patch, ctx)) < 0)
+ goto done;
+
+ git_parse_advance_ws(&ctx->parse_ctx);
+
+ if (git_parse_advance_expected_str(&ctx->parse_ctx, "\n") < 0 ||
+ ctx->parse_ctx.line_len > 0) {
+ error = git_parse_err("trailing data at line %"PRIuZ, ctx->parse_ctx.line_num);
+ goto done;
+ }
+
+ found = true;
+ break;
+ }
+
+ if (!found) {
+ error = git_parse_err("invalid patch header at line %"PRIuZ,
+ ctx->parse_ctx.line_num);
+ goto done;
+ }
+ }
+
+ if (state != STATE_END) {
+ error = git_parse_err("unexpected header line %"PRIuZ, ctx->parse_ctx.line_num);
+ goto done;
+ }
+
+done:
+ return error;
+}
+
+static int parse_int(int *out, git_patch_parse_ctx *ctx)
+{
+ int64_t num;
+
+ if (git_parse_advance_digit(&num, &ctx->parse_ctx, 10) < 0 || !git__is_int(num))
+ return -1;
+
+ *out = (int)num;
+ return 0;
+}
+
+static int parse_hunk_header(
+ git_patch_hunk *hunk,
+ git_patch_parse_ctx *ctx)
+{
+ const char *header_start = ctx->parse_ctx.line;
+ char c;
+
+ hunk->hunk.old_lines = 1;
+ hunk->hunk.new_lines = 1;
+
+ if (git_parse_advance_expected_str(&ctx->parse_ctx, "@@ -") < 0 ||
+ parse_int(&hunk->hunk.old_start, ctx) < 0)
+ goto fail;
+
+ if (git_parse_peek(&c, &ctx->parse_ctx, 0) == 0 && c == ',') {
+ if (git_parse_advance_expected_str(&ctx->parse_ctx, ",") < 0 ||
+ parse_int(&hunk->hunk.old_lines, ctx) < 0)
+ goto fail;
+ }
+
+ if (git_parse_advance_expected_str(&ctx->parse_ctx, " +") < 0 ||
+ parse_int(&hunk->hunk.new_start, ctx) < 0)
+ goto fail;
+
+ if (git_parse_peek(&c, &ctx->parse_ctx, 0) == 0 && c == ',') {
+ if (git_parse_advance_expected_str(&ctx->parse_ctx, ",") < 0 ||
+ parse_int(&hunk->hunk.new_lines, ctx) < 0)
+ goto fail;
+ }
+
+ if (git_parse_advance_expected_str(&ctx->parse_ctx, " @@") < 0)
+ goto fail;
+
+ git_parse_advance_line(&ctx->parse_ctx);
+
+ if (!hunk->hunk.old_lines && !hunk->hunk.new_lines)
+ goto fail;
+
+ hunk->hunk.header_len = ctx->parse_ctx.line - header_start;
+ if (hunk->hunk.header_len > (GIT_DIFF_HUNK_HEADER_SIZE - 1))
+ return git_parse_err("oversized patch hunk header at line %"PRIuZ,
+ ctx->parse_ctx.line_num);
+
+ memcpy(hunk->hunk.header, header_start, hunk->hunk.header_len);
+ hunk->hunk.header[hunk->hunk.header_len] = '\0';
+
+ return 0;
+
+fail:
+ git_error_set(GIT_ERROR_PATCH, "invalid patch hunk header at line %"PRIuZ,
+ ctx->parse_ctx.line_num);
+ return -1;
+}
+
+static int eof_for_origin(int origin) {
+ if (origin == GIT_DIFF_LINE_ADDITION)
+ return GIT_DIFF_LINE_ADD_EOFNL;
+ if (origin == GIT_DIFF_LINE_DELETION)
+ return GIT_DIFF_LINE_DEL_EOFNL;
+ return GIT_DIFF_LINE_CONTEXT_EOFNL;
+}
+
+static int parse_hunk_body(
+ git_patch_parsed *patch,
+ git_patch_hunk *hunk,
+ git_patch_parse_ctx *ctx)
+{
+ git_diff_line *line;
+ int error = 0;
+
+ int oldlines = hunk->hunk.old_lines;
+ int newlines = hunk->hunk.new_lines;
+ int last_origin = 0;
+
+ for (;
+ ctx->parse_ctx.remain_len > 1 &&
+ (oldlines || newlines) &&
+ !git_parse_ctx_contains_s(&ctx->parse_ctx, "@@ -");
+ git_parse_advance_line(&ctx->parse_ctx)) {
+
+ int old_lineno, new_lineno, origin, prefix = 1;
+ char c;
+
+ if (git__add_int_overflow(&old_lineno, hunk->hunk.old_start, hunk->hunk.old_lines) ||
+ git__sub_int_overflow(&old_lineno, old_lineno, oldlines) ||
+ git__add_int_overflow(&new_lineno, hunk->hunk.new_start, hunk->hunk.new_lines) ||
+ git__sub_int_overflow(&new_lineno, new_lineno, newlines)) {
+ error = git_parse_err("unrepresentable line count at line %"PRIuZ,
+ ctx->parse_ctx.line_num);
+ goto done;
+ }
+
+ if (ctx->parse_ctx.line_len == 0 || ctx->parse_ctx.line[ctx->parse_ctx.line_len - 1] != '\n') {
+ error = git_parse_err("invalid patch instruction at line %"PRIuZ,
+ ctx->parse_ctx.line_num);
+ goto done;
+ }
+
+ git_parse_peek(&c, &ctx->parse_ctx, 0);
+
+ switch (c) {
+ case '\n':
+ prefix = 0;
+ /* fall through */
+
+ case ' ':
+ origin = GIT_DIFF_LINE_CONTEXT;
+ oldlines--;
+ newlines--;
+ break;
+
+ case '-':
+ origin = GIT_DIFF_LINE_DELETION;
+ oldlines--;
+ new_lineno = -1;
+ break;
+
+ case '+':
+ origin = GIT_DIFF_LINE_ADDITION;
+ newlines--;
+ old_lineno = -1;
+ break;
+
+ case '\\':
+ /*
+ * If there are no oldlines left, then this is probably
+ * the "\ No newline at end of file" marker. Do not
+ * verify its format, as it may be localized.
+ */
+ if (!oldlines) {
+ prefix = 0;
+ origin = eof_for_origin(last_origin);
+ old_lineno = -1;
+ new_lineno = -1;
+ break;
+ }
+ /* fall through */
+
+ default:
+ error = git_parse_err("invalid patch hunk at line %"PRIuZ, ctx->parse_ctx.line_num);
+ goto done;
+ }
+
+ line = git_array_alloc(patch->base.lines);
+ GIT_ERROR_CHECK_ALLOC(line);
+
+ memset(line, 0x0, sizeof(git_diff_line));
+
+ line->content_len = ctx->parse_ctx.line_len - prefix;
+ line->content = git__strndup(ctx->parse_ctx.line + prefix, line->content_len);
+ GIT_ERROR_CHECK_ALLOC(line->content);
+ line->content_offset = ctx->parse_ctx.content_len - ctx->parse_ctx.remain_len;
+ line->origin = origin;
+ line->num_lines = 1;
+ line->old_lineno = old_lineno;
+ line->new_lineno = new_lineno;
+
+ hunk->line_count++;
+
+ last_origin = origin;
+ }
+
+ if (oldlines || newlines) {
+ error = git_parse_err(
+ "invalid patch hunk, expected %d old lines and %d new lines",
+ hunk->hunk.old_lines, hunk->hunk.new_lines);
+ goto done;
+ }
+
+ /*
+ * Handle "\ No newline at end of file". Only expect the leading
+ * backslash, though, because the rest of the string could be
+ * localized. Because `diff` optimizes for the case where you
+ * want to apply the patch by hand.
+ */
+ if (git_parse_ctx_contains_s(&ctx->parse_ctx, "\\ ") &&
+ git_array_size(patch->base.lines) > 0) {
+
+ line = git_array_get(patch->base.lines, git_array_size(patch->base.lines) - 1);
+
+ if (line->content_len < 1) {
+ error = git_parse_err("last line has no trailing newline");
+ goto done;
+ }
+
+ line = git_array_alloc(patch->base.lines);
+ GIT_ERROR_CHECK_ALLOC(line);
+
+ memset(line, 0x0, sizeof(git_diff_line));
+
+ line->content_len = ctx->parse_ctx.line_len;
+ line->content = git__strndup(ctx->parse_ctx.line, line->content_len);
+ GIT_ERROR_CHECK_ALLOC(line->content);
+ line->content_offset = ctx->parse_ctx.content_len - ctx->parse_ctx.remain_len;
+ line->origin = eof_for_origin(last_origin);
+ line->num_lines = 1;
+ line->old_lineno = -1;
+ line->new_lineno = -1;
+
+ hunk->line_count++;
+
+ git_parse_advance_line(&ctx->parse_ctx);
+ }
+
+done:
+ return error;
+}
+
+static int parse_patch_header(
+ git_patch_parsed *patch,
+ git_patch_parse_ctx *ctx)
+{
+ int error = 0;
+
+ for (; ctx->parse_ctx.remain_len > 0; git_parse_advance_line(&ctx->parse_ctx)) {
+ /* This line is too short to be a patch header. */
+ if (ctx->parse_ctx.line_len < 6)
+ continue;
+
+ /* This might be a hunk header without a patch header, provide a
+ * sensible error message. */
+ if (git_parse_ctx_contains_s(&ctx->parse_ctx, "@@ -")) {
+ size_t line_num = ctx->parse_ctx.line_num;
+ git_patch_hunk hunk;
+
+ /* If this cannot be parsed as a hunk header, it's just leading
+ * noise, continue.
+ */
+ if (parse_hunk_header(&hunk, ctx) < 0) {
+ git_error_clear();
+ continue;
+ }
+
+ error = git_parse_err("invalid hunk header outside patch at line %"PRIuZ,
+ line_num);
+ goto done;
+ }
+
+ /* This buffer is too short to contain a patch. */
+ if (ctx->parse_ctx.remain_len < ctx->parse_ctx.line_len + 6)
+ break;
+
+ /* A proper git patch */
+ if (git_parse_ctx_contains_s(&ctx->parse_ctx, "diff --git ")) {
+ error = parse_header_git(patch, ctx);
+ goto done;
+ }
+
+ error = 0;
+ continue;
+ }
+
+ git_error_set(GIT_ERROR_PATCH, "no patch found");
+ error = GIT_ENOTFOUND;
+
+done:
+ return error;
+}
+
+static int parse_patch_binary_side(
+ git_diff_binary_file *binary,
+ git_patch_parse_ctx *ctx)
+{
+ git_diff_binary_t type = GIT_DIFF_BINARY_NONE;
+ git_str base85 = GIT_STR_INIT, decoded = GIT_STR_INIT;
+ int64_t len;
+ int error = 0;
+
+ if (git_parse_ctx_contains_s(&ctx->parse_ctx, "literal ")) {
+ type = GIT_DIFF_BINARY_LITERAL;
+ git_parse_advance_chars(&ctx->parse_ctx, 8);
+ } else if (git_parse_ctx_contains_s(&ctx->parse_ctx, "delta ")) {
+ type = GIT_DIFF_BINARY_DELTA;
+ git_parse_advance_chars(&ctx->parse_ctx, 6);
+ } else {
+ error = git_parse_err(
+ "unknown binary delta type at line %"PRIuZ, ctx->parse_ctx.line_num);
+ goto done;
+ }
+
+ if (git_parse_advance_digit(&len, &ctx->parse_ctx, 10) < 0 ||
+ git_parse_advance_nl(&ctx->parse_ctx) < 0 || len < 0) {
+ error = git_parse_err("invalid binary size at line %"PRIuZ, ctx->parse_ctx.line_num);
+ goto done;
+ }
+
+ while (ctx->parse_ctx.line_len) {
+ char c;
+ size_t encoded_len, decoded_len = 0, decoded_orig = decoded.size;
+
+ git_parse_peek(&c, &ctx->parse_ctx, 0);
+
+ if (c == '\n')
+ break;
+ else if (c >= 'A' && c <= 'Z')
+ decoded_len = c - 'A' + 1;
+ else if (c >= 'a' && c <= 'z')
+ decoded_len = c - 'a' + (('z' - 'a') + 1) + 1;
+
+ if (!decoded_len) {
+ error = git_parse_err("invalid binary length at line %"PRIuZ, ctx->parse_ctx.line_num);
+ goto done;
+ }
+
+ git_parse_advance_chars(&ctx->parse_ctx, 1);
+
+ encoded_len = ((decoded_len / 4) + !!(decoded_len % 4)) * 5;
+
+ if (!encoded_len || !ctx->parse_ctx.line_len || encoded_len > ctx->parse_ctx.line_len - 1) {
+ error = git_parse_err("truncated binary data at line %"PRIuZ, ctx->parse_ctx.line_num);
+ goto done;
+ }
+
+ if ((error = git_str_decode_base85(
+ &decoded, ctx->parse_ctx.line, encoded_len, decoded_len)) < 0)
+ goto done;
+
+ if (decoded.size - decoded_orig != decoded_len) {
+ error = git_parse_err("truncated binary data at line %"PRIuZ, ctx->parse_ctx.line_num);
+ goto done;
+ }
+
+ git_parse_advance_chars(&ctx->parse_ctx, encoded_len);
+
+ if (git_parse_advance_nl(&ctx->parse_ctx) < 0) {
+ error = git_parse_err("trailing data at line %"PRIuZ, ctx->parse_ctx.line_num);
+ goto done;
+ }
+ }
+
+ binary->type = type;
+ binary->inflatedlen = (size_t)len;
+ binary->datalen = decoded.size;
+ binary->data = git_str_detach(&decoded);
+
+done:
+ git_str_dispose(&base85);
+ git_str_dispose(&decoded);
+ return error;
+}
+
+static int parse_patch_binary(
+ git_patch_parsed *patch,
+ git_patch_parse_ctx *ctx)
+{
+ int error;
+
+ if (git_parse_advance_expected_str(&ctx->parse_ctx, "GIT binary patch") < 0 ||
+ git_parse_advance_nl(&ctx->parse_ctx) < 0)
+ return git_parse_err("corrupt git binary header at line %"PRIuZ, ctx->parse_ctx.line_num);
+
+ /* parse old->new binary diff */
+ if ((error = parse_patch_binary_side(
+ &patch->base.binary.new_file, ctx)) < 0)
+ return error;
+
+ if (git_parse_advance_nl(&ctx->parse_ctx) < 0)
+ return git_parse_err("corrupt git binary separator at line %"PRIuZ,
+ ctx->parse_ctx.line_num);
+
+ /* parse new->old binary diff */
+ if ((error = parse_patch_binary_side(
+ &patch->base.binary.old_file, ctx)) < 0)
+ return error;
+
+ if (git_parse_advance_nl(&ctx->parse_ctx) < 0)
+ return git_parse_err("corrupt git binary patch separator at line %"PRIuZ,
+ ctx->parse_ctx.line_num);
+
+ patch->base.binary.contains_data = 1;
+ patch->base.delta->flags |= GIT_DIFF_FLAG_BINARY;
+ return 0;
+}
+
+static int parse_patch_binary_nodata(
+ git_patch_parsed *patch,
+ git_patch_parse_ctx *ctx)
+{
+ const char *old = patch->old_path ? patch->old_path : patch->header_old_path;
+ const char *new = patch->new_path ? patch->new_path : patch->header_new_path;
+
+ if (!old || !new)
+ return git_parse_err("corrupt binary data without paths at line %"PRIuZ, ctx->parse_ctx.line_num);
+
+ if (patch->base.delta->status == GIT_DELTA_ADDED)
+ old = "/dev/null";
+ else if (patch->base.delta->status == GIT_DELTA_DELETED)
+ new = "/dev/null";
+
+ if (git_parse_advance_expected_str(&ctx->parse_ctx, "Binary files ") < 0 ||
+ git_parse_advance_expected_str(&ctx->parse_ctx, old) < 0 ||
+ git_parse_advance_expected_str(&ctx->parse_ctx, " and ") < 0 ||
+ git_parse_advance_expected_str(&ctx->parse_ctx, new) < 0 ||
+ git_parse_advance_expected_str(&ctx->parse_ctx, " differ") < 0 ||
+ git_parse_advance_nl(&ctx->parse_ctx) < 0)
+ return git_parse_err("corrupt git binary header at line %"PRIuZ, ctx->parse_ctx.line_num);
+
+ patch->base.binary.contains_data = 0;
+ patch->base.delta->flags |= GIT_DIFF_FLAG_BINARY;
+ return 0;
+}
+
+static int parse_patch_hunks(
+ git_patch_parsed *patch,
+ git_patch_parse_ctx *ctx)
+{
+ git_patch_hunk *hunk;
+ int error = 0;
+
+ while (git_parse_ctx_contains_s(&ctx->parse_ctx, "@@ -")) {
+ hunk = git_array_alloc(patch->base.hunks);
+ GIT_ERROR_CHECK_ALLOC(hunk);
+
+ memset(hunk, 0, sizeof(git_patch_hunk));
+
+ hunk->line_start = git_array_size(patch->base.lines);
+ hunk->line_count = 0;
+
+ if ((error = parse_hunk_header(hunk, ctx)) < 0 ||
+ (error = parse_hunk_body(patch, hunk, ctx)) < 0)
+ goto done;
+ }
+
+ patch->base.delta->flags |= GIT_DIFF_FLAG_NOT_BINARY;
+
+done:
+ return error;
+}
+
+static int parse_patch_body(
+ git_patch_parsed *patch, git_patch_parse_ctx *ctx)
+{
+ if (git_parse_ctx_contains_s(&ctx->parse_ctx, "GIT binary patch"))
+ return parse_patch_binary(patch, ctx);
+ else if (git_parse_ctx_contains_s(&ctx->parse_ctx, "Binary files "))
+ return parse_patch_binary_nodata(patch, ctx);
+ else
+ return parse_patch_hunks(patch, ctx);
+}
+
+static int check_header_names(
+ const char *one,
+ const char *two,
+ const char *old_or_new,
+ bool two_null)
+{
+ if (!one || !two)
+ return 0;
+
+ if (two_null && strcmp(two, "/dev/null") != 0)
+ return git_parse_err("expected %s path of '/dev/null'", old_or_new);
+
+ else if (!two_null && strcmp(one, two) != 0)
+ return git_parse_err("mismatched %s path names", old_or_new);
+
+ return 0;
+}
+
+static int check_prefix(
+ char **out,
+ size_t *out_len,
+ git_patch_parsed *patch,
+ const char *path_start)
+{
+ const char *path = path_start;
+ size_t prefix_len = patch->ctx->opts.prefix_len;
+ size_t remain_len = prefix_len;
+
+ *out = NULL;
+ *out_len = 0;
+
+ if (prefix_len == 0)
+ goto done;
+
+ /* leading slashes do not count as part of the prefix in git apply */
+ while (*path == '/')
+ path++;
+
+ while (*path && remain_len) {
+ if (*path == '/')
+ remain_len--;
+
+ path++;
+ }
+
+ if (remain_len || !*path)
+ return git_parse_err(
+ "header filename does not contain %"PRIuZ" path components",
+ prefix_len);
+
+done:
+ *out_len = (path - path_start);
+ *out = git__strndup(path_start, *out_len);
+
+ return (*out == NULL) ? -1 : 0;
+}
+
+static int check_filenames(git_patch_parsed *patch)
+{
+ const char *prefixed_new, *prefixed_old;
+ size_t old_prefixlen = 0, new_prefixlen = 0;
+ bool added = (patch->base.delta->status == GIT_DELTA_ADDED);
+ bool deleted = (patch->base.delta->status == GIT_DELTA_DELETED);
+
+ if (patch->old_path && !patch->new_path)
+ return git_parse_err("missing new path");
+
+ if (!patch->old_path && patch->new_path)
+ return git_parse_err("missing old path");
+
+ /* Ensure (non-renamed) paths match */
+ if (check_header_names(patch->header_old_path, patch->old_path, "old", added) < 0 ||
+ check_header_names(patch->header_new_path, patch->new_path, "new", deleted) < 0)
+ return -1;
+
+ prefixed_old = (!added && patch->old_path) ? patch->old_path : patch->header_old_path;
+ prefixed_new = (!deleted && patch->new_path) ? patch->new_path : patch->header_new_path;
+
+ if ((prefixed_old && check_prefix(&patch->old_prefix, &old_prefixlen, patch, prefixed_old) < 0) ||
+ (prefixed_new && check_prefix(&patch->new_prefix, &new_prefixlen, patch, prefixed_new) < 0))
+ return -1;
+
+ /* Prefer the rename filenames as they are unambiguous and unprefixed */
+ if (patch->rename_old_path)
+ patch->base.delta->old_file.path = patch->rename_old_path;
+ else if (prefixed_old)
+ patch->base.delta->old_file.path = prefixed_old + old_prefixlen;
+ else
+ patch->base.delta->old_file.path = NULL;
+
+ if (patch->rename_new_path)
+ patch->base.delta->new_file.path = patch->rename_new_path;
+ else if (prefixed_new)
+ patch->base.delta->new_file.path = prefixed_new + new_prefixlen;
+ else
+ patch->base.delta->new_file.path = NULL;
+
+ if (!patch->base.delta->old_file.path &&
+ !patch->base.delta->new_file.path)
+ return git_parse_err("git diff header lacks old / new paths");
+
+ return 0;
+}
+
+static int check_patch(git_patch_parsed *patch)
+{
+ git_diff_delta *delta = patch->base.delta;
+
+ if (check_filenames(patch) < 0)
+ return -1;
+
+ if (delta->old_file.path &&
+ delta->status != GIT_DELTA_DELETED &&
+ !delta->new_file.mode)
+ delta->new_file.mode = delta->old_file.mode;
+
+ if (delta->status == GIT_DELTA_MODIFIED &&
+ !(delta->flags & GIT_DIFF_FLAG_BINARY) &&
+ delta->new_file.mode == delta->old_file.mode &&
+ git_array_size(patch->base.hunks) == 0)
+ return git_parse_err("patch with no hunks");
+
+ if (delta->status == GIT_DELTA_ADDED) {
+ git_oid_clear(&delta->old_file.id,
+ patch->base.diff_opts.oid_type);
+ delta->old_file.id_abbrev = 0;
+ }
+
+ if (delta->status == GIT_DELTA_DELETED) {
+ git_oid_clear(&delta->new_file.id,
+ patch->base.diff_opts.oid_type);
+ delta->new_file.id_abbrev = 0;
+ }
+
+ return 0;
+}
+
+git_patch_parse_ctx *git_patch_parse_ctx_init(
+ const char *content,
+ size_t content_len,
+ const git_patch_options *opts)
+{
+ git_patch_parse_ctx *ctx;
+ git_patch_options default_opts = GIT_PATCH_OPTIONS_INIT;
+
+ if ((ctx = git__calloc(1, sizeof(git_patch_parse_ctx))) == NULL)
+ return NULL;
+
+ if ((git_parse_ctx_init(&ctx->parse_ctx, content, content_len)) < 0) {
+ git__free(ctx);
+ return NULL;
+ }
+
+ if (opts)
+ memcpy(&ctx->opts, opts, sizeof(git_patch_options));
+ else
+ memcpy(&ctx->opts, &default_opts, sizeof(git_patch_options));
+
+ GIT_REFCOUNT_INC(ctx);
+ return ctx;
+}
+
+static void patch_parse_ctx_free(git_patch_parse_ctx *ctx)
+{
+ if (!ctx)
+ return;
+
+ git_parse_ctx_clear(&ctx->parse_ctx);
+ git__free(ctx);
+}
+
+void git_patch_parse_ctx_free(git_patch_parse_ctx *ctx)
+{
+ GIT_REFCOUNT_DEC(ctx, patch_parse_ctx_free);
+}
+
+int git_patch_parsed_from_diff(git_patch **out, git_diff *d, size_t idx)
+{
+ git_diff_parsed *diff = (git_diff_parsed *)d;
+ git_patch *p;
+
+ if ((p = git_vector_get(&diff->patches, idx)) == NULL)
+ return -1;
+
+ GIT_REFCOUNT_INC(p);
+ *out = p;
+
+ return 0;
+}
+
+static void patch_parsed__free(git_patch *p)
+{
+ git_patch_parsed *patch = (git_patch_parsed *)p;
+ git_diff_line *line;
+ size_t i;
+
+ if (!patch)
+ return;
+
+ git_patch_parse_ctx_free(patch->ctx);
+
+ git__free((char *)patch->base.binary.old_file.data);
+ git__free((char *)patch->base.binary.new_file.data);
+ git_array_clear(patch->base.hunks);
+ git_array_foreach(patch->base.lines, i, line)
+ git__free((char *) line->content);
+ git_array_clear(patch->base.lines);
+ git__free(patch->base.delta);
+
+ git__free(patch->old_prefix);
+ git__free(patch->new_prefix);
+ git__free(patch->header_old_path);
+ git__free(patch->header_new_path);
+ git__free(patch->rename_old_path);
+ git__free(patch->rename_new_path);
+ git__free(patch->old_path);
+ git__free(patch->new_path);
+ git__free(patch);
+}
+
+int git_patch_parse(
+ git_patch **out,
+ git_patch_parse_ctx *ctx)
+{
+ git_patch_parsed *patch;
+ size_t start, used;
+ int error = 0;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(ctx);
+
+ *out = NULL;
+
+ patch = git__calloc(1, sizeof(git_patch_parsed));
+ GIT_ERROR_CHECK_ALLOC(patch);
+
+ patch->ctx = ctx;
+ GIT_REFCOUNT_INC(patch->ctx);
+
+ patch->base.free_fn = patch_parsed__free;
+
+ patch->base.delta = git__calloc(1, sizeof(git_diff_delta));
+ GIT_ERROR_CHECK_ALLOC(patch->base.delta);
+
+ patch->base.delta->status = GIT_DELTA_MODIFIED;
+ patch->base.delta->nfiles = 2;
+
+ patch->base.diff_opts.oid_type = ctx->opts.oid_type;
+
+ start = ctx->parse_ctx.remain_len;
+
+ if ((error = parse_patch_header(patch, ctx)) < 0 ||
+ (error = parse_patch_body(patch, ctx)) < 0 ||
+ (error = check_patch(patch)) < 0)
+ goto done;
+
+ used = start - ctx->parse_ctx.remain_len;
+ ctx->parse_ctx.remain += used;
+
+ patch->base.diff_opts.old_prefix = patch->old_prefix;
+ patch->base.diff_opts.new_prefix = patch->new_prefix;
+ patch->base.diff_opts.flags |= GIT_DIFF_SHOW_BINARY;
+
+ GIT_REFCOUNT_INC(&patch->base);
+ *out = &patch->base;
+
+done:
+ if (error < 0)
+ patch_parsed__free(&patch->base);
+
+ return error;
+}
+
+int git_patch_from_buffer(
+ git_patch **out,
+ const char *content,
+ size_t content_len,
+ const git_patch_options *opts)
+{
+ git_patch_parse_ctx *ctx;
+ int error;
+
+ ctx = git_patch_parse_ctx_init(content, content_len, opts);
+ GIT_ERROR_CHECK_ALLOC(ctx);
+
+ error = git_patch_parse(out, ctx);
+
+ git_patch_parse_ctx_free(ctx);
+ return error;
+}
+
diff --git a/src/libgit2/patch_parse.h b/src/libgit2/patch_parse.h
new file mode 100644
index 0000000..140629d
--- /dev/null
+++ b/src/libgit2/patch_parse.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_patch_parse_h__
+#define INCLUDE_patch_parse_h__
+
+#include "common.h"
+
+#include "parse.h"
+#include "patch.h"
+
+typedef struct {
+ git_refcount rc;
+
+ git_patch_options opts;
+
+ git_parse_ctx parse_ctx;
+} git_patch_parse_ctx;
+
+extern git_patch_parse_ctx *git_patch_parse_ctx_init(
+ const char *content,
+ size_t content_len,
+ const git_patch_options *opts);
+
+extern void git_patch_parse_ctx_free(git_patch_parse_ctx *ctx);
+
+/**
+ * Create a patch for a single file from the contents of a patch buffer.
+ *
+ * @param out The patch to be created
+ * @param contents The contents of a patch file
+ * @param contents_len The length of the patch file
+ * @param opts The git_patch_options
+ * @return 0 on success, <0 on failure.
+ */
+extern int git_patch_from_buffer(
+ git_patch **out,
+ const char *contents,
+ size_t contents_len,
+ const git_patch_options *opts);
+
+extern int git_patch_parse(
+ git_patch **out,
+ git_patch_parse_ctx *ctx);
+
+extern int git_patch_parsed_from_diff(git_patch **, git_diff *, size_t);
+
+#endif
diff --git a/src/libgit2/path.c b/src/libgit2/path.c
new file mode 100644
index 0000000..a19340e
--- /dev/null
+++ b/src/libgit2/path.c
@@ -0,0 +1,375 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "path.h"
+
+#include "repository.h"
+#include "fs_path.h"
+#include "utf8.h"
+
+typedef struct {
+ git_repository *repo;
+ uint16_t file_mode;
+ unsigned int flags;
+} repository_path_validate_data;
+
+static int32_t next_hfs_char(const char **in, size_t *len)
+{
+ while (*len) {
+ uint32_t codepoint;
+ int cp_len = git_utf8_iterate(&codepoint, *in, *len);
+ if (cp_len < 0)
+ return -1;
+
+ (*in) += cp_len;
+ (*len) -= cp_len;
+
+ /* these code points are ignored completely */
+ switch (codepoint) {
+ case 0x200c: /* ZERO WIDTH NON-JOINER */
+ case 0x200d: /* ZERO WIDTH JOINER */
+ case 0x200e: /* LEFT-TO-RIGHT MARK */
+ case 0x200f: /* RIGHT-TO-LEFT MARK */
+ case 0x202a: /* LEFT-TO-RIGHT EMBEDDING */
+ case 0x202b: /* RIGHT-TO-LEFT EMBEDDING */
+ case 0x202c: /* POP DIRECTIONAL FORMATTING */
+ case 0x202d: /* LEFT-TO-RIGHT OVERRIDE */
+ case 0x202e: /* RIGHT-TO-LEFT OVERRIDE */
+ case 0x206a: /* INHIBIT SYMMETRIC SWAPPING */
+ case 0x206b: /* ACTIVATE SYMMETRIC SWAPPING */
+ case 0x206c: /* INHIBIT ARABIC FORM SHAPING */
+ case 0x206d: /* ACTIVATE ARABIC FORM SHAPING */
+ case 0x206e: /* NATIONAL DIGIT SHAPES */
+ case 0x206f: /* NOMINAL DIGIT SHAPES */
+ case 0xfeff: /* ZERO WIDTH NO-BREAK SPACE */
+ continue;
+ }
+
+ /* fold into lowercase -- this will only fold characters in
+ * the ASCII range, which is perfectly fine, because the
+ * git folder name can only be composed of ascii characters
+ */
+ return git__tolower((int)codepoint);
+ }
+ return 0; /* NULL byte -- end of string */
+}
+
+static bool validate_dotgit_hfs_generic(
+ const char *path,
+ size_t len,
+ const char *needle,
+ size_t needle_len)
+{
+ size_t i;
+ char c;
+
+ if (next_hfs_char(&path, &len) != '.')
+ return true;
+
+ for (i = 0; i < needle_len; i++) {
+ c = next_hfs_char(&path, &len);
+ if (c != needle[i])
+ return true;
+ }
+
+ if (next_hfs_char(&path, &len) != '\0')
+ return true;
+
+ return false;
+}
+
+static bool validate_dotgit_hfs(const char *path, size_t len)
+{
+ return validate_dotgit_hfs_generic(path, len, "git", CONST_STRLEN("git"));
+}
+
+GIT_INLINE(bool) validate_dotgit_ntfs(
+ git_repository *repo,
+ const char *path,
+ size_t len)
+{
+ git_str *reserved = git_repository__reserved_names_win32;
+ size_t reserved_len = git_repository__reserved_names_win32_len;
+ size_t start = 0, i;
+
+ if (repo)
+ git_repository__reserved_names(&reserved, &reserved_len, repo, true);
+
+ for (i = 0; i < reserved_len; i++) {
+ git_str *r = &reserved[i];
+
+ if (len >= r->size &&
+ strncasecmp(path, r->ptr, r->size) == 0) {
+ start = r->size;
+ break;
+ }
+ }
+
+ if (!start)
+ return true;
+
+ /*
+ * Reject paths that start with Windows-style directory separators
+ * (".git\") or NTFS alternate streams (".git:") and could be used
+ * to write to the ".git" directory on Windows platforms.
+ */
+ if (path[start] == '\\' || path[start] == ':')
+ return false;
+
+ /* Reject paths like '.git ' or '.git.' */
+ for (i = start; i < len; i++) {
+ if (path[i] != ' ' && path[i] != '.')
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Windows paths that end with spaces and/or dots are elided to the
+ * path without them for backward compatibility. That is to say
+ * that opening file "foo ", "foo." or even "foo . . ." will all
+ * map to a filename of "foo". This function identifies spaces and
+ * dots at the end of a filename, whether the proper end of the
+ * filename (end of string) or a colon (which would indicate a
+ * Windows alternate data stream.)
+ */
+GIT_INLINE(bool) ntfs_end_of_filename(const char *path)
+{
+ const char *c = path;
+
+ for (;; c++) {
+ if (*c == '\0' || *c == ':')
+ return true;
+ if (*c != ' ' && *c != '.')
+ return false;
+ }
+
+ return true;
+}
+
+GIT_INLINE(bool) validate_dotgit_ntfs_generic(
+ const char *name,
+ size_t len,
+ const char *dotgit_name,
+ size_t dotgit_len,
+ const char *shortname_pfix)
+{
+ int i, saw_tilde;
+
+ if (name[0] == '.' && len >= dotgit_len &&
+ !strncasecmp(name + 1, dotgit_name, dotgit_len)) {
+ return !ntfs_end_of_filename(name + dotgit_len + 1);
+ }
+
+ /* Detect the basic NTFS shortname with the first six chars */
+ if (!strncasecmp(name, dotgit_name, 6) && name[6] == '~' &&
+ name[7] >= '1' && name[7] <= '4')
+ return !ntfs_end_of_filename(name + 8);
+
+ /* Catch fallback names */
+ for (i = 0, saw_tilde = 0; i < 8; i++) {
+ if (name[i] == '\0') {
+ return true;
+ } else if (saw_tilde) {
+ if (name[i] < '0' || name[i] > '9')
+ return true;
+ } else if (name[i] == '~') {
+ if (name[i+1] < '1' || name[i+1] > '9')
+ return true;
+ saw_tilde = 1;
+ } else if (i >= 6) {
+ return true;
+ } else if ((unsigned char)name[i] > 127) {
+ return true;
+ } else if (git__tolower(name[i]) != shortname_pfix[i]) {
+ return true;
+ }
+ }
+
+ return !ntfs_end_of_filename(name + i);
+}
+
+/*
+ * Return the length of the common prefix between str and prefix, comparing them
+ * case-insensitively (must be ASCII to match).
+ */
+GIT_INLINE(size_t) common_prefix_icase(const char *str, size_t len, const char *prefix)
+{
+ size_t count = 0;
+
+ while (len > 0 && tolower(*str) == tolower(*prefix)) {
+ count++;
+ str++;
+ prefix++;
+ len--;
+ }
+
+ return count;
+}
+
+static bool validate_repo_component(
+ const char *component,
+ size_t len,
+ void *payload)
+{
+ repository_path_validate_data *data = (repository_path_validate_data *)payload;
+
+ if (data->flags & GIT_PATH_REJECT_DOT_GIT_HFS) {
+ if (!validate_dotgit_hfs(component, len))
+ return false;
+
+ if (S_ISLNK(data->file_mode) &&
+ git_path_is_gitfile(component, len, GIT_PATH_GITFILE_GITMODULES, GIT_PATH_FS_HFS))
+ return false;
+ }
+
+ if (data->flags & GIT_PATH_REJECT_DOT_GIT_NTFS) {
+ if (!validate_dotgit_ntfs(data->repo, component, len))
+ return false;
+
+ if (S_ISLNK(data->file_mode) &&
+ git_path_is_gitfile(component, len, GIT_PATH_GITFILE_GITMODULES, GIT_PATH_FS_NTFS))
+ return false;
+ }
+
+ /* don't bother rerunning the `.git` test if we ran the HFS or NTFS
+ * specific tests, they would have already rejected `.git`.
+ */
+ if ((data->flags & GIT_PATH_REJECT_DOT_GIT_HFS) == 0 &&
+ (data->flags & GIT_PATH_REJECT_DOT_GIT_NTFS) == 0 &&
+ (data->flags & GIT_PATH_REJECT_DOT_GIT_LITERAL)) {
+ if (len >= 4 &&
+ component[0] == '.' &&
+ (component[1] == 'g' || component[1] == 'G') &&
+ (component[2] == 'i' || component[2] == 'I') &&
+ (component[3] == 't' || component[3] == 'T')) {
+ if (len == 4)
+ return false;
+
+ if (S_ISLNK(data->file_mode) &&
+ common_prefix_icase(component, len, ".gitmodules") == len)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+GIT_INLINE(unsigned int) dotgit_flags(
+ git_repository *repo,
+ unsigned int flags)
+{
+ int protectHFS = 0, protectNTFS = 1;
+ int error = 0;
+
+ flags |= GIT_PATH_REJECT_DOT_GIT_LITERAL;
+
+#ifdef __APPLE__
+ protectHFS = 1;
+#endif
+
+ if (repo && !protectHFS)
+ error = git_repository__configmap_lookup(&protectHFS, repo, GIT_CONFIGMAP_PROTECTHFS);
+ if (!error && protectHFS)
+ flags |= GIT_PATH_REJECT_DOT_GIT_HFS;
+
+ if (repo)
+ error = git_repository__configmap_lookup(&protectNTFS, repo, GIT_CONFIGMAP_PROTECTNTFS);
+ if (!error && protectNTFS)
+ flags |= GIT_PATH_REJECT_DOT_GIT_NTFS;
+
+ return flags;
+}
+
+GIT_INLINE(unsigned int) length_flags(
+ git_repository *repo,
+ unsigned int flags)
+{
+#ifdef GIT_WIN32
+ int allow = 0;
+
+ if (repo &&
+ git_repository__configmap_lookup(&allow, repo, GIT_CONFIGMAP_LONGPATHS) < 0)
+ allow = 0;
+
+ if (allow)
+ flags &= ~GIT_FS_PATH_REJECT_LONG_PATHS;
+
+#else
+ GIT_UNUSED(repo);
+ flags &= ~GIT_FS_PATH_REJECT_LONG_PATHS;
+#endif
+
+ return flags;
+}
+
+bool git_path_str_is_valid(
+ git_repository *repo,
+ const git_str *path,
+ uint16_t file_mode,
+ unsigned int flags)
+{
+ repository_path_validate_data data = {0};
+
+ /* Upgrade the ".git" checks based on platform */
+ if ((flags & GIT_PATH_REJECT_DOT_GIT))
+ flags = dotgit_flags(repo, flags);
+
+ /* Update the length checks based on platform */
+ if ((flags & GIT_FS_PATH_REJECT_LONG_PATHS))
+ flags = length_flags(repo, flags);
+
+ data.repo = repo;
+ data.file_mode = file_mode;
+ data.flags = flags;
+
+ return git_fs_path_str_is_valid_ext(path, flags, NULL, validate_repo_component, NULL, &data);
+}
+
+static const struct {
+ const char *file;
+ const char *hash;
+ size_t filelen;
+} gitfiles[] = {
+ { "gitignore", "gi250a", CONST_STRLEN("gitignore") },
+ { "gitmodules", "gi7eba", CONST_STRLEN("gitmodules") },
+ { "gitattributes", "gi7d29", CONST_STRLEN("gitattributes") }
+};
+
+extern int git_path_is_gitfile(
+ const char *path,
+ size_t pathlen,
+ git_path_gitfile gitfile,
+ git_path_fs fs)
+{
+ const char *file, *hash;
+ size_t filelen;
+
+ if (!(gitfile >= GIT_PATH_GITFILE_GITIGNORE && gitfile < ARRAY_SIZE(gitfiles))) {
+ git_error_set(GIT_ERROR_OS, "invalid gitfile for path validation");
+ return -1;
+ }
+
+ file = gitfiles[gitfile].file;
+ filelen = gitfiles[gitfile].filelen;
+ hash = gitfiles[gitfile].hash;
+
+ switch (fs) {
+ case GIT_PATH_FS_GENERIC:
+ return !validate_dotgit_ntfs_generic(path, pathlen, file, filelen, hash) ||
+ !validate_dotgit_hfs_generic(path, pathlen, file, filelen);
+ case GIT_PATH_FS_NTFS:
+ return !validate_dotgit_ntfs_generic(path, pathlen, file, filelen, hash);
+ case GIT_PATH_FS_HFS:
+ return !validate_dotgit_hfs_generic(path, pathlen, file, filelen);
+ default:
+ git_error_set(GIT_ERROR_OS, "invalid filesystem for path validation");
+ return -1;
+ }
+}
+
diff --git a/src/libgit2/path.h b/src/libgit2/path.h
new file mode 100644
index 0000000..c4a2c42
--- /dev/null
+++ b/src/libgit2/path.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_path_h__
+#define INCLUDE_path_h__
+
+#include "common.h"
+
+#include "fs_path.h"
+#include <git2/sys/path.h>
+
+#define GIT_PATH_REJECT_DOT_GIT (GIT_FS_PATH_REJECT_MAX << 1)
+#define GIT_PATH_REJECT_DOT_GIT_LITERAL (GIT_FS_PATH_REJECT_MAX << 2)
+#define GIT_PATH_REJECT_DOT_GIT_HFS (GIT_FS_PATH_REJECT_MAX << 3)
+#define GIT_PATH_REJECT_DOT_GIT_NTFS (GIT_FS_PATH_REJECT_MAX << 4)
+
+/* Paths that should never be written into the working directory. */
+#define GIT_PATH_REJECT_WORKDIR_DEFAULTS \
+ GIT_FS_PATH_REJECT_FILESYSTEM_DEFAULTS | GIT_PATH_REJECT_DOT_GIT
+
+/* Paths that should never be written to the index. */
+#define GIT_PATH_REJECT_INDEX_DEFAULTS \
+ GIT_FS_PATH_REJECT_TRAVERSAL | GIT_PATH_REJECT_DOT_GIT
+
+extern bool git_path_str_is_valid(
+ git_repository *repo,
+ const git_str *path,
+ uint16_t file_mode,
+ unsigned int flags);
+
+GIT_INLINE(bool) git_path_is_valid(
+ git_repository *repo,
+ const char *path,
+ uint16_t file_mode,
+ unsigned int flags)
+{
+ git_str str = GIT_STR_INIT_CONST(path, SIZE_MAX);
+ return git_path_str_is_valid(repo, &str, file_mode, flags);
+}
+
+GIT_INLINE(int) git_path_validate_str_length(
+ git_repository *repo,
+ const git_str *path)
+{
+ if (!git_path_str_is_valid(repo, path, 0, GIT_FS_PATH_REJECT_LONG_PATHS)) {
+ if (path->size == SIZE_MAX)
+ git_error_set(GIT_ERROR_FILESYSTEM, "path too long: '%s'", path->ptr);
+ else
+ git_error_set(GIT_ERROR_FILESYSTEM, "path too long: '%.*s'", (int)path->size, path->ptr);
+
+ return -1;
+ }
+
+ return 0;
+}
+
+GIT_INLINE(int) git_path_validate_length(
+ git_repository *repo,
+ const char *path)
+{
+ git_str str = GIT_STR_INIT_CONST(path, SIZE_MAX);
+ return git_path_validate_str_length(repo, &str);
+}
+
+#endif
diff --git a/src/libgit2/pathspec.c b/src/libgit2/pathspec.c
new file mode 100644
index 0000000..3e44643
--- /dev/null
+++ b/src/libgit2/pathspec.c
@@ -0,0 +1,722 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "pathspec.h"
+
+#include "git2/pathspec.h"
+#include "git2/diff.h"
+#include "attr_file.h"
+#include "iterator.h"
+#include "repository.h"
+#include "index.h"
+#include "bitvec.h"
+#include "diff.h"
+#include "wildmatch.h"
+
+/* what is the common non-wildcard prefix for all items in the pathspec */
+char *git_pathspec_prefix(const git_strarray *pathspec)
+{
+ git_str prefix = GIT_STR_INIT;
+ const char *scan;
+
+ if (!pathspec || !pathspec->count ||
+ git_str_common_prefix(&prefix, pathspec->strings, pathspec->count) < 0)
+ return NULL;
+
+ /* diff prefix will only be leading non-wildcards */
+ for (scan = prefix.ptr; *scan; ++scan) {
+ if (git__iswildcard(*scan) &&
+ (scan == prefix.ptr || (*(scan - 1) != '\\')))
+ break;
+ }
+ git_str_truncate(&prefix, scan - prefix.ptr);
+
+ if (prefix.size <= 0) {
+ git_str_dispose(&prefix);
+ return NULL;
+ }
+
+ git_str_unescape(&prefix);
+
+ return git_str_detach(&prefix);
+}
+
+/* is there anything in the spec that needs to be filtered on */
+bool git_pathspec_is_empty(const git_strarray *pathspec)
+{
+ size_t i;
+
+ if (pathspec == NULL)
+ return true;
+
+ for (i = 0; i < pathspec->count; ++i) {
+ const char *str = pathspec->strings[i];
+
+ if (str && str[0])
+ return false;
+ }
+
+ return true;
+}
+
+/* build a vector of fnmatch patterns to evaluate efficiently */
+int git_pathspec__vinit(
+ git_vector *vspec, const git_strarray *strspec, git_pool *strpool)
+{
+ size_t i;
+
+ memset(vspec, 0, sizeof(*vspec));
+
+ if (git_pathspec_is_empty(strspec))
+ return 0;
+
+ if (git_vector_init(vspec, strspec->count, NULL) < 0)
+ return -1;
+
+ for (i = 0; i < strspec->count; ++i) {
+ int ret;
+ const char *pattern = strspec->strings[i];
+ git_attr_fnmatch *match = git__calloc(1, sizeof(git_attr_fnmatch));
+ if (!match)
+ return -1;
+
+ match->flags = GIT_ATTR_FNMATCH_ALLOWSPACE | GIT_ATTR_FNMATCH_ALLOWNEG;
+
+ ret = git_attr_fnmatch__parse(match, strpool, NULL, &pattern);
+ if (ret == GIT_ENOTFOUND) {
+ git__free(match);
+ continue;
+ } else if (ret < 0) {
+ git__free(match);
+ return ret;
+ }
+
+ if (git_vector_insert(vspec, match) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+/* free data from the pathspec vector */
+void git_pathspec__vfree(git_vector *vspec)
+{
+ git_vector_free_deep(vspec);
+}
+
+struct pathspec_match_context {
+ int wildmatch_flags;
+ int (*strcomp)(const char *, const char *);
+ int (*strncomp)(const char *, const char *, size_t);
+};
+
+static void pathspec_match_context_init(
+ struct pathspec_match_context *ctxt,
+ bool disable_fnmatch,
+ bool casefold)
+{
+ if (disable_fnmatch)
+ ctxt->wildmatch_flags = -1;
+ else if (casefold)
+ ctxt->wildmatch_flags = WM_CASEFOLD;
+ else
+ ctxt->wildmatch_flags = 0;
+
+ if (casefold) {
+ ctxt->strcomp = git__strcasecmp;
+ ctxt->strncomp = git__strncasecmp;
+ } else {
+ ctxt->strcomp = git__strcmp;
+ ctxt->strncomp = git__strncmp;
+ }
+}
+
+static int pathspec_match_one(
+ const git_attr_fnmatch *match,
+ struct pathspec_match_context *ctxt,
+ const char *path)
+{
+ int result = (match->flags & GIT_ATTR_FNMATCH_MATCH_ALL) ? 0 : WM_NOMATCH;
+
+ if (result == WM_NOMATCH)
+ result = ctxt->strcomp(match->pattern, path) ? WM_NOMATCH : 0;
+
+ if (ctxt->wildmatch_flags >= 0 && result == WM_NOMATCH)
+ result = wildmatch(match->pattern, path, ctxt->wildmatch_flags);
+
+ /* if we didn't match, look for exact dirname prefix match */
+ if (result == WM_NOMATCH &&
+ (match->flags & GIT_ATTR_FNMATCH_HASWILD) == 0 &&
+ ctxt->strncomp(path, match->pattern, match->length) == 0 &&
+ path[match->length] == '/')
+ result = 0;
+
+ /* if we didn't match and this is a negative match, check for exact
+ * match of filename with leading '!'
+ */
+ if (result == WM_NOMATCH &&
+ (match->flags & GIT_ATTR_FNMATCH_NEGATIVE) != 0 &&
+ *path == '!' &&
+ ctxt->strncomp(path + 1, match->pattern, match->length) == 0 &&
+ (!path[match->length + 1] || path[match->length + 1] == '/'))
+ return 1;
+
+ if (result == 0)
+ return (match->flags & GIT_ATTR_FNMATCH_NEGATIVE) ? 0 : 1;
+ return -1;
+}
+
+static int git_pathspec__match_at(
+ size_t *matched_at,
+ const git_vector *vspec,
+ struct pathspec_match_context *ctxt,
+ const char *path0,
+ const char *path1)
+{
+ int result = GIT_ENOTFOUND;
+ size_t i = 0;
+ const git_attr_fnmatch *match;
+
+ git_vector_foreach(vspec, i, match) {
+ if (path0 && (result = pathspec_match_one(match, ctxt, path0)) >= 0)
+ break;
+ if (path1 && (result = pathspec_match_one(match, ctxt, path1)) >= 0)
+ break;
+ }
+
+ *matched_at = i;
+ return result;
+}
+
+/* match a path against the vectorized pathspec */
+bool git_pathspec__match(
+ const git_vector *vspec,
+ const char *path,
+ bool disable_fnmatch,
+ bool casefold,
+ const char **matched_pathspec,
+ size_t *matched_at)
+{
+ int result;
+ size_t pos;
+ struct pathspec_match_context ctxt;
+
+ if (matched_pathspec)
+ *matched_pathspec = NULL;
+ if (matched_at)
+ *matched_at = GIT_PATHSPEC_NOMATCH;
+
+ if (!vspec || !vspec->length)
+ return true;
+
+ pathspec_match_context_init(&ctxt, disable_fnmatch, casefold);
+
+ result = git_pathspec__match_at(&pos, vspec, &ctxt, path, NULL);
+ if (result >= 0) {
+ if (matched_pathspec) {
+ const git_attr_fnmatch *match = git_vector_get(vspec, pos);
+ *matched_pathspec = match->pattern;
+ }
+
+ if (matched_at)
+ *matched_at = pos;
+ }
+
+ return (result > 0);
+}
+
+
+int git_pathspec__init(git_pathspec *ps, const git_strarray *paths)
+{
+ int error = 0;
+
+ memset(ps, 0, sizeof(*ps));
+
+ ps->prefix = git_pathspec_prefix(paths);
+
+ if ((error = git_pool_init(&ps->pool, 1)) < 0 ||
+ (error = git_pathspec__vinit(&ps->pathspec, paths, &ps->pool)) < 0)
+ git_pathspec__clear(ps);
+
+ return error;
+}
+
+void git_pathspec__clear(git_pathspec *ps)
+{
+ git__free(ps->prefix);
+ git_pathspec__vfree(&ps->pathspec);
+ git_pool_clear(&ps->pool);
+ memset(ps, 0, sizeof(*ps));
+}
+
+int git_pathspec_new(git_pathspec **out, const git_strarray *pathspec)
+{
+ int error = 0;
+ git_pathspec *ps = git__malloc(sizeof(git_pathspec));
+ GIT_ERROR_CHECK_ALLOC(ps);
+
+ if ((error = git_pathspec__init(ps, pathspec)) < 0) {
+ git__free(ps);
+ return error;
+ }
+
+ GIT_REFCOUNT_INC(ps);
+ *out = ps;
+ return 0;
+}
+
+static void pathspec_free(git_pathspec *ps)
+{
+ git_pathspec__clear(ps);
+ git__free(ps);
+}
+
+void git_pathspec_free(git_pathspec *ps)
+{
+ if (!ps)
+ return;
+ GIT_REFCOUNT_DEC(ps, pathspec_free);
+}
+
+int git_pathspec_matches_path(
+ const git_pathspec *ps, uint32_t flags, const char *path)
+{
+ bool no_fnmatch = (flags & GIT_PATHSPEC_NO_GLOB) != 0;
+ bool casefold = (flags & GIT_PATHSPEC_IGNORE_CASE) != 0;
+
+ GIT_ASSERT_ARG(ps);
+ GIT_ASSERT_ARG(path);
+
+ return (0 != git_pathspec__match(
+ &ps->pathspec, path, no_fnmatch, casefold, NULL, NULL));
+}
+
+static void pathspec_match_free(git_pathspec_match_list *m)
+{
+ if (!m)
+ return;
+
+ git_pathspec_free(m->pathspec);
+ m->pathspec = NULL;
+
+ git_array_clear(m->matches);
+ git_array_clear(m->failures);
+ git_pool_clear(&m->pool);
+ git__free(m);
+}
+
+static git_pathspec_match_list *pathspec_match_alloc(
+ git_pathspec *ps, int datatype)
+{
+ git_pathspec_match_list *m = git__calloc(1, sizeof(git_pathspec_match_list));
+ if (!m)
+ return NULL;
+
+ if (git_pool_init(&m->pool, 1) < 0)
+ return NULL;
+
+ /* need to keep reference to pathspec and increment refcount because
+ * failures array stores pointers to the pattern strings of the
+ * pathspec that had no matches
+ */
+ GIT_REFCOUNT_INC(ps);
+ m->pathspec = ps;
+ m->datatype = datatype;
+
+ return m;
+}
+
+GIT_INLINE(size_t) pathspec_mark_pattern(git_bitvec *used, size_t pos)
+{
+ if (!git_bitvec_get(used, pos)) {
+ git_bitvec_set(used, pos, true);
+ return 1;
+ }
+
+ return 0;
+}
+
+static size_t pathspec_mark_remaining(
+ git_bitvec *used,
+ git_vector *patterns,
+ struct pathspec_match_context *ctxt,
+ size_t start,
+ const char *path0,
+ const char *path1)
+{
+ size_t count = 0;
+
+ if (path1 == path0)
+ path1 = NULL;
+
+ for (; start < patterns->length; ++start) {
+ const git_attr_fnmatch *pat = git_vector_get(patterns, start);
+
+ if (git_bitvec_get(used, start))
+ continue;
+
+ if (path0 && pathspec_match_one(pat, ctxt, path0) > 0)
+ count += pathspec_mark_pattern(used, start);
+ else if (path1 && pathspec_match_one(pat, ctxt, path1) > 0)
+ count += pathspec_mark_pattern(used, start);
+ }
+
+ return count;
+}
+
+static int pathspec_build_failure_array(
+ git_pathspec_string_array_t *failures,
+ git_vector *patterns,
+ git_bitvec *used,
+ git_pool *pool)
+{
+ size_t pos;
+ char **failed;
+ const git_attr_fnmatch *pat;
+
+ for (pos = 0; pos < patterns->length; ++pos) {
+ if (git_bitvec_get(used, pos))
+ continue;
+
+ if ((failed = git_array_alloc(*failures)) == NULL)
+ return -1;
+
+ pat = git_vector_get(patterns, pos);
+
+ if ((*failed = git_pool_strdup(pool, pat->pattern)) == NULL)
+ return -1;
+ }
+
+ return 0;
+}
+
+static int pathspec_match_from_iterator(
+ git_pathspec_match_list **out,
+ git_iterator *iter,
+ uint32_t flags,
+ git_pathspec *ps)
+{
+ int error = 0;
+ git_pathspec_match_list *m = NULL;
+ const git_index_entry *entry = NULL;
+ struct pathspec_match_context ctxt;
+ git_vector *patterns = &ps->pathspec;
+ bool find_failures = out && (flags & GIT_PATHSPEC_FIND_FAILURES) != 0;
+ bool failures_only = !out || (flags & GIT_PATHSPEC_FAILURES_ONLY) != 0;
+ size_t pos, used_ct = 0, found_files = 0;
+ git_index *index = NULL;
+ git_bitvec used_patterns;
+ char **file;
+
+ if (git_bitvec_init(&used_patterns, patterns->length) < 0)
+ return -1;
+
+ if (out) {
+ *out = m = pathspec_match_alloc(ps, PATHSPEC_DATATYPE_STRINGS);
+ GIT_ERROR_CHECK_ALLOC(m);
+ }
+
+ if ((error = git_iterator_reset_range(iter, ps->prefix, ps->prefix)) < 0)
+ goto done;
+
+ if (git_iterator_type(iter) == GIT_ITERATOR_WORKDIR &&
+ (error = git_repository_index__weakptr(
+ &index, git_iterator_owner(iter))) < 0)
+ goto done;
+
+ pathspec_match_context_init(
+ &ctxt, (flags & GIT_PATHSPEC_NO_GLOB) != 0,
+ git_iterator_ignore_case(iter));
+
+ while (!(error = git_iterator_advance(&entry, iter))) {
+ /* search for match with entry->path */
+ int result = git_pathspec__match_at(
+ &pos, patterns, &ctxt, entry->path, NULL);
+
+ /* no matches for this path */
+ if (result < 0)
+ continue;
+
+ /* if result was a negative pattern match, then don't list file */
+ if (!result) {
+ used_ct += pathspec_mark_pattern(&used_patterns, pos);
+ continue;
+ }
+
+ /* check if path is ignored and untracked */
+ if (index != NULL &&
+ git_iterator_current_is_ignored(iter) &&
+ git_index__find_pos(NULL, index, entry->path, 0, GIT_INDEX_STAGE_ANY) < 0)
+ continue;
+
+ /* mark the matched pattern as used */
+ used_ct += pathspec_mark_pattern(&used_patterns, pos);
+ ++found_files;
+
+ /* if find_failures is on, check if any later patterns also match */
+ if (find_failures && used_ct < patterns->length)
+ used_ct += pathspec_mark_remaining(
+ &used_patterns, patterns, &ctxt, pos + 1, entry->path, NULL);
+
+ /* if only looking at failures, exit early or just continue */
+ if (failures_only || !out) {
+ if (used_ct == patterns->length)
+ break;
+ continue;
+ }
+
+ /* insert matched path into matches array */
+ if ((file = (char **)git_array_alloc(m->matches)) == NULL ||
+ (*file = git_pool_strdup(&m->pool, entry->path)) == NULL) {
+ error = -1;
+ goto done;
+ }
+ }
+
+ if (error < 0 && error != GIT_ITEROVER)
+ goto done;
+ error = 0;
+
+ /* insert patterns that had no matches into failures array */
+ if (find_failures && used_ct < patterns->length &&
+ (error = pathspec_build_failure_array(
+ &m->failures, patterns, &used_patterns, &m->pool)) < 0)
+ goto done;
+
+ /* if every pattern failed to match, then we have failed */
+ if ((flags & GIT_PATHSPEC_NO_MATCH_ERROR) != 0 && !found_files) {
+ git_error_set(GIT_ERROR_INVALID, "no matching files were found");
+ error = GIT_ENOTFOUND;
+ }
+
+done:
+ git_bitvec_free(&used_patterns);
+
+ if (error < 0) {
+ pathspec_match_free(m);
+ if (out) *out = NULL;
+ }
+
+ return error;
+}
+
+static git_iterator_flag_t pathspec_match_iter_flags(uint32_t flags)
+{
+ git_iterator_flag_t f = 0;
+
+ if ((flags & GIT_PATHSPEC_IGNORE_CASE) != 0)
+ f |= GIT_ITERATOR_IGNORE_CASE;
+ else if ((flags & GIT_PATHSPEC_USE_CASE) != 0)
+ f |= GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ return f;
+}
+
+int git_pathspec_match_workdir(
+ git_pathspec_match_list **out,
+ git_repository *repo,
+ uint32_t flags,
+ git_pathspec *ps)
+{
+ git_iterator *iter;
+ git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
+ int error = 0;
+
+ GIT_ASSERT_ARG(repo);
+
+ iter_opts.flags = pathspec_match_iter_flags(flags);
+
+ if (!(error = git_iterator_for_workdir(&iter, repo, NULL, NULL, &iter_opts))) {
+ error = pathspec_match_from_iterator(out, iter, flags, ps);
+ git_iterator_free(iter);
+ }
+
+ return error;
+}
+
+int git_pathspec_match_index(
+ git_pathspec_match_list **out,
+ git_index *index,
+ uint32_t flags,
+ git_pathspec *ps)
+{
+ git_iterator *iter;
+ git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
+ int error = 0;
+
+ GIT_ASSERT_ARG(index);
+
+ iter_opts.flags = pathspec_match_iter_flags(flags);
+
+ if (!(error = git_iterator_for_index(&iter, git_index_owner(index), index, &iter_opts))) {
+ error = pathspec_match_from_iterator(out, iter, flags, ps);
+ git_iterator_free(iter);
+ }
+
+ return error;
+}
+
+int git_pathspec_match_tree(
+ git_pathspec_match_list **out,
+ git_tree *tree,
+ uint32_t flags,
+ git_pathspec *ps)
+{
+ git_iterator *iter;
+ git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
+ int error = 0;
+
+ GIT_ASSERT_ARG(tree);
+
+ iter_opts.flags = pathspec_match_iter_flags(flags);
+
+ if (!(error = git_iterator_for_tree(&iter, tree, &iter_opts))) {
+ error = pathspec_match_from_iterator(out, iter, flags, ps);
+ git_iterator_free(iter);
+ }
+
+ return error;
+}
+
+int git_pathspec_match_diff(
+ git_pathspec_match_list **out,
+ git_diff *diff,
+ uint32_t flags,
+ git_pathspec *ps)
+{
+ int error = 0;
+ git_pathspec_match_list *m = NULL;
+ struct pathspec_match_context ctxt;
+ git_vector *patterns = &ps->pathspec;
+ bool find_failures = out && (flags & GIT_PATHSPEC_FIND_FAILURES) != 0;
+ bool failures_only = !out || (flags & GIT_PATHSPEC_FAILURES_ONLY) != 0;
+ size_t i, pos, used_ct = 0, found_deltas = 0;
+ const git_diff_delta *delta, **match;
+ git_bitvec used_patterns;
+
+ GIT_ASSERT_ARG(diff);
+
+ if (git_bitvec_init(&used_patterns, patterns->length) < 0)
+ return -1;
+
+ if (out) {
+ *out = m = pathspec_match_alloc(ps, PATHSPEC_DATATYPE_DIFF);
+ GIT_ERROR_CHECK_ALLOC(m);
+ }
+
+ pathspec_match_context_init(
+ &ctxt, (flags & GIT_PATHSPEC_NO_GLOB) != 0,
+ git_diff_is_sorted_icase(diff));
+
+ git_vector_foreach(&diff->deltas, i, delta) {
+ /* search for match with delta */
+ int result = git_pathspec__match_at(
+ &pos, patterns, &ctxt, delta->old_file.path, delta->new_file.path);
+
+ /* no matches for this path */
+ if (result < 0)
+ continue;
+
+ /* mark the matched pattern as used */
+ used_ct += pathspec_mark_pattern(&used_patterns, pos);
+
+ /* if result was a negative pattern match, then don't list file */
+ if (!result)
+ continue;
+
+ ++found_deltas;
+
+ /* if find_failures is on, check if any later patterns also match */
+ if (find_failures && used_ct < patterns->length)
+ used_ct += pathspec_mark_remaining(
+ &used_patterns, patterns, &ctxt, pos + 1,
+ delta->old_file.path, delta->new_file.path);
+
+ /* if only looking at failures, exit early or just continue */
+ if (failures_only || !out) {
+ if (used_ct == patterns->length)
+ break;
+ continue;
+ }
+
+ /* insert matched delta into matches array */
+ if (!(match = (const git_diff_delta **)git_array_alloc(m->matches))) {
+ error = -1;
+ goto done;
+ } else {
+ *match = delta;
+ }
+ }
+
+ /* insert patterns that had no matches into failures array */
+ if (find_failures && used_ct < patterns->length &&
+ (error = pathspec_build_failure_array(
+ &m->failures, patterns, &used_patterns, &m->pool)) < 0)
+ goto done;
+
+ /* if every pattern failed to match, then we have failed */
+ if ((flags & GIT_PATHSPEC_NO_MATCH_ERROR) != 0 && !found_deltas) {
+ git_error_set(GIT_ERROR_INVALID, "no matching deltas were found");
+ error = GIT_ENOTFOUND;
+ }
+
+done:
+ git_bitvec_free(&used_patterns);
+
+ if (error < 0) {
+ pathspec_match_free(m);
+ if (out) *out = NULL;
+ }
+
+ return error;
+}
+
+void git_pathspec_match_list_free(git_pathspec_match_list *m)
+{
+ if (m)
+ pathspec_match_free(m);
+}
+
+size_t git_pathspec_match_list_entrycount(
+ const git_pathspec_match_list *m)
+{
+ return m ? git_array_size(m->matches) : 0;
+}
+
+const char *git_pathspec_match_list_entry(
+ const git_pathspec_match_list *m, size_t pos)
+{
+ if (!m || m->datatype != PATHSPEC_DATATYPE_STRINGS ||
+ !git_array_valid_index(m->matches, pos))
+ return NULL;
+
+ return *((const char **)git_array_get(m->matches, pos));
+}
+
+const git_diff_delta *git_pathspec_match_list_diff_entry(
+ const git_pathspec_match_list *m, size_t pos)
+{
+ if (!m || m->datatype != PATHSPEC_DATATYPE_DIFF ||
+ !git_array_valid_index(m->matches, pos))
+ return NULL;
+
+ return *((const git_diff_delta **)git_array_get(m->matches, pos));
+}
+
+size_t git_pathspec_match_list_failed_entrycount(
+ const git_pathspec_match_list *m)
+{
+ return m ? git_array_size(m->failures) : 0;
+}
+
+const char * git_pathspec_match_list_failed_entry(
+ const git_pathspec_match_list *m, size_t pos)
+{
+ char **entry = m ? git_array_get(m->failures, pos) : NULL;
+
+ return entry ? *entry : NULL;
+}
diff --git a/src/libgit2/pathspec.h b/src/libgit2/pathspec.h
new file mode 100644
index 0000000..0256cb9
--- /dev/null
+++ b/src/libgit2/pathspec.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_pathspec_h__
+#define INCLUDE_pathspec_h__
+
+#include "common.h"
+
+#include "git2/pathspec.h"
+#include "str.h"
+#include "vector.h"
+#include "pool.h"
+#include "array.h"
+
+/* public compiled pathspec */
+struct git_pathspec {
+ git_refcount rc;
+ char *prefix;
+ git_vector pathspec;
+ git_pool pool;
+};
+
+enum {
+ PATHSPEC_DATATYPE_STRINGS = 0,
+ PATHSPEC_DATATYPE_DIFF = 1
+};
+
+typedef git_array_t(char *) git_pathspec_string_array_t;
+
+/* public interface to pathspec matching */
+struct git_pathspec_match_list {
+ git_pathspec *pathspec;
+ git_array_t(void *) matches;
+ git_pathspec_string_array_t failures;
+ git_pool pool;
+ int datatype;
+};
+
+/* what is the common non-wildcard prefix for all items in the pathspec */
+extern char *git_pathspec_prefix(const git_strarray *pathspec);
+
+/* is there anything in the spec that needs to be filtered on */
+extern bool git_pathspec_is_empty(const git_strarray *pathspec);
+
+/* build a vector of fnmatch patterns to evaluate efficiently */
+extern int git_pathspec__vinit(
+ git_vector *vspec, const git_strarray *strspec, git_pool *strpool);
+
+/* free data from the pathspec vector */
+extern void git_pathspec__vfree(git_vector *vspec);
+
+#define GIT_PATHSPEC_NOMATCH ((size_t)-1)
+
+/*
+ * Match a path against the vectorized pathspec.
+ * The matched pathspec is passed back into the `matched_pathspec` parameter,
+ * unless it is passed as NULL by the caller.
+ */
+extern bool git_pathspec__match(
+ const git_vector *vspec,
+ const char *path,
+ bool disable_fnmatch,
+ bool casefold,
+ const char **matched_pathspec,
+ size_t *matched_at);
+
+/* easy pathspec setup */
+
+extern int git_pathspec__init(git_pathspec *ps, const git_strarray *paths);
+
+extern void git_pathspec__clear(git_pathspec *ps);
+
+#endif
diff --git a/src/libgit2/proxy.c b/src/libgit2/proxy.c
new file mode 100644
index 0000000..ef91ad6
--- /dev/null
+++ b/src/libgit2/proxy.c
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "proxy.h"
+
+#include "git2/proxy.h"
+
+int git_proxy_options_init(git_proxy_options *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_proxy_options, GIT_PROXY_OPTIONS_INIT);
+ return 0;
+}
+
+#ifndef GIT_DEPRECATE_HARD
+int git_proxy_init_options(git_proxy_options *opts, unsigned int version)
+{
+ return git_proxy_options_init(opts, version);
+}
+#endif
+
+int git_proxy_options_dup(git_proxy_options *tgt, const git_proxy_options *src)
+{
+ if (!src) {
+ git_proxy_options_init(tgt, GIT_PROXY_OPTIONS_VERSION);
+ return 0;
+ }
+
+ memcpy(tgt, src, sizeof(git_proxy_options));
+ if (src->url) {
+ tgt->url = git__strdup(src->url);
+ GIT_ERROR_CHECK_ALLOC(tgt->url);
+ }
+
+ return 0;
+}
+
+void git_proxy_options_dispose(git_proxy_options *opts)
+{
+ if (!opts)
+ return;
+
+ git__free((char *) opts->url);
+ opts->url = NULL;
+}
diff --git a/src/libgit2/proxy.h b/src/libgit2/proxy.h
new file mode 100644
index 0000000..7c0ab59
--- /dev/null
+++ b/src/libgit2/proxy.h
@@ -0,0 +1,17 @@
+/*
+* Copyright (C) the libgit2 contributors. All rights reserved.
+*
+* This file is part of libgit2, distributed under the GNU GPL v2 with
+* a Linking Exception. For full terms see the included COPYING file.
+*/
+#ifndef INCLUDE_proxy_h__
+#define INCLUDE_proxy_h__
+
+#include "common.h"
+
+#include "git2/proxy.h"
+
+extern int git_proxy_options_dup(git_proxy_options *tgt, const git_proxy_options *src);
+extern void git_proxy_options_dispose(git_proxy_options *opts);
+
+#endif
diff --git a/src/libgit2/push.c b/src/libgit2/push.c
new file mode 100644
index 0000000..8b47abc
--- /dev/null
+++ b/src/libgit2/push.c
@@ -0,0 +1,568 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "push.h"
+
+#include "git2.h"
+
+#include "pack.h"
+#include "pack-objects.h"
+#include "remote.h"
+#include "vector.h"
+#include "tree.h"
+
+static int push_spec_rref_cmp(const void *a, const void *b)
+{
+ const push_spec *push_spec_a = a, *push_spec_b = b;
+
+ return strcmp(push_spec_a->refspec.dst, push_spec_b->refspec.dst);
+}
+
+static int push_status_ref_cmp(const void *a, const void *b)
+{
+ const push_status *push_status_a = a, *push_status_b = b;
+
+ return strcmp(push_status_a->ref, push_status_b->ref);
+}
+
+int git_push_new(git_push **out, git_remote *remote, const git_push_options *opts)
+{
+ git_push *p;
+
+ *out = NULL;
+
+ GIT_ERROR_CHECK_VERSION(opts, GIT_PUSH_OPTIONS_VERSION, "git_push_options");
+
+ p = git__calloc(1, sizeof(*p));
+ GIT_ERROR_CHECK_ALLOC(p);
+
+ p->repo = remote->repo;
+ p->remote = remote;
+ p->report_status = 1;
+ p->pb_parallelism = opts ? opts->pb_parallelism : 1;
+
+ if (opts) {
+ GIT_ERROR_CHECK_VERSION(&opts->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
+ memcpy(&p->callbacks, &opts->callbacks, sizeof(git_remote_callbacks));
+ }
+
+ if (git_vector_init(&p->specs, 0, push_spec_rref_cmp) < 0) {
+ git__free(p);
+ return -1;
+ }
+
+ if (git_vector_init(&p->status, 0, push_status_ref_cmp) < 0) {
+ git_vector_free(&p->specs);
+ git__free(p);
+ return -1;
+ }
+
+ if (git_vector_init(&p->updates, 0, NULL) < 0) {
+ git_vector_free(&p->status);
+ git_vector_free(&p->specs);
+ git__free(p);
+ return -1;
+ }
+
+ *out = p;
+ return 0;
+}
+
+static void free_refspec(push_spec *spec)
+{
+ if (spec == NULL)
+ return;
+
+ git_refspec__dispose(&spec->refspec);
+ git__free(spec);
+}
+
+static int check_rref(char *ref)
+{
+ if (git__prefixcmp(ref, "refs/")) {
+ git_error_set(GIT_ERROR_INVALID, "not a valid reference '%s'", ref);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int check_lref(git_push *push, char *ref)
+{
+ /* lref must be resolvable to an existing object */
+ git_object *obj;
+ int error = git_revparse_single(&obj, push->repo, ref);
+ git_object_free(obj);
+
+ if (!error)
+ return 0;
+
+ if (error == GIT_ENOTFOUND)
+ git_error_set(GIT_ERROR_REFERENCE,
+ "src refspec '%s' does not match any existing object", ref);
+ else
+ git_error_set(GIT_ERROR_INVALID, "not a valid reference '%s'", ref);
+ return -1;
+}
+
+static int parse_refspec(git_push *push, push_spec **spec, const char *str)
+{
+ push_spec *s;
+
+ *spec = NULL;
+
+ s = git__calloc(1, sizeof(*s));
+ GIT_ERROR_CHECK_ALLOC(s);
+
+ git_oid_clear(&s->loid, push->repo->oid_type);
+ git_oid_clear(&s->roid, push->repo->oid_type);
+
+ if (git_refspec__parse(&s->refspec, str, false) < 0) {
+ git_error_set(GIT_ERROR_INVALID, "invalid refspec %s", str);
+ goto on_error;
+ }
+
+ if (s->refspec.src && s->refspec.src[0] != '\0' &&
+ check_lref(push, s->refspec.src) < 0) {
+ goto on_error;
+ }
+
+ if (check_rref(s->refspec.dst) < 0)
+ goto on_error;
+
+ *spec = s;
+ return 0;
+
+on_error:
+ free_refspec(s);
+ return -1;
+}
+
+int git_push_add_refspec(git_push *push, const char *refspec)
+{
+ push_spec *spec;
+
+ if (parse_refspec(push, &spec, refspec) < 0 ||
+ git_vector_insert(&push->specs, spec) < 0)
+ return -1;
+
+ return 0;
+}
+
+int git_push_update_tips(git_push *push, const git_remote_callbacks *callbacks)
+{
+ git_str remote_ref_name = GIT_STR_INIT;
+ size_t i, j;
+ git_refspec *fetch_spec;
+ push_spec *push_spec = NULL;
+ git_reference *remote_ref;
+ push_status *status;
+ int error = 0;
+
+ git_vector_foreach(&push->status, i, status) {
+ int fire_callback = 1;
+
+ /* Skip unsuccessful updates which have non-empty messages */
+ if (status->msg)
+ continue;
+
+ /* Find the corresponding remote ref */
+ fetch_spec = git_remote__matching_refspec(push->remote, status->ref);
+ if (!fetch_spec)
+ continue;
+
+ /* Clear the buffer which can be dirty from previous iteration */
+ git_str_clear(&remote_ref_name);
+
+ if ((error = git_refspec__transform(&remote_ref_name, fetch_spec, status->ref)) < 0)
+ goto on_error;
+
+ /* Find matching push ref spec */
+ git_vector_foreach(&push->specs, j, push_spec) {
+ if (!strcmp(push_spec->refspec.dst, status->ref))
+ break;
+ }
+
+ /* Could not find the corresponding push ref spec for this push update */
+ if (j == push->specs.length)
+ continue;
+
+ /* Update the remote ref */
+ if (git_oid_is_zero(&push_spec->loid)) {
+ error = git_reference_lookup(&remote_ref, push->remote->repo, git_str_cstr(&remote_ref_name));
+
+ if (error >= 0) {
+ error = git_reference_delete(remote_ref);
+ git_reference_free(remote_ref);
+ }
+ } else {
+ error = git_reference_create(NULL, push->remote->repo,
+ git_str_cstr(&remote_ref_name), &push_spec->loid, 1,
+ "update by push");
+ }
+
+ if (error < 0) {
+ if (error != GIT_ENOTFOUND)
+ goto on_error;
+
+ git_error_clear();
+ fire_callback = 0;
+ }
+
+ if (fire_callback && callbacks && callbacks->update_tips) {
+ error = callbacks->update_tips(git_str_cstr(&remote_ref_name),
+ &push_spec->roid, &push_spec->loid, callbacks->payload);
+
+ if (error < 0)
+ goto on_error;
+ }
+ }
+
+ error = 0;
+
+on_error:
+ git_str_dispose(&remote_ref_name);
+ return error;
+}
+
+/**
+ * Insert all tags until we find a non-tag object, which is returned
+ * in `out`.
+ */
+static int enqueue_tag(git_object **out, git_push *push, git_oid *id)
+{
+ git_object *obj = NULL, *target = NULL;
+ int error;
+
+ if ((error = git_object_lookup(&obj, push->repo, id, GIT_OBJECT_TAG)) < 0)
+ return error;
+
+ while (git_object_type(obj) == GIT_OBJECT_TAG) {
+ if ((error = git_packbuilder_insert(push->pb, git_object_id(obj), NULL)) < 0)
+ break;
+
+ if ((error = git_tag_target(&target, (git_tag *) obj)) < 0)
+ break;
+
+ git_object_free(obj);
+ obj = target;
+ }
+
+ if (error < 0)
+ git_object_free(obj);
+ else
+ *out = obj;
+
+ return error;
+}
+
+static int queue_objects(git_push *push)
+{
+ git_remote_head *head;
+ push_spec *spec;
+ git_revwalk *rw;
+ unsigned int i;
+ int error = -1;
+
+ if (git_revwalk_new(&rw, push->repo) < 0)
+ return -1;
+
+ git_revwalk_sorting(rw, GIT_SORT_TIME);
+
+ git_vector_foreach(&push->specs, i, spec) {
+ git_object_t type;
+ size_t size;
+
+ if (git_oid_is_zero(&spec->loid))
+ /*
+ * Delete reference on remote side;
+ * nothing to do here.
+ */
+ continue;
+
+ if (git_oid_equal(&spec->loid, &spec->roid))
+ continue; /* up-to-date */
+
+ if ((error = git_odb_read_header(&size, &type, push->repo->_odb, &spec->loid)) < 0)
+ goto on_error;
+
+ if (type == GIT_OBJECT_TAG) {
+ git_object *target;
+
+ if ((error = enqueue_tag(&target, push, &spec->loid)) < 0)
+ goto on_error;
+
+ if (git_object_type(target) == GIT_OBJECT_COMMIT) {
+ if ((error = git_revwalk_push(rw, git_object_id(target))) < 0) {
+ git_object_free(target);
+ goto on_error;
+ }
+ } else {
+ if ((error = git_packbuilder_insert(
+ push->pb, git_object_id(target), NULL)) < 0) {
+ git_object_free(target);
+ goto on_error;
+ }
+ }
+ git_object_free(target);
+ } else if ((error = git_revwalk_push(rw, &spec->loid)) < 0)
+ goto on_error;
+
+ if (!spec->refspec.force) {
+ git_oid base;
+
+ if (git_oid_is_zero(&spec->roid))
+ continue;
+
+ if (!git_odb_exists(push->repo->_odb, &spec->roid)) {
+ git_error_set(GIT_ERROR_REFERENCE,
+ "cannot push because a reference that you are trying to update on the remote contains commits that are not present locally.");
+ error = GIT_ENONFASTFORWARD;
+ goto on_error;
+ }
+
+ error = git_merge_base(&base, push->repo,
+ &spec->loid, &spec->roid);
+
+ if (error == GIT_ENOTFOUND ||
+ (!error && !git_oid_equal(&base, &spec->roid))) {
+ git_error_set(GIT_ERROR_REFERENCE,
+ "cannot push non-fastforwardable reference");
+ error = GIT_ENONFASTFORWARD;
+ goto on_error;
+ }
+
+ if (error < 0)
+ goto on_error;
+ }
+ }
+
+ git_vector_foreach(&push->remote->refs, i, head) {
+ if (git_oid_is_zero(&head->oid))
+ continue;
+
+ if ((error = git_revwalk_hide(rw, &head->oid)) < 0 &&
+ error != GIT_ENOTFOUND && error != GIT_EINVALIDSPEC && error != GIT_EPEEL)
+ goto on_error;
+ }
+
+ error = git_packbuilder_insert_walk(push->pb, rw);
+
+on_error:
+ git_revwalk_free(rw);
+ return error;
+}
+
+static int add_update(git_push *push, push_spec *spec)
+{
+ git_push_update *u = git__calloc(1, sizeof(git_push_update));
+ GIT_ERROR_CHECK_ALLOC(u);
+
+ u->src_refname = git__strdup(spec->refspec.src);
+ GIT_ERROR_CHECK_ALLOC(u->src_refname);
+
+ u->dst_refname = git__strdup(spec->refspec.dst);
+ GIT_ERROR_CHECK_ALLOC(u->dst_refname);
+
+ git_oid_cpy(&u->src, &spec->roid);
+ git_oid_cpy(&u->dst, &spec->loid);
+
+ return git_vector_insert(&push->updates, u);
+}
+
+static int calculate_work(git_push *push)
+{
+ git_remote_head *head;
+ push_spec *spec;
+ unsigned int i, j;
+
+ /* Update local and remote oids*/
+
+ git_vector_foreach(&push->specs, i, spec) {
+ if (spec->refspec.src && spec->refspec.src[0]!= '\0') {
+ /* This is a create or update. Local ref must exist. */
+
+ git_object *obj;
+ int error = git_revparse_single(&obj, push->repo, spec->refspec.src);
+
+ if (error < 0) {
+ git_object_free(obj);
+ git_error_set(GIT_ERROR_REFERENCE, "src refspec %s does not match any", spec->refspec.src);
+ return -1;
+ }
+
+ git_oid_cpy(&spec->loid, git_object_id(obj));
+ git_object_free(obj);
+ }
+
+ /* Remote ref may or may not (e.g. during create) already exist. */
+ git_vector_foreach(&push->remote->refs, j, head) {
+ if (!strcmp(spec->refspec.dst, head->name)) {
+ git_oid_cpy(&spec->roid, &head->oid);
+ break;
+ }
+ }
+
+ if (add_update(push, spec) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+static int do_push(git_push *push)
+{
+ int error = 0;
+ git_transport *transport = push->remote->transport;
+ git_remote_callbacks *callbacks = &push->callbacks;
+
+ if (!transport->push) {
+ git_error_set(GIT_ERROR_NET, "remote transport doesn't support push");
+ error = -1;
+ goto on_error;
+ }
+
+ /*
+ * A pack-file MUST be sent if either create or update command
+ * is used, even if the server already has all the necessary
+ * objects. In this case the client MUST send an empty pack-file.
+ */
+
+ if ((error = git_packbuilder_new(&push->pb, push->repo)) < 0)
+ goto on_error;
+
+ git_packbuilder_set_threads(push->pb, push->pb_parallelism);
+
+ if (callbacks && callbacks->pack_progress)
+ if ((error = git_packbuilder_set_callbacks(push->pb, callbacks->pack_progress, callbacks->payload)) < 0)
+ goto on_error;
+
+ if ((error = calculate_work(push)) < 0)
+ goto on_error;
+
+ if (callbacks && callbacks->push_negotiation &&
+ (error = callbacks->push_negotiation((const git_push_update **) push->updates.contents,
+ push->updates.length, callbacks->payload)) < 0)
+ goto on_error;
+
+ if ((error = queue_objects(push)) < 0 ||
+ (error = transport->push(transport, push)) < 0)
+ goto on_error;
+
+on_error:
+ git_packbuilder_free(push->pb);
+ return error;
+}
+
+static int filter_refs(git_remote *remote)
+{
+ const git_remote_head **heads;
+ size_t heads_len, i;
+
+ git_vector_clear(&remote->refs);
+
+ if (git_remote_ls(&heads, &heads_len, remote) < 0)
+ return -1;
+
+ for (i = 0; i < heads_len; i++) {
+ if (git_vector_insert(&remote->refs, (void *)heads[i]) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+int git_push_finish(git_push *push)
+{
+ int error;
+
+ if (!git_remote_connected(push->remote)) {
+ git_error_set(GIT_ERROR_NET, "remote is disconnected");
+ return -1;
+ }
+
+ if ((error = filter_refs(push->remote)) < 0 ||
+ (error = do_push(push)) < 0)
+ return error;
+
+ if (!push->unpack_ok) {
+ error = -1;
+ git_error_set(GIT_ERROR_NET, "unpacking the sent packfile failed on the remote");
+ }
+
+ return error;
+}
+
+int git_push_status_foreach(git_push *push,
+ int (*cb)(const char *ref, const char *msg, void *data),
+ void *data)
+{
+ push_status *status;
+ unsigned int i;
+
+ git_vector_foreach(&push->status, i, status) {
+ int error = cb(status->ref, status->msg, data);
+ if (error)
+ return git_error_set_after_callback(error);
+ }
+
+ return 0;
+}
+
+void git_push_status_free(push_status *status)
+{
+ if (status == NULL)
+ return;
+
+ git__free(status->msg);
+ git__free(status->ref);
+ git__free(status);
+}
+
+void git_push_free(git_push *push)
+{
+ push_spec *spec;
+ push_status *status;
+ git_push_update *update;
+ unsigned int i;
+
+ if (push == NULL)
+ return;
+
+ git_vector_foreach(&push->specs, i, spec) {
+ free_refspec(spec);
+ }
+ git_vector_free(&push->specs);
+
+ git_vector_foreach(&push->status, i, status) {
+ git_push_status_free(status);
+ }
+ git_vector_free(&push->status);
+
+ git_vector_foreach(&push->updates, i, update) {
+ git__free(update->src_refname);
+ git__free(update->dst_refname);
+ git__free(update);
+ }
+ git_vector_free(&push->updates);
+
+ git__free(push);
+}
+
+int git_push_options_init(git_push_options *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_push_options, GIT_PUSH_OPTIONS_INIT);
+ return 0;
+}
+
+#ifndef GIT_DEPRECATE_HARD
+int git_push_init_options(git_push_options *opts, unsigned int version)
+{
+ return git_push_options_init(opts, version);
+}
+#endif
diff --git a/src/libgit2/push.h b/src/libgit2/push.h
new file mode 100644
index 0000000..fc72e84
--- /dev/null
+++ b/src/libgit2/push.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_push_h__
+#define INCLUDE_push_h__
+
+#include "common.h"
+
+#include "git2.h"
+#include "refspec.h"
+#include "remote.h"
+
+typedef struct push_spec {
+ struct git_refspec refspec;
+
+ git_oid loid;
+ git_oid roid;
+} push_spec;
+
+typedef struct push_status {
+ bool ok;
+
+ char *ref;
+ char *msg;
+} push_status;
+
+struct git_push {
+ git_repository *repo;
+ git_packbuilder *pb;
+ git_remote *remote;
+ git_vector specs;
+ git_vector updates;
+ bool report_status;
+
+ /* report-status */
+ bool unpack_ok;
+ git_vector status;
+
+ /* options */
+ unsigned pb_parallelism;
+ git_remote_callbacks callbacks;
+};
+
+/**
+ * Free the given push status object
+ *
+ * @param status The push status object
+ */
+void git_push_status_free(push_status *status);
+
+/**
+ * Create a new push object
+ *
+ * @param out New push object
+ * @param remote Remote instance
+ * @param opts Push options or NULL
+ *
+ * @return 0 or an error code
+ */
+int git_push_new(git_push **out, git_remote *remote, const git_push_options *opts);
+
+/**
+ * Add a refspec to be pushed
+ *
+ * @param push The push object
+ * @param refspec Refspec string
+ *
+ * @return 0 or an error code
+ */
+int git_push_add_refspec(git_push *push, const char *refspec);
+
+/**
+ * Update remote tips after a push
+ *
+ * @param push The push object
+ * @param callbacks the callbacks to use for this connection
+ *
+ * @return 0 or an error code
+ */
+int git_push_update_tips(git_push *push, const git_remote_callbacks *callbacks);
+
+/**
+ * Perform the push
+ *
+ * This function will return an error in case of a protocol error or
+ * the server being unable to unpack the data we sent.
+ *
+ * The return value does not reflect whether the server accepted or
+ * refused any reference updates. Use `git_push_status_foreach()` in
+ * order to find out which updates were accepted or rejected.
+ *
+ * @param push The push object
+ *
+ * @return 0 or an error code
+ */
+int git_push_finish(git_push *push);
+
+/**
+ * Invoke callback `cb' on each status entry
+ *
+ * For each of the updated references, we receive a status report in the
+ * form of `ok refs/heads/master` or `ng refs/heads/master <msg>`.
+ * `msg != NULL` means the reference has not been updated for the given
+ * reason.
+ *
+ * Return a non-zero value from the callback to stop the loop.
+ *
+ * @param push The push object
+ * @param cb The callback to call on each object
+ * @param data The payload passed to the callback
+ *
+ * @return 0 on success, non-zero callback return value, or error code
+ */
+int git_push_status_foreach(git_push *push,
+ int (*cb)(const char *ref, const char *msg, void *data),
+ void *data);
+
+/**
+ * Free the given push object
+ *
+ * @param push The push object
+ */
+void git_push_free(git_push *push);
+
+#endif
diff --git a/src/libgit2/reader.c b/src/libgit2/reader.c
new file mode 100644
index 0000000..df2b280
--- /dev/null
+++ b/src/libgit2/reader.c
@@ -0,0 +1,269 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "reader.h"
+
+#include "futils.h"
+#include "blob.h"
+
+#include "git2/tree.h"
+#include "git2/blob.h"
+#include "git2/index.h"
+#include "git2/repository.h"
+
+/* tree reader */
+
+typedef struct {
+ git_reader reader;
+ git_tree *tree;
+} tree_reader;
+
+static int tree_reader_read(
+ git_str *out,
+ git_oid *out_id,
+ git_filemode_t *out_filemode,
+ git_reader *_reader,
+ const char *filename)
+{
+ tree_reader *reader = (tree_reader *)_reader;
+ git_tree_entry *tree_entry = NULL;
+ git_blob *blob = NULL;
+ git_object_size_t blobsize;
+ int error;
+
+ if ((error = git_tree_entry_bypath(&tree_entry, reader->tree, filename)) < 0 ||
+ (error = git_blob_lookup(&blob, git_tree_owner(reader->tree), git_tree_entry_id(tree_entry))) < 0)
+ goto done;
+
+ blobsize = git_blob_rawsize(blob);
+ GIT_ERROR_CHECK_BLOBSIZE(blobsize);
+
+ if ((error = git_str_set(out, git_blob_rawcontent(blob), (size_t)blobsize)) < 0)
+ goto done;
+
+ if (out_id)
+ git_oid_cpy(out_id, git_tree_entry_id(tree_entry));
+
+ if (out_filemode)
+ *out_filemode = git_tree_entry_filemode(tree_entry);
+
+done:
+ git_blob_free(blob);
+ git_tree_entry_free(tree_entry);
+ return error;
+}
+
+int git_reader_for_tree(git_reader **out, git_tree *tree)
+{
+ tree_reader *reader;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(tree);
+
+ reader = git__calloc(1, sizeof(tree_reader));
+ GIT_ERROR_CHECK_ALLOC(reader);
+
+ reader->reader.read = tree_reader_read;
+ reader->tree = tree;
+
+ *out = (git_reader *)reader;
+ return 0;
+}
+
+/* workdir reader */
+
+typedef struct {
+ git_reader reader;
+ git_repository *repo;
+ git_index *index;
+} workdir_reader;
+
+static int workdir_reader_read(
+ git_str *out,
+ git_oid *out_id,
+ git_filemode_t *out_filemode,
+ git_reader *_reader,
+ const char *filename)
+{
+ workdir_reader *reader = (workdir_reader *)_reader;
+ git_str path = GIT_STR_INIT;
+ struct stat st;
+ git_filemode_t filemode;
+ git_filter_list *filters = NULL;
+ const git_index_entry *idx_entry;
+ git_oid id;
+ int error;
+
+ if ((error = git_repository_workdir_path(&path, reader->repo, filename)) < 0)
+ goto done;
+
+ if ((error = p_lstat(path.ptr, &st)) < 0) {
+ if (error == -1 && errno == ENOENT)
+ error = GIT_ENOTFOUND;
+
+ git_error_set(GIT_ERROR_OS, "could not stat '%s'", path.ptr);
+ goto done;
+ }
+
+ filemode = git_futils_canonical_mode(st.st_mode);
+
+ /*
+ * Patch application - for example - uses the filtered version of
+ * the working directory data to match git. So we will run the
+ * workdir -> ODB filter on the contents in this workdir reader.
+ */
+ if ((error = git_filter_list_load(&filters, reader->repo, NULL, filename,
+ GIT_FILTER_TO_ODB, GIT_FILTER_DEFAULT)) < 0)
+ goto done;
+
+ if ((error = git_filter_list__apply_to_file(out,
+ filters, reader->repo, path.ptr)) < 0)
+ goto done;
+
+ if (out_id || reader->index) {
+ if ((error = git_odb__hash(&id, out->ptr, out->size, GIT_OBJECT_BLOB, reader->repo->oid_type)) < 0)
+ goto done;
+ }
+
+ if (reader->index) {
+ if (!(idx_entry = git_index_get_bypath(reader->index, filename, 0)) ||
+ filemode != idx_entry->mode ||
+ !git_oid_equal(&id, &idx_entry->id)) {
+ error = GIT_READER_MISMATCH;
+ goto done;
+ }
+ }
+
+ if (out_id)
+ git_oid_cpy(out_id, &id);
+
+ if (out_filemode)
+ *out_filemode = filemode;
+
+done:
+ git_filter_list_free(filters);
+ git_str_dispose(&path);
+ return error;
+}
+
+int git_reader_for_workdir(
+ git_reader **out,
+ git_repository *repo,
+ bool validate_index)
+{
+ workdir_reader *reader;
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+
+ reader = git__calloc(1, sizeof(workdir_reader));
+ GIT_ERROR_CHECK_ALLOC(reader);
+
+ reader->reader.read = workdir_reader_read;
+ reader->repo = repo;
+
+ if (validate_index &&
+ (error = git_repository_index__weakptr(&reader->index, repo)) < 0) {
+ git__free(reader);
+ return error;
+ }
+
+ *out = (git_reader *)reader;
+ return 0;
+}
+
+/* index reader */
+
+typedef struct {
+ git_reader reader;
+ git_repository *repo;
+ git_index *index;
+} index_reader;
+
+static int index_reader_read(
+ git_str *out,
+ git_oid *out_id,
+ git_filemode_t *out_filemode,
+ git_reader *_reader,
+ const char *filename)
+{
+ index_reader *reader = (index_reader *)_reader;
+ const git_index_entry *entry;
+ git_blob *blob;
+ int error;
+
+ if ((entry = git_index_get_bypath(reader->index, filename, 0)) == NULL)
+ return GIT_ENOTFOUND;
+
+ if ((error = git_blob_lookup(&blob, reader->repo, &entry->id)) < 0)
+ goto done;
+
+ if (out_id)
+ git_oid_cpy(out_id, &entry->id);
+
+ if (out_filemode)
+ *out_filemode = entry->mode;
+
+ error = git_blob__getbuf(out, blob);
+
+done:
+ git_blob_free(blob);
+ return error;
+}
+
+int git_reader_for_index(
+ git_reader **out,
+ git_repository *repo,
+ git_index *index)
+{
+ index_reader *reader;
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+
+ reader = git__calloc(1, sizeof(index_reader));
+ GIT_ERROR_CHECK_ALLOC(reader);
+
+ reader->reader.read = index_reader_read;
+ reader->repo = repo;
+
+ if (index) {
+ reader->index = index;
+ } else if ((error = git_repository_index__weakptr(&reader->index, repo)) < 0) {
+ git__free(reader);
+ return error;
+ }
+
+ *out = (git_reader *)reader;
+ return 0;
+}
+
+/* generic */
+
+int git_reader_read(
+ git_str *out,
+ git_oid *out_id,
+ git_filemode_t *out_filemode,
+ git_reader *reader,
+ const char *filename)
+{
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(reader);
+ GIT_ASSERT_ARG(filename);
+
+ return reader->read(out, out_id, out_filemode, reader, filename);
+}
+
+void git_reader_free(git_reader *reader)
+{
+ if (!reader)
+ return;
+
+ git__free(reader);
+}
diff --git a/src/libgit2/reader.h b/src/libgit2/reader.h
new file mode 100644
index 0000000..b58dc93
--- /dev/null
+++ b/src/libgit2/reader.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_reader_h__
+#define INCLUDE_reader_h__
+
+#include "common.h"
+
+/* Returned when the workdir does not match the index */
+#define GIT_READER_MISMATCH 1
+
+typedef struct git_reader git_reader;
+
+/*
+ * The `git_reader` structure is a generic interface for reading the
+ * contents of a file by its name, and implementations are provided
+ * for reading out of a tree, the index, and the working directory.
+ *
+ * Note that the reader implementation is meant to have a short
+ * lifecycle and does not increase the refcount of the object that
+ * it's reading. Callers should ensure that they do not use a
+ * reader after disposing the underlying object that it reads.
+ */
+struct git_reader {
+ int (*read)(git_str *out, git_oid *out_oid, git_filemode_t *mode, git_reader *reader, const char *filename);
+};
+
+/**
+ * Create a `git_reader` that will allow random access to the given
+ * tree. Paths requested via `git_reader_read` will be rooted at this
+ * tree, callers are not expected to recurse through tree lookups. Thus,
+ * you can request to read `/src/foo.c` and the tree provided to this
+ * function will be searched to find another tree named `src`, which
+ * will then be opened to find `foo.c`.
+ *
+ * @param out The reader for the given tree
+ * @param tree The tree object to read
+ * @return 0 on success, or an error code < 0
+ */
+extern int git_reader_for_tree(
+ git_reader **out,
+ git_tree *tree);
+
+/**
+ * Create a `git_reader` that will allow random access to the given
+ * index, or the repository's index.
+ *
+ * @param out The reader for the given index
+ * @param repo The repository containing the index
+ * @param index The index to read, or NULL to use the repository's index
+ * @return 0 on success, or an error code < 0
+ */
+extern int git_reader_for_index(
+ git_reader **out,
+ git_repository *repo,
+ git_index *index);
+
+/**
+ * Create a `git_reader` that will allow random access to the given
+ * repository's working directory. Note that the contents are read
+ * in repository format, meaning any workdir -> odb filters are
+ * applied.
+ *
+ * If `validate_index` is set to true, reads of files will hash the
+ * on-disk contents and ensure that the resulting object ID matches
+ * the repository's index. This ensures that the working directory
+ * is unmodified from the index contents.
+ *
+ * @param out The reader for the given working directory
+ * @param repo The repository containing the working directory
+ * @param validate_index If true, the working directory contents will
+ * be compared to the index contents during read to ensure that
+ * the working directory is unmodified.
+ * @return 0 on success, or an error code < 0
+ */
+extern int git_reader_for_workdir(
+ git_reader **out,
+ git_repository *repo,
+ bool validate_index);
+
+/**
+ * Read the given filename from the reader and populate the given buffer
+ * with the contents and the given oid with the object ID.
+ *
+ * @param out The buffer to populate with the file contents
+ * @param out_id The oid to populate with the object ID
+ * @param reader The reader to read
+ * @param filename The filename to read from the reader
+ */
+extern int git_reader_read(
+ git_str *out,
+ git_oid *out_id,
+ git_filemode_t *out_filemode,
+ git_reader *reader,
+ const char *filename);
+
+/**
+ * Free the given reader and any associated objects.
+ *
+ * @param reader The reader to free
+ */
+extern void git_reader_free(git_reader *reader);
+
+#endif
diff --git a/src/libgit2/rebase.c b/src/libgit2/rebase.c
new file mode 100644
index 0000000..77e442e
--- /dev/null
+++ b/src/libgit2/rebase.c
@@ -0,0 +1,1469 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+
+#include "str.h"
+#include "repository.h"
+#include "posix.h"
+#include "filebuf.h"
+#include "commit.h"
+#include "merge.h"
+#include "array.h"
+#include "config.h"
+#include "annotated_commit.h"
+#include "index.h"
+
+#include <git2/types.h>
+#include <git2/annotated_commit.h>
+#include <git2/rebase.h>
+#include <git2/commit.h>
+#include <git2/reset.h>
+#include <git2/revwalk.h>
+#include <git2/notes.h>
+
+#define REBASE_APPLY_DIR "rebase-apply"
+#define REBASE_MERGE_DIR "rebase-merge"
+
+#define HEAD_NAME_FILE "head-name"
+#define ORIG_HEAD_FILE "orig-head"
+#define HEAD_FILE "head"
+#define ONTO_FILE "onto"
+#define ONTO_NAME_FILE "onto_name"
+#define QUIET_FILE "quiet"
+#define INTERACTIVE_FILE "interactive"
+
+#define MSGNUM_FILE "msgnum"
+#define END_FILE "end"
+#define CMT_FILE_FMT "cmt.%" PRIuZ
+#define CURRENT_FILE "current"
+#define REWRITTEN_FILE "rewritten"
+
+#define ORIG_DETACHED_HEAD "detached HEAD"
+
+#define NOTES_DEFAULT_REF NULL
+
+#define REBASE_DIR_MODE 0777
+#define REBASE_FILE_MODE 0666
+
+typedef enum {
+ GIT_REBASE_NONE = 0,
+ GIT_REBASE_APPLY = 1,
+ GIT_REBASE_MERGE = 2,
+ GIT_REBASE_INTERACTIVE = 3
+} git_rebase_t;
+
+struct git_rebase {
+ git_repository *repo;
+
+ git_rebase_options options;
+
+ git_rebase_t type;
+ char *state_path;
+
+ /* Temporary buffer for paths within the state path. */
+ git_str state_filename;
+
+ unsigned int head_detached:1,
+ inmemory:1,
+ quiet:1,
+ started:1;
+
+ git_array_t(git_rebase_operation) operations;
+ size_t current;
+
+ /* Used by in-memory rebase */
+ git_index *index;
+ git_commit *last_commit;
+
+ /* Used by regular (not in-memory) merge-style rebase */
+ git_oid orig_head_id;
+ char *orig_head_name;
+
+ git_oid onto_id;
+ char *onto_name;
+};
+
+#define GIT_REBASE_STATE_INIT {0}
+
+static int rebase_state_type(
+ git_rebase_t *type_out,
+ char **path_out,
+ git_repository *repo)
+{
+ git_str path = GIT_STR_INIT;
+ git_str interactive_path = GIT_STR_INIT;
+ git_rebase_t type = GIT_REBASE_NONE;
+
+ if (git_str_joinpath(&path, repo->gitdir, REBASE_APPLY_DIR) < 0)
+ return -1;
+
+ if (git_fs_path_isdir(git_str_cstr(&path))) {
+ type = GIT_REBASE_APPLY;
+ goto done;
+ }
+
+ git_str_clear(&path);
+ if (git_str_joinpath(&path, repo->gitdir, REBASE_MERGE_DIR) < 0)
+ return -1;
+
+ if (git_fs_path_isdir(git_str_cstr(&path))) {
+ if (git_str_joinpath(&interactive_path, path.ptr, INTERACTIVE_FILE) < 0)
+ return -1;
+
+ if (git_fs_path_isfile(interactive_path.ptr))
+ type = GIT_REBASE_INTERACTIVE;
+ else
+ type = GIT_REBASE_MERGE;
+
+ goto done;
+ }
+
+done:
+ *type_out = type;
+
+ if (type != GIT_REBASE_NONE && path_out)
+ *path_out = git_str_detach(&path);
+
+ git_str_dispose(&path);
+ git_str_dispose(&interactive_path);
+
+ return 0;
+}
+
+GIT_INLINE(int) rebase_readfile(
+ git_str *out,
+ git_rebase *rebase,
+ const char *filename)
+{
+ /*
+ * `rebase->state_filename` is a temporary buffer to avoid
+ * unnecessary allocations and copies of `rebase->state_path`.
+ * At the start and end of this function it always contains the
+ * contents of `rebase->state_path` itself.
+ */
+ size_t state_path_len = rebase->state_filename.size;
+ int error;
+
+ git_str_clear(out);
+
+ if ((error = git_str_joinpath(&rebase->state_filename, rebase->state_filename.ptr, filename)) < 0 ||
+ (error = git_futils_readbuffer(out, rebase->state_filename.ptr)) < 0)
+ goto done;
+
+ git_str_rtrim(out);
+
+done:
+ git_str_truncate(&rebase->state_filename, state_path_len);
+ return error;
+}
+
+GIT_INLINE(int) rebase_readint(
+ size_t *out,
+ git_str *asc_out,
+ git_rebase *rebase,
+ const char *filename)
+{
+ int32_t num;
+ const char *eol;
+ int error = 0;
+
+ if ((error = rebase_readfile(asc_out, rebase, filename)) < 0)
+ return error;
+
+ if (git__strntol32(&num, asc_out->ptr, asc_out->size, &eol, 10) < 0 || num < 0 || *eol) {
+ git_error_set(GIT_ERROR_REBASE, "the file '%s' contains an invalid numeric value", filename);
+ return -1;
+ }
+
+ *out = (size_t) num;
+
+ return 0;
+}
+
+GIT_INLINE(int) rebase_readoid(
+ git_oid *out,
+ git_str *str_out,
+ git_rebase *rebase,
+ const char *filename)
+{
+ int error;
+
+ if ((error = rebase_readfile(str_out, rebase, filename)) < 0)
+ return error;
+
+ if (str_out->size != git_oid_hexsize(rebase->repo->oid_type) ||
+ git_oid__fromstr(out, str_out->ptr, rebase->repo->oid_type) < 0) {
+ git_error_set(GIT_ERROR_REBASE, "the file '%s' contains an invalid object ID", filename);
+ return -1;
+ }
+
+ return 0;
+}
+
+static git_rebase_operation *rebase_operation_alloc(
+ git_rebase *rebase,
+ git_rebase_operation_t type,
+ git_oid *id,
+ const char *exec)
+{
+ git_rebase_operation *operation;
+
+ GIT_ASSERT_WITH_RETVAL((type == GIT_REBASE_OPERATION_EXEC) == !id, NULL);
+ GIT_ASSERT_WITH_RETVAL((type == GIT_REBASE_OPERATION_EXEC) == !!exec, NULL);
+
+ if ((operation = git_array_alloc(rebase->operations)) == NULL)
+ return NULL;
+
+ operation->type = type;
+ git_oid_cpy((git_oid *)&operation->id, id);
+ operation->exec = exec;
+
+ return operation;
+}
+
+static int rebase_open_merge(git_rebase *rebase)
+{
+ git_str buf = GIT_STR_INIT, cmt = GIT_STR_INIT;
+ git_oid id;
+ git_rebase_operation *operation;
+ size_t i, msgnum = 0, end;
+ int error;
+
+ /* Read 'msgnum' if it exists (otherwise, let msgnum = 0) */
+ if ((error = rebase_readint(&msgnum, &buf, rebase, MSGNUM_FILE)) < 0 &&
+ error != GIT_ENOTFOUND)
+ goto done;
+
+ if (msgnum) {
+ rebase->started = 1;
+ rebase->current = msgnum - 1;
+ }
+
+ /* Read 'end' */
+ if ((error = rebase_readint(&end, &buf, rebase, END_FILE)) < 0)
+ goto done;
+
+ /* Read 'current' if it exists */
+ if ((error = rebase_readoid(&id, &buf, rebase, CURRENT_FILE)) < 0 &&
+ error != GIT_ENOTFOUND)
+ goto done;
+
+ /* Read cmt.* */
+ git_array_init_to_size(rebase->operations, end);
+ GIT_ERROR_CHECK_ARRAY(rebase->operations);
+
+ for (i = 0; i < end; i++) {
+ git_str_clear(&cmt);
+
+ if ((error = git_str_printf(&cmt, "cmt.%" PRIuZ, (i+1))) < 0 ||
+ (error = rebase_readoid(&id, &buf, rebase, cmt.ptr)) < 0)
+ goto done;
+
+ operation = rebase_operation_alloc(rebase, GIT_REBASE_OPERATION_PICK, &id, NULL);
+ GIT_ERROR_CHECK_ALLOC(operation);
+ }
+
+ /* Read 'onto_name' */
+ if ((error = rebase_readfile(&buf, rebase, ONTO_NAME_FILE)) < 0)
+ goto done;
+
+ rebase->onto_name = git_str_detach(&buf);
+
+done:
+ git_str_dispose(&cmt);
+ git_str_dispose(&buf);
+
+ return error;
+}
+
+static int rebase_alloc(git_rebase **out, const git_rebase_options *rebase_opts)
+{
+ git_rebase *rebase = git__calloc(1, sizeof(git_rebase));
+ GIT_ERROR_CHECK_ALLOC(rebase);
+
+ *out = NULL;
+
+ if (rebase_opts)
+ memcpy(&rebase->options, rebase_opts, sizeof(git_rebase_options));
+ else
+ git_rebase_options_init(&rebase->options, GIT_REBASE_OPTIONS_VERSION);
+
+ if (rebase_opts && rebase_opts->rewrite_notes_ref) {
+ rebase->options.rewrite_notes_ref = git__strdup(rebase_opts->rewrite_notes_ref);
+ GIT_ERROR_CHECK_ALLOC(rebase->options.rewrite_notes_ref);
+ }
+
+ *out = rebase;
+
+ return 0;
+}
+
+static int rebase_check_versions(const git_rebase_options *given_opts)
+{
+ GIT_ERROR_CHECK_VERSION(given_opts, GIT_REBASE_OPTIONS_VERSION, "git_rebase_options");
+
+ if (given_opts)
+ GIT_ERROR_CHECK_VERSION(&given_opts->checkout_options, GIT_CHECKOUT_OPTIONS_VERSION, "git_checkout_options");
+
+ return 0;
+}
+
+int git_rebase_open(
+ git_rebase **out,
+ git_repository *repo,
+ const git_rebase_options *given_opts)
+{
+ git_rebase *rebase;
+ git_str orig_head_name = GIT_STR_INIT,
+ orig_head_id = GIT_STR_INIT,
+ onto_id = GIT_STR_INIT;
+ int error;
+
+ GIT_ASSERT_ARG(repo);
+
+ if ((error = rebase_check_versions(given_opts)) < 0)
+ return error;
+
+ if (rebase_alloc(&rebase, given_opts) < 0)
+ return -1;
+
+ rebase->repo = repo;
+
+ if ((error = rebase_state_type(&rebase->type, &rebase->state_path, repo)) < 0)
+ goto done;
+
+ if (rebase->type == GIT_REBASE_NONE) {
+ git_error_set(GIT_ERROR_REBASE, "there is no rebase in progress");
+ error = GIT_ENOTFOUND;
+ goto done;
+ }
+
+ if ((error = git_str_puts(&rebase->state_filename, rebase->state_path)) < 0)
+ goto done;
+
+ if ((error = rebase_readfile(&orig_head_name, rebase, HEAD_NAME_FILE)) < 0)
+ goto done;
+
+ git_str_rtrim(&orig_head_name);
+
+ if (strcmp(ORIG_DETACHED_HEAD, orig_head_name.ptr) == 0)
+ rebase->head_detached = 1;
+
+ if ((error = rebase_readoid(&rebase->orig_head_id, &orig_head_id, rebase, ORIG_HEAD_FILE)) < 0) {
+ /* Previous versions of git.git used 'head' here; support that. */
+ if (error == GIT_ENOTFOUND)
+ error = rebase_readoid(&rebase->orig_head_id, &orig_head_id, rebase, HEAD_FILE);
+
+ if (error < 0)
+ goto done;
+ }
+
+ if ((error = rebase_readoid(&rebase->onto_id, &onto_id, rebase, ONTO_FILE)) < 0)
+ goto done;
+
+ if (!rebase->head_detached)
+ rebase->orig_head_name = git_str_detach(&orig_head_name);
+
+ switch (rebase->type) {
+ case GIT_REBASE_INTERACTIVE:
+ git_error_set(GIT_ERROR_REBASE, "interactive rebase is not supported");
+ error = -1;
+ break;
+ case GIT_REBASE_MERGE:
+ error = rebase_open_merge(rebase);
+ break;
+ case GIT_REBASE_APPLY:
+ git_error_set(GIT_ERROR_REBASE, "patch application rebase is not supported");
+ error = -1;
+ break;
+ default:
+ abort();
+ }
+
+done:
+ if (error == 0)
+ *out = rebase;
+ else
+ git_rebase_free(rebase);
+
+ git_str_dispose(&orig_head_name);
+ git_str_dispose(&orig_head_id);
+ git_str_dispose(&onto_id);
+ return error;
+}
+
+static int rebase_cleanup(git_rebase *rebase)
+{
+ if (!rebase || rebase->inmemory)
+ return 0;
+
+ return git_fs_path_isdir(rebase->state_path) ?
+ git_futils_rmdir_r(rebase->state_path, NULL, GIT_RMDIR_REMOVE_FILES) :
+ 0;
+}
+
+static int rebase_setupfile(git_rebase *rebase, const char *filename, int flags, const char *fmt, ...)
+{
+ git_str path = GIT_STR_INIT,
+ contents = GIT_STR_INIT;
+ va_list ap;
+ int error;
+
+ va_start(ap, fmt);
+ git_str_vprintf(&contents, fmt, ap);
+ va_end(ap);
+
+ if ((error = git_str_joinpath(&path, rebase->state_path, filename)) == 0)
+ error = git_futils_writebuffer(&contents, path.ptr, flags, REBASE_FILE_MODE);
+
+ git_str_dispose(&path);
+ git_str_dispose(&contents);
+
+ return error;
+}
+
+static const char *rebase_onto_name(const git_annotated_commit *onto)
+{
+ if (onto->ref_name && git__strncmp(onto->ref_name, "refs/heads/", 11) == 0)
+ return onto->ref_name + 11;
+ else if (onto->ref_name)
+ return onto->ref_name;
+ else
+ return onto->id_str;
+}
+
+static int rebase_setupfiles_merge(git_rebase *rebase)
+{
+ git_str commit_filename = GIT_STR_INIT;
+ char id_str[GIT_OID_MAX_HEXSIZE + 1];
+ git_rebase_operation *operation;
+ size_t i;
+ int error = 0;
+
+ if ((error = rebase_setupfile(rebase, END_FILE, 0, "%" PRIuZ "\n", git_array_size(rebase->operations))) < 0 ||
+ (error = rebase_setupfile(rebase, ONTO_NAME_FILE, 0, "%s\n", rebase->onto_name)) < 0)
+ goto done;
+
+ for (i = 0; i < git_array_size(rebase->operations); i++) {
+ operation = git_array_get(rebase->operations, i);
+
+ git_str_clear(&commit_filename);
+ git_str_printf(&commit_filename, CMT_FILE_FMT, i+1);
+
+ git_oid_tostr(id_str, GIT_OID_MAX_HEXSIZE + 1, &operation->id);
+
+ if ((error = rebase_setupfile(rebase, commit_filename.ptr, 0, "%s\n", id_str)) < 0)
+ goto done;
+ }
+
+done:
+ git_str_dispose(&commit_filename);
+ return error;
+}
+
+static int rebase_setupfiles(git_rebase *rebase)
+{
+ char onto[GIT_OID_MAX_HEXSIZE + 1], orig_head[GIT_OID_MAX_HEXSIZE + 1];
+ const char *orig_head_name;
+
+ git_oid_tostr(onto, GIT_OID_MAX_HEXSIZE + 1, &rebase->onto_id);
+ git_oid_tostr(orig_head, GIT_OID_MAX_HEXSIZE + 1, &rebase->orig_head_id);
+
+ if (p_mkdir(rebase->state_path, REBASE_DIR_MODE) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to create rebase directory '%s'", rebase->state_path);
+ return -1;
+ }
+
+ orig_head_name = rebase->head_detached ? ORIG_DETACHED_HEAD :
+ rebase->orig_head_name;
+
+ if (git_repository__set_orig_head(rebase->repo, &rebase->orig_head_id) < 0 ||
+ rebase_setupfile(rebase, HEAD_NAME_FILE, 0, "%s\n", orig_head_name) < 0 ||
+ rebase_setupfile(rebase, ONTO_FILE, 0, "%s\n", onto) < 0 ||
+ rebase_setupfile(rebase, ORIG_HEAD_FILE, 0, "%s\n", orig_head) < 0 ||
+ rebase_setupfile(rebase, QUIET_FILE, 0, rebase->quiet ? "t\n" : "\n") < 0)
+ return -1;
+
+ return rebase_setupfiles_merge(rebase);
+}
+
+int git_rebase_options_init(git_rebase_options *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_rebase_options, GIT_REBASE_OPTIONS_INIT);
+ return 0;
+}
+
+#ifndef GIT_DEPRECATE_HARD
+int git_rebase_init_options(git_rebase_options *opts, unsigned int version)
+{
+ return git_rebase_options_init(opts, version);
+}
+#endif
+
+static int rebase_ensure_not_in_progress(git_repository *repo)
+{
+ int error;
+ git_rebase_t type;
+
+ if ((error = rebase_state_type(&type, NULL, repo)) < 0)
+ return error;
+
+ if (type != GIT_REBASE_NONE) {
+ git_error_set(GIT_ERROR_REBASE, "there is an existing rebase in progress");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int rebase_ensure_not_dirty(
+ git_repository *repo,
+ bool check_index,
+ bool check_workdir,
+ int fail_with)
+{
+ git_tree *head = NULL;
+ git_index *index = NULL;
+ git_diff *diff = NULL;
+ int error = 0;
+
+ if (check_index) {
+ if ((error = git_repository_head_tree(&head, repo)) < 0 ||
+ (error = git_repository_index(&index, repo)) < 0 ||
+ (error = git_diff_tree_to_index(&diff, repo, head, index, NULL)) < 0)
+ goto done;
+
+ if (git_diff_num_deltas(diff) > 0) {
+ git_error_set(GIT_ERROR_REBASE, "uncommitted changes exist in index");
+ error = fail_with;
+ goto done;
+ }
+
+ git_diff_free(diff);
+ diff = NULL;
+ }
+
+ if (check_workdir) {
+ git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
+ diff_opts.ignore_submodules = GIT_SUBMODULE_IGNORE_UNTRACKED;
+ if ((error = git_diff_index_to_workdir(&diff, repo, index, &diff_opts)) < 0)
+ goto done;
+
+ if (git_diff_num_deltas(diff) > 0) {
+ git_error_set(GIT_ERROR_REBASE, "unstaged changes exist in workdir");
+ error = fail_with;
+ goto done;
+ }
+ }
+
+done:
+ git_diff_free(diff);
+ git_index_free(index);
+ git_tree_free(head);
+
+ return error;
+}
+
+static int rebase_init_operations(
+ git_rebase *rebase,
+ git_repository *repo,
+ const git_annotated_commit *branch,
+ const git_annotated_commit *upstream,
+ const git_annotated_commit *onto)
+{
+ git_revwalk *revwalk = NULL;
+ git_commit *commit;
+ git_oid id;
+ bool merge;
+ git_rebase_operation *operation;
+ int error;
+
+ if (!upstream)
+ upstream = onto;
+
+ if ((error = git_revwalk_new(&revwalk, rebase->repo)) < 0 ||
+ (error = git_revwalk_push(revwalk, git_annotated_commit_id(branch))) < 0 ||
+ (error = git_revwalk_hide(revwalk, git_annotated_commit_id(upstream))) < 0)
+ goto done;
+
+ git_revwalk_sorting(revwalk, GIT_SORT_REVERSE);
+
+ while ((error = git_revwalk_next(&id, revwalk)) == 0) {
+ if ((error = git_commit_lookup(&commit, repo, &id)) < 0)
+ goto done;
+
+ merge = (git_commit_parentcount(commit) > 1);
+ git_commit_free(commit);
+
+ if (merge)
+ continue;
+
+ operation = rebase_operation_alloc(rebase, GIT_REBASE_OPERATION_PICK, &id, NULL);
+ GIT_ERROR_CHECK_ALLOC(operation);
+ }
+
+ error = 0;
+
+done:
+ git_revwalk_free(revwalk);
+ return error;
+}
+
+static int rebase_init_merge(
+ git_rebase *rebase,
+ git_repository *repo,
+ const git_annotated_commit *branch,
+ const git_annotated_commit *upstream,
+ const git_annotated_commit *onto)
+{
+ git_reference *head_ref = NULL;
+ git_commit *onto_commit = NULL;
+ git_str reflog = GIT_STR_INIT;
+ git_str state_path = GIT_STR_INIT;
+ int error;
+
+ GIT_UNUSED(upstream);
+
+ if ((error = git_str_joinpath(&state_path, repo->gitdir, REBASE_MERGE_DIR)) < 0 ||
+ (error = git_str_put(&rebase->state_filename, state_path.ptr, state_path.size)) < 0)
+ goto done;
+
+ rebase->state_path = git_str_detach(&state_path);
+ GIT_ERROR_CHECK_ALLOC(rebase->state_path);
+
+ if (branch->ref_name && strcmp(branch->ref_name, "HEAD")) {
+ rebase->orig_head_name = git__strdup(branch->ref_name);
+ GIT_ERROR_CHECK_ALLOC(rebase->orig_head_name);
+ } else {
+ rebase->head_detached = 1;
+ }
+
+ rebase->onto_name = git__strdup(rebase_onto_name(onto));
+ GIT_ERROR_CHECK_ALLOC(rebase->onto_name);
+
+ rebase->quiet = rebase->options.quiet;
+
+ git_oid_cpy(&rebase->orig_head_id, git_annotated_commit_id(branch));
+ git_oid_cpy(&rebase->onto_id, git_annotated_commit_id(onto));
+
+ if ((error = rebase_setupfiles(rebase)) < 0 ||
+ (error = git_str_printf(&reflog,
+ "rebase: checkout %s", rebase_onto_name(onto))) < 0 ||
+ (error = git_commit_lookup(
+ &onto_commit, repo, git_annotated_commit_id(onto))) < 0 ||
+ (error = git_checkout_tree(repo,
+ (git_object *)onto_commit, &rebase->options.checkout_options)) < 0 ||
+ (error = git_reference_create(&head_ref, repo, GIT_HEAD_FILE,
+ git_annotated_commit_id(onto), 1, reflog.ptr)) < 0)
+ goto done;
+
+done:
+ git_reference_free(head_ref);
+ git_commit_free(onto_commit);
+ git_str_dispose(&reflog);
+ git_str_dispose(&state_path);
+
+ return error;
+}
+
+static int rebase_init_inmemory(
+ git_rebase *rebase,
+ git_repository *repo,
+ const git_annotated_commit *branch,
+ const git_annotated_commit *upstream,
+ const git_annotated_commit *onto)
+{
+ GIT_UNUSED(branch);
+ GIT_UNUSED(upstream);
+
+ return git_commit_lookup(
+ &rebase->last_commit, repo, git_annotated_commit_id(onto));
+}
+
+int git_rebase_init(
+ git_rebase **out,
+ git_repository *repo,
+ const git_annotated_commit *branch,
+ const git_annotated_commit *upstream,
+ const git_annotated_commit *onto,
+ const git_rebase_options *given_opts)
+{
+ git_rebase *rebase = NULL;
+ git_annotated_commit *head_branch = NULL;
+ git_reference *head_ref = NULL;
+ bool inmemory = (given_opts && given_opts->inmemory);
+ int error;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(upstream || onto);
+
+ *out = NULL;
+
+ if (!onto)
+ onto = upstream;
+
+ if ((error = rebase_check_versions(given_opts)) < 0)
+ goto done;
+
+ if (!inmemory) {
+ if ((error = git_repository__ensure_not_bare(repo, "rebase")) < 0 ||
+ (error = rebase_ensure_not_in_progress(repo)) < 0 ||
+ (error = rebase_ensure_not_dirty(repo, true, true, GIT_ERROR)) < 0)
+ goto done;
+ }
+
+ if (!branch) {
+ if ((error = git_repository_head(&head_ref, repo)) < 0 ||
+ (error = git_annotated_commit_from_ref(&head_branch, repo, head_ref)) < 0)
+ goto done;
+
+ branch = head_branch;
+ }
+
+ if (rebase_alloc(&rebase, given_opts) < 0)
+ return -1;
+
+ rebase->repo = repo;
+ rebase->inmemory = inmemory;
+ rebase->type = GIT_REBASE_MERGE;
+
+ if ((error = rebase_init_operations(rebase, repo, branch, upstream, onto)) < 0)
+ goto done;
+
+ if (inmemory)
+ error = rebase_init_inmemory(rebase, repo, branch, upstream, onto);
+ else
+ error = rebase_init_merge(rebase, repo, branch ,upstream, onto);
+
+ if (error == 0)
+ *out = rebase;
+
+done:
+ git_reference_free(head_ref);
+ git_annotated_commit_free(head_branch);
+
+ if (error < 0) {
+ rebase_cleanup(rebase);
+ git_rebase_free(rebase);
+ }
+
+ return error;
+}
+
+static void normalize_checkout_options_for_apply(
+ git_checkout_options *checkout_opts,
+ git_rebase *rebase,
+ git_commit *current_commit)
+{
+ memcpy(checkout_opts, &rebase->options.checkout_options, sizeof(git_checkout_options));
+
+ if (!checkout_opts->ancestor_label)
+ checkout_opts->ancestor_label = "ancestor";
+
+ if (rebase->type == GIT_REBASE_MERGE) {
+ if (!checkout_opts->our_label)
+ checkout_opts->our_label = rebase->onto_name;
+
+ if (!checkout_opts->their_label)
+ checkout_opts->their_label = git_commit_summary(current_commit);
+ } else {
+ abort();
+ }
+}
+
+GIT_INLINE(int) rebase_movenext(git_rebase *rebase)
+{
+ size_t next = rebase->started ? rebase->current + 1 : 0;
+
+ if (next == git_array_size(rebase->operations))
+ return GIT_ITEROVER;
+
+ rebase->started = 1;
+ rebase->current = next;
+
+ return 0;
+}
+
+static int rebase_next_merge(
+ git_rebase_operation **out,
+ git_rebase *rebase)
+{
+ git_str path = GIT_STR_INIT;
+ git_commit *current_commit = NULL, *parent_commit = NULL;
+ git_tree *current_tree = NULL, *head_tree = NULL, *parent_tree = NULL;
+ git_index *index = NULL;
+ git_indexwriter indexwriter = GIT_INDEXWRITER_INIT;
+ git_rebase_operation *operation;
+ git_checkout_options checkout_opts;
+ char current_idstr[GIT_OID_MAX_HEXSIZE + 1];
+ unsigned int parent_count;
+ int error;
+
+ *out = NULL;
+
+ operation = git_array_get(rebase->operations, rebase->current);
+
+ if ((error = git_commit_lookup(&current_commit, rebase->repo, &operation->id)) < 0 ||
+ (error = git_commit_tree(&current_tree, current_commit)) < 0 ||
+ (error = git_repository_head_tree(&head_tree, rebase->repo)) < 0)
+ goto done;
+
+ if ((parent_count = git_commit_parentcount(current_commit)) > 1) {
+ git_error_set(GIT_ERROR_REBASE, "cannot rebase a merge commit");
+ error = -1;
+ goto done;
+ } else if (parent_count) {
+ if ((error = git_commit_parent(&parent_commit, current_commit, 0)) < 0 ||
+ (error = git_commit_tree(&parent_tree, parent_commit)) < 0)
+ goto done;
+ }
+
+ git_oid_tostr(current_idstr, GIT_OID_MAX_HEXSIZE + 1, &operation->id);
+
+ normalize_checkout_options_for_apply(&checkout_opts, rebase, current_commit);
+
+ if ((error = git_indexwriter_init_for_operation(&indexwriter, rebase->repo, &checkout_opts.checkout_strategy)) < 0 ||
+ (error = rebase_setupfile(rebase, MSGNUM_FILE, 0, "%" PRIuZ "\n", rebase->current+1)) < 0 ||
+ (error = rebase_setupfile(rebase, CURRENT_FILE, 0, "%s\n", current_idstr)) < 0 ||
+ (error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, &rebase->options.merge_options)) < 0 ||
+ (error = git_merge__check_result(rebase->repo, index)) < 0 ||
+ (error = git_checkout_index(rebase->repo, index, &checkout_opts)) < 0 ||
+ (error = git_indexwriter_commit(&indexwriter)) < 0)
+ goto done;
+
+ *out = operation;
+
+done:
+ git_indexwriter_cleanup(&indexwriter);
+ git_index_free(index);
+ git_tree_free(current_tree);
+ git_tree_free(head_tree);
+ git_tree_free(parent_tree);
+ git_commit_free(parent_commit);
+ git_commit_free(current_commit);
+ git_str_dispose(&path);
+
+ return error;
+}
+
+static int rebase_next_inmemory(
+ git_rebase_operation **out,
+ git_rebase *rebase)
+{
+ git_commit *current_commit = NULL, *parent_commit = NULL;
+ git_tree *current_tree = NULL, *head_tree = NULL, *parent_tree = NULL;
+ git_rebase_operation *operation;
+ git_index *index = NULL;
+ unsigned int parent_count;
+ int error;
+
+ *out = NULL;
+
+ operation = git_array_get(rebase->operations, rebase->current);
+
+ if ((error = git_commit_lookup(&current_commit, rebase->repo, &operation->id)) < 0 ||
+ (error = git_commit_tree(&current_tree, current_commit)) < 0)
+ goto done;
+
+ if ((parent_count = git_commit_parentcount(current_commit)) > 1) {
+ git_error_set(GIT_ERROR_REBASE, "cannot rebase a merge commit");
+ error = -1;
+ goto done;
+ } else if (parent_count) {
+ if ((error = git_commit_parent(&parent_commit, current_commit, 0)) < 0 ||
+ (error = git_commit_tree(&parent_tree, parent_commit)) < 0)
+ goto done;
+ }
+
+ if ((error = git_commit_tree(&head_tree, rebase->last_commit)) < 0 ||
+ (error = git_merge_trees(&index, rebase->repo, parent_tree, head_tree, current_tree, &rebase->options.merge_options)) < 0)
+ goto done;
+
+ if (!rebase->index) {
+ rebase->index = index;
+ index = NULL;
+ } else {
+ if ((error = git_index_read_index(rebase->index, index)) < 0)
+ goto done;
+ }
+
+ *out = operation;
+
+done:
+ git_commit_free(current_commit);
+ git_commit_free(parent_commit);
+ git_tree_free(current_tree);
+ git_tree_free(head_tree);
+ git_tree_free(parent_tree);
+ git_index_free(index);
+
+ return error;
+}
+
+int git_rebase_next(
+ git_rebase_operation **out,
+ git_rebase *rebase)
+{
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(rebase);
+
+ if ((error = rebase_movenext(rebase)) < 0)
+ return error;
+
+ if (rebase->inmemory)
+ error = rebase_next_inmemory(out, rebase);
+ else if (rebase->type == GIT_REBASE_MERGE)
+ error = rebase_next_merge(out, rebase);
+ else
+ abort();
+
+ return error;
+}
+
+int git_rebase_inmemory_index(
+ git_index **out,
+ git_rebase *rebase)
+{
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(rebase);
+ GIT_ASSERT_ARG(rebase->index);
+
+ GIT_REFCOUNT_INC(rebase->index);
+ *out = rebase->index;
+
+ return 0;
+}
+
+#ifndef GIT_DEPRECATE_HARD
+static int create_signed(
+ git_oid *out,
+ git_rebase *rebase,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *message_encoding,
+ const char *message,
+ git_tree *tree,
+ size_t parent_count,
+ const git_commit **parents)
+{
+ git_str commit_content = GIT_STR_INIT;
+ git_buf commit_signature = { NULL, 0, 0 },
+ signature_field = { NULL, 0, 0 };
+ int error;
+
+ git_error_clear();
+
+ if ((error = git_commit__create_buffer(&commit_content,
+ rebase->repo, author, committer, message_encoding,
+ message, tree, parent_count, parents)) < 0)
+ goto done;
+
+ error = rebase->options.signing_cb(&commit_signature,
+ &signature_field, commit_content.ptr,
+ rebase->options.payload);
+
+ if (error) {
+ if (error != GIT_PASSTHROUGH)
+ git_error_set_after_callback_function(error, "signing_cb");
+
+ goto done;
+ }
+
+ error = git_commit_create_with_signature(out, rebase->repo,
+ commit_content.ptr,
+ commit_signature.size > 0 ? commit_signature.ptr : NULL,
+ signature_field.size > 0 ? signature_field.ptr : NULL);
+
+done:
+ git_buf_dispose(&commit_signature);
+ git_buf_dispose(&signature_field);
+ git_str_dispose(&commit_content);
+ return error;
+}
+#endif
+
+static int rebase_commit__create(
+ git_commit **out,
+ git_rebase *rebase,
+ git_index *index,
+ git_commit *parent_commit,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *message_encoding,
+ const char *message)
+{
+ git_rebase_operation *operation;
+ git_commit *current_commit = NULL, *commit = NULL;
+ git_tree *parent_tree = NULL, *tree = NULL;
+ git_oid tree_id, commit_id;
+ int error;
+
+ operation = git_array_get(rebase->operations, rebase->current);
+
+ if (git_index_has_conflicts(index)) {
+ git_error_set(GIT_ERROR_REBASE, "conflicts have not been resolved");
+ error = GIT_EUNMERGED;
+ goto done;
+ }
+
+ if ((error = git_commit_lookup(&current_commit, rebase->repo, &operation->id)) < 0 ||
+ (error = git_commit_tree(&parent_tree, parent_commit)) < 0 ||
+ (error = git_index_write_tree_to(&tree_id, index, rebase->repo)) < 0 ||
+ (error = git_tree_lookup(&tree, rebase->repo, &tree_id)) < 0)
+ goto done;
+
+ if (git_oid_equal(&tree_id, git_tree_id(parent_tree))) {
+ git_error_set(GIT_ERROR_REBASE, "this patch has already been applied");
+ error = GIT_EAPPLIED;
+ goto done;
+ }
+
+ if (!author)
+ author = git_commit_author(current_commit);
+
+ if (!message) {
+ message_encoding = git_commit_message_encoding(current_commit);
+ message = git_commit_message(current_commit);
+ }
+
+ git_error_clear();
+ error = GIT_PASSTHROUGH;
+
+ if (rebase->options.commit_create_cb) {
+ error = rebase->options.commit_create_cb(&commit_id,
+ author, committer, message_encoding, message,
+ tree, 1, (const git_commit **)&parent_commit,
+ rebase->options.payload);
+
+ git_error_set_after_callback_function(error,
+ "commit_create_cb");
+ }
+#ifndef GIT_DEPRECATE_HARD
+ else if (rebase->options.signing_cb) {
+ error = create_signed(&commit_id, rebase, author,
+ committer, message_encoding, message, tree,
+ 1, (const git_commit **)&parent_commit);
+ }
+#endif
+
+ if (error == GIT_PASSTHROUGH)
+ error = git_commit_create(&commit_id, rebase->repo, NULL,
+ author, committer, message_encoding, message,
+ tree, 1, (const git_commit **)&parent_commit);
+
+ if (error)
+ goto done;
+
+ if ((error = git_commit_lookup(&commit, rebase->repo, &commit_id)) < 0)
+ goto done;
+
+ *out = commit;
+
+done:
+ if (error < 0)
+ git_commit_free(commit);
+
+ git_commit_free(current_commit);
+ git_tree_free(parent_tree);
+ git_tree_free(tree);
+
+ return error;
+}
+
+static int rebase_commit_merge(
+ git_oid *commit_id,
+ git_rebase *rebase,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *message_encoding,
+ const char *message)
+{
+ git_rebase_operation *operation;
+ git_reference *head = NULL;
+ git_commit *head_commit = NULL, *commit = NULL;
+ git_index *index = NULL;
+ char old_idstr[GIT_OID_MAX_HEXSIZE + 1], new_idstr[GIT_OID_MAX_HEXSIZE + 1];
+ int error;
+
+ operation = git_array_get(rebase->operations, rebase->current);
+ GIT_ASSERT(operation);
+
+ if ((error = rebase_ensure_not_dirty(rebase->repo, false, true, GIT_EUNMERGED)) < 0 ||
+ (error = git_repository_head(&head, rebase->repo)) < 0 ||
+ (error = git_reference_peel((git_object **)&head_commit, head, GIT_OBJECT_COMMIT)) < 0 ||
+ (error = git_repository_index(&index, rebase->repo)) < 0 ||
+ (error = rebase_commit__create(&commit, rebase, index, head_commit,
+ author, committer, message_encoding, message)) < 0 ||
+ (error = git_reference__update_for_commit(
+ rebase->repo, NULL, "HEAD", git_commit_id(commit), "rebase")) < 0)
+ goto done;
+
+ git_oid_tostr(old_idstr, GIT_OID_MAX_HEXSIZE + 1, &operation->id);
+ git_oid_tostr(new_idstr, GIT_OID_MAX_HEXSIZE + 1, git_commit_id(commit));
+
+ if ((error = rebase_setupfile(rebase, REWRITTEN_FILE, O_CREAT|O_WRONLY|O_APPEND,
+ "%s %s\n", old_idstr, new_idstr)) < 0)
+ goto done;
+
+ git_oid_cpy(commit_id, git_commit_id(commit));
+
+done:
+ git_index_free(index);
+ git_reference_free(head);
+ git_commit_free(head_commit);
+ git_commit_free(commit);
+ return error;
+}
+
+static int rebase_commit_inmemory(
+ git_oid *commit_id,
+ git_rebase *rebase,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *message_encoding,
+ const char *message)
+{
+ git_commit *commit = NULL;
+ int error = 0;
+
+ GIT_ASSERT_ARG(rebase->index);
+ GIT_ASSERT_ARG(rebase->last_commit);
+ GIT_ASSERT_ARG(rebase->current < rebase->operations.size);
+
+ if ((error = rebase_commit__create(&commit, rebase, rebase->index,
+ rebase->last_commit, author, committer, message_encoding, message)) < 0)
+ goto done;
+
+ git_commit_free(rebase->last_commit);
+ rebase->last_commit = commit;
+
+ git_oid_cpy(commit_id, git_commit_id(commit));
+
+done:
+ if (error < 0)
+ git_commit_free(commit);
+
+ return error;
+}
+
+int git_rebase_commit(
+ git_oid *id,
+ git_rebase *rebase,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *message_encoding,
+ const char *message)
+{
+ int error;
+
+ GIT_ASSERT_ARG(rebase);
+ GIT_ASSERT_ARG(committer);
+
+ if (rebase->inmemory)
+ error = rebase_commit_inmemory(
+ id, rebase, author, committer, message_encoding, message);
+ else if (rebase->type == GIT_REBASE_MERGE)
+ error = rebase_commit_merge(
+ id, rebase, author, committer, message_encoding, message);
+ else
+ abort();
+
+ return error;
+}
+
+int git_rebase_abort(git_rebase *rebase)
+{
+ git_reference *orig_head_ref = NULL;
+ git_commit *orig_head_commit = NULL;
+ int error;
+
+ GIT_ASSERT_ARG(rebase);
+
+ if (rebase->inmemory)
+ return 0;
+
+ error = rebase->head_detached ?
+ git_reference_create(&orig_head_ref, rebase->repo, GIT_HEAD_FILE,
+ &rebase->orig_head_id, 1, "rebase: aborting") :
+ git_reference_symbolic_create(
+ &orig_head_ref, rebase->repo, GIT_HEAD_FILE, rebase->orig_head_name, 1,
+ "rebase: aborting");
+
+ if (error < 0)
+ goto done;
+
+ if ((error = git_commit_lookup(
+ &orig_head_commit, rebase->repo, &rebase->orig_head_id)) < 0 ||
+ (error = git_reset(rebase->repo, (git_object *)orig_head_commit,
+ GIT_RESET_HARD, &rebase->options.checkout_options)) < 0)
+ goto done;
+
+ error = rebase_cleanup(rebase);
+
+done:
+ git_commit_free(orig_head_commit);
+ git_reference_free(orig_head_ref);
+
+ return error;
+}
+
+static int notes_ref_lookup(git_str *out, git_rebase *rebase)
+{
+ git_config *config = NULL;
+ int do_rewrite, error;
+
+ if (rebase->options.rewrite_notes_ref) {
+ git_str_attach_notowned(out,
+ rebase->options.rewrite_notes_ref,
+ strlen(rebase->options.rewrite_notes_ref));
+ return 0;
+ }
+
+ if ((error = git_repository_config(&config, rebase->repo)) < 0 ||
+ (error = git_config_get_bool(&do_rewrite, config, "notes.rewrite.rebase")) < 0) {
+
+ if (error != GIT_ENOTFOUND)
+ goto done;
+
+ git_error_clear();
+ do_rewrite = 1;
+ }
+
+ error = do_rewrite ?
+ git_config__get_string_buf(out, config, "notes.rewriteref") :
+ GIT_ENOTFOUND;
+
+done:
+ git_config_free(config);
+ return error;
+}
+
+static int rebase_copy_note(
+ git_rebase *rebase,
+ const char *notes_ref,
+ git_oid *from,
+ git_oid *to,
+ const git_signature *committer)
+{
+ git_note *note = NULL;
+ git_oid note_id;
+ git_signature *who = NULL;
+ int error;
+
+ if ((error = git_note_read(&note, rebase->repo, notes_ref, from)) < 0) {
+ if (error == GIT_ENOTFOUND) {
+ git_error_clear();
+ error = 0;
+ }
+
+ goto done;
+ }
+
+ if (!committer) {
+ if((error = git_signature_default(&who, rebase->repo)) < 0) {
+ if (error != GIT_ENOTFOUND ||
+ (error = git_signature_now(&who, "unknown", "unknown")) < 0)
+ goto done;
+
+ git_error_clear();
+ }
+
+ committer = who;
+ }
+
+ error = git_note_create(&note_id, rebase->repo, notes_ref,
+ git_note_author(note), committer, to, git_note_message(note), 0);
+
+done:
+ git_note_free(note);
+ git_signature_free(who);
+
+ return error;
+}
+
+static int rebase_copy_notes(
+ git_rebase *rebase,
+ const git_signature *committer)
+{
+ git_str path = GIT_STR_INIT,
+ rewritten = GIT_STR_INIT,
+ notes_ref = GIT_STR_INIT;
+ char *pair_list, *fromstr, *tostr, *end;
+ git_oid from, to;
+ unsigned int linenum = 1;
+ int error = 0;
+
+ if ((error = notes_ref_lookup(&notes_ref, rebase)) < 0) {
+ if (error == GIT_ENOTFOUND) {
+ git_error_clear();
+ error = 0;
+ }
+
+ goto done;
+ }
+
+ if ((error = git_str_joinpath(&path, rebase->state_path, REWRITTEN_FILE)) < 0 ||
+ (error = git_futils_readbuffer(&rewritten, path.ptr)) < 0)
+ goto done;
+
+ pair_list = rewritten.ptr;
+
+ while (*pair_list) {
+ fromstr = pair_list;
+
+ if ((end = strchr(fromstr, '\n')) == NULL)
+ goto on_error;
+
+ pair_list = end+1;
+ *end = '\0';
+
+ if ((end = strchr(fromstr, ' ')) == NULL)
+ goto on_error;
+
+ tostr = end+1;
+ *end = '\0';
+
+ if (strlen(fromstr) != git_oid_hexsize(rebase->repo->oid_type) ||
+ strlen(tostr) != git_oid_hexsize(rebase->repo->oid_type) ||
+ git_oid__fromstr(&from, fromstr, rebase->repo->oid_type) < 0 ||
+ git_oid__fromstr(&to, tostr, rebase->repo->oid_type) < 0)
+ goto on_error;
+
+ if ((error = rebase_copy_note(rebase, notes_ref.ptr, &from, &to, committer)) < 0)
+ goto done;
+
+ linenum++;
+ }
+
+ goto done;
+
+on_error:
+ git_error_set(GIT_ERROR_REBASE, "invalid rewritten file at line %d", linenum);
+ error = -1;
+
+done:
+ git_str_dispose(&rewritten);
+ git_str_dispose(&path);
+ git_str_dispose(&notes_ref);
+
+ return error;
+}
+
+static int return_to_orig_head(git_rebase *rebase)
+{
+ git_reference *terminal_ref = NULL, *branch_ref = NULL, *head_ref = NULL;
+ git_commit *terminal_commit = NULL;
+ git_str branch_msg = GIT_STR_INIT, head_msg = GIT_STR_INIT;
+ char onto[GIT_OID_MAX_HEXSIZE + 1];
+ int error = 0;
+
+ git_oid_tostr(onto, GIT_OID_MAX_HEXSIZE + 1, &rebase->onto_id);
+
+ if ((error = git_str_printf(&branch_msg,
+ "rebase finished: %s onto %s", rebase->orig_head_name, onto)) == 0 &&
+ (error = git_str_printf(&head_msg,
+ "rebase finished: returning to %s", rebase->orig_head_name)) == 0 &&
+ (error = git_repository_head(&terminal_ref, rebase->repo)) == 0 &&
+ (error = git_reference_peel((git_object **)&terminal_commit,
+ terminal_ref, GIT_OBJECT_COMMIT)) == 0 &&
+ (error = git_reference_create_matching(&branch_ref,
+ rebase->repo, rebase->orig_head_name,
+ git_commit_id(terminal_commit), 1,
+ &rebase->orig_head_id, branch_msg.ptr)) == 0)
+ error = git_reference_symbolic_create(&head_ref,
+ rebase->repo, GIT_HEAD_FILE, rebase->orig_head_name, 1,
+ head_msg.ptr);
+
+ git_str_dispose(&head_msg);
+ git_str_dispose(&branch_msg);
+ git_commit_free(terminal_commit);
+ git_reference_free(head_ref);
+ git_reference_free(branch_ref);
+ git_reference_free(terminal_ref);
+
+ return error;
+}
+
+int git_rebase_finish(
+ git_rebase *rebase,
+ const git_signature *signature)
+{
+ int error = 0;
+
+ GIT_ASSERT_ARG(rebase);
+
+ if (rebase->inmemory)
+ return 0;
+
+ if (!rebase->head_detached)
+ error = return_to_orig_head(rebase);
+
+ if (error == 0 && (error = rebase_copy_notes(rebase, signature)) == 0)
+ error = rebase_cleanup(rebase);
+
+ return error;
+}
+
+const char *git_rebase_orig_head_name(git_rebase *rebase) {
+ GIT_ASSERT_ARG_WITH_RETVAL(rebase, NULL);
+ return rebase->orig_head_name;
+}
+
+const git_oid *git_rebase_orig_head_id(git_rebase *rebase) {
+ GIT_ASSERT_ARG_WITH_RETVAL(rebase, NULL);
+ return &rebase->orig_head_id;
+}
+
+const char *git_rebase_onto_name(git_rebase *rebase) {
+ GIT_ASSERT_ARG_WITH_RETVAL(rebase, NULL);
+ return rebase->onto_name;
+}
+
+const git_oid *git_rebase_onto_id(git_rebase *rebase) {
+ return &rebase->onto_id;
+}
+
+size_t git_rebase_operation_entrycount(git_rebase *rebase)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(rebase, 0);
+
+ return git_array_size(rebase->operations);
+}
+
+size_t git_rebase_operation_current(git_rebase *rebase)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(rebase, 0);
+
+ return rebase->started ? rebase->current : GIT_REBASE_NO_OPERATION;
+}
+
+git_rebase_operation *git_rebase_operation_byindex(git_rebase *rebase, size_t idx)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(rebase, NULL);
+
+ return git_array_get(rebase->operations, idx);
+}
+
+void git_rebase_free(git_rebase *rebase)
+{
+ if (rebase == NULL)
+ return;
+
+ git_index_free(rebase->index);
+ git_commit_free(rebase->last_commit);
+ git__free(rebase->onto_name);
+ git__free(rebase->orig_head_name);
+ git__free(rebase->state_path);
+ git_str_dispose(&rebase->state_filename);
+ git_array_clear(rebase->operations);
+ git__free((char *)rebase->options.rewrite_notes_ref);
+ git__free(rebase);
+}
diff --git a/src/libgit2/refdb.c b/src/libgit2/refdb.c
new file mode 100644
index 0000000..ed33de9
--- /dev/null
+++ b/src/libgit2/refdb.c
@@ -0,0 +1,424 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "refdb.h"
+
+#include "git2/object.h"
+#include "git2/refs.h"
+#include "git2/refdb.h"
+#include "git2/sys/refdb_backend.h"
+
+#include "hash.h"
+#include "refs.h"
+#include "reflog.h"
+#include "posix.h"
+
+#define DEFAULT_NESTING_LEVEL 5
+#define MAX_NESTING_LEVEL 10
+
+int git_refdb_new(git_refdb **out, git_repository *repo)
+{
+ git_refdb *db;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+
+ db = git__calloc(1, sizeof(*db));
+ GIT_ERROR_CHECK_ALLOC(db);
+
+ db->repo = repo;
+
+ *out = db;
+ GIT_REFCOUNT_INC(db);
+ return 0;
+}
+
+int git_refdb_open(git_refdb **out, git_repository *repo)
+{
+ git_refdb *db;
+ git_refdb_backend *dir;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+
+ *out = NULL;
+
+ if (git_refdb_new(&db, repo) < 0)
+ return -1;
+
+ /* Add the default (filesystem) backend */
+ if (git_refdb_backend_fs(&dir, repo) < 0) {
+ git_refdb_free(db);
+ return -1;
+ }
+
+ db->repo = repo;
+ db->backend = dir;
+
+ *out = db;
+ return 0;
+}
+
+static void refdb_free_backend(git_refdb *db)
+{
+ if (db->backend)
+ db->backend->free(db->backend);
+}
+
+int git_refdb_set_backend(git_refdb *db, git_refdb_backend *backend)
+{
+ GIT_ERROR_CHECK_VERSION(backend, GIT_REFDB_BACKEND_VERSION, "git_refdb_backend");
+
+ if (!backend->exists || !backend->lookup || !backend->iterator ||
+ !backend->write || !backend->rename || !backend->del ||
+ !backend->has_log || !backend->ensure_log || !backend->free ||
+ !backend->reflog_read || !backend->reflog_write ||
+ !backend->reflog_rename || !backend->reflog_delete ||
+ (backend->lock && !backend->unlock)) {
+ git_error_set(GIT_ERROR_REFERENCE, "incomplete refdb backend implementation");
+ return GIT_EINVALID;
+ }
+
+ refdb_free_backend(db);
+ db->backend = backend;
+
+ return 0;
+}
+
+int git_refdb_compress(git_refdb *db)
+{
+ GIT_ASSERT_ARG(db);
+
+ if (db->backend->compress)
+ return db->backend->compress(db->backend);
+
+ return 0;
+}
+
+void git_refdb__free(git_refdb *db)
+{
+ refdb_free_backend(db);
+ git__memzero(db, sizeof(*db));
+ git__free(db);
+}
+
+void git_refdb_free(git_refdb *db)
+{
+ if (db == NULL)
+ return;
+
+ GIT_REFCOUNT_DEC(db, git_refdb__free);
+}
+
+int git_refdb_exists(int *exists, git_refdb *refdb, const char *ref_name)
+{
+ GIT_ASSERT_ARG(exists);
+ GIT_ASSERT_ARG(refdb);
+ GIT_ASSERT_ARG(refdb->backend);
+
+ return refdb->backend->exists(exists, refdb->backend, ref_name);
+}
+
+int git_refdb_lookup(git_reference **out, git_refdb *db, const char *ref_name)
+{
+ git_reference *ref;
+ int error;
+
+ GIT_ASSERT_ARG(db);
+ GIT_ASSERT_ARG(db->backend);
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(ref_name);
+
+ error = db->backend->lookup(&ref, db->backend, ref_name);
+ if (error < 0)
+ return error;
+
+ GIT_REFCOUNT_INC(db);
+ ref->db = db;
+
+ *out = ref;
+ return 0;
+}
+
+int git_refdb_resolve(
+ git_reference **out,
+ git_refdb *db,
+ const char *ref_name,
+ int max_nesting)
+{
+ git_reference *ref = NULL;
+ int error = 0, nesting;
+
+ *out = NULL;
+
+ if (max_nesting > MAX_NESTING_LEVEL)
+ max_nesting = MAX_NESTING_LEVEL;
+ else if (max_nesting < 0)
+ max_nesting = DEFAULT_NESTING_LEVEL;
+
+ if ((error = git_refdb_lookup(&ref, db, ref_name)) < 0)
+ goto out;
+
+ for (nesting = 0; nesting < max_nesting; nesting++) {
+ git_reference *resolved;
+
+ if (ref->type == GIT_REFERENCE_DIRECT)
+ break;
+
+ if ((error = git_refdb_lookup(&resolved, db, git_reference_symbolic_target(ref))) < 0) {
+ /* If we found a symbolic reference with a nonexistent target, return it. */
+ if (error == GIT_ENOTFOUND) {
+ error = 0;
+ *out = ref;
+ ref = NULL;
+ }
+ goto out;
+ }
+
+ git_reference_free(ref);
+ ref = resolved;
+ }
+
+ if (ref->type != GIT_REFERENCE_DIRECT && max_nesting != 0) {
+ git_error_set(GIT_ERROR_REFERENCE,
+ "cannot resolve reference (>%u levels deep)", max_nesting);
+ error = -1;
+ goto out;
+ }
+
+ *out = ref;
+ ref = NULL;
+out:
+ git_reference_free(ref);
+ return error;
+}
+
+int git_refdb_iterator(git_reference_iterator **out, git_refdb *db, const char *glob)
+{
+ int error;
+
+ if (!db->backend || !db->backend->iterator) {
+ git_error_set(GIT_ERROR_REFERENCE, "this backend doesn't support iterators");
+ return -1;
+ }
+
+ if ((error = db->backend->iterator(out, db->backend, glob)) < 0)
+ return error;
+
+ GIT_REFCOUNT_INC(db);
+ (*out)->db = db;
+
+ return 0;
+}
+
+int git_refdb_iterator_next(git_reference **out, git_reference_iterator *iter)
+{
+ int error;
+
+ if ((error = iter->next(out, iter)) < 0)
+ return error;
+
+ GIT_REFCOUNT_INC(iter->db);
+ (*out)->db = iter->db;
+
+ return 0;
+}
+
+int git_refdb_iterator_next_name(const char **out, git_reference_iterator *iter)
+{
+ return iter->next_name(out, iter);
+}
+
+void git_refdb_iterator_free(git_reference_iterator *iter)
+{
+ GIT_REFCOUNT_DEC(iter->db, git_refdb__free);
+ iter->free(iter);
+}
+
+int git_refdb_write(git_refdb *db, git_reference *ref, int force, const git_signature *who, const char *message, const git_oid *old_id, const char *old_target)
+{
+ GIT_ASSERT_ARG(db);
+ GIT_ASSERT_ARG(db->backend);
+
+ GIT_REFCOUNT_INC(db);
+ ref->db = db;
+
+ return db->backend->write(db->backend, ref, force, who, message, old_id, old_target);
+}
+
+int git_refdb_rename(
+ git_reference **out,
+ git_refdb *db,
+ const char *old_name,
+ const char *new_name,
+ int force,
+ const git_signature *who,
+ const char *message)
+{
+ int error;
+
+ GIT_ASSERT_ARG(db);
+ GIT_ASSERT_ARG(db->backend);
+
+ error = db->backend->rename(out, db->backend, old_name, new_name, force, who, message);
+ if (error < 0)
+ return error;
+
+ if (out) {
+ GIT_REFCOUNT_INC(db);
+ (*out)->db = db;
+ }
+
+ return 0;
+}
+
+int git_refdb_delete(struct git_refdb *db, const char *ref_name, const git_oid *old_id, const char *old_target)
+{
+ GIT_ASSERT_ARG(db);
+ GIT_ASSERT_ARG(db->backend);
+
+ return db->backend->del(db->backend, ref_name, old_id, old_target);
+}
+
+int git_refdb_reflog_read(git_reflog **out, git_refdb *db, const char *name)
+{
+ int error;
+
+ GIT_ASSERT_ARG(db);
+ GIT_ASSERT_ARG(db->backend);
+
+ if ((error = db->backend->reflog_read(out, db->backend, name)) < 0)
+ return error;
+
+ GIT_REFCOUNT_INC(db);
+ (*out)->db = db;
+
+ return 0;
+}
+
+int git_refdb_should_write_reflog(int *out, git_refdb *db, const git_reference *ref)
+{
+ int error, logall;
+
+ error = git_repository__configmap_lookup(&logall, db->repo, GIT_CONFIGMAP_LOGALLREFUPDATES);
+ if (error < 0)
+ return error;
+
+ /* Defaults to the opposite of the repo being bare */
+ if (logall == GIT_LOGALLREFUPDATES_UNSET)
+ logall = !git_repository_is_bare(db->repo);
+
+ *out = 0;
+ switch (logall) {
+ case GIT_LOGALLREFUPDATES_FALSE:
+ *out = 0;
+ break;
+
+ case GIT_LOGALLREFUPDATES_TRUE:
+ /* Only write if it already has a log,
+ * or if it's under heads/, remotes/ or notes/
+ */
+ *out = git_refdb_has_log(db, ref->name) ||
+ !git__prefixcmp(ref->name, GIT_REFS_HEADS_DIR) ||
+ !git__strcmp(ref->name, GIT_HEAD_FILE) ||
+ !git__prefixcmp(ref->name, GIT_REFS_REMOTES_DIR) ||
+ !git__prefixcmp(ref->name, GIT_REFS_NOTES_DIR);
+ break;
+
+ case GIT_LOGALLREFUPDATES_ALWAYS:
+ *out = 1;
+ break;
+ }
+
+ return 0;
+}
+
+int git_refdb_should_write_head_reflog(int *out, git_refdb *db, const git_reference *ref)
+{
+ git_reference *head = NULL, *resolved = NULL;
+ const char *name;
+ int error;
+
+ *out = 0;
+
+ if (ref->type == GIT_REFERENCE_SYMBOLIC) {
+ error = 0;
+ goto out;
+ }
+
+ if ((error = git_refdb_lookup(&head, db, GIT_HEAD_FILE)) < 0)
+ goto out;
+
+ if (git_reference_type(head) == GIT_REFERENCE_DIRECT)
+ goto out;
+
+ /* Go down the symref chain until we find the branch */
+ if ((error = git_refdb_resolve(&resolved, db, git_reference_symbolic_target(head), -1)) < 0) {
+ if (error != GIT_ENOTFOUND)
+ goto out;
+ error = 0;
+ name = git_reference_symbolic_target(head);
+ } else if (git_reference_type(resolved) == GIT_REFERENCE_SYMBOLIC) {
+ name = git_reference_symbolic_target(resolved);
+ } else {
+ name = git_reference_name(resolved);
+ }
+
+ if (strcmp(name, ref->name))
+ goto out;
+
+ *out = 1;
+
+out:
+ git_reference_free(resolved);
+ git_reference_free(head);
+ return error;
+}
+
+int git_refdb_has_log(git_refdb *db, const char *refname)
+{
+ GIT_ASSERT_ARG(db);
+ GIT_ASSERT_ARG(refname);
+
+ return db->backend->has_log(db->backend, refname);
+}
+
+int git_refdb_ensure_log(git_refdb *db, const char *refname)
+{
+ GIT_ASSERT_ARG(db);
+ GIT_ASSERT_ARG(refname);
+
+ return db->backend->ensure_log(db->backend, refname);
+}
+
+int git_refdb_init_backend(git_refdb_backend *backend, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ backend, version, git_refdb_backend, GIT_REFDB_BACKEND_INIT);
+ return 0;
+}
+
+int git_refdb_lock(void **payload, git_refdb *db, const char *refname)
+{
+ GIT_ASSERT_ARG(payload);
+ GIT_ASSERT_ARG(db);
+ GIT_ASSERT_ARG(refname);
+
+ if (!db->backend->lock) {
+ git_error_set(GIT_ERROR_REFERENCE, "backend does not support locking");
+ return -1;
+ }
+
+ return db->backend->lock(payload, db->backend, refname);
+}
+
+int git_refdb_unlock(git_refdb *db, void *payload, int success, int update_reflog, const git_reference *ref, const git_signature *sig, const char *message)
+{
+ GIT_ASSERT_ARG(db);
+
+ return db->backend->unlock(db->backend, payload, success, update_reflog, ref, sig, message);
+}
diff --git a/src/libgit2/refdb.h b/src/libgit2/refdb.h
new file mode 100644
index 0000000..84e19b1
--- /dev/null
+++ b/src/libgit2/refdb.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_refdb_h__
+#define INCLUDE_refdb_h__
+
+#include "common.h"
+
+#include "git2/refdb.h"
+#include "repository.h"
+
+struct git_refdb {
+ git_refcount rc;
+ git_repository *repo;
+ git_refdb_backend *backend;
+};
+
+void git_refdb__free(git_refdb *db);
+
+int git_refdb_exists(
+ int *exists,
+ git_refdb *refdb,
+ const char *ref_name);
+
+int git_refdb_lookup(
+ git_reference **out,
+ git_refdb *refdb,
+ const char *ref_name);
+
+/**
+ * Resolve the reference by following symbolic references.
+ *
+ * Given a reference name, this function will follow any symbolic references up
+ * to `max_nesting` deep and return the resolved direct reference. If any of
+ * the intermediate symbolic references points to a non-existing reference,
+ * then that symbolic reference is returned instead with an error code of `0`.
+ * If the given reference is a direct reference already, it is returned
+ * directly.
+ *
+ * If `max_nesting` is `0`, the reference will not be resolved. If it's
+ * negative, it will be set to the default resolve depth which is `5`.
+ *
+ * @param out Pointer to store the result in.
+ * @param db The refdb to use for resolving the reference.
+ * @param ref_name The reference name to lookup and resolve.
+ * @param max_nesting The maximum nesting depth.
+ * @return `0` on success, a negative error code otherwise.
+ */
+int git_refdb_resolve(
+ git_reference **out,
+ git_refdb *db,
+ const char *ref_name,
+ int max_nesting);
+
+int git_refdb_rename(
+ git_reference **out,
+ git_refdb *db,
+ const char *old_name,
+ const char *new_name,
+ int force,
+ const git_signature *who,
+ const char *message);
+
+int git_refdb_iterator(git_reference_iterator **out, git_refdb *db, const char *glob);
+int git_refdb_iterator_next(git_reference **out, git_reference_iterator *iter);
+int git_refdb_iterator_next_name(const char **out, git_reference_iterator *iter);
+void git_refdb_iterator_free(git_reference_iterator *iter);
+
+int git_refdb_write(git_refdb *refdb, git_reference *ref, int force, const git_signature *who, const char *message, const git_oid *old_id, const char *old_target);
+int git_refdb_delete(git_refdb *refdb, const char *ref_name, const git_oid *old_id, const char *old_target);
+
+int git_refdb_reflog_read(git_reflog **out, git_refdb *db, const char *name);
+int git_refdb_reflog_write(git_reflog *reflog);
+
+/**
+ * Determine whether a reflog entry should be created for the given reference.
+ *
+ * Whether or not writing to a reference should create a reflog entry is
+ * dependent on a number of things. Most importantly, there's the
+ * "core.logAllRefUpdates" setting that controls in which situations a
+ * reference should get a corresponding reflog entry. The following values for
+ * it are understood:
+ *
+ * - "false": Do not log reference updates.
+ *
+ * - "true": Log normal reference updates. This will write entries for
+ * references in "refs/heads", "refs/remotes", "refs/notes" and
+ * "HEAD" or if the reference already has a log entry.
+ *
+ * - "always": Always create a reflog entry.
+ *
+ * If unset, the value will default to "true" for non-bare repositories and
+ * "false" for bare ones.
+ *
+ * @param out pointer to which the result will be written, `1` means a reflog
+ * entry should be written, `0` means none should be written.
+ * @param db The refdb to decide this for.
+ * @param ref The reference one wants to check.
+ * @return `0` on success, a negative error code otherwise.
+ */
+int git_refdb_should_write_reflog(int *out, git_refdb *db, const git_reference *ref);
+
+/**
+ * Determine whether a reflog entry should be created for HEAD if creating one
+ * for the given reference
+ *
+ * In case the given reference is being pointed to by HEAD, then creating a
+ * reflog entry for this reference also requires us to create a corresponding
+ * reflog entry for HEAD. This function can be used to determine that scenario.
+ *
+ * @param out pointer to which the result will be written, `1` means a reflog
+ * entry should be written, `0` means none should be written.
+ * @param db The refdb to decide this for.
+ * @param ref The reference one wants to check.
+ * @return `0` on success, a negative error code otherwise.
+ */
+int git_refdb_should_write_head_reflog(int *out, git_refdb *db, const git_reference *ref);
+
+int git_refdb_has_log(git_refdb *db, const char *refname);
+int git_refdb_ensure_log(git_refdb *refdb, const char *refname);
+
+int git_refdb_lock(void **payload, git_refdb *db, const char *refname);
+int git_refdb_unlock(git_refdb *db, void *payload, int success, int update_reflog, const git_reference *ref, const git_signature *sig, const char *message);
+
+#endif
diff --git a/src/libgit2/refdb_fs.c b/src/libgit2/refdb_fs.c
new file mode 100644
index 0000000..e34a714
--- /dev/null
+++ b/src/libgit2/refdb_fs.c
@@ -0,0 +1,2508 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "refs.h"
+#include "hash.h"
+#include "repository.h"
+#include "futils.h"
+#include "filebuf.h"
+#include "pack.h"
+#include "parse.h"
+#include "reflog.h"
+#include "refdb.h"
+#include "iterator.h"
+#include "sortedcache.h"
+#include "signature.h"
+#include "wildmatch.h"
+#include "path.h"
+
+#include <git2/tag.h>
+#include <git2/object.h>
+#include <git2/refdb.h>
+#include <git2/branch.h>
+#include <git2/sys/refdb_backend.h>
+#include <git2/sys/refs.h>
+#include <git2/sys/reflog.h>
+
+#define DEFAULT_NESTING_LEVEL 5
+#define MAX_NESTING_LEVEL 10
+
+enum {
+ PACKREF_HAS_PEEL = 1,
+ PACKREF_WAS_LOOSE = 2,
+ PACKREF_CANNOT_PEEL = 4,
+ PACKREF_SHADOWED = 8
+};
+
+enum {
+ PEELING_NONE = 0,
+ PEELING_STANDARD,
+ PEELING_FULL
+};
+
+struct packref {
+ git_oid oid;
+ git_oid peel;
+ char flags;
+ char name[GIT_FLEX_ARRAY];
+};
+
+typedef struct refdb_fs_backend {
+ git_refdb_backend parent;
+
+ git_repository *repo;
+ /* path to git directory */
+ char *gitpath;
+ /* path to common objects' directory */
+ char *commonpath;
+
+ git_oid_t oid_type;
+
+ int fsync : 1,
+ sorted : 1;
+ int peeling_mode;
+ git_iterator_flag_t iterator_flags;
+ uint32_t direach_flags;
+ git_sortedcache *refcache;
+ git_map packed_refs_map;
+ git_mutex prlock; /* protect packed_refs_map */
+ git_futils_filestamp packed_refs_stamp;
+} refdb_fs_backend;
+
+static int refdb_reflog_fs__delete(git_refdb_backend *_backend, const char *name);
+static char *packed_set_peeling_mode(char *data, size_t data_sz, refdb_fs_backend *backend);
+
+GIT_INLINE(int) loose_path(
+ git_str *out,
+ const char *base,
+ const char *refname)
+{
+ if (git_str_joinpath(out, base, refname) < 0)
+ return -1;
+
+ return git_fs_path_validate_str_length_with_suffix(out,
+ CONST_STRLEN(".lock"));
+}
+
+GIT_INLINE(int) reflog_path(
+ git_str *out,
+ git_repository *repo,
+ const char *refname)
+{
+ const char *base;
+ int error;
+
+ base = (strcmp(refname, GIT_HEAD_FILE) == 0) ? repo->gitdir :
+ repo->commondir;
+
+ if ((error = git_str_joinpath(out, base, GIT_REFLOG_DIR)) < 0)
+ return error;
+
+ return loose_path(out, out->ptr, refname);
+}
+
+static int packref_cmp(const void *a_, const void *b_)
+{
+ const struct packref *a = a_, *b = b_;
+ return strcmp(a->name, b->name);
+}
+
+static int packed_reload(refdb_fs_backend *backend)
+{
+ int error;
+ git_str packedrefs = GIT_STR_INIT;
+ size_t oid_hexsize = git_oid_hexsize(backend->oid_type);
+ char *scan, *eof, *eol;
+
+ if (!backend->gitpath)
+ return 0;
+
+ error = git_sortedcache_lockandload(backend->refcache, &packedrefs);
+
+ /*
+ * If we can't find the packed-refs, clear table and return.
+ * Any other error just gets passed through.
+ * If no error, and file wasn't changed, just return.
+ * Anything else means we need to refresh the packed refs.
+ */
+ if (error <= 0) {
+ if (error == GIT_ENOTFOUND) {
+ GIT_UNUSED(git_sortedcache_clear(backend->refcache, true));
+ git_error_clear();
+ error = 0;
+ }
+ return error;
+ }
+
+ /* At this point, refresh the packed refs from the loaded buffer. */
+
+ GIT_UNUSED(git_sortedcache_clear(backend->refcache, false));
+
+ scan = packedrefs.ptr;
+ eof = scan + packedrefs.size;
+
+ scan = packed_set_peeling_mode(scan, packedrefs.size, backend);
+ if (!scan)
+ goto parse_failed;
+
+ while (scan < eof && *scan == '#') {
+ if (!(eol = strchr(scan, '\n')))
+ goto parse_failed;
+ scan = eol + 1;
+ }
+
+ while (scan < eof) {
+ struct packref *ref;
+ git_oid oid;
+
+ /* parse "<OID> <refname>\n" */
+
+ if (git_oid__fromstr(&oid, scan, backend->oid_type) < 0)
+ goto parse_failed;
+ scan += oid_hexsize;
+
+ if (*scan++ != ' ')
+ goto parse_failed;
+ if (!(eol = strchr(scan, '\n')))
+ goto parse_failed;
+ *eol = '\0';
+ if (eol[-1] == '\r')
+ eol[-1] = '\0';
+
+ if (git_sortedcache_upsert((void **)&ref, backend->refcache, scan) < 0)
+ goto parse_failed;
+ scan = eol + 1;
+
+ git_oid_cpy(&ref->oid, &oid);
+
+ /* look for optional "^<OID>\n" */
+
+ if (*scan == '^') {
+ if (git_oid__fromstr(&oid, scan + 1, backend->oid_type) < 0)
+ goto parse_failed;
+ scan += oid_hexsize + 1;
+
+ if (scan < eof) {
+ if (!(eol = strchr(scan, '\n')))
+ goto parse_failed;
+ scan = eol + 1;
+ }
+
+ git_oid_cpy(&ref->peel, &oid);
+ ref->flags |= PACKREF_HAS_PEEL;
+ }
+ else if (backend->peeling_mode == PEELING_FULL ||
+ (backend->peeling_mode == PEELING_STANDARD &&
+ git__prefixcmp(ref->name, GIT_REFS_TAGS_DIR) == 0))
+ ref->flags |= PACKREF_CANNOT_PEEL;
+ }
+
+ git_sortedcache_wunlock(backend->refcache);
+ git_str_dispose(&packedrefs);
+
+ return 0;
+
+parse_failed:
+ git_error_set(GIT_ERROR_REFERENCE, "corrupted packed references file");
+
+ GIT_UNUSED(git_sortedcache_clear(backend->refcache, false));
+ git_sortedcache_wunlock(backend->refcache);
+ git_str_dispose(&packedrefs);
+
+ return -1;
+}
+
+static int loose_parse_oid(
+ git_oid *oid,
+ const char *filename,
+ git_str *file_content,
+ git_oid_t oid_type)
+{
+ const char *str = git_str_cstr(file_content);
+ size_t oid_hexsize = git_oid_hexsize(oid_type);
+
+ if (git_str_len(file_content) < oid_hexsize)
+ goto corrupted;
+
+ /* we need to get 40 OID characters from the file */
+ if (git_oid__fromstr(oid, str, oid_type) < 0)
+ goto corrupted;
+
+ /* If the file is longer than 40 chars, the 41st must be a space */
+ str += oid_hexsize;
+ if (*str == '\0' || git__isspace(*str))
+ return 0;
+
+corrupted:
+ git_error_set(GIT_ERROR_REFERENCE, "corrupted loose reference file: %s", filename);
+ return -1;
+}
+
+static int loose_readbuffer(git_str *buf, const char *base, const char *path)
+{
+ int error;
+
+ if ((error = loose_path(buf, base, path)) < 0 ||
+ (error = git_futils_readbuffer(buf, buf->ptr)) < 0)
+ git_str_dispose(buf);
+
+ return error;
+}
+
+static int loose_lookup_to_packfile(refdb_fs_backend *backend, const char *name)
+{
+ int error = 0;
+ git_str ref_file = GIT_STR_INIT;
+ struct packref *ref = NULL;
+ git_oid oid;
+
+ /* if we fail to load the loose reference, assume someone changed
+ * the filesystem under us and skip it...
+ */
+ if (loose_readbuffer(&ref_file, backend->gitpath, name) < 0) {
+ git_error_clear();
+ goto done;
+ }
+
+ /* skip symbolic refs */
+ if (!git__prefixcmp(git_str_cstr(&ref_file), GIT_SYMREF))
+ goto done;
+
+ /* parse OID from file */
+ if ((error = loose_parse_oid(&oid, name, &ref_file, backend->oid_type)) < 0)
+ goto done;
+
+ if ((error = git_sortedcache_wlock(backend->refcache)) < 0)
+ goto done;
+
+ if (!(error = git_sortedcache_upsert(
+ (void **)&ref, backend->refcache, name))) {
+
+ git_oid_cpy(&ref->oid, &oid);
+ ref->flags = PACKREF_WAS_LOOSE;
+ }
+
+ git_sortedcache_wunlock(backend->refcache);
+
+done:
+ git_str_dispose(&ref_file);
+ return error;
+}
+
+static int _dirent_loose_load(void *payload, git_str *full_path)
+{
+ refdb_fs_backend *backend = payload;
+ const char *file_path;
+
+ if (git__suffixcmp(full_path->ptr, ".lock") == 0)
+ return 0;
+
+ if (git_fs_path_isdir(full_path->ptr)) {
+ int error = git_fs_path_direach(
+ full_path, backend->direach_flags, _dirent_loose_load, backend);
+ /* Race with the filesystem, ignore it */
+ if (error == GIT_ENOTFOUND) {
+ git_error_clear();
+ return 0;
+ }
+
+ return error;
+ }
+
+ file_path = full_path->ptr + strlen(backend->gitpath);
+
+ return loose_lookup_to_packfile(backend, file_path);
+}
+
+/*
+ * Load all the loose references from the repository
+ * into the in-memory Packfile, and build a vector with
+ * all the references so it can be written back to
+ * disk.
+ */
+static int packed_loadloose(refdb_fs_backend *backend)
+{
+ int error;
+ git_str refs_path = GIT_STR_INIT;
+
+ if (git_str_joinpath(&refs_path, backend->gitpath, GIT_REFS_DIR) < 0)
+ return -1;
+
+ /*
+ * Load all the loose files from disk into the Packfile table.
+ * This will overwrite any old packed entries with their
+ * updated loose versions
+ */
+ error = git_fs_path_direach(
+ &refs_path, backend->direach_flags, _dirent_loose_load, backend);
+
+ git_str_dispose(&refs_path);
+
+ return error;
+}
+
+static int refdb_fs_backend__exists(
+ int *exists,
+ git_refdb_backend *_backend,
+ const char *ref_name)
+{
+ refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
+ git_str ref_path = GIT_STR_INIT;
+ int error;
+
+ GIT_ASSERT_ARG(backend);
+
+ *exists = 0;
+
+ if ((error = loose_path(&ref_path, backend->gitpath, ref_name)) < 0)
+ goto out;
+
+ if (git_fs_path_isfile(ref_path.ptr)) {
+ *exists = 1;
+ goto out;
+ }
+
+ if ((error = packed_reload(backend)) < 0)
+ goto out;
+
+ if (git_sortedcache_lookup(backend->refcache, ref_name) != NULL) {
+ *exists = 1;
+ goto out;
+ }
+
+out:
+ git_str_dispose(&ref_path);
+ return error;
+}
+
+static const char *loose_parse_symbolic(git_str *file_content)
+{
+ const unsigned int header_len = (unsigned int)strlen(GIT_SYMREF);
+ const char *refname_start;
+
+ refname_start = (const char *)file_content->ptr;
+
+ if (git_str_len(file_content) < header_len + 1) {
+ git_error_set(GIT_ERROR_REFERENCE, "corrupted loose reference file");
+ return NULL;
+ }
+
+ /*
+ * Assume we have already checked for the header
+ * before calling this function
+ */
+ refname_start += header_len;
+
+ return refname_start;
+}
+
+/*
+ * Returns whether a reference is stored per worktree or not.
+ * Per-worktree references are:
+ *
+ * - all pseudorefs, e.g. HEAD and MERGE_HEAD
+ * - all references stored inside of "refs/bisect/"
+ */
+static bool is_per_worktree_ref(const char *ref_name)
+{
+ return git__prefixcmp(ref_name, "refs/") != 0 ||
+ git__prefixcmp(ref_name, "refs/bisect/") == 0;
+}
+
+static int loose_lookup(
+ git_reference **out,
+ refdb_fs_backend *backend,
+ const char *ref_name)
+{
+ git_str ref_file = GIT_STR_INIT;
+ int error = 0;
+ const char *ref_dir;
+
+ if (out)
+ *out = NULL;
+
+ if (is_per_worktree_ref(ref_name))
+ ref_dir = backend->gitpath;
+ else
+ ref_dir = backend->commonpath;
+
+ if ((error = loose_readbuffer(&ref_file, ref_dir, ref_name)) < 0)
+ /* cannot read loose ref file - gah */;
+ else if (git__prefixcmp(git_str_cstr(&ref_file), GIT_SYMREF) == 0) {
+ const char *target;
+
+ git_str_rtrim(&ref_file);
+
+ if (!(target = loose_parse_symbolic(&ref_file)))
+ error = -1;
+ else if (out != NULL)
+ *out = git_reference__alloc_symbolic(ref_name, target);
+ } else {
+ git_oid oid;
+
+ if (!(error = loose_parse_oid(&oid, ref_name, &ref_file, backend->oid_type)) &&
+ out != NULL)
+ *out = git_reference__alloc(ref_name, &oid, NULL);
+ }
+
+ git_str_dispose(&ref_file);
+ return error;
+}
+
+static int ref_error_notfound(const char *name)
+{
+ git_error_set(GIT_ERROR_REFERENCE, "reference '%s' not found", name);
+ return GIT_ENOTFOUND;
+}
+
+static char *packed_set_peeling_mode(
+ char *data,
+ size_t data_sz,
+ refdb_fs_backend *backend)
+{
+ static const char *traits_header = "# pack-refs with:";
+ char *eol;
+ backend->peeling_mode = PEELING_NONE;
+
+ if (git__prefixncmp(data, data_sz, traits_header) == 0) {
+ size_t hdr_sz = strlen(traits_header);
+ const char *sorted = " sorted ";
+ const char *peeled = " peeled ";
+ const char *fully_peeled = " fully-peeled ";
+ data += hdr_sz;
+ data_sz -= hdr_sz;
+
+ eol = memchr(data, '\n', data_sz);
+
+ if (!eol)
+ return NULL;
+
+ if (git__memmem(data, eol - data, fully_peeled, strlen(fully_peeled)))
+ backend->peeling_mode = PEELING_FULL;
+ else if (git__memmem(data, eol - data, peeled, strlen(peeled)))
+ backend->peeling_mode = PEELING_STANDARD;
+
+ backend->sorted = NULL != git__memmem(data, eol - data, sorted, strlen(sorted));
+
+ return eol + 1;
+ }
+ return data;
+}
+
+static void packed_map_free(refdb_fs_backend *backend)
+{
+ if (backend->packed_refs_map.data) {
+#ifdef GIT_WIN32
+ git__free(backend->packed_refs_map.data);
+#else
+ git_futils_mmap_free(&backend->packed_refs_map);
+#endif
+ backend->packed_refs_map.data = NULL;
+ backend->packed_refs_map.len = 0;
+ git_futils_filestamp_set(&backend->packed_refs_stamp, NULL);
+ }
+}
+
+static int packed_map_check(refdb_fs_backend *backend)
+{
+ int error = 0;
+ git_file fd = -1;
+ struct stat st;
+
+ if ((error = git_mutex_lock(&backend->prlock)) < 0)
+ return error;
+
+ if (backend->packed_refs_map.data &&
+ !git_futils_filestamp_check(
+ &backend->packed_refs_stamp, backend->refcache->path)) {
+ git_mutex_unlock(&backend->prlock);
+ return error;
+ }
+ packed_map_free(backend);
+
+ fd = git_futils_open_ro(backend->refcache->path);
+ if (fd < 0) {
+ git_mutex_unlock(&backend->prlock);
+ if (fd == GIT_ENOTFOUND) {
+ git_error_clear();
+ return 0;
+ }
+ return fd;
+ }
+
+ if (p_fstat(fd, &st) < 0) {
+ p_close(fd);
+ git_mutex_unlock(&backend->prlock);
+ git_error_set(GIT_ERROR_OS, "unable to stat packed-refs '%s'", backend->refcache->path);
+ return -1;
+ }
+
+ if (st.st_size == 0) {
+ p_close(fd);
+ git_mutex_unlock(&backend->prlock);
+ return 0;
+ }
+
+ git_futils_filestamp_set_from_stat(&backend->packed_refs_stamp, &st);
+
+#ifdef GIT_WIN32
+ /* on windows, we copy the entire file into memory rather than using
+ * mmap() because using mmap() on windows also locks the file and this
+ * map is long-lived. */
+ backend->packed_refs_map.len = (size_t)st.st_size;
+ backend->packed_refs_map.data =
+ git__malloc(backend->packed_refs_map.len);
+ GIT_ERROR_CHECK_ALLOC(backend->packed_refs_map.data);
+ {
+ ssize_t bytesread =
+ p_read(fd, backend->packed_refs_map.data,
+ backend->packed_refs_map.len);
+ error = (bytesread == (ssize_t)backend->packed_refs_map.len) ? 0 : -1;
+ }
+#else
+ error = git_futils_mmap_ro(&backend->packed_refs_map, fd, 0, (size_t)st.st_size);
+#endif
+ p_close(fd);
+ if (error < 0) {
+ git_mutex_unlock(&backend->prlock);
+ return error;
+ }
+
+ packed_set_peeling_mode(
+ backend->packed_refs_map.data, backend->packed_refs_map.len,
+ backend);
+
+ git_mutex_unlock(&backend->prlock);
+ return error;
+}
+
+/*
+ * Find beginning of packed-ref record pointed to by p.
+ * buf - a lower-bound pointer to some memory buffer
+ * p - an upper-bound pointer to the same memory buffer
+ */
+static const char *start_of_record(const char *buf, const char *p)
+{
+ const char *nl = p;
+ while (true) {
+ nl = git__memrchr(buf, '\n', nl - buf);
+ if (!nl)
+ return buf;
+
+ if (nl[1] == '^' && nl > buf)
+ --nl;
+ else
+ break;
+ };
+ return nl + 1;
+}
+
+/*
+ * Find end of packed-ref record pointed to by p.
+ * end - an upper-bound pointer to some memory buffer
+ * p - a lower-bound pointer to the same memory buffer
+ */
+static const char *end_of_record(const char *p, const char *end)
+{
+ while (1) {
+ size_t sz = end - p;
+ p = memchr(p, '\n', sz);
+ if (!p)
+ return end;
+ ++p;
+ if (p < end && p[0] == '^')
+ ++p;
+ else
+ break;
+ }
+ return p;
+}
+
+static int cmp_record_to_refname(
+ const char *rec,
+ size_t data_end,
+ const char *ref_name,
+ git_oid_t oid_type)
+{
+ const size_t ref_len = strlen(ref_name);
+ int cmp_val;
+ const char *end;
+ size_t oid_hexsize = git_oid_hexsize(oid_type);
+
+ rec += oid_hexsize + 1; /* <oid> + space */
+
+ /* an incomplete (corrupt) record is treated as less than ref_name */
+ if (data_end < oid_hexsize + 3)
+ return -1;
+
+ data_end -= oid_hexsize + 1;
+
+ end = memchr(rec, '\n', data_end);
+ if (end)
+ data_end = end - rec;
+
+ cmp_val = memcmp(rec, ref_name, min(ref_len, data_end));
+
+ if (cmp_val == 0 && data_end != ref_len)
+ return (data_end > ref_len) ? 1 : -1;
+ return cmp_val;
+}
+
+static int packed_unsorted_lookup(
+ git_reference **out,
+ refdb_fs_backend *backend,
+ const char *ref_name)
+{
+ int error = 0;
+ struct packref *entry;
+
+ if ((error = packed_reload(backend)) < 0)
+ return error;
+
+ if (git_sortedcache_rlock(backend->refcache) < 0)
+ return -1;
+
+ entry = git_sortedcache_lookup(backend->refcache, ref_name);
+ if (!entry) {
+ error = ref_error_notfound(ref_name);
+ } else {
+ *out = git_reference__alloc(ref_name, &entry->oid, &entry->peel);
+ if (!*out)
+ error = -1;
+ }
+
+ git_sortedcache_runlock(backend->refcache);
+
+ return error;
+}
+
+static int packed_lookup(
+ git_reference **out,
+ refdb_fs_backend *backend,
+ const char *ref_name)
+{
+ int error = 0;
+ const char *left, *right, *data_end;
+ size_t oid_hexsize = git_oid_hexsize(backend->oid_type);
+
+ if ((error = packed_map_check(backend)) < 0)
+ return error;
+
+ if (!backend->sorted)
+ return packed_unsorted_lookup(out, backend, ref_name);
+
+ left = backend->packed_refs_map.data;
+ right = data_end = (const char *) backend->packed_refs_map.data +
+ backend->packed_refs_map.len;
+
+ while (left < right && *left == '#') {
+ if (!(left = memchr(left, '\n', data_end - left)))
+ goto parse_failed;
+ left++;
+ }
+
+ while (left < right) {
+ const char *mid, *rec;
+ int compare;
+
+ mid = left + (right - left) / 2;
+ rec = start_of_record(left, mid);
+ compare = cmp_record_to_refname(rec, data_end - rec, ref_name, backend->oid_type);
+
+ if (compare < 0) {
+ left = end_of_record(mid, right);
+ } else if (compare > 0) {
+ right = rec;
+ } else {
+ const char *eol;
+ git_oid oid, peel, *peel_ptr = NULL;
+
+ if (data_end - rec < (long)oid_hexsize ||
+ git_oid__fromstr(&oid, rec, backend->oid_type) < 0) {
+ goto parse_failed;
+ }
+ rec += oid_hexsize + 1;
+ if (!(eol = memchr(rec, '\n', data_end - rec))) {
+ goto parse_failed;
+ }
+
+ /* look for optional "^<OID>\n" */
+
+ if (eol + 1 < data_end) {
+ rec = eol + 1;
+
+ if (*rec == '^') {
+ rec++;
+ if (data_end - rec < (long)oid_hexsize ||
+ git_oid__fromstr(&peel, rec, backend->oid_type) < 0) {
+ goto parse_failed;
+ }
+ peel_ptr = &peel;
+ }
+ }
+
+ *out = git_reference__alloc(ref_name, &oid, peel_ptr);
+ if (!*out) {
+ return -1;
+ }
+
+ return 0;
+ }
+ }
+ return ref_error_notfound(ref_name);
+
+parse_failed:
+ git_error_set(GIT_ERROR_REFERENCE, "corrupted packed references file");
+ return -1;
+}
+
+static int refdb_fs_backend__lookup(
+ git_reference **out,
+ git_refdb_backend *_backend,
+ const char *ref_name)
+{
+ refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
+ int error;
+
+ GIT_ASSERT_ARG(backend);
+
+ if (!(error = loose_lookup(out, backend, ref_name)))
+ return 0;
+
+ /* only try to lookup this reference on the packfile if it
+ * wasn't found on the loose refs; not if there was a critical error */
+ if (error == GIT_ENOTFOUND) {
+ git_error_clear();
+ error = packed_lookup(out, backend, ref_name);
+ }
+ return error;
+}
+
+typedef struct {
+ git_reference_iterator parent;
+
+ char *glob;
+
+ git_pool pool;
+ git_vector loose;
+
+ git_sortedcache *cache;
+ size_t loose_pos;
+ size_t packed_pos;
+} refdb_fs_iter;
+
+static void refdb_fs_backend__iterator_free(git_reference_iterator *_iter)
+{
+ refdb_fs_iter *iter = GIT_CONTAINER_OF(_iter, refdb_fs_iter, parent);
+
+ git_vector_free(&iter->loose);
+ git_pool_clear(&iter->pool);
+ git_sortedcache_free(iter->cache);
+ git__free(iter);
+}
+
+static int iter_load_loose_paths(
+ refdb_fs_backend *backend,
+ refdb_fs_iter *iter)
+{
+ int error = 0;
+ git_str path = GIT_STR_INIT;
+ git_iterator *fsit = NULL;
+ git_iterator_options fsit_opts = GIT_ITERATOR_OPTIONS_INIT;
+ const git_index_entry *entry = NULL;
+ const char *ref_prefix = GIT_REFS_DIR;
+ size_t ref_prefix_len = strlen(ref_prefix);
+
+ if (!backend->commonpath) /* do nothing if no commonpath for loose refs */
+ return 0;
+
+ fsit_opts.flags = backend->iterator_flags;
+ fsit_opts.oid_type = backend->oid_type;
+
+ if (iter->glob) {
+ const char *last_sep = NULL;
+ const char *pos;
+ for (pos = iter->glob; *pos; ++pos) {
+ switch (*pos) {
+ case '?':
+ case '*':
+ case '[':
+ case '\\':
+ break;
+ case '/':
+ last_sep = pos;
+ /* FALLTHROUGH */
+ default:
+ continue;
+ }
+ break;
+ }
+ if (last_sep) {
+ ref_prefix = iter->glob;
+ ref_prefix_len = (last_sep - ref_prefix) + 1;
+ }
+ }
+
+ if ((error = git_str_puts(&path, backend->commonpath)) < 0 ||
+ (error = git_str_put(&path, ref_prefix, ref_prefix_len)) < 0) {
+ git_str_dispose(&path);
+ return error;
+ }
+
+ if ((error = git_iterator_for_filesystem(&fsit, path.ptr, &fsit_opts)) < 0) {
+ git_str_dispose(&path);
+ return (iter->glob && error == GIT_ENOTFOUND)? 0 : error;
+ }
+
+ error = git_str_sets(&path, ref_prefix);
+
+ while (!error && !git_iterator_advance(&entry, fsit)) {
+ const char *ref_name;
+ char *ref_dup;
+
+ git_str_truncate(&path, ref_prefix_len);
+ git_str_puts(&path, entry->path);
+ ref_name = git_str_cstr(&path);
+
+ if (git__suffixcmp(ref_name, ".lock") == 0 ||
+ (iter->glob && wildmatch(iter->glob, ref_name, 0) != 0))
+ continue;
+
+ ref_dup = git_pool_strdup(&iter->pool, ref_name);
+ if (!ref_dup)
+ error = -1;
+ else
+ error = git_vector_insert(&iter->loose, ref_dup);
+ }
+
+ git_iterator_free(fsit);
+ git_str_dispose(&path);
+
+ return error;
+}
+
+static int refdb_fs_backend__iterator_next(
+ git_reference **out, git_reference_iterator *_iter)
+{
+ int error = GIT_ITEROVER;
+ refdb_fs_iter *iter = GIT_CONTAINER_OF(_iter, refdb_fs_iter, parent);
+ refdb_fs_backend *backend = GIT_CONTAINER_OF(iter->parent.db->backend, refdb_fs_backend, parent);
+ struct packref *ref;
+
+ while (iter->loose_pos < iter->loose.length) {
+ const char *path = git_vector_get(&iter->loose, iter->loose_pos++);
+
+ if (loose_lookup(out, backend, path) == 0) {
+ ref = git_sortedcache_lookup(iter->cache, path);
+ if (ref)
+ ref->flags |= PACKREF_SHADOWED;
+
+ return 0;
+ }
+
+ git_error_clear();
+ }
+
+ error = GIT_ITEROVER;
+ while (iter->packed_pos < git_sortedcache_entrycount(iter->cache)) {
+ ref = git_sortedcache_entry(iter->cache, iter->packed_pos++);
+ if (!ref) /* stop now if another thread deleted refs and we past end */
+ break;
+
+ if (ref->flags & PACKREF_SHADOWED)
+ continue;
+ if (iter->glob && wildmatch(iter->glob, ref->name, 0) != 0)
+ continue;
+
+ *out = git_reference__alloc(ref->name, &ref->oid, &ref->peel);
+ error = (*out != NULL) ? 0 : -1;
+ break;
+ }
+
+ return error;
+}
+
+static int refdb_fs_backend__iterator_next_name(
+ const char **out, git_reference_iterator *_iter)
+{
+ int error = GIT_ITEROVER;
+ refdb_fs_iter *iter = GIT_CONTAINER_OF(_iter, refdb_fs_iter, parent);
+ refdb_fs_backend *backend = GIT_CONTAINER_OF(iter->parent.db->backend, refdb_fs_backend, parent);
+ struct packref *ref;
+
+ while (iter->loose_pos < iter->loose.length) {
+ const char *path = git_vector_get(&iter->loose, iter->loose_pos++);
+ struct packref *ref;
+
+ if (loose_lookup(NULL, backend, path) == 0) {
+ ref = git_sortedcache_lookup(iter->cache, path);
+ if (ref)
+ ref->flags |= PACKREF_SHADOWED;
+
+ *out = path;
+ return 0;
+ }
+
+ git_error_clear();
+ }
+
+ error = GIT_ITEROVER;
+ while (iter->packed_pos < git_sortedcache_entrycount(iter->cache)) {
+ ref = git_sortedcache_entry(iter->cache, iter->packed_pos++);
+ if (!ref) /* stop now if another thread deleted refs and we past end */
+ break;
+
+ if (ref->flags & PACKREF_SHADOWED)
+ continue;
+ if (iter->glob && wildmatch(iter->glob, ref->name, 0) != 0)
+ continue;
+
+ *out = ref->name;
+ error = 0;
+ break;
+ }
+
+ return error;
+}
+
+static int refdb_fs_backend__iterator(
+ git_reference_iterator **out, git_refdb_backend *_backend, const char *glob)
+{
+ refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
+ refdb_fs_iter *iter = NULL;
+ int error;
+
+ GIT_ASSERT_ARG(backend);
+
+ iter = git__calloc(1, sizeof(refdb_fs_iter));
+ GIT_ERROR_CHECK_ALLOC(iter);
+
+ if ((error = git_pool_init(&iter->pool, 1)) < 0)
+ goto out;
+
+ if ((error = git_vector_init(&iter->loose, 8, NULL)) < 0)
+ goto out;
+
+ if (glob != NULL &&
+ (iter->glob = git_pool_strdup(&iter->pool, glob)) == NULL) {
+ error = GIT_ERROR_NOMEMORY;
+ goto out;
+ }
+
+ if ((error = iter_load_loose_paths(backend, iter)) < 0)
+ goto out;
+
+ if ((error = packed_reload(backend)) < 0)
+ goto out;
+
+ if ((error = git_sortedcache_copy(&iter->cache, backend->refcache, 1, NULL, NULL)) < 0)
+ goto out;
+
+ iter->parent.next = refdb_fs_backend__iterator_next;
+ iter->parent.next_name = refdb_fs_backend__iterator_next_name;
+ iter->parent.free = refdb_fs_backend__iterator_free;
+
+ *out = (git_reference_iterator *)iter;
+out:
+ if (error)
+ refdb_fs_backend__iterator_free((git_reference_iterator *)iter);
+ return error;
+}
+
+static bool ref_is_available(
+ const char *old_ref, const char *new_ref, const char *this_ref)
+{
+ if (old_ref == NULL || strcmp(old_ref, this_ref)) {
+ size_t reflen = strlen(this_ref);
+ size_t newlen = strlen(new_ref);
+ size_t cmplen = reflen < newlen ? reflen : newlen;
+ const char *lead = reflen < newlen ? new_ref : this_ref;
+
+ if (!strncmp(new_ref, this_ref, cmplen) && lead[cmplen] == '/') {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+static int reference_path_available(
+ refdb_fs_backend *backend,
+ const char *new_ref,
+ const char *old_ref,
+ int force)
+{
+ size_t i;
+ int error;
+
+ if ((error = packed_reload(backend)) < 0)
+ return error;
+
+ if (!force) {
+ int exists;
+
+ if ((error = refdb_fs_backend__exists(
+ &exists, (git_refdb_backend *)backend, new_ref)) < 0) {
+ return error;
+ }
+
+ if (exists) {
+ git_error_set(GIT_ERROR_REFERENCE,
+ "failed to write reference '%s': a reference with "
+ "that name already exists.", new_ref);
+ return GIT_EEXISTS;
+ }
+ }
+
+ if ((error = git_sortedcache_rlock(backend->refcache)) < 0)
+ return error;
+
+ for (i = 0; i < git_sortedcache_entrycount(backend->refcache); ++i) {
+ struct packref *ref = git_sortedcache_entry(backend->refcache, i);
+
+ if (ref && !ref_is_available(old_ref, new_ref, ref->name)) {
+ git_sortedcache_runlock(backend->refcache);
+ git_error_set(GIT_ERROR_REFERENCE,
+ "path to reference '%s' collides with existing one", new_ref);
+ return -1;
+ }
+ }
+
+ git_sortedcache_runlock(backend->refcache);
+ return 0;
+}
+
+static int loose_lock(git_filebuf *file, refdb_fs_backend *backend, const char *name)
+{
+ int error, filebuf_flags;
+ git_str ref_path = GIT_STR_INIT;
+ const char *basedir;
+
+ GIT_ASSERT_ARG(file);
+ GIT_ASSERT_ARG(backend);
+ GIT_ASSERT_ARG(name);
+
+ if (!git_path_is_valid(backend->repo, name, 0, GIT_FS_PATH_REJECT_FILESYSTEM_DEFAULTS)) {
+ git_error_set(GIT_ERROR_INVALID, "invalid reference name '%s'", name);
+ return GIT_EINVALIDSPEC;
+ }
+
+ if (is_per_worktree_ref(name))
+ basedir = backend->gitpath;
+ else
+ basedir = backend->commonpath;
+
+ /* Remove a possibly existing empty directory hierarchy
+ * which name would collide with the reference name
+ */
+ if ((error = git_futils_rmdir_r(name, basedir, GIT_RMDIR_SKIP_NONEMPTY)) < 0)
+ return error;
+
+ if ((error = loose_path(&ref_path, basedir, name)) < 0)
+ return error;
+
+ filebuf_flags = GIT_FILEBUF_CREATE_LEADING_DIRS;
+ if (backend->fsync)
+ filebuf_flags |= GIT_FILEBUF_FSYNC;
+
+ error = git_filebuf_open(file, ref_path.ptr, filebuf_flags, GIT_REFS_FILE_MODE);
+
+ if (error == GIT_EDIRECTORY)
+ git_error_set(GIT_ERROR_REFERENCE, "cannot lock ref '%s', there are refs beneath that folder", name);
+
+ git_str_dispose(&ref_path);
+ return error;
+}
+
+static int loose_commit(git_filebuf *file, const git_reference *ref)
+{
+ GIT_ASSERT_ARG(file);
+ GIT_ASSERT_ARG(ref);
+
+ if (ref->type == GIT_REFERENCE_DIRECT) {
+ char oid[GIT_OID_MAX_HEXSIZE + 1];
+ git_oid_nfmt(oid, sizeof(oid), &ref->target.oid);
+
+ git_filebuf_printf(file, "%s\n", oid);
+ } else if (ref->type == GIT_REFERENCE_SYMBOLIC) {
+ git_filebuf_printf(file, GIT_SYMREF "%s\n", ref->target.symbolic);
+ } else {
+ GIT_ASSERT(0);
+ }
+
+ return git_filebuf_commit(file);
+}
+
+static int refdb_fs_backend__lock(void **out, git_refdb_backend *_backend, const char *refname)
+{
+ int error;
+ git_filebuf *lock;
+ refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
+
+ lock = git__calloc(1, sizeof(git_filebuf));
+ GIT_ERROR_CHECK_ALLOC(lock);
+
+ if ((error = loose_lock(lock, backend, refname)) < 0) {
+ git__free(lock);
+ return error;
+ }
+
+ *out = lock;
+ return 0;
+}
+
+static int refdb_fs_backend__write_tail(
+ git_refdb_backend *_backend,
+ const git_reference *ref,
+ git_filebuf *file,
+ int update_reflog,
+ const git_oid *old_id,
+ const char *old_target,
+ const git_signature *who,
+ const char *message);
+
+static int refdb_fs_backend__delete_tail(
+ git_refdb_backend *_backend,
+ git_filebuf *file,
+ const char *ref_name,
+ const git_oid *old_id,
+ const char *old_target);
+
+static int refdb_fs_backend__unlock(git_refdb_backend *backend, void *payload, int success, int update_reflog,
+ const git_reference *ref, const git_signature *sig, const char *message)
+{
+ git_filebuf *lock = (git_filebuf *) payload;
+ int error = 0;
+
+ if (success == 2)
+ error = refdb_fs_backend__delete_tail(backend, lock, ref->name, NULL, NULL);
+ else if (success)
+ error = refdb_fs_backend__write_tail(backend, ref, lock, update_reflog, NULL, NULL, sig, message);
+ else
+ git_filebuf_cleanup(lock);
+
+ git__free(lock);
+ return error;
+}
+
+/*
+ * Find out what object this reference resolves to.
+ *
+ * For references that point to a 'big' tag (e.g. an
+ * actual tag object on the repository), we need to
+ * cache on the packfile the OID of the object to
+ * which that 'big tag' is pointing to.
+ */
+static int packed_find_peel(refdb_fs_backend *backend, struct packref *ref)
+{
+ git_object *object;
+
+ if (ref->flags & PACKREF_HAS_PEEL || ref->flags & PACKREF_CANNOT_PEEL)
+ return 0;
+
+ /*
+ * Find the tagged object in the repository
+ */
+ if (git_object_lookup(&object, backend->repo, &ref->oid, GIT_OBJECT_ANY) < 0)
+ return -1;
+
+ /*
+ * If the tagged object is a Tag object, we need to resolve it;
+ * if the ref is actually a 'weak' ref, we don't need to resolve
+ * anything.
+ */
+ if (git_object_type(object) == GIT_OBJECT_TAG) {
+ git_tag *tag = (git_tag *)object;
+
+ /*
+ * Find the object pointed at by this tag
+ */
+ git_oid_cpy(&ref->peel, git_tag_target_id(tag));
+ ref->flags |= PACKREF_HAS_PEEL;
+
+ /*
+ * The reference has now cached the resolved OID, and is
+ * marked at such. When written to the packfile, it'll be
+ * accompanied by this resolved oid
+ */
+ }
+
+ git_object_free(object);
+ return 0;
+}
+
+/*
+ * Write a single reference into a packfile
+ */
+static int packed_write_ref(struct packref *ref, git_filebuf *file)
+{
+ char oid[GIT_OID_MAX_HEXSIZE + 1];
+ git_oid_nfmt(oid, sizeof(oid), &ref->oid);
+
+ /*
+ * For references that peel to an object in the repo, we must
+ * write the resulting peel on a separate line, e.g.
+ *
+ * 6fa8a902cc1d18527e1355773c86721945475d37 refs/tags/libgit2-0.4
+ * ^2ec0cb7959b0bf965d54f95453f5b4b34e8d3100
+ *
+ * This obviously only applies to tags.
+ * The required peels have already been loaded into `ref->peel_target`.
+ */
+ if (ref->flags & PACKREF_HAS_PEEL) {
+ char peel[GIT_OID_MAX_HEXSIZE + 1];
+ git_oid_nfmt(peel, sizeof(peel), &ref->peel);
+
+ if (git_filebuf_printf(file, "%s %s\n^%s\n", oid, ref->name, peel) < 0)
+ return -1;
+ } else {
+ if (git_filebuf_printf(file, "%s %s\n", oid, ref->name) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Remove all loose references
+ *
+ * Once we have successfully written a packfile,
+ * all the loose references that were packed must be
+ * removed from disk.
+ *
+ * This is a dangerous method; make sure the packfile
+ * is well-written, because we are destructing references
+ * here otherwise.
+ */
+static int packed_remove_loose(refdb_fs_backend *backend)
+{
+ size_t i;
+ git_filebuf lock = GIT_FILEBUF_INIT;
+ git_str ref_content = GIT_STR_INIT;
+ int error = 0;
+
+ /* backend->refcache is already locked when this is called */
+
+ for (i = 0; i < git_sortedcache_entrycount(backend->refcache); ++i) {
+ struct packref *ref = git_sortedcache_entry(backend->refcache, i);
+ git_oid current_id;
+
+ if (!ref || !(ref->flags & PACKREF_WAS_LOOSE))
+ continue;
+
+ git_filebuf_cleanup(&lock);
+
+ /* We need to stop anybody from updating the ref while we try to do a safe delete */
+ error = loose_lock(&lock, backend, ref->name);
+ /* If someone else is updating it, let them do it */
+ if (error == GIT_EEXISTS || error == GIT_ENOTFOUND)
+ continue;
+
+ if (error < 0) {
+ git_str_dispose(&ref_content);
+ git_error_set(GIT_ERROR_REFERENCE, "failed to lock loose reference '%s'", ref->name);
+ return error;
+ }
+
+ error = git_futils_readbuffer(&ref_content, lock.path_original);
+ /* Someone else beat us to cleaning up the ref, let's simply continue */
+ if (error == GIT_ENOTFOUND)
+ continue;
+
+ /* This became a symref between us packing and trying to delete it, so ignore it */
+ if (!git__prefixcmp(ref_content.ptr, GIT_SYMREF))
+ continue;
+
+ /* Figure out the current id; if we find a bad ref file, skip it so we can do the rest */
+ if (loose_parse_oid(&current_id, lock.path_original, &ref_content, backend->oid_type) < 0)
+ continue;
+
+ /* If the ref moved since we packed it, we must not delete it */
+ if (!git_oid_equal(&current_id, &ref->oid))
+ continue;
+
+ /*
+ * if we fail to remove a single file, this is *not* good,
+ * but we should keep going and remove as many as possible.
+ * If we fail to remove, the ref is still in the old state, so
+ * we haven't lost information.
+ */
+ p_unlink(lock.path_original);
+ }
+
+ git_str_dispose(&ref_content);
+ git_filebuf_cleanup(&lock);
+ return 0;
+}
+
+/*
+ * Write all the contents in the in-memory packfile to disk.
+ */
+static int packed_write(refdb_fs_backend *backend)
+{
+ git_sortedcache *refcache = backend->refcache;
+ git_filebuf pack_file = GIT_FILEBUF_INIT;
+ int error, open_flags = 0;
+ size_t i;
+
+ /* take lock and close up packed-refs mmap if open */
+ if ((error = git_mutex_lock(&backend->prlock)) < 0) {
+ return error;
+ }
+
+ packed_map_free(backend);
+
+ git_mutex_unlock(&backend->prlock);
+
+ /* lock the cache to updates while we do this */
+ if ((error = git_sortedcache_wlock(refcache)) < 0)
+ return error;
+
+ if (backend->fsync)
+ open_flags = GIT_FILEBUF_FSYNC;
+
+ /* Open the file! */
+ if ((error = git_filebuf_open(&pack_file, git_sortedcache_path(refcache), open_flags, GIT_PACKEDREFS_FILE_MODE)) < 0)
+ goto fail;
+
+ /* Packfiles have a header... apparently
+ * This is in fact not required, but we might as well print it
+ * just for kicks */
+ if ((error = git_filebuf_printf(&pack_file, "%s\n", GIT_PACKEDREFS_HEADER)) < 0)
+ goto fail;
+
+ for (i = 0; i < git_sortedcache_entrycount(refcache); ++i) {
+ struct packref *ref = git_sortedcache_entry(refcache, i);
+
+ GIT_ASSERT_WITH_CLEANUP(ref, {
+ error = -1;
+ goto fail;
+ });
+
+ if ((error = packed_find_peel(backend, ref)) < 0)
+ goto fail;
+
+ if ((error = packed_write_ref(ref, &pack_file)) < 0)
+ goto fail;
+ }
+
+ /* if we've written all the references properly, we can commit
+ * the packfile to make the changes effective */
+ if ((error = git_filebuf_commit(&pack_file)) < 0)
+ goto fail;
+
+ /* when and only when the packfile has been properly written,
+ * we can go ahead and remove the loose refs */
+ if ((error = packed_remove_loose(backend)) < 0)
+ goto fail;
+
+ git_sortedcache_updated(refcache);
+ git_sortedcache_wunlock(refcache);
+
+ /* we're good now */
+ return 0;
+
+fail:
+ git_filebuf_cleanup(&pack_file);
+ git_sortedcache_wunlock(refcache);
+
+ return error;
+}
+
+static int packed_delete(refdb_fs_backend *backend, const char *ref_name)
+{
+ size_t pack_pos;
+ int error, found = 0;
+
+ if ((error = packed_reload(backend)) < 0)
+ goto cleanup;
+
+ if ((error = git_sortedcache_wlock(backend->refcache)) < 0)
+ goto cleanup;
+
+ /* If a packed reference exists, remove it from the packfile and repack if necessary */
+ error = git_sortedcache_lookup_index(&pack_pos, backend->refcache, ref_name);
+ if (error == 0) {
+ error = git_sortedcache_remove(backend->refcache, pack_pos);
+ found = 1;
+ }
+ if (error == GIT_ENOTFOUND)
+ error = 0;
+
+ git_sortedcache_wunlock(backend->refcache);
+
+ if (found)
+ error = packed_write(backend);
+
+cleanup:
+ return error;
+}
+
+static int reflog_append(refdb_fs_backend *backend, const git_reference *ref, const git_oid *old, const git_oid *new, const git_signature *author, const char *message);
+
+static int cmp_old_ref(int *cmp, git_refdb_backend *backend, const char *name,
+ const git_oid *old_id, const char *old_target)
+{
+ int error = 0;
+ git_reference *old_ref = NULL;
+
+ *cmp = 0;
+ /* It "matches" if there is no old value to compare against */
+ if (!old_id && !old_target)
+ return 0;
+
+ if ((error = refdb_fs_backend__lookup(&old_ref, backend, name)) < 0) {
+ if (error == GIT_ENOTFOUND && old_id && git_oid_is_zero(old_id))
+ return 0;
+ goto out;
+ }
+
+ /* If the types don't match, there's no way the values do */
+ if (old_id && old_ref->type != GIT_REFERENCE_DIRECT) {
+ *cmp = -1;
+ goto out;
+ }
+ if (old_target && old_ref->type != GIT_REFERENCE_SYMBOLIC) {
+ *cmp = 1;
+ goto out;
+ }
+
+ if (old_id && old_ref->type == GIT_REFERENCE_DIRECT)
+ *cmp = git_oid_cmp(old_id, &old_ref->target.oid);
+
+ if (old_target && old_ref->type == GIT_REFERENCE_SYMBOLIC)
+ *cmp = git__strcmp(old_target, old_ref->target.symbolic);
+
+out:
+ git_reference_free(old_ref);
+
+ return error;
+}
+
+/*
+ * The git.git comment regarding this, for your viewing pleasure:
+ *
+ * Special hack: If a branch is updated directly and HEAD
+ * points to it (may happen on the remote side of a push
+ * for example) then logically the HEAD reflog should be
+ * updated too.
+ * A generic solution implies reverse symref information,
+ * but finding all symrefs pointing to the given branch
+ * would be rather costly for this rare event (the direct
+ * update of a branch) to be worth it. So let's cheat and
+ * check with HEAD only which should cover 99% of all usage
+ * scenarios (even 100% of the default ones).
+ */
+static int maybe_append_head(refdb_fs_backend *backend, const git_reference *ref, const git_signature *who, const char *message)
+{
+ git_reference *head = NULL;
+ git_refdb *refdb = NULL;
+ int error, write_reflog;
+ git_oid old_id;
+
+ if ((error = git_repository_refdb(&refdb, backend->repo)) < 0 ||
+ (error = git_refdb_should_write_head_reflog(&write_reflog, refdb, ref)) < 0)
+ goto out;
+ if (!write_reflog)
+ goto out;
+
+ /* if we can't resolve, we use {0}*40 as old id */
+ if (git_reference_name_to_id(&old_id, backend->repo, ref->name) < 0)
+ memset(&old_id, 0, sizeof(old_id));
+
+ if ((error = git_reference_lookup(&head, backend->repo, GIT_HEAD_FILE)) < 0 ||
+ (error = reflog_append(backend, head, &old_id, git_reference_target(ref), who, message)) < 0)
+ goto out;
+
+out:
+ git_reference_free(head);
+ git_refdb_free(refdb);
+ return error;
+}
+
+static int refdb_fs_backend__write(
+ git_refdb_backend *_backend,
+ const git_reference *ref,
+ int force,
+ const git_signature *who,
+ const char *message,
+ const git_oid *old_id,
+ const char *old_target)
+{
+ refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
+ git_filebuf file = GIT_FILEBUF_INIT;
+ int error = 0;
+
+ GIT_ASSERT_ARG(backend);
+
+ if ((error = reference_path_available(backend, ref->name, NULL, force)) < 0)
+ return error;
+
+ /* We need to perform the reflog append and old value check under the ref's lock */
+ if ((error = loose_lock(&file, backend, ref->name)) < 0)
+ return error;
+
+ return refdb_fs_backend__write_tail(_backend, ref, &file, true, old_id, old_target, who, message);
+}
+
+static int refdb_fs_backend__write_tail(
+ git_refdb_backend *_backend,
+ const git_reference *ref,
+ git_filebuf *file,
+ int update_reflog,
+ const git_oid *old_id,
+ const char *old_target,
+ const git_signature *who,
+ const char *message)
+{
+ refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
+ int error = 0, cmp = 0, should_write;
+ const char *new_target = NULL;
+ const git_oid *new_id = NULL;
+
+ if ((error = cmp_old_ref(&cmp, _backend, ref->name, old_id, old_target)) < 0)
+ goto on_error;
+
+ if (cmp) {
+ git_error_set(GIT_ERROR_REFERENCE, "old reference value does not match");
+ error = GIT_EMODIFIED;
+ goto on_error;
+ }
+
+ if (ref->type == GIT_REFERENCE_SYMBOLIC)
+ new_target = ref->target.symbolic;
+ else
+ new_id = &ref->target.oid;
+
+ error = cmp_old_ref(&cmp, _backend, ref->name, new_id, new_target);
+ if (error < 0 && error != GIT_ENOTFOUND)
+ goto on_error;
+
+ /* Don't update if we have the same value */
+ if (!error && !cmp) {
+ error = 0;
+ goto on_error; /* not really error */
+ }
+
+ if (update_reflog) {
+ git_refdb *refdb;
+
+ if ((error = git_repository_refdb__weakptr(&refdb, backend->repo)) < 0 ||
+ (error = git_refdb_should_write_reflog(&should_write, refdb, ref)) < 0)
+ goto on_error;
+
+ if (should_write) {
+ if ((error = reflog_append(backend, ref, NULL, NULL, who, message)) < 0)
+ goto on_error;
+ if ((error = maybe_append_head(backend, ref, who, message)) < 0)
+ goto on_error;
+ }
+ }
+
+ return loose_commit(file, ref);
+
+on_error:
+ git_filebuf_cleanup(file);
+ return error;
+}
+
+static int refdb_fs_backend__prune_refs(
+ refdb_fs_backend *backend,
+ const char *ref_name,
+ const char *prefix)
+{
+ git_str relative_path = GIT_STR_INIT;
+ git_str base_path = GIT_STR_INIT;
+ size_t commonlen;
+ int error;
+
+ GIT_ASSERT_ARG(backend);
+ GIT_ASSERT_ARG(ref_name);
+
+ if ((error = git_str_sets(&relative_path, ref_name)) < 0)
+ goto cleanup;
+
+ git_fs_path_squash_slashes(&relative_path);
+ if ((commonlen = git_fs_path_common_dirlen("refs/heads/", git_str_cstr(&relative_path))) == strlen("refs/heads/") ||
+ (commonlen = git_fs_path_common_dirlen("refs/tags/", git_str_cstr(&relative_path))) == strlen("refs/tags/") ||
+ (commonlen = git_fs_path_common_dirlen("refs/remotes/", git_str_cstr(&relative_path))) == strlen("refs/remotes/")) {
+
+ git_str_truncate(&relative_path, commonlen);
+
+ if (prefix)
+ error = git_str_join3(&base_path, '/',
+ backend->commonpath, prefix,
+ git_str_cstr(&relative_path));
+ else
+ error = git_str_joinpath(&base_path,
+ backend->commonpath,
+ git_str_cstr(&relative_path));
+
+ if (!error)
+ error = git_path_validate_str_length(NULL, &base_path);
+
+ if (error < 0)
+ goto cleanup;
+
+ error = git_futils_rmdir_r(ref_name + commonlen,
+ git_str_cstr(&base_path),
+ GIT_RMDIR_EMPTY_PARENTS | GIT_RMDIR_SKIP_ROOT);
+
+ if (error == GIT_ENOTFOUND)
+ error = 0;
+ }
+
+cleanup:
+ git_str_dispose(&relative_path);
+ git_str_dispose(&base_path);
+ return error;
+}
+
+static int refdb_fs_backend__delete(
+ git_refdb_backend *_backend,
+ const char *ref_name,
+ const git_oid *old_id, const char *old_target)
+{
+ refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
+ git_filebuf file = GIT_FILEBUF_INIT;
+ int error = 0;
+
+ GIT_ASSERT_ARG(backend);
+ GIT_ASSERT_ARG(ref_name);
+
+ if ((error = loose_lock(&file, backend, ref_name)) < 0)
+ return error;
+
+ if ((error = refdb_reflog_fs__delete(_backend, ref_name)) < 0) {
+ git_filebuf_cleanup(&file);
+ return error;
+ }
+
+ return refdb_fs_backend__delete_tail(_backend, &file, ref_name, old_id, old_target);
+}
+
+static int loose_delete(refdb_fs_backend *backend, const char *ref_name)
+{
+ git_str path = GIT_STR_INIT;
+ int error = 0;
+
+ if ((error = loose_path(&path, backend->commonpath, ref_name)) < 0)
+ return error;
+
+ error = p_unlink(path.ptr);
+ if (error < 0 && errno == ENOENT)
+ error = GIT_ENOTFOUND;
+ else if (error != 0)
+ error = -1;
+
+ git_str_dispose(&path);
+
+ return error;
+}
+
+static int refdb_fs_backend__delete_tail(
+ git_refdb_backend *_backend,
+ git_filebuf *file,
+ const char *ref_name,
+ const git_oid *old_id, const char *old_target)
+{
+ refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
+ int error = 0, cmp = 0;
+ bool packed_deleted = 0;
+
+ error = cmp_old_ref(&cmp, _backend, ref_name, old_id, old_target);
+ if (error < 0)
+ goto cleanup;
+
+ if (cmp) {
+ git_error_set(GIT_ERROR_REFERENCE, "old reference value does not match");
+ error = GIT_EMODIFIED;
+ goto cleanup;
+ }
+
+ /*
+ * To ensure that an external observer will see either the current ref value
+ * (because the loose ref still exists), or a missing ref (after the packed-file is
+ * unlocked, there will be nothing left), we must ensure things happen in the
+ * following order:
+ *
+ * - the packed-ref file is locked and loaded, as well as a loose one, if it exists
+ * - we optimistically delete a packed ref, keeping track of whether it existed
+ * - we delete the loose ref, note that we have its .lock
+ * - the loose ref is "unlocked", then the packed-ref file is rewritten and unlocked
+ * - we should prune the path components if a loose ref was deleted
+ *
+ * Note that, because our packed backend doesn't expose its filesystem lock,
+ * we might not be able to guarantee that this is what actually happens (ie.
+ * as our current code never write packed-refs.lock, nothing stops observers
+ * from grabbing a "stale" value from there).
+ */
+ if ((error = packed_delete(backend, ref_name)) < 0 && error != GIT_ENOTFOUND)
+ goto cleanup;
+
+ if (error == 0)
+ packed_deleted = 1;
+
+ if ((error = loose_delete(backend, ref_name)) < 0 && error != GIT_ENOTFOUND)
+ goto cleanup;
+
+ if (error == GIT_ENOTFOUND) {
+ error = packed_deleted ? 0 : ref_error_notfound(ref_name);
+ goto cleanup;
+ }
+
+cleanup:
+ git_filebuf_cleanup(file);
+ if (error == 0)
+ error = refdb_fs_backend__prune_refs(backend, ref_name, "");
+ return error;
+}
+
+static int refdb_reflog_fs__rename(git_refdb_backend *_backend, const char *old_name, const char *new_name);
+
+static int refdb_fs_backend__rename(
+ git_reference **out,
+ git_refdb_backend *_backend,
+ const char *old_name,
+ const char *new_name,
+ int force,
+ const git_signature *who,
+ const char *message)
+{
+ refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
+ git_reference *old, *new = NULL;
+ git_filebuf file = GIT_FILEBUF_INIT;
+ int error;
+
+ GIT_ASSERT_ARG(backend);
+
+ if ((error = reference_path_available(
+ backend, new_name, old_name, force)) < 0 ||
+ (error = refdb_fs_backend__lookup(&old, _backend, old_name)) < 0)
+ return error;
+
+ if ((error = loose_lock(&file, backend, old->name)) < 0) {
+ git_reference_free(old);
+ return error;
+ }
+
+ new = git_reference__realloc(&old, new_name);
+ if (!new) {
+ git_reference_free(old);
+ git_filebuf_cleanup(&file);
+ return -1;
+ }
+
+ if ((error = refdb_fs_backend__delete_tail(_backend, &file, old_name, NULL, NULL)) < 0) {
+ git_reference_free(new);
+ git_filebuf_cleanup(&file);
+ return error;
+ }
+
+ if ((error = loose_lock(&file, backend, new_name)) < 0) {
+ git_reference_free(new);
+ return error;
+ }
+
+ /* Try to rename the refog; it's ok if the old doesn't exist */
+ error = refdb_reflog_fs__rename(_backend, old_name, new_name);
+ if (((error == 0) || (error == GIT_ENOTFOUND)) &&
+ ((error = reflog_append(backend, new, git_reference_target(new), NULL, who, message)) < 0)) {
+ git_reference_free(new);
+ git_filebuf_cleanup(&file);
+ return error;
+ }
+
+ if ((error = loose_commit(&file, new)) < 0 || out == NULL) {
+ git_reference_free(new);
+ git_filebuf_cleanup(&file);
+ return error;
+ }
+
+ *out = new;
+ return 0;
+}
+
+static int refdb_fs_backend__compress(git_refdb_backend *_backend)
+{
+ int error;
+ refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
+
+ GIT_ASSERT_ARG(backend);
+
+ if ((error = packed_reload(backend)) < 0 || /* load the existing packfile */
+ (error = packed_loadloose(backend)) < 0 || /* add all the loose refs */
+ (error = packed_write(backend)) < 0) /* write back to disk */
+ return error;
+
+ return 0;
+}
+
+static void refdb_fs_backend__free(git_refdb_backend *_backend)
+{
+ refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
+
+ if (!backend)
+ return;
+
+ git_sortedcache_free(backend->refcache);
+
+ git_mutex_lock(&backend->prlock);
+ packed_map_free(backend);
+ git_mutex_unlock(&backend->prlock);
+ git_mutex_free(&backend->prlock);
+
+ git__free(backend->gitpath);
+ git__free(backend->commonpath);
+ git__free(backend);
+}
+
+static char *setup_namespace(git_repository *repo, const char *in)
+{
+ git_str path = GIT_STR_INIT;
+ char *parts, *start, *end, *out = NULL;
+
+ if (!in)
+ goto done;
+
+ git_str_puts(&path, in);
+
+ /* if the repo is not namespaced, nothing else to do */
+ if (repo->namespace == NULL) {
+ out = git_str_detach(&path);
+ goto done;
+ }
+
+ parts = end = git__strdup(repo->namespace);
+ if (parts == NULL)
+ goto done;
+
+ /*
+ * From `man gitnamespaces`:
+ * namespaces which include a / will expand to a hierarchy
+ * of namespaces; for example, GIT_NAMESPACE=foo/bar will store
+ * refs under refs/namespaces/foo/refs/namespaces/bar/
+ */
+ while ((start = git__strsep(&end, "/")) != NULL)
+ git_str_printf(&path, "refs/namespaces/%s/", start);
+
+ git_str_printf(&path, "refs/namespaces/%s/refs", end);
+ git__free(parts);
+
+ /* Make sure that the folder with the namespace exists */
+ if (git_futils_mkdir_relative(git_str_cstr(&path), in, 0777,
+ GIT_MKDIR_PATH, NULL) < 0)
+ goto done;
+
+ /* Return root of the namespaced gitpath, i.e. without the trailing 'refs' */
+ git_str_rtruncate_at_char(&path, '/');
+ git_str_putc(&path, '/');
+ out = git_str_detach(&path);
+
+done:
+ git_str_dispose(&path);
+ return out;
+}
+
+static int reflog_alloc(
+ git_reflog **reflog,
+ const char *name,
+ git_oid_t oid_type)
+{
+ git_reflog *log;
+
+ *reflog = NULL;
+
+ log = git__calloc(1, sizeof(git_reflog));
+ GIT_ERROR_CHECK_ALLOC(log);
+
+ log->ref_name = git__strdup(name);
+ GIT_ERROR_CHECK_ALLOC(log->ref_name);
+
+ log->oid_type = oid_type;
+
+ if (git_vector_init(&log->entries, 0, NULL) < 0) {
+ git__free(log->ref_name);
+ git__free(log);
+ return -1;
+ }
+
+ *reflog = log;
+
+ return 0;
+}
+
+static int reflog_parse(git_reflog *log, const char *buf, size_t buf_size)
+{
+ git_parse_ctx parser = GIT_PARSE_CTX_INIT;
+
+ if ((git_parse_ctx_init(&parser, buf, buf_size)) < 0)
+ return -1;
+
+ for (; parser.remain_len; git_parse_advance_line(&parser)) {
+ git_reflog_entry *entry;
+ const char *sig;
+ char c;
+
+ entry = git__calloc(1, sizeof(*entry));
+ GIT_ERROR_CHECK_ALLOC(entry);
+ entry->committer = git__calloc(1, sizeof(*entry->committer));
+ GIT_ERROR_CHECK_ALLOC(entry->committer);
+
+ if (git_parse_advance_oid(&entry->oid_old, &parser, log->oid_type) < 0 ||
+ git_parse_advance_expected(&parser, " ", 1) < 0 ||
+ git_parse_advance_oid(&entry->oid_cur, &parser, log->oid_type) < 0)
+ goto next;
+
+ sig = parser.line;
+ while (git_parse_peek(&c, &parser, 0) == 0 && c != '\t' && c != '\n')
+ git_parse_advance_chars(&parser, 1);
+
+ if (git_signature__parse(entry->committer, &sig, parser.line, NULL, 0) < 0)
+ goto next;
+
+ if (c == '\t') {
+ size_t len;
+ git_parse_advance_chars(&parser, 1);
+
+ len = parser.line_len;
+ if (parser.line[len - 1] == '\n')
+ len--;
+
+ entry->msg = git__strndup(parser.line, len);
+ GIT_ERROR_CHECK_ALLOC(entry->msg);
+ }
+
+ if ((git_vector_insert(&log->entries, entry)) < 0) {
+ git_reflog_entry__free(entry);
+ return -1;
+ }
+
+ continue;
+
+next:
+ git_reflog_entry__free(entry);
+ }
+
+ return 0;
+}
+
+static int create_new_reflog_file(const char *filepath)
+{
+ int fd, error;
+
+ if ((error = git_futils_mkpath2file(filepath, GIT_REFLOG_DIR_MODE)) < 0)
+ return error;
+
+ if ((fd = p_open(filepath,
+ O_WRONLY | O_CREAT,
+ GIT_REFLOG_FILE_MODE)) < 0)
+ return -1;
+
+ return p_close(fd);
+}
+
+static int refdb_reflog_fs__ensure_log(git_refdb_backend *_backend, const char *name)
+{
+ refdb_fs_backend *backend;
+ git_repository *repo;
+ git_str path = GIT_STR_INIT;
+ int error;
+
+ GIT_ASSERT_ARG(_backend && name);
+
+ backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
+ repo = backend->repo;
+
+ if ((error = reflog_path(&path, repo, name)) < 0)
+ return error;
+
+ error = create_new_reflog_file(git_str_cstr(&path));
+ git_str_dispose(&path);
+
+ return error;
+}
+
+static int has_reflog(git_repository *repo, const char *name)
+{
+ int ret = 0;
+ git_str path = GIT_STR_INIT;
+
+ if (reflog_path(&path, repo, name) < 0)
+ goto cleanup;
+
+ ret = git_fs_path_isfile(git_str_cstr(&path));
+
+cleanup:
+ git_str_dispose(&path);
+ return ret;
+}
+
+static int refdb_reflog_fs__has_log(git_refdb_backend *_backend, const char *name)
+{
+ refdb_fs_backend *backend;
+
+ GIT_ASSERT_ARG(_backend);
+ GIT_ASSERT_ARG(name);
+
+ backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
+
+ return has_reflog(backend->repo, name);
+}
+
+static int refdb_reflog_fs__read(
+ git_reflog **out,
+ git_refdb_backend *_backend,
+ const char *name)
+{
+ int error = -1;
+ git_str log_path = GIT_STR_INIT;
+ git_str log_file = GIT_STR_INIT;
+ git_reflog *log = NULL;
+ git_repository *repo;
+ refdb_fs_backend *backend;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(_backend);
+ GIT_ASSERT_ARG(name);
+
+ backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
+ repo = backend->repo;
+
+ if (reflog_alloc(&log, name, backend->oid_type) < 0)
+ return -1;
+
+ if (reflog_path(&log_path, repo, name) < 0)
+ goto cleanup;
+
+ error = git_futils_readbuffer(&log_file, git_str_cstr(&log_path));
+ if (error < 0 && error != GIT_ENOTFOUND)
+ goto cleanup;
+
+ if ((error == GIT_ENOTFOUND) &&
+ ((error = create_new_reflog_file(git_str_cstr(&log_path))) < 0))
+ goto cleanup;
+
+ if ((error = reflog_parse(log,
+ git_str_cstr(&log_file), git_str_len(&log_file))) < 0)
+ goto cleanup;
+
+ *out = log;
+ goto success;
+
+cleanup:
+ git_reflog_free(log);
+
+success:
+ git_str_dispose(&log_file);
+ git_str_dispose(&log_path);
+
+ return error;
+}
+
+static int serialize_reflog_entry(
+ git_str *buf,
+ const git_oid *oid_old,
+ const git_oid *oid_new,
+ const git_signature *committer,
+ const char *msg)
+{
+ char raw_old[GIT_OID_MAX_HEXSIZE + 1];
+ char raw_new[GIT_OID_MAX_HEXSIZE + 1];
+
+ git_oid_tostr(raw_old, GIT_OID_MAX_HEXSIZE + 1, oid_old);
+ git_oid_tostr(raw_new, GIT_OID_MAX_HEXSIZE + 1, oid_new);
+
+ git_str_clear(buf);
+
+ git_str_puts(buf, raw_old);
+ git_str_putc(buf, ' ');
+ git_str_puts(buf, raw_new);
+
+ git_signature__writebuf(buf, " ", committer);
+
+ /* drop trailing LF */
+ git_str_rtrim(buf);
+
+ if (msg) {
+ size_t i;
+
+ git_str_putc(buf, '\t');
+ git_str_puts(buf, msg);
+
+ for (i = 0; i < buf->size - 2; i++)
+ if (buf->ptr[i] == '\n')
+ buf->ptr[i] = ' ';
+ git_str_rtrim(buf);
+ }
+
+ git_str_putc(buf, '\n');
+
+ return git_str_oom(buf);
+}
+
+static int lock_reflog(git_filebuf *file, refdb_fs_backend *backend, const char *refname)
+{
+ git_repository *repo;
+ git_str log_path = GIT_STR_INIT;
+ int error;
+
+ repo = backend->repo;
+
+ if (!git_path_is_valid(backend->repo, refname, 0, GIT_FS_PATH_REJECT_FILESYSTEM_DEFAULTS)) {
+ git_error_set(GIT_ERROR_INVALID, "invalid reference name '%s'", refname);
+ return GIT_EINVALIDSPEC;
+ }
+
+ if (reflog_path(&log_path, repo, refname) < 0)
+ return -1;
+
+ if (!git_fs_path_isfile(git_str_cstr(&log_path))) {
+ git_error_set(GIT_ERROR_INVALID,
+ "log file for reference '%s' doesn't exist", refname);
+ error = -1;
+ goto cleanup;
+ }
+
+ error = git_filebuf_open(file, git_str_cstr(&log_path), 0, GIT_REFLOG_FILE_MODE);
+
+cleanup:
+ git_str_dispose(&log_path);
+
+ return error;
+}
+
+static int refdb_reflog_fs__write(git_refdb_backend *_backend, git_reflog *reflog)
+{
+ int error = -1;
+ unsigned int i;
+ git_reflog_entry *entry;
+ refdb_fs_backend *backend;
+ git_str log = GIT_STR_INIT;
+ git_filebuf fbuf = GIT_FILEBUF_INIT;
+
+ GIT_ASSERT_ARG(_backend);
+ GIT_ASSERT_ARG(reflog);
+
+ backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
+
+ if ((error = lock_reflog(&fbuf, backend, reflog->ref_name)) < 0)
+ return -1;
+
+ git_vector_foreach(&reflog->entries, i, entry) {
+ if (serialize_reflog_entry(&log, &(entry->oid_old), &(entry->oid_cur), entry->committer, entry->msg) < 0)
+ goto cleanup;
+
+ if ((error = git_filebuf_write(&fbuf, log.ptr, log.size)) < 0)
+ goto cleanup;
+ }
+
+ error = git_filebuf_commit(&fbuf);
+ goto success;
+
+cleanup:
+ git_filebuf_cleanup(&fbuf);
+
+success:
+ git_str_dispose(&log);
+
+ return error;
+}
+
+/* Append to the reflog, must be called under reference lock */
+static int reflog_append(
+ refdb_fs_backend *backend,
+ const git_reference *ref,
+ const git_oid *old,
+ const git_oid *new,
+ const git_signature *who,
+ const char *message)
+{
+ int error, is_symbolic, open_flags;
+ git_oid old_id, new_id;
+ git_str buf = GIT_STR_INIT, path = GIT_STR_INIT;
+ git_repository *repo = backend->repo;
+
+ is_symbolic = ref->type == GIT_REFERENCE_SYMBOLIC;
+
+ /* "normal" symbolic updates do not write */
+ if (is_symbolic &&
+ strcmp(ref->name, GIT_HEAD_FILE) &&
+ !(old && new))
+ return 0;
+
+ /* From here on is_symbolic also means that it's HEAD */
+
+ git_oid_clear(&old_id, backend->oid_type);
+ git_oid_clear(&new_id, backend->oid_type);
+
+ if (old) {
+ git_oid_cpy(&old_id, old);
+ } else {
+ error = git_reference_name_to_id(&old_id, repo, ref->name);
+ if (error < 0 && error != GIT_ENOTFOUND)
+ return error;
+ }
+
+ if (new) {
+ git_oid_cpy(&new_id, new);
+ } else {
+ if (!is_symbolic) {
+ git_oid_cpy(&new_id, git_reference_target(ref));
+ } else {
+ error = git_reference_name_to_id(&new_id, repo, git_reference_symbolic_target(ref));
+ if (error < 0 && error != GIT_ENOTFOUND)
+ return error;
+ /* detaching HEAD does not create an entry */
+ if (error == GIT_ENOTFOUND)
+ return 0;
+
+ git_error_clear();
+ }
+ }
+
+ if ((error = serialize_reflog_entry(&buf, &old_id, &new_id, who, message)) < 0)
+ goto cleanup;
+
+ if ((error = reflog_path(&path, repo, ref->name)) < 0)
+ goto cleanup;
+
+ if (((error = git_futils_mkpath2file(git_str_cstr(&path), 0777)) < 0) &&
+ (error != GIT_EEXISTS)) {
+ goto cleanup;
+ }
+
+ /* If the new branch matches part of the namespace of a previously deleted branch,
+ * there maybe an obsolete/unused directory (or directory hierarchy) in the way.
+ */
+ if (git_fs_path_isdir(git_str_cstr(&path))) {
+ if ((error = git_futils_rmdir_r(git_str_cstr(&path), NULL, GIT_RMDIR_SKIP_NONEMPTY)) < 0) {
+ if (error == GIT_ENOTFOUND)
+ error = 0;
+ } else if (git_fs_path_isdir(git_str_cstr(&path))) {
+ git_error_set(GIT_ERROR_REFERENCE, "cannot create reflog at '%s', there are reflogs beneath that folder",
+ ref->name);
+ error = GIT_EDIRECTORY;
+ }
+
+ if (error != 0)
+ goto cleanup;
+ }
+
+ open_flags = O_WRONLY | O_CREAT | O_APPEND;
+
+ if (backend->fsync)
+ open_flags |= O_FSYNC;
+
+ error = git_futils_writebuffer(&buf, git_str_cstr(&path), open_flags, GIT_REFLOG_FILE_MODE);
+
+cleanup:
+ git_str_dispose(&buf);
+ git_str_dispose(&path);
+
+ return error;
+}
+
+static int refdb_reflog_fs__rename(git_refdb_backend *_backend, const char *old_name, const char *new_name)
+{
+ int error = 0, fd;
+ git_str old_path = GIT_STR_INIT;
+ git_str new_path = GIT_STR_INIT;
+ git_str temp_path = GIT_STR_INIT;
+ git_str normalized = GIT_STR_INIT;
+ git_repository *repo;
+ refdb_fs_backend *backend;
+
+ GIT_ASSERT_ARG(_backend);
+ GIT_ASSERT_ARG(old_name);
+ GIT_ASSERT_ARG(new_name);
+
+ backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
+ repo = backend->repo;
+
+ if ((error = git_reference__normalize_name(
+ &normalized, new_name, GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL)) < 0)
+ return error;
+
+ if (git_str_joinpath(&temp_path, repo->gitdir, GIT_REFLOG_DIR) < 0)
+ return -1;
+
+ if ((error = loose_path(&old_path, git_str_cstr(&temp_path), old_name)) < 0)
+ return error;
+
+ if ((error = loose_path(&new_path, git_str_cstr(&temp_path), git_str_cstr(&normalized))) < 0)
+ return error;
+
+ if (!git_fs_path_exists(git_str_cstr(&old_path))) {
+ error = GIT_ENOTFOUND;
+ goto cleanup;
+ }
+
+ /*
+ * Move the reflog to a temporary place. This two-phase renaming is required
+ * in order to cope with funny renaming use cases when one tries to move a reference
+ * to a partially colliding namespace:
+ * - a/b -> a/b/c
+ * - a/b/c/d -> a/b/c
+ */
+ if ((error = loose_path(&temp_path, git_str_cstr(&temp_path), "temp_reflog")) < 0)
+ return error;
+
+ if ((fd = git_futils_mktmp(&temp_path, git_str_cstr(&temp_path), GIT_REFLOG_FILE_MODE)) < 0) {
+ error = -1;
+ goto cleanup;
+ }
+
+ p_close(fd);
+
+ if (p_rename(git_str_cstr(&old_path), git_str_cstr(&temp_path)) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to rename reflog for %s", new_name);
+ error = -1;
+ goto cleanup;
+ }
+
+ if (git_fs_path_isdir(git_str_cstr(&new_path)) &&
+ (git_futils_rmdir_r(git_str_cstr(&new_path), NULL, GIT_RMDIR_SKIP_NONEMPTY) < 0)) {
+ error = -1;
+ goto cleanup;
+ }
+
+ if (git_futils_mkpath2file(git_str_cstr(&new_path), GIT_REFLOG_DIR_MODE) < 0) {
+ error = -1;
+ goto cleanup;
+ }
+
+ if (p_rename(git_str_cstr(&temp_path), git_str_cstr(&new_path)) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to rename reflog for %s", new_name);
+ error = -1;
+ }
+
+cleanup:
+ git_str_dispose(&temp_path);
+ git_str_dispose(&old_path);
+ git_str_dispose(&new_path);
+ git_str_dispose(&normalized);
+
+ return error;
+}
+
+static int refdb_reflog_fs__delete(git_refdb_backend *_backend, const char *name)
+{
+ refdb_fs_backend *backend = GIT_CONTAINER_OF(_backend, refdb_fs_backend, parent);
+ git_str path = GIT_STR_INIT;
+ int error;
+
+ GIT_ASSERT_ARG(_backend);
+ GIT_ASSERT_ARG(name);
+
+ if ((error = reflog_path(&path, backend->repo, name)) < 0)
+ goto out;
+
+ /*
+ * If a reference was moved downwards, eg refs/heads/br2 -> refs/heads/br2/new-name,
+ * refs/heads/br2 does exist but it's a directory. That's a valid situation.
+ * Proceed only if it's a file.
+ */
+ if (!git_fs_path_isfile(path.ptr))
+ goto out;
+
+ if ((error = p_unlink(path.ptr)) < 0)
+ goto out;
+
+ error = refdb_fs_backend__prune_refs(backend, name, GIT_REFLOG_DIR);
+
+out:
+ git_str_dispose(&path);
+
+ return error;
+}
+
+int git_refdb_backend_fs(
+ git_refdb_backend **backend_out,
+ git_repository *repository)
+{
+ int t = 0;
+ git_str gitpath = GIT_STR_INIT;
+ refdb_fs_backend *backend;
+
+ backend = git__calloc(1, sizeof(refdb_fs_backend));
+ GIT_ERROR_CHECK_ALLOC(backend);
+ if (git_mutex_init(&backend->prlock) < 0) {
+ git__free(backend);
+ return -1;
+ }
+
+
+ if (git_refdb_init_backend(&backend->parent, GIT_REFDB_BACKEND_VERSION) < 0)
+ goto fail;
+
+ backend->repo = repository;
+ backend->oid_type = repository->oid_type;
+
+ if (repository->gitdir) {
+ backend->gitpath = setup_namespace(repository, repository->gitdir);
+
+ if (backend->gitpath == NULL)
+ goto fail;
+ }
+
+ if (repository->commondir) {
+ backend->commonpath = setup_namespace(repository, repository->commondir);
+
+ if (backend->commonpath == NULL)
+ goto fail;
+ }
+
+ if (git_str_joinpath(&gitpath, backend->commonpath, GIT_PACKEDREFS_FILE) < 0 ||
+ git_sortedcache_new(
+ &backend->refcache, offsetof(struct packref, name),
+ NULL, NULL, packref_cmp, git_str_cstr(&gitpath)) < 0)
+ goto fail;
+
+ git_str_dispose(&gitpath);
+
+ if (!git_repository__configmap_lookup(&t, backend->repo, GIT_CONFIGMAP_IGNORECASE) && t) {
+ backend->iterator_flags |= GIT_ITERATOR_IGNORE_CASE;
+ backend->direach_flags |= GIT_FS_PATH_DIR_IGNORE_CASE;
+ }
+ if (!git_repository__configmap_lookup(&t, backend->repo, GIT_CONFIGMAP_PRECOMPOSE) && t) {
+ backend->iterator_flags |= GIT_ITERATOR_PRECOMPOSE_UNICODE;
+ backend->direach_flags |= GIT_FS_PATH_DIR_PRECOMPOSE_UNICODE;
+ }
+ if ((!git_repository__configmap_lookup(&t, backend->repo, GIT_CONFIGMAP_FSYNCOBJECTFILES) && t) ||
+ git_repository__fsync_gitdir)
+ backend->fsync = 1;
+ backend->iterator_flags |= GIT_ITERATOR_DESCEND_SYMLINKS;
+
+ backend->parent.exists = &refdb_fs_backend__exists;
+ backend->parent.lookup = &refdb_fs_backend__lookup;
+ backend->parent.iterator = &refdb_fs_backend__iterator;
+ backend->parent.write = &refdb_fs_backend__write;
+ backend->parent.del = &refdb_fs_backend__delete;
+ backend->parent.rename = &refdb_fs_backend__rename;
+ backend->parent.compress = &refdb_fs_backend__compress;
+ backend->parent.lock = &refdb_fs_backend__lock;
+ backend->parent.unlock = &refdb_fs_backend__unlock;
+ backend->parent.has_log = &refdb_reflog_fs__has_log;
+ backend->parent.ensure_log = &refdb_reflog_fs__ensure_log;
+ backend->parent.free = &refdb_fs_backend__free;
+ backend->parent.reflog_read = &refdb_reflog_fs__read;
+ backend->parent.reflog_write = &refdb_reflog_fs__write;
+ backend->parent.reflog_rename = &refdb_reflog_fs__rename;
+ backend->parent.reflog_delete = &refdb_reflog_fs__delete;
+
+ *backend_out = (git_refdb_backend *)backend;
+ return 0;
+
+fail:
+ git_mutex_free(&backend->prlock);
+ git_str_dispose(&gitpath);
+ git__free(backend->gitpath);
+ git__free(backend->commonpath);
+ git__free(backend);
+ return -1;
+}
diff --git a/src/libgit2/reflog.c b/src/libgit2/reflog.c
new file mode 100644
index 0000000..86d4355
--- /dev/null
+++ b/src/libgit2/reflog.c
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "reflog.h"
+
+#include "repository.h"
+#include "filebuf.h"
+#include "signature.h"
+#include "refdb.h"
+
+#include "git2/sys/refdb_backend.h"
+#include "git2/sys/reflog.h"
+
+void git_reflog_entry__free(git_reflog_entry *entry)
+{
+ git_signature_free(entry->committer);
+
+ git__free(entry->msg);
+ git__free(entry);
+}
+
+void git_reflog_free(git_reflog *reflog)
+{
+ size_t i;
+ git_reflog_entry *entry;
+
+ if (reflog == NULL)
+ return;
+
+ if (reflog->db)
+ GIT_REFCOUNT_DEC(reflog->db, git_refdb__free);
+
+ for (i=0; i < reflog->entries.length; i++) {
+ entry = git_vector_get(&reflog->entries, i);
+
+ git_reflog_entry__free(entry);
+ }
+
+ git_vector_free(&reflog->entries);
+ git__free(reflog->ref_name);
+ git__free(reflog);
+}
+
+int git_reflog_read(git_reflog **reflog, git_repository *repo, const char *name)
+{
+ git_refdb *refdb;
+ int error;
+
+ GIT_ASSERT_ARG(reflog);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
+
+ if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
+ return error;
+
+ return git_refdb_reflog_read(reflog, refdb, name);
+}
+
+int git_reflog_write(git_reflog *reflog)
+{
+ git_refdb *db;
+
+ GIT_ASSERT_ARG(reflog);
+ GIT_ASSERT_ARG(reflog->db);
+
+ db = reflog->db;
+ return db->backend->reflog_write(db->backend, reflog);
+}
+
+int git_reflog_append(
+ git_reflog *reflog,
+ const git_oid *new_oid,
+ const git_signature *committer,
+ const char *msg)
+{
+ const git_reflog_entry *previous;
+ git_reflog_entry *entry;
+
+ GIT_ASSERT_ARG(reflog);
+ GIT_ASSERT_ARG(new_oid);
+ GIT_ASSERT_ARG(committer);
+
+ entry = git__calloc(1, sizeof(git_reflog_entry));
+ GIT_ERROR_CHECK_ALLOC(entry);
+
+ if ((git_signature_dup(&entry->committer, committer)) < 0)
+ goto cleanup;
+
+ if (msg != NULL) {
+ size_t i, msglen = strlen(msg);
+
+ if ((entry->msg = git__strndup(msg, msglen)) == NULL)
+ goto cleanup;
+
+ /*
+ * Replace all newlines with spaces, except for
+ * the final trailing newline.
+ */
+ for (i = 0; i < msglen; i++)
+ if (entry->msg[i] == '\n')
+ entry->msg[i] = ' ';
+ }
+
+ previous = git_reflog_entry_byindex(reflog, 0);
+
+ if (previous == NULL)
+ git_oid_clear(&entry->oid_old, reflog->oid_type);
+ else
+ git_oid_cpy(&entry->oid_old, &previous->oid_cur);
+
+ git_oid_cpy(&entry->oid_cur, new_oid);
+
+ if (git_vector_insert(&reflog->entries, entry) < 0)
+ goto cleanup;
+
+ return 0;
+
+cleanup:
+ git_reflog_entry__free(entry);
+ return -1;
+}
+
+int git_reflog_rename(git_repository *repo, const char *old_name, const char *new_name)
+{
+ git_refdb *refdb;
+ int error;
+
+ if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
+ return -1;
+
+ return refdb->backend->reflog_rename(refdb->backend, old_name, new_name);
+}
+
+int git_reflog_delete(git_repository *repo, const char *name)
+{
+ git_refdb *refdb;
+ int error;
+
+ if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
+ return -1;
+
+ return refdb->backend->reflog_delete(refdb->backend, name);
+}
+
+size_t git_reflog_entrycount(git_reflog *reflog)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(reflog, 0);
+ return reflog->entries.length;
+}
+
+const git_reflog_entry *git_reflog_entry_byindex(const git_reflog *reflog, size_t idx)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(reflog, NULL);
+
+ if (idx >= reflog->entries.length)
+ return NULL;
+
+ return git_vector_get(
+ &reflog->entries, reflog_inverse_index(idx, reflog->entries.length));
+}
+
+const git_oid *git_reflog_entry_id_old(const git_reflog_entry *entry)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(entry, NULL);
+ return &entry->oid_old;
+}
+
+const git_oid *git_reflog_entry_id_new(const git_reflog_entry *entry)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(entry, NULL);
+ return &entry->oid_cur;
+}
+
+const git_signature *git_reflog_entry_committer(const git_reflog_entry *entry)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(entry, NULL);
+ return entry->committer;
+}
+
+const char *git_reflog_entry_message(const git_reflog_entry *entry)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(entry, NULL);
+ return entry->msg;
+}
+
+int git_reflog_drop(git_reflog *reflog, size_t idx, int rewrite_previous_entry)
+{
+ size_t entrycount;
+ git_reflog_entry *entry, *previous;
+
+ entrycount = git_reflog_entrycount(reflog);
+
+ entry = (git_reflog_entry *)git_reflog_entry_byindex(reflog, idx);
+
+ if (entry == NULL) {
+ git_error_set(GIT_ERROR_REFERENCE, "no reflog entry at index %"PRIuZ, idx);
+ return GIT_ENOTFOUND;
+ }
+
+ git_reflog_entry__free(entry);
+
+ if (git_vector_remove(
+ &reflog->entries, reflog_inverse_index(idx, entrycount)) < 0)
+ return -1;
+
+ if (!rewrite_previous_entry)
+ return 0;
+
+ /* No need to rewrite anything when removing the most recent entry */
+ if (idx == 0)
+ return 0;
+
+ /* Have the latest entry just been dropped? */
+ if (entrycount == 1)
+ return 0;
+
+ entry = (git_reflog_entry *)git_reflog_entry_byindex(reflog, idx - 1);
+
+ /* If the oldest entry has just been removed... */
+ if (idx == entrycount - 1) {
+ /* ...clear the oid_old member of the "new" oldest entry */
+ git_oid_clear(&entry->oid_old, reflog->oid_type);
+ return 0;
+ }
+
+ previous = (git_reflog_entry *)git_reflog_entry_byindex(reflog, idx);
+ git_oid_cpy(&entry->oid_old, &previous->oid_cur);
+
+ return 0;
+}
diff --git a/src/libgit2/reflog.h b/src/libgit2/reflog.h
new file mode 100644
index 0000000..bc98785
--- /dev/null
+++ b/src/libgit2/reflog.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_reflog_h__
+#define INCLUDE_reflog_h__
+
+#include "common.h"
+
+#include "git2/reflog.h"
+#include "vector.h"
+
+#define GIT_REFLOG_DIR "logs/"
+#define GIT_REFLOG_DIR_MODE 0777
+#define GIT_REFLOG_FILE_MODE 0666
+
+struct git_reflog_entry {
+ git_oid oid_old;
+ git_oid oid_cur;
+
+ git_signature *committer;
+
+ char *msg;
+};
+
+struct git_reflog {
+ git_refdb *db;
+ char *ref_name;
+ git_oid_t oid_type;
+ git_vector entries;
+};
+
+GIT_INLINE(size_t) reflog_inverse_index(size_t idx, size_t total)
+{
+ return (total - 1) - idx;
+}
+
+#endif
diff --git a/src/libgit2/refs.c b/src/libgit2/refs.c
new file mode 100644
index 0000000..5e2fe97
--- /dev/null
+++ b/src/libgit2/refs.c
@@ -0,0 +1,1404 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "refs.h"
+
+#include "hash.h"
+#include "repository.h"
+#include "futils.h"
+#include "filebuf.h"
+#include "pack.h"
+#include "reflog.h"
+#include "refdb.h"
+
+#include <git2/tag.h>
+#include <git2/object.h>
+#include <git2/oid.h>
+#include <git2/branch.h>
+#include <git2/refs.h>
+#include <git2/refdb.h>
+#include <git2/sys/refs.h>
+#include <git2/signature.h>
+#include <git2/commit.h>
+
+bool git_reference__enable_symbolic_ref_target_validation = true;
+
+enum {
+ GIT_PACKREF_HAS_PEEL = 1,
+ GIT_PACKREF_WAS_LOOSE = 2
+};
+
+static git_reference *alloc_ref(const char *name)
+{
+ git_reference *ref = NULL;
+ size_t namelen = strlen(name), reflen;
+
+ if (!GIT_ADD_SIZET_OVERFLOW(&reflen, sizeof(git_reference), namelen) &&
+ !GIT_ADD_SIZET_OVERFLOW(&reflen, reflen, 1) &&
+ (ref = git__calloc(1, reflen)) != NULL)
+ memcpy(ref->name, name, namelen + 1);
+
+ return ref;
+}
+
+git_reference *git_reference__alloc_symbolic(
+ const char *name, const char *target)
+{
+ git_reference *ref;
+
+ GIT_ASSERT_ARG_WITH_RETVAL(name, NULL);
+ GIT_ASSERT_ARG_WITH_RETVAL(target, NULL);
+
+ ref = alloc_ref(name);
+ if (!ref)
+ return NULL;
+
+ ref->type = GIT_REFERENCE_SYMBOLIC;
+
+ if ((ref->target.symbolic = git__strdup(target)) == NULL) {
+ git__free(ref);
+ return NULL;
+ }
+
+ return ref;
+}
+
+git_reference *git_reference__alloc(
+ const char *name,
+ const git_oid *oid,
+ const git_oid *peel)
+{
+ git_oid_t oid_type;
+ git_reference *ref;
+
+ GIT_ASSERT_ARG_WITH_RETVAL(name, NULL);
+ GIT_ASSERT_ARG_WITH_RETVAL(oid, NULL);
+
+ ref = alloc_ref(name);
+ if (!ref)
+ return NULL;
+
+ ref->type = GIT_REFERENCE_DIRECT;
+ git_oid_cpy(&ref->target.oid, oid);
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ oid_type = oid->type;
+#else
+ oid_type = GIT_OID_SHA1;
+#endif
+
+ if (peel != NULL)
+ git_oid_cpy(&ref->peel, peel);
+ else
+ git_oid_clear(&ref->peel, oid_type);
+
+ return ref;
+}
+
+git_reference *git_reference__realloc(
+ git_reference **ptr_to_ref, const char *name)
+{
+ size_t namelen, reflen;
+ git_reference *rewrite = NULL;
+
+ GIT_ASSERT_ARG_WITH_RETVAL(ptr_to_ref, NULL);
+ GIT_ASSERT_ARG_WITH_RETVAL(name, NULL);
+
+ namelen = strlen(name);
+
+ if (!GIT_ADD_SIZET_OVERFLOW(&reflen, sizeof(git_reference), namelen) &&
+ !GIT_ADD_SIZET_OVERFLOW(&reflen, reflen, 1) &&
+ (rewrite = git__realloc(*ptr_to_ref, reflen)) != NULL)
+ memcpy(rewrite->name, name, namelen + 1);
+
+ *ptr_to_ref = NULL;
+
+ return rewrite;
+}
+
+int git_reference_dup(git_reference **dest, git_reference *source)
+{
+ if (source->type == GIT_REFERENCE_SYMBOLIC)
+ *dest = git_reference__alloc_symbolic(source->name, source->target.symbolic);
+ else
+ *dest = git_reference__alloc(source->name, &source->target.oid, &source->peel);
+
+ GIT_ERROR_CHECK_ALLOC(*dest);
+
+ (*dest)->db = source->db;
+ GIT_REFCOUNT_INC((*dest)->db);
+
+ return 0;
+}
+
+void git_reference_free(git_reference *reference)
+{
+ if (reference == NULL)
+ return;
+
+ if (reference->type == GIT_REFERENCE_SYMBOLIC)
+ git__free(reference->target.symbolic);
+
+ if (reference->db)
+ GIT_REFCOUNT_DEC(reference->db, git_refdb__free);
+
+ git__free(reference);
+}
+
+int git_reference_delete(git_reference *ref)
+{
+ const git_oid *old_id = NULL;
+ const char *old_target = NULL;
+
+ if (!strcmp(ref->name, "HEAD")) {
+ git_error_set(GIT_ERROR_REFERENCE, "cannot delete HEAD");
+ return GIT_ERROR;
+ }
+
+ if (ref->type == GIT_REFERENCE_DIRECT)
+ old_id = &ref->target.oid;
+ else
+ old_target = ref->target.symbolic;
+
+ return git_refdb_delete(ref->db, ref->name, old_id, old_target);
+}
+
+int git_reference_remove(git_repository *repo, const char *name)
+{
+ git_refdb *db;
+ int error;
+
+ if ((error = git_repository_refdb__weakptr(&db, repo)) < 0)
+ return error;
+
+ return git_refdb_delete(db, name, NULL, NULL);
+}
+
+int git_reference_lookup(git_reference **ref_out,
+ git_repository *repo, const char *name)
+{
+ return git_reference_lookup_resolved(ref_out, repo, name, 0);
+}
+
+int git_reference_name_to_id(
+ git_oid *out, git_repository *repo, const char *name)
+{
+ int error;
+ git_reference *ref;
+
+ if ((error = git_reference_lookup_resolved(&ref, repo, name, -1)) < 0)
+ return error;
+
+ git_oid_cpy(out, git_reference_target(ref));
+ git_reference_free(ref);
+ return 0;
+}
+
+static int reference_normalize_for_repo(
+ git_refname_t out,
+ git_repository *repo,
+ const char *name,
+ bool validate)
+{
+ int precompose;
+ unsigned int flags = GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL;
+
+ if (!git_repository__configmap_lookup(&precompose, repo, GIT_CONFIGMAP_PRECOMPOSE) &&
+ precompose)
+ flags |= GIT_REFERENCE_FORMAT__PRECOMPOSE_UNICODE;
+
+ if (!validate)
+ flags |= GIT_REFERENCE_FORMAT__VALIDATION_DISABLE;
+
+ return git_reference_normalize_name(out, GIT_REFNAME_MAX, name, flags);
+}
+
+int git_reference_lookup_resolved(
+ git_reference **ref_out,
+ git_repository *repo,
+ const char *name,
+ int max_nesting)
+{
+ git_refname_t normalized;
+ git_refdb *refdb;
+ int error = 0;
+
+ GIT_ASSERT_ARG(ref_out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
+
+ if ((error = reference_normalize_for_repo(normalized, repo, name, true)) < 0 ||
+ (error = git_repository_refdb__weakptr(&refdb, repo)) < 0 ||
+ (error = git_refdb_resolve(ref_out, refdb, normalized, max_nesting)) < 0)
+ return error;
+
+ /*
+ * The resolved reference may be a symbolic reference in case its
+ * target doesn't exist. If the user asked us to resolve (e.g.
+ * `max_nesting != 0`), then we need to return an error in case we got
+ * a symbolic reference back.
+ */
+ if (max_nesting && git_reference_type(*ref_out) == GIT_REFERENCE_SYMBOLIC) {
+ git_reference_free(*ref_out);
+ *ref_out = NULL;
+ return GIT_ENOTFOUND;
+ }
+
+ return 0;
+}
+
+int git_reference_dwim(git_reference **out, git_repository *repo, const char *refname)
+{
+ int error = 0, i, valid;
+ bool fallbackmode = true, foundvalid = false;
+ git_reference *ref;
+ git_str refnamebuf = GIT_STR_INIT, name = GIT_STR_INIT;
+
+ static const char *formatters[] = {
+ "%s",
+ GIT_REFS_DIR "%s",
+ GIT_REFS_TAGS_DIR "%s",
+ GIT_REFS_HEADS_DIR "%s",
+ GIT_REFS_REMOTES_DIR "%s",
+ GIT_REFS_REMOTES_DIR "%s/" GIT_HEAD_FILE,
+ NULL
+ };
+
+ if (*refname)
+ git_str_puts(&name, refname);
+ else {
+ git_str_puts(&name, GIT_HEAD_FILE);
+ fallbackmode = false;
+ }
+
+ for (i = 0; formatters[i] && (fallbackmode || i == 0); i++) {
+
+ git_str_clear(&refnamebuf);
+
+ if ((error = git_str_printf(&refnamebuf, formatters[i], git_str_cstr(&name))) < 0 ||
+ (error = git_reference_name_is_valid(&valid, git_str_cstr(&refnamebuf))) < 0)
+ goto cleanup;
+
+ if (!valid) {
+ error = GIT_EINVALIDSPEC;
+ continue;
+ }
+ foundvalid = true;
+
+ error = git_reference_lookup_resolved(&ref, repo, git_str_cstr(&refnamebuf), -1);
+
+ if (!error) {
+ *out = ref;
+ error = 0;
+ goto cleanup;
+ }
+
+ if (error != GIT_ENOTFOUND)
+ goto cleanup;
+ }
+
+cleanup:
+ if (error && !foundvalid) {
+ /* never found a valid reference name */
+ git_error_set(GIT_ERROR_REFERENCE,
+ "could not use '%s' as valid reference name", git_str_cstr(&name));
+ }
+
+ if (error == GIT_ENOTFOUND)
+ git_error_set(GIT_ERROR_REFERENCE, "no reference found for shorthand '%s'", refname);
+
+ git_str_dispose(&name);
+ git_str_dispose(&refnamebuf);
+ return error;
+}
+
+/**
+ * Getters
+ */
+git_reference_t git_reference_type(const git_reference *ref)
+{
+ GIT_ASSERT_ARG(ref);
+ return ref->type;
+}
+
+const char *git_reference_name(const git_reference *ref)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(ref, NULL);
+ return ref->name;
+}
+
+git_repository *git_reference_owner(const git_reference *ref)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(ref, NULL);
+ return ref->db->repo;
+}
+
+const git_oid *git_reference_target(const git_reference *ref)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(ref, NULL);
+
+ if (ref->type != GIT_REFERENCE_DIRECT)
+ return NULL;
+
+ return &ref->target.oid;
+}
+
+const git_oid *git_reference_target_peel(const git_reference *ref)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(ref, NULL);
+
+ if (ref->type != GIT_REFERENCE_DIRECT || git_oid_is_zero(&ref->peel))
+ return NULL;
+
+ return &ref->peel;
+}
+
+const char *git_reference_symbolic_target(const git_reference *ref)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(ref, NULL);
+
+ if (ref->type != GIT_REFERENCE_SYMBOLIC)
+ return NULL;
+
+ return ref->target.symbolic;
+}
+
+static int reference__create(
+ git_reference **ref_out,
+ git_repository *repo,
+ const char *name,
+ const git_oid *oid,
+ const char *symbolic,
+ int force,
+ const git_signature *signature,
+ const char *log_message,
+ const git_oid *old_id,
+ const char *old_target)
+{
+ git_refname_t normalized;
+ git_refdb *refdb;
+ git_reference *ref = NULL;
+ int error = 0;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
+ GIT_ASSERT_ARG(symbolic || signature);
+
+ if (ref_out)
+ *ref_out = NULL;
+
+ error = reference_normalize_for_repo(normalized, repo, name, true);
+ if (error < 0)
+ return error;
+
+ error = git_repository_refdb__weakptr(&refdb, repo);
+ if (error < 0)
+ return error;
+
+ if (oid != NULL) {
+ GIT_ASSERT(symbolic == NULL);
+
+ if (!git_object__is_valid(repo, oid, GIT_OBJECT_ANY)) {
+ git_error_set(GIT_ERROR_REFERENCE,
+ "target OID for the reference doesn't exist on the repository");
+ return -1;
+ }
+
+ ref = git_reference__alloc(normalized, oid, NULL);
+ } else {
+ git_refname_t normalized_target;
+
+ error = reference_normalize_for_repo(normalized_target, repo,
+ symbolic, git_reference__enable_symbolic_ref_target_validation);
+
+ if (error < 0)
+ return error;
+
+ ref = git_reference__alloc_symbolic(normalized, normalized_target);
+ }
+
+ GIT_ERROR_CHECK_ALLOC(ref);
+
+ if ((error = git_refdb_write(refdb, ref, force, signature, log_message, old_id, old_target)) < 0) {
+ git_reference_free(ref);
+ return error;
+ }
+
+ if (ref_out == NULL)
+ git_reference_free(ref);
+ else
+ *ref_out = ref;
+
+ return 0;
+}
+
+static int refs_configured_ident(git_signature **out, const git_repository *repo)
+{
+ if (repo->ident_name && repo->ident_email)
+ return git_signature_now(out, repo->ident_name, repo->ident_email);
+
+ /* if not configured let us fall-through to the next method */
+ return -1;
+}
+
+int git_reference__log_signature(git_signature **out, git_repository *repo)
+{
+ int error;
+ git_signature *who;
+
+ if(((error = refs_configured_ident(&who, repo)) < 0) &&
+ ((error = git_signature_default(&who, repo)) < 0) &&
+ ((error = git_signature_now(&who, "unknown", "unknown")) < 0))
+ return error;
+
+ *out = who;
+ return 0;
+}
+
+int git_reference_create_matching(
+ git_reference **ref_out,
+ git_repository *repo,
+ const char *name,
+ const git_oid *id,
+ int force,
+ const git_oid *old_id,
+ const char *log_message)
+
+{
+ int error;
+ git_signature *who = NULL;
+
+ GIT_ASSERT_ARG(id);
+
+ if ((error = git_reference__log_signature(&who, repo)) < 0)
+ return error;
+
+ error = reference__create(
+ ref_out, repo, name, id, NULL, force, who, log_message, old_id, NULL);
+
+ git_signature_free(who);
+ return error;
+}
+
+int git_reference_create(
+ git_reference **ref_out,
+ git_repository *repo,
+ const char *name,
+ const git_oid *id,
+ int force,
+ const char *log_message)
+{
+ return git_reference_create_matching(ref_out, repo, name, id, force, NULL, log_message);
+}
+
+int git_reference_symbolic_create_matching(
+ git_reference **ref_out,
+ git_repository *repo,
+ const char *name,
+ const char *target,
+ int force,
+ const char *old_target,
+ const char *log_message)
+{
+ int error;
+ git_signature *who = NULL;
+
+ GIT_ASSERT_ARG(target);
+
+ if ((error = git_reference__log_signature(&who, repo)) < 0)
+ return error;
+
+ error = reference__create(
+ ref_out, repo, name, NULL, target, force, who, log_message, NULL, old_target);
+
+ git_signature_free(who);
+ return error;
+}
+
+int git_reference_symbolic_create(
+ git_reference **ref_out,
+ git_repository *repo,
+ const char *name,
+ const char *target,
+ int force,
+ const char *log_message)
+{
+ return git_reference_symbolic_create_matching(ref_out, repo, name, target, force, NULL, log_message);
+}
+
+static int ensure_is_an_updatable_direct_reference(git_reference *ref)
+{
+ if (ref->type == GIT_REFERENCE_DIRECT)
+ return 0;
+
+ git_error_set(GIT_ERROR_REFERENCE, "cannot set OID on symbolic reference");
+ return -1;
+}
+
+int git_reference_set_target(
+ git_reference **out,
+ git_reference *ref,
+ const git_oid *id,
+ const char *log_message)
+{
+ int error;
+ git_repository *repo;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(ref);
+ GIT_ASSERT_ARG(id);
+
+ repo = ref->db->repo;
+
+ if ((error = ensure_is_an_updatable_direct_reference(ref)) < 0)
+ return error;
+
+ return git_reference_create_matching(out, repo, ref->name, id, 1, &ref->target.oid, log_message);
+}
+
+static int ensure_is_an_updatable_symbolic_reference(git_reference *ref)
+{
+ if (ref->type == GIT_REFERENCE_SYMBOLIC)
+ return 0;
+
+ git_error_set(GIT_ERROR_REFERENCE, "cannot set symbolic target on a direct reference");
+ return -1;
+}
+
+int git_reference_symbolic_set_target(
+ git_reference **out,
+ git_reference *ref,
+ const char *target,
+ const char *log_message)
+{
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(ref);
+ GIT_ASSERT_ARG(target);
+
+ if ((error = ensure_is_an_updatable_symbolic_reference(ref)) < 0)
+ return error;
+
+ return git_reference_symbolic_create_matching(
+ out, ref->db->repo, ref->name, target, 1, ref->target.symbolic, log_message);
+}
+
+typedef struct {
+ const char *old_name;
+ git_refname_t new_name;
+} refs_update_head_payload;
+
+static int refs_update_head(git_repository *worktree, void *_payload)
+{
+ refs_update_head_payload *payload = (refs_update_head_payload *)_payload;
+ git_reference *head = NULL, *updated = NULL;
+ int error;
+
+ if ((error = git_reference_lookup(&head, worktree, GIT_HEAD_FILE)) < 0)
+ goto out;
+
+ if (git_reference_type(head) != GIT_REFERENCE_SYMBOLIC ||
+ git__strcmp(git_reference_symbolic_target(head), payload->old_name) != 0)
+ goto out;
+
+ /* Update HEAD if it was pointing to the reference being renamed */
+ if ((error = git_reference_symbolic_set_target(&updated, head, payload->new_name, NULL)) < 0) {
+ git_error_set(GIT_ERROR_REFERENCE, "failed to update HEAD after renaming reference");
+ goto out;
+ }
+
+out:
+ git_reference_free(updated);
+ git_reference_free(head);
+ return error;
+}
+
+int git_reference_rename(
+ git_reference **out,
+ git_reference *ref,
+ const char *new_name,
+ int force,
+ const char *log_message)
+{
+ refs_update_head_payload payload;
+ git_signature *signature = NULL;
+ git_repository *repo;
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(ref);
+
+ repo = git_reference_owner(ref);
+
+ if ((error = git_reference__log_signature(&signature, repo)) < 0 ||
+ (error = reference_normalize_for_repo(payload.new_name, repo, new_name, true)) < 0 ||
+ (error = git_refdb_rename(out, ref->db, ref->name, payload.new_name, force, signature, log_message)) < 0)
+ goto out;
+
+ payload.old_name = ref->name;
+
+ /* We may have to update any HEAD that was pointing to the renamed reference. */
+ if ((error = git_repository_foreach_worktree(repo, refs_update_head, &payload)) < 0)
+ goto out;
+
+out:
+ git_signature_free(signature);
+ return error;
+}
+
+int git_reference_resolve(git_reference **ref_out, const git_reference *ref)
+{
+ switch (git_reference_type(ref)) {
+ case GIT_REFERENCE_DIRECT:
+ return git_reference_lookup(ref_out, ref->db->repo, ref->name);
+
+ case GIT_REFERENCE_SYMBOLIC:
+ return git_reference_lookup_resolved(ref_out, ref->db->repo, ref->target.symbolic, -1);
+
+ default:
+ git_error_set(GIT_ERROR_REFERENCE, "invalid reference");
+ return -1;
+ }
+}
+
+int git_reference_foreach(
+ git_repository *repo,
+ git_reference_foreach_cb callback,
+ void *payload)
+{
+ git_reference_iterator *iter;
+ git_reference *ref;
+ int error;
+
+ if ((error = git_reference_iterator_new(&iter, repo)) < 0)
+ return error;
+
+ while (!(error = git_reference_next(&ref, iter))) {
+ if ((error = callback(ref, payload)) != 0) {
+ git_error_set_after_callback(error);
+ break;
+ }
+ }
+
+ if (error == GIT_ITEROVER)
+ error = 0;
+
+ git_reference_iterator_free(iter);
+ return error;
+}
+
+int git_reference_foreach_name(
+ git_repository *repo,
+ git_reference_foreach_name_cb callback,
+ void *payload)
+{
+ git_reference_iterator *iter;
+ const char *refname;
+ int error;
+
+ if ((error = git_reference_iterator_new(&iter, repo)) < 0)
+ return error;
+
+ while (!(error = git_reference_next_name(&refname, iter))) {
+ if ((error = callback(refname, payload)) != 0) {
+ git_error_set_after_callback(error);
+ break;
+ }
+ }
+
+ if (error == GIT_ITEROVER)
+ error = 0;
+
+ git_reference_iterator_free(iter);
+ return error;
+}
+
+int git_reference_foreach_glob(
+ git_repository *repo,
+ const char *glob,
+ git_reference_foreach_name_cb callback,
+ void *payload)
+{
+ git_reference_iterator *iter;
+ const char *refname;
+ int error;
+
+ if ((error = git_reference_iterator_glob_new(&iter, repo, glob)) < 0)
+ return error;
+
+ while (!(error = git_reference_next_name(&refname, iter))) {
+ if ((error = callback(refname, payload)) != 0) {
+ git_error_set_after_callback(error);
+ break;
+ }
+ }
+
+ if (error == GIT_ITEROVER)
+ error = 0;
+
+ git_reference_iterator_free(iter);
+ return error;
+}
+
+int git_reference_iterator_new(git_reference_iterator **out, git_repository *repo)
+{
+ git_refdb *refdb;
+
+ if (git_repository_refdb__weakptr(&refdb, repo) < 0)
+ return -1;
+
+ return git_refdb_iterator(out, refdb, NULL);
+}
+
+int git_reference_iterator_glob_new(
+ git_reference_iterator **out, git_repository *repo, const char *glob)
+{
+ git_refdb *refdb;
+
+ if (git_repository_refdb__weakptr(&refdb, repo) < 0)
+ return -1;
+
+ return git_refdb_iterator(out, refdb, glob);
+}
+
+int git_reference_next(git_reference **out, git_reference_iterator *iter)
+{
+ return git_refdb_iterator_next(out, iter);
+}
+
+int git_reference_next_name(const char **out, git_reference_iterator *iter)
+{
+ return git_refdb_iterator_next_name(out, iter);
+}
+
+void git_reference_iterator_free(git_reference_iterator *iter)
+{
+ if (iter == NULL)
+ return;
+
+ git_refdb_iterator_free(iter);
+}
+
+static int cb__reflist_add(const char *ref, void *data)
+{
+ char *name = git__strdup(ref);
+ GIT_ERROR_CHECK_ALLOC(name);
+ return git_vector_insert((git_vector *)data, name);
+}
+
+int git_reference_list(
+ git_strarray *array,
+ git_repository *repo)
+{
+ git_vector ref_list;
+
+ GIT_ASSERT_ARG(array);
+ GIT_ASSERT_ARG(repo);
+
+ array->strings = NULL;
+ array->count = 0;
+
+ if (git_vector_init(&ref_list, 8, NULL) < 0)
+ return -1;
+
+ if (git_reference_foreach_name(
+ repo, &cb__reflist_add, (void *)&ref_list) < 0) {
+ git_vector_free(&ref_list);
+ return -1;
+ }
+
+ array->strings = (char **)git_vector_detach(&array->count, NULL, &ref_list);
+
+ return 0;
+}
+
+static int is_valid_ref_char(char ch)
+{
+ if ((unsigned) ch <= ' ')
+ return 0;
+
+ switch (ch) {
+ case '~':
+ case '^':
+ case ':':
+ case '\\':
+ case '?':
+ case '[':
+ return 0;
+ default:
+ return 1;
+ }
+}
+
+static int ensure_segment_validity(const char *name, char may_contain_glob)
+{
+ const char *current = name;
+ char prev = '\0';
+ const int lock_len = (int)strlen(GIT_FILELOCK_EXTENSION);
+ int segment_len;
+
+ if (*current == '.')
+ return -1; /* Refname starts with "." */
+
+ for (current = name; ; current++) {
+ if (*current == '\0' || *current == '/')
+ break;
+
+ if (!is_valid_ref_char(*current))
+ return -1; /* Illegal character in refname */
+
+ if (prev == '.' && *current == '.')
+ return -1; /* Refname contains ".." */
+
+ if (prev == '@' && *current == '{')
+ return -1; /* Refname contains "@{" */
+
+ if (*current == '*') {
+ if (!may_contain_glob)
+ return -1;
+ may_contain_glob = 0;
+ }
+
+ prev = *current;
+ }
+
+ segment_len = (int)(current - name);
+
+ /* A refname component can not end with ".lock" */
+ if (segment_len >= lock_len &&
+ !memcmp(current - lock_len, GIT_FILELOCK_EXTENSION, lock_len))
+ return -1;
+
+ return segment_len;
+}
+
+static bool is_all_caps_and_underscore(const char *name, size_t len)
+{
+ size_t i;
+ char c;
+
+ GIT_ASSERT_ARG(name);
+ GIT_ASSERT_ARG(len > 0);
+
+ for (i = 0; i < len; i++)
+ {
+ c = name[i];
+ if ((c < 'A' || c > 'Z') && c != '_')
+ return false;
+ }
+
+ if (*name == '_' || name[len - 1] == '_')
+ return false;
+
+ return true;
+}
+
+/* Inspired from https://github.com/git/git/blob/f06d47e7e0d9db709ee204ed13a8a7486149f494/refs.c#L36-100 */
+int git_reference__normalize_name(
+ git_str *buf,
+ const char *name,
+ unsigned int flags)
+{
+ const char *current;
+ int segment_len, segments_count = 0, error = GIT_EINVALIDSPEC;
+ unsigned int process_flags;
+ bool normalize = (buf != NULL);
+ bool validate = (flags & GIT_REFERENCE_FORMAT__VALIDATION_DISABLE) == 0;
+
+#ifdef GIT_USE_ICONV
+ git_fs_path_iconv_t ic = GIT_PATH_ICONV_INIT;
+#endif
+
+ GIT_ASSERT_ARG(name);
+
+ process_flags = flags;
+ current = (char *)name;
+
+ if (validate && *current == '/')
+ goto cleanup;
+
+ if (normalize)
+ git_str_clear(buf);
+
+#ifdef GIT_USE_ICONV
+ if ((flags & GIT_REFERENCE_FORMAT__PRECOMPOSE_UNICODE) != 0) {
+ size_t namelen = strlen(current);
+ if ((error = git_fs_path_iconv_init_precompose(&ic)) < 0 ||
+ (error = git_fs_path_iconv(&ic, &current, &namelen)) < 0)
+ goto cleanup;
+ error = GIT_EINVALIDSPEC;
+ }
+#endif
+
+ if (!validate) {
+ git_str_sets(buf, current);
+
+ error = git_str_oom(buf) ? -1 : 0;
+ goto cleanup;
+ }
+
+ while (true) {
+ char may_contain_glob = process_flags & GIT_REFERENCE_FORMAT_REFSPEC_PATTERN;
+
+ segment_len = ensure_segment_validity(current, may_contain_glob);
+ if (segment_len < 0)
+ goto cleanup;
+
+ if (segment_len > 0) {
+ /*
+ * There may only be one glob in a pattern, thus we reset
+ * the pattern-flag in case the current segment has one.
+ */
+ if (memchr(current, '*', segment_len))
+ process_flags &= ~GIT_REFERENCE_FORMAT_REFSPEC_PATTERN;
+
+ if (normalize) {
+ size_t cur_len = git_str_len(buf);
+
+ git_str_joinpath(buf, git_str_cstr(buf), current);
+ git_str_truncate(buf,
+ cur_len + segment_len + (segments_count ? 1 : 0));
+
+ if (git_str_oom(buf)) {
+ error = -1;
+ goto cleanup;
+ }
+ }
+
+ segments_count++;
+ }
+
+ /* No empty segment is allowed when not normalizing */
+ if (segment_len == 0 && !normalize)
+ goto cleanup;
+
+ if (current[segment_len] == '\0')
+ break;
+
+ current += segment_len + 1;
+ }
+
+ /* A refname can not be empty */
+ if (segment_len == 0 && segments_count == 0)
+ goto cleanup;
+
+ /* A refname can not end with "." */
+ if (current[segment_len - 1] == '.')
+ goto cleanup;
+
+ /* A refname can not end with "/" */
+ if (current[segment_len - 1] == '/')
+ goto cleanup;
+
+ if ((segments_count == 1 ) && !(flags & GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL))
+ goto cleanup;
+
+ if ((segments_count == 1 ) &&
+ !(flags & GIT_REFERENCE_FORMAT_REFSPEC_SHORTHAND) &&
+ !(is_all_caps_and_underscore(name, (size_t)segment_len) ||
+ ((flags & GIT_REFERENCE_FORMAT_REFSPEC_PATTERN) && !strcmp("*", name))))
+ goto cleanup;
+
+ if ((segments_count > 1)
+ && (is_all_caps_and_underscore(name, strchr(name, '/') - name)))
+ goto cleanup;
+
+ error = 0;
+
+cleanup:
+ if (error == GIT_EINVALIDSPEC)
+ git_error_set(
+ GIT_ERROR_REFERENCE,
+ "the given reference name '%s' is not valid", name);
+
+ if (error && normalize)
+ git_str_dispose(buf);
+
+#ifdef GIT_USE_ICONV
+ git_fs_path_iconv_clear(&ic);
+#endif
+
+ return error;
+}
+
+int git_reference_normalize_name(
+ char *buffer_out,
+ size_t buffer_size,
+ const char *name,
+ unsigned int flags)
+{
+ git_str buf = GIT_STR_INIT;
+ int error;
+
+ if ((error = git_reference__normalize_name(&buf, name, flags)) < 0)
+ goto cleanup;
+
+ if (git_str_len(&buf) > buffer_size - 1) {
+ git_error_set(
+ GIT_ERROR_REFERENCE,
+ "the provided buffer is too short to hold the normalization of '%s'", name);
+ error = GIT_EBUFS;
+ goto cleanup;
+ }
+
+ if ((error = git_str_copy_cstr(buffer_out, buffer_size, &buf)) < 0)
+ goto cleanup;
+
+ error = 0;
+
+cleanup:
+ git_str_dispose(&buf);
+ return error;
+}
+
+#define GIT_REFERENCE_TYPEMASK (GIT_REFERENCE_DIRECT | GIT_REFERENCE_SYMBOLIC)
+
+int git_reference_cmp(
+ const git_reference *ref1,
+ const git_reference *ref2)
+{
+ git_reference_t type1, type2;
+
+ GIT_ASSERT_ARG(ref1);
+ GIT_ASSERT_ARG(ref2);
+
+ type1 = git_reference_type(ref1);
+ type2 = git_reference_type(ref2);
+
+ /* let's put symbolic refs before OIDs */
+ if (type1 != type2)
+ return (type1 == GIT_REFERENCE_SYMBOLIC) ? -1 : 1;
+
+ if (type1 == GIT_REFERENCE_SYMBOLIC)
+ return strcmp(ref1->target.symbolic, ref2->target.symbolic);
+
+ return git_oid__cmp(&ref1->target.oid, &ref2->target.oid);
+}
+
+/*
+ * Starting with the reference given by `ref_name`, follows symbolic
+ * references until a direct reference is found and updated the OID
+ * on that direct reference to `oid`.
+ */
+int git_reference__update_terminal(
+ git_repository *repo,
+ const char *ref_name,
+ const git_oid *oid,
+ const git_signature *sig,
+ const char *log_message)
+{
+ git_reference *ref = NULL, *ref2 = NULL;
+ git_signature *who = NULL;
+ git_refdb *refdb = NULL;
+ const git_signature *to_use;
+ int error = 0;
+
+ if (!sig && (error = git_reference__log_signature(&who, repo)) < 0)
+ goto out;
+
+ to_use = sig ? sig : who;
+
+ if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
+ goto out;
+
+ if ((error = git_refdb_resolve(&ref, refdb, ref_name, -1)) < 0) {
+ if (error == GIT_ENOTFOUND) {
+ git_error_clear();
+ error = reference__create(&ref2, repo, ref_name, oid, NULL, 0, to_use,
+ log_message, NULL, NULL);
+ }
+ goto out;
+ }
+
+ /* In case the resolved reference is symbolic, then it's a dangling symref. */
+ if (git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC) {
+ error = reference__create(&ref2, repo, ref->target.symbolic, oid, NULL, 0, to_use,
+ log_message, NULL, NULL);
+ } else {
+ error = reference__create(&ref2, repo, ref->name, oid, NULL, 1, to_use,
+ log_message, &ref->target.oid, NULL);
+ }
+
+out:
+ git_reference_free(ref2);
+ git_reference_free(ref);
+ git_signature_free(who);
+ return error;
+}
+
+static const char *commit_type(const git_commit *commit)
+{
+ unsigned int count = git_commit_parentcount(commit);
+
+ if (count >= 2)
+ return " (merge)";
+ else if (count == 0)
+ return " (initial)";
+ else
+ return "";
+}
+
+int git_reference__update_for_commit(
+ git_repository *repo,
+ git_reference *ref,
+ const char *ref_name,
+ const git_oid *id,
+ const char *operation)
+{
+ git_reference *ref_new = NULL;
+ git_commit *commit = NULL;
+ git_str reflog_msg = GIT_STR_INIT;
+ const git_signature *who;
+ int error;
+
+ if ((error = git_commit_lookup(&commit, repo, id)) < 0 ||
+ (error = git_str_printf(&reflog_msg, "%s%s: %s",
+ operation ? operation : "commit",
+ commit_type(commit),
+ git_commit_summary(commit))) < 0)
+ goto done;
+
+ who = git_commit_committer(commit);
+
+ if (ref) {
+ if ((error = ensure_is_an_updatable_direct_reference(ref)) < 0)
+ return error;
+
+ error = reference__create(&ref_new, repo, ref->name, id, NULL, 1, who,
+ git_str_cstr(&reflog_msg), &ref->target.oid, NULL);
+ }
+ else
+ error = git_reference__update_terminal(
+ repo, ref_name, id, who, git_str_cstr(&reflog_msg));
+
+done:
+ git_reference_free(ref_new);
+ git_str_dispose(&reflog_msg);
+ git_commit_free(commit);
+ return error;
+}
+
+int git_reference_has_log(git_repository *repo, const char *refname)
+{
+ int error;
+ git_refdb *refdb;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(refname);
+
+ if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
+ return error;
+
+ return git_refdb_has_log(refdb, refname);
+}
+
+int git_reference_ensure_log(git_repository *repo, const char *refname)
+{
+ int error;
+ git_refdb *refdb;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(refname);
+
+ if ((error = git_repository_refdb__weakptr(&refdb, repo)) < 0)
+ return error;
+
+ return git_refdb_ensure_log(refdb, refname);
+}
+
+int git_reference__is_branch(const char *ref_name)
+{
+ return git__prefixcmp(ref_name, GIT_REFS_HEADS_DIR) == 0;
+}
+
+int git_reference_is_branch(const git_reference *ref)
+{
+ GIT_ASSERT_ARG(ref);
+ return git_reference__is_branch(ref->name);
+}
+
+int git_reference__is_remote(const char *ref_name)
+{
+ return git__prefixcmp(ref_name, GIT_REFS_REMOTES_DIR) == 0;
+}
+
+int git_reference_is_remote(const git_reference *ref)
+{
+ GIT_ASSERT_ARG(ref);
+ return git_reference__is_remote(ref->name);
+}
+
+int git_reference__is_tag(const char *ref_name)
+{
+ return git__prefixcmp(ref_name, GIT_REFS_TAGS_DIR) == 0;
+}
+
+int git_reference_is_tag(const git_reference *ref)
+{
+ GIT_ASSERT_ARG(ref);
+ return git_reference__is_tag(ref->name);
+}
+
+int git_reference__is_note(const char *ref_name)
+{
+ return git__prefixcmp(ref_name, GIT_REFS_NOTES_DIR) == 0;
+}
+
+int git_reference_is_note(const git_reference *ref)
+{
+ GIT_ASSERT_ARG(ref);
+ return git_reference__is_note(ref->name);
+}
+
+static int peel_error(int error, const git_reference *ref, const char *msg)
+{
+ git_error_set(
+ GIT_ERROR_INVALID,
+ "the reference '%s' cannot be peeled - %s", git_reference_name(ref), msg);
+ return error;
+}
+
+int git_reference_peel(
+ git_object **peeled,
+ const git_reference *ref,
+ git_object_t target_type)
+{
+ const git_reference *resolved = NULL;
+ git_reference *allocated = NULL;
+ git_object *target = NULL;
+ int error;
+
+ GIT_ASSERT_ARG(ref);
+
+ if (ref->type == GIT_REFERENCE_DIRECT) {
+ resolved = ref;
+ } else {
+ if ((error = git_reference_resolve(&allocated, ref)) < 0)
+ return peel_error(error, ref, "Cannot resolve reference");
+
+ resolved = allocated;
+ }
+
+ /*
+ * If we try to peel an object to a tag, we cannot use
+ * the fully peeled object, as that will always resolve
+ * to a commit. So we only want to use the peeled value
+ * if it is not zero and the target is not a tag.
+ */
+ if (target_type != GIT_OBJECT_TAG && !git_oid_is_zero(&resolved->peel)) {
+ error = git_object_lookup(&target,
+ git_reference_owner(ref), &resolved->peel, GIT_OBJECT_ANY);
+ } else {
+ error = git_object_lookup(&target,
+ git_reference_owner(ref), &resolved->target.oid, GIT_OBJECT_ANY);
+ }
+
+ if (error < 0) {
+ peel_error(error, ref, "Cannot retrieve reference target");
+ goto cleanup;
+ }
+
+ if (target_type == GIT_OBJECT_ANY && git_object_type(target) != GIT_OBJECT_TAG)
+ error = git_object_dup(peeled, target);
+ else
+ error = git_object_peel(peeled, target, target_type);
+
+cleanup:
+ git_object_free(target);
+ git_reference_free(allocated);
+
+ return error;
+}
+
+int git_reference__name_is_valid(
+ int *valid,
+ const char *refname,
+ unsigned int flags)
+{
+ int error;
+
+ GIT_ASSERT(valid && refname);
+
+ *valid = 0;
+
+ error = git_reference__normalize_name(NULL, refname, flags);
+
+ if (!error)
+ *valid = 1;
+ else if (error == GIT_EINVALIDSPEC)
+ error = 0;
+
+ return error;
+}
+
+int git_reference_name_is_valid(int *valid, const char *refname)
+{
+ return git_reference__name_is_valid(valid, refname, GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL);
+}
+
+const char *git_reference__shorthand(const char *name)
+{
+ if (!git__prefixcmp(name, GIT_REFS_HEADS_DIR))
+ return name + strlen(GIT_REFS_HEADS_DIR);
+ else if (!git__prefixcmp(name, GIT_REFS_TAGS_DIR))
+ return name + strlen(GIT_REFS_TAGS_DIR);
+ else if (!git__prefixcmp(name, GIT_REFS_REMOTES_DIR))
+ return name + strlen(GIT_REFS_REMOTES_DIR);
+ else if (!git__prefixcmp(name, GIT_REFS_DIR))
+ return name + strlen(GIT_REFS_DIR);
+
+ /* No shorthands are available, so just return the name. */
+ return name;
+}
+
+const char *git_reference_shorthand(const git_reference *ref)
+{
+ return git_reference__shorthand(ref->name);
+}
+
+int git_reference__is_unborn_head(bool *unborn, const git_reference *ref, git_repository *repo)
+{
+ int error;
+ git_reference *tmp_ref;
+
+ GIT_ASSERT_ARG(unborn);
+ GIT_ASSERT_ARG(ref);
+ GIT_ASSERT_ARG(repo);
+
+ if (ref->type == GIT_REFERENCE_DIRECT) {
+ *unborn = 0;
+ return 0;
+ }
+
+ error = git_reference_lookup_resolved(&tmp_ref, repo, ref->name, -1);
+ git_reference_free(tmp_ref);
+
+ if (error != 0 && error != GIT_ENOTFOUND)
+ return error;
+ else if (error == GIT_ENOTFOUND && git__strcmp(ref->name, GIT_HEAD_FILE) == 0)
+ *unborn = true;
+ else
+ *unborn = false;
+
+ return 0;
+}
+
+/* Deprecated functions */
+
+#ifndef GIT_DEPRECATE_HARD
+
+int git_reference_is_valid_name(const char *refname)
+{
+ int valid = 0;
+
+ git_reference__name_is_valid(&valid, refname, GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL);
+
+ return valid;
+}
+
+#endif
diff --git a/src/libgit2/refs.h b/src/libgit2/refs.h
new file mode 100644
index 0000000..cb888bf
--- /dev/null
+++ b/src/libgit2/refs.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_refs_h__
+#define INCLUDE_refs_h__
+
+#include "common.h"
+
+#include "git2/oid.h"
+#include "git2/refs.h"
+#include "git2/refdb.h"
+#include "strmap.h"
+#include "str.h"
+#include "oid.h"
+
+extern bool git_reference__enable_symbolic_ref_target_validation;
+
+#define GIT_REFS_DIR "refs/"
+#define GIT_REFS_HEADS_DIR GIT_REFS_DIR "heads/"
+#define GIT_REFS_TAGS_DIR GIT_REFS_DIR "tags/"
+#define GIT_REFS_REMOTES_DIR GIT_REFS_DIR "remotes/"
+#define GIT_REFS_NOTES_DIR GIT_REFS_DIR "notes/"
+#define GIT_REFS_DIR_MODE 0777
+#define GIT_REFS_FILE_MODE 0666
+
+#define GIT_RENAMED_REF_FILE GIT_REFS_DIR "RENAMED-REF"
+
+#define GIT_SYMREF "ref: "
+#define GIT_PACKEDREFS_FILE "packed-refs"
+#define GIT_PACKEDREFS_HEADER "# pack-refs with: peeled fully-peeled sorted "
+#define GIT_PACKEDREFS_FILE_MODE 0666
+
+#define GIT_HEAD_FILE "HEAD"
+#define GIT_ORIG_HEAD_FILE "ORIG_HEAD"
+#define GIT_FETCH_HEAD_FILE "FETCH_HEAD"
+#define GIT_MERGE_HEAD_FILE "MERGE_HEAD"
+#define GIT_REVERT_HEAD_FILE "REVERT_HEAD"
+#define GIT_CHERRYPICK_HEAD_FILE "CHERRY_PICK_HEAD"
+#define GIT_BISECT_LOG_FILE "BISECT_LOG"
+#define GIT_REBASE_MERGE_DIR "rebase-merge/"
+#define GIT_REBASE_MERGE_INTERACTIVE_FILE GIT_REBASE_MERGE_DIR "interactive"
+#define GIT_REBASE_APPLY_DIR "rebase-apply/"
+#define GIT_REBASE_APPLY_REBASING_FILE GIT_REBASE_APPLY_DIR "rebasing"
+#define GIT_REBASE_APPLY_APPLYING_FILE GIT_REBASE_APPLY_DIR "applying"
+
+#define GIT_SEQUENCER_DIR "sequencer/"
+#define GIT_SEQUENCER_HEAD_FILE GIT_SEQUENCER_DIR "head"
+#define GIT_SEQUENCER_OPTIONS_FILE GIT_SEQUENCER_DIR "options"
+#define GIT_SEQUENCER_TODO_FILE GIT_SEQUENCER_DIR "todo"
+
+#define GIT_STASH_FILE "stash"
+#define GIT_REFS_STASH_FILE GIT_REFS_DIR GIT_STASH_FILE
+
+#define GIT_REFERENCE_FORMAT__PRECOMPOSE_UNICODE (1u << 16)
+#define GIT_REFERENCE_FORMAT__VALIDATION_DISABLE (1u << 15)
+
+#define GIT_REFNAME_MAX 1024
+
+typedef char git_refname_t[GIT_REFNAME_MAX];
+
+struct git_reference {
+ git_refdb *db;
+ git_reference_t type;
+
+ union {
+ git_oid oid;
+ char *symbolic;
+ } target;
+
+ git_oid peel;
+ char name[GIT_FLEX_ARRAY];
+};
+
+/**
+ * Reallocate the reference with a new name
+ *
+ * Note that this is a dangerous operation, as on success, all existing
+ * pointers to the old reference will now be dangling. Only call this on objects
+ * you control, possibly using `git_reference_dup`.
+ */
+git_reference *git_reference__realloc(git_reference **ptr_to_ref, const char *name);
+
+int git_reference__normalize_name(git_str *buf, const char *name, unsigned int flags);
+int git_reference__update_terminal(git_repository *repo, const char *ref_name, const git_oid *oid, const git_signature *sig, const char *log_message);
+int git_reference__name_is_valid(int *valid, const char *name, unsigned int flags);
+int git_reference__is_branch(const char *ref_name);
+int git_reference__is_remote(const char *ref_name);
+int git_reference__is_tag(const char *ref_name);
+int git_reference__is_note(const char *ref_name);
+const char *git_reference__shorthand(const char *name);
+
+/**
+ * Lookup a reference by name and try to resolve to an OID.
+ *
+ * You can control how many dereferences this will attempt to resolve the
+ * reference with the `max_deref` parameter, or pass -1 to use a sane
+ * default. If you pass 0 for `max_deref`, this will not attempt to resolve
+ * the reference. For any value of `max_deref` other than 0, not
+ * successfully resolving the reference will be reported as an error.
+
+ * The generated reference must be freed by the user.
+ *
+ * @param reference_out Pointer to the looked-up reference
+ * @param repo The repository to look up the reference
+ * @param name The long name for the reference (e.g. HEAD, ref/heads/master, refs/tags/v0.1.0, ...)
+ * @param max_deref Maximum number of dereferences to make of symbolic refs, 0 means simple lookup, < 0 means use default reasonable value
+ * @return 0 on success or < 0 on error; not being able to resolve the reference is an error unless 0 was passed for max_deref
+ */
+int git_reference_lookup_resolved(
+ git_reference **reference_out,
+ git_repository *repo,
+ const char *name,
+ int max_deref);
+
+int git_reference__log_signature(git_signature **out, git_repository *repo);
+
+/** Update a reference after a commit. */
+int git_reference__update_for_commit(
+ git_repository *repo,
+ git_reference *ref,
+ const char *ref_name,
+ const git_oid *id,
+ const char *operation);
+
+int git_reference__is_unborn_head(bool *unborn, const git_reference *ref, git_repository *repo);
+
+#endif
diff --git a/src/libgit2/refspec.c b/src/libgit2/refspec.c
new file mode 100644
index 0000000..f0a0c2b
--- /dev/null
+++ b/src/libgit2/refspec.c
@@ -0,0 +1,420 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "refspec.h"
+
+#include "buf.h"
+#include "refs.h"
+#include "util.h"
+#include "vector.h"
+#include "wildmatch.h"
+
+int git_refspec__parse(git_refspec *refspec, const char *input, bool is_fetch)
+{
+ /* Ported from https://github.com/git/git/blob/f06d47e7e0d9db709ee204ed13a8a7486149f494/remote.c#L518-636 */
+
+ size_t llen;
+ int is_glob = 0;
+ const char *lhs, *rhs;
+ int valid = 0;
+ unsigned int flags;
+
+ GIT_ASSERT_ARG(refspec);
+ GIT_ASSERT_ARG(input);
+
+ memset(refspec, 0x0, sizeof(git_refspec));
+ refspec->push = !is_fetch;
+
+ lhs = input;
+ if (*lhs == '+') {
+ refspec->force = 1;
+ lhs++;
+ }
+
+ rhs = strrchr(lhs, ':');
+
+ /*
+ * Before going on, special case ":" (or "+:") as a refspec
+ * for matching refs.
+ */
+ if (!is_fetch && rhs == lhs && rhs[1] == '\0') {
+ refspec->matching = 1;
+ refspec->string = git__strdup(input);
+ GIT_ERROR_CHECK_ALLOC(refspec->string);
+ refspec->src = git__strdup("");
+ GIT_ERROR_CHECK_ALLOC(refspec->src);
+ refspec->dst = git__strdup("");
+ GIT_ERROR_CHECK_ALLOC(refspec->dst);
+ return 0;
+ }
+
+ if (rhs) {
+ size_t rlen = strlen(++rhs);
+ if (rlen || !is_fetch) {
+ is_glob = (1 <= rlen && strchr(rhs, '*'));
+ refspec->dst = git__strndup(rhs, rlen);
+ }
+ }
+
+ llen = (rhs ? (size_t)(rhs - lhs - 1) : strlen(lhs));
+ if (1 <= llen && memchr(lhs, '*', llen)) {
+ if ((rhs && !is_glob) || (!rhs && is_fetch))
+ goto invalid;
+ is_glob = 1;
+ } else if (rhs && is_glob)
+ goto invalid;
+
+ refspec->pattern = is_glob;
+ refspec->src = git__strndup(lhs, llen);
+ flags = GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL |
+ GIT_REFERENCE_FORMAT_REFSPEC_SHORTHAND |
+ (is_glob ? GIT_REFERENCE_FORMAT_REFSPEC_PATTERN : 0);
+
+ if (is_fetch) {
+ /*
+ * LHS
+ * - empty is allowed; it means HEAD.
+ * - otherwise it must be a valid looking ref.
+ */
+ if (!*refspec->src)
+ ; /* empty is ok */
+ else if (git_reference__name_is_valid(&valid, refspec->src, flags) < 0)
+ goto on_error;
+ else if (!valid)
+ goto invalid;
+
+ /*
+ * RHS
+ * - missing is ok, and is same as empty.
+ * - empty is ok; it means not to store.
+ * - otherwise it must be a valid looking ref.
+ */
+ if (!refspec->dst)
+ ; /* ok */
+ else if (!*refspec->dst)
+ ; /* ok */
+ else if (git_reference__name_is_valid(&valid, refspec->dst, flags) < 0)
+ goto on_error;
+ else if (!valid)
+ goto invalid;
+ } else {
+ /*
+ * LHS
+ * - empty is allowed; it means delete.
+ * - when wildcarded, it must be a valid looking ref.
+ * - otherwise, it must be an extended SHA-1, but
+ * there is no existing way to validate this.
+ */
+ if (!*refspec->src)
+ ; /* empty is ok */
+ else if (is_glob) {
+ if (git_reference__name_is_valid(&valid, refspec->src, flags) < 0)
+ goto on_error;
+ else if (!valid)
+ goto invalid;
+ }
+ else {
+ ; /* anything goes, for now */
+ }
+
+ /*
+ * RHS
+ * - missing is allowed, but LHS then must be a
+ * valid looking ref.
+ * - empty is not allowed.
+ * - otherwise it must be a valid looking ref.
+ */
+ if (!refspec->dst) {
+ if (git_reference__name_is_valid(&valid, refspec->src, flags) < 0)
+ goto on_error;
+ else if (!valid)
+ goto invalid;
+ } else if (!*refspec->dst) {
+ goto invalid;
+ } else {
+ if (git_reference__name_is_valid(&valid, refspec->dst, flags) < 0)
+ goto on_error;
+ else if (!valid)
+ goto invalid;
+ }
+
+ /* if the RHS is empty, then it's a copy of the LHS */
+ if (!refspec->dst) {
+ refspec->dst = git__strdup(refspec->src);
+ GIT_ERROR_CHECK_ALLOC(refspec->dst);
+ }
+ }
+
+ refspec->string = git__strdup(input);
+ GIT_ERROR_CHECK_ALLOC(refspec->string);
+
+ return 0;
+
+invalid:
+ git_error_set(GIT_ERROR_INVALID,
+ "'%s' is not a valid refspec.", input);
+ git_refspec__dispose(refspec);
+ return GIT_EINVALIDSPEC;
+
+on_error:
+ git_refspec__dispose(refspec);
+ return -1;
+}
+
+void git_refspec__dispose(git_refspec *refspec)
+{
+ if (refspec == NULL)
+ return;
+
+ git__free(refspec->src);
+ git__free(refspec->dst);
+ git__free(refspec->string);
+
+ memset(refspec, 0x0, sizeof(git_refspec));
+}
+
+int git_refspec_parse(git_refspec **out_refspec, const char *input, int is_fetch)
+{
+ git_refspec *refspec;
+ GIT_ASSERT_ARG(out_refspec);
+ GIT_ASSERT_ARG(input);
+
+ *out_refspec = NULL;
+
+ refspec = git__malloc(sizeof(git_refspec));
+ GIT_ERROR_CHECK_ALLOC(refspec);
+
+ if (git_refspec__parse(refspec, input, !!is_fetch) != 0) {
+ git__free(refspec);
+ return -1;
+ }
+
+ *out_refspec = refspec;
+ return 0;
+}
+
+void git_refspec_free(git_refspec *refspec)
+{
+ git_refspec__dispose(refspec);
+ git__free(refspec);
+}
+
+const char *git_refspec_src(const git_refspec *refspec)
+{
+ return refspec == NULL ? NULL : refspec->src;
+}
+
+const char *git_refspec_dst(const git_refspec *refspec)
+{
+ return refspec == NULL ? NULL : refspec->dst;
+}
+
+const char *git_refspec_string(const git_refspec *refspec)
+{
+ return refspec == NULL ? NULL : refspec->string;
+}
+
+int git_refspec_force(const git_refspec *refspec)
+{
+ GIT_ASSERT_ARG(refspec);
+
+ return refspec->force;
+}
+
+int git_refspec_src_matches(const git_refspec *refspec, const char *refname)
+{
+ if (refspec == NULL || refspec->src == NULL)
+ return false;
+
+ return (wildmatch(refspec->src, refname, 0) == 0);
+}
+
+int git_refspec_dst_matches(const git_refspec *refspec, const char *refname)
+{
+ if (refspec == NULL || refspec->dst == NULL)
+ return false;
+
+ return (wildmatch(refspec->dst, refname, 0) == 0);
+}
+
+static int refspec_transform(
+ git_str *out, const char *from, const char *to, const char *name)
+{
+ const char *from_star, *to_star;
+ size_t replacement_len, star_offset;
+
+ git_str_clear(out);
+
+ /*
+ * There are two parts to each side of a refspec, the bit
+ * before the star and the bit after it. The star can be in
+ * the middle of the pattern, so we need to look at each bit
+ * individually.
+ */
+ from_star = strchr(from, '*');
+ to_star = strchr(to, '*');
+
+ GIT_ASSERT(from_star && to_star);
+
+ /* star offset, both in 'from' and in 'name' */
+ star_offset = from_star - from;
+
+ /* the first half is copied over */
+ git_str_put(out, to, to_star - to);
+
+ /*
+ * Copy over the name, but exclude the trailing part in "from" starting
+ * after the glob
+ */
+ replacement_len = strlen(name + star_offset) - strlen(from_star + 1);
+ git_str_put(out, name + star_offset, replacement_len);
+
+ return git_str_puts(out, to_star + 1);
+}
+
+int git_refspec_transform(git_buf *out, const git_refspec *spec, const char *name)
+{
+ GIT_BUF_WRAP_PRIVATE(out, git_refspec__transform, spec, name);
+}
+
+int git_refspec__transform(git_str *out, const git_refspec *spec, const char *name)
+{
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(spec);
+ GIT_ASSERT_ARG(name);
+
+ if (!git_refspec_src_matches(spec, name)) {
+ git_error_set(GIT_ERROR_INVALID, "ref '%s' doesn't match the source", name);
+ return -1;
+ }
+
+ if (!spec->pattern)
+ return git_str_puts(out, spec->dst ? spec->dst : "");
+
+ return refspec_transform(out, spec->src, spec->dst, name);
+}
+
+int git_refspec_rtransform(git_buf *out, const git_refspec *spec, const char *name)
+{
+ GIT_BUF_WRAP_PRIVATE(out, git_refspec__rtransform, spec, name);
+}
+
+int git_refspec__rtransform(git_str *out, const git_refspec *spec, const char *name)
+{
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(spec);
+ GIT_ASSERT_ARG(name);
+
+ if (!git_refspec_dst_matches(spec, name)) {
+ git_error_set(GIT_ERROR_INVALID, "ref '%s' doesn't match the destination", name);
+ return -1;
+ }
+
+ if (!spec->pattern)
+ return git_str_puts(out, spec->src);
+
+ return refspec_transform(out, spec->dst, spec->src, name);
+}
+
+int git_refspec__serialize(git_str *out, const git_refspec *refspec)
+{
+ if (refspec->force)
+ git_str_putc(out, '+');
+
+ git_str_printf(out, "%s:%s",
+ refspec->src != NULL ? refspec->src : "",
+ refspec->dst != NULL ? refspec->dst : "");
+
+ return git_str_oom(out) == false;
+}
+
+int git_refspec_is_wildcard(const git_refspec *spec)
+{
+ GIT_ASSERT_ARG(spec);
+ GIT_ASSERT_ARG(spec->src);
+
+ return (spec->src[strlen(spec->src) - 1] == '*');
+}
+
+git_direction git_refspec_direction(const git_refspec *spec)
+{
+ GIT_ASSERT_ARG(spec);
+
+ return spec->push;
+}
+
+int git_refspec__dwim_one(git_vector *out, git_refspec *spec, git_vector *refs)
+{
+ git_str buf = GIT_STR_INIT;
+ size_t j, pos;
+ git_remote_head key;
+ git_refspec *cur;
+
+ const char *formatters[] = {
+ GIT_REFS_DIR "%s",
+ GIT_REFS_TAGS_DIR "%s",
+ GIT_REFS_HEADS_DIR "%s",
+ NULL
+ };
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(spec);
+ GIT_ASSERT_ARG(refs);
+
+ cur = git__calloc(1, sizeof(git_refspec));
+ GIT_ERROR_CHECK_ALLOC(cur);
+
+ cur->force = spec->force;
+ cur->push = spec->push;
+ cur->pattern = spec->pattern;
+ cur->matching = spec->matching;
+ cur->string = git__strdup(spec->string);
+
+ /* shorthand on the lhs */
+ if (git__prefixcmp(spec->src, GIT_REFS_DIR)) {
+ for (j = 0; formatters[j]; j++) {
+ git_str_clear(&buf);
+ git_str_printf(&buf, formatters[j], spec->src);
+ GIT_ERROR_CHECK_ALLOC_STR(&buf);
+
+ key.name = (char *) git_str_cstr(&buf);
+ if (!git_vector_search(&pos, refs, &key)) {
+ /* we found something to match the shorthand, set src to that */
+ cur->src = git_str_detach(&buf);
+ }
+ }
+ }
+
+ /* No shorthands found, copy over the name */
+ if (cur->src == NULL && spec->src != NULL) {
+ cur->src = git__strdup(spec->src);
+ GIT_ERROR_CHECK_ALLOC(cur->src);
+ }
+
+ if (spec->dst && git__prefixcmp(spec->dst, GIT_REFS_DIR)) {
+ /* if it starts with "remotes" then we just prepend "refs/" */
+ if (!git__prefixcmp(spec->dst, "remotes/")) {
+ git_str_puts(&buf, GIT_REFS_DIR);
+ } else {
+ git_str_puts(&buf, GIT_REFS_HEADS_DIR);
+ }
+
+ git_str_puts(&buf, spec->dst);
+ GIT_ERROR_CHECK_ALLOC_STR(&buf);
+
+ cur->dst = git_str_detach(&buf);
+ }
+
+ git_str_dispose(&buf);
+
+ if (cur->dst == NULL && spec->dst != NULL) {
+ cur->dst = git__strdup(spec->dst);
+ GIT_ERROR_CHECK_ALLOC(cur->dst);
+ }
+
+ return git_vector_insert(out, cur);
+}
diff --git a/src/libgit2/refspec.h b/src/libgit2/refspec.h
new file mode 100644
index 0000000..bf4f7fc
--- /dev/null
+++ b/src/libgit2/refspec.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_refspec_h__
+#define INCLUDE_refspec_h__
+
+#include "common.h"
+
+#include "git2/refspec.h"
+#include "str.h"
+#include "vector.h"
+
+struct git_refspec {
+ char *string;
+ char *src;
+ char *dst;
+ unsigned int force :1,
+ push : 1,
+ pattern :1,
+ matching :1;
+};
+
+#define GIT_REFSPEC_TAGS "refs/tags/*:refs/tags/*"
+
+int git_refspec__transform(git_str *out, const git_refspec *spec, const char *name);
+int git_refspec__rtransform(git_str *out, const git_refspec *spec, const char *name);
+
+int git_refspec__parse(
+ struct git_refspec *refspec,
+ const char *str,
+ bool is_fetch);
+
+void git_refspec__dispose(git_refspec *refspec);
+
+int git_refspec__serialize(git_str *out, const git_refspec *refspec);
+
+/**
+ * Determines if a refspec is a wildcard refspec.
+ *
+ * @param spec the refspec
+ * @return 1 if the refspec is a wildcard, 0 otherwise
+ */
+int git_refspec_is_wildcard(const git_refspec *spec);
+
+/**
+ * DWIM `spec` with `refs` existing on the remote, append the dwim'ed
+ * result in `out`.
+ */
+int git_refspec__dwim_one(git_vector *out, git_refspec *spec, git_vector *refs);
+
+#endif
diff --git a/src/libgit2/remote.c b/src/libgit2/remote.c
new file mode 100644
index 0000000..fee2a7f
--- /dev/null
+++ b/src/libgit2/remote.c
@@ -0,0 +1,3097 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "remote.h"
+
+#include "buf.h"
+#include "branch.h"
+#include "config.h"
+#include "repository.h"
+#include "fetch.h"
+#include "refs.h"
+#include "refspec.h"
+#include "fetchhead.h"
+#include "push.h"
+#include "proxy.h"
+#include "strarray.h"
+
+#include "git2/config.h"
+#include "git2/types.h"
+#include "git2/oid.h"
+#include "git2/net.h"
+#include "transports/smart.h"
+
+#define CONFIG_URL_FMT "remote.%s.url"
+#define CONFIG_PUSHURL_FMT "remote.%s.pushurl"
+#define CONFIG_FETCH_FMT "remote.%s.fetch"
+#define CONFIG_PUSH_FMT "remote.%s.push"
+#define CONFIG_TAGOPT_FMT "remote.%s.tagopt"
+
+static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs);
+static int lookup_remote_prune_config(git_remote *remote, git_config *config, const char *name);
+static int apply_insteadof(char **out, git_config *config, const char *url, int direction, bool use_default_if_empty);
+
+static int add_refspec_to(git_vector *vector, const char *string, bool is_fetch)
+{
+ git_refspec *spec;
+
+ spec = git__calloc(1, sizeof(git_refspec));
+ GIT_ERROR_CHECK_ALLOC(spec);
+
+ if (git_refspec__parse(spec, string, is_fetch) < 0) {
+ git__free(spec);
+ return -1;
+ }
+
+ spec->push = !is_fetch;
+ if (git_vector_insert(vector, spec) < 0) {
+ git_refspec__dispose(spec);
+ git__free(spec);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int add_refspec(git_remote *remote, const char *string, bool is_fetch)
+{
+ return add_refspec_to(&remote->refspecs, string, is_fetch);
+}
+
+static int download_tags_value(git_remote *remote, git_config *cfg)
+{
+ git_config_entry *ce;
+ git_str buf = GIT_STR_INIT;
+ int error;
+
+ if (git_str_printf(&buf, "remote.%s.tagopt", remote->name) < 0)
+ return -1;
+
+ error = git_config__lookup_entry(&ce, cfg, git_str_cstr(&buf), false);
+ git_str_dispose(&buf);
+
+ if (!error && ce && ce->value) {
+ if (!strcmp(ce->value, "--no-tags"))
+ remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE;
+ else if (!strcmp(ce->value, "--tags"))
+ remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL;
+ }
+
+ git_config_entry_free(ce);
+ return error;
+}
+
+static int ensure_remote_name_is_valid(const char *name)
+{
+ int valid, error;
+
+ error = git_remote_name_is_valid(&valid, name);
+
+ if (!error && !valid) {
+ git_error_set(
+ GIT_ERROR_CONFIG,
+ "'%s' is not a valid remote name.", name ? name : "(null)");
+ error = GIT_EINVALIDSPEC;
+ }
+
+ return error;
+}
+
+static int write_add_refspec(git_repository *repo, const char *name, const char *refspec, bool fetch)
+{
+ git_config *cfg;
+ git_str var = GIT_STR_INIT;
+ git_refspec spec;
+ const char *fmt;
+ int error;
+
+ if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
+ return error;
+
+ fmt = fetch ? CONFIG_FETCH_FMT : CONFIG_PUSH_FMT;
+
+ if ((error = ensure_remote_name_is_valid(name)) < 0)
+ return error;
+
+ if ((error = git_refspec__parse(&spec, refspec, fetch)) < 0)
+ return error;
+
+ git_refspec__dispose(&spec);
+
+ if ((error = git_str_printf(&var, fmt, name)) < 0)
+ return error;
+
+ /*
+ * "$^" is an unmatchable regexp: it will not match anything at all, so
+ * all values will be considered new and we will not replace any
+ * present value.
+ */
+ if ((error = git_config_set_multivar(cfg, var.ptr, "$^", refspec)) < 0) {
+ goto cleanup;
+ }
+
+cleanup:
+ git_str_dispose(&var);
+ return 0;
+}
+
+static int canonicalize_url(git_str *out, const char *in)
+{
+ if (in == NULL || strlen(in) == 0) {
+ git_error_set(GIT_ERROR_INVALID, "cannot set empty URL");
+ return GIT_EINVALIDSPEC;
+ }
+
+#ifdef GIT_WIN32
+ /* Given a UNC path like \\server\path, we need to convert this
+ * to //server/path for compatibility with core git.
+ */
+ if (in[0] == '\\' && in[1] == '\\' &&
+ (git__isalpha(in[2]) || git__isdigit(in[2]))) {
+ const char *c;
+ for (c = in; *c; c++)
+ git_str_putc(out, *c == '\\' ? '/' : *c);
+
+ return git_str_oom(out) ? -1 : 0;
+ }
+#endif
+
+ return git_str_puts(out, in);
+}
+
+static int default_fetchspec_for_name(git_str *buf, const char *name)
+{
+ if (git_str_printf(buf, "+refs/heads/*:refs/remotes/%s/*", name) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int ensure_remote_doesnot_exist(git_repository *repo, const char *name)
+{
+ int error;
+ git_remote *remote;
+
+ error = git_remote_lookup(&remote, repo, name);
+
+ if (error == GIT_ENOTFOUND)
+ return 0;
+
+ if (error < 0)
+ return error;
+
+ git_remote_free(remote);
+
+ git_error_set(GIT_ERROR_CONFIG, "remote '%s' already exists", name);
+
+ return GIT_EEXISTS;
+}
+
+int git_remote_create_options_init(git_remote_create_options *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_remote_create_options, GIT_REMOTE_CREATE_OPTIONS_INIT);
+ return 0;
+}
+
+#ifndef GIT_DEPRECATE_HARD
+int git_remote_create_init_options(git_remote_create_options *opts, unsigned int version)
+{
+ return git_remote_create_options_init(opts, version);
+}
+#endif
+
+int git_remote_create_with_opts(git_remote **out, const char *url, const git_remote_create_options *opts)
+{
+ git_remote *remote = NULL;
+ git_config *config_ro = NULL, *config_rw;
+ git_str canonical_url = GIT_STR_INIT;
+ git_str var = GIT_STR_INIT;
+ git_str specbuf = GIT_STR_INIT;
+ const git_remote_create_options dummy_opts = GIT_REMOTE_CREATE_OPTIONS_INIT;
+ int error = -1;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(url);
+
+ if (!opts) {
+ opts = &dummy_opts;
+ }
+
+ GIT_ERROR_CHECK_VERSION(opts, GIT_REMOTE_CREATE_OPTIONS_VERSION, "git_remote_create_options");
+
+ if (opts->name != NULL) {
+ if ((error = ensure_remote_name_is_valid(opts->name)) < 0)
+ return error;
+
+ if (opts->repository &&
+ (error = ensure_remote_doesnot_exist(opts->repository, opts->name)) < 0)
+ return error;
+ }
+
+ if (opts->repository) {
+ if ((error = git_repository_config_snapshot(&config_ro, opts->repository)) < 0)
+ goto on_error;
+ }
+
+ remote = git__calloc(1, sizeof(git_remote));
+ GIT_ERROR_CHECK_ALLOC(remote);
+
+ remote->repo = opts->repository;
+
+ if ((error = git_vector_init(&remote->refs, 8, NULL)) < 0 ||
+ (error = canonicalize_url(&canonical_url, url)) < 0)
+ goto on_error;
+
+ if (opts->repository && !(opts->flags & GIT_REMOTE_CREATE_SKIP_INSTEADOF)) {
+ if ((error = apply_insteadof(&remote->url, config_ro, canonical_url.ptr, GIT_DIRECTION_FETCH, true)) < 0 ||
+ (error = apply_insteadof(&remote->pushurl, config_ro, canonical_url.ptr, GIT_DIRECTION_PUSH, false)) < 0)
+ goto on_error;
+ } else {
+ remote->url = git__strdup(canonical_url.ptr);
+ GIT_ERROR_CHECK_ALLOC(remote->url);
+ }
+
+ if (opts->name != NULL) {
+ remote->name = git__strdup(opts->name);
+ GIT_ERROR_CHECK_ALLOC(remote->name);
+
+ if (opts->repository &&
+ ((error = git_str_printf(&var, CONFIG_URL_FMT, opts->name)) < 0 ||
+ (error = git_repository_config__weakptr(&config_rw, opts->repository)) < 0 ||
+ (error = git_config_set_string(config_rw, var.ptr, canonical_url.ptr)) < 0))
+ goto on_error;
+ }
+
+ if (opts->fetchspec != NULL ||
+ (opts->name && !(opts->flags & GIT_REMOTE_CREATE_SKIP_DEFAULT_FETCHSPEC))) {
+ const char *fetch = NULL;
+ if (opts->fetchspec) {
+ fetch = opts->fetchspec;
+ } else {
+ if ((error = default_fetchspec_for_name(&specbuf, opts->name)) < 0)
+ goto on_error;
+
+ fetch = git_str_cstr(&specbuf);
+ }
+
+ if ((error = add_refspec(remote, fetch, true)) < 0)
+ goto on_error;
+
+ /* only write for named remotes with a repository */
+ if (opts->repository && opts->name &&
+ ((error = write_add_refspec(opts->repository, opts->name, fetch, true)) < 0 ||
+ (error = lookup_remote_prune_config(remote, config_ro, opts->name)) < 0))
+ goto on_error;
+
+ /* Move the data over to where the matching functions can find them */
+ if ((error = dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs)) < 0)
+ goto on_error;
+ }
+
+ /* A remote without a name doesn't download tags */
+ if (!opts->name)
+ remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_NONE;
+ else
+ remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_AUTO;
+
+
+ git_str_dispose(&var);
+
+ *out = remote;
+ error = 0;
+
+on_error:
+ if (error)
+ git_remote_free(remote);
+
+ git_config_free(config_ro);
+ git_str_dispose(&specbuf);
+ git_str_dispose(&canonical_url);
+ git_str_dispose(&var);
+ return error;
+}
+
+int git_remote_create(git_remote **out, git_repository *repo, const char *name, const char *url)
+{
+ git_str buf = GIT_STR_INIT;
+ int error;
+ git_remote_create_options opts = GIT_REMOTE_CREATE_OPTIONS_INIT;
+
+ /* Those 2 tests are duplicated here because of backward-compatibility */
+ if ((error = ensure_remote_name_is_valid(name)) < 0)
+ return error;
+
+ if (canonicalize_url(&buf, url) < 0)
+ return GIT_ERROR;
+
+ git_str_clear(&buf);
+
+ opts.repository = repo;
+ opts.name = name;
+
+ error = git_remote_create_with_opts(out, url, &opts);
+
+ git_str_dispose(&buf);
+
+ return error;
+}
+
+int git_remote_create_with_fetchspec(git_remote **out, git_repository *repo, const char *name, const char *url, const char *fetch)
+{
+ int error;
+ git_remote_create_options opts = GIT_REMOTE_CREATE_OPTIONS_INIT;
+
+ if ((error = ensure_remote_name_is_valid(name)) < 0)
+ return error;
+
+ opts.repository = repo;
+ opts.name = name;
+ opts.fetchspec = fetch;
+ opts.flags = GIT_REMOTE_CREATE_SKIP_DEFAULT_FETCHSPEC;
+
+ return git_remote_create_with_opts(out, url, &opts);
+}
+
+int git_remote_create_anonymous(git_remote **out, git_repository *repo, const char *url)
+{
+ git_remote_create_options opts = GIT_REMOTE_CREATE_OPTIONS_INIT;
+
+ opts.repository = repo;
+
+ return git_remote_create_with_opts(out, url, &opts);
+}
+
+int git_remote_create_detached(git_remote **out, const char *url)
+{
+ return git_remote_create_with_opts(out, url, NULL);
+}
+
+int git_remote_dup(git_remote **dest, git_remote *source)
+{
+ size_t i;
+ int error = 0;
+ git_refspec *spec;
+ git_remote *remote = git__calloc(1, sizeof(git_remote));
+ GIT_ERROR_CHECK_ALLOC(remote);
+
+ if (source->name != NULL) {
+ remote->name = git__strdup(source->name);
+ GIT_ERROR_CHECK_ALLOC(remote->name);
+ }
+
+ if (source->url != NULL) {
+ remote->url = git__strdup(source->url);
+ GIT_ERROR_CHECK_ALLOC(remote->url);
+ }
+
+ if (source->pushurl != NULL) {
+ remote->pushurl = git__strdup(source->pushurl);
+ GIT_ERROR_CHECK_ALLOC(remote->pushurl);
+ }
+
+ remote->repo = source->repo;
+ remote->download_tags = source->download_tags;
+ remote->prune_refs = source->prune_refs;
+
+ if (git_vector_init(&remote->refs, 32, NULL) < 0 ||
+ git_vector_init(&remote->refspecs, 2, NULL) < 0 ||
+ git_vector_init(&remote->active_refspecs, 2, NULL) < 0) {
+ error = -1;
+ goto cleanup;
+ }
+
+ git_vector_foreach(&source->refspecs, i, spec) {
+ if ((error = add_refspec(remote, spec->string, !spec->push)) < 0)
+ goto cleanup;
+ }
+
+ *dest = remote;
+
+cleanup:
+
+ if (error < 0)
+ git__free(remote);
+
+ return error;
+}
+
+struct refspec_cb_data {
+ git_remote *remote;
+ int fetch;
+};
+
+static int refspec_cb(const git_config_entry *entry, void *payload)
+{
+ struct refspec_cb_data *data = (struct refspec_cb_data *)payload;
+ return add_refspec(data->remote, entry->value, data->fetch);
+}
+
+static int get_optional_config(
+ bool *found, git_config *config, git_str *buf,
+ git_config_foreach_cb cb, void *payload)
+{
+ int error = 0;
+ const char *key = git_str_cstr(buf);
+
+ if (git_str_oom(buf))
+ return -1;
+
+ if (cb != NULL)
+ error = git_config_get_multivar_foreach(config, key, NULL, cb, payload);
+ else
+ error = git_config_get_string(payload, config, key);
+
+ if (found)
+ *found = !error;
+
+ if (error == GIT_ENOTFOUND) {
+ git_error_clear();
+ error = 0;
+ }
+
+ return error;
+}
+
+int git_remote_lookup(git_remote **out, git_repository *repo, const char *name)
+{
+ git_remote *remote = NULL;
+ git_str buf = GIT_STR_INIT;
+ const char *val;
+ int error = 0;
+ git_config *config;
+ struct refspec_cb_data data = { NULL };
+ bool optional_setting_found = false, found;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
+
+ if ((error = ensure_remote_name_is_valid(name)) < 0)
+ return error;
+
+ if ((error = git_repository_config_snapshot(&config, repo)) < 0)
+ return error;
+
+ remote = git__calloc(1, sizeof(git_remote));
+ GIT_ERROR_CHECK_ALLOC(remote);
+
+ remote->name = git__strdup(name);
+ GIT_ERROR_CHECK_ALLOC(remote->name);
+
+ if (git_vector_init(&remote->refs, 32, NULL) < 0 ||
+ git_vector_init(&remote->refspecs, 2, NULL) < 0 ||
+ git_vector_init(&remote->passive_refspecs, 2, NULL) < 0 ||
+ git_vector_init(&remote->active_refspecs, 2, NULL) < 0) {
+ error = -1;
+ goto cleanup;
+ }
+
+ if ((error = git_str_printf(&buf, "remote.%s.url", name)) < 0)
+ goto cleanup;
+
+ if ((error = get_optional_config(&found, config, &buf, NULL, (void *)&val)) < 0)
+ goto cleanup;
+
+ optional_setting_found |= found;
+
+ remote->repo = repo;
+ remote->download_tags = GIT_REMOTE_DOWNLOAD_TAGS_AUTO;
+
+ if (found && strlen(val) > 0) {
+ if ((error = apply_insteadof(&remote->url, config, val, GIT_DIRECTION_FETCH, true)) < 0 ||
+ (error = apply_insteadof(&remote->pushurl, config, val, GIT_DIRECTION_PUSH, false)) < 0)
+ goto cleanup;
+ }
+
+ val = NULL;
+ git_str_clear(&buf);
+ git_str_printf(&buf, "remote.%s.pushurl", name);
+
+ if ((error = get_optional_config(&found, config, &buf, NULL, (void *)&val)) < 0)
+ goto cleanup;
+
+ optional_setting_found |= found;
+
+ if (!optional_setting_found) {
+ error = GIT_ENOTFOUND;
+ git_error_set(GIT_ERROR_CONFIG, "remote '%s' does not exist", name);
+ goto cleanup;
+ }
+
+ if (found && strlen(val) > 0) {
+ if (remote->pushurl)
+ git__free(remote->pushurl);
+
+ if ((error = apply_insteadof(&remote->pushurl, config, val, GIT_DIRECTION_FETCH, true)) < 0)
+ goto cleanup;
+ }
+
+ data.remote = remote;
+ data.fetch = true;
+
+ git_str_clear(&buf);
+ git_str_printf(&buf, "remote.%s.fetch", name);
+
+ if ((error = get_optional_config(NULL, config, &buf, refspec_cb, &data)) < 0)
+ goto cleanup;
+
+ data.fetch = false;
+ git_str_clear(&buf);
+ git_str_printf(&buf, "remote.%s.push", name);
+
+ if ((error = get_optional_config(NULL, config, &buf, refspec_cb, &data)) < 0)
+ goto cleanup;
+
+ if ((error = download_tags_value(remote, config)) < 0)
+ goto cleanup;
+
+ if ((error = lookup_remote_prune_config(remote, config, name)) < 0)
+ goto cleanup;
+
+ /* Move the data over to where the matching functions can find them */
+ if ((error = dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs)) < 0)
+ goto cleanup;
+
+ *out = remote;
+
+cleanup:
+ git_config_free(config);
+ git_str_dispose(&buf);
+
+ if (error < 0)
+ git_remote_free(remote);
+
+ return error;
+}
+
+static int lookup_remote_prune_config(git_remote *remote, git_config *config, const char *name)
+{
+ git_str buf = GIT_STR_INIT;
+ int error = 0;
+
+ git_str_printf(&buf, "remote.%s.prune", name);
+
+ if ((error = git_config_get_bool(&remote->prune_refs, config, git_str_cstr(&buf))) < 0) {
+ if (error == GIT_ENOTFOUND) {
+ git_error_clear();
+
+ if ((error = git_config_get_bool(&remote->prune_refs, config, "fetch.prune")) < 0) {
+ if (error == GIT_ENOTFOUND) {
+ git_error_clear();
+ error = 0;
+ }
+ }
+ }
+ }
+
+ git_str_dispose(&buf);
+ return error;
+}
+
+const char *git_remote_name(const git_remote *remote)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(remote, NULL);
+ return remote->name;
+}
+
+git_repository *git_remote_owner(const git_remote *remote)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(remote, NULL);
+ return remote->repo;
+}
+
+const char *git_remote_url(const git_remote *remote)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(remote, NULL);
+ return remote->url;
+}
+
+int git_remote_set_instance_url(git_remote *remote, const char *url)
+{
+ char *tmp;
+
+ GIT_ASSERT_ARG(remote);
+ GIT_ASSERT_ARG(url);
+
+ if ((tmp = git__strdup(url)) == NULL)
+ return -1;
+
+ git__free(remote->url);
+ remote->url = tmp;
+
+ return 0;
+}
+
+static int set_url(git_repository *repo, const char *remote, const char *pattern, const char *url)
+{
+ git_config *cfg;
+ git_str buf = GIT_STR_INIT, canonical_url = GIT_STR_INIT;
+ int error;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(remote);
+
+ if ((error = ensure_remote_name_is_valid(remote)) < 0)
+ return error;
+
+ if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
+ return error;
+
+ if ((error = git_str_printf(&buf, pattern, remote)) < 0)
+ return error;
+
+ if (url) {
+ if ((error = canonicalize_url(&canonical_url, url)) < 0)
+ goto cleanup;
+
+ error = git_config_set_string(cfg, buf.ptr, url);
+ } else {
+ error = git_config_delete_entry(cfg, buf.ptr);
+ }
+
+cleanup:
+ git_str_dispose(&canonical_url);
+ git_str_dispose(&buf);
+
+ return error;
+}
+
+int git_remote_set_url(git_repository *repo, const char *remote, const char *url)
+{
+ return set_url(repo, remote, CONFIG_URL_FMT, url);
+}
+
+const char *git_remote_pushurl(const git_remote *remote)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(remote, NULL);
+ return remote->pushurl;
+}
+
+int git_remote_set_instance_pushurl(git_remote *remote, const char *url)
+{
+ char *tmp;
+
+ GIT_ASSERT_ARG(remote);
+ GIT_ASSERT_ARG(url);
+
+ if ((tmp = git__strdup(url)) == NULL)
+ return -1;
+
+ git__free(remote->pushurl);
+ remote->pushurl = tmp;
+
+ return 0;
+}
+
+int git_remote_set_pushurl(git_repository *repo, const char *remote, const char *url)
+{
+ return set_url(repo, remote, CONFIG_PUSHURL_FMT, url);
+}
+
+static int resolve_url(
+ git_str *resolved_url,
+ const char *url,
+ int direction,
+ const git_remote_callbacks *callbacks)
+{
+#ifdef GIT_DEPRECATE_HARD
+ GIT_UNUSED(direction);
+ GIT_UNUSED(callbacks);
+#else
+ git_buf buf = GIT_BUF_INIT;
+ int error;
+
+ if (callbacks && callbacks->resolve_url) {
+ error = callbacks->resolve_url(&buf, url, direction, callbacks->payload);
+
+ if (error != GIT_PASSTHROUGH) {
+ git_error_set_after_callback_function(error, "git_resolve_url_cb");
+
+ git_str_set(resolved_url, buf.ptr, buf.size);
+ git_buf_dispose(&buf);
+
+ return error;
+ }
+ }
+#endif
+
+ return git_str_sets(resolved_url, url);
+}
+
+int git_remote__urlfordirection(
+ git_str *url_out,
+ struct git_remote *remote,
+ int direction,
+ const git_remote_callbacks *callbacks)
+{
+ const char *url = NULL;
+
+ GIT_ASSERT_ARG(remote);
+ GIT_ASSERT_ARG(direction == GIT_DIRECTION_FETCH || direction == GIT_DIRECTION_PUSH);
+
+ if (callbacks && callbacks->remote_ready) {
+ int status = callbacks->remote_ready(remote, direction, callbacks->payload);
+
+ if (status != 0 && status != GIT_PASSTHROUGH) {
+ git_error_set_after_callback_function(status, "git_remote_ready_cb");
+ return status;
+ }
+ }
+
+ if (direction == GIT_DIRECTION_FETCH)
+ url = remote->url;
+ else if (direction == GIT_DIRECTION_PUSH)
+ url = remote->pushurl ? remote->pushurl : remote->url;
+
+ if (!url) {
+ git_error_set(GIT_ERROR_INVALID,
+ "malformed remote '%s' - missing %s URL",
+ remote->name ? remote->name : "(anonymous)",
+ direction == GIT_DIRECTION_FETCH ? "fetch" : "push");
+ return GIT_EINVALID;
+ }
+
+ return resolve_url(url_out, url, direction, callbacks);
+}
+
+int git_remote_connect_options_init(
+ git_remote_connect_options *opts,
+ unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_remote_connect_options, GIT_REMOTE_CONNECT_OPTIONS_INIT);
+ return 0;
+}
+
+int git_remote_connect_options_dup(
+ git_remote_connect_options *dst,
+ const git_remote_connect_options *src)
+{
+ memcpy(dst, src, sizeof(git_remote_connect_options));
+
+ if (git_proxy_options_dup(&dst->proxy_opts, &src->proxy_opts) < 0 ||
+ git_strarray_copy(&dst->custom_headers, &src->custom_headers) < 0)
+ return -1;
+
+ return 0;
+}
+
+void git_remote_connect_options_dispose(git_remote_connect_options *opts)
+{
+ if (!opts)
+ return;
+
+ git_strarray_dispose(&opts->custom_headers);
+ git_proxy_options_dispose(&opts->proxy_opts);
+}
+
+static size_t http_header_name_length(const char *http_header)
+{
+ const char *colon = strchr(http_header, ':');
+ if (!colon)
+ return 0;
+ return colon - http_header;
+}
+
+static bool is_malformed_http_header(const char *http_header)
+{
+ const char *c;
+ size_t name_len;
+
+ /* Disallow \r and \n */
+ if ((c = strchr(http_header, '\r')) != NULL)
+ return true;
+ if ((c = strchr(http_header, '\n')) != NULL)
+ return true;
+
+ /* Require a header name followed by : */
+ if ((name_len = http_header_name_length(http_header)) < 1)
+ return true;
+
+ return false;
+}
+
+static char *forbidden_custom_headers[] = {
+ "User-Agent",
+ "Host",
+ "Accept",
+ "Content-Type",
+ "Transfer-Encoding",
+ "Content-Length",
+};
+
+static bool is_forbidden_custom_header(const char *custom_header)
+{
+ unsigned long i;
+ size_t name_len = http_header_name_length(custom_header);
+
+ /* Disallow headers that we set */
+ for (i = 0; i < ARRAY_SIZE(forbidden_custom_headers); i++)
+ if (strncmp(forbidden_custom_headers[i], custom_header, name_len) == 0)
+ return true;
+
+ return false;
+}
+
+static int validate_custom_headers(const git_strarray *custom_headers)
+{
+ size_t i;
+
+ if (!custom_headers)
+ return 0;
+
+ for (i = 0; i < custom_headers->count; i++) {
+ if (is_malformed_http_header(custom_headers->strings[i])) {
+ git_error_set(GIT_ERROR_INVALID, "custom HTTP header '%s' is malformed", custom_headers->strings[i]);
+ return -1;
+ }
+
+ if (is_forbidden_custom_header(custom_headers->strings[i])) {
+ git_error_set(GIT_ERROR_INVALID, "custom HTTP header '%s' is already set by libgit2", custom_headers->strings[i]);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static int lookup_redirect_config(
+ git_remote_redirect_t *out,
+ git_repository *repo)
+{
+ git_config *config;
+ const char *value;
+ int bool_value, error = 0;
+
+ if (!repo) {
+ *out = GIT_REMOTE_REDIRECT_INITIAL;
+ return 0;
+ }
+
+ if ((error = git_repository_config_snapshot(&config, repo)) < 0)
+ goto done;
+
+ if ((error = git_config_get_string(&value, config, "http.followRedirects")) < 0) {
+ if (error == GIT_ENOTFOUND) {
+ *out = GIT_REMOTE_REDIRECT_INITIAL;
+ error = 0;
+ }
+
+ goto done;
+ }
+
+ if (git_config_parse_bool(&bool_value, value) == 0) {
+ *out = bool_value ? GIT_REMOTE_REDIRECT_ALL :
+ GIT_REMOTE_REDIRECT_NONE;
+ } else if (strcasecmp(value, "initial") == 0) {
+ *out = GIT_REMOTE_REDIRECT_INITIAL;
+ } else {
+ git_error_set(GIT_ERROR_CONFIG, "invalid configuration setting '%s' for 'http.followRedirects'", value);
+ error = -1;
+ }
+
+done:
+ git_config_free(config);
+ return error;
+}
+
+int git_remote_connect_options_normalize(
+ git_remote_connect_options *dst,
+ git_repository *repo,
+ const git_remote_connect_options *src)
+{
+ git_remote_connect_options_dispose(dst);
+ git_remote_connect_options_init(dst, GIT_REMOTE_CONNECT_OPTIONS_VERSION);
+
+ if (src) {
+ GIT_ERROR_CHECK_VERSION(src, GIT_REMOTE_CONNECT_OPTIONS_VERSION, "git_remote_connect_options");
+ GIT_ERROR_CHECK_VERSION(&src->callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
+ GIT_ERROR_CHECK_VERSION(&src->proxy_opts, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options");
+
+ if (validate_custom_headers(&src->custom_headers) < 0 ||
+ git_remote_connect_options_dup(dst, src) < 0)
+ return -1;
+ }
+
+ if (dst->follow_redirects == 0) {
+ if (lookup_redirect_config(&dst->follow_redirects, repo) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+int git_remote_connect_ext(
+ git_remote *remote,
+ git_direction direction,
+ const git_remote_connect_options *given_opts)
+{
+ git_remote_connect_options opts = GIT_REMOTE_CONNECT_OPTIONS_INIT;
+ git_str url = GIT_STR_INIT;
+ git_transport *t;
+ int error;
+
+ GIT_ASSERT_ARG(remote);
+
+ if (given_opts)
+ memcpy(&opts, given_opts, sizeof(git_remote_connect_options));
+
+ GIT_ERROR_CHECK_VERSION(&opts.callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
+ GIT_ERROR_CHECK_VERSION(&opts.proxy_opts, GIT_PROXY_OPTIONS_VERSION, "git_proxy_options");
+
+ t = remote->transport;
+
+ if ((error = git_remote__urlfordirection(&url, remote, direction, &opts.callbacks)) < 0)
+ goto on_error;
+
+ /* If we don't have a transport object yet, and the caller specified a
+ * custom transport factory, use that */
+ if (!t && opts.callbacks.transport &&
+ (error = opts.callbacks.transport(&t, remote, opts.callbacks.payload)) < 0)
+ goto on_error;
+
+ /* If we still don't have a transport, then use the global
+ * transport registrations which map URI schemes to transport factories */
+ if (!t && (error = git_transport_new(&t, remote, url.ptr)) < 0)
+ goto on_error;
+
+ if ((error = t->connect(t, url.ptr, direction, &opts)) != 0)
+ goto on_error;
+
+ remote->transport = t;
+
+ git_str_dispose(&url);
+
+ return 0;
+
+on_error:
+ if (t)
+ t->free(t);
+
+ git_str_dispose(&url);
+
+ if (t == remote->transport)
+ remote->transport = NULL;
+
+ return error;
+}
+
+int git_remote_connect(
+ git_remote *remote,
+ git_direction direction,
+ const git_remote_callbacks *callbacks,
+ const git_proxy_options *proxy,
+ const git_strarray *custom_headers)
+{
+ git_remote_connect_options opts = GIT_REMOTE_CONNECT_OPTIONS_INIT;
+
+ if (callbacks)
+ memcpy(&opts.callbacks, callbacks, sizeof(git_remote_callbacks));
+
+ if (proxy)
+ memcpy(&opts.proxy_opts, proxy, sizeof(git_proxy_options));
+
+ if (custom_headers)
+ memcpy(&opts.custom_headers, custom_headers, sizeof(git_strarray));
+
+ return git_remote_connect_ext(remote, direction, &opts);
+}
+
+int git_remote_ls(const git_remote_head ***out, size_t *size, git_remote *remote)
+{
+ GIT_ASSERT_ARG(remote);
+
+ if (!remote->transport) {
+ git_error_set(GIT_ERROR_NET, "this remote has never connected");
+ return -1;
+ }
+
+ return remote->transport->ls(out, size, remote->transport);
+}
+
+int git_remote_capabilities(unsigned int *out, git_remote *remote)
+{
+ GIT_ASSERT_ARG(remote);
+
+ *out = 0;
+
+ if (!remote->transport) {
+ git_error_set(GIT_ERROR_NET, "this remote has never connected");
+ return -1;
+ }
+
+ return remote->transport->capabilities(out, remote->transport);
+}
+
+int git_remote_oid_type(git_oid_t *out, git_remote *remote)
+{
+ GIT_ASSERT_ARG(remote);
+
+ if (!remote->transport) {
+ git_error_set(GIT_ERROR_NET, "this remote has never connected");
+ *out = 0;
+ return -1;
+ }
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ return remote->transport->oid_type(out, remote->transport);
+#else
+ *out = GIT_OID_SHA1;
+ return 0;
+#endif
+}
+
+static int lookup_config(char **out, git_config *cfg, const char *name)
+{
+ git_config_entry *ce = NULL;
+ int error;
+
+ if ((error = git_config__lookup_entry(&ce, cfg, name, false)) < 0)
+ return error;
+
+ if (ce && ce->value) {
+ *out = git__strdup(ce->value);
+ GIT_ERROR_CHECK_ALLOC(*out);
+ } else {
+ error = GIT_ENOTFOUND;
+ }
+
+ git_config_entry_free(ce);
+ return error;
+}
+
+static void url_config_trim(git_net_url *url)
+{
+ size_t len = strlen(url->path);
+
+ if (url->path[len - 1] == '/') {
+ len--;
+ } else {
+ while (len && url->path[len - 1] != '/')
+ len--;
+ }
+
+ url->path[len] = '\0';
+}
+
+static int http_proxy_config(char **out, git_remote *remote, git_net_url *url)
+{
+ git_config *cfg = NULL;
+ git_str buf = GIT_STR_INIT;
+ git_net_url lookup_url = GIT_NET_URL_INIT;
+ int error;
+
+ if ((error = git_net_url_dup(&lookup_url, url)) < 0)
+ goto done;
+
+ if (remote->repo) {
+ if ((error = git_repository_config(&cfg, remote->repo)) < 0)
+ goto done;
+ } else {
+ if ((error = git_config_open_default(&cfg)) < 0)
+ goto done;
+ }
+
+ /* remote.<name>.proxy config setting */
+ if (remote->name && remote->name[0]) {
+ git_str_clear(&buf);
+
+ if ((error = git_str_printf(&buf, "remote.%s.proxy", remote->name)) < 0 ||
+ (error = lookup_config(out, cfg, buf.ptr)) != GIT_ENOTFOUND)
+ goto done;
+ }
+
+ while (true) {
+ git_str_clear(&buf);
+
+ if ((error = git_str_puts(&buf, "http.")) < 0 ||
+ (error = git_net_url_fmt(&buf, &lookup_url)) < 0 ||
+ (error = git_str_puts(&buf, ".proxy")) < 0 ||
+ (error = lookup_config(out, cfg, buf.ptr)) != GIT_ENOTFOUND)
+ goto done;
+
+ if (! lookup_url.path[0])
+ break;
+
+ url_config_trim(&lookup_url);
+ }
+
+ git_str_clear(&buf);
+
+ error = lookup_config(out, cfg, "http.proxy");
+
+done:
+ git_config_free(cfg);
+ git_str_dispose(&buf);
+ git_net_url_dispose(&lookup_url);
+ return error;
+}
+
+static int http_proxy_env(char **out, git_remote *remote, git_net_url *url)
+{
+ git_str proxy_env = GIT_STR_INIT, no_proxy_env = GIT_STR_INIT;
+ bool use_ssl = (strcmp(url->scheme, "https") == 0);
+ int error;
+
+ GIT_UNUSED(remote);
+
+ /* http_proxy / https_proxy environment variables */
+ error = git__getenv(&proxy_env, use_ssl ? "https_proxy" : "http_proxy");
+
+ /* try uppercase environment variables */
+ if (error == GIT_ENOTFOUND)
+ error = git__getenv(&proxy_env, use_ssl ? "HTTPS_PROXY" : "HTTP_PROXY");
+
+ if (error)
+ goto done;
+
+ /* no_proxy/NO_PROXY environment variables */
+ error = git__getenv(&no_proxy_env, "no_proxy");
+
+ if (error == GIT_ENOTFOUND)
+ error = git__getenv(&no_proxy_env, "NO_PROXY");
+
+ if (error && error != GIT_ENOTFOUND)
+ goto done;
+
+ if (!git_net_url_matches_pattern_list(url, no_proxy_env.ptr))
+ *out = git_str_detach(&proxy_env);
+ else
+ error = GIT_ENOTFOUND;
+
+done:
+ git_str_dispose(&proxy_env);
+ git_str_dispose(&no_proxy_env);
+ return error;
+}
+
+int git_remote__http_proxy(char **out, git_remote *remote, git_net_url *url)
+{
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(remote);
+
+ *out = NULL;
+
+ /*
+ * Go through the possible sources for proxy configuration,
+ * Examine the various git config options first, then
+ * consult environment variables.
+ */
+ if ((error = http_proxy_config(out, remote, url)) != GIT_ENOTFOUND ||
+ (error = http_proxy_env(out, remote, url)) != GIT_ENOTFOUND)
+ return error;
+
+ return 0;
+}
+
+/* DWIM `refspecs` based on `refs` and append the output to `out` */
+static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs)
+{
+ size_t i;
+ git_refspec *spec;
+
+ git_vector_foreach(refspecs, i, spec) {
+ if (git_refspec__dwim_one(out, spec, refs) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+static void free_refspecs(git_vector *vec)
+{
+ size_t i;
+ git_refspec *spec;
+
+ git_vector_foreach(vec, i, spec) {
+ git_refspec__dispose(spec);
+ git__free(spec);
+ }
+
+ git_vector_clear(vec);
+}
+
+static int remote_head_cmp(const void *_a, const void *_b)
+{
+ const git_remote_head *a = (git_remote_head *) _a;
+ const git_remote_head *b = (git_remote_head *) _b;
+
+ return git__strcmp_cb(a->name, b->name);
+}
+
+static int ls_to_vector(git_vector *out, git_remote *remote)
+{
+ git_remote_head **heads;
+ size_t heads_len, i;
+
+ if (git_remote_ls((const git_remote_head ***)&heads, &heads_len, remote) < 0)
+ return -1;
+
+ if (git_vector_init(out, heads_len, remote_head_cmp) < 0)
+ return -1;
+
+ for (i = 0; i < heads_len; i++) {
+ if (git_vector_insert(out, heads[i]) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+static int connect_or_reset_options(
+ git_remote *remote,
+ int direction,
+ git_remote_connect_options *opts)
+{
+ if (!git_remote_connected(remote)) {
+ return git_remote_connect_ext(remote, direction, opts);
+ } else {
+ return remote->transport->set_connect_opts(remote->transport, opts);
+ }
+}
+
+/* Download from an already connected remote. */
+static int git_remote__download(
+ git_remote *remote,
+ const git_strarray *refspecs,
+ const git_fetch_options *opts)
+{
+ git_vector *to_active, specs = GIT_VECTOR_INIT, refs = GIT_VECTOR_INIT;
+ size_t i;
+ int error;
+
+ if (ls_to_vector(&refs, remote) < 0)
+ return -1;
+
+ if ((error = git_vector_init(&specs, 0, NULL)) < 0)
+ goto on_error;
+
+ remote->passed_refspecs = 0;
+ if (!refspecs || !refspecs->count) {
+ to_active = &remote->refspecs;
+ } else {
+ for (i = 0; i < refspecs->count; i++) {
+ if ((error = add_refspec_to(&specs, refspecs->strings[i], true)) < 0)
+ goto on_error;
+ }
+
+ to_active = &specs;
+ remote->passed_refspecs = 1;
+ }
+
+ free_refspecs(&remote->passive_refspecs);
+ if ((error = dwim_refspecs(&remote->passive_refspecs, &remote->refspecs, &refs)) < 0)
+ goto on_error;
+
+ free_refspecs(&remote->active_refspecs);
+ error = dwim_refspecs(&remote->active_refspecs, to_active, &refs);
+
+ git_vector_free(&refs);
+ free_refspecs(&specs);
+ git_vector_free(&specs);
+
+ if (error < 0)
+ goto on_error;
+
+ if (remote->push) {
+ git_push_free(remote->push);
+ remote->push = NULL;
+ }
+
+ if ((error = git_fetch_negotiate(remote, opts)) < 0)
+ goto on_error;
+
+ error = git_fetch_download_pack(remote);
+
+on_error:
+ git_vector_free(&refs);
+ free_refspecs(&specs);
+ git_vector_free(&specs);
+ return error;
+}
+
+int git_remote_download(
+ git_remote *remote,
+ const git_strarray *refspecs,
+ const git_fetch_options *opts)
+{
+ git_remote_connect_options connect_opts = GIT_REMOTE_CONNECT_OPTIONS_INIT;
+ int error;
+
+ GIT_ASSERT_ARG(remote);
+
+ if (!remote->repo) {
+ git_error_set(GIT_ERROR_INVALID, "cannot download detached remote");
+ return -1;
+ }
+
+ if (git_remote_connect_options__from_fetch_opts(&connect_opts,
+ remote, opts) < 0)
+ return -1;
+
+ if ((error = connect_or_reset_options(remote, GIT_DIRECTION_FETCH, &connect_opts)) < 0)
+ return error;
+
+ return git_remote__download(remote, refspecs, opts);
+}
+
+int git_remote_fetch(
+ git_remote *remote,
+ const git_strarray *refspecs,
+ const git_fetch_options *opts,
+ const char *reflog_message)
+{
+ int error, update_fetchhead = 1;
+ git_remote_autotag_option_t tagopt = remote->download_tags;
+ bool prune = false;
+ git_str reflog_msg_buf = GIT_STR_INIT;
+ git_remote_connect_options connect_opts = GIT_REMOTE_CONNECT_OPTIONS_INIT;
+ unsigned int capabilities;
+ git_oid_t oid_type;
+
+ GIT_ASSERT_ARG(remote);
+
+ if (!remote->repo) {
+ git_error_set(GIT_ERROR_INVALID, "cannot download detached remote");
+ return -1;
+ }
+
+ if (git_remote_connect_options__from_fetch_opts(&connect_opts,
+ remote, opts) < 0)
+ return -1;
+
+ if ((error = connect_or_reset_options(remote, GIT_DIRECTION_FETCH, &connect_opts)) < 0)
+ return error;
+
+ if (opts) {
+ update_fetchhead = opts->update_fetchhead;
+ tagopt = opts->download_tags;
+ }
+
+ if ((error = git_remote_capabilities(&capabilities, remote)) < 0 ||
+ (error = git_remote_oid_type(&oid_type, remote)) < 0)
+ return error;
+
+ /* Connect and download everything */
+ error = git_remote__download(remote, refspecs, opts);
+
+ /* We don't need to be connected anymore */
+ git_remote_disconnect(remote);
+
+ /* If the download failed, return the error */
+ if (error != 0)
+ goto done;
+
+ /* Default reflog message */
+ if (reflog_message)
+ git_str_sets(&reflog_msg_buf, reflog_message);
+ else {
+ git_str_printf(&reflog_msg_buf, "fetch %s",
+ remote->name ? remote->name : remote->url);
+ }
+
+ /* Create "remote/foo" branches for all remote branches */
+ error = git_remote_update_tips(remote, &connect_opts.callbacks, update_fetchhead, tagopt, git_str_cstr(&reflog_msg_buf));
+ git_str_dispose(&reflog_msg_buf);
+ if (error < 0)
+ goto done;
+
+ if (opts && opts->prune == GIT_FETCH_PRUNE)
+ prune = true;
+ else if (opts && opts->prune == GIT_FETCH_PRUNE_UNSPECIFIED && remote->prune_refs)
+ prune = true;
+ else if (opts && opts->prune == GIT_FETCH_NO_PRUNE)
+ prune = false;
+ else
+ prune = remote->prune_refs;
+
+ if (prune)
+ error = git_remote_prune(remote, &connect_opts.callbacks);
+
+done:
+ git_remote_connect_options_dispose(&connect_opts);
+ return error;
+}
+
+static int remote_head_for_fetchspec_src(git_remote_head **out, git_vector *update_heads, const char *fetchspec_src)
+{
+ unsigned int i;
+ git_remote_head *remote_ref;
+
+ GIT_ASSERT_ARG(update_heads);
+ GIT_ASSERT_ARG(fetchspec_src);
+
+ *out = NULL;
+
+ git_vector_foreach(update_heads, i, remote_ref) {
+ if (strcmp(remote_ref->name, fetchspec_src) == 0) {
+ *out = remote_ref;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int ref_to_update(int *update, git_str *remote_name, git_remote *remote, git_refspec *spec, const char *ref_name)
+{
+ int error = 0;
+ git_repository *repo;
+ git_str upstream_remote = GIT_STR_INIT;
+ git_str upstream_name = GIT_STR_INIT;
+
+ repo = git_remote_owner(remote);
+
+ if ((!git_reference__is_branch(ref_name)) ||
+ !git_remote_name(remote) ||
+ (error = git_branch__upstream_remote(&upstream_remote, repo, ref_name) < 0) ||
+ git__strcmp(git_remote_name(remote), git_str_cstr(&upstream_remote)) ||
+ (error = git_branch__upstream_name(&upstream_name, repo, ref_name)) < 0 ||
+ !git_refspec_dst_matches(spec, git_str_cstr(&upstream_name)) ||
+ (error = git_refspec__rtransform(remote_name, spec, upstream_name.ptr)) < 0) {
+ /* Not an error if there is no upstream */
+ if (error == GIT_ENOTFOUND) {
+ git_error_clear();
+ error = 0;
+ }
+
+ *update = 0;
+ } else {
+ *update = 1;
+ }
+
+ git_str_dispose(&upstream_remote);
+ git_str_dispose(&upstream_name);
+ return error;
+}
+
+static int remote_head_for_ref(git_remote_head **out, git_remote *remote, git_refspec *spec, git_vector *update_heads, git_reference *ref)
+{
+ git_reference *resolved_ref = NULL;
+ git_str remote_name = GIT_STR_INIT;
+ git_config *config = NULL;
+ const char *ref_name;
+ int error = 0, update;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(spec);
+ GIT_ASSERT_ARG(ref);
+
+ *out = NULL;
+
+ error = git_reference_resolve(&resolved_ref, ref);
+
+ /* If we're in an unborn branch, let's pretend nothing happened */
+ if (error == GIT_ENOTFOUND && git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC) {
+ ref_name = git_reference_symbolic_target(ref);
+ error = 0;
+ } else {
+ ref_name = git_reference_name(resolved_ref);
+ }
+
+ /*
+ * The ref name may be unresolvable - perhaps it's pointing to
+ * something invalid. In this case, there is no remote head for
+ * this ref.
+ */
+ if (!ref_name) {
+ error = 0;
+ goto cleanup;
+ }
+
+ if ((error = ref_to_update(&update, &remote_name, remote, spec, ref_name)) < 0)
+ goto cleanup;
+
+ if (update)
+ error = remote_head_for_fetchspec_src(out, update_heads, git_str_cstr(&remote_name));
+
+cleanup:
+ git_str_dispose(&remote_name);
+ git_reference_free(resolved_ref);
+ git_config_free(config);
+ return error;
+}
+
+static int git_remote_write_fetchhead(git_remote *remote, git_refspec *spec, git_vector *update_heads)
+{
+ git_reference *head_ref = NULL;
+ git_fetchhead_ref *fetchhead_ref;
+ git_remote_head *remote_ref, *merge_remote_ref;
+ git_vector fetchhead_refs;
+ bool include_all_fetchheads;
+ unsigned int i = 0;
+ int error = 0;
+
+ GIT_ASSERT_ARG(remote);
+
+ /* no heads, nothing to do */
+ if (update_heads->length == 0)
+ return 0;
+
+ if (git_vector_init(&fetchhead_refs, update_heads->length, git_fetchhead_ref_cmp) < 0)
+ return -1;
+
+ /* Iff refspec is * (but not subdir slash star), include tags */
+ include_all_fetchheads = (strcmp(GIT_REFS_HEADS_DIR "*", git_refspec_src(spec)) == 0);
+
+ /* Determine what to merge: if refspec was a wildcard, just use HEAD */
+ if (git_refspec_is_wildcard(spec)) {
+ if ((error = git_reference_lookup(&head_ref, remote->repo, GIT_HEAD_FILE)) < 0 ||
+ (error = remote_head_for_ref(&merge_remote_ref, remote, spec, update_heads, head_ref)) < 0)
+ goto cleanup;
+ } else {
+ /* If we're fetching a single refspec, that's the only thing that should be in FETCH_HEAD. */
+ if ((error = remote_head_for_fetchspec_src(&merge_remote_ref, update_heads, git_refspec_src(spec))) < 0)
+ goto cleanup;
+ }
+
+ /* Create the FETCH_HEAD file */
+ git_vector_foreach(update_heads, i, remote_ref) {
+ int merge_this_fetchhead = (merge_remote_ref == remote_ref);
+
+ if (!include_all_fetchheads &&
+ !git_refspec_src_matches(spec, remote_ref->name) &&
+ !merge_this_fetchhead)
+ continue;
+
+ if (git_fetchhead_ref_create(&fetchhead_ref,
+ &remote_ref->oid,
+ merge_this_fetchhead,
+ remote_ref->name,
+ git_remote_url(remote)) < 0)
+ goto cleanup;
+
+ if (git_vector_insert(&fetchhead_refs, fetchhead_ref) < 0)
+ goto cleanup;
+ }
+
+ git_fetchhead_write(remote->repo, &fetchhead_refs);
+
+cleanup:
+ for (i = 0; i < fetchhead_refs.length; ++i)
+ git_fetchhead_ref_free(fetchhead_refs.contents[i]);
+
+ git_vector_free(&fetchhead_refs);
+ git_reference_free(head_ref);
+
+ return error;
+}
+
+/**
+ * Generate a list of candidates for pruning by getting a list of
+ * references which match the rhs of an active refspec.
+ */
+static int prune_candidates(git_vector *candidates, git_remote *remote)
+{
+ git_strarray arr = { 0 };
+ size_t i;
+ int error;
+
+ if ((error = git_reference_list(&arr, remote->repo)) < 0)
+ return error;
+
+ for (i = 0; i < arr.count; i++) {
+ const char *refname = arr.strings[i];
+ char *refname_dup;
+
+ if (!git_remote__matching_dst_refspec(remote, refname))
+ continue;
+
+ refname_dup = git__strdup(refname);
+ GIT_ERROR_CHECK_ALLOC(refname_dup);
+
+ if ((error = git_vector_insert(candidates, refname_dup)) < 0)
+ goto out;
+ }
+
+out:
+ git_strarray_dispose(&arr);
+ return error;
+}
+
+static int find_head(const void *_a, const void *_b)
+{
+ git_remote_head *a = (git_remote_head *) _a;
+ git_remote_head *b = (git_remote_head *) _b;
+
+ return strcmp(a->name, b->name);
+}
+
+int git_remote_prune(git_remote *remote, const git_remote_callbacks *callbacks)
+{
+ size_t i, j;
+ git_vector remote_refs = GIT_VECTOR_INIT;
+ git_vector candidates = GIT_VECTOR_INIT;
+ const git_refspec *spec;
+ const char *refname;
+ int error;
+ git_oid zero_id;
+
+ GIT_ASSERT(remote && remote->repo);
+ git_oid_clear(&zero_id, remote->repo->oid_type);
+
+ if (callbacks)
+ GIT_ERROR_CHECK_VERSION(callbacks, GIT_REMOTE_CALLBACKS_VERSION, "git_remote_callbacks");
+
+ if ((error = ls_to_vector(&remote_refs, remote)) < 0)
+ goto cleanup;
+
+ git_vector_set_cmp(&remote_refs, find_head);
+
+ if ((error = prune_candidates(&candidates, remote)) < 0)
+ goto cleanup;
+
+ /*
+ * Remove those entries from the candidate list for which we
+ * can find a remote reference in at least one refspec.
+ */
+ git_vector_foreach(&candidates, i, refname) {
+ git_vector_foreach(&remote->active_refspecs, j, spec) {
+ git_str buf = GIT_STR_INIT;
+ size_t pos;
+ char *src_name;
+ git_remote_head key = {0};
+
+ if (!git_refspec_dst_matches(spec, refname))
+ continue;
+
+ if ((error = git_refspec__rtransform(&buf, spec, refname)) < 0)
+ goto cleanup;
+
+ key.name = (char *) git_str_cstr(&buf);
+ error = git_vector_bsearch(&pos, &remote_refs, &key);
+ git_str_dispose(&buf);
+
+ if (error < 0 && error != GIT_ENOTFOUND)
+ goto cleanup;
+
+ if (error == GIT_ENOTFOUND)
+ continue;
+
+ /* If we did find a source, remove it from the candidates. */
+ if ((error = git_vector_set((void **) &src_name, &candidates, i, NULL)) < 0)
+ goto cleanup;
+
+ git__free(src_name);
+ break;
+ }
+ }
+
+ /*
+ * For those candidates still left in the list, we need to
+ * remove them. We do not remove symrefs, as those are for
+ * stuff like origin/HEAD which will never match, but we do
+ * not want to remove them.
+ */
+ git_vector_foreach(&candidates, i, refname) {
+ git_reference *ref;
+ git_oid id;
+
+ if (refname == NULL)
+ continue;
+
+ error = git_reference_lookup(&ref, remote->repo, refname);
+ /* as we want it gone, let's not consider this an error */
+ if (error == GIT_ENOTFOUND)
+ continue;
+
+ if (error < 0)
+ goto cleanup;
+
+ if (git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC) {
+ git_reference_free(ref);
+ continue;
+ }
+
+ git_oid_cpy(&id, git_reference_target(ref));
+ error = git_reference_delete(ref);
+ git_reference_free(ref);
+ if (error < 0)
+ goto cleanup;
+
+ if (callbacks && callbacks->update_tips)
+ error = callbacks->update_tips(refname, &id, &zero_id, callbacks->payload);
+
+ if (error < 0)
+ goto cleanup;
+ }
+
+cleanup:
+ git_vector_free(&remote_refs);
+ git_vector_free_deep(&candidates);
+ return error;
+}
+
+static int update_ref(
+ const git_remote *remote,
+ const char *ref_name,
+ git_oid *id,
+ const char *msg,
+ const git_remote_callbacks *callbacks)
+{
+ git_reference *ref;
+ git_oid old_id;
+ int error;
+
+ GIT_ASSERT(remote && remote->repo);
+ git_oid_clear(&old_id, remote->repo->oid_type);
+
+ error = git_reference_name_to_id(&old_id, remote->repo, ref_name);
+
+ if (error < 0 && error != GIT_ENOTFOUND)
+ return error;
+ else if (error == 0 && git_oid_equal(&old_id, id))
+ return 0;
+
+ /* If we did find a current reference, make sure we haven't lost a race */
+ if (error)
+ error = git_reference_create(&ref, remote->repo, ref_name, id, true, msg);
+ else
+ error = git_reference_create_matching(&ref, remote->repo, ref_name, id, true, &old_id, msg);
+
+ git_reference_free(ref);
+
+ if (error < 0)
+ return error;
+
+ if (callbacks && callbacks->update_tips &&
+ (error = callbacks->update_tips(ref_name, &old_id, id, callbacks->payload)) < 0)
+ return error;
+
+ return 0;
+}
+
+static int update_one_tip(
+ git_vector *update_heads,
+ git_remote *remote,
+ git_refspec *spec,
+ git_remote_head *head,
+ git_refspec *tagspec,
+ git_remote_autotag_option_t tagopt,
+ const char *log_message,
+ const git_remote_callbacks *callbacks)
+{
+ git_odb *odb;
+ git_str refname = GIT_STR_INIT;
+ git_reference *ref = NULL;
+ bool autotag = false;
+ git_oid old;
+ int valid;
+ int error;
+
+ GIT_ASSERT(remote && remote->repo);
+
+ if ((error = git_repository_odb__weakptr(&odb, remote->repo)) < 0)
+ goto done;
+
+ /* Ignore malformed ref names (which also saves us from tag^{} */
+ if ((error = git_reference_name_is_valid(&valid, head->name)) < 0)
+ goto done;
+
+ if (!valid)
+ goto done;
+
+ /* If we have a tag, see if the auto-follow rules say to update it */
+ if (git_refspec_src_matches(tagspec, head->name)) {
+ if (tagopt == GIT_REMOTE_DOWNLOAD_TAGS_AUTO)
+ autotag = true;
+
+ if (tagopt != GIT_REMOTE_DOWNLOAD_TAGS_NONE) {
+ if (git_str_puts(&refname, head->name) < 0)
+ goto done;
+ }
+ }
+
+ /* If we didn't want to auto-follow the tag, check if the refspec matches */
+ if (!autotag && git_refspec_src_matches(spec, head->name)) {
+ if (spec->dst) {
+ if ((error = git_refspec__transform(&refname, spec, head->name)) < 0)
+ goto done;
+ } else {
+ /*
+ * no rhs means store it in FETCH_HEAD, even if we don't
+ * update anything else.
+ */
+ error = git_vector_insert(update_heads, head);
+ goto done;
+ }
+ }
+
+ /* If we still don't have a refname, we don't want it */
+ if (git_str_len(&refname) == 0)
+ goto done;
+
+ /* In autotag mode, only create tags for objects already in db */
+ if (autotag && !git_odb_exists(odb, &head->oid))
+ goto done;
+
+ if (!autotag && (error = git_vector_insert(update_heads, head)) < 0)
+ goto done;
+
+ error = git_reference_name_to_id(&old, remote->repo, refname.ptr);
+
+ if (error < 0 && error != GIT_ENOTFOUND)
+ goto done;
+
+ if (!(error || error == GIT_ENOTFOUND) &&
+ !spec->force &&
+ !git_graph_descendant_of(remote->repo, &head->oid, &old)) {
+ error = 0;
+ goto done;
+ }
+
+ if (error == GIT_ENOTFOUND) {
+ git_oid_clear(&old, remote->repo->oid_type);
+ error = 0;
+
+ if (autotag && (error = git_vector_insert(update_heads, head)) < 0)
+ goto done;
+ }
+
+ if (!git_oid__cmp(&old, &head->oid))
+ goto done;
+
+ /* In autotag mode, don't overwrite any locally-existing tags */
+ error = git_reference_create(&ref, remote->repo, refname.ptr, &head->oid, !autotag,
+ log_message);
+
+ if (error < 0) {
+ if (error == GIT_EEXISTS)
+ error = 0;
+
+ goto done;
+ }
+
+ if (callbacks && callbacks->update_tips != NULL &&
+ (error = callbacks->update_tips(refname.ptr, &old, &head->oid, callbacks->payload)) < 0)
+ git_error_set_after_callback_function(error, "git_remote_fetch");
+
+done:
+ git_reference_free(ref);
+ git_str_dispose(&refname);
+ return error;
+}
+
+static int update_tips_for_spec(
+ git_remote *remote,
+ const git_remote_callbacks *callbacks,
+ int update_fetchhead,
+ git_remote_autotag_option_t tagopt,
+ git_refspec *spec,
+ git_vector *refs,
+ const char *log_message)
+{
+ git_refspec tagspec;
+ git_remote_head *head, oid_head;
+ git_vector update_heads;
+ int error = 0;
+ size_t i;
+
+ GIT_ASSERT_ARG(remote && remote->repo);
+
+ if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0)
+ return -1;
+
+ /* Make a copy of the transport's refs */
+ if (git_vector_init(&update_heads, 16, NULL) < 0)
+ return -1;
+
+ /* Update tips based on the remote heads */
+ git_vector_foreach(refs, i, head) {
+ if (update_one_tip(&update_heads, remote, spec, head, &tagspec, tagopt, log_message, callbacks) < 0)
+ goto on_error;
+ }
+
+ /* Handle specified oid sources */
+ if (git_oid__is_hexstr(spec->src, remote->repo->oid_type)) {
+ git_oid id;
+
+ if ((error = git_oid__fromstr(&id, spec->src, remote->repo->oid_type)) < 0)
+ goto on_error;
+
+ if (spec->dst &&
+ (error = update_ref(remote, spec->dst, &id, log_message, callbacks)) < 0)
+ goto on_error;
+
+ git_oid_cpy(&oid_head.oid, &id);
+ oid_head.name = spec->src;
+
+ if ((error = git_vector_insert(&update_heads, &oid_head)) < 0)
+ goto on_error;
+ }
+
+ if (update_fetchhead &&
+ (error = git_remote_write_fetchhead(remote, spec, &update_heads)) < 0)
+ goto on_error;
+
+ git_refspec__dispose(&tagspec);
+ git_vector_free(&update_heads);
+ return 0;
+
+on_error:
+ git_refspec__dispose(&tagspec);
+ git_vector_free(&update_heads);
+ return -1;
+
+}
+
+/**
+ * Iteration over the three vectors, with a pause whenever we find a match
+ *
+ * On each stop, we store the iteration stat in the inout i,j,k
+ * parameters, and return the currently matching passive refspec as
+ * well as the head which we matched.
+ */
+static int next_head(const git_remote *remote, git_vector *refs,
+ git_refspec **out_spec, git_remote_head **out_head,
+ size_t *out_i, size_t *out_j, size_t *out_k)
+{
+ const git_vector *active, *passive;
+ git_remote_head *head;
+ git_refspec *spec, *passive_spec;
+ size_t i, j, k;
+ int valid;
+
+ active = &remote->active_refspecs;
+ passive = &remote->passive_refspecs;
+
+ i = *out_i;
+ j = *out_j;
+ k = *out_k;
+
+ for (; i < refs->length; i++) {
+ head = git_vector_get(refs, i);
+
+ if (git_reference_name_is_valid(&valid, head->name) < 0)
+ return -1;
+
+ if (!valid)
+ continue;
+
+ for (; j < active->length; j++) {
+ spec = git_vector_get(active, j);
+
+ if (!git_refspec_src_matches(spec, head->name))
+ continue;
+
+ for (; k < passive->length; k++) {
+ passive_spec = git_vector_get(passive, k);
+
+ if (!git_refspec_src_matches(passive_spec, head->name))
+ continue;
+
+ *out_spec = passive_spec;
+ *out_head = head;
+ *out_i = i;
+ *out_j = j;
+ *out_k = k + 1;
+ return 0;
+
+ }
+ k = 0;
+ }
+ j = 0;
+ }
+
+ return GIT_ITEROVER;
+}
+
+static int opportunistic_updates(
+ const git_remote *remote,
+ const git_remote_callbacks *callbacks,
+ git_vector *refs,
+ const char *msg)
+{
+ size_t i, j, k;
+ git_refspec *spec;
+ git_remote_head *head;
+ git_str refname = GIT_STR_INIT;
+ int error = 0;
+
+ i = j = k = 0;
+
+ /* Handle refspecs matching remote heads */
+ while ((error = next_head(remote, refs, &spec, &head, &i, &j, &k)) == 0) {
+ /*
+ * If we got here, there is a refspec which was used
+ * for fetching which matches the source of one of the
+ * passive refspecs, so we should update that
+ * remote-tracking branch, but not add it to
+ * FETCH_HEAD
+ */
+
+ git_str_clear(&refname);
+ if ((error = git_refspec__transform(&refname, spec, head->name)) < 0 ||
+ (error = update_ref(remote, refname.ptr, &head->oid, msg, callbacks)) < 0)
+ goto cleanup;
+ }
+
+ if (error != GIT_ITEROVER)
+ goto cleanup;
+
+ error = 0;
+
+cleanup:
+ git_str_dispose(&refname);
+ return error;
+}
+
+static int truncate_fetch_head(const char *gitdir)
+{
+ git_str path = GIT_STR_INIT;
+ int error;
+
+ if ((error = git_str_joinpath(&path, gitdir, GIT_FETCH_HEAD_FILE)) < 0)
+ return error;
+
+ error = git_futils_truncate(path.ptr, GIT_REFS_FILE_MODE);
+ git_str_dispose(&path);
+
+ return error;
+}
+
+int git_remote_update_tips(
+ git_remote *remote,
+ const git_remote_callbacks *callbacks,
+ int update_fetchhead,
+ git_remote_autotag_option_t download_tags,
+ const char *reflog_message)
+{
+ git_refspec *spec, tagspec;
+ git_vector refs = GIT_VECTOR_INIT;
+ git_remote_autotag_option_t tagopt;
+ int error;
+ size_t i;
+
+ /* push has its own logic hidden away in the push object */
+ if (remote->push) {
+ return git_push_update_tips(remote->push, callbacks);
+ }
+
+ if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0)
+ return -1;
+
+
+ if ((error = ls_to_vector(&refs, remote)) < 0)
+ goto out;
+
+ if (download_tags == GIT_REMOTE_DOWNLOAD_TAGS_UNSPECIFIED)
+ tagopt = remote->download_tags;
+ else
+ tagopt = download_tags;
+
+ if ((error = truncate_fetch_head(git_repository_path(remote->repo))) < 0)
+ goto out;
+
+ if (tagopt == GIT_REMOTE_DOWNLOAD_TAGS_ALL) {
+ if ((error = update_tips_for_spec(remote, callbacks, update_fetchhead, tagopt, &tagspec, &refs, reflog_message)) < 0)
+ goto out;
+ }
+
+ git_vector_foreach(&remote->active_refspecs, i, spec) {
+ if (spec->push)
+ continue;
+
+ if ((error = update_tips_for_spec(remote, callbacks, update_fetchhead, tagopt, spec, &refs, reflog_message)) < 0)
+ goto out;
+ }
+
+ /* Only try to do opportunistic updates if the refspec lists differ. */
+ if (remote->passed_refspecs)
+ error = opportunistic_updates(remote, callbacks, &refs, reflog_message);
+
+out:
+ git_vector_free(&refs);
+ git_refspec__dispose(&tagspec);
+ return error;
+}
+
+int git_remote_connected(const git_remote *remote)
+{
+ GIT_ASSERT_ARG(remote);
+
+ if (!remote->transport || !remote->transport->is_connected)
+ return 0;
+
+ /* Ask the transport if it's connected. */
+ return remote->transport->is_connected(remote->transport);
+}
+
+int git_remote_stop(git_remote *remote)
+{
+ GIT_ASSERT_ARG(remote);
+
+ if (remote->transport && remote->transport->cancel)
+ remote->transport->cancel(remote->transport);
+
+ return 0;
+}
+
+int git_remote_disconnect(git_remote *remote)
+{
+ GIT_ASSERT_ARG(remote);
+
+ if (git_remote_connected(remote))
+ remote->transport->close(remote->transport);
+
+ return 0;
+}
+
+static void free_heads(git_vector *heads)
+{
+ git_remote_head *head;
+ size_t i;
+
+ git_vector_foreach(heads, i, head) {
+ git__free(head->name);
+ git__free(head);
+ }
+}
+
+void git_remote_free(git_remote *remote)
+{
+ if (remote == NULL)
+ return;
+
+ if (remote->transport != NULL) {
+ git_remote_disconnect(remote);
+
+ remote->transport->free(remote->transport);
+ remote->transport = NULL;
+ }
+
+ git_vector_free(&remote->refs);
+
+ free_refspecs(&remote->refspecs);
+ git_vector_free(&remote->refspecs);
+
+ free_refspecs(&remote->active_refspecs);
+ git_vector_free(&remote->active_refspecs);
+
+ free_refspecs(&remote->passive_refspecs);
+ git_vector_free(&remote->passive_refspecs);
+
+ free_heads(&remote->local_heads);
+ git_vector_free(&remote->local_heads);
+
+ git_push_free(remote->push);
+ git__free(remote->url);
+ git__free(remote->pushurl);
+ git__free(remote->name);
+ git__free(remote);
+}
+
+static int remote_list_cb(const git_config_entry *entry, void *payload)
+{
+ git_vector *list = payload;
+ const char *name = entry->name + strlen("remote.");
+ size_t namelen = strlen(name);
+ char *remote_name;
+
+ /* we know name matches "remote.<stuff>.(push)?url" */
+
+ if (!strcmp(&name[namelen - 4], ".url"))
+ remote_name = git__strndup(name, namelen - 4); /* strip ".url" */
+ else
+ remote_name = git__strndup(name, namelen - 8); /* strip ".pushurl" */
+ GIT_ERROR_CHECK_ALLOC(remote_name);
+
+ return git_vector_insert(list, remote_name);
+}
+
+int git_remote_list(git_strarray *remotes_list, git_repository *repo)
+{
+ int error;
+ git_config *cfg;
+ git_vector list = GIT_VECTOR_INIT;
+
+ if ((error = git_repository_config__weakptr(&cfg, repo)) < 0)
+ return error;
+
+ if ((error = git_vector_init(&list, 4, git__strcmp_cb)) < 0)
+ return error;
+
+ error = git_config_foreach_match(
+ cfg, "^remote\\..*\\.(push)?url$", remote_list_cb, &list);
+
+ if (error < 0) {
+ git_vector_free_deep(&list);
+ return error;
+ }
+
+ git_vector_uniq(&list, git__free);
+
+ remotes_list->strings =
+ (char **)git_vector_detach(&remotes_list->count, NULL, &list);
+
+ return 0;
+}
+
+const git_indexer_progress *git_remote_stats(git_remote *remote)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(remote, NULL);
+ return &remote->stats;
+}
+
+git_remote_autotag_option_t git_remote_autotag(const git_remote *remote)
+{
+ return remote->download_tags;
+}
+
+int git_remote_set_autotag(git_repository *repo, const char *remote, git_remote_autotag_option_t value)
+{
+ git_str var = GIT_STR_INIT;
+ git_config *config;
+ int error;
+
+ GIT_ASSERT_ARG(repo && remote);
+
+ if ((error = ensure_remote_name_is_valid(remote)) < 0)
+ return error;
+
+ if ((error = git_repository_config__weakptr(&config, repo)) < 0)
+ return error;
+
+ if ((error = git_str_printf(&var, CONFIG_TAGOPT_FMT, remote)))
+ return error;
+
+ switch (value) {
+ case GIT_REMOTE_DOWNLOAD_TAGS_NONE:
+ error = git_config_set_string(config, var.ptr, "--no-tags");
+ break;
+ case GIT_REMOTE_DOWNLOAD_TAGS_ALL:
+ error = git_config_set_string(config, var.ptr, "--tags");
+ break;
+ case GIT_REMOTE_DOWNLOAD_TAGS_AUTO:
+ error = git_config_delete_entry(config, var.ptr);
+ if (error == GIT_ENOTFOUND)
+ error = 0;
+ break;
+ default:
+ git_error_set(GIT_ERROR_INVALID, "invalid value for the tagopt setting");
+ error = -1;
+ }
+
+ git_str_dispose(&var);
+ return error;
+}
+
+int git_remote_prune_refs(const git_remote *remote)
+{
+ return remote->prune_refs;
+}
+
+static int rename_remote_config_section(
+ git_repository *repo,
+ const char *old_name,
+ const char *new_name)
+{
+ git_str old_section_name = GIT_STR_INIT,
+ new_section_name = GIT_STR_INIT;
+ int error = -1;
+
+ if (git_str_printf(&old_section_name, "remote.%s", old_name) < 0)
+ goto cleanup;
+
+ if (new_name &&
+ (git_str_printf(&new_section_name, "remote.%s", new_name) < 0))
+ goto cleanup;
+
+ error = git_config_rename_section(
+ repo,
+ git_str_cstr(&old_section_name),
+ new_name ? git_str_cstr(&new_section_name) : NULL);
+
+cleanup:
+ git_str_dispose(&old_section_name);
+ git_str_dispose(&new_section_name);
+
+ return error;
+}
+
+struct update_data {
+ git_config *config;
+ const char *old_remote_name;
+ const char *new_remote_name;
+};
+
+static int update_config_entries_cb(
+ const git_config_entry *entry,
+ void *payload)
+{
+ struct update_data *data = (struct update_data *)payload;
+
+ if (strcmp(entry->value, data->old_remote_name))
+ return 0;
+
+ return git_config_set_string(
+ data->config, entry->name, data->new_remote_name);
+}
+
+static int update_branch_remote_config_entry(
+ git_repository *repo,
+ const char *old_name,
+ const char *new_name)
+{
+ int error;
+ struct update_data data = { NULL };
+
+ if ((error = git_repository_config__weakptr(&data.config, repo)) < 0)
+ return error;
+
+ data.old_remote_name = old_name;
+ data.new_remote_name = new_name;
+
+ return git_config_foreach_match(
+ data.config, "branch\\..+\\.remote", update_config_entries_cb, &data);
+}
+
+static int rename_one_remote_reference(
+ git_reference *reference_in,
+ const char *old_remote_name,
+ const char *new_remote_name)
+{
+ int error;
+ git_reference *ref = NULL, *dummy = NULL;
+ git_str namespace = GIT_STR_INIT, old_namespace = GIT_STR_INIT;
+ git_str new_name = GIT_STR_INIT;
+ git_str log_message = GIT_STR_INIT;
+ size_t pfx_len;
+ const char *target;
+
+ if ((error = git_str_printf(&namespace, GIT_REFS_REMOTES_DIR "%s/", new_remote_name)) < 0)
+ return error;
+
+ pfx_len = strlen(GIT_REFS_REMOTES_DIR) + strlen(old_remote_name) + 1;
+ git_str_puts(&new_name, namespace.ptr);
+ if ((error = git_str_puts(&new_name, git_reference_name(reference_in) + pfx_len)) < 0)
+ goto cleanup;
+
+ if ((error = git_str_printf(&log_message,
+ "renamed remote %s to %s",
+ old_remote_name, new_remote_name)) < 0)
+ goto cleanup;
+
+ if ((error = git_reference_rename(&ref, reference_in, git_str_cstr(&new_name), 1,
+ git_str_cstr(&log_message))) < 0)
+ goto cleanup;
+
+ if (git_reference_type(ref) != GIT_REFERENCE_SYMBOLIC)
+ goto cleanup;
+
+ /* Handle refs like origin/HEAD -> origin/master */
+ target = git_reference_symbolic_target(ref);
+ if ((error = git_str_printf(&old_namespace, GIT_REFS_REMOTES_DIR "%s/", old_remote_name)) < 0)
+ goto cleanup;
+
+ if (git__prefixcmp(target, old_namespace.ptr))
+ goto cleanup;
+
+ git_str_clear(&new_name);
+ git_str_puts(&new_name, namespace.ptr);
+ if ((error = git_str_puts(&new_name, target + pfx_len)) < 0)
+ goto cleanup;
+
+ error = git_reference_symbolic_set_target(&dummy, ref, git_str_cstr(&new_name),
+ git_str_cstr(&log_message));
+
+ git_reference_free(dummy);
+
+cleanup:
+ git_reference_free(reference_in);
+ git_reference_free(ref);
+ git_str_dispose(&namespace);
+ git_str_dispose(&old_namespace);
+ git_str_dispose(&new_name);
+ git_str_dispose(&log_message);
+ return error;
+}
+
+static int rename_remote_references(
+ git_repository *repo,
+ const char *old_name,
+ const char *new_name)
+{
+ int error;
+ git_str buf = GIT_STR_INIT;
+ git_reference *ref;
+ git_reference_iterator *iter;
+
+ if ((error = git_str_printf(&buf, GIT_REFS_REMOTES_DIR "%s/*", old_name)) < 0)
+ return error;
+
+ error = git_reference_iterator_glob_new(&iter, repo, git_str_cstr(&buf));
+ git_str_dispose(&buf);
+
+ if (error < 0)
+ return error;
+
+ while ((error = git_reference_next(&ref, iter)) == 0) {
+ if ((error = rename_one_remote_reference(ref, old_name, new_name)) < 0)
+ break;
+ }
+
+ git_reference_iterator_free(iter);
+
+ return (error == GIT_ITEROVER) ? 0 : error;
+}
+
+static int rename_fetch_refspecs(git_vector *problems, git_remote *remote, const char *new_name)
+{
+ git_config *config;
+ git_str base = GIT_STR_INIT, var = GIT_STR_INIT, val = GIT_STR_INIT;
+ const git_refspec *spec;
+ size_t i;
+ int error = 0;
+
+ if ((error = git_repository_config__weakptr(&config, remote->repo)) < 0)
+ return error;
+
+ if ((error = git_vector_init(problems, 1, NULL)) < 0)
+ return error;
+
+ if ((error = default_fetchspec_for_name(&base, remote->name)) < 0)
+ return error;
+
+ git_vector_foreach(&remote->refspecs, i, spec) {
+ if (spec->push)
+ continue;
+
+ /* Does the dst part of the refspec follow the expected format? */
+ if (strcmp(git_str_cstr(&base), spec->string)) {
+ char *dup;
+
+ dup = git__strdup(spec->string);
+ GIT_ERROR_CHECK_ALLOC(dup);
+
+ if ((error = git_vector_insert(problems, dup)) < 0)
+ break;
+
+ continue;
+ }
+
+ /* If we do want to move it to the new section */
+
+ git_str_clear(&val);
+ git_str_clear(&var);
+
+ if (default_fetchspec_for_name(&val, new_name) < 0 ||
+ git_str_printf(&var, "remote.%s.fetch", new_name) < 0)
+ {
+ error = -1;
+ break;
+ }
+
+ if ((error = git_config_set_string(
+ config, git_str_cstr(&var), git_str_cstr(&val))) < 0)
+ break;
+ }
+
+ git_str_dispose(&base);
+ git_str_dispose(&var);
+ git_str_dispose(&val);
+
+ if (error < 0) {
+ char *str;
+ git_vector_foreach(problems, i, str)
+ git__free(str);
+
+ git_vector_free(problems);
+ }
+
+ return error;
+}
+
+int git_remote_rename(git_strarray *out, git_repository *repo, const char *name, const char *new_name)
+{
+ int error;
+ git_vector problem_refspecs = GIT_VECTOR_INIT;
+ git_remote *remote = NULL;
+
+ GIT_ASSERT_ARG(out && repo && name && new_name);
+
+ if ((error = git_remote_lookup(&remote, repo, name)) < 0)
+ return error;
+
+ if ((error = ensure_remote_name_is_valid(new_name)) < 0)
+ goto cleanup;
+
+ if ((error = ensure_remote_doesnot_exist(repo, new_name)) < 0)
+ goto cleanup;
+
+ if ((error = rename_remote_config_section(repo, name, new_name)) < 0)
+ goto cleanup;
+
+ if ((error = update_branch_remote_config_entry(repo, name, new_name)) < 0)
+ goto cleanup;
+
+ if ((error = rename_remote_references(repo, name, new_name)) < 0)
+ goto cleanup;
+
+ if ((error = rename_fetch_refspecs(&problem_refspecs, remote, new_name)) < 0)
+ goto cleanup;
+
+ out->count = problem_refspecs.length;
+ out->strings = (char **) problem_refspecs.contents;
+
+cleanup:
+ if (error < 0)
+ git_vector_free(&problem_refspecs);
+
+ git_remote_free(remote);
+ return error;
+}
+
+int git_remote_name_is_valid(int *valid, const char *remote_name)
+{
+ git_str buf = GIT_STR_INIT;
+ git_refspec refspec = {0};
+ int error;
+
+ GIT_ASSERT(valid);
+
+ *valid = 0;
+
+ if (!remote_name || *remote_name == '\0')
+ return 0;
+
+ if ((error = git_str_printf(&buf, "refs/heads/test:refs/remotes/%s/test", remote_name)) < 0)
+ goto done;
+
+ error = git_refspec__parse(&refspec, git_str_cstr(&buf), true);
+
+ if (!error)
+ *valid = 1;
+ else if (error == GIT_EINVALIDSPEC)
+ error = 0;
+
+done:
+ git_str_dispose(&buf);
+ git_refspec__dispose(&refspec);
+
+ return error;
+}
+
+git_refspec *git_remote__matching_refspec(git_remote *remote, const char *refname)
+{
+ git_refspec *spec;
+ size_t i;
+
+ git_vector_foreach(&remote->active_refspecs, i, spec) {
+ if (spec->push)
+ continue;
+
+ if (git_refspec_src_matches(spec, refname))
+ return spec;
+ }
+
+ return NULL;
+}
+
+git_refspec *git_remote__matching_dst_refspec(git_remote *remote, const char *refname)
+{
+ git_refspec *spec;
+ size_t i;
+
+ git_vector_foreach(&remote->active_refspecs, i, spec) {
+ if (spec->push)
+ continue;
+
+ if (git_refspec_dst_matches(spec, refname))
+ return spec;
+ }
+
+ return NULL;
+}
+
+int git_remote_add_fetch(git_repository *repo, const char *remote, const char *refspec)
+{
+ return write_add_refspec(repo, remote, refspec, true);
+}
+
+int git_remote_add_push(git_repository *repo, const char *remote, const char *refspec)
+{
+ return write_add_refspec(repo, remote, refspec, false);
+}
+
+static int copy_refspecs(git_strarray *array, const git_remote *remote, unsigned int push)
+{
+ size_t i;
+ git_vector refspecs;
+ git_refspec *spec;
+ char *dup;
+
+ if (git_vector_init(&refspecs, remote->refspecs.length, NULL) < 0)
+ return -1;
+
+ git_vector_foreach(&remote->refspecs, i, spec) {
+ if (spec->push != push)
+ continue;
+
+ if ((dup = git__strdup(spec->string)) == NULL)
+ goto on_error;
+
+ if (git_vector_insert(&refspecs, dup) < 0) {
+ git__free(dup);
+ goto on_error;
+ }
+ }
+
+ array->strings = (char **)refspecs.contents;
+ array->count = refspecs.length;
+
+ return 0;
+
+on_error:
+ git_vector_free_deep(&refspecs);
+
+ return -1;
+}
+
+int git_remote_get_fetch_refspecs(git_strarray *array, const git_remote *remote)
+{
+ return copy_refspecs(array, remote, false);
+}
+
+int git_remote_get_push_refspecs(git_strarray *array, const git_remote *remote)
+{
+ return copy_refspecs(array, remote, true);
+}
+
+size_t git_remote_refspec_count(const git_remote *remote)
+{
+ return remote->refspecs.length;
+}
+
+const git_refspec *git_remote_get_refspec(const git_remote *remote, size_t n)
+{
+ return git_vector_get(&remote->refspecs, n);
+}
+
+int git_remote_init_callbacks(git_remote_callbacks *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_remote_callbacks, GIT_REMOTE_CALLBACKS_INIT);
+ return 0;
+}
+
+/* asserts a branch.<foo>.remote format */
+static const char *name_offset(size_t *len_out, const char *name)
+{
+ size_t prefix_len;
+ const char *dot;
+
+ prefix_len = strlen("remote.");
+ dot = strchr(name + prefix_len, '.');
+
+ GIT_ASSERT_ARG_WITH_RETVAL(dot, NULL);
+
+ *len_out = dot - name - prefix_len;
+ return name + prefix_len;
+}
+
+static int remove_branch_config_related_entries(
+ git_repository *repo,
+ const char *remote_name)
+{
+ int error;
+ git_config *config;
+ git_config_entry *entry;
+ git_config_iterator *iter;
+ git_str buf = GIT_STR_INIT;
+
+ if ((error = git_repository_config__weakptr(&config, repo)) < 0)
+ return error;
+
+ if ((error = git_config_iterator_glob_new(&iter, config, "branch\\..+\\.remote")) < 0)
+ return error;
+
+ /* find any branches with us as upstream and remove that config */
+ while ((error = git_config_next(&entry, iter)) == 0) {
+ const char *branch;
+ size_t branch_len;
+
+ if (strcmp(remote_name, entry->value))
+ continue;
+
+ if ((branch = name_offset(&branch_len, entry->name)) == NULL) {
+ error = -1;
+ break;
+ }
+
+ git_str_clear(&buf);
+ if ((error = git_str_printf(&buf, "branch.%.*s.merge", (int)branch_len, branch)) < 0)
+ break;
+
+ if ((error = git_config_delete_entry(config, git_str_cstr(&buf))) < 0) {
+ if (error != GIT_ENOTFOUND)
+ break;
+ git_error_clear();
+ }
+
+ git_str_clear(&buf);
+ if ((error = git_str_printf(&buf, "branch.%.*s.remote", (int)branch_len, branch)) < 0)
+ break;
+
+ if ((error = git_config_delete_entry(config, git_str_cstr(&buf))) < 0) {
+ if (error != GIT_ENOTFOUND)
+ break;
+ git_error_clear();
+ }
+ }
+
+ if (error == GIT_ITEROVER)
+ error = 0;
+
+ git_str_dispose(&buf);
+ git_config_iterator_free(iter);
+ return error;
+}
+
+static int remove_refs(git_repository *repo, const git_refspec *spec)
+{
+ git_reference_iterator *iter = NULL;
+ git_vector refs;
+ const char *name;
+ char *dup;
+ int error;
+ size_t i;
+
+ if ((error = git_vector_init(&refs, 8, NULL)) < 0)
+ return error;
+
+ if ((error = git_reference_iterator_new(&iter, repo)) < 0)
+ goto cleanup;
+
+ while ((error = git_reference_next_name(&name, iter)) == 0) {
+ if (!git_refspec_dst_matches(spec, name))
+ continue;
+
+ dup = git__strdup(name);
+ if (!dup) {
+ error = -1;
+ goto cleanup;
+ }
+
+ if ((error = git_vector_insert(&refs, dup)) < 0)
+ goto cleanup;
+ }
+ if (error == GIT_ITEROVER)
+ error = 0;
+ if (error < 0)
+ goto cleanup;
+
+ git_vector_foreach(&refs, i, name) {
+ if ((error = git_reference_remove(repo, name)) < 0)
+ break;
+ }
+
+cleanup:
+ git_reference_iterator_free(iter);
+ git_vector_foreach(&refs, i, dup) {
+ git__free(dup);
+ }
+ git_vector_free(&refs);
+ return error;
+}
+
+static int remove_remote_tracking(git_repository *repo, const char *remote_name)
+{
+ git_remote *remote;
+ int error;
+ size_t i, count;
+
+ /* we want to use what's on the config, regardless of changes to the instance in memory */
+ if ((error = git_remote_lookup(&remote, repo, remote_name)) < 0)
+ return error;
+
+ count = git_remote_refspec_count(remote);
+ for (i = 0; i < count; i++) {
+ const git_refspec *refspec = git_remote_get_refspec(remote, i);
+
+ /* shouldn't ever actually happen */
+ if (refspec == NULL)
+ continue;
+
+ if ((error = remove_refs(repo, refspec)) < 0)
+ break;
+ }
+
+ git_remote_free(remote);
+ return error;
+}
+
+int git_remote_delete(git_repository *repo, const char *name)
+{
+ int error;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
+
+ if ((error = remove_branch_config_related_entries(repo, name)) < 0 ||
+ (error = remove_remote_tracking(repo, name)) < 0 ||
+ (error = rename_remote_config_section(repo, name, NULL)) < 0)
+ return error;
+
+ return 0;
+}
+
+int git_remote_default_branch(git_buf *out, git_remote *remote)
+{
+ GIT_BUF_WRAP_PRIVATE(out, git_remote__default_branch, remote);
+}
+
+int git_remote__default_branch(git_str *out, git_remote *remote)
+{
+ const git_remote_head **heads;
+ const git_remote_head *guess = NULL;
+ const git_oid *head_id;
+ size_t heads_len, i;
+ git_str local_default = GIT_STR_INIT;
+ int error;
+
+ GIT_ASSERT_ARG(out);
+
+ if ((error = git_remote_ls(&heads, &heads_len, remote)) < 0)
+ goto done;
+
+ if (heads_len == 0 || strcmp(heads[0]->name, GIT_HEAD_FILE)) {
+ error = GIT_ENOTFOUND;
+ goto done;
+ }
+
+ /* the first one must be HEAD so if that has the symref info, we're done */
+ if (heads[0]->symref_target) {
+ error = git_str_puts(out, heads[0]->symref_target);
+ goto done;
+ }
+
+ /*
+ * If there's no symref information, we have to look over them
+ * and guess. We return the first match unless the default
+ * branch is a candidate. Then we return the default branch.
+ */
+
+ if ((error = git_repository_initialbranch(&local_default, remote->repo)) < 0)
+ goto done;
+
+ head_id = &heads[0]->oid;
+
+ for (i = 1; i < heads_len; i++) {
+ if (git_oid_cmp(head_id, &heads[i]->oid))
+ continue;
+
+ if (git__prefixcmp(heads[i]->name, GIT_REFS_HEADS_DIR))
+ continue;
+
+ if (!guess) {
+ guess = heads[i];
+ continue;
+ }
+
+ if (!git__strcmp(local_default.ptr, heads[i]->name)) {
+ guess = heads[i];
+ break;
+ }
+ }
+
+ if (!guess) {
+ error = GIT_ENOTFOUND;
+ goto done;
+ }
+
+ error = git_str_puts(out, guess->name);
+
+done:
+ git_str_dispose(&local_default);
+ return error;
+}
+
+int git_remote_upload(
+ git_remote *remote,
+ const git_strarray *refspecs,
+ const git_push_options *opts)
+{
+ git_remote_connect_options connect_opts = GIT_REMOTE_CONNECT_OPTIONS_INIT;
+ git_push *push;
+ git_refspec *spec;
+ size_t i;
+ int error;
+
+ GIT_ASSERT_ARG(remote);
+
+ if (!remote->repo) {
+ git_error_set(GIT_ERROR_INVALID, "cannot download detached remote");
+ return -1;
+ }
+
+ if ((error = git_remote_connect_options__from_push_opts(
+ &connect_opts, remote, opts)) < 0)
+ goto cleanup;
+
+ if ((error = connect_or_reset_options(remote, GIT_DIRECTION_PUSH, &connect_opts)) < 0)
+ goto cleanup;
+
+ free_refspecs(&remote->active_refspecs);
+ if ((error = dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs)) < 0)
+ goto cleanup;
+
+ if (remote->push) {
+ git_push_free(remote->push);
+ remote->push = NULL;
+ }
+
+ if ((error = git_push_new(&remote->push, remote, opts)) < 0)
+ goto cleanup;
+
+ push = remote->push;
+
+ if (refspecs && refspecs->count > 0) {
+ for (i = 0; i < refspecs->count; i++) {
+ if ((error = git_push_add_refspec(push, refspecs->strings[i])) < 0)
+ goto cleanup;
+ }
+ } else {
+ git_vector_foreach(&remote->refspecs, i, spec) {
+ if (!spec->push)
+ continue;
+ if ((error = git_push_add_refspec(push, spec->string)) < 0)
+ goto cleanup;
+ }
+ }
+
+ if ((error = git_push_finish(push)) < 0)
+ goto cleanup;
+
+ if (connect_opts.callbacks.push_update_reference &&
+ (error = git_push_status_foreach(push, connect_opts.callbacks.push_update_reference, connect_opts.callbacks.payload)) < 0)
+ goto cleanup;
+
+cleanup:
+ git_remote_connect_options_dispose(&connect_opts);
+ return error;
+}
+
+int git_remote_push(
+ git_remote *remote,
+ const git_strarray *refspecs,
+ const git_push_options *opts)
+{
+ git_remote_connect_options connect_opts = GIT_REMOTE_CONNECT_OPTIONS_INIT;
+ int error;
+
+ GIT_ASSERT_ARG(remote);
+
+ if (!remote->repo) {
+ git_error_set(GIT_ERROR_INVALID, "cannot download detached remote");
+ return -1;
+ }
+
+ if (git_remote_connect_options__from_push_opts(&connect_opts,
+ remote, opts) < 0)
+ return -1;
+
+ if ((error = git_remote_upload(remote, refspecs, opts)) < 0)
+ goto done;
+
+ error = git_remote_update_tips(remote, &connect_opts.callbacks, 0, 0, NULL);
+
+done:
+ git_remote_disconnect(remote);
+ git_remote_connect_options_dispose(&connect_opts);
+ return error;
+}
+
+#define PREFIX "url"
+#define SUFFIX_FETCH "insteadof"
+#define SUFFIX_PUSH "pushinsteadof"
+
+static int apply_insteadof(char **out, git_config *config, const char *url, int direction, bool use_default_if_empty)
+{
+ size_t match_length, prefix_length, suffix_length;
+ char *replacement = NULL;
+ const char *regexp;
+
+ git_str result = GIT_STR_INIT;
+ git_config_entry *entry;
+ git_config_iterator *iter;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(config);
+ GIT_ASSERT_ARG(url);
+ GIT_ASSERT_ARG(direction == GIT_DIRECTION_FETCH || direction == GIT_DIRECTION_PUSH);
+
+ /* Add 1 to prefix/suffix length due to the additional escaped dot */
+ prefix_length = strlen(PREFIX) + 1;
+ if (direction == GIT_DIRECTION_FETCH) {
+ regexp = PREFIX "\\..*\\." SUFFIX_FETCH;
+ suffix_length = strlen(SUFFIX_FETCH) + 1;
+ } else {
+ regexp = PREFIX "\\..*\\." SUFFIX_PUSH;
+ suffix_length = strlen(SUFFIX_PUSH) + 1;
+ }
+
+ if (git_config_iterator_glob_new(&iter, config, regexp) < 0)
+ return -1;
+
+ match_length = 0;
+ while (git_config_next(&entry, iter) == 0) {
+ size_t n, replacement_length;
+
+ /* Check if entry value is a prefix of URL */
+ if (git__prefixcmp(url, entry->value))
+ continue;
+
+ /* Check if entry value is longer than previous
+ * prefixes */
+ if ((n = strlen(entry->value)) <= match_length)
+ continue;
+
+ git__free(replacement);
+ match_length = n;
+
+ /* Cut off prefix and suffix of the value */
+ replacement_length =
+ strlen(entry->name) - (prefix_length + suffix_length);
+ replacement = git__strndup(entry->name + prefix_length,
+ replacement_length);
+ }
+
+ git_config_iterator_free(iter);
+
+ if (match_length == 0 && use_default_if_empty) {
+ *out = git__strdup(url);
+ return *out ? 0 : -1;
+ } else if (match_length == 0) {
+ *out = NULL;
+ return 0;
+ }
+
+ git_str_printf(&result, "%s%s", replacement, url + match_length);
+
+ git__free(replacement);
+
+ *out = git_str_detach(&result);
+ return 0;
+}
+
+/* Deprecated functions */
+
+#ifndef GIT_DEPRECATE_HARD
+
+int git_remote_is_valid_name(const char *remote_name)
+{
+ int valid = 0;
+
+ git_remote_name_is_valid(&valid, remote_name);
+ return valid;
+}
+
+#endif
diff --git a/src/libgit2/remote.h b/src/libgit2/remote.h
new file mode 100644
index 0000000..9e089be
--- /dev/null
+++ b/src/libgit2/remote.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_remote_h__
+#define INCLUDE_remote_h__
+
+#include "common.h"
+
+#include "git2/remote.h"
+#include "git2/transport.h"
+#include "git2/sys/remote.h"
+#include "git2/sys/transport.h"
+
+#include "refspec.h"
+#include "vector.h"
+#include "net.h"
+#include "proxy.h"
+
+#define GIT_REMOTE_ORIGIN "origin"
+
+struct git_remote {
+ char *name;
+ char *url;
+ char *pushurl;
+ git_vector refs;
+ git_vector refspecs;
+ git_vector active_refspecs;
+ git_vector passive_refspecs;
+ git_vector local_heads;
+ git_transport *transport;
+ git_repository *repo;
+ git_push *push;
+ git_indexer_progress stats;
+ unsigned int need_pack;
+ git_remote_autotag_option_t download_tags;
+ int prune_refs;
+ int passed_refspecs;
+ git_fetch_negotiation nego;
+};
+
+int git_remote__urlfordirection(git_str *url_out, struct git_remote *remote, int direction, const git_remote_callbacks *callbacks);
+int git_remote__http_proxy(char **out, git_remote *remote, git_net_url *url);
+
+git_refspec *git_remote__matching_refspec(git_remote *remote, const char *refname);
+git_refspec *git_remote__matching_dst_refspec(git_remote *remote, const char *refname);
+
+int git_remote__default_branch(git_str *out, git_remote *remote);
+
+int git_remote_connect_options_dup(
+ git_remote_connect_options *dst,
+ const git_remote_connect_options *src);
+int git_remote_connect_options_normalize(
+ git_remote_connect_options *dst,
+ git_repository *repo,
+ const git_remote_connect_options *src);
+
+int git_remote_capabilities(unsigned int *out, git_remote *remote);
+int git_remote_oid_type(git_oid_t *out, git_remote *remote);
+
+
+#define git_remote_connect_options__copy_opts(out, in) \
+ if (in) { \
+ (out)->callbacks = (in)->callbacks; \
+ (out)->proxy_opts = (in)->proxy_opts; \
+ (out)->custom_headers = (in)->custom_headers; \
+ (out)->follow_redirects = (in)->follow_redirects; \
+ }
+
+GIT_INLINE(int) git_remote_connect_options__from_fetch_opts(
+ git_remote_connect_options *out,
+ git_remote *remote,
+ const git_fetch_options *fetch_opts)
+{
+ git_remote_connect_options tmp = GIT_REMOTE_CONNECT_OPTIONS_INIT;
+ git_remote_connect_options__copy_opts(&tmp, fetch_opts);
+ return git_remote_connect_options_normalize(out, remote->repo, &tmp);
+}
+
+GIT_INLINE(int) git_remote_connect_options__from_push_opts(
+ git_remote_connect_options *out,
+ git_remote *remote,
+ const git_push_options *push_opts)
+{
+ git_remote_connect_options tmp = GIT_REMOTE_CONNECT_OPTIONS_INIT;
+ git_remote_connect_options__copy_opts(&tmp, push_opts);
+ return git_remote_connect_options_normalize(out, remote->repo, &tmp);
+}
+
+#undef git_remote_connect_options__copy_opts
+
+GIT_INLINE(void) git_remote_connect_options__dispose(
+ git_remote_connect_options *opts)
+{
+ git_proxy_options_dispose(&opts->proxy_opts);
+ git_strarray_dispose(&opts->custom_headers);
+}
+
+#endif
diff --git a/src/libgit2/repo_template.h b/src/libgit2/repo_template.h
new file mode 100644
index 0000000..099279a
--- /dev/null
+++ b/src/libgit2/repo_template.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_repo_template_h__
+#define INCLUDE_repo_template_h__
+
+#define GIT_OBJECTS_INFO_DIR GIT_OBJECTS_DIR "info/"
+#define GIT_OBJECTS_PACK_DIR GIT_OBJECTS_DIR "pack/"
+
+#define GIT_HOOKS_DIR "hooks/"
+#define GIT_HOOKS_DIR_MODE 0777
+
+#define GIT_HOOKS_README_FILE GIT_HOOKS_DIR "README.sample"
+#define GIT_HOOKS_README_MODE 0777
+#define GIT_HOOKS_README_CONTENT \
+"#!/bin/sh\n"\
+"#\n"\
+"# Place appropriately named executable hook scripts into this directory\n"\
+"# to intercept various actions that git takes. See `git help hooks` for\n"\
+"# more information.\n"
+
+#define GIT_INFO_DIR "info/"
+#define GIT_INFO_DIR_MODE 0777
+
+#define GIT_INFO_EXCLUDE_FILE GIT_INFO_DIR "exclude"
+#define GIT_INFO_EXCLUDE_MODE 0666
+#define GIT_INFO_EXCLUDE_CONTENT \
+"# File patterns to ignore; see `git help ignore` for more information.\n"\
+"# Lines that start with '#' are comments.\n"
+
+#define GIT_DESC_FILE "description"
+#define GIT_DESC_MODE 0666
+#define GIT_DESC_CONTENT \
+"Unnamed repository; edit this file 'description' to name the repository.\n"
+
+typedef struct {
+ const char *path;
+ mode_t mode;
+ const char *content;
+} repo_template_item;
+
+static repo_template_item repo_template[] = {
+ { GIT_OBJECTS_INFO_DIR, GIT_OBJECT_DIR_MODE, NULL }, /* '/objects/info/' */
+ { GIT_OBJECTS_PACK_DIR, GIT_OBJECT_DIR_MODE, NULL }, /* '/objects/pack/' */
+ { GIT_REFS_HEADS_DIR, GIT_REFS_DIR_MODE, NULL }, /* '/refs/heads/' */
+ { GIT_REFS_TAGS_DIR, GIT_REFS_DIR_MODE, NULL }, /* '/refs/tags/' */
+ { GIT_HOOKS_DIR, GIT_HOOKS_DIR_MODE, NULL }, /* '/hooks/' */
+ { GIT_INFO_DIR, GIT_INFO_DIR_MODE, NULL }, /* '/info/' */
+ { GIT_DESC_FILE, GIT_DESC_MODE, GIT_DESC_CONTENT },
+ { GIT_HOOKS_README_FILE, GIT_HOOKS_README_MODE, GIT_HOOKS_README_CONTENT },
+ { GIT_INFO_EXCLUDE_FILE, GIT_INFO_EXCLUDE_MODE, GIT_INFO_EXCLUDE_CONTENT },
+ { NULL, 0, NULL }
+};
+
+#endif
diff --git a/src/libgit2/repository.c b/src/libgit2/repository.c
new file mode 100644
index 0000000..05ece6e
--- /dev/null
+++ b/src/libgit2/repository.c
@@ -0,0 +1,3841 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "repository.h"
+
+#include <ctype.h>
+
+#include "git2/object.h"
+#include "git2/sys/repository.h"
+
+#include "buf.h"
+#include "common.h"
+#include "commit.h"
+#include "grafts.h"
+#include "tag.h"
+#include "blob.h"
+#include "futils.h"
+#include "sysdir.h"
+#include "filebuf.h"
+#include "index.h"
+#include "config.h"
+#include "refs.h"
+#include "filter.h"
+#include "odb.h"
+#include "refdb.h"
+#include "remote.h"
+#include "merge.h"
+#include "diff_driver.h"
+#include "annotated_commit.h"
+#include "submodule.h"
+#include "worktree.h"
+#include "path.h"
+#include "strmap.h"
+
+#ifdef GIT_WIN32
+# include "win32/w32_util.h"
+#endif
+
+bool git_repository__validate_ownership = true;
+bool git_repository__fsync_gitdir = false;
+
+static const struct {
+ git_repository_item_t parent;
+ git_repository_item_t fallback;
+ const char *name;
+ bool directory;
+} items[] = {
+ { GIT_REPOSITORY_ITEM_GITDIR, GIT_REPOSITORY_ITEM__LAST, NULL, true },
+ { GIT_REPOSITORY_ITEM_WORKDIR, GIT_REPOSITORY_ITEM__LAST, NULL, true },
+ { GIT_REPOSITORY_ITEM_COMMONDIR, GIT_REPOSITORY_ITEM__LAST, NULL, true },
+ { GIT_REPOSITORY_ITEM_GITDIR, GIT_REPOSITORY_ITEM__LAST, "index", false },
+ { GIT_REPOSITORY_ITEM_COMMONDIR, GIT_REPOSITORY_ITEM_GITDIR, "objects", true },
+ { GIT_REPOSITORY_ITEM_COMMONDIR, GIT_REPOSITORY_ITEM_GITDIR, "refs", true },
+ { GIT_REPOSITORY_ITEM_COMMONDIR, GIT_REPOSITORY_ITEM_GITDIR, "packed-refs", false },
+ { GIT_REPOSITORY_ITEM_COMMONDIR, GIT_REPOSITORY_ITEM_GITDIR, "remotes", true },
+ { GIT_REPOSITORY_ITEM_COMMONDIR, GIT_REPOSITORY_ITEM_GITDIR, "config", false },
+ { GIT_REPOSITORY_ITEM_COMMONDIR, GIT_REPOSITORY_ITEM_GITDIR, "info", true },
+ { GIT_REPOSITORY_ITEM_COMMONDIR, GIT_REPOSITORY_ITEM_GITDIR, "hooks", true },
+ { GIT_REPOSITORY_ITEM_COMMONDIR, GIT_REPOSITORY_ITEM_GITDIR, "logs", true },
+ { GIT_REPOSITORY_ITEM_GITDIR, GIT_REPOSITORY_ITEM__LAST, "modules", true },
+ { GIT_REPOSITORY_ITEM_COMMONDIR, GIT_REPOSITORY_ITEM_GITDIR, "worktrees", true }
+};
+
+static int check_repositoryformatversion(int *version, git_config *config);
+static int check_extensions(git_config *config, int version);
+static int load_global_config(git_config **config, bool use_env);
+static int load_objectformat(git_repository *repo, git_config *config);
+
+#define GIT_COMMONDIR_FILE "commondir"
+#define GIT_GITDIR_FILE "gitdir"
+
+#define GIT_FILE_CONTENT_PREFIX "gitdir:"
+
+#define GIT_BRANCH_DEFAULT "master"
+
+#define GIT_REPO_VERSION_DEFAULT 0
+#define GIT_REPO_VERSION_MAX 1
+
+git_str git_repository__reserved_names_win32[] = {
+ { DOT_GIT, 0, CONST_STRLEN(DOT_GIT) },
+ { GIT_DIR_SHORTNAME, 0, CONST_STRLEN(GIT_DIR_SHORTNAME) }
+};
+size_t git_repository__reserved_names_win32_len = 2;
+
+git_str git_repository__reserved_names_posix[] = {
+ { DOT_GIT, 0, CONST_STRLEN(DOT_GIT) },
+};
+size_t git_repository__reserved_names_posix_len = 1;
+
+static void set_odb(git_repository *repo, git_odb *odb)
+{
+ if (odb) {
+ GIT_REFCOUNT_OWN(odb, repo);
+ GIT_REFCOUNT_INC(odb);
+ }
+
+ if ((odb = git_atomic_swap(repo->_odb, odb)) != NULL) {
+ GIT_REFCOUNT_OWN(odb, NULL);
+ git_odb_free(odb);
+ }
+}
+
+static void set_refdb(git_repository *repo, git_refdb *refdb)
+{
+ if (refdb) {
+ GIT_REFCOUNT_OWN(refdb, repo);
+ GIT_REFCOUNT_INC(refdb);
+ }
+
+ if ((refdb = git_atomic_swap(repo->_refdb, refdb)) != NULL) {
+ GIT_REFCOUNT_OWN(refdb, NULL);
+ git_refdb_free(refdb);
+ }
+}
+
+static void set_config(git_repository *repo, git_config *config)
+{
+ if (config) {
+ GIT_REFCOUNT_OWN(config, repo);
+ GIT_REFCOUNT_INC(config);
+ }
+
+ if ((config = git_atomic_swap(repo->_config, config)) != NULL) {
+ GIT_REFCOUNT_OWN(config, NULL);
+ git_config_free(config);
+ }
+
+ git_repository__configmap_lookup_cache_clear(repo);
+}
+
+static void set_index(git_repository *repo, git_index *index)
+{
+ if (index) {
+ GIT_REFCOUNT_OWN(index, repo);
+ GIT_REFCOUNT_INC(index);
+ }
+
+ if ((index = git_atomic_swap(repo->_index, index)) != NULL) {
+ GIT_REFCOUNT_OWN(index, NULL);
+ git_index_free(index);
+ }
+}
+
+int git_repository__cleanup(git_repository *repo)
+{
+ GIT_ASSERT_ARG(repo);
+
+ git_repository_submodule_cache_clear(repo);
+ git_cache_clear(&repo->objects);
+ git_attr_cache_flush(repo);
+ git_grafts_free(repo->grafts);
+ repo->grafts = NULL;
+ git_grafts_free(repo->shallow_grafts);
+ repo->shallow_grafts = NULL;
+
+ set_config(repo, NULL);
+ set_index(repo, NULL);
+ set_odb(repo, NULL);
+ set_refdb(repo, NULL);
+
+ return 0;
+}
+
+void git_repository_free(git_repository *repo)
+{
+ size_t i;
+
+ if (repo == NULL)
+ return;
+
+ git_repository__cleanup(repo);
+
+ git_cache_dispose(&repo->objects);
+
+ git_diff_driver_registry_free(repo->diff_drivers);
+ repo->diff_drivers = NULL;
+
+ for (i = 0; i < repo->reserved_names.size; i++)
+ git_str_dispose(git_array_get(repo->reserved_names, i));
+ git_array_clear(repo->reserved_names);
+
+ git__free(repo->gitlink);
+ git__free(repo->gitdir);
+ git__free(repo->commondir);
+ git__free(repo->workdir);
+ git__free(repo->namespace);
+ git__free(repo->ident_name);
+ git__free(repo->ident_email);
+
+ git__memzero(repo, sizeof(*repo));
+ git__free(repo);
+}
+
+/* Check if we have a separate commondir (e.g. we have a worktree) */
+static int lookup_commondir(
+ bool *separate,
+ git_str *commondir,
+ git_str *repository_path,
+ uint32_t flags)
+{
+ git_str common_link = GIT_STR_INIT;
+ int error;
+
+ /* Environment variable overrides configuration */
+ if ((flags & GIT_REPOSITORY_OPEN_FROM_ENV)) {
+ error = git__getenv(commondir, "GIT_COMMON_DIR");
+
+ if (!error || error != GIT_ENOTFOUND)
+ goto done;
+ }
+
+ /*
+ * If there's no commondir file, the repository path is the
+ * common path, but it needs a trailing slash.
+ */
+ if (!git_fs_path_contains_file(repository_path, GIT_COMMONDIR_FILE)) {
+ if ((error = git_str_set(commondir, repository_path->ptr, repository_path->size)) == 0)
+ error = git_fs_path_to_dir(commondir);
+
+ *separate = false;
+ goto done;
+ }
+
+ *separate = true;
+
+ if ((error = git_str_joinpath(&common_link, repository_path->ptr, GIT_COMMONDIR_FILE)) < 0 ||
+ (error = git_futils_readbuffer(&common_link, common_link.ptr)) < 0)
+ goto done;
+
+ git_str_rtrim(&common_link);
+ if (git_fs_path_is_relative(common_link.ptr)) {
+ if ((error = git_str_joinpath(commondir, repository_path->ptr, common_link.ptr)) < 0)
+ goto done;
+ } else {
+ git_str_swap(commondir, &common_link);
+ }
+
+ /* Make sure the commondir path always has a trailing slash */
+ error = git_fs_path_prettify_dir(commondir, commondir->ptr, NULL);
+
+done:
+ git_str_dispose(&common_link);
+ return error;
+}
+
+GIT_INLINE(int) validate_repo_path(git_str *path)
+{
+ /*
+ * The longest static path in a repository (or commondir) is the
+ * packed refs file. (Loose refs may be longer since they
+ * include the reference name, but will be validated when the
+ * path is constructed.)
+ */
+ static size_t suffix_len =
+ CONST_STRLEN("objects/pack/pack-.pack.lock") +
+ GIT_OID_MAX_HEXSIZE;
+
+ return git_fs_path_validate_str_length_with_suffix(
+ path, suffix_len);
+}
+
+/*
+ * Git repository open methods
+ *
+ * Open a repository object from its path
+ */
+static int is_valid_repository_path(
+ bool *out,
+ git_str *repository_path,
+ git_str *common_path,
+ uint32_t flags)
+{
+ bool separate_commondir = false;
+ int error;
+
+ *out = false;
+
+ if ((error = lookup_commondir(&separate_commondir,
+ common_path, repository_path, flags)) < 0)
+ return error;
+
+ /* Ensure HEAD file exists */
+ if (git_fs_path_contains_file(repository_path, GIT_HEAD_FILE) == false)
+ return 0;
+
+ /* Check files in common dir */
+ if (git_fs_path_contains_dir(common_path, GIT_OBJECTS_DIR) == false)
+ return 0;
+ if (git_fs_path_contains_dir(common_path, GIT_REFS_DIR) == false)
+ return 0;
+
+ /* Ensure the repo (and commondir) are valid paths */
+ if ((error = validate_repo_path(common_path)) < 0 ||
+ (separate_commondir &&
+ (error = validate_repo_path(repository_path)) < 0))
+ return error;
+
+ *out = true;
+ return 0;
+}
+
+static git_repository *repository_alloc(void)
+{
+ git_repository *repo = git__calloc(1, sizeof(git_repository));
+
+ if (repo == NULL ||
+ git_cache_init(&repo->objects) < 0)
+ goto on_error;
+
+ git_array_init_to_size(repo->reserved_names, 4);
+ if (!repo->reserved_names.ptr)
+ goto on_error;
+
+ /* set all the entries in the configmap cache to `unset` */
+ git_repository__configmap_lookup_cache_clear(repo);
+
+ return repo;
+
+on_error:
+ if (repo)
+ git_cache_dispose(&repo->objects);
+
+ git__free(repo);
+ return NULL;
+}
+
+int git_repository_new(git_repository **out)
+{
+ git_repository *repo;
+
+ *out = repo = repository_alloc();
+ GIT_ERROR_CHECK_ALLOC(repo);
+
+ repo->is_bare = 1;
+ repo->is_worktree = 0;
+
+ return 0;
+}
+
+static int load_config_data(git_repository *repo, const git_config *config)
+{
+ int is_bare;
+
+ int err = git_config_get_bool(&is_bare, config, "core.bare");
+ if (err < 0 && err != GIT_ENOTFOUND)
+ return err;
+
+ /* Try to figure out if it's bare, default to non-bare if it's not set */
+ if (err != GIT_ENOTFOUND)
+ repo->is_bare = is_bare && !repo->is_worktree;
+ else
+ repo->is_bare = 0;
+
+ return 0;
+}
+
+static int load_workdir(
+ git_repository *repo,
+ git_config *config,
+ git_str *parent_path)
+{
+ git_config_entry *ce = NULL;
+ git_str worktree = GIT_STR_INIT;
+ git_str path = GIT_STR_INIT;
+ git_str workdir_env = GIT_STR_INIT;
+ const char *value = NULL;
+ int error;
+
+ if (repo->is_bare)
+ return 0;
+
+ /* Environment variables are preferred */
+ if (repo->use_env) {
+ error = git__getenv(&workdir_env, "GIT_WORK_TREE");
+
+ if (error == 0)
+ value = workdir_env.ptr;
+ else if (error == GIT_ENOTFOUND)
+ error = 0;
+ else
+ goto cleanup;
+ }
+
+ /* Examine configuration values if necessary */
+ if (!value) {
+ if ((error = git_config__lookup_entry(&ce, config,
+ "core.worktree", false)) < 0)
+ return error;
+
+ if (ce && ce->value)
+ value = ce->value;
+ }
+
+ if (repo->is_worktree) {
+ char *gitlink = git_worktree__read_link(repo->gitdir, GIT_GITDIR_FILE);
+ if (!gitlink) {
+ error = -1;
+ goto cleanup;
+ }
+
+ git_str_attach(&worktree, gitlink, 0);
+
+ if ((git_fs_path_dirname_r(&worktree, worktree.ptr)) < 0 ||
+ git_fs_path_to_dir(&worktree) < 0) {
+ error = -1;
+ goto cleanup;
+ }
+
+ repo->workdir = git_str_detach(&worktree);
+ } else if (value) {
+ if (!*value) {
+ git_error_set(GIT_ERROR_NET, "working directory cannot be set to empty path");
+ error = -1;
+ goto cleanup;
+ }
+
+ if ((error = git_fs_path_prettify_dir(&worktree,
+ value, repo->gitdir)) < 0)
+ goto cleanup;
+
+ repo->workdir = git_str_detach(&worktree);
+ } else if (parent_path && git_fs_path_isdir(parent_path->ptr)) {
+ repo->workdir = git_str_detach(parent_path);
+ } else {
+ if (git_fs_path_dirname_r(&worktree, repo->gitdir) < 0 ||
+ git_fs_path_to_dir(&worktree) < 0) {
+ error = -1;
+ goto cleanup;
+ }
+
+ repo->workdir = git_str_detach(&worktree);
+ }
+
+ GIT_ERROR_CHECK_ALLOC(repo->workdir);
+
+cleanup:
+ git_str_dispose(&path);
+ git_str_dispose(&workdir_env);
+ git_config_entry_free(ce);
+ return error;
+}
+
+/*
+ * This function returns furthest offset into path where a ceiling dir
+ * is found, so we can stop processing the path at that point.
+ *
+ * Note: converting this to use git_strs instead of GIT_PATH_MAX buffers on
+ * the stack could remove directories name limits, but at the cost of doing
+ * repeated malloc/frees inside the loop below, so let's not do it now.
+ */
+static size_t find_ceiling_dir_offset(
+ const char *path,
+ const char *ceiling_directories)
+{
+ char buf[GIT_PATH_MAX + 1];
+ char buf2[GIT_PATH_MAX + 1];
+ const char *ceil, *sep;
+ size_t len, max_len = 0, min_len;
+
+ GIT_ASSERT_ARG(path);
+
+ min_len = (size_t)(git_fs_path_root(path) + 1);
+
+ if (ceiling_directories == NULL || min_len == 0)
+ return min_len;
+
+ for (sep = ceil = ceiling_directories; *sep; ceil = sep + 1) {
+ for (sep = ceil; *sep && *sep != GIT_PATH_LIST_SEPARATOR; sep++);
+ len = sep - ceil;
+
+ if (len == 0 || len >= sizeof(buf) || git_fs_path_root(ceil) == -1)
+ continue;
+
+ strncpy(buf, ceil, len);
+ buf[len] = '\0';
+
+ if (p_realpath(buf, buf2) == NULL)
+ continue;
+
+ len = strlen(buf2);
+ if (len > 0 && buf2[len-1] == '/')
+ buf[--len] = '\0';
+
+ if (!strncmp(path, buf2, len) &&
+ (path[len] == '/' || !path[len]) &&
+ len > max_len)
+ {
+ max_len = len;
+ }
+ }
+
+ return (max_len <= min_len ? min_len : max_len);
+}
+
+/*
+ * Read the contents of `file_path` and set `path_out` to the repo dir that
+ * it points to. Before calling, set `path_out` to the base directory that
+ * should be used if the contents of `file_path` are a relative path.
+ */
+static int read_gitfile(git_str *path_out, const char *file_path)
+{
+ int error = 0;
+ git_str file = GIT_STR_INIT;
+ size_t prefix_len = strlen(GIT_FILE_CONTENT_PREFIX);
+
+ GIT_ASSERT_ARG(path_out);
+ GIT_ASSERT_ARG(file_path);
+
+ if (git_futils_readbuffer(&file, file_path) < 0)
+ return -1;
+
+ git_str_rtrim(&file);
+ /* apparently on Windows, some people use backslashes in paths */
+ git_fs_path_mkposix(file.ptr);
+
+ if (git_str_len(&file) <= prefix_len ||
+ memcmp(git_str_cstr(&file), GIT_FILE_CONTENT_PREFIX, prefix_len) != 0)
+ {
+ git_error_set(GIT_ERROR_REPOSITORY,
+ "the `.git` file at '%s' is malformed", file_path);
+ error = -1;
+ }
+ else if ((error = git_fs_path_dirname_r(path_out, file_path)) >= 0) {
+ const char *gitlink = git_str_cstr(&file) + prefix_len;
+ while (*gitlink && git__isspace(*gitlink)) gitlink++;
+
+ error = git_fs_path_prettify_dir(
+ path_out, gitlink, git_str_cstr(path_out));
+ }
+
+ git_str_dispose(&file);
+ return error;
+}
+
+typedef struct {
+ const char *repo_path;
+ git_str tmp;
+ bool *is_safe;
+} validate_ownership_data;
+
+static int validate_ownership_cb(const git_config_entry *entry, void *payload)
+{
+ validate_ownership_data *data = payload;
+
+ if (strcmp(entry->value, "") == 0) {
+ *data->is_safe = false;
+ } else if (strcmp(entry->value, "*") == 0) {
+ *data->is_safe = true;
+ } else {
+ const char *test_path = entry->value;
+
+#ifdef GIT_WIN32
+ /*
+ * Git for Windows does some truly bizarre things with
+ * paths that start with a forward slash; and expects you
+ * to escape that with `%(prefix)`. This syntax generally
+ * means to add the prefix that Git was installed to -- eg
+ * `/usr/local` -- unless it's an absolute path, in which
+ * case the leading `%(prefix)/` is just removed. And Git
+ * for Windows expects you to use this syntax for absolute
+ * Unix-style paths (in "Git Bash" or Windows Subsystem for
+ * Linux).
+ *
+ * Worse, the behavior used to be that a leading `/` was
+ * not absolute. It would indicate that Git for Windows
+ * should add the prefix. So `//` is required for absolute
+ * Unix-style paths. Yes, this is truly horrifying.
+ *
+ * Emulate that behavior, I guess, but only for absolute
+ * paths. We won't deal with the Git install prefix. Also,
+ * give WSL users an escape hatch where they don't have to
+ * think about this and can use the literal path that the
+ * filesystem APIs provide (`//wsl.localhost/...`).
+ */
+ if (strncmp(test_path, "%(prefix)//", strlen("%(prefix)//")) == 0)
+ test_path += strlen("%(prefix)/");
+ else if (strncmp(test_path, "//", 2) == 0 &&
+ strncmp(test_path, "//wsl.localhost/", strlen("//wsl.localhost/")) != 0)
+ test_path++;
+#endif
+
+ if (git_fs_path_prettify_dir(&data->tmp, test_path, NULL) == 0 &&
+ strcmp(data->tmp.ptr, data->repo_path) == 0)
+ *data->is_safe = true;
+ }
+
+ return 0;
+}
+
+static int validate_ownership_config(
+ bool *is_safe,
+ const char *path,
+ bool use_env)
+{
+ validate_ownership_data ownership_data = {
+ path, GIT_STR_INIT, is_safe
+ };
+ git_config *config;
+ int error;
+
+ if (load_global_config(&config, use_env) != 0)
+ return 0;
+
+ error = git_config_get_multivar_foreach(config,
+ "safe.directory", NULL,
+ validate_ownership_cb,
+ &ownership_data);
+
+ if (error == GIT_ENOTFOUND)
+ error = 0;
+
+ git_config_free(config);
+ git_str_dispose(&ownership_data.tmp);
+
+ return error;
+}
+
+static int validate_ownership_path(bool *is_safe, const char *path)
+{
+ git_fs_path_owner_t owner_level =
+ GIT_FS_PATH_OWNER_CURRENT_USER |
+ GIT_FS_PATH_USER_IS_ADMINISTRATOR |
+ GIT_FS_PATH_OWNER_RUNNING_SUDO;
+ int error = 0;
+
+ if (path)
+ error = git_fs_path_owner_is(is_safe, path, owner_level);
+
+ if (error == GIT_ENOTFOUND) {
+ *is_safe = true;
+ error = 0;
+ } else if (error == GIT_EINVALID) {
+ *is_safe = false;
+ error = 0;
+ }
+
+ return error;
+}
+
+static int validate_ownership(git_repository *repo)
+{
+ const char *validation_paths[3] = { NULL }, *path;
+ size_t validation_len = 0, i;
+ bool is_safe = false;
+ int error = 0;
+
+ /*
+ * If there's a worktree, validate the permissions to it *and*
+ * the git directory, and use the worktree as the configuration
+ * key for allowlisting the directory. In a bare setup, only
+ * look at the gitdir and use that as the allowlist. So we
+ * examine all `validation_paths` but use only the first as
+ * the configuration lookup.
+ */
+
+ if (repo->workdir)
+ validation_paths[validation_len++] = repo->workdir;
+
+ if (repo->gitlink)
+ validation_paths[validation_len++] = repo->gitlink;
+
+ validation_paths[validation_len++] = repo->gitdir;
+
+ for (i = 0; i < validation_len; i++) {
+ path = validation_paths[i];
+
+ if ((error = validate_ownership_path(&is_safe, path)) < 0)
+ goto done;
+
+ if (!is_safe)
+ break;
+ }
+
+ if (is_safe ||
+ (error = validate_ownership_config(
+ &is_safe, validation_paths[0], repo->use_env)) < 0)
+ goto done;
+
+ if (!is_safe) {
+ git_error_set(GIT_ERROR_CONFIG,
+ "repository path '%s' is not owned by current user",
+ path);
+ error = GIT_EOWNER;
+ }
+
+done:
+ return error;
+}
+
+struct repo_paths {
+ git_str gitdir;
+ git_str workdir;
+ git_str gitlink;
+ git_str commondir;
+};
+
+#define REPO_PATHS_INIT { GIT_STR_INIT }
+
+GIT_INLINE(void) repo_paths_dispose(struct repo_paths *paths)
+{
+ git_str_dispose(&paths->gitdir);
+ git_str_dispose(&paths->workdir);
+ git_str_dispose(&paths->gitlink);
+ git_str_dispose(&paths->commondir);
+}
+
+static int find_repo_traverse(
+ struct repo_paths *out,
+ const char *start_path,
+ const char *ceiling_dirs,
+ uint32_t flags)
+{
+ git_str path = GIT_STR_INIT;
+ git_str repo_link = GIT_STR_INIT;
+ git_str common_link = GIT_STR_INIT;
+ struct stat st;
+ dev_t initial_device = 0;
+ int min_iterations;
+ bool in_dot_git, is_valid;
+ size_t ceiling_offset = 0;
+ int error;
+
+ git_str_clear(&out->gitdir);
+
+ if ((error = git_fs_path_prettify(&path, start_path, NULL)) < 0)
+ return error;
+
+ /*
+ * In each loop we look first for a `.git` dir within the
+ * directory, then to see if the directory itself is a repo.
+ *
+ * In other words: if we start in /a/b/c, then we look at:
+ * /a/b/c/.git, /a/b/c, /a/b/.git, /a/b, /a/.git, /a
+ *
+ * With GIT_REPOSITORY_OPEN_BARE or GIT_REPOSITORY_OPEN_NO_DOTGIT,
+ * we assume we started with /a/b/c.git and don't append .git the
+ * first time through. min_iterations indicates the number of
+ * iterations left before going further counts as a search.
+ */
+ if (flags & (GIT_REPOSITORY_OPEN_BARE | GIT_REPOSITORY_OPEN_NO_DOTGIT)) {
+ in_dot_git = true;
+ min_iterations = 1;
+ } else {
+ in_dot_git = false;
+ min_iterations = 2;
+ }
+
+ for (;;) {
+ if (!(flags & GIT_REPOSITORY_OPEN_NO_DOTGIT)) {
+ if (!in_dot_git) {
+ if ((error = git_str_joinpath(&path, path.ptr, DOT_GIT)) < 0)
+ goto out;
+ }
+ in_dot_git = !in_dot_git;
+ }
+
+ if (p_stat(path.ptr, &st) == 0) {
+ /* check that we have not crossed device boundaries */
+ if (initial_device == 0)
+ initial_device = st.st_dev;
+ else if (st.st_dev != initial_device &&
+ !(flags & GIT_REPOSITORY_OPEN_CROSS_FS))
+ break;
+
+ if (S_ISDIR(st.st_mode)) {
+ if ((error = is_valid_repository_path(&is_valid, &path, &common_link, flags)) < 0)
+ goto out;
+
+ if (is_valid) {
+ if ((error = git_fs_path_to_dir(&path)) < 0 ||
+ (error = git_str_set(&out->gitdir, path.ptr, path.size)) < 0)
+ goto out;
+
+ if ((error = git_str_attach(&out->gitlink, git_worktree__read_link(path.ptr, GIT_GITDIR_FILE), 0)) < 0)
+ goto out;
+
+ git_str_swap(&common_link, &out->commondir);
+
+ break;
+ }
+ } else if (S_ISREG(st.st_mode) && git__suffixcmp(path.ptr, "/" DOT_GIT) == 0) {
+ if ((error = read_gitfile(&repo_link, path.ptr)) < 0 ||
+ (error = is_valid_repository_path(&is_valid, &repo_link, &common_link, flags)) < 0)
+ goto out;
+
+ if (is_valid) {
+ git_str_swap(&out->gitdir, &repo_link);
+
+ if ((error = git_str_put(&out->gitlink, path.ptr, path.size)) < 0)
+ goto out;
+
+ git_str_swap(&common_link, &out->commondir);
+ }
+ break;
+ }
+ }
+
+ /*
+ * Move up one directory. If we're in_dot_git, we'll
+ * search the parent itself next. If we're !in_dot_git,
+ * we'll search .git in the parent directory next (added
+ * at the top of the loop).
+ */
+ if ((error = git_fs_path_dirname_r(&path, path.ptr)) < 0)
+ goto out;
+
+ /*
+ * Once we've checked the directory (and .git if
+ * applicable), find the ceiling for a search.
+ */
+ if (min_iterations && (--min_iterations == 0))
+ ceiling_offset = find_ceiling_dir_offset(path.ptr, ceiling_dirs);
+
+ /* Check if we should stop searching here. */
+ if (min_iterations == 0 &&
+ (path.ptr[ceiling_offset] == 0 || (flags & GIT_REPOSITORY_OPEN_NO_SEARCH)))
+ break;
+ }
+
+ if (!(flags & GIT_REPOSITORY_OPEN_BARE)) {
+ if (!git_str_len(&out->gitdir))
+ git_str_clear(&out->workdir);
+ else if ((error = git_fs_path_dirname_r(&out->workdir, path.ptr)) < 0 ||
+ (error = git_fs_path_to_dir(&out->workdir)) < 0)
+ goto out;
+ }
+
+ /* If we didn't find the repository, and we don't have any other
+ * error to report, report that. */
+ if (!git_str_len(&out->gitdir)) {
+ git_error_set(GIT_ERROR_REPOSITORY, "could not find repository at '%s'", start_path);
+ error = GIT_ENOTFOUND;
+ goto out;
+ }
+
+out:
+ if (error)
+ repo_paths_dispose(out);
+
+ git_str_dispose(&path);
+ git_str_dispose(&repo_link);
+ git_str_dispose(&common_link);
+ return error;
+}
+
+static int load_grafts(git_repository *repo)
+{
+ git_str path = GIT_STR_INIT;
+ int error;
+
+ if ((error = git_repository__item_path(&path, repo, GIT_REPOSITORY_ITEM_INFO)) < 0 ||
+ (error = git_str_joinpath(&path, path.ptr, "grafts")) < 0 ||
+ (error = git_grafts_open_or_refresh(&repo->grafts, path.ptr, repo->oid_type)) < 0)
+ goto error;
+
+ git_str_clear(&path);
+
+ if ((error = git_str_joinpath(&path, repo->gitdir, "shallow")) < 0 ||
+ (error = git_grafts_open_or_refresh(&repo->shallow_grafts, path.ptr, repo->oid_type)) < 0)
+ goto error;
+
+error:
+ git_str_dispose(&path);
+ return error;
+}
+
+static int find_repo(
+ struct repo_paths *out,
+ const char *start_path,
+ const char *ceiling_dirs,
+ uint32_t flags)
+{
+ bool use_env = !!(flags & GIT_REPOSITORY_OPEN_FROM_ENV);
+ git_str gitdir_buf = GIT_STR_INIT,
+ ceiling_dirs_buf = GIT_STR_INIT,
+ across_fs_buf = GIT_STR_INIT;
+ int error;
+
+ if (use_env && !start_path) {
+ error = git__getenv(&gitdir_buf, "GIT_DIR");
+
+ if (!error) {
+ start_path = gitdir_buf.ptr;
+ flags |= GIT_REPOSITORY_OPEN_NO_SEARCH;
+ flags |= GIT_REPOSITORY_OPEN_NO_DOTGIT;
+ } else if (error == GIT_ENOTFOUND) {
+ start_path = ".";
+ } else {
+ goto done;
+ }
+ }
+
+ if (use_env && !ceiling_dirs) {
+ error = git__getenv(&ceiling_dirs_buf,
+ "GIT_CEILING_DIRECTORIES");
+
+ if (!error)
+ ceiling_dirs = ceiling_dirs_buf.ptr;
+ else if (error != GIT_ENOTFOUND)
+ goto done;
+ }
+
+ if (use_env) {
+ error = git__getenv(&across_fs_buf,
+ "GIT_DISCOVERY_ACROSS_FILESYSTEM");
+
+ if (!error) {
+ int across_fs = 0;
+
+ if ((error = git_config_parse_bool(&across_fs,
+ git_str_cstr(&across_fs_buf))) < 0)
+ goto done;
+
+ if (across_fs)
+ flags |= GIT_REPOSITORY_OPEN_CROSS_FS;
+ } else if (error != GIT_ENOTFOUND) {
+ goto done;
+ }
+ }
+
+ error = find_repo_traverse(out, start_path, ceiling_dirs, flags);
+
+done:
+ git_str_dispose(&gitdir_buf);
+ git_str_dispose(&ceiling_dirs_buf);
+ git_str_dispose(&across_fs_buf);
+
+ return error;
+}
+
+static int obtain_config_and_set_oid_type(
+ git_config **config_ptr,
+ git_repository *repo)
+{
+ int error;
+ git_config *config = NULL;
+ int version = 0;
+
+ /*
+ * We'd like to have the config, but git doesn't particularly
+ * care if it's not there, so we need to deal with that.
+ */
+
+ error = git_repository_config_snapshot(&config, repo);
+ if (error < 0 && error != GIT_ENOTFOUND)
+ goto out;
+
+ if (config &&
+ (error = check_repositoryformatversion(&version, config)) < 0)
+ goto out;
+
+ if ((error = check_extensions(config, version)) < 0)
+ goto out;
+
+ if (version > 0) {
+ if ((error = load_objectformat(repo, config)) < 0)
+ goto out;
+ } else {
+ repo->oid_type = GIT_OID_DEFAULT;
+ }
+
+out:
+ *config_ptr = config;
+
+ return error;
+}
+
+int git_repository_open_bare(
+ git_repository **repo_ptr,
+ const char *bare_path)
+{
+ git_str path = GIT_STR_INIT, common_path = GIT_STR_INIT;
+ git_repository *repo = NULL;
+ bool is_valid;
+ int error;
+ git_config *config;
+
+ if ((error = git_fs_path_prettify_dir(&path, bare_path, NULL)) < 0 ||
+ (error = is_valid_repository_path(&is_valid, &path, &common_path, 0)) < 0)
+ return error;
+
+ if (!is_valid) {
+ git_str_dispose(&path);
+ git_str_dispose(&common_path);
+ git_error_set(GIT_ERROR_REPOSITORY, "path is not a repository: %s", bare_path);
+ return GIT_ENOTFOUND;
+ }
+
+ repo = repository_alloc();
+ GIT_ERROR_CHECK_ALLOC(repo);
+
+ repo->gitdir = git_str_detach(&path);
+ GIT_ERROR_CHECK_ALLOC(repo->gitdir);
+ repo->commondir = git_str_detach(&common_path);
+ GIT_ERROR_CHECK_ALLOC(repo->commondir);
+
+ /* of course we're bare! */
+ repo->is_bare = 1;
+ repo->is_worktree = 0;
+ repo->workdir = NULL;
+
+ if ((error = obtain_config_and_set_oid_type(&config, repo)) < 0)
+ goto cleanup;
+
+ *repo_ptr = repo;
+
+cleanup:
+ git_config_free(config);
+
+ return error;
+}
+
+static int repo_load_namespace(git_repository *repo)
+{
+ git_str namespace_buf = GIT_STR_INIT;
+ int error;
+
+ if (!repo->use_env)
+ return 0;
+
+ error = git__getenv(&namespace_buf, "GIT_NAMESPACE");
+
+ if (error == 0)
+ repo->namespace = git_str_detach(&namespace_buf);
+ else if (error != GIT_ENOTFOUND)
+ return error;
+
+ return 0;
+}
+
+static int repo_is_worktree(unsigned *out, const git_repository *repo)
+{
+ git_str gitdir_link = GIT_STR_INIT;
+ int error;
+
+ /* Worktrees cannot have the same commondir and gitdir */
+ if (repo->commondir && repo->gitdir
+ && !strcmp(repo->commondir, repo->gitdir)) {
+ *out = 0;
+ return 0;
+ }
+
+ if ((error = git_str_joinpath(&gitdir_link, repo->gitdir, "gitdir")) < 0)
+ return -1;
+
+ /* A 'gitdir' file inside a git directory is currently
+ * only used when the repository is a working tree. */
+ *out = !!git_fs_path_exists(gitdir_link.ptr);
+
+ git_str_dispose(&gitdir_link);
+ return error;
+}
+
+int git_repository_open_ext(
+ git_repository **repo_ptr,
+ const char *start_path,
+ unsigned int flags,
+ const char *ceiling_dirs)
+{
+ struct repo_paths paths = { GIT_STR_INIT };
+ git_repository *repo = NULL;
+ git_config *config = NULL;
+ unsigned is_worktree;
+ int error;
+
+ if (repo_ptr)
+ *repo_ptr = NULL;
+
+ error = find_repo(&paths, start_path, ceiling_dirs, flags);
+
+ if (error < 0 || !repo_ptr)
+ goto cleanup;
+
+ repo = repository_alloc();
+ GIT_ERROR_CHECK_ALLOC(repo);
+
+ repo->use_env = !!(flags & GIT_REPOSITORY_OPEN_FROM_ENV);
+
+ repo->gitdir = git_str_detach(&paths.gitdir);
+ GIT_ERROR_CHECK_ALLOC(repo->gitdir);
+
+ if (paths.gitlink.size) {
+ repo->gitlink = git_str_detach(&paths.gitlink);
+ GIT_ERROR_CHECK_ALLOC(repo->gitlink);
+ }
+ if (paths.commondir.size) {
+ repo->commondir = git_str_detach(&paths.commondir);
+ GIT_ERROR_CHECK_ALLOC(repo->commondir);
+ }
+
+ if ((error = repo_is_worktree(&is_worktree, repo)) < 0)
+ goto cleanup;
+
+ repo->is_worktree = is_worktree;
+
+ error = obtain_config_and_set_oid_type(&config, repo);
+ if (error < 0)
+ goto cleanup;
+
+ if ((error = load_grafts(repo)) < 0)
+ goto cleanup;
+
+ if ((flags & GIT_REPOSITORY_OPEN_BARE) != 0) {
+ repo->is_bare = 1;
+ } else {
+ if (config &&
+ ((error = load_config_data(repo, config)) < 0 ||
+ (error = load_workdir(repo, config, &paths.workdir)) < 0))
+ goto cleanup;
+ }
+
+ if ((error = repo_load_namespace(repo)) < 0)
+ goto cleanup;
+
+ /*
+ * Ensure that the git directory and worktree are
+ * owned by the current user.
+ */
+ if (git_repository__validate_ownership &&
+ (error = validate_ownership(repo)) < 0)
+ goto cleanup;
+
+cleanup:
+ repo_paths_dispose(&paths);
+ git_config_free(config);
+
+ if (error < 0)
+ git_repository_free(repo);
+ else if (repo_ptr)
+ *repo_ptr = repo;
+
+ return error;
+}
+
+int git_repository_open(git_repository **repo_out, const char *path)
+{
+ return git_repository_open_ext(
+ repo_out, path, GIT_REPOSITORY_OPEN_NO_SEARCH, NULL);
+}
+
+int git_repository_open_from_worktree(git_repository **repo_out, git_worktree *wt)
+{
+ git_str path = GIT_STR_INIT;
+ git_repository *repo = NULL;
+ size_t len;
+ int err;
+
+ GIT_ASSERT_ARG(repo_out);
+ GIT_ASSERT_ARG(wt);
+
+ *repo_out = NULL;
+ len = strlen(wt->gitlink_path);
+
+ if (len <= 4 || strcasecmp(wt->gitlink_path + len - 4, ".git")) {
+ err = -1;
+ goto out;
+ }
+
+ if ((err = git_str_set(&path, wt->gitlink_path, len - 4)) < 0)
+ goto out;
+
+ if ((err = git_repository_open(&repo, path.ptr)) < 0)
+ goto out;
+
+ *repo_out = repo;
+
+out:
+ git_str_dispose(&path);
+
+ return err;
+}
+
+int git_repository__wrap_odb(
+ git_repository **out,
+ git_odb *odb,
+ git_oid_t oid_type)
+{
+ git_repository *repo;
+
+ repo = repository_alloc();
+ GIT_ERROR_CHECK_ALLOC(repo);
+
+ repo->oid_type = oid_type;
+
+ git_repository_set_odb(repo, odb);
+ *out = repo;
+
+ return 0;
+}
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+int git_repository_wrap_odb(
+ git_repository **out,
+ git_odb *odb,
+ git_oid_t oid_type)
+{
+ return git_repository__wrap_odb(out, odb, oid_type);
+}
+#else
+int git_repository_wrap_odb(git_repository **out, git_odb *odb)
+{
+ return git_repository__wrap_odb(out, odb, GIT_OID_DEFAULT);
+}
+#endif
+
+int git_repository_discover(
+ git_buf *out,
+ const char *start_path,
+ int across_fs,
+ const char *ceiling_dirs)
+{
+ struct repo_paths paths = { GIT_STR_INIT };
+ uint32_t flags = across_fs ? GIT_REPOSITORY_OPEN_CROSS_FS : 0;
+ int error;
+
+ GIT_ASSERT_ARG(start_path);
+
+ if ((error = find_repo(&paths, start_path, ceiling_dirs, flags)) == 0)
+ error = git_buf_fromstr(out, &paths.gitdir);
+
+ repo_paths_dispose(&paths);
+ return error;
+}
+
+static int load_config(
+ git_config **out,
+ git_repository *repo,
+ const char *global_config_path,
+ const char *xdg_config_path,
+ const char *system_config_path,
+ const char *programdata_path)
+{
+ int error;
+ git_str config_path = GIT_STR_INIT;
+ git_config *cfg = NULL;
+
+ GIT_ASSERT_ARG(out);
+
+ if ((error = git_config_new(&cfg)) < 0)
+ return error;
+
+ if (repo) {
+ if ((error = git_repository__item_path(&config_path, repo, GIT_REPOSITORY_ITEM_CONFIG)) == 0)
+ error = git_config_add_file_ondisk(cfg, config_path.ptr, GIT_CONFIG_LEVEL_LOCAL, repo, 0);
+
+ if (error && error != GIT_ENOTFOUND)
+ goto on_error;
+
+ git_str_dispose(&config_path);
+ }
+
+ if (global_config_path != NULL &&
+ (error = git_config_add_file_ondisk(
+ cfg, global_config_path, GIT_CONFIG_LEVEL_GLOBAL, repo, 0)) < 0 &&
+ error != GIT_ENOTFOUND)
+ goto on_error;
+
+ if (xdg_config_path != NULL &&
+ (error = git_config_add_file_ondisk(
+ cfg, xdg_config_path, GIT_CONFIG_LEVEL_XDG, repo, 0)) < 0 &&
+ error != GIT_ENOTFOUND)
+ goto on_error;
+
+ if (system_config_path != NULL &&
+ (error = git_config_add_file_ondisk(
+ cfg, system_config_path, GIT_CONFIG_LEVEL_SYSTEM, repo, 0)) < 0 &&
+ error != GIT_ENOTFOUND)
+ goto on_error;
+
+ if (programdata_path != NULL &&
+ (error = git_config_add_file_ondisk(
+ cfg, programdata_path, GIT_CONFIG_LEVEL_PROGRAMDATA, repo, 0)) < 0 &&
+ error != GIT_ENOTFOUND)
+ goto on_error;
+
+ git_error_clear(); /* clear any lingering ENOTFOUND errors */
+
+ *out = cfg;
+ return 0;
+
+on_error:
+ git_str_dispose(&config_path);
+ git_config_free(cfg);
+ *out = NULL;
+ return error;
+}
+
+static const char *path_unless_empty(git_str *buf)
+{
+ return git_str_len(buf) > 0 ? git_str_cstr(buf) : NULL;
+}
+
+GIT_INLINE(int) config_path_system(git_str *out, bool use_env)
+{
+ if (use_env) {
+ git_str no_system_buf = GIT_STR_INIT;
+ int no_system = 0;
+ int error;
+
+ error = git__getenv(&no_system_buf, "GIT_CONFIG_NOSYSTEM");
+
+ if (error && error != GIT_ENOTFOUND)
+ return error;
+
+ error = git_config_parse_bool(&no_system, no_system_buf.ptr);
+ git_str_dispose(&no_system_buf);
+
+ if (no_system)
+ return 0;
+
+ error = git__getenv(out, "GIT_CONFIG_SYSTEM");
+
+ if (error == 0 || error != GIT_ENOTFOUND)
+ return 0;
+ }
+
+ git_config__find_system(out);
+ return 0;
+}
+
+GIT_INLINE(int) config_path_global(git_str *out, bool use_env)
+{
+ if (use_env) {
+ int error = git__getenv(out, "GIT_CONFIG_GLOBAL");
+
+ if (error == 0 || error != GIT_ENOTFOUND)
+ return 0;
+ }
+
+ git_config__find_global(out);
+ return 0;
+}
+
+int git_repository_config__weakptr(git_config **out, git_repository *repo)
+{
+ int error = 0;
+
+ if (repo->_config == NULL) {
+ git_str system_buf = GIT_STR_INIT;
+ git_str global_buf = GIT_STR_INIT;
+ git_str xdg_buf = GIT_STR_INIT;
+ git_str programdata_buf = GIT_STR_INIT;
+ bool use_env = repo->use_env;
+ git_config *config;
+
+ if (!(error = config_path_system(&system_buf, use_env)) &&
+ !(error = config_path_global(&global_buf, use_env))) {
+ git_config__find_xdg(&xdg_buf);
+ git_config__find_programdata(&programdata_buf);
+ }
+
+ if (!error) {
+ /*
+ * If there is no global file, open a backend
+ * for it anyway.
+ */
+ if (git_str_len(&global_buf) == 0)
+ git_config__global_location(&global_buf);
+
+ error = load_config(
+ &config, repo,
+ path_unless_empty(&global_buf),
+ path_unless_empty(&xdg_buf),
+ path_unless_empty(&system_buf),
+ path_unless_empty(&programdata_buf));
+ }
+
+ if (!error) {
+ GIT_REFCOUNT_OWN(config, repo);
+
+ if (git_atomic_compare_and_swap(&repo->_config, NULL, config) != NULL) {
+ GIT_REFCOUNT_OWN(config, NULL);
+ git_config_free(config);
+ }
+ }
+
+ git_str_dispose(&global_buf);
+ git_str_dispose(&xdg_buf);
+ git_str_dispose(&system_buf);
+ git_str_dispose(&programdata_buf);
+ }
+
+ *out = repo->_config;
+ return error;
+}
+
+int git_repository_config(git_config **out, git_repository *repo)
+{
+ if (git_repository_config__weakptr(out, repo) < 0)
+ return -1;
+
+ GIT_REFCOUNT_INC(*out);
+ return 0;
+}
+
+int git_repository_config_snapshot(git_config **out, git_repository *repo)
+{
+ int error;
+ git_config *weak;
+
+ if ((error = git_repository_config__weakptr(&weak, repo)) < 0)
+ return error;
+
+ return git_config_snapshot(out, weak);
+}
+
+int git_repository_set_config(git_repository *repo, git_config *config)
+{
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(config);
+
+ set_config(repo, config);
+ return 0;
+}
+
+static int repository_odb_path(git_str *out, git_repository *repo)
+{
+ int error = GIT_ENOTFOUND;
+
+ if (repo->use_env)
+ error = git__getenv(out, "GIT_OBJECT_DIRECTORY");
+
+ if (error == GIT_ENOTFOUND)
+ error = git_repository__item_path(out, repo,
+ GIT_REPOSITORY_ITEM_OBJECTS);
+
+ return error;
+}
+
+static int repository_odb_alternates(
+ git_odb *odb,
+ git_repository *repo)
+{
+ git_str alternates = GIT_STR_INIT;
+ char *sep, *alt;
+ int error;
+
+ if (!repo->use_env)
+ return 0;
+
+ error = git__getenv(&alternates, "GIT_ALTERNATE_OBJECT_DIRECTORIES");
+
+ if (error != 0)
+ return (error == GIT_ENOTFOUND) ? 0 : error;
+
+ alt = alternates.ptr;
+
+ while (*alt) {
+ sep = strchr(alt, GIT_PATH_LIST_SEPARATOR);
+
+ if (sep)
+ *sep = '\0';
+
+ error = git_odb_add_disk_alternate(odb, alt);
+
+ if (sep)
+ alt = sep + 1;
+ else
+ break;
+ }
+
+ git_str_dispose(&alternates);
+ return 0;
+}
+
+int git_repository_odb__weakptr(git_odb **out, git_repository *repo)
+{
+ int error = 0;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(out);
+
+ *out = git_atomic_load(repo->_odb);
+ if (*out == NULL) {
+ git_str odb_path = GIT_STR_INIT;
+ git_odb_options odb_opts = GIT_ODB_OPTIONS_INIT;
+ git_odb *odb;
+
+ odb_opts.oid_type = repo->oid_type;
+
+ if ((error = repository_odb_path(&odb_path, repo)) < 0 ||
+ (error = git_odb__new(&odb, &odb_opts)) < 0 ||
+ (error = repository_odb_alternates(odb, repo)) < 0)
+ return error;
+
+ GIT_REFCOUNT_OWN(odb, repo);
+
+ if ((error = git_odb__set_caps(odb, GIT_ODB_CAP_FROM_OWNER)) < 0 ||
+ (error = git_odb__add_default_backends(odb, odb_path.ptr, 0, 0)) < 0) {
+ git_odb_free(odb);
+ return error;
+ }
+
+ if (git_atomic_compare_and_swap(&repo->_odb, NULL, odb) != NULL) {
+ GIT_REFCOUNT_OWN(odb, NULL);
+ git_odb_free(odb);
+ }
+
+ git_str_dispose(&odb_path);
+ *out = git_atomic_load(repo->_odb);
+ }
+
+ return error;
+}
+
+int git_repository_odb(git_odb **out, git_repository *repo)
+{
+ if (git_repository_odb__weakptr(out, repo) < 0)
+ return -1;
+
+ GIT_REFCOUNT_INC(*out);
+ return 0;
+}
+
+int git_repository_set_odb(git_repository *repo, git_odb *odb)
+{
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(odb);
+
+ set_odb(repo, odb);
+ return 0;
+}
+
+int git_repository_refdb__weakptr(git_refdb **out, git_repository *repo)
+{
+ int error = 0;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+
+ if (repo->_refdb == NULL) {
+ git_refdb *refdb;
+
+ error = git_refdb_open(&refdb, repo);
+ if (!error) {
+ GIT_REFCOUNT_OWN(refdb, repo);
+
+ if (git_atomic_compare_and_swap(&repo->_refdb, NULL, refdb) != NULL) {
+ GIT_REFCOUNT_OWN(refdb, NULL);
+ git_refdb_free(refdb);
+ }
+ }
+ }
+
+ *out = repo->_refdb;
+ return error;
+}
+
+int git_repository_refdb(git_refdb **out, git_repository *repo)
+{
+ if (git_repository_refdb__weakptr(out, repo) < 0)
+ return -1;
+
+ GIT_REFCOUNT_INC(*out);
+ return 0;
+}
+
+int git_repository_set_refdb(git_repository *repo, git_refdb *refdb)
+{
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(refdb);
+
+ set_refdb(repo, refdb);
+ return 0;
+}
+
+static int repository_index_path(git_str *out, git_repository *repo)
+{
+ int error = GIT_ENOTFOUND;
+
+ if (repo->use_env)
+ error = git__getenv(out, "GIT_INDEX_FILE");
+
+ if (error == GIT_ENOTFOUND)
+ error = git_repository__item_path(out, repo,
+ GIT_REPOSITORY_ITEM_INDEX);
+
+ return error;
+}
+
+int git_repository_index__weakptr(git_index **out, git_repository *repo)
+{
+ int error = 0;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+
+ if (repo->_index == NULL) {
+ git_str index_path = GIT_STR_INIT;
+ git_index *index;
+
+ if ((error = repository_index_path(&index_path, repo)) < 0)
+ return error;
+
+ error = git_index__open(&index, index_path.ptr, repo->oid_type);
+
+ if (!error) {
+ GIT_REFCOUNT_OWN(index, repo);
+
+ if (git_atomic_compare_and_swap(&repo->_index, NULL, index) != NULL) {
+ GIT_REFCOUNT_OWN(index, NULL);
+ git_index_free(index);
+ }
+
+ error = git_index_set_caps(repo->_index,
+ GIT_INDEX_CAPABILITY_FROM_OWNER);
+ }
+
+ git_str_dispose(&index_path);
+ }
+
+ *out = repo->_index;
+ return error;
+}
+
+int git_repository_index(git_index **out, git_repository *repo)
+{
+ if (git_repository_index__weakptr(out, repo) < 0)
+ return -1;
+
+ GIT_REFCOUNT_INC(*out);
+ return 0;
+}
+
+int git_repository_set_index(git_repository *repo, git_index *index)
+{
+ GIT_ASSERT_ARG(repo);
+ set_index(repo, index);
+ return 0;
+}
+
+int git_repository_grafts__weakptr(git_grafts **out, git_repository *repo)
+{
+ GIT_ASSERT_ARG(out && repo);
+ GIT_ASSERT(repo->grafts);
+ *out = repo->grafts;
+ return 0;
+}
+
+int git_repository_shallow_grafts__weakptr(git_grafts **out, git_repository *repo)
+{
+ GIT_ASSERT_ARG(out && repo);
+ GIT_ASSERT(repo->shallow_grafts);
+ *out = repo->shallow_grafts;
+ return 0;
+}
+
+int git_repository_set_namespace(git_repository *repo, const char *namespace)
+{
+ git__free(repo->namespace);
+
+ if (namespace == NULL) {
+ repo->namespace = NULL;
+ return 0;
+ }
+
+ return (repo->namespace = git__strdup(namespace)) ? 0 : -1;
+}
+
+const char *git_repository_get_namespace(git_repository *repo)
+{
+ return repo->namespace;
+}
+
+#ifdef GIT_WIN32
+static int reserved_names_add8dot3(git_repository *repo, const char *path)
+{
+ char *name = git_win32_path_8dot3_name(path);
+ const char *def = GIT_DIR_SHORTNAME;
+ const char *def_dot_git = DOT_GIT;
+ size_t name_len, def_len = CONST_STRLEN(GIT_DIR_SHORTNAME);
+ size_t def_dot_git_len = CONST_STRLEN(DOT_GIT);
+ git_str *buf;
+
+ if (!name)
+ return 0;
+
+ name_len = strlen(name);
+
+ if ((name_len == def_len && memcmp(name, def, def_len) == 0) ||
+ (name_len == def_dot_git_len && memcmp(name, def_dot_git, def_dot_git_len) == 0)) {
+ git__free(name);
+ return 0;
+ }
+
+ if ((buf = git_array_alloc(repo->reserved_names)) == NULL)
+ return -1;
+
+ git_str_attach(buf, name, name_len);
+ return true;
+}
+
+bool git_repository__reserved_names(
+ git_str **out, size_t *outlen, git_repository *repo, bool include_ntfs)
+{
+ GIT_UNUSED(include_ntfs);
+
+ if (repo->reserved_names.size == 0) {
+ git_str *buf;
+ size_t i;
+
+ /* Add the static defaults */
+ for (i = 0; i < git_repository__reserved_names_win32_len; i++) {
+ if ((buf = git_array_alloc(repo->reserved_names)) == NULL)
+ goto on_error;
+
+ buf->ptr = git_repository__reserved_names_win32[i].ptr;
+ buf->size = git_repository__reserved_names_win32[i].size;
+ }
+
+ /* Try to add any repo-specific reserved names - the gitlink file
+ * within a submodule or the repository (if the repository directory
+ * is beneath the workdir). These are typically `.git`, but should
+ * be protected in case they are not. Note, repo and workdir paths
+ * are always prettified to end in `/`, so a prefixcmp is safe.
+ */
+ if (!repo->is_bare) {
+ int (*prefixcmp)(const char *, const char *);
+ int error, ignorecase;
+
+ error = git_repository__configmap_lookup(
+ &ignorecase, repo, GIT_CONFIGMAP_IGNORECASE);
+ prefixcmp = (error || ignorecase) ? git__prefixcmp_icase :
+ git__prefixcmp;
+
+ if (repo->gitlink &&
+ reserved_names_add8dot3(repo, repo->gitlink) < 0)
+ goto on_error;
+
+ if (repo->gitdir &&
+ prefixcmp(repo->gitdir, repo->workdir) == 0 &&
+ reserved_names_add8dot3(repo, repo->gitdir) < 0)
+ goto on_error;
+ }
+ }
+
+ *out = repo->reserved_names.ptr;
+ *outlen = repo->reserved_names.size;
+
+ return true;
+
+ /* Always give good defaults, even on OOM */
+on_error:
+ *out = git_repository__reserved_names_win32;
+ *outlen = git_repository__reserved_names_win32_len;
+
+ return false;
+}
+#else
+bool git_repository__reserved_names(
+ git_str **out, size_t *outlen, git_repository *repo, bool include_ntfs)
+{
+ GIT_UNUSED(repo);
+
+ if (include_ntfs) {
+ *out = git_repository__reserved_names_win32;
+ *outlen = git_repository__reserved_names_win32_len;
+ } else {
+ *out = git_repository__reserved_names_posix;
+ *outlen = git_repository__reserved_names_posix_len;
+ }
+
+ return true;
+}
+#endif
+
+static int check_repositoryformatversion(int *version, git_config *config)
+{
+ int error;
+
+ error = git_config_get_int32(version, config, "core.repositoryformatversion");
+
+ /* git ignores this if the config variable isn't there */
+ if (error == GIT_ENOTFOUND)
+ return 0;
+
+ if (error < 0)
+ return -1;
+
+ if (*version < 0) {
+ git_error_set(GIT_ERROR_REPOSITORY,
+ "invalid repository version %d", *version);
+ }
+
+ if (GIT_REPO_VERSION_MAX < *version) {
+ git_error_set(GIT_ERROR_REPOSITORY,
+ "unsupported repository version %d; only versions up to %d are supported",
+ *version, GIT_REPO_VERSION_MAX);
+ return -1;
+ }
+
+ return 0;
+}
+
+static const char *builtin_extensions[] = {
+ "noop",
+ "objectformat"
+};
+
+static git_vector user_extensions = { 0, git__strcmp_cb };
+
+static int check_valid_extension(const git_config_entry *entry, void *payload)
+{
+ git_str cfg = GIT_STR_INIT;
+ bool reject;
+ const char *extension;
+ size_t i;
+ int error = 0;
+
+ GIT_UNUSED(payload);
+
+ git_vector_foreach (&user_extensions, i, extension) {
+ git_str_clear(&cfg);
+
+ /*
+ * Users can specify that they don't want to support an
+ * extension with a '!' prefix.
+ */
+ if ((reject = (extension[0] == '!')) == true)
+ extension = &extension[1];
+
+ if ((error = git_str_printf(&cfg, "extensions.%s", extension)) < 0)
+ goto done;
+
+ if (strcmp(entry->name, cfg.ptr) == 0) {
+ if (reject)
+ goto fail;
+
+ goto done;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(builtin_extensions); i++) {
+ git_str_clear(&cfg);
+ extension = builtin_extensions[i];
+
+ if ((error = git_str_printf(&cfg, "extensions.%s", extension)) < 0)
+ goto done;
+
+ if (strcmp(entry->name, cfg.ptr) == 0)
+ goto done;
+ }
+
+fail:
+ git_error_set(GIT_ERROR_REPOSITORY, "unsupported extension name %s", entry->name);
+ error = -1;
+
+done:
+ git_str_dispose(&cfg);
+ return error;
+}
+
+static int check_extensions(git_config *config, int version)
+{
+ if (version < 1)
+ return 0;
+
+ return git_config_foreach_match(config, "^extensions\\.", check_valid_extension, NULL);
+}
+
+static int load_objectformat(git_repository *repo, git_config *config)
+{
+ git_config_entry *entry = NULL;
+ int error;
+
+ if ((error = git_config_get_entry(&entry, config, "extensions.objectformat")) < 0) {
+ if (error == GIT_ENOTFOUND) {
+ repo->oid_type = GIT_OID_DEFAULT;
+ git_error_clear();
+ error = 0;
+ }
+
+ goto done;
+ }
+
+ if ((repo->oid_type = git_oid_type_fromstr(entry->value)) == 0) {
+ git_error_set(GIT_ERROR_REPOSITORY,
+ "unknown object format '%s'", entry->value);
+ error = GIT_EINVALID;
+ }
+
+done:
+ git_config_entry_free(entry);
+ return error;
+}
+
+int git_repository__set_objectformat(
+ git_repository *repo,
+ git_oid_t oid_type)
+{
+ git_config *cfg;
+
+ /*
+ * Older clients do not necessarily understand the
+ * `objectformat` extension, even when it's set to an
+ * object format that they understand (SHA1). Do not set
+ * the objectformat extension unless we're not using the
+ * default object format.
+ */
+ if (oid_type == GIT_OID_DEFAULT)
+ return 0;
+
+ if (!git_repository_is_empty(repo) && repo->oid_type != oid_type) {
+ git_error_set(GIT_ERROR_REPOSITORY,
+ "cannot change object id type of existing repository");
+ return -1;
+ }
+
+ if (git_repository_config__weakptr(&cfg, repo) < 0)
+ return -1;
+
+ if (git_config_set_int32(cfg,
+ "core.repositoryformatversion", 1) < 0 ||
+ git_config_set_string(cfg, "extensions.objectformat",
+ git_oid_type_name(oid_type)) < 0)
+ return -1;
+
+ /*
+ * During repo init, we may create some backends with the
+ * default oid type. Clear them so that we create them with
+ * the proper oid type.
+ */
+ if (repo->oid_type != oid_type) {
+ set_index(repo, NULL);
+ set_odb(repo, NULL);
+ set_refdb(repo, NULL);
+
+ repo->oid_type = oid_type;
+ }
+
+ return 0;
+}
+
+int git_repository__extensions(char ***out, size_t *out_len)
+{
+ git_vector extensions;
+ const char *builtin, *user;
+ char *extension;
+ size_t i, j;
+
+ if (git_vector_init(&extensions, 8, git__strcmp_cb) < 0)
+ return -1;
+
+ for (i = 0; i < ARRAY_SIZE(builtin_extensions); i++) {
+ bool match = false;
+
+ builtin = builtin_extensions[i];
+
+ git_vector_foreach (&user_extensions, j, user) {
+ if (user[0] == '!' && strcmp(builtin, &user[1]) == 0) {
+ match = true;
+ break;
+ }
+ }
+
+ if (match)
+ continue;
+
+ if ((extension = git__strdup(builtin)) == NULL ||
+ git_vector_insert(&extensions, extension) < 0)
+ return -1;
+ }
+
+ git_vector_foreach (&user_extensions, i, user) {
+ if (user[0] == '!')
+ continue;
+
+ if ((extension = git__strdup(user)) == NULL ||
+ git_vector_insert(&extensions, extension) < 0)
+ return -1;
+ }
+
+ git_vector_sort(&extensions);
+
+ *out = (char **)git_vector_detach(out_len, NULL, &extensions);
+ return 0;
+}
+
+static int dup_ext_err(void **old, void *extension)
+{
+ GIT_UNUSED(old);
+ GIT_UNUSED(extension);
+ return GIT_EEXISTS;
+}
+
+int git_repository__set_extensions(const char **extensions, size_t len)
+{
+ char *extension;
+ size_t i, j;
+ int error;
+
+ git_repository__free_extensions();
+
+ for (i = 0; i < len; i++) {
+ bool is_builtin = false;
+
+ for (j = 0; j < ARRAY_SIZE(builtin_extensions); j++) {
+ if (strcmp(builtin_extensions[j], extensions[i]) == 0) {
+ is_builtin = true;
+ break;
+ }
+ }
+
+ if (is_builtin)
+ continue;
+
+ if ((extension = git__strdup(extensions[i])) == NULL)
+ return -1;
+
+ if ((error = git_vector_insert_sorted(&user_extensions, extension, dup_ext_err)) < 0) {
+ git__free(extension);
+
+ if (error != GIT_EEXISTS)
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+void git_repository__free_extensions(void)
+{
+ git_vector_free_deep(&user_extensions);
+}
+
+int git_repository_create_head(const char *git_dir, const char *ref_name)
+{
+ git_str ref_path = GIT_STR_INIT;
+ git_filebuf ref = GIT_FILEBUF_INIT;
+ const char *fmt;
+ int error;
+
+ if ((error = git_str_joinpath(&ref_path, git_dir, GIT_HEAD_FILE)) < 0 ||
+ (error = git_filebuf_open(&ref, ref_path.ptr, 0, GIT_REFS_FILE_MODE)) < 0)
+ goto out;
+
+ if (git__prefixcmp(ref_name, GIT_REFS_DIR) == 0)
+ fmt = "ref: %s\n";
+ else
+ fmt = "ref: " GIT_REFS_HEADS_DIR "%s\n";
+
+ if ((error = git_filebuf_printf(&ref, fmt, ref_name)) < 0 ||
+ (error = git_filebuf_commit(&ref)) < 0)
+ goto out;
+
+out:
+ git_str_dispose(&ref_path);
+ git_filebuf_cleanup(&ref);
+ return error;
+}
+
+static bool is_chmod_supported(const char *file_path)
+{
+ struct stat st1, st2;
+
+ if (p_stat(file_path, &st1) < 0)
+ return false;
+
+ if (p_chmod(file_path, st1.st_mode ^ S_IXUSR) < 0)
+ return false;
+
+ if (p_stat(file_path, &st2) < 0)
+ return false;
+
+ return (st1.st_mode != st2.st_mode);
+}
+
+static bool is_filesystem_case_insensitive(const char *gitdir_path)
+{
+ git_str path = GIT_STR_INIT;
+ int is_insensitive = -1;
+
+ if (!git_str_joinpath(&path, gitdir_path, "CoNfIg"))
+ is_insensitive = git_fs_path_exists(git_str_cstr(&path));
+
+ git_str_dispose(&path);
+ return is_insensitive;
+}
+
+/*
+ * Return a configuration object with only the global and system
+ * configurations; no repository-level configuration.
+ */
+static int load_global_config(git_config **config, bool use_env)
+{
+ git_str global_buf = GIT_STR_INIT;
+ git_str xdg_buf = GIT_STR_INIT;
+ git_str system_buf = GIT_STR_INIT;
+ git_str programdata_buf = GIT_STR_INIT;
+ int error;
+
+ if (!(error = config_path_system(&system_buf, use_env)) &&
+ !(error = config_path_global(&global_buf, use_env))) {
+ git_config__find_xdg(&xdg_buf);
+ git_config__find_programdata(&programdata_buf);
+
+ error = load_config(config, NULL,
+ path_unless_empty(&global_buf),
+ path_unless_empty(&xdg_buf),
+ path_unless_empty(&system_buf),
+ path_unless_empty(&programdata_buf));
+ }
+
+ git_str_dispose(&global_buf);
+ git_str_dispose(&xdg_buf);
+ git_str_dispose(&system_buf);
+ git_str_dispose(&programdata_buf);
+
+ return error;
+}
+
+static bool are_symlinks_supported(const char *wd_path, bool use_env)
+{
+ git_config *config = NULL;
+ int symlinks = 0;
+
+ /*
+ * To emulate Git for Windows, symlinks on Windows must be explicitly
+ * opted-in. We examine the system configuration for a core.symlinks
+ * set to true. If found, we then examine the filesystem to see if
+ * symlinks are _actually_ supported by the current user. If that is
+ * _not_ set, then we do not test or enable symlink support.
+ */
+#ifdef GIT_WIN32
+ if (load_global_config(&config, use_env) < 0 ||
+ git_config_get_bool(&symlinks, config, "core.symlinks") < 0 ||
+ !symlinks)
+ goto done;
+#else
+ GIT_UNUSED(use_env);
+#endif
+
+ if (!(symlinks = git_fs_path_supports_symlinks(wd_path)))
+ goto done;
+
+done:
+ git_config_free(config);
+ return symlinks != 0;
+}
+
+static int create_empty_file(const char *path, mode_t mode)
+{
+ int fd;
+
+ if ((fd = p_creat(path, mode)) < 0) {
+ git_error_set(GIT_ERROR_OS, "error while creating '%s'", path);
+ return -1;
+ }
+
+ if (p_close(fd) < 0) {
+ git_error_set(GIT_ERROR_OS, "error while closing '%s'", path);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int repo_local_config(
+ git_config **out,
+ git_str *config_dir,
+ git_repository *repo,
+ const char *repo_dir)
+{
+ int error = 0;
+ git_config *parent;
+ const char *cfg_path;
+
+ if (git_str_joinpath(config_dir, repo_dir, GIT_CONFIG_FILENAME_INREPO) < 0)
+ return -1;
+ cfg_path = git_str_cstr(config_dir);
+
+ /* make LOCAL config if missing */
+ if (!git_fs_path_isfile(cfg_path) &&
+ (error = create_empty_file(cfg_path, GIT_CONFIG_FILE_MODE)) < 0)
+ return error;
+
+ /* if no repo, just open that file directly */
+ if (!repo)
+ return git_config_open_ondisk(out, cfg_path);
+
+ /* otherwise, open parent config and get that level */
+ if ((error = git_repository_config__weakptr(&parent, repo)) < 0)
+ return error;
+
+ if (git_config_open_level(out, parent, GIT_CONFIG_LEVEL_LOCAL) < 0) {
+ git_error_clear();
+
+ if (!(error = git_config_add_file_ondisk(
+ parent, cfg_path, GIT_CONFIG_LEVEL_LOCAL, repo, false)))
+ error = git_config_open_level(out, parent, GIT_CONFIG_LEVEL_LOCAL);
+ }
+
+ git_config_free(parent);
+
+ return error;
+}
+
+static int repo_init_fs_configs(
+ git_config *cfg,
+ const char *cfg_path,
+ const char *repo_dir,
+ const char *work_dir,
+ bool update_ignorecase,
+ bool use_env)
+{
+ int error = 0;
+
+ if (!work_dir)
+ work_dir = repo_dir;
+
+ if ((error = git_config_set_bool(
+ cfg, "core.filemode", is_chmod_supported(cfg_path))) < 0)
+ return error;
+
+ if (!are_symlinks_supported(work_dir, use_env)) {
+ if ((error = git_config_set_bool(cfg, "core.symlinks", false)) < 0)
+ return error;
+ } else if (git_config_delete_entry(cfg, "core.symlinks") < 0)
+ git_error_clear();
+
+ if (update_ignorecase) {
+ if (is_filesystem_case_insensitive(repo_dir)) {
+ if ((error = git_config_set_bool(cfg, "core.ignorecase", true)) < 0)
+ return error;
+ } else if (git_config_delete_entry(cfg, "core.ignorecase") < 0)
+ git_error_clear();
+ }
+
+#ifdef GIT_USE_ICONV
+ if ((error = git_config_set_bool(
+ cfg, "core.precomposeunicode",
+ git_fs_path_does_decompose_unicode(work_dir))) < 0)
+ return error;
+ /* on non-iconv platforms, don't even set core.precomposeunicode */
+#endif
+
+ return 0;
+}
+
+static int repo_init_config(
+ const char *repo_dir,
+ const char *work_dir,
+ uint32_t flags,
+ uint32_t mode,
+ git_oid_t oid_type)
+{
+ int error = 0;
+ git_str cfg_path = GIT_STR_INIT, worktree_path = GIT_STR_INIT;
+ git_config *config = NULL;
+ bool is_bare = ((flags & GIT_REPOSITORY_INIT_BARE) != 0);
+ bool is_reinit = ((flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0);
+ bool use_env = ((flags & GIT_REPOSITORY_OPEN_FROM_ENV) != 0);
+ int version = GIT_REPO_VERSION_DEFAULT;
+
+ if ((error = repo_local_config(&config, &cfg_path, NULL, repo_dir)) < 0)
+ goto cleanup;
+
+ if (is_reinit &&
+ (error = check_repositoryformatversion(&version, config)) < 0)
+ goto cleanup;
+
+ if ((error = check_extensions(config, version)) < 0)
+ goto cleanup;
+
+#define SET_REPO_CONFIG(TYPE, NAME, VAL) do { \
+ if ((error = git_config_set_##TYPE(config, NAME, VAL)) < 0) \
+ goto cleanup; } while (0)
+
+ SET_REPO_CONFIG(bool, "core.bare", is_bare);
+ SET_REPO_CONFIG(int32, "core.repositoryformatversion", version);
+
+ if ((error = repo_init_fs_configs(
+ config, cfg_path.ptr, repo_dir, work_dir,
+ !is_reinit, use_env)) < 0)
+ goto cleanup;
+
+ if (!is_bare) {
+ SET_REPO_CONFIG(bool, "core.logallrefupdates", true);
+
+ if (!(flags & GIT_REPOSITORY_INIT__NATURAL_WD)) {
+ if ((error = git_str_sets(&worktree_path, work_dir)) < 0)
+ goto cleanup;
+
+ if ((flags & GIT_REPOSITORY_INIT_RELATIVE_GITLINK))
+ if ((error = git_fs_path_make_relative(&worktree_path, repo_dir)) < 0)
+ goto cleanup;
+
+ SET_REPO_CONFIG(string, "core.worktree", worktree_path.ptr);
+ } else if (is_reinit) {
+ if (git_config_delete_entry(config, "core.worktree") < 0)
+ git_error_clear();
+ }
+ }
+
+ if (mode == GIT_REPOSITORY_INIT_SHARED_GROUP) {
+ SET_REPO_CONFIG(int32, "core.sharedrepository", 1);
+ SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true);
+ }
+ else if (mode == GIT_REPOSITORY_INIT_SHARED_ALL) {
+ SET_REPO_CONFIG(int32, "core.sharedrepository", 2);
+ SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true);
+ }
+
+ if (oid_type != GIT_OID_DEFAULT) {
+ SET_REPO_CONFIG(int32, "core.repositoryformatversion", 1);
+ SET_REPO_CONFIG(string, "extensions.objectformat", git_oid_type_name(oid_type));
+ }
+
+cleanup:
+ git_str_dispose(&cfg_path);
+ git_str_dispose(&worktree_path);
+ git_config_free(config);
+
+ return error;
+}
+
+static int repo_reinit_submodule_fs(git_submodule *sm, const char *n, void *p)
+{
+ git_repository *smrepo = NULL;
+ GIT_UNUSED(n); GIT_UNUSED(p);
+
+ if (git_submodule_open(&smrepo, sm) < 0 ||
+ git_repository_reinit_filesystem(smrepo, true) < 0)
+ git_error_clear();
+ git_repository_free(smrepo);
+
+ return 0;
+}
+
+int git_repository_reinit_filesystem(git_repository *repo, int recurse)
+{
+ int error = 0;
+ git_str path = GIT_STR_INIT;
+ git_config *config = NULL;
+ const char *repo_dir = git_repository_path(repo);
+
+ if (!(error = repo_local_config(&config, &path, repo, repo_dir)))
+ error = repo_init_fs_configs(config, path.ptr, repo_dir,
+ git_repository_workdir(repo), true, repo->use_env);
+
+ git_config_free(config);
+ git_str_dispose(&path);
+
+ git_repository__configmap_lookup_cache_clear(repo);
+
+ if (!repo->is_bare && recurse)
+ (void)git_submodule_foreach(repo, repo_reinit_submodule_fs, NULL);
+
+ return error;
+}
+
+static int repo_write_template(
+ const char *git_dir,
+ bool allow_overwrite,
+ const char *file,
+ mode_t mode,
+ bool hidden,
+ const char *content)
+{
+ git_str path = GIT_STR_INIT;
+ int fd, error = 0, flags;
+
+ if (git_str_joinpath(&path, git_dir, file) < 0)
+ return -1;
+
+ if (allow_overwrite)
+ flags = O_WRONLY | O_CREAT | O_TRUNC;
+ else
+ flags = O_WRONLY | O_CREAT | O_EXCL;
+
+ fd = p_open(git_str_cstr(&path), flags, mode);
+
+ if (fd >= 0) {
+ error = p_write(fd, content, strlen(content));
+
+ p_close(fd);
+ }
+ else if (errno != EEXIST)
+ error = fd;
+
+#ifdef GIT_WIN32
+ if (!error && hidden) {
+ if (git_win32__set_hidden(path.ptr, true) < 0)
+ error = -1;
+ }
+#else
+ GIT_UNUSED(hidden);
+#endif
+
+ git_str_dispose(&path);
+
+ if (error)
+ git_error_set(GIT_ERROR_OS,
+ "failed to initialize repository with template '%s'", file);
+
+ return error;
+}
+
+static int repo_write_gitlink(
+ const char *in_dir, const char *to_repo, bool use_relative_path)
+{
+ int error;
+ git_str buf = GIT_STR_INIT;
+ git_str path_to_repo = GIT_STR_INIT;
+ struct stat st;
+
+ git_fs_path_dirname_r(&buf, to_repo);
+ git_fs_path_to_dir(&buf);
+ if (git_str_oom(&buf))
+ return -1;
+
+ /* don't write gitlink to natural workdir */
+ if (git__suffixcmp(to_repo, "/" DOT_GIT "/") == 0 &&
+ strcmp(in_dir, buf.ptr) == 0)
+ {
+ error = GIT_PASSTHROUGH;
+ goto cleanup;
+ }
+
+ if ((error = git_str_joinpath(&buf, in_dir, DOT_GIT)) < 0)
+ goto cleanup;
+
+ if (!p_stat(buf.ptr, &st) && !S_ISREG(st.st_mode)) {
+ git_error_set(GIT_ERROR_REPOSITORY,
+ "cannot overwrite gitlink file into path '%s'", in_dir);
+ error = GIT_EEXISTS;
+ goto cleanup;
+ }
+
+ git_str_clear(&buf);
+
+ error = git_str_sets(&path_to_repo, to_repo);
+
+ if (!error && use_relative_path)
+ error = git_fs_path_make_relative(&path_to_repo, in_dir);
+
+ if (!error)
+ error = git_str_join(&buf, ' ', GIT_FILE_CONTENT_PREFIX, path_to_repo.ptr);
+
+ if (!error)
+ error = repo_write_template(in_dir, true, DOT_GIT, 0666, true, buf.ptr);
+
+cleanup:
+ git_str_dispose(&buf);
+ git_str_dispose(&path_to_repo);
+ return error;
+}
+
+static mode_t pick_dir_mode(git_repository_init_options *opts)
+{
+ if (opts->mode == GIT_REPOSITORY_INIT_SHARED_UMASK)
+ return 0777;
+ if (opts->mode == GIT_REPOSITORY_INIT_SHARED_GROUP)
+ return (0775 | S_ISGID);
+ if (opts->mode == GIT_REPOSITORY_INIT_SHARED_ALL)
+ return (0777 | S_ISGID);
+ return opts->mode;
+}
+
+#include "repo_template.h"
+
+static int repo_init_structure(
+ const char *repo_dir,
+ const char *work_dir,
+ git_repository_init_options *opts)
+{
+ int error = 0;
+ repo_template_item *tpl;
+ bool external_tpl =
+ ((opts->flags & GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE) != 0);
+ mode_t dmode = pick_dir_mode(opts);
+ bool chmod = opts->mode != GIT_REPOSITORY_INIT_SHARED_UMASK;
+
+ /* Hide the ".git" directory */
+#ifdef GIT_WIN32
+ if ((opts->flags & GIT_REPOSITORY_INIT__HAS_DOTGIT) != 0) {
+ if (git_win32__set_hidden(repo_dir, true) < 0) {
+ git_error_set(GIT_ERROR_OS,
+ "failed to mark Git repository folder as hidden");
+ return -1;
+ }
+ }
+#endif
+
+ /* Create the .git gitlink if appropriate */
+ if ((opts->flags & GIT_REPOSITORY_INIT_BARE) == 0 &&
+ (opts->flags & GIT_REPOSITORY_INIT__NATURAL_WD) == 0)
+ {
+ if (repo_write_gitlink(work_dir, repo_dir, opts->flags & GIT_REPOSITORY_INIT_RELATIVE_GITLINK) < 0)
+ return -1;
+ }
+
+ /* Copy external template if requested */
+ if (external_tpl) {
+ git_config *cfg = NULL;
+ const char *tdir = NULL;
+ bool default_template = false;
+ git_str template_buf = GIT_STR_INIT;
+
+ if (opts->template_path)
+ tdir = opts->template_path;
+ else if ((error = git_config_open_default(&cfg)) >= 0) {
+ if (!git_config__get_path(&template_buf, cfg, "init.templatedir"))
+ tdir = template_buf.ptr;
+ git_error_clear();
+ }
+
+ if (!tdir) {
+ if (!(error = git_sysdir_find_template_dir(&template_buf)))
+ tdir = template_buf.ptr;
+ default_template = true;
+ }
+
+ /*
+ * If tdir was the empty string, treat it like tdir was a path to an
+ * empty directory (so, don't do any copying). This is the behavior
+ * that git(1) exhibits, although it doesn't seem to be officially
+ * documented.
+ */
+ if (tdir && git__strcmp(tdir, "") != 0) {
+ uint32_t cpflags = GIT_CPDIR_COPY_SYMLINKS |
+ GIT_CPDIR_SIMPLE_TO_MODE |
+ GIT_CPDIR_COPY_DOTFILES;
+ if (opts->mode != GIT_REPOSITORY_INIT_SHARED_UMASK)
+ cpflags |= GIT_CPDIR_CHMOD_DIRS;
+ error = git_futils_cp_r(tdir, repo_dir, cpflags, dmode);
+ }
+
+ git_str_dispose(&template_buf);
+ git_config_free(cfg);
+
+ /* If tdir does not exist, then do not error out. This matches the
+ * behaviour of git(1), which just prints a warning and continues.
+ * TODO: issue warning when warning API is available.
+ * `git` prints to stderr: 'warning: templates not found in /path/to/tdir'
+ */
+ if (error < 0) {
+ if (!default_template && error != GIT_ENOTFOUND)
+ return error;
+
+ /* if template was default, ignore error and use internal */
+ git_error_clear();
+ external_tpl = false;
+ error = 0;
+ }
+ }
+
+ /* Copy internal template
+ * - always ensure existence of dirs
+ * - only create files if no external template was specified
+ */
+ for (tpl = repo_template; !error && tpl->path; ++tpl) {
+ if (!tpl->content) {
+ uint32_t mkdir_flags = GIT_MKDIR_PATH;
+ if (chmod)
+ mkdir_flags |= GIT_MKDIR_CHMOD;
+
+ error = git_futils_mkdir_relative(
+ tpl->path, repo_dir, dmode, mkdir_flags, NULL);
+ }
+ else if (!external_tpl) {
+ const char *content = tpl->content;
+
+ if (opts->description && strcmp(tpl->path, GIT_DESC_FILE) == 0)
+ content = opts->description;
+
+ error = repo_write_template(
+ repo_dir, false, tpl->path, tpl->mode, false, content);
+ }
+ }
+
+ return error;
+}
+
+static int mkdir_parent(git_str *buf, uint32_t mode, bool skip2)
+{
+ /* When making parent directories during repository initialization
+ * don't try to set gid or grant world write access
+ */
+ return git_futils_mkdir(
+ buf->ptr, mode & ~(S_ISGID | 0002),
+ GIT_MKDIR_PATH | GIT_MKDIR_VERIFY_DIR |
+ (skip2 ? GIT_MKDIR_SKIP_LAST2 : GIT_MKDIR_SKIP_LAST));
+}
+
+static int repo_init_directories(
+ git_str *repo_path,
+ git_str *wd_path,
+ const char *given_repo,
+ git_repository_init_options *opts)
+{
+ int error = 0;
+ bool is_bare, add_dotgit, has_dotgit, natural_wd;
+ mode_t dirmode;
+
+ /* There are three possible rules for what we are allowed to create:
+ * - MKPATH means anything we need
+ * - MKDIR means just the .git directory and its parent and the workdir
+ * - Neither means only the .git directory can be created
+ *
+ * There are 5 "segments" of path that we might need to deal with:
+ * 1. The .git directory
+ * 2. The parent of the .git directory
+ * 3. Everything above the parent of the .git directory
+ * 4. The working directory (often the same as #2)
+ * 5. Everything above the working directory (often the same as #3)
+ *
+ * For all directories created, we start with the init_mode value for
+ * permissions and then strip off bits in some cases:
+ *
+ * For MKPATH, we create #3 (and #5) paths without S_ISGID or S_IWOTH
+ * For MKPATH and MKDIR, we create #2 (and #4) without S_ISGID
+ * For all rules, we create #1 using the untouched init_mode
+ */
+
+ /* set up repo path */
+
+ is_bare = ((opts->flags & GIT_REPOSITORY_INIT_BARE) != 0);
+
+ add_dotgit =
+ (opts->flags & GIT_REPOSITORY_INIT_NO_DOTGIT_DIR) == 0 &&
+ !is_bare &&
+ git__suffixcmp(given_repo, "/" DOT_GIT) != 0 &&
+ git__suffixcmp(given_repo, "/" GIT_DIR) != 0;
+
+ if (git_str_joinpath(repo_path, given_repo, add_dotgit ? GIT_DIR : "") < 0)
+ return -1;
+
+ has_dotgit = (git__suffixcmp(repo_path->ptr, "/" GIT_DIR) == 0);
+ if (has_dotgit)
+ opts->flags |= GIT_REPOSITORY_INIT__HAS_DOTGIT;
+
+ /* set up workdir path */
+
+ if (!is_bare) {
+ if (opts->workdir_path) {
+ if (git_fs_path_join_unrooted(
+ wd_path, opts->workdir_path, repo_path->ptr, NULL) < 0)
+ return -1;
+ } else if (has_dotgit) {
+ if (git_fs_path_dirname_r(wd_path, repo_path->ptr) < 0)
+ return -1;
+ } else {
+ git_error_set(GIT_ERROR_REPOSITORY, "cannot pick working directory"
+ " for non-bare repository that isn't a '.git' directory");
+ return -1;
+ }
+
+ if (git_fs_path_to_dir(wd_path) < 0)
+ return -1;
+ } else {
+ git_str_clear(wd_path);
+ }
+
+ natural_wd =
+ has_dotgit &&
+ wd_path->size > 0 &&
+ wd_path->size + strlen(GIT_DIR) == repo_path->size &&
+ memcmp(repo_path->ptr, wd_path->ptr, wd_path->size) == 0;
+ if (natural_wd)
+ opts->flags |= GIT_REPOSITORY_INIT__NATURAL_WD;
+
+ /* create directories as needed / requested */
+
+ dirmode = pick_dir_mode(opts);
+
+ if ((opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0) {
+ /* create path #5 */
+ if (wd_path->size > 0 &&
+ (error = mkdir_parent(wd_path, dirmode, false)) < 0)
+ return error;
+
+ /* create path #3 (if not the same as #5) */
+ if (!natural_wd &&
+ (error = mkdir_parent(repo_path, dirmode, has_dotgit)) < 0)
+ return error;
+ }
+
+ if ((opts->flags & GIT_REPOSITORY_INIT_MKDIR) != 0 ||
+ (opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0)
+ {
+ /* create path #4 */
+ if (wd_path->size > 0 &&
+ (error = git_futils_mkdir(
+ wd_path->ptr, dirmode & ~S_ISGID,
+ GIT_MKDIR_VERIFY_DIR)) < 0)
+ return error;
+
+ /* create path #2 (if not the same as #4) */
+ if (!natural_wd &&
+ (error = git_futils_mkdir(
+ repo_path->ptr, dirmode & ~S_ISGID,
+ GIT_MKDIR_VERIFY_DIR | GIT_MKDIR_SKIP_LAST)) < 0)
+ return error;
+ }
+
+ if ((opts->flags & GIT_REPOSITORY_INIT_MKDIR) != 0 ||
+ (opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0 ||
+ has_dotgit)
+ {
+ /* create path #1 */
+ error = git_futils_mkdir(repo_path->ptr, dirmode,
+ GIT_MKDIR_VERIFY_DIR | ((dirmode & S_ISGID) ? GIT_MKDIR_CHMOD : 0));
+ }
+
+ /* prettify both directories now that they are created */
+
+ if (!error) {
+ error = git_fs_path_prettify_dir(repo_path, repo_path->ptr, NULL);
+
+ if (!error && wd_path->size > 0)
+ error = git_fs_path_prettify_dir(wd_path, wd_path->ptr, NULL);
+ }
+
+ return error;
+}
+
+static int repo_init_head(const char *repo_dir, const char *given)
+{
+ git_config *cfg = NULL;
+ git_str head_path = GIT_STR_INIT, cfg_branch = GIT_STR_INIT;
+ const char *initial_head = NULL;
+ int error;
+
+ if ((error = git_str_joinpath(&head_path, repo_dir, GIT_HEAD_FILE)) < 0)
+ goto out;
+
+ /*
+ * A template may have set a HEAD; use that unless it's been
+ * overridden by the caller's given initial head setting.
+ */
+ if (git_fs_path_exists(head_path.ptr) && !given)
+ goto out;
+
+ if (given) {
+ initial_head = given;
+ } else if ((error = git_config_open_default(&cfg)) >= 0 &&
+ (error = git_config__get_string_buf(&cfg_branch, cfg, "init.defaultbranch")) >= 0 &&
+ *cfg_branch.ptr) {
+ initial_head = cfg_branch.ptr;
+ }
+
+ if (!initial_head)
+ initial_head = GIT_BRANCH_DEFAULT;
+
+ error = git_repository_create_head(repo_dir, initial_head);
+
+out:
+ git_config_free(cfg);
+ git_str_dispose(&head_path);
+ git_str_dispose(&cfg_branch);
+
+ return error;
+}
+
+static int repo_init_create_origin(git_repository *repo, const char *url)
+{
+ int error;
+ git_remote *remote;
+
+ if (!(error = git_remote_create(&remote, repo, GIT_REMOTE_ORIGIN, url))) {
+ git_remote_free(remote);
+ }
+
+ return error;
+}
+
+int git_repository_init(
+ git_repository **repo_out, const char *path, unsigned is_bare)
+{
+ git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+
+ opts.flags = GIT_REPOSITORY_INIT_MKPATH; /* don't love this default */
+ if (is_bare)
+ opts.flags |= GIT_REPOSITORY_INIT_BARE;
+
+ return git_repository_init_ext(repo_out, path, &opts);
+}
+
+int git_repository_init_ext(
+ git_repository **out,
+ const char *given_repo,
+ git_repository_init_options *opts)
+{
+ git_str repo_path = GIT_STR_INIT, wd_path = GIT_STR_INIT,
+ common_path = GIT_STR_INIT;
+ const char *wd;
+ bool is_valid;
+ git_oid_t oid_type = GIT_OID_DEFAULT;
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(given_repo);
+ GIT_ASSERT_ARG(opts);
+
+ GIT_ERROR_CHECK_VERSION(opts, GIT_REPOSITORY_INIT_OPTIONS_VERSION, "git_repository_init_options");
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ if (opts->oid_type)
+ oid_type = opts->oid_type;
+#endif
+
+ if ((error = repo_init_directories(&repo_path, &wd_path, given_repo, opts)) < 0)
+ goto out;
+
+ wd = (opts->flags & GIT_REPOSITORY_INIT_BARE) ? NULL : git_str_cstr(&wd_path);
+
+ if ((error = is_valid_repository_path(&is_valid, &repo_path, &common_path, opts->flags)) < 0)
+ goto out;
+
+ if (is_valid) {
+ if ((opts->flags & GIT_REPOSITORY_INIT_NO_REINIT) != 0) {
+ git_error_set(GIT_ERROR_REPOSITORY,
+ "attempt to reinitialize '%s'", given_repo);
+ error = GIT_EEXISTS;
+ goto out;
+ }
+
+ opts->flags |= GIT_REPOSITORY_INIT__IS_REINIT;
+
+ if ((error = repo_init_config(repo_path.ptr, wd, opts->flags, opts->mode, oid_type)) < 0)
+ goto out;
+
+ /* TODO: reinitialize the templates */
+ } else {
+ if ((error = repo_init_structure(repo_path.ptr, wd, opts)) < 0 ||
+ (error = repo_init_config(repo_path.ptr, wd, opts->flags, opts->mode, oid_type)) < 0 ||
+ (error = repo_init_head(repo_path.ptr, opts->initial_head)) < 0)
+ goto out;
+ }
+
+ if ((error = git_repository_open(out, repo_path.ptr)) < 0)
+ goto out;
+
+ if (opts->origin_url &&
+ (error = repo_init_create_origin(*out, opts->origin_url)) < 0)
+ goto out;
+
+out:
+ git_str_dispose(&common_path);
+ git_str_dispose(&repo_path);
+ git_str_dispose(&wd_path);
+
+ return error;
+}
+
+int git_repository_head_detached(git_repository *repo)
+{
+ git_reference *ref;
+ git_odb *odb = NULL;
+ int exists;
+
+ if (git_repository_odb__weakptr(&odb, repo) < 0)
+ return -1;
+
+ if (git_reference_lookup(&ref, repo, GIT_HEAD_FILE) < 0)
+ return -1;
+
+ if (git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC) {
+ git_reference_free(ref);
+ return 0;
+ }
+
+ exists = git_odb_exists(odb, git_reference_target(ref));
+
+ git_reference_free(ref);
+ return exists;
+}
+
+int git_repository_head_detached_for_worktree(git_repository *repo, const char *name)
+{
+ git_reference *ref = NULL;
+ int error;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
+
+ if ((error = git_repository_head_for_worktree(&ref, repo, name)) < 0)
+ goto out;
+
+ error = (git_reference_type(ref) != GIT_REFERENCE_SYMBOLIC);
+out:
+ git_reference_free(ref);
+
+ return error;
+}
+
+int git_repository_head(git_reference **head_out, git_repository *repo)
+{
+ git_reference *head;
+ int error;
+
+ GIT_ASSERT_ARG(head_out);
+
+ if ((error = git_reference_lookup(&head, repo, GIT_HEAD_FILE)) < 0)
+ return error;
+
+ if (git_reference_type(head) == GIT_REFERENCE_DIRECT) {
+ *head_out = head;
+ return 0;
+ }
+
+ error = git_reference_lookup_resolved(head_out, repo, git_reference_symbolic_target(head), -1);
+ git_reference_free(head);
+
+ return error == GIT_ENOTFOUND ? GIT_EUNBORNBRANCH : error;
+}
+
+int git_repository_head_for_worktree(git_reference **out, git_repository *repo, const char *name)
+{
+ git_repository *worktree_repo = NULL;
+ git_worktree *worktree = NULL;
+ git_reference *head = NULL;
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
+
+ *out = NULL;
+
+ if ((error = git_worktree_lookup(&worktree, repo, name)) < 0 ||
+ (error = git_repository_open_from_worktree(&worktree_repo, worktree)) < 0 ||
+ (error = git_reference_lookup(&head, worktree_repo, GIT_HEAD_FILE)) < 0)
+ goto out;
+
+ if (git_reference_type(head) != GIT_REFERENCE_DIRECT) {
+ if ((error = git_reference_lookup_resolved(out, worktree_repo, git_reference_symbolic_target(head), -1)) < 0)
+ goto out;
+ } else {
+ *out = head;
+ head = NULL;
+ }
+
+out:
+ git_reference_free(head);
+ git_worktree_free(worktree);
+ git_repository_free(worktree_repo);
+ return error;
+}
+
+int git_repository_foreach_worktree(git_repository *repo,
+ git_repository_foreach_worktree_cb cb,
+ void *payload)
+{
+ git_strarray worktrees = {0};
+ git_repository *worktree_repo = NULL;
+ git_worktree *worktree = NULL;
+ int error;
+ size_t i;
+
+ /* apply operation to repository supplied when commondir is empty, implying there's
+ * no linked worktrees to iterate, which can occur when using custom odb/refdb
+ */
+ if (!repo->commondir)
+ return cb(repo, payload);
+
+ if ((error = git_repository_open(&worktree_repo, repo->commondir)) < 0 ||
+ (error = cb(worktree_repo, payload) != 0))
+ goto out;
+
+ git_repository_free(worktree_repo);
+ worktree_repo = NULL;
+
+ if ((error = git_worktree_list(&worktrees, repo)) < 0)
+ goto out;
+
+ for (i = 0; i < worktrees.count; i++) {
+ git_repository_free(worktree_repo);
+ worktree_repo = NULL;
+ git_worktree_free(worktree);
+ worktree = NULL;
+
+ if ((error = git_worktree_lookup(&worktree, repo, worktrees.strings[i]) < 0) ||
+ (error = git_repository_open_from_worktree(&worktree_repo, worktree)) < 0) {
+ if (error != GIT_ENOTFOUND)
+ goto out;
+ error = 0;
+ continue;
+ }
+
+ if ((error = cb(worktree_repo, payload)) != 0)
+ goto out;
+ }
+
+out:
+ git_strarray_dispose(&worktrees);
+ git_repository_free(worktree_repo);
+ git_worktree_free(worktree);
+ return error;
+}
+
+int git_repository_head_unborn(git_repository *repo)
+{
+ git_reference *ref = NULL;
+ int error;
+
+ error = git_repository_head(&ref, repo);
+ git_reference_free(ref);
+
+ if (error == GIT_EUNBORNBRANCH) {
+ git_error_clear();
+ return 1;
+ }
+
+ if (error < 0)
+ return -1;
+
+ return 0;
+}
+
+static int repo_contains_no_reference(git_repository *repo)
+{
+ git_reference_iterator *iter;
+ const char *refname;
+ int error;
+
+ if ((error = git_reference_iterator_new(&iter, repo)) < 0)
+ return error;
+
+ error = git_reference_next_name(&refname, iter);
+ git_reference_iterator_free(iter);
+
+ if (error == GIT_ITEROVER)
+ return 1;
+
+ return error;
+}
+
+int git_repository_initialbranch(git_str *out, git_repository *repo)
+{
+ git_config *config;
+ git_config_entry *entry = NULL;
+ const char *branch;
+ int valid, error;
+
+ if ((error = git_repository_config__weakptr(&config, repo)) < 0)
+ return error;
+
+ if ((error = git_config_get_entry(&entry, config, "init.defaultbranch")) == 0 &&
+ *entry->value) {
+ branch = entry->value;
+ }
+ else if (!error || error == GIT_ENOTFOUND) {
+ branch = GIT_BRANCH_DEFAULT;
+ }
+ else {
+ goto done;
+ }
+
+ if ((error = git_str_puts(out, GIT_REFS_HEADS_DIR)) < 0 ||
+ (error = git_str_puts(out, branch)) < 0 ||
+ (error = git_reference_name_is_valid(&valid, out->ptr)) < 0)
+ goto done;
+
+ if (!valid) {
+ git_error_set(GIT_ERROR_INVALID, "the value of init.defaultBranch is not a valid branch name");
+ error = -1;
+ }
+
+done:
+ git_config_entry_free(entry);
+ return error;
+}
+
+int git_repository_is_empty(git_repository *repo)
+{
+ git_reference *head = NULL;
+ git_str initialbranch = GIT_STR_INIT;
+ int result = 0;
+
+ if ((result = git_reference_lookup(&head, repo, GIT_HEAD_FILE)) < 0 ||
+ (result = git_repository_initialbranch(&initialbranch, repo)) < 0)
+ goto done;
+
+ result = (git_reference_type(head) == GIT_REFERENCE_SYMBOLIC &&
+ strcmp(git_reference_symbolic_target(head), initialbranch.ptr) == 0 &&
+ repo_contains_no_reference(repo));
+
+done:
+ git_reference_free(head);
+ git_str_dispose(&initialbranch);
+
+ return result;
+}
+
+static const char *resolved_parent_path(const git_repository *repo, git_repository_item_t item, git_repository_item_t fallback)
+{
+ const char *parent;
+
+ switch (item) {
+ case GIT_REPOSITORY_ITEM_GITDIR:
+ parent = git_repository_path(repo);
+ break;
+ case GIT_REPOSITORY_ITEM_WORKDIR:
+ parent = git_repository_workdir(repo);
+ break;
+ case GIT_REPOSITORY_ITEM_COMMONDIR:
+ parent = git_repository_commondir(repo);
+ break;
+ default:
+ git_error_set(GIT_ERROR_INVALID, "invalid item directory");
+ return NULL;
+ }
+ if (!parent && fallback != GIT_REPOSITORY_ITEM__LAST)
+ return resolved_parent_path(repo, fallback, GIT_REPOSITORY_ITEM__LAST);
+
+ return parent;
+}
+
+int git_repository_item_path(
+ git_buf *out,
+ const git_repository *repo,
+ git_repository_item_t item)
+{
+ GIT_BUF_WRAP_PRIVATE(out, git_repository__item_path, repo, item);
+}
+
+int git_repository__item_path(
+ git_str *out,
+ const git_repository *repo,
+ git_repository_item_t item)
+{
+ const char *parent = resolved_parent_path(repo, items[item].parent, items[item].fallback);
+ if (parent == NULL) {
+ git_error_set(GIT_ERROR_INVALID, "path cannot exist in repository");
+ return GIT_ENOTFOUND;
+ }
+
+ if (git_str_sets(out, parent) < 0)
+ return -1;
+
+ if (items[item].name) {
+ if (git_str_joinpath(out, parent, items[item].name) < 0)
+ return -1;
+ }
+
+ if (items[item].directory) {
+ if (git_fs_path_to_dir(out) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+const char *git_repository_path(const git_repository *repo)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(repo, NULL);
+ return repo->gitdir;
+}
+
+const char *git_repository_workdir(const git_repository *repo)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(repo, NULL);
+
+ if (repo->is_bare)
+ return NULL;
+
+ return repo->workdir;
+}
+
+int git_repository_workdir_path(
+ git_str *out, git_repository *repo, const char *path)
+{
+ int error;
+
+ if (!repo->workdir) {
+ git_error_set(GIT_ERROR_REPOSITORY, "repository has no working directory");
+ return GIT_EBAREREPO;
+ }
+
+ if (!(error = git_str_joinpath(out, repo->workdir, path)))
+ error = git_path_validate_str_length(repo, out);
+
+ return error;
+}
+
+const char *git_repository_commondir(const git_repository *repo)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(repo, NULL);
+ return repo->commondir;
+}
+
+int git_repository_set_workdir(
+ git_repository *repo, const char *workdir, int update_gitlink)
+{
+ int error = 0;
+ git_str path = GIT_STR_INIT;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(workdir);
+
+ if (git_fs_path_prettify_dir(&path, workdir, NULL) < 0)
+ return -1;
+
+ if (repo->workdir && strcmp(repo->workdir, path.ptr) == 0)
+ return 0;
+
+ if (update_gitlink) {
+ git_config *config;
+
+ if (git_repository_config__weakptr(&config, repo) < 0)
+ return -1;
+
+ error = repo_write_gitlink(path.ptr, git_repository_path(repo), false);
+
+ /* passthrough error means gitlink is unnecessary */
+ if (error == GIT_PASSTHROUGH)
+ error = git_config_delete_entry(config, "core.worktree");
+ else if (!error)
+ error = git_config_set_string(config, "core.worktree", path.ptr);
+
+ if (!error)
+ error = git_config_set_bool(config, "core.bare", false);
+ }
+
+ if (!error) {
+ char *old_workdir = repo->workdir;
+
+ repo->workdir = git_str_detach(&path);
+ repo->is_bare = 0;
+
+ git__free(old_workdir);
+ }
+
+ return error;
+}
+
+int git_repository_is_bare(const git_repository *repo)
+{
+ GIT_ASSERT_ARG(repo);
+ return repo->is_bare;
+}
+
+int git_repository_is_worktree(const git_repository *repo)
+{
+ GIT_ASSERT_ARG(repo);
+ return repo->is_worktree;
+}
+
+int git_repository_set_bare(git_repository *repo)
+{
+ int error;
+ git_config *config;
+
+ GIT_ASSERT_ARG(repo);
+
+ if (repo->is_bare)
+ return 0;
+
+ if ((error = git_repository_config__weakptr(&config, repo)) < 0)
+ return error;
+
+ if ((error = git_config_set_bool(config, "core.bare", true)) < 0)
+ return error;
+
+ if ((error = git_config__update_entry(config, "core.worktree", NULL, true, true)) < 0)
+ return error;
+
+ git__free(repo->workdir);
+ repo->workdir = NULL;
+ repo->is_bare = 1;
+
+ return 0;
+}
+
+int git_repository_head_tree(git_tree **tree, git_repository *repo)
+{
+ git_reference *head;
+ git_object *obj;
+ int error;
+
+ if ((error = git_repository_head(&head, repo)) < 0)
+ return error;
+
+ if ((error = git_reference_peel(&obj, head, GIT_OBJECT_TREE)) < 0)
+ goto cleanup;
+
+ *tree = (git_tree *)obj;
+
+cleanup:
+ git_reference_free(head);
+ return error;
+}
+
+int git_repository__set_orig_head(git_repository *repo, const git_oid *orig_head)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ git_str file_path = GIT_STR_INIT;
+ char orig_head_str[GIT_OID_MAX_HEXSIZE];
+ int error = 0;
+
+ git_oid_fmt(orig_head_str, orig_head);
+
+ if ((error = git_str_joinpath(&file_path, repo->gitdir, GIT_ORIG_HEAD_FILE)) == 0 &&
+ (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_CREATE_LEADING_DIRS, GIT_MERGE_FILE_MODE)) == 0 &&
+ (error = git_filebuf_printf(&file, "%.*s\n", (int)git_oid_hexsize(repo->oid_type), orig_head_str)) == 0)
+ error = git_filebuf_commit(&file);
+
+ if (error < 0)
+ git_filebuf_cleanup(&file);
+
+ git_str_dispose(&file_path);
+
+ return error;
+}
+
+static int git_repository__message(git_str *out, git_repository *repo)
+{
+ git_str path = GIT_STR_INIT;
+ struct stat st;
+ int error;
+
+ if (git_str_joinpath(&path, repo->gitdir, GIT_MERGE_MSG_FILE) < 0)
+ return -1;
+
+ if ((error = p_stat(git_str_cstr(&path), &st)) < 0) {
+ if (errno == ENOENT)
+ error = GIT_ENOTFOUND;
+ git_error_set(GIT_ERROR_OS, "could not access message file");
+ } else {
+ error = git_futils_readbuffer(out, git_str_cstr(&path));
+ }
+
+ git_str_dispose(&path);
+
+ return error;
+}
+
+int git_repository_message(git_buf *out, git_repository *repo)
+{
+ GIT_BUF_WRAP_PRIVATE(out, git_repository__message, repo);
+}
+
+int git_repository_message_remove(git_repository *repo)
+{
+ git_str path = GIT_STR_INIT;
+ int error;
+
+ if (git_str_joinpath(&path, repo->gitdir, GIT_MERGE_MSG_FILE) < 0)
+ return -1;
+
+ error = p_unlink(git_str_cstr(&path));
+ git_str_dispose(&path);
+
+ return error;
+}
+
+int git_repository_hashfile(
+ git_oid *out,
+ git_repository *repo,
+ const char *path,
+ git_object_t type,
+ const char *as_path)
+{
+ int error;
+ git_filter_list *fl = NULL;
+ git_file fd = -1;
+ uint64_t len;
+ git_str full_path = GIT_STR_INIT;
+ const char *workdir = git_repository_workdir(repo);
+
+ /* as_path can be NULL */
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(path);
+ GIT_ASSERT_ARG(repo);
+
+ if ((error = git_fs_path_join_unrooted(&full_path, path, workdir, NULL)) < 0 ||
+ (error = git_path_validate_str_length(repo, &full_path)) < 0)
+ return error;
+
+ /*
+ * NULL as_path means that we should derive it from the
+ * given path.
+ */
+ if (!as_path) {
+ if (workdir && !git__prefixcmp(full_path.ptr, workdir))
+ as_path = full_path.ptr + strlen(workdir);
+ else
+ as_path = "";
+ }
+
+ /* passing empty string for "as_path" indicated --no-filters */
+ if (strlen(as_path) > 0) {
+ error = git_filter_list_load(
+ &fl, repo, NULL, as_path,
+ GIT_FILTER_TO_ODB, GIT_FILTER_DEFAULT);
+
+ if (error < 0)
+ return error;
+ }
+
+ /* at this point, error is a count of the number of loaded filters */
+
+ fd = git_futils_open_ro(full_path.ptr);
+ if (fd < 0) {
+ error = fd;
+ goto cleanup;
+ }
+
+ if ((error = git_futils_filesize(&len, fd)) < 0)
+ goto cleanup;
+
+ if (!git__is_sizet(len)) {
+ git_error_set(GIT_ERROR_OS, "file size overflow for 32-bit systems");
+ error = -1;
+ goto cleanup;
+ }
+
+ error = git_odb__hashfd_filtered(out, fd, (size_t)len, type, repo->oid_type, fl);
+
+cleanup:
+ if (fd >= 0)
+ p_close(fd);
+ git_filter_list_free(fl);
+ git_str_dispose(&full_path);
+
+ return error;
+}
+
+static int checkout_message(git_str *out, git_reference *old, const char *new)
+{
+ const char *idstr;
+
+ git_str_puts(out, "checkout: moving from ");
+
+ if (git_reference_type(old) == GIT_REFERENCE_SYMBOLIC) {
+ git_str_puts(out, git_reference__shorthand(git_reference_symbolic_target(old)));
+ } else {
+ if ((idstr = git_oid_tostr_s(git_reference_target(old))) == NULL)
+ return -1;
+
+ git_str_puts(out, idstr);
+ }
+
+ git_str_puts(out, " to ");
+
+ if (git_reference__is_branch(new) ||
+ git_reference__is_tag(new) ||
+ git_reference__is_remote(new))
+ git_str_puts(out, git_reference__shorthand(new));
+ else
+ git_str_puts(out, new);
+
+ if (git_str_oom(out))
+ return -1;
+
+ return 0;
+}
+
+static int detach(git_repository *repo, const git_oid *id, const char *new)
+{
+ int error;
+ git_str log_message = GIT_STR_INIT;
+ git_object *object = NULL, *peeled = NULL;
+ git_reference *new_head = NULL, *current = NULL;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(id);
+
+ if ((error = git_reference_lookup(&current, repo, GIT_HEAD_FILE)) < 0)
+ return error;
+
+ if ((error = git_object_lookup(&object, repo, id, GIT_OBJECT_ANY)) < 0)
+ goto cleanup;
+
+ if ((error = git_object_peel(&peeled, object, GIT_OBJECT_COMMIT)) < 0)
+ goto cleanup;
+
+ if (new == NULL &&
+ (new = git_oid_tostr_s(git_object_id(peeled))) == NULL) {
+ error = -1;
+ goto cleanup;
+ }
+
+ if ((error = checkout_message(&log_message, current, new)) < 0)
+ goto cleanup;
+
+ error = git_reference_create(&new_head, repo, GIT_HEAD_FILE, git_object_id(peeled), true, git_str_cstr(&log_message));
+
+cleanup:
+ git_str_dispose(&log_message);
+ git_object_free(object);
+ git_object_free(peeled);
+ git_reference_free(current);
+ git_reference_free(new_head);
+ return error;
+}
+
+int git_repository_set_head(
+ git_repository *repo,
+ const char *refname)
+{
+ git_reference *ref = NULL, *current = NULL, *new_head = NULL;
+ git_str log_message = GIT_STR_INIT;
+ int error;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(refname);
+
+ if ((error = git_reference_lookup(&current, repo, GIT_HEAD_FILE)) < 0)
+ return error;
+
+ if ((error = checkout_message(&log_message, current, refname)) < 0)
+ goto cleanup;
+
+ error = git_reference_lookup(&ref, repo, refname);
+ if (error < 0 && error != GIT_ENOTFOUND)
+ goto cleanup;
+
+ if (ref && current->type == GIT_REFERENCE_SYMBOLIC && git__strcmp(current->target.symbolic, ref->name) &&
+ git_reference_is_branch(ref) && git_branch_is_checked_out(ref)) {
+ git_error_set(GIT_ERROR_REPOSITORY, "cannot set HEAD to reference '%s' as it is the current HEAD "
+ "of a linked repository.", git_reference_name(ref));
+ error = -1;
+ goto cleanup;
+ }
+
+ if (!error) {
+ if (git_reference_is_branch(ref)) {
+ error = git_reference_symbolic_create(&new_head, repo, GIT_HEAD_FILE,
+ git_reference_name(ref), true, git_str_cstr(&log_message));
+ } else {
+ error = detach(repo, git_reference_target(ref),
+ git_reference_is_tag(ref) || git_reference_is_remote(ref) ? refname : NULL);
+ }
+ } else if (git_reference__is_branch(refname)) {
+ error = git_reference_symbolic_create(&new_head, repo, GIT_HEAD_FILE, refname,
+ true, git_str_cstr(&log_message));
+ }
+
+cleanup:
+ git_str_dispose(&log_message);
+ git_reference_free(current);
+ git_reference_free(ref);
+ git_reference_free(new_head);
+ return error;
+}
+
+int git_repository_set_head_detached(
+ git_repository *repo,
+ const git_oid *committish)
+{
+ return detach(repo, committish, NULL);
+}
+
+int git_repository_set_head_detached_from_annotated(
+ git_repository *repo,
+ const git_annotated_commit *committish)
+{
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(committish);
+
+ return detach(repo, git_annotated_commit_id(committish), committish->description);
+}
+
+int git_repository_detach_head(git_repository *repo)
+{
+ git_reference *old_head = NULL, *new_head = NULL, *current = NULL;
+ git_object *object = NULL;
+ git_str log_message = GIT_STR_INIT;
+ const char *idstr;
+ int error;
+
+ GIT_ASSERT_ARG(repo);
+
+ if ((error = git_reference_lookup(&current, repo, GIT_HEAD_FILE)) < 0)
+ return error;
+
+ if ((error = git_repository_head(&old_head, repo)) < 0)
+ goto cleanup;
+
+ if ((error = git_object_lookup(&object, repo, git_reference_target(old_head), GIT_OBJECT_COMMIT)) < 0)
+ goto cleanup;
+
+ if ((idstr = git_oid_tostr_s(git_object_id(object))) == NULL) {
+ error = -1;
+ goto cleanup;
+ }
+
+ if ((error = checkout_message(&log_message, current, idstr)) < 0)
+ goto cleanup;
+
+ error = git_reference_create(&new_head, repo, GIT_HEAD_FILE, git_reference_target(old_head),
+ 1, git_str_cstr(&log_message));
+
+cleanup:
+ git_str_dispose(&log_message);
+ git_object_free(object);
+ git_reference_free(old_head);
+ git_reference_free(new_head);
+ git_reference_free(current);
+ return error;
+}
+
+/**
+ * Loosely ported from git.git
+ * https://github.com/git/git/blob/master/contrib/completion/git-prompt.sh#L198-289
+ */
+int git_repository_state(git_repository *repo)
+{
+ git_str repo_path = GIT_STR_INIT;
+ int state = GIT_REPOSITORY_STATE_NONE;
+
+ GIT_ASSERT_ARG(repo);
+
+ if (git_str_puts(&repo_path, repo->gitdir) < 0)
+ return -1;
+
+ if (git_fs_path_contains_file(&repo_path, GIT_REBASE_MERGE_INTERACTIVE_FILE))
+ state = GIT_REPOSITORY_STATE_REBASE_INTERACTIVE;
+ else if (git_fs_path_contains_dir(&repo_path, GIT_REBASE_MERGE_DIR))
+ state = GIT_REPOSITORY_STATE_REBASE_MERGE;
+ else if (git_fs_path_contains_file(&repo_path, GIT_REBASE_APPLY_REBASING_FILE))
+ state = GIT_REPOSITORY_STATE_REBASE;
+ else if (git_fs_path_contains_file(&repo_path, GIT_REBASE_APPLY_APPLYING_FILE))
+ state = GIT_REPOSITORY_STATE_APPLY_MAILBOX;
+ else if (git_fs_path_contains_dir(&repo_path, GIT_REBASE_APPLY_DIR))
+ state = GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE;
+ else if (git_fs_path_contains_file(&repo_path, GIT_MERGE_HEAD_FILE))
+ state = GIT_REPOSITORY_STATE_MERGE;
+ else if (git_fs_path_contains_file(&repo_path, GIT_REVERT_HEAD_FILE)) {
+ state = GIT_REPOSITORY_STATE_REVERT;
+ if (git_fs_path_contains_file(&repo_path, GIT_SEQUENCER_TODO_FILE)) {
+ state = GIT_REPOSITORY_STATE_REVERT_SEQUENCE;
+ }
+ } else if (git_fs_path_contains_file(&repo_path, GIT_CHERRYPICK_HEAD_FILE)) {
+ state = GIT_REPOSITORY_STATE_CHERRYPICK;
+ if (git_fs_path_contains_file(&repo_path, GIT_SEQUENCER_TODO_FILE)) {
+ state = GIT_REPOSITORY_STATE_CHERRYPICK_SEQUENCE;
+ }
+ } else if (git_fs_path_contains_file(&repo_path, GIT_BISECT_LOG_FILE))
+ state = GIT_REPOSITORY_STATE_BISECT;
+
+ git_str_dispose(&repo_path);
+ return state;
+}
+
+int git_repository__cleanup_files(
+ git_repository *repo, const char *files[], size_t files_len)
+{
+ git_str buf = GIT_STR_INIT;
+ size_t i;
+ int error;
+
+ for (error = 0, i = 0; !error && i < files_len; ++i) {
+ const char *path;
+
+ if (git_str_joinpath(&buf, repo->gitdir, files[i]) < 0)
+ return -1;
+
+ path = git_str_cstr(&buf);
+
+ if (git_fs_path_isfile(path)) {
+ error = p_unlink(path);
+ } else if (git_fs_path_isdir(path)) {
+ error = git_futils_rmdir_r(path, NULL,
+ GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_REMOVE_BLOCKERS);
+ }
+
+ git_str_clear(&buf);
+ }
+
+ git_str_dispose(&buf);
+ return error;
+}
+
+static const char *state_files[] = {
+ GIT_MERGE_HEAD_FILE,
+ GIT_MERGE_MODE_FILE,
+ GIT_MERGE_MSG_FILE,
+ GIT_REVERT_HEAD_FILE,
+ GIT_CHERRYPICK_HEAD_FILE,
+ GIT_BISECT_LOG_FILE,
+ GIT_REBASE_MERGE_DIR,
+ GIT_REBASE_APPLY_DIR,
+ GIT_SEQUENCER_DIR,
+};
+
+int git_repository_state_cleanup(git_repository *repo)
+{
+ GIT_ASSERT_ARG(repo);
+
+ return git_repository__cleanup_files(repo, state_files, ARRAY_SIZE(state_files));
+}
+
+int git_repository__shallow_roots(
+ git_oid **out,
+ size_t *out_len,
+ git_repository *repo)
+{
+ int error = 0;
+
+ if (!repo->shallow_grafts && (error = load_grafts(repo)) < 0)
+ return error;
+
+ if ((error = git_grafts_refresh(repo->shallow_grafts)) < 0)
+ return error;
+
+ if ((error = git_grafts_oids(out, out_len, repo->shallow_grafts)) < 0)
+ return error;
+
+ return 0;
+}
+
+int git_repository__shallow_roots_write(git_repository *repo, git_oidarray *roots)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ git_str path = GIT_STR_INIT;
+ char oid_str[GIT_OID_MAX_HEXSIZE + 1];
+ size_t i;
+ int filebuf_hash, error = 0;
+
+ GIT_ASSERT_ARG(repo);
+
+ filebuf_hash = git_filebuf_hash_flags(git_oid_algorithm(repo->oid_type));
+ GIT_ASSERT(filebuf_hash);
+
+ if ((error = git_str_joinpath(&path, repo->gitdir, "shallow")) < 0)
+ goto on_error;
+
+ if ((error = git_filebuf_open(&file, git_str_cstr(&path), filebuf_hash, 0666)) < 0)
+ goto on_error;
+
+ for (i = 0; i < roots->count; i++) {
+ git_oid_tostr(oid_str, sizeof(oid_str), &roots->ids[i]);
+ git_filebuf_write(&file, oid_str, git_oid_hexsize(repo->oid_type));
+ git_filebuf_write(&file, "\n", 1);
+ }
+
+ git_filebuf_commit(&file);
+
+ if ((error = load_grafts(repo)) < 0) {
+ error = -1;
+ goto on_error;
+ }
+
+ if (!roots->count)
+ remove(path.ptr);
+
+on_error:
+ git_str_dispose(&path);
+
+ return error;
+}
+
+int git_repository_is_shallow(git_repository *repo)
+{
+ git_str path = GIT_STR_INIT;
+ struct stat st;
+ int error;
+
+ if ((error = git_str_joinpath(&path, repo->gitdir, "shallow")) < 0)
+ return error;
+
+ error = git_fs_path_lstat(path.ptr, &st);
+ git_str_dispose(&path);
+
+ if (error == GIT_ENOTFOUND) {
+ git_error_clear();
+ return 0;
+ }
+
+ if (error < 0)
+ return error;
+
+ return st.st_size == 0 ? 0 : 1;
+}
+
+int git_repository_init_options_init(
+ git_repository_init_options *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_repository_init_options,
+ GIT_REPOSITORY_INIT_OPTIONS_INIT);
+ return 0;
+}
+
+#ifndef GIT_DEPRECATE_HARD
+int git_repository_init_init_options(
+ git_repository_init_options *opts, unsigned int version)
+{
+ return git_repository_init_options_init(opts, version);
+}
+#endif
+
+int git_repository_ident(const char **name, const char **email, const git_repository *repo)
+{
+ *name = repo->ident_name;
+ *email = repo->ident_email;
+
+ return 0;
+}
+
+int git_repository_set_ident(git_repository *repo, const char *name, const char *email)
+{
+ char *tmp_name = NULL, *tmp_email = NULL;
+
+ if (name) {
+ tmp_name = git__strdup(name);
+ GIT_ERROR_CHECK_ALLOC(tmp_name);
+ }
+
+ if (email) {
+ tmp_email = git__strdup(email);
+ GIT_ERROR_CHECK_ALLOC(tmp_email);
+ }
+
+ tmp_name = git_atomic_swap(repo->ident_name, tmp_name);
+ tmp_email = git_atomic_swap(repo->ident_email, tmp_email);
+
+ git__free(tmp_name);
+ git__free(tmp_email);
+
+ return 0;
+}
+
+int git_repository_submodule_cache_all(git_repository *repo)
+{
+ GIT_ASSERT_ARG(repo);
+ return git_submodule_cache_init(&repo->submodule_cache, repo);
+}
+
+int git_repository_submodule_cache_clear(git_repository *repo)
+{
+ int error = 0;
+ GIT_ASSERT_ARG(repo);
+
+ error = git_submodule_cache_free(repo->submodule_cache);
+ repo->submodule_cache = NULL;
+ return error;
+}
+
+git_oid_t git_repository_oid_type(git_repository *repo)
+{
+ return repo ? repo->oid_type : 0;
+}
diff --git a/src/libgit2/repository.h b/src/libgit2/repository.h
new file mode 100644
index 0000000..6d2b64c
--- /dev/null
+++ b/src/libgit2/repository.h
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_repository_h__
+#define INCLUDE_repository_h__
+
+#include "common.h"
+
+#include "git2/common.h"
+#include "git2/oid.h"
+#include "git2/odb.h"
+#include "git2/repository.h"
+#include "git2/object.h"
+#include "git2/config.h"
+
+#include "array.h"
+#include "cache.h"
+#include "refs.h"
+#include "str.h"
+#include "object.h"
+#include "attrcache.h"
+#include "submodule.h"
+#include "diff_driver.h"
+#include "grafts.h"
+
+#define DOT_GIT ".git"
+#define GIT_DIR DOT_GIT "/"
+#define GIT_DIR_MODE 0755
+#define GIT_BARE_DIR_MODE 0777
+
+/* Default DOS-compatible 8.3 "short name" for a git repository, "GIT~1" */
+#define GIT_DIR_SHORTNAME "GIT~1"
+
+extern bool git_repository__fsync_gitdir;
+extern bool git_repository__validate_ownership;
+
+/** Cvar cache identifiers */
+typedef enum {
+ GIT_CONFIGMAP_AUTO_CRLF = 0, /* core.autocrlf */
+ GIT_CONFIGMAP_EOL, /* core.eol */
+ GIT_CONFIGMAP_SYMLINKS, /* core.symlinks */
+ GIT_CONFIGMAP_IGNORECASE, /* core.ignorecase */
+ GIT_CONFIGMAP_FILEMODE, /* core.filemode */
+ GIT_CONFIGMAP_IGNORESTAT, /* core.ignorestat */
+ GIT_CONFIGMAP_TRUSTCTIME, /* core.trustctime */
+ GIT_CONFIGMAP_ABBREV, /* core.abbrev */
+ GIT_CONFIGMAP_PRECOMPOSE, /* core.precomposeunicode */
+ GIT_CONFIGMAP_SAFE_CRLF, /* core.safecrlf */
+ GIT_CONFIGMAP_LOGALLREFUPDATES, /* core.logallrefupdates */
+ GIT_CONFIGMAP_PROTECTHFS, /* core.protectHFS */
+ GIT_CONFIGMAP_PROTECTNTFS, /* core.protectNTFS */
+ GIT_CONFIGMAP_FSYNCOBJECTFILES, /* core.fsyncObjectFiles */
+ GIT_CONFIGMAP_LONGPATHS, /* core.longpaths */
+ GIT_CONFIGMAP_CACHE_MAX
+} git_configmap_item;
+
+/**
+ * Configuration map value enumerations
+ *
+ * These are the values that are actually stored in the configmap cache,
+ * instead of their string equivalents. These values are internal and
+ * symbolic; make sure that none of them is set to `-1`, since that is
+ * the unique identifier for "not cached"
+ */
+typedef enum {
+ /* The value hasn't been loaded from the cache yet */
+ GIT_CONFIGMAP_NOT_CACHED = -1,
+
+ /* core.safecrlf: false, 'fail', 'warn' */
+ GIT_SAFE_CRLF_FALSE = 0,
+ GIT_SAFE_CRLF_FAIL = 1,
+ GIT_SAFE_CRLF_WARN = 2,
+
+ /* core.autocrlf: false, true, 'input; */
+ GIT_AUTO_CRLF_FALSE = 0,
+ GIT_AUTO_CRLF_TRUE = 1,
+ GIT_AUTO_CRLF_INPUT = 2,
+ GIT_AUTO_CRLF_DEFAULT = GIT_AUTO_CRLF_FALSE,
+
+ /* core.eol: unset, 'crlf', 'lf', 'native' */
+ GIT_EOL_UNSET = 0,
+ GIT_EOL_CRLF = 1,
+ GIT_EOL_LF = 2,
+#ifdef GIT_WIN32
+ GIT_EOL_NATIVE = GIT_EOL_CRLF,
+#else
+ GIT_EOL_NATIVE = GIT_EOL_LF,
+#endif
+ GIT_EOL_DEFAULT = GIT_EOL_NATIVE,
+
+ /* core.symlinks: bool */
+ GIT_SYMLINKS_DEFAULT = GIT_CONFIGMAP_TRUE,
+ /* core.ignorecase */
+ GIT_IGNORECASE_DEFAULT = GIT_CONFIGMAP_FALSE,
+ /* core.filemode */
+ GIT_FILEMODE_DEFAULT = GIT_CONFIGMAP_TRUE,
+ /* core.ignorestat */
+ GIT_IGNORESTAT_DEFAULT = GIT_CONFIGMAP_FALSE,
+ /* core.trustctime */
+ GIT_TRUSTCTIME_DEFAULT = GIT_CONFIGMAP_TRUE,
+ /* core.abbrev */
+ GIT_ABBREV_DEFAULT = 7,
+ /* core.precomposeunicode */
+ GIT_PRECOMPOSE_DEFAULT = GIT_CONFIGMAP_FALSE,
+ /* core.safecrlf */
+ GIT_SAFE_CRLF_DEFAULT = GIT_CONFIGMAP_FALSE,
+ /* core.logallrefupdates */
+ GIT_LOGALLREFUPDATES_FALSE = GIT_CONFIGMAP_FALSE,
+ GIT_LOGALLREFUPDATES_TRUE = GIT_CONFIGMAP_TRUE,
+ GIT_LOGALLREFUPDATES_UNSET = 2,
+ GIT_LOGALLREFUPDATES_ALWAYS = 3,
+ GIT_LOGALLREFUPDATES_DEFAULT = GIT_LOGALLREFUPDATES_UNSET,
+ /* core.protectHFS */
+ GIT_PROTECTHFS_DEFAULT = GIT_CONFIGMAP_FALSE,
+ /* core.protectNTFS */
+ GIT_PROTECTNTFS_DEFAULT = GIT_CONFIGMAP_TRUE,
+ /* core.fsyncObjectFiles */
+ GIT_FSYNCOBJECTFILES_DEFAULT = GIT_CONFIGMAP_FALSE,
+ /* core.longpaths */
+ GIT_LONGPATHS_DEFAULT = GIT_CONFIGMAP_FALSE
+} git_configmap_value;
+
+/* internal repository init flags */
+enum {
+ GIT_REPOSITORY_INIT__HAS_DOTGIT = (1u << 16),
+ GIT_REPOSITORY_INIT__NATURAL_WD = (1u << 17),
+ GIT_REPOSITORY_INIT__IS_REINIT = (1u << 18)
+};
+
+/** Internal structure for repository object */
+struct git_repository {
+ git_odb *_odb;
+ git_refdb *_refdb;
+ git_config *_config;
+ git_index *_index;
+
+ git_cache objects;
+ git_attr_cache *attrcache;
+ git_diff_driver_registry *diff_drivers;
+
+ char *gitlink;
+ char *gitdir;
+ char *commondir;
+ char *workdir;
+ char *namespace;
+
+ char *ident_name;
+ char *ident_email;
+
+ git_array_t(git_str) reserved_names;
+
+ unsigned use_env:1,
+ is_bare:1,
+ is_worktree:1;
+ git_oid_t oid_type;
+
+ unsigned int lru_counter;
+
+ git_grafts *grafts;
+ git_grafts *shallow_grafts;
+
+ git_atomic32 attr_session_key;
+
+ intptr_t configmap_cache[GIT_CONFIGMAP_CACHE_MAX];
+ git_strmap *submodule_cache;
+};
+
+GIT_INLINE(git_attr_cache *) git_repository_attr_cache(git_repository *repo)
+{
+ return repo->attrcache;
+}
+
+int git_repository_head_tree(git_tree **tree, git_repository *repo);
+int git_repository_create_head(const char *git_dir, const char *ref_name);
+
+typedef int (*git_repository_foreach_worktree_cb)(git_repository *, void *);
+
+int git_repository_foreach_worktree(git_repository *repo,
+ git_repository_foreach_worktree_cb cb,
+ void *payload);
+
+/*
+ * Weak pointers to repository internals.
+ *
+ * The returned pointers do not need to be freed. Do not keep
+ * permanent references to these (i.e. between API calls), since they may
+ * become invalidated if the user replaces a repository internal.
+ */
+int git_repository_config__weakptr(git_config **out, git_repository *repo);
+int git_repository_odb__weakptr(git_odb **out, git_repository *repo);
+int git_repository_refdb__weakptr(git_refdb **out, git_repository *repo);
+int git_repository_index__weakptr(git_index **out, git_repository *repo);
+int git_repository_grafts__weakptr(git_grafts **out, git_repository *repo);
+int git_repository_shallow_grafts__weakptr(git_grafts **out, git_repository *repo);
+
+int git_repository__wrap_odb(
+ git_repository **out,
+ git_odb *odb,
+ git_oid_t oid_type);
+
+/*
+ * Configuration map cache
+ *
+ * Efficient access to the most used config variables of a repository.
+ * The cache is cleared every time the config backend is replaced.
+ */
+int git_repository__configmap_lookup(int *out, git_repository *repo, git_configmap_item item);
+void git_repository__configmap_lookup_cache_clear(git_repository *repo);
+
+int git_repository__item_path(git_str *out, const git_repository *repo, git_repository_item_t item);
+
+GIT_INLINE(int) git_repository__ensure_not_bare(
+ git_repository *repo,
+ const char *operation_name)
+{
+ if (!git_repository_is_bare(repo))
+ return 0;
+
+ git_error_set(
+ GIT_ERROR_REPOSITORY,
+ "cannot %s. This operation is not allowed against bare repositories.",
+ operation_name);
+
+ return GIT_EBAREREPO;
+}
+
+int git_repository__set_orig_head(git_repository *repo, const git_oid *orig_head);
+
+int git_repository__cleanup_files(git_repository *repo, const char *files[], size_t files_len);
+
+/* The default "reserved names" for a repository */
+extern git_str git_repository__reserved_names_win32[];
+extern size_t git_repository__reserved_names_win32_len;
+
+extern git_str git_repository__reserved_names_posix[];
+extern size_t git_repository__reserved_names_posix_len;
+
+/*
+ * Gets any "reserved names" in the repository. This will return paths
+ * that should not be allowed in the repository (like ".git") to avoid
+ * conflicting with the repository path, or with alternate mechanisms to
+ * the repository path (eg, "GIT~1"). Every attempt will be made to look
+ * up all possible reserved names - if there was a conflict for the shortname
+ * GIT~1, for example, this function will try to look up the alternate
+ * shortname. If that fails, this function returns false, but out and outlen
+ * will still be populated with good defaults.
+ */
+bool git_repository__reserved_names(
+ git_str **out, size_t *outlen, git_repository *repo, bool include_ntfs);
+
+int git_repository__shallow_roots(git_oid **out, size_t *out_len, git_repository *repo);
+int git_repository__shallow_roots_write(git_repository *repo, git_oidarray *roots);
+
+/*
+ * The default branch for the repository; the `init.defaultBranch`
+ * configuration option, if set, or `master` if it is not.
+ */
+int git_repository_initialbranch(git_str *out, git_repository *repo);
+
+/*
+ * Given a relative `path`, this makes it absolute based on the
+ * repository's working directory. This will perform validation
+ * to ensure that the path is not longer than MAX_PATH on Windows
+ * (unless `core.longpaths` is set in the repo config).
+ */
+int git_repository_workdir_path(git_str *out, git_repository *repo, const char *path);
+
+int git_repository__extensions(char ***out, size_t *out_len);
+int git_repository__set_extensions(const char **extensions, size_t len);
+void git_repository__free_extensions(void);
+
+/*
+ * Set the object format (OID type) for a repository; this will set
+ * both the configuration and the internal value for the oid type.
+ */
+int git_repository__set_objectformat(
+ git_repository *repo,
+ git_oid_t oid_type);
+
+#endif
diff --git a/src/libgit2/reset.c b/src/libgit2/reset.c
new file mode 100644
index 0000000..605c4af
--- /dev/null
+++ b/src/libgit2/reset.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+
+#include "commit.h"
+#include "tag.h"
+#include "merge.h"
+#include "diff.h"
+#include "annotated_commit.h"
+#include "git2/reset.h"
+#include "git2/checkout.h"
+#include "git2/merge.h"
+#include "git2/refs.h"
+
+#define ERROR_MSG "Cannot perform reset"
+
+int git_reset_default(
+ git_repository *repo,
+ const git_object *target,
+ const git_strarray *pathspecs)
+{
+ git_object *commit = NULL;
+ git_tree *tree = NULL;
+ git_diff *diff = NULL;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ size_t i, max_i;
+ git_index_entry entry;
+ int error;
+ git_index *index = NULL;
+
+ GIT_ASSERT_ARG(pathspecs && pathspecs->count > 0);
+
+ memset(&entry, 0, sizeof(git_index_entry));
+
+ if ((error = git_repository_index(&index, repo)) < 0)
+ goto cleanup;
+
+ if (target) {
+ if (git_object_owner(target) != repo) {
+ git_error_set(GIT_ERROR_OBJECT,
+ "%s_default - The given target does not belong to this repository.", ERROR_MSG);
+ return -1;
+ }
+
+ if ((error = git_object_peel(&commit, target, GIT_OBJECT_COMMIT)) < 0 ||
+ (error = git_commit_tree(&tree, (git_commit *)commit)) < 0)
+ goto cleanup;
+ }
+
+ opts.pathspec = *pathspecs;
+ opts.flags = GIT_DIFF_REVERSE;
+
+ if ((error = git_diff_tree_to_index(
+ &diff, repo, tree, index, &opts)) < 0)
+ goto cleanup;
+
+ for (i = 0, max_i = git_diff_num_deltas(diff); i < max_i; ++i) {
+ const git_diff_delta *delta = git_diff_get_delta(diff, i);
+
+ GIT_ASSERT(delta->status == GIT_DELTA_ADDED ||
+ delta->status == GIT_DELTA_MODIFIED ||
+ delta->status == GIT_DELTA_CONFLICTED ||
+ delta->status == GIT_DELTA_DELETED);
+
+ error = git_index_conflict_remove(index, delta->old_file.path);
+ if (error < 0) {
+ if (delta->status == GIT_DELTA_ADDED && error == GIT_ENOTFOUND)
+ git_error_clear();
+ else
+ goto cleanup;
+ }
+
+ if (delta->status == GIT_DELTA_DELETED) {
+ if ((error = git_index_remove(index, delta->old_file.path, 0)) < 0)
+ goto cleanup;
+ } else {
+ entry.mode = delta->new_file.mode;
+ git_oid_cpy(&entry.id, &delta->new_file.id);
+ entry.path = (char *)delta->new_file.path;
+
+ if ((error = git_index_add(index, &entry)) < 0)
+ goto cleanup;
+ }
+ }
+
+ error = git_index_write(index);
+
+cleanup:
+ git_object_free(commit);
+ git_tree_free(tree);
+ git_index_free(index);
+ git_diff_free(diff);
+
+ return error;
+}
+
+static int reset(
+ git_repository *repo,
+ const git_object *target,
+ const char *to,
+ git_reset_t reset_type,
+ const git_checkout_options *checkout_opts)
+{
+ git_object *commit = NULL;
+ git_index *index = NULL;
+ git_tree *tree = NULL;
+ int error = 0;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_str log_message = GIT_STR_INIT;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(target);
+
+ if (checkout_opts)
+ opts = *checkout_opts;
+
+ if (git_object_owner(target) != repo) {
+ git_error_set(GIT_ERROR_OBJECT,
+ "%s - The given target does not belong to this repository.", ERROR_MSG);
+ return -1;
+ }
+
+ if (reset_type != GIT_RESET_SOFT &&
+ (error = git_repository__ensure_not_bare(repo,
+ reset_type == GIT_RESET_MIXED ? "reset mixed" : "reset hard")) < 0)
+ return error;
+
+ if ((error = git_object_peel(&commit, target, GIT_OBJECT_COMMIT)) < 0 ||
+ (error = git_repository_index(&index, repo)) < 0 ||
+ (error = git_commit_tree(&tree, (git_commit *)commit)) < 0)
+ goto cleanup;
+
+ if (reset_type == GIT_RESET_SOFT &&
+ (git_repository_state(repo) == GIT_REPOSITORY_STATE_MERGE ||
+ git_index_has_conflicts(index)))
+ {
+ git_error_set(GIT_ERROR_OBJECT, "%s (soft) in the middle of a merge", ERROR_MSG);
+ error = GIT_EUNMERGED;
+ goto cleanup;
+ }
+
+ if ((error = git_str_printf(&log_message, "reset: moving to %s", to)) < 0)
+ return error;
+
+ if (reset_type == GIT_RESET_HARD) {
+ /* overwrite working directory with the new tree */
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ if ((error = git_checkout_tree(repo, (git_object *)tree, &opts)) < 0)
+ goto cleanup;
+ }
+
+ /* move HEAD to the new target */
+ if ((error = git_reference__update_terminal(repo, GIT_HEAD_FILE,
+ git_object_id(commit), NULL, git_str_cstr(&log_message))) < 0)
+ goto cleanup;
+
+ if (reset_type > GIT_RESET_SOFT) {
+ /* reset index to the target content */
+
+ if ((error = git_index_read_tree(index, tree)) < 0 ||
+ (error = git_index_write(index)) < 0)
+ goto cleanup;
+
+ if ((error = git_repository_state_cleanup(repo)) < 0) {
+ git_error_set(GIT_ERROR_INDEX, "%s - failed to clean up merge data", ERROR_MSG);
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ git_object_free(commit);
+ git_index_free(index);
+ git_tree_free(tree);
+ git_str_dispose(&log_message);
+
+ return error;
+}
+
+int git_reset(
+ git_repository *repo,
+ const git_object *target,
+ git_reset_t reset_type,
+ const git_checkout_options *checkout_opts)
+{
+ char to[GIT_OID_MAX_HEXSIZE + 1];
+
+ git_oid_tostr(to, GIT_OID_MAX_HEXSIZE + 1, git_object_id(target));
+ return reset(repo, target, to, reset_type, checkout_opts);
+}
+
+int git_reset_from_annotated(
+ git_repository *repo,
+ const git_annotated_commit *commit,
+ git_reset_t reset_type,
+ const git_checkout_options *checkout_opts)
+{
+ return reset(repo, (git_object *) commit->commit, commit->description, reset_type, checkout_opts);
+}
diff --git a/src/libgit2/revert.c b/src/libgit2/revert.c
new file mode 100644
index 0000000..4a31ad4
--- /dev/null
+++ b/src/libgit2/revert.c
@@ -0,0 +1,240 @@
+/*
+* Copyright (C) the libgit2 contributors. All rights reserved.
+*
+* This file is part of libgit2, distributed under the GNU GPL v2 with
+* a Linking Exception. For full terms see the included COPYING file.
+*/
+
+#include "common.h"
+
+#include "repository.h"
+#include "filebuf.h"
+#include "merge.h"
+#include "index.h"
+
+#include "git2/types.h"
+#include "git2/merge.h"
+#include "git2/revert.h"
+#include "git2/commit.h"
+#include "git2/sys/commit.h"
+
+#define GIT_REVERT_FILE_MODE 0666
+
+static int write_revert_head(
+ git_repository *repo,
+ const char *commit_oidstr)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ git_str file_path = GIT_STR_INIT;
+ int error = 0;
+
+ if ((error = git_str_joinpath(&file_path, repo->gitdir, GIT_REVERT_HEAD_FILE)) >= 0 &&
+ (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_CREATE_LEADING_DIRS, GIT_REVERT_FILE_MODE)) >= 0 &&
+ (error = git_filebuf_printf(&file, "%s\n", commit_oidstr)) >= 0)
+ error = git_filebuf_commit(&file);
+
+ if (error < 0)
+ git_filebuf_cleanup(&file);
+
+ git_str_dispose(&file_path);
+
+ return error;
+}
+
+static int write_merge_msg(
+ git_repository *repo,
+ const char *commit_oidstr,
+ const char *commit_msgline)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ git_str file_path = GIT_STR_INIT;
+ int error = 0;
+
+ if ((error = git_str_joinpath(&file_path, repo->gitdir, GIT_MERGE_MSG_FILE)) < 0 ||
+ (error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_CREATE_LEADING_DIRS, GIT_REVERT_FILE_MODE)) < 0 ||
+ (error = git_filebuf_printf(&file, "Revert \"%s\"\n\nThis reverts commit %s.\n",
+ commit_msgline, commit_oidstr)) < 0)
+ goto cleanup;
+
+ error = git_filebuf_commit(&file);
+
+cleanup:
+ if (error < 0)
+ git_filebuf_cleanup(&file);
+
+ git_str_dispose(&file_path);
+
+ return error;
+}
+
+static int revert_normalize_opts(
+ git_repository *repo,
+ git_revert_options *opts,
+ const git_revert_options *given,
+ const char *their_label)
+{
+ int error = 0;
+ unsigned int default_checkout_strategy = GIT_CHECKOUT_SAFE |
+ GIT_CHECKOUT_ALLOW_CONFLICTS;
+
+ GIT_UNUSED(repo);
+
+ if (given != NULL)
+ memcpy(opts, given, sizeof(git_revert_options));
+ else {
+ git_revert_options default_opts = GIT_REVERT_OPTIONS_INIT;
+ memcpy(opts, &default_opts, sizeof(git_revert_options));
+ }
+
+ if (!opts->checkout_opts.checkout_strategy)
+ opts->checkout_opts.checkout_strategy = default_checkout_strategy;
+
+ if (!opts->checkout_opts.our_label)
+ opts->checkout_opts.our_label = "HEAD";
+
+ if (!opts->checkout_opts.their_label)
+ opts->checkout_opts.their_label = their_label;
+
+ return error;
+}
+
+static int revert_state_cleanup(git_repository *repo)
+{
+ const char *state_files[] = { GIT_REVERT_HEAD_FILE, GIT_MERGE_MSG_FILE };
+
+ return git_repository__cleanup_files(repo, state_files, ARRAY_SIZE(state_files));
+}
+
+static int revert_seterr(git_commit *commit, const char *fmt)
+{
+ char commit_id[GIT_OID_MAX_HEXSIZE + 1];
+
+ git_oid_tostr(commit_id, GIT_OID_MAX_HEXSIZE + 1, git_commit_id(commit));
+ git_error_set(GIT_ERROR_REVERT, fmt, commit_id);
+
+ return -1;
+}
+
+int git_revert_commit(
+ git_index **out,
+ git_repository *repo,
+ git_commit *revert_commit,
+ git_commit *our_commit,
+ unsigned int mainline,
+ const git_merge_options *merge_opts)
+{
+ git_commit *parent_commit = NULL;
+ git_tree *parent_tree = NULL, *our_tree = NULL, *revert_tree = NULL;
+ int parent = 0, error = 0;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(revert_commit);
+ GIT_ASSERT_ARG(our_commit);
+
+ if (git_commit_parentcount(revert_commit) > 1) {
+ if (!mainline)
+ return revert_seterr(revert_commit,
+ "mainline branch is not specified but %s is a merge commit");
+
+ parent = mainline;
+ } else {
+ if (mainline)
+ return revert_seterr(revert_commit,
+ "mainline branch specified but %s is not a merge commit");
+
+ parent = git_commit_parentcount(revert_commit);
+ }
+
+ if (parent &&
+ ((error = git_commit_parent(&parent_commit, revert_commit, (parent - 1))) < 0 ||
+ (error = git_commit_tree(&parent_tree, parent_commit)) < 0))
+ goto done;
+
+ if ((error = git_commit_tree(&revert_tree, revert_commit)) < 0 ||
+ (error = git_commit_tree(&our_tree, our_commit)) < 0)
+ goto done;
+
+ error = git_merge_trees(out, repo, revert_tree, our_tree, parent_tree, merge_opts);
+
+done:
+ git_tree_free(parent_tree);
+ git_tree_free(our_tree);
+ git_tree_free(revert_tree);
+ git_commit_free(parent_commit);
+
+ return error;
+}
+
+int git_revert(
+ git_repository *repo,
+ git_commit *commit,
+ const git_revert_options *given_opts)
+{
+ git_revert_options opts;
+ git_reference *our_ref = NULL;
+ git_commit *our_commit = NULL;
+ char commit_id[GIT_OID_MAX_HEXSIZE + 1];
+ const char *commit_msg;
+ git_str their_label = GIT_STR_INIT;
+ git_index *index = NULL;
+ git_indexwriter indexwriter = GIT_INDEXWRITER_INIT;
+ int error;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(commit);
+
+ GIT_ERROR_CHECK_VERSION(given_opts, GIT_REVERT_OPTIONS_VERSION, "git_revert_options");
+
+ if ((error = git_repository__ensure_not_bare(repo, "revert")) < 0)
+ return error;
+
+ git_oid_tostr(commit_id, GIT_OID_MAX_HEXSIZE + 1, git_commit_id(commit));
+
+ if ((commit_msg = git_commit_summary(commit)) == NULL) {
+ error = -1;
+ goto on_error;
+ }
+
+ if ((error = git_str_printf(&their_label, "parent of %.7s... %s", commit_id, commit_msg)) < 0 ||
+ (error = revert_normalize_opts(repo, &opts, given_opts, git_str_cstr(&their_label))) < 0 ||
+ (error = git_indexwriter_init_for_operation(&indexwriter, repo, &opts.checkout_opts.checkout_strategy)) < 0 ||
+ (error = write_revert_head(repo, commit_id)) < 0 ||
+ (error = write_merge_msg(repo, commit_id, commit_msg)) < 0 ||
+ (error = git_repository_head(&our_ref, repo)) < 0 ||
+ (error = git_reference_peel((git_object **)&our_commit, our_ref, GIT_OBJECT_COMMIT)) < 0 ||
+ (error = git_revert_commit(&index, repo, commit, our_commit, opts.mainline, &opts.merge_opts)) < 0 ||
+ (error = git_merge__check_result(repo, index)) < 0 ||
+ (error = git_merge__append_conflicts_to_merge_msg(repo, index)) < 0 ||
+ (error = git_checkout_index(repo, index, &opts.checkout_opts)) < 0 ||
+ (error = git_indexwriter_commit(&indexwriter)) < 0)
+ goto on_error;
+
+ goto done;
+
+on_error:
+ revert_state_cleanup(repo);
+
+done:
+ git_indexwriter_cleanup(&indexwriter);
+ git_index_free(index);
+ git_commit_free(our_commit);
+ git_reference_free(our_ref);
+ git_str_dispose(&their_label);
+
+ return error;
+}
+
+int git_revert_options_init(git_revert_options *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_revert_options, GIT_REVERT_OPTIONS_INIT);
+ return 0;
+}
+
+#ifndef GIT_DEPRECATE_HARD
+int git_revert_init_options(git_revert_options *opts, unsigned int version)
+{
+ return git_revert_options_init(opts, version);
+}
+#endif
diff --git a/src/libgit2/revparse.c b/src/libgit2/revparse.c
new file mode 100644
index 0000000..06d92f8
--- /dev/null
+++ b/src/libgit2/revparse.c
@@ -0,0 +1,968 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+
+#include "str.h"
+#include "tree.h"
+#include "refdb.h"
+#include "regexp.h"
+#include "date.h"
+
+#include "git2.h"
+
+static int maybe_sha_or_abbrev(
+ git_object **out,
+ git_repository *repo,
+ const char *spec,
+ size_t speclen)
+{
+ git_oid oid;
+
+ if (git_oid__fromstrn(&oid, spec, speclen, repo->oid_type) < 0)
+ return GIT_ENOTFOUND;
+
+ return git_object_lookup_prefix(out, repo, &oid, speclen, GIT_OBJECT_ANY);
+}
+
+static int maybe_sha(
+ git_object **out,
+ git_repository *repo,
+ const char *spec)
+{
+ size_t speclen = strlen(spec);
+
+ if (speclen != git_oid_hexsize(repo->oid_type))
+ return GIT_ENOTFOUND;
+
+ return maybe_sha_or_abbrev(out, repo, spec, speclen);
+}
+
+static int maybe_abbrev(git_object **out, git_repository *repo, const char *spec)
+{
+ size_t speclen = strlen(spec);
+
+ return maybe_sha_or_abbrev(out, repo, spec, speclen);
+}
+
+static int build_regex(git_regexp *regex, const char *pattern)
+{
+ int error;
+
+ if (*pattern == '\0') {
+ git_error_set(GIT_ERROR_REGEX, "empty pattern");
+ return GIT_EINVALIDSPEC;
+ }
+
+ error = git_regexp_compile(regex, pattern, 0);
+ if (!error)
+ return 0;
+
+ git_regexp_dispose(regex);
+
+ return error;
+}
+
+static int maybe_describe(git_object**out, git_repository *repo, const char *spec)
+{
+ const char *substr;
+ int error;
+ git_regexp regex;
+
+ substr = strstr(spec, "-g");
+
+ if (substr == NULL)
+ return GIT_ENOTFOUND;
+
+ if (build_regex(&regex, ".+-[0-9]+-g[0-9a-fA-F]+") < 0)
+ return -1;
+
+ error = git_regexp_match(&regex, spec);
+ git_regexp_dispose(&regex);
+
+ if (error)
+ return GIT_ENOTFOUND;
+
+ return maybe_abbrev(out, repo, substr+2);
+}
+
+static int revparse_lookup_object(
+ git_object **object_out,
+ git_reference **reference_out,
+ git_repository *repo,
+ const char *spec)
+{
+ int error;
+ git_reference *ref;
+
+ if ((error = maybe_sha(object_out, repo, spec)) != GIT_ENOTFOUND)
+ return error;
+
+ error = git_reference_dwim(&ref, repo, spec);
+ if (!error) {
+
+ error = git_object_lookup(
+ object_out, repo, git_reference_target(ref), GIT_OBJECT_ANY);
+
+ if (!error)
+ *reference_out = ref;
+
+ return error;
+ }
+
+ if (error != GIT_ENOTFOUND)
+ return error;
+
+ if ((strlen(spec) < git_oid_hexsize(repo->oid_type)) &&
+ ((error = maybe_abbrev(object_out, repo, spec)) != GIT_ENOTFOUND))
+ return error;
+
+ if ((error = maybe_describe(object_out, repo, spec)) != GIT_ENOTFOUND)
+ return error;
+
+ git_error_set(GIT_ERROR_REFERENCE, "revspec '%s' not found", spec);
+ return GIT_ENOTFOUND;
+}
+
+static int try_parse_numeric(int *n, const char *curly_braces_content)
+{
+ int32_t content;
+ const char *end_ptr;
+
+ if (git__strntol32(&content, curly_braces_content, strlen(curly_braces_content),
+ &end_ptr, 10) < 0)
+ return -1;
+
+ if (*end_ptr != '\0')
+ return -1;
+
+ *n = (int)content;
+ return 0;
+}
+
+static int retrieve_previously_checked_out_branch_or_revision(git_object **out, git_reference **base_ref, git_repository *repo, const char *identifier, size_t position)
+{
+ git_reference *ref = NULL;
+ git_reflog *reflog = NULL;
+ git_regexp preg;
+ int error = -1;
+ size_t i, numentries, cur;
+ const git_reflog_entry *entry;
+ const char *msg;
+ git_str buf = GIT_STR_INIT;
+
+ cur = position;
+
+ if (*identifier != '\0' || *base_ref != NULL)
+ return GIT_EINVALIDSPEC;
+
+ if (build_regex(&preg, "checkout: moving from (.*) to .*") < 0)
+ return -1;
+
+ if (git_reference_lookup(&ref, repo, GIT_HEAD_FILE) < 0)
+ goto cleanup;
+
+ if (git_reflog_read(&reflog, repo, GIT_HEAD_FILE) < 0)
+ goto cleanup;
+
+ numentries = git_reflog_entrycount(reflog);
+
+ for (i = 0; i < numentries; i++) {
+ git_regmatch regexmatches[2];
+
+ entry = git_reflog_entry_byindex(reflog, i);
+ msg = git_reflog_entry_message(entry);
+ if (!msg)
+ continue;
+
+ if (git_regexp_search(&preg, msg, 2, regexmatches) < 0)
+ continue;
+
+ cur--;
+
+ if (cur > 0)
+ continue;
+
+ if ((git_str_put(&buf, msg+regexmatches[1].start, regexmatches[1].end - regexmatches[1].start)) < 0)
+ goto cleanup;
+
+ if ((error = git_reference_dwim(base_ref, repo, git_str_cstr(&buf))) == 0)
+ goto cleanup;
+
+ if (error < 0 && error != GIT_ENOTFOUND)
+ goto cleanup;
+
+ error = maybe_abbrev(out, repo, git_str_cstr(&buf));
+
+ goto cleanup;
+ }
+
+ error = GIT_ENOTFOUND;
+
+cleanup:
+ git_reference_free(ref);
+ git_str_dispose(&buf);
+ git_regexp_dispose(&preg);
+ git_reflog_free(reflog);
+ return error;
+}
+
+static int retrieve_oid_from_reflog(git_oid *oid, git_reference *ref, size_t identifier)
+{
+ git_reflog *reflog;
+ size_t numentries;
+ const git_reflog_entry *entry = NULL;
+ bool search_by_pos = (identifier <= 100000000);
+
+ if (git_reflog_read(&reflog, git_reference_owner(ref), git_reference_name(ref)) < 0)
+ return -1;
+
+ numentries = git_reflog_entrycount(reflog);
+
+ if (search_by_pos) {
+ if (numentries < identifier + 1)
+ goto notfound;
+
+ entry = git_reflog_entry_byindex(reflog, identifier);
+ git_oid_cpy(oid, git_reflog_entry_id_new(entry));
+ } else {
+ size_t i;
+ git_time commit_time;
+
+ for (i = 0; i < numentries; i++) {
+ entry = git_reflog_entry_byindex(reflog, i);
+ commit_time = git_reflog_entry_committer(entry)->when;
+
+ if (commit_time.time > (git_time_t)identifier)
+ continue;
+
+ git_oid_cpy(oid, git_reflog_entry_id_new(entry));
+ break;
+ }
+
+ if (i == numentries) {
+ if (entry == NULL)
+ goto notfound;
+
+ /*
+ * TODO: emit a warning (log for 'branch' only goes back to ...)
+ */
+ git_oid_cpy(oid, git_reflog_entry_id_new(entry));
+ }
+ }
+
+ git_reflog_free(reflog);
+ return 0;
+
+notfound:
+ git_error_set(
+ GIT_ERROR_REFERENCE,
+ "reflog for '%s' has only %"PRIuZ" entries, asked for %"PRIuZ,
+ git_reference_name(ref), numentries, identifier);
+
+ git_reflog_free(reflog);
+ return GIT_ENOTFOUND;
+}
+
+static int retrieve_revobject_from_reflog(git_object **out, git_reference **base_ref, git_repository *repo, const char *identifier, size_t position)
+{
+ git_reference *ref;
+ git_oid oid;
+ int error = -1;
+
+ if (*base_ref == NULL) {
+ /*
+ * When HEAD@{n} is specified, do not use dwim, which would resolve the
+ * reference (to the current branch that HEAD is pointing to).
+ */
+ if (position > 0 && strcmp(identifier, GIT_HEAD_FILE) == 0)
+ error = git_reference_lookup(&ref, repo, GIT_HEAD_FILE);
+ else
+ error = git_reference_dwim(&ref, repo, identifier);
+
+ if (error < 0)
+ return error;
+ } else {
+ ref = *base_ref;
+ *base_ref = NULL;
+ }
+
+ if (position == 0) {
+ error = git_object_lookup(out, repo, git_reference_target(ref), GIT_OBJECT_ANY);
+ goto cleanup;
+ }
+
+ if ((error = retrieve_oid_from_reflog(&oid, ref, position)) < 0)
+ goto cleanup;
+
+ error = git_object_lookup(out, repo, &oid, GIT_OBJECT_ANY);
+
+cleanup:
+ git_reference_free(ref);
+ return error;
+}
+
+static int retrieve_remote_tracking_reference(git_reference **base_ref, const char *identifier, git_repository *repo)
+{
+ git_reference *tracking, *ref;
+ int error = -1;
+
+ if (*base_ref == NULL) {
+ if ((error = git_reference_dwim(&ref, repo, identifier)) < 0)
+ return error;
+ } else {
+ ref = *base_ref;
+ *base_ref = NULL;
+ }
+
+ if (!git_reference_is_branch(ref)) {
+ error = GIT_EINVALIDSPEC;
+ goto cleanup;
+ }
+
+ if ((error = git_branch_upstream(&tracking, ref)) < 0)
+ goto cleanup;
+
+ *base_ref = tracking;
+
+cleanup:
+ git_reference_free(ref);
+ return error;
+}
+
+static int handle_at_syntax(git_object **out, git_reference **ref, const char *spec, size_t identifier_len, git_repository *repo, const char *curly_braces_content)
+{
+ bool is_numeric;
+ int parsed = 0, error = -1;
+ git_str identifier = GIT_STR_INIT;
+ git_time_t timestamp;
+
+ GIT_ASSERT(*out == NULL);
+
+ if (git_str_put(&identifier, spec, identifier_len) < 0)
+ return -1;
+
+ is_numeric = !try_parse_numeric(&parsed, curly_braces_content);
+
+ if (*curly_braces_content == '-' && (!is_numeric || parsed == 0)) {
+ error = GIT_EINVALIDSPEC;
+ goto cleanup;
+ }
+
+ if (is_numeric) {
+ if (parsed < 0)
+ error = retrieve_previously_checked_out_branch_or_revision(out, ref, repo, git_str_cstr(&identifier), -parsed);
+ else
+ error = retrieve_revobject_from_reflog(out, ref, repo, git_str_cstr(&identifier), parsed);
+
+ goto cleanup;
+ }
+
+ if (!strcmp(curly_braces_content, "u") || !strcmp(curly_braces_content, "upstream")) {
+ error = retrieve_remote_tracking_reference(ref, git_str_cstr(&identifier), repo);
+
+ goto cleanup;
+ }
+
+ if (git_date_parse(&timestamp, curly_braces_content) < 0) {
+ error = GIT_EINVALIDSPEC;
+ goto cleanup;
+ }
+
+ error = retrieve_revobject_from_reflog(out, ref, repo, git_str_cstr(&identifier), (size_t)timestamp);
+
+cleanup:
+ git_str_dispose(&identifier);
+ return error;
+}
+
+static git_object_t parse_obj_type(const char *str)
+{
+ if (!strcmp(str, "commit"))
+ return GIT_OBJECT_COMMIT;
+
+ if (!strcmp(str, "tree"))
+ return GIT_OBJECT_TREE;
+
+ if (!strcmp(str, "blob"))
+ return GIT_OBJECT_BLOB;
+
+ if (!strcmp(str, "tag"))
+ return GIT_OBJECT_TAG;
+
+ return GIT_OBJECT_INVALID;
+}
+
+static int dereference_to_non_tag(git_object **out, git_object *obj)
+{
+ if (git_object_type(obj) == GIT_OBJECT_TAG)
+ return git_tag_peel(out, (git_tag *)obj);
+
+ return git_object_dup(out, obj);
+}
+
+static int handle_caret_parent_syntax(git_object **out, git_object *obj, int n)
+{
+ git_object *temp_commit = NULL;
+ int error;
+
+ if ((error = git_object_peel(&temp_commit, obj, GIT_OBJECT_COMMIT)) < 0)
+ return (error == GIT_EAMBIGUOUS || error == GIT_ENOTFOUND) ?
+ GIT_EINVALIDSPEC : error;
+
+ if (n == 0) {
+ *out = temp_commit;
+ return 0;
+ }
+
+ error = git_commit_parent((git_commit **)out, (git_commit*)temp_commit, n - 1);
+
+ git_object_free(temp_commit);
+ return error;
+}
+
+static int handle_linear_syntax(git_object **out, git_object *obj, int n)
+{
+ git_object *temp_commit = NULL;
+ int error;
+
+ if ((error = git_object_peel(&temp_commit, obj, GIT_OBJECT_COMMIT)) < 0)
+ return (error == GIT_EAMBIGUOUS || error == GIT_ENOTFOUND) ?
+ GIT_EINVALIDSPEC : error;
+
+ error = git_commit_nth_gen_ancestor((git_commit **)out, (git_commit*)temp_commit, n);
+
+ git_object_free(temp_commit);
+ return error;
+}
+
+static int handle_colon_syntax(
+ git_object **out,
+ git_object *obj,
+ const char *path)
+{
+ git_object *tree;
+ int error = -1;
+ git_tree_entry *entry = NULL;
+
+ if ((error = git_object_peel(&tree, obj, GIT_OBJECT_TREE)) < 0)
+ return error == GIT_ENOTFOUND ? GIT_EINVALIDSPEC : error;
+
+ if (*path == '\0') {
+ *out = tree;
+ return 0;
+ }
+
+ /*
+ * TODO: Handle the relative path syntax
+ * (:./relative/path and :../relative/path)
+ */
+ if ((error = git_tree_entry_bypath(&entry, (git_tree *)tree, path)) < 0)
+ goto cleanup;
+
+ error = git_tree_entry_to_object(out, git_object_owner(tree), entry);
+
+cleanup:
+ git_tree_entry_free(entry);
+ git_object_free(tree);
+
+ return error;
+}
+
+static int walk_and_search(git_object **out, git_revwalk *walk, git_regexp *regex)
+{
+ int error;
+ git_oid oid;
+ git_object *obj;
+
+ while (!(error = git_revwalk_next(&oid, walk))) {
+
+ error = git_object_lookup(&obj, git_revwalk_repository(walk), &oid, GIT_OBJECT_COMMIT);
+ if ((error < 0) && (error != GIT_ENOTFOUND))
+ return -1;
+
+ if (!git_regexp_match(regex, git_commit_message((git_commit*)obj))) {
+ *out = obj;
+ return 0;
+ }
+
+ git_object_free(obj);
+ }
+
+ if (error < 0 && error == GIT_ITEROVER)
+ error = GIT_ENOTFOUND;
+
+ return error;
+}
+
+static int handle_grep_syntax(git_object **out, git_repository *repo, const git_oid *spec_oid, const char *pattern)
+{
+ git_regexp preg;
+ git_revwalk *walk = NULL;
+ int error;
+
+ if ((error = build_regex(&preg, pattern)) < 0)
+ return error;
+
+ if ((error = git_revwalk_new(&walk, repo)) < 0)
+ goto cleanup;
+
+ git_revwalk_sorting(walk, GIT_SORT_TIME);
+
+ if (spec_oid == NULL) {
+ if ((error = git_revwalk_push_glob(walk, "refs/*")) < 0)
+ goto cleanup;
+ } else if ((error = git_revwalk_push(walk, spec_oid)) < 0)
+ goto cleanup;
+
+ error = walk_and_search(out, walk, &preg);
+
+cleanup:
+ git_regexp_dispose(&preg);
+ git_revwalk_free(walk);
+
+ return error;
+}
+
+static int handle_caret_curly_syntax(git_object **out, git_object *obj, const char *curly_braces_content)
+{
+ git_object_t expected_type;
+
+ if (*curly_braces_content == '\0')
+ return dereference_to_non_tag(out, obj);
+
+ if (*curly_braces_content == '/')
+ return handle_grep_syntax(out, git_object_owner(obj), git_object_id(obj), curly_braces_content + 1);
+
+ expected_type = parse_obj_type(curly_braces_content);
+
+ if (expected_type == GIT_OBJECT_INVALID)
+ return GIT_EINVALIDSPEC;
+
+ return git_object_peel(out, obj, expected_type);
+}
+
+static int extract_curly_braces_content(git_str *buf, const char *spec, size_t *pos)
+{
+ git_str_clear(buf);
+
+ GIT_ASSERT_ARG(spec[*pos] == '^' || spec[*pos] == '@');
+
+ (*pos)++;
+
+ if (spec[*pos] == '\0' || spec[*pos] != '{')
+ return GIT_EINVALIDSPEC;
+
+ (*pos)++;
+
+ while (spec[*pos] != '}') {
+ if (spec[*pos] == '\0')
+ return GIT_EINVALIDSPEC;
+
+ if (git_str_putc(buf, spec[(*pos)++]) < 0)
+ return -1;
+ }
+
+ (*pos)++;
+
+ return 0;
+}
+
+static int extract_path(git_str *buf, const char *spec, size_t *pos)
+{
+ git_str_clear(buf);
+
+ GIT_ASSERT_ARG(spec[*pos] == ':');
+
+ (*pos)++;
+
+ if (git_str_puts(buf, spec + *pos) < 0)
+ return -1;
+
+ *pos += git_str_len(buf);
+
+ return 0;
+}
+
+static int extract_how_many(int *n, const char *spec, size_t *pos)
+{
+ const char *end_ptr;
+ int parsed, accumulated;
+ char kind = spec[*pos];
+
+ GIT_ASSERT_ARG(spec[*pos] == '^' || spec[*pos] == '~');
+
+ accumulated = 0;
+
+ do {
+ do {
+ (*pos)++;
+ accumulated++;
+ } while (spec[(*pos)] == kind && kind == '~');
+
+ if (git__isdigit(spec[*pos])) {
+ if (git__strntol32(&parsed, spec + *pos, strlen(spec + *pos), &end_ptr, 10) < 0)
+ return GIT_EINVALIDSPEC;
+
+ accumulated += (parsed - 1);
+ *pos = end_ptr - spec;
+ }
+
+ } while (spec[(*pos)] == kind && kind == '~');
+
+ *n = accumulated;
+
+ return 0;
+}
+
+static int object_from_reference(git_object **object, git_reference *reference)
+{
+ git_reference *resolved = NULL;
+ int error;
+
+ if (git_reference_resolve(&resolved, reference) < 0)
+ return -1;
+
+ error = git_object_lookup(object, reference->db->repo, git_reference_target(resolved), GIT_OBJECT_ANY);
+ git_reference_free(resolved);
+
+ return error;
+}
+
+static int ensure_base_rev_loaded(git_object **object, git_reference **reference, const char *spec, size_t identifier_len, git_repository *repo, bool allow_empty_identifier)
+{
+ int error;
+ git_str identifier = GIT_STR_INIT;
+
+ if (*object != NULL)
+ return 0;
+
+ if (*reference != NULL)
+ return object_from_reference(object, *reference);
+
+ if (!allow_empty_identifier && identifier_len == 0)
+ return GIT_EINVALIDSPEC;
+
+ if (git_str_put(&identifier, spec, identifier_len) < 0)
+ return -1;
+
+ error = revparse_lookup_object(object, reference, repo, git_str_cstr(&identifier));
+ git_str_dispose(&identifier);
+
+ return error;
+}
+
+static int ensure_base_rev_is_not_known_yet(git_object *object)
+{
+ if (object == NULL)
+ return 0;
+
+ return GIT_EINVALIDSPEC;
+}
+
+static bool any_left_hand_identifier(git_object *object, git_reference *reference, size_t identifier_len)
+{
+ if (object != NULL)
+ return true;
+
+ if (reference != NULL)
+ return true;
+
+ if (identifier_len > 0)
+ return true;
+
+ return false;
+}
+
+static int ensure_left_hand_identifier_is_not_known_yet(git_object *object, git_reference *reference)
+{
+ if (!ensure_base_rev_is_not_known_yet(object) && reference == NULL)
+ return 0;
+
+ return GIT_EINVALIDSPEC;
+}
+
+static int revparse(
+ git_object **object_out,
+ git_reference **reference_out,
+ size_t *identifier_len_out,
+ git_repository *repo,
+ const char *spec)
+{
+ size_t pos = 0, identifier_len = 0;
+ int error = -1, n;
+ git_str buf = GIT_STR_INIT;
+
+ git_reference *reference = NULL;
+ git_object *base_rev = NULL;
+
+ bool should_return_reference = true;
+ bool parsed = false;
+
+ GIT_ASSERT_ARG(object_out);
+ GIT_ASSERT_ARG(reference_out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(spec);
+
+ *object_out = NULL;
+ *reference_out = NULL;
+
+ while (!parsed && spec[pos]) {
+ switch (spec[pos]) {
+ case '^':
+ should_return_reference = false;
+
+ if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0)
+ goto cleanup;
+
+ if (spec[pos+1] == '{') {
+ git_object *temp_object = NULL;
+
+ if ((error = extract_curly_braces_content(&buf, spec, &pos)) < 0)
+ goto cleanup;
+
+ if ((error = handle_caret_curly_syntax(&temp_object, base_rev, git_str_cstr(&buf))) < 0)
+ goto cleanup;
+
+ git_object_free(base_rev);
+ base_rev = temp_object;
+ } else {
+ git_object *temp_object = NULL;
+
+ if ((error = extract_how_many(&n, spec, &pos)) < 0)
+ goto cleanup;
+
+ if ((error = handle_caret_parent_syntax(&temp_object, base_rev, n)) < 0)
+ goto cleanup;
+
+ git_object_free(base_rev);
+ base_rev = temp_object;
+ }
+ break;
+
+ case '~':
+ {
+ git_object *temp_object = NULL;
+
+ should_return_reference = false;
+
+ if ((error = extract_how_many(&n, spec, &pos)) < 0)
+ goto cleanup;
+
+ if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0)
+ goto cleanup;
+
+ if ((error = handle_linear_syntax(&temp_object, base_rev, n)) < 0)
+ goto cleanup;
+
+ git_object_free(base_rev);
+ base_rev = temp_object;
+ break;
+ }
+
+ case ':':
+ {
+ git_object *temp_object = NULL;
+
+ should_return_reference = false;
+
+ if ((error = extract_path(&buf, spec, &pos)) < 0)
+ goto cleanup;
+
+ if (any_left_hand_identifier(base_rev, reference, identifier_len)) {
+ if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, true)) < 0)
+ goto cleanup;
+
+ if ((error = handle_colon_syntax(&temp_object, base_rev, git_str_cstr(&buf))) < 0)
+ goto cleanup;
+ } else {
+ if (*git_str_cstr(&buf) == '/') {
+ if ((error = handle_grep_syntax(&temp_object, repo, NULL, git_str_cstr(&buf) + 1)) < 0)
+ goto cleanup;
+ } else {
+
+ /*
+ * TODO: support merge-stage path lookup (":2:Makefile")
+ * and plain index blob lookup (:i-am/a/blob)
+ */
+ git_error_set(GIT_ERROR_INVALID, "unimplemented");
+ error = GIT_ERROR;
+ goto cleanup;
+ }
+ }
+
+ git_object_free(base_rev);
+ base_rev = temp_object;
+ break;
+ }
+
+ case '@':
+ if (spec[pos+1] == '{') {
+ git_object *temp_object = NULL;
+
+ if ((error = extract_curly_braces_content(&buf, spec, &pos)) < 0)
+ goto cleanup;
+
+ if ((error = ensure_base_rev_is_not_known_yet(base_rev)) < 0)
+ goto cleanup;
+
+ if ((error = handle_at_syntax(&temp_object, &reference, spec, identifier_len, repo, git_str_cstr(&buf))) < 0)
+ goto cleanup;
+
+ if (temp_object != NULL)
+ base_rev = temp_object;
+ break;
+ } else if (spec[pos+1] == '\0') {
+ spec = "HEAD";
+ identifier_len = 4;
+ parsed = true;
+ break;
+ }
+ /* fall through */
+
+ default:
+ if ((error = ensure_left_hand_identifier_is_not_known_yet(base_rev, reference)) < 0)
+ goto cleanup;
+
+ pos++;
+ identifier_len++;
+ }
+ }
+
+ if ((error = ensure_base_rev_loaded(&base_rev, &reference, spec, identifier_len, repo, false)) < 0)
+ goto cleanup;
+
+ if (!should_return_reference) {
+ git_reference_free(reference);
+ reference = NULL;
+ }
+
+ *object_out = base_rev;
+ *reference_out = reference;
+ *identifier_len_out = identifier_len;
+ error = 0;
+
+cleanup:
+ if (error) {
+ if (error == GIT_EINVALIDSPEC)
+ git_error_set(GIT_ERROR_INVALID,
+ "failed to parse revision specifier - Invalid pattern '%s'", spec);
+
+ git_object_free(base_rev);
+ git_reference_free(reference);
+ }
+
+ git_str_dispose(&buf);
+ return error;
+}
+
+int git_revparse_ext(
+ git_object **object_out,
+ git_reference **reference_out,
+ git_repository *repo,
+ const char *spec)
+{
+ int error;
+ size_t identifier_len;
+ git_object *obj = NULL;
+ git_reference *ref = NULL;
+
+ if ((error = revparse(&obj, &ref, &identifier_len, repo, spec)) < 0)
+ goto cleanup;
+
+ *object_out = obj;
+ *reference_out = ref;
+ GIT_UNUSED(identifier_len);
+
+ return 0;
+
+cleanup:
+ git_object_free(obj);
+ git_reference_free(ref);
+ return error;
+}
+
+int git_revparse_single(git_object **out, git_repository *repo, const char *spec)
+{
+ int error;
+ git_object *obj = NULL;
+ git_reference *ref = NULL;
+
+ *out = NULL;
+
+ if ((error = git_revparse_ext(&obj, &ref, repo, spec)) < 0)
+ goto cleanup;
+
+ git_reference_free(ref);
+
+ *out = obj;
+
+ return 0;
+
+cleanup:
+ git_object_free(obj);
+ git_reference_free(ref);
+ return error;
+}
+
+int git_revparse(
+ git_revspec *revspec,
+ git_repository *repo,
+ const char *spec)
+{
+ const char *dotdot;
+ int error = 0;
+
+ GIT_ASSERT_ARG(revspec);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(spec);
+
+ memset(revspec, 0x0, sizeof(*revspec));
+
+ if ((dotdot = strstr(spec, "..")) != NULL) {
+ char *lstr;
+ const char *rstr;
+ revspec->flags = GIT_REVSPEC_RANGE;
+
+ /*
+ * Following git.git, don't allow '..' because it makes command line
+ * arguments which can be either paths or revisions ambiguous when the
+ * path is almost certainly intended. The empty range '...' is still
+ * allowed.
+ */
+ if (!git__strcmp(spec, "..")) {
+ git_error_set(GIT_ERROR_INVALID, "Invalid pattern '..'");
+ return GIT_EINVALIDSPEC;
+ }
+
+ lstr = git__substrdup(spec, dotdot - spec);
+ rstr = dotdot + 2;
+ if (dotdot[2] == '.') {
+ revspec->flags |= GIT_REVSPEC_MERGE_BASE;
+ rstr++;
+ }
+
+ error = git_revparse_single(
+ &revspec->from,
+ repo,
+ *lstr == '\0' ? "HEAD" : lstr);
+
+ if (!error) {
+ error = git_revparse_single(
+ &revspec->to,
+ repo,
+ *rstr == '\0' ? "HEAD" : rstr);
+ }
+
+ git__free((void*)lstr);
+ } else {
+ revspec->flags = GIT_REVSPEC_SINGLE;
+ error = git_revparse_single(&revspec->from, repo, spec);
+ }
+
+ return error;
+}
diff --git a/src/libgit2/revwalk.c b/src/libgit2/revwalk.c
new file mode 100644
index 0000000..4ea6fae
--- /dev/null
+++ b/src/libgit2/revwalk.c
@@ -0,0 +1,846 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "revwalk.h"
+
+#include "commit.h"
+#include "odb.h"
+#include "pool.h"
+
+#include "git2/revparse.h"
+#include "merge.h"
+#include "vector.h"
+
+static int get_revision(git_commit_list_node **out, git_revwalk *walk, git_commit_list **list);
+
+git_commit_list_node *git_revwalk__commit_lookup(
+ git_revwalk *walk, const git_oid *oid)
+{
+ git_commit_list_node *commit;
+
+ /* lookup and reserve space if not already present */
+ if ((commit = git_oidmap_get(walk->commits, oid)) != NULL)
+ return commit;
+
+ commit = git_commit_list_alloc_node(walk);
+ if (commit == NULL)
+ return NULL;
+
+ git_oid_cpy(&commit->oid, oid);
+
+ if ((git_oidmap_set(walk->commits, &commit->oid, commit)) < 0)
+ return NULL;
+
+ return commit;
+}
+
+int git_revwalk__push_commit(git_revwalk *walk, const git_oid *oid, const git_revwalk__push_options *opts)
+{
+ git_oid commit_id;
+ int error;
+ git_object *obj, *oobj;
+ git_commit_list_node *commit;
+ git_commit_list *list;
+
+ if ((error = git_object_lookup(&oobj, walk->repo, oid, GIT_OBJECT_ANY)) < 0)
+ return error;
+
+ error = git_object_peel(&obj, oobj, GIT_OBJECT_COMMIT);
+ git_object_free(oobj);
+
+ if (error == GIT_ENOTFOUND || error == GIT_EINVALIDSPEC || error == GIT_EPEEL) {
+ /* If this comes from e.g. push_glob("tags"), ignore this */
+ if (opts->from_glob)
+ return 0;
+
+ git_error_set(GIT_ERROR_INVALID, "object is not a committish");
+ return error;
+ }
+ if (error < 0)
+ return error;
+
+ git_oid_cpy(&commit_id, git_object_id(obj));
+ git_object_free(obj);
+
+ commit = git_revwalk__commit_lookup(walk, &commit_id);
+ if (commit == NULL)
+ return -1; /* error already reported by failed lookup */
+
+ /* A previous hide already told us we don't want this commit */
+ if (commit->uninteresting)
+ return 0;
+
+ if (opts->uninteresting) {
+ walk->limited = 1;
+ walk->did_hide = 1;
+ } else {
+ walk->did_push = 1;
+ }
+
+ commit->uninteresting = opts->uninteresting;
+ list = walk->user_input;
+
+ /* To insert by date, we need to parse so we know the date. */
+ if (opts->insert_by_date && ((error = git_commit_list_parse(walk, commit)) < 0))
+ return error;
+
+ if ((opts->insert_by_date == 0 ||
+ git_commit_list_insert_by_date(commit, &list) == NULL) &&
+ git_commit_list_insert(commit, &list) == NULL) {
+ git_error_set_oom();
+ return -1;
+ }
+
+ walk->user_input = list;
+
+ return 0;
+}
+
+int git_revwalk_push(git_revwalk *walk, const git_oid *oid)
+{
+ git_revwalk__push_options opts = GIT_REVWALK__PUSH_OPTIONS_INIT;
+
+ GIT_ASSERT_ARG(walk);
+ GIT_ASSERT_ARG(oid);
+
+ return git_revwalk__push_commit(walk, oid, &opts);
+}
+
+
+int git_revwalk_hide(git_revwalk *walk, const git_oid *oid)
+{
+ git_revwalk__push_options opts = GIT_REVWALK__PUSH_OPTIONS_INIT;
+
+ GIT_ASSERT_ARG(walk);
+ GIT_ASSERT_ARG(oid);
+
+ opts.uninteresting = 1;
+ return git_revwalk__push_commit(walk, oid, &opts);
+}
+
+int git_revwalk__push_ref(git_revwalk *walk, const char *refname, const git_revwalk__push_options *opts)
+{
+ git_oid oid;
+
+ int error = git_reference_name_to_id(&oid, walk->repo, refname);
+ if (opts->from_glob && (error == GIT_ENOTFOUND || error == GIT_EINVALIDSPEC || error == GIT_EPEEL)) {
+ return 0;
+ } else if (error < 0) {
+ return -1;
+ }
+
+ return git_revwalk__push_commit(walk, &oid, opts);
+}
+
+int git_revwalk__push_glob(git_revwalk *walk, const char *glob, const git_revwalk__push_options *given_opts)
+{
+ git_revwalk__push_options opts = GIT_REVWALK__PUSH_OPTIONS_INIT;
+ int error = 0;
+ git_str buf = GIT_STR_INIT;
+ git_reference *ref;
+ git_reference_iterator *iter;
+ size_t wildcard;
+
+ GIT_ASSERT_ARG(walk);
+ GIT_ASSERT_ARG(glob);
+
+ if (given_opts)
+ memcpy(&opts, given_opts, sizeof(opts));
+
+ /* refs/ is implied if not given in the glob */
+ if (git__prefixcmp(glob, GIT_REFS_DIR) != 0)
+ git_str_joinpath(&buf, GIT_REFS_DIR, glob);
+ else
+ git_str_puts(&buf, glob);
+ GIT_ERROR_CHECK_ALLOC_STR(&buf);
+
+ /* If no '?', '*' or '[' exist, we append '/ *' to the glob */
+ wildcard = strcspn(glob, "?*[");
+ if (!glob[wildcard])
+ git_str_put(&buf, "/*", 2);
+
+ if ((error = git_reference_iterator_glob_new(&iter, walk->repo, buf.ptr)) < 0)
+ goto out;
+
+ opts.from_glob = true;
+ while ((error = git_reference_next(&ref, iter)) == 0) {
+ error = git_revwalk__push_ref(walk, git_reference_name(ref), &opts);
+ git_reference_free(ref);
+ if (error < 0)
+ break;
+ }
+ git_reference_iterator_free(iter);
+
+ if (error == GIT_ITEROVER)
+ error = 0;
+out:
+ git_str_dispose(&buf);
+ return error;
+}
+
+int git_revwalk_push_glob(git_revwalk *walk, const char *glob)
+{
+ git_revwalk__push_options opts = GIT_REVWALK__PUSH_OPTIONS_INIT;
+
+ GIT_ASSERT_ARG(walk);
+ GIT_ASSERT_ARG(glob);
+
+ return git_revwalk__push_glob(walk, glob, &opts);
+}
+
+int git_revwalk_hide_glob(git_revwalk *walk, const char *glob)
+{
+ git_revwalk__push_options opts = GIT_REVWALK__PUSH_OPTIONS_INIT;
+
+ GIT_ASSERT_ARG(walk);
+ GIT_ASSERT_ARG(glob);
+
+ opts.uninteresting = 1;
+ return git_revwalk__push_glob(walk, glob, &opts);
+}
+
+int git_revwalk_push_head(git_revwalk *walk)
+{
+ git_revwalk__push_options opts = GIT_REVWALK__PUSH_OPTIONS_INIT;
+
+ GIT_ASSERT_ARG(walk);
+
+ return git_revwalk__push_ref(walk, GIT_HEAD_FILE, &opts);
+}
+
+int git_revwalk_hide_head(git_revwalk *walk)
+{
+ git_revwalk__push_options opts = GIT_REVWALK__PUSH_OPTIONS_INIT;
+
+ GIT_ASSERT_ARG(walk);
+
+ opts.uninteresting = 1;
+ return git_revwalk__push_ref(walk, GIT_HEAD_FILE, &opts);
+}
+
+int git_revwalk_push_ref(git_revwalk *walk, const char *refname)
+{
+ git_revwalk__push_options opts = GIT_REVWALK__PUSH_OPTIONS_INIT;
+
+ GIT_ASSERT_ARG(walk);
+ GIT_ASSERT_ARG(refname);
+
+ return git_revwalk__push_ref(walk, refname, &opts);
+}
+
+int git_revwalk_push_range(git_revwalk *walk, const char *range)
+{
+ git_revwalk__push_options opts = GIT_REVWALK__PUSH_OPTIONS_INIT;
+ git_revspec revspec;
+ int error = 0;
+
+ if ((error = git_revparse(&revspec, walk->repo, range)))
+ return error;
+
+ if (!revspec.to) {
+ git_error_set(GIT_ERROR_INVALID, "invalid revspec: range not provided");
+ error = GIT_EINVALIDSPEC;
+ goto out;
+ }
+
+ if (revspec.flags & GIT_REVSPEC_MERGE_BASE) {
+ /* TODO: support "<commit>...<commit>" */
+ git_error_set(GIT_ERROR_INVALID, "symmetric differences not implemented in revwalk");
+ error = GIT_EINVALIDSPEC;
+ goto out;
+ }
+
+ opts.uninteresting = 1;
+ if ((error = git_revwalk__push_commit(walk, git_object_id(revspec.from), &opts)))
+ goto out;
+
+ opts.uninteresting = 0;
+ error = git_revwalk__push_commit(walk, git_object_id(revspec.to), &opts);
+
+out:
+ git_object_free(revspec.from);
+ git_object_free(revspec.to);
+ return error;
+}
+
+int git_revwalk_hide_ref(git_revwalk *walk, const char *refname)
+{
+ git_revwalk__push_options opts = GIT_REVWALK__PUSH_OPTIONS_INIT;
+
+ GIT_ASSERT_ARG(walk);
+ GIT_ASSERT_ARG(refname);
+
+ opts.uninteresting = 1;
+ return git_revwalk__push_ref(walk, refname, &opts);
+}
+
+static int revwalk_enqueue_timesort(git_revwalk *walk, git_commit_list_node *commit)
+{
+ return git_pqueue_insert(&walk->iterator_time, commit);
+}
+
+static int revwalk_enqueue_unsorted(git_revwalk *walk, git_commit_list_node *commit)
+{
+ return git_commit_list_insert(commit, &walk->iterator_rand) ? 0 : -1;
+}
+
+static int revwalk_next_timesort(git_commit_list_node **object_out, git_revwalk *walk)
+{
+ git_commit_list_node *next;
+
+ while ((next = git_pqueue_pop(&walk->iterator_time)) != NULL) {
+ /* Some commits might become uninteresting after being added to the list */
+ if (!next->uninteresting) {
+ *object_out = next;
+ return 0;
+ }
+ }
+
+ git_error_clear();
+ return GIT_ITEROVER;
+}
+
+static int revwalk_next_unsorted(git_commit_list_node **object_out, git_revwalk *walk)
+{
+ int error;
+ git_commit_list_node *next;
+
+ while (!(error = get_revision(&next, walk, &walk->iterator_rand))) {
+ /* Some commits might become uninteresting after being added to the list */
+ if (!next->uninteresting) {
+ *object_out = next;
+ return 0;
+ }
+ }
+
+ return error;
+}
+
+static int revwalk_next_toposort(git_commit_list_node **object_out, git_revwalk *walk)
+{
+ int error;
+ git_commit_list_node *next;
+
+ while (!(error = get_revision(&next, walk, &walk->iterator_topo))) {
+ /* Some commits might become uninteresting after being added to the list */
+ if (!next->uninteresting) {
+ *object_out = next;
+ return 0;
+ }
+ }
+
+ return error;
+}
+
+static int revwalk_next_reverse(git_commit_list_node **object_out, git_revwalk *walk)
+{
+ *object_out = git_commit_list_pop(&walk->iterator_reverse);
+ return *object_out ? 0 : GIT_ITEROVER;
+}
+
+static void mark_parents_uninteresting(git_commit_list_node *commit)
+{
+ unsigned short i;
+ git_commit_list *parents = NULL;
+
+ for (i = 0; i < commit->out_degree; i++)
+ git_commit_list_insert(commit->parents[i], &parents);
+
+
+ while (parents) {
+ commit = git_commit_list_pop(&parents);
+
+ while (commit) {
+ if (commit->uninteresting)
+ break;
+
+ commit->uninteresting = 1;
+ /*
+ * If we've reached this commit some other way
+ * already, we need to mark its parents uninteresting
+ * as well.
+ */
+ if (!commit->parents)
+ break;
+
+ for (i = 0; i < commit->out_degree; i++)
+ git_commit_list_insert(commit->parents[i], &parents);
+ commit = commit->parents[0];
+ }
+ }
+}
+
+static int add_parents_to_list(git_revwalk *walk, git_commit_list_node *commit, git_commit_list **list)
+{
+ unsigned short i;
+ int error;
+
+ if (commit->added)
+ return 0;
+
+ commit->added = 1;
+
+ /*
+ * Go full on in the uninteresting case as we want to include
+ * as many of these as we can.
+ *
+ * Usually we haven't parsed the parent of a parent, but if we
+ * have it we reached it via other means so we want to mark
+ * its parents recursively too.
+ */
+ if (commit->uninteresting) {
+ for (i = 0; i < commit->out_degree; i++) {
+ git_commit_list_node *p = commit->parents[i];
+ p->uninteresting = 1;
+
+ /* git does it gently here, but we don't like missing objects */
+ if ((error = git_commit_list_parse(walk, p)) < 0)
+ return error;
+
+ if (p->parents)
+ mark_parents_uninteresting(p);
+
+ p->seen = 1;
+ git_commit_list_insert_by_date(p, list);
+ }
+
+ return 0;
+ }
+
+ /*
+ * Now on to what we do if the commit is indeed
+ * interesting. Here we do want things like first-parent take
+ * effect as this is what we'll be showing.
+ */
+ for (i = 0; i < commit->out_degree; i++) {
+ git_commit_list_node *p = commit->parents[i];
+
+ if ((error = git_commit_list_parse(walk, p)) < 0)
+ return error;
+
+ if (walk->hide_cb && walk->hide_cb(&p->oid, walk->hide_cb_payload))
+ continue;
+
+ if (!p->seen) {
+ p->seen = 1;
+ git_commit_list_insert_by_date(p, list);
+ }
+
+ if (walk->first_parent)
+ break;
+ }
+ return 0;
+}
+
+/* How many uninteresting commits we want to look at after we run out of interesting ones */
+#define SLOP 5
+
+static int still_interesting(git_commit_list *list, int64_t time, int slop)
+{
+ /* The empty list is pretty boring */
+ if (!list)
+ return 0;
+
+ /*
+ * If the destination list has commits with an earlier date than our
+ * source, we want to reset the slop counter as we're not done.
+ */
+ if (time <= list->item->time)
+ return SLOP;
+
+ for (; list; list = list->next) {
+ /*
+ * If the destination list still contains interesting commits we
+ * want to continue looking.
+ */
+ if (!list->item->uninteresting || list->item->time > time)
+ return SLOP;
+ }
+
+ /* Everything's uninteresting, reduce the count */
+ return slop - 1;
+}
+
+static int limit_list(git_commit_list **out, git_revwalk *walk, git_commit_list *commits)
+{
+ int error, slop = SLOP;
+ int64_t time = INT64_MAX;
+ git_commit_list *list = commits;
+ git_commit_list *newlist = NULL;
+ git_commit_list **p = &newlist;
+
+ while (list) {
+ git_commit_list_node *commit = git_commit_list_pop(&list);
+
+ if ((error = add_parents_to_list(walk, commit, &list)) < 0)
+ return error;
+
+ if (commit->uninteresting) {
+ mark_parents_uninteresting(commit);
+
+ slop = still_interesting(list, time, slop);
+ if (slop)
+ continue;
+
+ break;
+ }
+
+ if (walk->hide_cb && walk->hide_cb(&commit->oid, walk->hide_cb_payload))
+ continue;
+
+ time = commit->time;
+ p = &git_commit_list_insert(commit, p)->next;
+ }
+
+ git_commit_list_free(&list);
+ *out = newlist;
+ return 0;
+}
+
+static int get_revision(git_commit_list_node **out, git_revwalk *walk, git_commit_list **list)
+{
+ int error;
+ git_commit_list_node *commit;
+
+ commit = git_commit_list_pop(list);
+ if (!commit) {
+ git_error_clear();
+ return GIT_ITEROVER;
+ }
+
+ /*
+ * If we did not run limit_list and we must add parents to the
+ * list ourselves.
+ */
+ if (!walk->limited) {
+ if ((error = add_parents_to_list(walk, commit, list)) < 0)
+ return error;
+ }
+
+ *out = commit;
+ return 0;
+}
+
+static int sort_in_topological_order(git_commit_list **out, git_revwalk *walk, git_commit_list *list)
+{
+ git_commit_list *ll = NULL, *newlist, **pptr;
+ git_commit_list_node *next;
+ git_pqueue queue;
+ git_vector_cmp queue_cmp = NULL;
+ unsigned short i;
+ int error;
+
+ if (walk->sorting & GIT_SORT_TIME)
+ queue_cmp = git_commit_list_time_cmp;
+
+ if ((error = git_pqueue_init(&queue, 0, 8, queue_cmp)))
+ return error;
+
+ /*
+ * Start by resetting the in-degree to 1 for the commits in
+ * our list. We want to go through this list again, so we
+ * store it in the commit list as we extract it from the lower
+ * machinery.
+ */
+ for (ll = list; ll; ll = ll->next) {
+ ll->item->in_degree = 1;
+ }
+
+ /*
+ * Count up how many children each commit has. We limit
+ * ourselves to those commits in the original list (in-degree
+ * of 1) avoiding setting it for any parent that was hidden.
+ */
+ for(ll = list; ll; ll = ll->next) {
+ for (i = 0; i < ll->item->out_degree; ++i) {
+ git_commit_list_node *parent = ll->item->parents[i];
+ if (parent->in_degree)
+ parent->in_degree++;
+ }
+ }
+
+ /*
+ * Now we find the tips i.e. those not reachable from any other node
+ * i.e. those which still have an in-degree of 1.
+ */
+ for(ll = list; ll; ll = ll->next) {
+ if (ll->item->in_degree == 1) {
+ if ((error = git_pqueue_insert(&queue, ll->item)))
+ goto cleanup;
+ }
+ }
+
+ /*
+ * We need to output the tips in the order that they came out of the
+ * traversal, so if we're not doing time-sorting, we need to reverse the
+ * pqueue in order to get them to come out as we inserted them.
+ */
+ if ((walk->sorting & GIT_SORT_TIME) == 0)
+ git_pqueue_reverse(&queue);
+
+
+ pptr = &newlist;
+ newlist = NULL;
+ while ((next = git_pqueue_pop(&queue)) != NULL) {
+ for (i = 0; i < next->out_degree; ++i) {
+ git_commit_list_node *parent = next->parents[i];
+ if (parent->in_degree == 0)
+ continue;
+
+ if (--parent->in_degree == 1) {
+ if ((error = git_pqueue_insert(&queue, parent)))
+ goto cleanup;
+ }
+ }
+
+ /* All the children of 'item' have been emitted (since we got to it via the priority queue) */
+ next->in_degree = 0;
+
+ pptr = &git_commit_list_insert(next, pptr)->next;
+ }
+
+ *out = newlist;
+ error = 0;
+
+cleanup:
+ git_pqueue_free(&queue);
+ return error;
+}
+
+static int prepare_walk(git_revwalk *walk)
+{
+ int error = 0;
+ git_commit_list *list, *commits = NULL, *commits_last = NULL;
+ git_commit_list_node *next;
+
+ /* If there were no pushes, we know that the walk is already over */
+ if (!walk->did_push) {
+ git_error_clear();
+ return GIT_ITEROVER;
+ }
+
+ /*
+ * This is a bit convoluted, but necessary to maintain the order of
+ * the commits. This is especially important in situations where
+ * git_revwalk__push_glob is called with a git_revwalk__push_options
+ * setting insert_by_date = 1, which is critical for fetch negotiation.
+ */
+ for (list = walk->user_input; list; list = list->next) {
+ git_commit_list_node *commit = list->item;
+ if ((error = git_commit_list_parse(walk, commit)) < 0)
+ return error;
+
+ if (commit->uninteresting)
+ mark_parents_uninteresting(commit);
+
+ if (!commit->seen) {
+ git_commit_list *new_list = NULL;
+ if ((new_list = git_commit_list_create(commit, NULL)) == NULL) {
+ git_error_set_oom();
+ return -1;
+ }
+
+ commit->seen = 1;
+ if (commits_last == NULL)
+ commits = new_list;
+ else
+ commits_last->next = new_list;
+
+ commits_last = new_list;
+ }
+ }
+
+ if (walk->limited && (error = limit_list(&commits, walk, commits)) < 0)
+ return error;
+
+ if (walk->sorting & GIT_SORT_TOPOLOGICAL) {
+ error = sort_in_topological_order(&walk->iterator_topo, walk, commits);
+ git_commit_list_free(&commits);
+
+ if (error < 0)
+ return error;
+
+ walk->get_next = &revwalk_next_toposort;
+ } else if (walk->sorting & GIT_SORT_TIME) {
+ for (list = commits; list && !error; list = list->next)
+ error = walk->enqueue(walk, list->item);
+
+ git_commit_list_free(&commits);
+
+ if (error < 0)
+ return error;
+ } else {
+ walk->iterator_rand = commits;
+ walk->get_next = revwalk_next_unsorted;
+ }
+
+ if (walk->sorting & GIT_SORT_REVERSE) {
+
+ while ((error = walk->get_next(&next, walk)) == 0)
+ if (git_commit_list_insert(next, &walk->iterator_reverse) == NULL)
+ return -1;
+
+ if (error != GIT_ITEROVER)
+ return error;
+
+ walk->get_next = &revwalk_next_reverse;
+ }
+
+ walk->walking = 1;
+ return 0;
+}
+
+
+int git_revwalk_new(git_revwalk **revwalk_out, git_repository *repo)
+{
+ git_revwalk *walk = git__calloc(1, sizeof(git_revwalk));
+ GIT_ERROR_CHECK_ALLOC(walk);
+
+ if (git_oidmap_new(&walk->commits) < 0 ||
+ git_pqueue_init(&walk->iterator_time, 0, 8, git_commit_list_time_cmp) < 0 ||
+ git_pool_init(&walk->commit_pool, COMMIT_ALLOC) < 0)
+ return -1;
+
+ walk->get_next = &revwalk_next_unsorted;
+ walk->enqueue = &revwalk_enqueue_unsorted;
+
+ walk->repo = repo;
+
+ if (git_repository_odb(&walk->odb, repo) < 0) {
+ git_revwalk_free(walk);
+ return -1;
+ }
+
+ *revwalk_out = walk;
+ return 0;
+}
+
+void git_revwalk_free(git_revwalk *walk)
+{
+ if (walk == NULL)
+ return;
+
+ git_revwalk_reset(walk);
+ git_odb_free(walk->odb);
+
+ git_oidmap_free(walk->commits);
+ git_pool_clear(&walk->commit_pool);
+ git_pqueue_free(&walk->iterator_time);
+ git__free(walk);
+}
+
+git_repository *git_revwalk_repository(git_revwalk *walk)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(walk, NULL);
+
+ return walk->repo;
+}
+
+int git_revwalk_sorting(git_revwalk *walk, unsigned int sort_mode)
+{
+ GIT_ASSERT_ARG(walk);
+
+ if (walk->walking)
+ git_revwalk_reset(walk);
+
+ walk->sorting = sort_mode;
+
+ if (walk->sorting & GIT_SORT_TIME) {
+ walk->get_next = &revwalk_next_timesort;
+ walk->enqueue = &revwalk_enqueue_timesort;
+ } else {
+ walk->get_next = &revwalk_next_unsorted;
+ walk->enqueue = &revwalk_enqueue_unsorted;
+ }
+
+ if (walk->sorting != GIT_SORT_NONE)
+ walk->limited = 1;
+
+ return 0;
+}
+
+int git_revwalk_simplify_first_parent(git_revwalk *walk)
+{
+ walk->first_parent = 1;
+ return 0;
+}
+
+int git_revwalk_next(git_oid *oid, git_revwalk *walk)
+{
+ int error;
+ git_commit_list_node *next;
+
+ GIT_ASSERT_ARG(walk);
+ GIT_ASSERT_ARG(oid);
+
+ if (!walk->walking) {
+ if ((error = prepare_walk(walk)) < 0)
+ return error;
+ }
+
+ error = walk->get_next(&next, walk);
+
+ if (error == GIT_ITEROVER) {
+ git_revwalk_reset(walk);
+ git_error_clear();
+ return GIT_ITEROVER;
+ }
+
+ if (!error)
+ git_oid_cpy(oid, &next->oid);
+
+ return error;
+}
+
+int git_revwalk_reset(git_revwalk *walk)
+{
+ git_commit_list_node *commit;
+
+ GIT_ASSERT_ARG(walk);
+
+ git_oidmap_foreach_value(walk->commits, commit, {
+ commit->seen = 0;
+ commit->in_degree = 0;
+ commit->topo_delay = 0;
+ commit->uninteresting = 0;
+ commit->added = 0;
+ commit->flags = 0;
+ });
+
+ git_pqueue_clear(&walk->iterator_time);
+ git_commit_list_free(&walk->iterator_topo);
+ git_commit_list_free(&walk->iterator_rand);
+ git_commit_list_free(&walk->iterator_reverse);
+ git_commit_list_free(&walk->user_input);
+ walk->first_parent = 0;
+ walk->walking = 0;
+ walk->limited = 0;
+ walk->did_push = walk->did_hide = 0;
+ walk->sorting = GIT_SORT_NONE;
+
+ return 0;
+}
+
+int git_revwalk_add_hide_cb(
+ git_revwalk *walk,
+ git_revwalk_hide_cb hide_cb,
+ void *payload)
+{
+ GIT_ASSERT_ARG(walk);
+
+ if (walk->walking)
+ git_revwalk_reset(walk);
+
+ walk->hide_cb = hide_cb;
+ walk->hide_cb_payload = payload;
+
+ if (hide_cb)
+ walk->limited = 1;
+
+ return 0;
+}
+
diff --git a/src/libgit2/revwalk.h b/src/libgit2/revwalk.h
new file mode 100644
index 0000000..94b8a6f
--- /dev/null
+++ b/src/libgit2/revwalk.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_revwalk_h__
+#define INCLUDE_revwalk_h__
+
+#include "common.h"
+
+#include "git2/revwalk.h"
+#include "oidmap.h"
+#include "commit_list.h"
+#include "pqueue.h"
+#include "pool.h"
+#include "vector.h"
+
+#include "oidmap.h"
+
+struct git_revwalk {
+ git_repository *repo;
+ git_odb *odb;
+
+ git_oidmap *commits;
+ git_pool commit_pool;
+
+ git_commit_list *iterator_topo;
+ git_commit_list *iterator_rand;
+ git_commit_list *iterator_reverse;
+ git_pqueue iterator_time;
+
+ int (*get_next)(git_commit_list_node **, git_revwalk *);
+ int (*enqueue)(git_revwalk *, git_commit_list_node *);
+
+ unsigned walking:1,
+ first_parent: 1,
+ did_hide: 1,
+ did_push: 1,
+ limited: 1;
+ unsigned int sorting;
+
+ /* the pushes and hides */
+ git_commit_list *user_input;
+
+ /* hide callback */
+ git_revwalk_hide_cb hide_cb;
+ void *hide_cb_payload;
+};
+
+git_commit_list_node *git_revwalk__commit_lookup(git_revwalk *walk, const git_oid *oid);
+
+typedef struct {
+ int uninteresting;
+ int from_glob;
+ int insert_by_date;
+} git_revwalk__push_options;
+
+#define GIT_REVWALK__PUSH_OPTIONS_INIT { 0 }
+
+int git_revwalk__push_commit(git_revwalk *walk,
+ const git_oid *oid,
+ const git_revwalk__push_options *opts);
+
+int git_revwalk__push_ref(git_revwalk *walk,
+ const char *refname,
+ const git_revwalk__push_options *opts);
+
+int git_revwalk__push_glob(git_revwalk *walk,
+ const char *glob,
+ const git_revwalk__push_options *given_opts);
+
+#endif
diff --git a/src/libgit2/settings.h b/src/libgit2/settings.h
new file mode 100644
index 0000000..dc42ce9
--- /dev/null
+++ b/src/libgit2/settings.h
@@ -0,0 +1,11 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+extern int git_settings_global_init(void);
+
+extern const char *git_libgit2__user_agent(void);
+extern const char *git_libgit2__ssl_ciphers(void);
diff --git a/src/libgit2/signature.c b/src/libgit2/signature.c
new file mode 100644
index 0000000..5d6ab57
--- /dev/null
+++ b/src/libgit2/signature.c
@@ -0,0 +1,339 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "signature.h"
+
+#include "repository.h"
+#include "git2/common.h"
+#include "posix.h"
+
+void git_signature_free(git_signature *sig)
+{
+ if (sig == NULL)
+ return;
+
+ git__free(sig->name);
+ sig->name = NULL;
+ git__free(sig->email);
+ sig->email = NULL;
+ git__free(sig);
+}
+
+static int signature_parse_error(const char *msg)
+{
+ git_error_set(GIT_ERROR_INVALID, "failed to parse signature - %s", msg);
+ return GIT_EINVALID;
+}
+
+static int signature_error(const char *msg)
+{
+ git_error_set(GIT_ERROR_INVALID, "failed to parse signature - %s", msg);
+ return -1;
+}
+
+static bool contains_angle_brackets(const char *input)
+{
+ return strchr(input, '<') != NULL || strchr(input, '>') != NULL;
+}
+
+static bool is_crud(unsigned char c)
+{
+ return c <= 32 ||
+ c == '.' ||
+ c == ',' ||
+ c == ':' ||
+ c == ';' ||
+ c == '<' ||
+ c == '>' ||
+ c == '"' ||
+ c == '\\' ||
+ c == '\'';
+}
+
+static char *extract_trimmed(const char *ptr, size_t len)
+{
+ while (len && is_crud((unsigned char)ptr[0])) {
+ ptr++; len--;
+ }
+
+ while (len && is_crud((unsigned char)ptr[len - 1])) {
+ len--;
+ }
+
+ return git__substrdup(ptr, len);
+}
+
+int git_signature_new(git_signature **sig_out, const char *name, const char *email, git_time_t time, int offset)
+{
+ git_signature *p = NULL;
+
+ GIT_ASSERT_ARG(name);
+ GIT_ASSERT_ARG(email);
+
+ *sig_out = NULL;
+
+ if (contains_angle_brackets(name) ||
+ contains_angle_brackets(email)) {
+ return signature_error(
+ "Neither `name` nor `email` should contain angle brackets chars.");
+ }
+
+ p = git__calloc(1, sizeof(git_signature));
+ GIT_ERROR_CHECK_ALLOC(p);
+
+ p->name = extract_trimmed(name, strlen(name));
+ GIT_ERROR_CHECK_ALLOC(p->name);
+ p->email = extract_trimmed(email, strlen(email));
+ GIT_ERROR_CHECK_ALLOC(p->email);
+
+ if (p->name[0] == '\0' || p->email[0] == '\0') {
+ git_signature_free(p);
+ return signature_error("Signature cannot have an empty name or email");
+ }
+
+ p->when.time = time;
+ p->when.offset = offset;
+ p->when.sign = (offset < 0) ? '-' : '+';
+
+ *sig_out = p;
+ return 0;
+}
+
+int git_signature_dup(git_signature **dest, const git_signature *source)
+{
+ git_signature *signature;
+
+ if (source == NULL)
+ return 0;
+
+ signature = git__calloc(1, sizeof(git_signature));
+ GIT_ERROR_CHECK_ALLOC(signature);
+
+ signature->name = git__strdup(source->name);
+ GIT_ERROR_CHECK_ALLOC(signature->name);
+
+ signature->email = git__strdup(source->email);
+ GIT_ERROR_CHECK_ALLOC(signature->email);
+
+ signature->when.time = source->when.time;
+ signature->when.offset = source->when.offset;
+ signature->when.sign = source->when.sign;
+
+ *dest = signature;
+
+ return 0;
+}
+
+int git_signature__pdup(git_signature **dest, const git_signature *source, git_pool *pool)
+{
+ git_signature *signature;
+
+ if (source == NULL)
+ return 0;
+
+ signature = git_pool_mallocz(pool, sizeof(git_signature));
+ GIT_ERROR_CHECK_ALLOC(signature);
+
+ signature->name = git_pool_strdup(pool, source->name);
+ GIT_ERROR_CHECK_ALLOC(signature->name);
+
+ signature->email = git_pool_strdup(pool, source->email);
+ GIT_ERROR_CHECK_ALLOC(signature->email);
+
+ signature->when.time = source->when.time;
+ signature->when.offset = source->when.offset;
+ signature->when.sign = source->when.sign;
+
+ *dest = signature;
+
+ return 0;
+}
+
+int git_signature_now(git_signature **sig_out, const char *name, const char *email)
+{
+ time_t now;
+ time_t offset;
+ struct tm *utc_tm;
+ git_signature *sig;
+ struct tm _utc;
+
+ *sig_out = NULL;
+
+ /*
+ * Get the current time as seconds since the epoch and
+ * transform that into a tm struct containing the time at
+ * UTC. Give that to mktime which considers it a local time
+ * (tm_isdst = -1 asks it to take DST into account) and gives
+ * us that time as seconds since the epoch. The difference
+ * between its return value and 'now' is our offset to UTC.
+ */
+ time(&now);
+ utc_tm = p_gmtime_r(&now, &_utc);
+ utc_tm->tm_isdst = -1;
+ offset = (time_t)difftime(now, mktime(utc_tm));
+ offset /= 60;
+
+ if (git_signature_new(&sig, name, email, now, (int)offset) < 0)
+ return -1;
+
+ *sig_out = sig;
+
+ return 0;
+}
+
+int git_signature_default(git_signature **out, git_repository *repo)
+{
+ int error;
+ git_config *cfg;
+ const char *user_name, *user_email;
+
+ if ((error = git_repository_config_snapshot(&cfg, repo)) < 0)
+ return error;
+
+ if (!(error = git_config_get_string(&user_name, cfg, "user.name")) &&
+ !(error = git_config_get_string(&user_email, cfg, "user.email")))
+ error = git_signature_now(out, user_name, user_email);
+
+ git_config_free(cfg);
+ return error;
+}
+
+int git_signature__parse(git_signature *sig, const char **buffer_out,
+ const char *buffer_end, const char *header, char ender)
+{
+ const char *buffer = *buffer_out;
+ const char *email_start, *email_end;
+
+ memset(sig, 0, sizeof(git_signature));
+
+ if (ender &&
+ (buffer_end = memchr(buffer, ender, buffer_end - buffer)) == NULL)
+ return signature_parse_error("no newline given");
+
+ if (header) {
+ const size_t header_len = strlen(header);
+
+ if (buffer + header_len >= buffer_end || memcmp(buffer, header, header_len) != 0)
+ return signature_parse_error("expected prefix doesn't match actual");
+
+ buffer += header_len;
+ }
+
+ email_start = git__memrchr(buffer, '<', buffer_end - buffer);
+ email_end = git__memrchr(buffer, '>', buffer_end - buffer);
+
+ if (!email_start || !email_end || email_end <= email_start)
+ return signature_parse_error("malformed e-mail");
+
+ email_start += 1;
+ sig->name = extract_trimmed(buffer, email_start - buffer - 1);
+ sig->email = extract_trimmed(email_start, email_end - email_start);
+
+ /* Do we even have a time at the end of the signature? */
+ if (email_end + 2 < buffer_end) {
+ const char *time_start = email_end + 2;
+ const char *time_end;
+
+ if (git__strntol64(&sig->when.time, time_start,
+ buffer_end - time_start, &time_end, 10) < 0) {
+ git__free(sig->name);
+ git__free(sig->email);
+ sig->name = sig->email = NULL;
+ return signature_parse_error("invalid Unix timestamp");
+ }
+
+ /* do we have a timezone? */
+ if (time_end + 1 < buffer_end) {
+ int offset, hours, mins;
+ const char *tz_start, *tz_end;
+
+ tz_start = time_end + 1;
+
+ if ((tz_start[0] != '-' && tz_start[0] != '+') ||
+ git__strntol32(&offset, tz_start + 1,
+ buffer_end - tz_start - 1, &tz_end, 10) < 0) {
+ /* malformed timezone, just assume it's zero */
+ offset = 0;
+ }
+
+ hours = offset / 100;
+ mins = offset % 100;
+
+ /*
+ * only store timezone if it's not overflowing;
+ * see http://www.worldtimezone.com/faq.html
+ */
+ if (hours <= 14 && mins <= 59) {
+ sig->when.offset = (hours * 60) + mins;
+ sig->when.sign = tz_start[0];
+ if (tz_start[0] == '-')
+ sig->when.offset = -sig->when.offset;
+ }
+ }
+ }
+
+ *buffer_out = buffer_end + 1;
+ return 0;
+}
+
+int git_signature_from_buffer(git_signature **out, const char *buf)
+{
+ git_signature *sig;
+ const char *buf_end;
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(buf);
+
+ *out = NULL;
+
+ sig = git__calloc(1, sizeof(git_signature));
+ GIT_ERROR_CHECK_ALLOC(sig);
+
+ buf_end = buf + strlen(buf);
+ error = git_signature__parse(sig, &buf, buf_end, NULL, '\0');
+
+ if (error)
+ git__free(sig);
+ else
+ *out = sig;
+
+ return error;
+}
+
+void git_signature__writebuf(git_str *buf, const char *header, const git_signature *sig)
+{
+ int offset, hours, mins;
+ char sign;
+
+ offset = sig->when.offset;
+ sign = (sig->when.offset < 0 || sig->when.sign == '-') ? '-' : '+';
+
+ if (offset < 0)
+ offset = -offset;
+
+ hours = offset / 60;
+ mins = offset % 60;
+
+ git_str_printf(buf, "%s%s <%s> %u %c%02d%02d\n",
+ header ? header : "", sig->name, sig->email,
+ (unsigned)sig->when.time, sign, hours, mins);
+}
+
+bool git_signature__equal(const git_signature *one, const git_signature *two)
+{
+ GIT_ASSERT_ARG(one);
+ GIT_ASSERT_ARG(two);
+
+ return
+ git__strcmp(one->name, two->name) == 0 &&
+ git__strcmp(one->email, two->email) == 0 &&
+ one->when.time == two->when.time &&
+ one->when.offset == two->when.offset &&
+ one->when.sign == two->when.sign;
+}
+
diff --git a/src/libgit2/signature.h b/src/libgit2/signature.h
new file mode 100644
index 0000000..5c82709
--- /dev/null
+++ b/src/libgit2/signature.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_signature_h__
+#define INCLUDE_signature_h__
+
+#include "common.h"
+
+#include "git2/common.h"
+#include "git2/signature.h"
+#include "repository.h"
+#include <time.h>
+
+int git_signature__parse(git_signature *sig, const char **buffer_out, const char *buffer_end, const char *header, char ender);
+void git_signature__writebuf(git_str *buf, const char *header, const git_signature *sig);
+bool git_signature__equal(const git_signature *one, const git_signature *two);
+
+int git_signature__pdup(git_signature **dest, const git_signature *source, git_pool *pool);
+
+#endif
diff --git a/src/libgit2/stash.c b/src/libgit2/stash.c
new file mode 100644
index 0000000..b49e95c
--- /dev/null
+++ b/src/libgit2/stash.c
@@ -0,0 +1,1286 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+
+#include "repository.h"
+#include "commit.h"
+#include "tree.h"
+#include "reflog.h"
+#include "blob.h"
+#include "git2/diff.h"
+#include "git2/stash.h"
+#include "git2/status.h"
+#include "git2/checkout.h"
+#include "git2/index.h"
+#include "git2/transaction.h"
+#include "git2/merge.h"
+#include "index.h"
+#include "signature.h"
+#include "iterator.h"
+#include "merge.h"
+#include "diff.h"
+#include "diff_generate.h"
+#include "strarray.h"
+
+static int create_error(int error, const char *msg)
+{
+ git_error_set(GIT_ERROR_STASH, "cannot stash changes - %s", msg);
+ return error;
+}
+
+static int retrieve_head(git_reference **out, git_repository *repo)
+{
+ int error = git_repository_head(out, repo);
+
+ if (error == GIT_EUNBORNBRANCH)
+ return create_error(error, "you do not have the initial commit yet.");
+
+ return error;
+}
+
+static int append_abbreviated_oid(git_str *out, const git_oid *b_commit)
+{
+ char *formatted_oid;
+
+ formatted_oid = git_oid_allocfmt(b_commit);
+ GIT_ERROR_CHECK_ALLOC(formatted_oid);
+
+ git_str_put(out, formatted_oid, 7);
+ git__free(formatted_oid);
+
+ return git_str_oom(out) ? -1 : 0;
+}
+
+static int append_commit_description(git_str *out, git_commit *commit)
+{
+ const char *summary = git_commit_summary(commit);
+ GIT_ERROR_CHECK_ALLOC(summary);
+
+ if (append_abbreviated_oid(out, git_commit_id(commit)) < 0)
+ return -1;
+
+ git_str_putc(out, ' ');
+ git_str_puts(out, summary);
+ git_str_putc(out, '\n');
+
+ return git_str_oom(out) ? -1 : 0;
+}
+
+static int retrieve_base_commit_and_message(
+ git_commit **b_commit,
+ git_str *stash_message,
+ git_repository *repo)
+{
+ git_reference *head = NULL;
+ int error;
+
+ if ((error = retrieve_head(&head, repo)) < 0)
+ return error;
+
+ if (strcmp("HEAD", git_reference_name(head)) == 0)
+ error = git_str_puts(stash_message, "(no branch): ");
+ else
+ error = git_str_printf(
+ stash_message,
+ "%s: ",
+ git_reference_name(head) + strlen(GIT_REFS_HEADS_DIR));
+ if (error < 0)
+ goto cleanup;
+
+ if ((error = git_commit_lookup(
+ b_commit, repo, git_reference_target(head))) < 0)
+ goto cleanup;
+
+ if ((error = append_commit_description(stash_message, *b_commit)) < 0)
+ goto cleanup;
+
+cleanup:
+ git_reference_free(head);
+ return error;
+}
+
+static int build_tree_from_index(
+ git_tree **out,
+ git_repository *repo,
+ git_index *index)
+{
+ int error;
+ git_oid i_tree_oid;
+
+ if ((error = git_index_write_tree_to(&i_tree_oid, index, repo)) < 0)
+ return error;
+
+ return git_tree_lookup(out, repo, &i_tree_oid);
+}
+
+static int commit_index(
+ git_commit **i_commit,
+ git_repository *repo,
+ git_index *index,
+ const git_signature *stasher,
+ const char *message,
+ const git_commit *parent)
+{
+ git_tree *i_tree = NULL;
+ git_oid i_commit_oid;
+ git_str msg = GIT_STR_INIT;
+ int error;
+
+ if ((error = build_tree_from_index(&i_tree, repo, index)) < 0)
+ goto cleanup;
+
+ if ((error = git_str_printf(&msg, "index on %s\n", message)) < 0)
+ goto cleanup;
+
+ if ((error = git_commit_create(
+ &i_commit_oid,
+ git_index_owner(index),
+ NULL,
+ stasher,
+ stasher,
+ NULL,
+ git_str_cstr(&msg),
+ i_tree,
+ 1,
+ &parent)) < 0)
+ goto cleanup;
+
+ error = git_commit_lookup(i_commit, git_index_owner(index), &i_commit_oid);
+
+cleanup:
+ git_tree_free(i_tree);
+ git_str_dispose(&msg);
+ return error;
+}
+
+struct stash_update_rules {
+ bool include_changed;
+ bool include_untracked;
+ bool include_ignored;
+};
+
+/*
+ * Similar to git_index_add_bypath but able to operate on any
+ * index without making assumptions about the repository's index
+ */
+static int stash_to_index(
+ git_repository *repo,
+ git_index *index,
+ const char *path)
+{
+ git_index *repo_index = NULL;
+ git_index_entry entry = {{0}};
+ struct stat st;
+ int error;
+
+ if (!git_repository_is_bare(repo) &&
+ (error = git_repository_index__weakptr(&repo_index, repo)) < 0)
+ return error;
+
+ if ((error = git_blob__create_from_paths(
+ &entry.id, &st, repo, NULL, path, 0, true)) < 0)
+ return error;
+
+ git_index_entry__init_from_stat(&entry, &st,
+ (repo_index == NULL || !repo_index->distrust_filemode));
+
+ entry.path = path;
+
+ return git_index_add(index, &entry);
+}
+
+static int stash_update_index_from_paths(
+ git_repository *repo,
+ git_index *index,
+ const git_strarray *paths)
+{
+ unsigned int status_flags;
+ size_t i;
+ int error = 0;
+
+ for (i = 0; i < paths->count; i++) {
+ git_status_file(&status_flags, repo, paths->strings[i]);
+
+ if (status_flags & (GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_DELETED)) {
+ if ((error = git_index_remove(index, paths->strings[i], 0)) < 0)
+ return error;
+ } else {
+ if ((error = stash_to_index(repo, index, paths->strings[i])) < 0)
+ return error;
+ }
+ }
+
+ return error;
+}
+
+static int stash_update_index_from_diff(
+ git_repository *repo,
+ git_index *index,
+ const git_diff *diff,
+ struct stash_update_rules *data)
+{
+ int error = 0;
+ size_t d, max_d = git_diff_num_deltas(diff);
+
+ for (d = 0; !error && d < max_d; ++d) {
+ const char *add_path = NULL;
+ const git_diff_delta *delta = git_diff_get_delta(diff, d);
+
+ switch (delta->status) {
+ case GIT_DELTA_IGNORED:
+ if (data->include_ignored)
+ add_path = delta->new_file.path;
+ break;
+
+ case GIT_DELTA_UNTRACKED:
+ if (data->include_untracked &&
+ delta->new_file.mode != GIT_FILEMODE_TREE)
+ add_path = delta->new_file.path;
+ break;
+
+ case GIT_DELTA_ADDED:
+ case GIT_DELTA_MODIFIED:
+ if (data->include_changed)
+ add_path = delta->new_file.path;
+ break;
+
+ case GIT_DELTA_DELETED:
+ if (data->include_changed &&
+ !git_index_find(NULL, index, delta->old_file.path))
+ error = git_index_remove(index, delta->old_file.path, 0);
+ break;
+
+ default:
+ /* Unimplemented */
+ git_error_set(
+ GIT_ERROR_INVALID,
+ "cannot update index. Unimplemented status (%d)",
+ delta->status);
+ return -1;
+ }
+
+ if (add_path != NULL)
+ error = stash_to_index(repo, index, add_path);
+ }
+
+ return error;
+}
+
+static int build_untracked_tree(
+ git_tree **tree_out,
+ git_repository *repo,
+ git_commit *i_commit,
+ uint32_t flags)
+{
+ git_index *i_index = NULL;
+ git_tree *i_tree = NULL;
+ git_diff *diff = NULL;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ struct stash_update_rules data = {0};
+ int error;
+
+ if ((error = git_index__new(&i_index, repo->oid_type)) < 0)
+ goto cleanup;
+
+ if (flags & GIT_STASH_INCLUDE_UNTRACKED) {
+ opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED |
+ GIT_DIFF_RECURSE_UNTRACKED_DIRS;
+ data.include_untracked = true;
+ }
+
+ if (flags & GIT_STASH_INCLUDE_IGNORED) {
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED |
+ GIT_DIFF_RECURSE_IGNORED_DIRS;
+ data.include_ignored = true;
+ }
+
+ if ((error = git_commit_tree(&i_tree, i_commit)) < 0)
+ goto cleanup;
+
+ if ((error = git_diff_tree_to_workdir(&diff, repo, i_tree, &opts)) < 0)
+ goto cleanup;
+
+ if ((error = stash_update_index_from_diff(repo, i_index, diff, &data)) < 0)
+ goto cleanup;
+
+ error = build_tree_from_index(tree_out, repo, i_index);
+
+cleanup:
+ git_diff_free(diff);
+ git_tree_free(i_tree);
+ git_index_free(i_index);
+ return error;
+}
+
+static int commit_untracked(
+ git_commit **u_commit,
+ git_repository *repo,
+ const git_signature *stasher,
+ const char *message,
+ git_commit *i_commit,
+ uint32_t flags)
+{
+ git_tree *u_tree = NULL;
+ git_oid u_commit_oid;
+ git_str msg = GIT_STR_INIT;
+ int error;
+
+ if ((error = build_untracked_tree(&u_tree, repo, i_commit, flags)) < 0)
+ goto cleanup;
+
+ if ((error = git_str_printf(&msg, "untracked files on %s\n", message)) < 0)
+ goto cleanup;
+
+ if ((error = git_commit_create(
+ &u_commit_oid,
+ repo,
+ NULL,
+ stasher,
+ stasher,
+ NULL,
+ git_str_cstr(&msg),
+ u_tree,
+ 0,
+ NULL)) < 0)
+ goto cleanup;
+
+ error = git_commit_lookup(u_commit, repo, &u_commit_oid);
+
+cleanup:
+ git_tree_free(u_tree);
+ git_str_dispose(&msg);
+ return error;
+}
+
+static git_diff_delta *stash_delta_merge(
+ const git_diff_delta *a,
+ const git_diff_delta *b,
+ git_pool *pool)
+{
+ /* Special case for stash: if a file is deleted in the index, but exists
+ * in the working tree, we need to stash the workdir copy for the workdir.
+ */
+ if (a->status == GIT_DELTA_DELETED && b->status == GIT_DELTA_UNTRACKED) {
+ git_diff_delta *dup = git_diff__delta_dup(b, pool);
+
+ if (dup)
+ dup->status = GIT_DELTA_MODIFIED;
+ return dup;
+ }
+
+ return git_diff__merge_like_cgit(a, b, pool);
+}
+
+static int build_workdir_tree(
+ git_tree **tree_out,
+ git_repository *repo,
+ git_index *i_index,
+ git_commit *b_commit)
+{
+ git_tree *b_tree = NULL;
+ git_diff *diff = NULL, *idx_to_wd = NULL;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ struct stash_update_rules data = {0};
+ int error;
+
+ opts.flags = GIT_DIFF_IGNORE_SUBMODULES | GIT_DIFF_INCLUDE_UNTRACKED;
+
+ if ((error = git_commit_tree(&b_tree, b_commit)) < 0)
+ goto cleanup;
+
+ if ((error = git_diff_tree_to_index(&diff, repo, b_tree, i_index, &opts)) < 0 ||
+ (error = git_diff_index_to_workdir(&idx_to_wd, repo, i_index, &opts)) < 0 ||
+ (error = git_diff__merge(diff, idx_to_wd, stash_delta_merge)) < 0)
+ goto cleanup;
+
+ data.include_changed = true;
+
+ if ((error = stash_update_index_from_diff(repo, i_index, diff, &data)) < 0)
+ goto cleanup;
+
+ error = build_tree_from_index(tree_out, repo, i_index);
+
+cleanup:
+ git_diff_free(idx_to_wd);
+ git_diff_free(diff);
+ git_tree_free(b_tree);
+
+ return error;
+}
+
+static int build_stash_commit_from_tree(
+ git_oid *w_commit_oid,
+ git_repository *repo,
+ const git_signature *stasher,
+ const char *message,
+ git_commit *i_commit,
+ git_commit *b_commit,
+ git_commit *u_commit,
+ const git_tree *tree)
+{
+ const git_commit *parents[] = { NULL, NULL, NULL };
+
+ parents[0] = b_commit;
+ parents[1] = i_commit;
+ parents[2] = u_commit;
+
+ return git_commit_create(
+ w_commit_oid,
+ repo,
+ NULL,
+ stasher,
+ stasher,
+ NULL,
+ message,
+ tree,
+ u_commit ? 3 : 2,
+ parents);
+}
+
+static int build_stash_commit_from_index(
+ git_oid *w_commit_oid,
+ git_repository *repo,
+ const git_signature *stasher,
+ const char *message,
+ git_commit *i_commit,
+ git_commit *b_commit,
+ git_commit *u_commit,
+ git_index *index)
+{
+ git_tree *tree;
+ int error;
+
+ if ((error = build_tree_from_index(&tree, repo, index)) < 0)
+ goto cleanup;
+
+ error = build_stash_commit_from_tree(
+ w_commit_oid,
+ repo,
+ stasher,
+ message,
+ i_commit,
+ b_commit,
+ u_commit,
+ tree);
+
+cleanup:
+ git_tree_free(tree);
+ return error;
+}
+
+static int commit_worktree(
+ git_oid *w_commit_oid,
+ git_repository *repo,
+ const git_signature *stasher,
+ const char *message,
+ git_commit *i_commit,
+ git_commit *b_commit,
+ git_commit *u_commit)
+{
+ git_index *i_index = NULL, *r_index = NULL;
+ git_tree *w_tree = NULL;
+ int error = 0, ignorecase;
+
+ if ((error = git_repository_index(&r_index, repo) < 0) ||
+ (error = git_index__new(&i_index, repo->oid_type)) < 0 ||
+ (error = git_index__fill(i_index, &r_index->entries) < 0) ||
+ (error = git_repository__configmap_lookup(&ignorecase, repo, GIT_CONFIGMAP_IGNORECASE)) < 0)
+ goto cleanup;
+
+ git_index__set_ignore_case(i_index, ignorecase);
+
+ if ((error = build_workdir_tree(&w_tree, repo, i_index, b_commit)) < 0)
+ goto cleanup;
+
+ error = build_stash_commit_from_tree(
+ w_commit_oid,
+ repo,
+ stasher,
+ message,
+ i_commit,
+ b_commit,
+ u_commit,
+ w_tree
+ );
+
+cleanup:
+ git_tree_free(w_tree);
+ git_index_free(i_index);
+ git_index_free(r_index);
+ return error;
+}
+
+static int prepare_worktree_commit_message(git_str *out, const char *user_message)
+{
+ git_str buf = GIT_STR_INIT;
+ int error = 0;
+
+ if (!user_message) {
+ git_str_printf(&buf, "WIP on %s", git_str_cstr(out));
+ } else {
+ const char *colon;
+
+ if ((colon = strchr(git_str_cstr(out), ':')) == NULL)
+ goto cleanup;
+
+ git_str_puts(&buf, "On ");
+ git_str_put(&buf, git_str_cstr(out), colon - out->ptr);
+ git_str_printf(&buf, ": %s\n", user_message);
+ }
+
+ if (git_str_oom(&buf)) {
+ error = -1;
+ goto cleanup;
+ }
+
+ git_str_swap(out, &buf);
+
+cleanup:
+ git_str_dispose(&buf);
+ return error;
+}
+
+static int update_reflog(
+ git_oid *w_commit_oid,
+ git_repository *repo,
+ const char *message)
+{
+ git_reference *stash;
+ int error;
+
+ if ((error = git_reference_ensure_log(repo, GIT_REFS_STASH_FILE)) < 0)
+ return error;
+
+ error = git_reference_create(&stash, repo, GIT_REFS_STASH_FILE, w_commit_oid, 1, message);
+
+ git_reference_free(stash);
+
+ return error;
+}
+
+static int is_dirty_cb(const char *path, unsigned int status, void *payload)
+{
+ GIT_UNUSED(path);
+ GIT_UNUSED(status);
+ GIT_UNUSED(payload);
+
+ return GIT_PASSTHROUGH;
+}
+
+static int ensure_there_are_changes_to_stash(git_repository *repo, uint32_t flags)
+{
+ int error;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+
+ opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
+ opts.flags = GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
+
+ if (flags & GIT_STASH_INCLUDE_UNTRACKED)
+ opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
+
+ if (flags & GIT_STASH_INCLUDE_IGNORED)
+ opts.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED |
+ GIT_STATUS_OPT_RECURSE_IGNORED_DIRS;
+
+ error = git_status_foreach_ext(repo, &opts, is_dirty_cb, NULL);
+
+ if (error == GIT_PASSTHROUGH)
+ return 0;
+
+ if (!error)
+ return create_error(GIT_ENOTFOUND, "there is nothing to stash.");
+
+ return error;
+}
+
+static int has_changes_cb(
+ const char *path,
+ unsigned int status,
+ void *payload)
+{
+ GIT_UNUSED(path);
+ GIT_UNUSED(status);
+ GIT_UNUSED(payload);
+
+ if (status == GIT_STATUS_CURRENT)
+ return GIT_ENOTFOUND;
+
+ return 0;
+}
+
+static int ensure_there_are_changes_to_stash_paths(
+ git_repository *repo,
+ uint32_t flags,
+ const git_strarray *paths)
+{
+ int error;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+
+ opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
+ opts.flags = GIT_STATUS_OPT_EXCLUDE_SUBMODULES |
+ GIT_STATUS_OPT_INCLUDE_UNMODIFIED |
+ GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH;
+
+ if (flags & GIT_STASH_INCLUDE_UNTRACKED)
+ opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
+
+ if (flags & GIT_STASH_INCLUDE_IGNORED)
+ opts.flags |= GIT_STATUS_OPT_INCLUDE_IGNORED |
+ GIT_STATUS_OPT_RECURSE_IGNORED_DIRS;
+
+ git_strarray_copy(&opts.pathspec, paths);
+
+ error = git_status_foreach_ext(repo, &opts, has_changes_cb, NULL);
+
+ git_strarray_dispose(&opts.pathspec);
+
+ if (error == GIT_ENOTFOUND)
+ return create_error(GIT_ENOTFOUND, "one of the files does not have any changes to stash.");
+
+ return error;
+}
+
+static int reset_index_and_workdir(git_repository *repo, git_commit *commit, uint32_t flags)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+ if (flags & GIT_STASH_INCLUDE_UNTRACKED)
+ opts.checkout_strategy |= GIT_CHECKOUT_REMOVE_UNTRACKED;
+ if (flags & GIT_STASH_INCLUDE_IGNORED)
+ opts.checkout_strategy |= GIT_CHECKOUT_REMOVE_IGNORED;
+
+ return git_checkout_tree(repo, (git_object *)commit, &opts);
+}
+
+int git_stash_save(
+ git_oid *out,
+ git_repository *repo,
+ const git_signature *stasher,
+ const char *message,
+ uint32_t flags)
+{
+ git_stash_save_options opts = GIT_STASH_SAVE_OPTIONS_INIT;
+
+ GIT_ASSERT_ARG(stasher);
+
+ opts.stasher = stasher;
+ opts.message = message;
+ opts.flags = flags;
+
+ return git_stash_save_with_opts(out, repo, &opts);
+}
+
+int git_stash_save_with_opts(
+ git_oid *out,
+ git_repository *repo,
+ const git_stash_save_options *opts)
+{
+ git_index *index = NULL, *paths_index = NULL;
+ git_commit *b_commit = NULL, *i_commit = NULL, *u_commit = NULL;
+ git_str msg = GIT_STR_INIT;
+ git_tree *tree = NULL;
+ git_reference *head = NULL;
+ bool has_paths = false;
+
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(opts && opts->stasher);
+
+ has_paths = opts->paths.count > 0;
+
+ if ((error = git_repository__ensure_not_bare(repo, "stash save")) < 0)
+ return error;
+
+ if ((error = retrieve_base_commit_and_message(&b_commit, &msg, repo)) < 0)
+ goto cleanup;
+
+ if (!has_paths &&
+ (error = ensure_there_are_changes_to_stash(repo, opts->flags)) < 0)
+ goto cleanup;
+ else if (has_paths &&
+ (error = ensure_there_are_changes_to_stash_paths(
+ repo, opts->flags, &opts->paths)) < 0)
+ goto cleanup;
+
+ if ((error = git_repository_index(&index, repo)) < 0)
+ goto cleanup;
+
+ if ((error = commit_index(&i_commit, repo, index, opts->stasher,
+ git_str_cstr(&msg), b_commit)) < 0)
+ goto cleanup;
+
+ if ((opts->flags & (GIT_STASH_INCLUDE_UNTRACKED | GIT_STASH_INCLUDE_IGNORED)) &&
+ (error = commit_untracked(&u_commit, repo, opts->stasher,
+ git_str_cstr(&msg), i_commit, opts->flags)) < 0)
+ goto cleanup;
+
+ if ((error = prepare_worktree_commit_message(&msg, opts->message)) < 0)
+ goto cleanup;
+
+ if (!has_paths) {
+ if ((error = commit_worktree(out, repo, opts->stasher, git_str_cstr(&msg),
+ i_commit, b_commit, u_commit)) < 0)
+ goto cleanup;
+ } else {
+ if ((error = git_index__new(&paths_index, repo->oid_type)) < 0 ||
+ (error = retrieve_head(&head, repo)) < 0 ||
+ (error = git_reference_peel((git_object**)&tree, head, GIT_OBJECT_TREE)) < 0 ||
+ (error = git_index_read_tree(paths_index, tree)) < 0 ||
+ (error = stash_update_index_from_paths(repo, paths_index, &opts->paths)) < 0 ||
+ (error = build_stash_commit_from_index(out, repo, opts->stasher, git_str_cstr(&msg),
+ i_commit, b_commit, u_commit, paths_index)) < 0)
+ goto cleanup;
+ }
+
+ git_str_rtrim(&msg);
+
+ if ((error = update_reflog(out, repo, git_str_cstr(&msg))) < 0)
+ goto cleanup;
+
+ if (!(opts->flags & GIT_STASH_KEEP_ALL) &&
+ (error = reset_index_and_workdir(repo,
+ (opts->flags & GIT_STASH_KEEP_INDEX) ? i_commit : b_commit,opts->flags)) < 0)
+ goto cleanup;
+
+cleanup:
+ git_str_dispose(&msg);
+ git_commit_free(i_commit);
+ git_commit_free(b_commit);
+ git_commit_free(u_commit);
+ git_tree_free(tree);
+ git_reference_free(head);
+ git_index_free(index);
+ git_index_free(paths_index);
+
+ return error;
+}
+
+static int retrieve_stash_commit(
+ git_commit **commit,
+ git_repository *repo,
+ size_t index)
+{
+ git_reference *stash = NULL;
+ git_reflog *reflog = NULL;
+ int error;
+ size_t max;
+ const git_reflog_entry *entry;
+
+ if ((error = git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE)) < 0)
+ goto cleanup;
+
+ if ((error = git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE)) < 0)
+ goto cleanup;
+
+ max = git_reflog_entrycount(reflog);
+ if (!max || index > max - 1) {
+ error = GIT_ENOTFOUND;
+ git_error_set(GIT_ERROR_STASH, "no stashed state at position %" PRIuZ, index);
+ goto cleanup;
+ }
+
+ entry = git_reflog_entry_byindex(reflog, index);
+ if ((error = git_commit_lookup(commit, repo, git_reflog_entry_id_new(entry))) < 0)
+ goto cleanup;
+
+cleanup:
+ git_reference_free(stash);
+ git_reflog_free(reflog);
+ return error;
+}
+
+static int retrieve_stash_trees(
+ git_tree **out_stash_tree,
+ git_tree **out_base_tree,
+ git_tree **out_index_tree,
+ git_tree **out_index_parent_tree,
+ git_tree **out_untracked_tree,
+ git_commit *stash_commit)
+{
+ git_tree *stash_tree = NULL;
+ git_commit *base_commit = NULL;
+ git_tree *base_tree = NULL;
+ git_commit *index_commit = NULL;
+ git_tree *index_tree = NULL;
+ git_commit *index_parent_commit = NULL;
+ git_tree *index_parent_tree = NULL;
+ git_commit *untracked_commit = NULL;
+ git_tree *untracked_tree = NULL;
+ int error;
+
+ if ((error = git_commit_tree(&stash_tree, stash_commit)) < 0)
+ goto cleanup;
+
+ if ((error = git_commit_parent(&base_commit, stash_commit, 0)) < 0)
+ goto cleanup;
+ if ((error = git_commit_tree(&base_tree, base_commit)) < 0)
+ goto cleanup;
+
+ if ((error = git_commit_parent(&index_commit, stash_commit, 1)) < 0)
+ goto cleanup;
+ if ((error = git_commit_tree(&index_tree, index_commit)) < 0)
+ goto cleanup;
+
+ if ((error = git_commit_parent(&index_parent_commit, index_commit, 0)) < 0)
+ goto cleanup;
+ if ((error = git_commit_tree(&index_parent_tree, index_parent_commit)) < 0)
+ goto cleanup;
+
+ if (git_commit_parentcount(stash_commit) == 3) {
+ if ((error = git_commit_parent(&untracked_commit, stash_commit, 2)) < 0)
+ goto cleanup;
+ if ((error = git_commit_tree(&untracked_tree, untracked_commit)) < 0)
+ goto cleanup;
+ }
+
+ *out_stash_tree = stash_tree;
+ *out_base_tree = base_tree;
+ *out_index_tree = index_tree;
+ *out_index_parent_tree = index_parent_tree;
+ *out_untracked_tree = untracked_tree;
+
+cleanup:
+ git_commit_free(untracked_commit);
+ git_commit_free(index_parent_commit);
+ git_commit_free(index_commit);
+ git_commit_free(base_commit);
+ if (error < 0) {
+ git_tree_free(stash_tree);
+ git_tree_free(base_tree);
+ git_tree_free(index_tree);
+ git_tree_free(index_parent_tree);
+ git_tree_free(untracked_tree);
+ }
+ return error;
+}
+
+static int merge_indexes(
+ git_index **out,
+ git_repository *repo,
+ git_tree *ancestor_tree,
+ git_index *ours_index,
+ git_index *theirs_index)
+{
+ git_iterator *ancestor = NULL, *ours = NULL, *theirs = NULL;
+ git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
+ int error;
+
+ iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ if ((error = git_iterator_for_tree(&ancestor, ancestor_tree, &iter_opts)) < 0 ||
+ (error = git_iterator_for_index(&ours, repo, ours_index, &iter_opts)) < 0 ||
+ (error = git_iterator_for_index(&theirs, repo, theirs_index, &iter_opts)) < 0)
+ goto done;
+
+ error = git_merge__iterators(out, repo, ancestor, ours, theirs, NULL);
+
+done:
+ git_iterator_free(ancestor);
+ git_iterator_free(ours);
+ git_iterator_free(theirs);
+ return error;
+}
+
+static int merge_index_and_tree(
+ git_index **out,
+ git_repository *repo,
+ git_tree *ancestor_tree,
+ git_index *ours_index,
+ git_tree *theirs_tree)
+{
+ git_iterator *ancestor = NULL, *ours = NULL, *theirs = NULL;
+ git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
+ int error;
+
+ iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ if ((error = git_iterator_for_tree(&ancestor, ancestor_tree, &iter_opts)) < 0 ||
+ (error = git_iterator_for_index(&ours, repo, ours_index, &iter_opts)) < 0 ||
+ (error = git_iterator_for_tree(&theirs, theirs_tree, &iter_opts)) < 0)
+ goto done;
+
+ error = git_merge__iterators(out, repo, ancestor, ours, theirs, NULL);
+
+done:
+ git_iterator_free(ancestor);
+ git_iterator_free(ours);
+ git_iterator_free(theirs);
+ return error;
+}
+
+static void normalize_apply_options(
+ git_stash_apply_options *opts,
+ const git_stash_apply_options *given_apply_opts)
+{
+ if (given_apply_opts != NULL) {
+ memcpy(opts, given_apply_opts, sizeof(git_stash_apply_options));
+ } else {
+ git_stash_apply_options default_apply_opts = GIT_STASH_APPLY_OPTIONS_INIT;
+ memcpy(opts, &default_apply_opts, sizeof(git_stash_apply_options));
+ }
+
+ opts->checkout_options.checkout_strategy |= GIT_CHECKOUT_NO_REFRESH;
+
+ if (!opts->checkout_options.our_label)
+ opts->checkout_options.our_label = "Updated upstream";
+
+ if (!opts->checkout_options.their_label)
+ opts->checkout_options.their_label = "Stashed changes";
+}
+
+int git_stash_apply_options_init(git_stash_apply_options *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_stash_apply_options, GIT_STASH_APPLY_OPTIONS_INIT);
+ return 0;
+}
+
+int git_stash_save_options_init(git_stash_save_options *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_stash_save_options, GIT_STASH_SAVE_OPTIONS_INIT);
+ return 0;
+}
+
+#ifndef GIT_DEPRECATE_HARD
+int git_stash_apply_init_options(git_stash_apply_options *opts, unsigned int version)
+{
+ return git_stash_apply_options_init(opts, version);
+}
+#endif
+
+#define NOTIFY_PROGRESS(opts, progress_type) \
+ do { \
+ if ((opts).progress_cb && \
+ (error = (opts).progress_cb((progress_type), (opts).progress_payload))) { \
+ error = (error < 0) ? error : -1; \
+ goto cleanup; \
+ } \
+ } while(false);
+
+static int ensure_clean_index(git_repository *repo, git_index *index)
+{
+ git_tree *head_tree = NULL;
+ git_diff *index_diff = NULL;
+ int error = 0;
+
+ if ((error = git_repository_head_tree(&head_tree, repo)) < 0 ||
+ (error = git_diff_tree_to_index(
+ &index_diff, repo, head_tree, index, NULL)) < 0)
+ goto done;
+
+ if (git_diff_num_deltas(index_diff) > 0) {
+ git_error_set(GIT_ERROR_STASH, "%" PRIuZ " uncommitted changes exist in the index",
+ git_diff_num_deltas(index_diff));
+ error = GIT_EUNCOMMITTED;
+ }
+
+done:
+ git_diff_free(index_diff);
+ git_tree_free(head_tree);
+ return error;
+}
+
+static int stage_new_file(const git_index_entry **entries, void *data)
+{
+ git_index *index = data;
+
+ if(entries[0] == NULL)
+ return git_index_add(index, entries[1]);
+ else
+ return git_index_add(index, entries[0]);
+}
+
+static int stage_new_files(
+ git_index **out,
+ git_repository *repo,
+ git_tree *parent_tree,
+ git_tree *tree)
+{
+ git_iterator *iterators[2] = { NULL, NULL };
+ git_iterator_options iterator_options = GIT_ITERATOR_OPTIONS_INIT;
+ git_index *index = NULL;
+ int error;
+
+ if ((error = git_index__new(&index, repo->oid_type)) < 0 ||
+ (error = git_iterator_for_tree(
+ &iterators[0], parent_tree, &iterator_options)) < 0 ||
+ (error = git_iterator_for_tree(
+ &iterators[1], tree, &iterator_options)) < 0)
+ goto done;
+
+ error = git_iterator_walk(iterators, 2, stage_new_file, index);
+
+done:
+ if (error < 0)
+ git_index_free(index);
+ else
+ *out = index;
+
+ git_iterator_free(iterators[0]);
+ git_iterator_free(iterators[1]);
+
+ return error;
+}
+
+int git_stash_apply(
+ git_repository *repo,
+ size_t index,
+ const git_stash_apply_options *given_opts)
+{
+ git_stash_apply_options opts;
+ unsigned int checkout_strategy;
+ git_commit *stash_commit = NULL;
+ git_tree *stash_tree = NULL;
+ git_tree *stash_parent_tree = NULL;
+ git_tree *index_tree = NULL;
+ git_tree *index_parent_tree = NULL;
+ git_tree *untracked_tree = NULL;
+ git_index *stash_adds = NULL;
+ git_index *repo_index = NULL;
+ git_index *unstashed_index = NULL;
+ git_index *modified_index = NULL;
+ git_index *untracked_index = NULL;
+ int error;
+
+ GIT_ERROR_CHECK_VERSION(given_opts, GIT_STASH_APPLY_OPTIONS_VERSION, "git_stash_apply_options");
+
+ normalize_apply_options(&opts, given_opts);
+ checkout_strategy = opts.checkout_options.checkout_strategy;
+
+ NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_LOADING_STASH);
+
+ /* Retrieve commit corresponding to the given stash */
+ if ((error = retrieve_stash_commit(&stash_commit, repo, index)) < 0)
+ goto cleanup;
+
+ /* Retrieve all trees in the stash */
+ if ((error = retrieve_stash_trees(
+ &stash_tree, &stash_parent_tree, &index_tree,
+ &index_parent_tree, &untracked_tree, stash_commit)) < 0)
+ goto cleanup;
+
+ /* Load repo index */
+ if ((error = git_repository_index(&repo_index, repo)) < 0)
+ goto cleanup;
+
+ NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_ANALYZE_INDEX);
+
+ if ((error = ensure_clean_index(repo, repo_index)) < 0)
+ goto cleanup;
+
+ /* Restore index if required */
+ if ((opts.flags & GIT_STASH_APPLY_REINSTATE_INDEX) &&
+ git_oid_cmp(git_tree_id(stash_parent_tree), git_tree_id(index_tree))) {
+
+ if ((error = merge_index_and_tree(
+ &unstashed_index, repo, index_parent_tree, repo_index, index_tree)) < 0)
+ goto cleanup;
+
+ if (git_index_has_conflicts(unstashed_index)) {
+ error = GIT_ECONFLICT;
+ goto cleanup;
+ }
+
+ /* Otherwise, stage any new files in the stash tree. (Note: their
+ * previously unstaged contents are staged, not the previously staged.)
+ */
+ } else if ((opts.flags & GIT_STASH_APPLY_REINSTATE_INDEX) == 0) {
+ if ((error = stage_new_files(&stash_adds, repo,
+ stash_parent_tree, stash_tree)) < 0 ||
+ (error = merge_indexes(&unstashed_index, repo,
+ stash_parent_tree, repo_index, stash_adds)) < 0)
+ goto cleanup;
+ }
+
+ NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_ANALYZE_MODIFIED);
+
+ /* Restore modified files in workdir */
+ if ((error = merge_index_and_tree(
+ &modified_index, repo, stash_parent_tree, repo_index, stash_tree)) < 0)
+ goto cleanup;
+
+ /* If applicable, restore untracked / ignored files in workdir */
+ if (untracked_tree) {
+ NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_ANALYZE_UNTRACKED);
+
+ if ((error = merge_index_and_tree(&untracked_index, repo, NULL, repo_index, untracked_tree)) < 0)
+ goto cleanup;
+ }
+
+ if (untracked_index) {
+ opts.checkout_options.checkout_strategy |= GIT_CHECKOUT_DONT_UPDATE_INDEX;
+
+ NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_CHECKOUT_UNTRACKED);
+
+ if ((error = git_checkout_index(repo, untracked_index, &opts.checkout_options)) < 0)
+ goto cleanup;
+
+ opts.checkout_options.checkout_strategy = checkout_strategy;
+ }
+
+
+ /* If there are conflicts in the modified index, then we need to actually
+ * check that out as the repo's index. Otherwise, we don't update the
+ * index.
+ */
+
+ if (!git_index_has_conflicts(modified_index))
+ opts.checkout_options.checkout_strategy |= GIT_CHECKOUT_DONT_UPDATE_INDEX;
+
+ /* Check out the modified index using the existing repo index as baseline,
+ * so that existing modifications in the index can be rewritten even when
+ * checking out safely.
+ */
+ opts.checkout_options.baseline_index = repo_index;
+
+ NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_CHECKOUT_MODIFIED);
+
+ if ((error = git_checkout_index(repo, modified_index, &opts.checkout_options)) < 0)
+ goto cleanup;
+
+ if (unstashed_index && !git_index_has_conflicts(modified_index)) {
+ if ((error = git_index_read_index(repo_index, unstashed_index)) < 0)
+ goto cleanup;
+ }
+
+ NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_DONE);
+
+ error = git_index_write(repo_index);
+
+cleanup:
+ git_index_free(untracked_index);
+ git_index_free(modified_index);
+ git_index_free(unstashed_index);
+ git_index_free(stash_adds);
+ git_index_free(repo_index);
+ git_tree_free(untracked_tree);
+ git_tree_free(index_parent_tree);
+ git_tree_free(index_tree);
+ git_tree_free(stash_parent_tree);
+ git_tree_free(stash_tree);
+ git_commit_free(stash_commit);
+ return error;
+}
+
+int git_stash_foreach(
+ git_repository *repo,
+ git_stash_cb callback,
+ void *payload)
+{
+ git_reference *stash;
+ git_reflog *reflog = NULL;
+ int error;
+ size_t i, max;
+ const git_reflog_entry *entry;
+
+ error = git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE);
+ if (error == GIT_ENOTFOUND) {
+ git_error_clear();
+ return 0;
+ }
+ if (error < 0)
+ goto cleanup;
+
+ if ((error = git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE)) < 0)
+ goto cleanup;
+
+ max = git_reflog_entrycount(reflog);
+ for (i = 0; i < max; i++) {
+ entry = git_reflog_entry_byindex(reflog, i);
+
+ error = callback(i,
+ git_reflog_entry_message(entry),
+ git_reflog_entry_id_new(entry),
+ payload);
+
+ if (error) {
+ git_error_set_after_callback(error);
+ break;
+ }
+ }
+
+cleanup:
+ git_reference_free(stash);
+ git_reflog_free(reflog);
+ return error;
+}
+
+int git_stash_drop(
+ git_repository *repo,
+ size_t index)
+{
+ git_transaction *tx;
+ git_reference *stash = NULL;
+ git_reflog *reflog = NULL;
+ size_t max;
+ int error;
+
+ if ((error = git_transaction_new(&tx, repo)) < 0)
+ return error;
+
+ if ((error = git_transaction_lock_ref(tx, GIT_REFS_STASH_FILE)) < 0)
+ goto cleanup;
+
+ if ((error = git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE)) < 0)
+ goto cleanup;
+
+ if ((error = git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE)) < 0)
+ goto cleanup;
+
+ max = git_reflog_entrycount(reflog);
+
+ if (!max || index > max - 1) {
+ error = GIT_ENOTFOUND;
+ git_error_set(GIT_ERROR_STASH, "no stashed state at position %" PRIuZ, index);
+ goto cleanup;
+ }
+
+ if ((error = git_reflog_drop(reflog, index, true)) < 0)
+ goto cleanup;
+
+ if ((error = git_transaction_set_reflog(tx, GIT_REFS_STASH_FILE, reflog)) < 0)
+ goto cleanup;
+
+ if (max == 1) {
+ if ((error = git_transaction_remove(tx, GIT_REFS_STASH_FILE)) < 0)
+ goto cleanup;
+ } else if (index == 0) {
+ const git_reflog_entry *entry;
+
+ entry = git_reflog_entry_byindex(reflog, 0);
+ if ((error = git_transaction_set_target(tx, GIT_REFS_STASH_FILE, &entry->oid_cur, NULL, NULL)) < 0)
+ goto cleanup;
+ }
+
+ error = git_transaction_commit(tx);
+
+cleanup:
+ git_reference_free(stash);
+ git_transaction_free(tx);
+ git_reflog_free(reflog);
+ return error;
+}
+
+int git_stash_pop(
+ git_repository *repo,
+ size_t index,
+ const git_stash_apply_options *options)
+{
+ int error;
+
+ if ((error = git_stash_apply(repo, index, options)) < 0)
+ return error;
+
+ return git_stash_drop(repo, index);
+}
diff --git a/src/libgit2/status.c b/src/libgit2/status.c
new file mode 100644
index 0000000..df0f745
--- /dev/null
+++ b/src/libgit2/status.c
@@ -0,0 +1,584 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "status.h"
+
+#include "git2.h"
+#include "futils.h"
+#include "hash.h"
+#include "vector.h"
+#include "tree.h"
+#include "git2/status.h"
+#include "repository.h"
+#include "ignore.h"
+#include "index.h"
+#include "wildmatch.h"
+
+#include "git2/diff.h"
+#include "diff.h"
+#include "diff_generate.h"
+
+static unsigned int index_delta2status(const git_diff_delta *head2idx)
+{
+ git_status_t st = GIT_STATUS_CURRENT;
+
+ switch (head2idx->status) {
+ case GIT_DELTA_ADDED:
+ case GIT_DELTA_COPIED:
+ st = GIT_STATUS_INDEX_NEW;
+ break;
+ case GIT_DELTA_DELETED:
+ st = GIT_STATUS_INDEX_DELETED;
+ break;
+ case GIT_DELTA_MODIFIED:
+ st = GIT_STATUS_INDEX_MODIFIED;
+ break;
+ case GIT_DELTA_RENAMED:
+ st = GIT_STATUS_INDEX_RENAMED;
+
+ if (!git_oid_equal(&head2idx->old_file.id, &head2idx->new_file.id))
+ st |= GIT_STATUS_INDEX_MODIFIED;
+ break;
+ case GIT_DELTA_TYPECHANGE:
+ st = GIT_STATUS_INDEX_TYPECHANGE;
+ break;
+ case GIT_DELTA_CONFLICTED:
+ st = GIT_STATUS_CONFLICTED;
+ break;
+ default:
+ break;
+ }
+
+ return st;
+}
+
+static unsigned int workdir_delta2status(
+ git_diff *diff, git_diff_delta *idx2wd)
+{
+ git_status_t st = GIT_STATUS_CURRENT;
+
+ switch (idx2wd->status) {
+ case GIT_DELTA_ADDED:
+ case GIT_DELTA_COPIED:
+ case GIT_DELTA_UNTRACKED:
+ st = GIT_STATUS_WT_NEW;
+ break;
+ case GIT_DELTA_UNREADABLE:
+ st = GIT_STATUS_WT_UNREADABLE;
+ break;
+ case GIT_DELTA_DELETED:
+ st = GIT_STATUS_WT_DELETED;
+ break;
+ case GIT_DELTA_MODIFIED:
+ st = GIT_STATUS_WT_MODIFIED;
+ break;
+ case GIT_DELTA_IGNORED:
+ st = GIT_STATUS_IGNORED;
+ break;
+ case GIT_DELTA_RENAMED:
+ st = GIT_STATUS_WT_RENAMED;
+
+ if (!git_oid_equal(&idx2wd->old_file.id, &idx2wd->new_file.id)) {
+ /* if OIDs don't match, we might need to calculate them now to
+ * discern between RENAMED vs RENAMED+MODIFIED
+ */
+ if (git_oid_is_zero(&idx2wd->old_file.id) &&
+ diff->old_src == GIT_ITERATOR_WORKDIR &&
+ !git_diff__oid_for_file(
+ &idx2wd->old_file.id, diff, idx2wd->old_file.path,
+ idx2wd->old_file.mode, idx2wd->old_file.size))
+ idx2wd->old_file.flags |= GIT_DIFF_FLAG_VALID_ID;
+
+ if (git_oid_is_zero(&idx2wd->new_file.id) &&
+ diff->new_src == GIT_ITERATOR_WORKDIR &&
+ !git_diff__oid_for_file(
+ &idx2wd->new_file.id, diff, idx2wd->new_file.path,
+ idx2wd->new_file.mode, idx2wd->new_file.size))
+ idx2wd->new_file.flags |= GIT_DIFF_FLAG_VALID_ID;
+
+ if (!git_oid_equal(&idx2wd->old_file.id, &idx2wd->new_file.id))
+ st |= GIT_STATUS_WT_MODIFIED;
+ }
+ break;
+ case GIT_DELTA_TYPECHANGE:
+ st = GIT_STATUS_WT_TYPECHANGE;
+ break;
+ case GIT_DELTA_CONFLICTED:
+ st = GIT_STATUS_CONFLICTED;
+ break;
+ default:
+ break;
+ }
+
+ return st;
+}
+
+static bool status_is_included(
+ git_status_list *status,
+ git_diff_delta *head2idx,
+ git_diff_delta *idx2wd)
+{
+ if (!(status->opts.flags & GIT_STATUS_OPT_EXCLUDE_SUBMODULES))
+ return 1;
+
+ /* if excluding submodules and this is a submodule everywhere */
+ if (head2idx) {
+ if (head2idx->status != GIT_DELTA_ADDED &&
+ head2idx->old_file.mode != GIT_FILEMODE_COMMIT)
+ return 1;
+ if (head2idx->status != GIT_DELTA_DELETED &&
+ head2idx->new_file.mode != GIT_FILEMODE_COMMIT)
+ return 1;
+ }
+ if (idx2wd) {
+ if (idx2wd->status != GIT_DELTA_ADDED &&
+ idx2wd->old_file.mode != GIT_FILEMODE_COMMIT)
+ return 1;
+ if (idx2wd->status != GIT_DELTA_DELETED &&
+ idx2wd->new_file.mode != GIT_FILEMODE_COMMIT)
+ return 1;
+ }
+
+ /* only get here if every valid mode is GIT_FILEMODE_COMMIT */
+ return 0;
+}
+
+static git_status_t status_compute(
+ git_status_list *status,
+ git_diff_delta *head2idx,
+ git_diff_delta *idx2wd)
+{
+ git_status_t st = GIT_STATUS_CURRENT;
+
+ if (head2idx)
+ st |= index_delta2status(head2idx);
+
+ if (idx2wd)
+ st |= workdir_delta2status(status->idx2wd, idx2wd);
+
+ return st;
+}
+
+static int status_collect(
+ git_diff_delta *head2idx,
+ git_diff_delta *idx2wd,
+ void *payload)
+{
+ git_status_list *status = payload;
+ git_status_entry *status_entry;
+
+ if (!status_is_included(status, head2idx, idx2wd))
+ return 0;
+
+ status_entry = git__malloc(sizeof(git_status_entry));
+ GIT_ERROR_CHECK_ALLOC(status_entry);
+
+ status_entry->status = status_compute(status, head2idx, idx2wd);
+ status_entry->head_to_index = head2idx;
+ status_entry->index_to_workdir = idx2wd;
+
+ return git_vector_insert(&status->paired, status_entry);
+}
+
+GIT_INLINE(int) status_entry_cmp_base(
+ const void *a,
+ const void *b,
+ int (*strcomp)(const char *a, const char *b))
+{
+ const git_status_entry *entry_a = a;
+ const git_status_entry *entry_b = b;
+ const git_diff_delta *delta_a, *delta_b;
+
+ delta_a = entry_a->index_to_workdir ? entry_a->index_to_workdir :
+ entry_a->head_to_index;
+ delta_b = entry_b->index_to_workdir ? entry_b->index_to_workdir :
+ entry_b->head_to_index;
+
+ if (!delta_a && delta_b)
+ return -1;
+ if (delta_a && !delta_b)
+ return 1;
+ if (!delta_a && !delta_b)
+ return 0;
+
+ return strcomp(delta_a->new_file.path, delta_b->new_file.path);
+}
+
+static int status_entry_icmp(const void *a, const void *b)
+{
+ return status_entry_cmp_base(a, b, git__strcasecmp);
+}
+
+static int status_entry_cmp(const void *a, const void *b)
+{
+ return status_entry_cmp_base(a, b, git__strcmp);
+}
+
+static git_status_list *git_status_list_alloc(git_index *index)
+{
+ git_status_list *status = NULL;
+ int (*entrycmp)(const void *a, const void *b);
+
+ if (!(status = git__calloc(1, sizeof(git_status_list))))
+ return NULL;
+
+ entrycmp = index->ignore_case ? status_entry_icmp : status_entry_cmp;
+
+ if (git_vector_init(&status->paired, 0, entrycmp) < 0) {
+ git__free(status);
+ return NULL;
+ }
+
+ return status;
+}
+
+static int status_validate_options(const git_status_options *opts)
+{
+ if (!opts)
+ return 0;
+
+ GIT_ERROR_CHECK_VERSION(opts, GIT_STATUS_OPTIONS_VERSION, "git_status_options");
+
+ if (opts->show > GIT_STATUS_SHOW_WORKDIR_ONLY) {
+ git_error_set(GIT_ERROR_INVALID, "unknown status 'show' option");
+ return -1;
+ }
+
+ if ((opts->flags & GIT_STATUS_OPT_NO_REFRESH) != 0 &&
+ (opts->flags & GIT_STATUS_OPT_UPDATE_INDEX) != 0) {
+ git_error_set(GIT_ERROR_INVALID, "updating index from status "
+ "is not allowed when index refresh is disabled");
+ return -1;
+ }
+
+ return 0;
+}
+
+int git_status_list_new(
+ git_status_list **out,
+ git_repository *repo,
+ const git_status_options *opts)
+{
+ git_index *index = NULL;
+ git_status_list *status = NULL;
+ git_diff_options diffopt = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options findopt = GIT_DIFF_FIND_OPTIONS_INIT;
+ git_tree *head = NULL;
+ git_status_show_t show =
+ opts ? opts->show : GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
+ int error = 0;
+ unsigned int flags = opts ? opts->flags : GIT_STATUS_OPT_DEFAULTS;
+
+ *out = NULL;
+
+ if (status_validate_options(opts) < 0)
+ return -1;
+
+ if ((error = git_repository__ensure_not_bare(repo, "status")) < 0 ||
+ (error = git_repository_index(&index, repo)) < 0)
+ return error;
+
+ if (opts != NULL && opts->baseline != NULL) {
+ head = opts->baseline;
+ } else {
+ /* if there is no HEAD, that's okay - we'll make an empty iterator */
+ if ((error = git_repository_head_tree(&head, repo)) < 0) {
+ if (error != GIT_ENOTFOUND && error != GIT_EUNBORNBRANCH)
+ goto done;
+ git_error_clear();
+ }
+ }
+
+ /* refresh index from disk unless prevented */
+ if ((flags & GIT_STATUS_OPT_NO_REFRESH) == 0 &&
+ git_index_read_safely(index) < 0)
+ git_error_clear();
+
+ status = git_status_list_alloc(index);
+ GIT_ERROR_CHECK_ALLOC(status);
+
+ if (opts) {
+ memcpy(&status->opts, opts, sizeof(git_status_options));
+ memcpy(&diffopt.pathspec, &opts->pathspec, sizeof(diffopt.pathspec));
+ }
+
+ diffopt.flags = GIT_DIFF_INCLUDE_TYPECHANGE;
+ findopt.flags = GIT_DIFF_FIND_FOR_UNTRACKED;
+
+ if ((flags & GIT_STATUS_OPT_INCLUDE_UNTRACKED) != 0)
+ diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNTRACKED;
+ if ((flags & GIT_STATUS_OPT_INCLUDE_IGNORED) != 0)
+ diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_IGNORED;
+ if ((flags & GIT_STATUS_OPT_INCLUDE_UNMODIFIED) != 0)
+ diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNMODIFIED;
+ if ((flags & GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS) != 0)
+ diffopt.flags = diffopt.flags | GIT_DIFF_RECURSE_UNTRACKED_DIRS;
+ if ((flags & GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH) != 0)
+ diffopt.flags = diffopt.flags | GIT_DIFF_DISABLE_PATHSPEC_MATCH;
+ if ((flags & GIT_STATUS_OPT_RECURSE_IGNORED_DIRS) != 0)
+ diffopt.flags = diffopt.flags | GIT_DIFF_RECURSE_IGNORED_DIRS;
+ if ((flags & GIT_STATUS_OPT_EXCLUDE_SUBMODULES) != 0)
+ diffopt.flags = diffopt.flags | GIT_DIFF_IGNORE_SUBMODULES;
+ if ((flags & GIT_STATUS_OPT_UPDATE_INDEX) != 0)
+ diffopt.flags = diffopt.flags | GIT_DIFF_UPDATE_INDEX;
+ if ((flags & GIT_STATUS_OPT_INCLUDE_UNREADABLE) != 0)
+ diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNREADABLE;
+ if ((flags & GIT_STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED) != 0)
+ diffopt.flags = diffopt.flags | GIT_DIFF_INCLUDE_UNREADABLE_AS_UNTRACKED;
+
+ if ((flags & GIT_STATUS_OPT_RENAMES_FROM_REWRITES) != 0)
+ findopt.flags = findopt.flags |
+ GIT_DIFF_FIND_AND_BREAK_REWRITES |
+ GIT_DIFF_FIND_RENAMES_FROM_REWRITES |
+ GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY;
+
+ if (opts != NULL && opts->rename_threshold != 0)
+ findopt.rename_threshold = opts->rename_threshold;
+
+ if (show != GIT_STATUS_SHOW_WORKDIR_ONLY) {
+ if ((error = git_diff_tree_to_index(
+ &status->head2idx, repo, head, index, &diffopt)) < 0)
+ goto done;
+
+ if ((flags & GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX) != 0 &&
+ (error = git_diff_find_similar(status->head2idx, &findopt)) < 0)
+ goto done;
+ }
+
+ if (show != GIT_STATUS_SHOW_INDEX_ONLY) {
+ if ((error = git_diff_index_to_workdir(
+ &status->idx2wd, repo, index, &diffopt)) < 0) {
+ goto done;
+ }
+
+ if ((flags & GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR) != 0 &&
+ (error = git_diff_find_similar(status->idx2wd, &findopt)) < 0)
+ goto done;
+ }
+
+ error = git_diff__paired_foreach(
+ status->head2idx, status->idx2wd, status_collect, status);
+ if (error < 0)
+ goto done;
+
+ if (flags & GIT_STATUS_OPT_SORT_CASE_SENSITIVELY)
+ git_vector_set_cmp(&status->paired, status_entry_cmp);
+ if (flags & GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY)
+ git_vector_set_cmp(&status->paired, status_entry_icmp);
+
+ if ((flags &
+ (GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX |
+ GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR |
+ GIT_STATUS_OPT_SORT_CASE_SENSITIVELY |
+ GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY)) != 0)
+ git_vector_sort(&status->paired);
+
+done:
+ if (error < 0) {
+ git_status_list_free(status);
+ status = NULL;
+ }
+
+ *out = status;
+
+ if (opts == NULL || opts->baseline != head)
+ git_tree_free(head);
+ git_index_free(index);
+
+ return error;
+}
+
+size_t git_status_list_entrycount(git_status_list *status)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(status, 0);
+
+ return status->paired.length;
+}
+
+const git_status_entry *git_status_byindex(git_status_list *status, size_t i)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(status, NULL);
+
+ return git_vector_get(&status->paired, i);
+}
+
+void git_status_list_free(git_status_list *status)
+{
+ if (status == NULL)
+ return;
+
+ git_diff_free(status->head2idx);
+ git_diff_free(status->idx2wd);
+
+ git_vector_free_deep(&status->paired);
+
+ git__memzero(status, sizeof(*status));
+ git__free(status);
+}
+
+int git_status_foreach_ext(
+ git_repository *repo,
+ const git_status_options *opts,
+ git_status_cb cb,
+ void *payload)
+{
+ git_status_list *status;
+ const git_status_entry *status_entry;
+ size_t i;
+ int error = 0;
+
+ if ((error = git_status_list_new(&status, repo, opts)) < 0) {
+ return error;
+ }
+
+ git_vector_foreach(&status->paired, i, status_entry) {
+ const char *path = status_entry->head_to_index ?
+ status_entry->head_to_index->old_file.path :
+ status_entry->index_to_workdir->old_file.path;
+
+ if ((error = cb(path, status_entry->status, payload)) != 0) {
+ git_error_set_after_callback(error);
+ break;
+ }
+ }
+
+ git_status_list_free(status);
+
+ return error;
+}
+
+int git_status_foreach(git_repository *repo, git_status_cb cb, void *payload)
+{
+ return git_status_foreach_ext(repo, NULL, cb, payload);
+}
+
+struct status_file_info {
+ char *expected;
+ unsigned int count;
+ unsigned int status;
+ int wildmatch_flags;
+ int ambiguous;
+};
+
+static int get_one_status(const char *path, unsigned int status, void *data)
+{
+ struct status_file_info *sfi = data;
+ int (*strcomp)(const char *a, const char *b);
+
+ sfi->count++;
+ sfi->status = status;
+
+ strcomp = (sfi->wildmatch_flags & WM_CASEFOLD) ? git__strcasecmp : git__strcmp;
+
+ if (sfi->count > 1 ||
+ (strcomp(sfi->expected, path) != 0 &&
+ wildmatch(sfi->expected, path, sfi->wildmatch_flags) != 0))
+ {
+ sfi->ambiguous = true;
+ return GIT_EAMBIGUOUS; /* git_error_set will be done by caller */
+ }
+
+ return 0;
+}
+
+int git_status_file(
+ unsigned int *status_flags,
+ git_repository *repo,
+ const char *path)
+{
+ int error;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ struct status_file_info sfi = {0};
+ git_index *index;
+
+ GIT_ASSERT_ARG(status_flags);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(path);
+
+ if ((error = git_repository_index__weakptr(&index, repo)) < 0)
+ return error;
+
+ if ((sfi.expected = git__strdup(path)) == NULL)
+ return -1;
+ if (index->ignore_case)
+ sfi.wildmatch_flags = WM_CASEFOLD;
+
+ opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
+ opts.flags = GIT_STATUS_OPT_INCLUDE_IGNORED |
+ GIT_STATUS_OPT_RECURSE_IGNORED_DIRS |
+ GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS |
+ GIT_STATUS_OPT_INCLUDE_UNMODIFIED |
+ GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH;
+ opts.pathspec.count = 1;
+ opts.pathspec.strings = &sfi.expected;
+
+ error = git_status_foreach_ext(repo, &opts, get_one_status, &sfi);
+
+ if (error < 0 && sfi.ambiguous) {
+ git_error_set(GIT_ERROR_INVALID,
+ "ambiguous path '%s' given to git_status_file", sfi.expected);
+ error = GIT_EAMBIGUOUS;
+ }
+
+ if (!error && !sfi.count) {
+ git_error_set(GIT_ERROR_INVALID,
+ "attempt to get status of nonexistent file '%s'", path);
+ error = GIT_ENOTFOUND;
+ }
+
+ *status_flags = sfi.status;
+
+ git__free(sfi.expected);
+
+ return error;
+}
+
+int git_status_should_ignore(
+ int *ignored,
+ git_repository *repo,
+ const char *path)
+{
+ return git_ignore_path_is_ignored(ignored, repo, path);
+}
+
+int git_status_options_init(git_status_options *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_status_options, GIT_STATUS_OPTIONS_INIT);
+ return 0;
+}
+
+#ifndef GIT_DEPRECATE_HARD
+int git_status_init_options(git_status_options *opts, unsigned int version)
+{
+ return git_status_options_init(opts, version);
+}
+#endif
+
+int git_status_list_get_perfdata(
+ git_diff_perfdata *out, const git_status_list *status)
+{
+ GIT_ASSERT_ARG(out);
+
+ GIT_ERROR_CHECK_VERSION(out, GIT_DIFF_PERFDATA_VERSION, "git_diff_perfdata");
+
+ out->stat_calls = 0;
+ out->oid_calculations = 0;
+
+ if (status->head2idx) {
+ out->stat_calls += status->head2idx->perf.stat_calls;
+ out->oid_calculations += status->head2idx->perf.oid_calculations;
+ }
+ if (status->idx2wd) {
+ out->stat_calls += status->idx2wd->perf.stat_calls;
+ out->oid_calculations += status->idx2wd->perf.oid_calculations;
+ }
+
+ return 0;
+}
+
diff --git a/src/libgit2/status.h b/src/libgit2/status.h
new file mode 100644
index 0000000..907479a
--- /dev/null
+++ b/src/libgit2/status.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_status_h__
+#define INCLUDE_status_h__
+
+#include "common.h"
+
+#include "diff.h"
+#include "git2/status.h"
+#include "git2/diff.h"
+
+struct git_status_list {
+ git_status_options opts;
+
+ git_diff *head2idx;
+ git_diff *idx2wd;
+
+ git_vector paired;
+};
+
+#endif
diff --git a/src/libgit2/strarray.c b/src/libgit2/strarray.c
new file mode 100644
index 0000000..25e75f0
--- /dev/null
+++ b/src/libgit2/strarray.c
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "util.h"
+
+#include "common.h"
+#include "strarray.h"
+
+int git_strarray_copy(git_strarray *tgt, const git_strarray *src)
+{
+ size_t i;
+
+ GIT_ASSERT_ARG(tgt);
+ GIT_ASSERT_ARG(src);
+
+ memset(tgt, 0, sizeof(*tgt));
+
+ if (!src->count)
+ return 0;
+
+ tgt->strings = git__calloc(src->count, sizeof(char *));
+ GIT_ERROR_CHECK_ALLOC(tgt->strings);
+
+ for (i = 0; i < src->count; ++i) {
+ if (!src->strings[i])
+ continue;
+
+ tgt->strings[tgt->count] = git__strdup(src->strings[i]);
+ if (!tgt->strings[tgt->count]) {
+ git_strarray_dispose(tgt);
+ memset(tgt, 0, sizeof(*tgt));
+ return -1;
+ }
+
+ tgt->count++;
+ }
+
+ return 0;
+}
+
+void git_strarray_dispose(git_strarray *array)
+{
+ size_t i;
+
+ if (array == NULL)
+ return;
+
+ for (i = 0; i < array->count; ++i)
+ git__free(array->strings[i]);
+
+ git__free(array->strings);
+
+ memset(array, 0, sizeof(*array));
+}
+
+#ifndef GIT_DEPRECATE_HARD
+void git_strarray_free(git_strarray *array)
+{
+ git_strarray_dispose(array);
+}
+#endif
diff --git a/src/libgit2/strarray.h b/src/libgit2/strarray.h
new file mode 100644
index 0000000..1984805
--- /dev/null
+++ b/src/libgit2/strarray.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_strarray_h__
+#define INCLUDE_strarray_h__
+
+#include "common.h"
+#include "git2/strarray.h"
+
+/**
+ * Copy a string array object from source to target.
+ *
+ * Note: target is overwritten and hence should be empty, otherwise its
+ * contents are leaked. Call git_strarray_free() if necessary.
+ *
+ * @param tgt target
+ * @param src source
+ * @return 0 on success, < 0 on allocation failure
+ */
+extern int git_strarray_copy(git_strarray *tgt, const git_strarray *src);
+
+#endif
diff --git a/src/libgit2/stream.h b/src/libgit2/stream.h
new file mode 100644
index 0000000..f16b026
--- /dev/null
+++ b/src/libgit2/stream.h
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_stream_h__
+#define INCLUDE_stream_h__
+
+#include "common.h"
+#include "git2/sys/stream.h"
+
+GIT_INLINE(int) git_stream_connect(git_stream *st)
+{
+ return st->connect(st);
+}
+
+GIT_INLINE(int) git_stream_is_encrypted(git_stream *st)
+{
+ return st->encrypted;
+}
+
+GIT_INLINE(int) git_stream_certificate(git_cert **out, git_stream *st)
+{
+ if (!st->encrypted) {
+ git_error_set(GIT_ERROR_INVALID, "an unencrypted stream does not have a certificate");
+ return -1;
+ }
+
+ return st->certificate(out, st);
+}
+
+GIT_INLINE(int) git_stream_supports_proxy(git_stream *st)
+{
+ return st->proxy_support;
+}
+
+GIT_INLINE(int) git_stream_set_proxy(git_stream *st, const git_proxy_options *proxy_opts)
+{
+ if (!st->proxy_support) {
+ git_error_set(GIT_ERROR_INVALID, "proxy not supported on this stream");
+ return -1;
+ }
+
+ return st->set_proxy(st, proxy_opts);
+}
+
+GIT_INLINE(ssize_t) git_stream_read(git_stream *st, void *data, size_t len)
+{
+ return st->read(st, data, len);
+}
+
+GIT_INLINE(ssize_t) git_stream_write(git_stream *st, const char *data, size_t len, int flags)
+{
+ return st->write(st, data, len, flags);
+}
+
+GIT_INLINE(int) git_stream__write_full(git_stream *st, const char *data, size_t len, int flags)
+{
+ size_t total_written = 0;
+
+ while (total_written < len) {
+ ssize_t written = git_stream_write(st, data + total_written, len - total_written, flags);
+ if (written <= 0)
+ return -1;
+
+ total_written += written;
+ }
+
+ return 0;
+}
+
+GIT_INLINE(int) git_stream_close(git_stream *st)
+{
+ return st->close(st);
+}
+
+GIT_INLINE(void) git_stream_free(git_stream *st)
+{
+ if (!st)
+ return;
+
+ st->free(st);
+}
+
+#endif
diff --git a/src/libgit2/streams/mbedtls.c b/src/libgit2/streams/mbedtls.c
new file mode 100644
index 0000000..49aa76c
--- /dev/null
+++ b/src/libgit2/streams/mbedtls.c
@@ -0,0 +1,481 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "streams/mbedtls.h"
+
+#ifdef GIT_MBEDTLS
+
+#include <ctype.h>
+
+#include "runtime.h"
+#include "stream.h"
+#include "streams/socket.h"
+#include "git2/transport.h"
+#include "util.h"
+
+#ifndef GIT_DEFAULT_CERT_LOCATION
+#define GIT_DEFAULT_CERT_LOCATION NULL
+#endif
+
+/* Work around C90-conformance issues */
+#if !defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L)
+# if defined(_MSC_VER)
+# define inline __inline
+# elif defined(__GNUC__)
+# define inline __inline__
+# else
+# define inline
+# endif
+#endif
+
+#include <mbedtls/config.h>
+#include <mbedtls/ssl.h>
+#include <mbedtls/error.h>
+#include <mbedtls/entropy.h>
+#include <mbedtls/ctr_drbg.h>
+
+#undef inline
+
+#define GIT_SSL_DEFAULT_CIPHERS "TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256:TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256:TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384:TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256:TLS-DHE-DSS-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-DSS-WITH-AES-256-GCM-SHA384:TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA256:TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA256:TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA:TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA:TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384:TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA384:TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA:TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA:TLS-DHE-RSA-WITH-AES-128-CBC-SHA256:TLS-DHE-RSA-WITH-AES-256-CBC-SHA256:TLS-DHE-RSA-WITH-AES-128-CBC-SHA:TLS-DHE-RSA-WITH-AES-256-CBC-SHA:TLS-DHE-DSS-WITH-AES-128-CBC-SHA256:TLS-DHE-DSS-WITH-AES-256-CBC-SHA256:TLS-DHE-DSS-WITH-AES-128-CBC-SHA:TLS-DHE-DSS-WITH-AES-256-CBC-SHA:TLS-RSA-WITH-AES-128-GCM-SHA256:TLS-RSA-WITH-AES-256-GCM-SHA384:TLS-RSA-WITH-AES-128-CBC-SHA256:TLS-RSA-WITH-AES-256-CBC-SHA256:TLS-RSA-WITH-AES-128-CBC-SHA:TLS-RSA-WITH-AES-256-CBC-SHA"
+#define GIT_SSL_DEFAULT_CIPHERS_COUNT 30
+
+static mbedtls_ssl_config *git__ssl_conf;
+static int ciphers_list[GIT_SSL_DEFAULT_CIPHERS_COUNT];
+static mbedtls_entropy_context *mbedtls_entropy;
+
+/**
+ * This function aims to clean-up the SSL context which
+ * we allocated.
+ */
+static void shutdown_ssl(void)
+{
+ if (git__ssl_conf) {
+ mbedtls_x509_crt_free(git__ssl_conf->ca_chain);
+ git__free(git__ssl_conf->ca_chain);
+ mbedtls_ctr_drbg_free(git__ssl_conf->p_rng);
+ git__free(git__ssl_conf->p_rng);
+ mbedtls_ssl_config_free(git__ssl_conf);
+ git__free(git__ssl_conf);
+ git__ssl_conf = NULL;
+ }
+ if (mbedtls_entropy) {
+ mbedtls_entropy_free(mbedtls_entropy);
+ git__free(mbedtls_entropy);
+ mbedtls_entropy = NULL;
+ }
+}
+
+int git_mbedtls_stream_global_init(void)
+{
+ int loaded = 0;
+ char *crtpath = GIT_DEFAULT_CERT_LOCATION;
+ struct stat statbuf;
+ mbedtls_ctr_drbg_context *ctr_drbg = NULL;
+
+ size_t ciphers_known = 0;
+ char *cipher_name = NULL;
+ char *cipher_string = NULL;
+ char *cipher_string_tmp = NULL;
+
+ git__ssl_conf = git__malloc(sizeof(mbedtls_ssl_config));
+ GIT_ERROR_CHECK_ALLOC(git__ssl_conf);
+
+ mbedtls_ssl_config_init(git__ssl_conf);
+ if (mbedtls_ssl_config_defaults(git__ssl_conf,
+ MBEDTLS_SSL_IS_CLIENT,
+ MBEDTLS_SSL_TRANSPORT_STREAM,
+ MBEDTLS_SSL_PRESET_DEFAULT) != 0) {
+ git_error_set(GIT_ERROR_SSL, "failed to initialize mbedTLS");
+ goto cleanup;
+ }
+
+ /* configure TLSv1 */
+ mbedtls_ssl_conf_min_version(git__ssl_conf, MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_0);
+
+ /* verify_server_cert is responsible for making the check.
+ * OPTIONAL because REQUIRED drops the certificate as soon as the check
+ * is made, so we can never see the certificate and override it. */
+ mbedtls_ssl_conf_authmode(git__ssl_conf, MBEDTLS_SSL_VERIFY_OPTIONAL);
+
+ /* set the list of allowed ciphersuites */
+ ciphers_known = 0;
+ cipher_string = cipher_string_tmp = git__strdup(GIT_SSL_DEFAULT_CIPHERS);
+ GIT_ERROR_CHECK_ALLOC(cipher_string);
+
+ while ((cipher_name = git__strtok(&cipher_string_tmp, ":")) != NULL) {
+ int cipherid = mbedtls_ssl_get_ciphersuite_id(cipher_name);
+ if (cipherid == 0) continue;
+
+ if (ciphers_known >= ARRAY_SIZE(ciphers_list)) {
+ git_error_set(GIT_ERROR_SSL, "out of cipher list space");
+ goto cleanup;
+ }
+
+ ciphers_list[ciphers_known++] = cipherid;
+ }
+ git__free(cipher_string);
+
+ if (!ciphers_known) {
+ git_error_set(GIT_ERROR_SSL, "no cipher could be enabled");
+ goto cleanup;
+ }
+ mbedtls_ssl_conf_ciphersuites(git__ssl_conf, ciphers_list);
+
+ /* Seeding the random number generator */
+ mbedtls_entropy = git__malloc(sizeof(mbedtls_entropy_context));
+ GIT_ERROR_CHECK_ALLOC(mbedtls_entropy);
+
+ mbedtls_entropy_init(mbedtls_entropy);
+
+ ctr_drbg = git__malloc(sizeof(mbedtls_ctr_drbg_context));
+ GIT_ERROR_CHECK_ALLOC(ctr_drbg);
+
+ mbedtls_ctr_drbg_init(ctr_drbg);
+
+ if (mbedtls_ctr_drbg_seed(ctr_drbg,
+ mbedtls_entropy_func,
+ mbedtls_entropy, NULL, 0) != 0) {
+ git_error_set(GIT_ERROR_SSL, "failed to initialize mbedTLS entropy pool");
+ goto cleanup;
+ }
+
+ mbedtls_ssl_conf_rng(git__ssl_conf, mbedtls_ctr_drbg_random, ctr_drbg);
+
+ /* load default certificates */
+ if (crtpath != NULL && stat(crtpath, &statbuf) == 0 && S_ISREG(statbuf.st_mode))
+ loaded = (git_mbedtls__set_cert_location(crtpath, NULL) == 0);
+ if (!loaded && crtpath != NULL && stat(crtpath, &statbuf) == 0 && S_ISDIR(statbuf.st_mode))
+ loaded = (git_mbedtls__set_cert_location(NULL, crtpath) == 0);
+
+ return git_runtime_shutdown_register(shutdown_ssl);
+
+cleanup:
+ mbedtls_ctr_drbg_free(ctr_drbg);
+ git__free(ctr_drbg);
+ mbedtls_ssl_config_free(git__ssl_conf);
+ git__free(git__ssl_conf);
+ git__ssl_conf = NULL;
+
+ return -1;
+}
+
+static int bio_read(void *b, unsigned char *buf, size_t len)
+{
+ git_stream *io = (git_stream *) b;
+ return (int) git_stream_read(io, buf, min(len, INT_MAX));
+}
+
+static int bio_write(void *b, const unsigned char *buf, size_t len)
+{
+ git_stream *io = (git_stream *) b;
+ return (int) git_stream_write(io, (const char *)buf, min(len, INT_MAX), 0);
+}
+
+static int ssl_set_error(mbedtls_ssl_context *ssl, int error)
+{
+ char errbuf[512];
+ int ret = -1;
+
+ GIT_ASSERT(error != MBEDTLS_ERR_SSL_WANT_READ);
+ GIT_ASSERT(error != MBEDTLS_ERR_SSL_WANT_WRITE);
+
+ if (error != 0)
+ mbedtls_strerror( error, errbuf, 512 );
+
+ switch(error) {
+ case 0:
+ git_error_set(GIT_ERROR_SSL, "SSL error: unknown error");
+ break;
+
+ case MBEDTLS_ERR_X509_CERT_VERIFY_FAILED:
+ git_error_set(GIT_ERROR_SSL, "SSL error: %#04x [%x] - %s", error, ssl->session_negotiate->verify_result, errbuf);
+ ret = GIT_ECERTIFICATE;
+ break;
+
+ default:
+ git_error_set(GIT_ERROR_SSL, "SSL error: %#04x - %s", error, errbuf);
+ }
+
+ return ret;
+}
+
+static int ssl_teardown(mbedtls_ssl_context *ssl)
+{
+ int ret = 0;
+
+ ret = mbedtls_ssl_close_notify(ssl);
+ if (ret < 0)
+ ret = ssl_set_error(ssl, ret);
+
+ mbedtls_ssl_free(ssl);
+ return ret;
+}
+
+static int verify_server_cert(mbedtls_ssl_context *ssl)
+{
+ int ret = -1;
+
+ if ((ret = mbedtls_ssl_get_verify_result(ssl)) != 0) {
+ char vrfy_buf[512];
+ int len = mbedtls_x509_crt_verify_info(vrfy_buf, sizeof(vrfy_buf), "", ret);
+ if (len >= 1) vrfy_buf[len - 1] = '\0'; /* Remove trailing \n */
+ git_error_set(GIT_ERROR_SSL, "the SSL certificate is invalid: %#04x - %s", ret, vrfy_buf);
+ return GIT_ECERTIFICATE;
+ }
+
+ return 0;
+}
+
+typedef struct {
+ git_stream parent;
+ git_stream *io;
+ int owned;
+ bool connected;
+ char *host;
+ mbedtls_ssl_context *ssl;
+ git_cert_x509 cert_info;
+} mbedtls_stream;
+
+
+static int mbedtls_connect(git_stream *stream)
+{
+ int ret;
+ mbedtls_stream *st = (mbedtls_stream *) stream;
+
+ if (st->owned && (ret = git_stream_connect(st->io)) < 0)
+ return ret;
+
+ st->connected = true;
+
+ mbedtls_ssl_set_hostname(st->ssl, st->host);
+
+ mbedtls_ssl_set_bio(st->ssl, st->io, bio_write, bio_read, NULL);
+
+ if ((ret = mbedtls_ssl_handshake(st->ssl)) != 0)
+ return ssl_set_error(st->ssl, ret);
+
+ return verify_server_cert(st->ssl);
+}
+
+static int mbedtls_certificate(git_cert **out, git_stream *stream)
+{
+ unsigned char *encoded_cert;
+ mbedtls_stream *st = (mbedtls_stream *) stream;
+
+ const mbedtls_x509_crt *cert = mbedtls_ssl_get_peer_cert(st->ssl);
+ if (!cert) {
+ git_error_set(GIT_ERROR_SSL, "the server did not provide a certificate");
+ return -1;
+ }
+
+ /* Retrieve the length of the certificate first */
+ if (cert->raw.len == 0) {
+ git_error_set(GIT_ERROR_NET, "failed to retrieve certificate information");
+ return -1;
+ }
+
+ encoded_cert = git__malloc(cert->raw.len);
+ GIT_ERROR_CHECK_ALLOC(encoded_cert);
+ memcpy(encoded_cert, cert->raw.p, cert->raw.len);
+
+ st->cert_info.parent.cert_type = GIT_CERT_X509;
+ st->cert_info.data = encoded_cert;
+ st->cert_info.len = cert->raw.len;
+
+ *out = &st->cert_info.parent;
+
+ return 0;
+}
+
+static int mbedtls_set_proxy(git_stream *stream, const git_proxy_options *proxy_options)
+{
+ mbedtls_stream *st = (mbedtls_stream *) stream;
+
+ return git_stream_set_proxy(st->io, proxy_options);
+}
+
+static ssize_t mbedtls_stream_write(git_stream *stream, const char *data, size_t len, int flags)
+{
+ mbedtls_stream *st = (mbedtls_stream *) stream;
+ int written;
+
+ GIT_UNUSED(flags);
+
+ /*
+ * `mbedtls_ssl_write` can only represent INT_MAX bytes
+ * written via its return value. We thus need to clamp
+ * the maximum number of bytes written.
+ */
+ len = min(len, INT_MAX);
+
+ if ((written = mbedtls_ssl_write(st->ssl, (const unsigned char *)data, len)) <= 0)
+ return ssl_set_error(st->ssl, written);
+
+ return written;
+}
+
+static ssize_t mbedtls_stream_read(git_stream *stream, void *data, size_t len)
+{
+ mbedtls_stream *st = (mbedtls_stream *) stream;
+ int ret;
+
+ if ((ret = mbedtls_ssl_read(st->ssl, (unsigned char *)data, len)) <= 0)
+ ssl_set_error(st->ssl, ret);
+
+ return ret;
+}
+
+static int mbedtls_stream_close(git_stream *stream)
+{
+ mbedtls_stream *st = (mbedtls_stream *) stream;
+ int ret = 0;
+
+ if (st->connected && (ret = ssl_teardown(st->ssl)) != 0)
+ return -1;
+
+ st->connected = false;
+
+ return st->owned ? git_stream_close(st->io) : 0;
+}
+
+static void mbedtls_stream_free(git_stream *stream)
+{
+ mbedtls_stream *st = (mbedtls_stream *) stream;
+
+ if (st->owned)
+ git_stream_free(st->io);
+
+ git__free(st->host);
+ git__free(st->cert_info.data);
+ mbedtls_ssl_free(st->ssl);
+ git__free(st->ssl);
+ git__free(st);
+}
+
+static int mbedtls_stream_wrap(
+ git_stream **out,
+ git_stream *in,
+ const char *host,
+ int owned)
+{
+ mbedtls_stream *st;
+ int error;
+
+ st = git__calloc(1, sizeof(mbedtls_stream));
+ GIT_ERROR_CHECK_ALLOC(st);
+
+ st->io = in;
+ st->owned = owned;
+
+ st->ssl = git__malloc(sizeof(mbedtls_ssl_context));
+ GIT_ERROR_CHECK_ALLOC(st->ssl);
+ mbedtls_ssl_init(st->ssl);
+ if (mbedtls_ssl_setup(st->ssl, git__ssl_conf)) {
+ git_error_set(GIT_ERROR_SSL, "failed to create ssl object");
+ error = -1;
+ goto out_err;
+ }
+
+ st->host = git__strdup(host);
+ GIT_ERROR_CHECK_ALLOC(st->host);
+
+ st->parent.version = GIT_STREAM_VERSION;
+ st->parent.encrypted = 1;
+ st->parent.proxy_support = git_stream_supports_proxy(st->io);
+ st->parent.connect = mbedtls_connect;
+ st->parent.certificate = mbedtls_certificate;
+ st->parent.set_proxy = mbedtls_set_proxy;
+ st->parent.read = mbedtls_stream_read;
+ st->parent.write = mbedtls_stream_write;
+ st->parent.close = mbedtls_stream_close;
+ st->parent.free = mbedtls_stream_free;
+
+ *out = (git_stream *) st;
+ return 0;
+
+out_err:
+ mbedtls_ssl_free(st->ssl);
+ git_stream_close(st->io);
+ git_stream_free(st->io);
+ git__free(st);
+
+ return error;
+}
+
+int git_mbedtls_stream_wrap(
+ git_stream **out,
+ git_stream *in,
+ const char *host)
+{
+ return mbedtls_stream_wrap(out, in, host, 0);
+}
+
+int git_mbedtls_stream_new(
+ git_stream **out,
+ const char *host,
+ const char *port)
+{
+ git_stream *stream;
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(host);
+ GIT_ASSERT_ARG(port);
+
+ if ((error = git_socket_stream_new(&stream, host, port)) < 0)
+ return error;
+
+ if ((error = mbedtls_stream_wrap(out, stream, host, 1)) < 0) {
+ git_stream_close(stream);
+ git_stream_free(stream);
+ }
+
+ return error;
+}
+
+int git_mbedtls__set_cert_location(const char *file, const char *path)
+{
+ int ret = 0;
+ char errbuf[512];
+ mbedtls_x509_crt *cacert;
+
+ GIT_ASSERT_ARG(file || path);
+
+ cacert = git__malloc(sizeof(mbedtls_x509_crt));
+ GIT_ERROR_CHECK_ALLOC(cacert);
+
+ mbedtls_x509_crt_init(cacert);
+ if (file)
+ ret = mbedtls_x509_crt_parse_file(cacert, file);
+ if (ret >= 0 && path)
+ ret = mbedtls_x509_crt_parse_path(cacert, path);
+ /* mbedtls_x509_crt_parse_path returns the number of invalid certs on success */
+ if (ret < 0) {
+ mbedtls_x509_crt_free(cacert);
+ git__free(cacert);
+ mbedtls_strerror( ret, errbuf, 512 );
+ git_error_set(GIT_ERROR_SSL, "failed to load CA certificates: %#04x - %s", ret, errbuf);
+ return -1;
+ }
+
+ mbedtls_x509_crt_free(git__ssl_conf->ca_chain);
+ git__free(git__ssl_conf->ca_chain);
+ mbedtls_ssl_conf_ca_chain(git__ssl_conf, cacert, NULL);
+
+ return 0;
+}
+
+#else
+
+#include "stream.h"
+
+int git_mbedtls_stream_global_init(void)
+{
+ return 0;
+}
+
+#endif
diff --git a/src/libgit2/streams/mbedtls.h b/src/libgit2/streams/mbedtls.h
new file mode 100644
index 0000000..bcca6dd
--- /dev/null
+++ b/src/libgit2/streams/mbedtls.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_steams_mbedtls_h__
+#define INCLUDE_steams_mbedtls_h__
+
+#include "common.h"
+
+#include "git2/sys/stream.h"
+
+extern int git_mbedtls_stream_global_init(void);
+
+#ifdef GIT_MBEDTLS
+extern int git_mbedtls__set_cert_location(const char *file, const char *path);
+
+extern int git_mbedtls_stream_new(git_stream **out, const char *host, const char *port);
+extern int git_mbedtls_stream_wrap(git_stream **out, git_stream *in, const char *host);
+#endif
+
+#endif
diff --git a/src/libgit2/streams/openssl.c b/src/libgit2/streams/openssl.c
new file mode 100644
index 0000000..9db911e
--- /dev/null
+++ b/src/libgit2/streams/openssl.c
@@ -0,0 +1,739 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "streams/openssl.h"
+#include "streams/openssl_legacy.h"
+#include "streams/openssl_dynamic.h"
+
+#ifdef GIT_OPENSSL
+
+#include <ctype.h>
+
+#include "common.h"
+#include "runtime.h"
+#include "settings.h"
+#include "posix.h"
+#include "stream.h"
+#include "net.h"
+#include "streams/socket.h"
+#include "git2/transport.h"
+#include "git2/sys/openssl.h"
+
+#ifndef GIT_WIN32
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+#endif
+
+#ifndef GIT_OPENSSL_DYNAMIC
+# include <openssl/ssl.h>
+# include <openssl/err.h>
+# include <openssl/x509v3.h>
+# include <openssl/bio.h>
+#endif
+
+SSL_CTX *git__ssl_ctx;
+
+#define GIT_SSL_DEFAULT_CIPHERS "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA:DHE-DSS-AES128-SHA256:DHE-DSS-AES256-SHA256:DHE-DSS-AES128-SHA:DHE-DSS-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA"
+
+
+static BIO_METHOD *git_stream_bio_method;
+static int init_bio_method(void);
+
+/**
+ * This function aims to clean-up the SSL context which
+ * we allocated.
+ */
+static void shutdown_ssl(void)
+{
+ if (git_stream_bio_method) {
+ BIO_meth_free(git_stream_bio_method);
+ git_stream_bio_method = NULL;
+ }
+
+ if (git__ssl_ctx) {
+ SSL_CTX_free(git__ssl_ctx);
+ git__ssl_ctx = NULL;
+ }
+}
+
+#ifdef VALGRIND
+# if !defined(GIT_OPENSSL_LEGACY) && !defined(GIT_OPENSSL_DYNAMIC)
+
+static void *git_openssl_malloc(size_t bytes, const char *file, int line)
+{
+ GIT_UNUSED(file);
+ GIT_UNUSED(line);
+ return git__calloc(1, bytes);
+}
+
+static void *git_openssl_realloc(void *mem, size_t size, const char *file, int line)
+{
+ GIT_UNUSED(file);
+ GIT_UNUSED(line);
+ return git__realloc(mem, size);
+}
+
+static void git_openssl_free(void *mem, const char *file, int line)
+{
+ GIT_UNUSED(file);
+ GIT_UNUSED(line);
+ git__free(mem);
+}
+# else /* !GIT_OPENSSL_LEGACY && !GIT_OPENSSL_DYNAMIC */
+static void *git_openssl_malloc(size_t bytes)
+{
+ return git__calloc(1, bytes);
+}
+
+static void *git_openssl_realloc(void *mem, size_t size)
+{
+ return git__realloc(mem, size);
+}
+
+static void git_openssl_free(void *mem)
+{
+ git__free(mem);
+}
+# endif /* !GIT_OPENSSL_LEGACY && !GIT_OPENSSL_DYNAMIC */
+#endif /* VALGRIND */
+
+static int openssl_init(void)
+{
+ long ssl_opts = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3;
+ const char *ciphers = git_libgit2__ssl_ciphers();
+#ifdef VALGRIND
+ static bool allocators_initialized = false;
+#endif
+
+ /* Older OpenSSL and MacOS OpenSSL doesn't have this */
+#ifdef SSL_OP_NO_COMPRESSION
+ ssl_opts |= SSL_OP_NO_COMPRESSION;
+#endif
+
+#ifdef VALGRIND
+ /*
+ * Swap in our own allocator functions that initialize
+ * allocated memory to avoid spurious valgrind warnings.
+ * Don't error on failure; many builds of OpenSSL do not
+ * allow you to set these functions.
+ */
+ if (!allocators_initialized) {
+ CRYPTO_set_mem_functions(git_openssl_malloc,
+ git_openssl_realloc,
+ git_openssl_free);
+ allocators_initialized = true;
+ }
+#endif
+
+ OPENSSL_init_ssl(0, NULL);
+
+ /*
+ * Load SSLv{2,3} and TLSv1 so that we can talk with servers
+ * which use the SSL hellos, which are often used for
+ * compatibility. We then disable SSL so we only allow OpenSSL
+ * to speak TLSv1 to perform the encryption itself.
+ */
+ if (!(git__ssl_ctx = SSL_CTX_new(SSLv23_method())))
+ goto error;
+
+ SSL_CTX_set_options(git__ssl_ctx, ssl_opts);
+ SSL_CTX_set_mode(git__ssl_ctx, SSL_MODE_AUTO_RETRY);
+ SSL_CTX_set_verify(git__ssl_ctx, SSL_VERIFY_NONE, NULL);
+ if (!SSL_CTX_set_default_verify_paths(git__ssl_ctx))
+ goto error;
+
+ if (!ciphers)
+ ciphers = GIT_SSL_DEFAULT_CIPHERS;
+
+ if(!SSL_CTX_set_cipher_list(git__ssl_ctx, ciphers))
+ goto error;
+
+ if (init_bio_method() < 0)
+ goto error;
+
+ return git_runtime_shutdown_register(shutdown_ssl);
+
+error:
+ git_error_set(GIT_ERROR_NET, "could not initialize openssl: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ SSL_CTX_free(git__ssl_ctx);
+ git__ssl_ctx = NULL;
+ return -1;
+}
+
+/*
+ * When we use dynamic loading, we defer OpenSSL initialization until
+ * it's first used. `openssl_ensure_initialized` will do the work
+ * under a mutex.
+ */
+git_mutex openssl_mutex;
+bool openssl_initialized;
+
+int git_openssl_stream_global_init(void)
+{
+#ifndef GIT_OPENSSL_DYNAMIC
+ return openssl_init();
+#else
+ if (git_mutex_init(&openssl_mutex) != 0)
+ return -1;
+
+ return 0;
+#endif
+}
+
+static int openssl_ensure_initialized(void)
+{
+#ifdef GIT_OPENSSL_DYNAMIC
+ int error = 0;
+
+ if (git_mutex_lock(&openssl_mutex) != 0)
+ return -1;
+
+ if (!openssl_initialized) {
+ if ((error = git_openssl_stream_dynamic_init()) == 0)
+ error = openssl_init();
+
+ openssl_initialized = !error;
+ }
+
+ error |= git_mutex_unlock(&openssl_mutex);
+ return error;
+
+#else
+ return 0;
+#endif
+}
+
+#if !defined(GIT_OPENSSL_LEGACY) && !defined(GIT_OPENSSL_DYNAMIC)
+int git_openssl_set_locking(void)
+{
+# ifdef GIT_THREADS
+ return 0;
+# else
+ git_error_set(GIT_ERROR_THREAD, "libgit2 was not built with threads");
+ return -1;
+# endif
+}
+#endif
+
+
+static int bio_create(BIO *b)
+{
+ BIO_set_init(b, 1);
+ BIO_set_data(b, NULL);
+
+ return 1;
+}
+
+static int bio_destroy(BIO *b)
+{
+ if (!b)
+ return 0;
+
+ BIO_set_data(b, NULL);
+
+ return 1;
+}
+
+static int bio_read(BIO *b, char *buf, int len)
+{
+ git_stream *io = (git_stream *) BIO_get_data(b);
+
+ return (int) git_stream_read(io, buf, len);
+}
+
+static int bio_write(BIO *b, const char *buf, int len)
+{
+ git_stream *io = (git_stream *) BIO_get_data(b);
+ return (int) git_stream_write(io, buf, len, 0);
+}
+
+static long bio_ctrl(BIO *b, int cmd, long num, void *ptr)
+{
+ GIT_UNUSED(b);
+ GIT_UNUSED(num);
+ GIT_UNUSED(ptr);
+
+ if (cmd == BIO_CTRL_FLUSH)
+ return 1;
+
+ return 0;
+}
+
+static int bio_gets(BIO *b, char *buf, int len)
+{
+ GIT_UNUSED(b);
+ GIT_UNUSED(buf);
+ GIT_UNUSED(len);
+ return -1;
+}
+
+static int bio_puts(BIO *b, const char *str)
+{
+ return bio_write(b, str, strlen(str));
+}
+
+static int init_bio_method(void)
+{
+ /* Set up the BIO_METHOD we use for wrapping our own stream implementations */
+ git_stream_bio_method = BIO_meth_new(BIO_TYPE_SOURCE_SINK | BIO_get_new_index(), "git_stream");
+ GIT_ERROR_CHECK_ALLOC(git_stream_bio_method);
+
+ BIO_meth_set_write(git_stream_bio_method, bio_write);
+ BIO_meth_set_read(git_stream_bio_method, bio_read);
+ BIO_meth_set_puts(git_stream_bio_method, bio_puts);
+ BIO_meth_set_gets(git_stream_bio_method, bio_gets);
+ BIO_meth_set_ctrl(git_stream_bio_method, bio_ctrl);
+ BIO_meth_set_create(git_stream_bio_method, bio_create);
+ BIO_meth_set_destroy(git_stream_bio_method, bio_destroy);
+
+ return 0;
+}
+
+static int ssl_set_error(SSL *ssl, int error)
+{
+ int err;
+ unsigned long e;
+
+ err = SSL_get_error(ssl, error);
+
+ GIT_ASSERT(err != SSL_ERROR_WANT_READ);
+ GIT_ASSERT(err != SSL_ERROR_WANT_WRITE);
+
+ switch (err) {
+ case SSL_ERROR_WANT_CONNECT:
+ case SSL_ERROR_WANT_ACCEPT:
+ git_error_set(GIT_ERROR_SSL, "SSL error: connection failure");
+ break;
+ case SSL_ERROR_WANT_X509_LOOKUP:
+ git_error_set(GIT_ERROR_SSL, "SSL error: x509 error");
+ break;
+ case SSL_ERROR_SYSCALL:
+ e = ERR_get_error();
+ if (e > 0) {
+ char errmsg[256];
+ ERR_error_string_n(e, errmsg, sizeof(errmsg));
+ git_error_set(GIT_ERROR_NET, "SSL error: %s", errmsg);
+ break;
+ } else if (error < 0) {
+ git_error_set(GIT_ERROR_OS, "SSL error: syscall failure");
+ break;
+ }
+ git_error_set(GIT_ERROR_SSL, "SSL error: received early EOF");
+ return GIT_EEOF;
+ break;
+ case SSL_ERROR_SSL:
+ {
+ char errmsg[256];
+ e = ERR_get_error();
+ ERR_error_string_n(e, errmsg, sizeof(errmsg));
+ git_error_set(GIT_ERROR_SSL, "SSL error: %s", errmsg);
+ break;
+ }
+ case SSL_ERROR_NONE:
+ case SSL_ERROR_ZERO_RETURN:
+ default:
+ git_error_set(GIT_ERROR_SSL, "SSL error: unknown error");
+ break;
+ }
+ return -1;
+}
+
+static int ssl_teardown(SSL *ssl)
+{
+ int ret;
+
+ ret = SSL_shutdown(ssl);
+ if (ret < 0)
+ ret = ssl_set_error(ssl, ret);
+ else
+ ret = 0;
+
+ return ret;
+}
+
+static bool check_host_name(const char *host, const char *name)
+{
+ return !strcasecmp(host, name) ||
+ git_net_hostname_matches_cert(host, name);
+}
+
+static int verify_server_cert(SSL *ssl, const char *host)
+{
+ X509 *cert = NULL;
+ X509_NAME *peer_name;
+ ASN1_STRING *str;
+ unsigned char *peer_cn = NULL;
+ int matched = -1, type = GEN_DNS;
+ GENERAL_NAMES *alts;
+ struct in6_addr addr6;
+ struct in_addr addr4;
+ void *addr = NULL;
+ int i = -1, j, error = 0;
+
+ if (SSL_get_verify_result(ssl) != X509_V_OK) {
+ git_error_set(GIT_ERROR_SSL, "the SSL certificate is invalid");
+ return GIT_ECERTIFICATE;
+ }
+
+ /* Try to parse the host as an IP address to see if it is */
+ if (p_inet_pton(AF_INET, host, &addr4)) {
+ type = GEN_IPADD;
+ addr = &addr4;
+ } else {
+ if (p_inet_pton(AF_INET6, host, &addr6)) {
+ type = GEN_IPADD;
+ addr = &addr6;
+ }
+ }
+
+
+ cert = SSL_get_peer_certificate(ssl);
+ if (!cert) {
+ error = -1;
+ git_error_set(GIT_ERROR_SSL, "the server did not provide a certificate");
+ goto cleanup;
+ }
+
+ /* Check the alternative names */
+ alts = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
+ if (alts) {
+ int num;
+
+ num = sk_GENERAL_NAME_num(alts);
+ for (i = 0; i < num && matched != 1; i++) {
+ const GENERAL_NAME *gn = sk_GENERAL_NAME_value(alts, i);
+ const char *name = (char *) ASN1_STRING_get0_data(gn->d.ia5);
+ size_t namelen = (size_t) ASN1_STRING_length(gn->d.ia5);
+
+ /* Skip any names of a type we're not looking for */
+ if (gn->type != type)
+ continue;
+
+ if (type == GEN_DNS) {
+ /* If it contains embedded NULs, don't even try */
+ if (memchr(name, '\0', namelen))
+ continue;
+
+ matched = !!check_host_name(host, name);
+ } else if (type == GEN_IPADD) {
+ /* Here name isn't so much a name but a binary representation of the IP */
+ matched = addr && !!memcmp(name, addr, namelen);
+ }
+ }
+ }
+ GENERAL_NAMES_free(alts);
+
+ if (matched == 0)
+ goto cert_fail_name;
+
+ if (matched == 1) {
+ goto cleanup;
+ }
+
+ /* If no alternative names are available, check the common name */
+ peer_name = X509_get_subject_name(cert);
+ if (peer_name == NULL)
+ goto on_error;
+
+ if (peer_name) {
+ /* Get the index of the last CN entry */
+ while ((j = X509_NAME_get_index_by_NID(peer_name, NID_commonName, i)) >= 0)
+ i = j;
+ }
+
+ if (i < 0)
+ goto on_error;
+
+ str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(peer_name, i));
+ if (str == NULL)
+ goto on_error;
+
+ /* Work around a bug in OpenSSL whereby ASN1_STRING_to_UTF8 fails if it's already in utf-8 */
+ if (ASN1_STRING_type(str) == V_ASN1_UTF8STRING) {
+ int size = ASN1_STRING_length(str);
+
+ if (size > 0) {
+ peer_cn = OPENSSL_malloc(size + 1);
+ GIT_ERROR_CHECK_ALLOC(peer_cn);
+ memcpy(peer_cn, ASN1_STRING_get0_data(str), size);
+ peer_cn[size] = '\0';
+ } else {
+ goto cert_fail_name;
+ }
+ } else {
+ int size = ASN1_STRING_to_UTF8(&peer_cn, str);
+ GIT_ERROR_CHECK_ALLOC(peer_cn);
+ if (memchr(peer_cn, '\0', size))
+ goto cert_fail_name;
+ }
+
+ if (!check_host_name(host, (char *)peer_cn))
+ goto cert_fail_name;
+
+ goto cleanup;
+
+cert_fail_name:
+ error = GIT_ECERTIFICATE;
+ git_error_set(GIT_ERROR_SSL, "hostname does not match certificate");
+ goto cleanup;
+
+on_error:
+ error = ssl_set_error(ssl, 0);
+ goto cleanup;
+
+cleanup:
+ X509_free(cert);
+ OPENSSL_free(peer_cn);
+ return error;
+}
+
+typedef struct {
+ git_stream parent;
+ git_stream *io;
+ int owned;
+ bool connected;
+ char *host;
+ SSL *ssl;
+ git_cert_x509 cert_info;
+} openssl_stream;
+
+static int openssl_connect(git_stream *stream)
+{
+ int ret;
+ BIO *bio;
+ openssl_stream *st = (openssl_stream *) stream;
+
+ if (st->owned && (ret = git_stream_connect(st->io)) < 0)
+ return ret;
+
+ bio = BIO_new(git_stream_bio_method);
+ GIT_ERROR_CHECK_ALLOC(bio);
+
+ BIO_set_data(bio, st->io);
+ SSL_set_bio(st->ssl, bio, bio);
+
+ /* specify the host in case SNI is needed */
+#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
+ SSL_set_tlsext_host_name(st->ssl, st->host);
+#endif
+
+ if ((ret = SSL_connect(st->ssl)) <= 0)
+ return ssl_set_error(st->ssl, ret);
+
+ st->connected = true;
+
+ return verify_server_cert(st->ssl, st->host);
+}
+
+static int openssl_certificate(git_cert **out, git_stream *stream)
+{
+ openssl_stream *st = (openssl_stream *) stream;
+ X509 *cert = SSL_get_peer_certificate(st->ssl);
+ unsigned char *guard, *encoded_cert = NULL;
+ int error, len;
+
+ /* Retrieve the length of the certificate first */
+ len = i2d_X509(cert, NULL);
+ if (len < 0) {
+ git_error_set(GIT_ERROR_NET, "failed to retrieve certificate information");
+ error = -1;
+ goto out;
+ }
+
+ encoded_cert = git__malloc(len);
+ GIT_ERROR_CHECK_ALLOC(encoded_cert);
+ /* i2d_X509 makes 'guard' point to just after the data */
+ guard = encoded_cert;
+
+ len = i2d_X509(cert, &guard);
+ if (len < 0) {
+ git_error_set(GIT_ERROR_NET, "failed to retrieve certificate information");
+ error = -1;
+ goto out;
+ }
+
+ st->cert_info.parent.cert_type = GIT_CERT_X509;
+ st->cert_info.data = encoded_cert;
+ st->cert_info.len = len;
+ encoded_cert = NULL;
+
+ *out = &st->cert_info.parent;
+ error = 0;
+
+out:
+ git__free(encoded_cert);
+ X509_free(cert);
+ return error;
+}
+
+static int openssl_set_proxy(git_stream *stream, const git_proxy_options *proxy_opts)
+{
+ openssl_stream *st = (openssl_stream *) stream;
+
+ return git_stream_set_proxy(st->io, proxy_opts);
+}
+
+static ssize_t openssl_write(git_stream *stream, const char *data, size_t data_len, int flags)
+{
+ openssl_stream *st = (openssl_stream *) stream;
+ int ret, len = min(data_len, INT_MAX);
+
+ GIT_UNUSED(flags);
+
+ if ((ret = SSL_write(st->ssl, data, len)) <= 0)
+ return ssl_set_error(st->ssl, ret);
+
+ return ret;
+}
+
+static ssize_t openssl_read(git_stream *stream, void *data, size_t len)
+{
+ openssl_stream *st = (openssl_stream *) stream;
+ int ret;
+
+ if ((ret = SSL_read(st->ssl, data, len)) <= 0)
+ return ssl_set_error(st->ssl, ret);
+
+ return ret;
+}
+
+static int openssl_close(git_stream *stream)
+{
+ openssl_stream *st = (openssl_stream *) stream;
+ int ret;
+
+ if (st->connected && (ret = ssl_teardown(st->ssl)) < 0)
+ return -1;
+
+ st->connected = false;
+
+ return st->owned ? git_stream_close(st->io) : 0;
+}
+
+static void openssl_free(git_stream *stream)
+{
+ openssl_stream *st = (openssl_stream *) stream;
+
+ if (st->owned)
+ git_stream_free(st->io);
+
+ SSL_free(st->ssl);
+ git__free(st->host);
+ git__free(st->cert_info.data);
+ git__free(st);
+}
+
+static int openssl_stream_wrap(
+ git_stream **out,
+ git_stream *in,
+ const char *host,
+ int owned)
+{
+ openssl_stream *st;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(in);
+ GIT_ASSERT_ARG(host);
+
+ st = git__calloc(1, sizeof(openssl_stream));
+ GIT_ERROR_CHECK_ALLOC(st);
+
+ st->io = in;
+ st->owned = owned;
+
+ st->ssl = SSL_new(git__ssl_ctx);
+ if (st->ssl == NULL) {
+ git_error_set(GIT_ERROR_SSL, "failed to create ssl object");
+ git__free(st);
+ return -1;
+ }
+
+ st->host = git__strdup(host);
+ GIT_ERROR_CHECK_ALLOC(st->host);
+
+ st->parent.version = GIT_STREAM_VERSION;
+ st->parent.encrypted = 1;
+ st->parent.proxy_support = git_stream_supports_proxy(st->io);
+ st->parent.connect = openssl_connect;
+ st->parent.certificate = openssl_certificate;
+ st->parent.set_proxy = openssl_set_proxy;
+ st->parent.read = openssl_read;
+ st->parent.write = openssl_write;
+ st->parent.close = openssl_close;
+ st->parent.free = openssl_free;
+
+ *out = (git_stream *) st;
+ return 0;
+}
+
+int git_openssl_stream_wrap(git_stream **out, git_stream *in, const char *host)
+{
+ if (openssl_ensure_initialized() < 0)
+ return -1;
+
+ return openssl_stream_wrap(out, in, host, 0);
+}
+
+int git_openssl_stream_new(git_stream **out, const char *host, const char *port)
+{
+ git_stream *stream = NULL;
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(host);
+ GIT_ASSERT_ARG(port);
+
+ if (openssl_ensure_initialized() < 0)
+ return -1;
+
+ if ((error = git_socket_stream_new(&stream, host, port)) < 0)
+ return error;
+
+ if ((error = openssl_stream_wrap(out, stream, host, 1)) < 0) {
+ git_stream_close(stream);
+ git_stream_free(stream);
+ }
+
+ return error;
+}
+
+int git_openssl__set_cert_location(const char *file, const char *path)
+{
+ if (openssl_ensure_initialized() < 0)
+ return -1;
+
+ if (SSL_CTX_load_verify_locations(git__ssl_ctx, file, path) == 0) {
+ char errmsg[256];
+
+ ERR_error_string_n(ERR_get_error(), errmsg, sizeof(errmsg));
+ git_error_set(GIT_ERROR_SSL, "OpenSSL error: failed to load certificates: %s",
+ errmsg);
+
+ return -1;
+ }
+ return 0;
+}
+
+#else
+
+#include "stream.h"
+#include "git2/sys/openssl.h"
+
+int git_openssl_stream_global_init(void)
+{
+ return 0;
+}
+
+int git_openssl_set_locking(void)
+{
+ git_error_set(GIT_ERROR_SSL, "libgit2 was not built with OpenSSL support");
+ return -1;
+}
+
+#endif
diff --git a/src/libgit2/streams/openssl.h b/src/libgit2/streams/openssl.h
new file mode 100644
index 0000000..89fb60a
--- /dev/null
+++ b/src/libgit2/streams/openssl.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_streams_openssl_h__
+#define INCLUDE_streams_openssl_h__
+
+#include "common.h"
+#include "streams/openssl_legacy.h"
+#include "streams/openssl_dynamic.h"
+
+#include "git2/sys/stream.h"
+
+extern int git_openssl_stream_global_init(void);
+
+#if defined(GIT_OPENSSL) && !defined(GIT_OPENSSL_DYNAMIC)
+# include <openssl/ssl.h>
+# include <openssl/err.h>
+# include <openssl/x509v3.h>
+# include <openssl/bio.h>
+# endif
+
+#ifdef GIT_OPENSSL
+extern int git_openssl__set_cert_location(const char *file, const char *path);
+extern int git_openssl_stream_new(git_stream **out, const char *host, const char *port);
+extern int git_openssl_stream_wrap(git_stream **out, git_stream *in, const char *host);
+#endif
+
+#endif
diff --git a/src/libgit2/streams/openssl_dynamic.c b/src/libgit2/streams/openssl_dynamic.c
new file mode 100644
index 0000000..222c109
--- /dev/null
+++ b/src/libgit2/streams/openssl_dynamic.c
@@ -0,0 +1,313 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "streams/openssl.h"
+#include "streams/openssl_dynamic.h"
+
+#if defined(GIT_OPENSSL) && defined(GIT_OPENSSL_DYNAMIC)
+
+#include "runtime.h"
+
+#include <dlfcn.h>
+
+unsigned char *(*ASN1_STRING_data)(ASN1_STRING *x);
+const unsigned char *(*ASN1_STRING_get0_data)(const ASN1_STRING *x);
+int (*ASN1_STRING_length)(const ASN1_STRING *x);
+int (*ASN1_STRING_to_UTF8)(unsigned char **out, const ASN1_STRING *in);
+int (*ASN1_STRING_type)(const ASN1_STRING *x);
+
+void *(*BIO_get_data)(BIO *a);
+int (*BIO_get_new_index)(void);
+int (*OPENSSL_init_ssl)(uint64_t opts, const void *settings);
+void (*BIO_meth_free)(BIO_METHOD *biom);
+int (*BIO_meth_set_create)(BIO_METHOD *biom, int (*create) (BIO *));
+int (*BIO_meth_set_ctrl)(BIO_METHOD *biom, long (*ctrl) (BIO *, int, long, void *));
+int (*BIO_meth_set_destroy)(BIO_METHOD *biom, int (*destroy) (BIO *));
+int (*BIO_meth_set_gets)(BIO_METHOD *biom, int (*gets) (BIO *, char *, int));
+int (*BIO_meth_set_puts)(BIO_METHOD *biom, int (*puts) (BIO *, const char *));
+int (*BIO_meth_set_read)(BIO_METHOD *biom, int (*read) (BIO *, char *, int));
+int (*BIO_meth_set_write)(BIO_METHOD *biom, int (*write) (BIO *, const char *, int));
+BIO_METHOD *(*BIO_meth_new)(int type, const char *name);
+BIO *(*BIO_new)(const BIO_METHOD *type);
+void (*BIO_set_data)(BIO *a, void *ptr);
+void (*BIO_set_init)(BIO *a, int init);
+
+void (*CRYPTO_free)(void *ptr, const char *file, int line);
+void *(*CRYPTO_malloc)(size_t num, const char *file, int line);
+int (*CRYPTO_num_locks)(void);
+void (*CRYPTO_set_locking_callback)(void (*func)(int mode, int type, const char *file, int line));
+int (*CRYPTO_set_mem_functions)(void *(*m)(size_t bytes), void *(*r)(void *mem, size_t size), void (*f)(void *mem));
+int (*CRYPTO_THREADID_set_callback)(void (*func)(CRYPTO_THREADID *id));
+void (*CRYPTO_THREADID_set_numeric)(CRYPTO_THREADID *id, unsigned long val);
+
+char *(*ERR_error_string)(unsigned long e, char *buf);
+void (*ERR_error_string_n)(unsigned long e, char *buf, size_t len);
+unsigned long (*ERR_get_error)(void);
+
+int (*SSL_connect)(SSL *ssl);
+long (*SSL_ctrl)(SSL *ssl, int cmd, long arg, void *parg);
+void (*SSL_free)(SSL *ssl);
+int (*SSL_get_error)(SSL *ssl, int ret);
+X509 *(*SSL_get_peer_certificate)(const SSL *ssl);
+long (*SSL_get_verify_result)(const SSL *ssl);
+int (*SSL_library_init)(void);
+void (*SSL_load_error_strings)(void);
+SSL *(*SSL_new)(SSL_CTX *ctx);
+int (*SSL_read)(SSL *ssl, const void *buf, int num);
+void (*SSL_set_bio)(SSL *ssl, BIO *rbio, BIO *wbio);
+int (*SSL_shutdown)(SSL *ssl);
+int (*SSL_write)(SSL *ssl, const void *buf, int num);
+
+long (*SSL_CTX_ctrl)(SSL_CTX *ctx, int cmd, long larg, void *parg);
+void (*SSL_CTX_free)(SSL_CTX *ctx);
+SSL_CTX *(*SSL_CTX_new)(const SSL_METHOD *method);
+int (*SSL_CTX_set_cipher_list)(SSL_CTX *ctx, const char *str);
+int (*SSL_CTX_set_default_verify_paths)(SSL_CTX *ctx);
+long (*SSL_CTX_set_options)(SSL_CTX *ctx, long options);
+void (*SSL_CTX_set_verify)(SSL_CTX *ctx, int mode, int (*verify_callback)(int, X509_STORE_CTX *));
+int (*SSL_CTX_load_verify_locations)(SSL_CTX *ctx, const char *CAfile, const char *CApath);
+
+const SSL_METHOD *(*SSLv23_method)(void);
+const SSL_METHOD *(*TLS_method)(void);
+
+ASN1_STRING *(*X509_NAME_ENTRY_get_data)(const X509_NAME_ENTRY *ne);
+X509_NAME_ENTRY *(*X509_NAME_get_entry)(X509_NAME *name, int loc);
+int (*X509_NAME_get_index_by_NID)(X509_NAME *name, int nid, int lastpos);
+void (*X509_free)(X509 *a);
+void *(*X509_get_ext_d2i)(const X509 *x, int nid, int *crit, int *idx);
+X509_NAME *(*X509_get_subject_name)(const X509 *x);
+
+int (*i2d_X509)(X509 *a, unsigned char **ppout);
+
+int (*OPENSSL_sk_num)(const void *sk);
+void *(*OPENSSL_sk_value)(const void *sk, int i);
+void (*OPENSSL_sk_free)(void *sk);
+
+int (*sk_num)(const void *sk);
+void *(*sk_value)(const void *sk, int i);
+void (*sk_free)(void *sk);
+
+static void *openssl_handle;
+
+GIT_INLINE(void *) openssl_sym(int *err, const char *name, bool required)
+{
+ void *symbol;
+
+ /* if we've seen an err, noop to retain it */
+ if (*err)
+ return NULL;
+
+
+ if ((symbol = dlsym(openssl_handle, name)) == NULL && required) {
+ const char *msg = dlerror();
+ git_error_set(GIT_ERROR_SSL, "could not load ssl function '%s': %s", name, msg ? msg : "unknown error");
+ *err = -1;
+ }
+
+ return symbol;
+}
+
+static void dynamic_shutdown(void)
+{
+ dlclose(openssl_handle);
+ openssl_handle = NULL;
+}
+
+int git_openssl_stream_dynamic_init(void)
+{
+ int err = 0;
+
+ if ((openssl_handle = dlopen("libssl.so.1.1", RTLD_NOW)) == NULL &&
+ (openssl_handle = dlopen("libssl.1.1.dylib", RTLD_NOW)) == NULL &&
+ (openssl_handle = dlopen("libssl.so.1.0.0", RTLD_NOW)) == NULL &&
+ (openssl_handle = dlopen("libssl.1.0.0.dylib", RTLD_NOW)) == NULL &&
+ (openssl_handle = dlopen("libssl.so.10", RTLD_NOW)) == NULL &&
+ (openssl_handle = dlopen("libssl.so.3", RTLD_NOW)) == NULL) {
+ git_error_set(GIT_ERROR_SSL, "could not load ssl libraries");
+ return -1;
+ }
+
+ ASN1_STRING_data = (unsigned char *(*)(ASN1_STRING *x))openssl_sym(&err, "ASN1_STRING_data", false);
+ ASN1_STRING_get0_data = (const unsigned char *(*)(const ASN1_STRING *x))openssl_sym(&err, "ASN1_STRING_get0_data", false);
+ ASN1_STRING_length = (int (*)(const ASN1_STRING *))openssl_sym(&err, "ASN1_STRING_length", true);
+ ASN1_STRING_to_UTF8 = (int (*)(unsigned char **, const ASN1_STRING *))openssl_sym(&err, "ASN1_STRING_to_UTF8", true);
+ ASN1_STRING_type = (int (*)(const ASN1_STRING *))openssl_sym(&err, "ASN1_STRING_type", true);
+
+ BIO_get_data = (void *(*)(BIO *))openssl_sym(&err, "BIO_get_data", false);
+ BIO_get_new_index = (int (*)(void))openssl_sym(&err, "BIO_get_new_index", false);
+ BIO_meth_free = (void (*)(BIO_METHOD *))openssl_sym(&err, "BIO_meth_free", false);
+ BIO_meth_new = (BIO_METHOD *(*)(int, const char *))openssl_sym(&err, "BIO_meth_new", false);
+ BIO_meth_set_create = (int (*)(BIO_METHOD *, int (*)(BIO *)))openssl_sym(&err, "BIO_meth_set_create", false);
+ BIO_meth_set_ctrl = (int (*)(BIO_METHOD *, long (*)(BIO *, int, long, void *)))openssl_sym(&err, "BIO_meth_set_ctrl", false);
+ BIO_meth_set_destroy = (int (*)(BIO_METHOD *, int (*)(BIO *)))openssl_sym(&err, "BIO_meth_set_destroy", false);
+ BIO_meth_set_gets = (int (*)(BIO_METHOD *, int (*)(BIO *, char *, int)))openssl_sym(&err, "BIO_meth_set_gets", false);
+ BIO_meth_set_puts = (int (*)(BIO_METHOD *, int (*)(BIO *, const char *)))openssl_sym(&err, "BIO_meth_set_puts", false);
+ BIO_meth_set_read = (int (*)(BIO_METHOD *, int (*)(BIO *, char *, int)))openssl_sym(&err, "BIO_meth_set_read", false);
+ BIO_meth_set_write = (int (*)(BIO_METHOD *, int (*)(BIO *, const char *, int)))openssl_sym(&err, "BIO_meth_set_write", false);
+ BIO_new = (BIO *(*)(const BIO_METHOD *))openssl_sym(&err, "BIO_new", true);
+ BIO_set_data = (void (*)(BIO *a, void *))openssl_sym(&err, "BIO_set_data", false);
+ BIO_set_init = (void (*)(BIO *a, int))openssl_sym(&err, "BIO_set_init", false);
+
+ CRYPTO_free = (void (*)(void *, const char *, int))openssl_sym(&err, "CRYPTO_free", true);
+ CRYPTO_malloc = (void *(*)(size_t, const char *, int))openssl_sym(&err, "CRYPTO_malloc", true);
+ CRYPTO_num_locks = (int (*)(void))openssl_sym(&err, "CRYPTO_num_locks", false);
+ CRYPTO_set_locking_callback = (void (*)(void (*)(int, int, const char *, int)))openssl_sym(&err, "CRYPTO_set_locking_callback", false);
+ CRYPTO_set_mem_functions = (int (*)(void *(*)(size_t), void *(*)(void *, size_t), void (*f)(void *)))openssl_sym(&err, "CRYPTO_set_mem_functions", true);
+
+ CRYPTO_THREADID_set_callback = (int (*)(void (*)(CRYPTO_THREADID *)))openssl_sym(&err, "CRYPTO_THREADID_set_callback", false);
+ CRYPTO_THREADID_set_numeric = (void (*)(CRYPTO_THREADID *, unsigned long))openssl_sym(&err, "CRYPTO_THREADID_set_numeric", false);
+
+ ERR_error_string = (char *(*)(unsigned long, char *))openssl_sym(&err, "ERR_error_string", true);
+ ERR_error_string_n = (void (*)(unsigned long, char *, size_t))openssl_sym(&err, "ERR_error_string_n", true);
+ ERR_get_error = (unsigned long (*)(void))openssl_sym(&err, "ERR_get_error", true);
+
+ OPENSSL_init_ssl = (int (*)(uint64_t opts, const void *settings))openssl_sym(&err, "OPENSSL_init_ssl", false);
+ OPENSSL_sk_num = (int (*)(const void *))openssl_sym(&err, "OPENSSL_sk_num", false);
+ OPENSSL_sk_value = (void *(*)(const void *sk, int i))openssl_sym(&err, "OPENSSL_sk_value", false);
+ OPENSSL_sk_free = (void (*)(void *))openssl_sym(&err, "OPENSSL_sk_free", false);
+
+ sk_num = (int (*)(const void *))openssl_sym(&err, "sk_num", false);
+ sk_value = (void *(*)(const void *sk, int i))openssl_sym(&err, "sk_value", false);
+ sk_free = (void (*)(void *))openssl_sym(&err, "sk_free", false);
+
+ SSL_connect = (int (*)(SSL *))openssl_sym(&err, "SSL_connect", true);
+ SSL_ctrl = (long (*)(SSL *, int, long, void *))openssl_sym(&err, "SSL_ctrl", true);
+ SSL_library_init = (int (*)(void))openssl_sym(&err, "SSL_library_init", false);
+ SSL_free = (void (*)(SSL *))openssl_sym(&err, "SSL_free", true);
+ SSL_get_error = (int (*)(SSL *, int))openssl_sym(&err, "SSL_get_error", true);
+ SSL_get_verify_result = (long (*)(const SSL *ssl))openssl_sym(&err, "SSL_get_verify_result", true);
+ SSL_load_error_strings = (void (*)(void))openssl_sym(&err, "SSL_load_error_strings", false);
+ SSL_new = (SSL *(*)(SSL_CTX *))openssl_sym(&err, "SSL_new", true);
+ SSL_read = (int (*)(SSL *, const void *, int))openssl_sym(&err, "SSL_read", true);
+ SSL_set_bio = (void (*)(SSL *, BIO *, BIO *))openssl_sym(&err, "SSL_set_bio", true);
+ SSL_shutdown = (int (*)(SSL *ssl))openssl_sym(&err, "SSL_shutdown", true);
+ SSL_write = (int (*)(SSL *, const void *, int))openssl_sym(&err, "SSL_write", true);
+
+ if (!(SSL_get_peer_certificate = (X509 *(*)(const SSL *))openssl_sym(&err, "SSL_get_peer_certificate", false))) {
+ SSL_get_peer_certificate = (X509 *(*)(const SSL *))openssl_sym(&err, "SSL_get1_peer_certificate", true);
+ }
+
+ SSL_CTX_ctrl = (long (*)(SSL_CTX *, int, long, void *))openssl_sym(&err, "SSL_CTX_ctrl", true);
+ SSL_CTX_free = (void (*)(SSL_CTX *))openssl_sym(&err, "SSL_CTX_free", true);
+ SSL_CTX_new = (SSL_CTX *(*)(const SSL_METHOD *))openssl_sym(&err, "SSL_CTX_new", true);
+ SSL_CTX_set_cipher_list = (int (*)(SSL_CTX *, const char *))openssl_sym(&err, "SSL_CTX_set_cipher_list", true);
+ SSL_CTX_set_default_verify_paths = (int (*)(SSL_CTX *ctx))openssl_sym(&err, "SSL_CTX_set_default_verify_paths", true);
+ SSL_CTX_set_options = (long (*)(SSL_CTX *, long))openssl_sym(&err, "SSL_CTX_set_options", false);
+ SSL_CTX_set_verify = (void (*)(SSL_CTX *, int, int (*)(int, X509_STORE_CTX *)))openssl_sym(&err, "SSL_CTX_set_verify", true);
+ SSL_CTX_load_verify_locations = (int (*)(SSL_CTX *, const char *, const char *))openssl_sym(&err, "SSL_CTX_load_verify_locations", true);
+
+ SSLv23_method = (const SSL_METHOD *(*)(void))openssl_sym(&err, "SSLv23_method", false);
+ TLS_method = (const SSL_METHOD *(*)(void))openssl_sym(&err, "TLS_method", false);
+
+ X509_NAME_ENTRY_get_data = (ASN1_STRING *(*)(const X509_NAME_ENTRY *))openssl_sym(&err, "X509_NAME_ENTRY_get_data", true);
+ X509_NAME_get_entry = (X509_NAME_ENTRY *(*)(X509_NAME *, int))openssl_sym(&err, "X509_NAME_get_entry", true);
+ X509_NAME_get_index_by_NID = (int (*)(X509_NAME *, int, int))openssl_sym(&err, "X509_NAME_get_index_by_NID", true);
+ X509_free = (void (*)(X509 *))openssl_sym(&err, "X509_free", true);
+ X509_get_ext_d2i = (void *(*)(const X509 *x, int nid, int *crit, int *idx))openssl_sym(&err, "X509_get_ext_d2i", true);
+ X509_get_subject_name = (X509_NAME *(*)(const X509 *))openssl_sym(&err, "X509_get_subject_name", true);
+
+ i2d_X509 = (int (*)(X509 *a, unsigned char **ppout))openssl_sym(&err, "i2d_X509", true);
+
+ if (err)
+ goto on_error;
+
+ /* Add legacy functionality */
+ if (!OPENSSL_init_ssl) {
+ OPENSSL_init_ssl = OPENSSL_init_ssl__legacy;
+
+ if (!SSL_library_init ||
+ !SSL_load_error_strings ||
+ !CRYPTO_num_locks ||
+ !CRYPTO_set_locking_callback ||
+ !CRYPTO_THREADID_set_callback ||
+ !CRYPTO_THREADID_set_numeric) {
+ git_error_set(GIT_ERROR_SSL, "could not load legacy openssl initialization functions");
+ goto on_error;
+ }
+ }
+
+ if (!SSL_CTX_set_options)
+ SSL_CTX_set_options = SSL_CTX_set_options__legacy;
+
+ if (TLS_method)
+ SSLv23_method = TLS_method;
+
+ if (!BIO_meth_new) {
+ BIO_meth_new = BIO_meth_new__legacy;
+ BIO_meth_new = BIO_meth_new__legacy;
+ BIO_meth_free = BIO_meth_free__legacy;
+ BIO_meth_set_write = BIO_meth_set_write__legacy;
+ BIO_meth_set_read = BIO_meth_set_read__legacy;
+ BIO_meth_set_puts = BIO_meth_set_puts__legacy;
+ BIO_meth_set_gets = BIO_meth_set_gets__legacy;
+ BIO_meth_set_ctrl = BIO_meth_set_ctrl__legacy;
+ BIO_meth_set_create = BIO_meth_set_create__legacy;
+ BIO_meth_set_destroy = BIO_meth_set_destroy__legacy;
+ BIO_get_new_index = BIO_get_new_index__legacy;
+ BIO_set_data = BIO_set_data__legacy;
+ BIO_set_init = BIO_set_init__legacy;
+ BIO_get_data = BIO_get_data__legacy;
+ }
+
+ if (!ASN1_STRING_get0_data) {
+ if (!ASN1_STRING_data) {
+ git_error_set(GIT_ERROR_SSL, "could not load legacy openssl string function");
+ goto on_error;
+ }
+
+ ASN1_STRING_get0_data = ASN1_STRING_get0_data__legacy;
+ }
+
+ if ((!OPENSSL_sk_num && !sk_num) ||
+ (!OPENSSL_sk_value && !sk_value) ||
+ (!OPENSSL_sk_free && !sk_free)) {
+ git_error_set(GIT_ERROR_SSL, "could not load legacy openssl stack functions");
+ goto on_error;
+ }
+
+ if (git_runtime_shutdown_register(dynamic_shutdown) != 0)
+ goto on_error;
+
+ return 0;
+
+on_error:
+ dlclose(openssl_handle);
+ return -1;
+}
+
+
+int sk_GENERAL_NAME_num(const GENERAL_NAME *sk)
+{
+ if (OPENSSL_sk_num)
+ return OPENSSL_sk_num(sk);
+ else if (sk_num)
+ return sk_num(sk);
+
+ GIT_ASSERT_WITH_RETVAL(false, 0);
+ return 0;
+}
+
+GENERAL_NAME *sk_GENERAL_NAME_value(const GENERAL_NAME *sk, int i)
+{
+ if (OPENSSL_sk_value)
+ return OPENSSL_sk_value(sk, i);
+ else if (sk_value)
+ return sk_value(sk, i);
+
+ GIT_ASSERT_WITH_RETVAL(false, NULL);
+ return NULL;
+}
+
+void GENERAL_NAMES_free(GENERAL_NAME *sk)
+{
+ if (OPENSSL_sk_free)
+ OPENSSL_sk_free(sk);
+ else if (sk_free)
+ sk_free(sk);
+}
+
+#endif /* GIT_OPENSSL && GIT_OPENSSL_DYNAMIC */
diff --git a/src/libgit2/streams/openssl_dynamic.h b/src/libgit2/streams/openssl_dynamic.h
new file mode 100644
index 0000000..a996919
--- /dev/null
+++ b/src/libgit2/streams/openssl_dynamic.h
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are adhered to. The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * "This product includes cryptographic software written by
+ * Eric Young (eay@cryptsoft.com)"
+ * The word 'cryptographic' can be left out if the routines from the library
+ * being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ * the apps directory (application code) you must include an acknowledgement:
+ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publicly available version or
+ * derivative of this code cannot be changed. i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+/* ====================================================================
+ * Copyright (c) 1998-2007 The OpenSSL Project. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission. For written permission, please contact
+ * openssl-core@openssl.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ * nor may "OpenSSL" appear in their names without prior written
+ * permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the OpenSSL Project
+ * for use in the OpenSSL Toolkit (http://www.openssl.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com). This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ */
+/* ====================================================================
+ * Copyright 2002 Sun Microsystems, Inc. ALL RIGHTS RESERVED.
+ * ECC cipher suite support in OpenSSL originally developed by
+ * SUN MICROSYSTEMS, INC., and contributed to the OpenSSL project.
+ */
+/* ====================================================================
+ * Copyright 2005 Nokia. All rights reserved.
+ *
+ * The portions of the attached software ("Contribution") is developed by
+ * Nokia Corporation and is licensed pursuant to the OpenSSL open source
+ * license.
+ *
+ * The Contribution, originally written by Mika Kousa and Pasi Eronen of
+ * Nokia Corporation, consists of the "PSK" (Pre-Shared Key) ciphersuites
+ * support (see RFC 4279) to OpenSSL.
+ *
+ * No patent licenses or other rights except those expressly stated in
+ * the OpenSSL open source license shall be deemed granted or received
+ * expressly, by implication, estoppel, or otherwise.
+ *
+ * No assurances are provided by Nokia that the Contribution does not
+ * infringe the patent or other intellectual property rights of any third
+ * party or that the license provides you with all the necessary rights
+ * to make use of the Contribution.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. IN
+ * ADDITION TO THE DISCLAIMERS INCLUDED IN THE LICENSE, NOKIA
+ * SPECIFICALLY DISCLAIMS ANY LIABILITY FOR CLAIMS BROUGHT BY YOU OR ANY
+ * OTHER ENTITY BASED ON INFRINGEMENT OF INTELLECTUAL PROPERTY RIGHTS OR
+ * OTHERWISE.
+ */
+
+#ifndef INCLUDE_streams_openssl_dynamic_h__
+#define INCLUDE_streams_openssl_dynamic_h__
+
+#ifdef GIT_OPENSSL_DYNAMIC
+
+# define BIO_CTRL_FLUSH 11
+
+# define BIO_TYPE_SOURCE_SINK 0x0400
+
+# define CRYPTO_LOCK 1
+
+# define GEN_DNS 2
+# define GEN_IPADD 7
+
+# define NID_commonName 13
+# define NID_subject_alt_name 85
+
+# define SSL_VERIFY_NONE 0x00
+
+# define SSL_CTRL_OPTIONS 32
+# define SSL_CTRL_MODE 33
+# define SSL_CTRL_SET_TLSEXT_HOSTNAME 55
+
+# define SSL_ERROR_NONE 0
+# define SSL_ERROR_SSL 1
+# define SSL_ERROR_WANT_READ 2
+# define SSL_ERROR_WANT_WRITE 3
+# define SSL_ERROR_WANT_X509_LOOKUP 4
+# define SSL_ERROR_SYSCALL 5
+# define SSL_ERROR_ZERO_RETURN 6
+# define SSL_ERROR_WANT_CONNECT 7
+# define SSL_ERROR_WANT_ACCEPT 8
+
+# define SSL_OP_NO_COMPRESSION 0x00020000L
+# define SSL_OP_NO_SSLv2 0x01000000L
+# define SSL_OP_NO_SSLv3 0x02000000L
+
+# define SSL_MODE_AUTO_RETRY 0x00000004L
+
+# define TLSEXT_NAMETYPE_host_name 0
+
+# define V_ASN1_UTF8STRING 12
+
+# define X509_V_OK 0
+
+/* Most of the OpenSSL types are mercifully opaque, so we can treat them like `void *` */
+typedef struct bio_st BIO;
+typedef struct bio_method_st BIO_METHOD;
+typedef void bio_info_cb;
+typedef void * CRYPTO_EX_DATA;
+typedef void CRYPTO_THREADID;
+typedef void GENERAL_NAMES;
+typedef void SSL;
+typedef void SSL_CTX;
+typedef void SSL_METHOD;
+typedef void X509;
+typedef void X509_NAME;
+typedef void X509_NAME_ENTRY;
+typedef void X509_STORE_CTX;
+
+typedef struct {
+ int length;
+ int type;
+ unsigned char *data;
+ long flags;
+} ASN1_STRING;
+
+typedef struct {
+ int type;
+ union {
+ char *ptr;
+ ASN1_STRING *ia5;
+ } d;
+} GENERAL_NAME;
+
+struct bio_st {
+ BIO_METHOD *method;
+ /* bio, mode, argp, argi, argl, ret */
+ long (*callback) (struct bio_st *, int, const char *, int, long, long);
+ char *cb_arg; /* first argument for the callback */
+ int init;
+ int shutdown;
+ int flags; /* extra storage */
+ int retry_reason;
+ int num;
+ void *ptr;
+ struct bio_st *next_bio; /* used by filter BIOs */
+ struct bio_st *prev_bio; /* used by filter BIOs */
+ int references;
+ unsigned long num_read;
+ unsigned long num_write;
+ CRYPTO_EX_DATA ex_data;
+};
+
+struct bio_method_st {
+ int type;
+ const char *name;
+ int (*bwrite) (BIO *, const char *, int);
+ int (*bread) (BIO *, char *, int);
+ int (*bputs) (BIO *, const char *);
+ int (*bgets) (BIO *, char *, int);
+ long (*ctrl) (BIO *, int, long, void *);
+ int (*create) (BIO *);
+ int (*destroy) (BIO *);
+ long (*callback_ctrl) (BIO *, int, bio_info_cb *);
+};
+
+extern unsigned char *(*ASN1_STRING_data)(ASN1_STRING *x);
+extern const unsigned char *(*ASN1_STRING_get0_data)(const ASN1_STRING *x);
+extern int (*ASN1_STRING_length)(const ASN1_STRING *x);
+extern int (*ASN1_STRING_to_UTF8)(unsigned char **out, const ASN1_STRING *in);
+extern int (*ASN1_STRING_type)(const ASN1_STRING *x);
+
+extern void *(*BIO_get_data)(BIO *a);
+extern int (*BIO_get_new_index)(void);
+extern int (*OPENSSL_init_ssl)(uint64_t opts, const void *settings);
+extern void (*BIO_meth_free)(BIO_METHOD *biom);
+extern int (*BIO_meth_set_create)(BIO_METHOD *biom, int (*create) (BIO *));
+extern int (*BIO_meth_set_ctrl)(BIO_METHOD *biom, long (*ctrl) (BIO *, int, long, void *));
+extern int (*BIO_meth_set_destroy)(BIO_METHOD *biom, int (*destroy) (BIO *));
+extern int (*BIO_meth_set_gets)(BIO_METHOD *biom, int (*gets) (BIO *, char *, int));
+extern int (*BIO_meth_set_puts)(BIO_METHOD *biom, int (*puts) (BIO *, const char *));
+extern int (*BIO_meth_set_read)(BIO_METHOD *biom, int (*read) (BIO *, char *, int));
+extern int (*BIO_meth_set_write)(BIO_METHOD *biom, int (*write) (BIO *, const char *, int));
+extern BIO_METHOD *(*BIO_meth_new)(int type, const char *name);
+extern BIO *(*BIO_new)(const BIO_METHOD *type);
+extern void (*BIO_set_data)(BIO *a, void *ptr);
+extern void (*BIO_set_init)(BIO *a, int init);
+
+extern void (*CRYPTO_free)(void *ptr, const char *file, int line);
+extern void *(*CRYPTO_malloc)(size_t num, const char *file, int line);
+extern int (*CRYPTO_num_locks)(void);
+extern void (*CRYPTO_set_locking_callback)(void (*func)(int mode, int type, const char *file, int line));
+extern int (*CRYPTO_set_mem_functions)(void *(*m)(size_t bytes), void *(*r)(void *mem, size_t size), void (*f)(void *mem));
+extern int (*CRYPTO_THREADID_set_callback)(void (*func)(CRYPTO_THREADID *id));
+extern void (*CRYPTO_THREADID_set_numeric)(CRYPTO_THREADID *id, unsigned long val);
+
+extern char *(*ERR_error_string)(unsigned long e, char *buf);
+extern void (*ERR_error_string_n)(unsigned long e, char *buf, size_t len);
+extern unsigned long (*ERR_get_error)(void);
+
+# define OPENSSL_malloc(num) CRYPTO_malloc(num, __FILE__, __LINE__)
+# define OPENSSL_free(addr) CRYPTO_free(addr, __FILE__, __LINE__)
+
+extern int (*SSL_connect)(SSL *ssl);
+extern long (*SSL_ctrl)(SSL *ssl, int cmd, long arg, void *parg);
+extern void (*SSL_free)(SSL *ssl);
+extern int (*SSL_get_error)(SSL *ssl, int ret);
+extern X509 *(*SSL_get_peer_certificate)(const SSL *ssl);
+extern long (*SSL_get_verify_result)(const SSL *ssl);
+extern int (*SSL_library_init)(void);
+extern void (*SSL_load_error_strings)(void);
+extern SSL *(*SSL_new)(SSL_CTX *ctx);
+extern int (*SSL_read)(SSL *ssl, const void *buf, int num);
+extern void (*SSL_set_bio)(SSL *ssl, BIO *rbio, BIO *wbio);
+extern int (*SSL_shutdown)(SSL *ssl);
+extern int (*SSL_write)(SSL *ssl, const void *buf, int num);
+
+# define SSL_set_tlsext_host_name(s, name) SSL_ctrl((s), SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, (char *)(name));
+
+extern long (*SSL_CTX_ctrl)(SSL_CTX *ctx, int cmd, long larg, void *parg);
+extern void (*SSL_CTX_free)(SSL_CTX *ctx);
+extern SSL_CTX *(*SSL_CTX_new)(const SSL_METHOD *method);
+extern int (*SSL_CTX_set_cipher_list)(SSL_CTX *ctx, const char *str);
+extern int (*SSL_CTX_set_default_verify_paths)(SSL_CTX *ctx);
+extern long (*SSL_CTX_set_options)(SSL_CTX *ctx, long options);
+extern void (*SSL_CTX_set_verify)(SSL_CTX *ctx, int mode, int (*verify_callback)(int, X509_STORE_CTX *));
+extern int (*SSL_CTX_load_verify_locations)(SSL_CTX *ctx, const char *CAfile, const char *CApath);
+
+# define SSL_CTX_set_mode(ctx, mode) SSL_CTX_ctrl((ctx), SSL_CTRL_MODE, (mode), NULL);
+
+extern const SSL_METHOD *(*SSLv23_method)(void);
+extern const SSL_METHOD *(*TLS_method)(void);
+
+extern ASN1_STRING *(*X509_NAME_ENTRY_get_data)(const X509_NAME_ENTRY *ne);
+extern X509_NAME_ENTRY *(*X509_NAME_get_entry)(X509_NAME *name, int loc);
+extern int (*X509_NAME_get_index_by_NID)(X509_NAME *name, int nid, int lastpos);
+extern void (*X509_free)(X509 *a);
+extern void *(*X509_get_ext_d2i)(const X509 *x, int nid, int *crit, int *idx);
+extern X509_NAME *(*X509_get_subject_name)(const X509 *x);
+
+extern int (*i2d_X509)(X509 *a, unsigned char **ppout);
+
+extern int (*OPENSSL_sk_num)(const void *sk);
+extern void *(*OPENSSL_sk_value)(const void *sk, int i);
+extern void (*OPENSSL_sk_free)(void *sk);
+
+extern int (*sk_num)(const void *sk);
+extern void *(*sk_value)(const void *sk, int i);
+extern void (*sk_free)(void *sk);
+
+extern int sk_GENERAL_NAME_num(const GENERAL_NAME *sk);
+extern GENERAL_NAME *sk_GENERAL_NAME_value(const GENERAL_NAME *sk, int i);
+extern void GENERAL_NAMES_free(GENERAL_NAME *sk);
+
+extern int git_openssl_stream_dynamic_init(void);
+
+#endif /* GIT_OPENSSL_DYNAMIC */
+
+#endif
diff --git a/src/libgit2/streams/openssl_legacy.c b/src/libgit2/streams/openssl_legacy.c
new file mode 100644
index 0000000..e61e6ef
--- /dev/null
+++ b/src/libgit2/streams/openssl_legacy.c
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "streams/openssl.h"
+#include "streams/openssl_legacy.h"
+
+#include "runtime.h"
+#include "git2/sys/openssl.h"
+
+#if defined(GIT_OPENSSL) && !defined(GIT_OPENSSL_DYNAMIC)
+# include <openssl/ssl.h>
+# include <openssl/err.h>
+# include <openssl/x509v3.h>
+# include <openssl/bio.h>
+#endif
+
+#if defined(GIT_OPENSSL_LEGACY) || defined(GIT_OPENSSL_DYNAMIC)
+
+/*
+ * OpenSSL 1.1 made BIO opaque so we have to use functions to interact with it
+ * which do not exist in previous versions. We define these inline functions so
+ * we can program against the interface instead of littering the implementation
+ * with ifdefs. We do the same for OPENSSL_init_ssl.
+ */
+
+int OPENSSL_init_ssl__legacy(uint64_t opts, const void *settings)
+{
+ GIT_UNUSED(opts);
+ GIT_UNUSED(settings);
+ SSL_load_error_strings();
+ SSL_library_init();
+ return 0;
+}
+
+BIO_METHOD *BIO_meth_new__legacy(int type, const char *name)
+{
+ BIO_METHOD *meth = git__calloc(1, sizeof(BIO_METHOD));
+ if (!meth) {
+ return NULL;
+ }
+
+ meth->type = type;
+ meth->name = name;
+
+ return meth;
+}
+
+void BIO_meth_free__legacy(BIO_METHOD *biom)
+{
+ git__free(biom);
+}
+
+int BIO_meth_set_write__legacy(BIO_METHOD *biom, int (*write) (BIO *, const char *, int))
+{
+ biom->bwrite = write;
+ return 1;
+}
+
+int BIO_meth_set_read__legacy(BIO_METHOD *biom, int (*read) (BIO *, char *, int))
+{
+ biom->bread = read;
+ return 1;
+}
+
+int BIO_meth_set_puts__legacy(BIO_METHOD *biom, int (*puts) (BIO *, const char *))
+{
+ biom->bputs = puts;
+ return 1;
+}
+
+int BIO_meth_set_gets__legacy(BIO_METHOD *biom, int (*gets) (BIO *, char *, int))
+
+{
+ biom->bgets = gets;
+ return 1;
+}
+
+int BIO_meth_set_ctrl__legacy(BIO_METHOD *biom, long (*ctrl) (BIO *, int, long, void *))
+{
+ biom->ctrl = ctrl;
+ return 1;
+}
+
+int BIO_meth_set_create__legacy(BIO_METHOD *biom, int (*create) (BIO *))
+{
+ biom->create = create;
+ return 1;
+}
+
+int BIO_meth_set_destroy__legacy(BIO_METHOD *biom, int (*destroy) (BIO *))
+{
+ biom->destroy = destroy;
+ return 1;
+}
+
+int BIO_get_new_index__legacy(void)
+{
+ /* This exists as of 1.1 so before we'd just have 0 */
+ return 0;
+}
+
+void BIO_set_init__legacy(BIO *b, int init)
+{
+ b->init = init;
+}
+
+void BIO_set_data__legacy(BIO *a, void *ptr)
+{
+ a->ptr = ptr;
+}
+
+void *BIO_get_data__legacy(BIO *a)
+{
+ return a->ptr;
+}
+
+const unsigned char *ASN1_STRING_get0_data__legacy(const ASN1_STRING *x)
+{
+ return ASN1_STRING_data((ASN1_STRING *)x);
+}
+
+long SSL_CTX_set_options__legacy(SSL_CTX *ctx, long op)
+{
+ return SSL_CTX_ctrl(ctx, SSL_CTRL_OPTIONS, op, NULL);
+}
+
+# if defined(GIT_THREADS)
+static git_mutex *openssl_locks;
+
+static void openssl_locking_function(int mode, int n, const char *file, int line)
+{
+ int lock;
+
+ GIT_UNUSED(file);
+ GIT_UNUSED(line);
+
+ lock = mode & CRYPTO_LOCK;
+
+ if (lock)
+ (void)git_mutex_lock(&openssl_locks[n]);
+ else
+ git_mutex_unlock(&openssl_locks[n]);
+}
+
+static void shutdown_ssl_locking(void)
+{
+ int num_locks, i;
+
+ num_locks = CRYPTO_num_locks();
+ CRYPTO_set_locking_callback(NULL);
+
+ for (i = 0; i < num_locks; ++i)
+ git_mutex_free(&openssl_locks[i]);
+ git__free(openssl_locks);
+}
+
+static void threadid_cb(CRYPTO_THREADID *threadid)
+{
+ GIT_UNUSED(threadid);
+ CRYPTO_THREADID_set_numeric(threadid, git_thread_currentid());
+}
+
+int git_openssl_set_locking(void)
+{
+ int num_locks, i;
+
+#ifndef GIT_THREADS
+ git_error_set(GIT_ERROR_THREAD, "libgit2 was not built with threads");
+ return -1;
+#endif
+
+#ifdef GIT_OPENSSL_DYNAMIC
+ /*
+ * This function is required on legacy versions of OpenSSL; when building
+ * with dynamically-loaded OpenSSL, we detect whether we loaded it or not.
+ */
+ if (!CRYPTO_set_locking_callback)
+ return 0;
+#endif
+
+ CRYPTO_THREADID_set_callback(threadid_cb);
+
+ num_locks = CRYPTO_num_locks();
+ openssl_locks = git__calloc(num_locks, sizeof(git_mutex));
+ GIT_ERROR_CHECK_ALLOC(openssl_locks);
+
+ for (i = 0; i < num_locks; i++) {
+ if (git_mutex_init(&openssl_locks[i]) != 0) {
+ git_error_set(GIT_ERROR_SSL, "failed to initialize openssl locks");
+ return -1;
+ }
+ }
+
+ CRYPTO_set_locking_callback(openssl_locking_function);
+ return git_runtime_shutdown_register(shutdown_ssl_locking);
+}
+#endif /* GIT_THREADS */
+
+#endif /* GIT_OPENSSL_LEGACY || GIT_OPENSSL_DYNAMIC */
diff --git a/src/libgit2/streams/openssl_legacy.h b/src/libgit2/streams/openssl_legacy.h
new file mode 100644
index 0000000..e6dae95
--- /dev/null
+++ b/src/libgit2/streams/openssl_legacy.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_streams_openssl_legacy_h__
+#define INCLUDE_streams_openssl_legacy_h__
+
+#include "streams/openssl_dynamic.h"
+
+#if defined(GIT_OPENSSL) && !defined(GIT_OPENSSL_DYNAMIC)
+# include <openssl/ssl.h>
+# include <openssl/err.h>
+# include <openssl/x509v3.h>
+# include <openssl/bio.h>
+
+# if (defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER < 0x10100000L) || \
+ (defined(LIBRESSL_VERSION_NUMBER) && LIBRESSL_VERSION_NUMBER < 0x20700000L)
+# define GIT_OPENSSL_LEGACY
+# endif
+#endif
+
+#if defined(GIT_OPENSSL_LEGACY) && !defined(GIT_OPENSSL_DYNAMIC)
+# define OPENSSL_init_ssl OPENSSL_init_ssl__legacy
+# define BIO_meth_new BIO_meth_new__legacy
+# define BIO_meth_free BIO_meth_free__legacy
+# define BIO_meth_set_write BIO_meth_set_write__legacy
+# define BIO_meth_set_read BIO_meth_set_read__legacy
+# define BIO_meth_set_puts BIO_meth_set_puts__legacy
+# define BIO_meth_set_gets BIO_meth_set_gets__legacy
+# define BIO_meth_set_ctrl BIO_meth_set_ctrl__legacy
+# define BIO_meth_set_create BIO_meth_set_create__legacy
+# define BIO_meth_set_destroy BIO_meth_set_destroy__legacy
+# define BIO_get_new_index BIO_get_new_index__legacy
+# define BIO_set_data BIO_set_data__legacy
+# define BIO_set_init BIO_set_init__legacy
+# define BIO_get_data BIO_get_data__legacy
+# define ASN1_STRING_get0_data ASN1_STRING_get0_data__legacy
+#endif
+
+#if defined(GIT_OPENSSL_LEGACY) || defined(GIT_OPENSSL_DYNAMIC)
+
+extern int OPENSSL_init_ssl__legacy(uint64_t opts, const void *settings);
+extern BIO_METHOD *BIO_meth_new__legacy(int type, const char *name);
+extern void BIO_meth_free__legacy(BIO_METHOD *biom);
+extern int BIO_meth_set_write__legacy(BIO_METHOD *biom, int (*write) (BIO *, const char *, int));
+extern int BIO_meth_set_read__legacy(BIO_METHOD *biom, int (*read) (BIO *, char *, int));
+extern int BIO_meth_set_puts__legacy(BIO_METHOD *biom, int (*puts) (BIO *, const char *));
+extern int BIO_meth_set_gets__legacy(BIO_METHOD *biom, int (*gets) (BIO *, char *, int));
+extern int BIO_meth_set_ctrl__legacy(BIO_METHOD *biom, long (*ctrl) (BIO *, int, long, void *));
+extern int BIO_meth_set_create__legacy(BIO_METHOD *biom, int (*create) (BIO *));
+extern int BIO_meth_set_destroy__legacy(BIO_METHOD *biom, int (*destroy) (BIO *));
+extern int BIO_get_new_index__legacy(void);
+extern void BIO_set_data__legacy(BIO *a, void *ptr);
+extern void BIO_set_init__legacy(BIO *b, int init);
+extern void *BIO_get_data__legacy(BIO *a);
+extern const unsigned char *ASN1_STRING_get0_data__legacy(const ASN1_STRING *x);
+extern long SSL_CTX_set_options__legacy(SSL_CTX *ctx, long op);
+
+#endif
+
+#endif
diff --git a/src/libgit2/streams/registry.c b/src/libgit2/streams/registry.c
new file mode 100644
index 0000000..e60e1cd
--- /dev/null
+++ b/src/libgit2/streams/registry.c
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+
+#include "streams/registry.h"
+
+#include "runtime.h"
+#include "streams/tls.h"
+#include "streams/mbedtls.h"
+#include "streams/openssl.h"
+#include "streams/stransport.h"
+
+struct stream_registry {
+ git_rwlock lock;
+ git_stream_registration callbacks;
+ git_stream_registration tls_callbacks;
+};
+
+static struct stream_registry stream_registry;
+
+static void shutdown_stream_registry(void)
+{
+ git_rwlock_free(&stream_registry.lock);
+}
+
+int git_stream_registry_global_init(void)
+{
+ if (git_rwlock_init(&stream_registry.lock) < 0)
+ return -1;
+
+ return git_runtime_shutdown_register(shutdown_stream_registry);
+}
+
+GIT_INLINE(void) stream_registration_cpy(
+ git_stream_registration *target,
+ git_stream_registration *src)
+{
+ if (src)
+ memcpy(target, src, sizeof(git_stream_registration));
+ else
+ memset(target, 0, sizeof(git_stream_registration));
+}
+
+int git_stream_registry_lookup(git_stream_registration *out, git_stream_t type)
+{
+ git_stream_registration *target;
+ int error = GIT_ENOTFOUND;
+
+ GIT_ASSERT_ARG(out);
+
+ switch(type) {
+ case GIT_STREAM_STANDARD:
+ target = &stream_registry.callbacks;
+ break;
+ case GIT_STREAM_TLS:
+ target = &stream_registry.tls_callbacks;
+ break;
+ default:
+ git_error_set(GIT_ERROR_INVALID, "invalid stream type");
+ return -1;
+ }
+
+ if (git_rwlock_rdlock(&stream_registry.lock) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to lock stream registry");
+ return -1;
+ }
+
+ if (target->init) {
+ stream_registration_cpy(out, target);
+ error = 0;
+ }
+
+ git_rwlock_rdunlock(&stream_registry.lock);
+ return error;
+}
+
+int git_stream_register(git_stream_t type, git_stream_registration *registration)
+{
+ GIT_ASSERT(!registration || registration->init);
+
+ GIT_ERROR_CHECK_VERSION(registration, GIT_STREAM_VERSION, "stream_registration");
+
+ if (git_rwlock_wrlock(&stream_registry.lock) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to lock stream registry");
+ return -1;
+ }
+
+ if ((type & GIT_STREAM_STANDARD) == GIT_STREAM_STANDARD)
+ stream_registration_cpy(&stream_registry.callbacks, registration);
+
+ if ((type & GIT_STREAM_TLS) == GIT_STREAM_TLS)
+ stream_registration_cpy(&stream_registry.tls_callbacks, registration);
+
+ git_rwlock_wrunlock(&stream_registry.lock);
+ return 0;
+}
+
+#ifndef GIT_DEPRECATE_HARD
+int git_stream_register_tls(
+ int GIT_CALLBACK(ctor)(git_stream **out, const char *host, const char *port))
+{
+ git_stream_registration registration = {0};
+
+ if (ctor) {
+ registration.version = GIT_STREAM_VERSION;
+ registration.init = ctor;
+ registration.wrap = NULL;
+
+ return git_stream_register(GIT_STREAM_TLS, &registration);
+ } else {
+ return git_stream_register(GIT_STREAM_TLS, NULL);
+ }
+}
+#endif
diff --git a/src/libgit2/streams/registry.h b/src/libgit2/streams/registry.h
new file mode 100644
index 0000000..adc2b8b
--- /dev/null
+++ b/src/libgit2/streams/registry.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_streams_registry_h__
+#define INCLUDE_streams_registry_h__
+
+#include "common.h"
+#include "git2/sys/stream.h"
+
+/** Configure stream registry. */
+int git_stream_registry_global_init(void);
+
+/** Lookup a stream registration. */
+extern int git_stream_registry_lookup(git_stream_registration *out, git_stream_t type);
+
+#endif
diff --git a/src/libgit2/streams/schannel.c b/src/libgit2/streams/schannel.c
new file mode 100644
index 0000000..f096158
--- /dev/null
+++ b/src/libgit2/streams/schannel.c
@@ -0,0 +1,715 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "streams/schannel.h"
+
+#ifdef GIT_SCHANNEL
+
+#define SECURITY_WIN32
+
+#include <security.h>
+#include <schannel.h>
+#include <sspi.h>
+
+#include "stream.h"
+#include "streams/socket.h"
+
+#ifndef SP_PROT_TLS1_2_CLIENT
+# define SP_PROT_TLS1_2_CLIENT 2048
+#endif
+
+#ifndef SP_PROT_TLS1_3_CLIENT
+# define SP_PROT_TLS1_3_CLIENT 8192
+#endif
+
+#ifndef SECBUFFER_ALERT
+# define SECBUFFER_ALERT 17
+#endif
+
+#define READ_BLOCKSIZE (16 * 1024)
+
+typedef enum {
+ STATE_NONE = 0,
+ STATE_CRED = 1,
+ STATE_CONTEXT = 2,
+ STATE_CERTIFICATE = 3
+} schannel_state;
+
+typedef struct {
+ git_stream parent;
+ git_stream *io;
+ int owned;
+ bool connected;
+ wchar_t *host_w;
+
+ schannel_state state;
+
+ CredHandle cred;
+ CtxtHandle context;
+ SecPkgContext_StreamSizes stream_sizes;
+
+ CERT_CONTEXT *certificate;
+ const CERT_CHAIN_CONTEXT *cert_chain;
+ git_cert_x509 x509;
+
+ git_str plaintext_in;
+ git_str ciphertext_in;
+} schannel_stream;
+
+static int connect_context(schannel_stream *st)
+{
+ SCHANNEL_CRED cred = { 0 };
+ SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
+ DWORD context_flags;
+ static size_t MAX_RETRIES = 1024;
+ size_t retries;
+ ssize_t read_len;
+ int error = 0;
+
+ if (st->owned && (error = git_stream_connect(st->io)) < 0)
+ return error;
+
+ cred.dwVersion = SCHANNEL_CRED_VERSION;
+ cred.dwFlags = SCH_CRED_IGNORE_NO_REVOCATION_CHECK |
+ SCH_CRED_IGNORE_REVOCATION_OFFLINE |
+ SCH_CRED_MANUAL_CRED_VALIDATION |
+ SCH_CRED_NO_DEFAULT_CREDS |
+ SCH_CRED_NO_SERVERNAME_CHECK;
+ cred.grbitEnabledProtocols = SP_PROT_TLS1_2_CLIENT |
+ SP_PROT_TLS1_3_CLIENT;
+
+ if (AcquireCredentialsHandleW(NULL, SCHANNEL_NAME_W,
+ SECPKG_CRED_OUTBOUND, NULL, &cred, NULL,
+ NULL, &st->cred, NULL) != SEC_E_OK) {
+ git_error_set(GIT_ERROR_OS, "could not acquire credentials handle");
+ return -1;
+ }
+
+ st->state = STATE_CRED;
+
+ context_flags = ISC_REQ_ALLOCATE_MEMORY |
+ ISC_REQ_CONFIDENTIALITY |
+ ISC_REQ_REPLAY_DETECT |
+ ISC_REQ_SEQUENCE_DETECT |
+ ISC_REQ_STREAM;
+
+ for (retries = 0; retries < MAX_RETRIES; retries++) {
+ SecBuffer input_buf[] = {
+ { (unsigned long)st->ciphertext_in.size,
+ SECBUFFER_TOKEN,
+ st->ciphertext_in.size ? st->ciphertext_in.ptr : NULL },
+ { 0, SECBUFFER_EMPTY, NULL }
+ };
+ SecBuffer output_buf[] = { { 0, SECBUFFER_TOKEN, NULL },
+ { 0, SECBUFFER_ALERT, NULL } };
+
+ SecBufferDesc input_buf_desc = { SECBUFFER_VERSION, 2, input_buf };
+ SecBufferDesc output_buf_desc = { SECBUFFER_VERSION, 2, output_buf };
+
+ status = InitializeSecurityContextW(&st->cred,
+ retries ? &st->context : NULL, st->host_w,
+ context_flags, 0, 0, retries ? &input_buf_desc : NULL, 0,
+ retries ? NULL : &st->context, &output_buf_desc,
+ &context_flags, NULL);
+
+ if (status == SEC_E_OK || status == SEC_I_CONTINUE_NEEDED) {
+ st->state = STATE_CONTEXT;
+
+ if (output_buf[0].cbBuffer > 0) {
+ error = git_stream__write_full(st->io,
+ output_buf[0].pvBuffer,
+ output_buf[0].cbBuffer, 0);
+
+ FreeContextBuffer(output_buf[0].pvBuffer);
+ }
+
+ /* handle any leftover, unprocessed data */
+ if (input_buf[1].BufferType == SECBUFFER_EXTRA) {
+ GIT_ASSERT(st->ciphertext_in.size > input_buf[1].cbBuffer);
+
+ git_str_consume_bytes(&st->ciphertext_in,
+ st->ciphertext_in.size - input_buf[1].cbBuffer);
+ } else {
+ git_str_clear(&st->ciphertext_in);
+ }
+
+ if (error < 0 || status == SEC_E_OK)
+ break;
+ } else if (status == SEC_E_INCOMPLETE_MESSAGE) {
+ /* we need additional data from the client; */
+ if (git_str_grow_by(&st->ciphertext_in, READ_BLOCKSIZE) < 0) {
+ error = -1;
+ break;
+ }
+
+ if ((read_len = git_stream_read(st->io,
+ st->ciphertext_in.ptr + st->ciphertext_in.size,
+ (st->ciphertext_in.asize - st->ciphertext_in.size))) < 0) {
+ error = -1;
+ break;
+ }
+
+ GIT_ASSERT((size_t)read_len <=
+ st->ciphertext_in.asize - st->ciphertext_in.size);
+ st->ciphertext_in.size += read_len;
+ } else {
+ git_error_set(GIT_ERROR_OS,
+ "could not initialize security context");
+ error = -1;
+ break;
+ }
+
+ GIT_ASSERT(st->ciphertext_in.size < ULONG_MAX);
+ }
+
+ if (retries == MAX_RETRIES) {
+ git_error_set(GIT_ERROR_SSL,
+ "could not initialize security context: too many retries");
+ error = -1;
+ }
+
+ if (!error) {
+ if (QueryContextAttributesW(&st->context,
+ SECPKG_ATTR_STREAM_SIZES,
+ &st->stream_sizes) != SEC_E_OK) {
+ git_error_set(GIT_ERROR_SSL,
+ "could not query stream sizes");
+ error = -1;
+ }
+ }
+
+ return error;
+}
+
+static int set_certificate_lookup_error(DWORD status)
+{
+ switch (status) {
+ case CERT_TRUST_IS_NOT_TIME_VALID:
+ git_error_set(GIT_ERROR_SSL,
+ "certificate is expired or not yet valid");
+ break;
+ case CERT_TRUST_IS_REVOKED:
+ git_error_set(GIT_ERROR_SSL, "certificate is revoked");
+ break;
+ case CERT_TRUST_IS_NOT_SIGNATURE_VALID:
+ case CERT_TRUST_IS_NOT_VALID_FOR_USAGE:
+ case CERT_TRUST_INVALID_EXTENSION:
+ case CERT_TRUST_INVALID_POLICY_CONSTRAINTS:
+ case CERT_TRUST_INVALID_BASIC_CONSTRAINTS:
+ case CERT_TRUST_INVALID_NAME_CONSTRAINTS:
+ case CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT:
+ case CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT:
+ case CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT:
+ case CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT:
+ case CERT_TRUST_NO_ISSUANCE_CHAIN_POLICY:
+ case CERT_TRUST_HAS_NOT_SUPPORTED_CRITICAL_EXT:
+ git_error_set(GIT_ERROR_SSL, "certificate is not valid");
+ break;
+ case CERT_TRUST_IS_UNTRUSTED_ROOT:
+ case CERT_TRUST_IS_CYCLIC:
+ case CERT_TRUST_IS_EXPLICIT_DISTRUST:
+ git_error_set(GIT_ERROR_SSL, "certificate is not trusted");
+ break;
+ case CERT_TRUST_REVOCATION_STATUS_UNKNOWN:
+ git_error_set(GIT_ERROR_SSL,
+ "certificate revocation status could not be verified");
+ break;
+ case CERT_TRUST_IS_OFFLINE_REVOCATION:
+ git_error_set(GIT_ERROR_SSL,
+ "certificate revocation is offline or stale");
+ break;
+ case CERT_TRUST_HAS_WEAK_SIGNATURE:
+ git_error_set(GIT_ERROR_SSL, "certificate has a weak signature");
+ break;
+ default:
+ git_error_set(GIT_ERROR_SSL,
+ "unknown certificate lookup failure: %d", status);
+ return -1;
+ }
+
+ return GIT_ECERTIFICATE;
+}
+
+static int set_certificate_validation_error(DWORD status)
+{
+ switch (status) {
+ case TRUST_E_CERT_SIGNATURE:
+ git_error_set(GIT_ERROR_SSL,
+ "the certificate cannot be verified");
+ break;
+ case CRYPT_E_REVOKED:
+ git_error_set(GIT_ERROR_SSL,
+ "the certificate or signature has been revoked");
+ break;
+ case CERT_E_UNTRUSTEDROOT:
+ git_error_set(GIT_ERROR_SSL,
+ "the certificate root is not trusted");
+ break;
+ case CERT_E_UNTRUSTEDTESTROOT:
+ git_error_set(GIT_ERROR_SSL,
+ "the certificate root is a test certificate");
+ break;
+ case CERT_E_CHAINING:
+ git_error_set(GIT_ERROR_SSL,
+ "the certificate chain is invalid");
+ break;
+ case CERT_E_WRONG_USAGE:
+ case CERT_E_PURPOSE:
+ git_error_set(GIT_ERROR_SSL,
+ "the certificate is not valid for this usage");
+ break;
+ case CERT_E_EXPIRED:
+ git_error_set(GIT_ERROR_SSL,
+ "certificate is expired or not yet valid");
+ break;
+ case CERT_E_INVALID_NAME:
+ case CERT_E_CN_NO_MATCH:
+ git_error_set(GIT_ERROR_SSL,
+ "certificate is not valid for this hostname");
+ break;
+ case CERT_E_INVALID_POLICY:
+ case TRUST_E_BASIC_CONSTRAINTS:
+ case CERT_E_CRITICAL:
+ case CERT_E_VALIDITYPERIODNESTING:
+ git_error_set(GIT_ERROR_SSL, "certificate is not valid");
+ break;
+ case CRYPT_E_NO_REVOCATION_CHECK:
+ git_error_set(GIT_ERROR_SSL,
+ "certificate revocation status could not be verified");
+ break;
+ case CRYPT_E_REVOCATION_OFFLINE:
+ git_error_set(GIT_ERROR_SSL,
+ "certificate revocation is offline or stale");
+ break;
+ case CERT_E_ROLE:
+ git_error_set(GIT_ERROR_SSL, "certificate authority is not valid");
+ break;
+ default:
+ git_error_set(GIT_ERROR_SSL,
+ "unknown certificate policy checking failure: %d",
+ status);
+ return -1;
+ }
+
+ return GIT_ECERTIFICATE;
+}
+
+static int check_certificate(schannel_stream* st)
+{
+ CERT_CHAIN_PARA cert_chain_parameters;
+ SSL_EXTRA_CERT_CHAIN_POLICY_PARA ssl_policy_parameters;
+ CERT_CHAIN_POLICY_PARA cert_policy_parameters =
+ { sizeof(CERT_CHAIN_POLICY_PARA), 0, &ssl_policy_parameters };
+ CERT_CHAIN_POLICY_STATUS cert_policy_status;
+
+ memset(&cert_chain_parameters, 0, sizeof(CERT_CHAIN_PARA));
+ cert_chain_parameters.cbSize = sizeof(CERT_CHAIN_PARA);
+
+ if (QueryContextAttributesW(&st->context,
+ SECPKG_ATTR_REMOTE_CERT_CONTEXT,
+ &st->certificate) != SEC_E_OK) {
+ git_error_set(GIT_ERROR_OS,
+ "could not query remote certificate context");
+ return -1;
+ }
+
+ /* TODO: do we really want to do revokcation checking ? */
+ if (!CertGetCertificateChain(NULL, st->certificate, NULL,
+ st->certificate->hCertStore, &cert_chain_parameters,
+ CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT,
+ NULL, &st->cert_chain)) {
+ git_error_set(GIT_ERROR_OS, "could not query remote certificate chain");
+ CertFreeCertificateContext(st->certificate);
+ return -1;
+ }
+
+ st->state = STATE_CERTIFICATE;
+
+ /* Set up the x509 certificate data for future callbacks */
+
+ st->x509.parent.cert_type = GIT_CERT_X509;
+ st->x509.data = st->certificate->pbCertEncoded;
+ st->x509.len = st->certificate->cbCertEncoded;
+
+ /* Handle initial certificate validation */
+
+ if (st->cert_chain->TrustStatus.dwErrorStatus != CERT_TRUST_NO_ERROR)
+ return set_certificate_lookup_error(st->cert_chain->TrustStatus.dwErrorStatus);
+
+ ssl_policy_parameters.cbSize = sizeof(SSL_EXTRA_CERT_CHAIN_POLICY_PARA);
+ ssl_policy_parameters.dwAuthType = AUTHTYPE_SERVER;
+ ssl_policy_parameters.pwszServerName = st->host_w;
+
+ if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_SSL,
+ st->cert_chain, &cert_policy_parameters,
+ &cert_policy_status)) {
+ git_error_set(GIT_ERROR_OS, "could not verify certificate chain policy");
+ return -1;
+ }
+
+ if (cert_policy_status.dwError != SEC_E_OK)
+ return set_certificate_validation_error(cert_policy_status.dwError);
+
+ return 0;
+}
+
+static int schannel_connect(git_stream *stream)
+{
+ schannel_stream *st = (schannel_stream *)stream;
+ int error;
+
+ GIT_ASSERT(st->state == STATE_NONE);
+
+ if ((error = connect_context(st)) < 0 ||
+ (error = check_certificate(st)) < 0)
+ return error;
+
+ st->connected = 1;
+ return 0;
+}
+
+static int schannel_certificate(git_cert **out, git_stream *stream)
+{
+ schannel_stream *st = (schannel_stream *)stream;
+
+ *out = &st->x509.parent;
+ return 0;
+}
+
+static int schannel_set_proxy(
+ git_stream *stream,
+ const git_proxy_options *proxy_options)
+{
+ schannel_stream *st = (schannel_stream *)stream;
+ return git_stream_set_proxy(st->io, proxy_options);
+}
+
+static ssize_t schannel_write(
+ git_stream *stream,
+ const char *data,
+ size_t data_len,
+ int flags)
+{
+ schannel_stream *st = (schannel_stream *)stream;
+ SecBuffer encrypt_buf[3];
+ SecBufferDesc encrypt_buf_desc = { SECBUFFER_VERSION, 3, encrypt_buf };
+ git_str ciphertext_out = GIT_STR_INIT;
+ ssize_t total_len = 0;
+
+ GIT_UNUSED(flags);
+
+ if (data_len > SSIZE_MAX)
+ data_len = SSIZE_MAX;
+
+ git_str_init(&ciphertext_out,
+ st->stream_sizes.cbHeader +
+ st->stream_sizes.cbMaximumMessage +
+ st->stream_sizes.cbTrailer);
+
+ while (data_len > 0) {
+ size_t message_len = min(data_len, st->stream_sizes.cbMaximumMessage);
+ size_t ciphertext_len, ciphertext_written = 0;
+
+ encrypt_buf[0].BufferType = SECBUFFER_STREAM_HEADER;
+ encrypt_buf[0].cbBuffer = st->stream_sizes.cbHeader;
+ encrypt_buf[0].pvBuffer = ciphertext_out.ptr;
+
+ encrypt_buf[1].BufferType = SECBUFFER_DATA;
+ encrypt_buf[1].cbBuffer = (unsigned long)message_len;
+ encrypt_buf[1].pvBuffer =
+ ciphertext_out.ptr + st->stream_sizes.cbHeader;
+
+ encrypt_buf[2].BufferType = SECBUFFER_STREAM_TRAILER;
+ encrypt_buf[2].cbBuffer = st->stream_sizes.cbTrailer;
+ encrypt_buf[2].pvBuffer =
+ ciphertext_out.ptr + st->stream_sizes.cbHeader +
+ message_len;
+
+ memcpy(ciphertext_out.ptr + st->stream_sizes.cbHeader, data, message_len);
+
+ if (EncryptMessage(&st->context, 0, &encrypt_buf_desc, 0) != SEC_E_OK) {
+ git_error_set(GIT_ERROR_OS, "could not encrypt tls message");
+ total_len = -1;
+ goto done;
+ }
+
+ ciphertext_len = encrypt_buf[0].cbBuffer +
+ encrypt_buf[1].cbBuffer +
+ encrypt_buf[2].cbBuffer;
+
+ while (ciphertext_written < ciphertext_len) {
+ ssize_t chunk_len = git_stream_write(st->io,
+ ciphertext_out.ptr + ciphertext_written,
+ ciphertext_len - ciphertext_written, 0);
+
+ if (chunk_len < 0) {
+ total_len = -1;
+ goto done;
+ }
+
+ ciphertext_len -= chunk_len;
+ ciphertext_written += chunk_len;
+ }
+
+ total_len += message_len;
+
+ data += message_len;
+ data_len -= message_len;
+ }
+
+done:
+ git_str_dispose(&ciphertext_out);
+ return total_len;
+}
+
+static ssize_t schannel_read(git_stream *stream, void *_data, size_t data_len)
+{
+ schannel_stream *st = (schannel_stream *)stream;
+ char *data = (char *)_data;
+ SecBuffer decrypt_buf[4];
+ SecBufferDesc decrypt_buf_desc = { SECBUFFER_VERSION, 4, decrypt_buf };
+ SECURITY_STATUS status;
+ ssize_t chunk_len, total_len = 0;
+
+ if (data_len > SSIZE_MAX)
+ data_len = SSIZE_MAX;
+
+ /*
+ * Loop until we have some bytes to return - we may have decrypted
+ * bytes queued or ciphertext from the wire that we can decrypt and
+ * return. Return any queued bytes if they're available to avoid a
+ * network read, which may block. We may return less than the
+ * caller requested, and they can retry for an actual network
+ */
+ while ((size_t)total_len < data_len) {
+ if (st->plaintext_in.size > 0) {
+ size_t copy_len = min(st->plaintext_in.size, data_len);
+
+ memcpy(data, st->plaintext_in.ptr, copy_len);
+ git_str_consume_bytes(&st->plaintext_in, copy_len);
+
+ data += copy_len;
+ data_len -= copy_len;
+
+ total_len += copy_len;
+
+ continue;
+ }
+
+ if (st->ciphertext_in.size > 0) {
+ decrypt_buf[0].BufferType = SECBUFFER_DATA;
+ decrypt_buf[0].cbBuffer = (unsigned long)min(st->ciphertext_in.size, ULONG_MAX);
+ decrypt_buf[0].pvBuffer = st->ciphertext_in.ptr;
+
+ decrypt_buf[1].BufferType = SECBUFFER_EMPTY;
+ decrypt_buf[1].cbBuffer = 0;
+ decrypt_buf[1].pvBuffer = NULL;
+
+ decrypt_buf[2].BufferType = SECBUFFER_EMPTY;
+ decrypt_buf[2].cbBuffer = 0;
+ decrypt_buf[2].pvBuffer = NULL;
+
+ decrypt_buf[3].BufferType = SECBUFFER_EMPTY;
+ decrypt_buf[3].cbBuffer = 0;
+ decrypt_buf[3].pvBuffer = NULL;
+
+ status = DecryptMessage(&st->context, &decrypt_buf_desc, 0, NULL);
+
+ if (status == SEC_E_OK) {
+ GIT_ASSERT(decrypt_buf[0].BufferType == SECBUFFER_STREAM_HEADER);
+ GIT_ASSERT(decrypt_buf[1].BufferType == SECBUFFER_DATA);
+ GIT_ASSERT(decrypt_buf[2].BufferType == SECBUFFER_STREAM_TRAILER);
+
+ if (git_str_put(&st->plaintext_in, decrypt_buf[1].pvBuffer, decrypt_buf[1].cbBuffer) < 0) {
+ total_len = -1;
+ goto done;
+ }
+
+ if (decrypt_buf[3].BufferType == SECBUFFER_EXTRA) {
+ git_str_consume_bytes(&st->ciphertext_in, (st->ciphertext_in.size - decrypt_buf[3].cbBuffer));
+ } else {
+ git_str_clear(&st->ciphertext_in);
+ }
+
+ continue;
+ } else if (status == SEC_E_CONTEXT_EXPIRED) {
+ break;
+ } else if (status != SEC_E_INCOMPLETE_MESSAGE) {
+ git_error_set(GIT_ERROR_SSL, "could not decrypt tls message");
+ total_len = -1;
+ goto done;
+ }
+ }
+
+ if (total_len != 0)
+ break;
+
+ if (git_str_grow_by(&st->ciphertext_in, READ_BLOCKSIZE) < 0) {
+ total_len = -1;
+ goto done;
+ }
+
+ if ((chunk_len = git_stream_read(st->io, st->ciphertext_in.ptr + st->ciphertext_in.size, st->ciphertext_in.asize - st->ciphertext_in.size)) < 0) {
+ total_len = -1;
+ goto done;
+ }
+
+ st->ciphertext_in.size += chunk_len;
+ }
+
+done:
+ return total_len;
+}
+
+static int schannel_close(git_stream *stream)
+{
+ schannel_stream *st = (schannel_stream *)stream;
+ int error = 0;
+
+ if (st->connected) {
+ SecBuffer shutdown_buf;
+ SecBufferDesc shutdown_buf_desc =
+ { SECBUFFER_VERSION, 1, &shutdown_buf };
+ DWORD shutdown_message = SCHANNEL_SHUTDOWN, shutdown_flags;
+
+ shutdown_buf.BufferType = SECBUFFER_TOKEN;
+ shutdown_buf.cbBuffer = sizeof(DWORD);
+ shutdown_buf.pvBuffer = &shutdown_message;
+
+ if (ApplyControlToken(&st->context, &shutdown_buf_desc) != SEC_E_OK) {
+ git_error_set(GIT_ERROR_SSL, "could not shutdown stream");
+ error = -1;
+ }
+
+ shutdown_buf.BufferType = SECBUFFER_TOKEN;
+ shutdown_buf.cbBuffer = 0;
+ shutdown_buf.pvBuffer = NULL;
+
+ shutdown_flags = ISC_REQ_ALLOCATE_MEMORY |
+ ISC_REQ_CONFIDENTIALITY |
+ ISC_REQ_REPLAY_DETECT |
+ ISC_REQ_SEQUENCE_DETECT |
+ ISC_REQ_STREAM;
+
+ if (InitializeSecurityContext(&st->cred, &st->context,
+ NULL, shutdown_flags, 0, 0,
+ &shutdown_buf_desc, 0, NULL,
+ &shutdown_buf_desc, &shutdown_flags,
+ NULL) == SEC_E_OK) {
+ if (shutdown_buf.cbBuffer > 0) {
+ if (git_stream__write_full(st->io,
+ shutdown_buf.pvBuffer,
+ shutdown_buf.cbBuffer, 0) < 0)
+ error = -1;
+
+ FreeContextBuffer(shutdown_buf.pvBuffer);
+ }
+ }
+ }
+
+ st->connected = false;
+
+ if (st->owned && git_stream_close(st->io) < 0)
+ error = -1;
+
+ return error;
+}
+
+static void schannel_free(git_stream *stream)
+{
+ schannel_stream *st = (schannel_stream *)stream;
+
+ if (st->state >= STATE_CERTIFICATE) {
+ CertFreeCertificateContext(st->certificate);
+ CertFreeCertificateChain(st->cert_chain);
+ }
+
+ if (st->state >= STATE_CONTEXT)
+ DeleteSecurityContext(&st->context);
+
+ if (st->state >= STATE_CRED)
+ FreeCredentialsHandle(&st->cred);
+
+ st->state = STATE_NONE;
+
+ git_str_dispose(&st->ciphertext_in);
+ git_str_dispose(&st->plaintext_in);
+
+ git__free(st->host_w);
+
+ if (st->owned)
+ git_stream_free(st->io);
+
+ git__free(st);
+}
+
+static int schannel_stream_wrap(
+ git_stream **out,
+ git_stream *in,
+ const char *host,
+ int owned)
+{
+ schannel_stream *st;
+
+ st = git__calloc(1, sizeof(schannel_stream));
+ GIT_ERROR_CHECK_ALLOC(st);
+
+ st->io = in;
+ st->owned = owned;
+
+ if (git_utf8_to_16_alloc(&st->host_w, host) < 0) {
+ git__free(st);
+ return -1;
+ }
+
+ st->parent.version = GIT_STREAM_VERSION;
+ st->parent.encrypted = 1;
+ st->parent.proxy_support = git_stream_supports_proxy(st->io);
+ st->parent.connect = schannel_connect;
+ st->parent.certificate = schannel_certificate;
+ st->parent.set_proxy = schannel_set_proxy;
+ st->parent.read = schannel_read;
+ st->parent.write = schannel_write;
+ st->parent.close = schannel_close;
+ st->parent.free = schannel_free;
+
+ *out = (git_stream *)st;
+ return 0;
+}
+
+extern int git_schannel_stream_new(
+ git_stream **out,
+ const char *host,
+ const char *port)
+{
+ git_stream *stream;
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(host);
+ GIT_ASSERT_ARG(port);
+
+ if ((error = git_socket_stream_new(&stream, host, port)) < 0)
+ return error;
+
+ if ((error = schannel_stream_wrap(out, stream, host, 1)) < 0) {
+ git_stream_close(stream);
+ git_stream_free(stream);
+ }
+
+ return error;
+}
+
+extern int git_schannel_stream_wrap(
+ git_stream **out,
+ git_stream *in,
+ const char *host)
+{
+ return schannel_stream_wrap(out, in, host, 0);
+}
+
+#endif
diff --git a/src/libgit2/streams/schannel.h b/src/libgit2/streams/schannel.h
new file mode 100644
index 0000000..3584970
--- /dev/null
+++ b/src/libgit2/streams/schannel.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_steams_schannel_h__
+#define INCLUDE_steams_schannel_h__
+
+#include "common.h"
+
+#include "git2/sys/stream.h"
+
+#ifdef GIT_SCHANNEL
+
+extern int git_schannel_stream_new(
+ git_stream **out,
+ const char *host,
+ const char *port);
+
+extern int git_schannel_stream_wrap(
+ git_stream **out,
+ git_stream *in,
+ const char *host);
+
+#endif
+
+#endif
diff --git a/src/libgit2/streams/socket.c b/src/libgit2/streams/socket.c
new file mode 100644
index 0000000..a463312
--- /dev/null
+++ b/src/libgit2/streams/socket.c
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "streams/socket.h"
+
+#include "posix.h"
+#include "registry.h"
+#include "runtime.h"
+#include "stream.h"
+
+#ifndef _WIN32
+# include <sys/types.h>
+# include <sys/socket.h>
+# include <sys/select.h>
+# include <sys/time.h>
+# include <netdb.h>
+# include <netinet/in.h>
+# include <arpa/inet.h>
+#else
+# include <winsock2.h>
+# include <ws2tcpip.h>
+# ifdef _MSC_VER
+# pragma comment(lib, "ws2_32")
+# endif
+#endif
+
+int git_socket_stream__connect_timeout = 0;
+int git_socket_stream__timeout = 0;
+
+#ifdef GIT_WIN32
+static void net_set_error(const char *str)
+{
+ int error = WSAGetLastError();
+ char * win32_error = git_win32_get_error_message(error);
+
+ if (win32_error) {
+ git_error_set(GIT_ERROR_NET, "%s: %s", str, win32_error);
+ git__free(win32_error);
+ } else {
+ git_error_set(GIT_ERROR_NET, "%s", str);
+ }
+}
+#else
+static void net_set_error(const char *str)
+{
+ git_error_set(GIT_ERROR_NET, "%s: %s", str, strerror(errno));
+}
+#endif
+
+static int close_socket(GIT_SOCKET s)
+{
+ if (s == INVALID_SOCKET)
+ return 0;
+
+#ifdef GIT_WIN32
+ if (closesocket(s) != 0) {
+ net_set_error("could not close socket");
+ return -1;
+ }
+
+ return 0;
+#else
+ return close(s);
+#endif
+
+}
+
+static int set_nonblocking(GIT_SOCKET s)
+{
+#ifdef GIT_WIN32
+ unsigned long nonblocking = 1;
+
+ if (ioctlsocket(s, FIONBIO, &nonblocking) != 0) {
+ net_set_error("could not set socket non-blocking");
+ return -1;
+ }
+#else
+ int flags;
+
+ if ((flags = fcntl(s, F_GETFL, 0)) == -1) {
+ net_set_error("could not query socket flags");
+ return -1;
+ }
+
+ flags |= O_NONBLOCK;
+
+ if (fcntl(s, F_SETFL, flags) != 0) {
+ net_set_error("could not set socket non-blocking");
+ return -1;
+ }
+#endif
+
+ return 0;
+}
+
+/* Promote a sockerr to an errno for our error handling routines */
+static int handle_sockerr(GIT_SOCKET socket)
+{
+ int sockerr;
+ socklen_t errlen = sizeof(sockerr);
+
+ if (getsockopt(socket, SOL_SOCKET, SO_ERROR,
+ (void *)&sockerr, &errlen) < 0)
+ return -1;
+
+ if (sockerr == ETIMEDOUT)
+ return GIT_TIMEOUT;
+
+ errno = sockerr;
+ return -1;
+}
+
+GIT_INLINE(bool) connect_would_block(int error)
+{
+#ifdef GIT_WIN32
+ if (error == SOCKET_ERROR && WSAGetLastError() == WSAEWOULDBLOCK)
+ return true;
+#endif
+
+ if (error == -1 && errno == EINPROGRESS)
+ return true;
+
+ return false;
+}
+
+static int connect_with_timeout(
+ GIT_SOCKET socket,
+ const struct sockaddr *address,
+ socklen_t address_len,
+ int timeout)
+{
+ struct pollfd fd;
+ int error;
+
+ if (timeout && (error = set_nonblocking(socket)) < 0)
+ return error;
+
+ error = connect(socket, address, address_len);
+
+ if (error == 0 || !connect_would_block(error))
+ return error;
+
+ fd.fd = socket;
+ fd.events = POLLOUT;
+ fd.revents = 0;
+
+ error = p_poll(&fd, 1, timeout);
+
+ if (error == 0) {
+ return GIT_TIMEOUT;
+ } else if (error != 1) {
+ return -1;
+ } else if ((fd.revents & (POLLPRI | POLLHUP | POLLERR))) {
+ return handle_sockerr(socket);
+ } else if ((fd.revents & POLLOUT) != POLLOUT) {
+ git_error_set(GIT_ERROR_NET,
+ "unknown error while polling for connect: %d",
+ fd.revents);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int socket_connect(git_stream *stream)
+{
+ git_socket_stream *st = (git_socket_stream *) stream;
+ GIT_SOCKET s = INVALID_SOCKET;
+ struct addrinfo *info = NULL, *p;
+ struct addrinfo hints;
+ int error;
+
+ memset(&hints, 0x0, sizeof(struct addrinfo));
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_family = AF_UNSPEC;
+
+ if ((error = p_getaddrinfo(st->host, st->port, &hints, &info)) != 0) {
+ git_error_set(GIT_ERROR_NET,
+ "failed to resolve address for %s: %s",
+ st->host, p_gai_strerror(error));
+ return -1;
+ }
+
+ for (p = info; p != NULL; p = p->ai_next) {
+ s = socket(p->ai_family, p->ai_socktype | SOCK_CLOEXEC, p->ai_protocol);
+
+ if (s == INVALID_SOCKET)
+ continue;
+
+ error = connect_with_timeout(s, p->ai_addr,
+ (socklen_t)p->ai_addrlen,
+ st->parent.connect_timeout);
+
+ if (error == 0)
+ break;
+
+ /* If we can't connect, try the next one */
+ close_socket(s);
+ s = INVALID_SOCKET;
+
+ if (error == GIT_TIMEOUT)
+ break;
+ }
+
+ /* Oops, we couldn't connect to any address */
+ if (s == INVALID_SOCKET) {
+ if (error == GIT_TIMEOUT)
+ git_error_set(GIT_ERROR_NET, "failed to connect to %s: Operation timed out", st->host);
+ else
+ git_error_set(GIT_ERROR_OS, "failed to connect to %s", st->host);
+ error = -1;
+ goto done;
+ }
+
+ if (st->parent.timeout && !st->parent.connect_timeout &&
+ (error = set_nonblocking(s)) < 0)
+ return error;
+
+ st->s = s;
+ error = 0;
+
+done:
+ p_freeaddrinfo(info);
+ return error;
+}
+
+static ssize_t socket_write(
+ git_stream *stream,
+ const char *data,
+ size_t len,
+ int flags)
+{
+ git_socket_stream *st = (git_socket_stream *) stream;
+ struct pollfd fd;
+ ssize_t ret;
+
+ GIT_ASSERT(flags == 0);
+ GIT_UNUSED(flags);
+
+ ret = p_send(st->s, data, len, 0);
+
+ if (st->parent.timeout && ret < 0 &&
+ (errno == EAGAIN || errno != EWOULDBLOCK)) {
+ fd.fd = st->s;
+ fd.events = POLLOUT;
+ fd.revents = 0;
+
+ ret = p_poll(&fd, 1, st->parent.timeout);
+
+ if (ret == 1) {
+ ret = p_send(st->s, data, len, 0);
+ } else if (ret == 0) {
+ git_error_set(GIT_ERROR_NET,
+ "could not write to socket: timed out");
+ return GIT_TIMEOUT;
+ }
+ }
+
+ if (ret < 0) {
+ net_set_error("error receiving data from socket");
+ return -1;
+ }
+
+ return ret;
+}
+
+static ssize_t socket_read(
+ git_stream *stream,
+ void *data,
+ size_t len)
+{
+ git_socket_stream *st = (git_socket_stream *) stream;
+ struct pollfd fd;
+ ssize_t ret;
+
+ ret = p_recv(st->s, data, len, 0);
+
+ if (st->parent.timeout && ret < 0 &&
+ (errno == EAGAIN || errno != EWOULDBLOCK)) {
+ fd.fd = st->s;
+ fd.events = POLLIN;
+ fd.revents = 0;
+
+ ret = p_poll(&fd, 1, st->parent.timeout);
+
+ if (ret == 1) {
+ ret = p_recv(st->s, data, len, 0);
+ } else if (ret == 0) {
+ git_error_set(GIT_ERROR_NET,
+ "could not read from socket: timed out");
+ return GIT_TIMEOUT;
+ }
+ }
+
+ if (ret < 0) {
+ net_set_error("error receiving data from socket");
+ return -1;
+ }
+
+ return ret;
+}
+
+static int socket_close(git_stream *stream)
+{
+ git_socket_stream *st = (git_socket_stream *) stream;
+ int error;
+
+ error = close_socket(st->s);
+ st->s = INVALID_SOCKET;
+
+ return error;
+}
+
+static void socket_free(git_stream *stream)
+{
+ git_socket_stream *st = (git_socket_stream *) stream;
+
+ git__free(st->host);
+ git__free(st->port);
+ git__free(st);
+}
+
+static int default_socket_stream_new(
+ git_stream **out,
+ const char *host,
+ const char *port)
+{
+ git_socket_stream *st;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(host);
+ GIT_ASSERT_ARG(port);
+
+ st = git__calloc(1, sizeof(git_socket_stream));
+ GIT_ERROR_CHECK_ALLOC(st);
+
+ st->host = git__strdup(host);
+ GIT_ERROR_CHECK_ALLOC(st->host);
+
+ if (port) {
+ st->port = git__strdup(port);
+ GIT_ERROR_CHECK_ALLOC(st->port);
+ }
+
+ st->parent.version = GIT_STREAM_VERSION;
+ st->parent.timeout = git_socket_stream__timeout;
+ st->parent.connect_timeout = git_socket_stream__connect_timeout;
+ st->parent.connect = socket_connect;
+ st->parent.write = socket_write;
+ st->parent.read = socket_read;
+ st->parent.close = socket_close;
+ st->parent.free = socket_free;
+ st->s = INVALID_SOCKET;
+
+ *out = (git_stream *) st;
+ return 0;
+}
+
+int git_socket_stream_new(
+ git_stream **out,
+ const char *host,
+ const char *port)
+{
+ int (*init)(git_stream **, const char *, const char *) = NULL;
+ git_stream_registration custom = {0};
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(host);
+ GIT_ASSERT_ARG(port);
+
+ if ((error = git_stream_registry_lookup(&custom, GIT_STREAM_STANDARD)) == 0)
+ init = custom.init;
+ else if (error == GIT_ENOTFOUND)
+ init = default_socket_stream_new;
+ else
+ return error;
+
+ if (!init) {
+ git_error_set(GIT_ERROR_NET, "there is no socket stream available");
+ return -1;
+ }
+
+ return init(out, host, port);
+}
+
+#ifdef GIT_WIN32
+
+static void socket_stream_global_shutdown(void)
+{
+ WSACleanup();
+}
+
+int git_socket_stream_global_init(void)
+{
+ WORD winsock_version;
+ WSADATA wsa_data;
+
+ winsock_version = MAKEWORD(2, 2);
+
+ if (WSAStartup(winsock_version, &wsa_data) != 0) {
+ git_error_set(GIT_ERROR_OS, "could not initialize Windows Socket Library");
+ return -1;
+ }
+
+ if (LOBYTE(wsa_data.wVersion) != 2 ||
+ HIBYTE(wsa_data.wVersion) != 2) {
+ git_error_set(GIT_ERROR_SSL, "Windows Socket Library does not support Winsock 2.2");
+ return -1;
+ }
+
+ return git_runtime_shutdown_register(socket_stream_global_shutdown);
+}
+
+#else
+
+#include "stream.h"
+
+int git_socket_stream_global_init(void)
+{
+ return 0;
+}
+
+ #endif
diff --git a/src/libgit2/streams/socket.h b/src/libgit2/streams/socket.h
new file mode 100644
index 0000000..73e8de0
--- /dev/null
+++ b/src/libgit2/streams/socket.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_streams_socket_h__
+#define INCLUDE_streams_socket_h__
+
+#include "common.h"
+
+#include "stream.h"
+
+typedef struct {
+ git_stream parent;
+ char *host;
+ char *port;
+ GIT_SOCKET s;
+} git_socket_stream;
+
+extern int git_socket_stream_new(git_stream **out, const char *host, const char *port);
+
+extern int git_socket_stream_global_init(void);
+
+#endif
diff --git a/src/libgit2/streams/stransport.c b/src/libgit2/streams/stransport.c
new file mode 100644
index 0000000..7a3585e
--- /dev/null
+++ b/src/libgit2/streams/stransport.c
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "streams/stransport.h"
+
+#ifdef GIT_SECURE_TRANSPORT
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <Security/SecureTransport.h>
+#include <Security/SecCertificate.h>
+
+#include "git2/transport.h"
+
+#include "streams/socket.h"
+
+static int stransport_error(OSStatus ret)
+{
+ CFStringRef message;
+
+ if (ret == noErr || ret == errSSLClosedGraceful) {
+ git_error_clear();
+ return 0;
+ }
+
+#if !TARGET_OS_IPHONE
+ message = SecCopyErrorMessageString(ret, NULL);
+ GIT_ERROR_CHECK_ALLOC(message);
+
+ git_error_set(GIT_ERROR_NET, "SecureTransport error: %s", CFStringGetCStringPtr(message, kCFStringEncodingUTF8));
+ CFRelease(message);
+#else
+ git_error_set(GIT_ERROR_NET, "SecureTransport error: OSStatus %d", (unsigned int)ret);
+ GIT_UNUSED(message);
+#endif
+
+ return -1;
+}
+
+typedef struct {
+ git_stream parent;
+ git_stream *io;
+ int owned;
+ int error;
+ SSLContextRef ctx;
+ CFDataRef der_data;
+ git_cert_x509 cert_info;
+} stransport_stream;
+
+static int stransport_connect(git_stream *stream)
+{
+ stransport_stream *st = (stransport_stream *) stream;
+ int error;
+ SecTrustRef trust = NULL;
+ SecTrustResultType sec_res;
+ OSStatus ret;
+
+ if (st->owned && (error = git_stream_connect(st->io)) < 0)
+ return error;
+
+ ret = SSLHandshake(st->ctx);
+
+ if (ret != errSSLServerAuthCompleted && st->error != 0)
+ return -1;
+ else if (ret != errSSLServerAuthCompleted) {
+ git_error_set(GIT_ERROR_SSL, "unexpected return value from ssl handshake %d", (int)ret);
+ return -1;
+ }
+
+ if ((ret = SSLCopyPeerTrust(st->ctx, &trust)) != noErr)
+ goto on_error;
+
+ if (!trust)
+ return GIT_ECERTIFICATE;
+
+ if ((ret = SecTrustEvaluate(trust, &sec_res)) != noErr)
+ goto on_error;
+
+ CFRelease(trust);
+
+ if (sec_res == kSecTrustResultInvalid || sec_res == kSecTrustResultOtherError) {
+ git_error_set(GIT_ERROR_SSL, "internal security trust error");
+ return -1;
+ }
+
+ if (sec_res == kSecTrustResultDeny || sec_res == kSecTrustResultRecoverableTrustFailure ||
+ sec_res == kSecTrustResultFatalTrustFailure) {
+ git_error_set(GIT_ERROR_SSL, "untrusted connection error");
+ return GIT_ECERTIFICATE;
+ }
+
+ return 0;
+
+on_error:
+ if (trust)
+ CFRelease(trust);
+
+ return stransport_error(ret);
+}
+
+static int stransport_certificate(git_cert **out, git_stream *stream)
+{
+ stransport_stream *st = (stransport_stream *) stream;
+ SecTrustRef trust = NULL;
+ SecCertificateRef sec_cert;
+ OSStatus ret;
+
+ if ((ret = SSLCopyPeerTrust(st->ctx, &trust)) != noErr)
+ return stransport_error(ret);
+
+ sec_cert = SecTrustGetCertificateAtIndex(trust, 0);
+ st->der_data = SecCertificateCopyData(sec_cert);
+ CFRelease(trust);
+
+ if (st->der_data == NULL) {
+ git_error_set(GIT_ERROR_SSL, "retrieved invalid certificate data");
+ return -1;
+ }
+
+ st->cert_info.parent.cert_type = GIT_CERT_X509;
+ st->cert_info.data = (void *) CFDataGetBytePtr(st->der_data);
+ st->cert_info.len = CFDataGetLength(st->der_data);
+
+ *out = (git_cert *)&st->cert_info;
+ return 0;
+}
+
+static int stransport_set_proxy(
+ git_stream *stream,
+ const git_proxy_options *proxy_opts)
+{
+ stransport_stream *st = (stransport_stream *) stream;
+
+ return git_stream_set_proxy(st->io, proxy_opts);
+}
+
+/*
+ * Contrary to typical network IO callbacks, Secure Transport write callback is
+ * expected to write *all* passed data, not just as much as it can, and any
+ * other case would be considered a failure.
+ *
+ * This behavior is actually not specified in the Apple documentation, but is
+ * required for things to work correctly (and incidentally, that's also how
+ * Apple implements it in its projects at opensource.apple.com).
+ *
+ * Libgit2 streams happen to already have this very behavior so this is just
+ * passthrough.
+ */
+static OSStatus write_cb(SSLConnectionRef conn, const void *data, size_t *len)
+{
+ stransport_stream *st = (stransport_stream *)conn;
+ git_stream *io = st->io;
+ OSStatus ret;
+
+ st->error = 0;
+
+ ret = git_stream__write_full(io, data, *len, 0);
+
+ if (ret < 0) {
+ st->error = ret;
+ return (ret == GIT_TIMEOUT) ?
+ -9853 /* errSSLNetworkTimeout */:
+ -36 /* ioErr */;
+ }
+
+ return noErr;
+}
+
+static ssize_t stransport_write(git_stream *stream, const char *data, size_t len, int flags)
+{
+ stransport_stream *st = (stransport_stream *) stream;
+ size_t data_len, processed;
+ OSStatus ret;
+
+ GIT_UNUSED(flags);
+
+ data_len = min(len, SSIZE_MAX);
+ if ((ret = SSLWrite(st->ctx, data, data_len, &processed)) != noErr) {
+ if (st->error == GIT_TIMEOUT)
+ return GIT_TIMEOUT;
+
+ return stransport_error(ret);
+ }
+
+ GIT_ASSERT(processed < SSIZE_MAX);
+ return (ssize_t)processed;
+}
+
+/*
+ * Contrary to typical network IO callbacks, Secure Transport read callback is
+ * expected to read *exactly* the requested number of bytes, not just as much
+ * as it can, and any other case would be considered a failure.
+ *
+ * This behavior is actually not specified in the Apple documentation, but is
+ * required for things to work correctly (and incidentally, that's also how
+ * Apple implements it in its projects at opensource.apple.com).
+ */
+static OSStatus read_cb(SSLConnectionRef conn, void *data, size_t *len)
+{
+ stransport_stream *st = (stransport_stream *)conn;
+ git_stream *io = st->io;
+ OSStatus error = noErr;
+ size_t off = 0;
+ ssize_t ret;
+
+ st->error = 0;
+
+ do {
+ ret = git_stream_read(io, data + off, *len - off);
+
+ if (ret < 0) {
+ st->error = ret;
+ error = (ret == GIT_TIMEOUT) ?
+ -9853 /* errSSLNetworkTimeout */:
+ -36 /* ioErr */;
+ break;
+ } else if (ret == 0) {
+ error = errSSLClosedGraceful;
+ break;
+ }
+
+ off += ret;
+ } while (off < *len);
+
+ *len = off;
+ return error;
+}
+
+static ssize_t stransport_read(git_stream *stream, void *data, size_t len)
+{
+ stransport_stream *st = (stransport_stream *)stream;
+ size_t processed;
+ OSStatus ret;
+
+ if ((ret = SSLRead(st->ctx, data, len, &processed)) != noErr) {
+ if (st->error == GIT_TIMEOUT)
+ return GIT_TIMEOUT;
+
+ return stransport_error(ret);
+ }
+
+ return processed;
+}
+
+static int stransport_close(git_stream *stream)
+{
+ stransport_stream *st = (stransport_stream *) stream;
+ OSStatus ret;
+
+ ret = SSLClose(st->ctx);
+ if (ret != noErr && ret != errSSLClosedGraceful)
+ return stransport_error(ret);
+
+ return st->owned ? git_stream_close(st->io) : 0;
+}
+
+static void stransport_free(git_stream *stream)
+{
+ stransport_stream *st = (stransport_stream *) stream;
+
+ if (st->owned)
+ git_stream_free(st->io);
+
+ CFRelease(st->ctx);
+ if (st->der_data)
+ CFRelease(st->der_data);
+ git__free(st);
+}
+
+static int stransport_wrap(
+ git_stream **out,
+ git_stream *in,
+ const char *host,
+ int owned)
+{
+ stransport_stream *st;
+ OSStatus ret;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(in);
+ GIT_ASSERT_ARG(host);
+
+ st = git__calloc(1, sizeof(stransport_stream));
+ GIT_ERROR_CHECK_ALLOC(st);
+
+ st->io = in;
+ st->owned = owned;
+
+ st->ctx = SSLCreateContext(NULL, kSSLClientSide, kSSLStreamType);
+ if (!st->ctx) {
+ git_error_set(GIT_ERROR_NET, "failed to create SSL context");
+ git__free(st);
+ return -1;
+ }
+
+ if ((ret = SSLSetIOFuncs(st->ctx, read_cb, write_cb)) != noErr ||
+ (ret = SSLSetConnection(st->ctx, st)) != noErr ||
+ (ret = SSLSetSessionOption(st->ctx, kSSLSessionOptionBreakOnServerAuth, true)) != noErr ||
+ (ret = SSLSetProtocolVersionMin(st->ctx, kTLSProtocol1)) != noErr ||
+ (ret = SSLSetProtocolVersionMax(st->ctx, kTLSProtocol12)) != noErr ||
+ (ret = SSLSetPeerDomainName(st->ctx, host, strlen(host))) != noErr) {
+ CFRelease(st->ctx);
+ git__free(st);
+ return stransport_error(ret);
+ }
+
+ st->parent.version = GIT_STREAM_VERSION;
+ st->parent.encrypted = 1;
+ st->parent.proxy_support = git_stream_supports_proxy(st->io);
+ st->parent.connect = stransport_connect;
+ st->parent.certificate = stransport_certificate;
+ st->parent.set_proxy = stransport_set_proxy;
+ st->parent.read = stransport_read;
+ st->parent.write = stransport_write;
+ st->parent.close = stransport_close;
+ st->parent.free = stransport_free;
+
+ *out = (git_stream *) st;
+ return 0;
+}
+
+int git_stransport_stream_wrap(
+ git_stream **out,
+ git_stream *in,
+ const char *host)
+{
+ return stransport_wrap(out, in, host, 0);
+}
+
+int git_stransport_stream_new(git_stream **out, const char *host, const char *port)
+{
+ git_stream *stream = NULL;
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(host);
+
+ error = git_socket_stream_new(&stream, host, port);
+
+ if (!error)
+ error = stransport_wrap(out, stream, host, 1);
+
+ if (error < 0 && stream) {
+ git_stream_close(stream);
+ git_stream_free(stream);
+ }
+
+ return error;
+}
+
+#endif
diff --git a/src/libgit2/streams/stransport.h b/src/libgit2/streams/stransport.h
new file mode 100644
index 0000000..1026e20
--- /dev/null
+++ b/src/libgit2/streams/stransport.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_streams_stransport_h__
+#define INCLUDE_streams_stransport_h__
+
+#include "common.h"
+
+#include "git2/sys/stream.h"
+
+#ifdef GIT_SECURE_TRANSPORT
+
+extern int git_stransport_stream_new(git_stream **out, const char *host, const char *port);
+extern int git_stransport_stream_wrap(git_stream **out, git_stream *in, const char *host);
+
+#endif
+
+#endif
diff --git a/src/libgit2/streams/tls.c b/src/libgit2/streams/tls.c
new file mode 100644
index 0000000..246ac9c
--- /dev/null
+++ b/src/libgit2/streams/tls.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "git2/errors.h"
+
+#include "common.h"
+#include "streams/registry.h"
+#include "streams/tls.h"
+#include "streams/mbedtls.h"
+#include "streams/openssl.h"
+#include "streams/stransport.h"
+#include "streams/schannel.h"
+
+int git_tls_stream_new(git_stream **out, const char *host, const char *port)
+{
+ int (*init)(git_stream **, const char *, const char *) = NULL;
+ git_stream_registration custom = {0};
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(host);
+ GIT_ASSERT_ARG(port);
+
+ if ((error = git_stream_registry_lookup(&custom, GIT_STREAM_TLS)) == 0) {
+ init = custom.init;
+ } else if (error == GIT_ENOTFOUND) {
+#ifdef GIT_SECURE_TRANSPORT
+ init = git_stransport_stream_new;
+#elif defined(GIT_OPENSSL)
+ init = git_openssl_stream_new;
+#elif defined(GIT_MBEDTLS)
+ init = git_mbedtls_stream_new;
+#elif defined(GIT_SCHANNEL)
+ init = git_schannel_stream_new;
+#endif
+ } else {
+ return error;
+ }
+
+ if (!init) {
+ git_error_set(GIT_ERROR_SSL, "there is no TLS stream available");
+ return -1;
+ }
+
+ return init(out, host, port);
+}
+
+int git_tls_stream_wrap(git_stream **out, git_stream *in, const char *host)
+{
+ int (*wrap)(git_stream **, git_stream *, const char *) = NULL;
+ git_stream_registration custom = {0};
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(in);
+
+ if (git_stream_registry_lookup(&custom, GIT_STREAM_TLS) == 0) {
+ wrap = custom.wrap;
+ } else {
+#ifdef GIT_SECURE_TRANSPORT
+ wrap = git_stransport_stream_wrap;
+#elif defined(GIT_OPENSSL)
+ wrap = git_openssl_stream_wrap;
+#elif defined(GIT_MBEDTLS)
+ wrap = git_mbedtls_stream_wrap;
+#elif defined(GIT_SCHANNEL)
+ wrap = git_schannel_stream_wrap;
+#endif
+ }
+
+ if (!wrap) {
+ git_error_set(GIT_ERROR_SSL, "there is no TLS stream available");
+ return -1;
+ }
+
+ return wrap(out, in, host);
+}
diff --git a/src/libgit2/streams/tls.h b/src/libgit2/streams/tls.h
new file mode 100644
index 0000000..465a6ea
--- /dev/null
+++ b/src/libgit2/streams/tls.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_streams_tls_h__
+#define INCLUDE_streams_tls_h__
+
+#include "common.h"
+
+#include "git2/sys/stream.h"
+
+/**
+ * Create a TLS stream with the most appropriate backend available for
+ * the current platform, whether that's SecureTransport on macOS,
+ * OpenSSL or mbedTLS on other Unixes, or something else entirely.
+ */
+extern int git_tls_stream_new(git_stream **out, const char *host, const char *port);
+
+/**
+ * Create a TLS stream on top of an existing insecure stream, using
+ * the most appropriate backend available for the current platform.
+ *
+ * This allows us to create a CONNECT stream on top of a proxy;
+ * using SecureTransport on macOS, OpenSSL or mbedTLS on other
+ * Unixes, or something else entirely.
+ */
+extern int git_tls_stream_wrap(git_stream **out, git_stream *in, const char *host);
+
+#endif
diff --git a/src/libgit2/submodule.c b/src/libgit2/submodule.c
new file mode 100644
index 0000000..95ea84f
--- /dev/null
+++ b/src/libgit2/submodule.c
@@ -0,0 +1,2384 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "submodule.h"
+
+#include "buf.h"
+#include "branch.h"
+#include "vector.h"
+#include "posix.h"
+#include "config_backend.h"
+#include "config.h"
+#include "repository.h"
+#include "tree.h"
+#include "iterator.h"
+#include "fs_path.h"
+#include "str.h"
+#include "index.h"
+#include "worktree.h"
+#include "clone.h"
+#include "path.h"
+
+#include "git2/config.h"
+#include "git2/sys/config.h"
+#include "git2/types.h"
+#include "git2/index.h"
+
+#define GIT_MODULES_FILE ".gitmodules"
+
+static git_configmap _sm_update_map[] = {
+ {GIT_CONFIGMAP_STRING, "checkout", GIT_SUBMODULE_UPDATE_CHECKOUT},
+ {GIT_CONFIGMAP_STRING, "rebase", GIT_SUBMODULE_UPDATE_REBASE},
+ {GIT_CONFIGMAP_STRING, "merge", GIT_SUBMODULE_UPDATE_MERGE},
+ {GIT_CONFIGMAP_STRING, "none", GIT_SUBMODULE_UPDATE_NONE},
+ {GIT_CONFIGMAP_FALSE, NULL, GIT_SUBMODULE_UPDATE_NONE},
+ {GIT_CONFIGMAP_TRUE, NULL, GIT_SUBMODULE_UPDATE_CHECKOUT},
+};
+
+static git_configmap _sm_ignore_map[] = {
+ {GIT_CONFIGMAP_STRING, "none", GIT_SUBMODULE_IGNORE_NONE},
+ {GIT_CONFIGMAP_STRING, "untracked", GIT_SUBMODULE_IGNORE_UNTRACKED},
+ {GIT_CONFIGMAP_STRING, "dirty", GIT_SUBMODULE_IGNORE_DIRTY},
+ {GIT_CONFIGMAP_STRING, "all", GIT_SUBMODULE_IGNORE_ALL},
+ {GIT_CONFIGMAP_FALSE, NULL, GIT_SUBMODULE_IGNORE_NONE},
+ {GIT_CONFIGMAP_TRUE, NULL, GIT_SUBMODULE_IGNORE_ALL},
+};
+
+static git_configmap _sm_recurse_map[] = {
+ {GIT_CONFIGMAP_STRING, "on-demand", GIT_SUBMODULE_RECURSE_ONDEMAND},
+ {GIT_CONFIGMAP_FALSE, NULL, GIT_SUBMODULE_RECURSE_NO},
+ {GIT_CONFIGMAP_TRUE, NULL, GIT_SUBMODULE_RECURSE_YES},
+};
+
+enum {
+ CACHE_OK = 0,
+ CACHE_REFRESH = 1,
+ CACHE_FLUSH = 2
+};
+enum {
+ GITMODULES_EXISTING = 0,
+ GITMODULES_CREATE = 1
+};
+
+static int submodule_alloc(git_submodule **out, git_repository *repo, const char *name);
+static git_config_backend *open_gitmodules(git_repository *repo, int gitmod);
+static int gitmodules_snapshot(git_config **snap, git_repository *repo);
+static int get_url_base(git_str *url, git_repository *repo);
+static int lookup_head_remote_key(git_str *remote_key, git_repository *repo);
+static int lookup_default_remote(git_remote **remote, git_repository *repo);
+static int submodule_load_each(const git_config_entry *entry, void *payload);
+static int submodule_read_config(git_submodule *sm, git_config *cfg);
+static int submodule_load_from_wd_lite(git_submodule *);
+static void submodule_get_index_status(unsigned int *, git_submodule *);
+static void submodule_get_wd_status(unsigned int *, git_submodule *, git_repository *, git_submodule_ignore_t);
+static void submodule_update_from_index_entry(git_submodule *sm, const git_index_entry *ie);
+static void submodule_update_from_head_data(git_submodule *sm, mode_t mode, const git_oid *id);
+
+static int submodule_cmp(const void *a, const void *b)
+{
+ return strcmp(((git_submodule *)a)->name, ((git_submodule *)b)->name);
+}
+
+static int submodule_config_key_trunc_puts(git_str *key, const char *suffix)
+{
+ ssize_t idx = git_str_rfind(key, '.');
+ git_str_truncate(key, (size_t)(idx + 1));
+ return git_str_puts(key, suffix);
+}
+
+/*
+ * PUBLIC APIS
+ */
+
+static void submodule_set_lookup_error(int error, const char *name)
+{
+ if (!error)
+ return;
+
+ git_error_set(GIT_ERROR_SUBMODULE, (error == GIT_ENOTFOUND) ?
+ "no submodule named '%s'" :
+ "submodule '%s' has not been added yet", name);
+}
+
+typedef struct {
+ const char *path;
+ char *name;
+} fbp_data;
+
+static int find_by_path(const git_config_entry *entry, void *payload)
+{
+ fbp_data *data = payload;
+
+ if (!strcmp(entry->value, data->path)) {
+ const char *fdot, *ldot;
+ fdot = strchr(entry->name, '.');
+ ldot = strrchr(entry->name, '.');
+ data->name = git__strndup(fdot + 1, ldot - fdot - 1);
+ GIT_ERROR_CHECK_ALLOC(data->name);
+ }
+
+ return 0;
+}
+
+/*
+ * Checks to see if the submodule shares its name with a file or directory that
+ * already exists on the index. If so, the submodule cannot be added.
+ */
+static int is_path_occupied(bool *occupied, git_repository *repo, const char *path)
+{
+ int error = 0;
+ git_index *index;
+ git_str dir = GIT_STR_INIT;
+ *occupied = false;
+
+ if ((error = git_repository_index__weakptr(&index, repo)) < 0)
+ goto out;
+
+ if ((error = git_index_find(NULL, index, path)) != GIT_ENOTFOUND) {
+ if (!error) {
+ git_error_set(GIT_ERROR_SUBMODULE,
+ "File '%s' already exists in the index", path);
+ *occupied = true;
+ }
+ goto out;
+ }
+
+ if ((error = git_str_sets(&dir, path)) < 0)
+ goto out;
+
+ if ((error = git_fs_path_to_dir(&dir)) < 0)
+ goto out;
+
+ if ((error = git_index_find_prefix(NULL, index, dir.ptr)) != GIT_ENOTFOUND) {
+ if (!error) {
+ git_error_set(GIT_ERROR_SUBMODULE,
+ "Directory '%s' already exists in the index", path);
+ *occupied = true;
+ }
+ goto out;
+ }
+
+ error = 0;
+
+out:
+ git_str_dispose(&dir);
+ return error;
+}
+
+/**
+ * Release the name map returned by 'load_submodule_names'.
+ */
+static void free_submodule_names(git_strmap *names)
+{
+ const char *key;
+ char *value;
+
+ if (names == NULL)
+ return;
+
+ git_strmap_foreach(names, key, value, {
+ git__free((char *) key);
+ git__free(value);
+ });
+ git_strmap_free(names);
+
+ return;
+}
+
+/**
+ * Map submodule paths to names.
+ * TODO: for some use-cases, this might need case-folding on a
+ * case-insensitive filesystem
+ */
+static int load_submodule_names(git_strmap **out, git_repository *repo, git_config *cfg)
+{
+ const char *key = "submodule\\..*\\.path";
+ git_config_iterator *iter = NULL;
+ git_config_entry *entry;
+ git_str buf = GIT_STR_INIT;
+ git_strmap *names;
+ int isvalid, error;
+
+ *out = NULL;
+
+ if ((error = git_strmap_new(&names)) < 0)
+ goto out;
+
+ if ((error = git_config_iterator_glob_new(&iter, cfg, key)) < 0)
+ goto out;
+
+ while ((error = git_config_next(&entry, iter)) == 0) {
+ const char *fdot, *ldot;
+ fdot = strchr(entry->name, '.');
+ ldot = strrchr(entry->name, '.');
+
+ if (git_strmap_exists(names, entry->value)) {
+ git_error_set(GIT_ERROR_SUBMODULE,
+ "duplicated submodule path '%s'", entry->value);
+ error = -1;
+ goto out;
+ }
+
+ git_str_clear(&buf);
+ git_str_put(&buf, fdot + 1, ldot - fdot - 1);
+ isvalid = git_submodule_name_is_valid(repo, buf.ptr, 0);
+ if (isvalid < 0) {
+ error = isvalid;
+ goto out;
+ }
+ if (!isvalid)
+ continue;
+
+ if ((error = git_strmap_set(names, git__strdup(entry->value), git_str_detach(&buf))) < 0) {
+ git_error_set(GIT_ERROR_NOMEMORY, "error inserting submodule into hash table");
+ error = -1;
+ goto out;
+ }
+ }
+ if (error == GIT_ITEROVER)
+ error = 0;
+
+ *out = names;
+ names = NULL;
+
+out:
+ free_submodule_names(names);
+ git_str_dispose(&buf);
+ git_config_iterator_free(iter);
+ return error;
+}
+
+int git_submodule_cache_init(git_strmap **out, git_repository *repo)
+{
+ int error = 0;
+ git_strmap *cache = NULL;
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ if ((error = git_strmap_new(&cache)) < 0)
+ return error;
+ if ((error = git_submodule__map(repo, cache)) < 0) {
+ git_submodule_cache_free(cache);
+ return error;
+ }
+ *out = cache;
+ return error;
+}
+
+int git_submodule_cache_free(git_strmap *cache)
+{
+ git_submodule *sm = NULL;
+ if (cache == NULL)
+ return 0;
+ git_strmap_foreach_value(cache, sm, {
+ git_submodule_free(sm);
+ });
+ git_strmap_free(cache);
+ return 0;
+}
+
+int git_submodule_lookup(
+ git_submodule **out, /* NULL if user only wants to test existence */
+ git_repository *repo,
+ const char *name) /* trailing slash is allowed */
+{
+ return git_submodule__lookup_with_cache(out, repo, name, repo->submodule_cache);
+}
+
+int git_submodule__lookup_with_cache(
+ git_submodule **out, /* NULL if user only wants to test existence */
+ git_repository *repo,
+ const char *name, /* trailing slash is allowed */
+ git_strmap *cache)
+{
+ int error;
+ unsigned int location;
+ git_submodule *sm;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
+
+ if (repo->is_bare) {
+ git_error_set(GIT_ERROR_SUBMODULE, "cannot get submodules without a working tree");
+ return -1;
+ }
+
+ if (cache != NULL) {
+ if ((sm = git_strmap_get(cache, name)) != NULL) {
+ if (out) {
+ *out = sm;
+ GIT_REFCOUNT_INC(*out);
+ }
+ return 0;
+ }
+ }
+
+ if ((error = submodule_alloc(&sm, repo, name)) < 0)
+ return error;
+
+ if ((error = git_submodule_reload(sm, false)) < 0) {
+ git_submodule_free(sm);
+ return error;
+ }
+
+ if ((error = git_submodule_location(&location, sm)) < 0) {
+ git_submodule_free(sm);
+ return error;
+ }
+
+ /* If it's not configured or we're looking by path */
+ if (location == 0 || location == GIT_SUBMODULE_STATUS_IN_WD) {
+ git_config_backend *mods;
+ const char *pattern = "submodule\\..*\\.path";
+ git_str path = GIT_STR_INIT;
+ fbp_data data = { NULL, NULL };
+
+ git_str_puts(&path, name);
+ while (path.ptr[path.size-1] == '/') {
+ path.ptr[--path.size] = '\0';
+ }
+ data.path = path.ptr;
+
+ mods = open_gitmodules(repo, GITMODULES_EXISTING);
+
+ if (mods)
+ error = git_config_backend_foreach_match(mods, pattern, find_by_path, &data);
+
+ git_config_backend_free(mods);
+
+ if (error < 0) {
+ git_submodule_free(sm);
+ git_str_dispose(&path);
+ return error;
+ }
+
+ if (data.name) {
+ git__free(sm->name);
+ sm->name = data.name;
+ sm->path = git_str_detach(&path);
+
+ /* Try to load again with the right name */
+ if ((error = git_submodule_reload(sm, false)) < 0) {
+ git_submodule_free(sm);
+ return error;
+ }
+ }
+
+ git_str_dispose(&path);
+ }
+
+ if ((error = git_submodule_location(&location, sm)) < 0) {
+ git_submodule_free(sm);
+ return error;
+ }
+
+ /* If we still haven't found it, do the WD check */
+ if (location == 0 || location == GIT_SUBMODULE_STATUS_IN_WD) {
+ git_submodule_free(sm);
+ error = GIT_ENOTFOUND;
+
+ /* If it's not configured, we still check if there's a repo at the path */
+ if (git_repository_workdir(repo)) {
+ git_str path = GIT_STR_INIT;
+ if (git_str_join3(&path, '/',
+ git_repository_workdir(repo),
+ name, DOT_GIT) < 0 ||
+ git_path_validate_str_length(NULL, &path) < 0)
+ return -1;
+
+ if (git_fs_path_exists(path.ptr))
+ error = GIT_EEXISTS;
+
+ git_str_dispose(&path);
+ }
+
+ submodule_set_lookup_error(error, name);
+ return error;
+ }
+
+ if (out)
+ *out = sm;
+ else
+ git_submodule_free(sm);
+
+ return 0;
+}
+
+int git_submodule_name_is_valid(git_repository *repo, const char *name, int flags)
+{
+ git_str buf = GIT_STR_INIT;
+ int error, isvalid;
+
+ if (flags == 0)
+ flags = GIT_FS_PATH_REJECT_FILESYSTEM_DEFAULTS;
+
+ /* Avoid allocating a new string if we can avoid it */
+ if (strchr(name, '\\') != NULL) {
+ if ((error = git_fs_path_normalize_slashes(&buf, name)) < 0)
+ return error;
+ } else {
+ git_str_attach_notowned(&buf, name, strlen(name));
+ }
+
+ isvalid = git_path_is_valid(repo, buf.ptr, 0, flags);
+ git_str_dispose(&buf);
+
+ return isvalid;
+}
+
+static void submodule_free_dup(void *sm)
+{
+ git_submodule_free(sm);
+}
+
+static int submodule_get_or_create(git_submodule **out, git_repository *repo, git_strmap *map, const char *name)
+{
+ git_submodule *sm = NULL;
+ int error;
+
+ if ((sm = git_strmap_get(map, name)) != NULL)
+ goto done;
+
+ /* if the submodule doesn't exist yet in the map, create it */
+ if ((error = submodule_alloc(&sm, repo, name)) < 0)
+ return error;
+
+ if ((error = git_strmap_set(map, sm->name, sm)) < 0) {
+ git_submodule_free(sm);
+ return error;
+ }
+
+done:
+ GIT_REFCOUNT_INC(sm);
+ *out = sm;
+ return 0;
+}
+
+static int submodules_from_index(git_strmap *map, git_index *idx, git_config *cfg)
+{
+ int error;
+ git_iterator *i = NULL;
+ const git_index_entry *entry;
+ git_strmap *names;
+
+ if ((error = load_submodule_names(&names, git_index_owner(idx), cfg)))
+ goto done;
+
+ if ((error = git_iterator_for_index(&i, git_index_owner(idx), idx, NULL)) < 0)
+ goto done;
+
+ while (!(error = git_iterator_advance(&entry, i))) {
+ git_submodule *sm;
+
+ if ((sm = git_strmap_get(map, entry->path)) != NULL) {
+ if (S_ISGITLINK(entry->mode))
+ submodule_update_from_index_entry(sm, entry);
+ else
+ sm->flags |= GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE;
+ } else if (S_ISGITLINK(entry->mode)) {
+ const char *name;
+
+ if ((name = git_strmap_get(names, entry->path)) == NULL)
+ name = entry->path;
+
+ if (!submodule_get_or_create(&sm, git_index_owner(idx), map, name)) {
+ submodule_update_from_index_entry(sm, entry);
+ git_submodule_free(sm);
+ }
+ }
+ }
+
+ if (error == GIT_ITEROVER)
+ error = 0;
+
+done:
+ git_iterator_free(i);
+ free_submodule_names(names);
+
+ return error;
+}
+
+static int submodules_from_head(git_strmap *map, git_tree *head, git_config *cfg)
+{
+ int error;
+ git_iterator *i = NULL;
+ const git_index_entry *entry;
+ git_strmap *names;
+
+ if ((error = load_submodule_names(&names, git_tree_owner(head), cfg)))
+ goto done;
+
+ if ((error = git_iterator_for_tree(&i, head, NULL)) < 0)
+ goto done;
+
+ while (!(error = git_iterator_advance(&entry, i))) {
+ git_submodule *sm;
+
+ if ((sm = git_strmap_get(map, entry->path)) != NULL) {
+ if (S_ISGITLINK(entry->mode))
+ submodule_update_from_head_data(sm, entry->mode, &entry->id);
+ else
+ sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE;
+ } else if (S_ISGITLINK(entry->mode)) {
+ const char *name;
+
+ if ((name = git_strmap_get(names, entry->path)) == NULL)
+ name = entry->path;
+
+ if (!submodule_get_or_create(&sm, git_tree_owner(head), map, name)) {
+ submodule_update_from_head_data(
+ sm, entry->mode, &entry->id);
+ git_submodule_free(sm);
+ }
+ }
+ }
+
+ if (error == GIT_ITEROVER)
+ error = 0;
+
+done:
+ git_iterator_free(i);
+ free_submodule_names(names);
+
+ return error;
+}
+
+/* If have_sm is true, sm is populated, otherwise map an repo are. */
+typedef struct {
+ git_config *mods;
+ git_strmap *map;
+ git_repository *repo;
+} lfc_data;
+
+int git_submodule__map(git_repository *repo, git_strmap *map)
+{
+ int error = 0;
+ git_index *idx = NULL;
+ git_tree *head = NULL;
+ git_str path = GIT_STR_INIT;
+ git_submodule *sm;
+ git_config *mods = NULL;
+ bool has_workdir;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(map);
+
+ /* get sources that we will need to check */
+ if (git_repository_index(&idx, repo) < 0)
+ git_error_clear();
+ if (git_repository_head_tree(&head, repo) < 0)
+ git_error_clear();
+
+ has_workdir = git_repository_workdir(repo) != NULL;
+
+ if (has_workdir &&
+ (error = git_repository_workdir_path(&path, repo, GIT_MODULES_FILE)) < 0)
+ goto cleanup;
+
+ /* add submodule information from .gitmodules */
+ if (has_workdir) {
+ lfc_data data = { 0 };
+ data.map = map;
+ data.repo = repo;
+
+ if ((error = gitmodules_snapshot(&mods, repo)) < 0) {
+ if (error == GIT_ENOTFOUND)
+ error = 0;
+ goto cleanup;
+ }
+
+ data.mods = mods;
+ if ((error = git_config_foreach(
+ mods, submodule_load_each, &data)) < 0)
+ goto cleanup;
+ }
+ /* add back submodule information from index */
+ if (mods && idx) {
+ if ((error = submodules_from_index(map, idx, mods)) < 0)
+ goto cleanup;
+ }
+ /* add submodule information from HEAD */
+ if (mods && head) {
+ if ((error = submodules_from_head(map, head, mods)) < 0)
+ goto cleanup;
+ }
+ /* shallow scan submodules in work tree as needed */
+ if (has_workdir) {
+ git_strmap_foreach_value(map, sm, {
+ submodule_load_from_wd_lite(sm);
+ });
+ }
+
+cleanup:
+ git_config_free(mods);
+ /* TODO: if we got an error, mark submodule config as invalid? */
+ git_index_free(idx);
+ git_tree_free(head);
+ git_str_dispose(&path);
+ return error;
+}
+
+int git_submodule_foreach(
+ git_repository *repo,
+ git_submodule_cb callback,
+ void *payload)
+{
+ git_vector snapshot = GIT_VECTOR_INIT;
+ git_strmap *submodules;
+ git_submodule *sm;
+ int error;
+ size_t i;
+
+ if (repo->is_bare) {
+ git_error_set(GIT_ERROR_SUBMODULE, "cannot get submodules without a working tree");
+ return -1;
+ }
+
+ if ((error = git_strmap_new(&submodules)) < 0)
+ return error;
+
+ if ((error = git_submodule__map(repo, submodules)) < 0)
+ goto done;
+
+ if (!(error = git_vector_init(
+ &snapshot, git_strmap_size(submodules), submodule_cmp))) {
+
+ git_strmap_foreach_value(submodules, sm, {
+ if ((error = git_vector_insert(&snapshot, sm)) < 0)
+ break;
+ GIT_REFCOUNT_INC(sm);
+ });
+ }
+
+ if (error < 0)
+ goto done;
+
+ git_vector_uniq(&snapshot, submodule_free_dup);
+
+ git_vector_foreach(&snapshot, i, sm) {
+ if ((error = callback(sm, sm->name, payload)) != 0) {
+ git_error_set_after_callback(error);
+ break;
+ }
+ }
+
+done:
+ git_vector_foreach(&snapshot, i, sm)
+ git_submodule_free(sm);
+ git_vector_free(&snapshot);
+
+ git_strmap_foreach_value(submodules, sm, {
+ git_submodule_free(sm);
+ });
+ git_strmap_free(submodules);
+
+ return error;
+}
+
+static int submodule_repo_init(
+ git_repository **out,
+ git_repository *parent_repo,
+ const char *path,
+ const char *url,
+ bool use_gitlink)
+{
+ int error = 0;
+ git_str workdir = GIT_STR_INIT, repodir = GIT_STR_INIT;
+ git_repository_init_options initopt = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+ git_repository *subrepo = NULL;
+
+ error = git_repository_workdir_path(&workdir, parent_repo, path);
+ if (error < 0)
+ goto cleanup;
+
+ initopt.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_NO_REINIT;
+ initopt.origin_url = url;
+
+ /* init submodule repository and add origin remote as needed */
+
+ /* New style: sub-repo goes in <repo-dir>/modules/<name>/ with a
+ * gitlink in the sub-repo workdir directory to that repository
+ *
+ * Old style: sub-repo goes directly into repo/<name>/.git/
+ */
+ if (use_gitlink) {
+ error = git_repository__item_path(&repodir, parent_repo, GIT_REPOSITORY_ITEM_MODULES);
+ if (error < 0)
+ goto cleanup;
+ error = git_str_joinpath(&repodir, repodir.ptr, path);
+ if (error < 0)
+ goto cleanup;
+
+ initopt.workdir_path = workdir.ptr;
+ initopt.flags |=
+ GIT_REPOSITORY_INIT_NO_DOTGIT_DIR |
+ GIT_REPOSITORY_INIT_RELATIVE_GITLINK;
+
+ error = git_repository_init_ext(&subrepo, repodir.ptr, &initopt);
+ } else
+ error = git_repository_init_ext(&subrepo, workdir.ptr, &initopt);
+
+cleanup:
+ git_str_dispose(&workdir);
+ git_str_dispose(&repodir);
+
+ *out = subrepo;
+
+ return error;
+}
+
+static int git_submodule__resolve_url(
+ git_str *out,
+ git_repository *repo,
+ const char *url)
+{
+ int error = 0;
+ git_str normalized = GIT_STR_INIT;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(url);
+
+ /* We do this in all platforms in case someone on Windows created the .gitmodules */
+ if (strchr(url, '\\')) {
+ if ((error = git_fs_path_normalize_slashes(&normalized, url)) < 0)
+ return error;
+
+ url = normalized.ptr;
+ }
+
+
+ if (git_fs_path_is_relative(url)) {
+ if (!(error = get_url_base(out, repo)))
+ error = git_fs_path_apply_relative(out, url);
+ } else if (strchr(url, ':') != NULL || url[0] == '/') {
+ error = git_str_sets(out, url);
+ } else {
+ git_error_set(GIT_ERROR_SUBMODULE, "invalid format for submodule URL");
+ error = -1;
+ }
+
+ git_str_dispose(&normalized);
+ return error;
+}
+
+int git_submodule_resolve_url(
+ git_buf *out,
+ git_repository *repo,
+ const char *url)
+{
+ GIT_BUF_WRAP_PRIVATE(out, git_submodule__resolve_url, repo, url);
+}
+
+int git_submodule_add_setup(
+ git_submodule **out,
+ git_repository *repo,
+ const char *url,
+ const char *path,
+ int use_gitlink)
+{
+ int error = 0;
+ git_config_backend *mods = NULL;
+ git_submodule *sm = NULL;
+ git_str name = GIT_STR_INIT, real_url = GIT_STR_INIT;
+ git_repository *subrepo = NULL;
+ bool path_occupied;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(url);
+ GIT_ASSERT_ARG(path);
+
+ /* see if there is already an entry for this submodule */
+
+ if (git_submodule_lookup(NULL, repo, path) < 0)
+ git_error_clear();
+ else {
+ git_error_set(GIT_ERROR_SUBMODULE,
+ "attempt to add submodule '%s' that already exists", path);
+ return GIT_EEXISTS;
+ }
+
+ /* validate and normalize path */
+
+ if (git__prefixcmp(path, git_repository_workdir(repo)) == 0)
+ path += strlen(git_repository_workdir(repo));
+
+ if (git_fs_path_root(path) >= 0) {
+ git_error_set(GIT_ERROR_SUBMODULE, "submodule path must be a relative path");
+ error = -1;
+ goto cleanup;
+ }
+
+ if ((error = is_path_occupied(&path_occupied, repo, path)) < 0)
+ goto cleanup;
+
+ if (path_occupied) {
+ error = GIT_EEXISTS;
+ goto cleanup;
+ }
+
+ /* update .gitmodules */
+
+ if (!(mods = open_gitmodules(repo, GITMODULES_CREATE))) {
+ git_error_set(GIT_ERROR_SUBMODULE,
+ "adding submodules to a bare repository is not supported");
+ return -1;
+ }
+
+ if ((error = git_str_printf(&name, "submodule.%s.path", path)) < 0 ||
+ (error = git_config_backend_set_string(mods, name.ptr, path)) < 0)
+ goto cleanup;
+
+ if ((error = submodule_config_key_trunc_puts(&name, "url")) < 0 ||
+ (error = git_config_backend_set_string(mods, name.ptr, url)) < 0)
+ goto cleanup;
+
+ git_str_clear(&name);
+
+ /* init submodule repository and add origin remote as needed */
+
+ error = git_repository_workdir_path(&name, repo, path);
+ if (error < 0)
+ goto cleanup;
+
+ /* if the repo does not already exist, then init a new repo and add it.
+ * Otherwise, just add the existing repo.
+ */
+ if (!(git_fs_path_exists(name.ptr) &&
+ git_fs_path_contains(&name, DOT_GIT))) {
+
+ /* resolve the actual URL to use */
+ if ((error = git_submodule__resolve_url(&real_url, repo, url)) < 0)
+ goto cleanup;
+
+ if ((error = submodule_repo_init(&subrepo, repo, path, real_url.ptr, use_gitlink)) < 0)
+ goto cleanup;
+ }
+
+ if ((error = git_submodule_lookup(&sm, repo, path)) < 0)
+ goto cleanup;
+
+ error = git_submodule_init(sm, false);
+
+cleanup:
+ if (error && sm) {
+ git_submodule_free(sm);
+ sm = NULL;
+ }
+ if (out != NULL)
+ *out = sm;
+
+ git_config_backend_free(mods);
+ git_repository_free(subrepo);
+ git_str_dispose(&real_url);
+ git_str_dispose(&name);
+
+ return error;
+}
+
+int git_submodule_repo_init(
+ git_repository **out,
+ const git_submodule *sm,
+ int use_gitlink)
+{
+ int error;
+ git_repository *sub_repo = NULL;
+ const char *configured_url;
+ git_config *cfg = NULL;
+ git_str buf = GIT_STR_INIT;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(sm);
+
+ /* get the configured remote url of the submodule */
+ if ((error = git_str_printf(&buf, "submodule.%s.url", sm->name)) < 0 ||
+ (error = git_repository_config_snapshot(&cfg, sm->repo)) < 0 ||
+ (error = git_config_get_string(&configured_url, cfg, buf.ptr)) < 0 ||
+ (error = submodule_repo_init(&sub_repo, sm->repo, sm->path, configured_url, use_gitlink)) < 0)
+ goto done;
+
+ *out = sub_repo;
+
+done:
+ git_config_free(cfg);
+ git_str_dispose(&buf);
+ return error;
+}
+
+static int clone_return_origin(git_remote **out, git_repository *repo, const char *name, const char *url, void *payload)
+{
+ GIT_UNUSED(url);
+ GIT_UNUSED(payload);
+ return git_remote_lookup(out, repo, name);
+}
+
+static int clone_return_repo(git_repository **out, const char *path, int bare, void *payload)
+{
+ git_submodule *sm = payload;
+
+ GIT_UNUSED(path);
+ GIT_UNUSED(bare);
+ return git_submodule_open(out, sm);
+}
+
+int git_submodule_clone(git_repository **out, git_submodule *submodule, const git_submodule_update_options *given_opts)
+{
+ int error;
+ git_repository *clone;
+ git_str rel_path = GIT_STR_INIT;
+ git_submodule_update_options sub_opts = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
+ git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
+
+ GIT_ASSERT_ARG(submodule);
+
+ if (given_opts)
+ memcpy(&sub_opts, given_opts, sizeof(sub_opts));
+
+ GIT_ERROR_CHECK_VERSION(&sub_opts, GIT_SUBMODULE_UPDATE_OPTIONS_VERSION, "git_submodule_update_options");
+
+ memcpy(&opts.checkout_opts, &sub_opts.checkout_opts, sizeof(sub_opts.checkout_opts));
+ memcpy(&opts.fetch_opts, &sub_opts.fetch_opts, sizeof(sub_opts.fetch_opts));
+ opts.repository_cb = clone_return_repo;
+ opts.repository_cb_payload = submodule;
+ opts.remote_cb = clone_return_origin;
+ opts.remote_cb_payload = submodule;
+
+ error = git_repository_workdir_path(&rel_path, git_submodule_owner(submodule), git_submodule_path(submodule));
+ if (error < 0)
+ goto cleanup;
+
+ error = git_clone__submodule(&clone, git_submodule_url(submodule), git_str_cstr(&rel_path), &opts);
+ if (error < 0)
+ goto cleanup;
+
+ if (!out)
+ git_repository_free(clone);
+ else
+ *out = clone;
+
+cleanup:
+ git_str_dispose(&rel_path);
+
+ return error;
+}
+
+int git_submodule_add_finalize(git_submodule *sm)
+{
+ int error;
+ git_index *index;
+
+ GIT_ASSERT_ARG(sm);
+
+ if ((error = git_repository_index__weakptr(&index, sm->repo)) < 0 ||
+ (error = git_index_add_bypath(index, GIT_MODULES_FILE)) < 0)
+ return error;
+
+ return git_submodule_add_to_index(sm, true);
+}
+
+int git_submodule_add_to_index(git_submodule *sm, int write_index)
+{
+ int error;
+ git_repository *sm_repo = NULL;
+ git_index *index;
+ git_str path = GIT_STR_INIT;
+ git_commit *head;
+ git_index_entry entry;
+ struct stat st;
+
+ GIT_ASSERT_ARG(sm);
+
+ /* force reload of wd OID by git_submodule_open */
+ sm->flags = sm->flags & ~GIT_SUBMODULE_STATUS__WD_OID_VALID;
+
+ if ((error = git_repository_index__weakptr(&index, sm->repo)) < 0 ||
+ (error = git_repository_workdir_path(&path, sm->repo, sm->path)) < 0 ||
+ (error = git_submodule_open(&sm_repo, sm)) < 0)
+ goto cleanup;
+
+ /* read stat information for submodule working directory */
+ if (p_stat(path.ptr, &st) < 0) {
+ git_error_set(GIT_ERROR_SUBMODULE,
+ "cannot add submodule without working directory");
+ error = -1;
+ goto cleanup;
+ }
+
+ memset(&entry, 0, sizeof(entry));
+ entry.path = sm->path;
+ git_index_entry__init_from_stat(
+ &entry, &st, !(git_index_caps(index) & GIT_INDEX_CAPABILITY_NO_FILEMODE));
+
+ /* calling git_submodule_open will have set sm->wd_oid if possible */
+ if ((sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) == 0) {
+ git_error_set(GIT_ERROR_SUBMODULE,
+ "cannot add submodule without HEAD to index");
+ error = -1;
+ goto cleanup;
+ }
+ git_oid_cpy(&entry.id, &sm->wd_oid);
+
+ if ((error = git_commit_lookup(&head, sm_repo, &sm->wd_oid)) < 0)
+ goto cleanup;
+
+ entry.ctime.seconds = (int32_t)git_commit_time(head);
+ entry.ctime.nanoseconds = 0;
+ entry.mtime.seconds = (int32_t)git_commit_time(head);
+ entry.mtime.nanoseconds = 0;
+
+ git_commit_free(head);
+
+ /* add it */
+ error = git_index_add(index, &entry);
+
+ /* write it, if requested */
+ if (!error && write_index) {
+ error = git_index_write(index);
+
+ if (!error)
+ git_oid_cpy(&sm->index_oid, &sm->wd_oid);
+ }
+
+cleanup:
+ git_repository_free(sm_repo);
+ git_str_dispose(&path);
+ return error;
+}
+
+static const char *submodule_update_to_str(git_submodule_update_t update)
+{
+ int i;
+ for (i = 0; i < (int)ARRAY_SIZE(_sm_update_map); ++i)
+ if (_sm_update_map[i].map_value == (int)update)
+ return _sm_update_map[i].str_match;
+ return NULL;
+}
+
+git_repository *git_submodule_owner(git_submodule *submodule)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(submodule, NULL);
+ return submodule->repo;
+}
+
+const char *git_submodule_name(git_submodule *submodule)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(submodule, NULL);
+ return submodule->name;
+}
+
+const char *git_submodule_path(git_submodule *submodule)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(submodule, NULL);
+ return submodule->path;
+}
+
+const char *git_submodule_url(git_submodule *submodule)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(submodule, NULL);
+ return submodule->url;
+}
+
+static int write_var(git_repository *repo, const char *name, const char *var, const char *val)
+{
+ git_str key = GIT_STR_INIT;
+ git_config_backend *mods;
+ int error;
+
+ mods = open_gitmodules(repo, GITMODULES_CREATE);
+ if (!mods)
+ return -1;
+
+ if ((error = git_str_printf(&key, "submodule.%s.%s", name, var)) < 0)
+ goto cleanup;
+
+ if (val)
+ error = git_config_backend_set_string(mods, key.ptr, val);
+ else
+ error = git_config_backend_delete(mods, key.ptr);
+
+ git_str_dispose(&key);
+
+cleanup:
+ git_config_backend_free(mods);
+ return error;
+}
+
+static int write_mapped_var(git_repository *repo, const char *name, git_configmap *maps, size_t nmaps, const char *var, int ival)
+{
+ git_configmap_t type;
+ const char *val;
+
+ if (git_config_lookup_map_enum(&type, &val, maps, nmaps, ival) < 0) {
+ git_error_set(GIT_ERROR_SUBMODULE, "invalid value for %s", var);
+ return -1;
+ }
+
+ if (type == GIT_CONFIGMAP_TRUE)
+ val = "true";
+
+ return write_var(repo, name, var, val);
+}
+
+const char *git_submodule_branch(git_submodule *submodule)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(submodule, NULL);
+ return submodule->branch;
+}
+
+int git_submodule_set_branch(git_repository *repo, const char *name, const char *branch)
+{
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
+
+ return write_var(repo, name, "branch", branch);
+}
+
+int git_submodule_set_url(git_repository *repo, const char *name, const char *url)
+{
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
+ GIT_ASSERT_ARG(url);
+
+ return write_var(repo, name, "url", url);
+}
+
+const git_oid *git_submodule_index_id(git_submodule *submodule)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(submodule, NULL);
+
+ if (submodule->flags & GIT_SUBMODULE_STATUS__INDEX_OID_VALID)
+ return &submodule->index_oid;
+ else
+ return NULL;
+}
+
+const git_oid *git_submodule_head_id(git_submodule *submodule)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(submodule, NULL);
+
+ if (submodule->flags & GIT_SUBMODULE_STATUS__HEAD_OID_VALID)
+ return &submodule->head_oid;
+ else
+ return NULL;
+}
+
+const git_oid *git_submodule_wd_id(git_submodule *submodule)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(submodule, NULL);
+
+ /* load unless we think we have a valid oid */
+ if (!(submodule->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID)) {
+ git_repository *subrepo;
+
+ /* calling submodule open grabs the HEAD OID if possible */
+ if (!git_submodule_open_bare(&subrepo, submodule))
+ git_repository_free(subrepo);
+ else
+ git_error_clear();
+ }
+
+ if (submodule->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID)
+ return &submodule->wd_oid;
+ else
+ return NULL;
+}
+
+git_submodule_ignore_t git_submodule_ignore(git_submodule *submodule)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(submodule, GIT_SUBMODULE_IGNORE_UNSPECIFIED);
+
+ return (submodule->ignore < GIT_SUBMODULE_IGNORE_NONE) ?
+ GIT_SUBMODULE_IGNORE_NONE : submodule->ignore;
+}
+
+int git_submodule_set_ignore(git_repository *repo, const char *name, git_submodule_ignore_t ignore)
+{
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
+
+ return write_mapped_var(repo, name, _sm_ignore_map, ARRAY_SIZE(_sm_ignore_map), "ignore", ignore);
+}
+
+git_submodule_update_t git_submodule_update_strategy(git_submodule *submodule)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(submodule, GIT_SUBMODULE_UPDATE_NONE);
+
+ return (submodule->update < GIT_SUBMODULE_UPDATE_CHECKOUT) ?
+ GIT_SUBMODULE_UPDATE_CHECKOUT : submodule->update;
+}
+
+int git_submodule_set_update(git_repository *repo, const char *name, git_submodule_update_t update)
+{
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
+
+ return write_mapped_var(repo, name, _sm_update_map, ARRAY_SIZE(_sm_update_map), "update", update);
+}
+
+git_submodule_recurse_t git_submodule_fetch_recurse_submodules(
+ git_submodule *submodule)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(submodule, GIT_SUBMODULE_RECURSE_NO);
+ return submodule->fetch_recurse;
+}
+
+int git_submodule_set_fetch_recurse_submodules(git_repository *repo, const char *name, git_submodule_recurse_t recurse)
+{
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
+
+ return write_mapped_var(repo, name, _sm_recurse_map, ARRAY_SIZE(_sm_recurse_map), "fetchRecurseSubmodules", recurse);
+}
+
+static int submodule_repo_create(
+ git_repository **out,
+ git_repository *parent_repo,
+ const char *path)
+{
+ int error = 0;
+ git_str workdir = GIT_STR_INIT, repodir = GIT_STR_INIT;
+ git_repository_init_options initopt = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+ git_repository *subrepo = NULL;
+
+ initopt.flags =
+ GIT_REPOSITORY_INIT_MKPATH |
+ GIT_REPOSITORY_INIT_NO_REINIT |
+ GIT_REPOSITORY_INIT_NO_DOTGIT_DIR |
+ GIT_REPOSITORY_INIT_RELATIVE_GITLINK;
+
+ /* Workdir: path to sub-repo working directory */
+ error = git_repository_workdir_path(&workdir, parent_repo, path);
+ if (error < 0)
+ goto cleanup;
+
+ initopt.workdir_path = workdir.ptr;
+
+ /**
+ * Repodir: path to the sub-repo. sub-repo goes in:
+ * <repo-dir>/modules/<name>/ with a gitlink in the
+ * sub-repo workdir directory to that repository.
+ */
+ error = git_repository__item_path(&repodir, parent_repo, GIT_REPOSITORY_ITEM_MODULES);
+ if (error < 0)
+ goto cleanup;
+ error = git_str_joinpath(&repodir, repodir.ptr, path);
+ if (error < 0)
+ goto cleanup;
+
+ error = git_repository_init_ext(&subrepo, repodir.ptr, &initopt);
+
+cleanup:
+ git_str_dispose(&workdir);
+ git_str_dispose(&repodir);
+
+ *out = subrepo;
+
+ return error;
+}
+
+/**
+ * Callback to override sub-repository creation when
+ * cloning a sub-repository.
+ */
+static int git_submodule_update_repo_init_cb(
+ git_repository **out,
+ const char *path,
+ int bare,
+ void *payload)
+{
+ git_submodule *sm;
+
+ GIT_UNUSED(bare);
+
+ sm = payload;
+
+ return submodule_repo_create(out, sm->repo, path);
+}
+
+int git_submodule_update_options_init(git_submodule_update_options *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_submodule_update_options, GIT_SUBMODULE_UPDATE_OPTIONS_INIT);
+ return 0;
+}
+
+#ifndef GIT_DEPRECATE_HARD
+int git_submodule_update_init_options(git_submodule_update_options *opts, unsigned int version)
+{
+ return git_submodule_update_options_init(opts, version);
+}
+#endif
+
+int git_submodule_update(git_submodule *sm, int init, git_submodule_update_options *_update_options)
+{
+ int error;
+ unsigned int submodule_status;
+ git_config *config = NULL;
+ const char *submodule_url;
+ git_repository *sub_repo = NULL;
+ git_remote *remote = NULL;
+ git_object *target_commit = NULL;
+ git_str buf = GIT_STR_INIT;
+ git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
+ git_clone_options clone_options = GIT_CLONE_OPTIONS_INIT;
+
+ GIT_ASSERT_ARG(sm);
+
+ if (_update_options)
+ memcpy(&update_options, _update_options, sizeof(git_submodule_update_options));
+
+ GIT_ERROR_CHECK_VERSION(&update_options, GIT_SUBMODULE_UPDATE_OPTIONS_VERSION, "git_submodule_update_options");
+
+ /* Copy over the remote callbacks */
+ memcpy(&clone_options.fetch_opts, &update_options.fetch_opts, sizeof(git_fetch_options));
+
+ /* Get the status of the submodule to determine if it is already initialized */
+ if ((error = git_submodule_status(&submodule_status, sm->repo, sm->name, GIT_SUBMODULE_IGNORE_UNSPECIFIED)) < 0)
+ goto done;
+
+ /* If the submodule is configured but hasn't been added, skip it */
+ if (submodule_status == GIT_SUBMODULE_STATUS_IN_CONFIG)
+ goto done;
+
+ /*
+ * If submodule work dir is not already initialized, check to see
+ * what we need to do (initialize, clone, return error...)
+ */
+ if (submodule_status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) {
+ /*
+ * Work dir is not initialized, check to see if the submodule
+ * info has been copied into .git/config
+ */
+ if ((error = git_repository_config_snapshot(&config, sm->repo)) < 0 ||
+ (error = git_str_printf(&buf, "submodule.%s.url", git_submodule_name(sm))) < 0)
+ goto done;
+
+ if ((error = git_config_get_string(&submodule_url, config, git_str_cstr(&buf))) < 0) {
+ /*
+ * If the error is not "not found" or if it is "not found" and we are not
+ * initializing the submodule, then return error.
+ */
+ if (error != GIT_ENOTFOUND)
+ goto done;
+
+ if (!init) {
+ git_error_set(GIT_ERROR_SUBMODULE, "submodule is not initialized");
+ error = GIT_ERROR;
+ goto done;
+ }
+
+ /* The submodule has not been initialized yet - initialize it now.*/
+ if ((error = git_submodule_init(sm, 0)) < 0)
+ goto done;
+
+ git_config_free(config);
+ config = NULL;
+
+ if ((error = git_repository_config_snapshot(&config, sm->repo)) < 0 ||
+ (error = git_config_get_string(&submodule_url, config, git_str_cstr(&buf))) < 0)
+ goto done;
+ }
+
+ /** submodule is initialized - now clone it **/
+ /* override repo creation */
+ clone_options.repository_cb = git_submodule_update_repo_init_cb;
+ clone_options.repository_cb_payload = sm;
+
+ /*
+ * Do not perform checkout as part of clone, instead we
+ * will checkout the specific commit manually.
+ */
+ clone_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_NONE;
+
+ if ((error = git_clone__submodule(&sub_repo, submodule_url, sm->path, &clone_options)) < 0 ||
+ (error = git_repository_set_head_detached(sub_repo, git_submodule_index_id(sm))) < 0 ||
+ (error = git_checkout_head(sub_repo, &update_options.checkout_opts)) != 0)
+ goto done;
+ } else {
+ const git_oid *oid;
+
+ /**
+ * Work dir is initialized - look up the commit in the parent repository's index,
+ * update the workdir contents of the subrepository, and set the subrepository's
+ * head to the new commit.
+ */
+ if ((error = git_submodule_open(&sub_repo, sm)) < 0)
+ goto done;
+
+ if ((oid = git_submodule_index_id(sm)) == NULL) {
+ git_error_set(GIT_ERROR_SUBMODULE, "could not get ID of submodule in index");
+ error = -1;
+ goto done;
+ }
+
+ /* Look up the target commit in the submodule. */
+ if ((error = git_object_lookup(&target_commit, sub_repo, oid, GIT_OBJECT_COMMIT)) < 0) {
+ /* If it isn't found then fetch and try again. */
+ if (error != GIT_ENOTFOUND || !update_options.allow_fetch ||
+ (error = lookup_default_remote(&remote, sub_repo)) < 0 ||
+ (error = git_remote_fetch(remote, NULL, &update_options.fetch_opts, NULL)) < 0 ||
+ (error = git_object_lookup(&target_commit, sub_repo, git_submodule_index_id(sm), GIT_OBJECT_COMMIT)) < 0)
+ goto done;
+ }
+
+ if ((error = git_checkout_tree(sub_repo, target_commit, &update_options.checkout_opts)) != 0 ||
+ (error = git_repository_set_head_detached(sub_repo, git_submodule_index_id(sm))) < 0)
+ goto done;
+
+ /* Invalidate the wd flags as the workdir has been updated. */
+ sm->flags = sm->flags &
+ ~(GIT_SUBMODULE_STATUS_IN_WD |
+ GIT_SUBMODULE_STATUS__WD_OID_VALID |
+ GIT_SUBMODULE_STATUS__WD_SCANNED);
+ }
+
+done:
+ git_str_dispose(&buf);
+ git_config_free(config);
+ git_object_free(target_commit);
+ git_remote_free(remote);
+ git_repository_free(sub_repo);
+
+ return error;
+}
+
+int git_submodule_init(git_submodule *sm, int overwrite)
+{
+ int error;
+ const char *val;
+ git_str key = GIT_STR_INIT, effective_submodule_url = GIT_STR_INIT;
+ git_config *cfg = NULL;
+
+ if (!sm->url) {
+ git_error_set(GIT_ERROR_SUBMODULE,
+ "no URL configured for submodule '%s'", sm->name);
+ return -1;
+ }
+
+ if ((error = git_repository_config(&cfg, sm->repo)) < 0)
+ return error;
+
+ /* write "submodule.NAME.url" */
+
+ if ((error = git_submodule__resolve_url(&effective_submodule_url, sm->repo, sm->url)) < 0 ||
+ (error = git_str_printf(&key, "submodule.%s.url", sm->name)) < 0 ||
+ (error = git_config__update_entry(
+ cfg, key.ptr, effective_submodule_url.ptr, overwrite != 0, false)) < 0)
+ goto cleanup;
+
+ /* write "submodule.NAME.update" if not default */
+
+ val = (sm->update == GIT_SUBMODULE_UPDATE_CHECKOUT) ?
+ NULL : submodule_update_to_str(sm->update);
+
+ if ((error = git_str_printf(&key, "submodule.%s.update", sm->name)) < 0 ||
+ (error = git_config__update_entry(
+ cfg, key.ptr, val, overwrite != 0, false)) < 0)
+ goto cleanup;
+
+ /* success */
+
+cleanup:
+ git_config_free(cfg);
+ git_str_dispose(&key);
+ git_str_dispose(&effective_submodule_url);
+
+ return error;
+}
+
+int git_submodule_sync(git_submodule *sm)
+{
+ git_str key = GIT_STR_INIT, url = GIT_STR_INIT, remote_name = GIT_STR_INIT;
+ git_repository *smrepo = NULL;
+ git_config *cfg = NULL;
+ int error = 0;
+
+ if (!sm->url) {
+ git_error_set(GIT_ERROR_SUBMODULE, "no URL configured for submodule '%s'", sm->name);
+ return -1;
+ }
+
+ /* copy URL over to config only if it already exists */
+ if ((error = git_repository_config__weakptr(&cfg, sm->repo)) < 0 ||
+ (error = git_str_printf(&key, "submodule.%s.url", sm->name)) < 0 ||
+ (error = git_submodule__resolve_url(&url, sm->repo, sm->url)) < 0 ||
+ (error = git_config__update_entry(cfg, key.ptr, url.ptr, true, true)) < 0)
+ goto out;
+
+ if (!(sm->flags & GIT_SUBMODULE_STATUS_IN_WD))
+ goto out;
+
+ /* if submodule exists in the working directory, update remote url */
+ if ((error = git_submodule_open(&smrepo, sm)) < 0 ||
+ (error = git_repository_config__weakptr(&cfg, smrepo)) < 0)
+ goto out;
+
+ if (lookup_head_remote_key(&remote_name, smrepo) == 0) {
+ if ((error = git_str_join3(&key, '.', "remote", remote_name.ptr, "url")) < 0)
+ goto out;
+ } else if ((error = git_str_sets(&key, "remote.origin.url")) < 0) {
+ goto out;
+ }
+
+ if ((error = git_config__update_entry(cfg, key.ptr, url.ptr, true, false)) < 0)
+ goto out;
+
+out:
+ git_repository_free(smrepo);
+ git_str_dispose(&remote_name);
+ git_str_dispose(&key);
+ git_str_dispose(&url);
+ return error;
+}
+
+static int git_submodule__open(
+ git_repository **subrepo, git_submodule *sm, bool bare)
+{
+ int error;
+ git_str path = GIT_STR_INIT;
+ unsigned int flags = GIT_REPOSITORY_OPEN_NO_SEARCH;
+ const char *wd;
+
+ GIT_ASSERT_ARG(sm);
+ GIT_ASSERT_ARG(subrepo);
+
+ if (git_repository__ensure_not_bare(
+ sm->repo, "open submodule repository") < 0)
+ return GIT_EBAREREPO;
+
+ wd = git_repository_workdir(sm->repo);
+
+ if (git_str_join3(&path, '/', wd, sm->path, DOT_GIT) < 0)
+ return -1;
+
+ sm->flags = sm->flags &
+ ~(GIT_SUBMODULE_STATUS_IN_WD |
+ GIT_SUBMODULE_STATUS__WD_OID_VALID |
+ GIT_SUBMODULE_STATUS__WD_SCANNED);
+
+ if (bare)
+ flags |= GIT_REPOSITORY_OPEN_BARE;
+
+ error = git_repository_open_ext(subrepo, path.ptr, flags, wd);
+
+ /* if we opened the submodule successfully, grab HEAD OID, etc. */
+ if (!error) {
+ sm->flags |= GIT_SUBMODULE_STATUS_IN_WD |
+ GIT_SUBMODULE_STATUS__WD_SCANNED;
+
+ if (!git_reference_name_to_id(&sm->wd_oid, *subrepo, GIT_HEAD_FILE))
+ sm->flags |= GIT_SUBMODULE_STATUS__WD_OID_VALID;
+ else
+ git_error_clear();
+ } else if (git_fs_path_exists(path.ptr)) {
+ sm->flags |= GIT_SUBMODULE_STATUS__WD_SCANNED |
+ GIT_SUBMODULE_STATUS_IN_WD;
+ } else {
+ git_str_rtruncate_at_char(&path, '/'); /* remove "/.git" */
+
+ if (git_fs_path_isdir(path.ptr))
+ sm->flags |= GIT_SUBMODULE_STATUS__WD_SCANNED;
+ }
+
+ git_str_dispose(&path);
+
+ return error;
+}
+
+int git_submodule_open_bare(git_repository **subrepo, git_submodule *sm)
+{
+ return git_submodule__open(subrepo, sm, true);
+}
+
+int git_submodule_open(git_repository **subrepo, git_submodule *sm)
+{
+ return git_submodule__open(subrepo, sm, false);
+}
+
+static void submodule_update_from_index_entry(
+ git_submodule *sm, const git_index_entry *ie)
+{
+ bool already_found = (sm->flags & GIT_SUBMODULE_STATUS_IN_INDEX) != 0;
+
+ if (!S_ISGITLINK(ie->mode)) {
+ if (!already_found)
+ sm->flags |= GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE;
+ } else {
+ if (already_found)
+ sm->flags |= GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES;
+ else
+ git_oid_cpy(&sm->index_oid, &ie->id);
+
+ sm->flags |= GIT_SUBMODULE_STATUS_IN_INDEX |
+ GIT_SUBMODULE_STATUS__INDEX_OID_VALID;
+ }
+}
+
+static int submodule_update_index(git_submodule *sm)
+{
+ git_index *index;
+ const git_index_entry *ie;
+
+ if (git_repository_index__weakptr(&index, sm->repo) < 0)
+ return -1;
+
+ sm->flags = sm->flags &
+ ~(GIT_SUBMODULE_STATUS_IN_INDEX |
+ GIT_SUBMODULE_STATUS__INDEX_OID_VALID);
+
+ if (!(ie = git_index_get_bypath(index, sm->path, 0)))
+ return 0;
+
+ submodule_update_from_index_entry(sm, ie);
+
+ return 0;
+}
+
+static void submodule_update_from_head_data(
+ git_submodule *sm, mode_t mode, const git_oid *id)
+{
+ if (!S_ISGITLINK(mode))
+ sm->flags |= GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE;
+ else {
+ git_oid_cpy(&sm->head_oid, id);
+
+ sm->flags |= GIT_SUBMODULE_STATUS_IN_HEAD |
+ GIT_SUBMODULE_STATUS__HEAD_OID_VALID;
+ }
+}
+
+static int submodule_update_head(git_submodule *submodule)
+{
+ git_tree *head = NULL;
+ git_tree_entry *te = NULL;
+
+ submodule->flags = submodule->flags &
+ ~(GIT_SUBMODULE_STATUS_IN_HEAD |
+ GIT_SUBMODULE_STATUS__HEAD_OID_VALID);
+
+ /* if we can't look up file in current head, then done */
+ if (git_repository_head_tree(&head, submodule->repo) < 0 ||
+ git_tree_entry_bypath(&te, head, submodule->path) < 0)
+ git_error_clear();
+ else
+ submodule_update_from_head_data(submodule, te->attr, git_tree_entry_id(te));
+
+ git_tree_entry_free(te);
+ git_tree_free(head);
+ return 0;
+}
+
+int git_submodule_reload(git_submodule *sm, int force)
+{
+ git_config *mods = NULL;
+ int error;
+
+ GIT_UNUSED(force);
+
+ GIT_ASSERT_ARG(sm);
+
+ if ((error = git_submodule_name_is_valid(sm->repo, sm->name, 0)) <= 0)
+ /* This should come with a warning, but we've no API for that */
+ goto out;
+
+ if (git_repository_is_bare(sm->repo))
+ goto out;
+
+ /* refresh config data */
+ if ((error = gitmodules_snapshot(&mods, sm->repo)) < 0 && error != GIT_ENOTFOUND)
+ goto out;
+
+ if (mods != NULL && (error = submodule_read_config(sm, mods)) < 0)
+ goto out;
+
+ /* refresh wd data */
+ sm->flags &=
+ ~(GIT_SUBMODULE_STATUS_IN_WD |
+ GIT_SUBMODULE_STATUS__WD_OID_VALID |
+ GIT_SUBMODULE_STATUS__WD_FLAGS);
+
+ if ((error = submodule_load_from_wd_lite(sm)) < 0 ||
+ (error = submodule_update_index(sm)) < 0 ||
+ (error = submodule_update_head(sm)) < 0)
+ goto out;
+
+out:
+ git_config_free(mods);
+ return error;
+}
+
+static void submodule_copy_oid_maybe(
+ git_oid *tgt, const git_oid *src, bool valid)
+{
+ if (tgt) {
+ if (valid)
+ memcpy(tgt, src, sizeof(*tgt));
+ else
+ memset(tgt, 0, sizeof(*tgt));
+ }
+}
+
+int git_submodule__status(
+ unsigned int *out_status,
+ git_oid *out_head_id,
+ git_oid *out_index_id,
+ git_oid *out_wd_id,
+ git_submodule *sm,
+ git_submodule_ignore_t ign)
+{
+ unsigned int status;
+ git_repository *smrepo = NULL;
+
+ if (ign == GIT_SUBMODULE_IGNORE_UNSPECIFIED)
+ ign = sm->ignore;
+
+ /* only return location info if ignore == all */
+ if (ign == GIT_SUBMODULE_IGNORE_ALL) {
+ *out_status = (sm->flags & GIT_SUBMODULE_STATUS__IN_FLAGS);
+ return 0;
+ }
+
+ /* If the user has requested caching submodule state, performing these
+ * expensive operations (especially `submodule_update_head`, which is
+ * bottlenecked on `git_repository_head_tree`) eliminates much of the
+ * advantage. We will, therefore, interpret the request for caching to
+ * apply here to and skip them.
+ */
+
+ if (sm->repo->submodule_cache == NULL) {
+ /* refresh the index OID */
+ if (submodule_update_index(sm) < 0)
+ return -1;
+
+ /* refresh the HEAD OID */
+ if (submodule_update_head(sm) < 0)
+ return -1;
+ }
+
+ /* for ignore == dirty, don't scan the working directory */
+ if (ign == GIT_SUBMODULE_IGNORE_DIRTY) {
+ /* git_submodule_open_bare will load WD OID data */
+ if (git_submodule_open_bare(&smrepo, sm) < 0)
+ git_error_clear();
+ else
+ git_repository_free(smrepo);
+ smrepo = NULL;
+ } else if (git_submodule_open(&smrepo, sm) < 0) {
+ git_error_clear();
+ smrepo = NULL;
+ }
+
+ status = GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(sm->flags);
+
+ submodule_get_index_status(&status, sm);
+ submodule_get_wd_status(&status, sm, smrepo, ign);
+
+ git_repository_free(smrepo);
+
+ *out_status = status;
+
+ submodule_copy_oid_maybe(out_head_id, &sm->head_oid,
+ (sm->flags & GIT_SUBMODULE_STATUS__HEAD_OID_VALID) != 0);
+ submodule_copy_oid_maybe(out_index_id, &sm->index_oid,
+ (sm->flags & GIT_SUBMODULE_STATUS__INDEX_OID_VALID) != 0);
+ submodule_copy_oid_maybe(out_wd_id, &sm->wd_oid,
+ (sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) != 0);
+
+ return 0;
+}
+
+int git_submodule_status(unsigned int *status, git_repository *repo, const char *name, git_submodule_ignore_t ignore)
+{
+ git_submodule *sm;
+ int error;
+
+ GIT_ASSERT_ARG(status);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
+
+ if ((error = git_submodule_lookup(&sm, repo, name)) < 0)
+ return error;
+
+ error = git_submodule__status(status, NULL, NULL, NULL, sm, ignore);
+ git_submodule_free(sm);
+
+ return error;
+}
+
+int git_submodule_location(unsigned int *location, git_submodule *sm)
+{
+ GIT_ASSERT_ARG(location);
+ GIT_ASSERT_ARG(sm);
+
+ return git_submodule__status(
+ location, NULL, NULL, NULL, sm, GIT_SUBMODULE_IGNORE_ALL);
+}
+
+/*
+ * INTERNAL FUNCTIONS
+ */
+
+static int submodule_alloc(
+ git_submodule **out, git_repository *repo, const char *name)
+{
+ size_t namelen;
+ git_submodule *sm;
+
+ if (!name || !(namelen = strlen(name))) {
+ git_error_set(GIT_ERROR_SUBMODULE, "invalid submodule name");
+ return -1;
+ }
+
+ sm = git__calloc(1, sizeof(git_submodule));
+ GIT_ERROR_CHECK_ALLOC(sm);
+
+ sm->name = sm->path = git__strdup(name);
+ if (!sm->name) {
+ git__free(sm);
+ return -1;
+ }
+
+ GIT_REFCOUNT_INC(sm);
+ sm->ignore = sm->ignore_default = GIT_SUBMODULE_IGNORE_NONE;
+ sm->update = sm->update_default = GIT_SUBMODULE_UPDATE_CHECKOUT;
+ sm->fetch_recurse = sm->fetch_recurse_default = GIT_SUBMODULE_RECURSE_NO;
+ sm->repo = repo;
+ sm->branch = NULL;
+
+ *out = sm;
+ return 0;
+}
+
+static void submodule_release(git_submodule *sm)
+{
+ if (!sm)
+ return;
+
+ if (sm->repo) {
+ sm->repo = NULL;
+ }
+
+ if (sm->path != sm->name)
+ git__free(sm->path);
+ git__free(sm->name);
+ git__free(sm->url);
+ git__free(sm->branch);
+ git__memzero(sm, sizeof(*sm));
+ git__free(sm);
+}
+
+int git_submodule_dup(git_submodule **out, git_submodule *source)
+{
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(source);
+
+ GIT_REFCOUNT_INC(source);
+
+ *out = source;
+ return 0;
+}
+
+void git_submodule_free(git_submodule *sm)
+{
+ if (!sm)
+ return;
+ GIT_REFCOUNT_DEC(sm, submodule_release);
+}
+
+static int submodule_config_error(const char *property, const char *value)
+{
+ git_error_set(GIT_ERROR_INVALID,
+ "invalid value for submodule '%s' property: '%s'", property, value);
+ return -1;
+}
+
+int git_submodule_parse_ignore(git_submodule_ignore_t *out, const char *value)
+{
+ int val;
+
+ if (git_config_lookup_map_value(
+ &val, _sm_ignore_map, ARRAY_SIZE(_sm_ignore_map), value) < 0) {
+ *out = GIT_SUBMODULE_IGNORE_NONE;
+ return submodule_config_error("ignore", value);
+ }
+
+ *out = (git_submodule_ignore_t)val;
+ return 0;
+}
+
+int git_submodule_parse_update(git_submodule_update_t *out, const char *value)
+{
+ int val;
+
+ if (git_config_lookup_map_value(
+ &val, _sm_update_map, ARRAY_SIZE(_sm_update_map), value) < 0) {
+ *out = GIT_SUBMODULE_UPDATE_CHECKOUT;
+ return submodule_config_error("update", value);
+ }
+
+ *out = (git_submodule_update_t)val;
+ return 0;
+}
+
+static int submodule_parse_recurse(git_submodule_recurse_t *out, const char *value)
+{
+ int val;
+
+ if (git_config_lookup_map_value(
+ &val, _sm_recurse_map, ARRAY_SIZE(_sm_recurse_map), value) < 0) {
+ *out = GIT_SUBMODULE_RECURSE_YES;
+ return submodule_config_error("recurse", value);
+ }
+
+ *out = (git_submodule_recurse_t)val;
+ return 0;
+}
+
+static int get_value(const char **out, git_config *cfg, git_str *buf, const char *name, const char *field)
+{
+ int error;
+
+ git_str_clear(buf);
+
+ if ((error = git_str_printf(buf, "submodule.%s.%s", name, field)) < 0 ||
+ (error = git_config_get_string(out, cfg, buf->ptr)) < 0)
+ return error;
+
+ return error;
+}
+
+static bool looks_like_command_line_option(const char *s)
+{
+ if (s && s[0] == '-')
+ return true;
+
+ return false;
+}
+
+static int submodule_read_config(git_submodule *sm, git_config *cfg)
+{
+ git_str key = GIT_STR_INIT;
+ const char *value;
+ int error, in_config = 0;
+
+ /*
+ * TODO: Look up path in index and if it is present but not a GITLINK
+ * then this should be deleted (at least to match git's behavior)
+ */
+
+ if ((error = get_value(&value, cfg, &key, sm->name, "path")) == 0) {
+ in_config = 1;
+ /* We would warn here if we had that API */
+ if (!looks_like_command_line_option(value)) {
+ /*
+ * TODO: if case insensitive filesystem, then the following strcmp
+ * should be strcasecmp
+ */
+ if (strcmp(sm->name, value) != 0) {
+ if (sm->path != sm->name)
+ git__free(sm->path);
+ sm->path = git__strdup(value);
+ GIT_ERROR_CHECK_ALLOC(sm->path);
+ }
+
+ }
+ } else if (error != GIT_ENOTFOUND) {
+ goto cleanup;
+ }
+
+ if ((error = get_value(&value, cfg, &key, sm->name, "url")) == 0) {
+ /* We would warn here if we had that API */
+ if (!looks_like_command_line_option(value)) {
+ in_config = 1;
+ sm->url = git__strdup(value);
+ GIT_ERROR_CHECK_ALLOC(sm->url);
+ }
+ } else if (error != GIT_ENOTFOUND) {
+ goto cleanup;
+ }
+
+ if ((error = get_value(&value, cfg, &key, sm->name, "branch")) == 0) {
+ in_config = 1;
+ sm->branch = git__strdup(value);
+ GIT_ERROR_CHECK_ALLOC(sm->branch);
+ } else if (error != GIT_ENOTFOUND) {
+ goto cleanup;
+ }
+
+ if ((error = get_value(&value, cfg, &key, sm->name, "update")) == 0) {
+ in_config = 1;
+ if ((error = git_submodule_parse_update(&sm->update, value)) < 0)
+ goto cleanup;
+ sm->update_default = sm->update;
+ } else if (error != GIT_ENOTFOUND) {
+ goto cleanup;
+ }
+
+ if ((error = get_value(&value, cfg, &key, sm->name, "fetchRecurseSubmodules")) == 0) {
+ in_config = 1;
+ if ((error = submodule_parse_recurse(&sm->fetch_recurse, value)) < 0)
+ goto cleanup;
+ sm->fetch_recurse_default = sm->fetch_recurse;
+ } else if (error != GIT_ENOTFOUND) {
+ goto cleanup;
+ }
+
+ if ((error = get_value(&value, cfg, &key, sm->name, "ignore")) == 0) {
+ in_config = 1;
+ if ((error = git_submodule_parse_ignore(&sm->ignore, value)) < 0)
+ goto cleanup;
+ sm->ignore_default = sm->ignore;
+ } else if (error != GIT_ENOTFOUND) {
+ goto cleanup;
+ }
+
+ if (in_config)
+ sm->flags |= GIT_SUBMODULE_STATUS_IN_CONFIG;
+
+ error = 0;
+
+cleanup:
+ git_str_dispose(&key);
+ return error;
+}
+
+static int submodule_load_each(const git_config_entry *entry, void *payload)
+{
+ lfc_data *data = payload;
+ const char *namestart, *property;
+ git_strmap *map = data->map;
+ git_str name = GIT_STR_INIT;
+ git_submodule *sm;
+ int error, isvalid;
+
+ if (git__prefixcmp(entry->name, "submodule.") != 0)
+ return 0;
+
+ namestart = entry->name + strlen("submodule.");
+ property = strrchr(namestart, '.');
+
+ if (!property || (property == namestart))
+ return 0;
+
+ property++;
+
+ if ((error = git_str_set(&name, namestart, property - namestart -1)) < 0)
+ return error;
+
+ isvalid = git_submodule_name_is_valid(data->repo, name.ptr, 0);
+ if (isvalid <= 0) {
+ error = isvalid;
+ goto done;
+ }
+
+ /*
+ * Now that we have the submodule's name, we can use that to
+ * figure out whether it's in the map. If it's not, we create
+ * a new submodule, load the config and insert it. If it's
+ * already inserted, we've already loaded it, so we skip.
+ */
+ if (git_strmap_exists(map, name.ptr)) {
+ error = 0;
+ goto done;
+ }
+
+ if ((error = submodule_alloc(&sm, data->repo, name.ptr)) < 0)
+ goto done;
+
+ if ((error = submodule_read_config(sm, data->mods)) < 0) {
+ git_submodule_free(sm);
+ goto done;
+ }
+
+ if ((error = git_strmap_set(map, sm->name, sm)) < 0)
+ goto done;
+
+ error = 0;
+
+done:
+ git_str_dispose(&name);
+ return error;
+}
+
+static int submodule_load_from_wd_lite(git_submodule *sm)
+{
+ git_str path = GIT_STR_INIT;
+
+ if (git_repository_workdir_path(&path, sm->repo, sm->path) < 0)
+ return -1;
+
+ if (git_fs_path_isdir(path.ptr))
+ sm->flags |= GIT_SUBMODULE_STATUS__WD_SCANNED;
+
+ if (git_fs_path_contains(&path, DOT_GIT))
+ sm->flags |= GIT_SUBMODULE_STATUS_IN_WD;
+
+ git_str_dispose(&path);
+ return 0;
+}
+
+/**
+ * Requests a snapshot of $WORK_TREE/.gitmodules.
+ *
+ * Returns GIT_ENOTFOUND in case no .gitmodules file exist
+ */
+static int gitmodules_snapshot(git_config **snap, git_repository *repo)
+{
+ git_config *mods = NULL;
+ git_str path = GIT_STR_INIT;
+ int error;
+
+ if (git_repository_workdir(repo) == NULL)
+ return GIT_ENOTFOUND;
+
+ if ((error = git_repository_workdir_path(&path, repo, GIT_MODULES_FILE)) < 0)
+ return error;
+
+ if ((error = git_config_open_ondisk(&mods, path.ptr)) < 0)
+ goto cleanup;
+ git_str_dispose(&path);
+
+ if ((error = git_config_snapshot(snap, mods)) < 0)
+ goto cleanup;
+
+ error = 0;
+
+cleanup:
+ if (mods)
+ git_config_free(mods);
+ git_str_dispose(&path);
+
+ return error;
+}
+
+static git_config_backend *open_gitmodules(
+ git_repository *repo,
+ int okay_to_create)
+{
+ git_str path = GIT_STR_INIT;
+ git_config_backend *mods = NULL;
+
+ if (git_repository_workdir(repo) != NULL) {
+ if (git_repository_workdir_path(&path, repo, GIT_MODULES_FILE) != 0)
+ return NULL;
+
+ if (okay_to_create || git_fs_path_isfile(path.ptr)) {
+ /* git_config_backend_from_file should only fail if OOM */
+ if (git_config_backend_from_file(&mods, path.ptr) < 0)
+ mods = NULL;
+ /* open should only fail here if the file is malformed */
+ else if (git_config_backend_open(mods, GIT_CONFIG_LEVEL_LOCAL, repo) < 0) {
+ git_config_backend_free(mods);
+ mods = NULL;
+ }
+ }
+ }
+
+ git_str_dispose(&path);
+
+ return mods;
+}
+
+/* Lookup name of remote of the local tracking branch HEAD points to */
+static int lookup_head_remote_key(git_str *remote_name, git_repository *repo)
+{
+ int error;
+ git_reference *head = NULL;
+ git_str upstream_name = GIT_STR_INIT;
+
+ /* lookup and dereference HEAD */
+ if ((error = git_repository_head(&head, repo)) < 0)
+ return error;
+
+ /**
+ * If head does not refer to a branch, then return
+ * GIT_ENOTFOUND to indicate that we could not find
+ * a remote key for the local tracking branch HEAD points to.
+ **/
+ if (!git_reference_is_branch(head)) {
+ git_error_set(GIT_ERROR_INVALID,
+ "HEAD does not refer to a branch.");
+ error = GIT_ENOTFOUND;
+ goto done;
+ }
+
+ /* lookup remote tracking branch of HEAD */
+ if ((error = git_branch__upstream_name(
+ &upstream_name,
+ repo,
+ git_reference_name(head))) < 0)
+ goto done;
+
+ /* lookup remote of remote tracking branch */
+ if ((error = git_branch__remote_name(remote_name, repo, upstream_name.ptr)) < 0)
+ goto done;
+
+done:
+ git_str_dispose(&upstream_name);
+ git_reference_free(head);
+
+ return error;
+}
+
+/* Lookup the remote of the local tracking branch HEAD points to */
+static int lookup_head_remote(git_remote **remote, git_repository *repo)
+{
+ int error;
+ git_str remote_name = GIT_STR_INIT;
+
+ /* lookup remote of remote tracking branch name */
+ if (!(error = lookup_head_remote_key(&remote_name, repo)))
+ error = git_remote_lookup(remote, repo, remote_name.ptr);
+
+ git_str_dispose(&remote_name);
+
+ return error;
+}
+
+/* Lookup remote, either from HEAD or fall back on origin */
+static int lookup_default_remote(git_remote **remote, git_repository *repo)
+{
+ int error = lookup_head_remote(remote, repo);
+
+ /* if that failed, use 'origin' instead */
+ if (error == GIT_ENOTFOUND || error == GIT_EUNBORNBRANCH)
+ error = git_remote_lookup(remote, repo, "origin");
+
+ if (error == GIT_ENOTFOUND)
+ git_error_set(
+ GIT_ERROR_SUBMODULE,
+ "cannot get default remote for submodule - no local tracking "
+ "branch for HEAD and origin does not exist");
+
+ return error;
+}
+
+static int get_url_base(git_str *url, git_repository *repo)
+{
+ int error;
+ git_worktree *wt = NULL;
+ git_remote *remote = NULL;
+
+ if ((error = lookup_default_remote(&remote, repo)) == 0) {
+ error = git_str_sets(url, git_remote_url(remote));
+ goto out;
+ } else if (error != GIT_ENOTFOUND)
+ goto out;
+ else
+ git_error_clear();
+
+ /* if repository does not have a default remote, use workdir instead */
+ if (git_repository_is_worktree(repo)) {
+ if ((error = git_worktree_open_from_repository(&wt, repo)) < 0)
+ goto out;
+ error = git_str_sets(url, wt->parent_path);
+ } else {
+ error = git_str_sets(url, git_repository_workdir(repo));
+ }
+
+out:
+ git_remote_free(remote);
+ git_worktree_free(wt);
+
+ return error;
+}
+
+static void submodule_get_index_status(unsigned int *status, git_submodule *sm)
+{
+ const git_oid *head_oid = git_submodule_head_id(sm);
+ const git_oid *index_oid = git_submodule_index_id(sm);
+
+ *status = *status & ~GIT_SUBMODULE_STATUS__INDEX_FLAGS;
+
+ if (!head_oid) {
+ if (index_oid)
+ *status |= GIT_SUBMODULE_STATUS_INDEX_ADDED;
+ }
+ else if (!index_oid)
+ *status |= GIT_SUBMODULE_STATUS_INDEX_DELETED;
+ else if (!git_oid_equal(head_oid, index_oid))
+ *status |= GIT_SUBMODULE_STATUS_INDEX_MODIFIED;
+}
+
+
+static void submodule_get_wd_status(
+ unsigned int *status,
+ git_submodule *sm,
+ git_repository *sm_repo,
+ git_submodule_ignore_t ign)
+{
+ const git_oid *index_oid = git_submodule_index_id(sm);
+ const git_oid *wd_oid =
+ (sm->flags & GIT_SUBMODULE_STATUS__WD_OID_VALID) ? &sm->wd_oid : NULL;
+ git_tree *sm_head = NULL;
+ git_index *index = NULL;
+ git_diff_options opt = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff;
+
+ *status = *status & ~GIT_SUBMODULE_STATUS__WD_FLAGS;
+
+ if (!index_oid) {
+ if (wd_oid)
+ *status |= GIT_SUBMODULE_STATUS_WD_ADDED;
+ }
+ else if (!wd_oid) {
+ if ((sm->flags & GIT_SUBMODULE_STATUS__WD_SCANNED) != 0 &&
+ (sm->flags & GIT_SUBMODULE_STATUS_IN_WD) == 0)
+ *status |= GIT_SUBMODULE_STATUS_WD_UNINITIALIZED;
+ else
+ *status |= GIT_SUBMODULE_STATUS_WD_DELETED;
+ }
+ else if (!git_oid_equal(index_oid, wd_oid))
+ *status |= GIT_SUBMODULE_STATUS_WD_MODIFIED;
+
+ /* if we have no repo, then we're done */
+ if (!sm_repo)
+ return;
+
+ /* the diffs below could be optimized with an early termination
+ * option to the git_diff functions, but for now this is sufficient
+ * (and certainly no worse that what core git does).
+ */
+
+ if (ign == GIT_SUBMODULE_IGNORE_NONE)
+ opt.flags |= GIT_DIFF_INCLUDE_UNTRACKED;
+
+ (void)git_repository_index__weakptr(&index, sm_repo);
+
+ /* if we don't have an unborn head, check diff with index */
+ if (git_repository_head_tree(&sm_head, sm_repo) < 0)
+ git_error_clear();
+ else {
+ /* perform head to index diff on submodule */
+ if (git_diff_tree_to_index(&diff, sm_repo, sm_head, index, &opt) < 0)
+ git_error_clear();
+ else {
+ if (git_diff_num_deltas(diff) > 0)
+ *status |= GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED;
+ git_diff_free(diff);
+ diff = NULL;
+ }
+
+ git_tree_free(sm_head);
+ }
+
+ /* perform index-to-workdir diff on submodule */
+ if (git_diff_index_to_workdir(&diff, sm_repo, index, &opt) < 0)
+ git_error_clear();
+ else {
+ size_t untracked =
+ git_diff_num_deltas_of_type(diff, GIT_DELTA_UNTRACKED);
+
+ if (untracked > 0)
+ *status |= GIT_SUBMODULE_STATUS_WD_UNTRACKED;
+
+ if (git_diff_num_deltas(diff) != untracked)
+ *status |= GIT_SUBMODULE_STATUS_WD_WD_MODIFIED;
+
+ git_diff_free(diff);
+ diff = NULL;
+ }
+}
diff --git a/src/libgit2/submodule.h b/src/libgit2/submodule.h
new file mode 100644
index 0000000..40b7b70
--- /dev/null
+++ b/src/libgit2/submodule.h
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_submodule_h__
+#define INCLUDE_submodule_h__
+
+#include "common.h"
+
+#include "git2/submodule.h"
+#include "git2/repository.h"
+#include "futils.h"
+
+/* Notes:
+ *
+ * Submodule information can be in four places: the index, the config files
+ * (both .git/config and .gitmodules), the HEAD tree, and the working
+ * directory.
+ *
+ * In the index:
+ * - submodule is found by path
+ * - may be missing, present, or of the wrong type
+ * - will have an oid if present
+ *
+ * In the HEAD tree:
+ * - submodule is found by path
+ * - may be missing, present, or of the wrong type
+ * - will have an oid if present
+ *
+ * In the config files:
+ * - submodule is found by submodule "name" which is usually the path
+ * - may be missing or present
+ * - will have a name, path, url, and other properties
+ *
+ * In the working directory:
+ * - submodule is found by path
+ * - may be missing, an empty directory, a checked out directory,
+ * or of the wrong type
+ * - if checked out, will have a HEAD oid
+ * - if checked out, will have git history that can be used to compare oids
+ * - if checked out, may have modified files and/or untracked files
+ */
+
+/**
+ * Description of submodule
+ *
+ * This record describes a submodule found in a repository. There should be
+ * an entry for every submodule found in the HEAD and index, and for every
+ * submodule described in .gitmodules. The fields are as follows:
+ *
+ * - `rc` tracks the refcount of how many hash table entries in the
+ * git_submodule_cache there are for this submodule. It only comes into
+ * play if the name and path of the submodule differ.
+ *
+ * - `name` is the name of the submodule from .gitmodules.
+ * - `path` is the path to the submodule from the repo root. It is almost
+ * always the same as `name`.
+ * - `url` is the url for the submodule.
+ * - `update` is a git_submodule_update_t value - see gitmodules(5) update.
+ * - `update_default` is the update value from the config
+ * - `ignore` is a git_submodule_ignore_t value - see gitmodules(5) ignore.
+ * - `ignore_default` is the ignore value from the config
+ * - `fetch_recurse` is a git_submodule_recurse_t value - see gitmodules(5)
+ * fetchRecurseSubmodules.
+ * - `fetch_recurse_default` is the recurse value from the config
+ *
+ * - `repo` is the parent repository that contains this submodule.
+ * - `flags` after for internal use, tracking where this submodule has been
+ * found (head, index, config, workdir) and known status info, etc.
+ * - `head_oid` is the oid for the submodule path in the repo HEAD.
+ * - `index_oid` is the oid for the submodule recorded in the index.
+ * - `wd_oid` is the oid for the HEAD of the checked out submodule.
+ *
+ * If the submodule has been added to .gitmodules but not yet git added,
+ * then the `index_oid` will be zero but still marked valid. If the
+ * submodule has been deleted, but the delete has not been committed yet,
+ * then the `index_oid` will be set, but the `url` will be NULL.
+ */
+struct git_submodule {
+ git_refcount rc;
+
+ /* information from config */
+ char *name;
+ char *path; /* important: may just point to "name" string */
+ char *url;
+ char *branch;
+ git_submodule_update_t update;
+ git_submodule_update_t update_default;
+ git_submodule_ignore_t ignore;
+ git_submodule_ignore_t ignore_default;
+ git_submodule_recurse_t fetch_recurse;
+ git_submodule_recurse_t fetch_recurse_default;
+
+ /* internal information */
+ git_repository *repo;
+ uint32_t flags;
+ git_oid head_oid;
+ git_oid index_oid;
+ git_oid wd_oid;
+};
+
+/* Additional flags on top of public GIT_SUBMODULE_STATUS values */
+enum {
+ GIT_SUBMODULE_STATUS__WD_SCANNED = (1u << 20),
+ GIT_SUBMODULE_STATUS__HEAD_OID_VALID = (1u << 21),
+ GIT_SUBMODULE_STATUS__INDEX_OID_VALID = (1u << 22),
+ GIT_SUBMODULE_STATUS__WD_OID_VALID = (1u << 23),
+ GIT_SUBMODULE_STATUS__HEAD_NOT_SUBMODULE = (1u << 24),
+ GIT_SUBMODULE_STATUS__INDEX_NOT_SUBMODULE = (1u << 25),
+ GIT_SUBMODULE_STATUS__WD_NOT_SUBMODULE = (1u << 26),
+ GIT_SUBMODULE_STATUS__INDEX_MULTIPLE_ENTRIES = (1u << 27)
+};
+
+#define GIT_SUBMODULE_STATUS__CLEAR_INTERNAL(S) \
+ ((S) & ~(0xFFFFFFFFu << 20))
+
+/* Initialize an external submodule cache for the provided repo. */
+extern int git_submodule_cache_init(git_strmap **out, git_repository *repo);
+
+/* Release the resources of the submodule cache. */
+extern int git_submodule_cache_free(git_strmap *cache);
+
+/* Submodule lookup with an explicit cache */
+extern int git_submodule__lookup_with_cache(
+ git_submodule **out, git_repository *repo, const char *path, git_strmap *cache);
+
+/* Internal status fn returns status and optionally the various OIDs */
+extern int git_submodule__status(
+ unsigned int *out_status,
+ git_oid *out_head_id,
+ git_oid *out_index_id,
+ git_oid *out_wd_id,
+ git_submodule *sm,
+ git_submodule_ignore_t ign);
+
+/* Open submodule repository as bare repo for quick HEAD check, etc. */
+extern int git_submodule_open_bare(
+ git_repository **repo,
+ git_submodule *submodule);
+
+extern int git_submodule_parse_ignore(
+ git_submodule_ignore_t *out, const char *value);
+extern int git_submodule_parse_update(
+ git_submodule_update_t *out, const char *value);
+
+extern int git_submodule__map(
+ git_repository *repo,
+ git_strmap *map);
+
+/**
+ * Check whether a submodule's name is valid.
+ *
+ * Check the path against the path validity rules, either the filesystem
+ * defaults (like checkout does) or whichever you want to compare against.
+ *
+ * @param repo the repository which contains the submodule
+ * @param name the name to check
+ * @param flags the `GIT_PATH` flags to use for the check (0 to use filesystem defaults)
+ */
+extern int git_submodule_name_is_valid(git_repository *repo, const char *name, int flags);
+
+#endif
diff --git a/src/libgit2/sysdir.c b/src/libgit2/sysdir.c
new file mode 100644
index 0000000..7838a67
--- /dev/null
+++ b/src/libgit2/sysdir.c
@@ -0,0 +1,650 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "sysdir.h"
+
+#include "runtime.h"
+#include "str.h"
+#include "fs_path.h"
+#include <ctype.h>
+#if GIT_WIN32
+# include "fs_path.h"
+# include "win32/path_w32.h"
+# include "win32/utf-conv.h"
+#else
+# include <unistd.h>
+# include <pwd.h>
+#endif
+
+#ifdef GIT_WIN32
+# define REG_GITFORWINDOWS_KEY L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1"
+# define REG_GITFORWINDOWS_KEY_WOW64 L"SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\Git_is1"
+
+static int expand_win32_path(git_win32_path dest, const wchar_t *src)
+{
+ DWORD len = ExpandEnvironmentStringsW(src, dest, GIT_WIN_PATH_UTF16);
+
+ if (!len || len > GIT_WIN_PATH_UTF16)
+ return -1;
+
+ return 0;
+}
+
+static int win32_path_to_utf8(git_str *dest, const wchar_t *src)
+{
+ git_win32_utf8_path utf8_path;
+
+ if (git_win32_path_to_utf8(utf8_path, src) < 0) {
+ git_error_set(GIT_ERROR_OS, "unable to convert path to UTF-8");
+ return -1;
+ }
+
+ /* Convert backslashes to forward slashes */
+ git_fs_path_mkposix(utf8_path);
+
+ return git_str_sets(dest, utf8_path);
+}
+
+static git_win32_path mock_registry;
+static bool mock_registry_set;
+
+extern int git_win32__set_registry_system_dir(const wchar_t *mock_sysdir)
+{
+ if (!mock_sysdir) {
+ mock_registry[0] = L'\0';
+ mock_registry_set = false;
+ } else {
+ size_t len = wcslen(mock_sysdir);
+
+ if (len > GIT_WIN_PATH_MAX) {
+ git_error_set(GIT_ERROR_INVALID, "mock path too long");
+ return -1;
+ }
+
+ wcscpy(mock_registry, mock_sysdir);
+ mock_registry_set = true;
+ }
+
+ return 0;
+}
+
+static int lookup_registry_key(
+ git_win32_path out,
+ const HKEY hive,
+ const wchar_t* key,
+ const wchar_t *value)
+{
+ HKEY hkey;
+ DWORD type, size;
+ int error = GIT_ENOTFOUND;
+
+ /*
+ * Registry data may not be NUL terminated, provide room to do
+ * it ourselves.
+ */
+ size = (DWORD)((sizeof(git_win32_path) - 1) * sizeof(wchar_t));
+
+ if (RegOpenKeyExW(hive, key, 0, KEY_READ, &hkey) != 0)
+ return GIT_ENOTFOUND;
+
+ if (RegQueryValueExW(hkey, value, NULL, &type, (LPBYTE)out, &size) == 0 &&
+ type == REG_SZ &&
+ size > 0 &&
+ size < sizeof(git_win32_path)) {
+ size_t wsize = size / sizeof(wchar_t);
+ size_t len = wsize - 1;
+
+ if (out[wsize - 1] != L'\0') {
+ len = wsize;
+ out[wsize] = L'\0';
+ }
+
+ if (out[len - 1] == L'\\')
+ out[len - 1] = L'\0';
+
+ if (_waccess(out, F_OK) == 0)
+ error = 0;
+ }
+
+ RegCloseKey(hkey);
+ return error;
+}
+
+static int find_sysdir_in_registry(git_win32_path out)
+{
+ if (mock_registry_set) {
+ if (mock_registry[0] == L'\0')
+ return GIT_ENOTFOUND;
+
+ wcscpy(out, mock_registry);
+ return 0;
+ }
+
+ if (lookup_registry_key(out, HKEY_CURRENT_USER, REG_GITFORWINDOWS_KEY, L"InstallLocation") == 0 ||
+ lookup_registry_key(out, HKEY_CURRENT_USER, REG_GITFORWINDOWS_KEY_WOW64, L"InstallLocation") == 0 ||
+ lookup_registry_key(out, HKEY_LOCAL_MACHINE, REG_GITFORWINDOWS_KEY, L"InstallLocation") == 0 ||
+ lookup_registry_key(out, HKEY_LOCAL_MACHINE, REG_GITFORWINDOWS_KEY_WOW64, L"InstallLocation") == 0)
+ return 0;
+
+ return GIT_ENOTFOUND;
+}
+
+static int find_sysdir_in_path(git_win32_path out)
+{
+ size_t out_len;
+
+ if (git_win32_path_find_executable(out, L"git.exe") < 0 &&
+ git_win32_path_find_executable(out, L"git.cmd") < 0)
+ return GIT_ENOTFOUND;
+
+ out_len = wcslen(out);
+
+ /* Trim the file name */
+ if (out_len <= CONST_STRLEN(L"git.exe"))
+ return GIT_ENOTFOUND;
+
+ out_len -= CONST_STRLEN(L"git.exe");
+
+ if (out_len && out[out_len - 1] == L'\\')
+ out_len--;
+
+ /*
+ * Git for Windows usually places the command in a 'bin' or
+ * 'cmd' directory, trim that.
+ */
+ if (out_len >= CONST_STRLEN(L"\\bin") &&
+ wcsncmp(&out[out_len - CONST_STRLEN(L"\\bin")], L"\\bin", CONST_STRLEN(L"\\bin")) == 0)
+ out_len -= CONST_STRLEN(L"\\bin");
+ else if (out_len >= CONST_STRLEN(L"\\cmd") &&
+ wcsncmp(&out[out_len - CONST_STRLEN(L"\\cmd")], L"\\cmd", CONST_STRLEN(L"\\cmd")) == 0)
+ out_len -= CONST_STRLEN(L"\\cmd");
+
+ if (!out_len)
+ return GIT_ENOTFOUND;
+
+ out[out_len] = L'\0';
+ return 0;
+}
+
+static int find_win32_dirs(
+ git_str *out,
+ const wchar_t* tmpl[])
+{
+ git_win32_path path16;
+ git_str buf = GIT_STR_INIT;
+
+ git_str_clear(out);
+
+ for (; *tmpl != NULL; tmpl++) {
+ if (!expand_win32_path(path16, *tmpl) &&
+ path16[0] != L'%' &&
+ !_waccess(path16, F_OK)) {
+ win32_path_to_utf8(&buf, path16);
+
+ if (buf.size)
+ git_str_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr);
+ }
+ }
+
+ git_str_dispose(&buf);
+
+ return (git_str_oom(out) ? -1 : 0);
+}
+
+static int append_subdir(git_str *out, git_str *path, const char *subdir)
+{
+ static const char* architecture_roots[] = {
+ "",
+ "mingw64",
+ "mingw32",
+ NULL
+ };
+ const char **root;
+ size_t orig_path_len = path->size;
+
+ for (root = architecture_roots; *root; root++) {
+ if ((*root[0] && git_str_joinpath(path, path->ptr, *root) < 0) ||
+ git_str_joinpath(path, path->ptr, subdir) < 0)
+ return -1;
+
+ if (git_fs_path_exists(path->ptr) &&
+ git_str_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, path->ptr) < 0)
+ return -1;
+
+ git_str_truncate(path, orig_path_len);
+ }
+
+ return 0;
+}
+
+int git_win32__find_system_dirs(git_str *out, const char *subdir)
+{
+ git_win32_path pathdir, regdir;
+ git_str path8 = GIT_STR_INIT;
+ bool has_pathdir, has_regdir;
+ int error;
+
+ has_pathdir = (find_sysdir_in_path(pathdir) == 0);
+ has_regdir = (find_sysdir_in_registry(regdir) == 0);
+
+ if (!has_pathdir && !has_regdir)
+ return 0;
+
+ /*
+ * Usually the git in the path is the same git in the registry,
+ * in this case there's no need to duplicate the paths.
+ */
+ if (has_pathdir && has_regdir && wcscmp(pathdir, regdir) == 0)
+ has_regdir = false;
+
+ if (has_pathdir) {
+ if ((error = win32_path_to_utf8(&path8, pathdir)) < 0 ||
+ (error = append_subdir(out, &path8, subdir)) < 0)
+ goto done;
+ }
+
+ if (has_regdir) {
+ if ((error = win32_path_to_utf8(&path8, regdir)) < 0 ||
+ (error = append_subdir(out, &path8, subdir)) < 0)
+ goto done;
+ }
+
+done:
+ git_str_dispose(&path8);
+ return error;
+}
+#endif /* WIN32 */
+
+static int git_sysdir_guess_programdata_dirs(git_str *out)
+{
+#ifdef GIT_WIN32
+ static const wchar_t *programdata_tmpls[2] = {
+ L"%PROGRAMDATA%\\Git",
+ NULL,
+ };
+
+ return find_win32_dirs(out, programdata_tmpls);
+#else
+ git_str_clear(out);
+ return 0;
+#endif
+}
+
+static int git_sysdir_guess_system_dirs(git_str *out)
+{
+#ifdef GIT_WIN32
+ return git_win32__find_system_dirs(out, "etc");
+#else
+ return git_str_sets(out, "/etc");
+#endif
+}
+
+#ifndef GIT_WIN32
+static int get_passwd_home(git_str *out, uid_t uid)
+{
+ struct passwd pwd, *pwdptr;
+ char *buf = NULL;
+ long buflen;
+ int error;
+
+ GIT_ASSERT_ARG(out);
+
+ if ((buflen = sysconf(_SC_GETPW_R_SIZE_MAX)) == -1)
+ buflen = 1024;
+
+ do {
+ buf = git__realloc(buf, buflen);
+ error = getpwuid_r(uid, &pwd, buf, buflen, &pwdptr);
+ buflen *= 2;
+ } while (error == ERANGE && buflen <= 8192);
+
+ if (error) {
+ git_error_set(GIT_ERROR_OS, "failed to get passwd entry");
+ goto out;
+ }
+
+ if (!pwdptr) {
+ git_error_set(GIT_ERROR_OS, "no passwd entry found for user");
+ goto out;
+ }
+
+ if ((error = git_str_puts(out, pwdptr->pw_dir)) < 0)
+ goto out;
+
+out:
+ git__free(buf);
+ return error;
+}
+#endif
+
+static int git_sysdir_guess_home_dirs(git_str *out)
+{
+#ifdef GIT_WIN32
+ static const wchar_t *global_tmpls[4] = {
+ L"%HOME%\\",
+ L"%HOMEDRIVE%%HOMEPATH%\\",
+ L"%USERPROFILE%\\",
+ NULL,
+ };
+
+ return find_win32_dirs(out, global_tmpls);
+#else
+ int error;
+ uid_t uid, euid;
+ const char *sandbox_id;
+
+ uid = getuid();
+ euid = geteuid();
+
+ /**
+ * If APP_SANDBOX_CONTAINER_ID is set, we are running in a
+ * sandboxed environment on macOS.
+ */
+ sandbox_id = getenv("APP_SANDBOX_CONTAINER_ID");
+
+ /*
+ * In case we are running setuid, use the configuration
+ * of the effective user.
+ *
+ * If we are running in a sandboxed environment on macOS,
+ * we have to get the HOME dir from the password entry file.
+ */
+ if (!sandbox_id && uid == euid)
+ error = git__getenv(out, "HOME");
+ else
+ error = get_passwd_home(out, euid);
+
+ if (error == GIT_ENOTFOUND) {
+ git_error_clear();
+ error = 0;
+ }
+
+ return error;
+#endif
+}
+
+static int git_sysdir_guess_global_dirs(git_str *out)
+{
+ return git_sysdir_guess_home_dirs(out);
+}
+
+static int git_sysdir_guess_xdg_dirs(git_str *out)
+{
+#ifdef GIT_WIN32
+ static const wchar_t *global_tmpls[7] = {
+ L"%XDG_CONFIG_HOME%\\git",
+ L"%APPDATA%\\git",
+ L"%LOCALAPPDATA%\\git",
+ L"%HOME%\\.config\\git",
+ L"%HOMEDRIVE%%HOMEPATH%\\.config\\git",
+ L"%USERPROFILE%\\.config\\git",
+ NULL,
+ };
+
+ return find_win32_dirs(out, global_tmpls);
+#else
+ git_str env = GIT_STR_INIT;
+ int error;
+ uid_t uid, euid;
+
+ uid = getuid();
+ euid = geteuid();
+
+ /*
+ * In case we are running setuid, only look up passwd
+ * directory of the effective user.
+ */
+ if (uid == euid) {
+ if ((error = git__getenv(&env, "XDG_CONFIG_HOME")) == 0)
+ error = git_str_joinpath(out, env.ptr, "git");
+
+ if (error == GIT_ENOTFOUND && (error = git__getenv(&env, "HOME")) == 0)
+ error = git_str_joinpath(out, env.ptr, ".config/git");
+ } else {
+ if ((error = get_passwd_home(&env, euid)) == 0)
+ error = git_str_joinpath(out, env.ptr, ".config/git");
+ }
+
+ if (error == GIT_ENOTFOUND) {
+ git_error_clear();
+ error = 0;
+ }
+
+ git_str_dispose(&env);
+ return error;
+#endif
+}
+
+static int git_sysdir_guess_template_dirs(git_str *out)
+{
+#ifdef GIT_WIN32
+ return git_win32__find_system_dirs(out, "share/git-core/templates");
+#else
+ return git_str_sets(out, "/usr/share/git-core/templates");
+#endif
+}
+
+struct git_sysdir__dir {
+ git_str buf;
+ int (*guess)(git_str *out);
+};
+
+static struct git_sysdir__dir git_sysdir__dirs[] = {
+ { GIT_STR_INIT, git_sysdir_guess_system_dirs },
+ { GIT_STR_INIT, git_sysdir_guess_global_dirs },
+ { GIT_STR_INIT, git_sysdir_guess_xdg_dirs },
+ { GIT_STR_INIT, git_sysdir_guess_programdata_dirs },
+ { GIT_STR_INIT, git_sysdir_guess_template_dirs },
+ { GIT_STR_INIT, git_sysdir_guess_home_dirs }
+};
+
+static void git_sysdir_global_shutdown(void)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(git_sysdir__dirs); ++i)
+ git_str_dispose(&git_sysdir__dirs[i].buf);
+}
+
+int git_sysdir_global_init(void)
+{
+ size_t i;
+ int error = 0;
+
+ for (i = 0; !error && i < ARRAY_SIZE(git_sysdir__dirs); i++)
+ error = git_sysdir__dirs[i].guess(&git_sysdir__dirs[i].buf);
+
+ if (error)
+ return error;
+
+ return git_runtime_shutdown_register(git_sysdir_global_shutdown);
+}
+
+int git_sysdir_reset(void)
+{
+ size_t i;
+ int error = 0;
+
+ for (i = 0; !error && i < ARRAY_SIZE(git_sysdir__dirs); ++i) {
+ git_str_dispose(&git_sysdir__dirs[i].buf);
+ error = git_sysdir__dirs[i].guess(&git_sysdir__dirs[i].buf);
+ }
+
+ return error;
+}
+
+static int git_sysdir_check_selector(git_sysdir_t which)
+{
+ if (which < ARRAY_SIZE(git_sysdir__dirs))
+ return 0;
+
+ git_error_set(GIT_ERROR_INVALID, "config directory selector out of range");
+ return -1;
+}
+
+
+int git_sysdir_get(const git_str **out, git_sysdir_t which)
+{
+ GIT_ASSERT_ARG(out);
+
+ *out = NULL;
+
+ GIT_ERROR_CHECK_ERROR(git_sysdir_check_selector(which));
+
+ *out = &git_sysdir__dirs[which].buf;
+ return 0;
+}
+
+#define PATH_MAGIC "$PATH"
+
+int git_sysdir_set(git_sysdir_t which, const char *search_path)
+{
+ const char *expand_path = NULL;
+ git_str merge = GIT_STR_INIT;
+
+ GIT_ERROR_CHECK_ERROR(git_sysdir_check_selector(which));
+
+ if (search_path != NULL)
+ expand_path = strstr(search_path, PATH_MAGIC);
+
+ /* reset the default if this path has been cleared */
+ if (!search_path)
+ git_sysdir__dirs[which].guess(&git_sysdir__dirs[which].buf);
+
+ /* if $PATH is not referenced, then just set the path */
+ if (!expand_path) {
+ if (search_path)
+ git_str_sets(&git_sysdir__dirs[which].buf, search_path);
+
+ goto done;
+ }
+
+ /* otherwise set to join(before $PATH, old value, after $PATH) */
+ if (expand_path > search_path)
+ git_str_set(&merge, search_path, expand_path - search_path);
+
+ if (git_str_len(&git_sysdir__dirs[which].buf))
+ git_str_join(&merge, GIT_PATH_LIST_SEPARATOR,
+ merge.ptr, git_sysdir__dirs[which].buf.ptr);
+
+ expand_path += strlen(PATH_MAGIC);
+ if (*expand_path)
+ git_str_join(&merge, GIT_PATH_LIST_SEPARATOR, merge.ptr, expand_path);
+
+ git_str_swap(&git_sysdir__dirs[which].buf, &merge);
+ git_str_dispose(&merge);
+
+done:
+ if (git_str_oom(&git_sysdir__dirs[which].buf))
+ return -1;
+
+ return 0;
+}
+
+static int git_sysdir_find_in_dirlist(
+ git_str *path,
+ const char *name,
+ git_sysdir_t which,
+ const char *label)
+{
+ size_t len;
+ const char *scan, *next = NULL;
+ const git_str *syspath;
+
+ GIT_ERROR_CHECK_ERROR(git_sysdir_get(&syspath, which));
+ if (!syspath || !git_str_len(syspath))
+ goto done;
+
+ for (scan = git_str_cstr(syspath); scan; scan = next) {
+ /* find unescaped separator or end of string */
+ for (next = scan; *next; ++next) {
+ if (*next == GIT_PATH_LIST_SEPARATOR &&
+ (next <= scan || next[-1] != '\\'))
+ break;
+ }
+
+ len = (size_t)(next - scan);
+ next = (*next ? next + 1 : NULL);
+ if (!len)
+ continue;
+
+ GIT_ERROR_CHECK_ERROR(git_str_set(path, scan, len));
+ if (name)
+ GIT_ERROR_CHECK_ERROR(git_str_joinpath(path, path->ptr, name));
+
+ if (git_fs_path_exists(path->ptr))
+ return 0;
+ }
+
+done:
+ if (name)
+ git_error_set(GIT_ERROR_OS, "the %s file '%s' doesn't exist", label, name);
+ else
+ git_error_set(GIT_ERROR_OS, "the %s directory doesn't exist", label);
+ git_str_dispose(path);
+ return GIT_ENOTFOUND;
+}
+
+int git_sysdir_find_system_file(git_str *path, const char *filename)
+{
+ return git_sysdir_find_in_dirlist(
+ path, filename, GIT_SYSDIR_SYSTEM, "system");
+}
+
+int git_sysdir_find_global_file(git_str *path, const char *filename)
+{
+ return git_sysdir_find_in_dirlist(
+ path, filename, GIT_SYSDIR_GLOBAL, "global");
+}
+
+int git_sysdir_find_xdg_file(git_str *path, const char *filename)
+{
+ return git_sysdir_find_in_dirlist(
+ path, filename, GIT_SYSDIR_XDG, "global/xdg");
+}
+
+int git_sysdir_find_programdata_file(git_str *path, const char *filename)
+{
+ return git_sysdir_find_in_dirlist(
+ path, filename, GIT_SYSDIR_PROGRAMDATA, "ProgramData");
+}
+
+int git_sysdir_find_template_dir(git_str *path)
+{
+ return git_sysdir_find_in_dirlist(
+ path, NULL, GIT_SYSDIR_TEMPLATE, "template");
+}
+
+int git_sysdir_find_homedir(git_str *path)
+{
+ return git_sysdir_find_in_dirlist(
+ path, NULL, GIT_SYSDIR_HOME, "home directory");
+}
+
+int git_sysdir_expand_global_file(git_str *path, const char *filename)
+{
+ int error;
+
+ if ((error = git_sysdir_find_global_file(path, NULL)) == 0) {
+ if (filename)
+ error = git_str_joinpath(path, path->ptr, filename);
+ }
+
+ return error;
+}
+
+int git_sysdir_expand_homedir_file(git_str *path, const char *filename)
+{
+ int error;
+
+ if ((error = git_sysdir_find_homedir(path)) == 0) {
+ if (filename)
+ error = git_str_joinpath(path, path->ptr, filename);
+ }
+
+ return error;
+}
diff --git a/src/libgit2/sysdir.h b/src/libgit2/sysdir.h
new file mode 100644
index 0000000..03f59e1
--- /dev/null
+++ b/src/libgit2/sysdir.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_sysdir_h__
+#define INCLUDE_sysdir_h__
+
+#include "common.h"
+
+#include "posix.h"
+#include "str.h"
+
+/**
+ * Find a "global" file (i.e. one in a user's home directory).
+ *
+ * @param path buffer to write the full path into
+ * @param filename name of file to find in the home directory
+ * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error
+ */
+extern int git_sysdir_find_global_file(git_str *path, const char *filename);
+
+/**
+ * Find an "XDG" file (i.e. one in user's XDG config path).
+ *
+ * @param path buffer to write the full path into
+ * @param filename name of file to find in the home directory
+ * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error
+ */
+extern int git_sysdir_find_xdg_file(git_str *path, const char *filename);
+
+/**
+ * Find a "system" file (i.e. one shared for all users of the system).
+ *
+ * @param path buffer to write the full path into
+ * @param filename name of file to find in the home directory
+ * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error
+ */
+extern int git_sysdir_find_system_file(git_str *path, const char *filename);
+
+/**
+ * Find a "ProgramData" file (i.e. one in %PROGRAMDATA%)
+ *
+ * @param path buffer to write the full path into
+ * @param filename name of file to find in the ProgramData directory
+ * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error
+ */
+extern int git_sysdir_find_programdata_file(git_str *path, const char *filename);
+
+/**
+ * Find template directory.
+ *
+ * @param path buffer to write the full path into
+ * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error
+ */
+extern int git_sysdir_find_template_dir(git_str *path);
+
+/**
+ * Find the home directory. On Windows, this will look at the `HOME`,
+ * `HOMEPATH`, and `USERPROFILE` environment variables (in that order)
+ * and return the first path that is set and exists. On other systems,
+ * this will simply return the contents of the `HOME` environment variable.
+ *
+ * @param path buffer to write the full path into
+ * @return 0 if found, GIT_ENOTFOUND if not found, or -1 on other OS error
+ */
+extern int git_sysdir_find_homedir(git_str *path);
+
+/**
+ * Expand the name of a "global" file -- by default inside the user's
+ * home directory, but can be overridden by the user configuration.
+ * Unlike `find_global_file` (above), this makes no attempt to check
+ * for the existence of the file, and is useful if you want the full
+ * path regardless of existence.
+ *
+ * @param path buffer to write the full path into
+ * @param filename name of file in the home directory
+ * @return 0 on success or -1 on error
+ */
+extern int git_sysdir_expand_global_file(git_str *path, const char *filename);
+
+/**
+ * Expand the name of a file in the user's home directory. This
+ * function makes no attempt to check for the existence of the file,
+ * and is useful if you want the full path regardless of existence.
+ *
+ * @param path buffer to write the full path into
+ * @param filename name of file in the home directory
+ * @return 0 on success or -1 on error
+ */
+extern int git_sysdir_expand_homedir_file(git_str *path, const char *filename);
+
+typedef enum {
+ GIT_SYSDIR_SYSTEM = 0,
+ GIT_SYSDIR_GLOBAL = 1,
+ GIT_SYSDIR_XDG = 2,
+ GIT_SYSDIR_PROGRAMDATA = 3,
+ GIT_SYSDIR_TEMPLATE = 4,
+ GIT_SYSDIR_HOME = 5,
+ GIT_SYSDIR__MAX = 6
+} git_sysdir_t;
+
+/**
+ * Configures global data for configuration file search paths.
+ *
+ * @return 0 on success, <0 on failure
+ */
+extern int git_sysdir_global_init(void);
+
+/**
+ * Get the search path for global/system/xdg files
+ *
+ * @param out pointer to git_str containing search path
+ * @param which which list of paths to return
+ * @return 0 on success, <0 on failure
+ */
+extern int git_sysdir_get(const git_str **out, git_sysdir_t which);
+
+/**
+ * Set search paths for global/system/xdg files
+ *
+ * The first occurrence of the magic string "$PATH" in the new value will
+ * be replaced with the old value of the search path.
+ *
+ * @param which Which search path to modify
+ * @param paths New search path (separated by GIT_PATH_LIST_SEPARATOR)
+ * @return 0 on success, <0 on failure (allocation error)
+ */
+extern int git_sysdir_set(git_sysdir_t which, const char *paths);
+
+/**
+ * Reset search paths for global/system/xdg files.
+ */
+extern int git_sysdir_reset(void);
+
+#ifdef GIT_WIN32
+/** Sets the registry system dir to a mock; for testing. */
+extern int git_win32__set_registry_system_dir(const wchar_t *mock_sysdir);
+
+/** Find the given system dir; for testing. */
+extern int git_win32__find_system_dirs(git_str *out, const char *subdir);
+#endif
+
+#endif
diff --git a/src/libgit2/tag.c b/src/libgit2/tag.c
new file mode 100644
index 0000000..562ec13
--- /dev/null
+++ b/src/libgit2/tag.c
@@ -0,0 +1,599 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "tag.h"
+
+#include "commit.h"
+#include "signature.h"
+#include "wildmatch.h"
+#include "git2/object.h"
+#include "git2/repository.h"
+#include "git2/signature.h"
+#include "git2/odb_backend.h"
+
+void git_tag__free(void *_tag)
+{
+ git_tag *tag = _tag;
+ git_signature_free(tag->tagger);
+ git__free(tag->message);
+ git__free(tag->tag_name);
+ git__free(tag);
+}
+
+int git_tag_target(git_object **target, const git_tag *t)
+{
+ GIT_ASSERT_ARG(t);
+ return git_object_lookup(target, t->object.repo, &t->target, t->type);
+}
+
+const git_oid *git_tag_target_id(const git_tag *t)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(t, NULL);
+ return &t->target;
+}
+
+git_object_t git_tag_target_type(const git_tag *t)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(t, GIT_OBJECT_INVALID);
+ return t->type;
+}
+
+const char *git_tag_name(const git_tag *t)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(t, NULL);
+ return t->tag_name;
+}
+
+const git_signature *git_tag_tagger(const git_tag *t)
+{
+ return t->tagger;
+}
+
+const char *git_tag_message(const git_tag *t)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(t, NULL);
+ return t->message;
+}
+
+static int tag_error(const char *str)
+{
+ git_error_set(GIT_ERROR_TAG, "failed to parse tag: %s", str);
+ return GIT_EINVALID;
+}
+
+static int tag_parse(
+ git_tag *tag,
+ const char *buffer,
+ const char *buffer_end,
+ git_oid_t oid_type)
+{
+ static const char *tag_types[] = {
+ NULL, "commit\n", "tree\n", "blob\n", "tag\n"
+ };
+ size_t text_len, alloc_len;
+ const char *search;
+ unsigned int i;
+ int error;
+
+ if (git_object__parse_oid_header(&tag->target,
+ &buffer, buffer_end, "object ", oid_type) < 0)
+ return tag_error("object field invalid");
+
+ if (buffer + 5 >= buffer_end)
+ return tag_error("object too short");
+
+ if (memcmp(buffer, "type ", 5) != 0)
+ return tag_error("type field not found");
+ buffer += 5;
+
+ tag->type = GIT_OBJECT_INVALID;
+
+ for (i = 1; i < ARRAY_SIZE(tag_types); ++i) {
+ size_t type_length = strlen(tag_types[i]);
+
+ if (buffer + type_length >= buffer_end)
+ return tag_error("object too short");
+
+ if (memcmp(buffer, tag_types[i], type_length) == 0) {
+ tag->type = i;
+ buffer += type_length;
+ break;
+ }
+ }
+
+ if (tag->type == GIT_OBJECT_INVALID)
+ return tag_error("invalid object type");
+
+ if (buffer + 4 >= buffer_end)
+ return tag_error("object too short");
+
+ if (memcmp(buffer, "tag ", 4) != 0)
+ return tag_error("tag field not found");
+
+ buffer += 4;
+
+ search = memchr(buffer, '\n', buffer_end - buffer);
+ if (search == NULL)
+ return tag_error("object too short");
+
+ text_len = search - buffer;
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, text_len, 1);
+ tag->tag_name = git__malloc(alloc_len);
+ GIT_ERROR_CHECK_ALLOC(tag->tag_name);
+
+ memcpy(tag->tag_name, buffer, text_len);
+ tag->tag_name[text_len] = '\0';
+
+ buffer = search + 1;
+
+ tag->tagger = NULL;
+ if (buffer < buffer_end && *buffer != '\n') {
+ tag->tagger = git__malloc(sizeof(git_signature));
+ GIT_ERROR_CHECK_ALLOC(tag->tagger);
+
+ if ((error = git_signature__parse(tag->tagger, &buffer, buffer_end, "tagger ", '\n')) < 0)
+ return error;
+ }
+
+ tag->message = NULL;
+ if (buffer < buffer_end) {
+ /* If we're not at the end of the header, search for it */
+ if(*buffer != '\n') {
+ search = git__memmem(buffer, buffer_end - buffer,
+ "\n\n", 2);
+ if (search)
+ buffer = search + 1;
+ else
+ return tag_error("tag contains no message");
+ }
+
+ text_len = buffer_end - ++buffer;
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, text_len, 1);
+ tag->message = git__malloc(alloc_len);
+ GIT_ERROR_CHECK_ALLOC(tag->message);
+
+ memcpy(tag->message, buffer, text_len);
+ tag->message[text_len] = '\0';
+ }
+
+ return 0;
+}
+
+int git_tag__parse_raw(
+ void *_tag,
+ const char *data,
+ size_t size,
+ git_oid_t oid_type)
+{
+ return tag_parse(_tag, data, data + size, oid_type);
+}
+
+int git_tag__parse(
+ void *_tag,
+ git_odb_object *odb_obj,
+ git_oid_t oid_type)
+{
+ git_tag *tag = _tag;
+ const char *buffer = git_odb_object_data(odb_obj);
+ const char *buffer_end = buffer + git_odb_object_size(odb_obj);
+
+ return tag_parse(tag, buffer, buffer_end, oid_type);
+}
+
+static int retrieve_tag_reference(
+ git_reference **tag_reference_out,
+ git_str *ref_name_out,
+ git_repository *repo,
+ const char *tag_name)
+{
+ git_reference *tag_ref;
+ int error;
+
+ *tag_reference_out = NULL;
+
+ if (git_str_joinpath(ref_name_out, GIT_REFS_TAGS_DIR, tag_name) < 0)
+ return -1;
+
+ error = git_reference_lookup(&tag_ref, repo, ref_name_out->ptr);
+ if (error < 0)
+ return error; /* Be it not foundo or corrupted */
+
+ *tag_reference_out = tag_ref;
+
+ return 0;
+}
+
+static int retrieve_tag_reference_oid(
+ git_oid *oid,
+ git_str *ref_name_out,
+ git_repository *repo,
+ const char *tag_name)
+{
+ if (git_str_joinpath(ref_name_out, GIT_REFS_TAGS_DIR, tag_name) < 0)
+ return -1;
+
+ return git_reference_name_to_id(oid, repo, ref_name_out->ptr);
+}
+
+static int write_tag_annotation(
+ git_oid *oid,
+ git_repository *repo,
+ const char *tag_name,
+ const git_object *target,
+ const git_signature *tagger,
+ const char *message)
+{
+ git_str tag = GIT_STR_INIT;
+ git_odb *odb;
+
+ if (git_object__write_oid_header(&tag, "object ", git_object_id(target)) < 0)
+ goto on_error;
+
+ git_str_printf(&tag, "type %s\n", git_object_type2string(git_object_type(target)));
+ git_str_printf(&tag, "tag %s\n", tag_name);
+ git_signature__writebuf(&tag, "tagger ", tagger);
+ git_str_putc(&tag, '\n');
+
+ if (git_str_puts(&tag, message) < 0)
+ goto on_error;
+
+ if (git_repository_odb__weakptr(&odb, repo) < 0)
+ goto on_error;
+
+ if (git_odb_write(oid, odb, tag.ptr, tag.size, GIT_OBJECT_TAG) < 0)
+ goto on_error;
+
+ git_str_dispose(&tag);
+ return 0;
+
+on_error:
+ git_str_dispose(&tag);
+ git_error_set(GIT_ERROR_OBJECT, "failed to create tag annotation");
+ return -1;
+}
+
+static bool tag_name_is_valid(const char *tag_name)
+{
+ /*
+ * Discourage tag name starting with dash,
+ * https://github.com/git/git/commit/4f0accd638b8d2
+ */
+ return tag_name[0] != '-';
+}
+
+static int git_tag_create__internal(
+ git_oid *oid,
+ git_repository *repo,
+ const char *tag_name,
+ const git_object *target,
+ const git_signature *tagger,
+ const char *message,
+ int allow_ref_overwrite,
+ int create_tag_annotation)
+{
+ git_reference *new_ref = NULL;
+ git_str ref_name = GIT_STR_INIT;
+
+ int error;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(tag_name);
+ GIT_ASSERT_ARG(target);
+ GIT_ASSERT_ARG(!create_tag_annotation || (tagger && message));
+
+ if (git_object_owner(target) != repo) {
+ git_error_set(GIT_ERROR_INVALID, "the given target does not belong to this repository");
+ return -1;
+ }
+
+ if (!tag_name_is_valid(tag_name)) {
+ git_error_set(GIT_ERROR_TAG, "'%s' is not a valid tag name", tag_name);
+ return -1;
+ }
+
+ error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag_name);
+ if (error < 0 && error != GIT_ENOTFOUND)
+ goto cleanup;
+
+ /** Ensure the tag name doesn't conflict with an already existing
+ * reference unless overwriting has explicitly been requested **/
+ if (error == 0 && !allow_ref_overwrite) {
+ git_str_dispose(&ref_name);
+ git_error_set(GIT_ERROR_TAG, "tag already exists");
+ return GIT_EEXISTS;
+ }
+
+ if (create_tag_annotation) {
+ if (write_tag_annotation(oid, repo, tag_name, target, tagger, message) < 0) {
+ git_str_dispose(&ref_name);
+ return -1;
+ }
+ } else
+ git_oid_cpy(oid, git_object_id(target));
+
+ error = git_reference_create(&new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite, NULL);
+
+cleanup:
+ git_reference_free(new_ref);
+ git_str_dispose(&ref_name);
+ return error;
+}
+
+int git_tag_create(
+ git_oid *oid,
+ git_repository *repo,
+ const char *tag_name,
+ const git_object *target,
+ const git_signature *tagger,
+ const char *message,
+ int allow_ref_overwrite)
+{
+ return git_tag_create__internal(oid, repo, tag_name, target, tagger, message, allow_ref_overwrite, 1);
+}
+
+int git_tag_annotation_create(
+ git_oid *oid,
+ git_repository *repo,
+ const char *tag_name,
+ const git_object *target,
+ const git_signature *tagger,
+ const char *message)
+{
+ GIT_ASSERT_ARG(oid);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(tag_name);
+ GIT_ASSERT_ARG(target);
+ GIT_ASSERT_ARG(tagger);
+ GIT_ASSERT_ARG(message);
+
+ return write_tag_annotation(oid, repo, tag_name, target, tagger, message);
+}
+
+int git_tag_create_lightweight(
+ git_oid *oid,
+ git_repository *repo,
+ const char *tag_name,
+ const git_object *target,
+ int allow_ref_overwrite)
+{
+ return git_tag_create__internal(oid, repo, tag_name, target, NULL, NULL, allow_ref_overwrite, 0);
+}
+
+int git_tag_create_from_buffer(git_oid *oid, git_repository *repo, const char *buffer, int allow_ref_overwrite)
+{
+ git_tag tag;
+ int error;
+ git_odb *odb;
+ git_odb_stream *stream;
+ git_odb_object *target_obj;
+
+ git_reference *new_ref = NULL;
+ git_str ref_name = GIT_STR_INIT;
+
+ GIT_ASSERT_ARG(oid);
+ GIT_ASSERT_ARG(buffer);
+
+ memset(&tag, 0, sizeof(tag));
+
+ if (git_repository_odb__weakptr(&odb, repo) < 0)
+ return -1;
+
+ /* validate the buffer */
+ if (tag_parse(&tag, buffer, buffer + strlen(buffer), repo->oid_type) < 0)
+ return -1;
+
+ /* validate the target */
+ if (git_odb_read(&target_obj, odb, &tag.target) < 0)
+ goto on_error;
+
+ if (tag.type != target_obj->cached.type) {
+ git_error_set(GIT_ERROR_TAG, "the type for the given target is invalid");
+ goto on_error;
+ }
+
+ error = retrieve_tag_reference_oid(oid, &ref_name, repo, tag.tag_name);
+ if (error < 0 && error != GIT_ENOTFOUND)
+ goto on_error;
+
+ /* We don't need these objects after this */
+ git_signature_free(tag.tagger);
+ git__free(tag.tag_name);
+ git__free(tag.message);
+ git_odb_object_free(target_obj);
+
+ /** Ensure the tag name doesn't conflict with an already existing
+ * reference unless overwriting has explicitly been requested **/
+ if (error == 0 && !allow_ref_overwrite) {
+ git_str_dispose(&ref_name);
+ git_error_set(GIT_ERROR_TAG, "tag already exists");
+ return GIT_EEXISTS;
+ }
+
+ /* write the buffer */
+ if ((error = git_odb_open_wstream(
+ &stream, odb, strlen(buffer), GIT_OBJECT_TAG)) < 0) {
+ git_str_dispose(&ref_name);
+ return error;
+ }
+
+ if (!(error = git_odb_stream_write(stream, buffer, strlen(buffer))))
+ error = git_odb_stream_finalize_write(oid, stream);
+
+ git_odb_stream_free(stream);
+
+ if (error < 0) {
+ git_str_dispose(&ref_name);
+ return error;
+ }
+
+ error = git_reference_create(
+ &new_ref, repo, ref_name.ptr, oid, allow_ref_overwrite, NULL);
+
+ git_reference_free(new_ref);
+ git_str_dispose(&ref_name);
+
+ return error;
+
+on_error:
+ git_signature_free(tag.tagger);
+ git__free(tag.tag_name);
+ git__free(tag.message);
+ git_odb_object_free(target_obj);
+ return -1;
+}
+
+int git_tag_delete(git_repository *repo, const char *tag_name)
+{
+ git_reference *tag_ref;
+ git_str ref_name = GIT_STR_INIT;
+ int error;
+
+ error = retrieve_tag_reference(&tag_ref, &ref_name, repo, tag_name);
+
+ git_str_dispose(&ref_name);
+
+ if (error < 0)
+ return error;
+
+ error = git_reference_delete(tag_ref);
+
+ git_reference_free(tag_ref);
+
+ return error;
+}
+
+typedef struct {
+ git_repository *repo;
+ git_tag_foreach_cb cb;
+ void *cb_data;
+} tag_cb_data;
+
+static int tags_cb(const char *ref, void *data)
+{
+ int error;
+ git_oid oid;
+ tag_cb_data *d = (tag_cb_data *)data;
+
+ if (git__prefixcmp(ref, GIT_REFS_TAGS_DIR) != 0)
+ return 0; /* no tag */
+
+ if (!(error = git_reference_name_to_id(&oid, d->repo, ref))) {
+ if ((error = d->cb(ref, &oid, d->cb_data)) != 0)
+ git_error_set_after_callback_function(error, "git_tag_foreach");
+ }
+
+ return error;
+}
+
+int git_tag_foreach(git_repository *repo, git_tag_foreach_cb cb, void *cb_data)
+{
+ tag_cb_data data;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(cb);
+
+ data.cb = cb;
+ data.cb_data = cb_data;
+ data.repo = repo;
+
+ return git_reference_foreach_name(repo, &tags_cb, &data);
+}
+
+typedef struct {
+ git_vector *taglist;
+ const char *pattern;
+} tag_filter_data;
+
+#define GIT_REFS_TAGS_DIR_LEN strlen(GIT_REFS_TAGS_DIR)
+
+static int tag_list_cb(const char *tag_name, git_oid *oid, void *data)
+{
+ tag_filter_data *filter = (tag_filter_data *)data;
+ GIT_UNUSED(oid);
+
+ if (!*filter->pattern ||
+ wildmatch(filter->pattern, tag_name + GIT_REFS_TAGS_DIR_LEN, 0) == 0)
+ {
+ char *matched = git__strdup(tag_name + GIT_REFS_TAGS_DIR_LEN);
+ GIT_ERROR_CHECK_ALLOC(matched);
+
+ return git_vector_insert(filter->taglist, matched);
+ }
+
+ return 0;
+}
+
+int git_tag_list_match(git_strarray *tag_names, const char *pattern, git_repository *repo)
+{
+ int error;
+ tag_filter_data filter;
+ git_vector taglist;
+
+ GIT_ASSERT_ARG(tag_names);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(pattern);
+
+ if ((error = git_vector_init(&taglist, 8, NULL)) < 0)
+ return error;
+
+ filter.taglist = &taglist;
+ filter.pattern = pattern;
+
+ error = git_tag_foreach(repo, &tag_list_cb, (void *)&filter);
+
+ if (error < 0)
+ git_vector_free(&taglist);
+
+ tag_names->strings =
+ (char **)git_vector_detach(&tag_names->count, NULL, &taglist);
+
+ return 0;
+}
+
+int git_tag_list(git_strarray *tag_names, git_repository *repo)
+{
+ return git_tag_list_match(tag_names, "", repo);
+}
+
+int git_tag_peel(git_object **tag_target, const git_tag *tag)
+{
+ return git_object_peel(tag_target, (const git_object *)tag, GIT_OBJECT_ANY);
+}
+
+int git_tag_name_is_valid(int *valid, const char *name)
+{
+ git_str ref_name = GIT_STR_INIT;
+ int error = 0;
+
+ GIT_ASSERT(valid);
+
+ *valid = 0;
+
+ if (!name || !tag_name_is_valid(name))
+ goto done;
+
+ if ((error = git_str_puts(&ref_name, GIT_REFS_TAGS_DIR)) < 0 ||
+ (error = git_str_puts(&ref_name, name)) < 0)
+ goto done;
+
+ error = git_reference_name_is_valid(valid, ref_name.ptr);
+
+done:
+ git_str_dispose(&ref_name);
+ return error;
+}
+
+/* Deprecated Functions */
+
+#ifndef GIT_DEPRECATE_HARD
+int git_tag_create_frombuffer(git_oid *oid, git_repository *repo, const char *buffer, int allow_ref_overwrite)
+{
+ return git_tag_create_from_buffer(oid, repo, buffer, allow_ref_overwrite);
+}
+#endif
diff --git a/src/libgit2/tag.h b/src/libgit2/tag.h
new file mode 100644
index 0000000..fdaaa46
--- /dev/null
+++ b/src/libgit2/tag.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_tag_h__
+#define INCLUDE_tag_h__
+
+#include "common.h"
+
+#include "git2/tag.h"
+#include "repository.h"
+#include "odb.h"
+
+struct git_tag {
+ git_object object;
+
+ git_oid target;
+ git_object_t type;
+
+ char *tag_name;
+ git_signature *tagger;
+ char *message;
+};
+
+void git_tag__free(void *tag);
+int git_tag__parse(void *tag, git_odb_object *obj, git_oid_t oid_type);
+int git_tag__parse_raw(void *tag, const char *data, size_t size, git_oid_t oid_type);
+
+#endif
diff --git a/src/libgit2/threadstate.c b/src/libgit2/threadstate.c
new file mode 100644
index 0000000..ed9bb9b
--- /dev/null
+++ b/src/libgit2/threadstate.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "threadstate.h"
+#include "runtime.h"
+
+/**
+ * Handle the thread-local state
+ *
+ * `git_threadstate_global_init` will be called as part
+ * of `git_libgit2_init` (which itself must be called
+ * before calling any other function in the library).
+ *
+ * This function allocates a TLS index to store the per-
+ * thread state.
+ *
+ * Any internal method that requires thread-local state
+ * will then call `git_threadstate_get()` which returns a
+ * pointer to the thread-local state structure; this
+ * structure is lazily allocated on each thread.
+ *
+ * This mechanism will register a shutdown handler
+ * (`git_threadstate_global_shutdown`) which will free the
+ * TLS index. This shutdown handler will be called by
+ * `git_libgit2_shutdown`.
+ */
+
+static git_tlsdata_key tls_key;
+
+static void threadstate_dispose(git_threadstate *threadstate)
+{
+ if (!threadstate)
+ return;
+
+ if (threadstate->error_t.message != git_str__initstr)
+ git__free(threadstate->error_t.message);
+ threadstate->error_t.message = NULL;
+}
+
+static void GIT_SYSTEM_CALL threadstate_free(void *threadstate)
+{
+ threadstate_dispose(threadstate);
+ git__free(threadstate);
+}
+
+static void git_threadstate_global_shutdown(void)
+{
+ git_threadstate *threadstate;
+
+ threadstate = git_tlsdata_get(tls_key);
+ git_tlsdata_set(tls_key, NULL);
+
+ threadstate_dispose(threadstate);
+ git__free(threadstate);
+
+ git_tlsdata_dispose(tls_key);
+}
+
+int git_threadstate_global_init(void)
+{
+ if (git_tlsdata_init(&tls_key, &threadstate_free) != 0)
+ return -1;
+
+ return git_runtime_shutdown_register(git_threadstate_global_shutdown);
+}
+
+git_threadstate *git_threadstate_get(void)
+{
+ git_threadstate *threadstate;
+
+ if ((threadstate = git_tlsdata_get(tls_key)) != NULL)
+ return threadstate;
+
+ /*
+ * Avoid git__malloc here, since if it fails, it sets an error
+ * message, which requires thread state, which would allocate
+ * here, which would fail, which would set an error message...
+ */
+
+ if ((threadstate = git__allocator.gmalloc(sizeof(git_threadstate),
+ __FILE__, __LINE__)) == NULL)
+ return NULL;
+
+ memset(threadstate, 0, sizeof(git_threadstate));
+
+ if (git_str_init(&threadstate->error_buf, 0) < 0) {
+ git__allocator.gfree(threadstate);
+ return NULL;
+ }
+
+ git_tlsdata_set(tls_key, threadstate);
+ return threadstate;
+}
diff --git a/src/libgit2/threadstate.h b/src/libgit2/threadstate.h
new file mode 100644
index 0000000..6ef0419
--- /dev/null
+++ b/src/libgit2/threadstate.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_threadstate_h__
+#define INCLUDE_threadstate_h__
+
+#include "common.h"
+
+typedef struct {
+ git_error *last_error;
+ git_error error_t;
+ git_str error_buf;
+ char oid_fmt[GIT_OID_MAX_HEXSIZE+1];
+} git_threadstate;
+
+extern int git_threadstate_global_init(void);
+extern git_threadstate *git_threadstate_get(void);
+
+#endif
diff --git a/src/libgit2/trace.c b/src/libgit2/trace.c
new file mode 100644
index 0000000..b0c56c4
--- /dev/null
+++ b/src/libgit2/trace.c
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "trace.h"
+
+#include "str.h"
+#include "runtime.h"
+#include "git2/trace.h"
+
+struct git_trace_data git_trace__data = {0};
+
+int git_trace_set(git_trace_level_t level, git_trace_cb callback)
+{
+ GIT_ASSERT_ARG(level == 0 || callback != NULL);
+
+ git_trace__data.level = level;
+ git_trace__data.callback = callback;
+ GIT_MEMORY_BARRIER;
+
+ return 0;
+}
diff --git a/src/libgit2/trace.h b/src/libgit2/trace.h
new file mode 100644
index 0000000..239928d
--- /dev/null
+++ b/src/libgit2/trace.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_trace_h__
+#define INCLUDE_trace_h__
+
+#include "common.h"
+
+#include <git2/trace.h>
+#include "str.h"
+
+struct git_trace_data {
+ git_trace_level_t level;
+ git_trace_cb callback;
+};
+
+extern struct git_trace_data git_trace__data;
+
+GIT_INLINE(void) git_trace__write_fmt(
+ git_trace_level_t level,
+ const char *fmt,
+ va_list ap)
+{
+ git_trace_cb callback = git_trace__data.callback;
+ git_str message = GIT_STR_INIT;
+
+ git_str_vprintf(&message, fmt, ap);
+
+ callback(level, git_str_cstr(&message));
+
+ git_str_dispose(&message);
+}
+
+#define git_trace_level() (git_trace__data.level)
+
+GIT_INLINE(void) git_trace(git_trace_level_t level, const char *fmt, ...)
+{
+ if (git_trace__data.level >= level &&
+ git_trace__data.callback != NULL) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ git_trace__write_fmt(level, fmt, ap);
+ va_end(ap);
+ }
+}
+
+#endif
diff --git a/src/libgit2/trailer.c b/src/libgit2/trailer.c
new file mode 100644
index 0000000..4761c99
--- /dev/null
+++ b/src/libgit2/trailer.c
@@ -0,0 +1,430 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#include "array.h"
+#include "common.h"
+#include "git2/message.h"
+
+#include <stddef.h>
+#include <string.h>
+#include <ctype.h>
+
+#define COMMENT_LINE_CHAR '#'
+#define TRAILER_SEPARATORS ":"
+
+static const char *const git_generated_prefixes[] = {
+ "Signed-off-by: ",
+ "(cherry picked from commit ",
+ NULL
+};
+
+static int is_blank_line(const char *str)
+{
+ const char *s = str;
+ while (*s && *s != '\n' && isspace(*s))
+ s++;
+ return !*s || *s == '\n';
+}
+
+static const char *next_line(const char *str)
+{
+ const char *nl = strchr(str, '\n');
+
+ if (nl) {
+ return nl + 1;
+ } else {
+ /* return pointer to the NUL terminator: */
+ return str + strlen(str);
+ }
+}
+
+/*
+ * Return the position of the start of the last line. If len is 0, return 0.
+ */
+static bool last_line(size_t *out, const char *buf, size_t len)
+{
+ size_t i;
+
+ *out = 0;
+
+ if (len == 0)
+ return false;
+ if (len == 1)
+ return true;
+
+ /*
+ * Skip the last character (in addition to the null terminator),
+ * because if the last character is a newline, it is considered as part
+ * of the last line anyway.
+ */
+ i = len - 2;
+
+ for (; i > 0; i--) {
+ if (buf[i] == '\n') {
+ *out = i + 1;
+ return true;
+ }
+ }
+ return true;
+}
+
+/*
+ * If the given line is of the form
+ * "<token><optional whitespace><separator>..." or "<separator>...", sets out
+ * to the location of the separator and returns true. Otherwise, returns
+ * false. The optional whitespace is allowed there primarily to allow things
+ * like "Bug #43" where <token> is "Bug" and <separator> is "#".
+ *
+ * The separator-starts-line case (in which this function returns true and
+ * sets out to 0) is distinguished from the non-well-formed-line case (in
+ * which this function returns false) because some callers of this function
+ * need such a distinction.
+ */
+static bool find_separator(size_t *out, const char *line, const char *separators)
+{
+ int whitespace_found = 0;
+ const char *c;
+ for (c = line; *c; c++) {
+ if (strchr(separators, *c)) {
+ *out = c - line;
+ return true;
+ }
+
+ if (!whitespace_found && (isalnum(*c) || *c == '-'))
+ continue;
+ if (c != line && (*c == ' ' || *c == '\t')) {
+ whitespace_found = 1;
+ continue;
+ }
+ break;
+ }
+ return false;
+}
+
+/*
+ * Inspect the given string and determine the true "end" of the log message, in
+ * order to find where to put a new Signed-off-by: line. Ignored are
+ * trailing comment lines and blank lines. To support "git commit -s
+ * --amend" on an existing commit, we also ignore "Conflicts:". To
+ * support "git commit -v", we truncate at cut lines.
+ *
+ * Returns the number of bytes from the tail to ignore, to be fed as
+ * the second parameter to append_signoff().
+ */
+static size_t ignore_non_trailer(const char *buf, size_t len)
+{
+ size_t boc = 0, bol = 0;
+ int in_old_conflicts_block = 0;
+ size_t cutoff = len;
+
+ while (bol < cutoff) {
+ const char *next_line = memchr(buf + bol, '\n', len - bol);
+
+ if (!next_line)
+ next_line = buf + len;
+ else
+ next_line++;
+
+ if (buf[bol] == COMMENT_LINE_CHAR || buf[bol] == '\n') {
+ /* is this the first of the run of comments? */
+ if (!boc)
+ boc = bol;
+ /* otherwise, it is just continuing */
+ } else if (git__prefixcmp(buf + bol, "Conflicts:\n") == 0) {
+ in_old_conflicts_block = 1;
+ if (!boc)
+ boc = bol;
+ } else if (in_old_conflicts_block && buf[bol] == '\t') {
+ ; /* a pathname in the conflicts block */
+ } else if (boc) {
+ /* the previous was not trailing comment */
+ boc = 0;
+ in_old_conflicts_block = 0;
+ }
+ bol = next_line - buf;
+ }
+ return boc ? len - boc : len - cutoff;
+}
+
+/*
+ * Return the position of the start of the patch or the length of str if there
+ * is no patch in the message.
+ */
+static size_t find_patch_start(const char *str)
+{
+ const char *s;
+
+ for (s = str; *s; s = next_line(s)) {
+ if (git__prefixcmp(s, "---") == 0)
+ return s - str;
+ }
+
+ return s - str;
+}
+
+/*
+ * Return the position of the first trailer line or len if there are no
+ * trailers.
+ */
+static size_t find_trailer_start(const char *buf, size_t len)
+{
+ const char *s;
+ size_t end_of_title, l;
+ int only_spaces = 1;
+ int recognized_prefix = 0, trailer_lines = 0, non_trailer_lines = 0;
+ /*
+ * Number of possible continuation lines encountered. This will be
+ * reset to 0 if we encounter a trailer (since those lines are to be
+ * considered continuations of that trailer), and added to
+ * non_trailer_lines if we encounter a non-trailer (since those lines
+ * are to be considered non-trailers).
+ */
+ int possible_continuation_lines = 0;
+
+ /* The first paragraph is the title and cannot be trailers */
+ for (s = buf; s < buf + len; s = next_line(s)) {
+ if (s[0] == COMMENT_LINE_CHAR)
+ continue;
+ if (is_blank_line(s))
+ break;
+ }
+ end_of_title = s - buf;
+
+ /*
+ * Get the start of the trailers by looking starting from the end for a
+ * blank line before a set of non-blank lines that (i) are all
+ * trailers, or (ii) contains at least one Git-generated trailer and
+ * consists of at least 25% trailers.
+ */
+ l = len;
+ while (last_line(&l, buf, l) && l >= end_of_title) {
+ const char *bol = buf + l;
+ const char *const *p;
+ size_t separator_pos = 0;
+
+ if (bol[0] == COMMENT_LINE_CHAR) {
+ non_trailer_lines += possible_continuation_lines;
+ possible_continuation_lines = 0;
+ continue;
+ }
+ if (is_blank_line(bol)) {
+ if (only_spaces)
+ continue;
+ non_trailer_lines += possible_continuation_lines;
+ if (recognized_prefix &&
+ trailer_lines * 3 >= non_trailer_lines)
+ return next_line(bol) - buf;
+ else if (trailer_lines && !non_trailer_lines)
+ return next_line(bol) - buf;
+ return len;
+ }
+ only_spaces = 0;
+
+ for (p = git_generated_prefixes; *p; p++) {
+ if (git__prefixcmp(bol, *p) == 0) {
+ trailer_lines++;
+ possible_continuation_lines = 0;
+ recognized_prefix = 1;
+ goto continue_outer_loop;
+ }
+ }
+
+ find_separator(&separator_pos, bol, TRAILER_SEPARATORS);
+ if (separator_pos >= 1 && !isspace(bol[0])) {
+ trailer_lines++;
+ possible_continuation_lines = 0;
+ if (recognized_prefix)
+ continue;
+ } else if (isspace(bol[0]))
+ possible_continuation_lines++;
+ else {
+ non_trailer_lines++;
+ non_trailer_lines += possible_continuation_lines;
+ possible_continuation_lines = 0;
+ }
+continue_outer_loop:
+ ;
+ }
+
+ return len;
+}
+
+/* Return the position of the end of the trailers. */
+static size_t find_trailer_end(const char *buf, size_t len)
+{
+ return len - ignore_non_trailer(buf, len);
+}
+
+static char *extract_trailer_block(const char *message, size_t *len)
+{
+ size_t patch_start = find_patch_start(message);
+ size_t trailer_end = find_trailer_end(message, patch_start);
+ size_t trailer_start = find_trailer_start(message, trailer_end);
+
+ size_t trailer_len = trailer_end - trailer_start;
+
+ char *buffer = git__malloc(trailer_len + 1);
+ if (buffer == NULL)
+ return NULL;
+
+ memcpy(buffer, message + trailer_start, trailer_len);
+ buffer[trailer_len] = 0;
+
+ *len = trailer_len;
+
+ return buffer;
+}
+
+enum trailer_state {
+ S_START = 0,
+ S_KEY = 1,
+ S_KEY_WS = 2,
+ S_SEP_WS = 3,
+ S_VALUE = 4,
+ S_VALUE_NL = 5,
+ S_VALUE_END = 6,
+ S_IGNORE = 7
+};
+
+#define NEXT(st) { state = (st); ptr++; continue; }
+#define GOTO(st) { state = (st); continue; }
+
+typedef git_array_t(git_message_trailer) git_array_trailer_t;
+
+int git_message_trailers(git_message_trailer_array *trailer_arr, const char *message)
+{
+ enum trailer_state state = S_START;
+ int rc = 0;
+ char *ptr;
+ char *key = NULL;
+ char *value = NULL;
+ git_array_trailer_t arr = GIT_ARRAY_INIT;
+
+ size_t trailer_len;
+ char *trailer = extract_trailer_block(message, &trailer_len);
+ if (trailer == NULL)
+ return -1;
+
+ for (ptr = trailer;;) {
+ switch (state) {
+ case S_START: {
+ if (*ptr == 0) {
+ goto ret;
+ }
+
+ key = ptr;
+ GOTO(S_KEY);
+ }
+ case S_KEY: {
+ if (*ptr == 0) {
+ goto ret;
+ }
+
+ if (isalnum(*ptr) || *ptr == '-') {
+ /* legal key character */
+ NEXT(S_KEY);
+ }
+
+ if (*ptr == ' ' || *ptr == '\t') {
+ /* optional whitespace before separator */
+ *ptr = 0;
+ NEXT(S_KEY_WS);
+ }
+
+ if (strchr(TRAILER_SEPARATORS, *ptr)) {
+ *ptr = 0;
+ NEXT(S_SEP_WS);
+ }
+
+ /* illegal character */
+ GOTO(S_IGNORE);
+ }
+ case S_KEY_WS: {
+ if (*ptr == 0) {
+ goto ret;
+ }
+
+ if (*ptr == ' ' || *ptr == '\t') {
+ NEXT(S_KEY_WS);
+ }
+
+ if (strchr(TRAILER_SEPARATORS, *ptr)) {
+ NEXT(S_SEP_WS);
+ }
+
+ /* illegal character */
+ GOTO(S_IGNORE);
+ }
+ case S_SEP_WS: {
+ if (*ptr == 0) {
+ goto ret;
+ }
+
+ if (*ptr == ' ' || *ptr == '\t') {
+ NEXT(S_SEP_WS);
+ }
+
+ value = ptr;
+ NEXT(S_VALUE);
+ }
+ case S_VALUE: {
+ if (*ptr == 0) {
+ GOTO(S_VALUE_END);
+ }
+
+ if (*ptr == '\n') {
+ NEXT(S_VALUE_NL);
+ }
+
+ NEXT(S_VALUE);
+ }
+ case S_VALUE_NL: {
+ if (*ptr == ' ') {
+ /* continuation; */
+ NEXT(S_VALUE);
+ }
+
+ ptr[-1] = 0;
+ GOTO(S_VALUE_END);
+ }
+ case S_VALUE_END: {
+ git_message_trailer *t = git_array_alloc(arr);
+
+ t->key = key;
+ t->value = value;
+
+ key = NULL;
+ value = NULL;
+
+ GOTO(S_START);
+ }
+ case S_IGNORE: {
+ if (*ptr == 0) {
+ goto ret;
+ }
+
+ if (*ptr == '\n') {
+ NEXT(S_START);
+ }
+
+ NEXT(S_IGNORE);
+ }
+ }
+ }
+
+ret:
+ trailer_arr->_trailer_block = trailer;
+ trailer_arr->trailers = arr.ptr;
+ trailer_arr->count = arr.size;
+
+ return rc;
+}
+
+void git_message_trailer_array_free(git_message_trailer_array *arr)
+{
+ git__free(arr->_trailer_block);
+ git__free(arr->trailers);
+}
diff --git a/src/libgit2/transaction.c b/src/libgit2/transaction.c
new file mode 100644
index 0000000..ccffa99
--- /dev/null
+++ b/src/libgit2/transaction.c
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "transaction.h"
+
+#include "repository.h"
+#include "strmap.h"
+#include "refdb.h"
+#include "pool.h"
+#include "reflog.h"
+#include "signature.h"
+#include "config.h"
+
+#include "git2/transaction.h"
+#include "git2/signature.h"
+#include "git2/sys/refs.h"
+#include "git2/sys/refdb_backend.h"
+
+typedef enum {
+ TRANSACTION_NONE,
+ TRANSACTION_REFS,
+ TRANSACTION_CONFIG
+} transaction_t;
+
+typedef struct {
+ const char *name;
+ void *payload;
+
+ git_reference_t ref_type;
+ union {
+ git_oid id;
+ char *symbolic;
+ } target;
+ git_reflog *reflog;
+
+ const char *message;
+ git_signature *sig;
+
+ unsigned int committed :1,
+ remove :1;
+} transaction_node;
+
+struct git_transaction {
+ transaction_t type;
+ git_repository *repo;
+ git_refdb *db;
+ git_config *cfg;
+
+ git_strmap *locks;
+ git_pool pool;
+};
+
+int git_transaction_config_new(git_transaction **out, git_config *cfg)
+{
+ git_transaction *tx;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(cfg);
+
+ tx = git__calloc(1, sizeof(git_transaction));
+ GIT_ERROR_CHECK_ALLOC(tx);
+
+ tx->type = TRANSACTION_CONFIG;
+ tx->cfg = cfg;
+ *out = tx;
+ return 0;
+}
+
+int git_transaction_new(git_transaction **out, git_repository *repo)
+{
+ int error;
+ git_pool pool;
+ git_transaction *tx = NULL;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+
+ if ((error = git_pool_init(&pool, 1)) < 0)
+ goto on_error;
+
+ tx = git_pool_mallocz(&pool, sizeof(git_transaction));
+ if (!tx) {
+ error = -1;
+ goto on_error;
+ }
+
+ if ((error = git_strmap_new(&tx->locks)) < 0) {
+ error = -1;
+ goto on_error;
+ }
+
+ if ((error = git_repository_refdb(&tx->db, repo)) < 0)
+ goto on_error;
+
+ tx->type = TRANSACTION_REFS;
+ memcpy(&tx->pool, &pool, sizeof(git_pool));
+ tx->repo = repo;
+ *out = tx;
+ return 0;
+
+on_error:
+ git_pool_clear(&pool);
+ return error;
+}
+
+int git_transaction_lock_ref(git_transaction *tx, const char *refname)
+{
+ int error;
+ transaction_node *node;
+
+ GIT_ASSERT_ARG(tx);
+ GIT_ASSERT_ARG(refname);
+
+ node = git_pool_mallocz(&tx->pool, sizeof(transaction_node));
+ GIT_ERROR_CHECK_ALLOC(node);
+
+ node->name = git_pool_strdup(&tx->pool, refname);
+ GIT_ERROR_CHECK_ALLOC(node->name);
+
+ if ((error = git_refdb_lock(&node->payload, tx->db, refname)) < 0)
+ return error;
+
+ if ((error = git_strmap_set(tx->locks, node->name, node)) < 0)
+ goto cleanup;
+
+ return 0;
+
+cleanup:
+ git_refdb_unlock(tx->db, node->payload, false, false, NULL, NULL, NULL);
+
+ return error;
+}
+
+static int find_locked(transaction_node **out, git_transaction *tx, const char *refname)
+{
+ transaction_node *node;
+
+ if ((node = git_strmap_get(tx->locks, refname)) == NULL) {
+ git_error_set(GIT_ERROR_REFERENCE, "the specified reference is not locked");
+ return GIT_ENOTFOUND;
+ }
+
+ *out = node;
+ return 0;
+}
+
+static int copy_common(transaction_node *node, git_transaction *tx, const git_signature *sig, const char *msg)
+{
+ if (sig && git_signature__pdup(&node->sig, sig, &tx->pool) < 0)
+ return -1;
+
+ if (!node->sig) {
+ git_signature *tmp;
+ int error;
+
+ if (git_reference__log_signature(&tmp, tx->repo) < 0)
+ return -1;
+
+ /* make sure the sig we use is in our pool */
+ error = git_signature__pdup(&node->sig, tmp, &tx->pool);
+ git_signature_free(tmp);
+ if (error < 0)
+ return error;
+ }
+
+ if (msg) {
+ node->message = git_pool_strdup(&tx->pool, msg);
+ GIT_ERROR_CHECK_ALLOC(node->message);
+ }
+
+ return 0;
+}
+
+int git_transaction_set_target(git_transaction *tx, const char *refname, const git_oid *target, const git_signature *sig, const char *msg)
+{
+ int error;
+ transaction_node *node;
+
+ GIT_ASSERT_ARG(tx);
+ GIT_ASSERT_ARG(refname);
+ GIT_ASSERT_ARG(target);
+
+ if ((error = find_locked(&node, tx, refname)) < 0)
+ return error;
+
+ if ((error = copy_common(node, tx, sig, msg)) < 0)
+ return error;
+
+ git_oid_cpy(&node->target.id, target);
+ node->ref_type = GIT_REFERENCE_DIRECT;
+
+ return 0;
+}
+
+int git_transaction_set_symbolic_target(git_transaction *tx, const char *refname, const char *target, const git_signature *sig, const char *msg)
+{
+ int error;
+ transaction_node *node;
+
+ GIT_ASSERT_ARG(tx);
+ GIT_ASSERT_ARG(refname);
+ GIT_ASSERT_ARG(target);
+
+ if ((error = find_locked(&node, tx, refname)) < 0)
+ return error;
+
+ if ((error = copy_common(node, tx, sig, msg)) < 0)
+ return error;
+
+ node->target.symbolic = git_pool_strdup(&tx->pool, target);
+ GIT_ERROR_CHECK_ALLOC(node->target.symbolic);
+ node->ref_type = GIT_REFERENCE_SYMBOLIC;
+
+ return 0;
+}
+
+int git_transaction_remove(git_transaction *tx, const char *refname)
+{
+ int error;
+ transaction_node *node;
+
+ if ((error = find_locked(&node, tx, refname)) < 0)
+ return error;
+
+ node->remove = true;
+ node->ref_type = GIT_REFERENCE_DIRECT; /* the id will be ignored */
+
+ return 0;
+}
+
+static int dup_reflog(git_reflog **out, const git_reflog *in, git_pool *pool)
+{
+ git_reflog *reflog;
+ git_reflog_entry *entries;
+ size_t len, i;
+
+ reflog = git_pool_mallocz(pool, sizeof(git_reflog));
+ GIT_ERROR_CHECK_ALLOC(reflog);
+
+ reflog->ref_name = git_pool_strdup(pool, in->ref_name);
+ GIT_ERROR_CHECK_ALLOC(reflog->ref_name);
+
+ len = in->entries.length;
+ reflog->entries.length = len;
+ reflog->entries.contents = git_pool_mallocz(pool, len * sizeof(void *));
+ GIT_ERROR_CHECK_ALLOC(reflog->entries.contents);
+
+ entries = git_pool_mallocz(pool, len * sizeof(git_reflog_entry));
+ GIT_ERROR_CHECK_ALLOC(entries);
+
+ for (i = 0; i < len; i++) {
+ const git_reflog_entry *src;
+ git_reflog_entry *tgt;
+
+ tgt = &entries[i];
+ reflog->entries.contents[i] = tgt;
+
+ src = git_vector_get(&in->entries, i);
+ git_oid_cpy(&tgt->oid_old, &src->oid_old);
+ git_oid_cpy(&tgt->oid_cur, &src->oid_cur);
+
+ tgt->msg = git_pool_strdup(pool, src->msg);
+ GIT_ERROR_CHECK_ALLOC(tgt->msg);
+
+ if (git_signature__pdup(&tgt->committer, src->committer, pool) < 0)
+ return -1;
+ }
+
+
+ *out = reflog;
+ return 0;
+}
+
+int git_transaction_set_reflog(git_transaction *tx, const char *refname, const git_reflog *reflog)
+{
+ int error;
+ transaction_node *node;
+
+ GIT_ASSERT_ARG(tx);
+ GIT_ASSERT_ARG(refname);
+ GIT_ASSERT_ARG(reflog);
+
+ if ((error = find_locked(&node, tx, refname)) < 0)
+ return error;
+
+ if ((error = dup_reflog(&node->reflog, reflog, &tx->pool)) < 0)
+ return error;
+
+ return 0;
+}
+
+static int update_target(git_refdb *db, transaction_node *node)
+{
+ git_reference *ref;
+ int error, update_reflog;
+
+ if (node->ref_type == GIT_REFERENCE_DIRECT) {
+ ref = git_reference__alloc(node->name, &node->target.id, NULL);
+ } else if (node->ref_type == GIT_REFERENCE_SYMBOLIC) {
+ ref = git_reference__alloc_symbolic(node->name, node->target.symbolic);
+ } else {
+ abort();
+ }
+
+ GIT_ERROR_CHECK_ALLOC(ref);
+ update_reflog = node->reflog == NULL;
+
+ if (node->remove) {
+ error = git_refdb_unlock(db, node->payload, 2, false, ref, NULL, NULL);
+ } else if (node->ref_type == GIT_REFERENCE_DIRECT) {
+ error = git_refdb_unlock(db, node->payload, true, update_reflog, ref, node->sig, node->message);
+ } else if (node->ref_type == GIT_REFERENCE_SYMBOLIC) {
+ error = git_refdb_unlock(db, node->payload, true, update_reflog, ref, node->sig, node->message);
+ } else {
+ abort();
+ }
+
+ git_reference_free(ref);
+ node->committed = true;
+
+ return error;
+}
+
+int git_transaction_commit(git_transaction *tx)
+{
+ transaction_node *node;
+ int error = 0;
+
+ GIT_ASSERT_ARG(tx);
+
+ if (tx->type == TRANSACTION_CONFIG) {
+ error = git_config_unlock(tx->cfg, true);
+ tx->cfg = NULL;
+
+ return error;
+ }
+
+ git_strmap_foreach_value(tx->locks, node, {
+ if (node->reflog) {
+ if ((error = tx->db->backend->reflog_write(tx->db->backend, node->reflog)) < 0)
+ return error;
+ }
+
+ if (node->ref_type == GIT_REFERENCE_INVALID) {
+ /* ref was locked but not modified */
+ if ((error = git_refdb_unlock(tx->db, node->payload, false, false, NULL, NULL, NULL)) < 0) {
+ return error;
+ }
+ node->committed = true;
+ } else {
+ if ((error = update_target(tx->db, node)) < 0)
+ return error;
+ }
+ });
+
+ return 0;
+}
+
+void git_transaction_free(git_transaction *tx)
+{
+ transaction_node *node;
+ git_pool pool;
+
+ if (!tx)
+ return;
+
+ if (tx->type == TRANSACTION_CONFIG) {
+ if (tx->cfg) {
+ git_config_unlock(tx->cfg, false);
+ git_config_free(tx->cfg);
+ }
+
+ git__free(tx);
+ return;
+ }
+
+ /* start by unlocking the ones we've left hanging, if any */
+ git_strmap_foreach_value(tx->locks, node, {
+ if (node->committed)
+ continue;
+
+ git_refdb_unlock(tx->db, node->payload, false, false, NULL, NULL, NULL);
+ });
+
+ git_refdb_free(tx->db);
+ git_strmap_free(tx->locks);
+
+ /* tx is inside the pool, so we need to extract the data */
+ memcpy(&pool, &tx->pool, sizeof(git_pool));
+ git_pool_clear(&pool);
+}
diff --git a/src/libgit2/transaction.h b/src/libgit2/transaction.h
new file mode 100644
index 0000000..780c068
--- /dev/null
+++ b/src/libgit2/transaction.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_transaction_h__
+#define INCLUDE_transaction_h__
+
+#include "common.h"
+
+int git_transaction_config_new(git_transaction **out, git_config *cfg);
+
+#endif
diff --git a/src/libgit2/transport.c b/src/libgit2/transport.c
new file mode 100644
index 0000000..640ccac
--- /dev/null
+++ b/src/libgit2/transport.c
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+
+#include "git2/types.h"
+#include "git2/remote.h"
+#include "git2/net.h"
+#include "git2/transport.h"
+#include "git2/sys/transport.h"
+#include "fs_path.h"
+
+typedef struct transport_definition {
+ char *prefix;
+ git_transport_cb fn;
+ void *param;
+} transport_definition;
+
+static git_smart_subtransport_definition http_subtransport_definition = { git_smart_subtransport_http, 1, NULL };
+static git_smart_subtransport_definition git_subtransport_definition = { git_smart_subtransport_git, 0, NULL };
+#ifdef GIT_SSH
+static git_smart_subtransport_definition ssh_subtransport_definition = { git_smart_subtransport_ssh, 0, NULL };
+#endif
+
+static transport_definition local_transport_definition = { "file://", git_transport_local, NULL };
+
+static transport_definition transports[] = {
+ { "git://", git_transport_smart, &git_subtransport_definition },
+ { "http://", git_transport_smart, &http_subtransport_definition },
+ { "https://", git_transport_smart, &http_subtransport_definition },
+ { "file://", git_transport_local, NULL },
+#ifdef GIT_SSH
+ { "ssh://", git_transport_smart, &ssh_subtransport_definition },
+ { "ssh+git://", git_transport_smart, &ssh_subtransport_definition },
+ { "git+ssh://", git_transport_smart, &ssh_subtransport_definition },
+#endif
+ { NULL, 0, 0 }
+};
+
+static git_vector custom_transports = GIT_VECTOR_INIT;
+
+#define GIT_TRANSPORT_COUNT (sizeof(transports)/sizeof(transports[0])) - 1
+
+static transport_definition * transport_find_by_url(const char *url)
+{
+ size_t i = 0;
+ transport_definition *d;
+
+ /* Find a user transport who wants to deal with this URI */
+ git_vector_foreach(&custom_transports, i, d) {
+ if (strncasecmp(url, d->prefix, strlen(d->prefix)) == 0) {
+ return d;
+ }
+ }
+
+ /* Find a system transport for this URI */
+ for (i = 0; i < GIT_TRANSPORT_COUNT; ++i) {
+ d = &transports[i];
+
+ if (strncasecmp(url, d->prefix, strlen(d->prefix)) == 0) {
+ return d;
+ }
+ }
+
+ return NULL;
+}
+
+static int transport_find_fn(
+ git_transport_cb *out,
+ const char *url,
+ void **param)
+{
+ transport_definition *definition = transport_find_by_url(url);
+
+#ifdef GIT_WIN32
+ /* On Windows, it might not be possible to discern between absolute local
+ * and ssh paths - first check if this is a valid local path that points
+ * to a directory and if so assume local path, else assume SSH */
+
+ /* Check to see if the path points to a file on the local file system */
+ if (!definition && git_fs_path_exists(url) && git_fs_path_isdir(url))
+ definition = &local_transport_definition;
+#endif
+
+ /* For other systems, perform the SSH check first, to avoid going to the
+ * filesystem if it is not necessary */
+
+ /* It could be a SSH remote path. Check to see if there's a : */
+ if (!definition && strrchr(url, ':')) {
+ /* re-search transports again with ssh:// as url
+ * so that we can find a third party ssh transport */
+ definition = transport_find_by_url("ssh://");
+ }
+
+#ifndef GIT_WIN32
+ /* Check to see if the path points to a file on the local file system */
+ if (!definition && git_fs_path_exists(url) && git_fs_path_isdir(url))
+ definition = &local_transport_definition;
+#endif
+
+ if (!definition)
+ return GIT_ENOTFOUND;
+
+ *out = definition->fn;
+ *param = definition->param;
+
+ return 0;
+}
+
+/**************
+ * Public API *
+ **************/
+
+int git_transport_new(git_transport **out, git_remote *owner, const char *url)
+{
+ git_transport_cb fn;
+ git_transport *transport;
+ void *param;
+ int error;
+
+ if ((error = transport_find_fn(&fn, url, &param)) == GIT_ENOTFOUND) {
+ git_error_set(GIT_ERROR_NET, "unsupported URL protocol");
+ return -1;
+ } else if (error < 0)
+ return error;
+
+ if ((error = fn(&transport, owner, param)) < 0)
+ return error;
+
+ GIT_ERROR_CHECK_VERSION(transport, GIT_TRANSPORT_VERSION, "git_transport");
+
+ *out = transport;
+
+ return 0;
+}
+
+int git_transport_register(
+ const char *scheme,
+ git_transport_cb cb,
+ void *param)
+{
+ git_str prefix = GIT_STR_INIT;
+ transport_definition *d, *definition = NULL;
+ size_t i;
+ int error = 0;
+
+ GIT_ASSERT_ARG(scheme);
+ GIT_ASSERT_ARG(cb);
+
+ if ((error = git_str_printf(&prefix, "%s://", scheme)) < 0)
+ goto on_error;
+
+ git_vector_foreach(&custom_transports, i, d) {
+ if (strcasecmp(d->prefix, prefix.ptr) == 0) {
+ error = GIT_EEXISTS;
+ goto on_error;
+ }
+ }
+
+ definition = git__calloc(1, sizeof(transport_definition));
+ GIT_ERROR_CHECK_ALLOC(definition);
+
+ definition->prefix = git_str_detach(&prefix);
+ definition->fn = cb;
+ definition->param = param;
+
+ if (git_vector_insert(&custom_transports, definition) < 0)
+ goto on_error;
+
+ return 0;
+
+on_error:
+ git_str_dispose(&prefix);
+ git__free(definition);
+ return error;
+}
+
+int git_transport_unregister(const char *scheme)
+{
+ git_str prefix = GIT_STR_INIT;
+ transport_definition *d;
+ size_t i;
+ int error = 0;
+
+ GIT_ASSERT_ARG(scheme);
+
+ if ((error = git_str_printf(&prefix, "%s://", scheme)) < 0)
+ goto done;
+
+ git_vector_foreach(&custom_transports, i, d) {
+ if (strcasecmp(d->prefix, prefix.ptr) == 0) {
+ if ((error = git_vector_remove(&custom_transports, i)) < 0)
+ goto done;
+
+ git__free(d->prefix);
+ git__free(d);
+
+ if (!custom_transports.length)
+ git_vector_free(&custom_transports);
+
+ error = 0;
+ goto done;
+ }
+ }
+
+ error = GIT_ENOTFOUND;
+
+done:
+ git_str_dispose(&prefix);
+ return error;
+}
+
+int git_transport_init(git_transport *opts, unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(
+ opts, version, git_transport, GIT_TRANSPORT_INIT);
+ return 0;
+}
diff --git a/src/libgit2/transports/auth.c b/src/libgit2/transports/auth.c
new file mode 100644
index 0000000..90b6b12
--- /dev/null
+++ b/src/libgit2/transports/auth.c
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "auth.h"
+
+#include "git2/sys/credential.h"
+
+static int basic_next_token(
+ git_str *out,
+ git_http_auth_context *ctx,
+ git_credential *c)
+{
+ git_credential_userpass_plaintext *cred;
+ git_str raw = GIT_STR_INIT;
+ int error = GIT_EAUTH;
+
+ GIT_UNUSED(ctx);
+
+ if (c->credtype != GIT_CREDENTIAL_USERPASS_PLAINTEXT) {
+ git_error_set(GIT_ERROR_INVALID, "invalid credential type for basic auth");
+ goto on_error;
+ }
+
+ cred = (git_credential_userpass_plaintext *)c;
+
+ git_str_printf(&raw, "%s:%s", cred->username, cred->password);
+
+ if (git_str_oom(&raw) ||
+ git_str_puts(out, "Basic ") < 0 ||
+ git_str_encode_base64(out, git_str_cstr(&raw), raw.size) < 0)
+ goto on_error;
+
+ error = 0;
+
+on_error:
+ if (raw.size)
+ git__memzero(raw.ptr, raw.size);
+
+ git_str_dispose(&raw);
+ return error;
+}
+
+static git_http_auth_context basic_context = {
+ GIT_HTTP_AUTH_BASIC,
+ GIT_CREDENTIAL_USERPASS_PLAINTEXT,
+ 0,
+ NULL,
+ basic_next_token,
+ NULL,
+ NULL
+};
+
+int git_http_auth_basic(
+ git_http_auth_context **out, const git_net_url *url)
+{
+ GIT_UNUSED(url);
+
+ *out = &basic_context;
+ return 0;
+}
+
+int git_http_auth_dummy(
+ git_http_auth_context **out, const git_net_url *url)
+{
+ GIT_UNUSED(url);
+
+ *out = NULL;
+ return GIT_PASSTHROUGH;
+}
+
diff --git a/src/libgit2/transports/auth.h b/src/libgit2/transports/auth.h
new file mode 100644
index 0000000..9f6f8fd
--- /dev/null
+++ b/src/libgit2/transports/auth.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_transports_auth_h__
+#define INCLUDE_transports_auth_h__
+
+#include "common.h"
+#include "net.h"
+
+typedef enum {
+ GIT_HTTP_AUTH_BASIC = 1,
+ GIT_HTTP_AUTH_NEGOTIATE = 2,
+ GIT_HTTP_AUTH_NTLM = 4
+} git_http_auth_t;
+
+typedef struct git_http_auth_context git_http_auth_context;
+
+struct git_http_auth_context {
+ /** Type of scheme */
+ git_http_auth_t type;
+
+ /** Supported credentials */
+ git_credential_t credtypes;
+
+ /** Connection affinity or request affinity */
+ unsigned connection_affinity : 1;
+
+ /** Sets the challenge on the authentication context */
+ int (*set_challenge)(git_http_auth_context *ctx, const char *challenge);
+
+ /** Gets the next authentication token from the context */
+ int (*next_token)(git_str *out, git_http_auth_context *ctx, git_credential *cred);
+
+ /** Examines if all tokens have been presented. */
+ int (*is_complete)(git_http_auth_context *ctx);
+
+ /** Frees the authentication context */
+ void (*free)(git_http_auth_context *ctx);
+};
+
+typedef struct {
+ /** Type of scheme */
+ git_http_auth_t type;
+
+ /** Name of the scheme (as used in the Authorization header) */
+ const char *name;
+
+ /** Credential types this scheme supports */
+ git_credential_t credtypes;
+
+ /** Function to initialize an authentication context */
+ int (*init_context)(
+ git_http_auth_context **out,
+ const git_net_url *url);
+} git_http_auth_scheme;
+
+int git_http_auth_dummy(
+ git_http_auth_context **out,
+ const git_net_url *url);
+
+int git_http_auth_basic(
+ git_http_auth_context **out,
+ const git_net_url *url);
+
+#endif
diff --git a/src/libgit2/transports/auth_gssapi.c b/src/libgit2/transports/auth_gssapi.c
new file mode 100644
index 0000000..5005538
--- /dev/null
+++ b/src/libgit2/transports/auth_gssapi.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "auth_negotiate.h"
+
+#if defined(GIT_GSSAPI) || defined(GIT_GSSFRAMEWORK)
+
+#include "git2.h"
+#include "auth.h"
+#include "git2/sys/credential.h"
+
+#ifdef GIT_GSSFRAMEWORK
+#import <GSS/GSS.h>
+#elif defined(GIT_GSSAPI)
+#include <gssapi.h>
+#include <krb5.h>
+#endif
+
+static gss_OID_desc gssapi_oid_spnego =
+ { 6, (void *) "\x2b\x06\x01\x05\x05\x02" };
+static gss_OID_desc gssapi_oid_krb5 =
+ { 9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
+
+static gss_OID gssapi_oids[] =
+ { &gssapi_oid_spnego, &gssapi_oid_krb5, NULL };
+
+typedef struct {
+ git_http_auth_context parent;
+ unsigned configured : 1,
+ complete : 1;
+ git_str target;
+ char *challenge;
+ gss_ctx_id_t gss_context;
+ gss_OID oid;
+} http_auth_gssapi_context;
+
+static void gssapi_err_set(
+ OM_uint32 status_major,
+ OM_uint32 status_minor,
+ const char *message)
+{
+ gss_buffer_desc buffer = GSS_C_EMPTY_BUFFER;
+ OM_uint32 status_display, context = 0;
+
+ if (gss_display_status(&status_display, status_major, GSS_C_GSS_CODE,
+ GSS_C_NO_OID, &context, &buffer) == GSS_S_COMPLETE) {
+ git_error_set(GIT_ERROR_NET, "%s: %.*s (%d.%d)",
+ message, (int)buffer.length, (const char *)buffer.value,
+ status_major, status_minor);
+ gss_release_buffer(&status_minor, &buffer);
+ } else {
+ git_error_set(GIT_ERROR_NET, "%s: unknown negotiate error (%d.%d)",
+ message, status_major, status_minor);
+ }
+}
+
+static int gssapi_set_challenge(
+ git_http_auth_context *c,
+ const char *challenge)
+{
+ http_auth_gssapi_context *ctx = (http_auth_gssapi_context *)c;
+
+ GIT_ASSERT_ARG(ctx);
+ GIT_ASSERT_ARG(challenge);
+ GIT_ASSERT(ctx->configured);
+
+ git__free(ctx->challenge);
+
+ ctx->challenge = git__strdup(challenge);
+ GIT_ERROR_CHECK_ALLOC(ctx->challenge);
+
+ return 0;
+}
+
+static void gssapi_context_dispose(http_auth_gssapi_context *ctx)
+{
+ OM_uint32 status_minor;
+
+ if (ctx->gss_context != GSS_C_NO_CONTEXT) {
+ gss_delete_sec_context(
+ &status_minor, &ctx->gss_context, GSS_C_NO_BUFFER);
+ ctx->gss_context = GSS_C_NO_CONTEXT;
+ }
+
+ git_str_dispose(&ctx->target);
+
+ git__free(ctx->challenge);
+ ctx->challenge = NULL;
+}
+
+static int gssapi_next_token(
+ git_str *buf,
+ git_http_auth_context *c,
+ git_credential *cred)
+{
+ http_auth_gssapi_context *ctx = (http_auth_gssapi_context *)c;
+ OM_uint32 status_major, status_minor;
+ gss_buffer_desc target_buffer = GSS_C_EMPTY_BUFFER,
+ input_token = GSS_C_EMPTY_BUFFER,
+ output_token = GSS_C_EMPTY_BUFFER;
+ gss_buffer_t input_token_ptr = GSS_C_NO_BUFFER;
+ git_str input_buf = GIT_STR_INIT;
+ gss_name_t server = NULL;
+ gss_OID mech;
+ size_t challenge_len;
+ int error = 0;
+
+ GIT_ASSERT_ARG(buf);
+ GIT_ASSERT_ARG(ctx);
+ GIT_ASSERT_ARG(cred);
+
+ GIT_ASSERT(ctx->configured);
+ GIT_ASSERT(cred->credtype == GIT_CREDENTIAL_DEFAULT);
+
+ if (ctx->complete)
+ return 0;
+
+ target_buffer.value = (void *)ctx->target.ptr;
+ target_buffer.length = ctx->target.size;
+
+ status_major = gss_import_name(&status_minor, &target_buffer,
+ GSS_C_NT_HOSTBASED_SERVICE, &server);
+
+ if (GSS_ERROR(status_major)) {
+ gssapi_err_set(status_major, status_minor,
+ "could not parse principal");
+ error = -1;
+ goto done;
+ }
+
+ challenge_len = ctx->challenge ? strlen(ctx->challenge) : 0;
+
+ if (challenge_len < 9 || memcmp(ctx->challenge, "Negotiate", 9) != 0) {
+ git_error_set(GIT_ERROR_NET, "server did not request negotiate");
+ error = -1;
+ goto done;
+ }
+
+ if (challenge_len > 9) {
+ if (git_str_decode_base64(&input_buf,
+ ctx->challenge + 10, challenge_len - 10) < 0) {
+ git_error_set(GIT_ERROR_NET, "invalid negotiate challenge from server");
+ error = -1;
+ goto done;
+ }
+
+ input_token.value = input_buf.ptr;
+ input_token.length = input_buf.size;
+ input_token_ptr = &input_token;
+ } else if (ctx->gss_context != GSS_C_NO_CONTEXT) {
+ gssapi_context_dispose(ctx);
+ }
+
+ mech = &gssapi_oid_spnego;
+
+ status_major = gss_init_sec_context(
+ &status_minor,
+ GSS_C_NO_CREDENTIAL,
+ &ctx->gss_context,
+ server,
+ mech,
+ GSS_C_DELEG_FLAG | GSS_C_MUTUAL_FLAG,
+ GSS_C_INDEFINITE,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ input_token_ptr,
+ NULL,
+ &output_token,
+ NULL,
+ NULL);
+
+ if (GSS_ERROR(status_major)) {
+ gssapi_err_set(status_major, status_minor, "negotiate failure");
+ error = -1;
+ goto done;
+ }
+
+ /* This message merely told us auth was complete; we do not respond. */
+ if (status_major == GSS_S_COMPLETE) {
+ gssapi_context_dispose(ctx);
+ ctx->complete = 1;
+ goto done;
+ }
+
+ if (output_token.length == 0) {
+ git_error_set(GIT_ERROR_NET, "GSSAPI did not return token");
+ error = -1;
+ goto done;
+ }
+
+ git_str_puts(buf, "Negotiate ");
+ git_str_encode_base64(buf, output_token.value, output_token.length);
+
+ if (git_str_oom(buf))
+ error = -1;
+
+done:
+ gss_release_name(&status_minor, &server);
+ gss_release_buffer(&status_minor, (gss_buffer_t) &output_token);
+ git_str_dispose(&input_buf);
+ return error;
+}
+
+static int gssapi_is_complete(git_http_auth_context *c)
+{
+ http_auth_gssapi_context *ctx = (http_auth_gssapi_context *)c;
+
+ GIT_ASSERT_ARG(ctx);
+
+ return (ctx->complete == 1);
+}
+
+static void gssapi_context_free(git_http_auth_context *c)
+{
+ http_auth_gssapi_context *ctx = (http_auth_gssapi_context *)c;
+
+ gssapi_context_dispose(ctx);
+
+ ctx->configured = 0;
+ ctx->complete = 0;
+ ctx->oid = NULL;
+
+ git__free(ctx);
+}
+
+static int gssapi_init_context(
+ http_auth_gssapi_context *ctx,
+ const git_net_url *url)
+{
+ OM_uint32 status_major, status_minor;
+ gss_OID item, *oid;
+ gss_OID_set mechanism_list;
+ size_t i;
+
+ /* Query supported mechanisms looking for SPNEGO) */
+ status_major = gss_indicate_mechs(&status_minor, &mechanism_list);
+
+ if (GSS_ERROR(status_major)) {
+ gssapi_err_set(status_major, status_minor,
+ "could not query mechanisms");
+ return -1;
+ }
+
+ if (mechanism_list) {
+ for (oid = gssapi_oids; *oid; oid++) {
+ for (i = 0; i < mechanism_list->count; i++) {
+ item = &mechanism_list->elements[i];
+
+ if (item->length == (*oid)->length &&
+ memcmp(item->elements, (*oid)->elements, item->length) == 0) {
+ ctx->oid = *oid;
+ break;
+ }
+
+ }
+
+ if (ctx->oid)
+ break;
+ }
+ }
+
+ gss_release_oid_set(&status_minor, &mechanism_list);
+
+ if (!ctx->oid) {
+ git_error_set(GIT_ERROR_NET, "negotiate authentication is not supported");
+ return GIT_EAUTH;
+ }
+
+ git_str_puts(&ctx->target, "HTTP@");
+ git_str_puts(&ctx->target, url->host);
+
+ if (git_str_oom(&ctx->target))
+ return -1;
+
+ ctx->gss_context = GSS_C_NO_CONTEXT;
+ ctx->configured = 1;
+
+ return 0;
+}
+
+int git_http_auth_negotiate(
+ git_http_auth_context **out,
+ const git_net_url *url)
+{
+ http_auth_gssapi_context *ctx;
+
+ *out = NULL;
+
+ ctx = git__calloc(1, sizeof(http_auth_gssapi_context));
+ GIT_ERROR_CHECK_ALLOC(ctx);
+
+ if (gssapi_init_context(ctx, url) < 0) {
+ git__free(ctx);
+ return -1;
+ }
+
+ ctx->parent.type = GIT_HTTP_AUTH_NEGOTIATE;
+ ctx->parent.credtypes = GIT_CREDENTIAL_DEFAULT;
+ ctx->parent.connection_affinity = 1;
+ ctx->parent.set_challenge = gssapi_set_challenge;
+ ctx->parent.next_token = gssapi_next_token;
+ ctx->parent.is_complete = gssapi_is_complete;
+ ctx->parent.free = gssapi_context_free;
+
+ *out = (git_http_auth_context *)ctx;
+
+ return 0;
+}
+
+#endif /* GIT_GSSAPI */
+
diff --git a/src/libgit2/transports/auth_negotiate.h b/src/libgit2/transports/auth_negotiate.h
new file mode 100644
index 0000000..4360785
--- /dev/null
+++ b/src/libgit2/transports/auth_negotiate.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_transports_auth_negotiate_h__
+#define INCLUDE_transports_auth_negotiate_h__
+
+#include "common.h"
+#include "git2.h"
+#include "auth.h"
+
+#if defined(GIT_GSSAPI) || defined(GIT_GSSFRAMEWORK) || defined(GIT_WIN32)
+
+extern int git_http_auth_negotiate(
+ git_http_auth_context **out,
+ const git_net_url *url);
+
+#else
+
+#define git_http_auth_negotiate git_http_auth_dummy
+
+#endif /* GIT_GSSAPI */
+
+#endif
diff --git a/src/libgit2/transports/auth_ntlm.h b/src/libgit2/transports/auth_ntlm.h
new file mode 100644
index 0000000..33406ae
--- /dev/null
+++ b/src/libgit2/transports/auth_ntlm.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_transports_auth_ntlm_h__
+#define INCLUDE_transports_auth_ntlm_h__
+
+#include "auth.h"
+
+/* NTLM requires a full request/challenge/response */
+#define GIT_AUTH_STEPS_NTLM 2
+
+#if defined(GIT_NTLM) || defined(GIT_WIN32)
+
+#if defined(GIT_OPENSSL)
+# define CRYPT_OPENSSL
+#elif defined(GIT_MBEDTLS)
+# define CRYPT_MBEDTLS
+#elif defined(GIT_SECURE_TRANSPORT)
+# define CRYPT_COMMONCRYPTO
+#endif
+
+extern int git_http_auth_ntlm(
+ git_http_auth_context **out,
+ const git_net_url *url);
+
+#else
+
+#define git_http_auth_ntlm git_http_auth_dummy
+
+#endif /* GIT_NTLM */
+
+#endif
+
diff --git a/src/libgit2/transports/auth_ntlmclient.c b/src/libgit2/transports/auth_ntlmclient.c
new file mode 100644
index 0000000..6f26a61
--- /dev/null
+++ b/src/libgit2/transports/auth_ntlmclient.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "auth_ntlm.h"
+
+#include "common.h"
+#include "str.h"
+#include "auth.h"
+#include "git2/sys/credential.h"
+
+#ifdef GIT_NTLM
+
+#include "ntlmclient.h"
+
+typedef struct {
+ git_http_auth_context parent;
+ ntlm_client *ntlm;
+ char *challenge;
+ bool complete;
+} http_auth_ntlm_context;
+
+static int ntlmclient_set_challenge(
+ git_http_auth_context *c,
+ const char *challenge)
+{
+ http_auth_ntlm_context *ctx = (http_auth_ntlm_context *)c;
+
+ GIT_ASSERT_ARG(ctx);
+ GIT_ASSERT_ARG(challenge);
+
+ git__free(ctx->challenge);
+
+ ctx->challenge = git__strdup(challenge);
+ GIT_ERROR_CHECK_ALLOC(ctx->challenge);
+
+ return 0;
+}
+
+static int ntlmclient_set_credentials(http_auth_ntlm_context *ctx, git_credential *_cred)
+{
+ git_credential_userpass_plaintext *cred;
+ const char *sep, *username;
+ char *domain = NULL, *domainuser = NULL;
+ int error = 0;
+
+ GIT_ASSERT(_cred->credtype == GIT_CREDENTIAL_USERPASS_PLAINTEXT);
+ cred = (git_credential_userpass_plaintext *)_cred;
+
+ if ((sep = strchr(cred->username, '\\')) != NULL) {
+ domain = git__strndup(cred->username, (sep - cred->username));
+ GIT_ERROR_CHECK_ALLOC(domain);
+
+ domainuser = git__strdup(sep + 1);
+ GIT_ERROR_CHECK_ALLOC(domainuser);
+
+ username = domainuser;
+ } else {
+ username = cred->username;
+ }
+
+ if (ntlm_client_set_credentials(ctx->ntlm,
+ username, domain, cred->password) < 0) {
+ git_error_set(GIT_ERROR_NET, "could not set credentials: %s",
+ ntlm_client_errmsg(ctx->ntlm));
+ error = -1;
+ goto done;
+ }
+
+done:
+ git__free(domain);
+ git__free(domainuser);
+ return error;
+}
+
+static int ntlmclient_next_token(
+ git_str *buf,
+ git_http_auth_context *c,
+ git_credential *cred)
+{
+ http_auth_ntlm_context *ctx = (http_auth_ntlm_context *)c;
+ git_str input_buf = GIT_STR_INIT;
+ const unsigned char *msg;
+ size_t challenge_len, msg_len;
+ int error = GIT_EAUTH;
+
+ GIT_ASSERT_ARG(buf);
+ GIT_ASSERT_ARG(ctx);
+
+ GIT_ASSERT(ctx->ntlm);
+
+ challenge_len = ctx->challenge ? strlen(ctx->challenge) : 0;
+
+ if (ctx->complete)
+ ntlm_client_reset(ctx->ntlm);
+
+ /*
+ * Set us complete now since it's the default case; the one
+ * incomplete case (successfully created a client request)
+ * will explicitly set that it requires a second step.
+ */
+ ctx->complete = true;
+
+ if (cred && ntlmclient_set_credentials(ctx, cred) != 0)
+ goto done;
+
+ if (challenge_len < 4) {
+ git_error_set(GIT_ERROR_NET, "no ntlm challenge sent from server");
+ goto done;
+ } else if (challenge_len == 4) {
+ if (memcmp(ctx->challenge, "NTLM", 4) != 0) {
+ git_error_set(GIT_ERROR_NET, "server did not request NTLM");
+ goto done;
+ }
+
+ if (ntlm_client_negotiate(&msg, &msg_len, ctx->ntlm) != 0) {
+ git_error_set(GIT_ERROR_NET, "ntlm authentication failed: %s",
+ ntlm_client_errmsg(ctx->ntlm));
+ goto done;
+ }
+
+ ctx->complete = false;
+ } else {
+ if (memcmp(ctx->challenge, "NTLM ", 5) != 0) {
+ git_error_set(GIT_ERROR_NET, "challenge from server was not NTLM");
+ goto done;
+ }
+
+ if (git_str_decode_base64(&input_buf,
+ ctx->challenge + 5, challenge_len - 5) < 0) {
+ git_error_set(GIT_ERROR_NET, "invalid NTLM challenge from server");
+ goto done;
+ }
+
+ if (ntlm_client_set_challenge(ctx->ntlm,
+ (const unsigned char *)input_buf.ptr, input_buf.size) != 0) {
+ git_error_set(GIT_ERROR_NET, "ntlm challenge failed: %s",
+ ntlm_client_errmsg(ctx->ntlm));
+ goto done;
+ }
+
+ if (ntlm_client_response(&msg, &msg_len, ctx->ntlm) != 0) {
+ git_error_set(GIT_ERROR_NET, "ntlm authentication failed: %s",
+ ntlm_client_errmsg(ctx->ntlm));
+ goto done;
+ }
+ }
+
+ git_str_puts(buf, "NTLM ");
+ git_str_encode_base64(buf, (const char *)msg, msg_len);
+
+ if (git_str_oom(buf))
+ goto done;
+
+ error = 0;
+
+done:
+ git_str_dispose(&input_buf);
+ return error;
+}
+
+static int ntlmclient_is_complete(git_http_auth_context *c)
+{
+ http_auth_ntlm_context *ctx = (http_auth_ntlm_context *)c;
+
+ GIT_ASSERT_ARG(ctx);
+ return (ctx->complete == true);
+}
+
+static void ntlmclient_context_free(git_http_auth_context *c)
+{
+ http_auth_ntlm_context *ctx = (http_auth_ntlm_context *)c;
+
+ ntlm_client_free(ctx->ntlm);
+ git__free(ctx->challenge);
+ git__free(ctx);
+}
+
+static int ntlmclient_init_context(
+ http_auth_ntlm_context *ctx,
+ const git_net_url *url)
+{
+ GIT_UNUSED(url);
+
+ if ((ctx->ntlm = ntlm_client_init(NTLM_CLIENT_DEFAULTS)) == NULL) {
+ git_error_set_oom();
+ return -1;
+ }
+
+ return 0;
+}
+
+int git_http_auth_ntlm(
+ git_http_auth_context **out,
+ const git_net_url *url)
+{
+ http_auth_ntlm_context *ctx;
+
+ GIT_UNUSED(url);
+
+ *out = NULL;
+
+ ctx = git__calloc(1, sizeof(http_auth_ntlm_context));
+ GIT_ERROR_CHECK_ALLOC(ctx);
+
+ if (ntlmclient_init_context(ctx, url) < 0) {
+ git__free(ctx);
+ return -1;
+ }
+
+ ctx->parent.type = GIT_HTTP_AUTH_NTLM;
+ ctx->parent.credtypes = GIT_CREDENTIAL_USERPASS_PLAINTEXT;
+ ctx->parent.connection_affinity = 1;
+ ctx->parent.set_challenge = ntlmclient_set_challenge;
+ ctx->parent.next_token = ntlmclient_next_token;
+ ctx->parent.is_complete = ntlmclient_is_complete;
+ ctx->parent.free = ntlmclient_context_free;
+
+ *out = (git_http_auth_context *)ctx;
+
+ return 0;
+}
+
+#endif /* GIT_NTLM */
diff --git a/src/libgit2/transports/auth_sspi.c b/src/libgit2/transports/auth_sspi.c
new file mode 100644
index 0000000..f826936
--- /dev/null
+++ b/src/libgit2/transports/auth_sspi.c
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "auth_ntlm.h"
+#include "auth_negotiate.h"
+
+#ifdef GIT_WIN32
+
+#define SECURITY_WIN32
+
+#include "git2.h"
+#include "auth.h"
+#include "git2/sys/credential.h"
+
+#include <windows.h>
+#include <security.h>
+
+typedef struct {
+ git_http_auth_context parent;
+ wchar_t *target;
+
+ const char *package_name;
+ size_t package_name_len;
+ wchar_t *package_name_w;
+ SecPkgInfoW *package_info;
+ SEC_WINNT_AUTH_IDENTITY_W identity;
+ CredHandle cred;
+ CtxtHandle context;
+
+ int has_identity : 1,
+ has_credentials : 1,
+ has_context : 1,
+ complete : 1;
+ git_str challenge;
+} http_auth_sspi_context;
+
+static void sspi_reset_context(http_auth_sspi_context *ctx)
+{
+ if (ctx->has_identity) {
+ git__free(ctx->identity.User);
+ git__free(ctx->identity.Domain);
+ git__free(ctx->identity.Password);
+
+ memset(&ctx->identity, 0, sizeof(SEC_WINNT_AUTH_IDENTITY_W));
+
+ ctx->has_identity = 0;
+ }
+
+ if (ctx->has_credentials) {
+ FreeCredentialsHandle(&ctx->cred);
+ memset(&ctx->cred, 0, sizeof(CredHandle));
+
+ ctx->has_credentials = 0;
+ }
+
+ if (ctx->has_context) {
+ DeleteSecurityContext(&ctx->context);
+ memset(&ctx->context, 0, sizeof(CtxtHandle));
+
+ ctx->has_context = 0;
+ }
+
+ ctx->complete = 0;
+
+ git_str_dispose(&ctx->challenge);
+}
+
+static int sspi_set_challenge(
+ git_http_auth_context *c,
+ const char *challenge)
+{
+ http_auth_sspi_context *ctx = (http_auth_sspi_context *)c;
+ size_t challenge_len = strlen(challenge);
+
+ git_str_clear(&ctx->challenge);
+
+ if (strncmp(challenge, ctx->package_name, ctx->package_name_len) != 0) {
+ git_error_set(GIT_ERROR_NET, "invalid %s challenge from server", ctx->package_name);
+ return -1;
+ }
+
+ /*
+ * A package type indicator without a base64 payload indicates the
+ * mechanism; it's not an actual challenge. Ignore it.
+ */
+ if (challenge[ctx->package_name_len] == 0) {
+ return 0;
+ } else if (challenge[ctx->package_name_len] != ' ') {
+ git_error_set(GIT_ERROR_NET, "invalid %s challenge from server", ctx->package_name);
+ return -1;
+ }
+
+ if (git_str_decode_base64(&ctx->challenge,
+ challenge + (ctx->package_name_len + 1),
+ challenge_len - (ctx->package_name_len + 1)) < 0) {
+ git_error_set(GIT_ERROR_NET, "invalid %s challenge from server", ctx->package_name);
+ return -1;
+ }
+
+ GIT_ASSERT(ctx->challenge.size <= ULONG_MAX);
+ return 0;
+}
+
+static int create_identity(
+ SEC_WINNT_AUTH_IDENTITY_W **out,
+ http_auth_sspi_context *ctx,
+ git_credential *cred)
+{
+ git_credential_userpass_plaintext *userpass;
+ wchar_t *username = NULL, *domain = NULL, *password = NULL;
+ int username_len = 0, domain_len = 0, password_len = 0;
+ const char *sep;
+
+ if (cred->credtype == GIT_CREDENTIAL_DEFAULT) {
+ *out = NULL;
+ return 0;
+ }
+
+ if (cred->credtype != GIT_CREDENTIAL_USERPASS_PLAINTEXT) {
+ git_error_set(GIT_ERROR_NET, "unknown credential type: %d", cred->credtype);
+ return -1;
+ }
+
+ userpass = (git_credential_userpass_plaintext *)cred;
+
+ if ((sep = strchr(userpass->username, '\\')) != NULL) {
+ GIT_ASSERT(sep - userpass->username < INT_MAX);
+
+ username_len = git_utf8_to_16_alloc(&username, sep + 1);
+ domain_len = git_utf8_to_16_alloc_with_len(&domain,
+ userpass->username, (int)(sep - userpass->username));
+ } else {
+ username_len = git_utf8_to_16_alloc(&username,
+ userpass->username);
+ }
+
+ password_len = git_utf8_to_16_alloc(&password, userpass->password);
+
+ if (username_len < 0 || domain_len < 0 || password_len < 0) {
+ git__free(username);
+ git__free(domain);
+ git__free(password);
+ return -1;
+ }
+
+ ctx->identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
+ ctx->identity.User = username;
+ ctx->identity.UserLength = (unsigned long)username_len;
+ ctx->identity.Password = password;
+ ctx->identity.PasswordLength = (unsigned long)password_len;
+ ctx->identity.Domain = domain;
+ ctx->identity.DomainLength = (unsigned long)domain_len;
+
+ ctx->has_identity = 1;
+
+ *out = &ctx->identity;
+
+ return 0;
+}
+
+static int sspi_next_token(
+ git_str *buf,
+ git_http_auth_context *c,
+ git_credential *cred)
+{
+ http_auth_sspi_context *ctx = (http_auth_sspi_context *)c;
+ SEC_WINNT_AUTH_IDENTITY_W *identity = NULL;
+ TimeStamp timestamp;
+ DWORD context_flags;
+ SecBuffer input_buf = { 0, SECBUFFER_TOKEN, NULL };
+ SecBuffer output_buf = { 0, SECBUFFER_TOKEN, NULL };
+ SecBufferDesc input_buf_desc = { SECBUFFER_VERSION, 1, &input_buf };
+ SecBufferDesc output_buf_desc = { SECBUFFER_VERSION, 1, &output_buf };
+ SECURITY_STATUS status;
+
+ if (ctx->complete)
+ sspi_reset_context(ctx);
+
+ if (!ctx->has_context) {
+ if (create_identity(&identity, ctx, cred) < 0)
+ return -1;
+
+ status = AcquireCredentialsHandleW(NULL, ctx->package_name_w,
+ SECPKG_CRED_BOTH, NULL, identity, NULL,
+ NULL, &ctx->cred, &timestamp);
+
+ if (status != SEC_E_OK) {
+ git_error_set(GIT_ERROR_OS, "could not acquire credentials");
+ return -1;
+ }
+
+ ctx->has_credentials = 1;
+ }
+
+ context_flags = ISC_REQ_ALLOCATE_MEMORY |
+ ISC_REQ_CONFIDENTIALITY |
+ ISC_REQ_MUTUAL_AUTH;
+
+ if (ctx->challenge.size > 0) {
+ input_buf.BufferType = SECBUFFER_TOKEN;
+ input_buf.cbBuffer = (unsigned long)ctx->challenge.size;
+ input_buf.pvBuffer = ctx->challenge.ptr;
+ }
+
+ status = InitializeSecurityContextW(&ctx->cred,
+ ctx->has_context ? &ctx->context : NULL,
+ ctx->target,
+ context_flags,
+ 0,
+ SECURITY_NETWORK_DREP,
+ ctx->has_context ? &input_buf_desc : NULL,
+ 0,
+ ctx->has_context ? NULL : &ctx->context,
+ &output_buf_desc,
+ &context_flags,
+ NULL);
+
+ if (status == SEC_I_COMPLETE_AND_CONTINUE ||
+ status == SEC_I_COMPLETE_NEEDED)
+ status = CompleteAuthToken(&ctx->context, &output_buf_desc);
+
+ if (status == SEC_E_OK) {
+ ctx->complete = 1;
+ } else if (status != SEC_I_CONTINUE_NEEDED) {
+ git_error_set(GIT_ERROR_OS, "could not initialize security context");
+ return -1;
+ }
+
+ ctx->has_context = 1;
+ git_str_clear(&ctx->challenge);
+
+ if (output_buf.cbBuffer > 0) {
+ git_str_put(buf, ctx->package_name, ctx->package_name_len);
+ git_str_putc(buf, ' ');
+ git_str_encode_base64(buf, output_buf.pvBuffer, output_buf.cbBuffer);
+
+ FreeContextBuffer(output_buf.pvBuffer);
+
+ if (git_str_oom(buf))
+ return -1;
+ }
+
+ return 0;
+}
+
+static int sspi_is_complete(git_http_auth_context *c)
+{
+ http_auth_sspi_context *ctx = (http_auth_sspi_context *)c;
+
+ return ctx->complete;
+}
+
+static void sspi_context_free(git_http_auth_context *c)
+{
+ http_auth_sspi_context *ctx = (http_auth_sspi_context *)c;
+
+ sspi_reset_context(ctx);
+
+ FreeContextBuffer(ctx->package_info);
+ git__free(ctx->target);
+ git__free(ctx);
+}
+
+static int sspi_init_context(
+ git_http_auth_context **out,
+ git_http_auth_t type,
+ const git_net_url *url)
+{
+ http_auth_sspi_context *ctx;
+ git_str target = GIT_STR_INIT;
+
+ *out = NULL;
+
+ ctx = git__calloc(1, sizeof(http_auth_sspi_context));
+ GIT_ERROR_CHECK_ALLOC(ctx);
+
+ switch (type) {
+ case GIT_HTTP_AUTH_NTLM:
+ ctx->package_name = "NTLM";
+ ctx->package_name_len = CONST_STRLEN("NTLM");
+ ctx->package_name_w = L"NTLM";
+ ctx->parent.credtypes = GIT_CREDENTIAL_USERPASS_PLAINTEXT |
+ GIT_CREDENTIAL_DEFAULT;
+ break;
+ case GIT_HTTP_AUTH_NEGOTIATE:
+ ctx->package_name = "Negotiate";
+ ctx->package_name_len = CONST_STRLEN("Negotiate");
+ ctx->package_name_w = L"Negotiate";
+ ctx->parent.credtypes = GIT_CREDENTIAL_DEFAULT;
+ break;
+ default:
+ git_error_set(GIT_ERROR_NET, "unknown SSPI auth type: %d", ctx->parent.type);
+ git__free(ctx);
+ return -1;
+ }
+
+ if (QuerySecurityPackageInfoW(ctx->package_name_w, &ctx->package_info) != SEC_E_OK) {
+ git_error_set(GIT_ERROR_OS, "could not query security package");
+ git__free(ctx);
+ return -1;
+ }
+
+ if (git_str_printf(&target, "http/%s", url->host) < 0 ||
+ git_utf8_to_16_alloc(&ctx->target, target.ptr) < 0) {
+ FreeContextBuffer(ctx->package_info);
+ git__free(ctx);
+ return -1;
+ }
+
+ ctx->parent.type = type;
+ ctx->parent.connection_affinity = 1;
+ ctx->parent.set_challenge = sspi_set_challenge;
+ ctx->parent.next_token = sspi_next_token;
+ ctx->parent.is_complete = sspi_is_complete;
+ ctx->parent.free = sspi_context_free;
+
+ *out = (git_http_auth_context *)ctx;
+
+ git_str_dispose(&target);
+ return 0;
+}
+
+int git_http_auth_negotiate(
+ git_http_auth_context **out,
+ const git_net_url *url)
+{
+ return sspi_init_context(out, GIT_HTTP_AUTH_NEGOTIATE, url);
+}
+
+int git_http_auth_ntlm(
+ git_http_auth_context **out,
+ const git_net_url *url)
+{
+ return sspi_init_context(out, GIT_HTTP_AUTH_NTLM, url);
+}
+
+#endif /* GIT_WIN32 */
diff --git a/src/libgit2/transports/credential.c b/src/libgit2/transports/credential.c
new file mode 100644
index 0000000..6e00b02
--- /dev/null
+++ b/src/libgit2/transports/credential.c
@@ -0,0 +1,486 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+
+#include "git2/credential.h"
+#include "git2/sys/credential.h"
+#include "git2/credential_helpers.h"
+
+static int git_credential_ssh_key_type_new(
+ git_credential **cred,
+ const char *username,
+ const char *publickey,
+ const char *privatekey,
+ const char *passphrase,
+ git_credential_t credtype);
+
+int git_credential_has_username(git_credential *cred)
+{
+ if (cred->credtype == GIT_CREDENTIAL_DEFAULT)
+ return 0;
+
+ return 1;
+}
+
+const char *git_credential_get_username(git_credential *cred)
+{
+ switch (cred->credtype) {
+ case GIT_CREDENTIAL_USERNAME:
+ {
+ git_credential_username *c = (git_credential_username *) cred;
+ return c->username;
+ }
+ case GIT_CREDENTIAL_USERPASS_PLAINTEXT:
+ {
+ git_credential_userpass_plaintext *c = (git_credential_userpass_plaintext *) cred;
+ return c->username;
+ }
+ case GIT_CREDENTIAL_SSH_KEY:
+ case GIT_CREDENTIAL_SSH_MEMORY:
+ {
+ git_credential_ssh_key *c = (git_credential_ssh_key *) cred;
+ return c->username;
+ }
+ case GIT_CREDENTIAL_SSH_CUSTOM:
+ {
+ git_credential_ssh_custom *c = (git_credential_ssh_custom *) cred;
+ return c->username;
+ }
+ case GIT_CREDENTIAL_SSH_INTERACTIVE:
+ {
+ git_credential_ssh_interactive *c = (git_credential_ssh_interactive *) cred;
+ return c->username;
+ }
+
+ default:
+ return NULL;
+ }
+}
+
+static void plaintext_free(struct git_credential *cred)
+{
+ git_credential_userpass_plaintext *c = (git_credential_userpass_plaintext *)cred;
+
+ git__free(c->username);
+
+ /* Zero the memory which previously held the password */
+ if (c->password) {
+ size_t pass_len = strlen(c->password);
+ git__memzero(c->password, pass_len);
+ git__free(c->password);
+ }
+
+ git__free(c);
+}
+
+int git_credential_userpass_plaintext_new(
+ git_credential **cred,
+ const char *username,
+ const char *password)
+{
+ git_credential_userpass_plaintext *c;
+
+ GIT_ASSERT_ARG(cred);
+ GIT_ASSERT_ARG(username);
+ GIT_ASSERT_ARG(password);
+
+ c = git__malloc(sizeof(git_credential_userpass_plaintext));
+ GIT_ERROR_CHECK_ALLOC(c);
+
+ c->parent.credtype = GIT_CREDENTIAL_USERPASS_PLAINTEXT;
+ c->parent.free = plaintext_free;
+ c->username = git__strdup(username);
+
+ if (!c->username) {
+ git__free(c);
+ return -1;
+ }
+
+ c->password = git__strdup(password);
+
+ if (!c->password) {
+ git__free(c->username);
+ git__free(c);
+ return -1;
+ }
+
+ *cred = &c->parent;
+ return 0;
+}
+
+static void ssh_key_free(struct git_credential *cred)
+{
+ git_credential_ssh_key *c =
+ (git_credential_ssh_key *)cred;
+
+ git__free(c->username);
+
+ if (c->privatekey) {
+ /* Zero the memory which previously held the private key */
+ size_t key_len = strlen(c->privatekey);
+ git__memzero(c->privatekey, key_len);
+ git__free(c->privatekey);
+ }
+
+ if (c->passphrase) {
+ /* Zero the memory which previously held the passphrase */
+ size_t pass_len = strlen(c->passphrase);
+ git__memzero(c->passphrase, pass_len);
+ git__free(c->passphrase);
+ }
+
+ if (c->publickey) {
+ /* Zero the memory which previously held the public key */
+ size_t key_len = strlen(c->publickey);
+ git__memzero(c->publickey, key_len);
+ git__free(c->publickey);
+ }
+
+ git__free(c);
+}
+
+static void ssh_interactive_free(struct git_credential *cred)
+{
+ git_credential_ssh_interactive *c = (git_credential_ssh_interactive *)cred;
+
+ git__free(c->username);
+
+ git__free(c);
+}
+
+static void ssh_custom_free(struct git_credential *cred)
+{
+ git_credential_ssh_custom *c = (git_credential_ssh_custom *)cred;
+
+ git__free(c->username);
+
+ if (c->publickey) {
+ /* Zero the memory which previously held the publickey */
+ size_t key_len = strlen(c->publickey);
+ git__memzero(c->publickey, key_len);
+ git__free(c->publickey);
+ }
+
+ git__free(c);
+}
+
+static void default_free(struct git_credential *cred)
+{
+ git_credential_default *c = (git_credential_default *)cred;
+
+ git__free(c);
+}
+
+static void username_free(struct git_credential *cred)
+{
+ git__free(cred);
+}
+
+int git_credential_ssh_key_new(
+ git_credential **cred,
+ const char *username,
+ const char *publickey,
+ const char *privatekey,
+ const char *passphrase)
+{
+ return git_credential_ssh_key_type_new(
+ cred,
+ username,
+ publickey,
+ privatekey,
+ passphrase,
+ GIT_CREDENTIAL_SSH_KEY);
+}
+
+int git_credential_ssh_key_memory_new(
+ git_credential **cred,
+ const char *username,
+ const char *publickey,
+ const char *privatekey,
+ const char *passphrase)
+{
+#ifdef GIT_SSH_MEMORY_CREDENTIALS
+ return git_credential_ssh_key_type_new(
+ cred,
+ username,
+ publickey,
+ privatekey,
+ passphrase,
+ GIT_CREDENTIAL_SSH_MEMORY);
+#else
+ GIT_UNUSED(cred);
+ GIT_UNUSED(username);
+ GIT_UNUSED(publickey);
+ GIT_UNUSED(privatekey);
+ GIT_UNUSED(passphrase);
+
+ git_error_set(GIT_ERROR_INVALID,
+ "this version of libgit2 was not built with ssh memory credentials.");
+ return -1;
+#endif
+}
+
+static int git_credential_ssh_key_type_new(
+ git_credential **cred,
+ const char *username,
+ const char *publickey,
+ const char *privatekey,
+ const char *passphrase,
+ git_credential_t credtype)
+{
+ git_credential_ssh_key *c;
+
+ GIT_ASSERT_ARG(username);
+ GIT_ASSERT_ARG(cred);
+ GIT_ASSERT_ARG(privatekey);
+
+ c = git__calloc(1, sizeof(git_credential_ssh_key));
+ GIT_ERROR_CHECK_ALLOC(c);
+
+ c->parent.credtype = credtype;
+ c->parent.free = ssh_key_free;
+
+ c->username = git__strdup(username);
+ GIT_ERROR_CHECK_ALLOC(c->username);
+
+ c->privatekey = git__strdup(privatekey);
+ GIT_ERROR_CHECK_ALLOC(c->privatekey);
+
+ if (publickey) {
+ c->publickey = git__strdup(publickey);
+ GIT_ERROR_CHECK_ALLOC(c->publickey);
+ }
+
+ if (passphrase) {
+ c->passphrase = git__strdup(passphrase);
+ GIT_ERROR_CHECK_ALLOC(c->passphrase);
+ }
+
+ *cred = &c->parent;
+ return 0;
+}
+
+int git_credential_ssh_interactive_new(
+ git_credential **out,
+ const char *username,
+ git_credential_ssh_interactive_cb prompt_callback,
+ void *payload)
+{
+ git_credential_ssh_interactive *c;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(username);
+ GIT_ASSERT_ARG(prompt_callback);
+
+ c = git__calloc(1, sizeof(git_credential_ssh_interactive));
+ GIT_ERROR_CHECK_ALLOC(c);
+
+ c->parent.credtype = GIT_CREDENTIAL_SSH_INTERACTIVE;
+ c->parent.free = ssh_interactive_free;
+
+ c->username = git__strdup(username);
+ GIT_ERROR_CHECK_ALLOC(c->username);
+
+ c->prompt_callback = prompt_callback;
+ c->payload = payload;
+
+ *out = &c->parent;
+ return 0;
+}
+
+int git_credential_ssh_key_from_agent(git_credential **cred, const char *username) {
+ git_credential_ssh_key *c;
+
+ GIT_ASSERT_ARG(username);
+ GIT_ASSERT_ARG(cred);
+
+ c = git__calloc(1, sizeof(git_credential_ssh_key));
+ GIT_ERROR_CHECK_ALLOC(c);
+
+ c->parent.credtype = GIT_CREDENTIAL_SSH_KEY;
+ c->parent.free = ssh_key_free;
+
+ c->username = git__strdup(username);
+ GIT_ERROR_CHECK_ALLOC(c->username);
+
+ c->privatekey = NULL;
+
+ *cred = &c->parent;
+ return 0;
+}
+
+int git_credential_ssh_custom_new(
+ git_credential **cred,
+ const char *username,
+ const char *publickey,
+ size_t publickey_len,
+ git_credential_sign_cb sign_callback,
+ void *payload)
+{
+ git_credential_ssh_custom *c;
+
+ GIT_ASSERT_ARG(username);
+ GIT_ASSERT_ARG(cred);
+
+ c = git__calloc(1, sizeof(git_credential_ssh_custom));
+ GIT_ERROR_CHECK_ALLOC(c);
+
+ c->parent.credtype = GIT_CREDENTIAL_SSH_CUSTOM;
+ c->parent.free = ssh_custom_free;
+
+ c->username = git__strdup(username);
+ GIT_ERROR_CHECK_ALLOC(c->username);
+
+ if (publickey_len > 0) {
+ c->publickey = git__malloc(publickey_len);
+ GIT_ERROR_CHECK_ALLOC(c->publickey);
+
+ memcpy(c->publickey, publickey, publickey_len);
+ }
+
+ c->publickey_len = publickey_len;
+ c->sign_callback = sign_callback;
+ c->payload = payload;
+
+ *cred = &c->parent;
+ return 0;
+}
+
+int git_credential_default_new(git_credential **cred)
+{
+ git_credential_default *c;
+
+ GIT_ASSERT_ARG(cred);
+
+ c = git__calloc(1, sizeof(git_credential_default));
+ GIT_ERROR_CHECK_ALLOC(c);
+
+ c->credtype = GIT_CREDENTIAL_DEFAULT;
+ c->free = default_free;
+
+ *cred = c;
+ return 0;
+}
+
+int git_credential_username_new(git_credential **cred, const char *username)
+{
+ git_credential_username *c;
+ size_t len, allocsize;
+
+ GIT_ASSERT_ARG(cred);
+
+ len = strlen(username);
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&allocsize, sizeof(git_credential_username), len);
+ GIT_ERROR_CHECK_ALLOC_ADD(&allocsize, allocsize, 1);
+ c = git__malloc(allocsize);
+ GIT_ERROR_CHECK_ALLOC(c);
+
+ c->parent.credtype = GIT_CREDENTIAL_USERNAME;
+ c->parent.free = username_free;
+ memcpy(c->username, username, len + 1);
+
+ *cred = (git_credential *) c;
+ return 0;
+}
+
+void git_credential_free(git_credential *cred)
+{
+ if (!cred)
+ return;
+
+ cred->free(cred);
+}
+
+/* Deprecated credential functions */
+
+#ifndef GIT_DEPRECATE_HARD
+int git_cred_has_username(git_credential *cred)
+{
+ return git_credential_has_username(cred);
+}
+
+const char *git_cred_get_username(git_credential *cred)
+{
+ return git_credential_get_username(cred);
+}
+
+int git_cred_userpass_plaintext_new(
+ git_credential **out,
+ const char *username,
+ const char *password)
+{
+ return git_credential_userpass_plaintext_new(out,username, password);
+}
+
+int git_cred_default_new(git_credential **out)
+{
+ return git_credential_default_new(out);
+}
+
+int git_cred_username_new(git_credential **out, const char *username)
+{
+ return git_credential_username_new(out, username);
+}
+
+int git_cred_ssh_key_new(
+ git_credential **out,
+ const char *username,
+ const char *publickey,
+ const char *privatekey,
+ const char *passphrase)
+{
+ return git_credential_ssh_key_new(out, username,
+ publickey, privatekey, passphrase);
+}
+
+int git_cred_ssh_key_memory_new(
+ git_credential **out,
+ const char *username,
+ const char *publickey,
+ const char *privatekey,
+ const char *passphrase)
+{
+ return git_credential_ssh_key_memory_new(out, username,
+ publickey, privatekey, passphrase);
+}
+
+int git_cred_ssh_interactive_new(
+ git_credential **out,
+ const char *username,
+ git_credential_ssh_interactive_cb prompt_callback,
+ void *payload)
+{
+ return git_credential_ssh_interactive_new(out, username,
+ prompt_callback, payload);
+}
+
+int git_cred_ssh_key_from_agent(
+ git_credential **out,
+ const char *username)
+{
+ return git_credential_ssh_key_from_agent(out, username);
+}
+
+int git_cred_ssh_custom_new(
+ git_credential **out,
+ const char *username,
+ const char *publickey,
+ size_t publickey_len,
+ git_credential_sign_cb sign_callback,
+ void *payload)
+{
+ return git_credential_ssh_custom_new(out, username,
+ publickey, publickey_len, sign_callback, payload);
+}
+
+void git_cred_free(git_credential *cred)
+{
+ git_credential_free(cred);
+}
+#endif
diff --git a/src/libgit2/transports/credential_helpers.c b/src/libgit2/transports/credential_helpers.c
new file mode 100644
index 0000000..6d34a4e
--- /dev/null
+++ b/src/libgit2/transports/credential_helpers.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+
+#include "git2/credential_helpers.h"
+
+int git_credential_userpass(
+ git_credential **cred,
+ const char *url,
+ const char *user_from_url,
+ unsigned int allowed_types,
+ void *payload)
+{
+ git_credential_userpass_payload *userpass = (git_credential_userpass_payload*)payload;
+ const char *effective_username = NULL;
+
+ GIT_UNUSED(url);
+
+ if (!userpass || !userpass->password) return -1;
+
+ /* Username resolution: a username can be passed with the URL, the
+ * credentials payload, or both. Here's what we do. Note that if we get
+ * this far, we know that any password the url may contain has already
+ * failed at least once, so we ignore it.
+ *
+ * | Payload | URL | Used |
+ * +-------------+----------+-----------+
+ * | yes | no | payload |
+ * | yes | yes | payload |
+ * | no | yes | url |
+ * | no | no | FAIL |
+ */
+ if (userpass->username)
+ effective_username = userpass->username;
+ else if (user_from_url)
+ effective_username = user_from_url;
+ else
+ return -1;
+
+ if (GIT_CREDENTIAL_USERNAME & allowed_types)
+ return git_credential_username_new(cred, effective_username);
+
+ if ((GIT_CREDENTIAL_USERPASS_PLAINTEXT & allowed_types) == 0 ||
+ git_credential_userpass_plaintext_new(cred, effective_username, userpass->password) < 0)
+ return -1;
+
+ return 0;
+}
+
+/* Deprecated credential functions */
+
+#ifndef GIT_DEPRECATE_HARD
+int git_cred_userpass(
+ git_credential **out,
+ const char *url,
+ const char *user_from_url,
+ unsigned int allowed_types,
+ void *payload)
+{
+ return git_credential_userpass(out, url, user_from_url,
+ allowed_types, payload);
+}
+#endif
diff --git a/src/libgit2/transports/git.c b/src/libgit2/transports/git.c
new file mode 100644
index 0000000..53611f2
--- /dev/null
+++ b/src/libgit2/transports/git.c
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+
+#include "net.h"
+#include "stream.h"
+#include "streams/socket.h"
+#include "git2/sys/transport.h"
+
+#define OWNING_SUBTRANSPORT(s) ((git_subtransport *)(s)->parent.subtransport)
+
+static const char prefix_git[] = "git://";
+static const char cmd_uploadpack[] = "git-upload-pack";
+static const char cmd_receivepack[] = "git-receive-pack";
+
+typedef struct {
+ git_smart_subtransport_stream parent;
+ git_stream *io;
+ const char *cmd;
+ char *url;
+ unsigned sent_command : 1;
+} git_proto_stream;
+
+typedef struct {
+ git_smart_subtransport parent;
+ git_transport *owner;
+ git_proto_stream *current_stream;
+} git_subtransport;
+
+/*
+ * Create a git protocol request.
+ *
+ * For example: 0035git-upload-pack /libgit2/libgit2\0host=github.com\0
+ */
+static int gen_proto(git_str *request, const char *cmd, const char *url)
+{
+ char *delim, *repo;
+ char host[] = "host=";
+ size_t len;
+
+ delim = strchr(url, '/');
+ if (delim == NULL) {
+ git_error_set(GIT_ERROR_NET, "malformed URL");
+ return -1;
+ }
+
+ repo = delim;
+ if (repo[1] == '~')
+ ++repo;
+
+ delim = strchr(url, ':');
+ if (delim == NULL)
+ delim = strchr(url, '/');
+
+ len = 4 + strlen(cmd) + 1 + strlen(repo) + 1 + strlen(host) + (delim - url) + 1;
+
+ git_str_grow(request, len);
+ git_str_printf(request, "%04x%s %s%c%s",
+ (unsigned int)(len & 0x0FFFF), cmd, repo, 0, host);
+ git_str_put(request, url, delim - url);
+ git_str_putc(request, '\0');
+
+ if (git_str_oom(request))
+ return -1;
+
+ return 0;
+}
+
+static int send_command(git_proto_stream *s)
+{
+ git_str request = GIT_STR_INIT;
+ int error;
+
+ if ((error = gen_proto(&request, s->cmd, s->url)) < 0)
+ goto cleanup;
+
+ if ((error = git_stream__write_full(s->io, request.ptr, request.size, 0)) < 0)
+ goto cleanup;
+
+ s->sent_command = 1;
+
+cleanup:
+ git_str_dispose(&request);
+ return error;
+}
+
+static int git_proto_stream_read(
+ git_smart_subtransport_stream *stream,
+ char *buffer,
+ size_t buf_size,
+ size_t *bytes_read)
+{
+ git_proto_stream *s = (git_proto_stream *)stream;
+ ssize_t ret;
+ int error;
+
+ *bytes_read = 0;
+
+ if (!s->sent_command && (error = send_command(s)) < 0)
+ return error;
+
+ ret = git_stream_read(s->io, buffer, min(buf_size, INT_MAX));
+
+ if (ret < 0)
+ return -1;
+
+ *bytes_read = (size_t)ret;
+ return 0;
+}
+
+static int git_proto_stream_write(
+ git_smart_subtransport_stream *stream,
+ const char *buffer,
+ size_t len)
+{
+ git_proto_stream *s = (git_proto_stream *)stream;
+ int error;
+
+ if (!s->sent_command && (error = send_command(s)) < 0)
+ return error;
+
+ return git_stream__write_full(s->io, buffer, len, 0);
+}
+
+static void git_proto_stream_free(git_smart_subtransport_stream *stream)
+{
+ git_proto_stream *s;
+ git_subtransport *t;
+
+ if (!stream)
+ return;
+
+ s = (git_proto_stream *)stream;
+ t = OWNING_SUBTRANSPORT(s);
+
+ t->current_stream = NULL;
+
+ git_stream_close(s->io);
+ git_stream_free(s->io);
+ git__free(s->url);
+ git__free(s);
+}
+
+static int git_proto_stream_alloc(
+ git_subtransport *t,
+ const char *url,
+ const char *cmd,
+ const char *host,
+ const char *port,
+ git_smart_subtransport_stream **stream)
+{
+ git_proto_stream *s;
+
+ if (!stream)
+ return -1;
+
+ s = git__calloc(1, sizeof(git_proto_stream));
+ GIT_ERROR_CHECK_ALLOC(s);
+
+ s->parent.subtransport = &t->parent;
+ s->parent.read = git_proto_stream_read;
+ s->parent.write = git_proto_stream_write;
+ s->parent.free = git_proto_stream_free;
+
+ s->cmd = cmd;
+ s->url = git__strdup(url);
+
+ if (!s->url) {
+ git__free(s);
+ return -1;
+ }
+
+ if ((git_socket_stream_new(&s->io, host, port)) < 0)
+ return -1;
+
+ GIT_ERROR_CHECK_VERSION(s->io, GIT_STREAM_VERSION, "git_stream");
+
+ *stream = &s->parent;
+ return 0;
+}
+
+static int _git_uploadpack_ls(
+ git_subtransport *t,
+ const char *url,
+ git_smart_subtransport_stream **stream)
+{
+ git_net_url urldata = GIT_NET_URL_INIT;
+ const char *stream_url = url;
+ const char *host, *port;
+ git_proto_stream *s;
+ int error;
+
+ *stream = NULL;
+
+ if (!git__prefixcmp(url, prefix_git))
+ stream_url += strlen(prefix_git);
+
+ if ((error = git_net_url_parse(&urldata, url)) < 0)
+ return error;
+
+ host = urldata.host;
+ port = urldata.port ? urldata.port : GIT_DEFAULT_PORT;
+
+ error = git_proto_stream_alloc(t, stream_url, cmd_uploadpack, host, port, stream);
+
+ git_net_url_dispose(&urldata);
+
+ if (error < 0) {
+ git_proto_stream_free(*stream);
+ return error;
+ }
+
+ s = (git_proto_stream *) *stream;
+ if ((error = git_stream_connect(s->io)) < 0) {
+ git_proto_stream_free(*stream);
+ return error;
+ }
+
+ t->current_stream = s;
+
+ return 0;
+}
+
+static int _git_uploadpack(
+ git_subtransport *t,
+ const char *url,
+ git_smart_subtransport_stream **stream)
+{
+ GIT_UNUSED(url);
+
+ if (t->current_stream) {
+ *stream = &t->current_stream->parent;
+ return 0;
+ }
+
+ git_error_set(GIT_ERROR_NET, "must call UPLOADPACK_LS before UPLOADPACK");
+ return -1;
+}
+
+static int _git_receivepack_ls(
+ git_subtransport *t,
+ const char *url,
+ git_smart_subtransport_stream **stream)
+{
+ git_net_url urldata = GIT_NET_URL_INIT;
+ const char *stream_url = url;
+ git_proto_stream *s;
+ int error;
+
+ *stream = NULL;
+ if (!git__prefixcmp(url, prefix_git))
+ stream_url += strlen(prefix_git);
+
+ if ((error = git_net_url_parse(&urldata, url)) < 0)
+ return error;
+
+ error = git_proto_stream_alloc(t, stream_url, cmd_receivepack, urldata.host, urldata.port, stream);
+
+ git_net_url_dispose(&urldata);
+
+ if (error < 0) {
+ git_proto_stream_free(*stream);
+ return error;
+ }
+
+ s = (git_proto_stream *) *stream;
+
+ if ((error = git_stream_connect(s->io)) < 0)
+ return error;
+
+ t->current_stream = s;
+
+ return 0;
+}
+
+static int _git_receivepack(
+ git_subtransport *t,
+ const char *url,
+ git_smart_subtransport_stream **stream)
+{
+ GIT_UNUSED(url);
+
+ if (t->current_stream) {
+ *stream = &t->current_stream->parent;
+ return 0;
+ }
+
+ git_error_set(GIT_ERROR_NET, "must call RECEIVEPACK_LS before RECEIVEPACK");
+ return -1;
+}
+
+static int _git_action(
+ git_smart_subtransport_stream **stream,
+ git_smart_subtransport *subtransport,
+ const char *url,
+ git_smart_service_t action)
+{
+ git_subtransport *t = (git_subtransport *) subtransport;
+
+ switch (action) {
+ case GIT_SERVICE_UPLOADPACK_LS:
+ return _git_uploadpack_ls(t, url, stream);
+
+ case GIT_SERVICE_UPLOADPACK:
+ return _git_uploadpack(t, url, stream);
+
+ case GIT_SERVICE_RECEIVEPACK_LS:
+ return _git_receivepack_ls(t, url, stream);
+
+ case GIT_SERVICE_RECEIVEPACK:
+ return _git_receivepack(t, url, stream);
+ }
+
+ *stream = NULL;
+ return -1;
+}
+
+static int _git_close(git_smart_subtransport *subtransport)
+{
+ git_subtransport *t = (git_subtransport *) subtransport;
+
+ GIT_ASSERT(!t->current_stream);
+
+ GIT_UNUSED(t);
+
+ return 0;
+}
+
+static void _git_free(git_smart_subtransport *subtransport)
+{
+ git_subtransport *t = (git_subtransport *) subtransport;
+
+ git__free(t);
+}
+
+int git_smart_subtransport_git(git_smart_subtransport **out, git_transport *owner, void *param)
+{
+ git_subtransport *t;
+
+ GIT_UNUSED(param);
+
+ if (!out)
+ return -1;
+
+ t = git__calloc(1, sizeof(git_subtransport));
+ GIT_ERROR_CHECK_ALLOC(t);
+
+ t->owner = owner;
+ t->parent.action = _git_action;
+ t->parent.close = _git_close;
+ t->parent.free = _git_free;
+
+ *out = (git_smart_subtransport *) t;
+ return 0;
+}
diff --git a/src/libgit2/transports/http.c b/src/libgit2/transports/http.c
new file mode 100644
index 0000000..8437674
--- /dev/null
+++ b/src/libgit2/transports/http.c
@@ -0,0 +1,766 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+
+#ifndef GIT_WINHTTP
+
+#include "http_parser.h"
+#include "net.h"
+#include "remote.h"
+#include "smart.h"
+#include "auth.h"
+#include "http.h"
+#include "auth_negotiate.h"
+#include "auth_ntlm.h"
+#include "trace.h"
+#include "streams/tls.h"
+#include "streams/socket.h"
+#include "httpclient.h"
+#include "git2/sys/credential.h"
+
+bool git_http__expect_continue = false;
+
+typedef enum {
+ HTTP_STATE_NONE = 0,
+ HTTP_STATE_SENDING_REQUEST,
+ HTTP_STATE_RECEIVING_RESPONSE,
+ HTTP_STATE_DONE
+} http_state;
+
+typedef struct {
+ git_http_method method;
+ const char *url;
+ const char *request_type;
+ const char *response_type;
+ unsigned int initial : 1,
+ chunked : 1;
+} http_service;
+
+typedef struct {
+ git_smart_subtransport_stream parent;
+ const http_service *service;
+ http_state state;
+ unsigned replay_count;
+} http_stream;
+
+typedef struct {
+ git_net_url url;
+
+ git_credential *cred;
+ unsigned auth_schemetypes;
+ unsigned url_cred_presented : 1;
+} http_server;
+
+typedef struct {
+ git_smart_subtransport parent;
+ transport_smart *owner;
+
+ http_server server;
+ http_server proxy;
+
+ git_http_client *http_client;
+} http_subtransport;
+
+static const http_service upload_pack_ls_service = {
+ GIT_HTTP_METHOD_GET, "/info/refs?service=git-upload-pack",
+ NULL,
+ "application/x-git-upload-pack-advertisement",
+ 1,
+ 0
+};
+static const http_service upload_pack_service = {
+ GIT_HTTP_METHOD_POST, "/git-upload-pack",
+ "application/x-git-upload-pack-request",
+ "application/x-git-upload-pack-result",
+ 0,
+ 0
+};
+static const http_service receive_pack_ls_service = {
+ GIT_HTTP_METHOD_GET, "/info/refs?service=git-receive-pack",
+ NULL,
+ "application/x-git-receive-pack-advertisement",
+ 1,
+ 0
+};
+static const http_service receive_pack_service = {
+ GIT_HTTP_METHOD_POST, "/git-receive-pack",
+ "application/x-git-receive-pack-request",
+ "application/x-git-receive-pack-result",
+ 0,
+ 1
+};
+
+#define SERVER_TYPE_REMOTE "remote"
+#define SERVER_TYPE_PROXY "proxy"
+
+#define OWNING_SUBTRANSPORT(s) ((http_subtransport *)(s)->parent.subtransport)
+
+static int apply_url_credentials(
+ git_credential **cred,
+ unsigned int allowed_types,
+ const char *username,
+ const char *password)
+{
+ GIT_ASSERT_ARG(username);
+
+ if (!password)
+ password = "";
+
+ if (allowed_types & GIT_CREDENTIAL_USERPASS_PLAINTEXT)
+ return git_credential_userpass_plaintext_new(cred, username, password);
+
+ if ((allowed_types & GIT_CREDENTIAL_DEFAULT) && *username == '\0' && *password == '\0')
+ return git_credential_default_new(cred);
+
+ return GIT_PASSTHROUGH;
+}
+
+GIT_INLINE(void) free_cred(git_credential **cred)
+{
+ if (*cred) {
+ git_credential_free(*cred);
+ (*cred) = NULL;
+ }
+}
+
+static int handle_auth(
+ http_server *server,
+ const char *server_type,
+ const char *url,
+ unsigned int allowed_schemetypes,
+ unsigned int allowed_credtypes,
+ git_credential_acquire_cb callback,
+ void *callback_payload)
+{
+ int error = 1;
+
+ if (server->cred)
+ free_cred(&server->cred);
+
+ /* Start with URL-specified credentials, if there were any. */
+ if ((allowed_credtypes & GIT_CREDENTIAL_USERPASS_PLAINTEXT) &&
+ !server->url_cred_presented &&
+ server->url.username) {
+ error = apply_url_credentials(&server->cred, allowed_credtypes, server->url.username, server->url.password);
+ server->url_cred_presented = 1;
+
+ /* treat GIT_PASSTHROUGH as if callback isn't set */
+ if (error == GIT_PASSTHROUGH)
+ error = 1;
+ }
+
+ if (error > 0 && callback) {
+ error = callback(&server->cred, url, server->url.username, allowed_credtypes, callback_payload);
+
+ /* treat GIT_PASSTHROUGH as if callback isn't set */
+ if (error == GIT_PASSTHROUGH)
+ error = 1;
+ }
+
+ if (error > 0) {
+ git_error_set(GIT_ERROR_HTTP, "%s authentication required but no callback set", server_type);
+ error = GIT_EAUTH;
+ }
+
+ if (!error)
+ server->auth_schemetypes = allowed_schemetypes;
+
+ return error;
+}
+
+GIT_INLINE(int) handle_remote_auth(
+ http_stream *stream,
+ git_http_response *response)
+{
+ http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
+ git_remote_connect_options *connect_opts = &transport->owner->connect_opts;
+
+ if (response->server_auth_credtypes == 0) {
+ git_error_set(GIT_ERROR_HTTP, "server requires authentication that we do not support");
+ return GIT_EAUTH;
+ }
+
+ /* Otherwise, prompt for credentials. */
+ return handle_auth(
+ &transport->server,
+ SERVER_TYPE_REMOTE,
+ transport->owner->url,
+ response->server_auth_schemetypes,
+ response->server_auth_credtypes,
+ connect_opts->callbacks.credentials,
+ connect_opts->callbacks.payload);
+}
+
+GIT_INLINE(int) handle_proxy_auth(
+ http_stream *stream,
+ git_http_response *response)
+{
+ http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
+ git_remote_connect_options *connect_opts = &transport->owner->connect_opts;
+
+ if (response->proxy_auth_credtypes == 0) {
+ git_error_set(GIT_ERROR_HTTP, "proxy requires authentication that we do not support");
+ return GIT_EAUTH;
+ }
+
+ /* Otherwise, prompt for credentials. */
+ return handle_auth(
+ &transport->proxy,
+ SERVER_TYPE_PROXY,
+ connect_opts->proxy_opts.url,
+ response->server_auth_schemetypes,
+ response->proxy_auth_credtypes,
+ connect_opts->proxy_opts.credentials,
+ connect_opts->proxy_opts.payload);
+}
+
+static bool allow_redirect(http_stream *stream)
+{
+ http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
+
+ switch (transport->owner->connect_opts.follow_redirects) {
+ case GIT_REMOTE_REDIRECT_INITIAL:
+ return (stream->service->initial == 1);
+ case GIT_REMOTE_REDIRECT_ALL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int handle_response(
+ bool *complete,
+ http_stream *stream,
+ git_http_response *response,
+ bool allow_replay)
+{
+ http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
+ int error;
+
+ *complete = false;
+
+ if (allow_replay && git_http_response_is_redirect(response)) {
+ if (!response->location) {
+ git_error_set(GIT_ERROR_HTTP, "redirect without location");
+ return -1;
+ }
+
+ if (git_net_url_apply_redirect(&transport->server.url, response->location, allow_redirect(stream), stream->service->url) < 0) {
+ return -1;
+ }
+
+ return 0;
+ } else if (git_http_response_is_redirect(response)) {
+ git_error_set(GIT_ERROR_HTTP, "unexpected redirect");
+ return -1;
+ }
+
+ /* If we're in the middle of challenge/response auth, continue. */
+ if (allow_replay && response->resend_credentials) {
+ return 0;
+ } else if (allow_replay && response->status == GIT_HTTP_STATUS_UNAUTHORIZED) {
+ if ((error = handle_remote_auth(stream, response)) < 0)
+ return error;
+
+ return git_http_client_skip_body(transport->http_client);
+ } else if (allow_replay && response->status == GIT_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED) {
+ if ((error = handle_proxy_auth(stream, response)) < 0)
+ return error;
+
+ return git_http_client_skip_body(transport->http_client);
+ } else if (response->status == GIT_HTTP_STATUS_UNAUTHORIZED ||
+ response->status == GIT_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED) {
+ git_error_set(GIT_ERROR_HTTP, "unexpected authentication failure");
+ return GIT_EAUTH;
+ }
+
+ if (response->status != GIT_HTTP_STATUS_OK) {
+ git_error_set(GIT_ERROR_HTTP, "unexpected http status code: %d", response->status);
+ return -1;
+ }
+
+ /* The response must contain a Content-Type header. */
+ if (!response->content_type) {
+ git_error_set(GIT_ERROR_HTTP, "no content-type header in response");
+ return -1;
+ }
+
+ /* The Content-Type header must match our expectation. */
+ if (strcmp(response->content_type, stream->service->response_type) != 0) {
+ git_error_set(GIT_ERROR_HTTP, "invalid content-type: '%s'", response->content_type);
+ return -1;
+ }
+
+ *complete = true;
+ stream->state = HTTP_STATE_RECEIVING_RESPONSE;
+ return 0;
+}
+
+static int lookup_proxy(
+ bool *out_use,
+ http_subtransport *transport)
+{
+ git_remote_connect_options *connect_opts = &transport->owner->connect_opts;
+ const char *proxy;
+ git_remote *remote;
+ char *config = NULL;
+ int error = 0;
+
+ *out_use = false;
+ git_net_url_dispose(&transport->proxy.url);
+
+ switch (connect_opts->proxy_opts.type) {
+ case GIT_PROXY_SPECIFIED:
+ proxy = connect_opts->proxy_opts.url;
+ break;
+
+ case GIT_PROXY_AUTO:
+ remote = transport->owner->owner;
+
+ error = git_remote__http_proxy(&config, remote, &transport->server.url);
+
+ if (error || !config)
+ goto done;
+
+ proxy = config;
+ break;
+
+ default:
+ return 0;
+ }
+
+ if (!proxy ||
+ (error = git_net_url_parse_http(&transport->proxy.url, proxy)) < 0)
+ goto done;
+
+ if (!git_net_url_valid(&transport->proxy.url)) {
+ git_error_set(GIT_ERROR_HTTP, "invalid URL: '%s'", proxy);
+ error = -1;
+ goto done;
+ }
+
+ *out_use = true;
+
+done:
+ git__free(config);
+ return error;
+}
+
+static int generate_request(
+ git_net_url *url,
+ git_http_request *request,
+ http_stream *stream,
+ size_t len)
+{
+ http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
+ bool use_proxy = false;
+ int error;
+
+ if ((error = git_net_url_joinpath(url,
+ &transport->server.url, stream->service->url)) < 0 ||
+ (error = lookup_proxy(&use_proxy, transport)) < 0)
+ return error;
+
+ request->method = stream->service->method;
+ request->url = url;
+ request->credentials = transport->server.cred;
+ request->proxy = use_proxy ? &transport->proxy.url : NULL;
+ request->proxy_credentials = transport->proxy.cred;
+ request->custom_headers = &transport->owner->connect_opts.custom_headers;
+
+ if (stream->service->method == GIT_HTTP_METHOD_POST) {
+ request->chunked = stream->service->chunked;
+ request->content_length = stream->service->chunked ? 0 : len;
+ request->content_type = stream->service->request_type;
+ request->accept = stream->service->response_type;
+ request->expect_continue = git_http__expect_continue;
+ }
+
+ return 0;
+}
+
+/*
+ * Read from an HTTP transport - for the first invocation of this function
+ * (ie, when stream->state == HTTP_STATE_NONE), we'll send a GET request
+ * to the remote host. We will stream that data back on all subsequent
+ * calls.
+ */
+static int http_stream_read(
+ git_smart_subtransport_stream *s,
+ char *buffer,
+ size_t buffer_size,
+ size_t *out_len)
+{
+ http_stream *stream = (http_stream *)s;
+ http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
+ git_net_url url = GIT_NET_URL_INIT;
+ git_net_url proxy_url = GIT_NET_URL_INIT;
+ git_http_request request = {0};
+ git_http_response response = {0};
+ bool complete;
+ int error;
+
+ *out_len = 0;
+
+ if (stream->state == HTTP_STATE_NONE) {
+ stream->state = HTTP_STATE_SENDING_REQUEST;
+ stream->replay_count = 0;
+ }
+
+ /*
+ * Formulate the URL, send the request and read the response
+ * headers. Some of the request body may also be read.
+ */
+ while (stream->state == HTTP_STATE_SENDING_REQUEST &&
+ stream->replay_count < GIT_HTTP_REPLAY_MAX) {
+ git_net_url_dispose(&url);
+ git_net_url_dispose(&proxy_url);
+ git_http_response_dispose(&response);
+
+ if ((error = generate_request(&url, &request, stream, 0)) < 0 ||
+ (error = git_http_client_send_request(
+ transport->http_client, &request)) < 0 ||
+ (error = git_http_client_read_response(
+ &response, transport->http_client)) < 0 ||
+ (error = handle_response(&complete, stream, &response, true)) < 0)
+ goto done;
+
+ if (complete)
+ break;
+
+ stream->replay_count++;
+ }
+
+ if (stream->state == HTTP_STATE_SENDING_REQUEST) {
+ git_error_set(GIT_ERROR_HTTP, "too many redirects or authentication replays");
+ error = GIT_ERROR; /* not GIT_EAUTH, because the exact cause is unclear */
+ goto done;
+ }
+
+ GIT_ASSERT(stream->state == HTTP_STATE_RECEIVING_RESPONSE);
+
+ error = git_http_client_read_body(transport->http_client, buffer, buffer_size);
+
+ if (error > 0) {
+ *out_len = error;
+ error = 0;
+ }
+
+done:
+ git_net_url_dispose(&url);
+ git_net_url_dispose(&proxy_url);
+ git_http_response_dispose(&response);
+
+ return error;
+}
+
+static bool needs_probe(http_stream *stream)
+{
+ http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
+
+ return (transport->server.auth_schemetypes == GIT_HTTP_AUTH_NTLM ||
+ transport->server.auth_schemetypes == GIT_HTTP_AUTH_NEGOTIATE);
+}
+
+static int send_probe(http_stream *stream)
+{
+ http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
+ git_http_client *client = transport->http_client;
+ const char *probe = "0000";
+ size_t len = 4;
+ git_net_url url = GIT_NET_URL_INIT;
+ git_http_request request = {0};
+ git_http_response response = {0};
+ bool complete = false;
+ size_t step, steps = 1;
+ int error;
+
+ /* NTLM requires a full challenge/response */
+ if (transport->server.auth_schemetypes == GIT_HTTP_AUTH_NTLM)
+ steps = GIT_AUTH_STEPS_NTLM;
+
+ /*
+ * Send at most two requests: one without any authentication to see
+ * if we get prompted to authenticate. If we do, send a second one
+ * with the first authentication message. The final authentication
+ * message with the response will occur with the *actual* POST data.
+ */
+ for (step = 0; step < steps && !complete; step++) {
+ git_net_url_dispose(&url);
+ git_http_response_dispose(&response);
+
+ if ((error = generate_request(&url, &request, stream, len)) < 0 ||
+ (error = git_http_client_send_request(client, &request)) < 0 ||
+ (error = git_http_client_send_body(client, probe, len)) < 0 ||
+ (error = git_http_client_read_response(&response, client)) < 0 ||
+ (error = git_http_client_skip_body(client)) < 0 ||
+ (error = handle_response(&complete, stream, &response, true)) < 0)
+ goto done;
+ }
+
+done:
+ git_http_response_dispose(&response);
+ git_net_url_dispose(&url);
+ return error;
+}
+
+/*
+* Write to an HTTP transport - for the first invocation of this function
+* (ie, when stream->state == HTTP_STATE_NONE), we'll send a POST request
+* to the remote host. If we're sending chunked data, then subsequent calls
+* will write the additional data given in the buffer. If we're not chunking,
+* then the caller should have given us all the data in the original call.
+* The caller should call http_stream_read_response to get the result.
+*/
+static int http_stream_write(
+ git_smart_subtransport_stream *s,
+ const char *buffer,
+ size_t len)
+{
+ http_stream *stream = GIT_CONTAINER_OF(s, http_stream, parent);
+ http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
+ git_net_url url = GIT_NET_URL_INIT;
+ git_http_request request = {0};
+ git_http_response response = {0};
+ int error;
+
+ while (stream->state == HTTP_STATE_NONE &&
+ stream->replay_count < GIT_HTTP_REPLAY_MAX) {
+
+ git_net_url_dispose(&url);
+ git_http_response_dispose(&response);
+
+ /*
+ * If we're authenticating with a connection-based mechanism
+ * (NTLM, Kerberos), send a "probe" packet. Servers SHOULD
+ * authenticate an entire keep-alive connection, so ideally
+ * we should not need to authenticate but some servers do
+ * not support this. By sending a probe packet, we'll be
+ * able to follow up with a second POST using the actual
+ * data (and, in the degenerate case, the authentication
+ * header as well).
+ */
+ if (needs_probe(stream) && (error = send_probe(stream)) < 0)
+ goto done;
+
+ /* Send the regular POST request. */
+ if ((error = generate_request(&url, &request, stream, len)) < 0 ||
+ (error = git_http_client_send_request(
+ transport->http_client, &request)) < 0)
+ goto done;
+
+ if (request.expect_continue &&
+ git_http_client_has_response(transport->http_client)) {
+ bool complete;
+
+ /*
+ * If we got a response to an expect/continue, then
+ * it's something other than a 100 and we should
+ * deal with the response somehow.
+ */
+ if ((error = git_http_client_read_response(&response, transport->http_client)) < 0 ||
+ (error = handle_response(&complete, stream, &response, true)) < 0)
+ goto done;
+ } else {
+ stream->state = HTTP_STATE_SENDING_REQUEST;
+ }
+
+ stream->replay_count++;
+ }
+
+ if (stream->state == HTTP_STATE_NONE) {
+ git_error_set(GIT_ERROR_HTTP,
+ "too many redirects or authentication replays");
+ error = GIT_ERROR; /* not GIT_EAUTH because the exact cause is unclear */
+ goto done;
+ }
+
+ GIT_ASSERT(stream->state == HTTP_STATE_SENDING_REQUEST);
+
+ error = git_http_client_send_body(transport->http_client, buffer, len);
+
+done:
+ git_http_response_dispose(&response);
+ git_net_url_dispose(&url);
+ return error;
+}
+
+/*
+* Read from an HTTP transport after it has been written to. This is the
+* response from a POST request made by http_stream_write.
+*/
+static int http_stream_read_response(
+ git_smart_subtransport_stream *s,
+ char *buffer,
+ size_t buffer_size,
+ size_t *out_len)
+{
+ http_stream *stream = (http_stream *)s;
+ http_subtransport *transport = OWNING_SUBTRANSPORT(stream);
+ git_http_client *client = transport->http_client;
+ git_http_response response = {0};
+ bool complete;
+ int error;
+
+ *out_len = 0;
+
+ if (stream->state == HTTP_STATE_SENDING_REQUEST) {
+ if ((error = git_http_client_read_response(&response, client)) < 0 ||
+ (error = handle_response(&complete, stream, &response, false)) < 0)
+ goto done;
+
+ GIT_ASSERT(complete);
+ stream->state = HTTP_STATE_RECEIVING_RESPONSE;
+ }
+
+ error = git_http_client_read_body(client, buffer, buffer_size);
+
+ if (error > 0) {
+ *out_len = error;
+ error = 0;
+ }
+
+done:
+ git_http_response_dispose(&response);
+ return error;
+}
+
+static void http_stream_free(git_smart_subtransport_stream *stream)
+{
+ http_stream *s = GIT_CONTAINER_OF(stream, http_stream, parent);
+ git__free(s);
+}
+
+static const http_service *select_service(git_smart_service_t action)
+{
+ switch (action) {
+ case GIT_SERVICE_UPLOADPACK_LS:
+ return &upload_pack_ls_service;
+ case GIT_SERVICE_UPLOADPACK:
+ return &upload_pack_service;
+ case GIT_SERVICE_RECEIVEPACK_LS:
+ return &receive_pack_ls_service;
+ case GIT_SERVICE_RECEIVEPACK:
+ return &receive_pack_service;
+ }
+
+ return NULL;
+}
+
+static int http_action(
+ git_smart_subtransport_stream **out,
+ git_smart_subtransport *t,
+ const char *url,
+ git_smart_service_t action)
+{
+ http_subtransport *transport = GIT_CONTAINER_OF(t, http_subtransport, parent);
+ git_remote_connect_options *connect_opts = &transport->owner->connect_opts;
+ git_http_client_options opts = {0};
+ http_stream *stream;
+ const http_service *service;
+ int error;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(t);
+
+ *out = NULL;
+
+ /*
+ * If we've seen a redirect then preserve the location that we've
+ * been given. This is important to continue authorization against
+ * the redirect target, not the user-given source; the endpoint may
+ * have redirected us from HTTP->HTTPS and is using an auth mechanism
+ * that would be insecure in plaintext (eg, HTTP Basic).
+ */
+ if (!git_net_url_valid(&transport->server.url) &&
+ (error = git_net_url_parse(&transport->server.url, url)) < 0)
+ return error;
+
+ if ((service = select_service(action)) == NULL) {
+ git_error_set(GIT_ERROR_HTTP, "invalid action");
+ return -1;
+ }
+
+ stream = git__calloc(sizeof(http_stream), 1);
+ GIT_ERROR_CHECK_ALLOC(stream);
+
+ opts.server_certificate_check_cb = connect_opts->callbacks.certificate_check;
+ opts.server_certificate_check_payload = connect_opts->callbacks.payload;
+ opts.proxy_certificate_check_cb = connect_opts->proxy_opts.certificate_check;
+ opts.proxy_certificate_check_payload = connect_opts->proxy_opts.payload;
+
+ if (transport->http_client) {
+ git_http_client_set_options(transport->http_client, &opts);
+ } else {
+ if (git_http_client_new(&transport->http_client, &opts) < 0)
+ return -1;
+ }
+
+ stream->service = service;
+ stream->parent.subtransport = &transport->parent;
+
+ if (service->method == GIT_HTTP_METHOD_GET) {
+ stream->parent.read = http_stream_read;
+ } else {
+ stream->parent.write = http_stream_write;
+ stream->parent.read = http_stream_read_response;
+ }
+
+ stream->parent.free = http_stream_free;
+
+ *out = (git_smart_subtransport_stream *)stream;
+ return 0;
+}
+
+static int http_close(git_smart_subtransport *t)
+{
+ http_subtransport *transport = GIT_CONTAINER_OF(t, http_subtransport, parent);
+
+ free_cred(&transport->server.cred);
+ free_cred(&transport->proxy.cred);
+
+ transport->server.url_cred_presented = false;
+ transport->proxy.url_cred_presented = false;
+
+ git_net_url_dispose(&transport->server.url);
+ git_net_url_dispose(&transport->proxy.url);
+
+ return 0;
+}
+
+static void http_free(git_smart_subtransport *t)
+{
+ http_subtransport *transport = GIT_CONTAINER_OF(t, http_subtransport, parent);
+
+ git_http_client_free(transport->http_client);
+
+ http_close(t);
+ git__free(transport);
+}
+
+int git_smart_subtransport_http(git_smart_subtransport **out, git_transport *owner, void *param)
+{
+ http_subtransport *transport;
+
+ GIT_UNUSED(param);
+
+ GIT_ASSERT_ARG(out);
+
+ transport = git__calloc(sizeof(http_subtransport), 1);
+ GIT_ERROR_CHECK_ALLOC(transport);
+
+ transport->owner = (transport_smart *)owner;
+ transport->parent.action = http_action;
+ transport->parent.close = http_close;
+ transport->parent.free = http_free;
+
+ *out = (git_smart_subtransport *) transport;
+ return 0;
+}
+
+#endif /* !GIT_WINHTTP */
diff --git a/src/libgit2/transports/http.h b/src/libgit2/transports/http.h
new file mode 100644
index 0000000..8e8e722
--- /dev/null
+++ b/src/libgit2/transports/http.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_transports_http_h__
+#define INCLUDE_transports_http_h__
+
+#include "settings.h"
+#include "httpclient.h"
+
+#define GIT_HTTP_REPLAY_MAX 15
+
+extern bool git_http__expect_continue;
+
+GIT_INLINE(int) git_http__user_agent(git_str *buf)
+{
+ const char *ua = git_libgit2__user_agent();
+
+ if (!ua)
+ ua = "libgit2 " LIBGIT2_VERSION;
+
+ return git_str_printf(buf, "git/2.0 (%s)", ua);
+}
+
+#endif
diff --git a/src/libgit2/transports/httpclient.c b/src/libgit2/transports/httpclient.c
new file mode 100644
index 0000000..a20b594
--- /dev/null
+++ b/src/libgit2/transports/httpclient.c
@@ -0,0 +1,1593 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+#include "git2.h"
+#include "http_parser.h"
+#include "vector.h"
+#include "trace.h"
+#include "httpclient.h"
+#include "http.h"
+#include "auth.h"
+#include "auth_negotiate.h"
+#include "auth_ntlm.h"
+#include "git2/sys/credential.h"
+#include "net.h"
+#include "stream.h"
+#include "streams/socket.h"
+#include "streams/tls.h"
+#include "auth.h"
+
+static git_http_auth_scheme auth_schemes[] = {
+ { GIT_HTTP_AUTH_NEGOTIATE, "Negotiate", GIT_CREDENTIAL_DEFAULT, git_http_auth_negotiate },
+ { GIT_HTTP_AUTH_NTLM, "NTLM", GIT_CREDENTIAL_USERPASS_PLAINTEXT, git_http_auth_ntlm },
+ { GIT_HTTP_AUTH_BASIC, "Basic", GIT_CREDENTIAL_USERPASS_PLAINTEXT, git_http_auth_basic },
+};
+
+/*
+ * Use a 16kb read buffer to match the maximum size of a TLS packet. This
+ * is critical for compatibility with SecureTransport, which will always do
+ * a network read on every call, even if it has data buffered to return to
+ * you. That buffered data may be the _end_ of a keep-alive response, so
+ * if SecureTransport performs another network read, it will wait until the
+ * server ultimately times out before it returns that buffered data to you.
+ * Since SecureTransport only reads a single TLS packet at a time, by
+ * calling it with a read buffer that is the maximum size of a TLS packet,
+ * we ensure that it will never buffer.
+ */
+#define GIT_READ_BUFFER_SIZE (16 * 1024)
+
+typedef struct {
+ git_net_url url;
+ git_stream *stream;
+
+ git_vector auth_challenges;
+ git_http_auth_context *auth_context;
+} git_http_server;
+
+typedef enum {
+ PROXY = 1,
+ SERVER
+} git_http_server_t;
+
+typedef enum {
+ NONE = 0,
+ SENDING_REQUEST,
+ SENDING_BODY,
+ SENT_REQUEST,
+ HAS_EARLY_RESPONSE,
+ READING_RESPONSE,
+ READING_BODY,
+ DONE
+} http_client_state;
+
+/* Parser state */
+typedef enum {
+ PARSE_HEADER_NONE = 0,
+ PARSE_HEADER_NAME,
+ PARSE_HEADER_VALUE,
+ PARSE_HEADER_COMPLETE
+} parse_header_state;
+
+typedef enum {
+ PARSE_STATUS_OK,
+ PARSE_STATUS_NO_OUTPUT,
+ PARSE_STATUS_ERROR
+} parse_status;
+
+typedef struct {
+ git_http_client *client;
+ git_http_response *response;
+
+ /* Temporary buffers to avoid extra mallocs */
+ git_str parse_header_name;
+ git_str parse_header_value;
+
+ /* Parser state */
+ int error;
+ parse_status parse_status;
+
+ /* Headers parsing */
+ parse_header_state parse_header_state;
+
+ /* Body parsing */
+ char *output_buf; /* Caller's output buffer */
+ size_t output_size; /* Size of caller's output buffer */
+ size_t output_written; /* Bytes we've written to output buffer */
+} http_parser_context;
+
+/* HTTP client connection */
+struct git_http_client {
+ git_http_client_options opts;
+
+ /* Are we writing to the proxy or server, and state of the client. */
+ git_http_server_t current_server;
+ http_client_state state;
+
+ http_parser parser;
+
+ git_http_server server;
+ git_http_server proxy;
+
+ unsigned request_count;
+ unsigned connected : 1,
+ proxy_connected : 1,
+ keepalive : 1,
+ request_chunked : 1;
+
+ /* Temporary buffers to avoid extra mallocs */
+ git_str request_msg;
+ git_str read_buf;
+
+ /* A subset of information from the request */
+ size_t request_body_len,
+ request_body_remain;
+
+ /*
+ * When state == HAS_EARLY_RESPONSE, the response of our proxy
+ * that we have buffered and will deliver during read_response.
+ */
+ git_http_response early_response;
+};
+
+bool git_http_response_is_redirect(git_http_response *response)
+{
+ return (response->status == GIT_HTTP_MOVED_PERMANENTLY ||
+ response->status == GIT_HTTP_FOUND ||
+ response->status == GIT_HTTP_SEE_OTHER ||
+ response->status == GIT_HTTP_TEMPORARY_REDIRECT ||
+ response->status == GIT_HTTP_PERMANENT_REDIRECT);
+}
+
+void git_http_response_dispose(git_http_response *response)
+{
+ if (!response)
+ return;
+
+ git__free(response->content_type);
+ git__free(response->location);
+
+ memset(response, 0, sizeof(git_http_response));
+}
+
+static int on_header_complete(http_parser *parser)
+{
+ http_parser_context *ctx = (http_parser_context *) parser->data;
+ git_http_client *client = ctx->client;
+ git_http_response *response = ctx->response;
+
+ git_str *name = &ctx->parse_header_name;
+ git_str *value = &ctx->parse_header_value;
+
+ if (!strcasecmp("Content-Type", name->ptr)) {
+ if (response->content_type) {
+ git_error_set(GIT_ERROR_HTTP,
+ "multiple content-type headers");
+ return -1;
+ }
+
+ response->content_type =
+ git__strndup(value->ptr, value->size);
+ GIT_ERROR_CHECK_ALLOC(ctx->response->content_type);
+ } else if (!strcasecmp("Content-Length", name->ptr)) {
+ int64_t len;
+
+ if (response->content_length) {
+ git_error_set(GIT_ERROR_HTTP,
+ "multiple content-length headers");
+ return -1;
+ }
+
+ if (git__strntol64(&len, value->ptr, value->size,
+ NULL, 10) < 0 || len < 0) {
+ git_error_set(GIT_ERROR_HTTP,
+ "invalid content-length");
+ return -1;
+ }
+
+ response->content_length = (size_t)len;
+ } else if (!strcasecmp("Transfer-Encoding", name->ptr) &&
+ !strcasecmp("chunked", value->ptr)) {
+ ctx->response->chunked = 1;
+ } else if (!strcasecmp("Proxy-Authenticate", git_str_cstr(name))) {
+ char *dup = git__strndup(value->ptr, value->size);
+ GIT_ERROR_CHECK_ALLOC(dup);
+
+ if (git_vector_insert(&client->proxy.auth_challenges, dup) < 0)
+ return -1;
+ } else if (!strcasecmp("WWW-Authenticate", name->ptr)) {
+ char *dup = git__strndup(value->ptr, value->size);
+ GIT_ERROR_CHECK_ALLOC(dup);
+
+ if (git_vector_insert(&client->server.auth_challenges, dup) < 0)
+ return -1;
+ } else if (!strcasecmp("Location", name->ptr)) {
+ if (response->location) {
+ git_error_set(GIT_ERROR_HTTP,
+ "multiple location headers");
+ return -1;
+ }
+
+ response->location = git__strndup(value->ptr, value->size);
+ GIT_ERROR_CHECK_ALLOC(response->location);
+ }
+
+ return 0;
+}
+
+static int on_header_field(http_parser *parser, const char *str, size_t len)
+{
+ http_parser_context *ctx = (http_parser_context *) parser->data;
+
+ switch (ctx->parse_header_state) {
+ /*
+ * We last saw a header value, process the name/value pair and
+ * get ready to handle this new name.
+ */
+ case PARSE_HEADER_VALUE:
+ if (on_header_complete(parser) < 0)
+ return ctx->parse_status = PARSE_STATUS_ERROR;
+
+ git_str_clear(&ctx->parse_header_name);
+ git_str_clear(&ctx->parse_header_value);
+ /* Fall through */
+
+ case PARSE_HEADER_NONE:
+ case PARSE_HEADER_NAME:
+ ctx->parse_header_state = PARSE_HEADER_NAME;
+
+ if (git_str_put(&ctx->parse_header_name, str, len) < 0)
+ return ctx->parse_status = PARSE_STATUS_ERROR;
+
+ break;
+
+ default:
+ git_error_set(GIT_ERROR_HTTP,
+ "header name seen at unexpected time");
+ return ctx->parse_status = PARSE_STATUS_ERROR;
+ }
+
+ return 0;
+}
+
+static int on_header_value(http_parser *parser, const char *str, size_t len)
+{
+ http_parser_context *ctx = (http_parser_context *) parser->data;
+
+ switch (ctx->parse_header_state) {
+ case PARSE_HEADER_NAME:
+ case PARSE_HEADER_VALUE:
+ ctx->parse_header_state = PARSE_HEADER_VALUE;
+
+ if (git_str_put(&ctx->parse_header_value, str, len) < 0)
+ return ctx->parse_status = PARSE_STATUS_ERROR;
+
+ break;
+
+ default:
+ git_error_set(GIT_ERROR_HTTP,
+ "header value seen at unexpected time");
+ return ctx->parse_status = PARSE_STATUS_ERROR;
+ }
+
+ return 0;
+}
+
+GIT_INLINE(bool) challenge_matches_scheme(
+ const char *challenge,
+ git_http_auth_scheme *scheme)
+{
+ const char *scheme_name = scheme->name;
+ size_t scheme_len = strlen(scheme_name);
+
+ if (!strncasecmp(challenge, scheme_name, scheme_len) &&
+ (challenge[scheme_len] == '\0' || challenge[scheme_len] == ' '))
+ return true;
+
+ return false;
+}
+
+static git_http_auth_scheme *scheme_for_challenge(const char *challenge)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(auth_schemes); i++) {
+ if (challenge_matches_scheme(challenge, &auth_schemes[i]))
+ return &auth_schemes[i];
+ }
+
+ return NULL;
+}
+
+GIT_INLINE(void) collect_authinfo(
+ unsigned int *schemetypes,
+ unsigned int *credtypes,
+ git_vector *challenges)
+{
+ git_http_auth_scheme *scheme;
+ const char *challenge;
+ size_t i;
+
+ *schemetypes = 0;
+ *credtypes = 0;
+
+ git_vector_foreach(challenges, i, challenge) {
+ if ((scheme = scheme_for_challenge(challenge)) != NULL) {
+ *schemetypes |= scheme->type;
+ *credtypes |= scheme->credtypes;
+ }
+ }
+}
+
+static int resend_needed(git_http_client *client, git_http_response *response)
+{
+ git_http_auth_context *auth_context;
+
+ if (response->status == GIT_HTTP_STATUS_UNAUTHORIZED &&
+ (auth_context = client->server.auth_context) &&
+ auth_context->is_complete &&
+ !auth_context->is_complete(auth_context))
+ return 1;
+
+ if (response->status == GIT_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED &&
+ (auth_context = client->proxy.auth_context) &&
+ auth_context->is_complete &&
+ !auth_context->is_complete(auth_context))
+ return 1;
+
+ return 0;
+}
+
+static int on_headers_complete(http_parser *parser)
+{
+ http_parser_context *ctx = (http_parser_context *) parser->data;
+
+ /* Finalize the last seen header */
+ switch (ctx->parse_header_state) {
+ case PARSE_HEADER_VALUE:
+ if (on_header_complete(parser) < 0)
+ return ctx->parse_status = PARSE_STATUS_ERROR;
+
+ /* Fall through */
+
+ case PARSE_HEADER_NONE:
+ ctx->parse_header_state = PARSE_HEADER_COMPLETE;
+ break;
+
+ default:
+ git_error_set(GIT_ERROR_HTTP,
+ "header completion at unexpected time");
+ return ctx->parse_status = PARSE_STATUS_ERROR;
+ }
+
+ ctx->response->status = parser->status_code;
+ ctx->client->keepalive = http_should_keep_alive(parser);
+
+ /* Prepare for authentication */
+ collect_authinfo(&ctx->response->server_auth_schemetypes,
+ &ctx->response->server_auth_credtypes,
+ &ctx->client->server.auth_challenges);
+ collect_authinfo(&ctx->response->proxy_auth_schemetypes,
+ &ctx->response->proxy_auth_credtypes,
+ &ctx->client->proxy.auth_challenges);
+
+ ctx->response->resend_credentials = resend_needed(ctx->client,
+ ctx->response);
+
+ /* Stop parsing. */
+ http_parser_pause(parser, 1);
+
+ if (ctx->response->content_type || ctx->response->chunked)
+ ctx->client->state = READING_BODY;
+ else
+ ctx->client->state = DONE;
+
+ return 0;
+}
+
+static int on_body(http_parser *parser, const char *buf, size_t len)
+{
+ http_parser_context *ctx = (http_parser_context *) parser->data;
+ size_t max_len;
+
+ /* Saw data when we expected not to (eg, in consume_response_body) */
+ if (ctx->output_buf == NULL || ctx->output_size == 0) {
+ ctx->parse_status = PARSE_STATUS_NO_OUTPUT;
+ return 0;
+ }
+
+ GIT_ASSERT(ctx->output_size >= ctx->output_written);
+
+ max_len = min(ctx->output_size - ctx->output_written, len);
+ max_len = min(max_len, INT_MAX);
+
+ memcpy(ctx->output_buf + ctx->output_written, buf, max_len);
+ ctx->output_written += max_len;
+
+ return 0;
+}
+
+static int on_message_complete(http_parser *parser)
+{
+ http_parser_context *ctx = (http_parser_context *) parser->data;
+
+ ctx->client->state = DONE;
+ return 0;
+}
+
+GIT_INLINE(int) stream_write(
+ git_http_server *server,
+ const char *data,
+ size_t len)
+{
+ git_trace(GIT_TRACE_TRACE,
+ "Sending request:\n%.*s", (int)len, data);
+
+ return git_stream__write_full(server->stream, data, len, 0);
+}
+
+GIT_INLINE(int) client_write_request(git_http_client *client)
+{
+ git_stream *stream = client->current_server == PROXY ?
+ client->proxy.stream : client->server.stream;
+
+ git_trace(GIT_TRACE_TRACE,
+ "Sending request:\n%.*s",
+ (int)client->request_msg.size, client->request_msg.ptr);
+
+ return git_stream__write_full(stream,
+ client->request_msg.ptr,
+ client->request_msg.size,
+ 0);
+}
+
+static const char *name_for_method(git_http_method method)
+{
+ switch (method) {
+ case GIT_HTTP_METHOD_GET:
+ return "GET";
+ case GIT_HTTP_METHOD_POST:
+ return "POST";
+ case GIT_HTTP_METHOD_CONNECT:
+ return "CONNECT";
+ }
+
+ return NULL;
+}
+
+/*
+ * Find the scheme that is suitable for the given credentials, based on the
+ * server's auth challenges.
+ */
+static bool best_scheme_and_challenge(
+ git_http_auth_scheme **scheme_out,
+ const char **challenge_out,
+ git_vector *challenges,
+ git_credential *credentials)
+{
+ const char *challenge;
+ size_t i, j;
+
+ for (i = 0; i < ARRAY_SIZE(auth_schemes); i++) {
+ git_vector_foreach(challenges, j, challenge) {
+ git_http_auth_scheme *scheme = &auth_schemes[i];
+
+ if (challenge_matches_scheme(challenge, scheme) &&
+ (scheme->credtypes & credentials->credtype)) {
+ *scheme_out = scheme;
+ *challenge_out = challenge;
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+/*
+ * Find the challenge from the server for our current auth context.
+ */
+static const char *challenge_for_context(
+ git_vector *challenges,
+ git_http_auth_context *auth_ctx)
+{
+ const char *challenge;
+ size_t i, j;
+
+ for (i = 0; i < ARRAY_SIZE(auth_schemes); i++) {
+ if (auth_schemes[i].type == auth_ctx->type) {
+ git_http_auth_scheme *scheme = &auth_schemes[i];
+
+ git_vector_foreach(challenges, j, challenge) {
+ if (challenge_matches_scheme(challenge, scheme))
+ return challenge;
+ }
+ }
+ }
+
+ return NULL;
+}
+
+static const char *init_auth_context(
+ git_http_server *server,
+ git_vector *challenges,
+ git_credential *credentials)
+{
+ git_http_auth_scheme *scheme;
+ const char *challenge;
+ int error;
+
+ if (!best_scheme_and_challenge(&scheme, &challenge, challenges, credentials)) {
+ git_error_set(GIT_ERROR_HTTP, "could not find appropriate mechanism for credentials");
+ return NULL;
+ }
+
+ error = scheme->init_context(&server->auth_context, &server->url);
+
+ if (error == GIT_PASSTHROUGH) {
+ git_error_set(GIT_ERROR_HTTP, "'%s' authentication is not supported", scheme->name);
+ return NULL;
+ }
+
+ return challenge;
+}
+
+static void free_auth_context(git_http_server *server)
+{
+ if (!server->auth_context)
+ return;
+
+ if (server->auth_context->free)
+ server->auth_context->free(server->auth_context);
+
+ server->auth_context = NULL;
+}
+
+static int apply_credentials(
+ git_str *buf,
+ git_http_server *server,
+ const char *header_name,
+ git_credential *credentials)
+{
+ git_http_auth_context *auth = server->auth_context;
+ git_vector *challenges = &server->auth_challenges;
+ const char *challenge;
+ git_str token = GIT_STR_INIT;
+ int error = 0;
+
+ /* We've started a new request without creds; free the context. */
+ if (auth && !credentials) {
+ free_auth_context(server);
+ return 0;
+ }
+
+ /* We haven't authenticated, nor were we asked to. Nothing to do. */
+ if (!auth && !git_vector_length(challenges))
+ return 0;
+
+ if (!auth) {
+ challenge = init_auth_context(server, challenges, credentials);
+ auth = server->auth_context;
+
+ if (!challenge || !auth) {
+ error = -1;
+ goto done;
+ }
+ } else if (auth->set_challenge) {
+ challenge = challenge_for_context(challenges, auth);
+ }
+
+ if (auth->set_challenge && challenge &&
+ (error = auth->set_challenge(auth, challenge)) < 0)
+ goto done;
+
+ if ((error = auth->next_token(&token, auth, credentials)) < 0)
+ goto done;
+
+ if (auth->is_complete && auth->is_complete(auth)) {
+ /*
+ * If we're done with an auth mechanism with connection affinity,
+ * we don't need to send any more headers and can dispose the context.
+ */
+ if (auth->connection_affinity)
+ free_auth_context(server);
+ } else if (!token.size) {
+ git_error_set(GIT_ERROR_HTTP, "failed to respond to authentication challenge");
+ error = GIT_EAUTH;
+ goto done;
+ }
+
+ if (token.size > 0)
+ error = git_str_printf(buf, "%s: %s\r\n", header_name, token.ptr);
+
+done:
+ git_str_dispose(&token);
+ return error;
+}
+
+GIT_INLINE(int) apply_server_credentials(
+ git_str *buf,
+ git_http_client *client,
+ git_http_request *request)
+{
+ return apply_credentials(buf,
+ &client->server,
+ "Authorization",
+ request->credentials);
+}
+
+GIT_INLINE(int) apply_proxy_credentials(
+ git_str *buf,
+ git_http_client *client,
+ git_http_request *request)
+{
+ return apply_credentials(buf,
+ &client->proxy,
+ "Proxy-Authorization",
+ request->proxy_credentials);
+}
+
+static int puts_host_and_port(git_str *buf, git_net_url *url, bool force_port)
+{
+ bool ipv6 = git_net_url_is_ipv6(url);
+
+ if (ipv6)
+ git_str_putc(buf, '[');
+
+ git_str_puts(buf, url->host);
+
+ if (ipv6)
+ git_str_putc(buf, ']');
+
+ if (force_port || !git_net_url_is_default_port(url)) {
+ git_str_putc(buf, ':');
+ git_str_puts(buf, url->port);
+ }
+
+ return git_str_oom(buf) ? -1 : 0;
+}
+
+static int generate_connect_request(
+ git_http_client *client,
+ git_http_request *request)
+{
+ git_str *buf;
+ int error;
+
+ git_str_clear(&client->request_msg);
+ buf = &client->request_msg;
+
+ git_str_puts(buf, "CONNECT ");
+ puts_host_and_port(buf, &client->server.url, true);
+ git_str_puts(buf, " HTTP/1.1\r\n");
+
+ git_str_puts(buf, "User-Agent: ");
+ git_http__user_agent(buf);
+ git_str_puts(buf, "\r\n");
+
+ git_str_puts(buf, "Host: ");
+ puts_host_and_port(buf, &client->server.url, true);
+ git_str_puts(buf, "\r\n");
+
+ if ((error = apply_proxy_credentials(buf, client, request) < 0))
+ return -1;
+
+ git_str_puts(buf, "\r\n");
+
+ return git_str_oom(buf) ? -1 : 0;
+}
+
+static bool use_connect_proxy(git_http_client *client)
+{
+ return client->proxy.url.host && !strcmp(client->server.url.scheme, "https");
+}
+
+static int generate_request(
+ git_http_client *client,
+ git_http_request *request)
+{
+ git_str *buf;
+ size_t i;
+ int error;
+
+ GIT_ASSERT_ARG(client);
+ GIT_ASSERT_ARG(request);
+
+ git_str_clear(&client->request_msg);
+ buf = &client->request_msg;
+
+ /* GET|POST path HTTP/1.1 */
+ git_str_puts(buf, name_for_method(request->method));
+ git_str_putc(buf, ' ');
+
+ if (request->proxy && strcmp(request->url->scheme, "https"))
+ git_net_url_fmt(buf, request->url);
+ else
+ git_net_url_fmt_path(buf, request->url);
+
+ git_str_puts(buf, " HTTP/1.1\r\n");
+
+ git_str_puts(buf, "User-Agent: ");
+ git_http__user_agent(buf);
+ git_str_puts(buf, "\r\n");
+
+ git_str_puts(buf, "Host: ");
+ puts_host_and_port(buf, request->url, false);
+ git_str_puts(buf, "\r\n");
+
+ if (request->accept)
+ git_str_printf(buf, "Accept: %s\r\n", request->accept);
+ else
+ git_str_puts(buf, "Accept: */*\r\n");
+
+ if (request->content_type)
+ git_str_printf(buf, "Content-Type: %s\r\n",
+ request->content_type);
+
+ if (request->chunked)
+ git_str_puts(buf, "Transfer-Encoding: chunked\r\n");
+
+ if (request->content_length > 0)
+ git_str_printf(buf, "Content-Length: %"PRIuZ "\r\n",
+ request->content_length);
+
+ if (request->expect_continue)
+ git_str_printf(buf, "Expect: 100-continue\r\n");
+
+ if ((error = apply_server_credentials(buf, client, request)) < 0 ||
+ (!use_connect_proxy(client) &&
+ (error = apply_proxy_credentials(buf, client, request)) < 0))
+ return error;
+
+ if (request->custom_headers) {
+ for (i = 0; i < request->custom_headers->count; i++) {
+ const char *hdr = request->custom_headers->strings[i];
+
+ if (hdr)
+ git_str_printf(buf, "%s\r\n", hdr);
+ }
+ }
+
+ git_str_puts(buf, "\r\n");
+
+ if (git_str_oom(buf))
+ return -1;
+
+ return 0;
+}
+
+static int check_certificate(
+ git_stream *stream,
+ git_net_url *url,
+ int is_valid,
+ git_transport_certificate_check_cb cert_cb,
+ void *cert_cb_payload)
+{
+ git_cert *cert;
+ git_error_state last_error = {0};
+ int error;
+
+ if ((error = git_stream_certificate(&cert, stream)) < 0)
+ return error;
+
+ git_error_state_capture(&last_error, GIT_ECERTIFICATE);
+
+ error = cert_cb(cert, is_valid, url->host, cert_cb_payload);
+
+ if (error == GIT_PASSTHROUGH && !is_valid)
+ return git_error_state_restore(&last_error);
+ else if (error == GIT_PASSTHROUGH)
+ error = 0;
+ else if (error && !git_error_last())
+ git_error_set(GIT_ERROR_HTTP,
+ "user rejected certificate for %s", url->host);
+
+ git_error_state_free(&last_error);
+ return error;
+}
+
+static int server_connect_stream(
+ git_http_server *server,
+ git_transport_certificate_check_cb cert_cb,
+ void *cb_payload)
+{
+ int error;
+
+ GIT_ERROR_CHECK_VERSION(server->stream, GIT_STREAM_VERSION, "git_stream");
+
+ error = git_stream_connect(server->stream);
+
+ if (error && error != GIT_ECERTIFICATE)
+ return error;
+
+ if (git_stream_is_encrypted(server->stream) && cert_cb != NULL)
+ error = check_certificate(server->stream, &server->url, !error,
+ cert_cb, cb_payload);
+
+ return error;
+}
+
+static void reset_auth_connection(git_http_server *server)
+{
+ /*
+ * If we've authenticated and we're doing "normal"
+ * authentication with a request affinity (Basic, Digest)
+ * then we want to _keep_ our context, since authentication
+ * survives even through non-keep-alive connections. If
+ * we've authenticated and we're doing connection-based
+ * authentication (NTLM, Negotiate) - indicated by the presence
+ * of an `is_complete` callback - then we need to restart
+ * authentication on a new connection.
+ */
+
+ if (server->auth_context &&
+ server->auth_context->connection_affinity)
+ free_auth_context(server);
+}
+
+/*
+ * Updates the server data structure with the new URL; returns 1 if the server
+ * has changed and we need to reconnect, returns 0 otherwise.
+ */
+GIT_INLINE(int) server_setup_from_url(
+ git_http_server *server,
+ git_net_url *url)
+{
+ GIT_ASSERT_ARG(url);
+ GIT_ASSERT_ARG(url->scheme);
+ GIT_ASSERT_ARG(url->host);
+ GIT_ASSERT_ARG(url->port);
+
+ if (!server->url.scheme || strcmp(server->url.scheme, url->scheme) ||
+ !server->url.host || strcmp(server->url.host, url->host) ||
+ !server->url.port || strcmp(server->url.port, url->port)) {
+ git__free(server->url.scheme);
+ git__free(server->url.host);
+ git__free(server->url.port);
+
+ server->url.scheme = git__strdup(url->scheme);
+ GIT_ERROR_CHECK_ALLOC(server->url.scheme);
+
+ server->url.host = git__strdup(url->host);
+ GIT_ERROR_CHECK_ALLOC(server->url.host);
+
+ server->url.port = git__strdup(url->port);
+ GIT_ERROR_CHECK_ALLOC(server->url.port);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+static void reset_parser(git_http_client *client)
+{
+ http_parser_init(&client->parser, HTTP_RESPONSE);
+}
+
+static int setup_hosts(
+ git_http_client *client,
+ git_http_request *request)
+{
+ int ret, diff = 0;
+
+ GIT_ASSERT_ARG(client);
+ GIT_ASSERT_ARG(request);
+
+ GIT_ASSERT(request->url);
+
+ if ((ret = server_setup_from_url(&client->server, request->url)) < 0)
+ return ret;
+
+ diff |= ret;
+
+ if (request->proxy &&
+ (ret = server_setup_from_url(&client->proxy, request->proxy)) < 0)
+ return ret;
+
+ diff |= ret;
+
+ if (diff) {
+ free_auth_context(&client->server);
+ free_auth_context(&client->proxy);
+
+ client->connected = 0;
+ }
+
+ return 0;
+}
+
+GIT_INLINE(int) server_create_stream(git_http_server *server)
+{
+ git_net_url *url = &server->url;
+
+ if (strcasecmp(url->scheme, "https") == 0)
+ return git_tls_stream_new(&server->stream, url->host, url->port);
+ else if (strcasecmp(url->scheme, "http") == 0)
+ return git_socket_stream_new(&server->stream, url->host, url->port);
+
+ git_error_set(GIT_ERROR_HTTP, "unknown http scheme '%s'", url->scheme);
+ return -1;
+}
+
+GIT_INLINE(void) save_early_response(
+ git_http_client *client,
+ git_http_response *response)
+{
+ /* Buffer the response so we can return it in read_response */
+ client->state = HAS_EARLY_RESPONSE;
+
+ memcpy(&client->early_response, response, sizeof(git_http_response));
+ memset(response, 0, sizeof(git_http_response));
+}
+
+static int proxy_connect(
+ git_http_client *client,
+ git_http_request *request)
+{
+ git_http_response response = {0};
+ int error;
+
+ if (!client->proxy_connected || !client->keepalive) {
+ git_trace(GIT_TRACE_DEBUG, "Connecting to proxy %s port %s",
+ client->proxy.url.host, client->proxy.url.port);
+
+ if ((error = server_create_stream(&client->proxy)) < 0 ||
+ (error = server_connect_stream(&client->proxy,
+ client->opts.proxy_certificate_check_cb,
+ client->opts.proxy_certificate_check_payload)) < 0)
+ goto done;
+
+ client->proxy_connected = 1;
+ }
+
+ client->current_server = PROXY;
+ client->state = SENDING_REQUEST;
+
+ if ((error = generate_connect_request(client, request)) < 0 ||
+ (error = client_write_request(client)) < 0)
+ goto done;
+
+ client->state = SENT_REQUEST;
+
+ if ((error = git_http_client_read_response(&response, client)) < 0 ||
+ (error = git_http_client_skip_body(client)) < 0)
+ goto done;
+
+ GIT_ASSERT(client->state == DONE);
+
+ if (response.status == GIT_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED) {
+ save_early_response(client, &response);
+
+ error = GIT_RETRY;
+ goto done;
+ } else if (response.status != GIT_HTTP_STATUS_OK) {
+ git_error_set(GIT_ERROR_HTTP, "proxy returned unexpected status: %d", response.status);
+ error = -1;
+ goto done;
+ }
+
+ reset_parser(client);
+ client->state = NONE;
+
+done:
+ git_http_response_dispose(&response);
+ return error;
+}
+
+static int server_connect(git_http_client *client)
+{
+ git_net_url *url = &client->server.url;
+ git_transport_certificate_check_cb cert_cb;
+ void *cert_payload;
+ int error;
+
+ client->current_server = SERVER;
+
+ if (client->proxy.stream)
+ error = git_tls_stream_wrap(&client->server.stream, client->proxy.stream, url->host);
+ else
+ error = server_create_stream(&client->server);
+
+ if (error < 0)
+ goto done;
+
+ cert_cb = client->opts.server_certificate_check_cb;
+ cert_payload = client->opts.server_certificate_check_payload;
+
+ error = server_connect_stream(&client->server, cert_cb, cert_payload);
+
+done:
+ return error;
+}
+
+GIT_INLINE(void) close_stream(git_http_server *server)
+{
+ if (server->stream) {
+ git_stream_close(server->stream);
+ git_stream_free(server->stream);
+ server->stream = NULL;
+ }
+}
+
+static int http_client_connect(
+ git_http_client *client,
+ git_http_request *request)
+{
+ bool use_proxy = false;
+ int error;
+
+ if ((error = setup_hosts(client, request)) < 0)
+ goto on_error;
+
+ /* We're connected to our destination server; no need to reconnect */
+ if (client->connected && client->keepalive &&
+ (client->state == NONE || client->state == DONE))
+ return 0;
+
+ client->connected = 0;
+ client->request_count = 0;
+
+ close_stream(&client->server);
+ reset_auth_connection(&client->server);
+
+ reset_parser(client);
+
+ /* Reconnect to the proxy if necessary. */
+ use_proxy = use_connect_proxy(client);
+
+ if (use_proxy) {
+ if (!client->proxy_connected || !client->keepalive ||
+ (client->state != NONE && client->state != DONE)) {
+ close_stream(&client->proxy);
+ reset_auth_connection(&client->proxy);
+
+ client->proxy_connected = 0;
+ }
+
+ if ((error = proxy_connect(client, request)) < 0)
+ goto on_error;
+ }
+
+ git_trace(GIT_TRACE_DEBUG, "Connecting to remote %s port %s",
+ client->server.url.host, client->server.url.port);
+
+ if ((error = server_connect(client)) < 0)
+ goto on_error;
+
+ client->connected = 1;
+ return error;
+
+on_error:
+ if (error != GIT_RETRY)
+ close_stream(&client->proxy);
+
+ close_stream(&client->server);
+ return error;
+}
+
+GIT_INLINE(int) client_read(git_http_client *client)
+{
+ http_parser_context *parser_context = client->parser.data;
+ git_stream *stream;
+ char *buf = client->read_buf.ptr + client->read_buf.size;
+ size_t max_len;
+ ssize_t read_len;
+
+ stream = client->current_server == PROXY ?
+ client->proxy.stream : client->server.stream;
+
+ /*
+ * We use a git_str for convenience, but statically allocate it and
+ * don't resize. Limit our consumption to INT_MAX since calling
+ * functions use an int return type to return number of bytes read.
+ */
+ max_len = client->read_buf.asize - client->read_buf.size;
+ max_len = min(max_len, INT_MAX);
+
+ if (parser_context->output_size)
+ max_len = min(max_len, parser_context->output_size);
+
+ if (max_len == 0) {
+ git_error_set(GIT_ERROR_HTTP, "no room in output buffer");
+ return -1;
+ }
+
+ read_len = git_stream_read(stream, buf, max_len);
+
+ if (read_len >= 0) {
+ client->read_buf.size += read_len;
+
+ git_trace(GIT_TRACE_TRACE, "Received:\n%.*s",
+ (int)read_len, buf);
+ }
+
+ return (int)read_len;
+}
+
+static bool parser_settings_initialized;
+static http_parser_settings parser_settings;
+
+GIT_INLINE(http_parser_settings *) http_client_parser_settings(void)
+{
+ if (!parser_settings_initialized) {
+ parser_settings.on_header_field = on_header_field;
+ parser_settings.on_header_value = on_header_value;
+ parser_settings.on_headers_complete = on_headers_complete;
+ parser_settings.on_body = on_body;
+ parser_settings.on_message_complete = on_message_complete;
+
+ parser_settings_initialized = true;
+ }
+
+ return &parser_settings;
+}
+
+GIT_INLINE(int) client_read_and_parse(git_http_client *client)
+{
+ http_parser *parser = &client->parser;
+ http_parser_context *ctx = (http_parser_context *) parser->data;
+ unsigned char http_errno;
+ int read_len;
+ size_t parsed_len;
+
+ /*
+ * If we have data in our read buffer, that means we stopped early
+ * when parsing headers. Use the data in the read buffer instead of
+ * reading more from the socket.
+ */
+ if (!client->read_buf.size && (read_len = client_read(client)) < 0)
+ return read_len;
+
+ parsed_len = http_parser_execute(parser,
+ http_client_parser_settings(),
+ client->read_buf.ptr,
+ client->read_buf.size);
+ http_errno = client->parser.http_errno;
+
+ if (parsed_len > INT_MAX) {
+ git_error_set(GIT_ERROR_HTTP, "unexpectedly large parse");
+ return -1;
+ }
+
+ if (ctx->parse_status == PARSE_STATUS_ERROR) {
+ client->connected = 0;
+ return ctx->error ? ctx->error : -1;
+ }
+
+ /*
+ * If we finished reading the headers or body, we paused parsing.
+ * Otherwise the parser will start filling the body, or even parse
+ * a new response if the server pipelined us multiple responses.
+ * (This can happen in response to an expect/continue request,
+ * where the server gives you a 100 and 200 simultaneously.)
+ */
+ if (http_errno == HPE_PAUSED) {
+ /*
+ * http-parser has a "feature" where it will not deliver the
+ * final byte when paused in a callback. Consume that byte.
+ * https://github.com/nodejs/http-parser/issues/97
+ */
+ GIT_ASSERT(client->read_buf.size > parsed_len);
+
+ http_parser_pause(parser, 0);
+
+ parsed_len += http_parser_execute(parser,
+ http_client_parser_settings(),
+ client->read_buf.ptr + parsed_len,
+ 1);
+ }
+
+ /* Most failures will be reported in http_errno */
+ else if (parser->http_errno != HPE_OK) {
+ git_error_set(GIT_ERROR_HTTP, "http parser error: %s",
+ http_errno_description(http_errno));
+ return -1;
+ }
+
+ /* Otherwise we should have consumed the entire buffer. */
+ else if (parsed_len != client->read_buf.size) {
+ git_error_set(GIT_ERROR_HTTP,
+ "http parser did not consume entire buffer: %s",
+ http_errno_description(http_errno));
+ return -1;
+ }
+
+ /* recv returned 0, the server hung up on us */
+ else if (!parsed_len) {
+ git_error_set(GIT_ERROR_HTTP, "unexpected EOF");
+ return -1;
+ }
+
+ git_str_consume_bytes(&client->read_buf, parsed_len);
+
+ return (int)parsed_len;
+}
+
+/*
+ * See if we've consumed the entire response body. If the client was
+ * reading the body but did not consume it entirely, it's possible that
+ * they knew that the stream had finished (in a git response, seeing a
+ * final flush) and stopped reading. But if the response was chunked,
+ * we may have not consumed the final chunk marker. Consume it to
+ * ensure that we don't have it waiting in our socket. If there's
+ * more than just a chunk marker, close the connection.
+ */
+static void complete_response_body(git_http_client *client)
+{
+ http_parser_context parser_context = {0};
+
+ /* If we're not keeping alive, don't bother. */
+ if (!client->keepalive) {
+ client->connected = 0;
+ goto done;
+ }
+
+ parser_context.client = client;
+ client->parser.data = &parser_context;
+
+ /* If there was an error, just close the connection. */
+ if (client_read_and_parse(client) < 0 ||
+ parser_context.error != HPE_OK ||
+ (parser_context.parse_status != PARSE_STATUS_OK &&
+ parser_context.parse_status != PARSE_STATUS_NO_OUTPUT)) {
+ git_error_clear();
+ client->connected = 0;
+ }
+
+done:
+ git_str_clear(&client->read_buf);
+}
+
+int git_http_client_send_request(
+ git_http_client *client,
+ git_http_request *request)
+{
+ git_http_response response = {0};
+ int error = -1;
+
+ GIT_ASSERT_ARG(client);
+ GIT_ASSERT_ARG(request);
+
+ /* If the client did not finish reading, clean up the stream. */
+ if (client->state == READING_BODY)
+ complete_response_body(client);
+
+ /* If we're waiting for proxy auth, don't sending more requests. */
+ if (client->state == HAS_EARLY_RESPONSE)
+ return 0;
+
+ if (git_trace_level() >= GIT_TRACE_DEBUG) {
+ git_str url = GIT_STR_INIT;
+ git_net_url_fmt(&url, request->url);
+ git_trace(GIT_TRACE_DEBUG, "Sending %s request to %s",
+ name_for_method(request->method),
+ url.ptr ? url.ptr : "<invalid>");
+ git_str_dispose(&url);
+ }
+
+ if ((error = http_client_connect(client, request)) < 0 ||
+ (error = generate_request(client, request)) < 0 ||
+ (error = client_write_request(client)) < 0)
+ goto done;
+
+ client->state = SENT_REQUEST;
+
+ if (request->expect_continue) {
+ if ((error = git_http_client_read_response(&response, client)) < 0 ||
+ (error = git_http_client_skip_body(client)) < 0)
+ goto done;
+
+ error = 0;
+
+ if (response.status != GIT_HTTP_STATUS_CONTINUE) {
+ save_early_response(client, &response);
+ goto done;
+ }
+ }
+
+ if (request->content_length || request->chunked) {
+ client->state = SENDING_BODY;
+ client->request_body_len = request->content_length;
+ client->request_body_remain = request->content_length;
+ client->request_chunked = request->chunked;
+ }
+
+ reset_parser(client);
+
+done:
+ if (error == GIT_RETRY)
+ error = 0;
+
+ git_http_response_dispose(&response);
+ return error;
+}
+
+bool git_http_client_has_response(git_http_client *client)
+{
+ return (client->state == HAS_EARLY_RESPONSE ||
+ client->state > SENT_REQUEST);
+}
+
+int git_http_client_send_body(
+ git_http_client *client,
+ const char *buffer,
+ size_t buffer_len)
+{
+ git_http_server *server;
+ git_str hdr = GIT_STR_INIT;
+ int error;
+
+ GIT_ASSERT_ARG(client);
+
+ /* If we're waiting for proxy auth, don't sending more requests. */
+ if (client->state == HAS_EARLY_RESPONSE)
+ return 0;
+
+ if (client->state != SENDING_BODY) {
+ git_error_set(GIT_ERROR_HTTP, "client is in invalid state");
+ return -1;
+ }
+
+ if (!buffer_len)
+ return 0;
+
+ server = &client->server;
+
+ if (client->request_body_len) {
+ GIT_ASSERT(buffer_len <= client->request_body_remain);
+
+ if ((error = stream_write(server, buffer, buffer_len)) < 0)
+ goto done;
+
+ client->request_body_remain -= buffer_len;
+ } else {
+ if ((error = git_str_printf(&hdr, "%" PRIxZ "\r\n", buffer_len)) < 0 ||
+ (error = stream_write(server, hdr.ptr, hdr.size)) < 0 ||
+ (error = stream_write(server, buffer, buffer_len)) < 0 ||
+ (error = stream_write(server, "\r\n", 2)) < 0)
+ goto done;
+ }
+
+done:
+ git_str_dispose(&hdr);
+ return error;
+}
+
+static int complete_request(git_http_client *client)
+{
+ int error = 0;
+
+ GIT_ASSERT_ARG(client);
+ GIT_ASSERT(client->state == SENDING_BODY);
+
+ if (client->request_body_len && client->request_body_remain) {
+ git_error_set(GIT_ERROR_HTTP, "truncated write");
+ error = -1;
+ } else if (client->request_chunked) {
+ error = stream_write(&client->server, "0\r\n\r\n", 5);
+ }
+
+ client->state = SENT_REQUEST;
+ return error;
+}
+
+int git_http_client_read_response(
+ git_http_response *response,
+ git_http_client *client)
+{
+ http_parser_context parser_context = {0};
+ int error;
+
+ GIT_ASSERT_ARG(response);
+ GIT_ASSERT_ARG(client);
+
+ if (client->state == SENDING_BODY) {
+ if ((error = complete_request(client)) < 0)
+ goto done;
+ }
+
+ if (client->state == HAS_EARLY_RESPONSE) {
+ memcpy(response, &client->early_response, sizeof(git_http_response));
+ memset(&client->early_response, 0, sizeof(git_http_response));
+ client->state = DONE;
+ return 0;
+ }
+
+ if (client->state != SENT_REQUEST) {
+ git_error_set(GIT_ERROR_HTTP, "client is in invalid state");
+ error = -1;
+ goto done;
+ }
+
+ git_http_response_dispose(response);
+
+ if (client->current_server == PROXY) {
+ git_vector_free_deep(&client->proxy.auth_challenges);
+ } else if(client->current_server == SERVER) {
+ git_vector_free_deep(&client->server.auth_challenges);
+ }
+
+ client->state = READING_RESPONSE;
+ client->keepalive = 0;
+ client->parser.data = &parser_context;
+
+ parser_context.client = client;
+ parser_context.response = response;
+
+ while (client->state == READING_RESPONSE) {
+ if ((error = client_read_and_parse(client)) < 0)
+ goto done;
+ }
+
+ GIT_ASSERT(client->state == READING_BODY || client->state == DONE);
+
+done:
+ git_str_dispose(&parser_context.parse_header_name);
+ git_str_dispose(&parser_context.parse_header_value);
+
+ return error;
+}
+
+int git_http_client_read_body(
+ git_http_client *client,
+ char *buffer,
+ size_t buffer_size)
+{
+ http_parser_context parser_context = {0};
+ int error = 0;
+
+ if (client->state == DONE)
+ return 0;
+
+ if (client->state != READING_BODY) {
+ git_error_set(GIT_ERROR_HTTP, "client is in invalid state");
+ return -1;
+ }
+
+ /*
+ * Now we'll read from the socket and http_parser will pipeline the
+ * data directly to the client.
+ */
+
+ parser_context.client = client;
+ parser_context.output_buf = buffer;
+ parser_context.output_size = buffer_size;
+
+ client->parser.data = &parser_context;
+
+ /*
+ * Clients expect to get a non-zero amount of data from us,
+ * so we either block until we have data to return, until we
+ * hit EOF or there's an error. Do this in a loop, since we
+ * may end up reading only some stream metadata (like chunk
+ * information).
+ */
+ while (!parser_context.output_written) {
+ error = client_read_and_parse(client);
+
+ if (error <= 0)
+ goto done;
+
+ if (client->state == DONE)
+ break;
+ }
+
+ GIT_ASSERT(parser_context.output_written <= INT_MAX);
+ error = (int)parser_context.output_written;
+
+done:
+ if (error < 0)
+ client->connected = 0;
+
+ return error;
+}
+
+int git_http_client_skip_body(git_http_client *client)
+{
+ http_parser_context parser_context = {0};
+ int error;
+
+ if (client->state == DONE)
+ return 0;
+
+ if (client->state != READING_BODY) {
+ git_error_set(GIT_ERROR_HTTP, "client is in invalid state");
+ return -1;
+ }
+
+ parser_context.client = client;
+ client->parser.data = &parser_context;
+
+ do {
+ error = client_read_and_parse(client);
+
+ if (parser_context.error != HPE_OK ||
+ (parser_context.parse_status != PARSE_STATUS_OK &&
+ parser_context.parse_status != PARSE_STATUS_NO_OUTPUT)) {
+ git_error_set(GIT_ERROR_HTTP,
+ "unexpected data handled in callback");
+ error = -1;
+ }
+ } while (error >= 0 && client->state != DONE);
+
+ if (error < 0)
+ client->connected = 0;
+
+ return error;
+}
+
+/*
+ * Create an http_client capable of communicating with the given remote
+ * host.
+ */
+int git_http_client_new(
+ git_http_client **out,
+ git_http_client_options *opts)
+{
+ git_http_client *client;
+
+ GIT_ASSERT_ARG(out);
+
+ client = git__calloc(1, sizeof(git_http_client));
+ GIT_ERROR_CHECK_ALLOC(client);
+
+ git_str_init(&client->read_buf, GIT_READ_BUFFER_SIZE);
+ GIT_ERROR_CHECK_ALLOC(client->read_buf.ptr);
+
+ if (opts)
+ memcpy(&client->opts, opts, sizeof(git_http_client_options));
+
+ *out = client;
+ return 0;
+}
+
+/* Update the options of an existing httpclient instance. */
+void git_http_client_set_options(
+ git_http_client *client,
+ git_http_client_options *opts)
+{
+ if (opts)
+ memcpy(&client->opts, opts, sizeof(git_http_client_options));
+}
+
+GIT_INLINE(void) http_server_close(git_http_server *server)
+{
+ if (server->stream) {
+ git_stream_close(server->stream);
+ git_stream_free(server->stream);
+ server->stream = NULL;
+ }
+
+ git_net_url_dispose(&server->url);
+
+ git_vector_free_deep(&server->auth_challenges);
+ free_auth_context(server);
+}
+
+static void http_client_close(git_http_client *client)
+{
+ http_server_close(&client->server);
+ http_server_close(&client->proxy);
+
+ git_str_dispose(&client->request_msg);
+
+ client->state = 0;
+ client->request_count = 0;
+ client->connected = 0;
+ client->keepalive = 0;
+}
+
+void git_http_client_free(git_http_client *client)
+{
+ if (!client)
+ return;
+
+ http_client_close(client);
+ git_str_dispose(&client->read_buf);
+ git__free(client);
+}
diff --git a/src/libgit2/transports/httpclient.h b/src/libgit2/transports/httpclient.h
new file mode 100644
index 0000000..22c4dd0
--- /dev/null
+++ b/src/libgit2/transports/httpclient.h
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_transports_httpclient_h__
+#define INCLUDE_transports_httpclient_h__
+
+#include "common.h"
+#include "net.h"
+
+#define GIT_HTTP_STATUS_CONTINUE 100
+#define GIT_HTTP_STATUS_OK 200
+#define GIT_HTTP_MOVED_PERMANENTLY 301
+#define GIT_HTTP_FOUND 302
+#define GIT_HTTP_SEE_OTHER 303
+#define GIT_HTTP_TEMPORARY_REDIRECT 307
+#define GIT_HTTP_PERMANENT_REDIRECT 308
+#define GIT_HTTP_STATUS_UNAUTHORIZED 401
+#define GIT_HTTP_STATUS_PROXY_AUTHENTICATION_REQUIRED 407
+
+typedef struct git_http_client git_http_client;
+
+/** Method for the HTTP request */
+typedef enum {
+ GIT_HTTP_METHOD_GET,
+ GIT_HTTP_METHOD_POST,
+ GIT_HTTP_METHOD_CONNECT
+} git_http_method;
+
+/** An HTTP request */
+typedef struct {
+ git_http_method method; /**< Method for the request */
+ git_net_url *url; /**< Full request URL */
+ git_net_url *proxy; /**< Proxy to use */
+
+ /* Headers */
+ const char *accept; /**< Contents of the Accept header */
+ const char *content_type; /**< Content-Type header (for POST) */
+ git_credential *credentials; /**< Credentials to authenticate with */
+ git_credential *proxy_credentials; /**< Credentials for proxy */
+ git_strarray *custom_headers; /**< Additional headers to deliver */
+
+ /* To POST a payload, either set content_length OR set chunked. */
+ size_t content_length; /**< Length of the POST body */
+ unsigned chunked : 1, /**< Post with chunking */
+ expect_continue : 1; /**< Use expect/continue negotiation */
+} git_http_request;
+
+typedef struct {
+ int status;
+
+ /* Headers */
+ char *content_type;
+ size_t content_length;
+ char *location;
+
+ /* Authentication headers */
+ unsigned server_auth_schemetypes; /**< Schemes requested by remote */
+ unsigned server_auth_credtypes; /**< Supported cred types for remote */
+
+ unsigned proxy_auth_schemetypes; /**< Schemes requested by proxy */
+ unsigned proxy_auth_credtypes; /**< Supported cred types for proxy */
+
+ unsigned chunked : 1, /**< Response body is chunked */
+ resend_credentials : 1; /**< Resend with authentication */
+} git_http_response;
+
+typedef struct {
+ /** Certificate check callback for the remote */
+ git_transport_certificate_check_cb server_certificate_check_cb;
+ void *server_certificate_check_payload;
+
+ /** Certificate check callback for the proxy */
+ git_transport_certificate_check_cb proxy_certificate_check_cb;
+ void *proxy_certificate_check_payload;
+} git_http_client_options;
+
+/**
+ * Create a new httpclient instance with the given options.
+ *
+ * @param out pointer to receive the new instance
+ * @param opts options to create the client with or NULL for defaults
+ */
+extern int git_http_client_new(
+ git_http_client **out,
+ git_http_client_options *opts);
+
+/**
+ * Update the options of an existing httpclient instance.
+ *
+ * @param client the httpclient instance to modify
+ * @param opts new options or NULL to keep existing options
+ */
+extern void git_http_client_set_options(
+ git_http_client *client,
+ git_http_client_options *opts);
+
+/*
+ * Sends a request to the host specified by the request URL. If the
+ * method is POST, either the content_length or the chunked flag must
+ * be specified. The body should be provided in subsequent calls to
+ * git_http_client_send_body.
+ *
+ * @param client the client to write the request to
+ * @param request the request to send
+ */
+extern int git_http_client_send_request(
+ git_http_client *client,
+ git_http_request *request);
+
+/*
+ * After sending a request, there may already be a response to read --
+ * either because there was a non-continue response to an expect: continue
+ * request, or because the server pipelined a response to us before we even
+ * sent the request. Examine the state.
+ *
+ * @param client the client to examine
+ * @return true if there's already a response to read, false otherwise
+ */
+extern bool git_http_client_has_response(git_http_client *client);
+
+/**
+ * Sends the given buffer to the remote as part of the request body. The
+ * request must have specified either a content_length or the chunked flag.
+ *
+ * @param client the client to write the request body to
+ * @param buffer the request body
+ * @param buffer_len number of bytes of the buffer to send
+ */
+extern int git_http_client_send_body(
+ git_http_client *client,
+ const char *buffer,
+ size_t buffer_len);
+
+/**
+ * Reads the headers of a response to a request. This will consume the
+ * entirety of the headers of a response from the server. The body (if any)
+ * can be read by calling git_http_client_read_body. Callers must free
+ * the response with git_http_response_dispose.
+ *
+ * @param response pointer to the response object to fill
+ * @param client the client to read the response from
+ */
+extern int git_http_client_read_response(
+ git_http_response *response,
+ git_http_client *client);
+
+/**
+ * Reads some or all of the body of a response. At most buffer_size (or
+ * INT_MAX) bytes will be read and placed into the buffer provided. The
+ * number of bytes read will be returned, or 0 to indicate that the end of
+ * the body has been read.
+ *
+ * @param client the client to read the response from
+ * @param buffer pointer to the buffer to fill
+ * @param buffer_size the maximum number of bytes to read
+ * @return the number of bytes read, 0 on end of body, or error code
+ */
+extern int git_http_client_read_body(
+ git_http_client *client,
+ char *buffer,
+ size_t buffer_size);
+
+/**
+ * Reads all of the (remainder of the) body of the response and ignores it.
+ * None of the data from the body will be returned to the caller.
+ *
+ * @param client the client to read the response from
+ * @return 0 or an error code
+ */
+extern int git_http_client_skip_body(git_http_client *client);
+
+/**
+ * Examines the status code of the response to determine if it is a
+ * redirect of any type (eg, 301, 302, etc).
+ *
+ * @param response the response to inspect
+ * @return true if the response is a redirect, false otherwise
+ */
+extern bool git_http_response_is_redirect(git_http_response *response);
+
+/**
+ * Frees any memory associated with the response.
+ *
+ * @param response the response to free
+ */
+extern void git_http_response_dispose(git_http_response *response);
+
+/**
+ * Frees any memory associated with the client. If any sockets are open,
+ * they will be closed.
+ *
+ * @param client the client to free
+ */
+extern void git_http_client_free(git_http_client *client);
+
+#endif
diff --git a/src/libgit2/transports/local.c b/src/libgit2/transports/local.c
new file mode 100644
index 0000000..64c21af
--- /dev/null
+++ b/src/libgit2/transports/local.c
@@ -0,0 +1,777 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+
+#include "pack-objects.h"
+#include "refs.h"
+#include "posix.h"
+#include "fs_path.h"
+#include "repository.h"
+#include "odb.h"
+#include "push.h"
+#include "remote.h"
+#include "proxy.h"
+
+#include "git2/types.h"
+#include "git2/net.h"
+#include "git2/repository.h"
+#include "git2/object.h"
+#include "git2/tag.h"
+#include "git2/transport.h"
+#include "git2/revwalk.h"
+#include "git2/odb_backend.h"
+#include "git2/pack.h"
+#include "git2/commit.h"
+#include "git2/revparse.h"
+#include "git2/sys/remote.h"
+
+typedef struct {
+ git_transport parent;
+ git_remote *owner;
+ char *url;
+ int direction;
+ git_atomic32 cancelled;
+ git_repository *repo;
+ git_remote_connect_options connect_opts;
+ git_vector refs;
+ unsigned connected : 1,
+ have_refs : 1;
+} transport_local;
+
+static void free_head(git_remote_head *head)
+{
+ git__free(head->name);
+ git__free(head->symref_target);
+ git__free(head);
+}
+
+static void free_heads(git_vector *heads)
+{
+ git_remote_head *head;
+ size_t i;
+
+ git_vector_foreach(heads, i, head)
+ free_head(head);
+
+ git_vector_free(heads);
+}
+
+static int add_ref(transport_local *t, const char *name)
+{
+ const char peeled[] = "^{}";
+ git_reference *ref, *resolved;
+ git_remote_head *head;
+ git_oid obj_id;
+ git_object *obj = NULL, *target = NULL;
+ git_str buf = GIT_STR_INIT;
+ int error;
+
+ if ((error = git_reference_lookup(&ref, t->repo, name)) < 0)
+ return error;
+
+ error = git_reference_resolve(&resolved, ref);
+ if (error < 0) {
+ git_reference_free(ref);
+ if (!strcmp(name, GIT_HEAD_FILE) && error == GIT_ENOTFOUND) {
+ /* This is actually okay. Empty repos often have a HEAD that
+ * points to a nonexistent "refs/heads/master". */
+ git_error_clear();
+ return 0;
+ }
+ return error;
+ }
+
+ git_oid_cpy(&obj_id, git_reference_target(resolved));
+ git_reference_free(resolved);
+
+ head = git__calloc(1, sizeof(git_remote_head));
+ GIT_ERROR_CHECK_ALLOC(head);
+
+ head->name = git__strdup(name);
+ GIT_ERROR_CHECK_ALLOC(head->name);
+
+ git_oid_cpy(&head->oid, &obj_id);
+
+ if (git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC) {
+ head->symref_target = git__strdup(git_reference_symbolic_target(ref));
+ GIT_ERROR_CHECK_ALLOC(head->symref_target);
+ }
+ git_reference_free(ref);
+
+ if ((error = git_vector_insert(&t->refs, head)) < 0) {
+ free_head(head);
+ return error;
+ }
+
+ /* If it's not a tag, we don't need to try to peel it */
+ if (git__prefixcmp(name, GIT_REFS_TAGS_DIR))
+ return 0;
+
+ if ((error = git_object_lookup(&obj, t->repo, &head->oid, GIT_OBJECT_ANY)) < 0)
+ return error;
+
+ head = NULL;
+
+ /* If it's not an annotated tag, or if we're mocking
+ * git-receive-pack, just get out */
+ if (git_object_type(obj) != GIT_OBJECT_TAG ||
+ t->direction != GIT_DIRECTION_FETCH) {
+ git_object_free(obj);
+ return 0;
+ }
+
+ /* And if it's a tag, peel it, and add it to the list */
+ head = git__calloc(1, sizeof(git_remote_head));
+ GIT_ERROR_CHECK_ALLOC(head);
+
+ if (git_str_join(&buf, 0, name, peeled) < 0) {
+ free_head(head);
+ return -1;
+ }
+ head->name = git_str_detach(&buf);
+
+ if (!(error = git_tag_peel(&target, (git_tag *)obj))) {
+ git_oid_cpy(&head->oid, git_object_id(target));
+
+ if ((error = git_vector_insert(&t->refs, head)) < 0) {
+ free_head(head);
+ }
+ }
+
+ git_object_free(obj);
+ git_object_free(target);
+
+ return error;
+}
+
+static int store_refs(transport_local *t)
+{
+ size_t i;
+ git_remote_head *head;
+ git_strarray ref_names = {0};
+
+ GIT_ASSERT_ARG(t);
+
+ if (git_reference_list(&ref_names, t->repo) < 0)
+ goto on_error;
+
+ /* Clear all heads we might have fetched in a previous connect */
+ git_vector_foreach(&t->refs, i, head) {
+ git__free(head->name);
+ git__free(head);
+ }
+
+ /* Clear the vector so we can reuse it */
+ git_vector_clear(&t->refs);
+
+ /* Sort the references first */
+ git__tsort((void **)ref_names.strings, ref_names.count, &git__strcmp_cb);
+
+ /* Add HEAD iff direction is fetch */
+ if (t->direction == GIT_DIRECTION_FETCH && add_ref(t, GIT_HEAD_FILE) < 0)
+ goto on_error;
+
+ for (i = 0; i < ref_names.count; ++i) {
+ if (add_ref(t, ref_names.strings[i]) < 0)
+ goto on_error;
+ }
+
+ t->have_refs = 1;
+ git_strarray_dispose(&ref_names);
+ return 0;
+
+on_error:
+ git_vector_free(&t->refs);
+ git_strarray_dispose(&ref_names);
+ return -1;
+}
+
+/*
+ * Try to open the url as a git directory. The direction doesn't
+ * matter in this case because we're calculating the heads ourselves.
+ */
+static int local_connect(
+ git_transport *transport,
+ const char *url,
+ int direction,
+ const git_remote_connect_options *connect_opts)
+{
+ git_repository *repo;
+ int error;
+ transport_local *t = (transport_local *)transport;
+ const char *path;
+ git_str buf = GIT_STR_INIT;
+
+ if (t->connected)
+ return 0;
+
+ if (git_remote_connect_options_normalize(&t->connect_opts, t->owner->repo, connect_opts) < 0)
+ return -1;
+
+ free_heads(&t->refs);
+
+ t->url = git__strdup(url);
+ GIT_ERROR_CHECK_ALLOC(t->url);
+ t->direction = direction;
+
+ /* 'url' may be a url or path; convert to a path */
+ if ((error = git_fs_path_from_url_or_path(&buf, url)) < 0) {
+ git_str_dispose(&buf);
+ return error;
+ }
+ path = git_str_cstr(&buf);
+
+ error = git_repository_open(&repo, path);
+
+ git_str_dispose(&buf);
+
+ if (error < 0)
+ return -1;
+
+ t->repo = repo;
+
+ if (store_refs(t) < 0)
+ return -1;
+
+ t->connected = 1;
+
+ return 0;
+}
+
+static int local_set_connect_opts(
+ git_transport *transport,
+ const git_remote_connect_options *connect_opts)
+{
+ transport_local *t = (transport_local *)transport;
+
+ if (!t->connected) {
+ git_error_set(GIT_ERROR_NET, "cannot reconfigure a transport that is not connected");
+ return -1;
+ }
+
+ return git_remote_connect_options_normalize(&t->connect_opts, t->owner->repo, connect_opts);
+}
+
+static int local_capabilities(unsigned int *capabilities, git_transport *transport)
+{
+ GIT_UNUSED(transport);
+
+ *capabilities = GIT_REMOTE_CAPABILITY_TIP_OID |
+ GIT_REMOTE_CAPABILITY_REACHABLE_OID;
+ return 0;
+}
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+static int local_oid_type(git_oid_t *out, git_transport *transport)
+{
+ transport_local *t = (transport_local *)transport;
+
+ *out = t->repo->oid_type;
+
+ return 0;
+}
+#endif
+
+static int local_ls(const git_remote_head ***out, size_t *size, git_transport *transport)
+{
+ transport_local *t = (transport_local *)transport;
+
+ if (!t->have_refs) {
+ git_error_set(GIT_ERROR_NET, "the transport has not yet loaded the refs");
+ return -1;
+ }
+
+ *out = (const git_remote_head **)t->refs.contents;
+ *size = t->refs.length;
+
+ return 0;
+}
+
+static int local_negotiate_fetch(
+ git_transport *transport,
+ git_repository *repo,
+ const git_fetch_negotiation *wants)
+{
+ transport_local *t = (transport_local*)transport;
+ git_remote_head *rhead;
+ unsigned int i;
+
+ GIT_UNUSED(wants);
+
+ /* Fill in the loids */
+ git_vector_foreach(&t->refs, i, rhead) {
+ git_object *obj;
+
+ int error = git_revparse_single(&obj, repo, rhead->name);
+ if (!error)
+ git_oid_cpy(&rhead->loid, git_object_id(obj));
+ else if (error != GIT_ENOTFOUND)
+ return error;
+ else
+ git_error_clear();
+ git_object_free(obj);
+ }
+
+ return 0;
+}
+
+static int local_shallow_roots(
+ git_oidarray *out,
+ git_transport *transport)
+{
+ GIT_UNUSED(out);
+ GIT_UNUSED(transport);
+
+ return 0;
+}
+
+static int local_push_update_remote_ref(
+ git_repository *remote_repo,
+ const char *lref,
+ const char *rref,
+ git_oid *loid,
+ git_oid *roid)
+{
+ int error;
+ git_reference *remote_ref = NULL;
+
+ /* check for lhs, if it's empty it means to delete */
+ if (lref[0] != '\0') {
+ /* Create or update a ref */
+ error = git_reference_create(NULL, remote_repo, rref, loid,
+ !git_oid_is_zero(roid), NULL);
+ } else {
+ /* Delete a ref */
+ if ((error = git_reference_lookup(&remote_ref, remote_repo, rref)) < 0) {
+ if (error == GIT_ENOTFOUND)
+ error = 0;
+ return error;
+ }
+
+ error = git_reference_delete(remote_ref);
+ git_reference_free(remote_ref);
+ }
+
+ return error;
+}
+
+static int transfer_to_push_transfer(const git_indexer_progress *stats, void *payload)
+{
+ const git_remote_callbacks *cbs = payload;
+
+ if (!cbs || !cbs->push_transfer_progress)
+ return 0;
+
+ return cbs->push_transfer_progress(stats->received_objects, stats->total_objects, stats->received_bytes,
+ cbs->payload);
+}
+
+static int local_push(
+ git_transport *transport,
+ git_push *push)
+{
+ transport_local *t = (transport_local *)transport;
+ git_remote_callbacks *cbs = &t->connect_opts.callbacks;
+ git_repository *remote_repo = NULL;
+ push_spec *spec;
+ char *url = NULL;
+ const char *path;
+ git_str buf = GIT_STR_INIT, odb_path = GIT_STR_INIT;
+ int error;
+ size_t j;
+
+ /* 'push->remote->url' may be a url or path; convert to a path */
+ if ((error = git_fs_path_from_url_or_path(&buf, push->remote->url)) < 0) {
+ git_str_dispose(&buf);
+ return error;
+ }
+ path = git_str_cstr(&buf);
+
+ error = git_repository_open(&remote_repo, path);
+
+ git_str_dispose(&buf);
+
+ if (error < 0)
+ return error;
+
+ /* We don't currently support pushing locally to non-bare repos. Proper
+ non-bare repo push support would require checking configs to see if
+ we should override the default 'don't let this happen' behavior.
+
+ Note that this is only an issue when pushing to the current branch,
+ but we forbid all pushes just in case */
+ if (!remote_repo->is_bare) {
+ error = GIT_EBAREREPO;
+ git_error_set(GIT_ERROR_INVALID, "local push doesn't (yet) support pushing to non-bare repos.");
+ goto on_error;
+ }
+
+ if ((error = git_repository__item_path(&odb_path, remote_repo, GIT_REPOSITORY_ITEM_OBJECTS)) < 0
+ || (error = git_str_joinpath(&odb_path, odb_path.ptr, "pack")) < 0)
+ goto on_error;
+
+ error = git_packbuilder_write(push->pb, odb_path.ptr, 0, transfer_to_push_transfer, (void *) cbs);
+ git_str_dispose(&odb_path);
+
+ if (error < 0)
+ goto on_error;
+
+ push->unpack_ok = 1;
+
+ git_vector_foreach(&push->specs, j, spec) {
+ push_status *status;
+ const git_error *last;
+ char *ref = spec->refspec.dst;
+
+ status = git__calloc(1, sizeof(push_status));
+ if (!status)
+ goto on_error;
+
+ status->ref = git__strdup(ref);
+ if (!status->ref) {
+ git_push_status_free(status);
+ goto on_error;
+ }
+
+ error = local_push_update_remote_ref(remote_repo, spec->refspec.src, spec->refspec.dst,
+ &spec->loid, &spec->roid);
+
+ switch (error) {
+ case GIT_OK:
+ break;
+ case GIT_EINVALIDSPEC:
+ status->msg = git__strdup("funny refname");
+ break;
+ case GIT_ENOTFOUND:
+ status->msg = git__strdup("Remote branch not found to delete");
+ break;
+ default:
+ last = git_error_last();
+
+ if (last && last->message)
+ status->msg = git__strdup(last->message);
+ else
+ status->msg = git__strdup("Unspecified error encountered");
+ break;
+ }
+
+ /* failed to allocate memory for a status message */
+ if (error < 0 && !status->msg) {
+ git_push_status_free(status);
+ goto on_error;
+ }
+
+ /* failed to insert the ref update status */
+ if ((error = git_vector_insert(&push->status, status)) < 0) {
+ git_push_status_free(status);
+ goto on_error;
+ }
+ }
+
+ if (push->specs.length) {
+ url = git__strdup(t->url);
+
+ if (!url || t->parent.close(&t->parent) < 0 ||
+ t->parent.connect(&t->parent, url,
+ GIT_DIRECTION_PUSH, NULL))
+ goto on_error;
+ }
+
+ error = 0;
+
+on_error:
+ git_repository_free(remote_repo);
+ git__free(url);
+
+ return error;
+}
+
+typedef struct foreach_data {
+ git_indexer_progress *stats;
+ git_indexer_progress_cb progress_cb;
+ void *progress_payload;
+ git_odb_writepack *writepack;
+} foreach_data;
+
+static int foreach_cb(void *buf, size_t len, void *payload)
+{
+ foreach_data *data = (foreach_data*)payload;
+
+ data->stats->received_bytes += len;
+ return data->writepack->append(data->writepack, buf, len, data->stats);
+}
+
+static const char *counting_objects_fmt = "Counting objects %d\r";
+static const char *compressing_objects_fmt = "Compressing objects: %.0f%% (%d/%d)";
+
+static int local_counting(int stage, unsigned int current, unsigned int total, void *payload)
+{
+ git_str progress_info = GIT_STR_INIT;
+ transport_local *t = payload;
+ int error;
+
+ if (!t->connect_opts.callbacks.sideband_progress)
+ return 0;
+
+ if (stage == GIT_PACKBUILDER_ADDING_OBJECTS) {
+ git_str_printf(&progress_info, counting_objects_fmt, current);
+ } else if (stage == GIT_PACKBUILDER_DELTAFICATION) {
+ float perc = (((float) current) / total) * 100;
+ git_str_printf(&progress_info, compressing_objects_fmt, perc, current, total);
+ if (current == total)
+ git_str_printf(&progress_info, ", done\n");
+ else
+ git_str_putc(&progress_info, '\r');
+
+ }
+
+ if (git_str_oom(&progress_info))
+ return -1;
+
+ if (progress_info.size > INT_MAX) {
+ git_error_set(GIT_ERROR_NET, "remote sent overly large progress data");
+ git_str_dispose(&progress_info);
+ return -1;
+ }
+
+
+ error = t->connect_opts.callbacks.sideband_progress(
+ progress_info.ptr,
+ (int)progress_info.size,
+ t->connect_opts.callbacks.payload);
+
+ git_str_dispose(&progress_info);
+ return error;
+}
+
+static int foreach_reference_cb(git_reference *reference, void *payload)
+{
+ git_revwalk *walk = (git_revwalk *)payload;
+ int error;
+
+ if (git_reference_type(reference) != GIT_REFERENCE_DIRECT) {
+ git_reference_free(reference);
+ return 0;
+ }
+
+ error = git_revwalk_hide(walk, git_reference_target(reference));
+ /* The reference is in the local repository, so the target may not
+ * exist on the remote. It also may not be a commit. */
+ if (error == GIT_ENOTFOUND || error == GIT_ERROR_INVALID) {
+ git_error_clear();
+ error = 0;
+ }
+
+ git_reference_free(reference);
+
+ return error;
+}
+
+static int local_download_pack(
+ git_transport *transport,
+ git_repository *repo,
+ git_indexer_progress *stats)
+{
+ transport_local *t = (transport_local*)transport;
+ git_revwalk *walk = NULL;
+ git_remote_head *rhead;
+ unsigned int i;
+ int error = -1;
+ git_packbuilder *pack = NULL;
+ git_odb_writepack *writepack = NULL;
+ git_odb *odb = NULL;
+ git_str progress_info = GIT_STR_INIT;
+ foreach_data data = {0};
+
+ if ((error = git_revwalk_new(&walk, t->repo)) < 0)
+ goto cleanup;
+
+ git_revwalk_sorting(walk, GIT_SORT_TIME);
+
+ if ((error = git_packbuilder_new(&pack, t->repo)) < 0)
+ goto cleanup;
+
+ git_packbuilder_set_callbacks(pack, local_counting, t);
+
+ stats->total_objects = 0;
+ stats->indexed_objects = 0;
+ stats->received_objects = 0;
+ stats->received_bytes = 0;
+
+ git_vector_foreach(&t->refs, i, rhead) {
+ git_object *obj;
+ if ((error = git_object_lookup(&obj, t->repo, &rhead->oid, GIT_OBJECT_ANY)) < 0)
+ goto cleanup;
+
+ if (git_object_type(obj) == GIT_OBJECT_COMMIT) {
+ /* Revwalker includes only wanted commits */
+ error = git_revwalk_push(walk, &rhead->oid);
+ } else {
+ /* Tag or some other wanted object. Add it on its own */
+ error = git_packbuilder_insert_recur(pack, &rhead->oid, rhead->name);
+ }
+ git_object_free(obj);
+ if (error < 0)
+ goto cleanup;
+ }
+
+ if ((error = git_reference_foreach(repo, foreach_reference_cb, walk)))
+ goto cleanup;
+
+ if ((error = git_packbuilder_insert_walk(pack, walk)))
+ goto cleanup;
+
+ if (t->connect_opts.callbacks.sideband_progress) {
+ if ((error = git_str_printf(
+ &progress_info,
+ counting_objects_fmt,
+ git_packbuilder_object_count(pack))) < 0 ||
+ (error = t->connect_opts.callbacks.sideband_progress(
+ progress_info.ptr,
+ (int)progress_info.size,
+ t->connect_opts.callbacks.payload)) < 0)
+ goto cleanup;
+ }
+
+ /* Walk the objects, building a packfile */
+ if ((error = git_repository_odb__weakptr(&odb, repo)) < 0)
+ goto cleanup;
+
+ /* One last one with the newline */
+ if (t->connect_opts.callbacks.sideband_progress) {
+ git_str_clear(&progress_info);
+
+ if ((error = git_str_printf(
+ &progress_info,
+ counting_objects_fmt,
+ git_packbuilder_object_count(pack))) < 0 ||
+ (error = git_str_putc(&progress_info, '\n')) < 0 ||
+ (error = t->connect_opts.callbacks.sideband_progress(
+ progress_info.ptr,
+ (int)progress_info.size,
+ t->connect_opts.callbacks.payload)) < 0)
+ goto cleanup;
+ }
+
+ if ((error = git_odb_write_pack(
+ &writepack,
+ odb,
+ t->connect_opts.callbacks.transfer_progress,
+ t->connect_opts.callbacks.payload)) < 0)
+ goto cleanup;
+
+ /* Write the data to the ODB */
+ data.stats = stats;
+ data.progress_cb = t->connect_opts.callbacks.transfer_progress;
+ data.progress_payload = t->connect_opts.callbacks.payload;
+ data.writepack = writepack;
+
+ /* autodetect */
+ git_packbuilder_set_threads(pack, 0);
+
+ if ((error = git_packbuilder_foreach(pack, foreach_cb, &data)) != 0)
+ goto cleanup;
+
+ error = writepack->commit(writepack, stats);
+
+cleanup:
+ if (writepack) writepack->free(writepack);
+ git_str_dispose(&progress_info);
+ git_packbuilder_free(pack);
+ git_revwalk_free(walk);
+ return error;
+}
+
+static int local_is_connected(git_transport *transport)
+{
+ transport_local *t = (transport_local *)transport;
+
+ return t->connected;
+}
+
+static void local_cancel(git_transport *transport)
+{
+ transport_local *t = (transport_local *)transport;
+
+ git_atomic32_set(&t->cancelled, 1);
+}
+
+static int local_close(git_transport *transport)
+{
+ transport_local *t = (transport_local *)transport;
+
+ t->connected = 0;
+
+ if (t->repo) {
+ git_repository_free(t->repo);
+ t->repo = NULL;
+ }
+
+ if (t->url) {
+ git__free(t->url);
+ t->url = NULL;
+ }
+
+ return 0;
+}
+
+static void local_free(git_transport *transport)
+{
+ transport_local *t = (transport_local *)transport;
+
+ free_heads(&t->refs);
+
+ /* Close the transport, if it's still open. */
+ local_close(transport);
+
+ /* Free the transport */
+ git__free(t);
+}
+
+/**************
+ * Public API *
+ **************/
+
+int git_transport_local(git_transport **out, git_remote *owner, void *param)
+{
+ int error;
+ transport_local *t;
+
+ GIT_UNUSED(param);
+
+ t = git__calloc(1, sizeof(transport_local));
+ GIT_ERROR_CHECK_ALLOC(t);
+
+ t->parent.version = GIT_TRANSPORT_VERSION;
+ t->parent.connect = local_connect;
+ t->parent.set_connect_opts = local_set_connect_opts;
+ t->parent.capabilities = local_capabilities;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ t->parent.oid_type = local_oid_type;
+#endif
+ t->parent.negotiate_fetch = local_negotiate_fetch;
+ t->parent.shallow_roots = local_shallow_roots;
+ t->parent.download_pack = local_download_pack;
+ t->parent.push = local_push;
+ t->parent.close = local_close;
+ t->parent.free = local_free;
+ t->parent.ls = local_ls;
+ t->parent.is_connected = local_is_connected;
+ t->parent.cancel = local_cancel;
+
+ if ((error = git_vector_init(&t->refs, 0, NULL)) < 0) {
+ git__free(t);
+ return error;
+ }
+
+ t->owner = owner;
+
+ *out = (git_transport *) t;
+
+ return 0;
+}
diff --git a/src/libgit2/transports/smart.c b/src/libgit2/transports/smart.c
new file mode 100644
index 0000000..5372728
--- /dev/null
+++ b/src/libgit2/transports/smart.c
@@ -0,0 +1,525 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "smart.h"
+
+#include "git2.h"
+#include "git2/sys/remote.h"
+#include "refs.h"
+#include "refspec.h"
+#include "proxy.h"
+
+int git_smart__recv(transport_smart *t)
+{
+ size_t bytes_read;
+ int ret;
+
+ GIT_ASSERT_ARG(t);
+ GIT_ASSERT(t->current_stream);
+
+ if (git_staticstr_remain(&t->buffer) == 0) {
+ git_error_set(GIT_ERROR_NET, "out of buffer space");
+ return -1;
+ }
+
+ ret = t->current_stream->read(t->current_stream,
+ git_staticstr_offset(&t->buffer),
+ git_staticstr_remain(&t->buffer),
+ &bytes_read);
+
+ if (ret < 0)
+ return ret;
+
+ GIT_ASSERT(bytes_read <= INT_MAX);
+ GIT_ASSERT(bytes_read <= git_staticstr_remain(&t->buffer));
+
+ git_staticstr_increase(&t->buffer, bytes_read);
+
+ if (t->packetsize_cb && !t->cancelled.val) {
+ ret = t->packetsize_cb(bytes_read, t->packetsize_payload);
+
+ if (ret) {
+ git_atomic32_set(&t->cancelled, 1);
+ return GIT_EUSER;
+ }
+ }
+
+ return (int)bytes_read;
+}
+
+GIT_INLINE(int) git_smart__reset_stream(transport_smart *t, bool close_subtransport)
+{
+ if (t->current_stream) {
+ t->current_stream->free(t->current_stream);
+ t->current_stream = NULL;
+ }
+
+ if (close_subtransport) {
+ git__free(t->url);
+ t->url = NULL;
+
+ if (t->wrapped->close(t->wrapped) < 0)
+ return -1;
+ }
+
+ git__free(t->caps.object_format);
+ t->caps.object_format = NULL;
+
+ git__free(t->caps.agent);
+ t->caps.agent = NULL;
+
+ return 0;
+}
+
+int git_smart__update_heads(transport_smart *t, git_vector *symrefs)
+{
+ size_t i;
+ git_pkt *pkt;
+
+ git_vector_clear(&t->heads);
+ git_vector_foreach(&t->refs, i, pkt) {
+ git_pkt_ref *ref = (git_pkt_ref *) pkt;
+ if (pkt->type != GIT_PKT_REF)
+ continue;
+
+ if (symrefs) {
+ git_refspec *spec;
+ git_str buf = GIT_STR_INIT;
+ size_t j;
+ int error = 0;
+
+ git_vector_foreach(symrefs, j, spec) {
+ git_str_clear(&buf);
+ if (git_refspec_src_matches(spec, ref->head.name) &&
+ !(error = git_refspec__transform(&buf, spec, ref->head.name))) {
+ git__free(ref->head.symref_target);
+ ref->head.symref_target = git_str_detach(&buf);
+ }
+ }
+
+ git_str_dispose(&buf);
+
+ if (error < 0)
+ return error;
+ }
+
+ if (git_vector_insert(&t->heads, &ref->head) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+static void free_symrefs(git_vector *symrefs)
+{
+ git_refspec *spec;
+ size_t i;
+
+ git_vector_foreach(symrefs, i, spec) {
+ git_refspec__dispose(spec);
+ git__free(spec);
+ }
+
+ git_vector_free(symrefs);
+}
+
+static int git_smart__connect(
+ git_transport *transport,
+ const char *url,
+ int direction,
+ const git_remote_connect_options *connect_opts)
+{
+ transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
+ git_smart_subtransport_stream *stream;
+ int error;
+ git_pkt *pkt;
+ git_pkt_ref *first;
+ git_vector symrefs;
+ git_smart_service_t service;
+
+ if (git_smart__reset_stream(t, true) < 0)
+ return -1;
+
+ if (git_remote_connect_options_normalize(&t->connect_opts, t->owner->repo, connect_opts) < 0)
+ return -1;
+
+ t->url = git__strdup(url);
+ GIT_ERROR_CHECK_ALLOC(t->url);
+
+ t->direction = direction;
+
+ if (GIT_DIRECTION_FETCH == t->direction) {
+ service = GIT_SERVICE_UPLOADPACK_LS;
+ } else if (GIT_DIRECTION_PUSH == t->direction) {
+ service = GIT_SERVICE_RECEIVEPACK_LS;
+ } else {
+ git_error_set(GIT_ERROR_NET, "invalid direction");
+ return -1;
+ }
+
+ if ((error = t->wrapped->action(&stream, t->wrapped, t->url, service)) < 0)
+ return error;
+
+ /* Save off the current stream (i.e. socket) that we are working with */
+ t->current_stream = stream;
+
+ /* 2 flushes for RPC; 1 for stateful */
+ if ((error = git_smart__store_refs(t, t->rpc ? 2 : 1)) < 0)
+ return error;
+
+ /* Strip the comment packet for RPC */
+ if (t->rpc) {
+ pkt = (git_pkt *)git_vector_get(&t->refs, 0);
+
+ if (!pkt || GIT_PKT_COMMENT != pkt->type) {
+ git_error_set(GIT_ERROR_NET, "invalid response");
+ return -1;
+ } else {
+ /* Remove the comment pkt from the list */
+ git_vector_remove(&t->refs, 0);
+ git__free(pkt);
+ }
+ }
+
+ /* We now have loaded the refs. */
+ t->have_refs = 1;
+
+ pkt = (git_pkt *)git_vector_get(&t->refs, 0);
+ if (pkt && GIT_PKT_REF != pkt->type) {
+ git_error_set(GIT_ERROR_NET, "invalid response");
+ return -1;
+ }
+ first = (git_pkt_ref *)pkt;
+
+ if ((error = git_vector_init(&symrefs, 1, NULL)) < 0)
+ return error;
+
+ /* Detect capabilities */
+ if ((error = git_smart__detect_caps(first, &t->caps, &symrefs)) == 0) {
+ /* If the only ref in the list is capabilities^{} with OID_ZERO, remove it */
+ if (1 == t->refs.length && !strcmp(first->head.name, "capabilities^{}") &&
+ git_oid_is_zero(&first->head.oid)) {
+ git_vector_clear(&t->refs);
+ git_pkt_free((git_pkt *)first);
+ }
+
+ /* Keep a list of heads for _ls */
+ git_smart__update_heads(t, &symrefs);
+ } else if (error == GIT_ENOTFOUND) {
+ /* There was no ref packet received, or the cap list was empty */
+ error = 0;
+ } else {
+ git_error_set(GIT_ERROR_NET, "invalid response");
+ goto cleanup;
+ }
+
+ if (t->rpc && (error = git_smart__reset_stream(t, false)) < 0)
+ goto cleanup;
+
+ /* We're now logically connected. */
+ t->connected = 1;
+
+cleanup:
+ free_symrefs(&symrefs);
+
+ return error;
+}
+
+static int git_smart__set_connect_opts(
+ git_transport *transport,
+ const git_remote_connect_options *opts)
+{
+ transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
+
+ if (!t->connected) {
+ git_error_set(GIT_ERROR_NET, "cannot reconfigure a transport that is not connected");
+ return -1;
+ }
+
+ return git_remote_connect_options_normalize(&t->connect_opts, t->owner->repo, opts);
+}
+
+static int git_smart__capabilities(unsigned int *capabilities, git_transport *transport)
+{
+ transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
+
+ *capabilities = 0;
+
+ if (t->caps.want_tip_sha1)
+ *capabilities |= GIT_REMOTE_CAPABILITY_TIP_OID;
+
+ if (t->caps.want_reachable_sha1)
+ *capabilities |= GIT_REMOTE_CAPABILITY_REACHABLE_OID;
+
+ return 0;
+}
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+static int git_smart__oid_type(git_oid_t *out, git_transport *transport)
+{
+ transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
+
+ *out = 0;
+
+ if (t->caps.object_format == NULL) {
+ *out = GIT_OID_DEFAULT;
+ } else {
+ *out = git_oid_type_fromstr(t->caps.object_format);
+
+ if (!*out) {
+ git_error_set(GIT_ERROR_INVALID,
+ "unknown object format '%s'",
+ t->caps.object_format);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+#endif
+
+static int git_smart__ls(const git_remote_head ***out, size_t *size, git_transport *transport)
+{
+ transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
+
+ if (!t->have_refs) {
+ git_error_set(GIT_ERROR_NET, "the transport has not yet loaded the refs");
+ return -1;
+ }
+
+ *out = (const git_remote_head **) t->heads.contents;
+ *size = t->heads.length;
+
+ return 0;
+}
+
+int git_smart__negotiation_step(git_transport *transport, void *data, size_t len)
+{
+ transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
+ git_smart_subtransport_stream *stream;
+ int error;
+
+ if (t->rpc && git_smart__reset_stream(t, false) < 0)
+ return -1;
+
+ if (GIT_DIRECTION_FETCH != t->direction) {
+ git_error_set(GIT_ERROR_NET, "this operation is only valid for fetch");
+ return -1;
+ }
+
+ if ((error = t->wrapped->action(&stream, t->wrapped, t->url, GIT_SERVICE_UPLOADPACK)) < 0)
+ return error;
+
+ /* If this is a stateful implementation, the stream we get back should be the same */
+ GIT_ASSERT(t->rpc || t->current_stream == stream);
+
+ /* Save off the current stream (i.e. socket) that we are working with */
+ t->current_stream = stream;
+
+ if ((error = stream->write(stream, (const char *)data, len)) < 0)
+ return error;
+
+ return 0;
+}
+
+int git_smart__get_push_stream(transport_smart *t, git_smart_subtransport_stream **stream)
+{
+ int error;
+
+ if (t->rpc && git_smart__reset_stream(t, false) < 0)
+ return -1;
+
+ if (GIT_DIRECTION_PUSH != t->direction) {
+ git_error_set(GIT_ERROR_NET, "this operation is only valid for push");
+ return -1;
+ }
+
+ if ((error = t->wrapped->action(stream, t->wrapped, t->url, GIT_SERVICE_RECEIVEPACK)) < 0)
+ return error;
+
+ /* If this is a stateful implementation, the stream we get back should be the same */
+ GIT_ASSERT(t->rpc || t->current_stream == *stream);
+
+ /* Save off the current stream (i.e. socket) that we are working with */
+ t->current_stream = *stream;
+
+ return 0;
+}
+
+static void git_smart__cancel(git_transport *transport)
+{
+ transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
+
+ git_atomic32_set(&t->cancelled, 1);
+}
+
+static int git_smart__is_connected(git_transport *transport)
+{
+ transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
+
+ return t->connected;
+}
+
+static int git_smart__close(git_transport *transport)
+{
+ transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
+ git_vector *common = &t->common;
+ unsigned int i;
+ git_pkt *p;
+ int ret;
+ git_smart_subtransport_stream *stream;
+ const char flush[] = "0000";
+
+ /*
+ * If we're still connected at this point and not using RPC,
+ * we should say goodbye by sending a flush, or git-daemon
+ * will complain that we disconnected unexpectedly.
+ */
+ if (t->connected && !t->rpc &&
+ !t->wrapped->action(&stream, t->wrapped, t->url, GIT_SERVICE_UPLOADPACK)) {
+ t->current_stream->write(t->current_stream, flush, 4);
+ }
+
+ ret = git_smart__reset_stream(t, true);
+
+ git_vector_foreach(common, i, p)
+ git_pkt_free(p);
+
+ git_vector_free(common);
+
+ if (t->url) {
+ git__free(t->url);
+ t->url = NULL;
+ }
+
+ t->connected = 0;
+
+ return ret;
+}
+
+static void git_smart__free(git_transport *transport)
+{
+ transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
+ git_vector *refs = &t->refs;
+ unsigned int i;
+ git_pkt *p;
+
+ /* Make sure that the current stream is closed, if we have one. */
+ git_smart__close(transport);
+
+ /* Free the subtransport */
+ t->wrapped->free(t->wrapped);
+
+ git_vector_free(&t->heads);
+ git_vector_foreach(refs, i, p)
+ git_pkt_free(p);
+
+ git_vector_free(refs);
+
+ git_remote_connect_options_dispose(&t->connect_opts);
+
+ git_array_dispose(t->shallow_roots);
+
+ git__free(t->caps.object_format);
+ git__free(t->caps.agent);
+ git__free(t);
+}
+
+static int ref_name_cmp(const void *a, const void *b)
+{
+ const git_pkt_ref *ref_a = a, *ref_b = b;
+
+ return strcmp(ref_a->head.name, ref_b->head.name);
+}
+
+int git_transport_smart_certificate_check(git_transport *transport, git_cert *cert, int valid, const char *hostname)
+{
+ transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
+ git_remote_connect_options *connect_opts = &t->connect_opts;
+
+ GIT_ASSERT_ARG(transport);
+ GIT_ASSERT_ARG(cert);
+ GIT_ASSERT_ARG(hostname);
+
+ if (!connect_opts->callbacks.certificate_check)
+ return GIT_PASSTHROUGH;
+
+ return connect_opts->callbacks.certificate_check(cert, valid, hostname, connect_opts->callbacks.payload);
+}
+
+int git_transport_smart_credentials(git_credential **out, git_transport *transport, const char *user, int methods)
+{
+ transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
+ git_remote_connect_options *connect_opts = &t->connect_opts;
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(transport);
+
+ if (!connect_opts->callbacks.credentials)
+ return GIT_PASSTHROUGH;
+
+ return connect_opts->callbacks.credentials(out, t->url, user, methods, connect_opts->callbacks.payload);
+}
+
+int git_transport_remote_connect_options(
+ git_remote_connect_options *out,
+ git_transport *transport)
+{
+ transport_smart *t = GIT_CONTAINER_OF(transport, transport_smart, parent);
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(transport);
+
+ return git_remote_connect_options_dup(out, &t->connect_opts);
+}
+
+int git_transport_smart(git_transport **out, git_remote *owner, void *param)
+{
+ transport_smart *t;
+ git_smart_subtransport_definition *definition = (git_smart_subtransport_definition *)param;
+
+ if (!param)
+ return -1;
+
+ t = git__calloc(1, sizeof(transport_smart));
+ GIT_ERROR_CHECK_ALLOC(t);
+
+ t->parent.version = GIT_TRANSPORT_VERSION;
+ t->parent.connect = git_smart__connect;
+ t->parent.set_connect_opts = git_smart__set_connect_opts;
+ t->parent.capabilities = git_smart__capabilities;
+#ifdef GIT_EXPERIMENTAL_SHA256
+ t->parent.oid_type = git_smart__oid_type;
+#endif
+ t->parent.close = git_smart__close;
+ t->parent.free = git_smart__free;
+ t->parent.negotiate_fetch = git_smart__negotiate_fetch;
+ t->parent.shallow_roots = git_smart__shallow_roots;
+ t->parent.download_pack = git_smart__download_pack;
+ t->parent.push = git_smart__push;
+ t->parent.ls = git_smart__ls;
+ t->parent.is_connected = git_smart__is_connected;
+ t->parent.cancel = git_smart__cancel;
+
+ t->owner = owner;
+ t->rpc = definition->rpc;
+
+ if (git_vector_init(&t->refs, 16, ref_name_cmp) < 0 ||
+ git_vector_init(&t->heads, 16, ref_name_cmp) < 0 ||
+ definition->callback(&t->wrapped, &t->parent, definition->param) < 0) {
+ git_vector_free(&t->refs);
+ git_vector_free(&t->heads);
+ t->wrapped->free(t->wrapped);
+ git__free(t);
+ return -1;
+ }
+
+ git_staticstr_init(&t->buffer, GIT_SMART_BUFFER_SIZE);
+
+ *out = (git_transport *) t;
+ return 0;
+}
diff --git a/src/libgit2/transports/smart.h b/src/libgit2/transports/smart.h
new file mode 100644
index 0000000..52c7553
--- /dev/null
+++ b/src/libgit2/transports/smart.h
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_transports_smart_h__
+#define INCLUDE_transports_smart_h__
+
+#include "common.h"
+
+#include "git2.h"
+#include "vector.h"
+#include "push.h"
+#include "str.h"
+#include "oidarray.h"
+#include "staticstr.h"
+#include "git2/sys/transport.h"
+
+#define GIT_SMART_BUFFER_SIZE 65536
+
+#define GIT_SIDE_BAND_DATA 1
+#define GIT_SIDE_BAND_PROGRESS 2
+#define GIT_SIDE_BAND_ERROR 3
+
+#define GIT_CAP_OFS_DELTA "ofs-delta"
+#define GIT_CAP_MULTI_ACK "multi_ack"
+#define GIT_CAP_MULTI_ACK_DETAILED "multi_ack_detailed"
+#define GIT_CAP_SIDE_BAND "side-band"
+#define GIT_CAP_SIDE_BAND_64K "side-band-64k"
+#define GIT_CAP_INCLUDE_TAG "include-tag"
+#define GIT_CAP_DELETE_REFS "delete-refs"
+#define GIT_CAP_REPORT_STATUS "report-status"
+#define GIT_CAP_THIN_PACK "thin-pack"
+#define GIT_CAP_SYMREF "symref"
+#define GIT_CAP_WANT_TIP_SHA1 "allow-tip-sha1-in-want"
+#define GIT_CAP_WANT_REACHABLE_SHA1 "allow-reachable-sha1-in-want"
+#define GIT_CAP_SHALLOW "shallow"
+#define GIT_CAP_OBJECT_FORMAT "object-format="
+#define GIT_CAP_AGENT "agent="
+
+extern bool git_smart__ofs_delta_enabled;
+
+typedef enum {
+ GIT_PKT_CMD,
+ GIT_PKT_FLUSH,
+ GIT_PKT_REF,
+ GIT_PKT_HAVE,
+ GIT_PKT_ACK,
+ GIT_PKT_NAK,
+ GIT_PKT_COMMENT,
+ GIT_PKT_ERR,
+ GIT_PKT_DATA,
+ GIT_PKT_PROGRESS,
+ GIT_PKT_OK,
+ GIT_PKT_NG,
+ GIT_PKT_UNPACK,
+ GIT_PKT_SHALLOW,
+ GIT_PKT_UNSHALLOW
+} git_pkt_type;
+
+/* Used for multi_ack and multi_ack_detailed */
+enum git_ack_status {
+ GIT_ACK_NONE,
+ GIT_ACK_CONTINUE,
+ GIT_ACK_COMMON,
+ GIT_ACK_READY
+};
+
+/* This would be a flush pkt */
+typedef struct {
+ git_pkt_type type;
+} git_pkt;
+
+struct git_pkt_cmd {
+ git_pkt_type type;
+ char *cmd;
+ char *path;
+ char *host;
+};
+
+/* This is a pkt-line with some info in it */
+typedef struct {
+ git_pkt_type type;
+ git_remote_head head;
+ char *capabilities;
+} git_pkt_ref;
+
+/* Useful later */
+typedef struct {
+ git_pkt_type type;
+ git_oid oid;
+ enum git_ack_status status;
+} git_pkt_ack;
+
+typedef struct {
+ git_pkt_type type;
+ char comment[GIT_FLEX_ARRAY];
+} git_pkt_comment;
+
+typedef struct {
+ git_pkt_type type;
+ size_t len;
+ char data[GIT_FLEX_ARRAY];
+} git_pkt_data;
+
+typedef git_pkt_data git_pkt_progress;
+
+typedef struct {
+ git_pkt_type type;
+ size_t len;
+ char error[GIT_FLEX_ARRAY];
+} git_pkt_err;
+
+typedef struct {
+ git_pkt_type type;
+ char *ref;
+} git_pkt_ok;
+
+typedef struct {
+ git_pkt_type type;
+ char *ref;
+ char *msg;
+} git_pkt_ng;
+
+typedef struct {
+ git_pkt_type type;
+ int unpack_ok;
+} git_pkt_unpack;
+
+typedef struct {
+ git_pkt_type type;
+ git_oid oid;
+} git_pkt_shallow;
+
+typedef struct transport_smart_caps {
+ unsigned int common:1,
+ ofs_delta:1,
+ multi_ack:1,
+ multi_ack_detailed:1,
+ side_band:1,
+ side_band_64k:1,
+ include_tag:1,
+ delete_refs:1,
+ report_status:1,
+ thin_pack:1,
+ want_tip_sha1:1,
+ want_reachable_sha1:1,
+ shallow:1;
+ char *object_format;
+ char *agent;
+} transport_smart_caps;
+
+typedef int (*packetsize_cb)(size_t received, void *payload);
+
+typedef struct {
+ git_transport parent;
+ git_remote *owner;
+ char *url;
+ git_remote_connect_options connect_opts;
+ int direction;
+ git_smart_subtransport *wrapped;
+ git_smart_subtransport_stream *current_stream;
+ transport_smart_caps caps;
+ git_vector refs;
+ git_vector heads;
+ git_vector common;
+ git_array_oid_t shallow_roots;
+ git_atomic32 cancelled;
+ packetsize_cb packetsize_cb;
+ void *packetsize_payload;
+ unsigned rpc : 1,
+ have_refs : 1,
+ connected : 1;
+ git_staticstr_with_size(GIT_SMART_BUFFER_SIZE) buffer;
+} transport_smart;
+
+/* smart_protocol.c */
+int git_smart__store_refs(transport_smart *t, int flushes);
+int git_smart__detect_caps(git_pkt_ref *pkt, transport_smart_caps *caps, git_vector *symrefs);
+int git_smart__push(git_transport *transport, git_push *push);
+
+int git_smart__negotiate_fetch(
+ git_transport *transport,
+ git_repository *repo,
+ const git_fetch_negotiation *wants);
+
+int git_smart__shallow_roots(git_oidarray *out, git_transport *transport);
+
+int git_smart__download_pack(
+ git_transport *transport,
+ git_repository *repo,
+ git_indexer_progress *stats);
+
+/* smart.c */
+int git_smart__recv(transport_smart *t);
+
+int git_smart__negotiation_step(git_transport *transport, void *data, size_t len);
+int git_smart__get_push_stream(transport_smart *t, git_smart_subtransport_stream **out);
+
+int git_smart__update_heads(transport_smart *t, git_vector *symrefs);
+
+/* smart_pkt.c */
+typedef struct {
+ git_oid_t oid_type;
+ int seen_capabilities: 1;
+} git_pkt_parse_data;
+
+int git_pkt_parse_line(git_pkt **head, const char **endptr, const char *line, size_t linelen, git_pkt_parse_data *data);
+int git_pkt_buffer_flush(git_str *buf);
+int git_pkt_send_flush(GIT_SOCKET s);
+int git_pkt_buffer_done(git_str *buf);
+int git_pkt_buffer_wants(const git_fetch_negotiation *wants, transport_smart_caps *caps, git_str *buf);
+int git_pkt_buffer_have(git_oid *oid, git_str *buf);
+void git_pkt_free(git_pkt *pkt);
+
+#endif
diff --git a/src/libgit2/transports/smart_pkt.c b/src/libgit2/transports/smart_pkt.c
new file mode 100644
index 0000000..3307acf
--- /dev/null
+++ b/src/libgit2/transports/smart_pkt.c
@@ -0,0 +1,870 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+
+#include "smart.h"
+#include "util.h"
+#include "posix.h"
+#include "str.h"
+#include "oid.h"
+
+#include "git2/types.h"
+#include "git2/errors.h"
+#include "git2/refs.h"
+#include "git2/revwalk.h"
+
+#include <ctype.h>
+
+#define PKT_DONE_STR "0009done\n"
+#define PKT_FLUSH_STR "0000"
+#define PKT_HAVE_PREFIX "have "
+#define PKT_WANT_PREFIX "want "
+
+#define PKT_LEN_SIZE 4
+#define PKT_MAX_SIZE 0xffff
+#define PKT_MAX_WANTLEN (PKT_LEN_SIZE + CONST_STRLEN(PKT_WANT_PREFIX) + GIT_OID_MAX_HEXSIZE + 1)
+
+static int flush_pkt(git_pkt **out)
+{
+ git_pkt *pkt;
+
+ pkt = git__malloc(sizeof(git_pkt));
+ GIT_ERROR_CHECK_ALLOC(pkt);
+
+ pkt->type = GIT_PKT_FLUSH;
+ *out = pkt;
+
+ return 0;
+}
+
+/* the rest of the line will be useful for multi_ack and multi_ack_detailed */
+static int ack_pkt(
+ git_pkt **out,
+ const char *line,
+ size_t len,
+ git_pkt_parse_data *data)
+{
+ git_pkt_ack *pkt;
+ size_t oid_hexsize = git_oid_hexsize(data->oid_type);
+
+ GIT_ASSERT(data && data->oid_type);
+
+ pkt = git__calloc(1, sizeof(git_pkt_ack));
+ GIT_ERROR_CHECK_ALLOC(pkt);
+ pkt->type = GIT_PKT_ACK;
+
+ if (git__prefixncmp(line, len, "ACK "))
+ goto out_err;
+ line += 4;
+ len -= 4;
+
+ if (len < oid_hexsize ||
+ git_oid__fromstr(&pkt->oid, line, data->oid_type) < 0)
+ goto out_err;
+ line += oid_hexsize;
+ len -= oid_hexsize;
+
+ if (len && line[0] == ' ') {
+ line++;
+ len--;
+
+ if (!git__prefixncmp(line, len, "continue"))
+ pkt->status = GIT_ACK_CONTINUE;
+ else if (!git__prefixncmp(line, len, "common"))
+ pkt->status = GIT_ACK_COMMON;
+ else if (!git__prefixncmp(line, len, "ready"))
+ pkt->status = GIT_ACK_READY;
+ else
+ goto out_err;
+ }
+
+ *out = (git_pkt *) pkt;
+
+ return 0;
+
+out_err:
+ git_error_set(GIT_ERROR_NET, "error parsing ACK pkt-line");
+ git__free(pkt);
+ return -1;
+}
+
+static int nak_pkt(git_pkt **out)
+{
+ git_pkt *pkt;
+
+ pkt = git__malloc(sizeof(git_pkt));
+ GIT_ERROR_CHECK_ALLOC(pkt);
+
+ pkt->type = GIT_PKT_NAK;
+ *out = pkt;
+
+ return 0;
+}
+
+static int comment_pkt(git_pkt **out, const char *line, size_t len)
+{
+ git_pkt_comment *pkt;
+ size_t alloclen;
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_pkt_comment), len);
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
+ pkt = git__malloc(alloclen);
+ GIT_ERROR_CHECK_ALLOC(pkt);
+
+ pkt->type = GIT_PKT_COMMENT;
+ memcpy(pkt->comment, line, len);
+ pkt->comment[len] = '\0';
+
+ *out = (git_pkt *) pkt;
+
+ return 0;
+}
+
+static int err_pkt(git_pkt **out, const char *line, size_t len)
+{
+ git_pkt_err *pkt = NULL;
+ size_t alloclen;
+
+ /* Remove "ERR " from the line */
+ if (git__prefixncmp(line, len, "ERR "))
+ goto out_err;
+ line += 4;
+ len -= 4;
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_pkt_progress), len);
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
+ pkt = git__malloc(alloclen);
+ GIT_ERROR_CHECK_ALLOC(pkt);
+ pkt->type = GIT_PKT_ERR;
+ pkt->len = len;
+
+ memcpy(pkt->error, line, len);
+ pkt->error[len] = '\0';
+
+ *out = (git_pkt *) pkt;
+
+ return 0;
+
+out_err:
+ git_error_set(GIT_ERROR_NET, "error parsing ERR pkt-line");
+ git__free(pkt);
+ return -1;
+}
+
+static int data_pkt(git_pkt **out, const char *line, size_t len)
+{
+ git_pkt_data *pkt;
+ size_t alloclen;
+
+ line++;
+ len--;
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_pkt_progress), len);
+ pkt = git__malloc(alloclen);
+ GIT_ERROR_CHECK_ALLOC(pkt);
+
+ pkt->type = GIT_PKT_DATA;
+ pkt->len = len;
+ memcpy(pkt->data, line, len);
+
+ *out = (git_pkt *) pkt;
+
+ return 0;
+}
+
+static int sideband_progress_pkt(git_pkt **out, const char *line, size_t len)
+{
+ git_pkt_progress *pkt;
+ size_t alloclen;
+
+ line++;
+ len--;
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_pkt_progress), len);
+ pkt = git__malloc(alloclen);
+ GIT_ERROR_CHECK_ALLOC(pkt);
+
+ pkt->type = GIT_PKT_PROGRESS;
+ pkt->len = len;
+ memcpy(pkt->data, line, len);
+
+ *out = (git_pkt *) pkt;
+
+ return 0;
+}
+
+static int sideband_error_pkt(git_pkt **out, const char *line, size_t len)
+{
+ git_pkt_err *pkt;
+ size_t alloc_len;
+
+ line++;
+ len--;
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, sizeof(git_pkt_err), len);
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 1);
+ pkt = git__malloc(alloc_len);
+ GIT_ERROR_CHECK_ALLOC(pkt);
+
+ pkt->type = GIT_PKT_ERR;
+ pkt->len = (int)len;
+ memcpy(pkt->error, line, len);
+ pkt->error[len] = '\0';
+
+ *out = (git_pkt *)pkt;
+
+ return 0;
+}
+
+static int set_data(
+ git_pkt_parse_data *data,
+ const char *line,
+ size_t len)
+{
+ const char *caps, *format_str = NULL, *eos;
+ size_t format_len;
+ git_oid_t remote_oid_type;
+
+ GIT_ASSERT_ARG(data);
+
+ if ((caps = memchr(line, '\0', len)) != NULL &&
+ len > (size_t)((caps - line) + 1)) {
+ caps++;
+
+ if (strncmp(caps, "object-format=", CONST_STRLEN("object-format=")) == 0)
+ format_str = caps + CONST_STRLEN("object-format=");
+ else if ((format_str = strstr(caps, " object-format=")) != NULL)
+ format_str += CONST_STRLEN(" object-format=");
+ }
+
+ if (format_str) {
+ if ((eos = strchr(format_str, ' ')) == NULL)
+ eos = strchr(format_str, '\0');
+
+ GIT_ASSERT(eos);
+
+ format_len = eos - format_str;
+
+ if ((remote_oid_type = git_oid_type_fromstrn(format_str, format_len)) == 0) {
+ git_error_set(GIT_ERROR_INVALID, "unknown remote object format '%.*s'", (int)format_len, format_str);
+ return -1;
+ }
+ } else {
+ remote_oid_type = GIT_OID_SHA1;
+ }
+
+ if (!data->oid_type) {
+ data->oid_type = remote_oid_type;
+ } else if (data->oid_type != remote_oid_type) {
+ git_error_set(GIT_ERROR_INVALID,
+ "the local object format '%s' does not match the remote object format '%s'",
+ git_oid_type_name(data->oid_type),
+ git_oid_type_name(remote_oid_type));
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Parse an other-ref line.
+ */
+static int ref_pkt(
+ git_pkt **out,
+ const char *line,
+ size_t len,
+ git_pkt_parse_data *data)
+{
+ git_pkt_ref *pkt;
+ size_t alloclen, oid_hexsize;
+
+ pkt = git__calloc(1, sizeof(git_pkt_ref));
+ GIT_ERROR_CHECK_ALLOC(pkt);
+ pkt->type = GIT_PKT_REF;
+
+ /* Determine OID type from capabilities */
+ if (!data->seen_capabilities && set_data(data, line, len) < 0)
+ return -1;
+
+ GIT_ASSERT(data->oid_type);
+ oid_hexsize = git_oid_hexsize(data->oid_type);
+
+ if (len < oid_hexsize ||
+ git_oid__fromstr(&pkt->head.oid, line, data->oid_type) < 0)
+ goto out_err;
+ line += oid_hexsize;
+ len -= oid_hexsize;
+
+ if (git__prefixncmp(line, len, " "))
+ goto out_err;
+
+ line++;
+ len--;
+
+ if (!len)
+ goto out_err;
+
+ if (line[len - 1] == '\n')
+ --len;
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, len, 1);
+ pkt->head.name = git__malloc(alloclen);
+ GIT_ERROR_CHECK_ALLOC(pkt->head.name);
+
+ memcpy(pkt->head.name, line, len);
+ pkt->head.name[len] = '\0';
+
+ if (strlen(pkt->head.name) < len) {
+ if (!data->seen_capabilities)
+ pkt->capabilities = strchr(pkt->head.name, '\0') + 1;
+ else
+ goto out_err;
+ }
+
+ data->seen_capabilities = 1;
+
+ *out = (git_pkt *)pkt;
+ return 0;
+
+out_err:
+ git_error_set(GIT_ERROR_NET, "error parsing REF pkt-line");
+ if (pkt)
+ git__free(pkt->head.name);
+ git__free(pkt);
+ return -1;
+}
+
+static int ok_pkt(git_pkt **out, const char *line, size_t len)
+{
+ git_pkt_ok *pkt;
+ size_t alloc_len;
+
+ pkt = git__malloc(sizeof(*pkt));
+ GIT_ERROR_CHECK_ALLOC(pkt);
+ pkt->type = GIT_PKT_OK;
+
+ if (git__prefixncmp(line, len, "ok "))
+ goto out_err;
+ line += 3;
+ len -= 3;
+
+ if (len && line[len - 1] == '\n')
+ --len;
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, len, 1);
+ pkt->ref = git__malloc(alloc_len);
+ GIT_ERROR_CHECK_ALLOC(pkt->ref);
+
+ memcpy(pkt->ref, line, len);
+ pkt->ref[len] = '\0';
+
+ *out = (git_pkt *)pkt;
+ return 0;
+
+out_err:
+ git_error_set(GIT_ERROR_NET, "error parsing OK pkt-line");
+ git__free(pkt);
+ return -1;
+}
+
+static int ng_pkt(git_pkt **out, const char *line, size_t len)
+{
+ git_pkt_ng *pkt;
+ const char *ptr, *eol;
+ size_t alloclen;
+
+ pkt = git__malloc(sizeof(*pkt));
+ GIT_ERROR_CHECK_ALLOC(pkt);
+
+ pkt->ref = NULL;
+ pkt->type = GIT_PKT_NG;
+
+ eol = line + len;
+
+ if (git__prefixncmp(line, len, "ng "))
+ goto out_err;
+ line += 3;
+
+ if (!(ptr = memchr(line, ' ', eol - line)))
+ goto out_err;
+ len = ptr - line;
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, len, 1);
+ pkt->ref = git__malloc(alloclen);
+ GIT_ERROR_CHECK_ALLOC(pkt->ref);
+
+ memcpy(pkt->ref, line, len);
+ pkt->ref[len] = '\0';
+
+ line = ptr + 1;
+ if (line >= eol)
+ goto out_err;
+
+ if (!(ptr = memchr(line, '\n', eol - line)))
+ goto out_err;
+ len = ptr - line;
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, len, 1);
+ pkt->msg = git__malloc(alloclen);
+ GIT_ERROR_CHECK_ALLOC(pkt->msg);
+
+ memcpy(pkt->msg, line, len);
+ pkt->msg[len] = '\0';
+
+ *out = (git_pkt *)pkt;
+ return 0;
+
+out_err:
+ git_error_set(GIT_ERROR_NET, "invalid packet line");
+ git__free(pkt->ref);
+ git__free(pkt);
+ return -1;
+}
+
+static int unpack_pkt(git_pkt **out, const char *line, size_t len)
+{
+ git_pkt_unpack *pkt;
+
+ pkt = git__malloc(sizeof(*pkt));
+ GIT_ERROR_CHECK_ALLOC(pkt);
+ pkt->type = GIT_PKT_UNPACK;
+
+ if (!git__prefixncmp(line, len, "unpack ok"))
+ pkt->unpack_ok = 1;
+ else
+ pkt->unpack_ok = 0;
+
+ *out = (git_pkt *)pkt;
+ return 0;
+}
+
+static int shallow_pkt(
+ git_pkt **out,
+ const char *line,
+ size_t len,
+ git_pkt_parse_data *data)
+{
+ git_pkt_shallow *pkt;
+ size_t oid_hexsize = git_oid_hexsize(data->oid_type);
+
+ GIT_ASSERT(data && data->oid_type);
+
+ pkt = git__calloc(1, sizeof(git_pkt_shallow));
+ GIT_ERROR_CHECK_ALLOC(pkt);
+
+ pkt->type = GIT_PKT_SHALLOW;
+
+ if (git__prefixncmp(line, len, "shallow "))
+ goto out_err;
+
+ line += 8;
+ len -= 8;
+
+ if (len != oid_hexsize)
+ goto out_err;
+
+ git_oid__fromstr(&pkt->oid, line, data->oid_type);
+ line += oid_hexsize + 1;
+ len -= oid_hexsize + 1;
+
+ *out = (git_pkt *)pkt;
+
+ return 0;
+
+out_err:
+ git_error_set(GIT_ERROR_NET, "invalid packet line");
+ git__free(pkt);
+ return -1;
+}
+
+static int unshallow_pkt(
+ git_pkt **out,
+ const char *line,
+ size_t len,
+ git_pkt_parse_data *data)
+{
+ git_pkt_shallow *pkt;
+ size_t oid_hexsize = git_oid_hexsize(data->oid_type);
+
+ GIT_ASSERT(data && data->oid_type);
+
+ pkt = git__calloc(1, sizeof(git_pkt_shallow));
+ GIT_ERROR_CHECK_ALLOC(pkt);
+
+ pkt->type = GIT_PKT_UNSHALLOW;
+
+ if (git__prefixncmp(line, len, "unshallow "))
+ goto out_err;
+
+ line += 10;
+ len -= 10;
+
+ if (len != oid_hexsize)
+ goto out_err;
+
+ git_oid__fromstr(&pkt->oid, line, data->oid_type);
+ line += oid_hexsize + 1;
+ len -= oid_hexsize + 1;
+
+ *out = (git_pkt *) pkt;
+
+ return 0;
+
+out_err:
+ git_error_set(GIT_ERROR_NET, "invalid packet line");
+ git__free(pkt);
+ return -1;
+}
+
+static int parse_len(size_t *out, const char *line, size_t linelen)
+{
+ char num[PKT_LEN_SIZE + 1];
+ int i, k, error;
+ int32_t len;
+ const char *num_end;
+
+ /* Not even enough for the length */
+ if (linelen < PKT_LEN_SIZE)
+ return GIT_EBUFS;
+
+ memcpy(num, line, PKT_LEN_SIZE);
+ num[PKT_LEN_SIZE] = '\0';
+
+ for (i = 0; i < PKT_LEN_SIZE; ++i) {
+ if (!isxdigit(num[i])) {
+ /* Make sure there are no special characters before passing to error message */
+ for (k = 0; k < PKT_LEN_SIZE; ++k) {
+ if(!isprint(num[k])) {
+ num[k] = '.';
+ }
+ }
+
+ git_error_set(GIT_ERROR_NET, "invalid hex digit in length: '%s'", num);
+ return -1;
+ }
+ }
+
+ if ((error = git__strntol32(&len, num, PKT_LEN_SIZE, &num_end, 16)) < 0)
+ return error;
+
+ if (len < 0)
+ return -1;
+
+ *out = (size_t) len;
+ return 0;
+}
+
+/*
+ * As per the documentation, the syntax is:
+ *
+ * pkt-line = data-pkt / flush-pkt
+ * data-pkt = pkt-len pkt-payload
+ * pkt-len = 4*(HEXDIG)
+ * pkt-payload = (pkt-len -4)*(OCTET)
+ * flush-pkt = "0000"
+ *
+ * Which means that the first four bytes are the length of the line,
+ * in ASCII hexadecimal (including itself)
+ */
+
+int git_pkt_parse_line(
+ git_pkt **pkt,
+ const char **endptr,
+ const char *line,
+ size_t linelen,
+ git_pkt_parse_data *data)
+{
+ int error;
+ size_t len;
+
+ if ((error = parse_len(&len, line, linelen)) < 0) {
+ /*
+ * If we fail to parse the length, it might be
+ * because the server is trying to send us the
+ * packfile already or because we do not yet have
+ * enough data.
+ */
+ if (error == GIT_EBUFS)
+ ;
+ else if (!git__prefixncmp(line, linelen, "PACK"))
+ git_error_set(GIT_ERROR_NET, "unexpected pack file");
+ else
+ git_error_set(GIT_ERROR_NET, "bad packet length");
+ return error;
+ }
+
+ /*
+ * Make sure there is enough in the buffer to satisfy
+ * this line.
+ */
+ if (linelen < len)
+ return GIT_EBUFS;
+
+ /*
+ * The length has to be exactly 0 in case of a flush
+ * packet or greater than PKT_LEN_SIZE, as the decoded
+ * length includes its own encoded length of four bytes.
+ */
+ if (len != 0 && len < PKT_LEN_SIZE)
+ return GIT_ERROR;
+
+ line += PKT_LEN_SIZE;
+ /*
+ * The Git protocol does not specify empty lines as part
+ * of the protocol. Not knowing what to do with an empty
+ * line, we should return an error upon hitting one.
+ */
+ if (len == PKT_LEN_SIZE) {
+ git_error_set_str(GIT_ERROR_NET, "Invalid empty packet");
+ return GIT_ERROR;
+ }
+
+ if (len == 0) { /* Flush pkt */
+ *endptr = line;
+ return flush_pkt(pkt);
+ }
+
+ len -= PKT_LEN_SIZE; /* the encoded length includes its own size */
+
+ if (*line == GIT_SIDE_BAND_DATA)
+ error = data_pkt(pkt, line, len);
+ else if (*line == GIT_SIDE_BAND_PROGRESS)
+ error = sideband_progress_pkt(pkt, line, len);
+ else if (*line == GIT_SIDE_BAND_ERROR)
+ error = sideband_error_pkt(pkt, line, len);
+ else if (!git__prefixncmp(line, len, "ACK"))
+ error = ack_pkt(pkt, line, len, data);
+ else if (!git__prefixncmp(line, len, "NAK"))
+ error = nak_pkt(pkt);
+ else if (!git__prefixncmp(line, len, "ERR"))
+ error = err_pkt(pkt, line, len);
+ else if (*line == '#')
+ error = comment_pkt(pkt, line, len);
+ else if (!git__prefixncmp(line, len, "ok"))
+ error = ok_pkt(pkt, line, len);
+ else if (!git__prefixncmp(line, len, "ng"))
+ error = ng_pkt(pkt, line, len);
+ else if (!git__prefixncmp(line, len, "unpack"))
+ error = unpack_pkt(pkt, line, len);
+ else if (!git__prefixcmp(line, "shallow"))
+ error = shallow_pkt(pkt, line, len, data);
+ else if (!git__prefixcmp(line, "unshallow"))
+ error = unshallow_pkt(pkt, line, len, data);
+ else
+ error = ref_pkt(pkt, line, len, data);
+
+ *endptr = line + len;
+
+ return error;
+}
+
+void git_pkt_free(git_pkt *pkt)
+{
+ if (pkt == NULL) {
+ return;
+ }
+ if (pkt->type == GIT_PKT_REF) {
+ git_pkt_ref *p = (git_pkt_ref *) pkt;
+ git__free(p->head.name);
+ git__free(p->head.symref_target);
+ }
+
+ if (pkt->type == GIT_PKT_OK) {
+ git_pkt_ok *p = (git_pkt_ok *) pkt;
+ git__free(p->ref);
+ }
+
+ if (pkt->type == GIT_PKT_NG) {
+ git_pkt_ng *p = (git_pkt_ng *) pkt;
+ git__free(p->ref);
+ git__free(p->msg);
+ }
+
+ git__free(pkt);
+}
+
+int git_pkt_buffer_flush(git_str *buf)
+{
+ return git_str_put(buf, PKT_FLUSH_STR, CONST_STRLEN(PKT_FLUSH_STR));
+}
+
+static int buffer_want_with_caps(
+ const git_remote_head *head,
+ transport_smart_caps *caps,
+ git_oid_t oid_type,
+ git_str *buf)
+{
+ git_str str = GIT_STR_INIT;
+ char oid[GIT_OID_MAX_HEXSIZE];
+ size_t oid_hexsize, len;
+
+ oid_hexsize = git_oid_hexsize(oid_type);
+ git_oid_fmt(oid, &head->oid);
+
+ /* Prefer multi_ack_detailed */
+ if (caps->multi_ack_detailed)
+ git_str_puts(&str, GIT_CAP_MULTI_ACK_DETAILED " ");
+ else if (caps->multi_ack)
+ git_str_puts(&str, GIT_CAP_MULTI_ACK " ");
+
+ /* Prefer side-band-64k if the server supports both */
+ if (caps->side_band_64k)
+ git_str_printf(&str, "%s ", GIT_CAP_SIDE_BAND_64K);
+ else if (caps->side_band)
+ git_str_printf(&str, "%s ", GIT_CAP_SIDE_BAND);
+
+ if (caps->include_tag)
+ git_str_puts(&str, GIT_CAP_INCLUDE_TAG " ");
+
+ if (caps->thin_pack)
+ git_str_puts(&str, GIT_CAP_THIN_PACK " ");
+
+ if (caps->ofs_delta)
+ git_str_puts(&str, GIT_CAP_OFS_DELTA " ");
+
+ if (caps->shallow)
+ git_str_puts(&str, GIT_CAP_SHALLOW " ");
+
+ if (git_str_oom(&str))
+ return -1;
+
+ if (str.size > (PKT_MAX_SIZE - (PKT_MAX_WANTLEN + 1))) {
+ git_error_set(GIT_ERROR_NET,
+ "tried to produce packet with invalid caps length %" PRIuZ, str.size);
+ return -1;
+ }
+
+ len = PKT_LEN_SIZE + CONST_STRLEN(PKT_WANT_PREFIX) +
+ oid_hexsize + 1 /* NUL */ +
+ git_str_len(&str) + 1 /* LF */;
+
+ git_str_grow_by(buf, len);
+ git_str_printf(buf,
+ "%04x%s%.*s %s\n", (unsigned int)len, PKT_WANT_PREFIX,
+ (int)oid_hexsize, oid, git_str_cstr(&str));
+ git_str_dispose(&str);
+
+ GIT_ERROR_CHECK_ALLOC_STR(buf);
+
+ return 0;
+}
+
+/*
+ * All "want" packets have the same length and format, so what we do
+ * is overwrite the OID each time.
+ */
+
+int git_pkt_buffer_wants(
+ const git_fetch_negotiation *wants,
+ transport_smart_caps *caps,
+ git_str *buf)
+{
+ const git_remote_head *head;
+ char oid[GIT_OID_MAX_HEXSIZE];
+ git_oid_t oid_type;
+ size_t oid_hexsize, want_len, i = 0;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ oid_type = wants->refs_len > 0 ? wants->refs[0]->oid.type : GIT_OID_SHA1;
+#else
+ oid_type = GIT_OID_SHA1;
+#endif
+
+ oid_hexsize = git_oid_hexsize(oid_type);
+
+ want_len = PKT_LEN_SIZE + CONST_STRLEN(PKT_WANT_PREFIX) +
+ oid_hexsize + 1 /* LF */;
+
+ if (caps->common) {
+ for (; i < wants->refs_len; ++i) {
+ head = wants->refs[i];
+ if (!head->local)
+ break;
+ }
+
+ if (buffer_want_with_caps(wants->refs[i], caps, oid_type, buf) < 0)
+ return -1;
+
+ i++;
+ }
+
+ for (; i < wants->refs_len; ++i) {
+ head = wants->refs[i];
+
+ if (head->local)
+ continue;
+
+ git_oid_fmt(oid, &head->oid);
+
+ git_str_printf(buf, "%04x%s%.*s\n",
+ (unsigned int)want_len, PKT_WANT_PREFIX,
+ (int)oid_hexsize, oid);
+
+ if (git_str_oom(buf))
+ return -1;
+ }
+
+ /* Tell the server about our shallow objects */
+ for (i = 0; i < wants->shallow_roots_len; i++) {
+ char oid[GIT_OID_MAX_HEXSIZE + 1];
+ git_str shallow_buf = GIT_STR_INIT;
+
+ git_oid_tostr(oid, GIT_OID_MAX_HEXSIZE + 1, &wants->shallow_roots[i]);
+ git_str_puts(&shallow_buf, "shallow ");
+ git_str_puts(&shallow_buf, oid);
+ git_str_putc(&shallow_buf, '\n');
+
+ git_str_printf(buf, "%04x%s", (unsigned int)git_str_len(&shallow_buf) + 4, git_str_cstr(&shallow_buf));
+
+ git_str_dispose(&shallow_buf);
+
+ if (git_str_oom(buf))
+ return -1;
+ }
+
+ if (wants->depth > 0) {
+ git_str deepen_buf = GIT_STR_INIT;
+
+ git_str_printf(&deepen_buf, "deepen %d\n", wants->depth);
+ git_str_printf(buf,"%04x%s", (unsigned int)git_str_len(&deepen_buf) + 4, git_str_cstr(&deepen_buf));
+
+ git_str_dispose(&deepen_buf);
+
+ if (git_str_oom(buf))
+ return -1;
+ }
+
+ return git_pkt_buffer_flush(buf);
+}
+
+int git_pkt_buffer_have(git_oid *oid, git_str *buf)
+{
+ char oid_str[GIT_OID_MAX_HEXSIZE];
+ git_oid_t oid_type;
+ size_t oid_hexsize, have_len;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ oid_type = oid->type;
+#else
+ oid_type = GIT_OID_SHA1;
+#endif
+
+ oid_hexsize = git_oid_hexsize(oid_type);
+ have_len = PKT_LEN_SIZE + CONST_STRLEN(PKT_HAVE_PREFIX) +
+ oid_hexsize + 1 /* LF */;
+
+ git_oid_fmt(oid_str, oid);
+ return git_str_printf(buf, "%04x%s%.*s\n",
+ (unsigned int)have_len, PKT_HAVE_PREFIX,
+ (int)oid_hexsize, oid_str);
+}
+
+int git_pkt_buffer_done(git_str *buf)
+{
+ return git_str_put(buf, PKT_DONE_STR, CONST_STRLEN(PKT_DONE_STR));
+}
diff --git a/src/libgit2/transports/smart_protocol.c b/src/libgit2/transports/smart_protocol.c
new file mode 100644
index 0000000..c9c422d
--- /dev/null
+++ b/src/libgit2/transports/smart_protocol.c
@@ -0,0 +1,1231 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+
+#include "git2.h"
+#include "git2/odb_backend.h"
+
+#include "smart.h"
+#include "refs.h"
+#include "repository.h"
+#include "push.h"
+#include "pack-objects.h"
+#include "remote.h"
+#include "util.h"
+#include "revwalk.h"
+
+#define NETWORK_XFER_THRESHOLD (100*1024)
+/* The minimal interval between progress updates (in seconds). */
+#define MIN_PROGRESS_UPDATE_INTERVAL 0.5
+
+bool git_smart__ofs_delta_enabled = true;
+
+int git_smart__store_refs(transport_smart *t, int flushes)
+{
+ git_vector *refs = &t->refs;
+ int error, flush = 0, recvd;
+ const char *line_end = NULL;
+ git_pkt *pkt = NULL;
+ git_pkt_parse_data pkt_parse_data = { 0 };
+ size_t i;
+
+ /* Clear existing refs in case git_remote_connect() is called again
+ * after git_remote_disconnect().
+ */
+ git_vector_foreach(refs, i, pkt) {
+ git_pkt_free(pkt);
+ }
+ git_vector_clear(refs);
+ pkt = NULL;
+
+ do {
+ if (t->buffer.len > 0)
+ error = git_pkt_parse_line(&pkt, &line_end,
+ t->buffer.data, t->buffer.len,
+ &pkt_parse_data);
+ else
+ error = GIT_EBUFS;
+
+ if (error < 0 && error != GIT_EBUFS)
+ return error;
+
+ if (error == GIT_EBUFS) {
+ if ((recvd = git_smart__recv(t)) < 0)
+ return recvd;
+
+ if (recvd == 0) {
+ git_error_set(GIT_ERROR_NET, "early EOF");
+ return GIT_EEOF;
+ }
+
+ continue;
+ }
+
+ git_staticstr_consume(&t->buffer, line_end);
+
+ if (pkt->type == GIT_PKT_ERR) {
+ git_error_set(GIT_ERROR_NET, "remote error: %s", ((git_pkt_err *)pkt)->error);
+ git__free(pkt);
+ return -1;
+ }
+
+ if (pkt->type != GIT_PKT_FLUSH && git_vector_insert(refs, pkt) < 0)
+ return -1;
+
+ if (pkt->type == GIT_PKT_FLUSH) {
+ flush++;
+ git_pkt_free(pkt);
+ }
+ } while (flush < flushes);
+
+ return flush;
+}
+
+static int append_symref(const char **out, git_vector *symrefs, const char *ptr)
+{
+ int error;
+ const char *end;
+ git_str buf = GIT_STR_INIT;
+ git_refspec *mapping = NULL;
+
+ ptr += strlen(GIT_CAP_SYMREF);
+ if (*ptr != '=')
+ goto on_invalid;
+
+ ptr++;
+ if (!(end = strchr(ptr, ' ')) &&
+ !(end = strchr(ptr, '\0')))
+ goto on_invalid;
+
+ if ((error = git_str_put(&buf, ptr, end - ptr)) < 0)
+ return error;
+
+ /* symref mapping has refspec format */
+ mapping = git__calloc(1, sizeof(git_refspec));
+ GIT_ERROR_CHECK_ALLOC(mapping);
+
+ error = git_refspec__parse(mapping, git_str_cstr(&buf), true);
+ git_str_dispose(&buf);
+
+ /* if the error isn't OOM, then it's a parse error; let's use a nicer message */
+ if (error < 0) {
+ if (git_error_last()->klass != GIT_ERROR_NOMEMORY)
+ goto on_invalid;
+
+ git__free(mapping);
+ return error;
+ }
+
+ if ((error = git_vector_insert(symrefs, mapping)) < 0)
+ return error;
+
+ *out = end;
+ return 0;
+
+on_invalid:
+ git_error_set(GIT_ERROR_NET, "remote sent invalid symref");
+ git_refspec__dispose(mapping);
+ git__free(mapping);
+ return -1;
+}
+
+int git_smart__detect_caps(
+ git_pkt_ref *pkt,
+ transport_smart_caps *caps,
+ git_vector *symrefs)
+{
+ const char *ptr, *start;
+
+ /* No refs or capabilities, odd but not a problem */
+ if (pkt == NULL || pkt->capabilities == NULL)
+ return GIT_ENOTFOUND;
+
+ ptr = pkt->capabilities;
+ while (ptr != NULL && *ptr != '\0') {
+ if (*ptr == ' ')
+ ptr++;
+
+ if (git_smart__ofs_delta_enabled && !git__prefixcmp(ptr, GIT_CAP_OFS_DELTA)) {
+ caps->common = caps->ofs_delta = 1;
+ ptr += strlen(GIT_CAP_OFS_DELTA);
+ continue;
+ }
+
+ /* Keep multi_ack_detailed before multi_ack */
+ if (!git__prefixcmp(ptr, GIT_CAP_MULTI_ACK_DETAILED)) {
+ caps->common = caps->multi_ack_detailed = 1;
+ ptr += strlen(GIT_CAP_MULTI_ACK_DETAILED);
+ continue;
+ }
+
+ if (!git__prefixcmp(ptr, GIT_CAP_MULTI_ACK)) {
+ caps->common = caps->multi_ack = 1;
+ ptr += strlen(GIT_CAP_MULTI_ACK);
+ continue;
+ }
+
+ if (!git__prefixcmp(ptr, GIT_CAP_INCLUDE_TAG)) {
+ caps->common = caps->include_tag = 1;
+ ptr += strlen(GIT_CAP_INCLUDE_TAG);
+ continue;
+ }
+
+ /* Keep side-band check after side-band-64k */
+ if (!git__prefixcmp(ptr, GIT_CAP_SIDE_BAND_64K)) {
+ caps->common = caps->side_band_64k = 1;
+ ptr += strlen(GIT_CAP_SIDE_BAND_64K);
+ continue;
+ }
+
+ if (!git__prefixcmp(ptr, GIT_CAP_SIDE_BAND)) {
+ caps->common = caps->side_band = 1;
+ ptr += strlen(GIT_CAP_SIDE_BAND);
+ continue;
+ }
+
+ if (!git__prefixcmp(ptr, GIT_CAP_DELETE_REFS)) {
+ caps->common = caps->delete_refs = 1;
+ ptr += strlen(GIT_CAP_DELETE_REFS);
+ continue;
+ }
+
+ if (!git__prefixcmp(ptr, GIT_CAP_THIN_PACK)) {
+ caps->common = caps->thin_pack = 1;
+ ptr += strlen(GIT_CAP_THIN_PACK);
+ continue;
+ }
+
+ if (!git__prefixcmp(ptr, GIT_CAP_SYMREF)) {
+ int error;
+
+ if ((error = append_symref(&ptr, symrefs, ptr)) < 0)
+ return error;
+
+ continue;
+ }
+
+ if (!git__prefixcmp(ptr, GIT_CAP_WANT_TIP_SHA1)) {
+ caps->common = caps->want_tip_sha1 = 1;
+ ptr += strlen(GIT_CAP_WANT_TIP_SHA1);
+ continue;
+ }
+
+ if (!git__prefixcmp(ptr, GIT_CAP_WANT_REACHABLE_SHA1)) {
+ caps->common = caps->want_reachable_sha1 = 1;
+ ptr += strlen(GIT_CAP_WANT_REACHABLE_SHA1);
+ continue;
+ }
+
+ if (!git__prefixcmp(ptr, GIT_CAP_OBJECT_FORMAT)) {
+ ptr += strlen(GIT_CAP_OBJECT_FORMAT);
+
+ start = ptr;
+ ptr = strchr(ptr, ' ');
+
+ if ((caps->object_format = git__strndup(start, (ptr - start))) == NULL)
+ return -1;
+ continue;
+ }
+
+ if (!git__prefixcmp(ptr, GIT_CAP_AGENT)) {
+ ptr += strlen(GIT_CAP_AGENT);
+
+ start = ptr;
+ ptr = strchr(ptr, ' ');
+
+ if ((caps->agent = git__strndup(start, (ptr - start))) == NULL)
+ return -1;
+ continue;
+ }
+
+ if (!git__prefixcmp(ptr, GIT_CAP_SHALLOW)) {
+ caps->common = caps->shallow = 1;
+ ptr += strlen(GIT_CAP_SHALLOW);
+ continue;
+ }
+
+ /* We don't know this capability, so skip it */
+ ptr = strchr(ptr, ' ');
+ }
+
+ return 0;
+}
+
+static int recv_pkt(
+ git_pkt **out_pkt,
+ git_pkt_type *out_type,
+ transport_smart *t)
+{
+ const char *ptr = t->buffer.data, *line_end = ptr;
+ git_pkt *pkt = NULL;
+ git_pkt_parse_data pkt_parse_data = { 0 };
+ int error = 0, ret;
+
+ pkt_parse_data.oid_type = t->owner->repo->oid_type;
+ pkt_parse_data.seen_capabilities = 1;
+
+ do {
+ if (t->buffer.len > 0)
+ error = git_pkt_parse_line(&pkt, &line_end, ptr,
+ t->buffer.len, &pkt_parse_data);
+ else
+ error = GIT_EBUFS;
+
+ if (error == 0)
+ break; /* return the pkt */
+
+ if (error < 0 && error != GIT_EBUFS)
+ return error;
+
+ if ((ret = git_smart__recv(t)) < 0) {
+ return ret;
+ } else if (ret == 0) {
+ git_error_set(GIT_ERROR_NET, "early EOF");
+ return GIT_EEOF;
+ }
+ } while (error);
+
+ git_staticstr_consume(&t->buffer, line_end);
+
+ if (out_type != NULL)
+ *out_type = pkt->type;
+ if (out_pkt != NULL)
+ *out_pkt = pkt;
+ else
+ git__free(pkt);
+
+ return error;
+}
+
+static int store_common(transport_smart *t)
+{
+ git_pkt *pkt = NULL;
+ int error;
+
+ do {
+ if ((error = recv_pkt(&pkt, NULL, t)) < 0)
+ return error;
+
+ if (pkt->type != GIT_PKT_ACK) {
+ git__free(pkt);
+ return 0;
+ }
+
+ if (git_vector_insert(&t->common, pkt) < 0) {
+ git__free(pkt);
+ return -1;
+ }
+ } while (1);
+
+ return 0;
+}
+
+static int wait_while_ack(transport_smart *t)
+{
+ int error;
+ git_pkt *pkt = NULL;
+ git_pkt_ack *ack = NULL;
+
+ while (1) {
+ git_pkt_free(pkt);
+
+ if ((error = recv_pkt(&pkt, NULL, t)) < 0)
+ return error;
+
+ if (pkt->type == GIT_PKT_NAK)
+ break;
+ if (pkt->type != GIT_PKT_ACK)
+ continue;
+
+ ack = (git_pkt_ack*)pkt;
+
+ if (ack->status != GIT_ACK_CONTINUE &&
+ ack->status != GIT_ACK_COMMON &&
+ ack->status != GIT_ACK_READY) {
+ break;
+ }
+ }
+
+ git_pkt_free(pkt);
+ return 0;
+}
+
+static int cap_not_sup_err(const char *cap_name)
+{
+ git_error_set(GIT_ERROR_NET, "server doesn't support %s", cap_name);
+ return GIT_EINVALID;
+}
+
+/* Disables server capabilities we're not interested in */
+static int setup_caps(
+ transport_smart_caps *caps,
+ const git_fetch_negotiation *wants)
+{
+ if (wants->depth > 0) {
+ if (!caps->shallow)
+ return cap_not_sup_err(GIT_CAP_SHALLOW);
+ } else {
+ caps->shallow = 0;
+ }
+
+ return 0;
+}
+
+static int setup_shallow_roots(
+ git_array_oid_t *out,
+ const git_fetch_negotiation *wants)
+{
+ git_array_clear(*out);
+
+ if (wants->shallow_roots_len > 0) {
+ git_array_init_to_size(*out, wants->shallow_roots_len);
+ GIT_ERROR_CHECK_ALLOC(out->ptr);
+
+ memcpy(out->ptr, wants->shallow_roots,
+ sizeof(git_oid) * wants->shallow_roots_len);
+ }
+
+ return 0;
+}
+
+int git_smart__negotiate_fetch(
+ git_transport *transport,
+ git_repository *repo,
+ const git_fetch_negotiation *wants)
+{
+ transport_smart *t = (transport_smart *)transport;
+ git_revwalk__push_options opts = GIT_REVWALK__PUSH_OPTIONS_INIT;
+ git_str data = GIT_STR_INIT;
+ git_revwalk *walk = NULL;
+ int error = -1;
+ git_pkt_type pkt_type;
+ unsigned int i;
+ git_oid oid;
+
+ if ((error = setup_caps(&t->caps, wants)) < 0 ||
+ (error = setup_shallow_roots(&t->shallow_roots, wants)) < 0)
+ return error;
+
+ if ((error = git_pkt_buffer_wants(wants, &t->caps, &data)) < 0)
+ return error;
+
+ if ((error = git_revwalk_new(&walk, repo)) < 0)
+ goto on_error;
+
+ opts.insert_by_date = 1;
+ if ((error = git_revwalk__push_glob(walk, "refs/*", &opts)) < 0)
+ goto on_error;
+
+ if (wants->depth > 0) {
+ git_pkt_shallow *pkt;
+
+ if ((error = git_smart__negotiation_step(&t->parent, data.ptr, data.size)) < 0)
+ goto on_error;
+
+ while ((error = recv_pkt((git_pkt **)&pkt, NULL, t)) == 0) {
+ bool complete = false;
+
+ if (pkt->type == GIT_PKT_SHALLOW) {
+ error = git_oidarray__add(&t->shallow_roots, &pkt->oid);
+ } else if (pkt->type == GIT_PKT_UNSHALLOW) {
+ git_oidarray__remove(&t->shallow_roots, &pkt->oid);
+ } else if (pkt->type == GIT_PKT_FLUSH) {
+ /* Server is done, stop processing shallow oids */
+ complete = true;
+ } else {
+ git_error_set(GIT_ERROR_NET, "unexpected packet type");
+ error = -1;
+ }
+
+ git_pkt_free((git_pkt *) pkt);
+
+ if (complete || error < 0)
+ break;
+ }
+
+ if (error < 0)
+ goto on_error;
+ }
+
+ /*
+ * Our support for ACK extensions is simply to parse them. On
+ * the first ACK we will accept that as enough common
+ * objects. We give up if we haven't found an answer in the
+ * first 256 we send.
+ */
+ i = 0;
+ while (i < 256) {
+ error = git_revwalk_next(&oid, walk);
+
+ if (error < 0) {
+ if (GIT_ITEROVER == error)
+ break;
+
+ goto on_error;
+ }
+
+ git_pkt_buffer_have(&oid, &data);
+ i++;
+ if (i % 20 == 0) {
+ if (t->cancelled.val) {
+ git_error_set(GIT_ERROR_NET, "The fetch was cancelled by the user");
+ error = GIT_EUSER;
+ goto on_error;
+ }
+
+ git_pkt_buffer_flush(&data);
+ if (git_str_oom(&data)) {
+ error = -1;
+ goto on_error;
+ }
+
+ if ((error = git_smart__negotiation_step(&t->parent, data.ptr, data.size)) < 0)
+ goto on_error;
+
+ git_str_clear(&data);
+ if (t->caps.multi_ack || t->caps.multi_ack_detailed) {
+ if ((error = store_common(t)) < 0)
+ goto on_error;
+ } else {
+ if ((error = recv_pkt(NULL, &pkt_type, t)) < 0)
+ goto on_error;
+
+ if (pkt_type == GIT_PKT_ACK) {
+ break;
+ } else if (pkt_type == GIT_PKT_NAK) {
+ continue;
+ } else {
+ git_error_set(GIT_ERROR_NET, "unexpected pkt type");
+ error = -1;
+ goto on_error;
+ }
+ }
+ }
+
+ if (t->common.length > 0)
+ break;
+
+ if (i % 20 == 0 && t->rpc) {
+ git_pkt_ack *pkt;
+ unsigned int j;
+
+ if ((error = git_pkt_buffer_wants(wants, &t->caps, &data)) < 0)
+ goto on_error;
+
+ git_vector_foreach(&t->common, j, pkt) {
+ if ((error = git_pkt_buffer_have(&pkt->oid, &data)) < 0)
+ goto on_error;
+ }
+
+ if (git_str_oom(&data)) {
+ error = -1;
+ goto on_error;
+ }
+ }
+ }
+
+ /* Tell the other end that we're done negotiating */
+ if (t->rpc && t->common.length > 0) {
+ git_pkt_ack *pkt;
+ unsigned int j;
+
+ if ((error = git_pkt_buffer_wants(wants, &t->caps, &data)) < 0)
+ goto on_error;
+
+ git_vector_foreach(&t->common, j, pkt) {
+ if ((error = git_pkt_buffer_have(&pkt->oid, &data)) < 0)
+ goto on_error;
+ }
+
+ if (git_str_oom(&data)) {
+ error = -1;
+ goto on_error;
+ }
+ }
+
+ if ((error = git_pkt_buffer_done(&data)) < 0)
+ goto on_error;
+
+ if (t->cancelled.val) {
+ git_error_set(GIT_ERROR_NET, "the fetch was cancelled");
+ error = GIT_EUSER;
+ goto on_error;
+ }
+
+ if ((error = git_smart__negotiation_step(&t->parent, data.ptr, data.size)) < 0)
+ goto on_error;
+
+ git_str_dispose(&data);
+ git_revwalk_free(walk);
+
+ /* Now let's eat up whatever the server gives us */
+ if (!t->caps.multi_ack && !t->caps.multi_ack_detailed) {
+ if ((error = recv_pkt(NULL, &pkt_type, t)) < 0)
+ return error;
+
+ if (pkt_type != GIT_PKT_ACK && pkt_type != GIT_PKT_NAK) {
+ git_error_set(GIT_ERROR_NET, "unexpected pkt type");
+ return -1;
+ }
+ } else {
+ error = wait_while_ack(t);
+ }
+
+ return error;
+
+on_error:
+ git_revwalk_free(walk);
+ git_str_dispose(&data);
+ return error;
+}
+
+int git_smart__shallow_roots(git_oidarray *out, git_transport *transport)
+{
+ transport_smart *t = (transport_smart *)transport;
+ size_t len;
+
+ GIT_ERROR_CHECK_ALLOC_MULTIPLY(&len, t->shallow_roots.size, sizeof(git_oid));
+
+ out->count = t->shallow_roots.size;
+
+ if (len) {
+ out->ids = git__malloc(len);
+ memcpy(out->ids, t->shallow_roots.ptr, len);
+ } else {
+ out->ids = NULL;
+ }
+
+ return 0;
+}
+
+static int no_sideband(
+ transport_smart *t,
+ struct git_odb_writepack *writepack,
+ git_indexer_progress *stats)
+{
+ int recvd;
+
+ do {
+ if (t->cancelled.val) {
+ git_error_set(GIT_ERROR_NET, "the fetch was cancelled by the user");
+ return GIT_EUSER;
+ }
+
+ if (writepack->append(writepack, t->buffer.data, t->buffer.len, stats) < 0)
+ return -1;
+
+ git_staticstr_clear(&t->buffer);
+
+ if ((recvd = git_smart__recv(t)) < 0)
+ return recvd;
+ } while(recvd > 0);
+
+ if (writepack->commit(writepack, stats) < 0)
+ return -1;
+
+ return 0;
+}
+
+struct network_packetsize_payload
+{
+ git_indexer_progress_cb callback;
+ void *payload;
+ git_indexer_progress *stats;
+ size_t last_fired_bytes;
+};
+
+static int network_packetsize(size_t received, void *payload)
+{
+ struct network_packetsize_payload *npp = (struct network_packetsize_payload*)payload;
+
+ /* Accumulate bytes */
+ npp->stats->received_bytes += received;
+
+ /* Fire notification if the threshold is reached */
+ if ((npp->stats->received_bytes - npp->last_fired_bytes) > NETWORK_XFER_THRESHOLD) {
+ npp->last_fired_bytes = npp->stats->received_bytes;
+
+ if (npp->callback(npp->stats, npp->payload))
+ return GIT_EUSER;
+ }
+
+ return 0;
+}
+
+int git_smart__download_pack(
+ git_transport *transport,
+ git_repository *repo,
+ git_indexer_progress *stats)
+{
+ transport_smart *t = (transport_smart *)transport;
+ git_odb *odb;
+ struct git_odb_writepack *writepack = NULL;
+ int error = 0;
+ struct network_packetsize_payload npp = {0};
+
+ git_indexer_progress_cb progress_cb = t->connect_opts.callbacks.transfer_progress;
+ void *progress_payload = t->connect_opts.callbacks.payload;
+
+ memset(stats, 0, sizeof(git_indexer_progress));
+
+ if (progress_cb) {
+ npp.callback = progress_cb;
+ npp.payload = progress_payload;
+ npp.stats = stats;
+ t->packetsize_cb = &network_packetsize;
+ t->packetsize_payload = &npp;
+
+ /* We might have something in the buffer already from negotiate_fetch */
+ if (t->buffer.len > 0 && !t->cancelled.val) {
+ if (t->packetsize_cb(t->buffer.len, t->packetsize_payload))
+ git_atomic32_set(&t->cancelled, 1);
+ }
+ }
+
+ if ((error = git_repository_odb__weakptr(&odb, repo)) < 0 ||
+ ((error = git_odb_write_pack(&writepack, odb, progress_cb, progress_payload)) != 0))
+ goto done;
+
+ /*
+ * If the remote doesn't support the side-band, we can feed
+ * the data directly to the pack writer. Otherwise, we need to
+ * check which one belongs there.
+ */
+ if (!t->caps.side_band && !t->caps.side_band_64k) {
+ error = no_sideband(t, writepack, stats);
+ goto done;
+ }
+
+ do {
+ git_pkt *pkt = NULL;
+
+ /* Check cancellation before network call */
+ if (t->cancelled.val) {
+ git_error_clear();
+ error = GIT_EUSER;
+ goto done;
+ }
+
+ if ((error = recv_pkt(&pkt, NULL, t)) >= 0) {
+ /* Check cancellation after network call */
+ if (t->cancelled.val) {
+ git_error_clear();
+ error = GIT_EUSER;
+ } else if (pkt->type == GIT_PKT_PROGRESS) {
+ if (t->connect_opts.callbacks.sideband_progress) {
+ git_pkt_progress *p = (git_pkt_progress *) pkt;
+
+ if (p->len > INT_MAX) {
+ git_error_set(GIT_ERROR_NET, "oversized progress message");
+ error = GIT_ERROR;
+ goto done;
+ }
+
+ error = t->connect_opts.callbacks.sideband_progress(p->data, (int)p->len, t->connect_opts.callbacks.payload);
+ }
+ } else if (pkt->type == GIT_PKT_DATA) {
+ git_pkt_data *p = (git_pkt_data *) pkt;
+
+ if (p->len)
+ error = writepack->append(writepack, p->data, p->len, stats);
+ } else if (pkt->type == GIT_PKT_FLUSH) {
+ /* A flush indicates the end of the packfile */
+ git__free(pkt);
+ break;
+ }
+ }
+
+ git_pkt_free(pkt);
+
+ if (error < 0)
+ goto done;
+
+ } while (1);
+
+ /*
+ * Trailing execution of progress_cb, if necessary...
+ * Only the callback through the npp datastructure currently
+ * updates the last_fired_bytes value. It is possible that
+ * progress has already been reported with the correct
+ * "received_bytes" value, but until (if?) this is unified
+ * then we will report progress again to be sure that the
+ * correct last received_bytes value is reported.
+ */
+ if (npp.callback && npp.stats->received_bytes > npp.last_fired_bytes) {
+ error = npp.callback(npp.stats, npp.payload);
+ if (error != 0)
+ goto done;
+ }
+
+ error = writepack->commit(writepack, stats);
+
+done:
+ if (writepack)
+ writepack->free(writepack);
+ if (progress_cb) {
+ t->packetsize_cb = NULL;
+ t->packetsize_payload = NULL;
+ }
+
+ return error;
+}
+
+static int gen_pktline(git_str *buf, git_push *push)
+{
+ push_spec *spec;
+ size_t i, len;
+ char old_id[GIT_OID_SHA1_HEXSIZE+1], new_id[GIT_OID_SHA1_HEXSIZE+1];
+
+ old_id[GIT_OID_SHA1_HEXSIZE] = '\0'; new_id[GIT_OID_SHA1_HEXSIZE] = '\0';
+
+ git_vector_foreach(&push->specs, i, spec) {
+ len = 2*GIT_OID_SHA1_HEXSIZE + 7 + strlen(spec->refspec.dst);
+
+ if (i == 0) {
+ ++len; /* '\0' */
+ if (push->report_status)
+ len += strlen(GIT_CAP_REPORT_STATUS) + 1;
+ len += strlen(GIT_CAP_SIDE_BAND_64K) + 1;
+ }
+
+ git_oid_fmt(old_id, &spec->roid);
+ git_oid_fmt(new_id, &spec->loid);
+
+ git_str_printf(buf, "%04"PRIxZ"%s %s %s", len, old_id, new_id, spec->refspec.dst);
+
+ if (i == 0) {
+ git_str_putc(buf, '\0');
+ /* Core git always starts their capabilities string with a space */
+ if (push->report_status) {
+ git_str_putc(buf, ' ');
+ git_str_printf(buf, GIT_CAP_REPORT_STATUS);
+ }
+ git_str_putc(buf, ' ');
+ git_str_printf(buf, GIT_CAP_SIDE_BAND_64K);
+ }
+
+ git_str_putc(buf, '\n');
+ }
+
+ git_str_puts(buf, "0000");
+ return git_str_oom(buf) ? -1 : 0;
+}
+
+static int add_push_report_pkt(git_push *push, git_pkt *pkt)
+{
+ push_status *status;
+
+ switch (pkt->type) {
+ case GIT_PKT_OK:
+ status = git__calloc(1, sizeof(push_status));
+ GIT_ERROR_CHECK_ALLOC(status);
+ status->msg = NULL;
+ status->ref = git__strdup(((git_pkt_ok *)pkt)->ref);
+ if (!status->ref ||
+ git_vector_insert(&push->status, status) < 0) {
+ git_push_status_free(status);
+ return -1;
+ }
+ break;
+ case GIT_PKT_NG:
+ status = git__calloc(1, sizeof(push_status));
+ GIT_ERROR_CHECK_ALLOC(status);
+ status->ref = git__strdup(((git_pkt_ng *)pkt)->ref);
+ status->msg = git__strdup(((git_pkt_ng *)pkt)->msg);
+ if (!status->ref || !status->msg ||
+ git_vector_insert(&push->status, status) < 0) {
+ git_push_status_free(status);
+ return -1;
+ }
+ break;
+ case GIT_PKT_UNPACK:
+ push->unpack_ok = ((git_pkt_unpack *)pkt)->unpack_ok;
+ break;
+ case GIT_PKT_FLUSH:
+ return GIT_ITEROVER;
+ default:
+ git_error_set(GIT_ERROR_NET, "report-status: protocol error");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int add_push_report_sideband_pkt(git_push *push, git_pkt_data *data_pkt, git_str *data_pkt_buf)
+{
+ git_pkt *pkt;
+ git_pkt_parse_data pkt_parse_data = { 0 };
+ const char *line, *line_end = NULL;
+ size_t line_len;
+ int error;
+ int reading_from_buf = data_pkt_buf->size > 0;
+
+ if (reading_from_buf) {
+ /* We had an existing partial packet, so add the new
+ * packet to the buffer and parse the whole thing */
+ git_str_put(data_pkt_buf, data_pkt->data, data_pkt->len);
+ line = data_pkt_buf->ptr;
+ line_len = data_pkt_buf->size;
+ }
+ else {
+ line = data_pkt->data;
+ line_len = data_pkt->len;
+ }
+
+ while (line_len > 0) {
+ error = git_pkt_parse_line(&pkt, &line_end, line, line_len, &pkt_parse_data);
+
+ if (error == GIT_EBUFS) {
+ /* Buffer the data when the inner packet is split
+ * across multiple sideband packets */
+ if (!reading_from_buf)
+ git_str_put(data_pkt_buf, line, line_len);
+ error = 0;
+ goto done;
+ }
+ else if (error < 0)
+ goto done;
+
+ /* Advance in the buffer */
+ line_len -= (line_end - line);
+ line = line_end;
+
+ error = add_push_report_pkt(push, pkt);
+
+ git_pkt_free(pkt);
+
+ if (error < 0 && error != GIT_ITEROVER)
+ goto done;
+ }
+
+ error = 0;
+
+done:
+ if (reading_from_buf)
+ git_str_consume(data_pkt_buf, line_end);
+ return error;
+}
+
+static int parse_report(transport_smart *transport, git_push *push)
+{
+ git_pkt *pkt = NULL;
+ git_pkt_parse_data pkt_parse_data = { 0 };
+ const char *line_end = NULL;
+ int error, recvd;
+ git_str data_pkt_buf = GIT_STR_INIT;
+
+ for (;;) {
+ if (transport->buffer.len > 0)
+ error = git_pkt_parse_line(&pkt, &line_end,
+ transport->buffer.data,
+ transport->buffer.len,
+ &pkt_parse_data);
+ else
+ error = GIT_EBUFS;
+
+ if (error < 0 && error != GIT_EBUFS) {
+ error = -1;
+ goto done;
+ }
+
+ if (error == GIT_EBUFS) {
+ if ((recvd = git_smart__recv(transport)) < 0) {
+ error = recvd;
+ goto done;
+ }
+
+ if (recvd == 0) {
+ git_error_set(GIT_ERROR_NET, "early EOF");
+ error = GIT_EEOF;
+ goto done;
+ }
+ continue;
+ }
+
+ git_staticstr_consume(&transport->buffer, line_end);
+ error = 0;
+
+ switch (pkt->type) {
+ case GIT_PKT_DATA:
+ /* This is a sideband packet which contains other packets */
+ error = add_push_report_sideband_pkt(push, (git_pkt_data *)pkt, &data_pkt_buf);
+ break;
+ case GIT_PKT_ERR:
+ git_error_set(GIT_ERROR_NET, "report-status: Error reported: %s",
+ ((git_pkt_err *)pkt)->error);
+ error = -1;
+ break;
+ case GIT_PKT_PROGRESS:
+ if (transport->connect_opts.callbacks.sideband_progress) {
+ git_pkt_progress *p = (git_pkt_progress *) pkt;
+
+ if (p->len > INT_MAX) {
+ git_error_set(GIT_ERROR_NET, "oversized progress message");
+ error = GIT_ERROR;
+ goto done;
+ }
+
+ error = transport->connect_opts.callbacks.sideband_progress(p->data, (int)p->len, transport->connect_opts.callbacks.payload);
+ }
+ break;
+ default:
+ error = add_push_report_pkt(push, pkt);
+ break;
+ }
+
+ git_pkt_free(pkt);
+
+ /* add_push_report_pkt returns GIT_ITEROVER when it receives a flush */
+ if (error == GIT_ITEROVER) {
+ error = 0;
+ if (data_pkt_buf.size > 0) {
+ /* If there was data remaining in the pack data buffer,
+ * then the server sent a partial pkt-line */
+ git_error_set(GIT_ERROR_NET, "incomplete pack data pkt-line");
+ error = GIT_ERROR;
+ }
+ goto done;
+ }
+
+ if (error < 0) {
+ goto done;
+ }
+ }
+done:
+ git_str_dispose(&data_pkt_buf);
+ return error;
+}
+
+static int add_ref_from_push_spec(git_vector *refs, push_spec *push_spec)
+{
+ git_pkt_ref *added = git__calloc(1, sizeof(git_pkt_ref));
+ GIT_ERROR_CHECK_ALLOC(added);
+
+ added->type = GIT_PKT_REF;
+ git_oid_cpy(&added->head.oid, &push_spec->loid);
+ added->head.name = git__strdup(push_spec->refspec.dst);
+
+ if (!added->head.name ||
+ git_vector_insert(refs, added) < 0) {
+ git_pkt_free((git_pkt *)added);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int update_refs_from_report(
+ git_vector *refs,
+ git_vector *push_specs,
+ git_vector *push_report)
+{
+ git_pkt_ref *ref;
+ push_spec *push_spec;
+ push_status *push_status;
+ size_t i, j, refs_len;
+ int cmp;
+
+ /* For each push spec we sent to the server, we should have
+ * gotten back a status packet in the push report */
+ if (push_specs->length != push_report->length) {
+ git_error_set(GIT_ERROR_NET, "report-status: protocol error");
+ return -1;
+ }
+
+ /* We require that push_specs be sorted with push_spec_rref_cmp,
+ * and that push_report be sorted with push_status_ref_cmp */
+ git_vector_sort(push_specs);
+ git_vector_sort(push_report);
+
+ git_vector_foreach(push_specs, i, push_spec) {
+ push_status = git_vector_get(push_report, i);
+
+ /* For each push spec we sent to the server, we should have
+ * gotten back a status packet in the push report which matches */
+ if (strcmp(push_spec->refspec.dst, push_status->ref)) {
+ git_error_set(GIT_ERROR_NET, "report-status: protocol error");
+ return -1;
+ }
+ }
+
+ /* We require that refs be sorted with ref_name_cmp */
+ git_vector_sort(refs);
+ i = j = 0;
+ refs_len = refs->length;
+
+ /* Merge join push_specs with refs */
+ while (i < push_specs->length && j < refs_len) {
+ push_spec = git_vector_get(push_specs, i);
+ push_status = git_vector_get(push_report, i);
+ ref = git_vector_get(refs, j);
+
+ cmp = strcmp(push_spec->refspec.dst, ref->head.name);
+
+ /* Iterate appropriately */
+ if (cmp <= 0) i++;
+ if (cmp >= 0) j++;
+
+ /* Add case */
+ if (cmp < 0 &&
+ !push_status->msg &&
+ add_ref_from_push_spec(refs, push_spec) < 0)
+ return -1;
+
+ /* Update case, delete case */
+ if (cmp == 0 &&
+ !push_status->msg)
+ git_oid_cpy(&ref->head.oid, &push_spec->loid);
+ }
+
+ for (; i < push_specs->length; i++) {
+ push_spec = git_vector_get(push_specs, i);
+ push_status = git_vector_get(push_report, i);
+
+ /* Add case */
+ if (!push_status->msg &&
+ add_ref_from_push_spec(refs, push_spec) < 0)
+ return -1;
+ }
+
+ /* Remove any refs which we updated to have a zero OID. */
+ git_vector_rforeach(refs, i, ref) {
+ if (git_oid_is_zero(&ref->head.oid)) {
+ git_vector_remove(refs, i);
+ git_pkt_free((git_pkt *)ref);
+ }
+ }
+
+ git_vector_sort(refs);
+
+ return 0;
+}
+
+struct push_packbuilder_payload
+{
+ git_smart_subtransport_stream *stream;
+ git_packbuilder *pb;
+ git_push_transfer_progress_cb cb;
+ void *cb_payload;
+ size_t last_bytes;
+ uint64_t last_progress_report_time;
+};
+
+static int stream_thunk(void *buf, size_t size, void *data)
+{
+ int error = 0;
+ struct push_packbuilder_payload *payload = data;
+
+ if ((error = payload->stream->write(payload->stream, (const char *)buf, size)) < 0)
+ return error;
+
+ if (payload->cb) {
+ uint64_t current_time = git_time_monotonic();
+ uint64_t elapsed = current_time - payload->last_progress_report_time;
+ payload->last_bytes += size;
+
+ if (elapsed >= MIN_PROGRESS_UPDATE_INTERVAL) {
+ payload->last_progress_report_time = current_time;
+ error = payload->cb(payload->pb->nr_written, payload->pb->nr_objects, payload->last_bytes, payload->cb_payload);
+ }
+ }
+
+ return error;
+}
+
+int git_smart__push(git_transport *transport, git_push *push)
+{
+ transport_smart *t = (transport_smart *)transport;
+ git_remote_callbacks *cbs = &t->connect_opts.callbacks;
+ struct push_packbuilder_payload packbuilder_payload = {0};
+ git_str pktline = GIT_STR_INIT;
+ int error = 0, need_pack = 0;
+ push_spec *spec;
+ unsigned int i;
+
+ packbuilder_payload.pb = push->pb;
+
+ if (cbs && cbs->push_transfer_progress) {
+ packbuilder_payload.cb = cbs->push_transfer_progress;
+ packbuilder_payload.cb_payload = cbs->payload;
+ }
+
+#ifdef PUSH_DEBUG
+{
+ git_remote_head *head;
+ char hex[GIT_OID_SHA1_HEXSIZE+1]; hex[GIT_OID_SHA1_HEXSIZE] = '\0';
+
+ git_vector_foreach(&push->remote->refs, i, head) {
+ git_oid_fmt(hex, &head->oid);
+ fprintf(stderr, "%s (%s)\n", hex, head->name);
+ }
+
+ git_vector_foreach(&push->specs, i, spec) {
+ git_oid_fmt(hex, &spec->roid);
+ fprintf(stderr, "%s (%s) -> ", hex, spec->lref);
+ git_oid_fmt(hex, &spec->loid);
+ fprintf(stderr, "%s (%s)\n", hex, spec->rref ?
+ spec->rref : spec->lref);
+ }
+}
+#endif
+
+ /*
+ * Figure out if we need to send a packfile; which is in all
+ * cases except when we only send delete commands
+ */
+ git_vector_foreach(&push->specs, i, spec) {
+ if (spec->refspec.src && spec->refspec.src[0] != '\0') {
+ need_pack = 1;
+ break;
+ }
+ }
+
+ /* prepare pack before sending pack header to avoid timeouts */
+ if (need_pack && ((error = git_packbuilder__prepare(push->pb))) < 0)
+ goto done;
+
+ if ((error = git_smart__get_push_stream(t, &packbuilder_payload.stream)) < 0 ||
+ (error = gen_pktline(&pktline, push)) < 0 ||
+ (error = packbuilder_payload.stream->write(packbuilder_payload.stream, git_str_cstr(&pktline), git_str_len(&pktline))) < 0)
+ goto done;
+
+ if (need_pack &&
+ (error = git_packbuilder_foreach(push->pb, &stream_thunk, &packbuilder_payload)) < 0)
+ goto done;
+
+ /* If we sent nothing or the server doesn't support report-status, then
+ * we consider the pack to have been unpacked successfully */
+ if (!push->specs.length || !push->report_status)
+ push->unpack_ok = 1;
+ else if ((error = parse_report(t, push)) < 0)
+ goto done;
+
+ /* If progress is being reported write the final report */
+ if (cbs && cbs->push_transfer_progress) {
+ error = cbs->push_transfer_progress(
+ push->pb->nr_written,
+ push->pb->nr_objects,
+ packbuilder_payload.last_bytes,
+ cbs->payload);
+
+ if (error < 0)
+ goto done;
+ }
+
+ if (push->status.length) {
+ error = update_refs_from_report(&t->refs, &push->specs, &push->status);
+ if (error < 0)
+ goto done;
+
+ error = git_smart__update_heads(t, NULL);
+ }
+
+done:
+ git_str_dispose(&pktline);
+ return error;
+}
diff --git a/src/libgit2/transports/ssh.c b/src/libgit2/transports/ssh.c
new file mode 100644
index 0000000..de63d45
--- /dev/null
+++ b/src/libgit2/transports/ssh.c
@@ -0,0 +1,1147 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "ssh.h"
+
+#ifdef GIT_SSH
+#include <libssh2.h>
+#endif
+
+#include "runtime.h"
+#include "net.h"
+#include "smart.h"
+#include "streams/socket.h"
+#include "sysdir.h"
+
+#include "git2/credential.h"
+#include "git2/sys/credential.h"
+
+#ifdef GIT_SSH
+
+#define OWNING_SUBTRANSPORT(s) ((ssh_subtransport *)(s)->parent.subtransport)
+
+static const char cmd_uploadpack[] = "git-upload-pack";
+static const char cmd_receivepack[] = "git-receive-pack";
+
+typedef struct {
+ git_smart_subtransport_stream parent;
+ git_stream *io;
+ LIBSSH2_SESSION *session;
+ LIBSSH2_CHANNEL *channel;
+ const char *cmd;
+ git_net_url url;
+ unsigned sent_command : 1;
+} ssh_stream;
+
+typedef struct {
+ git_smart_subtransport parent;
+ transport_smart *owner;
+ ssh_stream *current_stream;
+ git_credential *cred;
+ char *cmd_uploadpack;
+ char *cmd_receivepack;
+} ssh_subtransport;
+
+static int list_auth_methods(int *out, LIBSSH2_SESSION *session, const char *username);
+
+static void ssh_error(LIBSSH2_SESSION *session, const char *errmsg)
+{
+ char *ssherr;
+ libssh2_session_last_error(session, &ssherr, NULL, 0);
+
+ git_error_set(GIT_ERROR_SSH, "%s: %s", errmsg, ssherr);
+}
+
+/*
+ * Create a git protocol request.
+ *
+ * For example: git-upload-pack '/libgit2/libgit2'
+ */
+static int gen_proto(git_str *request, const char *cmd, git_net_url *url)
+{
+ const char *repo;
+
+ repo = url->path;
+
+ if (repo && repo[0] == '/' && repo[1] == '~')
+ repo++;
+
+ if (!repo || !repo[0]) {
+ git_error_set(GIT_ERROR_NET, "malformed git protocol URL");
+ return -1;
+ }
+
+ git_str_puts(request, cmd);
+ git_str_puts(request, " '");
+ git_str_puts(request, repo);
+ git_str_puts(request, "'");
+
+ if (git_str_oom(request))
+ return -1;
+
+ return 0;
+}
+
+static int send_command(ssh_stream *s)
+{
+ int error;
+ git_str request = GIT_STR_INIT;
+
+ error = gen_proto(&request, s->cmd, &s->url);
+ if (error < 0)
+ goto cleanup;
+
+ error = libssh2_channel_exec(s->channel, request.ptr);
+ if (error < LIBSSH2_ERROR_NONE) {
+ ssh_error(s->session, "SSH could not execute request");
+ goto cleanup;
+ }
+
+ s->sent_command = 1;
+
+cleanup:
+ git_str_dispose(&request);
+ return error;
+}
+
+static int ssh_stream_read(
+ git_smart_subtransport_stream *stream,
+ char *buffer,
+ size_t buf_size,
+ size_t *bytes_read)
+{
+ int rc;
+ ssh_stream *s = GIT_CONTAINER_OF(stream, ssh_stream, parent);
+
+ *bytes_read = 0;
+
+ if (!s->sent_command && send_command(s) < 0)
+ return -1;
+
+ if ((rc = libssh2_channel_read(s->channel, buffer, buf_size)) < LIBSSH2_ERROR_NONE) {
+ ssh_error(s->session, "SSH could not read data");
+ return -1;
+ }
+
+ /*
+ * If we can't get anything out of stdout, it's typically a
+ * not-found error, so read from stderr and signal EOF on
+ * stderr.
+ */
+ if (rc == 0) {
+ if ((rc = libssh2_channel_read_stderr(s->channel, buffer, buf_size)) > 0) {
+ git_error_set(GIT_ERROR_SSH, "%*s", rc, buffer);
+ return GIT_EEOF;
+ } else if (rc < LIBSSH2_ERROR_NONE) {
+ ssh_error(s->session, "SSH could not read stderr");
+ return -1;
+ }
+ }
+
+
+ *bytes_read = rc;
+
+ return 0;
+}
+
+static int ssh_stream_write(
+ git_smart_subtransport_stream *stream,
+ const char *buffer,
+ size_t len)
+{
+ ssh_stream *s = GIT_CONTAINER_OF(stream, ssh_stream, parent);
+ size_t off = 0;
+ ssize_t ret = 0;
+
+ if (!s->sent_command && send_command(s) < 0)
+ return -1;
+
+ do {
+ ret = libssh2_channel_write(s->channel, buffer + off, len - off);
+ if (ret < 0)
+ break;
+
+ off += ret;
+
+ } while (off < len);
+
+ if (ret < 0) {
+ ssh_error(s->session, "SSH could not write data");
+ return -1;
+ }
+
+ return 0;
+}
+
+static void ssh_stream_free(git_smart_subtransport_stream *stream)
+{
+ ssh_stream *s = GIT_CONTAINER_OF(stream, ssh_stream, parent);
+ ssh_subtransport *t;
+
+ if (!stream)
+ return;
+
+ t = OWNING_SUBTRANSPORT(s);
+ t->current_stream = NULL;
+
+ if (s->channel) {
+ libssh2_channel_close(s->channel);
+ libssh2_channel_free(s->channel);
+ s->channel = NULL;
+ }
+
+ if (s->session) {
+ libssh2_session_disconnect(s->session, "closing transport");
+ libssh2_session_free(s->session);
+ s->session = NULL;
+ }
+
+ if (s->io) {
+ git_stream_close(s->io);
+ git_stream_free(s->io);
+ s->io = NULL;
+ }
+
+ git_net_url_dispose(&s->url);
+ git__free(s);
+}
+
+static int ssh_stream_alloc(
+ ssh_subtransport *t,
+ const char *cmd,
+ git_smart_subtransport_stream **stream)
+{
+ ssh_stream *s;
+
+ GIT_ASSERT_ARG(stream);
+
+ s = git__calloc(sizeof(ssh_stream), 1);
+ GIT_ERROR_CHECK_ALLOC(s);
+
+ s->parent.subtransport = &t->parent;
+ s->parent.read = ssh_stream_read;
+ s->parent.write = ssh_stream_write;
+ s->parent.free = ssh_stream_free;
+
+ s->cmd = cmd;
+
+ *stream = &s->parent;
+ return 0;
+}
+
+static int ssh_agent_auth(LIBSSH2_SESSION *session, git_credential_ssh_key *c) {
+ int rc = LIBSSH2_ERROR_NONE;
+
+ struct libssh2_agent_publickey *curr, *prev = NULL;
+
+ LIBSSH2_AGENT *agent = libssh2_agent_init(session);
+
+ if (agent == NULL)
+ return -1;
+
+ rc = libssh2_agent_connect(agent);
+
+ if (rc != LIBSSH2_ERROR_NONE) {
+ rc = LIBSSH2_ERROR_AUTHENTICATION_FAILED;
+ goto shutdown;
+ }
+
+ rc = libssh2_agent_list_identities(agent);
+
+ if (rc != LIBSSH2_ERROR_NONE)
+ goto shutdown;
+
+ while (1) {
+ rc = libssh2_agent_get_identity(agent, &curr, prev);
+
+ if (rc < 0)
+ goto shutdown;
+
+ /* rc is set to 1 whenever the ssh agent ran out of keys to check.
+ * Set the error code to authentication failure rather than erroring
+ * out with an untranslatable error code.
+ */
+ if (rc == 1) {
+ rc = LIBSSH2_ERROR_AUTHENTICATION_FAILED;
+ goto shutdown;
+ }
+
+ rc = libssh2_agent_userauth(agent, c->username, curr);
+
+ if (rc == 0)
+ break;
+
+ prev = curr;
+ }
+
+shutdown:
+
+ if (rc != LIBSSH2_ERROR_NONE)
+ ssh_error(session, "error authenticating");
+
+ libssh2_agent_disconnect(agent);
+ libssh2_agent_free(agent);
+
+ return rc;
+}
+
+static int _git_ssh_authenticate_session(
+ LIBSSH2_SESSION *session,
+ git_credential *cred)
+{
+ int rc;
+
+ do {
+ git_error_clear();
+ switch (cred->credtype) {
+ case GIT_CREDENTIAL_USERPASS_PLAINTEXT: {
+ git_credential_userpass_plaintext *c = (git_credential_userpass_plaintext *)cred;
+ rc = libssh2_userauth_password(session, c->username, c->password);
+ break;
+ }
+ case GIT_CREDENTIAL_SSH_KEY: {
+ git_credential_ssh_key *c = (git_credential_ssh_key *)cred;
+
+ if (c->privatekey)
+ rc = libssh2_userauth_publickey_fromfile(
+ session, c->username, c->publickey,
+ c->privatekey, c->passphrase);
+ else
+ rc = ssh_agent_auth(session, c);
+
+ break;
+ }
+ case GIT_CREDENTIAL_SSH_CUSTOM: {
+ git_credential_ssh_custom *c = (git_credential_ssh_custom *)cred;
+
+ rc = libssh2_userauth_publickey(
+ session, c->username, (const unsigned char *)c->publickey,
+ c->publickey_len, c->sign_callback, &c->payload);
+ break;
+ }
+ case GIT_CREDENTIAL_SSH_INTERACTIVE: {
+ void **abstract = libssh2_session_abstract(session);
+ git_credential_ssh_interactive *c = (git_credential_ssh_interactive *)cred;
+
+ /* ideally, we should be able to set this by calling
+ * libssh2_session_init_ex() instead of libssh2_session_init().
+ * libssh2's API is inconsistent here i.e. libssh2_userauth_publickey()
+ * allows you to pass the `abstract` as part of the call, whereas
+ * libssh2_userauth_keyboard_interactive() does not!
+ *
+ * The only way to set the `abstract` pointer is by calling
+ * libssh2_session_abstract(), which will replace the existing
+ * pointer as is done below. This is safe for now (at time of writing),
+ * but may not be valid in future.
+ */
+ *abstract = c->payload;
+
+ rc = libssh2_userauth_keyboard_interactive(
+ session, c->username, c->prompt_callback);
+ break;
+ }
+#ifdef GIT_SSH_MEMORY_CREDENTIALS
+ case GIT_CREDENTIAL_SSH_MEMORY: {
+ git_credential_ssh_key *c = (git_credential_ssh_key *)cred;
+
+ GIT_ASSERT(c->username);
+ GIT_ASSERT(c->privatekey);
+
+ rc = libssh2_userauth_publickey_frommemory(
+ session,
+ c->username,
+ strlen(c->username),
+ c->publickey,
+ c->publickey ? strlen(c->publickey) : 0,
+ c->privatekey,
+ strlen(c->privatekey),
+ c->passphrase);
+ break;
+ }
+#endif
+ default:
+ rc = LIBSSH2_ERROR_AUTHENTICATION_FAILED;
+ }
+ } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
+
+ if (rc == LIBSSH2_ERROR_PASSWORD_EXPIRED ||
+ rc == LIBSSH2_ERROR_AUTHENTICATION_FAILED ||
+ rc == LIBSSH2_ERROR_PUBLICKEY_UNVERIFIED)
+ return GIT_EAUTH;
+
+ if (rc != LIBSSH2_ERROR_NONE) {
+ if (!git_error_last())
+ ssh_error(session, "Failed to authenticate SSH session");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int request_creds(git_credential **out, ssh_subtransport *t, const char *user, int auth_methods)
+{
+ int error, no_callback = 0;
+ git_credential *cred = NULL;
+
+ if (!t->owner->connect_opts.callbacks.credentials) {
+ no_callback = 1;
+ } else {
+ error = t->owner->connect_opts.callbacks.credentials(
+ &cred,
+ t->owner->url,
+ user,
+ auth_methods,
+ t->owner->connect_opts.callbacks.payload);
+
+ if (error == GIT_PASSTHROUGH) {
+ no_callback = 1;
+ } else if (error < 0) {
+ return error;
+ } else if (!cred) {
+ git_error_set(GIT_ERROR_SSH, "callback failed to initialize SSH credentials");
+ return -1;
+ }
+ }
+
+ if (no_callback) {
+ git_error_set(GIT_ERROR_SSH, "authentication required but no callback set");
+ return GIT_EAUTH;
+ }
+
+ if (!(cred->credtype & auth_methods)) {
+ cred->free(cred);
+ git_error_set(GIT_ERROR_SSH, "authentication callback returned unsupported credentials type");
+ return GIT_EAUTH;
+ }
+
+ *out = cred;
+
+ return 0;
+}
+
+#define SSH_DIR ".ssh"
+#define KNOWN_HOSTS_FILE "known_hosts"
+
+/*
+ * Load the known_hosts file.
+ *
+ * Returns success but leaves the output NULL if we couldn't find the file.
+ */
+static int load_known_hosts(LIBSSH2_KNOWNHOSTS **hosts, LIBSSH2_SESSION *session)
+{
+ git_str path = GIT_STR_INIT, sshdir = GIT_STR_INIT;
+ LIBSSH2_KNOWNHOSTS *known_hosts = NULL;
+ int error;
+
+ GIT_ASSERT_ARG(hosts);
+
+ if ((error = git_sysdir_expand_homedir_file(&sshdir, SSH_DIR)) < 0 ||
+ (error = git_str_joinpath(&path, git_str_cstr(&sshdir), KNOWN_HOSTS_FILE)) < 0)
+ goto out;
+
+ if ((known_hosts = libssh2_knownhost_init(session)) == NULL) {
+ ssh_error(session, "error initializing known hosts");
+ error = -1;
+ goto out;
+ }
+
+ /*
+ * Try to read the file and consider not finding it as not trusting the
+ * host rather than an error.
+ */
+ error = libssh2_knownhost_readfile(known_hosts, git_str_cstr(&path), LIBSSH2_KNOWNHOST_FILE_OPENSSH);
+ if (error == LIBSSH2_ERROR_FILE)
+ error = 0;
+ if (error < 0)
+ ssh_error(session, "error reading known_hosts");
+
+out:
+ *hosts = known_hosts;
+
+ git_str_dispose(&sshdir);
+ git_str_dispose(&path);
+
+ return error;
+}
+
+static void add_hostkey_pref_if_avail(
+ LIBSSH2_KNOWNHOSTS *known_hosts,
+ const char *hostname,
+ int port,
+ git_str *prefs,
+ int type,
+ const char *type_name)
+{
+ struct libssh2_knownhost *host = NULL;
+ const char key = '\0';
+ int mask = LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW | type;
+ int error;
+
+ error = libssh2_knownhost_checkp(known_hosts, hostname, port, &key, 1, mask, &host);
+ if (error == LIBSSH2_KNOWNHOST_CHECK_MISMATCH) {
+ if (git_str_len(prefs) > 0) {
+ git_str_putc(prefs, ',');
+ }
+ git_str_puts(prefs, type_name);
+ }
+}
+
+/*
+ * We figure out what kind of key we want to ask the remote for by trying to
+ * look it up with a nonsense key and using that mismatch to figure out what key
+ * we do have stored for the host.
+ *
+ * Populates prefs with the string to pass to libssh2_session_method_pref.
+ */
+static void find_hostkey_preference(
+ LIBSSH2_KNOWNHOSTS *known_hosts,
+ const char *hostname,
+ int port,
+ git_str *prefs)
+{
+ /*
+ * The order here is important as it indicates the priority of what will
+ * be preferred.
+ */
+#ifdef LIBSSH2_KNOWNHOST_KEY_ED25519
+ add_hostkey_pref_if_avail(known_hosts, hostname, port, prefs, LIBSSH2_KNOWNHOST_KEY_ED25519, "ssh-ed25519");
+#endif
+#ifdef LIBSSH2_KNOWNHOST_KEY_ECDSA_256
+ add_hostkey_pref_if_avail(known_hosts, hostname, port, prefs, LIBSSH2_KNOWNHOST_KEY_ECDSA_256, "ecdsa-sha2-nistp256");
+ add_hostkey_pref_if_avail(known_hosts, hostname, port, prefs, LIBSSH2_KNOWNHOST_KEY_ECDSA_384, "ecdsa-sha2-nistp384");
+ add_hostkey_pref_if_avail(known_hosts, hostname, port, prefs, LIBSSH2_KNOWNHOST_KEY_ECDSA_521, "ecdsa-sha2-nistp521");
+#endif
+ add_hostkey_pref_if_avail(known_hosts, hostname, port, prefs, LIBSSH2_KNOWNHOST_KEY_SSHRSA, "ssh-rsa");
+}
+
+static int _git_ssh_session_create(
+ LIBSSH2_SESSION **session,
+ LIBSSH2_KNOWNHOSTS **hosts,
+ const char *hostname,
+ int port,
+ git_stream *io)
+{
+ git_socket_stream *socket = GIT_CONTAINER_OF(io, git_socket_stream, parent);
+ LIBSSH2_SESSION *s;
+ LIBSSH2_KNOWNHOSTS *known_hosts;
+ git_str prefs = GIT_STR_INIT;
+ int rc = 0;
+
+ GIT_ASSERT_ARG(session);
+ GIT_ASSERT_ARG(hosts);
+
+ s = libssh2_session_init();
+ if (!s) {
+ git_error_set(GIT_ERROR_NET, "failed to initialize SSH session");
+ return -1;
+ }
+
+ if ((rc = load_known_hosts(&known_hosts, s)) < 0) {
+ ssh_error(s, "error loading known_hosts");
+ libssh2_session_free(s);
+ return -1;
+ }
+
+ find_hostkey_preference(known_hosts, hostname, port, &prefs);
+ if (git_str_len(&prefs) > 0) {
+ do {
+ rc = libssh2_session_method_pref(s, LIBSSH2_METHOD_HOSTKEY, git_str_cstr(&prefs));
+ } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
+ if (rc != LIBSSH2_ERROR_NONE) {
+ ssh_error(s, "failed to set hostkey preference");
+ goto on_error;
+ }
+ }
+ git_str_dispose(&prefs);
+
+ do {
+ rc = libssh2_session_handshake(s, socket->s);
+ } while (LIBSSH2_ERROR_EAGAIN == rc || LIBSSH2_ERROR_TIMEOUT == rc);
+
+ if (rc != LIBSSH2_ERROR_NONE) {
+ ssh_error(s, "failed to start SSH session");
+ goto on_error;
+ }
+
+ libssh2_session_set_blocking(s, 1);
+
+ *session = s;
+ *hosts = known_hosts;
+
+ return 0;
+
+on_error:
+ libssh2_knownhost_free(known_hosts);
+ libssh2_session_free(s);
+ return -1;
+}
+
+
+/*
+ * Returns the typemask argument to pass to libssh2_knownhost_check{,p} based on
+ * the type of key that libssh2_session_hostkey returns.
+ */
+static int fingerprint_type_mask(int keytype)
+{
+ int mask = LIBSSH2_KNOWNHOST_TYPE_PLAIN | LIBSSH2_KNOWNHOST_KEYENC_RAW;
+ return mask;
+
+ switch (keytype) {
+ case LIBSSH2_HOSTKEY_TYPE_RSA:
+ mask |= LIBSSH2_KNOWNHOST_KEY_SSHRSA;
+ break;
+ case LIBSSH2_HOSTKEY_TYPE_DSS:
+ mask |= LIBSSH2_KNOWNHOST_KEY_SSHDSS;
+ break;
+#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_256
+ case LIBSSH2_HOSTKEY_TYPE_ECDSA_256:
+ mask |= LIBSSH2_KNOWNHOST_KEY_ECDSA_256;
+ break;
+ case LIBSSH2_HOSTKEY_TYPE_ECDSA_384:
+ mask |= LIBSSH2_KNOWNHOST_KEY_ECDSA_384;
+ break;
+ case LIBSSH2_HOSTKEY_TYPE_ECDSA_521:
+ mask |= LIBSSH2_KNOWNHOST_KEY_ECDSA_521;
+ break;
+#endif
+#ifdef LIBSSH2_HOSTKEY_TYPE_ED25519
+ case LIBSSH2_HOSTKEY_TYPE_ED25519:
+ mask |= LIBSSH2_KNOWNHOST_KEY_ED25519;
+ break;
+#endif
+ }
+
+ return mask;
+}
+
+/*
+ * Check the host against the user's known_hosts file.
+ *
+ * Returns 1/0 for valid/''not-valid or <0 for an error
+ */
+static int check_against_known_hosts(
+ LIBSSH2_SESSION *session,
+ LIBSSH2_KNOWNHOSTS *known_hosts,
+ const char *hostname,
+ int port,
+ const char *key,
+ size_t key_len,
+ int key_type)
+{
+ int check, typemask, ret = 0;
+ struct libssh2_knownhost *host = NULL;
+
+ if (known_hosts == NULL)
+ return 0;
+
+ typemask = fingerprint_type_mask(key_type);
+ check = libssh2_knownhost_checkp(known_hosts, hostname, port, key, key_len, typemask, &host);
+ if (check == LIBSSH2_KNOWNHOST_CHECK_FAILURE) {
+ ssh_error(session, "error checking for known host");
+ return -1;
+ }
+
+ ret = check == LIBSSH2_KNOWNHOST_CHECK_MATCH ? 1 : 0;
+
+ return ret;
+}
+
+/*
+ * Perform the check for the session's certificate against known hosts if
+ * possible and then ask the user if they have a callback.
+ *
+ * Returns 1/0 for valid/not-valid or <0 for an error
+ */
+static int check_certificate(
+ LIBSSH2_SESSION *session,
+ LIBSSH2_KNOWNHOSTS *known_hosts,
+ git_transport_certificate_check_cb check_cb,
+ void *check_cb_payload,
+ const char *host,
+ int port)
+{
+ git_cert_hostkey cert = {{ 0 }};
+ const char *key;
+ size_t cert_len;
+ int cert_type, cert_valid = 0, error = 0;
+
+ if ((key = libssh2_session_hostkey(session, &cert_len, &cert_type)) == NULL) {
+ ssh_error(session, "failed to retrieve hostkey");
+ return -1;
+ }
+
+ if ((cert_valid = check_against_known_hosts(session, known_hosts, host, port, key, cert_len, cert_type)) < 0)
+ return -1;
+
+ cert.parent.cert_type = GIT_CERT_HOSTKEY_LIBSSH2;
+ if (key != NULL) {
+ cert.type |= GIT_CERT_SSH_RAW;
+ cert.hostkey = key;
+ cert.hostkey_len = cert_len;
+ switch (cert_type) {
+ case LIBSSH2_HOSTKEY_TYPE_RSA:
+ cert.raw_type = GIT_CERT_SSH_RAW_TYPE_RSA;
+ break;
+ case LIBSSH2_HOSTKEY_TYPE_DSS:
+ cert.raw_type = GIT_CERT_SSH_RAW_TYPE_DSS;
+ break;
+
+#ifdef LIBSSH2_HOSTKEY_TYPE_ECDSA_256
+ case LIBSSH2_HOSTKEY_TYPE_ECDSA_256:
+ cert.raw_type = GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_256;
+ break;
+ case LIBSSH2_HOSTKEY_TYPE_ECDSA_384:
+ cert.raw_type = GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_384;
+ break;
+ case LIBSSH2_KNOWNHOST_KEY_ECDSA_521:
+ cert.raw_type = GIT_CERT_SSH_RAW_TYPE_KEY_ECDSA_521;
+ break;
+#endif
+
+#ifdef LIBSSH2_HOSTKEY_TYPE_ED25519
+ case LIBSSH2_HOSTKEY_TYPE_ED25519:
+ cert.raw_type = GIT_CERT_SSH_RAW_TYPE_KEY_ED25519;
+ break;
+#endif
+ default:
+ cert.raw_type = GIT_CERT_SSH_RAW_TYPE_UNKNOWN;
+ }
+ }
+
+#ifdef LIBSSH2_HOSTKEY_HASH_SHA256
+ key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA256);
+ if (key != NULL) {
+ cert.type |= GIT_CERT_SSH_SHA256;
+ memcpy(&cert.hash_sha256, key, 32);
+ }
+#endif
+
+ key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_SHA1);
+ if (key != NULL) {
+ cert.type |= GIT_CERT_SSH_SHA1;
+ memcpy(&cert.hash_sha1, key, 20);
+ }
+
+ key = libssh2_hostkey_hash(session, LIBSSH2_HOSTKEY_HASH_MD5);
+ if (key != NULL) {
+ cert.type |= GIT_CERT_SSH_MD5;
+ memcpy(&cert.hash_md5, key, 16);
+ }
+
+ if (cert.type == 0) {
+ git_error_set(GIT_ERROR_SSH, "unable to get the host key");
+ return -1;
+ }
+
+ git_error_clear();
+ error = 0;
+ if (!cert_valid) {
+ git_error_set(GIT_ERROR_SSH, "invalid or unknown remote ssh hostkey");
+ error = GIT_ECERTIFICATE;
+ }
+
+ if (check_cb != NULL) {
+ git_cert_hostkey *cert_ptr = &cert;
+ git_error_state previous_error = {0};
+
+ git_error_state_capture(&previous_error, error);
+ error = check_cb((git_cert *) cert_ptr, cert_valid, host, check_cb_payload);
+ if (error == GIT_PASSTHROUGH) {
+ error = git_error_state_restore(&previous_error);
+ } else if (error < 0 && !git_error_last()) {
+ git_error_set(GIT_ERROR_NET, "unknown remote host key");
+ }
+
+ git_error_state_free(&previous_error);
+ }
+
+ return error;
+}
+
+#define SSH_DEFAULT_PORT "22"
+
+static int _git_ssh_setup_conn(
+ ssh_subtransport *t,
+ const char *url,
+ const char *cmd,
+ git_smart_subtransport_stream **stream)
+{
+ int auth_methods, error = 0, port;
+ ssh_stream *s;
+ git_credential *cred = NULL;
+ LIBSSH2_SESSION *session=NULL;
+ LIBSSH2_CHANNEL *channel=NULL;
+ LIBSSH2_KNOWNHOSTS *known_hosts = NULL;
+
+ t->current_stream = NULL;
+
+ *stream = NULL;
+ if (ssh_stream_alloc(t, cmd, stream) < 0)
+ return -1;
+
+ s = (ssh_stream *)*stream;
+ s->session = NULL;
+ s->channel = NULL;
+
+ if ((error = git_net_url_parse_standard_or_scp(&s->url, url)) < 0 ||
+ (error = git_socket_stream_new(&s->io, s->url.host, s->url.port)) < 0 ||
+ (error = git_stream_connect(s->io)) < 0)
+ goto done;
+
+ /*
+ * Try to parse the port as a number, if we can't then fall back to
+ * default. It would be nice if we could get the port that was resolved
+ * as part of the stream connection, but that's not something that's
+ * exposed.
+ */
+ if (git__strntol32(&port, s->url.port, strlen(s->url.port), NULL, 10) < 0) {
+ git_error_set(GIT_ERROR_NET, "invalid port to ssh: %s", s->url.port);
+ error = -1;
+ goto done;
+ }
+
+ if ((error = _git_ssh_session_create(&session, &known_hosts, s->url.host, port, s->io)) < 0)
+ goto done;
+
+ if ((error = check_certificate(session, known_hosts, t->owner->connect_opts.callbacks.certificate_check, t->owner->connect_opts.callbacks.payload, s->url.host, port)) < 0)
+ goto done;
+
+ /* we need the username to ask for auth methods */
+ if (!s->url.username) {
+ if ((error = request_creds(&cred, t, NULL, GIT_CREDENTIAL_USERNAME)) < 0)
+ goto done;
+
+ s->url.username = git__strdup(((git_credential_username *) cred)->username);
+ cred->free(cred);
+ cred = NULL;
+ if (!s->url.username)
+ goto done;
+ } else if (s->url.username && s->url.password) {
+ if ((error = git_credential_userpass_plaintext_new(&cred, s->url.username, s->url.password)) < 0)
+ goto done;
+ }
+
+ if ((error = list_auth_methods(&auth_methods, session, s->url.username)) < 0)
+ goto done;
+
+ error = GIT_EAUTH;
+ /* if we already have something to try */
+ if (cred && auth_methods & cred->credtype)
+ error = _git_ssh_authenticate_session(session, cred);
+
+ while (error == GIT_EAUTH) {
+ if (cred) {
+ cred->free(cred);
+ cred = NULL;
+ }
+
+ if ((error = request_creds(&cred, t, s->url.username, auth_methods)) < 0)
+ goto done;
+
+ if (strcmp(s->url.username, git_credential_get_username(cred))) {
+ git_error_set(GIT_ERROR_SSH, "username does not match previous request");
+ error = -1;
+ goto done;
+ }
+
+ error = _git_ssh_authenticate_session(session, cred);
+
+ if (error == GIT_EAUTH) {
+ /* refresh auth methods */
+ if ((error = list_auth_methods(&auth_methods, session, s->url.username)) < 0)
+ goto done;
+ else
+ error = GIT_EAUTH;
+ }
+ }
+
+ if (error < 0)
+ goto done;
+
+ channel = libssh2_channel_open_session(session);
+ if (!channel) {
+ error = -1;
+ ssh_error(session, "Failed to open SSH channel");
+ goto done;
+ }
+
+ libssh2_channel_set_blocking(channel, 1);
+
+ s->session = session;
+ s->channel = channel;
+
+ t->current_stream = s;
+
+done:
+ if (known_hosts)
+ libssh2_knownhost_free(known_hosts);
+
+ if (error < 0) {
+ ssh_stream_free(*stream);
+
+ if (session)
+ libssh2_session_free(session);
+ }
+
+ if (cred)
+ cred->free(cred);
+
+ return error;
+}
+
+static int ssh_uploadpack_ls(
+ ssh_subtransport *t,
+ const char *url,
+ git_smart_subtransport_stream **stream)
+{
+ const char *cmd = t->cmd_uploadpack ? t->cmd_uploadpack : cmd_uploadpack;
+
+ return _git_ssh_setup_conn(t, url, cmd, stream);
+}
+
+static int ssh_uploadpack(
+ ssh_subtransport *t,
+ const char *url,
+ git_smart_subtransport_stream **stream)
+{
+ GIT_UNUSED(url);
+
+ if (t->current_stream) {
+ *stream = &t->current_stream->parent;
+ return 0;
+ }
+
+ git_error_set(GIT_ERROR_NET, "must call UPLOADPACK_LS before UPLOADPACK");
+ return -1;
+}
+
+static int ssh_receivepack_ls(
+ ssh_subtransport *t,
+ const char *url,
+ git_smart_subtransport_stream **stream)
+{
+ const char *cmd = t->cmd_receivepack ? t->cmd_receivepack : cmd_receivepack;
+
+
+ return _git_ssh_setup_conn(t, url, cmd, stream);
+}
+
+static int ssh_receivepack(
+ ssh_subtransport *t,
+ const char *url,
+ git_smart_subtransport_stream **stream)
+{
+ GIT_UNUSED(url);
+
+ if (t->current_stream) {
+ *stream = &t->current_stream->parent;
+ return 0;
+ }
+
+ git_error_set(GIT_ERROR_NET, "must call RECEIVEPACK_LS before RECEIVEPACK");
+ return -1;
+}
+
+static int _ssh_action(
+ git_smart_subtransport_stream **stream,
+ git_smart_subtransport *subtransport,
+ const char *url,
+ git_smart_service_t action)
+{
+ ssh_subtransport *t = GIT_CONTAINER_OF(subtransport, ssh_subtransport, parent);
+
+ switch (action) {
+ case GIT_SERVICE_UPLOADPACK_LS:
+ return ssh_uploadpack_ls(t, url, stream);
+
+ case GIT_SERVICE_UPLOADPACK:
+ return ssh_uploadpack(t, url, stream);
+
+ case GIT_SERVICE_RECEIVEPACK_LS:
+ return ssh_receivepack_ls(t, url, stream);
+
+ case GIT_SERVICE_RECEIVEPACK:
+ return ssh_receivepack(t, url, stream);
+ }
+
+ *stream = NULL;
+ return -1;
+}
+
+static int _ssh_close(git_smart_subtransport *subtransport)
+{
+ ssh_subtransport *t = GIT_CONTAINER_OF(subtransport, ssh_subtransport, parent);
+
+ GIT_ASSERT(!t->current_stream);
+
+ GIT_UNUSED(t);
+
+ return 0;
+}
+
+static void _ssh_free(git_smart_subtransport *subtransport)
+{
+ ssh_subtransport *t = GIT_CONTAINER_OF(subtransport, ssh_subtransport, parent);
+
+ git__free(t->cmd_uploadpack);
+ git__free(t->cmd_receivepack);
+ git__free(t);
+}
+
+#define SSH_AUTH_PUBLICKEY "publickey"
+#define SSH_AUTH_PASSWORD "password"
+#define SSH_AUTH_KEYBOARD_INTERACTIVE "keyboard-interactive"
+
+static int list_auth_methods(int *out, LIBSSH2_SESSION *session, const char *username)
+{
+ const char *list, *ptr;
+
+ *out = 0;
+
+ list = libssh2_userauth_list(session, username, strlen(username));
+
+ /* either error, or the remote accepts NONE auth, which is bizarre, let's punt */
+ if (list == NULL && !libssh2_userauth_authenticated(session)) {
+ ssh_error(session, "remote rejected authentication");
+ return GIT_EAUTH;
+ }
+
+ ptr = list;
+ while (ptr) {
+ if (*ptr == ',')
+ ptr++;
+
+ if (!git__prefixcmp(ptr, SSH_AUTH_PUBLICKEY)) {
+ *out |= GIT_CREDENTIAL_SSH_KEY;
+ *out |= GIT_CREDENTIAL_SSH_CUSTOM;
+#ifdef GIT_SSH_MEMORY_CREDENTIALS
+ *out |= GIT_CREDENTIAL_SSH_MEMORY;
+#endif
+ ptr += strlen(SSH_AUTH_PUBLICKEY);
+ continue;
+ }
+
+ if (!git__prefixcmp(ptr, SSH_AUTH_PASSWORD)) {
+ *out |= GIT_CREDENTIAL_USERPASS_PLAINTEXT;
+ ptr += strlen(SSH_AUTH_PASSWORD);
+ continue;
+ }
+
+ if (!git__prefixcmp(ptr, SSH_AUTH_KEYBOARD_INTERACTIVE)) {
+ *out |= GIT_CREDENTIAL_SSH_INTERACTIVE;
+ ptr += strlen(SSH_AUTH_KEYBOARD_INTERACTIVE);
+ continue;
+ }
+
+ /* Skip it if we don't know it */
+ ptr = strchr(ptr, ',');
+ }
+
+ return 0;
+}
+#endif
+
+int git_smart_subtransport_ssh(
+ git_smart_subtransport **out, git_transport *owner, void *param)
+{
+#ifdef GIT_SSH
+ ssh_subtransport *t;
+
+ GIT_ASSERT_ARG(out);
+
+ GIT_UNUSED(param);
+
+ t = git__calloc(sizeof(ssh_subtransport), 1);
+ GIT_ERROR_CHECK_ALLOC(t);
+
+ t->owner = (transport_smart *)owner;
+ t->parent.action = _ssh_action;
+ t->parent.close = _ssh_close;
+ t->parent.free = _ssh_free;
+
+ *out = (git_smart_subtransport *) t;
+ return 0;
+#else
+ GIT_UNUSED(owner);
+ GIT_UNUSED(param);
+
+ GIT_ASSERT_ARG(out);
+ *out = NULL;
+
+ git_error_set(GIT_ERROR_INVALID, "cannot create SSH transport. Library was built without SSH support");
+ return -1;
+#endif
+}
+
+int git_transport_ssh_with_paths(git_transport **out, git_remote *owner, void *payload)
+{
+#ifdef GIT_SSH
+ git_strarray *paths = (git_strarray *) payload;
+ git_transport *transport;
+ transport_smart *smart;
+ ssh_subtransport *t;
+ int error;
+ git_smart_subtransport_definition ssh_definition = {
+ git_smart_subtransport_ssh,
+ 0, /* no RPC */
+ NULL,
+ };
+
+ if (paths->count != 2) {
+ git_error_set(GIT_ERROR_SSH, "invalid ssh paths, must be two strings");
+ return GIT_EINVALIDSPEC;
+ }
+
+ if ((error = git_transport_smart(&transport, owner, &ssh_definition)) < 0)
+ return error;
+
+ smart = (transport_smart *) transport;
+ t = (ssh_subtransport *) smart->wrapped;
+
+ t->cmd_uploadpack = git__strdup(paths->strings[0]);
+ GIT_ERROR_CHECK_ALLOC(t->cmd_uploadpack);
+ t->cmd_receivepack = git__strdup(paths->strings[1]);
+ GIT_ERROR_CHECK_ALLOC(t->cmd_receivepack);
+
+ *out = transport;
+ return 0;
+#else
+ GIT_UNUSED(owner);
+ GIT_UNUSED(payload);
+
+ GIT_ASSERT_ARG(out);
+ *out = NULL;
+
+ git_error_set(GIT_ERROR_INVALID, "cannot create SSH transport. Library was built without SSH support");
+ return -1;
+#endif
+}
+
+#ifdef GIT_SSH
+static void shutdown_ssh(void)
+{
+ libssh2_exit();
+}
+#endif
+
+int git_transport_ssh_global_init(void)
+{
+#ifdef GIT_SSH
+ if (libssh2_init(0) < 0) {
+ git_error_set(GIT_ERROR_SSH, "unable to initialize libssh2");
+ return -1;
+ }
+
+ return git_runtime_shutdown_register(shutdown_ssh);
+
+#else
+
+ /* Nothing to initialize */
+ return 0;
+
+#endif
+}
diff --git a/src/libgit2/transports/ssh.h b/src/libgit2/transports/ssh.h
new file mode 100644
index 0000000..d3e741f
--- /dev/null
+++ b/src/libgit2/transports/ssh.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_transports_ssh_h__
+#define INCLUDE_transports_ssh_h__
+
+#include "common.h"
+
+int git_transport_ssh_global_init(void);
+
+#endif
diff --git a/src/libgit2/transports/winhttp.c b/src/libgit2/transports/winhttp.c
new file mode 100644
index 0000000..ae572c5
--- /dev/null
+++ b/src/libgit2/transports/winhttp.c
@@ -0,0 +1,1690 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common.h"
+
+#ifdef GIT_WINHTTP
+
+#include "git2.h"
+#include "git2/transport.h"
+#include "posix.h"
+#include "str.h"
+#include "smart.h"
+#include "remote.h"
+#include "repository.h"
+#include "http.h"
+#include "git2/sys/credential.h"
+
+#include <wincrypt.h>
+#include <winhttp.h>
+
+/* For IInternetSecurityManager zone check */
+#include <objbase.h>
+#include <urlmon.h>
+
+#define WIDEN2(s) L ## s
+#define WIDEN(s) WIDEN2(s)
+
+#define MAX_CONTENT_TYPE_LEN 100
+#define WINHTTP_OPTION_PEERDIST_EXTENSION_STATE 109
+#define CACHED_POST_BODY_BUF_SIZE 4096
+#define UUID_LENGTH_CCH 32
+#define TIMEOUT_INFINITE -1
+#define DEFAULT_CONNECT_TIMEOUT 60000
+#ifndef WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH
+#define WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH 0
+#endif
+
+#ifndef WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1
+# define WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 0x00000200
+#endif
+
+#ifndef WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2
+# define WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 0x00000800
+#endif
+
+#ifndef WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_3
+# define WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_3 0x00002000
+#endif
+
+#ifndef WINHTTP_NO_CLIENT_CERT_CONTEXT
+# define WINHTTP_NO_CLIENT_CERT_CONTEXT NULL
+#endif
+
+#ifndef HTTP_STATUS_PERMANENT_REDIRECT
+# define HTTP_STATUS_PERMANENT_REDIRECT 308
+#endif
+
+#ifndef DWORD_MAX
+# define DWORD_MAX 0xffffffff
+#endif
+
+bool git_http__expect_continue = false;
+
+static const char *prefix_https = "https://";
+static const char *upload_pack_service = "upload-pack";
+static const char *upload_pack_ls_service_url = "/info/refs?service=git-upload-pack";
+static const char *upload_pack_service_url = "/git-upload-pack";
+static const char *receive_pack_service = "receive-pack";
+static const char *receive_pack_ls_service_url = "/info/refs?service=git-receive-pack";
+static const char *receive_pack_service_url = "/git-receive-pack";
+static const wchar_t *get_verb = L"GET";
+static const wchar_t *post_verb = L"POST";
+static const wchar_t *pragma_nocache = L"Pragma: no-cache";
+static const wchar_t *transfer_encoding = L"Transfer-Encoding: chunked";
+static const int no_check_cert_flags = SECURITY_FLAG_IGNORE_CERT_CN_INVALID |
+ SECURITY_FLAG_IGNORE_CERT_DATE_INVALID |
+ SECURITY_FLAG_IGNORE_UNKNOWN_CA;
+
+#if defined(__MINGW32__)
+static const CLSID CLSID_InternetSecurityManager_mingw =
+ { 0x7B8A2D94, 0x0AC9, 0x11D1,
+ { 0x89, 0x6C, 0x00, 0xC0, 0x4F, 0xB6, 0xBF, 0xC4 } };
+static const IID IID_IInternetSecurityManager_mingw =
+ { 0x79EAC9EE, 0xBAF9, 0x11CE,
+ { 0x8C, 0x82, 0x00, 0xAA, 0x00, 0x4B, 0xA9, 0x0B } };
+
+# define CLSID_InternetSecurityManager CLSID_InternetSecurityManager_mingw
+# define IID_IInternetSecurityManager IID_IInternetSecurityManager_mingw
+#endif
+
+#define OWNING_SUBTRANSPORT(s) ((winhttp_subtransport *)(s)->parent.subtransport)
+
+typedef enum {
+ GIT_WINHTTP_AUTH_BASIC = 1,
+ GIT_WINHTTP_AUTH_NTLM = 2,
+ GIT_WINHTTP_AUTH_NEGOTIATE = 4,
+ GIT_WINHTTP_AUTH_DIGEST = 8
+} winhttp_authmechanism_t;
+
+typedef struct {
+ git_smart_subtransport_stream parent;
+ const char *service;
+ const char *service_url;
+ const wchar_t *verb;
+ HINTERNET request;
+ wchar_t *request_uri;
+ char *chunk_buffer;
+ unsigned chunk_buffer_len;
+ HANDLE post_body;
+ DWORD post_body_len;
+ unsigned sent_request : 1,
+ received_response : 1,
+ chunked : 1,
+ status_sending_request_reached: 1;
+} winhttp_stream;
+
+typedef struct {
+ git_net_url url;
+ git_credential *cred;
+ int auth_mechanisms;
+ bool url_cred_presented;
+} winhttp_server;
+
+typedef struct {
+ git_smart_subtransport parent;
+ transport_smart *owner;
+
+ winhttp_server server;
+ winhttp_server proxy;
+
+ HINTERNET session;
+ HINTERNET connection;
+} winhttp_subtransport;
+
+static int apply_userpass_credentials(HINTERNET request, DWORD target, int mechanisms, git_credential *cred)
+{
+ git_credential_userpass_plaintext *c = (git_credential_userpass_plaintext *)cred;
+ wchar_t *user = NULL, *pass = NULL;
+ int user_len = 0, pass_len = 0, error = 0;
+ DWORD native_scheme;
+
+ if (mechanisms & GIT_WINHTTP_AUTH_NEGOTIATE) {
+ native_scheme = WINHTTP_AUTH_SCHEME_NEGOTIATE;
+ } else if (mechanisms & GIT_WINHTTP_AUTH_NTLM) {
+ native_scheme = WINHTTP_AUTH_SCHEME_NTLM;
+ } else if (mechanisms & GIT_WINHTTP_AUTH_DIGEST) {
+ native_scheme = WINHTTP_AUTH_SCHEME_DIGEST;
+ } else if (mechanisms & GIT_WINHTTP_AUTH_BASIC) {
+ native_scheme = WINHTTP_AUTH_SCHEME_BASIC;
+ } else {
+ git_error_set(GIT_ERROR_HTTP, "invalid authentication scheme");
+ error = GIT_EAUTH;
+ goto done;
+ }
+
+ if ((error = user_len = git_utf8_to_16_alloc(&user, c->username)) < 0)
+ goto done;
+
+ if ((error = pass_len = git_utf8_to_16_alloc(&pass, c->password)) < 0)
+ goto done;
+
+ if (!WinHttpSetCredentials(request, target, native_scheme, user, pass, NULL)) {
+ git_error_set(GIT_ERROR_OS, "failed to set credentials");
+ error = -1;
+ }
+
+done:
+ if (user_len > 0)
+ git__memzero(user, user_len * sizeof(wchar_t));
+
+ if (pass_len > 0)
+ git__memzero(pass, pass_len * sizeof(wchar_t));
+
+ git__free(user);
+ git__free(pass);
+
+ return error;
+}
+
+static int apply_default_credentials(HINTERNET request, DWORD target, int mechanisms)
+{
+ DWORD autologon_level = WINHTTP_AUTOLOGON_SECURITY_LEVEL_LOW;
+ DWORD native_scheme = 0;
+
+ if ((mechanisms & GIT_WINHTTP_AUTH_NEGOTIATE) != 0) {
+ native_scheme = WINHTTP_AUTH_SCHEME_NEGOTIATE;
+ } else if ((mechanisms & GIT_WINHTTP_AUTH_NTLM) != 0) {
+ native_scheme = WINHTTP_AUTH_SCHEME_NTLM;
+ } else {
+ git_error_set(GIT_ERROR_HTTP, "invalid authentication scheme");
+ return GIT_EAUTH;
+ }
+
+ /*
+ * Autologon policy must be "low" to use default creds.
+ * This is safe as the user has explicitly requested it.
+ */
+ if (!WinHttpSetOption(request, WINHTTP_OPTION_AUTOLOGON_POLICY, &autologon_level, sizeof(DWORD))) {
+ git_error_set(GIT_ERROR_OS, "could not configure logon policy");
+ return -1;
+ }
+
+ if (!WinHttpSetCredentials(request, target, native_scheme, NULL, NULL, NULL)) {
+ git_error_set(GIT_ERROR_OS, "could not configure credentials");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int acquire_url_cred(
+ git_credential **cred,
+ unsigned int allowed_types,
+ const char *username,
+ const char *password)
+{
+ if (allowed_types & GIT_CREDENTIAL_USERPASS_PLAINTEXT)
+ return git_credential_userpass_plaintext_new(cred, username, password);
+
+ if ((allowed_types & GIT_CREDENTIAL_DEFAULT) && *username == '\0' && *password == '\0')
+ return git_credential_default_new(cred);
+
+ return 1;
+}
+
+static int acquire_fallback_cred(
+ git_credential **cred,
+ const char *url,
+ unsigned int allowed_types)
+{
+ int error = 1;
+
+ /* If the target URI supports integrated Windows authentication
+ * as an authentication mechanism */
+ if (GIT_CREDENTIAL_DEFAULT & allowed_types) {
+ wchar_t *wide_url;
+ HRESULT hCoInitResult;
+
+ /* Convert URL to wide characters */
+ if (git_utf8_to_16_alloc(&wide_url, url) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to convert string to wide form");
+ return -1;
+ }
+
+ hCoInitResult = CoInitializeEx(NULL, COINIT_MULTITHREADED);
+
+ if (SUCCEEDED(hCoInitResult) || hCoInitResult == RPC_E_CHANGED_MODE) {
+ IInternetSecurityManager *pISM;
+
+ /* And if the target URI is in the My Computer, Intranet, or Trusted zones */
+ if (SUCCEEDED(CoCreateInstance(&CLSID_InternetSecurityManager, NULL,
+ CLSCTX_ALL, &IID_IInternetSecurityManager, (void **)&pISM))) {
+ DWORD dwZone;
+
+ if (SUCCEEDED(pISM->lpVtbl->MapUrlToZone(pISM, wide_url, &dwZone, 0)) &&
+ (URLZONE_LOCAL_MACHINE == dwZone ||
+ URLZONE_INTRANET == dwZone ||
+ URLZONE_TRUSTED == dwZone)) {
+ git_credential *existing = *cred;
+
+ if (existing)
+ existing->free(existing);
+
+ /* Then use default Windows credentials to authenticate this request */
+ error = git_credential_default_new(cred);
+ }
+
+ pISM->lpVtbl->Release(pISM);
+ }
+
+ /* Only uninitialize if the call to CoInitializeEx was successful. */
+ if (SUCCEEDED(hCoInitResult))
+ CoUninitialize();
+ }
+
+ git__free(wide_url);
+ }
+
+ return error;
+}
+
+static int certificate_check(winhttp_stream *s, int valid)
+{
+ int error;
+ winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
+ PCERT_CONTEXT cert_ctx;
+ DWORD cert_ctx_size = sizeof(cert_ctx);
+ git_cert_x509 cert;
+
+ /* If there is no override, we should fail if WinHTTP doesn't think it's fine */
+ if (t->owner->connect_opts.callbacks.certificate_check == NULL && !valid) {
+ if (!git_error_last())
+ git_error_set(GIT_ERROR_HTTP, "unknown certificate check failure");
+
+ return GIT_ECERTIFICATE;
+ }
+
+ if (t->owner->connect_opts.callbacks.certificate_check == NULL || git__strcmp(t->server.url.scheme, "https") != 0)
+ return 0;
+
+ if (!WinHttpQueryOption(s->request, WINHTTP_OPTION_SERVER_CERT_CONTEXT, &cert_ctx, &cert_ctx_size)) {
+ git_error_set(GIT_ERROR_OS, "failed to get server certificate");
+ return -1;
+ }
+
+ git_error_clear();
+ cert.parent.cert_type = GIT_CERT_X509;
+ cert.data = cert_ctx->pbCertEncoded;
+ cert.len = cert_ctx->cbCertEncoded;
+ error = t->owner->connect_opts.callbacks.certificate_check((git_cert *) &cert, valid, t->server.url.host, t->owner->connect_opts.callbacks.payload);
+ CertFreeCertificateContext(cert_ctx);
+
+ if (error == GIT_PASSTHROUGH)
+ error = valid ? 0 : GIT_ECERTIFICATE;
+
+ if (error < 0 && !git_error_last())
+ git_error_set(GIT_ERROR_HTTP, "user cancelled certificate check");
+
+ return error;
+}
+
+static void winhttp_stream_close(winhttp_stream *s)
+{
+ if (s->chunk_buffer) {
+ git__free(s->chunk_buffer);
+ s->chunk_buffer = NULL;
+ }
+
+ if (s->post_body) {
+ CloseHandle(s->post_body);
+ s->post_body = NULL;
+ }
+
+ if (s->request_uri) {
+ git__free(s->request_uri);
+ s->request_uri = NULL;
+ }
+
+ if (s->request) {
+ WinHttpCloseHandle(s->request);
+ s->request = NULL;
+ }
+
+ s->sent_request = 0;
+}
+
+static int apply_credentials(
+ HINTERNET request,
+ git_net_url *url,
+ int target,
+ git_credential *creds,
+ int mechanisms)
+{
+ int error = 0;
+
+ GIT_UNUSED(url);
+
+ /* If we have creds, just apply them */
+ if (creds && creds->credtype == GIT_CREDENTIAL_USERPASS_PLAINTEXT)
+ error = apply_userpass_credentials(request, target, mechanisms, creds);
+ else if (creds && creds->credtype == GIT_CREDENTIAL_DEFAULT)
+ error = apply_default_credentials(request, target, mechanisms);
+
+ return error;
+}
+
+static int winhttp_stream_connect(winhttp_stream *s)
+{
+ winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
+ git_str buf = GIT_STR_INIT;
+ char *proxy_url = NULL;
+ wchar_t ct[MAX_CONTENT_TYPE_LEN];
+ LPCWSTR types[] = { L"*/*", NULL };
+ BOOL peerdist = FALSE;
+ int error = -1;
+ unsigned long disable_redirects = WINHTTP_DISABLE_REDIRECTS;
+ int default_timeout = TIMEOUT_INFINITE;
+ int default_connect_timeout = DEFAULT_CONNECT_TIMEOUT;
+ DWORD autologon_policy = WINHTTP_AUTOLOGON_SECURITY_LEVEL_HIGH;
+
+ const char *service_url = s->service_url;
+ size_t i;
+ const git_proxy_options *proxy_opts;
+
+ /* If path already ends in /, remove the leading slash from service_url */
+ if ((git__suffixcmp(t->server.url.path, "/") == 0) && (git__prefixcmp(service_url, "/") == 0))
+ service_url++;
+ /* Prepare URL */
+ git_str_printf(&buf, "%s%s", t->server.url.path, service_url);
+
+ if (git_str_oom(&buf))
+ return -1;
+
+ /* Convert URL to wide characters */
+ if (git_utf8_to_16_alloc(&s->request_uri, git_str_cstr(&buf)) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to convert string to wide form");
+ goto on_error;
+ }
+
+ /* Establish request */
+ s->request = WinHttpOpenRequest(
+ t->connection,
+ s->verb,
+ s->request_uri,
+ NULL,
+ WINHTTP_NO_REFERER,
+ types,
+ git__strcmp(t->server.url.scheme, "https") == 0 ? WINHTTP_FLAG_SECURE : 0);
+
+ if (!s->request) {
+ git_error_set(GIT_ERROR_OS, "failed to open request");
+ goto on_error;
+ }
+
+ /* Never attempt default credentials; we'll provide them explicitly. */
+ if (!WinHttpSetOption(s->request, WINHTTP_OPTION_AUTOLOGON_POLICY, &autologon_policy, sizeof(DWORD)))
+ return -1;
+
+ if (!WinHttpSetTimeouts(s->request, default_timeout, default_connect_timeout, default_timeout, default_timeout)) {
+ git_error_set(GIT_ERROR_OS, "failed to set timeouts for WinHTTP");
+ goto on_error;
+ }
+
+ proxy_opts = &t->owner->connect_opts.proxy_opts;
+ if (proxy_opts->type == GIT_PROXY_AUTO) {
+ /* Set proxy if necessary */
+ if (git_remote__http_proxy(&proxy_url, t->owner->owner, &t->server.url) < 0)
+ goto on_error;
+ }
+ else if (proxy_opts->type == GIT_PROXY_SPECIFIED) {
+ proxy_url = git__strdup(proxy_opts->url);
+ GIT_ERROR_CHECK_ALLOC(proxy_url);
+ }
+
+ if (proxy_url) {
+ git_str processed_url = GIT_STR_INIT;
+ WINHTTP_PROXY_INFO proxy_info;
+ wchar_t *proxy_wide;
+
+ git_net_url_dispose(&t->proxy.url);
+
+ if ((error = git_net_url_parse_http(&t->proxy.url, proxy_url)) < 0)
+ goto on_error;
+
+ if (!git_net_url_valid(&t->proxy.url)) {
+ git_error_set(GIT_ERROR_HTTP, "invalid URL: '%s'", proxy_url);
+ error = -1;
+ goto on_error;
+ }
+
+ git_str_puts(&processed_url, t->proxy.url.scheme);
+ git_str_PUTS(&processed_url, "://");
+
+ if (git_net_url_is_ipv6(&t->proxy.url))
+ git_str_putc(&processed_url, '[');
+
+ git_str_puts(&processed_url, t->proxy.url.host);
+
+ if (git_net_url_is_ipv6(&t->proxy.url))
+ git_str_putc(&processed_url, ']');
+
+ if (!git_net_url_is_default_port(&t->proxy.url))
+ git_str_printf(&processed_url, ":%s", t->proxy.url.port);
+
+ if (git_str_oom(&processed_url)) {
+ error = -1;
+ goto on_error;
+ }
+
+ /* Convert URL to wide characters */
+ error = git_utf8_to_16_alloc(&proxy_wide, processed_url.ptr);
+ git_str_dispose(&processed_url);
+ if (error < 0)
+ goto on_error;
+
+ proxy_info.dwAccessType = WINHTTP_ACCESS_TYPE_NAMED_PROXY;
+ proxy_info.lpszProxy = proxy_wide;
+ proxy_info.lpszProxyBypass = NULL;
+
+ if (!WinHttpSetOption(s->request,
+ WINHTTP_OPTION_PROXY,
+ &proxy_info,
+ sizeof(WINHTTP_PROXY_INFO))) {
+ git_error_set(GIT_ERROR_OS, "failed to set proxy");
+ git__free(proxy_wide);
+ goto on_error;
+ }
+
+ git__free(proxy_wide);
+
+ if ((error = apply_credentials(s->request, &t->proxy.url, WINHTTP_AUTH_TARGET_PROXY, t->proxy.cred, t->proxy.auth_mechanisms)) < 0)
+ goto on_error;
+ }
+
+ /* Disable WinHTTP redirects so we can handle them manually. Why, you ask?
+ * http://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/b2ff8879-ab9f-4218-8f09-16d25dff87ae
+ */
+ if (!WinHttpSetOption(s->request,
+ WINHTTP_OPTION_DISABLE_FEATURE,
+ &disable_redirects,
+ sizeof(disable_redirects))) {
+ git_error_set(GIT_ERROR_OS, "failed to disable redirects");
+ error = -1;
+ goto on_error;
+ }
+
+ /* Strip unwanted headers (X-P2P-PeerDist, X-P2P-PeerDistEx) that WinHTTP
+ * adds itself. This option may not be supported by the underlying
+ * platform, so we do not error-check it */
+ WinHttpSetOption(s->request,
+ WINHTTP_OPTION_PEERDIST_EXTENSION_STATE,
+ &peerdist,
+ sizeof(peerdist));
+
+ /* Send Pragma: no-cache header */
+ if (!WinHttpAddRequestHeaders(s->request, pragma_nocache, (ULONG) -1L, WINHTTP_ADDREQ_FLAG_ADD)) {
+ git_error_set(GIT_ERROR_OS, "failed to add a header to the request");
+ goto on_error;
+ }
+
+ if (post_verb == s->verb) {
+ /* Send Content-Type and Accept headers -- only necessary on a POST */
+ git_str_clear(&buf);
+ if (git_str_printf(&buf,
+ "Content-Type: application/x-git-%s-request",
+ s->service) < 0)
+ goto on_error;
+
+ if (git_utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_str_cstr(&buf)) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to convert content-type to wide characters");
+ goto on_error;
+ }
+
+ if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG)-1L,
+ WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) {
+ git_error_set(GIT_ERROR_OS, "failed to add a header to the request");
+ goto on_error;
+ }
+
+ git_str_clear(&buf);
+ if (git_str_printf(&buf,
+ "Accept: application/x-git-%s-result",
+ s->service) < 0)
+ goto on_error;
+
+ if (git_utf8_to_16(ct, MAX_CONTENT_TYPE_LEN, git_str_cstr(&buf)) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to convert accept header to wide characters");
+ goto on_error;
+ }
+
+ if (!WinHttpAddRequestHeaders(s->request, ct, (ULONG)-1L,
+ WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) {
+ git_error_set(GIT_ERROR_OS, "failed to add a header to the request");
+ goto on_error;
+ }
+ }
+
+ for (i = 0; i < t->owner->connect_opts.custom_headers.count; i++) {
+ if (t->owner->connect_opts.custom_headers.strings[i]) {
+ wchar_t *custom_header_wide = NULL;
+
+ git_str_clear(&buf);
+ git_str_puts(&buf, t->owner->connect_opts.custom_headers.strings[i]);
+
+ /* Convert header to wide characters */
+ if ((error = git_utf8_to_16_alloc(&custom_header_wide, git_str_cstr(&buf))) < 0)
+ goto on_error;
+
+ if (!WinHttpAddRequestHeaders(s->request, custom_header_wide, (ULONG)-1L,
+ WINHTTP_ADDREQ_FLAG_ADD | WINHTTP_ADDREQ_FLAG_REPLACE)) {
+ git_error_set(GIT_ERROR_OS, "failed to add a header to the request");
+ git__free(custom_header_wide);
+ goto on_error;
+ }
+
+ git__free(custom_header_wide);
+ }
+ }
+
+ if ((error = apply_credentials(s->request, &t->server.url, WINHTTP_AUTH_TARGET_SERVER, t->server.cred, t->server.auth_mechanisms)) < 0)
+ goto on_error;
+
+ /* We've done everything up to calling WinHttpSendRequest. */
+
+ error = 0;
+
+on_error:
+ if (error < 0)
+ winhttp_stream_close(s);
+
+ git__free(proxy_url);
+ git_str_dispose(&buf);
+ return error;
+}
+
+static int parse_unauthorized_response(
+ int *allowed_types,
+ int *allowed_mechanisms,
+ HINTERNET request)
+{
+ DWORD supported, first, target;
+
+ *allowed_types = 0;
+ *allowed_mechanisms = 0;
+
+ /* WinHttpQueryHeaders() must be called before WinHttpQueryAuthSchemes().
+ * We can assume this was already done, since we know we are unauthorized.
+ */
+ if (!WinHttpQueryAuthSchemes(request, &supported, &first, &target)) {
+ git_error_set(GIT_ERROR_OS, "failed to parse supported auth schemes");
+ return GIT_EAUTH;
+ }
+
+ if (WINHTTP_AUTH_SCHEME_NTLM & supported) {
+ *allowed_types |= GIT_CREDENTIAL_USERPASS_PLAINTEXT;
+ *allowed_types |= GIT_CREDENTIAL_DEFAULT;
+ *allowed_mechanisms |= GIT_WINHTTP_AUTH_NTLM;
+ }
+
+ if (WINHTTP_AUTH_SCHEME_NEGOTIATE & supported) {
+ *allowed_types |= GIT_CREDENTIAL_DEFAULT;
+ *allowed_mechanisms |= GIT_WINHTTP_AUTH_NEGOTIATE;
+ }
+
+ if (WINHTTP_AUTH_SCHEME_BASIC & supported) {
+ *allowed_types |= GIT_CREDENTIAL_USERPASS_PLAINTEXT;
+ *allowed_mechanisms |= GIT_WINHTTP_AUTH_BASIC;
+ }
+
+ if (WINHTTP_AUTH_SCHEME_DIGEST & supported) {
+ *allowed_types |= GIT_CREDENTIAL_USERPASS_PLAINTEXT;
+ *allowed_mechanisms |= GIT_WINHTTP_AUTH_DIGEST;
+ }
+
+ return 0;
+}
+
+static int write_chunk(HINTERNET request, const char *buffer, size_t len)
+{
+ DWORD bytes_written;
+ git_str buf = GIT_STR_INIT;
+
+ /* Chunk header */
+ git_str_printf(&buf, "%"PRIXZ"\r\n", len);
+
+ if (git_str_oom(&buf))
+ return -1;
+
+ if (!WinHttpWriteData(request,
+ git_str_cstr(&buf), (DWORD)git_str_len(&buf),
+ &bytes_written)) {
+ git_str_dispose(&buf);
+ git_error_set(GIT_ERROR_OS, "failed to write chunk header");
+ return -1;
+ }
+
+ git_str_dispose(&buf);
+
+ /* Chunk body */
+ if (!WinHttpWriteData(request,
+ buffer, (DWORD)len,
+ &bytes_written)) {
+ git_error_set(GIT_ERROR_OS, "failed to write chunk");
+ return -1;
+ }
+
+ /* Chunk footer */
+ if (!WinHttpWriteData(request,
+ "\r\n", 2,
+ &bytes_written)) {
+ git_error_set(GIT_ERROR_OS, "failed to write chunk footer");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int winhttp_close_connection(winhttp_subtransport *t)
+{
+ int ret = 0;
+
+ if (t->connection) {
+ if (!WinHttpCloseHandle(t->connection)) {
+ git_error_set(GIT_ERROR_OS, "unable to close connection");
+ ret = -1;
+ }
+
+ t->connection = NULL;
+ }
+
+ if (t->session) {
+ if (!WinHttpCloseHandle(t->session)) {
+ git_error_set(GIT_ERROR_OS, "unable to close session");
+ ret = -1;
+ }
+
+ t->session = NULL;
+ }
+
+ return ret;
+}
+
+static void CALLBACK winhttp_status(
+ HINTERNET connection,
+ DWORD_PTR ctx,
+ DWORD code,
+ LPVOID info,
+ DWORD info_len)
+{
+ DWORD status;
+
+ GIT_UNUSED(connection);
+ GIT_UNUSED(info_len);
+
+ switch (code) {
+ case WINHTTP_CALLBACK_STATUS_SECURE_FAILURE:
+ status = *((DWORD *)info);
+
+ if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_CN_INVALID))
+ git_error_set(GIT_ERROR_HTTP, "SSL certificate issued for different common name");
+ else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_DATE_INVALID))
+ git_error_set(GIT_ERROR_HTTP, "SSL certificate has expired");
+ else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CA))
+ git_error_set(GIT_ERROR_HTTP, "SSL certificate signed by unknown CA");
+ else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_INVALID_CERT))
+ git_error_set(GIT_ERROR_HTTP, "SSL certificate is invalid");
+ else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REV_FAILED))
+ git_error_set(GIT_ERROR_HTTP, "certificate revocation check failed");
+ else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_CERT_REVOKED))
+ git_error_set(GIT_ERROR_HTTP, "SSL certificate was revoked");
+ else if ((status & WINHTTP_CALLBACK_STATUS_FLAG_SECURITY_CHANNEL_ERROR))
+ git_error_set(GIT_ERROR_HTTP, "security libraries could not be loaded");
+ else
+ git_error_set(GIT_ERROR_HTTP, "unknown security error %lu", status);
+
+ break;
+
+ case WINHTTP_CALLBACK_STATUS_SENDING_REQUEST:
+ ((winhttp_stream *) ctx)->status_sending_request_reached = 1;
+
+ break;
+ }
+}
+
+static int winhttp_connect(
+ winhttp_subtransport *t)
+{
+ wchar_t *wide_host = NULL;
+ int32_t port;
+ wchar_t *wide_ua = NULL;
+ git_str ipv6 = GIT_STR_INIT, ua = GIT_STR_INIT;
+ const char *host;
+ int error = -1;
+ int default_timeout = TIMEOUT_INFINITE;
+ int default_connect_timeout = DEFAULT_CONNECT_TIMEOUT;
+ DWORD protocols =
+ WINHTTP_FLAG_SECURE_PROTOCOL_TLS1 |
+ WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_1 |
+ WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2 |
+ WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_3;
+
+ t->session = NULL;
+ t->connection = NULL;
+
+ /* Prepare port */
+ if (git__strntol32(&port, t->server.url.port,
+ strlen(t->server.url.port), NULL, 10) < 0)
+ goto on_error;
+
+ /* IPv6? Add braces around the host. */
+ if (git_net_url_is_ipv6(&t->server.url)) {
+ if (git_str_printf(&ipv6, "[%s]", t->server.url.host) < 0)
+ goto on_error;
+
+ host = ipv6.ptr;
+ } else {
+ host = t->server.url.host;
+ }
+
+ /* Prepare host */
+ if (git_utf8_to_16_alloc(&wide_host, host) < 0) {
+ git_error_set(GIT_ERROR_OS, "unable to convert host to wide characters");
+ goto on_error;
+ }
+
+
+ if (git_http__user_agent(&ua) < 0)
+ goto on_error;
+
+ if (git_utf8_to_16_alloc(&wide_ua, git_str_cstr(&ua)) < 0) {
+ git_error_set(GIT_ERROR_OS, "unable to convert host to wide characters");
+ goto on_error;
+ }
+
+ /* Establish session */
+ t->session = WinHttpOpen(
+ wide_ua,
+ WINHTTP_ACCESS_TYPE_DEFAULT_PROXY,
+ WINHTTP_NO_PROXY_NAME,
+ WINHTTP_NO_PROXY_BYPASS,
+ 0);
+
+ if (!t->session) {
+ git_error_set(GIT_ERROR_OS, "failed to init WinHTTP");
+ goto on_error;
+ }
+
+ /*
+ * Do a best-effort attempt to enable TLS 1.3 and 1.2 but allow this to
+ * fail; if TLS 1.2 or 1.3 support is not available for some reason,
+ * ignore the failure (it will keep the default protocols).
+ */
+ if (WinHttpSetOption(t->session,
+ WINHTTP_OPTION_SECURE_PROTOCOLS,
+ &protocols,
+ sizeof(protocols)) == FALSE) {
+ protocols &= ~WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_3;
+ WinHttpSetOption(t->session,
+ WINHTTP_OPTION_SECURE_PROTOCOLS,
+ &protocols,
+ sizeof(protocols));
+ }
+
+ if (!WinHttpSetTimeouts(t->session, default_timeout, default_connect_timeout, default_timeout, default_timeout)) {
+ git_error_set(GIT_ERROR_OS, "failed to set timeouts for WinHTTP");
+ goto on_error;
+ }
+
+
+ /* Establish connection */
+ t->connection = WinHttpConnect(
+ t->session,
+ wide_host,
+ (INTERNET_PORT) port,
+ 0);
+
+ if (!t->connection) {
+ git_error_set(GIT_ERROR_OS, "failed to connect to host");
+ goto on_error;
+ }
+
+ if (WinHttpSetStatusCallback(
+ t->connection,
+ winhttp_status,
+ WINHTTP_CALLBACK_FLAG_SECURE_FAILURE | WINHTTP_CALLBACK_FLAG_SEND_REQUEST,
+ 0
+ ) == WINHTTP_INVALID_STATUS_CALLBACK) {
+ git_error_set(GIT_ERROR_OS, "failed to set status callback");
+ goto on_error;
+ }
+
+ error = 0;
+
+on_error:
+ if (error < 0)
+ winhttp_close_connection(t);
+
+ git_str_dispose(&ua);
+ git_str_dispose(&ipv6);
+ git__free(wide_host);
+ git__free(wide_ua);
+
+ return error;
+}
+
+static int do_send_request(winhttp_stream *s, size_t len, bool chunked)
+{
+ int attempts;
+ bool success;
+
+ if (len > DWORD_MAX) {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ return -1;
+ }
+
+ for (attempts = 0; attempts < 5; attempts++) {
+ if (chunked) {
+ success = WinHttpSendRequest(s->request,
+ WINHTTP_NO_ADDITIONAL_HEADERS, 0,
+ WINHTTP_NO_REQUEST_DATA, 0,
+ WINHTTP_IGNORE_REQUEST_TOTAL_LENGTH, (DWORD_PTR)s);
+ } else {
+ success = WinHttpSendRequest(s->request,
+ WINHTTP_NO_ADDITIONAL_HEADERS, 0,
+ WINHTTP_NO_REQUEST_DATA, 0,
+ (DWORD)len, (DWORD_PTR)s);
+ }
+
+ if (success || GetLastError() != (DWORD)SEC_E_BUFFER_TOO_SMALL)
+ break;
+ }
+
+ return success ? 0 : -1;
+}
+
+static int send_request(winhttp_stream *s, size_t len, bool chunked)
+{
+ int request_failed = 1, error, attempts = 0;
+ DWORD ignore_flags, send_request_error;
+
+ git_error_clear();
+
+ while (request_failed && attempts++ < 3) {
+ int cert_valid = 1;
+ int client_cert_requested = 0;
+ request_failed = 0;
+ if ((error = do_send_request(s, len, chunked)) < 0) {
+ send_request_error = GetLastError();
+ request_failed = 1;
+ switch (send_request_error) {
+ case ERROR_WINHTTP_SECURE_FAILURE:
+ cert_valid = 0;
+ break;
+ case ERROR_WINHTTP_CLIENT_AUTH_CERT_NEEDED:
+ client_cert_requested = 1;
+ break;
+ default:
+ git_error_set(GIT_ERROR_OS, "failed to send request");
+ return -1;
+ }
+ }
+
+ /*
+ * Only check the certificate if we were able to reach the sending request phase, or
+ * received a secure failure error. Otherwise, the server certificate won't be available
+ * since the request wasn't able to complete (e.g. proxy auth required)
+ */
+ if (!cert_valid ||
+ (!request_failed && s->status_sending_request_reached)) {
+ git_error_clear();
+ if ((error = certificate_check(s, cert_valid)) < 0) {
+ if (!git_error_last())
+ git_error_set(GIT_ERROR_OS, "user cancelled certificate check");
+
+ return error;
+ }
+ }
+
+ /* if neither the request nor the certificate check returned errors, we're done */
+ if (!request_failed)
+ return 0;
+
+ if (!cert_valid) {
+ ignore_flags = no_check_cert_flags;
+ if (!WinHttpSetOption(s->request, WINHTTP_OPTION_SECURITY_FLAGS, &ignore_flags, sizeof(ignore_flags))) {
+ git_error_set(GIT_ERROR_OS, "failed to set security options");
+ return -1;
+ }
+ }
+
+ if (client_cert_requested) {
+ /*
+ * Client certificates are not supported, explicitly tell the server that
+ * (it's possible a client certificate was requested but is not required)
+ */
+ if (!WinHttpSetOption(s->request, WINHTTP_OPTION_CLIENT_CERT_CONTEXT, WINHTTP_NO_CLIENT_CERT_CONTEXT, 0)) {
+ git_error_set(GIT_ERROR_OS, "failed to set client cert context");
+ return -1;
+ }
+ }
+ }
+
+ return error;
+}
+
+static int acquire_credentials(
+ HINTERNET request,
+ winhttp_server *server,
+ const char *url_str,
+ git_credential_acquire_cb cred_cb,
+ void *cred_cb_payload)
+{
+ int allowed_types;
+ int error = 1;
+
+ if (parse_unauthorized_response(&allowed_types, &server->auth_mechanisms, request) < 0)
+ return -1;
+
+ if (allowed_types) {
+ git_credential_free(server->cred);
+ server->cred = NULL;
+
+ /* Start with URL-specified credentials, if there were any. */
+ if (!server->url_cred_presented && server->url.username && server->url.password) {
+ error = acquire_url_cred(&server->cred, allowed_types, server->url.username, server->url.password);
+ server->url_cred_presented = 1;
+
+ if (error < 0)
+ return error;
+ }
+
+ /* Next use the user-defined callback, if there is one. */
+ if (error > 0 && cred_cb) {
+ error = cred_cb(&server->cred, url_str, server->url.username, allowed_types, cred_cb_payload);
+
+ /* Treat GIT_PASSTHROUGH as though git_credential_acquire_cb isn't set */
+ if (error == GIT_PASSTHROUGH)
+ error = 1;
+ else if (error < 0)
+ return error;
+ }
+
+ /* Finally, invoke the fallback default credential lookup. */
+ if (error > 0) {
+ error = acquire_fallback_cred(&server->cred, url_str, allowed_types);
+
+ if (error < 0)
+ return error;
+ }
+ }
+
+ /*
+ * No error occurred but we could not find appropriate credentials.
+ * This behaves like a pass-through.
+ */
+ return error;
+}
+
+static int winhttp_stream_read(
+ git_smart_subtransport_stream *stream,
+ char *buffer,
+ size_t buf_size,
+ size_t *bytes_read)
+{
+ winhttp_stream *s = (winhttp_stream *)stream;
+ winhttp_subtransport *t = OWNING_SUBTRANSPORT(s);
+ DWORD dw_bytes_read;
+ char replay_count = 0;
+ int error;
+
+replay:
+ /* Enforce a reasonable cap on the number of replays */
+ if (replay_count++ >= GIT_HTTP_REPLAY_MAX) {
+ git_error_set(GIT_ERROR_HTTP, "too many redirects or authentication replays");
+ return GIT_ERROR; /* not GIT_EAUTH because the exact cause is not clear */
+ }
+
+ /* Connect if necessary */
+ if (!s->request && winhttp_stream_connect(s) < 0)
+ return -1;
+
+ if (!s->received_response) {
+ DWORD status_code, status_code_length, content_type_length, bytes_written;
+ char expected_content_type_8[MAX_CONTENT_TYPE_LEN];
+ wchar_t expected_content_type[MAX_CONTENT_TYPE_LEN], content_type[MAX_CONTENT_TYPE_LEN];
+
+ if (!s->sent_request) {
+
+ if ((error = send_request(s, s->post_body_len, false)) < 0)
+ return error;
+
+ s->sent_request = 1;
+ }
+
+ if (s->chunked) {
+ GIT_ASSERT(s->verb == post_verb);
+
+ /* Flush, if necessary */
+ if (s->chunk_buffer_len > 0 &&
+ write_chunk(s->request, s->chunk_buffer, s->chunk_buffer_len) < 0)
+ return -1;
+
+ s->chunk_buffer_len = 0;
+
+ /* Write the final chunk. */
+ if (!WinHttpWriteData(s->request,
+ "0\r\n\r\n", 5,
+ &bytes_written)) {
+ git_error_set(GIT_ERROR_OS, "failed to write final chunk");
+ return -1;
+ }
+ }
+ else if (s->post_body) {
+ char *buffer;
+ DWORD len = s->post_body_len, bytes_read;
+
+ if (INVALID_SET_FILE_POINTER == SetFilePointer(s->post_body,
+ 0, 0, FILE_BEGIN) &&
+ NO_ERROR != GetLastError()) {
+ git_error_set(GIT_ERROR_OS, "failed to reset file pointer");
+ return -1;
+ }
+
+ buffer = git__malloc(CACHED_POST_BODY_BUF_SIZE);
+ GIT_ERROR_CHECK_ALLOC(buffer);
+
+ while (len > 0) {
+ DWORD bytes_written;
+
+ if (!ReadFile(s->post_body, buffer,
+ min(CACHED_POST_BODY_BUF_SIZE, len),
+ &bytes_read, NULL) ||
+ !bytes_read) {
+ git__free(buffer);
+ git_error_set(GIT_ERROR_OS, "failed to read from temp file");
+ return -1;
+ }
+
+ if (!WinHttpWriteData(s->request, buffer,
+ bytes_read, &bytes_written)) {
+ git__free(buffer);
+ git_error_set(GIT_ERROR_OS, "failed to write data");
+ return -1;
+ }
+
+ len -= bytes_read;
+ GIT_ASSERT(bytes_read == bytes_written);
+ }
+
+ git__free(buffer);
+
+ /* Eagerly close the temp file */
+ CloseHandle(s->post_body);
+ s->post_body = NULL;
+ }
+
+ if (!WinHttpReceiveResponse(s->request, 0)) {
+ git_error_set(GIT_ERROR_OS, "failed to receive response");
+ return -1;
+ }
+
+ /* Verify that we got a 200 back */
+ status_code_length = sizeof(status_code);
+
+ if (!WinHttpQueryHeaders(s->request,
+ WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,
+ WINHTTP_HEADER_NAME_BY_INDEX,
+ &status_code, &status_code_length,
+ WINHTTP_NO_HEADER_INDEX)) {
+ git_error_set(GIT_ERROR_OS, "failed to retrieve status code");
+ return -1;
+ }
+
+ /* The implementation of WinHTTP prior to Windows 7 will not
+ * redirect to an identical URI. Some Git hosters use self-redirects
+ * as part of their DoS mitigation strategy. Check first to see if we
+ * have a redirect status code, and that we haven't already streamed
+ * a post body. (We can't replay a streamed POST.) */
+ if (!s->chunked &&
+ (HTTP_STATUS_MOVED == status_code ||
+ HTTP_STATUS_REDIRECT == status_code ||
+ (HTTP_STATUS_REDIRECT_METHOD == status_code &&
+ get_verb == s->verb) ||
+ HTTP_STATUS_REDIRECT_KEEP_VERB == status_code ||
+ HTTP_STATUS_PERMANENT_REDIRECT == status_code)) {
+
+ /* Check for Windows 7. This workaround is only necessary on
+ * Windows Vista and earlier. Windows 7 is version 6.1. */
+ wchar_t *location;
+ DWORD location_length;
+ char *location8;
+
+ /* OK, fetch the Location header from the redirect. */
+ if (WinHttpQueryHeaders(s->request,
+ WINHTTP_QUERY_LOCATION,
+ WINHTTP_HEADER_NAME_BY_INDEX,
+ WINHTTP_NO_OUTPUT_BUFFER,
+ &location_length,
+ WINHTTP_NO_HEADER_INDEX) ||
+ GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ git_error_set(GIT_ERROR_OS, "failed to read Location header");
+ return -1;
+ }
+
+ location = git__malloc(location_length);
+ GIT_ERROR_CHECK_ALLOC(location);
+
+ if (!WinHttpQueryHeaders(s->request,
+ WINHTTP_QUERY_LOCATION,
+ WINHTTP_HEADER_NAME_BY_INDEX,
+ location,
+ &location_length,
+ WINHTTP_NO_HEADER_INDEX)) {
+ git_error_set(GIT_ERROR_OS, "failed to read Location header");
+ git__free(location);
+ return -1;
+ }
+
+ /* Convert the Location header to UTF-8 */
+ if (git_utf8_from_16_alloc(&location8, location) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to convert Location header to UTF-8");
+ git__free(location);
+ return -1;
+ }
+
+ git__free(location);
+
+ /* Replay the request */
+ winhttp_stream_close(s);
+
+ if (!git__prefixcmp_icase(location8, prefix_https)) {
+ bool follow = (t->owner->connect_opts.follow_redirects != GIT_REMOTE_REDIRECT_NONE);
+
+ /* Upgrade to secure connection; disconnect and start over */
+ if (git_net_url_apply_redirect(&t->server.url, location8, follow, s->service_url) < 0) {
+ git__free(location8);
+ return -1;
+ }
+
+ winhttp_close_connection(t);
+
+ if (winhttp_connect(t) < 0)
+ return -1;
+ }
+
+ git__free(location8);
+ goto replay;
+ }
+
+ /* Handle authentication failures */
+ if (status_code == HTTP_STATUS_DENIED) {
+ int error = acquire_credentials(s->request,
+ &t->server,
+ t->owner->url,
+ t->owner->connect_opts.callbacks.credentials,
+ t->owner->connect_opts.callbacks.payload);
+
+ if (error < 0) {
+ return error;
+ } else if (!error) {
+ GIT_ASSERT(t->server.cred);
+ winhttp_stream_close(s);
+ goto replay;
+ }
+ } else if (status_code == HTTP_STATUS_PROXY_AUTH_REQ) {
+ int error = acquire_credentials(s->request,
+ &t->proxy,
+ t->owner->connect_opts.proxy_opts.url,
+ t->owner->connect_opts.proxy_opts.credentials,
+ t->owner->connect_opts.proxy_opts.payload);
+
+ if (error < 0) {
+ return error;
+ } else if (!error) {
+ GIT_ASSERT(t->proxy.cred);
+ winhttp_stream_close(s);
+ goto replay;
+ }
+ }
+
+ if (HTTP_STATUS_OK != status_code) {
+ git_error_set(GIT_ERROR_HTTP, "request failed with status code: %lu", status_code);
+ return -1;
+ }
+
+ /* Verify that we got the correct content-type back */
+ if (post_verb == s->verb)
+ p_snprintf(expected_content_type_8, MAX_CONTENT_TYPE_LEN, "application/x-git-%s-result", s->service);
+ else
+ p_snprintf(expected_content_type_8, MAX_CONTENT_TYPE_LEN, "application/x-git-%s-advertisement", s->service);
+
+ if (git_utf8_to_16(expected_content_type, MAX_CONTENT_TYPE_LEN, expected_content_type_8) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to convert expected content-type to wide characters");
+ return -1;
+ }
+
+ content_type_length = sizeof(content_type);
+
+ if (!WinHttpQueryHeaders(s->request,
+ WINHTTP_QUERY_CONTENT_TYPE,
+ WINHTTP_HEADER_NAME_BY_INDEX,
+ &content_type, &content_type_length,
+ WINHTTP_NO_HEADER_INDEX)) {
+ git_error_set(GIT_ERROR_OS, "failed to retrieve response content-type");
+ return -1;
+ }
+
+ if (wcscmp(expected_content_type, content_type)) {
+ git_error_set(GIT_ERROR_HTTP, "received unexpected content-type");
+ return -1;
+ }
+
+ s->received_response = 1;
+ }
+
+ if (!WinHttpReadData(s->request,
+ (LPVOID)buffer,
+ (DWORD)buf_size,
+ &dw_bytes_read))
+ {
+ git_error_set(GIT_ERROR_OS, "failed to read data");
+ return -1;
+ }
+
+ *bytes_read = dw_bytes_read;
+
+ return 0;
+}
+
+static int winhttp_stream_write_single(
+ git_smart_subtransport_stream *stream,
+ const char *buffer,
+ size_t len)
+{
+ winhttp_stream *s = (winhttp_stream *)stream;
+ DWORD bytes_written;
+ int error;
+
+ if (!s->request && winhttp_stream_connect(s) < 0)
+ return -1;
+
+ /* This implementation of write permits only a single call. */
+ if (s->sent_request) {
+ git_error_set(GIT_ERROR_HTTP, "subtransport configured for only one write");
+ return -1;
+ }
+
+ if ((error = send_request(s, len, false)) < 0)
+ return error;
+
+ s->sent_request = 1;
+
+ if (!WinHttpWriteData(s->request,
+ (LPCVOID)buffer,
+ (DWORD)len,
+ &bytes_written)) {
+ git_error_set(GIT_ERROR_OS, "failed to write data");
+ return -1;
+ }
+
+ GIT_ASSERT((DWORD)len == bytes_written);
+
+ return 0;
+}
+
+static int put_uuid_string(LPWSTR buffer, size_t buffer_len_cch)
+{
+ UUID uuid;
+ RPC_STATUS status = UuidCreate(&uuid);
+ int result;
+
+ if (RPC_S_OK != status &&
+ RPC_S_UUID_LOCAL_ONLY != status &&
+ RPC_S_UUID_NO_ADDRESS != status) {
+ git_error_set(GIT_ERROR_HTTP, "unable to generate name for temp file");
+ return -1;
+ }
+
+ if (buffer_len_cch < UUID_LENGTH_CCH + 1) {
+ git_error_set(GIT_ERROR_HTTP, "buffer too small for name of temp file");
+ return -1;
+ }
+
+#if !defined(__MINGW32__) || defined(MINGW_HAS_SECURE_API)
+ result = swprintf_s(buffer, buffer_len_cch,
+#else
+ result = wsprintfW(buffer,
+#endif
+ L"%08x%04x%04x%02x%02x%02x%02x%02x%02x%02x%02x",
+ uuid.Data1, uuid.Data2, uuid.Data3,
+ uuid.Data4[0], uuid.Data4[1], uuid.Data4[2], uuid.Data4[3],
+ uuid.Data4[4], uuid.Data4[5], uuid.Data4[6], uuid.Data4[7]);
+
+ if (result < UUID_LENGTH_CCH) {
+ git_error_set(GIT_ERROR_OS, "unable to generate name for temp file");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int get_temp_file(LPWSTR buffer, DWORD buffer_len_cch)
+{
+ size_t len;
+
+ if (!GetTempPathW(buffer_len_cch, buffer)) {
+ git_error_set(GIT_ERROR_OS, "failed to get temp path");
+ return -1;
+ }
+
+ len = wcslen(buffer);
+
+ if (buffer[len - 1] != '\\' && len < buffer_len_cch)
+ buffer[len++] = '\\';
+
+ if (put_uuid_string(&buffer[len], (size_t)buffer_len_cch - len) < 0)
+ return -1;
+
+ return 0;
+}
+
+static int winhttp_stream_write_buffered(
+ git_smart_subtransport_stream *stream,
+ const char *buffer,
+ size_t len)
+{
+ winhttp_stream *s = (winhttp_stream *)stream;
+ DWORD bytes_written;
+
+ if (!s->request && winhttp_stream_connect(s) < 0)
+ return -1;
+
+ /* Buffer the payload, using a temporary file so we delegate
+ * memory management of the data to the operating system. */
+ if (!s->post_body) {
+ wchar_t temp_path[MAX_PATH + 1];
+
+ if (get_temp_file(temp_path, MAX_PATH + 1) < 0)
+ return -1;
+
+ s->post_body = CreateFileW(temp_path,
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_DELETE, NULL,
+ CREATE_NEW,
+ FILE_ATTRIBUTE_TEMPORARY | FILE_FLAG_DELETE_ON_CLOSE | FILE_FLAG_SEQUENTIAL_SCAN,
+ NULL);
+
+ if (INVALID_HANDLE_VALUE == s->post_body) {
+ s->post_body = NULL;
+ git_error_set(GIT_ERROR_OS, "failed to create temporary file");
+ return -1;
+ }
+ }
+
+ if (!WriteFile(s->post_body, buffer, (DWORD)len, &bytes_written, NULL)) {
+ git_error_set(GIT_ERROR_OS, "failed to write to temporary file");
+ return -1;
+ }
+
+ GIT_ASSERT((DWORD)len == bytes_written);
+
+ s->post_body_len += bytes_written;
+
+ return 0;
+}
+
+static int winhttp_stream_write_chunked(
+ git_smart_subtransport_stream *stream,
+ const char *buffer,
+ size_t len)
+{
+ winhttp_stream *s = (winhttp_stream *)stream;
+ int error;
+
+ if (!s->request && winhttp_stream_connect(s) < 0)
+ return -1;
+
+ if (!s->sent_request) {
+ /* Send Transfer-Encoding: chunked header */
+ if (!WinHttpAddRequestHeaders(s->request,
+ transfer_encoding, (ULONG) -1L,
+ WINHTTP_ADDREQ_FLAG_ADD)) {
+ git_error_set(GIT_ERROR_OS, "failed to add a header to the request");
+ return -1;
+ }
+
+ if ((error = send_request(s, 0, true)) < 0)
+ return error;
+
+ s->sent_request = 1;
+ }
+
+ if (len > CACHED_POST_BODY_BUF_SIZE) {
+ /* Flush, if necessary */
+ if (s->chunk_buffer_len > 0) {
+ if (write_chunk(s->request, s->chunk_buffer, s->chunk_buffer_len) < 0)
+ return -1;
+
+ s->chunk_buffer_len = 0;
+ }
+
+ /* Write chunk directly */
+ if (write_chunk(s->request, buffer, len) < 0)
+ return -1;
+ }
+ else {
+ /* Append as much to the buffer as we can */
+ int count = (int)min(CACHED_POST_BODY_BUF_SIZE - s->chunk_buffer_len, len);
+
+ if (!s->chunk_buffer) {
+ s->chunk_buffer = git__malloc(CACHED_POST_BODY_BUF_SIZE);
+ GIT_ERROR_CHECK_ALLOC(s->chunk_buffer);
+ }
+
+ memcpy(s->chunk_buffer + s->chunk_buffer_len, buffer, count);
+ s->chunk_buffer_len += count;
+ buffer += count;
+ len -= count;
+
+ /* Is the buffer full? If so, then flush */
+ if (CACHED_POST_BODY_BUF_SIZE == s->chunk_buffer_len) {
+ if (write_chunk(s->request, s->chunk_buffer, s->chunk_buffer_len) < 0)
+ return -1;
+
+ s->chunk_buffer_len = 0;
+
+ /* Is there any remaining data from the source? */
+ if (len > 0) {
+ memcpy(s->chunk_buffer, buffer, len);
+ s->chunk_buffer_len = (unsigned int)len;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void winhttp_stream_free(git_smart_subtransport_stream *stream)
+{
+ winhttp_stream *s = (winhttp_stream *)stream;
+
+ winhttp_stream_close(s);
+ git__free(s);
+}
+
+static int winhttp_stream_alloc(winhttp_subtransport *t, winhttp_stream **stream)
+{
+ winhttp_stream *s;
+
+ if (!stream)
+ return -1;
+
+ s = git__calloc(1, sizeof(winhttp_stream));
+ GIT_ERROR_CHECK_ALLOC(s);
+
+ s->parent.subtransport = &t->parent;
+ s->parent.read = winhttp_stream_read;
+ s->parent.write = winhttp_stream_write_single;
+ s->parent.free = winhttp_stream_free;
+
+ *stream = s;
+
+ return 0;
+}
+
+static int winhttp_uploadpack_ls(
+ winhttp_subtransport *t,
+ winhttp_stream *s)
+{
+ GIT_UNUSED(t);
+
+ s->service = upload_pack_service;
+ s->service_url = upload_pack_ls_service_url;
+ s->verb = get_verb;
+
+ return 0;
+}
+
+static int winhttp_uploadpack(
+ winhttp_subtransport *t,
+ winhttp_stream *s)
+{
+ GIT_UNUSED(t);
+
+ s->service = upload_pack_service;
+ s->service_url = upload_pack_service_url;
+ s->verb = post_verb;
+
+ return 0;
+}
+
+static int winhttp_receivepack_ls(
+ winhttp_subtransport *t,
+ winhttp_stream *s)
+{
+ GIT_UNUSED(t);
+
+ s->service = receive_pack_service;
+ s->service_url = receive_pack_ls_service_url;
+ s->verb = get_verb;
+
+ return 0;
+}
+
+static int winhttp_receivepack(
+ winhttp_subtransport *t,
+ winhttp_stream *s)
+{
+ GIT_UNUSED(t);
+
+ /* WinHTTP only supports Transfer-Encoding: chunked
+ * on Windows Vista (NT 6.0) and higher. */
+ s->chunked = git_has_win32_version(6, 0, 0);
+
+ if (s->chunked)
+ s->parent.write = winhttp_stream_write_chunked;
+ else
+ s->parent.write = winhttp_stream_write_buffered;
+
+ s->service = receive_pack_service;
+ s->service_url = receive_pack_service_url;
+ s->verb = post_verb;
+
+ return 0;
+}
+
+static int winhttp_action(
+ git_smart_subtransport_stream **stream,
+ git_smart_subtransport *subtransport,
+ const char *url,
+ git_smart_service_t action)
+{
+ winhttp_subtransport *t = (winhttp_subtransport *)subtransport;
+ winhttp_stream *s;
+ int ret = -1;
+
+ if (!t->connection)
+ if ((ret = git_net_url_parse(&t->server.url, url)) < 0 ||
+ (ret = winhttp_connect(t)) < 0)
+ return ret;
+
+ if (winhttp_stream_alloc(t, &s) < 0)
+ return -1;
+
+ if (!stream)
+ return -1;
+
+ switch (action)
+ {
+ case GIT_SERVICE_UPLOADPACK_LS:
+ ret = winhttp_uploadpack_ls(t, s);
+ break;
+
+ case GIT_SERVICE_UPLOADPACK:
+ ret = winhttp_uploadpack(t, s);
+ break;
+
+ case GIT_SERVICE_RECEIVEPACK_LS:
+ ret = winhttp_receivepack_ls(t, s);
+ break;
+
+ case GIT_SERVICE_RECEIVEPACK:
+ ret = winhttp_receivepack(t, s);
+ break;
+
+ default:
+ GIT_ASSERT(0);
+ }
+
+ if (!ret)
+ *stream = &s->parent;
+
+ return ret;
+}
+
+static int winhttp_close(git_smart_subtransport *subtransport)
+{
+ winhttp_subtransport *t = (winhttp_subtransport *)subtransport;
+
+ git_net_url_dispose(&t->server.url);
+ git_net_url_dispose(&t->proxy.url);
+
+ if (t->server.cred) {
+ t->server.cred->free(t->server.cred);
+ t->server.cred = NULL;
+ }
+
+ if (t->proxy.cred) {
+ t->proxy.cred->free(t->proxy.cred);
+ t->proxy.cred = NULL;
+ }
+
+ return winhttp_close_connection(t);
+}
+
+static void winhttp_free(git_smart_subtransport *subtransport)
+{
+ winhttp_subtransport *t = (winhttp_subtransport *)subtransport;
+
+ winhttp_close(subtransport);
+
+ git__free(t);
+}
+
+int git_smart_subtransport_http(git_smart_subtransport **out, git_transport *owner, void *param)
+{
+ winhttp_subtransport *t;
+
+ GIT_UNUSED(param);
+
+ if (!out)
+ return -1;
+
+ t = git__calloc(1, sizeof(winhttp_subtransport));
+ GIT_ERROR_CHECK_ALLOC(t);
+
+ t->owner = (transport_smart *)owner;
+ t->parent.action = winhttp_action;
+ t->parent.close = winhttp_close;
+ t->parent.free = winhttp_free;
+
+ *out = (git_smart_subtransport *) t;
+ return 0;
+}
+
+#endif /* GIT_WINHTTP */
diff --git a/src/libgit2/tree-cache.c b/src/libgit2/tree-cache.c
new file mode 100644
index 0000000..95d8798
--- /dev/null
+++ b/src/libgit2/tree-cache.c
@@ -0,0 +1,287 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "tree-cache.h"
+
+#include "pool.h"
+#include "tree.h"
+
+static git_tree_cache *find_child(
+ const git_tree_cache *tree, const char *path, const char *end)
+{
+ size_t i, dirlen = end ? (size_t)(end - path) : strlen(path);
+
+ for (i = 0; i < tree->children_count; ++i) {
+ git_tree_cache *child = tree->children[i];
+
+ if (child->namelen == dirlen && !memcmp(path, child->name, dirlen))
+ return child;
+ }
+
+ return NULL;
+}
+
+void git_tree_cache_invalidate_path(git_tree_cache *tree, const char *path)
+{
+ const char *ptr = path, *end;
+
+ if (tree == NULL)
+ return;
+
+ tree->entry_count = -1;
+
+ while (ptr != NULL) {
+ end = strchr(ptr, '/');
+
+ if (end == NULL) /* End of path */
+ break;
+
+ tree = find_child(tree, ptr, end);
+ if (tree == NULL) /* We don't have that tree */
+ return;
+
+ tree->entry_count = -1;
+ ptr = end + 1;
+ }
+}
+
+const git_tree_cache *git_tree_cache_get(const git_tree_cache *tree, const char *path)
+{
+ const char *ptr = path, *end;
+
+ if (tree == NULL) {
+ return NULL;
+ }
+
+ while (1) {
+ end = strchr(ptr, '/');
+
+ tree = find_child(tree, ptr, end);
+ if (tree == NULL) /* Can't find it */
+ return NULL;
+
+ if (end == NULL || *end + 1 == '\0')
+ return tree;
+
+ ptr = end + 1;
+ }
+}
+
+static int read_tree_internal(
+ git_tree_cache **out,
+ const char **buffer_in,
+ const char *buffer_end,
+ git_oid_t oid_type,
+ git_pool *pool)
+{
+ git_tree_cache *tree = NULL;
+ const char *name_start, *buffer;
+ size_t oid_size = git_oid_size(oid_type);
+ int count;
+
+ buffer = name_start = *buffer_in;
+
+ if ((buffer = memchr(buffer, '\0', buffer_end - buffer)) == NULL)
+ goto corrupted;
+
+ if (++buffer >= buffer_end)
+ goto corrupted;
+
+ if (git_tree_cache_new(&tree, name_start, oid_type, pool) < 0)
+ return -1;
+
+ /* Blank-terminated ASCII decimal number of entries in this tree */
+ if (git__strntol32(&count, buffer, buffer_end - buffer, &buffer, 10) < 0)
+ goto corrupted;
+
+ tree->entry_count = count;
+
+ if (*buffer != ' ' || ++buffer >= buffer_end)
+ goto corrupted;
+
+ /* Number of children of the tree, newline-terminated */
+ if (git__strntol32(&count, buffer, buffer_end - buffer, &buffer, 10) < 0 || count < 0)
+ goto corrupted;
+
+ tree->children_count = count;
+
+ if (*buffer != '\n' || ++buffer > buffer_end)
+ goto corrupted;
+
+ /* The OID is only there if it's not invalidated */
+ if (tree->entry_count >= 0) {
+ /* 160-bit SHA-1 for this tree and it's children */
+ if (buffer + oid_size > buffer_end)
+ goto corrupted;
+
+ git_oid__fromraw(&tree->oid, (const unsigned char *)buffer, oid_type);
+ buffer += oid_size;
+ }
+
+ /* Parse children: */
+ if (tree->children_count > 0) {
+ size_t i, bufsize;
+
+ GIT_ERROR_CHECK_ALLOC_MULTIPLY(&bufsize, tree->children_count, sizeof(git_tree_cache*));
+
+ tree->children = git_pool_malloc(pool, bufsize);
+ GIT_ERROR_CHECK_ALLOC(tree->children);
+
+ memset(tree->children, 0x0, bufsize);
+
+ for (i = 0; i < tree->children_count; ++i) {
+ if (read_tree_internal(&tree->children[i], &buffer, buffer_end, oid_type, pool) < 0)
+ goto corrupted;
+ }
+ }
+
+ *buffer_in = buffer;
+ *out = tree;
+ return 0;
+
+ corrupted:
+ git_error_set(GIT_ERROR_INDEX, "corrupted TREE extension in index");
+ return -1;
+}
+
+int git_tree_cache_read(
+ git_tree_cache **tree,
+ const char *buffer,
+ size_t buffer_size,
+ git_oid_t oid_type,
+ git_pool *pool)
+{
+ const char *buffer_end = buffer + buffer_size;
+
+ if (read_tree_internal(tree, &buffer, buffer_end, oid_type, pool) < 0)
+ return -1;
+
+ if (buffer < buffer_end) {
+ git_error_set(GIT_ERROR_INDEX, "corrupted TREE extension in index (unexpected trailing data)");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int read_tree_recursive(git_tree_cache *cache, const git_tree *tree, git_pool *pool)
+{
+ git_repository *repo;
+ size_t i, j, nentries, ntrees, alloc_size;
+ int error;
+
+ repo = git_tree_owner(tree);
+
+ git_oid_cpy(&cache->oid, git_tree_id(tree));
+ nentries = git_tree_entrycount(tree);
+
+ /*
+ * We make sure we know how many trees we need to allocate for
+ * so we don't have to realloc and change the pointers for the
+ * parents.
+ */
+ ntrees = 0;
+ for (i = 0; i < nentries; i++) {
+ const git_tree_entry *entry;
+
+ entry = git_tree_entry_byindex(tree, i);
+ if (git_tree_entry_filemode(entry) == GIT_FILEMODE_TREE)
+ ntrees++;
+ }
+
+ GIT_ERROR_CHECK_ALLOC_MULTIPLY(&alloc_size, ntrees, sizeof(git_tree_cache *));
+
+ cache->children_count = ntrees;
+ cache->children = git_pool_mallocz(pool, alloc_size);
+ GIT_ERROR_CHECK_ALLOC(cache->children);
+
+ j = 0;
+ for (i = 0; i < nentries; i++) {
+ const git_tree_entry *entry;
+ git_tree *subtree;
+
+ entry = git_tree_entry_byindex(tree, i);
+ if (git_tree_entry_filemode(entry) != GIT_FILEMODE_TREE) {
+ cache->entry_count++;
+ continue;
+ }
+
+ if ((error = git_tree_cache_new(&cache->children[j], git_tree_entry_name(entry), cache->oid_type, pool)) < 0)
+ return error;
+
+ if ((error = git_tree_lookup(&subtree, repo, git_tree_entry_id(entry))) < 0)
+ return error;
+
+ error = read_tree_recursive(cache->children[j], subtree, pool);
+ git_tree_free(subtree);
+ cache->entry_count += cache->children[j]->entry_count;
+ j++;
+
+ if (error < 0)
+ return error;
+ }
+
+ return 0;
+}
+
+int git_tree_cache_read_tree(git_tree_cache **out, const git_tree *tree, git_oid_t oid_type, git_pool *pool)
+{
+ int error;
+ git_tree_cache *cache;
+
+ if ((error = git_tree_cache_new(&cache, "", oid_type, pool)) < 0)
+ return error;
+
+ if ((error = read_tree_recursive(cache, tree, pool)) < 0)
+ return error;
+
+ *out = cache;
+ return 0;
+}
+
+int git_tree_cache_new(git_tree_cache **out, const char *name, git_oid_t oid_type, git_pool *pool)
+{
+ size_t name_len, alloc_size;
+ git_tree_cache *tree;
+
+ name_len = strlen(name);
+
+ GIT_ERROR_CHECK_ALLOC_ADD3(&alloc_size, sizeof(git_tree_cache), name_len, 1);
+
+ tree = git_pool_malloc(pool, alloc_size);
+ GIT_ERROR_CHECK_ALLOC(tree);
+
+ memset(tree, 0x0, sizeof(git_tree_cache));
+ /* NUL-terminated tree name */
+ tree->oid_type = oid_type;
+ tree->namelen = name_len;
+ memcpy(tree->name, name, name_len);
+ tree->name[name_len] = '\0';
+
+ *out = tree;
+ return 0;
+}
+
+static void write_tree(git_str *out, git_tree_cache *tree)
+{
+ size_t i;
+
+ git_str_printf(out, "%s%c%"PRIdZ" %"PRIuZ"\n", tree->name, 0, tree->entry_count, tree->children_count);
+
+ if (tree->entry_count != -1)
+ git_str_put(out, (char *)&tree->oid.id, git_oid_size(tree->oid_type));
+
+ for (i = 0; i < tree->children_count; i++)
+ write_tree(out, tree->children[i]);
+}
+
+int git_tree_cache_write(git_str *out, git_tree_cache *tree)
+{
+ write_tree(out, tree);
+
+ return git_str_oom(out) ? -1 : 0;
+}
diff --git a/src/libgit2/tree-cache.h b/src/libgit2/tree-cache.h
new file mode 100644
index 0000000..e4a73f2
--- /dev/null
+++ b/src/libgit2/tree-cache.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_tree_cache_h__
+#define INCLUDE_tree_cache_h__
+
+#include "common.h"
+
+#include "pool.h"
+#include "str.h"
+#include "git2/oid.h"
+
+typedef struct git_tree_cache {
+ struct git_tree_cache **children;
+ size_t children_count;
+
+ git_oid_t oid_type;
+
+ ssize_t entry_count;
+ git_oid oid;
+ size_t namelen;
+ char name[GIT_FLEX_ARRAY];
+} git_tree_cache;
+
+int git_tree_cache_write(git_str *out, git_tree_cache *tree);
+int git_tree_cache_read(git_tree_cache **tree, const char *buffer, size_t buffer_size, git_oid_t oid_type, git_pool *pool);
+void git_tree_cache_invalidate_path(git_tree_cache *tree, const char *path);
+const git_tree_cache *git_tree_cache_get(const git_tree_cache *tree, const char *path);
+int git_tree_cache_new(git_tree_cache **out, const char *name, git_oid_t oid_type, git_pool *pool);
+/**
+ * Read a tree as the root of the tree cache (like for `git read-tree`)
+ */
+int git_tree_cache_read_tree(git_tree_cache **out, const git_tree *tree, git_oid_t oid_type, git_pool *pool);
+void git_tree_cache_free(git_tree_cache *tree);
+
+#endif
diff --git a/src/libgit2/tree.c b/src/libgit2/tree.c
new file mode 100644
index 0000000..236a87f
--- /dev/null
+++ b/src/libgit2/tree.c
@@ -0,0 +1,1330 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "tree.h"
+
+#include "commit.h"
+#include "git2/repository.h"
+#include "git2/object.h"
+#include "futils.h"
+#include "tree-cache.h"
+#include "index.h"
+#include "path.h"
+
+#define DEFAULT_TREE_SIZE 16
+#define MAX_FILEMODE_BYTES 6
+
+#define TREE_ENTRY_CHECK_NAMELEN(n) \
+ if (n > UINT16_MAX) { git_error_set(GIT_ERROR_INVALID, "tree entry path too long"); }
+
+static bool valid_filemode(const int filemode)
+{
+ return (filemode == GIT_FILEMODE_TREE
+ || filemode == GIT_FILEMODE_BLOB
+ || filemode == GIT_FILEMODE_BLOB_EXECUTABLE
+ || filemode == GIT_FILEMODE_LINK
+ || filemode == GIT_FILEMODE_COMMIT);
+}
+
+GIT_INLINE(git_filemode_t) normalize_filemode(git_filemode_t filemode)
+{
+ /* Tree bits set, but it's not a commit */
+ if (GIT_MODE_TYPE(filemode) == GIT_FILEMODE_TREE)
+ return GIT_FILEMODE_TREE;
+
+ /* If any of the x bits are set */
+ if (GIT_PERMS_IS_EXEC(filemode))
+ return GIT_FILEMODE_BLOB_EXECUTABLE;
+
+ /* 16XXXX means commit */
+ if (GIT_MODE_TYPE(filemode) == GIT_FILEMODE_COMMIT)
+ return GIT_FILEMODE_COMMIT;
+
+ /* 12XXXX means symlink */
+ if (GIT_MODE_TYPE(filemode) == GIT_FILEMODE_LINK)
+ return GIT_FILEMODE_LINK;
+
+ /* Otherwise, return a blob */
+ return GIT_FILEMODE_BLOB;
+}
+
+static int valid_entry_name(git_repository *repo, const char *filename)
+{
+ return *filename != '\0' &&
+ git_path_is_valid(repo, filename, 0,
+ GIT_FS_PATH_REJECT_TRAVERSAL | GIT_PATH_REJECT_DOT_GIT | GIT_FS_PATH_REJECT_SLASH);
+}
+
+static int entry_sort_cmp(const void *a, const void *b)
+{
+ const git_tree_entry *e1 = (const git_tree_entry *)a;
+ const git_tree_entry *e2 = (const git_tree_entry *)b;
+
+ return git_fs_path_cmp(
+ e1->filename, e1->filename_len, git_tree_entry__is_tree(e1),
+ e2->filename, e2->filename_len, git_tree_entry__is_tree(e2),
+ git__strncmp);
+}
+
+int git_tree_entry_cmp(const git_tree_entry *e1, const git_tree_entry *e2)
+{
+ return entry_sort_cmp(e1, e2);
+}
+
+/**
+ * Allocate a new self-contained entry, with enough space after it to
+ * store the filename and the id.
+ */
+static git_tree_entry *alloc_entry(const char *filename, size_t filename_len, const git_oid *id)
+{
+ git_tree_entry *entry = NULL;
+ char *filename_ptr;
+ size_t tree_len;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ size_t oid_size = git_oid_size(id->type);
+#else
+ size_t oid_size = GIT_OID_SHA1_SIZE;
+#endif
+
+ TREE_ENTRY_CHECK_NAMELEN(filename_len);
+
+ if (GIT_ADD_SIZET_OVERFLOW(&tree_len, sizeof(git_tree_entry), filename_len) ||
+ GIT_ADD_SIZET_OVERFLOW(&tree_len, tree_len, 1) ||
+ GIT_ADD_SIZET_OVERFLOW(&tree_len, tree_len, oid_size))
+ return NULL;
+
+ entry = git__calloc(1, tree_len);
+ if (!entry)
+ return NULL;
+
+ filename_ptr = ((char *) entry) + sizeof(git_tree_entry);
+ memcpy(filename_ptr, filename, filename_len);
+ entry->filename = filename_ptr;
+ entry->filename_len = (uint16_t)filename_len;
+
+ git_oid_cpy(&entry->oid, id);
+
+ return entry;
+}
+
+struct tree_key_search {
+ const char *filename;
+ uint16_t filename_len;
+};
+
+static int homing_search_cmp(const void *key, const void *array_member)
+{
+ const struct tree_key_search *ksearch = key;
+ const git_tree_entry *entry = array_member;
+
+ const uint16_t len1 = ksearch->filename_len;
+ const uint16_t len2 = entry->filename_len;
+
+ return memcmp(
+ ksearch->filename,
+ entry->filename,
+ len1 < len2 ? len1 : len2
+ );
+}
+
+/*
+ * Search for an entry in a given tree.
+ *
+ * Note that this search is performed in two steps because
+ * of the way tree entries are sorted internally in git:
+ *
+ * Entries in a tree are not sorted alphabetically; two entries
+ * with the same root prefix will have different positions
+ * depending on whether they are folders (subtrees) or normal files.
+ *
+ * Consequently, it is not possible to find an entry on the tree
+ * with a binary search if you don't know whether the filename
+ * you're looking for is a folder or a normal file.
+ *
+ * To work around this, we first perform a homing binary search
+ * on the tree, using the minimal length root prefix of our filename.
+ * Once the comparisons for this homing search start becoming
+ * ambiguous because of folder vs file sorting, we look linearly
+ * around the area for our target file.
+ */
+static int tree_key_search(
+ size_t *at_pos,
+ const git_tree *tree,
+ const char *filename,
+ size_t filename_len)
+{
+ struct tree_key_search ksearch;
+ const git_tree_entry *entry;
+ size_t homing, i;
+
+ TREE_ENTRY_CHECK_NAMELEN(filename_len);
+
+ ksearch.filename = filename;
+ ksearch.filename_len = (uint16_t)filename_len;
+
+ /* Initial homing search; find an entry on the tree with
+ * the same prefix as the filename we're looking for */
+
+ if (git_array_search(&homing,
+ tree->entries, &homing_search_cmp, &ksearch) < 0)
+ return GIT_ENOTFOUND; /* just a signal error; not passed back to user */
+
+ /* We found a common prefix. Look forward as long as
+ * there are entries that share the common prefix */
+ for (i = homing; i < tree->entries.size; ++i) {
+ entry = git_array_get(tree->entries, i);
+
+ if (homing_search_cmp(&ksearch, entry) < 0)
+ break;
+
+ if (entry->filename_len == filename_len &&
+ memcmp(filename, entry->filename, filename_len) == 0) {
+ if (at_pos)
+ *at_pos = i;
+
+ return 0;
+ }
+ }
+
+ /* If we haven't found our filename yet, look backwards
+ * too as long as we have entries with the same prefix */
+ if (homing > 0) {
+ i = homing - 1;
+
+ do {
+ entry = git_array_get(tree->entries, i);
+
+ if (homing_search_cmp(&ksearch, entry) > 0)
+ break;
+
+ if (entry->filename_len == filename_len &&
+ memcmp(filename, entry->filename, filename_len) == 0) {
+ if (at_pos)
+ *at_pos = i;
+
+ return 0;
+ }
+ } while (i-- > 0);
+ }
+
+ /* The filename doesn't exist at all */
+ return GIT_ENOTFOUND;
+}
+
+void git_tree_entry_free(git_tree_entry *entry)
+{
+ if (entry == NULL)
+ return;
+
+ git__free(entry);
+}
+
+int git_tree_entry_dup(git_tree_entry **dest, const git_tree_entry *source)
+{
+ git_tree_entry *cpy;
+
+ GIT_ASSERT_ARG(source);
+
+ cpy = alloc_entry(source->filename, source->filename_len, &source->oid);
+ if (cpy == NULL)
+ return -1;
+
+ cpy->attr = source->attr;
+
+ *dest = cpy;
+ return 0;
+}
+
+void git_tree__free(void *_tree)
+{
+ git_tree *tree = _tree;
+
+ git_odb_object_free(tree->odb_obj);
+ git_array_clear(tree->entries);
+ git__free(tree);
+}
+
+git_filemode_t git_tree_entry_filemode(const git_tree_entry *entry)
+{
+ return normalize_filemode(entry->attr);
+}
+
+git_filemode_t git_tree_entry_filemode_raw(const git_tree_entry *entry)
+{
+ return entry->attr;
+}
+
+const char *git_tree_entry_name(const git_tree_entry *entry)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(entry, NULL);
+ return entry->filename;
+}
+
+const git_oid *git_tree_entry_id(const git_tree_entry *entry)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(entry, NULL);
+ return &entry->oid;
+}
+
+git_object_t git_tree_entry_type(const git_tree_entry *entry)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(entry, GIT_OBJECT_INVALID);
+
+ if (S_ISGITLINK(entry->attr))
+ return GIT_OBJECT_COMMIT;
+ else if (S_ISDIR(entry->attr))
+ return GIT_OBJECT_TREE;
+ else
+ return GIT_OBJECT_BLOB;
+}
+
+int git_tree_entry_to_object(
+ git_object **object_out,
+ git_repository *repo,
+ const git_tree_entry *entry)
+{
+ GIT_ASSERT_ARG(entry);
+ GIT_ASSERT_ARG(object_out);
+
+ return git_object_lookup(object_out, repo, &entry->oid, GIT_OBJECT_ANY);
+}
+
+static const git_tree_entry *entry_fromname(
+ const git_tree *tree, const char *name, size_t name_len)
+{
+ size_t idx;
+
+ if (tree_key_search(&idx, tree, name, name_len) < 0)
+ return NULL;
+
+ return git_array_get(tree->entries, idx);
+}
+
+const git_tree_entry *git_tree_entry_byname(
+ const git_tree *tree, const char *filename)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(tree, NULL);
+ GIT_ASSERT_ARG_WITH_RETVAL(filename, NULL);
+
+ return entry_fromname(tree, filename, strlen(filename));
+}
+
+const git_tree_entry *git_tree_entry_byindex(
+ const git_tree *tree, size_t idx)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(tree, NULL);
+ return git_array_get(tree->entries, idx);
+}
+
+const git_tree_entry *git_tree_entry_byid(
+ const git_tree *tree, const git_oid *id)
+{
+ size_t i;
+ const git_tree_entry *e;
+
+ GIT_ASSERT_ARG_WITH_RETVAL(tree, NULL);
+
+ git_array_foreach(tree->entries, i, e) {
+ if (git_oid_equal(&e->oid, id))
+ return e;
+ }
+
+ return NULL;
+}
+
+size_t git_tree_entrycount(const git_tree *tree)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(tree, 0);
+ return tree->entries.size;
+}
+
+size_t git_treebuilder_entrycount(git_treebuilder *bld)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(bld, 0);
+
+ return git_strmap_size(bld->map);
+}
+
+GIT_INLINE(void) set_error(const char *str, const char *path)
+{
+ if (path)
+ git_error_set(GIT_ERROR_TREE, "%s - %s", str, path);
+ else
+ git_error_set(GIT_ERROR_TREE, "%s", str);
+}
+
+static int tree_error(const char *str, const char *path)
+{
+ set_error(str, path);
+ return -1;
+}
+
+static int tree_parse_error(const char *str, const char *path)
+{
+ set_error(str, path);
+ return GIT_EINVALID;
+}
+
+static int parse_mode(uint16_t *mode_out, const char *buffer, size_t buffer_len, const char **buffer_out)
+{
+ int32_t mode;
+ int error;
+
+ if (!buffer_len || git__isspace(*buffer))
+ return -1;
+
+ if ((error = git__strntol32(&mode, buffer, buffer_len, buffer_out, 8)) < 0)
+ return error;
+
+ if (mode < 0 || mode > UINT16_MAX)
+ return -1;
+
+ *mode_out = mode;
+
+ return 0;
+}
+
+int git_tree__parse_raw(void *_tree, const char *data, size_t size, git_oid_t oid_type)
+{
+ git_tree *tree = _tree;
+ const char *buffer;
+ const char *buffer_end;
+ const long oid_size = (long)git_oid_size(oid_type);
+
+ buffer = data;
+ buffer_end = buffer + size;
+
+ tree->odb_obj = NULL;
+ git_array_init_to_size(tree->entries, DEFAULT_TREE_SIZE);
+ GIT_ERROR_CHECK_ARRAY(tree->entries);
+
+ while (buffer < buffer_end) {
+ git_tree_entry *entry;
+ size_t filename_len;
+ const char *nul;
+ uint16_t attr;
+
+ if (parse_mode(&attr, buffer, buffer_end - buffer, &buffer) < 0 || !buffer)
+ return tree_parse_error("failed to parse tree: can't parse filemode", NULL);
+
+ if (buffer >= buffer_end || (*buffer++) != ' ')
+ return tree_parse_error("failed to parse tree: missing space after filemode", NULL);
+
+ if ((nul = memchr(buffer, 0, buffer_end - buffer)) == NULL)
+ return tree_parse_error("failed to parse tree: object is corrupted", NULL);
+
+ if ((filename_len = nul - buffer) == 0 || filename_len > UINT16_MAX)
+ return tree_parse_error("failed to parse tree: can't parse filename", NULL);
+
+ if ((buffer_end - (nul + 1)) < (long)oid_size)
+ return tree_parse_error("failed to parse tree: can't parse OID", NULL);
+
+ /* Allocate the entry */
+ entry = git_array_alloc(tree->entries);
+ GIT_ERROR_CHECK_ALLOC(entry);
+
+ entry->attr = attr;
+ entry->filename_len = (uint16_t)filename_len;
+ entry->filename = buffer;
+ buffer += filename_len + 1;
+
+ git_oid__fromraw(&entry->oid, (unsigned char *)buffer, oid_type);
+ buffer += oid_size;
+ }
+
+ return 0;
+}
+
+int git_tree__parse(void *_tree, git_odb_object *odb_obj, git_oid_t oid_type)
+{
+ git_tree *tree = _tree;
+ const char *data = git_odb_object_data(odb_obj);
+ size_t size = git_odb_object_size(odb_obj);
+ int error;
+
+ if ((error = git_tree__parse_raw(tree, data, size, oid_type)) < 0 ||
+ (error = git_odb_object_dup(&tree->odb_obj, odb_obj)) < 0)
+ return error;
+
+ return error;
+}
+
+static size_t find_next_dir(const char *dirname, git_index *index, size_t start)
+{
+ size_t dirlen, i, entries = git_index_entrycount(index);
+
+ dirlen = strlen(dirname);
+ for (i = start; i < entries; ++i) {
+ const git_index_entry *entry = git_index_get_byindex(index, i);
+ if (strlen(entry->path) < dirlen ||
+ memcmp(entry->path, dirname, dirlen) ||
+ (dirlen > 0 && entry->path[dirlen] != '/')) {
+ break;
+ }
+ }
+
+ return i;
+}
+
+static git_object_t otype_from_mode(git_filemode_t filemode)
+{
+ switch (filemode) {
+ case GIT_FILEMODE_TREE:
+ return GIT_OBJECT_TREE;
+ case GIT_FILEMODE_COMMIT:
+ return GIT_OBJECT_COMMIT;
+ default:
+ return GIT_OBJECT_BLOB;
+ }
+}
+
+static int check_entry(git_repository *repo, const char *filename, const git_oid *id, git_filemode_t filemode)
+{
+ if (!valid_filemode(filemode))
+ return tree_error("failed to insert entry: invalid filemode for file", filename);
+
+ if (!valid_entry_name(repo, filename))
+ return tree_error("failed to insert entry: invalid name for a tree entry", filename);
+
+ if (git_oid_is_zero(id))
+ return tree_error("failed to insert entry: invalid null OID", filename);
+
+ if (filemode != GIT_FILEMODE_COMMIT &&
+ !git_object__is_valid(repo, id, otype_from_mode(filemode)))
+ return tree_error("failed to insert entry: invalid object specified", filename);
+
+ return 0;
+}
+
+static int git_treebuilder__write_with_buffer(
+ git_oid *oid,
+ git_treebuilder *bld,
+ git_str *buf)
+{
+ int error = 0;
+ size_t i, entrycount;
+ git_odb *odb;
+ git_tree_entry *entry;
+ git_vector entries = GIT_VECTOR_INIT;
+ size_t oid_size = git_oid_size(bld->repo->oid_type);
+
+ git_str_clear(buf);
+
+ entrycount = git_strmap_size(bld->map);
+ if ((error = git_vector_init(&entries, entrycount, entry_sort_cmp)) < 0)
+ goto out;
+
+ if (buf->asize == 0 &&
+ (error = git_str_grow(buf, entrycount * 72)) < 0)
+ goto out;
+
+ git_strmap_foreach_value(bld->map, entry, {
+ if ((error = git_vector_insert(&entries, entry)) < 0)
+ goto out;
+ });
+
+ git_vector_sort(&entries);
+
+ for (i = 0; i < entries.length && !error; ++i) {
+ entry = git_vector_get(&entries, i);
+
+ git_str_printf(buf, "%o ", entry->attr);
+ git_str_put(buf, entry->filename, entry->filename_len + 1);
+ git_str_put(buf, (char *)entry->oid.id, oid_size);
+
+ if (git_str_oom(buf)) {
+ error = -1;
+ goto out;
+ }
+ }
+
+ if ((error = git_repository_odb__weakptr(&odb, bld->repo)) == 0)
+ error = git_odb_write(oid, odb, buf->ptr, buf->size, GIT_OBJECT_TREE);
+
+out:
+ git_vector_free(&entries);
+
+ return error;
+}
+
+static int append_entry(
+ git_treebuilder *bld,
+ const char *filename,
+ const git_oid *id,
+ git_filemode_t filemode,
+ bool validate)
+{
+ git_tree_entry *entry;
+ int error = 0;
+
+ if (validate && ((error = check_entry(bld->repo, filename, id, filemode)) < 0))
+ return error;
+
+ entry = alloc_entry(filename, strlen(filename), id);
+ GIT_ERROR_CHECK_ALLOC(entry);
+
+ entry->attr = (uint16_t)filemode;
+
+ if ((error = git_strmap_set(bld->map, entry->filename, entry)) < 0) {
+ git_tree_entry_free(entry);
+ git_error_set(GIT_ERROR_TREE, "failed to append entry %s to the tree builder", filename);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int write_tree(
+ git_oid *oid,
+ git_repository *repo,
+ git_index *index,
+ const char *dirname,
+ size_t start,
+ git_str *shared_buf)
+{
+ git_treebuilder *bld = NULL;
+ size_t i, entries = git_index_entrycount(index);
+ int error;
+ size_t dirname_len = strlen(dirname);
+ const git_tree_cache *cache;
+
+ cache = git_tree_cache_get(index->tree, dirname);
+ if (cache != NULL && cache->entry_count >= 0){
+ git_oid_cpy(oid, &cache->oid);
+ return (int)find_next_dir(dirname, index, start);
+ }
+
+ if ((error = git_treebuilder_new(&bld, repo, NULL)) < 0 || bld == NULL)
+ return -1;
+
+ /*
+ * This loop is unfortunate, but necessary. The index doesn't have
+ * any directories, so we need to handle that manually, and we
+ * need to keep track of the current position.
+ */
+ for (i = start; i < entries; ++i) {
+ const git_index_entry *entry = git_index_get_byindex(index, i);
+ const char *filename, *next_slash;
+
+ /*
+ * If we've left our (sub)tree, exit the loop and return. The
+ * first check is an early out (and security for the
+ * third). The second check is a simple prefix comparison. The
+ * third check catches situations where there is a directory
+ * win32/sys and a file win32mmap.c. Without it, the following
+ * code believes there is a file win32/mmap.c
+ */
+ if (strlen(entry->path) < dirname_len ||
+ memcmp(entry->path, dirname, dirname_len) ||
+ (dirname_len > 0 && entry->path[dirname_len] != '/')) {
+ break;
+ }
+
+ filename = entry->path + dirname_len;
+ if (*filename == '/')
+ filename++;
+ next_slash = strchr(filename, '/');
+ if (next_slash) {
+ git_oid sub_oid;
+ int written;
+ char *subdir, *last_comp;
+
+ subdir = git__strndup(entry->path, next_slash - entry->path);
+ GIT_ERROR_CHECK_ALLOC(subdir);
+
+ /* Write out the subtree */
+ written = write_tree(&sub_oid, repo, index, subdir, i, shared_buf);
+ if (written < 0) {
+ git__free(subdir);
+ goto on_error;
+ } else {
+ i = written - 1; /* -1 because of the loop increment */
+ }
+
+ /*
+ * We need to figure out what we want toinsert
+ * into this tree. If we're traversing
+ * deps/zlib/, then we only want to write
+ * 'zlib' into the tree.
+ */
+ last_comp = strrchr(subdir, '/');
+ if (last_comp) {
+ last_comp++; /* Get rid of the '/' */
+ } else {
+ last_comp = subdir;
+ }
+
+ error = append_entry(bld, last_comp, &sub_oid, S_IFDIR, true);
+ git__free(subdir);
+ if (error < 0)
+ goto on_error;
+ } else {
+ error = append_entry(bld, filename, &entry->id, entry->mode, true);
+ if (error < 0)
+ goto on_error;
+ }
+ }
+
+ if (git_treebuilder__write_with_buffer(oid, bld, shared_buf) < 0)
+ goto on_error;
+
+ git_treebuilder_free(bld);
+ return (int)i;
+
+on_error:
+ git_treebuilder_free(bld);
+ return -1;
+}
+
+int git_tree__write_index(
+ git_oid *oid, git_index *index, git_repository *repo)
+{
+ int ret;
+ git_tree *tree;
+ git_str shared_buf = GIT_STR_INIT;
+ bool old_ignore_case = false;
+
+ GIT_ASSERT_ARG(oid);
+ GIT_ASSERT_ARG(index);
+ GIT_ASSERT_ARG(repo);
+
+ if (git_index_has_conflicts(index)) {
+ git_error_set(GIT_ERROR_INDEX,
+ "cannot create a tree from a not fully merged index.");
+ return GIT_EUNMERGED;
+ }
+
+ if (index->tree != NULL && index->tree->entry_count >= 0) {
+ git_oid_cpy(oid, &index->tree->oid);
+ return 0;
+ }
+
+ /* The tree cache didn't help us; we'll have to write
+ * out a tree. If the index is ignore_case, we must
+ * make it case-sensitive for the duration of the tree-write
+ * operation. */
+
+ if (index->ignore_case) {
+ old_ignore_case = true;
+ git_index__set_ignore_case(index, false);
+ }
+
+ ret = write_tree(oid, repo, index, "", 0, &shared_buf);
+ git_str_dispose(&shared_buf);
+
+ if (old_ignore_case)
+ git_index__set_ignore_case(index, true);
+
+ index->tree = NULL;
+
+ if (ret < 0)
+ return ret;
+
+ git_pool_clear(&index->tree_pool);
+
+ if ((ret = git_tree_lookup(&tree, repo, oid)) < 0)
+ return ret;
+
+ /* Read the tree cache into the index */
+ ret = git_tree_cache_read_tree(&index->tree, tree, index->oid_type, &index->tree_pool);
+ git_tree_free(tree);
+
+ return ret;
+}
+
+int git_treebuilder_new(
+ git_treebuilder **builder_p,
+ git_repository *repo,
+ const git_tree *source)
+{
+ git_treebuilder *bld;
+ size_t i;
+
+ GIT_ASSERT_ARG(builder_p);
+ GIT_ASSERT_ARG(repo);
+
+ bld = git__calloc(1, sizeof(git_treebuilder));
+ GIT_ERROR_CHECK_ALLOC(bld);
+
+ bld->repo = repo;
+
+ if (git_strmap_new(&bld->map) < 0) {
+ git__free(bld);
+ return -1;
+ }
+
+ if (source != NULL) {
+ git_tree_entry *entry_src;
+
+ git_array_foreach(source->entries, i, entry_src) {
+ if (append_entry(
+ bld, entry_src->filename,
+ &entry_src->oid,
+ entry_src->attr,
+ false) < 0)
+ goto on_error;
+ }
+ }
+
+ *builder_p = bld;
+ return 0;
+
+on_error:
+ git_treebuilder_free(bld);
+ return -1;
+}
+
+int git_treebuilder_insert(
+ const git_tree_entry **entry_out,
+ git_treebuilder *bld,
+ const char *filename,
+ const git_oid *id,
+ git_filemode_t filemode)
+{
+ git_tree_entry *entry;
+ int error;
+
+ GIT_ASSERT_ARG(bld);
+ GIT_ASSERT_ARG(id);
+ GIT_ASSERT_ARG(filename);
+
+ if ((error = check_entry(bld->repo, filename, id, filemode)) < 0)
+ return error;
+
+ if ((entry = git_strmap_get(bld->map, filename)) != NULL) {
+ git_oid_cpy(&entry->oid, id);
+ } else {
+ entry = alloc_entry(filename, strlen(filename), id);
+ GIT_ERROR_CHECK_ALLOC(entry);
+
+ if ((error = git_strmap_set(bld->map, entry->filename, entry)) < 0) {
+ git_tree_entry_free(entry);
+ git_error_set(GIT_ERROR_TREE, "failed to insert %s", filename);
+ return -1;
+ }
+ }
+
+ entry->attr = filemode;
+
+ if (entry_out)
+ *entry_out = entry;
+
+ return 0;
+}
+
+static git_tree_entry *treebuilder_get(git_treebuilder *bld, const char *filename)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(bld, NULL);
+ GIT_ASSERT_ARG_WITH_RETVAL(filename, NULL);
+
+ return git_strmap_get(bld->map, filename);
+}
+
+const git_tree_entry *git_treebuilder_get(git_treebuilder *bld, const char *filename)
+{
+ return treebuilder_get(bld, filename);
+}
+
+int git_treebuilder_remove(git_treebuilder *bld, const char *filename)
+{
+ git_tree_entry *entry = treebuilder_get(bld, filename);
+
+ if (entry == NULL)
+ return tree_error("failed to remove entry: file isn't in the tree", filename);
+
+ git_strmap_delete(bld->map, filename);
+ git_tree_entry_free(entry);
+
+ return 0;
+}
+
+int git_treebuilder_write(git_oid *oid, git_treebuilder *bld)
+{
+ GIT_ASSERT_ARG(oid);
+ GIT_ASSERT_ARG(bld);
+
+ return git_treebuilder__write_with_buffer(oid, bld, &bld->write_cache);
+}
+
+int git_treebuilder_filter(
+ git_treebuilder *bld,
+ git_treebuilder_filter_cb filter,
+ void *payload)
+{
+ const char *filename;
+ git_tree_entry *entry;
+
+ GIT_ASSERT_ARG(bld);
+ GIT_ASSERT_ARG(filter);
+
+ git_strmap_foreach(bld->map, filename, entry, {
+ if (filter(entry, payload)) {
+ git_strmap_delete(bld->map, filename);
+ git_tree_entry_free(entry);
+ }
+ });
+
+ return 0;
+}
+
+int git_treebuilder_clear(git_treebuilder *bld)
+{
+ git_tree_entry *e;
+
+ GIT_ASSERT_ARG(bld);
+
+ git_strmap_foreach_value(bld->map, e, git_tree_entry_free(e));
+ git_strmap_clear(bld->map);
+
+ return 0;
+}
+
+void git_treebuilder_free(git_treebuilder *bld)
+{
+ if (bld == NULL)
+ return;
+
+ git_str_dispose(&bld->write_cache);
+ git_treebuilder_clear(bld);
+ git_strmap_free(bld->map);
+ git__free(bld);
+}
+
+static size_t subpath_len(const char *path)
+{
+ const char *slash_pos = strchr(path, '/');
+ if (slash_pos == NULL)
+ return strlen(path);
+
+ return slash_pos - path;
+}
+
+int git_tree_entry_bypath(
+ git_tree_entry **entry_out,
+ const git_tree *root,
+ const char *path)
+{
+ int error = 0;
+ git_tree *subtree;
+ const git_tree_entry *entry;
+ size_t filename_len;
+
+ /* Find how long is the current path component (i.e.
+ * the filename between two slashes */
+ filename_len = subpath_len(path);
+
+ if (filename_len == 0) {
+ git_error_set(GIT_ERROR_TREE, "invalid tree path given");
+ return GIT_ENOTFOUND;
+ }
+
+ entry = entry_fromname(root, path, filename_len);
+
+ if (entry == NULL) {
+ git_error_set(GIT_ERROR_TREE,
+ "the path '%.*s' does not exist in the given tree", (int) filename_len, path);
+ return GIT_ENOTFOUND;
+ }
+
+ switch (path[filename_len]) {
+ case '/':
+ /* If there are more components in the path...
+ * then this entry *must* be a tree */
+ if (!git_tree_entry__is_tree(entry)) {
+ git_error_set(GIT_ERROR_TREE,
+ "the path '%.*s' exists but is not a tree", (int) filename_len, path);
+ return GIT_ENOTFOUND;
+ }
+
+ /* If there's only a slash left in the path, we
+ * return the current entry; otherwise, we keep
+ * walking down the path */
+ if (path[filename_len + 1] != '\0')
+ break;
+ /* fall through */
+ case '\0':
+ /* If there are no more components in the path, return
+ * this entry */
+ return git_tree_entry_dup(entry_out, entry);
+ }
+
+ if (git_tree_lookup(&subtree, root->object.repo, &entry->oid) < 0)
+ return -1;
+
+ error = git_tree_entry_bypath(
+ entry_out,
+ subtree,
+ path + filename_len + 1
+ );
+
+ git_tree_free(subtree);
+ return error;
+}
+
+static int tree_walk(
+ const git_tree *tree,
+ git_treewalk_cb callback,
+ git_str *path,
+ void *payload,
+ bool preorder)
+{
+ int error = 0;
+ size_t i;
+ const git_tree_entry *entry;
+
+ git_array_foreach(tree->entries, i, entry) {
+ if (preorder) {
+ error = callback(path->ptr, entry, payload);
+ if (error < 0) { /* negative value stops iteration */
+ git_error_set_after_callback_function(error, "git_tree_walk");
+ break;
+ }
+ if (error > 0) { /* positive value skips this entry */
+ error = 0;
+ continue;
+ }
+ }
+
+ if (git_tree_entry__is_tree(entry)) {
+ git_tree *subtree;
+ size_t path_len = git_str_len(path);
+
+ error = git_tree_lookup(&subtree, tree->object.repo, &entry->oid);
+ if (error < 0)
+ break;
+
+ /* append the next entry to the path */
+ git_str_puts(path, entry->filename);
+ git_str_putc(path, '/');
+
+ if (git_str_oom(path))
+ error = -1;
+ else
+ error = tree_walk(subtree, callback, path, payload, preorder);
+
+ git_tree_free(subtree);
+ if (error != 0)
+ break;
+
+ git_str_truncate(path, path_len);
+ }
+
+ if (!preorder) {
+ error = callback(path->ptr, entry, payload);
+ if (error < 0) { /* negative value stops iteration */
+ git_error_set_after_callback_function(error, "git_tree_walk");
+ break;
+ }
+ error = 0;
+ }
+ }
+
+ return error;
+}
+
+int git_tree_walk(
+ const git_tree *tree,
+ git_treewalk_mode mode,
+ git_treewalk_cb callback,
+ void *payload)
+{
+ int error = 0;
+ git_str root_path = GIT_STR_INIT;
+
+ if (mode != GIT_TREEWALK_POST && mode != GIT_TREEWALK_PRE) {
+ git_error_set(GIT_ERROR_INVALID, "invalid walking mode for tree walk");
+ return -1;
+ }
+
+ error = tree_walk(
+ tree, callback, &root_path, payload, (mode == GIT_TREEWALK_PRE));
+
+ git_str_dispose(&root_path);
+
+ return error;
+}
+
+static int compare_entries(const void *_a, const void *_b)
+{
+ const git_tree_update *a = (git_tree_update *) _a;
+ const git_tree_update *b = (git_tree_update *) _b;
+
+ return strcmp(a->path, b->path);
+}
+
+static int on_dup_entry(void **old, void *new)
+{
+ GIT_UNUSED(old); GIT_UNUSED(new);
+
+ git_error_set(GIT_ERROR_TREE, "duplicate entries given for update");
+ return -1;
+}
+
+/*
+ * We keep the previous tree and the new one at each level of the
+ * stack. When we leave a level we're done with that tree and we can
+ * write it out to the odb.
+ */
+typedef struct {
+ git_treebuilder *bld;
+ git_tree *tree;
+ char *name;
+} tree_stack_entry;
+
+/** Count how many slashes (i.e. path components) there are in this string */
+GIT_INLINE(size_t) count_slashes(const char *path)
+{
+ size_t count = 0;
+ const char *slash;
+
+ while ((slash = strchr(path, '/')) != NULL) {
+ count++;
+ path = slash + 1;
+ }
+
+ return count;
+}
+
+static bool next_component(git_str *out, const char *in)
+{
+ const char *slash = strchr(in, '/');
+
+ git_str_clear(out);
+
+ if (slash)
+ git_str_put(out, in, slash - in);
+
+ return !!slash;
+}
+
+static int create_popped_tree(tree_stack_entry *current, tree_stack_entry *popped, git_str *component)
+{
+ int error;
+ git_oid new_tree;
+
+ git_tree_free(popped->tree);
+
+ /* If the tree would be empty, remove it from the one higher up */
+ if (git_treebuilder_entrycount(popped->bld) == 0) {
+ git_treebuilder_free(popped->bld);
+ error = git_treebuilder_remove(current->bld, popped->name);
+ git__free(popped->name);
+ return error;
+ }
+
+ error = git_treebuilder_write(&new_tree, popped->bld);
+ git_treebuilder_free(popped->bld);
+
+ if (error < 0) {
+ git__free(popped->name);
+ return error;
+ }
+
+ /* We've written out the tree, now we have to put the new value into its parent */
+ git_str_clear(component);
+ git_str_puts(component, popped->name);
+ git__free(popped->name);
+
+ GIT_ERROR_CHECK_ALLOC(component->ptr);
+
+ /* Error out if this would create a D/F conflict in this update */
+ if (current->tree) {
+ const git_tree_entry *to_replace;
+ to_replace = git_tree_entry_byname(current->tree, component->ptr);
+ if (to_replace && git_tree_entry_type(to_replace) != GIT_OBJECT_TREE) {
+ git_error_set(GIT_ERROR_TREE, "D/F conflict when updating tree");
+ return -1;
+ }
+ }
+
+ return git_treebuilder_insert(NULL, current->bld, component->ptr, &new_tree, GIT_FILEMODE_TREE);
+}
+
+int git_tree_create_updated(git_oid *out, git_repository *repo, git_tree *baseline, size_t nupdates, const git_tree_update *updates)
+{
+ git_array_t(tree_stack_entry) stack = GIT_ARRAY_INIT;
+ tree_stack_entry *root_elem;
+ git_vector entries;
+ int error;
+ size_t i;
+ git_str component = GIT_STR_INIT;
+
+ if ((error = git_vector_init(&entries, nupdates, compare_entries)) < 0)
+ return error;
+
+ /* Sort the entries for treversal */
+ for (i = 0 ; i < nupdates; i++) {
+ if ((error = git_vector_insert_sorted(&entries, (void *) &updates[i], on_dup_entry)) < 0)
+ goto cleanup;
+ }
+
+ root_elem = git_array_alloc(stack);
+ GIT_ERROR_CHECK_ALLOC(root_elem);
+ memset(root_elem, 0, sizeof(*root_elem));
+
+ if (baseline && (error = git_tree_dup(&root_elem->tree, baseline)) < 0)
+ goto cleanup;
+
+ if ((error = git_treebuilder_new(&root_elem->bld, repo, root_elem->tree)) < 0)
+ goto cleanup;
+
+ for (i = 0; i < nupdates; i++) {
+ const git_tree_update *last_update = i == 0 ? NULL : git_vector_get(&entries, i-1);
+ const git_tree_update *update = git_vector_get(&entries, i);
+ size_t common_prefix = 0, steps_up, j;
+ const char *path;
+
+ /* Figure out how much we need to change from the previous tree */
+ if (last_update)
+ common_prefix = git_fs_path_common_dirlen(last_update->path, update->path);
+
+ /*
+ * The entries are sorted, so when we find we're no
+ * longer in the same directory, we need to abandon
+ * the old tree (steps up) and dive down to the next
+ * one.
+ */
+ steps_up = last_update == NULL ? 0 : count_slashes(&last_update->path[common_prefix]);
+
+ for (j = 0; j < steps_up; j++) {
+ tree_stack_entry *current, *popped = git_array_pop(stack);
+ GIT_ASSERT(popped);
+
+ current = git_array_last(stack);
+ GIT_ASSERT(current);
+
+ if ((error = create_popped_tree(current, popped, &component)) < 0)
+ goto cleanup;
+ }
+
+ /* Now that we've created the trees we popped from the stack, let's go back down */
+ path = &update->path[common_prefix];
+ while (next_component(&component, path)) {
+ tree_stack_entry *last, *new_entry;
+ const git_tree_entry *entry;
+
+ last = git_array_last(stack);
+ entry = last->tree ? git_tree_entry_byname(last->tree, component.ptr) : NULL;
+ if (!entry)
+ entry = treebuilder_get(last->bld, component.ptr);
+
+ if (entry && git_tree_entry_type(entry) != GIT_OBJECT_TREE) {
+ git_error_set(GIT_ERROR_TREE, "D/F conflict when updating tree");
+ error = -1;
+ goto cleanup;
+ }
+
+ new_entry = git_array_alloc(stack);
+ GIT_ERROR_CHECK_ALLOC(new_entry);
+ memset(new_entry, 0, sizeof(*new_entry));
+
+ new_entry->tree = NULL;
+ if (entry && (error = git_tree_lookup(&new_entry->tree, repo, git_tree_entry_id(entry))) < 0)
+ goto cleanup;
+
+ if ((error = git_treebuilder_new(&new_entry->bld, repo, new_entry->tree)) < 0)
+ goto cleanup;
+
+ new_entry->name = git__strdup(component.ptr);
+ GIT_ERROR_CHECK_ALLOC(new_entry->name);
+
+ /* Get to the start of the next component */
+ path += component.size + 1;
+ }
+
+ /* After all that, we're finally at the place where we want to perform the update */
+ switch (update->action) {
+ case GIT_TREE_UPDATE_UPSERT:
+ {
+ /* Make sure we're replacing something of the same type */
+ tree_stack_entry *last = git_array_last(stack);
+ char *basename = git_fs_path_basename(update->path);
+ const git_tree_entry *e = git_treebuilder_get(last->bld, basename);
+ if (e && git_tree_entry_type(e) != git_object__type_from_filemode(update->filemode)) {
+ git__free(basename);
+ git_error_set(GIT_ERROR_TREE, "cannot replace '%s' with '%s' at '%s'",
+ git_object_type2string(git_tree_entry_type(e)),
+ git_object_type2string(git_object__type_from_filemode(update->filemode)),
+ update->path);
+ error = -1;
+ goto cleanup;
+ }
+
+ error = git_treebuilder_insert(NULL, last->bld, basename, &update->id, update->filemode);
+ git__free(basename);
+ break;
+ }
+ case GIT_TREE_UPDATE_REMOVE:
+ {
+ tree_stack_entry *last = git_array_last(stack);
+ char *basename = git_fs_path_basename(update->path);
+ error = git_treebuilder_remove(last->bld, basename);
+ git__free(basename);
+ break;
+ }
+ default:
+ git_error_set(GIT_ERROR_TREE, "unknown action for update");
+ error = -1;
+ goto cleanup;
+ }
+
+ if (error < 0)
+ goto cleanup;
+ }
+
+ /* We're done, go up the stack again and write out the tree */
+ {
+ tree_stack_entry *current = NULL, *popped = NULL;
+ while ((popped = git_array_pop(stack)) != NULL) {
+ current = git_array_last(stack);
+ /* We've reached the top, current is the root tree */
+ if (!current)
+ break;
+
+ if ((error = create_popped_tree(current, popped, &component)) < 0)
+ goto cleanup;
+ }
+
+ /* Write out the root tree */
+ git__free(popped->name);
+ git_tree_free(popped->tree);
+
+ error = git_treebuilder_write(out, popped->bld);
+ git_treebuilder_free(popped->bld);
+ if (error < 0)
+ goto cleanup;
+ }
+
+cleanup:
+ {
+ tree_stack_entry *e;
+ while ((e = git_array_pop(stack)) != NULL) {
+ git_treebuilder_free(e->bld);
+ git_tree_free(e->tree);
+ git__free(e->name);
+ }
+ }
+
+ git_str_dispose(&component);
+ git_array_clear(stack);
+ git_vector_free(&entries);
+ return error;
+}
+
+/* Deprecated Functions */
+
+#ifndef GIT_DEPRECATE_HARD
+
+int git_treebuilder_write_with_buffer(git_oid *oid, git_treebuilder *bld, git_buf *buf)
+{
+ GIT_UNUSED(buf);
+
+ return git_treebuilder_write(oid, bld);
+}
+
+#endif
diff --git a/src/libgit2/tree.h b/src/libgit2/tree.h
new file mode 100644
index 0000000..5088450
--- /dev/null
+++ b/src/libgit2/tree.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_tree_h__
+#define INCLUDE_tree_h__
+
+#include "common.h"
+
+#include "git2/tree.h"
+#include "repository.h"
+#include "odb.h"
+#include "vector.h"
+#include "strmap.h"
+#include "pool.h"
+
+struct git_tree_entry {
+ uint16_t attr;
+ uint16_t filename_len;
+ git_oid oid;
+ const char *filename;
+};
+
+struct git_tree {
+ git_object object;
+ git_odb_object *odb_obj;
+ git_array_t(git_tree_entry) entries;
+};
+
+struct git_treebuilder {
+ git_repository *repo;
+ git_strmap *map;
+ git_str write_cache;
+};
+
+GIT_INLINE(bool) git_tree_entry__is_tree(const struct git_tree_entry *e)
+{
+ return (S_ISDIR(e->attr) && !S_ISGITLINK(e->attr));
+}
+
+void git_tree__free(void *tree);
+int git_tree__parse(void *tree, git_odb_object *obj, git_oid_t oid_type);
+int git_tree__parse_raw(void *_tree, const char *data, size_t size, git_oid_t oid_type);
+
+/**
+ * Write a tree to the given repository
+ */
+int git_tree__write_index(
+ git_oid *oid, git_index *index, git_repository *repo);
+
+/**
+ * Obsolete mode kept for compatibility reasons
+ */
+#define GIT_FILEMODE_BLOB_GROUP_WRITABLE 0100664
+
+#endif
diff --git a/src/libgit2/userdiff.h b/src/libgit2/userdiff.h
new file mode 100644
index 0000000..c9a80d7
--- /dev/null
+++ b/src/libgit2/userdiff.h
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_userdiff_h__
+#define INCLUDE_userdiff_h__
+
+#include "regexp.h"
+
+/*
+ * This file isolates the built in diff driver function name patterns.
+ * Most of these patterns are taken from Git (with permission from the
+ * original authors for relicensing to libgit2).
+ */
+
+typedef struct {
+ const char *name;
+ const char *fns;
+ const char *words;
+ int flags;
+} git_diff_driver_definition;
+
+#define WORD_DEFAULT "|[^[:space:]]|[\xc0-\xff][\x80-\xbf]+"
+
+/*
+ * These builtin driver definition macros have same signature as in core
+ * git userdiff.c so that the data can be extracted verbatim
+ */
+#define PATTERNS(NAME, FN_PATS, WORD_PAT) \
+ { NAME, FN_PATS, WORD_PAT WORD_DEFAULT, 0 }
+#define IPATTERN(NAME, FN_PATS, WORD_PAT) \
+ { NAME, FN_PATS, WORD_PAT WORD_DEFAULT, GIT_REGEXP_ICASE }
+
+/*
+ * The table of diff driver patterns
+ *
+ * Function name patterns are a list of newline separated patterns that
+ * match a function declaration (i.e. the line you want in the hunk header),
+ * or a negative pattern prefixed with a '!' to reject a pattern (such as
+ * rejecting goto labels in C code).
+ *
+ * Word boundary patterns are just a simple pattern that will be OR'ed with
+ * the default value above (i.e. whitespace or non-ASCII characters).
+ */
+static git_diff_driver_definition builtin_defs[] = {
+
+IPATTERN("ada",
+ "!^(.*[ \t])?(is[ \t]+new|renames|is[ \t]+separate)([ \t].*)?$\n"
+ "!^[ \t]*with[ \t].*$\n"
+ "^[ \t]*((procedure|function)[ \t]+.*)$\n"
+ "^[ \t]*((package|protected|task)[ \t]+.*)$",
+ /* -- */
+ "[a-zA-Z][a-zA-Z0-9_]*"
+ "|[-+]?[0-9][0-9#_.aAbBcCdDeEfF]*([eE][+-]?[0-9_]+)?"
+ "|=>|\\.\\.|\\*\\*|:=|/=|>=|<=|<<|>>|<>"),
+
+IPATTERN("fortran",
+ "!^([C*]|[ \t]*!)\n"
+ "!^[ \t]*MODULE[ \t]+PROCEDURE[ \t]\n"
+ "^[ \t]*((END[ \t]+)?(PROGRAM|MODULE|BLOCK[ \t]+DATA"
+ "|([^'\" \t]+[ \t]+)*(SUBROUTINE|FUNCTION))[ \t]+[A-Z].*)$",
+ /* -- */
+ "[a-zA-Z][a-zA-Z0-9_]*"
+ "|\\.([Ee][Qq]|[Nn][Ee]|[Gg][TtEe]|[Ll][TtEe]|[Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee]|[Aa][Nn][Dd]|[Oo][Rr]|[Nn]?[Ee][Qq][Vv]|[Nn][Oo][Tt])\\."
+ /* numbers and format statements like 2E14.4, or ES12.6, 9X.
+ * Don't worry about format statements without leading digits since
+ * they would have been matched above as a variable anyway. */
+ "|[-+]?[0-9.]+([AaIiDdEeFfLlTtXx][Ss]?[-+]?[0-9.]*)?(_[a-zA-Z0-9][a-zA-Z0-9_]*)?"
+ "|//|\\*\\*|::|[/<>=]="),
+
+PATTERNS("html", "^[ \t]*(<[Hh][1-6][ \t].*>.*)$",
+ "[^<>= \t]+"),
+
+PATTERNS("java",
+ "!^[ \t]*(catch|do|for|if|instanceof|new|return|switch|throw|while)\n"
+ "^[ \t]*(([A-Za-z_][A-Za-z_0-9]*[ \t]+)+[A-Za-z_][A-Za-z_0-9]*[ \t]*\\([^;]*)$",
+ /* -- */
+ "[a-zA-Z_][a-zA-Z0-9_]*"
+ "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
+ "|[-+*/<>%&^|=!]="
+ "|--|\\+\\+|<<=?|>>>?=?|&&|\\|\\|"),
+
+PATTERNS("matlab",
+ "^[[:space:]]*((classdef|function)[[:space:]].*)$|^%%[[:space:]].*$",
+ "[a-zA-Z_][a-zA-Z0-9_]*|[-+0-9.e]+|[=~<>]=|\\.[*/\\^']|\\|\\||&&"),
+
+PATTERNS("objc",
+ /* Negate C statements that can look like functions */
+ "!^[ \t]*(do|for|if|else|return|switch|while)\n"
+ /* Objective-C methods */
+ "^[ \t]*([-+][ \t]*\\([ \t]*[A-Za-z_][A-Za-z_0-9* \t]*\\)[ \t]*[A-Za-z_].*)$\n"
+ /* C functions */
+ "^[ \t]*(([A-Za-z_][A-Za-z_0-9]*[ \t]+)+[A-Za-z_][A-Za-z_0-9]*[ \t]*\\([^;]*)$\n"
+ /* Objective-C class/protocol definitions */
+ "^(@(implementation|interface|protocol)[ \t].*)$",
+ /* -- */
+ "[a-zA-Z_][a-zA-Z0-9_]*"
+ "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
+ "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"),
+
+PATTERNS("pascal",
+ "^(((class[ \t]+)?(procedure|function)|constructor|destructor|interface|"
+ "implementation|initialization|finalization)[ \t]*.*)$"
+ "\n"
+ "^(.*=[ \t]*(class|record).*)$",
+ /* -- */
+ "[a-zA-Z_][a-zA-Z0-9_]*"
+ "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+"
+ "|<>|<=|>=|:=|\\.\\."),
+
+PATTERNS("perl",
+ "^package .*\n"
+ "^sub [[:alnum:]_':]+[ \t]*"
+ "(\\([^)]*\\)[ \t]*)?" /* prototype */
+ /*
+ * Attributes. A regex can't count nested parentheses,
+ * so just slurp up whatever we see, taking care not
+ * to accept lines like "sub foo; # defined elsewhere".
+ *
+ * An attribute could contain a semicolon, but at that
+ * point it seems reasonable enough to give up.
+ */
+ "(:[^;#]*)?"
+ "(\\{[ \t]*)?" /* brace can come here or on the next line */
+ "(#.*)?$\n" /* comment */
+ "^(BEGIN|END|INIT|CHECK|UNITCHECK|AUTOLOAD|DESTROY)[ \t]*"
+ "(\\{[ \t]*)?" /* brace can come here or on the next line */
+ "(#.*)?$\n"
+ "^=head[0-9] .*", /* POD */
+ /* -- */
+ "[[:alpha:]_'][[:alnum:]_']*"
+ "|0[xb]?[0-9a-fA-F_]*"
+ /* taking care not to interpret 3..5 as (3.)(.5) */
+ "|[0-9a-fA-F_]+(\\.[0-9a-fA-F_]+)?([eE][-+]?[0-9_]+)?"
+ "|=>|-[rwxoRWXOezsfdlpSugkbctTBMAC>]|~~|::"
+ "|&&=|\\|\\|=|//=|\\*\\*="
+ "|&&|\\|\\||//|\\+\\+|--|\\*\\*|\\.\\.\\.?"
+ "|[-+*/%.^&<>=!|]="
+ "|=~|!~"
+ "|<<|<>|<=>|>>"),
+
+PATTERNS("python", "^[ \t]*((class|def)[ \t].*)$",
+ /* -- */
+ "[a-zA-Z_][a-zA-Z0-9_]*"
+ "|[-+0-9.e]+[jJlL]?|0[xX]?[0-9a-fA-F]+[lL]?"
+ "|[-+*/<>%&^|=!]=|//=?|<<=?|>>=?|\\*\\*=?"),
+
+PATTERNS("ruby", "^[ \t]*((class|module|def)[ \t].*)$",
+ /* -- */
+ "(@|@@|\\$)?[a-zA-Z_][a-zA-Z0-9_]*"
+ "|[-+0-9.e]+|0[xXbB]?[0-9a-fA-F]+|\\?(\\\\C-)?(\\\\M-)?."
+ "|//=?|[-+*/<>%&^|=!]=|<<=?|>>=?|===|\\.{1,3}|::|[!=]~"),
+
+PATTERNS("bibtex", "(@[a-zA-Z]{1,}[ \t]*\\{{0,1}[ \t]*[^ \t\"@',\\#}{~%]*).*$",
+ "[={}\"]|[^={}\" \t]+"),
+
+PATTERNS("tex", "^(\\\\((sub)*section|chapter|part)\\*{0,1}\\{.*)$",
+ "\\\\[a-zA-Z@]+|\\\\.|[a-zA-Z0-9\x80-\xff]+"),
+
+PATTERNS("cpp",
+ /* Jump targets or access declarations */
+ "!^[ \t]*[A-Za-z_][A-Za-z_0-9]*:[[:space:]]*($|/[/*])\n"
+ /* functions/methods, variables, and compounds at top level */
+ "^((::[[:space:]]*)?[A-Za-z_].*)$",
+ /* -- */
+ "[a-zA-Z_][a-zA-Z0-9_]*"
+ "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lLuU]*"
+ "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->\\*?|\\.\\*"),
+
+PATTERNS("csharp",
+ /* Keywords */
+ "!^[ \t]*(do|while|for|if|else|instanceof|new|return|switch|case|throw|catch|using)\n"
+ /* Methods and constructors */
+ "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[<>@._[:alnum:]]+[ \t]*\\(.*\\))[ \t]*$\n"
+ /* Properties */
+ "^[ \t]*(((static|public|internal|private|protected|new|virtual|sealed|override|unsafe)[ \t]+)*[][<>@.~_[:alnum:]]+[ \t]+[@._[:alnum:]]+)[ \t]*$\n"
+ /* Type definitions */
+ "^[ \t]*(((static|public|internal|private|protected|new|unsafe|sealed|abstract|partial)[ \t]+)*(class|enum|interface|struct)[ \t]+.*)$\n"
+ /* Namespace */
+ "^[ \t]*(namespace[ \t]+.*)$",
+ /* -- */
+ "[a-zA-Z_][a-zA-Z0-9_]*"
+ "|[-+0-9.e]+[fFlL]?|0[xXbB]?[0-9a-fA-F]+[lL]?"
+ "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"),
+
+PATTERNS("php",
+ "^[ \t]*(((public|private|protected|static|final)[ \t]+)*((class|function)[ \t].*))$",
+ /* -- */
+ "[a-zA-Z_][a-zA-Z0-9_]*"
+ "|[-+0-9.e]+[fFlL]?|0[xX]?[0-9a-fA-F]+[lL]?"
+ "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"),
+
+PATTERNS("javascript",
+ "([a-zA-Z_$][a-zA-Z0-9_$]*(\\.[a-zA-Z0-9_$]+)*[ \t]*=[ \t]*function([ \t][a-zA-Z_$][a-zA-Z0-9_$]*)?[^\\{]*)\n"
+ "([a-zA-Z_$][a-zA-Z0-9_$]*[ \t]*:[ \t]*function([ \t][a-zA-Z_$][a-zA-Z0-9_$]*)?[^\\{]*)\n"
+ "[^a-zA-Z0-9_\\$](function([ \t][a-zA-Z_$][a-zA-Z0-9_$]*)?[^\\{]*)",
+ /* -- */
+ "[a-zA-Z_][a-zA-Z0-9_]*"
+ "|[-+0-9.e]+[fFlL]?|0[xX]?[0-9a-fA-F]+[lL]?"
+ "|[-+*/<>%&^|=!]=|--|\\+\\+|<<=?|>>=?|&&|\\|\\||::|->"),
+};
+
+#undef IPATTERN
+#undef PATTERNS
+#undef WORD_DEFAULT
+
+#endif
+
diff --git a/src/libgit2/worktree.c b/src/libgit2/worktree.c
new file mode 100644
index 0000000..a878634
--- /dev/null
+++ b/src/libgit2/worktree.c
@@ -0,0 +1,672 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "worktree.h"
+
+#include "buf.h"
+#include "repository.h"
+#include "path.h"
+
+#include "git2/branch.h"
+#include "git2/commit.h"
+#include "git2/worktree.h"
+
+static bool is_worktree_dir(const char *dir)
+{
+ git_str buf = GIT_STR_INIT;
+ int error;
+
+ if (git_str_sets(&buf, dir) < 0)
+ return -1;
+
+ error = git_fs_path_contains_file(&buf, "commondir")
+ && git_fs_path_contains_file(&buf, "gitdir")
+ && git_fs_path_contains_file(&buf, "HEAD");
+
+ git_str_dispose(&buf);
+ return error;
+}
+
+int git_worktree_list(git_strarray *wts, git_repository *repo)
+{
+ git_vector worktrees = GIT_VECTOR_INIT;
+ git_str path = GIT_STR_INIT;
+ char *worktree;
+ size_t i, len;
+ int error;
+
+ GIT_ASSERT_ARG(wts);
+ GIT_ASSERT_ARG(repo);
+
+ wts->count = 0;
+ wts->strings = NULL;
+
+ if ((error = git_str_joinpath(&path, repo->commondir, "worktrees/")) < 0)
+ goto exit;
+ if (!git_fs_path_exists(path.ptr) || git_fs_path_is_empty_dir(path.ptr))
+ goto exit;
+ if ((error = git_fs_path_dirload(&worktrees, path.ptr, path.size, 0x0)) < 0)
+ goto exit;
+
+ len = path.size;
+
+ git_vector_foreach(&worktrees, i, worktree) {
+ git_str_truncate(&path, len);
+ git_str_puts(&path, worktree);
+
+ if (!is_worktree_dir(path.ptr)) {
+ git_vector_remove(&worktrees, i);
+ git__free(worktree);
+ }
+ }
+
+ wts->strings = (char **)git_vector_detach(&wts->count, NULL, &worktrees);
+
+exit:
+ git_str_dispose(&path);
+
+ return error;
+}
+
+char *git_worktree__read_link(const char *base, const char *file)
+{
+ git_str path = GIT_STR_INIT, buf = GIT_STR_INIT;
+
+ GIT_ASSERT_ARG_WITH_RETVAL(base, NULL);
+ GIT_ASSERT_ARG_WITH_RETVAL(file, NULL);
+
+ if (git_str_joinpath(&path, base, file) < 0)
+ goto err;
+ if (git_futils_readbuffer(&buf, path.ptr) < 0)
+ goto err;
+ git_str_dispose(&path);
+
+ git_str_rtrim(&buf);
+
+ if (!git_fs_path_is_relative(buf.ptr))
+ return git_str_detach(&buf);
+
+ if (git_str_sets(&path, base) < 0)
+ goto err;
+ if (git_fs_path_apply_relative(&path, buf.ptr) < 0)
+ goto err;
+ git_str_dispose(&buf);
+
+ return git_str_detach(&path);
+
+err:
+ git_str_dispose(&buf);
+ git_str_dispose(&path);
+
+ return NULL;
+}
+
+static int write_wtfile(const char *base, const char *file, const git_str *buf)
+{
+ git_str path = GIT_STR_INIT;
+ int err;
+
+ GIT_ASSERT_ARG(base);
+ GIT_ASSERT_ARG(file);
+ GIT_ASSERT_ARG(buf);
+
+ if ((err = git_str_joinpath(&path, base, file)) < 0)
+ goto out;
+
+ if ((err = git_futils_writebuffer(buf, path.ptr, O_CREAT|O_EXCL|O_WRONLY, 0644)) < 0)
+ goto out;
+
+out:
+ git_str_dispose(&path);
+
+ return err;
+}
+
+static int open_worktree_dir(git_worktree **out, const char *parent, const char *dir, const char *name)
+{
+ git_str gitdir = GIT_STR_INIT;
+ git_worktree *wt = NULL;
+ int error = 0;
+
+ if (!is_worktree_dir(dir)) {
+ error = -1;
+ goto out;
+ }
+
+ if ((error = git_path_validate_length(NULL, dir)) < 0)
+ goto out;
+
+ if ((wt = git__calloc(1, sizeof(*wt))) == NULL) {
+ error = -1;
+ goto out;
+ }
+
+ if ((wt->name = git__strdup(name)) == NULL ||
+ (wt->commondir_path = git_worktree__read_link(dir, "commondir")) == NULL ||
+ (wt->gitlink_path = git_worktree__read_link(dir, "gitdir")) == NULL ||
+ (parent && (wt->parent_path = git__strdup(parent)) == NULL) ||
+ (wt->worktree_path = git_fs_path_dirname(wt->gitlink_path)) == NULL) {
+ error = -1;
+ goto out;
+ }
+
+ if ((error = git_fs_path_prettify_dir(&gitdir, dir, NULL)) < 0)
+ goto out;
+ wt->gitdir_path = git_str_detach(&gitdir);
+
+ if ((error = git_worktree_is_locked(NULL, wt)) < 0)
+ goto out;
+ wt->locked = !!error;
+ error = 0;
+
+ *out = wt;
+
+out:
+ if (error)
+ git_worktree_free(wt);
+ git_str_dispose(&gitdir);
+
+ return error;
+}
+
+int git_worktree_lookup(git_worktree **out, git_repository *repo, const char *name)
+{
+ git_str path = GIT_STR_INIT;
+ git_worktree *wt = NULL;
+ int error;
+
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
+
+ *out = NULL;
+
+ if ((error = git_str_join3(&path, '/', repo->commondir, "worktrees", name)) < 0)
+ goto out;
+
+ if (!git_fs_path_isdir(path.ptr)) {
+ error = GIT_ENOTFOUND;
+ goto out;
+ }
+
+ if ((error = (open_worktree_dir(out, git_repository_workdir(repo), path.ptr, name))) < 0)
+ goto out;
+
+out:
+ git_str_dispose(&path);
+
+ if (error)
+ git_worktree_free(wt);
+
+ return error;
+}
+
+int git_worktree_open_from_repository(git_worktree **out, git_repository *repo)
+{
+ git_str parent = GIT_STR_INIT;
+ const char *gitdir, *commondir;
+ char *name = NULL;
+ int error = 0;
+
+ if (!git_repository_is_worktree(repo)) {
+ git_error_set(GIT_ERROR_WORKTREE, "cannot open worktree of a non-worktree repo");
+ error = -1;
+ goto out;
+ }
+
+ gitdir = git_repository_path(repo);
+ commondir = git_repository_commondir(repo);
+
+ if ((error = git_fs_path_prettify_dir(&parent, "..", commondir)) < 0)
+ goto out;
+
+ /* The name is defined by the last component in '.git/worktree/%s' */
+ name = git_fs_path_basename(gitdir);
+
+ if ((error = open_worktree_dir(out, parent.ptr, gitdir, name)) < 0)
+ goto out;
+
+out:
+ git__free(name);
+ git_str_dispose(&parent);
+
+ return error;
+}
+
+void git_worktree_free(git_worktree *wt)
+{
+ if (!wt)
+ return;
+
+ git__free(wt->commondir_path);
+ git__free(wt->worktree_path);
+ git__free(wt->gitlink_path);
+ git__free(wt->gitdir_path);
+ git__free(wt->parent_path);
+ git__free(wt->name);
+ git__free(wt);
+}
+
+int git_worktree_validate(const git_worktree *wt)
+{
+ GIT_ASSERT_ARG(wt);
+
+ if (!is_worktree_dir(wt->gitdir_path)) {
+ git_error_set(GIT_ERROR_WORKTREE,
+ "worktree gitdir ('%s') is not valid",
+ wt->gitlink_path);
+ return GIT_ERROR;
+ }
+
+ if (wt->parent_path && !git_fs_path_exists(wt->parent_path)) {
+ git_error_set(GIT_ERROR_WORKTREE,
+ "worktree parent directory ('%s') does not exist ",
+ wt->parent_path);
+ return GIT_ERROR;
+ }
+
+ if (!git_fs_path_exists(wt->commondir_path)) {
+ git_error_set(GIT_ERROR_WORKTREE,
+ "worktree common directory ('%s') does not exist ",
+ wt->commondir_path);
+ return GIT_ERROR;
+ }
+
+ if (!git_fs_path_exists(wt->worktree_path)) {
+ git_error_set(GIT_ERROR_WORKTREE,
+ "worktree directory '%s' does not exist",
+ wt->worktree_path);
+ return GIT_ERROR;
+ }
+
+ return 0;
+}
+
+int git_worktree_add_options_init(git_worktree_add_options *opts,
+ unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(opts, version,
+ git_worktree_add_options, GIT_WORKTREE_ADD_OPTIONS_INIT);
+ return 0;
+}
+
+#ifndef GIT_DEPRECATE_HARD
+int git_worktree_add_init_options(git_worktree_add_options *opts,
+ unsigned int version)
+{
+ return git_worktree_add_options_init(opts, version);
+}
+#endif
+
+int git_worktree_add(git_worktree **out, git_repository *repo,
+ const char *name, const char *worktree,
+ const git_worktree_add_options *opts)
+{
+ git_str gitdir = GIT_STR_INIT, wddir = GIT_STR_INIT, buf = GIT_STR_INIT;
+ git_reference *ref = NULL, *head = NULL;
+ git_commit *commit = NULL;
+ git_repository *wt = NULL;
+ git_checkout_options coopts;
+ git_worktree_add_options wtopts = GIT_WORKTREE_ADD_OPTIONS_INIT;
+ int err;
+
+ GIT_ERROR_CHECK_VERSION(
+ opts, GIT_WORKTREE_ADD_OPTIONS_VERSION, "git_worktree_add_options");
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(repo);
+ GIT_ASSERT_ARG(name);
+ GIT_ASSERT_ARG(worktree);
+
+ *out = NULL;
+
+ if (opts)
+ memcpy(&wtopts, opts, sizeof(wtopts));
+
+ memcpy(&coopts, &wtopts.checkout_options, sizeof(coopts));
+
+ if (wtopts.ref) {
+ if (!git_reference_is_branch(wtopts.ref)) {
+ git_error_set(GIT_ERROR_WORKTREE, "reference is not a branch");
+ err = -1;
+ goto out;
+ }
+
+ if (git_branch_is_checked_out(wtopts.ref)) {
+ git_error_set(GIT_ERROR_WORKTREE, "reference is already checked out");
+ err = -1;
+ goto out;
+ }
+ }
+
+ /* Create gitdir directory ".git/worktrees/<name>" */
+ if ((err = git_str_joinpath(&gitdir, repo->commondir, "worktrees")) < 0)
+ goto out;
+ if (!git_fs_path_exists(gitdir.ptr))
+ if ((err = git_futils_mkdir(gitdir.ptr, 0755, GIT_MKDIR_EXCL)) < 0)
+ goto out;
+ if ((err = git_str_joinpath(&gitdir, gitdir.ptr, name)) < 0)
+ goto out;
+ if ((err = git_futils_mkdir(gitdir.ptr, 0755, GIT_MKDIR_EXCL)) < 0)
+ goto out;
+ if ((err = git_fs_path_prettify_dir(&gitdir, gitdir.ptr, NULL)) < 0)
+ goto out;
+
+ /* Create worktree work dir */
+ if ((err = git_futils_mkdir(worktree, 0755, GIT_MKDIR_EXCL)) < 0)
+ goto out;
+ if ((err = git_fs_path_prettify_dir(&wddir, worktree, NULL)) < 0)
+ goto out;
+
+ if (wtopts.lock) {
+ int fd;
+
+ if ((err = git_str_joinpath(&buf, gitdir.ptr, "locked")) < 0)
+ goto out;
+
+ if ((fd = p_creat(buf.ptr, 0644)) < 0) {
+ err = fd;
+ goto out;
+ }
+
+ p_close(fd);
+ git_str_clear(&buf);
+ }
+
+ /* Create worktree .git file */
+ if ((err = git_str_printf(&buf, "gitdir: %s\n", gitdir.ptr)) < 0)
+ goto out;
+ if ((err = write_wtfile(wddir.ptr, ".git", &buf)) < 0)
+ goto out;
+
+ /* Create gitdir files */
+ if ((err = git_fs_path_prettify_dir(&buf, repo->commondir, NULL) < 0)
+ || (err = git_str_putc(&buf, '\n')) < 0
+ || (err = write_wtfile(gitdir.ptr, "commondir", &buf)) < 0)
+ goto out;
+ if ((err = git_str_joinpath(&buf, wddir.ptr, ".git")) < 0
+ || (err = git_str_putc(&buf, '\n')) < 0
+ || (err = write_wtfile(gitdir.ptr, "gitdir", &buf)) < 0)
+ goto out;
+
+ /* Set up worktree reference */
+ if (wtopts.ref) {
+ if ((err = git_reference_dup(&ref, wtopts.ref)) < 0)
+ goto out;
+ } else {
+ if ((err = git_repository_head(&head, repo)) < 0)
+ goto out;
+ if ((err = git_commit_lookup(&commit, repo, &head->target.oid)) < 0)
+ goto out;
+ if ((err = git_branch_create(&ref, repo, name, commit, false)) < 0)
+ goto out;
+ }
+
+ /* Set worktree's HEAD */
+ if ((err = git_repository_create_head(gitdir.ptr, git_reference_name(ref))) < 0)
+ goto out;
+ if ((err = git_repository_open(&wt, wddir.ptr)) < 0)
+ goto out;
+
+ /* Checkout worktree's HEAD */
+ if ((err = git_checkout_head(wt, &coopts)) < 0)
+ goto out;
+
+ /* Load result */
+ if ((err = git_worktree_lookup(out, repo, name)) < 0)
+ goto out;
+
+out:
+ git_str_dispose(&gitdir);
+ git_str_dispose(&wddir);
+ git_str_dispose(&buf);
+ git_reference_free(ref);
+ git_reference_free(head);
+ git_commit_free(commit);
+ git_repository_free(wt);
+
+ return err;
+}
+
+int git_worktree_lock(git_worktree *wt, const char *reason)
+{
+ git_str buf = GIT_STR_INIT, path = GIT_STR_INIT;
+ int error;
+
+ GIT_ASSERT_ARG(wt);
+
+ if ((error = git_worktree_is_locked(NULL, wt)) < 0)
+ goto out;
+ if (error) {
+ error = GIT_ELOCKED;
+ goto out;
+ }
+
+ if ((error = git_str_joinpath(&path, wt->gitdir_path, "locked")) < 0)
+ goto out;
+
+ if (reason)
+ git_str_attach_notowned(&buf, reason, strlen(reason));
+
+ if ((error = git_futils_writebuffer(&buf, path.ptr, O_CREAT|O_EXCL|O_WRONLY, 0644)) < 0)
+ goto out;
+
+ wt->locked = 1;
+
+out:
+ git_str_dispose(&path);
+
+ return error;
+}
+
+int git_worktree_unlock(git_worktree *wt)
+{
+ git_str path = GIT_STR_INIT;
+ int error;
+
+ GIT_ASSERT_ARG(wt);
+
+ if ((error = git_worktree_is_locked(NULL, wt)) < 0)
+ return error;
+ if (!error)
+ return 1;
+
+ if (git_str_joinpath(&path, wt->gitdir_path, "locked") < 0)
+ return -1;
+
+ if (p_unlink(path.ptr) != 0) {
+ git_str_dispose(&path);
+ return -1;
+ }
+
+ wt->locked = 0;
+
+ git_str_dispose(&path);
+
+ return 0;
+}
+
+static int git_worktree__is_locked(git_str *reason, const git_worktree *wt)
+{
+ git_str path = GIT_STR_INIT;
+ int error, locked;
+
+ GIT_ASSERT_ARG(wt);
+
+ if (reason)
+ git_str_clear(reason);
+
+ if ((error = git_str_joinpath(&path, wt->gitdir_path, "locked")) < 0)
+ goto out;
+ locked = git_fs_path_exists(path.ptr);
+ if (locked && reason &&
+ (error = git_futils_readbuffer(reason, path.ptr)) < 0)
+ goto out;
+
+ error = locked;
+out:
+ git_str_dispose(&path);
+
+ return error;
+}
+
+int git_worktree_is_locked(git_buf *reason, const git_worktree *wt)
+{
+ git_str str = GIT_STR_INIT;
+ int error = 0;
+
+ if (reason && (error = git_buf_tostr(&str, reason)) < 0)
+ return error;
+
+ error = git_worktree__is_locked(reason ? &str : NULL, wt);
+
+ if (error >= 0 && reason) {
+ if (git_buf_fromstr(reason, &str) < 0)
+ error = -1;
+ }
+
+ git_str_dispose(&str);
+ return error;
+}
+
+const char *git_worktree_name(const git_worktree *wt)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(wt, NULL);
+ return wt->name;
+}
+
+const char *git_worktree_path(const git_worktree *wt)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(wt, NULL);
+ return wt->worktree_path;
+}
+
+int git_worktree_prune_options_init(
+ git_worktree_prune_options *opts,
+ unsigned int version)
+{
+ GIT_INIT_STRUCTURE_FROM_TEMPLATE(opts, version,
+ git_worktree_prune_options, GIT_WORKTREE_PRUNE_OPTIONS_INIT);
+ return 0;
+}
+
+#ifndef GIT_DEPRECATE_HARD
+int git_worktree_prune_init_options(git_worktree_prune_options *opts,
+ unsigned int version)
+{
+ return git_worktree_prune_options_init(opts, version);
+}
+#endif
+
+int git_worktree_is_prunable(git_worktree *wt,
+ git_worktree_prune_options *opts)
+{
+ git_worktree_prune_options popts = GIT_WORKTREE_PRUNE_OPTIONS_INIT;
+ git_str path = GIT_STR_INIT;
+ int ret = 0;
+
+ GIT_ERROR_CHECK_VERSION(
+ opts, GIT_WORKTREE_PRUNE_OPTIONS_VERSION,
+ "git_worktree_prune_options");
+
+ if (opts)
+ memcpy(&popts, opts, sizeof(popts));
+
+ if ((popts.flags & GIT_WORKTREE_PRUNE_LOCKED) == 0) {
+ git_str reason = GIT_STR_INIT;
+
+ if ((ret = git_worktree__is_locked(&reason, wt)) < 0)
+ goto out;
+
+ if (ret) {
+ git_error_set(GIT_ERROR_WORKTREE,
+ "not pruning locked working tree: '%s'",
+ reason.size ? reason.ptr : "is locked");
+
+ git_str_dispose(&reason);
+ ret = 0;
+ goto out;
+ }
+ }
+
+ if ((popts.flags & GIT_WORKTREE_PRUNE_VALID) == 0 &&
+ git_worktree_validate(wt) == 0) {
+ git_error_set(GIT_ERROR_WORKTREE, "not pruning valid working tree");
+ goto out;
+ }
+
+ if ((ret = git_str_printf(&path, "%s/worktrees/%s", wt->commondir_path, wt->name) < 0))
+ goto out;
+
+ if (!git_fs_path_exists(path.ptr)) {
+ git_error_set(GIT_ERROR_WORKTREE, "worktree gitdir ('%s') does not exist", path.ptr);
+ goto out;
+ }
+
+ ret = 1;
+
+out:
+ git_str_dispose(&path);
+ return ret;
+}
+
+int git_worktree_prune(git_worktree *wt,
+ git_worktree_prune_options *opts)
+{
+ git_worktree_prune_options popts = GIT_WORKTREE_PRUNE_OPTIONS_INIT;
+ git_str path = GIT_STR_INIT;
+ char *wtpath;
+ int err;
+
+ GIT_ERROR_CHECK_VERSION(
+ opts, GIT_WORKTREE_PRUNE_OPTIONS_VERSION,
+ "git_worktree_prune_options");
+
+ if (opts)
+ memcpy(&popts, opts, sizeof(popts));
+
+ if (!git_worktree_is_prunable(wt, &popts)) {
+ err = -1;
+ goto out;
+ }
+
+ /* Delete gitdir in parent repository */
+ if ((err = git_str_join3(&path, '/', wt->commondir_path, "worktrees", wt->name)) < 0)
+ goto out;
+ if (!git_fs_path_exists(path.ptr))
+ {
+ git_error_set(GIT_ERROR_WORKTREE, "worktree gitdir '%s' does not exist", path.ptr);
+ err = -1;
+ goto out;
+ }
+ if ((err = git_futils_rmdir_r(path.ptr, NULL, GIT_RMDIR_REMOVE_FILES)) < 0)
+ goto out;
+
+ /* Skip deletion of the actual working tree if it does
+ * not exist or deletion was not requested */
+ if ((popts.flags & GIT_WORKTREE_PRUNE_WORKING_TREE) == 0 ||
+ !git_fs_path_exists(wt->gitlink_path))
+ {
+ goto out;
+ }
+
+ if ((wtpath = git_fs_path_dirname(wt->gitlink_path)) == NULL)
+ goto out;
+ git_str_attach(&path, wtpath, 0);
+ if (!git_fs_path_exists(path.ptr))
+ {
+ git_error_set(GIT_ERROR_WORKTREE, "working tree '%s' does not exist", path.ptr);
+ err = -1;
+ goto out;
+ }
+ if ((err = git_futils_rmdir_r(path.ptr, NULL, GIT_RMDIR_REMOVE_FILES)) < 0)
+ goto out;
+
+out:
+ git_str_dispose(&path);
+
+ return err;
+}
diff --git a/src/libgit2/worktree.h b/src/libgit2/worktree.h
new file mode 100644
index 0000000..587189f
--- /dev/null
+++ b/src/libgit2/worktree.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_worktree_h__
+#define INCLUDE_worktree_h__
+
+#include "common.h"
+
+#include "git2/common.h"
+#include "git2/worktree.h"
+
+struct git_worktree {
+ /* Name of the working tree. This is the name of the
+ * containing directory in the `$PARENT/.git/worktrees/`
+ * directory. */
+ char *name;
+
+ /* Path to the where the worktree lives in the filesystem */
+ char *worktree_path;
+ /* Path to the .git file in the working tree's repository */
+ char *gitlink_path;
+ /* Path to the .git directory inside the parent's
+ * worktrees directory */
+ char *gitdir_path;
+ /* Path to the common directory contained in the parent
+ * repository */
+ char *commondir_path;
+ /* Path to the parent's working directory */
+ char *parent_path;
+
+ unsigned int locked:1;
+};
+
+char *git_worktree__read_link(const char *base, const char *file);
+
+#endif
diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt
new file mode 100644
index 0000000..ee35eb9
--- /dev/null
+++ b/src/util/CMakeLists.txt
@@ -0,0 +1,79 @@
+# util: a shared library for common utility functions for libgit2 projects
+
+add_library(util OBJECT)
+set_target_properties(util PROPERTIES C_STANDARD 90)
+set_target_properties(util PROPERTIES C_EXTENSIONS OFF)
+
+configure_file(git2_features.h.in git2_features.h)
+
+set(UTIL_INCLUDES
+ "${PROJECT_BINARY_DIR}/src/util"
+ "${PROJECT_BINARY_DIR}/include"
+ "${PROJECT_SOURCE_DIR}/src/util"
+ "${PROJECT_SOURCE_DIR}/include")
+
+file(GLOB UTIL_SRC *.c *.h allocators/*.c allocators/*.h hash.h)
+list(SORT UTIL_SRC)
+
+#
+# Platform specific sources
+#
+
+if(WIN32 AND NOT CYGWIN)
+ file(GLOB UTIL_SRC_OS win32/*.c win32/*.h)
+ list(SORT UTIL_SRC_OS)
+elseif(NOT AMIGA)
+ file(GLOB UTIL_SRC_OS unix/*.c unix/*.h)
+ list(SORT UTIL_SRC_OS)
+endif()
+
+#
+# Hash backend selection
+#
+
+if(USE_SHA1 STREQUAL "CollisionDetection")
+ file(GLOB UTIL_SRC_SHA1 hash/collisiondetect.* hash/sha1dc/*)
+ target_compile_definitions(util PRIVATE SHA1DC_NO_STANDARD_INCLUDES=1)
+ target_compile_definitions(util PRIVATE SHA1DC_CUSTOM_INCLUDE_SHA1_C=\"git2_util.h\")
+ target_compile_definitions(util PRIVATE SHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C=\"git2_util.h\")
+elseif(USE_SHA1 STREQUAL "OpenSSL" OR USE_SHA1 STREQUAL "OpenSSL-Dynamic")
+ add_definitions(-DOPENSSL_API_COMPAT=0x10100000L)
+ file(GLOB UTIL_SRC_SHA1 hash/openssl.*)
+elseif(USE_SHA1 STREQUAL "CommonCrypto")
+ file(GLOB UTIL_SRC_SHA1 hash/common_crypto.*)
+elseif(USE_SHA1 STREQUAL "mbedTLS")
+ file(GLOB UTIL_SRC_SHA1 hash/mbedtls.*)
+elseif(USE_SHA1 STREQUAL "Win32")
+ file(GLOB UTIL_SRC_SHA1 hash/win32.*)
+else()
+ message(FATAL_ERROR "Asked for unknown SHA1 backend: ${USE_SHA1}")
+endif()
+
+list(SORT UTIL_SRC_SHA1)
+
+if(USE_SHA256 STREQUAL "Builtin")
+ file(GLOB UTIL_SRC_SHA256 hash/builtin.* hash/rfc6234/*)
+elseif(USE_SHA256 STREQUAL "OpenSSL" OR USE_SHA256 STREQUAL "OpenSSL-Dynamic")
+ add_definitions(-DOPENSSL_API_COMPAT=0x10100000L)
+ file(GLOB UTIL_SRC_SHA256 hash/openssl.*)
+elseif(USE_SHA256 STREQUAL "CommonCrypto")
+ file(GLOB UTIL_SRC_SHA256 hash/common_crypto.*)
+elseif(USE_SHA256 STREQUAL "mbedTLS")
+ file(GLOB UTIL_SRC_SHA256 hash/mbedtls.*)
+elseif(USE_SHA256 STREQUAL "Win32")
+ file(GLOB UTIL_SRC_SHA256 hash/win32.*)
+else()
+ message(FATAL_ERROR "Asked for unknown SHA256 backend: ${USE_SHA256}")
+endif()
+
+list(SORT UTIL_SRC_SHA256)
+
+#
+# Build the library
+#
+
+target_sources(util PRIVATE ${UTIL_SRC} ${UTIL_SRC_OS} ${UTIL_SRC_SHA1} ${UTIL_SRC_SHA256})
+ide_split_sources(util)
+
+target_include_directories(util PRIVATE ${UTIL_INCLUDES} ${LIBGIT2_DEPENDENCY_INCLUDES} PUBLIC ${libgit2_SOURCE_DIR}/include)
+target_include_directories(util SYSTEM PRIVATE ${LIBGIT2_SYSTEM_INCLUDES})
diff --git a/src/util/alloc.c b/src/util/alloc.c
new file mode 100644
index 0000000..6ec173d
--- /dev/null
+++ b/src/util/alloc.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "alloc.h"
+#include "runtime.h"
+
+#include "allocators/failalloc.h"
+#include "allocators/stdalloc.h"
+#include "allocators/win32_leakcheck.h"
+
+/* Fail any allocation until git_libgit2_init is called. */
+git_allocator git__allocator = {
+ git_failalloc_malloc,
+ git_failalloc_realloc,
+ git_failalloc_free
+};
+
+void *git__calloc(size_t nelem, size_t elsize)
+{
+ size_t newsize;
+ void *ptr;
+
+ if (GIT_MULTIPLY_SIZET_OVERFLOW(&newsize, nelem, elsize))
+ return NULL;
+
+ if ((ptr = git__malloc(newsize)))
+ memset(ptr, 0, newsize);
+
+ return ptr;
+}
+
+void *git__reallocarray(void *ptr, size_t nelem, size_t elsize)
+{
+ size_t newsize;
+
+ if (GIT_MULTIPLY_SIZET_OVERFLOW(&newsize, nelem, elsize))
+ return NULL;
+
+ return git__realloc(ptr, newsize);
+}
+
+void *git__mallocarray(size_t nelem, size_t elsize)
+{
+ return git__reallocarray(NULL, nelem, elsize);
+}
+
+char *git__strdup(const char *str)
+{
+ size_t len = strlen(str) + 1;
+ void *ptr = git__malloc(len);
+
+ if (ptr)
+ memcpy(ptr, str, len);
+
+ return ptr;
+}
+
+char *git__strndup(const char *str, size_t n)
+{
+ size_t len = p_strnlen(str, n);
+ char *ptr = git__malloc(len + 1);
+
+ if (ptr) {
+ memcpy(ptr, str, len);
+ ptr[len] = '\0';
+ }
+
+ return ptr;
+}
+
+char *git__substrdup(const char *str, size_t n)
+{
+ char *ptr = git__malloc(n + 1);
+
+ if (ptr) {
+ memcpy(ptr, str, n);
+ ptr[n] = '\0';
+ }
+
+ return ptr;
+}
+
+static int setup_default_allocator(void)
+{
+#if defined(GIT_WIN32_LEAKCHECK)
+ return git_win32_leakcheck_init_allocator(&git__allocator);
+#else
+ return git_stdalloc_init_allocator(&git__allocator);
+#endif
+}
+
+int git_allocator_global_init(void)
+{
+ /*
+ * We don't want to overwrite any allocator which has been set
+ * before the init function is called.
+ */
+ if (git__allocator.gmalloc != git_failalloc_malloc)
+ return 0;
+
+ return setup_default_allocator();
+}
+
+int git_allocator_setup(git_allocator *allocator)
+{
+ if (!allocator)
+ return setup_default_allocator();
+
+ memcpy(&git__allocator, allocator, sizeof(*allocator));
+ return 0;
+}
diff --git a/src/util/alloc.h b/src/util/alloc.h
new file mode 100644
index 0000000..32b614b
--- /dev/null
+++ b/src/util/alloc.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_alloc_h__
+#define INCLUDE_alloc_h__
+
+#include "git2/sys/alloc.h"
+
+#include "git2_util.h"
+
+extern git_allocator git__allocator;
+
+GIT_INLINE(void *) git__malloc(size_t len)
+{
+ void *p = git__allocator.gmalloc(len, __FILE__, __LINE__);
+
+ if (!p)
+ git_error_set_oom();
+
+ return p;
+}
+
+GIT_INLINE(void *) git__realloc(void *ptr, size_t size)
+{
+ void *p = git__allocator.grealloc(ptr, size, __FILE__, __LINE__);
+
+ if (!p)
+ git_error_set_oom();
+
+ return p;
+}
+
+GIT_INLINE(void) git__free(void *ptr)
+{
+ git__allocator.gfree(ptr);
+}
+
+extern void *git__calloc(size_t nelem, size_t elsize);
+extern void *git__mallocarray(size_t nelem, size_t elsize);
+extern void *git__reallocarray(void *ptr, size_t nelem, size_t elsize);
+
+extern char *git__strdup(const char *str);
+extern char *git__strndup(const char *str, size_t n);
+extern char *git__substrdup(const char *str, size_t n);
+
+/**
+ * This function is being called by our global setup routines to
+ * initialize the standard allocator.
+ */
+int git_allocator_global_init(void);
+
+/**
+ * Switch out libgit2's global memory allocator
+ *
+ * @param allocator The new allocator that should be used. All function pointers
+ * of it need to be set correctly.
+ * @return An error code or 0.
+ */
+int git_allocator_setup(git_allocator *allocator);
+
+#endif
diff --git a/src/util/allocators/failalloc.c b/src/util/allocators/failalloc.c
new file mode 100644
index 0000000..c1025e3
--- /dev/null
+++ b/src/util/allocators/failalloc.c
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "failalloc.h"
+
+void *git_failalloc_malloc(size_t len, const char *file, int line)
+{
+ GIT_UNUSED(len);
+ GIT_UNUSED(file);
+ GIT_UNUSED(line);
+
+ return NULL;
+}
+
+void *git_failalloc_realloc(void *ptr, size_t size, const char *file, int line)
+{
+ GIT_UNUSED(ptr);
+ GIT_UNUSED(size);
+ GIT_UNUSED(file);
+ GIT_UNUSED(line);
+
+ return NULL;
+}
+
+void git_failalloc_free(void *ptr)
+{
+ GIT_UNUSED(ptr);
+}
diff --git a/src/util/allocators/failalloc.h b/src/util/allocators/failalloc.h
new file mode 100644
index 0000000..a3788e6
--- /dev/null
+++ b/src/util/allocators/failalloc.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_allocators_failalloc_h__
+#define INCLUDE_allocators_failalloc_h__
+
+#include "git2_util.h"
+
+extern void *git_failalloc_malloc(size_t len, const char *file, int line);
+extern void *git_failalloc_realloc(void *ptr, size_t size, const char *file, int line);
+extern void git_failalloc_free(void *ptr);
+
+#endif
diff --git a/src/util/allocators/stdalloc.c b/src/util/allocators/stdalloc.c
new file mode 100644
index 0000000..f2d72a7
--- /dev/null
+++ b/src/util/allocators/stdalloc.c
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "stdalloc.h"
+
+static void *stdalloc__malloc(size_t len, const char *file, int line)
+{
+ GIT_UNUSED(file);
+ GIT_UNUSED(line);
+
+#ifdef GIT_DEBUG_STRICT_ALLOC
+ if (!len)
+ return NULL;
+#endif
+
+ return malloc(len);
+}
+
+static void *stdalloc__realloc(void *ptr, size_t size, const char *file, int line)
+{
+ GIT_UNUSED(file);
+ GIT_UNUSED(line);
+
+#ifdef GIT_DEBUG_STRICT_ALLOC
+ if (!size)
+ return NULL;
+#endif
+
+ return realloc(ptr, size);
+}
+
+static void stdalloc__free(void *ptr)
+{
+ free(ptr);
+}
+
+int git_stdalloc_init_allocator(git_allocator *allocator)
+{
+ allocator->gmalloc = stdalloc__malloc;
+ allocator->grealloc = stdalloc__realloc;
+ allocator->gfree = stdalloc__free;
+ return 0;
+}
diff --git a/src/util/allocators/stdalloc.h b/src/util/allocators/stdalloc.h
new file mode 100644
index 0000000..955038c
--- /dev/null
+++ b/src/util/allocators/stdalloc.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_allocators_stdalloc_h__
+#define INCLUDE_allocators_stdalloc_h__
+
+#include "git2_util.h"
+
+#include "alloc.h"
+
+int git_stdalloc_init_allocator(git_allocator *allocator);
+
+#endif
diff --git a/src/util/allocators/win32_leakcheck.c b/src/util/allocators/win32_leakcheck.c
new file mode 100644
index 0000000..cdf16d3
--- /dev/null
+++ b/src/util/allocators/win32_leakcheck.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "win32_leakcheck.h"
+
+#if defined(GIT_WIN32_LEAKCHECK)
+
+#include "win32/w32_leakcheck.h"
+
+static void *leakcheck_malloc(size_t len, const char *file, int line)
+{
+ void *ptr = _malloc_dbg(len, _NORMAL_BLOCK, git_win32_leakcheck_stacktrace(1,file), line);
+ if (!ptr) git_error_set_oom();
+ return ptr;
+}
+
+static void *leakcheck_realloc(void *ptr, size_t size, const char *file, int line)
+{
+ void *new_ptr = _realloc_dbg(ptr, size, _NORMAL_BLOCK, git_win32_leakcheck_stacktrace(1,file), line);
+ if (!new_ptr) git_error_set_oom();
+ return new_ptr;
+}
+
+static void leakcheck_free(void *ptr)
+{
+ free(ptr);
+}
+
+int git_win32_leakcheck_init_allocator(git_allocator *allocator)
+{
+ allocator->gmalloc = leakcheck_malloc;
+ allocator->grealloc = leakcheck_realloc;
+ allocator->gfree = leakcheck_free;
+ return 0;
+}
+
+#else
+
+int git_win32_leakcheck_init_allocator(git_allocator *allocator)
+{
+ GIT_UNUSED(allocator);
+ git_error_set(GIT_EINVALID, "leakcheck memory allocator not available");
+ return -1;
+}
+
+#endif
diff --git a/src/util/allocators/win32_leakcheck.h b/src/util/allocators/win32_leakcheck.h
new file mode 100644
index 0000000..edcd930
--- /dev/null
+++ b/src/util/allocators/win32_leakcheck.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_allocators_win32_leakcheck_h
+#define INCLUDE_allocators_win32_leakcheck_h
+
+#include "git2_util.h"
+
+#include "alloc.h"
+
+int git_win32_leakcheck_init_allocator(git_allocator *allocator);
+
+#endif
diff --git a/src/util/array.h b/src/util/array.h
new file mode 100644
index 0000000..633d598
--- /dev/null
+++ b/src/util/array.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_array_h__
+#define INCLUDE_array_h__
+
+#include "git2_util.h"
+
+/*
+ * Use this to declare a typesafe resizable array of items, a la:
+ *
+ * git_array_t(int) my_ints = GIT_ARRAY_INIT;
+ * ...
+ * int *i = git_array_alloc(my_ints);
+ * GIT_ERROR_CHECK_ALLOC(i);
+ * ...
+ * git_array_clear(my_ints);
+ *
+ * You may also want to do things like:
+ *
+ * typedef git_array_t(my_struct) my_struct_array_t;
+ */
+#define git_array_t(type) struct { type *ptr; size_t size, asize; }
+
+#define GIT_ARRAY_INIT { NULL, 0, 0 }
+
+#define git_array_init(a) \
+ do { (a).size = (a).asize = 0; (a).ptr = NULL; } while (0)
+
+#define git_array_init_to_size(a, desired) \
+ do { (a).size = 0; (a).asize = desired; (a).ptr = git__calloc(desired, sizeof(*(a).ptr)); } while (0)
+
+#define git_array_dispose(a) \
+ do { git__free((a).ptr); } while (0)
+
+#define git_array_clear(a) \
+ do { git__free((a).ptr); git_array_init(a); } while (0)
+
+#define GIT_ERROR_CHECK_ARRAY(a) GIT_ERROR_CHECK_ALLOC((a).ptr)
+
+
+typedef git_array_t(char) git_array_generic_t;
+
+/* use a generic array for growth, return 0 on success */
+GIT_INLINE(int) git_array_grow(void *_a, size_t item_size)
+{
+ volatile git_array_generic_t *a = _a;
+ size_t new_size;
+ char *new_array;
+
+ if (a->size < 8) {
+ new_size = 8;
+ } else {
+ if (GIT_MULTIPLY_SIZET_OVERFLOW(&new_size, a->size, 3))
+ goto on_oom;
+ new_size /= 2;
+ }
+
+ if ((new_array = git__reallocarray(a->ptr, new_size, item_size)) == NULL)
+ goto on_oom;
+
+ a->ptr = new_array;
+ a->asize = new_size;
+ return 0;
+
+on_oom:
+ git_array_clear(*a);
+ return -1;
+}
+
+#define git_array_alloc(a) \
+ (((a).size < (a).asize || git_array_grow(&(a), sizeof(*(a).ptr)) == 0) ? \
+ &(a).ptr[(a).size++] : (void *)NULL)
+
+#define git_array_last(a) ((a).size ? &(a).ptr[(a).size - 1] : (void *)NULL)
+
+#define git_array_pop(a) ((a).size ? &(a).ptr[--(a).size] : (void *)NULL)
+
+#define git_array_get(a, i) (((i) < (a).size) ? &(a).ptr[(i)] : (void *)NULL)
+
+#define git_array_size(a) (a).size
+
+#define git_array_valid_index(a, i) ((i) < (a).size)
+
+#define git_array_foreach(a, i, element) \
+ for ((i) = 0; (i) < (a).size && ((element) = &(a).ptr[(i)]); (i)++)
+
+typedef int (*git_array_compare_cb)(const void *, const void *);
+
+GIT_INLINE(int) git_array__search(
+ size_t *out,
+ void *array_ptr,
+ size_t item_size,
+ size_t array_len,
+ git_array_compare_cb compare,
+ const void *key)
+{
+ size_t lim;
+ unsigned char *part, *array = array_ptr, *base = array_ptr;
+ int cmp = -1;
+
+ for (lim = array_len; lim != 0; lim >>= 1) {
+ part = base + (lim >> 1) * item_size;
+ cmp = (*compare)(key, part);
+
+ if (cmp == 0) {
+ base = part;
+ break;
+ }
+ if (cmp > 0) { /* key > p; take right partition */
+ base = part + 1 * item_size;
+ lim--;
+ } /* else take left partition */
+ }
+
+ if (out)
+ *out = (base - array) / item_size;
+
+ return (cmp == 0) ? 0 : GIT_ENOTFOUND;
+}
+
+#define git_array_search(out, a, cmp, key) \
+ git_array__search(out, (a).ptr, sizeof(*(a).ptr), (a).size, \
+ (cmp), (key))
+
+#endif
diff --git a/src/util/assert_safe.h b/src/util/assert_safe.h
new file mode 100644
index 0000000..cc0bac5
--- /dev/null
+++ b/src/util/assert_safe.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_assert_safe_h__
+#define INCLUDE_assert_safe_h__
+
+/*
+ * In a debug build, we'll assert(3) for aide in debugging. In release
+ * builds, we will provide macros that will set an error message that
+ * indicate a failure and return. Note that memory leaks can occur in
+ * a release-mode assertion failure -- it is impractical to provide
+ * safe clean up routines in these very extreme failures, but care
+ * should be taken to not leak very large objects.
+ */
+
+#if (defined(_DEBUG) || defined(GIT_ASSERT_HARD)) && GIT_ASSERT_HARD != 0
+# include <assert.h>
+
+# define GIT_ASSERT(expr) assert(expr)
+# define GIT_ASSERT_ARG(expr) assert(expr)
+
+# define GIT_ASSERT_WITH_RETVAL(expr, fail) assert(expr)
+# define GIT_ASSERT_ARG_WITH_RETVAL(expr, fail) assert(expr)
+
+# define GIT_ASSERT_WITH_CLEANUP(expr, cleanup) assert(expr)
+#else
+
+/** Internal consistency check to stop the function. */
+# define GIT_ASSERT(expr) GIT_ASSERT_WITH_RETVAL(expr, -1)
+
+/**
+ * Assert that a consumer-provided argument is valid, setting an
+ * actionable error message and returning -1 if it is not.
+ */
+# define GIT_ASSERT_ARG(expr) GIT_ASSERT_ARG_WITH_RETVAL(expr, -1)
+
+/** Internal consistency check to return the `fail` param on failure. */
+# define GIT_ASSERT_WITH_RETVAL(expr, fail) \
+ GIT_ASSERT__WITH_RETVAL(expr, GIT_ERROR_INTERNAL, "unrecoverable internal error", fail)
+
+/**
+ * Assert that a consumer-provided argument is valid, setting an
+ * actionable error message and returning the `fail` param if not.
+ */
+# define GIT_ASSERT_ARG_WITH_RETVAL(expr, fail) \
+ GIT_ASSERT__WITH_RETVAL(expr, GIT_ERROR_INVALID, "invalid argument", fail)
+
+# define GIT_ASSERT__WITH_RETVAL(expr, code, msg, fail) do { \
+ if (!(expr)) { \
+ git_error_set(code, "%s: '%s'", msg, #expr); \
+ return fail; \
+ } \
+ } while(0)
+
+/**
+ * Go to to the given label on assertion failures; useful when you have
+ * taken a lock or otherwise need to release a resource.
+ */
+# define GIT_ASSERT_WITH_CLEANUP(expr, cleanup) \
+ GIT_ASSERT__WITH_CLEANUP(expr, GIT_ERROR_INTERNAL, "unrecoverable internal error", cleanup)
+
+# define GIT_ASSERT__WITH_CLEANUP(expr, code, msg, cleanup) do { \
+ if (!(expr)) { \
+ git_error_set(code, "%s: '%s'", msg, #expr); \
+ cleanup; \
+ } \
+ } while(0)
+
+#endif /* GIT_ASSERT_HARD */
+
+#endif
diff --git a/src/util/bitvec.h b/src/util/bitvec.h
new file mode 100644
index 0000000..544832d
--- /dev/null
+++ b/src/util/bitvec.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_bitvec_h__
+#define INCLUDE_bitvec_h__
+
+#include "common.h"
+
+/*
+ * This is a silly little fixed length bit vector type that will store
+ * vectors of 64 bits or less directly in the structure and allocate
+ * memory for vectors longer than 64 bits. You can use the two versions
+ * transparently through the API and avoid heap allocation completely when
+ * using a short bit vector as a result.
+ */
+typedef struct {
+ size_t length;
+ union {
+ uint64_t *words;
+ uint64_t bits;
+ } u;
+} git_bitvec;
+
+GIT_INLINE(int) git_bitvec_init(git_bitvec *bv, size_t capacity)
+{
+ memset(bv, 0x0, sizeof(*bv));
+
+ if (capacity >= 64) {
+ bv->length = (capacity / 64) + 1;
+ bv->u.words = git__calloc(bv->length, sizeof(uint64_t));
+ if (!bv->u.words)
+ return -1;
+ }
+
+ return 0;
+}
+
+#define GIT_BITVEC_MASK(BIT) ((uint64_t)1 << (BIT % 64))
+#define GIT_BITVEC_WORD(BV, BIT) (BV->length ? &BV->u.words[BIT / 64] : &BV->u.bits)
+
+GIT_INLINE(void) git_bitvec_set(git_bitvec *bv, size_t bit, bool on)
+{
+ uint64_t *word = GIT_BITVEC_WORD(bv, bit);
+ uint64_t mask = GIT_BITVEC_MASK(bit);
+
+ if (on)
+ *word |= mask;
+ else
+ *word &= ~mask;
+}
+
+GIT_INLINE(bool) git_bitvec_get(git_bitvec *bv, size_t bit)
+{
+ uint64_t *word = GIT_BITVEC_WORD(bv, bit);
+ return (*word & GIT_BITVEC_MASK(bit)) != 0;
+}
+
+GIT_INLINE(void) git_bitvec_clear(git_bitvec *bv)
+{
+ if (!bv->length)
+ bv->u.bits = 0;
+ else
+ memset(bv->u.words, 0x0, bv->length * sizeof(uint64_t));
+}
+
+GIT_INLINE(void) git_bitvec_free(git_bitvec *bv)
+{
+ if (bv->length)
+ git__free(bv->u.words);
+}
+
+#endif
diff --git a/src/util/cc-compat.h b/src/util/cc-compat.h
new file mode 100644
index 0000000..ede6e9a
--- /dev/null
+++ b/src/util/cc-compat.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_cc_compat_h__
+#define INCLUDE_cc_compat_h__
+
+#include <stdarg.h>
+
+/*
+ * See if our compiler is known to support flexible array members.
+ */
+#ifndef GIT_FLEX_ARRAY
+# if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)
+# define GIT_FLEX_ARRAY /* empty */
+# elif defined(__GNUC__)
+# if (__GNUC__ >= 3)
+# define GIT_FLEX_ARRAY /* empty */
+# else
+# define GIT_FLEX_ARRAY 0 /* older GNU extension */
+# endif
+# endif
+
+/* Default to safer but a bit wasteful traditional style */
+# ifndef GIT_FLEX_ARRAY
+# define GIT_FLEX_ARRAY 1
+# endif
+#endif
+
+#if defined(__GNUC__)
+# define GIT_ALIGN(x,size) x __attribute__ ((aligned(size)))
+#elif defined(_MSC_VER)
+# define GIT_ALIGN(x,size) __declspec(align(size)) x
+#else
+# define GIT_ALIGN(x,size) x
+#endif
+
+#if defined(__GNUC__)
+# define GIT_UNUSED(x) \
+ do { \
+ __typeof__(x) _unused __attribute__((unused)); \
+ _unused = (x); \
+ } while (0)
+# define GIT_UNUSED_ARG __attribute__((unused))
+#else
+# define GIT_UNUSED(x) ((void)(x))
+# define GIT_UNUSED_ARG
+#endif
+
+/* Define the printf format specifier to use for size_t output */
+#if defined(_MSC_VER) || defined(__MINGW32__)
+
+/* Visual Studio 2012 and prior lack PRId64 entirely */
+# ifndef PRId64
+# define PRId64 "I64d"
+# endif
+
+/* The first block is needed to avoid warnings on MingW amd64 */
+# if (SIZE_MAX == ULLONG_MAX)
+# define PRIuZ "I64u"
+# define PRIxZ "I64x"
+# define PRIXZ "I64X"
+# define PRIdZ "I64d"
+# else
+# define PRIuZ "Iu"
+# define PRIxZ "Ix"
+# define PRIXZ "IX"
+# define PRIdZ "Id"
+# endif
+
+#else
+# define PRIuZ "zu"
+# define PRIxZ "zx"
+# define PRIXZ "zX"
+# define PRIdZ "zd"
+#endif
+
+/* Microsoft Visual C/C++ */
+#if defined(_MSC_VER)
+/* disable "deprecated function" warnings */
+# pragma warning ( disable : 4996 )
+/* disable "conditional expression is constant" level 4 warnings */
+# pragma warning ( disable : 4127 )
+#endif
+
+#if defined (_MSC_VER)
+ typedef unsigned char bool;
+# ifndef true
+# define true 1
+# endif
+# ifndef false
+# define false 0
+# endif
+#else
+# include <stdbool.h>
+#endif
+
+#ifndef va_copy
+# ifdef __va_copy
+# define va_copy(dst, src) __va_copy(dst, src)
+# else
+# define va_copy(dst, src) ((dst) = (src))
+# endif
+#endif
+
+#endif
diff --git a/src/util/date.c b/src/util/date.c
new file mode 100644
index 0000000..4d757e2
--- /dev/null
+++ b/src/util/date.c
@@ -0,0 +1,899 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "git2_util.h"
+
+#ifndef GIT_WIN32
+#include <sys/time.h>
+#endif
+
+#include "util.h"
+#include "posix.h"
+#include "date.h"
+
+#include <ctype.h>
+#include <time.h>
+
+typedef enum {
+ DATE_NORMAL = 0,
+ DATE_RELATIVE,
+ DATE_SHORT,
+ DATE_LOCAL,
+ DATE_ISO8601,
+ DATE_RFC2822,
+ DATE_RAW
+} date_mode;
+
+/*
+ * This is like mktime, but without normalization of tm_wday and tm_yday.
+ */
+static git_time_t tm_to_time_t(const struct tm *tm)
+{
+ static const int mdays[] = {
+ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334
+ };
+ int year = tm->tm_year - 70;
+ int month = tm->tm_mon;
+ int day = tm->tm_mday;
+
+ if (year < 0 || year > 129) /* algo only works for 1970-2099 */
+ return -1;
+ if (month < 0 || month > 11) /* array bounds */
+ return -1;
+ if (month < 2 || (year + 2) % 4)
+ day--;
+ if (tm->tm_hour < 0 || tm->tm_min < 0 || tm->tm_sec < 0)
+ return -1;
+ return (year * 365 + (year + 1) / 4 + mdays[month] + day) * 24*60*60UL +
+ tm->tm_hour * 60*60 + tm->tm_min * 60 + tm->tm_sec;
+}
+
+static const char *month_names[] = {
+ "January", "February", "March", "April", "May", "June",
+ "July", "August", "September", "October", "November", "December"
+};
+
+static const char *weekday_names[] = {
+ "Sundays", "Mondays", "Tuesdays", "Wednesdays", "Thursdays", "Fridays", "Saturdays"
+};
+
+
+
+/*
+ * Check these. And note how it doesn't do the summer-time conversion.
+ *
+ * In my world, it's always summer, and things are probably a bit off
+ * in other ways too.
+ */
+static const struct {
+ const char *name;
+ int offset;
+ int dst;
+} timezone_names[] = {
+ { "IDLW", -12, 0, }, /* International Date Line West */
+ { "NT", -11, 0, }, /* Nome */
+ { "CAT", -10, 0, }, /* Central Alaska */
+ { "HST", -10, 0, }, /* Hawaii Standard */
+ { "HDT", -10, 1, }, /* Hawaii Daylight */
+ { "YST", -9, 0, }, /* Yukon Standard */
+ { "YDT", -9, 1, }, /* Yukon Daylight */
+ { "PST", -8, 0, }, /* Pacific Standard */
+ { "PDT", -8, 1, }, /* Pacific Daylight */
+ { "MST", -7, 0, }, /* Mountain Standard */
+ { "MDT", -7, 1, }, /* Mountain Daylight */
+ { "CST", -6, 0, }, /* Central Standard */
+ { "CDT", -6, 1, }, /* Central Daylight */
+ { "EST", -5, 0, }, /* Eastern Standard */
+ { "EDT", -5, 1, }, /* Eastern Daylight */
+ { "AST", -3, 0, }, /* Atlantic Standard */
+ { "ADT", -3, 1, }, /* Atlantic Daylight */
+ { "WAT", -1, 0, }, /* West Africa */
+
+ { "GMT", 0, 0, }, /* Greenwich Mean */
+ { "UTC", 0, 0, }, /* Universal (Coordinated) */
+ { "Z", 0, 0, }, /* Zulu, alias for UTC */
+
+ { "WET", 0, 0, }, /* Western European */
+ { "BST", 0, 1, }, /* British Summer */
+ { "CET", +1, 0, }, /* Central European */
+ { "MET", +1, 0, }, /* Middle European */
+ { "MEWT", +1, 0, }, /* Middle European Winter */
+ { "MEST", +1, 1, }, /* Middle European Summer */
+ { "CEST", +1, 1, }, /* Central European Summer */
+ { "MESZ", +1, 1, }, /* Middle European Summer */
+ { "FWT", +1, 0, }, /* French Winter */
+ { "FST", +1, 1, }, /* French Summer */
+ { "EET", +2, 0, }, /* Eastern Europe */
+ { "EEST", +2, 1, }, /* Eastern European Daylight */
+ { "WAST", +7, 0, }, /* West Australian Standard */
+ { "WADT", +7, 1, }, /* West Australian Daylight */
+ { "CCT", +8, 0, }, /* China Coast */
+ { "JST", +9, 0, }, /* Japan Standard */
+ { "EAST", +10, 0, }, /* Eastern Australian Standard */
+ { "EADT", +10, 1, }, /* Eastern Australian Daylight */
+ { "GST", +10, 0, }, /* Guam Standard */
+ { "NZT", +12, 0, }, /* New Zealand */
+ { "NZST", +12, 0, }, /* New Zealand Standard */
+ { "NZDT", +12, 1, }, /* New Zealand Daylight */
+ { "IDLE", +12, 0, }, /* International Date Line East */
+};
+
+static size_t match_string(const char *date, const char *str)
+{
+ size_t i = 0;
+
+ for (i = 0; *date; date++, str++, i++) {
+ if (*date == *str)
+ continue;
+ if (toupper(*date) == toupper(*str))
+ continue;
+ if (!isalnum(*date))
+ break;
+ return 0;
+ }
+ return i;
+}
+
+static int skip_alpha(const char *date)
+{
+ int i = 0;
+ do {
+ i++;
+ } while (isalpha(date[i]));
+ return i;
+}
+
+/*
+* Parse month, weekday, or timezone name
+*/
+static size_t match_alpha(const char *date, struct tm *tm, int *offset)
+{
+ unsigned int i;
+
+ for (i = 0; i < 12; i++) {
+ size_t match = match_string(date, month_names[i]);
+ if (match >= 3) {
+ tm->tm_mon = i;
+ return match;
+ }
+ }
+
+ for (i = 0; i < 7; i++) {
+ size_t match = match_string(date, weekday_names[i]);
+ if (match >= 3) {
+ tm->tm_wday = i;
+ return match;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(timezone_names); i++) {
+ size_t match = match_string(date, timezone_names[i].name);
+ if (match >= 3 || match == strlen(timezone_names[i].name)) {
+ int off = timezone_names[i].offset;
+
+ /* This is bogus, but we like summer */
+ off += timezone_names[i].dst;
+
+ /* Only use the tz name offset if we don't have anything better */
+ if (*offset == -1)
+ *offset = 60*off;
+
+ return match;
+ }
+ }
+
+ if (match_string(date, "PM") == 2) {
+ tm->tm_hour = (tm->tm_hour % 12) + 12;
+ return 2;
+ }
+
+ if (match_string(date, "AM") == 2) {
+ tm->tm_hour = (tm->tm_hour % 12) + 0;
+ return 2;
+ }
+
+ /* BAD */
+ return skip_alpha(date);
+}
+
+static int is_date(int year, int month, int day, struct tm *now_tm, time_t now, struct tm *tm)
+{
+ if (month > 0 && month < 13 && day > 0 && day < 32) {
+ struct tm check = *tm;
+ struct tm *r = (now_tm ? &check : tm);
+ git_time_t specified;
+
+ r->tm_mon = month - 1;
+ r->tm_mday = day;
+ if (year == -1) {
+ if (!now_tm)
+ return 1;
+ r->tm_year = now_tm->tm_year;
+ }
+ else if (year >= 1970 && year < 2100)
+ r->tm_year = year - 1900;
+ else if (year > 70 && year < 100)
+ r->tm_year = year;
+ else if (year < 38)
+ r->tm_year = year + 100;
+ else
+ return 0;
+ if (!now_tm)
+ return 1;
+
+ specified = tm_to_time_t(r);
+
+ /* Be it commit time or author time, it does not make
+ * sense to specify timestamp way into the future. Make
+ * sure it is not later than ten days from now...
+ */
+ if (now + 10*24*3600 < specified)
+ return 0;
+ tm->tm_mon = r->tm_mon;
+ tm->tm_mday = r->tm_mday;
+ if (year != -1)
+ tm->tm_year = r->tm_year;
+ return 1;
+ }
+ return 0;
+}
+
+static size_t match_multi_number(unsigned long num, char c, const char *date, char *end, struct tm *tm)
+{
+ time_t now;
+ struct tm now_tm;
+ struct tm *refuse_future;
+ long num2, num3;
+
+ num2 = strtol(end+1, &end, 10);
+ num3 = -1;
+ if (*end == c && isdigit(end[1]))
+ num3 = strtol(end+1, &end, 10);
+
+ /* Time? Date? */
+ switch (c) {
+ case ':':
+ if (num3 < 0)
+ num3 = 0;
+ if (num < 25 && num2 >= 0 && num2 < 60 && num3 >= 0 && num3 <= 60) {
+ tm->tm_hour = num;
+ tm->tm_min = num2;
+ tm->tm_sec = num3;
+ break;
+ }
+ return 0;
+
+ case '-':
+ case '/':
+ case '.':
+ now = time(NULL);
+ refuse_future = NULL;
+ if (p_gmtime_r(&now, &now_tm))
+ refuse_future = &now_tm;
+
+ if (num > 70) {
+ /* yyyy-mm-dd? */
+ if (is_date(num, num2, num3, refuse_future, now, tm))
+ break;
+ /* yyyy-dd-mm? */
+ if (is_date(num, num3, num2, refuse_future, now, tm))
+ break;
+ }
+ /* Our eastern European friends say dd.mm.yy[yy]
+ * is the norm there, so giving precedence to
+ * mm/dd/yy[yy] form only when separator is not '.'
+ */
+ if (c != '.' &&
+ is_date(num3, num, num2, refuse_future, now, tm))
+ break;
+ /* European dd.mm.yy[yy] or funny US dd/mm/yy[yy] */
+ if (is_date(num3, num2, num, refuse_future, now, tm))
+ break;
+ /* Funny European mm.dd.yy */
+ if (c == '.' &&
+ is_date(num3, num, num2, refuse_future, now, tm))
+ break;
+ return 0;
+ }
+ return end - date;
+}
+
+/*
+ * Have we filled in any part of the time/date yet?
+ * We just do a binary 'and' to see if the sign bit
+ * is set in all the values.
+ */
+static int nodate(struct tm *tm)
+{
+ return (tm->tm_year &
+ tm->tm_mon &
+ tm->tm_mday &
+ tm->tm_hour &
+ tm->tm_min &
+ tm->tm_sec) < 0;
+}
+
+/*
+ * We've seen a digit. Time? Year? Date?
+ */
+static size_t match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt)
+{
+ size_t n;
+ char *end;
+ unsigned long num;
+
+ num = strtoul(date, &end, 10);
+
+ /*
+ * Seconds since 1970? We trigger on that for any numbers with
+ * more than 8 digits. This is because we don't want to rule out
+ * numbers like 20070606 as a YYYYMMDD date.
+ */
+ if (num >= 100000000 && nodate(tm)) {
+ time_t time = num;
+ if (p_gmtime_r(&time, tm)) {
+ *tm_gmt = 1;
+ return end - date;
+ }
+ }
+
+ /*
+ * Check for special formats: num[-.:/]num[same]num
+ */
+ switch (*end) {
+ case ':':
+ case '.':
+ case '/':
+ case '-':
+ if (isdigit(end[1])) {
+ size_t match = match_multi_number(num, *end, date, end, tm);
+ if (match)
+ return match;
+ }
+ }
+
+ /*
+ * None of the special formats? Try to guess what
+ * the number meant. We use the number of digits
+ * to make a more educated guess..
+ */
+ n = 0;
+ do {
+ n++;
+ } while (isdigit(date[n]));
+
+ /* Four-digit year or a timezone? */
+ if (n == 4) {
+ if (num <= 1400 && *offset == -1) {
+ unsigned int minutes = num % 100;
+ unsigned int hours = num / 100;
+ *offset = hours*60 + minutes;
+ } else if (num > 1900 && num < 2100)
+ tm->tm_year = num - 1900;
+ return n;
+ }
+
+ /*
+ * Ignore lots of numerals. We took care of 4-digit years above.
+ * Days or months must be one or two digits.
+ */
+ if (n > 2)
+ return n;
+
+ /*
+ * NOTE! We will give precedence to day-of-month over month or
+ * year numbers in the 1-12 range. So 05 is always "mday 5",
+ * unless we already have a mday..
+ *
+ * IOW, 01 Apr 05 parses as "April 1st, 2005".
+ */
+ if (num > 0 && num < 32 && tm->tm_mday < 0) {
+ tm->tm_mday = num;
+ return n;
+ }
+
+ /* Two-digit year? */
+ if (n == 2 && tm->tm_year < 0) {
+ if (num < 10 && tm->tm_mday >= 0) {
+ tm->tm_year = num + 100;
+ return n;
+ }
+ if (num >= 70) {
+ tm->tm_year = num;
+ return n;
+ }
+ }
+
+ if (num > 0 && num < 13 && tm->tm_mon < 0)
+ tm->tm_mon = num-1;
+
+ return n;
+}
+
+static size_t match_tz(const char *date, int *offp)
+{
+ char *end;
+ int hour = strtoul(date + 1, &end, 10);
+ size_t n = end - (date + 1);
+ int min = 0;
+
+ if (n == 4) {
+ /* hhmm */
+ min = hour % 100;
+ hour = hour / 100;
+ } else if (n != 2) {
+ min = 99; /* random stuff */
+ } else if (*end == ':') {
+ /* hh:mm? */
+ min = strtoul(end + 1, &end, 10);
+ if (end - (date + 1) != 5)
+ min = 99; /* random stuff */
+ } /* otherwise we parsed "hh" */
+
+ /*
+ * Don't accept any random stuff. Even though some places have
+ * offset larger than 12 hours (e.g. Pacific/Kiritimati is at
+ * UTC+14), there is something wrong if hour part is much
+ * larger than that. We might also want to check that the
+ * minutes are divisible by 15 or something too. (Offset of
+ * Kathmandu, Nepal is UTC+5:45)
+ */
+ if (min < 60 && hour < 24) {
+ int offset = hour * 60 + min;
+ if (*date == '-')
+ offset = -offset;
+ *offp = offset;
+ }
+ return end - date;
+}
+
+/*
+ * Parse a string like "0 +0000" as ancient timestamp near epoch, but
+ * only when it appears not as part of any other string.
+ */
+static int match_object_header_date(const char *date, git_time_t *timestamp, int *offset)
+{
+ char *end;
+ unsigned long stamp;
+ int ofs;
+
+ if (*date < '0' || '9' <= *date)
+ return -1;
+ stamp = strtoul(date, &end, 10);
+ if (*end != ' ' || stamp == ULONG_MAX || (end[1] != '+' && end[1] != '-'))
+ return -1;
+ date = end + 2;
+ ofs = strtol(date, &end, 10);
+ if ((*end != '\0' && (*end != '\n')) || end != date + 4)
+ return -1;
+ ofs = (ofs / 100) * 60 + (ofs % 100);
+ if (date[-1] == '-')
+ ofs = -ofs;
+ *timestamp = stamp;
+ *offset = ofs;
+ return 0;
+}
+
+/* Gr. strptime is crap for this; it doesn't have a way to require RFC2822
+ (i.e. English) day/month names, and it doesn't work correctly with %z. */
+static int parse_date_basic(const char *date, git_time_t *timestamp, int *offset)
+{
+ struct tm tm;
+ int tm_gmt;
+ git_time_t dummy_timestamp;
+ int dummy_offset;
+
+ if (!timestamp)
+ timestamp = &dummy_timestamp;
+ if (!offset)
+ offset = &dummy_offset;
+
+ memset(&tm, 0, sizeof(tm));
+ tm.tm_year = -1;
+ tm.tm_mon = -1;
+ tm.tm_mday = -1;
+ tm.tm_isdst = -1;
+ tm.tm_hour = -1;
+ tm.tm_min = -1;
+ tm.tm_sec = -1;
+ *offset = -1;
+ tm_gmt = 0;
+
+ if (*date == '@' &&
+ !match_object_header_date(date + 1, timestamp, offset))
+ return 0; /* success */
+ for (;;) {
+ size_t match = 0;
+ unsigned char c = *date;
+
+ /* Stop at end of string or newline */
+ if (!c || c == '\n')
+ break;
+
+ if (isalpha(c))
+ match = match_alpha(date, &tm, offset);
+ else if (isdigit(c))
+ match = match_digit(date, &tm, offset, &tm_gmt);
+ else if ((c == '-' || c == '+') && isdigit(date[1]))
+ match = match_tz(date, offset);
+
+ if (!match) {
+ /* BAD */
+ match = 1;
+ }
+
+ date += match;
+ }
+
+ /* mktime uses local timezone */
+ *timestamp = tm_to_time_t(&tm);
+ if (*offset == -1)
+ *offset = (int)((time_t)*timestamp - mktime(&tm)) / 60;
+
+ if (*timestamp == (git_time_t)-1)
+ return -1;
+
+ if (!tm_gmt)
+ *timestamp -= *offset * 60;
+ return 0; /* success */
+}
+
+
+/*
+ * Relative time update (eg "2 days ago"). If we haven't set the time
+ * yet, we need to set it from current time.
+ */
+static git_time_t update_tm(struct tm *tm, struct tm *now, unsigned long sec)
+{
+ time_t n;
+
+ if (tm->tm_mday < 0)
+ tm->tm_mday = now->tm_mday;
+ if (tm->tm_mon < 0)
+ tm->tm_mon = now->tm_mon;
+ if (tm->tm_year < 0) {
+ tm->tm_year = now->tm_year;
+ if (tm->tm_mon > now->tm_mon)
+ tm->tm_year--;
+ }
+
+ n = mktime(tm) - sec;
+ p_localtime_r(&n, tm);
+ return n;
+}
+
+static void date_now(struct tm *tm, struct tm *now, int *num)
+{
+ GIT_UNUSED(num);
+ update_tm(tm, now, 0);
+}
+
+static void date_yesterday(struct tm *tm, struct tm *now, int *num)
+{
+ GIT_UNUSED(num);
+ update_tm(tm, now, 24*60*60);
+}
+
+static void date_time(struct tm *tm, struct tm *now, int hour)
+{
+ if (tm->tm_hour < hour)
+ date_yesterday(tm, now, NULL);
+ tm->tm_hour = hour;
+ tm->tm_min = 0;
+ tm->tm_sec = 0;
+}
+
+static void date_midnight(struct tm *tm, struct tm *now, int *num)
+{
+ GIT_UNUSED(num);
+ date_time(tm, now, 0);
+}
+
+static void date_noon(struct tm *tm, struct tm *now, int *num)
+{
+ GIT_UNUSED(num);
+ date_time(tm, now, 12);
+}
+
+static void date_tea(struct tm *tm, struct tm *now, int *num)
+{
+ GIT_UNUSED(num);
+ date_time(tm, now, 17);
+}
+
+static void date_pm(struct tm *tm, struct tm *now, int *num)
+{
+ int hour, n = *num;
+ *num = 0;
+ GIT_UNUSED(now);
+
+ hour = tm->tm_hour;
+ if (n) {
+ hour = n;
+ tm->tm_min = 0;
+ tm->tm_sec = 0;
+ }
+ tm->tm_hour = (hour % 12) + 12;
+}
+
+static void date_am(struct tm *tm, struct tm *now, int *num)
+{
+ int hour, n = *num;
+ *num = 0;
+ GIT_UNUSED(now);
+
+ hour = tm->tm_hour;
+ if (n) {
+ hour = n;
+ tm->tm_min = 0;
+ tm->tm_sec = 0;
+ }
+ tm->tm_hour = (hour % 12);
+}
+
+static void date_never(struct tm *tm, struct tm *now, int *num)
+{
+ time_t n = 0;
+ GIT_UNUSED(now);
+ GIT_UNUSED(num);
+ p_localtime_r(&n, tm);
+}
+
+static const struct special {
+ const char *name;
+ void (*fn)(struct tm *, struct tm *, int *);
+} special[] = {
+ { "yesterday", date_yesterday },
+ { "noon", date_noon },
+ { "midnight", date_midnight },
+ { "tea", date_tea },
+ { "PM", date_pm },
+ { "AM", date_am },
+ { "never", date_never },
+ { "now", date_now },
+ { NULL }
+};
+
+static const char *number_name[] = {
+ "zero", "one", "two", "three", "four",
+ "five", "six", "seven", "eight", "nine", "ten",
+};
+
+static const struct typelen {
+ const char *type;
+ int length;
+} typelen[] = {
+ { "seconds", 1 },
+ { "minutes", 60 },
+ { "hours", 60*60 },
+ { "days", 24*60*60 },
+ { "weeks", 7*24*60*60 },
+ { NULL }
+};
+
+static const char *approxidate_alpha(const char *date, struct tm *tm, struct tm *now, int *num, int *touched)
+{
+ const struct typelen *tl;
+ const struct special *s;
+ const char *end = date;
+ int i;
+
+ while (isalpha(*++end))
+ /* scan to non-alpha */;
+
+ for (i = 0; i < 12; i++) {
+ size_t match = match_string(date, month_names[i]);
+ if (match >= 3) {
+ tm->tm_mon = i;
+ *touched = 1;
+ return end;
+ }
+ }
+
+ for (s = special; s->name; s++) {
+ size_t len = strlen(s->name);
+ if (match_string(date, s->name) == len) {
+ s->fn(tm, now, num);
+ *touched = 1;
+ return end;
+ }
+ }
+
+ if (!*num) {
+ for (i = 1; i < 11; i++) {
+ size_t len = strlen(number_name[i]);
+ if (match_string(date, number_name[i]) == len) {
+ *num = i;
+ *touched = 1;
+ return end;
+ }
+ }
+ if (match_string(date, "last") == 4) {
+ *num = 1;
+ *touched = 1;
+ }
+ return end;
+ }
+
+ tl = typelen;
+ while (tl->type) {
+ size_t len = strlen(tl->type);
+ if (match_string(date, tl->type) >= len-1) {
+ update_tm(tm, now, tl->length * (unsigned long)*num);
+ *num = 0;
+ *touched = 1;
+ return end;
+ }
+ tl++;
+ }
+
+ for (i = 0; i < 7; i++) {
+ size_t match = match_string(date, weekday_names[i]);
+ if (match >= 3) {
+ int diff, n = *num -1;
+ *num = 0;
+
+ diff = tm->tm_wday - i;
+ if (diff <= 0)
+ n++;
+ diff += 7*n;
+
+ update_tm(tm, now, diff * 24 * 60 * 60);
+ *touched = 1;
+ return end;
+ }
+ }
+
+ if (match_string(date, "months") >= 5) {
+ int n;
+ update_tm(tm, now, 0); /* fill in date fields if needed */
+ n = tm->tm_mon - *num;
+ *num = 0;
+ while (n < 0) {
+ n += 12;
+ tm->tm_year--;
+ }
+ tm->tm_mon = n;
+ *touched = 1;
+ return end;
+ }
+
+ if (match_string(date, "years") >= 4) {
+ update_tm(tm, now, 0); /* fill in date fields if needed */
+ tm->tm_year -= *num;
+ *num = 0;
+ *touched = 1;
+ return end;
+ }
+
+ return end;
+}
+
+static const char *approxidate_digit(const char *date, struct tm *tm, int *num)
+{
+ char *end;
+ unsigned long number = strtoul(date, &end, 10);
+
+ switch (*end) {
+ case ':':
+ case '.':
+ case '/':
+ case '-':
+ if (isdigit(end[1])) {
+ size_t match = match_multi_number(number, *end, date, end, tm);
+ if (match)
+ return date + match;
+ }
+ }
+
+ /* Accept zero-padding only for small numbers ("Dec 02", never "Dec 0002") */
+ if (date[0] != '0' || end - date <= 2)
+ *num = number;
+ return end;
+}
+
+/*
+ * Do we have a pending number at the end, or when
+ * we see a new one? Let's assume it's a month day,
+ * as in "Dec 6, 1992"
+ */
+static void pending_number(struct tm *tm, int *num)
+{
+ int number = *num;
+
+ if (number) {
+ *num = 0;
+ if (tm->tm_mday < 0 && number < 32)
+ tm->tm_mday = number;
+ else if (tm->tm_mon < 0 && number < 13)
+ tm->tm_mon = number-1;
+ else if (tm->tm_year < 0) {
+ if (number > 1969 && number < 2100)
+ tm->tm_year = number - 1900;
+ else if (number > 69 && number < 100)
+ tm->tm_year = number;
+ else if (number < 38)
+ tm->tm_year = 100 + number;
+ /* We mess up for number = 00 ? */
+ }
+ }
+}
+
+static git_time_t approxidate_str(const char *date,
+ time_t time_sec,
+ int *error_ret)
+{
+ int number = 0;
+ int touched = 0;
+ struct tm tm = {0}, now;
+
+ p_localtime_r(&time_sec, &tm);
+ now = tm;
+
+ tm.tm_year = -1;
+ tm.tm_mon = -1;
+ tm.tm_mday = -1;
+
+ for (;;) {
+ unsigned char c = *date;
+ if (!c)
+ break;
+ date++;
+ if (isdigit(c)) {
+ pending_number(&tm, &number);
+ date = approxidate_digit(date-1, &tm, &number);
+ touched = 1;
+ continue;
+ }
+ if (isalpha(c))
+ date = approxidate_alpha(date-1, &tm, &now, &number, &touched);
+ }
+ pending_number(&tm, &number);
+ if (!touched)
+ *error_ret = -1;
+ return update_tm(&tm, &now, 0);
+}
+
+int git_date_parse(git_time_t *out, const char *date)
+{
+ time_t time_sec;
+ git_time_t timestamp;
+ int offset, error_ret=0;
+
+ if (!parse_date_basic(date, &timestamp, &offset)) {
+ *out = timestamp;
+ return 0;
+ }
+
+ if (time(&time_sec) == -1)
+ return -1;
+
+ *out = approxidate_str(date, time_sec, &error_ret);
+ return error_ret;
+}
+
+int git_date_rfc2822_fmt(git_str *out, git_time_t time, int offset)
+{
+ time_t t;
+ struct tm gmt;
+
+ GIT_ASSERT_ARG(out);
+
+ t = (time_t) (time + offset * 60);
+
+ if (p_gmtime_r(&t, &gmt) == NULL)
+ return -1;
+
+ return git_str_printf(out, "%.3s, %u %.3s %.4u %02u:%02u:%02u %+03d%02d",
+ weekday_names[gmt.tm_wday],
+ gmt.tm_mday,
+ month_names[gmt.tm_mon],
+ gmt.tm_year + 1900,
+ gmt.tm_hour, gmt.tm_min, gmt.tm_sec,
+ offset / 60, offset % 60);
+}
+
diff --git a/src/util/date.h b/src/util/date.h
new file mode 100644
index 0000000..7ebd3c3
--- /dev/null
+++ b/src/util/date.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_date_h__
+#define INCLUDE_date_h__
+
+#include "util.h"
+#include "str.h"
+
+/*
+ * Parse a string into a value as a git_time_t.
+ *
+ * Sample valid input:
+ * - "yesterday"
+ * - "July 17, 2003"
+ * - "2003-7-17 08:23"
+ */
+extern int git_date_parse(git_time_t *out, const char *date);
+
+/*
+ * Format a git_time as a RFC2822 string
+ *
+ * @param out buffer to store formatted date
+ * @param time the time to be formatted
+ * @param offset the timezone offset
+ * @return 0 if successful; -1 on error
+ */
+extern int git_date_rfc2822_fmt(git_str *out, git_time_t time, int offset);
+
+#endif
diff --git a/src/util/filebuf.c b/src/util/filebuf.c
new file mode 100644
index 0000000..7afb76b
--- /dev/null
+++ b/src/util/filebuf.c
@@ -0,0 +1,600 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "filebuf.h"
+
+#include "futils.h"
+
+static const size_t WRITE_BUFFER_SIZE = (4096 * 2);
+
+enum buferr_t {
+ BUFERR_OK = 0,
+ BUFERR_WRITE,
+ BUFERR_ZLIB,
+ BUFERR_MEM
+};
+
+#define ENSURE_BUF_OK(buf) if ((buf)->last_error != BUFERR_OK) { return -1; }
+
+static int verify_last_error(git_filebuf *file)
+{
+ switch (file->last_error) {
+ case BUFERR_WRITE:
+ git_error_set(GIT_ERROR_OS, "failed to write out file");
+ return -1;
+
+ case BUFERR_MEM:
+ git_error_set_oom();
+ return -1;
+
+ case BUFERR_ZLIB:
+ git_error_set(GIT_ERROR_ZLIB,
+ "Buffer error when writing out ZLib data");
+ return -1;
+
+ default:
+ return 0;
+ }
+}
+
+static int lock_file(git_filebuf *file, int flags, mode_t mode)
+{
+ if (git_fs_path_exists(file->path_lock) == true) {
+ git_error_clear(); /* actual OS error code just confuses */
+ git_error_set(GIT_ERROR_OS,
+ "failed to lock file '%s' for writing", file->path_lock);
+ return GIT_ELOCKED;
+ }
+
+ /* create path to the file buffer is required */
+ if (flags & GIT_FILEBUF_CREATE_LEADING_DIRS) {
+ /* XXX: Should dirmode here be configurable? Or is 0777 always fine? */
+ file->fd = git_futils_creat_locked_withpath(file->path_lock, 0777, mode);
+ } else {
+ file->fd = git_futils_creat_locked(file->path_lock, mode);
+ }
+
+ if (file->fd < 0)
+ return file->fd;
+
+ file->fd_is_open = true;
+
+ if ((flags & GIT_FILEBUF_APPEND) && git_fs_path_exists(file->path_original) == true) {
+ git_file source;
+ char buffer[GIT_BUFSIZE_FILEIO];
+ ssize_t read_bytes;
+ int error = 0;
+
+ source = p_open(file->path_original, O_RDONLY);
+ if (source < 0) {
+ git_error_set(GIT_ERROR_OS,
+ "failed to open file '%s' for reading",
+ file->path_original);
+ return -1;
+ }
+
+ while ((read_bytes = p_read(source, buffer, sizeof(buffer))) > 0) {
+ if ((error = p_write(file->fd, buffer, read_bytes)) < 0)
+ break;
+ if (file->compute_digest)
+ git_hash_update(&file->digest, buffer, read_bytes);
+ }
+
+ p_close(source);
+
+ if (read_bytes < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to read file '%s'", file->path_original);
+ return -1;
+ } else if (error < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to write file '%s'", file->path_lock);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+void git_filebuf_cleanup(git_filebuf *file)
+{
+ if (file->fd_is_open && file->fd >= 0)
+ p_close(file->fd);
+
+ if (file->created_lock && !file->did_rename && file->path_lock && git_fs_path_exists(file->path_lock))
+ p_unlink(file->path_lock);
+
+ if (file->compute_digest) {
+ git_hash_ctx_cleanup(&file->digest);
+ file->compute_digest = 0;
+ }
+
+ if (file->buffer)
+ git__free(file->buffer);
+
+ /* use the presence of z_buf to decide if we need to deflateEnd */
+ if (file->z_buf) {
+ git__free(file->z_buf);
+ deflateEnd(&file->zs);
+ }
+
+ if (file->path_original)
+ git__free(file->path_original);
+ if (file->path_lock)
+ git__free(file->path_lock);
+
+ memset(file, 0x0, sizeof(git_filebuf));
+ file->fd = -1;
+}
+
+GIT_INLINE(int) flush_buffer(git_filebuf *file)
+{
+ int result = file->write(file, file->buffer, file->buf_pos);
+ file->buf_pos = 0;
+ return result;
+}
+
+int git_filebuf_flush(git_filebuf *file)
+{
+ return flush_buffer(file);
+}
+
+static int write_normal(git_filebuf *file, void *source, size_t len)
+{
+ if (len > 0) {
+ if (p_write(file->fd, (void *)source, len) < 0) {
+ file->last_error = BUFERR_WRITE;
+ return -1;
+ }
+
+ if (file->compute_digest)
+ git_hash_update(&file->digest, source, len);
+ }
+
+ return 0;
+}
+
+static int write_deflate(git_filebuf *file, void *source, size_t len)
+{
+ z_stream *zs = &file->zs;
+
+ if (len > 0 || file->flush_mode == Z_FINISH) {
+ zs->next_in = source;
+ zs->avail_in = (uInt)len;
+
+ do {
+ size_t have;
+
+ zs->next_out = file->z_buf;
+ zs->avail_out = (uInt)file->buf_size;
+
+ if (deflate(zs, file->flush_mode) == Z_STREAM_ERROR) {
+ file->last_error = BUFERR_ZLIB;
+ return -1;
+ }
+
+ have = file->buf_size - (size_t)zs->avail_out;
+
+ if (p_write(file->fd, file->z_buf, have) < 0) {
+ file->last_error = BUFERR_WRITE;
+ return -1;
+ }
+
+ } while (zs->avail_out == 0);
+
+ GIT_ASSERT(zs->avail_in == 0);
+
+ if (file->compute_digest)
+ git_hash_update(&file->digest, source, len);
+ }
+
+ return 0;
+}
+
+#define MAX_SYMLINK_DEPTH 5
+
+static int resolve_symlink(git_str *out, const char *path)
+{
+ int i, error, root;
+ ssize_t ret;
+ struct stat st;
+ git_str curpath = GIT_STR_INIT, target = GIT_STR_INIT;
+
+ if ((error = git_str_grow(&target, GIT_PATH_MAX + 1)) < 0 ||
+ (error = git_str_puts(&curpath, path)) < 0)
+ return error;
+
+ for (i = 0; i < MAX_SYMLINK_DEPTH; i++) {
+ error = p_lstat(curpath.ptr, &st);
+ if (error < 0 && errno == ENOENT) {
+ error = git_str_puts(out, curpath.ptr);
+ goto cleanup;
+ }
+
+ if (error < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to stat '%s'", curpath.ptr);
+ error = -1;
+ goto cleanup;
+ }
+
+ if (!S_ISLNK(st.st_mode)) {
+ error = git_str_puts(out, curpath.ptr);
+ goto cleanup;
+ }
+
+ ret = p_readlink(curpath.ptr, target.ptr, GIT_PATH_MAX);
+ if (ret < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to read symlink '%s'", curpath.ptr);
+ error = -1;
+ goto cleanup;
+ }
+
+ if (ret == GIT_PATH_MAX) {
+ git_error_set(GIT_ERROR_INVALID, "symlink target too long");
+ error = -1;
+ goto cleanup;
+ }
+
+ /* readlink(2) won't NUL-terminate for us */
+ target.ptr[ret] = '\0';
+ target.size = ret;
+
+ root = git_fs_path_root(target.ptr);
+ if (root >= 0) {
+ if ((error = git_str_sets(&curpath, target.ptr)) < 0)
+ goto cleanup;
+ } else {
+ git_str dir = GIT_STR_INIT;
+
+ if ((error = git_fs_path_dirname_r(&dir, curpath.ptr)) < 0)
+ goto cleanup;
+
+ git_str_swap(&curpath, &dir);
+ git_str_dispose(&dir);
+
+ if ((error = git_fs_path_apply_relative(&curpath, target.ptr)) < 0)
+ goto cleanup;
+ }
+ }
+
+ git_error_set(GIT_ERROR_INVALID, "maximum symlink depth reached");
+ error = -1;
+
+cleanup:
+ git_str_dispose(&curpath);
+ git_str_dispose(&target);
+ return error;
+}
+
+int git_filebuf_open(git_filebuf *file, const char *path, int flags, mode_t mode)
+{
+ return git_filebuf_open_withsize(file, path, flags, mode, WRITE_BUFFER_SIZE);
+}
+
+int git_filebuf_open_withsize(git_filebuf *file, const char *path, int flags, mode_t mode, size_t size)
+{
+ int compression, error = -1;
+ size_t path_len, alloc_len;
+
+ GIT_ASSERT_ARG(file);
+ GIT_ASSERT_ARG(path);
+ GIT_ASSERT(file->buffer == NULL);
+
+ memset(file, 0x0, sizeof(git_filebuf));
+
+ if (flags & GIT_FILEBUF_DO_NOT_BUFFER)
+ file->do_not_buffer = true;
+
+ if (flags & GIT_FILEBUF_FSYNC)
+ file->do_fsync = true;
+
+ file->buf_size = size;
+ file->buf_pos = 0;
+ file->fd = -1;
+ file->last_error = BUFERR_OK;
+
+ /* Allocate the main cache buffer */
+ if (!file->do_not_buffer) {
+ file->buffer = git__malloc(file->buf_size);
+ GIT_ERROR_CHECK_ALLOC(file->buffer);
+ }
+
+ /* If we are hashing on-write, allocate a new hash context */
+ if (flags & GIT_FILEBUF_HASH_SHA1) {
+ file->compute_digest = 1;
+
+ if (git_hash_ctx_init(&file->digest, GIT_HASH_ALGORITHM_SHA1) < 0)
+ goto cleanup;
+ } else if (flags & GIT_FILEBUF_HASH_SHA256) {
+ file->compute_digest = 1;
+
+ if (git_hash_ctx_init(&file->digest, GIT_HASH_ALGORITHM_SHA256) < 0)
+ goto cleanup;
+ }
+
+ compression = flags >> GIT_FILEBUF_DEFLATE_SHIFT;
+
+ /* If we are deflating on-write, */
+ if (compression != 0) {
+ /* Initialize the ZLib stream */
+ if (deflateInit(&file->zs, compression) != Z_OK) {
+ git_error_set(GIT_ERROR_ZLIB, "failed to initialize zlib");
+ goto cleanup;
+ }
+
+ /* Allocate the Zlib cache buffer */
+ file->z_buf = git__malloc(file->buf_size);
+ GIT_ERROR_CHECK_ALLOC(file->z_buf);
+
+ /* Never flush */
+ file->flush_mode = Z_NO_FLUSH;
+ file->write = &write_deflate;
+ } else {
+ file->write = &write_normal;
+ }
+
+ /* If we are writing to a temp file */
+ if (flags & GIT_FILEBUF_TEMPORARY) {
+ git_str tmp_path = GIT_STR_INIT;
+
+ /* Open the file as temporary for locking */
+ file->fd = git_futils_mktmp(&tmp_path, path, mode);
+
+ if (file->fd < 0) {
+ git_str_dispose(&tmp_path);
+ goto cleanup;
+ }
+ file->fd_is_open = true;
+ file->created_lock = true;
+
+ /* No original path */
+ file->path_original = NULL;
+ file->path_lock = git_str_detach(&tmp_path);
+ GIT_ERROR_CHECK_ALLOC(file->path_lock);
+ } else {
+ git_str resolved_path = GIT_STR_INIT;
+
+ if ((error = resolve_symlink(&resolved_path, path)) < 0)
+ goto cleanup;
+
+ /* Save the original path of the file */
+ path_len = resolved_path.size;
+ file->path_original = git_str_detach(&resolved_path);
+
+ /* create the locking path by appending ".lock" to the original */
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, path_len, GIT_FILELOCK_EXTLENGTH);
+ file->path_lock = git__malloc(alloc_len);
+ GIT_ERROR_CHECK_ALLOC(file->path_lock);
+
+ memcpy(file->path_lock, file->path_original, path_len);
+ memcpy(file->path_lock + path_len, GIT_FILELOCK_EXTENSION, GIT_FILELOCK_EXTLENGTH);
+
+ if (git_fs_path_isdir(file->path_original)) {
+ git_error_set(GIT_ERROR_FILESYSTEM, "path '%s' is a directory", file->path_original);
+ error = GIT_EDIRECTORY;
+ goto cleanup;
+ }
+
+ /* open the file for locking */
+ if ((error = lock_file(file, flags, mode)) < 0)
+ goto cleanup;
+
+ file->created_lock = true;
+ }
+
+ return 0;
+
+cleanup:
+ git_filebuf_cleanup(file);
+ return error;
+}
+
+int git_filebuf_hash(unsigned char *out, git_filebuf *file)
+{
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(file);
+ GIT_ASSERT_ARG(file->compute_digest);
+
+ flush_buffer(file);
+
+ if (verify_last_error(file) < 0)
+ return -1;
+
+ git_hash_final(out, &file->digest);
+ git_hash_ctx_cleanup(&file->digest);
+ file->compute_digest = 0;
+
+ return 0;
+}
+
+int git_filebuf_commit_at(git_filebuf *file, const char *path)
+{
+ git__free(file->path_original);
+ file->path_original = git__strdup(path);
+ GIT_ERROR_CHECK_ALLOC(file->path_original);
+
+ return git_filebuf_commit(file);
+}
+
+int git_filebuf_commit(git_filebuf *file)
+{
+ /* temporary files cannot be committed */
+ GIT_ASSERT_ARG(file);
+ GIT_ASSERT(file->path_original);
+
+ file->flush_mode = Z_FINISH;
+ flush_buffer(file);
+
+ if (verify_last_error(file) < 0)
+ goto on_error;
+
+ file->fd_is_open = false;
+
+ if (file->do_fsync && p_fsync(file->fd) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to fsync '%s'", file->path_lock);
+ goto on_error;
+ }
+
+ if (p_close(file->fd) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to close file at '%s'", file->path_lock);
+ goto on_error;
+ }
+
+ file->fd = -1;
+
+ if (p_rename(file->path_lock, file->path_original) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to rename lockfile to '%s'", file->path_original);
+ goto on_error;
+ }
+
+ if (file->do_fsync && git_futils_fsync_parent(file->path_original) < 0)
+ goto on_error;
+
+ file->did_rename = true;
+
+ git_filebuf_cleanup(file);
+ return 0;
+
+on_error:
+ git_filebuf_cleanup(file);
+ return -1;
+}
+
+GIT_INLINE(void) add_to_cache(git_filebuf *file, const void *buf, size_t len)
+{
+ memcpy(file->buffer + file->buf_pos, buf, len);
+ file->buf_pos += len;
+}
+
+int git_filebuf_write(git_filebuf *file, const void *buff, size_t len)
+{
+ const unsigned char *buf = buff;
+
+ ENSURE_BUF_OK(file);
+
+ if (file->do_not_buffer)
+ return file->write(file, (void *)buff, len);
+
+ for (;;) {
+ size_t space_left = file->buf_size - file->buf_pos;
+
+ /* cache if it's small */
+ if (space_left > len) {
+ add_to_cache(file, buf, len);
+ return 0;
+ }
+
+ add_to_cache(file, buf, space_left);
+ if (flush_buffer(file) < 0)
+ return -1;
+
+ len -= space_left;
+ buf += space_left;
+ }
+}
+
+int git_filebuf_reserve(git_filebuf *file, void **buffer, size_t len)
+{
+ size_t space_left = file->buf_size - file->buf_pos;
+
+ *buffer = NULL;
+
+ ENSURE_BUF_OK(file);
+
+ if (len > file->buf_size) {
+ file->last_error = BUFERR_MEM;
+ return -1;
+ }
+
+ if (space_left <= len) {
+ if (flush_buffer(file) < 0)
+ return -1;
+ }
+
+ *buffer = (file->buffer + file->buf_pos);
+ file->buf_pos += len;
+
+ return 0;
+}
+
+int git_filebuf_printf(git_filebuf *file, const char *format, ...)
+{
+ va_list arglist;
+ size_t space_left, len, alloclen;
+ int written, res;
+ char *tmp_buffer;
+
+ ENSURE_BUF_OK(file);
+
+ space_left = file->buf_size - file->buf_pos;
+
+ do {
+ va_start(arglist, format);
+ written = p_vsnprintf((char *)file->buffer + file->buf_pos, space_left, format, arglist);
+ va_end(arglist);
+
+ if (written < 0) {
+ file->last_error = BUFERR_MEM;
+ return -1;
+ }
+
+ len = written;
+ if (len + 1 <= space_left) {
+ file->buf_pos += len;
+ return 0;
+ }
+
+ if (flush_buffer(file) < 0)
+ return -1;
+
+ space_left = file->buf_size - file->buf_pos;
+
+ } while (len + 1 <= space_left);
+
+ if (GIT_ADD_SIZET_OVERFLOW(&alloclen, len, 1) ||
+ !(tmp_buffer = git__malloc(alloclen))) {
+ file->last_error = BUFERR_MEM;
+ return -1;
+ }
+
+ va_start(arglist, format);
+ written = p_vsnprintf(tmp_buffer, len + 1, format, arglist);
+ va_end(arglist);
+
+ if (written < 0) {
+ git__free(tmp_buffer);
+ file->last_error = BUFERR_MEM;
+ return -1;
+ }
+
+ res = git_filebuf_write(file, tmp_buffer, len);
+ git__free(tmp_buffer);
+
+ return res;
+}
+
+int git_filebuf_stats(time_t *mtime, size_t *size, git_filebuf *file)
+{
+ int res;
+ struct stat st;
+
+ if (file->fd_is_open)
+ res = p_fstat(file->fd, &st);
+ else
+ res = p_stat(file->path_original, &st);
+
+ if (res < 0) {
+ git_error_set(GIT_ERROR_OS, "could not get stat info for '%s'",
+ file->path_original);
+ return res;
+ }
+
+ if (mtime)
+ *mtime = st.st_mtime;
+ if (size)
+ *size = (size_t)st.st_size;
+
+ return 0;
+}
diff --git a/src/util/filebuf.h b/src/util/filebuf.h
new file mode 100644
index 0000000..e23b9ed
--- /dev/null
+++ b/src/util/filebuf.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_filebuf_h__
+#define INCLUDE_filebuf_h__
+
+#include "git2_util.h"
+
+#include "futils.h"
+#include "hash.h"
+#include <zlib.h>
+
+#ifdef GIT_THREADS
+# define GIT_FILEBUF_THREADS
+#endif
+
+#define GIT_FILEBUF_HASH_SHA1 (1 << 0)
+#define GIT_FILEBUF_HASH_SHA256 (1 << 1)
+#define GIT_FILEBUF_APPEND (1 << 2)
+#define GIT_FILEBUF_CREATE_LEADING_DIRS (1 << 3)
+#define GIT_FILEBUF_TEMPORARY (1 << 4)
+#define GIT_FILEBUF_DO_NOT_BUFFER (1 << 5)
+#define GIT_FILEBUF_FSYNC (1 << 6)
+#define GIT_FILEBUF_DEFLATE_SHIFT (7)
+
+#define GIT_FILELOCK_EXTENSION ".lock\0"
+#define GIT_FILELOCK_EXTLENGTH 6
+
+typedef struct git_filebuf git_filebuf;
+struct git_filebuf {
+ char *path_original;
+ char *path_lock;
+
+ int (*write)(git_filebuf *file, void *source, size_t len);
+
+ bool compute_digest;
+ git_hash_ctx digest;
+
+ unsigned char *buffer;
+ unsigned char *z_buf;
+
+ z_stream zs;
+ int flush_mode;
+
+ size_t buf_size, buf_pos;
+ git_file fd;
+ bool fd_is_open;
+ bool created_lock;
+ bool did_rename;
+ bool do_not_buffer;
+ bool do_fsync;
+ int last_error;
+};
+
+#define GIT_FILEBUF_INIT {0}
+
+/*
+ * The git_filebuf object lifecycle is:
+ * - Allocate git_filebuf, preferably using GIT_FILEBUF_INIT.
+ *
+ * - Call git_filebuf_open() to initialize the filebuf for use.
+ *
+ * - Make as many calls to git_filebuf_write(), git_filebuf_printf(),
+ * git_filebuf_reserve() as you like. The error codes for these
+ * functions don't need to be checked. They are stored internally
+ * by the file buffer.
+ *
+ * - While you are writing, you may call git_filebuf_hash() to get
+ * the hash of all you have written so far. This function will
+ * fail if any of the previous writes to the buffer failed.
+ *
+ * - To close the git_filebuf, you may call git_filebuf_commit() or
+ * git_filebuf_commit_at() to save the file, or
+ * git_filebuf_cleanup() to abandon the file. All of these will
+ * free the git_filebuf object. Likewise, all of these will fail
+ * if any of the previous writes to the buffer failed, and set
+ * an error code accordingly.
+ */
+int git_filebuf_write(git_filebuf *lock, const void *buff, size_t len);
+int git_filebuf_reserve(git_filebuf *file, void **buff, size_t len);
+int git_filebuf_printf(git_filebuf *file, const char *format, ...) GIT_FORMAT_PRINTF(2, 3);
+
+int git_filebuf_open(git_filebuf *lock, const char *path, int flags, mode_t mode);
+int git_filebuf_open_withsize(git_filebuf *file, const char *path, int flags, mode_t mode, size_t size);
+int git_filebuf_commit(git_filebuf *lock);
+int git_filebuf_commit_at(git_filebuf *lock, const char *path);
+void git_filebuf_cleanup(git_filebuf *lock);
+int git_filebuf_hash(unsigned char *out, git_filebuf *file);
+int git_filebuf_flush(git_filebuf *file);
+int git_filebuf_stats(time_t *mtime, size_t *size, git_filebuf *file);
+
+GIT_INLINE(int) git_filebuf_hash_flags(git_hash_algorithm_t algorithm)
+{
+ switch (algorithm) {
+ case GIT_HASH_ALGORITHM_SHA1:
+ return GIT_FILEBUF_HASH_SHA1;
+ case GIT_HASH_ALGORITHM_SHA256:
+ return GIT_FILEBUF_HASH_SHA256;
+ default:
+ return 0;
+ }
+}
+
+#endif
diff --git a/src/util/fs_path.c b/src/util/fs_path.c
new file mode 100644
index 0000000..e03fcf7
--- /dev/null
+++ b/src/util/fs_path.c
@@ -0,0 +1,2066 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "fs_path.h"
+
+#include "git2_util.h"
+#include "futils.h"
+#include "posix.h"
+#ifdef GIT_WIN32
+#include "win32/posix.h"
+#include "win32/w32_buffer.h"
+#include "win32/w32_util.h"
+#include "win32/version.h"
+#include <aclapi.h>
+#else
+#include <dirent.h>
+#endif
+#include <stdio.h>
+#include <ctype.h>
+
+#define ensure_error_set(code) do { \
+ const git_error *e = git_error_last(); \
+ if (!e || !e->message) \
+ git_error_set(e ? e->klass : GIT_ERROR_CALLBACK, \
+ "filesystem callback returned %d", code); \
+ } while(0)
+
+static int dos_drive_prefix_length(const char *path)
+{
+ int i;
+
+ /*
+ * Does it start with an ASCII letter (i.e. highest bit not set),
+ * followed by a colon?
+ */
+ if (!(0x80 & (unsigned char)*path))
+ return *path && path[1] == ':' ? 2 : 0;
+
+ /*
+ * While drive letters must be letters of the English alphabet, it is
+ * possible to assign virtually _any_ Unicode character via `subst` as
+ * a drive letter to "virtual drives". Even `1`, or `ä`. Or fun stuff
+ * like this:
+ *
+ * subst Ö: %USERPROFILE%\Desktop
+ */
+ for (i = 1; i < 4 && (0x80 & (unsigned char)path[i]); i++)
+ ; /* skip first UTF-8 character */
+ return path[i] == ':' ? i + 1 : 0;
+}
+
+#ifdef GIT_WIN32
+static bool looks_like_network_computer_name(const char *path, int pos)
+{
+ if (pos < 3)
+ return false;
+
+ if (path[0] != '/' || path[1] != '/')
+ return false;
+
+ while (pos-- > 2) {
+ if (path[pos] == '/')
+ return false;
+ }
+
+ return true;
+}
+#endif
+
+/*
+ * Based on the Android implementation, BSD licensed.
+ * http://android.git.kernel.org/
+ *
+ * Copyright (C) 2008 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+int git_fs_path_basename_r(git_str *buffer, const char *path)
+{
+ const char *endp, *startp;
+ int len, result;
+
+ /* Empty or NULL string gets treated as "." */
+ if (path == NULL || *path == '\0') {
+ startp = ".";
+ len = 1;
+ goto Exit;
+ }
+
+ /* Strip trailing slashes */
+ endp = path + strlen(path) - 1;
+ while (endp > path && *endp == '/')
+ endp--;
+
+ /* All slashes becomes "/" */
+ if (endp == path && *endp == '/') {
+ startp = "/";
+ len = 1;
+ goto Exit;
+ }
+
+ /* Find the start of the base */
+ startp = endp;
+ while (startp > path && *(startp - 1) != '/')
+ startp--;
+
+ /* Cast is safe because max path < max int */
+ len = (int)(endp - startp + 1);
+
+Exit:
+ result = len;
+
+ if (buffer != NULL && git_str_set(buffer, startp, len) < 0)
+ return -1;
+
+ return result;
+}
+
+/*
+ * Determine if the path is a Windows prefix and, if so, returns
+ * its actual length. If it is not a prefix, returns -1.
+ */
+static int win32_prefix_length(const char *path, int len)
+{
+#ifndef GIT_WIN32
+ GIT_UNUSED(path);
+ GIT_UNUSED(len);
+#else
+ /*
+ * Mimic unix behavior where '/.git' returns '/': 'C:/.git'
+ * will return 'C:/' here
+ */
+ if (dos_drive_prefix_length(path) == len)
+ return len;
+
+ /*
+ * Similarly checks if we're dealing with a network computer name
+ * '//computername/.git' will return '//computername/'
+ */
+ if (looks_like_network_computer_name(path, len))
+ return len;
+#endif
+
+ return -1;
+}
+
+/*
+ * Based on the Android implementation, BSD licensed.
+ * Check http://android.git.kernel.org/
+ */
+int git_fs_path_dirname_r(git_str *buffer, const char *path)
+{
+ const char *endp;
+ int is_prefix = 0, len;
+
+ /* Empty or NULL string gets treated as "." */
+ if (path == NULL || *path == '\0') {
+ path = ".";
+ len = 1;
+ goto Exit;
+ }
+
+ /* Strip trailing slashes */
+ endp = path + strlen(path) - 1;
+ while (endp > path && *endp == '/')
+ endp--;
+
+ if (endp - path + 1 > INT_MAX) {
+ git_error_set(GIT_ERROR_INVALID, "path too long");
+ return -1;
+ }
+
+ if ((len = win32_prefix_length(path, (int)(endp - path + 1))) > 0) {
+ is_prefix = 1;
+ goto Exit;
+ }
+
+ /* Find the start of the dir */
+ while (endp > path && *endp != '/')
+ endp--;
+
+ /* Either the dir is "/" or there are no slashes */
+ if (endp == path) {
+ path = (*endp == '/') ? "/" : ".";
+ len = 1;
+ goto Exit;
+ }
+
+ do {
+ endp--;
+ } while (endp > path && *endp == '/');
+
+ if (endp - path + 1 > INT_MAX) {
+ git_error_set(GIT_ERROR_INVALID, "path too long");
+ return -1;
+ }
+
+ if ((len = win32_prefix_length(path, (int)(endp - path + 1))) > 0) {
+ is_prefix = 1;
+ goto Exit;
+ }
+
+ /* Cast is safe because max path < max int */
+ len = (int)(endp - path + 1);
+
+Exit:
+ if (buffer) {
+ if (git_str_set(buffer, path, len) < 0)
+ return -1;
+ if (is_prefix && git_str_putc(buffer, '/') < 0)
+ return -1;
+ }
+
+ return len;
+}
+
+
+char *git_fs_path_dirname(const char *path)
+{
+ git_str buf = GIT_STR_INIT;
+ char *dirname;
+
+ git_fs_path_dirname_r(&buf, path);
+ dirname = git_str_detach(&buf);
+ git_str_dispose(&buf); /* avoid memleak if error occurs */
+
+ return dirname;
+}
+
+char *git_fs_path_basename(const char *path)
+{
+ git_str buf = GIT_STR_INIT;
+ char *basename;
+
+ git_fs_path_basename_r(&buf, path);
+ basename = git_str_detach(&buf);
+ git_str_dispose(&buf); /* avoid memleak if error occurs */
+
+ return basename;
+}
+
+size_t git_fs_path_basename_offset(git_str *buffer)
+{
+ ssize_t slash;
+
+ if (!buffer || buffer->size <= 0)
+ return 0;
+
+ slash = git_str_rfind_next(buffer, '/');
+
+ if (slash >= 0 && buffer->ptr[slash] == '/')
+ return (size_t)(slash + 1);
+
+ return 0;
+}
+
+int git_fs_path_root(const char *path)
+{
+ int offset = 0, prefix_len;
+
+ /* Does the root of the path look like a windows drive ? */
+ if ((prefix_len = dos_drive_prefix_length(path)))
+ offset += prefix_len;
+
+#ifdef GIT_WIN32
+ /* Are we dealing with a windows network path? */
+ else if ((path[0] == '/' && path[1] == '/' && path[2] != '/') ||
+ (path[0] == '\\' && path[1] == '\\' && path[2] != '\\'))
+ {
+ offset += 2;
+
+ /* Skip the computer name segment */
+ while (path[offset] && path[offset] != '/' && path[offset] != '\\')
+ offset++;
+ }
+
+ if (path[offset] == '\\')
+ return offset;
+#endif
+
+ if (path[offset] == '/')
+ return offset;
+
+ return -1; /* Not a real error - signals that path is not rooted */
+}
+
+static void path_trim_slashes(git_str *path)
+{
+ int ceiling = git_fs_path_root(path->ptr) + 1;
+
+ if (ceiling < 0)
+ return;
+
+ while (path->size > (size_t)ceiling) {
+ if (path->ptr[path->size-1] != '/')
+ break;
+
+ path->ptr[path->size-1] = '\0';
+ path->size--;
+ }
+}
+
+int git_fs_path_join_unrooted(
+ git_str *path_out, const char *path, const char *base, ssize_t *root_at)
+{
+ ssize_t root;
+
+ GIT_ASSERT_ARG(path_out);
+ GIT_ASSERT_ARG(path);
+
+ root = (ssize_t)git_fs_path_root(path);
+
+ if (base != NULL && root < 0) {
+ if (git_str_joinpath(path_out, base, path) < 0)
+ return -1;
+
+ root = (ssize_t)strlen(base);
+ } else {
+ if (git_str_sets(path_out, path) < 0)
+ return -1;
+
+ if (root < 0)
+ root = 0;
+ else if (base)
+ git_fs_path_equal_or_prefixed(base, path, &root);
+ }
+
+ if (root_at)
+ *root_at = root;
+
+ return 0;
+}
+
+void git_fs_path_squash_slashes(git_str *path)
+{
+ char *p, *q;
+
+ if (path->size == 0)
+ return;
+
+ for (p = path->ptr, q = path->ptr; *q; p++, q++) {
+ *p = *q;
+
+ while (*q == '/' && *(q+1) == '/') {
+ path->size--;
+ q++;
+ }
+ }
+
+ *p = '\0';
+}
+
+int git_fs_path_prettify(git_str *path_out, const char *path, const char *base)
+{
+ char buf[GIT_PATH_MAX];
+
+ GIT_ASSERT_ARG(path_out);
+ GIT_ASSERT_ARG(path);
+
+ /* construct path if needed */
+ if (base != NULL && git_fs_path_root(path) < 0) {
+ if (git_str_joinpath(path_out, base, path) < 0)
+ return -1;
+ path = path_out->ptr;
+ }
+
+ if (p_realpath(path, buf) == NULL) {
+ /* git_error_set resets the errno when dealing with a GIT_ERROR_OS kind of error */
+ int error = (errno == ENOENT || errno == ENOTDIR) ? GIT_ENOTFOUND : -1;
+ git_error_set(GIT_ERROR_OS, "failed to resolve path '%s'", path);
+
+ git_str_clear(path_out);
+
+ return error;
+ }
+
+ return git_str_sets(path_out, buf);
+}
+
+int git_fs_path_prettify_dir(git_str *path_out, const char *path, const char *base)
+{
+ int error = git_fs_path_prettify(path_out, path, base);
+ return (error < 0) ? error : git_fs_path_to_dir(path_out);
+}
+
+int git_fs_path_to_dir(git_str *path)
+{
+ if (path->asize > 0 &&
+ git_str_len(path) > 0 &&
+ path->ptr[git_str_len(path) - 1] != '/')
+ git_str_putc(path, '/');
+
+ return git_str_oom(path) ? -1 : 0;
+}
+
+void git_fs_path_string_to_dir(char *path, size_t size)
+{
+ size_t end = strlen(path);
+
+ if (end && path[end - 1] != '/' && end < size) {
+ path[end] = '/';
+ path[end + 1] = '\0';
+ }
+}
+
+int git__percent_decode(git_str *decoded_out, const char *input)
+{
+ int len, hi, lo, i;
+
+ GIT_ASSERT_ARG(decoded_out);
+ GIT_ASSERT_ARG(input);
+
+ len = (int)strlen(input);
+ git_str_clear(decoded_out);
+
+ for(i = 0; i < len; i++)
+ {
+ char c = input[i];
+
+ if (c != '%')
+ goto append;
+
+ if (i >= len - 2)
+ goto append;
+
+ hi = git__fromhex(input[i + 1]);
+ lo = git__fromhex(input[i + 2]);
+
+ if (hi < 0 || lo < 0)
+ goto append;
+
+ c = (char)(hi << 4 | lo);
+ i += 2;
+
+append:
+ if (git_str_putc(decoded_out, c) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+static int error_invalid_local_file_uri(const char *uri)
+{
+ git_error_set(GIT_ERROR_CONFIG, "'%s' is not a valid local file URI", uri);
+ return -1;
+}
+
+static int local_file_url_prefixlen(const char *file_url)
+{
+ int len = -1;
+
+ if (git__prefixcmp(file_url, "file://") == 0) {
+ if (file_url[7] == '/')
+ len = 8;
+ else if (git__prefixcmp(file_url + 7, "localhost/") == 0)
+ len = 17;
+ }
+
+ return len;
+}
+
+bool git_fs_path_is_local_file_url(const char *file_url)
+{
+ return (local_file_url_prefixlen(file_url) > 0);
+}
+
+int git_fs_path_fromurl(git_str *local_path_out, const char *file_url)
+{
+ int offset;
+
+ GIT_ASSERT_ARG(local_path_out);
+ GIT_ASSERT_ARG(file_url);
+
+ if ((offset = local_file_url_prefixlen(file_url)) < 0 ||
+ file_url[offset] == '\0' || file_url[offset] == '/')
+ return error_invalid_local_file_uri(file_url);
+
+#ifndef GIT_WIN32
+ offset--; /* A *nix absolute path starts with a forward slash */
+#endif
+
+ git_str_clear(local_path_out);
+ return git__percent_decode(local_path_out, file_url + offset);
+}
+
+int git_fs_path_walk_up(
+ git_str *path,
+ const char *ceiling,
+ int (*cb)(void *data, const char *),
+ void *data)
+{
+ int error = 0;
+ git_str iter;
+ ssize_t stop = 0, scan;
+ char oldc = '\0';
+
+ GIT_ASSERT_ARG(path);
+ GIT_ASSERT_ARG(cb);
+
+ if (ceiling != NULL) {
+ if (git__prefixcmp(path->ptr, ceiling) == 0)
+ stop = (ssize_t)strlen(ceiling);
+ else
+ stop = git_str_len(path);
+ }
+ scan = git_str_len(path);
+
+ /* empty path: yield only once */
+ if (!scan) {
+ error = cb(data, "");
+ if (error)
+ ensure_error_set(error);
+ return error;
+ }
+
+ iter.ptr = path->ptr;
+ iter.size = git_str_len(path);
+ iter.asize = path->asize;
+
+ while (scan >= stop) {
+ error = cb(data, iter.ptr);
+ iter.ptr[scan] = oldc;
+
+ if (error) {
+ ensure_error_set(error);
+ break;
+ }
+
+ scan = git_str_rfind_next(&iter, '/');
+ if (scan >= 0) {
+ scan++;
+ oldc = iter.ptr[scan];
+ iter.size = scan;
+ iter.ptr[scan] = '\0';
+ }
+ }
+
+ if (scan >= 0)
+ iter.ptr[scan] = oldc;
+
+ /* relative path: yield for the last component */
+ if (!error && stop == 0 && iter.ptr[0] != '/') {
+ error = cb(data, "");
+ if (error)
+ ensure_error_set(error);
+ }
+
+ return error;
+}
+
+bool git_fs_path_exists(const char *path)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(path, false);
+ return p_access(path, F_OK) == 0;
+}
+
+bool git_fs_path_isdir(const char *path)
+{
+ struct stat st;
+ if (p_stat(path, &st) < 0)
+ return false;
+
+ return S_ISDIR(st.st_mode) != 0;
+}
+
+bool git_fs_path_isfile(const char *path)
+{
+ struct stat st;
+
+ GIT_ASSERT_ARG_WITH_RETVAL(path, false);
+ if (p_stat(path, &st) < 0)
+ return false;
+
+ return S_ISREG(st.st_mode) != 0;
+}
+
+bool git_fs_path_islink(const char *path)
+{
+ struct stat st;
+
+ GIT_ASSERT_ARG_WITH_RETVAL(path, false);
+ if (p_lstat(path, &st) < 0)
+ return false;
+
+ return S_ISLNK(st.st_mode) != 0;
+}
+
+#ifdef GIT_WIN32
+
+bool git_fs_path_is_empty_dir(const char *path)
+{
+ git_win32_path filter_w;
+ bool empty = false;
+
+ if (git_win32__findfirstfile_filter(filter_w, path)) {
+ WIN32_FIND_DATAW findData;
+ HANDLE hFind = FindFirstFileW(filter_w, &findData);
+
+ /* FindFirstFile will fail if there are no children to the given
+ * path, which can happen if the given path is a file (and obviously
+ * has no children) or if the given path is an empty mount point.
+ * (Most directories have at least directory entries '.' and '..',
+ * but ridiculously another volume mounted in another drive letter's
+ * path space do not, and thus have nothing to enumerate.) If
+ * FindFirstFile fails, check if this is a directory-like thing
+ * (a mount point).
+ */
+ if (hFind == INVALID_HANDLE_VALUE)
+ return git_fs_path_isdir(path);
+
+ /* If the find handle was created successfully, then it's a directory */
+ empty = true;
+
+ do {
+ /* Allow the enumeration to return . and .. and still be considered
+ * empty. In the special case of drive roots (i.e. C:\) where . and
+ * .. do not occur, we can still consider the path to be an empty
+ * directory if there's nothing there. */
+ if (!git_fs_path_is_dot_or_dotdotW(findData.cFileName)) {
+ empty = false;
+ break;
+ }
+ } while (FindNextFileW(hFind, &findData));
+
+ FindClose(hFind);
+ }
+
+ return empty;
+}
+
+#else
+
+static int path_found_entry(void *payload, git_str *path)
+{
+ GIT_UNUSED(payload);
+ return !git_fs_path_is_dot_or_dotdot(path->ptr);
+}
+
+bool git_fs_path_is_empty_dir(const char *path)
+{
+ int error;
+ git_str dir = GIT_STR_INIT;
+
+ if (!git_fs_path_isdir(path))
+ return false;
+
+ if ((error = git_str_sets(&dir, path)) != 0)
+ git_error_clear();
+ else
+ error = git_fs_path_direach(&dir, 0, path_found_entry, NULL);
+
+ git_str_dispose(&dir);
+
+ return !error;
+}
+
+#endif
+
+int git_fs_path_set_error(int errno_value, const char *path, const char *action)
+{
+ switch (errno_value) {
+ case ENOENT:
+ case ENOTDIR:
+ git_error_set(GIT_ERROR_OS, "could not find '%s' to %s", path, action);
+ return GIT_ENOTFOUND;
+
+ case EINVAL:
+ case ENAMETOOLONG:
+ git_error_set(GIT_ERROR_OS, "invalid path for filesystem '%s'", path);
+ return GIT_EINVALIDSPEC;
+
+ case EEXIST:
+ git_error_set(GIT_ERROR_OS, "failed %s - '%s' already exists", action, path);
+ return GIT_EEXISTS;
+
+ case EACCES:
+ git_error_set(GIT_ERROR_OS, "failed %s - '%s' is locked", action, path);
+ return GIT_ELOCKED;
+
+ default:
+ git_error_set(GIT_ERROR_OS, "could not %s '%s'", action, path);
+ return -1;
+ }
+}
+
+int git_fs_path_lstat(const char *path, struct stat *st)
+{
+ if (p_lstat(path, st) == 0)
+ return 0;
+
+ return git_fs_path_set_error(errno, path, "stat");
+}
+
+static bool _check_dir_contents(
+ git_str *dir,
+ const char *sub,
+ bool (*predicate)(const char *))
+{
+ bool result;
+ size_t dir_size = git_str_len(dir);
+ size_t sub_size = strlen(sub);
+ size_t alloc_size;
+
+ /* leave base valid even if we could not make space for subdir */
+ if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, dir_size, sub_size) ||
+ GIT_ADD_SIZET_OVERFLOW(&alloc_size, alloc_size, 2) ||
+ git_str_try_grow(dir, alloc_size, false) < 0)
+ return false;
+
+ /* save excursion */
+ if (git_str_joinpath(dir, dir->ptr, sub) < 0)
+ return false;
+
+ result = predicate(dir->ptr);
+
+ /* restore path */
+ git_str_truncate(dir, dir_size);
+ return result;
+}
+
+bool git_fs_path_contains(git_str *dir, const char *item)
+{
+ return _check_dir_contents(dir, item, &git_fs_path_exists);
+}
+
+bool git_fs_path_contains_dir(git_str *base, const char *subdir)
+{
+ return _check_dir_contents(base, subdir, &git_fs_path_isdir);
+}
+
+bool git_fs_path_contains_file(git_str *base, const char *file)
+{
+ return _check_dir_contents(base, file, &git_fs_path_isfile);
+}
+
+int git_fs_path_find_dir(git_str *dir)
+{
+ int error = 0;
+ char buf[GIT_PATH_MAX];
+
+ if (p_realpath(dir->ptr, buf) != NULL)
+ error = git_str_sets(dir, buf);
+
+ /* call dirname if this is not a directory */
+ if (!error) /* && git_fs_path_isdir(dir->ptr) == false) */
+ error = (git_fs_path_dirname_r(dir, dir->ptr) < 0) ? -1 : 0;
+
+ if (!error)
+ error = git_fs_path_to_dir(dir);
+
+ return error;
+}
+
+int git_fs_path_resolve_relative(git_str *path, size_t ceiling)
+{
+ char *base, *to, *from, *next;
+ size_t len;
+
+ GIT_ERROR_CHECK_ALLOC_STR(path);
+
+ if (ceiling > path->size)
+ ceiling = path->size;
+
+ /* recognize drive prefixes, etc. that should not be backed over */
+ if (ceiling == 0)
+ ceiling = git_fs_path_root(path->ptr) + 1;
+
+ /* recognize URL prefixes that should not be backed over */
+ if (ceiling == 0) {
+ for (next = path->ptr; *next && git__isalpha(*next); ++next);
+ if (next[0] == ':' && next[1] == '/' && next[2] == '/')
+ ceiling = (next + 3) - path->ptr;
+ }
+
+ base = to = from = path->ptr + ceiling;
+
+ while (*from) {
+ for (next = from; *next && *next != '/'; ++next);
+
+ len = next - from;
+
+ if (len == 1 && from[0] == '.')
+ /* do nothing with singleton dot */;
+
+ else if (len == 2 && from[0] == '.' && from[1] == '.') {
+ /* error out if trying to up one from a hard base */
+ if (to == base && ceiling != 0) {
+ git_error_set(GIT_ERROR_INVALID,
+ "cannot strip root component off url");
+ return -1;
+ }
+
+ /* no more path segments to strip,
+ * use '../' as a new base path */
+ if (to == base) {
+ if (*next == '/')
+ len++;
+
+ if (to != from)
+ memmove(to, from, len);
+
+ to += len;
+ /* this is now the base, can't back up from a
+ * relative prefix */
+ base = to;
+ } else {
+ /* back up a path segment */
+ while (to > base && to[-1] == '/') to--;
+ while (to > base && to[-1] != '/') to--;
+ }
+ } else {
+ if (*next == '/' && *from != '/')
+ len++;
+
+ if (to != from)
+ memmove(to, from, len);
+
+ to += len;
+ }
+
+ from += len;
+
+ while (*from == '/') from++;
+ }
+
+ *to = '\0';
+
+ path->size = to - path->ptr;
+
+ return 0;
+}
+
+int git_fs_path_apply_relative(git_str *target, const char *relpath)
+{
+ return git_str_joinpath(target, git_str_cstr(target), relpath) ||
+ git_fs_path_resolve_relative(target, 0);
+}
+
+int git_fs_path_cmp(
+ const char *name1, size_t len1, int isdir1,
+ const char *name2, size_t len2, int isdir2,
+ int (*compare)(const char *, const char *, size_t))
+{
+ unsigned char c1, c2;
+ size_t len = len1 < len2 ? len1 : len2;
+ int cmp;
+
+ cmp = compare(name1, name2, len);
+ if (cmp)
+ return cmp;
+
+ c1 = name1[len];
+ c2 = name2[len];
+
+ if (c1 == '\0' && isdir1)
+ c1 = '/';
+
+ if (c2 == '\0' && isdir2)
+ c2 = '/';
+
+ return (c1 < c2) ? -1 : (c1 > c2) ? 1 : 0;
+}
+
+size_t git_fs_path_common_dirlen(const char *one, const char *two)
+{
+ const char *p, *q, *dirsep = NULL;
+
+ for (p = one, q = two; *p && *q; p++, q++) {
+ if (*p == '/' && *q == '/')
+ dirsep = p;
+ else if (*p != *q)
+ break;
+ }
+
+ return dirsep ? (dirsep - one) + 1 : 0;
+}
+
+int git_fs_path_make_relative(git_str *path, const char *parent)
+{
+ const char *p, *q, *p_dirsep, *q_dirsep;
+ size_t plen = path->size, newlen, alloclen, depth = 1, i, offset;
+
+ for (p_dirsep = p = path->ptr, q_dirsep = q = parent; *p && *q; p++, q++) {
+ if (*p == '/' && *q == '/') {
+ p_dirsep = p;
+ q_dirsep = q;
+ }
+ else if (*p != *q)
+ break;
+ }
+
+ /* need at least 1 common path segment */
+ if ((p_dirsep == path->ptr || q_dirsep == parent) &&
+ (*p_dirsep != '/' || *q_dirsep != '/')) {
+ git_error_set(GIT_ERROR_INVALID,
+ "%s is not a parent of %s", parent, path->ptr);
+ return GIT_ENOTFOUND;
+ }
+
+ if (*p == '/' && !*q)
+ p++;
+ else if (!*p && *q == '/')
+ q++;
+ else if (!*p && !*q)
+ return git_str_clear(path), 0;
+ else {
+ p = p_dirsep + 1;
+ q = q_dirsep + 1;
+ }
+
+ plen -= (p - path->ptr);
+
+ if (!*q)
+ return git_str_set(path, p, plen);
+
+ for (; (q = strchr(q, '/')) && *(q + 1); q++)
+ depth++;
+
+ GIT_ERROR_CHECK_ALLOC_MULTIPLY(&newlen, depth, 3);
+ GIT_ERROR_CHECK_ALLOC_ADD(&newlen, newlen, plen);
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, newlen, 1);
+
+ /* save the offset as we might realllocate the pointer */
+ offset = p - path->ptr;
+ if (git_str_try_grow(path, alloclen, 1) < 0)
+ return -1;
+ p = path->ptr + offset;
+
+ memmove(path->ptr + (depth * 3), p, plen + 1);
+
+ for (i = 0; i < depth; i++)
+ memcpy(path->ptr + (i * 3), "../", 3);
+
+ path->size = newlen;
+ return 0;
+}
+
+bool git_fs_path_has_non_ascii(const char *path, size_t pathlen)
+{
+ const uint8_t *scan = (const uint8_t *)path, *end;
+
+ for (end = scan + pathlen; scan < end; ++scan)
+ if (*scan & 0x80)
+ return true;
+
+ return false;
+}
+
+#ifdef GIT_USE_ICONV
+
+int git_fs_path_iconv_init_precompose(git_fs_path_iconv_t *ic)
+{
+ git_str_init(&ic->buf, 0);
+ ic->map = iconv_open(GIT_PATH_REPO_ENCODING, GIT_PATH_NATIVE_ENCODING);
+ return 0;
+}
+
+void git_fs_path_iconv_clear(git_fs_path_iconv_t *ic)
+{
+ if (ic) {
+ if (ic->map != (iconv_t)-1)
+ iconv_close(ic->map);
+ git_str_dispose(&ic->buf);
+ }
+}
+
+int git_fs_path_iconv(git_fs_path_iconv_t *ic, const char **in, size_t *inlen)
+{
+ char *nfd = (char*)*in, *nfc;
+ size_t nfdlen = *inlen, nfclen, wantlen = nfdlen, alloclen, rv;
+ int retry = 1;
+
+ if (!ic || ic->map == (iconv_t)-1 ||
+ !git_fs_path_has_non_ascii(*in, *inlen))
+ return 0;
+
+ git_str_clear(&ic->buf);
+
+ while (1) {
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, wantlen, 1);
+ if (git_str_grow(&ic->buf, alloclen) < 0)
+ return -1;
+
+ nfc = ic->buf.ptr + ic->buf.size;
+ nfclen = ic->buf.asize - ic->buf.size;
+
+ rv = iconv(ic->map, &nfd, &nfdlen, &nfc, &nfclen);
+
+ ic->buf.size = (nfc - ic->buf.ptr);
+
+ if (rv != (size_t)-1)
+ break;
+
+ /* if we cannot convert the data (probably because iconv thinks
+ * it is not valid UTF-8 source data), then use original data
+ */
+ if (errno != E2BIG)
+ return 0;
+
+ /* make space for 2x the remaining data to be converted
+ * (with per retry overhead to avoid infinite loops)
+ */
+ wantlen = ic->buf.size + max(nfclen, nfdlen) * 2 + (size_t)(retry * 4);
+
+ if (retry++ > 4)
+ goto fail;
+ }
+
+ ic->buf.ptr[ic->buf.size] = '\0';
+
+ *in = ic->buf.ptr;
+ *inlen = ic->buf.size;
+
+ return 0;
+
+fail:
+ git_error_set(GIT_ERROR_OS, "unable to convert unicode path data");
+ return -1;
+}
+
+static const char *nfc_file = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D";
+static const char *nfd_file = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D";
+
+/* Check if the platform is decomposing unicode data for us. We will
+ * emulate core Git and prefer to use precomposed unicode data internally
+ * on these platforms, composing the decomposed unicode on the fly.
+ *
+ * This mainly happens on the Mac where HDFS stores filenames as
+ * decomposed unicode. Even on VFAT and SAMBA file systems, the Mac will
+ * return decomposed unicode from readdir() even when the actual
+ * filesystem is storing precomposed unicode.
+ */
+bool git_fs_path_does_decompose_unicode(const char *root)
+{
+ git_str nfc_path = GIT_STR_INIT;
+ git_str nfd_path = GIT_STR_INIT;
+ int fd;
+ bool found_decomposed = false;
+ size_t orig_len;
+ const char *trailer;
+
+ /* Create a file using a precomposed path and then try to find it
+ * using the decomposed name. If the lookup fails, then we will mark
+ * that we should precompose unicode for this repository.
+ */
+ if (git_str_joinpath(&nfc_path, root, nfc_file) < 0)
+ goto done;
+
+ /* record original path length before trailer */
+ orig_len = nfc_path.size;
+
+ if ((fd = git_futils_mktmp(&nfc_path, nfc_path.ptr, 0666)) < 0)
+ goto done;
+ p_close(fd);
+
+ trailer = nfc_path.ptr + orig_len;
+
+ /* try to look up as NFD path */
+ if (git_str_joinpath(&nfd_path, root, nfd_file) < 0 ||
+ git_str_puts(&nfd_path, trailer) < 0)
+ goto done;
+
+ found_decomposed = git_fs_path_exists(nfd_path.ptr);
+
+ /* remove temporary file (using original precomposed path) */
+ (void)p_unlink(nfc_path.ptr);
+
+done:
+ git_str_dispose(&nfc_path);
+ git_str_dispose(&nfd_path);
+ return found_decomposed;
+}
+
+#else
+
+bool git_fs_path_does_decompose_unicode(const char *root)
+{
+ GIT_UNUSED(root);
+ return false;
+}
+
+#endif
+
+#if defined(__sun) || defined(__GNU__)
+typedef char path_dirent_data[sizeof(struct dirent) + FILENAME_MAX + 1];
+#else
+typedef struct dirent path_dirent_data;
+#endif
+
+int git_fs_path_direach(
+ git_str *path,
+ uint32_t flags,
+ int (*fn)(void *, git_str *),
+ void *arg)
+{
+ int error = 0;
+ ssize_t wd_len;
+ DIR *dir;
+ struct dirent *de;
+
+#ifdef GIT_USE_ICONV
+ git_fs_path_iconv_t ic = GIT_PATH_ICONV_INIT;
+#endif
+
+ GIT_UNUSED(flags);
+
+ if (git_fs_path_to_dir(path) < 0)
+ return -1;
+
+ wd_len = git_str_len(path);
+
+ if ((dir = opendir(path->ptr)) == NULL) {
+ git_error_set(GIT_ERROR_OS, "failed to open directory '%s'", path->ptr);
+ if (errno == ENOENT)
+ return GIT_ENOTFOUND;
+
+ return -1;
+ }
+
+#ifdef GIT_USE_ICONV
+ if ((flags & GIT_FS_PATH_DIR_PRECOMPOSE_UNICODE) != 0)
+ (void)git_fs_path_iconv_init_precompose(&ic);
+#endif
+
+ while ((de = readdir(dir)) != NULL) {
+ const char *de_path = de->d_name;
+ size_t de_len = strlen(de_path);
+
+ if (git_fs_path_is_dot_or_dotdot(de_path))
+ continue;
+
+#ifdef GIT_USE_ICONV
+ if ((error = git_fs_path_iconv(&ic, &de_path, &de_len)) < 0)
+ break;
+#endif
+
+ if ((error = git_str_put(path, de_path, de_len)) < 0)
+ break;
+
+ git_error_clear();
+ error = fn(arg, path);
+
+ git_str_truncate(path, wd_len); /* restore path */
+
+ /* Only set our own error if the callback did not set one already */
+ if (error != 0) {
+ if (!git_error_last())
+ ensure_error_set(error);
+
+ break;
+ }
+ }
+
+ closedir(dir);
+
+#ifdef GIT_USE_ICONV
+ git_fs_path_iconv_clear(&ic);
+#endif
+
+ return error;
+}
+
+#if defined(GIT_WIN32) && !defined(__MINGW32__)
+
+/* Using _FIND_FIRST_EX_LARGE_FETCH may increase performance in Windows 7
+ * and better.
+ */
+#ifndef FIND_FIRST_EX_LARGE_FETCH
+# define FIND_FIRST_EX_LARGE_FETCH 2
+#endif
+
+int git_fs_path_diriter_init(
+ git_fs_path_diriter *diriter,
+ const char *path,
+ unsigned int flags)
+{
+ git_win32_path path_filter;
+
+ static int is_win7_or_later = -1;
+ if (is_win7_or_later < 0)
+ is_win7_or_later = git_has_win32_version(6, 1, 0);
+
+ GIT_ASSERT_ARG(diriter);
+ GIT_ASSERT_ARG(path);
+
+ memset(diriter, 0, sizeof(git_fs_path_diriter));
+ diriter->handle = INVALID_HANDLE_VALUE;
+
+ if (git_str_puts(&diriter->path_utf8, path) < 0)
+ return -1;
+
+ path_trim_slashes(&diriter->path_utf8);
+
+ if (diriter->path_utf8.size == 0) {
+ git_error_set(GIT_ERROR_FILESYSTEM, "could not open directory '%s'", path);
+ return -1;
+ }
+
+ if ((diriter->parent_len = git_win32_path_from_utf8(diriter->path, diriter->path_utf8.ptr)) < 0 ||
+ !git_win32__findfirstfile_filter(path_filter, diriter->path_utf8.ptr)) {
+ git_error_set(GIT_ERROR_OS, "could not parse the directory path '%s'", path);
+ return -1;
+ }
+
+ diriter->handle = FindFirstFileExW(
+ path_filter,
+ is_win7_or_later ? FindExInfoBasic : FindExInfoStandard,
+ &diriter->current,
+ FindExSearchNameMatch,
+ NULL,
+ is_win7_or_later ? FIND_FIRST_EX_LARGE_FETCH : 0);
+
+ if (diriter->handle == INVALID_HANDLE_VALUE) {
+ git_error_set(GIT_ERROR_OS, "could not open directory '%s'", path);
+ return -1;
+ }
+
+ diriter->parent_utf8_len = diriter->path_utf8.size;
+ diriter->flags = flags;
+ return 0;
+}
+
+static int diriter_update_paths(git_fs_path_diriter *diriter)
+{
+ size_t filename_len, path_len;
+
+ filename_len = wcslen(diriter->current.cFileName);
+
+ if (GIT_ADD_SIZET_OVERFLOW(&path_len, diriter->parent_len, filename_len) ||
+ GIT_ADD_SIZET_OVERFLOW(&path_len, path_len, 2))
+ return -1;
+
+ if (path_len > GIT_WIN_PATH_UTF16) {
+ git_error_set(GIT_ERROR_FILESYSTEM,
+ "invalid path '%.*ls\\%ls' (path too long)",
+ diriter->parent_len, diriter->path, diriter->current.cFileName);
+ return -1;
+ }
+
+ diriter->path[diriter->parent_len] = L'\\';
+ memcpy(&diriter->path[diriter->parent_len+1],
+ diriter->current.cFileName, filename_len * sizeof(wchar_t));
+ diriter->path[path_len-1] = L'\0';
+
+ git_str_truncate(&diriter->path_utf8, diriter->parent_utf8_len);
+
+ if (diriter->parent_utf8_len > 0 &&
+ diriter->path_utf8.ptr[diriter->parent_utf8_len-1] != '/')
+ git_str_putc(&diriter->path_utf8, '/');
+
+ git_str_put_w(&diriter->path_utf8, diriter->current.cFileName, filename_len);
+
+ if (git_str_oom(&diriter->path_utf8))
+ return -1;
+
+ return 0;
+}
+
+int git_fs_path_diriter_next(git_fs_path_diriter *diriter)
+{
+ bool skip_dot = !(diriter->flags & GIT_FS_PATH_DIR_INCLUDE_DOT_AND_DOTDOT);
+
+ do {
+ /* Our first time through, we already have the data from
+ * FindFirstFileW. Use it, otherwise get the next file.
+ */
+ if (!diriter->needs_next)
+ diriter->needs_next = 1;
+ else if (!FindNextFileW(diriter->handle, &diriter->current))
+ return GIT_ITEROVER;
+ } while (skip_dot && git_fs_path_is_dot_or_dotdotW(diriter->current.cFileName));
+
+ if (diriter_update_paths(diriter) < 0)
+ return -1;
+
+ return 0;
+}
+
+int git_fs_path_diriter_filename(
+ const char **out,
+ size_t *out_len,
+ git_fs_path_diriter *diriter)
+{
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(out_len);
+ GIT_ASSERT_ARG(diriter);
+ GIT_ASSERT(diriter->path_utf8.size > diriter->parent_utf8_len);
+
+ *out = &diriter->path_utf8.ptr[diriter->parent_utf8_len+1];
+ *out_len = diriter->path_utf8.size - diriter->parent_utf8_len - 1;
+ return 0;
+}
+
+int git_fs_path_diriter_fullpath(
+ const char **out,
+ size_t *out_len,
+ git_fs_path_diriter *diriter)
+{
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(out_len);
+ GIT_ASSERT_ARG(diriter);
+
+ *out = diriter->path_utf8.ptr;
+ *out_len = diriter->path_utf8.size;
+ return 0;
+}
+
+int git_fs_path_diriter_stat(struct stat *out, git_fs_path_diriter *diriter)
+{
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(diriter);
+
+ return git_win32__file_attribute_to_stat(out,
+ (WIN32_FILE_ATTRIBUTE_DATA *)&diriter->current,
+ diriter->path);
+}
+
+void git_fs_path_diriter_free(git_fs_path_diriter *diriter)
+{
+ if (diriter == NULL)
+ return;
+
+ git_str_dispose(&diriter->path_utf8);
+
+ if (diriter->handle != INVALID_HANDLE_VALUE) {
+ FindClose(diriter->handle);
+ diriter->handle = INVALID_HANDLE_VALUE;
+ }
+}
+
+#else
+
+int git_fs_path_diriter_init(
+ git_fs_path_diriter *diriter,
+ const char *path,
+ unsigned int flags)
+{
+ GIT_ASSERT_ARG(diriter);
+ GIT_ASSERT_ARG(path);
+
+ memset(diriter, 0, sizeof(git_fs_path_diriter));
+
+ if (git_str_puts(&diriter->path, path) < 0)
+ return -1;
+
+ path_trim_slashes(&diriter->path);
+
+ if (diriter->path.size == 0) {
+ git_error_set(GIT_ERROR_FILESYSTEM, "could not open directory '%s'", path);
+ return -1;
+ }
+
+ if ((diriter->dir = opendir(diriter->path.ptr)) == NULL) {
+ git_str_dispose(&diriter->path);
+
+ git_error_set(GIT_ERROR_OS, "failed to open directory '%s'", path);
+ return -1;
+ }
+
+#ifdef GIT_USE_ICONV
+ if ((flags & GIT_FS_PATH_DIR_PRECOMPOSE_UNICODE) != 0)
+ (void)git_fs_path_iconv_init_precompose(&diriter->ic);
+#endif
+
+ diriter->parent_len = diriter->path.size;
+ diriter->flags = flags;
+
+ return 0;
+}
+
+int git_fs_path_diriter_next(git_fs_path_diriter *diriter)
+{
+ struct dirent *de;
+ const char *filename;
+ size_t filename_len;
+ bool skip_dot = !(diriter->flags & GIT_FS_PATH_DIR_INCLUDE_DOT_AND_DOTDOT);
+ int error = 0;
+
+ GIT_ASSERT_ARG(diriter);
+
+ errno = 0;
+
+ do {
+ if ((de = readdir(diriter->dir)) == NULL) {
+ if (!errno)
+ return GIT_ITEROVER;
+
+ git_error_set(GIT_ERROR_OS,
+ "could not read directory '%s'", diriter->path.ptr);
+ return -1;
+ }
+ } while (skip_dot && git_fs_path_is_dot_or_dotdot(de->d_name));
+
+ filename = de->d_name;
+ filename_len = strlen(filename);
+
+#ifdef GIT_USE_ICONV
+ if ((diriter->flags & GIT_FS_PATH_DIR_PRECOMPOSE_UNICODE) != 0 &&
+ (error = git_fs_path_iconv(&diriter->ic, &filename, &filename_len)) < 0)
+ return error;
+#endif
+
+ git_str_truncate(&diriter->path, diriter->parent_len);
+
+ if (diriter->parent_len > 0 &&
+ diriter->path.ptr[diriter->parent_len-1] != '/')
+ git_str_putc(&diriter->path, '/');
+
+ git_str_put(&diriter->path, filename, filename_len);
+
+ if (git_str_oom(&diriter->path))
+ return -1;
+
+ return error;
+}
+
+int git_fs_path_diriter_filename(
+ const char **out,
+ size_t *out_len,
+ git_fs_path_diriter *diriter)
+{
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(out_len);
+ GIT_ASSERT_ARG(diriter);
+ GIT_ASSERT(diriter->path.size > diriter->parent_len);
+
+ *out = &diriter->path.ptr[diriter->parent_len+1];
+ *out_len = diriter->path.size - diriter->parent_len - 1;
+ return 0;
+}
+
+int git_fs_path_diriter_fullpath(
+ const char **out,
+ size_t *out_len,
+ git_fs_path_diriter *diriter)
+{
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(out_len);
+ GIT_ASSERT_ARG(diriter);
+
+ *out = diriter->path.ptr;
+ *out_len = diriter->path.size;
+ return 0;
+}
+
+int git_fs_path_diriter_stat(struct stat *out, git_fs_path_diriter *diriter)
+{
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(diriter);
+
+ return git_fs_path_lstat(diriter->path.ptr, out);
+}
+
+void git_fs_path_diriter_free(git_fs_path_diriter *diriter)
+{
+ if (diriter == NULL)
+ return;
+
+ if (diriter->dir) {
+ closedir(diriter->dir);
+ diriter->dir = NULL;
+ }
+
+#ifdef GIT_USE_ICONV
+ git_fs_path_iconv_clear(&diriter->ic);
+#endif
+
+ git_str_dispose(&diriter->path);
+}
+
+#endif
+
+int git_fs_path_dirload(
+ git_vector *contents,
+ const char *path,
+ size_t prefix_len,
+ uint32_t flags)
+{
+ git_fs_path_diriter iter = GIT_FS_PATH_DIRITER_INIT;
+ const char *name;
+ size_t name_len;
+ char *dup;
+ int error;
+
+ GIT_ASSERT_ARG(contents);
+ GIT_ASSERT_ARG(path);
+
+ if ((error = git_fs_path_diriter_init(&iter, path, flags)) < 0)
+ return error;
+
+ while ((error = git_fs_path_diriter_next(&iter)) == 0) {
+ if ((error = git_fs_path_diriter_fullpath(&name, &name_len, &iter)) < 0)
+ break;
+
+ GIT_ASSERT(name_len > prefix_len);
+
+ dup = git__strndup(name + prefix_len, name_len - prefix_len);
+ GIT_ERROR_CHECK_ALLOC(dup);
+
+ if ((error = git_vector_insert(contents, dup)) < 0)
+ break;
+ }
+
+ if (error == GIT_ITEROVER)
+ error = 0;
+
+ git_fs_path_diriter_free(&iter);
+ return error;
+}
+
+int git_fs_path_from_url_or_path(git_str *local_path_out, const char *url_or_path)
+{
+ if (git_fs_path_is_local_file_url(url_or_path))
+ return git_fs_path_fromurl(local_path_out, url_or_path);
+ else
+ return git_str_sets(local_path_out, url_or_path);
+}
+
+/* Reject paths like AUX or COM1, or those versions that end in a dot or
+ * colon. ("AUX." or "AUX:")
+ */
+GIT_INLINE(bool) validate_dospath(
+ const char *component,
+ size_t len,
+ const char dospath[3],
+ bool trailing_num)
+{
+ size_t last = trailing_num ? 4 : 3;
+
+ if (len < last || git__strncasecmp(component, dospath, 3) != 0)
+ return true;
+
+ if (trailing_num && (component[3] < '1' || component[3] > '9'))
+ return true;
+
+ return (len > last &&
+ component[last] != '.' &&
+ component[last] != ':');
+}
+
+GIT_INLINE(bool) validate_char(unsigned char c, unsigned int flags)
+{
+ if ((flags & GIT_FS_PATH_REJECT_BACKSLASH) && c == '\\')
+ return false;
+
+ if ((flags & GIT_FS_PATH_REJECT_SLASH) && c == '/')
+ return false;
+
+ if (flags & GIT_FS_PATH_REJECT_NT_CHARS) {
+ if (c < 32)
+ return false;
+
+ switch (c) {
+ case '<':
+ case '>':
+ case ':':
+ case '"':
+ case '|':
+ case '?':
+ case '*':
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*
+ * We fundamentally don't like some paths when dealing with user-inputted
+ * strings (to avoid escaping a sandbox): we don't want dot or dot-dot
+ * anywhere, we want to avoid writing weird paths on Windows that can't
+ * be handled by tools that use the non-\\?\ APIs, we don't want slashes
+ * or double slashes at the end of paths that can make them ambiguous.
+ *
+ * For checkout, we don't want to recurse into ".git" either.
+ */
+static bool validate_component(
+ const char *component,
+ size_t len,
+ unsigned int flags)
+{
+ if (len == 0)
+ return !(flags & GIT_FS_PATH_REJECT_EMPTY_COMPONENT);
+
+ if ((flags & GIT_FS_PATH_REJECT_TRAVERSAL) &&
+ len == 1 && component[0] == '.')
+ return false;
+
+ if ((flags & GIT_FS_PATH_REJECT_TRAVERSAL) &&
+ len == 2 && component[0] == '.' && component[1] == '.')
+ return false;
+
+ if ((flags & GIT_FS_PATH_REJECT_TRAILING_DOT) &&
+ component[len - 1] == '.')
+ return false;
+
+ if ((flags & GIT_FS_PATH_REJECT_TRAILING_SPACE) &&
+ component[len - 1] == ' ')
+ return false;
+
+ if ((flags & GIT_FS_PATH_REJECT_TRAILING_COLON) &&
+ component[len - 1] == ':')
+ return false;
+
+ if (flags & GIT_FS_PATH_REJECT_DOS_PATHS) {
+ if (!validate_dospath(component, len, "CON", false) ||
+ !validate_dospath(component, len, "PRN", false) ||
+ !validate_dospath(component, len, "AUX", false) ||
+ !validate_dospath(component, len, "NUL", false) ||
+ !validate_dospath(component, len, "COM", true) ||
+ !validate_dospath(component, len, "LPT", true))
+ return false;
+ }
+
+ return true;
+}
+
+#ifdef GIT_WIN32
+GIT_INLINE(bool) validate_length(
+ const char *path,
+ size_t len,
+ size_t utf8_char_len)
+{
+ GIT_UNUSED(path);
+ GIT_UNUSED(len);
+
+ return (utf8_char_len <= MAX_PATH);
+}
+#endif
+
+bool git_fs_path_str_is_valid_ext(
+ const git_str *path,
+ unsigned int flags,
+ bool (*validate_char_cb)(char ch, void *payload),
+ bool (*validate_component_cb)(const char *component, size_t len, void *payload),
+ bool (*validate_length_cb)(const char *path, size_t len, size_t utf8_char_len),
+ void *payload)
+{
+ const char *start, *c;
+ size_t len = 0;
+
+ if (!flags)
+ return true;
+
+ for (start = c = path->ptr; *c && len < path->size; c++, len++) {
+ if (!validate_char(*c, flags))
+ return false;
+
+ if (validate_char_cb && !validate_char_cb(*c, payload))
+ return false;
+
+ if (*c != '/')
+ continue;
+
+ if (!validate_component(start, (c - start), flags))
+ return false;
+
+ if (validate_component_cb &&
+ !validate_component_cb(start, (c - start), payload))
+ return false;
+
+ start = c + 1;
+ }
+
+ /*
+ * We want to support paths specified as either `const char *`
+ * or `git_str *`; we pass size as `SIZE_MAX` when we use a
+ * `const char *` to avoid a `strlen`. Ensure that we didn't
+ * have a NUL in the buffer if there was a non-SIZE_MAX length.
+ */
+ if (path->size != SIZE_MAX && len != path->size)
+ return false;
+
+ if (!validate_component(start, (c - start), flags))
+ return false;
+
+ if (validate_component_cb &&
+ !validate_component_cb(start, (c - start), payload))
+ return false;
+
+#ifdef GIT_WIN32
+ if ((flags & GIT_FS_PATH_REJECT_LONG_PATHS) != 0) {
+ size_t utf8_len = git_utf8_char_length(path->ptr, len);
+
+ if (!validate_length(path->ptr, len, utf8_len))
+ return false;
+
+ if (validate_length_cb &&
+ !validate_length_cb(path->ptr, len, utf8_len))
+ return false;
+ }
+#else
+ GIT_UNUSED(validate_length_cb);
+#endif
+
+ return true;
+}
+
+int git_fs_path_validate_str_length_with_suffix(
+ git_str *path,
+ size_t suffix_len)
+{
+#ifdef GIT_WIN32
+ size_t utf8_len = git_utf8_char_length(path->ptr, path->size);
+ size_t total_len;
+
+ if (GIT_ADD_SIZET_OVERFLOW(&total_len, utf8_len, suffix_len) ||
+ total_len > MAX_PATH) {
+
+ git_error_set(GIT_ERROR_FILESYSTEM, "path too long: '%.*s'",
+ (int)path->size, path->ptr);
+ return -1;
+ }
+#else
+ GIT_UNUSED(path);
+ GIT_UNUSED(suffix_len);
+#endif
+
+ return 0;
+}
+
+int git_fs_path_normalize_slashes(git_str *out, const char *path)
+{
+ int error;
+ char *p;
+
+ if ((error = git_str_puts(out, path)) < 0)
+ return error;
+
+ for (p = out->ptr; *p; p++) {
+ if (*p == '\\')
+ *p = '/';
+ }
+
+ return 0;
+}
+
+bool git_fs_path_supports_symlinks(const char *dir)
+{
+ git_str path = GIT_STR_INIT;
+ bool supported = false;
+ struct stat st;
+ int fd;
+
+ if ((fd = git_futils_mktmp(&path, dir, 0666)) < 0 ||
+ p_close(fd) < 0 ||
+ p_unlink(path.ptr) < 0 ||
+ p_symlink("testing", path.ptr) < 0 ||
+ p_lstat(path.ptr, &st) < 0)
+ goto done;
+
+ supported = (S_ISLNK(st.st_mode) != 0);
+done:
+ if (path.size)
+ (void)p_unlink(path.ptr);
+ git_str_dispose(&path);
+ return supported;
+}
+
+static git_fs_path_owner_t mock_owner = GIT_FS_PATH_OWNER_NONE;
+
+void git_fs_path__set_owner(git_fs_path_owner_t owner)
+{
+ mock_owner = owner;
+}
+
+#ifdef GIT_WIN32
+static PSID *sid_dup(PSID sid)
+{
+ DWORD len;
+ PSID dup;
+
+ len = GetLengthSid(sid);
+
+ if ((dup = git__malloc(len)) == NULL)
+ return NULL;
+
+ if (!CopySid(len, dup, sid)) {
+ git_error_set(GIT_ERROR_OS, "could not duplicate sid");
+ git__free(dup);
+ return NULL;
+ }
+
+ return dup;
+}
+
+static int current_user_sid(PSID *out)
+{
+ TOKEN_USER *info = NULL;
+ HANDLE token = NULL;
+ DWORD len = 0;
+ int error = -1;
+
+ if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) {
+ git_error_set(GIT_ERROR_OS, "could not lookup process information");
+ goto done;
+ }
+
+ if (GetTokenInformation(token, TokenUser, NULL, 0, &len) ||
+ GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ git_error_set(GIT_ERROR_OS, "could not lookup token metadata");
+ goto done;
+ }
+
+ info = git__malloc(len);
+ GIT_ERROR_CHECK_ALLOC(info);
+
+ if (!GetTokenInformation(token, TokenUser, info, len, &len)) {
+ git_error_set(GIT_ERROR_OS, "could not lookup current user");
+ goto done;
+ }
+
+ if ((*out = sid_dup(info->User.Sid)))
+ error = 0;
+
+done:
+ if (token)
+ CloseHandle(token);
+
+ git__free(info);
+ return error;
+}
+
+static int file_owner_sid(PSID *out, const char *path)
+{
+ git_win32_path path_w32;
+ PSECURITY_DESCRIPTOR descriptor = NULL;
+ PSID owner_sid;
+ DWORD ret;
+ int error = GIT_EINVALID;
+
+ if (git_win32_path_from_utf8(path_w32, path) < 0)
+ return -1;
+
+ ret = GetNamedSecurityInfoW(path_w32, SE_FILE_OBJECT,
+ OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
+ &owner_sid, NULL, NULL, NULL, &descriptor);
+
+ if (ret == ERROR_FILE_NOT_FOUND || ret == ERROR_PATH_NOT_FOUND)
+ error = GIT_ENOTFOUND;
+ else if (ret != ERROR_SUCCESS)
+ git_error_set(GIT_ERROR_OS, "failed to get security information");
+ else if (!IsValidSid(owner_sid))
+ git_error_set(GIT_ERROR_OS, "file owner is not valid");
+ else if ((*out = sid_dup(owner_sid)))
+ error = 0;
+
+ if (descriptor)
+ LocalFree(descriptor);
+
+ return error;
+}
+
+int git_fs_path_owner_is(
+ bool *out,
+ const char *path,
+ git_fs_path_owner_t owner_type)
+{
+ PSID owner_sid = NULL, user_sid = NULL;
+ BOOL is_admin, admin_owned;
+ int error;
+
+ if (mock_owner) {
+ *out = ((mock_owner & owner_type) != 0);
+ return 0;
+ }
+
+ if ((error = file_owner_sid(&owner_sid, path)) < 0)
+ goto done;
+
+ if ((owner_type & GIT_FS_PATH_OWNER_CURRENT_USER) != 0) {
+ if ((error = current_user_sid(&user_sid)) < 0)
+ goto done;
+
+ if (EqualSid(owner_sid, user_sid)) {
+ *out = true;
+ goto done;
+ }
+ }
+
+ admin_owned =
+ IsWellKnownSid(owner_sid, WinBuiltinAdministratorsSid) ||
+ IsWellKnownSid(owner_sid, WinLocalSystemSid);
+
+ if (admin_owned &&
+ (owner_type & GIT_FS_PATH_OWNER_ADMINISTRATOR) != 0) {
+ *out = true;
+ goto done;
+ }
+
+ if (admin_owned &&
+ (owner_type & GIT_FS_PATH_USER_IS_ADMINISTRATOR) != 0 &&
+ CheckTokenMembership(NULL, owner_sid, &is_admin) &&
+ is_admin) {
+ *out = true;
+ goto done;
+ }
+
+ *out = false;
+
+done:
+ git__free(owner_sid);
+ git__free(user_sid);
+ return error;
+}
+
+#else
+
+static int sudo_uid_lookup(uid_t *out)
+{
+ git_str uid_str = GIT_STR_INIT;
+ int64_t uid;
+ int error;
+
+ if ((error = git__getenv(&uid_str, "SUDO_UID")) == 0 &&
+ (error = git__strntol64(&uid, uid_str.ptr, uid_str.size, NULL, 10)) == 0 &&
+ uid == (int64_t)((uid_t)uid)) {
+ *out = (uid_t)uid;
+ }
+
+ git_str_dispose(&uid_str);
+ return error;
+}
+
+int git_fs_path_owner_is(
+ bool *out,
+ const char *path,
+ git_fs_path_owner_t owner_type)
+{
+ struct stat st;
+ uid_t euid, sudo_uid;
+
+ if (mock_owner) {
+ *out = ((mock_owner & owner_type) != 0);
+ return 0;
+ }
+
+ euid = geteuid();
+
+ if (p_lstat(path, &st) != 0) {
+ if (errno == ENOENT)
+ return GIT_ENOTFOUND;
+
+ git_error_set(GIT_ERROR_OS, "could not stat '%s'", path);
+ return -1;
+ }
+
+ if ((owner_type & GIT_FS_PATH_OWNER_CURRENT_USER) != 0 &&
+ st.st_uid == euid) {
+ *out = true;
+ return 0;
+ }
+
+ if ((owner_type & GIT_FS_PATH_OWNER_ADMINISTRATOR) != 0 &&
+ st.st_uid == 0) {
+ *out = true;
+ return 0;
+ }
+
+ if ((owner_type & GIT_FS_PATH_OWNER_RUNNING_SUDO) != 0 &&
+ euid == 0 &&
+ sudo_uid_lookup(&sudo_uid) == 0 &&
+ st.st_uid == sudo_uid) {
+ *out = true;
+ return 0;
+ }
+
+ *out = false;
+ return 0;
+}
+
+#endif
+
+int git_fs_path_owner_is_current_user(bool *out, const char *path)
+{
+ return git_fs_path_owner_is(out, path, GIT_FS_PATH_OWNER_CURRENT_USER);
+}
+
+int git_fs_path_owner_is_system(bool *out, const char *path)
+{
+ return git_fs_path_owner_is(out, path, GIT_FS_PATH_OWNER_ADMINISTRATOR);
+}
+
+int git_fs_path_find_executable(git_str *fullpath, const char *executable)
+{
+#ifdef GIT_WIN32
+ git_win32_path fullpath_w, executable_w;
+ int error;
+
+ if (git_utf8_to_16(executable_w, GIT_WIN_PATH_MAX, executable) < 0)
+ return -1;
+
+ error = git_win32_path_find_executable(fullpath_w, executable_w);
+
+ if (error == 0)
+ error = git_str_put_w(fullpath, fullpath_w, wcslen(fullpath_w));
+
+ return error;
+#else
+ git_str path = GIT_STR_INIT;
+ const char *current_dir, *term;
+ bool found = false;
+
+ if (git__getenv(&path, "PATH") < 0)
+ return -1;
+
+ current_dir = path.ptr;
+
+ while (*current_dir) {
+ if (! (term = strchr(current_dir, GIT_PATH_LIST_SEPARATOR)))
+ term = strchr(current_dir, '\0');
+
+ git_str_clear(fullpath);
+ if (git_str_put(fullpath, current_dir, (term - current_dir)) < 0 ||
+ git_str_putc(fullpath, '/') < 0 ||
+ git_str_puts(fullpath, executable) < 0)
+ return -1;
+
+ if (git_fs_path_isfile(fullpath->ptr)) {
+ found = true;
+ break;
+ }
+
+ current_dir = term;
+
+ while (*current_dir == GIT_PATH_LIST_SEPARATOR)
+ current_dir++;
+ }
+
+ git_str_dispose(&path);
+
+ if (found)
+ return 0;
+
+ git_str_clear(fullpath);
+ return GIT_ENOTFOUND;
+#endif
+}
diff --git a/src/util/fs_path.h b/src/util/fs_path.h
new file mode 100644
index 0000000..e5ca673
--- /dev/null
+++ b/src/util/fs_path.h
@@ -0,0 +1,790 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_fs_path_h__
+#define INCLUDE_fs_path_h__
+
+#include "git2_util.h"
+
+#include "posix.h"
+#include "str.h"
+#include "vector.h"
+#include "utf8.h"
+
+/**
+ * Path manipulation utils
+ *
+ * These are path utilities that munge paths without actually
+ * looking at the real filesystem.
+ */
+
+/*
+ * The dirname() function shall take a pointer to a character string
+ * that contains a pathname, and return a pointer to a string that is a
+ * pathname of the parent directory of that file. Trailing '/' characters
+ * in the path are not counted as part of the path.
+ *
+ * If path does not contain a '/', then dirname() shall return a pointer to
+ * the string ".". If path is a null pointer or points to an empty string,
+ * dirname() shall return a pointer to the string "." .
+ *
+ * The `git_fs_path_dirname` implementation is thread safe. The returned
+ * string must be manually free'd.
+ *
+ * The `git_fs_path_dirname_r` implementation writes the dirname to a `git_str`
+ * if the buffer pointer is not NULL.
+ * It returns an error code < 0 if there is an allocation error, otherwise
+ * the length of the dirname (which will be > 0).
+ */
+extern char *git_fs_path_dirname(const char *path);
+extern int git_fs_path_dirname_r(git_str *buffer, const char *path);
+
+/*
+ * This function returns the basename of the file, which is the last
+ * part of its full name given by fname, with the drive letter and
+ * leading directories stripped off. For example, the basename of
+ * c:/foo/bar/file.ext is file.ext, and the basename of a:foo is foo.
+ *
+ * Trailing slashes and backslashes are significant: the basename of
+ * c:/foo/bar/ is an empty string after the rightmost slash.
+ *
+ * The `git_fs_path_basename` implementation is thread safe. The returned
+ * string must be manually free'd.
+ *
+ * The `git_fs_path_basename_r` implementation writes the basename to a `git_str`.
+ * It returns an error code < 0 if there is an allocation error, otherwise
+ * the length of the basename (which will be >= 0).
+ */
+extern char *git_fs_path_basename(const char *path);
+extern int git_fs_path_basename_r(git_str *buffer, const char *path);
+
+/* Return the offset of the start of the basename. Unlike the other
+ * basename functions, this returns 0 if the path is empty.
+ */
+extern size_t git_fs_path_basename_offset(git_str *buffer);
+
+/**
+ * Find offset to root of path if path has one.
+ *
+ * This will return a number >= 0 which is the offset to the start of the
+ * path, if the path is rooted (i.e. "/rooted/path" returns 0 and
+ * "c:/windows/rooted/path" returns 2). If the path is not rooted, this
+ * returns -1.
+ */
+extern int git_fs_path_root(const char *path);
+
+/**
+ * Ensure path has a trailing '/'.
+ */
+extern int git_fs_path_to_dir(git_str *path);
+
+/**
+ * Ensure string has a trailing '/' if there is space for it.
+ */
+extern void git_fs_path_string_to_dir(char *path, size_t size);
+
+/**
+ * Taken from git.git; returns nonzero if the given path is "." or "..".
+ */
+GIT_INLINE(int) git_fs_path_is_dot_or_dotdot(const char *name)
+{
+ return (name[0] == '.' &&
+ (name[1] == '\0' ||
+ (name[1] == '.' && name[2] == '\0')));
+}
+
+#ifdef GIT_WIN32
+GIT_INLINE(int) git_fs_path_is_dot_or_dotdotW(const wchar_t *name)
+{
+ return (name[0] == L'.' &&
+ (name[1] == L'\0' ||
+ (name[1] == L'.' && name[2] == L'\0')));
+}
+
+#define git_fs_path_is_absolute(p) \
+ (git__isalpha((p)[0]) && (p)[1] == ':' && ((p)[2] == '\\' || (p)[2] == '/'))
+
+#define git_fs_path_is_dirsep(p) \
+ ((p) == '/' || (p) == '\\')
+
+/**
+ * Convert backslashes in path to forward slashes.
+ */
+GIT_INLINE(void) git_fs_path_mkposix(char *path)
+{
+ while (*path) {
+ if (*path == '\\')
+ *path = '/';
+
+ path++;
+ }
+}
+#else
+# define git_fs_path_mkposix(p) /* blank */
+
+#define git_fs_path_is_absolute(p) \
+ ((p)[0] == '/')
+
+#define git_fs_path_is_dirsep(p) \
+ ((p) == '/')
+
+#endif
+
+/**
+ * Check if string is a relative path (i.e. starts with "./" or "../")
+ */
+GIT_INLINE(int) git_fs_path_is_relative(const char *p)
+{
+ return (p[0] == '.' && (p[1] == '/' || (p[1] == '.' && p[2] == '/')));
+}
+
+/**
+ * Check if string is at end of path segment (i.e. looking at '/' or '\0')
+ */
+GIT_INLINE(int) git_fs_path_at_end_of_segment(const char *p)
+{
+ return !*p || *p == '/';
+}
+
+extern int git__percent_decode(git_str *decoded_out, const char *input);
+
+/**
+ * Extract path from file:// URL.
+ */
+extern int git_fs_path_fromurl(git_str *local_path_out, const char *file_url);
+
+
+/**
+ * Path filesystem utils
+ *
+ * These are path utilities that actually access the filesystem.
+ */
+
+/**
+ * Check if a file exists and can be accessed.
+ * @return true or false
+ */
+extern bool git_fs_path_exists(const char *path);
+
+/**
+ * Check if the given path points to a directory.
+ * @return true or false
+ */
+extern bool git_fs_path_isdir(const char *path);
+
+/**
+ * Check if the given path points to a regular file.
+ * @return true or false
+ */
+extern bool git_fs_path_isfile(const char *path);
+
+/**
+ * Check if the given path points to a symbolic link.
+ * @return true or false
+ */
+extern bool git_fs_path_islink(const char *path);
+
+/**
+ * Check if the given path is a directory, and is empty.
+ */
+extern bool git_fs_path_is_empty_dir(const char *path);
+
+/**
+ * Stat a file and/or link and set error if needed.
+ */
+extern int git_fs_path_lstat(const char *path, struct stat *st);
+
+/**
+ * Check if the parent directory contains the item.
+ *
+ * @param dir Directory to check.
+ * @param item Item that might be in the directory.
+ * @return 0 if item exists in directory, <0 otherwise.
+ */
+extern bool git_fs_path_contains(git_str *dir, const char *item);
+
+/**
+ * Check if the given path contains the given subdirectory.
+ *
+ * @param parent Directory path that might contain subdir
+ * @param subdir Subdirectory name to look for in parent
+ * @return true if subdirectory exists, false otherwise.
+ */
+extern bool git_fs_path_contains_dir(git_str *parent, const char *subdir);
+
+/**
+ * Determine the common directory length between two paths, including
+ * the final path separator. For example, given paths 'a/b/c/1.txt
+ * and 'a/b/c/d/2.txt', the common directory is 'a/b/c/', and this
+ * will return the length of the string 'a/b/c/', which is 6.
+ *
+ * @param one The first path
+ * @param two The second path
+ * @return The length of the common directory
+ */
+extern size_t git_fs_path_common_dirlen(const char *one, const char *two);
+
+/**
+ * Make the path relative to the given parent path.
+ *
+ * @param path The path to make relative
+ * @param parent The parent path to make path relative to
+ * @return 0 if path was made relative, GIT_ENOTFOUND
+ * if there was not common root between the paths,
+ * or <0.
+ */
+extern int git_fs_path_make_relative(git_str *path, const char *parent);
+
+/**
+ * Check if the given path contains the given file.
+ *
+ * @param dir Directory path that might contain file
+ * @param file File name to look for in parent
+ * @return true if file exists, false otherwise.
+ */
+extern bool git_fs_path_contains_file(git_str *dir, const char *file);
+
+/**
+ * Prepend base to unrooted path or just copy path over.
+ *
+ * This will optionally return the index into the path where the "root"
+ * is, either the end of the base directory prefix or the path root.
+ */
+extern int git_fs_path_join_unrooted(
+ git_str *path_out, const char *path, const char *base, ssize_t *root_at);
+
+/**
+ * Removes multiple occurrences of '/' in a row, squashing them into a
+ * single '/'.
+ */
+extern void git_fs_path_squash_slashes(git_str *path);
+
+/**
+ * Clean up path, prepending base if it is not already rooted.
+ */
+extern int git_fs_path_prettify(git_str *path_out, const char *path, const char *base);
+
+/**
+ * Clean up path, prepending base if it is not already rooted and
+ * appending a slash.
+ */
+extern int git_fs_path_prettify_dir(git_str *path_out, const char *path, const char *base);
+
+/**
+ * Get a directory from a path.
+ *
+ * If path is a directory, this acts like `git_fs_path_prettify_dir`
+ * (cleaning up path and appending a '/'). If path is a normal file,
+ * this prettifies it, then removed the filename a la dirname and
+ * appends the trailing '/'. If the path does not exist, it is
+ * treated like a regular filename.
+ */
+extern int git_fs_path_find_dir(git_str *dir);
+
+/**
+ * Resolve relative references within a path.
+ *
+ * This eliminates "./" and "../" relative references inside a path,
+ * as well as condensing multiple slashes into single ones. It will
+ * not touch the path before the "ceiling" length.
+ *
+ * Additionally, this will recognize an "c:/" drive prefix or a "xyz://" URL
+ * prefix and not touch that part of the path.
+ */
+extern int git_fs_path_resolve_relative(git_str *path, size_t ceiling);
+
+/**
+ * Apply a relative path to base path.
+ *
+ * Note that the base path could be a filename or a URL and this
+ * should still work. The relative path is walked segment by segment
+ * with three rules: series of slashes will be condensed to a single
+ * slash, "." will be eaten with no change, and ".." will remove a
+ * segment from the base path.
+ */
+extern int git_fs_path_apply_relative(git_str *target, const char *relpath);
+
+enum {
+ GIT_FS_PATH_DIR_IGNORE_CASE = (1u << 0),
+ GIT_FS_PATH_DIR_PRECOMPOSE_UNICODE = (1u << 1),
+ GIT_FS_PATH_DIR_INCLUDE_DOT_AND_DOTDOT = (1u << 2),
+};
+
+/**
+ * Walk each directory entry, except '.' and '..', calling fn(state).
+ *
+ * @param pathbuf Buffer the function reads the initial directory
+ * path from, and updates with each successive entry's name.
+ * @param flags Combination of GIT_FS_PATH_DIR flags.
+ * @param callback Callback for each entry. Passed the `payload` and each
+ * successive path inside the directory as a full path. This may
+ * safely append text to the pathbuf if needed. Return non-zero to
+ * cancel iteration (and return value will be propagated back).
+ * @param payload Passed to callback as first argument.
+ * @return 0 on success or error code from OS error or from callback
+ */
+extern int git_fs_path_direach(
+ git_str *pathbuf,
+ uint32_t flags,
+ int (*callback)(void *payload, git_str *path),
+ void *payload);
+
+/**
+ * Sort function to order two paths
+ */
+extern int git_fs_path_cmp(
+ const char *name1, size_t len1, int isdir1,
+ const char *name2, size_t len2, int isdir2,
+ int (*compare)(const char *, const char *, size_t));
+
+/**
+ * Invoke callback up path directory by directory until the ceiling is
+ * reached (inclusive of a final call at the root_path).
+ *
+ * Returning anything other than 0 from the callback function
+ * will stop the iteration and propagate the error to the caller.
+ *
+ * @param pathbuf Buffer the function reads the directory from and
+ * and updates with each successive name.
+ * @param ceiling Prefix of path at which to stop walking up. If NULL,
+ * this will walk all the way up to the root. If not a prefix of
+ * pathbuf, the callback will be invoked a single time on the
+ * original input path.
+ * @param callback Function to invoke on each path. Passed the `payload`
+ * and the buffer containing the current path. The path should not
+ * be modified in any way. Return non-zero to stop iteration.
+ * @param payload Passed to fn as the first ath.
+ */
+extern int git_fs_path_walk_up(
+ git_str *pathbuf,
+ const char *ceiling,
+ int (*callback)(void *payload, const char *path),
+ void *payload);
+
+
+enum {
+ GIT_FS_PATH_NOTEQUAL = 0,
+ GIT_FS_PATH_EQUAL = 1,
+ GIT_FS_PATH_PREFIX = 2
+};
+
+/*
+ * Determines if a path is equal to or potentially a child of another.
+ * @param parent The possible parent
+ * @param child The possible child
+ */
+GIT_INLINE(int) git_fs_path_equal_or_prefixed(
+ const char *parent,
+ const char *child,
+ ssize_t *prefixlen)
+{
+ const char *p = parent, *c = child;
+ int lastslash = 0;
+
+ while (*p && *c) {
+ lastslash = (*p == '/');
+
+ if (*p++ != *c++)
+ return GIT_FS_PATH_NOTEQUAL;
+ }
+
+ if (*p != '\0')
+ return GIT_FS_PATH_NOTEQUAL;
+
+ if (*c == '\0') {
+ if (prefixlen)
+ *prefixlen = p - parent;
+
+ return GIT_FS_PATH_EQUAL;
+ }
+
+ if (*c == '/' || lastslash) {
+ if (prefixlen)
+ *prefixlen = (p - parent) - lastslash;
+
+ return GIT_FS_PATH_PREFIX;
+ }
+
+ return GIT_FS_PATH_NOTEQUAL;
+}
+
+/* translate errno to libgit2 error code and set error message */
+extern int git_fs_path_set_error(
+ int errno_value, const char *path, const char *action);
+
+/* check if non-ascii characters are present in filename */
+extern bool git_fs_path_has_non_ascii(const char *path, size_t pathlen);
+
+#define GIT_PATH_REPO_ENCODING "UTF-8"
+
+#ifdef __APPLE__
+#define GIT_PATH_NATIVE_ENCODING "UTF-8-MAC"
+#else
+#define GIT_PATH_NATIVE_ENCODING "UTF-8"
+#endif
+
+#ifdef GIT_USE_ICONV
+
+#include <iconv.h>
+
+typedef struct {
+ iconv_t map;
+ git_str buf;
+} git_fs_path_iconv_t;
+
+#define GIT_PATH_ICONV_INIT { (iconv_t)-1, GIT_STR_INIT }
+
+/* Init iconv data for converting decomposed UTF-8 to precomposed */
+extern int git_fs_path_iconv_init_precompose(git_fs_path_iconv_t *ic);
+
+/* Clear allocated iconv data */
+extern void git_fs_path_iconv_clear(git_fs_path_iconv_t *ic);
+
+/*
+ * Rewrite `in` buffer using iconv map if necessary, replacing `in`
+ * pointer internal iconv buffer if rewrite happened. The `in` pointer
+ * will be left unchanged if no rewrite was needed.
+ */
+extern int git_fs_path_iconv(git_fs_path_iconv_t *ic, const char **in, size_t *inlen);
+
+#endif /* GIT_USE_ICONV */
+
+extern bool git_fs_path_does_decompose_unicode(const char *root);
+
+
+typedef struct git_fs_path_diriter git_fs_path_diriter;
+
+#if defined(GIT_WIN32) && !defined(__MINGW32__)
+
+struct git_fs_path_diriter
+{
+ git_win32_path path;
+ size_t parent_len;
+
+ git_str path_utf8;
+ size_t parent_utf8_len;
+
+ HANDLE handle;
+
+ unsigned int flags;
+
+ WIN32_FIND_DATAW current;
+ unsigned int needs_next;
+};
+
+#define GIT_FS_PATH_DIRITER_INIT { {0}, 0, GIT_STR_INIT, 0, INVALID_HANDLE_VALUE }
+
+#else
+
+struct git_fs_path_diriter
+{
+ git_str path;
+ size_t parent_len;
+
+ unsigned int flags;
+
+ DIR *dir;
+
+#ifdef GIT_USE_ICONV
+ git_fs_path_iconv_t ic;
+#endif
+};
+
+#define GIT_FS_PATH_DIRITER_INIT { GIT_STR_INIT }
+
+#endif
+
+/**
+ * Initialize a directory iterator.
+ *
+ * @param diriter Pointer to a diriter structure that will be setup.
+ * @param path The path that will be iterated over
+ * @param flags Directory reader flags
+ * @return 0 or an error code
+ */
+extern int git_fs_path_diriter_init(
+ git_fs_path_diriter *diriter,
+ const char *path,
+ unsigned int flags);
+
+/**
+ * Advance the directory iterator. Will return GIT_ITEROVER when
+ * the iteration has completed successfully.
+ *
+ * @param diriter The directory iterator
+ * @return 0, GIT_ITEROVER, or an error code
+ */
+extern int git_fs_path_diriter_next(git_fs_path_diriter *diriter);
+
+/**
+ * Returns the file name of the current item in the iterator.
+ *
+ * @param out Pointer to store the path in
+ * @param out_len Pointer to store the length of the path in
+ * @param diriter The directory iterator
+ * @return 0 or an error code
+ */
+extern int git_fs_path_diriter_filename(
+ const char **out,
+ size_t *out_len,
+ git_fs_path_diriter *diriter);
+
+/**
+ * Returns the full path of the current item in the iterator; that
+ * is the current filename plus the path of the directory that the
+ * iterator was constructed with.
+ *
+ * @param out Pointer to store the path in
+ * @param out_len Pointer to store the length of the path in
+ * @param diriter The directory iterator
+ * @return 0 or an error code
+ */
+extern int git_fs_path_diriter_fullpath(
+ const char **out,
+ size_t *out_len,
+ git_fs_path_diriter *diriter);
+
+/**
+ * Performs an `lstat` on the current item in the iterator.
+ *
+ * @param out Pointer to store the stat data in
+ * @param diriter The directory iterator
+ * @return 0 or an error code
+ */
+extern int git_fs_path_diriter_stat(struct stat *out, git_fs_path_diriter *diriter);
+
+/**
+ * Closes the directory iterator.
+ *
+ * @param diriter The directory iterator
+ */
+extern void git_fs_path_diriter_free(git_fs_path_diriter *diriter);
+
+/**
+ * Load all directory entries (except '.' and '..') into a vector.
+ *
+ * For cases where `git_fs_path_direach()` is not appropriate, this
+ * allows you to load the filenames in a directory into a vector
+ * of strings. That vector can then be sorted, iterated, or whatever.
+ * Remember to free alloc of the allocated strings when you are done.
+ *
+ * @param contents Vector to fill with directory entry names.
+ * @param path The directory to read from.
+ * @param prefix_len When inserting entries, the trailing part of path
+ * will be prefixed after this length. I.e. given path "/a/b" and
+ * prefix_len 3, the entries will look like "b/e1", "b/e2", etc.
+ * @param flags Combination of GIT_FS_PATH_DIR flags.
+ */
+extern int git_fs_path_dirload(
+ git_vector *contents,
+ const char *path,
+ size_t prefix_len,
+ uint32_t flags);
+
+
+/* Used for paths to repositories on the filesystem */
+extern bool git_fs_path_is_local_file_url(const char *file_url);
+extern int git_fs_path_from_url_or_path(git_str *local_path_out, const char *url_or_path);
+
+/* Flags to determine path validity in `git_fs_path_isvalid` */
+#define GIT_FS_PATH_REJECT_EMPTY_COMPONENT (1 << 0)
+#define GIT_FS_PATH_REJECT_TRAVERSAL (1 << 1)
+#define GIT_FS_PATH_REJECT_SLASH (1 << 2)
+#define GIT_FS_PATH_REJECT_BACKSLASH (1 << 3)
+#define GIT_FS_PATH_REJECT_TRAILING_DOT (1 << 4)
+#define GIT_FS_PATH_REJECT_TRAILING_SPACE (1 << 5)
+#define GIT_FS_PATH_REJECT_TRAILING_COLON (1 << 6)
+#define GIT_FS_PATH_REJECT_DOS_PATHS (1 << 7)
+#define GIT_FS_PATH_REJECT_NT_CHARS (1 << 8)
+#define GIT_FS_PATH_REJECT_LONG_PATHS (1 << 9)
+
+#define GIT_FS_PATH_REJECT_MAX (1 << 9)
+
+/* Default path safety for writing files to disk: since we use the
+ * Win32 "File Namespace" APIs ("\\?\") we need to protect from
+ * paths that the normal Win32 APIs would not write.
+ */
+#ifdef GIT_WIN32
+# define GIT_FS_PATH_REJECT_FILESYSTEM_DEFAULTS \
+ GIT_FS_PATH_REJECT_EMPTY_COMPONENT | \
+ GIT_FS_PATH_REJECT_TRAVERSAL | \
+ GIT_FS_PATH_REJECT_BACKSLASH | \
+ GIT_FS_PATH_REJECT_TRAILING_DOT | \
+ GIT_FS_PATH_REJECT_TRAILING_SPACE | \
+ GIT_FS_PATH_REJECT_TRAILING_COLON | \
+ GIT_FS_PATH_REJECT_DOS_PATHS | \
+ GIT_FS_PATH_REJECT_NT_CHARS
+#else
+# define GIT_FS_PATH_REJECT_FILESYSTEM_DEFAULTS \
+ GIT_FS_PATH_REJECT_EMPTY_COMPONENT | \
+ GIT_FS_PATH_REJECT_TRAVERSAL
+#endif
+
+/**
+ * Validate a filesystem path; with custom callbacks per-character and
+ * per-path component.
+ */
+extern bool git_fs_path_str_is_valid_ext(
+ const git_str *path,
+ unsigned int flags,
+ bool (*validate_char_cb)(char ch, void *payload),
+ bool (*validate_component_cb)(const char *component, size_t len, void *payload),
+ bool (*validate_length_cb)(const char *component, size_t len, size_t utf8_char_len),
+ void *payload);
+
+GIT_INLINE(bool) git_fs_path_is_valid_ext(
+ const char *path,
+ unsigned int flags,
+ bool (*validate_char_cb)(char ch, void *payload),
+ bool (*validate_component_cb)(const char *component, size_t len, void *payload),
+ bool (*validate_length_cb)(const char *component, size_t len, size_t utf8_char_len),
+ void *payload)
+{
+ const git_str str = GIT_STR_INIT_CONST(path, SIZE_MAX);
+ return git_fs_path_str_is_valid_ext(
+ &str,
+ flags,
+ validate_char_cb,
+ validate_component_cb,
+ validate_length_cb,
+ payload);
+}
+
+/**
+ * Validate a filesystem path. This ensures that the given path is legal
+ * and does not contain any "unsafe" components like path traversal ('.'
+ * or '..'), characters that are inappropriate for lesser filesystems
+ * (trailing ' ' or ':' characters), or filenames ("component names")
+ * that are not supported ('AUX', 'COM1").
+ */
+GIT_INLINE(bool) git_fs_path_is_valid(
+ const char *path,
+ unsigned int flags)
+{
+ const git_str str = GIT_STR_INIT_CONST(path, SIZE_MAX);
+ return git_fs_path_str_is_valid_ext(&str, flags, NULL, NULL, NULL, NULL);
+}
+
+/** Validate a filesystem path in a `git_str`. */
+GIT_INLINE(bool) git_fs_path_str_is_valid(
+ const git_str *path,
+ unsigned int flags)
+{
+ return git_fs_path_str_is_valid_ext(path, flags, NULL, NULL, NULL, NULL);
+}
+
+extern int git_fs_path_validate_str_length_with_suffix(
+ git_str *path,
+ size_t suffix_len);
+
+/**
+ * Validate an on-disk path, taking into account that it will have a
+ * suffix appended (eg, `.lock`).
+ */
+GIT_INLINE(int) git_fs_path_validate_filesystem_with_suffix(
+ const char *path,
+ size_t path_len,
+ size_t suffix_len)
+{
+#ifdef GIT_WIN32
+ size_t path_chars, total_chars;
+
+ path_chars = git_utf8_char_length(path, path_len);
+
+ if (GIT_ADD_SIZET_OVERFLOW(&total_chars, path_chars, suffix_len) ||
+ total_chars > MAX_PATH) {
+ git_error_set(GIT_ERROR_FILESYSTEM, "path too long: '%s'", path);
+ return -1;
+ }
+ return 0;
+#else
+ GIT_UNUSED(path);
+ GIT_UNUSED(path_len);
+ GIT_UNUSED(suffix_len);
+ return 0;
+#endif
+}
+
+/**
+ * Validate an path on the filesystem. This ensures that the given
+ * path is valid for the operating system/platform; for example, this
+ * will ensure that the given absolute path is smaller than MAX_PATH on
+ * Windows.
+ *
+ * For paths within the working directory, you should use ensure that
+ * `core.longpaths` is obeyed. Use `git_fs_path_validate_workdir`.
+ */
+GIT_INLINE(int) git_fs_path_validate_filesystem(
+ const char *path,
+ size_t path_len)
+{
+ return git_fs_path_validate_filesystem_with_suffix(path, path_len, 0);
+}
+
+/**
+ * Convert any backslashes into slashes
+ */
+int git_fs_path_normalize_slashes(git_str *out, const char *path);
+
+bool git_fs_path_supports_symlinks(const char *dir);
+
+typedef enum {
+ GIT_FS_PATH_OWNER_NONE = 0,
+
+ /** The file must be owned by the current user. */
+ GIT_FS_PATH_OWNER_CURRENT_USER = (1 << 0),
+
+ /** The file must be owned by the system account. */
+ GIT_FS_PATH_OWNER_ADMINISTRATOR = (1 << 1),
+
+ /**
+ * The file may be owned by a system account if the current
+ * user is in an administrator group. Windows only; this is
+ * a noop on non-Windows systems.
+ */
+ GIT_FS_PATH_USER_IS_ADMINISTRATOR = (1 << 2),
+
+ /**
+ * The file is owned by the current user, who is running `sudo`.
+ */
+ GIT_FS_PATH_OWNER_RUNNING_SUDO = (1 << 3),
+
+ /** The file may be owned by another user. */
+ GIT_FS_PATH_OWNER_OTHER = (1 << 4)
+} git_fs_path_owner_t;
+
+/**
+ * Sets the mock ownership for files; subsequent calls to
+ * `git_fs_path_owner_is_*` functions will return this data until
+ * cleared with `GIT_FS_PATH_OWNER_NONE`.
+ */
+void git_fs_path__set_owner(git_fs_path_owner_t owner);
+
+/** Verify that the file in question is owned by the given owner. */
+int git_fs_path_owner_is(
+ bool *out,
+ const char *path,
+ git_fs_path_owner_t owner_type);
+
+/**
+ * Verify that the file in question is owned by an administrator or system
+ * account.
+ */
+int git_fs_path_owner_is_system(bool *out, const char *path);
+
+/**
+ * Verify that the file in question is owned by the current user;
+ */
+
+int git_fs_path_owner_is_current_user(bool *out, const char *path);
+
+/**
+ * Search the current PATH for the given executable, returning the full
+ * path if it is found.
+ */
+int git_fs_path_find_executable(git_str *fullpath, const char *executable);
+
+#endif
diff --git a/src/util/futils.c b/src/util/futils.c
new file mode 100644
index 0000000..7b5a24b
--- /dev/null
+++ b/src/util/futils.c
@@ -0,0 +1,1236 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "futils.h"
+
+#include "runtime.h"
+#include "strmap.h"
+#include "hash.h"
+#include "rand.h"
+
+#include <ctype.h>
+
+#define GIT_FILEMODE_DEFAULT 0100666
+
+int git_futils_mkpath2file(const char *file_path, const mode_t mode)
+{
+ return git_futils_mkdir(
+ file_path, mode,
+ GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST | GIT_MKDIR_VERIFY_DIR);
+}
+
+int git_futils_mktmp(git_str *path_out, const char *filename, mode_t mode)
+{
+ const int open_flags = O_RDWR | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC;
+ unsigned int tries = 32;
+ int fd;
+
+ while (tries--) {
+ uint64_t rand = git_rand_next();
+
+ git_str_sets(path_out, filename);
+ git_str_puts(path_out, "_git2_");
+ git_str_encode_hexstr(path_out, (void *)&rand, sizeof(uint64_t));
+
+ if (git_str_oom(path_out))
+ return -1;
+
+ /* Note that we open with O_CREAT | O_EXCL */
+ if ((fd = p_open(path_out->ptr, open_flags, mode)) >= 0)
+ return fd;
+ }
+
+ git_error_set(GIT_ERROR_OS,
+ "failed to create temporary file '%s'", path_out->ptr);
+ git_str_dispose(path_out);
+ return -1;
+}
+
+int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode_t mode)
+{
+ int fd;
+
+ if (git_futils_mkpath2file(path, dirmode) < 0)
+ return -1;
+
+ fd = p_creat(path, mode);
+ if (fd < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to create file '%s'", path);
+ return -1;
+ }
+
+ return fd;
+}
+
+int git_futils_creat_locked(const char *path, const mode_t mode)
+{
+ int fd = p_open(path, O_WRONLY | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC,
+ mode);
+
+ if (fd < 0) {
+ int error = errno;
+ git_error_set(GIT_ERROR_OS, "failed to create locked file '%s'", path);
+ switch (error) {
+ case EEXIST:
+ return GIT_ELOCKED;
+ case ENOENT:
+ return GIT_ENOTFOUND;
+ default:
+ return -1;
+ }
+ }
+
+ return fd;
+}
+
+int git_futils_creat_locked_withpath(const char *path, const mode_t dirmode, const mode_t mode)
+{
+ if (git_futils_mkpath2file(path, dirmode) < 0)
+ return -1;
+
+ return git_futils_creat_locked(path, mode);
+}
+
+int git_futils_open_ro(const char *path)
+{
+ int fd = p_open(path, O_RDONLY);
+ if (fd < 0)
+ return git_fs_path_set_error(errno, path, "open");
+ return fd;
+}
+
+int git_futils_truncate(const char *path, int mode)
+{
+ int fd = p_open(path, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, mode);
+ if (fd < 0)
+ return git_fs_path_set_error(errno, path, "open");
+
+ close(fd);
+ return 0;
+}
+
+int git_futils_filesize(uint64_t *out, git_file fd)
+{
+ struct stat sb;
+
+ if (p_fstat(fd, &sb)) {
+ git_error_set(GIT_ERROR_OS, "failed to stat file descriptor");
+ return -1;
+ }
+
+ if (sb.st_size < 0) {
+ git_error_set(GIT_ERROR_INVALID, "invalid file size");
+ return -1;
+ }
+
+ *out = sb.st_size;
+ return 0;
+}
+
+mode_t git_futils_canonical_mode(mode_t raw_mode)
+{
+ if (S_ISREG(raw_mode))
+ return S_IFREG | GIT_PERMS_CANONICAL(raw_mode);
+ else if (S_ISLNK(raw_mode))
+ return S_IFLNK;
+ else if (S_ISGITLINK(raw_mode))
+ return S_IFGITLINK;
+ else if (S_ISDIR(raw_mode))
+ return S_IFDIR;
+ else
+ return 0;
+}
+
+int git_futils_readbuffer_fd(git_str *buf, git_file fd, size_t len)
+{
+ ssize_t read_size = 0;
+ size_t alloc_len;
+
+ git_str_clear(buf);
+
+ if (!git__is_ssizet(len)) {
+ git_error_set(GIT_ERROR_INVALID, "read too large");
+ return -1;
+ }
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, len, 1);
+ if (git_str_grow(buf, alloc_len) < 0)
+ return -1;
+
+ /* p_read loops internally to read len bytes */
+ read_size = p_read(fd, buf->ptr, len);
+
+ if (read_size < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to read descriptor");
+ git_str_dispose(buf);
+ return -1;
+ }
+
+ if ((size_t)read_size != len) {
+ git_error_set(GIT_ERROR_FILESYSTEM, "could not read (expected %" PRIuZ " bytes, read %" PRIuZ ")", len, (size_t)read_size);
+ git_str_dispose(buf);
+ return -1;
+ }
+
+ buf->ptr[read_size] = '\0';
+ buf->size = read_size;
+
+ return 0;
+}
+
+int git_futils_readbuffer_fd_full(git_str *buf, git_file fd)
+{
+ static size_t blocksize = 10240;
+ size_t alloc_len = 0, total_size = 0;
+ ssize_t read_size = 0;
+
+ git_str_clear(buf);
+
+ while (true) {
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, blocksize);
+
+ if (git_str_grow(buf, alloc_len) < 0)
+ return -1;
+
+ /* p_read loops internally to read blocksize bytes */
+ read_size = p_read(fd, buf->ptr, blocksize);
+
+ if (read_size < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to read descriptor");
+ git_str_dispose(buf);
+ return -1;
+ }
+
+ total_size += read_size;
+
+ if ((size_t)read_size < blocksize) {
+ break;
+ }
+ }
+
+ buf->ptr[total_size] = '\0';
+ buf->size = total_size;
+
+ return 0;
+}
+
+int git_futils_readbuffer_updated(
+ git_str *out,
+ const char *path,
+ unsigned char checksum[GIT_HASH_SHA256_SIZE],
+ int *updated)
+{
+ int error;
+ git_file fd;
+ struct stat st;
+ git_str buf = GIT_STR_INIT;
+ unsigned char checksum_new[GIT_HASH_SHA256_SIZE];
+
+ GIT_ASSERT_ARG(out);
+ GIT_ASSERT_ARG(path && *path);
+
+ if (updated != NULL)
+ *updated = 0;
+
+ if (p_stat(path, &st) < 0)
+ return git_fs_path_set_error(errno, path, "stat");
+
+
+ if (S_ISDIR(st.st_mode)) {
+ git_error_set(GIT_ERROR_INVALID, "requested file is a directory");
+ return GIT_ENOTFOUND;
+ }
+
+ if (!git__is_sizet(st.st_size+1)) {
+ git_error_set(GIT_ERROR_OS, "invalid regular file stat for '%s'", path);
+ return -1;
+ }
+
+ if ((fd = git_futils_open_ro(path)) < 0)
+ return fd;
+
+ if (git_futils_readbuffer_fd(&buf, fd, (size_t)st.st_size) < 0) {
+ p_close(fd);
+ return -1;
+ }
+
+ p_close(fd);
+
+ if (checksum) {
+ error = git_hash_buf(checksum_new, buf.ptr,
+ buf.size, GIT_HASH_ALGORITHM_SHA256);
+
+ if (error < 0) {
+ git_str_dispose(&buf);
+ return error;
+ }
+
+ /*
+ * If we were given a checksum, we only want to use it if it's different
+ */
+ if (!memcmp(checksum, checksum_new, GIT_HASH_SHA256_SIZE)) {
+ git_str_dispose(&buf);
+ if (updated)
+ *updated = 0;
+
+ return 0;
+ }
+
+ memcpy(checksum, checksum_new, GIT_HASH_SHA256_SIZE);
+ }
+
+ /*
+ * If we're here, the file did change, or the user didn't have an old version
+ */
+ if (updated != NULL)
+ *updated = 1;
+
+ git_str_swap(out, &buf);
+ git_str_dispose(&buf);
+
+ return 0;
+}
+
+int git_futils_readbuffer(git_str *buf, const char *path)
+{
+ return git_futils_readbuffer_updated(buf, path, NULL, NULL);
+}
+
+int git_futils_writebuffer(
+ const git_str *buf, const char *path, int flags, mode_t mode)
+{
+ int fd, do_fsync = 0, error = 0;
+
+ if (!flags)
+ flags = O_CREAT | O_TRUNC | O_WRONLY;
+
+ if ((flags & O_FSYNC) != 0)
+ do_fsync = 1;
+
+ flags &= ~O_FSYNC;
+
+ if (!mode)
+ mode = GIT_FILEMODE_DEFAULT;
+
+ if ((fd = p_open(path, flags, mode)) < 0) {
+ git_error_set(GIT_ERROR_OS, "could not open '%s' for writing", path);
+ return fd;
+ }
+
+ if ((error = p_write(fd, git_str_cstr(buf), git_str_len(buf))) < 0) {
+ git_error_set(GIT_ERROR_OS, "could not write to '%s'", path);
+ (void)p_close(fd);
+ return error;
+ }
+
+ if (do_fsync && (error = p_fsync(fd)) < 0) {
+ git_error_set(GIT_ERROR_OS, "could not fsync '%s'", path);
+ p_close(fd);
+ return error;
+ }
+
+ if ((error = p_close(fd)) < 0) {
+ git_error_set(GIT_ERROR_OS, "error while closing '%s'", path);
+ return error;
+ }
+
+ if (do_fsync && (flags & O_CREAT))
+ error = git_futils_fsync_parent(path);
+
+ return error;
+}
+
+int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode)
+{
+ if (git_futils_mkpath2file(to, dirmode) < 0)
+ return -1;
+
+ if (p_rename(from, to) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to rename '%s' to '%s'", from, to);
+ return -1;
+ }
+
+ return 0;
+}
+
+int git_futils_mmap_ro(git_map *out, git_file fd, off64_t begin, size_t len)
+{
+ return p_mmap(out, len, GIT_PROT_READ, GIT_MAP_SHARED, fd, begin);
+}
+
+int git_futils_mmap_ro_file(git_map *out, const char *path)
+{
+ git_file fd = git_futils_open_ro(path);
+ uint64_t len;
+ int result;
+
+ if (fd < 0)
+ return fd;
+
+ if ((result = git_futils_filesize(&len, fd)) < 0)
+ goto out;
+
+ if (!git__is_sizet(len)) {
+ git_error_set(GIT_ERROR_OS, "file `%s` too large to mmap", path);
+ result = -1;
+ goto out;
+ }
+
+ result = git_futils_mmap_ro(out, fd, 0, (size_t)len);
+out:
+ p_close(fd);
+ return result;
+}
+
+void git_futils_mmap_free(git_map *out)
+{
+ p_munmap(out);
+}
+
+GIT_INLINE(int) mkdir_validate_dir(
+ const char *path,
+ struct stat *st,
+ mode_t mode,
+ uint32_t flags,
+ struct git_futils_mkdir_options *opts)
+{
+ /* with exclusive create, existing dir is an error */
+ if ((flags & GIT_MKDIR_EXCL) != 0) {
+ git_error_set(GIT_ERROR_FILESYSTEM,
+ "failed to make directory '%s': directory exists", path);
+ return GIT_EEXISTS;
+ }
+
+ if ((S_ISREG(st->st_mode) && (flags & GIT_MKDIR_REMOVE_FILES)) ||
+ (S_ISLNK(st->st_mode) && (flags & GIT_MKDIR_REMOVE_SYMLINKS))) {
+ if (p_unlink(path) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to remove %s '%s'",
+ S_ISLNK(st->st_mode) ? "symlink" : "file", path);
+ return GIT_EEXISTS;
+ }
+
+ opts->perfdata.mkdir_calls++;
+
+ if (p_mkdir(path, mode) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to make directory '%s'", path);
+ return GIT_EEXISTS;
+ }
+ }
+
+ else if (S_ISLNK(st->st_mode)) {
+ /* Re-stat the target, make sure it's a directory */
+ opts->perfdata.stat_calls++;
+
+ if (p_stat(path, st) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to make directory '%s'", path);
+ return GIT_EEXISTS;
+ }
+ }
+
+ else if (!S_ISDIR(st->st_mode)) {
+ git_error_set(GIT_ERROR_FILESYSTEM,
+ "failed to make directory '%s': directory exists", path);
+ return GIT_EEXISTS;
+ }
+
+ return 0;
+}
+
+GIT_INLINE(int) mkdir_validate_mode(
+ const char *path,
+ struct stat *st,
+ bool terminal_path,
+ mode_t mode,
+ uint32_t flags,
+ struct git_futils_mkdir_options *opts)
+{
+ if (((terminal_path && (flags & GIT_MKDIR_CHMOD) != 0) ||
+ (flags & GIT_MKDIR_CHMOD_PATH) != 0) && st->st_mode != mode) {
+
+ opts->perfdata.chmod_calls++;
+
+ if (p_chmod(path, mode) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to set permissions on '%s'", path);
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+GIT_INLINE(int) mkdir_canonicalize(
+ git_str *path,
+ uint32_t flags)
+{
+ ssize_t root_len;
+
+ if (path->size == 0) {
+ git_error_set(GIT_ERROR_OS, "attempt to create empty path");
+ return -1;
+ }
+
+ /* Trim trailing slashes (except the root) */
+ if ((root_len = git_fs_path_root(path->ptr)) < 0)
+ root_len = 0;
+ else
+ root_len++;
+
+ while (path->size > (size_t)root_len && path->ptr[path->size - 1] == '/')
+ path->ptr[--path->size] = '\0';
+
+ /* if we are not supposed to made the last element, truncate it */
+ if ((flags & GIT_MKDIR_SKIP_LAST2) != 0) {
+ git_fs_path_dirname_r(path, path->ptr);
+ flags |= GIT_MKDIR_SKIP_LAST;
+ }
+ if ((flags & GIT_MKDIR_SKIP_LAST) != 0) {
+ git_fs_path_dirname_r(path, path->ptr);
+ }
+
+ /* We were either given the root path (or trimmed it to
+ * the root), we don't have anything to do.
+ */
+ if (path->size <= (size_t)root_len)
+ git_str_clear(path);
+
+ return 0;
+}
+
+int git_futils_mkdir(
+ const char *path,
+ mode_t mode,
+ uint32_t flags)
+{
+ git_str make_path = GIT_STR_INIT, parent_path = GIT_STR_INIT;
+ const char *relative;
+ struct git_futils_mkdir_options opts = { 0 };
+ struct stat st;
+ size_t depth = 0;
+ int len = 0, root_len, error;
+
+ if ((error = git_str_puts(&make_path, path)) < 0 ||
+ (error = mkdir_canonicalize(&make_path, flags)) < 0 ||
+ (error = git_str_puts(&parent_path, make_path.ptr)) < 0 ||
+ make_path.size == 0)
+ goto done;
+
+ root_len = git_fs_path_root(make_path.ptr);
+
+ /* find the first parent directory that exists. this will be used
+ * as the base to dirname_relative.
+ */
+ for (relative = make_path.ptr; parent_path.size; ) {
+ error = p_lstat(parent_path.ptr, &st);
+
+ if (error == 0) {
+ break;
+ } else if (errno != ENOENT) {
+ git_error_set(GIT_ERROR_OS, "failed to stat '%s'", parent_path.ptr);
+ error = -1;
+ goto done;
+ }
+
+ depth++;
+
+ /* examine the parent of the current path */
+ if ((len = git_fs_path_dirname_r(&parent_path, parent_path.ptr)) < 0) {
+ error = len;
+ goto done;
+ }
+
+ GIT_ASSERT(len);
+
+ /*
+ * We've walked all the given path's parents and it's either relative
+ * (the parent is simply '.') or rooted (the length is less than or
+ * equal to length of the root path). The path may be less than the
+ * root path length on Windows, where `C:` == `C:/`.
+ */
+ if ((len == 1 && parent_path.ptr[0] == '.') ||
+ (len == 1 && parent_path.ptr[0] == '/') ||
+ len <= root_len) {
+ relative = make_path.ptr;
+ break;
+ }
+
+ relative = make_path.ptr + len + 1;
+
+ /* not recursive? just make this directory relative to its parent. */
+ if ((flags & GIT_MKDIR_PATH) == 0)
+ break;
+ }
+
+ /* we found an item at the location we're trying to create,
+ * validate it.
+ */
+ if (depth == 0) {
+ error = mkdir_validate_dir(make_path.ptr, &st, mode, flags, &opts);
+
+ if (!error)
+ error = mkdir_validate_mode(
+ make_path.ptr, &st, true, mode, flags, &opts);
+
+ goto done;
+ }
+
+ /* we already took `SKIP_LAST` and `SKIP_LAST2` into account when
+ * canonicalizing `make_path`.
+ */
+ flags &= ~(GIT_MKDIR_SKIP_LAST2 | GIT_MKDIR_SKIP_LAST);
+
+ error = git_futils_mkdir_relative(relative,
+ parent_path.size ? parent_path.ptr : NULL, mode, flags, &opts);
+
+done:
+ git_str_dispose(&make_path);
+ git_str_dispose(&parent_path);
+ return error;
+}
+
+int git_futils_mkdir_r(const char *path, const mode_t mode)
+{
+ return git_futils_mkdir(path, mode, GIT_MKDIR_PATH);
+}
+
+int git_futils_mkdir_relative(
+ const char *relative_path,
+ const char *base,
+ mode_t mode,
+ uint32_t flags,
+ struct git_futils_mkdir_options *opts)
+{
+ git_str make_path = GIT_STR_INIT;
+ ssize_t root = 0, min_root_len;
+ char lastch = '/', *tail;
+ struct stat st;
+ struct git_futils_mkdir_options empty_opts = {0};
+ int error;
+
+ if (!opts)
+ opts = &empty_opts;
+
+ /* build path and find "root" where we should start calling mkdir */
+ if (git_fs_path_join_unrooted(&make_path, relative_path, base, &root) < 0)
+ return -1;
+
+ if ((error = mkdir_canonicalize(&make_path, flags)) < 0 ||
+ make_path.size == 0)
+ goto done;
+
+ /* if we are not supposed to make the whole path, reset root */
+ if ((flags & GIT_MKDIR_PATH) == 0)
+ root = git_str_rfind(&make_path, '/');
+
+ /* advance root past drive name or network mount prefix */
+ min_root_len = git_fs_path_root(make_path.ptr);
+ if (root < min_root_len)
+ root = min_root_len;
+ while (root >= 0 && make_path.ptr[root] == '/')
+ ++root;
+
+ /* clip root to make_path length */
+ if (root > (ssize_t)make_path.size)
+ root = (ssize_t)make_path.size; /* i.e. NUL byte of string */
+ if (root < 0)
+ root = 0;
+
+ /* walk down tail of path making each directory */
+ for (tail = &make_path.ptr[root]; *tail; *tail = lastch) {
+ bool mkdir_attempted = false;
+
+ /* advance tail to include next path component */
+ while (*tail == '/')
+ tail++;
+ while (*tail && *tail != '/')
+ tail++;
+
+ /* truncate path at next component */
+ lastch = *tail;
+ *tail = '\0';
+ st.st_mode = 0;
+
+ if (opts->dir_map && git_strmap_exists(opts->dir_map, make_path.ptr))
+ continue;
+
+ /* See what's going on with this path component */
+ opts->perfdata.stat_calls++;
+
+retry_lstat:
+ if (p_lstat(make_path.ptr, &st) < 0) {
+ if (mkdir_attempted || errno != ENOENT) {
+ git_error_set(GIT_ERROR_OS, "cannot access component in path '%s'", make_path.ptr);
+ error = -1;
+ goto done;
+ }
+
+ git_error_clear();
+ opts->perfdata.mkdir_calls++;
+ mkdir_attempted = true;
+ if (p_mkdir(make_path.ptr, mode) < 0) {
+ if (errno == EEXIST)
+ goto retry_lstat;
+ git_error_set(GIT_ERROR_OS, "failed to make directory '%s'", make_path.ptr);
+ error = -1;
+ goto done;
+ }
+ } else {
+ if ((error = mkdir_validate_dir(
+ make_path.ptr, &st, mode, flags, opts)) < 0)
+ goto done;
+ }
+
+ /* chmod if requested and necessary */
+ if ((error = mkdir_validate_mode(
+ make_path.ptr, &st, (lastch == '\0'), mode, flags, opts)) < 0)
+ goto done;
+
+ if (opts->dir_map && opts->pool) {
+ char *cache_path;
+ size_t alloc_size;
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_size, make_path.size, 1);
+ cache_path = git_pool_malloc(opts->pool, alloc_size);
+ GIT_ERROR_CHECK_ALLOC(cache_path);
+
+ memcpy(cache_path, make_path.ptr, make_path.size + 1);
+
+ if ((error = git_strmap_set(opts->dir_map, cache_path, cache_path)) < 0)
+ goto done;
+ }
+ }
+
+ error = 0;
+
+ /* check that full path really is a directory if requested & needed */
+ if ((flags & GIT_MKDIR_VERIFY_DIR) != 0 &&
+ lastch != '\0') {
+ opts->perfdata.stat_calls++;
+
+ if (p_stat(make_path.ptr, &st) < 0 || !S_ISDIR(st.st_mode)) {
+ git_error_set(GIT_ERROR_OS, "path is not a directory '%s'",
+ make_path.ptr);
+ error = GIT_ENOTFOUND;
+ }
+ }
+
+done:
+ git_str_dispose(&make_path);
+ return error;
+}
+
+typedef struct {
+ const char *base;
+ size_t baselen;
+ uint32_t flags;
+ int depth;
+} futils__rmdir_data;
+
+#define FUTILS_MAX_DEPTH 100
+
+static int futils__error_cannot_rmdir(const char *path, const char *filemsg)
+{
+ if (filemsg)
+ git_error_set(GIT_ERROR_OS, "could not remove directory '%s': %s",
+ path, filemsg);
+ else
+ git_error_set(GIT_ERROR_OS, "could not remove directory '%s'", path);
+
+ return -1;
+}
+
+static int futils__rm_first_parent(git_str *path, const char *ceiling)
+{
+ int error = GIT_ENOTFOUND;
+ struct stat st;
+
+ while (error == GIT_ENOTFOUND) {
+ git_str_rtruncate_at_char(path, '/');
+
+ if (!path->size || git__prefixcmp(path->ptr, ceiling) != 0)
+ error = 0;
+ else if (p_lstat_posixly(path->ptr, &st) == 0) {
+ if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))
+ error = p_unlink(path->ptr);
+ else if (!S_ISDIR(st.st_mode))
+ error = -1; /* fail to remove non-regular file */
+ } else if (errno != ENOTDIR)
+ error = -1;
+ }
+
+ if (error)
+ futils__error_cannot_rmdir(path->ptr, "cannot remove parent");
+
+ return error;
+}
+
+static int futils__rmdir_recurs_foreach(void *opaque, git_str *path)
+{
+ int error = 0;
+ futils__rmdir_data *data = opaque;
+ struct stat st;
+
+ if (data->depth > FUTILS_MAX_DEPTH)
+ error = futils__error_cannot_rmdir(
+ path->ptr, "directory nesting too deep");
+
+ else if ((error = p_lstat_posixly(path->ptr, &st)) < 0) {
+ if (errno == ENOENT)
+ error = 0;
+ else if (errno == ENOTDIR) {
+ /* asked to remove a/b/c/d/e and a/b is a normal file */
+ if ((data->flags & GIT_RMDIR_REMOVE_BLOCKERS) != 0)
+ error = futils__rm_first_parent(path, data->base);
+ else
+ futils__error_cannot_rmdir(
+ path->ptr, "parent is not directory");
+ }
+ else
+ error = git_fs_path_set_error(errno, path->ptr, "rmdir");
+ }
+
+ else if (S_ISDIR(st.st_mode)) {
+ data->depth++;
+
+ error = git_fs_path_direach(path, 0, futils__rmdir_recurs_foreach, data);
+
+ data->depth--;
+
+ if (error < 0)
+ return error;
+
+ if (data->depth == 0 && (data->flags & GIT_RMDIR_SKIP_ROOT) != 0)
+ return error;
+
+ if ((error = p_rmdir(path->ptr)) < 0) {
+ if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) != 0 &&
+ (errno == ENOTEMPTY || errno == EEXIST || errno == EBUSY))
+ error = 0;
+ else
+ error = git_fs_path_set_error(errno, path->ptr, "rmdir");
+ }
+ }
+
+ else if ((data->flags & GIT_RMDIR_REMOVE_FILES) != 0) {
+ if (p_unlink(path->ptr) < 0)
+ error = git_fs_path_set_error(errno, path->ptr, "remove");
+ }
+
+ else if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) == 0)
+ error = futils__error_cannot_rmdir(path->ptr, "still present");
+
+ return error;
+}
+
+static int futils__rmdir_empty_parent(void *opaque, const char *path)
+{
+ futils__rmdir_data *data = opaque;
+ int error = 0;
+
+ if (strlen(path) <= data->baselen)
+ error = GIT_ITEROVER;
+
+ else if (p_rmdir(path) < 0) {
+ int en = errno;
+
+ if (en == ENOENT || en == ENOTDIR) {
+ /* do nothing */
+ } else if ((data->flags & GIT_RMDIR_SKIP_NONEMPTY) == 0 &&
+ en == EBUSY) {
+ error = git_fs_path_set_error(errno, path, "rmdir");
+ } else if (en == ENOTEMPTY || en == EEXIST || en == EBUSY) {
+ error = GIT_ITEROVER;
+ } else {
+ error = git_fs_path_set_error(errno, path, "rmdir");
+ }
+ }
+
+ return error;
+}
+
+int git_futils_rmdir_r(
+ const char *path, const char *base, uint32_t flags)
+{
+ int error;
+ git_str fullpath = GIT_STR_INIT;
+ futils__rmdir_data data;
+
+ /* build path and find "root" where we should start calling mkdir */
+ if (git_fs_path_join_unrooted(&fullpath, path, base, NULL) < 0)
+ return -1;
+
+ memset(&data, 0, sizeof(data));
+ data.base = base ? base : "";
+ data.baselen = base ? strlen(base) : 0;
+ data.flags = flags;
+
+ error = futils__rmdir_recurs_foreach(&data, &fullpath);
+
+ /* remove now-empty parents if requested */
+ if (!error && (flags & GIT_RMDIR_EMPTY_PARENTS) != 0)
+ error = git_fs_path_walk_up(
+ &fullpath, base, futils__rmdir_empty_parent, &data);
+
+ if (error == GIT_ITEROVER) {
+ git_error_clear();
+ error = 0;
+ }
+
+ git_str_dispose(&fullpath);
+
+ return error;
+}
+
+int git_futils_fake_symlink(const char *target, const char *path)
+{
+ int retcode = GIT_ERROR;
+ int fd = git_futils_creat_withpath(path, 0755, 0644);
+ if (fd >= 0) {
+ retcode = p_write(fd, target, strlen(target));
+ p_close(fd);
+ }
+ return retcode;
+}
+
+static int cp_by_fd(int ifd, int ofd, bool close_fd_when_done)
+{
+ int error = 0;
+ char buffer[GIT_BUFSIZE_FILEIO];
+ ssize_t len = 0;
+
+ while (!error && (len = p_read(ifd, buffer, sizeof(buffer))) > 0)
+ /* p_write() does not have the same semantics as write(). It loops
+ * internally and will return 0 when it has completed writing.
+ */
+ error = p_write(ofd, buffer, len);
+
+ if (len < 0) {
+ git_error_set(GIT_ERROR_OS, "read error while copying file");
+ error = (int)len;
+ }
+
+ if (error < 0)
+ git_error_set(GIT_ERROR_OS, "write error while copying file");
+
+ if (close_fd_when_done) {
+ p_close(ifd);
+ p_close(ofd);
+ }
+
+ return error;
+}
+
+int git_futils_cp(const char *from, const char *to, mode_t filemode)
+{
+ int ifd, ofd;
+
+ if ((ifd = git_futils_open_ro(from)) < 0)
+ return ifd;
+
+ if ((ofd = p_open(to, O_WRONLY | O_CREAT | O_EXCL, filemode)) < 0) {
+ p_close(ifd);
+ return git_fs_path_set_error(errno, to, "open for writing");
+ }
+
+ return cp_by_fd(ifd, ofd, true);
+}
+
+int git_futils_touch(const char *path, time_t *when)
+{
+ struct p_timeval times[2];
+ int ret;
+
+ times[0].tv_sec = times[1].tv_sec = when ? *when : time(NULL);
+ times[0].tv_usec = times[1].tv_usec = 0;
+
+ ret = p_utimes(path, times);
+
+ return (ret < 0) ? git_fs_path_set_error(errno, path, "touch") : 0;
+}
+
+static int cp_link(const char *from, const char *to, size_t link_size)
+{
+ int error = 0;
+ ssize_t read_len;
+ char *link_data;
+ size_t alloc_size;
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_size, link_size, 1);
+ link_data = git__malloc(alloc_size);
+ GIT_ERROR_CHECK_ALLOC(link_data);
+
+ read_len = p_readlink(from, link_data, link_size);
+ if (read_len != (ssize_t)link_size) {
+ git_error_set(GIT_ERROR_OS, "failed to read symlink data for '%s'", from);
+ error = -1;
+ }
+ else {
+ link_data[read_len] = '\0';
+
+ if (p_symlink(link_data, to) < 0) {
+ git_error_set(GIT_ERROR_OS, "could not symlink '%s' as '%s'",
+ link_data, to);
+ error = -1;
+ }
+ }
+
+ git__free(link_data);
+ return error;
+}
+
+typedef struct {
+ const char *to_root;
+ git_str to;
+ ssize_t from_prefix;
+ uint32_t flags;
+ uint32_t mkdir_flags;
+ mode_t dirmode;
+} cp_r_info;
+
+#define GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT (1u << 10)
+
+static int _cp_r_mkdir(cp_r_info *info, git_str *from)
+{
+ int error = 0;
+
+ /* create root directory the first time we need to create a directory */
+ if ((info->flags & GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT) == 0) {
+ error = git_futils_mkdir(
+ info->to_root, info->dirmode,
+ (info->flags & GIT_CPDIR_CHMOD_DIRS) ? GIT_MKDIR_CHMOD : 0);
+
+ info->flags |= GIT_CPDIR__MKDIR_DONE_FOR_TO_ROOT;
+ }
+
+ /* create directory with root as base to prevent excess chmods */
+ if (!error)
+ error = git_futils_mkdir_relative(
+ from->ptr + info->from_prefix, info->to_root,
+ info->dirmode, info->mkdir_flags, NULL);
+
+ return error;
+}
+
+static int _cp_r_callback(void *ref, git_str *from)
+{
+ int error = 0;
+ cp_r_info *info = ref;
+ struct stat from_st, to_st;
+ bool exists = false;
+
+ if ((info->flags & GIT_CPDIR_COPY_DOTFILES) == 0 &&
+ from->ptr[git_fs_path_basename_offset(from)] == '.')
+ return 0;
+
+ if ((error = git_str_joinpath(
+ &info->to, info->to_root, from->ptr + info->from_prefix)) < 0)
+ return error;
+
+ if (!(error = git_fs_path_lstat(info->to.ptr, &to_st)))
+ exists = true;
+ else if (error != GIT_ENOTFOUND)
+ return error;
+ else {
+ git_error_clear();
+ error = 0;
+ }
+
+ if ((error = git_fs_path_lstat(from->ptr, &from_st)) < 0)
+ return error;
+
+ if (S_ISDIR(from_st.st_mode)) {
+ mode_t oldmode = info->dirmode;
+
+ /* if we are not chmod'ing, then overwrite dirmode */
+ if ((info->flags & GIT_CPDIR_CHMOD_DIRS) == 0)
+ info->dirmode = from_st.st_mode;
+
+ /* make directory now if CREATE_EMPTY_DIRS is requested and needed */
+ if (!exists && (info->flags & GIT_CPDIR_CREATE_EMPTY_DIRS) != 0)
+ error = _cp_r_mkdir(info, from);
+
+ /* recurse onto target directory */
+ if (!error && (!exists || S_ISDIR(to_st.st_mode)))
+ error = git_fs_path_direach(from, 0, _cp_r_callback, info);
+
+ if (oldmode != 0)
+ info->dirmode = oldmode;
+
+ return error;
+ }
+
+ if (exists) {
+ if ((info->flags & GIT_CPDIR_OVERWRITE) == 0)
+ return 0;
+
+ if (p_unlink(info->to.ptr) < 0) {
+ git_error_set(GIT_ERROR_OS, "cannot overwrite existing file '%s'",
+ info->to.ptr);
+ return GIT_EEXISTS;
+ }
+ }
+
+ /* Done if this isn't a regular file or a symlink */
+ if (!S_ISREG(from_st.st_mode) &&
+ (!S_ISLNK(from_st.st_mode) ||
+ (info->flags & GIT_CPDIR_COPY_SYMLINKS) == 0))
+ return 0;
+
+ /* Make container directory on demand if needed */
+ if ((info->flags & GIT_CPDIR_CREATE_EMPTY_DIRS) == 0 &&
+ (error = _cp_r_mkdir(info, from)) < 0)
+ return error;
+
+ /* make symlink or regular file */
+ if (info->flags & GIT_CPDIR_LINK_FILES) {
+ if ((error = p_link(from->ptr, info->to.ptr)) < 0)
+ git_error_set(GIT_ERROR_OS, "failed to link '%s'", from->ptr);
+ } else if (S_ISLNK(from_st.st_mode)) {
+ error = cp_link(from->ptr, info->to.ptr, (size_t)from_st.st_size);
+ } else {
+ mode_t usemode = from_st.st_mode;
+
+ if ((info->flags & GIT_CPDIR_SIMPLE_TO_MODE) != 0)
+ usemode = GIT_PERMS_FOR_WRITE(usemode);
+
+ error = git_futils_cp(from->ptr, info->to.ptr, usemode);
+ }
+
+ return error;
+}
+
+int git_futils_cp_r(
+ const char *from,
+ const char *to,
+ uint32_t flags,
+ mode_t dirmode)
+{
+ int error;
+ git_str path = GIT_STR_INIT;
+ cp_r_info info;
+
+ if (git_str_joinpath(&path, from, "") < 0) /* ensure trailing slash */
+ return -1;
+
+ memset(&info, 0, sizeof(info));
+ info.to_root = to;
+ info.flags = flags;
+ info.dirmode = dirmode;
+ info.from_prefix = path.size;
+ git_str_init(&info.to, 0);
+
+ /* precalculate mkdir flags */
+ if ((flags & GIT_CPDIR_CREATE_EMPTY_DIRS) == 0) {
+ /* if not creating empty dirs, then use mkdir to create the path on
+ * demand right before files are copied.
+ */
+ info.mkdir_flags = GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST;
+ if ((flags & GIT_CPDIR_CHMOD_DIRS) != 0)
+ info.mkdir_flags |= GIT_MKDIR_CHMOD_PATH;
+ } else {
+ /* otherwise, we will do simple mkdir as directories are encountered */
+ info.mkdir_flags =
+ ((flags & GIT_CPDIR_CHMOD_DIRS) != 0) ? GIT_MKDIR_CHMOD : 0;
+ }
+
+ error = _cp_r_callback(&info, &path);
+
+ git_str_dispose(&path);
+ git_str_dispose(&info.to);
+
+ return error;
+}
+
+int git_futils_filestamp_check(
+ git_futils_filestamp *stamp, const char *path)
+{
+ struct stat st;
+
+ /* if the stamp is NULL, then always reload */
+ if (stamp == NULL)
+ return 1;
+
+ if (p_stat(path, &st) < 0)
+ return GIT_ENOTFOUND;
+
+ if (stamp->mtime.tv_sec == st.st_mtime &&
+#if defined(GIT_USE_NSEC)
+ stamp->mtime.tv_nsec == st.st_mtime_nsec &&
+#endif
+ stamp->size == (uint64_t)st.st_size &&
+ stamp->ino == (unsigned int)st.st_ino)
+ return 0;
+
+ stamp->mtime.tv_sec = st.st_mtime;
+#if defined(GIT_USE_NSEC)
+ stamp->mtime.tv_nsec = st.st_mtime_nsec;
+#endif
+ stamp->size = (uint64_t)st.st_size;
+ stamp->ino = (unsigned int)st.st_ino;
+
+ return 1;
+}
+
+void git_futils_filestamp_set(
+ git_futils_filestamp *target, const git_futils_filestamp *source)
+{
+ if (source)
+ memcpy(target, source, sizeof(*target));
+ else
+ memset(target, 0, sizeof(*target));
+}
+
+
+void git_futils_filestamp_set_from_stat(
+ git_futils_filestamp *stamp, struct stat *st)
+{
+ if (st) {
+ stamp->mtime.tv_sec = st->st_mtime;
+#if defined(GIT_USE_NSEC)
+ stamp->mtime.tv_nsec = st->st_mtime_nsec;
+#else
+ stamp->mtime.tv_nsec = 0;
+#endif
+ stamp->size = (uint64_t)st->st_size;
+ stamp->ino = (unsigned int)st->st_ino;
+ } else {
+ memset(stamp, 0, sizeof(*stamp));
+ }
+}
+
+int git_futils_fsync_dir(const char *path)
+{
+#ifdef GIT_WIN32
+ GIT_UNUSED(path);
+ return 0;
+#else
+ int fd, error = -1;
+
+ if ((fd = p_open(path, O_RDONLY)) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to open directory '%s' for fsync", path);
+ return -1;
+ }
+
+ if ((error = p_fsync(fd)) < 0)
+ git_error_set(GIT_ERROR_OS, "failed to fsync directory '%s'", path);
+
+ p_close(fd);
+ return error;
+#endif
+}
+
+int git_futils_fsync_parent(const char *path)
+{
+ char *parent;
+ int error;
+
+ if ((parent = git_fs_path_dirname(path)) == NULL)
+ return -1;
+
+ error = git_futils_fsync_dir(parent);
+ git__free(parent);
+ return error;
+}
diff --git a/src/util/futils.h b/src/util/futils.h
new file mode 100644
index 0000000..3f207af
--- /dev/null
+++ b/src/util/futils.h
@@ -0,0 +1,403 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_futils_h__
+#define INCLUDE_futils_h__
+
+#include "git2_util.h"
+
+#include "map.h"
+#include "posix.h"
+#include "fs_path.h"
+#include "pool.h"
+#include "strmap.h"
+#include "hash.h"
+
+/**
+ * Filebuffer methods
+ *
+ * Read whole files into an in-memory buffer for processing
+ */
+extern int git_futils_readbuffer(git_str *obj, const char *path);
+extern int git_futils_readbuffer_updated(
+ git_str *obj,
+ const char *path,
+ unsigned char checksum[GIT_HASH_SHA1_SIZE],
+ int *updated);
+extern int git_futils_readbuffer_fd_full(git_str *obj, git_file fd);
+extern int git_futils_readbuffer_fd(git_str *obj, git_file fd, size_t len);
+
+/* Additional constants for `git_futils_writebuffer`'s `open_flags`. We
+ * support these internally and they will be removed before the `open` call.
+ */
+#ifndef O_FSYNC
+# define O_FSYNC (1 << 31)
+#endif
+
+extern int git_futils_writebuffer(
+ const git_str *buf, const char *path, int open_flags, mode_t mode);
+
+/**
+ * File utils
+ *
+ * These are custom filesystem-related helper methods. They are
+ * rather high level, and wrap the underlying POSIX methods
+ *
+ * All these methods return 0 on success,
+ * or an error code on failure and an error message is set.
+ */
+
+/**
+ * Create and open a file, while also
+ * creating all the folders in its path
+ */
+extern int git_futils_creat_withpath(const char *path, const mode_t dirmode, const mode_t mode);
+
+/**
+ * Create and open a process-locked file
+ */
+extern int git_futils_creat_locked(const char *path, const mode_t mode);
+
+/**
+ * Create and open a process-locked file, while
+ * also creating all the folders in its path
+ */
+extern int git_futils_creat_locked_withpath(const char *path, const mode_t dirmode, const mode_t mode);
+
+/**
+ * Create a path recursively.
+ */
+extern int git_futils_mkdir_r(const char *path, const mode_t mode);
+
+/**
+ * Flags to pass to `git_futils_mkdir`.
+ *
+ * * GIT_MKDIR_EXCL is "exclusive" - i.e. generate an error if dir exists.
+ * * GIT_MKDIR_PATH says to make all components in the path.
+ * * GIT_MKDIR_CHMOD says to chmod the final directory entry after creation
+ * * GIT_MKDIR_CHMOD_PATH says to chmod each directory component in the path
+ * * GIT_MKDIR_SKIP_LAST says to leave off the last element of the path
+ * * GIT_MKDIR_SKIP_LAST2 says to leave off the last 2 elements of the path
+ * * GIT_MKDIR_VERIFY_DIR says confirm final item is a dir, not just EEXIST
+ * * GIT_MKDIR_REMOVE_FILES says to remove files and recreate dirs
+ * * GIT_MKDIR_REMOVE_SYMLINKS says to remove symlinks and recreate dirs
+ *
+ * Note that the chmod options will be executed even if the directory already
+ * exists, unless GIT_MKDIR_EXCL is given.
+ */
+typedef enum {
+ GIT_MKDIR_EXCL = 1,
+ GIT_MKDIR_PATH = 2,
+ GIT_MKDIR_CHMOD = 4,
+ GIT_MKDIR_CHMOD_PATH = 8,
+ GIT_MKDIR_SKIP_LAST = 16,
+ GIT_MKDIR_SKIP_LAST2 = 32,
+ GIT_MKDIR_VERIFY_DIR = 64,
+ GIT_MKDIR_REMOVE_FILES = 128,
+ GIT_MKDIR_REMOVE_SYMLINKS = 256
+} git_futils_mkdir_flags;
+
+struct git_futils_mkdir_perfdata
+{
+ size_t stat_calls;
+ size_t mkdir_calls;
+ size_t chmod_calls;
+};
+
+struct git_futils_mkdir_options
+{
+ git_strmap *dir_map;
+ git_pool *pool;
+ struct git_futils_mkdir_perfdata perfdata;
+};
+
+/**
+ * Create a directory or entire path.
+ *
+ * This makes a directory (and the entire path leading up to it if requested),
+ * and optionally chmods the directory immediately after (or each part of the
+ * path if requested).
+ *
+ * @param path The path to create, relative to base.
+ * @param base Root for relative path. These directories will never be made.
+ * @param mode The mode to use for created directories.
+ * @param flags Combination of the mkdir flags above.
+ * @param opts Extended options, or null.
+ * @return 0 on success, else error code
+ */
+extern int git_futils_mkdir_relative(const char *path, const char *base, mode_t mode, uint32_t flags, struct git_futils_mkdir_options *opts);
+
+/**
+ * Create a directory or entire path. Similar to `git_futils_mkdir_relative`
+ * without performance data.
+ */
+extern int git_futils_mkdir(const char *path, mode_t mode, uint32_t flags);
+
+/**
+ * Create all the folders required to contain
+ * the full path of a file
+ */
+extern int git_futils_mkpath2file(const char *path, const mode_t mode);
+
+/**
+ * Flags to pass to `git_futils_rmdir_r`.
+ *
+ * * GIT_RMDIR_EMPTY_HIERARCHY - the default; remove hierarchy of empty
+ * dirs and generate error if any files are found.
+ * * GIT_RMDIR_REMOVE_FILES - attempt to remove files in the hierarchy.
+ * * GIT_RMDIR_SKIP_NONEMPTY - skip non-empty directories with no error.
+ * * GIT_RMDIR_EMPTY_PARENTS - remove containing directories up to base
+ * if removing this item leaves them empty
+ * * GIT_RMDIR_REMOVE_BLOCKERS - remove blocking file that causes ENOTDIR
+ * * GIT_RMDIR_SKIP_ROOT - don't remove root directory itself
+ */
+typedef enum {
+ GIT_RMDIR_EMPTY_HIERARCHY = 0,
+ GIT_RMDIR_REMOVE_FILES = (1 << 0),
+ GIT_RMDIR_SKIP_NONEMPTY = (1 << 1),
+ GIT_RMDIR_EMPTY_PARENTS = (1 << 2),
+ GIT_RMDIR_REMOVE_BLOCKERS = (1 << 3),
+ GIT_RMDIR_SKIP_ROOT = (1 << 4)
+} git_futils_rmdir_flags;
+
+/**
+ * Remove path and any files and directories beneath it.
+ *
+ * @param path Path to the top level directory to process.
+ * @param base Root for relative path.
+ * @param flags Combination of git_futils_rmdir_flags values
+ * @return 0 on success; -1 on error.
+ */
+extern int git_futils_rmdir_r(const char *path, const char *base, uint32_t flags);
+
+/**
+ * Create and open a temporary file with a `_git2_` suffix in a
+ * protected directory; the file created will created will honor
+ * the current `umask`. Writes the filename into path_out.
+ *
+ * This function uses a high-quality PRNG seeded by the system's
+ * entropy pool _where available_ and falls back to a simple seed
+ * (time plus system information) when not. This is suitable for
+ * writing within a protected directory, but the system's safe
+ * temporary file creation functions should be preferred where
+ * available when writing into world-writable (temp) directories.
+ *
+ * @return On success, an open file descriptor, else an error code < 0.
+ */
+extern int git_futils_mktmp(git_str *path_out, const char *filename, mode_t mode);
+
+/**
+ * Move a file on the filesystem, create the
+ * destination path if it doesn't exist
+ */
+extern int git_futils_mv_withpath(const char *from, const char *to, const mode_t dirmode);
+
+/**
+ * Copy a file
+ *
+ * The filemode will be used for the newly created file.
+ */
+extern int git_futils_cp(
+ const char *from,
+ const char *to,
+ mode_t filemode);
+
+/**
+ * Set the files atime and mtime to the given time, or the current time
+ * if `ts` is NULL.
+ */
+extern int git_futils_touch(const char *path, time_t *when);
+
+/**
+ * Flags that can be passed to `git_futils_cp_r`.
+ *
+ * - GIT_CPDIR_CREATE_EMPTY_DIRS: create directories even if there are no
+ * files under them (otherwise directories will only be created lazily
+ * when a file inside them is copied).
+ * - GIT_CPDIR_COPY_SYMLINKS: copy symlinks, otherwise they are ignored.
+ * - GIT_CPDIR_COPY_DOTFILES: copy files with leading '.', otherwise ignored.
+ * - GIT_CPDIR_OVERWRITE: overwrite pre-existing files with source content,
+ * otherwise they are silently skipped.
+ * - GIT_CPDIR_CHMOD_DIRS: explicitly chmod directories to `dirmode`
+ * - GIT_CPDIR_SIMPLE_TO_MODE: default tries to replicate the mode of the
+ * source file to the target; with this flag, always use 0666 (or 0777 if
+ * source has exec bits set) for target.
+ * - GIT_CPDIR_LINK_FILES will try to use hardlinks for the files
+ */
+typedef enum {
+ GIT_CPDIR_CREATE_EMPTY_DIRS = (1u << 0),
+ GIT_CPDIR_COPY_SYMLINKS = (1u << 1),
+ GIT_CPDIR_COPY_DOTFILES = (1u << 2),
+ GIT_CPDIR_OVERWRITE = (1u << 3),
+ GIT_CPDIR_CHMOD_DIRS = (1u << 4),
+ GIT_CPDIR_SIMPLE_TO_MODE = (1u << 5),
+ GIT_CPDIR_LINK_FILES = (1u << 6)
+} git_futils_cpdir_flags;
+
+/**
+ * Copy a directory tree.
+ *
+ * This copies directories and files from one root to another. You can
+ * pass a combination of GIT_CPDIR flags as defined above.
+ *
+ * If you pass the CHMOD flag, then the dirmode will be applied to all
+ * directories that are created during the copy, overriding the natural
+ * permissions. If you do not pass the CHMOD flag, then the dirmode
+ * will actually be copied from the source files and the `dirmode` arg
+ * will be ignored.
+ */
+extern int git_futils_cp_r(
+ const char *from,
+ const char *to,
+ uint32_t flags,
+ mode_t dirmode);
+
+/**
+ * Open a file readonly and set error if needed.
+ */
+extern int git_futils_open_ro(const char *path);
+
+/**
+ * Truncate a file, creating it if it doesn't exist.
+ */
+extern int git_futils_truncate(const char *path, int mode);
+
+/**
+ * Get the filesize in bytes of a file
+ */
+extern int git_futils_filesize(uint64_t *out, git_file fd);
+
+#define GIT_PERMS_IS_EXEC(MODE) (((MODE) & 0100) != 0)
+#define GIT_PERMS_CANONICAL(MODE) (GIT_PERMS_IS_EXEC(MODE) ? 0755 : 0644)
+#define GIT_PERMS_FOR_WRITE(MODE) (GIT_PERMS_IS_EXEC(MODE) ? 0777 : 0666)
+
+#define GIT_MODE_PERMS_MASK 0777
+#define GIT_MODE_TYPE_MASK 0170000
+#define GIT_MODE_TYPE(MODE) ((MODE) & GIT_MODE_TYPE_MASK)
+#define GIT_MODE_ISBLOB(MODE) (GIT_MODE_TYPE(MODE) == GIT_MODE_TYPE(GIT_FILEMODE_BLOB))
+
+/**
+ * Convert a mode_t from the OS to a legal git mode_t value.
+ */
+extern mode_t git_futils_canonical_mode(mode_t raw_mode);
+
+
+/**
+ * Read-only map all or part of a file into memory.
+ * When possible this function should favor a virtual memory
+ * style mapping over some form of malloc()+read(), as the
+ * data access will be random and is not likely to touch the
+ * majority of the region requested.
+ *
+ * @param out buffer to populate with the mapping information.
+ * @param fd open descriptor to configure the mapping from.
+ * @param begin first byte to map, this should be page aligned.
+ * @param len number of bytes to map.
+ * @return
+ * - 0 on success;
+ * - -1 on error.
+ */
+extern int git_futils_mmap_ro(
+ git_map *out,
+ git_file fd,
+ off64_t begin,
+ size_t len);
+
+/**
+ * Read-only map an entire file.
+ *
+ * @param out buffer to populate with the mapping information.
+ * @param path path to file to be opened.
+ * @return
+ * - 0 on success;
+ * - GIT_ENOTFOUND if not found;
+ * - -1 on an unspecified OS related error.
+ */
+extern int git_futils_mmap_ro_file(
+ git_map *out,
+ const char *path);
+
+/**
+ * Release the memory associated with a previous memory mapping.
+ * @param map the mapping description previously configured.
+ */
+extern void git_futils_mmap_free(git_map *map);
+
+/**
+ * Create a "fake" symlink (text file containing the target path).
+ *
+ * @param target original symlink target
+ * @param path symlink file to be created
+ * @return 0 on success, -1 on error
+ */
+extern int git_futils_fake_symlink(const char *target, const char *path);
+
+/**
+ * A file stamp represents a snapshot of information about a file that can
+ * be used to test if the file changes. This portable implementation is
+ * based on stat data about that file, but it is possible that OS specific
+ * versions could be implemented in the future.
+ */
+typedef struct {
+ struct timespec mtime;
+ uint64_t size;
+ unsigned int ino;
+} git_futils_filestamp;
+
+/**
+ * Compare stat information for file with reference info.
+ *
+ * This function updates the file stamp to current data for the given path
+ * and returns 0 if the file is up-to-date relative to the prior setting,
+ * 1 if the file has been changed, or GIT_ENOTFOUND if the file doesn't
+ * exist. This will not call git_error_set, so you must set the error if you
+ * plan to return an error.
+ *
+ * @param stamp File stamp to be checked
+ * @param path Path to stat and check if changed
+ * @return 0 if up-to-date, 1 if out-of-date, GIT_ENOTFOUND if cannot stat
+ */
+extern int git_futils_filestamp_check(
+ git_futils_filestamp *stamp, const char *path);
+
+/**
+ * Set or reset file stamp data
+ *
+ * This writes the target file stamp. If the source is NULL, this will set
+ * the target stamp to values that will definitely be out of date. If the
+ * source is not NULL, this copies the source values to the target.
+ *
+ * @param tgt File stamp to write to
+ * @param src File stamp to copy from or NULL to clear the target
+ */
+extern void git_futils_filestamp_set(
+ git_futils_filestamp *tgt, const git_futils_filestamp *src);
+
+/**
+ * Set file stamp data from stat structure
+ */
+extern void git_futils_filestamp_set_from_stat(
+ git_futils_filestamp *stamp, struct stat *st);
+
+/**
+ * `fsync` the parent directory of the given path, if `fsync` is
+ * supported for directories on this platform.
+ *
+ * @param path Path of the directory to sync.
+ * @return 0 on success, -1 on error
+ */
+extern int git_futils_fsync_dir(const char *path);
+
+/**
+ * `fsync` the parent directory of the given path, if `fsync` is
+ * supported for directories on this platform.
+ *
+ * @param path Path of the file whose parent directory should be synced.
+ * @return 0 on success, -1 on error
+ */
+extern int git_futils_fsync_parent(const char *path);
+
+#endif
diff --git a/src/util/git2_features.h.in b/src/util/git2_features.h.in
new file mode 100644
index 0000000..a84ea89
--- /dev/null
+++ b/src/util/git2_features.h.in
@@ -0,0 +1,68 @@
+#ifndef INCLUDE_features_h__
+#define INCLUDE_features_h__
+
+#cmakedefine GIT_DEBUG_POOL 1
+#cmakedefine GIT_DEBUG_STRICT_ALLOC 1
+#cmakedefine GIT_DEBUG_STRICT_OPEN 1
+
+#cmakedefine GIT_THREADS 1
+#cmakedefine GIT_WIN32_LEAKCHECK 1
+
+#cmakedefine GIT_ARCH_64 1
+#cmakedefine GIT_ARCH_32 1
+
+#cmakedefine GIT_USE_ICONV 1
+#cmakedefine GIT_USE_NSEC 1
+#cmakedefine GIT_USE_STAT_MTIM 1
+#cmakedefine GIT_USE_STAT_MTIMESPEC 1
+#cmakedefine GIT_USE_STAT_MTIME_NSEC 1
+#cmakedefine GIT_USE_FUTIMENS 1
+
+#cmakedefine GIT_REGEX_REGCOMP_L
+#cmakedefine GIT_REGEX_REGCOMP
+#cmakedefine GIT_REGEX_PCRE
+#cmakedefine GIT_REGEX_PCRE2
+#cmakedefine GIT_REGEX_BUILTIN 1
+
+#cmakedefine GIT_QSORT_BSD
+#cmakedefine GIT_QSORT_GNU
+#cmakedefine GIT_QSORT_C11
+#cmakedefine GIT_QSORT_MSC
+
+#cmakedefine GIT_SSH 1
+#cmakedefine GIT_SSH_MEMORY_CREDENTIALS 1
+
+#cmakedefine GIT_NTLM 1
+#cmakedefine GIT_GSSAPI 1
+#cmakedefine GIT_GSSFRAMEWORK 1
+
+#cmakedefine GIT_WINHTTP 1
+#cmakedefine GIT_HTTPS 1
+#cmakedefine GIT_OPENSSL 1
+#cmakedefine GIT_OPENSSL_DYNAMIC 1
+#cmakedefine GIT_SECURE_TRANSPORT 1
+#cmakedefine GIT_MBEDTLS 1
+#cmakedefine GIT_SCHANNEL 1
+
+#cmakedefine GIT_SHA1_COLLISIONDETECT 1
+#cmakedefine GIT_SHA1_WIN32 1
+#cmakedefine GIT_SHA1_COMMON_CRYPTO 1
+#cmakedefine GIT_SHA1_OPENSSL 1
+#cmakedefine GIT_SHA1_OPENSSL_DYNAMIC 1
+#cmakedefine GIT_SHA1_MBEDTLS 1
+
+#cmakedefine GIT_SHA256_BUILTIN 1
+#cmakedefine GIT_SHA256_WIN32 1
+#cmakedefine GIT_SHA256_COMMON_CRYPTO 1
+#cmakedefine GIT_SHA256_OPENSSL 1
+#cmakedefine GIT_SHA256_OPENSSL_DYNAMIC 1
+#cmakedefine GIT_SHA256_MBEDTLS 1
+
+#cmakedefine GIT_RAND_GETENTROPY 1
+#cmakedefine GIT_RAND_GETLOADAVG 1
+
+#cmakedefine GIT_IO_POLL 1
+#cmakedefine GIT_IO_WSAPOLL 1
+#cmakedefine GIT_IO_SELECT 1
+
+#endif
diff --git a/src/util/git2_util.h b/src/util/git2_util.h
new file mode 100644
index 0000000..c62dc24
--- /dev/null
+++ b/src/util/git2_util.h
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_git2_util_h__
+#define INCLUDE_git2_util_h__
+
+#if !defined(LIBGIT2_NO_FEATURES_H)
+# include "git2_features.h"
+#endif
+
+#include "git2/common.h"
+#include "cc-compat.h"
+
+typedef struct git_str git_str;
+
+/** Declare a function as always inlined. */
+#if defined(_MSC_VER)
+# define GIT_INLINE(type) static __inline type
+#elif defined(__GNUC__)
+# define GIT_INLINE(type) static __inline__ type
+#else
+# define GIT_INLINE(type) static type
+#endif
+
+/** Support for gcc/clang __has_builtin intrinsic */
+#ifndef __has_builtin
+# define __has_builtin(x) 0
+#endif
+
+/**
+ * Declare that a function's return value must be used.
+ *
+ * Used mostly to guard against potential silent bugs at runtime. This is
+ * recommended to be added to functions that:
+ *
+ * - Allocate / reallocate memory. This prevents memory leaks or errors where
+ * buffers are expected to have grown to a certain size, but could not be
+ * resized.
+ * - Acquire locks. When a lock cannot be acquired, that will almost certainly
+ * cause a data race / undefined behavior.
+ */
+#if defined(__GNUC__)
+# define GIT_WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#else
+# define GIT_WARN_UNUSED_RESULT
+#endif
+
+#include <assert.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef GIT_WIN32
+
+# include <io.h>
+# include <direct.h>
+# include <winsock2.h>
+# include <windows.h>
+# include <ws2tcpip.h>
+# include "win32/msvc-compat.h"
+# include "win32/mingw-compat.h"
+# include "win32/win32-compat.h"
+# include "win32/w32_common.h"
+# include "win32/version.h"
+# include "win32/error.h"
+# ifdef GIT_THREADS
+# include "win32/thread.h"
+# endif
+
+#else
+
+# include <unistd.h>
+# include <strings.h>
+# ifdef GIT_THREADS
+# include <pthread.h>
+# include <sched.h>
+# endif
+
+#define GIT_LIBGIT2_CALL
+#define GIT_SYSTEM_CALL
+
+#ifdef GIT_USE_STAT_ATIMESPEC
+# define st_atim st_atimespec
+# define st_ctim st_ctimespec
+# define st_mtim st_mtimespec
+#endif
+
+# include <arpa/inet.h>
+
+#endif
+
+#include "git2/types.h"
+#include "git2/errors.h"
+#include "thread.h"
+#include "integer.h"
+#include "assert_safe.h"
+
+#include "posix.h"
+
+#define GIT_BUFSIZE_DEFAULT 65536
+#define GIT_BUFSIZE_FILEIO GIT_BUFSIZE_DEFAULT
+#define GIT_BUFSIZE_FILTERIO GIT_BUFSIZE_DEFAULT
+#define GIT_BUFSIZE_NETIO GIT_BUFSIZE_DEFAULT
+
+
+/**
+ * Check a pointer allocation result, returning -1 if it failed.
+ */
+#define GIT_ERROR_CHECK_ALLOC(ptr) do { \
+ if ((ptr) == NULL) { return -1; } \
+ } while(0)
+
+/**
+ * Check a buffer allocation result, returning -1 if it failed.
+ */
+#define GIT_ERROR_CHECK_ALLOC_STR(buf) do { \
+ if ((void *)(buf) == NULL || git_str_oom(buf)) { return -1; } \
+ } while(0)
+
+/**
+ * Check a return value and propagate result if non-zero.
+ */
+#define GIT_ERROR_CHECK_ERROR(code) \
+ do { int _err = (code); if (_err) return _err; } while (0)
+
+
+/** Check for additive overflow, setting an error if would occur. */
+#define GIT_ADD_SIZET_OVERFLOW(out, one, two) \
+ (git__add_sizet_overflow(out, one, two) ? (git_error_set_oom(), 1) : 0)
+
+/** Check for additive overflow, setting an error if would occur. */
+#define GIT_MULTIPLY_SIZET_OVERFLOW(out, nelem, elsize) \
+ (git__multiply_sizet_overflow(out, nelem, elsize) ? (git_error_set_oom(), 1) : 0)
+
+/** Check for additive overflow, failing if it would occur. */
+#define GIT_ERROR_CHECK_ALLOC_ADD(out, one, two) \
+ if (GIT_ADD_SIZET_OVERFLOW(out, one, two)) { return -1; }
+
+#define GIT_ERROR_CHECK_ALLOC_ADD3(out, one, two, three) \
+ if (GIT_ADD_SIZET_OVERFLOW(out, one, two) || \
+ GIT_ADD_SIZET_OVERFLOW(out, *(out), three)) { return -1; }
+
+#define GIT_ERROR_CHECK_ALLOC_ADD4(out, one, two, three, four) \
+ if (GIT_ADD_SIZET_OVERFLOW(out, one, two) || \
+ GIT_ADD_SIZET_OVERFLOW(out, *(out), three) || \
+ GIT_ADD_SIZET_OVERFLOW(out, *(out), four)) { return -1; }
+
+#define GIT_ERROR_CHECK_ALLOC_ADD5(out, one, two, three, four, five) \
+ if (GIT_ADD_SIZET_OVERFLOW(out, one, two) || \
+ GIT_ADD_SIZET_OVERFLOW(out, *(out), three) || \
+ GIT_ADD_SIZET_OVERFLOW(out, *(out), four) || \
+ GIT_ADD_SIZET_OVERFLOW(out, *(out), five)) { return -1; }
+
+/** Check for multiplicative overflow, failing if it would occur. */
+#define GIT_ERROR_CHECK_ALLOC_MULTIPLY(out, nelem, elsize) \
+ if (GIT_MULTIPLY_SIZET_OVERFLOW(out, nelem, elsize)) { return -1; }
+
+#include "util.h"
+
+#endif
diff --git a/src/util/hash.c b/src/util/hash.c
new file mode 100644
index 0000000..ff900ce
--- /dev/null
+++ b/src/util/hash.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "hash.h"
+
+int git_hash_global_init(void)
+{
+ if (git_hash_sha1_global_init() < 0 ||
+ git_hash_sha256_global_init() < 0)
+ return -1;
+
+ return 0;
+}
+
+int git_hash_ctx_init(git_hash_ctx *ctx, git_hash_algorithm_t algorithm)
+{
+ int error;
+
+ switch (algorithm) {
+ case GIT_HASH_ALGORITHM_SHA1:
+ error = git_hash_sha1_ctx_init(&ctx->ctx.sha1);
+ break;
+ case GIT_HASH_ALGORITHM_SHA256:
+ error = git_hash_sha256_ctx_init(&ctx->ctx.sha256);
+ break;
+ default:
+ git_error_set(GIT_ERROR_INTERNAL, "unknown hash algorithm");
+ error = -1;
+ }
+
+ ctx->algorithm = algorithm;
+ return error;
+}
+
+void git_hash_ctx_cleanup(git_hash_ctx *ctx)
+{
+ switch (ctx->algorithm) {
+ case GIT_HASH_ALGORITHM_SHA1:
+ git_hash_sha1_ctx_cleanup(&ctx->ctx.sha1);
+ return;
+ case GIT_HASH_ALGORITHM_SHA256:
+ git_hash_sha256_ctx_cleanup(&ctx->ctx.sha256);
+ return;
+ default:
+ /* unreachable */ ;
+ }
+}
+
+int git_hash_init(git_hash_ctx *ctx)
+{
+ switch (ctx->algorithm) {
+ case GIT_HASH_ALGORITHM_SHA1:
+ return git_hash_sha1_init(&ctx->ctx.sha1);
+ case GIT_HASH_ALGORITHM_SHA256:
+ return git_hash_sha256_init(&ctx->ctx.sha256);
+ default:
+ /* unreachable */ ;
+ }
+
+ git_error_set(GIT_ERROR_INTERNAL, "unknown hash algorithm");
+ return -1;
+}
+
+int git_hash_update(git_hash_ctx *ctx, const void *data, size_t len)
+{
+ switch (ctx->algorithm) {
+ case GIT_HASH_ALGORITHM_SHA1:
+ return git_hash_sha1_update(&ctx->ctx.sha1, data, len);
+ case GIT_HASH_ALGORITHM_SHA256:
+ return git_hash_sha256_update(&ctx->ctx.sha256, data, len);
+ default:
+ /* unreachable */ ;
+ }
+
+ git_error_set(GIT_ERROR_INTERNAL, "unknown hash algorithm");
+ return -1;
+}
+
+int git_hash_final(unsigned char *out, git_hash_ctx *ctx)
+{
+ switch (ctx->algorithm) {
+ case GIT_HASH_ALGORITHM_SHA1:
+ return git_hash_sha1_final(out, &ctx->ctx.sha1);
+ case GIT_HASH_ALGORITHM_SHA256:
+ return git_hash_sha256_final(out, &ctx->ctx.sha256);
+ default:
+ /* unreachable */ ;
+ }
+
+ git_error_set(GIT_ERROR_INTERNAL, "unknown hash algorithm");
+ return -1;
+}
+
+int git_hash_buf(
+ unsigned char *out,
+ const void *data,
+ size_t len,
+ git_hash_algorithm_t algorithm)
+{
+ git_hash_ctx ctx;
+ int error = 0;
+
+ if (git_hash_ctx_init(&ctx, algorithm) < 0)
+ return -1;
+
+ if ((error = git_hash_update(&ctx, data, len)) >= 0)
+ error = git_hash_final(out, &ctx);
+
+ git_hash_ctx_cleanup(&ctx);
+
+ return error;
+}
+
+int git_hash_vec(
+ unsigned char *out,
+ git_str_vec *vec,
+ size_t n,
+ git_hash_algorithm_t algorithm)
+{
+ git_hash_ctx ctx;
+ size_t i;
+ int error = 0;
+
+ if (git_hash_ctx_init(&ctx, algorithm) < 0)
+ return -1;
+
+ for (i = 0; i < n; i++) {
+ if ((error = git_hash_update(&ctx, vec[i].data, vec[i].len)) < 0)
+ goto done;
+ }
+
+ error = git_hash_final(out, &ctx);
+
+done:
+ git_hash_ctx_cleanup(&ctx);
+
+ return error;
+}
+
+int git_hash_fmt(char *out, unsigned char *hash, size_t hash_len)
+{
+ static char hex[] = "0123456789abcdef";
+ char *str = out;
+ size_t i;
+
+ for (i = 0; i < hash_len; i++) {
+ *str++ = hex[hash[i] >> 4];
+ *str++ = hex[hash[i] & 0x0f];
+ }
+
+ *str++ = '\0';
+
+ return 0;
+}
diff --git a/src/util/hash.h b/src/util/hash.h
new file mode 100644
index 0000000..21fcaf0
--- /dev/null
+++ b/src/util/hash.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_hash_h__
+#define INCLUDE_hash_h__
+
+#include "git2_util.h"
+
+#include "hash/sha.h"
+
+typedef struct {
+ void *data;
+ size_t len;
+} git_str_vec;
+
+typedef enum {
+ GIT_HASH_ALGORITHM_NONE = 0,
+ GIT_HASH_ALGORITHM_SHA1,
+ GIT_HASH_ALGORITHM_SHA256
+} git_hash_algorithm_t;
+
+#define GIT_HASH_MAX_SIZE GIT_HASH_SHA256_SIZE
+
+typedef struct git_hash_ctx {
+ union {
+ git_hash_sha1_ctx sha1;
+ git_hash_sha256_ctx sha256;
+ } ctx;
+ git_hash_algorithm_t algorithm;
+} git_hash_ctx;
+
+int git_hash_global_init(void);
+
+int git_hash_ctx_init(git_hash_ctx *ctx, git_hash_algorithm_t algorithm);
+void git_hash_ctx_cleanup(git_hash_ctx *ctx);
+
+int git_hash_init(git_hash_ctx *c);
+int git_hash_update(git_hash_ctx *c, const void *data, size_t len);
+int git_hash_final(unsigned char *out, git_hash_ctx *c);
+
+int git_hash_buf(unsigned char *out, const void *data, size_t len, git_hash_algorithm_t algorithm);
+int git_hash_vec(unsigned char *out, git_str_vec *vec, size_t n, git_hash_algorithm_t algorithm);
+
+int git_hash_fmt(char *out, unsigned char *hash, size_t hash_len);
+
+GIT_INLINE(size_t) git_hash_size(git_hash_algorithm_t algorithm) {
+ switch (algorithm) {
+ case GIT_HASH_ALGORITHM_SHA1:
+ return GIT_HASH_SHA1_SIZE;
+ case GIT_HASH_ALGORITHM_SHA256:
+ return GIT_HASH_SHA256_SIZE;
+ default:
+ return 0;
+ }
+}
+
+#endif
diff --git a/src/util/hash/builtin.c b/src/util/hash/builtin.c
new file mode 100644
index 0000000..cc4aa58
--- /dev/null
+++ b/src/util/hash/builtin.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "builtin.h"
+
+int git_hash_sha256_global_init(void)
+{
+ return 0;
+}
+
+int git_hash_sha256_ctx_init(git_hash_sha256_ctx *ctx)
+{
+ return git_hash_sha256_init(ctx);
+}
+
+void git_hash_sha256_ctx_cleanup(git_hash_sha256_ctx *ctx)
+{
+ GIT_UNUSED(ctx);
+}
+
+int git_hash_sha256_init(git_hash_sha256_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+ if (SHA256Reset(&ctx->c)) {
+ git_error_set(GIT_ERROR_SHA, "SHA256 error");
+ return -1;
+ }
+ return 0;
+}
+
+int git_hash_sha256_update(git_hash_sha256_ctx *ctx, const void *data, size_t len)
+{
+ GIT_ASSERT_ARG(ctx);
+ if (SHA256Input(&ctx->c, data, len)) {
+ git_error_set(GIT_ERROR_SHA, "SHA256 error");
+ return -1;
+ }
+ return 0;
+}
+
+int git_hash_sha256_final(unsigned char *out, git_hash_sha256_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+ if (SHA256Result(&ctx->c, out)) {
+ git_error_set(GIT_ERROR_SHA, "SHA256 error");
+ return -1;
+ }
+ return 0;
+}
diff --git a/src/util/hash/builtin.h b/src/util/hash/builtin.h
new file mode 100644
index 0000000..769df1a
--- /dev/null
+++ b/src/util/hash/builtin.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_hash_builtin_h__
+#define INCLUDE_hash_builtin_h__
+
+#include "hash/sha.h"
+
+#include "rfc6234/sha.h"
+
+struct git_hash_sha256_ctx {
+ SHA256Context c;
+};
+
+#endif
diff --git a/src/util/hash/collisiondetect.c b/src/util/hash/collisiondetect.c
new file mode 100644
index 0000000..c51a402
--- /dev/null
+++ b/src/util/hash/collisiondetect.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "collisiondetect.h"
+
+int git_hash_sha1_global_init(void)
+{
+ return 0;
+}
+
+int git_hash_sha1_ctx_init(git_hash_sha1_ctx *ctx)
+{
+ return git_hash_sha1_init(ctx);
+}
+
+void git_hash_sha1_ctx_cleanup(git_hash_sha1_ctx *ctx)
+{
+ GIT_UNUSED(ctx);
+}
+
+int git_hash_sha1_init(git_hash_sha1_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+ SHA1DCInit(&ctx->c);
+ return 0;
+}
+
+int git_hash_sha1_update(git_hash_sha1_ctx *ctx, const void *data, size_t len)
+{
+ GIT_ASSERT_ARG(ctx);
+ SHA1DCUpdate(&ctx->c, data, len);
+ return 0;
+}
+
+int git_hash_sha1_final(unsigned char *out, git_hash_sha1_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+ if (SHA1DCFinal(out, &ctx->c)) {
+ git_error_set(GIT_ERROR_SHA, "SHA1 collision attack detected");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/util/hash/collisiondetect.h b/src/util/hash/collisiondetect.h
new file mode 100644
index 0000000..8de5502
--- /dev/null
+++ b/src/util/hash/collisiondetect.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_hash_collisiondetect_h__
+#define INCLUDE_hash_collisiondetect_h__
+
+#include "hash/sha.h"
+
+#include "sha1dc/sha1.h"
+
+struct git_hash_sha1_ctx {
+ SHA1_CTX c;
+};
+
+#endif
diff --git a/src/util/hash/common_crypto.c b/src/util/hash/common_crypto.c
new file mode 100644
index 0000000..b327ba9
--- /dev/null
+++ b/src/util/hash/common_crypto.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "common_crypto.h"
+
+#define CC_LONG_MAX ((CC_LONG)-1)
+
+#ifdef GIT_SHA1_COMMON_CRYPTO
+
+int git_hash_sha1_global_init(void)
+{
+ return 0;
+}
+
+int git_hash_sha1_ctx_init(git_hash_sha1_ctx *ctx)
+{
+ return git_hash_sha1_init(ctx);
+}
+
+void git_hash_sha1_ctx_cleanup(git_hash_sha1_ctx *ctx)
+{
+ GIT_UNUSED(ctx);
+}
+
+int git_hash_sha1_init(git_hash_sha1_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+ CC_SHA1_Init(&ctx->c);
+ return 0;
+}
+
+int git_hash_sha1_update(git_hash_sha1_ctx *ctx, const void *_data, size_t len)
+{
+ const unsigned char *data = _data;
+
+ GIT_ASSERT_ARG(ctx);
+
+ while (len > 0) {
+ CC_LONG chunk = (len > CC_LONG_MAX) ? CC_LONG_MAX : (CC_LONG)len;
+
+ CC_SHA1_Update(&ctx->c, data, chunk);
+
+ data += chunk;
+ len -= chunk;
+ }
+
+ return 0;
+}
+
+int git_hash_sha1_final(unsigned char *out, git_hash_sha1_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+ CC_SHA1_Final(out, &ctx->c);
+ return 0;
+}
+
+#endif
+
+#ifdef GIT_SHA256_COMMON_CRYPTO
+
+int git_hash_sha256_global_init(void)
+{
+ return 0;
+}
+
+int git_hash_sha256_ctx_init(git_hash_sha256_ctx *ctx)
+{
+ return git_hash_sha256_init(ctx);
+}
+
+void git_hash_sha256_ctx_cleanup(git_hash_sha256_ctx *ctx)
+{
+ GIT_UNUSED(ctx);
+}
+
+int git_hash_sha256_init(git_hash_sha256_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+ CC_SHA256_Init(&ctx->c);
+ return 0;
+}
+
+int git_hash_sha256_update(git_hash_sha256_ctx *ctx, const void *_data, size_t len)
+{
+ const unsigned char *data = _data;
+
+ GIT_ASSERT_ARG(ctx);
+
+ while (len > 0) {
+ CC_LONG chunk = (len > CC_LONG_MAX) ? CC_LONG_MAX : (CC_LONG)len;
+
+ CC_SHA256_Update(&ctx->c, data, chunk);
+
+ data += chunk;
+ len -= chunk;
+ }
+
+ return 0;
+}
+
+int git_hash_sha256_final(unsigned char *out, git_hash_sha256_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+ CC_SHA256_Final(out, &ctx->c);
+ return 0;
+}
+
+#endif
diff --git a/src/util/hash/common_crypto.h b/src/util/hash/common_crypto.h
new file mode 100644
index 0000000..157712b
--- /dev/null
+++ b/src/util/hash/common_crypto.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_hash_common_crypto_h__
+#define INCLUDE_hash_common_crypto_h__
+
+#include "hash/sha.h"
+
+#include <CommonCrypto/CommonDigest.h>
+
+#ifdef GIT_SHA1_COMMON_CRYPTO
+struct git_hash_sha1_ctx {
+ CC_SHA1_CTX c;
+};
+#endif
+
+#ifdef GIT_SHA256_COMMON_CRYPTO
+struct git_hash_sha256_ctx {
+ CC_SHA256_CTX c;
+};
+#endif
+
+#endif
diff --git a/src/util/hash/mbedtls.c b/src/util/hash/mbedtls.c
new file mode 100644
index 0000000..ecdfb78
--- /dev/null
+++ b/src/util/hash/mbedtls.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "mbedtls.h"
+
+#ifdef GIT_SHA1_MBEDTLS
+
+int git_hash_sha1_global_init(void)
+{
+ return 0;
+}
+
+int git_hash_sha1_ctx_init(git_hash_sha1_ctx *ctx)
+{
+ return git_hash_sha1_init(ctx);
+}
+
+void git_hash_sha1_ctx_cleanup(git_hash_sha1_ctx *ctx)
+{
+ if (ctx)
+ mbedtls_sha1_free(&ctx->c);
+}
+
+int git_hash_sha1_init(git_hash_sha1_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+ mbedtls_sha1_init(&ctx->c);
+ mbedtls_sha1_starts(&ctx->c);
+ return 0;
+}
+
+int git_hash_sha1_update(git_hash_sha1_ctx *ctx, const void *data, size_t len)
+{
+ GIT_ASSERT_ARG(ctx);
+ mbedtls_sha1_update(&ctx->c, data, len);
+ return 0;
+}
+
+int git_hash_sha1_final(unsigned char *out, git_hash_sha1_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+ mbedtls_sha1_finish(&ctx->c, out);
+ return 0;
+}
+
+#endif
+
+#ifdef GIT_SHA256_MBEDTLS
+
+int git_hash_sha256_global_init(void)
+{
+ return 0;
+}
+
+int git_hash_sha256_ctx_init(git_hash_sha256_ctx *ctx)
+{
+ return git_hash_sha256_init(ctx);
+}
+
+void git_hash_sha256_ctx_cleanup(git_hash_sha256_ctx *ctx)
+{
+ if (ctx)
+ mbedtls_sha256_free(&ctx->c);
+}
+
+int git_hash_sha256_init(git_hash_sha256_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+ mbedtls_sha256_init(&ctx->c);
+ mbedtls_sha256_starts(&ctx->c, 0);
+ return 0;
+}
+
+int git_hash_sha256_update(git_hash_sha256_ctx *ctx, const void *data, size_t len)
+{
+ GIT_ASSERT_ARG(ctx);
+ mbedtls_sha256_update(&ctx->c, data, len);
+ return 0;
+}
+
+int git_hash_sha256_final(unsigned char *out, git_hash_sha256_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+ mbedtls_sha256_finish(&ctx->c, out);
+ return 0;
+}
+
+#endif
diff --git a/src/util/hash/mbedtls.h b/src/util/hash/mbedtls.h
new file mode 100644
index 0000000..05fb38b
--- /dev/null
+++ b/src/util/hash/mbedtls.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_hash_mbedtls_h__
+#define INCLUDE_hash_mbedtls_h__
+
+#include "hash/sha.h"
+
+#ifdef GIT_SHA1_MBEDTLS
+# include <mbedtls/sha1.h>
+
+struct git_hash_sha1_ctx {
+ mbedtls_sha1_context c;
+};
+#endif
+
+#ifdef GIT_SHA256_MBEDTLS
+# include <mbedtls/sha256.h>
+
+struct git_hash_sha256_ctx {
+ mbedtls_sha256_context c;
+};
+#endif
+
+#endif /* INCLUDE_hash_sha1_mbedtls_h__ */
diff --git a/src/util/hash/openssl.c b/src/util/hash/openssl.c
new file mode 100644
index 0000000..eaf91e7
--- /dev/null
+++ b/src/util/hash/openssl.c
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "openssl.h"
+
+#ifdef GIT_OPENSSL_DYNAMIC
+# include <dlfcn.h>
+
+static int handle_count;
+static void *openssl_handle;
+
+static int git_hash_openssl_global_shutdown(void)
+{
+ if (--handle_count == 0) {
+ dlclose(openssl_handle);
+ openssl_handle = NULL;
+ }
+
+ return 0;
+}
+
+static int git_hash_openssl_global_init(void)
+{
+ if (!handle_count) {
+ if ((openssl_handle = dlopen("libssl.so.1.1", RTLD_NOW)) == NULL &&
+ (openssl_handle = dlopen("libssl.1.1.dylib", RTLD_NOW)) == NULL &&
+ (openssl_handle = dlopen("libssl.so.1.0.0", RTLD_NOW)) == NULL &&
+ (openssl_handle = dlopen("libssl.1.0.0.dylib", RTLD_NOW)) == NULL &&
+ (openssl_handle = dlopen("libssl.so.10", RTLD_NOW)) == NULL &&
+ (openssl_handle = dlopen("libssl.so.3", RTLD_NOW)) == NULL) {
+ git_error_set(GIT_ERROR_SSL, "could not load ssl libraries");
+ return -1;
+ }
+ }
+
+ if (git_hash_openssl_global_shutdown() < 0)
+ return -1;
+
+ handle_count++;
+ return 0;
+}
+
+#endif
+
+#ifdef GIT_SHA1_OPENSSL
+
+# ifdef GIT_OPENSSL_DYNAMIC
+static int (*SHA1_Init)(SHA_CTX *c);
+static int (*SHA1_Update)(SHA_CTX *c, const void *data, size_t len);
+static int (*SHA1_Final)(unsigned char *md, SHA_CTX *c);
+# endif
+
+int git_hash_sha1_global_init(void)
+{
+#ifdef GIT_OPENSSL_DYNAMIC
+ if (git_hash_openssl_global_init() < 0)
+ return -1;
+
+ if ((SHA1_Init = dlsym(openssl_handle, "SHA1_Init")) == NULL ||
+ (SHA1_Update = dlsym(openssl_handle, "SHA1_Update")) == NULL ||
+ (SHA1_Final = dlsym(openssl_handle, "SHA1_Final")) == NULL) {
+ const char *msg = dlerror();
+ git_error_set(GIT_ERROR_SSL, "could not load hash function: %s", msg ? msg : "unknown error");
+ return -1;
+ }
+#endif
+
+ return 0;
+}
+
+int git_hash_sha1_ctx_init(git_hash_sha1_ctx *ctx)
+{
+ return git_hash_sha1_init(ctx);
+}
+
+void git_hash_sha1_ctx_cleanup(git_hash_sha1_ctx *ctx)
+{
+ GIT_UNUSED(ctx);
+}
+
+int git_hash_sha1_init(git_hash_sha1_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+
+ if (SHA1_Init(&ctx->c) != 1) {
+ git_error_set(GIT_ERROR_SHA, "failed to initialize sha1 context");
+ return -1;
+ }
+
+ return 0;
+}
+
+int git_hash_sha1_update(git_hash_sha1_ctx *ctx, const void *data, size_t len)
+{
+ GIT_ASSERT_ARG(ctx);
+
+ if (SHA1_Update(&ctx->c, data, len) != 1) {
+ git_error_set(GIT_ERROR_SHA, "failed to update sha1");
+ return -1;
+ }
+
+ return 0;
+}
+
+int git_hash_sha1_final(unsigned char *out, git_hash_sha1_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+
+ if (SHA1_Final(out, &ctx->c) != 1) {
+ git_error_set(GIT_ERROR_SHA, "failed to finalize sha1");
+ return -1;
+ }
+
+ return 0;
+}
+
+#endif
+
+#ifdef GIT_SHA256_OPENSSL
+
+# ifdef GIT_OPENSSL_DYNAMIC
+static int (*SHA256_Init)(SHA256_CTX *c);
+static int (*SHA256_Update)(SHA256_CTX *c, const void *data, size_t len);
+static int (*SHA256_Final)(unsigned char *md, SHA256_CTX *c);
+#endif
+
+int git_hash_sha256_global_init(void)
+{
+#ifdef GIT_OPENSSL_DYNAMIC
+ if (git_hash_openssl_global_init() < 0)
+ return -1;
+
+ if ((SHA256_Init = dlsym(openssl_handle, "SHA256_Init")) == NULL ||
+ (SHA256_Update = dlsym(openssl_handle, "SHA256_Update")) == NULL ||
+ (SHA256_Final = dlsym(openssl_handle, "SHA256_Final")) == NULL) {
+ const char *msg = dlerror();
+ git_error_set(GIT_ERROR_SSL, "could not load hash function: %s", msg ? msg : "unknown error");
+ return -1;
+ }
+#endif
+
+ return 0;
+}
+
+int git_hash_sha256_ctx_init(git_hash_sha256_ctx *ctx)
+{
+ return git_hash_sha256_init(ctx);
+}
+
+void git_hash_sha256_ctx_cleanup(git_hash_sha256_ctx *ctx)
+{
+ GIT_UNUSED(ctx);
+}
+
+int git_hash_sha256_init(git_hash_sha256_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+
+ if (SHA256_Init(&ctx->c) != 1) {
+ git_error_set(GIT_ERROR_SHA, "failed to initialize sha256 context");
+ return -1;
+ }
+
+ return 0;
+}
+
+int git_hash_sha256_update(git_hash_sha256_ctx *ctx, const void *data, size_t len)
+{
+ GIT_ASSERT_ARG(ctx);
+
+ if (SHA256_Update(&ctx->c, data, len) != 1) {
+ git_error_set(GIT_ERROR_SHA, "failed to update sha256");
+ return -1;
+ }
+
+ return 0;
+}
+
+int git_hash_sha256_final(unsigned char *out, git_hash_sha256_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+
+ if (SHA256_Final(out, &ctx->c) != 1) {
+ git_error_set(GIT_ERROR_SHA, "failed to finalize sha256");
+ return -1;
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/src/util/hash/openssl.h b/src/util/hash/openssl.h
new file mode 100644
index 0000000..7cb089a
--- /dev/null
+++ b/src/util/hash/openssl.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_hash_openssl_h__
+#define INCLUDE_hash_openssl_h__
+
+#include "hash/sha.h"
+
+#ifndef GIT_OPENSSL_DYNAMIC
+# include <openssl/sha.h>
+#else
+
+typedef struct {
+ unsigned int h0, h1, h2, h3, h4;
+ unsigned int Nl, Nh;
+ unsigned int data[16];
+ unsigned int num;
+} SHA_CTX;
+
+typedef struct {
+ unsigned int h[8];
+ unsigned int Nl, Nh;
+ unsigned int data[16];
+ unsigned int num, md_len;
+} SHA256_CTX;
+
+#endif
+
+#ifdef GIT_SHA1_OPENSSL
+struct git_hash_sha1_ctx {
+ SHA_CTX c;
+};
+#endif
+
+#ifdef GIT_SHA256_OPENSSL
+struct git_hash_sha256_ctx {
+ SHA256_CTX c;
+};
+#endif
+
+#endif
diff --git a/src/util/hash/rfc6234/sha.h b/src/util/hash/rfc6234/sha.h
new file mode 100644
index 0000000..0fbcc50
--- /dev/null
+++ b/src/util/hash/rfc6234/sha.h
@@ -0,0 +1,243 @@
+/**************************** sha.h ****************************/
+/***************** See RFC 6234 for details. *******************/
+/*
+ Copyright (c) 2011 IETF Trust and the persons identified as
+ authors of the code. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or
+ without modification, are permitted provided that the following
+ conditions are met:
+
+ - Redistributions of source code must retain the above
+ copyright notice, this list of conditions and
+ the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+
+ - Neither the name of Internet Society, IETF or IETF Trust, nor
+ the names of specific contributors, may be used to endorse or
+ promote products derived from this software without specific
+ prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef _SHA_H_
+#define _SHA_H_
+
+/*
+ * Description:
+ * This file implements the Secure Hash Algorithms
+ * as defined in the U.S. National Institute of Standards
+ * and Technology Federal Information Processing Standards
+ * Publication (FIPS PUB) 180-3 published in October 2008
+ * and formerly defined in its predecessors, FIPS PUB 180-1
+ * and FIP PUB 180-2.
+ *
+ * A combined document showing all algorithms is available at
+ * http://csrc.nist.gov/publications/fips/
+ * fips180-3/fips180-3_final.pdf
+ *
+ * The five hashes are defined in these sizes:
+ * SHA-1 20 byte / 160 bit
+ * SHA-224 28 byte / 224 bit
+ * SHA-256 32 byte / 256 bit
+ * SHA-384 48 byte / 384 bit
+ * SHA-512 64 byte / 512 bit
+ *
+ * Compilation Note:
+ * These files may be compiled with two options:
+ * USE_32BIT_ONLY - use 32-bit arithmetic only, for systems
+ * without 64-bit integers
+ *
+ * USE_MODIFIED_MACROS - use alternate form of the SHA_Ch()
+ * and SHA_Maj() macros that are equivalent
+ * and potentially faster on many systems
+ *
+ */
+
+#include <stdint.h>
+/*
+ * If you do not have the ISO standard stdint.h header file, then you
+ * must typedef the following:
+ * name meaning
+ * uint64_t unsigned 64-bit integer
+ * uint32_t unsigned 32-bit integer
+ * uint8_t unsigned 8-bit integer (i.e., unsigned char)
+ * int_least16_t integer of >= 16 bits
+ *
+ * See stdint-example.h
+ */
+
+#ifndef _SHA_enum_
+#define _SHA_enum_
+/*
+ * All SHA functions return one of these values.
+ */
+enum {
+ shaSuccess = 0,
+ shaNull, /* Null pointer parameter */
+ shaInputTooLong, /* input data too long */
+ shaStateError, /* called Input after FinalBits or Result */
+ shaBadParam /* passed a bad parameter */
+};
+#endif /* _SHA_enum_ */
+
+/*
+ * These constants hold size information for each of the SHA
+ * hashing operations
+ */
+enum {
+ SHA1_Message_Block_Size = 64, SHA224_Message_Block_Size = 64,
+ SHA256_Message_Block_Size = 64, SHA384_Message_Block_Size = 128,
+ SHA512_Message_Block_Size = 128,
+ USHA_Max_Message_Block_Size = SHA512_Message_Block_Size,
+ SHA1HashSize = 20, SHA224HashSize = 28, SHA256HashSize = 32,
+ SHA384HashSize = 48, SHA512HashSize = 64,
+ USHAMaxHashSize = SHA512HashSize,
+
+ SHA1HashSizeBits = 160, SHA224HashSizeBits = 224,
+ SHA256HashSizeBits = 256, SHA384HashSizeBits = 384,
+ SHA512HashSizeBits = 512, USHAMaxHashSizeBits = SHA512HashSizeBits
+};
+
+/*
+ * These constants are used in the USHA (Unified SHA) functions.
+ */
+typedef enum SHAversion {
+ SHA1, SHA224, SHA256, SHA384, SHA512
+} SHAversion;
+
+/*
+ * This structure will hold context information for the SHA-1
+ * hashing operation.
+ */
+typedef struct SHA1Context {
+ uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest */
+
+ uint32_t Length_High; /* Message length in bits */
+ uint32_t Length_Low; /* Message length in bits */
+
+ int_least16_t Message_Block_Index; /* Message_Block array index */
+ /* 512-bit message blocks */
+ uint8_t Message_Block[SHA1_Message_Block_Size];
+
+ int Computed; /* Is the hash computed? */
+ int Corrupted; /* Cumulative corruption code */
+} SHA1Context;
+
+/*
+ * This structure will hold context information for the SHA-256
+ * hashing operation.
+ */
+typedef struct SHA256Context {
+ uint32_t Intermediate_Hash[SHA256HashSize/4]; /* Message Digest */
+
+ uint32_t Length_High; /* Message length in bits */
+ uint32_t Length_Low; /* Message length in bits */
+
+ int_least16_t Message_Block_Index; /* Message_Block array index */
+ /* 512-bit message blocks */
+ uint8_t Message_Block[SHA256_Message_Block_Size];
+
+ int Computed; /* Is the hash computed? */
+ int Corrupted; /* Cumulative corruption code */
+} SHA256Context;
+
+/*
+ * This structure will hold context information for the SHA-512
+ * hashing operation.
+ */
+typedef struct SHA512Context {
+#ifdef USE_32BIT_ONLY
+ uint32_t Intermediate_Hash[SHA512HashSize/4]; /* Message Digest */
+ uint32_t Length[4]; /* Message length in bits */
+#else /* !USE_32BIT_ONLY */
+ uint64_t Intermediate_Hash[SHA512HashSize/8]; /* Message Digest */
+ uint64_t Length_High, Length_Low; /* Message length in bits */
+#endif /* USE_32BIT_ONLY */
+
+ int_least16_t Message_Block_Index; /* Message_Block array index */
+ /* 1024-bit message blocks */
+ uint8_t Message_Block[SHA512_Message_Block_Size];
+
+ int Computed; /* Is the hash computed?*/
+ int Corrupted; /* Cumulative corruption code */
+} SHA512Context;
+
+/*
+ * This structure will hold context information for the SHA-224
+ * hashing operation. It uses the SHA-256 structure for computation.
+ */
+typedef struct SHA256Context SHA224Context;
+
+/*
+ * This structure will hold context information for the SHA-384
+ * hashing operation. It uses the SHA-512 structure for computation.
+ */
+typedef struct SHA512Context SHA384Context;
+
+/*
+ * Function Prototypes
+ */
+
+/* SHA-1 */
+extern int SHA1Reset(SHA1Context *);
+extern int SHA1Input(SHA1Context *, const uint8_t *bytes,
+ unsigned int bytecount);
+extern int SHA1FinalBits(SHA1Context *, uint8_t bits,
+ unsigned int bit_count);
+extern int SHA1Result(SHA1Context *,
+ uint8_t Message_Digest[SHA1HashSize]);
+
+/* SHA-224 */
+extern int SHA224Reset(SHA224Context *);
+extern int SHA224Input(SHA224Context *, const uint8_t *bytes,
+ unsigned int bytecount);
+extern int SHA224FinalBits(SHA224Context *, uint8_t bits,
+ unsigned int bit_count);
+extern int SHA224Result(SHA224Context *,
+ uint8_t Message_Digest[SHA224HashSize]);
+
+/* SHA-256 */
+extern int SHA256Reset(SHA256Context *);
+extern int SHA256Input(SHA256Context *, const uint8_t *bytes,
+ unsigned int bytecount);
+extern int SHA256FinalBits(SHA256Context *, uint8_t bits,
+ unsigned int bit_count);
+extern int SHA256Result(SHA256Context *,
+ uint8_t Message_Digest[SHA256HashSize]);
+
+/* SHA-384 */
+extern int SHA384Reset(SHA384Context *);
+extern int SHA384Input(SHA384Context *, const uint8_t *bytes,
+ unsigned int bytecount);
+extern int SHA384FinalBits(SHA384Context *, uint8_t bits,
+ unsigned int bit_count);
+extern int SHA384Result(SHA384Context *,
+ uint8_t Message_Digest[SHA384HashSize]);
+
+/* SHA-512 */
+extern int SHA512Reset(SHA512Context *);
+extern int SHA512Input(SHA512Context *, const uint8_t *bytes,
+ unsigned int bytecount);
+extern int SHA512FinalBits(SHA512Context *, uint8_t bits,
+ unsigned int bit_count);
+extern int SHA512Result(SHA512Context *,
+ uint8_t Message_Digest[SHA512HashSize]);
+
+#endif /* _SHA_H_ */
diff --git a/src/util/hash/rfc6234/sha224-256.c b/src/util/hash/rfc6234/sha224-256.c
new file mode 100644
index 0000000..c8e0cf8
--- /dev/null
+++ b/src/util/hash/rfc6234/sha224-256.c
@@ -0,0 +1,601 @@
+/************************* sha224-256.c ************************/
+/***************** See RFC 6234 for details. *******************/
+/* Copyright (c) 2011 IETF Trust and the persons identified as */
+/* authors of the code. All rights reserved. */
+/* See sha.h for terms of use and redistribution. */
+
+/*
+ * Description:
+ * This file implements the Secure Hash Algorithms SHA-224 and
+ * SHA-256 as defined in the U.S. National Institute of Standards
+ * and Technology Federal Information Processing Standards
+ * Publication (FIPS PUB) 180-3 published in October 2008
+ * and formerly defined in its predecessors, FIPS PUB 180-1
+ * and FIP PUB 180-2.
+ *
+ * A combined document showing all algorithms is available at
+ * http://csrc.nist.gov/publications/fips/
+ * fips180-3/fips180-3_final.pdf
+ *
+ * The SHA-224 and SHA-256 algorithms produce 224-bit and 256-bit
+ * message digests for a given data stream. It should take about
+ * 2**n steps to find a message with the same digest as a given
+ * message and 2**(n/2) to find any two messages with the same
+ * digest, when n is the digest size in bits. Therefore, this
+ * algorithm can serve as a means of providing a
+ * "fingerprint" for a message.
+ *
+ * Portability Issues:
+ * SHA-224 and SHA-256 are defined in terms of 32-bit "words".
+ * This code uses <stdint.h> (included via "sha.h") to define 32-
+ * and 8-bit unsigned integer types. If your C compiler does not
+ * support 32-bit unsigned integers, this code is not
+ * appropriate.
+ *
+ * Caveats:
+ * SHA-224 and SHA-256 are designed to work with messages less
+ * than 2^64 bits long. This implementation uses SHA224/256Input()
+ * to hash the bits that are a multiple of the size of an 8-bit
+ * octet, and then optionally uses SHA224/256FinalBits()
+ * to hash the final few bits of the input.
+ */
+
+#include "sha.h"
+
+/*
+ * These definitions are defined in FIPS 180-3, section 4.1.
+ * Ch() and Maj() are defined identically in sections 4.1.1,
+ * 4.1.2, and 4.1.3.
+ *
+ * The definitions used in FIPS 180-3 are as follows:
+ */
+#ifndef USE_MODIFIED_MACROS
+#define SHA_Ch(x,y,z) (((x) & (y)) ^ ((~(x)) & (z)))
+#define SHA_Maj(x,y,z) (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
+#else /* USE_MODIFIED_MACROS */
+/*
+ * The following definitions are equivalent and potentially faster.
+ */
+#define SHA_Ch(x, y, z) (((x) & ((y) ^ (z))) ^ (z))
+#define SHA_Maj(x, y, z) (((x) & ((y) | (z))) | ((y) & (z)))
+#endif /* USE_MODIFIED_MACROS */
+
+#define SHA_Parity(x, y, z) ((x) ^ (y) ^ (z))
+
+/* Define the SHA shift, rotate left, and rotate right macros */
+#define SHA256_SHR(bits,word) ((word) >> (bits))
+#define SHA256_ROTL(bits,word) \
+ (((word) << (bits)) | ((word) >> (32-(bits))))
+#define SHA256_ROTR(bits,word) \
+ (((word) >> (bits)) | ((word) << (32-(bits))))
+
+/* Define the SHA SIGMA and sigma macros */
+#define SHA256_SIGMA0(word) \
+ (SHA256_ROTR( 2,word) ^ SHA256_ROTR(13,word) ^ SHA256_ROTR(22,word))
+#define SHA256_SIGMA1(word) \
+ (SHA256_ROTR( 6,word) ^ SHA256_ROTR(11,word) ^ SHA256_ROTR(25,word))
+#define SHA256_sigma0(word) \
+ (SHA256_ROTR( 7,word) ^ SHA256_ROTR(18,word) ^ SHA256_SHR( 3,word))
+#define SHA256_sigma1(word) \
+ (SHA256_ROTR(17,word) ^ SHA256_ROTR(19,word) ^ SHA256_SHR(10,word))
+
+/*
+ * Add "length" to the length.
+ * Set Corrupted when overflow has occurred.
+ */
+static uint32_t addTemp;
+#define SHA224_256AddLength(context, length) \
+ (addTemp = (context)->Length_Low, (context)->Corrupted = \
+ (((context)->Length_Low += (length)) < addTemp) && \
+ (++(context)->Length_High == 0) ? shaInputTooLong : \
+ (context)->Corrupted )
+
+/* Local Function Prototypes */
+static int SHA224_256Reset(SHA256Context *context, uint32_t *H0);
+static void SHA224_256ProcessMessageBlock(SHA256Context *context);
+static void SHA224_256Finalize(SHA256Context *context,
+ uint8_t Pad_Byte);
+static void SHA224_256PadMessage(SHA256Context *context,
+ uint8_t Pad_Byte);
+static int SHA224_256ResultN(SHA256Context *context,
+ uint8_t Message_Digest[ ], int HashSize);
+
+/* Initial Hash Values: FIPS 180-3 section 5.3.2 */
+static uint32_t SHA224_H0[SHA256HashSize/4] = {
+ 0xC1059ED8, 0x367CD507, 0x3070DD17, 0xF70E5939,
+ 0xFFC00B31, 0x68581511, 0x64F98FA7, 0xBEFA4FA4
+};
+
+/* Initial Hash Values: FIPS 180-3 section 5.3.3 */
+static uint32_t SHA256_H0[SHA256HashSize/4] = {
+ 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
+ 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19
+};
+
+/*
+ * SHA224Reset
+ *
+ * Description:
+ * This function will initialize the SHA224Context in preparation
+ * for computing a new SHA224 message digest.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to reset.
+ *
+ * Returns:
+ * sha Error Code.
+ */
+int SHA224Reset(SHA224Context *context)
+{
+ return SHA224_256Reset(context, SHA224_H0);
+}
+
+/*
+ * SHA224Input
+ *
+ * Description:
+ * This function accepts an array of octets as the next portion
+ * of the message.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The SHA context to update.
+ * message_array[ ]: [in]
+ * An array of octets representing the next portion of
+ * the message.
+ * length: [in]
+ * The length of the message in message_array.
+ *
+ * Returns:
+ * sha Error Code.
+ *
+ */
+int SHA224Input(SHA224Context *context, const uint8_t *message_array,
+ unsigned int length)
+{
+ return SHA256Input(context, message_array, length);
+}
+
+/*
+ * SHA224FinalBits
+ *
+ * Description:
+ * This function will add in any final bits of the message.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The SHA context to update.
+ * message_bits: [in]
+ * The final bits of the message, in the upper portion of the
+ * byte. (Use 0b###00000 instead of 0b00000### to input the
+ * three bits ###.)
+ * length: [in]
+ * The number of bits in message_bits, between 1 and 7.
+ *
+ * Returns:
+ * sha Error Code.
+ */
+int SHA224FinalBits(SHA224Context *context,
+ uint8_t message_bits, unsigned int length)
+{
+ return SHA256FinalBits(context, message_bits, length);
+}
+
+/*
+ * SHA224Result
+ *
+ * Description:
+ * This function will return the 224-bit message digest
+ * into the Message_Digest array provided by the caller.
+ * NOTE:
+ * The first octet of hash is stored in the element with index 0,
+ * the last octet of hash in the element with index 27.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to use to calculate the SHA hash.
+ * Message_Digest[ ]: [out]
+ * Where the digest is returned.
+ *
+ * Returns:
+ * sha Error Code.
+ */
+int SHA224Result(SHA224Context *context,
+ uint8_t Message_Digest[SHA224HashSize])
+{
+ return SHA224_256ResultN(context, Message_Digest, SHA224HashSize);
+}
+
+/*
+ * SHA256Reset
+ *
+ * Description:
+ * This function will initialize the SHA256Context in preparation
+ * for computing a new SHA256 message digest.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to reset.
+ *
+ * Returns:
+ * sha Error Code.
+ */
+int SHA256Reset(SHA256Context *context)
+{
+ return SHA224_256Reset(context, SHA256_H0);
+}
+
+/*
+ * SHA256Input
+ *
+ * Description:
+ * This function accepts an array of octets as the next portion
+ * of the message.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The SHA context to update.
+ * message_array[ ]: [in]
+ * An array of octets representing the next portion of
+ * the message.
+ * length: [in]
+ * The length of the message in message_array.
+ *
+ * Returns:
+ * sha Error Code.
+ */
+int SHA256Input(SHA256Context *context, const uint8_t *message_array,
+ unsigned int length)
+{
+ if (!context) return shaNull;
+ if (!length) return shaSuccess;
+ if (!message_array) return shaNull;
+ if (context->Computed) return context->Corrupted = shaStateError;
+ if (context->Corrupted) return context->Corrupted;
+
+ while (length--) {
+ context->Message_Block[context->Message_Block_Index++] =
+ *message_array;
+
+ if ((SHA224_256AddLength(context, 8) == shaSuccess) &&
+ (context->Message_Block_Index == SHA256_Message_Block_Size))
+ SHA224_256ProcessMessageBlock(context);
+
+ message_array++;
+ }
+
+ return context->Corrupted;
+
+}
+
+/*
+ * SHA256FinalBits
+ *
+ * Description:
+ * This function will add in any final bits of the message.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The SHA context to update.
+ * message_bits: [in]
+ * The final bits of the message, in the upper portion of the
+ * byte. (Use 0b###00000 instead of 0b00000### to input the
+ * three bits ###.)
+ * length: [in]
+ * The number of bits in message_bits, between 1 and 7.
+ *
+ * Returns:
+ * sha Error Code.
+ */
+int SHA256FinalBits(SHA256Context *context,
+ uint8_t message_bits, unsigned int length)
+{
+ static uint8_t masks[8] = {
+ /* 0 0b00000000 */ 0x00, /* 1 0b10000000 */ 0x80,
+ /* 2 0b11000000 */ 0xC0, /* 3 0b11100000 */ 0xE0,
+ /* 4 0b11110000 */ 0xF0, /* 5 0b11111000 */ 0xF8,
+ /* 6 0b11111100 */ 0xFC, /* 7 0b11111110 */ 0xFE
+ };
+ static uint8_t markbit[8] = {
+ /* 0 0b10000000 */ 0x80, /* 1 0b01000000 */ 0x40,
+ /* 2 0b00100000 */ 0x20, /* 3 0b00010000 */ 0x10,
+ /* 4 0b00001000 */ 0x08, /* 5 0b00000100 */ 0x04,
+ /* 6 0b00000010 */ 0x02, /* 7 0b00000001 */ 0x01
+ };
+
+ if (!context) return shaNull;
+ if (!length) return shaSuccess;
+ if (context->Corrupted) return context->Corrupted;
+ if (context->Computed) return context->Corrupted = shaStateError;
+ if (length >= 8) return context->Corrupted = shaBadParam;
+
+ SHA224_256AddLength(context, length);
+ SHA224_256Finalize(context, (uint8_t)
+ ((message_bits & masks[length]) | markbit[length]));
+
+ return context->Corrupted;
+}
+
+/*
+ * SHA256Result
+ *
+ * Description:
+ * This function will return the 256-bit message digest
+ * into the Message_Digest array provided by the caller.
+ * NOTE:
+ * The first octet of hash is stored in the element with index 0,
+ * the last octet of hash in the element with index 31.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to use to calculate the SHA hash.
+ * Message_Digest[ ]: [out]
+ * Where the digest is returned.
+ *
+ * Returns:
+ * sha Error Code.
+ */
+int SHA256Result(SHA256Context *context,
+ uint8_t Message_Digest[SHA256HashSize])
+{
+ return SHA224_256ResultN(context, Message_Digest, SHA256HashSize);
+}
+
+/*
+ * SHA224_256Reset
+ *
+ * Description:
+ * This helper function will initialize the SHA256Context in
+ * preparation for computing a new SHA-224 or SHA-256 message digest.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to reset.
+ * H0[ ]: [in]
+ * The initial hash value array to use.
+ *
+ * Returns:
+ * sha Error Code.
+ */
+static int SHA224_256Reset(SHA256Context *context, uint32_t *H0)
+{
+ if (!context) return shaNull;
+
+ context->Length_High = context->Length_Low = 0;
+ context->Message_Block_Index = 0;
+
+ context->Intermediate_Hash[0] = H0[0];
+ context->Intermediate_Hash[1] = H0[1];
+ context->Intermediate_Hash[2] = H0[2];
+ context->Intermediate_Hash[3] = H0[3];
+ context->Intermediate_Hash[4] = H0[4];
+ context->Intermediate_Hash[5] = H0[5];
+ context->Intermediate_Hash[6] = H0[6];
+ context->Intermediate_Hash[7] = H0[7];
+
+ context->Computed = 0;
+ context->Corrupted = shaSuccess;
+
+ return shaSuccess;
+}
+
+/*
+ * SHA224_256ProcessMessageBlock
+ *
+ * Description:
+ * This helper function will process the next 512 bits of the
+ * message stored in the Message_Block array.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The SHA context to update.
+ *
+ * Returns:
+ * Nothing.
+ *
+ * Comments:
+ * Many of the variable names in this code, especially the
+ * single character names, were used because those were the
+ * names used in the Secure Hash Standard.
+ */
+static void SHA224_256ProcessMessageBlock(SHA256Context *context)
+{
+ /* Constants defined in FIPS 180-3, section 4.2.2 */
+ static const uint32_t K[64] = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b,
+ 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01,
+ 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7,
+ 0xc19bf174, 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+ 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152,
+ 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147,
+ 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,
+ 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819,
+ 0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08,
+ 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f,
+ 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+ };
+ int t, t4; /* Loop counter */
+ uint32_t temp1, temp2; /* Temporary word value */
+ uint32_t W[64]; /* Word sequence */
+ uint32_t A, B, C, D, E, F, G, H; /* Word buffers */
+
+ /*
+ * Initialize the first 16 words in the array W
+ */
+ for (t = t4 = 0; t < 16; t++, t4 += 4)
+ W[t] = (((uint32_t)context->Message_Block[t4]) << 24) |
+ (((uint32_t)context->Message_Block[t4 + 1]) << 16) |
+ (((uint32_t)context->Message_Block[t4 + 2]) << 8) |
+ (((uint32_t)context->Message_Block[t4 + 3]));
+
+ for (t = 16; t < 64; t++)
+ W[t] = SHA256_sigma1(W[t-2]) + W[t-7] +
+ SHA256_sigma0(W[t-15]) + W[t-16];
+
+ A = context->Intermediate_Hash[0];
+ B = context->Intermediate_Hash[1];
+ C = context->Intermediate_Hash[2];
+ D = context->Intermediate_Hash[3];
+ E = context->Intermediate_Hash[4];
+ F = context->Intermediate_Hash[5];
+ G = context->Intermediate_Hash[6];
+ H = context->Intermediate_Hash[7];
+
+ for (t = 0; t < 64; t++) {
+ temp1 = H + SHA256_SIGMA1(E) + SHA_Ch(E,F,G) + K[t] + W[t];
+ temp2 = SHA256_SIGMA0(A) + SHA_Maj(A,B,C);
+ H = G;
+ G = F;
+ F = E;
+ E = D + temp1;
+ D = C;
+ C = B;
+ B = A;
+ A = temp1 + temp2;
+ }
+
+ context->Intermediate_Hash[0] += A;
+ context->Intermediate_Hash[1] += B;
+ context->Intermediate_Hash[2] += C;
+ context->Intermediate_Hash[3] += D;
+ context->Intermediate_Hash[4] += E;
+ context->Intermediate_Hash[5] += F;
+ context->Intermediate_Hash[6] += G;
+ context->Intermediate_Hash[7] += H;
+
+ context->Message_Block_Index = 0;
+}
+
+/*
+ * SHA224_256Finalize
+ *
+ * Description:
+ * This helper function finishes off the digest calculations.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The SHA context to update.
+ * Pad_Byte: [in]
+ * The last byte to add to the message block before the 0-padding
+ * and length. This will contain the last bits of the message
+ * followed by another single bit. If the message was an
+ * exact multiple of 8-bits long, Pad_Byte will be 0x80.
+ *
+ * Returns:
+ * sha Error Code.
+ */
+static void SHA224_256Finalize(SHA256Context *context,
+ uint8_t Pad_Byte)
+{
+ int i;
+ SHA224_256PadMessage(context, Pad_Byte);
+ /* message may be sensitive, so clear it out */
+ for (i = 0; i < SHA256_Message_Block_Size; ++i)
+ context->Message_Block[i] = 0;
+ context->Length_High = 0; /* and clear length */
+ context->Length_Low = 0;
+ context->Computed = 1;
+}
+
+/*
+ * SHA224_256PadMessage
+ *
+ * Description:
+ * According to the standard, the message must be padded to the next
+ * even multiple of 512 bits. The first padding bit must be a '1'.
+ * The last 64 bits represent the length of the original message.
+ * All bits in between should be 0. This helper function will pad
+ * the message according to those rules by filling the
+ * Message_Block array accordingly. When it returns, it can be
+ * assumed that the message digest has been computed.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to pad.
+ * Pad_Byte: [in]
+ * The last byte to add to the message block before the 0-padding
+ * and length. This will contain the last bits of the message
+ * followed by another single bit. If the message was an
+ * exact multiple of 8-bits long, Pad_Byte will be 0x80.
+ *
+ * Returns:
+ * Nothing.
+ */
+static void SHA224_256PadMessage(SHA256Context *context,
+ uint8_t Pad_Byte)
+{
+ /*
+ * Check to see if the current message block is too small to hold
+ * the initial padding bits and length. If so, we will pad the
+ * block, process it, and then continue padding into a second
+ * block.
+ */
+ if (context->Message_Block_Index >= (SHA256_Message_Block_Size-8)) {
+ context->Message_Block[context->Message_Block_Index++] = Pad_Byte;
+ while (context->Message_Block_Index < SHA256_Message_Block_Size)
+ context->Message_Block[context->Message_Block_Index++] = 0;
+ SHA224_256ProcessMessageBlock(context);
+ } else
+ context->Message_Block[context->Message_Block_Index++] = Pad_Byte;
+
+ while (context->Message_Block_Index < (SHA256_Message_Block_Size-8))
+ context->Message_Block[context->Message_Block_Index++] = 0;
+
+ /*
+ * Store the message length as the last 8 octets
+ */
+ context->Message_Block[56] = (uint8_t)(context->Length_High >> 24);
+ context->Message_Block[57] = (uint8_t)(context->Length_High >> 16);
+ context->Message_Block[58] = (uint8_t)(context->Length_High >> 8);
+ context->Message_Block[59] = (uint8_t)(context->Length_High);
+ context->Message_Block[60] = (uint8_t)(context->Length_Low >> 24);
+ context->Message_Block[61] = (uint8_t)(context->Length_Low >> 16);
+ context->Message_Block[62] = (uint8_t)(context->Length_Low >> 8);
+ context->Message_Block[63] = (uint8_t)(context->Length_Low);
+
+ SHA224_256ProcessMessageBlock(context);
+}
+
+/*
+ * SHA224_256ResultN
+ *
+ * Description:
+ * This helper function will return the 224-bit or 256-bit message
+ * digest into the Message_Digest array provided by the caller.
+ * NOTE:
+ * The first octet of hash is stored in the element with index 0,
+ * the last octet of hash in the element with index 27/31.
+ *
+ * Parameters:
+ * context: [in/out]
+ * The context to use to calculate the SHA hash.
+ * Message_Digest[ ]: [out]
+ * Where the digest is returned.
+ * HashSize: [in]
+ * The size of the hash, either 28 or 32.
+ *
+ * Returns:
+ * sha Error Code.
+ */
+static int SHA224_256ResultN(SHA256Context *context,
+ uint8_t Message_Digest[ ], int HashSize)
+{
+ int i;
+
+ if (!context) return shaNull;
+ if (!Message_Digest) return shaNull;
+ if (context->Corrupted) return context->Corrupted;
+
+ if (!context->Computed)
+ SHA224_256Finalize(context, 0x80);
+
+ for (i = 0; i < HashSize; ++i)
+ Message_Digest[i] = (uint8_t)
+ (context->Intermediate_Hash[i>>2] >> 8 * ( 3 - ( i & 0x03 ) ));
+
+ return shaSuccess;
+}
+
diff --git a/src/util/hash/sha.h b/src/util/hash/sha.h
new file mode 100644
index 0000000..4f59623
--- /dev/null
+++ b/src/util/hash/sha.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_hash_sha_h__
+#define INCLUDE_hash_sha_h__
+
+#include "git2_util.h"
+
+typedef struct git_hash_sha1_ctx git_hash_sha1_ctx;
+typedef struct git_hash_sha256_ctx git_hash_sha256_ctx;
+
+#if defined(GIT_SHA1_COMMON_CRYPTO) || defined(GIT_SHA256_COMMON_CRYPTO)
+# include "common_crypto.h"
+#endif
+
+#if defined(GIT_SHA1_OPENSSL) || defined(GIT_SHA256_OPENSSL)
+# include "openssl.h"
+#endif
+
+#if defined(GIT_SHA1_WIN32) || defined(GIT_SHA256_WIN32)
+# include "win32.h"
+#endif
+
+#if defined(GIT_SHA1_MBEDTLS) || defined(GIT_SHA256_MBEDTLS)
+# include "mbedtls.h"
+#endif
+
+#if defined(GIT_SHA1_COLLISIONDETECT)
+# include "collisiondetect.h"
+#endif
+
+#if defined(GIT_SHA256_BUILTIN)
+# include "builtin.h"
+#endif
+
+/*
+ * SHA1
+ */
+
+#define GIT_HASH_SHA1_SIZE 20
+
+int git_hash_sha1_global_init(void);
+
+int git_hash_sha1_ctx_init(git_hash_sha1_ctx *ctx);
+void git_hash_sha1_ctx_cleanup(git_hash_sha1_ctx *ctx);
+
+int git_hash_sha1_init(git_hash_sha1_ctx *c);
+int git_hash_sha1_update(git_hash_sha1_ctx *c, const void *data, size_t len);
+int git_hash_sha1_final(unsigned char *out, git_hash_sha1_ctx *c);
+
+/*
+ * SHA256
+ */
+
+#define GIT_HASH_SHA256_SIZE 32
+
+int git_hash_sha256_global_init(void);
+
+int git_hash_sha256_ctx_init(git_hash_sha256_ctx *ctx);
+void git_hash_sha256_ctx_cleanup(git_hash_sha256_ctx *ctx);
+
+int git_hash_sha256_init(git_hash_sha256_ctx *c);
+int git_hash_sha256_update(git_hash_sha256_ctx *c, const void *data, size_t len);
+int git_hash_sha256_final(unsigned char *out, git_hash_sha256_ctx *c);
+
+#endif
diff --git a/src/util/hash/sha1dc/sha1.c b/src/util/hash/sha1dc/sha1.c
new file mode 100644
index 0000000..9298227
--- /dev/null
+++ b/src/util/hash/sha1dc/sha1.c
@@ -0,0 +1,1909 @@
+/***
+* Copyright 2017 Marc Stevens <marc@marc-stevens.nl>, Dan Shumow (danshu@microsoft.com)
+* Distributed under the MIT Software License.
+* See accompanying file LICENSE.txt or copy at
+* https://opensource.org/licenses/MIT
+***/
+
+#ifndef SHA1DC_NO_STANDARD_INCLUDES
+#include <string.h>
+#include <memory.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h> /* make sure macros like _BIG_ENDIAN visible */
+#endif
+
+#ifdef SHA1DC_CUSTOM_INCLUDE_SHA1_C
+#include SHA1DC_CUSTOM_INCLUDE_SHA1_C
+#endif
+
+#ifndef SHA1DC_INIT_SAFE_HASH_DEFAULT
+#define SHA1DC_INIT_SAFE_HASH_DEFAULT 1
+#endif
+
+#include "sha1.h"
+#include "ubc_check.h"
+
+#if (defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || \
+ defined(i386) || defined(__i386) || defined(__i386__) || defined(__i486__) || \
+ defined(__i586__) || defined(__i686__) || defined(_M_IX86) || defined(__X86__) || \
+ defined(_X86_) || defined(__THW_INTEL__) || defined(__I86__) || defined(__INTEL__) || \
+ defined(__386) || defined(_M_X64) || defined(_M_AMD64))
+#define SHA1DC_ON_INTEL_LIKE_PROCESSOR
+#endif
+
+/*
+ Because Little-Endian architectures are most common,
+ we only set SHA1DC_BIGENDIAN if one of these conditions is met.
+ Note that all MSFT platforms are little endian,
+ so none of these will be defined under the MSC compiler.
+ If you are compiling on a big endian platform and your compiler does not define one of these,
+ you will have to add whatever macros your tool chain defines to indicate Big-Endianness.
+ */
+
+#if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__)
+/*
+ * Should detect Big Endian under GCC since at least 4.6.0 (gcc svn
+ * rev #165881). See
+ * https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html
+ *
+ * This also works under clang since 3.2, it copied the GCC-ism. See
+ * clang.git's 3b198a97d2 ("Preprocessor: add __BYTE_ORDER__
+ * predefined macro", 2012-07-27)
+ */
+#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+#define SHA1DC_BIGENDIAN
+#endif
+
+/* Not under GCC-alike */
+#elif defined(__BYTE_ORDER) && defined(__BIG_ENDIAN)
+/*
+ * Should detect Big Endian under glibc.git since 14245eb70e ("entered
+ * into RCS", 1992-11-25). Defined in <endian.h> which will have been
+ * brought in by standard headers. See glibc.git and
+ * https://sourceforge.net/p/predef/wiki/Endianness/
+ */
+#if __BYTE_ORDER == __BIG_ENDIAN
+#define SHA1DC_BIGENDIAN
+#endif
+
+/* Not under GCC-alike or glibc */
+#elif defined(_BYTE_ORDER) && defined(_BIG_ENDIAN) && defined(_LITTLE_ENDIAN)
+/*
+ * *BSD and newlib (embedded linux, cygwin, etc).
+ * the defined(_BIG_ENDIAN) && defined(_LITTLE_ENDIAN) part prevents
+ * this condition from matching with Solaris/sparc.
+ * (Solaris defines only one endian macro)
+ */
+#if _BYTE_ORDER == _BIG_ENDIAN
+#define SHA1DC_BIGENDIAN
+#endif
+
+/* Not under GCC-alike or glibc or *BSD or newlib */
+#elif (defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || \
+ defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || \
+ defined(__sparc))
+/*
+ * Should define Big Endian for a whitelist of known processors. See
+ * https://sourceforge.net/p/predef/wiki/Endianness/ and
+ * http://www.oracle.com/technetwork/server-storage/solaris/portingtosolaris-138514.html
+ */
+#define SHA1DC_BIGENDIAN
+
+/* Not under GCC-alike or glibc or *BSD or newlib or <processor whitelist> */
+#elif (defined(_AIX) || defined(__hpux))
+
+/*
+ * Defines Big Endian on a whitelist of OSs that are known to be Big
+ * Endian-only. See
+ * https://public-inbox.org/git/93056823-2740-d072-1ebd-46b440b33d7e@felt.demon.nl/
+ */
+#define SHA1DC_BIGENDIAN
+
+/* Not under GCC-alike or glibc or *BSD or newlib or <processor whitelist> or <os whitelist> */
+#elif defined(SHA1DC_ON_INTEL_LIKE_PROCESSOR)
+/*
+ * As a last resort before we do anything else we're not 100% sure
+ * about below, we blacklist specific processors here. We could add
+ * more, see e.g. https://wiki.debian.org/ArchitectureSpecificsMemo
+ */
+#else /* Not under GCC-alike or glibc or *BSD or newlib or <processor whitelist> or <os whitelist> or <processor blacklist> */
+
+/* We do nothing more here for now */
+/*#error "Uncomment this to see if you fall through all the detection"*/
+
+#endif /* Big Endian detection */
+
+#if (defined(SHA1DC_FORCE_LITTLEENDIAN) && defined(SHA1DC_BIGENDIAN))
+#undef SHA1DC_BIGENDIAN
+#endif
+#if (defined(SHA1DC_FORCE_BIGENDIAN) && !defined(SHA1DC_BIGENDIAN))
+#define SHA1DC_BIGENDIAN
+#endif
+/*ENDIANNESS SELECTION*/
+
+#ifndef SHA1DC_FORCE_ALIGNED_ACCESS
+#if defined(SHA1DC_FORCE_UNALIGNED_ACCESS) || defined(SHA1DC_ON_INTEL_LIKE_PROCESSOR)
+#define SHA1DC_ALLOW_UNALIGNED_ACCESS
+#endif /*UNALIGNED ACCESS DETECTION*/
+#endif /*FORCE ALIGNED ACCESS*/
+
+#define rotate_right(x,n) (((x)>>(n))|((x)<<(32-(n))))
+#define rotate_left(x,n) (((x)<<(n))|((x)>>(32-(n))))
+
+#define sha1_bswap32(x) \
+ {x = ((x << 8) & 0xFF00FF00) | ((x >> 8) & 0xFF00FF); x = (x << 16) | (x >> 16);}
+
+#define sha1_mix(W, t) (rotate_left(W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16], 1))
+
+#ifdef SHA1DC_BIGENDIAN
+ #define sha1_load(m, t, temp) { temp = m[t]; }
+#else
+ #define sha1_load(m, t, temp) { temp = m[t]; sha1_bswap32(temp); }
+#endif
+
+#define sha1_store(W, t, x) *(volatile uint32_t *)&W[t] = x
+
+#define sha1_f1(b,c,d) ((d)^((b)&((c)^(d))))
+#define sha1_f2(b,c,d) ((b)^(c)^(d))
+#define sha1_f3(b,c,d) (((b)&(c))+((d)&((b)^(c))))
+#define sha1_f4(b,c,d) ((b)^(c)^(d))
+
+#define HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, m, t) \
+ { e += rotate_left(a, 5) + sha1_f1(b,c,d) + 0x5A827999 + m[t]; b = rotate_left(b, 30); }
+#define HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, m, t) \
+ { e += rotate_left(a, 5) + sha1_f2(b,c,d) + 0x6ED9EBA1 + m[t]; b = rotate_left(b, 30); }
+#define HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, m, t) \
+ { e += rotate_left(a, 5) + sha1_f3(b,c,d) + 0x8F1BBCDC + m[t]; b = rotate_left(b, 30); }
+#define HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, m, t) \
+ { e += rotate_left(a, 5) + sha1_f4(b,c,d) + 0xCA62C1D6 + m[t]; b = rotate_left(b, 30); }
+
+#define HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, m, t) \
+ { b = rotate_right(b, 30); e -= rotate_left(a, 5) + sha1_f1(b,c,d) + 0x5A827999 + m[t]; }
+#define HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, m, t) \
+ { b = rotate_right(b, 30); e -= rotate_left(a, 5) + sha1_f2(b,c,d) + 0x6ED9EBA1 + m[t]; }
+#define HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, m, t) \
+ { b = rotate_right(b, 30); e -= rotate_left(a, 5) + sha1_f3(b,c,d) + 0x8F1BBCDC + m[t]; }
+#define HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, m, t) \
+ { b = rotate_right(b, 30); e -= rotate_left(a, 5) + sha1_f4(b,c,d) + 0xCA62C1D6 + m[t]; }
+
+#define SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(a, b, c, d, e, m, W, t, temp) \
+ {sha1_load(m, t, temp); sha1_store(W, t, temp); e += temp + rotate_left(a, 5) + sha1_f1(b,c,d) + 0x5A827999; b = rotate_left(b, 30);}
+
+#define SHA1COMPRESS_FULL_ROUND1_STEP_EXPAND(a, b, c, d, e, W, t, temp) \
+ {temp = sha1_mix(W, t); sha1_store(W, t, temp); e += temp + rotate_left(a, 5) + sha1_f1(b,c,d) + 0x5A827999; b = rotate_left(b, 30); }
+
+#define SHA1COMPRESS_FULL_ROUND2_STEP(a, b, c, d, e, W, t, temp) \
+ {temp = sha1_mix(W, t); sha1_store(W, t, temp); e += temp + rotate_left(a, 5) + sha1_f2(b,c,d) + 0x6ED9EBA1; b = rotate_left(b, 30); }
+
+#define SHA1COMPRESS_FULL_ROUND3_STEP(a, b, c, d, e, W, t, temp) \
+ {temp = sha1_mix(W, t); sha1_store(W, t, temp); e += temp + rotate_left(a, 5) + sha1_f3(b,c,d) + 0x8F1BBCDC; b = rotate_left(b, 30); }
+
+#define SHA1COMPRESS_FULL_ROUND4_STEP(a, b, c, d, e, W, t, temp) \
+ {temp = sha1_mix(W, t); sha1_store(W, t, temp); e += temp + rotate_left(a, 5) + sha1_f4(b,c,d) + 0xCA62C1D6; b = rotate_left(b, 30); }
+
+
+#define SHA1_STORE_STATE(i) states[i][0] = a; states[i][1] = b; states[i][2] = c; states[i][3] = d; states[i][4] = e;
+
+#ifdef BUILDNOCOLLDETECTSHA1COMPRESSION
+void sha1_compression(uint32_t ihv[5], const uint32_t m[16])
+{
+ uint32_t W[80];
+ uint32_t a,b,c,d,e;
+ unsigned i;
+
+ memcpy(W, m, 16 * 4);
+ for (i = 16; i < 80; ++i)
+ W[i] = sha1_mix(W, i);
+
+ a = ihv[0]; b = ihv[1]; c = ihv[2]; d = ihv[3]; e = ihv[4];
+
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 0);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 1);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 2);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 3);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 4);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 5);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 6);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 7);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 8);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 9);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 10);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 11);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 12);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 13);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 14);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 15);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 16);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 17);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 18);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 19);
+
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 20);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 21);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 22);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 23);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 24);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 25);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 26);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 27);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 28);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 29);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 30);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 31);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 32);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 33);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 34);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 35);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 36);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 37);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 38);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 39);
+
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 40);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 41);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 42);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 43);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 44);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 45);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 46);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 47);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 48);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 49);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 50);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 51);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 52);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 53);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 54);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 55);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 56);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 57);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 58);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 59);
+
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 60);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 61);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 62);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 63);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 64);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 65);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 66);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 67);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 68);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 69);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 70);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 71);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 72);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 73);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 74);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 75);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 76);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 77);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 78);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 79);
+
+ ihv[0] += a; ihv[1] += b; ihv[2] += c; ihv[3] += d; ihv[4] += e;
+}
+#endif /*BUILDNOCOLLDETECTSHA1COMPRESSION*/
+
+
+static void sha1_compression_W(uint32_t ihv[5], const uint32_t W[80])
+{
+ uint32_t a = ihv[0], b = ihv[1], c = ihv[2], d = ihv[3], e = ihv[4];
+
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 0);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 1);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 2);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 3);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 4);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 5);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 6);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 7);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 8);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 9);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 10);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 11);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 12);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 13);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 14);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 15);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 16);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 17);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 18);
+ HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 19);
+
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 20);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 21);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 22);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 23);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 24);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 25);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 26);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 27);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 28);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 29);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 30);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 31);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 32);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 33);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 34);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 35);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 36);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 37);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 38);
+ HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 39);
+
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 40);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 41);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 42);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 43);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 44);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 45);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 46);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 47);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 48);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 49);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 50);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 51);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 52);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 53);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 54);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 55);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 56);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 57);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 58);
+ HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 59);
+
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 60);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 61);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 62);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 63);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 64);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 65);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 66);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 67);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 68);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 69);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 70);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 71);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 72);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 73);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 74);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 75);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 76);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 77);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 78);
+ HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 79);
+
+ ihv[0] += a; ihv[1] += b; ihv[2] += c; ihv[3] += d; ihv[4] += e;
+}
+
+
+
+void sha1_compression_states(uint32_t ihv[5], const uint32_t m[16], uint32_t W[80], uint32_t states[80][5])
+{
+ uint32_t a = ihv[0], b = ihv[1], c = ihv[2], d = ihv[3], e = ihv[4];
+ uint32_t temp;
+
+#ifdef DOSTORESTATE00
+ SHA1_STORE_STATE(0)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(a, b, c, d, e, m, W, 0, temp);
+
+#ifdef DOSTORESTATE01
+ SHA1_STORE_STATE(1)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(e, a, b, c, d, m, W, 1, temp);
+
+#ifdef DOSTORESTATE02
+ SHA1_STORE_STATE(2)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(d, e, a, b, c, m, W, 2, temp);
+
+#ifdef DOSTORESTATE03
+ SHA1_STORE_STATE(3)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(c, d, e, a, b, m, W, 3, temp);
+
+#ifdef DOSTORESTATE04
+ SHA1_STORE_STATE(4)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(b, c, d, e, a, m, W, 4, temp);
+
+#ifdef DOSTORESTATE05
+ SHA1_STORE_STATE(5)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(a, b, c, d, e, m, W, 5, temp);
+
+#ifdef DOSTORESTATE06
+ SHA1_STORE_STATE(6)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(e, a, b, c, d, m, W, 6, temp);
+
+#ifdef DOSTORESTATE07
+ SHA1_STORE_STATE(7)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(d, e, a, b, c, m, W, 7, temp);
+
+#ifdef DOSTORESTATE08
+ SHA1_STORE_STATE(8)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(c, d, e, a, b, m, W, 8, temp);
+
+#ifdef DOSTORESTATE09
+ SHA1_STORE_STATE(9)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(b, c, d, e, a, m, W, 9, temp);
+
+#ifdef DOSTORESTATE10
+ SHA1_STORE_STATE(10)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(a, b, c, d, e, m, W, 10, temp);
+
+#ifdef DOSTORESTATE11
+ SHA1_STORE_STATE(11)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(e, a, b, c, d, m, W, 11, temp);
+
+#ifdef DOSTORESTATE12
+ SHA1_STORE_STATE(12)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(d, e, a, b, c, m, W, 12, temp);
+
+#ifdef DOSTORESTATE13
+ SHA1_STORE_STATE(13)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(c, d, e, a, b, m, W, 13, temp);
+
+#ifdef DOSTORESTATE14
+ SHA1_STORE_STATE(14)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(b, c, d, e, a, m, W, 14, temp);
+
+#ifdef DOSTORESTATE15
+ SHA1_STORE_STATE(15)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(a, b, c, d, e, m, W, 15, temp);
+
+#ifdef DOSTORESTATE16
+ SHA1_STORE_STATE(16)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_EXPAND(e, a, b, c, d, W, 16, temp);
+
+#ifdef DOSTORESTATE17
+ SHA1_STORE_STATE(17)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_EXPAND(d, e, a, b, c, W, 17, temp);
+
+#ifdef DOSTORESTATE18
+ SHA1_STORE_STATE(18)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_EXPAND(c, d, e, a, b, W, 18, temp);
+
+#ifdef DOSTORESTATE19
+ SHA1_STORE_STATE(19)
+#endif
+ SHA1COMPRESS_FULL_ROUND1_STEP_EXPAND(b, c, d, e, a, W, 19, temp);
+
+
+
+#ifdef DOSTORESTATE20
+ SHA1_STORE_STATE(20)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(a, b, c, d, e, W, 20, temp);
+
+#ifdef DOSTORESTATE21
+ SHA1_STORE_STATE(21)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(e, a, b, c, d, W, 21, temp);
+
+#ifdef DOSTORESTATE22
+ SHA1_STORE_STATE(22)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(d, e, a, b, c, W, 22, temp);
+
+#ifdef DOSTORESTATE23
+ SHA1_STORE_STATE(23)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(c, d, e, a, b, W, 23, temp);
+
+#ifdef DOSTORESTATE24
+ SHA1_STORE_STATE(24)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(b, c, d, e, a, W, 24, temp);
+
+#ifdef DOSTORESTATE25
+ SHA1_STORE_STATE(25)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(a, b, c, d, e, W, 25, temp);
+
+#ifdef DOSTORESTATE26
+ SHA1_STORE_STATE(26)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(e, a, b, c, d, W, 26, temp);
+
+#ifdef DOSTORESTATE27
+ SHA1_STORE_STATE(27)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(d, e, a, b, c, W, 27, temp);
+
+#ifdef DOSTORESTATE28
+ SHA1_STORE_STATE(28)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(c, d, e, a, b, W, 28, temp);
+
+#ifdef DOSTORESTATE29
+ SHA1_STORE_STATE(29)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(b, c, d, e, a, W, 29, temp);
+
+#ifdef DOSTORESTATE30
+ SHA1_STORE_STATE(30)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(a, b, c, d, e, W, 30, temp);
+
+#ifdef DOSTORESTATE31
+ SHA1_STORE_STATE(31)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(e, a, b, c, d, W, 31, temp);
+
+#ifdef DOSTORESTATE32
+ SHA1_STORE_STATE(32)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(d, e, a, b, c, W, 32, temp);
+
+#ifdef DOSTORESTATE33
+ SHA1_STORE_STATE(33)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(c, d, e, a, b, W, 33, temp);
+
+#ifdef DOSTORESTATE34
+ SHA1_STORE_STATE(34)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(b, c, d, e, a, W, 34, temp);
+
+#ifdef DOSTORESTATE35
+ SHA1_STORE_STATE(35)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(a, b, c, d, e, W, 35, temp);
+
+#ifdef DOSTORESTATE36
+ SHA1_STORE_STATE(36)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(e, a, b, c, d, W, 36, temp);
+
+#ifdef DOSTORESTATE37
+ SHA1_STORE_STATE(37)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(d, e, a, b, c, W, 37, temp);
+
+#ifdef DOSTORESTATE38
+ SHA1_STORE_STATE(38)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(c, d, e, a, b, W, 38, temp);
+
+#ifdef DOSTORESTATE39
+ SHA1_STORE_STATE(39)
+#endif
+ SHA1COMPRESS_FULL_ROUND2_STEP(b, c, d, e, a, W, 39, temp);
+
+
+
+#ifdef DOSTORESTATE40
+ SHA1_STORE_STATE(40)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(a, b, c, d, e, W, 40, temp);
+
+#ifdef DOSTORESTATE41
+ SHA1_STORE_STATE(41)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(e, a, b, c, d, W, 41, temp);
+
+#ifdef DOSTORESTATE42
+ SHA1_STORE_STATE(42)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(d, e, a, b, c, W, 42, temp);
+
+#ifdef DOSTORESTATE43
+ SHA1_STORE_STATE(43)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(c, d, e, a, b, W, 43, temp);
+
+#ifdef DOSTORESTATE44
+ SHA1_STORE_STATE(44)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(b, c, d, e, a, W, 44, temp);
+
+#ifdef DOSTORESTATE45
+ SHA1_STORE_STATE(45)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(a, b, c, d, e, W, 45, temp);
+
+#ifdef DOSTORESTATE46
+ SHA1_STORE_STATE(46)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(e, a, b, c, d, W, 46, temp);
+
+#ifdef DOSTORESTATE47
+ SHA1_STORE_STATE(47)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(d, e, a, b, c, W, 47, temp);
+
+#ifdef DOSTORESTATE48
+ SHA1_STORE_STATE(48)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(c, d, e, a, b, W, 48, temp);
+
+#ifdef DOSTORESTATE49
+ SHA1_STORE_STATE(49)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(b, c, d, e, a, W, 49, temp);
+
+#ifdef DOSTORESTATE50
+ SHA1_STORE_STATE(50)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(a, b, c, d, e, W, 50, temp);
+
+#ifdef DOSTORESTATE51
+ SHA1_STORE_STATE(51)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(e, a, b, c, d, W, 51, temp);
+
+#ifdef DOSTORESTATE52
+ SHA1_STORE_STATE(52)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(d, e, a, b, c, W, 52, temp);
+
+#ifdef DOSTORESTATE53
+ SHA1_STORE_STATE(53)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(c, d, e, a, b, W, 53, temp);
+
+#ifdef DOSTORESTATE54
+ SHA1_STORE_STATE(54)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(b, c, d, e, a, W, 54, temp);
+
+#ifdef DOSTORESTATE55
+ SHA1_STORE_STATE(55)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(a, b, c, d, e, W, 55, temp);
+
+#ifdef DOSTORESTATE56
+ SHA1_STORE_STATE(56)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(e, a, b, c, d, W, 56, temp);
+
+#ifdef DOSTORESTATE57
+ SHA1_STORE_STATE(57)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(d, e, a, b, c, W, 57, temp);
+
+#ifdef DOSTORESTATE58
+ SHA1_STORE_STATE(58)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(c, d, e, a, b, W, 58, temp);
+
+#ifdef DOSTORESTATE59
+ SHA1_STORE_STATE(59)
+#endif
+ SHA1COMPRESS_FULL_ROUND3_STEP(b, c, d, e, a, W, 59, temp);
+
+
+
+
+#ifdef DOSTORESTATE60
+ SHA1_STORE_STATE(60)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(a, b, c, d, e, W, 60, temp);
+
+#ifdef DOSTORESTATE61
+ SHA1_STORE_STATE(61)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(e, a, b, c, d, W, 61, temp);
+
+#ifdef DOSTORESTATE62
+ SHA1_STORE_STATE(62)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(d, e, a, b, c, W, 62, temp);
+
+#ifdef DOSTORESTATE63
+ SHA1_STORE_STATE(63)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(c, d, e, a, b, W, 63, temp);
+
+#ifdef DOSTORESTATE64
+ SHA1_STORE_STATE(64)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(b, c, d, e, a, W, 64, temp);
+
+#ifdef DOSTORESTATE65
+ SHA1_STORE_STATE(65)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(a, b, c, d, e, W, 65, temp);
+
+#ifdef DOSTORESTATE66
+ SHA1_STORE_STATE(66)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(e, a, b, c, d, W, 66, temp);
+
+#ifdef DOSTORESTATE67
+ SHA1_STORE_STATE(67)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(d, e, a, b, c, W, 67, temp);
+
+#ifdef DOSTORESTATE68
+ SHA1_STORE_STATE(68)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(c, d, e, a, b, W, 68, temp);
+
+#ifdef DOSTORESTATE69
+ SHA1_STORE_STATE(69)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(b, c, d, e, a, W, 69, temp);
+
+#ifdef DOSTORESTATE70
+ SHA1_STORE_STATE(70)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(a, b, c, d, e, W, 70, temp);
+
+#ifdef DOSTORESTATE71
+ SHA1_STORE_STATE(71)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(e, a, b, c, d, W, 71, temp);
+
+#ifdef DOSTORESTATE72
+ SHA1_STORE_STATE(72)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(d, e, a, b, c, W, 72, temp);
+
+#ifdef DOSTORESTATE73
+ SHA1_STORE_STATE(73)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(c, d, e, a, b, W, 73, temp);
+
+#ifdef DOSTORESTATE74
+ SHA1_STORE_STATE(74)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(b, c, d, e, a, W, 74, temp);
+
+#ifdef DOSTORESTATE75
+ SHA1_STORE_STATE(75)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(a, b, c, d, e, W, 75, temp);
+
+#ifdef DOSTORESTATE76
+ SHA1_STORE_STATE(76)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(e, a, b, c, d, W, 76, temp);
+
+#ifdef DOSTORESTATE77
+ SHA1_STORE_STATE(77)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(d, e, a, b, c, W, 77, temp);
+
+#ifdef DOSTORESTATE78
+ SHA1_STORE_STATE(78)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(c, d, e, a, b, W, 78, temp);
+
+#ifdef DOSTORESTATE79
+ SHA1_STORE_STATE(79)
+#endif
+ SHA1COMPRESS_FULL_ROUND4_STEP(b, c, d, e, a, W, 79, temp);
+
+
+
+ ihv[0] += a; ihv[1] += b; ihv[2] += c; ihv[3] += d; ihv[4] += e;
+}
+
+
+
+
+#define SHA1_RECOMPRESS(t) \
+static void sha1recompress_fast_ ## t (uint32_t ihvin[5], uint32_t ihvout[5], const uint32_t me2[80], const uint32_t state[5]) \
+{ \
+ uint32_t a = state[0], b = state[1], c = state[2], d = state[3], e = state[4]; \
+ if (t > 79) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(b, c, d, e, a, me2, 79); \
+ if (t > 78) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(c, d, e, a, b, me2, 78); \
+ if (t > 77) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(d, e, a, b, c, me2, 77); \
+ if (t > 76) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(e, a, b, c, d, me2, 76); \
+ if (t > 75) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, me2, 75); \
+ if (t > 74) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(b, c, d, e, a, me2, 74); \
+ if (t > 73) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(c, d, e, a, b, me2, 73); \
+ if (t > 72) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(d, e, a, b, c, me2, 72); \
+ if (t > 71) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(e, a, b, c, d, me2, 71); \
+ if (t > 70) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, me2, 70); \
+ if (t > 69) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(b, c, d, e, a, me2, 69); \
+ if (t > 68) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(c, d, e, a, b, me2, 68); \
+ if (t > 67) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(d, e, a, b, c, me2, 67); \
+ if (t > 66) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(e, a, b, c, d, me2, 66); \
+ if (t > 65) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, me2, 65); \
+ if (t > 64) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(b, c, d, e, a, me2, 64); \
+ if (t > 63) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(c, d, e, a, b, me2, 63); \
+ if (t > 62) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(d, e, a, b, c, me2, 62); \
+ if (t > 61) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(e, a, b, c, d, me2, 61); \
+ if (t > 60) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, me2, 60); \
+ if (t > 59) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(b, c, d, e, a, me2, 59); \
+ if (t > 58) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(c, d, e, a, b, me2, 58); \
+ if (t > 57) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(d, e, a, b, c, me2, 57); \
+ if (t > 56) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(e, a, b, c, d, me2, 56); \
+ if (t > 55) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, me2, 55); \
+ if (t > 54) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(b, c, d, e, a, me2, 54); \
+ if (t > 53) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(c, d, e, a, b, me2, 53); \
+ if (t > 52) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(d, e, a, b, c, me2, 52); \
+ if (t > 51) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(e, a, b, c, d, me2, 51); \
+ if (t > 50) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, me2, 50); \
+ if (t > 49) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(b, c, d, e, a, me2, 49); \
+ if (t > 48) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(c, d, e, a, b, me2, 48); \
+ if (t > 47) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(d, e, a, b, c, me2, 47); \
+ if (t > 46) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(e, a, b, c, d, me2, 46); \
+ if (t > 45) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, me2, 45); \
+ if (t > 44) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(b, c, d, e, a, me2, 44); \
+ if (t > 43) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(c, d, e, a, b, me2, 43); \
+ if (t > 42) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(d, e, a, b, c, me2, 42); \
+ if (t > 41) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(e, a, b, c, d, me2, 41); \
+ if (t > 40) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, me2, 40); \
+ if (t > 39) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(b, c, d, e, a, me2, 39); \
+ if (t > 38) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(c, d, e, a, b, me2, 38); \
+ if (t > 37) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(d, e, a, b, c, me2, 37); \
+ if (t > 36) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(e, a, b, c, d, me2, 36); \
+ if (t > 35) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, me2, 35); \
+ if (t > 34) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(b, c, d, e, a, me2, 34); \
+ if (t > 33) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(c, d, e, a, b, me2, 33); \
+ if (t > 32) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(d, e, a, b, c, me2, 32); \
+ if (t > 31) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(e, a, b, c, d, me2, 31); \
+ if (t > 30) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, me2, 30); \
+ if (t > 29) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(b, c, d, e, a, me2, 29); \
+ if (t > 28) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(c, d, e, a, b, me2, 28); \
+ if (t > 27) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(d, e, a, b, c, me2, 27); \
+ if (t > 26) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(e, a, b, c, d, me2, 26); \
+ if (t > 25) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, me2, 25); \
+ if (t > 24) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(b, c, d, e, a, me2, 24); \
+ if (t > 23) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(c, d, e, a, b, me2, 23); \
+ if (t > 22) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(d, e, a, b, c, me2, 22); \
+ if (t > 21) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(e, a, b, c, d, me2, 21); \
+ if (t > 20) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, me2, 20); \
+ if (t > 19) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(b, c, d, e, a, me2, 19); \
+ if (t > 18) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(c, d, e, a, b, me2, 18); \
+ if (t > 17) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(d, e, a, b, c, me2, 17); \
+ if (t > 16) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(e, a, b, c, d, me2, 16); \
+ if (t > 15) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, me2, 15); \
+ if (t > 14) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(b, c, d, e, a, me2, 14); \
+ if (t > 13) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(c, d, e, a, b, me2, 13); \
+ if (t > 12) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(d, e, a, b, c, me2, 12); \
+ if (t > 11) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(e, a, b, c, d, me2, 11); \
+ if (t > 10) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, me2, 10); \
+ if (t > 9) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(b, c, d, e, a, me2, 9); \
+ if (t > 8) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(c, d, e, a, b, me2, 8); \
+ if (t > 7) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(d, e, a, b, c, me2, 7); \
+ if (t > 6) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(e, a, b, c, d, me2, 6); \
+ if (t > 5) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, me2, 5); \
+ if (t > 4) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(b, c, d, e, a, me2, 4); \
+ if (t > 3) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(c, d, e, a, b, me2, 3); \
+ if (t > 2) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(d, e, a, b, c, me2, 2); \
+ if (t > 1) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(e, a, b, c, d, me2, 1); \
+ if (t > 0) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, me2, 0); \
+ ihvin[0] = a; ihvin[1] = b; ihvin[2] = c; ihvin[3] = d; ihvin[4] = e; \
+ a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; \
+ if (t <= 0) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, me2, 0); \
+ if (t <= 1) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, me2, 1); \
+ if (t <= 2) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, me2, 2); \
+ if (t <= 3) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, me2, 3); \
+ if (t <= 4) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, me2, 4); \
+ if (t <= 5) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, me2, 5); \
+ if (t <= 6) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, me2, 6); \
+ if (t <= 7) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, me2, 7); \
+ if (t <= 8) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, me2, 8); \
+ if (t <= 9) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, me2, 9); \
+ if (t <= 10) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, me2, 10); \
+ if (t <= 11) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, me2, 11); \
+ if (t <= 12) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, me2, 12); \
+ if (t <= 13) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, me2, 13); \
+ if (t <= 14) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, me2, 14); \
+ if (t <= 15) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, me2, 15); \
+ if (t <= 16) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, me2, 16); \
+ if (t <= 17) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, me2, 17); \
+ if (t <= 18) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, me2, 18); \
+ if (t <= 19) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, me2, 19); \
+ if (t <= 20) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, me2, 20); \
+ if (t <= 21) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, me2, 21); \
+ if (t <= 22) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, me2, 22); \
+ if (t <= 23) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, me2, 23); \
+ if (t <= 24) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, me2, 24); \
+ if (t <= 25) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, me2, 25); \
+ if (t <= 26) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, me2, 26); \
+ if (t <= 27) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, me2, 27); \
+ if (t <= 28) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, me2, 28); \
+ if (t <= 29) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, me2, 29); \
+ if (t <= 30) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, me2, 30); \
+ if (t <= 31) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, me2, 31); \
+ if (t <= 32) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, me2, 32); \
+ if (t <= 33) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, me2, 33); \
+ if (t <= 34) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, me2, 34); \
+ if (t <= 35) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, me2, 35); \
+ if (t <= 36) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, me2, 36); \
+ if (t <= 37) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, me2, 37); \
+ if (t <= 38) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, me2, 38); \
+ if (t <= 39) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, me2, 39); \
+ if (t <= 40) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, me2, 40); \
+ if (t <= 41) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, me2, 41); \
+ if (t <= 42) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, me2, 42); \
+ if (t <= 43) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, me2, 43); \
+ if (t <= 44) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, me2, 44); \
+ if (t <= 45) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, me2, 45); \
+ if (t <= 46) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, me2, 46); \
+ if (t <= 47) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, me2, 47); \
+ if (t <= 48) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, me2, 48); \
+ if (t <= 49) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, me2, 49); \
+ if (t <= 50) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, me2, 50); \
+ if (t <= 51) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, me2, 51); \
+ if (t <= 52) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, me2, 52); \
+ if (t <= 53) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, me2, 53); \
+ if (t <= 54) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, me2, 54); \
+ if (t <= 55) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, me2, 55); \
+ if (t <= 56) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, me2, 56); \
+ if (t <= 57) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, me2, 57); \
+ if (t <= 58) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, me2, 58); \
+ if (t <= 59) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, me2, 59); \
+ if (t <= 60) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, me2, 60); \
+ if (t <= 61) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, me2, 61); \
+ if (t <= 62) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, me2, 62); \
+ if (t <= 63) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, me2, 63); \
+ if (t <= 64) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, me2, 64); \
+ if (t <= 65) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, me2, 65); \
+ if (t <= 66) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, me2, 66); \
+ if (t <= 67) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, me2, 67); \
+ if (t <= 68) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, me2, 68); \
+ if (t <= 69) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, me2, 69); \
+ if (t <= 70) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, me2, 70); \
+ if (t <= 71) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, me2, 71); \
+ if (t <= 72) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, me2, 72); \
+ if (t <= 73) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, me2, 73); \
+ if (t <= 74) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, me2, 74); \
+ if (t <= 75) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, me2, 75); \
+ if (t <= 76) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, me2, 76); \
+ if (t <= 77) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, me2, 77); \
+ if (t <= 78) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, me2, 78); \
+ if (t <= 79) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, me2, 79); \
+ ihvout[0] = ihvin[0] + a; ihvout[1] = ihvin[1] + b; ihvout[2] = ihvin[2] + c; ihvout[3] = ihvin[3] + d; ihvout[4] = ihvin[4] + e; \
+}
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 4127) /* Compiler complains about the checks in the above macro being constant. */
+#endif
+
+#ifdef DOSTORESTATE0
+SHA1_RECOMPRESS(0)
+#endif
+
+#ifdef DOSTORESTATE1
+SHA1_RECOMPRESS(1)
+#endif
+
+#ifdef DOSTORESTATE2
+SHA1_RECOMPRESS(2)
+#endif
+
+#ifdef DOSTORESTATE3
+SHA1_RECOMPRESS(3)
+#endif
+
+#ifdef DOSTORESTATE4
+SHA1_RECOMPRESS(4)
+#endif
+
+#ifdef DOSTORESTATE5
+SHA1_RECOMPRESS(5)
+#endif
+
+#ifdef DOSTORESTATE6
+SHA1_RECOMPRESS(6)
+#endif
+
+#ifdef DOSTORESTATE7
+SHA1_RECOMPRESS(7)
+#endif
+
+#ifdef DOSTORESTATE8
+SHA1_RECOMPRESS(8)
+#endif
+
+#ifdef DOSTORESTATE9
+SHA1_RECOMPRESS(9)
+#endif
+
+#ifdef DOSTORESTATE10
+SHA1_RECOMPRESS(10)
+#endif
+
+#ifdef DOSTORESTATE11
+SHA1_RECOMPRESS(11)
+#endif
+
+#ifdef DOSTORESTATE12
+SHA1_RECOMPRESS(12)
+#endif
+
+#ifdef DOSTORESTATE13
+SHA1_RECOMPRESS(13)
+#endif
+
+#ifdef DOSTORESTATE14
+SHA1_RECOMPRESS(14)
+#endif
+
+#ifdef DOSTORESTATE15
+SHA1_RECOMPRESS(15)
+#endif
+
+#ifdef DOSTORESTATE16
+SHA1_RECOMPRESS(16)
+#endif
+
+#ifdef DOSTORESTATE17
+SHA1_RECOMPRESS(17)
+#endif
+
+#ifdef DOSTORESTATE18
+SHA1_RECOMPRESS(18)
+#endif
+
+#ifdef DOSTORESTATE19
+SHA1_RECOMPRESS(19)
+#endif
+
+#ifdef DOSTORESTATE20
+SHA1_RECOMPRESS(20)
+#endif
+
+#ifdef DOSTORESTATE21
+SHA1_RECOMPRESS(21)
+#endif
+
+#ifdef DOSTORESTATE22
+SHA1_RECOMPRESS(22)
+#endif
+
+#ifdef DOSTORESTATE23
+SHA1_RECOMPRESS(23)
+#endif
+
+#ifdef DOSTORESTATE24
+SHA1_RECOMPRESS(24)
+#endif
+
+#ifdef DOSTORESTATE25
+SHA1_RECOMPRESS(25)
+#endif
+
+#ifdef DOSTORESTATE26
+SHA1_RECOMPRESS(26)
+#endif
+
+#ifdef DOSTORESTATE27
+SHA1_RECOMPRESS(27)
+#endif
+
+#ifdef DOSTORESTATE28
+SHA1_RECOMPRESS(28)
+#endif
+
+#ifdef DOSTORESTATE29
+SHA1_RECOMPRESS(29)
+#endif
+
+#ifdef DOSTORESTATE30
+SHA1_RECOMPRESS(30)
+#endif
+
+#ifdef DOSTORESTATE31
+SHA1_RECOMPRESS(31)
+#endif
+
+#ifdef DOSTORESTATE32
+SHA1_RECOMPRESS(32)
+#endif
+
+#ifdef DOSTORESTATE33
+SHA1_RECOMPRESS(33)
+#endif
+
+#ifdef DOSTORESTATE34
+SHA1_RECOMPRESS(34)
+#endif
+
+#ifdef DOSTORESTATE35
+SHA1_RECOMPRESS(35)
+#endif
+
+#ifdef DOSTORESTATE36
+SHA1_RECOMPRESS(36)
+#endif
+
+#ifdef DOSTORESTATE37
+SHA1_RECOMPRESS(37)
+#endif
+
+#ifdef DOSTORESTATE38
+SHA1_RECOMPRESS(38)
+#endif
+
+#ifdef DOSTORESTATE39
+SHA1_RECOMPRESS(39)
+#endif
+
+#ifdef DOSTORESTATE40
+SHA1_RECOMPRESS(40)
+#endif
+
+#ifdef DOSTORESTATE41
+SHA1_RECOMPRESS(41)
+#endif
+
+#ifdef DOSTORESTATE42
+SHA1_RECOMPRESS(42)
+#endif
+
+#ifdef DOSTORESTATE43
+SHA1_RECOMPRESS(43)
+#endif
+
+#ifdef DOSTORESTATE44
+SHA1_RECOMPRESS(44)
+#endif
+
+#ifdef DOSTORESTATE45
+SHA1_RECOMPRESS(45)
+#endif
+
+#ifdef DOSTORESTATE46
+SHA1_RECOMPRESS(46)
+#endif
+
+#ifdef DOSTORESTATE47
+SHA1_RECOMPRESS(47)
+#endif
+
+#ifdef DOSTORESTATE48
+SHA1_RECOMPRESS(48)
+#endif
+
+#ifdef DOSTORESTATE49
+SHA1_RECOMPRESS(49)
+#endif
+
+#ifdef DOSTORESTATE50
+SHA1_RECOMPRESS(50)
+#endif
+
+#ifdef DOSTORESTATE51
+SHA1_RECOMPRESS(51)
+#endif
+
+#ifdef DOSTORESTATE52
+SHA1_RECOMPRESS(52)
+#endif
+
+#ifdef DOSTORESTATE53
+SHA1_RECOMPRESS(53)
+#endif
+
+#ifdef DOSTORESTATE54
+SHA1_RECOMPRESS(54)
+#endif
+
+#ifdef DOSTORESTATE55
+SHA1_RECOMPRESS(55)
+#endif
+
+#ifdef DOSTORESTATE56
+SHA1_RECOMPRESS(56)
+#endif
+
+#ifdef DOSTORESTATE57
+SHA1_RECOMPRESS(57)
+#endif
+
+#ifdef DOSTORESTATE58
+SHA1_RECOMPRESS(58)
+#endif
+
+#ifdef DOSTORESTATE59
+SHA1_RECOMPRESS(59)
+#endif
+
+#ifdef DOSTORESTATE60
+SHA1_RECOMPRESS(60)
+#endif
+
+#ifdef DOSTORESTATE61
+SHA1_RECOMPRESS(61)
+#endif
+
+#ifdef DOSTORESTATE62
+SHA1_RECOMPRESS(62)
+#endif
+
+#ifdef DOSTORESTATE63
+SHA1_RECOMPRESS(63)
+#endif
+
+#ifdef DOSTORESTATE64
+SHA1_RECOMPRESS(64)
+#endif
+
+#ifdef DOSTORESTATE65
+SHA1_RECOMPRESS(65)
+#endif
+
+#ifdef DOSTORESTATE66
+SHA1_RECOMPRESS(66)
+#endif
+
+#ifdef DOSTORESTATE67
+SHA1_RECOMPRESS(67)
+#endif
+
+#ifdef DOSTORESTATE68
+SHA1_RECOMPRESS(68)
+#endif
+
+#ifdef DOSTORESTATE69
+SHA1_RECOMPRESS(69)
+#endif
+
+#ifdef DOSTORESTATE70
+SHA1_RECOMPRESS(70)
+#endif
+
+#ifdef DOSTORESTATE71
+SHA1_RECOMPRESS(71)
+#endif
+
+#ifdef DOSTORESTATE72
+SHA1_RECOMPRESS(72)
+#endif
+
+#ifdef DOSTORESTATE73
+SHA1_RECOMPRESS(73)
+#endif
+
+#ifdef DOSTORESTATE74
+SHA1_RECOMPRESS(74)
+#endif
+
+#ifdef DOSTORESTATE75
+SHA1_RECOMPRESS(75)
+#endif
+
+#ifdef DOSTORESTATE76
+SHA1_RECOMPRESS(76)
+#endif
+
+#ifdef DOSTORESTATE77
+SHA1_RECOMPRESS(77)
+#endif
+
+#ifdef DOSTORESTATE78
+SHA1_RECOMPRESS(78)
+#endif
+
+#ifdef DOSTORESTATE79
+SHA1_RECOMPRESS(79)
+#endif
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+static void sha1_recompression_step(uint32_t step, uint32_t ihvin[5], uint32_t ihvout[5], const uint32_t me2[80], const uint32_t state[5])
+{
+ switch (step)
+ {
+#ifdef DOSTORESTATE0
+ case 0:
+ sha1recompress_fast_0(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE1
+ case 1:
+ sha1recompress_fast_1(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE2
+ case 2:
+ sha1recompress_fast_2(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE3
+ case 3:
+ sha1recompress_fast_3(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE4
+ case 4:
+ sha1recompress_fast_4(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE5
+ case 5:
+ sha1recompress_fast_5(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE6
+ case 6:
+ sha1recompress_fast_6(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE7
+ case 7:
+ sha1recompress_fast_7(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE8
+ case 8:
+ sha1recompress_fast_8(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE9
+ case 9:
+ sha1recompress_fast_9(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE10
+ case 10:
+ sha1recompress_fast_10(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE11
+ case 11:
+ sha1recompress_fast_11(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE12
+ case 12:
+ sha1recompress_fast_12(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE13
+ case 13:
+ sha1recompress_fast_13(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE14
+ case 14:
+ sha1recompress_fast_14(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE15
+ case 15:
+ sha1recompress_fast_15(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE16
+ case 16:
+ sha1recompress_fast_16(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE17
+ case 17:
+ sha1recompress_fast_17(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE18
+ case 18:
+ sha1recompress_fast_18(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE19
+ case 19:
+ sha1recompress_fast_19(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE20
+ case 20:
+ sha1recompress_fast_20(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE21
+ case 21:
+ sha1recompress_fast_21(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE22
+ case 22:
+ sha1recompress_fast_22(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE23
+ case 23:
+ sha1recompress_fast_23(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE24
+ case 24:
+ sha1recompress_fast_24(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE25
+ case 25:
+ sha1recompress_fast_25(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE26
+ case 26:
+ sha1recompress_fast_26(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE27
+ case 27:
+ sha1recompress_fast_27(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE28
+ case 28:
+ sha1recompress_fast_28(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE29
+ case 29:
+ sha1recompress_fast_29(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE30
+ case 30:
+ sha1recompress_fast_30(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE31
+ case 31:
+ sha1recompress_fast_31(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE32
+ case 32:
+ sha1recompress_fast_32(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE33
+ case 33:
+ sha1recompress_fast_33(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE34
+ case 34:
+ sha1recompress_fast_34(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE35
+ case 35:
+ sha1recompress_fast_35(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE36
+ case 36:
+ sha1recompress_fast_36(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE37
+ case 37:
+ sha1recompress_fast_37(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE38
+ case 38:
+ sha1recompress_fast_38(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE39
+ case 39:
+ sha1recompress_fast_39(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE40
+ case 40:
+ sha1recompress_fast_40(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE41
+ case 41:
+ sha1recompress_fast_41(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE42
+ case 42:
+ sha1recompress_fast_42(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE43
+ case 43:
+ sha1recompress_fast_43(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE44
+ case 44:
+ sha1recompress_fast_44(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE45
+ case 45:
+ sha1recompress_fast_45(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE46
+ case 46:
+ sha1recompress_fast_46(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE47
+ case 47:
+ sha1recompress_fast_47(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE48
+ case 48:
+ sha1recompress_fast_48(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE49
+ case 49:
+ sha1recompress_fast_49(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE50
+ case 50:
+ sha1recompress_fast_50(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE51
+ case 51:
+ sha1recompress_fast_51(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE52
+ case 52:
+ sha1recompress_fast_52(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE53
+ case 53:
+ sha1recompress_fast_53(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE54
+ case 54:
+ sha1recompress_fast_54(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE55
+ case 55:
+ sha1recompress_fast_55(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE56
+ case 56:
+ sha1recompress_fast_56(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE57
+ case 57:
+ sha1recompress_fast_57(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE58
+ case 58:
+ sha1recompress_fast_58(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE59
+ case 59:
+ sha1recompress_fast_59(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE60
+ case 60:
+ sha1recompress_fast_60(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE61
+ case 61:
+ sha1recompress_fast_61(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE62
+ case 62:
+ sha1recompress_fast_62(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE63
+ case 63:
+ sha1recompress_fast_63(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE64
+ case 64:
+ sha1recompress_fast_64(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE65
+ case 65:
+ sha1recompress_fast_65(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE66
+ case 66:
+ sha1recompress_fast_66(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE67
+ case 67:
+ sha1recompress_fast_67(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE68
+ case 68:
+ sha1recompress_fast_68(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE69
+ case 69:
+ sha1recompress_fast_69(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE70
+ case 70:
+ sha1recompress_fast_70(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE71
+ case 71:
+ sha1recompress_fast_71(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE72
+ case 72:
+ sha1recompress_fast_72(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE73
+ case 73:
+ sha1recompress_fast_73(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE74
+ case 74:
+ sha1recompress_fast_74(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE75
+ case 75:
+ sha1recompress_fast_75(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE76
+ case 76:
+ sha1recompress_fast_76(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE77
+ case 77:
+ sha1recompress_fast_77(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE78
+ case 78:
+ sha1recompress_fast_78(ihvin, ihvout, me2, state);
+ break;
+#endif
+#ifdef DOSTORESTATE79
+ case 79:
+ sha1recompress_fast_79(ihvin, ihvout, me2, state);
+ break;
+#endif
+ default:
+ abort();
+ }
+
+}
+
+
+
+static void sha1_process(SHA1_CTX *ctx, const uint32_t block[16])
+{
+ unsigned i, j;
+ uint32_t ubc_dv_mask[DVMASKSIZE] = { 0xFFFFFFFF };
+ uint32_t ihvtmp[5];
+
+ ctx->ihv1[0] = ctx->ihv[0];
+ ctx->ihv1[1] = ctx->ihv[1];
+ ctx->ihv1[2] = ctx->ihv[2];
+ ctx->ihv1[3] = ctx->ihv[3];
+ ctx->ihv1[4] = ctx->ihv[4];
+
+ sha1_compression_states(ctx->ihv, block, ctx->m1, ctx->states);
+
+ if (ctx->detect_coll)
+ {
+ if (ctx->ubc_check)
+ {
+ ubc_check(ctx->m1, ubc_dv_mask);
+ }
+
+ if (ubc_dv_mask[0] != 0)
+ {
+ for (i = 0; sha1_dvs[i].dvType != 0; ++i)
+ {
+ if (ubc_dv_mask[0] & ((uint32_t)(1) << sha1_dvs[i].maskb))
+ {
+ for (j = 0; j < 80; ++j)
+ ctx->m2[j] = ctx->m1[j] ^ sha1_dvs[i].dm[j];
+
+ sha1_recompression_step(sha1_dvs[i].testt, ctx->ihv2, ihvtmp, ctx->m2, ctx->states[sha1_dvs[i].testt]);
+
+ /* to verify SHA-1 collision detection code with collisions for reduced-step SHA-1 */
+ if ((0 == ((ihvtmp[0] ^ ctx->ihv[0]) | (ihvtmp[1] ^ ctx->ihv[1]) | (ihvtmp[2] ^ ctx->ihv[2]) | (ihvtmp[3] ^ ctx->ihv[3]) | (ihvtmp[4] ^ ctx->ihv[4])))
+ || (ctx->reduced_round_coll && 0==((ctx->ihv1[0] ^ ctx->ihv2[0]) | (ctx->ihv1[1] ^ ctx->ihv2[1]) | (ctx->ihv1[2] ^ ctx->ihv2[2]) | (ctx->ihv1[3] ^ ctx->ihv2[3]) | (ctx->ihv1[4] ^ ctx->ihv2[4]))))
+ {
+ ctx->found_collision = 1;
+
+ if (ctx->safe_hash)
+ {
+ sha1_compression_W(ctx->ihv, ctx->m1);
+ sha1_compression_W(ctx->ihv, ctx->m1);
+ }
+
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+void SHA1DCInit(SHA1_CTX *ctx)
+{
+ ctx->total = 0;
+ ctx->ihv[0] = 0x67452301;
+ ctx->ihv[1] = 0xEFCDAB89;
+ ctx->ihv[2] = 0x98BADCFE;
+ ctx->ihv[3] = 0x10325476;
+ ctx->ihv[4] = 0xC3D2E1F0;
+ ctx->found_collision = 0;
+ ctx->safe_hash = SHA1DC_INIT_SAFE_HASH_DEFAULT;
+ ctx->ubc_check = 1;
+ ctx->detect_coll = 1;
+ ctx->reduced_round_coll = 0;
+ ctx->callback = NULL;
+}
+
+void SHA1DCSetSafeHash(SHA1_CTX *ctx, int safehash)
+{
+ if (safehash)
+ ctx->safe_hash = 1;
+ else
+ ctx->safe_hash = 0;
+}
+
+
+void SHA1DCSetUseUBC(SHA1_CTX *ctx, int ubc_check)
+{
+ if (ubc_check)
+ ctx->ubc_check = 1;
+ else
+ ctx->ubc_check = 0;
+}
+
+void SHA1DCSetUseDetectColl(SHA1_CTX *ctx, int detect_coll)
+{
+ if (detect_coll)
+ ctx->detect_coll = 1;
+ else
+ ctx->detect_coll = 0;
+}
+
+void SHA1DCSetDetectReducedRoundCollision(SHA1_CTX *ctx, int reduced_round_coll)
+{
+ if (reduced_round_coll)
+ ctx->reduced_round_coll = 1;
+ else
+ ctx->reduced_round_coll = 0;
+}
+
+void SHA1DCSetCallback(SHA1_CTX *ctx, collision_block_callback callback)
+{
+ ctx->callback = callback;
+}
+
+void SHA1DCUpdate(SHA1_CTX *ctx, const char *buf, size_t len)
+{
+ unsigned left, fill;
+
+ if (len == 0)
+ return;
+
+ left = ctx->total & 63;
+ fill = 64 - left;
+
+ if (left && len >= fill)
+ {
+ ctx->total += fill;
+ memcpy(ctx->buffer + left, buf, fill);
+ sha1_process(ctx, (uint32_t*)(ctx->buffer));
+ buf += fill;
+ len -= fill;
+ left = 0;
+ }
+ while (len >= 64)
+ {
+ ctx->total += 64;
+
+#if defined(SHA1DC_ALLOW_UNALIGNED_ACCESS)
+ sha1_process(ctx, (uint32_t*)(buf));
+#else
+ memcpy(ctx->buffer, buf, 64);
+ sha1_process(ctx, (uint32_t*)(ctx->buffer));
+#endif /* defined(SHA1DC_ALLOW_UNALIGNED_ACCESS) */
+ buf += 64;
+ len -= 64;
+ }
+ if (len > 0)
+ {
+ ctx->total += len;
+ memcpy(ctx->buffer + left, buf, len);
+ }
+}
+
+static const unsigned char sha1_padding[64] =
+{
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+int SHA1DCFinal(unsigned char output[20], SHA1_CTX *ctx)
+{
+ uint32_t last = ctx->total & 63;
+ uint32_t padn = (last < 56) ? (56 - last) : (120 - last);
+ uint64_t total;
+ SHA1DCUpdate(ctx, (const char*)(sha1_padding), padn);
+
+ total = ctx->total - padn;
+ total <<= 3;
+ ctx->buffer[56] = (unsigned char)(total >> 56);
+ ctx->buffer[57] = (unsigned char)(total >> 48);
+ ctx->buffer[58] = (unsigned char)(total >> 40);
+ ctx->buffer[59] = (unsigned char)(total >> 32);
+ ctx->buffer[60] = (unsigned char)(total >> 24);
+ ctx->buffer[61] = (unsigned char)(total >> 16);
+ ctx->buffer[62] = (unsigned char)(total >> 8);
+ ctx->buffer[63] = (unsigned char)(total);
+ sha1_process(ctx, (uint32_t*)(ctx->buffer));
+ output[0] = (unsigned char)(ctx->ihv[0] >> 24);
+ output[1] = (unsigned char)(ctx->ihv[0] >> 16);
+ output[2] = (unsigned char)(ctx->ihv[0] >> 8);
+ output[3] = (unsigned char)(ctx->ihv[0]);
+ output[4] = (unsigned char)(ctx->ihv[1] >> 24);
+ output[5] = (unsigned char)(ctx->ihv[1] >> 16);
+ output[6] = (unsigned char)(ctx->ihv[1] >> 8);
+ output[7] = (unsigned char)(ctx->ihv[1]);
+ output[8] = (unsigned char)(ctx->ihv[2] >> 24);
+ output[9] = (unsigned char)(ctx->ihv[2] >> 16);
+ output[10] = (unsigned char)(ctx->ihv[2] >> 8);
+ output[11] = (unsigned char)(ctx->ihv[2]);
+ output[12] = (unsigned char)(ctx->ihv[3] >> 24);
+ output[13] = (unsigned char)(ctx->ihv[3] >> 16);
+ output[14] = (unsigned char)(ctx->ihv[3] >> 8);
+ output[15] = (unsigned char)(ctx->ihv[3]);
+ output[16] = (unsigned char)(ctx->ihv[4] >> 24);
+ output[17] = (unsigned char)(ctx->ihv[4] >> 16);
+ output[18] = (unsigned char)(ctx->ihv[4] >> 8);
+ output[19] = (unsigned char)(ctx->ihv[4]);
+ return ctx->found_collision;
+}
+
+#ifdef SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_C
+#include SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_C
+#endif
diff --git a/src/util/hash/sha1dc/sha1.h b/src/util/hash/sha1dc/sha1.h
new file mode 100644
index 0000000..1e4e94b
--- /dev/null
+++ b/src/util/hash/sha1dc/sha1.h
@@ -0,0 +1,110 @@
+/***
+* Copyright 2017 Marc Stevens <marc@marc-stevens.nl>, Dan Shumow <danshu@microsoft.com>
+* Distributed under the MIT Software License.
+* See accompanying file LICENSE.txt or copy at
+* https://opensource.org/licenses/MIT
+***/
+
+#ifndef SHA1DC_SHA1_H
+#define SHA1DC_SHA1_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#ifndef SHA1DC_NO_STANDARD_INCLUDES
+#include <stdint.h>
+#endif
+
+/* sha-1 compression function that takes an already expanded message, and additionally store intermediate states */
+/* only stores states ii (the state between step ii-1 and step ii) when DOSTORESTATEii is defined in ubc_check.h */
+void sha1_compression_states(uint32_t[5], const uint32_t[16], uint32_t[80], uint32_t[80][5]);
+
+/*
+// Function type for sha1_recompression_step_T (uint32_t ihvin[5], uint32_t ihvout[5], const uint32_t me2[80], const uint32_t state[5]).
+// Where 0 <= T < 80
+// me2 is an expanded message (the expansion of an original message block XOR'ed with a disturbance vector's message block difference.)
+// state is the internal state (a,b,c,d,e) before step T of the SHA-1 compression function while processing the original message block.
+// The function will return:
+// ihvin: The reconstructed input chaining value.
+// ihvout: The reconstructed output chaining value.
+*/
+typedef void(*sha1_recompression_type)(uint32_t*, uint32_t*, const uint32_t*, const uint32_t*);
+
+/* A callback function type that can be set to be called when a collision block has been found: */
+/* void collision_block_callback(uint64_t byteoffset, const uint32_t ihvin1[5], const uint32_t ihvin2[5], const uint32_t m1[80], const uint32_t m2[80]) */
+typedef void(*collision_block_callback)(uint64_t, const uint32_t*, const uint32_t*, const uint32_t*, const uint32_t*);
+
+/* The SHA-1 context. */
+typedef struct {
+ uint64_t total;
+ uint32_t ihv[5];
+ unsigned char buffer[64];
+ int found_collision;
+ int safe_hash;
+ int detect_coll;
+ int ubc_check;
+ int reduced_round_coll;
+ collision_block_callback callback;
+
+ uint32_t ihv1[5];
+ uint32_t ihv2[5];
+ uint32_t m1[80];
+ uint32_t m2[80];
+ uint32_t states[80][5];
+} SHA1_CTX;
+
+/* Initialize SHA-1 context. */
+void SHA1DCInit(SHA1_CTX*);
+
+/*
+ Function to enable safe SHA-1 hashing:
+ Collision attacks are thwarted by hashing a detected near-collision block 3 times.
+ Think of it as extending SHA-1 from 80-steps to 240-steps for such blocks:
+ The best collision attacks against SHA-1 have complexity about 2^60,
+ thus for 240-steps an immediate lower-bound for the best cryptanalytic attacks would be 2^180.
+ An attacker would be better off using a generic birthday search of complexity 2^80.
+
+ Enabling safe SHA-1 hashing will result in the correct SHA-1 hash for messages where no collision attack was detected,
+ but it will result in a different SHA-1 hash for messages where a collision attack was detected.
+ This will automatically invalidate SHA-1 based digital signature forgeries.
+ Enabled by default.
+*/
+void SHA1DCSetSafeHash(SHA1_CTX*, int);
+
+/*
+ Function to disable or enable the use of Unavoidable Bitconditions (provides a significant speed up).
+ Enabled by default
+ */
+void SHA1DCSetUseUBC(SHA1_CTX*, int);
+
+/*
+ Function to disable or enable the use of Collision Detection.
+ Enabled by default.
+ */
+void SHA1DCSetUseDetectColl(SHA1_CTX*, int);
+
+/* function to disable or enable the detection of reduced-round SHA-1 collisions */
+/* disabled by default */
+void SHA1DCSetDetectReducedRoundCollision(SHA1_CTX*, int);
+
+/* function to set a callback function, pass NULL to disable */
+/* by default no callback set */
+void SHA1DCSetCallback(SHA1_CTX*, collision_block_callback);
+
+/* update SHA-1 context with buffer contents */
+void SHA1DCUpdate(SHA1_CTX*, const char*, size_t);
+
+/* obtain SHA-1 hash from SHA-1 context */
+/* returns: 0 = no collision detected, otherwise = collision found => warn user for active attack */
+int SHA1DCFinal(unsigned char[20], SHA1_CTX*);
+
+#if defined(__cplusplus)
+}
+#endif
+
+#ifdef SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_H
+#include SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_H
+#endif
+
+#endif
diff --git a/src/util/hash/sha1dc/ubc_check.c b/src/util/hash/sha1dc/ubc_check.c
new file mode 100644
index 0000000..b3beff2
--- /dev/null
+++ b/src/util/hash/sha1dc/ubc_check.c
@@ -0,0 +1,372 @@
+/***
+* Copyright 2017 Marc Stevens <marc@marc-stevens.nl>, Dan Shumow <danshu@microsoft.com>
+* Distributed under the MIT Software License.
+* See accompanying file LICENSE.txt or copy at
+* https://opensource.org/licenses/MIT
+***/
+
+/*
+// this file was generated by the 'parse_bitrel' program in the tools section
+// using the data files from directory 'tools/data/3565'
+//
+// sha1_dvs contains a list of SHA-1 Disturbance Vectors (DV) to check
+// dvType, dvK and dvB define the DV: I(K,B) or II(K,B) (see the paper)
+// dm[80] is the expanded message block XOR-difference defined by the DV
+// testt is the step to do the recompression from for collision detection
+// maski and maskb define the bit to check for each DV in the dvmask returned by ubc_check
+//
+// ubc_check takes as input an expanded message block and verifies the unavoidable bitconditions for all listed DVs
+// it returns a dvmask where each bit belonging to a DV is set if all unavoidable bitconditions for that DV have been met
+// thus one needs to do the recompression check for each DV that has its bit set
+//
+// ubc_check is programmatically generated and the unavoidable bitconditions have been hardcoded
+// a directly verifiable version named ubc_check_verify can be found in ubc_check_verify.c
+// ubc_check has been verified against ubc_check_verify using the 'ubc_check_test' program in the tools section
+*/
+
+#ifndef SHA1DC_NO_STANDARD_INCLUDES
+#include <stdint.h>
+#endif
+#ifdef SHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C
+#include SHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C
+#endif
+#include "ubc_check.h"
+
+static const uint32_t DV_I_43_0_bit = (uint32_t)(1) << 0;
+static const uint32_t DV_I_44_0_bit = (uint32_t)(1) << 1;
+static const uint32_t DV_I_45_0_bit = (uint32_t)(1) << 2;
+static const uint32_t DV_I_46_0_bit = (uint32_t)(1) << 3;
+static const uint32_t DV_I_46_2_bit = (uint32_t)(1) << 4;
+static const uint32_t DV_I_47_0_bit = (uint32_t)(1) << 5;
+static const uint32_t DV_I_47_2_bit = (uint32_t)(1) << 6;
+static const uint32_t DV_I_48_0_bit = (uint32_t)(1) << 7;
+static const uint32_t DV_I_48_2_bit = (uint32_t)(1) << 8;
+static const uint32_t DV_I_49_0_bit = (uint32_t)(1) << 9;
+static const uint32_t DV_I_49_2_bit = (uint32_t)(1) << 10;
+static const uint32_t DV_I_50_0_bit = (uint32_t)(1) << 11;
+static const uint32_t DV_I_50_2_bit = (uint32_t)(1) << 12;
+static const uint32_t DV_I_51_0_bit = (uint32_t)(1) << 13;
+static const uint32_t DV_I_51_2_bit = (uint32_t)(1) << 14;
+static const uint32_t DV_I_52_0_bit = (uint32_t)(1) << 15;
+static const uint32_t DV_II_45_0_bit = (uint32_t)(1) << 16;
+static const uint32_t DV_II_46_0_bit = (uint32_t)(1) << 17;
+static const uint32_t DV_II_46_2_bit = (uint32_t)(1) << 18;
+static const uint32_t DV_II_47_0_bit = (uint32_t)(1) << 19;
+static const uint32_t DV_II_48_0_bit = (uint32_t)(1) << 20;
+static const uint32_t DV_II_49_0_bit = (uint32_t)(1) << 21;
+static const uint32_t DV_II_49_2_bit = (uint32_t)(1) << 22;
+static const uint32_t DV_II_50_0_bit = (uint32_t)(1) << 23;
+static const uint32_t DV_II_50_2_bit = (uint32_t)(1) << 24;
+static const uint32_t DV_II_51_0_bit = (uint32_t)(1) << 25;
+static const uint32_t DV_II_51_2_bit = (uint32_t)(1) << 26;
+static const uint32_t DV_II_52_0_bit = (uint32_t)(1) << 27;
+static const uint32_t DV_II_53_0_bit = (uint32_t)(1) << 28;
+static const uint32_t DV_II_54_0_bit = (uint32_t)(1) << 29;
+static const uint32_t DV_II_55_0_bit = (uint32_t)(1) << 30;
+static const uint32_t DV_II_56_0_bit = (uint32_t)(1) << 31;
+
+dv_info_t sha1_dvs[] =
+{
+ {1,43,0,58,0,0, { 0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408,0x800000e6,0x8000004c,0x00000803,0x80000161,0x80000599 } }
+, {1,44,0,58,0,1, { 0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408,0x800000e6,0x8000004c,0x00000803,0x80000161 } }
+, {1,45,0,58,0,2, { 0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408,0x800000e6,0x8000004c,0x00000803 } }
+, {1,46,0,58,0,3, { 0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408,0x800000e6,0x8000004c } }
+, {1,46,2,58,0,4, { 0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a,0x00000060,0x00000590,0x00001020,0x0000039a,0x00000132 } }
+, {1,47,0,58,0,5, { 0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408,0x800000e6 } }
+, {1,47,2,58,0,6, { 0x20000043,0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a,0x00000060,0x00000590,0x00001020,0x0000039a } }
+, {1,48,0,58,0,7, { 0xb800000a,0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408 } }
+, {1,48,2,58,0,8, { 0xe000002a,0x20000043,0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a,0x00000060,0x00000590,0x00001020 } }
+, {1,49,0,58,0,9, { 0x18000000,0xb800000a,0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164 } }
+, {1,49,2,58,0,10, { 0x60000000,0xe000002a,0x20000043,0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a,0x00000060,0x00000590 } }
+, {1,50,0,65,0,11, { 0x0800000c,0x18000000,0xb800000a,0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018 } }
+, {1,50,2,65,0,12, { 0x20000030,0x60000000,0xe000002a,0x20000043,0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a,0x00000060 } }
+, {1,51,0,65,0,13, { 0xe8000000,0x0800000c,0x18000000,0xb800000a,0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202 } }
+, {1,51,2,65,0,14, { 0xa0000003,0x20000030,0x60000000,0xe000002a,0x20000043,0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a } }
+, {1,52,0,65,0,15, { 0x04000010,0xe8000000,0x0800000c,0x18000000,0xb800000a,0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012 } }
+, {2,45,0,58,0,16, { 0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b,0x8000016d,0x8000041a,0x000002e4,0x80000054,0x00000967 } }
+, {2,46,0,58,0,17, { 0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b,0x8000016d,0x8000041a,0x000002e4,0x80000054 } }
+, {2,46,2,58,0,18, { 0x90000070,0xb0000053,0x30000008,0x00000043,0xd0000072,0xb0000010,0xf0000062,0xc0000042,0x00000030,0xe0000042,0x20000060,0xe0000041,0x20000050,0xc0000041,0xe0000072,0xa0000003,0xc0000012,0x60000041,0xc0000032,0x20000001,0xc0000002,0xe0000042,0x60000042,0x80000002,0x00000000,0x00000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000001,0x00000060,0x80000003,0x40000002,0xc0000040,0xc0000002,0x80000000,0x80000000,0x80000002,0x00000040,0x00000002,0x80000000,0x80000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000105,0x00000089,0x00000016,0x0000020b,0x0000011b,0x0000012d,0x0000041e,0x00000224,0x00000050,0x0000092e,0x0000046c,0x000005b6,0x0000106a,0x00000b90,0x00000152 } }
+, {2,47,0,58,0,19, { 0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b,0x8000016d,0x8000041a,0x000002e4 } }
+, {2,48,0,58,0,20, { 0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b,0x8000016d,0x8000041a } }
+, {2,49,0,58,0,21, { 0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b,0x8000016d } }
+, {2,49,2,58,0,22, { 0xf0000010,0xf000006a,0x80000040,0x90000070,0xb0000053,0x30000008,0x00000043,0xd0000072,0xb0000010,0xf0000062,0xc0000042,0x00000030,0xe0000042,0x20000060,0xe0000041,0x20000050,0xc0000041,0xe0000072,0xa0000003,0xc0000012,0x60000041,0xc0000032,0x20000001,0xc0000002,0xe0000042,0x60000042,0x80000002,0x00000000,0x00000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000001,0x00000060,0x80000003,0x40000002,0xc0000040,0xc0000002,0x80000000,0x80000000,0x80000002,0x00000040,0x00000002,0x80000000,0x80000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000105,0x00000089,0x00000016,0x0000020b,0x0000011b,0x0000012d,0x0000041e,0x00000224,0x00000050,0x0000092e,0x0000046c,0x000005b6 } }
+, {2,50,0,65,0,23, { 0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b } }
+, {2,50,2,65,0,24, { 0xd0000072,0xf0000010,0xf000006a,0x80000040,0x90000070,0xb0000053,0x30000008,0x00000043,0xd0000072,0xb0000010,0xf0000062,0xc0000042,0x00000030,0xe0000042,0x20000060,0xe0000041,0x20000050,0xc0000041,0xe0000072,0xa0000003,0xc0000012,0x60000041,0xc0000032,0x20000001,0xc0000002,0xe0000042,0x60000042,0x80000002,0x00000000,0x00000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000001,0x00000060,0x80000003,0x40000002,0xc0000040,0xc0000002,0x80000000,0x80000000,0x80000002,0x00000040,0x00000002,0x80000000,0x80000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000105,0x00000089,0x00000016,0x0000020b,0x0000011b,0x0000012d,0x0000041e,0x00000224,0x00000050,0x0000092e,0x0000046c } }
+, {2,51,0,65,0,25, { 0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b } }
+, {2,51,2,65,0,26, { 0x00000043,0xd0000072,0xf0000010,0xf000006a,0x80000040,0x90000070,0xb0000053,0x30000008,0x00000043,0xd0000072,0xb0000010,0xf0000062,0xc0000042,0x00000030,0xe0000042,0x20000060,0xe0000041,0x20000050,0xc0000041,0xe0000072,0xa0000003,0xc0000012,0x60000041,0xc0000032,0x20000001,0xc0000002,0xe0000042,0x60000042,0x80000002,0x00000000,0x00000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000001,0x00000060,0x80000003,0x40000002,0xc0000040,0xc0000002,0x80000000,0x80000000,0x80000002,0x00000040,0x00000002,0x80000000,0x80000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000105,0x00000089,0x00000016,0x0000020b,0x0000011b,0x0000012d,0x0000041e,0x00000224,0x00000050,0x0000092e } }
+, {2,52,0,65,0,27, { 0x0c000002,0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014 } }
+, {2,53,0,65,0,28, { 0xcc000014,0x0c000002,0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089 } }
+, {2,54,0,65,0,29, { 0x0400001c,0xcc000014,0x0c000002,0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107 } }
+, {2,55,0,65,0,30, { 0x00000010,0x0400001c,0xcc000014,0x0c000002,0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b } }
+, {2,56,0,65,0,31, { 0x2600001a,0x00000010,0x0400001c,0xcc000014,0x0c000002,0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046 } }
+, {0,0,0,0,0,0, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}
+};
+void ubc_check(const uint32_t W[80], uint32_t dvmask[1])
+{
+ uint32_t mask = ~((uint32_t)(0));
+ mask &= (((((W[44]^W[45])>>29)&1)-1) | ~(DV_I_48_0_bit|DV_I_51_0_bit|DV_I_52_0_bit|DV_II_45_0_bit|DV_II_46_0_bit|DV_II_50_0_bit|DV_II_51_0_bit));
+ mask &= (((((W[49]^W[50])>>29)&1)-1) | ~(DV_I_46_0_bit|DV_II_45_0_bit|DV_II_50_0_bit|DV_II_51_0_bit|DV_II_55_0_bit|DV_II_56_0_bit));
+ mask &= (((((W[48]^W[49])>>29)&1)-1) | ~(DV_I_45_0_bit|DV_I_52_0_bit|DV_II_49_0_bit|DV_II_50_0_bit|DV_II_54_0_bit|DV_II_55_0_bit));
+ mask &= ((((W[47]^(W[50]>>25))&(1<<4))-(1<<4)) | ~(DV_I_47_0_bit|DV_I_49_0_bit|DV_I_51_0_bit|DV_II_45_0_bit|DV_II_51_0_bit|DV_II_56_0_bit));
+ mask &= (((((W[47]^W[48])>>29)&1)-1) | ~(DV_I_44_0_bit|DV_I_51_0_bit|DV_II_48_0_bit|DV_II_49_0_bit|DV_II_53_0_bit|DV_II_54_0_bit));
+ mask &= (((((W[46]>>4)^(W[49]>>29))&1)-1) | ~(DV_I_46_0_bit|DV_I_48_0_bit|DV_I_50_0_bit|DV_I_52_0_bit|DV_II_50_0_bit|DV_II_55_0_bit));
+ mask &= (((((W[46]^W[47])>>29)&1)-1) | ~(DV_I_43_0_bit|DV_I_50_0_bit|DV_II_47_0_bit|DV_II_48_0_bit|DV_II_52_0_bit|DV_II_53_0_bit));
+ mask &= (((((W[45]>>4)^(W[48]>>29))&1)-1) | ~(DV_I_45_0_bit|DV_I_47_0_bit|DV_I_49_0_bit|DV_I_51_0_bit|DV_II_49_0_bit|DV_II_54_0_bit));
+ mask &= (((((W[45]^W[46])>>29)&1)-1) | ~(DV_I_49_0_bit|DV_I_52_0_bit|DV_II_46_0_bit|DV_II_47_0_bit|DV_II_51_0_bit|DV_II_52_0_bit));
+ mask &= (((((W[44]>>4)^(W[47]>>29))&1)-1) | ~(DV_I_44_0_bit|DV_I_46_0_bit|DV_I_48_0_bit|DV_I_50_0_bit|DV_II_48_0_bit|DV_II_53_0_bit));
+ mask &= (((((W[43]>>4)^(W[46]>>29))&1)-1) | ~(DV_I_43_0_bit|DV_I_45_0_bit|DV_I_47_0_bit|DV_I_49_0_bit|DV_II_47_0_bit|DV_II_52_0_bit));
+ mask &= (((((W[43]^W[44])>>29)&1)-1) | ~(DV_I_47_0_bit|DV_I_50_0_bit|DV_I_51_0_bit|DV_II_45_0_bit|DV_II_49_0_bit|DV_II_50_0_bit));
+ mask &= (((((W[42]>>4)^(W[45]>>29))&1)-1) | ~(DV_I_44_0_bit|DV_I_46_0_bit|DV_I_48_0_bit|DV_I_52_0_bit|DV_II_46_0_bit|DV_II_51_0_bit));
+ mask &= (((((W[41]>>4)^(W[44]>>29))&1)-1) | ~(DV_I_43_0_bit|DV_I_45_0_bit|DV_I_47_0_bit|DV_I_51_0_bit|DV_II_45_0_bit|DV_II_50_0_bit));
+ mask &= (((((W[40]^W[41])>>29)&1)-1) | ~(DV_I_44_0_bit|DV_I_47_0_bit|DV_I_48_0_bit|DV_II_46_0_bit|DV_II_47_0_bit|DV_II_56_0_bit));
+ mask &= (((((W[54]^W[55])>>29)&1)-1) | ~(DV_I_51_0_bit|DV_II_47_0_bit|DV_II_50_0_bit|DV_II_55_0_bit|DV_II_56_0_bit));
+ mask &= (((((W[53]^W[54])>>29)&1)-1) | ~(DV_I_50_0_bit|DV_II_46_0_bit|DV_II_49_0_bit|DV_II_54_0_bit|DV_II_55_0_bit));
+ mask &= (((((W[52]^W[53])>>29)&1)-1) | ~(DV_I_49_0_bit|DV_II_45_0_bit|DV_II_48_0_bit|DV_II_53_0_bit|DV_II_54_0_bit));
+ mask &= ((((W[50]^(W[53]>>25))&(1<<4))-(1<<4)) | ~(DV_I_50_0_bit|DV_I_52_0_bit|DV_II_46_0_bit|DV_II_48_0_bit|DV_II_54_0_bit));
+ mask &= (((((W[50]^W[51])>>29)&1)-1) | ~(DV_I_47_0_bit|DV_II_46_0_bit|DV_II_51_0_bit|DV_II_52_0_bit|DV_II_56_0_bit));
+ mask &= ((((W[49]^(W[52]>>25))&(1<<4))-(1<<4)) | ~(DV_I_49_0_bit|DV_I_51_0_bit|DV_II_45_0_bit|DV_II_47_0_bit|DV_II_53_0_bit));
+ mask &= ((((W[48]^(W[51]>>25))&(1<<4))-(1<<4)) | ~(DV_I_48_0_bit|DV_I_50_0_bit|DV_I_52_0_bit|DV_II_46_0_bit|DV_II_52_0_bit));
+ mask &= (((((W[42]^W[43])>>29)&1)-1) | ~(DV_I_46_0_bit|DV_I_49_0_bit|DV_I_50_0_bit|DV_II_48_0_bit|DV_II_49_0_bit));
+ mask &= (((((W[41]^W[42])>>29)&1)-1) | ~(DV_I_45_0_bit|DV_I_48_0_bit|DV_I_49_0_bit|DV_II_47_0_bit|DV_II_48_0_bit));
+ mask &= (((((W[40]>>4)^(W[43]>>29))&1)-1) | ~(DV_I_44_0_bit|DV_I_46_0_bit|DV_I_50_0_bit|DV_II_49_0_bit|DV_II_56_0_bit));
+ mask &= (((((W[39]>>4)^(W[42]>>29))&1)-1) | ~(DV_I_43_0_bit|DV_I_45_0_bit|DV_I_49_0_bit|DV_II_48_0_bit|DV_II_55_0_bit));
+ if (mask & (DV_I_44_0_bit|DV_I_48_0_bit|DV_II_47_0_bit|DV_II_54_0_bit|DV_II_56_0_bit))
+ mask &= (((((W[38]>>4)^(W[41]>>29))&1)-1) | ~(DV_I_44_0_bit|DV_I_48_0_bit|DV_II_47_0_bit|DV_II_54_0_bit|DV_II_56_0_bit));
+ mask &= (((((W[37]>>4)^(W[40]>>29))&1)-1) | ~(DV_I_43_0_bit|DV_I_47_0_bit|DV_II_46_0_bit|DV_II_53_0_bit|DV_II_55_0_bit));
+ if (mask & (DV_I_52_0_bit|DV_II_48_0_bit|DV_II_51_0_bit|DV_II_56_0_bit))
+ mask &= (((((W[55]^W[56])>>29)&1)-1) | ~(DV_I_52_0_bit|DV_II_48_0_bit|DV_II_51_0_bit|DV_II_56_0_bit));
+ if (mask & (DV_I_52_0_bit|DV_II_48_0_bit|DV_II_50_0_bit|DV_II_56_0_bit))
+ mask &= ((((W[52]^(W[55]>>25))&(1<<4))-(1<<4)) | ~(DV_I_52_0_bit|DV_II_48_0_bit|DV_II_50_0_bit|DV_II_56_0_bit));
+ if (mask & (DV_I_51_0_bit|DV_II_47_0_bit|DV_II_49_0_bit|DV_II_55_0_bit))
+ mask &= ((((W[51]^(W[54]>>25))&(1<<4))-(1<<4)) | ~(DV_I_51_0_bit|DV_II_47_0_bit|DV_II_49_0_bit|DV_II_55_0_bit));
+ if (mask & (DV_I_48_0_bit|DV_II_47_0_bit|DV_II_52_0_bit|DV_II_53_0_bit))
+ mask &= (((((W[51]^W[52])>>29)&1)-1) | ~(DV_I_48_0_bit|DV_II_47_0_bit|DV_II_52_0_bit|DV_II_53_0_bit));
+ if (mask & (DV_I_46_0_bit|DV_I_49_0_bit|DV_II_45_0_bit|DV_II_48_0_bit))
+ mask &= (((((W[36]>>4)^(W[40]>>29))&1)-1) | ~(DV_I_46_0_bit|DV_I_49_0_bit|DV_II_45_0_bit|DV_II_48_0_bit));
+ if (mask & (DV_I_52_0_bit|DV_II_48_0_bit|DV_II_49_0_bit))
+ mask &= ((0-(((W[53]^W[56])>>29)&1)) | ~(DV_I_52_0_bit|DV_II_48_0_bit|DV_II_49_0_bit));
+ if (mask & (DV_I_50_0_bit|DV_II_46_0_bit|DV_II_47_0_bit))
+ mask &= ((0-(((W[51]^W[54])>>29)&1)) | ~(DV_I_50_0_bit|DV_II_46_0_bit|DV_II_47_0_bit));
+ if (mask & (DV_I_49_0_bit|DV_I_51_0_bit|DV_II_45_0_bit))
+ mask &= ((0-(((W[50]^W[52])>>29)&1)) | ~(DV_I_49_0_bit|DV_I_51_0_bit|DV_II_45_0_bit));
+ if (mask & (DV_I_48_0_bit|DV_I_50_0_bit|DV_I_52_0_bit))
+ mask &= ((0-(((W[49]^W[51])>>29)&1)) | ~(DV_I_48_0_bit|DV_I_50_0_bit|DV_I_52_0_bit));
+ if (mask & (DV_I_47_0_bit|DV_I_49_0_bit|DV_I_51_0_bit))
+ mask &= ((0-(((W[48]^W[50])>>29)&1)) | ~(DV_I_47_0_bit|DV_I_49_0_bit|DV_I_51_0_bit));
+ if (mask & (DV_I_46_0_bit|DV_I_48_0_bit|DV_I_50_0_bit))
+ mask &= ((0-(((W[47]^W[49])>>29)&1)) | ~(DV_I_46_0_bit|DV_I_48_0_bit|DV_I_50_0_bit));
+ if (mask & (DV_I_45_0_bit|DV_I_47_0_bit|DV_I_49_0_bit))
+ mask &= ((0-(((W[46]^W[48])>>29)&1)) | ~(DV_I_45_0_bit|DV_I_47_0_bit|DV_I_49_0_bit));
+ mask &= ((((W[45]^W[47])&(1<<6))-(1<<6)) | ~(DV_I_47_2_bit|DV_I_49_2_bit|DV_I_51_2_bit));
+ if (mask & (DV_I_44_0_bit|DV_I_46_0_bit|DV_I_48_0_bit))
+ mask &= ((0-(((W[45]^W[47])>>29)&1)) | ~(DV_I_44_0_bit|DV_I_46_0_bit|DV_I_48_0_bit));
+ mask &= (((((W[44]^W[46])>>6)&1)-1) | ~(DV_I_46_2_bit|DV_I_48_2_bit|DV_I_50_2_bit));
+ if (mask & (DV_I_43_0_bit|DV_I_45_0_bit|DV_I_47_0_bit))
+ mask &= ((0-(((W[44]^W[46])>>29)&1)) | ~(DV_I_43_0_bit|DV_I_45_0_bit|DV_I_47_0_bit));
+ mask &= ((0-((W[41]^(W[42]>>5))&(1<<1))) | ~(DV_I_48_2_bit|DV_II_46_2_bit|DV_II_51_2_bit));
+ mask &= ((0-((W[40]^(W[41]>>5))&(1<<1))) | ~(DV_I_47_2_bit|DV_I_51_2_bit|DV_II_50_2_bit));
+ if (mask & (DV_I_44_0_bit|DV_I_46_0_bit|DV_II_56_0_bit))
+ mask &= ((0-(((W[40]^W[42])>>4)&1)) | ~(DV_I_44_0_bit|DV_I_46_0_bit|DV_II_56_0_bit));
+ mask &= ((0-((W[39]^(W[40]>>5))&(1<<1))) | ~(DV_I_46_2_bit|DV_I_50_2_bit|DV_II_49_2_bit));
+ if (mask & (DV_I_43_0_bit|DV_I_45_0_bit|DV_II_55_0_bit))
+ mask &= ((0-(((W[39]^W[41])>>4)&1)) | ~(DV_I_43_0_bit|DV_I_45_0_bit|DV_II_55_0_bit));
+ if (mask & (DV_I_44_0_bit|DV_II_54_0_bit|DV_II_56_0_bit))
+ mask &= ((0-(((W[38]^W[40])>>4)&1)) | ~(DV_I_44_0_bit|DV_II_54_0_bit|DV_II_56_0_bit));
+ if (mask & (DV_I_43_0_bit|DV_II_53_0_bit|DV_II_55_0_bit))
+ mask &= ((0-(((W[37]^W[39])>>4)&1)) | ~(DV_I_43_0_bit|DV_II_53_0_bit|DV_II_55_0_bit));
+ mask &= ((0-((W[36]^(W[37]>>5))&(1<<1))) | ~(DV_I_47_2_bit|DV_I_50_2_bit|DV_II_46_2_bit));
+ if (mask & (DV_I_45_0_bit|DV_I_48_0_bit|DV_II_47_0_bit))
+ mask &= (((((W[35]>>4)^(W[39]>>29))&1)-1) | ~(DV_I_45_0_bit|DV_I_48_0_bit|DV_II_47_0_bit));
+ if (mask & (DV_I_48_0_bit|DV_II_48_0_bit))
+ mask &= ((0-((W[63]^(W[64]>>5))&(1<<0))) | ~(DV_I_48_0_bit|DV_II_48_0_bit));
+ if (mask & (DV_I_45_0_bit|DV_II_45_0_bit))
+ mask &= ((0-((W[63]^(W[64]>>5))&(1<<1))) | ~(DV_I_45_0_bit|DV_II_45_0_bit));
+ if (mask & (DV_I_47_0_bit|DV_II_47_0_bit))
+ mask &= ((0-((W[62]^(W[63]>>5))&(1<<0))) | ~(DV_I_47_0_bit|DV_II_47_0_bit));
+ if (mask & (DV_I_46_0_bit|DV_II_46_0_bit))
+ mask &= ((0-((W[61]^(W[62]>>5))&(1<<0))) | ~(DV_I_46_0_bit|DV_II_46_0_bit));
+ mask &= ((0-((W[61]^(W[62]>>5))&(1<<2))) | ~(DV_I_46_2_bit|DV_II_46_2_bit));
+ if (mask & (DV_I_45_0_bit|DV_II_45_0_bit))
+ mask &= ((0-((W[60]^(W[61]>>5))&(1<<0))) | ~(DV_I_45_0_bit|DV_II_45_0_bit));
+ if (mask & (DV_II_51_0_bit|DV_II_54_0_bit))
+ mask &= (((((W[58]^W[59])>>29)&1)-1) | ~(DV_II_51_0_bit|DV_II_54_0_bit));
+ if (mask & (DV_II_50_0_bit|DV_II_53_0_bit))
+ mask &= (((((W[57]^W[58])>>29)&1)-1) | ~(DV_II_50_0_bit|DV_II_53_0_bit));
+ if (mask & (DV_II_52_0_bit|DV_II_54_0_bit))
+ mask &= ((((W[56]^(W[59]>>25))&(1<<4))-(1<<4)) | ~(DV_II_52_0_bit|DV_II_54_0_bit));
+ if (mask & (DV_II_51_0_bit|DV_II_52_0_bit))
+ mask &= ((0-(((W[56]^W[59])>>29)&1)) | ~(DV_II_51_0_bit|DV_II_52_0_bit));
+ if (mask & (DV_II_49_0_bit|DV_II_52_0_bit))
+ mask &= (((((W[56]^W[57])>>29)&1)-1) | ~(DV_II_49_0_bit|DV_II_52_0_bit));
+ if (mask & (DV_II_51_0_bit|DV_II_53_0_bit))
+ mask &= ((((W[55]^(W[58]>>25))&(1<<4))-(1<<4)) | ~(DV_II_51_0_bit|DV_II_53_0_bit));
+ if (mask & (DV_II_50_0_bit|DV_II_52_0_bit))
+ mask &= ((((W[54]^(W[57]>>25))&(1<<4))-(1<<4)) | ~(DV_II_50_0_bit|DV_II_52_0_bit));
+ if (mask & (DV_II_49_0_bit|DV_II_51_0_bit))
+ mask &= ((((W[53]^(W[56]>>25))&(1<<4))-(1<<4)) | ~(DV_II_49_0_bit|DV_II_51_0_bit));
+ mask &= ((((W[51]^(W[50]>>5))&(1<<1))-(1<<1)) | ~(DV_I_50_2_bit|DV_II_46_2_bit));
+ mask &= ((((W[48]^W[50])&(1<<6))-(1<<6)) | ~(DV_I_50_2_bit|DV_II_46_2_bit));
+ if (mask & (DV_I_51_0_bit|DV_I_52_0_bit))
+ mask &= ((0-(((W[48]^W[55])>>29)&1)) | ~(DV_I_51_0_bit|DV_I_52_0_bit));
+ mask &= ((((W[47]^W[49])&(1<<6))-(1<<6)) | ~(DV_I_49_2_bit|DV_I_51_2_bit));
+ mask &= ((((W[48]^(W[47]>>5))&(1<<1))-(1<<1)) | ~(DV_I_47_2_bit|DV_II_51_2_bit));
+ mask &= ((((W[46]^W[48])&(1<<6))-(1<<6)) | ~(DV_I_48_2_bit|DV_I_50_2_bit));
+ mask &= ((((W[47]^(W[46]>>5))&(1<<1))-(1<<1)) | ~(DV_I_46_2_bit|DV_II_50_2_bit));
+ mask &= ((0-((W[44]^(W[45]>>5))&(1<<1))) | ~(DV_I_51_2_bit|DV_II_49_2_bit));
+ mask &= ((((W[43]^W[45])&(1<<6))-(1<<6)) | ~(DV_I_47_2_bit|DV_I_49_2_bit));
+ mask &= (((((W[42]^W[44])>>6)&1)-1) | ~(DV_I_46_2_bit|DV_I_48_2_bit));
+ mask &= ((((W[43]^(W[42]>>5))&(1<<1))-(1<<1)) | ~(DV_II_46_2_bit|DV_II_51_2_bit));
+ mask &= ((((W[42]^(W[41]>>5))&(1<<1))-(1<<1)) | ~(DV_I_51_2_bit|DV_II_50_2_bit));
+ mask &= ((((W[41]^(W[40]>>5))&(1<<1))-(1<<1)) | ~(DV_I_50_2_bit|DV_II_49_2_bit));
+ if (mask & (DV_I_52_0_bit|DV_II_51_0_bit))
+ mask &= ((((W[39]^(W[43]>>25))&(1<<4))-(1<<4)) | ~(DV_I_52_0_bit|DV_II_51_0_bit));
+ if (mask & (DV_I_51_0_bit|DV_II_50_0_bit))
+ mask &= ((((W[38]^(W[42]>>25))&(1<<4))-(1<<4)) | ~(DV_I_51_0_bit|DV_II_50_0_bit));
+ if (mask & (DV_I_48_2_bit|DV_I_51_2_bit))
+ mask &= ((0-((W[37]^(W[38]>>5))&(1<<1))) | ~(DV_I_48_2_bit|DV_I_51_2_bit));
+ if (mask & (DV_I_50_0_bit|DV_II_49_0_bit))
+ mask &= ((((W[37]^(W[41]>>25))&(1<<4))-(1<<4)) | ~(DV_I_50_0_bit|DV_II_49_0_bit));
+ if (mask & (DV_II_52_0_bit|DV_II_54_0_bit))
+ mask &= ((0-((W[36]^W[38])&(1<<4))) | ~(DV_II_52_0_bit|DV_II_54_0_bit));
+ mask &= ((0-((W[35]^(W[36]>>5))&(1<<1))) | ~(DV_I_46_2_bit|DV_I_49_2_bit));
+ if (mask & (DV_I_51_0_bit|DV_II_47_0_bit))
+ mask &= ((((W[35]^(W[39]>>25))&(1<<3))-(1<<3)) | ~(DV_I_51_0_bit|DV_II_47_0_bit));
+if (mask) {
+
+ if (mask & DV_I_43_0_bit)
+ if (
+ !((W[61]^(W[62]>>5)) & (1<<1))
+ || !(!((W[59]^(W[63]>>25)) & (1<<5)))
+ || !((W[58]^(W[63]>>30)) & (1<<0))
+ ) mask &= ~DV_I_43_0_bit;
+ if (mask & DV_I_44_0_bit)
+ if (
+ !((W[62]^(W[63]>>5)) & (1<<1))
+ || !(!((W[60]^(W[64]>>25)) & (1<<5)))
+ || !((W[59]^(W[64]>>30)) & (1<<0))
+ ) mask &= ~DV_I_44_0_bit;
+ if (mask & DV_I_46_2_bit)
+ mask &= ((~((W[40]^W[42])>>2)) | ~DV_I_46_2_bit);
+ if (mask & DV_I_47_2_bit)
+ if (
+ !((W[62]^(W[63]>>5)) & (1<<2))
+ || !(!((W[41]^W[43]) & (1<<6)))
+ ) mask &= ~DV_I_47_2_bit;
+ if (mask & DV_I_48_2_bit)
+ if (
+ !((W[63]^(W[64]>>5)) & (1<<2))
+ || !(!((W[48]^(W[49]<<5)) & (1<<6)))
+ ) mask &= ~DV_I_48_2_bit;
+ if (mask & DV_I_49_2_bit)
+ if (
+ !(!((W[49]^(W[50]<<5)) & (1<<6)))
+ || !((W[42]^W[50]) & (1<<1))
+ || !(!((W[39]^(W[40]<<5)) & (1<<6)))
+ || !((W[38]^W[40]) & (1<<1))
+ ) mask &= ~DV_I_49_2_bit;
+ if (mask & DV_I_50_0_bit)
+ mask &= ((((W[36]^W[37])<<7)) | ~DV_I_50_0_bit);
+ if (mask & DV_I_50_2_bit)
+ mask &= ((((W[43]^W[51])<<11)) | ~DV_I_50_2_bit);
+ if (mask & DV_I_51_0_bit)
+ mask &= ((((W[37]^W[38])<<9)) | ~DV_I_51_0_bit);
+ if (mask & DV_I_51_2_bit)
+ if (
+ !(!((W[51]^(W[52]<<5)) & (1<<6)))
+ || !(!((W[49]^W[51]) & (1<<6)))
+ || !(!((W[37]^(W[37]>>5)) & (1<<1)))
+ || !(!((W[35]^(W[39]>>25)) & (1<<5)))
+ ) mask &= ~DV_I_51_2_bit;
+ if (mask & DV_I_52_0_bit)
+ mask &= ((((W[38]^W[39])<<11)) | ~DV_I_52_0_bit);
+ if (mask & DV_II_46_2_bit)
+ mask &= ((((W[47]^W[51])<<17)) | ~DV_II_46_2_bit);
+ if (mask & DV_II_48_0_bit)
+ if (
+ !(!((W[36]^(W[40]>>25)) & (1<<3)))
+ || !((W[35]^(W[40]<<2)) & (1<<30))
+ ) mask &= ~DV_II_48_0_bit;
+ if (mask & DV_II_49_0_bit)
+ if (
+ !(!((W[37]^(W[41]>>25)) & (1<<3)))
+ || !((W[36]^(W[41]<<2)) & (1<<30))
+ ) mask &= ~DV_II_49_0_bit;
+ if (mask & DV_II_49_2_bit)
+ if (
+ !(!((W[53]^(W[54]<<5)) & (1<<6)))
+ || !(!((W[51]^W[53]) & (1<<6)))
+ || !((W[50]^W[54]) & (1<<1))
+ || !(!((W[45]^(W[46]<<5)) & (1<<6)))
+ || !(!((W[37]^(W[41]>>25)) & (1<<5)))
+ || !((W[36]^(W[41]>>30)) & (1<<0))
+ ) mask &= ~DV_II_49_2_bit;
+ if (mask & DV_II_50_0_bit)
+ if (
+ !((W[55]^W[58]) & (1<<29))
+ || !(!((W[38]^(W[42]>>25)) & (1<<3)))
+ || !((W[37]^(W[42]<<2)) & (1<<30))
+ ) mask &= ~DV_II_50_0_bit;
+ if (mask & DV_II_50_2_bit)
+ if (
+ !(!((W[54]^(W[55]<<5)) & (1<<6)))
+ || !(!((W[52]^W[54]) & (1<<6)))
+ || !((W[51]^W[55]) & (1<<1))
+ || !((W[45]^W[47]) & (1<<1))
+ || !(!((W[38]^(W[42]>>25)) & (1<<5)))
+ || !((W[37]^(W[42]>>30)) & (1<<0))
+ ) mask &= ~DV_II_50_2_bit;
+ if (mask & DV_II_51_0_bit)
+ if (
+ !(!((W[39]^(W[43]>>25)) & (1<<3)))
+ || !((W[38]^(W[43]<<2)) & (1<<30))
+ ) mask &= ~DV_II_51_0_bit;
+ if (mask & DV_II_51_2_bit)
+ if (
+ !(!((W[55]^(W[56]<<5)) & (1<<6)))
+ || !(!((W[53]^W[55]) & (1<<6)))
+ || !((W[52]^W[56]) & (1<<1))
+ || !((W[46]^W[48]) & (1<<1))
+ || !(!((W[39]^(W[43]>>25)) & (1<<5)))
+ || !((W[38]^(W[43]>>30)) & (1<<0))
+ ) mask &= ~DV_II_51_2_bit;
+ if (mask & DV_II_52_0_bit)
+ if (
+ !(!((W[59]^W[60]) & (1<<29)))
+ || !(!((W[40]^(W[44]>>25)) & (1<<3)))
+ || !(!((W[40]^(W[44]>>25)) & (1<<4)))
+ || !((W[39]^(W[44]<<2)) & (1<<30))
+ ) mask &= ~DV_II_52_0_bit;
+ if (mask & DV_II_53_0_bit)
+ if (
+ !((W[58]^W[61]) & (1<<29))
+ || !(!((W[57]^(W[61]>>25)) & (1<<4)))
+ || !(!((W[41]^(W[45]>>25)) & (1<<3)))
+ || !(!((W[41]^(W[45]>>25)) & (1<<4)))
+ ) mask &= ~DV_II_53_0_bit;
+ if (mask & DV_II_54_0_bit)
+ if (
+ !(!((W[58]^(W[62]>>25)) & (1<<4)))
+ || !(!((W[42]^(W[46]>>25)) & (1<<3)))
+ || !(!((W[42]^(W[46]>>25)) & (1<<4)))
+ ) mask &= ~DV_II_54_0_bit;
+ if (mask & DV_II_55_0_bit)
+ if (
+ !(!((W[59]^(W[63]>>25)) & (1<<4)))
+ || !(!((W[57]^(W[59]>>25)) & (1<<4)))
+ || !(!((W[43]^(W[47]>>25)) & (1<<3)))
+ || !(!((W[43]^(W[47]>>25)) & (1<<4)))
+ ) mask &= ~DV_II_55_0_bit;
+ if (mask & DV_II_56_0_bit)
+ if (
+ !(!((W[60]^(W[64]>>25)) & (1<<4)))
+ || !(!((W[44]^(W[48]>>25)) & (1<<3)))
+ || !(!((W[44]^(W[48]>>25)) & (1<<4)))
+ ) mask &= ~DV_II_56_0_bit;
+}
+
+ dvmask[0]=mask;
+}
+
+#ifdef SHA1DC_CUSTOM_TRAILING_INCLUDE_UBC_CHECK_C
+#include SHA1DC_CUSTOM_TRAILING_INCLUDE_UBC_CHECK_C
+#endif
diff --git a/src/util/hash/sha1dc/ubc_check.h b/src/util/hash/sha1dc/ubc_check.h
new file mode 100644
index 0000000..d7e17dc
--- /dev/null
+++ b/src/util/hash/sha1dc/ubc_check.h
@@ -0,0 +1,52 @@
+/***
+* Copyright 2017 Marc Stevens <marc@marc-stevens.nl>, Dan Shumow <danshu@microsoft.com>
+* Distributed under the MIT Software License.
+* See accompanying file LICENSE.txt or copy at
+* https://opensource.org/licenses/MIT
+***/
+
+/*
+// this file was generated by the 'parse_bitrel' program in the tools section
+// using the data files from directory 'tools/data/3565'
+//
+// sha1_dvs contains a list of SHA-1 Disturbance Vectors (DV) to check
+// dvType, dvK and dvB define the DV: I(K,B) or II(K,B) (see the paper)
+// dm[80] is the expanded message block XOR-difference defined by the DV
+// testt is the step to do the recompression from for collision detection
+// maski and maskb define the bit to check for each DV in the dvmask returned by ubc_check
+//
+// ubc_check takes as input an expanded message block and verifies the unavoidable bitconditions for all listed DVs
+// it returns a dvmask where each bit belonging to a DV is set if all unavoidable bitconditions for that DV have been met
+// thus one needs to do the recompression check for each DV that has its bit set
+*/
+
+#ifndef SHA1DC_UBC_CHECK_H
+#define SHA1DC_UBC_CHECK_H
+
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+#ifndef SHA1DC_NO_STANDARD_INCLUDES
+#include <stdint.h>
+#endif
+
+#define DVMASKSIZE 1
+typedef struct { int dvType; int dvK; int dvB; int testt; int maski; int maskb; uint32_t dm[80]; } dv_info_t;
+extern dv_info_t sha1_dvs[];
+void ubc_check(const uint32_t W[80], uint32_t dvmask[DVMASKSIZE]);
+
+#define DOSTORESTATE58
+#define DOSTORESTATE65
+
+#define CHECK_DVMASK(_DVMASK) (0 != _DVMASK[0])
+
+#if defined(__cplusplus)
+}
+#endif
+
+#ifdef SHA1DC_CUSTOM_TRAILING_INCLUDE_UBC_CHECK_H
+#include SHA1DC_CUSTOM_TRAILING_INCLUDE_UBC_CHECK_H
+#endif
+
+#endif
diff --git a/src/util/hash/win32.c b/src/util/hash/win32.c
new file mode 100644
index 0000000..f80c0d5
--- /dev/null
+++ b/src/util/hash/win32.c
@@ -0,0 +1,549 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "win32.h"
+
+#include "runtime.h"
+
+#include <wincrypt.h>
+#include <strsafe.h>
+
+#define GIT_HASH_CNG_DLL_NAME "bcrypt.dll"
+
+/* BCRYPT_SHA1_ALGORITHM */
+#define GIT_HASH_CNG_SHA1_TYPE L"SHA1"
+#define GIT_HASH_CNG_SHA256_TYPE L"SHA256"
+
+/* BCRYPT_OBJECT_LENGTH */
+#define GIT_HASH_CNG_HASH_OBJECT_LEN L"ObjectLength"
+
+/* BCRYPT_HASH_REUSEABLE_FLAGS */
+#define GIT_HASH_CNG_HASH_REUSABLE 0x00000020
+
+/* Definitions */
+
+/* CryptoAPI is available for hashing on Windows XP and newer. */
+struct cryptoapi_provider {
+ HCRYPTPROV handle;
+};
+
+/*
+ * CNG (bcrypt.dll) is significantly more performant than CryptoAPI and is
+ * preferred, however it is only available on Windows 2008 and newer and
+ * must therefore be dynamically loaded, and we must inline constants that
+ * would not exist when building in pre-Windows 2008 environments.
+ */
+
+/* Function declarations for CNG */
+typedef NTSTATUS (WINAPI *cng_open_algorithm_provider_fn)(
+ HANDLE /* BCRYPT_ALG_HANDLE */ *phAlgorithm,
+ LPCWSTR pszAlgId,
+ LPCWSTR pszImplementation,
+ DWORD dwFlags);
+
+typedef NTSTATUS (WINAPI *cng_get_property_fn)(
+ HANDLE /* BCRYPT_HANDLE */ hObject,
+ LPCWSTR pszProperty,
+ PUCHAR pbOutput,
+ ULONG cbOutput,
+ ULONG *pcbResult,
+ ULONG dwFlags);
+
+typedef NTSTATUS (WINAPI *cng_create_hash_fn)(
+ HANDLE /* BCRYPT_ALG_HANDLE */ hAlgorithm,
+ HANDLE /* BCRYPT_HASH_HANDLE */ *phHash,
+ PUCHAR pbHashObject, ULONG cbHashObject,
+ PUCHAR pbSecret,
+ ULONG cbSecret,
+ ULONG dwFlags);
+
+typedef NTSTATUS (WINAPI *cng_finish_hash_fn)(
+ HANDLE /* BCRYPT_HASH_HANDLE */ hHash,
+ PUCHAR pbOutput,
+ ULONG cbOutput,
+ ULONG dwFlags);
+
+typedef NTSTATUS (WINAPI *cng_hash_data_fn)(
+ HANDLE /* BCRYPT_HASH_HANDLE */ hHash,
+ PUCHAR pbInput,
+ ULONG cbInput,
+ ULONG dwFlags);
+
+typedef NTSTATUS (WINAPI *cng_destroy_hash_fn)(
+ HANDLE /* BCRYPT_HASH_HANDLE */ hHash);
+
+typedef NTSTATUS (WINAPI *cng_close_algorithm_provider_fn)(
+ HANDLE /* BCRYPT_ALG_HANDLE */ hAlgorithm,
+ ULONG dwFlags);
+
+struct cng_provider {
+ /* DLL for CNG */
+ HINSTANCE dll;
+
+ /* Function pointers for CNG */
+ cng_open_algorithm_provider_fn open_algorithm_provider;
+ cng_get_property_fn get_property;
+ cng_create_hash_fn create_hash;
+ cng_finish_hash_fn finish_hash;
+ cng_hash_data_fn hash_data;
+ cng_destroy_hash_fn destroy_hash;
+ cng_close_algorithm_provider_fn close_algorithm_provider;
+
+ HANDLE /* BCRYPT_ALG_HANDLE */ sha1_handle;
+ DWORD sha1_object_size;
+
+ HANDLE /* BCRYPT_ALG_HANDLE */ sha256_handle;
+ DWORD sha256_object_size;
+};
+
+typedef struct {
+ git_hash_win32_provider_t type;
+
+ union {
+ struct cryptoapi_provider cryptoapi;
+ struct cng_provider cng;
+ } provider;
+} hash_win32_provider;
+
+/* Hash provider definition */
+
+static hash_win32_provider hash_provider = {0};
+
+/* Hash initialization */
+
+/* Initialize CNG, if available */
+GIT_INLINE(int) cng_provider_init(void)
+{
+ char dll_path[MAX_PATH];
+ DWORD dll_path_len, size_len;
+
+ /* Only use CNG on Windows 2008 / Vista SP1 or better (Windows 6.0 SP1) */
+ if (!git_has_win32_version(6, 0, 1)) {
+ git_error_set(GIT_ERROR_SHA, "CryptoNG is not supported on this platform");
+ return -1;
+ }
+
+ /* Load bcrypt.dll explicitly from the system directory */
+ if ((dll_path_len = GetSystemDirectory(dll_path, MAX_PATH)) == 0 ||
+ dll_path_len > MAX_PATH ||
+ StringCchCat(dll_path, MAX_PATH, "\\") < 0 ||
+ StringCchCat(dll_path, MAX_PATH, GIT_HASH_CNG_DLL_NAME) < 0 ||
+ (hash_provider.provider.cng.dll = LoadLibrary(dll_path)) == NULL) {
+ git_error_set(GIT_ERROR_SHA, "CryptoNG library could not be loaded");
+ return -1;
+ }
+
+ /* Load the function addresses */
+ if ((hash_provider.provider.cng.open_algorithm_provider = (cng_open_algorithm_provider_fn)((void *)GetProcAddress(hash_provider.provider.cng.dll, "BCryptOpenAlgorithmProvider"))) == NULL ||
+ (hash_provider.provider.cng.get_property = (cng_get_property_fn)((void *)GetProcAddress(hash_provider.provider.cng.dll, "BCryptGetProperty"))) == NULL ||
+ (hash_provider.provider.cng.create_hash = (cng_create_hash_fn)((void *)GetProcAddress(hash_provider.provider.cng.dll, "BCryptCreateHash"))) == NULL ||
+ (hash_provider.provider.cng.finish_hash = (cng_finish_hash_fn)((void *)GetProcAddress(hash_provider.provider.cng.dll, "BCryptFinishHash"))) == NULL ||
+ (hash_provider.provider.cng.hash_data = (cng_hash_data_fn)((void *)GetProcAddress(hash_provider.provider.cng.dll, "BCryptHashData"))) == NULL ||
+ (hash_provider.provider.cng.destroy_hash = (cng_destroy_hash_fn)((void *)GetProcAddress(hash_provider.provider.cng.dll, "BCryptDestroyHash"))) == NULL ||
+ (hash_provider.provider.cng.close_algorithm_provider = (cng_close_algorithm_provider_fn)((void *)GetProcAddress(hash_provider.provider.cng.dll, "BCryptCloseAlgorithmProvider"))) == NULL) {
+ FreeLibrary(hash_provider.provider.cng.dll);
+
+ git_error_set(GIT_ERROR_OS, "CryptoNG functions could not be loaded");
+ return -1;
+ }
+
+ /* Load the SHA1 algorithm */
+ if (hash_provider.provider.cng.open_algorithm_provider(&hash_provider.provider.cng.sha1_handle, GIT_HASH_CNG_SHA1_TYPE, NULL, GIT_HASH_CNG_HASH_REUSABLE) < 0 ||
+ hash_provider.provider.cng.get_property(hash_provider.provider.cng.sha1_handle, GIT_HASH_CNG_HASH_OBJECT_LEN, (PBYTE)&hash_provider.provider.cng.sha1_object_size, sizeof(DWORD), &size_len, 0) < 0) {
+ git_error_set(GIT_ERROR_OS, "algorithm provider could not be initialized");
+ goto on_error;
+ }
+
+ /* Load the SHA256 algorithm */
+ if (hash_provider.provider.cng.open_algorithm_provider(&hash_provider.provider.cng.sha256_handle, GIT_HASH_CNG_SHA256_TYPE, NULL, GIT_HASH_CNG_HASH_REUSABLE) < 0 ||
+ hash_provider.provider.cng.get_property(hash_provider.provider.cng.sha256_handle, GIT_HASH_CNG_HASH_OBJECT_LEN, (PBYTE)&hash_provider.provider.cng.sha256_object_size, sizeof(DWORD), &size_len, 0) < 0) {
+ git_error_set(GIT_ERROR_OS, "algorithm provider could not be initialized");
+ goto on_error;
+ }
+
+ hash_provider.type = GIT_HASH_WIN32_CNG;
+ return 0;
+
+on_error:
+ if (hash_provider.provider.cng.sha1_handle)
+ hash_provider.provider.cng.close_algorithm_provider(hash_provider.provider.cng.sha1_handle, 0);
+
+ if (hash_provider.provider.cng.sha256_handle)
+ hash_provider.provider.cng.close_algorithm_provider(hash_provider.provider.cng.sha256_handle, 0);
+
+ if (hash_provider.provider.cng.dll)
+ FreeLibrary(hash_provider.provider.cng.dll);
+
+ return -1;
+}
+
+GIT_INLINE(void) cng_provider_shutdown(void)
+{
+ if (hash_provider.type == GIT_HASH_WIN32_INVALID)
+ return;
+
+ hash_provider.provider.cng.close_algorithm_provider(hash_provider.provider.cng.sha1_handle, 0);
+ hash_provider.provider.cng.close_algorithm_provider(hash_provider.provider.cng.sha256_handle, 0);
+ FreeLibrary(hash_provider.provider.cng.dll);
+
+ hash_provider.type = GIT_HASH_WIN32_INVALID;
+}
+
+/* Initialize CryptoAPI */
+GIT_INLINE(int) cryptoapi_provider_init(void)
+{
+ if (!CryptAcquireContext(&hash_provider.provider.cryptoapi.handle, NULL, 0, PROV_RSA_AES, CRYPT_VERIFYCONTEXT)) {
+ git_error_set(GIT_ERROR_OS, "legacy hash context could not be started");
+ return -1;
+ }
+
+ hash_provider.type = GIT_HASH_WIN32_CRYPTOAPI;
+ return 0;
+}
+
+GIT_INLINE(void) cryptoapi_provider_shutdown(void)
+{
+ if (hash_provider.type == GIT_HASH_WIN32_INVALID)
+ return;
+
+ CryptReleaseContext(hash_provider.provider.cryptoapi.handle, 0);
+
+ hash_provider.type = GIT_HASH_WIN32_INVALID;
+}
+
+static void hash_provider_shutdown(void)
+{
+ if (hash_provider.type == GIT_HASH_WIN32_CNG)
+ cng_provider_shutdown();
+ else if (hash_provider.type == GIT_HASH_WIN32_CRYPTOAPI)
+ cryptoapi_provider_shutdown();
+}
+
+static int hash_provider_init(void)
+{
+ int error = 0;
+
+ if (hash_provider.type != GIT_HASH_WIN32_INVALID)
+ return 0;
+
+ if ((error = cng_provider_init()) < 0)
+ error = cryptoapi_provider_init();
+
+ if (!error)
+ error = git_runtime_shutdown_register(hash_provider_shutdown);
+
+ return error;
+}
+
+git_hash_win32_provider_t git_hash_win32_provider(void)
+{
+ return hash_provider.type;
+}
+
+int git_hash_win32_set_provider(git_hash_win32_provider_t provider)
+{
+ if (provider == hash_provider.type)
+ return 0;
+
+ hash_provider_shutdown();
+
+ if (provider == GIT_HASH_WIN32_CNG)
+ return cng_provider_init();
+ else if (provider == GIT_HASH_WIN32_CRYPTOAPI)
+ return cryptoapi_provider_init();
+
+ git_error_set(GIT_ERROR_SHA, "unsupported win32 provider");
+ return -1;
+}
+
+/* CryptoAPI: available in Windows XP and newer */
+
+GIT_INLINE(int) hash_cryptoapi_init(git_hash_win32_ctx *ctx)
+{
+ if (ctx->ctx.cryptoapi.valid)
+ CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle);
+
+ if (!CryptCreateHash(hash_provider.provider.cryptoapi.handle, ctx->algorithm, 0, 0, &ctx->ctx.cryptoapi.hash_handle)) {
+ ctx->ctx.cryptoapi.valid = 0;
+ git_error_set(GIT_ERROR_OS, "legacy hash implementation could not be created");
+ return -1;
+ }
+
+ ctx->ctx.cryptoapi.valid = 1;
+ return 0;
+}
+
+GIT_INLINE(int) hash_cryptoapi_update(git_hash_win32_ctx *ctx, const void *_data, size_t len)
+{
+ const BYTE *data = (BYTE *)_data;
+
+ GIT_ASSERT(ctx->ctx.cryptoapi.valid);
+
+ while (len > 0) {
+ DWORD chunk = (len > MAXDWORD) ? MAXDWORD : (DWORD)len;
+
+ if (!CryptHashData(ctx->ctx.cryptoapi.hash_handle, data, chunk, 0)) {
+ git_error_set(GIT_ERROR_OS, "legacy hash data could not be updated");
+ return -1;
+ }
+
+ data += chunk;
+ len -= chunk;
+ }
+
+ return 0;
+}
+
+GIT_INLINE(int) hash_cryptoapi_final(unsigned char *out, git_hash_win32_ctx *ctx)
+{
+ DWORD len = ctx->algorithm == CALG_SHA_256 ? GIT_HASH_SHA256_SIZE : GIT_HASH_SHA1_SIZE;
+ int error = 0;
+
+ GIT_ASSERT(ctx->ctx.cryptoapi.valid);
+
+ if (!CryptGetHashParam(ctx->ctx.cryptoapi.hash_handle, HP_HASHVAL, out, &len, 0)) {
+ git_error_set(GIT_ERROR_OS, "legacy hash data could not be finished");
+ error = -1;
+ }
+
+ CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle);
+ ctx->ctx.cryptoapi.valid = 0;
+
+ return error;
+}
+
+GIT_INLINE(void) hash_ctx_cryptoapi_cleanup(git_hash_win32_ctx *ctx)
+{
+ if (ctx->ctx.cryptoapi.valid)
+ CryptDestroyHash(ctx->ctx.cryptoapi.hash_handle);
+}
+
+GIT_INLINE(int) hash_sha1_cryptoapi_ctx_init_init(git_hash_win32_ctx *ctx)
+{
+ ctx->algorithm = CALG_SHA1;
+ return hash_cryptoapi_init(ctx);
+}
+
+GIT_INLINE(int) hash_sha256_cryptoapi_ctx_init(git_hash_win32_ctx *ctx)
+{
+ ctx->algorithm = CALG_SHA_256;
+ return hash_cryptoapi_init(ctx);
+}
+
+/* CNG: Available in Windows Server 2008 and newer */
+
+GIT_INLINE(int) hash_sha1_cng_ctx_init(git_hash_win32_ctx *ctx)
+{
+ if ((ctx->ctx.cng.hash_object = git__malloc(hash_provider.provider.cng.sha1_object_size)) == NULL)
+ return -1;
+
+ if (hash_provider.provider.cng.create_hash(hash_provider.provider.cng.sha1_handle, &ctx->ctx.cng.hash_handle, ctx->ctx.cng.hash_object, hash_provider.provider.cng.sha1_object_size, NULL, 0, 0) < 0) {
+ git__free(ctx->ctx.cng.hash_object);
+
+ git_error_set(GIT_ERROR_OS, "sha1 implementation could not be created");
+ return -1;
+ }
+
+ ctx->algorithm = CALG_SHA1;
+ return 0;
+}
+
+GIT_INLINE(int) hash_sha256_cng_ctx_init(git_hash_win32_ctx *ctx)
+{
+ if ((ctx->ctx.cng.hash_object = git__malloc(hash_provider.provider.cng.sha256_object_size)) == NULL)
+ return -1;
+
+ if (hash_provider.provider.cng.create_hash(hash_provider.provider.cng.sha256_handle, &ctx->ctx.cng.hash_handle, ctx->ctx.cng.hash_object, hash_provider.provider.cng.sha256_object_size, NULL, 0, 0) < 0) {
+ git__free(ctx->ctx.cng.hash_object);
+
+ git_error_set(GIT_ERROR_OS, "sha256 implementation could not be created");
+ return -1;
+ }
+
+ ctx->algorithm = CALG_SHA_256;
+ return 0;
+}
+
+GIT_INLINE(int) hash_cng_init(git_hash_win32_ctx *ctx)
+{
+ BYTE hash[GIT_HASH_SHA256_SIZE];
+ ULONG size = ctx->algorithm == CALG_SHA_256 ? GIT_HASH_SHA256_SIZE : GIT_HASH_SHA1_SIZE;
+
+ if (!ctx->ctx.cng.updated)
+ return 0;
+
+ /* CNG needs to be finished to restart */
+ if (hash_provider.provider.cng.finish_hash(ctx->ctx.cng.hash_handle, hash, size, 0) < 0) {
+ git_error_set(GIT_ERROR_OS, "hash implementation could not be finished");
+ return -1;
+ }
+
+ ctx->ctx.cng.updated = 0;
+
+ return 0;
+}
+
+GIT_INLINE(int) hash_cng_update(git_hash_win32_ctx *ctx, const void *_data, size_t len)
+{
+ PBYTE data = (PBYTE)_data;
+
+ while (len > 0) {
+ ULONG chunk = (len > ULONG_MAX) ? ULONG_MAX : (ULONG)len;
+
+ if (hash_provider.provider.cng.hash_data(ctx->ctx.cng.hash_handle, data, chunk, 0) < 0) {
+ git_error_set(GIT_ERROR_OS, "hash could not be updated");
+ return -1;
+ }
+
+ data += chunk;
+ len -= chunk;
+ }
+
+ return 0;
+}
+
+GIT_INLINE(int) hash_cng_final(unsigned char *out, git_hash_win32_ctx *ctx)
+{
+ ULONG size = ctx->algorithm == CALG_SHA_256 ? GIT_HASH_SHA256_SIZE : GIT_HASH_SHA1_SIZE;
+
+ if (hash_provider.provider.cng.finish_hash(ctx->ctx.cng.hash_handle, out, size, 0) < 0) {
+ git_error_set(GIT_ERROR_OS, "hash could not be finished");
+ return -1;
+ }
+
+ ctx->ctx.cng.updated = 0;
+
+ return 0;
+}
+
+GIT_INLINE(void) hash_ctx_cng_cleanup(git_hash_win32_ctx *ctx)
+{
+ hash_provider.provider.cng.destroy_hash(ctx->ctx.cng.hash_handle);
+ git__free(ctx->ctx.cng.hash_object);
+}
+
+/* Indirection between CryptoAPI and CNG */
+
+GIT_INLINE(int) hash_sha1_win32_ctx_init(git_hash_win32_ctx *ctx)
+{
+ GIT_ASSERT_ARG(hash_provider.type);
+
+ memset(ctx, 0x0, sizeof(git_hash_win32_ctx));
+ return (hash_provider.type == GIT_HASH_WIN32_CNG) ? hash_sha1_cng_ctx_init(ctx) : hash_sha1_cryptoapi_ctx_init_init(ctx);
+}
+
+GIT_INLINE(int) hash_sha256_win32_ctx_init(git_hash_win32_ctx *ctx)
+{
+ GIT_ASSERT_ARG(hash_provider.type);
+
+ memset(ctx, 0x0, sizeof(git_hash_win32_ctx));
+ return (hash_provider.type == GIT_HASH_WIN32_CNG) ? hash_sha256_cng_ctx_init(ctx) : hash_sha256_cryptoapi_ctx_init(ctx);
+}
+
+GIT_INLINE(int) hash_win32_init(git_hash_win32_ctx *ctx)
+{
+ return (hash_provider.type == GIT_HASH_WIN32_CNG) ? hash_cng_init(ctx) : hash_cryptoapi_init(ctx);
+}
+
+GIT_INLINE(int) hash_win32_update(git_hash_win32_ctx *ctx, const void *data, size_t len)
+{
+ return (hash_provider.type == GIT_HASH_WIN32_CNG) ? hash_cng_update(ctx, data, len) : hash_cryptoapi_update(ctx, data, len);
+}
+
+GIT_INLINE(int) hash_win32_final(unsigned char *out, git_hash_win32_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+ return (hash_provider.type == GIT_HASH_WIN32_CNG) ? hash_cng_final(out, ctx) : hash_cryptoapi_final(out, ctx);
+}
+
+GIT_INLINE(void) hash_win32_cleanup(git_hash_win32_ctx *ctx)
+{
+ if (hash_provider.type == GIT_HASH_WIN32_CNG)
+ hash_ctx_cng_cleanup(ctx);
+ else if(hash_provider.type == GIT_HASH_WIN32_CRYPTOAPI)
+ hash_ctx_cryptoapi_cleanup(ctx);
+}
+
+#ifdef GIT_SHA1_WIN32
+
+int git_hash_sha1_global_init(void)
+{
+ return hash_provider_init();
+}
+
+int git_hash_sha1_ctx_init(git_hash_sha1_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+ return hash_sha1_win32_ctx_init(&ctx->win32);
+}
+
+int git_hash_sha1_init(git_hash_sha1_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+ return hash_win32_init(&ctx->win32);
+}
+
+int git_hash_sha1_update(git_hash_sha1_ctx *ctx, const void *data, size_t len)
+{
+ GIT_ASSERT_ARG(ctx);
+ return hash_win32_update(&ctx->win32, data, len);
+}
+
+int git_hash_sha1_final(unsigned char *out, git_hash_sha1_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+ return hash_win32_final(out, &ctx->win32);
+}
+
+void git_hash_sha1_ctx_cleanup(git_hash_sha1_ctx *ctx)
+{
+ if (!ctx)
+ return;
+ hash_win32_cleanup(&ctx->win32);
+}
+
+#endif
+
+#ifdef GIT_SHA256_WIN32
+
+int git_hash_sha256_global_init(void)
+{
+ return hash_provider_init();
+}
+
+int git_hash_sha256_ctx_init(git_hash_sha256_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+ return hash_sha256_win32_ctx_init(&ctx->win32);
+}
+
+int git_hash_sha256_init(git_hash_sha256_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+ return hash_win32_init(&ctx->win32);
+}
+
+int git_hash_sha256_update(git_hash_sha256_ctx *ctx, const void *data, size_t len)
+{
+ GIT_ASSERT_ARG(ctx);
+ return hash_win32_update(&ctx->win32, data, len);
+}
+
+int git_hash_sha256_final(unsigned char *out, git_hash_sha256_ctx *ctx)
+{
+ GIT_ASSERT_ARG(ctx);
+ return hash_win32_final(out, &ctx->win32);
+}
+
+void git_hash_sha256_ctx_cleanup(git_hash_sha256_ctx *ctx)
+{
+ if (!ctx)
+ return;
+ hash_win32_cleanup(&ctx->win32);
+}
+
+#endif
diff --git a/src/util/hash/win32.h b/src/util/hash/win32.h
new file mode 100644
index 0000000..a9fb87a
--- /dev/null
+++ b/src/util/hash/win32.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_hash_win32_h__
+#define INCLUDE_hash_win32_h__
+
+#include "hash/sha.h"
+
+#include <wincrypt.h>
+
+typedef enum {
+ GIT_HASH_WIN32_INVALID = 0,
+ GIT_HASH_WIN32_CRYPTOAPI,
+ GIT_HASH_WIN32_CNG
+} git_hash_win32_provider_t;
+
+struct git_hash_win32_cryptoapi_ctx {
+ bool valid;
+ HCRYPTHASH hash_handle;
+};
+
+struct git_hash_win32_cng_ctx {
+ bool updated;
+ HANDLE /* BCRYPT_HASH_HANDLE */ hash_handle;
+ PBYTE hash_object;
+};
+
+typedef struct {
+ ALG_ID algorithm;
+
+ union {
+ struct git_hash_win32_cryptoapi_ctx cryptoapi;
+ struct git_hash_win32_cng_ctx cng;
+ } ctx;
+} git_hash_win32_ctx;
+
+/*
+ * Gets/sets the current hash provider (cng or cryptoapi). This is only
+ * for testing purposes.
+ */
+git_hash_win32_provider_t git_hash_win32_provider(void);
+int git_hash_win32_set_provider(git_hash_win32_provider_t provider);
+
+#ifdef GIT_SHA1_WIN32
+struct git_hash_sha1_ctx {
+ git_hash_win32_ctx win32;
+};
+#endif
+
+#ifdef GIT_SHA256_WIN32
+struct git_hash_sha256_ctx {
+ git_hash_win32_ctx win32;
+};
+#endif
+
+#endif
diff --git a/src/util/integer.h b/src/util/integer.h
new file mode 100644
index 0000000..6327717
--- /dev/null
+++ b/src/util/integer.h
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_integer_h__
+#define INCLUDE_integer_h__
+
+/** @return true if p fits into the range of a size_t */
+GIT_INLINE(int) git__is_sizet(int64_t p)
+{
+ size_t r = (size_t)p;
+ return p == (int64_t)r;
+}
+
+/** @return true if p fits into the range of an ssize_t */
+GIT_INLINE(int) git__is_ssizet(size_t p)
+{
+ ssize_t r = (ssize_t)p;
+ return p == (size_t)r;
+}
+
+/** @return true if p fits into the range of a uint16_t */
+GIT_INLINE(int) git__is_uint16(size_t p)
+{
+ uint16_t r = (uint16_t)p;
+ return p == (size_t)r;
+}
+
+/** @return true if p fits into the range of a uint32_t */
+GIT_INLINE(int) git__is_uint32(size_t p)
+{
+ uint32_t r = (uint32_t)p;
+ return p == (size_t)r;
+}
+
+/** @return true if p fits into the range of an unsigned long */
+GIT_INLINE(int) git__is_ulong(int64_t p)
+{
+ unsigned long r = (unsigned long)p;
+ return p == (int64_t)r;
+}
+
+/** @return true if p fits into the range of an int */
+GIT_INLINE(int) git__is_int(int64_t p)
+{
+ int r = (int)p;
+ return p == (int64_t)r;
+}
+
+/* Use clang/gcc compiler intrinsics whenever possible */
+#if (__has_builtin(__builtin_add_overflow) || \
+ (defined(__GNUC__) && (__GNUC__ >= 5)))
+
+# if (SIZE_MAX == UINT_MAX)
+# define git__add_sizet_overflow(out, one, two) \
+ __builtin_uadd_overflow(one, two, out)
+# define git__multiply_sizet_overflow(out, one, two) \
+ __builtin_umul_overflow(one, two, out)
+# elif (SIZE_MAX == ULONG_MAX)
+# define git__add_sizet_overflow(out, one, two) \
+ __builtin_uaddl_overflow(one, two, out)
+# define git__multiply_sizet_overflow(out, one, two) \
+ __builtin_umull_overflow(one, two, out)
+# elif (SIZE_MAX == ULLONG_MAX)
+# define git__add_sizet_overflow(out, one, two) \
+ __builtin_uaddll_overflow(one, two, out)
+# define git__multiply_sizet_overflow(out, one, two) \
+ __builtin_umulll_overflow(one, two, out)
+# else
+# error compiler has add with overflow intrinsics but SIZE_MAX is unknown
+# endif
+
+# define git__add_int_overflow(out, one, two) \
+ __builtin_sadd_overflow(one, two, out)
+# define git__sub_int_overflow(out, one, two) \
+ __builtin_ssub_overflow(one, two, out)
+
+# define git__add_int64_overflow(out, one, two) \
+ __builtin_add_overflow(one, two, out)
+
+/* clang on 32-bit systems produces an undefined reference to `__mulodi4`. */
+# if !defined(__clang__) || !defined(GIT_ARCH_32)
+# define git__multiply_int64_overflow(out, one, two) \
+ __builtin_mul_overflow(one, two, out)
+# endif
+
+/* Use Microsoft's safe integer handling functions where available */
+#elif defined(_MSC_VER)
+
+# define ENABLE_INTSAFE_SIGNED_FUNCTIONS
+# include <intsafe.h>
+
+# define git__add_sizet_overflow(out, one, two) \
+ (SizeTAdd(one, two, out) != S_OK)
+# define git__multiply_sizet_overflow(out, one, two) \
+ (SizeTMult(one, two, out) != S_OK)
+
+#define git__add_int_overflow(out, one, two) \
+ (IntAdd(one, two, out) != S_OK)
+#define git__sub_int_overflow(out, one, two) \
+ (IntSub(one, two, out) != S_OK)
+
+#define git__add_int64_overflow(out, one, two) \
+ (LongLongAdd(one, two, out) != S_OK)
+#define git__multiply_int64_overflow(out, one, two) \
+ (LongLongMult(one, two, out) != S_OK)
+
+#else
+
+/**
+ * Sets `one + two` into `out`, unless the arithmetic would overflow.
+ * @return false if the result fits in a `size_t`, true on overflow.
+ */
+GIT_INLINE(bool) git__add_sizet_overflow(size_t *out, size_t one, size_t two)
+{
+ if (SIZE_MAX - one < two)
+ return true;
+ *out = one + two;
+ return false;
+}
+
+/**
+ * Sets `one * two` into `out`, unless the arithmetic would overflow.
+ * @return false if the result fits in a `size_t`, true on overflow.
+ */
+GIT_INLINE(bool) git__multiply_sizet_overflow(size_t *out, size_t one, size_t two)
+{
+ if (one && SIZE_MAX / one < two)
+ return true;
+ *out = one * two;
+ return false;
+}
+
+GIT_INLINE(bool) git__add_int_overflow(int *out, int one, int two)
+{
+ if ((two > 0 && one > (INT_MAX - two)) ||
+ (two < 0 && one < (INT_MIN - two)))
+ return true;
+ *out = one + two;
+ return false;
+}
+
+GIT_INLINE(bool) git__sub_int_overflow(int *out, int one, int two)
+{
+ if ((two > 0 && one < (INT_MIN + two)) ||
+ (two < 0 && one > (INT_MAX + two)))
+ return true;
+ *out = one - two;
+ return false;
+}
+
+GIT_INLINE(bool) git__add_int64_overflow(int64_t *out, int64_t one, int64_t two)
+{
+ if ((two > 0 && one > (INT64_MAX - two)) ||
+ (two < 0 && one < (INT64_MIN - two)))
+ return true;
+ *out = one + two;
+ return false;
+}
+
+#endif
+
+/* If we could not provide an intrinsic implementation for this, provide a (slow) fallback. */
+#if !defined(git__multiply_int64_overflow)
+GIT_INLINE(bool) git__multiply_int64_overflow(int64_t *out, int64_t one, int64_t two)
+{
+ /*
+ * Detects whether `INT64_MAX < (one * two) || INT64_MIN > (one * two)`,
+ * without incurring in undefined behavior. That is done by performing the
+ * comparison with a division instead of a multiplication, which translates
+ * to `INT64_MAX / one < two || INT64_MIN / one > two`. Some caveats:
+ *
+ * - The comparison sign is inverted when both sides of the inequality are
+ * multiplied/divided by a negative number, so if `one < 0` the comparison
+ * needs to be flipped.
+ * - `INT64_MAX / -1` itself overflows (or traps), so that case should be
+ * avoided.
+ * - Since the overflow flag is defined as the discrepance between the result
+ * of performing the multiplication in a signed integer at twice the width
+ * of the operands, and the truncated+sign-extended version of that same
+ * result, there are four cases where the result is the opposite of what
+ * would be expected:
+ * * `INT64_MIN * -1` / `-1 * INT64_MIN`
+ * * `INT64_MIN * 1 / `1 * INT64_MIN`
+ */
+ if (one && two) {
+ if (one > 0 && two > 0) {
+ if (INT64_MAX / one < two)
+ return true;
+ } else if (one < 0 && two < 0) {
+ if ((one == -1 && two == INT64_MIN) ||
+ (two == -1 && one == INT64_MIN)) {
+ *out = INT64_MIN;
+ return false;
+ }
+ if (INT64_MAX / one > two)
+ return true;
+ } else if (one > 0 && two < 0) {
+ if ((one == 1 && two == INT64_MIN) ||
+ (INT64_MIN / one > two))
+ return true;
+ } else if (one == -1) {
+ if (INT64_MIN / two > one)
+ return true;
+ } else {
+ if ((one == INT64_MIN && two == 1) ||
+ (INT64_MIN / one < two))
+ return true;
+ }
+ }
+ *out = one * two;
+ return false;
+}
+#endif
+
+#endif
diff --git a/src/util/khash.h b/src/util/khash.h
new file mode 100644
index 0000000..c9b7f13
--- /dev/null
+++ b/src/util/khash.h
@@ -0,0 +1,615 @@
+/* The MIT License
+
+ Copyright (c) 2008, 2009, 2011 by Attractive Chaos <attractor@live.co.uk>
+
+ Permission is hereby granted, free of charge, to any person obtaining
+ a copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be
+ included in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+*/
+
+/*
+ An example:
+
+#include "khash.h"
+KHASH_MAP_INIT_INT(32, char)
+int main() {
+ int ret, is_missing;
+ khiter_t k;
+ khash_t(32) *h = kh_init(32);
+ k = kh_put(32, h, 5, &ret);
+ kh_value(h, k) = 10;
+ k = kh_get(32, h, 10);
+ is_missing = (k == kh_end(h));
+ k = kh_get(32, h, 5);
+ kh_del(32, h, k);
+ for (k = kh_begin(h); k != kh_end(h); ++k)
+ if (kh_exist(h, k)) kh_value(h, k) = 1;
+ kh_destroy(32, h);
+ return 0;
+}
+*/
+
+/*
+ 2013-05-02 (0.2.8):
+
+ * Use quadratic probing. When the capacity is power of 2, stepping function
+ i*(i+1)/2 guarantees to traverse each bucket. It is better than double
+ hashing on cache performance and is more robust than linear probing.
+
+ In theory, double hashing should be more robust than quadratic probing.
+ However, my implementation is probably not for large hash tables, because
+ the second hash function is closely tied to the first hash function,
+ which reduce the effectiveness of double hashing.
+
+ Reference: http://research.cs.vt.edu/AVresearch/hashing/quadratic.php
+
+ 2011-12-29 (0.2.7):
+
+ * Minor code clean up; no actual effect.
+
+ 2011-09-16 (0.2.6):
+
+ * The capacity is a power of 2. This seems to dramatically improve the
+ speed for simple keys. Thank Zilong Tan for the suggestion. Reference:
+
+ - http://code.google.com/p/ulib/
+ - http://nothings.org/computer/judy/
+
+ * Allow to optionally use linear probing which usually has better
+ performance for random input. Double hashing is still the default as it
+ is more robust to certain non-random input.
+
+ * Added Wang's integer hash function (not used by default). This hash
+ function is more robust to certain non-random input.
+
+ 2011-02-14 (0.2.5):
+
+ * Allow to declare global functions.
+
+ 2009-09-26 (0.2.4):
+
+ * Improve portability
+
+ 2008-09-19 (0.2.3):
+
+ * Corrected the example
+ * Improved interfaces
+
+ 2008-09-11 (0.2.2):
+
+ * Improved speed a little in kh_put()
+
+ 2008-09-10 (0.2.1):
+
+ * Added kh_clear()
+ * Fixed a compiling error
+
+ 2008-09-02 (0.2.0):
+
+ * Changed to token concatenation which increases flexibility.
+
+ 2008-08-31 (0.1.2):
+
+ * Fixed a bug in kh_get(), which has not been tested previously.
+
+ 2008-08-31 (0.1.1):
+
+ * Added destructor
+*/
+
+
+#ifndef __AC_KHASH_H
+#define __AC_KHASH_H
+
+/*!
+ @header
+
+ Generic hash table library.
+ */
+
+#define AC_VERSION_KHASH_H "0.2.8"
+
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+/* compiler specific configuration */
+
+typedef uint32_t khint32_t;
+typedef uint64_t khint64_t;
+
+#ifndef kh_inline
+#ifdef _MSC_VER
+#define kh_inline __inline
+#elif defined(__GNUC__)
+#define kh_inline __inline__
+#else
+#define kh_inline
+#endif
+#endif /* kh_inline */
+
+typedef khint32_t khint_t;
+typedef khint_t khiter_t;
+
+#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2)
+#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1)
+#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3)
+#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1)))
+#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1)))
+#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1)))
+#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1))
+
+#define __ac_fsize(m) ((m) < 16? 1 : (m)>>4)
+
+#ifndef kroundup32
+#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x))
+#endif
+
+#ifndef kcalloc
+#define kcalloc(N,Z) calloc(N,Z)
+#endif
+#ifndef kmalloc
+#define kmalloc(Z) malloc(Z)
+#endif
+#ifndef krealloc
+#define krealloc(P,Z) realloc(P,Z)
+#endif
+#ifndef kreallocarray
+#define kreallocarray(P,N,Z) ((SIZE_MAX - N < Z) ? NULL : krealloc(P, (N*Z)))
+#endif
+#ifndef kfree
+#define kfree(P) free(P)
+#endif
+
+static const double __ac_HASH_UPPER = 0.77;
+
+#define __KHASH_TYPE(name, khkey_t, khval_t) \
+ typedef struct kh_##name##_s { \
+ khint_t n_buckets, size, n_occupied, upper_bound; \
+ khint32_t *flags; \
+ khkey_t *keys; \
+ khval_t *vals; \
+ } kh_##name##_t;
+
+#define __KHASH_PROTOTYPES(name, khkey_t, khval_t) \
+ extern kh_##name##_t *kh_init_##name(void); \
+ extern void kh_destroy_##name(kh_##name##_t *h); \
+ extern void kh_clear_##name(kh_##name##_t *h); \
+ extern khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key); \
+ extern int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets); \
+ extern khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret); \
+ extern void kh_del_##name(kh_##name##_t *h, khint_t x);
+
+#define __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
+ SCOPE kh_##name##_t *kh_init_##name(void) { \
+ return (kh_##name##_t*)kcalloc(1, sizeof(kh_##name##_t)); \
+ } \
+ SCOPE void kh_destroy_##name(kh_##name##_t *h) \
+ { \
+ if (h) { \
+ kfree((void *)h->keys); kfree(h->flags); \
+ kfree((void *)h->vals); \
+ kfree(h); \
+ } \
+ } \
+ SCOPE void kh_clear_##name(kh_##name##_t *h) \
+ { \
+ if (h && h->flags) { \
+ memset(h->flags, 0xaa, __ac_fsize(h->n_buckets) * sizeof(khint32_t)); \
+ h->size = h->n_occupied = 0; \
+ } \
+ } \
+ SCOPE khint_t kh_get_##name(const kh_##name##_t *h, khkey_t key) \
+ { \
+ if (h->n_buckets) { \
+ khint_t k, i, last, mask, step = 0; \
+ mask = h->n_buckets - 1; \
+ k = __hash_func(key); i = k & mask; \
+ last = i; \
+ while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
+ i = (i + (++step)) & mask; \
+ if (i == last) return h->n_buckets; \
+ } \
+ return __ac_iseither(h->flags, i)? h->n_buckets : i; \
+ } else return 0; \
+ } \
+ SCOPE int kh_resize_##name(kh_##name##_t *h, khint_t new_n_buckets) \
+ { /* This function uses 0.25*n_buckets bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets. */ \
+ khint32_t *new_flags = 0; \
+ khint_t j = 1; \
+ { \
+ kroundup32(new_n_buckets); \
+ if (new_n_buckets < 4) new_n_buckets = 4; \
+ if (h->size >= (khint_t)(new_n_buckets * __ac_HASH_UPPER + 0.5)) j = 0; /* requested size is too small */ \
+ else { /* hash table size to be changed (shrink or expand); rehash */ \
+ new_flags = (khint32_t*)kreallocarray(NULL, __ac_fsize(new_n_buckets), sizeof(khint32_t)); \
+ if (!new_flags) return -1; \
+ memset(new_flags, 0xaa, __ac_fsize(new_n_buckets) * sizeof(khint32_t)); \
+ if (h->n_buckets < new_n_buckets) { /* expand */ \
+ khkey_t *new_keys = (khkey_t*)kreallocarray((void *)h->keys, new_n_buckets, sizeof(khkey_t)); \
+ if (!new_keys) { kfree(new_flags); return -1; } \
+ h->keys = new_keys; \
+ if (kh_is_map) { \
+ khval_t *new_vals = (khval_t*)kreallocarray((void *)h->vals, new_n_buckets, sizeof(khval_t)); \
+ if (!new_vals) { kfree(new_flags); return -1; } \
+ h->vals = new_vals; \
+ } \
+ } /* otherwise shrink */ \
+ } \
+ } \
+ if (j) { /* rehashing is needed */ \
+ for (j = 0; j != h->n_buckets; ++j) { \
+ if (__ac_iseither(h->flags, j) == 0) { \
+ khkey_t key = h->keys[j]; \
+ khval_t val; \
+ khint_t new_mask; \
+ new_mask = new_n_buckets - 1; \
+ if (kh_is_map) val = h->vals[j]; \
+ __ac_set_isdel_true(h->flags, j); \
+ while (1) { /* kick-out process; sort of like in Cuckoo hashing */ \
+ khint_t k, i, step = 0; \
+ k = __hash_func(key); \
+ i = k & new_mask; \
+ while (!__ac_isempty(new_flags, i)) i = (i + (++step)) & new_mask; \
+ __ac_set_isempty_false(new_flags, i); \
+ if (i < h->n_buckets && __ac_iseither(h->flags, i) == 0) { /* kick out the existing element */ \
+ { khkey_t tmp = h->keys[i]; h->keys[i] = key; key = tmp; } \
+ if (kh_is_map) { khval_t tmp = h->vals[i]; h->vals[i] = val; val = tmp; } \
+ __ac_set_isdel_true(h->flags, i); /* mark it as deleted in the old hash table */ \
+ } else { /* write the element and jump out of the loop */ \
+ h->keys[i] = key; \
+ if (kh_is_map) h->vals[i] = val; \
+ break; \
+ } \
+ } \
+ } \
+ } \
+ if (h->n_buckets > new_n_buckets) { /* shrink the hash table */ \
+ h->keys = (khkey_t*)kreallocarray((void *)h->keys, new_n_buckets, sizeof(khkey_t)); \
+ if (kh_is_map) h->vals = (khval_t*)kreallocarray((void *)h->vals, new_n_buckets, sizeof(khval_t)); \
+ } \
+ kfree(h->flags); /* free the working space */ \
+ h->flags = new_flags; \
+ h->n_buckets = new_n_buckets; \
+ h->n_occupied = h->size; \
+ h->upper_bound = (khint_t)(h->n_buckets * __ac_HASH_UPPER + 0.5); \
+ } \
+ return 0; \
+ } \
+ SCOPE khint_t kh_put_##name(kh_##name##_t *h, khkey_t key, int *ret) \
+ { \
+ khint_t x; \
+ if (h->n_occupied >= h->upper_bound) { /* update the hash table */ \
+ if (h->n_buckets > (h->size<<1)) { \
+ if (kh_resize_##name(h, h->n_buckets - 1) < 0) { /* clear "deleted" elements */ \
+ *ret = -1; return h->n_buckets; \
+ } \
+ } else if (kh_resize_##name(h, h->n_buckets + 1) < 0) { /* expand the hash table */ \
+ *ret = -1; return h->n_buckets; \
+ } \
+ } /* TODO: to implement automatically shrinking; resize() already support shrinking */ \
+ { \
+ khint_t k, i, site, last, mask = h->n_buckets - 1, step = 0; \
+ x = site = h->n_buckets; k = __hash_func(key); i = k & mask; \
+ if (__ac_isempty(h->flags, i)) x = i; /* for speed up */ \
+ else { \
+ last = i; \
+ while (!__ac_isempty(h->flags, i) && (__ac_isdel(h->flags, i) || !__hash_equal(h->keys[i], key))) { \
+ if (__ac_isdel(h->flags, i)) site = i; \
+ i = (i + (++step)) & mask; \
+ if (i == last) { x = site; break; } \
+ } \
+ if (x == h->n_buckets) { \
+ if (__ac_isempty(h->flags, i) && site != h->n_buckets) x = site; \
+ else x = i; \
+ } \
+ } \
+ } \
+ if (__ac_isempty(h->flags, x)) { /* not present at all */ \
+ h->keys[x] = key; \
+ __ac_set_isboth_false(h->flags, x); \
+ ++h->size; ++h->n_occupied; \
+ *ret = 1; \
+ } else if (__ac_isdel(h->flags, x)) { /* deleted */ \
+ h->keys[x] = key; \
+ __ac_set_isboth_false(h->flags, x); \
+ ++h->size; \
+ *ret = 2; \
+ } else *ret = 0; /* Don't touch h->keys[x] if present and not deleted */ \
+ return x; \
+ } \
+ SCOPE void kh_del_##name(kh_##name##_t *h, khint_t x) \
+ { \
+ if (x != h->n_buckets && !__ac_iseither(h->flags, x)) { \
+ __ac_set_isdel_true(h->flags, x); \
+ --h->size; \
+ } \
+ }
+
+#define KHASH_DECLARE(name, khkey_t, khval_t) \
+ __KHASH_TYPE(name, khkey_t, khval_t) \
+ __KHASH_PROTOTYPES(name, khkey_t, khval_t)
+
+#define KHASH_INIT2(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
+ __KHASH_TYPE(name, khkey_t, khval_t) \
+ __KHASH_IMPL(name, SCOPE, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
+
+#define KHASH_INIT(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
+ KHASH_INIT2(name, static kh_inline, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal)
+
+/* --- BEGIN OF HASH FUNCTIONS --- */
+
+/*! @function
+ @abstract Integer hash function
+ @param key The integer [khint32_t]
+ @return The hash value [khint_t]
+ */
+#define kh_int_hash_func(key) (khint32_t)(key)
+/*! @function
+ @abstract Integer comparison function
+ */
+#define kh_int_hash_equal(a, b) ((a) == (b))
+/*! @function
+ @abstract 64-bit integer hash function
+ @param key The integer [khint64_t]
+ @return The hash value [khint_t]
+ */
+#define kh_int64_hash_func(key) (khint32_t)((key)>>33^(key)^(key)<<11)
+/*! @function
+ @abstract 64-bit integer comparison function
+ */
+#define kh_int64_hash_equal(a, b) ((a) == (b))
+/*! @function
+ @abstract const char* hash function
+ @param s Pointer to a null terminated string
+ @return The hash value
+ */
+static kh_inline khint_t __ac_X31_hash_string(const char *s)
+{
+ khint_t h = (khint_t)*s;
+ if (h) for (++s ; *s; ++s) h = (h << 5) - h + (khint_t)*s;
+ return h;
+}
+/*! @function
+ @abstract Another interface to const char* hash function
+ @param key Pointer to a null terminated string [const char*]
+ @return The hash value [khint_t]
+ */
+#define kh_str_hash_func(key) __ac_X31_hash_string(key)
+/*! @function
+ @abstract Const char* comparison function
+ */
+#define kh_str_hash_equal(a, b) (strcmp(a, b) == 0)
+
+static kh_inline khint_t __ac_Wang_hash(khint_t key)
+{
+ key += ~(key << 15);
+ key ^= (key >> 10);
+ key += (key << 3);
+ key ^= (key >> 6);
+ key += ~(key << 11);
+ key ^= (key >> 16);
+ return key;
+}
+#define kh_int_hash_func2(k) __ac_Wang_hash((khint_t)key)
+
+/* --- END OF HASH FUNCTIONS --- */
+
+/* Other convenient macros... */
+
+/*!
+ @abstract Type of the hash table.
+ @param name Name of the hash table [symbol]
+ */
+#define khash_t(name) kh_##name##_t
+
+/*! @function
+ @abstract Initiate a hash table.
+ @param name Name of the hash table [symbol]
+ @return Pointer to the hash table [khash_t(name)*]
+ */
+#define kh_init(name) kh_init_##name()
+
+/*! @function
+ @abstract Destroy a hash table.
+ @param name Name of the hash table [symbol]
+ @param h Pointer to the hash table [khash_t(name)*]
+ */
+#define kh_destroy(name, h) kh_destroy_##name(h)
+
+/*! @function
+ @abstract Reset a hash table without deallocating memory.
+ @param name Name of the hash table [symbol]
+ @param h Pointer to the hash table [khash_t(name)*]
+ */
+#define kh_clear(name, h) kh_clear_##name(h)
+
+/*! @function
+ @abstract Resize a hash table.
+ @param name Name of the hash table [symbol]
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param s New size [khint_t]
+ */
+#define kh_resize(name, h, s) kh_resize_##name(h, s)
+
+/*! @function
+ @abstract Insert a key to the hash table.
+ @param name Name of the hash table [symbol]
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param k Key [type of keys]
+ @param r Extra return code: -1 if the operation failed;
+ 0 if the key is present in the hash table;
+ 1 if the bucket is empty (never used); 2 if the element in
+ the bucket has been deleted [int*]
+ @return Iterator to the inserted element [khint_t]
+ */
+#define kh_put(name, h, k, r) kh_put_##name(h, k, r)
+
+/*! @function
+ @abstract Retrieve a key from the hash table.
+ @param name Name of the hash table [symbol]
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param k Key [type of keys]
+ @return Iterator to the found element, or kh_end(h) if the element is absent [khint_t]
+ */
+#define kh_get(name, h, k) kh_get_##name(h, k)
+
+/*! @function
+ @abstract Remove a key from the hash table.
+ @param name Name of the hash table [symbol]
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param k Iterator to the element to be deleted [khint_t]
+ */
+#define kh_del(name, h, k) kh_del_##name(h, k)
+
+/*! @function
+ @abstract Test whether a bucket contains data.
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param x Iterator to the bucket [khint_t]
+ @return 1 if containing data; 0 otherwise [int]
+ */
+#define kh_exist(h, x) (!__ac_iseither((h)->flags, (x)))
+
+/*! @function
+ @abstract Get key given an iterator
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param x Iterator to the bucket [khint_t]
+ @return Key [type of keys]
+ */
+#define kh_key(h, x) ((h)->keys[x])
+
+/*! @function
+ @abstract Get value given an iterator
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param x Iterator to the bucket [khint_t]
+ @return Value [type of values]
+ @discussion For hash sets, calling this results in segfault.
+ */
+#define kh_val(h, x) ((h)->vals[x])
+
+/*! @function
+ @abstract Alias of kh_val()
+ */
+#define kh_value(h, x) ((h)->vals[x])
+
+/*! @function
+ @abstract Get the start iterator
+ @param h Pointer to the hash table [khash_t(name)*]
+ @return The start iterator [khint_t]
+ */
+#define kh_begin(h) (khint_t)(0)
+
+/*! @function
+ @abstract Get the end iterator
+ @param h Pointer to the hash table [khash_t(name)*]
+ @return The end iterator [khint_t]
+ */
+#define kh_end(h) ((h)->n_buckets)
+
+/*! @function
+ @abstract Get the number of elements in the hash table
+ @param h Pointer to the hash table [khash_t(name)*]
+ @return Number of elements in the hash table [khint_t]
+ */
+#define kh_size(h) ((h)->size)
+
+/*! @function
+ @abstract Get the number of buckets in the hash table
+ @param h Pointer to the hash table [khash_t(name)*]
+ @return Number of buckets in the hash table [khint_t]
+ */
+#define kh_n_buckets(h) ((h)->n_buckets)
+
+/*! @function
+ @abstract Iterate over the entries in the hash table
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param kvar Variable to which key will be assigned
+ @param vvar Variable to which value will be assigned
+ @param code Block of code to execute
+ */
+#define kh_foreach(h, kvar, vvar, code) { khint_t __i; \
+ for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
+ if (!kh_exist(h,__i)) continue; \
+ (kvar) = kh_key(h,__i); \
+ (vvar) = kh_val(h,__i); \
+ code; \
+ } }
+
+/*! @function
+ @abstract Iterate over the values in the hash table
+ @param h Pointer to the hash table [khash_t(name)*]
+ @param vvar Variable to which value will be assigned
+ @param code Block of code to execute
+ */
+#define kh_foreach_value(h, vvar, code) { khint_t __i; \
+ for (__i = kh_begin(h); __i != kh_end(h); ++__i) { \
+ if (!kh_exist(h,__i)) continue; \
+ (vvar) = kh_val(h,__i); \
+ code; \
+ } }
+
+/* More convenient interfaces */
+
+/*! @function
+ @abstract Instantiate a hash set containing integer keys
+ @param name Name of the hash table [symbol]
+ */
+#define KHASH_SET_INIT_INT(name) \
+ KHASH_INIT(name, khint32_t, char, 0, kh_int_hash_func, kh_int_hash_equal)
+
+/*! @function
+ @abstract Instantiate a hash map containing integer keys
+ @param name Name of the hash table [symbol]
+ @param khval_t Type of values [type]
+ */
+#define KHASH_MAP_INIT_INT(name, khval_t) \
+ KHASH_INIT(name, khint32_t, khval_t, 1, kh_int_hash_func, kh_int_hash_equal)
+
+/*! @function
+ @abstract Instantiate a hash map containing 64-bit integer keys
+ @param name Name of the hash table [symbol]
+ */
+#define KHASH_SET_INIT_INT64(name) \
+ KHASH_INIT(name, khint64_t, char, 0, kh_int64_hash_func, kh_int64_hash_equal)
+
+/*! @function
+ @abstract Instantiate a hash map containing 64-bit integer keys
+ @param name Name of the hash table [symbol]
+ @param khval_t Type of values [type]
+ */
+#define KHASH_MAP_INIT_INT64(name, khval_t) \
+ KHASH_INIT(name, khint64_t, khval_t, 1, kh_int64_hash_func, kh_int64_hash_equal)
+
+typedef const char *kh_cstr_t;
+/*! @function
+ @abstract Instantiate a hash map containing const char* keys
+ @param name Name of the hash table [symbol]
+ */
+#define KHASH_SET_INIT_STR(name) \
+ KHASH_INIT(name, kh_cstr_t, char, 0, kh_str_hash_func, kh_str_hash_equal)
+
+/*! @function
+ @abstract Instantiate a hash map containing const char* keys
+ @param name Name of the hash table [symbol]
+ @param khval_t Type of values [type]
+ */
+#define KHASH_MAP_INIT_STR(name, khval_t) \
+ KHASH_INIT(name, kh_cstr_t, khval_t, 1, kh_str_hash_func, kh_str_hash_equal)
+
+#endif /* __AC_KHASH_H */
diff --git a/src/util/map.h b/src/util/map.h
new file mode 100644
index 0000000..c101e46
--- /dev/null
+++ b/src/util/map.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_map_h__
+#define INCLUDE_map_h__
+
+#include "git2_util.h"
+
+
+/* p_mmap() prot values */
+#define GIT_PROT_NONE 0x0
+#define GIT_PROT_READ 0x1
+#define GIT_PROT_WRITE 0x2
+#define GIT_PROT_EXEC 0x4
+
+/* git__mmmap() flags values */
+#define GIT_MAP_FILE 0
+#define GIT_MAP_SHARED 1
+#define GIT_MAP_PRIVATE 2
+#define GIT_MAP_TYPE 0xf
+#define GIT_MAP_FIXED 0x10
+
+#ifdef __amigaos4__
+#define MAP_FAILED 0
+#endif
+
+typedef struct { /* memory mapped buffer */
+ void *data; /* data bytes */
+ size_t len; /* data length */
+#ifdef GIT_WIN32
+ HANDLE fmh; /* file mapping handle */
+#endif
+} git_map;
+
+#define GIT_MMAP_VALIDATE(out, len, prot, flags) do { \
+ GIT_ASSERT(out != NULL && len > 0); \
+ GIT_ASSERT((prot & GIT_PROT_WRITE) || (prot & GIT_PROT_READ)); \
+ GIT_ASSERT((flags & GIT_MAP_FIXED) == 0); } while (0)
+
+extern int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, off64_t offset);
+extern int p_munmap(git_map *map);
+
+#endif
diff --git a/src/util/net.c b/src/util/net.c
new file mode 100644
index 0000000..afd52ce
--- /dev/null
+++ b/src/util/net.c
@@ -0,0 +1,1154 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "net.h"
+
+#include <ctype.h>
+
+#include "posix.h"
+#include "str.h"
+#include "http_parser.h"
+#include "runtime.h"
+
+#define DEFAULT_PORT_HTTP "80"
+#define DEFAULT_PORT_HTTPS "443"
+#define DEFAULT_PORT_GIT "9418"
+#define DEFAULT_PORT_SSH "22"
+
+#define GIT_NET_URL_PARSER_INIT { 0 }
+
+typedef struct {
+ int hierarchical : 1;
+
+ const char *scheme;
+ const char *user;
+ const char *password;
+ const char *host;
+ const char *port;
+ const char *path;
+ const char *query;
+ const char *fragment;
+
+ size_t scheme_len;
+ size_t user_len;
+ size_t password_len;
+ size_t host_len;
+ size_t port_len;
+ size_t path_len;
+ size_t query_len;
+ size_t fragment_len;
+} git_net_url_parser;
+
+bool git_net_hostname_matches_cert(
+ const char *hostname,
+ const char *pattern)
+{
+ for (;;) {
+ char c = git__tolower(*pattern++);
+
+ if (c == '\0')
+ return *hostname ? false : true;
+
+ if (c == '*') {
+ c = *pattern;
+
+ /* '*' at the end matches everything left */
+ if (c == '\0')
+ return true;
+
+ /*
+ * We've found a pattern, so move towards the
+ * next matching char. The '.' is handled
+ * specially because wildcards aren't allowed
+ * to cross subdomains.
+ */
+ while(*hostname) {
+ char h = git__tolower(*hostname);
+
+ if (h == c)
+ return git_net_hostname_matches_cert(hostname++, pattern);
+ else if (h == '.')
+ return git_net_hostname_matches_cert(hostname, pattern);
+
+ hostname++;
+ }
+
+ return false;
+ }
+
+ if (c != git__tolower(*hostname++))
+ return false;
+ }
+
+ return false;
+}
+
+#define is_valid_scheme_char(c) \
+ (((c) >= 'a' && (c) <= 'z') || \
+ ((c) >= 'A' && (c) <= 'Z') || \
+ ((c) >= '0' && (c) <= '9') || \
+ (c) == '+' || (c) == '-' || (c) == '.')
+
+bool git_net_str_is_url(const char *str)
+{
+ const char *c;
+
+ for (c = str; *c; c++) {
+ if (*c == ':' && *(c+1) == '/' && *(c+2) == '/')
+ return true;
+
+ if (!is_valid_scheme_char(*c))
+ break;
+ }
+
+ return false;
+}
+
+static const char *default_port_for_scheme(const char *scheme)
+{
+ if (strcmp(scheme, "http") == 0)
+ return DEFAULT_PORT_HTTP;
+ else if (strcmp(scheme, "https") == 0)
+ return DEFAULT_PORT_HTTPS;
+ else if (strcmp(scheme, "git") == 0)
+ return DEFAULT_PORT_GIT;
+ else if (strcmp(scheme, "ssh") == 0 ||
+ strcmp(scheme, "ssh+git") == 0 ||
+ strcmp(scheme, "git+ssh") == 0)
+ return DEFAULT_PORT_SSH;
+
+ return NULL;
+}
+
+static bool is_ssh_scheme(const char *scheme, size_t scheme_len)
+{
+ if (!scheme_len)
+ return false;
+
+ return strncasecmp(scheme, "ssh", scheme_len) == 0 ||
+ strncasecmp(scheme, "ssh+git", scheme_len) == 0 ||
+ strncasecmp(scheme, "git+ssh", scheme_len) == 0;
+}
+
+int git_net_url_dup(git_net_url *out, git_net_url *in)
+{
+ if (in->scheme) {
+ out->scheme = git__strdup(in->scheme);
+ GIT_ERROR_CHECK_ALLOC(out->scheme);
+ }
+
+ if (in->host) {
+ out->host = git__strdup(in->host);
+ GIT_ERROR_CHECK_ALLOC(out->host);
+ }
+
+ if (in->port) {
+ out->port = git__strdup(in->port);
+ GIT_ERROR_CHECK_ALLOC(out->port);
+ }
+
+ if (in->path) {
+ out->path = git__strdup(in->path);
+ GIT_ERROR_CHECK_ALLOC(out->path);
+ }
+
+ if (in->query) {
+ out->query = git__strdup(in->query);
+ GIT_ERROR_CHECK_ALLOC(out->query);
+ }
+
+ if (in->username) {
+ out->username = git__strdup(in->username);
+ GIT_ERROR_CHECK_ALLOC(out->username);
+ }
+
+ if (in->password) {
+ out->password = git__strdup(in->password);
+ GIT_ERROR_CHECK_ALLOC(out->password);
+ }
+
+ return 0;
+}
+
+static int url_invalid(const char *message)
+{
+ git_error_set(GIT_ERROR_NET, "invalid url: %s", message);
+ return GIT_EINVALIDSPEC;
+}
+
+static int url_parse_authority(
+ git_net_url_parser *parser,
+ const char *authority,
+ size_t len)
+{
+ const char *c, *hostport_end, *host_end = NULL,
+ *userpass_end, *user_end = NULL;
+
+ enum {
+ HOSTPORT, HOST, IPV6, HOST_END, USERPASS, USER
+ } state = HOSTPORT;
+
+ if (len == 0)
+ return 0;
+
+ /*
+ * walk the authority backwards so that we can parse google code's
+ * ssh urls that are not rfc compliant and allow @ in the username
+ */
+ for (hostport_end = authority + len, c = hostport_end - 1;
+ c >= authority && !user_end;
+ c--) {
+ switch (state) {
+ case HOSTPORT:
+ if (*c == ':') {
+ parser->port = c + 1;
+ parser->port_len = hostport_end - parser->port;
+ host_end = c;
+ state = HOST;
+ break;
+ }
+
+ /*
+ * if we've only seen digits then we don't know
+ * if we're parsing just a host or a host and port.
+ * if we see a non-digit, then we're in a host,
+ * otherwise, fall through to possibly match the
+ * "@" (user/host separator).
+ */
+
+ if (*c < '0' || *c > '9') {
+ host_end = hostport_end;
+ state = HOST;
+ }
+
+ /* fall through */
+
+ case HOST:
+ if (*c == ']' && host_end == c + 1) {
+ host_end = c;
+ state = IPV6;
+ }
+
+ else if (*c == '@') {
+ parser->host = c + 1;
+ parser->host_len = host_end ?
+ host_end - parser->host :
+ hostport_end - parser->host;
+ userpass_end = c;
+ state = USERPASS;
+ }
+
+ else if (*c == '[' || *c == ']' || *c == ':') {
+ return url_invalid("malformed hostname");
+ }
+
+ break;
+
+ case IPV6:
+ if (*c == '[') {
+ parser->host = c + 1;
+ parser->host_len = host_end - parser->host;
+ state = HOST_END;
+ }
+
+ else if ((*c < '0' || *c > '9') &&
+ (*c < 'a' || *c > 'f') &&
+ (*c < 'A' || *c > 'F') &&
+ (*c != ':')) {
+ return url_invalid("malformed hostname");
+ }
+
+ break;
+
+ case HOST_END:
+ if (*c == '@') {
+ userpass_end = c;
+ state = USERPASS;
+ break;
+ }
+
+ return url_invalid("malformed hostname");
+
+ case USERPASS:
+ if (*c == '@' &&
+ !is_ssh_scheme(parser->scheme, parser->scheme_len))
+ return url_invalid("malformed hostname");
+
+ if (*c == ':') {
+ parser->password = c + 1;
+ parser->password_len = userpass_end - parser->password;
+ user_end = c;
+ state = USER;
+ break;
+ }
+
+ break;
+
+ default:
+ GIT_ASSERT(!"unhandled state");
+ }
+ }
+
+ switch (state) {
+ case HOSTPORT:
+ parser->host = authority;
+ parser->host_len = (hostport_end - parser->host);
+ break;
+ case HOST:
+ parser->host = authority;
+ parser->host_len = (host_end - parser->host);
+ break;
+ case IPV6:
+ return url_invalid("malformed hostname");
+ case HOST_END:
+ break;
+ case USERPASS:
+ parser->user = authority;
+ parser->user_len = (userpass_end - parser->user);
+ break;
+ case USER:
+ parser->user = authority;
+ parser->user_len = (user_end - parser->user);
+ break;
+ default:
+ GIT_ASSERT(!"unhandled state");
+ }
+
+ return 0;
+}
+
+static int url_parse_path(
+ git_net_url_parser *parser,
+ const char *path,
+ size_t len)
+{
+ const char *c, *end;
+
+ enum { PATH, QUERY, FRAGMENT } state = PATH;
+
+ parser->path = path;
+ end = path + len;
+
+ for (c = path; c < end; c++) {
+ switch (state) {
+ case PATH:
+ switch (*c) {
+ case '?':
+ parser->path_len = (c - parser->path);
+ parser->query = c + 1;
+ state = QUERY;
+ break;
+ case '#':
+ parser->path_len = (c - parser->path);
+ parser->fragment = c + 1;
+ state = FRAGMENT;
+ break;
+ }
+ break;
+
+ case QUERY:
+ if (*c == '#') {
+ parser->query_len = (c - parser->query);
+ parser->fragment = c + 1;
+ state = FRAGMENT;
+ }
+ break;
+
+ case FRAGMENT:
+ break;
+
+ default:
+ GIT_ASSERT(!"unhandled state");
+ }
+ }
+
+ switch (state) {
+ case PATH:
+ parser->path_len = (c - parser->path);
+ break;
+ case QUERY:
+ parser->query_len = (c - parser->query);
+ break;
+ case FRAGMENT:
+ parser->fragment_len = (c - parser->fragment);
+ break;
+ }
+
+ return 0;
+}
+
+static int url_parse_finalize(git_net_url *url, git_net_url_parser *parser)
+{
+ git_str scheme = GIT_STR_INIT, user = GIT_STR_INIT,
+ password = GIT_STR_INIT, host = GIT_STR_INIT,
+ port = GIT_STR_INIT, path = GIT_STR_INIT,
+ query = GIT_STR_INIT, fragment = GIT_STR_INIT;
+ const char *default_port;
+ int error = 0;
+
+ if (parser->scheme_len) {
+ if ((error = git_str_put(&scheme, parser->scheme, parser->scheme_len)) < 0)
+ goto done;
+
+ git__strntolower(scheme.ptr, scheme.size);
+ }
+
+ if (parser->user_len &&
+ (error = git_str_decode_percent(&user, parser->user, parser->user_len)) < 0)
+ goto done;
+
+ if (parser->password_len &&
+ (error = git_str_decode_percent(&password, parser->password, parser->password_len)) < 0)
+ goto done;
+
+ if (parser->host_len &&
+ (error = git_str_decode_percent(&host, parser->host, parser->host_len)) < 0)
+ goto done;
+
+ if (parser->port_len)
+ error = git_str_put(&port, parser->port, parser->port_len);
+ else if (parser->scheme_len && (default_port = default_port_for_scheme(scheme.ptr)) != NULL)
+ error = git_str_puts(&port, default_port);
+
+ if (error < 0)
+ goto done;
+
+ if (parser->path_len)
+ error = git_str_put(&path, parser->path, parser->path_len);
+ else if (parser->hierarchical)
+ error = git_str_puts(&path, "/");
+
+ if (error < 0)
+ goto done;
+
+ if (parser->query_len &&
+ (error = git_str_decode_percent(&query, parser->query, parser->query_len)) < 0)
+ goto done;
+
+ if (parser->fragment_len &&
+ (error = git_str_decode_percent(&fragment, parser->fragment, parser->fragment_len)) < 0)
+ goto done;
+
+ url->scheme = git_str_detach(&scheme);
+ url->host = git_str_detach(&host);
+ url->port = git_str_detach(&port);
+ url->path = git_str_detach(&path);
+ url->query = git_str_detach(&query);
+ url->fragment = git_str_detach(&fragment);
+ url->username = git_str_detach(&user);
+ url->password = git_str_detach(&password);
+
+ error = 0;
+
+done:
+ git_str_dispose(&scheme);
+ git_str_dispose(&user);
+ git_str_dispose(&password);
+ git_str_dispose(&host);
+ git_str_dispose(&port);
+ git_str_dispose(&path);
+ git_str_dispose(&query);
+ git_str_dispose(&fragment);
+
+ return error;
+}
+
+int git_net_url_parse(git_net_url *url, const char *given)
+{
+ git_net_url_parser parser = GIT_NET_URL_PARSER_INIT;
+ const char *c, *authority, *path;
+ size_t authority_len = 0, path_len = 0;
+ int error = 0;
+
+ enum {
+ SCHEME_START, SCHEME,
+ AUTHORITY_START, AUTHORITY,
+ PATH_START, PATH
+ } state = SCHEME_START;
+
+ memset(url, 0, sizeof(git_net_url));
+
+ for (c = given; *c; c++) {
+ switch (state) {
+ case SCHEME_START:
+ parser.scheme = c;
+ state = SCHEME;
+
+ /* fall through */
+
+ case SCHEME:
+ if (*c == ':') {
+ parser.scheme_len = (c - parser.scheme);
+
+ if (parser.scheme_len &&
+ *(c+1) == '/' && *(c+2) == '/') {
+ c += 2;
+ parser.hierarchical = 1;
+ state = AUTHORITY_START;
+ } else {
+ state = PATH_START;
+ }
+ } else if (!is_valid_scheme_char(*c)) {
+ /*
+ * an illegal scheme character means that we
+ * were just given a relative path
+ */
+ path = given;
+ state = PATH;
+ break;
+ }
+ break;
+
+ case AUTHORITY_START:
+ authority = c;
+ state = AUTHORITY;
+
+ /* fall through */
+ case AUTHORITY:
+ if (*c != '/')
+ break;
+
+ authority_len = (c - authority);
+
+ /* fall through */
+ case PATH_START:
+ path = c;
+ state = PATH;
+ break;
+
+ case PATH:
+ break;
+
+ default:
+ GIT_ASSERT(!"unhandled state");
+ }
+ }
+
+ switch (state) {
+ case SCHEME:
+ /*
+ * if we never saw a ':' then we were given a relative
+ * path, not a bare scheme
+ */
+ path = given;
+ path_len = (c - path);
+ break;
+ case AUTHORITY_START:
+ break;
+ case AUTHORITY:
+ authority_len = (c - authority);
+ break;
+ case PATH_START:
+ break;
+ case PATH:
+ path_len = (c - path);
+ break;
+ default:
+ GIT_ASSERT(!"unhandled state");
+ }
+
+ if (authority_len &&
+ (error = url_parse_authority(&parser, authority, authority_len)) < 0)
+ goto done;
+
+ if (path_len &&
+ (error = url_parse_path(&parser, path, path_len)) < 0)
+ goto done;
+
+ error = url_parse_finalize(url, &parser);
+
+done:
+ return error;
+}
+
+int git_net_url_parse_http(
+ git_net_url *url,
+ const char *given)
+{
+ git_net_url_parser parser = GIT_NET_URL_PARSER_INIT;
+ const char *c, *authority, *path = NULL;
+ size_t authority_len = 0, path_len = 0;
+ int error;
+
+ /* Hopefully this is a proper URL with a scheme. */
+ if (git_net_str_is_url(given))
+ return git_net_url_parse(url, given);
+
+ memset(url, 0, sizeof(git_net_url));
+
+ /* Without a scheme, we are in the host (authority) section. */
+ for (c = authority = given; *c; c++) {
+ if (!path && *c == '/') {
+ authority_len = (c - authority);
+ path = c;
+ }
+ }
+
+ if (path)
+ path_len = (c - path);
+ else
+ authority_len = (c - authority);
+
+ parser.scheme = "http";
+ parser.scheme_len = 4;
+ parser.hierarchical = 1;
+
+ if (authority_len &&
+ (error = url_parse_authority(&parser, authority, authority_len)) < 0)
+ return error;
+
+ if (path_len &&
+ (error = url_parse_path(&parser, path, path_len)) < 0)
+ return error;
+
+ return url_parse_finalize(url, &parser);
+}
+
+static int scp_invalid(const char *message)
+{
+ git_error_set(GIT_ERROR_NET, "invalid scp-style path: %s", message);
+ return GIT_EINVALIDSPEC;
+}
+
+static bool is_ipv6(const char *str)
+{
+ const char *c;
+ size_t colons = 0;
+
+ if (*str++ != '[')
+ return false;
+
+ for (c = str; *c; c++) {
+ if (*c == ':')
+ colons++;
+
+ if (*c == ']')
+ return (colons > 1);
+
+ if (*c != ':' &&
+ (*c < '0' || *c > '9') &&
+ (*c < 'a' || *c > 'f') &&
+ (*c < 'A' || *c > 'F'))
+ return false;
+ }
+
+ return false;
+}
+
+static bool has_at(const char *str)
+{
+ const char *c;
+
+ for (c = str; *c; c++) {
+ if (*c == '@')
+ return true;
+
+ if (*c == ':')
+ break;
+ }
+
+ return false;
+}
+
+int git_net_url_parse_scp(git_net_url *url, const char *given)
+{
+ const char *default_port = default_port_for_scheme("ssh");
+ const char *c, *user, *host, *port, *path = NULL;
+ size_t user_len = 0, host_len = 0, port_len = 0;
+ unsigned short bracket = 0;
+
+ enum {
+ NONE,
+ USER,
+ HOST_START, HOST, HOST_END,
+ IPV6, IPV6_END,
+ PORT_START, PORT, PORT_END,
+ PATH_START
+ } state = NONE;
+
+ memset(url, 0, sizeof(git_net_url));
+
+ for (c = given; *c && !path; c++) {
+ switch (state) {
+ case NONE:
+ switch (*c) {
+ case '@':
+ return scp_invalid("unexpected '@'");
+ case ':':
+ return scp_invalid("unexpected ':'");
+ case '[':
+ if (is_ipv6(c)) {
+ state = IPV6;
+ host = c;
+ } else if (bracket++ > 1) {
+ return scp_invalid("unexpected '['");
+ }
+ break;
+ default:
+ if (has_at(c)) {
+ state = USER;
+ user = c;
+ } else {
+ state = HOST;
+ host = c;
+ }
+ break;
+ }
+ break;
+
+ case USER:
+ if (*c == '@') {
+ user_len = (c - user);
+ state = HOST_START;
+ }
+ break;
+
+ case HOST_START:
+ state = (*c == '[') ? IPV6 : HOST;
+ host = c;
+ break;
+
+ case HOST:
+ if (*c == ':') {
+ host_len = (c - host);
+ state = bracket ? PORT_START : PATH_START;
+ } else if (*c == ']') {
+ if (bracket-- == 0)
+ return scp_invalid("unexpected ']'");
+
+ host_len = (c - host);
+ state = HOST_END;
+ }
+ break;
+
+ case HOST_END:
+ if (*c != ':')
+ return scp_invalid("unexpected character after hostname");
+ state = PATH_START;
+ break;
+
+ case IPV6:
+ if (*c == ']')
+ state = IPV6_END;
+ break;
+
+ case IPV6_END:
+ if (*c != ':')
+ return scp_invalid("unexpected character after ipv6 address");
+
+ host_len = (c - host);
+ state = bracket ? PORT_START : PATH_START;
+ break;
+
+ case PORT_START:
+ port = c;
+ state = PORT;
+ break;
+
+ case PORT:
+ if (*c == ']') {
+ if (bracket-- == 0)
+ return scp_invalid("unexpected ']'");
+
+ port_len = c - port;
+ state = PORT_END;
+ }
+ break;
+
+ case PORT_END:
+ if (*c != ':')
+ return scp_invalid("unexpected character after ipv6 address");
+
+ state = PATH_START;
+ break;
+
+ case PATH_START:
+ path = c;
+ break;
+
+ default:
+ GIT_ASSERT(!"unhandled state");
+ }
+ }
+
+ if (!path)
+ return scp_invalid("path is required");
+
+ GIT_ERROR_CHECK_ALLOC(url->scheme = git__strdup("ssh"));
+
+ if (user_len)
+ GIT_ERROR_CHECK_ALLOC(url->username = git__strndup(user, user_len));
+
+ GIT_ASSERT(host_len);
+ GIT_ERROR_CHECK_ALLOC(url->host = git__strndup(host, host_len));
+
+ if (port_len)
+ GIT_ERROR_CHECK_ALLOC(url->port = git__strndup(port, port_len));
+ else
+ GIT_ERROR_CHECK_ALLOC(url->port = git__strdup(default_port));
+
+ GIT_ASSERT(path);
+ GIT_ERROR_CHECK_ALLOC(url->path = git__strdup(path));
+
+ return 0;
+}
+
+int git_net_url_parse_standard_or_scp(git_net_url *url, const char *given)
+{
+ return git_net_str_is_url(given) ?
+ git_net_url_parse(url, given) :
+ git_net_url_parse_scp(url, given);
+}
+
+int git_net_url_joinpath(
+ git_net_url *out,
+ git_net_url *one,
+ const char *two)
+{
+ git_str path = GIT_STR_INIT;
+ const char *query;
+ size_t one_len, two_len;
+
+ git_net_url_dispose(out);
+
+ if ((query = strchr(two, '?')) != NULL) {
+ two_len = query - two;
+
+ if (*(++query) != '\0') {
+ out->query = git__strdup(query);
+ GIT_ERROR_CHECK_ALLOC(out->query);
+ }
+ } else {
+ two_len = strlen(two);
+ }
+
+ /* Strip all trailing `/`s from the first path */
+ one_len = one->path ? strlen(one->path) : 0;
+ while (one_len && one->path[one_len - 1] == '/')
+ one_len--;
+
+ /* Strip all leading `/`s from the second path */
+ while (*two == '/') {
+ two++;
+ two_len--;
+ }
+
+ git_str_put(&path, one->path, one_len);
+ git_str_putc(&path, '/');
+ git_str_put(&path, two, two_len);
+
+ if (git_str_oom(&path))
+ return -1;
+
+ out->path = git_str_detach(&path);
+
+ if (one->scheme) {
+ out->scheme = git__strdup(one->scheme);
+ GIT_ERROR_CHECK_ALLOC(out->scheme);
+ }
+
+ if (one->host) {
+ out->host = git__strdup(one->host);
+ GIT_ERROR_CHECK_ALLOC(out->host);
+ }
+
+ if (one->port) {
+ out->port = git__strdup(one->port);
+ GIT_ERROR_CHECK_ALLOC(out->port);
+ }
+
+ if (one->username) {
+ out->username = git__strdup(one->username);
+ GIT_ERROR_CHECK_ALLOC(out->username);
+ }
+
+ if (one->password) {
+ out->password = git__strdup(one->password);
+ GIT_ERROR_CHECK_ALLOC(out->password);
+ }
+
+ return 0;
+}
+
+/*
+ * Some servers strip the query parameters from the Location header
+ * when sending a redirect. Others leave it in place.
+ * Check for both, starting with the stripped case first,
+ * since it appears to be more common.
+ */
+static void remove_service_suffix(
+ git_net_url *url,
+ const char *service_suffix)
+{
+ const char *service_query = strchr(service_suffix, '?');
+ size_t full_suffix_len = strlen(service_suffix);
+ size_t suffix_len = service_query ?
+ (size_t)(service_query - service_suffix) : full_suffix_len;
+ size_t path_len = strlen(url->path);
+ ssize_t truncate = -1;
+
+ /*
+ * Check for a redirect without query parameters,
+ * like "/newloc/info/refs"'
+ */
+ if (suffix_len && path_len >= suffix_len) {
+ size_t suffix_offset = path_len - suffix_len;
+
+ if (git__strncmp(url->path + suffix_offset, service_suffix, suffix_len) == 0 &&
+ (!service_query || git__strcmp(url->query, service_query + 1) == 0)) {
+ truncate = suffix_offset;
+ }
+ }
+
+ /*
+ * If we haven't already found where to truncate to remove the
+ * suffix, check for a redirect with query parameters, like
+ * "/newloc/info/refs?service=git-upload-pack"
+ */
+ if (truncate < 0 && git__suffixcmp(url->path, service_suffix) == 0)
+ truncate = path_len - full_suffix_len;
+
+ /* Ensure we leave a minimum of '/' as the path */
+ if (truncate == 0)
+ truncate++;
+
+ if (truncate > 0) {
+ url->path[truncate] = '\0';
+
+ git__free(url->query);
+ url->query = NULL;
+ }
+}
+
+int git_net_url_apply_redirect(
+ git_net_url *url,
+ const char *redirect_location,
+ bool allow_offsite,
+ const char *service_suffix)
+{
+ git_net_url tmp = GIT_NET_URL_INIT;
+ int error = 0;
+
+ GIT_ASSERT(url);
+ GIT_ASSERT(redirect_location);
+
+ if (redirect_location[0] == '/') {
+ git__free(url->path);
+
+ if ((url->path = git__strdup(redirect_location)) == NULL) {
+ error = -1;
+ goto done;
+ }
+ } else {
+ git_net_url *original = url;
+
+ if ((error = git_net_url_parse(&tmp, redirect_location)) < 0)
+ goto done;
+
+ /* Validate that this is a legal redirection */
+
+ if (original->scheme &&
+ strcmp(original->scheme, tmp.scheme) != 0 &&
+ strcmp(tmp.scheme, "https") != 0) {
+ git_error_set(GIT_ERROR_NET, "cannot redirect from '%s' to '%s'",
+ original->scheme, tmp.scheme);
+
+ error = -1;
+ goto done;
+ }
+
+ if (original->host &&
+ !allow_offsite &&
+ git__strcasecmp(original->host, tmp.host) != 0) {
+ git_error_set(GIT_ERROR_NET, "cannot redirect from '%s' to '%s'",
+ original->host, tmp.host);
+
+ error = -1;
+ goto done;
+ }
+
+ git_net_url_swap(url, &tmp);
+ }
+
+ /* Remove the service suffix if it was given to us */
+ if (service_suffix)
+ remove_service_suffix(url, service_suffix);
+
+done:
+ git_net_url_dispose(&tmp);
+ return error;
+}
+
+bool git_net_url_valid(git_net_url *url)
+{
+ return (url->host && url->port && url->path);
+}
+
+bool git_net_url_is_default_port(git_net_url *url)
+{
+ const char *default_port;
+
+ if (url->scheme && (default_port = default_port_for_scheme(url->scheme)) != NULL)
+ return (strcmp(url->port, default_port) == 0);
+ else
+ return false;
+}
+
+bool git_net_url_is_ipv6(git_net_url *url)
+{
+ return (strchr(url->host, ':') != NULL);
+}
+
+void git_net_url_swap(git_net_url *a, git_net_url *b)
+{
+ git_net_url tmp = GIT_NET_URL_INIT;
+
+ memcpy(&tmp, a, sizeof(git_net_url));
+ memcpy(a, b, sizeof(git_net_url));
+ memcpy(b, &tmp, sizeof(git_net_url));
+}
+
+int git_net_url_fmt(git_str *buf, git_net_url *url)
+{
+ GIT_ASSERT_ARG(url);
+ GIT_ASSERT_ARG(url->scheme);
+ GIT_ASSERT_ARG(url->host);
+
+ git_str_puts(buf, url->scheme);
+ git_str_puts(buf, "://");
+
+ if (url->username) {
+ git_str_puts(buf, url->username);
+
+ if (url->password) {
+ git_str_puts(buf, ":");
+ git_str_puts(buf, url->password);
+ }
+
+ git_str_putc(buf, '@');
+ }
+
+ git_str_puts(buf, url->host);
+
+ if (url->port && !git_net_url_is_default_port(url)) {
+ git_str_putc(buf, ':');
+ git_str_puts(buf, url->port);
+ }
+
+ git_str_puts(buf, url->path ? url->path : "/");
+
+ if (url->query) {
+ git_str_putc(buf, '?');
+ git_str_puts(buf, url->query);
+ }
+
+ return git_str_oom(buf) ? -1 : 0;
+}
+
+int git_net_url_fmt_path(git_str *buf, git_net_url *url)
+{
+ git_str_puts(buf, url->path ? url->path : "/");
+
+ if (url->query) {
+ git_str_putc(buf, '?');
+ git_str_puts(buf, url->query);
+ }
+
+ return git_str_oom(buf) ? -1 : 0;
+}
+
+static bool matches_pattern(
+ git_net_url *url,
+ const char *pattern,
+ size_t pattern_len)
+{
+ const char *domain, *port = NULL, *colon;
+ size_t host_len, domain_len, port_len = 0, wildcard = 0;
+
+ GIT_UNUSED(url);
+ GIT_UNUSED(pattern);
+
+ if (!pattern_len)
+ return false;
+ else if (pattern_len == 1 && pattern[0] == '*')
+ return true;
+ else if (pattern_len > 1 && pattern[0] == '*' && pattern[1] == '.')
+ wildcard = 2;
+ else if (pattern[0] == '.')
+ wildcard = 1;
+
+ domain = pattern + wildcard;
+ domain_len = pattern_len - wildcard;
+
+ if ((colon = memchr(domain, ':', domain_len)) != NULL) {
+ domain_len = colon - domain;
+ port = colon + 1;
+ port_len = pattern_len - wildcard - domain_len - 1;
+ }
+
+ /* A pattern's port *must* match if it's specified */
+ if (port_len && git__strlcmp(url->port, port, port_len) != 0)
+ return false;
+
+ /* No wildcard? Host must match exactly. */
+ if (!wildcard)
+ return !git__strlcmp(url->host, domain, domain_len);
+
+ /* Wildcard: ensure there's (at least) a suffix match */
+ if ((host_len = strlen(url->host)) < domain_len ||
+ memcmp(url->host + (host_len - domain_len), domain, domain_len))
+ return false;
+
+ /* The pattern is *.domain and the host is simply domain */
+ if (host_len == domain_len)
+ return true;
+
+ /* The pattern is *.domain and the host is foo.domain */
+ return (url->host[host_len - domain_len - 1] == '.');
+}
+
+bool git_net_url_matches_pattern(git_net_url *url, const char *pattern)
+{
+ return matches_pattern(url, pattern, strlen(pattern));
+}
+
+bool git_net_url_matches_pattern_list(
+ git_net_url *url,
+ const char *pattern_list)
+{
+ const char *pattern, *pattern_end, *sep;
+
+ for (pattern = pattern_list;
+ pattern && *pattern;
+ pattern = sep ? sep + 1 : NULL) {
+ sep = strchr(pattern, ',');
+ pattern_end = sep ? sep : strchr(pattern, '\0');
+
+ if (matches_pattern(url, pattern, (pattern_end - pattern)))
+ return true;
+ }
+
+ return false;
+}
+
+void git_net_url_dispose(git_net_url *url)
+{
+ if (url->username)
+ git__memzero(url->username, strlen(url->username));
+
+ if (url->password)
+ git__memzero(url->password, strlen(url->password));
+
+ git__free(url->scheme); url->scheme = NULL;
+ git__free(url->host); url->host = NULL;
+ git__free(url->port); url->port = NULL;
+ git__free(url->path); url->path = NULL;
+ git__free(url->query); url->query = NULL;
+ git__free(url->fragment); url->fragment = NULL;
+ git__free(url->username); url->username = NULL;
+ git__free(url->password); url->password = NULL;
+}
diff --git a/src/util/net.h b/src/util/net.h
new file mode 100644
index 0000000..8024956
--- /dev/null
+++ b/src/util/net.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_net_h__
+#define INCLUDE_net_h__
+
+#include "git2_util.h"
+
+/*
+ * Hostname handling
+ */
+
+/*
+ * See if a given hostname matches a certificate name pattern, according
+ * to RFC2818 rules (which specifies HTTP over TLS). Mainly, an asterisk
+ * matches anything, but is limited to a single url component.
+ */
+extern bool git_net_hostname_matches_cert(
+ const char *hostname,
+ const char *pattern);
+
+/*
+ * URL handling
+ */
+
+typedef struct git_net_url {
+ char *scheme;
+ char *host;
+ char *port;
+ char *path;
+ char *query;
+ char *fragment;
+ char *username;
+ char *password;
+} git_net_url;
+
+#define GIT_NET_URL_INIT { NULL }
+
+/** Is a given string a url? */
+extern bool git_net_str_is_url(const char *str);
+
+/** Duplicate a URL */
+extern int git_net_url_dup(git_net_url *out, git_net_url *in);
+
+/** Parses a string containing a URL into a structure. */
+extern int git_net_url_parse(git_net_url *url, const char *str);
+
+/** Parses a string containing an SCP style path into a URL structure. */
+extern int git_net_url_parse_scp(git_net_url *url, const char *str);
+
+/**
+ * Parses a string containing a standard URL or an SCP style path into
+ * a URL structure.
+ */
+extern int git_net_url_parse_standard_or_scp(git_net_url *url, const char *str);
+
+/**
+ * Parses a string containing an HTTP endpoint that may not be a
+ * well-formed URL. For example, "localhost" or "localhost:port".
+ */
+extern int git_net_url_parse_http(
+ git_net_url *url,
+ const char *str);
+
+/** Appends a path and/or query string to the given URL */
+extern int git_net_url_joinpath(
+ git_net_url *out,
+ git_net_url *in,
+ const char *path);
+
+/** Ensures that a URL is minimally valid (contains a host, port and path) */
+extern bool git_net_url_valid(git_net_url *url);
+
+/** Returns true if the URL is on the default port. */
+extern bool git_net_url_is_default_port(git_net_url *url);
+
+/** Returns true if the host portion of the URL is an ipv6 address. */
+extern bool git_net_url_is_ipv6(git_net_url *url);
+
+/* Applies a redirect to the URL with a git-aware service suffix. */
+extern int git_net_url_apply_redirect(
+ git_net_url *url,
+ const char *redirect_location,
+ bool allow_offsite,
+ const char *service_suffix);
+
+/** Swaps the contents of one URL for another. */
+extern void git_net_url_swap(git_net_url *a, git_net_url *b);
+
+/** Places the URL into the given buffer. */
+extern int git_net_url_fmt(git_str *out, git_net_url *url);
+
+/** Place the path and query string into the given buffer. */
+extern int git_net_url_fmt_path(git_str *buf, git_net_url *url);
+
+/** Determines if the url matches given pattern or pattern list */
+extern bool git_net_url_matches_pattern(
+ git_net_url *url,
+ const char *pattern);
+extern bool git_net_url_matches_pattern_list(
+ git_net_url *url,
+ const char *pattern_list);
+
+/** Disposes the contents of the structure. */
+extern void git_net_url_dispose(git_net_url *url);
+
+#endif
diff --git a/src/util/pool.c b/src/util/pool.c
new file mode 100644
index 0000000..16ffa39
--- /dev/null
+++ b/src/util/pool.c
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "pool.h"
+
+#include "posix.h"
+#ifndef GIT_WIN32
+#include <unistd.h>
+#endif
+
+struct git_pool_page {
+ git_pool_page *next;
+ size_t size;
+ size_t avail;
+ GIT_ALIGN(char data[GIT_FLEX_ARRAY], 8);
+};
+
+static void *pool_alloc_page(git_pool *pool, size_t size);
+
+#ifndef GIT_DEBUG_POOL
+
+static size_t system_page_size = 0;
+
+int git_pool_global_init(void)
+{
+ if (git__page_size(&system_page_size) < 0)
+ system_page_size = 4096;
+ /* allow space for malloc overhead */
+ system_page_size -= (2 * sizeof(void *)) + sizeof(git_pool_page);
+ return 0;
+}
+
+int git_pool_init(git_pool *pool, size_t item_size)
+{
+ GIT_ASSERT_ARG(pool);
+ GIT_ASSERT_ARG(item_size >= 1);
+
+ memset(pool, 0, sizeof(git_pool));
+ pool->item_size = item_size;
+ pool->page_size = system_page_size;
+
+ return 0;
+}
+
+void git_pool_clear(git_pool *pool)
+{
+ git_pool_page *scan, *next;
+
+ for (scan = pool->pages; scan != NULL; scan = next) {
+ next = scan->next;
+ git__free(scan);
+ }
+
+ pool->pages = NULL;
+}
+
+static void *pool_alloc_page(git_pool *pool, size_t size)
+{
+ git_pool_page *page;
+ const size_t new_page_size = (size <= pool->page_size) ? pool->page_size : size;
+ size_t alloc_size;
+
+ if (GIT_ADD_SIZET_OVERFLOW(&alloc_size, new_page_size, sizeof(git_pool_page)) ||
+ !(page = git__malloc(alloc_size)))
+ return NULL;
+
+ page->size = new_page_size;
+ page->avail = new_page_size - size;
+ page->next = pool->pages;
+
+ pool->pages = page;
+
+ return page->data;
+}
+
+static void *pool_alloc(git_pool *pool, size_t size)
+{
+ git_pool_page *page = pool->pages;
+ void *ptr = NULL;
+
+ if (!page || page->avail < size)
+ return pool_alloc_page(pool, size);
+
+ ptr = &page->data[page->size - page->avail];
+ page->avail -= size;
+
+ return ptr;
+}
+
+uint32_t git_pool__open_pages(git_pool *pool)
+{
+ uint32_t ct = 0;
+ git_pool_page *scan;
+ for (scan = pool->pages; scan != NULL; scan = scan->next) ct++;
+ return ct;
+}
+
+bool git_pool__ptr_in_pool(git_pool *pool, void *ptr)
+{
+ git_pool_page *scan;
+ for (scan = pool->pages; scan != NULL; scan = scan->next)
+ if ((void *)scan->data <= ptr &&
+ (void *)(((char *)scan->data) + scan->size) > ptr)
+ return true;
+ return false;
+}
+
+#else
+
+int git_pool_global_init(void)
+{
+ return 0;
+}
+
+static int git_pool__ptr_cmp(const void * a, const void * b)
+{
+ if(a > b) {
+ return 1;
+ }
+ if(a < b) {
+ return -1;
+ }
+ else {
+ return 0;
+ }
+}
+
+int git_pool_init(git_pool *pool, size_t item_size)
+{
+ GIT_ASSERT_ARG(pool);
+ GIT_ASSERT_ARG(item_size >= 1);
+
+ memset(pool, 0, sizeof(git_pool));
+ pool->item_size = item_size;
+ pool->page_size = git_pool__system_page_size();
+ git_vector_init(&pool->allocations, 100, git_pool__ptr_cmp);
+
+ return 0;
+}
+
+void git_pool_clear(git_pool *pool)
+{
+ git_vector_free_deep(&pool->allocations);
+}
+
+static void *pool_alloc(git_pool *pool, size_t size) {
+ void *ptr = NULL;
+ if((ptr = git__malloc(size)) == NULL) {
+ return NULL;
+ }
+ git_vector_insert_sorted(&pool->allocations, ptr, NULL);
+ return ptr;
+}
+
+bool git_pool__ptr_in_pool(git_pool *pool, void *ptr)
+{
+ size_t pos;
+ return git_vector_bsearch(&pos, &pool->allocations, ptr) != GIT_ENOTFOUND;
+}
+#endif
+
+void git_pool_swap(git_pool *a, git_pool *b)
+{
+ git_pool temp;
+
+ if (a == b)
+ return;
+
+ memcpy(&temp, a, sizeof(temp));
+ memcpy(a, b, sizeof(temp));
+ memcpy(b, &temp, sizeof(temp));
+}
+
+static size_t alloc_size(git_pool *pool, size_t count)
+{
+ const size_t align = sizeof(void *) - 1;
+
+ if (pool->item_size > 1) {
+ const size_t item_size = (pool->item_size + align) & ~align;
+ return item_size * count;
+ }
+
+ return (count + align) & ~align;
+}
+
+void *git_pool_malloc(git_pool *pool, size_t items)
+{
+ return pool_alloc(pool, alloc_size(pool, items));
+}
+
+void *git_pool_mallocz(git_pool *pool, size_t items)
+{
+ const size_t size = alloc_size(pool, items);
+ void *ptr = pool_alloc(pool, size);
+ if (ptr)
+ memset(ptr, 0x0, size);
+ return ptr;
+}
+
+char *git_pool_strndup(git_pool *pool, const char *str, size_t n)
+{
+ char *ptr = NULL;
+
+ GIT_ASSERT_ARG_WITH_RETVAL(pool, NULL);
+ GIT_ASSERT_ARG_WITH_RETVAL(str, NULL);
+ GIT_ASSERT_ARG_WITH_RETVAL(pool->item_size == sizeof(char), NULL);
+
+ if (n == SIZE_MAX)
+ return NULL;
+
+ if ((ptr = git_pool_malloc(pool, (n + 1))) != NULL) {
+ memcpy(ptr, str, n);
+ ptr[n] = '\0';
+ }
+
+ return ptr;
+}
+
+char *git_pool_strdup(git_pool *pool, const char *str)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(pool, NULL);
+ GIT_ASSERT_ARG_WITH_RETVAL(str, NULL);
+ GIT_ASSERT_ARG_WITH_RETVAL(pool->item_size == sizeof(char), NULL);
+
+ return git_pool_strndup(pool, str, strlen(str));
+}
+
+char *git_pool_strdup_safe(git_pool *pool, const char *str)
+{
+ return str ? git_pool_strdup(pool, str) : NULL;
+}
+
+char *git_pool_strcat(git_pool *pool, const char *a, const char *b)
+{
+ void *ptr;
+ size_t len_a, len_b, total;
+
+ GIT_ASSERT_ARG_WITH_RETVAL(pool, NULL);
+ GIT_ASSERT_ARG_WITH_RETVAL(pool->item_size == sizeof(char), NULL);
+
+ len_a = a ? strlen(a) : 0;
+ len_b = b ? strlen(b) : 0;
+
+ if (GIT_ADD_SIZET_OVERFLOW(&total, len_a, len_b) ||
+ GIT_ADD_SIZET_OVERFLOW(&total, total, 1))
+ return NULL;
+
+ if ((ptr = git_pool_malloc(pool, total)) != NULL) {
+ if (len_a)
+ memcpy(ptr, a, len_a);
+ if (len_b)
+ memcpy(((char *)ptr) + len_a, b, len_b);
+ *(((char *)ptr) + len_a + len_b) = '\0';
+ }
+ return ptr;
+}
diff --git a/src/util/pool.h b/src/util/pool.h
new file mode 100644
index 0000000..0238431
--- /dev/null
+++ b/src/util/pool.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_pool_h__
+#define INCLUDE_pool_h__
+
+#include "git2_util.h"
+
+#include "vector.h"
+
+typedef struct git_pool_page git_pool_page;
+
+#ifndef GIT_DEBUG_POOL
+/**
+ * Chunked allocator.
+ *
+ * A `git_pool` can be used when you want to cheaply allocate
+ * multiple items of the same type and are willing to free them
+ * all together with a single call. The two most common cases
+ * are a set of fixed size items (such as lots of OIDs) or a
+ * bunch of strings.
+ *
+ * Internally, a `git_pool` allocates pages of memory and then
+ * deals out blocks from the trailing unused portion of each page.
+ * The pages guarantee that the number of actual allocations done
+ * will be much smaller than the number of items needed.
+ *
+ * For examples of how to set up a `git_pool` see `git_pool_init`.
+ */
+typedef struct {
+ git_pool_page *pages; /* allocated pages */
+ size_t item_size; /* size of single alloc unit in bytes */
+ size_t page_size; /* size of page in bytes */
+} git_pool;
+
+#define GIT_POOL_INIT { NULL, 0, 0 }
+
+#else
+
+/**
+ * Debug chunked allocator.
+ *
+ * Acts just like `git_pool` but instead of actually pooling allocations it
+ * passes them through to `git__malloc`. This makes it possible to easily debug
+ * systems that use `git_pool` using valgrind.
+ *
+ * In order to track allocations during the lifetime of the pool we use a
+ * `git_vector`. When the pool is deallocated everything in the vector is
+ * freed.
+ *
+ * `API is exactly the same as the standard `git_pool` with one exception.
+ * Since we aren't allocating pages to hand out in chunks we can't easily
+ * implement `git_pool__open_pages`.
+ */
+typedef struct {
+ git_vector allocations;
+ size_t item_size;
+ size_t page_size;
+} git_pool;
+
+#define GIT_POOL_INIT { GIT_VECTOR_INIT, 0, 0 }
+
+#endif
+
+/**
+ * Initialize a pool.
+ *
+ * To allocation strings, use like this:
+ *
+ * git_pool_init(&string_pool, 1);
+ * my_string = git_pool_strdup(&string_pool, your_string);
+ *
+ * To allocate items of fixed size, use like this:
+ *
+ * git_pool_init(&pool, sizeof(item));
+ * my_item = git_pool_malloc(&pool, 1);
+ *
+ * Of course, you can use this in other ways, but those are the
+ * two most common patterns.
+ */
+extern int git_pool_init(git_pool *pool, size_t item_size);
+
+/**
+ * Free all items in pool
+ */
+extern void git_pool_clear(git_pool *pool);
+
+/**
+ * Swap two pools with one another
+ */
+extern void git_pool_swap(git_pool *a, git_pool *b);
+
+/**
+ * Allocate space for one or more items from a pool.
+ */
+extern void *git_pool_malloc(git_pool *pool, size_t items);
+extern void *git_pool_mallocz(git_pool *pool, size_t items);
+
+/**
+ * Allocate space and duplicate string data into it.
+ *
+ * This is allowed only for pools with item_size == sizeof(char)
+ */
+extern char *git_pool_strndup(git_pool *pool, const char *str, size_t n);
+
+/**
+ * Allocate space and duplicate a string into it.
+ *
+ * This is allowed only for pools with item_size == sizeof(char)
+ */
+extern char *git_pool_strdup(git_pool *pool, const char *str);
+
+/**
+ * Allocate space and duplicate a string into it, NULL is no error.
+ *
+ * This is allowed only for pools with item_size == sizeof(char)
+ */
+extern char *git_pool_strdup_safe(git_pool *pool, const char *str);
+
+/**
+ * Allocate space for the concatenation of two strings.
+ *
+ * This is allowed only for pools with item_size == sizeof(char)
+ */
+extern char *git_pool_strcat(git_pool *pool, const char *a, const char *b);
+
+/*
+ * Misc utilities
+ */
+#ifndef GIT_DEBUG_POOL
+extern uint32_t git_pool__open_pages(git_pool *pool);
+#endif
+extern bool git_pool__ptr_in_pool(git_pool *pool, void *ptr);
+
+/**
+ * This function is being called by our global setup routines to
+ * initialize the system pool size.
+ *
+ * @return 0 on success, <0 on failure
+ */
+extern int git_pool_global_init(void);
+
+#endif
diff --git a/src/util/posix.c b/src/util/posix.c
new file mode 100644
index 0000000..cfc0e07
--- /dev/null
+++ b/src/util/posix.c
@@ -0,0 +1,357 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "posix.h"
+
+#include "fs_path.h"
+#include <stdio.h>
+#include <ctype.h>
+
+size_t p_fsync__cnt = 0;
+
+#ifndef GIT_WIN32
+
+#ifdef NO_ADDRINFO
+
+int p_getaddrinfo(
+ const char *host,
+ const char *port,
+ struct addrinfo *hints,
+ struct addrinfo **info)
+{
+ struct addrinfo *ainfo, *ai;
+ int p = 0;
+
+ GIT_UNUSED(hints);
+
+ if ((ainfo = git__malloc(sizeof(struct addrinfo))) == NULL)
+ return -1;
+
+ if ((ainfo->ai_hostent = gethostbyname(host)) == NULL) {
+ git__free(ainfo);
+ return -2;
+ }
+
+ ainfo->ai_servent = getservbyname(port, 0);
+
+ if (ainfo->ai_servent)
+ ainfo->ai_port = ainfo->ai_servent->s_port;
+ else
+ ainfo->ai_port = htons(atol(port));
+
+ memcpy(&ainfo->ai_addr_in.sin_addr,
+ ainfo->ai_hostent->h_addr_list[0],
+ ainfo->ai_hostent->h_length);
+
+ ainfo->ai_protocol = 0;
+ ainfo->ai_socktype = hints->ai_socktype;
+ ainfo->ai_family = ainfo->ai_hostent->h_addrtype;
+ ainfo->ai_addr_in.sin_family = ainfo->ai_family;
+ ainfo->ai_addr_in.sin_port = ainfo->ai_port;
+ ainfo->ai_addr = (struct addrinfo *)&ainfo->ai_addr_in;
+ ainfo->ai_addrlen = sizeof(struct sockaddr_in);
+
+ *info = ainfo;
+
+ if (ainfo->ai_hostent->h_addr_list[1] == NULL) {
+ ainfo->ai_next = NULL;
+ return 0;
+ }
+
+ ai = ainfo;
+
+ for (p = 1; ainfo->ai_hostent->h_addr_list[p] != NULL; p++) {
+ if (!(ai->ai_next = git__malloc(sizeof(struct addrinfo)))) {
+ p_freeaddrinfo(ainfo);
+ return -1;
+ }
+ memcpy(ai->ai_next, ainfo, sizeof(struct addrinfo));
+ memcpy(&ai->ai_next->ai_addr_in.sin_addr,
+ ainfo->ai_hostent->h_addr_list[p],
+ ainfo->ai_hostent->h_length);
+ ai->ai_next->ai_addr = (struct addrinfo *)&ai->ai_next->ai_addr_in;
+ ai = ai->ai_next;
+ }
+
+ ai->ai_next = NULL;
+ return 0;
+}
+
+void p_freeaddrinfo(struct addrinfo *info)
+{
+ struct addrinfo *p, *next;
+
+ p = info;
+
+ while(p != NULL) {
+ next = p->ai_next;
+ git__free(p);
+ p = next;
+ }
+}
+
+const char *p_gai_strerror(int ret)
+{
+ switch(ret) {
+ case -1: return "Out of memory"; break;
+ case -2: return "Address lookup failed"; break;
+ default: return "Unknown error"; break;
+ }
+}
+
+#endif /* NO_ADDRINFO */
+
+int p_open(const char *path, volatile int flags, ...)
+{
+ mode_t mode = 0;
+
+ #ifdef GIT_DEBUG_STRICT_OPEN
+ if (strstr(path, "//") != NULL) {
+ errno = EACCES;
+ return -1;
+ }
+ #endif
+
+ if (flags & O_CREAT) {
+ va_list arg_list;
+
+ va_start(arg_list, flags);
+ mode = (mode_t)va_arg(arg_list, int);
+ va_end(arg_list);
+ }
+
+ return open(path, flags | O_BINARY | O_CLOEXEC, mode);
+}
+
+int p_creat(const char *path, mode_t mode)
+{
+ return open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY | O_CLOEXEC, mode);
+}
+
+int p_getcwd(char *buffer_out, size_t size)
+{
+ char *cwd_buffer;
+
+ GIT_ASSERT_ARG(buffer_out);
+ GIT_ASSERT_ARG(size > 0);
+
+ cwd_buffer = getcwd(buffer_out, size);
+
+ if (cwd_buffer == NULL)
+ return -1;
+
+ git_fs_path_mkposix(buffer_out);
+ git_fs_path_string_to_dir(buffer_out, size); /* append trailing slash */
+
+ return 0;
+}
+
+int p_rename(const char *from, const char *to)
+{
+ if (!link(from, to)) {
+ p_unlink(from);
+ return 0;
+ }
+
+ if (!rename(from, to))
+ return 0;
+
+ return -1;
+}
+
+#endif /* GIT_WIN32 */
+
+ssize_t p_read(git_file fd, void *buf, size_t cnt)
+{
+ char *b = buf;
+
+ if (!git__is_ssizet(cnt)) {
+#ifdef GIT_WIN32
+ SetLastError(ERROR_INVALID_PARAMETER);
+#endif
+ errno = EINVAL;
+ return -1;
+ }
+
+ while (cnt) {
+ ssize_t r;
+#ifdef GIT_WIN32
+ r = read(fd, b, cnt > INT_MAX ? INT_MAX : (unsigned int)cnt);
+#else
+ r = read(fd, b, cnt);
+#endif
+ if (r < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ return -1;
+ }
+ if (!r)
+ break;
+ cnt -= r;
+ b += r;
+ }
+ return (b - (char *)buf);
+}
+
+int p_write(git_file fd, const void *buf, size_t cnt)
+{
+ const char *b = buf;
+
+ while (cnt) {
+ ssize_t r;
+#ifdef GIT_WIN32
+ GIT_ASSERT((size_t)((unsigned int)cnt) == cnt);
+ r = write(fd, b, (unsigned int)cnt);
+#else
+ r = write(fd, b, cnt);
+#endif
+ if (r < 0) {
+ if (errno == EINTR || GIT_ISBLOCKED(errno))
+ continue;
+ return -1;
+ }
+ if (!r) {
+ errno = EPIPE;
+ return -1;
+ }
+ cnt -= r;
+ b += r;
+ }
+ return 0;
+}
+
+#ifdef NO_MMAP
+
+#include "map.h"
+
+int git__page_size(size_t *page_size)
+{
+ /* dummy; here we don't need any alignment anyway */
+ *page_size = 4096;
+ return 0;
+}
+
+int git__mmap_alignment(size_t *alignment)
+{
+ /* dummy; here we don't need any alignment anyway */
+ *alignment = 4096;
+ return 0;
+}
+
+
+int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, off64_t offset)
+{
+ const char *ptr;
+ size_t remaining_len;
+
+ GIT_MMAP_VALIDATE(out, len, prot, flags);
+
+ /* writes cannot be emulated without handling pagefaults since write happens by
+ * writing to mapped memory */
+ if (prot & GIT_PROT_WRITE) {
+ git_error_set(GIT_ERROR_OS, "trying to map %s-writeable",
+ ((flags & GIT_MAP_TYPE) == GIT_MAP_SHARED) ? "shared": "private");
+ return -1;
+ }
+
+ if (!git__is_ssizet(len)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ out->len = 0;
+ out->data = git__malloc(len);
+ GIT_ERROR_CHECK_ALLOC(out->data);
+
+ remaining_len = len;
+ ptr = (const char *)out->data;
+ while (remaining_len > 0) {
+ ssize_t nb;
+ HANDLE_EINTR(nb, p_pread(fd, (void *)ptr, remaining_len, offset));
+ if (nb <= 0) {
+ git_error_set(GIT_ERROR_OS, "mmap emulation failed");
+ git__free(out->data);
+ out->data = NULL;
+ return -1;
+ }
+
+ ptr += nb;
+ offset += nb;
+ remaining_len -= nb;
+ }
+
+ out->len = len;
+ return 0;
+}
+
+int p_munmap(git_map *map)
+{
+ GIT_ASSERT_ARG(map);
+ git__free(map->data);
+
+ /* Initializing will help debug use-after-free */
+ map->len = 0;
+ map->data = NULL;
+
+ return 0;
+}
+
+#endif
+
+#if defined(GIT_IO_POLL) || defined(GIT_IO_WSAPOLL)
+
+/* Handled by posix.h; this test simplifies the final else */
+
+#elif defined(GIT_IO_SELECT)
+
+int p_poll(struct pollfd *fds, unsigned int nfds, int timeout_ms)
+{
+ fd_set read_fds, write_fds, except_fds;
+ struct timeval timeout = { 0, 0 };
+ unsigned int i;
+ int max_fd = -1, ret;
+
+ FD_ZERO(&read_fds);
+ FD_ZERO(&write_fds);
+ FD_ZERO(&except_fds);
+
+ for (i = 0; i < nfds; i++) {
+ if ((fds[i].events & POLLIN))
+ FD_SET(fds[i].fd, &read_fds);
+
+ if ((fds[i].events & POLLOUT))
+ FD_SET(fds[i].fd, &write_fds);
+
+ if ((fds[i].events & POLLPRI))
+ FD_SET(fds[i].fd, &except_fds);
+
+ max_fd = MAX(max_fd, fds[i].fd);
+ }
+
+ if (timeout_ms > 0) {
+ timeout.tv_sec = timeout_ms / 1000;
+ timeout.tv_usec = (timeout_ms % 1000) * 1000;
+ }
+
+ if ((ret = select(max_fd + 1, &read_fds, &write_fds, &except_fds,
+ timeout_ms < 0 ? NULL : &timeout)) < 0)
+ goto done;
+
+ for (i = 0; i < nfds; i++) {
+ fds[i].revents = 0 |
+ FD_ISSET(fds[i].fd, &read_fds) ? POLLIN : 0 |
+ FD_ISSET(fds[i].fd, &write_fds) ? POLLOUT : 0 |
+ FD_ISSET(fds[i].fd, &except_fds) ? POLLPRI : 0;
+ }
+
+done:
+ return ret;
+}
+
+#else
+# error no poll compatible implementation
+#endif
diff --git a/src/util/posix.h b/src/util/posix.h
new file mode 100644
index 0000000..7470745
--- /dev/null
+++ b/src/util/posix.h
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_posix_h__
+#define INCLUDE_posix_h__
+
+#include "git2_util.h"
+
+#include <stdlib.h>
+#include <fcntl.h>
+#include <time.h>
+
+/* stat: file mode type testing macros */
+#ifndef S_IFGITLINK
+#define S_IFGITLINK 0160000
+#define S_ISGITLINK(m) (((m) & S_IFMT) == S_IFGITLINK)
+#endif
+
+#ifndef S_IFLNK
+#define S_IFLNK 0120000
+#undef _S_IFLNK
+#define _S_IFLNK S_IFLNK
+#endif
+
+#ifndef S_IWUSR
+#define S_IWUSR 00200
+#endif
+
+#ifndef S_IXUSR
+#define S_IXUSR 00100
+#endif
+
+#ifndef S_ISLNK
+#define S_ISLNK(m) (((m) & _S_IFMT) == _S_IFLNK)
+#endif
+
+#ifndef S_ISDIR
+#define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR)
+#endif
+
+#ifndef S_ISREG
+#define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG)
+#endif
+
+#ifndef S_ISFIFO
+#define S_ISFIFO(m) (((m) & _S_IFMT) == _S_IFIFO)
+#endif
+
+/* if S_ISGID is not defined, then don't try to set it */
+#ifndef S_ISGID
+#define S_ISGID 0
+#endif
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+#ifndef SOCK_CLOEXEC
+#define SOCK_CLOEXEC 0
+#endif
+
+/* access() mode parameter #defines */
+#ifndef F_OK
+#define F_OK 0 /* existence check */
+#endif
+#ifndef W_OK
+#define W_OK 2 /* write mode check */
+#endif
+#ifndef R_OK
+#define R_OK 4 /* read mode check */
+#endif
+
+/* Determine whether an errno value indicates that a read or write failed
+ * because the descriptor is blocked.
+ */
+#if defined(EWOULDBLOCK)
+#define GIT_ISBLOCKED(e) ((e) == EAGAIN || (e) == EWOULDBLOCK)
+#else
+#define GIT_ISBLOCKED(e) ((e) == EAGAIN)
+#endif
+
+/* define some standard errnos that the runtime may be missing. for example,
+ * mingw lacks EAFNOSUPPORT. */
+#ifndef EAFNOSUPPORT
+#define EAFNOSUPPORT (INT_MAX-1)
+#endif
+
+/* Compiler independent macro to handle signal interrpted system calls */
+#define HANDLE_EINTR(result, x) do { \
+ result = (x); \
+ } while (result == -1 && errno == EINTR);
+
+
+/* Provide a 64-bit size for offsets. */
+
+#if defined(_MSC_VER)
+typedef __int64 off64_t;
+#elif defined(__HAIKU__)
+typedef __haiku_std_int64 off64_t;
+#elif defined(__APPLE__)
+typedef __int64_t off64_t;
+#elif defined(_AIX)
+typedef long long off64_t;
+#else
+typedef int64_t off64_t;
+#endif
+
+typedef int git_file;
+
+/**
+ * Standard POSIX Methods
+ *
+ * All the methods starting with the `p_` prefix are
+ * direct ports of the standard POSIX methods.
+ *
+ * Some of the methods are slightly wrapped to provide
+ * saner defaults. Some of these methods are emulated
+ * in Windows platforms.
+ *
+ * Use your manpages to check the docs on these.
+ */
+
+extern ssize_t p_read(git_file fd, void *buf, size_t cnt);
+extern int p_write(git_file fd, const void *buf, size_t cnt);
+
+extern ssize_t p_pread(int fd, void *data, size_t size, off64_t offset);
+extern ssize_t p_pwrite(int fd, const void *data, size_t size, off64_t offset);
+
+#define p_close(fd) close(fd)
+#define p_umask(m) umask(m)
+
+extern int p_open(const char *path, int flags, ...);
+extern int p_creat(const char *path, mode_t mode);
+extern int p_getcwd(char *buffer_out, size_t size);
+extern int p_rename(const char *from, const char *to);
+
+extern int git__page_size(size_t *page_size);
+extern int git__mmap_alignment(size_t *page_size);
+
+/* The number of times `p_fsync` has been called. Note that this is for
+ * test code only; it it not necessarily thread-safe and should not be
+ * relied upon in production.
+ */
+extern size_t p_fsync__cnt;
+
+/**
+ * Platform-dependent methods
+ */
+#ifdef GIT_WIN32
+# include "win32/posix.h"
+#else
+# include "unix/posix.h"
+#endif
+
+#include "strnlen.h"
+
+#ifdef NO_READDIR_R
+GIT_INLINE(int) p_readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result)
+{
+ GIT_UNUSED(entry);
+ *result = readdir(dirp);
+ return 0;
+}
+#else /* NO_READDIR_R */
+# define p_readdir_r(d,e,r) readdir_r(d,e,r)
+#endif
+
+#ifdef NO_ADDRINFO
+# include <netdb.h>
+struct addrinfo {
+ struct hostent *ai_hostent;
+ struct servent *ai_servent;
+ struct sockaddr_in ai_addr_in;
+ struct sockaddr *ai_addr;
+ size_t ai_addrlen;
+ int ai_family;
+ int ai_socktype;
+ int ai_protocol;
+ long ai_port;
+ struct addrinfo *ai_next;
+};
+
+extern int p_getaddrinfo(const char *host, const char *port,
+ struct addrinfo *hints, struct addrinfo **info);
+extern void p_freeaddrinfo(struct addrinfo *info);
+extern const char *p_gai_strerror(int ret);
+#else
+# define p_getaddrinfo(a, b, c, d) getaddrinfo(a, b, c, d)
+# define p_freeaddrinfo(a) freeaddrinfo(a)
+# define p_gai_strerror(c) gai_strerror(c)
+#endif /* NO_ADDRINFO */
+
+#ifdef GIT_IO_POLL
+# include <poll.h>
+# define p_poll poll
+#elif GIT_IO_WSAPOLL
+# include <winsock2.h>
+# define p_poll WSAPoll
+#else
+# define POLLIN 0x01
+# define POLLPRI 0x02
+# define POLLOUT 0x04
+# define POLLERR 0x08
+# define POLLHUP 0x10
+
+struct pollfd {
+ int fd;
+ short events;
+ short revents;
+};
+
+extern int p_poll(struct pollfd *fds, unsigned int nfds, int timeout);
+#endif
+
+#endif
diff --git a/src/util/pqueue.c b/src/util/pqueue.c
new file mode 100644
index 0000000..3820e99
--- /dev/null
+++ b/src/util/pqueue.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "pqueue.h"
+
+#include "util.h"
+
+#define PQUEUE_LCHILD_OF(I) (((I)<<1)+1)
+#define PQUEUE_RCHILD_OF(I) (((I)<<1)+2)
+#define PQUEUE_PARENT_OF(I) (((I)-1)>>1)
+
+int git_pqueue_init(
+ git_pqueue *pq,
+ uint32_t flags,
+ size_t init_size,
+ git_vector_cmp cmp)
+{
+ int error = git_vector_init(pq, init_size, cmp);
+
+ if (!error) {
+ /* mix in our flags */
+ pq->flags |= flags;
+
+ /* if fixed size heap, pretend vector is exactly init_size elements */
+ if ((flags & GIT_PQUEUE_FIXED_SIZE) && init_size > 0)
+ pq->_alloc_size = init_size;
+ }
+
+ return error;
+}
+
+static void pqueue_up(git_pqueue *pq, size_t el)
+{
+ size_t parent_el = PQUEUE_PARENT_OF(el);
+ void *kid = git_vector_get(pq, el);
+
+ while (el > 0) {
+ void *parent = pq->contents[parent_el];
+
+ if (pq->_cmp(parent, kid) <= 0)
+ break;
+
+ pq->contents[el] = parent;
+
+ el = parent_el;
+ parent_el = PQUEUE_PARENT_OF(el);
+ }
+
+ pq->contents[el] = kid;
+}
+
+static void pqueue_down(git_pqueue *pq, size_t el)
+{
+ void *parent = git_vector_get(pq, el), *kid, *rkid;
+
+ while (1) {
+ size_t kid_el = PQUEUE_LCHILD_OF(el);
+
+ if ((kid = git_vector_get(pq, kid_el)) == NULL)
+ break;
+
+ if ((rkid = git_vector_get(pq, kid_el + 1)) != NULL &&
+ pq->_cmp(kid, rkid) > 0) {
+ kid = rkid;
+ kid_el += 1;
+ }
+
+ if (pq->_cmp(parent, kid) <= 0)
+ break;
+
+ pq->contents[el] = kid;
+ el = kid_el;
+ }
+
+ pq->contents[el] = parent;
+}
+
+int git_pqueue_insert(git_pqueue *pq, void *item)
+{
+ int error = 0;
+
+ /* if heap is full, pop the top element if new one should replace it */
+ if ((pq->flags & GIT_PQUEUE_FIXED_SIZE) != 0 &&
+ pq->length >= pq->_alloc_size)
+ {
+ /* skip this item if below min item in heap or if
+ * we do not have a comparison function */
+ if (!pq->_cmp || pq->_cmp(item, git_vector_get(pq, 0)) <= 0)
+ return 0;
+ /* otherwise remove the min item before inserting new */
+ (void)git_pqueue_pop(pq);
+ }
+
+ if (!(error = git_vector_insert(pq, item)) && pq->_cmp)
+ pqueue_up(pq, pq->length - 1);
+
+ return error;
+}
+
+void *git_pqueue_pop(git_pqueue *pq)
+{
+ void *rval;
+
+ if (!pq->_cmp) {
+ rval = git_vector_last(pq);
+ } else {
+ rval = git_pqueue_get(pq, 0);
+ }
+
+ if (git_pqueue_size(pq) > 1 && pq->_cmp) {
+ /* move last item to top of heap, shrink, and push item down */
+ pq->contents[0] = git_vector_last(pq);
+ git_vector_pop(pq);
+ pqueue_down(pq, 0);
+ } else {
+ /* all we need to do is shrink the heap in this case */
+ git_vector_pop(pq);
+ }
+
+ return rval;
+}
diff --git a/src/util/pqueue.h b/src/util/pqueue.h
new file mode 100644
index 0000000..97232b4
--- /dev/null
+++ b/src/util/pqueue.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_pqueue_h__
+#define INCLUDE_pqueue_h__
+
+#include "git2_util.h"
+
+#include "vector.h"
+
+typedef git_vector git_pqueue;
+
+enum {
+ /* flag meaning: don't grow heap, keep highest values only */
+ GIT_PQUEUE_FIXED_SIZE = (GIT_VECTOR_FLAG_MAX << 1)
+};
+
+/**
+ * Initialize priority queue
+ *
+ * @param pq The priority queue struct to initialize
+ * @param flags Flags (see above) to control queue behavior
+ * @param init_size The initial queue size
+ * @param cmp The entry priority comparison function
+ * @return 0 on success, <0 on error
+ */
+extern int git_pqueue_init(
+ git_pqueue *pq,
+ uint32_t flags,
+ size_t init_size,
+ git_vector_cmp cmp);
+
+#define git_pqueue_free git_vector_free
+#define git_pqueue_clear git_vector_clear
+#define git_pqueue_size git_vector_length
+#define git_pqueue_get git_vector_get
+#define git_pqueue_reverse git_vector_reverse
+
+/**
+ * Insert a new item into the queue
+ *
+ * @param pq The priority queue
+ * @param item Pointer to the item data
+ * @return 0 on success, <0 on failure
+ */
+extern int git_pqueue_insert(git_pqueue *pq, void *item);
+
+/**
+ * Remove the top item in the priority queue
+ *
+ * @param pq The priority queue
+ * @return item from heap on success, NULL if queue is empty
+ */
+extern void *git_pqueue_pop(git_pqueue *pq);
+
+#endif
diff --git a/src/util/rand.c b/src/util/rand.c
new file mode 100644
index 0000000..2ed0605
--- /dev/null
+++ b/src/util/rand.c
@@ -0,0 +1,236 @@
+/* Written in 2018 by David Blackman and Sebastiano Vigna (vigna@acm.org)
+
+To the extent possible under law, the author has dedicated all copyright
+and related and neighboring rights to this software to the public domain
+worldwide. This software is distributed without any warranty.
+
+See <http://creativecommons.org/publicdomain/zero/1.0/>. */
+
+#include "git2_util.h"
+#include "rand.h"
+#include "runtime.h"
+
+#if defined(GIT_RAND_GETENTROPY)
+# include <sys/random.h>
+#endif
+
+#if defined(GIT_WIN32)
+# include <wincrypt.h>
+#endif
+
+static uint64_t state[4];
+static git_mutex state_lock;
+
+typedef union {
+ double f;
+ uint64_t d;
+} bits;
+
+#if defined(GIT_WIN32)
+GIT_INLINE(int) getseed(uint64_t *seed)
+{
+ HCRYPTPROV provider;
+ SYSTEMTIME systemtime;
+ FILETIME filetime, idletime, kerneltime, usertime;
+
+ if (CryptAcquireContext(&provider, 0, 0, PROV_RSA_FULL,
+ CRYPT_VERIFYCONTEXT|CRYPT_SILENT)) {
+ BOOL success = CryptGenRandom(provider, sizeof(uint64_t), (void *)seed);
+ CryptReleaseContext(provider, 0);
+
+ if (success)
+ return 0;
+ }
+
+ GetSystemTime(&systemtime);
+ if (!SystemTimeToFileTime(&systemtime, &filetime)) {
+ git_error_set(GIT_ERROR_OS, "could not get time for random seed");
+ return -1;
+ }
+
+ /* Fall-through: generate a seed from the time and system state */
+ *seed = 0;
+ *seed |= ((uint64_t)filetime.dwLowDateTime << 32);
+ *seed |= ((uint64_t)filetime.dwHighDateTime);
+
+ GetSystemTimes(&idletime, &kerneltime, &usertime);
+
+ *seed ^= ((uint64_t)idletime.dwLowDateTime << 32);
+ *seed ^= ((uint64_t)kerneltime.dwLowDateTime);
+ *seed ^= ((uint64_t)usertime.dwLowDateTime << 32);
+
+ *seed ^= ((uint64_t)idletime.dwHighDateTime);
+ *seed ^= ((uint64_t)kerneltime.dwHighDateTime << 12);
+ *seed ^= ((uint64_t)usertime.dwHighDateTime << 24);
+
+ *seed ^= ((uint64_t)GetCurrentProcessId() << 32);
+ *seed ^= ((uint64_t)GetCurrentThreadId() << 48);
+
+ *seed ^= git_time_monotonic();
+
+ /* Mix in the addresses of some functions and variables */
+ *seed ^= (((uint64_t)((uintptr_t)seed) << 32));
+ *seed ^= (((uint64_t)((uintptr_t)&errno)));
+
+ return 0;
+}
+
+#else
+
+GIT_INLINE(int) getseed(uint64_t *seed)
+{
+ struct timeval tv;
+ double loadavg[3];
+ int fd;
+
+# if defined(GIT_RAND_GETLOADAVG)
+ bits convert;
+# endif
+
+# if defined(GIT_RAND_GETENTROPY)
+ GIT_UNUSED((fd = 0));
+
+ if (getentropy(seed, sizeof(uint64_t)) == 0)
+ return 0;
+# else
+ /*
+ * Try to read from /dev/urandom; most modern systems will have
+ * this, but we may be chrooted, etc, so it's not a fatal error
+ */
+ if ((fd = open("/dev/urandom", O_RDONLY)) >= 0) {
+ ssize_t ret = read(fd, seed, sizeof(uint64_t));
+ close(fd);
+
+ if (ret == (ssize_t)sizeof(uint64_t))
+ return 0;
+ }
+# endif
+
+ /* Fall-through: generate a seed from the time and system state */
+ if (gettimeofday(&tv, NULL) < 0) {
+ git_error_set(GIT_ERROR_OS, "could get time for random seed");
+ return -1;
+ }
+
+ *seed = 0;
+ *seed |= ((uint64_t)tv.tv_usec << 40);
+ *seed |= ((uint64_t)tv.tv_sec);
+
+ *seed ^= ((uint64_t)getpid() << 48);
+ *seed ^= ((uint64_t)getppid() << 32);
+ *seed ^= ((uint64_t)getpgid(0) << 28);
+ *seed ^= ((uint64_t)getsid(0) << 16);
+ *seed ^= ((uint64_t)getuid() << 8);
+ *seed ^= ((uint64_t)getgid());
+
+# if defined(GIT_RAND_GETLOADAVG)
+ getloadavg(loadavg, 3);
+
+ convert.f = loadavg[0]; *seed ^= (convert.d >> 36);
+ convert.f = loadavg[1]; *seed ^= (convert.d);
+ convert.f = loadavg[2]; *seed ^= (convert.d >> 16);
+# else
+ GIT_UNUSED(loadavg[0]);
+# endif
+
+ *seed ^= git_time_monotonic();
+
+ /* Mix in the addresses of some variables */
+ *seed ^= ((uint64_t)((size_t)((void *)seed)) << 32);
+ *seed ^= ((uint64_t)((size_t)((void *)&errno)));
+
+ return 0;
+}
+#endif
+
+static void git_rand_global_shutdown(void)
+{
+ git_mutex_free(&state_lock);
+}
+
+int git_rand_global_init(void)
+{
+ uint64_t seed = 0;
+
+ if (git_mutex_init(&state_lock) < 0 || getseed(&seed) < 0)
+ return -1;
+
+ if (!seed) {
+ git_error_set(GIT_ERROR_INTERNAL, "failed to generate random seed");
+ return -1;
+ }
+
+ git_rand_seed(seed);
+ git_runtime_shutdown_register(git_rand_global_shutdown);
+
+ return 0;
+}
+
+/*
+ * This is splitmix64. xoroshiro256** uses 256 bit seed; this is used
+ * to generate 256 bits of seed from the given 64, per the author's
+ * recommendation.
+ */
+GIT_INLINE(uint64_t) splitmix64(uint64_t *in)
+{
+ uint64_t z;
+
+ *in += 0x9e3779b97f4a7c15;
+
+ z = *in;
+ z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9;
+ z = (z ^ (z >> 27)) * 0x94d049bb133111eb;
+ return z ^ (z >> 31);
+}
+
+void git_rand_seed(uint64_t seed)
+{
+ uint64_t mixer;
+
+ mixer = seed;
+
+ git_mutex_lock(&state_lock);
+ state[0] = splitmix64(&mixer);
+ state[1] = splitmix64(&mixer);
+ state[2] = splitmix64(&mixer);
+ state[3] = splitmix64(&mixer);
+ git_mutex_unlock(&state_lock);
+}
+
+/* This is xoshiro256** 1.0, one of our all-purpose, rock-solid
+ generators. It has excellent (sub-ns) speed, a state (256 bits) that is
+ large enough for any parallel application, and it passes all tests we
+ are aware of.
+
+ For generating just floating-point numbers, xoshiro256+ is even faster.
+
+ The state must be seeded so that it is not everywhere zero. If you have
+ a 64-bit seed, we suggest to seed a splitmix64 generator and use its
+ output to fill s. */
+
+GIT_INLINE(uint64_t) rotl(const uint64_t x, int k) {
+ return (x << k) | (x >> (64 - k));
+}
+
+uint64_t git_rand_next(void) {
+ uint64_t t, result;
+
+ git_mutex_lock(&state_lock);
+
+ result = rotl(state[1] * 5, 7) * 9;
+
+ t = state[1] << 17;
+
+ state[2] ^= state[0];
+ state[3] ^= state[1];
+ state[1] ^= state[2];
+ state[0] ^= state[3];
+
+ state[2] ^= t;
+
+ state[3] = rotl(state[3], 45);
+
+ git_mutex_unlock(&state_lock);
+
+ return result;
+}
diff --git a/src/util/rand.h b/src/util/rand.h
new file mode 100644
index 0000000..fa0619a
--- /dev/null
+++ b/src/util/rand.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_rand_h__
+#define INCLUDE_rand_h__
+
+#include "git2_util.h"
+
+/**
+ * Initialize the random number generation subsystem. This will
+ * seed the random number generator with the system's entropy pool,
+ * if available, and will fall back to the current time and
+ * system information if not.
+ */
+int git_rand_global_init(void);
+
+/**
+ * Seed the pseudo-random number generator. This is not needed to be
+ * called; the PRNG is seeded by `git_rand_global_init`, but it may
+ * be useful for testing. When the same seed is specified, the same
+ * sequence of random numbers from `git_rand_next` is emitted.
+ *
+ * @param seed the seed to use
+ */
+void git_rand_seed(uint64_t seed);
+
+/**
+ * Get the next pseudo-random number in the sequence.
+ *
+ * @return a 64-bit pseudo-random number
+ */
+uint64_t git_rand_next(void);
+
+#endif
diff --git a/src/util/regexp.c b/src/util/regexp.c
new file mode 100644
index 0000000..0870088
--- /dev/null
+++ b/src/util/regexp.c
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "regexp.h"
+
+#if defined(GIT_REGEX_BUILTIN) || defined(GIT_REGEX_PCRE)
+
+int git_regexp_compile(git_regexp *r, const char *pattern, int flags)
+{
+ int erroffset, cflags = 0;
+ const char *error = NULL;
+
+ if (flags & GIT_REGEXP_ICASE)
+ cflags |= PCRE_CASELESS;
+
+ if ((*r = pcre_compile(pattern, cflags, &error, &erroffset, NULL)) == NULL) {
+ git_error_set_str(GIT_ERROR_REGEX, error);
+ return GIT_EINVALIDSPEC;
+ }
+
+ return 0;
+}
+
+void git_regexp_dispose(git_regexp *r)
+{
+ pcre_free(*r);
+ *r = NULL;
+}
+
+int git_regexp_match(const git_regexp *r, const char *string)
+{
+ int error;
+ if ((error = pcre_exec(*r, NULL, string, (int) strlen(string), 0, 0, NULL, 0)) < 0)
+ return (error == PCRE_ERROR_NOMATCH) ? GIT_ENOTFOUND : GIT_EINVALIDSPEC;
+ return 0;
+}
+
+int git_regexp_search(const git_regexp *r, const char *string, size_t nmatches, git_regmatch *matches)
+{
+ int static_ovec[9] = {0}, *ovec;
+ int error;
+ size_t i;
+
+ /* The ovec array always needs to be a multiple of three */
+ if (nmatches <= ARRAY_SIZE(static_ovec) / 3)
+ ovec = static_ovec;
+ else
+ ovec = git__calloc(nmatches * 3, sizeof(*ovec));
+ GIT_ERROR_CHECK_ALLOC(ovec);
+
+ if ((error = pcre_exec(*r, NULL, string, (int) strlen(string), 0, 0, ovec, (int) nmatches * 3)) < 0)
+ goto out;
+
+ if (error == 0)
+ error = (int) nmatches;
+
+ for (i = 0; i < (unsigned int) error; i++) {
+ matches[i].start = (ovec[i * 2] < 0) ? -1 : ovec[i * 2];
+ matches[i].end = (ovec[i * 2 + 1] < 0) ? -1 : ovec[i * 2 + 1];
+ }
+ for (i = (unsigned int) error; i < nmatches; i++)
+ matches[i].start = matches[i].end = -1;
+
+out:
+ if (nmatches > ARRAY_SIZE(static_ovec) / 3)
+ git__free(ovec);
+ if (error < 0)
+ return (error == PCRE_ERROR_NOMATCH) ? GIT_ENOTFOUND : GIT_EINVALIDSPEC;
+ return 0;
+}
+
+#elif defined(GIT_REGEX_PCRE2)
+
+int git_regexp_compile(git_regexp *r, const char *pattern, int flags)
+{
+ unsigned char errmsg[1024];
+ PCRE2_SIZE erroff;
+ int error, cflags = 0;
+
+ if (flags & GIT_REGEXP_ICASE)
+ cflags |= PCRE2_CASELESS;
+
+ if ((*r = pcre2_compile((const unsigned char *) pattern, PCRE2_ZERO_TERMINATED,
+ cflags, &error, &erroff, NULL)) == NULL) {
+ pcre2_get_error_message(error, errmsg, sizeof(errmsg));
+ git_error_set_str(GIT_ERROR_REGEX, (char *) errmsg);
+ return GIT_EINVALIDSPEC;
+ }
+
+ return 0;
+}
+
+void git_regexp_dispose(git_regexp *r)
+{
+ pcre2_code_free(*r);
+ *r = NULL;
+}
+
+int git_regexp_match(const git_regexp *r, const char *string)
+{
+ pcre2_match_data *data;
+ int error;
+
+ data = pcre2_match_data_create(1, NULL);
+ GIT_ERROR_CHECK_ALLOC(data);
+
+ error = pcre2_match(*r, (const unsigned char *) string, strlen(string), 0, 0, data, NULL);
+ pcre2_match_data_free(data);
+ if (error < 0)
+ return (error == PCRE2_ERROR_NOMATCH) ? GIT_ENOTFOUND : GIT_EINVALIDSPEC;
+
+ return 0;
+}
+
+int git_regexp_search(const git_regexp *r, const char *string, size_t nmatches, git_regmatch *matches)
+{
+ pcre2_match_data *data = NULL;
+ PCRE2_SIZE *ovec;
+ int error;
+ size_t i;
+
+ if ((data = pcre2_match_data_create(nmatches, NULL)) == NULL) {
+ git_error_set_oom();
+ goto out;
+ }
+
+ if ((error = pcre2_match(*r, (const unsigned char *) string, strlen(string),
+ 0, 0, data, NULL)) < 0)
+ goto out;
+
+ if (error == 0 || (unsigned int) error > nmatches)
+ error = nmatches;
+ ovec = pcre2_get_ovector_pointer(data);
+
+ for (i = 0; i < (unsigned int) error; i++) {
+ matches[i].start = (ovec[i * 2] == PCRE2_UNSET) ? -1 : (ssize_t) ovec[i * 2];
+ matches[i].end = (ovec[i * 2 + 1] == PCRE2_UNSET) ? -1 : (ssize_t) ovec[i * 2 + 1];
+ }
+ for (i = (unsigned int) error; i < nmatches; i++)
+ matches[i].start = matches[i].end = -1;
+
+out:
+ pcre2_match_data_free(data);
+ if (error < 0)
+ return (error == PCRE2_ERROR_NOMATCH) ? GIT_ENOTFOUND : GIT_EINVALIDSPEC;
+ return 0;
+}
+
+#elif defined(GIT_REGEX_REGCOMP) || defined(GIT_REGEX_REGCOMP_L)
+
+#if defined(GIT_REGEX_REGCOMP_L)
+# include <xlocale.h>
+#endif
+
+int git_regexp_compile(git_regexp *r, const char *pattern, int flags)
+{
+ int cflags = REG_EXTENDED, error;
+ char errmsg[1024];
+
+ if (flags & GIT_REGEXP_ICASE)
+ cflags |= REG_ICASE;
+
+# if defined(GIT_REGEX_REGCOMP)
+ if ((error = regcomp(r, pattern, cflags)) != 0)
+# else
+ if ((error = regcomp_l(r, pattern, cflags, (locale_t) 0)) != 0)
+# endif
+ {
+ regerror(error, r, errmsg, sizeof(errmsg));
+ git_error_set_str(GIT_ERROR_REGEX, errmsg);
+ return GIT_EINVALIDSPEC;
+ }
+
+ return 0;
+}
+
+void git_regexp_dispose(git_regexp *r)
+{
+ regfree(r);
+}
+
+int git_regexp_match(const git_regexp *r, const char *string)
+{
+ int error;
+ if ((error = regexec(r, string, 0, NULL, 0)) != 0)
+ return (error == REG_NOMATCH) ? GIT_ENOTFOUND : GIT_EINVALIDSPEC;
+ return 0;
+}
+
+int git_regexp_search(const git_regexp *r, const char *string, size_t nmatches, git_regmatch *matches)
+{
+ regmatch_t static_m[3], *m;
+ int error;
+ size_t i;
+
+ if (nmatches <= ARRAY_SIZE(static_m))
+ m = static_m;
+ else
+ m = git__calloc(nmatches, sizeof(*m));
+
+ if ((error = regexec(r, string, nmatches, m, 0)) != 0)
+ goto out;
+
+ for (i = 0; i < nmatches; i++) {
+ matches[i].start = (m[i].rm_so < 0) ? -1 : m[i].rm_so;
+ matches[i].end = (m[i].rm_eo < 0) ? -1 : m[i].rm_eo;
+ }
+
+out:
+ if (nmatches > ARRAY_SIZE(static_m))
+ git__free(m);
+ if (error)
+ return (error == REG_NOMATCH) ? GIT_ENOTFOUND : GIT_EINVALIDSPEC;
+ return 0;
+}
+
+#endif
diff --git a/src/util/regexp.h b/src/util/regexp.h
new file mode 100644
index 0000000..d0862b1
--- /dev/null
+++ b/src/util/regexp.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_regexp_h__
+#define INCLUDE_regexp_h__
+
+#include "git2_util.h"
+
+#if defined(GIT_REGEX_BUILTIN) || defined(GIT_REGEX_PCRE)
+# include "pcre.h"
+typedef pcre *git_regexp;
+# define GIT_REGEX_INIT NULL
+#elif defined(GIT_REGEX_PCRE2)
+# define PCRE2_CODE_UNIT_WIDTH 8
+# include <pcre2.h>
+typedef pcre2_code *git_regexp;
+# define GIT_REGEX_INIT NULL
+#elif defined(GIT_REGEX_REGCOMP) || defined(GIT_REGEX_REGCOMP_L)
+# include <regex.h>
+typedef regex_t git_regexp;
+# define GIT_REGEX_INIT { 0 }
+#else
+# error "No regex backend"
+#endif
+
+/** Options supported by @git_regexp_compile. */
+typedef enum {
+ /** Enable case-insensitive matching */
+ GIT_REGEXP_ICASE = (1 << 0)
+} git_regexp_flags_t;
+
+/** Structure containing information about regular expression matching groups */
+typedef struct {
+ /** Start of the given match. -1 if the group didn't match anything */
+ ssize_t start;
+ /** End of the given match. -1 if the group didn't match anything */
+ ssize_t end;
+} git_regmatch;
+
+/**
+ * Compile a regular expression. The compiled expression needs to
+ * be cleaned up afterwards with `git_regexp_dispose`.
+ *
+ * @param r Pointer to the storage where to initialize the regular expression.
+ * @param pattern The pattern that shall be compiled.
+ * @param flags Flags to alter how the pattern shall be handled.
+ * 0 for defaults, otherwise see @git_regexp_flags_t.
+ * @return 0 on success, otherwise a negative return value.
+ */
+int git_regexp_compile(git_regexp *r, const char *pattern, int flags);
+
+/**
+ * Free memory associated with the regular expression
+ *
+ * @param r The regular expression structure to dispose.
+ */
+void git_regexp_dispose(git_regexp *r);
+
+/**
+ * Test whether a given string matches a compiled regular
+ * expression.
+ *
+ * @param r Compiled regular expression.
+ * @param string String to match against the regular expression.
+ * @return 0 if the string matches, a negative error code
+ * otherwise. GIT_ENOTFOUND if no match was found,
+ * GIT_EINVALIDSPEC if the regular expression matching
+ * was invalid.
+ */
+int git_regexp_match(const git_regexp *r, const char *string);
+
+/**
+ * Search for matches inside of a given string.
+ *
+ * Given a regular expression with capturing groups, this
+ * function will populate provided @git_regmatch structures with
+ * offsets for each of the given matches. Non-matching groups
+ * will have start and end values of the respective @git_regmatch
+ * structure set to -1.
+ *
+ * @param r Compiled regular expression.
+ * @param string String to match against the regular expression.
+ * @param nmatches Number of @git_regmatch structures provided by
+ * the user.
+ * @param matches Pointer to an array of @git_regmatch structures.
+ * @return 0 if the string matches, a negative error code
+ * otherwise. GIT_ENOTFOUND if no match was found,
+ * GIT_EINVALIDSPEC if the regular expression matching
+ * was invalid.
+ */
+int git_regexp_search(const git_regexp *r, const char *string, size_t nmatches, git_regmatch *matches);
+
+#endif
diff --git a/src/util/runtime.c b/src/util/runtime.c
new file mode 100644
index 0000000..a7711ff
--- /dev/null
+++ b/src/util/runtime.c
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "git2_util.h"
+#include "runtime.h"
+
+static git_runtime_shutdown_fn shutdown_callback[32];
+static git_atomic32 shutdown_callback_count;
+
+static git_atomic32 init_count;
+
+static int init_common(git_runtime_init_fn init_fns[], size_t cnt)
+{
+ size_t i;
+ int ret;
+
+ /* Initialize subsystems that have global state */
+ for (i = 0; i < cnt; i++) {
+ if ((ret = init_fns[i]()) != 0)
+ break;
+ }
+
+ GIT_MEMORY_BARRIER;
+
+ return ret;
+}
+
+static void shutdown_common(void)
+{
+ git_runtime_shutdown_fn cb;
+ int pos;
+
+ for (pos = git_atomic32_get(&shutdown_callback_count);
+ pos > 0;
+ pos = git_atomic32_dec(&shutdown_callback_count)) {
+ cb = git_atomic_swap(shutdown_callback[pos - 1], NULL);
+
+ if (cb != NULL)
+ cb();
+ }
+}
+
+int git_runtime_shutdown_register(git_runtime_shutdown_fn callback)
+{
+ int count = git_atomic32_inc(&shutdown_callback_count);
+
+ if (count > (int)ARRAY_SIZE(shutdown_callback) || count == 0) {
+ git_error_set(GIT_ERROR_INVALID,
+ "too many shutdown callbacks registered");
+ git_atomic32_dec(&shutdown_callback_count);
+ return -1;
+ }
+
+ shutdown_callback[count - 1] = callback;
+
+ return 0;
+}
+
+#if defined(GIT_THREADS) && defined(GIT_WIN32)
+
+/*
+ * On Win32, we use a spinlock to provide locking semantics. This is
+ * lighter-weight than a proper critical section.
+ */
+static volatile LONG init_spinlock = 0;
+
+GIT_INLINE(int) init_lock(void)
+{
+ while (InterlockedCompareExchange(&init_spinlock, 1, 0)) { Sleep(0); }
+ return 0;
+}
+
+GIT_INLINE(int) init_unlock(void)
+{
+ InterlockedExchange(&init_spinlock, 0);
+ return 0;
+}
+
+#elif defined(GIT_THREADS) && defined(_POSIX_THREADS)
+
+/*
+ * On POSIX, we need to use a proper mutex for locking. We might prefer
+ * a spinlock here, too, but there's no static initializer for a
+ * pthread_spinlock_t.
+ */
+static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+GIT_INLINE(int) init_lock(void)
+{
+ return pthread_mutex_lock(&init_mutex) == 0 ? 0 : -1;
+}
+
+GIT_INLINE(int) init_unlock(void)
+{
+ return pthread_mutex_unlock(&init_mutex) == 0 ? 0 : -1;
+}
+
+#elif defined(GIT_THREADS)
+# error unknown threading model
+#else
+
+# define init_lock() git__noop()
+# define init_unlock() git__noop()
+
+#endif
+
+int git_runtime_init(git_runtime_init_fn init_fns[], size_t cnt)
+{
+ int ret;
+
+ if (init_lock() < 0)
+ return -1;
+
+ /* Only do work on a 0 -> 1 transition of the refcount */
+ if ((ret = git_atomic32_inc(&init_count)) == 1) {
+ if (init_common(init_fns, cnt) < 0)
+ ret = -1;
+ }
+
+ if (init_unlock() < 0)
+ return -1;
+
+ return ret;
+}
+
+int git_runtime_init_count(void)
+{
+ int ret;
+
+ if (init_lock() < 0)
+ return -1;
+
+ ret = git_atomic32_get(&init_count);
+
+ if (init_unlock() < 0)
+ return -1;
+
+ return ret;
+}
+
+int git_runtime_shutdown(void)
+{
+ int ret;
+
+ /* Enter the lock */
+ if (init_lock() < 0)
+ return -1;
+
+ /* Only do work on a 1 -> 0 transition of the refcount */
+ if ((ret = git_atomic32_dec(&init_count)) == 0)
+ shutdown_common();
+
+ /* Exit the lock */
+ if (init_unlock() < 0)
+ return -1;
+
+ return ret;
+}
diff --git a/src/util/runtime.h b/src/util/runtime.h
new file mode 100644
index 0000000..6cbfd60
--- /dev/null
+++ b/src/util/runtime.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_runtime_h__
+#define INCLUDE_runtime_h__
+
+#include "git2_util.h"
+
+typedef int (*git_runtime_init_fn)(void);
+typedef void (*git_runtime_shutdown_fn)(void);
+
+/**
+ * Start up a new runtime. If this is the first time that this
+ * function is called within the context of the current library
+ * or executable, then the given `init_fns` will be invoked. If
+ * it is not the first time, they will be ignored.
+ *
+ * The given initialization functions _may_ register shutdown
+ * handlers using `git_runtime_shutdown_register` to be notified
+ * when the runtime is shutdown.
+ *
+ * @param init_fns The list of initialization functions to call
+ * @param cnt The number of init_fns
+ * @return The number of initializations performed (including this one) or an error
+ */
+int git_runtime_init(git_runtime_init_fn init_fns[], size_t cnt);
+
+/*
+ * Returns the number of initializations active (the number of calls to
+ * `git_runtime_init` minus the number of calls sto `git_runtime_shutdown`).
+ * If 0, the runtime is not currently initialized.
+ *
+ * @return The number of initializations performed or an error
+ */
+int git_runtime_init_count(void);
+
+/**
+ * Shut down the runtime. If this is the last shutdown call,
+ * such that there are no remaining `init` calls, then any
+ * shutdown hooks that have been registered will be invoked.
+ *
+ * The number of outstanding initializations will be returned.
+ * If this number is 0, then the runtime is shutdown.
+ *
+ * @return The number of outstanding initializations (after this one) or an error
+ */
+int git_runtime_shutdown(void);
+
+/**
+ * Register a shutdown handler for this runtime. This should be done
+ * by a function invoked by `git_runtime_init` to ensure that the
+ * appropriate locks are taken.
+ *
+ * @param callback The shutdown handler callback
+ * @return 0 or an error code
+ */
+int git_runtime_shutdown_register(git_runtime_shutdown_fn callback);
+
+#endif
diff --git a/src/util/sortedcache.c b/src/util/sortedcache.c
new file mode 100644
index 0000000..7ff900e
--- /dev/null
+++ b/src/util/sortedcache.c
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "sortedcache.h"
+
+int git_sortedcache_new(
+ git_sortedcache **out,
+ size_t item_path_offset,
+ git_sortedcache_free_item_fn free_item,
+ void *free_item_payload,
+ git_vector_cmp item_cmp,
+ const char *path)
+{
+ git_sortedcache *sc;
+ size_t pathlen, alloclen;
+
+ pathlen = path ? strlen(path) : 0;
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, sizeof(git_sortedcache), pathlen);
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
+ sc = git__calloc(1, alloclen);
+ GIT_ERROR_CHECK_ALLOC(sc);
+
+ if (git_pool_init(&sc->pool, 1) < 0 ||
+ git_vector_init(&sc->items, 4, item_cmp) < 0 ||
+ git_strmap_new(&sc->map) < 0)
+ goto fail;
+
+ if (git_rwlock_init(&sc->lock)) {
+ git_error_set(GIT_ERROR_OS, "failed to initialize lock");
+ goto fail;
+ }
+
+ sc->item_path_offset = item_path_offset;
+ sc->free_item = free_item;
+ sc->free_item_payload = free_item_payload;
+ GIT_REFCOUNT_INC(sc);
+ if (pathlen)
+ memcpy(sc->path, path, pathlen);
+
+ *out = sc;
+ return 0;
+
+fail:
+ git_strmap_free(sc->map);
+ git_vector_free(&sc->items);
+ git_pool_clear(&sc->pool);
+ git__free(sc);
+ return -1;
+}
+
+void git_sortedcache_incref(git_sortedcache *sc)
+{
+ GIT_REFCOUNT_INC(sc);
+}
+
+const char *git_sortedcache_path(git_sortedcache *sc)
+{
+ return sc->path;
+}
+
+static void sortedcache_clear(git_sortedcache *sc)
+{
+ git_strmap_clear(sc->map);
+
+ if (sc->free_item) {
+ size_t i;
+ void *item;
+
+ git_vector_foreach(&sc->items, i, item) {
+ sc->free_item(sc->free_item_payload, item);
+ }
+ }
+
+ git_vector_clear(&sc->items);
+
+ git_pool_clear(&sc->pool);
+}
+
+static void sortedcache_free(git_sortedcache *sc)
+{
+ /* acquire write lock to make sure everyone else is done */
+ if (git_sortedcache_wlock(sc) < 0)
+ return;
+
+ sortedcache_clear(sc);
+ git_vector_free(&sc->items);
+ git_strmap_free(sc->map);
+
+ git_sortedcache_wunlock(sc);
+
+ git_rwlock_free(&sc->lock);
+ git__free(sc);
+}
+
+void git_sortedcache_free(git_sortedcache *sc)
+{
+ if (!sc)
+ return;
+ GIT_REFCOUNT_DEC(sc, sortedcache_free);
+}
+
+static int sortedcache_copy_item(void *payload, void *tgt_item, void *src_item)
+{
+ git_sortedcache *sc = payload;
+ /* path will already have been copied by upsert */
+ memcpy(tgt_item, src_item, sc->item_path_offset);
+ return 0;
+}
+
+/* copy a sorted cache */
+int git_sortedcache_copy(
+ git_sortedcache **out,
+ git_sortedcache *src,
+ bool lock,
+ int (*copy_item)(void *payload, void *tgt_item, void *src_item),
+ void *payload)
+{
+ int error = 0;
+ git_sortedcache *tgt;
+ size_t i;
+ void *src_item, *tgt_item;
+
+ /* just use memcpy if no special copy fn is passed in */
+ if (!copy_item) {
+ copy_item = sortedcache_copy_item;
+ payload = src;
+ }
+
+ if ((error = git_sortedcache_new(
+ &tgt, src->item_path_offset,
+ src->free_item, src->free_item_payload,
+ src->items._cmp, src->path)) < 0)
+ return error;
+
+ if (lock && git_sortedcache_rlock(src) < 0) {
+ git_sortedcache_free(tgt);
+ return -1;
+ }
+
+ git_vector_foreach(&src->items, i, src_item) {
+ char *path = ((char *)src_item) + src->item_path_offset;
+
+ if ((error = git_sortedcache_upsert(&tgt_item, tgt, path)) < 0 ||
+ (error = copy_item(payload, tgt_item, src_item)) < 0)
+ break;
+ }
+
+ if (lock)
+ git_sortedcache_runlock(src);
+ if (error)
+ git_sortedcache_free(tgt);
+
+ *out = !error ? tgt : NULL;
+
+ return error;
+}
+
+/* lock sortedcache while making modifications */
+int git_sortedcache_wlock(git_sortedcache *sc)
+{
+ GIT_UNUSED(sc); /* prevent warning when compiled w/o threads */
+
+ if (git_rwlock_wrlock(&sc->lock) < 0) {
+ git_error_set(GIT_ERROR_OS, "unable to acquire write lock on cache");
+ return -1;
+ }
+ return 0;
+}
+
+/* unlock sorted cache when done with modifications */
+void git_sortedcache_wunlock(git_sortedcache *sc)
+{
+ git_vector_sort(&sc->items);
+ git_rwlock_wrunlock(&sc->lock);
+}
+
+/* lock sortedcache for read */
+int git_sortedcache_rlock(git_sortedcache *sc)
+{
+ GIT_UNUSED(sc); /* prevent warning when compiled w/o threads */
+
+ if (git_rwlock_rdlock(&sc->lock) < 0) {
+ git_error_set(GIT_ERROR_OS, "unable to acquire read lock on cache");
+ return -1;
+ }
+ return 0;
+}
+
+/* unlock sorted cache when done reading */
+void git_sortedcache_runlock(git_sortedcache *sc)
+{
+ GIT_UNUSED(sc); /* prevent warning when compiled w/o threads */
+ git_rwlock_rdunlock(&sc->lock);
+}
+
+/* if the file has changed, lock cache and load file contents into buf;
+ * returns <0 on error, >0 if file has not changed
+ */
+int git_sortedcache_lockandload(git_sortedcache *sc, git_str *buf)
+{
+ int error, fd;
+ struct stat st;
+
+ if ((error = git_sortedcache_wlock(sc)) < 0)
+ return error;
+
+ if ((error = git_futils_filestamp_check(&sc->stamp, sc->path)) <= 0)
+ goto unlock;
+
+ if ((fd = git_futils_open_ro(sc->path)) < 0) {
+ error = fd;
+ goto unlock;
+ }
+
+ if (p_fstat(fd, &st) < 0) {
+ git_error_set(GIT_ERROR_OS, "failed to stat file");
+ error = -1;
+ (void)p_close(fd);
+ goto unlock;
+ }
+
+ if (!git__is_sizet(st.st_size)) {
+ git_error_set(GIT_ERROR_INVALID, "unable to load file larger than size_t");
+ error = -1;
+ (void)p_close(fd);
+ goto unlock;
+ }
+
+ if (buf)
+ error = git_futils_readbuffer_fd(buf, fd, (size_t)st.st_size);
+
+ (void)p_close(fd);
+
+ if (error < 0)
+ goto unlock;
+
+ return 1; /* return 1 -> file needs reload and was successfully loaded */
+
+unlock:
+ git_sortedcache_wunlock(sc);
+ return error;
+}
+
+void git_sortedcache_updated(git_sortedcache *sc)
+{
+ /* update filestamp to latest value */
+ git_futils_filestamp_check(&sc->stamp, sc->path);
+}
+
+/* release all items in sorted cache */
+int git_sortedcache_clear(git_sortedcache *sc, bool wlock)
+{
+ if (wlock && git_sortedcache_wlock(sc) < 0)
+ return -1;
+
+ sortedcache_clear(sc);
+
+ if (wlock)
+ git_sortedcache_wunlock(sc);
+
+ return 0;
+}
+
+/* find and/or insert item, returning pointer to item data */
+int git_sortedcache_upsert(void **out, git_sortedcache *sc, const char *key)
+{
+ size_t keylen, itemlen;
+ int error = 0;
+ char *item_key;
+ void *item;
+
+ if ((item = git_strmap_get(sc->map, key)) != NULL)
+ goto done;
+
+ keylen = strlen(key);
+ itemlen = sc->item_path_offset + keylen + 1;
+ itemlen = (itemlen + 7) & ~7;
+
+ if ((item = git_pool_mallocz(&sc->pool, itemlen)) == NULL) {
+ /* don't use GIT_ERROR_CHECK_ALLOC b/c of lock */
+ error = -1;
+ goto done;
+ }
+
+ /* one strange thing is that even if the vector or hash table insert
+ * fail, there is no way to free the pool item so we just abandon it
+ */
+
+ item_key = ((char *)item) + sc->item_path_offset;
+ memcpy(item_key, key, keylen);
+
+ if ((error = git_strmap_set(sc->map, item_key, item)) < 0)
+ goto done;
+
+ if ((error = git_vector_insert(&sc->items, item)) < 0)
+ git_strmap_delete(sc->map, item_key);
+
+done:
+ if (out)
+ *out = !error ? item : NULL;
+ return error;
+}
+
+/* lookup item by key */
+void *git_sortedcache_lookup(const git_sortedcache *sc, const char *key)
+{
+ return git_strmap_get(sc->map, key);
+}
+
+/* find out how many items are in the cache */
+size_t git_sortedcache_entrycount(const git_sortedcache *sc)
+{
+ return git_vector_length(&sc->items);
+}
+
+/* lookup item by index */
+void *git_sortedcache_entry(git_sortedcache *sc, size_t pos)
+{
+ /* make sure the items are sorted so this gets the correct item */
+ if (!git_vector_is_sorted(&sc->items))
+ git_vector_sort(&sc->items);
+
+ return git_vector_get(&sc->items, pos);
+}
+
+/* helper struct so bsearch callback can know offset + key value for cmp */
+struct sortedcache_magic_key {
+ size_t offset;
+ const char *key;
+};
+
+static int sortedcache_magic_cmp(const void *key, const void *value)
+{
+ const struct sortedcache_magic_key *magic = key;
+ const char *value_key = ((const char *)value) + magic->offset;
+ return strcmp(magic->key, value_key);
+}
+
+/* lookup index of item by key */
+int git_sortedcache_lookup_index(
+ size_t *out, git_sortedcache *sc, const char *key)
+{
+ struct sortedcache_magic_key magic;
+
+ magic.offset = sc->item_path_offset;
+ magic.key = key;
+
+ return git_vector_bsearch2(out, &sc->items, sortedcache_magic_cmp, &magic);
+}
+
+/* remove entry from cache */
+int git_sortedcache_remove(git_sortedcache *sc, size_t pos)
+{
+ char *item;
+
+ /*
+ * Because of pool allocation, this can't actually remove the item,
+ * but we can remove it from the items vector and the hash table.
+ */
+
+ if ((item = git_vector_get(&sc->items, pos)) == NULL) {
+ git_error_set(GIT_ERROR_INVALID, "removing item out of range");
+ return GIT_ENOTFOUND;
+ }
+
+ (void)git_vector_remove(&sc->items, pos);
+
+ git_strmap_delete(sc->map, item + sc->item_path_offset);
+
+ if (sc->free_item)
+ sc->free_item(sc->free_item_payload, item);
+
+ return 0;
+}
+
diff --git a/src/util/sortedcache.h b/src/util/sortedcache.h
new file mode 100644
index 0000000..3eee465
--- /dev/null
+++ b/src/util/sortedcache.h
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_sorted_cache_h__
+#define INCLUDE_sorted_cache_h__
+
+#include "git2_util.h"
+
+#include "util.h"
+#include "futils.h"
+#include "vector.h"
+#include "thread.h"
+#include "pool.h"
+#include "strmap.h"
+
+#include <stddef.h>
+
+/*
+ * The purpose of this data structure is to cache the parsed contents of a
+ * file (a.k.a. the backing file) where each item in the file can be
+ * identified by a key string and you want to both look them up by name
+ * and traverse them in sorted order. Each item is assumed to itself end
+ * in a GIT_FLEX_ARRAY.
+ */
+
+typedef void (*git_sortedcache_free_item_fn)(void *payload, void *item);
+
+typedef struct {
+ git_refcount rc;
+ git_rwlock lock;
+ size_t item_path_offset;
+ git_sortedcache_free_item_fn free_item;
+ void *free_item_payload;
+ git_pool pool;
+ git_vector items;
+ git_strmap *map;
+ git_futils_filestamp stamp;
+ char path[GIT_FLEX_ARRAY];
+} git_sortedcache;
+
+/* Create a new sortedcache
+ *
+ * Even though every sortedcache stores items with a GIT_FLEX_ARRAY at
+ * the end containing their key string, you have to provide the item_cmp
+ * sorting function because the sorting function doesn't get a payload
+ * and therefore can't know the offset to the item key string. :-(
+ *
+ * @param out The allocated git_sortedcache
+ * @param item_path_offset Offset to the GIT_FLEX_ARRAY item key in the
+ * struct - use offsetof(struct mine, key-field) to get this
+ * @param free_item Optional callback to free each item
+ * @param free_item_payload Optional payload passed to free_item callback
+ * @param item_cmp Compare the keys of two items
+ * @param path The path to the backing store file for this cache; this
+ * may be NULL. The cache makes it easy to load this and check
+ * if it has been modified since the last load and/or write.
+ */
+GIT_WARN_UNUSED_RESULT int git_sortedcache_new(
+ git_sortedcache **out,
+ size_t item_path_offset, /* use offsetof(struct, path-field) macro */
+ git_sortedcache_free_item_fn free_item,
+ void *free_item_payload,
+ git_vector_cmp item_cmp,
+ const char *path);
+
+/* Copy a sorted cache
+ *
+ * - `copy_item` can be NULL to just use memcpy
+ * - if `lock`, grabs read lock on `src` during copy and releases after
+ */
+GIT_WARN_UNUSED_RESULT int git_sortedcache_copy(
+ git_sortedcache **out,
+ git_sortedcache *src,
+ bool lock,
+ int (*copy_item)(void *payload, void *tgt_item, void *src_item),
+ void *payload);
+
+/* Free sorted cache (first calling `free_item` callbacks)
+ *
+ * Don't call on a locked collection - it may acquire a write lock
+ */
+void git_sortedcache_free(git_sortedcache *sc);
+
+/* Increment reference count - balance with call to free */
+void git_sortedcache_incref(git_sortedcache *sc);
+
+/* Get the pathname associated with this cache at creation time */
+const char *git_sortedcache_path(git_sortedcache *sc);
+
+/*
+ * CACHE WRITE FUNCTIONS
+ *
+ * The following functions require you to have a writer lock to make the
+ * modification. Some of the functions take a `wlock` parameter and
+ * will optionally lock and unlock for you if that is passed as true.
+ *
+ */
+
+/* Lock sortedcache for write */
+GIT_WARN_UNUSED_RESULT int git_sortedcache_wlock(git_sortedcache *sc);
+
+/* Unlock sorted cache when done with write */
+void git_sortedcache_wunlock(git_sortedcache *sc);
+
+/* Lock cache and load backing file into a buffer.
+ *
+ * This grabs a write lock on the cache then looks at the modification
+ * time and size of the file on disk.
+ *
+ * If the file appears to have changed, this loads the file contents into
+ * the buffer and returns a positive value leaving the cache locked - the
+ * caller should parse the file content, update the cache as needed, then
+ * release the lock. NOTE: In this case, the caller MUST unlock the cache.
+ *
+ * If the file appears to be unchanged, then this automatically releases
+ * the lock on the cache, clears the buffer, and returns 0.
+ *
+ * @return 0 if up-to-date, 1 if out-of-date, <0 on error
+ */
+GIT_WARN_UNUSED_RESULT int git_sortedcache_lockandload(
+ git_sortedcache *sc, git_str *buf);
+
+/* Refresh file timestamp after write completes
+ * You should already be holding the write lock when you call this.
+ */
+void git_sortedcache_updated(git_sortedcache *sc);
+
+/* Release all items in sorted cache
+ *
+ * If `wlock` is true, grabs write lock and releases when done, otherwise
+ * you should already be holding a write lock when you call this.
+ */
+GIT_WARN_UNUSED_RESULT int git_sortedcache_clear(
+ git_sortedcache *sc, bool wlock);
+
+/* Find and/or insert item, returning pointer to item data.
+ * You should already be holding the write lock when you call this.
+ */
+GIT_WARN_UNUSED_RESULT int git_sortedcache_upsert(
+ void **out, git_sortedcache *sc, const char *key);
+
+/* Removes entry at pos from cache
+ * You should already be holding the write lock when you call this.
+ */
+int git_sortedcache_remove(git_sortedcache *sc, size_t pos);
+
+/*
+ * CACHE READ FUNCTIONS
+ *
+ * The following functions access items in the cache. To prevent the
+ * results from being invalidated before they can be used, you should be
+ * holding either a read lock or a write lock when using these functions.
+ *
+ */
+
+/* Lock sortedcache for read */
+GIT_WARN_UNUSED_RESULT int git_sortedcache_rlock(git_sortedcache *sc);
+
+/* Unlock sorted cache when done with read */
+void git_sortedcache_runlock(git_sortedcache *sc);
+
+/* Lookup item by key - returns NULL if not found */
+void *git_sortedcache_lookup(const git_sortedcache *sc, const char *key);
+
+/* Get how many items are in the cache
+ *
+ * You can call this function without holding a lock, but be aware
+ * that it may change before you use it.
+ */
+size_t git_sortedcache_entrycount(const git_sortedcache *sc);
+
+/* Lookup item by index - returns NULL if out of range */
+void *git_sortedcache_entry(git_sortedcache *sc, size_t pos);
+
+/* Lookup index of item by key - returns GIT_ENOTFOUND if not found */
+int git_sortedcache_lookup_index(
+ size_t *out, git_sortedcache *sc, const char *key);
+
+#endif
diff --git a/src/util/staticstr.h b/src/util/staticstr.h
new file mode 100644
index 0000000..b7d0790
--- /dev/null
+++ b/src/util/staticstr.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_stackstr_h__
+#define INCLUDE_stackstr_h__
+
+#include "git2_util.h"
+
+typedef struct {
+ /* Length of / number of bytes used by `data`. */
+ size_t len;
+
+ /* Size of the allocated `data` buffer. */
+ size_t size;
+
+ /* The actual string buffer data. */
+ char data[GIT_FLEX_ARRAY];
+} git_staticstr;
+
+#define git_staticstr_with_size(__size) \
+ struct { \
+ size_t len; \
+ size_t size; \
+ char data[__size]; \
+ }
+
+#define git_staticstr_init(__str, __size) \
+ do { \
+ (__str)->len = 0; \
+ (__str)->size = __size; \
+ (__str)->data[0] = '\0'; \
+ } while(0)
+
+#define git_staticstr_offset(__str) \
+ ((__str)->data + (__str)->len)
+
+#define git_staticstr_remain(__str) \
+ ((__str)->len > (__str)->size ? 0 : ((__str)->size - (__str)->len))
+
+#define git_staticstr_increase(__str, __len) \
+ do { ((__str)->len += __len); } while(0)
+
+#define git_staticstr_consume_bytes(__str, __len) \
+ do { git_staticstr_consume(__str, (__str)->data + __len); } while(0)
+
+#define git_staticstr_consume(__str, __end) \
+ do { \
+ if (__end > (__str)->data && \
+ __end <= (__str)->data + (__str)->len) { \
+ size_t __consumed = __end - (__str)->data; \
+ memmove((__str)->data, __end, (__str)->len - __consumed); \
+ (__str)->len -= __consumed; \
+ (__str)->data[(__str)->len] = '\0'; \
+ } \
+ } while(0)
+
+#define git_staticstr_clear(__str) \
+ do { \
+ (__str)->len = 0; \
+ (__str)->data[0] = 0; \
+ } while(0)
+
+#endif
diff --git a/src/util/str.c b/src/util/str.c
new file mode 100644
index 0000000..0d405bf
--- /dev/null
+++ b/src/util/str.c
@@ -0,0 +1,1372 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "str.h"
+#include "posix.h"
+#include <ctype.h>
+
+/* Used as default value for git_str->ptr so that people can always
+ * assume ptr is non-NULL and zero terminated even for new git_strs.
+ */
+char git_str__initstr[1];
+
+char git_str__oom[1];
+
+#define ENSURE_SIZE(b, d) \
+ if ((b)->ptr == git_str__oom || \
+ ((d) > (b)->asize && git_str_grow((b), (d)) < 0))\
+ return -1;
+
+
+int git_str_init(git_str *buf, size_t initial_size)
+{
+ buf->asize = 0;
+ buf->size = 0;
+ buf->ptr = git_str__initstr;
+
+ ENSURE_SIZE(buf, initial_size);
+
+ return 0;
+}
+
+int git_str_try_grow(
+ git_str *buf, size_t target_size, bool mark_oom)
+{
+ char *new_ptr;
+ size_t new_size;
+
+ if (buf->ptr == git_str__oom)
+ return -1;
+
+ if (buf->asize == 0 && buf->size != 0) {
+ git_error_set(GIT_ERROR_INVALID, "cannot grow a borrowed buffer");
+ return GIT_EINVALID;
+ }
+
+ if (!target_size)
+ target_size = buf->size;
+
+ if (target_size <= buf->asize)
+ return 0;
+
+ if (buf->asize == 0) {
+ new_size = target_size;
+ new_ptr = NULL;
+ } else {
+ new_size = buf->asize;
+ /*
+ * Grow the allocated buffer by 1.5 to allow
+ * re-use of memory holes resulting from the
+ * realloc. If this is still too small, then just
+ * use the target size.
+ */
+ if ((new_size = (new_size << 1) - (new_size >> 1)) < target_size)
+ new_size = target_size;
+ new_ptr = buf->ptr;
+ }
+
+ /* round allocation up to multiple of 8 */
+ new_size = (new_size + 7) & ~7;
+
+ if (new_size < buf->size) {
+ if (mark_oom) {
+ if (buf->ptr && buf->ptr != git_str__initstr)
+ git__free(buf->ptr);
+ buf->ptr = git_str__oom;
+ }
+
+ git_error_set_oom();
+ return -1;
+ }
+
+ new_ptr = git__realloc(new_ptr, new_size);
+
+ if (!new_ptr) {
+ if (mark_oom) {
+ if (buf->ptr && (buf->ptr != git_str__initstr))
+ git__free(buf->ptr);
+ buf->ptr = git_str__oom;
+ }
+ return -1;
+ }
+
+ buf->asize = new_size;
+ buf->ptr = new_ptr;
+
+ /* truncate the existing buffer size if necessary */
+ if (buf->size >= buf->asize)
+ buf->size = buf->asize - 1;
+ buf->ptr[buf->size] = '\0';
+
+ return 0;
+}
+
+int git_str_grow(git_str *buffer, size_t target_size)
+{
+ return git_str_try_grow(buffer, target_size, true);
+}
+
+int git_str_grow_by(git_str *buffer, size_t additional_size)
+{
+ size_t newsize;
+
+ if (GIT_ADD_SIZET_OVERFLOW(&newsize, buffer->size, additional_size)) {
+ buffer->ptr = git_str__oom;
+ return -1;
+ }
+
+ return git_str_try_grow(buffer, newsize, true);
+}
+
+void git_str_dispose(git_str *buf)
+{
+ if (!buf) return;
+
+ if (buf->asize > 0 && buf->ptr != NULL && buf->ptr != git_str__oom)
+ git__free(buf->ptr);
+
+ git_str_init(buf, 0);
+}
+
+void git_str_clear(git_str *buf)
+{
+ buf->size = 0;
+
+ if (!buf->ptr) {
+ buf->ptr = git_str__initstr;
+ buf->asize = 0;
+ }
+
+ if (buf->asize > 0)
+ buf->ptr[0] = '\0';
+}
+
+int git_str_set(git_str *buf, const void *data, size_t len)
+{
+ size_t alloclen;
+
+ if (len == 0 || data == NULL) {
+ git_str_clear(buf);
+ } else {
+ if (data != buf->ptr) {
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, len, 1);
+ ENSURE_SIZE(buf, alloclen);
+ memmove(buf->ptr, data, len);
+ }
+
+ buf->size = len;
+ if (buf->asize > buf->size)
+ buf->ptr[buf->size] = '\0';
+
+ }
+ return 0;
+}
+
+int git_str_sets(git_str *buf, const char *string)
+{
+ return git_str_set(buf, string, string ? strlen(string) : 0);
+}
+
+int git_str_putc(git_str *buf, char c)
+{
+ size_t new_size;
+ GIT_ERROR_CHECK_ALLOC_ADD(&new_size, buf->size, 2);
+ ENSURE_SIZE(buf, new_size);
+ buf->ptr[buf->size++] = c;
+ buf->ptr[buf->size] = '\0';
+ return 0;
+}
+
+int git_str_putcn(git_str *buf, char c, size_t len)
+{
+ size_t new_size;
+ GIT_ERROR_CHECK_ALLOC_ADD(&new_size, buf->size, len);
+ GIT_ERROR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
+ ENSURE_SIZE(buf, new_size);
+ memset(buf->ptr + buf->size, c, len);
+ buf->size += len;
+ buf->ptr[buf->size] = '\0';
+ return 0;
+}
+
+int git_str_put(git_str *buf, const char *data, size_t len)
+{
+ if (len) {
+ size_t new_size;
+
+ GIT_ASSERT_ARG(data);
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&new_size, buf->size, len);
+ GIT_ERROR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
+ ENSURE_SIZE(buf, new_size);
+ memmove(buf->ptr + buf->size, data, len);
+ buf->size += len;
+ buf->ptr[buf->size] = '\0';
+ }
+ return 0;
+}
+
+int git_str_puts(git_str *buf, const char *string)
+{
+ GIT_ASSERT_ARG(string);
+
+ return git_str_put(buf, string, strlen(string));
+}
+
+static char hex_encode[] = "0123456789abcdef";
+
+int git_str_encode_hexstr(git_str *str, const char *data, size_t len)
+{
+ size_t new_size, i;
+ char *s;
+
+ GIT_ERROR_CHECK_ALLOC_MULTIPLY(&new_size, len, 2);
+ GIT_ERROR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
+
+ if (git_str_grow_by(str, new_size) < 0)
+ return -1;
+
+ s = str->ptr + str->size;
+
+ for (i = 0; i < len; i++) {
+ *s++ = hex_encode[(data[i] & 0xf0) >> 4];
+ *s++ = hex_encode[(data[i] & 0x0f)];
+ }
+
+ str->size += (len * 2);
+ str->ptr[str->size] = '\0';
+
+ return 0;
+}
+
+static const char base64_encode[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+int git_str_encode_base64(git_str *buf, const char *data, size_t len)
+{
+ size_t extra = len % 3;
+ uint8_t *write, a, b, c;
+ const uint8_t *read = (const uint8_t *)data;
+ size_t blocks = (len / 3) + !!extra, alloclen;
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&blocks, blocks, 1);
+ GIT_ERROR_CHECK_ALLOC_MULTIPLY(&alloclen, blocks, 4);
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, buf->size);
+
+ ENSURE_SIZE(buf, alloclen);
+ write = (uint8_t *)&buf->ptr[buf->size];
+
+ /* convert each run of 3 bytes into 4 output bytes */
+ for (len -= extra; len > 0; len -= 3) {
+ a = *read++;
+ b = *read++;
+ c = *read++;
+
+ *write++ = base64_encode[a >> 2];
+ *write++ = base64_encode[(a & 0x03) << 4 | b >> 4];
+ *write++ = base64_encode[(b & 0x0f) << 2 | c >> 6];
+ *write++ = base64_encode[c & 0x3f];
+ }
+
+ if (extra > 0) {
+ a = *read++;
+ b = (extra > 1) ? *read++ : 0;
+
+ *write++ = base64_encode[a >> 2];
+ *write++ = base64_encode[(a & 0x03) << 4 | b >> 4];
+ *write++ = (extra > 1) ? base64_encode[(b & 0x0f) << 2] : '=';
+ *write++ = '=';
+ }
+
+ buf->size = ((char *)write) - buf->ptr;
+ buf->ptr[buf->size] = '\0';
+
+ return 0;
+}
+
+/* The inverse of base64_encode */
+static const int8_t base64_decode[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, 0, -1, -1,
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
+ -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+int git_str_decode_base64(git_str *buf, const char *base64, size_t len)
+{
+ size_t i;
+ int8_t a, b, c, d;
+ size_t orig_size = buf->size, new_size;
+
+ if (len % 4) {
+ git_error_set(GIT_ERROR_INVALID, "invalid base64 input");
+ return -1;
+ }
+
+ GIT_ASSERT_ARG(len % 4 == 0);
+ GIT_ERROR_CHECK_ALLOC_ADD(&new_size, (len / 4 * 3), buf->size);
+ GIT_ERROR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
+ ENSURE_SIZE(buf, new_size);
+
+ for (i = 0; i < len; i += 4) {
+ if ((a = base64_decode[(unsigned char)base64[i]]) < 0 ||
+ (b = base64_decode[(unsigned char)base64[i+1]]) < 0 ||
+ (c = base64_decode[(unsigned char)base64[i+2]]) < 0 ||
+ (d = base64_decode[(unsigned char)base64[i+3]]) < 0) {
+ buf->size = orig_size;
+ buf->ptr[buf->size] = '\0';
+
+ git_error_set(GIT_ERROR_INVALID, "invalid base64 input");
+ return -1;
+ }
+
+ buf->ptr[buf->size++] = ((a << 2) | (b & 0x30) >> 4);
+ buf->ptr[buf->size++] = ((b & 0x0f) << 4) | ((c & 0x3c) >> 2);
+ buf->ptr[buf->size++] = (c & 0x03) << 6 | (d & 0x3f);
+ }
+
+ buf->ptr[buf->size] = '\0';
+ return 0;
+}
+
+static const char base85_encode[] =
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~";
+
+int git_str_encode_base85(git_str *buf, const char *data, size_t len)
+{
+ size_t blocks = (len / 4) + !!(len % 4), alloclen;
+
+ GIT_ERROR_CHECK_ALLOC_MULTIPLY(&alloclen, blocks, 5);
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, buf->size);
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
+
+ ENSURE_SIZE(buf, alloclen);
+
+ while (len) {
+ uint32_t acc = 0;
+ char b85[5];
+ int i;
+
+ for (i = 24; i >= 0; i -= 8) {
+ uint8_t ch = *data++;
+ acc |= (uint32_t)ch << i;
+
+ if (--len == 0)
+ break;
+ }
+
+ for (i = 4; i >= 0; i--) {
+ int val = acc % 85;
+ acc /= 85;
+
+ b85[i] = base85_encode[val];
+ }
+
+ for (i = 0; i < 5; i++)
+ buf->ptr[buf->size++] = b85[i];
+ }
+
+ buf->ptr[buf->size] = '\0';
+
+ return 0;
+}
+
+/* The inverse of base85_encode */
+static const int8_t base85_decode[] = {
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 63, -1, 64, 65, 66, 67, -1, 68, 69, 70, 71, -1, 72, -1, -1,
+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, -1, 73, 74, 75, 76, 77,
+ 78, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, -1, -1, -1, 79, 80,
+ 81, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 82, 83, 84, 85, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+};
+
+int git_str_decode_base85(
+ git_str *buf,
+ const char *base85,
+ size_t base85_len,
+ size_t output_len)
+{
+ size_t orig_size = buf->size, new_size;
+
+ if (base85_len % 5 ||
+ output_len > base85_len * 4 / 5) {
+ git_error_set(GIT_ERROR_INVALID, "invalid base85 input");
+ return -1;
+ }
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&new_size, output_len, buf->size);
+ GIT_ERROR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
+ ENSURE_SIZE(buf, new_size);
+
+ while (output_len) {
+ unsigned acc = 0;
+ int de, cnt = 4;
+ unsigned char ch;
+ do {
+ ch = *base85++;
+ de = base85_decode[ch];
+ if (--de < 0)
+ goto on_error;
+
+ acc = acc * 85 + de;
+ } while (--cnt);
+ ch = *base85++;
+ de = base85_decode[ch];
+ if (--de < 0)
+ goto on_error;
+
+ /* Detect overflow. */
+ if (0xffffffff / 85 < acc ||
+ 0xffffffff - de < (acc *= 85))
+ goto on_error;
+
+ acc += de;
+
+ cnt = (output_len < 4) ? (int)output_len : 4;
+ output_len -= cnt;
+ do {
+ acc = (acc << 8) | (acc >> 24);
+ buf->ptr[buf->size++] = acc;
+ } while (--cnt);
+ }
+
+ buf->ptr[buf->size] = 0;
+
+ return 0;
+
+on_error:
+ buf->size = orig_size;
+ buf->ptr[buf->size] = '\0';
+
+ git_error_set(GIT_ERROR_INVALID, "invalid base85 input");
+ return -1;
+}
+
+#define HEX_DECODE(c) ((c | 32) % 39 - 9)
+
+int git_str_decode_percent(
+ git_str *buf,
+ const char *str,
+ size_t str_len)
+{
+ size_t str_pos, new_size;
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&new_size, buf->size, str_len);
+ GIT_ERROR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
+ ENSURE_SIZE(buf, new_size);
+
+ for (str_pos = 0; str_pos < str_len; buf->size++, str_pos++) {
+ if (str[str_pos] == '%' &&
+ str_len > str_pos + 2 &&
+ isxdigit(str[str_pos + 1]) &&
+ isxdigit(str[str_pos + 2])) {
+ buf->ptr[buf->size] = (HEX_DECODE(str[str_pos + 1]) << 4) +
+ HEX_DECODE(str[str_pos + 2]);
+ str_pos += 2;
+ } else {
+ buf->ptr[buf->size] = str[str_pos];
+ }
+ }
+
+ buf->ptr[buf->size] = '\0';
+ return 0;
+}
+
+int git_str_vprintf(git_str *buf, const char *format, va_list ap)
+{
+ size_t expected_size, new_size;
+ int len;
+
+ GIT_ERROR_CHECK_ALLOC_MULTIPLY(&expected_size, strlen(format), 2);
+ GIT_ERROR_CHECK_ALLOC_ADD(&expected_size, expected_size, buf->size);
+ ENSURE_SIZE(buf, expected_size);
+
+ while (1) {
+ va_list args;
+ va_copy(args, ap);
+
+ len = p_vsnprintf(
+ buf->ptr + buf->size,
+ buf->asize - buf->size,
+ format, args
+ );
+
+ va_end(args);
+
+ if (len < 0) {
+ git__free(buf->ptr);
+ buf->ptr = git_str__oom;
+ return -1;
+ }
+
+ if ((size_t)len + 1 <= buf->asize - buf->size) {
+ buf->size += len;
+ break;
+ }
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&new_size, buf->size, len);
+ GIT_ERROR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
+ ENSURE_SIZE(buf, new_size);
+ }
+
+ return 0;
+}
+
+int git_str_printf(git_str *buf, const char *format, ...)
+{
+ int r;
+ va_list ap;
+
+ va_start(ap, format);
+ r = git_str_vprintf(buf, format, ap);
+ va_end(ap);
+
+ return r;
+}
+
+int git_str_copy_cstr(char *data, size_t datasize, const git_str *buf)
+{
+ size_t copylen;
+
+ GIT_ASSERT_ARG(data);
+ GIT_ASSERT_ARG(datasize);
+ GIT_ASSERT_ARG(buf);
+
+ data[0] = '\0';
+
+ if (buf->size == 0 || buf->asize <= 0)
+ return 0;
+
+ copylen = buf->size;
+ if (copylen > datasize - 1)
+ copylen = datasize - 1;
+ memmove(data, buf->ptr, copylen);
+ data[copylen] = '\0';
+
+ return 0;
+}
+
+void git_str_consume_bytes(git_str *buf, size_t len)
+{
+ git_str_consume(buf, buf->ptr + len);
+}
+
+void git_str_consume(git_str *buf, const char *end)
+{
+ if (end > buf->ptr && end <= buf->ptr + buf->size) {
+ size_t consumed = end - buf->ptr;
+ memmove(buf->ptr, end, buf->size - consumed);
+ buf->size -= consumed;
+ buf->ptr[buf->size] = '\0';
+ }
+}
+
+void git_str_truncate(git_str *buf, size_t len)
+{
+ if (len >= buf->size)
+ return;
+
+ buf->size = len;
+ if (buf->size < buf->asize)
+ buf->ptr[buf->size] = '\0';
+}
+
+void git_str_shorten(git_str *buf, size_t amount)
+{
+ if (buf->size > amount)
+ git_str_truncate(buf, buf->size - amount);
+ else
+ git_str_clear(buf);
+}
+
+void git_str_truncate_at_char(git_str *buf, char separator)
+{
+ ssize_t idx = git_str_find(buf, separator);
+ if (idx >= 0)
+ git_str_truncate(buf, (size_t)idx);
+}
+
+void git_str_rtruncate_at_char(git_str *buf, char separator)
+{
+ ssize_t idx = git_str_rfind_next(buf, separator);
+ git_str_truncate(buf, idx < 0 ? 0 : (size_t)idx);
+}
+
+void git_str_swap(git_str *str_a, git_str *str_b)
+{
+ git_str t = *str_a;
+ *str_a = *str_b;
+ *str_b = t;
+}
+
+char *git_str_detach(git_str *buf)
+{
+ char *data = buf->ptr;
+
+ if (buf->asize == 0 || buf->ptr == git_str__oom)
+ return NULL;
+
+ git_str_init(buf, 0);
+
+ return data;
+}
+
+int git_str_attach(git_str *buf, char *ptr, size_t asize)
+{
+ git_str_dispose(buf);
+
+ if (ptr) {
+ buf->ptr = ptr;
+ buf->size = strlen(ptr);
+ if (asize)
+ buf->asize = (asize < buf->size) ? buf->size + 1 : asize;
+ else /* pass 0 to fall back on strlen + 1 */
+ buf->asize = buf->size + 1;
+ }
+
+ ENSURE_SIZE(buf, asize);
+ return 0;
+}
+
+void git_str_attach_notowned(git_str *buf, const char *ptr, size_t size)
+{
+ if (git_str_is_allocated(buf))
+ git_str_dispose(buf);
+
+ if (!size) {
+ git_str_init(buf, 0);
+ } else {
+ buf->ptr = (char *)ptr;
+ buf->asize = 0;
+ buf->size = size;
+ }
+}
+
+int git_str_join_n(git_str *buf, char separator, int nbuf, ...)
+{
+ va_list ap;
+ int i;
+ size_t total_size = 0, original_size = buf->size;
+ char *out, *original = buf->ptr;
+
+ if (buf->size > 0 && buf->ptr[buf->size - 1] != separator)
+ ++total_size; /* space for initial separator */
+
+ /* Make two passes to avoid multiple reallocation */
+
+ va_start(ap, nbuf);
+ for (i = 0; i < nbuf; ++i) {
+ const char *segment;
+ size_t segment_len;
+
+ segment = va_arg(ap, const char *);
+ if (!segment)
+ continue;
+
+ segment_len = strlen(segment);
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&total_size, total_size, segment_len);
+
+ if (segment_len == 0 || segment[segment_len - 1] != separator)
+ GIT_ERROR_CHECK_ALLOC_ADD(&total_size, total_size, 1);
+ }
+ va_end(ap);
+
+ /* expand buffer if needed */
+ if (total_size == 0)
+ return 0;
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&total_size, total_size, 1);
+ if (git_str_grow_by(buf, total_size) < 0)
+ return -1;
+
+ out = buf->ptr + buf->size;
+
+ /* append separator to existing buf if needed */
+ if (buf->size > 0 && out[-1] != separator)
+ *out++ = separator;
+
+ va_start(ap, nbuf);
+ for (i = 0; i < nbuf; ++i) {
+ const char *segment;
+ size_t segment_len;
+
+ segment = va_arg(ap, const char *);
+ if (!segment)
+ continue;
+
+ /* deal with join that references buffer's original content */
+ if (segment >= original && segment < original + original_size) {
+ size_t offset = (segment - original);
+ segment = buf->ptr + offset;
+ segment_len = original_size - offset;
+ } else {
+ segment_len = strlen(segment);
+ }
+
+ /* skip leading separators */
+ if (out > buf->ptr && out[-1] == separator)
+ while (segment_len > 0 && *segment == separator) {
+ segment++;
+ segment_len--;
+ }
+
+ /* copy over next buffer */
+ if (segment_len > 0) {
+ memmove(out, segment, segment_len);
+ out += segment_len;
+ }
+
+ /* append trailing separator (except for last item) */
+ if (i < nbuf - 1 && out > buf->ptr && out[-1] != separator)
+ *out++ = separator;
+ }
+ va_end(ap);
+
+ /* set size based on num characters actually written */
+ buf->size = out - buf->ptr;
+ buf->ptr[buf->size] = '\0';
+
+ return 0;
+}
+
+int git_str_join(
+ git_str *buf,
+ char separator,
+ const char *str_a,
+ const char *str_b)
+{
+ size_t strlen_a = str_a ? strlen(str_a) : 0;
+ size_t strlen_b = strlen(str_b);
+ size_t alloc_len;
+ int need_sep = 0;
+ ssize_t offset_a = -1;
+
+ /* not safe to have str_b point internally to the buffer */
+ if (buf->size)
+ GIT_ASSERT_ARG(str_b < buf->ptr || str_b >= buf->ptr + buf->size);
+
+ /* figure out if we need to insert a separator */
+ if (separator && strlen_a) {
+ while (*str_b == separator) { str_b++; strlen_b--; }
+ if (str_a[strlen_a - 1] != separator)
+ need_sep = 1;
+ }
+
+ /* str_a could be part of the buffer */
+ if (buf->size && str_a >= buf->ptr && str_a < buf->ptr + buf->size)
+ offset_a = str_a - buf->ptr;
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, strlen_a, strlen_b);
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, need_sep);
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 1);
+ ENSURE_SIZE(buf, alloc_len);
+
+ /* fix up internal pointers */
+ if (offset_a >= 0)
+ str_a = buf->ptr + offset_a;
+
+ /* do the actual copying */
+ if (offset_a != 0 && str_a)
+ memmove(buf->ptr, str_a, strlen_a);
+ if (need_sep)
+ buf->ptr[strlen_a] = separator;
+ memcpy(buf->ptr + strlen_a + need_sep, str_b, strlen_b);
+
+ buf->size = strlen_a + strlen_b + need_sep;
+ buf->ptr[buf->size] = '\0';
+
+ return 0;
+}
+
+int git_str_join3(
+ git_str *buf,
+ char separator,
+ const char *str_a,
+ const char *str_b,
+ const char *str_c)
+{
+ size_t len_a = strlen(str_a),
+ len_b = strlen(str_b),
+ len_c = strlen(str_c),
+ len_total;
+ int sep_a = 0, sep_b = 0;
+ char *tgt;
+
+ /* for this function, disallow pointers into the existing buffer */
+ GIT_ASSERT(str_a < buf->ptr || str_a >= buf->ptr + buf->size);
+ GIT_ASSERT(str_b < buf->ptr || str_b >= buf->ptr + buf->size);
+ GIT_ASSERT(str_c < buf->ptr || str_c >= buf->ptr + buf->size);
+
+ if (separator) {
+ if (len_a > 0) {
+ while (*str_b == separator) { str_b++; len_b--; }
+ sep_a = (str_a[len_a - 1] != separator);
+ }
+ if (len_a > 0 || len_b > 0)
+ while (*str_c == separator) { str_c++; len_c--; }
+ if (len_b > 0)
+ sep_b = (str_b[len_b - 1] != separator);
+ }
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&len_total, len_a, sep_a);
+ GIT_ERROR_CHECK_ALLOC_ADD(&len_total, len_total, len_b);
+ GIT_ERROR_CHECK_ALLOC_ADD(&len_total, len_total, sep_b);
+ GIT_ERROR_CHECK_ALLOC_ADD(&len_total, len_total, len_c);
+ GIT_ERROR_CHECK_ALLOC_ADD(&len_total, len_total, 1);
+ ENSURE_SIZE(buf, len_total);
+
+ tgt = buf->ptr;
+
+ if (len_a) {
+ memcpy(tgt, str_a, len_a);
+ tgt += len_a;
+ }
+ if (sep_a)
+ *tgt++ = separator;
+ if (len_b) {
+ memcpy(tgt, str_b, len_b);
+ tgt += len_b;
+ }
+ if (sep_b)
+ *tgt++ = separator;
+ if (len_c)
+ memcpy(tgt, str_c, len_c);
+
+ buf->size = len_a + sep_a + len_b + sep_b + len_c;
+ buf->ptr[buf->size] = '\0';
+
+ return 0;
+}
+
+void git_str_rtrim(git_str *buf)
+{
+ while (buf->size > 0) {
+ if (!git__isspace(buf->ptr[buf->size - 1]))
+ break;
+
+ buf->size--;
+ }
+
+ if (buf->asize > buf->size)
+ buf->ptr[buf->size] = '\0';
+}
+
+int git_str_cmp(const git_str *a, const git_str *b)
+{
+ int result = memcmp(a->ptr, b->ptr, min(a->size, b->size));
+ return (result != 0) ? result :
+ (a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0;
+}
+
+int git_str_splice(
+ git_str *buf,
+ size_t where,
+ size_t nb_to_remove,
+ const char *data,
+ size_t nb_to_insert)
+{
+ char *splice_loc;
+ size_t new_size, alloc_size;
+
+ GIT_ASSERT(buf);
+ GIT_ASSERT(where <= buf->size);
+ GIT_ASSERT(nb_to_remove <= buf->size - where);
+
+ splice_loc = buf->ptr + where;
+
+ /* Ported from git.git
+ * https://github.com/git/git/blob/16eed7c/strbuf.c#L159-176
+ */
+ GIT_ERROR_CHECK_ALLOC_ADD(&new_size, (buf->size - nb_to_remove), nb_to_insert);
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloc_size, new_size, 1);
+ ENSURE_SIZE(buf, alloc_size);
+
+ memmove(splice_loc + nb_to_insert,
+ splice_loc + nb_to_remove,
+ buf->size - where - nb_to_remove);
+
+ memcpy(splice_loc, data, nb_to_insert);
+
+ buf->size = new_size;
+ buf->ptr[buf->size] = '\0';
+ return 0;
+}
+
+/* Quote per http://marc.info/?l=git&m=112927316408690&w=2 */
+int git_str_quote(git_str *buf)
+{
+ const char whitespace[] = { 'a', 'b', 't', 'n', 'v', 'f', 'r' };
+ git_str quoted = GIT_STR_INIT;
+ size_t i = 0;
+ bool quote = false;
+ int error = 0;
+
+ /* walk to the first char that needs quoting */
+ if (buf->size && buf->ptr[0] == '!')
+ quote = true;
+
+ for (i = 0; !quote && i < buf->size; i++) {
+ if (buf->ptr[i] == '"' || buf->ptr[i] == '\\' ||
+ buf->ptr[i] < ' ' || buf->ptr[i] > '~') {
+ quote = true;
+ break;
+ }
+ }
+
+ if (!quote)
+ goto done;
+
+ git_str_putc(&quoted, '"');
+ git_str_put(&quoted, buf->ptr, i);
+
+ for (; i < buf->size; i++) {
+ /* whitespace - use the map above, which is ordered by ascii value */
+ if (buf->ptr[i] >= '\a' && buf->ptr[i] <= '\r') {
+ git_str_putc(&quoted, '\\');
+ git_str_putc(&quoted, whitespace[buf->ptr[i] - '\a']);
+ }
+
+ /* double quote and backslash must be escaped */
+ else if (buf->ptr[i] == '"' || buf->ptr[i] == '\\') {
+ git_str_putc(&quoted, '\\');
+ git_str_putc(&quoted, buf->ptr[i]);
+ }
+
+ /* escape anything unprintable as octal */
+ else if (buf->ptr[i] != ' ' &&
+ (buf->ptr[i] < '!' || buf->ptr[i] > '~')) {
+ git_str_printf(&quoted, "\\%03o", (unsigned char)buf->ptr[i]);
+ }
+
+ /* yay, printable! */
+ else {
+ git_str_putc(&quoted, buf->ptr[i]);
+ }
+ }
+
+ git_str_putc(&quoted, '"');
+
+ if (git_str_oom(&quoted)) {
+ error = -1;
+ goto done;
+ }
+
+ git_str_swap(&quoted, buf);
+
+done:
+ git_str_dispose(&quoted);
+ return error;
+}
+
+/* Unquote per http://marc.info/?l=git&m=112927316408690&w=2 */
+int git_str_unquote(git_str *buf)
+{
+ size_t i, j;
+ char ch;
+
+ git_str_rtrim(buf);
+
+ if (buf->size < 2 || buf->ptr[0] != '"' || buf->ptr[buf->size-1] != '"')
+ goto invalid;
+
+ for (i = 0, j = 1; j < buf->size-1; i++, j++) {
+ ch = buf->ptr[j];
+
+ if (ch == '\\') {
+ if (j == buf->size-2)
+ goto invalid;
+
+ ch = buf->ptr[++j];
+
+ switch (ch) {
+ /* \" or \\ simply copy the char in */
+ case '"': case '\\':
+ break;
+
+ /* add the appropriate escaped char */
+ case 'a': ch = '\a'; break;
+ case 'b': ch = '\b'; break;
+ case 'f': ch = '\f'; break;
+ case 'n': ch = '\n'; break;
+ case 'r': ch = '\r'; break;
+ case 't': ch = '\t'; break;
+ case 'v': ch = '\v'; break;
+
+ /* \xyz digits convert to the char*/
+ case '0': case '1': case '2': case '3':
+ if (j == buf->size-3) {
+ git_error_set(GIT_ERROR_INVALID,
+ "truncated quoted character \\%c", ch);
+ return -1;
+ }
+
+ if (buf->ptr[j+1] < '0' || buf->ptr[j+1] > '7' ||
+ buf->ptr[j+2] < '0' || buf->ptr[j+2] > '7') {
+ git_error_set(GIT_ERROR_INVALID,
+ "truncated quoted character \\%c%c%c",
+ buf->ptr[j], buf->ptr[j+1], buf->ptr[j+2]);
+ return -1;
+ }
+
+ ch = ((buf->ptr[j] - '0') << 6) |
+ ((buf->ptr[j+1] - '0') << 3) |
+ (buf->ptr[j+2] - '0');
+ j += 2;
+ break;
+
+ default:
+ git_error_set(GIT_ERROR_INVALID, "invalid quoted character \\%c", ch);
+ return -1;
+ }
+ }
+
+ buf->ptr[i] = ch;
+ }
+
+ buf->ptr[i] = '\0';
+ buf->size = i;
+
+ return 0;
+
+invalid:
+ git_error_set(GIT_ERROR_INVALID, "invalid quoted line");
+ return -1;
+}
+
+int git_str_puts_escaped(
+ git_str *buf,
+ const char *string,
+ const char *esc_chars,
+ const char *esc_with)
+{
+ const char *scan;
+ size_t total = 0, esc_len = strlen(esc_with), count, alloclen;
+
+ if (!string)
+ return 0;
+
+ for (scan = string; *scan; ) {
+ /* count run of non-escaped characters */
+ count = strcspn(scan, esc_chars);
+ total += count;
+ scan += count;
+ /* count run of escaped characters */
+ count = strspn(scan, esc_chars);
+ total += count * (esc_len + 1);
+ scan += count;
+ }
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, total, 1);
+ if (git_str_grow_by(buf, alloclen) < 0)
+ return -1;
+
+ for (scan = string; *scan; ) {
+ count = strcspn(scan, esc_chars);
+
+ memmove(buf->ptr + buf->size, scan, count);
+ scan += count;
+ buf->size += count;
+
+ for (count = strspn(scan, esc_chars); count > 0; --count) {
+ /* copy escape sequence */
+ memmove(buf->ptr + buf->size, esc_with, esc_len);
+ buf->size += esc_len;
+ /* copy character to be escaped */
+ buf->ptr[buf->size] = *scan;
+ buf->size++;
+ scan++;
+ }
+ }
+
+ buf->ptr[buf->size] = '\0';
+
+ return 0;
+}
+
+void git_str_unescape(git_str *buf)
+{
+ buf->size = git__unescape(buf->ptr);
+}
+
+int git_str_crlf_to_lf(git_str *tgt, const git_str *src)
+{
+ const char *scan = src->ptr;
+ const char *scan_end = src->ptr + src->size;
+ const char *next = memchr(scan, '\r', src->size);
+ size_t new_size;
+ char *out;
+
+ GIT_ASSERT(tgt != src);
+
+ if (!next)
+ return git_str_set(tgt, src->ptr, src->size);
+
+ /* reduce reallocs while in the loop */
+ GIT_ERROR_CHECK_ALLOC_ADD(&new_size, src->size, 1);
+ if (git_str_grow(tgt, new_size) < 0)
+ return -1;
+
+ out = tgt->ptr;
+ tgt->size = 0;
+
+ /* Find the next \r and copy whole chunk up to there to tgt */
+ for (; next; scan = next + 1, next = memchr(scan, '\r', scan_end - scan)) {
+ if (next > scan) {
+ size_t copylen = (size_t)(next - scan);
+ memcpy(out, scan, copylen);
+ out += copylen;
+ }
+
+ /* Do not drop \r unless it is followed by \n */
+ if (next + 1 == scan_end || next[1] != '\n')
+ *out++ = '\r';
+ }
+
+ /* Copy remaining input into dest */
+ if (scan < scan_end) {
+ size_t remaining = (size_t)(scan_end - scan);
+ memcpy(out, scan, remaining);
+ out += remaining;
+ }
+
+ tgt->size = (size_t)(out - tgt->ptr);
+ tgt->ptr[tgt->size] = '\0';
+
+ return 0;
+}
+
+int git_str_lf_to_crlf(git_str *tgt, const git_str *src)
+{
+ const char *start = src->ptr;
+ const char *end = start + src->size;
+ const char *scan = start;
+ const char *next = memchr(scan, '\n', src->size);
+ size_t alloclen;
+
+ GIT_ASSERT(tgt != src);
+
+ if (!next)
+ return git_str_set(tgt, src->ptr, src->size);
+
+ /* attempt to reduce reallocs while in the loop */
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, src->size, src->size >> 4);
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
+ if (git_str_grow(tgt, alloclen) < 0)
+ return -1;
+ tgt->size = 0;
+
+ for (; next; scan = next + 1, next = memchr(scan, '\n', end - scan)) {
+ size_t copylen = next - scan;
+
+ /* if we find mixed line endings, carry on */
+ if (copylen && next[-1] == '\r')
+ copylen--;
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&alloclen, copylen, 3);
+ if (git_str_grow_by(tgt, alloclen) < 0)
+ return -1;
+
+ if (copylen) {
+ memcpy(tgt->ptr + tgt->size, scan, copylen);
+ tgt->size += copylen;
+ }
+
+ tgt->ptr[tgt->size++] = '\r';
+ tgt->ptr[tgt->size++] = '\n';
+ }
+
+ tgt->ptr[tgt->size] = '\0';
+ return git_str_put(tgt, scan, end - scan);
+}
+
+int git_str_common_prefix(git_str *buf, char *const *const strings, size_t count)
+{
+ size_t i;
+ const char *str, *pfx;
+
+ git_str_clear(buf);
+
+ if (!strings || !count)
+ return 0;
+
+ /* initialize common prefix to first string */
+ if (git_str_sets(buf, strings[0]) < 0)
+ return -1;
+
+ /* go through the rest of the strings, truncating to shared prefix */
+ for (i = 1; i < count; ++i) {
+
+ for (str = strings[i], pfx = buf->ptr;
+ *str && *str == *pfx;
+ str++, pfx++)
+ /* scanning */;
+
+ git_str_truncate(buf, pfx - buf->ptr);
+
+ if (!buf->size)
+ break;
+ }
+
+ return 0;
+}
+
+int git_str_is_binary(const git_str *buf)
+{
+ const char *scan = buf->ptr, *end = buf->ptr + buf->size;
+ git_str_bom_t bom;
+ int printable = 0, nonprintable = 0;
+
+ scan += git_str_detect_bom(&bom, buf);
+
+ if (bom > GIT_STR_BOM_UTF8)
+ return 1;
+
+ while (scan < end) {
+ unsigned char c = *scan++;
+
+ /* Printable characters are those above SPACE (0x1F) excluding DEL,
+ * and including BS, ESC and FF.
+ */
+ if ((c > 0x1F && c != 127) || c == '\b' || c == '\033' || c == '\014')
+ printable++;
+ else if (c == '\0')
+ return true;
+ else if (!git__isspace(c))
+ nonprintable++;
+ }
+
+ return ((printable >> 7) < nonprintable);
+}
+
+int git_str_contains_nul(const git_str *buf)
+{
+ return (memchr(buf->ptr, '\0', buf->size) != NULL);
+}
+
+int git_str_detect_bom(git_str_bom_t *bom, const git_str *buf)
+{
+ const char *ptr;
+ size_t len;
+
+ *bom = GIT_STR_BOM_NONE;
+ /* need at least 2 bytes to look for any BOM */
+ if (buf->size < 2)
+ return 0;
+
+ ptr = buf->ptr;
+ len = buf->size;
+
+ switch (*ptr++) {
+ case 0:
+ if (len >= 4 && ptr[0] == 0 && ptr[1] == '\xFE' && ptr[2] == '\xFF') {
+ *bom = GIT_STR_BOM_UTF32_BE;
+ return 4;
+ }
+ break;
+ case '\xEF':
+ if (len >= 3 && ptr[0] == '\xBB' && ptr[1] == '\xBF') {
+ *bom = GIT_STR_BOM_UTF8;
+ return 3;
+ }
+ break;
+ case '\xFE':
+ if (*ptr == '\xFF') {
+ *bom = GIT_STR_BOM_UTF16_BE;
+ return 2;
+ }
+ break;
+ case '\xFF':
+ if (*ptr != '\xFE')
+ break;
+ if (len >= 4 && ptr[1] == 0 && ptr[2] == 0) {
+ *bom = GIT_STR_BOM_UTF32_LE;
+ return 4;
+ } else {
+ *bom = GIT_STR_BOM_UTF16_LE;
+ return 2;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+bool git_str_gather_text_stats(
+ git_str_text_stats *stats, const git_str *buf, bool skip_bom)
+{
+ const char *scan = buf->ptr, *end = buf->ptr + buf->size;
+ int skip;
+
+ memset(stats, 0, sizeof(*stats));
+
+ /* BOM detection */
+ skip = git_str_detect_bom(&stats->bom, buf);
+ if (skip_bom)
+ scan += skip;
+
+ /* Ignore EOF character */
+ if (buf->size > 0 && end[-1] == '\032')
+ end--;
+
+ /* Counting loop */
+ while (scan < end) {
+ unsigned char c = *scan++;
+
+ if (c > 0x1F && c != 0x7F)
+ stats->printable++;
+ else switch (c) {
+ case '\0':
+ stats->nul++;
+ stats->nonprintable++;
+ break;
+ case '\n':
+ stats->lf++;
+ break;
+ case '\r':
+ stats->cr++;
+ if (scan < end && *scan == '\n')
+ stats->crlf++;
+ break;
+ case '\t': case '\f': case '\v': case '\b': case 0x1b: /*ESC*/
+ stats->printable++;
+ break;
+ default:
+ stats->nonprintable++;
+ break;
+ }
+ }
+
+ /* Treat files with a bare CR as binary */
+ return (stats->cr != stats->crlf || stats->nul > 0 ||
+ ((stats->printable >> 7) < stats->nonprintable));
+}
diff --git a/src/util/str.h b/src/util/str.h
new file mode 100644
index 0000000..588e6fc
--- /dev/null
+++ b/src/util/str.h
@@ -0,0 +1,357 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_str_h__
+#define INCLUDE_str_h__
+
+#include "git2_util.h"
+
+struct git_str {
+ char *ptr;
+ size_t asize;
+ size_t size;
+};
+
+typedef enum {
+ GIT_STR_BOM_NONE = 0,
+ GIT_STR_BOM_UTF8 = 1,
+ GIT_STR_BOM_UTF16_LE = 2,
+ GIT_STR_BOM_UTF16_BE = 3,
+ GIT_STR_BOM_UTF32_LE = 4,
+ GIT_STR_BOM_UTF32_BE = 5
+} git_str_bom_t;
+
+typedef struct {
+ git_str_bom_t bom; /* BOM found at head of text */
+ unsigned int nul, cr, lf, crlf; /* NUL, CR, LF and CRLF counts */
+ unsigned int printable, nonprintable; /* These are just approximations! */
+} git_str_text_stats;
+
+extern char git_str__initstr[];
+extern char git_str__oom[];
+
+/* Use to initialize string buffer structure when git_str is on stack */
+#define GIT_STR_INIT { git_str__initstr, 0, 0 }
+
+/**
+ * Static initializer for git_str from static string buffer
+ */
+#define GIT_STR_INIT_CONST(str, len) { (char *)(str), 0, (size_t)(len) }
+
+GIT_INLINE(bool) git_str_is_allocated(const git_str *str)
+{
+ return (str->ptr != NULL && str->asize > 0);
+}
+
+/**
+ * Initialize a git_str structure.
+ *
+ * For the cases where GIT_STR_INIT cannot be used to do static
+ * initialization.
+ */
+extern int git_str_init(git_str *str, size_t initial_size);
+
+extern void git_str_dispose(git_str *str);
+
+/**
+ * Resize the string buffer allocation to make more space.
+ *
+ * This will attempt to grow the string buffer to accommodate the target
+ * size. The bstring buffer's `ptr` will be replaced with a newly
+ * allocated block of data. Be careful so that memory allocated by the
+ * caller is not lost. As a special variant, if you pass `target_size` as
+ * 0 and the memory is not allocated by libgit2, this will allocate a new
+ * buffer of size `size` and copy the external data into it.
+ *
+ * Currently, this will never shrink a buffer, only expand it.
+ *
+ * If the allocation fails, this will return an error and the buffer will be
+ * marked as invalid for future operations, invaliding the contents.
+ *
+ * @param str The buffer to be resized; may or may not be allocated yet
+ * @param target_size The desired available size
+ * @return 0 on success, -1 on allocation failure
+ */
+int git_str_grow(git_str *str, size_t target_size);
+
+/**
+ * Resize the buffer allocation to make more space.
+ *
+ * This will attempt to grow the string buffer to accommodate the
+ * additional size. It is similar to `git_str_grow`, but performs the
+ * new size calculation, checking for overflow.
+ *
+ * Like `git_str_grow`, if this is a user-supplied string buffer,
+ * this will allocate a new string uffer.
+ */
+extern int git_str_grow_by(git_str *str, size_t additional_size);
+
+/**
+ * Attempt to grow the buffer to hold at least `target_size` bytes.
+ *
+ * If the allocation fails, this will return an error. If `mark_oom` is
+ * true, this will mark the string buffer as invalid for future
+ * operations; if false, existing string buffer content will be preserved,
+ * but calling code must handle that string buffer was not expanded. If
+ * `preserve_external` is true, then any existing data pointed to be
+ * `ptr` even if `asize` is zero will be copied into the newly allocated
+ * string buffer.
+ */
+extern int git_str_try_grow(
+ git_str *str, size_t target_size, bool mark_oom);
+
+extern void git_str_swap(git_str *str_a, git_str *str_b);
+extern char *git_str_detach(git_str *str);
+extern int git_str_attach(git_str *str, char *ptr, size_t asize);
+
+/* Populates a `git_str` where the contents are not "owned" by the string
+ * buffer, and calls to `git_str_dispose` will not free the given str.
+ */
+extern void git_str_attach_notowned(
+ git_str *str, const char *ptr, size_t size);
+
+/**
+ * Test if there have been any reallocation failures with this git_str.
+ *
+ * Any function that writes to a git_str can fail due to memory allocation
+ * issues. If one fails, the git_str will be marked with an OOM error and
+ * further calls to modify the string buffer will fail. Check
+ * git_str_oom() at the end of your sequence and it will be true if you
+ * ran out of memory at any point with that string buffer.
+ *
+ * @return false if no error, true if allocation error
+ */
+GIT_INLINE(bool) git_str_oom(const git_str *str)
+{
+ return (str->ptr == git_str__oom);
+}
+
+/*
+ * Functions below that return int value error codes will return 0 on
+ * success or -1 on failure (which generally means an allocation failed).
+ * Using a git_str where the allocation has failed with result in -1 from
+ * all further calls using that string buffer. As a result, you can
+ * ignore the return code of these functions and call them in a series
+ * then just call git_str_oom at the end.
+ */
+
+int git_str_set(git_str *str, const void *data, size_t datalen);
+
+int git_str_sets(git_str *str, const char *string);
+int git_str_putc(git_str *str, char c);
+int git_str_putcn(git_str *str, char c, size_t len);
+int git_str_put(git_str *str, const char *data, size_t len);
+int git_str_puts(git_str *str, const char *string);
+int git_str_printf(git_str *str, const char *format, ...) GIT_FORMAT_PRINTF(2, 3);
+int git_str_vprintf(git_str *str, const char *format, va_list ap);
+void git_str_clear(git_str *str);
+void git_str_consume_bytes(git_str *str, size_t len);
+void git_str_consume(git_str *str, const char *end);
+void git_str_truncate(git_str *str, size_t len);
+void git_str_shorten(git_str *str, size_t amount);
+void git_str_truncate_at_char(git_str *path, char separator);
+void git_str_rtruncate_at_char(git_str *path, char separator);
+
+/** General join with separator */
+int git_str_join_n(git_str *str, char separator, int len, ...);
+/** Fast join of two strings - first may legally point into `str` data */
+int git_str_join(git_str *str, char separator, const char *str_a, const char *str_b);
+/** Fast join of three strings - cannot reference `str` data */
+int git_str_join3(git_str *str, char separator, const char *str_a, const char *str_b, const char *str_c);
+
+/**
+ * Join two strings as paths, inserting a slash between as needed.
+ * @return 0 on success, -1 on failure
+ */
+GIT_INLINE(int) git_str_joinpath(git_str *str, const char *a, const char *b)
+{
+ return git_str_join(str, '/', a, b);
+}
+
+GIT_INLINE(const char *) git_str_cstr(const git_str *str)
+{
+ return str->ptr;
+}
+
+GIT_INLINE(size_t) git_str_len(const git_str *str)
+{
+ return str->size;
+}
+
+int git_str_copy_cstr(char *data, size_t datasize, const git_str *str);
+
+#define git_str_PUTS(str, cstr) git_str_put(str, cstr, sizeof(cstr) - 1)
+
+GIT_INLINE(ssize_t) git_str_rfind_next(const git_str *str, char ch)
+{
+ ssize_t idx = (ssize_t)str->size - 1;
+ while (idx >= 0 && str->ptr[idx] == ch) idx--;
+ while (idx >= 0 && str->ptr[idx] != ch) idx--;
+ return idx;
+}
+
+GIT_INLINE(ssize_t) git_str_rfind(const git_str *str, char ch)
+{
+ ssize_t idx = (ssize_t)str->size - 1;
+ while (idx >= 0 && str->ptr[idx] != ch) idx--;
+ return idx;
+}
+
+GIT_INLINE(ssize_t) git_str_find(const git_str *str, char ch)
+{
+ void *found = memchr(str->ptr, ch, str->size);
+ return found ? (ssize_t)((const char *)found - str->ptr) : -1;
+}
+
+/* Remove whitespace from the end of the string buffer */
+void git_str_rtrim(git_str *str);
+
+int git_str_cmp(const git_str *a, const git_str *b);
+
+/* Quote and unquote a string buffer as specified in
+ * http://marc.info/?l=git&m=112927316408690&w=2
+ */
+int git_str_quote(git_str *str);
+int git_str_unquote(git_str *str);
+
+/* Write data as a hex string */
+int git_str_encode_hexstr(git_str *str, const char *data, size_t len);
+
+/* Write data as base64 encoded in string buffer */
+int git_str_encode_base64(git_str *str, const char *data, size_t len);
+/* Decode the given bas64 and write the result to the string buffer */
+int git_str_decode_base64(git_str *str, const char *base64, size_t len);
+
+/* Write data as "base85" encoded in string buffer */
+int git_str_encode_base85(git_str *str, const char *data, size_t len);
+/* Decode the given "base85" and write the result to the string buffer */
+int git_str_decode_base85(git_str *str, const char *base64, size_t len, size_t output_len);
+
+/*
+ * Decode the given percent-encoded string and write the result to the
+ * string buffer.
+ */
+int git_str_decode_percent(git_str *str, const char *encoded, size_t len);
+
+/*
+ * Insert, remove or replace a portion of the string buffer.
+ *
+ * @param str The string buffer to work with
+ *
+ * @param where The location in the string buffer where the transformation
+ * should be applied.
+ *
+ * @param nb_to_remove The number of chars to be removed. 0 to not
+ * remove any character in the string buffer.
+ *
+ * @param data A pointer to the data which should be inserted.
+ *
+ * @param nb_to_insert The number of chars to be inserted. 0 to not
+ * insert any character from the string buffer.
+ *
+ * @return 0 or an error code.
+ */
+int git_str_splice(
+ git_str *str,
+ size_t where,
+ size_t nb_to_remove,
+ const char *data,
+ size_t nb_to_insert);
+
+/**
+ * Append string to string buffer, prefixing each character from
+ * `esc_chars` with `esc_with` string.
+ *
+ * @param str String buffer to append data to
+ * @param string String to escape and append
+ * @param esc_chars Characters to be escaped
+ * @param esc_with String to insert in from of each found character
+ * @return 0 on success, <0 on failure (probably allocation problem)
+ */
+extern int git_str_puts_escaped(
+ git_str *str,
+ const char *string,
+ const char *esc_chars,
+ const char *esc_with);
+
+/**
+ * Append string escaping characters that are regex special
+ */
+GIT_INLINE(int) git_str_puts_escape_regex(git_str *str, const char *string)
+{
+ return git_str_puts_escaped(str, string, "^.[]$()|*+?{}\\", "\\");
+}
+
+/**
+ * Unescape all characters in a string buffer in place
+ *
+ * I.e. remove backslashes
+ */
+extern void git_str_unescape(git_str *str);
+
+/**
+ * Replace all \r\n with \n.
+ *
+ * @return 0 on success, -1 on memory error
+ */
+extern int git_str_crlf_to_lf(git_str *tgt, const git_str *src);
+
+/**
+ * Replace all \n with \r\n. Does not modify existing \r\n.
+ *
+ * @return 0 on success, -1 on memory error
+ */
+extern int git_str_lf_to_crlf(git_str *tgt, const git_str *src);
+
+/**
+ * Fill string buffer with the common prefix of a array of strings
+ *
+ * String buffer will be set to empty if there is no common prefix
+ */
+extern int git_str_common_prefix(git_str *buf, char *const *const strings, size_t count);
+
+/**
+ * Check if a string buffer begins with a UTF BOM
+ *
+ * @param bom Set to the type of BOM detected or GIT_BOM_NONE
+ * @param str String buffer in which to check the first bytes for a BOM
+ * @return Number of bytes of BOM data (or 0 if no BOM found)
+ */
+extern int git_str_detect_bom(git_str_bom_t *bom, const git_str *str);
+
+/**
+ * Gather stats for a piece of text
+ *
+ * Fill the `stats` structure with counts of unreadable characters, carriage
+ * returns, etc, so it can be used in heuristics. This automatically skips
+ * a trailing EOF (\032 character). Also it will look for a BOM at the
+ * start of the text and can be told to skip that as well.
+ *
+ * @param stats Structure to be filled in
+ * @param str Text to process
+ * @param skip_bom Exclude leading BOM from stats if true
+ * @return Does the string buffer heuristically look like binary data
+ */
+extern bool git_str_gather_text_stats(
+ git_str_text_stats *stats, const git_str *str, bool skip_bom);
+
+/**
+* Check quickly if string buffer looks like it contains binary data
+*
+* @param str string buffer to check
+* @return 1 if string buffer looks like non-text data
+*/
+int git_str_is_binary(const git_str *str);
+
+/**
+* Check quickly if buffer contains a NUL byte
+*
+* @param str string buffer to check
+* @return 1 if string buffer contains a NUL byte
+*/
+int git_str_contains_nul(const git_str *str);
+
+#endif
diff --git a/src/util/strmap.c b/src/util/strmap.c
new file mode 100644
index 0000000..c6e5b6d
--- /dev/null
+++ b/src/util/strmap.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "strmap.h"
+
+#define kmalloc git__malloc
+#define kcalloc git__calloc
+#define krealloc git__realloc
+#define kreallocarray git__reallocarray
+#define kfree git__free
+#include "khash.h"
+
+__KHASH_TYPE(str, const char *, void *)
+
+__KHASH_IMPL(str, static kh_inline, const char *, void *, 1, kh_str_hash_func, kh_str_hash_equal)
+
+int git_strmap_new(git_strmap **out)
+{
+ *out = kh_init(str);
+ GIT_ERROR_CHECK_ALLOC(*out);
+
+ return 0;
+}
+
+void git_strmap_free(git_strmap *map)
+{
+ kh_destroy(str, map);
+}
+
+void git_strmap_clear(git_strmap *map)
+{
+ kh_clear(str, map);
+}
+
+size_t git_strmap_size(git_strmap *map)
+{
+ return kh_size(map);
+}
+
+void *git_strmap_get(git_strmap *map, const char *key)
+{
+ size_t idx = kh_get(str, map, key);
+ if (idx == kh_end(map) || !kh_exist(map, idx))
+ return NULL;
+ return kh_val(map, idx);
+}
+
+int git_strmap_set(git_strmap *map, const char *key, void *value)
+{
+ size_t idx;
+ int rval;
+
+ idx = kh_put(str, map, key, &rval);
+ if (rval < 0)
+ return -1;
+
+ if (rval == 0)
+ kh_key(map, idx) = key;
+
+ kh_val(map, idx) = value;
+
+ return 0;
+}
+
+int git_strmap_delete(git_strmap *map, const char *key)
+{
+ khiter_t idx = kh_get(str, map, key);
+ if (idx == kh_end(map))
+ return GIT_ENOTFOUND;
+ kh_del(str, map, idx);
+ return 0;
+}
+
+int git_strmap_exists(git_strmap *map, const char *key)
+{
+ return kh_get(str, map, key) != kh_end(map);
+}
+
+int git_strmap_iterate(void **value, git_strmap *map, size_t *iter, const char **key)
+{
+ size_t i = *iter;
+
+ while (i < map->n_buckets && !kh_exist(map, i))
+ i++;
+
+ if (i >= map->n_buckets)
+ return GIT_ITEROVER;
+
+ if (key)
+ *key = kh_key(map, i);
+ if (value)
+ *value = kh_val(map, i);
+ *iter = ++i;
+
+ return 0;
+}
diff --git a/src/util/strmap.h b/src/util/strmap.h
new file mode 100644
index 0000000..b64d3dc
--- /dev/null
+++ b/src/util/strmap.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_strmap_h__
+#define INCLUDE_strmap_h__
+
+#include "git2_util.h"
+
+/** A map with C strings as key. */
+typedef struct kh_str_s git_strmap;
+
+/**
+ * Allocate a new string map.
+ *
+ * @param out Pointer to the map that shall be allocated.
+ * @return 0 on success, an error code if allocation has failed.
+ */
+int git_strmap_new(git_strmap **out);
+
+/**
+ * Free memory associated with the map.
+ *
+ * Note that this function will _not_ free keys or values added
+ * to this map.
+ *
+ * @param map Pointer to the map that is to be free'd. May be
+ * `NULL`.
+ */
+void git_strmap_free(git_strmap *map);
+
+/**
+ * Clear all entries from the map.
+ *
+ * This function will remove all entries from the associated map.
+ * Memory associated with it will not be released, though.
+ *
+ * @param map Pointer to the map that shall be cleared. May be
+ * `NULL`.
+ */
+void git_strmap_clear(git_strmap *map);
+
+/**
+ * Return the number of elements in the map.
+ *
+ * @parameter map map containing the elements
+ * @return number of elements in the map
+ */
+size_t git_strmap_size(git_strmap *map);
+
+/**
+ * Return value associated with the given key.
+ *
+ * @param map map to search key in
+ * @param key key to search for
+ * @return value associated with the given key or NULL if the key was not found
+ */
+void *git_strmap_get(git_strmap *map, const char *key);
+
+/**
+ * Set the entry for key to value.
+ *
+ * If the map has no corresponding entry for the given key, a new
+ * entry will be created with the given value. If an entry exists
+ * already, its value will be updated to match the given value.
+ *
+ * @param map map to create new entry in
+ * @param key key to set
+ * @param value value to associate the key with; may be NULL
+ * @return zero if the key was successfully set, a negative error
+ * code otherwise
+ */
+int git_strmap_set(git_strmap *map, const char *key, void *value);
+
+/**
+ * Delete an entry from the map.
+ *
+ * Delete the given key and its value from the map. If no such
+ * key exists, this will do nothing.
+ *
+ * @param map map to delete key in
+ * @param key key to delete
+ * @return `0` if the key has been deleted, GIT_ENOTFOUND if no
+ * such key was found, a negative code in case of an
+ * error
+ */
+int git_strmap_delete(git_strmap *map, const char *key);
+
+/**
+ * Check whether a key exists in the given map.
+ *
+ * @param map map to query for the key
+ * @param key key to search for
+ * @return 0 if the key has not been found, 1 otherwise
+ */
+int git_strmap_exists(git_strmap *map, const char *key);
+
+/**
+ * Iterate over entries of the map.
+ *
+ * This functions allows to iterate over all key-value entries of
+ * the map. The current position is stored in the `iter` variable
+ * and should be initialized to `0` before the first call to this
+ * function.
+ *
+ * @param map map to iterate over
+ * @param value pointer to the variable where to store the current
+ * value. May be NULL.
+ * @param iter iterator storing the current position. Initialize
+ * with zero previous to the first call.
+ * @param key pointer to the variable where to store the current
+ * key. May be NULL.
+ * @return `0` if the next entry was correctly retrieved.
+ * GIT_ITEROVER if no entries are left. A negative error
+ * code otherwise.
+ */
+int git_strmap_iterate(void **value, git_strmap *map, size_t *iter, const char **key);
+
+#define git_strmap_foreach(h, kvar, vvar, code) { size_t __i = 0; \
+ while (git_strmap_iterate((void **) &(vvar), h, &__i, &(kvar)) == 0) { \
+ code; \
+ } }
+
+#define git_strmap_foreach_value(h, vvar, code) { size_t __i = 0; \
+ while (git_strmap_iterate((void **) &(vvar), h, &__i, NULL) == 0) { \
+ code; \
+ } }
+
+#endif
diff --git a/src/util/strnlen.h b/src/util/strnlen.h
new file mode 100644
index 0000000..eecfe3c
--- /dev/null
+++ b/src/util/strnlen.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_strlen_h__
+#define INCLUDE_strlen_h__
+
+#if defined(__MINGW32__) || defined(__sun) || defined(__APPLE__) || defined(__MidnightBSD__) ||\
+ (defined(_MSC_VER) && _MSC_VER < 1500)
+# define NO_STRNLEN
+#endif
+
+#ifdef NO_STRNLEN
+GIT_INLINE(size_t) p_strnlen(const char *s, size_t maxlen) {
+ const char *end = memchr(s, 0, maxlen);
+ return end ? (size_t)(end - s) : maxlen;
+}
+#else
+# define p_strnlen strnlen
+#endif
+
+#endif
diff --git a/src/util/thread.c b/src/util/thread.c
new file mode 100644
index 0000000..bc7364f
--- /dev/null
+++ b/src/util/thread.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "git2_util.h"
+
+#if !defined(GIT_THREADS)
+
+#define TLSDATA_MAX 16
+
+typedef struct {
+ void *value;
+ void (GIT_SYSTEM_CALL *destroy_fn)(void *);
+} tlsdata_value;
+
+static tlsdata_value tlsdata_values[TLSDATA_MAX];
+static int tlsdata_cnt = 0;
+
+int git_tlsdata_init(git_tlsdata_key *key, void (GIT_SYSTEM_CALL *destroy_fn)(void *))
+{
+ if (tlsdata_cnt >= TLSDATA_MAX)
+ return -1;
+
+ tlsdata_values[tlsdata_cnt].value = NULL;
+ tlsdata_values[tlsdata_cnt].destroy_fn = destroy_fn;
+
+ *key = tlsdata_cnt;
+ tlsdata_cnt++;
+
+ return 0;
+}
+
+int git_tlsdata_set(git_tlsdata_key key, void *value)
+{
+ if (key < 0 || key > tlsdata_cnt)
+ return -1;
+
+ tlsdata_values[key].value = value;
+ return 0;
+}
+
+void *git_tlsdata_get(git_tlsdata_key key)
+{
+ if (key < 0 || key > tlsdata_cnt)
+ return NULL;
+
+ return tlsdata_values[key].value;
+}
+
+int git_tlsdata_dispose(git_tlsdata_key key)
+{
+ void *value;
+ void (*destroy_fn)(void *) = NULL;
+
+ if (key < 0 || key > tlsdata_cnt)
+ return -1;
+
+ value = tlsdata_values[key].value;
+ destroy_fn = tlsdata_values[key].destroy_fn;
+
+ tlsdata_values[key].value = NULL;
+ tlsdata_values[key].destroy_fn = NULL;
+
+ if (value && destroy_fn)
+ destroy_fn(value);
+
+ return 0;
+}
+
+#elif defined(GIT_WIN32)
+
+int git_tlsdata_init(git_tlsdata_key *key, void (GIT_SYSTEM_CALL *destroy_fn)(void *))
+{
+ DWORD fls_index = FlsAlloc(destroy_fn);
+
+ if (fls_index == FLS_OUT_OF_INDEXES)
+ return -1;
+
+ *key = fls_index;
+ return 0;
+}
+
+int git_tlsdata_set(git_tlsdata_key key, void *value)
+{
+ if (!FlsSetValue(key, value))
+ return -1;
+
+ return 0;
+}
+
+void *git_tlsdata_get(git_tlsdata_key key)
+{
+ return FlsGetValue(key);
+}
+
+int git_tlsdata_dispose(git_tlsdata_key key)
+{
+ if (!FlsFree(key))
+ return -1;
+
+ return 0;
+}
+
+#elif defined(_POSIX_THREADS)
+
+int git_tlsdata_init(git_tlsdata_key *key, void (GIT_SYSTEM_CALL *destroy_fn)(void *))
+{
+ if (pthread_key_create(key, destroy_fn) != 0)
+ return -1;
+
+ return 0;
+}
+
+int git_tlsdata_set(git_tlsdata_key key, void *value)
+{
+ if (pthread_setspecific(key, value) != 0)
+ return -1;
+
+ return 0;
+}
+
+void *git_tlsdata_get(git_tlsdata_key key)
+{
+ return pthread_getspecific(key);
+}
+
+int git_tlsdata_dispose(git_tlsdata_key key)
+{
+ if (pthread_key_delete(key) != 0)
+ return -1;
+
+ return 0;
+}
+
+#else
+# error unknown threading model
+#endif
diff --git a/src/util/thread.h b/src/util/thread.h
new file mode 100644
index 0000000..c32554b
--- /dev/null
+++ b/src/util/thread.h
@@ -0,0 +1,480 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_thread_h__
+#define INCLUDE_thread_h__
+
+#if defined(GIT_THREADS)
+
+#if defined(__clang__)
+
+# if (__clang_major__ < 3 || (__clang_major__ == 3 && __clang_minor__ < 1))
+# error Atomic primitives do not exist on this version of clang; configure libgit2 with -DUSE_THREADS=OFF
+# else
+# define GIT_BUILTIN_ATOMIC
+# endif
+
+#elif defined(__GNUC__)
+
+# if (__GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 1))
+# error Atomic primitives do not exist on this version of gcc; configure libgit2 with -DUSE_THREADS=OFF
+# elif (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7))
+# define GIT_BUILTIN_ATOMIC
+# else
+# define GIT_BUILTIN_SYNC
+# endif
+
+#endif
+
+#endif /* GIT_THREADS */
+
+/* Common operations even if threading has been disabled */
+typedef struct {
+#if defined(GIT_WIN32)
+ volatile long val;
+#else
+ volatile int val;
+#endif
+} git_atomic32;
+
+#ifdef GIT_ARCH_64
+
+typedef struct {
+#if defined(GIT_WIN32)
+ volatile __int64 val;
+#else
+ volatile int64_t val;
+#endif
+} git_atomic64;
+
+typedef git_atomic64 git_atomic_ssize;
+
+#define git_atomic_ssize_set git_atomic64_set
+#define git_atomic_ssize_add git_atomic64_add
+#define git_atomic_ssize_get git_atomic64_get
+
+#else
+
+typedef git_atomic32 git_atomic_ssize;
+
+#define git_atomic_ssize_set git_atomic32_set
+#define git_atomic_ssize_add git_atomic32_add
+#define git_atomic_ssize_get git_atomic32_get
+
+#endif
+
+#ifdef GIT_THREADS
+
+#ifdef GIT_WIN32
+# include "win32/thread.h"
+#else
+# include "unix/pthread.h"
+#endif
+
+/*
+ * Atomically sets the contents of *a to be val.
+ */
+GIT_INLINE(void) git_atomic32_set(git_atomic32 *a, int val)
+{
+#if defined(GIT_WIN32)
+ InterlockedExchange(&a->val, (LONG)val);
+#elif defined(GIT_BUILTIN_ATOMIC)
+ __atomic_store_n(&a->val, val, __ATOMIC_SEQ_CST);
+#elif defined(GIT_BUILTIN_SYNC)
+ __sync_lock_test_and_set(&a->val, val);
+#else
+# error "Unsupported architecture for atomic operations"
+#endif
+}
+
+/*
+ * Atomically increments the contents of *a by 1, and stores the result back into *a.
+ * @return the result of the operation.
+ */
+GIT_INLINE(int) git_atomic32_inc(git_atomic32 *a)
+{
+#if defined(GIT_WIN32)
+ return InterlockedIncrement(&a->val);
+#elif defined(GIT_BUILTIN_ATOMIC)
+ return __atomic_add_fetch(&a->val, 1, __ATOMIC_SEQ_CST);
+#elif defined(GIT_BUILTIN_SYNC)
+ return __sync_add_and_fetch(&a->val, 1);
+#else
+# error "Unsupported architecture for atomic operations"
+#endif
+}
+
+/*
+ * Atomically adds the contents of *a and addend, and stores the result back into *a.
+ * @return the result of the operation.
+ */
+GIT_INLINE(int) git_atomic32_add(git_atomic32 *a, int32_t addend)
+{
+#if defined(GIT_WIN32)
+ return InterlockedAdd(&a->val, addend);
+#elif defined(GIT_BUILTIN_ATOMIC)
+ return __atomic_add_fetch(&a->val, addend, __ATOMIC_SEQ_CST);
+#elif defined(GIT_BUILTIN_SYNC)
+ return __sync_add_and_fetch(&a->val, addend);
+#else
+# error "Unsupported architecture for atomic operations"
+#endif
+}
+
+/*
+ * Atomically decrements the contents of *a by 1, and stores the result back into *a.
+ * @return the result of the operation.
+ */
+GIT_INLINE(int) git_atomic32_dec(git_atomic32 *a)
+{
+#if defined(GIT_WIN32)
+ return InterlockedDecrement(&a->val);
+#elif defined(GIT_BUILTIN_ATOMIC)
+ return __atomic_sub_fetch(&a->val, 1, __ATOMIC_SEQ_CST);
+#elif defined(GIT_BUILTIN_SYNC)
+ return __sync_sub_and_fetch(&a->val, 1);
+#else
+# error "Unsupported architecture for atomic operations"
+#endif
+}
+
+/*
+ * Atomically gets the contents of *a.
+ * @return the contents of *a.
+ */
+GIT_INLINE(int) git_atomic32_get(git_atomic32 *a)
+{
+#if defined(GIT_WIN32)
+ return (int)InterlockedCompareExchange(&a->val, 0, 0);
+#elif defined(GIT_BUILTIN_ATOMIC)
+ return __atomic_load_n(&a->val, __ATOMIC_SEQ_CST);
+#elif defined(GIT_BUILTIN_SYNC)
+ return __sync_val_compare_and_swap(&a->val, 0, 0);
+#else
+# error "Unsupported architecture for atomic operations"
+#endif
+}
+
+GIT_INLINE(void *) git_atomic__compare_and_swap(
+ void * volatile *ptr, void *oldval, void *newval)
+{
+#if defined(GIT_WIN32)
+ return InterlockedCompareExchangePointer((volatile PVOID *)ptr, newval, oldval);
+#elif defined(GIT_BUILTIN_ATOMIC)
+ void *foundval = oldval;
+ __atomic_compare_exchange(ptr, &foundval, &newval, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
+ return foundval;
+#elif defined(GIT_BUILTIN_SYNC)
+ return __sync_val_compare_and_swap(ptr, oldval, newval);
+#else
+# error "Unsupported architecture for atomic operations"
+#endif
+}
+
+GIT_INLINE(volatile void *) git_atomic__swap(
+ void * volatile *ptr, void *newval)
+{
+#if defined(GIT_WIN32)
+ return InterlockedExchangePointer(ptr, newval);
+#elif defined(GIT_BUILTIN_ATOMIC)
+ void * foundval = NULL;
+ __atomic_exchange(ptr, &newval, &foundval, __ATOMIC_SEQ_CST);
+ return foundval;
+#elif defined(GIT_BUILTIN_SYNC)
+ return (volatile void *)__sync_lock_test_and_set(ptr, newval);
+#else
+# error "Unsupported architecture for atomic operations"
+#endif
+}
+
+GIT_INLINE(volatile void *) git_atomic__load(void * volatile *ptr)
+{
+#if defined(GIT_WIN32)
+ void *newval = NULL, *oldval = NULL;
+ return (volatile void *)InterlockedCompareExchangePointer((volatile PVOID *)ptr, newval, oldval);
+#elif defined(GIT_BUILTIN_ATOMIC)
+ return (volatile void *)__atomic_load_n(ptr, __ATOMIC_SEQ_CST);
+#elif defined(GIT_BUILTIN_SYNC)
+ return (volatile void *)__sync_val_compare_and_swap(ptr, 0, 0);
+#else
+# error "Unsupported architecture for atomic operations"
+#endif
+}
+
+#ifdef GIT_ARCH_64
+
+/*
+ * Atomically adds the contents of *a and addend, and stores the result back into *a.
+ * @return the result of the operation.
+ */
+GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend)
+{
+#if defined(GIT_WIN32)
+ return InterlockedAdd64(&a->val, addend);
+#elif defined(GIT_BUILTIN_ATOMIC)
+ return __atomic_add_fetch(&a->val, addend, __ATOMIC_SEQ_CST);
+#elif defined(GIT_BUILTIN_SYNC)
+ return __sync_add_and_fetch(&a->val, addend);
+#else
+# error "Unsupported architecture for atomic operations"
+#endif
+}
+
+/*
+ * Atomically sets the contents of *a to be val.
+ */
+GIT_INLINE(void) git_atomic64_set(git_atomic64 *a, int64_t val)
+{
+#if defined(GIT_WIN32)
+ InterlockedExchange64(&a->val, val);
+#elif defined(GIT_BUILTIN_ATOMIC)
+ __atomic_store_n(&a->val, val, __ATOMIC_SEQ_CST);
+#elif defined(GIT_BUILTIN_SYNC)
+ __sync_lock_test_and_set(&a->val, val);
+#else
+# error "Unsupported architecture for atomic operations"
+#endif
+}
+
+/*
+ * Atomically gets the contents of *a.
+ * @return the contents of *a.
+ */
+GIT_INLINE(int64_t) git_atomic64_get(git_atomic64 *a)
+{
+#if defined(GIT_WIN32)
+ return (int64_t)InterlockedCompareExchange64(&a->val, 0, 0);
+#elif defined(GIT_BUILTIN_ATOMIC)
+ return __atomic_load_n(&a->val, __ATOMIC_SEQ_CST);
+#elif defined(GIT_BUILTIN_SYNC)
+ return __sync_val_compare_and_swap(&a->val, 0, 0);
+#else
+# error "Unsupported architecture for atomic operations"
+#endif
+}
+
+#endif
+
+#else
+
+#define git_threads_global_init git__noop
+
+#define git_thread unsigned int
+#define git_thread_create(t, s, a) git__noop(t, s, a)
+#define git_thread_join(i, s) git__noop_args(i, s)
+
+/* Pthreads Mutex */
+#define git_mutex unsigned int
+#define git_mutex_init(a) git__noop_args(a)
+#define git_mutex_init(a) git__noop_args(a)
+#define git_mutex_lock(a) git__noop_args(a)
+#define git_mutex_unlock(a) git__noop_args(a)
+#define git_mutex_free(a) git__noop_args(a)
+
+/* Pthreads condition vars */
+#define git_cond unsigned int
+#define git_cond_init(c) git__noop_args(c)
+#define git_cond_free(c) git__noop_args(c)
+#define git_cond_wait(c, l) git__noop_args(c, l)
+#define git_cond_signal(c) git__noop_args(c)
+#define git_cond_broadcast(c) git__noop_args(c)
+
+/* Pthreads rwlock */
+#define git_rwlock unsigned int
+#define git_rwlock_init(a) git__noop_args(a)
+#define git_rwlock_rdlock(a) git__noop_args(a)
+#define git_rwlock_rdunlock(a) git__noop_args(a)
+#define git_rwlock_wrlock(a) git__noop_args(a)
+#define git_rwlock_wrunlock(a) git__noop_args(a)
+#define git_rwlock_free(a) git__noop_args(a)
+
+#define GIT_RWLOCK_STATIC_INIT 0
+
+
+GIT_INLINE(void) git_atomic32_set(git_atomic32 *a, int val)
+{
+ a->val = val;
+}
+
+GIT_INLINE(int) git_atomic32_inc(git_atomic32 *a)
+{
+ return ++a->val;
+}
+
+GIT_INLINE(int) git_atomic32_add(git_atomic32 *a, int32_t addend)
+{
+ a->val += addend;
+ return a->val;
+}
+
+GIT_INLINE(int) git_atomic32_dec(git_atomic32 *a)
+{
+ return --a->val;
+}
+
+GIT_INLINE(int) git_atomic32_get(git_atomic32 *a)
+{
+ return (int)a->val;
+}
+
+GIT_INLINE(void *) git_atomic__compare_and_swap(
+ void * volatile *ptr, void *oldval, void *newval)
+{
+ void *foundval = *ptr;
+ if (foundval == oldval)
+ *ptr = newval;
+ return foundval;
+}
+
+GIT_INLINE(volatile void *) git_atomic__swap(
+ void * volatile *ptr, void *newval)
+{
+ volatile void *old = *ptr;
+ *ptr = newval;
+ return old;
+}
+
+GIT_INLINE(volatile void *) git_atomic__load(void * volatile *ptr)
+{
+ return *ptr;
+}
+
+#ifdef GIT_ARCH_64
+
+GIT_INLINE(int64_t) git_atomic64_add(git_atomic64 *a, int64_t addend)
+{
+ a->val += addend;
+ return a->val;
+}
+
+GIT_INLINE(void) git_atomic64_set(git_atomic64 *a, int64_t val)
+{
+ a->val = val;
+}
+
+GIT_INLINE(int64_t) git_atomic64_get(git_atomic64 *a)
+{
+ return (int64_t)a->val;
+}
+
+#endif
+
+#endif
+
+/*
+ * Atomically replace the contents of *ptr (if they are equal to oldval) with
+ * newval. ptr must point to a pointer or a value that is the same size as a
+ * pointer. This is semantically compatible with:
+ *
+ * #define git_atomic_compare_and_swap(ptr, oldval, newval) \
+ * ({ \
+ * void *foundval = *ptr; \
+ * if (foundval == oldval) \
+ * *ptr = newval; \
+ * foundval; \
+ * })
+ *
+ * @return the original contents of *ptr.
+ */
+#define git_atomic_compare_and_swap(ptr, oldval, newval) \
+ git_atomic__compare_and_swap((void * volatile *)ptr, oldval, newval)
+
+/*
+ * Atomically replace the contents of v with newval. v must be the same size as
+ * a pointer. This is semantically compatible with:
+ *
+ * #define git_atomic_swap(v, newval) \
+ * ({ \
+ * volatile void *old = v; \
+ * v = newval; \
+ * old; \
+ * })
+ *
+ * @return the original contents of v.
+ */
+#define git_atomic_swap(v, newval) \
+ (void *)git_atomic__swap((void * volatile *)&(v), newval)
+
+/*
+ * Atomically reads the contents of v. v must be the same size as a pointer.
+ * This is semantically compatible with:
+ *
+ * #define git_atomic_load(v) v
+ *
+ * @return the contents of v.
+ */
+#define git_atomic_load(v) \
+ (void *)git_atomic__load((void * volatile *)&(v))
+
+#if defined(GIT_THREADS)
+
+# if defined(GIT_WIN32)
+# define GIT_MEMORY_BARRIER MemoryBarrier()
+# elif defined(GIT_BUILTIN_ATOMIC)
+# define GIT_MEMORY_BARRIER __atomic_thread_fence(__ATOMIC_SEQ_CST)
+# elif defined(GIT_BUILTIN_SYNC)
+# define GIT_MEMORY_BARRIER __sync_synchronize()
+# endif
+
+#else
+
+# define GIT_MEMORY_BARRIER /* noop */
+
+#endif
+
+/* Thread-local data */
+
+#if !defined(GIT_THREADS)
+# define git_tlsdata_key int
+#elif defined(GIT_WIN32)
+# define git_tlsdata_key DWORD
+#elif defined(_POSIX_THREADS)
+# define git_tlsdata_key pthread_key_t
+#else
+# error unknown threading model
+#endif
+
+/**
+ * Create a thread-local data key. The destroy function will be
+ * called upon thread exit. On some platforms, it may be called
+ * when all threads have deleted their keys.
+ *
+ * Note that the tlsdata functions do not set an error message on
+ * failure; this is because the error handling in libgit2 is itself
+ * handled by thread-local data storage.
+ *
+ * @param key the tlsdata key
+ * @param destroy_fn function pointer called upon thread exit
+ * @return 0 on success, non-zero on failure
+ */
+int git_tlsdata_init(git_tlsdata_key *key, void (GIT_SYSTEM_CALL *destroy_fn)(void *));
+
+/**
+ * Set a the thread-local value for the given key.
+ *
+ * @param key the tlsdata key to store data on
+ * @param value the pointer to store
+ * @return 0 on success, non-zero on failure
+ */
+int git_tlsdata_set(git_tlsdata_key key, void *value);
+
+/**
+ * Get the thread-local value for the given key.
+ *
+ * @param key the tlsdata key to retrieve the value of
+ * @return the pointer stored with git_tlsdata_set
+ */
+void *git_tlsdata_get(git_tlsdata_key key);
+
+/**
+ * Delete the given thread-local key.
+ *
+ * @param key the tlsdata key to dispose
+ * @return 0 on success, non-zero on failure
+ */
+int git_tlsdata_dispose(git_tlsdata_key key);
+
+#endif
diff --git a/src/util/tsort.c b/src/util/tsort.c
new file mode 100644
index 0000000..2ef03d0
--- /dev/null
+++ b/src/util/tsort.c
@@ -0,0 +1,382 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "git2_util.h"
+
+/**
+ * An array-of-pointers implementation of Python's Timsort
+ * Based on code by Christopher Swenson under the MIT license
+ *
+ * Copyright (c) 2010 Christopher Swenson
+ * Copyright (c) 2011 Vicent Marti
+ */
+
+#ifndef MAX
+# define MAX(x,y) (((x) > (y) ? (x) : (y)))
+#endif
+
+#ifndef MIN
+# define MIN(x,y) (((x) < (y) ? (x) : (y)))
+#endif
+
+static int binsearch(
+ void **dst, const void *x, size_t size, git__sort_r_cmp cmp, void *payload)
+{
+ int l, c, r;
+ void *lx, *cx;
+
+ l = 0;
+ r = (int)size - 1;
+ c = r >> 1;
+ lx = dst[l];
+
+ /* check for beginning conditions */
+ if (cmp(x, lx, payload) < 0)
+ return 0;
+
+ else if (cmp(x, lx, payload) == 0) {
+ int i = 1;
+ while (cmp(x, dst[i], payload) == 0)
+ i++;
+ return i;
+ }
+
+ /* guaranteed not to be >= rx */
+ cx = dst[c];
+ while (1) {
+ const int val = cmp(x, cx, payload);
+ if (val < 0) {
+ if (c - l <= 1) return c;
+ r = c;
+ } else if (val > 0) {
+ if (r - c <= 1) return c + 1;
+ l = c;
+ lx = cx;
+ } else {
+ do {
+ cx = dst[++c];
+ } while (cmp(x, cx, payload) == 0);
+ return c;
+ }
+ c = l + ((r - l) >> 1);
+ cx = dst[c];
+ }
+}
+
+/* Binary insertion sort, but knowing that the first "start" entries are sorted. Used in timsort. */
+static void bisort(
+ void **dst, size_t start, size_t size, git__sort_r_cmp cmp, void *payload)
+{
+ size_t i;
+ void *x;
+ int location;
+
+ for (i = start; i < size; i++) {
+ int j;
+ /* If this entry is already correct, just move along */
+ if (cmp(dst[i - 1], dst[i], payload) <= 0)
+ continue;
+
+ /* Else we need to find the right place, shift everything over, and squeeze in */
+ x = dst[i];
+ location = binsearch(dst, x, i, cmp, payload);
+ for (j = (int)i - 1; j >= location; j--) {
+ dst[j + 1] = dst[j];
+ }
+ dst[location] = x;
+ }
+}
+
+
+/* timsort implementation, based on timsort.txt */
+struct tsort_run {
+ ssize_t start;
+ ssize_t length;
+};
+
+struct tsort_store {
+ size_t alloc;
+ git__sort_r_cmp cmp;
+ void *payload;
+ void **storage;
+};
+
+static void reverse_elements(void **dst, ssize_t start, ssize_t end)
+{
+ while (start < end) {
+ void *tmp = dst[start];
+ dst[start] = dst[end];
+ dst[end] = tmp;
+
+ start++;
+ end--;
+ }
+}
+
+static ssize_t count_run(
+ void **dst, ssize_t start, ssize_t size, struct tsort_store *store)
+{
+ ssize_t curr = start + 2;
+
+ if (size - start == 1)
+ return 1;
+
+ if (start >= size - 2) {
+ if (store->cmp(dst[size - 2], dst[size - 1], store->payload) > 0) {
+ void *tmp = dst[size - 1];
+ dst[size - 1] = dst[size - 2];
+ dst[size - 2] = tmp;
+ }
+
+ return 2;
+ }
+
+ if (store->cmp(dst[start], dst[start + 1], store->payload) <= 0) {
+ while (curr < size - 1 &&
+ store->cmp(dst[curr - 1], dst[curr], store->payload) <= 0)
+ curr++;
+
+ return curr - start;
+ } else {
+ while (curr < size - 1 &&
+ store->cmp(dst[curr - 1], dst[curr], store->payload) > 0)
+ curr++;
+
+ /* reverse in-place */
+ reverse_elements(dst, start, curr - 1);
+ return curr - start;
+ }
+}
+
+static size_t compute_minrun(size_t n)
+{
+ int r = 0;
+ while (n >= 64) {
+ r |= n & 1;
+ n >>= 1;
+ }
+ return n + r;
+}
+
+static int check_invariant(struct tsort_run *stack, ssize_t stack_curr)
+{
+ if (stack_curr < 2)
+ return 1;
+
+ else if (stack_curr == 2) {
+ const ssize_t A = stack[stack_curr - 2].length;
+ const ssize_t B = stack[stack_curr - 1].length;
+ return (A > B);
+ } else {
+ const ssize_t A = stack[stack_curr - 3].length;
+ const ssize_t B = stack[stack_curr - 2].length;
+ const ssize_t C = stack[stack_curr - 1].length;
+ return !((A <= B + C) || (B <= C));
+ }
+}
+
+static int resize(struct tsort_store *store, size_t new_size)
+{
+ if (store->alloc < new_size) {
+ void **tempstore;
+
+ tempstore = git__reallocarray(store->storage, new_size, sizeof(void *));
+
+ /**
+ * Do not propagate on OOM; this will abort the sort and
+ * leave the array unsorted, but no error code will be
+ * raised
+ */
+ if (tempstore == NULL)
+ return -1;
+
+ store->storage = tempstore;
+ store->alloc = new_size;
+ }
+
+ return 0;
+}
+
+static void merge(void **dst, const struct tsort_run *stack, ssize_t stack_curr, struct tsort_store *store)
+{
+ const ssize_t A = stack[stack_curr - 2].length;
+ const ssize_t B = stack[stack_curr - 1].length;
+ const ssize_t curr = stack[stack_curr - 2].start;
+
+ void **storage;
+ ssize_t i, j, k;
+
+ if (resize(store, MIN(A, B)) < 0)
+ return;
+
+ storage = store->storage;
+
+ /* left merge */
+ if (A < B) {
+ memcpy(storage, &dst[curr], A * sizeof(void *));
+ i = 0;
+ j = curr + A;
+
+ for (k = curr; k < curr + A + B; k++) {
+ if ((i < A) && (j < curr + A + B)) {
+ if (store->cmp(storage[i], dst[j], store->payload) <= 0)
+ dst[k] = storage[i++];
+ else
+ dst[k] = dst[j++];
+ } else if (i < A) {
+ dst[k] = storage[i++];
+ } else
+ dst[k] = dst[j++];
+ }
+ } else {
+ memcpy(storage, &dst[curr + A], B * sizeof(void *));
+ i = B - 1;
+ j = curr + A - 1;
+
+ for (k = curr + A + B - 1; k >= curr; k--) {
+ if ((i >= 0) && (j >= curr)) {
+ if (store->cmp(dst[j], storage[i], store->payload) > 0)
+ dst[k] = dst[j--];
+ else
+ dst[k] = storage[i--];
+ } else if (i >= 0)
+ dst[k] = storage[i--];
+ else
+ dst[k] = dst[j--];
+ }
+ }
+}
+
+static ssize_t collapse(void **dst, struct tsort_run *stack, ssize_t stack_curr, struct tsort_store *store, ssize_t size)
+{
+ ssize_t A, B, C;
+
+ while (1) {
+ /* if the stack only has one thing on it, we are done with the collapse */
+ if (stack_curr <= 1)
+ break;
+
+ /* if this is the last merge, just do it */
+ if ((stack_curr == 2) && (stack[0].length + stack[1].length == size)) {
+ merge(dst, stack, stack_curr, store);
+ stack[0].length += stack[1].length;
+ stack_curr--;
+ break;
+ }
+
+ /* check if the invariant is off for a stack of 2 elements */
+ else if ((stack_curr == 2) && (stack[0].length <= stack[1].length)) {
+ merge(dst, stack, stack_curr, store);
+ stack[0].length += stack[1].length;
+ stack_curr--;
+ break;
+ }
+ else if (stack_curr == 2)
+ break;
+
+ A = stack[stack_curr - 3].length;
+ B = stack[stack_curr - 2].length;
+ C = stack[stack_curr - 1].length;
+
+ /* check first invariant */
+ if (A <= B + C) {
+ if (A < C) {
+ merge(dst, stack, stack_curr - 1, store);
+ stack[stack_curr - 3].length += stack[stack_curr - 2].length;
+ stack[stack_curr - 2] = stack[stack_curr - 1];
+ stack_curr--;
+ } else {
+ merge(dst, stack, stack_curr, store);
+ stack[stack_curr - 2].length += stack[stack_curr - 1].length;
+ stack_curr--;
+ }
+ } else if (B <= C) {
+ merge(dst, stack, stack_curr, store);
+ stack[stack_curr - 2].length += stack[stack_curr - 1].length;
+ stack_curr--;
+ } else
+ break;
+ }
+
+ return stack_curr;
+}
+
+#define PUSH_NEXT() do {\
+ len = count_run(dst, curr, size, store);\
+ run = minrun;\
+ if (run > (ssize_t)size - curr) run = size - curr;\
+ if (run > len) {\
+ bisort(&dst[curr], len, run, cmp, payload);\
+ len = run;\
+ }\
+ run_stack[stack_curr].start = curr;\
+ run_stack[stack_curr++].length = len;\
+ curr += len;\
+ if (curr == (ssize_t)size) {\
+ /* finish up */ \
+ while (stack_curr > 1) { \
+ merge(dst, run_stack, stack_curr, store); \
+ run_stack[stack_curr - 2].length += run_stack[stack_curr - 1].length; \
+ stack_curr--; \
+ } \
+ if (store->storage != NULL) {\
+ git__free(store->storage);\
+ store->storage = NULL;\
+ }\
+ return;\
+ }\
+}\
+while (0)
+
+void git__tsort_r(
+ void **dst, size_t size, git__sort_r_cmp cmp, void *payload)
+{
+ struct tsort_store _store, *store = &_store;
+ struct tsort_run run_stack[128];
+
+ ssize_t stack_curr = 0;
+ ssize_t len, run;
+ ssize_t curr = 0;
+ ssize_t minrun;
+
+ if (size < 64) {
+ bisort(dst, 1, size, cmp, payload);
+ return;
+ }
+
+ /* compute the minimum run length */
+ minrun = (ssize_t)compute_minrun(size);
+
+ /* temporary storage for merges */
+ store->alloc = 0;
+ store->storage = NULL;
+ store->cmp = cmp;
+ store->payload = payload;
+
+ PUSH_NEXT();
+ PUSH_NEXT();
+ PUSH_NEXT();
+
+ while (1) {
+ if (!check_invariant(run_stack, stack_curr)) {
+ stack_curr = collapse(dst, run_stack, stack_curr, store, size);
+ continue;
+ }
+
+ PUSH_NEXT();
+ }
+}
+
+static int tsort_r_cmp(const void *a, const void *b, void *payload)
+{
+ return ((git__tsort_cmp)payload)(a, b);
+}
+
+void git__tsort(void **dst, size_t size, git__tsort_cmp cmp)
+{
+ git__tsort_r(dst, size, tsort_r_cmp, cmp);
+}
diff --git a/src/util/unix/map.c b/src/util/unix/map.c
new file mode 100644
index 0000000..9330776
--- /dev/null
+++ b/src/util/unix/map.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "git2_util.h"
+
+#if !defined(GIT_WIN32) && !defined(NO_MMAP)
+
+#include "map.h"
+#include <sys/mman.h>
+#include <unistd.h>
+#include <errno.h>
+
+int git__page_size(size_t *page_size)
+{
+ long sc_page_size = sysconf(_SC_PAGE_SIZE);
+ if (sc_page_size < 0) {
+ git_error_set(GIT_ERROR_OS, "can't determine system page size");
+ return -1;
+ }
+ *page_size = (size_t) sc_page_size;
+ return 0;
+}
+
+int git__mmap_alignment(size_t *alignment)
+{
+ return git__page_size(alignment);
+}
+
+int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, off64_t offset)
+{
+ int mprot = PROT_READ;
+ int mflag = 0;
+
+ GIT_MMAP_VALIDATE(out, len, prot, flags);
+
+ out->data = NULL;
+ out->len = 0;
+
+ if (prot & GIT_PROT_WRITE)
+ mprot |= PROT_WRITE;
+
+ if ((flags & GIT_MAP_TYPE) == GIT_MAP_SHARED)
+ mflag = MAP_SHARED;
+ else if ((flags & GIT_MAP_TYPE) == GIT_MAP_PRIVATE)
+ mflag = MAP_PRIVATE;
+ else
+ mflag = MAP_SHARED;
+
+ out->data = mmap(NULL, len, mprot, mflag, fd, offset);
+
+ if (!out->data || out->data == MAP_FAILED) {
+ git_error_set(GIT_ERROR_OS, "failed to mmap. Could not write data");
+ return -1;
+ }
+
+ out->len = len;
+
+ return 0;
+}
+
+int p_munmap(git_map *map)
+{
+ GIT_ASSERT_ARG(map);
+ munmap(map->data, map->len);
+ map->data = NULL;
+ map->len = 0;
+
+ return 0;
+}
+
+#endif
+
diff --git a/src/util/unix/posix.h b/src/util/unix/posix.h
new file mode 100644
index 0000000..778477e
--- /dev/null
+++ b/src/util/unix/posix.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_unix_posix_h__
+#define INCLUDE_unix_posix_h__
+
+#include "git2_util.h"
+
+#include <stdio.h>
+#include <dirent.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+typedef int GIT_SOCKET;
+#define INVALID_SOCKET -1
+
+#define p_lseek(f,n,w) lseek(f, n, w)
+#define p_fstat(f,b) fstat(f, b)
+#define p_lstat(p,b) lstat(p,b)
+#define p_stat(p,b) stat(p, b)
+
+#if defined(GIT_USE_STAT_MTIMESPEC)
+# define st_atime_nsec st_atimespec.tv_nsec
+# define st_mtime_nsec st_mtimespec.tv_nsec
+# define st_ctime_nsec st_ctimespec.tv_nsec
+#elif defined(GIT_USE_STAT_MTIM)
+# define st_atime_nsec st_atim.tv_nsec
+# define st_mtime_nsec st_mtim.tv_nsec
+# define st_ctime_nsec st_ctim.tv_nsec
+#elif !defined(GIT_USE_STAT_MTIME_NSEC) && defined(GIT_USE_NSEC)
+# error GIT_USE_NSEC defined but unknown struct stat nanosecond type
+#endif
+
+#define p_utimes(f, t) utimes(f, t)
+
+#define p_readlink(a, b, c) readlink(a, b, c)
+#define p_symlink(o,n) symlink(o, n)
+#define p_link(o,n) link(o, n)
+#define p_unlink(p) unlink(p)
+#define p_mkdir(p,m) mkdir(p, m)
+extern char *p_realpath(const char *, char *);
+
+GIT_INLINE(int) p_fsync(int fd)
+{
+ p_fsync__cnt++;
+ return fsync(fd);
+}
+
+#define p_recv(s,b,l,f) recv(s,b,l,f)
+#define p_send(s,b,l,f) send(s,b,l,f)
+#define p_inet_pton(a, b, c) inet_pton(a, b, c)
+
+#define p_strcasecmp(s1, s2) strcasecmp(s1, s2)
+#define p_strncasecmp(s1, s2, c) strncasecmp(s1, s2, c)
+#define p_vsnprintf(b, c, f, a) vsnprintf(b, c, f, a)
+#define p_snprintf snprintf
+#define p_chdir(p) chdir(p)
+#define p_rmdir(p) rmdir(p)
+#define p_access(p,m) access(p,m)
+#define p_ftruncate(fd, sz) ftruncate(fd, sz)
+
+/*
+ * Pre-Android 5 did not implement a virtual filesystem atop FAT
+ * partitions for Unix permissions, which causes chmod to fail. However,
+ * Unix permissions have no effect on Android anyway as file permissions
+ * are not actually managed this way, so treating it as a no-op across
+ * all Android is safe.
+ */
+#ifdef __ANDROID__
+# define p_chmod(p,m) 0
+#else
+# define p_chmod(p,m) chmod(p, m)
+#endif
+
+/* see win32/posix.h for explanation about why this exists */
+#define p_lstat_posixly(p,b) lstat(p,b)
+
+#define p_localtime_r(c, r) localtime_r(c, r)
+#define p_gmtime_r(c, r) gmtime_r(c, r)
+
+#define p_timeval timeval
+
+#ifdef GIT_USE_FUTIMENS
+GIT_INLINE(int) p_futimes(int f, const struct p_timeval t[2])
+{
+ struct timespec s[2];
+ s[0].tv_sec = t[0].tv_sec;
+ s[0].tv_nsec = t[0].tv_usec * 1000;
+ s[1].tv_sec = t[1].tv_sec;
+ s[1].tv_nsec = t[1].tv_usec * 1000;
+ return futimens(f, s);
+}
+#else
+# define p_futimes futimes
+#endif
+
+#define p_pread(f, d, s, o) pread(f, d, s, o)
+#define p_pwrite(f, d, s, o) pwrite(f, d, s, o)
+
+#endif
diff --git a/src/util/unix/pthread.h b/src/util/unix/pthread.h
new file mode 100644
index 0000000..55f4ae2
--- /dev/null
+++ b/src/util/unix/pthread.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_unix_pthread_h__
+#define INCLUDE_unix_pthread_h__
+
+typedef struct {
+ pthread_t thread;
+} git_thread;
+
+GIT_INLINE(int) git_threads_global_init(void) { return 0; }
+
+#define git_thread_create(git_thread_ptr, start_routine, arg) \
+ pthread_create(&(git_thread_ptr)->thread, NULL, start_routine, arg)
+#define git_thread_join(git_thread_ptr, status) \
+ pthread_join((git_thread_ptr)->thread, status)
+#define git_thread_currentid() ((size_t)(pthread_self()))
+#define git_thread_exit(retval) pthread_exit(retval)
+
+/* Git Mutex */
+#define git_mutex pthread_mutex_t
+#define git_mutex_init(a) pthread_mutex_init(a, NULL)
+#define git_mutex_lock(a) pthread_mutex_lock(a)
+#define git_mutex_unlock(a) pthread_mutex_unlock(a)
+#define git_mutex_free(a) pthread_mutex_destroy(a)
+
+/* Git condition vars */
+#define git_cond pthread_cond_t
+#define git_cond_init(c) pthread_cond_init(c, NULL)
+#define git_cond_free(c) pthread_cond_destroy(c)
+#define git_cond_wait(c, l) pthread_cond_wait(c, l)
+#define git_cond_signal(c) pthread_cond_signal(c)
+#define git_cond_broadcast(c) pthread_cond_broadcast(c)
+
+/* Pthread (-ish) rwlock
+ *
+ * This differs from normal pthreads rwlocks in two ways:
+ * 1. Separate APIs for releasing read locks and write locks (as
+ * opposed to the pure POSIX API which only has one unlock fn)
+ * 2. You should not use recursive read locks (i.e. grabbing a read
+ * lock in a thread that already holds a read lock) because the
+ * Windows implementation doesn't support it
+ */
+#define git_rwlock pthread_rwlock_t
+#define git_rwlock_init(a) pthread_rwlock_init(a, NULL)
+#define git_rwlock_rdlock(a) pthread_rwlock_rdlock(a)
+#define git_rwlock_rdunlock(a) pthread_rwlock_unlock(a)
+#define git_rwlock_wrlock(a) pthread_rwlock_wrlock(a)
+#define git_rwlock_wrunlock(a) pthread_rwlock_unlock(a)
+#define git_rwlock_free(a) pthread_rwlock_destroy(a)
+#define GIT_RWLOCK_STATIC_INIT PTHREAD_RWLOCK_INITIALIZER
+
+#endif
diff --git a/src/util/unix/realpath.c b/src/util/unix/realpath.c
new file mode 100644
index 0000000..9e31a63
--- /dev/null
+++ b/src/util/unix/realpath.c
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "git2_util.h"
+
+#ifndef GIT_WIN32
+
+#include <limits.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+char *p_realpath(const char *pathname, char *resolved)
+{
+ char *ret;
+ if ((ret = realpath(pathname, resolved)) == NULL)
+ return NULL;
+
+#ifdef __OpenBSD__
+ /* The OpenBSD realpath function behaves differently,
+ * figure out if the file exists */
+ if (access(ret, F_OK) < 0)
+ ret = NULL;
+#endif
+ return ret;
+}
+
+#endif
diff --git a/src/util/utf8.c b/src/util/utf8.c
new file mode 100644
index 0000000..c566fdf
--- /dev/null
+++ b/src/util/utf8.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "utf8.h"
+
+#include "git2_util.h"
+
+/*
+ * git_utf8_iterate is taken from the utf8proc project,
+ * http://www.public-software-group.org/utf8proc
+ *
+ * Copyright (c) 2009 Public Software Group e. V., Berlin, Germany
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the ""Software""),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+static const uint8_t utf8proc_utf8class[256] = {
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 4, 4, 4, 4, 4, 4, 4, 4, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+static int utf8_charlen(const uint8_t *str, size_t str_len)
+{
+ uint8_t length;
+ size_t i;
+
+ length = utf8proc_utf8class[str[0]];
+ if (!length)
+ return -1;
+
+ if (str_len > 0 && length > str_len)
+ return -1;
+
+ for (i = 1; i < length; i++) {
+ if ((str[i] & 0xC0) != 0x80)
+ return -1;
+ }
+
+ return (int)length;
+}
+
+int git_utf8_iterate(uint32_t *out, const char *_str, size_t str_len)
+{
+ const uint8_t *str = (const uint8_t *)_str;
+ uint32_t uc = 0;
+ int length;
+
+ *out = 0;
+
+ if ((length = utf8_charlen(str, str_len)) < 0)
+ return -1;
+
+ switch (length) {
+ case 1:
+ uc = str[0];
+ break;
+ case 2:
+ uc = ((str[0] & 0x1F) << 6) + (str[1] & 0x3F);
+ if (uc < 0x80) uc = -1;
+ break;
+ case 3:
+ uc = ((str[0] & 0x0F) << 12) + ((str[1] & 0x3F) << 6)
+ + (str[2] & 0x3F);
+ if (uc < 0x800 || (uc >= 0xD800 && uc < 0xE000) ||
+ (uc >= 0xFDD0 && uc < 0xFDF0)) uc = -1;
+ break;
+ case 4:
+ uc = ((str[0] & 0x07) << 18) + ((str[1] & 0x3F) << 12)
+ + ((str[2] & 0x3F) << 6) + (str[3] & 0x3F);
+ if (uc < 0x10000 || uc >= 0x110000) uc = -1;
+ break;
+ default:
+ return -1;
+ }
+
+ if ((uc & 0xFFFF) >= 0xFFFE)
+ return -1;
+
+ *out = uc;
+ return length;
+}
+
+size_t git_utf8_char_length(const char *_str, size_t str_len)
+{
+ const uint8_t *str = (const uint8_t *)_str;
+ size_t offset = 0, count = 0;
+
+ while (offset < str_len) {
+ int length = utf8_charlen(str + offset, str_len - offset);
+
+ if (length < 0)
+ length = 1;
+
+ offset += length;
+ count++;
+ }
+
+ return count;
+}
+
+size_t git_utf8_valid_buf_length(const char *_str, size_t str_len)
+{
+ const uint8_t *str = (const uint8_t *)_str;
+ size_t offset = 0;
+
+ while (offset < str_len) {
+ int length = utf8_charlen(str + offset, str_len - offset);
+
+ if (length < 0)
+ break;
+
+ offset += length;
+ }
+
+ return offset;
+}
diff --git a/src/util/utf8.h b/src/util/utf8.h
new file mode 100644
index 0000000..753ab07
--- /dev/null
+++ b/src/util/utf8.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_utf8_h__
+#define INCLUDE_utf8_h__
+
+#include "git2_util.h"
+
+/*
+ * Iterate through an UTF-8 string, yielding one codepoint at a time.
+ *
+ * @param out pointer where to store the current codepoint
+ * @param str current position in the string
+ * @param str_len size left in the string
+ * @return length in bytes of the read codepoint; -1 if the codepoint was invalid
+ */
+extern int git_utf8_iterate(uint32_t *out, const char *str, size_t str_len);
+
+/**
+ * Returns the number of characters in the given string.
+ *
+ * This function will count invalid codepoints; if any given byte is
+ * not part of a valid UTF-8 codepoint, then it will be counted toward
+ * the length in characters.
+ *
+ * In other words:
+ * 0x24 (U+0024 "$") has length 1
+ * 0xc2 0xa2 (U+00A2 "¢") has length 1
+ * 0x24 0xc2 0xa2 (U+0024 U+00A2 "$¢") has length 2
+ * 0xf0 0x90 0x8d 0x88 (U+10348 "ðˆ") has length 1
+ * 0x24 0xc0 0xc1 0x34 (U+0024 <invalid> <invalid> "4) has length 4
+ *
+ * @param str string to scan
+ * @param str_len size of the string
+ * @return length in characters of the string
+ */
+extern size_t git_utf8_char_length(const char *str, size_t str_len);
+
+/**
+ * Iterate through an UTF-8 string and stops after finding any invalid UTF-8
+ * codepoints.
+ *
+ * @param str string to scan
+ * @param str_len size of the string
+ * @return length in bytes of the string that contains valid data
+ */
+extern size_t git_utf8_valid_buf_length(const char *str, size_t str_len);
+
+#endif
diff --git a/src/util/util.c b/src/util/util.c
new file mode 100644
index 0000000..c8e8303
--- /dev/null
+++ b/src/util/util.c
@@ -0,0 +1,824 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "util.h"
+
+#include "git2_util.h"
+
+#ifdef GIT_WIN32
+# include "win32/utf-conv.h"
+# include "win32/w32_buffer.h"
+
+# ifndef WIN32_LEAN_AND_MEAN
+# define WIN32_LEAN_AND_MEAN
+# endif
+# include <windows.h>
+
+# ifdef GIT_QSORT_MSC
+# include <search.h>
+# endif
+#endif
+
+#ifdef _MSC_VER
+# include <Shlwapi.h>
+#endif
+
+#if defined(hpux) || defined(__hpux) || defined(_hpux)
+# include <sys/pstat.h>
+#endif
+
+int git__strntol64(int64_t *result, const char *nptr, size_t nptr_len, const char **endptr, int base)
+{
+ const char *p;
+ int64_t n, nn, v;
+ int c, ovfl, neg, ndig;
+
+ p = nptr;
+ neg = 0;
+ n = 0;
+ ndig = 0;
+ ovfl = 0;
+
+ /*
+ * White space
+ */
+ while (nptr_len && git__isspace(*p))
+ p++, nptr_len--;
+
+ if (!nptr_len)
+ goto Return;
+
+ /*
+ * Sign
+ */
+ if (*p == '-' || *p == '+') {
+ if (*p == '-')
+ neg = 1;
+ p++;
+ nptr_len--;
+ }
+
+ if (!nptr_len)
+ goto Return;
+
+ /*
+ * Automatically detect the base if none was given to us.
+ * Right now, we assume that a number starting with '0x'
+ * is hexadecimal and a number starting with '0' is
+ * octal.
+ */
+ if (base == 0) {
+ if (*p != '0')
+ base = 10;
+ else if (nptr_len > 2 && (p[1] == 'x' || p[1] == 'X'))
+ base = 16;
+ else
+ base = 8;
+ }
+
+ if (base < 0 || 36 < base)
+ goto Return;
+
+ /*
+ * Skip prefix of '0x'-prefixed hexadecimal numbers. There is no
+ * need to do the same for '0'-prefixed octal numbers as a
+ * leading '0' does not have any impact. Also, if we skip a
+ * leading '0' in such a string, then we may end up with no
+ * digits left and produce an error later on which isn't one.
+ */
+ if (base == 16 && nptr_len > 2 && p[0] == '0' && (p[1] == 'x' || p[1] == 'X')) {
+ p += 2;
+ nptr_len -= 2;
+ }
+
+ /*
+ * Non-empty sequence of digits
+ */
+ for (; nptr_len > 0; p++,ndig++,nptr_len--) {
+ c = *p;
+ v = base;
+ if ('0'<=c && c<='9')
+ v = c - '0';
+ else if ('a'<=c && c<='z')
+ v = c - 'a' + 10;
+ else if ('A'<=c && c<='Z')
+ v = c - 'A' + 10;
+ if (v >= base)
+ break;
+ v = neg ? -v : v;
+ if (git__multiply_int64_overflow(&nn, n, base) || git__add_int64_overflow(&n, nn, v)) {
+ ovfl = 1;
+ /* Keep on iterating until the end of this number */
+ continue;
+ }
+ }
+
+Return:
+ if (ndig == 0) {
+ git_error_set(GIT_ERROR_INVALID, "failed to convert string to long: not a number");
+ return -1;
+ }
+
+ if (endptr)
+ *endptr = p;
+
+ if (ovfl) {
+ git_error_set(GIT_ERROR_INVALID, "failed to convert string to long: overflow error");
+ return -1;
+ }
+
+ *result = n;
+ return 0;
+}
+
+int git__strntol32(int32_t *result, const char *nptr, size_t nptr_len, const char **endptr, int base)
+{
+ const char *tmp_endptr;
+ int32_t tmp_int;
+ int64_t tmp_long;
+ int error;
+
+ if ((error = git__strntol64(&tmp_long, nptr, nptr_len, &tmp_endptr, base)) < 0)
+ return error;
+
+ tmp_int = tmp_long & 0xFFFFFFFF;
+ if (tmp_int != tmp_long) {
+ int len = (int)(tmp_endptr - nptr);
+ git_error_set(GIT_ERROR_INVALID, "failed to convert: '%.*s' is too large", len, nptr);
+ return -1;
+ }
+
+ *result = tmp_int;
+ if (endptr)
+ *endptr = tmp_endptr;
+
+ return error;
+}
+
+int git__strcasecmp(const char *a, const char *b)
+{
+ while (*a && *b && git__tolower(*a) == git__tolower(*b))
+ ++a, ++b;
+ return ((unsigned char)git__tolower(*a) - (unsigned char)git__tolower(*b));
+}
+
+int git__strcasesort_cmp(const char *a, const char *b)
+{
+ int cmp = 0;
+
+ while (*a && *b) {
+ if (*a != *b) {
+ if (git__tolower(*a) != git__tolower(*b))
+ break;
+ /* use case in sort order even if not in equivalence */
+ if (!cmp)
+ cmp = (int)(*(const uint8_t *)a) - (int)(*(const uint8_t *)b);
+ }
+
+ ++a, ++b;
+ }
+
+ if (*a || *b)
+ return (unsigned char)git__tolower(*a) - (unsigned char)git__tolower(*b);
+
+ return cmp;
+}
+
+int git__strncasecmp(const char *a, const char *b, size_t sz)
+{
+ int al, bl;
+
+ do {
+ al = (unsigned char)git__tolower(*a);
+ bl = (unsigned char)git__tolower(*b);
+ ++a, ++b;
+ } while (--sz && al && al == bl);
+
+ return al - bl;
+}
+
+void git__strntolower(char *str, size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len; ++i) {
+ str[i] = (char)git__tolower(str[i]);
+ }
+}
+
+void git__strtolower(char *str)
+{
+ git__strntolower(str, strlen(str));
+}
+
+GIT_INLINE(int) prefixcmp(const char *str, size_t str_n, const char *prefix, bool icase)
+{
+ int s, p;
+
+ while (str_n--) {
+ s = (unsigned char)*str++;
+ p = (unsigned char)*prefix++;
+
+ if (icase) {
+ s = git__tolower(s);
+ p = git__tolower(p);
+ }
+
+ if (!p)
+ return 0;
+
+ if (s != p)
+ return s - p;
+ }
+
+ return (0 - *prefix);
+}
+
+int git__prefixcmp(const char *str, const char *prefix)
+{
+ unsigned char s, p;
+
+ while (1) {
+ p = *prefix++;
+ s = *str++;
+
+ if (!p)
+ return 0;
+
+ if (s != p)
+ return s - p;
+ }
+}
+
+int git__prefixncmp(const char *str, size_t str_n, const char *prefix)
+{
+ return prefixcmp(str, str_n, prefix, false);
+}
+
+int git__prefixcmp_icase(const char *str, const char *prefix)
+{
+ return prefixcmp(str, SIZE_MAX, prefix, true);
+}
+
+int git__prefixncmp_icase(const char *str, size_t str_n, const char *prefix)
+{
+ return prefixcmp(str, str_n, prefix, true);
+}
+
+int git__suffixcmp(const char *str, const char *suffix)
+{
+ size_t a = strlen(str);
+ size_t b = strlen(suffix);
+ if (a < b)
+ return -1;
+ return strcmp(str + (a - b), suffix);
+}
+
+char *git__strtok(char **end, const char *sep)
+{
+ char *ptr = *end;
+
+ while (*ptr && strchr(sep, *ptr))
+ ++ptr;
+
+ if (*ptr) {
+ char *start = ptr;
+ *end = start + 1;
+
+ while (**end && !strchr(sep, **end))
+ ++*end;
+
+ if (**end) {
+ **end = '\0';
+ ++*end;
+ }
+
+ return start;
+ }
+
+ return NULL;
+}
+
+/* Similar to strtok, but does not collapse repeated tokens. */
+char *git__strsep(char **end, const char *sep)
+{
+ char *start = *end, *ptr = *end;
+
+ while (*ptr && !strchr(sep, *ptr))
+ ++ptr;
+
+ if (*ptr) {
+ *end = ptr + 1;
+ *ptr = '\0';
+
+ return start;
+ }
+
+ return NULL;
+}
+
+size_t git__linenlen(const char *buffer, size_t buffer_len)
+{
+ char *nl = memchr(buffer, '\n', buffer_len);
+ return nl ? (size_t)(nl - buffer) + 1 : buffer_len;
+}
+
+/*
+ * Adapted Not So Naive algorithm from http://www-igm.univ-mlv.fr/~lecroq/string/
+ */
+const void * git__memmem(const void *haystack, size_t haystacklen,
+ const void *needle, size_t needlelen)
+{
+ const char *h, *n;
+ size_t j, k, l;
+
+ if (needlelen > haystacklen || !haystacklen || !needlelen)
+ return NULL;
+
+ h = (const char *) haystack,
+ n = (const char *) needle;
+
+ if (needlelen == 1)
+ return memchr(haystack, *n, haystacklen);
+
+ if (n[0] == n[1]) {
+ k = 2;
+ l = 1;
+ } else {
+ k = 1;
+ l = 2;
+ }
+
+ j = 0;
+ while (j <= haystacklen - needlelen) {
+ if (n[1] != h[j + 1]) {
+ j += k;
+ } else {
+ if (memcmp(n + 2, h + j + 2, needlelen - 2) == 0 &&
+ n[0] == h[j])
+ return h + j;
+ j += l;
+ }
+ }
+
+ return NULL;
+}
+
+void git__hexdump(const char *buffer, size_t len)
+{
+ static const size_t LINE_WIDTH = 16;
+
+ size_t line_count, last_line, i, j;
+ const char *line;
+
+ line_count = (len / LINE_WIDTH);
+ last_line = (len % LINE_WIDTH);
+
+ for (i = 0; i < line_count; ++i) {
+ printf("%08" PRIxZ " ", (i * LINE_WIDTH));
+
+ line = buffer + (i * LINE_WIDTH);
+ for (j = 0; j < LINE_WIDTH; ++j, ++line) {
+ printf("%02x ", (unsigned char)*line & 0xFF);
+
+ if (j == (LINE_WIDTH / 2))
+ printf(" ");
+ }
+
+ printf(" |");
+
+ line = buffer + (i * LINE_WIDTH);
+ for (j = 0; j < LINE_WIDTH; ++j, ++line)
+ printf("%c", (*line >= 32 && *line <= 126) ? *line : '.');
+
+ printf("|\n");
+ }
+
+ if (last_line > 0) {
+ printf("%08" PRIxZ " ", (line_count * LINE_WIDTH));
+
+ line = buffer + (line_count * LINE_WIDTH);
+ for (j = 0; j < last_line; ++j, ++line) {
+ printf("%02x ", (unsigned char)*line & 0xFF);
+
+ if (j == (LINE_WIDTH / 2))
+ printf(" ");
+ }
+
+ if (j < (LINE_WIDTH / 2))
+ printf(" ");
+ for (j = 0; j < (LINE_WIDTH - last_line); ++j)
+ printf(" ");
+
+ printf(" |");
+
+ line = buffer + (line_count * LINE_WIDTH);
+ for (j = 0; j < last_line; ++j, ++line)
+ printf("%c", (*line >= 32 && *line <= 126) ? *line : '.');
+
+ printf("|\n");
+ }
+
+ printf("\n");
+}
+
+#ifdef GIT_LEGACY_HASH
+uint32_t git__hash(const void *key, int len, unsigned int seed)
+{
+ const uint32_t m = 0x5bd1e995;
+ const int r = 24;
+ uint32_t h = seed ^ len;
+
+ const unsigned char *data = (const unsigned char *)key;
+
+ while(len >= 4) {
+ uint32_t k = *(uint32_t *)data;
+
+ k *= m;
+ k ^= k >> r;
+ k *= m;
+
+ h *= m;
+ h ^= k;
+
+ data += 4;
+ len -= 4;
+ }
+
+ switch(len) {
+ case 3: h ^= data[2] << 16;
+ case 2: h ^= data[1] << 8;
+ case 1: h ^= data[0];
+ h *= m;
+ };
+
+ h ^= h >> 13;
+ h *= m;
+ h ^= h >> 15;
+
+ return h;
+}
+#else
+/*
+ Cross-platform version of Murmurhash3
+ http://code.google.com/p/smhasher/wiki/MurmurHash3
+ by Austin Appleby (aappleby@gmail.com)
+
+ This code is on the public domain.
+*/
+uint32_t git__hash(const void *key, int len, uint32_t seed)
+{
+
+#define MURMUR_BLOCK() {\
+ k1 *= c1; \
+ k1 = git__rotl(k1,11);\
+ k1 *= c2;\
+ h1 ^= k1;\
+ h1 = h1*3 + 0x52dce729;\
+ c1 = c1*5 + 0x7b7d159c;\
+ c2 = c2*5 + 0x6bce6396;\
+}
+
+ const uint8_t *data = (const uint8_t*)key;
+ const int nblocks = len / 4;
+
+ const uint32_t *blocks = (const uint32_t *)(data + nblocks * 4);
+ const uint8_t *tail = (const uint8_t *)(data + nblocks * 4);
+
+ uint32_t h1 = 0x971e137b ^ seed;
+ uint32_t k1;
+
+ uint32_t c1 = 0x95543787;
+ uint32_t c2 = 0x2ad7eb25;
+
+ int i;
+
+ for (i = -nblocks; i; i++) {
+ k1 = blocks[i];
+ MURMUR_BLOCK();
+ }
+
+ k1 = 0;
+
+ switch(len & 3) {
+ case 3: k1 ^= tail[2] << 16;
+ /* fall through */
+ case 2: k1 ^= tail[1] << 8;
+ /* fall through */
+ case 1: k1 ^= tail[0];
+ MURMUR_BLOCK();
+ }
+
+ h1 ^= len;
+ h1 ^= h1 >> 16;
+ h1 *= 0x85ebca6b;
+ h1 ^= h1 >> 13;
+ h1 *= 0xc2b2ae35;
+ h1 ^= h1 >> 16;
+
+ return h1;
+}
+#endif
+
+/**
+ * A modified `bsearch` from the BSD glibc.
+ *
+ * Copyright (c) 1990 Regents of the University of California.
+ * All rights reserved.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. [rescinded 22 July 1999]
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+int git__bsearch(
+ void **array,
+ size_t array_len,
+ const void *key,
+ int (*compare)(const void *, const void *),
+ size_t *position)
+{
+ size_t lim;
+ int cmp = -1;
+ void **part, **base = array;
+
+ for (lim = array_len; lim != 0; lim >>= 1) {
+ part = base + (lim >> 1);
+ cmp = (*compare)(key, *part);
+ if (cmp == 0) {
+ base = part;
+ break;
+ }
+ if (cmp > 0) { /* key > p; take right partition */
+ base = part + 1;
+ lim--;
+ } /* else take left partition */
+ }
+
+ if (position)
+ *position = (base - array);
+
+ return (cmp == 0) ? 0 : GIT_ENOTFOUND;
+}
+
+int git__bsearch_r(
+ void **array,
+ size_t array_len,
+ const void *key,
+ int (*compare_r)(const void *, const void *, void *),
+ void *payload,
+ size_t *position)
+{
+ size_t lim;
+ int cmp = -1;
+ void **part, **base = array;
+
+ for (lim = array_len; lim != 0; lim >>= 1) {
+ part = base + (lim >> 1);
+ cmp = (*compare_r)(key, *part, payload);
+ if (cmp == 0) {
+ base = part;
+ break;
+ }
+ if (cmp > 0) { /* key > p; take right partition */
+ base = part + 1;
+ lim--;
+ } /* else take left partition */
+ }
+
+ if (position)
+ *position = (base - array);
+
+ return (cmp == 0) ? 0 : GIT_ENOTFOUND;
+}
+
+/**
+ * A strcmp wrapper
+ *
+ * We don't want direct pointers to the CRT on Windows, we may
+ * get stdcall conflicts.
+ */
+int git__strcmp_cb(const void *a, const void *b)
+{
+ return strcmp((const char *)a, (const char *)b);
+}
+
+int git__strcasecmp_cb(const void *a, const void *b)
+{
+ return strcasecmp((const char *)a, (const char *)b);
+}
+
+int git__parse_bool(int *out, const char *value)
+{
+ /* A missing value means true */
+ if (value == NULL ||
+ !strcasecmp(value, "true") ||
+ !strcasecmp(value, "yes") ||
+ !strcasecmp(value, "on")) {
+ *out = 1;
+ return 0;
+ }
+ if (!strcasecmp(value, "false") ||
+ !strcasecmp(value, "no") ||
+ !strcasecmp(value, "off") ||
+ value[0] == '\0') {
+ *out = 0;
+ return 0;
+ }
+
+ return -1;
+}
+
+size_t git__unescape(char *str)
+{
+ char *scan, *pos = str;
+
+ if (!str)
+ return 0;
+
+ for (scan = str; *scan; pos++, scan++) {
+ if (*scan == '\\' && *(scan + 1) != '\0')
+ scan++; /* skip '\' but include next char */
+ if (pos != scan)
+ *pos = *scan;
+ }
+
+ if (pos != scan) {
+ *pos = '\0';
+ }
+
+ return (pos - str);
+}
+
+#if defined(GIT_QSORT_MSC) || defined(GIT_QSORT_BSD)
+typedef struct {
+ git__sort_r_cmp cmp;
+ void *payload;
+} git__qsort_r_glue;
+
+static int GIT_LIBGIT2_CALL git__qsort_r_glue_cmp(
+ void *payload, const void *a, const void *b)
+{
+ git__qsort_r_glue *glue = payload;
+ return glue->cmp(a, b, glue->payload);
+}
+#endif
+
+
+#if !defined(GIT_QSORT_BSD) && \
+ !defined(GIT_QSORT_GNU) && \
+ !defined(GIT_QSORT_C11) && \
+ !defined(GIT_QSORT_MSC)
+
+static void swap(uint8_t *a, uint8_t *b, size_t elsize)
+{
+ char tmp[256];
+
+ while (elsize) {
+ size_t n = elsize < sizeof(tmp) ? elsize : sizeof(tmp);
+ memcpy(tmp, a + elsize - n, n);
+ memcpy(a + elsize - n, b + elsize - n, n);
+ memcpy(b + elsize - n, tmp, n);
+ elsize -= n;
+ }
+}
+
+static void insertsort(
+ void *els, size_t nel, size_t elsize,
+ git__sort_r_cmp cmp, void *payload)
+{
+ uint8_t *base = els;
+ uint8_t *end = base + nel * elsize;
+ uint8_t *i, *j;
+
+ for (i = base + elsize; i < end; i += elsize)
+ for (j = i; j > base && cmp(j, j - elsize, payload) < 0; j -= elsize)
+ swap(j, j - elsize, elsize);
+}
+
+#endif
+
+void git__qsort_r(
+ void *els, size_t nel, size_t elsize, git__sort_r_cmp cmp, void *payload)
+{
+#if defined(GIT_QSORT_GNU)
+ qsort_r(els, nel, elsize, cmp, payload);
+#elif defined(GIT_QSORT_C11)
+ qsort_s(els, nel, elsize, cmp, payload);
+#elif defined(GIT_QSORT_BSD)
+ git__qsort_r_glue glue = { cmp, payload };
+ qsort_r(els, nel, elsize, &glue, git__qsort_r_glue_cmp);
+#elif defined(GIT_QSORT_MSC)
+ git__qsort_r_glue glue = { cmp, payload };
+ qsort_s(els, nel, elsize, git__qsort_r_glue_cmp, &glue);
+#else
+ insertsort(els, nel, elsize, cmp, payload);
+#endif
+}
+
+#ifdef GIT_WIN32
+int git__getenv(git_str *out, const char *name)
+{
+ wchar_t *wide_name = NULL, *wide_value = NULL;
+ DWORD value_len;
+ int error = -1;
+
+ git_str_clear(out);
+
+ if (git_utf8_to_16_alloc(&wide_name, name) < 0)
+ return -1;
+
+ if ((value_len = GetEnvironmentVariableW(wide_name, NULL, 0)) > 0) {
+ wide_value = git__malloc(value_len * sizeof(wchar_t));
+ GIT_ERROR_CHECK_ALLOC(wide_value);
+
+ value_len = GetEnvironmentVariableW(wide_name, wide_value, value_len);
+ }
+
+ if (value_len)
+ error = git_str_put_w(out, wide_value, value_len);
+ else if (GetLastError() == ERROR_SUCCESS || GetLastError() == ERROR_ENVVAR_NOT_FOUND)
+ error = GIT_ENOTFOUND;
+ else
+ git_error_set(GIT_ERROR_OS, "could not read environment variable '%s'", name);
+
+ git__free(wide_name);
+ git__free(wide_value);
+ return error;
+}
+#else
+int git__getenv(git_str *out, const char *name)
+{
+ const char *val = getenv(name);
+
+ git_str_clear(out);
+
+ if (!val)
+ return GIT_ENOTFOUND;
+
+ return git_str_puts(out, val);
+}
+#endif
+
+/*
+ * By doing this in two steps we can at least get
+ * the function to be somewhat coherent, even
+ * with this disgusting nest of #ifdefs.
+ */
+#ifndef _SC_NPROCESSORS_ONLN
+# ifdef _SC_NPROC_ONLN
+# define _SC_NPROCESSORS_ONLN _SC_NPROC_ONLN
+# elif defined _SC_CRAY_NCPU
+# define _SC_NPROCESSORS_ONLN _SC_CRAY_NCPU
+# endif
+#endif
+
+int git__online_cpus(void)
+{
+#ifdef _SC_NPROCESSORS_ONLN
+ long ncpus;
+#endif
+
+#ifdef _WIN32
+ SYSTEM_INFO info;
+ GetSystemInfo(&info);
+
+ if ((int)info.dwNumberOfProcessors > 0)
+ return (int)info.dwNumberOfProcessors;
+#elif defined(hpux) || defined(__hpux) || defined(_hpux)
+ struct pst_dynamic psd;
+
+ if (!pstat_getdynamic(&psd, sizeof(psd), (size_t)1, 0))
+ return (int)psd.psd_proc_cnt;
+#endif
+
+#ifdef _SC_NPROCESSORS_ONLN
+ if ((ncpus = (long)sysconf(_SC_NPROCESSORS_ONLN)) > 0)
+ return (int)ncpus;
+#endif
+
+ return 1;
+}
diff --git a/src/util/util.h b/src/util/util.h
new file mode 100644
index 0000000..7f178b1
--- /dev/null
+++ b/src/util/util.h
@@ -0,0 +1,396 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_util_h__
+#define INCLUDE_util_h__
+
+#ifndef GIT_WIN32
+# include <ctype.h>
+#endif
+
+#include "str.h"
+#include "git2_util.h"
+#include "strnlen.h"
+#include "thread.h"
+
+#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
+#define bitsizeof(x) (CHAR_BIT * sizeof(x))
+#define MSB(x, bits) ((x) & (~UINT64_C(0) << (bitsizeof(x) - (bits))))
+#ifndef min
+# define min(a,b) ((a) < (b) ? (a) : (b))
+#endif
+#ifndef max
+# define max(a,b) ((a) > (b) ? (a) : (b))
+#endif
+
+#if defined(__GNUC__)
+# define GIT_CONTAINER_OF(ptr, type, member) \
+ __builtin_choose_expr( \
+ __builtin_offsetof(type, member) == 0 && \
+ __builtin_types_compatible_p(__typeof__(&((type *) 0)->member), __typeof__(ptr)), \
+ ((type *) (ptr)), \
+ (void)0)
+#else
+# define GIT_CONTAINER_OF(ptr, type, member) (type *)(ptr)
+#endif
+
+/**
+ * Return the length of a constant string.
+ * We are aware that `strlen` performs the same task and is usually
+ * optimized away by the compiler, whilst being safer because it returns
+ * valid values when passed a pointer instead of a constant string; however
+ * this macro will transparently work with wide-char and single-char strings.
+ */
+#define CONST_STRLEN(x) ((sizeof(x)/sizeof(x[0])) - 1)
+
+#define STRCMP_CASESELECT(IGNORE_CASE, STR1, STR2) \
+ ((IGNORE_CASE) ? strcasecmp((STR1), (STR2)) : strcmp((STR1), (STR2)))
+
+#define CASESELECT(IGNORE_CASE, ICASE, CASE) \
+ ((IGNORE_CASE) ? (ICASE) : (CASE))
+
+extern int git__prefixcmp(const char *str, const char *prefix);
+extern int git__prefixcmp_icase(const char *str, const char *prefix);
+extern int git__prefixncmp(const char *str, size_t str_n, const char *prefix);
+extern int git__prefixncmp_icase(const char *str, size_t str_n, const char *prefix);
+extern int git__suffixcmp(const char *str, const char *suffix);
+
+GIT_INLINE(int) git__signum(int val)
+{
+ return ((val > 0) - (val < 0));
+}
+
+extern int git__strntol32(int32_t *n, const char *buff, size_t buff_len, const char **end_buf, int base);
+extern int git__strntol64(int64_t *n, const char *buff, size_t buff_len, const char **end_buf, int base);
+
+
+extern void git__hexdump(const char *buffer, size_t n);
+extern uint32_t git__hash(const void *key, int len, uint32_t seed);
+
+/* 32-bit cross-platform rotl */
+#ifdef _MSC_VER /* use built-in method in MSVC */
+# define git__rotl(v, s) (uint32_t)_rotl(v, s)
+#else /* use bitops in GCC; with o2 this gets optimized to a rotl instruction */
+# define git__rotl(v, s) (uint32_t)(((uint32_t)(v) << (s)) | ((uint32_t)(v) >> (32 - (s))))
+#endif
+
+extern char *git__strtok(char **end, const char *sep);
+extern char *git__strsep(char **end, const char *sep);
+
+extern void git__strntolower(char *str, size_t len);
+extern void git__strtolower(char *str);
+
+#ifdef GIT_WIN32
+GIT_INLINE(int) git__tolower(int c)
+{
+ return (c >= 'A' && c <= 'Z') ? (c + 32) : c;
+}
+#else
+# define git__tolower(a) tolower(a)
+#endif
+
+extern size_t git__linenlen(const char *buffer, size_t buffer_len);
+
+GIT_INLINE(const char *) git__next_line(const char *s)
+{
+ while (*s && *s != '\n') s++;
+ while (*s == '\n' || *s == '\r') s++;
+ return s;
+}
+
+GIT_INLINE(const void *) git__memrchr(const void *s, int c, size_t n)
+{
+ const unsigned char *cp;
+
+ if (n != 0) {
+ cp = (unsigned char *)s + n;
+ do {
+ if (*(--cp) == (unsigned char)c)
+ return cp;
+ } while (--n != 0);
+ }
+
+ return NULL;
+}
+
+extern const void * git__memmem(const void *haystack, size_t haystacklen,
+ const void *needle, size_t needlelen);
+
+typedef int (*git__tsort_cmp)(const void *a, const void *b);
+
+extern void git__tsort(void **dst, size_t size, git__tsort_cmp cmp);
+
+typedef int (*git__sort_r_cmp)(const void *a, const void *b, void *payload);
+
+extern void git__tsort_r(
+ void **dst, size_t size, git__sort_r_cmp cmp, void *payload);
+
+extern void git__qsort_r(
+ void *els, size_t nel, size_t elsize, git__sort_r_cmp cmp, void *payload);
+
+/**
+ * @param position If non-NULL, this will be set to the position where the
+ * element is or would be inserted if not found.
+ * @return 0 if found; GIT_ENOTFOUND if not found
+ */
+extern int git__bsearch(
+ void **array,
+ size_t array_len,
+ const void *key,
+ int (*compare)(const void *key, const void *element),
+ size_t *position);
+
+extern int git__bsearch_r(
+ void **array,
+ size_t array_len,
+ const void *key,
+ int (*compare_r)(const void *key, const void *element, void *payload),
+ void *payload,
+ size_t *position);
+
+#define git__strcmp strcmp
+#define git__strncmp strncmp
+
+extern int git__strcmp_cb(const void *a, const void *b);
+extern int git__strcasecmp_cb(const void *a, const void *b);
+
+extern int git__strcasecmp(const char *a, const char *b);
+extern int git__strncasecmp(const char *a, const char *b, size_t sz);
+
+extern int git__strcasesort_cmp(const char *a, const char *b);
+
+/*
+ * Compare some NUL-terminated `a` to a possibly non-NUL terminated
+ * `b` of length `b_len`; like `strncmp` but ensuring that
+ * `strlen(a) == b_len` as well.
+ */
+GIT_INLINE(int) git__strlcmp(const char *a, const char *b, size_t b_len)
+{
+ int cmp = strncmp(a, b, b_len);
+ return cmp ? cmp : (int)a[b_len];
+}
+
+typedef struct {
+ git_atomic32 refcount;
+ void *owner;
+} git_refcount;
+
+typedef void (*git_refcount_freeptr)(void *r);
+
+#define GIT_REFCOUNT_INC(r) { \
+ git_atomic32_inc(&(r)->rc.refcount); \
+}
+
+#define GIT_REFCOUNT_DEC(_r, do_free) { \
+ git_refcount *r = &(_r)->rc; \
+ int val = git_atomic32_dec(&r->refcount); \
+ if (val <= 0 && r->owner == NULL) { do_free(_r); } \
+}
+
+#define GIT_REFCOUNT_OWN(r, o) { \
+ (void)git_atomic_swap((r)->rc.owner, o); \
+}
+
+#define GIT_REFCOUNT_OWNER(r) git_atomic_load((r)->rc.owner)
+
+#define GIT_REFCOUNT_VAL(r) git_atomic32_get((r)->rc.refcount)
+
+
+static signed char from_hex[] = {
+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 00 */
+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 10 */
+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 20 */
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, /* 30 */
+-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 40 */
+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 50 */
+-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 60 */
+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 70 */
+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 80 */
+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* 90 */
+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* a0 */
+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* b0 */
+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* c0 */
+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* d0 */
+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* e0 */
+-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, /* f0 */
+};
+
+GIT_INLINE(int) git__fromhex(char h)
+{
+ return from_hex[(unsigned char) h];
+}
+
+GIT_INLINE(int) git__ishex(const char *str)
+{
+ unsigned i;
+ for (i=0; str[i] != '\0'; i++)
+ if (git__fromhex(str[i]) < 0)
+ return 0;
+ return 1;
+}
+
+GIT_INLINE(size_t) git__size_t_bitmask(size_t v)
+{
+ v--;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+
+ return v;
+}
+
+GIT_INLINE(size_t) git__size_t_powerof2(size_t v)
+{
+ return git__size_t_bitmask(v) + 1;
+}
+
+GIT_INLINE(bool) git__isupper(int c)
+{
+ return (c >= 'A' && c <= 'Z');
+}
+
+GIT_INLINE(bool) git__isalpha(int c)
+{
+ return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'));
+}
+
+GIT_INLINE(bool) git__isdigit(int c)
+{
+ return (c >= '0' && c <= '9');
+}
+
+GIT_INLINE(bool) git__isspace(int c)
+{
+ return (c == ' ' || c == '\t' || c == '\n' || c == '\f' || c == '\r' || c == '\v');
+}
+
+GIT_INLINE(bool) git__isspace_nonlf(int c)
+{
+ return (c == ' ' || c == '\t' || c == '\f' || c == '\r' || c == '\v');
+}
+
+GIT_INLINE(bool) git__iswildcard(int c)
+{
+ return (c == '*' || c == '?' || c == '[');
+}
+
+GIT_INLINE(bool) git__isxdigit(int c)
+{
+ return ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
+}
+
+/*
+ * Parse a string value as a boolean, just like Core Git does.
+ *
+ * Valid values for true are: 'true', 'yes', 'on'
+ * Valid values for false are: 'false', 'no', 'off'
+ */
+extern int git__parse_bool(int *out, const char *value);
+
+/*
+ * Unescapes a string in-place.
+ *
+ * Edge cases behavior:
+ * - "jackie\" -> "jacky\"
+ * - "chan\\" -> "chan\"
+ */
+extern size_t git__unescape(char *str);
+
+/*
+ * Safely zero-out memory, making sure that the compiler
+ * doesn't optimize away the operation.
+ */
+GIT_INLINE(void) git__memzero(void *data, size_t size)
+{
+#ifdef _MSC_VER
+ SecureZeroMemory((PVOID)data, size);
+#else
+ volatile uint8_t *scan = (volatile uint8_t *)data;
+
+ while (size--)
+ *scan++ = 0x0;
+#endif
+}
+
+#ifdef GIT_WIN32
+
+GIT_INLINE(uint64_t) git_time_monotonic(void)
+{
+ /* GetTickCount64 returns the number of milliseconds that have
+ * elapsed since the system was started. */
+ return GetTickCount64();
+}
+
+#elif __APPLE__
+
+#include <mach/mach_time.h>
+#include <sys/time.h>
+
+GIT_INLINE(uint64_t) git_time_monotonic(void)
+{
+ static double scaling_factor = 0;
+
+ if (scaling_factor == 0) {
+ mach_timebase_info_data_t info;
+
+ scaling_factor = mach_timebase_info(&info) == KERN_SUCCESS ?
+ ((double)info.numer / (double)info.denom) / 1.0E6 :
+ -1;
+ } else if (scaling_factor < 0) {
+ struct timeval tv;
+
+ /* mach_timebase_info failed; fall back to gettimeofday */
+ gettimeofday(&tv, NULL);
+ return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+ }
+
+ return (uint64_t)(mach_absolute_time() * scaling_factor);
+}
+
+#elif defined(__amigaos4__)
+
+#include <proto/timer.h>
+
+GIT_INLINE(uint64_t) git_time_monotonic(void)
+{
+ struct TimeVal tv;
+ ITimer->GetUpTime(&tv);
+ return (tv.Seconds * 1000) + (tv.Microseconds / 1000);
+}
+
+#else
+
+#include <sys/time.h>
+
+GIT_INLINE(uint64_t) git_time_monotonic(void)
+{
+ struct timeval tv;
+
+#ifdef CLOCK_MONOTONIC
+ struct timespec tp;
+ if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0)
+ return (tp.tv_sec * 1000) + (tp.tv_nsec / 1.0E6);
+#endif
+
+ /* Fall back to using gettimeofday */
+ gettimeofday(&tv, NULL);
+ return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
+}
+
+#endif
+
+extern int git__getenv(git_str *out, const char *name);
+
+extern int git__online_cpus(void);
+
+GIT_INLINE(int) git__noop(void) { return 0; }
+GIT_INLINE(int) git__noop_args(void *a, ...) { GIT_UNUSED(a); return 0; }
+
+#include "alloc.h"
+
+#endif
diff --git a/src/util/varint.c b/src/util/varint.c
new file mode 100644
index 0000000..9ffc1d7
--- /dev/null
+++ b/src/util/varint.c
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "varint.h"
+
+uintmax_t git_decode_varint(const unsigned char *bufp, size_t *varint_len)
+{
+ const unsigned char *buf = bufp;
+ unsigned char c = *buf++;
+ uintmax_t val = c & 127;
+ while (c & 128) {
+ val += 1;
+ if (!val || MSB(val, 7)) {
+ /* This is not a valid varint_len, so it signals
+ the error */
+ *varint_len = 0;
+ return 0; /* overflow */
+ }
+ c = *buf++;
+ val = (val << 7) + (c & 127);
+ }
+ *varint_len = buf - bufp;
+ return val;
+}
+
+int git_encode_varint(unsigned char *buf, size_t bufsize, uintmax_t value)
+{
+ unsigned char varint[16];
+ unsigned pos = sizeof(varint) - 1;
+ varint[pos] = value & 127;
+ while (value >>= 7)
+ varint[--pos] = 128 | (--value & 127);
+ if (buf) {
+ if (bufsize < (sizeof(varint) - pos))
+ return -1;
+ memcpy(buf, varint + pos, sizeof(varint) - pos);
+ }
+ return sizeof(varint) - pos;
+}
diff --git a/src/util/varint.h b/src/util/varint.h
new file mode 100644
index 0000000..79b8f55
--- /dev/null
+++ b/src/util/varint.h
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_varint_h__
+#define INCLUDE_varint_h__
+
+#include "git2_util.h"
+
+#include <stdint.h>
+
+extern int git_encode_varint(unsigned char *, size_t, uintmax_t);
+extern uintmax_t git_decode_varint(const unsigned char *, size_t *);
+
+#endif
diff --git a/src/util/vector.c b/src/util/vector.c
new file mode 100644
index 0000000..4a4bc8c
--- /dev/null
+++ b/src/util/vector.c
@@ -0,0 +1,431 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "vector.h"
+
+#include "integer.h"
+
+/* In elements, not bytes */
+#define MIN_ALLOCSIZE 8
+
+GIT_INLINE(size_t) compute_new_size(git_vector *v)
+{
+ size_t new_size = v->_alloc_size;
+
+ /* Use a resize factor of 1.5, which is quick to compute using integer
+ * instructions and less than the golden ratio (1.618...) */
+ if (new_size < MIN_ALLOCSIZE)
+ new_size = MIN_ALLOCSIZE;
+ else if (new_size <= (SIZE_MAX / 3) * 2)
+ new_size += new_size / 2;
+ else
+ new_size = SIZE_MAX;
+
+ return new_size;
+}
+
+GIT_INLINE(int) resize_vector(git_vector *v, size_t new_size)
+{
+ void *new_contents;
+
+ if (new_size == 0)
+ return 0;
+
+ new_contents = git__reallocarray(v->contents, new_size, sizeof(void *));
+ GIT_ERROR_CHECK_ALLOC(new_contents);
+
+ v->_alloc_size = new_size;
+ v->contents = new_contents;
+
+ return 0;
+}
+
+int git_vector_size_hint(git_vector *v, size_t size_hint)
+{
+ if (v->_alloc_size >= size_hint)
+ return 0;
+ return resize_vector(v, size_hint);
+}
+
+int git_vector_dup(git_vector *v, const git_vector *src, git_vector_cmp cmp)
+{
+ GIT_ASSERT_ARG(v);
+ GIT_ASSERT_ARG(src);
+
+ v->_alloc_size = 0;
+ v->contents = NULL;
+ v->_cmp = cmp ? cmp : src->_cmp;
+ v->length = src->length;
+ v->flags = src->flags;
+ if (cmp != src->_cmp)
+ git_vector_set_sorted(v, 0);
+
+ if (src->length) {
+ size_t bytes;
+ GIT_ERROR_CHECK_ALLOC_MULTIPLY(&bytes, src->length, sizeof(void *));
+ v->contents = git__malloc(bytes);
+ GIT_ERROR_CHECK_ALLOC(v->contents);
+ v->_alloc_size = src->length;
+ memcpy(v->contents, src->contents, bytes);
+ }
+
+ return 0;
+}
+
+void git_vector_free(git_vector *v)
+{
+ if (!v)
+ return;
+
+ git__free(v->contents);
+ v->contents = NULL;
+
+ v->length = 0;
+ v->_alloc_size = 0;
+}
+
+void git_vector_free_deep(git_vector *v)
+{
+ size_t i;
+
+ if (!v)
+ return;
+
+ for (i = 0; i < v->length; ++i) {
+ git__free(v->contents[i]);
+ v->contents[i] = NULL;
+ }
+
+ git_vector_free(v);
+}
+
+int git_vector_init(git_vector *v, size_t initial_size, git_vector_cmp cmp)
+{
+ GIT_ASSERT_ARG(v);
+
+ v->_alloc_size = 0;
+ v->_cmp = cmp;
+ v->length = 0;
+ v->flags = GIT_VECTOR_SORTED;
+ v->contents = NULL;
+
+ return resize_vector(v, max(initial_size, MIN_ALLOCSIZE));
+}
+
+void **git_vector_detach(size_t *size, size_t *asize, git_vector *v)
+{
+ void **data = v->contents;
+
+ if (size)
+ *size = v->length;
+ if (asize)
+ *asize = v->_alloc_size;
+
+ v->_alloc_size = 0;
+ v->length = 0;
+ v->contents = NULL;
+
+ return data;
+}
+
+int git_vector_insert(git_vector *v, void *element)
+{
+ GIT_ASSERT_ARG(v);
+
+ if (v->length >= v->_alloc_size &&
+ resize_vector(v, compute_new_size(v)) < 0)
+ return -1;
+
+ v->contents[v->length++] = element;
+
+ git_vector_set_sorted(v, v->length <= 1);
+
+ return 0;
+}
+
+int git_vector_insert_sorted(
+ git_vector *v, void *element, int (*on_dup)(void **old, void *new))
+{
+ int result;
+ size_t pos;
+
+ GIT_ASSERT_ARG(v);
+ GIT_ASSERT(v->_cmp);
+
+ if (!git_vector_is_sorted(v))
+ git_vector_sort(v);
+
+ if (v->length >= v->_alloc_size &&
+ resize_vector(v, compute_new_size(v)) < 0)
+ return -1;
+
+ /* If we find the element and have a duplicate handler callback,
+ * invoke it. If it returns non-zero, then cancel insert, otherwise
+ * proceed with normal insert.
+ */
+ if (!git__bsearch(v->contents, v->length, element, v->_cmp, &pos) &&
+ on_dup && (result = on_dup(&v->contents[pos], element)) < 0)
+ return result;
+
+ /* shift elements to the right */
+ if (pos < v->length)
+ memmove(v->contents + pos + 1, v->contents + pos,
+ (v->length - pos) * sizeof(void *));
+
+ v->contents[pos] = element;
+ v->length++;
+
+ return 0;
+}
+
+void git_vector_sort(git_vector *v)
+{
+ if (git_vector_is_sorted(v) || !v->_cmp)
+ return;
+
+ if (v->length > 1)
+ git__tsort(v->contents, v->length, v->_cmp);
+ git_vector_set_sorted(v, 1);
+}
+
+int git_vector_bsearch2(
+ size_t *at_pos,
+ git_vector *v,
+ git_vector_cmp key_lookup,
+ const void *key)
+{
+ GIT_ASSERT_ARG(v);
+ GIT_ASSERT_ARG(key);
+ GIT_ASSERT(key_lookup);
+
+ /* need comparison function to sort the vector */
+ if (!v->_cmp)
+ return -1;
+
+ git_vector_sort(v);
+
+ return git__bsearch(v->contents, v->length, key, key_lookup, at_pos);
+}
+
+int git_vector_search2(
+ size_t *at_pos, const git_vector *v, git_vector_cmp key_lookup, const void *key)
+{
+ size_t i;
+
+ GIT_ASSERT_ARG(v);
+ GIT_ASSERT_ARG(key);
+ GIT_ASSERT(key_lookup);
+
+ for (i = 0; i < v->length; ++i) {
+ if (key_lookup(key, v->contents[i]) == 0) {
+ if (at_pos)
+ *at_pos = i;
+
+ return 0;
+ }
+ }
+
+ return GIT_ENOTFOUND;
+}
+
+static int strict_comparison(const void *a, const void *b)
+{
+ return (a == b) ? 0 : -1;
+}
+
+int git_vector_search(size_t *at_pos, const git_vector *v, const void *entry)
+{
+ return git_vector_search2(at_pos, v, v->_cmp ? v->_cmp : strict_comparison, entry);
+}
+
+int git_vector_remove(git_vector *v, size_t idx)
+{
+ size_t shift_count;
+
+ GIT_ASSERT_ARG(v);
+
+ if (idx >= v->length)
+ return GIT_ENOTFOUND;
+
+ shift_count = v->length - idx - 1;
+
+ if (shift_count)
+ memmove(&v->contents[idx], &v->contents[idx + 1],
+ shift_count * sizeof(void *));
+
+ v->length--;
+ return 0;
+}
+
+void git_vector_pop(git_vector *v)
+{
+ if (v->length > 0)
+ v->length--;
+}
+
+void git_vector_uniq(git_vector *v, void (*git_free_cb)(void *))
+{
+ git_vector_cmp cmp;
+ size_t i, j;
+
+ if (v->length <= 1)
+ return;
+
+ git_vector_sort(v);
+ cmp = v->_cmp ? v->_cmp : strict_comparison;
+
+ for (i = 0, j = 1 ; j < v->length; ++j)
+ if (!cmp(v->contents[i], v->contents[j])) {
+ if (git_free_cb)
+ git_free_cb(v->contents[i]);
+
+ v->contents[i] = v->contents[j];
+ } else
+ v->contents[++i] = v->contents[j];
+
+ v->length -= j - i - 1;
+}
+
+void git_vector_remove_matching(
+ git_vector *v,
+ int (*match)(const git_vector *v, size_t idx, void *payload),
+ void *payload)
+{
+ size_t i, j;
+
+ for (i = 0, j = 0; j < v->length; ++j) {
+ v->contents[i] = v->contents[j];
+
+ if (!match(v, i, payload))
+ i++;
+ }
+
+ v->length = i;
+}
+
+void git_vector_clear(git_vector *v)
+{
+ v->length = 0;
+ git_vector_set_sorted(v, 1);
+}
+
+void git_vector_swap(git_vector *a, git_vector *b)
+{
+ git_vector t;
+
+ if (a != b) {
+ memcpy(&t, a, sizeof(t));
+ memcpy(a, b, sizeof(t));
+ memcpy(b, &t, sizeof(t));
+ }
+}
+
+int git_vector_resize_to(git_vector *v, size_t new_length)
+{
+ if (new_length > v->_alloc_size &&
+ resize_vector(v, new_length) < 0)
+ return -1;
+
+ if (new_length > v->length)
+ memset(&v->contents[v->length], 0,
+ sizeof(void *) * (new_length - v->length));
+
+ v->length = new_length;
+
+ return 0;
+}
+
+int git_vector_insert_null(git_vector *v, size_t idx, size_t insert_len)
+{
+ size_t new_length;
+
+ GIT_ASSERT_ARG(insert_len > 0);
+ GIT_ASSERT_ARG(idx <= v->length);
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&new_length, v->length, insert_len);
+
+ if (new_length > v->_alloc_size && resize_vector(v, new_length) < 0)
+ return -1;
+
+ memmove(&v->contents[idx + insert_len], &v->contents[idx],
+ sizeof(void *) * (v->length - idx));
+ memset(&v->contents[idx], 0, sizeof(void *) * insert_len);
+
+ v->length = new_length;
+ return 0;
+}
+
+int git_vector_remove_range(git_vector *v, size_t idx, size_t remove_len)
+{
+ size_t new_length = v->length - remove_len;
+ size_t end_idx = 0;
+
+ GIT_ASSERT_ARG(remove_len > 0);
+
+ if (git__add_sizet_overflow(&end_idx, idx, remove_len))
+ GIT_ASSERT(0);
+
+ GIT_ASSERT(end_idx <= v->length);
+
+ if (end_idx < v->length)
+ memmove(&v->contents[idx], &v->contents[end_idx],
+ sizeof(void *) * (v->length - end_idx));
+
+ memset(&v->contents[new_length], 0, sizeof(void *) * remove_len);
+
+ v->length = new_length;
+ return 0;
+}
+
+int git_vector_set(void **old, git_vector *v, size_t position, void *value)
+{
+ if (position + 1 > v->length) {
+ if (git_vector_resize_to(v, position + 1) < 0)
+ return -1;
+ }
+
+ if (old != NULL)
+ *old = v->contents[position];
+
+ v->contents[position] = value;
+
+ return 0;
+}
+
+int git_vector_verify_sorted(const git_vector *v)
+{
+ size_t i;
+
+ if (!git_vector_is_sorted(v))
+ return -1;
+
+ for (i = 1; i < v->length; ++i) {
+ if (v->_cmp(v->contents[i - 1], v->contents[i]) > 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+void git_vector_reverse(git_vector *v)
+{
+ size_t a, b;
+
+ if (v->length == 0)
+ return;
+
+ a = 0;
+ b = v->length - 1;
+
+ while (a < b) {
+ void *tmp = v->contents[a];
+ v->contents[a] = v->contents[b];
+ v->contents[b] = tmp;
+ a++;
+ b--;
+ }
+}
diff --git a/src/util/vector.h b/src/util/vector.h
new file mode 100644
index 0000000..e50cdfe
--- /dev/null
+++ b/src/util/vector.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_vector_h__
+#define INCLUDE_vector_h__
+
+#include "git2_util.h"
+
+typedef int (*git_vector_cmp)(const void *, const void *);
+
+enum {
+ GIT_VECTOR_SORTED = (1u << 0),
+ GIT_VECTOR_FLAG_MAX = (1u << 1)
+};
+
+typedef struct git_vector {
+ size_t _alloc_size;
+ git_vector_cmp _cmp;
+ void **contents;
+ size_t length;
+ uint32_t flags;
+} git_vector;
+
+#define GIT_VECTOR_INIT {0}
+
+GIT_WARN_UNUSED_RESULT int git_vector_init(
+ git_vector *v, size_t initial_size, git_vector_cmp cmp);
+void git_vector_free(git_vector *v);
+void git_vector_free_deep(git_vector *v); /* free each entry and self */
+void git_vector_clear(git_vector *v);
+GIT_WARN_UNUSED_RESULT int git_vector_dup(
+ git_vector *v, const git_vector *src, git_vector_cmp cmp);
+void git_vector_swap(git_vector *a, git_vector *b);
+int git_vector_size_hint(git_vector *v, size_t size_hint);
+
+void **git_vector_detach(size_t *size, size_t *asize, git_vector *v);
+
+void git_vector_sort(git_vector *v);
+
+/** Linear search for matching entry using internal comparison function */
+int git_vector_search(size_t *at_pos, const git_vector *v, const void *entry);
+
+/** Linear search for matching entry using explicit comparison function */
+int git_vector_search2(size_t *at_pos, const git_vector *v, git_vector_cmp cmp, const void *key);
+
+/**
+ * Binary search for matching entry using explicit comparison function that
+ * returns position where item would go if not found.
+ */
+int git_vector_bsearch2(
+ size_t *at_pos, git_vector *v, git_vector_cmp cmp, const void *key);
+
+/** Binary search for matching entry using internal comparison function */
+GIT_INLINE(int) git_vector_bsearch(size_t *at_pos, git_vector *v, const void *key)
+{
+ return git_vector_bsearch2(at_pos, v, v->_cmp, key);
+}
+
+GIT_INLINE(void *) git_vector_get(const git_vector *v, size_t position)
+{
+ return (position < v->length) ? v->contents[position] : NULL;
+}
+
+#define GIT_VECTOR_GET(V,I) ((I) < (V)->length ? (V)->contents[(I)] : NULL)
+
+GIT_INLINE(size_t) git_vector_length(const git_vector *v)
+{
+ return v->length;
+}
+
+GIT_INLINE(void *) git_vector_last(const git_vector *v)
+{
+ return (v->length > 0) ? git_vector_get(v, v->length - 1) : NULL;
+}
+
+#define git_vector_foreach(v, iter, elem) \
+ for ((iter) = 0; (iter) < (v)->length && ((elem) = (v)->contents[(iter)], 1); (iter)++ )
+
+#define git_vector_rforeach(v, iter, elem) \
+ for ((iter) = (v)->length - 1; (iter) < SIZE_MAX && ((elem) = (v)->contents[(iter)], 1); (iter)-- )
+
+int git_vector_insert(git_vector *v, void *element);
+int git_vector_insert_sorted(git_vector *v, void *element,
+ int (*on_dup)(void **old, void *new));
+int git_vector_remove(git_vector *v, size_t idx);
+void git_vector_pop(git_vector *v);
+void git_vector_uniq(git_vector *v, void (*git_free_cb)(void *));
+
+void git_vector_remove_matching(
+ git_vector *v,
+ int (*match)(const git_vector *v, size_t idx, void *payload),
+ void *payload);
+
+int git_vector_resize_to(git_vector *v, size_t new_length);
+int git_vector_insert_null(git_vector *v, size_t idx, size_t insert_len);
+int git_vector_remove_range(git_vector *v, size_t idx, size_t remove_len);
+
+int git_vector_set(void **old, git_vector *v, size_t position, void *value);
+
+/** Check if vector is sorted */
+#define git_vector_is_sorted(V) (((V)->flags & GIT_VECTOR_SORTED) != 0)
+
+/** Directly set sorted state of vector */
+#define git_vector_set_sorted(V,S) do { \
+ (V)->flags = (S) ? ((V)->flags | GIT_VECTOR_SORTED) : \
+ ((V)->flags & ~GIT_VECTOR_SORTED); } while (0)
+
+/** Set the comparison function used for sorting the vector */
+GIT_INLINE(void) git_vector_set_cmp(git_vector *v, git_vector_cmp cmp)
+{
+ if (cmp != v->_cmp) {
+ v->_cmp = cmp;
+ git_vector_set_sorted(v, 0);
+ }
+}
+
+/* Just use this in tests, not for realz. returns -1 if not sorted */
+int git_vector_verify_sorted(const git_vector *v);
+
+/**
+ * Reverse the vector in-place.
+ */
+void git_vector_reverse(git_vector *v);
+
+#endif
diff --git a/src/util/wildmatch.c b/src/util/wildmatch.c
new file mode 100644
index 0000000..a894e48
--- /dev/null
+++ b/src/util/wildmatch.c
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ *
+ * Do shell-style pattern matching for ?, \, [], and * characters.
+ * It is 8bit clean.
+ *
+ * Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.
+ * Rich $alz is now <rsalz@bbn.com>.
+ *
+ * Modified by Wayne Davison to special-case '/' matching, to make '**'
+ * work differently than '*', and to fix the character-class code.
+ *
+ * Imported from git.git.
+ */
+
+#include "wildmatch.h"
+
+#define GIT_SPACE 0x01
+#define GIT_DIGIT 0x02
+#define GIT_ALPHA 0x04
+#define GIT_GLOB_SPECIAL 0x08
+#define GIT_REGEX_SPECIAL 0x10
+#define GIT_PATHSPEC_MAGIC 0x20
+#define GIT_CNTRL 0x40
+#define GIT_PUNCT 0x80
+
+enum {
+ S = GIT_SPACE,
+ A = GIT_ALPHA,
+ D = GIT_DIGIT,
+ G = GIT_GLOB_SPECIAL, /* *, ?, [, \\ */
+ R = GIT_REGEX_SPECIAL, /* $, (, ), +, ., ^, {, | */
+ P = GIT_PATHSPEC_MAGIC, /* other non-alnum, except for ] and } */
+ X = GIT_CNTRL,
+ U = GIT_PUNCT,
+ Z = GIT_CNTRL | GIT_SPACE
+};
+
+static const unsigned char sane_ctype[256] = {
+ X, X, X, X, X, X, X, X, X, Z, Z, X, X, Z, X, X, /* 0.. 15 */
+ X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, X, /* 16.. 31 */
+ S, P, P, P, R, P, P, P, R, R, G, R, P, P, R, P, /* 32.. 47 */
+ D, D, D, D, D, D, D, D, D, D, P, P, P, P, P, G, /* 48.. 63 */
+ P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 64.. 79 */
+ A, A, A, A, A, A, A, A, A, A, A, G, G, U, R, P, /* 80.. 95 */
+ P, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, /* 96..111 */
+ A, A, A, A, A, A, A, A, A, A, A, R, R, U, P, X, /* 112..127 */
+ /* Nothing in the 128.. range */
+};
+
+#define sane_istest(x,mask) ((sane_ctype[(unsigned char)(x)] & (mask)) != 0)
+#define is_glob_special(x) sane_istest(x,GIT_GLOB_SPECIAL)
+
+typedef unsigned char uchar;
+
+/* What character marks an inverted character class? */
+#define NEGATE_CLASS '!'
+#define NEGATE_CLASS2 '^'
+
+#define CC_EQ(class, len, litmatch) ((len) == sizeof (litmatch)-1 \
+ && *(class) == *(litmatch) \
+ && strncmp((char*)class, litmatch, len) == 0)
+
+#if defined STDC_HEADERS || !defined isascii
+# define ISASCII(c) 1
+#else
+# define ISASCII(c) isascii(c)
+#endif
+
+#ifdef isblank
+# define ISBLANK(c) (ISASCII(c) && isblank(c))
+#else
+# define ISBLANK(c) ((c) == ' ' || (c) == '\t')
+#endif
+
+#ifdef isgraph
+# define ISGRAPH(c) (ISASCII(c) && isgraph(c))
+#else
+# define ISGRAPH(c) (ISASCII(c) && isprint(c) && !isspace(c))
+#endif
+
+#define ISPRINT(c) (ISASCII(c) && isprint(c))
+#define ISDIGIT(c) (ISASCII(c) && isdigit(c))
+#define ISALNUM(c) (ISASCII(c) && isalnum(c))
+#define ISALPHA(c) (ISASCII(c) && isalpha(c))
+#define ISCNTRL(c) (ISASCII(c) && iscntrl(c))
+#define ISLOWER(c) (ISASCII(c) && islower(c))
+#define ISPUNCT(c) (ISASCII(c) && ispunct(c))
+#define ISSPACE(c) (ISASCII(c) && isspace(c))
+#define ISUPPER(c) (ISASCII(c) && isupper(c))
+#define ISXDIGIT(c) (ISASCII(c) && isxdigit(c))
+
+/* Match pattern "p" against "text" */
+static int dowild(const uchar *p, const uchar *text, unsigned int flags)
+{
+ uchar p_ch;
+ const uchar *pattern = p;
+
+ for ( ; (p_ch = *p) != '\0'; text++, p++) {
+ int matched, match_slash, negated;
+ uchar t_ch, prev_ch;
+ if ((t_ch = *text) == '\0' && p_ch != '*')
+ return WM_ABORT_ALL;
+ if ((flags & WM_CASEFOLD) && ISUPPER(t_ch))
+ t_ch = tolower(t_ch);
+ if ((flags & WM_CASEFOLD) && ISUPPER(p_ch))
+ p_ch = tolower(p_ch);
+ switch (p_ch) {
+ case '\\':
+ /* Literal match with following character. Note that the test
+ * in "default" handles the p[1] == '\0' failure case. */
+ p_ch = *++p;
+ /* FALLTHROUGH */
+ default:
+ if (t_ch != p_ch)
+ return WM_NOMATCH;
+ continue;
+ case '?':
+ /* Match anything but '/'. */
+ if ((flags & WM_PATHNAME) && t_ch == '/')
+ return WM_NOMATCH;
+ continue;
+ case '*':
+ if (*++p == '*') {
+ const uchar *prev_p = p - 2;
+ while (*++p == '*') {}
+ if (!(flags & WM_PATHNAME))
+ /* without WM_PATHNAME, '*' == '**' */
+ match_slash = 1;
+ else if ((prev_p < pattern || *prev_p == '/') &&
+ (*p == '\0' || *p == '/' ||
+ (p[0] == '\\' && p[1] == '/'))) {
+ /*
+ * Assuming we already match 'foo/' and are at
+ * <star star slash>, just assume it matches
+ * nothing and go ahead match the rest of the
+ * pattern with the remaining string. This
+ * helps make foo/<*><*>/bar (<> because
+ * otherwise it breaks C comment syntax) match
+ * both foo/bar and foo/a/bar.
+ */
+ if (p[0] == '/' &&
+ dowild(p + 1, text, flags) == WM_MATCH)
+ return WM_MATCH;
+ match_slash = 1;
+ } else /* WM_PATHNAME is set */
+ match_slash = 0;
+ } else
+ /* without WM_PATHNAME, '*' == '**' */
+ match_slash = flags & WM_PATHNAME ? 0 : 1;
+ if (*p == '\0') {
+ /* Trailing "**" matches everything. Trailing "*" matches
+ * only if there are no more slash characters. */
+ if (!match_slash) {
+ if (strchr((char*)text, '/') != NULL)
+ return WM_NOMATCH;
+ }
+ return WM_MATCH;
+ } else if (!match_slash && *p == '/') {
+ /*
+ * _one_ asterisk followed by a slash
+ * with WM_PATHNAME matches the next
+ * directory
+ */
+ const char *slash = strchr((char*)text, '/');
+ if (!slash)
+ return WM_NOMATCH;
+ text = (const uchar*)slash;
+ /* the slash is consumed by the top-level for loop */
+ break;
+ }
+ while (1) {
+ if (t_ch == '\0')
+ break;
+ /*
+ * Try to advance faster when an asterisk is
+ * followed by a literal. We know in this case
+ * that the string before the literal
+ * must belong to "*".
+ * If match_slash is false, do not look past
+ * the first slash as it cannot belong to '*'.
+ */
+ if (!is_glob_special(*p)) {
+ p_ch = *p;
+ if ((flags & WM_CASEFOLD) && ISUPPER(p_ch))
+ p_ch = tolower(p_ch);
+ while ((t_ch = *text) != '\0' &&
+ (match_slash || t_ch != '/')) {
+ if ((flags & WM_CASEFOLD) && ISUPPER(t_ch))
+ t_ch = tolower(t_ch);
+ if (t_ch == p_ch)
+ break;
+ text++;
+ }
+ if (t_ch != p_ch)
+ return WM_NOMATCH;
+ }
+ if ((matched = dowild(p, text, flags)) != WM_NOMATCH) {
+ if (!match_slash || matched != WM_ABORT_TO_STARSTAR)
+ return matched;
+ } else if (!match_slash && t_ch == '/')
+ return WM_ABORT_TO_STARSTAR;
+ t_ch = *++text;
+ }
+ return WM_ABORT_ALL;
+ case '[':
+ p_ch = *++p;
+#ifdef NEGATE_CLASS2
+ if (p_ch == NEGATE_CLASS2)
+ p_ch = NEGATE_CLASS;
+#endif
+ /* Assign literal 1/0 because of "matched" comparison. */
+ negated = p_ch == NEGATE_CLASS ? 1 : 0;
+ if (negated) {
+ /* Inverted character class. */
+ p_ch = *++p;
+ }
+ prev_ch = 0;
+ matched = 0;
+ do {
+ if (!p_ch)
+ return WM_ABORT_ALL;
+ if (p_ch == '\\') {
+ p_ch = *++p;
+ if (!p_ch)
+ return WM_ABORT_ALL;
+ if (t_ch == p_ch)
+ matched = 1;
+ } else if (p_ch == '-' && prev_ch && p[1] && p[1] != ']') {
+ p_ch = *++p;
+ if (p_ch == '\\') {
+ p_ch = *++p;
+ if (!p_ch)
+ return WM_ABORT_ALL;
+ }
+ if (t_ch <= p_ch && t_ch >= prev_ch)
+ matched = 1;
+ else if ((flags & WM_CASEFOLD) && ISLOWER(t_ch)) {
+ uchar t_ch_upper = toupper(t_ch);
+ if (t_ch_upper <= p_ch && t_ch_upper >= prev_ch)
+ matched = 1;
+ }
+ p_ch = 0; /* This makes "prev_ch" get set to 0. */
+ } else if (p_ch == '[' && p[1] == ':') {
+ const uchar *s;
+ int i;
+ for (s = p += 2; (p_ch = *p) && p_ch != ']'; p++) {} /*SHARED ITERATOR*/
+ if (!p_ch)
+ return WM_ABORT_ALL;
+ i = (int)(p - s - 1);
+ if (i < 0 || p[-1] != ':') {
+ /* Didn't find ":]", so treat like a normal set. */
+ p = s - 2;
+ p_ch = '[';
+ if (t_ch == p_ch)
+ matched = 1;
+ continue;
+ }
+ if (CC_EQ(s,i, "alnum")) {
+ if (ISALNUM(t_ch))
+ matched = 1;
+ } else if (CC_EQ(s,i, "alpha")) {
+ if (ISALPHA(t_ch))
+ matched = 1;
+ } else if (CC_EQ(s,i, "blank")) {
+ if (ISBLANK(t_ch))
+ matched = 1;
+ } else if (CC_EQ(s,i, "cntrl")) {
+ if (ISCNTRL(t_ch))
+ matched = 1;
+ } else if (CC_EQ(s,i, "digit")) {
+ if (ISDIGIT(t_ch))
+ matched = 1;
+ } else if (CC_EQ(s,i, "graph")) {
+ if (ISGRAPH(t_ch))
+ matched = 1;
+ } else if (CC_EQ(s,i, "lower")) {
+ if (ISLOWER(t_ch))
+ matched = 1;
+ } else if (CC_EQ(s,i, "print")) {
+ if (ISPRINT(t_ch))
+ matched = 1;
+ } else if (CC_EQ(s,i, "punct")) {
+ if (ISPUNCT(t_ch))
+ matched = 1;
+ } else if (CC_EQ(s,i, "space")) {
+ if (ISSPACE(t_ch))
+ matched = 1;
+ } else if (CC_EQ(s,i, "upper")) {
+ if (ISUPPER(t_ch))
+ matched = 1;
+ else if ((flags & WM_CASEFOLD) && ISLOWER(t_ch))
+ matched = 1;
+ } else if (CC_EQ(s,i, "xdigit")) {
+ if (ISXDIGIT(t_ch))
+ matched = 1;
+ } else /* malformed [:class:] string */
+ return WM_ABORT_ALL;
+ p_ch = 0; /* This makes "prev_ch" get set to 0. */
+ } else if (t_ch == p_ch)
+ matched = 1;
+ } while (prev_ch = p_ch, (p_ch = *++p) != ']');
+ if (matched == negated ||
+ ((flags & WM_PATHNAME) && t_ch == '/'))
+ return WM_NOMATCH;
+ continue;
+ }
+ }
+
+ return *text ? WM_NOMATCH : WM_MATCH;
+}
+
+/* Match the "pattern" against the "text" string. */
+int wildmatch(const char *pattern, const char *text, unsigned int flags)
+{
+ return dowild((const uchar*)pattern, (const uchar*)text, flags);
+}
diff --git a/src/util/wildmatch.h b/src/util/wildmatch.h
new file mode 100644
index 0000000..f206405
--- /dev/null
+++ b/src/util/wildmatch.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_wildmatch_h__
+#define INCLUDE_wildmatch_h__
+
+#include "git2_util.h"
+
+#define WM_CASEFOLD 1
+#define WM_PATHNAME 2
+
+#define WM_NOMATCH 1
+#define WM_MATCH 0
+#define WM_ABORT_ALL -1
+#define WM_ABORT_TO_STARSTAR -2
+
+int wildmatch(const char *pattern, const char *text, unsigned int flags);
+
+#endif
diff --git a/src/util/win32/dir.c b/src/util/win32/dir.c
new file mode 100644
index 0000000..44052ca
--- /dev/null
+++ b/src/util/win32/dir.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "dir.h"
+
+#define GIT__WIN32_NO_WRAP_DIR
+#include "posix.h"
+
+git__DIR *git__opendir(const char *dir)
+{
+ git_win32_path filter_w;
+ git__DIR *new = NULL;
+ size_t dirlen, alloclen;
+
+ if (!dir || !git_win32__findfirstfile_filter(filter_w, dir))
+ return NULL;
+
+ dirlen = strlen(dir);
+
+ if (GIT_ADD_SIZET_OVERFLOW(&alloclen, sizeof(*new), dirlen) ||
+ GIT_ADD_SIZET_OVERFLOW(&alloclen, alloclen, 1) ||
+ !(new = git__calloc(1, alloclen)))
+ return NULL;
+
+ memcpy(new->dir, dir, dirlen);
+
+ new->h = FindFirstFileW(filter_w, &new->f);
+
+ if (new->h == INVALID_HANDLE_VALUE) {
+ git_error_set(GIT_ERROR_OS, "could not open directory '%s'", dir);
+ git__free(new);
+ return NULL;
+ }
+
+ new->first = 1;
+ return new;
+}
+
+int git__readdir_ext(
+ git__DIR *d,
+ struct git__dirent *entry,
+ struct git__dirent **result,
+ int *is_dir)
+{
+ if (!d || !entry || !result || d->h == INVALID_HANDLE_VALUE)
+ return -1;
+
+ *result = NULL;
+
+ if (d->first)
+ d->first = 0;
+ else if (!FindNextFileW(d->h, &d->f)) {
+ if (GetLastError() == ERROR_NO_MORE_FILES)
+ return 0;
+ git_error_set(GIT_ERROR_OS, "could not read from directory '%s'", d->dir);
+ return -1;
+ }
+
+ /* Convert the path to UTF-8 */
+ if (git_win32_path_to_utf8(entry->d_name, d->f.cFileName) < 0)
+ return -1;
+
+ entry->d_ino = 0;
+
+ *result = entry;
+
+ if (is_dir != NULL)
+ *is_dir = ((d->f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0);
+
+ return 0;
+}
+
+struct git__dirent *git__readdir(git__DIR *d)
+{
+ struct git__dirent *result;
+ if (git__readdir_ext(d, &d->entry, &result, NULL) < 0)
+ return NULL;
+ return result;
+}
+
+void git__rewinddir(git__DIR *d)
+{
+ git_win32_path filter_w;
+
+ if (!d)
+ return;
+
+ if (d->h != INVALID_HANDLE_VALUE) {
+ FindClose(d->h);
+ d->h = INVALID_HANDLE_VALUE;
+ d->first = 0;
+ }
+
+ if (!git_win32__findfirstfile_filter(filter_w, d->dir))
+ return;
+
+ d->h = FindFirstFileW(filter_w, &d->f);
+
+ if (d->h == INVALID_HANDLE_VALUE)
+ git_error_set(GIT_ERROR_OS, "could not open directory '%s'", d->dir);
+ else
+ d->first = 1;
+}
+
+int git__closedir(git__DIR *d)
+{
+ if (!d)
+ return 0;
+
+ if (d->h != INVALID_HANDLE_VALUE) {
+ FindClose(d->h);
+ d->h = INVALID_HANDLE_VALUE;
+ }
+
+ git__free(d);
+ return 0;
+}
+
diff --git a/src/util/win32/dir.h b/src/util/win32/dir.h
new file mode 100644
index 0000000..8101115
--- /dev/null
+++ b/src/util/win32/dir.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_win32_dir_h__
+#define INCLUDE_win32_dir_h__
+
+#include "git2_util.h"
+
+#include "w32_util.h"
+
+struct git__dirent {
+ int d_ino;
+ git_win32_utf8_path d_name;
+};
+
+typedef struct {
+ HANDLE h;
+ WIN32_FIND_DATAW f;
+ struct git__dirent entry;
+ int first;
+ char dir[GIT_FLEX_ARRAY];
+} git__DIR;
+
+extern git__DIR *git__opendir(const char *);
+extern struct git__dirent *git__readdir(git__DIR *);
+extern int git__readdir_ext(
+ git__DIR *, struct git__dirent *, struct git__dirent **, int *);
+extern void git__rewinddir(git__DIR *);
+extern int git__closedir(git__DIR *);
+
+# ifndef GIT__WIN32_NO_WRAP_DIR
+# define dirent git__dirent
+# define DIR git__DIR
+# define opendir git__opendir
+# define readdir git__readdir
+# define readdir_r(d,e,r) git__readdir_ext((d),(e),(r),NULL)
+# define rewinddir git__rewinddir
+# define closedir git__closedir
+# endif
+
+#endif
diff --git a/src/util/win32/error.c b/src/util/win32/error.c
new file mode 100644
index 0000000..dfd6fa1
--- /dev/null
+++ b/src/util/win32/error.c
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "error.h"
+
+#include "utf-conv.h"
+
+#ifdef GIT_WINHTTP
+# include <winhttp.h>
+#endif
+
+char *git_win32_get_error_message(DWORD error_code)
+{
+ LPWSTR lpMsgBuf = NULL;
+ HMODULE hModule = NULL;
+ char *utf8_msg = NULL;
+ DWORD dwFlags =
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_IGNORE_INSERTS;
+
+ if (!error_code)
+ return NULL;
+
+#ifdef GIT_WINHTTP
+ /* Errors raised by WinHTTP are not in the system resource table */
+ if (error_code >= WINHTTP_ERROR_BASE &&
+ error_code <= WINHTTP_ERROR_LAST)
+ hModule = GetModuleHandleW(L"winhttp");
+#endif
+
+ GIT_UNUSED(hModule);
+
+ if (hModule)
+ dwFlags |= FORMAT_MESSAGE_FROM_HMODULE;
+ else
+ dwFlags |= FORMAT_MESSAGE_FROM_SYSTEM;
+
+ if (FormatMessageW(dwFlags, hModule, error_code,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPWSTR)&lpMsgBuf, 0, NULL)) {
+ /* Convert the message to UTF-8. If this fails, we will
+ * return NULL, which is a condition expected by the caller */
+ if (git_utf8_from_16_alloc(&utf8_msg, lpMsgBuf) < 0)
+ utf8_msg = NULL;
+
+ LocalFree(lpMsgBuf);
+ }
+
+ return utf8_msg;
+}
diff --git a/src/util/win32/error.h b/src/util/win32/error.h
new file mode 100644
index 0000000..fd53b7f
--- /dev/null
+++ b/src/util/win32/error.h
@@ -0,0 +1,15 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_win32_error_h__
+#define INCLUDE_win32_error_h__
+
+#include "git2_util.h"
+
+extern char *git_win32_get_error_message(DWORD error_code);
+
+#endif
diff --git a/src/util/win32/map.c b/src/util/win32/map.c
new file mode 100644
index 0000000..52e1363
--- /dev/null
+++ b/src/util/win32/map.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "git2_util.h"
+
+#include "map.h"
+#include <errno.h>
+
+#ifndef NO_MMAP
+
+static DWORD get_page_size(void)
+{
+ static DWORD page_size;
+ SYSTEM_INFO sys;
+
+ if (!page_size) {
+ GetSystemInfo(&sys);
+ page_size = sys.dwPageSize;
+ }
+
+ return page_size;
+}
+
+static DWORD get_allocation_granularity(void)
+{
+ static DWORD granularity;
+ SYSTEM_INFO sys;
+
+ if (!granularity) {
+ GetSystemInfo(&sys);
+ granularity = sys.dwAllocationGranularity;
+ }
+
+ return granularity;
+}
+
+int git__page_size(size_t *page_size)
+{
+ *page_size = get_page_size();
+ return 0;
+}
+
+int git__mmap_alignment(size_t *page_size)
+{
+ *page_size = get_allocation_granularity();
+ return 0;
+}
+
+int p_mmap(git_map *out, size_t len, int prot, int flags, int fd, off64_t offset)
+{
+ HANDLE fh = (HANDLE)_get_osfhandle(fd);
+ DWORD alignment = get_allocation_granularity();
+ DWORD fmap_prot = 0;
+ DWORD view_prot = 0;
+ DWORD off_low = 0;
+ DWORD off_hi = 0;
+ off64_t page_start;
+ off64_t page_offset;
+
+ GIT_MMAP_VALIDATE(out, len, prot, flags);
+
+ out->data = NULL;
+ out->len = 0;
+ out->fmh = NULL;
+
+ if (fh == INVALID_HANDLE_VALUE) {
+ errno = EBADF;
+ git_error_set(GIT_ERROR_OS, "failed to mmap. Invalid handle value");
+ return -1;
+ }
+
+ if (prot & GIT_PROT_WRITE)
+ fmap_prot |= PAGE_READWRITE;
+ else if (prot & GIT_PROT_READ)
+ fmap_prot |= PAGE_READONLY;
+
+ if (prot & GIT_PROT_WRITE)
+ view_prot |= FILE_MAP_WRITE;
+ if (prot & GIT_PROT_READ)
+ view_prot |= FILE_MAP_READ;
+
+ page_start = (offset / alignment) * alignment;
+ page_offset = offset - page_start;
+
+ if (page_offset != 0) { /* offset must be multiple of the allocation granularity */
+ errno = EINVAL;
+ git_error_set(GIT_ERROR_OS, "failed to mmap. Offset must be multiple of allocation granularity");
+ return -1;
+ }
+
+ out->fmh = CreateFileMapping(fh, NULL, fmap_prot, 0, 0, NULL);
+ if (!out->fmh || out->fmh == INVALID_HANDLE_VALUE) {
+ git_error_set(GIT_ERROR_OS, "failed to mmap. Invalid handle value");
+ out->fmh = NULL;
+ return -1;
+ }
+
+ off_low = (DWORD)(page_start);
+ off_hi = (DWORD)(page_start >> 32);
+ out->data = MapViewOfFile(out->fmh, view_prot, off_hi, off_low, len);
+ if (!out->data) {
+ git_error_set(GIT_ERROR_OS, "failed to mmap. No data written");
+ CloseHandle(out->fmh);
+ out->fmh = NULL;
+ return -1;
+ }
+ out->len = len;
+
+ return 0;
+}
+
+int p_munmap(git_map *map)
+{
+ int error = 0;
+
+ GIT_ASSERT_ARG(map);
+
+ if (map->data) {
+ if (!UnmapViewOfFile(map->data)) {
+ git_error_set(GIT_ERROR_OS, "failed to munmap. Could not unmap view of file");
+ error = -1;
+ }
+ map->data = NULL;
+ }
+
+ if (map->fmh) {
+ if (!CloseHandle(map->fmh)) {
+ git_error_set(GIT_ERROR_OS, "failed to munmap. Could not close handle");
+ error = -1;
+ }
+ map->fmh = NULL;
+ }
+
+ return error;
+}
+
+#endif
diff --git a/src/util/win32/mingw-compat.h b/src/util/win32/mingw-compat.h
new file mode 100644
index 0000000..aa2bef9
--- /dev/null
+++ b/src/util/win32/mingw-compat.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_win32_mingw_compat_h__
+#define INCLUDE_win32_mingw_compat_h__
+
+#if defined(__MINGW32__)
+
+#undef stat
+
+#if _WIN32_WINNT < 0x0600 && !defined(__MINGW64_VERSION_MAJOR)
+#undef MemoryBarrier
+void __mingworg_MemoryBarrier(void);
+#define MemoryBarrier __mingworg_MemoryBarrier
+#define VOLUME_NAME_DOS 0x0
+#endif
+
+#endif
+
+#endif
diff --git a/src/util/win32/msvc-compat.h b/src/util/win32/msvc-compat.h
new file mode 100644
index 0000000..03f9f36
--- /dev/null
+++ b/src/util/win32/msvc-compat.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_win32_msvc_compat_h__
+#define INCLUDE_win32_msvc_compat_h__
+
+#if defined(_MSC_VER)
+
+typedef unsigned short mode_t;
+typedef SSIZE_T ssize_t;
+
+#ifdef _WIN64
+# define SSIZE_MAX _I64_MAX
+#else
+# define SSIZE_MAX LONG_MAX
+#endif
+
+#define strcasecmp(s1, s2) _stricmp(s1, s2)
+#define strncasecmp(s1, s2, c) _strnicmp(s1, s2, c)
+
+#endif
+
+/*
+ * Offer GIT_LIBGIT2_CALL for our calling conventions (__cdecl, always).
+ * This is useful for providing callbacks to userspace code.
+ *
+ * Offer GIT_SYSTEM_CALL for the system calling conventions (__stdcall on
+ * Win32). Useful for providing callbacks to system libraries.
+ */
+#define GIT_LIBGIT2_CALL __cdecl
+#define GIT_SYSTEM_CALL NTAPI
+
+#endif
diff --git a/src/util/win32/path_w32.c b/src/util/win32/path_w32.c
new file mode 100644
index 0000000..7a559e4
--- /dev/null
+++ b/src/util/win32/path_w32.c
@@ -0,0 +1,642 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "path_w32.h"
+
+#include "fs_path.h"
+#include "utf-conv.h"
+#include "posix.h"
+#include "reparse.h"
+#include "dir.h"
+
+#define PATH__NT_NAMESPACE L"\\\\?\\"
+#define PATH__NT_NAMESPACE_LEN 4
+
+#define PATH__ABSOLUTE_LEN 3
+
+#define path__is_nt_namespace(p) \
+ (((p)[0] == '\\' && (p)[1] == '\\' && (p)[2] == '?' && (p)[3] == '\\') || \
+ ((p)[0] == '/' && (p)[1] == '/' && (p)[2] == '?' && (p)[3] == '/'))
+
+#define path__is_unc(p) \
+ (((p)[0] == '\\' && (p)[1] == '\\') || ((p)[0] == '/' && (p)[1] == '/'))
+
+#define path__startswith_slash(p) \
+ ((p)[0] == '\\' || (p)[0] == '/')
+
+GIT_INLINE(int) path__cwd(wchar_t *path, int size)
+{
+ int len;
+
+ if ((len = GetCurrentDirectoryW(size, path)) == 0) {
+ errno = GetLastError() == ERROR_ACCESS_DENIED ? EACCES : ENOENT;
+ return -1;
+ } else if (len > size) {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+
+ /* The Win32 APIs may return "\\?\" once you've used it first.
+ * But it may not. What a gloriously predictable API!
+ */
+ if (wcsncmp(path, PATH__NT_NAMESPACE, PATH__NT_NAMESPACE_LEN))
+ return len;
+
+ len -= PATH__NT_NAMESPACE_LEN;
+
+ memmove(path, path + PATH__NT_NAMESPACE_LEN, sizeof(wchar_t) * len);
+ return len;
+}
+
+static wchar_t *path__skip_server(wchar_t *path)
+{
+ wchar_t *c;
+
+ for (c = path; *c; c++) {
+ if (git_fs_path_is_dirsep(*c))
+ return c + 1;
+ }
+
+ return c;
+}
+
+static wchar_t *path__skip_prefix(wchar_t *path)
+{
+ if (path__is_nt_namespace(path)) {
+ path += PATH__NT_NAMESPACE_LEN;
+
+ if (wcsncmp(path, L"UNC\\", 4) == 0)
+ path = path__skip_server(path + 4);
+ else if (git_fs_path_is_absolute(path))
+ path += PATH__ABSOLUTE_LEN;
+ } else if (git_fs_path_is_absolute(path)) {
+ path += PATH__ABSOLUTE_LEN;
+ } else if (path__is_unc(path)) {
+ path = path__skip_server(path + 2);
+ }
+
+ return path;
+}
+
+int git_win32_path_canonicalize(git_win32_path path)
+{
+ wchar_t *base, *from, *to, *next;
+ size_t len;
+
+ base = to = path__skip_prefix(path);
+
+ /* Unposixify if the prefix */
+ for (from = path; from < to; from++) {
+ if (*from == L'/')
+ *from = L'\\';
+ }
+
+ while (*from) {
+ for (next = from; *next; ++next) {
+ if (*next == L'/') {
+ *next = L'\\';
+ break;
+ }
+
+ if (*next == L'\\')
+ break;
+ }
+
+ len = next - from;
+
+ if (len == 1 && from[0] == L'.')
+ /* do nothing with singleton dot */;
+
+ else if (len == 2 && from[0] == L'.' && from[1] == L'.') {
+ if (to == base) {
+ /* no more path segments to strip, eat the "../" */
+ if (*next == L'\\')
+ len++;
+
+ base = to;
+ } else {
+ /* back up a path segment */
+ while (to > base && to[-1] == L'\\') to--;
+ while (to > base && to[-1] != L'\\') to--;
+ }
+ } else {
+ if (*next == L'\\' && *from != L'\\')
+ len++;
+
+ if (to != from)
+ memmove(to, from, sizeof(wchar_t) * len);
+
+ to += len;
+ }
+
+ from += len;
+
+ while (*from == L'\\') from++;
+ }
+
+ /* Strip trailing backslashes */
+ while (to > base && to[-1] == L'\\') to--;
+
+ *to = L'\0';
+
+ if ((to - path) > INT_MAX) {
+ SetLastError(ERROR_FILENAME_EXCED_RANGE);
+ return -1;
+ }
+
+ return (int)(to - path);
+}
+
+static int git_win32_path_join(
+ git_win32_path dest,
+ const wchar_t *one,
+ size_t one_len,
+ const wchar_t *two,
+ size_t two_len)
+{
+ size_t backslash = 0;
+
+ if (one_len && two_len && one[one_len - 1] != L'\\')
+ backslash = 1;
+
+ if (one_len + two_len + backslash > MAX_PATH) {
+ git_error_set(GIT_ERROR_INVALID, "path too long");
+ return -1;
+ }
+
+ memmove(dest, one, one_len * sizeof(wchar_t));
+
+ if (backslash)
+ dest[one_len] = L'\\';
+
+ memcpy(dest + one_len + backslash, two, two_len * sizeof(wchar_t));
+ dest[one_len + backslash + two_len] = L'\0';
+
+ return 0;
+}
+
+struct win32_path_iter {
+ wchar_t *env;
+ const wchar_t *current_dir;
+};
+
+static int win32_path_iter_init(struct win32_path_iter *iter)
+{
+ DWORD len = GetEnvironmentVariableW(L"PATH", NULL, 0);
+
+ if (!len && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
+ iter->env = NULL;
+ iter->current_dir = NULL;
+ return 0;
+ } else if (!len) {
+ git_error_set(GIT_ERROR_OS, "could not load PATH");
+ return -1;
+ }
+
+ iter->env = git__malloc(len * sizeof(wchar_t));
+ GIT_ERROR_CHECK_ALLOC(iter->env);
+
+ len = GetEnvironmentVariableW(L"PATH", iter->env, len);
+
+ if (len == 0) {
+ git_error_set(GIT_ERROR_OS, "could not load PATH");
+ return -1;
+ }
+
+ iter->current_dir = iter->env;
+ return 0;
+}
+
+static int win32_path_iter_next(
+ const wchar_t **out,
+ size_t *out_len,
+ struct win32_path_iter *iter)
+{
+ const wchar_t *start;
+ wchar_t term;
+ size_t len = 0;
+
+ if (!iter->current_dir || !*iter->current_dir)
+ return GIT_ITEROVER;
+
+ term = (*iter->current_dir == L'"') ? *iter->current_dir++ : L';';
+ start = iter->current_dir;
+
+ while (*iter->current_dir && *iter->current_dir != term) {
+ iter->current_dir++;
+ len++;
+ }
+
+ *out = start;
+ *out_len = len;
+
+ if (term == L'"' && *iter->current_dir)
+ iter->current_dir++;
+
+ while (*iter->current_dir == L';')
+ iter->current_dir++;
+
+ return 0;
+}
+
+static void win32_path_iter_dispose(struct win32_path_iter *iter)
+{
+ if (!iter)
+ return;
+
+ git__free(iter->env);
+ iter->env = NULL;
+ iter->current_dir = NULL;
+}
+
+int git_win32_path_find_executable(git_win32_path fullpath, wchar_t *exe)
+{
+ struct win32_path_iter path_iter;
+ const wchar_t *dir;
+ size_t dir_len, exe_len = wcslen(exe);
+ bool found = false;
+
+ if (win32_path_iter_init(&path_iter) < 0)
+ return -1;
+
+ while (win32_path_iter_next(&dir, &dir_len, &path_iter) != GIT_ITEROVER) {
+ if (git_win32_path_join(fullpath, dir, dir_len, exe, exe_len) < 0)
+ continue;
+
+ if (_waccess(fullpath, 0) == 0) {
+ found = true;
+ break;
+ }
+ }
+
+ win32_path_iter_dispose(&path_iter);
+
+ if (found)
+ return 0;
+
+ fullpath[0] = L'\0';
+ return GIT_ENOTFOUND;
+}
+
+static int win32_path_cwd(wchar_t *out, size_t len)
+{
+ int cwd_len;
+
+ if (len > INT_MAX) {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+
+ if ((cwd_len = path__cwd(out, (int)len)) < 0)
+ return -1;
+
+ /* UNC paths */
+ if (wcsncmp(L"\\\\", out, 2) == 0) {
+ /* Our buffer must be at least 5 characters larger than the
+ * current working directory: we swallow one of the leading
+ * '\'s, but we we add a 'UNC' specifier to the path, plus
+ * a trailing directory separator, plus a NUL.
+ */
+ if (cwd_len > GIT_WIN_PATH_MAX - 4) {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+
+ memmove(out+2, out, sizeof(wchar_t) * cwd_len);
+ out[0] = L'U';
+ out[1] = L'N';
+ out[2] = L'C';
+
+ cwd_len += 2;
+ }
+
+ /* Our buffer must be at least 2 characters larger than the current
+ * working directory. (One character for the directory separator,
+ * one for the null.
+ */
+ else if (cwd_len > GIT_WIN_PATH_MAX - 2) {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+
+ return cwd_len;
+}
+
+int git_win32_path_from_utf8(git_win32_path out, const char *src)
+{
+ wchar_t *dest = out;
+
+ /* All win32 paths are in NT-prefixed format, beginning with "\\?\". */
+ memcpy(dest, PATH__NT_NAMESPACE, sizeof(wchar_t) * PATH__NT_NAMESPACE_LEN);
+ dest += PATH__NT_NAMESPACE_LEN;
+
+ /* See if this is an absolute path (beginning with a drive letter) */
+ if (git_fs_path_is_absolute(src)) {
+ if (git_utf8_to_16(dest, GIT_WIN_PATH_MAX, src) < 0)
+ goto on_error;
+ }
+ /* File-prefixed NT-style paths beginning with \\?\ */
+ else if (path__is_nt_namespace(src)) {
+ /* Skip the NT prefix, the destination already contains it */
+ if (git_utf8_to_16(dest, GIT_WIN_PATH_MAX, src + PATH__NT_NAMESPACE_LEN) < 0)
+ goto on_error;
+ }
+ /* UNC paths */
+ else if (path__is_unc(src)) {
+ memcpy(dest, L"UNC\\", sizeof(wchar_t) * 4);
+ dest += 4;
+
+ /* Skip the leading "\\" */
+ if (git_utf8_to_16(dest, GIT_WIN_PATH_MAX - 2, src + 2) < 0)
+ goto on_error;
+ }
+ /* Absolute paths omitting the drive letter */
+ else if (path__startswith_slash(src)) {
+ if (path__cwd(dest, GIT_WIN_PATH_MAX) < 0)
+ goto on_error;
+
+ if (!git_fs_path_is_absolute(dest)) {
+ errno = ENOENT;
+ goto on_error;
+ }
+
+ /* Skip the drive letter specification ("C:") */
+ if (git_utf8_to_16(dest + 2, GIT_WIN_PATH_MAX - 2, src) < 0)
+ goto on_error;
+ }
+ /* Relative paths */
+ else {
+ int cwd_len;
+
+ if ((cwd_len = win32_path_cwd(dest, GIT_WIN_PATH_MAX)) < 0)
+ goto on_error;
+
+ dest[cwd_len++] = L'\\';
+
+ if (git_utf8_to_16(dest + cwd_len, GIT_WIN_PATH_MAX - cwd_len, src) < 0)
+ goto on_error;
+ }
+
+ return git_win32_path_canonicalize(out);
+
+on_error:
+ /* set windows error code so we can use its error message */
+ if (errno == ENAMETOOLONG)
+ SetLastError(ERROR_FILENAME_EXCED_RANGE);
+
+ return -1;
+}
+
+int git_win32_path_relative_from_utf8(git_win32_path out, const char *src)
+{
+ wchar_t *dest = out, *p;
+ int len;
+
+ /* Handle absolute paths */
+ if (git_fs_path_is_absolute(src) ||
+ path__is_nt_namespace(src) ||
+ path__is_unc(src) ||
+ path__startswith_slash(src)) {
+ return git_win32_path_from_utf8(out, src);
+ }
+
+ if ((len = git_utf8_to_16(dest, GIT_WIN_PATH_MAX, src)) < 0)
+ return -1;
+
+ for (p = dest; p < (dest + len); p++) {
+ if (*p == L'/')
+ *p = L'\\';
+ }
+
+ return len;
+}
+
+int git_win32_path_to_utf8(git_win32_utf8_path dest, const wchar_t *src)
+{
+ char *out = dest;
+ int len;
+
+ /* Strip NT namespacing "\\?\" */
+ if (path__is_nt_namespace(src)) {
+ src += 4;
+
+ /* "\\?\UNC\server\share" -> "\\server\share" */
+ if (wcsncmp(src, L"UNC\\", 4) == 0) {
+ src += 4;
+
+ memcpy(dest, "\\\\", 2);
+ out = dest + 2;
+ }
+ }
+
+ if ((len = git_utf8_from_16(out, GIT_WIN_PATH_UTF8, src)) < 0)
+ return len;
+
+ git_fs_path_mkposix(dest);
+
+ return len;
+}
+
+char *git_win32_path_8dot3_name(const char *path)
+{
+ git_win32_path longpath, shortpath;
+ wchar_t *start;
+ char *shortname;
+ int len, namelen = 1;
+
+ if (git_win32_path_from_utf8(longpath, path) < 0)
+ return NULL;
+
+ len = GetShortPathNameW(longpath, shortpath, GIT_WIN_PATH_UTF16);
+
+ while (len && shortpath[len-1] == L'\\')
+ shortpath[--len] = L'\0';
+
+ if (len == 0 || len >= GIT_WIN_PATH_UTF16)
+ return NULL;
+
+ for (start = shortpath + (len - 1);
+ start > shortpath && *(start-1) != '/' && *(start-1) != '\\';
+ start--)
+ namelen++;
+
+ /* We may not have actually been given a short name. But if we have,
+ * it will be in the ASCII byte range, so we don't need to worry about
+ * multi-byte sequences and can allocate naively.
+ */
+ if (namelen > 12 || (shortname = git__malloc(namelen + 1)) == NULL)
+ return NULL;
+
+ if ((len = git_utf8_from_16(shortname, namelen + 1, start)) < 0)
+ return NULL;
+
+ return shortname;
+}
+
+static bool path_is_volume(wchar_t *target, size_t target_len)
+{
+ return (target_len && wcsncmp(target, L"\\??\\Volume{", 11) == 0);
+}
+
+/* On success, returns the length, in characters, of the path stored in dest.
+ * On failure, returns a negative value. */
+int git_win32_path_readlink_w(git_win32_path dest, const git_win32_path path)
+{
+ BYTE buf[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
+ GIT_REPARSE_DATA_BUFFER *reparse_buf = (GIT_REPARSE_DATA_BUFFER *)buf;
+ HANDLE handle = NULL;
+ DWORD ioctl_ret;
+ wchar_t *target;
+ size_t target_len;
+
+ int error = -1;
+
+ handle = CreateFileW(path, GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
+ FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL);
+
+ if (handle == INVALID_HANDLE_VALUE) {
+ errno = ENOENT;
+ return -1;
+ }
+
+ if (!DeviceIoControl(handle, FSCTL_GET_REPARSE_POINT, NULL, 0,
+ reparse_buf, sizeof(buf), &ioctl_ret, NULL)) {
+ errno = EINVAL;
+ goto on_error;
+ }
+
+ switch (reparse_buf->ReparseTag) {
+ case IO_REPARSE_TAG_SYMLINK:
+ target = reparse_buf->ReparseBuffer.SymbolicLink.PathBuffer +
+ (reparse_buf->ReparseBuffer.SymbolicLink.SubstituteNameOffset / sizeof(WCHAR));
+ target_len = reparse_buf->ReparseBuffer.SymbolicLink.SubstituteNameLength / sizeof(WCHAR);
+ break;
+ case IO_REPARSE_TAG_MOUNT_POINT:
+ target = reparse_buf->ReparseBuffer.MountPoint.PathBuffer +
+ (reparse_buf->ReparseBuffer.MountPoint.SubstituteNameOffset / sizeof(WCHAR));
+ target_len = reparse_buf->ReparseBuffer.MountPoint.SubstituteNameLength / sizeof(WCHAR);
+ break;
+ default:
+ errno = EINVAL;
+ goto on_error;
+ }
+
+ if (path_is_volume(target, target_len)) {
+ /* This path is a reparse point that represents another volume mounted
+ * at this location, it is not a symbolic link our input was canonical.
+ */
+ errno = EINVAL;
+ error = -1;
+ } else if (target_len) {
+ /* The path may need to have a namespace prefix removed. */
+ target_len = git_win32_path_remove_namespace(target, target_len);
+
+ /* Need one additional character in the target buffer
+ * for the terminating NULL. */
+ if (GIT_WIN_PATH_UTF16 > target_len) {
+ wcscpy(dest, target);
+ error = (int)target_len;
+ }
+ }
+
+on_error:
+ CloseHandle(handle);
+ return error;
+}
+
+/**
+ * Removes any trailing backslashes from a path, except in the case of a drive
+ * letter path (C:\, D:\, etc.). This function cannot fail.
+ *
+ * @param path The path which should be trimmed.
+ * @return The length of the modified string (<= the input length)
+ */
+size_t git_win32_path_trim_end(wchar_t *str, size_t len)
+{
+ while (1) {
+ if (!len || str[len - 1] != L'\\')
+ break;
+
+ /*
+ * Don't trim backslashes from drive letter paths, which
+ * are 3 characters long and of the form C:\, D:\, etc.
+ */
+ if (len == 3 && git_win32__isalpha(str[0]) && str[1] == ':')
+ break;
+
+ len--;
+ }
+
+ str[len] = L'\0';
+
+ return len;
+}
+
+/**
+ * Removes any of the following namespace prefixes from a path,
+ * if found: "\??\", "\\?\", "\\?\UNC\". This function cannot fail.
+ *
+ * @param path The path which should be converted.
+ * @return The length of the modified string (<= the input length)
+ */
+size_t git_win32_path_remove_namespace(wchar_t *str, size_t len)
+{
+ static const wchar_t dosdevices_namespace[] = L"\\\?\?\\";
+ static const wchar_t nt_namespace[] = L"\\\\?\\";
+ static const wchar_t unc_namespace_remainder[] = L"UNC\\";
+ static const wchar_t unc_prefix[] = L"\\\\";
+
+ const wchar_t *prefix = NULL, *remainder = NULL;
+ size_t prefix_len = 0, remainder_len = 0;
+
+ /* "\??\" -- DOS Devices prefix */
+ if (len >= CONST_STRLEN(dosdevices_namespace) &&
+ !wcsncmp(str, dosdevices_namespace, CONST_STRLEN(dosdevices_namespace))) {
+ remainder = str + CONST_STRLEN(dosdevices_namespace);
+ remainder_len = len - CONST_STRLEN(dosdevices_namespace);
+ }
+ /* "\\?\" -- NT namespace prefix */
+ else if (len >= CONST_STRLEN(nt_namespace) &&
+ !wcsncmp(str, nt_namespace, CONST_STRLEN(nt_namespace))) {
+ remainder = str + CONST_STRLEN(nt_namespace);
+ remainder_len = len - CONST_STRLEN(nt_namespace);
+ }
+
+ /* "\??\UNC\", "\\?\UNC\" -- UNC prefix */
+ if (remainder_len >= CONST_STRLEN(unc_namespace_remainder) &&
+ !wcsncmp(remainder, unc_namespace_remainder, CONST_STRLEN(unc_namespace_remainder))) {
+
+ /*
+ * The proper Win32 path for a UNC share has "\\" at beginning of it
+ * and looks like "\\server\share\<folderStructure>". So remove the
+ * UNC namespace and add a prefix of "\\" in its place.
+ */
+ remainder += CONST_STRLEN(unc_namespace_remainder);
+ remainder_len -= CONST_STRLEN(unc_namespace_remainder);
+
+ prefix = unc_prefix;
+ prefix_len = CONST_STRLEN(unc_prefix);
+ }
+
+ /*
+ * Sanity check that the new string isn't longer than the old one.
+ * (This could only happen due to programmer error introducing a
+ * prefix longer than the namespace it replaces.)
+ */
+ if (remainder && len >= remainder_len + prefix_len) {
+ if (prefix)
+ memmove(str, prefix, prefix_len * sizeof(wchar_t));
+
+ memmove(str + prefix_len, remainder, remainder_len * sizeof(wchar_t));
+
+ len = remainder_len + prefix_len;
+ str[len] = L'\0';
+ }
+
+ return git_win32_path_trim_end(str, len);
+}
diff --git a/src/util/win32/path_w32.h b/src/util/win32/path_w32.h
new file mode 100644
index 0000000..b241d5c
--- /dev/null
+++ b/src/util/win32/path_w32.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_win32_path_w32_h__
+#define INCLUDE_win32_path_w32_h__
+
+#include "git2_util.h"
+
+/**
+ * Create a Win32 path (in UCS-2 format) from a UTF-8 string. If the given
+ * path is relative, then it will be turned into an absolute path by having
+ * the current working directory prepended.
+ *
+ * @param dest The buffer to receive the wide string.
+ * @param src The UTF-8 string to convert.
+ * @return The length of the wide string, in characters (not counting the NULL terminator), or < 0 for failure
+ */
+extern int git_win32_path_from_utf8(git_win32_path dest, const char *src);
+
+/**
+ * Create a Win32 path (in UCS-2 format) from a UTF-8 string. If the given
+ * path is relative, then it will not be turned into an absolute path.
+ *
+ * @param dest The buffer to receive the wide string.
+ * @param src The UTF-8 string to convert.
+ * @return The length of the wide string, in characters (not counting the NULL terminator), or < 0 for failure
+ */
+extern int git_win32_path_relative_from_utf8(git_win32_path dest, const char *src);
+
+/**
+ * Canonicalize a Win32 UCS-2 path so that it is suitable for delivery to the
+ * Win32 APIs: remove multiple directory separators, squashing to a single one,
+ * strip trailing directory separators, ensure directory separators are all
+ * canonical (always backslashes, never forward slashes) and process any
+ * directory entries of '.' or '..'.
+ *
+ * Note that this is intended to be used on absolute Windows paths, those
+ * that start with `C:\`, `\\server\share`, `\\?\`, etc.
+ *
+ * This processes the buffer in place.
+ *
+ * @param path The buffer to process
+ * @return The new length of the buffer, in wchar_t's (not counting the NULL terminator)
+ */
+extern int git_win32_path_canonicalize(git_win32_path path);
+
+/**
+ * Create an internal format (posix-style) UTF-8 path from a Win32 UCS-2 path.
+ *
+ * @param dest The buffer to receive the UTF-8 string.
+ * @param src The wide string to convert.
+ * @return The length of the UTF-8 string, in bytes (not counting the NULL terminator), or < 0 for failure
+ */
+extern int git_win32_path_to_utf8(git_win32_utf8_path dest, const wchar_t *src);
+
+/**
+ * Get the short name for the terminal path component in the given path.
+ * For example, given "C:\Foo\Bar\Asdf.txt", this will return the short name
+ * for the file "Asdf.txt".
+ *
+ * @param path The given path in UTF-8
+ * @return The name of the shortname for the given path
+ */
+extern char *git_win32_path_8dot3_name(const char *path);
+
+extern int git_win32_path_readlink_w(git_win32_path dest, const git_win32_path path);
+
+/**
+ * Removes any trailing backslashes from a path, except in the case of a drive
+ * letter path (C:\, D:\, etc.). This function cannot fail.
+ *
+ * @param path The path which should be trimmed.
+ * @return The length of the modified string (<= the input length)
+ */
+size_t git_win32_path_trim_end(wchar_t *str, size_t len);
+
+/**
+ * Removes any of the following namespace prefixes from a path,
+ * if found: "\??\", "\\?\", "\\?\UNC\". This function cannot fail.
+ *
+ * @param path The path which should be converted.
+ * @return The length of the modified string (<= the input length)
+ */
+size_t git_win32_path_remove_namespace(wchar_t *str, size_t len);
+
+int git_win32_path_find_executable(git_win32_path fullpath, wchar_t* exe);
+
+#endif
diff --git a/src/util/win32/posix.h b/src/util/win32/posix.h
new file mode 100644
index 0000000..03fa2ac
--- /dev/null
+++ b/src/util/win32/posix.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_win32_posix_h__
+#define INCLUDE_win32_posix_h__
+
+#include "git2_util.h"
+#include "../posix.h"
+#include "win32-compat.h"
+#include "path_w32.h"
+#include "utf-conv.h"
+#include "dir.h"
+
+extern unsigned long git_win32__createfile_sharemode;
+extern int git_win32__retries;
+
+typedef SOCKET GIT_SOCKET;
+
+#define p_lseek(f,n,w) _lseeki64(f, n, w)
+
+extern int p_fstat(int fd, struct stat *buf);
+extern int p_lstat(const char *file_name, struct stat *buf);
+extern int p_stat(const char *path, struct stat *buf);
+
+extern int p_utimes(const char *filename, const struct p_timeval times[2]);
+extern int p_futimes(int fd, const struct p_timeval times[2]);
+
+extern int p_readlink(const char *path, char *buf, size_t bufsiz);
+extern int p_symlink(const char *old, const char *new);
+extern int p_link(const char *old, const char *new);
+extern int p_unlink(const char *path);
+extern int p_mkdir(const char *path, mode_t mode);
+extern int p_fsync(int fd);
+extern char *p_realpath(const char *orig_path, char *buffer);
+
+extern int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags);
+extern int p_send(GIT_SOCKET socket, const void *buffer, size_t length, int flags);
+extern int p_inet_pton(int af, const char *src, void* dst);
+
+extern int p_vsnprintf(char *buffer, size_t count, const char *format, va_list argptr);
+extern int p_snprintf(char *buffer, size_t count, const char *format, ...) GIT_FORMAT_PRINTF(3, 4);
+extern int p_chdir(const char *path);
+extern int p_chmod(const char *path, mode_t mode);
+extern int p_rmdir(const char *path);
+extern int p_access(const char *path, mode_t mode);
+extern int p_ftruncate(int fd, off64_t size);
+
+/* p_lstat is almost but not quite POSIX correct. Specifically, the use of
+ * ENOTDIR is wrong, in that it does not mean precisely that a non-directory
+ * entry was encountered. Making it correct is potentially expensive,
+ * however, so this is a separate version of p_lstat to use when correct
+ * POSIX ENOTDIR semantics is required.
+ */
+extern int p_lstat_posixly(const char *filename, struct stat *buf);
+
+extern struct tm * p_localtime_r(const time_t *timer, struct tm *result);
+extern struct tm * p_gmtime_r(const time_t *timer, struct tm *result);
+
+#endif
diff --git a/src/util/win32/posix_w32.c b/src/util/win32/posix_w32.c
new file mode 100644
index 0000000..3fec469
--- /dev/null
+++ b/src/util/win32/posix_w32.c
@@ -0,0 +1,1047 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "git2_util.h"
+
+#include "../posix.h"
+#include "../futils.h"
+#include "fs_path.h"
+#include "path_w32.h"
+#include "utf-conv.h"
+#include "reparse.h"
+#include <errno.h>
+#include <io.h>
+#include <fcntl.h>
+#include <ws2tcpip.h>
+
+#ifndef FILE_NAME_NORMALIZED
+# define FILE_NAME_NORMALIZED 0
+#endif
+
+#ifndef IO_REPARSE_TAG_SYMLINK
+#define IO_REPARSE_TAG_SYMLINK (0xA000000CL)
+#endif
+
+#ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
+# define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE 0x02
+#endif
+
+#ifndef SYMBOLIC_LINK_FLAG_DIRECTORY
+# define SYMBOLIC_LINK_FLAG_DIRECTORY 0x01
+#endif
+
+/* Allowable mode bits on Win32. Using mode bits that are not supported on
+ * Win32 (eg S_IRWXU) is generally ignored, but Wine warns loudly about it
+ * so we simply remove them.
+ */
+#define WIN32_MODE_MASK (_S_IREAD | _S_IWRITE)
+
+unsigned long git_win32__createfile_sharemode =
+ FILE_SHARE_READ | FILE_SHARE_WRITE;
+int git_win32__retries = 10;
+
+GIT_INLINE(void) set_errno(void)
+{
+ switch (GetLastError()) {
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ case ERROR_INVALID_DRIVE:
+ case ERROR_NO_MORE_FILES:
+ case ERROR_BAD_NETPATH:
+ case ERROR_BAD_NET_NAME:
+ case ERROR_BAD_PATHNAME:
+ case ERROR_FILENAME_EXCED_RANGE:
+ errno = ENOENT;
+ break;
+ case ERROR_BAD_ENVIRONMENT:
+ errno = E2BIG;
+ break;
+ case ERROR_BAD_FORMAT:
+ case ERROR_INVALID_STARTING_CODESEG:
+ case ERROR_INVALID_STACKSEG:
+ case ERROR_INVALID_MODULETYPE:
+ case ERROR_INVALID_EXE_SIGNATURE:
+ case ERROR_EXE_MARKED_INVALID:
+ case ERROR_BAD_EXE_FORMAT:
+ case ERROR_ITERATED_DATA_EXCEEDS_64k:
+ case ERROR_INVALID_MINALLOCSIZE:
+ case ERROR_DYNLINK_FROM_INVALID_RING:
+ case ERROR_IOPL_NOT_ENABLED:
+ case ERROR_INVALID_SEGDPL:
+ case ERROR_AUTODATASEG_EXCEEDS_64k:
+ case ERROR_RING2SEG_MUST_BE_MOVABLE:
+ case ERROR_RELOC_CHAIN_XEEDS_SEGLIM:
+ case ERROR_INFLOOP_IN_RELOC_CHAIN:
+ errno = ENOEXEC;
+ break;
+ case ERROR_INVALID_HANDLE:
+ case ERROR_INVALID_TARGET_HANDLE:
+ case ERROR_DIRECT_ACCESS_HANDLE:
+ errno = EBADF;
+ break;
+ case ERROR_WAIT_NO_CHILDREN:
+ case ERROR_CHILD_NOT_COMPLETE:
+ errno = ECHILD;
+ break;
+ case ERROR_NO_PROC_SLOTS:
+ case ERROR_MAX_THRDS_REACHED:
+ case ERROR_NESTING_NOT_ALLOWED:
+ errno = EAGAIN;
+ break;
+ case ERROR_ARENA_TRASHED:
+ case ERROR_NOT_ENOUGH_MEMORY:
+ case ERROR_INVALID_BLOCK:
+ case ERROR_NOT_ENOUGH_QUOTA:
+ errno = ENOMEM;
+ break;
+ case ERROR_ACCESS_DENIED:
+ case ERROR_CURRENT_DIRECTORY:
+ case ERROR_WRITE_PROTECT:
+ case ERROR_BAD_UNIT:
+ case ERROR_NOT_READY:
+ case ERROR_BAD_COMMAND:
+ case ERROR_CRC:
+ case ERROR_BAD_LENGTH:
+ case ERROR_SEEK:
+ case ERROR_NOT_DOS_DISK:
+ case ERROR_SECTOR_NOT_FOUND:
+ case ERROR_OUT_OF_PAPER:
+ case ERROR_WRITE_FAULT:
+ case ERROR_READ_FAULT:
+ case ERROR_GEN_FAILURE:
+ case ERROR_SHARING_VIOLATION:
+ case ERROR_LOCK_VIOLATION:
+ case ERROR_WRONG_DISK:
+ case ERROR_SHARING_BUFFER_EXCEEDED:
+ case ERROR_NETWORK_ACCESS_DENIED:
+ case ERROR_CANNOT_MAKE:
+ case ERROR_FAIL_I24:
+ case ERROR_DRIVE_LOCKED:
+ case ERROR_SEEK_ON_DEVICE:
+ case ERROR_NOT_LOCKED:
+ case ERROR_LOCK_FAILED:
+ errno = EACCES;
+ break;
+ case ERROR_FILE_EXISTS:
+ case ERROR_ALREADY_EXISTS:
+ errno = EEXIST;
+ break;
+ case ERROR_NOT_SAME_DEVICE:
+ errno = EXDEV;
+ break;
+ case ERROR_INVALID_FUNCTION:
+ case ERROR_INVALID_ACCESS:
+ case ERROR_INVALID_DATA:
+ case ERROR_INVALID_PARAMETER:
+ case ERROR_NEGATIVE_SEEK:
+ errno = EINVAL;
+ break;
+ case ERROR_TOO_MANY_OPEN_FILES:
+ errno = EMFILE;
+ break;
+ case ERROR_DISK_FULL:
+ errno = ENOSPC;
+ break;
+ case ERROR_BROKEN_PIPE:
+ errno = EPIPE;
+ break;
+ case ERROR_DIR_NOT_EMPTY:
+ errno = ENOTEMPTY;
+ break;
+ default:
+ errno = EINVAL;
+ }
+}
+
+GIT_INLINE(bool) last_error_retryable(void)
+{
+ int os_error = GetLastError();
+
+ return (os_error == ERROR_SHARING_VIOLATION ||
+ os_error == ERROR_ACCESS_DENIED);
+}
+
+#define do_with_retries(fn, remediation) \
+ do { \
+ int __retry, __ret; \
+ for (__retry = git_win32__retries; __retry; __retry--) { \
+ if ((__ret = (fn)) != GIT_RETRY) \
+ return __ret; \
+ if (__retry > 1 && (__ret = (remediation)) != 0) { \
+ if (__ret == GIT_RETRY) \
+ continue; \
+ return __ret; \
+ } \
+ Sleep(5); \
+ } \
+ return -1; \
+ } while (0) \
+
+static int ensure_writable(wchar_t *path)
+{
+ DWORD attrs;
+
+ if ((attrs = GetFileAttributesW(path)) == INVALID_FILE_ATTRIBUTES)
+ goto on_error;
+
+ if ((attrs & FILE_ATTRIBUTE_READONLY) == 0)
+ return 0;
+
+ if (!SetFileAttributesW(path, (attrs & ~FILE_ATTRIBUTE_READONLY)))
+ goto on_error;
+
+ return GIT_RETRY;
+
+on_error:
+ set_errno();
+ return -1;
+}
+
+/**
+ * Truncate or extend file.
+ *
+ * We now take a "git_off_t" rather than "long" because
+ * files may be longer than 2Gb.
+ */
+int p_ftruncate(int fd, off64_t size)
+{
+ if (size < 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+#if !defined(__MINGW32__) || defined(MINGW_HAS_SECURE_API)
+ return ((_chsize_s(fd, size) == 0) ? 0 : -1);
+#else
+ /* TODO MINGW32 Find a replacement for _chsize() that handles big files. */
+ if (size > INT32_MAX) {
+ errno = EFBIG;
+ return -1;
+ }
+ return _chsize(fd, (long)size);
+#endif
+}
+
+int p_mkdir(const char *path, mode_t mode)
+{
+ git_win32_path buf;
+
+ GIT_UNUSED(mode);
+
+ if (git_win32_path_from_utf8(buf, path) < 0)
+ return -1;
+
+ return _wmkdir(buf);
+}
+
+int p_link(const char *old, const char *new)
+{
+ GIT_UNUSED(old);
+ GIT_UNUSED(new);
+ errno = ENOSYS;
+ return -1;
+}
+
+GIT_INLINE(int) unlink_once(const wchar_t *path)
+{
+ DWORD error;
+
+ if (DeleteFileW(path))
+ return 0;
+
+ if ((error = GetLastError()) == ERROR_ACCESS_DENIED) {
+ WIN32_FILE_ATTRIBUTE_DATA fdata;
+ if (!GetFileAttributesExW(path, GetFileExInfoStandard, &fdata) ||
+ !(fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) ||
+ !(fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
+ goto out;
+
+ if (RemoveDirectoryW(path))
+ return 0;
+ }
+
+out:
+ SetLastError(error);
+
+ if (last_error_retryable())
+ return GIT_RETRY;
+
+ set_errno();
+ return -1;
+}
+
+int p_unlink(const char *path)
+{
+ git_win32_path wpath;
+
+ if (git_win32_path_from_utf8(wpath, path) < 0)
+ return -1;
+
+ do_with_retries(unlink_once(wpath), ensure_writable(wpath));
+}
+
+int p_fsync(int fd)
+{
+ HANDLE fh = (HANDLE)_get_osfhandle(fd);
+
+ p_fsync__cnt++;
+
+ if (fh == INVALID_HANDLE_VALUE) {
+ errno = EBADF;
+ return -1;
+ }
+
+ if (!FlushFileBuffers(fh)) {
+ DWORD code = GetLastError();
+
+ if (code == ERROR_INVALID_HANDLE)
+ errno = EINVAL;
+ else
+ errno = EIO;
+
+ return -1;
+ }
+
+ return 0;
+}
+
+#define WIN32_IS_WSEP(CH) ((CH) == L'/' || (CH) == L'\\')
+
+static int lstat_w(
+ wchar_t *path,
+ struct stat *buf,
+ bool posix_enotdir)
+{
+ WIN32_FILE_ATTRIBUTE_DATA fdata;
+
+ if (GetFileAttributesExW(path, GetFileExInfoStandard, &fdata)) {
+ if (!buf)
+ return 0;
+
+ return git_win32__file_attribute_to_stat(buf, &fdata, path);
+ }
+
+ switch (GetLastError()) {
+ case ERROR_ACCESS_DENIED:
+ errno = EACCES;
+ break;
+ default:
+ errno = ENOENT;
+ break;
+ }
+
+ /* To match POSIX behavior, set ENOTDIR when any of the folders in the
+ * file path is a regular file, otherwise set ENOENT.
+ */
+ if (errno == ENOENT && posix_enotdir) {
+ size_t path_len = wcslen(path);
+
+ /* scan up path until we find an existing item */
+ while (1) {
+ DWORD attrs;
+
+ /* remove last directory component */
+ for (path_len--; path_len > 0 && !WIN32_IS_WSEP(path[path_len]); path_len--);
+
+ if (path_len <= 0)
+ break;
+
+ path[path_len] = L'\0';
+ attrs = GetFileAttributesW(path);
+
+ if (attrs != INVALID_FILE_ATTRIBUTES) {
+ if (!(attrs & FILE_ATTRIBUTE_DIRECTORY))
+ errno = ENOTDIR;
+ break;
+ }
+ }
+ }
+
+ return -1;
+}
+
+static int do_lstat(const char *path, struct stat *buf, bool posixly_correct)
+{
+ git_win32_path path_w;
+ int len;
+
+ if ((len = git_win32_path_from_utf8(path_w, path)) < 0)
+ return -1;
+
+ git_win32_path_trim_end(path_w, len);
+
+ return lstat_w(path_w, buf, posixly_correct);
+}
+
+int p_lstat(const char *filename, struct stat *buf)
+{
+ return do_lstat(filename, buf, false);
+}
+
+int p_lstat_posixly(const char *filename, struct stat *buf)
+{
+ return do_lstat(filename, buf, true);
+}
+
+int p_readlink(const char *path, char *buf, size_t bufsiz)
+{
+ git_win32_path path_w, target_w;
+ git_win32_utf8_path target;
+ int len;
+
+ /* readlink(2) does not NULL-terminate the string written
+ * to the target buffer. Furthermore, the target buffer need
+ * not be large enough to hold the entire result. A truncated
+ * result should be written in this case. Since this truncation
+ * could occur in the middle of the encoding of a code point,
+ * we need to buffer the result on the stack. */
+
+ if (git_win32_path_from_utf8(path_w, path) < 0 ||
+ git_win32_path_readlink_w(target_w, path_w) < 0 ||
+ (len = git_win32_path_to_utf8(target, target_w)) < 0)
+ return -1;
+
+ bufsiz = min((size_t)len, bufsiz);
+ memcpy(buf, target, bufsiz);
+
+ return (int)bufsiz;
+}
+
+static bool target_is_dir(const char *target, const char *path)
+{
+ git_str resolved = GIT_STR_INIT;
+ git_win32_path resolved_w;
+ bool isdir = true;
+
+ if (git_fs_path_is_absolute(target))
+ git_win32_path_from_utf8(resolved_w, target);
+ else if (git_fs_path_dirname_r(&resolved, path) < 0 ||
+ git_fs_path_apply_relative(&resolved, target) < 0 ||
+ git_win32_path_from_utf8(resolved_w, resolved.ptr) < 0)
+ goto out;
+
+ isdir = GetFileAttributesW(resolved_w) & FILE_ATTRIBUTE_DIRECTORY;
+
+out:
+ git_str_dispose(&resolved);
+ return isdir;
+}
+
+int p_symlink(const char *target, const char *path)
+{
+ git_win32_path target_w, path_w;
+ DWORD dwFlags;
+
+ /*
+ * Convert both target and path to Windows-style paths. Note that we do
+ * not want to use `git_win32_path_from_utf8` for converting the target,
+ * as that function will automatically pre-pend the current working
+ * directory in case the path is not absolute. As Git will instead use
+ * relative symlinks, this is not something we want.
+ */
+ if (git_win32_path_from_utf8(path_w, path) < 0 ||
+ git_win32_path_relative_from_utf8(target_w, target) < 0)
+ return -1;
+
+ dwFlags = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
+ if (target_is_dir(target, path))
+ dwFlags |= SYMBOLIC_LINK_FLAG_DIRECTORY;
+
+ if (!CreateSymbolicLinkW(path_w, target_w, dwFlags))
+ return -1;
+
+ return 0;
+}
+
+struct open_opts {
+ DWORD access;
+ DWORD sharing;
+ SECURITY_ATTRIBUTES security;
+ DWORD creation_disposition;
+ DWORD attributes;
+ int osf_flags;
+};
+
+GIT_INLINE(void) open_opts_from_posix(struct open_opts *opts, int flags, mode_t mode)
+{
+ memset(opts, 0, sizeof(struct open_opts));
+
+ switch (flags & (O_WRONLY | O_RDWR)) {
+ case O_WRONLY:
+ opts->access = GENERIC_WRITE;
+ break;
+ case O_RDWR:
+ opts->access = GENERIC_READ | GENERIC_WRITE;
+ break;
+ default:
+ opts->access = GENERIC_READ;
+ break;
+ }
+
+ opts->sharing = (DWORD)git_win32__createfile_sharemode;
+
+ switch (flags & (O_CREAT | O_TRUNC | O_EXCL)) {
+ case O_CREAT | O_EXCL:
+ case O_CREAT | O_TRUNC | O_EXCL:
+ opts->creation_disposition = CREATE_NEW;
+ break;
+ case O_CREAT | O_TRUNC:
+ opts->creation_disposition = CREATE_ALWAYS;
+ break;
+ case O_TRUNC:
+ opts->creation_disposition = TRUNCATE_EXISTING;
+ break;
+ case O_CREAT:
+ opts->creation_disposition = OPEN_ALWAYS;
+ break;
+ default:
+ opts->creation_disposition = OPEN_EXISTING;
+ break;
+ }
+
+ opts->attributes = ((flags & O_CREAT) && !(mode & S_IWRITE)) ?
+ FILE_ATTRIBUTE_READONLY : FILE_ATTRIBUTE_NORMAL;
+ opts->osf_flags = flags & (O_RDONLY | O_APPEND);
+
+ opts->security.nLength = sizeof(SECURITY_ATTRIBUTES);
+ opts->security.lpSecurityDescriptor = NULL;
+ opts->security.bInheritHandle = 0;
+}
+
+GIT_INLINE(int) open_once(
+ const wchar_t *path,
+ struct open_opts *opts)
+{
+ int fd;
+
+ HANDLE handle = CreateFileW(path, opts->access, opts->sharing,
+ &opts->security, opts->creation_disposition, opts->attributes, 0);
+
+ if (handle == INVALID_HANDLE_VALUE) {
+ if (last_error_retryable())
+ return GIT_RETRY;
+
+ set_errno();
+ return -1;
+ }
+
+ if ((fd = _open_osfhandle((intptr_t)handle, opts->osf_flags)) < 0)
+ CloseHandle(handle);
+
+ return fd;
+}
+
+int p_open(const char *path, int flags, ...)
+{
+ git_win32_path wpath;
+ mode_t mode = 0;
+ struct open_opts opts = {0};
+
+ #ifdef GIT_DEBUG_STRICT_OPEN
+ if (strstr(path, "//") != NULL) {
+ errno = EACCES;
+ return -1;
+ }
+ #endif
+
+ if (git_win32_path_from_utf8(wpath, path) < 0)
+ return -1;
+
+ if (flags & O_CREAT) {
+ va_list arg_list;
+
+ va_start(arg_list, flags);
+ mode = (mode_t)va_arg(arg_list, int);
+ va_end(arg_list);
+ }
+
+ open_opts_from_posix(&opts, flags, mode);
+
+ do_with_retries(
+ open_once(wpath, &opts),
+ 0);
+}
+
+int p_creat(const char *path, mode_t mode)
+{
+ return p_open(path, O_WRONLY | O_CREAT | O_TRUNC, mode);
+}
+
+int p_utimes(const char *path, const struct p_timeval times[2])
+{
+ git_win32_path wpath;
+ int fd, error;
+ DWORD attrs_orig, attrs_new = 0;
+ struct open_opts opts = { 0 };
+
+ if (git_win32_path_from_utf8(wpath, path) < 0)
+ return -1;
+
+ attrs_orig = GetFileAttributesW(wpath);
+
+ if (attrs_orig & FILE_ATTRIBUTE_READONLY) {
+ attrs_new = attrs_orig & ~FILE_ATTRIBUTE_READONLY;
+
+ if (!SetFileAttributesW(wpath, attrs_new)) {
+ git_error_set(GIT_ERROR_OS, "failed to set attributes");
+ return -1;
+ }
+ }
+
+ open_opts_from_posix(&opts, O_RDWR, 0);
+
+ if ((fd = open_once(wpath, &opts)) < 0) {
+ error = -1;
+ goto done;
+ }
+
+ error = p_futimes(fd, times);
+ close(fd);
+
+done:
+ if (attrs_orig != attrs_new) {
+ DWORD os_error = GetLastError();
+ SetFileAttributesW(wpath, attrs_orig);
+ SetLastError(os_error);
+ }
+
+ return error;
+}
+
+int p_futimes(int fd, const struct p_timeval times[2])
+{
+ HANDLE handle;
+ FILETIME atime = { 0 }, mtime = { 0 };
+
+ if (times == NULL) {
+ SYSTEMTIME st;
+
+ GetSystemTime(&st);
+ SystemTimeToFileTime(&st, &atime);
+ SystemTimeToFileTime(&st, &mtime);
+ }
+ else {
+ git_win32__timeval_to_filetime(&atime, times[0]);
+ git_win32__timeval_to_filetime(&mtime, times[1]);
+ }
+
+ if ((handle = (HANDLE)_get_osfhandle(fd)) == INVALID_HANDLE_VALUE)
+ return -1;
+
+ if (SetFileTime(handle, NULL, &atime, &mtime) == 0)
+ return -1;
+
+ return 0;
+}
+
+int p_getcwd(char *buffer_out, size_t size)
+{
+ git_win32_path buf;
+ wchar_t *cwd = _wgetcwd(buf, GIT_WIN_PATH_UTF16);
+
+ if (!cwd)
+ return -1;
+
+ git_win32_path_remove_namespace(cwd, wcslen(cwd));
+
+ /* Convert the working directory back to UTF-8 */
+ if (git_utf8_from_16(buffer_out, size, cwd) < 0) {
+ DWORD code = GetLastError();
+
+ if (code == ERROR_INSUFFICIENT_BUFFER)
+ errno = ERANGE;
+ else
+ errno = EINVAL;
+
+ return -1;
+ }
+
+ git_fs_path_mkposix(buffer_out);
+ return 0;
+}
+
+static int getfinalpath_w(
+ git_win32_path dest,
+ const wchar_t *path)
+{
+ HANDLE hFile;
+ DWORD dwChars;
+
+ /* Use FILE_FLAG_BACKUP_SEMANTICS so we can open a directory. Do not
+ * specify FILE_FLAG_OPEN_REPARSE_POINT; we want to open a handle to the
+ * target of the link. */
+ hFile = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_DELETE,
+ NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
+
+ if (INVALID_HANDLE_VALUE == hFile)
+ return -1;
+
+ /* Call GetFinalPathNameByHandle */
+ dwChars = GetFinalPathNameByHandleW(hFile, dest, GIT_WIN_PATH_UTF16, FILE_NAME_NORMALIZED);
+ CloseHandle(hFile);
+
+ if (!dwChars || dwChars >= GIT_WIN_PATH_UTF16)
+ return -1;
+
+ /* The path may be delivered to us with a namespace prefix; remove */
+ return (int)git_win32_path_remove_namespace(dest, dwChars);
+}
+
+static int follow_and_lstat_link(git_win32_path path, struct stat *buf)
+{
+ git_win32_path target_w;
+
+ if (getfinalpath_w(target_w, path) < 0)
+ return -1;
+
+ return lstat_w(target_w, buf, false);
+}
+
+int p_fstat(int fd, struct stat *buf)
+{
+ BY_HANDLE_FILE_INFORMATION fhInfo;
+
+ HANDLE fh = (HANDLE)_get_osfhandle(fd);
+
+ if (fh == INVALID_HANDLE_VALUE ||
+ !GetFileInformationByHandle(fh, &fhInfo)) {
+ errno = EBADF;
+ return -1;
+ }
+
+ git_win32__file_information_to_stat(buf, &fhInfo);
+ return 0;
+}
+
+int p_stat(const char *path, struct stat *buf)
+{
+ git_win32_path path_w;
+ int len;
+
+ if ((len = git_win32_path_from_utf8(path_w, path)) < 0 ||
+ lstat_w(path_w, buf, false) < 0)
+ return -1;
+
+ /* The item is a symbolic link or mount point. No need to iterate
+ * to follow multiple links; use GetFinalPathNameFromHandle. */
+ if (S_ISLNK(buf->st_mode))
+ return follow_and_lstat_link(path_w, buf);
+
+ return 0;
+}
+
+int p_chdir(const char *path)
+{
+ git_win32_path buf;
+
+ if (git_win32_path_from_utf8(buf, path) < 0)
+ return -1;
+
+ return _wchdir(buf);
+}
+
+int p_chmod(const char *path, mode_t mode)
+{
+ git_win32_path buf;
+
+ if (git_win32_path_from_utf8(buf, path) < 0)
+ return -1;
+
+ return _wchmod(buf, mode);
+}
+
+int p_rmdir(const char *path)
+{
+ git_win32_path buf;
+ int error;
+
+ if (git_win32_path_from_utf8(buf, path) < 0)
+ return -1;
+
+ error = _wrmdir(buf);
+
+ if (error == -1) {
+ switch (GetLastError()) {
+ /* _wrmdir() is documented to return EACCES if "A program has an open
+ * handle to the directory." This sounds like what everybody else calls
+ * EBUSY. Let's convert appropriate error codes.
+ */
+ case ERROR_SHARING_VIOLATION:
+ errno = EBUSY;
+ break;
+
+ /* This error can be returned when trying to rmdir an extant file. */
+ case ERROR_DIRECTORY:
+ errno = ENOTDIR;
+ break;
+ }
+ }
+
+ return error;
+}
+
+char *p_realpath(const char *orig_path, char *buffer)
+{
+ git_win32_path orig_path_w, buffer_w;
+
+ if (git_win32_path_from_utf8(orig_path_w, orig_path) < 0)
+ return NULL;
+
+ /* Note that if the path provided is a relative path, then the current directory
+ * is used to resolve the path -- which is a concurrency issue because the current
+ * directory is a process-wide variable. */
+ if (!GetFullPathNameW(orig_path_w, GIT_WIN_PATH_UTF16, buffer_w, NULL)) {
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ errno = ENAMETOOLONG;
+ else
+ errno = EINVAL;
+
+ return NULL;
+ }
+
+ /* The path must exist. */
+ if (GetFileAttributesW(buffer_w) == INVALID_FILE_ATTRIBUTES) {
+ errno = ENOENT;
+ return NULL;
+ }
+
+ if (!buffer && !(buffer = git__malloc(GIT_WIN_PATH_UTF8))) {
+ errno = ENOMEM;
+ return NULL;
+ }
+
+ /* Convert the path to UTF-8. If the caller provided a buffer, then it
+ * is assumed to be GIT_WIN_PATH_UTF8 characters in size. If it isn't,
+ * then we may overflow. */
+ if (git_win32_path_to_utf8(buffer, buffer_w) < 0)
+ return NULL;
+
+ git_fs_path_mkposix(buffer);
+
+ return buffer;
+}
+
+int p_vsnprintf(char *buffer, size_t count, const char *format, va_list argptr)
+{
+#if defined(_MSC_VER)
+ int len;
+
+ if (count == 0)
+ return _vscprintf(format, argptr);
+
+ #if _MSC_VER >= 1500
+ len = _vsnprintf_s(buffer, count, _TRUNCATE, format, argptr);
+ #else
+ len = _vsnprintf(buffer, count, format, argptr);
+ #endif
+
+ if (len < 0)
+ return _vscprintf(format, argptr);
+
+ return len;
+#else /* MinGW */
+ return vsnprintf(buffer, count, format, argptr);
+#endif
+}
+
+int p_snprintf(char *buffer, size_t count, const char *format, ...)
+{
+ va_list va;
+ int r;
+
+ va_start(va, format);
+ r = p_vsnprintf(buffer, count, format, va);
+ va_end(va);
+
+ return r;
+}
+
+int p_access(const char *path, mode_t mode)
+{
+ git_win32_path buf;
+
+ if (git_win32_path_from_utf8(buf, path) < 0)
+ return -1;
+
+ return _waccess(buf, mode & WIN32_MODE_MASK);
+}
+
+GIT_INLINE(int) rename_once(const wchar_t *from, const wchar_t *to)
+{
+ if (MoveFileExW(from, to, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED))
+ return 0;
+
+ if (last_error_retryable())
+ return GIT_RETRY;
+
+ set_errno();
+ return -1;
+}
+
+int p_rename(const char *from, const char *to)
+{
+ git_win32_path wfrom, wto;
+
+ if (git_win32_path_from_utf8(wfrom, from) < 0 ||
+ git_win32_path_from_utf8(wto, to) < 0)
+ return -1;
+
+ do_with_retries(rename_once(wfrom, wto), ensure_writable(wto));
+}
+
+int p_recv(GIT_SOCKET socket, void *buffer, size_t length, int flags)
+{
+ if ((size_t)((int)length) != length)
+ return -1; /* git_error_set will be done by caller */
+
+ return recv(socket, buffer, (int)length, flags);
+}
+
+int p_send(GIT_SOCKET socket, const void *buffer, size_t length, int flags)
+{
+ if ((size_t)((int)length) != length)
+ return -1; /* git_error_set will be done by caller */
+
+ return send(socket, buffer, (int)length, flags);
+}
+
+/**
+ * Borrowed from http://old.nabble.com/Porting-localtime_r-and-gmtime_r-td15282276.html
+ * On Win32, `gmtime_r` doesn't exist but `gmtime` is threadsafe, so we can use that
+ */
+struct tm *
+p_localtime_r (const time_t *timer, struct tm *result)
+{
+ struct tm *local_result;
+ local_result = localtime (timer);
+
+ if (local_result == NULL || result == NULL)
+ return NULL;
+
+ memcpy (result, local_result, sizeof (struct tm));
+ return result;
+}
+struct tm *
+p_gmtime_r (const time_t *timer, struct tm *result)
+{
+ struct tm *local_result;
+ local_result = gmtime (timer);
+
+ if (local_result == NULL || result == NULL)
+ return NULL;
+
+ memcpy (result, local_result, sizeof (struct tm));
+ return result;
+}
+
+int p_inet_pton(int af, const char *src, void *dst)
+{
+ struct sockaddr_storage sin;
+ void *addr;
+ int sin_len = sizeof(struct sockaddr_storage), addr_len;
+ int error = 0;
+
+ if (af == AF_INET) {
+ addr = &((struct sockaddr_in *)&sin)->sin_addr;
+ addr_len = sizeof(struct in_addr);
+ } else if (af == AF_INET6) {
+ addr = &((struct sockaddr_in6 *)&sin)->sin6_addr;
+ addr_len = sizeof(struct in6_addr);
+ } else {
+ errno = EAFNOSUPPORT;
+ return -1;
+ }
+
+ if ((error = WSAStringToAddressA((LPSTR)src, af, NULL, (LPSOCKADDR)&sin, &sin_len)) == 0) {
+ memcpy(dst, addr, addr_len);
+ return 1;
+ }
+
+ switch(WSAGetLastError()) {
+ case WSAEINVAL:
+ return 0;
+ case WSAEFAULT:
+ errno = ENOSPC;
+ return -1;
+ case WSA_NOT_ENOUGH_MEMORY:
+ errno = ENOMEM;
+ return -1;
+ }
+
+ errno = EINVAL;
+ return -1;
+}
+
+ssize_t p_pread(int fd, void *data, size_t size, off64_t offset)
+{
+ HANDLE fh;
+ DWORD rsize = 0;
+ OVERLAPPED ov = {0};
+ LARGE_INTEGER pos = {0};
+ off64_t final_offset = 0;
+
+ /* Fail if the final offset would have overflowed to match POSIX semantics. */
+ if (!git__is_ssizet(size) || git__add_int64_overflow(&final_offset, offset, (int64_t)size)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /*
+ * Truncate large writes to the maximum allowable size: the caller
+ * needs to always call this in a loop anyways.
+ */
+ if (size > INT32_MAX) {
+ size = INT32_MAX;
+ }
+
+ pos.QuadPart = offset;
+ ov.Offset = pos.LowPart;
+ ov.OffsetHigh = pos.HighPart;
+ fh = (HANDLE)_get_osfhandle(fd);
+
+ if (ReadFile(fh, data, (DWORD)size, &rsize, &ov)) {
+ return (ssize_t)rsize;
+ }
+
+ set_errno();
+ return -1;
+}
+
+ssize_t p_pwrite(int fd, const void *data, size_t size, off64_t offset)
+{
+ HANDLE fh;
+ DWORD wsize = 0;
+ OVERLAPPED ov = {0};
+ LARGE_INTEGER pos = {0};
+ off64_t final_offset = 0;
+
+ /* Fail if the final offset would have overflowed to match POSIX semantics. */
+ if (!git__is_ssizet(size) || git__add_int64_overflow(&final_offset, offset, (int64_t)size)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /*
+ * Truncate large writes to the maximum allowable size: the caller
+ * needs to always call this in a loop anyways.
+ */
+ if (size > INT32_MAX) {
+ size = INT32_MAX;
+ }
+
+ pos.QuadPart = offset;
+ ov.Offset = pos.LowPart;
+ ov.OffsetHigh = pos.HighPart;
+ fh = (HANDLE)_get_osfhandle(fd);
+
+ if (WriteFile(fh, data, (DWORD)size, &wsize, &ov)) {
+ return (ssize_t)wsize;
+ }
+
+ set_errno();
+ return -1;
+}
diff --git a/src/util/win32/precompiled.c b/src/util/win32/precompiled.c
new file mode 100644
index 0000000..5f656a4
--- /dev/null
+++ b/src/util/win32/precompiled.c
@@ -0,0 +1 @@
+#include "precompiled.h"
diff --git a/src/util/win32/precompiled.h b/src/util/win32/precompiled.h
new file mode 100644
index 0000000..1163c3d
--- /dev/null
+++ b/src/util/win32/precompiled.h
@@ -0,0 +1,21 @@
+#include "git2_util.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <time.h>
+#include <stdarg.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <io.h>
+#include <direct.h>
+#ifdef GIT_THREADS
+ #include "win32/thread.h"
+#endif
+
+#include "git2.h"
diff --git a/src/util/win32/reparse.h b/src/util/win32/reparse.h
new file mode 100644
index 0000000..2331231
--- /dev/null
+++ b/src/util/win32/reparse.h
@@ -0,0 +1,57 @@
+/*
+* Copyright (C) the libgit2 contributors. All rights reserved.
+*
+* This file is part of libgit2, distributed under the GNU GPL v2 with
+* a Linking Exception. For full terms see the included COPYING file.
+*/
+
+#ifndef INCLUDE_win32_reparse_h__
+#define INCLUDE_win32_reparse_h__
+
+/* This structure is defined on MSDN at
+* http://msdn.microsoft.com/en-us/library/windows/hardware/ff552012(v=vs.85).aspx
+*
+* It was formerly included in the Windows 2000 SDK and remains defined in
+* MinGW, so we must define it with a silly name to avoid conflicting.
+*/
+typedef struct _GIT_REPARSE_DATA_BUFFER {
+ ULONG ReparseTag;
+ USHORT ReparseDataLength;
+ USHORT Reserved;
+ union {
+ struct {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ ULONG Flags;
+ WCHAR PathBuffer[1];
+ } SymbolicLink;
+ struct {
+ USHORT SubstituteNameOffset;
+ USHORT SubstituteNameLength;
+ USHORT PrintNameOffset;
+ USHORT PrintNameLength;
+ WCHAR PathBuffer[1];
+ } MountPoint;
+ struct {
+ UCHAR DataBuffer[1];
+ } Generic;
+ } ReparseBuffer;
+} GIT_REPARSE_DATA_BUFFER;
+
+#define REPARSE_DATA_HEADER_SIZE 8
+#define REPARSE_DATA_MOUNTPOINT_HEADER_SIZE 8
+#define REPARSE_DATA_UNION_SIZE 12
+
+/* Missing in MinGW */
+#ifndef FSCTL_GET_REPARSE_POINT
+# define FSCTL_GET_REPARSE_POINT 0x000900a8
+#endif
+
+/* Missing in MinGW */
+#ifndef FSCTL_SET_REPARSE_POINT
+# define FSCTL_SET_REPARSE_POINT 0x000900a4
+#endif
+
+#endif
diff --git a/src/util/win32/thread.c b/src/util/win32/thread.c
new file mode 100644
index 0000000..f5cacd3
--- /dev/null
+++ b/src/util/win32/thread.c
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "thread.h"
+#include "runtime.h"
+
+#define CLEAN_THREAD_EXIT 0x6F012842
+
+typedef void (WINAPI *win32_srwlock_fn)(GIT_SRWLOCK *);
+
+static win32_srwlock_fn win32_srwlock_initialize;
+static win32_srwlock_fn win32_srwlock_acquire_shared;
+static win32_srwlock_fn win32_srwlock_release_shared;
+static win32_srwlock_fn win32_srwlock_acquire_exclusive;
+static win32_srwlock_fn win32_srwlock_release_exclusive;
+
+static DWORD fls_index;
+
+/* The thread procedure stub used to invoke the caller's procedure
+ * and capture the return value for later collection. Windows will
+ * only hold a DWORD, but we need to be able to store an entire
+ * void pointer. This requires the indirection. */
+static DWORD WINAPI git_win32__threadproc(LPVOID lpParameter)
+{
+ git_thread *thread = lpParameter;
+
+ /* Set the current thread for `git_thread_exit` */
+ FlsSetValue(fls_index, thread);
+
+ thread->result = thread->proc(thread->param);
+
+ return CLEAN_THREAD_EXIT;
+}
+
+static void git_threads_global_shutdown(void)
+{
+ FlsFree(fls_index);
+}
+
+int git_threads_global_init(void)
+{
+ HMODULE hModule = GetModuleHandleW(L"kernel32");
+
+ if (hModule) {
+ win32_srwlock_initialize = (win32_srwlock_fn)(void *)
+ GetProcAddress(hModule, "InitializeSRWLock");
+ win32_srwlock_acquire_shared = (win32_srwlock_fn)(void *)
+ GetProcAddress(hModule, "AcquireSRWLockShared");
+ win32_srwlock_release_shared = (win32_srwlock_fn)(void *)
+ GetProcAddress(hModule, "ReleaseSRWLockShared");
+ win32_srwlock_acquire_exclusive = (win32_srwlock_fn)(void *)
+ GetProcAddress(hModule, "AcquireSRWLockExclusive");
+ win32_srwlock_release_exclusive = (win32_srwlock_fn)(void *)
+ GetProcAddress(hModule, "ReleaseSRWLockExclusive");
+ }
+
+ if ((fls_index = FlsAlloc(NULL)) == FLS_OUT_OF_INDEXES)
+ return -1;
+
+ return git_runtime_shutdown_register(git_threads_global_shutdown);
+}
+
+int git_thread_create(
+ git_thread *GIT_RESTRICT thread,
+ void *(*start_routine)(void*),
+ void *GIT_RESTRICT arg)
+{
+ thread->result = NULL;
+ thread->param = arg;
+ thread->proc = start_routine;
+ thread->thread = CreateThread(
+ NULL, 0, git_win32__threadproc, thread, 0, NULL);
+
+ return thread->thread ? 0 : -1;
+}
+
+int git_thread_join(
+ git_thread *thread,
+ void **value_ptr)
+{
+ DWORD exit;
+
+ if (WaitForSingleObject(thread->thread, INFINITE) != WAIT_OBJECT_0)
+ return -1;
+
+ if (!GetExitCodeThread(thread->thread, &exit)) {
+ CloseHandle(thread->thread);
+ return -1;
+ }
+
+ /* Check for the thread having exited uncleanly. If exit was unclean,
+ * then we don't have a return value to give back to the caller. */
+ GIT_ASSERT(exit == CLEAN_THREAD_EXIT);
+
+ if (value_ptr)
+ *value_ptr = thread->result;
+
+ CloseHandle(thread->thread);
+ return 0;
+}
+
+void git_thread_exit(void *value)
+{
+ git_thread *thread = FlsGetValue(fls_index);
+
+ if (thread)
+ thread->result = value;
+
+ ExitThread(CLEAN_THREAD_EXIT);
+}
+
+size_t git_thread_currentid(void)
+{
+ return GetCurrentThreadId();
+}
+
+int git_mutex_init(git_mutex *GIT_RESTRICT mutex)
+{
+ InitializeCriticalSection(mutex);
+ return 0;
+}
+
+int git_mutex_free(git_mutex *mutex)
+{
+ DeleteCriticalSection(mutex);
+ return 0;
+}
+
+int git_mutex_lock(git_mutex *mutex)
+{
+ EnterCriticalSection(mutex);
+ return 0;
+}
+
+int git_mutex_unlock(git_mutex *mutex)
+{
+ LeaveCriticalSection(mutex);
+ return 0;
+}
+
+int git_cond_init(git_cond *cond)
+{
+ /* This is an auto-reset event. */
+ *cond = CreateEventW(NULL, FALSE, FALSE, NULL);
+ GIT_ASSERT(*cond);
+
+ /* If we can't create the event, claim that the reason was out-of-memory.
+ * The actual reason can be fetched with GetLastError(). */
+ return *cond ? 0 : ENOMEM;
+}
+
+int git_cond_free(git_cond *cond)
+{
+ BOOL closed;
+
+ if (!cond)
+ return EINVAL;
+
+ closed = CloseHandle(*cond);
+ GIT_ASSERT(closed);
+ GIT_UNUSED(closed);
+
+ *cond = NULL;
+ return 0;
+}
+
+int git_cond_wait(git_cond *cond, git_mutex *mutex)
+{
+ int error;
+ DWORD wait_result;
+
+ if (!cond || !mutex)
+ return EINVAL;
+
+ /* The caller must be holding the mutex. */
+ error = git_mutex_unlock(mutex);
+
+ if (error)
+ return error;
+
+ wait_result = WaitForSingleObject(*cond, INFINITE);
+ GIT_ASSERT(WAIT_OBJECT_0 == wait_result);
+ GIT_UNUSED(wait_result);
+
+ return git_mutex_lock(mutex);
+}
+
+int git_cond_signal(git_cond *cond)
+{
+ BOOL signaled;
+
+ if (!cond)
+ return EINVAL;
+
+ signaled = SetEvent(*cond);
+ GIT_ASSERT(signaled);
+ GIT_UNUSED(signaled);
+
+ return 0;
+}
+
+int git_rwlock_init(git_rwlock *GIT_RESTRICT lock)
+{
+ if (win32_srwlock_initialize)
+ win32_srwlock_initialize(&lock->native.srwl);
+ else
+ InitializeCriticalSection(&lock->native.csec);
+
+ return 0;
+}
+
+int git_rwlock_rdlock(git_rwlock *lock)
+{
+ if (win32_srwlock_acquire_shared)
+ win32_srwlock_acquire_shared(&lock->native.srwl);
+ else
+ EnterCriticalSection(&lock->native.csec);
+
+ return 0;
+}
+
+int git_rwlock_rdunlock(git_rwlock *lock)
+{
+ if (win32_srwlock_release_shared)
+ win32_srwlock_release_shared(&lock->native.srwl);
+ else
+ LeaveCriticalSection(&lock->native.csec);
+
+ return 0;
+}
+
+int git_rwlock_wrlock(git_rwlock *lock)
+{
+ if (win32_srwlock_acquire_exclusive)
+ win32_srwlock_acquire_exclusive(&lock->native.srwl);
+ else
+ EnterCriticalSection(&lock->native.csec);
+
+ return 0;
+}
+
+int git_rwlock_wrunlock(git_rwlock *lock)
+{
+ if (win32_srwlock_release_exclusive)
+ win32_srwlock_release_exclusive(&lock->native.srwl);
+ else
+ LeaveCriticalSection(&lock->native.csec);
+
+ return 0;
+}
+
+int git_rwlock_free(git_rwlock *lock)
+{
+ if (!win32_srwlock_initialize)
+ DeleteCriticalSection(&lock->native.csec);
+ git__memzero(lock, sizeof(*lock));
+ return 0;
+}
diff --git a/src/util/win32/thread.h b/src/util/win32/thread.h
new file mode 100644
index 0000000..184762e
--- /dev/null
+++ b/src/util/win32/thread.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_win32_thread_h__
+#define INCLUDE_win32_thread_h__
+
+#include "git2_util.h"
+
+#if defined (_MSC_VER)
+# define GIT_RESTRICT __restrict
+#else
+# define GIT_RESTRICT __restrict__
+#endif
+
+typedef struct {
+ HANDLE thread;
+ void *(*proc)(void *);
+ void *param;
+ void *result;
+} git_thread;
+
+typedef CRITICAL_SECTION git_mutex;
+typedef HANDLE git_cond;
+
+typedef struct { void *Ptr; } GIT_SRWLOCK;
+
+typedef struct {
+ union {
+ GIT_SRWLOCK srwl;
+ CRITICAL_SECTION csec;
+ } native;
+} git_rwlock;
+
+int git_threads_global_init(void);
+
+int git_thread_create(git_thread *GIT_RESTRICT,
+ void *(*) (void *),
+ void *GIT_RESTRICT);
+int git_thread_join(git_thread *, void **);
+size_t git_thread_currentid(void);
+void git_thread_exit(void *);
+
+int git_mutex_init(git_mutex *GIT_RESTRICT mutex);
+int git_mutex_free(git_mutex *);
+int git_mutex_lock(git_mutex *);
+int git_mutex_unlock(git_mutex *);
+
+int git_cond_init(git_cond *);
+int git_cond_free(git_cond *);
+int git_cond_wait(git_cond *, git_mutex *);
+int git_cond_signal(git_cond *);
+
+int git_rwlock_init(git_rwlock *GIT_RESTRICT lock);
+int git_rwlock_rdlock(git_rwlock *);
+int git_rwlock_rdunlock(git_rwlock *);
+int git_rwlock_wrlock(git_rwlock *);
+int git_rwlock_wrunlock(git_rwlock *);
+int git_rwlock_free(git_rwlock *);
+
+#endif
diff --git a/src/util/win32/utf-conv.c b/src/util/win32/utf-conv.c
new file mode 100644
index 0000000..ad35c0c
--- /dev/null
+++ b/src/util/win32/utf-conv.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "utf-conv.h"
+
+GIT_INLINE(void) git__set_errno(void)
+{
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ errno = ENAMETOOLONG;
+ else
+ errno = EINVAL;
+}
+
+int git_utf8_to_16(wchar_t *dest, size_t dest_size, const char *src)
+{
+ /* Length of -1 indicates NULL termination of the input string. */
+ return git_utf8_to_16_with_len(dest, dest_size, src, -1);
+}
+
+int git_utf8_to_16_with_len(
+ wchar_t *dest,
+ size_t _dest_size,
+ const char *src,
+ int src_len)
+{
+ int dest_size = (int)min(_dest_size, INT_MAX);
+ int len;
+
+ /*
+ * Subtract 1 from the result to turn 0 into -1 (an error code) and
+ * to not count the NULL terminator as part of the string's length.
+ * MultiByteToWideChar never returns int's minvalue, so underflow
+ * is not possible.
+ */
+ len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
+ src, src_len, dest, dest_size) - 1;
+
+ if (len < 0)
+ git__set_errno();
+
+ return len;
+}
+
+int git_utf8_from_16(char *dest, size_t dest_size, const wchar_t *src)
+{
+ /* Length of -1 indicates NULL termination of the input string. */
+ return git_utf8_from_16_with_len(dest, dest_size, src, -1);
+}
+
+int git_utf8_from_16_with_len(
+ char *dest,
+ size_t _dest_size,
+ const wchar_t *src,
+ int src_len)
+{
+ int dest_size = (int)min(_dest_size, INT_MAX);
+ int len;
+
+ /*
+ * Subtract 1 from the result to turn 0 into -1 (an error code) and
+ * to not count the NULL terminator as part of the string's length.
+ * WideCharToMultiByte never returns int's minvalue, so underflow
+ * is not possible.
+ */
+ len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
+ src, src_len, dest, dest_size, NULL, NULL) - 1;
+
+ if (len < 0)
+ git__set_errno();
+
+ return len;
+}
+
+int git_utf8_to_16_alloc(wchar_t **dest, const char *src)
+{
+ /* Length of -1 indicates NULL termination of the input string. */
+ return git_utf8_to_16_alloc_with_len(dest, src, -1);
+}
+
+int git_utf8_to_16_alloc_with_len(wchar_t **dest, const char *src, int src_len)
+{
+ int utf16_size;
+
+ *dest = NULL;
+
+ utf16_size = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS,
+ src, src_len, NULL, 0);
+
+ if (!utf16_size) {
+ git__set_errno();
+ return -1;
+ }
+
+ *dest = git__mallocarray(utf16_size, sizeof(wchar_t));
+ GIT_ERROR_CHECK_ALLOC(*dest);
+
+ utf16_size = git_utf8_to_16_with_len(*dest, (size_t)utf16_size,
+ src, src_len);
+
+ if (utf16_size < 0) {
+ git__free(*dest);
+ *dest = NULL;
+ }
+
+ return utf16_size;
+}
+
+int git_utf8_from_16_alloc(char **dest, const wchar_t *src)
+{
+ /* Length of -1 indicates NULL termination of the input string. */
+ return git_utf8_from_16_alloc_with_len(dest, src, -1);
+}
+
+int git_utf8_from_16_alloc_with_len(char **dest, const wchar_t *src, int src_len)
+{
+ int utf8_size;
+
+ *dest = NULL;
+
+ utf8_size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
+ src, src_len, NULL, 0, NULL, NULL);
+
+ if (!utf8_size) {
+ git__set_errno();
+ return -1;
+ }
+
+ *dest = git__malloc(utf8_size);
+ GIT_ERROR_CHECK_ALLOC(*dest);
+
+ utf8_size = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS,
+ src, src_len, *dest, utf8_size, NULL, NULL);
+
+ if (utf8_size < 0) {
+ git__free(*dest);
+ *dest = NULL;
+ }
+
+ return utf8_size;
+}
diff --git a/src/util/win32/utf-conv.h b/src/util/win32/utf-conv.h
new file mode 100644
index 0000000..301f5a6
--- /dev/null
+++ b/src/util/win32/utf-conv.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_win32_utf_conv_h__
+#define INCLUDE_win32_utf_conv_h__
+
+#include "git2_util.h"
+
+#include <wchar.h>
+
+#ifndef WC_ERR_INVALID_CHARS
+# define WC_ERR_INVALID_CHARS 0x80
+#endif
+
+/**
+ * Converts a NUL-terminated UTF-8 string to wide characters. This is a
+ * convenience function for `git_utf8_to_16_with_len`.
+ *
+ * @param dest The buffer to receive the wide string.
+ * @param dest_size The size of the buffer, in characters.
+ * @param src The UTF-8 string to convert.
+ * @return The length of the wide string, in characters
+ * (not counting the NULL terminator), or < 0 for failure
+ */
+int git_utf8_to_16(wchar_t *dest, size_t dest_size, const char *src);
+
+/**
+ * Converts a UTF-8 string to wide characters.
+ *
+ * @param dest The buffer to receive the wide string.
+ * @param dest_size The size of the buffer, in characters.
+ * @param src The UTF-8 string to convert.
+ * @param src_len The length of the string to convert.
+ * @return The length of the wide string, in characters
+ * (not counting the NULL terminator), or < 0 for failure
+ */
+int git_utf8_to_16_with_len(
+ wchar_t *dest,
+ size_t dest_size,
+ const char *src,
+ int src_len);
+
+/**
+ * Converts a NUL-terminated wide string to UTF-8. This is a convenience
+ * function for `git_utf8_from_16_with_len`.
+ *
+ * @param dest The buffer to receive the UTF-8 string.
+ * @param dest_size The size of the buffer, in bytes.
+ * @param src The wide string to convert.
+ * @param src_len The length of the string to convert.
+ * @return The length of the UTF-8 string, in bytes
+ * (not counting the NULL terminator), or < 0 for failure
+ */
+int git_utf8_from_16(char *dest, size_t dest_size, const wchar_t *src);
+
+/**
+ * Converts a wide string to UTF-8.
+ *
+ * @param dest The buffer to receive the UTF-8 string.
+ * @param dest_size The size of the buffer, in bytes.
+ * @param src The wide string to convert.
+ * @param src_len The length of the string to convert.
+ * @return The length of the UTF-8 string, in bytes
+ * (not counting the NULL terminator), or < 0 for failure
+ */
+int git_utf8_from_16_with_len(char *dest, size_t dest_size, const wchar_t *src, int src_len);
+
+/**
+ * Converts a UTF-8 string to wide characters. Memory is allocated to hold
+ * the converted string. The caller is responsible for freeing the string
+ * with git__free.
+ *
+ * @param dest Receives a pointer to the wide string.
+ * @param src The UTF-8 string to convert.
+ * @return The length of the wide string, in characters
+ * (not counting the NULL terminator), or < 0 for failure
+ */
+int git_utf8_to_16_alloc(wchar_t **dest, const char *src);
+
+/**
+ * Converts a UTF-8 string to wide characters. Memory is allocated to hold
+ * the converted string. The caller is responsible for freeing the string
+ * with git__free.
+ *
+ * @param dest Receives a pointer to the wide string.
+ * @param src The UTF-8 string to convert.
+ * @param src_len The length of the string.
+ * @return The length of the wide string, in characters
+ * (not counting the NULL terminator), or < 0 for failure
+ */
+int git_utf8_to_16_alloc_with_len(
+ wchar_t **dest,
+ const char *src,
+ int src_len);
+
+/**
+ * Converts a wide string to UTF-8. Memory is allocated to hold the
+ * converted string. The caller is responsible for freeing the string
+ * with git__free.
+ *
+ * @param dest Receives a pointer to the UTF-8 string.
+ * @param src The wide string to convert.
+ * @return The length of the UTF-8 string, in bytes
+ * (not counting the NULL terminator), or < 0 for failure
+ */
+int git_utf8_from_16_alloc(char **dest, const wchar_t *src);
+
+/**
+ * Converts a wide string to UTF-8. Memory is allocated to hold the
+ * converted string. The caller is responsible for freeing the string
+ * with git__free.
+ *
+ * @param dest Receives a pointer to the UTF-8 string.
+ * @param src The wide string to convert.
+ * @param src_len The length of the wide string.
+ * @return The length of the UTF-8 string, in bytes
+ * (not counting the NULL terminator), or < 0 for failure
+ */
+int git_utf8_from_16_alloc_with_len(
+ char **dest,
+ const wchar_t *src,
+ int src_len);
+
+#endif
diff --git a/src/util/win32/version.h b/src/util/win32/version.h
new file mode 100644
index 0000000..7966769
--- /dev/null
+++ b/src/util/win32/version.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_win32_version_h__
+#define INCLUDE_win32_version_h__
+
+#include <windows.h>
+
+GIT_INLINE(int) git_has_win32_version(int major, int minor, int service_pack)
+{
+ OSVERSIONINFOEX version_test = {0};
+ DWORD version_test_mask;
+ DWORDLONG version_condition_mask = 0;
+
+ version_test.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
+ version_test.dwMajorVersion = major;
+ version_test.dwMinorVersion = minor;
+ version_test.wServicePackMajor = (WORD)service_pack;
+ version_test.wServicePackMinor = 0;
+
+ version_test_mask = (VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR);
+
+ VER_SET_CONDITION(version_condition_mask, VER_MAJORVERSION, VER_GREATER_EQUAL);
+ VER_SET_CONDITION(version_condition_mask, VER_MINORVERSION, VER_GREATER_EQUAL);
+ VER_SET_CONDITION(version_condition_mask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
+ VER_SET_CONDITION(version_condition_mask, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL);
+
+ if (!VerifyVersionInfo(&version_test, version_test_mask, version_condition_mask))
+ return 0;
+
+ return 1;
+}
+
+#endif
diff --git a/src/util/win32/w32_buffer.c b/src/util/win32/w32_buffer.c
new file mode 100644
index 0000000..6fee820
--- /dev/null
+++ b/src/util/win32/w32_buffer.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "w32_buffer.h"
+
+#include "utf-conv.h"
+
+GIT_INLINE(int) handle_wc_error(void)
+{
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ errno = ENAMETOOLONG;
+ else
+ errno = EINVAL;
+
+ return -1;
+}
+
+int git_str_put_w(git_str *buf, const wchar_t *string_w, size_t len_w)
+{
+ int utf8_len, utf8_write_len;
+ size_t new_size;
+
+ if (!len_w) {
+ return 0;
+ } else if (len_w > INT_MAX) {
+ git_error_set_oom();
+ return -1;
+ }
+
+ GIT_ASSERT(string_w);
+
+ /* Measure the string necessary for conversion */
+ if ((utf8_len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, string_w, (int)len_w, NULL, 0, NULL, NULL)) == 0)
+ return 0;
+
+ GIT_ASSERT(utf8_len > 0);
+
+ GIT_ERROR_CHECK_ALLOC_ADD(&new_size, buf->size, (size_t)utf8_len);
+ GIT_ERROR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
+
+ if (git_str_grow(buf, new_size) < 0)
+ return -1;
+
+ if ((utf8_write_len = WideCharToMultiByte(
+ CP_UTF8, WC_ERR_INVALID_CHARS, string_w, (int)len_w, &buf->ptr[buf->size], utf8_len, NULL, NULL)) == 0)
+ return handle_wc_error();
+
+ GIT_ASSERT(utf8_write_len == utf8_len);
+
+ buf->size += utf8_write_len;
+ buf->ptr[buf->size] = '\0';
+ return 0;
+}
diff --git a/src/util/win32/w32_buffer.h b/src/util/win32/w32_buffer.h
new file mode 100644
index 0000000..68ea960
--- /dev/null
+++ b/src/util/win32/w32_buffer.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_win32_w32_buffer_h__
+#define INCLUDE_win32_w32_buffer_h__
+
+#include "git2_util.h"
+#include "str.h"
+
+/**
+ * Convert a wide character string to UTF-8 and append the results to the
+ * buffer.
+ */
+int git_str_put_w(git_str *buf, const wchar_t *string_w, size_t len_w);
+
+#endif
diff --git a/src/util/win32/w32_common.h b/src/util/win32/w32_common.h
new file mode 100644
index 0000000..c20b3e8
--- /dev/null
+++ b/src/util/win32/w32_common.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_win32_w32_common_h__
+#define INCLUDE_win32_w32_common_h__
+
+#include <git2/common.h>
+
+/*
+ * 4096 is the max allowed Git path. `MAX_PATH` (260) is the typical max allowed
+ * Windows path length, however win32 Unicode APIs generally allow up to 32,767
+ * if prefixed with "\\?\" (i.e. converted to an NT-style name).
+ */
+#define GIT_WIN_PATH_MAX GIT_PATH_MAX
+
+/*
+ * Provides a large enough buffer to support Windows Git paths:
+ * GIT_WIN_PATH_MAX is 4096, corresponding to a maximum path length of 4095
+ * characters plus a NULL terminator. Prefixing with "\\?\" adds 4 characters,
+ * but if the original was a UNC path, then we turn "\\server\share" into
+ * "\\?\UNC\server\share". So we replace the first two characters with
+ * 8 characters, a net gain of 6, so the maximum length is GIT_WIN_PATH_MAX+6.
+ */
+#define GIT_WIN_PATH_UTF16 GIT_WIN_PATH_MAX+6
+
+/* Maximum size of a UTF-8 Win32 Git path. We remove the "\\?\" or "\\?\UNC\"
+ * prefixes for presentation, bringing us back to 4095 (non-NULL)
+ * characters. UTF-8 does have 4-byte sequences, but they are encoded in
+ * UTF-16 using surrogate pairs, which takes up the space of two characters.
+ * Two characters in the range U+0800 -> U+FFFF take up more space in UTF-8
+ * (6 bytes) than one surrogate pair (4 bytes).
+ */
+#define GIT_WIN_PATH_UTF8 ((GIT_WIN_PATH_MAX - 1) * 3 + 1)
+
+/*
+ * The length of a Windows "shortname", for 8.3 compatibility.
+ */
+#define GIT_WIN_PATH_SHORTNAME 13
+
+/* Win32 path types */
+typedef wchar_t git_win32_path[GIT_WIN_PATH_UTF16];
+typedef char git_win32_utf8_path[GIT_WIN_PATH_UTF8];
+
+#endif
diff --git a/src/util/win32/w32_leakcheck.c b/src/util/win32/w32_leakcheck.c
new file mode 100644
index 0000000..0f095de
--- /dev/null
+++ b/src/util/win32/w32_leakcheck.c
@@ -0,0 +1,581 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "w32_leakcheck.h"
+
+#if defined(GIT_WIN32_LEAKCHECK)
+
+#include "Windows.h"
+#include "Dbghelp.h"
+#include "win32/posix.h"
+#include "hash.h"
+#include "runtime.h"
+
+/* Stack frames (for stack tracing, below) */
+
+static bool g_win32_stack_initialized = false;
+static HANDLE g_win32_stack_process = INVALID_HANDLE_VALUE;
+static git_win32_leakcheck_stack_aux_cb_alloc g_aux_cb_alloc = NULL;
+static git_win32_leakcheck_stack_aux_cb_lookup g_aux_cb_lookup = NULL;
+
+int git_win32_leakcheck_stack_set_aux_cb(
+ git_win32_leakcheck_stack_aux_cb_alloc cb_alloc,
+ git_win32_leakcheck_stack_aux_cb_lookup cb_lookup)
+{
+ g_aux_cb_alloc = cb_alloc;
+ g_aux_cb_lookup = cb_lookup;
+
+ return 0;
+}
+
+/**
+ * Load symbol table data. This should be done in the primary
+ * thread at startup (under a lock if there are other threads
+ * active).
+ */
+void git_win32_leakcheck_stack_init(void)
+{
+ if (!g_win32_stack_initialized) {
+ g_win32_stack_process = GetCurrentProcess();
+ SymSetOptions(SYMOPT_LOAD_LINES);
+ SymInitialize(g_win32_stack_process, NULL, TRUE);
+ g_win32_stack_initialized = true;
+ }
+}
+
+/**
+ * Cleanup symbol table data. This should be done in the
+ * primary thead at shutdown (under a lock if there are other
+ * threads active).
+ */
+void git_win32_leakcheck_stack_cleanup(void)
+{
+ if (g_win32_stack_initialized) {
+ SymCleanup(g_win32_stack_process);
+ g_win32_stack_process = INVALID_HANDLE_VALUE;
+ g_win32_stack_initialized = false;
+ }
+}
+
+int git_win32_leakcheck_stack_capture(git_win32_leakcheck_stack_raw_data *pdata, int skip)
+{
+ if (!g_win32_stack_initialized) {
+ git_error_set(GIT_ERROR_INVALID, "git_win32_stack not initialized.");
+ return GIT_ERROR;
+ }
+
+ memset(pdata, 0, sizeof(*pdata));
+ pdata->nr_frames = RtlCaptureStackBackTrace(
+ skip+1, GIT_WIN32_LEAKCHECK_STACK_MAX_FRAMES, pdata->frames, NULL);
+
+ /* If an "aux" data provider was registered, ask it to capture
+ * whatever data it needs and give us an "aux_id" to it so that
+ * we can refer to it later when reporting.
+ */
+ if (g_aux_cb_alloc)
+ (g_aux_cb_alloc)(&pdata->aux_id);
+
+ return 0;
+}
+
+int git_win32_leakcheck_stack_compare(
+ git_win32_leakcheck_stack_raw_data *d1,
+ git_win32_leakcheck_stack_raw_data *d2)
+{
+ return memcmp(d1, d2, sizeof(*d1));
+}
+
+int git_win32_leakcheck_stack_format(
+ char *pbuf, size_t buf_len,
+ const git_win32_leakcheck_stack_raw_data *pdata,
+ const char *prefix, const char *suffix)
+{
+#define MY_MAX_FILENAME 255
+
+ /* SYMBOL_INFO has char FileName[1] at the end. The docs say to
+ * to malloc it with extra space for your desired max filename.
+ */
+ struct {
+ SYMBOL_INFO symbol;
+ char extra[MY_MAX_FILENAME + 1];
+ } s;
+
+ IMAGEHLP_LINE64 line;
+ size_t buf_used = 0;
+ unsigned int k;
+ char detail[MY_MAX_FILENAME * 2]; /* filename plus space for function name and formatting */
+ size_t detail_len;
+
+ if (!g_win32_stack_initialized) {
+ git_error_set(GIT_ERROR_INVALID, "git_win32_stack not initialized.");
+ return GIT_ERROR;
+ }
+
+ if (!prefix)
+ prefix = "\t";
+ if (!suffix)
+ suffix = "\n";
+
+ memset(pbuf, 0, buf_len);
+
+ memset(&s, 0, sizeof(s));
+ s.symbol.MaxNameLen = MY_MAX_FILENAME;
+ s.symbol.SizeOfStruct = sizeof(SYMBOL_INFO);
+
+ memset(&line, 0, sizeof(line));
+ line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
+
+ for (k=0; k < pdata->nr_frames; k++) {
+ DWORD64 frame_k = (DWORD64)pdata->frames[k];
+ DWORD dwUnused;
+
+ if (SymFromAddr(g_win32_stack_process, frame_k, 0, &s.symbol) &&
+ SymGetLineFromAddr64(g_win32_stack_process, frame_k, &dwUnused, &line)) {
+ const char *pslash;
+ const char *pfile;
+
+ pslash = strrchr(line.FileName, '\\');
+ pfile = ((pslash) ? (pslash+1) : line.FileName);
+ p_snprintf(detail, sizeof(detail), "%s%s:%d> %s%s",
+ prefix, pfile, line.LineNumber, s.symbol.Name, suffix);
+ } else {
+ /* This happens when we cross into another module.
+ * For example, in CLAR tests, this is typically
+ * the CRT startup code. Just print an unknown
+ * frame and continue.
+ */
+ p_snprintf(detail, sizeof(detail), "%s??%s", prefix, suffix);
+ }
+ detail_len = strlen(detail);
+
+ if (buf_len < (buf_used + detail_len + 1)) {
+ /* we don't have room for this frame in the buffer, so just stop. */
+ break;
+ }
+
+ memcpy(&pbuf[buf_used], detail, detail_len);
+ buf_used += detail_len;
+ }
+
+ /* "aux_id" 0 is reserved to mean no aux data. This is needed to handle
+ * allocs that occur before the aux callbacks were registered.
+ */
+ if (pdata->aux_id > 0) {
+ p_snprintf(detail, sizeof(detail), "%saux_id: %d%s",
+ prefix, pdata->aux_id, suffix);
+ detail_len = strlen(detail);
+ if ((buf_used + detail_len + 1) < buf_len) {
+ memcpy(&pbuf[buf_used], detail, detail_len);
+ buf_used += detail_len;
+ }
+
+ /* If an "aux" data provider is still registered, ask it to append its detailed
+ * data to the end of ours using the "aux_id" it gave us when this de-duped
+ * item was created.
+ */
+ if (g_aux_cb_lookup)
+ (g_aux_cb_lookup)(pdata->aux_id, &pbuf[buf_used], (buf_len - buf_used - 1));
+ }
+
+ return GIT_OK;
+}
+
+int git_win32_leakcheck_stack(
+ char * pbuf, size_t buf_len,
+ int skip,
+ const char *prefix, const char *suffix)
+{
+ git_win32_leakcheck_stack_raw_data data;
+ int error;
+
+ if ((error = git_win32_leakcheck_stack_capture(&data, skip)) < 0)
+ return error;
+ if ((error = git_win32_leakcheck_stack_format(pbuf, buf_len, &data, prefix, suffix)) < 0)
+ return error;
+ return 0;
+}
+
+/* Stack tracing */
+
+#define STACKTRACE_UID_LEN (15)
+
+/**
+ * The stacktrace of an allocation can be distilled
+ * to a unique id based upon the stackframe pointers
+ * and ignoring any size arguments. We will use these
+ * UIDs as the (char const*) __FILE__ argument we
+ * give to the CRT malloc routines.
+ */
+typedef struct {
+ char uid[STACKTRACE_UID_LEN + 1];
+} git_win32_leakcheck_stacktrace_uid;
+
+/**
+ * All mallocs with the same stacktrace will be de-duped
+ * and aggregated into this row.
+ */
+typedef struct {
+ git_win32_leakcheck_stacktrace_uid uid; /* must be first */
+ git_win32_leakcheck_stack_raw_data raw_data;
+ unsigned int count_allocs; /* times this alloc signature seen since init */
+ unsigned int count_allocs_at_last_checkpoint; /* times since last mark */
+ unsigned int transient_count_leaks; /* sum of leaks */
+} git_win32_leakcheck_stacktrace_row;
+
+static CRITICAL_SECTION g_crtdbg_stacktrace_cs;
+
+/**
+ * CRTDBG memory leak tracking takes a "char const * const file_name"
+ * and stores the pointer in the heap data (instead of allocing a copy
+ * for itself). Normally, this is not a problem, since we usually pass
+ * in __FILE__. But I'm going to lie to it and pass in the address of
+ * the UID in place of the file_name. Also, I do not want to alloc the
+ * stacktrace data (because we are called from inside our alloc routines).
+ * Therefore, I'm creating a very large static pool array to store row
+ * data. This also eliminates the temptation to realloc it (and move the
+ * UID pointers).
+ *
+ * And to efficiently look for duplicates we need an index on the rows
+ * so we can bsearch it. Again, without mallocing.
+ *
+ * If we observe more than MY_ROW_LIMIT unique malloc signatures, we
+ * fall through and use the traditional __FILE__ processing and don't
+ * try to de-dup them. If your testing hits this limit, just increase
+ * it and try again.
+ */
+
+#define MY_ROW_LIMIT (2 * 1024 * 1024)
+static git_win32_leakcheck_stacktrace_row g_cs_rows[MY_ROW_LIMIT];
+static git_win32_leakcheck_stacktrace_row *g_cs_index[MY_ROW_LIMIT];
+
+static unsigned int g_cs_end = MY_ROW_LIMIT;
+static unsigned int g_cs_ins = 0; /* insertion point == unique allocs seen */
+static unsigned int g_count_total_allocs = 0; /* number of allocs seen */
+static unsigned int g_transient_count_total_leaks = 0; /* number of total leaks */
+static unsigned int g_transient_count_dedup_leaks = 0; /* number of unique leaks */
+static bool g_limit_reached = false; /* had allocs after we filled row table */
+
+static unsigned int g_checkpoint_id = 0; /* to better label leak checkpoints */
+static bool g_transient_leaks_since_mark = false; /* payload for hook */
+
+/**
+ * Compare function for bsearch on g_cs_index table.
+ */
+static int row_cmp(const void *v1, const void *v2)
+{
+ git_win32_leakcheck_stack_raw_data *d1 = (git_win32_leakcheck_stack_raw_data*)v1;
+ git_win32_leakcheck_stacktrace_row *r2 = (git_win32_leakcheck_stacktrace_row *)v2;
+
+ return (git_win32_leakcheck_stack_compare(d1, &r2->raw_data));
+}
+
+/**
+ * Unique insert the new data into the row and index tables.
+ * We have to sort by the stackframe data itself, not the uid.
+ */
+static git_win32_leakcheck_stacktrace_row * insert_unique(
+ const git_win32_leakcheck_stack_raw_data *pdata)
+{
+ size_t pos;
+ if (git__bsearch(g_cs_index, g_cs_ins, pdata, row_cmp, &pos) < 0) {
+ /* Append new unique item to row table. */
+ memcpy(&g_cs_rows[g_cs_ins].raw_data, pdata, sizeof(*pdata));
+ sprintf(g_cs_rows[g_cs_ins].uid.uid, "##%08lx", g_cs_ins);
+
+ /* Insert pointer to it into the proper place in the index table. */
+ if (pos < g_cs_ins)
+ memmove(&g_cs_index[pos+1], &g_cs_index[pos], (g_cs_ins - pos)*sizeof(g_cs_index[0]));
+ g_cs_index[pos] = &g_cs_rows[g_cs_ins];
+
+ g_cs_ins++;
+ }
+
+ g_cs_index[pos]->count_allocs++;
+
+ return g_cs_index[pos];
+}
+
+/**
+ * Hook function to receive leak data from the CRT. (This includes
+ * both "<file_name>:(<line_number>)" data, but also each of the
+ * various headers and fields.
+ *
+ * Scan this for the special "##<pos>" UID forms that we substituted
+ * for the "<file_name>". Map <pos> back to the row data and
+ * increment its leak count.
+ *
+ * See https://msdn.microsoft.com/en-us/library/74kabxyx.aspx
+ *
+ * We suppress the actual crtdbg output.
+ */
+static int __cdecl report_hook(int nRptType, char *szMsg, int *retVal)
+{
+ static int hook_result = TRUE; /* FALSE to get stock dump; TRUE to suppress. */
+ unsigned int pos;
+
+ *retVal = 0; /* do not invoke debugger */
+
+ if ((szMsg[0] != '#') || (szMsg[1] != '#'))
+ return hook_result;
+
+ if (sscanf(&szMsg[2], "%08lx", &pos) < 1)
+ return hook_result;
+ if (pos >= g_cs_ins)
+ return hook_result;
+
+ if (g_transient_leaks_since_mark) {
+ if (g_cs_rows[pos].count_allocs == g_cs_rows[pos].count_allocs_at_last_checkpoint)
+ return hook_result;
+ }
+
+ g_cs_rows[pos].transient_count_leaks++;
+
+ if (g_cs_rows[pos].transient_count_leaks == 1)
+ g_transient_count_dedup_leaks++;
+
+ g_transient_count_total_leaks++;
+
+ return hook_result;
+}
+
+/**
+ * Write leak data to all of the various places we need.
+ * We force the caller to sprintf() the message first
+ * because we want to avoid fprintf() because it allocs.
+ */
+static void my_output(const char *buf)
+{
+ fwrite(buf, strlen(buf), 1, stderr);
+ OutputDebugString(buf);
+}
+
+/**
+ * For each row with leaks, dump a stacktrace for it.
+ */
+static void dump_summary(const char *label)
+{
+ unsigned int k;
+ char buf[10 * 1024];
+
+ if (g_transient_count_total_leaks == 0)
+ return;
+
+ fflush(stdout);
+ fflush(stderr);
+ my_output("\n");
+
+ if (g_limit_reached) {
+ sprintf(buf,
+ "LEAK SUMMARY: de-dup row table[%d] filled. Increase MY_ROW_LIMIT.\n",
+ MY_ROW_LIMIT);
+ my_output(buf);
+ }
+
+ if (!label)
+ label = "";
+
+ if (g_transient_leaks_since_mark) {
+ sprintf(buf, "LEAK CHECKPOINT %d: leaks %d unique %d: %s\n",
+ g_checkpoint_id, g_transient_count_total_leaks, g_transient_count_dedup_leaks, label);
+ my_output(buf);
+ } else {
+ sprintf(buf, "LEAK SUMMARY: TOTAL leaks %d de-duped %d: %s\n",
+ g_transient_count_total_leaks, g_transient_count_dedup_leaks, label);
+ my_output(buf);
+ }
+ my_output("\n");
+
+ for (k = 0; k < g_cs_ins; k++) {
+ if (g_cs_rows[k].transient_count_leaks > 0) {
+ sprintf(buf, "LEAK: %s leaked %d of %d times:\n",
+ g_cs_rows[k].uid.uid,
+ g_cs_rows[k].transient_count_leaks,
+ g_cs_rows[k].count_allocs);
+ my_output(buf);
+
+ if (git_win32_leakcheck_stack_format(
+ buf, sizeof(buf), &g_cs_rows[k].raw_data,
+ NULL, NULL) >= 0) {
+ my_output(buf);
+ }
+
+ my_output("\n");
+ }
+ }
+
+ fflush(stderr);
+}
+
+/**
+ * Initialize our memory leak tracking and de-dup data structures.
+ * This should ONLY be called by git_libgit2_init().
+ */
+void git_win32_leakcheck_stacktrace_init(void)
+{
+ InitializeCriticalSection(&g_crtdbg_stacktrace_cs);
+
+ EnterCriticalSection(&g_crtdbg_stacktrace_cs);
+
+ _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
+
+ _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE);
+ _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE);
+ _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_FILE);
+
+ _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
+ _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
+ _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
+
+ LeaveCriticalSection(&g_crtdbg_stacktrace_cs);
+}
+
+int git_win32_leakcheck_stacktrace_dump(
+ git_win32_leakcheck_stacktrace_options opt,
+ const char *label)
+{
+ _CRT_REPORT_HOOK old;
+ unsigned int k;
+ int r = 0;
+
+#define IS_BIT_SET(o,b) (((o) & (b)) != 0)
+
+ bool b_set_mark = IS_BIT_SET(opt, GIT_WIN32_LEAKCHECK_STACKTRACE_SET_MARK);
+ bool b_leaks_since_mark = IS_BIT_SET(opt, GIT_WIN32_LEAKCHECK_STACKTRACE_LEAKS_SINCE_MARK);
+ bool b_leaks_total = IS_BIT_SET(opt, GIT_WIN32_LEAKCHECK_STACKTRACE_LEAKS_TOTAL);
+ bool b_quiet = IS_BIT_SET(opt, GIT_WIN32_LEAKCHECK_STACKTRACE_QUIET);
+
+ if (b_leaks_since_mark && b_leaks_total) {
+ git_error_set(GIT_ERROR_INVALID, "cannot combine LEAKS_SINCE_MARK and LEAKS_TOTAL.");
+ return GIT_ERROR;
+ }
+ if (!b_set_mark && !b_leaks_since_mark && !b_leaks_total) {
+ git_error_set(GIT_ERROR_INVALID, "nothing to do.");
+ return GIT_ERROR;
+ }
+
+ EnterCriticalSection(&g_crtdbg_stacktrace_cs);
+
+ if (b_leaks_since_mark || b_leaks_total) {
+ /* All variables with "transient" in the name are per-dump counters
+ * and reset before each dump. This lets us handle checkpoints.
+ */
+ g_transient_count_total_leaks = 0;
+ g_transient_count_dedup_leaks = 0;
+ for (k = 0; k < g_cs_ins; k++) {
+ g_cs_rows[k].transient_count_leaks = 0;
+ }
+ }
+
+ g_transient_leaks_since_mark = b_leaks_since_mark;
+
+ old = _CrtSetReportHook(report_hook);
+ _CrtDumpMemoryLeaks();
+ _CrtSetReportHook(old);
+
+ if (b_leaks_since_mark || b_leaks_total) {
+ r = g_transient_count_dedup_leaks;
+
+ if (!b_quiet)
+ dump_summary(label);
+ }
+
+ if (b_set_mark) {
+ for (k = 0; k < g_cs_ins; k++) {
+ g_cs_rows[k].count_allocs_at_last_checkpoint = g_cs_rows[k].count_allocs;
+ }
+
+ g_checkpoint_id++;
+ }
+
+ LeaveCriticalSection(&g_crtdbg_stacktrace_cs);
+
+ return r;
+}
+
+/**
+ * Shutdown our memory leak tracking and dump summary data.
+ * This should ONLY be called by git_libgit2_shutdown().
+ *
+ * We explicitly call _CrtDumpMemoryLeaks() during here so
+ * that we can compute summary data for the leaks. We print
+ * the stacktrace of each unique leak.
+ *
+ * This cleanup does not happen if the app calls exit()
+ * without calling the libgit2 shutdown code.
+ *
+ * This info we print here is independent of any automatic
+ * reporting during exit() caused by _CRTDBG_LEAK_CHECK_DF.
+ * Set it in your app if you also want traditional reporting.
+ */
+void git_win32_leakcheck_stacktrace_cleanup(void)
+{
+ /* At shutdown/cleanup, dump cumulative leak info
+ * with everything since startup. This might generate
+ * extra noise if the caller has been doing checkpoint
+ * dumps, but it might also eliminate some false
+ * positives for resources previously reported during
+ * checkpoints.
+ */
+ git_win32_leakcheck_stacktrace_dump(
+ GIT_WIN32_LEAKCHECK_STACKTRACE_LEAKS_TOTAL,
+ "CLEANUP");
+
+ DeleteCriticalSection(&g_crtdbg_stacktrace_cs);
+}
+
+const char *git_win32_leakcheck_stacktrace(int skip, const char *file)
+{
+ git_win32_leakcheck_stack_raw_data new_data;
+ git_win32_leakcheck_stacktrace_row *row;
+ const char * result = file;
+
+ if (git_win32_leakcheck_stack_capture(&new_data, skip+1) < 0)
+ return result;
+
+ EnterCriticalSection(&g_crtdbg_stacktrace_cs);
+
+ if (g_cs_ins < g_cs_end) {
+ row = insert_unique(&new_data);
+ result = row->uid.uid;
+ } else {
+ g_limit_reached = true;
+ }
+
+ g_count_total_allocs++;
+
+ LeaveCriticalSection(&g_crtdbg_stacktrace_cs);
+
+ return result;
+}
+
+static void git_win32_leakcheck_global_shutdown(void)
+{
+ git_win32_leakcheck_stacktrace_cleanup();
+ git_win32_leakcheck_stack_cleanup();
+}
+
+bool git_win32_leakcheck_has_leaks(void)
+{
+ return (g_transient_count_total_leaks > 0);
+}
+
+int git_win32_leakcheck_global_init(void)
+{
+ git_win32_leakcheck_stacktrace_init();
+ git_win32_leakcheck_stack_init();
+
+ return git_runtime_shutdown_register(git_win32_leakcheck_global_shutdown);
+}
+
+#else
+
+int git_win32_leakcheck_global_init(void)
+{
+ return 0;
+}
+
+#endif
diff --git a/src/util/win32/w32_leakcheck.h b/src/util/win32/w32_leakcheck.h
new file mode 100644
index 0000000..82d8638
--- /dev/null
+++ b/src/util/win32/w32_leakcheck.h
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_win32_leakcheck_h__
+#define INCLUDE_win32_leakcheck_h__
+
+#include "git2_util.h"
+
+/* Initialize the win32 leak checking system. */
+int git_win32_leakcheck_global_init(void);
+
+#if defined(GIT_WIN32_LEAKCHECK)
+
+#include <stdlib.h>
+#include <crtdbg.h>
+
+#include "git2/errors.h"
+#include "strnlen.h"
+
+bool git_win32_leakcheck_has_leaks(void);
+
+/* Stack frames (for stack tracing, below) */
+
+/**
+ * This type defines a callback to be used to augment a C stacktrace
+ * with "aux" data. This can be used, for example, to allow LibGit2Sharp
+ * (or other interpreted consumer libraries) to give us C# stacktrace
+ * data for the PInvoke.
+ *
+ * This callback will be called during crtdbg-instrumented allocs.
+ *
+ * @param aux_id [out] A returned "aux_id" representing a unique
+ * (de-duped at the C# layer) stacktrace. "aux_id" 0 is reserved
+ * to mean no aux stacktrace data.
+ */
+typedef void (*git_win32_leakcheck_stack_aux_cb_alloc)(unsigned int *aux_id);
+
+/**
+ * This type defines a callback to be used to augment the output of
+ * a stacktrace. This will be used to request the C# layer format
+ * the C# stacktrace associated with "aux_id" into the provided
+ * buffer.
+ *
+ * This callback will be called during leak reporting.
+ *
+ * @param aux_id The "aux_id" key associated with a stacktrace.
+ * @param aux_msg A buffer where a formatted message should be written.
+ * @param aux_msg_len The size of the buffer.
+ */
+typedef void (*git_win32_leakcheck_stack_aux_cb_lookup)(unsigned int aux_id, char *aux_msg, size_t aux_msg_len);
+
+/**
+ * Register an "aux" data provider to augment our C stacktrace data.
+ *
+ * This can be used, for example, to allow LibGit2Sharp (or other
+ * interpreted consumer libraries) to give us the C# stacktrace of
+ * the PInvoke.
+ *
+ * If you choose to use this feature, it should be registered during
+ * initialization and not changed for the duration of the process.
+ */
+int git_win32_leakcheck_stack_set_aux_cb(
+ git_win32_leakcheck_stack_aux_cb_alloc cb_alloc,
+ git_win32_leakcheck_stack_aux_cb_lookup cb_lookup);
+
+/**
+ * Maximum number of stackframes to record for a
+ * single stacktrace.
+ */
+#define GIT_WIN32_LEAKCHECK_STACK_MAX_FRAMES 30
+
+/**
+ * Wrapper containing the raw unprocessed stackframe
+ * data for a single stacktrace and any "aux_id".
+ *
+ * I put the aux_id first so leaks will be sorted by it.
+ * So, for example, if a specific callstack in C# leaks
+ * a repo handle, all of the pointers within the associated
+ * repo pointer will be grouped together.
+ */
+typedef struct {
+ unsigned int aux_id;
+ unsigned int nr_frames;
+ void *frames[GIT_WIN32_LEAKCHECK_STACK_MAX_FRAMES];
+} git_win32_leakcheck_stack_raw_data;
+
+/**
+ * Capture raw stack trace data for the current process/thread.
+ *
+ * @param skip Number of initial frames to skip. Pass 0 to
+ * begin with the caller of this routine. Pass 1 to begin
+ * with its caller. And so on.
+ */
+int git_win32_leakcheck_stack_capture(git_win32_leakcheck_stack_raw_data *pdata, int skip);
+
+/**
+ * Compare 2 raw stacktraces with the usual -1,0,+1 result.
+ * This includes any "aux_id" values in the comparison, so that
+ * our de-dup is also "aux" context relative.
+ */
+int git_win32_leakcheck_stack_compare(
+ git_win32_leakcheck_stack_raw_data *d1,
+ git_win32_leakcheck_stack_raw_data *d2);
+
+/**
+ * Format raw stacktrace data into buffer WITHOUT using any mallocs.
+ *
+ * @param prefix String written before each frame; defaults to "\t".
+ * @param suffix String written after each frame; defaults to "\n".
+ */
+int git_win32_leakcheck_stack_format(
+ char *pbuf, size_t buf_len,
+ const git_win32_leakcheck_stack_raw_data *pdata,
+ const char *prefix, const char *suffix);
+
+/**
+ * Convenience routine to capture and format stacktrace into
+ * a buffer WITHOUT using any mallocs. This is primarily a
+ * wrapper for testing.
+ *
+ * @param skip Number of initial frames to skip. Pass 0 to
+ * begin with the caller of this routine. Pass 1 to begin
+ * with its caller. And so on.
+ * @param prefix String written before each frame; defaults to "\t".
+ * @param suffix String written after each frame; defaults to "\n".
+ */
+int git_win32_leakcheck_stack(
+ char * pbuf, size_t buf_len,
+ int skip,
+ const char *prefix, const char *suffix);
+
+/* Stack tracing */
+
+/* MSVC CRTDBG memory leak reporting.
+ *
+ * We DO NOT use the "_CRTDBG_MAP_ALLOC" macro described in the MSVC
+ * documentation because all allocs/frees in libgit2 already go through
+ * the "git__" routines defined in this file. Simply using the normal
+ * reporting mechanism causes all leaks to be attributed to a routine
+ * here in util.h (ie, the actual call to calloc()) rather than the
+ * caller of git__calloc().
+ *
+ * Therefore, we declare a set of "git__crtdbg__" routines to replace
+ * the corresponding "git__" routines and re-define the "git__" symbols
+ * as macros. This allows us to get and report the file:line info of
+ * the real caller.
+ *
+ * We DO NOT replace the "git__free" routine because it needs to remain
+ * a function pointer because it is used as a function argument when
+ * setting up various structure "destructors".
+ *
+ * We also DO NOT use the "_CRTDBG_MAP_ALLOC" macro because it causes
+ * "free" to be remapped to "_free_dbg" and this causes problems for
+ * structures which define a field named "free".
+ *
+ * Finally, CRTDBG must be explicitly enabled and configured at program
+ * startup. See tests/main.c for an example.
+ */
+
+/**
+ * Checkpoint options.
+ */
+typedef enum git_win32_leakcheck_stacktrace_options {
+ /**
+ * Set checkpoint marker.
+ */
+ GIT_WIN32_LEAKCHECK_STACKTRACE_SET_MARK = (1 << 0),
+
+ /**
+ * Dump leaks since last checkpoint marker.
+ * May not be combined with _LEAKS_TOTAL.
+ *
+ * Note that this may generate false positives for global TLS
+ * error state and other global caches that aren't cleaned up
+ * until the thread/process terminates. So when using this
+ * around a region of interest, also check the final (at exit)
+ * dump before digging into leaks reported here.
+ */
+ GIT_WIN32_LEAKCHECK_STACKTRACE_LEAKS_SINCE_MARK = (1 << 1),
+
+ /**
+ * Dump leaks since init. May not be combined
+ * with _LEAKS_SINCE_MARK.
+ */
+ GIT_WIN32_LEAKCHECK_STACKTRACE_LEAKS_TOTAL = (1 << 2),
+
+ /**
+ * Suppress printing during dumps.
+ * Just return leak count.
+ */
+ GIT_WIN32_LEAKCHECK_STACKTRACE_QUIET = (1 << 3),
+
+} git_win32_leakcheck_stacktrace_options;
+
+/**
+ * Checkpoint memory state and/or dump unique stack traces of
+ * current memory leaks.
+ *
+ * @return number of unique leaks (relative to requested starting
+ * point) or error.
+ */
+int git_win32_leakcheck_stacktrace_dump(
+ git_win32_leakcheck_stacktrace_options opt,
+ const char *label);
+
+/**
+ * Construct stacktrace and append it to the global buffer.
+ * Return pointer to start of this string. On any error or
+ * lack of buffer space, just return the given file buffer
+ * so it will behave as usual.
+ *
+ * This should ONLY be called by our internal memory allocations
+ * routines.
+ */
+const char *git_win32_leakcheck_stacktrace(int skip, const char *file);
+
+#endif
+#endif
diff --git a/src/util/win32/w32_util.c b/src/util/win32/w32_util.c
new file mode 100644
index 0000000..f5b006a
--- /dev/null
+++ b/src/util/win32/w32_util.c
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "w32_util.h"
+
+/**
+ * Creates a FindFirstFile(Ex) filter string from a UTF-8 path.
+ * The filter string enumerates all items in the directory.
+ *
+ * @param dest The buffer to receive the filter string.
+ * @param src The UTF-8 path of the directory to enumerate.
+ * @return True if the filter string was created successfully; false otherwise
+ */
+bool git_win32__findfirstfile_filter(git_win32_path dest, const char *src)
+{
+ static const wchar_t suffix[] = L"\\*";
+ int len = git_win32_path_from_utf8(dest, src);
+
+ /* Ensure the path was converted */
+ if (len < 0)
+ return false;
+
+ /* Ensure that the path does not end with a trailing slash,
+ * because we're about to add one. Don't rely our trim_end
+ * helper, because we want to remove the backslash even for
+ * drive letter paths, in this case. */
+ if (len > 0 &&
+ (dest[len - 1] == L'/' || dest[len - 1] == L'\\')) {
+ dest[len - 1] = L'\0';
+ len--;
+ }
+
+ /* Ensure we have enough room to add the suffix */
+ if ((size_t)len >= GIT_WIN_PATH_UTF16 - CONST_STRLEN(suffix))
+ return false;
+
+ wcscat(dest, suffix);
+ return true;
+}
+
+/**
+ * Ensures the given path (file or folder) has the +H (hidden) attribute set.
+ *
+ * @param path The path which should receive the +H bit.
+ * @return 0 on success; -1 on failure
+ */
+int git_win32__set_hidden(const char *path, bool hidden)
+{
+ git_win32_path buf;
+ DWORD attrs, newattrs;
+
+ if (git_win32_path_from_utf8(buf, path) < 0)
+ return -1;
+
+ attrs = GetFileAttributesW(buf);
+
+ /* Ensure the path exists */
+ if (attrs == INVALID_FILE_ATTRIBUTES)
+ return -1;
+
+ if (hidden)
+ newattrs = attrs | FILE_ATTRIBUTE_HIDDEN;
+ else
+ newattrs = attrs & ~FILE_ATTRIBUTE_HIDDEN;
+
+ if (attrs != newattrs && !SetFileAttributesW(buf, newattrs)) {
+ git_error_set(GIT_ERROR_OS, "failed to %s hidden bit for '%s'",
+ hidden ? "set" : "unset", path);
+ return -1;
+ }
+
+ return 0;
+}
+
+int git_win32__hidden(bool *out, const char *path)
+{
+ git_win32_path buf;
+ DWORD attrs;
+
+ if (git_win32_path_from_utf8(buf, path) < 0)
+ return -1;
+
+ attrs = GetFileAttributesW(buf);
+
+ /* Ensure the path exists */
+ if (attrs == INVALID_FILE_ATTRIBUTES)
+ return -1;
+
+ *out = (attrs & FILE_ATTRIBUTE_HIDDEN) ? true : false;
+ return 0;
+}
+
+int git_win32__file_attribute_to_stat(
+ struct stat *st,
+ const WIN32_FILE_ATTRIBUTE_DATA *attrdata,
+ const wchar_t *path)
+{
+ git_win32__stat_init(st,
+ attrdata->dwFileAttributes,
+ attrdata->nFileSizeHigh,
+ attrdata->nFileSizeLow,
+ attrdata->ftCreationTime,
+ attrdata->ftLastAccessTime,
+ attrdata->ftLastWriteTime);
+
+ if (attrdata->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT && path) {
+ git_win32_path target;
+
+ if (git_win32_path_readlink_w(target, path) >= 0) {
+ st->st_mode = (st->st_mode & ~S_IFMT) | S_IFLNK;
+
+ /* st_size gets the UTF-8 length of the target name, in bytes,
+ * not counting the NULL terminator */
+ if ((st->st_size = git_utf8_from_16(NULL, 0, target)) < 0) {
+ git_error_set(GIT_ERROR_OS, "could not convert reparse point name for '%ls'", path);
+ return -1;
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/src/util/win32/w32_util.h b/src/util/win32/w32_util.h
new file mode 100644
index 0000000..5196637
--- /dev/null
+++ b/src/util/win32/w32_util.h
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#ifndef INCLUDE_win32_w32_util_h__
+#define INCLUDE_win32_w32_util_h__
+
+#include "git2_util.h"
+
+#include "utf-conv.h"
+#include "posix.h"
+#include "path_w32.h"
+
+/*
+
+#include "common.h"
+#include "path.h"
+#include "path_w32.h"
+#include "utf-conv.h"
+#include "posix.h"
+#include "reparse.h"
+#include "dir.h"
+*/
+
+
+GIT_INLINE(bool) git_win32__isalpha(wchar_t c)
+{
+ return ((c >= L'A' && c <= L'Z') || (c >= L'a' && c <= L'z'));
+}
+
+/**
+ * Creates a FindFirstFile(Ex) filter string from a UTF-8 path.
+ * The filter string enumerates all items in the directory.
+ *
+ * @param dest The buffer to receive the filter string.
+ * @param src The UTF-8 path of the directory to enumerate.
+ * @return True if the filter string was created successfully; false otherwise
+ */
+bool git_win32__findfirstfile_filter(git_win32_path dest, const char *src);
+
+/**
+ * Ensures the given path (file or folder) has the +H (hidden) attribute set
+ * or unset.
+ *
+ * @param path The path that should receive the +H bit.
+ * @param hidden true to set +H, false to unset it
+ * @return 0 on success; -1 on failure
+ */
+extern int git_win32__set_hidden(const char *path, bool hidden);
+
+/**
+ * Determines if the given file or folder has the hidden attribute set.
+ * @param hidden pointer to store hidden value
+ * @param path The path that should be queried for hiddenness.
+ * @return 0 on success or an error code.
+ */
+extern int git_win32__hidden(bool *hidden, const char *path);
+
+extern int git_win32__file_attribute_to_stat(
+ struct stat *st,
+ const WIN32_FILE_ATTRIBUTE_DATA *attrdata,
+ const wchar_t *path);
+
+/**
+ * Converts a FILETIME structure to a struct timespec.
+ *
+ * @param FILETIME A pointer to a FILETIME
+ * @param ts A pointer to the timespec structure to fill in
+ */
+GIT_INLINE(void) git_win32__filetime_to_timespec(
+ const FILETIME *ft,
+ struct timespec *ts)
+{
+ int64_t winTime = ((int64_t)ft->dwHighDateTime << 32) + ft->dwLowDateTime;
+ winTime -= INT64_C(116444736000000000); /* Windows to Unix Epoch conversion */
+ ts->tv_sec = (time_t)(winTime / 10000000);
+#ifdef GIT_USE_NSEC
+ ts->tv_nsec = (winTime % 10000000) * 100;
+#else
+ ts->tv_nsec = 0;
+#endif
+}
+
+GIT_INLINE(void) git_win32__timeval_to_filetime(
+ FILETIME *ft, const struct p_timeval tv)
+{
+ int64_t ticks = (tv.tv_sec * INT64_C(10000000)) +
+ (tv.tv_usec * INT64_C(10)) + INT64_C(116444736000000000);
+
+ ft->dwHighDateTime = ((ticks >> 32) & INT64_C(0xffffffff));
+ ft->dwLowDateTime = (ticks & INT64_C(0xffffffff));
+}
+
+GIT_INLINE(void) git_win32__stat_init(
+ struct stat *st,
+ DWORD dwFileAttributes,
+ DWORD nFileSizeHigh,
+ DWORD nFileSizeLow,
+ FILETIME ftCreationTime,
+ FILETIME ftLastAccessTime,
+ FILETIME ftLastWriteTime)
+{
+ mode_t mode = S_IREAD;
+
+ memset(st, 0, sizeof(struct stat));
+
+ if (dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+ mode |= S_IFDIR;
+ else
+ mode |= S_IFREG;
+
+ if ((dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0)
+ mode |= S_IWRITE;
+
+ st->st_ino = 0;
+ st->st_gid = 0;
+ st->st_uid = 0;
+ st->st_nlink = 1;
+ st->st_mode = mode;
+ st->st_size = ((int64_t)nFileSizeHigh << 32) + nFileSizeLow;
+ st->st_dev = _getdrive() - 1;
+ st->st_rdev = st->st_dev;
+ git_win32__filetime_to_timespec(&ftLastAccessTime, &(st->st_atim));
+ git_win32__filetime_to_timespec(&ftLastWriteTime, &(st->st_mtim));
+ git_win32__filetime_to_timespec(&ftCreationTime, &(st->st_ctim));
+}
+
+GIT_INLINE(void) git_win32__file_information_to_stat(
+ struct stat *st,
+ const BY_HANDLE_FILE_INFORMATION *fileinfo)
+{
+ git_win32__stat_init(st,
+ fileinfo->dwFileAttributes,
+ fileinfo->nFileSizeHigh,
+ fileinfo->nFileSizeLow,
+ fileinfo->ftCreationTime,
+ fileinfo->ftLastAccessTime,
+ fileinfo->ftLastWriteTime);
+}
+
+#endif
diff --git a/src/util/win32/win32-compat.h b/src/util/win32/win32-compat.h
new file mode 100644
index 0000000..dee40a4
--- /dev/null
+++ b/src/util/win32/win32-compat.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_win32_win32_compat_h__
+#define INCLUDE_win32_win32_compat_h__
+
+#include <stdint.h>
+#include <time.h>
+#include <wchar.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+typedef long suseconds_t;
+
+struct p_timeval {
+ time_t tv_sec;
+ suseconds_t tv_usec;
+};
+
+struct p_timespec {
+ time_t tv_sec;
+ long tv_nsec;
+};
+
+#define timespec p_timespec
+
+struct p_stat {
+ _dev_t st_dev;
+ _ino_t st_ino;
+ mode_t st_mode;
+ short st_nlink;
+ short st_uid;
+ short st_gid;
+ _dev_t st_rdev;
+ __int64 st_size;
+ struct timespec st_atim;
+ struct timespec st_mtim;
+ struct timespec st_ctim;
+#define st_atime st_atim.tv_sec
+#define st_mtime st_mtim.tv_sec
+#define st_ctime st_ctim.tv_sec
+#define st_atime_nsec st_atim.tv_nsec
+#define st_mtime_nsec st_mtim.tv_nsec
+#define st_ctime_nsec st_ctim.tv_nsec
+};
+
+#define stat p_stat
+
+#endif
diff --git a/src/util/zstream.c b/src/util/zstream.c
new file mode 100644
index 0000000..cb8b125
--- /dev/null
+++ b/src/util/zstream.c
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "zstream.h"
+
+#include <zlib.h>
+
+#include "str.h"
+
+#define ZSTREAM_BUFFER_SIZE (1024 * 1024)
+#define ZSTREAM_BUFFER_MIN_EXTRA 8
+
+GIT_INLINE(int) zstream_seterr(git_zstream *zs)
+{
+ switch (zs->zerr) {
+ case Z_OK:
+ case Z_STREAM_END:
+ case Z_BUF_ERROR: /* not fatal; we retry with a larger buffer */
+ return 0;
+ case Z_MEM_ERROR:
+ git_error_set_oom();
+ break;
+ default:
+ if (zs->z.msg)
+ git_error_set_str(GIT_ERROR_ZLIB, zs->z.msg);
+ else
+ git_error_set(GIT_ERROR_ZLIB, "unknown compression error");
+ }
+
+ return -1;
+}
+
+int git_zstream_init(git_zstream *zstream, git_zstream_t type)
+{
+ zstream->type = type;
+
+ if (zstream->type == GIT_ZSTREAM_INFLATE)
+ zstream->zerr = inflateInit(&zstream->z);
+ else
+ zstream->zerr = deflateInit(&zstream->z, Z_DEFAULT_COMPRESSION);
+ return zstream_seterr(zstream);
+}
+
+void git_zstream_free(git_zstream *zstream)
+{
+ if (zstream->type == GIT_ZSTREAM_INFLATE)
+ inflateEnd(&zstream->z);
+ else
+ deflateEnd(&zstream->z);
+}
+
+void git_zstream_reset(git_zstream *zstream)
+{
+ if (zstream->type == GIT_ZSTREAM_INFLATE)
+ inflateReset(&zstream->z);
+ else
+ deflateReset(&zstream->z);
+ zstream->in = NULL;
+ zstream->in_len = 0;
+ zstream->zerr = Z_STREAM_END;
+}
+
+int git_zstream_set_input(git_zstream *zstream, const void *in, size_t in_len)
+{
+ zstream->in = in;
+ zstream->in_len = in_len;
+ zstream->zerr = Z_OK;
+ return 0;
+}
+
+bool git_zstream_done(git_zstream *zstream)
+{
+ return (!zstream->in_len && zstream->zerr == Z_STREAM_END);
+}
+
+bool git_zstream_eos(git_zstream *zstream)
+{
+ return zstream->zerr == Z_STREAM_END;
+}
+
+size_t git_zstream_suggest_output_len(git_zstream *zstream)
+{
+ if (zstream->in_len > ZSTREAM_BUFFER_SIZE)
+ return ZSTREAM_BUFFER_SIZE;
+ else if (zstream->in_len > ZSTREAM_BUFFER_MIN_EXTRA)
+ return zstream->in_len;
+ else
+ return ZSTREAM_BUFFER_MIN_EXTRA;
+}
+
+int git_zstream_get_output_chunk(
+ void *out, size_t *out_len, git_zstream *zstream)
+{
+ size_t in_queued, in_used, out_queued;
+
+ /* set up input data */
+ zstream->z.next_in = (Bytef *)zstream->in;
+
+ /* feed as much data to zlib as it can consume, at most UINT_MAX */
+ if (zstream->in_len > UINT_MAX) {
+ zstream->z.avail_in = UINT_MAX;
+ zstream->flush = Z_NO_FLUSH;
+ } else {
+ zstream->z.avail_in = (uInt)zstream->in_len;
+ zstream->flush = Z_FINISH;
+ }
+ in_queued = (size_t)zstream->z.avail_in;
+
+ /* set up output data */
+ zstream->z.next_out = out;
+ zstream->z.avail_out = (uInt)*out_len;
+
+ if ((size_t)zstream->z.avail_out != *out_len)
+ zstream->z.avail_out = UINT_MAX;
+ out_queued = (size_t)zstream->z.avail_out;
+
+ /* compress next chunk */
+ if (zstream->type == GIT_ZSTREAM_INFLATE)
+ zstream->zerr = inflate(&zstream->z, zstream->flush);
+ else
+ zstream->zerr = deflate(&zstream->z, zstream->flush);
+
+ if (zstream_seterr(zstream))
+ return -1;
+
+ in_used = (in_queued - zstream->z.avail_in);
+ zstream->in_len -= in_used;
+ zstream->in += in_used;
+
+ *out_len = (out_queued - zstream->z.avail_out);
+
+ return 0;
+}
+
+int git_zstream_get_output(void *out, size_t *out_len, git_zstream *zstream)
+{
+ size_t out_remain = *out_len;
+
+ if (zstream->in_len && zstream->zerr == Z_STREAM_END) {
+ git_error_set(GIT_ERROR_ZLIB, "zlib input had trailing garbage");
+ return -1;
+ }
+
+ while (out_remain > 0 && zstream->zerr != Z_STREAM_END) {
+ size_t out_written = out_remain;
+
+ if (git_zstream_get_output_chunk(out, &out_written, zstream) < 0)
+ return -1;
+
+ out_remain -= out_written;
+ out = ((char *)out) + out_written;
+ }
+
+ /* either we finished the input or we did not flush the data */
+ GIT_ASSERT(zstream->in_len > 0 || zstream->flush == Z_FINISH);
+
+ /* set out_size to number of bytes actually written to output */
+ *out_len = *out_len - out_remain;
+
+ return 0;
+}
+
+static int zstream_buf(git_str *out, const void *in, size_t in_len, git_zstream_t type)
+{
+ git_zstream zs = GIT_ZSTREAM_INIT;
+ int error = 0;
+
+ if ((error = git_zstream_init(&zs, type)) < 0)
+ return error;
+
+ if ((error = git_zstream_set_input(&zs, in, in_len)) < 0)
+ goto done;
+
+ while (!git_zstream_done(&zs)) {
+ size_t step = git_zstream_suggest_output_len(&zs), written;
+
+ if ((error = git_str_grow_by(out, step)) < 0)
+ goto done;
+
+ written = out->asize - out->size;
+
+ if ((error = git_zstream_get_output(
+ out->ptr + out->size, &written, &zs)) < 0)
+ goto done;
+
+ out->size += written;
+ }
+
+ /* NULL terminate for consistency if possible */
+ if (out->size < out->asize)
+ out->ptr[out->size] = '\0';
+
+done:
+ git_zstream_free(&zs);
+ return error;
+}
+
+int git_zstream_deflatebuf(git_str *out, const void *in, size_t in_len)
+{
+ return zstream_buf(out, in, in_len, GIT_ZSTREAM_DEFLATE);
+}
+
+int git_zstream_inflatebuf(git_str *out, const void *in, size_t in_len)
+{
+ return zstream_buf(out, in, in_len, GIT_ZSTREAM_INFLATE);
+}
diff --git a/src/util/zstream.h b/src/util/zstream.h
new file mode 100644
index 0000000..d78b112
--- /dev/null
+++ b/src/util/zstream.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#ifndef INCLUDE_zstream_h__
+#define INCLUDE_zstream_h__
+
+#include "git2_util.h"
+
+#include <zlib.h>
+
+#include "str.h"
+
+typedef enum {
+ GIT_ZSTREAM_INFLATE,
+ GIT_ZSTREAM_DEFLATE
+} git_zstream_t;
+
+typedef struct {
+ z_stream z;
+ git_zstream_t type;
+ const char *in;
+ size_t in_len;
+ int flush;
+ int zerr;
+} git_zstream;
+
+#define GIT_ZSTREAM_INIT {{0}}
+
+int git_zstream_init(git_zstream *zstream, git_zstream_t type);
+void git_zstream_free(git_zstream *zstream);
+
+int git_zstream_set_input(git_zstream *zstream, const void *in, size_t in_len);
+
+size_t git_zstream_suggest_output_len(git_zstream *zstream);
+
+/* get as much output as is available in the input buffer */
+int git_zstream_get_output_chunk(
+ void *out, size_t *out_len, git_zstream *zstream);
+
+/* get all the output from the entire input buffer */
+int git_zstream_get_output(void *out, size_t *out_len, git_zstream *zstream);
+
+bool git_zstream_done(git_zstream *zstream);
+bool git_zstream_eos(git_zstream *zstream);
+
+void git_zstream_reset(git_zstream *zstream);
+
+int git_zstream_deflatebuf(git_str *out, const void *in, size_t in_len);
+int git_zstream_inflatebuf(git_str *out, const void *in, size_t in_len);
+
+#endif
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644
index 0000000..df100e9
--- /dev/null
+++ b/tests/CMakeLists.txt
@@ -0,0 +1,6 @@
+# The main libgit2 tests tree: this CMakeLists.txt includes the
+# subprojects that make up core libgit2 support.
+
+add_subdirectory(headertest)
+add_subdirectory(libgit2)
+add_subdirectory(util)
diff --git a/tests/README.md b/tests/README.md
new file mode 100644
index 0000000..460e045
--- /dev/null
+++ b/tests/README.md
@@ -0,0 +1,83 @@
+# libgit2 tests
+
+These are the unit and integration tests for the libgit2 projects.
+
+* `benchmarks`
+ These are benchmark tests that excercise the CLI.
+* `clar`
+ This is [clar](https://github.com/clar-test/clar) the common test framework.
+* `headertest`
+ This is a simple project that ensures that our public headers are
+ compatible with extremely strict compilation options.
+* `libgit2`
+ These tests exercise the core git functionality in libgit2 itself.
+* `resources`
+ These are the resources for the tests, including files and git
+ repositories.
+* `util`
+ These are tests of the common utility library.
+
+## Writing tests for libgit2
+
+libgit2 uses the [clar test framework](http://github.com/clar-test/clar), a
+C testing framework.
+
+The best resources for learning clar are [clar itself](https://github.com/clar-test/clar)
+and the existing tests within libgit2. In general:
+
+* If you place a `.c` file into a test directory, it is eligible to contain
+test cases.
+* The function name for your test is important; test function names begin
+ with `test_`, followed by the folder path (underscore separated), two
+ underscores as a delimiter, then the test name. For example, a file
+ `merge/analysis.c` may contain a test `uptodate`:
+
+ ```
+ void test_merge_analysis__uptodate(void)
+ {
+ ...
+ }
+ ```
+
+* You can run an individual test by passing `-s` to the test runner. Tests
+ are referred to by their function names; for example, the function
+ `test_merge_analysis__uptodate` is referred to as `merge::analysis::uptodate`.
+ To run only that function you can use the `-s` option on the test runner:
+
+ ```
+ libgit2_tests -smerge::analysis::uptodate
+ ```
+
+## Memory leak checking
+
+These are automatically run as part of CI, but if you want to check locally:
+
+### Linux
+
+Uses [`valgrind`](http://www.valgrind.org/):
+
+```console
+$ cmake -DBUILD_TESTS=ON -DVALGRIND=ON ..
+$ cmake --build .
+$ valgrind --leak-check=full --show-reachable=yes --num-callers=50 --suppressions=../libgit2_tests.supp \
+ ./libgit2_tests
+```
+
+### macOS
+
+Uses [`leaks`](https://developer.apple.com/library/archive/documentation/Performance/Conceptual/ManagingMemory/Articles/FindingLeaks.html), which requires XCode installed:
+
+```console
+$ MallocStackLogging=1 MallocScribble=1 MallocLogFile=/dev/null CLAR_AT_EXIT="leaks -quiet \$PPID" \
+ ./libgit2_tests
+```
+
+### Windows
+
+Build with the `WIN32_LEAKCHECK` option:
+
+```console
+$ cmake -DBUILD_TESTS=ON -DWIN32_LEAKCHECK=ON ..
+$ cmake --build .
+$ ./libgit2_tests
+```
diff --git a/tests/benchmarks/README.md b/tests/benchmarks/README.md
new file mode 100644
index 0000000..f66b27a
--- /dev/null
+++ b/tests/benchmarks/README.md
@@ -0,0 +1,121 @@
+# libgit2 benchmarks
+
+This folder contains the individual benchmark tests for libgit2,
+meant for understanding the performance characteristics of libgit2,
+comparing your development code to the existing libgit2 code, or
+comparing libgit2 to the git reference implementation.
+
+## Running benchmark tests
+
+Benchmark tests can be run in several different ways: running all
+benchmarks, running one (or more) suite of benchmarks, or running a
+single individual benchmark. You can target either an individual
+version of a CLI, or you can A/B test a baseline CLI against a test
+CLI.
+
+### Specifying the command-line interface to test
+
+By default, the `git` in your path is benchmarked. Use the
+`-c` (or `--cli`) option to specify the command-line interface
+to test.
+
+Example: `libgit2_bench --cli git2_cli` will run the tests against
+`git2_cli`.
+
+### Running tests to compare two different implementations
+
+You can compare a baseline command-line interface against a test
+command-line interface using the `-b (or `--baseline-cli`) option.
+
+Example: `libgit2_bench --baseline-cli git --cli git2_cli` will
+run the tests against both `git` and `git2_cli`.
+
+### Running individual benchmark tests
+
+Similar to how a test suite or individual test is specified in
+[clar](https://github.com/clar-test/clar), the `-s` (or `--suite`)
+option may be used to specify the suite or individual test to run.
+Like clar, the suite and test name are separated by `::`, and like
+clar, this is a prefix match.
+
+Examples:
+* `libgit2_bench -shash_object` will run the tests in the
+ `hash_object` suite.
+* `libgit2_bench -shash_object::random_1kb` will run the
+ `hash_object::random_1kb` test.
+* `libgit2_bench -shash_object::random` will run all the tests that
+ begin with `hash_object::random`.
+
+## Writing benchmark tests
+
+Benchmark tests are meant to be easy to write. Each individual
+benchmark is a shell script that allows it to do set up (eg, creating
+or cloning a repository, creating temporary files, etc), then running
+benchmarks, then teardown.
+
+The `benchmark_helpers.sh` script provides many helpful utility
+functions to allow for cross-platform benchmarking, as well as a
+wrapper for `hyperfine` that is suited to testing libgit2.
+Note that the helper script must be included first, at the beginning
+of the benchmark test.
+
+### Benchmark example
+
+This simplistic example compares the speed of running the `git help`
+command in the baseline CLI to the test CLI.
+
+```bash
+#!/bin/bash -e
+
+# include the benchmark library
+. "$(dirname "$0")/benchmark_helpers.sh"
+
+# run the "help" command; this will benchmark `git2_cli help`
+gitbench help
+```
+
+### Naming
+
+The filename of the benchmark itself is important. A benchmark's
+filename should be the name of the benchmark suite, followed by two
+underscores, followed by the name of the benchmark. For example,
+`hash-object__random_1kb` is the `random_1kb` test in the `hash-object`
+suite.
+
+### Options
+
+The `gitbench` function accepts several options.
+
+* `--sandbox <path>`
+ The name of a test resource (in the `tests/resources` directory).
+ This will be copied as-is to the sandbox location before test
+ execution. This is copied _before_ the `prepare` script is run.
+ This option may be specified multiple times.
+* `--repository <path>`
+ The name of a test resource repository (in the `tests/resources`
+ directory). This repository will be copied into a sandbox location
+ before test execution, and your test will run in this directory.
+ This is copied _before_ the `prepare` script is run.
+* `--prepare <script>`
+ A script to run before each invocation of the test is run. This can
+ set up data for the test that will _not_ be timed. This script is run
+ in bash on all platforms.
+
+ Several helper functions are available within the context of a prepare
+ script:
+
+ * `flush_disk_cache`
+ Calling this will flush the disk cache before each test run.
+ This should probably be run at the end of the `prepare` script.
+ * `create_random_file <path> [<size>]`
+ Calling this will populate a file at the given `path` with `size`
+ bytes of random data.
+ * `create_text_file <path> [<size>]`
+ Calling this will populate a file at the given `path` with `size`
+ bytes of predictable text, with the platform line endings. This
+ is preferred over random data as it's reproducible.
+
+* `--warmup <n>`
+ Specifies that the test should run `n` times before actually measuring
+ the timing; useful for "warming up" a cache.
+
diff --git a/tests/benchmarks/benchmark.sh b/tests/benchmarks/benchmark.sh
new file mode 100755
index 0000000..4a89807
--- /dev/null
+++ b/tests/benchmarks/benchmark.sh
@@ -0,0 +1,298 @@
+#!/bin/bash
+
+set -eo pipefail
+
+#
+# parse the command line
+#
+
+usage() { echo "usage: $(basename "$0") [--cli <path>] [--baseline-cli <path>] [--suite <suite>] [--json <path>] [--zip <path>] [--verbose] [--debug]"; }
+
+TEST_CLI="git"
+BASELINE_CLI=
+SUITE=
+JSON_RESULT=
+ZIP_RESULT=
+OUTPUT_DIR=
+VERBOSE=
+DEBUG=
+NEXT=
+
+for a in "$@"; do
+ if [ "${NEXT}" = "cli" ]; then
+ TEST_CLI="${a}"
+ NEXT=
+ elif [ "${NEXT}" = "baseline-cli" ]; then
+ BASELINE_CLI="${a}"
+ NEXT=
+ elif [ "${NEXT}" = "suite" ]; then
+ SUITE="${a}"
+ NEXT=
+ elif [ "${NEXT}" = "json" ]; then
+ JSON_RESULT="${a}"
+ NEXT=
+ elif [ "${NEXT}" = "zip" ]; then
+ ZIP_RESULT="${a}"
+ NEXT=
+ elif [ "${NEXT}" = "output-dir" ]; then
+ OUTPUT_DIR="${a}"
+ NEXT=
+ elif [ "${a}" = "c" ] || [ "${a}" = "--cli" ]; then
+ NEXT="cli"
+ elif [[ "${a}" == "-c"* ]]; then
+ TEST_CLI="${a/-c/}"
+ elif [ "${a}" = "b" ] || [ "${a}" = "--baseline-cli" ]; then
+ NEXT="baseline-cli"
+ elif [[ "${a}" == "-b"* ]]; then
+ BASELINE_CLI="${a/-b/}"
+ elif [ "${a}" = "-s" ] || [ "${a}" = "--suite" ]; then
+ NEXT="suite"
+ elif [[ "${a}" == "-s"* ]]; then
+ SUITE="${a/-s/}"
+ elif [ "${a}" = "-v" ] || [ "${a}" == "--verbose" ]; then
+ VERBOSE=1
+ elif [ "${a}" == "--debug" ]; then
+ VERBOSE=1
+ DEBUG=1
+ elif [ "${a}" = "-j" ] || [ "${a}" == "--json" ]; then
+ NEXT="json"
+ elif [[ "${a}" == "-j"* ]]; then
+ JSON_RESULT="${a/-j/}"
+ elif [ "${a}" = "-z" ] || [ "${a}" == "--zip" ]; then
+ NEXT="zip"
+ elif [[ "${a}" == "-z"* ]]; then
+ ZIP_RESULT="${a/-z/}"
+ elif [ "${a}" = "--output-dir" ]; then
+ NEXT="output-dir"
+ else
+ echo "$(basename "$0"): unknown option: ${a}" 1>&2
+ usage 1>&2
+ exit 1
+ fi
+done
+
+if [ "${NEXT}" != "" ]; then
+ usage 1>&2
+ exit 1
+fi
+
+if [ "${OUTPUT_DIR}" = "" ]; then
+ OUTPUT_DIR=${OUTPUT_DIR:="$(mktemp -d)"}
+ CLEANUP_DIR=1
+fi
+
+#
+# collect some information about the test environment
+#
+
+SYSTEM_OS=$(uname -s)
+if [ "${SYSTEM_OS}" = "Darwin" ]; then SYSTEM_OS="macOS"; fi
+
+SYSTEM_KERNEL=$(uname -v)
+
+fullpath() {
+ if [[ "$(uname -s)" == "MINGW"* && $(cygpath -u "${TEST_CLI}") == "/"* ]]; then
+ echo "${TEST_CLI}"
+ elif [[ "${TEST_CLI}" == "/"* ]]; then
+ echo "${TEST_CLI}"
+ else
+ which "${TEST_CLI}"
+ fi
+}
+
+cli_version() {
+ if [[ "$(uname -s)" == "MINGW"* ]]; then
+ $(cygpath -u "$1") --version
+ else
+ "$1" --version
+ fi
+}
+
+TEST_CLI_NAME=$(basename "${TEST_CLI}")
+TEST_CLI_PATH=$(fullpath "${TEST_CLI}")
+TEST_CLI_VERSION=$(cli_version "${TEST_CLI}")
+
+if [ "${BASELINE_CLI}" != "" ]; then
+ if [[ "${BASELINE_CLI}" == "/"* ]]; then
+ BASELINE_CLI_PATH="${BASELINE_CLI}"
+ else
+ BASELINE_CLI_PATH=$(which "${BASELINE_CLI}")
+ fi
+
+ BASELINE_CLI_NAME=$(basename "${BASELINE_CLI}")
+ BASELINE_CLI_PATH=$(fullpath "${BASELINE_CLI}")
+ BASELINE_CLI_VERSION=$(cli_version "${BASELINE_CLI}")
+fi
+
+#
+# run the benchmarks
+#
+
+echo "##############################################################################"
+if [ "${SUITE}" != "" ]; then
+ SUITE_PREFIX="${SUITE/::/__}"
+ echo "## Running ${SUITE} benchmarks"
+else
+ echo "## Running all benchmarks"
+fi
+echo "##############################################################################"
+echo ""
+
+if [ "${BASELINE_CLI}" != "" ]; then
+ echo "# Baseline CLI: ${BASELINE_CLI} (${BASELINE_CLI_VERSION})"
+fi
+echo "# Test CLI: ${TEST_CLI} (${TEST_CLI_VERSION})"
+echo ""
+
+BENCHMARK_DIR=${BENCHMARK_DIR:=$(dirname "$0")}
+ANY_FOUND=
+ANY_FAILED=
+
+indent() { sed "s/^/ /"; }
+time_in_ms() { if [ "$(uname -s)" = "Darwin" ]; then date "+%s000"; else date "+%s%N" ; fi; }
+humanize_secs() {
+ units=('s' 'ms' 'us' 'ns')
+ unit=0
+ time="${1}"
+
+ if [ "${time}" = "" ]; then
+ echo ""
+ return
+ fi
+
+ # bash doesn't do floating point arithmetic. ick.
+ while [[ "${time}" == "0."* ]] && [ "$((unit+1))" != "${#units[*]}" ]; do
+ time="$(echo | awk "{ print ${time} * 1000 }")"
+ unit=$((unit+1))
+ done
+
+ echo "${time} ${units[$unit]}"
+}
+
+TIME_START=$(time_in_ms)
+
+for TEST_PATH in "${BENCHMARK_DIR}"/*; do
+ TEST_FILE=$(basename "${TEST_PATH}")
+
+ if [ ! -f "${TEST_PATH}" ] || [ ! -x "${TEST_PATH}" ]; then
+ continue
+ fi
+
+ if [[ "${TEST_FILE}" != *"__"* ]]; then
+ continue
+ fi
+
+ if [[ "${TEST_FILE}" != "${SUITE_PREFIX}"* ]]; then
+ continue
+ fi
+
+ ANY_FOUND=1
+ TEST_NAME="${TEST_FILE/__/::}"
+
+ echo -n "${TEST_NAME}:"
+ if [ "${VERBOSE}" = "1" ]; then
+ echo ""
+ else
+ echo -n " "
+ fi
+
+ if [ "${DEBUG}" = "1" ]; then
+ SHOW_OUTPUT="--show-output"
+ fi
+
+ OUTPUT_FILE="${OUTPUT_DIR}/${TEST_FILE}.out"
+ JSON_FILE="${OUTPUT_DIR}/${TEST_FILE}.json"
+ ERROR_FILE="${OUTPUT_DIR}/${TEST_FILE}.err"
+
+ FAILED=
+ ${TEST_PATH} --cli "${TEST_CLI}" --baseline-cli "${BASELINE_CLI}" --json "${JSON_FILE}" ${SHOW_OUTPUT} >"${OUTPUT_FILE}" 2>"${ERROR_FILE}" || FAILED=1
+
+ if [ "${FAILED}" = "1" ]; then
+ if [ "${VERBOSE}" != "1" ]; then
+ echo "failed!"
+ fi
+
+ indent < "${ERROR_FILE}"
+ ANY_FAILED=1
+ continue
+ fi
+
+ # in verbose mode, just print the hyperfine results; otherwise,
+ # pull the useful information out of its json and summarize it
+ if [ "${VERBOSE}" = "1" ]; then
+ indent < "${OUTPUT_FILE}"
+ else
+ jq -r '[ .results[0].mean, .results[0].stddev, .results[1].mean, .results[1].stddev ] | @tsv' < "${JSON_FILE}" | while IFS=$'\t' read -r one_mean one_stddev two_mean two_stddev; do
+ one_mean=$(humanize_secs "${one_mean}")
+ one_stddev=$(humanize_secs "${one_stddev}")
+
+ if [ "${two_mean}" != "" ]; then
+ two_mean=$(humanize_secs "${two_mean}")
+ two_stddev=$(humanize_secs "${two_stddev}")
+
+ echo "${one_mean} ± ${one_stddev} vs ${two_mean} ± ${two_stddev}"
+ else
+ echo "${one_mean} ± ${one_stddev}"
+ fi
+ done
+ fi
+
+ # add our metadata to the hyperfine json result
+ jq ". |= { \"name\": \"${TEST_NAME}\" } + ." < "${JSON_FILE}" > "${JSON_FILE}.new" && mv "${JSON_FILE}.new" "${JSON_FILE}"
+done
+
+TIME_END=$(time_in_ms)
+
+if [ "$ANY_FOUND" != "1" ]; then
+ echo ""
+ echo "error: no benchmark suite \"${SUITE}\"."
+ echo ""
+ exit 1
+fi
+
+escape() {
+ echo "${1//\\/\\\\}"
+}
+
+# combine all the individual benchmark results into a single json file
+if [ "${JSON_RESULT}" != "" ]; then
+ if [ "${VERBOSE}" = "1" ]; then
+ echo ""
+ echo "# Writing JSON results: ${JSON_RESULT}"
+ fi
+
+ SYSTEM_JSON="{ \"os\": \"${SYSTEM_OS}\", \"kernel\": \"${SYSTEM_KERNEL}\" }"
+ TIME_JSON="{ \"start\": ${TIME_START}, \"end\": ${TIME_END} }"
+ TEST_CLI_JSON="{ \"name\": \"${TEST_CLI_NAME}\", \"path\": \"$(escape "${TEST_CLI_PATH}")\", \"version\": \"${TEST_CLI_VERSION}\" }"
+ BASELINE_CLI_JSON="{ \"name\": \"${BASELINE_CLI_NAME}\", \"path\": \"$(escape "${BASELINE_CLI_PATH}")\", \"version\": \"${BASELINE_CLI_VERSION}\" }"
+
+ if [ "${BASELINE_CLI}" != "" ]; then
+ EXECUTOR_JSON="{ \"baseline\": ${BASELINE_CLI_JSON}, \"cli\": ${TEST_CLI_JSON} }"
+ else
+ EXECUTOR_JSON="{ \"cli\": ${TEST_CLI_JSON} }"
+ fi
+
+ # add our metadata to all the test results
+ jq -n "{ \"system\": ${SYSTEM_JSON}, \"time\": ${TIME_JSON}, \"executor\": ${EXECUTOR_JSON}, \"tests\": [inputs] }" "${OUTPUT_DIR}"/*.json > "${JSON_RESULT}"
+fi
+
+# combine all the data into a zip if requested
+if [ "${ZIP_RESULT}" != "" ]; then
+ if [ "${VERBOSE}" = "1" ]; then
+ if [ "${JSON_RESULT}" = "" ]; then echo ""; fi
+ echo "# Writing ZIP results: ${ZIP_RESULT}"
+ fi
+
+ zip -jr "${ZIP_RESULT}" "${OUTPUT_DIR}" >/dev/null
+fi
+
+if [ "$CLEANUP_DIR" = "1" ]; then
+ rm -f "${OUTPUT_DIR}"/*.out
+ rm -f "${OUTPUT_DIR}"/*.err
+ rm -f "${OUTPUT_DIR}"/*.json
+ rmdir "${OUTPUT_DIR}"
+fi
+
+if [ "$ANY_FAILED" = "1" ]; then
+ exit 1
+fi
diff --git a/tests/benchmarks/benchmark_helpers.sh b/tests/benchmarks/benchmark_helpers.sh
new file mode 100644
index 0000000..14dbb43
--- /dev/null
+++ b/tests/benchmarks/benchmark_helpers.sh
@@ -0,0 +1,363 @@
+# variables that benchmark tests can set
+#
+
+set -eo pipefail
+
+#
+# command-line parsing
+#
+
+usage() { echo "usage: $(basename "$0") [--cli <path>] [--baseline-cli <path>] [--output-style <style>] [--json <path>]"; }
+
+NEXT=
+BASELINE_CLI=
+TEST_CLI="git"
+JSON=
+SHOW_OUTPUT=
+
+if [ "$CI" != "" ]; then
+ OUTPUT_STYLE="color"
+else
+ OUTPUT_STYLE="auto"
+fi
+
+#
+# parse the arguments to the outer script that's including us; these are arguments that
+# the `benchmark.sh` passes (or that a user could specify when running an individual test)
+#
+
+for a in "$@"; do
+ if [ "${NEXT}" = "cli" ]; then
+ TEST_CLI="${a}"
+ NEXT=
+ elif [ "${NEXT}" = "baseline-cli" ]; then
+ BASELINE_CLI="${a}"
+ NEXT=
+ elif [ "${NEXT}" = "output-style" ]; then
+ OUTPUT_STYLE="${a}"
+ NEXT=
+ elif [ "${NEXT}" = "json" ]; then
+ JSON="${a}"
+ NEXT=
+ elif [ "${a}" = "-c" ] || [ "${a}" = "--cli" ]; then
+ NEXT="cli"
+ elif [[ "${a}" == "-c"* ]]; then
+ TEST_CLI="${a/-c/}"
+ elif [ "${a}" = "-b" ] || [ "${a}" = "--baseline-cli" ]; then
+ NEXT="baseline-cli"
+ elif [[ "${a}" == "-b"* ]]; then
+ BASELINE_CLI="${a/-b/}"
+ elif [ "${a}" == "--output-style" ]; then
+ NEXT="output-style"
+ elif [ "${a}" = "-j" ] || [ "${a}" = "--json" ]; then
+ NEXT="json"
+ elif [[ "${a}" == "-j"* ]]; then
+ JSON="${a}"
+ elif [ "${a}" = "--show-output" ]; then
+ SHOW_OUTPUT=1
+ OUTPUT_STYLE=
+ else
+ echo "$(basename "$0"): unknown option: ${a}" 1>&2
+ usage 1>&2
+ exit 1
+ fi
+done
+
+if [ "${NEXT}" != "" ]; then
+ echo "$(basename "$0"): option requires a value: --${NEXT}" 1>&2
+ usage 1>&2
+ exit 1
+fi
+
+fullpath() {
+ FULLPATH="${1}"
+ if [[ "$(uname -s)" == "MINGW"* ]]; then FULLPATH="$(cygpath -u "${1}")"; fi
+
+ if [[ "${FULLPATH}" != *"/"* ]]; then
+ FULLPATH="$(which "${FULLPATH}")"
+ if [ "$?" != "0" ]; then exit 1; fi
+ else
+ FULLPATH="$(cd "$(dirname "${FULLPATH}")" && pwd)/$(basename "${FULLPATH}")"
+ fi
+
+ if [[ "$(uname -s)" == "MINGW"* ]]; then FULLPATH="$(cygpath -w "${FULLPATH}")"; fi
+ echo "${FULLPATH}"
+}
+
+resources_dir() {
+ cd "$(dirname "$0")/../resources" && pwd
+}
+
+temp_dir() {
+ if [ "$(uname -s)" == "Darwin" ]; then
+ mktemp -dt libgit2_bench
+ else
+ mktemp -dt libgit2_bench.XXXXXXX
+ fi
+}
+
+create_preparescript() {
+ # add some functions for users to use in preparation
+ cat >> "${SANDBOX_DIR}/prepare.sh" << EOF
+ set -e
+
+ SANDBOX_DIR="${SANDBOX_DIR}"
+ RESOURCES_DIR="$(resources_dir)"
+
+ create_text_file() {
+ FILENAME="\${1}"
+ SIZE="\${2}"
+
+ if [ "\${FILENAME}" = "" ]; then
+ echo "usage: create_text_file <name> [size]" 1>&2
+ exit 1
+ fi
+
+ if [ "\${SIZE}" = "" ]; then
+ SIZE="1024"
+ fi
+
+ if [[ "\$(uname -s)" == "MINGW"* ]]; then
+ EOL="\r\n"
+ EOL_LEN="2"
+ CONTENTS="This is a reproducible text file. (With Unix line endings.)\n"
+ CONTENTS_LEN="60"
+ else
+ EOL="\n"
+ EOL_LEN="1"
+ CONTENTS="This is a reproducible text file. (With DOS line endings.)\r\n"
+ CONTENTS_LEN="60"
+ fi
+
+ rm -f "\${FILENAME:?}"
+ touch "\${FILENAME}"
+
+ if [ "\${SIZE}" -ge "\$((\${CONTENTS_LEN} + \${EOL_LEN}))" ]; then
+ SIZE="\$((\${SIZE} - \${CONTENTS_LEN}))"
+ COUNT="\$(((\${SIZE} - \${EOL_LEN}) / \${CONTENTS_LEN}))"
+
+ if [ "\${SIZE}" -gt "\${EOL_LEN}" ]; then
+ dd if="\${FILENAME}" of="\${FILENAME}" bs="\${CONTENTS_LEN}" seek=1 count="\${COUNT}" 2>/dev/null
+ fi
+
+ SIZE="\$((\${SIZE} - (\${COUNT} * \${CONTENTS_LEN})))"
+ fi
+
+ while [ "\${SIZE}" -gt "\${EOL_LEN}" ]; do
+ echo -ne "." >> "\${FILENAME}"
+ SIZE="\$((\${SIZE} - 1))"
+ done
+
+ if [ "\${SIZE}" = "\${EOL_LEN}" ]; then
+ echo -ne "\${EOL}" >> "\${FILENAME}"
+ SIZE="\$((\${SIZE} - \${EOL_LEN}))"
+ else
+ while [ "\${SIZE}" -gt "0" ]; do
+ echo -ne "." >> "\${FILENAME}"
+ SIZE="\$((\${SIZE} - 1))"
+ done
+ fi
+ }
+
+ create_random_file() {
+ FILENAME="\${1}"
+ SIZE="\${2}"
+
+ if [ "\${FILENAME}" = "" ]; then
+ echo "usage: create_random_file <name> [size]" 1>&2
+ exit 1
+ fi
+
+ if [ "\${SIZE}" = "" ]; then
+ SIZE="1024"
+ fi
+
+ dd if="/dev/urandom" of="\${FILENAME}" bs="\${SIZE}" count=1 2>/dev/null
+ }
+
+ flush_disk_cache() {
+ if [ "\$(uname -s)" = "Darwin" ]; then
+ sync && sudo purge
+ elif [ "\$(uname -s)" = "Linux" ]; then
+ sync && echo 3 | sudo tee /proc/sys/vm/drop_caches >/dev/null
+ elif [[ "\$(uname -s)" == "MINGW"* ]]; then
+ PurgeStandbyList
+ fi
+ }
+
+ sandbox() {
+ RESOURCE="\${1}"
+
+ if [ "\${RESOURCE}" = "" ]; then
+ echo "usage: sandbox <path>" 1>&2
+ exit 1
+ fi
+
+ if [ ! -d "\${RESOURCES_DIR}/\${RESOURCE}" ]; then
+ echo "sandbox: the resource \"\${RESOURCE}\" does not exist"
+ exit 1
+ fi
+
+ rm -rf "\${SANDBOX_DIR:?}/\${RESOURCE}"
+ cp -R "\${RESOURCES_DIR}/\${RESOURCE}" "\${SANDBOX_DIR}/"
+ }
+
+ sandbox_repo() {
+ RESOURCE="\${1}"
+
+ sandbox "\${RESOURCE}"
+
+ if [ -d "\${SANDBOX_DIR}/\${RESOURCE}/.gitted" ]; then
+ mv "\${SANDBOX_DIR}/\${RESOURCE}/.gitted" "\${SANDBOX_DIR}/\${RESOURCE}/.git";
+ fi
+ if [ -f "\${SANDBOX_DIR}/\${RESOURCE}/gitattributes" ]; then
+ mv "\${SANDBOX_DIR}/\${RESOURCE}/gitattributes" "\${SANDBOX_DIR}/\${RESOURCE}/.gitattributes";
+ fi
+ if [ -f "\${SANDBOX_DIR}/\${RESOURCE}/gitignore" ]; then
+ mv "\${SANDBOX_DIR}/\${RESOURCE}/gitignore" "\${SANDBOX_DIR}/\${RESOURCE}/.gitignore";
+ fi
+ }
+
+ cd "\${SANDBOX_DIR}"
+EOF
+
+ if [ "${PREPARE}" != "" ]; then
+ echo "" >> "${SANDBOX_DIR}/prepare.sh"
+ echo "${PREPARE}" >> "${SANDBOX_DIR}/prepare.sh"
+ fi
+
+ echo "${SANDBOX_DIR}/prepare.sh"
+}
+
+create_runscript() {
+ SCRIPT_NAME="${1}"; shift
+ CLI_PATH="${1}"; shift
+
+ if [[ "${CHDIR}" = "/"* ]]; then
+ START_DIR="${CHDIR}"
+ elif [ "${CHDIR}" != "" ]; then
+ START_DIR="${SANDBOX_DIR}/${CHDIR}"
+ else
+ START_DIR="${SANDBOX_DIR}"
+ fi
+
+ # our run script starts by chdir'ing to the sandbox or repository directory
+ echo -n "cd \"${START_DIR}\" && \"${CLI_PATH}\"" >> "${SANDBOX_DIR}/${SCRIPT_NAME}.sh"
+
+ for a in "$@"; do
+ echo -n " \"${a}\"" >> "${SANDBOX_DIR}/${SCRIPT_NAME}.sh"
+ done
+
+ echo "${SANDBOX_DIR}/${SCRIPT_NAME}.sh"
+}
+
+gitbench_usage() { echo "usage: gitbench command..."; }
+
+#
+# this is the function that the outer script calls to actually do the sandboxing and
+# invocation of hyperfine.
+#
+gitbench() {
+ NEXT=
+
+ # this test should run the given command in preparation of the tests
+ # this preparation script will be run _after_ repository creation and
+ # _before_ flushing the disk cache
+ PREPARE=
+
+ # this test should run within the given directory; this is a
+ # relative path beneath the sandbox directory.
+ CHDIR=
+
+ # this test should run `n` warmups
+ WARMUP=0
+
+ if [ "$*" = "" ]; then
+ gitbench_usage 1>&2
+ exit 1
+ fi
+
+ for a in "$@"; do
+ if [ "${NEXT}" = "warmup" ]; then
+ WARMUP="${a}"
+ NEXT=
+ elif [ "${NEXT}" = "prepare" ]; then
+ PREPARE="${a}"
+ NEXT=
+ elif [ "${NEXT}" = "chdir" ]; then
+ CHDIR="${a}"
+ NEXT=
+ elif [ "${a}" = "--warmup" ]; then
+ NEXT="warmup"
+ elif [ "${a}" = "--prepare" ]; then
+ NEXT="prepare"
+ elif [ "${a}" = "--chdir" ]; then
+ NEXT="chdir"
+ elif [[ "${a}" == "--"* ]]; then
+ echo "unknown argument: \"${a}\"" 1>&2
+ gitbench_usage 1>&2
+ exit 1
+ else
+ break
+ fi
+
+ shift
+ done
+
+ if [ "${NEXT}" != "" ]; then
+ echo "$(basename "$0"): option requires a value: --${NEXT}" 1>&2
+ gitbench_usage 1>&2
+ exit 1
+ fi
+
+ # sanity check
+
+ for a in "${SANDBOX[@]}"; do
+ if [ ! -d "$(resources_dir)/${a}" ]; then
+ echo "$0: no resource '${a}' found" 1>&2
+ exit 1
+ fi
+ done
+
+ if [ "$REPOSITORY" != "" ]; then
+ if [ ! -d "$(resources_dir)/${REPOSITORY}" ]; then
+ echo "$0: no repository resource '${REPOSITORY}' found" 1>&2
+ exit 1
+ fi
+ fi
+
+ # set up our sandboxing
+
+ SANDBOX_DIR="$(temp_dir)"
+
+ if [ "${BASELINE_CLI}" != "" ]; then
+ BASELINE_CLI_PATH=$(fullpath "${BASELINE_CLI}")
+ BASELINE_RUN_SCRIPT=$(create_runscript "baseline" "${BASELINE_CLI_PATH}" "$@")
+ fi
+ TEST_CLI_PATH=$(fullpath "${TEST_CLI}")
+ TEST_RUN_SCRIPT=$(create_runscript "test" "${TEST_CLI_PATH}" "$@")
+
+ PREPARE_SCRIPT="$(create_preparescript)"
+ ARGUMENTS=("--prepare" "bash ${PREPARE_SCRIPT}" "--warmup" "${WARMUP}")
+
+ if [ "${OUTPUT_STYLE}" != "" ]; then
+ ARGUMENTS+=("--style" "${OUTPUT_STYLE}")
+ fi
+
+ if [ "${SHOW_OUTPUT}" != "" ]; then
+ ARGUMENTS+=("--show-output")
+ fi
+
+ if [ "$JSON" != "" ]; then
+ ARGUMENTS+=("--export-json" "${JSON}")
+ fi
+
+ if [ "${BASELINE_CLI}" != "" ]; then
+ ARGUMENTS+=("-n" "${BASELINE_CLI} $*" "bash ${BASELINE_RUN_SCRIPT}")
+ fi
+
+ ARGUMENTS+=("-n" "${TEST_CLI} $*" "bash ${TEST_RUN_SCRIPT}")
+
+ hyperfine "${ARGUMENTS[@]}"
+ rm -rf "${SANDBOX_DIR:?}"
+}
diff --git a/tests/benchmarks/hash-object__text_100kb b/tests/benchmarks/hash-object__text_100kb
new file mode 100755
index 0000000..d77f224
--- /dev/null
+++ b/tests/benchmarks/hash-object__text_100kb
@@ -0,0 +1,7 @@
+#!/bin/bash -e
+
+. "$(dirname "$0")/benchmark_helpers.sh"
+
+gitbench --prepare "create_text_file text_100kb 102400 &&
+ flush_disk_cache" \
+ hash-object "text_100kb"
diff --git a/tests/benchmarks/hash-object__text_10mb b/tests/benchmarks/hash-object__text_10mb
new file mode 100755
index 0000000..215afc6
--- /dev/null
+++ b/tests/benchmarks/hash-object__text_10mb
@@ -0,0 +1,7 @@
+#!/bin/bash -e
+
+. "$(dirname "$0")/benchmark_helpers.sh"
+
+gitbench --prepare "create_text_file text_10mb 10485760 &&
+ flush_disk_cache" \
+ hash-object "text_10mb"
diff --git a/tests/benchmarks/hash-object__text_1kb b/tests/benchmarks/hash-object__text_1kb
new file mode 100755
index 0000000..1348b2f
--- /dev/null
+++ b/tests/benchmarks/hash-object__text_1kb
@@ -0,0 +1,7 @@
+#!/bin/bash -e
+
+. "$(dirname "$0")/benchmark_helpers.sh"
+
+gitbench --prepare "create_text_file text_1kb 1024 &&
+ flush_disk_cache" \
+ hash-object "text_1kb"
diff --git a/tests/benchmarks/hash-object__text_cached_100kb b/tests/benchmarks/hash-object__text_cached_100kb
new file mode 100755
index 0000000..7c2a841
--- /dev/null
+++ b/tests/benchmarks/hash-object__text_cached_100kb
@@ -0,0 +1,7 @@
+#!/bin/bash -e
+
+. "$(dirname "$0")/benchmark_helpers.sh"
+
+gitbench --prepare "create_text_file text_100kb 102400" \
+ --warmup 5 \
+ hash-object "text_100kb"
diff --git a/tests/benchmarks/hash-object__text_cached_10mb b/tests/benchmarks/hash-object__text_cached_10mb
new file mode 100755
index 0000000..311ddca
--- /dev/null
+++ b/tests/benchmarks/hash-object__text_cached_10mb
@@ -0,0 +1,7 @@
+#!/bin/bash -e
+
+. "$(dirname "$0")/benchmark_helpers.sh"
+
+gitbench --prepare "create_text_file text_10mb 10485760" \
+ --warmup 5 \
+ hash-object "text_10mb"
diff --git a/tests/benchmarks/hash-object__text_cached_1kb b/tests/benchmarks/hash-object__text_cached_1kb
new file mode 100755
index 0000000..44c06ea
--- /dev/null
+++ b/tests/benchmarks/hash-object__text_cached_1kb
@@ -0,0 +1,7 @@
+#!/bin/bash -e
+
+. "$(dirname "$0")/benchmark_helpers.sh"
+
+gitbench --prepare "create_text_file text_1kb 1024" \
+ --warmup 5 \
+ hash-object "text_1kb"
diff --git a/tests/benchmarks/hash-object__write_text_100kb b/tests/benchmarks/hash-object__write_text_100kb
new file mode 100755
index 0000000..fb72c09
--- /dev/null
+++ b/tests/benchmarks/hash-object__write_text_100kb
@@ -0,0 +1,9 @@
+#!/bin/bash -e
+
+. "$(dirname "$0")/benchmark_helpers.sh"
+
+gitbench --prepare "sandbox_repo empty_standard_repo &&
+ create_text_file text_100kb 102400 &&
+ flush_disk_cache" \
+ --chdir "empty_standard_repo" \
+ hash-object -w "../text_100kb"
diff --git a/tests/benchmarks/hash-object__write_text_10mb b/tests/benchmarks/hash-object__write_text_10mb
new file mode 100755
index 0000000..9da0919
--- /dev/null
+++ b/tests/benchmarks/hash-object__write_text_10mb
@@ -0,0 +1,9 @@
+#!/bin/bash -e
+
+. "$(dirname "$0")/benchmark_helpers.sh"
+
+gitbench --prepare "sandbox_repo empty_standard_repo &&
+ create_text_file text_10mb 10485760 &&
+ flush_disk_cache" \
+ --chdir "empty_standard_repo" \
+ hash-object "../text_10mb"
diff --git a/tests/benchmarks/hash-object__write_text_1kb b/tests/benchmarks/hash-object__write_text_1kb
new file mode 100755
index 0000000..ca34393
--- /dev/null
+++ b/tests/benchmarks/hash-object__write_text_1kb
@@ -0,0 +1,9 @@
+#!/bin/bash -e
+
+. "$(dirname "$0")/benchmark_helpers.sh"
+
+gitbench --prepare "sandbox_repo empty_standard_repo &&
+ create_text_file text_1kb 1024 &&
+ flush_disk_cache" \
+ --chdir "empty_standard_repo" \
+ hash-object "../text_1kb"
diff --git a/tests/benchmarks/hash-object__write_text_cached_100kb b/tests/benchmarks/hash-object__write_text_cached_100kb
new file mode 100755
index 0000000..6803eee
--- /dev/null
+++ b/tests/benchmarks/hash-object__write_text_cached_100kb
@@ -0,0 +1,9 @@
+#!/bin/bash -e
+
+. "$(dirname "$0")/benchmark_helpers.sh"
+
+gitbench --prepare "sandbox_repo empty_standard_repo &&
+ create_text_file text_100kb 102400" \
+ --warmup 5 \
+ --chdir "empty_standard_repo" \
+ hash-object "../text_100kb"
diff --git a/tests/benchmarks/hash-object__write_text_cached_10mb b/tests/benchmarks/hash-object__write_text_cached_10mb
new file mode 100755
index 0000000..4335cc8
--- /dev/null
+++ b/tests/benchmarks/hash-object__write_text_cached_10mb
@@ -0,0 +1,9 @@
+#!/bin/bash -e
+
+. "$(dirname "$0")/benchmark_helpers.sh"
+
+gitbench --prepare "sandbox_repo empty_standard_repo &&
+ create_text_file text_10mb 10485760" \
+ --warmup 5 \
+ --chdir "empty_standard_repo" \
+ hash-object "../text_10mb"
diff --git a/tests/benchmarks/hash-object__write_text_cached_1kb b/tests/benchmarks/hash-object__write_text_cached_1kb
new file mode 100755
index 0000000..8a4c5c9
--- /dev/null
+++ b/tests/benchmarks/hash-object__write_text_cached_1kb
@@ -0,0 +1,9 @@
+#!/bin/bash -e
+
+. "$(dirname "$0")/benchmark_helpers.sh"
+
+gitbench --prepare "sandbox_repo empty_standard_repo &&
+ create_text_file text_1kb 1024" \
+ --warmup 5 \
+ --chdir "empty_standard_repo" \
+ hash-object "../text_1kb"
diff --git a/tests/clar/clar.c b/tests/clar/clar.c
new file mode 100644
index 0000000..c9c3fde
--- /dev/null
+++ b/tests/clar/clar.c
@@ -0,0 +1,840 @@
+/*
+ * Copyright (c) Vicent Marti. All rights reserved.
+ *
+ * This file is part of clar, distributed under the ISC license.
+ * For full terms see the included COPYING file.
+ */
+#include <assert.h>
+#include <setjmp.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <math.h>
+#include <stdarg.h>
+#include <wchar.h>
+#include <time.h>
+
+/* required for sandboxing */
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#ifdef _WIN32
+# include <windows.h>
+# include <io.h>
+# include <shellapi.h>
+# include <direct.h>
+
+# define _MAIN_CC __cdecl
+
+# ifndef stat
+# define stat(path, st) _stat(path, st)
+# endif
+# ifndef mkdir
+# define mkdir(path, mode) _mkdir(path)
+# endif
+# ifndef chdir
+# define chdir(path) _chdir(path)
+# endif
+# ifndef access
+# define access(path, mode) _access(path, mode)
+# endif
+# ifndef strdup
+# define strdup(str) _strdup(str)
+# endif
+# ifndef strcasecmp
+# define strcasecmp(a,b) _stricmp(a,b)
+# endif
+
+# ifndef __MINGW32__
+# pragma comment(lib, "shell32")
+# ifndef strncpy
+# define strncpy(to, from, to_size) strncpy_s(to, to_size, from, _TRUNCATE)
+# endif
+# ifndef W_OK
+# define W_OK 02
+# endif
+# ifndef S_ISDIR
+# define S_ISDIR(x) ((x & _S_IFDIR) != 0)
+# endif
+# define p_snprintf(buf,sz,fmt,...) _snprintf_s(buf,sz,_TRUNCATE,fmt,__VA_ARGS__)
+# else
+# define p_snprintf snprintf
+# endif
+
+# ifndef PRIuZ
+# define PRIuZ "Iu"
+# endif
+# ifndef PRIxZ
+# define PRIxZ "Ix"
+# endif
+
+# if defined(_MSC_VER) || defined(__MINGW32__)
+ typedef struct stat STAT_T;
+# else
+ typedef struct _stat STAT_T;
+# endif
+#else
+# include <sys/wait.h> /* waitpid(2) */
+# include <unistd.h>
+# define _MAIN_CC
+# define p_snprintf snprintf
+# ifndef PRIuZ
+# define PRIuZ "zu"
+# endif
+# ifndef PRIxZ
+# define PRIxZ "zx"
+# endif
+ typedef struct stat STAT_T;
+#endif
+
+#define MAX(x, y) (((x) > (y)) ? (x) : (y))
+
+#include "clar.h"
+
+static void fs_rm(const char *_source);
+static void fs_copy(const char *_source, const char *dest);
+
+static const char *
+fixture_path(const char *base, const char *fixture_name);
+
+struct clar_error {
+ const char *file;
+ const char *function;
+ size_t line_number;
+ const char *error_msg;
+ char *description;
+
+ struct clar_error *next;
+};
+
+struct clar_explicit {
+ size_t suite_idx;
+ const char *filter;
+
+ struct clar_explicit *next;
+};
+
+struct clar_report {
+ const char *test;
+ int test_number;
+ const char *suite;
+
+ enum cl_test_status status;
+ time_t start;
+ double elapsed;
+
+ struct clar_error *errors;
+ struct clar_error *last_error;
+
+ struct clar_report *next;
+};
+
+struct clar_summary {
+ const char *filename;
+ FILE *fp;
+};
+
+static struct {
+ enum cl_test_status test_status;
+
+ const char *active_test;
+ const char *active_suite;
+
+ int total_skipped;
+ int total_errors;
+
+ int tests_ran;
+ int suites_ran;
+
+ enum cl_output_format output_format;
+
+ int report_errors_only;
+ int exit_on_error;
+ int verbosity;
+
+ int write_summary;
+ char *summary_filename;
+ struct clar_summary *summary;
+
+ struct clar_explicit *explicit;
+ struct clar_explicit *last_explicit;
+
+ struct clar_report *reports;
+ struct clar_report *last_report;
+
+ void (*local_cleanup)(void *);
+ void *local_cleanup_payload;
+
+ jmp_buf trampoline;
+ int trampoline_enabled;
+
+ cl_trace_cb *pfn_trace_cb;
+ void *trace_payload;
+
+} _clar;
+
+struct clar_func {
+ const char *name;
+ void (*ptr)(void);
+};
+
+struct clar_suite {
+ const char *name;
+ struct clar_func initialize;
+ struct clar_func cleanup;
+ const struct clar_func *tests;
+ size_t test_count;
+ int enabled;
+};
+
+/* From clar_print_*.c */
+static void clar_print_init(int test_count, int suite_count, const char *suite_names);
+static void clar_print_shutdown(int test_count, int suite_count, int error_count);
+static void clar_print_error(int num, const struct clar_report *report, const struct clar_error *error);
+static void clar_print_ontest(const char *suite_name, const char *test_name, int test_number, enum cl_test_status failed);
+static void clar_print_onsuite(const char *suite_name, int suite_index);
+static void clar_print_onabort(const char *msg, ...);
+
+/* From clar_sandbox.c */
+static void clar_unsandbox(void);
+static int clar_sandbox(void);
+
+/* From summary.h */
+static struct clar_summary *clar_summary_init(const char *filename);
+static int clar_summary_shutdown(struct clar_summary *fp);
+
+/* Load the declarations for the test suite */
+#include "clar.suite"
+
+
+#define CL_TRACE(ev) \
+ do { \
+ if (_clar.pfn_trace_cb) \
+ _clar.pfn_trace_cb(ev, \
+ _clar.active_suite, \
+ _clar.active_test, \
+ _clar.trace_payload); \
+ } while (0)
+
+void cl_trace_register(cl_trace_cb *cb, void *payload)
+{
+ _clar.pfn_trace_cb = cb;
+ _clar.trace_payload = payload;
+}
+
+
+/* Core test functions */
+static void
+clar_report_errors(struct clar_report *report)
+{
+ struct clar_error *error;
+ int i = 1;
+
+ for (error = report->errors; error; error = error->next)
+ clar_print_error(i++, _clar.last_report, error);
+}
+
+static void
+clar_report_all(void)
+{
+ struct clar_report *report;
+ struct clar_error *error;
+ int i = 1;
+
+ for (report = _clar.reports; report; report = report->next) {
+ if (report->status != CL_TEST_FAILURE)
+ continue;
+
+ for (error = report->errors; error; error = error->next)
+ clar_print_error(i++, report, error);
+ }
+}
+
+#ifdef WIN32
+# define clar_time DWORD
+
+static void clar_time_now(clar_time *out)
+{
+ *out = GetTickCount();
+}
+
+static double clar_time_diff(clar_time *start, clar_time *end)
+{
+ return ((double)*end - (double)*start) / 1000;
+}
+#else
+# include <sys/time.h>
+
+# define clar_time struct timeval
+
+static void clar_time_now(clar_time *out)
+{
+ struct timezone tz;
+
+ gettimeofday(out, &tz);
+}
+
+static double clar_time_diff(clar_time *start, clar_time *end)
+{
+ return ((double)end->tv_sec + (double)end->tv_usec / 1.0E6) -
+ ((double)start->tv_sec + (double)start->tv_usec / 1.0E6);
+}
+#endif
+
+static void
+clar_run_test(
+ const struct clar_suite *suite,
+ const struct clar_func *test,
+ const struct clar_func *initialize,
+ const struct clar_func *cleanup)
+{
+ clar_time start, end;
+
+ _clar.trampoline_enabled = 1;
+
+ CL_TRACE(CL_TRACE__TEST__BEGIN);
+
+ _clar.last_report->start = time(NULL);
+ clar_time_now(&start);
+
+ if (setjmp(_clar.trampoline) == 0) {
+ if (initialize->ptr != NULL)
+ initialize->ptr();
+
+ CL_TRACE(CL_TRACE__TEST__RUN_BEGIN);
+ test->ptr();
+ CL_TRACE(CL_TRACE__TEST__RUN_END);
+ }
+
+ clar_time_now(&end);
+
+ _clar.trampoline_enabled = 0;
+
+ if (_clar.last_report->status == CL_TEST_NOTRUN)
+ _clar.last_report->status = CL_TEST_OK;
+
+ _clar.last_report->elapsed = clar_time_diff(&start, &end);
+
+ if (_clar.local_cleanup != NULL)
+ _clar.local_cleanup(_clar.local_cleanup_payload);
+
+ if (cleanup->ptr != NULL)
+ cleanup->ptr();
+
+ CL_TRACE(CL_TRACE__TEST__END);
+
+ _clar.tests_ran++;
+
+ /* remove any local-set cleanup methods */
+ _clar.local_cleanup = NULL;
+ _clar.local_cleanup_payload = NULL;
+
+ if (_clar.report_errors_only) {
+ clar_report_errors(_clar.last_report);
+ } else {
+ clar_print_ontest(suite->name, test->name, _clar.tests_ran, _clar.last_report->status);
+ }
+}
+
+static void
+clar_run_suite(const struct clar_suite *suite, const char *filter)
+{
+ const struct clar_func *test = suite->tests;
+ size_t i, matchlen;
+ struct clar_report *report;
+ int exact = 0;
+
+ if (!suite->enabled)
+ return;
+
+ if (_clar.exit_on_error && _clar.total_errors)
+ return;
+
+ if (!_clar.report_errors_only)
+ clar_print_onsuite(suite->name, ++_clar.suites_ran);
+
+ _clar.active_suite = suite->name;
+ _clar.active_test = NULL;
+ CL_TRACE(CL_TRACE__SUITE_BEGIN);
+
+ if (filter) {
+ size_t suitelen = strlen(suite->name);
+ matchlen = strlen(filter);
+ if (matchlen <= suitelen) {
+ filter = NULL;
+ } else {
+ filter += suitelen;
+ while (*filter == ':')
+ ++filter;
+ matchlen = strlen(filter);
+
+ if (matchlen && filter[matchlen - 1] == '$') {
+ exact = 1;
+ matchlen--;
+ }
+ }
+ }
+
+ for (i = 0; i < suite->test_count; ++i) {
+ if (filter && strncmp(test[i].name, filter, matchlen))
+ continue;
+
+ if (exact && strlen(test[i].name) != matchlen)
+ continue;
+
+ _clar.active_test = test[i].name;
+
+ report = calloc(1, sizeof(struct clar_report));
+ report->suite = _clar.active_suite;
+ report->test = _clar.active_test;
+ report->test_number = _clar.tests_ran;
+ report->status = CL_TEST_NOTRUN;
+
+ if (_clar.reports == NULL)
+ _clar.reports = report;
+
+ if (_clar.last_report != NULL)
+ _clar.last_report->next = report;
+
+ _clar.last_report = report;
+
+ clar_run_test(suite, &test[i], &suite->initialize, &suite->cleanup);
+
+ if (_clar.exit_on_error && _clar.total_errors)
+ return;
+ }
+
+ _clar.active_test = NULL;
+ CL_TRACE(CL_TRACE__SUITE_END);
+}
+
+static void
+clar_usage(const char *arg)
+{
+ printf("Usage: %s [options]\n\n", arg);
+ printf("Options:\n");
+ printf(" -sname Run only the suite with `name` (can go to individual test name)\n");
+ printf(" -iname Include the suite with `name`\n");
+ printf(" -xname Exclude the suite with `name`\n");
+ printf(" -v Increase verbosity (show suite names)\n");
+ printf(" -q Only report tests that had an error\n");
+ printf(" -Q Quit as soon as a test fails\n");
+ printf(" -t Display results in tap format\n");
+ printf(" -l Print suite names\n");
+ printf(" -r[filename] Write summary file (to the optional filename)\n");
+ exit(-1);
+}
+
+static void
+clar_parse_args(int argc, char **argv)
+{
+ int i;
+
+ /* Verify options before execute */
+ for (i = 1; i < argc; ++i) {
+ char *argument = argv[i];
+
+ if (argument[0] != '-' || argument[1] == '\0'
+ || strchr("sixvqQtlr", argument[1]) == NULL) {
+ clar_usage(argv[0]);
+ }
+ }
+
+ for (i = 1; i < argc; ++i) {
+ char *argument = argv[i];
+
+ switch (argument[1]) {
+ case 's':
+ case 'i':
+ case 'x': { /* given suite name */
+ int offset = (argument[2] == '=') ? 3 : 2, found = 0;
+ char action = argument[1];
+ size_t j, arglen, suitelen, cmplen;
+
+ argument += offset;
+ arglen = strlen(argument);
+
+ if (arglen == 0)
+ clar_usage(argv[0]);
+
+ for (j = 0; j < _clar_suite_count; ++j) {
+ suitelen = strlen(_clar_suites[j].name);
+ cmplen = (arglen < suitelen) ? arglen : suitelen;
+
+ if (strncmp(argument, _clar_suites[j].name, cmplen) == 0) {
+ int exact = (arglen >= suitelen);
+
+ /* Do we have a real suite prefix separated by a
+ * trailing '::' or just a matching substring? */
+ if (arglen > suitelen && (argument[suitelen] != ':'
+ || argument[suitelen + 1] != ':'))
+ continue;
+
+ ++found;
+
+ if (!exact)
+ _clar.verbosity = MAX(_clar.verbosity, 1);
+
+ switch (action) {
+ case 's': {
+ struct clar_explicit *explicit =
+ calloc(1, sizeof(struct clar_explicit));
+ assert(explicit);
+
+ explicit->suite_idx = j;
+ explicit->filter = argument;
+
+ if (_clar.explicit == NULL)
+ _clar.explicit = explicit;
+
+ if (_clar.last_explicit != NULL)
+ _clar.last_explicit->next = explicit;
+
+ _clar_suites[j].enabled = 1;
+ _clar.last_explicit = explicit;
+ break;
+ }
+ case 'i': _clar_suites[j].enabled = 1; break;
+ case 'x': _clar_suites[j].enabled = 0; break;
+ }
+
+ if (exact)
+ break;
+ }
+ }
+
+ if (!found) {
+ clar_print_onabort("No suite matching '%s' found.\n", argument);
+ exit(-1);
+ }
+ break;
+ }
+
+ case 'q':
+ _clar.report_errors_only = 1;
+ break;
+
+ case 'Q':
+ _clar.exit_on_error = 1;
+ break;
+
+ case 't':
+ _clar.output_format = CL_OUTPUT_TAP;
+ break;
+
+ case 'l': {
+ size_t j;
+ printf("Test suites (use -s<name> to run just one):\n");
+ for (j = 0; j < _clar_suite_count; ++j)
+ printf(" %3d: %s\n", (int)j, _clar_suites[j].name);
+
+ exit(0);
+ }
+
+ case 'v':
+ _clar.verbosity++;
+ break;
+
+ case 'r':
+ _clar.write_summary = 1;
+ free(_clar.summary_filename);
+ _clar.summary_filename = *(argument + 2) ? strdup(argument + 2) : NULL;
+ break;
+
+ default:
+ assert(!"Unexpected commandline argument!");
+ }
+ }
+}
+
+void
+clar_test_init(int argc, char **argv)
+{
+ const char *summary_env;
+
+ if (argc > 1)
+ clar_parse_args(argc, argv);
+
+ clar_print_init(
+ (int)_clar_callback_count,
+ (int)_clar_suite_count,
+ ""
+ );
+
+ if (!_clar.summary_filename &&
+ (summary_env = getenv("CLAR_SUMMARY")) != NULL) {
+ _clar.write_summary = 1;
+ _clar.summary_filename = strdup(summary_env);
+ }
+
+ if (_clar.write_summary && !_clar.summary_filename)
+ _clar.summary_filename = strdup("summary.xml");
+
+ if (_clar.write_summary &&
+ !(_clar.summary = clar_summary_init(_clar.summary_filename))) {
+ clar_print_onabort("Failed to open the summary file\n");
+ exit(-1);
+ }
+
+ if (clar_sandbox() < 0) {
+ clar_print_onabort("Failed to sandbox the test runner.\n");
+ exit(-1);
+ }
+}
+
+int
+clar_test_run(void)
+{
+ size_t i;
+ struct clar_explicit *explicit;
+
+ if (_clar.explicit) {
+ for (explicit = _clar.explicit; explicit; explicit = explicit->next)
+ clar_run_suite(&_clar_suites[explicit->suite_idx], explicit->filter);
+ } else {
+ for (i = 0; i < _clar_suite_count; ++i)
+ clar_run_suite(&_clar_suites[i], NULL);
+ }
+
+ return _clar.total_errors;
+}
+
+void
+clar_test_shutdown(void)
+{
+ struct clar_explicit *explicit, *explicit_next;
+ struct clar_report *report, *report_next;
+
+ clar_print_shutdown(
+ _clar.tests_ran,
+ (int)_clar_suite_count,
+ _clar.total_errors
+ );
+
+ clar_unsandbox();
+
+ if (_clar.write_summary && clar_summary_shutdown(_clar.summary) < 0) {
+ clar_print_onabort("Failed to write the summary file\n");
+ exit(-1);
+ }
+
+ for (explicit = _clar.explicit; explicit; explicit = explicit_next) {
+ explicit_next = explicit->next;
+ free(explicit);
+ }
+
+ for (report = _clar.reports; report; report = report_next) {
+ report_next = report->next;
+ free(report);
+ }
+
+ free(_clar.summary_filename);
+}
+
+int
+clar_test(int argc, char **argv)
+{
+ int errors;
+
+ clar_test_init(argc, argv);
+ errors = clar_test_run();
+ clar_test_shutdown();
+
+ return errors;
+}
+
+static void abort_test(void)
+{
+ if (!_clar.trampoline_enabled) {
+ clar_print_onabort(
+ "Fatal error: a cleanup method raised an exception.");
+ clar_report_errors(_clar.last_report);
+ exit(-1);
+ }
+
+ CL_TRACE(CL_TRACE__TEST__LONGJMP);
+ longjmp(_clar.trampoline, -1);
+}
+
+void clar__skip(void)
+{
+ _clar.last_report->status = CL_TEST_SKIP;
+ _clar.total_skipped++;
+ abort_test();
+}
+
+void clar__fail(
+ const char *file,
+ const char *function,
+ size_t line,
+ const char *error_msg,
+ const char *description,
+ int should_abort)
+{
+ struct clar_error *error = calloc(1, sizeof(struct clar_error));
+
+ if (_clar.last_report->errors == NULL)
+ _clar.last_report->errors = error;
+
+ if (_clar.last_report->last_error != NULL)
+ _clar.last_report->last_error->next = error;
+
+ _clar.last_report->last_error = error;
+
+ error->file = file;
+ error->function = function;
+ error->line_number = line;
+ error->error_msg = error_msg;
+
+ if (description != NULL)
+ error->description = strdup(description);
+
+ _clar.total_errors++;
+ _clar.last_report->status = CL_TEST_FAILURE;
+
+ if (should_abort)
+ abort_test();
+}
+
+void clar__assert(
+ int condition,
+ const char *file,
+ const char *function,
+ size_t line,
+ const char *error_msg,
+ const char *description,
+ int should_abort)
+{
+ if (condition)
+ return;
+
+ clar__fail(file, function, line, error_msg, description, should_abort);
+}
+
+void clar__assert_equal(
+ const char *file,
+ const char *function,
+ size_t line,
+ const char *err,
+ int should_abort,
+ const char *fmt,
+ ...)
+{
+ va_list args;
+ char buf[4096];
+ int is_equal = 1;
+
+ va_start(args, fmt);
+
+ if (!strcmp("%s", fmt)) {
+ const char *s1 = va_arg(args, const char *);
+ const char *s2 = va_arg(args, const char *);
+ is_equal = (!s1 || !s2) ? (s1 == s2) : !strcmp(s1, s2);
+
+ if (!is_equal) {
+ if (s1 && s2) {
+ int pos;
+ for (pos = 0; s1[pos] == s2[pos] && s1[pos] && s2[pos]; ++pos)
+ /* find differing byte offset */;
+ p_snprintf(buf, sizeof(buf), "'%s' != '%s' (at byte %d)",
+ s1, s2, pos);
+ } else {
+ p_snprintf(buf, sizeof(buf), "'%s' != '%s'", s1, s2);
+ }
+ }
+ }
+ else if(!strcmp("%.*s", fmt)) {
+ const char *s1 = va_arg(args, const char *);
+ const char *s2 = va_arg(args, const char *);
+ int len = va_arg(args, int);
+ is_equal = (!s1 || !s2) ? (s1 == s2) : !strncmp(s1, s2, len);
+
+ if (!is_equal) {
+ if (s1 && s2) {
+ int pos;
+ for (pos = 0; s1[pos] == s2[pos] && pos < len; ++pos)
+ /* find differing byte offset */;
+ p_snprintf(buf, sizeof(buf), "'%.*s' != '%.*s' (at byte %d)",
+ len, s1, len, s2, pos);
+ } else {
+ p_snprintf(buf, sizeof(buf), "'%.*s' != '%.*s'", len, s1, len, s2);
+ }
+ }
+ }
+ else if (!strcmp("%ls", fmt)) {
+ const wchar_t *wcs1 = va_arg(args, const wchar_t *);
+ const wchar_t *wcs2 = va_arg(args, const wchar_t *);
+ is_equal = (!wcs1 || !wcs2) ? (wcs1 == wcs2) : !wcscmp(wcs1, wcs2);
+
+ if (!is_equal) {
+ if (wcs1 && wcs2) {
+ int pos;
+ for (pos = 0; wcs1[pos] == wcs2[pos] && wcs1[pos] && wcs2[pos]; ++pos)
+ /* find differing byte offset */;
+ p_snprintf(buf, sizeof(buf), "'%ls' != '%ls' (at byte %d)",
+ wcs1, wcs2, pos);
+ } else {
+ p_snprintf(buf, sizeof(buf), "'%ls' != '%ls'", wcs1, wcs2);
+ }
+ }
+ }
+ else if(!strcmp("%.*ls", fmt)) {
+ const wchar_t *wcs1 = va_arg(args, const wchar_t *);
+ const wchar_t *wcs2 = va_arg(args, const wchar_t *);
+ int len = va_arg(args, int);
+ is_equal = (!wcs1 || !wcs2) ? (wcs1 == wcs2) : !wcsncmp(wcs1, wcs2, len);
+
+ if (!is_equal) {
+ if (wcs1 && wcs2) {
+ int pos;
+ for (pos = 0; wcs1[pos] == wcs2[pos] && pos < len; ++pos)
+ /* find differing byte offset */;
+ p_snprintf(buf, sizeof(buf), "'%.*ls' != '%.*ls' (at byte %d)",
+ len, wcs1, len, wcs2, pos);
+ } else {
+ p_snprintf(buf, sizeof(buf), "'%.*ls' != '%.*ls'", len, wcs1, len, wcs2);
+ }
+ }
+ }
+ else if (!strcmp("%"PRIuZ, fmt) || !strcmp("%"PRIxZ, fmt)) {
+ size_t sz1 = va_arg(args, size_t), sz2 = va_arg(args, size_t);
+ is_equal = (sz1 == sz2);
+ if (!is_equal) {
+ int offset = p_snprintf(buf, sizeof(buf), fmt, sz1);
+ strncat(buf, " != ", sizeof(buf) - offset);
+ p_snprintf(buf + offset + 4, sizeof(buf) - offset - 4, fmt, sz2);
+ }
+ }
+ else if (!strcmp("%p", fmt)) {
+ void *p1 = va_arg(args, void *), *p2 = va_arg(args, void *);
+ is_equal = (p1 == p2);
+ if (!is_equal)
+ p_snprintf(buf, sizeof(buf), "%p != %p", p1, p2);
+ }
+ else {
+ int i1 = va_arg(args, int), i2 = va_arg(args, int);
+ is_equal = (i1 == i2);
+ if (!is_equal) {
+ int offset = p_snprintf(buf, sizeof(buf), fmt, i1);
+ strncat(buf, " != ", sizeof(buf) - offset);
+ p_snprintf(buf + offset + 4, sizeof(buf) - offset - 4, fmt, i2);
+ }
+ }
+
+ va_end(args);
+
+ if (!is_equal)
+ clar__fail(file, function, line, err, buf, should_abort);
+}
+
+void cl_set_cleanup(void (*cleanup)(void *), void *opaque)
+{
+ _clar.local_cleanup = cleanup;
+ _clar.local_cleanup_payload = opaque;
+}
+
+#include "clar/sandbox.h"
+#include "clar/fixtures.h"
+#include "clar/fs.h"
+#include "clar/print.h"
+#include "clar/summary.h"
diff --git a/tests/clar/clar.h b/tests/clar/clar.h
new file mode 100644
index 0000000..8c22382
--- /dev/null
+++ b/tests/clar/clar.h
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) Vicent Marti. All rights reserved.
+ *
+ * This file is part of clar, distributed under the ISC license.
+ * For full terms see the included COPYING file.
+ */
+#ifndef __CLAR_TEST_H__
+#define __CLAR_TEST_H__
+
+#include <stdlib.h>
+
+enum cl_test_status {
+ CL_TEST_OK,
+ CL_TEST_FAILURE,
+ CL_TEST_SKIP,
+ CL_TEST_NOTRUN,
+};
+
+enum cl_output_format {
+ CL_OUTPUT_CLAP,
+ CL_OUTPUT_TAP,
+};
+
+/** Setup clar environment */
+void clar_test_init(int argc, char *argv[]);
+int clar_test_run(void);
+void clar_test_shutdown(void);
+
+/** One shot setup & run */
+int clar_test(int argc, char *argv[]);
+
+const char *clar_sandbox_path(void);
+
+void cl_set_cleanup(void (*cleanup)(void *), void *opaque);
+void cl_fs_cleanup(void);
+
+/**
+ * cl_trace_* is a hook to provide a simple global tracing
+ * mechanism.
+ *
+ * The goal here is to let main() provide clar-proper
+ * with a callback to optionally write log info for
+ * test operations into the same stream used by their
+ * actual tests. This would let them print test names
+ * and maybe performance data as they choose.
+ *
+ * The goal is NOT to alter the flow of control or to
+ * override test selection/skipping. (So the callback
+ * does not return a value.)
+ *
+ * The goal is NOT to duplicate the existing
+ * pass/fail/skip reporting. (So the callback
+ * does not accept a status/errorcode argument.)
+ *
+ */
+typedef enum cl_trace_event {
+ CL_TRACE__SUITE_BEGIN,
+ CL_TRACE__SUITE_END,
+ CL_TRACE__TEST__BEGIN,
+ CL_TRACE__TEST__END,
+ CL_TRACE__TEST__RUN_BEGIN,
+ CL_TRACE__TEST__RUN_END,
+ CL_TRACE__TEST__LONGJMP,
+} cl_trace_event;
+
+typedef void (cl_trace_cb)(
+ cl_trace_event ev,
+ const char *suite_name,
+ const char *test_name,
+ void *payload);
+
+/**
+ * Register a callback into CLAR to send global trace events.
+ * Pass NULL to disable.
+ */
+void cl_trace_register(cl_trace_cb *cb, void *payload);
+
+
+#ifdef CLAR_FIXTURE_PATH
+const char *cl_fixture(const char *fixture_name);
+void cl_fixture_sandbox(const char *fixture_name);
+void cl_fixture_cleanup(const char *fixture_name);
+const char *cl_fixture_basename(const char *fixture_name);
+#endif
+
+/**
+ * Assertion macros with explicit error message
+ */
+#define cl_must_pass_(expr, desc) clar__assert((expr) >= 0, __FILE__, __func__, __LINE__, "Function call failed: " #expr, desc, 1)
+#define cl_must_fail_(expr, desc) clar__assert((expr) < 0, __FILE__, __func__, __LINE__, "Expected function call to fail: " #expr, desc, 1)
+#define cl_assert_(expr, desc) clar__assert((expr) != 0, __FILE__, __func__, __LINE__, "Expression is not true: " #expr, desc, 1)
+
+/**
+ * Check macros with explicit error message
+ */
+#define cl_check_pass_(expr, desc) clar__assert((expr) >= 0, __FILE__, __func__, __LINE__, "Function call failed: " #expr, desc, 0)
+#define cl_check_fail_(expr, desc) clar__assert((expr) < 0, __FILE__, __func__, __LINE__, "Expected function call to fail: " #expr, desc, 0)
+#define cl_check_(expr, desc) clar__assert((expr) != 0, __FILE__, __func__, __LINE__, "Expression is not true: " #expr, desc, 0)
+
+/**
+ * Assertion macros with no error message
+ */
+#define cl_must_pass(expr) cl_must_pass_(expr, NULL)
+#define cl_must_fail(expr) cl_must_fail_(expr, NULL)
+#define cl_assert(expr) cl_assert_(expr, NULL)
+
+/**
+ * Check macros with no error message
+ */
+#define cl_check_pass(expr) cl_check_pass_(expr, NULL)
+#define cl_check_fail(expr) cl_check_fail_(expr, NULL)
+#define cl_check(expr) cl_check_(expr, NULL)
+
+/**
+ * Forced failure/warning
+ */
+#define cl_fail(desc) clar__fail(__FILE__, __func__, __LINE__, "Test failed.", desc, 1)
+#define cl_warning(desc) clar__fail(__FILE__, __func__, __LINE__, "Warning during test execution:", desc, 0)
+
+#define cl_skip() clar__skip()
+
+/**
+ * Typed assertion macros
+ */
+#define cl_assert_equal_s(s1,s2) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #s1 " != " #s2, 1, "%s", (s1), (s2))
+#define cl_assert_equal_s_(s1,s2,note) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%s", (s1), (s2))
+
+#define cl_assert_equal_wcs(wcs1,wcs2) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2, 1, "%ls", (wcs1), (wcs2))
+#define cl_assert_equal_wcs_(wcs1,wcs2,note) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2 " (" #note ")", 1, "%ls", (wcs1), (wcs2))
+
+#define cl_assert_equal_strn(s1,s2,len) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #s1 " != " #s2, 1, "%.*s", (s1), (s2), (int)(len))
+#define cl_assert_equal_strn_(s1,s2,len,note) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #s1 " != " #s2 " (" #note ")", 1, "%.*s", (s1), (s2), (int)(len))
+
+#define cl_assert_equal_wcsn(wcs1,wcs2,len) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2, 1, "%.*ls", (wcs1), (wcs2), (int)(len))
+#define cl_assert_equal_wcsn_(wcs1,wcs2,len,note) clar__assert_equal(__FILE__,__func__,__LINE__,"String mismatch: " #wcs1 " != " #wcs2 " (" #note ")", 1, "%.*ls", (wcs1), (wcs2), (int)(len))
+
+#define cl_assert_equal_i(i1,i2) clar__assert_equal(__FILE__,__func__,__LINE__,#i1 " != " #i2, 1, "%d", (int)(i1), (int)(i2))
+#define cl_assert_equal_i_(i1,i2,note) clar__assert_equal(__FILE__,__func__,__LINE__,#i1 " != " #i2 " (" #note ")", 1, "%d", (i1), (i2))
+#define cl_assert_equal_i_fmt(i1,i2,fmt) clar__assert_equal(__FILE__,__func__,__LINE__,#i1 " != " #i2, 1, (fmt), (int)(i1), (int)(i2))
+
+#define cl_assert_equal_b(b1,b2) clar__assert_equal(__FILE__,__func__,__LINE__,#b1 " != " #b2, 1, "%d", (int)((b1) != 0),(int)((b2) != 0))
+
+#define cl_assert_equal_p(p1,p2) clar__assert_equal(__FILE__,__func__,__LINE__,"Pointer mismatch: " #p1 " != " #p2, 1, "%p", (p1), (p2))
+
+void clar__skip(void);
+
+void clar__fail(
+ const char *file,
+ const char *func,
+ size_t line,
+ const char *error,
+ const char *description,
+ int should_abort);
+
+void clar__assert(
+ int condition,
+ const char *file,
+ const char *func,
+ size_t line,
+ const char *error,
+ const char *description,
+ int should_abort);
+
+void clar__assert_equal(
+ const char *file,
+ const char *func,
+ size_t line,
+ const char *err,
+ int should_abort,
+ const char *fmt,
+ ...);
+
+#endif
diff --git a/tests/clar/clar/fixtures.h b/tests/clar/clar/fixtures.h
new file mode 100644
index 0000000..77033d3
--- /dev/null
+++ b/tests/clar/clar/fixtures.h
@@ -0,0 +1,50 @@
+static const char *
+fixture_path(const char *base, const char *fixture_name)
+{
+ static char _path[4096];
+ size_t root_len;
+
+ root_len = strlen(base);
+ strncpy(_path, base, sizeof(_path));
+
+ if (_path[root_len - 1] != '/')
+ _path[root_len++] = '/';
+
+ if (fixture_name[0] == '/')
+ fixture_name++;
+
+ strncpy(_path + root_len,
+ fixture_name,
+ sizeof(_path) - root_len);
+
+ return _path;
+}
+
+#ifdef CLAR_FIXTURE_PATH
+const char *cl_fixture(const char *fixture_name)
+{
+ return fixture_path(CLAR_FIXTURE_PATH, fixture_name);
+}
+
+void cl_fixture_sandbox(const char *fixture_name)
+{
+ fs_copy(cl_fixture(fixture_name), _clar_path);
+}
+
+const char *cl_fixture_basename(const char *fixture_name)
+{
+ const char *p;
+
+ for (p = fixture_name; *p; p++) {
+ if (p[0] == '/' && p[1] && p[1] != '/')
+ fixture_name = p+1;
+ }
+
+ return fixture_name;
+}
+
+void cl_fixture_cleanup(const char *fixture_name)
+{
+ fs_rm(fixture_path(_clar_path, cl_fixture_basename(fixture_name)));
+}
+#endif
diff --git a/tests/clar/clar/fs.h b/tests/clar/clar/fs.h
new file mode 100644
index 0000000..44ede45
--- /dev/null
+++ b/tests/clar/clar/fs.h
@@ -0,0 +1,520 @@
+/*
+ * By default, use a read/write loop to copy files on POSIX systems.
+ * On Linux, use sendfile by default as it's slightly faster. On
+ * macOS, we avoid fcopyfile by default because it's slightly slower.
+ */
+#undef USE_FCOPYFILE
+#define USE_SENDFILE 1
+
+#ifdef _WIN32
+
+#ifdef CLAR_WIN32_LONGPATHS
+# define CLAR_MAX_PATH 4096
+#else
+# define CLAR_MAX_PATH MAX_PATH
+#endif
+
+#define RM_RETRY_COUNT 5
+#define RM_RETRY_DELAY 10
+
+#ifdef __MINGW32__
+
+/* These security-enhanced functions are not available
+ * in MinGW, so just use the vanilla ones */
+#define wcscpy_s(a, b, c) wcscpy((a), (c))
+#define wcscat_s(a, b, c) wcscat((a), (c))
+
+#endif /* __MINGW32__ */
+
+static int
+fs__dotordotdot(WCHAR *_tocheck)
+{
+ return _tocheck[0] == '.' &&
+ (_tocheck[1] == '\0' ||
+ (_tocheck[1] == '.' && _tocheck[2] == '\0'));
+}
+
+static int
+fs_rmdir_rmdir(WCHAR *_wpath)
+{
+ unsigned retries = 1;
+
+ while (!RemoveDirectoryW(_wpath)) {
+ /* Only retry when we have retries remaining, and the
+ * error was ERROR_DIR_NOT_EMPTY. */
+ if (retries++ > RM_RETRY_COUNT ||
+ ERROR_DIR_NOT_EMPTY != GetLastError())
+ return -1;
+
+ /* Give whatever has a handle to a child item some time
+ * to release it before trying again */
+ Sleep(RM_RETRY_DELAY * retries * retries);
+ }
+
+ return 0;
+}
+
+static void translate_path(WCHAR *path, size_t path_size)
+{
+ size_t path_len, i;
+
+ if (wcsncmp(path, L"\\\\?\\", 4) == 0)
+ return;
+
+ path_len = wcslen(path);
+ cl_assert(path_size > path_len + 4);
+
+ for (i = path_len; i > 0; i--) {
+ WCHAR c = path[i - 1];
+
+ if (c == L'/')
+ path[i + 3] = L'\\';
+ else
+ path[i + 3] = path[i - 1];
+ }
+
+ path[0] = L'\\';
+ path[1] = L'\\';
+ path[2] = L'?';
+ path[3] = L'\\';
+ path[path_len + 4] = L'\0';
+}
+
+static void
+fs_rmdir_helper(WCHAR *_wsource)
+{
+ WCHAR buffer[CLAR_MAX_PATH];
+ HANDLE find_handle;
+ WIN32_FIND_DATAW find_data;
+ size_t buffer_prefix_len;
+
+ /* Set up the buffer and capture the length */
+ wcscpy_s(buffer, CLAR_MAX_PATH, _wsource);
+ translate_path(buffer, CLAR_MAX_PATH);
+ wcscat_s(buffer, CLAR_MAX_PATH, L"\\");
+ buffer_prefix_len = wcslen(buffer);
+
+ /* FindFirstFile needs a wildcard to match multiple items */
+ wcscat_s(buffer, CLAR_MAX_PATH, L"*");
+ find_handle = FindFirstFileW(buffer, &find_data);
+ cl_assert(INVALID_HANDLE_VALUE != find_handle);
+
+ do {
+ /* FindFirstFile/FindNextFile gives back . and ..
+ * entries at the beginning */
+ if (fs__dotordotdot(find_data.cFileName))
+ continue;
+
+ wcscpy_s(buffer + buffer_prefix_len, CLAR_MAX_PATH - buffer_prefix_len, find_data.cFileName);
+
+ if (FILE_ATTRIBUTE_DIRECTORY & find_data.dwFileAttributes)
+ fs_rmdir_helper(buffer);
+ else {
+ /* If set, the +R bit must be cleared before deleting */
+ if (FILE_ATTRIBUTE_READONLY & find_data.dwFileAttributes)
+ cl_assert(SetFileAttributesW(buffer, find_data.dwFileAttributes & ~FILE_ATTRIBUTE_READONLY));
+
+ cl_assert(DeleteFileW(buffer));
+ }
+ }
+ while (FindNextFileW(find_handle, &find_data));
+
+ /* Ensure that we successfully completed the enumeration */
+ cl_assert(ERROR_NO_MORE_FILES == GetLastError());
+
+ /* Close the find handle */
+ FindClose(find_handle);
+
+ /* Now that the directory is empty, remove it */
+ cl_assert(0 == fs_rmdir_rmdir(_wsource));
+}
+
+static int
+fs_rm_wait(WCHAR *_wpath)
+{
+ unsigned retries = 1;
+ DWORD last_error;
+
+ do {
+ if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(_wpath))
+ last_error = GetLastError();
+ else
+ last_error = ERROR_SUCCESS;
+
+ /* Is the item gone? */
+ if (ERROR_FILE_NOT_FOUND == last_error ||
+ ERROR_PATH_NOT_FOUND == last_error)
+ return 0;
+
+ Sleep(RM_RETRY_DELAY * retries * retries);
+ }
+ while (retries++ <= RM_RETRY_COUNT);
+
+ return -1;
+}
+
+static void
+fs_rm(const char *_source)
+{
+ WCHAR wsource[CLAR_MAX_PATH];
+ DWORD attrs;
+
+ /* The input path is UTF-8. Convert it to wide characters
+ * for use with the Windows API */
+ cl_assert(MultiByteToWideChar(CP_UTF8,
+ MB_ERR_INVALID_CHARS,
+ _source,
+ -1, /* Indicates NULL termination */
+ wsource,
+ CLAR_MAX_PATH));
+
+ translate_path(wsource, CLAR_MAX_PATH);
+
+ /* Does the item exist? If not, we have no work to do */
+ attrs = GetFileAttributesW(wsource);
+
+ if (INVALID_FILE_ATTRIBUTES == attrs)
+ return;
+
+ if (FILE_ATTRIBUTE_DIRECTORY & attrs)
+ fs_rmdir_helper(wsource);
+ else {
+ /* The item is a file. Strip the +R bit */
+ if (FILE_ATTRIBUTE_READONLY & attrs)
+ cl_assert(SetFileAttributesW(wsource, attrs & ~FILE_ATTRIBUTE_READONLY));
+
+ cl_assert(DeleteFileW(wsource));
+ }
+
+ /* Wait for the DeleteFile or RemoveDirectory call to complete */
+ cl_assert(0 == fs_rm_wait(wsource));
+}
+
+static void
+fs_copydir_helper(WCHAR *_wsource, WCHAR *_wdest)
+{
+ WCHAR buf_source[CLAR_MAX_PATH], buf_dest[CLAR_MAX_PATH];
+ HANDLE find_handle;
+ WIN32_FIND_DATAW find_data;
+ size_t buf_source_prefix_len, buf_dest_prefix_len;
+
+ wcscpy_s(buf_source, CLAR_MAX_PATH, _wsource);
+ wcscat_s(buf_source, CLAR_MAX_PATH, L"\\");
+ translate_path(buf_source, CLAR_MAX_PATH);
+ buf_source_prefix_len = wcslen(buf_source);
+
+ wcscpy_s(buf_dest, CLAR_MAX_PATH, _wdest);
+ wcscat_s(buf_dest, CLAR_MAX_PATH, L"\\");
+ translate_path(buf_dest, CLAR_MAX_PATH);
+ buf_dest_prefix_len = wcslen(buf_dest);
+
+ /* Get an enumerator for the items in the source. */
+ wcscat_s(buf_source, CLAR_MAX_PATH, L"*");
+ find_handle = FindFirstFileW(buf_source, &find_data);
+ cl_assert(INVALID_HANDLE_VALUE != find_handle);
+
+ /* Create the target directory. */
+ cl_assert(CreateDirectoryW(_wdest, NULL));
+
+ do {
+ /* FindFirstFile/FindNextFile gives back . and ..
+ * entries at the beginning */
+ if (fs__dotordotdot(find_data.cFileName))
+ continue;
+
+ wcscpy_s(buf_source + buf_source_prefix_len, CLAR_MAX_PATH - buf_source_prefix_len, find_data.cFileName);
+ wcscpy_s(buf_dest + buf_dest_prefix_len, CLAR_MAX_PATH - buf_dest_prefix_len, find_data.cFileName);
+
+ if (FILE_ATTRIBUTE_DIRECTORY & find_data.dwFileAttributes)
+ fs_copydir_helper(buf_source, buf_dest);
+ else
+ cl_assert(CopyFileW(buf_source, buf_dest, TRUE));
+ }
+ while (FindNextFileW(find_handle, &find_data));
+
+ /* Ensure that we successfully completed the enumeration */
+ cl_assert(ERROR_NO_MORE_FILES == GetLastError());
+
+ /* Close the find handle */
+ FindClose(find_handle);
+}
+
+static void
+fs_copy(const char *_source, const char *_dest)
+{
+ WCHAR wsource[CLAR_MAX_PATH], wdest[CLAR_MAX_PATH];
+ DWORD source_attrs, dest_attrs;
+ HANDLE find_handle;
+ WIN32_FIND_DATAW find_data;
+
+ /* The input paths are UTF-8. Convert them to wide characters
+ * for use with the Windows API. */
+ cl_assert(MultiByteToWideChar(CP_UTF8,
+ MB_ERR_INVALID_CHARS,
+ _source,
+ -1,
+ wsource,
+ CLAR_MAX_PATH));
+
+ cl_assert(MultiByteToWideChar(CP_UTF8,
+ MB_ERR_INVALID_CHARS,
+ _dest,
+ -1,
+ wdest,
+ CLAR_MAX_PATH));
+
+ translate_path(wsource, CLAR_MAX_PATH);
+ translate_path(wdest, CLAR_MAX_PATH);
+
+ /* Check the source for existence */
+ source_attrs = GetFileAttributesW(wsource);
+ cl_assert(INVALID_FILE_ATTRIBUTES != source_attrs);
+
+ /* Check the target for existence */
+ dest_attrs = GetFileAttributesW(wdest);
+
+ if (INVALID_FILE_ATTRIBUTES != dest_attrs) {
+ /* Target exists; append last path part of source to target.
+ * Use FindFirstFile to parse the path */
+ find_handle = FindFirstFileW(wsource, &find_data);
+ cl_assert(INVALID_HANDLE_VALUE != find_handle);
+ wcscat_s(wdest, CLAR_MAX_PATH, L"\\");
+ wcscat_s(wdest, CLAR_MAX_PATH, find_data.cFileName);
+ FindClose(find_handle);
+
+ /* Check the new target for existence */
+ cl_assert(INVALID_FILE_ATTRIBUTES == GetFileAttributesW(wdest));
+ }
+
+ if (FILE_ATTRIBUTE_DIRECTORY & source_attrs)
+ fs_copydir_helper(wsource, wdest);
+ else
+ cl_assert(CopyFileW(wsource, wdest, TRUE));
+}
+
+void
+cl_fs_cleanup(void)
+{
+ fs_rm(fixture_path(_clar_path, "*"));
+}
+
+#else
+
+#include <errno.h>
+#include <string.h>
+#include <limits.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#if defined(__linux__)
+# include <sys/sendfile.h>
+#endif
+
+#if defined(__APPLE__)
+# include <copyfile.h>
+#endif
+
+static void basename_r(const char **out, int *out_len, const char *in)
+{
+ size_t in_len = strlen(in), start_pos;
+
+ for (in_len = strlen(in); in_len; in_len--) {
+ if (in[in_len - 1] != '/')
+ break;
+ }
+
+ for (start_pos = in_len; start_pos; start_pos--) {
+ if (in[start_pos - 1] == '/')
+ break;
+ }
+
+ cl_assert(in_len - start_pos < INT_MAX);
+
+ if (in_len - start_pos > 0) {
+ *out = &in[start_pos];
+ *out_len = (in_len - start_pos);
+ } else {
+ *out = "/";
+ *out_len = 1;
+ }
+}
+
+static char *joinpath(const char *dir, const char *base, int base_len)
+{
+ char *out;
+ int len;
+
+ if (base_len == -1) {
+ size_t bl = strlen(base);
+
+ cl_assert(bl < INT_MAX);
+ base_len = (int)bl;
+ }
+
+ len = strlen(dir) + base_len + 2;
+ cl_assert(len > 0);
+
+ cl_assert(out = malloc(len));
+ cl_assert(snprintf(out, len, "%s/%.*s", dir, base_len, base) < len);
+
+ return out;
+}
+
+static void
+fs_copydir_helper(const char *source, const char *dest, int dest_mode)
+{
+ DIR *source_dir;
+ struct dirent *d;
+
+ mkdir(dest, dest_mode);
+
+ cl_assert_(source_dir = opendir(source), "Could not open source dir");
+ while ((d = (errno = 0, readdir(source_dir))) != NULL) {
+ char *child;
+
+ if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+ continue;
+
+ child = joinpath(source, d->d_name, -1);
+ fs_copy(child, dest);
+ free(child);
+ }
+
+ cl_assert_(errno == 0, "Failed to iterate source dir");
+
+ closedir(source_dir);
+}
+
+static void
+fs_copyfile_helper(const char *source, size_t source_len, const char *dest, int dest_mode)
+{
+ int in, out;
+
+ cl_must_pass((in = open(source, O_RDONLY)));
+ cl_must_pass((out = open(dest, O_WRONLY|O_CREAT|O_TRUNC, dest_mode)));
+
+#if USE_FCOPYFILE && defined(__APPLE__)
+ ((void)(source_len)); /* unused */
+ cl_must_pass(fcopyfile(in, out, 0, COPYFILE_DATA));
+#elif USE_SENDFILE && defined(__linux__)
+ {
+ ssize_t ret = 0;
+
+ while (source_len && (ret = sendfile(out, in, NULL, source_len)) > 0) {
+ source_len -= (size_t)ret;
+ }
+ cl_assert(ret >= 0);
+ }
+#else
+ {
+ char buf[131072];
+ ssize_t ret;
+
+ ((void)(source_len)); /* unused */
+
+ while ((ret = read(in, buf, sizeof(buf))) > 0) {
+ size_t len = (size_t)ret;
+
+ while (len && (ret = write(out, buf, len)) > 0) {
+ cl_assert(ret <= (ssize_t)len);
+ len -= ret;
+ }
+ cl_assert(ret >= 0);
+ }
+ cl_assert(ret == 0);
+ }
+#endif
+
+ close(in);
+ close(out);
+}
+
+static void
+fs_copy(const char *source, const char *_dest)
+{
+ char *dbuf = NULL;
+ const char *dest = NULL;
+ struct stat source_st, dest_st;
+
+ cl_must_pass_(lstat(source, &source_st), "Failed to stat copy source");
+
+ if (lstat(_dest, &dest_st) == 0) {
+ const char *base;
+ int base_len;
+
+ /* Target exists and is directory; append basename */
+ cl_assert(S_ISDIR(dest_st.st_mode));
+
+ basename_r(&base, &base_len, source);
+ cl_assert(base_len < INT_MAX);
+
+ dbuf = joinpath(_dest, base, base_len);
+ dest = dbuf;
+ } else if (errno != ENOENT) {
+ cl_fail("Cannot copy; cannot stat destination");
+ } else {
+ dest = _dest;
+ }
+
+ if (S_ISDIR(source_st.st_mode)) {
+ fs_copydir_helper(source, dest, source_st.st_mode);
+ } else {
+ fs_copyfile_helper(source, source_st.st_size, dest, source_st.st_mode);
+ }
+
+ free(dbuf);
+}
+
+static void
+fs_rmdir_helper(const char *path)
+{
+ DIR *dir;
+ struct dirent *d;
+
+ cl_assert_(dir = opendir(path), "Could not open dir");
+ while ((d = (errno = 0, readdir(dir))) != NULL) {
+ char *child;
+
+ if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
+ continue;
+
+ child = joinpath(path, d->d_name, -1);
+ fs_rm(child);
+ free(child);
+ }
+
+ cl_assert_(errno == 0, "Failed to iterate source dir");
+ closedir(dir);
+
+ cl_must_pass_(rmdir(path), "Could not remove directory");
+}
+
+static void
+fs_rm(const char *path)
+{
+ struct stat st;
+
+ if (lstat(path, &st)) {
+ if (errno == ENOENT)
+ return;
+
+ cl_fail("Cannot copy; cannot stat destination");
+ }
+
+ if (S_ISDIR(st.st_mode)) {
+ fs_rmdir_helper(path);
+ } else {
+ cl_must_pass(unlink(path));
+ }
+}
+
+void
+cl_fs_cleanup(void)
+{
+ clar_unsandbox();
+ clar_sandbox();
+}
+#endif
diff --git a/tests/clar/clar/print.h b/tests/clar/clar/print.h
new file mode 100644
index 0000000..c17e2f6
--- /dev/null
+++ b/tests/clar/clar/print.h
@@ -0,0 +1,211 @@
+/* clap: clar protocol, the traditional clar output format */
+
+static void clar_print_clap_init(int test_count, int suite_count, const char *suite_names)
+{
+ (void)test_count;
+ printf("Loaded %d suites: %s\n", (int)suite_count, suite_names);
+ printf("Started (test status codes: OK='.' FAILURE='F' SKIPPED='S')\n");
+}
+
+static void clar_print_clap_shutdown(int test_count, int suite_count, int error_count)
+{
+ (void)test_count;
+ (void)suite_count;
+ (void)error_count;
+
+ printf("\n\n");
+ clar_report_all();
+}
+
+static void clar_print_clap_error(int num, const struct clar_report *report, const struct clar_error *error)
+{
+ printf(" %d) Failure:\n", num);
+
+ printf("%s::%s [%s:%"PRIuZ"]\n",
+ report->suite,
+ report->test,
+ error->file,
+ error->line_number);
+
+ printf(" %s\n", error->error_msg);
+
+ if (error->description != NULL)
+ printf(" %s\n", error->description);
+
+ printf("\n");
+ fflush(stdout);
+}
+
+static void clar_print_clap_ontest(const char *suite_name, const char *test_name, int test_number, enum cl_test_status status)
+{
+ (void)test_name;
+ (void)test_number;
+
+ if (_clar.verbosity > 1) {
+ printf("%s::%s: ", suite_name, test_name);
+
+ switch (status) {
+ case CL_TEST_OK: printf("ok\n"); break;
+ case CL_TEST_FAILURE: printf("fail\n"); break;
+ case CL_TEST_SKIP: printf("skipped"); break;
+ case CL_TEST_NOTRUN: printf("notrun"); break;
+ }
+ } else {
+ switch (status) {
+ case CL_TEST_OK: printf("."); break;
+ case CL_TEST_FAILURE: printf("F"); break;
+ case CL_TEST_SKIP: printf("S"); break;
+ case CL_TEST_NOTRUN: printf("N"); break;
+ }
+
+ fflush(stdout);
+ }
+}
+
+static void clar_print_clap_onsuite(const char *suite_name, int suite_index)
+{
+ if (_clar.verbosity == 1)
+ printf("\n%s", suite_name);
+
+ (void)suite_index;
+}
+
+static void clar_print_clap_onabort(const char *fmt, va_list arg)
+{
+ vfprintf(stderr, fmt, arg);
+}
+
+/* tap: test anywhere protocol format */
+
+static void clar_print_tap_init(int test_count, int suite_count, const char *suite_names)
+{
+ (void)test_count;
+ (void)suite_count;
+ (void)suite_names;
+ printf("TAP version 13\n");
+}
+
+static void clar_print_tap_shutdown(int test_count, int suite_count, int error_count)
+{
+ (void)suite_count;
+ (void)error_count;
+
+ printf("1..%d\n", test_count);
+}
+
+static void clar_print_tap_error(int num, const struct clar_report *report, const struct clar_error *error)
+{
+ (void)num;
+ (void)report;
+ (void)error;
+}
+
+static void print_escaped(const char *str)
+{
+ char *c;
+
+ while ((c = strchr(str, '\'')) != NULL) {
+ printf("%.*s", (int)(c - str), str);
+ printf("''");
+ str = c + 1;
+ }
+
+ printf("%s", str);
+}
+
+static void clar_print_tap_ontest(const char *suite_name, const char *test_name, int test_number, enum cl_test_status status)
+{
+ const struct clar_error *error = _clar.last_report->errors;
+
+ (void)test_name;
+ (void)test_number;
+
+ switch(status) {
+ case CL_TEST_OK:
+ printf("ok %d - %s::%s\n", test_number, suite_name, test_name);
+ break;
+ case CL_TEST_FAILURE:
+ printf("not ok %d - %s::%s\n", test_number, suite_name, test_name);
+
+ printf(" ---\n");
+ printf(" reason: |\n");
+ printf(" %s\n", error->error_msg);
+
+ if (error->description)
+ printf(" %s\n", error->description);
+
+ printf(" at:\n");
+ printf(" file: '"); print_escaped(error->file); printf("'\n");
+ printf(" line: %" PRIuZ "\n", error->line_number);
+ printf(" function: '%s'\n", error->function);
+ printf(" ---\n");
+
+ break;
+ case CL_TEST_SKIP:
+ case CL_TEST_NOTRUN:
+ printf("ok %d - # SKIP %s::%s\n", test_number, suite_name, test_name);
+ break;
+ }
+
+ fflush(stdout);
+}
+
+static void clar_print_tap_onsuite(const char *suite_name, int suite_index)
+{
+ printf("# start of suite %d: %s\n", suite_index, suite_name);
+}
+
+static void clar_print_tap_onabort(const char *fmt, va_list arg)
+{
+ printf("Bail out! ");
+ vprintf(fmt, arg);
+ fflush(stdout);
+}
+
+/* indirection between protocol output selection */
+
+#define PRINT(FN, ...) do { \
+ switch (_clar.output_format) { \
+ case CL_OUTPUT_CLAP: \
+ clar_print_clap_##FN (__VA_ARGS__); \
+ break; \
+ case CL_OUTPUT_TAP: \
+ clar_print_tap_##FN (__VA_ARGS__); \
+ break; \
+ default: \
+ abort(); \
+ } \
+ } while (0)
+
+static void clar_print_init(int test_count, int suite_count, const char *suite_names)
+{
+ PRINT(init, test_count, suite_count, suite_names);
+}
+
+static void clar_print_shutdown(int test_count, int suite_count, int error_count)
+{
+ PRINT(shutdown, test_count, suite_count, error_count);
+}
+
+static void clar_print_error(int num, const struct clar_report *report, const struct clar_error *error)
+{
+ PRINT(error, num, report, error);
+}
+
+static void clar_print_ontest(const char *suite_name, const char *test_name, int test_number, enum cl_test_status status)
+{
+ PRINT(ontest, suite_name, test_name, test_number, status);
+}
+
+static void clar_print_onsuite(const char *suite_name, int suite_index)
+{
+ PRINT(onsuite, suite_name, suite_index);
+}
+
+static void clar_print_onabort(const char *msg, ...)
+{
+ va_list argp;
+ va_start(argp, msg);
+ PRINT(onabort, msg, argp);
+ va_end(argp);
+}
diff --git a/tests/clar/clar/sandbox.h b/tests/clar/clar/sandbox.h
new file mode 100644
index 0000000..0ba1479
--- /dev/null
+++ b/tests/clar/clar/sandbox.h
@@ -0,0 +1,154 @@
+#ifdef __APPLE__
+#include <sys/syslimits.h>
+#endif
+
+static char _clar_path[4096 + 1];
+
+static int
+is_valid_tmp_path(const char *path)
+{
+ STAT_T st;
+
+ if (stat(path, &st) != 0)
+ return 0;
+
+ if (!S_ISDIR(st.st_mode))
+ return 0;
+
+ return (access(path, W_OK) == 0);
+}
+
+static int
+find_tmp_path(char *buffer, size_t length)
+{
+#ifndef _WIN32
+ static const size_t var_count = 5;
+ static const char *env_vars[] = {
+ "CLAR_TMP", "TMPDIR", "TMP", "TEMP", "USERPROFILE"
+ };
+
+ size_t i;
+
+ for (i = 0; i < var_count; ++i) {
+ const char *env = getenv(env_vars[i]);
+ if (!env)
+ continue;
+
+ if (is_valid_tmp_path(env)) {
+#ifdef __APPLE__
+ if (length >= PATH_MAX && realpath(env, buffer) != NULL)
+ return 0;
+#endif
+ strncpy(buffer, env, length - 1);
+ buffer[length - 1] = '\0';
+ return 0;
+ }
+ }
+
+ /* If the environment doesn't say anything, try to use /tmp */
+ if (is_valid_tmp_path("/tmp")) {
+#ifdef __APPLE__
+ if (length >= PATH_MAX && realpath("/tmp", buffer) != NULL)
+ return 0;
+#endif
+ strncpy(buffer, "/tmp", length - 1);
+ buffer[length - 1] = '\0';
+ return 0;
+ }
+
+#else
+ DWORD env_len = GetEnvironmentVariable("CLAR_TMP", buffer, (DWORD)length);
+ if (env_len > 0 && env_len < (DWORD)length)
+ return 0;
+
+ if (GetTempPath((DWORD)length, buffer))
+ return 0;
+#endif
+
+ /* This system doesn't like us, try to use the current directory */
+ if (is_valid_tmp_path(".")) {
+ strncpy(buffer, ".", length - 1);
+ buffer[length - 1] = '\0';
+ return 0;
+ }
+
+ return -1;
+}
+
+static void clar_unsandbox(void)
+{
+ if (_clar_path[0] == '\0')
+ return;
+
+ cl_must_pass(chdir(".."));
+
+ fs_rm(_clar_path);
+}
+
+static int build_sandbox_path(void)
+{
+#ifdef CLAR_TMPDIR
+ const char path_tail[] = CLAR_TMPDIR "_XXXXXX";
+#else
+ const char path_tail[] = "clar_tmp_XXXXXX";
+#endif
+
+ size_t len;
+
+ if (find_tmp_path(_clar_path, sizeof(_clar_path)) < 0)
+ return -1;
+
+ len = strlen(_clar_path);
+
+#ifdef _WIN32
+ { /* normalize path to POSIX forward slashes */
+ size_t i;
+ for (i = 0; i < len; ++i) {
+ if (_clar_path[i] == '\\')
+ _clar_path[i] = '/';
+ }
+ }
+#endif
+
+ if (_clar_path[len - 1] != '/') {
+ _clar_path[len++] = '/';
+ }
+
+ strncpy(_clar_path + len, path_tail, sizeof(_clar_path) - len);
+
+#if defined(__MINGW32__)
+ if (_mktemp(_clar_path) == NULL)
+ return -1;
+
+ if (mkdir(_clar_path, 0700) != 0)
+ return -1;
+#elif defined(_WIN32)
+ if (_mktemp_s(_clar_path, sizeof(_clar_path)) != 0)
+ return -1;
+
+ if (mkdir(_clar_path, 0700) != 0)
+ return -1;
+#else
+ if (mkdtemp(_clar_path) == NULL)
+ return -1;
+#endif
+
+ return 0;
+}
+
+static int clar_sandbox(void)
+{
+ if (_clar_path[0] == '\0' && build_sandbox_path() < 0)
+ return -1;
+
+ if (chdir(_clar_path) != 0)
+ return -1;
+
+ return 0;
+}
+
+const char *clar_sandbox_path(void)
+{
+ return _clar_path;
+}
+
diff --git a/tests/clar/clar/summary.h b/tests/clar/clar/summary.h
new file mode 100644
index 0000000..4dd352e
--- /dev/null
+++ b/tests/clar/clar/summary.h
@@ -0,0 +1,143 @@
+
+#include <stdio.h>
+#include <time.h>
+
+static int clar_summary_close_tag(
+ struct clar_summary *summary, const char *tag, int indent)
+{
+ const char *indt;
+
+ if (indent == 0) indt = "";
+ else if (indent == 1) indt = "\t";
+ else indt = "\t\t";
+
+ return fprintf(summary->fp, "%s</%s>\n", indt, tag);
+}
+
+static int clar_summary_testsuites(struct clar_summary *summary)
+{
+ return fprintf(summary->fp, "<testsuites>\n");
+}
+
+static int clar_summary_testsuite(struct clar_summary *summary,
+ int idn, const char *name, time_t timestamp,
+ int test_count, int fail_count, int error_count)
+{
+ struct tm *tm = localtime(&timestamp);
+ char iso_dt[20];
+
+ if (strftime(iso_dt, sizeof(iso_dt), "%Y-%m-%dT%H:%M:%S", tm) == 0)
+ return -1;
+
+ return fprintf(summary->fp, "\t<testsuite"
+ " id=\"%d\""
+ " name=\"%s\""
+ " hostname=\"localhost\""
+ " timestamp=\"%s\""
+ " tests=\"%d\""
+ " failures=\"%d\""
+ " errors=\"%d\">\n",
+ idn, name, iso_dt, test_count, fail_count, error_count);
+}
+
+static int clar_summary_testcase(struct clar_summary *summary,
+ const char *name, const char *classname, double elapsed)
+{
+ return fprintf(summary->fp,
+ "\t\t<testcase name=\"%s\" classname=\"%s\" time=\"%.2f\">\n",
+ name, classname, elapsed);
+}
+
+static int clar_summary_failure(struct clar_summary *summary,
+ const char *type, const char *message, const char *desc)
+{
+ return fprintf(summary->fp,
+ "\t\t\t<failure type=\"%s\"><![CDATA[%s\n%s]]></failure>\n",
+ type, message, desc);
+}
+
+static int clar_summary_skipped(struct clar_summary *summary)
+{
+ return fprintf(summary->fp, "\t\t\t<skipped />\n");
+}
+
+struct clar_summary *clar_summary_init(const char *filename)
+{
+ struct clar_summary *summary;
+ FILE *fp;
+
+ if ((fp = fopen(filename, "w")) == NULL) {
+ perror("fopen");
+ return NULL;
+ }
+
+ if ((summary = malloc(sizeof(struct clar_summary))) == NULL) {
+ perror("malloc");
+ fclose(fp);
+ return NULL;
+ }
+
+ summary->filename = filename;
+ summary->fp = fp;
+
+ return summary;
+}
+
+int clar_summary_shutdown(struct clar_summary *summary)
+{
+ struct clar_report *report;
+ const char *last_suite = NULL;
+
+ if (clar_summary_testsuites(summary) < 0)
+ goto on_error;
+
+ report = _clar.reports;
+ while (report != NULL) {
+ struct clar_error *error = report->errors;
+
+ if (last_suite == NULL || strcmp(last_suite, report->suite) != 0) {
+ if (clar_summary_testsuite(summary, 0, report->suite,
+ report->start, _clar.tests_ran, _clar.total_errors, 0) < 0)
+ goto on_error;
+ }
+
+ last_suite = report->suite;
+
+ clar_summary_testcase(summary, report->test, report->suite, report->elapsed);
+
+ while (error != NULL) {
+ if (clar_summary_failure(summary, "assert",
+ error->error_msg, error->description) < 0)
+ goto on_error;
+
+ error = error->next;
+ }
+
+ if (report->status == CL_TEST_SKIP)
+ clar_summary_skipped(summary);
+
+ if (clar_summary_close_tag(summary, "testcase", 2) < 0)
+ goto on_error;
+
+ report = report->next;
+
+ if (!report || strcmp(last_suite, report->suite) != 0) {
+ if (clar_summary_close_tag(summary, "testsuite", 1) < 0)
+ goto on_error;
+ }
+ }
+
+ if (clar_summary_close_tag(summary, "testsuites", 0) < 0 ||
+ fclose(summary->fp) != 0)
+ goto on_error;
+
+ printf("written summary file to %s\n", summary->filename);
+
+ free(summary);
+ return 0;
+
+on_error:
+ fclose(summary->fp);
+ free(summary);
+ return -1;
+}
diff --git a/tests/clar/clar_libgit2.c b/tests/clar/clar_libgit2.c
new file mode 100644
index 0000000..a1b92fc
--- /dev/null
+++ b/tests/clar/clar_libgit2.c
@@ -0,0 +1,710 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "fs_path.h"
+#include "futils.h"
+#include "git2/sys/repository.h"
+
+void cl_git_report_failure(
+ int error, int expected, const char *file, const char *func, int line, const char *fncall)
+{
+ char msg[4096];
+ const git_error *last = git_error_last();
+
+ if (expected)
+ p_snprintf(msg, 4096, "error %d (expected %d) - %s",
+ error, expected, last ? last->message : "<no message>");
+ else if (error || last)
+ p_snprintf(msg, 4096, "error %d - %s",
+ error, last ? last->message : "<no message>");
+ else
+ p_snprintf(msg, 4096, "no error, expected non-zero return");
+
+ clar__assert(0, file, func, line, fncall, msg, 1);
+}
+
+void cl_git_mkfile(const char *filename, const char *content)
+{
+ int fd;
+
+ fd = p_creat(filename, 0666);
+ cl_assert(fd != -1);
+
+ if (content) {
+ cl_must_pass(p_write(fd, content, strlen(content)));
+ } else {
+ cl_must_pass(p_write(fd, filename, strlen(filename)));
+ cl_must_pass(p_write(fd, "\n", 1));
+ }
+
+ cl_must_pass(p_close(fd));
+}
+
+void cl_git_write2file(
+ const char *path, const char *content, size_t content_len,
+ int flags, unsigned int mode)
+{
+ int fd;
+ cl_assert(path && content);
+ cl_assert((fd = p_open(path, flags, mode)) >= 0);
+ if (!content_len)
+ content_len = strlen(content);
+ cl_must_pass(p_write(fd, content, content_len));
+ cl_must_pass(p_close(fd));
+}
+
+void cl_git_append2file(const char *path, const char *content)
+{
+ cl_git_write2file(path, content, 0, O_WRONLY | O_CREAT | O_APPEND, 0644);
+}
+
+void cl_git_rewritefile(const char *path, const char *content)
+{
+ cl_git_write2file(path, content, 0, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+}
+
+void cl_git_rmfile(const char *filename)
+{
+ cl_must_pass(p_unlink(filename));
+}
+
+char *cl_getenv(const char *name)
+{
+ git_str out = GIT_STR_INIT;
+ int error = git__getenv(&out, name);
+
+ cl_assert(error >= 0 || error == GIT_ENOTFOUND);
+
+ if (error == GIT_ENOTFOUND)
+ return NULL;
+
+ if (out.size == 0) {
+ char *dup = git__strdup("");
+ cl_assert(dup);
+
+ return dup;
+ }
+
+ return git_str_detach(&out);
+}
+
+bool cl_is_env_set(const char *name)
+{
+ char *env = cl_getenv(name);
+ bool result = (env != NULL);
+ git__free(env);
+ return result;
+}
+
+#ifdef GIT_WIN32
+
+#include "win32/utf-conv.h"
+
+int cl_setenv(const char *name, const char *value)
+{
+ wchar_t *wide_name, *wide_value = NULL;
+
+ cl_assert(git_utf8_to_16_alloc(&wide_name, name) >= 0);
+
+ if (value) {
+ cl_assert(git_utf8_to_16_alloc(&wide_value, value) >= 0);
+ cl_assert(SetEnvironmentVariableW(wide_name, wide_value));
+ } else {
+ /* Windows XP returns 0 (failed) when passing NULL for lpValue when
+ * lpName does not exist in the environment block. This behavior
+ * seems to have changed in later versions. Don't check the return value
+ * of SetEnvironmentVariable when passing NULL for lpValue. */
+ SetEnvironmentVariableW(wide_name, NULL);
+ }
+
+ git__free(wide_name);
+ git__free(wide_value);
+ return 0;
+}
+
+/* This function performs retries on calls to MoveFile in order
+ * to provide enhanced reliability in the face of antivirus
+ * agents that may be scanning the source (or in the case that
+ * the source is a directory, a child of the source). */
+int cl_rename(const char *source, const char *dest)
+{
+ git_win32_path source_utf16;
+ git_win32_path dest_utf16;
+ unsigned retries = 1;
+
+ cl_assert(git_win32_path_from_utf8(source_utf16, source) >= 0);
+ cl_assert(git_win32_path_from_utf8(dest_utf16, dest) >= 0);
+
+ while (!MoveFileW(source_utf16, dest_utf16)) {
+ /* Only retry if the error is ERROR_ACCESS_DENIED;
+ * this may indicate that an antivirus agent is
+ * preventing the rename from source to target */
+ if (retries > 5 ||
+ ERROR_ACCESS_DENIED != GetLastError())
+ return -1;
+
+ /* With 5 retries and a coefficient of 10ms, the maximum
+ * delay here is 550 ms */
+ Sleep(10 * retries * retries);
+ retries++;
+ }
+
+ return 0;
+}
+
+#else
+
+#include <stdlib.h>
+
+int cl_setenv(const char *name, const char *value)
+{
+ return (value == NULL) ? unsetenv(name) : setenv(name, value, 1);
+}
+
+int cl_rename(const char *source, const char *dest)
+{
+ return p_rename(source, dest);
+}
+
+#endif
+
+static const char *_cl_sandbox = NULL;
+static git_repository *_cl_repo = NULL;
+
+git_repository *cl_git_sandbox_init(const char *sandbox)
+{
+ /* Get the name of the sandbox folder which will be created */
+ const char *basename = cl_fixture_basename(sandbox);
+
+ /* Copy the whole sandbox folder from our fixtures to our test sandbox
+ * area. After this it can be accessed with `./sandbox`
+ */
+ cl_fixture_sandbox(sandbox);
+ _cl_sandbox = sandbox;
+
+ cl_git_pass(p_chdir(basename));
+
+ /* If this is not a bare repo, then rename `sandbox/.gitted` to
+ * `sandbox/.git` which must be done since we cannot store a folder
+ * named `.git` inside the fixtures folder of our libgit2 repo.
+ */
+ if (p_access(".gitted", F_OK) == 0)
+ cl_git_pass(cl_rename(".gitted", ".git"));
+
+ /* If we have `gitattributes`, rename to `.gitattributes`. This may
+ * be necessary if we don't want the attributes to be applied in the
+ * libgit2 repo, but just during testing.
+ */
+ if (p_access("gitattributes", F_OK) == 0)
+ cl_git_pass(cl_rename("gitattributes", ".gitattributes"));
+
+ /* As with `gitattributes`, we may need `gitignore` just for testing. */
+ if (p_access("gitignore", F_OK) == 0)
+ cl_git_pass(cl_rename("gitignore", ".gitignore"));
+
+ cl_git_pass(p_chdir(".."));
+
+ /* Now open the sandbox repository and make it available for tests */
+ cl_git_pass(git_repository_open(&_cl_repo, basename));
+
+ /* Adjust configs after copying to new filesystem */
+ cl_git_pass(git_repository_reinit_filesystem(_cl_repo, 0));
+
+ return _cl_repo;
+}
+
+git_repository *cl_git_sandbox_init_new(const char *sandbox)
+{
+ cl_git_pass(git_repository_init(&_cl_repo, sandbox, false));
+ _cl_sandbox = sandbox;
+
+ return _cl_repo;
+}
+
+git_repository *cl_git_sandbox_reopen(void)
+{
+ if (_cl_repo) {
+ git_repository_free(_cl_repo);
+ _cl_repo = NULL;
+
+ cl_git_pass(git_repository_open(
+ &_cl_repo, cl_fixture_basename(_cl_sandbox)));
+ }
+
+ return _cl_repo;
+}
+
+void cl_git_sandbox_cleanup(void)
+{
+ if (_cl_repo) {
+ git_repository_free(_cl_repo);
+ _cl_repo = NULL;
+ }
+ if (_cl_sandbox) {
+ cl_fixture_cleanup(_cl_sandbox);
+ _cl_sandbox = NULL;
+ }
+}
+
+bool cl_toggle_filemode(const char *filename)
+{
+ struct stat st1, st2;
+
+ cl_must_pass(p_stat(filename, &st1));
+ cl_must_pass(p_chmod(filename, st1.st_mode ^ 0100));
+ cl_must_pass(p_stat(filename, &st2));
+
+ return (st1.st_mode != st2.st_mode);
+}
+
+bool cl_is_chmod_supported(void)
+{
+ static int _is_supported = -1;
+
+ if (_is_supported < 0) {
+ cl_git_mkfile("filemode.t", "Test if filemode can be modified");
+ _is_supported = cl_toggle_filemode("filemode.t");
+ cl_must_pass(p_unlink("filemode.t"));
+ }
+
+ return _is_supported;
+}
+
+const char* cl_git_fixture_url(const char *fixturename)
+{
+ return cl_git_path_url(cl_fixture(fixturename));
+}
+
+const char* cl_git_path_url(const char *path)
+{
+ static char url[4096 + 1];
+
+ const char *in_buf;
+ git_str path_buf = GIT_STR_INIT;
+ git_str url_buf = GIT_STR_INIT;
+
+ cl_git_pass(git_fs_path_prettify_dir(&path_buf, path, NULL));
+ cl_git_pass(git_str_puts(&url_buf, "file://"));
+
+#ifdef GIT_WIN32
+ /*
+ * A FILE uri matches the following format: file://[host]/path
+ * where "host" can be empty and "path" is an absolute path to the resource.
+ *
+ * In this test, no hostname is used, but we have to ensure the leading triple slashes:
+ *
+ * *nix: file:///usr/home/...
+ * Windows: file:///C:/Users/...
+ */
+ cl_git_pass(git_str_putc(&url_buf, '/'));
+#endif
+
+ in_buf = git_str_cstr(&path_buf);
+
+ /*
+ * A very hacky Url encoding that only takes care of escaping the spaces
+ */
+ while (*in_buf) {
+ if (*in_buf == ' ')
+ cl_git_pass(git_str_puts(&url_buf, "%20"));
+ else
+ cl_git_pass(git_str_putc(&url_buf, *in_buf));
+
+ in_buf++;
+ }
+
+ cl_assert(url_buf.size < sizeof(url) - 1);
+
+ strncpy(url, git_str_cstr(&url_buf), sizeof(url) - 1);
+ url[sizeof(url) - 1] = '\0';
+ git_str_dispose(&url_buf);
+ git_str_dispose(&path_buf);
+ return url;
+}
+
+const char *cl_git_sandbox_path(int is_dir, ...)
+{
+ const char *path = NULL;
+ static char _temp[GIT_PATH_MAX];
+ git_str buf = GIT_STR_INIT;
+ va_list arg;
+
+ cl_git_pass(git_str_sets(&buf, clar_sandbox_path()));
+
+ va_start(arg, is_dir);
+
+ while ((path = va_arg(arg, const char *)) != NULL) {
+ cl_git_pass(git_str_joinpath(&buf, buf.ptr, path));
+ }
+ va_end(arg);
+
+ cl_git_pass(git_fs_path_prettify(&buf, buf.ptr, NULL));
+ if (is_dir)
+ git_fs_path_to_dir(&buf);
+
+ /* make sure we won't truncate */
+ cl_assert(git_str_len(&buf) < sizeof(_temp));
+ git_str_copy_cstr(_temp, sizeof(_temp), &buf);
+
+ git_str_dispose(&buf);
+
+ return _temp;
+}
+
+typedef struct {
+ const char *filename;
+ size_t filename_len;
+} remove_data;
+
+static int remove_placeholders_recurs(void *_data, git_str *path)
+{
+ remove_data *data = (remove_data *)_data;
+ size_t pathlen;
+
+ if (git_fs_path_isdir(path->ptr) == true)
+ return git_fs_path_direach(path, 0, remove_placeholders_recurs, data);
+
+ pathlen = path->size;
+
+ if (pathlen < data->filename_len)
+ return 0;
+
+ /* if path ends in '/'+filename (or equals filename) */
+ if (!strcmp(data->filename, path->ptr + pathlen - data->filename_len) &&
+ (pathlen == data->filename_len ||
+ path->ptr[pathlen - data->filename_len - 1] == '/'))
+ return p_unlink(path->ptr);
+
+ return 0;
+}
+
+int cl_git_remove_placeholders(const char *directory_path, const char *filename)
+{
+ int error;
+ remove_data data;
+ git_str buffer = GIT_STR_INIT;
+
+ if (git_fs_path_isdir(directory_path) == false)
+ return -1;
+
+ if (git_str_sets(&buffer, directory_path) < 0)
+ return -1;
+
+ data.filename = filename;
+ data.filename_len = strlen(filename);
+
+ error = remove_placeholders_recurs(&data, &buffer);
+
+ git_str_dispose(&buffer);
+
+ return error;
+}
+
+#define CL_COMMIT_NAME "Libgit2 Tester"
+#define CL_COMMIT_EMAIL "libgit2-test@github.com"
+#define CL_COMMIT_MSG "Test commit of tree "
+
+void cl_repo_commit_from_index(
+ git_oid *out,
+ git_repository *repo,
+ git_signature *sig,
+ git_time_t time,
+ const char *msg)
+{
+ git_index *index;
+ git_oid commit_id, tree_id;
+ git_object *parent = NULL;
+ git_reference *ref = NULL;
+ git_tree *tree = NULL;
+ char buf[128];
+ int free_sig = (sig == NULL);
+
+ /* it is fine if looking up HEAD fails - we make this the first commit */
+ git_revparse_ext(&parent, &ref, repo, "HEAD");
+
+ /* write the index content as a tree */
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_write_tree(&tree_id, index));
+ cl_git_pass(git_index_write(index));
+ git_index_free(index);
+
+ cl_git_pass(git_tree_lookup(&tree, repo, &tree_id));
+
+ if (sig)
+ cl_assert(sig->name && sig->email);
+ else if (!time)
+ cl_git_pass(git_signature_now(&sig, CL_COMMIT_NAME, CL_COMMIT_EMAIL));
+ else
+ cl_git_pass(git_signature_new(
+ &sig, CL_COMMIT_NAME, CL_COMMIT_EMAIL, time, 0));
+
+ if (!msg) {
+ strcpy(buf, CL_COMMIT_MSG);
+ git_oid_tostr(buf + strlen(CL_COMMIT_MSG),
+ sizeof(buf) - strlen(CL_COMMIT_MSG), &tree_id);
+ msg = buf;
+ }
+
+ cl_git_pass(git_commit_create_v(
+ &commit_id, repo, ref ? git_reference_name(ref) : "HEAD",
+ sig, sig, NULL, msg, tree, parent ? 1 : 0, parent));
+
+ if (out)
+ git_oid_cpy(out, &commit_id);
+
+ git_object_free(parent);
+ git_reference_free(ref);
+ if (free_sig)
+ git_signature_free(sig);
+ git_tree_free(tree);
+}
+
+void cl_repo_set_bool(git_repository *repo, const char *cfg, int value)
+{
+ git_config *config;
+ cl_git_pass(git_repository_config(&config, repo));
+ cl_git_pass(git_config_set_bool(config, cfg, value != 0));
+ git_config_free(config);
+}
+
+int cl_repo_get_bool(git_repository *repo, const char *cfg)
+{
+ int val = 0;
+ git_config *config;
+ cl_git_pass(git_repository_config(&config, repo));
+ if (git_config_get_bool(&val, config, cfg) < 0)
+ git_error_clear();
+ git_config_free(config);
+ return val;
+}
+
+void cl_repo_set_int(git_repository *repo, const char *cfg, int value)
+{
+ git_config *config;
+ cl_git_pass(git_repository_config(&config, repo));
+ cl_git_pass(git_config_set_int32(config, cfg, value));
+ git_config_free(config);
+}
+
+int cl_repo_get_int(git_repository *repo, const char *cfg)
+{
+ int val = 0;
+ git_config *config;
+ cl_git_pass(git_repository_config(&config, repo));
+ if (git_config_get_int32(&val, config, cfg) < 0)
+ git_error_clear();
+ git_config_free(config);
+ return val;
+}
+
+void cl_repo_set_string(git_repository *repo, const char *cfg, const char *value)
+{
+ git_config *config;
+ cl_git_pass(git_repository_config(&config, repo));
+ cl_git_pass(git_config_set_string(config, cfg, value));
+ git_config_free(config);
+}
+
+/* this is essentially the code from git__unescape modified slightly */
+static size_t strip_cr_from_buf(char *start, size_t len)
+{
+ char *scan, *trail, *end = start + len;
+
+ for (scan = trail = start; scan < end; trail++, scan++) {
+ while (*scan == '\r')
+ scan++; /* skip '\r' */
+
+ if (trail != scan)
+ *trail = *scan;
+ }
+
+ *trail = '\0';
+
+ return (trail - start);
+}
+
+void clar__assert_equal_file(
+ const char *expected_data,
+ size_t expected_bytes,
+ int ignore_cr,
+ const char *path,
+ const char *file,
+ const char *func,
+ int line)
+{
+ char buf[4000];
+ ssize_t bytes, total_bytes = 0;
+ int fd = p_open(path, O_RDONLY | O_BINARY);
+ cl_assert(fd >= 0);
+
+ if (expected_data && !expected_bytes)
+ expected_bytes = strlen(expected_data);
+
+ while ((bytes = p_read(fd, buf, sizeof(buf))) != 0) {
+ clar__assert(
+ bytes > 0, file, func, line, "error reading from file", path, 1);
+
+ if (ignore_cr)
+ bytes = strip_cr_from_buf(buf, bytes);
+
+ if (memcmp(expected_data, buf, bytes) != 0) {
+ int pos;
+ for (pos = 0; pos < bytes && expected_data[pos] == buf[pos]; ++pos)
+ /* find differing byte offset */;
+ p_snprintf(
+ buf, sizeof(buf), "file content mismatch at byte %"PRIdZ,
+ (ssize_t)(total_bytes + pos));
+ p_close(fd);
+ clar__fail(file, func, line, path, buf, 1);
+ }
+
+ expected_data += bytes;
+ total_bytes += bytes;
+ }
+
+ p_close(fd);
+
+ clar__assert(!bytes, file, func, line, "error reading from file", path, 1);
+ clar__assert_equal(file, func, line, "mismatched file length", 1, "%"PRIuZ,
+ (size_t)expected_bytes, (size_t)total_bytes);
+}
+
+#define FAKE_HOMEDIR_NAME "cl_fake_home"
+
+static git_buf _cl_restore_homedir = GIT_BUF_INIT;
+
+void cl_fake_homedir_cleanup(void *payload)
+{
+ GIT_UNUSED(payload);
+
+ if (_cl_restore_homedir.ptr) {
+ cl_git_pass(git_futils_rmdir_r(FAKE_HOMEDIR_NAME, NULL, GIT_RMDIR_REMOVE_FILES));
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_HOMEDIR, _cl_restore_homedir.ptr));
+ git_buf_dispose(&_cl_restore_homedir);
+ }
+}
+
+void cl_fake_homedir(git_str *out)
+{
+ git_str path = GIT_STR_INIT;
+
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_GET_HOMEDIR, &_cl_restore_homedir));
+
+ cl_set_cleanup(cl_fake_homedir_cleanup, NULL);
+
+ /* TOC/TOU but merely attempts to prevent accidental cleanup. */
+ cl_assert(!git_fs_path_exists(FAKE_HOMEDIR_NAME));
+ cl_must_pass(p_mkdir(FAKE_HOMEDIR_NAME, 0777));
+ cl_git_pass(git_fs_path_prettify(&path, FAKE_HOMEDIR_NAME, NULL));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_HOMEDIR, path.ptr));
+
+ if (out)
+ git_str_swap(out, &path);
+
+ git_str_dispose(&path);
+}
+
+#define FAKE_GLOBALCONFIG_NAME "cl_fake_global"
+
+static git_buf _cl_restore_globalconfig = GIT_BUF_INIT;
+
+void cl_fake_globalconfig_cleanup(void *payload)
+{
+ GIT_UNUSED(payload);
+
+ if (_cl_restore_globalconfig.ptr) {
+ cl_git_pass(git_futils_rmdir_r(FAKE_GLOBALCONFIG_NAME, NULL, GIT_RMDIR_REMOVE_FILES));
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_HOMEDIR, _cl_restore_globalconfig.ptr));
+ git_buf_dispose(&_cl_restore_globalconfig);
+ }
+}
+
+void cl_fake_globalconfig(git_str *out)
+{
+ git_str path = GIT_STR_INIT;
+
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, &_cl_restore_globalconfig));
+
+ cl_set_cleanup(cl_fake_globalconfig_cleanup, NULL);
+
+ /* TOC/TOU but merely attempts to prevent accidental cleanup. */
+ cl_assert(!git_fs_path_exists(FAKE_GLOBALCONFIG_NAME));
+ cl_must_pass(p_mkdir(FAKE_GLOBALCONFIG_NAME, 0777));
+ cl_git_pass(git_fs_path_prettify(&path, FAKE_GLOBALCONFIG_NAME, NULL));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
+
+ if (out)
+ git_str_swap(out, &path);
+
+ git_str_dispose(&path);
+}
+
+void cl_sandbox_set_homedir(const char *home)
+{
+ git_str path = GIT_STR_INIT;
+
+ if (home) {
+ git_libgit2_opts(GIT_OPT_SET_HOMEDIR, home);
+ } else {
+ git_str_joinpath(&path, clar_sandbox_path(), "__home");
+
+ if (!git_fs_path_exists(path.ptr))
+ cl_must_pass(p_mkdir(path.ptr, 0777));
+
+ git_libgit2_opts(GIT_OPT_SET_HOMEDIR, path.ptr);
+ }
+
+ git_str_dispose(&path);
+}
+
+void cl_sandbox_set_search_path_defaults(void)
+{
+ git_str path = GIT_STR_INIT;
+
+ git_str_joinpath(&path, clar_sandbox_path(), "__config");
+
+ if (!git_fs_path_exists(path.ptr))
+ cl_must_pass(p_mkdir(path.ptr, 0777));
+
+ git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr);
+ git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr);
+ git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr);
+ git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_PROGRAMDATA, path.ptr);
+
+ git_str_dispose(&path);
+}
+
+void cl_sandbox_disable_ownership_validation(void)
+{
+ git_libgit2_opts(GIT_OPT_SET_OWNER_VALIDATION, 0);
+}
+
+#ifdef GIT_WIN32
+bool cl_sandbox_supports_8dot3(void)
+{
+ git_str longpath = GIT_STR_INIT;
+ char *shortname;
+ bool supported;
+
+ cl_git_pass(
+ git_str_joinpath(&longpath, clar_sandbox_path(), "longer_than_8dot3"));
+
+ cl_git_write2file(longpath.ptr, "", 0, O_RDWR|O_CREAT, 0666);
+ shortname = git_win32_path_8dot3_name(longpath.ptr);
+
+ supported = (shortname != NULL);
+
+ git__free(shortname);
+ git_str_dispose(&longpath);
+
+ return supported;
+}
+#endif
+
diff --git a/tests/clar/clar_libgit2.h b/tests/clar/clar_libgit2.h
new file mode 100644
index 0000000..c33b5d2
--- /dev/null
+++ b/tests/clar/clar_libgit2.h
@@ -0,0 +1,269 @@
+#ifndef __CLAR_LIBGIT2__
+#define __CLAR_LIBGIT2__
+
+#include "clar.h"
+#include <git2.h>
+#include "common.h"
+#include "posix.h"
+#include "oid.h"
+
+/**
+ * Replace for `clar_must_pass` that passes the last library error as the
+ * test failure message.
+ *
+ * Use this wrapper around all `git_` library calls that return error codes!
+ */
+#define cl_git_pass(expr) cl_git_expect((expr), 0, __FILE__, __func__, __LINE__)
+
+#define cl_git_fail_with(error, expr) cl_git_expect((expr), error, __FILE__, __func__, __LINE__)
+
+#define cl_git_expect(expr, expected, file, func, line) do { \
+ int _lg2_error; \
+ git_error_clear(); \
+ if ((_lg2_error = (expr)) != expected) \
+ cl_git_report_failure(_lg2_error, expected, file, func, line, "Function call failed: " #expr); \
+ } while (0)
+
+/**
+ * Wrapper for `clar_must_fail` -- this one is
+ * just for consistency. Use with `git_` library
+ * calls that are supposed to fail!
+ */
+#define cl_git_fail(expr) do { \
+ if ((expr) == 0) \
+ git_error_clear(), \
+ cl_git_report_failure(0, 0, __FILE__, __func__, __LINE__, "Function call succeeded: " #expr); \
+ } while (0)
+
+/**
+ * Like cl_git_pass, only for Win32 error code conventions
+ */
+#define cl_win32_pass(expr) do { \
+ int _win32_res; \
+ if ((_win32_res = (expr)) == 0) { \
+ git_error_set(GIT_ERROR_OS, "Returned: %d, system error code: %lu", _win32_res, GetLastError()); \
+ cl_git_report_failure(_win32_res, 0, __FILE__, __func__, __LINE__, "System call failed: " #expr); \
+ } \
+ } while(0)
+
+/**
+ * Thread safe assertions; you cannot use `cl_git_report_failure` from a
+ * child thread since it will try to `longjmp` to abort and "the effect of
+ * a call to longjmp() where initialization of the jmp_buf structure was
+ * not performed in the calling thread is undefined."
+ *
+ * Instead, callers can provide a clar thread error context to a thread,
+ * which will populate and return it on failure. Callers can check the
+ * status with `cl_git_thread_check`.
+ */
+typedef struct {
+ int error;
+ const char *file;
+ const char *func;
+ int line;
+ const char *expr;
+ char error_msg[4096];
+} cl_git_thread_err;
+
+#ifdef GIT_THREADS
+# define cl_git_thread_pass(threaderr, expr) cl_git_thread_pass_(threaderr, (expr), __FILE__, __func__, __LINE__)
+#else
+# define cl_git_thread_pass(threaderr, expr) cl_git_pass(expr)
+#endif
+
+#define cl_git_thread_pass_(__threaderr, __expr, __file, __func, __line) do { \
+ git_error_clear(); \
+ if ((((cl_git_thread_err *)__threaderr)->error = (__expr)) != 0) { \
+ const git_error *_last = git_error_last(); \
+ ((cl_git_thread_err *)__threaderr)->file = __file; \
+ ((cl_git_thread_err *)__threaderr)->func = __func; \
+ ((cl_git_thread_err *)__threaderr)->line = __line; \
+ ((cl_git_thread_err *)__threaderr)->expr = "Function call failed: " #__expr; \
+ p_snprintf(((cl_git_thread_err *)__threaderr)->error_msg, 4096, "thread 0x%" PRIxZ " - error %d - %s", \
+ git_thread_currentid(), ((cl_git_thread_err *)__threaderr)->error, \
+ _last ? _last->message : "<no message>"); \
+ git_thread_exit(__threaderr); \
+ } \
+ } while (0)
+
+GIT_INLINE(void) cl_git_thread_check(void *data)
+{
+ cl_git_thread_err *threaderr = (cl_git_thread_err *)data;
+ if (threaderr->error != 0)
+ clar__assert(0, threaderr->file, threaderr->func, threaderr->line, threaderr->expr, threaderr->error_msg, 1);
+}
+
+void cl_git_report_failure(int, int, const char *, const char *, int, const char *);
+
+#define cl_assert_at_line(expr,file,func,line) \
+ clar__assert((expr) != 0, file, func, line, "Expression is not true: " #expr, NULL, 1)
+
+GIT_INLINE(void) clar__assert_in_range(
+ int lo, int val, int hi,
+ const char *file, const char *func, int line,
+ const char *err, int should_abort)
+{
+ if (lo > val || hi < val) {
+ char buf[128];
+ p_snprintf(buf, sizeof(buf), "%d not in [%d,%d]", val, lo, hi);
+ clar__fail(file, func, line, err, buf, should_abort);
+ }
+}
+
+#define cl_assert_equal_sz(sz1,sz2) do { \
+ size_t __sz1 = (size_t)(sz1), __sz2 = (size_t)(sz2); \
+ clar__assert_equal(__FILE__,__func__,__LINE__,#sz1 " != " #sz2, 1, "%"PRIuZ, __sz1, __sz2); \
+} while (0)
+
+#define cl_assert_in_range(L,V,H) \
+ clar__assert_in_range((L),(V),(H),__FILE__,__func__,__LINE__,"Range check: " #V " in [" #L "," #H "]", 1)
+
+#define cl_assert_equal_file(DATA,SIZE,PATH) \
+ clar__assert_equal_file(DATA,SIZE,0,PATH,__FILE__,__func__,(int)__LINE__)
+
+#define cl_assert_equal_file_ignore_cr(DATA,SIZE,PATH) \
+ clar__assert_equal_file(DATA,SIZE,1,PATH,__FILE__,__func__,(int)__LINE__)
+
+void clar__assert_equal_file(
+ const char *expected_data,
+ size_t expected_size,
+ int ignore_cr,
+ const char *path,
+ const char *file,
+ const char *func,
+ int line);
+
+GIT_INLINE(void) clar__assert_equal_oid(
+ const char *file, const char *func, int line, const char *desc,
+ const git_oid *one, const git_oid *two)
+{
+
+ if (git_oid_equal(one, two))
+ return;
+
+ if (git_oid_type(one) != git_oid_type(two)) {
+ char err[64];
+
+ snprintf(err, 64, "different oid types: %d vs %d", git_oid_type(one), git_oid_type(two));
+ clar__fail(file, func, line, desc, err, 1);
+ } else if (git_oid_type(one) == GIT_OID_SHA1) {
+ char err[] = "\"........................................\" != \"........................................\"";
+ git_oid_fmt(&err[1], one);
+ git_oid_fmt(&err[47], two);
+
+ clar__fail(file, func, line, desc, err, 1);
+#ifdef GIT_EXPERIMENTAL_SHA256
+ } else if (one->type == GIT_OID_SHA256) {
+ char err[] = "\"................................................................\" != \"................................................................\"";
+
+ git_oid_fmt(&err[1], one);
+ git_oid_fmt(&err[71], one);
+
+ clar__fail(file, func, line, desc, err, 1);
+#endif
+ } else {
+ clar__fail(file, func, line, desc, "unknown oid types", 1);
+ }
+}
+
+#define cl_assert_equal_oid(one, two) \
+ clar__assert_equal_oid(__FILE__, __func__, __LINE__, \
+ "OID mismatch: " #one " != " #two, (one), (two))
+
+/*
+ * Some utility macros for building long strings
+ */
+#define REP4(STR) STR STR STR STR
+#define REP15(STR) REP4(STR) REP4(STR) REP4(STR) STR STR STR
+#define REP16(STR) REP4(REP4(STR))
+#define REP256(STR) REP16(REP16(STR))
+#define REP1024(STR) REP4(REP256(STR))
+
+/* Write the contents of a buffer to disk */
+void cl_git_mkfile(const char *filename, const char *content);
+void cl_git_append2file(const char *filename, const char *new_content);
+void cl_git_rewritefile(const char *filename, const char *new_content);
+void cl_git_write2file(const char *path, const char *data,
+ size_t datalen, int flags, unsigned int mode);
+void cl_git_rmfile(const char *filename);
+
+bool cl_toggle_filemode(const char *filename);
+bool cl_is_chmod_supported(void);
+
+/* Environment wrappers */
+char *cl_getenv(const char *name);
+bool cl_is_env_set(const char *name);
+int cl_setenv(const char *name, const char *value);
+
+/* Reliable rename */
+int cl_rename(const char *source, const char *dest);
+
+/* Git sandbox setup helpers */
+
+git_repository *cl_git_sandbox_init(const char *sandbox);
+git_repository *cl_git_sandbox_init_new(const char *name);
+void cl_git_sandbox_cleanup(void);
+git_repository *cl_git_sandbox_reopen(void);
+
+/*
+ * build a sandbox-relative from path segments
+ * is_dir will add a trailing slash
+ * vararg must be a NULL-terminated char * list
+ */
+const char *cl_git_sandbox_path(int is_dir, ...);
+
+/* Local-repo url helpers */
+const char* cl_git_fixture_url(const char *fixturename);
+const char* cl_git_path_url(const char *path);
+
+/* Test repository cleaner */
+int cl_git_remove_placeholders(const char *directory_path, const char *filename);
+
+/* commit creation helpers */
+void cl_repo_commit_from_index(
+ git_oid *out,
+ git_repository *repo,
+ git_signature *sig,
+ git_time_t time,
+ const char *msg);
+
+/* config setting helpers */
+void cl_repo_set_bool(git_repository *repo, const char *cfg, int value);
+int cl_repo_get_bool(git_repository *repo, const char *cfg);
+
+void cl_repo_set_int(git_repository *repo, const char *cfg, int value);
+int cl_repo_get_int(git_repository *repo, const char *cfg);
+
+void cl_repo_set_string(git_repository *repo, const char *cfg, const char *value);
+
+/*
+ * set up a fake "home" directory -- automatically configures cleanup
+ * function to restore the home directory, although you can call it
+ * explicitly if you wish (with NULL).
+ */
+void cl_fake_homedir(git_str *);
+void cl_fake_homedir_cleanup(void *);
+
+/*
+ * set up a fake global configuration directory -- automatically
+ * configures cleanup function to restore the global config
+ * although you can call it explicitly if you wish (with NULL).
+ */
+void cl_fake_globalconfig(git_str *);
+void cl_fake_globalconfig_cleanup(void *);
+
+void cl_sandbox_set_homedir(const char *);
+void cl_sandbox_set_search_path_defaults(void);
+void cl_sandbox_disable_ownership_validation(void);
+
+#ifdef GIT_WIN32
+# define cl_msleep(x) Sleep(x)
+#else
+# define cl_msleep(x) usleep(1000 * (x))
+#endif
+
+#ifdef GIT_WIN32
+bool cl_sandbox_supports_8dot3(void);
+#endif
+
+#endif
diff --git a/tests/clar/clar_libgit2_alloc.c b/tests/clar/clar_libgit2_alloc.c
new file mode 100644
index 0000000..e930379
--- /dev/null
+++ b/tests/clar/clar_libgit2_alloc.c
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "clar_libgit2_alloc.h"
+
+static size_t bytes_available;
+
+/*
+ * The clar allocator uses a tagging mechanism for pointers that
+ * prepends the actual pointer's number bytes as `size_t`.
+ *
+ * First, this is required in order to be able to implement
+ * proper bookkeeping of allocated bytes in both `free` and
+ * `realloc`.
+ *
+ * Second, it may also be able to spot bugs that are
+ * otherwise hard to grasp, as the returned pointer cannot be
+ * free'd directly via free(3P). Instead, one is forced to use
+ * the tandem of `cl__malloc` and `cl__free`, as otherwise the
+ * code is going to crash hard. This is considered to be a
+ * feature, as it helps e.g. in finding cases where by accident
+ * malloc(3P) and free(3P) were used instead of git__malloc and
+ * git__free, respectively.
+ *
+ * The downside is obviously that each allocation grows by
+ * sizeof(size_t) bytes. As the allocator is for testing purposes
+ * only, this tradeoff is considered to be perfectly fine,
+ * though.
+ */
+
+static void *cl__malloc(size_t len, const char *file, int line)
+{
+ char *ptr = NULL;
+ size_t alloclen;
+
+ GIT_UNUSED(file);
+ GIT_UNUSED(line);
+
+ if (len > bytes_available)
+ goto out;
+
+ if (GIT_ADD_SIZET_OVERFLOW(&alloclen, len, sizeof(size_t)) ||
+ (ptr = malloc(alloclen)) == NULL)
+ goto out;
+ memcpy(ptr, &len, sizeof(size_t));
+
+ bytes_available -= len;
+
+out:
+ return ptr ? ptr + sizeof(size_t) : NULL;
+}
+
+static void cl__free(void *ptr)
+{
+ if (ptr) {
+ char *p = ptr;
+ size_t len;
+ memcpy(&len, p - sizeof(size_t), sizeof(size_t));
+ free(p - sizeof(size_t));
+ bytes_available += len;
+ }
+}
+
+static void *cl__realloc(void *ptr, size_t size, const char *file, int line)
+{
+ size_t copybytes = 0;
+ char *p = ptr;
+ void *new;
+
+ if (p)
+ memcpy(&copybytes, p - sizeof(size_t), sizeof(size_t));
+
+ if (copybytes > size)
+ copybytes = size;
+
+ if ((new = cl__malloc(size, file, line)) == NULL)
+ goto out;
+
+ if (p) {
+ memcpy(new, p, copybytes);
+ cl__free(p);
+ }
+
+out:
+ return new;
+}
+
+void cl_alloc_limit(size_t bytes)
+{
+ git_allocator alloc;
+
+ alloc.gmalloc = cl__malloc;
+ alloc.grealloc = cl__realloc;
+ alloc.gfree = cl__free;
+
+ git_allocator_setup(&alloc);
+
+ bytes_available = bytes;
+}
+
+void cl_alloc_reset(void)
+{
+ git_allocator stdalloc;
+ git_stdalloc_init_allocator(&stdalloc);
+ git_allocator_setup(&stdalloc);
+}
diff --git a/tests/clar/clar_libgit2_alloc.h b/tests/clar/clar_libgit2_alloc.h
new file mode 100644
index 0000000..78a18b6
--- /dev/null
+++ b/tests/clar/clar_libgit2_alloc.h
@@ -0,0 +1,11 @@
+#ifndef __CLAR_LIBGIT2_ALLOC__
+#define __CLAR_LIBGIT2_ALLOC__
+
+#include "clar.h"
+#include "common.h"
+#include "git2/sys/alloc.h"
+
+void cl_alloc_limit(size_t bytes);
+void cl_alloc_reset(void);
+
+#endif
diff --git a/tests/clar/clar_libgit2_timer.c b/tests/clar/clar_libgit2_timer.c
new file mode 100644
index 0000000..6c75413
--- /dev/null
+++ b/tests/clar/clar_libgit2_timer.c
@@ -0,0 +1,30 @@
+#include "clar_libgit2.h"
+#include "clar_libgit2_timer.h"
+
+void cl_perf_timer__init(cl_perf_timer *t)
+{
+ memset(t, 0, sizeof(cl_perf_timer));
+}
+
+void cl_perf_timer__start(cl_perf_timer *t)
+{
+ t->time_started = git_time_monotonic();
+}
+
+void cl_perf_timer__stop(cl_perf_timer *t)
+{
+ uint64_t time_now = git_time_monotonic();
+
+ t->last = time_now - t->time_started;
+ t->sum += t->last;
+}
+
+uint64_t cl_perf_timer__last(const cl_perf_timer *t)
+{
+ return t->last;
+}
+
+uint64_t cl_perf_timer__sum(const cl_perf_timer *t)
+{
+ return t->sum;
+}
diff --git a/tests/clar/clar_libgit2_timer.h b/tests/clar/clar_libgit2_timer.h
new file mode 100644
index 0000000..8870672
--- /dev/null
+++ b/tests/clar/clar_libgit2_timer.h
@@ -0,0 +1,35 @@
+#ifndef __CLAR_LIBGIT2_TIMER__
+#define __CLAR_LIBGIT2_TIMER__
+
+struct cl_perf_timer
+{
+ /* cumulative running time across all start..stop intervals */
+ uint64_t sum;
+
+ /* value of last start..stop interval */
+ uint64_t last;
+
+ /* clock value at start */
+ uint64_t time_started;
+};
+
+#define CL_PERF_TIMER_INIT {0}
+
+typedef struct cl_perf_timer cl_perf_timer;
+
+void cl_perf_timer__init(cl_perf_timer *t);
+void cl_perf_timer__start(cl_perf_timer *t);
+void cl_perf_timer__stop(cl_perf_timer *t);
+
+/**
+ * return value of last start..stop interval in seconds.
+ */
+uint64_t cl_perf_timer__last(const cl_perf_timer *t);
+
+/**
+ * return cumulative running time across all start..stop
+ * intervals in seconds.
+ */
+uint64_t cl_perf_timer__sum(const cl_perf_timer *t);
+
+#endif /* __CLAR_LIBGIT2_TIMER__ */
diff --git a/tests/clar/clar_libgit2_trace.c b/tests/clar/clar_libgit2_trace.c
new file mode 100644
index 0000000..814a5fa
--- /dev/null
+++ b/tests/clar/clar_libgit2_trace.c
@@ -0,0 +1,263 @@
+#include "clar_libgit2_trace.h"
+#include "clar_libgit2.h"
+#include "clar_libgit2_timer.h"
+#include "trace.h"
+
+struct method {
+ const char *name;
+ void (*git_trace_cb)(git_trace_level_t level, const char *msg);
+ void (*close)(void);
+};
+
+static const char *message_prefix(git_trace_level_t level)
+{
+ switch (level) {
+ case GIT_TRACE_NONE:
+ return "[NONE]: ";
+ case GIT_TRACE_FATAL:
+ return "[FATAL]: ";
+ case GIT_TRACE_ERROR:
+ return "[ERROR]: ";
+ case GIT_TRACE_WARN:
+ return "[WARN]: ";
+ case GIT_TRACE_INFO:
+ return "[INFO]: ";
+ case GIT_TRACE_DEBUG:
+ return "[DEBUG]: ";
+ case GIT_TRACE_TRACE:
+ return "[TRACE]: ";
+ default:
+ return "[?????]: ";
+ }
+}
+
+static void _git_trace_cb__printf(git_trace_level_t level, const char *msg)
+{
+ printf("%s%s\n", message_prefix(level), msg);
+}
+
+#if defined(GIT_WIN32)
+static void _git_trace_cb__debug(git_trace_level_t level, const char *msg)
+{
+ OutputDebugString(message_prefix(level));
+ OutputDebugString(msg);
+ OutputDebugString("\n");
+
+ printf("%s%s\n", message_prefix(level), msg);
+}
+#else
+#define _git_trace_cb__debug _git_trace_cb__printf
+#endif
+
+
+static void _trace_printf_close(void)
+{
+ fflush(stdout);
+}
+
+#define _trace_debug_close _trace_printf_close
+
+
+static struct method s_methods[] = {
+ { "printf", _git_trace_cb__printf, _trace_printf_close },
+ { "debug", _git_trace_cb__debug, _trace_debug_close },
+ /* TODO add file method */
+ {0},
+};
+
+
+static int s_trace_loaded = 0;
+static int s_trace_level = GIT_TRACE_NONE;
+static struct method *s_trace_method = NULL;
+static int s_trace_tests = 0;
+
+static int set_method(const char *name)
+{
+ int k;
+
+ if (!name || !*name)
+ name = "printf";
+
+ for (k=0; (s_methods[k].name); k++) {
+ if (strcmp(name, s_methods[k].name) == 0) {
+ s_trace_method = &s_methods[k];
+ return 0;
+ }
+ }
+ fprintf(stderr, "Unknown CLAR_TRACE_METHOD: '%s'\n", name);
+ return -1;
+}
+
+
+/**
+ * Lookup CLAR_TRACE_LEVEL and CLAR_TRACE_METHOD from
+ * the environment and set the above s_trace_* fields.
+ *
+ * If CLAR_TRACE_LEVEL is not set, we disable tracing.
+ *
+ * TODO If set, we assume GIT_TRACE_TRACE level, which
+ * logs everything. Later, we may want to parse the
+ * value of the environment variable and set a specific
+ * level.
+ *
+ * We assume the "printf" method. This can be changed
+ * with the CLAR_TRACE_METHOD environment variable.
+ * Currently, this is only needed on Windows for a "debug"
+ * version which also writes to the debug output window
+ * in Visual Studio.
+ *
+ * TODO add a "file" method that would open and write
+ * to a well-known file. This would help keep trace
+ * output and clar output separate.
+ *
+ */
+static void _load_trace_params(void)
+{
+ char *sz_level;
+ char *sz_method;
+ char *sz_tests;
+
+ s_trace_loaded = 1;
+
+ sz_level = cl_getenv("CLAR_TRACE_LEVEL");
+ if (!sz_level || !*sz_level) {
+ s_trace_level = GIT_TRACE_NONE;
+ s_trace_method = NULL;
+ return;
+ }
+
+ /* TODO Parse sz_level and set s_trace_level. */
+ s_trace_level = GIT_TRACE_TRACE;
+
+ sz_method = cl_getenv("CLAR_TRACE_METHOD");
+ if (set_method(sz_method) < 0)
+ set_method(NULL);
+
+ sz_tests = cl_getenv("CLAR_TRACE_TESTS");
+ if (sz_tests != NULL)
+ s_trace_tests = 1;
+}
+
+#define HR "================================================================"
+
+/**
+ * Timer to report the take spend in a test's run() method.
+ */
+static cl_perf_timer s_timer_run = CL_PERF_TIMER_INIT;
+
+/**
+ * Timer to report total time in a test (init, run, cleanup).
+ */
+static cl_perf_timer s_timer_test = CL_PERF_TIMER_INIT;
+
+static void _cl_trace_cb__event_handler(
+ cl_trace_event ev,
+ const char *suite_name,
+ const char *test_name,
+ void *payload)
+{
+ GIT_UNUSED(payload);
+
+ if (!s_trace_tests)
+ return;
+
+ switch (ev) {
+ case CL_TRACE__SUITE_BEGIN:
+ git_trace(GIT_TRACE_TRACE, "\n\n%s\n%s: Begin Suite", HR, suite_name);
+#if 0 && defined(GIT_WIN32_LEAKCHECK)
+ git_win32__crtdbg_stacktrace__dump(
+ GIT_WIN32__CRTDBG_STACKTRACE__SET_MARK,
+ suite_name);
+#endif
+ break;
+
+ case CL_TRACE__SUITE_END:
+#if 0 && defined(GIT_WIN32_LEAKCHECK)
+ /* As an example of checkpointing, dump leaks within this suite.
+ * This may generate false positives for things like the global
+ * TLS error state and maybe the odb cache since they aren't
+ * freed until the global shutdown and outside the scope of this
+ * set of tests.
+ *
+ * This may under-report if the test itself uses a checkpoint.
+ * See tests/trace/windows/stacktrace.c
+ */
+ git_win32__crtdbg_stacktrace__dump(
+ GIT_WIN32__CRTDBG_STACKTRACE__LEAKS_SINCE_MARK,
+ suite_name);
+#endif
+ git_trace(GIT_TRACE_TRACE, "\n\n%s: End Suite\n%s", suite_name, HR);
+ break;
+
+ case CL_TRACE__TEST__BEGIN:
+ git_trace(GIT_TRACE_TRACE, "\n%s::%s: Begin Test", suite_name, test_name);
+ cl_perf_timer__init(&s_timer_test);
+ cl_perf_timer__start(&s_timer_test);
+ break;
+
+ case CL_TRACE__TEST__END:
+ cl_perf_timer__stop(&s_timer_test);
+ git_trace(GIT_TRACE_TRACE, "%s::%s: End Test (%" PRIuZ " %" PRIuZ ")", suite_name, test_name,
+ cl_perf_timer__last(&s_timer_run),
+ cl_perf_timer__last(&s_timer_test));
+ break;
+
+ case CL_TRACE__TEST__RUN_BEGIN:
+ git_trace(GIT_TRACE_TRACE, "%s::%s: Begin Run", suite_name, test_name);
+ cl_perf_timer__init(&s_timer_run);
+ cl_perf_timer__start(&s_timer_run);
+ break;
+
+ case CL_TRACE__TEST__RUN_END:
+ cl_perf_timer__stop(&s_timer_run);
+ git_trace(GIT_TRACE_TRACE, "%s::%s: End Run", suite_name, test_name);
+ break;
+
+ case CL_TRACE__TEST__LONGJMP:
+ cl_perf_timer__stop(&s_timer_run);
+ git_trace(GIT_TRACE_TRACE, "%s::%s: Aborted", suite_name, test_name);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/**
+ * Setup/Enable git_trace() based upon settings user's environment.
+ */
+void cl_global_trace_register(void)
+{
+ if (!s_trace_loaded)
+ _load_trace_params();
+
+ if (s_trace_level == GIT_TRACE_NONE)
+ return;
+ if (s_trace_method == NULL)
+ return;
+ if (s_trace_method->git_trace_cb == NULL)
+ return;
+
+ git_trace_set(s_trace_level, s_trace_method->git_trace_cb);
+ cl_trace_register(_cl_trace_cb__event_handler, NULL);
+}
+
+/**
+ * If we turned on git_trace() earlier, turn it off.
+ *
+ * This is intended to let us close/flush any buffered
+ * IO if necessary.
+ *
+ */
+void cl_global_trace_disable(void)
+{
+ cl_trace_register(NULL, NULL);
+ git_trace_set(GIT_TRACE_NONE, NULL);
+ if (s_trace_method && s_trace_method->close)
+ s_trace_method->close();
+
+ /* Leave s_trace_ vars set so they can restart tracing
+ * since we only want to hit the environment variables
+ * once.
+ */
+}
diff --git a/tests/clar/clar_libgit2_trace.h b/tests/clar/clar_libgit2_trace.h
new file mode 100644
index 0000000..09d1e05
--- /dev/null
+++ b/tests/clar/clar_libgit2_trace.h
@@ -0,0 +1,7 @@
+#ifndef __CLAR_LIBGIT2_TRACE__
+#define __CLAR_LIBGIT2_TRACE__
+
+void cl_global_trace_register(void);
+void cl_global_trace_disable(void);
+
+#endif
diff --git a/tests/clar/generate.py b/tests/clar/generate.py
new file mode 100644
index 0000000..d2cdb68
--- /dev/null
+++ b/tests/clar/generate.py
@@ -0,0 +1,316 @@
+#!/usr/bin/env python
+#
+# Copyright (c) Vicent Marti. All rights reserved.
+#
+# This file is part of clar, distributed under the ISC license.
+# For full terms see the included COPYING file.
+#
+
+from __future__ import with_statement
+from string import Template
+import re, fnmatch, os, sys, codecs, pickle, io
+
+class Module(object):
+ class Template(object):
+ def __init__(self, module):
+ self.module = module
+
+ def _render_callback(self, cb):
+ if not cb:
+ return ' { NULL, NULL }'
+ return ' { "%s", &%s }' % (cb['short_name'], cb['symbol'])
+
+ class DeclarationTemplate(Template):
+ def render(self):
+ out = "\n".join("extern %s;" % cb['declaration'] for cb in self.module.callbacks) + "\n"
+
+ for initializer in self.module.initializers:
+ out += "extern %s;\n" % initializer['declaration']
+
+ if self.module.cleanup:
+ out += "extern %s;\n" % self.module.cleanup['declaration']
+
+ return out
+
+ class CallbacksTemplate(Template):
+ def render(self):
+ out = "static const struct clar_func _clar_cb_%s[] = {\n" % self.module.name
+ out += ",\n".join(self._render_callback(cb) for cb in self.module.callbacks)
+ out += "\n};\n"
+ return out
+
+ class InfoTemplate(Template):
+ def render(self):
+ templates = []
+
+ initializers = self.module.initializers
+ if len(initializers) == 0:
+ initializers = [ None ]
+
+ for initializer in initializers:
+ name = self.module.clean_name()
+ if initializer and initializer['short_name'].startswith('initialize_'):
+ variant = initializer['short_name'][len('initialize_'):]
+ name += " (%s)" % variant.replace('_', ' ')
+
+ template = Template(
+ r"""
+ {
+ "${clean_name}",
+ ${initialize},
+ ${cleanup},
+ ${cb_ptr}, ${cb_count}, ${enabled}
+ }"""
+ ).substitute(
+ clean_name = name,
+ initialize = self._render_callback(initializer),
+ cleanup = self._render_callback(self.module.cleanup),
+ cb_ptr = "_clar_cb_%s" % self.module.name,
+ cb_count = len(self.module.callbacks),
+ enabled = int(self.module.enabled)
+ )
+ templates.append(template)
+
+ return ','.join(templates)
+
+ def __init__(self, name):
+ self.name = name
+
+ self.mtime = 0
+ self.enabled = True
+ self.modified = False
+
+ def clean_name(self):
+ return self.name.replace("_", "::")
+
+ def _skip_comments(self, text):
+ SKIP_COMMENTS_REGEX = re.compile(
+ r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
+ re.DOTALL | re.MULTILINE)
+
+ def _replacer(match):
+ s = match.group(0)
+ return "" if s.startswith('/') else s
+
+ return re.sub(SKIP_COMMENTS_REGEX, _replacer, text)
+
+ def parse(self, contents):
+ TEST_FUNC_REGEX = r"^(void\s+(test_%s__(\w+))\s*\(\s*void\s*\))\s*\{"
+
+ contents = self._skip_comments(contents)
+ regex = re.compile(TEST_FUNC_REGEX % self.name, re.MULTILINE)
+
+ self.callbacks = []
+ self.initializers = []
+ self.cleanup = None
+
+ for (declaration, symbol, short_name) in regex.findall(contents):
+ data = {
+ "short_name" : short_name,
+ "declaration" : declaration,
+ "symbol" : symbol
+ }
+
+ if short_name.startswith('initialize'):
+ self.initializers.append(data)
+ elif short_name == 'cleanup':
+ self.cleanup = data
+ else:
+ self.callbacks.append(data)
+
+ return self.callbacks != []
+
+ def refresh(self, path):
+ self.modified = False
+
+ try:
+ st = os.stat(path)
+
+ # Not modified
+ if st.st_mtime == self.mtime:
+ return True
+
+ self.modified = True
+ self.mtime = st.st_mtime
+
+ with codecs.open(path, encoding='utf-8') as fp:
+ raw_content = fp.read()
+
+ except IOError:
+ return False
+
+ return self.parse(raw_content)
+
+class TestSuite(object):
+
+ def __init__(self, path, output):
+ self.path = path
+ self.output = output
+
+ def maybe_generate(self, path):
+ if not os.path.isfile(path):
+ return True
+
+ if any(module.modified for module in self.modules.values()):
+ return True
+
+ return False
+
+ def find_modules(self):
+ modules = []
+ for root, _, files in os.walk(self.path):
+ module_root = root[len(self.path):]
+ module_root = [c for c in module_root.split(os.sep) if c]
+
+ tests_in_module = fnmatch.filter(files, "*.c")
+
+ for test_file in tests_in_module:
+ full_path = os.path.join(root, test_file)
+ module_name = "_".join(module_root + [test_file[:-2]]).replace("-", "_")
+
+ modules.append((full_path, module_name))
+
+ return modules
+
+ def load_cache(self):
+ path = os.path.join(self.output, '.clarcache')
+ cache = {}
+
+ try:
+ fp = open(path, 'rb')
+ cache = pickle.load(fp)
+ fp.close()
+ except (IOError, ValueError):
+ pass
+
+ return cache
+
+ def save_cache(self):
+ path = os.path.join(self.output, '.clarcache')
+ with open(path, 'wb') as cache:
+ pickle.dump(self.modules, cache)
+
+ def load(self, force = False):
+ module_data = self.find_modules()
+ self.modules = {} if force else self.load_cache()
+
+ for path, name in module_data:
+ if name not in self.modules:
+ self.modules[name] = Module(name)
+
+ if not self.modules[name].refresh(path):
+ del self.modules[name]
+
+ def disable(self, excluded):
+ for exclude in excluded:
+ for module in self.modules.values():
+ name = module.clean_name()
+ if name.startswith(exclude):
+ module.enabled = False
+ module.modified = True
+
+ def suite_count(self):
+ return sum(max(1, len(m.initializers)) for m in self.modules.values())
+
+ def callback_count(self):
+ return sum(len(module.callbacks) for module in self.modules.values())
+
+ def write(self):
+ wrote_suite = self.write_suite()
+ wrote_header = self.write_header()
+
+ if wrote_suite or wrote_header:
+ self.save_cache()
+ return True
+
+ return False
+
+ def write_output(self, fn, data):
+ if not self.maybe_generate(fn):
+ return False
+
+ current = None
+
+ try:
+ with open(fn, 'r') as input:
+ current = input.read()
+ except OSError:
+ pass
+ except IOError:
+ pass
+
+ if current == data:
+ return False
+
+ with open(fn, 'w') as output:
+ output.write(data)
+
+ return True
+
+ def write_suite(self):
+ suite_fn = os.path.join(self.output, 'clar.suite')
+
+ with io.StringIO() as suite_file:
+ modules = sorted(self.modules.values(), key=lambda module: module.name)
+
+ for module in modules:
+ t = Module.DeclarationTemplate(module)
+ suite_file.write(t.render())
+
+ for module in modules:
+ t = Module.CallbacksTemplate(module)
+ suite_file.write(t.render())
+
+ suites = "static struct clar_suite _clar_suites[] = {" + ','.join(
+ Module.InfoTemplate(module).render() for module in modules
+ ) + "\n};\n"
+
+ suite_file.write(suites)
+
+ suite_file.write(u"static const size_t _clar_suite_count = %d;\n" % self.suite_count())
+ suite_file.write(u"static const size_t _clar_callback_count = %d;\n" % self.callback_count())
+
+ return self.write_output(suite_fn, suite_file.getvalue())
+
+ return False
+
+ def write_header(self):
+ header_fn = os.path.join(self.output, 'clar_suite.h')
+
+ with io.StringIO() as header_file:
+ header_file.write(u"#ifndef _____clar_suite_h_____\n")
+ header_file.write(u"#define _____clar_suite_h_____\n")
+
+ modules = sorted(self.modules.values(), key=lambda module: module.name)
+
+ for module in modules:
+ t = Module.DeclarationTemplate(module)
+ header_file.write(t.render())
+
+ header_file.write(u"#endif\n")
+
+ return self.write_output(header_fn, header_file.getvalue())
+
+ return False
+
+if __name__ == '__main__':
+ from optparse import OptionParser
+
+ parser = OptionParser()
+ parser.add_option('-f', '--force', action="store_true", dest='force', default=False)
+ parser.add_option('-x', '--exclude', dest='excluded', action='append', default=[])
+ parser.add_option('-o', '--output', dest='output')
+
+ options, args = parser.parse_args()
+ if len(args) > 1:
+ print("More than one path given")
+ sys.exit(1)
+
+ path = args.pop() if args else '.'
+ output = options.output or path
+ suite = TestSuite(path, output)
+ suite.load(options.force)
+ suite.disable(options.excluded)
+ if suite.write():
+ print("Written `clar.suite`, `clar_suite.h` (%d tests in %d suites)" % (suite.callback_count(), suite.suite_count()))
+
diff --git a/tests/clar/main.c b/tests/clar/main.c
new file mode 100644
index 0000000..e3f4fe7
--- /dev/null
+++ b/tests/clar/main.c
@@ -0,0 +1,52 @@
+#include "clar_libgit2.h"
+#include "clar_libgit2_trace.h"
+
+#ifdef GIT_WIN32_LEAKCHECK
+# include "win32/w32_leakcheck.h"
+#endif
+
+#ifdef _WIN32
+int __cdecl main(int argc, char *argv[])
+#else
+int main(int argc, char *argv[])
+#endif
+{
+ int res;
+ char *at_exit_cmd;
+
+ clar_test_init(argc, argv);
+
+ res = git_libgit2_init();
+ if (res < 0) {
+ const git_error *err = git_error_last();
+ const char *msg = err ? err->message : "unknown failure";
+ fprintf(stderr, "failed to init libgit2: %s\n", msg);
+ return res;
+ }
+
+ cl_global_trace_register();
+ cl_sandbox_set_homedir(getenv("CLAR_HOMEDIR"));
+ cl_sandbox_set_search_path_defaults();
+ cl_sandbox_disable_ownership_validation();
+
+ /* Run the test suite */
+ res = clar_test_run();
+
+ clar_test_shutdown();
+
+ cl_global_trace_disable();
+ git_libgit2_shutdown();
+
+#ifdef GIT_WIN32_LEAKCHECK
+ if (git_win32_leakcheck_has_leaks())
+ res = res || 1;
+#endif
+
+ at_exit_cmd = getenv("CLAR_AT_EXIT");
+ if (at_exit_cmd != NULL) {
+ int at_exit = system(at_exit_cmd);
+ return res || at_exit;
+ }
+
+ return res;
+}
diff --git a/tests/headertest/CMakeLists.txt b/tests/headertest/CMakeLists.txt
new file mode 100644
index 0000000..c70ce1a
--- /dev/null
+++ b/tests/headertest/CMakeLists.txt
@@ -0,0 +1,14 @@
+# Header file validation project: ensure that we do not publish any sloppy
+# definitions in our headers and that a consumer can include <git2.dll>
+# even when they have aggressive C90 warnings enabled.
+
+add_executable(headertest headertest.c)
+set_target_properties(headertest PROPERTIES C_STANDARD 90)
+set_target_properties(headertest PROPERTIES C_EXTENSIONS OFF)
+target_include_directories(headertest PRIVATE ${LIBGIT2_INCLUDES})
+
+if (MSVC)
+ target_compile_options(headertest PUBLIC /W4 /WX)
+else()
+ target_compile_options(headertest PUBLIC -Wall -Wextra -pedantic -Werror)
+endif()
diff --git a/tests/headertest/headertest.c b/tests/headertest/headertest.c
new file mode 100644
index 0000000..2af8a14
--- /dev/null
+++ b/tests/headertest/headertest.c
@@ -0,0 +1,13 @@
+/*
+ * Dummy project to validate header files
+ *
+ * This project is not intended to be executed, it should only include all
+ * header files to make sure that they can be used with stricter compiler
+ * settings than the libgit2 source files generally supports.
+ */
+#include "git2.h"
+
+int main(void)
+{
+ return 0;
+}
diff --git a/tests/libgit2/CMakeLists.txt b/tests/libgit2/CMakeLists.txt
new file mode 100644
index 0000000..af70f55
--- /dev/null
+++ b/tests/libgit2/CMakeLists.txt
@@ -0,0 +1,77 @@
+# tests: the unit and integration tests for libgit2
+
+set(Python_ADDITIONAL_VERSIONS 3 2.7)
+find_package(PythonInterp)
+
+if(NOT PYTHONINTERP_FOUND)
+ message(FATAL_ERROR "Could not find a python interpreter, which is needed to build the tests. "
+ "Make sure python is available, or pass -DBUILD_TESTS=OFF to skip building the tests")
+ENDIF()
+
+set(CLAR_PATH "${PROJECT_SOURCE_DIR}/tests/clar")
+set(CLAR_FIXTURES "${PROJECT_SOURCE_DIR}/tests/resources/")
+set(TEST_PATH "${CMAKE_CURRENT_SOURCE_DIR}")
+add_definitions(-DCLAR_FIXTURE_PATH=\"${CLAR_FIXTURES}\")
+add_definitions(-DCLAR_TMPDIR=\"libgit2_tests\")
+add_definitions(-DCLAR_WIN32_LONGPATHS)
+add_definitions(-D_FILE_OFFSET_BITS=64)
+
+# Ensure that we do not use deprecated functions internally
+add_definitions(-DGIT_DEPRECATE_HARD)
+
+set(TEST_INCLUDES "${CLAR_PATH}" "${TEST_PATH}" "${CMAKE_CURRENT_BINARY_DIR}")
+file(GLOB_RECURSE SRC_TEST ${TEST_PATH}/*/*.c ${TEST_PATH}/*/*.h)
+file(GLOB_RECURSE SRC_CLAR ${CLAR_PATH}/*.c ${CLAR_PATH}/*.h)
+
+if(MSVC_IDE)
+ list(APPEND SRC_TEST "precompiled.c")
+endif()
+
+add_custom_command(
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/clar.suite ${CMAKE_CURRENT_BINARY_DIR}/clar_suite.h
+ COMMAND ${PYTHON_EXECUTABLE} ${CLAR_PATH}/generate.py -o "${CMAKE_CURRENT_BINARY_DIR}" -f -xonline -xstress -xperf .
+ DEPENDS ${SRC_TEST}
+ WORKING_DIRECTORY ${TEST_PATH}
+)
+
+set_source_files_properties(
+ ${CLAR_PATH}/clar.c
+ PROPERTIES OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/clar.suite)
+
+add_executable(libgit2_tests ${SRC_CLAR} ${SRC_TEST} ${LIBGIT2_OBJECTS})
+
+set_target_properties(libgit2_tests PROPERTIES C_STANDARD 90)
+set_target_properties(libgit2_tests PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR})
+
+target_include_directories(libgit2_tests PRIVATE ${TEST_INCLUDES} ${LIBGIT2_INCLUDES} ${LIBGIT2_DEPENDENCY_INCLUDES})
+target_include_directories(libgit2_tests SYSTEM PRIVATE ${LIBGIT2_SYSTEM_INCLUDES})
+target_link_libraries(libgit2_tests ${LIBGIT2_SYSTEM_LIBS})
+
+ide_split_sources(libgit2_tests)
+
+#
+# Old versions of gcc require us to declare our test functions; don't do
+# this on newer compilers to avoid unnecessary recompilation.
+#
+if(CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0)
+ target_compile_options(libgit2_tests PRIVATE -include "clar_suite.h")
+endif()
+
+if(MSVC_IDE)
+ # Precompiled headers
+ set_target_properties(libgit2_tests PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h")
+ set_source_files_properties("precompiled.c" COMPILE_FLAGS "/Ycprecompiled.h")
+endif()
+
+include(AddClarTest)
+add_clar_test(libgit2_tests offline -v -xonline)
+add_clar_test(libgit2_tests invasive -v -sfilter::stream::bigfile -sodb::largefiles -siterator::workdir::filesystem_gunk -srepo::init -srepo::init::at_filesystem_root -sonline::clone::connect_timeout_default)
+add_clar_test(libgit2_tests online -v -sonline -xonline::customcert)
+add_clar_test(libgit2_tests online_customcert -v -sonline::customcert)
+add_clar_test(libgit2_tests gitdaemon -v -sonline::push)
+add_clar_test(libgit2_tests gitdaemon_namespace -v -sonline::clone::namespace)
+add_clar_test(libgit2_tests gitdaemon_sha256 -v -sonline::clone::sha256)
+add_clar_test(libgit2_tests ssh -v -sonline::push -sonline::clone::ssh_cert -sonline::clone::ssh_with_paths -sonline::clone::path_whitespace_ssh -sonline::clone::ssh_auth_methods)
+add_clar_test(libgit2_tests proxy -v -sonline::clone::proxy)
+add_clar_test(libgit2_tests auth_clone -v -sonline::clone::cred)
+add_clar_test(libgit2_tests auth_clone_and_push -v -sonline::clone::push -sonline::push)
diff --git a/tests/libgit2/apply/apply_helpers.c b/tests/libgit2/apply/apply_helpers.c
new file mode 100644
index 0000000..e78b8ff
--- /dev/null
+++ b/tests/libgit2/apply/apply_helpers.c
@@ -0,0 +1,135 @@
+#include "clar_libgit2.h"
+#include "apply_helpers.h"
+
+struct iterator_compare_data {
+ struct merge_index_entry *expected;
+ size_t cnt;
+ size_t idx;
+};
+
+static int iterator_compare(const git_index_entry *entry, void *_data)
+{
+ git_oid expected_id;
+
+ struct iterator_compare_data *data = (struct iterator_compare_data *)_data;
+
+ cl_assert_equal_i(GIT_INDEX_ENTRY_STAGE(entry), data->expected[data->idx].stage);
+ cl_git_pass(git_oid__fromstr(&expected_id, data->expected[data->idx].oid_str, GIT_OID_SHA1));
+ cl_assert_equal_oid(&entry->id, &expected_id);
+ cl_assert_equal_i(entry->mode, data->expected[data->idx].mode);
+ cl_assert_equal_s(entry->path, data->expected[data->idx].path);
+
+ if (data->idx >= data->cnt)
+ return -1;
+
+ data->idx++;
+
+ return 0;
+}
+
+void validate_apply_workdir(
+ git_repository *repo,
+ struct merge_index_entry *workdir_entries,
+ size_t workdir_cnt)
+{
+ git_index *index;
+ git_iterator *iterator;
+ git_iterator_options opts = GIT_ITERATOR_OPTIONS_INIT;
+ struct iterator_compare_data data = { workdir_entries, workdir_cnt };
+
+ opts.flags |= GIT_ITERATOR_INCLUDE_HASH;
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_iterator_for_workdir(&iterator, repo, index, NULL, &opts));
+
+ cl_git_pass(git_iterator_foreach(iterator, iterator_compare, &data));
+ cl_assert_equal_i(data.idx, data.cnt);
+
+ git_iterator_free(iterator);
+ git_index_free(index);
+}
+
+void validate_apply_index(
+ git_repository *repo,
+ struct merge_index_entry *index_entries,
+ size_t index_cnt)
+{
+ git_index *index;
+ git_iterator *iterator;
+ struct iterator_compare_data data = { index_entries, index_cnt };
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_iterator_for_index(&iterator, repo, index, NULL));
+
+ cl_git_pass(git_iterator_foreach(iterator, iterator_compare, &data));
+ cl_assert_equal_i(data.idx, data.cnt);
+
+ git_iterator_free(iterator);
+ git_index_free(index);
+}
+
+static int iterator_eq(const git_index_entry **entry, void *_data)
+{
+ GIT_UNUSED(_data);
+
+ if (!entry[0] || !entry[1])
+ return -1;
+
+ cl_assert_equal_i(GIT_INDEX_ENTRY_STAGE(entry[0]), GIT_INDEX_ENTRY_STAGE(entry[1]));
+ cl_assert_equal_oid(&entry[0]->id, &entry[1]->id);
+ cl_assert_equal_i(entry[0]->mode, entry[1]->mode);
+ cl_assert_equal_s(entry[0]->path, entry[1]->path);
+
+ return 0;
+}
+
+void validate_index_unchanged(git_repository *repo)
+{
+ git_tree *head;
+ git_index *index;
+ git_iterator *head_iterator, *index_iterator, *iterators[2];
+
+ cl_git_pass(git_repository_head_tree(&head, repo));
+ cl_git_pass(git_repository_index(&index, repo));
+
+ cl_git_pass(git_iterator_for_tree(&head_iterator, head, NULL));
+ cl_git_pass(git_iterator_for_index(&index_iterator, repo, index, NULL));
+
+ iterators[0] = head_iterator;
+ iterators[1] = index_iterator;
+
+ cl_git_pass(git_iterator_walk(iterators, 2, iterator_eq, NULL));
+
+ git_iterator_free(head_iterator);
+ git_iterator_free(index_iterator);
+
+ git_tree_free(head);
+ git_index_free(index);
+}
+
+void validate_workdir_unchanged(git_repository *repo)
+{
+ git_tree *head;
+ git_index *index;
+ git_iterator *head_iterator, *workdir_iterator, *iterators[2];
+ git_iterator_options workdir_opts = GIT_ITERATOR_OPTIONS_INIT;
+
+ cl_git_pass(git_repository_head_tree(&head, repo));
+ cl_git_pass(git_repository_index(&index, repo));
+
+ workdir_opts.flags |= GIT_ITERATOR_INCLUDE_HASH;
+
+ cl_git_pass(git_iterator_for_tree(&head_iterator, head, NULL));
+ cl_git_pass(git_iterator_for_workdir(&workdir_iterator, repo, index, NULL, &workdir_opts));
+
+ iterators[0] = head_iterator;
+ iterators[1] = workdir_iterator;
+
+ cl_git_pass(git_iterator_walk(iterators, 2, iterator_eq, NULL));
+
+ git_iterator_free(head_iterator);
+ git_iterator_free(workdir_iterator);
+
+ git_tree_free(head);
+ git_index_free(index);
+}
diff --git a/tests/libgit2/apply/apply_helpers.h b/tests/libgit2/apply/apply_helpers.h
new file mode 100644
index 0000000..b1a1479
--- /dev/null
+++ b/tests/libgit2/apply/apply_helpers.h
@@ -0,0 +1,498 @@
+#include "../merge/merge_helpers.h"
+#include "../diff/diff_helpers.h"
+
+#define TEST_REPO_PATH "merge-recursive"
+
+#define DIFF_MODIFY_TWO_FILES \
+ "diff --git a/asparagus.txt b/asparagus.txt\n" \
+ "index f516580..ffb36e5 100644\n" \
+ "--- a/asparagus.txt\n" \
+ "+++ b/asparagus.txt\n" \
+ "@@ -1 +1 @@\n" \
+ "-ASPARAGUS SOUP!\n" \
+ "+ASPARAGUS SOUP.\n" \
+ "diff --git a/veal.txt b/veal.txt\n" \
+ "index 94d2c01..a7b0665 100644\n" \
+ "--- a/veal.txt\n" \
+ "+++ b/veal.txt\n" \
+ "@@ -1 +1 @@\n" \
+ "-VEAL SOUP!\n" \
+ "+VEAL SOUP.\n" \
+ "@@ -7 +7 @@ occasionally, then put into it a shin of veal, let it boil two hours\n" \
+ "-longer. take out the slices of ham, and skim off the grease if any\n" \
+ "+longer; take out the slices of ham, and skim off the grease if any\n"
+
+/* This is the binary equivalent of DIFF_MODIFY_TWO_FILES */
+#define DIFF_MODIFY_TWO_FILES_BINARY \
+ "diff --git a/asparagus.txt b/asparagus.txt\n" \
+ "index f51658077d85f2264fa179b4d0848268cb3475c3..ffb36e513f5fdf8a6ba850a20142676a2ac4807d 100644\n" \
+ "GIT binary patch\n" \
+ "delta 24\n" \
+ "fcmX@ja+-zTF*v|6$k9DCSRvRyG(c}7zYP-rT_OhP\n" \
+ "\n" \
+ "delta 24\n" \
+ "fcmX@ja+-zTF*v|6$k9DCSRvRyG(d49zYP-rT;T@W\n" \
+ "\n" \
+ "diff --git a/veal.txt b/veal.txt\n" \
+ "index 94d2c01087f48213bd157222d54edfefd77c9bba..a7b066537e6be7109abfe4ff97b675d4e077da20 100644\n" \
+ "GIT binary patch\n" \
+ "delta 26\n" \
+ "hcmX@kah!uI%+=9HA=p1OKyM?L03)OIW@$zpW&mXg25bNT\n" \
+ "\n" \
+ "delta 26\n" \
+ "hcmX@kah!uI%+=9HA=p1OKyf3N03)N`W@$zpW&mU#22ub3\n" \
+ "\n"
+
+#define DIFF_DELETE_FILE \
+ "diff --git a/gravy.txt b/gravy.txt\n" \
+ "deleted file mode 100644\n" \
+ "index c4e6cca..0000000\n" \
+ "--- a/gravy.txt\n" \
+ "+++ /dev/null\n" \
+ "@@ -1,8 +0,0 @@\n" \
+ "-GRAVY SOUP.\n" \
+ "-\n" \
+ "-Get eight pounds of coarse lean beef--wash it clean and lay it in your\n" \
+ "-pot, put in the same ingredients as for the shin soup, with the same\n" \
+ "-quantity of water, and follow the process directed for that. Strain the\n" \
+ "-soup through a sieve, and serve it up clear, with nothing more than\n" \
+ "-toasted bread in it; two table-spoonsful of mushroom catsup will add a\n" \
+ "-fine flavour to the soup.\n"
+
+#define DIFF_ADD_FILE \
+ "diff --git a/newfile.txt b/newfile.txt\n" \
+ "new file mode 100644\n" \
+ "index 0000000..6370543\n" \
+ "--- /dev/null\n" \
+ "+++ b/newfile.txt\n" \
+ "@@ -0,0 +1,2 @@\n" \
+ "+This is a new file!\n" \
+ "+Added by a patch.\n"
+
+#define DIFF_EXECUTABLE_FILE \
+ "diff --git a/beef.txt b/beef.txt\n" \
+ "old mode 100644\n" \
+ "new mode 100755\n"
+
+#define DIFF_MANY_CHANGES_ONE \
+ "diff --git a/veal.txt b/veal.txt\n" \
+ "index 94d2c01..c9d7d5d 100644\n" \
+ "--- a/veal.txt\n" \
+ "+++ b/veal.txt\n" \
+ "@@ -1,2 +1,2 @@\n" \
+ "-VEAL SOUP!\n" \
+ "+VEAL SOUP\n" \
+ " \n" \
+ "@@ -4,3 +4,2 @@\n" \
+ " spoonful of black pepper pounded, and two of salt, with two or three\n" \
+ "-slices of lean ham; let it boil steadily two hours; skim it\n" \
+ " occasionally, then put into it a shin of veal, let it boil two hours\n" \
+ "@@ -8,3 +7,3 @@\n" \
+ " should rise, take a gill of good cream, mix with it two table-spoonsful\n" \
+ "-of flour very nicely, and the yelks of two eggs beaten well, strain this\n" \
+ "+OF FLOUR very nicely, and the yelks of two eggs beaten well, strain this\n" \
+ " mixture, and add some chopped parsley; pour some soup on by degrees,\n" \
+ "@@ -12,2 +11,3 @@\n" \
+ " boiled two or three minutes to take off the raw taste of the eggs. If\n" \
+ "+Inserted line.\n" \
+ " the cream be not perfectly sweet, and the eggs quite new, the thickening\n" \
+ "@@ -15,3 +15,3 @@\n" \
+ " in, first taking off their skins, by letting them stand a few minutes in\n" \
+ "-hot water, when they may be easily peeled. When made in this way you\n" \
+ "+Changed line.\n" \
+ " must thicken it with the flour only. Any part of the veal may be used,\n"
+
+#define DIFF_MANY_CHANGES_TWO \
+ "diff --git a/veal.txt b/veal.txt\n" \
+ "index 94d2c01..6b943d6 100644\n" \
+ "--- a/veal.txt\n" \
+ "+++ b/veal.txt\n" \
+ "@@ -1,2 +1,2 @@\n" \
+ "-VEAL SOUP!\n" \
+ "+VEAL SOUP!!!\n" \
+ " \n" \
+ "@@ -4,3 +4,2 @@\n" \
+ " spoonful of black pepper pounded, and two of salt, with two or three\n" \
+ "-slices of lean ham; let it boil steadily two hours; skim it\n" \
+ " occasionally, then put into it a shin of veal, let it boil two hours\n" \
+ "@@ -8,3 +7,3 @@\n" \
+ " should rise, take a gill of good cream, mix with it two table-spoonsful\n" \
+ "-of flour very nicely, and the yelks of two eggs beaten well, strain this\n" \
+ "+of flour very nicely, AND the yelks of two eggs beaten well, strain this\n" \
+ " mixture, and add some chopped parsley; pour some soup on by degrees,\n" \
+ "@@ -12,2 +11,3 @@\n" \
+ " boiled two or three minutes to take off the raw taste of the eggs. If\n" \
+ "+New line.\n" \
+ " the cream be not perfectly sweet, and the eggs quite new, the thickening\n" \
+ "@@ -15,4 +15,5 @@\n" \
+ " in, first taking off their skins, by letting them stand a few minutes in\n" \
+ "-hot water, when they may be easily peeled. When made in this way you\n" \
+ "-must thicken it with the flour only. Any part of the veal may be used,\n" \
+ "-but the shin or knuckle is the nicest.\n" \
+ "+HOT water, when they may be easily peeled. When made in this way you\n" \
+ "+must THICKEN it with the flour only. Any part of the veal may be used,\n" \
+ "+but the shin OR knuckle is the nicest.\n" \
+ "+Another new line.\n" \
+
+#define DIFF_RENAME_FILE \
+ "diff --git a/beef.txt b/notbeef.txt\n" \
+ "similarity index 100%\n" \
+ "rename from beef.txt\n" \
+ "rename to notbeef.txt\n"
+
+#define DIFF_RENAME_AND_MODIFY_FILE \
+ "diff --git a/beef.txt b/notbeef.txt\n" \
+ "similarity index 97%\n" \
+ "rename from beef.txt\n" \
+ "rename to notbeef.txt\n" \
+ "index 68f6182..6fa1014 100644\n" \
+ "--- a/beef.txt\n" \
+ "+++ b/notbeef.txt\n" \
+ "@@ -1,4 +1,4 @@\n" \
+ "-BEEF SOUP.\n" \
+ "+THIS IS NOT BEEF SOUP, IT HAS A NEW NAME.\n" \
+ "\n" \
+ " Take the hind shin of beef, cut off all the flesh off the leg-bone,\n" \
+ " which must be taken away entirely, or the soup will be greasy. Wash the\n"
+
+#define DIFF_RENAME_A_TO_B_TO_C \
+ "diff --git a/asparagus.txt b/asparagus.txt\n" \
+ "deleted file mode 100644\n" \
+ "index f516580..0000000\n" \
+ "--- a/asparagus.txt\n" \
+ "+++ /dev/null\n" \
+ "@@ -1,10 +0,0 @@\n" \
+ "-ASPARAGUS SOUP!\n" \
+ "-\n" \
+ "-Take four large bunches of asparagus, scrape it nicely, cut off one inch\n" \
+ "-of the tops, and lay them in water, chop the stalks and put them on the\n" \
+ "-fire with a piece of bacon, a large onion cut up, and pepper and salt;\n" \
+ "-add two quarts of water, boil them till the stalks are quite soft, then\n" \
+ "-pulp them through a sieve, and strain the water to it, which must be put\n" \
+ "-back in the pot; put into it a chicken cut up, with the tops of\n" \
+ "-asparagus which had been laid by, boil it until these last articles are\n" \
+ "-sufficiently done, thicken with flour, butter and milk, and serve it up.\n" \
+ "diff --git a/beef.txt b/beef.txt\n" \
+ "index 68f6182..f516580 100644\n" \
+ "--- a/beef.txt\n" \
+ "+++ b/beef.txt\n" \
+ "@@ -1,22 +1,10 @@\n" \
+ "-BEEF SOUP.\n" \
+ "+ASPARAGUS SOUP!\n" \
+ "\n" \
+ "-Take the hind shin of beef, cut off all the flesh off the leg-bone,\n" \
+ "-which must be taken away entirely, or the soup will be greasy. Wash the\n" \
+ "-meat clean and lay it in a pot, sprinkle over it one small\n" \
+ "-table-spoonful of pounded black pepper, and two of salt; three onions\n" \
+ "-the size of a hen's egg, cut small, six small carrots scraped and cut\n" \
+ "-up, two small turnips pared and cut into dice; pour on three quarts of\n" \
+ "-water, cover the pot close, and keep it gently and steadily boiling five\n" \
+ "-hours, which will leave about three pints of clear soup; do not let the\n" \
+ "-pot boil over, but take off the scum carefully, as it rises. When it has\n" \
+ "-boiled four hours, put in a small bundle of thyme and parsley, and a\n" \
+ "-pint of celery cut small, or a tea-spoonful of celery seed pounded.\n" \
+ "-These latter ingredients would lose their delicate flavour if boiled too\n" \
+ "-much. Just before you take it up, brown it in the following manner: put\n" \
+ "-a small table-spoonful of nice brown sugar into an iron skillet, set it\n" \
+ "-on the fire and stir it till it melts and looks very dark, pour into it\n" \
+ "-a ladle full of the soup, a little at a time; stirring it all the while.\n" \
+ "-Strain this browning and mix it well with the soup; take out the bundle\n" \
+ "-of thyme and parsley, put the nicest pieces of meat in your tureen, and\n" \
+ "-pour on the soup and vegetables; put in some toasted bread cut in dice,\n" \
+ "-and serve it up.\n" \
+ "+Take four large bunches of asparagus, scrape it nicely, cut off one inch\n" \
+ "+of the tops, and lay them in water, chop the stalks and put them on the\n" \
+ "+fire with a piece of bacon, a large onion cut up, and pepper and salt;\n" \
+ "+add two quarts of water, boil them till the stalks are quite soft, then\n" \
+ "+pulp them through a sieve, and strain the water to it, which must be put\n" \
+ "+back in the pot; put into it a chicken cut up, with the tops of\n" \
+ "+asparagus which had been laid by, boil it until these last articles are\n" \
+ "+sufficiently done, thicken with flour, butter and milk, and serve it up.\n" \
+ "diff --git a/notbeef.txt b/notbeef.txt\n" \
+ "new file mode 100644\n" \
+ "index 0000000..68f6182\n" \
+ "--- /dev/null\n" \
+ "+++ b/notbeef.txt\n" \
+ "@@ -0,0 +1,22 @@\n" \
+ "+BEEF SOUP.\n" \
+ "+\n" \
+ "+Take the hind shin of beef, cut off all the flesh off the leg-bone,\n" \
+ "+which must be taken away entirely, or the soup will be greasy. Wash the\n" \
+ "+meat clean and lay it in a pot, sprinkle over it one small\n" \
+ "+table-spoonful of pounded black pepper, and two of salt; three onions\n" \
+ "+the size of a hen's egg, cut small, six small carrots scraped and cut\n" \
+ "+up, two small turnips pared and cut into dice; pour on three quarts of\n" \
+ "+water, cover the pot close, and keep it gently and steadily boiling five\n" \
+ "+hours, which will leave about three pints of clear soup; do not let the\n" \
+ "+pot boil over, but take off the scum carefully, as it rises. When it has\n" \
+ "+boiled four hours, put in a small bundle of thyme and parsley, and a\n" \
+ "+pint of celery cut small, or a tea-spoonful of celery seed pounded.\n" \
+ "+These latter ingredients would lose their delicate flavour if boiled too\n" \
+ "+much. Just before you take it up, brown it in the following manner: put\n" \
+ "+a small table-spoonful of nice brown sugar into an iron skillet, set it\n" \
+ "+on the fire and stir it till it melts and looks very dark, pour into it\n" \
+ "+a ladle full of the soup, a little at a time; stirring it all the while.\n" \
+ "+Strain this browning and mix it well with the soup; take out the bundle\n" \
+ "+of thyme and parsley, put the nicest pieces of meat in your tureen, and\n" \
+ "+pour on the soup and vegetables; put in some toasted bread cut in dice,\n" \
+ "+and serve it up.\n"
+
+#define DIFF_RENAME_A_TO_B_TO_C_EXACT \
+ "diff --git a/asparagus.txt b/beef.txt\n" \
+ "similarity index 100%\n" \
+ "rename from asparagus.txt\n" \
+ "rename to beef.txt\n" \
+ "diff --git a/beef.txt b/notbeef.txt\n" \
+ "similarity index 100%\n" \
+ "rename from beef.txt\n" \
+ "rename to notbeef.txt\n"
+
+#define DIFF_RENAME_CIRCULAR \
+ "diff --git a/asparagus.txt b/beef.txt\n" \
+ "similarity index 100%\n" \
+ "rename from asparagus.txt\n" \
+ "rename to beef.txt\n" \
+ "diff --git a/beef.txt b/notbeef.txt\n" \
+ "similarity index 100%\n" \
+ "rename from beef.txt\n" \
+ "rename to asparagus.txt\n"
+
+#define DIFF_RENAME_2_TO_1 \
+ "diff --git a/asparagus.txt b/2.txt\n" \
+ "similarity index 100%\n" \
+ "rename from asparagus.txt\n" \
+ "rename to 2.txt\n" \
+ "diff --git a/beef.txt b/2.txt\n" \
+ "similarity index 100%\n" \
+ "rename from beef.txt\n" \
+ "rename to 2.txt\n"
+
+#define DIFF_RENAME_1_TO_2 \
+ "diff --git a/asparagus.txt b/2.txt\n" \
+ "similarity index 100%\n" \
+ "rename from asparagus.txt\n" \
+ "rename to 1.txt\n" \
+ "diff --git a/asparagus.txt b/2.txt\n" \
+ "similarity index 100%\n" \
+ "rename from asparagus.txt\n" \
+ "rename to 2.txt\n"
+
+#define DIFF_TWO_DELTAS_ONE_FILE \
+ "diff --git a/beef.txt b/beef.txt\n" \
+ "index 68f6182..235069d 100644\n" \
+ "--- a/beef.txt\n" \
+ "+++ b/beef.txt\n" \
+ "@@ -1,4 +1,4 @@\n" \
+ "-BEEF SOUP.\n" \
+ "+BEEF SOUP!\n" \
+ "\n" \
+ " Take the hind shin of beef, cut off all the flesh off the leg-bone,\n" \
+ " which must be taken away entirely, or the soup will be greasy. Wash the\n" \
+ "diff --git a/beef.txt b/beef.txt\n" \
+ "index 68f6182..e059eb5 100644\n" \
+ "--- a/beef.txt\n" \
+ "+++ b/beef.txt\n" \
+ "@@ -19,4 +19,4 @@ a ladle full of the soup, a little at a time; stirring it all the while.\n" \
+ " Strain this browning and mix it well with the soup; take out the bundle\n" \
+ " of thyme and parsley, put the nicest pieces of meat in your tureen, and\n" \
+ " pour on the soup and vegetables; put in some toasted bread cut in dice,\n" \
+ "-and serve it up.\n" \
+ "+and serve it up!\n"
+
+#define DIFF_TWO_DELTAS_ONE_NEW_FILE \
+ "diff --git a/newfile.txt b/newfile.txt\n" \
+ "new file mode 100644\n" \
+ "index 0000000..6434b13\n" \
+ "--- /dev/null\n" \
+ "+++ b/newfile.txt\n" \
+ "@@ -0,0 +1 @@\n" \
+ "+This is a new file.\n" \
+ "diff --git a/newfile.txt b/newfile.txt\n" \
+ "index 6434b13..08d4c44 100644\n" \
+ "--- a/newfile.txt\n" \
+ "+++ b/newfile.txt\n" \
+ "@@ -1 +1,3 @@\n" \
+ " This is a new file.\n" \
+ "+\n" \
+ "+This is another change to a new file.\n"
+
+#define DIFF_RENAME_AND_MODIFY_DELTAS \
+ "diff --git a/veal.txt b/asdf.txt\n" \
+ "similarity index 96%\n" \
+ "rename from veal.txt\n" \
+ "rename to asdf.txt\n" \
+ "index 94d2c01..292cb60 100644\n" \
+ "--- a/veal.txt\n" \
+ "+++ b/asdf.txt\n" \
+ "@@ -15,4 +15,4 @@ will curdle in the soup. For a change you may put a dozen ripe tomatos\n" \
+ " in, first taking off their skins, by letting them stand a few minutes in\n" \
+ " hot water, when they may be easily peeled. When made in this way you\n" \
+ " must thicken it with the flour only. Any part of the veal may be used,\n" \
+ "-but the shin or knuckle is the nicest.\n" \
+ "+but the shin or knuckle is the nicest!\n" \
+ "diff --git a/asdf.txt b/asdf.txt\n" \
+ "index 292cb60..61c686b 100644\n" \
+ "--- a/asdf.txt\n" \
+ "+++ b/asdf.txt\n" \
+ "@@ -1,4 +1,4 @@\n" \
+ "-VEAL SOUP!\n" \
+ "+VEAL SOUP\n" \
+ "\n" \
+ " Put into a pot three quarts of water, three onions cut small, one\n" \
+ " spoonful of black pepper pounded, and two of salt, with two or three\n"
+
+#define DIFF_RENAME_AFTER_MODIFY \
+ "diff --git a/veal.txt b/veal.txt\n" \
+ "index 292cb60..61c686b 100644\n" \
+ "--- a/veal.txt\n" \
+ "+++ b/veal.txt\n" \
+ "@@ -1,4 +1,4 @@\n" \
+ "-VEAL SOUP!\n" \
+ "+VEAL SOUP\n" \
+ "\n" \
+ " Put into a pot three quarts of water, three onions cut small, one\n" \
+ " spoonful of black pepper pounded, and two of salt, with two or three\n" \
+ "diff --git a/veal.txt b/other.txt\n" \
+ "similarity index 96%\n" \
+ "rename from veal.txt\n" \
+ "rename to other.txt\n" \
+ "index 94d2c01..292cb60 100644\n" \
+ "--- a/veal.txt\n" \
+ "+++ b/other.txt\n" \
+ "@@ -15,4 +15,4 @@ will curdle in the soup. For a change you may put a dozen ripe tomatos\n" \
+ " in, first taking off their skins, by letting them stand a few minutes in\n" \
+ " hot water, when they may be easily peeled. When made in this way you\n" \
+ " must thicken it with the flour only. Any part of the veal may be used,\n" \
+ "-but the shin or knuckle is the nicest.\n" \
+ "+but the shin or knuckle is the nicest!\n"
+
+#define DIFF_RENAME_AFTER_MODIFY_TARGET_PATH \
+ "diff --git a/beef.txt b/beef.txt\n" \
+ "index 292cb60..61c686b 100644\n" \
+ "--- a/beef.txt\n" \
+ "+++ b/beef.txt\n" \
+ "@@ -1,4 +1,4 @@\n" \
+ "-VEAL SOUP!\n" \
+ "+VEAL SOUP\n" \
+ "\n" \
+ " Put into a pot three quarts of water, three onions cut small, one\n" \
+ " spoonful of black pepper pounded, and two of salt, with two or three\n" \
+ "diff --git a/veal.txt b/beef.txt\n" \
+ "similarity index 96%\n" \
+ "rename from veal.txt\n" \
+ "rename to beef.txt\n" \
+ "index 94d2c01..292cb60 100644\n" \
+ "--- a/veal.txt\n" \
+ "+++ b/beef.txt\n" \
+ "@@ -15,4 +15,4 @@ will curdle in the soup. For a change you may put a dozen ripe tomatos\n" \
+ " in, first taking off their skins, by letting them stand a few minutes in\n" \
+ " hot water, when they may be easily peeled. When made in this way you\n" \
+ " must thicken it with the flour only. Any part of the veal may be used,\n" \
+ "-but the shin or knuckle is the nicest.\n" \
+ "+but the shin or knuckle is the nicest!\n"
+
+#define DIFF_RENAME_AND_MODIFY_SOURCE_PATH \
+ "diff --git a/veal.txt b/asdf.txt\n" \
+ "similarity index 96%\n" \
+ "rename from veal.txt\n" \
+ "rename to asdf.txt\n" \
+ "index 94d2c01..292cb60 100644\n" \
+ "--- a/veal.txt\n" \
+ "+++ b/asdf.txt\n" \
+ "@@ -15,4 +15,4 @@ will curdle in the soup. For a change you may put a dozen ripe tomatos\n" \
+ " in, first taking off their skins, by letting them stand a few minutes in\n" \
+ " hot water, when they may be easily peeled. When made in this way you\n" \
+ " must thicken it with the flour only. Any part of the veal may be used,\n" \
+ "-but the shin or knuckle is the nicest.\n" \
+ "+but the shin or knuckle is the nicest!\n" \
+ "diff --git a/veal.txt b/veal.txt\n" \
+ "index 292cb60..61c686b 100644\n" \
+ "--- a/veal.txt\n" \
+ "+++ b/veal.txt\n" \
+ "@@ -1,4 +1,4 @@\n" \
+ "-VEAL SOUP!\n" \
+ "+VEAL SOUP\n" \
+ "\n" \
+ " Put into a pot three quarts of water, three onions cut small, one\n" \
+ " spoonful of black pepper pounded, and two of salt, with two or three\n"
+
+#define DIFF_DELETE_AND_READD_FILE \
+ "diff --git a/asparagus.txt b/asparagus.txt\n" \
+ "deleted file mode 100644\n" \
+ "index f516580..0000000\n" \
+ "--- a/asparagus.txt\n" \
+ "+++ /dev/null\n" \
+ "@@ -1,10 +0,0 @@\n" \
+ "-ASPARAGUS SOUP!\n" \
+ "-\n" \
+ "-Take four large bunches of asparagus, scrape it nicely, cut off one inch\n" \
+ "-of the tops, and lay them in water, chop the stalks and put them on the\n" \
+ "-fire with a piece of bacon, a large onion cut up, and pepper and salt;\n" \
+ "-add two quarts of water, boil them till the stalks are quite soft, then\n" \
+ "-pulp them through a sieve, and strain the water to it, which must be put\n" \
+ "-back in the pot; put into it a chicken cut up, with the tops of\n" \
+ "-asparagus which had been laid by, boil it until these last articles are\n" \
+ "-sufficiently done, thicken with flour, butter and milk, and serve it up.\n" \
+ "diff --git a/asparagus.txt b/asparagus.txt\n" \
+ "new file mode 100644\n" \
+ "index 0000000..2dc7f8b\n" \
+ "--- /dev/null\n" \
+ "+++ b/asparagus.txt\n" \
+ "@@ -0,0 +1 @@\n" \
+ "+New file.\n" \
+
+#define DIFF_REMOVE_FILE_TWICE \
+ "diff --git a/asparagus.txt b/asparagus.txt\n" \
+ "deleted file mode 100644\n" \
+ "index f516580..0000000\n" \
+ "--- a/asparagus.txt\n" \
+ "+++ /dev/null\n" \
+ "@@ -1,10 +0,0 @@\n" \
+ "-ASPARAGUS SOUP!\n" \
+ "-\n" \
+ "-Take four large bunches of asparagus, scrape it nicely, cut off one inch\n" \
+ "-of the tops, and lay them in water, chop the stalks and put them on the\n" \
+ "-fire with a piece of bacon, a large onion cut up, and pepper and salt;\n" \
+ "-add two quarts of water, boil them till the stalks are quite soft, then\n" \
+ "-pulp them through a sieve, and strain the water to it, which must be put\n" \
+ "-back in the pot; put into it a chicken cut up, with the tops of\n" \
+ "-asparagus which had been laid by, boil it until these last articles are\n" \
+ "-sufficiently done, thicken with flour, butter and milk, and serve it up.\n" \
+ "diff --git a/asparagus.txt b/asparagus.txt\n" \
+ "deleted file mode 100644\n" \
+ "index f516580..0000000\n" \
+ "--- a/asparagus.txt\n" \
+ "+++ /dev/null\n" \
+ "@@ -1,10 +0,0 @@\n" \
+ "-ASPARAGUS SOUP!\n" \
+ "-\n" \
+ "-Take four large bunches of asparagus, scrape it nicely, cut off one inch\n" \
+ "-of the tops, and lay them in water, chop the stalks and put them on the\n" \
+ "-fire with a piece of bacon, a large onion cut up, and pepper and salt;\n" \
+ "-add two quarts of water, boil them till the stalks are quite soft, then\n" \
+ "-pulp them through a sieve, and strain the water to it, which must be put\n" \
+ "-back in the pot; put into it a chicken cut up, with the tops of\n" \
+ "-asparagus which had been laid by, boil it until these last articles are\n" \
+ "-sufficiently done, thicken with flour, butter and milk, and serve it up.\n"
+
+#define DIFF_ADD_INVALID_FILENAME \
+ "diff --git a/.git/hello_world.txt b/.git/hello_world.txt\n" \
+ "new file mode 100644\n" \
+ "index 0000000..f75ba05\n" \
+ "--- /dev/null\n" \
+ "+++ b/.git/hello_world.txt\n" \
+ "@@ -0,0 +1 @@\n" \
+ "+Hello, world.\n"
+
+void validate_apply_workdir(
+ git_repository *repo,
+ struct merge_index_entry *workdir_entries,
+ size_t workdir_cnt);
+
+void validate_apply_index(
+ git_repository *repo,
+ struct merge_index_entry *index_entries,
+ size_t index_cnt);
+
+void validate_index_unchanged(git_repository *repo);
+void validate_workdir_unchanged(git_repository *repo);
diff --git a/tests/libgit2/apply/both.c b/tests/libgit2/apply/both.c
new file mode 100644
index 0000000..44c5b19
--- /dev/null
+++ b/tests/libgit2/apply/both.c
@@ -0,0 +1,747 @@
+#include "clar_libgit2.h"
+#include "apply_helpers.h"
+
+static git_repository *repo;
+
+#define TEST_REPO_PATH "merge-recursive"
+
+void test_apply_both__initialize(void)
+{
+ git_oid oid;
+ git_commit *commit;
+
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+
+ git_oid__fromstr(&oid, "539bd011c4822c560c1d17cab095006b7a10f707", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+ cl_git_pass(git_reset(repo, (git_object *)commit, GIT_RESET_HARD, NULL));
+ git_commit_free(commit);
+}
+
+void test_apply_both__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_apply_both__generated_diff(void)
+{
+ git_oid a_oid, b_oid;
+ git_commit *a_commit, *b_commit;
+ git_tree *a_tree, *b_tree;
+ git_diff *diff;
+ git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
+
+ struct merge_index_entry both_expected[] = {
+ { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
+ };
+ size_t both_expected_cnt = sizeof(both_expected) /
+ sizeof(struct merge_index_entry);
+
+ git_oid__fromstr(&a_oid, "539bd011c4822c560c1d17cab095006b7a10f707", GIT_OID_SHA1);
+ git_oid__fromstr(&b_oid, "7c7bf85e978f1d18c0566f702d2cb7766b9c8d4f", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&a_commit, repo, &a_oid));
+ cl_git_pass(git_commit_lookup(&b_commit, repo, &b_oid));
+
+ cl_git_pass(git_commit_tree(&a_tree, a_commit));
+ cl_git_pass(git_commit_tree(&b_tree, b_commit));
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, repo, a_tree, b_tree, &diff_opts));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
+
+ validate_apply_index(repo, both_expected, both_expected_cnt);
+ validate_apply_workdir(repo, both_expected, both_expected_cnt);
+
+ git_diff_free(diff);
+ git_tree_free(a_tree);
+ git_tree_free(b_tree);
+ git_commit_free(a_commit);
+ git_commit_free(b_commit);
+}
+
+void test_apply_both__parsed_diff(void)
+{
+ git_diff *diff;
+
+ struct merge_index_entry both_expected[] = {
+ { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
+ };
+ size_t both_expected_cnt = sizeof(both_expected) /
+ sizeof(struct merge_index_entry);
+
+ cl_git_pass(diff_from_buffer(&diff,
+ DIFF_MODIFY_TWO_FILES, strlen(DIFF_MODIFY_TWO_FILES)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
+
+ validate_apply_index(repo, both_expected, both_expected_cnt);
+ validate_apply_workdir(repo, both_expected, both_expected_cnt);
+
+ git_diff_free(diff);
+}
+
+void test_apply_both__removes_file(void)
+{
+ git_diff *diff;
+
+ struct merge_index_entry both_expected[] = {
+ { 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" },
+ };
+ size_t both_expected_cnt = sizeof(both_expected) /
+ sizeof(struct merge_index_entry);
+
+ cl_git_pass(diff_from_buffer(&diff, DIFF_DELETE_FILE,
+ strlen(DIFF_DELETE_FILE)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
+
+ validate_apply_index(repo, both_expected, both_expected_cnt);
+ validate_apply_workdir(repo, both_expected, both_expected_cnt);
+
+ git_diff_free(diff);
+}
+
+void test_apply_both__adds_file(void)
+{
+ git_diff *diff;
+
+ struct merge_index_entry both_expected[] = {
+ { 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "6370543fcfedb3e6516ec53b06158f3687dc1447", 0, "newfile.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" },
+ };
+ size_t both_expected_cnt = sizeof(both_expected) /
+ sizeof(struct merge_index_entry);
+
+ cl_git_pass(diff_from_buffer(&diff,
+ DIFF_ADD_FILE, strlen(DIFF_ADD_FILE)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
+
+ validate_apply_index(repo, both_expected, both_expected_cnt);
+ validate_apply_workdir(repo, both_expected, both_expected_cnt);
+
+ git_diff_free(diff);
+}
+
+void test_apply_both__application_failure_leaves_index_unmodified(void)
+{
+ git_diff *diff;
+ git_index *index;
+
+ const char *diff_file = DIFF_MODIFY_TWO_FILES;
+
+ struct merge_index_entry index_expected[] = {
+ { 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ };
+ size_t index_expected_cnt = sizeof(index_expected) /
+ sizeof(struct merge_index_entry);
+
+ /* mutate the index */
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_remove(index, "veal.txt", 0));
+ cl_git_pass(git_index_write(index));
+ git_index_free(index);
+
+ cl_git_pass(diff_from_buffer(&diff, diff_file, strlen(diff_file)));
+ cl_git_fail_with(GIT_EAPPLYFAIL, git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
+
+ validate_apply_index(repo, index_expected, index_expected_cnt);
+ validate_workdir_unchanged(repo);
+
+ git_diff_free(diff);
+}
+
+void test_apply_both__index_must_match_workdir(void)
+{
+ git_diff *diff;
+ git_index *index;
+ git_index_entry idx_entry;
+
+ const char *diff_file = DIFF_MODIFY_TWO_FILES;
+
+ /*
+ * Append a line to the end of the file in both the index and the
+ * working directory. Although the appended line would allow for
+ * patch application in each, the line appended is different in
+ * each, so the application should not be allowed.
+ */
+ cl_git_append2file("merge-recursive/asparagus.txt",
+ "This is a modification.\n");
+
+ cl_git_pass(git_repository_index(&index, repo));
+
+ memset(&idx_entry, 0, sizeof(git_index_entry));
+ idx_entry.mode = 0100644;
+ idx_entry.path = "asparagus.txt";
+ cl_git_pass(git_oid__fromstr(&idx_entry.id, "06d3fefb8726ab1099acc76e02dfb85e034b2538", GIT_OID_SHA1));
+ cl_git_pass(git_index_add(index, &idx_entry));
+
+ cl_git_pass(git_index_write(index));
+ git_index_free(index);
+
+ cl_git_pass(diff_from_buffer(&diff, diff_file, strlen(diff_file)));
+ cl_git_fail_with(GIT_EAPPLYFAIL, git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
+
+ git_diff_free(diff);
+}
+
+void test_apply_both__index_mode_must_match_workdir(void)
+{
+ git_diff *diff;
+
+ if (!cl_is_chmod_supported())
+ clar__skip();
+
+ /* Set a file in the working directory executable. */
+ cl_must_pass(p_chmod("merge-recursive/asparagus.txt", 0755));
+
+ cl_git_pass(diff_from_buffer(&diff, DIFF_MODIFY_TWO_FILES,
+ strlen(DIFF_MODIFY_TWO_FILES)));
+ cl_git_fail_with(GIT_EAPPLYFAIL, git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
+
+ git_diff_free(diff);
+}
+
+void test_apply_both__application_failure_leaves_workdir_unmodified(void)
+{
+ git_diff *diff;
+ git_index *index;
+
+ const char *diff_file = DIFF_MODIFY_TWO_FILES;
+
+ struct merge_index_entry workdir_expected[] = {
+ { 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "8684724651336001c5dbce74bed6736d2443958d", 0, "veal.txt" },
+ };
+ size_t workdir_expected_cnt = sizeof(workdir_expected) /
+ sizeof(struct merge_index_entry);
+
+ /* mutate the workdir */
+ cl_git_rewritefile("merge-recursive/veal.txt",
+ "This is a modification.\n");
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_add_bypath(index, "veal.txt"));
+ cl_git_pass(git_index_write(index));
+ git_index_free(index);
+
+ cl_git_pass(diff_from_buffer(&diff, diff_file, strlen(diff_file)));
+ cl_git_fail_with(GIT_EAPPLYFAIL, git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
+
+ validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
+
+ git_diff_free(diff);
+}
+
+void test_apply_both__keeps_nonconflicting_changes(void)
+{
+ git_diff *diff;
+ git_index *index;
+ git_index_entry idx_entry;
+
+ const char *diff_file = DIFF_MODIFY_TWO_FILES;
+
+ struct merge_index_entry index_expected[] = {
+ { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
+ { 0100644, "898d12687fb35be271c27c795a6b32c8b51da79e", 0, "beef.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
+ };
+ size_t index_expected_cnt = sizeof(index_expected) /
+ sizeof(struct merge_index_entry);
+
+ struct merge_index_entry workdir_expected[] = {
+ { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "f75ba05f340c51065cbea2e1fdbfe5fe13144c97", 0, "gravy.txt" },
+ { 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
+ };
+ size_t workdir_expected_cnt = sizeof(workdir_expected) /
+ sizeof(struct merge_index_entry);
+
+ /* mutate the index */
+ cl_git_pass(git_repository_index(&index, repo));
+
+ memset(&idx_entry, 0, sizeof(git_index_entry));
+ idx_entry.mode = 0100644;
+ idx_entry.path = "beef.txt";
+ cl_git_pass(git_oid__fromstr(&idx_entry.id, "898d12687fb35be271c27c795a6b32c8b51da79e", GIT_OID_SHA1));
+ cl_git_pass(git_index_add(index, &idx_entry));
+
+ cl_git_pass(git_index_remove(index, "bouilli.txt", 0));
+ cl_git_pass(git_index_write(index));
+ git_index_free(index);
+
+ /* and mutate the working directory */
+ cl_git_rmfile("merge-recursive/oyster.txt");
+ cl_git_rewritefile("merge-recursive/gravy.txt", "Hello, world.\n");
+
+ cl_git_pass(diff_from_buffer(&diff, diff_file, strlen(diff_file)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
+
+ validate_apply_index(repo, index_expected, index_expected_cnt);
+ validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
+
+ git_diff_free(diff);
+}
+
+void test_apply_both__can_apply_nonconflicting_file_changes(void)
+{
+ git_diff *diff;
+ git_index *index;
+
+ const char *diff_file = DIFF_MODIFY_TWO_FILES;
+
+ struct merge_index_entry both_expected[] = {
+ { 0100644, "f8a701c8a1a22c1729ee50faff1111f2d64f96fc", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
+ };
+ size_t both_expected_cnt = sizeof(both_expected) /
+ sizeof(struct merge_index_entry);
+
+ /*
+ * Replace the workdir file with a version that is different than
+ * HEAD but such that the patch still applies cleanly. This item
+ * has a new line appended.
+ */
+ cl_git_append2file("merge-recursive/asparagus.txt",
+ "This line is added in the index and the workdir.\n");
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_add_bypath(index, "asparagus.txt"));
+ cl_git_pass(git_index_write(index));
+ git_index_free(index);
+
+ cl_git_pass(diff_from_buffer(&diff, diff_file, strlen(diff_file)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
+
+ validate_apply_index(repo, both_expected, both_expected_cnt);
+ validate_apply_workdir(repo, both_expected, both_expected_cnt);
+
+ git_diff_free(diff);
+}
+
+void test_apply_both__honors_crlf_attributes(void)
+{
+ git_diff *diff;
+ git_oid oid;
+ git_commit *commit;
+
+ const char *diff_file = DIFF_MODIFY_TWO_FILES;
+
+ struct merge_index_entry index_expected[] = {
+ { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
+ };
+ size_t index_expected_cnt = sizeof(index_expected) /
+ sizeof(struct merge_index_entry);
+
+ struct merge_index_entry workdir_expected[] = {
+ { 0100644, "176a458f94e0ea5272ce67c36bf30b6be9caf623", 0, ".gitattributes" },
+ { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
+ };
+ size_t workdir_expected_cnt = sizeof(workdir_expected) /
+ sizeof(struct merge_index_entry);
+
+ cl_git_mkfile("merge-recursive/.gitattributes", "* text=auto\n");
+
+ cl_git_rmfile("merge-recursive/asparagus.txt");
+ cl_git_rmfile("merge-recursive/veal.txt");
+
+ git_oid__fromstr(&oid, "539bd011c4822c560c1d17cab095006b7a10f707", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+ cl_git_pass(git_reset(repo, (git_object *)commit, GIT_RESET_HARD, NULL));
+ git_commit_free(commit);
+
+ cl_git_pass(diff_from_buffer(&diff, diff_file, strlen(diff_file)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
+
+ validate_apply_index(repo, index_expected, index_expected_cnt);
+ validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
+
+ git_diff_free(diff);
+}
+
+void test_apply_both__rename(void)
+{
+ git_diff *diff;
+
+ struct merge_index_entry both_expected[] = {
+ { 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "notbeef.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" },
+ };
+ size_t both_expected_cnt = sizeof(both_expected) /
+ sizeof(struct merge_index_entry);
+
+ cl_git_pass(diff_from_buffer(&diff, DIFF_RENAME_FILE,
+ strlen(DIFF_RENAME_FILE)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
+
+ validate_apply_index(repo, both_expected, both_expected_cnt);
+ validate_apply_workdir(repo, both_expected, both_expected_cnt);
+
+ git_diff_free(diff);
+}
+
+void test_apply_both__rename_and_modify(void)
+{
+ git_diff *diff;
+
+ struct merge_index_entry both_expected[] = {
+ { 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "6fa10147f00fe1fab1d5e835529a9dad53db8552", 0, "notbeef.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" },
+ };
+ size_t both_expected_cnt = sizeof(both_expected) /
+ sizeof(struct merge_index_entry);
+
+ cl_git_pass(diff_from_buffer(&diff, DIFF_RENAME_AND_MODIFY_FILE,
+ strlen(DIFF_RENAME_AND_MODIFY_FILE)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
+
+ validate_apply_index(repo, both_expected, both_expected_cnt);
+ validate_apply_workdir(repo, both_expected, both_expected_cnt);
+
+ git_diff_free(diff);
+}
+
+void test_apply_both__rename_a_to_b_to_c(void)
+{
+ git_diff *diff;
+
+ struct merge_index_entry both_expected[] = {
+ { 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "notbeef.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" },
+ };
+ size_t both_expected_cnt = sizeof(both_expected) /
+ sizeof(struct merge_index_entry);
+
+ cl_git_pass(diff_from_buffer(&diff, DIFF_RENAME_A_TO_B_TO_C,
+ strlen(DIFF_RENAME_A_TO_B_TO_C)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
+
+ validate_apply_index(repo, both_expected, both_expected_cnt);
+ validate_apply_workdir(repo, both_expected, both_expected_cnt);
+
+ git_diff_free(diff);
+}
+
+void test_apply_both__rename_a_to_b_to_c_exact(void)
+{
+ git_diff *diff;
+
+ struct merge_index_entry both_expected[] = {
+ { 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "notbeef.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" },
+ };
+ size_t both_expected_cnt = sizeof(both_expected) /
+ sizeof(struct merge_index_entry);
+
+ cl_git_pass(diff_from_buffer(&diff, DIFF_RENAME_A_TO_B_TO_C_EXACT,
+ strlen(DIFF_RENAME_A_TO_B_TO_C_EXACT)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
+
+ validate_apply_index(repo, both_expected, both_expected_cnt);
+ validate_apply_workdir(repo, both_expected, both_expected_cnt);
+
+ git_diff_free(diff);
+}
+
+void test_apply_both__rename_circular(void)
+{
+ git_diff *diff;
+
+ struct merge_index_entry both_expected[] = {
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "asparagus.txt" },
+ { 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" }
+ };
+ size_t both_expected_cnt = sizeof(both_expected) /
+ sizeof(struct merge_index_entry);
+
+ cl_git_pass(diff_from_buffer(&diff, DIFF_RENAME_CIRCULAR,
+ strlen(DIFF_RENAME_CIRCULAR)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
+
+ validate_apply_index(repo, both_expected, both_expected_cnt);
+ validate_apply_workdir(repo, both_expected, both_expected_cnt);
+
+ git_diff_free(diff);
+}
+
+void test_apply_both__rename_2_to_1(void)
+{
+ git_diff *diff;
+
+ struct merge_index_entry both_expected[] = {
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "2.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" }
+ };
+ size_t both_expected_cnt = sizeof(both_expected) /
+ sizeof(struct merge_index_entry);
+
+ cl_git_pass(diff_from_buffer(&diff, DIFF_RENAME_2_TO_1,
+ strlen(DIFF_RENAME_2_TO_1)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
+
+ validate_apply_index(repo, both_expected, both_expected_cnt);
+ validate_apply_workdir(repo, both_expected, both_expected_cnt);
+
+ git_diff_free(diff);
+}
+
+void test_apply_both__rename_1_to_2(void)
+{
+ git_diff *diff;
+
+ struct merge_index_entry both_expected[] = {
+ { 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "1.txt" },
+ { 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "2.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" }
+ };
+ size_t both_expected_cnt = sizeof(both_expected) /
+ sizeof(struct merge_index_entry);
+
+ cl_git_pass(diff_from_buffer(&diff, DIFF_RENAME_1_TO_2,
+ strlen(DIFF_RENAME_1_TO_2)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
+
+ validate_apply_index(repo, both_expected, both_expected_cnt);
+ validate_apply_workdir(repo, both_expected, both_expected_cnt);
+
+ git_diff_free(diff);
+}
+
+void test_apply_both__two_deltas_one_file(void)
+{
+ git_diff *diff;
+
+ struct merge_index_entry both_expected[] = {
+ { 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
+ { 0100644, "0a9fd4415635e72573f0f6b5e68084cfe18f5075", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" }
+ };
+ size_t both_expected_cnt = sizeof(both_expected) /
+ sizeof(struct merge_index_entry);
+
+ cl_git_pass(diff_from_buffer(&diff, DIFF_TWO_DELTAS_ONE_FILE,
+ strlen(DIFF_TWO_DELTAS_ONE_FILE)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
+
+ validate_apply_index(repo, both_expected, both_expected_cnt);
+ validate_apply_workdir(repo, both_expected, both_expected_cnt);
+
+ git_diff_free(diff);
+}
+
+void test_apply_both__two_deltas_one_new_file(void)
+{
+ git_diff *diff;
+
+ struct merge_index_entry both_expected[] = {
+ { 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "08d4c445cf0078f3d9b604b82f32f4d87e083325", 0, "newfile.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" }
+ };
+ size_t both_expected_cnt = sizeof(both_expected) /
+ sizeof(struct merge_index_entry);
+
+ cl_git_pass(diff_from_buffer(&diff, DIFF_TWO_DELTAS_ONE_NEW_FILE,
+ strlen(DIFF_TWO_DELTAS_ONE_NEW_FILE)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
+
+ validate_apply_index(repo, both_expected, both_expected_cnt);
+ validate_apply_workdir(repo, both_expected, both_expected_cnt);
+
+ git_diff_free(diff);
+}
+
+void test_apply_both__rename_and_modify_deltas(void)
+{
+ git_diff *diff;
+
+ struct merge_index_entry both_expected[] = {
+ { 0100644, "61c686bed39684eee8a2757ceb1291004a21333f", 0, "asdf.txt" },
+ { 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ };
+ size_t both_expected_cnt = sizeof(both_expected) /
+ sizeof(struct merge_index_entry);
+
+ cl_git_pass(diff_from_buffer(&diff, DIFF_RENAME_AND_MODIFY_DELTAS,
+ strlen(DIFF_RENAME_AND_MODIFY_DELTAS)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
+
+ validate_apply_index(repo, both_expected, both_expected_cnt);
+ validate_apply_workdir(repo, both_expected, both_expected_cnt);
+
+ git_diff_free(diff);
+}
+
+void test_apply_both__rename_delta_after_modify_delta(void)
+{
+ git_diff *diff;
+
+ struct merge_index_entry both_expected[] = {
+ { 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "292cb60ce5e25c337c5b6e12957bbbfe1be4bf49", 0, "other.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "c8c120f466591bbe3b8867361d5ec3cdd9fda756", 0, "veal.txt" }
+ };
+ size_t both_expected_cnt = sizeof(both_expected) /
+ sizeof(struct merge_index_entry);
+
+ cl_git_pass(diff_from_buffer(&diff, DIFF_RENAME_AFTER_MODIFY,
+ strlen(DIFF_RENAME_AFTER_MODIFY)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
+
+ validate_apply_index(repo, both_expected, both_expected_cnt);
+ validate_apply_workdir(repo, both_expected, both_expected_cnt);
+
+ git_diff_free(diff);
+}
+
+void test_apply_both__cant_rename_after_modify_nonexistent_target_path(void)
+{
+ git_diff *diff;
+
+ cl_git_pass(diff_from_buffer(&diff, DIFF_RENAME_AFTER_MODIFY_TARGET_PATH,
+ strlen(DIFF_RENAME_AFTER_MODIFY_TARGET_PATH)));
+ cl_git_fail(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
+
+ git_diff_free(diff);
+}
+
+void test_apply_both__cant_modify_source_path_after_rename(void)
+{
+ git_diff *diff;
+
+ cl_git_pass(diff_from_buffer(&diff, DIFF_RENAME_AND_MODIFY_SOURCE_PATH,
+ strlen(DIFF_RENAME_AND_MODIFY_SOURCE_PATH)));
+ cl_git_fail(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
+
+ git_diff_free(diff);
+}
+
+void test_apply_both__readd_deleted_file(void)
+{
+ git_diff *diff;
+
+ struct merge_index_entry both_expected[] = {
+ { 0100644, "2dc7f8b24ba27f3888368bd180df03ff4c6c6fab", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" }
+ };
+ size_t both_expected_cnt = sizeof(both_expected) /
+ sizeof(struct merge_index_entry);
+
+ cl_git_pass(diff_from_buffer(&diff, DIFF_DELETE_AND_READD_FILE,
+ strlen(DIFF_DELETE_AND_READD_FILE)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
+
+ validate_apply_index(repo, both_expected, both_expected_cnt);
+ validate_apply_workdir(repo, both_expected, both_expected_cnt);
+
+ git_diff_free(diff);
+}
+
+void test_apply_both__cant_remove_file_twice(void)
+{
+ git_diff *diff;
+
+ cl_git_pass(diff_from_buffer(&diff, DIFF_REMOVE_FILE_TWICE,
+ strlen(DIFF_REMOVE_FILE_TWICE)));
+ cl_git_fail(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
+
+ git_diff_free(diff);
+}
+
+void test_apply_both__cant_add_invalid_filename(void)
+{
+ git_diff *diff;
+
+ cl_git_pass(diff_from_buffer(&diff, DIFF_ADD_INVALID_FILENAME,
+ strlen(DIFF_ADD_INVALID_FILENAME)));
+ cl_git_fail(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, NULL));
+
+ git_diff_free(diff);
+}
diff --git a/tests/libgit2/apply/callbacks.c b/tests/libgit2/apply/callbacks.c
new file mode 100644
index 0000000..f076ca4
--- /dev/null
+++ b/tests/libgit2/apply/callbacks.c
@@ -0,0 +1,128 @@
+#include "clar_libgit2.h"
+#include "apply_helpers.h"
+
+static git_repository *repo;
+
+#define TEST_REPO_PATH "merge-recursive"
+
+void test_apply_callbacks__initialize(void)
+{
+ git_oid oid;
+ git_commit *commit;
+
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+
+ git_oid__fromstr(&oid, "539bd011c4822c560c1d17cab095006b7a10f707", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+ cl_git_pass(git_reset(repo, (git_object *)commit, GIT_RESET_HARD, NULL));
+ git_commit_free(commit);
+}
+
+void test_apply_callbacks__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static int delta_abort_cb(const git_diff_delta *delta, void *payload)
+{
+ GIT_UNUSED(payload);
+
+ if (!strcmp(delta->old_file.path, "veal.txt"))
+ return -99;
+
+ return 0;
+}
+
+void test_apply_callbacks__delta_aborts(void)
+{
+ git_diff *diff;
+ git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
+
+ opts.delta_cb = delta_abort_cb;
+
+ cl_git_pass(diff_from_buffer(&diff,
+ DIFF_MODIFY_TWO_FILES, strlen(DIFF_MODIFY_TWO_FILES)));
+ cl_git_fail_with(-99,
+ git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, &opts));
+
+ validate_index_unchanged(repo);
+ validate_workdir_unchanged(repo);
+
+ git_diff_free(diff);
+}
+
+static int delta_skip_cb(const git_diff_delta *delta, void *payload)
+{
+ GIT_UNUSED(payload);
+
+ if (!strcmp(delta->old_file.path, "asparagus.txt"))
+ return 1;
+
+ return 0;
+}
+
+void test_apply_callbacks__delta_can_skip(void)
+{
+ git_diff *diff;
+ git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
+
+ struct merge_index_entry workdir_expected[] = {
+ { 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
+ };
+ size_t workdir_expected_cnt = sizeof(workdir_expected) /
+ sizeof(struct merge_index_entry);
+
+ opts.delta_cb = delta_skip_cb;
+
+ cl_git_pass(diff_from_buffer(&diff,
+ DIFF_MODIFY_TWO_FILES, strlen(DIFF_MODIFY_TWO_FILES)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, &opts));
+
+ validate_index_unchanged(repo);
+ validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
+
+ git_diff_free(diff);
+}
+
+static int hunk_skip_odds_cb(const git_diff_hunk *hunk, void *payload)
+{
+ int *count = (int *)payload;
+ GIT_UNUSED(hunk);
+
+ return ((*count)++ % 2 == 1);
+}
+
+void test_apply_callbacks__hunk_can_skip(void)
+{
+ git_diff *diff;
+ git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
+ int count = 0;
+
+ struct merge_index_entry workdir_expected[] = {
+ { 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "06f751b6ba4f017ddbf4248015768300268e092a", 0, "veal.txt" },
+ };
+ size_t workdir_expected_cnt = sizeof(workdir_expected) /
+ sizeof(struct merge_index_entry);
+
+ opts.hunk_cb = hunk_skip_odds_cb;
+ opts.payload = &count;
+
+ cl_git_pass(diff_from_buffer(&diff,
+ DIFF_MANY_CHANGES_ONE, strlen(DIFF_MANY_CHANGES_ONE)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, &opts));
+
+ validate_index_unchanged(repo);
+ validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
+
+ git_diff_free(diff);
+}
diff --git a/tests/libgit2/apply/check.c b/tests/libgit2/apply/check.c
new file mode 100644
index 0000000..0c1f86d
--- /dev/null
+++ b/tests/libgit2/apply/check.c
@@ -0,0 +1,121 @@
+#include "clar_libgit2.h"
+#include "apply_helpers.h"
+
+static git_repository *repo;
+
+#define TEST_REPO_PATH "merge-recursive"
+
+void test_apply_check__initialize(void)
+{
+ git_oid oid;
+ git_commit *commit;
+
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+
+ git_oid__fromstr(&oid, "539bd011c4822c560c1d17cab095006b7a10f707", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+ cl_git_pass(git_reset(repo, (git_object *)commit, GIT_RESET_HARD, NULL));
+ git_commit_free(commit);
+}
+
+void test_apply_check__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_apply_check__generate_diff(void)
+{
+ git_oid a_oid, b_oid;
+ git_commit *a_commit, *b_commit;
+ git_tree *a_tree, *b_tree;
+ git_diff *diff;
+ git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
+ git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
+
+ cl_git_pass(git_oid__fromstr(&a_oid, "539bd011c4822c560c1d17cab095006b7a10f707", GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&b_oid, "7c7bf85e978f1d18c0566f702d2cb7766b9c8d4f", GIT_OID_SHA1));
+ cl_git_pass(git_commit_lookup(&a_commit, repo, &a_oid));
+ cl_git_pass(git_commit_lookup(&b_commit, repo, &b_oid));
+
+ cl_git_pass(git_commit_tree(&a_tree, a_commit));
+ cl_git_pass(git_commit_tree(&b_tree, b_commit));
+
+ opts.flags |= GIT_APPLY_CHECK;
+ cl_git_pass(git_diff_tree_to_tree(&diff, repo, a_tree, b_tree, &diff_opts));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_BOTH, &opts));
+
+ validate_index_unchanged(repo);
+ validate_workdir_unchanged(repo);
+
+ git_diff_free(diff);
+ git_tree_free(a_tree);
+ git_tree_free(b_tree);
+ git_commit_free(a_commit);
+ git_commit_free(b_commit);
+}
+
+void test_apply_check__parsed_diff(void)
+{
+ git_diff *diff;
+ git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
+
+ opts.flags |= GIT_APPLY_CHECK;
+ cl_git_pass(diff_from_buffer(&diff,
+ DIFF_MODIFY_TWO_FILES, strlen(DIFF_MODIFY_TWO_FILES)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, &opts));
+
+ validate_index_unchanged(repo);
+ validate_workdir_unchanged(repo);
+
+ git_diff_free(diff);
+}
+
+void test_apply_check__binary(void)
+{
+ git_diff *diff;
+ git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
+
+ opts.flags |= GIT_APPLY_CHECK;
+ cl_git_pass(diff_from_buffer(&diff,
+ DIFF_MODIFY_TWO_FILES_BINARY,
+ strlen(DIFF_MODIFY_TWO_FILES_BINARY)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, &opts));
+
+ validate_index_unchanged(repo);
+ validate_workdir_unchanged(repo);
+
+ git_diff_free(diff);
+}
+
+void test_apply_check__does_not_apply(void)
+{
+ git_diff *diff;
+ git_index *index;
+ git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
+
+ const char *diff_file = DIFF_MODIFY_TWO_FILES;
+
+ struct merge_index_entry index_expected[] = {
+ { 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ };
+ size_t index_expected_cnt = sizeof(index_expected) /
+ sizeof(struct merge_index_entry);
+
+ /* mutate the index */
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_remove(index, "veal.txt", 0));
+ cl_git_pass(git_index_write(index));
+ git_index_free(index);
+
+ opts.flags |= GIT_APPLY_CHECK;
+ cl_git_pass(diff_from_buffer(&diff, diff_file, strlen(diff_file)));
+ cl_git_fail_with(GIT_EAPPLYFAIL, git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, &opts));
+
+ validate_apply_index(repo, index_expected, index_expected_cnt);
+
+ git_diff_free(diff);
+}
diff --git a/tests/libgit2/apply/fromdiff.c b/tests/libgit2/apply/fromdiff.c
new file mode 100644
index 0000000..9da6ac9
--- /dev/null
+++ b/tests/libgit2/apply/fromdiff.c
@@ -0,0 +1,378 @@
+#include "clar_libgit2.h"
+#include "git2/sys/repository.h"
+
+#include "apply.h"
+#include "repository.h"
+
+#include "../patch/patch_common.h"
+
+static git_repository *repo = NULL;
+static git_diff_options binary_opts = GIT_DIFF_OPTIONS_INIT;
+
+void test_apply_fromdiff__initialize(void)
+{
+ repo = cl_git_sandbox_init("renames");
+
+ binary_opts.flags |= GIT_DIFF_SHOW_BINARY;
+}
+
+void test_apply_fromdiff__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static int apply_gitbuf(
+ const git_str *old,
+ const char *oldname,
+ const git_str *new,
+ const char *newname,
+ const char *patch_expected,
+ const git_diff_options *diff_opts)
+{
+ git_patch *patch;
+ git_str result = GIT_STR_INIT;
+ git_buf patchbuf = GIT_BUF_INIT;
+ char *filename;
+ unsigned int mode;
+ int error;
+
+ cl_git_pass(git_patch_from_buffers(&patch,
+ old ? old->ptr : NULL, old ? old->size : 0,
+ oldname,
+ new ? new->ptr : NULL, new ? new->size : 0,
+ newname,
+ diff_opts));
+
+ if (patch_expected) {
+ cl_git_pass(git_patch_to_buf(&patchbuf, patch));
+ cl_assert_equal_s(patch_expected, patchbuf.ptr);
+ }
+
+ error = git_apply__patch(&result, &filename, &mode, old ? old->ptr : NULL, old ? old->size : 0, patch, NULL);
+
+ if (error == 0 && new == NULL) {
+ cl_assert_equal_i(0, result.size);
+ cl_assert_equal_p(NULL, filename);
+ cl_assert_equal_i(0, mode);
+ }
+ else if (error == 0) {
+ cl_assert_equal_s(new->ptr, result.ptr);
+ cl_assert_equal_s(newname ? newname : oldname, filename);
+ cl_assert_equal_i(0100644, mode);
+ }
+
+ git__free(filename);
+ git_str_dispose(&result);
+ git_buf_dispose(&patchbuf);
+ git_patch_free(patch);
+
+ return error;
+}
+
+static int apply_buf(
+ const char *old,
+ const char *oldname,
+ const char *new,
+ const char *newname,
+ const char *patch_expected,
+ const git_diff_options *diff_opts)
+{
+ git_str o = GIT_STR_INIT, n = GIT_STR_INIT,
+ *optr = NULL, *nptr = NULL;
+
+ if (old) {
+ o.ptr = (char *)old;
+ o.size = strlen(old);
+ optr = &o;
+ }
+
+ if (new) {
+ n.ptr = (char *)new;
+ n.size = strlen(new);
+ nptr = &n;
+ }
+
+ return apply_gitbuf(optr, oldname, nptr, newname, patch_expected, diff_opts);
+}
+
+void test_apply_fromdiff__change_middle(void)
+{
+ cl_git_pass(apply_buf(
+ FILE_ORIGINAL, "file.txt",
+ FILE_CHANGE_MIDDLE, "file.txt",
+ PATCH_ORIGINAL_TO_CHANGE_MIDDLE, NULL));
+}
+
+void test_apply_fromdiff__change_middle_nocontext(void)
+{
+ git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
+ diff_opts.context_lines = 0;
+
+ cl_git_pass(apply_buf(
+ FILE_ORIGINAL, "file.txt",
+ FILE_CHANGE_MIDDLE, "file.txt",
+ PATCH_ORIGINAL_TO_CHANGE_MIDDLE_NOCONTEXT, &diff_opts));
+}
+
+void test_apply_fromdiff__change_firstline(void)
+{
+ cl_git_pass(apply_buf(
+ FILE_ORIGINAL, "file.txt",
+ FILE_CHANGE_FIRSTLINE, "file.txt",
+ PATCH_ORIGINAL_TO_CHANGE_FIRSTLINE, NULL));
+}
+
+void test_apply_fromdiff__lastline(void)
+{
+ cl_git_pass(apply_buf(
+ FILE_ORIGINAL, "file.txt",
+ FILE_CHANGE_LASTLINE, "file.txt",
+ PATCH_ORIGINAL_TO_CHANGE_LASTLINE, NULL));
+}
+
+void test_apply_fromdiff__change_middle_and_lastline_nocontext(void)
+{
+ git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
+ diff_opts.context_lines = 0;
+
+ cl_git_pass(apply_buf(
+ FILE_ORIGINAL, "file.txt",
+ FILE_CHANGE_MIDDLE_AND_LASTLINE, "file.txt",
+ PATCH_ORIGINAL_TO_CHANGE_MIDDLE_AND_LASTLINE_NOCONTEXT, &diff_opts));
+}
+
+void test_apply_fromdiff__prepend(void)
+{
+ cl_git_pass(apply_buf(
+ FILE_ORIGINAL, "file.txt",
+ FILE_PREPEND, "file.txt",
+ PATCH_ORIGINAL_TO_PREPEND, NULL));
+}
+
+void test_apply_fromdiff__prepend_nocontext(void)
+{
+ git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
+ diff_opts.context_lines = 0;
+
+ cl_git_pass(apply_buf(
+ FILE_ORIGINAL, "file.txt",
+ FILE_PREPEND, "file.txt",
+ PATCH_ORIGINAL_TO_PREPEND_NOCONTEXT, &diff_opts));
+}
+
+void test_apply_fromdiff__prepend_and_change(void)
+{
+ cl_git_pass(apply_buf(
+ FILE_ORIGINAL, "file.txt",
+ FILE_PREPEND_AND_CHANGE, "file.txt",
+ PATCH_ORIGINAL_TO_PREPEND_AND_CHANGE, NULL));
+}
+
+void test_apply_fromdiff__prepend_and_change_nocontext(void)
+{
+ git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
+ diff_opts.context_lines = 0;
+
+ cl_git_pass(apply_buf(
+ FILE_ORIGINAL, "file.txt",
+ FILE_PREPEND_AND_CHANGE, "file.txt",
+ PATCH_ORIGINAL_TO_PREPEND_AND_CHANGE_NOCONTEXT, &diff_opts));
+}
+
+void test_apply_fromdiff__delete_and_change(void)
+{
+ cl_git_pass(apply_buf(
+ FILE_ORIGINAL, "file.txt",
+ FILE_DELETE_AND_CHANGE, "file.txt",
+ PATCH_ORIGINAL_TO_DELETE_AND_CHANGE, NULL));
+}
+
+void test_apply_fromdiff__delete_and_change_nocontext(void)
+{
+ git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
+ diff_opts.context_lines = 0;
+
+ cl_git_pass(apply_buf(
+ FILE_ORIGINAL, "file.txt",
+ FILE_DELETE_AND_CHANGE, "file.txt",
+ PATCH_ORIGINAL_TO_DELETE_AND_CHANGE_NOCONTEXT, &diff_opts));
+}
+
+void test_apply_fromdiff__delete_firstline(void)
+{
+ cl_git_pass(apply_buf(
+ FILE_ORIGINAL, "file.txt",
+ FILE_DELETE_FIRSTLINE, "file.txt",
+ PATCH_ORIGINAL_TO_DELETE_FIRSTLINE, NULL));
+}
+
+void test_apply_fromdiff__append(void)
+{
+ cl_git_pass(apply_buf(
+ FILE_ORIGINAL, "file.txt",
+ FILE_APPEND, "file.txt",
+ PATCH_ORIGINAL_TO_APPEND, NULL));
+}
+
+void test_apply_fromdiff__append_nocontext(void)
+{
+ git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
+ diff_opts.context_lines = 0;
+
+ cl_git_pass(apply_buf(
+ FILE_ORIGINAL, "file.txt",
+ FILE_APPEND, "file.txt",
+ PATCH_ORIGINAL_TO_APPEND_NOCONTEXT, &diff_opts));
+}
+
+void test_apply_fromdiff__prepend_and_append(void)
+{
+ cl_git_pass(apply_buf(
+ FILE_ORIGINAL, "file.txt",
+ FILE_PREPEND_AND_APPEND, "file.txt",
+ PATCH_ORIGINAL_TO_PREPEND_AND_APPEND, NULL));
+}
+
+void test_apply_fromdiff__to_empty_file(void)
+{
+ cl_git_pass(apply_buf(
+ FILE_ORIGINAL, "file.txt",
+ "", NULL,
+ PATCH_ORIGINAL_TO_EMPTY_FILE, NULL));
+}
+
+void test_apply_fromdiff__from_empty_file(void)
+{
+ cl_git_pass(apply_buf(
+ "", NULL,
+ FILE_ORIGINAL, "file.txt",
+ PATCH_EMPTY_FILE_TO_ORIGINAL, NULL));
+}
+
+void test_apply_fromdiff__add(void)
+{
+ cl_git_pass(apply_buf(
+ NULL, NULL,
+ FILE_ORIGINAL, "file.txt",
+ PATCH_ADD_ORIGINAL, NULL));
+}
+
+void test_apply_fromdiff__delete(void)
+{
+ cl_git_pass(apply_buf(
+ FILE_ORIGINAL, "file.txt",
+ NULL, NULL,
+ PATCH_DELETE_ORIGINAL, NULL));
+}
+
+void test_apply_fromdiff__no_change(void)
+{
+ cl_git_pass(apply_buf(
+ FILE_ORIGINAL, "file.txt",
+ FILE_ORIGINAL, "file.txt",
+ "", NULL));
+}
+
+void test_apply_fromdiff__binary_add(void)
+{
+ git_str newfile = GIT_STR_INIT;
+
+ newfile.ptr = FILE_BINARY_DELTA_MODIFIED;
+ newfile.size = FILE_BINARY_DELTA_MODIFIED_LEN;
+
+ cl_git_pass(apply_gitbuf(
+ NULL, NULL,
+ &newfile, "binary.bin",
+ NULL, &binary_opts));
+}
+
+void test_apply_fromdiff__binary_no_change(void)
+{
+ git_str original = GIT_STR_INIT;
+
+ original.ptr = FILE_BINARY_DELTA_ORIGINAL;
+ original.size = FILE_BINARY_DELTA_ORIGINAL_LEN;
+
+ cl_git_pass(apply_gitbuf(
+ &original, "binary.bin",
+ &original, "binary.bin",
+ "", &binary_opts));
+}
+
+void test_apply_fromdiff__binary_change_delta(void)
+{
+ git_str original = GIT_STR_INIT, modified = GIT_STR_INIT;
+
+ original.ptr = FILE_BINARY_DELTA_ORIGINAL;
+ original.size = FILE_BINARY_DELTA_ORIGINAL_LEN;
+
+ modified.ptr = FILE_BINARY_DELTA_MODIFIED;
+ modified.size = FILE_BINARY_DELTA_MODIFIED_LEN;
+
+ cl_git_pass(apply_gitbuf(
+ &original, "binary.bin",
+ &modified, "binary.bin",
+ NULL, &binary_opts));
+}
+
+void test_apply_fromdiff__binary_change_literal(void)
+{
+ git_str original = GIT_STR_INIT, modified = GIT_STR_INIT;
+
+ original.ptr = FILE_BINARY_LITERAL_ORIGINAL;
+ original.size = FILE_BINARY_LITERAL_ORIGINAL_LEN;
+
+ modified.ptr = FILE_BINARY_LITERAL_MODIFIED;
+ modified.size = FILE_BINARY_LITERAL_MODIFIED_LEN;
+
+ cl_git_pass(apply_gitbuf(
+ &original, "binary.bin",
+ &modified, "binary.bin",
+ NULL, &binary_opts));
+}
+
+void test_apply_fromdiff__binary_delete(void)
+{
+ git_str original = GIT_STR_INIT;
+
+ original.ptr = FILE_BINARY_DELTA_MODIFIED;
+ original.size = FILE_BINARY_DELTA_MODIFIED_LEN;
+
+ cl_git_pass(apply_gitbuf(
+ &original, "binary.bin",
+ NULL, NULL,
+ NULL, &binary_opts));
+}
+
+void test_apply_fromdiff__patching_correctly_truncates_source(void)
+{
+ git_str original = GIT_STR_INIT, patched = GIT_STR_INIT;
+ git_patch *patch;
+ unsigned int mode;
+ char *path;
+
+ cl_git_pass(git_patch_from_buffers(&patch,
+ "foo\nbar", 7, "file.txt",
+ "foo\nfoo", 7, "file.txt", NULL));
+
+ /*
+ * Previously, we would fail to correctly truncate the source buffer if
+ * the source has more than one line and ends with a non-newline
+ * character. In the following call, we thus truncate the source string
+ * in the middle of the second line. Without the bug fixed, we would
+ * successfully apply the patch to the source and return success. With
+ * the overflow being fixed, we should return an error.
+ */
+ cl_git_fail_with(GIT_EAPPLYFAIL,
+ git_apply__patch(&patched, &path, &mode,
+ "foo\nbar\n", 5, patch, NULL));
+
+ /* Verify that the patch succeeds if we do not truncate */
+ cl_git_pass(git_apply__patch(&patched, &path, &mode,
+ "foo\nbar\n", 7, patch, NULL));
+
+ git_str_dispose(&original);
+ git_str_dispose(&patched);
+ git_patch_free(patch);
+ git__free(path);
+}
diff --git a/tests/libgit2/apply/fromfile.c b/tests/libgit2/apply/fromfile.c
new file mode 100644
index 0000000..8cde623
--- /dev/null
+++ b/tests/libgit2/apply/fromfile.c
@@ -0,0 +1,449 @@
+#include "clar_libgit2.h"
+#include "git2/sys/repository.h"
+
+#include "apply.h"
+#include "patch.h"
+#include "patch_parse.h"
+#include "repository.h"
+
+#include "../patch/patch_common.h"
+
+static git_repository *repo = NULL;
+
+void test_apply_fromfile__initialize(void)
+{
+ repo = cl_git_sandbox_init("renames");
+}
+
+void test_apply_fromfile__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static int apply_patchfile(
+ const char *old,
+ size_t old_len,
+ const char *new,
+ size_t new_len,
+ const char *patchfile,
+ const char *filename_expected,
+ unsigned int mode_expected)
+{
+ git_patch *patch;
+ git_str result = GIT_STR_INIT;
+ git_str patchbuf = GIT_STR_INIT;
+ char *filename;
+ unsigned int mode;
+ int error;
+
+ cl_git_pass(git_patch_from_buffer(&patch, patchfile, strlen(patchfile), NULL));
+
+ error = git_apply__patch(&result, &filename, &mode, old, old_len, patch, NULL);
+
+ if (error == 0) {
+ cl_assert_equal_i(new_len, result.size);
+ if (new_len)
+ cl_assert(memcmp(new, result.ptr, new_len) == 0);
+
+ cl_assert_equal_s(filename_expected, filename);
+ cl_assert_equal_i(mode_expected, mode);
+ }
+
+ git__free(filename);
+ git_str_dispose(&result);
+ git_str_dispose(&patchbuf);
+ git_patch_free(patch);
+
+ return error;
+}
+
+static int validate_and_apply_patchfile(
+ const char *old,
+ size_t old_len,
+ const char *new,
+ size_t new_len,
+ const char *patchfile,
+ const git_diff_options *diff_opts,
+ const char *filename_expected,
+ unsigned int mode_expected)
+{
+ git_patch *patch_fromdiff;
+ git_buf validated = GIT_BUF_INIT;
+ int error;
+
+ cl_git_pass(git_patch_from_buffers(&patch_fromdiff,
+ old, old_len, "file.txt",
+ new, new_len, "file.txt",
+ diff_opts));
+ cl_git_pass(git_patch_to_buf(&validated, patch_fromdiff));
+
+ cl_assert_equal_s(patchfile, validated.ptr);
+
+ error = apply_patchfile(old, old_len, new, new_len, patchfile, filename_expected, mode_expected);
+
+ git_buf_dispose(&validated);
+ git_patch_free(patch_fromdiff);
+
+ return error;
+}
+
+void test_apply_fromfile__change_middle(void)
+{
+ cl_git_pass(validate_and_apply_patchfile(
+ FILE_ORIGINAL, strlen(FILE_ORIGINAL),
+ FILE_CHANGE_MIDDLE, strlen(FILE_CHANGE_MIDDLE),
+ PATCH_ORIGINAL_TO_CHANGE_MIDDLE, NULL,
+ "file.txt", 0100644));
+}
+
+void test_apply_fromfile__change_middle_nocontext(void)
+{
+ git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
+ diff_opts.context_lines = 0;
+
+ cl_git_pass(validate_and_apply_patchfile(
+ FILE_ORIGINAL, strlen(FILE_ORIGINAL),
+ FILE_CHANGE_MIDDLE, strlen(FILE_CHANGE_MIDDLE),
+ PATCH_ORIGINAL_TO_CHANGE_MIDDLE_NOCONTEXT,
+ &diff_opts, "file.txt", 0100644));
+}
+
+
+void test_apply_fromfile__change_firstline(void)
+{
+ cl_git_pass(validate_and_apply_patchfile(
+ FILE_ORIGINAL, strlen(FILE_ORIGINAL),
+ FILE_CHANGE_FIRSTLINE, strlen(FILE_CHANGE_FIRSTLINE),
+ PATCH_ORIGINAL_TO_CHANGE_FIRSTLINE, NULL,
+ "file.txt", 0100644));
+}
+
+void test_apply_fromfile__lastline(void)
+{
+ cl_git_pass(validate_and_apply_patchfile(
+ FILE_ORIGINAL, strlen(FILE_ORIGINAL),
+ FILE_CHANGE_LASTLINE, strlen(FILE_CHANGE_LASTLINE),
+ PATCH_ORIGINAL_TO_CHANGE_LASTLINE, NULL,
+ "file.txt", 0100644));
+}
+
+void test_apply_fromfile__change_middle_shrink(void)
+{
+ cl_git_pass(validate_and_apply_patchfile(
+ FILE_ORIGINAL, strlen(FILE_ORIGINAL),
+ FILE_CHANGE_MIDDLE_SHRINK, strlen(FILE_CHANGE_MIDDLE_SHRINK),
+ PATCH_ORIGINAL_TO_CHANGE_MIDDLE_SHRINK, NULL,
+ "file.txt", 0100644));
+}
+
+void test_apply_fromfile__change_middle_shrink_nocontext(void)
+{
+ git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
+ diff_opts.context_lines = 0;
+
+ cl_git_pass(validate_and_apply_patchfile(
+ FILE_ORIGINAL, strlen(FILE_ORIGINAL),
+ FILE_CHANGE_MIDDLE_SHRINK, strlen(FILE_CHANGE_MIDDLE_SHRINK),
+ PATCH_ORIGINAL_TO_MIDDLE_SHRINK_NOCONTEXT, &diff_opts,
+ "file.txt", 0100644));
+}
+
+void test_apply_fromfile__change_middle_grow(void)
+{
+ cl_git_pass(validate_and_apply_patchfile(
+ FILE_ORIGINAL, strlen(FILE_ORIGINAL),
+ FILE_CHANGE_MIDDLE_GROW, strlen(FILE_CHANGE_MIDDLE_GROW),
+ PATCH_ORIGINAL_TO_CHANGE_MIDDLE_GROW, NULL,
+ "file.txt", 0100644));
+}
+
+void test_apply_fromfile__change_middle_grow_nocontext(void)
+{
+ git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
+ diff_opts.context_lines = 0;
+
+ cl_git_pass(validate_and_apply_patchfile(
+ FILE_ORIGINAL, strlen(FILE_ORIGINAL),
+ FILE_CHANGE_MIDDLE_GROW, strlen(FILE_CHANGE_MIDDLE_GROW),
+ PATCH_ORIGINAL_TO_MIDDLE_GROW_NOCONTEXT, &diff_opts,
+ "file.txt", 0100644));
+}
+
+void test_apply_fromfile__prepend(void)
+{
+ cl_git_pass(validate_and_apply_patchfile(
+ FILE_ORIGINAL, strlen(FILE_ORIGINAL),
+ FILE_PREPEND, strlen(FILE_PREPEND),
+ PATCH_ORIGINAL_TO_PREPEND, NULL, "file.txt", 0100644));
+}
+
+void test_apply_fromfile__prepend_nocontext(void)
+{
+ git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
+ diff_opts.context_lines = 0;
+
+ cl_git_pass(validate_and_apply_patchfile(
+ FILE_ORIGINAL, strlen(FILE_ORIGINAL),
+ FILE_PREPEND, strlen(FILE_PREPEND),
+ PATCH_ORIGINAL_TO_PREPEND_NOCONTEXT, &diff_opts,
+ "file.txt", 0100644));
+}
+
+void test_apply_fromfile__append(void)
+{
+ cl_git_pass(validate_and_apply_patchfile(
+ FILE_ORIGINAL, strlen(FILE_ORIGINAL),
+ FILE_APPEND, strlen(FILE_APPEND),
+ PATCH_ORIGINAL_TO_APPEND, NULL, "file.txt", 0100644));
+}
+
+void test_apply_fromfile__append_nocontext(void)
+{
+ git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
+ diff_opts.context_lines = 0;
+
+ cl_git_pass(validate_and_apply_patchfile(
+ FILE_ORIGINAL, strlen(FILE_ORIGINAL),
+ FILE_APPEND, strlen(FILE_APPEND),
+ PATCH_ORIGINAL_TO_APPEND_NOCONTEXT, &diff_opts,
+ "file.txt", 0100644));
+}
+
+void test_apply_fromfile__prepend_and_append(void)
+{
+ cl_git_pass(validate_and_apply_patchfile(
+ FILE_ORIGINAL, strlen(FILE_ORIGINAL),
+ FILE_PREPEND_AND_APPEND, strlen(FILE_PREPEND_AND_APPEND),
+ PATCH_ORIGINAL_TO_PREPEND_AND_APPEND, NULL,
+ "file.txt", 0100644));
+}
+
+void test_apply_fromfile__to_empty_file(void)
+{
+ cl_git_pass(validate_and_apply_patchfile(
+ FILE_ORIGINAL, strlen(FILE_ORIGINAL),
+ "", 0,
+ PATCH_ORIGINAL_TO_EMPTY_FILE, NULL, "file.txt", 0100644));
+}
+
+void test_apply_fromfile__from_empty_file(void)
+{
+ cl_git_pass(validate_and_apply_patchfile(
+ "", 0,
+ FILE_ORIGINAL, strlen(FILE_ORIGINAL),
+ PATCH_EMPTY_FILE_TO_ORIGINAL, NULL, "file.txt", 0100644));
+}
+
+void test_apply_fromfile__add(void)
+{
+ cl_git_pass(validate_and_apply_patchfile(
+ NULL, 0,
+ FILE_ORIGINAL, strlen(FILE_ORIGINAL),
+ PATCH_ADD_ORIGINAL, NULL, "file.txt", 0100644));
+}
+
+void test_apply_fromfile__delete(void)
+{
+ cl_git_pass(validate_and_apply_patchfile(
+ FILE_ORIGINAL, strlen(FILE_ORIGINAL),
+ NULL, 0,
+ PATCH_DELETE_ORIGINAL, NULL, NULL, 0));
+}
+
+
+void test_apply_fromfile__rename_exact(void)
+{
+ cl_git_pass(apply_patchfile(
+ FILE_ORIGINAL, strlen(FILE_ORIGINAL),
+ FILE_ORIGINAL, strlen(FILE_ORIGINAL),
+ PATCH_RENAME_EXACT, "newfile.txt", 0100644));
+}
+
+void test_apply_fromfile__rename_similar(void)
+{
+ cl_git_pass(apply_patchfile(
+ FILE_ORIGINAL, strlen(FILE_ORIGINAL),
+ FILE_CHANGE_MIDDLE, strlen(FILE_CHANGE_MIDDLE),
+ PATCH_RENAME_SIMILAR, "newfile.txt", 0100644));
+}
+
+void test_apply_fromfile__rename_similar_quotedname(void)
+{
+ cl_git_pass(apply_patchfile(
+ FILE_ORIGINAL, strlen(FILE_ORIGINAL),
+ FILE_CHANGE_MIDDLE, strlen(FILE_CHANGE_MIDDLE),
+ PATCH_RENAME_SIMILAR_QUOTEDNAME, "foo\"bar.txt", 0100644));
+}
+
+void test_apply_fromfile__modechange(void)
+{
+ cl_git_pass(apply_patchfile(
+ FILE_ORIGINAL, strlen(FILE_ORIGINAL),
+ FILE_ORIGINAL, strlen(FILE_ORIGINAL),
+ PATCH_MODECHANGE_UNCHANGED, "file.txt", 0100755));
+}
+
+void test_apply_fromfile__modechange_with_modification(void)
+{
+ cl_git_pass(apply_patchfile(
+ FILE_ORIGINAL, strlen(FILE_ORIGINAL),
+ FILE_CHANGE_MIDDLE, strlen(FILE_CHANGE_MIDDLE),
+ PATCH_MODECHANGE_MODIFIED, "file.txt", 0100755));
+}
+
+void test_apply_fromfile__noisy(void)
+{
+ cl_git_pass(apply_patchfile(
+ FILE_ORIGINAL, strlen(FILE_ORIGINAL),
+ FILE_CHANGE_MIDDLE, strlen(FILE_CHANGE_MIDDLE),
+ PATCH_NOISY, "file.txt", 0100644));
+}
+
+void test_apply_fromfile__noisy_nocontext(void)
+{
+ cl_git_pass(apply_patchfile(
+ FILE_ORIGINAL, strlen(FILE_ORIGINAL),
+ FILE_CHANGE_MIDDLE, strlen(FILE_CHANGE_MIDDLE),
+ PATCH_NOISY_NOCONTEXT, "file.txt", 0100644));
+}
+
+void test_apply_fromfile__fail_truncated_1(void)
+{
+ git_patch *patch;
+ cl_git_fail(git_patch_from_buffer(&patch, PATCH_TRUNCATED_1,
+ strlen(PATCH_TRUNCATED_1), NULL));
+}
+
+void test_apply_fromfile__fail_truncated_2(void)
+{
+ git_patch *patch;
+ cl_git_fail(git_patch_from_buffer(&patch, PATCH_TRUNCATED_2,
+ strlen(PATCH_TRUNCATED_2), NULL));
+}
+
+void test_apply_fromfile__fail_truncated_3(void)
+{
+ git_patch *patch;
+ cl_git_fail(git_patch_from_buffer(&patch, PATCH_TRUNCATED_3,
+ strlen(PATCH_TRUNCATED_3), NULL));
+}
+
+void test_apply_fromfile__fail_corrupt_githeader(void)
+{
+ git_patch *patch;
+ cl_git_fail(git_patch_from_buffer(&patch, PATCH_CORRUPT_GIT_HEADER,
+ strlen(PATCH_CORRUPT_GIT_HEADER), NULL));
+}
+
+void test_apply_fromfile__empty_context(void)
+{
+ cl_git_pass(apply_patchfile(
+ FILE_EMPTY_CONTEXT_ORIGINAL, strlen(FILE_EMPTY_CONTEXT_ORIGINAL),
+ FILE_EMPTY_CONTEXT_MODIFIED, strlen(FILE_EMPTY_CONTEXT_MODIFIED),
+ PATCH_EMPTY_CONTEXT,
+ "file.txt", 0100644));
+}
+
+void test_apply_fromfile__append_no_nl(void)
+{
+ cl_git_pass(validate_and_apply_patchfile(
+ FILE_ORIGINAL, strlen(FILE_ORIGINAL),
+ FILE_APPEND_NO_NL, strlen(FILE_APPEND_NO_NL),
+ PATCH_APPEND_NO_NL, NULL, "file.txt", 0100644));
+}
+
+void test_apply_fromfile__fail_missing_new_file(void)
+{
+ git_patch *patch;
+ cl_git_fail(git_patch_from_buffer(&patch,
+ PATCH_CORRUPT_MISSING_NEW_FILE,
+ strlen(PATCH_CORRUPT_MISSING_NEW_FILE), NULL));
+}
+
+void test_apply_fromfile__fail_missing_old_file(void)
+{
+ git_patch *patch;
+ cl_git_fail(git_patch_from_buffer(&patch,
+ PATCH_CORRUPT_MISSING_OLD_FILE,
+ strlen(PATCH_CORRUPT_MISSING_OLD_FILE), NULL));
+}
+
+void test_apply_fromfile__fail_no_changes(void)
+{
+ git_patch *patch;
+ cl_git_fail(git_patch_from_buffer(&patch,
+ PATCH_CORRUPT_NO_CHANGES,
+ strlen(PATCH_CORRUPT_NO_CHANGES), NULL));
+}
+
+void test_apply_fromfile__fail_missing_hunk_header(void)
+{
+ git_patch *patch;
+ cl_git_fail(git_patch_from_buffer(&patch,
+ PATCH_CORRUPT_MISSING_HUNK_HEADER,
+ strlen(PATCH_CORRUPT_MISSING_HUNK_HEADER), NULL));
+}
+
+void test_apply_fromfile__fail_not_a_patch(void)
+{
+ git_patch *patch;
+ cl_git_fail(git_patch_from_buffer(&patch, PATCH_NOT_A_PATCH,
+ strlen(PATCH_NOT_A_PATCH), NULL));
+}
+
+void test_apply_fromfile__binary_add(void)
+{
+ cl_git_pass(apply_patchfile(
+ NULL, 0,
+ FILE_BINARY_DELTA_MODIFIED, FILE_BINARY_DELTA_MODIFIED_LEN,
+ PATCH_BINARY_ADD, "binary.bin", 0100644));
+}
+
+void test_apply_fromfile__binary_change_delta(void)
+{
+ cl_git_pass(apply_patchfile(
+ FILE_BINARY_DELTA_ORIGINAL, FILE_BINARY_DELTA_ORIGINAL_LEN,
+ FILE_BINARY_DELTA_MODIFIED, FILE_BINARY_DELTA_MODIFIED_LEN,
+ PATCH_BINARY_DELTA, "binary.bin", 0100644));
+}
+
+void test_apply_fromfile__binary_change_literal(void)
+{
+ cl_git_pass(apply_patchfile(
+ FILE_BINARY_LITERAL_ORIGINAL, FILE_BINARY_LITERAL_ORIGINAL_LEN,
+ FILE_BINARY_LITERAL_MODIFIED, FILE_BINARY_LITERAL_MODIFIED_LEN,
+ PATCH_BINARY_LITERAL, "binary.bin", 0100644));
+}
+
+void test_apply_fromfile__binary_delete(void)
+{
+ cl_git_pass(apply_patchfile(
+ FILE_BINARY_DELTA_MODIFIED, FILE_BINARY_DELTA_MODIFIED_LEN,
+ NULL, 0,
+ PATCH_BINARY_DELETE, NULL, 0));
+}
+
+void test_apply_fromfile__binary_change_does_not_apply(void)
+{
+ /* try to apply patch backwards, ensure it does not apply */
+ cl_git_fail(apply_patchfile(
+ FILE_BINARY_DELTA_MODIFIED, FILE_BINARY_DELTA_MODIFIED_LEN,
+ FILE_BINARY_DELTA_ORIGINAL, FILE_BINARY_DELTA_ORIGINAL_LEN,
+ PATCH_BINARY_DELTA, "binary.bin", 0100644));
+}
+
+void test_apply_fromfile__binary_change_must_be_reversible(void)
+{
+ cl_git_fail(apply_patchfile(
+ FILE_BINARY_DELTA_MODIFIED, FILE_BINARY_DELTA_MODIFIED_LEN,
+ NULL, 0,
+ PATCH_BINARY_NOT_REVERSIBLE, NULL, 0));
+}
+
+void test_apply_fromfile__empty_file_not_allowed(void)
+{
+ git_patch *patch;
+
+ cl_git_fail(git_patch_from_buffer(&patch, "", 0, NULL));
+ cl_git_fail(git_patch_from_buffer(&patch, NULL, 0, NULL));
+}
diff --git a/tests/libgit2/apply/index.c b/tests/libgit2/apply/index.c
new file mode 100644
index 0000000..564d55c
--- /dev/null
+++ b/tests/libgit2/apply/index.c
@@ -0,0 +1,321 @@
+#include "clar_libgit2.h"
+#include "apply_helpers.h"
+
+static git_repository *repo;
+
+#define TEST_REPO_PATH "merge-recursive"
+
+void test_apply_index__initialize(void)
+{
+ git_oid oid;
+ git_commit *commit;
+
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+
+ git_oid__fromstr(&oid, "539bd011c4822c560c1d17cab095006b7a10f707", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+ cl_git_pass(git_reset(repo, (git_object *)commit, GIT_RESET_HARD, NULL));
+ git_commit_free(commit);
+}
+
+void test_apply_index__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_apply_index__generate_diff(void)
+{
+ git_oid a_oid, b_oid;
+ git_commit *a_commit, *b_commit;
+ git_tree *a_tree, *b_tree;
+ git_diff *diff;
+ git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
+
+ struct merge_index_entry index_expected[] = {
+ { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
+ };
+ size_t index_expected_cnt = sizeof(index_expected) /
+ sizeof(struct merge_index_entry);
+
+ git_oid__fromstr(&a_oid, "539bd011c4822c560c1d17cab095006b7a10f707", GIT_OID_SHA1);
+ git_oid__fromstr(&b_oid, "7c7bf85e978f1d18c0566f702d2cb7766b9c8d4f", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&a_commit, repo, &a_oid));
+ cl_git_pass(git_commit_lookup(&b_commit, repo, &b_oid));
+
+ cl_git_pass(git_commit_tree(&a_tree, a_commit));
+ cl_git_pass(git_commit_tree(&b_tree, b_commit));
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, repo, a_tree, b_tree, &diff_opts));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, NULL));
+
+ validate_apply_index(repo, index_expected, index_expected_cnt);
+ validate_workdir_unchanged(repo);
+
+ git_diff_free(diff);
+ git_tree_free(a_tree);
+ git_tree_free(b_tree);
+ git_commit_free(a_commit);
+ git_commit_free(b_commit);
+}
+
+void test_apply_index__parsed_diff(void)
+{
+ git_diff *diff;
+
+ struct merge_index_entry index_expected[] = {
+ { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
+ };
+ size_t index_expected_cnt = sizeof(index_expected) /
+ sizeof(struct merge_index_entry);
+
+ cl_git_pass(diff_from_buffer(&diff,
+ DIFF_MODIFY_TWO_FILES, strlen(DIFF_MODIFY_TWO_FILES)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, NULL));
+
+ validate_apply_index(repo, index_expected, index_expected_cnt);
+ validate_workdir_unchanged(repo);
+
+ git_diff_free(diff);
+}
+
+void test_apply_index__removes_file(void)
+{
+ git_diff *diff;
+
+ struct merge_index_entry index_expected[] = {
+ { 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" },
+ };
+ size_t index_expected_cnt = sizeof(index_expected) /
+ sizeof(struct merge_index_entry);
+
+ cl_git_pass(diff_from_buffer(&diff, DIFF_DELETE_FILE,
+ strlen(DIFF_DELETE_FILE)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, NULL));
+
+ validate_apply_index(repo, index_expected, index_expected_cnt);
+ validate_workdir_unchanged(repo);
+
+ git_diff_free(diff);
+}
+
+void test_apply_index__adds_file(void)
+{
+ git_diff *diff;
+
+ struct merge_index_entry index_expected[] = {
+ { 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "6370543fcfedb3e6516ec53b06158f3687dc1447", 0, "newfile.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" },
+ };
+ size_t index_expected_cnt = sizeof(index_expected) /
+ sizeof(struct merge_index_entry);
+
+ cl_git_pass(diff_from_buffer(&diff,
+ DIFF_ADD_FILE, strlen(DIFF_ADD_FILE)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, NULL));
+
+ validate_apply_index(repo, index_expected, index_expected_cnt);
+ validate_workdir_unchanged(repo);
+
+ git_diff_free(diff);
+}
+
+void test_apply_index__modified_workdir_with_unmodified_index_is_ok(void)
+{
+ git_diff *diff;
+
+ const char *diff_file = DIFF_MODIFY_TWO_FILES;
+
+ struct merge_index_entry index_expected[] = {
+ { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
+ };
+ size_t index_expected_cnt = sizeof(index_expected) /
+ sizeof(struct merge_index_entry);
+
+ struct merge_index_entry workdir_expected[] = {
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "f75ba05f340c51065cbea2e1fdbfe5fe13144c97", 0, "veal.txt" }
+ };
+ size_t workdir_expected_cnt = sizeof(workdir_expected) /
+ sizeof(struct merge_index_entry);
+
+ /* mutate the workdir and leave the index matching HEAD */
+ cl_git_rmfile("merge-recursive/asparagus.txt");
+ cl_git_rewritefile("merge-recursive/veal.txt", "Hello, world.\n");
+
+ cl_git_pass(diff_from_buffer(&diff, diff_file, strlen(diff_file)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, NULL));
+
+ validate_apply_index(repo, index_expected, index_expected_cnt);
+ validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
+
+ git_diff_free(diff);
+}
+
+void test_apply_index__application_failure_leaves_index_unmodified(void)
+{
+ git_diff *diff;
+ git_index *index;
+
+ const char *diff_file = DIFF_MODIFY_TWO_FILES;
+
+ struct merge_index_entry index_expected[] = {
+ { 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ };
+ size_t index_expected_cnt = sizeof(index_expected) /
+ sizeof(struct merge_index_entry);
+
+ /* mutate the index */
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_remove(index, "veal.txt", 0));
+ cl_git_pass(git_index_write(index));
+ git_index_free(index);
+
+ cl_git_pass(diff_from_buffer(&diff, diff_file, strlen(diff_file)));
+ cl_git_fail_with(GIT_EAPPLYFAIL, git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, NULL));
+
+ validate_apply_index(repo, index_expected, index_expected_cnt);
+
+ git_diff_free(diff);
+}
+
+void test_apply_index__keeps_nonconflicting_changes(void)
+{
+ git_diff *diff;
+ git_index *index;
+ git_index_entry idx_entry;
+
+ const char *diff_file = DIFF_MODIFY_TWO_FILES;
+
+ struct merge_index_entry index_expected[] = {
+ { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
+ { 0100644, "898d12687fb35be271c27c795a6b32c8b51da79e", 0, "beef.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
+ };
+ size_t index_expected_cnt = sizeof(index_expected) /
+ sizeof(struct merge_index_entry);
+
+ /* mutate the index */
+ cl_git_pass(git_repository_index(&index, repo));
+
+ memset(&idx_entry, 0, sizeof(git_index_entry));
+ idx_entry.mode = 0100644;
+ idx_entry.path = "beef.txt";
+ cl_git_pass(git_oid__fromstr(&idx_entry.id, "898d12687fb35be271c27c795a6b32c8b51da79e", GIT_OID_SHA1));
+ cl_git_pass(git_index_add(index, &idx_entry));
+
+ cl_git_pass(git_index_remove(index, "bouilli.txt", 0));
+ cl_git_pass(git_index_write(index));
+ git_index_free(index);
+
+ cl_git_pass(diff_from_buffer(&diff, diff_file, strlen(diff_file)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, NULL));
+
+ validate_apply_index(repo, index_expected, index_expected_cnt);
+ validate_workdir_unchanged(repo);
+
+ git_diff_free(diff);
+}
+
+void test_apply_index__can_apply_nonconflicting_file_changes(void)
+{
+ git_diff *diff;
+ git_index *index;
+ git_index_entry idx_entry;
+
+ const char *diff_file = DIFF_MODIFY_TWO_FILES;
+
+ struct merge_index_entry index_expected[] = {
+ { 0100644, "4f2d1645dee99ced096877911de540c65ade2ef8", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
+ };
+ size_t index_expected_cnt = sizeof(index_expected) /
+ sizeof(struct merge_index_entry);
+
+ /*
+ * Replace the index entry with a version that is different than
+ * HEAD but such that the patch still applies cleanly. This item
+ * has a new line appended.
+ */
+
+ cl_git_pass(git_repository_index(&index, repo));
+
+ memset(&idx_entry, 0, sizeof(git_index_entry));
+ idx_entry.mode = 0100644;
+ idx_entry.path = "asparagus.txt";
+ cl_git_pass(git_oid__fromstr(&idx_entry.id, "06d3fefb8726ab1099acc76e02dfb85e034b2538", GIT_OID_SHA1));
+ cl_git_pass(git_index_add(index, &idx_entry));
+
+ cl_git_pass(git_index_write(index));
+ git_index_free(index);
+
+ cl_git_pass(diff_from_buffer(&diff, diff_file, strlen(diff_file)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, NULL));
+
+ validate_apply_index(repo, index_expected, index_expected_cnt);
+ validate_workdir_unchanged(repo);
+
+ git_diff_free(diff);
+}
+
+void test_apply_index__change_mode(void)
+{
+ git_diff *diff;
+
+ const char *diff_file = DIFF_EXECUTABLE_FILE;
+
+ struct merge_index_entry index_expected[] = {
+ { 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
+ { 0100755, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" },
+ };
+ size_t index_expected_cnt = sizeof(index_expected) /
+ sizeof(struct merge_index_entry);
+
+ cl_git_pass(diff_from_buffer(&diff, diff_file, strlen(diff_file)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_INDEX, NULL));
+
+ validate_apply_index(repo, index_expected, index_expected_cnt);
+ validate_workdir_unchanged(repo);
+
+ git_diff_free(diff);
+}
diff --git a/tests/libgit2/apply/partial.c b/tests/libgit2/apply/partial.c
new file mode 100644
index 0000000..fd4908d
--- /dev/null
+++ b/tests/libgit2/apply/partial.c
@@ -0,0 +1,231 @@
+#include "clar_libgit2.h"
+#include "git2/sys/repository.h"
+
+#include "apply.h"
+#include "repository.h"
+
+#include "../patch/patch_common.h"
+
+static git_repository *repo = NULL;
+
+void test_apply_partial__initialize(void)
+{
+ repo = cl_git_sandbox_init("renames");
+}
+
+void test_apply_partial__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static int skip_addition(
+ const git_diff_hunk *hunk,
+ void *payload)
+{
+ GIT_UNUSED(payload);
+
+ return (hunk->new_lines > hunk->old_lines) ? 1 : 0;
+}
+
+static int skip_deletion(
+ const git_diff_hunk *hunk,
+ void *payload)
+{
+ GIT_UNUSED(payload);
+
+ return (hunk->new_lines < hunk->old_lines) ? 1 : 0;
+}
+
+static int skip_change(
+ const git_diff_hunk *hunk,
+ void *payload)
+{
+ GIT_UNUSED(payload);
+
+ return (hunk->new_lines == hunk->old_lines) ? 1 : 0;
+}
+
+static int abort_addition(
+ const git_diff_hunk *hunk,
+ void *payload)
+{
+ GIT_UNUSED(payload);
+
+ return (hunk->new_lines > hunk->old_lines) ? GIT_EUSER : 0;
+}
+
+static int abort_deletion(
+ const git_diff_hunk *hunk,
+ void *payload)
+{
+ GIT_UNUSED(payload);
+
+ return (hunk->new_lines < hunk->old_lines) ? GIT_EUSER : 0;
+}
+
+static int abort_change(
+ const git_diff_hunk *hunk,
+ void *payload)
+{
+ GIT_UNUSED(payload);
+
+ return (hunk->new_lines == hunk->old_lines) ? GIT_EUSER : 0;
+}
+
+static int apply_buf(
+ const char *old,
+ const char *oldname,
+ const char *new,
+ const char *newname,
+ const char *expected,
+ const git_diff_options *diff_opts,
+ git_apply_hunk_cb hunk_cb,
+ void *payload)
+{
+ git_patch *patch;
+ git_str result = GIT_STR_INIT;
+ git_str patchbuf = GIT_STR_INIT;
+ git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
+ char *filename;
+ unsigned int mode;
+ int error;
+ size_t oldsize = strlen(old);
+ size_t newsize = strlen(new);
+
+ opts.hunk_cb = hunk_cb;
+ opts.payload = payload;
+
+ cl_git_pass(git_patch_from_buffers(&patch, old, oldsize, oldname, new, newsize, newname, diff_opts));
+ if ((error = git_apply__patch(&result, &filename, &mode, old, oldsize, patch, &opts)) == 0) {
+ cl_assert_equal_s(expected, result.ptr);
+ cl_assert_equal_s(newname, filename);
+ cl_assert_equal_i(0100644, mode);
+ }
+
+ git__free(filename);
+ git_str_dispose(&result);
+ git_str_dispose(&patchbuf);
+ git_patch_free(patch);
+
+ return error;
+}
+
+void test_apply_partial__prepend_and_change_skip_addition(void)
+{
+ cl_git_pass(apply_buf(
+ FILE_ORIGINAL, "file.txt",
+ FILE_PREPEND_AND_CHANGE, "file.txt",
+ FILE_ORIGINAL, NULL, skip_addition, NULL));
+}
+
+void test_apply_partial__prepend_and_change_nocontext_skip_addition(void)
+{
+ git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
+ diff_opts.context_lines = 0;
+
+ cl_git_pass(apply_buf(
+ FILE_ORIGINAL, "file.txt",
+ FILE_PREPEND_AND_CHANGE, "file.txt",
+ FILE_CHANGE_MIDDLE, &diff_opts, skip_addition, NULL));
+}
+
+void test_apply_partial__prepend_and_change_nocontext_abort_addition(void)
+{
+ git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
+ diff_opts.context_lines = 0;
+
+ cl_git_fail(apply_buf(
+ FILE_ORIGINAL, "file.txt",
+ FILE_PREPEND_AND_CHANGE, "file.txt",
+ FILE_ORIGINAL, &diff_opts, abort_addition, NULL));
+}
+
+void test_apply_partial__prepend_and_change_skip_change(void)
+{
+ cl_git_pass(apply_buf(
+ FILE_ORIGINAL, "file.txt",
+ FILE_PREPEND_AND_CHANGE, "file.txt",
+ FILE_PREPEND_AND_CHANGE, NULL, skip_change, NULL));
+}
+
+void test_apply_partial__prepend_and_change_nocontext_skip_change(void)
+{
+ git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
+ diff_opts.context_lines = 0;
+
+ cl_git_pass(apply_buf(
+ FILE_ORIGINAL, "file.txt",
+ FILE_PREPEND_AND_CHANGE, "file.txt",
+ FILE_PREPEND, &diff_opts, skip_change, NULL));
+}
+
+void test_apply_partial__prepend_and_change_nocontext_abort_change(void)
+{
+ git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
+ diff_opts.context_lines = 0;
+
+ cl_git_fail(apply_buf(
+ FILE_ORIGINAL, "file.txt",
+ FILE_PREPEND_AND_CHANGE, "file.txt",
+ FILE_PREPEND, &diff_opts, abort_change, NULL));
+}
+
+void test_apply_partial__delete_and_change_skip_deletion(void)
+{
+ cl_git_pass(apply_buf(
+ FILE_ORIGINAL, "file.txt",
+ FILE_DELETE_AND_CHANGE, "file.txt",
+ FILE_ORIGINAL, NULL, skip_deletion, NULL));
+}
+
+void test_apply_partial__delete_and_change_nocontext_skip_deletion(void)
+{
+ git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
+ diff_opts.context_lines = 0;
+
+ cl_git_pass(apply_buf(
+ FILE_ORIGINAL, "file.txt",
+ FILE_DELETE_AND_CHANGE, "file.txt",
+ FILE_CHANGE_MIDDLE, &diff_opts, skip_deletion, NULL));
+}
+
+void test_apply_partial__delete_and_change_nocontext_abort_deletion(void)
+{
+ git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
+ diff_opts.context_lines = 0;
+
+ cl_git_fail(apply_buf(
+ FILE_ORIGINAL, "file.txt",
+ FILE_DELETE_AND_CHANGE, "file.txt",
+ FILE_ORIGINAL, &diff_opts, abort_deletion, NULL));
+}
+
+void test_apply_partial__delete_and_change_skip_change(void)
+{
+ cl_git_pass(apply_buf(
+ FILE_ORIGINAL, "file.txt",
+ FILE_DELETE_AND_CHANGE, "file.txt",
+ FILE_DELETE_AND_CHANGE, NULL, skip_change, NULL));
+}
+
+void test_apply_partial__delete_and_change_nocontext_skip_change(void)
+{
+ git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
+ diff_opts.context_lines = 0;
+
+ cl_git_pass(apply_buf(
+ FILE_ORIGINAL, "file.txt",
+ FILE_DELETE_AND_CHANGE, "file.txt",
+ FILE_DELETE_FIRSTLINE, &diff_opts, skip_change, NULL));
+}
+
+void test_apply_partial__delete_and_change_nocontext_abort_change(void)
+{
+ git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
+ diff_opts.context_lines = 0;
+
+ cl_git_fail(apply_buf(
+ FILE_ORIGINAL, "file.txt",
+ FILE_DELETE_AND_CHANGE, "file.txt",
+ FILE_DELETE_FIRSTLINE, &diff_opts, abort_change, NULL));
+}
diff --git a/tests/libgit2/apply/tree.c b/tests/libgit2/apply/tree.c
new file mode 100644
index 0000000..b97fe8d
--- /dev/null
+++ b/tests/libgit2/apply/tree.c
@@ -0,0 +1,94 @@
+#include "clar_libgit2.h"
+#include "apply_helpers.h"
+#include "../merge/merge_helpers.h"
+
+static git_repository *repo;
+
+#define TEST_REPO_PATH "merge-recursive"
+
+
+void test_apply_tree__initialize(void)
+{
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+}
+
+void test_apply_tree__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_apply_tree__one(void)
+{
+ git_oid a_oid, b_oid;
+ git_commit *a_commit, *b_commit;
+ git_tree *a_tree, *b_tree;
+ git_diff *diff;
+ git_index *index = NULL;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+
+ struct merge_index_entry expected[] = {
+ { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
+ };
+
+ git_oid__fromstr(&a_oid, "539bd011c4822c560c1d17cab095006b7a10f707", GIT_OID_SHA1);
+ git_oid__fromstr(&b_oid, "7c7bf85e978f1d18c0566f702d2cb7766b9c8d4f", GIT_OID_SHA1);
+
+ cl_git_pass(git_commit_lookup(&a_commit, repo, &a_oid));
+ cl_git_pass(git_commit_lookup(&b_commit, repo, &b_oid));
+
+ cl_git_pass(git_commit_tree(&a_tree, a_commit));
+ cl_git_pass(git_commit_tree(&b_tree, b_commit));
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, repo, a_tree, b_tree, &opts));
+
+ cl_git_pass(git_apply_to_tree(&index, repo, a_tree, diff, NULL));
+ merge_test_index(index, expected, 6);
+
+ git_index_free(index);
+ git_diff_free(diff);
+ git_tree_free(a_tree);
+ git_tree_free(b_tree);
+ git_commit_free(a_commit);
+ git_commit_free(b_commit);
+}
+
+void test_apply_tree__adds_file(void)
+{
+ git_oid a_oid;
+ git_commit *a_commit;
+ git_tree *a_tree;
+ git_diff *diff;
+ git_index *index = NULL;
+
+ struct merge_index_entry expected[] = {
+ { 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "6370543fcfedb3e6516ec53b06158f3687dc1447", 0, "newfile.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" },
+ };
+
+ git_oid__fromstr(&a_oid, "539bd011c4822c560c1d17cab095006b7a10f707", GIT_OID_SHA1);
+
+ cl_git_pass(git_commit_lookup(&a_commit, repo, &a_oid));
+
+ cl_git_pass(git_commit_tree(&a_tree, a_commit));
+
+ cl_git_pass(diff_from_buffer(&diff,
+ DIFF_ADD_FILE, strlen(DIFF_ADD_FILE)));
+
+ cl_git_pass(git_apply_to_tree(&index, repo, a_tree, diff, NULL));
+ merge_test_index(index, expected, 7);
+
+ git_index_free(index);
+ git_diff_free(diff);
+ git_tree_free(a_tree);
+ git_commit_free(a_commit);
+}
diff --git a/tests/libgit2/apply/workdir.c b/tests/libgit2/apply/workdir.c
new file mode 100644
index 0000000..5ae5684
--- /dev/null
+++ b/tests/libgit2/apply/workdir.c
@@ -0,0 +1,358 @@
+#include "clar_libgit2.h"
+#include "apply_helpers.h"
+
+static git_repository *repo;
+
+#define TEST_REPO_PATH "merge-recursive"
+
+void test_apply_workdir__initialize(void)
+{
+ git_oid oid;
+ git_commit *commit;
+
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+
+ git_oid__fromstr(&oid, "539bd011c4822c560c1d17cab095006b7a10f707", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+ cl_git_pass(git_reset(repo, (git_object *)commit, GIT_RESET_HARD, NULL));
+ git_commit_free(commit);
+}
+
+void test_apply_workdir__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_apply_workdir__generated_diff(void)
+{
+ git_oid a_oid, b_oid;
+ git_commit *a_commit, *b_commit;
+ git_tree *a_tree, *b_tree;
+ git_diff *diff;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+
+ struct merge_index_entry workdir_expected[] = {
+ { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
+ };
+ size_t workdir_expected_cnt = sizeof(workdir_expected) /
+ sizeof(struct merge_index_entry);
+
+ git_oid__fromstr(&a_oid, "539bd011c4822c560c1d17cab095006b7a10f707", GIT_OID_SHA1);
+ git_oid__fromstr(&b_oid, "7c7bf85e978f1d18c0566f702d2cb7766b9c8d4f", GIT_OID_SHA1); cl_git_pass(git_commit_lookup(&a_commit, repo, &a_oid));
+ cl_git_pass(git_commit_lookup(&b_commit, repo, &b_oid));
+
+ cl_git_pass(git_commit_tree(&a_tree, a_commit));
+ cl_git_pass(git_commit_tree(&b_tree, b_commit));
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, repo, a_tree, b_tree, &opts));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, NULL));
+
+ validate_index_unchanged(repo);
+ validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
+
+ git_diff_free(diff);
+ git_tree_free(a_tree);
+ git_tree_free(b_tree);
+ git_commit_free(a_commit);
+ git_commit_free(b_commit);
+}
+
+void test_apply_workdir__parsed_diff(void)
+{
+ git_diff *diff;
+
+ struct merge_index_entry workdir_expected[] = {
+ { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
+ };
+ size_t workdir_expected_cnt = sizeof(workdir_expected) /
+ sizeof(struct merge_index_entry);
+
+ cl_git_pass(diff_from_buffer(&diff,
+ DIFF_MODIFY_TWO_FILES, strlen(DIFF_MODIFY_TWO_FILES)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, NULL));
+
+ validate_index_unchanged(repo);
+ validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
+
+ git_diff_free(diff);
+}
+
+void test_apply_workdir__removes_file(void)
+{
+ git_diff *diff;
+
+ struct merge_index_entry workdir_expected[] = {
+ { 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" },
+ };
+ size_t workdir_expected_cnt = sizeof(workdir_expected) /
+ sizeof(struct merge_index_entry);
+
+ cl_git_pass(diff_from_buffer(&diff, DIFF_DELETE_FILE,
+ strlen(DIFF_DELETE_FILE)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, NULL));
+
+ validate_index_unchanged(repo);
+ validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
+
+ git_diff_free(diff);
+}
+
+void test_apply_workdir__adds_file(void)
+{
+ git_diff *diff;
+
+ struct merge_index_entry workdir_expected[] = {
+ { 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "6370543fcfedb3e6516ec53b06158f3687dc1447", 0, "newfile.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" },
+ };
+ size_t workdir_expected_cnt = sizeof(workdir_expected) /
+ sizeof(struct merge_index_entry);
+
+ cl_git_pass(diff_from_buffer(&diff,
+ DIFF_ADD_FILE, strlen(DIFF_ADD_FILE)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, NULL));
+
+ validate_index_unchanged(repo);
+ validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
+
+ git_diff_free(diff);
+}
+
+void test_apply_workdir__modified_index_with_unmodified_workdir_is_ok(void)
+{
+ git_index *index;
+ git_index_entry idx_entry = {{0}};
+ git_diff *diff;
+
+ const char *diff_file = DIFF_MODIFY_TWO_FILES;
+
+ struct merge_index_entry index_expected[] = {
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "veal.txt" }
+ };
+ size_t index_expected_cnt = sizeof(index_expected) /
+ sizeof(struct merge_index_entry);
+
+ struct merge_index_entry workdir_expected[] = {
+ { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
+ };
+ size_t workdir_expected_cnt = sizeof(workdir_expected) /
+ sizeof(struct merge_index_entry);
+
+ /* mutate the index and leave the workdir matching HEAD */
+ cl_git_pass(git_repository_index(&index, repo));
+
+ idx_entry.mode = 0100644;
+ idx_entry.path = "veal.txt";
+ cl_git_pass(git_oid__fromstr(&idx_entry.id, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", GIT_OID_SHA1));
+
+ cl_git_pass(git_index_add(index, &idx_entry));
+ cl_git_pass(git_index_remove(index, "asparagus.txt", 0));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(diff_from_buffer(&diff, diff_file, strlen(diff_file)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, NULL));
+
+ validate_apply_index(repo, index_expected, index_expected_cnt);
+ validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
+
+ git_index_free(index);
+ git_diff_free(diff);
+}
+
+void test_apply_workdir__application_failure_leaves_workdir_unmodified(void)
+{
+ git_diff *diff;
+
+ const char *diff_file = DIFF_MODIFY_TWO_FILES;
+
+ struct merge_index_entry workdir_expected[] = {
+ { 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "8684724651336001c5dbce74bed6736d2443958d", 0, "veal.txt" },
+ };
+ size_t workdir_expected_cnt = sizeof(workdir_expected) /
+ sizeof(struct merge_index_entry);
+
+ /* mutate the workdir */
+ cl_git_rewritefile("merge-recursive/veal.txt",
+ "This is a modification.\n");
+
+ cl_git_pass(diff_from_buffer(&diff, diff_file, strlen(diff_file)));
+ cl_git_fail_with(GIT_EAPPLYFAIL, git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, NULL));
+
+ validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
+
+ git_diff_free(diff);
+}
+
+void test_apply_workdir__keeps_nonconflicting_changes(void)
+{
+ git_diff *diff;
+
+ struct merge_index_entry workdir_expected[] = {
+ { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "f75ba05f340c51065cbea2e1fdbfe5fe13144c97", 0, "gravy.txt" },
+ { 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
+ };
+ size_t workdir_expected_cnt = sizeof(workdir_expected) /
+ sizeof(struct merge_index_entry);
+
+ cl_git_rmfile("merge-recursive/oyster.txt");
+ cl_git_rewritefile("merge-recursive/gravy.txt", "Hello, world.\n");
+
+ cl_git_pass(diff_from_buffer(&diff,
+ DIFF_MODIFY_TWO_FILES, strlen(DIFF_MODIFY_TWO_FILES)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, NULL));
+
+ validate_index_unchanged(repo);
+ validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
+
+ git_diff_free(diff);
+}
+
+void test_apply_workdir__can_apply_nonconflicting_file_changes(void)
+{
+ git_diff *diff;
+
+ const char *diff_file = DIFF_MODIFY_TWO_FILES;
+
+ struct merge_index_entry workdir_expected[] = {
+ { 0100644, "5db1a0fef164cb66cc0c00d35cc5af979ddc1a64", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
+ };
+ size_t workdir_expected_cnt = sizeof(workdir_expected) /
+ sizeof(struct merge_index_entry);
+
+ /*
+ * Replace the workdir file with a version that is different than
+ * HEAD but such that the patch still applies cleanly. This item
+ * has a new line appended.
+ */
+ cl_git_append2file("merge-recursive/asparagus.txt",
+ "This line is added in the workdir.\n");
+
+ cl_git_pass(diff_from_buffer(&diff, diff_file, strlen(diff_file)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, NULL));
+
+ validate_index_unchanged(repo);
+ validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
+
+ git_diff_free(diff);
+}
+
+void test_apply_workdir__change_mode(void)
+{
+#ifndef GIT_WIN32
+ git_diff *diff;
+
+ const char *diff_file = DIFF_EXECUTABLE_FILE;
+
+ struct merge_index_entry workdir_expected[] = {
+ { 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
+ { 0100755, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" },
+ };
+ size_t workdir_expected_cnt = sizeof(workdir_expected) /
+ sizeof(struct merge_index_entry);
+
+ cl_git_pass(diff_from_buffer(&diff, diff_file, strlen(diff_file)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, NULL));
+
+ validate_index_unchanged(repo);
+ validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
+
+ git_diff_free(diff);
+#endif
+}
+
+void test_apply_workdir__apply_many_changes_one(void)
+{
+ git_diff *diff;
+ git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
+
+ struct merge_index_entry workdir_expected[] = {
+ { 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "c9d7d5d58088bc91f6e06f17ca3a205091568d3a", 0, "veal.txt" },
+ };
+ size_t workdir_expected_cnt = sizeof(workdir_expected) /
+ sizeof(struct merge_index_entry);
+
+ cl_git_pass(diff_from_buffer(&diff,
+ DIFF_MANY_CHANGES_ONE, strlen(DIFF_MANY_CHANGES_ONE)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, &opts));
+
+ validate_index_unchanged(repo);
+ validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
+
+ git_diff_free(diff);
+}
+
+void test_apply_workdir__apply_many_changes_two(void)
+{
+ git_diff *diff;
+ git_apply_options opts = GIT_APPLY_OPTIONS_INIT;
+
+ struct merge_index_entry workdir_expected[] = {
+ { 0100644, "f51658077d85f2264fa179b4d0848268cb3475c3", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "6b943d65af6d8db74d747284fa4ca7d716ad5bbb", 0, "veal.txt" },
+ };
+ size_t workdir_expected_cnt = sizeof(workdir_expected) /
+ sizeof(struct merge_index_entry);
+
+ cl_git_pass(diff_from_buffer(&diff,
+ DIFF_MANY_CHANGES_TWO, strlen(DIFF_MANY_CHANGES_TWO)));
+ cl_git_pass(git_apply(repo, diff, GIT_APPLY_LOCATION_WORKDIR, &opts));
+
+ validate_index_unchanged(repo);
+ validate_apply_workdir(repo, workdir_expected, workdir_expected_cnt);
+
+ git_diff_free(diff);
+}
diff --git a/tests/libgit2/attr/attr_expect.h b/tests/libgit2/attr/attr_expect.h
new file mode 100644
index 0000000..faf7a11
--- /dev/null
+++ b/tests/libgit2/attr/attr_expect.h
@@ -0,0 +1,43 @@
+#ifndef __CLAR_TEST_ATTR_EXPECT__
+#define __CLAR_TEST_ATTR_EXPECT__
+
+enum attr_expect_t {
+ EXPECT_FALSE,
+ EXPECT_TRUE,
+ EXPECT_UNDEFINED,
+ EXPECT_STRING
+};
+
+struct attr_expected {
+ const char *path;
+ const char *attr;
+ enum attr_expect_t expected;
+ const char *expected_str;
+};
+
+GIT_INLINE(void) attr_check_expected(
+ enum attr_expect_t expected,
+ const char *expected_str,
+ const char *name,
+ const char *value)
+{
+ switch (expected) {
+ case EXPECT_TRUE:
+ cl_assert_(GIT_ATTR_IS_TRUE(value), name);
+ break;
+
+ case EXPECT_FALSE:
+ cl_assert_(GIT_ATTR_IS_FALSE(value), name);
+ break;
+
+ case EXPECT_UNDEFINED:
+ cl_assert_(GIT_ATTR_IS_UNSPECIFIED(value), name);
+ break;
+
+ case EXPECT_STRING:
+ cl_assert_equal_s(expected_str, value);
+ break;
+ }
+}
+
+#endif
diff --git a/tests/libgit2/attr/file.c b/tests/libgit2/attr/file.c
new file mode 100644
index 0000000..1ac48c0
--- /dev/null
+++ b/tests/libgit2/attr/file.c
@@ -0,0 +1,243 @@
+#include "clar_libgit2.h"
+#include "attr_file.h"
+#include "attr_expect.h"
+
+#define get_rule(X) ((git_attr_rule *)git_vector_get(&file->rules,(X)))
+#define get_assign(R,Y) ((git_attr_assignment *)git_vector_get(&(R)->assigns,(Y)))
+
+void test_attr_file__simple_read(void)
+{
+ git_attr_file *file;
+ git_attr_assignment *assign;
+ git_attr_rule *rule;
+
+ cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr0")));
+
+ cl_assert_equal_s(cl_fixture("attr/attr0"), file->entry->path);
+ cl_assert(file->rules.length == 1);
+
+ rule = get_rule(0);
+ cl_assert(rule != NULL);
+ cl_assert_equal_s("*", rule->match.pattern);
+ cl_assert(rule->match.length == 1);
+ cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_HASWILD) != 0);
+
+ cl_assert(rule->assigns.length == 1);
+ assign = get_assign(rule, 0);
+ cl_assert(assign != NULL);
+ cl_assert_equal_s("binary", assign->name);
+ cl_assert(GIT_ATTR_IS_TRUE(assign->value));
+
+ git_attr_file__free(file);
+}
+
+void test_attr_file__match_variants(void)
+{
+ git_attr_file *file;
+ git_attr_rule *rule;
+ git_attr_assignment *assign;
+
+ cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr1")));
+
+ cl_assert_equal_s(cl_fixture("attr/attr1"), file->entry->path);
+ cl_assert(file->rules.length == 10);
+
+ /* let's do a thorough check of this rule, then just verify
+ * the things that are unique for the later rules
+ */
+ rule = get_rule(0);
+ cl_assert(rule);
+ cl_assert_equal_s("pat0", rule->match.pattern);
+ cl_assert(rule->match.length == strlen("pat0"));
+ cl_assert(rule->assigns.length == 1);
+ assign = get_assign(rule,0);
+ cl_assert_equal_s("attr0", assign->name);
+ cl_assert(assign->name_hash == git_attr_file__name_hash(assign->name));
+ cl_assert(GIT_ATTR_IS_TRUE(assign->value));
+
+ rule = get_rule(1);
+ cl_assert_equal_s("pat1", rule->match.pattern);
+ cl_assert(rule->match.length == strlen("pat1"));
+ cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_NEGATIVE) != 0);
+
+ rule = get_rule(2);
+ cl_assert_equal_s("pat2", rule->match.pattern);
+ cl_assert(rule->match.length == strlen("pat2"));
+ cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_DIRECTORY) != 0);
+
+ rule = get_rule(3);
+ cl_assert_equal_s("pat3dir/pat3file", rule->match.pattern);
+ cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_FULLPATH) != 0);
+
+ rule = get_rule(4);
+ cl_assert_equal_s("pat4.*", rule->match.pattern);
+ cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_HASWILD) != 0);
+
+ rule = get_rule(5);
+ cl_assert_equal_s("*.pat5", rule->match.pattern);
+ cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_HASWILD) != 0);
+
+ rule = get_rule(7);
+ cl_assert_equal_s("pat7[a-e]??[xyz]", rule->match.pattern);
+ cl_assert(rule->assigns.length == 1);
+ cl_assert((rule->match.flags & GIT_ATTR_FNMATCH_HASWILD) != 0);
+ assign = get_assign(rule,0);
+ cl_assert_equal_s("attr7", assign->name);
+ cl_assert(GIT_ATTR_IS_TRUE(assign->value));
+
+ rule = get_rule(8);
+ cl_assert_equal_s("pat8 with spaces", rule->match.pattern);
+ cl_assert(rule->match.length == strlen("pat8 with spaces"));
+
+ rule = get_rule(9);
+ cl_assert_equal_s("pat9", rule->match.pattern);
+
+ git_attr_file__free(file);
+}
+
+static void check_one_assign(
+ git_attr_file *file,
+ int rule_idx,
+ int assign_idx,
+ const char *pattern,
+ const char *name,
+ enum attr_expect_t expected,
+ const char *expected_str)
+{
+ git_attr_rule *rule = get_rule(rule_idx);
+ git_attr_assignment *assign = get_assign(rule, assign_idx);
+
+ cl_assert_equal_s(pattern, rule->match.pattern);
+ cl_assert(rule->assigns.length == 1);
+ cl_assert_equal_s(name, assign->name);
+ cl_assert(assign->name_hash == git_attr_file__name_hash(assign->name));
+
+ attr_check_expected(expected, expected_str, assign->name, assign->value);
+}
+
+void test_attr_file__assign_variants(void)
+{
+ git_attr_file *file;
+ git_attr_rule *rule;
+ git_attr_assignment *assign;
+
+ cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr2")));
+
+ cl_assert_equal_s(cl_fixture("attr/attr2"), file->entry->path);
+ cl_assert(file->rules.length == 11);
+
+ check_one_assign(file, 0, 0, "pat0", "simple", EXPECT_TRUE, NULL);
+ check_one_assign(file, 1, 0, "pat1", "neg", EXPECT_FALSE, NULL);
+ check_one_assign(file, 2, 0, "*", "notundef", EXPECT_TRUE, NULL);
+ check_one_assign(file, 3, 0, "pat2", "notundef", EXPECT_UNDEFINED, NULL);
+ check_one_assign(file, 4, 0, "pat3", "assigned", EXPECT_STRING, "test-value");
+ check_one_assign(file, 5, 0, "pat4", "rule-with-more-chars", EXPECT_STRING, "value-with-more-chars");
+ check_one_assign(file, 6, 0, "pat5", "empty", EXPECT_TRUE, NULL);
+ check_one_assign(file, 7, 0, "pat6", "negempty", EXPECT_FALSE, NULL);
+
+ rule = get_rule(8);
+ cl_assert_equal_s("pat7", rule->match.pattern);
+ cl_assert(rule->assigns.length == 5);
+ /* assignments will be sorted by hash value, so we have to do
+ * lookups by search instead of by position
+ */
+ assign = git_attr_rule__lookup_assignment(rule, "multiple");
+ cl_assert(assign);
+ cl_assert_equal_s("multiple", assign->name);
+ cl_assert(GIT_ATTR_IS_TRUE(assign->value));
+ assign = git_attr_rule__lookup_assignment(rule, "single");
+ cl_assert(assign);
+ cl_assert_equal_s("single", assign->name);
+ cl_assert(GIT_ATTR_IS_FALSE(assign->value));
+ assign = git_attr_rule__lookup_assignment(rule, "values");
+ cl_assert(assign);
+ cl_assert_equal_s("values", assign->name);
+ cl_assert_equal_s("1", assign->value);
+ assign = git_attr_rule__lookup_assignment(rule, "also");
+ cl_assert(assign);
+ cl_assert_equal_s("also", assign->name);
+ cl_assert_equal_s("a-really-long-value/*", assign->value);
+ assign = git_attr_rule__lookup_assignment(rule, "happy");
+ cl_assert(assign);
+ cl_assert_equal_s("happy", assign->name);
+ cl_assert_equal_s("yes!", assign->value);
+ assign = git_attr_rule__lookup_assignment(rule, "other");
+ cl_assert(!assign);
+
+ rule = get_rule(9);
+ cl_assert_equal_s("pat8", rule->match.pattern);
+ cl_assert(rule->assigns.length == 2);
+ assign = git_attr_rule__lookup_assignment(rule, "again");
+ cl_assert(assign);
+ cl_assert_equal_s("again", assign->name);
+ cl_assert(GIT_ATTR_IS_TRUE(assign->value));
+ assign = git_attr_rule__lookup_assignment(rule, "another");
+ cl_assert(assign);
+ cl_assert_equal_s("another", assign->name);
+ cl_assert_equal_s("12321", assign->value);
+
+ check_one_assign(file, 10, 0, "pat9", "at-eof", EXPECT_FALSE, NULL);
+
+ git_attr_file__free(file);
+}
+
+static void assert_examples(git_attr_file *file)
+{
+ git_attr_rule *rule;
+ git_attr_assignment *assign;
+
+ rule = get_rule(0);
+ cl_assert_equal_s("*.java", rule->match.pattern);
+ cl_assert(rule->assigns.length == 3);
+ assign = git_attr_rule__lookup_assignment(rule, "diff");
+ cl_assert_equal_s("diff", assign->name);
+ cl_assert_equal_s("java", assign->value);
+ assign = git_attr_rule__lookup_assignment(rule, "crlf");
+ cl_assert_equal_s("crlf", assign->name);
+ cl_assert(GIT_ATTR_IS_FALSE(assign->value));
+ assign = git_attr_rule__lookup_assignment(rule, "myAttr");
+ cl_assert_equal_s("myAttr", assign->name);
+ cl_assert(GIT_ATTR_IS_TRUE(assign->value));
+ assign = git_attr_rule__lookup_assignment(rule, "missing");
+ cl_assert(assign == NULL);
+
+ rule = get_rule(1);
+ cl_assert_equal_s("NoMyAttr.java", rule->match.pattern);
+ cl_assert(rule->assigns.length == 1);
+ assign = get_assign(rule, 0);
+ cl_assert_equal_s("myAttr", assign->name);
+ cl_assert(GIT_ATTR_IS_UNSPECIFIED(assign->value));
+
+ rule = get_rule(2);
+ cl_assert_equal_s("README", rule->match.pattern);
+ cl_assert(rule->assigns.length == 1);
+ assign = get_assign(rule, 0);
+ cl_assert_equal_s("caveat", assign->name);
+ cl_assert_equal_s("unspecified", assign->value);
+}
+
+void test_attr_file__check_attr_examples(void)
+{
+ git_attr_file *file;
+
+ cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr3")));
+ cl_assert_equal_s(cl_fixture("attr/attr3"), file->entry->path);
+ cl_assert(file->rules.length == 3);
+
+ assert_examples(file);
+
+ git_attr_file__free(file);
+}
+
+void test_attr_file__whitespace(void)
+{
+ git_attr_file *file;
+
+ cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr4")));
+ cl_assert_equal_s(cl_fixture("attr/attr4"), file->entry->path);
+ cl_assert(file->rules.length == 3);
+
+ assert_examples(file);
+
+ git_attr_file__free(file);
+}
diff --git a/tests/libgit2/attr/flags.c b/tests/libgit2/attr/flags.c
new file mode 100644
index 0000000..6470ec7
--- /dev/null
+++ b/tests/libgit2/attr/flags.c
@@ -0,0 +1,108 @@
+#include "clar_libgit2.h"
+#include "git2/attr.h"
+
+void test_attr_flags__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_attr_flags__bare(void)
+{
+ git_repository *repo = cl_git_sandbox_init("testrepo.git");
+ const char *value;
+
+ cl_assert(git_repository_is_bare(repo));
+
+ cl_git_pass(git_attr_get(
+ &value, repo, GIT_ATTR_CHECK_NO_SYSTEM, "README.md", "diff"));
+ cl_assert(GIT_ATTR_IS_UNSPECIFIED(value));
+}
+
+void test_attr_flags__index_vs_workdir(void)
+{
+ git_repository *repo = cl_git_sandbox_init("attr_index");
+ const char *value;
+
+ cl_assert(!git_repository_is_bare(repo));
+
+ /* wd then index */
+ cl_git_pass(git_attr_get(
+ &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_FILE_THEN_INDEX,
+ "README.md", "bar"));
+ cl_assert(GIT_ATTR_IS_FALSE(value));
+
+ cl_git_pass(git_attr_get(
+ &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_FILE_THEN_INDEX,
+ "README.md", "blargh"));
+ cl_assert_equal_s(value, "goop");
+
+ cl_git_pass(git_attr_get(
+ &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_FILE_THEN_INDEX,
+ "README.txt", "foo"));
+ cl_assert(GIT_ATTR_IS_FALSE(value));
+
+ /* index then wd */
+ cl_git_pass(git_attr_get(
+ &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_INDEX_THEN_FILE,
+ "README.md", "bar"));
+ cl_assert(GIT_ATTR_IS_TRUE(value));
+
+ cl_git_pass(git_attr_get(
+ &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_INDEX_THEN_FILE,
+ "README.md", "blargh"));
+ cl_assert_equal_s(value, "garble");
+
+ cl_git_pass(git_attr_get(
+ &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_INDEX_THEN_FILE,
+ "README.txt", "foo"));
+ cl_assert(GIT_ATTR_IS_TRUE(value));
+}
+
+void test_attr_flags__subdir(void)
+{
+ git_repository *repo = cl_git_sandbox_init("attr_index");
+ const char *value;
+
+ /* wd then index */
+ cl_git_pass(git_attr_get(
+ &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_FILE_THEN_INDEX,
+ "sub/sub/README.md", "bar"));
+ cl_assert_equal_s(value, "1234");
+
+ cl_git_pass(git_attr_get(
+ &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_FILE_THEN_INDEX,
+ "sub/sub/README.txt", "another"));
+ cl_assert_equal_s(value, "one");
+
+ cl_git_pass(git_attr_get(
+ &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_FILE_THEN_INDEX,
+ "sub/sub/README.txt", "again"));
+ cl_assert(GIT_ATTR_IS_TRUE(value));
+
+ cl_git_pass(git_attr_get(
+ &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_FILE_THEN_INDEX,
+ "sub/sub/README.txt", "beep"));
+ cl_assert_equal_s(value, "10");
+
+ /* index then wd */
+ cl_git_pass(git_attr_get(
+ &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_INDEX_THEN_FILE,
+ "sub/sub/README.md", "bar"));
+ cl_assert_equal_s(value, "1337");
+
+ cl_git_pass(git_attr_get(
+ &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_INDEX_THEN_FILE,
+ "sub/sub/README.txt", "another"));
+ cl_assert_equal_s(value, "one");
+
+ cl_git_pass(git_attr_get(
+ &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_INDEX_THEN_FILE,
+ "sub/sub/README.txt", "again"));
+ cl_assert(GIT_ATTR_IS_TRUE(value));
+
+ cl_git_pass(git_attr_get(
+ &value, repo, GIT_ATTR_CHECK_NO_SYSTEM | GIT_ATTR_CHECK_INDEX_THEN_FILE,
+ "sub/sub/README.txt", "beep"));
+ cl_assert_equal_s(value, "5");
+}
+
diff --git a/tests/libgit2/attr/lookup.c b/tests/libgit2/attr/lookup.c
new file mode 100644
index 0000000..f19f38f
--- /dev/null
+++ b/tests/libgit2/attr/lookup.c
@@ -0,0 +1,263 @@
+#include "clar_libgit2.h"
+#include "attr_file.h"
+
+#include "attr_expect.h"
+
+void test_attr_lookup__simple(void)
+{
+ git_attr_file *file;
+ git_attr_path path;
+ const char *value = NULL;
+
+ cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr0")));
+ cl_assert_equal_s(cl_fixture("attr/attr0"), file->entry->path);
+ cl_assert(file->rules.length == 1);
+
+ cl_git_pass(git_attr_path__init(&path, "test", NULL, GIT_DIR_FLAG_UNKNOWN));
+ cl_assert_equal_s("test", path.path);
+ cl_assert_equal_s("test", path.basename);
+ cl_assert(!path.is_dir);
+
+ cl_git_pass(git_attr_file__lookup_one(file,&path,"binary",&value));
+ cl_assert(GIT_ATTR_IS_TRUE(value));
+
+ cl_git_pass(git_attr_file__lookup_one(file,&path,"missing",&value));
+ cl_assert(!value);
+
+ git_attr_path__free(&path);
+ git_attr_file__free(file);
+}
+
+static void run_test_cases(git_attr_file *file, struct attr_expected *cases, int force_dir)
+{
+ git_attr_path path;
+ const char *value = NULL;
+ struct attr_expected *c;
+ int error;
+
+ for (c = cases; c->path != NULL; c++) {
+ cl_git_pass(git_attr_path__init(&path, c->path, NULL, GIT_DIR_FLAG_UNKNOWN));
+
+ if (force_dir)
+ path.is_dir = 1;
+
+ error = git_attr_file__lookup_one(file,&path,c->attr,&value);
+ cl_git_pass(error);
+
+ attr_check_expected(c->expected, c->expected_str, c->attr, value);
+
+ git_attr_path__free(&path);
+ }
+}
+
+void test_attr_lookup__match_variants(void)
+{
+ git_attr_file *file;
+ git_attr_path path;
+
+ struct attr_expected dir_cases[] = {
+ { "pat2", "attr2", EXPECT_TRUE, NULL },
+ { "/testing/for/pat2", "attr2", EXPECT_TRUE, NULL },
+ { "/not/pat2/yousee", "attr2", EXPECT_UNDEFINED, NULL },
+ { "/fun/fun/fun/pat4.dir", "attr4", EXPECT_TRUE, NULL },
+ { "foo.pat5", "attr5", EXPECT_TRUE, NULL },
+ { NULL, NULL, 0, NULL }
+ };
+
+ struct attr_expected cases[] = {
+ /* pat0 -> simple match */
+ { "pat0", "attr0", EXPECT_TRUE, NULL },
+ { "/testing/for/pat0", "attr0", EXPECT_TRUE, NULL },
+ { "relative/to/pat0", "attr0", EXPECT_TRUE, NULL },
+ { "this-contains-pat0-inside", "attr0", EXPECT_UNDEFINED, NULL },
+ { "this-aint-right", "attr0", EXPECT_UNDEFINED, NULL },
+ { "/this/pat0/dont/match", "attr0", EXPECT_UNDEFINED, NULL },
+ /* negative match */
+ { "pat0", "attr1", EXPECT_TRUE, NULL },
+ { "pat1", "attr1", EXPECT_UNDEFINED, NULL },
+ { "/testing/for/pat1", "attr1", EXPECT_UNDEFINED, NULL },
+ { "/testing/for/pat0", "attr1", EXPECT_TRUE, NULL },
+ { "/testing/for/pat1/inside", "attr1", EXPECT_TRUE, NULL },
+ { "misc", "attr1", EXPECT_TRUE, NULL },
+ /* dir match */
+ { "pat2", "attr2", EXPECT_UNDEFINED, NULL },
+ { "/testing/for/pat2", "attr2", EXPECT_UNDEFINED, NULL },
+ { "/not/pat2/yousee", "attr2", EXPECT_UNDEFINED, NULL },
+ /* path match */
+ { "pat3file", "attr3", EXPECT_UNDEFINED, NULL },
+ { "/pat3dir/pat3file", "attr3", EXPECT_TRUE, NULL },
+ { "pat3dir/pat3file", "attr3", EXPECT_TRUE, NULL },
+ /* pattern* match */
+ { "pat4.txt", "attr4", EXPECT_TRUE, NULL },
+ { "/fun/fun/fun/pat4.c", "attr4", EXPECT_TRUE, NULL },
+ { "pat4.", "attr4", EXPECT_TRUE, NULL },
+ { "pat4", "attr4", EXPECT_UNDEFINED, NULL },
+ /* *pattern match */
+ { "foo.pat5", "attr5", EXPECT_TRUE, NULL },
+ { "/this/is/ok.pat5", "attr5", EXPECT_TRUE, NULL },
+ { "/this/is/bad.pat5/yousee.txt", "attr5", EXPECT_UNDEFINED, NULL },
+ { "foo.pat5", "attr100", EXPECT_UNDEFINED, NULL },
+ /* glob match with slashes */
+ { "foo.pat6", "attr6", EXPECT_UNDEFINED, NULL },
+ { "pat6/pat6/foobar.pat6", "attr6", EXPECT_TRUE, NULL },
+ { "pat6/pat6/.pat6", "attr6", EXPECT_TRUE, NULL },
+ { "pat6/pat6/extra/foobar.pat6", "attr6", EXPECT_UNDEFINED, NULL },
+ { "/prefix/pat6/pat6/foobar.pat6", "attr6", EXPECT_UNDEFINED, NULL },
+ { "/pat6/pat6/foobar.pat6", "attr6", EXPECT_TRUE, NULL },
+ /* complex pattern */
+ { "pat7a12z", "attr7", EXPECT_TRUE, NULL },
+ { "pat7e__x", "attr7", EXPECT_TRUE, NULL },
+ { "pat7b/1y", "attr7", EXPECT_UNDEFINED, NULL }, /* ? does not match / */
+ { "pat7e_x", "attr7", EXPECT_UNDEFINED, NULL },
+ { "pat7aaaa", "attr7", EXPECT_UNDEFINED, NULL },
+ { "pat7zzzz", "attr7", EXPECT_UNDEFINED, NULL },
+ { "/this/can/be/anything/pat7a12z", "attr7", EXPECT_TRUE, NULL },
+ { "but/it/still/must/match/pat7aaaa", "attr7", EXPECT_UNDEFINED, NULL },
+ { "pat7aaay.fail", "attr7", EXPECT_UNDEFINED, NULL },
+ /* pattern with spaces */
+ { "pat8 with spaces", "attr8", EXPECT_TRUE, NULL },
+ { "/gotta love/pat8 with spaces", "attr8", EXPECT_TRUE, NULL },
+ { "failing pat8 with spaces", "attr8", EXPECT_UNDEFINED, NULL },
+ { "spaces", "attr8", EXPECT_UNDEFINED, NULL },
+ /* pattern at eof */
+ { "pat9", "attr9", EXPECT_TRUE, NULL },
+ { "/eof/pat9", "attr9", EXPECT_TRUE, NULL },
+ { "pat", "attr9", EXPECT_UNDEFINED, NULL },
+ { "at9", "attr9", EXPECT_UNDEFINED, NULL },
+ { "pat9.fail", "attr9", EXPECT_UNDEFINED, NULL },
+ /* sentinel at end */
+ { NULL, NULL, 0, NULL }
+ };
+
+ cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr1")));
+ cl_assert_equal_s(cl_fixture("attr/attr1"), file->entry->path);
+ cl_assert(file->rules.length == 10);
+
+ cl_git_pass(git_attr_path__init(&path, "/testing/for/pat0", NULL, GIT_DIR_FLAG_UNKNOWN));
+ cl_assert_equal_s("pat0", path.basename);
+
+ run_test_cases(file, cases, 0);
+ run_test_cases(file, dir_cases, 1);
+
+ git_attr_file__free(file);
+ git_attr_path__free(&path);
+}
+
+void test_attr_lookup__assign_variants(void)
+{
+ git_attr_file *file;
+
+ struct attr_expected cases[] = {
+ /* pat0 -> simple assign */
+ { "pat0", "simple", EXPECT_TRUE, NULL },
+ { "/testing/pat0", "simple", EXPECT_TRUE, NULL },
+ { "pat0", "fail", EXPECT_UNDEFINED, NULL },
+ { "/testing/pat0", "fail", EXPECT_UNDEFINED, NULL },
+ /* negative assign */
+ { "pat1", "neg", EXPECT_FALSE, NULL },
+ { "/testing/pat1", "neg", EXPECT_FALSE, NULL },
+ { "pat1", "fail", EXPECT_UNDEFINED, NULL },
+ { "/testing/pat1", "fail", EXPECT_UNDEFINED, NULL },
+ /* forced undef */
+ { "pat1", "notundef", EXPECT_TRUE, NULL },
+ { "pat2", "notundef", EXPECT_UNDEFINED, NULL },
+ { "/lead/in/pat1", "notundef", EXPECT_TRUE, NULL },
+ { "/lead/in/pat2", "notundef", EXPECT_UNDEFINED, NULL },
+ /* assign value */
+ { "pat3", "assigned", EXPECT_STRING, "test-value" },
+ { "pat3", "notassigned", EXPECT_UNDEFINED, NULL },
+ /* assign value */
+ { "pat4", "rule-with-more-chars", EXPECT_STRING, "value-with-more-chars" },
+ { "pat4", "notassigned-rule-with-more-chars", EXPECT_UNDEFINED, NULL },
+ /* empty assignments */
+ { "pat5", "empty", EXPECT_TRUE, NULL },
+ { "pat6", "negempty", EXPECT_FALSE, NULL },
+ /* multiple assignment */
+ { "pat7", "multiple", EXPECT_TRUE, NULL },
+ { "pat7", "single", EXPECT_FALSE, NULL },
+ { "pat7", "values", EXPECT_STRING, "1" },
+ { "pat7", "also", EXPECT_STRING, "a-really-long-value/*" },
+ { "pat7", "happy", EXPECT_STRING, "yes!" },
+ { "pat8", "again", EXPECT_TRUE, NULL },
+ { "pat8", "another", EXPECT_STRING, "12321" },
+ /* bad assignment */
+ { "patbad0", "simple", EXPECT_UNDEFINED, NULL },
+ { "patbad0", "notundef", EXPECT_TRUE, NULL },
+ { "patbad1", "simple", EXPECT_UNDEFINED, NULL },
+ /* eof assignment */
+ { "pat9", "at-eof", EXPECT_FALSE, NULL },
+ /* sentinel at end */
+ { NULL, NULL, 0, NULL }
+ };
+
+ cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr2")));
+ cl_assert(file->rules.length == 11);
+
+ run_test_cases(file, cases, 0);
+
+ git_attr_file__free(file);
+}
+
+void test_attr_lookup__check_attr_examples(void)
+{
+ git_attr_file *file;
+
+ struct attr_expected cases[] = {
+ { "foo.java", "diff", EXPECT_STRING, "java" },
+ { "foo.java", "crlf", EXPECT_FALSE, NULL },
+ { "foo.java", "myAttr", EXPECT_TRUE, NULL },
+ { "foo.java", "other", EXPECT_UNDEFINED, NULL },
+ { "/prefix/dir/foo.java", "diff", EXPECT_STRING, "java" },
+ { "/prefix/dir/foo.java", "crlf", EXPECT_FALSE, NULL },
+ { "/prefix/dir/foo.java", "myAttr", EXPECT_TRUE, NULL },
+ { "/prefix/dir/foo.java", "other", EXPECT_UNDEFINED, NULL },
+ { "NoMyAttr.java", "crlf", EXPECT_FALSE, NULL },
+ { "NoMyAttr.java", "myAttr", EXPECT_UNDEFINED, NULL },
+ { "NoMyAttr.java", "other", EXPECT_UNDEFINED, NULL },
+ { "/prefix/dir/NoMyAttr.java", "crlf", EXPECT_FALSE, NULL },
+ { "/prefix/dir/NoMyAttr.java", "myAttr", EXPECT_UNDEFINED, NULL },
+ { "/prefix/dir/NoMyAttr.java", "other", EXPECT_UNDEFINED, NULL },
+ { "README", "caveat", EXPECT_STRING, "unspecified" },
+ { "/specific/path/README", "caveat", EXPECT_STRING, "unspecified" },
+ { "README", "missing", EXPECT_UNDEFINED, NULL },
+ { "/specific/path/README", "missing", EXPECT_UNDEFINED, NULL },
+ /* sentinel at end */
+ { NULL, NULL, 0, NULL }
+ };
+
+ cl_git_pass(git_attr_file__load_standalone(&file, cl_fixture("attr/attr3")));
+ cl_assert(file->rules.length == 3);
+
+ run_test_cases(file, cases, 0);
+
+ git_attr_file__free(file);
+}
+
+void test_attr_lookup__from_buffer(void)
+{
+ git_attr_file *file;
+ git_attr_file_source source = {0};
+
+ struct attr_expected cases[] = {
+ { "abc", "foo", EXPECT_TRUE, NULL },
+ { "abc", "bar", EXPECT_TRUE, NULL },
+ { "abc", "baz", EXPECT_TRUE, NULL },
+ { "aaa", "foo", EXPECT_TRUE, NULL },
+ { "aaa", "bar", EXPECT_UNDEFINED, NULL },
+ { "aaa", "baz", EXPECT_TRUE, NULL },
+ { "qqq", "foo", EXPECT_UNDEFINED, NULL },
+ { "qqq", "bar", EXPECT_UNDEFINED, NULL },
+ { "qqq", "baz", EXPECT_TRUE, NULL },
+ { NULL, NULL, 0, NULL }
+ };
+
+ cl_git_pass(git_attr_file__new(&file, NULL, &source));
+
+ cl_git_pass(git_attr_file__parse_buffer(NULL, file, "a* foo\nabc bar\n* baz", true));
+
+ cl_assert(file->rules.length == 3);
+
+ run_test_cases(file, cases, 0);
+
+ git_attr_file__free(file);
+}
diff --git a/tests/libgit2/attr/macro.c b/tests/libgit2/attr/macro.c
new file mode 100644
index 0000000..1fbfd13
--- /dev/null
+++ b/tests/libgit2/attr/macro.c
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+
+#include "clar_libgit2.h"
+
+#include "git2/sys/repository.h"
+#include "attr.h"
+
+static git_repository *g_repo = NULL;
+
+void test_attr_macro__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ g_repo = NULL;
+}
+
+void test_attr_macro__macros(void)
+{
+ const char *names[7] = { "rootattr", "binary", "diff", "crlf", "merge", "text", "frotz" };
+ const char *names2[5] = { "mymacro", "positive", "negative", "rootattr", "another" };
+ const char *names3[3] = { "macro2", "multi2", "multi3" };
+ const char *values[7];
+
+ g_repo = cl_git_sandbox_init("attr");
+
+ cl_git_pass(git_attr_get_many(values, g_repo, 0, "binfile", 7, names));
+
+ cl_assert(GIT_ATTR_IS_TRUE(values[0]));
+ cl_assert(GIT_ATTR_IS_TRUE(values[1]));
+ cl_assert(GIT_ATTR_IS_FALSE(values[2]));
+ cl_assert(GIT_ATTR_IS_FALSE(values[3]));
+ cl_assert(GIT_ATTR_IS_FALSE(values[4]));
+ cl_assert(GIT_ATTR_IS_FALSE(values[5]));
+ cl_assert(GIT_ATTR_IS_UNSPECIFIED(values[6]));
+
+ cl_git_pass(git_attr_get_many(values, g_repo, 0, "macro_test", 5, names2));
+
+ cl_assert(GIT_ATTR_IS_TRUE(values[0]));
+ cl_assert(GIT_ATTR_IS_TRUE(values[1]));
+ cl_assert(GIT_ATTR_IS_FALSE(values[2]));
+ cl_assert(GIT_ATTR_IS_UNSPECIFIED(values[3]));
+ cl_assert_equal_s("77", values[4]);
+
+ cl_git_pass(git_attr_get_many(values, g_repo, 0, "macro_test", 3, names3));
+
+ cl_assert(GIT_ATTR_IS_TRUE(values[0]));
+ cl_assert(GIT_ATTR_IS_FALSE(values[1]));
+ cl_assert_equal_s("answer", values[2]);
+}
+
+void test_attr_macro__bad_macros(void)
+{
+ const char *names[6] = { "rootattr", "positive", "negative",
+ "firstmacro", "secondmacro", "thirdmacro" };
+ const char *values[6];
+
+ g_repo = cl_git_sandbox_init("attr");
+
+ cl_git_pass(git_attr_get_many(values, g_repo, 0, "macro_bad", 6, names));
+
+ /* these three just confirm that the "mymacro" rule ran */
+ cl_assert(GIT_ATTR_IS_UNSPECIFIED(values[0]));
+ cl_assert(GIT_ATTR_IS_TRUE(values[1]));
+ cl_assert(GIT_ATTR_IS_FALSE(values[2]));
+
+ /* file contains:
+ * # let's try some malicious macro defs
+ * [attr]firstmacro -thirdmacro -secondmacro
+ * [attr]secondmacro firstmacro -firstmacro
+ * [attr]thirdmacro secondmacro=hahaha -firstmacro
+ * macro_bad firstmacro secondmacro thirdmacro
+ *
+ * firstmacro assignment list ends up with:
+ * -thirdmacro -secondmacro
+ * secondmacro assignment list expands "firstmacro" and ends up with:
+ * -thirdmacro -secondmacro -firstmacro
+ * thirdmacro assignment don't expand so list ends up with:
+ * secondmacro="hahaha"
+ *
+ * macro_bad assignment list ends up with:
+ * -thirdmacro -secondmacro firstmacro &&
+ * -thirdmacro -secondmacro -firstmacro secondmacro &&
+ * secondmacro="hahaha" thirdmacro
+ *
+ * so summary results should be:
+ * -firstmacro secondmacro="hahaha" thirdmacro
+ */
+ cl_assert(GIT_ATTR_IS_FALSE(values[3]));
+ cl_assert_equal_s("hahaha", values[4]);
+ cl_assert(GIT_ATTR_IS_TRUE(values[5]));
+}
+
+void test_attr_macro__macros_in_root_wd_apply(void)
+{
+ const char *value;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_pass(p_mkdir("empty_standard_repo/dir", 0777));
+ cl_git_rewritefile("empty_standard_repo/.gitattributes", "[attr]customattr key=value\n");
+ cl_git_rewritefile("empty_standard_repo/dir/.gitattributes", "file customattr\n");
+
+ cl_git_pass(git_attr_get(&value, g_repo, 0, "dir/file", "key"));
+ cl_assert_equal_s(value, "value");
+}
+
+void test_attr_macro__changing_macro_in_root_wd_updates_attributes(void)
+{
+ const char *value;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_rewritefile("empty_standard_repo/.gitattributes",
+ "[attr]customattr key=first\n"
+ "file customattr\n");
+ cl_git_pass(git_attr_get(&value, g_repo, 0, "file", "key"));
+ cl_assert_equal_s(value, "first");
+
+ cl_git_rewritefile("empty_standard_repo/.gitattributes",
+ "[attr]customattr key=second\n"
+ "file customattr\n");
+ cl_git_pass(git_attr_get(&value, g_repo, 0, "file", "key"));
+ cl_assert_equal_s(value, "second");
+}
+
+void test_attr_macro__macros_in_subdir_do_not_apply(void)
+{
+ const char *value;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_pass(p_mkdir("empty_standard_repo/dir", 0777));
+ cl_git_rewritefile("empty_standard_repo/dir/.gitattributes",
+ "[attr]customattr key=value\n"
+ "file customattr\n");
+
+ /* This should _not_ pass, as macros in subdirectories shall be ignored */
+ cl_git_pass(git_attr_get(&value, g_repo, 0, "dir/file", "key"));
+ cl_assert_equal_p(value, NULL);
+}
+
+void test_attr_macro__adding_macro_succeeds(void)
+{
+ const char *value;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+ cl_git_pass(git_attr_add_macro(g_repo, "macro", "key=value"));
+ cl_git_rewritefile("empty_standard_repo/.gitattributes", "file.txt macro\n");
+
+ cl_git_pass(git_attr_get(&value, g_repo, 0, "file.txt", "key"));
+ cl_assert_equal_s(value, "value");
+}
+
+void test_attr_macro__adding_boolean_macros_succeeds(void)
+{
+ const char *value;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+ cl_git_pass(git_attr_add_macro(g_repo, "macro-pos", "positive"));
+ cl_git_pass(git_attr_add_macro(g_repo, "macro-neg", "-negative"));
+ cl_git_rewritefile("empty_standard_repo/.gitattributes", "file.txt macro-pos macro-neg\n");
+
+ cl_git_pass(git_attr_get(&value, g_repo, 0, "file.txt", "positive"));
+ cl_assert(GIT_ATTR_IS_TRUE(value));
+ cl_git_pass(git_attr_get(&value, g_repo, 0, "file.txt", "negative"));
+ cl_assert(GIT_ATTR_IS_FALSE(value));
+}
+
+void test_attr_macro__redefining_macro_succeeds(void)
+{
+ const char *value;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+ cl_git_pass(git_attr_add_macro(g_repo, "macro", "key=value1"));
+ cl_git_pass(git_attr_add_macro(g_repo, "macro", "key=value2"));
+ cl_git_rewritefile("empty_standard_repo/.gitattributes", "file.txt macro");
+
+ cl_git_pass(git_attr_get(&value, g_repo, 0, "file.txt", "key"));
+ cl_assert_equal_s(value, "value2");
+}
+
+void test_attr_macro__recursive_macro_resolves(void)
+{
+ const char *value;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+ cl_git_pass(git_attr_add_macro(g_repo, "expandme", "key=value"));
+ cl_git_pass(git_attr_add_macro(g_repo, "macro", "expandme"));
+ cl_git_rewritefile("empty_standard_repo/.gitattributes", "file.txt macro");
+
+ cl_git_pass(git_attr_get(&value, g_repo, 0, "file.txt", "key"));
+ cl_assert_equal_s(value, "value");
+}
diff --git a/tests/libgit2/attr/repo.c b/tests/libgit2/attr/repo.c
new file mode 100644
index 0000000..abd2381
--- /dev/null
+++ b/tests/libgit2/attr/repo.c
@@ -0,0 +1,405 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+#include "git2/attr.h"
+#include "attr.h"
+#include "sysdir.h"
+
+#include "attr_expect.h"
+#include "git2/sys/repository.h"
+
+static git_repository *g_repo = NULL;
+
+void test_attr_repo__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("attr");
+}
+
+void test_attr_repo__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ g_repo = NULL;
+ cl_sandbox_set_search_path_defaults();
+}
+
+static struct attr_expected get_one_test_cases[] = {
+ { "root_test1", "repoattr", EXPECT_TRUE, NULL },
+ { "root_test1", "rootattr", EXPECT_TRUE, NULL },
+ { "root_test1", "missingattr", EXPECT_UNDEFINED, NULL },
+ { "root_test1", "subattr", EXPECT_UNDEFINED, NULL },
+ { "root_test1", "negattr", EXPECT_UNDEFINED, NULL },
+ { "root_test2", "repoattr", EXPECT_TRUE, NULL },
+ { "root_test2", "rootattr", EXPECT_FALSE, NULL },
+ { "root_test2", "missingattr", EXPECT_UNDEFINED, NULL },
+ { "root_test2", "multiattr", EXPECT_FALSE, NULL },
+ { "root_test3", "repoattr", EXPECT_TRUE, NULL },
+ { "root_test3", "rootattr", EXPECT_UNDEFINED, NULL },
+ { "root_test3", "multiattr", EXPECT_STRING, "3" },
+ { "root_test3", "multi2", EXPECT_UNDEFINED, NULL },
+ { "sub/subdir_test1", "repoattr", EXPECT_TRUE, NULL },
+ { "sub/subdir_test1", "rootattr", EXPECT_TRUE, NULL },
+ { "sub/subdir_test1", "missingattr", EXPECT_UNDEFINED, NULL },
+ { "sub/subdir_test1", "subattr", EXPECT_STRING, "yes" },
+ { "sub/subdir_test1", "negattr", EXPECT_FALSE, NULL },
+ { "sub/subdir_test1", "another", EXPECT_UNDEFINED, NULL },
+ { "sub/subdir_test2.txt", "repoattr", EXPECT_TRUE, NULL },
+ { "sub/subdir_test2.txt", "rootattr", EXPECT_TRUE, NULL },
+ { "sub/subdir_test2.txt", "missingattr", EXPECT_UNDEFINED, NULL },
+ { "sub/subdir_test2.txt", "subattr", EXPECT_STRING, "yes" },
+ { "sub/subdir_test2.txt", "negattr", EXPECT_FALSE, NULL },
+ { "sub/subdir_test2.txt", "another", EXPECT_STRING, "zero" },
+ { "sub/subdir_test2.txt", "reposub", EXPECT_TRUE, NULL },
+ { "sub/sub/subdir.txt", "another", EXPECT_STRING, "one" },
+ { "sub/sub/subdir.txt", "reposubsub", EXPECT_TRUE, NULL },
+ { "sub/sub/subdir.txt", "reposub", EXPECT_UNDEFINED, NULL },
+ { "does-not-exist", "foo", EXPECT_STRING, "yes" },
+ { "sub/deep/file", "deepdeep", EXPECT_TRUE, NULL },
+ { "sub/sub/d/no", "test", EXPECT_STRING, "a/b/d/*" },
+ { "sub/sub/d/yes", "test", EXPECT_UNDEFINED, NULL },
+};
+
+void test_attr_repo__get_one(void)
+{
+ int i;
+
+ for (i = 0; i < (int)ARRAY_SIZE(get_one_test_cases); ++i) {
+ struct attr_expected *scan = &get_one_test_cases[i];
+ const char *value;
+
+ cl_git_pass(git_attr_get(&value, g_repo, 0, scan->path, scan->attr));
+ attr_check_expected(
+ scan->expected, scan->expected_str, scan->attr, value);
+ }
+
+ cl_assert(git_attr_cache__is_cached(
+ g_repo, GIT_ATTR_FILE_SOURCE_FILE, ".git/info/attributes"));
+ cl_assert(git_attr_cache__is_cached(
+ g_repo, GIT_ATTR_FILE_SOURCE_FILE, ".gitattributes"));
+ cl_assert(git_attr_cache__is_cached(
+ g_repo, GIT_ATTR_FILE_SOURCE_FILE, "sub/.gitattributes"));
+}
+
+void test_attr_repo__get_one_start_deep(void)
+{
+ int i;
+
+ for (i = (int)ARRAY_SIZE(get_one_test_cases) - 1; i >= 0; --i) {
+ struct attr_expected *scan = &get_one_test_cases[i];
+ const char *value;
+
+ cl_git_pass(git_attr_get(&value, g_repo, 0, scan->path, scan->attr));
+ attr_check_expected(
+ scan->expected, scan->expected_str, scan->attr, value);
+ }
+
+ cl_assert(git_attr_cache__is_cached(
+ g_repo, GIT_ATTR_FILE_SOURCE_FILE, ".git/info/attributes"));
+ cl_assert(git_attr_cache__is_cached(
+ g_repo, GIT_ATTR_FILE_SOURCE_FILE, ".gitattributes"));
+ cl_assert(git_attr_cache__is_cached(
+ g_repo, GIT_ATTR_FILE_SOURCE_FILE, "sub/.gitattributes"));
+}
+
+void test_attr_repo__get_many(void)
+{
+ const char *names[4] = { "repoattr", "rootattr", "missingattr", "subattr" };
+ const char *values[4];
+
+ cl_git_pass(git_attr_get_many(values, g_repo, 0, "root_test1", 4, names));
+
+ cl_assert(GIT_ATTR_IS_TRUE(values[0]));
+ cl_assert(GIT_ATTR_IS_TRUE(values[1]));
+ cl_assert(GIT_ATTR_IS_UNSPECIFIED(values[2]));
+ cl_assert(GIT_ATTR_IS_UNSPECIFIED(values[3]));
+
+ cl_git_pass(git_attr_get_many(values, g_repo, 0, "root_test2", 4, names));
+
+ cl_assert(GIT_ATTR_IS_TRUE(values[0]));
+ cl_assert(GIT_ATTR_IS_FALSE(values[1]));
+ cl_assert(GIT_ATTR_IS_UNSPECIFIED(values[2]));
+ cl_assert(GIT_ATTR_IS_UNSPECIFIED(values[3]));
+
+ cl_git_pass(git_attr_get_many(values, g_repo, 0, "sub/subdir_test1", 4, names));
+
+ cl_assert(GIT_ATTR_IS_TRUE(values[0]));
+ cl_assert(GIT_ATTR_IS_TRUE(values[1]));
+ cl_assert(GIT_ATTR_IS_UNSPECIFIED(values[2]));
+ cl_assert_equal_s("yes", values[3]);
+}
+
+void test_attr_repo__get_many_in_place(void)
+{
+ const char *vals[4] = { "repoattr", "rootattr", "missingattr", "subattr" };
+
+ /* it should be legal to look up values into the same array that has
+ * the attribute names, overwriting each name as the value is found.
+ */
+
+ cl_git_pass(git_attr_get_many(vals, g_repo, 0, "sub/subdir_test1", 4, vals));
+
+ cl_assert(GIT_ATTR_IS_TRUE(vals[0]));
+ cl_assert(GIT_ATTR_IS_TRUE(vals[1]));
+ cl_assert(GIT_ATTR_IS_UNSPECIFIED(vals[2]));
+ cl_assert_equal_s("yes", vals[3]);
+}
+
+static int count_attrs(
+ const char *name,
+ const char *value,
+ void *payload)
+{
+ GIT_UNUSED(name);
+ GIT_UNUSED(value);
+
+ *((int *)payload) += 1;
+
+ return 0;
+}
+
+#define CANCEL_VALUE 12345
+
+static int cancel_iteration(
+ const char *name,
+ const char *value,
+ void *payload)
+{
+ GIT_UNUSED(name);
+ GIT_UNUSED(value);
+
+ *((int *)payload) -= 1;
+
+ if (*((int *)payload) < 0)
+ return CANCEL_VALUE;
+
+ return 0;
+}
+
+void test_attr_repo__foreach(void)
+{
+ int count;
+
+ count = 0;
+ cl_git_pass(git_attr_foreach(
+ g_repo, 0, "root_test1", &count_attrs, &count));
+ cl_assert(count == 2);
+
+ count = 0;
+ cl_git_pass(git_attr_foreach(g_repo, 0, "sub/subdir_test1",
+ &count_attrs, &count));
+ cl_assert(count == 4); /* repoattr, rootattr, subattr, negattr */
+
+ count = 0;
+ cl_git_pass(git_attr_foreach(g_repo, 0, "sub/subdir_test2.txt",
+ &count_attrs, &count));
+ cl_assert(count == 6); /* repoattr, rootattr, subattr, reposub, negattr, another */
+
+ count = 2;
+ cl_assert_equal_i(
+ CANCEL_VALUE, git_attr_foreach(
+ g_repo, 0, "sub/subdir_test1", &cancel_iteration, &count)
+ );
+}
+
+void test_attr_repo__manpage_example(void)
+{
+ const char *value;
+
+ cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "foo"));
+ cl_assert(GIT_ATTR_IS_TRUE(value));
+
+ cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "bar"));
+ cl_assert(GIT_ATTR_IS_UNSPECIFIED(value));
+
+ cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "baz"));
+ cl_assert(GIT_ATTR_IS_FALSE(value));
+
+ cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "merge"));
+ cl_assert_equal_s("filfre", value);
+
+ cl_git_pass(git_attr_get(&value, g_repo, 0, "sub/abc", "frotz"));
+ cl_assert(GIT_ATTR_IS_UNSPECIFIED(value));
+}
+
+#define CONTENT "I'm going to be dynamically processed\r\n" \
+ "And my line endings...\r\n" \
+ "...are going to be\n" \
+ "normalized!\r\n"
+
+#define GITATTR "* text=auto\n" \
+ "*.txt text\n" \
+ "*.data binary\n"
+
+static void add_to_workdir(const char *filename, const char *content)
+{
+ git_str buf = GIT_STR_INIT;
+
+ cl_git_pass(git_str_joinpath(&buf, "attr", filename));
+ cl_git_rewritefile(git_str_cstr(&buf), content);
+
+ git_str_dispose(&buf);
+}
+
+static void assert_proper_normalization(git_index *index, const char *filename, const char *expected_sha)
+{
+ size_t index_pos;
+ const git_index_entry *entry;
+
+ add_to_workdir(filename, CONTENT);
+ cl_git_pass(git_index_add_bypath(index, filename));
+
+ cl_assert(!git_index_find(&index_pos, index, filename));
+
+ entry = git_index_get_byindex(index, index_pos);
+ cl_assert_equal_i(0, git_oid_streq(&entry->id, expected_sha));
+}
+
+void test_attr_repo__staging_properly_normalizes_line_endings_according_to_gitattributes_directives(void)
+{
+ git_index* index;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ add_to_workdir(".gitattributes", GITATTR);
+
+ assert_proper_normalization(index, "text.txt", "22c74203bace3c2e950278c7ab08da0fca9f4e9b");
+ assert_proper_normalization(index, "huh.dunno", "22c74203bace3c2e950278c7ab08da0fca9f4e9b");
+ assert_proper_normalization(index, "binary.data", "66eeff1fcbacf589e6d70aa70edd3fce5be2b37c");
+
+ git_index_free(index);
+}
+
+void test_attr_repo__bare_repo_with_index(void)
+{
+ const char *names[4] = { "test1", "test2", "test3", "test4" };
+ const char *values[4];
+ git_index *index;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_mkfile(
+ "attr/.gitattributes",
+ "*.txt test1 test2=foobar -test3\n"
+ "trial.txt -test1 test2=barfoo !test3 test4\n");
+ cl_git_pass(git_index_add_bypath(index, ".gitattributes"));
+ git_index_free(index);
+
+ cl_must_pass(p_unlink("attr/.gitattributes"));
+ cl_assert(!git_fs_path_exists("attr/.gitattributes"));
+
+ cl_git_pass(git_repository_set_bare(g_repo));
+
+ cl_git_pass(git_attr_get_many(values, g_repo, 0, "file.txt", 4, names));
+
+ cl_assert(GIT_ATTR_IS_TRUE(values[0]));
+ cl_assert_equal_s("foobar", values[1]);
+ cl_assert(GIT_ATTR_IS_FALSE(values[2]));
+ cl_assert(GIT_ATTR_IS_UNSPECIFIED(values[3]));
+
+ cl_git_pass(git_attr_get_many(values, g_repo, 0, "trial.txt", 4, names));
+
+ cl_assert(GIT_ATTR_IS_FALSE(values[0]));
+ cl_assert_equal_s("barfoo", values[1]);
+ cl_assert(GIT_ATTR_IS_UNSPECIFIED(values[2]));
+ cl_assert(GIT_ATTR_IS_TRUE(values[3]));
+
+ cl_git_pass(git_attr_get_many(values, g_repo, 0, "sub/sub/subdir.txt", 4, names));
+
+ cl_assert(GIT_ATTR_IS_TRUE(values[0]));
+ cl_assert_equal_s("foobar", values[1]);
+ cl_assert(GIT_ATTR_IS_FALSE(values[2]));
+ cl_assert(GIT_ATTR_IS_UNSPECIFIED(values[3]));
+}
+
+void test_attr_repo__sysdir(void)
+{
+ git_str sysdir = GIT_STR_INIT;
+ const char *value;
+
+ cl_git_pass(p_mkdir("system", 0777));
+ cl_git_rewritefile("system/gitattributes", "file merge=foo");
+ cl_git_pass(git_str_joinpath(&sysdir, clar_sandbox_path(), "system"));
+ cl_git_pass(git_sysdir_set(GIT_SYSDIR_SYSTEM, sysdir.ptr));
+ g_repo = cl_git_sandbox_reopen();
+
+ cl_git_pass(git_attr_get(&value, g_repo, 0, "file", "merge"));
+ cl_assert_equal_s(value, "foo");
+
+ cl_git_pass(p_unlink("system/gitattributes"));
+ cl_git_pass(p_rmdir("system"));
+ git_str_dispose(&sysdir);
+}
+
+void test_attr_repo__sysdir_with_session(void)
+{
+ const char *values[2], *attrs[2] = { "foo", "bar" };
+ git_str sysdir = GIT_STR_INIT;
+ git_attr_session session;
+
+ cl_git_pass(p_mkdir("system", 0777));
+ cl_git_rewritefile("system/gitattributes", "file foo=1 bar=2");
+ cl_git_pass(git_str_joinpath(&sysdir, clar_sandbox_path(), "system"));
+ cl_git_pass(git_sysdir_set(GIT_SYSDIR_SYSTEM, sysdir.ptr));
+ g_repo = cl_git_sandbox_reopen();
+
+ cl_git_pass(git_attr_session__init(&session, g_repo));
+ cl_git_pass(git_attr_get_many_with_session(values, g_repo, &session, NULL, "file", ARRAY_SIZE(attrs), attrs));
+
+ cl_assert_equal_s(values[0], "1");
+ cl_assert_equal_s(values[1], "2");
+
+ cl_git_pass(p_unlink("system/gitattributes"));
+ cl_git_pass(p_rmdir("system"));
+ git_str_dispose(&sysdir);
+ git_attr_session__free(&session);
+}
+
+void test_attr_repo__rewrite(void)
+{
+ const char *value;
+
+ cl_git_rewritefile("attr/.gitattributes", "file.txt foo=first\n");
+ cl_git_pass(git_attr_get(&value, g_repo, 0, "file.txt", "foo"));
+ cl_assert_equal_s(value, "first");
+
+ cl_git_rewritefile("attr/.gitattributes", "file.txt foo=second\n");
+ cl_git_pass(git_attr_get(&value, g_repo, 0, "file.txt", "foo"));
+ cl_assert_equal_s(value, "second");
+
+ cl_git_rewritefile("attr/.gitattributes", "file.txt other=value\n");
+ cl_git_pass(git_attr_get(&value, g_repo, 0, "file.txt", "foo"));
+ cl_assert_equal_p(value, NULL);
+}
+
+void test_attr_repo__rewrite_sysdir(void)
+{
+ git_str sysdir = GIT_STR_INIT;
+ const char *value;
+
+ cl_git_pass(p_mkdir("system", 0777));
+ cl_git_pass(git_str_joinpath(&sysdir, clar_sandbox_path(), "system"));
+ cl_git_pass(git_sysdir_set(GIT_SYSDIR_SYSTEM, sysdir.ptr));
+ g_repo = cl_git_sandbox_reopen();
+
+ cl_git_rewritefile("system/gitattributes", "file foo=first");
+ cl_git_pass(git_attr_get(&value, g_repo, 0, "file", "foo"));
+ cl_assert_equal_s(value, "first");
+
+ cl_git_rewritefile("system/gitattributes", "file foo=second");
+ cl_git_pass(git_attr_get(&value, g_repo, 0, "file", "foo"));
+ cl_assert_equal_s(value, "second");
+
+ git_str_dispose(&sysdir);
+}
+
+void test_attr_repo__unlink(void)
+{
+ const char *value;
+
+ cl_git_rewritefile("attr/.gitattributes", "file.txt foo=value1\n");
+ cl_git_pass(git_attr_get(&value, g_repo, 0, "file.txt", "foo"));
+ cl_assert_equal_s(value, "value1");
+
+ cl_git_pass(p_unlink("attr/.gitattributes"));
+
+ cl_git_pass(git_attr_get(&value, g_repo, 0, "file.txt", "foo"));
+ cl_assert_equal_p(value, NULL);
+}
diff --git a/tests/libgit2/blame/blame_helpers.c b/tests/libgit2/blame/blame_helpers.c
new file mode 100644
index 0000000..8aeaa58
--- /dev/null
+++ b/tests/libgit2/blame/blame_helpers.c
@@ -0,0 +1,67 @@
+#include "blame_helpers.h"
+
+void hunk_message(size_t idx, const git_blame_hunk *hunk, const char *fmt, ...)
+{
+ va_list arglist;
+
+ printf("Hunk %"PRIuZ" (line %"PRIuZ" +%"PRIuZ"): ", idx,
+ hunk->final_start_line_number, hunk->lines_in_hunk-1);
+
+ va_start(arglist, fmt);
+ vprintf(fmt, arglist);
+ va_end(arglist);
+
+ printf("\n");
+}
+
+void check_blame_hunk_index(git_repository *repo, git_blame *blame, int idx,
+ size_t start_line, size_t len, char boundary, const char *commit_id, const char *orig_path)
+{
+ char expected[GIT_OID_SHA1_HEXSIZE+1] = {0}, actual[GIT_OID_SHA1_HEXSIZE+1] = {0};
+ const git_blame_hunk *hunk = git_blame_get_hunk_byindex(blame, idx);
+ cl_assert(hunk);
+
+ if (!strncmp(commit_id, "0000", 4)) {
+ strcpy(expected, "0000000000000000000000000000000000000000");
+ } else {
+ git_object *obj;
+ cl_git_pass(git_revparse_single(&obj, repo, commit_id));
+ git_oid_fmt(expected, git_object_id(obj));
+ git_object_free(obj);
+ }
+
+ if (hunk->final_start_line_number != start_line) {
+ hunk_message(idx, hunk, "mismatched start line number: expected %"PRIuZ", got %"PRIuZ,
+ start_line, hunk->final_start_line_number);
+ }
+ cl_assert_equal_i(hunk->final_start_line_number, start_line);
+
+ if (hunk->lines_in_hunk != len) {
+ hunk_message(idx, hunk, "mismatched line count: expected %"PRIuZ", got %"PRIuZ,
+ len, hunk->lines_in_hunk);
+ }
+ cl_assert_equal_i(hunk->lines_in_hunk, len);
+
+ git_oid_fmt(actual, &hunk->final_commit_id);
+ if (strcmp(expected, actual)) {
+ hunk_message(idx, hunk, "has mismatched original id (got %s, expected %s)\n",
+ actual, expected);
+ }
+ cl_assert_equal_s(actual, expected);
+ cl_assert_equal_oid(&hunk->final_commit_id, &hunk->orig_commit_id);
+
+
+ if (strcmp(hunk->orig_path, orig_path)) {
+ hunk_message(idx, hunk, "has mismatched original path (got '%s', expected '%s')\n",
+ hunk->orig_path, orig_path);
+ }
+ cl_assert_equal_s(hunk->orig_path, orig_path);
+
+ if (hunk->boundary != boundary) {
+ hunk_message(idx, hunk, "doesn't match boundary flag (got %d, expected %d)\n",
+ hunk->boundary, boundary);
+ }
+ cl_assert_equal_i(boundary, hunk->boundary);
+}
+
+
diff --git a/tests/libgit2/blame/blame_helpers.h b/tests/libgit2/blame/blame_helpers.h
new file mode 100644
index 0000000..5b34b4a
--- /dev/null
+++ b/tests/libgit2/blame/blame_helpers.h
@@ -0,0 +1,14 @@
+#include "clar_libgit2.h"
+#include "blame.h"
+
+void hunk_message(size_t idx, const git_blame_hunk *hunk, const char *fmt, ...) GIT_FORMAT_PRINTF(3, 4);
+
+void check_blame_hunk_index(
+ git_repository *repo,
+ git_blame *blame,
+ int idx,
+ size_t start_line,
+ size_t len,
+ char boundary,
+ const char *commit_id,
+ const char *orig_path);
diff --git a/tests/libgit2/blame/buffer.c b/tests/libgit2/blame/buffer.c
new file mode 100644
index 0000000..06d5042
--- /dev/null
+++ b/tests/libgit2/blame/buffer.c
@@ -0,0 +1,192 @@
+#include "blame_helpers.h"
+
+static git_repository *g_repo;
+static git_blame *g_fileblame, *g_bufferblame;
+
+void test_blame_buffer__initialize(void)
+{
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git")));
+ cl_git_pass(git_blame_file(&g_fileblame, g_repo, "b.txt", NULL));
+ g_bufferblame = NULL;
+}
+
+void test_blame_buffer__cleanup(void)
+{
+ git_blame_free(g_fileblame);
+ git_blame_free(g_bufferblame);
+ git_repository_free(g_repo);
+}
+
+void test_blame_buffer__index(void)
+{
+ const git_blame_hunk *hunk;
+ const char *buffer = "Hello\nWorld!";
+
+ /*
+ * We need to open a different file from the ones used in other tests. Close
+ * the one opened in test_blame_buffer__initialize() to avoid a leak.
+ */
+ git_blame_free(g_fileblame);
+ g_fileblame = NULL;
+ cl_git_pass(git_blame_file(&g_fileblame, g_repo, "file.txt", NULL));
+
+ cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
+ cl_assert_equal_i(2, git_blame_get_hunk_count(g_bufferblame));
+
+ check_blame_hunk_index(g_repo, g_bufferblame, 0, 1, 1, 0, "836bc00b", "file.txt");
+ hunk = git_blame_get_hunk_byline(g_bufferblame, 1);
+ cl_assert(hunk);
+ cl_assert_equal_s("lhchavez", hunk->final_signature->name);
+ check_blame_hunk_index(g_repo, g_bufferblame, 1, 2, 1, 0, "00000000", "file.txt");
+ hunk = git_blame_get_hunk_byline(g_bufferblame, 2);
+ cl_assert(hunk);
+ cl_assert(hunk->final_signature == NULL);
+}
+
+void test_blame_buffer__added_line(void)
+{
+ const git_blame_hunk *hunk;
+
+ const char *buffer = "\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+\n\
+abcdefg\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\n";
+
+ cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
+ cl_assert_equal_i(5, git_blame_get_hunk_count(g_bufferblame));
+ check_blame_hunk_index(g_repo, g_bufferblame, 2, 6, 1, 0, "000000", "b.txt");
+
+ hunk = git_blame_get_hunk_byline(g_bufferblame, 16);
+ cl_assert(hunk);
+ cl_assert_equal_s("Ben Straub", hunk->final_signature->name);
+}
+
+void test_blame_buffer__deleted_line(void)
+{
+ const char *buffer = "\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\n";
+
+ cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
+ check_blame_hunk_index(g_repo, g_bufferblame, 2, 6, 3, 0, "63d671eb", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 3, 9, 1, 0, "63d671eb", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 4, 10, 5, 0, "aa06ecca", "b.txt");
+}
+
+void test_blame_buffer__add_splits_hunk(void)
+{
+ const char *buffer = "\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+abc\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\n";
+
+ cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
+ check_blame_hunk_index(g_repo, g_bufferblame, 2, 6, 2, 0, "63d671eb", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 3, 8, 1, 0, "00000000", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 4, 9, 3, 0, "63d671eb", "b.txt");
+}
+
+void test_blame_buffer__delete_crosses_hunk_boundary(void)
+{
+ const char *buffer = "\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\n";
+
+ cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
+ check_blame_hunk_index(g_repo, g_bufferblame, 2, 6, 1, 0, "63d671eb", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 3, 7, 2, 0, "aa06ecca", "b.txt");
+}
+
+void test_blame_buffer__replace_line(void)
+{
+ const char *buffer = "\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+abc\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\n";
+
+ cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
+ check_blame_hunk_index(g_repo, g_bufferblame, 2, 6, 1, 0, "63d671eb", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 3, 7, 1, 0, "00000000", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 4, 8, 3, 0, "63d671eb", "b.txt");
+}
+
+void test_blame_buffer__add_lines_at_end(void)
+{
+ const char *buffer = "\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\
+\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\
+\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\
+\n\
+abc\n\
+def\n";
+ cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer)));
+
+ cl_assert_equal_i(5, git_blame_get_hunk_count(g_bufferblame));
+ check_blame_hunk_index(g_repo, g_bufferblame, 0, 1, 4, 0, "da237394", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 1, 5, 1, 1, "b99f7ac0", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 2, 6, 5, 0, "63d671eb", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 3, 11, 5, 0, "aa06ecca", "b.txt");
+ check_blame_hunk_index(g_repo, g_bufferblame, 4, 16, 2, 0, "00000000", "b.txt");
+}
diff --git a/tests/libgit2/blame/getters.c b/tests/libgit2/blame/getters.c
new file mode 100644
index 0000000..c160cd3
--- /dev/null
+++ b/tests/libgit2/blame/getters.c
@@ -0,0 +1,56 @@
+#include "clar_libgit2.h"
+
+#include "blame.h"
+
+git_blame *g_blame;
+
+void test_blame_getters__initialize(void)
+{
+ size_t i;
+ git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+
+ git_blame_hunk hunks[] = {
+ { 3, GIT_OID_SHA1_ZERO, 1, NULL, GIT_OID_SHA1_ZERO, "a", 0},
+ { 3, GIT_OID_SHA1_ZERO, 4, NULL, GIT_OID_SHA1_ZERO, "b", 0},
+ { 3, GIT_OID_SHA1_ZERO, 7, NULL, GIT_OID_SHA1_ZERO, "c", 0},
+ { 3, GIT_OID_SHA1_ZERO, 10, NULL, GIT_OID_SHA1_ZERO, "d", 0},
+ { 3, GIT_OID_SHA1_ZERO, 13, NULL, GIT_OID_SHA1_ZERO, "e", 0},
+ };
+
+ g_blame = git_blame__alloc(NULL, opts, "");
+
+ for (i=0; i<5; i++) {
+ git_blame_hunk *h = git__calloc(1, sizeof(git_blame_hunk));
+ h->final_start_line_number = hunks[i].final_start_line_number;
+ h->orig_path = git__strdup(hunks[i].orig_path);
+ h->lines_in_hunk = hunks[i].lines_in_hunk;
+
+ git_vector_insert(&g_blame->hunks, h);
+ }
+}
+
+void test_blame_getters__cleanup(void)
+{
+ git_blame_free(g_blame);
+}
+
+
+void test_blame_getters__byindex(void)
+{
+ const git_blame_hunk *h = git_blame_get_hunk_byindex(g_blame, 2);
+ cl_assert(h);
+ cl_assert_equal_s(h->orig_path, "c");
+
+ h = git_blame_get_hunk_byindex(g_blame, 95);
+ cl_assert_equal_p(h, NULL);
+}
+
+void test_blame_getters__byline(void)
+{
+ const git_blame_hunk *h = git_blame_get_hunk_byline(g_blame, 5);
+ cl_assert(h);
+ cl_assert_equal_s(h->orig_path, "b");
+
+ h = git_blame_get_hunk_byline(g_blame, 95);
+ cl_assert_equal_p(h, NULL);
+}
diff --git a/tests/libgit2/blame/harder.c b/tests/libgit2/blame/harder.c
new file mode 100644
index 0000000..e777417
--- /dev/null
+++ b/tests/libgit2/blame/harder.c
@@ -0,0 +1,79 @@
+#include "clar_libgit2.h"
+
+#include "blame.h"
+
+
+/**
+ * The test repo has a history that looks like this:
+ *
+ * * (A) bc7c5ac
+ * |\
+ * | * (B) aa06ecc
+ * * | (C) 63d671e
+ * |/
+ * * (D) da23739
+ * * (E) b99f7ac
+ *
+ */
+
+static git_repository *g_repo = NULL;
+
+void test_blame_harder__initialize(void)
+{
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git")));
+}
+
+void test_blame_harder__cleanup(void)
+{
+ git_repository_free(g_repo);
+ g_repo = NULL;
+}
+
+
+
+void test_blame_harder__m(void)
+{
+ /* TODO */
+ git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+
+ GIT_UNUSED(opts);
+
+ opts.flags = GIT_BLAME_TRACK_COPIES_SAME_FILE;
+}
+
+
+void test_blame_harder__c(void)
+{
+ git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+
+ GIT_UNUSED(opts);
+
+ /* Attribute the first hunk in b.txt to (E), since it was cut/pasted from
+ * a.txt in (D).
+ */
+ opts.flags = GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES;
+}
+
+void test_blame_harder__cc(void)
+{
+ git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+
+ GIT_UNUSED(opts);
+
+ /* Attribute the second hunk in b.txt to (E), since it was copy/pasted from
+ * a.txt in (C).
+ */
+ opts.flags = GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES;
+}
+
+void test_blame_harder__ccc(void)
+{
+ git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+
+ GIT_UNUSED(opts);
+
+ /* Attribute the third hunk in b.txt to (E). This hunk was deleted from
+ * a.txt in (D), but reintroduced in (B).
+ */
+ opts.flags = GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES;
+}
diff --git a/tests/libgit2/blame/simple.c b/tests/libgit2/blame/simple.c
new file mode 100644
index 0000000..6b13ccc
--- /dev/null
+++ b/tests/libgit2/blame/simple.c
@@ -0,0 +1,362 @@
+#include "blame_helpers.h"
+
+static git_repository *g_repo;
+static git_blame *g_blame;
+
+void test_blame_simple__initialize(void)
+{
+ g_repo = NULL;
+ g_blame = NULL;
+}
+
+void test_blame_simple__cleanup(void)
+{
+ git_blame_free(g_blame);
+ git_repository_free(g_repo);
+}
+
+/*
+ * $ git blame -s branch_file.txt
+ * orig line no final line no
+ * commit V author timestamp V
+ * c47800c7 1 (Scott Chacon 2010-05-25 11:58:14 -0700 1
+ * a65fedf3 2 (Scott Chacon 2011-08-09 19:33:46 -0700 2
+ */
+void test_blame_simple__trivial_testrepo(void)
+{
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo/.gitted")));
+ cl_git_pass(git_blame_file(&g_blame, g_repo, "branch_file.txt", NULL));
+
+ cl_assert_equal_i(2, git_blame_get_hunk_count(g_blame));
+ check_blame_hunk_index(g_repo, g_blame, 0, 1, 1, 0, "c47800c7", "branch_file.txt");
+ check_blame_hunk_index(g_repo, g_blame, 1, 2, 1, 0, "a65fedf3", "branch_file.txt");
+}
+
+/*
+ * $ git blame -n b.txt
+ * orig line no final line no
+ * commit V author timestamp V
+ * da237394 1 (Ben Straub 2013-02-12 15:11:30 -0800 1
+ * da237394 2 (Ben Straub 2013-02-12 15:11:30 -0800 2
+ * da237394 3 (Ben Straub 2013-02-12 15:11:30 -0800 3
+ * da237394 4 (Ben Straub 2013-02-12 15:11:30 -0800 4
+ * ^b99f7ac 1 (Ben Straub 2013-02-12 15:10:12 -0800 5
+ * 63d671eb 6 (Ben Straub 2013-02-12 15:13:04 -0800 6
+ * 63d671eb 7 (Ben Straub 2013-02-12 15:13:04 -0800 7
+ * 63d671eb 8 (Ben Straub 2013-02-12 15:13:04 -0800 8
+ * 63d671eb 9 (Ben Straub 2013-02-12 15:13:04 -0800 9
+ * 63d671eb 10 (Ben Straub 2013-02-12 15:13:04 -0800 10
+ * aa06ecca 6 (Ben Straub 2013-02-12 15:14:46 -0800 11
+ * aa06ecca 7 (Ben Straub 2013-02-12 15:14:46 -0800 12
+ * aa06ecca 8 (Ben Straub 2013-02-12 15:14:46 -0800 13
+ * aa06ecca 9 (Ben Straub 2013-02-12 15:14:46 -0800 14
+ * aa06ecca 10 (Ben Straub 2013-02-12 15:14:46 -0800 15
+ */
+void test_blame_simple__trivial_blamerepo(void)
+{
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git")));
+ cl_git_pass(git_blame_file(&g_blame, g_repo, "b.txt", NULL));
+
+ cl_assert_equal_i(4, git_blame_get_hunk_count(g_blame));
+ check_blame_hunk_index(g_repo, g_blame, 0, 1, 4, 0, "da237394", "b.txt");
+ check_blame_hunk_index(g_repo, g_blame, 1, 5, 1, 1, "b99f7ac0", "b.txt");
+ check_blame_hunk_index(g_repo, g_blame, 2, 6, 5, 0, "63d671eb", "b.txt");
+ check_blame_hunk_index(g_repo, g_blame, 3, 11, 5, 0, "aa06ecca", "b.txt");
+}
+
+
+/*
+ * $ git blame -n 359fc2d -- include/git2.h
+ * orig line no final line no
+ * commit orig path V author timestamp V
+ * d12299fe src/git.h 1 (Vicent Martí 2010-12-03 22:22:10 +0200 1
+ * 359fc2d2 include/git2.h 2 (Edward Thomson 2013-01-08 17:07:25 -0600 2
+ * d12299fe src/git.h 5 (Vicent Martí 2010-12-03 22:22:10 +0200 3
+ * bb742ede include/git2.h 4 (Vicent Martí 2011-09-19 01:54:32 +0300 4
+ * bb742ede include/git2.h 5 (Vicent Martí 2011-09-19 01:54:32 +0300 5
+ * d12299fe src/git.h 24 (Vicent Martí 2010-12-03 22:22:10 +0200 6
+ * d12299fe src/git.h 25 (Vicent Martí 2010-12-03 22:22:10 +0200 7
+ * d12299fe src/git.h 26 (Vicent Martí 2010-12-03 22:22:10 +0200 8
+ * d12299fe src/git.h 27 (Vicent Martí 2010-12-03 22:22:10 +0200 9
+ * d12299fe src/git.h 28 (Vicent Martí 2010-12-03 22:22:10 +0200 10
+ * 96fab093 include/git2.h 11 (Sven Strickroth 2011-10-09 18:37:41 +0200 11
+ * 9d1dcca2 src/git2.h 33 (Vicent Martí 2011-02-07 10:35:58 +0200 12
+ * 44908fe7 src/git2.h 29 (Vicent Martí 2010-12-06 23:03:16 +0200 13
+ * a15c550d include/git2.h 14 (Vicent Martí 2011-11-16 14:09:44 +0100 14
+ * 44908fe7 src/git2.h 30 (Vicent Martí 2010-12-06 23:03:16 +0200 15
+ * d12299fe src/git.h 32 (Vicent Martí 2010-12-03 22:22:10 +0200 16
+ * 44908fe7 src/git2.h 33 (Vicent Martí 2010-12-06 23:03:16 +0200 17
+ * d12299fe src/git.h 34 (Vicent Martí 2010-12-03 22:22:10 +0200 18
+ * 44908fe7 src/git2.h 35 (Vicent Martí 2010-12-06 23:03:16 +0200 19
+ * 638c2ca4 src/git2.h 36 (Vicent Martí 2010-12-18 02:10:25 +0200 20
+ * 44908fe7 src/git2.h 36 (Vicent Martí 2010-12-06 23:03:16 +0200 21
+ * d12299fe src/git.h 37 (Vicent Martí 2010-12-03 22:22:10 +0200 22
+ * 44908fe7 src/git2.h 38 (Vicent Martí 2010-12-06 23:03:16 +0200 23
+ * 44908fe7 src/git2.h 39 (Vicent Martí 2010-12-06 23:03:16 +0200 24
+ * bf787bd8 include/git2.h 25 (Carlos Martín Nieto 2012-04-08 18:56:50 +0200 25
+ * 0984c876 include/git2.h 26 (Scott J. Goldman 2012-11-28 18:27:43 -0800 26
+ * 2f8a8ab2 src/git2.h 41 (Vicent Martí 2011-01-29 01:56:25 +0200 27
+ * 27df4275 include/git2.h 47 (Michael Schubert 2011-06-28 14:13:12 +0200 28
+ * a346992f include/git2.h 28 (Ben Straub 2012-05-10 09:47:14 -0700 29
+ * d12299fe src/git.h 40 (Vicent Martí 2010-12-03 22:22:10 +0200 30
+ * 44908fe7 src/git2.h 41 (Vicent Martí 2010-12-06 23:03:16 +0200 31
+ * 44908fe7 src/git2.h 42 (Vicent Martí 2010-12-06 23:03:16 +0200 32
+ * 44908fe7 src/git2.h 43 (Vicent Martí 2010-12-06 23:03:16 +0200 33
+ * 44908fe7 src/git2.h 44 (Vicent Martí 2010-12-06 23:03:16 +0200 34
+ * 44908fe7 src/git2.h 45 (Vicent Martí 2010-12-06 23:03:16 +0200 35
+ * 65b09b1d include/git2.h 33 (Russell Belfer 2012-02-02 18:03:43 -0800 36
+ * d12299fe src/git.h 46 (Vicent Martí 2010-12-03 22:22:10 +0200 37
+ * 44908fe7 src/git2.h 47 (Vicent Martí 2010-12-06 23:03:16 +0200 38
+ * 5d4cd003 include/git2.h 55 (Carlos Martín Nieto 2011-03-28 17:02:45 +0200 39
+ * 41fb1ca0 include/git2.h 39 (Philip Kelley 2012-10-29 13:41:14 -0400 40
+ * 2dc31040 include/git2.h 56 (Carlos Martín Nieto 2011-06-20 18:58:57 +0200 41
+ * 764df57e include/git2.h 40 (Ben Straub 2012-06-15 13:14:43 -0700 42
+ * 5280f4e6 include/git2.h 41 (Ben Straub 2012-07-31 19:39:06 -0700 43
+ * 613d5eb9 include/git2.h 43 (Philip Kelley 2012-11-28 11:42:37 -0500 44
+ * d12299fe src/git.h 48 (Vicent Martí 2010-12-03 22:22:10 +0200 45
+ * 111ee3fe include/git2.h 41 (Vicent Martí 2012-07-11 14:37:26 +0200 46
+ * f004c4a8 include/git2.h 44 (Russell Belfer 2012-08-21 17:26:39 -0700 47
+ * 111ee3fe include/git2.h 42 (Vicent Martí 2012-07-11 14:37:26 +0200 48
+ * 9c82357b include/git2.h 58 (Carlos Martín Nieto 2011-06-17 18:13:14 +0200 49
+ * d6258deb include/git2.h 61 (Carlos Martín Nieto 2011-06-25 15:10:09 +0200 50
+ * b311e313 include/git2.h 63 (Julien Miotte 2011-07-27 18:31:13 +0200 51
+ * 3412391d include/git2.h 63 (Carlos Martín Nieto 2011-07-07 11:47:31 +0200 52
+ * bfc9ca59 include/git2.h 43 (Russell Belfer 2012-03-28 16:45:36 -0700 53
+ * bf477ed4 include/git2.h 44 (Michael Schubert 2012-02-15 00:33:38 +0100 54
+ * edebceff include/git2.h 46 (nulltoken 2012-05-01 13:57:45 +0200 55
+ * 743a4b3b include/git2.h 48 (nulltoken 2012-06-15 22:24:59 +0200 56
+ * 0a32dca5 include/git2.h 54 (Michael Schubert 2012-08-19 22:26:32 +0200 57
+ * 590fb68b include/git2.h 55 (nulltoken 2012-10-04 13:47:45 +0200 58
+ * bf477ed4 include/git2.h 45 (Michael Schubert 2012-02-15 00:33:38 +0100 59
+ * d12299fe src/git.h 49 (Vicent Martí 2010-12-03 22:22:10 +0200 60
+ */
+void test_blame_simple__trivial_libgit2(void)
+{
+ git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+ git_object *obj;
+
+ /* If we can't open the libgit2 repo or if it isn't a full repo
+ * with proper history, just skip this test */
+ if (git_repository_open(&g_repo, cl_fixture("../..")) < 0)
+ cl_skip();
+
+ if (git_repository_is_shallow(g_repo))
+ cl_skip();
+
+ if (git_revparse_single(&obj, g_repo, "359fc2d") < 0)
+ cl_skip();
+
+ git_oid_cpy(&opts.newest_commit, git_object_id(obj));
+ git_object_free(obj);
+
+ cl_git_pass(git_blame_file(&g_blame, g_repo, "include/git2.h", &opts));
+
+ check_blame_hunk_index(g_repo, g_blame, 0, 1, 1, 0, "d12299fe", "src/git.h");
+ check_blame_hunk_index(g_repo, g_blame, 1, 2, 1, 0, "359fc2d2", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 2, 3, 1, 0, "d12299fe", "src/git.h");
+ check_blame_hunk_index(g_repo, g_blame, 3, 4, 2, 0, "bb742ede", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 4, 6, 5, 0, "d12299fe", "src/git.h");
+ check_blame_hunk_index(g_repo, g_blame, 5, 11, 1, 0, "96fab093", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 6, 12, 1, 0, "9d1dcca2", "src/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 7, 13, 1, 0, "44908fe7", "src/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 8, 14, 1, 0, "a15c550d", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 9, 15, 1, 0, "44908fe7", "src/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 10, 16, 1, 0, "d12299fe", "src/git.h");
+ check_blame_hunk_index(g_repo, g_blame, 11, 17, 1, 0, "44908fe7", "src/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 12, 18, 1, 0, "d12299fe", "src/git.h");
+ check_blame_hunk_index(g_repo, g_blame, 13, 19, 1, 0, "44908fe7", "src/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 14, 20, 1, 0, "638c2ca4", "src/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 15, 21, 1, 0, "44908fe7", "src/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 16, 22, 1, 0, "d12299fe", "src/git.h");
+ check_blame_hunk_index(g_repo, g_blame, 17, 23, 2, 0, "44908fe7", "src/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 18, 25, 1, 0, "bf787bd8", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 19, 26, 1, 0, "0984c876", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 20, 27, 1, 0, "2f8a8ab2", "src/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 21, 28, 1, 0, "27df4275", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 22, 29, 1, 0, "a346992f", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 23, 30, 1, 0, "d12299fe", "src/git.h");
+ check_blame_hunk_index(g_repo, g_blame, 24, 31, 5, 0, "44908fe7", "src/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 25, 36, 1, 0, "65b09b1d", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 26, 37, 1, 0, "d12299fe", "src/git.h");
+ check_blame_hunk_index(g_repo, g_blame, 27, 38, 1, 0, "44908fe7", "src/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 28, 39, 1, 0, "5d4cd003", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 29, 40, 1, 0, "41fb1ca0", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 30, 41, 1, 0, "2dc31040", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 31, 42, 1, 0, "764df57e", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 32, 43, 1, 0, "5280f4e6", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 33, 44, 1, 0, "613d5eb9", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 34, 45, 1, 0, "d12299fe", "src/git.h");
+ check_blame_hunk_index(g_repo, g_blame, 35, 46, 1, 0, "111ee3fe", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 36, 47, 1, 0, "f004c4a8", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 37, 48, 1, 0, "111ee3fe", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 38, 49, 1, 0, "9c82357b", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 39, 50, 1, 0, "d6258deb", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 40, 51, 1, 0, "b311e313", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 41, 52, 1, 0, "3412391d", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 42, 53, 1, 0, "bfc9ca59", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 43, 54, 1, 0, "bf477ed4", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 44, 55, 1, 0, "edebceff", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 45, 56, 1, 0, "743a4b3b", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 46, 57, 1, 0, "0a32dca5", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 47, 58, 1, 0, "590fb68b", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 48, 59, 1, 0, "bf477ed4", "include/git2.h");
+ check_blame_hunk_index(g_repo, g_blame, 49, 60, 1, 0, "d12299fe", "src/git.h");
+}
+
+/* This was leading to segfaults on some systems during cache eviction. */
+void test_blame_simple__trivial_libgit2_under_cache_pressure(void)
+{
+ ssize_t old_max_storage = git_cache__max_storage;
+ git_cache__max_storage = 1024 * 1024;
+ test_blame_simple__trivial_libgit2();
+ git_cache__max_storage = old_max_storage;
+}
+
+/*
+ * $ git blame -n b.txt -L 8
+ * orig line no final line no
+ * commit V author timestamp V
+ * 63d671eb 8 (Ben Straub 2013-02-12 15:13:04 -0800 8
+ * 63d671eb 9 (Ben Straub 2013-02-12 15:13:04 -0800 9
+ * 63d671eb 10 (Ben Straub 2013-02-12 15:13:04 -0800 10
+ * aa06ecca 6 (Ben Straub 2013-02-12 15:14:46 -0800 11
+ * aa06ecca 7 (Ben Straub 2013-02-12 15:14:46 -0800 12
+ * aa06ecca 8 (Ben Straub 2013-02-12 15:14:46 -0800 13
+ * aa06ecca 9 (Ben Straub 2013-02-12 15:14:46 -0800 14
+ * aa06ecca 10 (Ben Straub 2013-02-12 15:14:46 -0800 15
+ */
+void test_blame_simple__can_restrict_lines_min(void)
+{
+ git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git")));
+
+ opts.min_line = 8;
+ cl_git_pass(git_blame_file(&g_blame, g_repo, "b.txt", &opts));
+ cl_assert_equal_i(2, git_blame_get_hunk_count(g_blame));
+ check_blame_hunk_index(g_repo, g_blame, 0, 8, 3, 0, "63d671eb", "b.txt");
+ check_blame_hunk_index(g_repo, g_blame, 1, 11, 5, 0, "aa06ecca", "b.txt");
+}
+
+/*
+ * $ git blame -n c.txt
+ * orig line no final line no
+ * commit V author timestamp V
+ * 702c7aa5 1 (Carl Schwan 2020-01-29 01:52:31 +0100 4
+ */
+void test_blame_simple__can_ignore_whitespace_change(void)
+{
+ git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git")));
+
+ opts.flags |= GIT_BLAME_IGNORE_WHITESPACE;
+ cl_git_pass(git_blame_file(&g_blame, g_repo, "c.txt", &opts));
+ cl_assert_equal_i(1, git_blame_get_hunk_count(g_blame));
+ check_blame_hunk_index(g_repo, g_blame, 0, 1, 4, 0, "702c7aa5", "c.txt");
+}
+
+/*
+ * $ git blame -n b.txt -L ,6
+ * orig line no final line no
+ * commit V author timestamp V
+ * da237394 1 (Ben Straub 2013-02-12 15:11:30 -0800 1
+ * da237394 2 (Ben Straub 2013-02-12 15:11:30 -0800 2
+ * da237394 3 (Ben Straub 2013-02-12 15:11:30 -0800 3
+ * da237394 4 (Ben Straub 2013-02-12 15:11:30 -0800 4
+ * ^b99f7ac 1 (Ben Straub 2013-02-12 15:10:12 -0800 5
+ * 63d671eb 6 (Ben Straub 2013-02-12 15:13:04 -0800 6
+ */
+void test_blame_simple__can_restrict_lines_max(void)
+{
+ git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git")));
+
+ opts.max_line = 6;
+ cl_git_pass(git_blame_file(&g_blame, g_repo, "b.txt", &opts));
+ cl_assert_equal_i(3, git_blame_get_hunk_count(g_blame));
+ check_blame_hunk_index(g_repo, g_blame, 0, 1, 4, 0, "da237394", "b.txt");
+ check_blame_hunk_index(g_repo, g_blame, 1, 5, 1, 1, "b99f7ac0", "b.txt");
+ check_blame_hunk_index(g_repo, g_blame, 2, 6, 1, 0, "63d671eb", "b.txt");
+}
+
+/*
+ * $ git blame -n b.txt -L 2,7
+ * orig line no final line no
+ * commit V author timestamp V
+ * da237394 2 (Ben Straub 2013-02-12 15:11:30 -0800 2
+ * da237394 3 (Ben Straub 2013-02-12 15:11:30 -0800 3
+ * da237394 4 (Ben Straub 2013-02-12 15:11:30 -0800 4
+ * ^b99f7ac 1 (Ben Straub 2013-02-12 15:10:12 -0800 5
+ * 63d671eb 6 (Ben Straub 2013-02-12 15:13:04 -0800 6
+ * 63d671eb 7 (Ben Straub 2013-02-12 15:13:04 -0800 7
+ */
+void test_blame_simple__can_restrict_lines_both(void)
+{
+ git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git")));
+
+ opts.min_line = 2;
+ opts.max_line = 7;
+ cl_git_pass(git_blame_file(&g_blame, g_repo, "b.txt", &opts));
+ cl_assert_equal_i(3, git_blame_get_hunk_count(g_blame));
+ check_blame_hunk_index(g_repo, g_blame, 0, 2, 3, 0, "da237394", "b.txt");
+ check_blame_hunk_index(g_repo, g_blame, 1, 5, 1, 1, "b99f7ac0", "b.txt");
+ check_blame_hunk_index(g_repo, g_blame, 2, 6, 2, 0, "63d671eb", "b.txt");
+}
+
+void test_blame_simple__can_blame_huge_file(void)
+{
+ git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git")));
+
+ cl_git_pass(git_blame_file(&g_blame, g_repo, "huge.txt", &opts));
+ cl_assert_equal_i(2, git_blame_get_hunk_count(g_blame));
+ check_blame_hunk_index(g_repo, g_blame, 0, 1, 65536, 0, "4eecfea", "huge.txt");
+ check_blame_hunk_index(g_repo, g_blame, 1, 65537, 1, 0, "6653ff4", "huge.txt");
+}
+
+/*
+ * $ git blame -n branch_file.txt be3563a..HEAD
+ * orig line no final line no
+ * commit V author timestamp V
+ * ^be3563a 1 (Scott Chacon 2010-05-25 11:58:27 -0700 1) hi
+ * a65fedf3 2 (Scott Chacon 2011-08-09 19:33:46 -0700 2) bye!
+ */
+void test_blame_simple__can_restrict_to_newish_commits(void)
+{
+ git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
+
+ {
+ git_object *obj;
+ cl_git_pass(git_revparse_single(&obj, g_repo, "be3563a"));
+ git_oid_cpy(&opts.oldest_commit, git_object_id(obj));
+ git_object_free(obj);
+ }
+
+ cl_git_pass(git_blame_file(&g_blame, g_repo, "branch_file.txt", &opts));
+
+ cl_assert_equal_i(2, git_blame_get_hunk_count(g_blame));
+ check_blame_hunk_index(g_repo, g_blame, 0, 1, 1, 1, "be3563a", "branch_file.txt");
+ check_blame_hunk_index(g_repo, g_blame, 1, 2, 1, 0, "a65fedf", "branch_file.txt");
+}
+
+void test_blame_simple__can_restrict_to_first_parent_commits(void)
+{
+ git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+ opts.flags |= GIT_BLAME_FIRST_PARENT;
+
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("blametest.git")));
+
+ cl_git_pass(git_blame_file(&g_blame, g_repo, "b.txt", &opts));
+ cl_assert_equal_i(4, git_blame_get_hunk_count(g_blame));
+ check_blame_hunk_index(g_repo, g_blame, 0, 1, 4, 0, "da237394", "b.txt");
+ check_blame_hunk_index(g_repo, g_blame, 1, 5, 1, 1, "b99f7ac0", "b.txt");
+ check_blame_hunk_index(g_repo, g_blame, 2, 6, 5, 0, "63d671eb", "b.txt");
+ check_blame_hunk_index(g_repo, g_blame, 3, 11, 5, 0, "bc7c5ac2", "b.txt");
+}
diff --git a/tests/libgit2/checkout/binaryunicode.c b/tests/libgit2/checkout/binaryunicode.c
new file mode 100644
index 0000000..b8c6c07
--- /dev/null
+++ b/tests/libgit2/checkout/binaryunicode.c
@@ -0,0 +1,59 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+#include "repo/repo_helpers.h"
+#include "path.h"
+#include "futils.h"
+#include "odb.h"
+
+static git_repository *g_repo;
+
+void test_checkout_binaryunicode__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("binaryunicode");
+}
+
+void test_checkout_binaryunicode__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static void execute_test(void)
+{
+ git_oid oid, check;
+ git_commit *commit;
+ git_tree *tree;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/branch1"));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &oid));
+ cl_git_pass(git_commit_tree(&tree, commit));
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+
+ cl_git_pass(git_checkout_tree(g_repo, (git_object *)tree, &opts));
+
+ git_tree_free(tree);
+ git_commit_free(commit);
+
+ /* Verify that the lenna.jpg file was checked out correctly */
+ cl_git_pass(git_oid__fromstr(&check, "8ab005d890fe53f65eda14b23672f60d9f4ec5a1", GIT_OID_SHA1));
+ cl_git_pass(git_odb__hashfile(&oid, "binaryunicode/lenna.jpg", GIT_OBJECT_BLOB, GIT_OID_SHA1));
+ cl_assert_equal_oid(&oid, &check);
+
+ /* Verify that the text file was checked out correctly */
+ cl_git_pass(git_oid__fromstr(&check, "965b223880dd4249e2c66a0cc0b4cffe1dc40f5a", GIT_OID_SHA1));
+ cl_git_pass(git_odb__hashfile(&oid, "binaryunicode/utf16_withbom_noeol_crlf.txt", GIT_OBJECT_BLOB, GIT_OID_SHA1));
+ cl_assert_equal_oid(&oid, &check);
+}
+
+void test_checkout_binaryunicode__noautocrlf(void)
+{
+ cl_repo_set_bool(g_repo, "core.autocrlf", false);
+ execute_test();
+}
+
+void test_checkout_binaryunicode__autocrlf(void)
+{
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+ execute_test();
+}
diff --git a/tests/libgit2/checkout/checkout_helpers.c b/tests/libgit2/checkout/checkout_helpers.c
new file mode 100644
index 0000000..1e9c21b
--- /dev/null
+++ b/tests/libgit2/checkout/checkout_helpers.c
@@ -0,0 +1,151 @@
+#include "clar_libgit2.h"
+#include "checkout_helpers.h"
+#include "refs.h"
+#include "futils.h"
+#include "index.h"
+
+void assert_on_branch(git_repository *repo, const char *branch)
+{
+ git_reference *head;
+ git_str bname = GIT_STR_INIT;
+
+ cl_git_pass(git_reference_lookup(&head, repo, GIT_HEAD_FILE));
+ cl_assert_(git_reference_type(head) == GIT_REFERENCE_SYMBOLIC, branch);
+
+ cl_git_pass(git_str_joinpath(&bname, "refs/heads", branch));
+ cl_assert_equal_s(bname.ptr, git_reference_symbolic_target(head));
+
+ git_reference_free(head);
+ git_str_dispose(&bname);
+}
+
+void reset_index_to_treeish(git_object *treeish)
+{
+ git_object *tree;
+ git_index *index;
+ git_repository *repo = git_object_owner(treeish);
+
+ cl_git_pass(git_object_peel(&tree, treeish, GIT_OBJECT_TREE));
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_read_tree(index, (git_tree *)tree));
+ cl_git_pass(git_index_write(index));
+
+ git_object_free(tree);
+ git_index_free(index);
+}
+
+int checkout_count_callback(
+ git_checkout_notify_t why,
+ const char *path,
+ const git_diff_file *baseline,
+ const git_diff_file *target,
+ const git_diff_file *workdir,
+ void *payload)
+{
+ checkout_counts *ct = payload;
+
+ GIT_UNUSED(baseline); GIT_UNUSED(target); GIT_UNUSED(workdir);
+
+ if (why & GIT_CHECKOUT_NOTIFY_CONFLICT) {
+ ct->n_conflicts++;
+
+ if (ct->debug) {
+ if (workdir) {
+ if (baseline) {
+ if (target)
+ fprintf(stderr, "M %s (conflicts with M %s)\n",
+ workdir->path, target->path);
+ else
+ fprintf(stderr, "M %s (conflicts with D %s)\n",
+ workdir->path, baseline->path);
+ } else {
+ if (target)
+ fprintf(stderr, "Existing %s (conflicts with A %s)\n",
+ workdir->path, target->path);
+ else
+ fprintf(stderr, "How can an untracked file be a conflict (%s)\n", workdir->path);
+ }
+ } else {
+ if (baseline) {
+ if (target)
+ fprintf(stderr, "D %s (conflicts with M %s)\n",
+ target->path, baseline->path);
+ else
+ fprintf(stderr, "D %s (conflicts with D %s)\n",
+ baseline->path, baseline->path);
+ } else {
+ if (target)
+ fprintf(stderr, "How can an added file with no workdir be a conflict (%s)\n", target->path);
+ else
+ fprintf(stderr, "How can a nonexistent file be a conflict (%s)\n", path);
+ }
+ }
+ }
+ }
+
+ if (why & GIT_CHECKOUT_NOTIFY_DIRTY) {
+ ct->n_dirty++;
+
+ if (ct->debug) {
+ if (workdir)
+ fprintf(stderr, "M %s\n", workdir->path);
+ else
+ fprintf(stderr, "D %s\n", baseline->path);
+ }
+ }
+
+ if (why & GIT_CHECKOUT_NOTIFY_UPDATED) {
+ ct->n_updates++;
+
+ if (ct->debug) {
+ if (baseline) {
+ if (target)
+ fprintf(stderr, "update: M %s\n", path);
+ else
+ fprintf(stderr, "update: D %s\n", path);
+ } else {
+ if (target)
+ fprintf(stderr, "update: A %s\n", path);
+ else
+ fprintf(stderr, "update: this makes no sense %s\n", path);
+ }
+ }
+ }
+
+ if (why & GIT_CHECKOUT_NOTIFY_UNTRACKED) {
+ ct->n_untracked++;
+
+ if (ct->debug)
+ fprintf(stderr, "? %s\n", path);
+ }
+
+ if (why & GIT_CHECKOUT_NOTIFY_IGNORED) {
+ ct->n_ignored++;
+
+ if (ct->debug)
+ fprintf(stderr, "I %s\n", path);
+ }
+
+ return 0;
+}
+
+void tick_index(git_index *index)
+{
+ struct timespec ts;
+ struct p_timeval times[2];
+
+ cl_assert(index->on_disk);
+ cl_assert(git_index_path(index));
+
+ cl_git_pass(git_index_read(index, true));
+ ts = index->stamp.mtime;
+
+ times[0].tv_sec = ts.tv_sec;
+ times[0].tv_usec = ts.tv_nsec / 1000;
+ times[1].tv_sec = ts.tv_sec + 5;
+ times[1].tv_usec = ts.tv_nsec / 1000;
+
+ cl_git_pass(p_utimes(git_index_path(index), times));
+ cl_git_pass(git_index_read(index, true));
+}
diff --git a/tests/libgit2/checkout/checkout_helpers.h b/tests/libgit2/checkout/checkout_helpers.h
new file mode 100644
index 0000000..879b48b
--- /dev/null
+++ b/tests/libgit2/checkout/checkout_helpers.h
@@ -0,0 +1,30 @@
+#include "git2/object.h"
+#include "git2/repository.h"
+
+extern void assert_on_branch(git_repository *repo, const char *branch);
+extern void reset_index_to_treeish(git_object *treeish);
+
+#define check_file_contents(PATH,EXP) \
+ cl_assert_equal_file(EXP,0,PATH)
+
+#define check_file_contents_nocr(PATH,EXP) \
+ cl_assert_equal_file_ignore_cr(EXP,0,PATH)
+
+typedef struct {
+ int n_conflicts;
+ int n_dirty;
+ int n_updates;
+ int n_untracked;
+ int n_ignored;
+ int debug;
+} checkout_counts;
+
+extern int checkout_count_callback(
+ git_checkout_notify_t why,
+ const char *path,
+ const git_diff_file *baseline,
+ const git_diff_file *target,
+ const git_diff_file *workdir,
+ void *payload);
+
+extern void tick_index(git_index *index);
diff --git a/tests/libgit2/checkout/conflict.c b/tests/libgit2/checkout/conflict.c
new file mode 100644
index 0000000..b2eb939
--- /dev/null
+++ b/tests/libgit2/checkout/conflict.c
@@ -0,0 +1,1145 @@
+#include "clar_libgit2.h"
+#include "git2/repository.h"
+#include "git2/sys/index.h"
+#include "futils.h"
+#include "repository.h"
+
+static git_repository *g_repo;
+static git_index *g_index;
+
+#define TEST_REPO_PATH "merge-resolve"
+
+#define CONFLICTING_ANCESTOR_OID "d427e0b2e138501a3d15cc376077a3631e15bd46"
+#define CONFLICTING_OURS_OID "4e886e602529caa9ab11d71f86634bd1b6e0de10"
+#define CONFLICTING_THEIRS_OID "2bd0a343aeef7a2cf0d158478966a6e587ff3863"
+
+#define AUTOMERGEABLE_ANCESTOR_OID "6212c31dab5e482247d7977e4f0dd3601decf13b"
+#define AUTOMERGEABLE_OURS_OID "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf"
+#define AUTOMERGEABLE_THEIRS_OID "058541fc37114bfc1dddf6bd6bffc7fae5c2e6fe"
+
+#define LINK_ANCESTOR_OID "1a010b1c0f081b2e8901d55307a15c29ff30af0e"
+#define LINK_OURS_OID "72ea499e108df5ff0a4a913e7655bbeeb1fb69f2"
+#define LINK_THEIRS_OID "8bfb012a6d809e499bd8d3e194a3929bc8995b93"
+
+#define LINK_ANCESTOR_TARGET "file"
+#define LINK_OURS_TARGET "other-file"
+#define LINK_THEIRS_TARGET "still-another-file"
+
+#define CONFLICTING_OURS_FILE \
+ "this file is changed in master and branch\n"
+#define CONFLICTING_THEIRS_FILE \
+ "this file is changed in branch and master\n"
+#define CONFLICTING_DIFF3_FILE \
+ "<<<<<<< ours\n" \
+ "this file is changed in master and branch\n" \
+ "=======\n" \
+ "this file is changed in branch and master\n" \
+ ">>>>>>> theirs\n"
+
+#define AUTOMERGEABLE_MERGED_FILE \
+ "this file is changed in master\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is changed in branch\n"
+
+struct checkout_index_entry {
+ uint16_t mode;
+ char oid_str[GIT_OID_SHA1_HEXSIZE+1];
+ int stage;
+ char path[128];
+};
+
+struct checkout_name_entry {
+ char ancestor[64];
+ char ours[64];
+ char theirs[64];
+};
+
+void test_checkout_conflict__initialize(void)
+{
+ git_config *cfg;
+
+ g_repo = cl_git_sandbox_init(TEST_REPO_PATH);
+ git_repository_index(&g_index, g_repo);
+
+ cl_git_rewritefile(
+ TEST_REPO_PATH "/.gitattributes",
+ "* text eol=lf\n");
+
+ /* Ensure that the user's merge.conflictstyle doesn't interfere */
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_git_pass(git_config_set_string(cfg, "merge.conflictstyle", "merge"));
+ git_config_free(cfg);
+}
+
+void test_checkout_conflict__cleanup(void)
+{
+ git_index_free(g_index);
+ cl_git_sandbox_cleanup();
+}
+
+static void create_index(struct checkout_index_entry *entries, size_t entries_len)
+{
+ git_str path = GIT_STR_INIT;
+ size_t i;
+
+ for (i = 0; i < entries_len; i++) {
+ git_str_joinpath(&path, TEST_REPO_PATH, entries[i].path);
+
+ if (entries[i].stage == 3 && (i == 0 || strcmp(entries[i-1].path, entries[i].path) != 0 || entries[i-1].stage != 2))
+ p_unlink(git_str_cstr(&path));
+
+ cl_git_pass(git_index_remove_bypath(g_index, entries[i].path));
+ }
+
+ for (i = 0; i < entries_len; i++) {
+ git_index_entry entry;
+
+ memset(&entry, 0x0, sizeof(git_index_entry));
+
+ entry.mode = entries[i].mode;
+ GIT_INDEX_ENTRY_STAGE_SET(&entry, entries[i].stage);
+ git_oid__fromstr(&entry.id, entries[i].oid_str, GIT_OID_SHA1);
+ entry.path = entries[i].path;
+
+ cl_git_pass(git_index_add(g_index, &entry));
+ }
+
+ git_str_dispose(&path);
+}
+
+static void create_index_names(struct checkout_name_entry *entries, size_t entries_len)
+{
+ size_t i;
+
+ for (i = 0; i < entries_len; i++) {
+ cl_git_pass(git_index_name_add(g_index,
+ strlen(entries[i].ancestor) == 0 ? NULL : entries[i].ancestor,
+ strlen(entries[i].ours) == 0 ? NULL : entries[i].ours,
+ strlen(entries[i].theirs) == 0 ? NULL : entries[i].theirs));
+ }
+}
+
+static void create_conflicting_index(void)
+{
+ struct checkout_index_entry checkout_index_entries[] = {
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "conflicting.txt" },
+ { 0100644, CONFLICTING_OURS_OID, 2, "conflicting.txt" },
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "conflicting.txt" },
+ };
+
+ create_index(checkout_index_entries, 3);
+ cl_git_pass(git_index_write(g_index));
+}
+
+static void ensure_workdir_contents(const char *path, const char *contents)
+{
+ git_str fullpath = GIT_STR_INIT, data_buf = GIT_STR_INIT;
+
+ cl_git_pass(
+ git_str_joinpath(&fullpath, git_repository_workdir(g_repo), path));
+
+ cl_git_pass(git_futils_readbuffer(&data_buf, git_str_cstr(&fullpath)));
+ cl_assert(strcmp(git_str_cstr(&data_buf), contents) == 0);
+
+ git_str_dispose(&fullpath);
+ git_str_dispose(&data_buf);
+}
+
+static void ensure_workdir_oid(const char *path, const char *oid_str)
+{
+ git_oid expected, actual;
+
+ cl_git_pass(git_oid__fromstr(&expected, oid_str, GIT_OID_SHA1));
+ cl_git_pass(git_repository_hashfile(&actual, g_repo, path, GIT_OBJECT_BLOB, NULL));
+ cl_assert_equal_oid(&expected, &actual);
+}
+
+static void ensure_workdir_mode(const char *path, int mode)
+{
+#ifdef GIT_WIN32
+ GIT_UNUSED(path);
+ GIT_UNUSED(mode);
+#else
+ git_str fullpath = GIT_STR_INIT;
+ struct stat st;
+
+ cl_git_pass(
+ git_str_joinpath(&fullpath, git_repository_workdir(g_repo), path));
+
+ cl_git_pass(p_stat(git_str_cstr(&fullpath), &st));
+ cl_assert_equal_i((mode & S_IRWXU), (st.st_mode & S_IRWXU));
+
+ git_str_dispose(&fullpath);
+#endif
+}
+
+static void ensure_workdir(const char *path, int mode, const char *oid_str)
+{
+ ensure_workdir_mode(path, mode);
+ ensure_workdir_oid(path, oid_str);
+}
+
+static void ensure_workdir_link(
+ git_repository *repo,
+ const char *path,
+ const char *target)
+{
+ int symlinks;
+
+ cl_git_pass(git_repository__configmap_lookup(&symlinks, repo, GIT_CONFIGMAP_SYMLINKS));
+
+ if (!symlinks) {
+ ensure_workdir_contents(path, target);
+ } else {
+ git_str fullpath = GIT_STR_INIT;
+ char actual[1024];
+ struct stat st;
+ int len;
+
+ cl_git_pass(
+ git_str_joinpath(&fullpath, git_repository_workdir(g_repo), path));
+
+ cl_git_pass(p_lstat(git_str_cstr(&fullpath), &st));
+ cl_assert(S_ISLNK(st.st_mode));
+
+ cl_assert((len = p_readlink(git_str_cstr(&fullpath), actual, 1024)) > 0);
+ actual[len] = '\0';
+ cl_assert(strcmp(actual, target) == 0);
+
+ git_str_dispose(&fullpath);
+ }
+}
+
+void test_checkout_conflict__ignored(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ opts.checkout_strategy |= GIT_CHECKOUT_SKIP_UNMERGED;
+
+ create_conflicting_index();
+ cl_git_pass(p_unlink(TEST_REPO_PATH "/conflicting.txt"));
+
+ cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+ cl_assert(!git_fs_path_exists(TEST_REPO_PATH "/conflicting.txt"));
+}
+
+void test_checkout_conflict__ours(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ opts.checkout_strategy |= GIT_CHECKOUT_USE_OURS;
+
+ create_conflicting_index();
+
+ cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+ ensure_workdir_contents("conflicting.txt", CONFLICTING_OURS_FILE);
+}
+
+void test_checkout_conflict__theirs(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ opts.checkout_strategy |= GIT_CHECKOUT_USE_THEIRS;
+
+ create_conflicting_index();
+
+ cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+ ensure_workdir_contents("conflicting.txt", CONFLICTING_THEIRS_FILE);
+
+}
+
+void test_checkout_conflict__diff3(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ create_conflicting_index();
+
+ cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+ ensure_workdir_contents("conflicting.txt", CONFLICTING_DIFF3_FILE);
+}
+
+void test_checkout_conflict__automerge(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ struct checkout_index_entry checkout_index_entries[] = {
+ { 0100644, AUTOMERGEABLE_ANCESTOR_OID, 1, "automergeable.txt" },
+ { 0100644, AUTOMERGEABLE_OURS_OID, 2, "automergeable.txt" },
+ { 0100644, AUTOMERGEABLE_THEIRS_OID, 3, "automergeable.txt" },
+ };
+
+ create_index(checkout_index_entries, 3);
+ cl_git_pass(git_index_write(g_index));
+
+ cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+ ensure_workdir_contents("automergeable.txt", AUTOMERGEABLE_MERGED_FILE);
+}
+
+void test_checkout_conflict__directory_file(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ struct checkout_index_entry checkout_index_entries[] = {
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "df-1" },
+ { 0100644, CONFLICTING_OURS_OID, 2, "df-1" },
+ { 0100644, CONFLICTING_THEIRS_OID, 0, "df-1/file" },
+
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "df-2" },
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "df-2" },
+ { 0100644, CONFLICTING_OURS_OID, 0, "df-2/file" },
+
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "df-3" },
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "df-3/file" },
+ { 0100644, CONFLICTING_OURS_OID, 2, "df-3/file" },
+
+ { 0100644, CONFLICTING_OURS_OID, 2, "df-4" },
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "df-4/file" },
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "df-4/file" },
+ };
+
+ opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
+
+ create_index(checkout_index_entries, 12);
+ cl_git_pass(git_index_write(g_index));
+
+ cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+ ensure_workdir_oid("df-1/file", CONFLICTING_THEIRS_OID);
+ ensure_workdir_oid("df-1~ours", CONFLICTING_OURS_OID);
+ ensure_workdir_oid("df-2/file", CONFLICTING_OURS_OID);
+ ensure_workdir_oid("df-2~theirs", CONFLICTING_THEIRS_OID);
+ ensure_workdir_oid("df-3/file", CONFLICTING_OURS_OID);
+ ensure_workdir_oid("df-3~theirs", CONFLICTING_THEIRS_OID);
+ ensure_workdir_oid("df-4~ours", CONFLICTING_OURS_OID);
+ ensure_workdir_oid("df-4/file", CONFLICTING_THEIRS_OID);
+}
+
+void test_checkout_conflict__directory_file_with_custom_labels(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ struct checkout_index_entry checkout_index_entries[] = {
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "df-1" },
+ { 0100644, CONFLICTING_OURS_OID, 2, "df-1" },
+ { 0100644, CONFLICTING_THEIRS_OID, 0, "df-1/file" },
+
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "df-2" },
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "df-2" },
+ { 0100644, CONFLICTING_OURS_OID, 0, "df-2/file" },
+
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "df-3" },
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "df-3/file" },
+ { 0100644, CONFLICTING_OURS_OID, 2, "df-3/file" },
+
+ { 0100644, CONFLICTING_OURS_OID, 2, "df-4" },
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "df-4/file" },
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "df-4/file" },
+ };
+
+ opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
+ opts.our_label = "HEAD";
+ opts.their_label = "branch";
+
+ create_index(checkout_index_entries, 12);
+ cl_git_pass(git_index_write(g_index));
+
+ cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+ ensure_workdir_oid("df-1/file", CONFLICTING_THEIRS_OID);
+ ensure_workdir_oid("df-1~HEAD", CONFLICTING_OURS_OID);
+ ensure_workdir_oid("df-2/file", CONFLICTING_OURS_OID);
+ ensure_workdir_oid("df-2~branch", CONFLICTING_THEIRS_OID);
+ ensure_workdir_oid("df-3/file", CONFLICTING_OURS_OID);
+ ensure_workdir_oid("df-3~branch", CONFLICTING_THEIRS_OID);
+ ensure_workdir_oid("df-4~HEAD", CONFLICTING_OURS_OID);
+ ensure_workdir_oid("df-4/file", CONFLICTING_THEIRS_OID);
+}
+
+void test_checkout_conflict__link_file(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ struct checkout_index_entry checkout_index_entries[] = {
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "link-1" },
+ { 0100644, CONFLICTING_OURS_OID, 2, "link-1" },
+ { 0120000, LINK_THEIRS_OID, 3, "link-1" },
+
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "link-2" },
+ { 0120000, LINK_OURS_OID, 2, "link-2" },
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "link-2" },
+
+ { 0120000, LINK_ANCESTOR_OID, 1, "link-3" },
+ { 0100644, CONFLICTING_OURS_OID, 2, "link-3" },
+ { 0120000, LINK_THEIRS_OID, 3, "link-3" },
+
+ { 0120000, LINK_ANCESTOR_OID, 1, "link-4" },
+ { 0120000, LINK_OURS_OID, 2, "link-4" },
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "link-4" },
+ };
+
+ opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
+
+ create_index(checkout_index_entries, 12);
+ cl_git_pass(git_index_write(g_index));
+
+ cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+ /* Typechange conflicts always keep the file in the workdir */
+ ensure_workdir_oid("link-1", CONFLICTING_OURS_OID);
+ ensure_workdir_oid("link-2", CONFLICTING_THEIRS_OID);
+ ensure_workdir_oid("link-3", CONFLICTING_OURS_OID);
+ ensure_workdir_oid("link-4", CONFLICTING_THEIRS_OID);
+}
+
+void test_checkout_conflict__links(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ struct checkout_index_entry checkout_index_entries[] = {
+ { 0120000, LINK_ANCESTOR_OID, 1, "link-1" },
+ { 0120000, LINK_OURS_OID, 2, "link-1" },
+ { 0120000, LINK_THEIRS_OID, 3, "link-1" },
+
+ { 0120000, LINK_OURS_OID, 2, "link-2" },
+ { 0120000, LINK_THEIRS_OID, 3, "link-2" },
+ };
+
+ opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
+
+ create_index(checkout_index_entries, 5);
+ cl_git_pass(git_index_write(g_index));
+
+ cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+ /* Conflicts with links always keep the ours side (even with -Xtheirs) */
+ ensure_workdir_link(g_repo, "link-1", LINK_OURS_TARGET);
+ ensure_workdir_link(g_repo, "link-2", LINK_OURS_TARGET);
+}
+
+void test_checkout_conflict__add_add(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ struct checkout_index_entry checkout_index_entries[] = {
+ { 0100644, CONFLICTING_OURS_OID, 2, "conflicting.txt" },
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "conflicting.txt" },
+ };
+
+ opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
+
+ create_index(checkout_index_entries, 2);
+ cl_git_pass(git_index_write(g_index));
+
+ cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+ /* Add/add writes diff3 files */
+ ensure_workdir_contents("conflicting.txt", CONFLICTING_DIFF3_FILE);
+}
+
+void test_checkout_conflict__mode_change(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ struct checkout_index_entry checkout_index_entries[] = {
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "executable-1" },
+ { 0100755, CONFLICTING_ANCESTOR_OID, 2, "executable-1" },
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "executable-1" },
+
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "executable-2" },
+ { 0100644, CONFLICTING_OURS_OID, 2, "executable-2" },
+ { 0100755, CONFLICTING_ANCESTOR_OID, 3, "executable-2" },
+
+ { 0100755, CONFLICTING_ANCESTOR_OID, 1, "executable-3" },
+ { 0100644, CONFLICTING_ANCESTOR_OID, 2, "executable-3" },
+ { 0100755, CONFLICTING_THEIRS_OID, 3, "executable-3" },
+
+ { 0100755, CONFLICTING_ANCESTOR_OID, 1, "executable-4" },
+ { 0100755, CONFLICTING_OURS_OID, 2, "executable-4" },
+ { 0100644, CONFLICTING_ANCESTOR_OID, 3, "executable-4" },
+
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "executable-5" },
+ { 0100755, CONFLICTING_OURS_OID, 2, "executable-5" },
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "executable-5" },
+
+ { 0100755, CONFLICTING_ANCESTOR_OID, 1, "executable-6" },
+ { 0100644, CONFLICTING_OURS_OID, 2, "executable-6" },
+ { 0100755, CONFLICTING_THEIRS_OID, 3, "executable-6" },
+ };
+
+ opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
+
+ create_index(checkout_index_entries, 18);
+ cl_git_pass(git_index_write(g_index));
+
+ cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+ /* Keep the modified mode */
+ ensure_workdir_oid("executable-1", CONFLICTING_THEIRS_OID);
+ ensure_workdir_mode("executable-1", 0100755);
+
+ ensure_workdir_oid("executable-2", CONFLICTING_OURS_OID);
+ ensure_workdir_mode("executable-2", 0100755);
+
+ ensure_workdir_oid("executable-3", CONFLICTING_THEIRS_OID);
+ ensure_workdir_mode("executable-3", 0100644);
+
+ ensure_workdir_oid("executable-4", CONFLICTING_OURS_OID);
+ ensure_workdir_mode("executable-4", 0100644);
+
+ ensure_workdir_contents("executable-5", CONFLICTING_DIFF3_FILE);
+ ensure_workdir_mode("executable-5", 0100755);
+
+ ensure_workdir_contents("executable-6", CONFLICTING_DIFF3_FILE);
+ ensure_workdir_mode("executable-6", 0100644);
+}
+
+void test_checkout_conflict__renames(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ struct checkout_index_entry checkout_index_entries[] = {
+ { 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e", 0, "0a-no-change.txt" },
+ { 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-duplicated-in-ours.txt" },
+ { 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 1, "0b-rewritten-in-ours.txt" },
+ { 0100644, "e376fbdd06ebf021c92724da9f26f44212734e3e", 2, "0b-rewritten-in-ours.txt" },
+ { 0100644, "b2d399ae15224e1d58066e3c8df70ce37de7a656", 3, "0b-rewritten-in-ours.txt" },
+ { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-duplicated-in-theirs.txt" },
+ { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 1, "0c-rewritten-in-theirs.txt" },
+ { 0100644, "efc9121fdedaf08ba180b53ebfbcf71bd488ed09", 2, "0c-rewritten-in-theirs.txt" },
+ { 0100644, "712ebba6669ea847d9829e4f1059d6c830c8b531", 3, "0c-rewritten-in-theirs.txt" },
+ { 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638", 0, "1a-newname-in-ours-edited-in-theirs.txt" },
+ { 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-newname-in-ours.txt" },
+ { 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a", 0, "1b-newname-in-theirs-edited-in-ours.txt" },
+ { 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-newname-in-theirs.txt" },
+ { 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt" },
+ { 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 2, "3a-newname-in-ours-deleted-in-theirs.txt" },
+ { 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 1, "3a-renamed-in-ours-deleted-in-theirs.txt" },
+ { 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 3, "3b-newname-in-theirs-deleted-in-ours.txt" },
+ { 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 1, "3b-renamed-in-theirs-deleted-in-ours.txt" },
+ { 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 2, "4a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "8b5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a", 3, "4a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 1, "4a-renamed-in-ours-added-in-theirs.txt" },
+ { 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9", 2, "4b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 3, "4b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 1, "4b-renamed-in-theirs-added-in-ours.txt" },
+ { 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 2, "5a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "98ba4205fcf31f5dd93c916d35fe3f3b3d0e6714", 3, "5a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 1, "5a-renamed-in-ours-added-in-theirs.txt" },
+ { 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 3, "5a-renamed-in-ours-added-in-theirs.txt" },
+ { 0100644, "385c8a0f26ddf79e9041e15e17dc352ed2c4cced", 2, "5b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 3, "5b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 1, "5b-renamed-in-theirs-added-in-ours.txt" },
+ { 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 2, "5b-renamed-in-theirs-added-in-ours.txt" },
+ { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 2, "6-both-renamed-1-to-2-ours.txt" },
+ { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 3, "6-both-renamed-1-to-2-theirs.txt" },
+ { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 1, "6-both-renamed-1-to-2.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 1, "7-both-renamed-side-1.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 3, "7-both-renamed-side-1.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 1, "7-both-renamed-side-2.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 2, "7-both-renamed-side-2.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 2, "7-both-renamed.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 3, "7-both-renamed.txt" }
+ };
+
+ struct checkout_name_entry checkout_name_entries[] = {
+ {
+ "3a-renamed-in-ours-deleted-in-theirs.txt",
+ "3a-newname-in-ours-deleted-in-theirs.txt",
+ ""
+ },
+
+ {
+ "3b-renamed-in-theirs-deleted-in-ours.txt",
+ "",
+ "3b-newname-in-theirs-deleted-in-ours.txt"
+ },
+
+ {
+ "4a-renamed-in-ours-added-in-theirs.txt",
+ "4a-newname-in-ours-added-in-theirs.txt",
+ ""
+ },
+
+ {
+ "4b-renamed-in-theirs-added-in-ours.txt",
+ "",
+ "4b-newname-in-theirs-added-in-ours.txt"
+ },
+
+ {
+ "5a-renamed-in-ours-added-in-theirs.txt",
+ "5a-newname-in-ours-added-in-theirs.txt",
+ "5a-renamed-in-ours-added-in-theirs.txt"
+ },
+
+ {
+ "5b-renamed-in-theirs-added-in-ours.txt",
+ "5b-renamed-in-theirs-added-in-ours.txt",
+ "5b-newname-in-theirs-added-in-ours.txt"
+ },
+
+ {
+ "6-both-renamed-1-to-2.txt",
+ "6-both-renamed-1-to-2-ours.txt",
+ "6-both-renamed-1-to-2-theirs.txt"
+ },
+
+ {
+ "7-both-renamed-side-1.txt",
+ "7-both-renamed.txt",
+ "7-both-renamed-side-1.txt"
+ },
+
+ {
+ "7-both-renamed-side-2.txt",
+ "7-both-renamed-side-2.txt",
+ "7-both-renamed.txt"
+ }
+ };
+
+ opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
+
+ create_index(checkout_index_entries, 41);
+ create_index_names(checkout_name_entries, 9);
+ cl_git_pass(git_index_write(g_index));
+
+ cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+ ensure_workdir("0a-no-change.txt",
+ 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e");
+
+ ensure_workdir("0b-duplicated-in-ours.txt",
+ 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6");
+
+ ensure_workdir("0b-rewritten-in-ours.txt",
+ 0100644, "4c7e515d6d52d820496858f2f059ece69e99e2e3");
+
+ ensure_workdir("0c-duplicated-in-theirs.txt",
+ 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31");
+
+ ensure_workdir("0c-rewritten-in-theirs.txt",
+ 0100644, "4648d658682d1155c2a3db5b0c53305e26884ea5");
+
+ ensure_workdir("1a-newname-in-ours-edited-in-theirs.txt",
+ 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638");
+
+ ensure_workdir("1a-newname-in-ours.txt",
+ 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb");
+
+ ensure_workdir("1b-newname-in-theirs-edited-in-ours.txt",
+ 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a");
+
+ ensure_workdir("1b-newname-in-theirs.txt",
+ 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136");
+
+ ensure_workdir("2-newname-in-both.txt",
+ 0100644, "178940b450f238a56c0d75b7955cb57b38191982");
+
+ ensure_workdir("3a-newname-in-ours-deleted-in-theirs.txt",
+ 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9");
+
+ ensure_workdir("3b-newname-in-theirs-deleted-in-ours.txt",
+ 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495");
+
+ ensure_workdir("4a-newname-in-ours-added-in-theirs.txt~ours",
+ 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c");
+
+ ensure_workdir("4a-newname-in-ours-added-in-theirs.txt~theirs",
+ 0100644, "8b5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a");
+
+ ensure_workdir("4b-newname-in-theirs-added-in-ours.txt~ours",
+ 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9");
+
+ ensure_workdir("4b-newname-in-theirs-added-in-ours.txt~theirs",
+ 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db");
+
+ ensure_workdir("5a-newname-in-ours-added-in-theirs.txt~ours",
+ 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436");
+
+ ensure_workdir("5a-newname-in-ours-added-in-theirs.txt~theirs",
+ 0100644, "98ba4205fcf31f5dd93c916d35fe3f3b3d0e6714");
+
+ ensure_workdir("5b-newname-in-theirs-added-in-ours.txt~ours",
+ 0100644, "385c8a0f26ddf79e9041e15e17dc352ed2c4cced");
+
+ ensure_workdir("5b-newname-in-theirs-added-in-ours.txt~theirs",
+ 0100644, "63247125386de9ec90a27ad36169307bf8a11a38");
+
+ ensure_workdir("6-both-renamed-1-to-2-ours.txt",
+ 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450");
+
+ ensure_workdir("6-both-renamed-1-to-2-theirs.txt",
+ 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450");
+
+ ensure_workdir("7-both-renamed.txt~ours",
+ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11");
+
+ ensure_workdir("7-both-renamed.txt~theirs",
+ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07");
+}
+
+void test_checkout_conflict__rename_keep_ours(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ struct checkout_index_entry checkout_index_entries[] = {
+ { 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e", 0, "0a-no-change.txt" },
+ { 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-duplicated-in-ours.txt" },
+ { 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 1, "0b-rewritten-in-ours.txt" },
+ { 0100644, "e376fbdd06ebf021c92724da9f26f44212734e3e", 2, "0b-rewritten-in-ours.txt" },
+ { 0100644, "b2d399ae15224e1d58066e3c8df70ce37de7a656", 3, "0b-rewritten-in-ours.txt" },
+ { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-duplicated-in-theirs.txt" },
+ { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 1, "0c-rewritten-in-theirs.txt" },
+ { 0100644, "efc9121fdedaf08ba180b53ebfbcf71bd488ed09", 2, "0c-rewritten-in-theirs.txt" },
+ { 0100644, "712ebba6669ea847d9829e4f1059d6c830c8b531", 3, "0c-rewritten-in-theirs.txt" },
+ { 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638", 0, "1a-newname-in-ours-edited-in-theirs.txt" },
+ { 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-newname-in-ours.txt" },
+ { 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a", 0, "1b-newname-in-theirs-edited-in-ours.txt" },
+ { 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-newname-in-theirs.txt" },
+ { 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt" },
+ { 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 2, "3a-newname-in-ours-deleted-in-theirs.txt" },
+ { 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 1, "3a-renamed-in-ours-deleted-in-theirs.txt" },
+ { 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 3, "3b-newname-in-theirs-deleted-in-ours.txt" },
+ { 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 1, "3b-renamed-in-theirs-deleted-in-ours.txt" },
+ { 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 2, "4a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "8b5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a", 3, "4a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 1, "4a-renamed-in-ours-added-in-theirs.txt" },
+ { 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9", 2, "4b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 3, "4b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 1, "4b-renamed-in-theirs-added-in-ours.txt" },
+ { 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 2, "5a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "98ba4205fcf31f5dd93c916d35fe3f3b3d0e6714", 3, "5a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 1, "5a-renamed-in-ours-added-in-theirs.txt" },
+ { 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 3, "5a-renamed-in-ours-added-in-theirs.txt" },
+ { 0100644, "385c8a0f26ddf79e9041e15e17dc352ed2c4cced", 2, "5b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 3, "5b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 1, "5b-renamed-in-theirs-added-in-ours.txt" },
+ { 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 2, "5b-renamed-in-theirs-added-in-ours.txt" },
+ { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 2, "6-both-renamed-1-to-2-ours.txt" },
+ { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 3, "6-both-renamed-1-to-2-theirs.txt" },
+ { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 1, "6-both-renamed-1-to-2.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 1, "7-both-renamed-side-1.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 3, "7-both-renamed-side-1.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 1, "7-both-renamed-side-2.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 2, "7-both-renamed-side-2.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 2, "7-both-renamed.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 3, "7-both-renamed.txt" }
+ };
+
+ struct checkout_name_entry checkout_name_entries[] = {
+ {
+ "3a-renamed-in-ours-deleted-in-theirs.txt",
+ "3a-newname-in-ours-deleted-in-theirs.txt",
+ ""
+ },
+
+ {
+ "3b-renamed-in-theirs-deleted-in-ours.txt",
+ "",
+ "3b-newname-in-theirs-deleted-in-ours.txt"
+ },
+
+ {
+ "4a-renamed-in-ours-added-in-theirs.txt",
+ "4a-newname-in-ours-added-in-theirs.txt",
+ ""
+ },
+
+ {
+ "4b-renamed-in-theirs-added-in-ours.txt",
+ "",
+ "4b-newname-in-theirs-added-in-ours.txt"
+ },
+
+ {
+ "5a-renamed-in-ours-added-in-theirs.txt",
+ "5a-newname-in-ours-added-in-theirs.txt",
+ "5a-renamed-in-ours-added-in-theirs.txt"
+ },
+
+ {
+ "5b-renamed-in-theirs-added-in-ours.txt",
+ "5b-renamed-in-theirs-added-in-ours.txt",
+ "5b-newname-in-theirs-added-in-ours.txt"
+ },
+
+ {
+ "6-both-renamed-1-to-2.txt",
+ "6-both-renamed-1-to-2-ours.txt",
+ "6-both-renamed-1-to-2-theirs.txt"
+ },
+
+ {
+ "7-both-renamed-side-1.txt",
+ "7-both-renamed.txt",
+ "7-both-renamed-side-1.txt"
+ },
+
+ {
+ "7-both-renamed-side-2.txt",
+ "7-both-renamed-side-2.txt",
+ "7-both-renamed.txt"
+ }
+ };
+
+ opts.checkout_strategy |= GIT_CHECKOUT_SAFE | GIT_CHECKOUT_USE_OURS;
+
+ create_index(checkout_index_entries, 41);
+ create_index_names(checkout_name_entries, 9);
+ cl_git_pass(git_index_write(g_index));
+
+ cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+ ensure_workdir("0a-no-change.txt",
+ 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e");
+
+ ensure_workdir("0b-duplicated-in-ours.txt",
+ 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6");
+
+ ensure_workdir("0b-rewritten-in-ours.txt",
+ 0100644, "e376fbdd06ebf021c92724da9f26f44212734e3e");
+
+ ensure_workdir("0c-duplicated-in-theirs.txt",
+ 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31");
+
+ ensure_workdir("0c-rewritten-in-theirs.txt",
+ 0100644, "efc9121fdedaf08ba180b53ebfbcf71bd488ed09");
+
+ ensure_workdir("1a-newname-in-ours-edited-in-theirs.txt",
+ 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638");
+
+ ensure_workdir("1a-newname-in-ours.txt",
+ 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb");
+
+ ensure_workdir("1b-newname-in-theirs-edited-in-ours.txt",
+ 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a");
+
+ ensure_workdir("1b-newname-in-theirs.txt",
+ 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136");
+
+ ensure_workdir("2-newname-in-both.txt",
+ 0100644, "178940b450f238a56c0d75b7955cb57b38191982");
+
+ ensure_workdir("3a-newname-in-ours-deleted-in-theirs.txt",
+ 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9");
+
+ ensure_workdir("3b-newname-in-theirs-deleted-in-ours.txt",
+ 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495");
+
+ ensure_workdir("4a-newname-in-ours-added-in-theirs.txt",
+ 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c");
+
+ ensure_workdir("4b-newname-in-theirs-added-in-ours.txt",
+ 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9");
+
+ ensure_workdir("5a-newname-in-ours-added-in-theirs.txt",
+ 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436");
+
+ ensure_workdir("5b-newname-in-theirs-added-in-ours.txt",
+ 0100644, "385c8a0f26ddf79e9041e15e17dc352ed2c4cced");
+
+ ensure_workdir("6-both-renamed-1-to-2-ours.txt",
+ 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450");
+
+ ensure_workdir("7-both-renamed.txt",
+ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11");
+}
+
+void test_checkout_conflict__name_mangled_file_exists_in_workdir(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ struct checkout_index_entry checkout_index_entries[] = {
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 1, "test-one-side-one.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 3, "test-one-side-one.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 1, "test-one-side-two.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 2, "test-one-side-two.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 2, "test-one.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 3, "test-one.txt" },
+
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 1, "test-two-side-one.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 3, "test-two-side-one.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 1, "test-two-side-two.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 2, "test-two-side-two.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 2, "test-two.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 3, "test-two.txt" },
+
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 1, "test-three-side-one.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 3, "test-three-side-one.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 1, "test-three-side-two.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 2, "test-three-side-two.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 2, "test-three.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 3, "test-three.txt" },
+
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "directory_file-one" },
+ { 0100644, CONFLICTING_OURS_OID, 2, "directory_file-one" },
+ { 0100644, CONFLICTING_THEIRS_OID, 0, "directory_file-one/file" },
+
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "directory_file-two" },
+ { 0100644, CONFLICTING_OURS_OID, 0, "directory_file-two/file" },
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "directory_file-two" },
+ };
+
+ struct checkout_name_entry checkout_name_entries[] = {
+ {
+ "test-one-side-one.txt",
+ "test-one.txt",
+ "test-one-side-one.txt"
+ },
+ {
+ "test-one-side-two.txt",
+ "test-one-side-two.txt",
+ "test-one.txt"
+ },
+
+ {
+ "test-two-side-one.txt",
+ "test-two.txt",
+ "test-two-side-one.txt"
+ },
+ {
+ "test-two-side-two.txt",
+ "test-two-side-two.txt",
+ "test-two.txt"
+ },
+
+ {
+ "test-three-side-one.txt",
+ "test-three.txt",
+ "test-three-side-one.txt"
+ },
+ {
+ "test-three-side-two.txt",
+ "test-three-side-two.txt",
+ "test-three.txt"
+ }
+ };
+
+ opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
+
+ create_index(checkout_index_entries, 24);
+ create_index_names(checkout_name_entries, 6);
+ cl_git_pass(git_index_write(g_index));
+
+ /* Add some files on disk that conflict with the names that would be chosen
+ * for the files written for each side. */
+
+ cl_git_rewritefile("merge-resolve/test-one.txt~ours",
+ "Expect index contents to be written to ~ours_0");
+ cl_git_rewritefile("merge-resolve/test-one.txt~theirs",
+ "Expect index contents to be written to ~theirs_0");
+
+ cl_git_rewritefile("merge-resolve/test-two.txt~ours",
+ "Expect index contents to be written to ~ours_3");
+ cl_git_rewritefile("merge-resolve/test-two.txt~theirs",
+ "Expect index contents to be written to ~theirs_3");
+ cl_git_rewritefile("merge-resolve/test-two.txt~ours_0",
+ "Expect index contents to be written to ~ours_3");
+ cl_git_rewritefile("merge-resolve/test-two.txt~theirs_0",
+ "Expect index contents to be written to ~theirs_3");
+ cl_git_rewritefile("merge-resolve/test-two.txt~ours_1",
+ "Expect index contents to be written to ~ours_3");
+ cl_git_rewritefile("merge-resolve/test-two.txt~theirs_1",
+ "Expect index contents to be written to ~theirs_3");
+ cl_git_rewritefile("merge-resolve/test-two.txt~ours_2",
+ "Expect index contents to be written to ~ours_3");
+ cl_git_rewritefile("merge-resolve/test-two.txt~theirs_2",
+ "Expect index contents to be written to ~theirs_3");
+
+ cl_git_rewritefile("merge-resolve/test-three.txt~Ours",
+ "Expect case insensitive filesystems to create ~ours_0");
+ cl_git_rewritefile("merge-resolve/test-three.txt~THEIRS",
+ "Expect case insensitive filesystems to create ~theirs_0");
+
+ cl_git_rewritefile("merge-resolve/directory_file-one~ours",
+ "Index contents written to ~ours_0 in this D/F conflict");
+ cl_git_rewritefile("merge-resolve/directory_file-two~theirs",
+ "Index contents written to ~theirs_0 in this D/F conflict");
+
+ cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+ ensure_workdir("test-one.txt~ours_0",
+ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11");
+ ensure_workdir("test-one.txt~theirs_0",
+ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07");
+
+ ensure_workdir("test-two.txt~ours_3",
+ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11");
+ ensure_workdir("test-two.txt~theirs_3",
+ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07");
+
+ /* Name is mangled on case insensitive only */
+#if defined(GIT_WIN32) || defined(__APPLE__)
+ ensure_workdir("test-three.txt~ours_0",
+ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11");
+ ensure_workdir("test-three.txt~theirs_0",
+ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07");
+#else
+ ensure_workdir("test-three.txt~ours",
+ 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11");
+ ensure_workdir("test-three.txt~theirs",
+ 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07");
+#endif
+
+ ensure_workdir("directory_file-one~ours_0", 0100644, CONFLICTING_OURS_OID);
+ ensure_workdir("directory_file-two~theirs_0", 0100644, CONFLICTING_THEIRS_OID);
+}
+
+void test_checkout_conflict__update_only(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ struct checkout_index_entry checkout_index_entries[] = {
+ { 0100644, AUTOMERGEABLE_ANCESTOR_OID, 1, "automergeable.txt" },
+ { 0100644, AUTOMERGEABLE_OURS_OID, 2, "automergeable.txt" },
+ { 0100644, AUTOMERGEABLE_THEIRS_OID, 3, "automergeable.txt" },
+
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "modify-delete" },
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "modify-delete" },
+
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "directory_file-one" },
+ { 0100644, CONFLICTING_OURS_OID, 2, "directory_file-one" },
+ { 0100644, CONFLICTING_THEIRS_OID, 0, "directory_file-one/file" },
+
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "directory_file-two" },
+ { 0100644, CONFLICTING_OURS_OID, 0, "directory_file-two/file" },
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "directory_file-two" },
+ };
+
+ opts.checkout_strategy |= GIT_CHECKOUT_UPDATE_ONLY;
+
+ create_index(checkout_index_entries, 3);
+ cl_git_pass(git_index_write(g_index));
+
+ cl_git_pass(p_mkdir("merge-resolve/directory_file-two", 0777));
+ cl_git_rewritefile("merge-resolve/directory_file-two/file", CONFLICTING_OURS_FILE);
+
+ cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+ ensure_workdir_contents("automergeable.txt", AUTOMERGEABLE_MERGED_FILE);
+ ensure_workdir("directory_file-two/file", 0100644, CONFLICTING_OURS_OID);
+
+ cl_assert(!git_fs_path_exists("merge-resolve/modify-delete"));
+ cl_assert(!git_fs_path_exists("merge-resolve/test-one.txt"));
+ cl_assert(!git_fs_path_exists("merge-resolve/test-one-side-one.txt"));
+ cl_assert(!git_fs_path_exists("merge-resolve/test-one-side-two.txt"));
+ cl_assert(!git_fs_path_exists("merge-resolve/test-one.txt~ours"));
+ cl_assert(!git_fs_path_exists("merge-resolve/test-one.txt~theirs"));
+ cl_assert(!git_fs_path_exists("merge-resolve/directory_file-one/file"));
+ cl_assert(!git_fs_path_exists("merge-resolve/directory_file-one~ours"));
+ cl_assert(!git_fs_path_exists("merge-resolve/directory_file-two~theirs"));
+}
+
+void test_checkout_conflict__path_filters(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ char *paths[] = { "conflicting-1.txt", "conflicting-3.txt" };
+ git_strarray patharray = {0};
+
+ struct checkout_index_entry checkout_index_entries[] = {
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "conflicting-1.txt" },
+ { 0100644, CONFLICTING_OURS_OID, 2, "conflicting-1.txt" },
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "conflicting-1.txt" },
+
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "conflicting-2.txt" },
+ { 0100644, CONFLICTING_OURS_OID, 2, "conflicting-2.txt" },
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "conflicting-2.txt" },
+
+ { 0100644, AUTOMERGEABLE_ANCESTOR_OID, 1, "conflicting-3.txt" },
+ { 0100644, AUTOMERGEABLE_OURS_OID, 2, "conflicting-3.txt" },
+ { 0100644, AUTOMERGEABLE_THEIRS_OID, 3, "conflicting-3.txt" },
+
+ { 0100644, AUTOMERGEABLE_ANCESTOR_OID, 1, "conflicting-4.txt" },
+ { 0100644, AUTOMERGEABLE_OURS_OID, 2, "conflicting-4.txt" },
+ { 0100644, AUTOMERGEABLE_THEIRS_OID, 3, "conflicting-4.txt" },
+ };
+
+ patharray.count = 2;
+ patharray.strings = paths;
+
+ opts.paths = patharray;
+
+ create_index(checkout_index_entries, 12);
+ cl_git_pass(git_index_write(g_index));
+
+ cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+ ensure_workdir_contents("conflicting-1.txt", CONFLICTING_DIFF3_FILE);
+ cl_assert(!git_fs_path_exists("merge-resolve/conflicting-2.txt"));
+ ensure_workdir_contents("conflicting-3.txt", AUTOMERGEABLE_MERGED_FILE);
+ cl_assert(!git_fs_path_exists("merge-resolve/conflicting-4.txt"));
+}
+
+static void collect_progress(
+ const char *path,
+ size_t completed_steps,
+ size_t total_steps,
+ void *payload)
+{
+ git_vector *paths = payload;
+
+ GIT_UNUSED(completed_steps);
+ GIT_UNUSED(total_steps);
+
+ if (path == NULL)
+ return;
+
+ git_vector_insert(paths, strdup(path));
+}
+
+void test_checkout_conflict__report_progress(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_vector paths = GIT_VECTOR_INIT;
+ char *path;
+ size_t i;
+
+ struct checkout_index_entry checkout_index_entries[] = {
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "conflicting-1.txt" },
+ { 0100644, CONFLICTING_OURS_OID, 2, "conflicting-1.txt" },
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "conflicting-1.txt" },
+
+ { 0100644, CONFLICTING_ANCESTOR_OID, 1, "conflicting-2.txt" },
+ { 0100644, CONFLICTING_OURS_OID, 2, "conflicting-2.txt" },
+ { 0100644, CONFLICTING_THEIRS_OID, 3, "conflicting-2.txt" },
+
+ { 0100644, AUTOMERGEABLE_ANCESTOR_OID, 1, "conflicting-3.txt" },
+ { 0100644, AUTOMERGEABLE_OURS_OID, 2, "conflicting-3.txt" },
+ { 0100644, AUTOMERGEABLE_THEIRS_OID, 3, "conflicting-3.txt" },
+
+ { 0100644, AUTOMERGEABLE_ANCESTOR_OID, 1, "conflicting-4.txt" },
+ { 0100644, AUTOMERGEABLE_OURS_OID, 2, "conflicting-4.txt" },
+ { 0100644, AUTOMERGEABLE_THEIRS_OID, 3, "conflicting-4.txt" },
+ };
+
+ opts.progress_cb = collect_progress;
+ opts.progress_payload = &paths;
+
+
+ create_index(checkout_index_entries, 12);
+ cl_git_pass(git_index_write(g_index));
+
+ cl_git_pass(git_checkout_index(g_repo, g_index, &opts));
+
+ cl_assert_equal_i(4, git_vector_length(&paths));
+ cl_assert_equal_s("conflicting-1.txt", git_vector_get(&paths, 0));
+ cl_assert_equal_s("conflicting-2.txt", git_vector_get(&paths, 1));
+ cl_assert_equal_s("conflicting-3.txt", git_vector_get(&paths, 2));
+ cl_assert_equal_s("conflicting-4.txt", git_vector_get(&paths, 3));
+
+ git_vector_foreach(&paths, i, path)
+ git__free(path);
+
+ git_vector_free(&paths);
+}
diff --git a/tests/libgit2/checkout/crlf.c b/tests/libgit2/checkout/crlf.c
new file mode 100644
index 0000000..21f8a85
--- /dev/null
+++ b/tests/libgit2/checkout/crlf.c
@@ -0,0 +1,496 @@
+#include "clar_libgit2.h"
+#include "checkout_helpers.h"
+#include "../filter/crlf.h"
+#include "futils.h"
+
+#include "git2/checkout.h"
+#include "repository.h"
+#include "index.h"
+#include "posix.h"
+
+static git_repository *g_repo;
+
+static const char *systype;
+static git_str expected_fixture = GIT_STR_INIT;
+
+static int unlink_file(void *payload, git_str *path)
+{
+ char *fn;
+
+ cl_assert(fn = git_fs_path_basename(path->ptr));
+
+ GIT_UNUSED(payload);
+
+ if (strcmp(fn, ".git"))
+ cl_must_pass(p_unlink(path->ptr));
+
+ git__free(fn);
+ return 0;
+}
+
+void test_checkout_crlf__initialize(void)
+{
+ git_str reponame = GIT_STR_INIT;
+
+ g_repo = cl_git_sandbox_init("crlf");
+
+ /*
+ * remove the contents of the working directory so that we can
+ * check out over it.
+ */
+ cl_git_pass(git_str_puts(&reponame, "crlf"));
+ cl_git_pass(git_fs_path_direach(&reponame, 0, unlink_file, NULL));
+
+ if (GIT_EOL_NATIVE == GIT_EOL_CRLF)
+ systype = "windows";
+ else
+ systype = "posix";
+
+ git_str_dispose(&reponame);
+}
+
+void test_checkout_crlf__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+
+ if (expected_fixture.size) {
+ cl_fixture_cleanup(expected_fixture.ptr);
+ git_str_dispose(&expected_fixture);
+ }
+}
+
+struct compare_data
+{
+ const char *dirname;
+ const char *autocrlf;
+ const char *attrs;
+};
+
+static int compare_file(void *payload, git_str *actual_path)
+{
+ git_str expected_path = GIT_STR_INIT;
+ git_str actual_contents = GIT_STR_INIT;
+ git_str expected_contents = GIT_STR_INIT;
+ struct compare_data *cd = payload;
+ bool failed = true;
+ int cmp_git, cmp_gitattributes;
+ char *basename;
+
+ basename = git_fs_path_basename(actual_path->ptr);
+ cmp_git = strcmp(basename, ".git");
+ cmp_gitattributes = strcmp(basename, ".gitattributes");
+
+ if (cmp_git == 0 || cmp_gitattributes == 0) {
+ failed = false;
+ goto done;
+ }
+
+ cl_git_pass(git_str_joinpath(&expected_path, cd->dirname, basename));
+
+ if (!git_fs_path_isfile(expected_path.ptr) ||
+ !git_fs_path_isfile(actual_path->ptr))
+ goto done;
+
+ if (git_futils_readbuffer(&actual_contents, actual_path->ptr) < 0 ||
+ git_futils_readbuffer(&expected_contents, expected_path.ptr) < 0)
+ goto done;
+
+ if (actual_contents.size != expected_contents.size)
+ goto done;
+
+ if (memcmp(actual_contents.ptr, expected_contents.ptr, expected_contents.size) != 0)
+ goto done;
+
+ failed = false;
+
+done:
+ if (failed) {
+ git_str details = GIT_STR_INIT;
+ git_str_printf(&details, "filename=%s, system=%s, autocrlf=%s, attrs={%s}",
+ git_fs_path_basename(actual_path->ptr), systype, cd->autocrlf, cd->attrs);
+ clar__fail(__FILE__, __func__, __LINE__,
+ "checked out contents did not match expected", details.ptr, 0);
+ git_str_dispose(&details);
+ }
+
+ git__free(basename);
+ git_str_dispose(&expected_contents);
+ git_str_dispose(&actual_contents);
+ git_str_dispose(&expected_path);
+
+ return 0;
+}
+
+static void test_checkout(const char *autocrlf, const char *attrs)
+{
+ git_str attrbuf = GIT_STR_INIT;
+ git_str expected_dirname = GIT_STR_INIT;
+ git_str systype_and_direction = GIT_STR_INIT;
+ git_str sandboxname = GIT_STR_INIT;
+ git_str reponame = GIT_STR_INIT;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ struct compare_data compare_data = { NULL, autocrlf, attrs };
+ const char *c;
+
+ cl_git_pass(git_str_puts(&reponame, "crlf"));
+
+ cl_git_pass(git_str_puts(&systype_and_direction, systype));
+ cl_git_pass(git_str_puts(&systype_and_direction, "_to_workdir"));
+
+ cl_git_pass(git_str_puts(&sandboxname, "autocrlf_"));
+ cl_git_pass(git_str_puts(&sandboxname, autocrlf));
+
+ if (*attrs) {
+ cl_git_pass(git_str_puts(&sandboxname, ","));
+
+ for (c = attrs; *c; c++) {
+ if (*c == ' ')
+ cl_git_pass(git_str_putc(&sandboxname, ','));
+ else if (*c == '=')
+ cl_git_pass(git_str_putc(&sandboxname, '_'));
+ else
+ cl_git_pass(git_str_putc(&sandboxname, *c));
+ }
+
+ cl_git_pass(git_str_printf(&attrbuf, "* %s\n", attrs));
+ cl_git_mkfile("crlf/.gitattributes", attrbuf.ptr);
+ }
+
+ cl_repo_set_string(g_repo, "core.autocrlf", autocrlf);
+
+ cl_git_pass(git_str_joinpath(&expected_dirname, systype_and_direction.ptr, sandboxname.ptr));
+ cl_git_pass(git_str_joinpath(&expected_fixture, "crlf_data", expected_dirname.ptr));
+ cl_fixture_sandbox(expected_fixture.ptr);
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+
+ compare_data.dirname = sandboxname.ptr;
+ cl_git_pass(git_fs_path_direach(&reponame, 0, compare_file, &compare_data));
+
+ cl_fixture_cleanup(expected_fixture.ptr);
+ git_str_dispose(&expected_fixture);
+
+ git_str_dispose(&attrbuf);
+ git_str_dispose(&expected_fixture);
+ git_str_dispose(&expected_dirname);
+ git_str_dispose(&sandboxname);
+ git_str_dispose(&systype_and_direction);
+ git_str_dispose(&reponame);
+}
+
+static void empty_workdir(const char *name)
+{
+ git_vector contents = GIT_VECTOR_INIT;
+ char *basename;
+ int cmp;
+ size_t i;
+ const char *fn;
+
+ cl_git_pass(git_fs_path_dirload(&contents, name, 0, 0));
+ git_vector_foreach(&contents, i, fn) {
+ cl_assert(basename = git_fs_path_basename(fn));
+ cmp = strncasecmp(basename, ".git", 4);
+
+ git__free(basename);
+
+ if (cmp)
+ cl_git_pass(p_unlink(fn));
+ }
+ git_vector_free_deep(&contents);
+}
+
+void test_checkout_crlf__matches_core_git(void)
+{
+ const char *autocrlf[] = { "true", "false", "input", NULL };
+ const char *attrs[] = { "", "-crlf", "-text", "eol=crlf", "eol=lf",
+ "text", "text eol=crlf", "text eol=lf",
+ "text=auto", "text=auto eol=crlf", "text=auto eol=lf",
+ NULL };
+ const char **a, **b;
+
+ for (a = autocrlf; *a; a++) {
+ for (b = attrs; *b; b++) {
+ empty_workdir("crlf");
+ test_checkout(*a, *b);
+ }
+ }
+}
+
+void test_checkout_crlf__detect_crlf_autocrlf_false(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", false);
+
+ git_checkout_head(g_repo, &opts);
+
+ check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW);
+ check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
+}
+
+void test_checkout_crlf__autocrlf_false_index_size_is_unfiltered_size(void)
+{
+ git_index *index;
+ const git_index_entry *entry;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", false);
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ tick_index(index);
+
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+
+ cl_assert((entry = git_index_get_bypath(index, "all-lf", 0)) != NULL);
+ cl_assert(entry->file_size == strlen(ALL_LF_TEXT_RAW));
+
+ cl_assert((entry = git_index_get_bypath(index, "all-crlf", 0)) != NULL);
+ cl_assert(entry->file_size == strlen(ALL_CRLF_TEXT_RAW));
+
+ git_index_free(index);
+}
+
+void test_checkout_crlf__detect_crlf_autocrlf_true(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+ git_checkout_head(g_repo, &opts);
+
+ check_file_contents("./crlf/all-lf", ALL_LF_TEXT_AS_CRLF);
+ check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
+}
+
+void test_checkout_crlf__detect_crlf_autocrlf_true_utf8(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+ git_repository_set_head(g_repo, "refs/heads/master");
+ git_checkout_head(g_repo, &opts);
+
+ check_file_contents("./crlf/few-utf8-chars-lf", FEW_UTF8_CRLF_RAW);
+ check_file_contents("./crlf/many-utf8-chars-lf", MANY_UTF8_CRLF_RAW);
+
+ check_file_contents("./crlf/few-utf8-chars-crlf", FEW_UTF8_CRLF_RAW);
+ check_file_contents("./crlf/many-utf8-chars-crlf", MANY_UTF8_CRLF_RAW);
+}
+
+void test_checkout_crlf__autocrlf_true_index_size_is_filtered_size(void)
+{
+ git_index *index;
+ const git_index_entry *entry;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+ git_repository_index(&index, g_repo);
+ tick_index(index);
+
+ git_checkout_head(g_repo, &opts);
+
+ cl_assert((entry = git_index_get_bypath(index, "all-lf", 0)) != NULL);
+
+ cl_assert_equal_sz(strlen(ALL_LF_TEXT_AS_CRLF), entry->file_size);
+
+ cl_assert((entry = git_index_get_bypath(index, "all-crlf", 0)) != NULL);
+ cl_assert_equal_sz(strlen(ALL_CRLF_TEXT_RAW), entry->file_size);
+
+ git_index_free(index);
+}
+
+void test_checkout_crlf__with_ident(void)
+{
+ git_index *index;
+ const git_index_entry *entry;
+ git_blob *blob;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_mkfile("crlf/.gitattributes",
+ "*.txt text\n*.bin binary\n"
+ "*.crlf text eol=crlf\n"
+ "*.lf text eol=lf\n"
+ "*.ident text ident\n"
+ "*.identcrlf ident text eol=crlf\n"
+ "*.identlf ident text eol=lf\n");
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+ /* add files with $Id$ */
+
+ cl_git_mkfile("crlf/lf.ident", ALL_LF_TEXT_RAW "\n$Id: initial content$\n");
+ cl_git_mkfile("crlf/crlf.ident", ALL_CRLF_TEXT_RAW "\r\n$Id$\r\n\r\n");
+ cl_git_mkfile("crlf/more1.identlf", "$Id$\n" MORE_LF_TEXT_RAW);
+ cl_git_mkfile("crlf/more2.identcrlf", "\r\n$Id: $\r\n" MORE_CRLF_TEXT_RAW);
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_add_bypath(index, "lf.ident"));
+ cl_git_pass(git_index_add_bypath(index, "crlf.ident"));
+ cl_git_pass(git_index_add_bypath(index, "more1.identlf"));
+ cl_git_pass(git_index_add_bypath(index, "more2.identcrlf"));
+ cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "Some ident files\n");
+
+ git_checkout_head(g_repo, &opts);
+
+ /* check that blobs have $Id$ */
+
+ cl_assert((entry = git_index_get_bypath(index, "lf.ident", 0)));
+ cl_git_pass(git_blob_lookup(&blob, g_repo, &entry->id));
+ cl_assert_equal_s(
+ ALL_LF_TEXT_RAW "\n$Id$\n", git_blob_rawcontent(blob));
+ git_blob_free(blob);
+
+ cl_assert((entry = git_index_get_bypath(index, "more2.identcrlf", 0)));
+ cl_git_pass(git_blob_lookup(&blob, g_repo, &entry->id));
+ cl_assert_equal_s(
+ "\n$Id$\n" MORE_CRLF_TEXT_AS_LF, git_blob_rawcontent(blob));
+ git_blob_free(blob);
+
+ /* check that filesystem is initially untouched - matching core Git */
+
+ cl_assert_equal_file(
+ ALL_LF_TEXT_RAW "\n$Id: initial content$\n", 0, "crlf/lf.ident");
+
+ /* check that forced checkout rewrites correctly */
+
+ p_unlink("crlf/lf.ident");
+ p_unlink("crlf/crlf.ident");
+ p_unlink("crlf/more1.identlf");
+ p_unlink("crlf/more2.identcrlf");
+
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+
+ cl_assert_equal_file(
+ ALL_LF_TEXT_AS_CRLF
+ "\r\n$Id: fcf6d4d9c212dc66563b1171b1cd99953c756467 $\r\n",
+ 0, "crlf/lf.ident");
+ cl_assert_equal_file(
+ ALL_CRLF_TEXT_RAW
+ "\r\n$Id: f2c66ad9b2b5a734d9bf00d5000cc10a62b8a857 $\r\n\r\n",
+ 0, "crlf/crlf.ident");
+
+ cl_assert_equal_file(
+ "$Id: f7830382dac1f1583422be5530fdfbd26289431b $\n"
+ MORE_LF_TEXT_AS_LF, 0, "crlf/more1.identlf");
+
+ cl_assert_equal_file(
+ "\r\n$Id: 74677a68413012ce8d7e7cfc3f12603df3a3eac4 $\r\n"
+ MORE_CRLF_TEXT_AS_CRLF, 0, "crlf/more2.identcrlf");
+
+ git_index_free(index);
+}
+
+void test_checkout_crlf__autocrlf_false_no_attrs(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", false);
+
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+
+ check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW);
+ check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
+}
+
+void test_checkout_crlf__autocrlf_true_no_attrs(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+
+ check_file_contents("./crlf/all-lf", ALL_LF_TEXT_AS_CRLF);
+ check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_AS_CRLF);
+}
+
+void test_checkout_crlf__autocrlf_input_no_attrs(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_repo_set_string(g_repo, "core.autocrlf", "input");
+
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+
+ check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW);
+ check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
+}
+
+void test_checkout_crlf__autocrlf_false_text_auto_attr(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_mkfile("./crlf/.gitattributes", "* text=auto\n");
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", false);
+
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+
+ if (GIT_EOL_NATIVE == GIT_EOL_CRLF) {
+ check_file_contents("./crlf/all-lf", ALL_LF_TEXT_AS_CRLF);
+ check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_AS_CRLF);
+ } else {
+ check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW);
+ check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
+ }
+}
+
+void test_checkout_crlf__autocrlf_true_text_auto_attr(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_mkfile("./crlf/.gitattributes", "* text=auto\n");
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+
+ check_file_contents("./crlf/all-lf", ALL_LF_TEXT_AS_CRLF);
+ check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_AS_CRLF);
+}
+
+void test_checkout_crlf__autocrlf_input_text_auto_attr(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_mkfile("./crlf/.gitattributes", "* text=auto\n");
+
+ cl_repo_set_string(g_repo, "core.autocrlf", "input");
+
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+
+ check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW);
+ check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
+}
+
+void test_checkout_crlf__can_write_empty_file(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/empty-files"));
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+
+ check_file_contents("./crlf/test1.txt", "");
+
+ check_file_contents("./crlf/test2.txt", "test2.txt's content\r\n");
+
+ check_file_contents("./crlf/test3.txt", "");
+}
diff --git a/tests/libgit2/checkout/head.c b/tests/libgit2/checkout/head.c
new file mode 100644
index 0000000..3b0bf47
--- /dev/null
+++ b/tests/libgit2/checkout/head.c
@@ -0,0 +1,292 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+#include "repo/repo_helpers.h"
+#include "path.h"
+#include "futils.h"
+
+static git_repository *g_repo;
+
+void test_checkout_head__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_checkout_head__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_checkout_head__unborn_head_returns_GIT_EUNBORNBRANCH(void)
+{
+ make_head_unborn(g_repo, NON_EXISTING_HEAD);
+
+ cl_assert_equal_i(GIT_EUNBORNBRANCH, git_checkout_head(g_repo, NULL));
+}
+
+void test_checkout_head__with_index_only_tree(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_index *index;
+
+ /* let's start by getting things into a known state */
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+
+ /* now let's stage some new stuff including a new directory */
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ p_mkdir("testrepo/newdir", 0777);
+ cl_git_mkfile("testrepo/newdir/newfile.txt", "new file\n");
+
+ cl_git_pass(git_index_add_bypath(index, "newdir/newfile.txt"));
+ cl_git_pass(git_index_write(index));
+
+ cl_assert(git_fs_path_isfile("testrepo/newdir/newfile.txt"));
+ cl_assert(git_index_get_bypath(index, "newdir/newfile.txt", 0) != NULL);
+
+ git_index_free(index);
+
+ /* okay, so now we have staged this new file; let's see if we can remove */
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE | GIT_CHECKOUT_REMOVE_UNTRACKED;
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_assert(!git_fs_path_isfile("testrepo/newdir/newfile.txt"));
+ cl_assert(git_index_get_bypath(index, "newdir/newfile.txt", 0) == NULL);
+
+ git_index_free(index);
+}
+
+void test_checkout_head__do_not_remove_untracked_file(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_index *index;
+
+ cl_git_pass(p_mkdir("testrepo/tracked", 0755));
+ cl_git_mkfile("testrepo/tracked/tracked", "tracked\n");
+ cl_git_mkfile("testrepo/tracked/untracked", "untracked\n");
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_add_bypath(index, "tracked/tracked"));
+ cl_git_pass(git_index_write(index));
+
+ git_index_free(index);
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+
+ cl_assert(!git_fs_path_isfile("testrepo/tracked/tracked"));
+ cl_assert(git_fs_path_isfile("testrepo/tracked/untracked"));
+}
+
+void test_checkout_head__do_not_remove_untracked_file_in_subdir(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_index *index;
+
+ cl_git_pass(p_mkdir("testrepo/tracked", 0755));
+ cl_git_pass(p_mkdir("testrepo/tracked/subdir", 0755));
+ cl_git_mkfile("testrepo/tracked/tracked", "tracked\n");
+ cl_git_mkfile("testrepo/tracked/subdir/tracked", "tracked\n");
+ cl_git_mkfile("testrepo/tracked/subdir/untracked", "untracked\n");
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_add_bypath(index, "tracked/tracked"));
+ cl_git_pass(git_index_add_bypath(index, "tracked/subdir/tracked"));
+ cl_git_pass(git_index_write(index));
+
+ git_index_free(index);
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+
+ cl_assert(!git_fs_path_isfile("testrepo/tracked/tracked"));
+ cl_assert(!git_fs_path_isfile("testrepo/tracked/subdir/tracked"));
+ cl_assert(git_fs_path_isfile("testrepo/tracked/subdir/untracked"));
+}
+
+void test_checkout_head__do_remove_untracked_paths(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_index *index;
+ char *paths[] = {"tracked/untracked"};
+
+ cl_git_pass(p_mkdir("testrepo/tracked", 0755));
+ cl_git_pass(p_mkdir("testrepo/tracked/subdir", 0755));
+ cl_git_mkfile("testrepo/tracked/tracked", "tracked\n");
+ cl_git_mkfile("testrepo/tracked/untracked", "untracked\n");
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_add_bypath(index, "tracked/tracked"));
+ cl_git_pass(git_index_write(index));
+
+ git_index_free(index);
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE | GIT_CHECKOUT_REMOVE_UNTRACKED;
+ opts.paths.strings = paths;
+ opts.paths.count = 1;
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+
+ cl_assert(git_fs_path_isfile("testrepo/tracked/tracked"));
+ cl_assert(!git_fs_path_isfile("testrepo/tracked/untracked"));
+}
+
+void test_checkout_head__do_remove_tracked_subdir(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_index *index;
+
+ cl_git_pass(p_mkdir("testrepo/subdir", 0755));
+ cl_git_pass(p_mkdir("testrepo/subdir/tracked", 0755));
+ cl_git_mkfile("testrepo/subdir/tracked-file", "tracked\n");
+ cl_git_mkfile("testrepo/subdir/untracked-file", "untracked\n");
+ cl_git_mkfile("testrepo/subdir/tracked/tracked1", "tracked\n");
+ cl_git_mkfile("testrepo/subdir/tracked/tracked2", "tracked\n");
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_add_bypath(index, "subdir/tracked-file"));
+ cl_git_pass(git_index_add_bypath(index, "subdir/tracked/tracked1"));
+ cl_git_pass(git_index_add_bypath(index, "subdir/tracked/tracked2"));
+ cl_git_pass(git_index_write(index));
+
+ git_index_free(index);
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+
+ cl_assert(!git_fs_path_isdir("testrepo/subdir/tracked"));
+ cl_assert(!git_fs_path_isfile("testrepo/subdir/tracked-file"));
+ cl_assert(git_fs_path_isfile("testrepo/subdir/untracked-file"));
+}
+
+void test_checkout_head__typechange_workdir(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_object *target;
+ struct stat st;
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_revparse_single(&target, g_repo, "HEAD"));
+ cl_git_pass(git_reset(g_repo, target, GIT_RESET_HARD, NULL));
+
+ cl_must_pass(p_chmod("testrepo/new.txt", 0755));
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+
+ cl_git_pass(p_stat("testrepo/new.txt", &st));
+ cl_assert(!GIT_PERMS_IS_EXEC(st.st_mode));
+
+ git_object_free(target);
+}
+
+void test_checkout_head__typechange_index_and_workdir(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_object *target;
+ git_index *index;
+ struct stat st;
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_revparse_single(&target, g_repo, "HEAD"));
+ cl_git_pass(git_reset(g_repo, target, GIT_RESET_HARD, NULL));
+
+ cl_must_pass(p_chmod("testrepo/new.txt", 0755));
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_add_bypath(index, "new.txt"));
+ cl_git_pass(git_index_write(index));
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+
+ cl_git_pass(p_stat("testrepo/new.txt", &st));
+ cl_assert(!GIT_PERMS_IS_EXEC(st.st_mode));
+
+ git_object_free(target);
+ git_index_free(index);
+}
+
+void test_checkout_head__workdir_filemode_is_simplified(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_object *target, *branch;
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_revparse_single(&target, g_repo, "a38d028f71eaa590febb7d716b1ca32350cf70da"));
+ cl_git_pass(git_reset(g_repo, target, GIT_RESET_HARD, NULL));
+
+ cl_must_pass(p_chmod("testrepo/branch_file.txt", 0666));
+
+ /*
+ * Checkout should not fail with a conflict; though the file mode
+ * on disk is literally different to the base (0666 vs 0644), Git
+ * ignores the actual mode and simply treats both as non-executable.
+ */
+ cl_git_pass(git_revparse_single(&branch, g_repo, "099fabac3a9ea935598528c27f866e34089c2eff"));
+
+ opts.checkout_strategy &= ~GIT_CHECKOUT_FORCE;
+ opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
+ cl_git_pass(git_checkout_tree(g_repo, branch, NULL));
+
+ git_object_free(branch);
+ git_object_free(target);
+}
+
+void test_checkout_head__obeys_filemode_true(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_object *target, *branch;
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ /* In this commit, `README` is executable */
+ cl_git_pass(git_revparse_single(&target, g_repo, "f9ed4af42472941da45a3c"));
+ cl_git_pass(git_reset(g_repo, target, GIT_RESET_HARD, NULL));
+
+ cl_repo_set_bool(g_repo, "core.filemode", true);
+ cl_must_pass(p_chmod("testrepo/README", 0644));
+
+ /*
+ * Checkout will fail with a conflict; the file mode is updated in
+ * the checkout target, but the contents have changed in our branch.
+ */
+ cl_git_pass(git_revparse_single(&branch, g_repo, "099fabac3a9ea935598528c27f866e34089c2eff"));
+
+ opts.checkout_strategy &= ~GIT_CHECKOUT_FORCE;
+ opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
+ cl_git_fail_with(GIT_ECONFLICT, git_checkout_tree(g_repo, branch, NULL));
+
+ git_object_free(branch);
+ git_object_free(target);
+}
+
+void test_checkout_head__obeys_filemode_false(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_object *target, *branch;
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ /* In this commit, `README` is executable */
+ cl_git_pass(git_revparse_single(&target, g_repo, "f9ed4af42472941da45a3c"));
+ cl_git_pass(git_reset(g_repo, target, GIT_RESET_HARD, NULL));
+
+ cl_repo_set_bool(g_repo, "core.filemode", false);
+ cl_must_pass(p_chmod("testrepo/README", 0644));
+
+ /*
+ * Checkout will fail with a conflict; the file contents are updated
+ * in the checkout target, but the filemode has changed in our branch.
+ */
+ cl_git_pass(git_revparse_single(&branch, g_repo, "099fabac3a9ea935598528c27f866e34089c2eff"));
+
+ opts.checkout_strategy &= ~GIT_CHECKOUT_FORCE;
+ opts.checkout_strategy |= GIT_CHECKOUT_SAFE;
+ cl_git_pass(git_checkout_tree(g_repo, branch, NULL));
+
+ git_object_free(branch);
+ git_object_free(target);
+}
diff --git a/tests/libgit2/checkout/icase.c b/tests/libgit2/checkout/icase.c
new file mode 100644
index 0000000..d77c7ab
--- /dev/null
+++ b/tests/libgit2/checkout/icase.c
@@ -0,0 +1,292 @@
+#include "clar_libgit2.h"
+
+#include "git2/checkout.h"
+#include "refs.h"
+#include "path.h"
+#include "repository.h"
+
+#ifdef GIT_WIN32
+# include <windows.h>
+#else
+# include <dirent.h>
+#endif
+
+static git_repository *repo;
+static git_object *obj;
+static git_checkout_options checkout_opts;
+
+void test_checkout_icase__initialize(void)
+{
+ git_oid id;
+ git_config *cfg;
+ int icase = 0;
+
+ repo = cl_git_sandbox_init("testrepo");
+
+ cl_git_pass(git_repository_config_snapshot(&cfg, repo));
+ git_config_get_bool(&icase, cfg, "core.ignorecase");
+ git_config_free(cfg);
+
+ if (!icase)
+ cl_skip();
+
+ cl_git_pass(git_reference_name_to_id(&id, repo, "refs/heads/dir"));
+ cl_git_pass(git_object_lookup(&obj, repo, &id, GIT_OBJECT_ANY));
+
+ git_checkout_options_init(&checkout_opts, GIT_CHECKOUT_OPTIONS_VERSION);
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_NONE;
+}
+
+void test_checkout_icase__cleanup(void)
+{
+ git_object_free(obj);
+ cl_git_sandbox_cleanup();
+}
+
+static char *get_filename(const char *in)
+{
+ char *search_dirname, *search_filename, *filename = NULL;
+ git_str out = GIT_STR_INIT;
+ DIR *dir;
+ struct dirent *de;
+
+ cl_assert(search_dirname = git_fs_path_dirname(in));
+ cl_assert(search_filename = git_fs_path_basename(in));
+
+ cl_assert(dir = opendir(search_dirname));
+
+ while ((de = readdir(dir))) {
+ if (strcasecmp(de->d_name, search_filename) == 0) {
+ git_str_join(&out, '/', search_dirname, de->d_name);
+ filename = git_str_detach(&out);
+ break;
+ }
+ }
+
+ closedir(dir);
+
+ git__free(search_dirname);
+ git__free(search_filename);
+ git_str_dispose(&out);
+
+ return filename;
+}
+
+static void assert_name_is(const char *expected)
+{
+ char *actual;
+ size_t actual_len, expected_len, start;
+
+ cl_assert(actual = get_filename(expected));
+
+ expected_len = strlen(expected);
+ actual_len = strlen(actual);
+ cl_assert(actual_len >= expected_len);
+
+ start = actual_len - expected_len;
+ cl_assert_equal_s(expected, actual + start);
+
+ if (start)
+ cl_assert_equal_strn("/", actual + (start - 1), 1);
+
+ free(actual);
+}
+
+static int symlink_or_fake(git_repository *repo, const char *a, const char *b)
+{
+ int symlinks;
+
+ cl_git_pass(git_repository__configmap_lookup(&symlinks, repo, GIT_CONFIGMAP_SYMLINKS));
+
+ if (symlinks)
+ return p_symlink(a, b);
+ else
+ return git_futils_fake_symlink(a, b);
+}
+
+void test_checkout_icase__refuses_to_overwrite_files_for_files(void)
+{
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE|GIT_CHECKOUT_RECREATE_MISSING;
+
+ cl_git_write2file("testrepo/BRANCH_FILE.txt", "neue file\n", 10, \
+ O_WRONLY | O_CREAT | O_TRUNC, 0644);
+
+ cl_git_fail(git_checkout_tree(repo, obj, &checkout_opts));
+ assert_name_is("testrepo/BRANCH_FILE.txt");
+}
+
+void test_checkout_icase__overwrites_files_for_files_when_forced(void)
+{
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_write2file("testrepo/NEW.txt", "neue file\n", 10, \
+ O_WRONLY | O_CREAT | O_TRUNC, 0644);
+
+ cl_git_pass(git_checkout_tree(repo, obj, &checkout_opts));
+ assert_name_is("testrepo/new.txt");
+}
+
+void test_checkout_icase__refuses_to_overwrite_links_for_files(void)
+{
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE|GIT_CHECKOUT_RECREATE_MISSING;
+
+ cl_must_pass(symlink_or_fake(repo, "../tmp", "testrepo/BRANCH_FILE.txt"));
+
+ cl_git_fail(git_checkout_tree(repo, obj, &checkout_opts));
+
+ cl_assert(!git_fs_path_exists("tmp"));
+ assert_name_is("testrepo/BRANCH_FILE.txt");
+}
+
+void test_checkout_icase__overwrites_links_for_files_when_forced(void)
+{
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_must_pass(symlink_or_fake(repo, "../tmp", "testrepo/NEW.txt"));
+
+ cl_git_pass(git_checkout_tree(repo, obj, &checkout_opts));
+
+ cl_assert(!git_fs_path_exists("tmp"));
+ assert_name_is("testrepo/new.txt");
+}
+
+void test_checkout_icase__overwrites_empty_folders_for_files(void)
+{
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE|GIT_CHECKOUT_RECREATE_MISSING;
+
+ cl_must_pass(p_mkdir("testrepo/NEW.txt", 0777));
+
+ cl_git_pass(git_checkout_tree(repo, obj, &checkout_opts));
+
+ assert_name_is("testrepo/new.txt");
+ cl_assert(!git_fs_path_isdir("testrepo/new.txt"));
+}
+
+void test_checkout_icase__refuses_to_overwrite_populated_folders_for_files(void)
+{
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE|GIT_CHECKOUT_RECREATE_MISSING;
+
+ cl_must_pass(p_mkdir("testrepo/BRANCH_FILE.txt", 0777));
+ cl_git_write2file("testrepo/BRANCH_FILE.txt/foobar", "neue file\n", 10, \
+ O_WRONLY | O_CREAT | O_TRUNC, 0644);
+
+ cl_git_fail(git_checkout_tree(repo, obj, &checkout_opts));
+
+ assert_name_is("testrepo/BRANCH_FILE.txt");
+ cl_assert(git_fs_path_isdir("testrepo/BRANCH_FILE.txt"));
+}
+
+void test_checkout_icase__overwrites_folders_for_files_when_forced(void)
+{
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_must_pass(p_mkdir("testrepo/NEW.txt", 0777));
+ cl_git_write2file("testrepo/NEW.txt/foobar", "neue file\n", 10, \
+ O_WRONLY | O_CREAT | O_TRUNC, 0644);
+
+ cl_git_pass(git_checkout_tree(repo, obj, &checkout_opts));
+
+ assert_name_is("testrepo/new.txt");
+ cl_assert(!git_fs_path_isdir("testrepo/new.txt"));
+}
+
+void test_checkout_icase__refuses_to_overwrite_files_for_folders(void)
+{
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE|GIT_CHECKOUT_RECREATE_MISSING;
+
+ cl_git_write2file("testrepo/A", "neue file\n", 10, \
+ O_WRONLY | O_CREAT | O_TRUNC, 0644);
+
+ cl_git_fail(git_checkout_tree(repo, obj, &checkout_opts));
+ assert_name_is("testrepo/A");
+ cl_assert(!git_fs_path_isdir("testrepo/A"));
+}
+
+void test_checkout_icase__overwrites_files_for_folders_when_forced(void)
+{
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_write2file("testrepo/A", "neue file\n", 10, \
+ O_WRONLY | O_CREAT | O_TRUNC, 0644);
+
+ cl_git_pass(git_checkout_tree(repo, obj, &checkout_opts));
+ assert_name_is("testrepo/a");
+ cl_assert(git_fs_path_isdir("testrepo/a"));
+}
+
+void test_checkout_icase__refuses_to_overwrite_links_for_folders(void)
+{
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE|GIT_CHECKOUT_RECREATE_MISSING;
+
+ cl_must_pass(symlink_or_fake(repo, "..", "testrepo/A"));
+
+ cl_git_fail(git_checkout_tree(repo, obj, &checkout_opts));
+
+ cl_assert(!git_fs_path_exists("b.txt"));
+ assert_name_is("testrepo/A");
+}
+
+void test_checkout_icase__overwrites_links_for_folders_when_forced(void)
+{
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_must_pass(symlink_or_fake(repo, "..", "testrepo/A"));
+
+ cl_git_pass(git_checkout_tree(repo, obj, &checkout_opts));
+
+ cl_assert(!git_fs_path_exists("b.txt"));
+ assert_name_is("testrepo/a");
+}
+
+void test_checkout_icase__ignores_unstaged_casechange(void)
+{
+ git_reference *orig_ref, *br2_ref;
+ git_commit *orig, *br2;
+ git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+
+ cl_git_pass(git_reference_lookup_resolved(&orig_ref, repo, "HEAD", 100));
+ cl_git_pass(git_commit_lookup(&orig, repo, git_reference_target(orig_ref)));
+ cl_git_pass(git_reset(repo, (git_object *)orig, GIT_RESET_HARD, NULL));
+
+ cl_rename("testrepo/branch_file.txt", "testrepo/Branch_File.txt");
+
+ cl_git_pass(git_reference_lookup_resolved(&br2_ref, repo, "refs/heads/br2", 100));
+ cl_git_pass(git_commit_lookup(&br2, repo, git_reference_target(br2_ref)));
+
+ cl_git_pass(git_checkout_tree(repo, (const git_object *)br2, &checkout_opts));
+
+ git_commit_free(orig);
+ git_commit_free(br2);
+ git_reference_free(orig_ref);
+ git_reference_free(br2_ref);
+}
+
+void test_checkout_icase__conflicts_with_casechanged_subtrees(void)
+{
+ git_reference *orig_ref;
+ git_object *orig, *subtrees;
+ git_oid oid;
+ git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+
+ cl_git_pass(git_reference_lookup_resolved(&orig_ref, repo, "HEAD", 100));
+ cl_git_pass(git_object_lookup(&orig, repo, git_reference_target(orig_ref), GIT_OBJECT_COMMIT));
+ cl_git_pass(git_reset(repo, (git_object *)orig, GIT_RESET_HARD, NULL));
+
+ cl_must_pass(p_mkdir("testrepo/AB", 0777));
+ cl_must_pass(p_mkdir("testrepo/AB/C", 0777));
+ cl_git_write2file("testrepo/AB/C/3.txt", "Foobar!\n", 8, O_RDWR|O_CREAT, 0666);
+
+ cl_git_pass(git_reference_name_to_id(&oid, repo, "refs/heads/subtrees"));
+ cl_git_pass(git_object_lookup(&subtrees, repo, &oid, GIT_OBJECT_ANY));
+
+ cl_git_fail(git_checkout_tree(repo, subtrees, &checkout_opts));
+
+ git_object_free(orig);
+ git_object_free(subtrees);
+ git_reference_free(orig_ref);
+}
+
diff --git a/tests/libgit2/checkout/index.c b/tests/libgit2/checkout/index.c
new file mode 100644
index 0000000..3dfdaa6
--- /dev/null
+++ b/tests/libgit2/checkout/index.c
@@ -0,0 +1,882 @@
+#include "clar_libgit2.h"
+#include "checkout_helpers.h"
+
+#include "git2/checkout.h"
+#include "futils.h"
+#include "repository.h"
+#include "index.h"
+#include "remote.h"
+#include "repo/repo_helpers.h"
+
+static git_repository *g_repo;
+static git_str g_global_path = GIT_STR_INIT;
+
+void test_checkout_index__initialize(void)
+{
+ git_tree *tree;
+
+ g_repo = cl_git_sandbox_init("testrepo");
+
+ cl_git_pass(git_repository_head_tree(&tree, g_repo));
+
+ reset_index_to_treeish((git_object *)tree);
+ git_tree_free(tree);
+
+ cl_git_rewritefile(
+ "./testrepo/.gitattributes",
+ "* text eol=lf\n");
+
+ git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL,
+ &g_global_path);
+}
+
+void test_checkout_index__cleanup(void)
+{
+ git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL,
+ g_global_path.ptr);
+ git_str_dispose(&g_global_path);
+
+ cl_git_sandbox_cleanup();
+
+ /* try to remove directories created by tests */
+ cl_fixture_cleanup("alternative");
+ cl_fixture_cleanup("symlink");
+ cl_fixture_cleanup("symlink.git");
+ cl_fixture_cleanup("tmp_global_path");
+}
+
+void test_checkout_index__cannot_checkout_a_bare_repository(void)
+{
+ cl_git_sandbox_cleanup();
+ g_repo = cl_git_sandbox_init("testrepo.git");
+
+ cl_git_fail(git_checkout_index(g_repo, NULL, NULL));
+}
+
+void test_checkout_index__can_create_missing_files(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ cl_assert_equal_i(false, git_fs_path_isfile("./testrepo/README"));
+ cl_assert_equal_i(false, git_fs_path_isfile("./testrepo/branch_file.txt"));
+ cl_assert_equal_i(false, git_fs_path_isfile("./testrepo/new.txt"));
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ check_file_contents("./testrepo/README", "hey there\n");
+ check_file_contents("./testrepo/branch_file.txt", "hi\nbye!\n");
+ check_file_contents("./testrepo/new.txt", "my new file\n");
+}
+
+void test_checkout_index__can_remove_untracked_files(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ git_futils_mkdir("./testrepo/dir/subdir/subsubdir", 0755, GIT_MKDIR_PATH);
+ cl_git_mkfile("./testrepo/dir/one", "one\n");
+ cl_git_mkfile("./testrepo/dir/subdir/two", "two\n");
+
+ cl_assert_equal_i(true, git_fs_path_isdir("./testrepo/dir/subdir/subsubdir"));
+
+ opts.checkout_strategy =
+ GIT_CHECKOUT_SAFE |
+ GIT_CHECKOUT_RECREATE_MISSING |
+ GIT_CHECKOUT_REMOVE_UNTRACKED;
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ cl_assert_equal_i(false, git_fs_path_isdir("./testrepo/dir"));
+}
+
+void test_checkout_index__can_disable_pathspec_match(void)
+{
+ char *files_to_checkout[] = { "test10.txt", "test11.txt"};
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_object *objects;
+ git_index *index;
+
+ /* reset to beginning of history (i.e. just a README file) */
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE | GIT_CHECKOUT_REMOVE_UNTRACKED;
+
+ cl_git_pass(git_revparse_single(&objects, g_repo, "8496071c1b46c854b31185ea97743be6a8774479"));
+ cl_git_pass(git_checkout_tree(g_repo, objects, &opts));
+ cl_git_pass(git_repository_set_head_detached(g_repo, git_object_id(objects)));
+ git_object_free(objects);
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ /* We create 4 files and commit them */
+ cl_git_mkfile("testrepo/test9.txt", "original\n");
+ cl_git_mkfile("testrepo/test10.txt", "original\n");
+ cl_git_mkfile("testrepo/test11.txt", "original\n");
+ cl_git_mkfile("testrepo/test12.txt", "original\n");
+
+ cl_git_pass(git_index_add_bypath(index, "test9.txt"));
+ cl_git_pass(git_index_add_bypath(index, "test10.txt"));
+ cl_git_pass(git_index_add_bypath(index, "test11.txt"));
+ cl_git_pass(git_index_add_bypath(index, "test12.txt"));
+ cl_git_pass(git_index_write(index));
+
+ cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "commit our test files");
+
+ /* We modify the content of all 4 of our files */
+ cl_git_rewritefile("testrepo/test9.txt", "modified\n");
+ cl_git_rewritefile("testrepo/test10.txt", "modified\n");
+ cl_git_rewritefile("testrepo/test11.txt", "modified\n");
+ cl_git_rewritefile("testrepo/test12.txt", "modified\n");
+
+ /* We checkout only test10.txt and test11.txt */
+ opts.checkout_strategy =
+ GIT_CHECKOUT_FORCE |
+ GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH;
+ opts.paths.strings = files_to_checkout;
+ opts.paths.count = ARRAY_SIZE(files_to_checkout);
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ /* The only files that have been reverted to their original content
+ should be test10.txt and test11.txt */
+ check_file_contents("testrepo/test9.txt", "modified\n");
+ check_file_contents("testrepo/test10.txt", "original\n");
+ check_file_contents("testrepo/test11.txt", "original\n");
+ check_file_contents("testrepo/test12.txt", "modified\n");
+
+ git_index_free(index);
+}
+
+void test_checkout_index__honor_the_specified_pathspecs(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ char *entries[] = { "*.txt" };
+
+ opts.paths.strings = entries;
+ opts.paths.count = 1;
+
+ cl_assert_equal_i(false, git_fs_path_isfile("./testrepo/README"));
+ cl_assert_equal_i(false, git_fs_path_isfile("./testrepo/branch_file.txt"));
+ cl_assert_equal_i(false, git_fs_path_isfile("./testrepo/new.txt"));
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ cl_assert_equal_i(false, git_fs_path_isfile("./testrepo/README"));
+ check_file_contents("./testrepo/branch_file.txt", "hi\nbye!\n");
+ check_file_contents("./testrepo/new.txt", "my new file\n");
+}
+
+void test_checkout_index__honor_the_gitattributes_directives(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ const char *attributes =
+ "branch_file.txt text eol=crlf\n"
+ "new.txt text eol=lf\n";
+
+ cl_git_mkfile("./testrepo/.gitattributes", attributes);
+ cl_repo_set_bool(g_repo, "core.autocrlf", false);
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ check_file_contents("./testrepo/README", "hey there\n");
+ check_file_contents("./testrepo/new.txt", "my new file\n");
+ check_file_contents("./testrepo/branch_file.txt", "hi\r\nbye!\r\n");
+}
+
+void test_checkout_index__honor_coreautocrlf_setting_set_to_true(void)
+{
+#ifdef GIT_WIN32
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ const char *expected_readme_text = "hey there\r\n";
+
+ cl_git_pass(p_unlink("./testrepo/.gitattributes"));
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ check_file_contents("./testrepo/README", expected_readme_text);
+#endif
+}
+
+static void populate_symlink_workdir(void)
+{
+ git_str path = GIT_STR_INIT;
+ git_repository *repo;
+ git_remote *origin;
+ git_object *target;
+
+ const char *url = git_repository_path(g_repo);
+
+ cl_git_pass(git_str_joinpath(&path, clar_sandbox_path(), "symlink.git"));
+ cl_git_pass(git_repository_init(&repo, path.ptr, true));
+ cl_git_pass(git_repository_set_workdir(repo, "symlink", 1));
+
+ /* Delete the `origin` repo (if it exists) so we can recreate it. */
+ git_remote_delete(repo, GIT_REMOTE_ORIGIN);
+
+ cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url));
+ cl_git_pass(git_remote_fetch(origin, NULL, NULL, NULL));
+ git_remote_free(origin);
+
+ cl_git_pass(git_revparse_single(&target, repo, "remotes/origin/master"));
+ cl_git_pass(git_reset(repo, target, GIT_RESET_HARD, NULL));
+
+ git_object_free(target);
+ git_repository_free(repo);
+ git_str_dispose(&path);
+}
+
+void test_checkout_index__honor_coresymlinks_default_true(void)
+{
+ char link_data[GIT_PATH_MAX];
+ int link_size = GIT_PATH_MAX;
+
+ cl_must_pass(p_mkdir("symlink", 0777));
+
+ if (!git_fs_path_supports_symlinks("symlink/test"))
+ cl_skip();
+
+#ifdef GIT_WIN32
+ /*
+ * Windows explicitly requires the global configuration to have
+ * core.symlinks=true in addition to actual filesystem support.
+ */
+ create_tmp_global_config("tmp_global_path", "core.symlinks", "true");
+#endif
+
+ populate_symlink_workdir();
+
+ link_size = p_readlink("./symlink/link_to_new.txt", link_data, link_size);
+ cl_assert(link_size >= 0);
+
+ link_data[link_size] = '\0';
+ cl_assert_equal_i(link_size, strlen("new.txt"));
+ cl_assert_equal_s(link_data, "new.txt");
+ check_file_contents("./symlink/link_to_new.txt", "my new file\n");
+}
+
+void test_checkout_index__honor_coresymlinks_default_false(void)
+{
+ cl_must_pass(p_mkdir("symlink", 0777));
+
+#ifndef GIT_WIN32
+ /*
+ * This test is largely for Windows platforms to ensure that
+ * we respect an unset core.symlinks even when the platform
+ * supports symlinks. Bail entirely on POSIX platforms that
+ * do support symlinks.
+ */
+ if (git_fs_path_supports_symlinks("symlink/test"))
+ cl_skip();
+#endif
+
+ populate_symlink_workdir();
+ check_file_contents("./symlink/link_to_new.txt", "new.txt");
+}
+
+void test_checkout_index__coresymlinks_set_to_true_fails_when_unsupported(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ if (git_fs_path_supports_symlinks("testrepo/test")) {
+ cl_skip();
+ }
+
+ cl_repo_set_bool(g_repo, "core.symlinks", true);
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
+ cl_git_fail(git_checkout_index(g_repo, NULL, &opts));
+}
+
+void test_checkout_index__honor_coresymlinks_setting_set_to_true(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ char link_data[GIT_PATH_MAX];
+ size_t link_size = GIT_PATH_MAX;
+
+ if (!git_fs_path_supports_symlinks("testrepo/test")) {
+ cl_skip();
+ }
+
+ cl_repo_set_bool(g_repo, "core.symlinks", true);
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ link_size = p_readlink("./testrepo/link_to_new.txt", link_data, link_size);
+ link_data[link_size] = '\0';
+ cl_assert_equal_i(link_size, strlen("new.txt"));
+ cl_assert_equal_s(link_data, "new.txt");
+ check_file_contents("./testrepo/link_to_new.txt", "my new file\n");
+}
+
+void test_checkout_index__honor_coresymlinks_setting_set_to_false(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ cl_repo_set_bool(g_repo, "core.symlinks", false);
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ check_file_contents("./testrepo/link_to_new.txt", "new.txt");
+}
+
+void test_checkout_index__donot_overwrite_modified_file_by_default(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ cl_git_mkfile("./testrepo/new.txt", "This isn't what's stored!");
+
+ /* set this up to not return an error code on conflicts, but it
+ * still will not have permission to overwrite anything...
+ */
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_ALLOW_CONFLICTS;
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ check_file_contents("./testrepo/new.txt", "This isn't what's stored!");
+}
+
+void test_checkout_index__can_overwrite_modified_file(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ cl_git_mkfile("./testrepo/new.txt", "This isn't what's stored!");
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ check_file_contents("./testrepo/new.txt", "my new file\n");
+}
+
+void test_checkout_index__options_disable_filters(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ cl_git_mkfile("./testrepo/.gitattributes", "*.txt text eol=crlf\n");
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
+ opts.disable_filters = false;
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ check_file_contents("./testrepo/new.txt", "my new file\r\n");
+
+ p_unlink("./testrepo/new.txt");
+
+ opts.disable_filters = true;
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ check_file_contents("./testrepo/new.txt", "my new file\n");
+}
+
+void test_checkout_index__options_dir_modes(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ struct stat st;
+ git_oid oid;
+ git_commit *commit;
+ mode_t um;
+
+ if (!cl_is_chmod_supported())
+ return;
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &oid));
+
+ reset_index_to_treeish((git_object *)commit);
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
+ opts.dir_mode = 0701;
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ /* umask will influence actual directory creation mode */
+ (void)p_umask(um = p_umask(022));
+
+ cl_git_pass(p_stat("./testrepo/a", &st));
+ /* Haiku & Hurd use other mode bits, so we must mask them out */
+ cl_assert_equal_i_fmt(st.st_mode & (S_IFMT | 07777), (GIT_FILEMODE_TREE | 0701) & ~um, "%07o");
+
+ /* File-mode test, since we're on the 'dir' branch */
+ cl_git_pass(p_stat("./testrepo/a/b.txt", &st));
+ cl_assert_equal_i_fmt(st.st_mode & (S_IFMT | 07777), GIT_FILEMODE_BLOB_EXECUTABLE & ~um, "%07o");
+
+ git_commit_free(commit);
+}
+
+void test_checkout_index__options_override_file_modes(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ struct stat st;
+
+ if (!cl_is_chmod_supported())
+ return;
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
+ opts.file_mode = 0700;
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ cl_git_pass(p_stat("./testrepo/new.txt", &st));
+ cl_assert_equal_i_fmt(st.st_mode & GIT_MODE_PERMS_MASK, 0700, "%07o");
+}
+
+void test_checkout_index__options_open_flags(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ cl_git_mkfile("./testrepo/new.txt", "hi\n");
+
+ opts.checkout_strategy =
+ GIT_CHECKOUT_FORCE | GIT_CHECKOUT_DONT_REMOVE_EXISTING;
+ opts.file_open_flags = O_CREAT | O_RDWR | O_APPEND;
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ check_file_contents("./testrepo/new.txt", "hi\nmy new file\n");
+}
+
+struct notify_data {
+ const char *file;
+ const char *sha;
+};
+
+static int test_checkout_notify_cb(
+ git_checkout_notify_t why,
+ const char *path,
+ const git_diff_file *baseline,
+ const git_diff_file *target,
+ const git_diff_file *workdir,
+ void *payload)
+{
+ struct notify_data *expectations = (struct notify_data *)payload;
+
+ GIT_UNUSED(workdir);
+
+ cl_assert_equal_i(GIT_CHECKOUT_NOTIFY_CONFLICT, why);
+ cl_assert_equal_s(expectations->file, path);
+ cl_assert_equal_i(0, git_oid_streq(&baseline->id, expectations->sha));
+ cl_assert_equal_i(0, git_oid_streq(&target->id, expectations->sha));
+
+ return 0;
+}
+
+void test_checkout_index__can_notify_of_skipped_files(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ struct notify_data data;
+
+ cl_git_mkfile("./testrepo/new.txt", "This isn't what's stored!");
+
+ /*
+ * $ git ls-tree HEAD
+ * 100644 blob a8233120f6ad708f843d861ce2b7228ec4e3dec6 README
+ * 100644 blob 3697d64be941a53d4ae8f6a271e4e3fa56b022cc branch_file.txt
+ * 100644 blob a71586c1dfe8a71c6cbf6c129f404c5642ff31bd new.txt
+ */
+ data.file = "new.txt";
+ data.sha = "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd";
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE |
+ GIT_CHECKOUT_RECREATE_MISSING |
+ GIT_CHECKOUT_ALLOW_CONFLICTS;
+ opts.notify_flags = GIT_CHECKOUT_NOTIFY_CONFLICT;
+ opts.notify_cb = test_checkout_notify_cb;
+ opts.notify_payload = &data;
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+}
+
+static int dont_notify_cb(
+ git_checkout_notify_t why,
+ const char *path,
+ const git_diff_file *baseline,
+ const git_diff_file *target,
+ const git_diff_file *workdir,
+ void *payload)
+{
+ GIT_UNUSED(why);
+ GIT_UNUSED(path);
+ GIT_UNUSED(baseline);
+ GIT_UNUSED(target);
+ GIT_UNUSED(workdir);
+ GIT_UNUSED(payload);
+
+ cl_assert(false);
+
+ return 0;
+}
+
+void test_checkout_index__wont_notify_of_expected_line_ending_changes(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ cl_git_pass(p_unlink("./testrepo/.gitattributes"));
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+ cl_git_mkfile("./testrepo/new.txt", "my new file\r\n");
+
+ opts.checkout_strategy =
+ GIT_CHECKOUT_SAFE |
+ GIT_CHECKOUT_RECREATE_MISSING |
+ GIT_CHECKOUT_ALLOW_CONFLICTS;
+ opts.notify_flags = GIT_CHECKOUT_NOTIFY_CONFLICT;
+ opts.notify_cb = dont_notify_cb;
+ opts.notify_payload = NULL;
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+}
+
+static void checkout_progress_counter(
+ const char *path, size_t cur, size_t tot, void *payload)
+{
+ GIT_UNUSED(path); GIT_UNUSED(cur); GIT_UNUSED(tot);
+ (*(int *)payload)++;
+}
+
+void test_checkout_index__calls_progress_callback(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ int calls = 0;
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
+ opts.progress_cb = checkout_progress_counter;
+ opts.progress_payload = &calls;
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+ cl_assert(calls > 0);
+}
+
+void test_checkout_index__can_overcome_name_clashes(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_index *index;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ git_index_clear(index);
+
+ cl_git_mkfile("./testrepo/path0", "content\r\n");
+ cl_git_pass(p_mkdir("./testrepo/path1", 0777));
+ cl_git_mkfile("./testrepo/path1/file1", "content\r\n");
+
+ cl_git_pass(git_index_add_bypath(index, "path0"));
+ cl_git_pass(git_index_add_bypath(index, "path1/file1"));
+
+ cl_git_pass(p_unlink("./testrepo/path0"));
+ cl_git_pass(git_futils_rmdir_r(
+ "./testrepo/path1", NULL, GIT_RMDIR_REMOVE_FILES));
+
+ cl_git_mkfile("./testrepo/path1", "content\r\n");
+ cl_git_pass(p_mkdir("./testrepo/path0", 0777));
+ cl_git_mkfile("./testrepo/path0/file0", "content\r\n");
+
+ cl_assert(git_fs_path_isfile("./testrepo/path1"));
+ cl_assert(git_fs_path_isfile("./testrepo/path0/file0"));
+
+ opts.checkout_strategy =
+ GIT_CHECKOUT_SAFE |
+ GIT_CHECKOUT_RECREATE_MISSING |
+ GIT_CHECKOUT_ALLOW_CONFLICTS;
+ cl_git_pass(git_checkout_index(g_repo, index, &opts));
+
+ cl_assert(git_fs_path_isfile("./testrepo/path1"));
+ cl_assert(git_fs_path_isfile("./testrepo/path0/file0"));
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+ cl_git_pass(git_checkout_index(g_repo, index, &opts));
+
+ cl_assert(git_fs_path_isfile("./testrepo/path0"));
+ cl_assert(git_fs_path_isfile("./testrepo/path1/file1"));
+
+ git_index_free(index);
+}
+
+void test_checkout_index__validates_struct_version(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ const git_error *err;
+
+ opts.version = 1024;
+ cl_git_fail(git_checkout_index(g_repo, NULL, &opts));
+
+ err = git_error_last();
+ cl_assert_equal_i(err->klass, GIT_ERROR_INVALID);
+
+ opts.version = 0;
+ git_error_clear();
+ cl_git_fail(git_checkout_index(g_repo, NULL, &opts));
+
+ err = git_error_last();
+ cl_assert_equal_i(err->klass, GIT_ERROR_INVALID);
+}
+
+void test_checkout_index__can_update_prefixed_files(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ cl_assert_equal_i(false, git_fs_path_isfile("./testrepo/README"));
+ cl_assert_equal_i(false, git_fs_path_isfile("./testrepo/branch_file.txt"));
+ cl_assert_equal_i(false, git_fs_path_isfile("./testrepo/new.txt"));
+
+ cl_git_mkfile("./testrepo/READ", "content\n");
+ cl_git_mkfile("./testrepo/README.after", "content\n");
+ cl_git_pass(p_mkdir("./testrepo/branch_file", 0777));
+ cl_git_pass(p_mkdir("./testrepo/branch_file/contained_dir", 0777));
+ cl_git_mkfile("./testrepo/branch_file/contained_file", "content\n");
+ cl_git_pass(p_mkdir("./testrepo/branch_file.txt.after", 0777));
+
+ opts.checkout_strategy =
+ GIT_CHECKOUT_SAFE |
+ GIT_CHECKOUT_RECREATE_MISSING |
+ GIT_CHECKOUT_REMOVE_UNTRACKED;
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ /* remove untracked will remove the .gitattributes file before the blobs
+ * were created, so they will have had crlf filtering applied on Windows
+ */
+ check_file_contents_nocr("./testrepo/README", "hey there\n");
+ check_file_contents_nocr("./testrepo/branch_file.txt", "hi\nbye!\n");
+ check_file_contents_nocr("./testrepo/new.txt", "my new file\n");
+
+ cl_assert(!git_fs_path_exists("testrepo/READ"));
+ cl_assert(!git_fs_path_exists("testrepo/README.after"));
+ cl_assert(!git_fs_path_exists("testrepo/branch_file"));
+ cl_assert(!git_fs_path_exists("testrepo/branch_file.txt.after"));
+}
+
+void test_checkout_index__can_checkout_a_newly_initialized_repository(void)
+{
+ cl_git_sandbox_cleanup();
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_remove_placeholders(git_repository_path(g_repo), "dummy-marker.txt");
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, NULL));
+}
+
+void test_checkout_index__issue_1397(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ cl_git_sandbox_cleanup();
+ g_repo = cl_git_sandbox_init("issue_1397");
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ check_file_contents("./issue_1397/crlf_file.txt", "first line\r\nsecond line\r\nboth with crlf");
+}
+
+void test_checkout_index__target_directory(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ checkout_counts cts;
+ memset(&cts, 0, sizeof(cts));
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE |
+ GIT_CHECKOUT_RECREATE_MISSING;
+ opts.target_directory = "alternative";
+ cl_assert(!git_fs_path_isdir("alternative"));
+
+ opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
+ opts.notify_cb = checkout_count_callback;
+ opts.notify_payload = &cts;
+
+ /* create some files that *would* conflict if we were using the wd */
+ cl_git_mkfile("testrepo/README", "I'm in the way!\n");
+ cl_git_mkfile("testrepo/new.txt", "my new file\n");
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ cl_assert_equal_i(0, cts.n_untracked);
+ cl_assert_equal_i(0, cts.n_ignored);
+ cl_assert_equal_i(4, cts.n_updates);
+
+ check_file_contents("./alternative/README", "hey there\n");
+ check_file_contents("./alternative/branch_file.txt", "hi\nbye!\n");
+ check_file_contents("./alternative/new.txt", "my new file\n");
+
+ cl_git_pass(git_futils_rmdir_r(
+ "alternative", NULL, GIT_RMDIR_REMOVE_FILES));
+}
+
+void test_checkout_index__target_directory_from_bare(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_index *index;
+ git_object *head = NULL;
+ checkout_counts cts;
+ memset(&cts, 0, sizeof(cts));
+
+ cl_git_sandbox_cleanup();
+ g_repo = cl_git_sandbox_init("testrepo.git");
+ cl_assert(git_repository_is_bare(g_repo));
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_revparse_single(&head, g_repo, "HEAD^{tree}"));
+ cl_git_pass(git_index_read_tree(index, (const git_tree *)head));
+ cl_git_pass(git_index_write(index));
+ git_index_free(index);
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE |
+ GIT_CHECKOUT_RECREATE_MISSING;
+
+ opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
+ opts.notify_cb = checkout_count_callback;
+ opts.notify_payload = &cts;
+
+ /* fail to checkout a bare repo */
+ cl_git_fail(git_checkout_index(g_repo, NULL, &opts));
+
+ opts.target_directory = "alternative";
+ cl_assert(!git_fs_path_isdir("alternative"));
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ cl_assert_equal_i(0, cts.n_untracked);
+ cl_assert_equal_i(0, cts.n_ignored);
+ cl_assert_equal_i(3, cts.n_updates);
+
+ /* files will have been filtered if needed, so strip CR */
+ check_file_contents_nocr("./alternative/README", "hey there\n");
+ check_file_contents_nocr("./alternative/branch_file.txt", "hi\nbye!\n");
+ check_file_contents_nocr("./alternative/new.txt", "my new file\n");
+
+ cl_git_pass(git_futils_rmdir_r(
+ "alternative", NULL, GIT_RMDIR_REMOVE_FILES));
+
+ git_object_free(head);
+}
+
+void test_checkout_index__can_get_repo_from_index(void)
+{
+ git_index *index;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ cl_assert_equal_i(false, git_fs_path_isfile("./testrepo/README"));
+ cl_assert_equal_i(false, git_fs_path_isfile("./testrepo/branch_file.txt"));
+ cl_assert_equal_i(false, git_fs_path_isfile("./testrepo/new.txt"));
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_pass(git_checkout_index(NULL, index, &opts));
+
+ check_file_contents("./testrepo/README", "hey there\n");
+ check_file_contents("./testrepo/branch_file.txt", "hi\nbye!\n");
+ check_file_contents("./testrepo/new.txt", "my new file\n");
+
+ git_index_free(index);
+}
+
+static void add_conflict(git_index *index, const char *path)
+{
+ git_index_entry entry;
+
+ memset(&entry, 0, sizeof(git_index_entry));
+
+ entry.mode = 0100644;
+ entry.path = path;
+
+ git_oid__fromstr(&entry.id, "d427e0b2e138501a3d15cc376077a3631e15bd46", GIT_OID_SHA1);
+ GIT_INDEX_ENTRY_STAGE_SET(&entry, 1);
+ cl_git_pass(git_index_add(index, &entry));
+
+ git_oid__fromstr(&entry.id, "4e886e602529caa9ab11d71f86634bd1b6e0de10", GIT_OID_SHA1);
+ GIT_INDEX_ENTRY_STAGE_SET(&entry, 2);
+ cl_git_pass(git_index_add(index, &entry));
+
+ git_oid__fromstr(&entry.id, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", GIT_OID_SHA1);
+ GIT_INDEX_ENTRY_STAGE_SET(&entry, 3);
+ cl_git_pass(git_index_add(index, &entry));
+}
+
+void test_checkout_index__writes_conflict_file(void)
+{
+ git_index *index;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_str conflicting_buf = GIT_STR_INIT;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ add_conflict(index, "conflicting.txt");
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ cl_git_pass(git_futils_readbuffer(&conflicting_buf, "testrepo/conflicting.txt"));
+ cl_assert(strcmp(conflicting_buf.ptr,
+ "<<<<<<< ours\n"
+ "this file is changed in master and branch\n"
+ "=======\n"
+ "this file is changed in branch and master\n"
+ ">>>>>>> theirs\n") == 0);
+ git_str_dispose(&conflicting_buf);
+
+ git_index_free(index);
+}
+
+void test_checkout_index__adding_conflict_removes_stage_0(void)
+{
+ git_index *new_index, *index;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ cl_git_pass(git_index__new(&new_index, GIT_OID_SHA1));
+
+ add_conflict(new_index, "new.txt");
+ cl_git_pass(git_checkout_index(g_repo, new_index, &opts));
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_assert(git_index_get_bypath(index, "new.txt", 0) == NULL);
+ cl_assert(git_index_get_bypath(index, "new.txt", 1) != NULL);
+ cl_assert(git_index_get_bypath(index, "new.txt", 2) != NULL);
+ cl_assert(git_index_get_bypath(index, "new.txt", 3) != NULL);
+
+ git_index_free(index);
+ git_index_free(new_index);
+}
+
+void test_checkout_index__conflicts_honor_coreautocrlf(void)
+{
+#ifdef GIT_WIN32
+ git_index *index;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_str conflicting_buf = GIT_STR_INIT;
+
+ cl_git_pass(p_unlink("./testrepo/.gitattributes"));
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ add_conflict(index, "conflicting.txt");
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
+
+ cl_git_pass(git_futils_readbuffer(&conflicting_buf, "testrepo/conflicting.txt"));
+ cl_assert(strcmp(conflicting_buf.ptr,
+ "<<<<<<< ours\r\n"
+ "this file is changed in master and branch\r\n"
+ "=======\r\n"
+ "this file is changed in branch and master\r\n"
+ ">>>>>>> theirs\r\n") == 0);
+ git_str_dispose(&conflicting_buf);
+
+ git_index_free(index);
+#endif
+}
diff --git a/tests/libgit2/checkout/nasty.c b/tests/libgit2/checkout/nasty.c
new file mode 100644
index 0000000..732f1d5
--- /dev/null
+++ b/tests/libgit2/checkout/nasty.c
@@ -0,0 +1,386 @@
+#include "clar_libgit2.h"
+#include "checkout_helpers.h"
+
+#include "git2/checkout.h"
+#include "repository.h"
+#include "futils.h"
+
+static const char *repo_name = "nasty";
+static git_repository *repo;
+static git_checkout_options checkout_opts;
+
+void test_checkout_nasty__initialize(void)
+{
+ repo = cl_git_sandbox_init(repo_name);
+
+ GIT_INIT_STRUCTURE(&checkout_opts, GIT_CHECKOUT_OPTIONS_VERSION);
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+}
+
+void test_checkout_nasty__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static void test_checkout_passes(const char *refname, const char *filename)
+{
+ git_oid commit_id;
+ git_commit *commit;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_str path = GIT_STR_INIT;
+
+ cl_git_pass(git_str_joinpath(&path, repo_name, filename));
+
+ cl_git_pass(git_reference_name_to_id(&commit_id, repo, refname));
+ cl_git_pass(git_commit_lookup(&commit, repo, &commit_id));
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE |
+ GIT_CHECKOUT_DONT_UPDATE_INDEX;
+
+ cl_git_pass(git_checkout_tree(repo, (const git_object *)commit, &opts));
+ cl_assert(!git_fs_path_exists(path.ptr));
+
+ git_commit_free(commit);
+ git_str_dispose(&path);
+}
+
+static void test_checkout_fails(const char *refname, const char *filename)
+{
+ git_oid commit_id;
+ git_commit *commit;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_str path = GIT_STR_INIT;
+
+ cl_git_pass(git_str_joinpath(&path, repo_name, filename));
+
+ cl_git_pass(git_reference_name_to_id(&commit_id, repo, refname));
+ cl_git_pass(git_commit_lookup(&commit, repo, &commit_id));
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_fail(git_checkout_tree(repo, (const git_object *)commit, &opts));
+ cl_assert(!git_fs_path_exists(path.ptr));
+
+ git_commit_free(commit);
+ git_str_dispose(&path);
+}
+
+/* A tree that contains ".git" as a tree, with a blob inside
+ * (".git/foobar").
+ */
+void test_checkout_nasty__dotgit_tree(void)
+{
+ test_checkout_fails("refs/heads/dotgit_tree", ".git/foobar");
+}
+
+/* A tree that contains ".GIT" as a tree, with a blob inside
+ * (".GIT/foobar").
+ */
+void test_checkout_nasty__dotcapitalgit_tree(void)
+{
+ test_checkout_fails("refs/heads/dotcapitalgit_tree", ".GIT/foobar");
+}
+
+/* A tree that contains a tree ".", with a blob inside ("./foobar").
+ */
+void test_checkout_nasty__dot_tree(void)
+{
+ test_checkout_fails("refs/heads/dot_tree", "foobar");
+}
+
+/* A tree that contains a tree ".", with a tree ".git", with a blob
+ * inside ("./.git/foobar").
+ */
+void test_checkout_nasty__dot_dotgit_tree(void)
+{
+ test_checkout_fails("refs/heads/dot_dotgit_tree", ".git/foobar");
+}
+
+/* A tree that contains a tree, with a tree "..", with a tree ".git", with a
+ * blob inside ("foo/../.git/foobar").
+ */
+void test_checkout_nasty__dotdot_dotgit_tree(void)
+{
+ test_checkout_fails("refs/heads/dotdot_dotgit_tree", ".git/foobar");
+}
+
+/* A tree that contains a tree, with a tree "..", with a blob inside
+ * ("foo/../foobar").
+ */
+void test_checkout_nasty__dotdot_tree(void)
+{
+ test_checkout_fails("refs/heads/dotdot_tree", "foobar");
+}
+
+/* A tree that contains a blob with the rogue name ".git/foobar" */
+void test_checkout_nasty__dotgit_path(void)
+{
+ test_checkout_fails("refs/heads/dotgit_path", ".git/foobar");
+}
+
+/* A tree that contains a blob with the rogue name ".GIT/foobar" */
+void test_checkout_nasty__dotcapitalgit_path(void)
+{
+ test_checkout_fails("refs/heads/dotcapitalgit_path", ".GIT/foobar");
+}
+
+/* A tree that contains a blob with the rogue name "./.git/foobar" */
+void test_checkout_nasty__dot_dotgit_path(void)
+{
+ test_checkout_fails("refs/heads/dot_dotgit_path", ".git/foobar");
+}
+
+/* A tree that contains a blob with the rogue name "./.GIT/foobar" */
+void test_checkout_nasty__dot_dotcapitalgit_path(void)
+{
+ test_checkout_fails("refs/heads/dot_dotcapitalgit_path", ".GIT/foobar");
+}
+
+/* A tree that contains a blob with the rogue name "foo/../.git/foobar" */
+void test_checkout_nasty__dotdot_dotgit_path(void)
+{
+ test_checkout_fails("refs/heads/dotdot_dotgit_path", ".git/foobar");
+}
+
+/* A tree that contains a blob with the rogue name "foo/../.GIT/foobar" */
+void test_checkout_nasty__dotdot_dotcapitalgit_path(void)
+{
+ test_checkout_fails("refs/heads/dotdot_dotcapitalgit_path", ".GIT/foobar");
+}
+
+/* A tree that contains a blob with the rogue name "foo/." */
+void test_checkout_nasty__dot_path(void)
+{
+ test_checkout_fails("refs/heads/dot_path", "./foobar");
+}
+
+/* A tree that contains a blob with the rogue name "foo/." */
+void test_checkout_nasty__dot_path_two(void)
+{
+ test_checkout_fails("refs/heads/dot_path_two", "foo/.");
+}
+
+/* A tree that contains a blob with the rogue name "foo/../foobar" */
+void test_checkout_nasty__dotdot_path(void)
+{
+ test_checkout_fails("refs/heads/dotdot_path", "foobar");
+}
+
+/* A tree that contains an entry with a backslash ".git\foobar" */
+void test_checkout_nasty__dotgit_backslash_path(void)
+{
+#ifdef GIT_WIN32
+ test_checkout_fails("refs/heads/dotgit_backslash_path", ".git/foobar");
+#endif
+}
+
+/* A tree that contains an entry with a backslash ".GIT\foobar" */
+void test_checkout_nasty__dotcapitalgit_backslash_path(void)
+{
+#ifdef GIT_WIN32
+ test_checkout_fails("refs/heads/dotcapitalgit_backslash_path", ".GIT/foobar");
+#endif
+}
+
+/* A tree that contains an entry with a backslash ".\.GIT\foobar" */
+void test_checkout_nasty__dot_backslash_dotcapitalgit_path(void)
+{
+#ifdef GIT_WIN32
+ test_checkout_fails("refs/heads/dot_backslash_dotcapitalgit_path", ".GIT/foobar");
+#endif
+}
+
+/* A tree that contains an entry ".git.", because Win32 APIs will drop the
+ * trailing slash.
+ */
+void test_checkout_nasty__dot_git_dot(void)
+{
+#ifdef GIT_WIN32
+ test_checkout_fails("refs/heads/dot_git_dot", ".git/foobar");
+#endif
+}
+
+/* A tree that contains an entry "git~1", because that is typically the
+ * short name for ".git".
+ */
+void test_checkout_nasty__git_tilde1(void)
+{
+ test_checkout_fails("refs/heads/git_tilde1", ".git/foobar");
+ test_checkout_fails("refs/heads/git_tilde1", "git~1/foobar");
+}
+
+/* A tree that contains an entry "git~2", when we have forced the short
+ * name for ".git" into "GIT~2".
+ */
+void test_checkout_nasty__git_custom_shortname(void)
+{
+#ifdef GIT_WIN32
+ if (!cl_sandbox_supports_8dot3())
+ clar__skip();
+
+ cl_must_pass(p_rename("nasty/.git", "nasty/_temp"));
+ cl_git_write2file("nasty/git~1", "", 0, O_RDWR|O_CREAT, 0666);
+ cl_must_pass(p_rename("nasty/_temp", "nasty/.git"));
+ test_checkout_fails("refs/heads/git_tilde2", ".git/foobar");
+#endif
+}
+
+/* A tree that contains an entry "git~3", which should be allowed, since
+ * it is not the typical short name ("GIT~1") or the actual short name
+ * ("GIT~2") for ".git".
+ */
+void test_checkout_nasty__only_looks_like_a_git_shortname(void)
+{
+#ifdef GIT_WIN32
+ git_oid commit_id;
+ git_commit *commit;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ cl_must_pass(p_rename("nasty/.git", "nasty/_temp"));
+ cl_git_write2file("nasty/git~1", "", 0, O_RDWR|O_CREAT, 0666);
+ cl_must_pass(p_rename("nasty/_temp", "nasty/.git"));
+
+ cl_git_pass(git_reference_name_to_id(&commit_id, repo, "refs/heads/git_tilde3"));
+ cl_git_pass(git_commit_lookup(&commit, repo, &commit_id));
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_checkout_tree(repo, (const git_object *)commit, &opts));
+ cl_assert(git_fs_path_exists("nasty/git~3/foobar"));
+
+ git_commit_free(commit);
+#endif
+}
+
+/* A tree that contains an entry "git:", because Win32 APIs will reject
+ * that as looking too similar to a drive letter.
+ */
+void test_checkout_nasty__dot_git_colon(void)
+{
+#ifdef GIT_WIN32
+ test_checkout_fails("refs/heads/dot_git_colon", ".git/foobar");
+#endif
+}
+
+/* A tree that contains an entry "git:foo", because Win32 APIs will turn
+ * that into ".git".
+ */
+void test_checkout_nasty__dot_git_colon_stuff(void)
+{
+#ifdef GIT_WIN32
+ test_checkout_fails("refs/heads/dot_git_colon_stuff", ".git/foobar");
+#endif
+}
+
+/* A tree that contains an entry ".git::$INDEX_ALLOCATION" because NTFS
+ * will interpret that as a synonym to ".git", even when mounted via SMB
+ * on macOS.
+ */
+void test_checkout_nasty__dotgit_alternate_data_stream(void)
+{
+ test_checkout_fails("refs/heads/dotgit_alternate_data_stream", ".git/dummy-file");
+ test_checkout_fails("refs/heads/dotgit_alternate_data_stream", ".git::$INDEX_ALLOCATION/dummy-file");
+}
+
+/* Trees that contains entries with a tree ".git" that contain
+ * byte sequences:
+ * { 0xe2, 0x80, 0x8c }
+ * { 0xe2, 0x80, 0x8d }
+ * { 0xe2, 0x80, 0x8e }
+ * { 0xe2, 0x80, 0x8f }
+ * { 0xe2, 0x80, 0xaa }
+ * { 0xe2, 0x80, 0xab }
+ * { 0xe2, 0x80, 0xac }
+ * { 0xe2, 0x80, 0xad }
+ * { 0xe2, 0x81, 0xae }
+ * { 0xe2, 0x81, 0xaa }
+ * { 0xe2, 0x81, 0xab }
+ * { 0xe2, 0x81, 0xac }
+ * { 0xe2, 0x81, 0xad }
+ * { 0xe2, 0x81, 0xae }
+ * { 0xe2, 0x81, 0xaf }
+ * { 0xef, 0xbb, 0xbf }
+ * Because these map to characters that HFS filesystems "ignore". Thus
+ * ".git<U+200C>" will map to ".git".
+ */
+void test_checkout_nasty__dot_git_hfs_ignorable(void)
+{
+#ifdef __APPLE__
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_1", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_2", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_3", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_4", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_5", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_6", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_7", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_8", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_9", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_10", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_11", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_12", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_13", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_14", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_15", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_16", ".git/foobar");
+#endif
+}
+
+void test_checkout_nasty__honors_core_protecthfs(void)
+{
+ cl_repo_set_bool(repo, "core.protectHFS", true);
+
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_1", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_2", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_3", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_4", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_5", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_6", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_7", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_8", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_9", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_10", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_11", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_12", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_13", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_14", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_15", ".git/foobar");
+ test_checkout_fails("refs/heads/dotgit_hfs_ignorable_16", ".git/foobar");
+}
+
+void test_checkout_nasty__honors_core_protectntfs(void)
+{
+ cl_repo_set_bool(repo, "core.protectNTFS", true);
+
+ test_checkout_fails("refs/heads/dotgit_backslash_path", ".git/foobar");
+ test_checkout_fails("refs/heads/dotcapitalgit_backslash_path", ".GIT/foobar");
+ test_checkout_fails("refs/heads/dot_git_dot", ".git/foobar");
+ test_checkout_fails("refs/heads/git_tilde1", ".git/foobar");
+}
+
+void test_checkout_nasty__symlink1(void)
+{
+ test_checkout_passes("refs/heads/symlink1", ".git/foobar");
+}
+
+void test_checkout_nasty__symlink2(void)
+{
+ test_checkout_passes("refs/heads/symlink2", ".git/foobar");
+}
+
+void test_checkout_nasty__symlink3(void)
+{
+ test_checkout_passes("refs/heads/symlink3", ".git/foobar");
+}
+
+void test_checkout_nasty__gitmodules_symlink(void)
+{
+ cl_repo_set_bool(repo, "core.protectHFS", true);
+ test_checkout_fails("refs/heads/gitmodules-symlink", ".gitmodules");
+ cl_repo_set_bool(repo, "core.protectHFS", false);
+
+ cl_repo_set_bool(repo, "core.protectNTFS", true);
+ test_checkout_fails("refs/heads/gitmodules-symlink", ".gitmodules");
+ cl_repo_set_bool(repo, "core.protectNTFS", false);
+
+ test_checkout_fails("refs/heads/gitmodules-symlink", ".gitmodules");
+}
diff --git a/tests/libgit2/checkout/tree.c b/tests/libgit2/checkout/tree.c
new file mode 100644
index 0000000..65df00c
--- /dev/null
+++ b/tests/libgit2/checkout/tree.c
@@ -0,0 +1,1685 @@
+#include "clar_libgit2.h"
+#include "checkout_helpers.h"
+
+#include "git2/checkout.h"
+#include "repository.h"
+#include "futils.h"
+
+static git_repository *g_repo;
+static git_checkout_options g_opts;
+static git_object *g_object;
+
+static void assert_status_entrycount(git_repository *repo, size_t count)
+{
+ git_status_list *status;
+
+ cl_git_pass(git_status_list_new(&status, repo, NULL));
+ cl_assert_equal_i(count, git_status_list_entrycount(status));
+
+ git_status_list_free(status);
+}
+
+void test_checkout_tree__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+
+ GIT_INIT_STRUCTURE(&g_opts, GIT_CHECKOUT_OPTIONS_VERSION);
+ g_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+}
+
+void test_checkout_tree__cleanup(void)
+{
+ git_object_free(g_object);
+ g_object = NULL;
+
+ cl_git_sandbox_cleanup();
+
+ if (git_fs_path_isdir("alternative"))
+ git_futils_rmdir_r("alternative", NULL, GIT_RMDIR_REMOVE_FILES);
+}
+
+void test_checkout_tree__cannot_checkout_a_non_treeish(void)
+{
+ /* blob */
+ cl_git_pass(git_revparse_single(&g_object, g_repo, "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd"));
+ cl_git_fail(git_checkout_tree(g_repo, g_object, NULL));
+}
+
+void test_checkout_tree__can_checkout_a_subdirectory_from_a_commit(void)
+{
+ char *entries[] = { "ab/de/" };
+
+ g_opts.paths.strings = entries;
+ g_opts.paths.count = 1;
+
+ cl_git_pass(git_revparse_single(&g_object, g_repo, "subtrees"));
+
+ cl_assert_equal_i(false, git_fs_path_isdir("./testrepo/ab/"));
+
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+
+ cl_assert_equal_i(true, git_fs_path_isfile("./testrepo/ab/de/2.txt"));
+ cl_assert_equal_i(true, git_fs_path_isfile("./testrepo/ab/de/fgh/1.txt"));
+}
+
+void test_checkout_tree__can_checkout_and_remove_directory(void)
+{
+ cl_assert_equal_i(false, git_fs_path_isdir("./testrepo/ab/"));
+
+ /* Checkout branch "subtrees" and update HEAD, so that HEAD matches the
+ * current working tree
+ */
+ cl_git_pass(git_revparse_single(&g_object, g_repo, "subtrees"));
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/subtrees"));
+
+ cl_assert_equal_i(true, git_fs_path_isdir("./testrepo/ab/"));
+ cl_assert_equal_i(true, git_fs_path_isfile("./testrepo/ab/de/2.txt"));
+ cl_assert_equal_i(true, git_fs_path_isfile("./testrepo/ab/de/fgh/1.txt"));
+
+ git_object_free(g_object);
+ g_object = NULL;
+
+ /* Checkout branch "master" and update HEAD, so that HEAD matches the
+ * current working tree
+ */
+ cl_git_pass(git_revparse_single(&g_object, g_repo, "master"));
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/master"));
+
+ /* This directory should no longer exist */
+ cl_assert_equal_i(false, git_fs_path_isdir("./testrepo/ab/"));
+}
+
+void test_checkout_tree__can_checkout_a_subdirectory_from_a_subtree(void)
+{
+ char *entries[] = { "de/" };
+
+ g_opts.paths.strings = entries;
+ g_opts.paths.count = 1;
+
+ cl_git_pass(git_revparse_single(&g_object, g_repo, "subtrees:ab"));
+
+ cl_assert_equal_i(false, git_fs_path_isdir("./testrepo/de/"));
+
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+
+ cl_assert_equal_i(true, git_fs_path_isfile("./testrepo/de/2.txt"));
+ cl_assert_equal_i(true, git_fs_path_isfile("./testrepo/de/fgh/1.txt"));
+}
+
+static void progress(const char *path, size_t cur, size_t tot, void *payload)
+{
+ bool *was_called = (bool*)payload;
+ GIT_UNUSED(path); GIT_UNUSED(cur); GIT_UNUSED(tot);
+ *was_called = true;
+}
+
+void test_checkout_tree__calls_progress_callback(void)
+{
+ bool was_called = 0;
+
+ g_opts.progress_cb = progress;
+ g_opts.progress_payload = &was_called;
+
+ cl_git_pass(git_revparse_single(&g_object, g_repo, "master"));
+
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+
+ cl_assert_equal_i(was_called, true);
+}
+
+void test_checkout_tree__doesnt_write_unrequested_files_to_worktree(void)
+{
+ git_oid master_oid;
+ git_oid chomped_oid;
+ git_commit* p_master_commit;
+ git_commit* p_chomped_commit;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ git_oid__fromstr(&master_oid, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", GIT_OID_SHA1);
+ git_oid__fromstr(&chomped_oid, "e90810b8df3e80c413d903f631643c716887138d", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&p_master_commit, g_repo, &master_oid));
+ cl_git_pass(git_commit_lookup(&p_chomped_commit, g_repo, &chomped_oid));
+
+ /* GIT_CHECKOUT_NONE should not add any file to the working tree from the
+ * index as it is supposed to be a dry run.
+ */
+ opts.checkout_strategy = GIT_CHECKOUT_NONE;
+ git_checkout_tree(g_repo, (git_object*)p_chomped_commit, &opts);
+ cl_assert_equal_i(false, git_fs_path_isfile("testrepo/readme.txt"));
+
+ git_commit_free(p_master_commit);
+ git_commit_free(p_chomped_commit);
+}
+
+void test_checkout_tree__can_switch_branches(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_oid oid;
+ git_object *obj = NULL;
+
+ assert_on_branch(g_repo, "master");
+
+ /* do first checkout with FORCE because we don't know if testrepo
+ * base data is clean for a checkout or not
+ */
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
+
+ cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir"));
+
+ cl_assert(git_fs_path_isfile("testrepo/README"));
+ cl_assert(git_fs_path_isfile("testrepo/branch_file.txt"));
+ cl_assert(git_fs_path_isfile("testrepo/new.txt"));
+ cl_assert(git_fs_path_isfile("testrepo/a/b.txt"));
+
+ cl_assert(!git_fs_path_isdir("testrepo/ab"));
+
+ assert_on_branch(g_repo, "dir");
+
+ git_object_free(obj);
+
+ /* do second checkout safe because we should be clean after first */
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/subtrees"));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
+
+ cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/subtrees"));
+
+ cl_assert(git_fs_path_isfile("testrepo/README"));
+ cl_assert(git_fs_path_isfile("testrepo/branch_file.txt"));
+ cl_assert(git_fs_path_isfile("testrepo/new.txt"));
+ cl_assert(git_fs_path_isfile("testrepo/ab/4.txt"));
+ cl_assert(git_fs_path_isfile("testrepo/ab/c/3.txt"));
+ cl_assert(git_fs_path_isfile("testrepo/ab/de/2.txt"));
+ cl_assert(git_fs_path_isfile("testrepo/ab/de/fgh/1.txt"));
+
+ cl_assert(!git_fs_path_isdir("testrepo/a"));
+
+ assert_on_branch(g_repo, "subtrees");
+
+ git_object_free(obj);
+}
+
+void test_checkout_tree__can_remove_untracked(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_REMOVE_UNTRACKED;
+
+ cl_git_mkfile("testrepo/untracked_file", "as you wish");
+ cl_assert(git_fs_path_isfile("testrepo/untracked_file"));
+
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+
+ cl_assert(!git_fs_path_isfile("testrepo/untracked_file"));
+}
+
+void test_checkout_tree__can_remove_ignored(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ int ignored = 0;
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_REMOVE_IGNORED;
+
+ cl_git_mkfile("testrepo/ignored_file", "as you wish");
+
+ cl_git_pass(git_ignore_add_rule(g_repo, "ignored_file\n"));
+
+ cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "ignored_file"));
+ cl_assert_equal_i(1, ignored);
+
+ cl_assert(git_fs_path_isfile("testrepo/ignored_file"));
+
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+
+ cl_assert(!git_fs_path_isfile("testrepo/ignored_file"));
+}
+
+static int checkout_tree_with_blob_ignored_in_workdir(int strategy, bool isdir)
+{
+ git_oid oid;
+ git_object *obj = NULL;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ int ignored = 0, error;
+
+ assert_on_branch(g_repo, "master");
+
+ /* do first checkout with FORCE because we don't know if testrepo
+ * base data is clean for a checkout or not
+ */
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
+
+ cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir"));
+
+ cl_assert(git_fs_path_isfile("testrepo/README"));
+ cl_assert(git_fs_path_isfile("testrepo/branch_file.txt"));
+ cl_assert(git_fs_path_isfile("testrepo/new.txt"));
+ cl_assert(git_fs_path_isfile("testrepo/a/b.txt"));
+
+ cl_assert(!git_fs_path_isdir("testrepo/ab"));
+
+ assert_on_branch(g_repo, "dir");
+
+ git_object_free(obj);
+
+ opts.checkout_strategy = strategy;
+
+ if (isdir) {
+ cl_must_pass(p_mkdir("testrepo/ab", 0777));
+ cl_must_pass(p_mkdir("testrepo/ab/4.txt", 0777));
+
+ cl_git_mkfile("testrepo/ab/4.txt/file1.txt", "as you wish");
+ cl_git_mkfile("testrepo/ab/4.txt/file2.txt", "foo bar foo");
+ cl_git_mkfile("testrepo/ab/4.txt/file3.txt", "inky blinky pinky clyde");
+
+ cl_assert(git_fs_path_isdir("testrepo/ab/4.txt"));
+ } else {
+ cl_must_pass(p_mkdir("testrepo/ab", 0777));
+ cl_git_mkfile("testrepo/ab/4.txt", "as you wish");
+
+ cl_assert(git_fs_path_isfile("testrepo/ab/4.txt"));
+ }
+
+ cl_git_pass(git_ignore_add_rule(g_repo, "ab/4.txt\n"));
+
+ cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "ab/4.txt"));
+ cl_assert_equal_i(1, ignored);
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/subtrees"));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
+
+ error = git_checkout_tree(g_repo, obj, &opts);
+
+ git_object_free(obj);
+
+ return error;
+}
+
+void test_checkout_tree__conflict_on_ignored_when_not_overwriting(void)
+{
+ int error;
+
+ cl_git_fail(error = checkout_tree_with_blob_ignored_in_workdir(
+ GIT_CHECKOUT_SAFE | GIT_CHECKOUT_DONT_OVERWRITE_IGNORED, false));
+
+ cl_assert_equal_i(GIT_ECONFLICT, error);
+}
+
+void test_checkout_tree__can_overwrite_ignored_by_default(void)
+{
+ cl_git_pass(checkout_tree_with_blob_ignored_in_workdir(GIT_CHECKOUT_SAFE, false));
+
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/subtrees"));
+
+ cl_assert(git_fs_path_isfile("testrepo/ab/4.txt"));
+
+ assert_on_branch(g_repo, "subtrees");
+}
+
+void test_checkout_tree__conflict_on_ignored_folder_when_not_overwriting(void)
+{
+ int error;
+
+ cl_git_fail(error = checkout_tree_with_blob_ignored_in_workdir(
+ GIT_CHECKOUT_SAFE | GIT_CHECKOUT_DONT_OVERWRITE_IGNORED, true));
+
+ cl_assert_equal_i(GIT_ECONFLICT, error);
+}
+
+void test_checkout_tree__can_overwrite_ignored_folder_by_default(void)
+{
+ cl_git_pass(checkout_tree_with_blob_ignored_in_workdir(GIT_CHECKOUT_SAFE, true));
+
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/subtrees"));
+
+ cl_assert(git_fs_path_isfile("testrepo/ab/4.txt"));
+
+ assert_on_branch(g_repo, "subtrees");
+
+}
+
+void test_checkout_tree__can_update_only(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_oid oid;
+ git_object *obj = NULL;
+
+ /* first let's get things into a known state - by checkout out the HEAD */
+
+ assert_on_branch(g_repo, "master");
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+
+ cl_assert(!git_fs_path_isdir("testrepo/a"));
+
+ check_file_contents_nocr("testrepo/branch_file.txt", "hi\nbye!\n");
+
+ /* now checkout branch but with update only */
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_ONLY;
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
+
+ cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir"));
+
+ assert_on_branch(g_repo, "dir");
+
+ /* this normally would have been created (which was tested separately in
+ * the test_checkout_tree__can_switch_branches test), but with
+ * UPDATE_ONLY it will not have been created.
+ */
+ cl_assert(!git_fs_path_isdir("testrepo/a"));
+
+ /* but this file still should have been updated */
+ check_file_contents_nocr("testrepo/branch_file.txt", "hi\n");
+
+ git_object_free(obj);
+}
+
+void test_checkout_tree__can_checkout_with_pattern(void)
+{
+ char *entries[] = { "[l-z]*.txt" };
+
+ /* reset to beginning of history (i.e. just a README file) */
+
+ g_opts.checkout_strategy =
+ GIT_CHECKOUT_FORCE | GIT_CHECKOUT_REMOVE_UNTRACKED;
+
+ cl_git_pass(git_revparse_single(&g_object, g_repo, "8496071c1b46c854b31185ea97743be6a8774479"));
+
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+ cl_git_pass(
+ git_repository_set_head_detached(g_repo, git_object_id(g_object)));
+
+ git_object_free(g_object);
+ g_object = NULL;
+
+ cl_assert(git_fs_path_exists("testrepo/README"));
+ cl_assert(!git_fs_path_exists("testrepo/branch_file.txt"));
+ cl_assert(!git_fs_path_exists("testrepo/link_to_new.txt"));
+ cl_assert(!git_fs_path_exists("testrepo/new.txt"));
+
+ /* now to a narrow patterned checkout */
+
+ g_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+ g_opts.paths.strings = entries;
+ g_opts.paths.count = 1;
+
+ cl_git_pass(git_revparse_single(&g_object, g_repo, "refs/heads/master"));
+
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+
+ cl_assert(git_fs_path_exists("testrepo/README"));
+ cl_assert(!git_fs_path_exists("testrepo/branch_file.txt"));
+ cl_assert(git_fs_path_exists("testrepo/link_to_new.txt"));
+ cl_assert(git_fs_path_exists("testrepo/new.txt"));
+}
+
+void test_checkout_tree__pathlist_checkout_ignores_non_matches(void)
+{
+ char *entries[] = { "branch_file.txt", "link_to_new.txt" };
+
+ /* reset to beginning of history (i.e. just a README file) */
+
+ g_opts.checkout_strategy =
+ GIT_CHECKOUT_FORCE | GIT_CHECKOUT_REMOVE_UNTRACKED;
+
+ cl_git_pass(git_revparse_single(&g_object, g_repo, "refs/heads/master"));
+
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/master"));
+
+ cl_assert(git_fs_path_exists("testrepo/README"));
+ cl_assert(git_fs_path_exists("testrepo/branch_file.txt"));
+ cl_assert(git_fs_path_exists("testrepo/link_to_new.txt"));
+ cl_assert(git_fs_path_exists("testrepo/new.txt"));
+
+ git_object_free(g_object);
+ cl_git_pass(git_revparse_single(&g_object, g_repo, "8496071c1b46c854b31185ea97743be6a8774479"));
+
+ g_opts.checkout_strategy =
+ GIT_CHECKOUT_FORCE | GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH;
+ g_opts.paths.strings = entries;
+ g_opts.paths.count = 2;
+
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+
+ cl_assert(git_fs_path_exists("testrepo/README"));
+ cl_assert(!git_fs_path_exists("testrepo/branch_file.txt"));
+ cl_assert(!git_fs_path_exists("testrepo/link_to_new.txt"));
+ cl_assert(git_fs_path_exists("testrepo/new.txt"));
+}
+
+void test_checkout_tree__can_disable_pattern_match(void)
+{
+ char *entries[] = { "b*.txt" };
+
+ /* reset to beginning of history (i.e. just a README file) */
+
+ g_opts.checkout_strategy =
+ GIT_CHECKOUT_FORCE | GIT_CHECKOUT_REMOVE_UNTRACKED;
+
+ cl_git_pass(git_revparse_single(&g_object, g_repo, "8496071c1b46c854b31185ea97743be6a8774479"));
+
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+ cl_git_pass(
+ git_repository_set_head_detached(g_repo, git_object_id(g_object)));
+
+ git_object_free(g_object);
+ g_object = NULL;
+
+ cl_assert(!git_fs_path_isfile("testrepo/branch_file.txt"));
+
+ /* now to a narrow patterned checkout, but disable pattern */
+
+ g_opts.checkout_strategy =
+ GIT_CHECKOUT_SAFE |
+ GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH;
+ g_opts.paths.strings = entries;
+ g_opts.paths.count = 1;
+
+ cl_git_pass(git_revparse_single(&g_object, g_repo, "refs/heads/master"));
+
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+
+ cl_assert(!git_fs_path_isfile("testrepo/branch_file.txt"));
+
+ /* let's try that again, but allow the pattern match */
+
+ g_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+
+ cl_assert(git_fs_path_isfile("testrepo/branch_file.txt"));
+}
+
+static void assert_conflict(
+ const char *entry_path,
+ const char *new_content,
+ const char *parent_sha,
+ const char *commit_sha)
+{
+ git_index *index;
+ git_object *hack_tree;
+ git_reference *branch, *head;
+ git_str file_path = GIT_STR_INIT;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ /* Create a branch pointing at the parent */
+ cl_git_pass(git_revparse_single(&g_object, g_repo, parent_sha));
+ cl_git_pass(git_branch_create(&branch, g_repo,
+ "potential_conflict", (git_commit *)g_object, 0));
+
+ /* Make HEAD point to this branch */
+ cl_git_pass(git_reference_symbolic_create(
+ &head, g_repo, "HEAD", git_reference_name(branch), 1, NULL));
+ git_reference_free(head);
+ git_reference_free(branch);
+
+ /* Checkout the parent */
+ g_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+
+ /* Hack-ishy workaround to ensure *all* the index entries
+ * match the content of the tree
+ */
+ cl_git_pass(git_object_peel(&hack_tree, g_object, GIT_OBJECT_TREE));
+ cl_git_pass(git_index_read_tree(index, (git_tree *)hack_tree));
+ cl_git_pass(git_index_write(index));
+ git_object_free(hack_tree);
+ git_object_free(g_object);
+ g_object = NULL;
+
+ /* Create a conflicting file */
+ cl_git_pass(git_str_joinpath(&file_path, "./testrepo", entry_path));
+ cl_git_mkfile(git_str_cstr(&file_path), new_content);
+ git_str_dispose(&file_path);
+
+ /* Trying to checkout the original commit */
+ cl_git_pass(git_revparse_single(&g_object, g_repo, commit_sha));
+
+ g_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+ cl_assert_equal_i(
+ GIT_ECONFLICT, git_checkout_tree(g_repo, g_object, &g_opts));
+
+ /* Stage the conflicting change */
+ cl_git_pass(git_index_add_bypath(index, entry_path));
+ cl_git_pass(git_index_write(index));
+ git_index_free(index);
+
+ cl_assert_equal_i(
+ GIT_ECONFLICT, git_checkout_tree(g_repo, g_object, &g_opts));
+}
+
+void test_checkout_tree__checking_out_a_conflicting_type_change_returns_ECONFLICT(void)
+{
+ /*
+ * 099faba adds a symlink named 'link_to_new.txt'
+ * a65fedf is the parent of 099faba
+ */
+
+ assert_conflict("link_to_new.txt", "old.txt", "a65fedf", "099faba");
+}
+
+void test_checkout_tree__checking_out_a_conflicting_type_change_returns_ECONFLICT_2(void)
+{
+ /*
+ * cf80f8d adds a directory named 'a/'
+ * a4a7dce is the parent of cf80f8d
+ */
+
+ assert_conflict("a", "hello\n", "a4a7dce", "cf80f8d");
+}
+
+void test_checkout_tree__checking_out_a_conflicting_content_change_returns_ECONFLICT(void)
+{
+ /*
+ * c47800c adds a symlink named 'branch_file.txt'
+ * 5b5b025 is the parent of 763d71a
+ */
+
+ assert_conflict("branch_file.txt", "hello\n", "5b5b025", "c47800c");
+}
+
+void test_checkout_tree__donot_update_deleted_file_by_default(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_oid old_id, new_id;
+ git_commit *old_commit = NULL, *new_commit = NULL;
+ git_index *index = NULL;
+ checkout_counts ct;
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+
+ memset(&ct, 0, sizeof(ct));
+ opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
+ opts.notify_cb = checkout_count_callback;
+ opts.notify_payload = &ct;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_pass(git_oid__fromstr(&old_id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644", GIT_OID_SHA1));
+ cl_git_pass(git_commit_lookup(&old_commit, g_repo, &old_id));
+ cl_git_pass(git_reset(g_repo, (git_object *)old_commit, GIT_RESET_HARD, NULL));
+
+ cl_git_pass(p_unlink("testrepo/branch_file.txt"));
+ cl_git_pass(git_index_remove_bypath(index ,"branch_file.txt"));
+ cl_git_pass(git_index_write(index));
+
+ cl_assert(!git_fs_path_exists("testrepo/branch_file.txt"));
+
+ cl_git_pass(git_oid__fromstr(&new_id, "099fabac3a9ea935598528c27f866e34089c2eff", GIT_OID_SHA1));
+ cl_git_pass(git_commit_lookup(&new_commit, g_repo, &new_id));
+
+
+ cl_git_fail(git_checkout_tree(g_repo, (git_object *)new_commit, &opts));
+
+ cl_assert_equal_i(1, ct.n_conflicts);
+ cl_assert_equal_i(1, ct.n_updates);
+
+ git_commit_free(old_commit);
+ git_commit_free(new_commit);
+ git_index_free(index);
+}
+
+struct checkout_cancel_at {
+ const char *filename;
+ int error;
+ int count;
+};
+
+static int checkout_cancel_cb(
+ git_checkout_notify_t why,
+ const char *path,
+ const git_diff_file *b,
+ const git_diff_file *t,
+ const git_diff_file *w,
+ void *payload)
+{
+ struct checkout_cancel_at *ca = payload;
+
+ GIT_UNUSED(why); GIT_UNUSED(b); GIT_UNUSED(t); GIT_UNUSED(w);
+
+ ca->count++;
+
+ if (!strcmp(path, ca->filename))
+ return ca->error;
+
+ return 0;
+}
+
+void test_checkout_tree__can_cancel_checkout_from_notify(void)
+{
+ struct checkout_cancel_at ca;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_oid oid;
+ git_object *obj = NULL;
+
+ assert_on_branch(g_repo, "master");
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
+
+ ca.filename = "new.txt";
+ ca.error = -5555;
+ ca.count = 0;
+
+ opts.notify_flags = GIT_CHECKOUT_NOTIFY_UPDATED;
+ opts.notify_cb = checkout_cancel_cb;
+ opts.notify_payload = &ca;
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_assert(!git_fs_path_exists("testrepo/new.txt"));
+
+ cl_git_fail_with(git_checkout_tree(g_repo, obj, &opts), -5555);
+
+ cl_assert(!git_fs_path_exists("testrepo/new.txt"));
+
+ /* on case-insensitive FS = a/b.txt, branch_file.txt, new.txt */
+ /* on case-sensitive FS = README, then above */
+
+ if (git_fs_path_exists("testrepo/.git/CoNfIg")) /* case insensitive */
+ cl_assert_equal_i(3, ca.count);
+ else
+ cl_assert_equal_i(4, ca.count);
+
+ /* and again with a different stopping point and return code */
+ ca.filename = "README";
+ ca.error = 123;
+ ca.count = 0;
+
+ cl_git_fail_with(git_checkout_tree(g_repo, obj, &opts), 123);
+
+ cl_assert(!git_fs_path_exists("testrepo/new.txt"));
+
+ if (git_fs_path_exists("testrepo/.git/CoNfIg")) /* case insensitive */
+ cl_assert_equal_i(4, ca.count);
+ else
+ cl_assert_equal_i(1, ca.count);
+
+ git_object_free(obj);
+}
+
+void test_checkout_tree__can_checkout_with_last_workdir_item_missing(void)
+{
+ git_index *index = NULL;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_oid tree_id, commit_id;
+ git_tree *tree = NULL;
+ git_commit *commit = NULL;
+
+ git_repository_index(&index, g_repo);
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_reference_name_to_id(&commit_id, g_repo, "refs/heads/master"));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &commit_id));
+
+ cl_git_pass(git_checkout_tree(g_repo, (git_object *)commit, &opts));
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/master"));
+
+ cl_git_pass(p_mkdir("./testrepo/this-is-dir", 0777));
+ cl_git_mkfile("./testrepo/this-is-dir/contained_file", "content\n");
+
+ cl_git_pass(git_index_add_bypath(index, "this-is-dir/contained_file"));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_index_write_tree(&tree_id, index));
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
+
+ cl_git_pass(p_unlink("./testrepo/this-is-dir/contained_file"));
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+
+ opts.checkout_strategy = 1;
+ git_checkout_tree(g_repo, (git_object *)tree, &opts);
+
+ git_tree_free(tree);
+ git_commit_free(commit);
+ git_index_free(index);
+}
+
+void test_checkout_tree__issue_1397(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ const char *partial_oid = "8a7ef04";
+ git_object *tree = NULL;
+
+ test_checkout_tree__cleanup(); /* cleanup default checkout */
+
+ g_repo = cl_git_sandbox_init("issue_1397");
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+ cl_git_pass(git_revparse_single(&tree, g_repo, partial_oid));
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_checkout_tree(g_repo, tree, &opts));
+
+ check_file_contents("./issue_1397/crlf_file.txt", "first line\r\nsecond line\r\nboth with crlf");
+
+ git_object_free(tree);
+}
+
+void test_checkout_tree__can_write_to_empty_dirs(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_oid oid;
+ git_object *obj = NULL;
+
+ assert_on_branch(g_repo, "master");
+
+ cl_git_pass(p_mkdir("testrepo/a", 0777));
+
+ /* do first checkout with FORCE because we don't know if testrepo
+ * base data is clean for a checkout or not
+ */
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
+
+ cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
+
+ cl_assert(git_fs_path_isfile("testrepo/a/b.txt"));
+
+ git_object_free(obj);
+}
+
+void test_checkout_tree__fails_when_dir_in_use(void)
+{
+#ifdef GIT_WIN32
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_oid oid;
+ git_object *obj = NULL;
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
+
+ cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
+
+ cl_assert(git_fs_path_isfile("testrepo/a/b.txt"));
+
+ git_object_free(obj);
+
+ cl_git_pass(p_chdir("testrepo/a"));
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/master"));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
+
+ cl_git_fail(git_checkout_tree(g_repo, obj, &opts));
+
+ cl_git_pass(p_chdir("../.."));
+
+ cl_assert(git_fs_path_is_empty_dir("testrepo/a"));
+
+ git_object_free(obj);
+#endif
+}
+
+void test_checkout_tree__can_continue_when_dir_in_use(void)
+{
+#ifdef GIT_WIN32
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_oid oid;
+ git_object *obj = NULL;
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE |
+ GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES;
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
+
+ cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
+
+ cl_assert(git_fs_path_isfile("testrepo/a/b.txt"));
+
+ git_object_free(obj);
+
+ cl_git_pass(p_chdir("testrepo/a"));
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/master"));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
+
+ cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
+
+ cl_git_pass(p_chdir("../.."));
+
+ cl_assert(git_fs_path_is_empty_dir("testrepo/a"));
+
+ git_object_free(obj);
+#endif
+}
+
+void test_checkout_tree__target_directory_from_bare(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_oid oid;
+ checkout_counts cts;
+ memset(&cts, 0, sizeof(cts));
+
+ test_checkout_tree__cleanup(); /* cleanup default checkout */
+
+ g_repo = cl_git_sandbox_init("testrepo.git");
+ cl_assert(git_repository_is_bare(g_repo));
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE |
+ GIT_CHECKOUT_RECREATE_MISSING;
+
+ opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
+ opts.notify_cb = checkout_count_callback;
+ opts.notify_payload = &cts;
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "HEAD"));
+ cl_git_pass(git_object_lookup(&g_object, g_repo, &oid, GIT_OBJECT_ANY));
+
+ cl_git_fail(git_checkout_tree(g_repo, g_object, &opts));
+
+ opts.target_directory = "alternative";
+ cl_assert(!git_fs_path_isdir("alternative"));
+
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &opts));
+
+ cl_assert_equal_i(0, cts.n_untracked);
+ cl_assert_equal_i(0, cts.n_ignored);
+ cl_assert_equal_i(3, cts.n_updates);
+
+ check_file_contents_nocr("./alternative/README", "hey there\n");
+ check_file_contents_nocr("./alternative/branch_file.txt", "hi\nbye!\n");
+ check_file_contents_nocr("./alternative/new.txt", "my new file\n");
+
+ cl_git_pass(git_futils_rmdir_r(
+ "alternative", NULL, GIT_RMDIR_REMOVE_FILES));
+}
+
+void test_checkout_tree__extremely_long_file_name(void)
+{
+ /* A utf-8 string with 83 characters, but 249 bytes. */
+ const char *longname = "\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97";
+ char path[1024] = {0};
+
+ g_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+ cl_git_pass(git_revparse_single(&g_object, g_repo, "long-file-name"));
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+
+ sprintf(path, "testrepo/%s.txt", longname);
+ cl_assert(git_fs_path_exists(path));
+
+ git_object_free(g_object);
+ cl_git_pass(git_revparse_single(&g_object, g_repo, "master"));
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
+ cl_assert(!git_fs_path_exists(path));
+}
+
+static void create_conflict(const char *path)
+{
+ git_index *index;
+ git_index_entry entry;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ memset(&entry, 0x0, sizeof(git_index_entry));
+ entry.mode = 0100644;
+ GIT_INDEX_ENTRY_STAGE_SET(&entry, 1);
+ git_oid__fromstr(&entry.id, "d427e0b2e138501a3d15cc376077a3631e15bd46", GIT_OID_SHA1);
+ entry.path = path;
+ cl_git_pass(git_index_add(index, &entry));
+
+ GIT_INDEX_ENTRY_STAGE_SET(&entry, 2);
+ git_oid__fromstr(&entry.id, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", GIT_OID_SHA1);
+ cl_git_pass(git_index_add(index, &entry));
+
+ GIT_INDEX_ENTRY_STAGE_SET(&entry, 3);
+ git_oid__fromstr(&entry.id, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", GIT_OID_SHA1);
+ cl_git_pass(git_index_add(index, &entry));
+
+ cl_git_pass(git_index_write(index));
+ git_index_free(index);
+}
+
+void test_checkout_tree__fails_when_conflicts_exist_in_index(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_oid oid;
+ git_object *obj = NULL;
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "HEAD"));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
+
+ create_conflict("conflicts.txt");
+
+ cl_git_fail(git_checkout_tree(g_repo, obj, &opts));
+
+ git_object_free(obj);
+}
+
+void test_checkout_tree__filemode_preserved_in_index(void)
+{
+ git_oid executable_oid;
+ git_commit *commit;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_index *index;
+ const git_index_entry *entry;
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ /* test a freshly added executable */
+ cl_git_pass(git_oid__fromstr(&executable_oid, "afe4393b2b2a965f06acf2ca9658eaa01e0cd6b6", GIT_OID_SHA1));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
+
+ cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
+ cl_assert(entry = git_index_get_bypath(index, "executable.txt", 0));
+ cl_assert(GIT_PERMS_IS_EXEC(entry->mode));
+
+ git_commit_free(commit);
+
+
+ /* Now start with a commit which has a text file */
+ cl_git_pass(git_oid__fromstr(&executable_oid, "cf80f8de9f1185bf3a05f993f6121880dd0cfbc9", GIT_OID_SHA1));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
+
+ cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
+ cl_assert(entry = git_index_get_bypath(index, "a/b.txt", 0));
+ cl_assert(!GIT_PERMS_IS_EXEC(entry->mode));
+
+ git_commit_free(commit);
+
+
+ /* And then check out to a commit which converts the text file to an executable */
+ cl_git_pass(git_oid__fromstr(&executable_oid, "144344043ba4d4a405da03de3844aa829ae8be0e", GIT_OID_SHA1));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
+
+ cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
+ cl_assert(entry = git_index_get_bypath(index, "a/b.txt", 0));
+ cl_assert(GIT_PERMS_IS_EXEC(entry->mode));
+
+ git_commit_free(commit);
+
+
+ /* Finally, check out the text file again and check that the exec bit is cleared */
+ cl_git_pass(git_oid__fromstr(&executable_oid, "cf80f8de9f1185bf3a05f993f6121880dd0cfbc9", GIT_OID_SHA1));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
+
+ cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
+ cl_assert(entry = git_index_get_bypath(index, "a/b.txt", 0));
+ cl_assert(!GIT_PERMS_IS_EXEC(entry->mode));
+
+ git_commit_free(commit);
+
+
+ git_index_free(index);
+}
+
+#ifndef GIT_WIN32
+static mode_t read_filemode(const char *path)
+{
+ git_str fullpath = GIT_STR_INIT;
+ struct stat st;
+ mode_t result;
+
+ git_str_joinpath(&fullpath, "testrepo", path);
+ cl_must_pass(p_stat(fullpath.ptr, &st));
+
+ result = GIT_PERMS_IS_EXEC(st.st_mode) ?
+ GIT_FILEMODE_BLOB_EXECUTABLE : GIT_FILEMODE_BLOB;
+
+ git_str_dispose(&fullpath);
+
+ return result;
+}
+#endif
+
+void test_checkout_tree__filemode_preserved_in_workdir(void)
+{
+#ifndef GIT_WIN32
+ git_oid executable_oid;
+ git_commit *commit;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ /* test a freshly added executable */
+ cl_git_pass(git_oid__fromstr(&executable_oid, "afe4393b2b2a965f06acf2ca9658eaa01e0cd6b6", GIT_OID_SHA1));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
+
+ cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
+ cl_assert(GIT_PERMS_IS_EXEC(read_filemode("executable.txt")));
+
+ git_commit_free(commit);
+
+
+ /* Now start with a commit which has a text file */
+ cl_git_pass(git_oid__fromstr(&executable_oid, "cf80f8de9f1185bf3a05f993f6121880dd0cfbc9", GIT_OID_SHA1));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
+
+ cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
+ cl_assert(!GIT_PERMS_IS_EXEC(read_filemode("a/b.txt")));
+
+ git_commit_free(commit);
+
+
+ /* And then check out to a commit which converts the text file to an executable */
+ cl_git_pass(git_oid__fromstr(&executable_oid, "144344043ba4d4a405da03de3844aa829ae8be0e", GIT_OID_SHA1));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
+
+ cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
+ cl_assert(GIT_PERMS_IS_EXEC(read_filemode("a/b.txt")));
+
+ git_commit_free(commit);
+
+
+ /* Finally, check out the text file again and check that the exec bit is cleared */
+ cl_git_pass(git_oid__fromstr(&executable_oid, "cf80f8de9f1185bf3a05f993f6121880dd0cfbc9", GIT_OID_SHA1));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
+
+ cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
+ cl_assert(!GIT_PERMS_IS_EXEC(read_filemode("a/b.txt")));
+
+ git_commit_free(commit);
+#else
+ cl_skip();
+#endif
+}
+
+void test_checkout_tree__removes_conflicts(void)
+{
+ git_oid commit_id;
+ git_commit *commit;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_index *index;
+
+ cl_git_pass(git_oid__fromstr(&commit_id, "afe4393b2b2a965f06acf2ca9658eaa01e0cd6b6", GIT_OID_SHA1));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &commit_id));
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_remove(index, "executable.txt", 0));
+
+ create_conflict("executable.txt");
+ cl_git_mkfile("testrepo/executable.txt", "This is the conflict file.\n");
+
+ create_conflict("other.txt");
+ cl_git_mkfile("testrepo/other.txt", "This is another conflict file.\n");
+
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
+
+ cl_assert_equal_p(NULL, git_index_get_bypath(index, "executable.txt", 1));
+ cl_assert_equal_p(NULL, git_index_get_bypath(index, "executable.txt", 2));
+ cl_assert_equal_p(NULL, git_index_get_bypath(index, "executable.txt", 3));
+
+ cl_assert_equal_p(NULL, git_index_get_bypath(index, "other.txt", 1));
+ cl_assert_equal_p(NULL, git_index_get_bypath(index, "other.txt", 2));
+ cl_assert_equal_p(NULL, git_index_get_bypath(index, "other.txt", 3));
+
+ cl_assert(!git_fs_path_exists("testrepo/other.txt"));
+
+ git_commit_free(commit);
+ git_index_free(index);
+}
+
+
+void test_checkout_tree__removes_conflicts_only_by_pathscope(void)
+{
+ git_oid commit_id;
+ git_commit *commit;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_index *index;
+ const char *path = "executable.txt";
+
+ cl_git_pass(git_oid__fromstr(&commit_id, "afe4393b2b2a965f06acf2ca9658eaa01e0cd6b6", GIT_OID_SHA1));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &commit_id));
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+ opts.paths.count = 1;
+ opts.paths.strings = (char **)&path;
+
+ cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_remove(index, "executable.txt", 0));
+
+ create_conflict("executable.txt");
+ cl_git_mkfile("testrepo/executable.txt", "This is the conflict file.\n");
+
+ create_conflict("other.txt");
+ cl_git_mkfile("testrepo/other.txt", "This is another conflict file.\n");
+
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
+
+ cl_assert_equal_p(NULL, git_index_get_bypath(index, "executable.txt", 1));
+ cl_assert_equal_p(NULL, git_index_get_bypath(index, "executable.txt", 2));
+ cl_assert_equal_p(NULL, git_index_get_bypath(index, "executable.txt", 3));
+
+ cl_assert(git_index_get_bypath(index, "other.txt", 1) != NULL);
+ cl_assert(git_index_get_bypath(index, "other.txt", 2) != NULL);
+ cl_assert(git_index_get_bypath(index, "other.txt", 3) != NULL);
+
+ cl_assert(git_fs_path_exists("testrepo/other.txt"));
+
+ git_commit_free(commit);
+ git_index_free(index);
+}
+
+void test_checkout_tree__case_changing_rename(void)
+{
+ git_index *index;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_oid master_id, dir_commit_id, tree_id, commit_id;
+ git_commit *master_commit, *dir_commit;
+ git_tree *tree;
+ git_signature *signature;
+ const git_index_entry *index_entry;
+ bool case_sensitive;
+
+ assert_on_branch(g_repo, "master");
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ /* Switch branches and perform a case-changing rename */
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_reference_name_to_id(&dir_commit_id, g_repo, "refs/heads/dir"));
+ cl_git_pass(git_commit_lookup(&dir_commit, g_repo, &dir_commit_id));
+
+ cl_git_pass(git_checkout_tree(g_repo, (git_object *)dir_commit, &opts));
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir"));
+
+ cl_assert(git_fs_path_isfile("testrepo/README"));
+ case_sensitive = !git_fs_path_isfile("testrepo/readme");
+
+ cl_assert(index_entry = git_index_get_bypath(index, "README", 0));
+ cl_assert_equal_s("README", index_entry->path);
+
+ cl_git_pass(git_index_remove_bypath(index, "README"));
+ cl_git_pass(p_rename("testrepo/README", "testrepo/__readme__"));
+ cl_git_pass(p_rename("testrepo/__readme__", "testrepo/readme"));
+ cl_git_append2file("testrepo/readme", "An addendum...");
+ cl_git_pass(git_index_add_bypath(index, "readme"));
+
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_index_write_tree(&tree_id, index));
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
+
+ cl_git_pass(git_signature_new(&signature, "Renamer", "rename@contoso.com", time(NULL), 0));
+
+ cl_git_pass(git_commit_create(&commit_id, g_repo, "refs/heads/dir", signature, signature, NULL, "case-changing rename", tree, 1, (const git_commit **)&dir_commit));
+
+ cl_assert(git_fs_path_isfile("testrepo/readme"));
+ if (case_sensitive)
+ cl_assert(!git_fs_path_isfile("testrepo/README"));
+
+ cl_assert(index_entry = git_index_get_bypath(index, "readme", 0));
+ cl_assert_equal_s("readme", index_entry->path);
+
+ /* Switching back to master should rename readme -> README */
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+
+ cl_git_pass(git_reference_name_to_id(&master_id, g_repo, "refs/heads/master"));
+ cl_git_pass(git_commit_lookup(&master_commit, g_repo, &master_id));
+
+ cl_git_pass(git_checkout_tree(g_repo, (git_object *)master_commit, &opts));
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/master"));
+
+ assert_on_branch(g_repo, "master");
+
+ cl_assert(git_fs_path_isfile("testrepo/README"));
+ if (case_sensitive)
+ cl_assert(!git_fs_path_isfile("testrepo/readme"));
+
+ cl_assert(index_entry = git_index_get_bypath(index, "README", 0));
+ cl_assert_equal_s("README", index_entry->path);
+
+ git_index_free(index);
+ git_signature_free(signature);
+ git_tree_free(tree);
+ git_commit_free(dir_commit);
+ git_commit_free(master_commit);
+}
+
+static void perfdata_cb(const git_checkout_perfdata *in, void *payload)
+{
+ memcpy(payload, in, sizeof(git_checkout_perfdata));
+}
+
+void test_checkout_tree__can_collect_perfdata(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_oid oid;
+ git_object *obj = NULL;
+ git_checkout_perfdata perfdata = {0};
+
+ opts.perfdata_cb = perfdata_cb;
+ opts.perfdata_payload = &perfdata;
+
+ assert_on_branch(g_repo, "master");
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
+
+ cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
+
+ cl_assert(perfdata.mkdir_calls > 0);
+ cl_assert(perfdata.stat_calls > 0);
+
+ git_object_free(obj);
+}
+
+static void update_attr_callback(
+ const char *path,
+ size_t completed_steps,
+ size_t total_steps,
+ void *payload)
+{
+ GIT_UNUSED(completed_steps);
+ GIT_UNUSED(total_steps);
+ GIT_UNUSED(payload);
+
+ if (path && strcmp(path, "ident1.txt") == 0)
+ cl_git_write2file("testrepo/.gitattributes",
+ "*.txt ident\n", 12, O_RDWR|O_CREAT, 0666);
+}
+
+void test_checkout_tree__caches_attributes_during_checkout(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_oid oid;
+ git_object *obj = NULL;
+ git_str ident1 = GIT_STR_INIT, ident2 = GIT_STR_INIT;
+ char *ident_paths[] = { "ident1.txt", "ident2.txt" };
+
+ opts.progress_cb = update_attr_callback;
+
+ assert_on_branch(g_repo, "master");
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+ opts.paths.strings = ident_paths;
+ opts.paths.count = 2;
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/ident"));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
+
+ cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
+
+ cl_git_pass(git_futils_readbuffer(&ident1, "testrepo/ident1.txt"));
+ cl_git_pass(git_futils_readbuffer(&ident2, "testrepo/ident2.txt"));
+
+ cl_assert_equal_strn(ident1.ptr, "# $Id$", 6);
+ cl_assert_equal_strn(ident2.ptr, "# $Id$", 6);
+
+ cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
+
+ cl_git_pass(git_futils_readbuffer(&ident1, "testrepo/ident1.txt"));
+ cl_git_pass(git_futils_readbuffer(&ident2, "testrepo/ident2.txt"));
+
+ cl_assert_equal_strn(ident1.ptr, "# $Id: ", 7);
+ cl_assert_equal_strn(ident2.ptr, "# $Id: ", 7);
+
+ git_str_dispose(&ident1);
+ git_str_dispose(&ident2);
+ git_object_free(obj);
+}
+
+void test_checkout_tree__can_not_update_index(void)
+{
+ git_oid oid;
+ git_object *head;
+ unsigned int status;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_index *index;
+
+ opts.checkout_strategy |=
+ GIT_CHECKOUT_FORCE | GIT_CHECKOUT_DONT_UPDATE_INDEX;
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "HEAD"));
+ cl_git_pass(git_object_lookup(&head, g_repo, &oid, GIT_OBJECT_ANY));
+
+ cl_git_pass(git_reset(g_repo, head, GIT_RESET_HARD, &g_opts));
+
+ cl_assert_equal_i(false, git_fs_path_isdir("./testrepo/ab/"));
+
+ cl_git_pass(git_revparse_single(&g_object, g_repo, "subtrees"));
+
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &opts));
+
+ cl_assert_equal_i(true, git_fs_path_isfile("./testrepo/ab/de/2.txt"));
+ cl_git_pass(git_status_file(&status, g_repo, "ab/de/2.txt"));
+ cl_assert_equal_i(GIT_STATUS_WT_NEW, status);
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_status_file(&status, g_repo, "ab/de/2.txt"));
+ cl_assert_equal_i(GIT_STATUS_WT_NEW, status);
+
+ git_object_free(head);
+ git_index_free(index);
+}
+
+void test_checkout_tree__can_update_but_not_write_index(void)
+{
+ git_oid oid;
+ git_object *head;
+ unsigned int status;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_index *index;
+ git_repository *other;
+
+ opts.checkout_strategy |=
+ GIT_CHECKOUT_FORCE | GIT_CHECKOUT_DONT_WRITE_INDEX;
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "HEAD"));
+ cl_git_pass(git_object_lookup(&head, g_repo, &oid, GIT_OBJECT_ANY));
+
+ cl_git_pass(git_reset(g_repo, head, GIT_RESET_HARD, &g_opts));
+
+ cl_assert_equal_i(false, git_fs_path_isdir("./testrepo/ab/"));
+
+ cl_git_pass(git_revparse_single(&g_object, g_repo, "subtrees"));
+
+ cl_git_pass(git_checkout_tree(g_repo, g_object, &opts));
+
+ cl_assert_equal_i(true, git_fs_path_isfile("./testrepo/ab/de/2.txt"));
+ cl_git_pass(git_status_file(&status, g_repo, "ab/de/2.txt"));
+ cl_assert_equal_i(GIT_STATUS_INDEX_NEW, status);
+
+ cl_git_pass(git_repository_open(&other, "testrepo"));
+ cl_git_pass(git_status_file(&status, other, "ab/de/2.txt"));
+ cl_assert_equal_i(GIT_STATUS_WT_NEW, status);
+ git_repository_free(other);
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_repository_open(&other, "testrepo"));
+ cl_git_pass(git_status_file(&status, other, "ab/de/2.txt"));
+ cl_assert_equal_i(GIT_STATUS_INDEX_NEW, status);
+ git_repository_free(other);
+
+ git_object_free(head);
+ git_index_free(index);
+}
+
+/* Emulate checking out in a repo created by clone --no-checkout,
+ * which would not have written an index. */
+void test_checkout_tree__safe_proceeds_if_no_index(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_oid oid;
+ git_object *obj = NULL;
+
+ assert_on_branch(g_repo, "master");
+ cl_must_pass(p_unlink("testrepo/.git/index"));
+
+ /* do second checkout safe because we should be clean after first */
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/subtrees"));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
+
+ cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/subtrees"));
+
+ cl_assert(git_fs_path_isfile("testrepo/README"));
+ cl_assert(git_fs_path_isfile("testrepo/branch_file.txt"));
+ cl_assert(git_fs_path_isfile("testrepo/new.txt"));
+ cl_assert(git_fs_path_isfile("testrepo/ab/4.txt"));
+ cl_assert(git_fs_path_isfile("testrepo/ab/c/3.txt"));
+ cl_assert(git_fs_path_isfile("testrepo/ab/de/2.txt"));
+ cl_assert(git_fs_path_isfile("testrepo/ab/de/fgh/1.txt"));
+
+ cl_assert(!git_fs_path_isdir("testrepo/a"));
+
+ assert_on_branch(g_repo, "subtrees");
+
+ git_object_free(obj);
+}
+
+static int checkout_conflict_count_cb(
+ git_checkout_notify_t why,
+ const char *path,
+ const git_diff_file *b,
+ const git_diff_file *t,
+ const git_diff_file *w,
+ void *payload)
+{
+ size_t *n = payload;
+
+ GIT_UNUSED(why);
+ GIT_UNUSED(path);
+ GIT_UNUSED(b);
+ GIT_UNUSED(t);
+ GIT_UNUSED(w);
+
+ (*n)++;
+
+ return 0;
+}
+
+/* A repo that has a HEAD (even a properly born HEAD that peels to
+ * a commit) but no index should be treated as if it's an empty baseline
+ */
+void test_checkout_tree__baseline_is_empty_when_no_index(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_reference *head;
+ git_object *obj;
+ size_t conflicts = 0;
+
+ assert_on_branch(g_repo, "master");
+
+ cl_git_pass(git_repository_head(&head, g_repo));
+ cl_git_pass(git_reference_peel(&obj, head, GIT_OBJECT_COMMIT));
+
+ cl_git_pass(git_reset(g_repo, obj, GIT_RESET_HARD, NULL));
+
+ cl_must_pass(p_unlink("testrepo/.git/index"));
+
+ /* for a safe checkout, we should have checkout conflicts with
+ * the existing untracked files.
+ */
+ opts.checkout_strategy &= ~GIT_CHECKOUT_FORCE;
+ opts.notify_flags = GIT_CHECKOUT_NOTIFY_CONFLICT;
+ opts.notify_cb = checkout_conflict_count_cb;
+ opts.notify_payload = &conflicts;
+
+ cl_git_fail_with(GIT_ECONFLICT, git_checkout_tree(g_repo, obj, &opts));
+ cl_assert_equal_i(4, conflicts);
+
+ /* but force should succeed and update the index */
+ opts.checkout_strategy |= GIT_CHECKOUT_FORCE;
+ cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
+
+ assert_status_entrycount(g_repo, 0);
+
+ git_object_free(obj);
+ git_reference_free(head);
+}
+
+void test_checkout_tree__mode_change_is_force_updated(void)
+{
+ git_index *index;
+ git_reference *head;
+ git_object *obj;
+
+ if (!cl_is_chmod_supported())
+ clar__skip();
+
+ assert_on_branch(g_repo, "master");
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_repository_head(&head, g_repo));
+ cl_git_pass(git_reference_peel(&obj, head, GIT_OBJECT_COMMIT));
+
+ cl_git_pass(git_reset(g_repo, obj, GIT_RESET_HARD, NULL));
+ assert_status_entrycount(g_repo, 0);
+
+ /* update the mode on-disk */
+ cl_must_pass(p_chmod("testrepo/README", 0755));
+
+ assert_status_entrycount(g_repo, 1);
+ cl_git_pass(git_checkout_tree(g_repo, obj, &g_opts));
+ assert_status_entrycount(g_repo, 0);
+
+ /* update the mode on-disk and in the index */
+ cl_must_pass(p_chmod("testrepo/README", 0755));
+ cl_must_pass(git_index_add_bypath(index, "README"));
+
+ cl_git_pass(git_index_write(index));
+ assert_status_entrycount(g_repo, 1);
+
+ cl_git_pass(git_checkout_tree(g_repo, obj, &g_opts));
+ cl_git_pass(git_index_write(index));
+
+ assert_status_entrycount(g_repo, 0);
+
+ git_object_free(obj);
+ git_reference_free(head);
+ git_index_free(index);
+}
+
+void test_checkout_tree__nullopts(void)
+{
+ cl_git_pass(git_checkout_tree(g_repo, NULL, NULL));
+}
+
+static void modify_index_ondisk(void)
+{
+ git_repository *other_repo;
+ git_index *other_index;
+ git_index_entry entry = {{0}};
+
+ cl_git_pass(git_repository_open(&other_repo, git_repository_workdir(g_repo)));
+ cl_git_pass(git_repository_index(&other_index, other_repo));
+
+ cl_git_pass(git_oid__fromstr(&entry.id, "1385f264afb75a56a5bec74243be9b367ba4ca08", GIT_OID_SHA1));
+ entry.mode = 0100644;
+ entry.path = "README";
+
+ cl_git_pass(git_index_add(other_index, &entry));
+ cl_git_pass(git_index_write(other_index));
+
+ git_index_free(other_index);
+ git_repository_free(other_repo);
+}
+
+static void modify_index_and_checkout_tree(git_checkout_options *opts)
+{
+ git_index *index;
+ git_reference *head;
+ git_object *obj;
+
+ /* External changes to the index are maintained by default */
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_repository_head(&head, g_repo));
+ cl_git_pass(git_reference_peel(&obj, head, GIT_OBJECT_COMMIT));
+
+ cl_git_pass(git_reset(g_repo, obj, GIT_RESET_HARD, NULL));
+ assert_status_entrycount(g_repo, 0);
+
+ modify_index_ondisk();
+
+ /* The file in the index remains modified */
+ cl_git_pass(git_checkout_tree(g_repo, obj, opts));
+
+ git_object_free(obj);
+ git_reference_free(head);
+ git_index_free(index);
+}
+
+void test_checkout_tree__retains_external_index_changes(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+
+ modify_index_and_checkout_tree(&opts);
+ assert_status_entrycount(g_repo, 1);
+}
+
+void test_checkout_tree__no_index_refresh(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_NO_REFRESH;
+
+ modify_index_and_checkout_tree(&opts);
+ assert_status_entrycount(g_repo, 0);
+}
+
+void test_checkout_tree__dry_run(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_oid oid;
+ git_object *obj = NULL;
+ checkout_counts ct;
+
+ /* first let's get things into a known state - by checkout out the HEAD */
+
+ assert_on_branch(g_repo, "master");
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+
+ cl_assert(!git_fs_path_isdir("testrepo/a"));
+
+ check_file_contents_nocr("testrepo/branch_file.txt", "hi\nbye!\n");
+
+ /* now checkout branch but with dry run enabled */
+
+ memset(&ct, 0, sizeof(ct));
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_DRY_RUN;
+ opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
+ opts.notify_cb = checkout_count_callback;
+ opts.notify_payload = &ct;
+
+ cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
+
+ cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir"));
+
+ assert_on_branch(g_repo, "dir");
+
+ /* these normally would have been created and updated, but with
+ * DRY_RUN they will be unchanged.
+ */
+ cl_assert(!git_fs_path_isdir("testrepo/a"));
+ check_file_contents_nocr("testrepo/branch_file.txt", "hi\nbye!\n");
+
+ /* check that notify callback was invoked */
+ cl_assert_equal_i(ct.n_updates, 2);
+
+ git_object_free(obj);
+}
diff --git a/tests/libgit2/checkout/typechange.c b/tests/libgit2/checkout/typechange.c
new file mode 100644
index 0000000..1fa2d30
--- /dev/null
+++ b/tests/libgit2/checkout/typechange.c
@@ -0,0 +1,335 @@
+#include "clar_libgit2.h"
+#include "diff_generate.h"
+#include "git2/checkout.h"
+#include "path.h"
+#include "posix.h"
+#include "futils.h"
+
+static git_repository *g_repo = NULL;
+
+/*
+From the test repo used for this test:
+--------------------------------------
+
+This is a test repo for libgit2 where tree entries have type changes
+
+The key types that could be found in tree entries are:
+
+1 - GIT_FILEMODE_NEW = 0000000
+2 - GIT_FILEMODE_TREE = 0040000
+3 - GIT_FILEMODE_BLOB = 0100644
+4 - GIT_FILEMODE_BLOB_EXECUTABLE = 0100755
+5 - GIT_FILEMODE_LINK = 0120000
+6 - GIT_FILEMODE_COMMIT = 0160000
+
+I will try to have every type of transition somewhere in the history
+of this repo.
+
+Commits
+-------
+Initial commit - a(1) b(1) c(1) d(1) e(1)
+Create content - a(1->2) b(1->3) c(1->4) d(1->5) e(1->6)
+Changes #1 - a(2->3) b(3->4) c(4->5) d(5->6) e(6->2)
+Changes #2 - a(3->5) b(4->6) c(5->2) d(6->3) e(2->4)
+Changes #3 - a(5->3) b(6->4) c(2->5) d(3->6) e(4->2)
+Changes #4 - a(3->2) b(4->3) c(5->4) d(6->5) e(2->6)
+Changes #5 - a(2->1) b(3->1) c(4->1) d(5->1) e(6->1)
+
+*/
+
+static const char *g_typechange_oids[] = {
+ "79b9f23e85f55ea36a472a902e875bc1121a94cb",
+ "9bdb75b73836a99e3dbeea640a81de81031fdc29",
+ "0e7ed140b514b8cae23254cb8656fe1674403aff",
+ "9d0235c7a7edc0889a18f97a42ee6db9fe688447",
+ "9b19edf33a03a0c59cdfc113bfa5c06179bf9b1a",
+ "1b63caae4a5ca96f78e8dfefc376c6a39a142475",
+ "6eae26c90e8ccc4d16208972119c40635489c6f0",
+ NULL
+};
+
+static bool g_typechange_empty[] = {
+ true, false, false, false, false, false, true, true
+};
+
+static const int g_typechange_expected_conflicts[] = {
+ 1, 2, 3, 3, 2, 3, 2
+};
+
+static const int g_typechange_expected_untracked[] = {
+ 6, 4, 3, 2, 3, 2, 5
+};
+
+void test_checkout_typechange__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("typechanges");
+
+ cl_fixture_sandbox("submod2_target");
+ p_rename("submod2_target/.gitted", "submod2_target/.git");
+}
+
+void test_checkout_typechange__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ cl_fixture_cleanup("submod2_target");
+}
+
+static void assert_file_exists(const char *path)
+{
+ cl_assert_(git_fs_path_isfile(path), path);
+}
+
+static void assert_dir_exists(const char *path)
+{
+ cl_assert_(git_fs_path_isdir(path), path);
+}
+
+static void assert_workdir_matches_tree(
+ git_repository *repo, const git_oid *id, const char *root, bool recurse)
+{
+ git_object *obj;
+ git_tree *tree;
+ size_t i, max_i;
+ git_str path = GIT_STR_INIT;
+
+ if (!root)
+ root = git_repository_workdir(repo);
+ cl_assert(root);
+
+ cl_git_pass(git_object_lookup(&obj, repo, id, GIT_OBJECT_ANY));
+ cl_git_pass(git_object_peel((git_object **)&tree, obj, GIT_OBJECT_TREE));
+ git_object_free(obj);
+
+ max_i = git_tree_entrycount(tree);
+
+ for (i = 0; i < max_i; ++i) {
+ const git_tree_entry *te = git_tree_entry_byindex(tree, i);
+ cl_assert(te);
+
+ cl_git_pass(git_str_joinpath(&path, root, git_tree_entry_name(te)));
+
+ switch (git_tree_entry_type(te)) {
+ case GIT_OBJECT_COMMIT:
+ assert_dir_exists(path.ptr);
+ break;
+ case GIT_OBJECT_TREE:
+ assert_dir_exists(path.ptr);
+ if (recurse)
+ assert_workdir_matches_tree(
+ repo, git_tree_entry_id(te), path.ptr, true);
+ break;
+ case GIT_OBJECT_BLOB:
+ switch (git_tree_entry_filemode(te)) {
+ case GIT_FILEMODE_BLOB:
+ case GIT_FILEMODE_BLOB_EXECUTABLE:
+ assert_file_exists(path.ptr);
+ /* because of cross-platform, don't confirm exec bit yet */
+ break;
+ case GIT_FILEMODE_LINK:
+ cl_assert_(git_fs_path_exists(path.ptr), path.ptr);
+ /* because of cross-platform, don't confirm link yet */
+ break;
+ default:
+ cl_assert(false); /* really?! */
+ }
+ break;
+ default:
+ cl_assert(false); /* really?!! */
+ }
+ }
+
+ git_tree_free(tree);
+ git_str_dispose(&path);
+}
+
+void test_checkout_typechange__checkout_typechanges_safe(void)
+{
+ int i;
+ git_object *obj;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ for (i = 0; g_typechange_oids[i] != NULL; ++i) {
+ cl_git_pass(git_revparse_single(&obj, g_repo, g_typechange_oids[i]));
+
+ opts.checkout_strategy = !i ? GIT_CHECKOUT_FORCE : GIT_CHECKOUT_SAFE;
+
+ cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
+
+ cl_git_pass(
+ git_repository_set_head_detached(g_repo, git_object_id(obj)));
+
+ assert_workdir_matches_tree(g_repo, git_object_id(obj), NULL, true);
+
+ git_object_free(obj);
+
+ if (!g_typechange_empty[i]) {
+ cl_assert(git_fs_path_isdir("typechanges"));
+ cl_assert(git_fs_path_exists("typechanges/a"));
+ cl_assert(git_fs_path_exists("typechanges/b"));
+ cl_assert(git_fs_path_exists("typechanges/c"));
+ cl_assert(git_fs_path_exists("typechanges/d"));
+ cl_assert(git_fs_path_exists("typechanges/e"));
+ } else {
+ cl_assert(git_fs_path_isdir("typechanges"));
+ cl_assert(!git_fs_path_exists("typechanges/a"));
+ cl_assert(!git_fs_path_exists("typechanges/b"));
+ cl_assert(!git_fs_path_exists("typechanges/c"));
+ cl_assert(!git_fs_path_exists("typechanges/d"));
+ cl_assert(!git_fs_path_exists("typechanges/e"));
+ }
+ }
+}
+
+typedef struct {
+ int conflicts;
+ int dirty;
+ int updates;
+ int untracked;
+ int ignored;
+} notify_counts;
+
+static int notify_counter(
+ git_checkout_notify_t why,
+ const char *path,
+ const git_diff_file *baseline,
+ const git_diff_file *target,
+ const git_diff_file *workdir,
+ void *payload)
+{
+ notify_counts *cts = payload;
+
+ GIT_UNUSED(path);
+ GIT_UNUSED(baseline);
+ GIT_UNUSED(target);
+ GIT_UNUSED(workdir);
+
+ switch (why) {
+ case GIT_CHECKOUT_NOTIFY_CONFLICT: cts->conflicts++; break;
+ case GIT_CHECKOUT_NOTIFY_DIRTY: cts->dirty++; break;
+ case GIT_CHECKOUT_NOTIFY_UPDATED: cts->updates++; break;
+ case GIT_CHECKOUT_NOTIFY_UNTRACKED: cts->untracked++; break;
+ case GIT_CHECKOUT_NOTIFY_IGNORED: cts->ignored++; break;
+ default: break;
+ }
+
+ return 0;
+}
+
+static void force_create_file(const char *file)
+{
+ int error = git_futils_rmdir_r(file, NULL,
+ GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_REMOVE_BLOCKERS);
+ cl_assert(!error || error == GIT_ENOTFOUND);
+ cl_git_pass(git_futils_mkpath2file(file, 0777));
+ cl_git_rewritefile(file, "yowza!!");
+}
+
+static int make_submodule_dirty(git_submodule *sm, const char *name, void *payload)
+{
+ git_str submodulepath = GIT_STR_INIT;
+ git_str dirtypath = GIT_STR_INIT;
+ git_repository *submodule_repo;
+
+ GIT_UNUSED(name);
+ GIT_UNUSED(payload);
+
+ /* remove submodule directory in preparation for init and repo_init */
+ cl_git_pass(git_str_joinpath(
+ &submodulepath,
+ git_repository_workdir(g_repo),
+ git_submodule_path(sm)
+ ));
+ git_futils_rmdir_r(git_str_cstr(&submodulepath), NULL, GIT_RMDIR_REMOVE_FILES);
+
+ /* initialize submodule's repository */
+ cl_git_pass(git_submodule_repo_init(&submodule_repo, sm, 0));
+
+ /* create a file in the submodule workdir to make it dirty */
+ cl_git_pass(
+ git_str_joinpath(&dirtypath, git_repository_workdir(submodule_repo), "dirty"));
+ force_create_file(git_str_cstr(&dirtypath));
+
+ git_str_dispose(&dirtypath);
+ git_str_dispose(&submodulepath);
+ git_repository_free(submodule_repo);
+
+ return 0;
+}
+
+void test_checkout_typechange__checkout_with_conflicts(void)
+{
+ int i;
+ git_object *obj;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ notify_counts cts = {0};
+
+ opts.notify_flags =
+ GIT_CHECKOUT_NOTIFY_CONFLICT | GIT_CHECKOUT_NOTIFY_UNTRACKED;
+ opts.notify_cb = notify_counter;
+ opts.notify_payload = &cts;
+
+ for (i = 0; g_typechange_oids[i] != NULL; ++i) {
+ cl_git_pass(git_revparse_single(&obj, g_repo, g_typechange_oids[i]));
+
+ force_create_file("typechanges/a/blocker");
+ force_create_file("typechanges/b");
+ force_create_file("typechanges/c/sub/sub/file");
+ git_futils_rmdir_r("typechanges/d", NULL, GIT_RMDIR_REMOVE_FILES);
+ p_mkdir("typechanges/d", 0777); /* intentionally empty dir */
+ force_create_file("typechanges/untracked");
+ cl_git_pass(git_submodule_foreach(g_repo, make_submodule_dirty, NULL));
+
+ opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+ memset(&cts, 0, sizeof(cts));
+
+ cl_git_fail(git_checkout_tree(g_repo, obj, &opts));
+ cl_assert_equal_i(cts.conflicts, g_typechange_expected_conflicts[i]);
+ cl_assert_equal_i(cts.untracked, g_typechange_expected_untracked[i]);
+ cl_assert_equal_i(cts.dirty, 0);
+ cl_assert_equal_i(cts.updates, 0);
+ cl_assert_equal_i(cts.ignored, 0);
+
+ opts.checkout_strategy =
+ GIT_CHECKOUT_FORCE | GIT_CHECKOUT_REMOVE_UNTRACKED;
+ memset(&cts, 0, sizeof(cts));
+
+ cl_assert(git_fs_path_exists("typechanges/untracked"));
+
+ cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
+ cl_assert_equal_i(0, cts.conflicts);
+
+ cl_assert(!git_fs_path_exists("typechanges/untracked"));
+
+ cl_git_pass(
+ git_repository_set_head_detached(g_repo, git_object_id(obj)));
+
+ assert_workdir_matches_tree(g_repo, git_object_id(obj), NULL, true);
+
+ git_object_free(obj);
+ }
+}
+
+void test_checkout_typechange__status_char(void)
+{
+ size_t i;
+ git_oid oid;
+ git_commit *commit;
+ git_diff *diff;
+ const git_diff_delta *delta;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ char expected[8] = {'M', 'M', 'R', 'T', 'D', 'R', 'A', 'R'};
+
+ git_oid__fromstr(&oid, "9b19edf33a03a0c59cdfc113bfa5c06179bf9b1a", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &oid));
+ diffopts.flags |= GIT_DIFF_INCLUDE_TYPECHANGE;
+ cl_git_pass(git_diff__commit(&diff, g_repo, commit, &diffopts));
+ cl_git_pass(git_diff_find_similar(diff, NULL));
+
+ for (i = 0; i < git_diff_num_deltas(diff); i++) {
+ delta = git_diff_get_delta(diff, i);
+ cl_assert_equal_i(expected[i], git_diff_status_char(delta->status));
+ }
+
+ git_diff_free(diff);
+ git_commit_free(commit);
+}
diff --git a/tests/libgit2/cherrypick/bare.c b/tests/libgit2/cherrypick/bare.c
new file mode 100644
index 0000000..6644688
--- /dev/null
+++ b/tests/libgit2/cherrypick/bare.c
@@ -0,0 +1,105 @@
+#include "clar.h"
+#include "clar_libgit2.h"
+
+#include "futils.h"
+#include "git2/cherrypick.h"
+
+#include "../merge/merge_helpers.h"
+
+#define TEST_REPO_PATH "cherrypick"
+
+static git_repository *repo;
+
+void test_cherrypick_bare__initialize(void)
+{
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+}
+
+void test_cherrypick_bare__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_cherrypick_bare__automerge(void)
+{
+ git_commit *head = NULL, *commit = NULL;
+ git_index *index = NULL;
+ git_oid head_oid, cherry_oid;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "38c05a857e831a7e759d83778bfc85d003e21c45", 0, "file1.txt" },
+ { 0100644, "a661b5dec1004e2c62654ded3762370c27cf266b", 0, "file2.txt" },
+ { 0100644, "df6b290e0bd6a89b01d69f66687e8abf385283ca", 0, "file3.txt" },
+ };
+
+ git_oid__fromstr(&head_oid, "d3d77487660ee3c0194ee01dc5eaf478782b1c7e", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
+
+ git_oid__fromstr(&cherry_oid, "cfc4f0999a8367568e049af4f72e452d40828a15", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
+
+ cl_git_pass(git_cherrypick_commit(&index, repo, commit, head, 0, NULL));
+ cl_assert(merge_test_index(index, merge_index_entries, 3));
+
+ git_index_free(index);
+ git_commit_free(head);
+ git_commit_free(commit);
+}
+
+void test_cherrypick_bare__conflicts(void)
+{
+ git_commit *head = NULL, *commit = NULL;
+ git_index *index = NULL;
+ git_oid head_oid, cherry_oid;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "242e7977ba73637822ffb265b46004b9b0e5153b", 0, "file1.txt" },
+ { 0100644, "a58ca3fee5eb68b11adc2703e5843f968c9dad1e", 1, "file2.txt" },
+ { 0100644, "bd6ffc8c6c41f0f85ff9e3d61c9479516bac0024", 2, "file2.txt" },
+ { 0100644, "563f6473a3858f99b80e5f93c660512ed38e1e6f", 3, "file2.txt" },
+ { 0100644, "28d9eb4208074ad1cc84e71ccc908b34573f05d2", 1, "file3.txt" },
+ { 0100644, "1124c2c1ae07b26fded662d6c3f3631d9dc16f88", 2, "file3.txt" },
+ { 0100644, "e233b9ed408a95e9d4b65fec7fc34943a556deb2", 3, "file3.txt" },
+ };
+
+ git_oid__fromstr(&head_oid, "bafbf6912c09505ac60575cd43d3f2aba3bd84d8", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
+
+ git_oid__fromstr(&cherry_oid, "e9b63f3655b2ad80c0ff587389b5a9589a3a7110", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
+
+ cl_git_pass(git_cherrypick_commit(&index, repo, commit, head, 0, NULL));
+ cl_assert(merge_test_index(index, merge_index_entries, 7));
+
+ git_index_free(index);
+ git_commit_free(head);
+ git_commit_free(commit);
+}
+
+void test_cherrypick_bare__orphan(void)
+{
+ git_commit *head = NULL, *commit = NULL;
+ git_index *index = NULL;
+ git_oid head_oid, cherry_oid;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "38c05a857e831a7e759d83778bfc85d003e21c45", 0, "file1.txt" },
+ { 0100644, "a661b5dec1004e2c62654ded3762370c27cf266b", 0, "file2.txt" },
+ { 0100644, "85a4a1d791973644f24c72f5e89420d3064cc452", 0, "file3.txt" },
+ { 0100644, "9ccb9bf50c011fd58dcbaa65df917bf79539717f", 0, "orphan.txt" },
+ };
+
+ git_oid__fromstr(&head_oid, "d3d77487660ee3c0194ee01dc5eaf478782b1c7e", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
+
+ git_oid__fromstr(&cherry_oid, "74f06b5bfec6d33d7264f73606b57a7c0b963819", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
+
+ cl_git_pass(git_cherrypick_commit(&index, repo, commit, head, 0, NULL));
+ cl_assert(merge_test_index(index, merge_index_entries, 4));
+
+ git_index_free(index);
+ git_commit_free(head);
+ git_commit_free(commit);
+}
+
diff --git a/tests/libgit2/cherrypick/workdir.c b/tests/libgit2/cherrypick/workdir.c
new file mode 100644
index 0000000..c16b781
--- /dev/null
+++ b/tests/libgit2/cherrypick/workdir.c
@@ -0,0 +1,469 @@
+#include "clar.h"
+#include "clar_libgit2.h"
+
+#include "futils.h"
+#include "git2/cherrypick.h"
+
+#include "../merge/merge_helpers.h"
+
+#define TEST_REPO_PATH "cherrypick"
+
+static git_repository *repo;
+static git_index *repo_index;
+
+/* Fixture setup and teardown */
+void test_cherrypick_workdir__initialize(void)
+{
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+ git_repository_index(&repo_index, repo);
+}
+
+void test_cherrypick_workdir__cleanup(void)
+{
+ git_index_free(repo_index);
+ cl_git_sandbox_cleanup();
+}
+
+/* git reset --hard d3d77487660ee3c0194ee01dc5eaf478782b1c7e
+ * git cherry-pick cfc4f0999a8367568e049af4f72e452d40828a15
+ * git cherry-pick 964ea3da044d9083181a88ba6701de9e35778bf4
+ * git cherry-pick a43a050c588d4e92f11a6b139680923e9728477d
+ */
+void test_cherrypick_workdir__automerge(void)
+{
+ git_oid head_oid;
+ git_signature *signature = NULL;
+ size_t i;
+
+ const char *cherrypick_oids[] = {
+ "cfc4f0999a8367568e049af4f72e452d40828a15",
+ "964ea3da044d9083181a88ba6701de9e35778bf4",
+ "a43a050c588d4e92f11a6b139680923e9728477d",
+ };
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "38c05a857e831a7e759d83778bfc85d003e21c45", 0, "file1.txt" },
+ { 0100644, "a661b5dec1004e2c62654ded3762370c27cf266b", 0, "file2.txt" },
+ { 0100644, "df6b290e0bd6a89b01d69f66687e8abf385283ca", 0, "file3.txt" },
+
+ { 0100644, "38c05a857e831a7e759d83778bfc85d003e21c45", 0, "file1.txt" },
+ { 0100644, "bd8fc3c59fb52d3c8b5907ace7defa5803f82419", 0, "file2.txt" },
+ { 0100644, "df6b290e0bd6a89b01d69f66687e8abf385283ca", 0, "file3.txt" },
+
+ { 0100644, "f06427bee380364bc7e0cb26a9245158e4726ce0", 0, "file1.txt" },
+ { 0100644, "bd8fc3c59fb52d3c8b5907ace7defa5803f82419", 0, "file2.txt" },
+ { 0100644, "df6b290e0bd6a89b01d69f66687e8abf385283ca", 0, "file3.txt" },
+ };
+
+ cl_git_pass(git_signature_new(&signature, "Picker", "picker@example.org", time(NULL), 0));
+
+ git_oid__fromstr(&head_oid, "d3d77487660ee3c0194ee01dc5eaf478782b1c7e", GIT_OID_SHA1);
+
+ for (i = 0; i < 3; ++i) {
+ git_commit *head = NULL, *commit = NULL;
+ git_oid cherry_oid, cherrypicked_oid, cherrypicked_tree_oid;
+ git_tree *cherrypicked_tree = NULL;
+
+ cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
+ cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL));
+
+ git_oid__fromstr(&cherry_oid, cherrypick_oids[i], GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
+ cl_git_pass(git_cherrypick(repo, commit, NULL));
+
+ cl_assert(git_fs_path_exists(TEST_REPO_PATH "/.git/CHERRY_PICK_HEAD"));
+ cl_assert(git_fs_path_exists(TEST_REPO_PATH "/.git/MERGE_MSG"));
+
+ cl_git_pass(git_index_write_tree(&cherrypicked_tree_oid, repo_index));
+ cl_git_pass(git_tree_lookup(&cherrypicked_tree, repo, &cherrypicked_tree_oid));
+ cl_git_pass(git_commit_create(&cherrypicked_oid, repo, "HEAD", signature, signature, NULL,
+ "Cherry picked!", cherrypicked_tree, 1, (const git_commit **)&head));
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries + i * 3, 3));
+
+ git_oid_cpy(&head_oid, &cherrypicked_oid);
+
+ git_tree_free(cherrypicked_tree);
+ git_commit_free(head);
+ git_commit_free(commit);
+ }
+
+ git_signature_free(signature);
+}
+
+/* git reset --hard cfc4f0999a8367568e049af4f72e452d40828a15
+ * git cherry-pick a43a050c588d4e92f11a6b139680923e9728477d*/
+void test_cherrypick_workdir__empty_result(void)
+{
+ git_oid head_oid;
+ git_signature *signature = NULL;
+ git_commit *head = NULL, *commit = NULL;
+ git_oid cherry_oid;
+
+ const char *cherrypick_oid = "a43a050c588d4e92f11a6b139680923e9728477d";
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "19c5c7207054604b69c84d08a7571ef9672bb5c2", 0, "file1.txt" },
+ { 0100644, "a58ca3fee5eb68b11adc2703e5843f968c9dad1e", 0, "file2.txt" },
+ { 0100644, "28d9eb4208074ad1cc84e71ccc908b34573f05d2", 0, "file3.txt" },
+ };
+
+ cl_git_pass(git_signature_new(&signature, "Picker", "picker@example.org", time(NULL), 0));
+
+ git_oid__fromstr(&head_oid, "cfc4f0999a8367568e049af4f72e452d40828a15", GIT_OID_SHA1);
+
+ /* Create an untracked file that should not conflict */
+ cl_git_mkfile(TEST_REPO_PATH "/file4.txt", "");
+ cl_assert(git_fs_path_exists(TEST_REPO_PATH "/file4.txt"));
+
+ cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
+ cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL));
+
+ git_oid__fromstr(&cherry_oid, cherrypick_oid, GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
+ cl_git_pass(git_cherrypick(repo, commit, NULL));
+
+ /* The resulting tree should not have changed, the change was already on HEAD */
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 3));
+
+ git_commit_free(head);
+ git_commit_free(commit);
+
+ git_signature_free(signature);
+}
+
+/* git reset --hard bafbf6912c09505ac60575cd43d3f2aba3bd84d8
+ * git cherry-pick e9b63f3655b2ad80c0ff587389b5a9589a3a7110
+ */
+void test_cherrypick_workdir__conflicts(void)
+{
+ git_commit *head = NULL, *commit = NULL;
+ git_oid head_oid, cherry_oid;
+ git_str conflicting_buf = GIT_STR_INIT, mergemsg_buf = GIT_STR_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "242e7977ba73637822ffb265b46004b9b0e5153b", 0, "file1.txt" },
+ { 0100644, "a58ca3fee5eb68b11adc2703e5843f968c9dad1e", 1, "file2.txt" },
+ { 0100644, "bd6ffc8c6c41f0f85ff9e3d61c9479516bac0024", 2, "file2.txt" },
+ { 0100644, "563f6473a3858f99b80e5f93c660512ed38e1e6f", 3, "file2.txt" },
+ { 0100644, "28d9eb4208074ad1cc84e71ccc908b34573f05d2", 1, "file3.txt" },
+ { 0100644, "1124c2c1ae07b26fded662d6c3f3631d9dc16f88", 2, "file3.txt" },
+ { 0100644, "e233b9ed408a95e9d4b65fec7fc34943a556deb2", 3, "file3.txt" },
+ };
+
+ git_oid__fromstr(&head_oid, "bafbf6912c09505ac60575cd43d3f2aba3bd84d8", GIT_OID_SHA1);
+
+ cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
+ cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL));
+
+ git_oid__fromstr(&cherry_oid, "e9b63f3655b2ad80c0ff587389b5a9589a3a7110", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
+ cl_git_pass(git_cherrypick(repo, commit, NULL));
+
+ cl_assert(git_fs_path_exists(TEST_REPO_PATH "/.git/CHERRY_PICK_HEAD"));
+ cl_assert(git_fs_path_exists(TEST_REPO_PATH "/.git/MERGE_MSG"));
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 7));
+
+ cl_git_pass(git_futils_readbuffer(&mergemsg_buf,
+ TEST_REPO_PATH "/.git/MERGE_MSG"));
+ cl_assert(strcmp(git_str_cstr(&mergemsg_buf),
+ "Change all files\n" \
+ "\n" \
+ "#Conflicts:\n" \
+ "#\tfile2.txt\n" \
+ "#\tfile3.txt\n") == 0);
+
+ cl_git_pass(git_futils_readbuffer(&conflicting_buf,
+ TEST_REPO_PATH "/file2.txt"));
+
+ cl_assert(strcmp(git_str_cstr(&conflicting_buf),
+ "!File 2\n" \
+ "File 2\n" \
+ "File 2\n" \
+ "File 2\n" \
+ "File 2\n" \
+ "File 2\n" \
+ "File 2\n" \
+ "File 2\n" \
+ "File 2\n" \
+ "File 2\n" \
+ "File 2!!\n" \
+ "File 2\n" \
+ "File 2\n" \
+ "File 2\n" \
+ "<<<<<<< HEAD\n" \
+ "File 2\n" \
+ "=======\n" \
+ "File 2!\n" \
+ "File 2\n" \
+ "File 2!\n" \
+ ">>>>>>> e9b63f3... Change all files\n") == 0);
+
+ cl_git_pass(git_futils_readbuffer(&conflicting_buf,
+ TEST_REPO_PATH "/file3.txt"));
+
+ cl_assert(strcmp(git_str_cstr(&conflicting_buf),
+ "!File 3\n" \
+ "File 3\n" \
+ "File 3\n" \
+ "File 3\n" \
+ "File 3\n" \
+ "File 3\n" \
+ "File 3\n" \
+ "File 3\n" \
+ "File 3\n" \
+ "File 3\n" \
+ "File 3\n" \
+ "File 3!!\n" \
+ "File 3\n" \
+ "File 3\n" \
+ "File 3\n" \
+ "<<<<<<< HEAD\n" \
+ "=======\n" \
+ "File 3!\n" \
+ "File 3!\n" \
+ ">>>>>>> e9b63f3... Change all files\n") == 0);
+
+ git_commit_free(commit);
+ git_commit_free(head);
+ git_str_dispose(&mergemsg_buf);
+ git_str_dispose(&conflicting_buf);
+}
+
+/* git reset --hard bafbf6912c09505ac60575cd43d3f2aba3bd84d8
+ * git cherry-pick -X ours e9b63f3655b2ad80c0ff587389b5a9589a3a7110
+ */
+void test_cherrypick_workdir__conflict_use_ours(void)
+{
+ git_commit *head = NULL, *commit = NULL;
+ git_oid head_oid, cherry_oid;
+ git_cherrypick_options opts = GIT_CHERRYPICK_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "242e7977ba73637822ffb265b46004b9b0e5153b", 0, "file1.txt" },
+ { 0100644, "a58ca3fee5eb68b11adc2703e5843f968c9dad1e", 1, "file2.txt" },
+ { 0100644, "bd6ffc8c6c41f0f85ff9e3d61c9479516bac0024", 2, "file2.txt" },
+ { 0100644, "563f6473a3858f99b80e5f93c660512ed38e1e6f", 3, "file2.txt" },
+ { 0100644, "28d9eb4208074ad1cc84e71ccc908b34573f05d2", 1, "file3.txt" },
+ { 0100644, "1124c2c1ae07b26fded662d6c3f3631d9dc16f88", 2, "file3.txt" },
+ { 0100644, "e233b9ed408a95e9d4b65fec7fc34943a556deb2", 3, "file3.txt" },
+ };
+
+ struct merge_index_entry merge_filesystem_entries[] = {
+ { 0100644, "242e7977ba73637822ffb265b46004b9b0e5153b", 0, "file1.txt" },
+ { 0100644, "bd6ffc8c6c41f0f85ff9e3d61c9479516bac0024", 0, "file2.txt" },
+ { 0100644, "1124c2c1ae07b26fded662d6c3f3631d9dc16f88", 0, "file3.txt" },
+ };
+
+ /* leave the index in a conflicted state, but checkout "ours" to the workdir */
+ opts.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_USE_OURS;
+
+ git_oid__fromstr(&head_oid, "bafbf6912c09505ac60575cd43d3f2aba3bd84d8", GIT_OID_SHA1);
+
+ cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
+ cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL));
+
+ git_oid__fromstr(&cherry_oid, "e9b63f3655b2ad80c0ff587389b5a9589a3a7110", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
+ cl_git_pass(git_cherrypick(repo, commit, &opts));
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 7));
+ cl_assert(merge_test_workdir(repo, merge_filesystem_entries, 3));
+
+ /* resolve conflicts in the index by taking "ours" */
+ opts.merge_opts.file_favor = GIT_MERGE_FILE_FAVOR_OURS;
+
+ cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL));
+ cl_git_pass(git_cherrypick(repo, commit, &opts));
+
+ cl_assert(merge_test_index(repo_index, merge_filesystem_entries, 3));
+ cl_assert(merge_test_workdir(repo, merge_filesystem_entries, 3));
+
+ git_commit_free(commit);
+ git_commit_free(head);
+}
+
+/* git reset --hard cfc4f0999a8367568e049af4f72e452d40828a15
+ * git cherry-pick 2a26c7e88b285613b302ba76712bc998863f3cbc
+ */
+void test_cherrypick_workdir__rename(void)
+{
+ git_commit *head, *commit;
+ git_oid head_oid, cherry_oid;
+ git_cherrypick_options opts = GIT_CHERRYPICK_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "19c5c7207054604b69c84d08a7571ef9672bb5c2", 0, "file1.txt" },
+ { 0100644, "a58ca3fee5eb68b11adc2703e5843f968c9dad1e", 0, "file2.txt" },
+ { 0100644, "28d9eb4208074ad1cc84e71ccc908b34573f05d2", 0, "file3.txt.renamed" },
+ };
+
+ opts.merge_opts.flags |= GIT_MERGE_FIND_RENAMES;
+ opts.merge_opts.rename_threshold = 50;
+
+ git_oid__fromstr(&head_oid, "cfc4f0999a8367568e049af4f72e452d40828a15", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
+ cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL));
+
+ git_oid__fromstr(&cherry_oid, "2a26c7e88b285613b302ba76712bc998863f3cbc", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
+ cl_git_pass(git_cherrypick(repo, commit, &opts));
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 3));
+
+ git_commit_free(commit);
+ git_commit_free(head);
+}
+
+/* git reset --hard 44cd2ed2052c9c68f9a439d208e9614dc2a55c70
+ * git cherry-pick 2a26c7e88b285613b302ba76712bc998863f3cbc
+ */
+void test_cherrypick_workdir__both_renamed(void)
+{
+ git_commit *head, *commit;
+ git_oid head_oid, cherry_oid;
+ git_str mergemsg_buf = GIT_STR_INIT;
+ git_cherrypick_options opts = GIT_CHERRYPICK_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "19c5c7207054604b69c84d08a7571ef9672bb5c2", 0, "file1.txt" },
+ { 0100644, "a58ca3fee5eb68b11adc2703e5843f968c9dad1e", 0, "file2.txt" },
+ { 0100644, "e233b9ed408a95e9d4b65fec7fc34943a556deb2", 1, "file3.txt" },
+ { 0100644, "e233b9ed408a95e9d4b65fec7fc34943a556deb2", 3, "file3.txt.renamed" },
+ { 0100644, "28d9eb4208074ad1cc84e71ccc908b34573f05d2", 2, "file3.txt.renamed_on_branch" },
+ };
+
+ opts.merge_opts.flags |= GIT_MERGE_FIND_RENAMES;
+ opts.merge_opts.rename_threshold = 50;
+
+ git_oid__fromstr(&head_oid, "44cd2ed2052c9c68f9a439d208e9614dc2a55c70", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
+ cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL));
+
+ git_oid__fromstr(&cherry_oid, "2a26c7e88b285613b302ba76712bc998863f3cbc", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
+ cl_git_pass(git_cherrypick(repo, commit, &opts));
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 5));
+
+ cl_git_pass(git_futils_readbuffer(&mergemsg_buf,
+ TEST_REPO_PATH "/.git/MERGE_MSG"));
+ cl_assert(strcmp(git_str_cstr(&mergemsg_buf),
+ "Renamed file3.txt -> file3.txt.renamed\n" \
+ "\n" \
+ "#Conflicts:\n" \
+ "#\tfile3.txt\n" \
+ "#\tfile3.txt.renamed\n" \
+ "#\tfile3.txt.renamed_on_branch\n") == 0);
+
+ git_str_dispose(&mergemsg_buf);
+ git_commit_free(commit);
+ git_commit_free(head);
+}
+
+void test_cherrypick_workdir__nonmerge_fails_mainline_specified(void)
+{
+ git_reference *head;
+ git_commit *commit;
+ git_cherrypick_options opts = GIT_CHERRYPICK_OPTIONS_INIT;
+
+ cl_git_pass(git_repository_head(&head, repo));
+ cl_git_pass(git_reference_peel((git_object **)&commit, head, GIT_OBJECT_COMMIT));
+
+ opts.mainline = 1;
+ cl_must_fail(git_cherrypick(repo, commit, &opts));
+ cl_assert(!git_fs_path_exists(TEST_REPO_PATH "/.git/CHERRY_PICK_HEAD"));
+ cl_assert(!git_fs_path_exists(TEST_REPO_PATH "/.git/MERGE_MSG"));
+
+ git_reference_free(head);
+ git_commit_free(commit);
+}
+
+/* git reset --hard cfc4f0999a8367568e049af4f72e452d40828a15
+ * git cherry-pick abe4603bc7cd5b8167a267e0e2418fd2348f8cff
+ */
+void test_cherrypick_workdir__merge_fails_without_mainline_specified(void)
+{
+ git_commit *head, *commit;
+ git_oid head_oid, cherry_oid;
+
+ git_oid__fromstr(&head_oid, "cfc4f0999a8367568e049af4f72e452d40828a15", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
+ cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL));
+
+ git_oid__fromstr(&cherry_oid, "abe4603bc7cd5b8167a267e0e2418fd2348f8cff", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
+
+ cl_must_fail(git_cherrypick(repo, commit, NULL));
+ cl_assert(!git_fs_path_exists(TEST_REPO_PATH "/.git/CHERRY_PICK_HEAD"));
+ cl_assert(!git_fs_path_exists(TEST_REPO_PATH "/.git/MERGE_MSG"));
+
+ git_commit_free(commit);
+ git_commit_free(head);
+}
+
+/* git reset --hard cfc4f0999a8367568e049af4f72e452d40828a15
+ * git cherry-pick -m1 abe4603bc7cd5b8167a267e0e2418fd2348f8cff
+ */
+void test_cherrypick_workdir__merge_first_parent(void)
+{
+ git_commit *head, *commit;
+ git_oid head_oid, cherry_oid;
+ git_cherrypick_options opts = GIT_CHERRYPICK_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "f90f9dcbdac2cce5cc166346160e19cb693ef4e8", 0, "file1.txt" },
+ { 0100644, "563f6473a3858f99b80e5f93c660512ed38e1e6f", 0, "file2.txt" },
+ { 0100644, "e233b9ed408a95e9d4b65fec7fc34943a556deb2", 0, "file3.txt" },
+ };
+
+ opts.mainline = 1;
+
+ git_oid__fromstr(&head_oid, "cfc4f0999a8367568e049af4f72e452d40828a15", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
+ cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL));
+
+ git_oid__fromstr(&cherry_oid, "abe4603bc7cd5b8167a267e0e2418fd2348f8cff", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
+
+ cl_git_pass(git_cherrypick(repo, commit, &opts));
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 3));
+
+ git_commit_free(commit);
+ git_commit_free(head);
+}
+
+/* git reset --hard cfc4f0999a8367568e049af4f72e452d40828a15
+ * git cherry-pick -m2 abe4603bc7cd5b8167a267e0e2418fd2348f8cff
+ */
+void test_cherrypick_workdir__merge_second_parent(void)
+{
+ git_commit *head, *commit;
+ git_oid head_oid, cherry_oid;
+ git_cherrypick_options opts = GIT_CHERRYPICK_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "487434cace79238a7091e2220611d4f20a765690", 0, "file1.txt" },
+ { 0100644, "e5183bfd18e3a0a691fadde2f0d5610b73282d31", 0, "file2.txt" },
+ { 0100644, "409a1bec58bf35348e8b62b72bb9c1f45cf5a587", 0, "file3.txt" },
+ };
+
+ opts.mainline = 2;
+
+ git_oid__fromstr(&head_oid, "cfc4f0999a8367568e049af4f72e452d40828a15", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
+ cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL));
+
+ git_oid__fromstr(&cherry_oid, "abe4603bc7cd5b8167a267e0e2418fd2348f8cff", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, repo, &cherry_oid));
+
+ cl_git_pass(git_cherrypick(repo, commit, &opts));
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 3));
+
+ git_commit_free(commit);
+ git_commit_free(head);
+}
+
diff --git a/tests/libgit2/clone/empty.c b/tests/libgit2/clone/empty.c
new file mode 100644
index 0000000..94847bc
--- /dev/null
+++ b/tests/libgit2/clone/empty.c
@@ -0,0 +1,102 @@
+#include "clar_libgit2.h"
+
+#include "git2/clone.h"
+#include "repository.h"
+#include "repo/repo_helpers.h"
+
+static git_clone_options g_options;
+static git_repository *g_repo;
+static git_repository *g_repo_cloned;
+
+void test_clone_empty__initialize(void)
+{
+ git_repository *sandbox = cl_git_sandbox_init("empty_bare.git");
+ git_fetch_options dummy_options = GIT_FETCH_OPTIONS_INIT;
+ cl_git_remove_placeholders(git_repository_path(sandbox), "dummy-marker.txt");
+
+ g_repo = NULL;
+
+ memset(&g_options, 0, sizeof(git_clone_options));
+ g_options.version = GIT_CLONE_OPTIONS_VERSION;
+ g_options.fetch_opts = dummy_options;
+}
+
+void test_clone_empty__cleanup(void)
+{
+ cl_fixture_cleanup("tmp_global_path");
+ cl_git_sandbox_cleanup();
+}
+
+static void cleanup_repository(void *path)
+{
+ cl_fixture_cleanup((const char *)path);
+
+ git_repository_free(g_repo_cloned);
+ g_repo_cloned = NULL;
+}
+
+void test_clone_empty__can_clone_an_empty_local_repo_barely(void)
+{
+ char *local_name = "refs/heads/master";
+ const char *expected_tracked_branch_name = "refs/remotes/origin/master";
+ const char *expected_remote_name = "origin";
+ git_buf buf = GIT_BUF_INIT;
+ git_reference *ref;
+
+ cl_set_cleanup(&cleanup_repository, "./empty");
+
+ g_options.bare = true;
+ cl_git_pass(git_clone(&g_repo_cloned, "./empty_bare.git", "./empty", &g_options));
+
+ /* Although the HEAD is unborn... */
+ cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&ref, g_repo_cloned, local_name));
+
+ /* ...one can still retrieve the name of the remote tracking reference */
+ cl_git_pass(git_branch_upstream_name(&buf, g_repo_cloned, local_name));
+
+ cl_assert_equal_s(expected_tracked_branch_name, buf.ptr);
+ git_buf_dispose(&buf);
+
+ /* ...and the name of the remote... */
+ cl_git_pass(git_branch_remote_name(&buf, g_repo_cloned, expected_tracked_branch_name));
+
+ cl_assert_equal_s(expected_remote_name, buf.ptr);
+ git_buf_dispose(&buf);
+
+ /* ...even when the remote HEAD is unborn as well */
+ cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&ref, g_repo_cloned,
+ expected_tracked_branch_name));
+}
+
+void test_clone_empty__respects_initialbranch_config(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ create_tmp_global_config("tmp_global_path", "init.defaultbranch", "my_default_branch");
+
+ cl_set_cleanup(&cleanup_repository, "./empty");
+
+ g_options.bare = true;
+ cl_git_pass(git_clone(&g_repo_cloned, "./empty_bare.git", "./empty", &g_options));
+ cl_git_pass(git_branch_upstream_name(&buf, g_repo_cloned, "refs/heads/my_default_branch"));
+ cl_assert_equal_s("refs/remotes/origin/my_default_branch", buf.ptr);
+ git_buf_dispose(&buf);
+}
+
+void test_clone_empty__can_clone_an_empty_local_repo(void)
+{
+ cl_set_cleanup(&cleanup_repository, "./empty");
+
+ cl_git_pass(git_clone(&g_repo_cloned, "./empty_bare.git", "./empty", &g_options));
+}
+
+void test_clone_empty__can_clone_an_empty_standard_repo(void)
+{
+ cl_git_sandbox_cleanup();
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+ cl_git_remove_placeholders(git_repository_path(g_repo), "dummy-marker.txt");
+
+ cl_set_cleanup(&cleanup_repository, "./empty");
+
+ cl_git_pass(git_clone(&g_repo_cloned, "./empty_standard_repo", "./empty", &g_options));
+}
diff --git a/tests/libgit2/clone/local.c b/tests/libgit2/clone/local.c
new file mode 100644
index 0000000..e0bd74d
--- /dev/null
+++ b/tests/libgit2/clone/local.c
@@ -0,0 +1,212 @@
+#include "clar_libgit2.h"
+
+#include "git2/clone.h"
+#include "clone.h"
+#include "path.h"
+#include "posix.h"
+#include "futils.h"
+
+static int file_url(git_str *buf, const char *host, const char *path)
+{
+ if (path[0] == '/')
+ path++;
+
+ git_str_clear(buf);
+ return git_str_printf(buf, "file://%s/%s", host, path);
+}
+
+#ifdef GIT_WIN32
+static int git_style_unc_path(git_str *buf, const char *host, const char *path)
+{
+ git_str_clear(buf);
+
+ if (host)
+ git_str_printf(buf, "//%s/", host);
+
+ if (path[0] == '/')
+ path++;
+
+ if (git__isalpha(path[0]) && path[1] == ':' && path[2] == '/') {
+ git_str_printf(buf, "%c$/", path[0]);
+ path += 3;
+ }
+
+ git_str_puts(buf, path);
+
+ return git_str_oom(buf) ? -1 : 0;
+}
+
+static int unc_path(git_str *buf, const char *host, const char *path)
+{
+ char *c;
+
+ if (git_style_unc_path(buf, host, path) < 0)
+ return -1;
+
+ for (c = buf->ptr; *c; c++)
+ if (*c == '/')
+ *c = '\\';
+
+ return 0;
+}
+#endif
+
+void test_clone_local__should_clone_local(void)
+{
+ git_str buf = GIT_STR_INIT;
+
+ /* we use a fixture path because it needs to exist for us to want to clone */
+ const char *path = cl_fixture("testrepo.git");
+
+ cl_git_pass(file_url(&buf, "", path));
+ cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_AUTO));
+ cl_assert_equal_i(1, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL));
+ cl_assert_equal_i(1, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_NO_LINKS));
+ cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_NO_LOCAL));
+
+ cl_git_pass(file_url(&buf, "localhost", path));
+ cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_AUTO));
+ cl_assert_equal_i(1, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL));
+ cl_assert_equal_i(1, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_NO_LINKS));
+ cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_NO_LOCAL));
+
+ cl_git_pass(file_url(&buf, "other-host.mycompany.com", path));
+ cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_AUTO));
+ cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL));
+ cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_NO_LINKS));
+ cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_NO_LOCAL));
+
+ /* Ensure that file:/// urls are percent decoded: .git == %2e%67%69%74 */
+ cl_git_pass(file_url(&buf, "", path));
+ git_str_shorten(&buf, 4);
+ cl_git_pass(git_str_puts(&buf, "%2e%67%69%74"));
+ cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_AUTO));
+ cl_assert_equal_i(1, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL));
+ cl_assert_equal_i(1, git_clone__should_clone_local(buf.ptr, GIT_CLONE_LOCAL_NO_LINKS));
+ cl_assert_equal_i(0, git_clone__should_clone_local(buf.ptr, GIT_CLONE_NO_LOCAL));
+
+ cl_assert_equal_i(1, git_clone__should_clone_local(path, GIT_CLONE_LOCAL_AUTO));
+ cl_assert_equal_i(1, git_clone__should_clone_local(path, GIT_CLONE_LOCAL));
+ cl_assert_equal_i(1, git_clone__should_clone_local(path, GIT_CLONE_LOCAL_NO_LINKS));
+ cl_assert_equal_i(0, git_clone__should_clone_local(path, GIT_CLONE_NO_LOCAL));
+
+ git_str_dispose(&buf);
+}
+
+void test_clone_local__hardlinks(void)
+{
+ git_repository *repo;
+ git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
+ git_str buf = GIT_STR_INIT;
+ struct stat st;
+
+ /*
+ * In this first clone, we just copy over, since the temp dir
+ * will often be in a different filesystem, so we cannot
+ * link. It also allows us to control the number of links
+ */
+ opts.bare = true;
+ opts.local = GIT_CLONE_LOCAL_NO_LINKS;
+ cl_git_pass(git_clone(&repo, cl_fixture("testrepo.git"), "./clone.git", &opts));
+ git_repository_free(repo);
+
+ /* This second clone is in the same filesystem, so we can hardlink */
+
+ opts.local = GIT_CLONE_LOCAL;
+ cl_git_pass(git_clone(&repo, cl_git_path_url("clone.git"), "./clone2.git", &opts));
+
+#ifndef GIT_WIN32
+ git_str_clear(&buf);
+ cl_git_pass(git_str_join_n(&buf, '/', 4, git_repository_path(repo), "objects", "08", "b041783f40edfe12bb406c9c9a8a040177c125"));
+
+ cl_git_pass(p_stat(buf.ptr, &st));
+ cl_assert_equal_i(2, st.st_nlink);
+#endif
+
+ git_repository_free(repo);
+ git_str_clear(&buf);
+
+ opts.local = GIT_CLONE_LOCAL_NO_LINKS;
+ cl_git_pass(git_clone(&repo, cl_git_path_url("clone.git"), "./clone3.git", &opts));
+
+ git_str_clear(&buf);
+ cl_git_pass(git_str_join_n(&buf, '/', 4, git_repository_path(repo), "objects", "08", "b041783f40edfe12bb406c9c9a8a040177c125"));
+
+ cl_git_pass(p_stat(buf.ptr, &st));
+ cl_assert_equal_i(1, st.st_nlink);
+
+ git_repository_free(repo);
+
+ /* this one should automatically use links */
+ cl_git_pass(git_clone(&repo, "./clone.git", "./clone4.git", NULL));
+
+#ifndef GIT_WIN32
+ git_str_clear(&buf);
+ cl_git_pass(git_str_join_n(&buf, '/', 4, git_repository_path(repo), "objects", "08", "b041783f40edfe12bb406c9c9a8a040177c125"));
+
+ cl_git_pass(p_stat(buf.ptr, &st));
+ cl_assert_equal_i(3, st.st_nlink);
+#endif
+
+ git_str_dispose(&buf);
+ git_repository_free(repo);
+
+ cl_git_pass(git_futils_rmdir_r("./clone.git", NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_git_pass(git_futils_rmdir_r("./clone2.git", NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_git_pass(git_futils_rmdir_r("./clone3.git", NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_git_pass(git_futils_rmdir_r("./clone4.git", NULL, GIT_RMDIR_REMOVE_FILES));
+}
+
+void test_clone_local__standard_unc_paths_are_written_git_style(void)
+{
+#ifdef GIT_WIN32
+ git_repository *repo;
+ git_remote *remote;
+ git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
+ git_str unc = GIT_STR_INIT, git_unc = GIT_STR_INIT;
+
+ /* we use a fixture path because it needs to exist for us to want to clone */
+ const char *path = cl_fixture("testrepo.git");
+
+ cl_git_pass(unc_path(&unc, "localhost", path));
+ cl_git_pass(git_style_unc_path(&git_unc, "localhost", path));
+
+ cl_git_pass(git_clone(&repo, unc.ptr, "./clone.git", &opts));
+ cl_git_pass(git_remote_lookup(&remote, repo, "origin"));
+
+ cl_assert_equal_s(git_unc.ptr, git_remote_url(remote));
+
+ git_remote_free(remote);
+ git_repository_free(repo);
+ git_str_dispose(&unc);
+ git_str_dispose(&git_unc);
+
+ cl_git_pass(git_futils_rmdir_r("./clone.git", NULL, GIT_RMDIR_REMOVE_FILES));
+#endif
+}
+
+void test_clone_local__git_style_unc_paths(void)
+{
+#ifdef GIT_WIN32
+ git_repository *repo;
+ git_remote *remote;
+ git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
+ git_str git_unc = GIT_STR_INIT;
+
+ /* we use a fixture path because it needs to exist for us to want to clone */
+ const char *path = cl_fixture("testrepo.git");
+
+ cl_git_pass(git_style_unc_path(&git_unc, "localhost", path));
+
+ cl_git_pass(git_clone(&repo, git_unc.ptr, "./clone.git", &opts));
+ cl_git_pass(git_remote_lookup(&remote, repo, "origin"));
+
+ cl_assert_equal_s(git_unc.ptr, git_remote_url(remote));
+
+ git_remote_free(remote);
+ git_repository_free(repo);
+ git_str_dispose(&git_unc);
+
+ cl_git_pass(git_futils_rmdir_r("./clone.git", NULL, GIT_RMDIR_REMOVE_FILES));
+#endif
+}
diff --git a/tests/libgit2/clone/nonetwork.c b/tests/libgit2/clone/nonetwork.c
new file mode 100644
index 0000000..5316003
--- /dev/null
+++ b/tests/libgit2/clone/nonetwork.c
@@ -0,0 +1,362 @@
+#include "clar_libgit2.h"
+
+#include "git2/clone.h"
+#include "../submodule/submodule_helpers.h"
+#include "remote.h"
+#include "futils.h"
+#include "repository.h"
+#include "index.h"
+
+#define LIVE_REPO_URL "git://github.com/libgit2/TestGitRepository"
+
+static git_clone_options g_options;
+static git_repository *g_repo;
+static git_reference* g_ref;
+static git_remote* g_remote;
+
+void test_clone_nonetwork__initialize(void)
+{
+ git_checkout_options dummy_opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_fetch_options dummy_fetch = GIT_FETCH_OPTIONS_INIT;
+
+ g_repo = NULL;
+
+ memset(&g_options, 0, sizeof(git_clone_options));
+ g_options.version = GIT_CLONE_OPTIONS_VERSION;
+ g_options.checkout_opts = dummy_opts;
+ g_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+ g_options.fetch_opts = dummy_fetch;
+}
+
+void test_clone_nonetwork__cleanup(void)
+{
+ if (g_repo) {
+ git_repository_free(g_repo);
+ g_repo = NULL;
+ }
+
+ if (g_ref) {
+ git_reference_free(g_ref);
+ g_ref = NULL;
+ }
+
+ if (g_remote) {
+ git_remote_free(g_remote);
+ g_remote = NULL;
+ }
+
+ cl_fixture_cleanup("./foo");
+}
+
+void test_clone_nonetwork__bad_urls(void)
+{
+ /* Clone should clean up the mess if the URL isn't a git repository */
+ cl_git_fail(git_clone(&g_repo, "not_a_repo", "./foo", &g_options));
+ cl_assert(!git_fs_path_exists("./foo"));
+ g_options.bare = true;
+ cl_git_fail(git_clone(&g_repo, "not_a_repo", "./foo", &g_options));
+ cl_assert(!git_fs_path_exists("./foo"));
+
+ cl_git_fail(git_clone(&g_repo, "git://example.com:asdf", "./foo", &g_options));
+ cl_git_fail(git_clone(&g_repo, "https://example.com:asdf/foo", "./foo", &g_options));
+ cl_git_fail(git_clone(&g_repo, "git://github.com/git://github.com/foo/bar.git.git",
+ "./foo", &g_options));
+ cl_git_fail(git_clone(&g_repo, "arrbee:my/bad:password@github.com:1111/strange:words.git",
+ "./foo", &g_options));
+}
+
+void test_clone_nonetwork__do_not_clean_existing_directory(void)
+{
+ /* Clone should not remove the directory if it already exists, but
+ * Should clean up entries it creates. */
+ p_mkdir("./foo", GIT_DIR_MODE);
+ cl_git_fail(git_clone(&g_repo, "not_a_repo", "./foo", &g_options));
+ cl_assert(git_fs_path_is_empty_dir("./foo"));
+
+ /* Try again with a bare repository. */
+ g_options.bare = true;
+ cl_git_fail(git_clone(&g_repo, "not_a_repo", "./foo", &g_options));
+ cl_assert(git_fs_path_is_empty_dir("./foo"));
+}
+
+void test_clone_nonetwork__local(void)
+{
+ cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
+}
+
+void test_clone_nonetwork__local_absolute_path(void)
+{
+ const char *local_src;
+ local_src = cl_fixture("testrepo.git");
+ cl_git_pass(git_clone(&g_repo, local_src, "./foo", &g_options));
+}
+
+void test_clone_nonetwork__local_bare(void)
+{
+ g_options.bare = true;
+ cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
+}
+
+void test_clone_nonetwork__fail_when_the_target_is_a_file(void)
+{
+ cl_git_mkfile("./foo", "Bar!");
+ cl_git_fail(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
+}
+
+void test_clone_nonetwork__fail_with_already_existing_but_non_empty_directory(void)
+{
+ p_mkdir("./foo", GIT_DIR_MODE);
+ cl_git_mkfile("./foo/bar", "Baz!");
+ cl_git_fail(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
+}
+
+static int custom_origin_name_remote_create(
+ git_remote **out,
+ git_repository *repo,
+ const char *name,
+ const char *url,
+ void *payload)
+{
+ GIT_UNUSED(name);
+ GIT_UNUSED(payload);
+
+ return git_remote_create(out, repo, "my_origin", url);
+}
+
+void test_clone_nonetwork__custom_origin_name(void)
+{
+ g_options.remote_cb = custom_origin_name_remote_create;
+ cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
+
+ cl_git_pass(git_remote_lookup(&g_remote, g_repo, "my_origin"));
+}
+
+void test_clone_nonetwork__defaults(void)
+{
+ cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", NULL));
+ cl_assert(g_repo);
+ cl_git_pass(git_remote_lookup(&g_remote, g_repo, "origin"));
+}
+
+void test_clone_nonetwork__cope_with_already_existing_directory(void)
+{
+ p_mkdir("./foo", GIT_DIR_MODE);
+ cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
+}
+
+void test_clone_nonetwork__can_prevent_the_checkout_of_a_standard_repo(void)
+{
+ git_str path = GIT_STR_INIT;
+
+ g_options.checkout_opts.checkout_strategy = 0;
+ cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
+
+ cl_git_pass(git_str_joinpath(&path, git_repository_workdir(g_repo), "master.txt"));
+ cl_assert_equal_i(false, git_fs_path_isfile(git_str_cstr(&path)));
+
+ git_str_dispose(&path);
+}
+
+void test_clone_nonetwork__can_checkout_given_branch(void)
+{
+ git_reference *remote_head;
+
+ g_options.checkout_branch = "test";
+ cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
+
+ cl_assert_equal_i(0, git_repository_head_unborn(g_repo));
+
+ cl_git_pass(git_repository_head(&g_ref, g_repo));
+ cl_assert_equal_s(git_reference_name(g_ref), "refs/heads/test");
+
+ cl_assert(git_fs_path_exists("foo/readme.txt"));
+
+ cl_git_pass(git_reference_lookup(&remote_head, g_repo, "refs/remotes/origin/HEAD"));
+ cl_assert_equal_i(GIT_REFERENCE_SYMBOLIC, git_reference_type(remote_head));
+ cl_assert_equal_s("refs/remotes/origin/master", git_reference_symbolic_target(remote_head));
+
+ git_reference_free(remote_head);
+}
+
+static int clone_cancel_fetch_transfer_progress_cb(
+ const git_indexer_progress *stats, void *data)
+{
+ GIT_UNUSED(stats); GIT_UNUSED(data);
+ return -54321;
+}
+
+void test_clone_nonetwork__can_cancel_clone_in_fetch(void)
+{
+ g_options.checkout_branch = "test";
+
+ g_options.fetch_opts.callbacks.transfer_progress =
+ clone_cancel_fetch_transfer_progress_cb;
+
+ cl_git_fail_with(git_clone(
+ &g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options),
+ -54321);
+
+ cl_assert(!g_repo);
+ cl_assert(!git_fs_path_exists("foo/readme.txt"));
+}
+
+static int clone_cancel_checkout_cb(
+ git_checkout_notify_t why,
+ const char *path,
+ const git_diff_file *b,
+ const git_diff_file *t,
+ const git_diff_file *w,
+ void *payload)
+{
+ const char *at_file = payload;
+ GIT_UNUSED(why); GIT_UNUSED(b); GIT_UNUSED(t); GIT_UNUSED(w);
+ if (!strcmp(path, at_file))
+ return -12345;
+ return 0;
+}
+
+void test_clone_nonetwork__can_cancel_clone_in_checkout(void)
+{
+ g_options.checkout_branch = "test";
+
+ g_options.checkout_opts.notify_flags = GIT_CHECKOUT_NOTIFY_UPDATED;
+ g_options.checkout_opts.notify_cb = clone_cancel_checkout_cb;
+ g_options.checkout_opts.notify_payload = "readme.txt";
+
+ cl_git_fail_with(git_clone(
+ &g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options),
+ -12345);
+
+ cl_assert(!g_repo);
+ cl_assert(!git_fs_path_exists("foo/readme.txt"));
+}
+
+void test_clone_nonetwork__can_detached_head(void)
+{
+ git_object *obj;
+ git_repository *cloned;
+ git_reference *cloned_head;
+
+ cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
+
+ cl_git_pass(git_revparse_single(&obj, g_repo, "master~1"));
+ cl_git_pass(git_repository_set_head_detached(g_repo, git_object_id(obj)));
+
+ cl_git_pass(git_clone(&cloned, "./foo", "./foo1", &g_options));
+
+ cl_assert(git_repository_head_detached(cloned));
+
+ cl_git_pass(git_repository_head(&cloned_head, cloned));
+ cl_assert_equal_oid(git_object_id(obj), git_reference_target(cloned_head));
+
+ git_object_free(obj);
+ git_reference_free(cloned_head);
+ git_repository_free(cloned);
+
+ cl_fixture_cleanup("./foo1");
+}
+
+void test_clone_nonetwork__clone_tag_to_tree(void)
+{
+ git_repository *stage;
+ git_index_entry entry;
+ git_index *index;
+ git_odb *odb;
+ git_oid tree_id;
+ git_tree *tree;
+ git_reference *tag;
+ git_tree_entry *tentry;
+ const char *file_path = "some/deep/path.txt";
+ const char *file_content = "some content\n";
+ const char *tag_name = "refs/tags/tree-tag";
+
+ stage = cl_git_sandbox_init("testrepo.git");
+ cl_git_pass(git_repository_odb(&odb, stage));
+ cl_git_pass(git_index__new(&index, GIT_OID_SHA1));
+
+ memset(&entry, 0, sizeof(git_index_entry));
+ entry.path = file_path;
+ entry.mode = GIT_FILEMODE_BLOB;
+ cl_git_pass(git_odb_write(&entry.id, odb, file_content, strlen(file_content), GIT_OBJECT_BLOB));
+
+ cl_git_pass(git_index_add(index, &entry));
+ cl_git_pass(git_index_write_tree_to(&tree_id, index, stage));
+ cl_git_pass(git_reference_create(&tag, stage, tag_name, &tree_id, 0, NULL));
+ git_reference_free(tag);
+ git_odb_free(odb);
+ git_index_free(index);
+
+ g_options.local = GIT_CLONE_NO_LOCAL;
+ cl_git_pass(git_clone(&g_repo, cl_git_path_url(git_repository_path(stage)), "./foo", &g_options));
+ git_repository_free(stage);
+
+ cl_git_pass(git_reference_lookup(&tag, g_repo, tag_name));
+ cl_git_pass(git_tree_lookup(&tree, g_repo, git_reference_target(tag)));
+ git_reference_free(tag);
+
+ cl_git_pass(git_tree_entry_bypath(&tentry, tree, file_path));
+ git_tree_entry_free(tentry);
+ git_tree_free(tree);
+
+ cl_fixture_cleanup("testrepo.git");
+}
+
+static void assert_correct_reflog(const char *name)
+{
+ git_reflog *log;
+ const git_reflog_entry *entry;
+ git_str expected_message = GIT_STR_INIT;
+
+ git_str_printf(&expected_message,
+ "clone: from %s", cl_git_fixture_url("testrepo.git"));
+
+ cl_git_pass(git_reflog_read(&log, g_repo, name));
+ cl_assert_equal_i(1, git_reflog_entrycount(log));
+ entry = git_reflog_entry_byindex(log, 0);
+ cl_assert_equal_s(expected_message.ptr, git_reflog_entry_message(entry));
+
+ git_reflog_free(log);
+
+ git_str_dispose(&expected_message);
+}
+
+void test_clone_nonetwork__clone_updates_reflog_properly(void)
+{
+ cl_git_pass(git_clone(&g_repo, cl_git_fixture_url("testrepo.git"), "./foo", &g_options));
+ assert_correct_reflog("HEAD");
+ assert_correct_reflog("refs/heads/master");
+}
+
+static void cleanup_repository(void *path)
+{
+ if (g_repo) {
+ git_repository_free(g_repo);
+ g_repo = NULL;
+ }
+
+ cl_fixture_cleanup((const char *)path);
+}
+
+void test_clone_nonetwork__clone_from_empty_sets_upstream(void)
+{
+ git_config *config;
+ git_repository *repo;
+ const char *str;
+
+ /* Create an empty repo to clone from */
+ cl_set_cleanup(&cleanup_repository, "./test1");
+ cl_git_pass(git_repository_init(&g_repo, "./test1", 0));
+ cl_set_cleanup(&cleanup_repository, "./repowithunborn");
+ cl_git_pass(git_clone(&repo, "./test1", "./repowithunborn", NULL));
+
+ cl_git_pass(git_repository_config_snapshot(&config, repo));
+
+ cl_git_pass(git_config_get_string(&str, config, "branch.master.remote"));
+ cl_assert_equal_s("origin", str);
+ cl_git_pass(git_config_get_string(&str, config, "branch.master.merge"));
+ cl_assert_equal_s("refs/heads/master", str);
+
+ git_config_free(config);
+ git_repository_free(repo);
+ cl_fixture_cleanup("./repowithunborn");
+}
diff --git a/tests/libgit2/clone/transport.c b/tests/libgit2/clone/transport.c
new file mode 100644
index 0000000..fa4f653
--- /dev/null
+++ b/tests/libgit2/clone/transport.c
@@ -0,0 +1,51 @@
+#include "clar_libgit2.h"
+
+#include "git2/clone.h"
+#include "git2/transport.h"
+#include "git2/sys/transport.h"
+#include "futils.h"
+
+static int custom_transport(
+ git_transport **out,
+ git_remote *owner,
+ void *payload)
+{
+ *((int*)payload) = 1;
+
+ return git_transport_local(out, owner, payload);
+}
+
+static int custom_transport_remote_create(
+ git_remote **out,
+ git_repository *repo,
+ const char *name,
+ const char *url,
+ void *payload)
+{
+ int error;
+
+ GIT_UNUSED(payload);
+
+ if ((error = git_remote_create(out, repo, name, url)) < 0)
+ return error;
+
+ return 0;
+}
+
+void test_clone_transport__custom_transport(void)
+{
+ git_repository *repo;
+ git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT;
+ int custom_transport_used = 0;
+
+ clone_opts.remote_cb = custom_transport_remote_create;
+ clone_opts.fetch_opts.callbacks.transport = custom_transport;
+ clone_opts.fetch_opts.callbacks.payload = &custom_transport_used;
+
+ cl_git_pass(git_clone(&repo, cl_fixture("testrepo.git"), "./custom_transport.git", &clone_opts));
+ git_repository_free(repo);
+
+ cl_git_pass(git_futils_rmdir_r("./custom_transport.git", NULL, GIT_RMDIR_REMOVE_FILES));
+
+ cl_assert(custom_transport_used == 1);
+}
diff --git a/tests/libgit2/commit/commit.c b/tests/libgit2/commit/commit.c
new file mode 100644
index 0000000..140f87d
--- /dev/null
+++ b/tests/libgit2/commit/commit.c
@@ -0,0 +1,189 @@
+#include "clar_libgit2.h"
+#include "commit.h"
+#include "git2/commit.h"
+
+static git_repository *_repo;
+
+void test_commit_commit__initialize(void)
+{
+ cl_fixture_sandbox("testrepo.git");
+ cl_git_pass(git_repository_open(&_repo, "testrepo.git"));
+}
+
+void test_commit_commit__cleanup(void)
+{
+ git_repository_free(_repo);
+ _repo = NULL;
+
+ cl_fixture_cleanup("testrepo.git");
+}
+
+void test_commit_commit__create_unexisting_update_ref(void)
+{
+ git_oid oid;
+ git_tree *tree;
+ git_commit *commit;
+ git_signature *s;
+ git_reference *ref;
+
+ git_oid__fromstr(&oid, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, _repo, &oid));
+
+ git_oid__fromstr(&oid, "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162", GIT_OID_SHA1);
+ cl_git_pass(git_tree_lookup(&tree, _repo, &oid));
+
+ cl_git_pass(git_signature_now(&s, "alice", "alice@example.com"));
+
+ cl_git_fail(git_reference_lookup(&ref, _repo, "refs/heads/foo/bar"));
+ cl_git_pass(git_commit_create(&oid, _repo, "refs/heads/foo/bar", s, s,
+ NULL, "some msg", tree, 1, (const git_commit **) &commit));
+
+ /* fail because the parent isn't the tip of the branch anymore */
+ cl_git_fail(git_commit_create(&oid, _repo, "refs/heads/foo/bar", s, s,
+ NULL, "some msg", tree, 1, (const git_commit **) &commit));
+
+ cl_git_pass(git_reference_lookup(&ref, _repo, "refs/heads/foo/bar"));
+ cl_assert_equal_oid(&oid, git_reference_target(ref));
+
+ git_tree_free(tree);
+ git_commit_free(commit);
+ git_signature_free(s);
+ git_reference_free(ref);
+}
+
+void test_commit_commit__create_initial_commit(void)
+{
+ git_oid oid;
+ git_tree *tree;
+ git_commit *commit;
+ git_signature *s;
+ git_reference *ref;
+
+ git_oid__fromstr(&oid, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, _repo, &oid));
+
+ git_oid__fromstr(&oid, "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162", GIT_OID_SHA1);
+ cl_git_pass(git_tree_lookup(&tree, _repo, &oid));
+
+ cl_git_pass(git_signature_now(&s, "alice", "alice@example.com"));
+
+ cl_git_fail(git_reference_lookup(&ref, _repo, "refs/heads/foo/bar"));
+ cl_git_pass(git_commit_create(&oid, _repo, "refs/heads/foo/bar", s, s,
+ NULL, "initial commit", tree, 0, NULL));
+
+ cl_git_pass(git_reference_lookup(&ref, _repo, "refs/heads/foo/bar"));
+
+ cl_assert_equal_oid(&oid, git_reference_target(ref));
+
+ git_tree_free(tree);
+ git_commit_free(commit);
+ git_signature_free(s);
+ git_reference_free(ref);
+}
+
+void test_commit_commit__create_initial_commit_parent_not_current(void)
+{
+ git_oid oid;
+ git_oid original_oid;
+ git_tree *tree;
+ git_commit *commit;
+ git_signature *s;
+
+ git_oid__fromstr(&oid, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, _repo, &oid));
+
+ git_oid__fromstr(&oid, "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162", GIT_OID_SHA1);
+ cl_git_pass(git_tree_lookup(&tree, _repo, &oid));
+
+ cl_git_pass(git_signature_now(&s, "alice", "alice@example.com"));
+
+ cl_git_pass(git_reference_name_to_id(&original_oid, _repo, "HEAD"));
+
+ cl_git_fail(git_commit_create(&oid, _repo, "HEAD", s, s,
+ NULL, "initial commit", tree, 0, NULL));
+
+ cl_git_pass(git_reference_name_to_id(&oid, _repo, "HEAD"));
+
+ cl_assert_equal_oid(&oid, &original_oid);
+
+ git_tree_free(tree);
+ git_commit_free(commit);
+ git_signature_free(s);
+}
+
+static void assert_commit_summary(const char *expected, const char *given)
+{
+ git_commit *dummy;
+
+ cl_assert(dummy = git__calloc(1, sizeof(struct git_commit)));
+
+ dummy->raw_message = git__strdup(given);
+ cl_assert_equal_s(expected, git_commit_summary(dummy));
+
+ git_commit__free(dummy);
+}
+
+static void assert_commit_body(const char *expected, const char *given)
+{
+ git_commit *dummy;
+
+ cl_assert(dummy = git__calloc(1, sizeof(struct git_commit)));
+
+ dummy->raw_message = git__strdup(given);
+ cl_assert_equal_s(expected, git_commit_body(dummy));
+
+ git_commit__free(dummy);
+}
+
+void test_commit_commit__summary(void)
+{
+ assert_commit_summary("One-liner with no trailing newline", "One-liner with no trailing newline");
+ assert_commit_summary("One-liner with trailing newline", "One-liner with trailing newline\n");
+ assert_commit_summary("One-liner with trailing newline and space", "One-liner with trailing newline and space\n ");
+ assert_commit_summary("Trimmed leading&trailing newlines", "\n\nTrimmed leading&trailing newlines\n\n");
+ assert_commit_summary("First paragraph only", "\nFirst paragraph only\n\n(There are more!)");
+ assert_commit_summary("First paragraph only with space in the next line", "\nFirst paragraph only with space in the next line\n \n(There are more!)");
+ assert_commit_summary("First paragraph only with spaces in the next line", "\nFirst paragraph only with spaces in the next line\n \n(There are more!)");
+ assert_commit_summary("First paragraph with unwrapped trailing\tlines", "\nFirst paragraph\nwith unwrapped\ntrailing\tlines\n\n(Yes, unwrapped!)");
+ assert_commit_summary("\tLeading tabs", "\tLeading\n\ttabs\n\nare preserved"); /* tabs around newlines are collapsed down to a single space */
+ assert_commit_summary(" Leading Spaces", " Leading\n Spaces\n\nare preserved"); /* spaces around newlines are collapsed down to a single space */
+ assert_commit_summary("Trailing tabs\tare removed", "Trailing tabs\tare removed\t\t");
+ assert_commit_summary("Trailing spaces are removed", "Trailing spaces are removed ");
+ assert_commit_summary("Trailing tabs", "Trailing tabs\t\n\nare removed");
+ assert_commit_summary("Trailing spaces", "Trailing spaces \n\nare removed");
+ assert_commit_summary("Newlines are replaced by spaces", "Newlines\nare\nreplaced by spaces\n");
+ assert_commit_summary(" Spaces after newlines are collapsed", "\n Spaces after newlines\n are\n collapsed\n "); /* newlines at the very beginning are ignored and not collapsed */
+ assert_commit_summary(" Spaces before newlines are collapsed", " \nSpaces before newlines \nare \ncollapsed \n");
+ assert_commit_summary(" Spaces around newlines are collapsed", " \n Spaces around newlines \n are \n collapsed \n ");
+ assert_commit_summary(" Trailing newlines are" , " \n Trailing newlines \n are \n\n collapsed \n ");
+ assert_commit_summary(" Trailing spaces are stripped", " \n Trailing spaces \n are stripped \n\n \n \t ");
+ assert_commit_summary("", "");
+ assert_commit_summary("", " ");
+ assert_commit_summary("", "\n");
+ assert_commit_summary("", "\n \n");
+}
+
+void test_commit_commit__body(void)
+{
+ assert_commit_body(NULL, "One-liner with no trailing newline");
+ assert_commit_body(NULL, "One-liner with trailing newline\n");
+ assert_commit_body(NULL, "\n\nTrimmed leading&trailing newlines\n\n");
+ assert_commit_body("(There are more!)", "\nFirst paragraph only\n\n(There are more!)");
+ assert_commit_body("(Yes, unwrapped!)", "\nFirst paragraph\nwith unwrapped\ntrailing\tlines\n\n(Yes, unwrapped!)");
+ assert_commit_body("are preserved", "\tLeading\n\ttabs\n\nare preserved"); /* tabs around newlines are collapsed down to a single space */
+ assert_commit_body("are preserved", " Leading\n Spaces\n\nare preserved"); /* spaces around newlines are collapsed down to a single space */
+ assert_commit_body(NULL, "Trailing tabs\tare removed\t\t");
+ assert_commit_body(NULL, "Trailing spaces are removed ");
+ assert_commit_body("are removed", "Trailing tabs\t\n\nare removed");
+ assert_commit_body("are removed", "Trailing spaces \n\nare removed");
+ assert_commit_body(NULL,"Newlines\nare\nreplaced by spaces\n");
+ assert_commit_body(NULL , "\n Spaces after newlines\n are\n collapsed\n "); /* newlines at the very beginning are ignored and not collapsed */
+ assert_commit_body(NULL , " \nSpaces before newlines \nare \ncollapsed \n");
+ assert_commit_body(NULL , " \n Spaces around newlines \n are \n collapsed \n ");
+ assert_commit_body("collapsed" , " \n Trailing newlines \n are \n\n collapsed \n ");
+ assert_commit_body(NULL, " \n Trailing spaces \n are stripped \n\n \n \t ");
+ assert_commit_body(NULL , "");
+ assert_commit_body(NULL , " ");
+ assert_commit_body(NULL , "\n");
+ assert_commit_body(NULL , "\n \n");
+}
diff --git a/tests/libgit2/commit/parent.c b/tests/libgit2/commit/parent.c
new file mode 100644
index 0000000..1ec96ba
--- /dev/null
+++ b/tests/libgit2/commit/parent.c
@@ -0,0 +1,60 @@
+#include "clar_libgit2.h"
+
+static git_repository *_repo;
+static git_commit *commit;
+
+void test_commit_parent__initialize(void)
+{
+ git_oid oid;
+
+ cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git")));
+
+ git_oid__fromstr(&oid, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, _repo, &oid));
+}
+
+void test_commit_parent__cleanup(void)
+{
+ git_commit_free(commit);
+ commit = NULL;
+
+ git_repository_free(_repo);
+ _repo = NULL;
+}
+
+static void assert_nth_gen_parent(unsigned int gen, const char *expected_oid)
+{
+ git_commit *parent = NULL;
+ int error;
+
+ error = git_commit_nth_gen_ancestor(&parent, commit, gen);
+
+ if (expected_oid != NULL) {
+ cl_assert_equal_i(0, error);
+ cl_assert_equal_i(0, git_oid_streq(git_commit_id(parent), expected_oid));
+ } else
+ cl_assert_equal_i(GIT_ENOTFOUND, error);
+
+ git_commit_free(parent);
+}
+
+/*
+ * $ git show be35~0
+ * commit be3563ae3f795b2b4353bcce3a527ad0a4f7f644
+ *
+ * $ git show be35~1
+ * commit 9fd738e8f7967c078dceed8190330fc8648ee56a
+ *
+ * $ git show be35~3
+ * commit 5b5b025afb0b4c913b4c338a42934a3863bf3644
+ *
+ * $ git show be35~42
+ * fatal: ambiguous argument 'be35~42': unknown revision or path not in the working tree.
+ */
+void test_commit_parent__can_retrieve_nth_generation_parent(void)
+{
+ assert_nth_gen_parent(0, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ assert_nth_gen_parent(1, "9fd738e8f7967c078dceed8190330fc8648ee56a");
+ assert_nth_gen_parent(3, "5b5b025afb0b4c913b4c338a42934a3863bf3644");
+ assert_nth_gen_parent(42, NULL);
+}
diff --git a/tests/libgit2/commit/parse.c b/tests/libgit2/commit/parse.c
new file mode 100644
index 0000000..3a1fc3d
--- /dev/null
+++ b/tests/libgit2/commit/parse.c
@@ -0,0 +1,553 @@
+#include "clar_libgit2.h"
+#include <git2/types.h>
+#include "commit.h"
+#include "signature.h"
+
+/* Fixture setup */
+static git_repository *g_repo;
+void test_commit_parse__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+void test_commit_parse__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+
+/* Header parsing */
+typedef struct {
+ const char *line;
+ const char *header;
+} parse_test_case;
+
+static parse_test_case passing_header_cases[] = {
+ { "parent 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "parent " },
+ { "tree 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree " },
+ { "random_heading 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "random_heading " },
+ { "stuck_heading05452d6349abcd67aa396dfb28660d765d8b2a36\n", "stuck_heading" },
+ { "tree 5F4BEFFC0759261D015AA63A3A85613FF2F235DE\n", "tree " },
+ { "tree 1A669B8AB81B5EB7D9DB69562D34952A38A9B504\n", "tree " },
+ { "tree 5B20DCC6110FCC75D31C6CEDEBD7F43ECA65B503\n", "tree " },
+ { "tree 173E7BF00EA5C33447E99E6C1255954A13026BE4\n", "tree " },
+ { NULL, NULL }
+};
+
+static parse_test_case failing_header_cases[] = {
+ { "parent 05452d6349abcd67aa396dfb28660d765d8b2a36", "parent " },
+ { "05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree " },
+ { "parent05452d6349abcd67aa396dfb28660d765d8b2a6a\n", "parent " },
+ { "parent 05452d6349abcd67aa396dfb280d765d8b2a6\n", "parent " },
+ { "tree 05452d6349abcd67aa396dfb28660d765d8b2a36\n", "tree " },
+ { "parent 0545xd6349abcd67aa396dfb28660d765d8b2a36\n", "parent " },
+ { "parent 0545xd6349abcd67aa396dfb28660d765d8b2a36FF\n", "parent " },
+ { "", "tree " },
+ { "", "" },
+ { NULL, NULL }
+};
+
+void test_commit_parse__header(void)
+{
+ git_oid oid;
+
+ parse_test_case *testcase;
+ for (testcase = passing_header_cases; testcase->line != NULL; testcase++)
+ {
+ const char *line = testcase->line;
+ const char *line_end = line + strlen(line);
+
+ cl_git_pass(git_object__parse_oid_header(&oid,
+ &line, line_end, testcase->header, GIT_OID_SHA1));
+ cl_assert(line == line_end);
+ }
+
+ for (testcase = failing_header_cases; testcase->line != NULL; testcase++)
+ {
+ const char *line = testcase->line;
+ const char *line_end = line + strlen(line);
+
+ cl_git_fail(git_object__parse_oid_header(&oid,
+ &line, line_end, testcase->header, GIT_OID_SHA1));
+ }
+}
+
+
+/* Signature parsing */
+typedef struct {
+ const char *string;
+ const char *header;
+ const char *name;
+ const char *email;
+ git_time_t time;
+ int offset;
+} passing_signature_test_case;
+
+passing_signature_test_case passing_signature_cases[] = {
+ {"author Vicent Marti <tanoku@gmail.com> 12345 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 12345, 0},
+ {"author Vicent Marti <> 12345 \n", "author ", "Vicent Marti", "", 12345, 0},
+ {"author Vicent Marti <tanoku@gmail.com> 231301 +1020\n", "author ", "Vicent Marti", "tanoku@gmail.com", 231301, 620},
+ {"author Vicent Marti with an outrageously long name which will probably overflow the buffer <tanoku@gmail.com> 12345 \n", "author ", "Vicent Marti with an outrageously long name which will probably overflow the buffer", "tanoku@gmail.com", 12345, 0},
+ {"author Vicent Marti <tanokuwithaveryveryverylongemailwhichwillprobablyvoverflowtheemailbuffer@gmail.com> 12345 \n", "author ", "Vicent Marti", "tanokuwithaveryveryverylongemailwhichwillprobablyvoverflowtheemailbuffer@gmail.com", 12345, 0},
+ {"committer Vicent Marti <tanoku@gmail.com> 123456 +0000 \n", "committer ", "Vicent Marti", "tanoku@gmail.com", 123456, 0},
+ {"committer Vicent Marti <tanoku@gmail.com> 123456 +0100 \n", "committer ", "Vicent Marti", "tanoku@gmail.com", 123456, 60},
+ {"committer Vicent Marti <tanoku@gmail.com> 123456 -0100 \n", "committer ", "Vicent Marti", "tanoku@gmail.com", 123456, -60},
+ /* Parse a signature without an author field */
+ {"committer <tanoku@gmail.com> 123456 -0100 \n", "committer ", "", "tanoku@gmail.com", 123456, -60},
+ /* Parse a signature without an author field */
+ {"committer <tanoku@gmail.com> 123456 -0100 \n", "committer ", "", "tanoku@gmail.com", 123456, -60},
+ /* Parse a signature with an empty author field */
+ {"committer <tanoku@gmail.com> 123456 -0100 \n", "committer ", "", "tanoku@gmail.com", 123456, -60},
+ /* Parse a signature with an empty email field */
+ {"committer Vicent Marti <> 123456 -0100 \n", "committer ", "Vicent Marti", "", 123456, -60},
+ /* Parse a signature with an empty email field */
+ {"committer Vicent Marti < > 123456 -0100 \n", "committer ", "Vicent Marti", "", 123456, -60},
+ /* Parse a signature with empty name and email */
+ {"committer <> 123456 -0100 \n", "committer ", "", "", 123456, -60},
+ /* Parse a signature with empty name and email */
+ {"committer <> 123456 -0100 \n", "committer ", "", "", 123456, -60},
+ /* Parse a signature with empty name and email */
+ {"committer < > 123456 -0100 \n", "committer ", "", "", 123456, -60},
+ /* Parse an obviously invalid signature */
+ {"committer foo<@bar> 123456 -0100 \n", "committer ", "foo", "@bar", 123456, -60},
+ /* Parse an obviously invalid signature */
+ {"committer foo<@bar> 123456 -0100 \n", "committer ", "foo", "@bar", 123456, -60},
+ /* Parse an obviously invalid signature */
+ {"committer <>\n", "committer ", "", "", 0, 0},
+ {"committer Vicent Marti <tanoku@gmail.com> 123456 -1500 \n", "committer ", "Vicent Marti", "tanoku@gmail.com", 123456, 0},
+ {"committer Vicent Marti <tanoku@gmail.com> 123456 +0163 \n", "committer ", "Vicent Marti", "tanoku@gmail.com", 123456, 0},
+ {"author Vicent Marti <tanoku@gmail.com>\n", "author ", "Vicent Marti", "tanoku@gmail.com", 0, 0},
+ /* a variety of dates */
+ {"author Vicent Marti <tanoku@gmail.com> 0 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 0, 0},
+ {"author Vicent Marti <tanoku@gmail.com> 1234567890 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 1234567890, 0},
+ {"author Vicent Marti <tanoku@gmail.com> 2147483647 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 0x7fffffff, 0},
+ {"author Vicent Marti <tanoku@gmail.com> 4294967295 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 0xffffffff, 0},
+ {"author Vicent Marti <tanoku@gmail.com> 4294967296 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 4294967296, 0},
+ {"author Vicent Marti <tanoku@gmail.com> 8589934592 \n", "author ", "Vicent Marti", "tanoku@gmail.com", 8589934592, 0},
+
+ {NULL,NULL,NULL,NULL,0,0}
+};
+
+typedef struct {
+ const char *string;
+ const char *header;
+} failing_signature_test_case;
+
+failing_signature_test_case failing_signature_cases[] = {
+ {"committer Vicent Marti tanoku@gmail.com> 123456 -0100 \n", "committer "},
+ {"author Vicent Marti <tanoku@gmail.com> 12345 \n", "author "},
+ {"author Vicent Marti <tanoku@gmail.com> 12345 \n", "committer "},
+ {"author Vicent Marti 12345 \n", "author "},
+ {"author Vicent Marti <broken@email 12345 \n", "author "},
+ {"committer Vicent Marti ><\n", "committer "},
+ {"author ", "author "},
+ {NULL, NULL,}
+};
+
+void test_commit_parse__signature(void)
+{
+ passing_signature_test_case *passcase;
+ failing_signature_test_case *failcase;
+
+ for (passcase = passing_signature_cases; passcase->string != NULL; passcase++)
+ {
+ const char *str = passcase->string;
+ size_t len = strlen(passcase->string);
+ struct git_signature person = {0};
+
+ cl_git_pass(git_signature__parse(&person, &str, str + len, passcase->header, '\n'));
+ cl_assert_equal_s(passcase->name, person.name);
+ cl_assert_equal_s(passcase->email, person.email);
+ cl_assert_equal_i((int)passcase->time, (int)person.when.time);
+ cl_assert_equal_i(passcase->offset, person.when.offset);
+ git__free(person.name); git__free(person.email);
+ }
+
+ for (failcase = failing_signature_cases; failcase->string != NULL; failcase++)
+ {
+ const char *str = failcase->string;
+ size_t len = strlen(failcase->string);
+ git_signature person = {0};
+ cl_git_fail(git_signature__parse(&person, &str, str + len, failcase->header, '\n'));
+ git__free(person.name); git__free(person.email);
+ }
+}
+
+
+
+static char *failing_commit_cases[] = {
+/* empty commit */
+"",
+/* random garbage */
+"asd97sa9du902e9a0jdsuusad09as9du098709aweu8987sd\n",
+/* broken endlines 1 */
+"tree f6c0dad3c7b3481caa9d73db21f91964894a945b\r\n\
+parent 05452d6349abcd67aa396dfb28660d765d8b2a36\r\n\
+author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\r\n\
+committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\r\n\
+\r\n\
+a test commit with broken endlines\r\n",
+/* broken endlines 2 */
+"tree f6c0dad3c7b3481caa9d73db21f91964894a945b\
+parent 05452d6349abcd67aa396dfb28660d765d8b2a36\
+author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\
+committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\
+\
+another test commit with broken endlines",
+/* starting endlines */
+"\ntree f6c0dad3c7b3481caa9d73db21f91964894a945b\n\
+parent 05452d6349abcd67aa396dfb28660d765d8b2a36\n\
+author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
+committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
+\n\
+a test commit with a starting endline\n",
+/* corrupted commit 1 */
+"tree f6c0dad3c7b3481caa9d73db21f91964894a945b\n\
+parent 05452d6349abcd67aa396df",
+/* corrupted commit 2 */
+"tree f6c0dad3c7b3481caa9d73db21f91964894a945b\n\
+parent ",
+/* corrupted commit 3 */
+"tree f6c0dad3c7b3481caa9d73db21f91964894a945b\n\
+parent ",
+/* corrupted commit 4 */
+"tree f6c0dad3c7b3481caa9d73db21f91964894a945b\n\
+par",
+};
+
+
+static char *passing_commit_cases[] = {
+/* simple commit with no message */
+"tree 1810dff58d8a660512d4832e740f692884338ccd\n\
+author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
+committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
+\n",
+/* simple commit, no parent */
+"tree 1810dff58d8a660512d4832e740f692884338ccd\n\
+author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
+committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
+\n\
+a simple commit which works\n",
+/* simple commit, no parent, no newline in message */
+"tree 1810dff58d8a660512d4832e740f692884338ccd\n\
+author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
+committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
+\n\
+a simple commit which works",
+/* simple commit, 1 parent */
+"tree 1810dff58d8a660512d4832e740f692884338ccd\n\
+parent e90810b8df3e80c413d903f631643c716887138d\n\
+author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
+committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
+\n\
+a simple commit which works\n",
+/* simple commit with GPG signature */
+"tree 6b79e22d69bf46e289df0345a14ca059dfc9bdf6\n\
+parent 34734e478d6cf50c27c9d69026d93974d052c454\n\
+author Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
+committer Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
+gpgsig -----BEGIN PGP SIGNATURE-----\n\
+ Version: GnuPG v1.4.12 (Darwin)\n\
+ \n\
+ iQIcBAABAgAGBQJQ+FMIAAoJEH+LfPdZDSs1e3EQAJMjhqjWF+WkGLHju7pTw2al\n\
+ o6IoMAhv0Z/LHlWhzBd9e7JeCnanRt12bAU7yvYp9+Z+z+dbwqLwDoFp8LVuigl8\n\
+ JGLcnwiUW3rSvhjdCp9irdb4+bhKUnKUzSdsR2CK4/hC0N2i/HOvMYX+BRsvqweq\n\
+ AsAkA6dAWh+gAfedrBUkCTGhlNYoetjdakWqlGL1TiKAefEZrtA1TpPkGn92vbLq\n\
+ SphFRUY9hVn1ZBWrT3hEpvAIcZag3rTOiRVT1X1flj8B2vGCEr3RrcwOIZikpdaW\n\
+ who/X3xh/DGbI2RbuxmmJpxxP/8dsVchRJJzBwG+yhwU/iN3MlV2c5D69tls/Dok\n\
+ 6VbyU4lm/ae0y3yR83D9dUlkycOnmmlBAHKIZ9qUts9X7mWJf0+yy2QxJVpjaTGG\n\
+ cmnQKKPeNIhGJk2ENnnnzjEve7L7YJQF6itbx5VCOcsGh3Ocb3YR7DMdWjt7f8pu\n\
+ c6j+q1rP7EpE2afUN/geSlp5i3x8aXZPDj67jImbVCE/Q1X9voCtyzGJH7MXR0N9\n\
+ ZpRF8yzveRfMH8bwAJjSOGAFF5XkcR/RNY95o+J+QcgBLdX48h+ZdNmUf6jqlu3J\n\
+ 7KmTXXQcOVpN6dD3CmRFsbjq+x6RHwa8u1iGn+oIkX908r97ckfB/kHKH7ZdXIJc\n\
+ cpxtDQQMGYFpXK/71stq\n\
+ =ozeK\n\
+ -----END PGP SIGNATURE-----\n\
+\n\
+a simple commit which works\n",
+/* some tools create two author entries */
+"tree 1810dff58d8a660512d4832e740f692884338ccd\n\
+author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
+author Helpful Coworker <helpful@coworker> 1273848544 +0200\n\
+committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
+\n\
+a simple commit which works",
+};
+
+static int parse_commit(git_commit **out, const char *buffer)
+{
+ git_commit *commit;
+ git_odb_object fake_odb_object;
+ int error;
+
+ commit = (git_commit*)git__malloc(sizeof(git_commit));
+ memset(commit, 0x0, sizeof(git_commit));
+ commit->object.repo = g_repo;
+
+ memset(&fake_odb_object, 0x0, sizeof(git_odb_object));
+ fake_odb_object.buffer = (char *)buffer;
+ fake_odb_object.cached.size = strlen(fake_odb_object.buffer);
+
+ error = git_commit__parse(commit, &fake_odb_object, GIT_OID_SHA1);
+
+ *out = commit;
+ return error;
+}
+
+void test_commit_parse__entire_commit(void)
+{
+ const int failing_commit_count = ARRAY_SIZE(failing_commit_cases);
+ const int passing_commit_count = ARRAY_SIZE(passing_commit_cases);
+ int i;
+ git_commit *commit;
+
+ for (i = 0; i < failing_commit_count; ++i) {
+ cl_git_fail(parse_commit(&commit, failing_commit_cases[i]));
+ git_commit__free(commit);
+ }
+
+ for (i = 0; i < passing_commit_count; ++i) {
+ cl_git_pass(parse_commit(&commit, passing_commit_cases[i]));
+
+ if (!i)
+ cl_assert_equal_s("", git_commit_message(commit));
+ else
+ cl_assert(git__prefixcmp(
+ git_commit_message(commit), "a simple commit which works") == 0);
+
+ git_commit__free(commit);
+ }
+}
+
+
+/* query the details on a parsed commit */
+void test_commit_parse__details0(void) {
+ static const char *commit_ids[] = {
+ "a4a7dce85cf63874e984719f4fdd239f5145052f", /* 0 */
+ "9fd738e8f7967c078dceed8190330fc8648ee56a", /* 1 */
+ "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", /* 2 */
+ "c47800c7266a2be04c571c04d5a6614691ea99bd", /* 3 */
+ "8496071c1b46c854b31185ea97743be6a8774479", /* 4 */
+ "5b5b025afb0b4c913b4c338a42934a3863bf3644", /* 5 */
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", /* 6 */
+ };
+ const size_t commit_count = sizeof(commit_ids) / sizeof(const char *);
+ unsigned int i;
+
+ for (i = 0; i < commit_count; ++i) {
+ git_oid id;
+ git_commit *commit;
+
+ const git_signature *author, *committer;
+ const char *message;
+ git_time_t commit_time;
+ unsigned int parents, p;
+ git_commit *parent = NULL, *old_parent = NULL;
+
+ git_oid__fromstr(&id, commit_ids[i], GIT_OID_SHA1);
+
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &id));
+
+ message = git_commit_message(commit);
+ author = git_commit_author(commit);
+ committer = git_commit_committer(commit);
+ commit_time = git_commit_time(commit);
+ parents = git_commit_parentcount(commit);
+
+ cl_assert_equal_s("Scott Chacon", author->name);
+ cl_assert_equal_s("schacon@gmail.com", author->email);
+ cl_assert_equal_s("Scott Chacon", committer->name);
+ cl_assert_equal_s("schacon@gmail.com", committer->email);
+ cl_assert(message != NULL);
+ cl_assert(commit_time > 0);
+ cl_assert(parents <= 2);
+ for (p = 0;p < parents;p++) {
+ if (old_parent != NULL)
+ git_commit_free(old_parent);
+
+ old_parent = parent;
+ cl_git_pass(git_commit_parent(&parent, commit, p));
+ cl_assert(parent != NULL);
+ cl_assert(git_commit_author(parent) != NULL); /* is it really a commit? */
+ }
+ git_commit_free(old_parent);
+ git_commit_free(parent);
+
+ cl_git_fail(git_commit_parent(&parent, commit, parents));
+ git_commit_free(commit);
+ }
+}
+
+void test_commit_parse__leading_lf(void)
+{
+ git_commit *commit;
+ const char *buffer =
+"tree 1810dff58d8a660512d4832e740f692884338ccd\n\
+parent e90810b8df3e80c413d903f631643c716887138d\n\
+author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
+committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
+\n\
+\n\
+\n\
+This commit has a few LF at the start of the commit message";
+ const char *message =
+"This commit has a few LF at the start of the commit message";
+ const char *raw_message =
+"\n\
+\n\
+This commit has a few LF at the start of the commit message";
+ cl_git_pass(parse_commit(&commit, buffer));
+ cl_assert_equal_s(message, git_commit_message(commit));
+ cl_assert_equal_s(raw_message, git_commit_message_raw(commit));
+ git_commit__free(commit);
+}
+
+void test_commit_parse__only_lf(void)
+{
+ git_commit *commit;
+ const char *buffer =
+"tree 1810dff58d8a660512d4832e740f692884338ccd\n\
+parent e90810b8df3e80c413d903f631643c716887138d\n\
+author Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
+committer Vicent Marti <tanoku@gmail.com> 1273848544 +0200\n\
+\n\
+\n\
+\n";
+ const char *message = "";
+ const char *raw_message = "\n\n";
+
+ cl_git_pass(parse_commit(&commit, buffer));
+ cl_assert_equal_s(message, git_commit_message(commit));
+ cl_assert_equal_s(raw_message, git_commit_message_raw(commit));
+ git_commit__free(commit);
+}
+
+void test_commit_parse__arbitrary_field(void)
+{
+ git_commit *commit;
+ git_buf buf = GIT_BUF_INIT;
+ const char *gpgsig = "-----BEGIN PGP SIGNATURE-----\n\
+Version: GnuPG v1.4.12 (Darwin)\n\
+\n\
+iQIcBAABAgAGBQJQ+FMIAAoJEH+LfPdZDSs1e3EQAJMjhqjWF+WkGLHju7pTw2al\n\
+o6IoMAhv0Z/LHlWhzBd9e7JeCnanRt12bAU7yvYp9+Z+z+dbwqLwDoFp8LVuigl8\n\
+JGLcnwiUW3rSvhjdCp9irdb4+bhKUnKUzSdsR2CK4/hC0N2i/HOvMYX+BRsvqweq\n\
+AsAkA6dAWh+gAfedrBUkCTGhlNYoetjdakWqlGL1TiKAefEZrtA1TpPkGn92vbLq\n\
+SphFRUY9hVn1ZBWrT3hEpvAIcZag3rTOiRVT1X1flj8B2vGCEr3RrcwOIZikpdaW\n\
+who/X3xh/DGbI2RbuxmmJpxxP/8dsVchRJJzBwG+yhwU/iN3MlV2c5D69tls/Dok\n\
+6VbyU4lm/ae0y3yR83D9dUlkycOnmmlBAHKIZ9qUts9X7mWJf0+yy2QxJVpjaTGG\n\
+cmnQKKPeNIhGJk2ENnnnzjEve7L7YJQF6itbx5VCOcsGh3Ocb3YR7DMdWjt7f8pu\n\
+c6j+q1rP7EpE2afUN/geSlp5i3x8aXZPDj67jImbVCE/Q1X9voCtyzGJH7MXR0N9\n\
+ZpRF8yzveRfMH8bwAJjSOGAFF5XkcR/RNY95o+J+QcgBLdX48h+ZdNmUf6jqlu3J\n\
+7KmTXXQcOVpN6dD3CmRFsbjq+x6RHwa8u1iGn+oIkX908r97ckfB/kHKH7ZdXIJc\n\
+cpxtDQQMGYFpXK/71stq\n\
+=ozeK\n\
+-----END PGP SIGNATURE-----";
+
+ cl_git_pass(parse_commit(&commit, passing_commit_cases[4]));
+
+ cl_git_pass(git_commit_header_field(&buf, commit, "tree"));
+ cl_assert_equal_s("6b79e22d69bf46e289df0345a14ca059dfc9bdf6", buf.ptr);
+ git_buf_dispose(&buf);
+
+ cl_git_pass(git_commit_header_field(&buf, commit, "parent"));
+ cl_assert_equal_s("34734e478d6cf50c27c9d69026d93974d052c454", buf.ptr);
+ git_buf_dispose(&buf);
+
+ cl_git_pass(git_commit_header_field(&buf, commit, "gpgsig"));
+ cl_assert_equal_s(gpgsig, buf.ptr);
+ git_buf_dispose(&buf);
+
+ cl_git_fail_with(GIT_ENOTFOUND, git_commit_header_field(&buf, commit, "awesomeness"));
+ cl_git_fail_with(GIT_ENOTFOUND, git_commit_header_field(&buf, commit, "par"));
+
+ git_commit__free(commit);
+ cl_git_pass(parse_commit(&commit, passing_commit_cases[0]));
+
+ cl_git_pass(git_commit_header_field(&buf, commit, "committer"));
+ cl_assert_equal_s("Vicent Marti <tanoku@gmail.com> 1273848544 +0200", buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_commit__free(commit);
+}
+
+void test_commit_parse__extract_signature(void)
+{
+ git_odb *odb;
+ git_oid commit_id;
+ git_buf signature = GIT_BUF_INIT, signed_data = GIT_BUF_INIT;
+ const char *gpgsig = "-----BEGIN PGP SIGNATURE-----\n\
+Version: GnuPG v1.4.12 (Darwin)\n\
+\n\
+iQIcBAABAgAGBQJQ+FMIAAoJEH+LfPdZDSs1e3EQAJMjhqjWF+WkGLHju7pTw2al\n\
+o6IoMAhv0Z/LHlWhzBd9e7JeCnanRt12bAU7yvYp9+Z+z+dbwqLwDoFp8LVuigl8\n\
+JGLcnwiUW3rSvhjdCp9irdb4+bhKUnKUzSdsR2CK4/hC0N2i/HOvMYX+BRsvqweq\n\
+AsAkA6dAWh+gAfedrBUkCTGhlNYoetjdakWqlGL1TiKAefEZrtA1TpPkGn92vbLq\n\
+SphFRUY9hVn1ZBWrT3hEpvAIcZag3rTOiRVT1X1flj8B2vGCEr3RrcwOIZikpdaW\n\
+who/X3xh/DGbI2RbuxmmJpxxP/8dsVchRJJzBwG+yhwU/iN3MlV2c5D69tls/Dok\n\
+6VbyU4lm/ae0y3yR83D9dUlkycOnmmlBAHKIZ9qUts9X7mWJf0+yy2QxJVpjaTGG\n\
+cmnQKKPeNIhGJk2ENnnnzjEve7L7YJQF6itbx5VCOcsGh3Ocb3YR7DMdWjt7f8pu\n\
+c6j+q1rP7EpE2afUN/geSlp5i3x8aXZPDj67jImbVCE/Q1X9voCtyzGJH7MXR0N9\n\
+ZpRF8yzveRfMH8bwAJjSOGAFF5XkcR/RNY95o+J+QcgBLdX48h+ZdNmUf6jqlu3J\n\
+7KmTXXQcOVpN6dD3CmRFsbjq+x6RHwa8u1iGn+oIkX908r97ckfB/kHKH7ZdXIJc\n\
+cpxtDQQMGYFpXK/71stq\n\
+=ozeK\n\
+-----END PGP SIGNATURE-----";
+
+ const char *data = "tree 6b79e22d69bf46e289df0345a14ca059dfc9bdf6\n\
+parent 34734e478d6cf50c27c9d69026d93974d052c454\n\
+author Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
+committer Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
+\n\
+a simple commit which works\n";
+
+ const char *oneline_signature = "tree 51832e6397b30309c8bcad9c55fa6ae67778f378\n\
+parent a1b6decaaac768b5e01e1b5dbf5b2cc081bed1eb\n\
+author Some User <someuser@gmail.com> 1454537944 -0700\n\
+committer Some User <someuser@gmail.com> 1454537944 -0700\n\
+gpgsig bad\n\
+\n\
+corrupt signature\n";
+
+ const char *oneline_data = "tree 51832e6397b30309c8bcad9c55fa6ae67778f378\n\
+parent a1b6decaaac768b5e01e1b5dbf5b2cc081bed1eb\n\
+author Some User <someuser@gmail.com> 1454537944 -0700\n\
+committer Some User <someuser@gmail.com> 1454537944 -0700\n\
+\n\
+corrupt signature\n";
+
+ cl_git_pass(git_repository_odb__weakptr(&odb, g_repo));
+ cl_git_pass(git_odb_write(&commit_id, odb, passing_commit_cases[4], strlen(passing_commit_cases[4]), GIT_OBJECT_COMMIT));
+
+ cl_git_pass(git_commit_extract_signature(&signature, &signed_data, g_repo, &commit_id, NULL));
+ cl_assert_equal_s(gpgsig, signature.ptr);
+ cl_assert_equal_s(data, signed_data.ptr);
+
+ git_buf_dispose(&signature);
+ git_buf_dispose(&signed_data);
+
+ cl_git_pass(git_commit_extract_signature(&signature, &signed_data, g_repo, &commit_id, "gpgsig"));
+ cl_assert_equal_s(gpgsig, signature.ptr);
+ cl_assert_equal_s(data, signed_data.ptr);
+
+ git_buf_dispose(&signature);
+ git_buf_dispose(&signed_data);
+
+ /* Try to parse a tree */
+ cl_git_pass(git_oid__fromstr(&commit_id, "45dd856fdd4d89b884c340ba0e047752d9b085d6", GIT_OID_SHA1));
+ cl_git_fail_with(GIT_ENOTFOUND, git_commit_extract_signature(&signature, &signed_data, g_repo, &commit_id, NULL));
+ cl_assert_equal_i(GIT_ERROR_INVALID, git_error_last()->klass);
+
+ /* Try to parse an unsigned commit */
+ cl_git_pass(git_odb_write(&commit_id, odb, passing_commit_cases[1], strlen(passing_commit_cases[1]), GIT_OBJECT_COMMIT));
+ cl_git_fail_with(GIT_ENOTFOUND, git_commit_extract_signature(&signature, &signed_data, g_repo, &commit_id, NULL));
+ cl_assert_equal_i(GIT_ERROR_OBJECT, git_error_last()->klass);
+
+ /* Parse the commit with a single-line signature */
+ cl_git_pass(git_odb_write(&commit_id, odb, oneline_signature, strlen(oneline_signature), GIT_OBJECT_COMMIT));
+ cl_git_pass(git_commit_extract_signature(&signature, &signed_data, g_repo, &commit_id, NULL));
+ cl_assert_equal_s("bad", signature.ptr);
+ cl_assert_equal_s(oneline_data, signed_data.ptr);
+
+ git_buf_dispose(&signature);
+ git_buf_dispose(&signed_data);
+}
diff --git a/tests/libgit2/commit/signature.c b/tests/libgit2/commit/signature.c
new file mode 100644
index 0000000..a915514
--- /dev/null
+++ b/tests/libgit2/commit/signature.c
@@ -0,0 +1,148 @@
+#include "clar_libgit2.h"
+#include "signature.h"
+
+static int try_build_signature(const char *name, const char *email, git_time_t time, int offset)
+{
+ git_signature *sign;
+ int error = 0;
+
+ if ((error = git_signature_new(&sign, name, email, time, offset)) < 0)
+ return error;
+
+ git_signature_free((git_signature *)sign);
+
+ return error;
+}
+
+static void assert_name_and_email(
+ const char *expected_name,
+ const char *expected_email,
+ const char *name,
+ const char *email)
+{
+ git_signature *sign;
+
+ cl_git_pass(git_signature_new(&sign, name, email, 1234567890, 60));
+ cl_assert_equal_s(expected_name, sign->name);
+ cl_assert_equal_s(expected_email, sign->email);
+
+ git_signature_free(sign);
+}
+
+void test_commit_signature__leading_and_trailing_spaces_are_trimmed(void)
+{
+ assert_name_and_email("nulltoken", "emeric.fermas@gmail.com", " nulltoken ", " emeric.fermas@gmail.com ");
+ assert_name_and_email("nulltoken", "emeric.fermas@gmail.com", " nulltoken ", " emeric.fermas@gmail.com \n");
+ assert_name_and_email("nulltoken", "emeric.fermas@gmail.com", " \t nulltoken \n", " \n emeric.fermas@gmail.com \n");
+}
+
+void test_commit_signature__leading_and_trailing_crud_is_trimmed(void)
+{
+ assert_name_and_email("nulltoken", "emeric.fermas@gmail.com", "\"nulltoken\"", "\"emeric.fermas@gmail.com\"");
+ assert_name_and_email("nulltoken w", "emeric.fermas@gmail.com", "nulltoken w.", "emeric.fermas@gmail.com");
+ assert_name_and_email("nulltoken \xe2\x98\xba", "emeric.fermas@gmail.com", "nulltoken \xe2\x98\xba", "emeric.fermas@gmail.com");
+}
+
+void test_commit_signature__timezone_does_not_read_oob(void)
+{
+ const char *header = "A <a@example.com> 1461698487 +1234", *header_end;
+ git_signature *sig;
+
+ /* Let the buffer end midway between the timezone offeset's "+12" and "34" */
+ header_end = header + strlen(header) - 2;
+
+ sig = git__calloc(1, sizeof(git_signature));
+ cl_assert(sig);
+
+ cl_git_pass(git_signature__parse(sig, &header, header_end, NULL, '\0'));
+ cl_assert_equal_s(sig->name, "A");
+ cl_assert_equal_s(sig->email, "a@example.com");
+ cl_assert_equal_i(sig->when.time, 1461698487);
+ cl_assert_equal_i(sig->when.offset, 12);
+
+ git_signature_free(sig);
+}
+
+void test_commit_signature__angle_brackets_in_names_are_not_supported(void)
+{
+ cl_git_fail(try_build_signature("<Phil Haack", "phil@haack", 1234567890, 60));
+ cl_git_fail(try_build_signature("Phil>Haack", "phil@haack", 1234567890, 60));
+ cl_git_fail(try_build_signature("<Phil Haack>", "phil@haack", 1234567890, 60));
+}
+
+void test_commit_signature__angle_brackets_in_email_are_not_supported(void)
+{
+ cl_git_fail(try_build_signature("Phil Haack", ">phil@haack", 1234567890, 60));
+ cl_git_fail(try_build_signature("Phil Haack", "phil@>haack", 1234567890, 60));
+ cl_git_fail(try_build_signature("Phil Haack", "<phil@haack>", 1234567890, 60));
+}
+
+void test_commit_signature__create_empties(void)
+{
+ /* can not create a signature with empty name or email */
+ cl_git_pass(try_build_signature("nulltoken", "emeric.fermas@gmail.com", 1234567890, 60));
+
+ cl_git_fail(try_build_signature("", "emeric.fermas@gmail.com", 1234567890, 60));
+ cl_git_fail(try_build_signature(" ", "emeric.fermas@gmail.com", 1234567890, 60));
+ cl_git_fail(try_build_signature("nulltoken", "", 1234567890, 60));
+ cl_git_fail(try_build_signature("nulltoken", " ", 1234567890, 60));
+}
+
+void test_commit_signature__create_one_char(void)
+{
+ /* creating a one character signature */
+ assert_name_and_email("x", "foo@bar.baz", "x", "foo@bar.baz");
+}
+
+void test_commit_signature__create_two_char(void)
+{
+ /* creating a two character signature */
+ assert_name_and_email("xx", "foo@bar.baz", "xx", "foo@bar.baz");
+}
+
+void test_commit_signature__create_zero_char(void)
+{
+ /* creating a zero character signature */
+ git_signature *sign;
+ cl_git_fail(git_signature_new(&sign, "", "x@y.z", 1234567890, 60));
+ cl_assert(sign == NULL);
+}
+
+void test_commit_signature__from_buf(void)
+{
+ git_signature *sign;
+
+ cl_git_pass(git_signature_from_buffer(&sign, "Test User <test@test.tt> 1461698487 +0200"));
+ cl_assert_equal_s("Test User", sign->name);
+ cl_assert_equal_s("test@test.tt", sign->email);
+ cl_assert_equal_i(1461698487, sign->when.time);
+ cl_assert_equal_i(120, sign->when.offset);
+ git_signature_free(sign);
+}
+
+void test_commit_signature__from_buf_with_neg_zero_offset(void)
+{
+ git_signature *sign;
+
+ cl_git_pass(git_signature_from_buffer(&sign, "Test User <test@test.tt> 1461698487 -0000"));
+ cl_assert_equal_s("Test User", sign->name);
+ cl_assert_equal_s("test@test.tt", sign->email);
+ cl_assert_equal_i(1461698487, sign->when.time);
+ cl_assert_equal_i(0, sign->when.offset);
+ cl_assert_equal_i('-', sign->when.sign);
+ git_signature_free(sign);
+}
+
+void test_commit_signature__pos_and_neg_zero_offsets_dont_match(void)
+{
+ git_signature *with_neg_zero;
+ git_signature *with_pos_zero;
+
+ cl_git_pass(git_signature_from_buffer(&with_neg_zero, "Test User <test@test.tt> 1461698487 -0000"));
+ cl_git_pass(git_signature_from_buffer(&with_pos_zero, "Test User <test@test.tt> 1461698487 +0000"));
+
+ cl_assert(!git_signature__equal(with_neg_zero, with_pos_zero));
+
+ git_signature_free((git_signature *)with_neg_zero);
+ git_signature_free((git_signature *)with_pos_zero);
+}
diff --git a/tests/libgit2/commit/write.c b/tests/libgit2/commit/write.c
new file mode 100644
index 0000000..890f738
--- /dev/null
+++ b/tests/libgit2/commit/write.c
@@ -0,0 +1,424 @@
+#include "clar_libgit2.h"
+#include "git2/sys/commit.h"
+
+static const char *committer_name = "Vicent Marti";
+static const char *committer_email = "vicent@github.com";
+static const char *commit_message = "This commit has been created in memory\n\
+ This is a commit created in memory and it will be written back to disk\n";
+static const char *tree_id_str = "1810dff58d8a660512d4832e740f692884338ccd";
+static const char *parent_id_str = "8496071c1b46c854b31185ea97743be6a8774479";
+static const char *root_commit_message = "This is a root commit\n\
+ This is a root commit and should be the only one in this branch\n";
+static const char *root_reflog_message = "commit (initial): This is a root commit \
+This is a root commit and should be the only one in this branch";
+static char *head_old;
+static git_reference *head, *branch;
+static git_commit *commit;
+
+/* Fixture setup */
+static git_repository *g_repo;
+void test_commit_write__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_commit_write__cleanup(void)
+{
+ git_reference_free(head);
+ head = NULL;
+
+ git_reference_free(branch);
+ branch = NULL;
+
+ git_commit_free(commit);
+ commit = NULL;
+
+ git__free(head_old);
+ head_old = NULL;
+
+ cl_git_sandbox_cleanup();
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 1));
+}
+
+
+/* write a new commit object from memory to disk */
+void test_commit_write__from_memory(void)
+{
+ git_oid tree_id, parent_id, commit_id;
+ git_signature *author, *committer;
+ const git_signature *author1, *committer1;
+ git_commit *parent;
+ git_tree *tree;
+
+ git_oid__fromstr(&tree_id, tree_id_str, GIT_OID_SHA1);
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
+
+ git_oid__fromstr(&parent_id, parent_id_str, GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&parent, g_repo, &parent_id));
+
+ /* create signatures */
+ cl_git_pass(git_signature_new(&committer, committer_name, committer_email, 123456789, 60));
+ cl_git_pass(git_signature_new(&author, committer_name, committer_email, 987654321, 90));
+
+ cl_git_pass(git_commit_create_v(
+ &commit_id, /* out id */
+ g_repo,
+ NULL, /* do not update the HEAD */
+ author,
+ committer,
+ NULL,
+ commit_message,
+ tree,
+ 1, parent));
+
+ git_object_free((git_object *)parent);
+ git_object_free((git_object *)tree);
+
+ git_signature_free(committer);
+ git_signature_free(author);
+
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &commit_id));
+
+ /* Check attributes were set correctly */
+ author1 = git_commit_author(commit);
+ cl_assert(author1 != NULL);
+ cl_assert_equal_s(committer_name, author1->name);
+ cl_assert_equal_s(committer_email, author1->email);
+ cl_assert(author1->when.time == 987654321);
+ cl_assert(author1->when.offset == 90);
+
+ committer1 = git_commit_committer(commit);
+ cl_assert(committer1 != NULL);
+ cl_assert_equal_s(committer_name, committer1->name);
+ cl_assert_equal_s(committer_email, committer1->email);
+ cl_assert(committer1->when.time == 123456789);
+ cl_assert(committer1->when.offset == 60);
+
+ cl_assert_equal_s(commit_message, git_commit_message(commit));
+}
+
+void test_commit_write__into_buf(void)
+{
+ git_oid tree_id;
+ git_signature *author, *committer;
+ git_tree *tree;
+ git_commit *parent;
+ git_oid parent_id;
+ git_buf commit = GIT_BUF_INIT;
+
+ git_oid__fromstr(&tree_id, tree_id_str, GIT_OID_SHA1);
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
+
+ /* create signatures */
+ cl_git_pass(git_signature_new(&committer, committer_name, committer_email, 123456789, 60));
+ cl_git_pass(git_signature_new(&author, committer_name, committer_email, 987654321, 90));
+
+ git_oid__fromstr(&parent_id, parent_id_str, GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&parent, g_repo, &parent_id));
+
+ cl_git_pass(git_commit_create_buffer(&commit, g_repo, author, committer,
+ NULL, root_commit_message, tree, 1, (const git_commit **) &parent));
+
+ cl_assert_equal_s(commit.ptr,
+ "tree 1810dff58d8a660512d4832e740f692884338ccd\n\
+parent 8496071c1b46c854b31185ea97743be6a8774479\n\
+author Vicent Marti <vicent@github.com> 987654321 +0130\n\
+committer Vicent Marti <vicent@github.com> 123456789 +0100\n\
+\n\
+This is a root commit\n\
+ This is a root commit and should be the only one in this branch\n\
+");
+
+ git_buf_dispose(&commit);
+ git_tree_free(tree);
+ git_commit_free(parent);
+ git_signature_free(author);
+ git_signature_free(committer);
+}
+
+/* create a root commit */
+void test_commit_write__root(void)
+{
+ git_oid tree_id, commit_id;
+ const git_oid *branch_oid;
+ git_signature *author, *committer;
+ const char *branch_name = "refs/heads/root-commit-branch";
+ git_tree *tree;
+ git_reflog *log;
+ const git_reflog_entry *entry;
+
+ git_oid__fromstr(&tree_id, tree_id_str, GIT_OID_SHA1);
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
+
+ /* create signatures */
+ cl_git_pass(git_signature_new(&committer, committer_name, committer_email, 123456789, 60));
+ cl_git_pass(git_signature_new(&author, committer_name, committer_email, 987654321, 90));
+
+ /* First we need to update HEAD so it points to our non-existent branch */
+ cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
+ cl_assert(git_reference_type(head) == GIT_REFERENCE_SYMBOLIC);
+ head_old = git__strdup(git_reference_symbolic_target(head));
+ cl_assert(head_old != NULL);
+ git_reference_free(head);
+
+ cl_git_pass(git_reference_symbolic_create(&head, g_repo, "HEAD", branch_name, 1, NULL));
+
+ cl_git_pass(git_commit_create_v(
+ &commit_id, /* out id */
+ g_repo,
+ "HEAD",
+ author,
+ committer,
+ NULL,
+ root_commit_message,
+ tree,
+ 0));
+
+ git_object_free((git_object *)tree);
+ git_signature_free(author);
+
+ /*
+ * The fact that creating a commit works has already been
+ * tested. Here we just make sure it's our commit and that it was
+ * written as a root commit.
+ */
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &commit_id));
+ cl_assert(git_commit_parentcount(commit) == 0);
+ cl_git_pass(git_reference_lookup(&branch, g_repo, branch_name));
+ branch_oid = git_reference_target(branch);
+ cl_assert_equal_oid(branch_oid, &commit_id);
+ cl_assert_equal_s(root_commit_message, git_commit_message(commit));
+
+ cl_git_pass(git_reflog_read(&log, g_repo, branch_name));
+ cl_assert_equal_i(1, git_reflog_entrycount(log));
+ entry = git_reflog_entry_byindex(log, 0);
+ cl_assert_equal_s(committer->email, git_reflog_entry_committer(entry)->email);
+ cl_assert_equal_s(committer->name, git_reflog_entry_committer(entry)->name);
+ cl_assert_equal_s(root_reflog_message, git_reflog_entry_message(entry));
+
+ git_signature_free(committer);
+ git_reflog_free(log);
+}
+
+static int create_commit_from_ids(
+ git_oid *result,
+ const git_oid *tree_id,
+ const git_oid *parent_id)
+{
+ git_signature *author, *committer;
+ const git_oid *parent_ids[1];
+ int ret;
+
+ cl_git_pass(git_signature_new(
+ &committer, committer_name, committer_email, 123456789, 60));
+ cl_git_pass(git_signature_new(
+ &author, committer_name, committer_email, 987654321, 90));
+
+ parent_ids[0] = parent_id;
+
+ ret = git_commit_create_from_ids(
+ result,
+ g_repo,
+ NULL,
+ author,
+ committer,
+ NULL,
+ root_commit_message,
+ tree_id,
+ 1,
+ parent_ids);
+
+ git_signature_free(committer);
+ git_signature_free(author);
+
+ return ret;
+}
+
+void test_commit_write__can_write_invalid_objects(void)
+{
+ git_oid expected_id, tree_id, parent_id, commit_id;
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 0));
+
+ /* this is a valid tree and parent */
+ git_oid__fromstr(&tree_id, tree_id_str, GIT_OID_SHA1);
+ git_oid__fromstr(&parent_id, parent_id_str, GIT_OID_SHA1);
+
+ git_oid__fromstr(&expected_id, "c8571bbec3a72c4bcad31648902e5a453f1adece", GIT_OID_SHA1);
+ cl_git_pass(create_commit_from_ids(&commit_id, &tree_id, &parent_id));
+ cl_assert_equal_oid(&expected_id, &commit_id);
+
+ /* this is a wholly invented tree id */
+ git_oid__fromstr(&tree_id, "1234567890123456789012345678901234567890", GIT_OID_SHA1);
+ git_oid__fromstr(&parent_id, parent_id_str, GIT_OID_SHA1);
+
+ git_oid__fromstr(&expected_id, "996008340b8e68d69bf3c28d7c57fb7ec3c8e202", GIT_OID_SHA1);
+ cl_git_pass(create_commit_from_ids(&commit_id, &tree_id, &parent_id));
+ cl_assert_equal_oid(&expected_id, &commit_id);
+
+ /* this is a wholly invented parent id */
+ git_oid__fromstr(&tree_id, tree_id_str, GIT_OID_SHA1);
+ git_oid__fromstr(&parent_id, "1234567890123456789012345678901234567890", GIT_OID_SHA1);
+
+ git_oid__fromstr(&expected_id, "d78f660cab89d9791ca6714b57978bf2a7e709fd", GIT_OID_SHA1);
+ cl_git_pass(create_commit_from_ids(&commit_id, &tree_id, &parent_id));
+ cl_assert_equal_oid(&expected_id, &commit_id);
+
+ /* these are legitimate objects, but of the wrong type */
+ git_oid__fromstr(&tree_id, parent_id_str, GIT_OID_SHA1);
+ git_oid__fromstr(&parent_id, tree_id_str, GIT_OID_SHA1);
+
+ git_oid__fromstr(&expected_id, "5d80c07414e3f18792949699dfcacadf7748f361", GIT_OID_SHA1);
+ cl_git_pass(create_commit_from_ids(&commit_id, &tree_id, &parent_id));
+ cl_assert_equal_oid(&expected_id, &commit_id);
+}
+
+void test_commit_write__can_validate_objects(void)
+{
+ git_oid tree_id, parent_id, commit_id;
+
+ /* this is a valid tree and parent */
+ git_oid__fromstr(&tree_id, tree_id_str, GIT_OID_SHA1);
+ git_oid__fromstr(&parent_id, parent_id_str, GIT_OID_SHA1);
+ cl_git_pass(create_commit_from_ids(&commit_id, &tree_id, &parent_id));
+
+ /* this is a wholly invented tree id */
+ git_oid__fromstr(&tree_id, "1234567890123456789012345678901234567890", GIT_OID_SHA1);
+ git_oid__fromstr(&parent_id, parent_id_str, GIT_OID_SHA1);
+ cl_git_fail(create_commit_from_ids(&commit_id, &tree_id, &parent_id));
+
+ /* this is a wholly invented parent id */
+ git_oid__fromstr(&tree_id, tree_id_str, GIT_OID_SHA1);
+ git_oid__fromstr(&parent_id, "1234567890123456789012345678901234567890", GIT_OID_SHA1);
+ cl_git_fail(create_commit_from_ids(&commit_id, &tree_id, &parent_id));
+
+ /* these are legitimate objects, but of the wrong type */
+ git_oid__fromstr(&tree_id, parent_id_str, GIT_OID_SHA1);
+ git_oid__fromstr(&parent_id, tree_id_str, GIT_OID_SHA1);
+ cl_git_fail(create_commit_from_ids(&commit_id, &tree_id, &parent_id));
+}
+
+void test_commit_write__attach_signature_checks_objects(void)
+{
+ const char *sig = "magic word: pretty please";
+ const char *badtree = "tree 6b79e22d69bf46e289df0345a14ca059dfc9bdf6\n\
+parent 34734e478d6cf50c27c9d69026d93974d052c454\n\
+author Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
+committer Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
+\n\
+a simple commit which does not work\n";
+
+ const char *badparent = "tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n\
+parent 34734e478d6cf50c27c9d69026d93974d052c454\n\
+author Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
+committer Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
+\n\
+a simple commit which does not work\n";
+
+ git_oid id;
+
+ cl_git_fail_with(-1, git_commit_create_with_signature(&id, g_repo, badtree, sig, "magicsig"));
+ cl_git_fail_with(-1, git_commit_create_with_signature(&id, g_repo, badparent, sig, "magicsig"));
+
+}
+
+void test_commit_write__attach_singleline_signature(void)
+{
+ const char *sig = "magic word: pretty please";
+
+ const char *data = "tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n\
+parent 8496071c1b46c854b31185ea97743be6a8774479\n\
+author Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
+committer Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
+\n\
+a simple commit which works\n";
+
+ const char *complete = "tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n\
+parent 8496071c1b46c854b31185ea97743be6a8774479\n\
+author Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
+committer Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
+magicsig magic word: pretty please\n\
+\n\
+a simple commit which works\n";
+
+ git_oid id;
+ git_odb *odb;
+ git_odb_object *obj;
+
+ cl_git_pass(git_commit_create_with_signature(&id, g_repo, data, sig, "magicsig"));
+
+ cl_git_pass(git_repository_odb(&odb, g_repo));
+ cl_git_pass(git_odb_read(&obj, odb, &id));
+ cl_assert_equal_s(complete, git_odb_object_data(obj));
+
+ git_odb_object_free(obj);
+ git_odb_free(odb);
+}
+
+void test_commit_write__attach_multiline_signature(void)
+{
+ const char *gpgsig = "-----BEGIN PGP SIGNATURE-----\n\
+Version: GnuPG v1.4.12 (Darwin)\n\
+\n\
+iQIcBAABAgAGBQJQ+FMIAAoJEH+LfPdZDSs1e3EQAJMjhqjWF+WkGLHju7pTw2al\n\
+o6IoMAhv0Z/LHlWhzBd9e7JeCnanRt12bAU7yvYp9+Z+z+dbwqLwDoFp8LVuigl8\n\
+JGLcnwiUW3rSvhjdCp9irdb4+bhKUnKUzSdsR2CK4/hC0N2i/HOvMYX+BRsvqweq\n\
+AsAkA6dAWh+gAfedrBUkCTGhlNYoetjdakWqlGL1TiKAefEZrtA1TpPkGn92vbLq\n\
+SphFRUY9hVn1ZBWrT3hEpvAIcZag3rTOiRVT1X1flj8B2vGCEr3RrcwOIZikpdaW\n\
+who/X3xh/DGbI2RbuxmmJpxxP/8dsVchRJJzBwG+yhwU/iN3MlV2c5D69tls/Dok\n\
+6VbyU4lm/ae0y3yR83D9dUlkycOnmmlBAHKIZ9qUts9X7mWJf0+yy2QxJVpjaTGG\n\
+cmnQKKPeNIhGJk2ENnnnzjEve7L7YJQF6itbx5VCOcsGh3Ocb3YR7DMdWjt7f8pu\n\
+c6j+q1rP7EpE2afUN/geSlp5i3x8aXZPDj67jImbVCE/Q1X9voCtyzGJH7MXR0N9\n\
+ZpRF8yzveRfMH8bwAJjSOGAFF5XkcR/RNY95o+J+QcgBLdX48h+ZdNmUf6jqlu3J\n\
+7KmTXXQcOVpN6dD3CmRFsbjq+x6RHwa8u1iGn+oIkX908r97ckfB/kHKH7ZdXIJc\n\
+cpxtDQQMGYFpXK/71stq\n\
+=ozeK\n\
+-----END PGP SIGNATURE-----";
+
+ const char *data = "tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n\
+parent 8496071c1b46c854b31185ea97743be6a8774479\n\
+author Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
+committer Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
+\n\
+a simple commit which works\n";
+
+const char *complete = "tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904\n\
+parent 8496071c1b46c854b31185ea97743be6a8774479\n\
+author Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
+committer Ben Burkert <ben@benburkert.com> 1358451456 -0800\n\
+gpgsig -----BEGIN PGP SIGNATURE-----\n\
+ Version: GnuPG v1.4.12 (Darwin)\n\
+ \n\
+ iQIcBAABAgAGBQJQ+FMIAAoJEH+LfPdZDSs1e3EQAJMjhqjWF+WkGLHju7pTw2al\n\
+ o6IoMAhv0Z/LHlWhzBd9e7JeCnanRt12bAU7yvYp9+Z+z+dbwqLwDoFp8LVuigl8\n\
+ JGLcnwiUW3rSvhjdCp9irdb4+bhKUnKUzSdsR2CK4/hC0N2i/HOvMYX+BRsvqweq\n\
+ AsAkA6dAWh+gAfedrBUkCTGhlNYoetjdakWqlGL1TiKAefEZrtA1TpPkGn92vbLq\n\
+ SphFRUY9hVn1ZBWrT3hEpvAIcZag3rTOiRVT1X1flj8B2vGCEr3RrcwOIZikpdaW\n\
+ who/X3xh/DGbI2RbuxmmJpxxP/8dsVchRJJzBwG+yhwU/iN3MlV2c5D69tls/Dok\n\
+ 6VbyU4lm/ae0y3yR83D9dUlkycOnmmlBAHKIZ9qUts9X7mWJf0+yy2QxJVpjaTGG\n\
+ cmnQKKPeNIhGJk2ENnnnzjEve7L7YJQF6itbx5VCOcsGh3Ocb3YR7DMdWjt7f8pu\n\
+ c6j+q1rP7EpE2afUN/geSlp5i3x8aXZPDj67jImbVCE/Q1X9voCtyzGJH7MXR0N9\n\
+ ZpRF8yzveRfMH8bwAJjSOGAFF5XkcR/RNY95o+J+QcgBLdX48h+ZdNmUf6jqlu3J\n\
+ 7KmTXXQcOVpN6dD3CmRFsbjq+x6RHwa8u1iGn+oIkX908r97ckfB/kHKH7ZdXIJc\n\
+ cpxtDQQMGYFpXK/71stq\n\
+ =ozeK\n\
+ -----END PGP SIGNATURE-----\n\
+\n\
+a simple commit which works\n";
+
+ git_oid one, two;
+ git_odb *odb;
+ git_odb_object *obj;
+
+ cl_git_pass(git_commit_create_with_signature(&one, g_repo, data, gpgsig, "gpgsig"));
+ cl_git_pass(git_commit_create_with_signature(&two, g_repo, data, gpgsig, NULL));
+
+ cl_assert(!git_oid_cmp(&one, &two));
+ cl_git_pass(git_repository_odb(&odb, g_repo));
+ cl_git_pass(git_odb_read(&obj, odb, &one));
+ cl_assert_equal_s(complete, git_odb_object_data(obj));
+
+ git_odb_object_free(obj);
+ git_odb_free(odb);
+}
diff --git a/tests/libgit2/config/add.c b/tests/libgit2/config/add.c
new file mode 100644
index 0000000..405f1e2
--- /dev/null
+++ b/tests/libgit2/config/add.c
@@ -0,0 +1,37 @@
+#include "clar_libgit2.h"
+
+void test_config_add__initialize(void)
+{
+ cl_fixture_sandbox("config/config10");
+}
+
+void test_config_add__cleanup(void)
+{
+ cl_fixture_cleanup("config10");
+}
+
+void test_config_add__to_existing_section(void)
+{
+ git_config *cfg;
+ int32_t i;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config10"));
+ cl_git_pass(git_config_set_int32(cfg, "empty.tmp", 5));
+ cl_git_pass(git_config_get_int32(&i, cfg, "empty.tmp"));
+ cl_assert(i == 5);
+ cl_git_pass(git_config_delete_entry(cfg, "empty.tmp"));
+ git_config_free(cfg);
+}
+
+void test_config_add__to_new_section(void)
+{
+ git_config *cfg;
+ int32_t i;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config10"));
+ cl_git_pass(git_config_set_int32(cfg, "section.tmp", 5));
+ cl_git_pass(git_config_get_int32(&i, cfg, "section.tmp"));
+ cl_assert(i == 5);
+ cl_git_pass(git_config_delete_entry(cfg, "section.tmp"));
+ git_config_free(cfg);
+}
diff --git a/tests/libgit2/config/backend.c b/tests/libgit2/config/backend.c
new file mode 100644
index 0000000..96feecc
--- /dev/null
+++ b/tests/libgit2/config/backend.c
@@ -0,0 +1,24 @@
+#include "clar_libgit2.h"
+#include "git2/sys/config.h"
+
+void test_config_backend__checks_version(void)
+{
+ git_config *cfg;
+ git_config_backend backend = GIT_CONFIG_BACKEND_INIT;
+ const git_error *err;
+
+ backend.version = 1024;
+
+ cl_git_pass(git_config_new(&cfg));
+ cl_git_fail(git_config_add_backend(cfg, &backend, 0, NULL, false));
+ err = git_error_last();
+ cl_assert_equal_i(GIT_ERROR_INVALID, err->klass);
+
+ git_error_clear();
+ backend.version = 1024;
+ cl_git_fail(git_config_add_backend(cfg, &backend, 0, NULL, false));
+ err = git_error_last();
+ cl_assert_equal_i(GIT_ERROR_INVALID, err->klass);
+
+ git_config_free(cfg);
+}
diff --git a/tests/libgit2/config/conditionals.c b/tests/libgit2/config/conditionals.c
new file mode 100644
index 0000000..564719d
--- /dev/null
+++ b/tests/libgit2/config/conditionals.c
@@ -0,0 +1,175 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+#include "repository.h"
+
+#ifdef GIT_WIN32
+# define ROOT_PREFIX "C:"
+#else
+# define ROOT_PREFIX
+#endif
+
+static git_repository *_repo;
+
+void test_config_conditionals__initialize(void)
+{
+ _repo = cl_git_sandbox_init("empty_standard_repo");
+}
+
+void test_config_conditionals__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static void assert_condition_includes(const char *keyword, const char *path, bool expected)
+{
+ git_buf value = GIT_BUF_INIT;
+ git_str buf = GIT_STR_INIT;
+ git_config *cfg;
+
+ cl_git_pass(git_str_printf(&buf, "[includeIf \"%s:%s\"]\n", keyword, path));
+ cl_git_pass(git_str_puts(&buf, "path = other\n"));
+
+ cl_git_mkfile("empty_standard_repo/.git/config", buf.ptr);
+ cl_git_mkfile("empty_standard_repo/.git/other", "[foo]\nbar=baz\n");
+ _repo = cl_git_sandbox_reopen();
+
+ git_str_dispose(&buf);
+
+ cl_git_pass(git_repository_config(&cfg, _repo));
+
+ if (expected) {
+ cl_git_pass(git_config_get_string_buf(&value, cfg, "foo.bar"));
+ cl_assert_equal_s("baz", value.ptr);
+ } else {
+ cl_git_fail_with(GIT_ENOTFOUND,
+ git_config_get_string_buf(&value, cfg, "foo.bar"));
+ }
+
+ git_str_dispose(&buf);
+ git_buf_dispose(&value);
+ git_config_free(cfg);
+}
+
+static char *sandbox_path(git_str *buf, const char *suffix)
+{
+ char *path = p_realpath(clar_sandbox_path(), NULL);
+ cl_assert(path);
+ cl_git_pass(git_str_attach(buf, path, 0));
+ cl_git_pass(git_str_joinpath(buf, buf->ptr, suffix));
+ return buf->ptr;
+}
+
+void test_config_conditionals__gitdir(void)
+{
+ git_str path = GIT_STR_INIT;
+
+ assert_condition_includes("gitdir", ROOT_PREFIX "/", true);
+ assert_condition_includes("gitdir", "empty_stand", false);
+ assert_condition_includes("gitdir", "empty_stand/", false);
+ assert_condition_includes("gitdir", "empty_stand/.git", false);
+ assert_condition_includes("gitdir", "empty_stand/.git/", false);
+ assert_condition_includes("gitdir", "empty_stand*/", true);
+ assert_condition_includes("gitdir", "empty_stand*/.git", true);
+ assert_condition_includes("gitdir", "empty_stand*/.git/", false);
+ assert_condition_includes("gitdir", "empty_standard_repo", false);
+ assert_condition_includes("gitdir", "empty_standard_repo/", true);
+ assert_condition_includes("gitdir", "empty_standard_repo/.git", true);
+ assert_condition_includes("gitdir", "empty_standard_repo/.git/", false);
+
+ assert_condition_includes("gitdir", "./", false);
+
+ assert_condition_includes("gitdir", ROOT_PREFIX "/nonexistent", false);
+ assert_condition_includes("gitdir", ROOT_PREFIX "/empty_standard_repo", false);
+ assert_condition_includes("gitdir", "~/empty_standard_repo", false);
+
+ assert_condition_includes("gitdir", sandbox_path(&path, "/"), true);
+ assert_condition_includes("gitdir", sandbox_path(&path, "/*"), false);
+ assert_condition_includes("gitdir", sandbox_path(&path, "/**"), true);
+
+ assert_condition_includes("gitdir", sandbox_path(&path, "empty_standard_repo"), false);
+ assert_condition_includes("gitdir", sandbox_path(&path, "empty_standard_repo/"), true);
+ assert_condition_includes("gitdir", sandbox_path(&path, "empty_standard_repo/"), true);
+ assert_condition_includes("gitdir", sandbox_path(&path, "Empty_Standard_Repo"), false);
+ assert_condition_includes("gitdir", sandbox_path(&path, "Empty_Standard_Repo/"), false);
+
+ git_str_dispose(&path);
+}
+
+void test_config_conditionals__gitdir_i(void)
+{
+ git_str path = GIT_STR_INIT;
+
+ assert_condition_includes("gitdir/i", sandbox_path(&path, "empty_standard_repo/"), true);
+ assert_condition_includes("gitdir/i", sandbox_path(&path, "EMPTY_STANDARD_REPO/"), true);
+
+ git_str_dispose(&path);
+}
+
+void test_config_conditionals__invalid_conditional_fails(void)
+{
+ assert_condition_includes("foobar", ".git", false);
+}
+
+static void set_head(git_repository *repo, const char *name)
+{
+ cl_git_pass(git_repository_create_head(git_repository_path(repo), name));
+}
+
+void test_config_conditionals__onbranch(void)
+{
+ assert_condition_includes("onbranch", "master", true);
+ assert_condition_includes("onbranch", "m*", true);
+ assert_condition_includes("onbranch", "*", true);
+ assert_condition_includes("onbranch", "master/", false);
+ assert_condition_includes("onbranch", "foo", false);
+
+ set_head(_repo, "foo");
+ assert_condition_includes("onbranch", "master", false);
+ assert_condition_includes("onbranch", "foo", true);
+ assert_condition_includes("onbranch", "f*o", true);
+
+ set_head(_repo, "dir/ref");
+ assert_condition_includes("onbranch", "dir/ref", true);
+ assert_condition_includes("onbranch", "dir/", true);
+ assert_condition_includes("onbranch", "dir/*", true);
+ assert_condition_includes("onbranch", "dir/**", true);
+ assert_condition_includes("onbranch", "**", true);
+ assert_condition_includes("onbranch", "dir", false);
+ assert_condition_includes("onbranch", "dir*", false);
+
+ set_head(_repo, "dir/subdir/ref");
+ assert_condition_includes("onbranch", "dir/subdir/", true);
+ assert_condition_includes("onbranch", "dir/subdir/*", true);
+ assert_condition_includes("onbranch", "dir/subdir/ref", true);
+ assert_condition_includes("onbranch", "dir/", true);
+ assert_condition_includes("onbranch", "dir/**", true);
+ assert_condition_includes("onbranch", "**", true);
+ assert_condition_includes("onbranch", "dir", false);
+ assert_condition_includes("onbranch", "dir*", false);
+ assert_condition_includes("onbranch", "dir/*", false);
+}
+
+void test_config_conditionals__empty(void)
+{
+ git_buf value = GIT_BUF_INIT;
+ git_str buf = GIT_STR_INIT;
+ git_config *cfg;
+
+ cl_git_pass(git_str_puts(&buf, "[includeIf]\n"));
+ cl_git_pass(git_str_puts(&buf, "path = other\n"));
+
+ cl_git_mkfile("empty_standard_repo/.git/config", buf.ptr);
+ cl_git_mkfile("empty_standard_repo/.git/other", "[foo]\nbar=baz\n");
+ _repo = cl_git_sandbox_reopen();
+
+ git_str_dispose(&buf);
+
+ cl_git_pass(git_repository_config(&cfg, _repo));
+
+ cl_git_fail_with(GIT_ENOTFOUND,
+ git_config_get_string_buf(&value, cfg, "foo.bar"));
+
+ git_str_dispose(&buf);
+ git_buf_dispose(&value);
+ git_config_free(cfg);
+}
diff --git a/tests/libgit2/config/config_helpers.c b/tests/libgit2/config/config_helpers.c
new file mode 100644
index 0000000..ecdab5b
--- /dev/null
+++ b/tests/libgit2/config/config_helpers.c
@@ -0,0 +1,67 @@
+#include "clar_libgit2.h"
+#include "config_helpers.h"
+#include "repository.h"
+
+void assert_config_entry_existence(
+ git_repository *repo,
+ const char *name,
+ bool is_supposed_to_exist)
+{
+ git_config *config;
+ git_config_entry *entry = NULL;
+ int result;
+
+ cl_git_pass(git_repository_config__weakptr(&config, repo));
+
+ result = git_config_get_entry(&entry, config, name);
+ git_config_entry_free(entry);
+
+ if (is_supposed_to_exist)
+ cl_git_pass(result);
+ else
+ cl_assert_equal_i(GIT_ENOTFOUND, result);
+}
+
+void assert_config_entry_value(
+ git_repository *repo,
+ const char *name,
+ const char *expected_value)
+{
+ git_config *config;
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_pass(git_repository_config__weakptr(&config, repo));
+
+ cl_git_pass(git_config_get_string_buf(&buf, config, name));
+
+ cl_assert_equal_s(expected_value, buf.ptr);
+ git_buf_dispose(&buf);
+}
+
+static int count_config_entries_cb(
+ const git_config_entry *entry,
+ void *payload)
+{
+ int *how_many = (int *)payload;
+
+ GIT_UNUSED(entry);
+
+ (*how_many)++;
+
+ return 0;
+}
+
+int count_config_entries_match(git_repository *repo, const char *pattern)
+{
+ git_config *config;
+ int how_many = 0;
+
+ cl_git_pass(git_repository_config(&config, repo));
+
+ cl_assert_equal_i(0, git_config_foreach_match(
+ config, pattern, count_config_entries_cb, &how_many));
+
+ git_config_free(config);
+
+ return how_many;
+}
diff --git a/tests/libgit2/config/config_helpers.h b/tests/libgit2/config/config_helpers.h
new file mode 100644
index 0000000..4406457
--- /dev/null
+++ b/tests/libgit2/config/config_helpers.h
@@ -0,0 +1,13 @@
+extern void assert_config_entry_existence(
+ git_repository *repo,
+ const char *name,
+ bool is_supposed_to_exist);
+
+extern void assert_config_entry_value(
+ git_repository *repo,
+ const char *name,
+ const char *expected_value);
+
+extern int count_config_entries_match(
+ git_repository *repo,
+ const char *pattern);
diff --git a/tests/libgit2/config/configlevel.c b/tests/libgit2/config/configlevel.c
new file mode 100644
index 0000000..8422d32
--- /dev/null
+++ b/tests/libgit2/config/configlevel.c
@@ -0,0 +1,73 @@
+#include "clar_libgit2.h"
+
+void test_config_configlevel__adding_the_same_level_twice_returns_EEXISTS(void)
+{
+ int error;
+ git_config *cfg;
+
+ cl_git_pass(git_config_new(&cfg));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
+ GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
+ error = git_config_add_file_ondisk(cfg, cl_fixture("config/config16"),
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0);
+
+ cl_git_fail(error);
+ cl_assert_equal_i(GIT_EEXISTS, error);
+
+ git_config_free(cfg);
+}
+
+void test_config_configlevel__can_replace_a_config_file_at_an_existing_level(void)
+{
+ git_config *cfg;
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_pass(git_config_new(&cfg));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config18"),
+ GIT_CONFIG_LEVEL_LOCAL, NULL, 1));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config19"),
+ GIT_CONFIG_LEVEL_LOCAL, NULL, 1));
+
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "core.stringglobal"));
+ cl_assert_equal_s("don't find me!", buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_config_free(cfg);
+}
+
+void test_config_configlevel__can_read_from_a_single_level_focused_file_after_parent_config_has_been_freed(void)
+{
+ git_config *cfg;
+ git_config *single_level_cfg;
+ git_buf buf = {0};
+
+ cl_git_pass(git_config_new(&cfg));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config18"),
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config19"),
+ GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
+
+ cl_git_pass(git_config_open_level(&single_level_cfg, cfg, GIT_CONFIG_LEVEL_LOCAL));
+
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_get_string_buf(&buf, single_level_cfg, "core.stringglobal"));
+ cl_assert_equal_s("don't find me!", buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_config_free(single_level_cfg);
+}
+
+void test_config_configlevel__fetching_a_level_from_an_empty_compound_config_returns_ENOTFOUND(void)
+{
+ git_config *cfg;
+ git_config *local_cfg;
+
+ cl_git_pass(git_config_new(&cfg));
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_config_open_level(&local_cfg, cfg, GIT_CONFIG_LEVEL_LOCAL));
+
+ git_config_free(cfg);
+}
diff --git a/tests/libgit2/config/find.c b/tests/libgit2/config/find.c
new file mode 100644
index 0000000..7ca8ec7
--- /dev/null
+++ b/tests/libgit2/config/find.c
@@ -0,0 +1,11 @@
+#include "clar_libgit2.h"
+
+void test_config_find__one(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_fail_with(GIT_ENOTFOUND, git_config_find_global(&buf));
+ cl_git_fail_with(GIT_ENOTFOUND, git_config_find_xdg(&buf));
+ cl_git_fail_with(GIT_ENOTFOUND, git_config_find_system(&buf));
+ cl_git_fail_with(GIT_ENOTFOUND, git_config_find_programdata(&buf));
+}
diff --git a/tests/libgit2/config/global.c b/tests/libgit2/config/global.c
new file mode 100644
index 0000000..5aba4ee
--- /dev/null
+++ b/tests/libgit2/config/global.c
@@ -0,0 +1,172 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+
+void test_config_global__initialize(void)
+{
+ git_str path = GIT_STR_INIT;
+
+ cl_git_pass(git_futils_mkdir_r("home", 0777));
+ cl_git_pass(git_fs_path_prettify(&path, "home", NULL));
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
+
+ cl_git_pass(git_futils_mkdir_r("xdg/git", 0777));
+ cl_git_pass(git_fs_path_prettify(&path, "xdg/git", NULL));
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr));
+
+ cl_git_pass(git_futils_mkdir_r("etc", 0777));
+ cl_git_pass(git_fs_path_prettify(&path, "etc", NULL));
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr));
+
+ git_str_dispose(&path);
+}
+
+void test_config_global__cleanup(void)
+{
+ cl_sandbox_set_search_path_defaults();
+ cl_git_pass(git_futils_rmdir_r("home", NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_git_pass(git_futils_rmdir_r("xdg", NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_git_pass(git_futils_rmdir_r("etc", NULL, GIT_RMDIR_REMOVE_FILES));
+}
+
+void test_config_global__open_global(void)
+{
+ git_config *cfg, *global, *selected, *dummy;
+ int32_t value;
+
+ cl_git_mkfile("home/.gitconfig", "[global]\n test = 4567\n");
+
+ cl_git_pass(git_config_open_default(&cfg));
+ cl_git_pass(git_config_get_int32(&value, cfg, "global.test"));
+ cl_assert_equal_i(4567, value);
+
+ cl_git_pass(git_config_open_level(&global, cfg, GIT_CONFIG_LEVEL_GLOBAL));
+ cl_git_pass(git_config_get_int32(&value, global, "global.test"));
+ cl_assert_equal_i(4567, value);
+
+ cl_git_fail(git_config_open_level(&dummy, cfg, GIT_CONFIG_LEVEL_XDG));
+
+ cl_git_pass(git_config_open_global(&selected, cfg));
+ cl_git_pass(git_config_get_int32(&value, selected, "global.test"));
+ cl_assert_equal_i(4567, value);
+
+ git_config_free(selected);
+ git_config_free(global);
+ git_config_free(cfg);
+}
+
+void test_config_global__open_symlinked_global(void)
+{
+#ifndef GIT_WIN32
+ git_config *cfg;
+ int32_t value;
+
+ cl_git_mkfile("home/.gitconfig.linked", "[global]\n test = 4567\n");
+ cl_must_pass(symlink(".gitconfig.linked", "home/.gitconfig"));
+
+ cl_git_pass(git_config_open_default(&cfg));
+ cl_git_pass(git_config_get_int32(&value, cfg, "global.test"));
+ cl_assert_equal_i(4567, value);
+
+ git_config_free(cfg);
+#endif
+}
+
+void test_config_global__lock_missing_global_config(void)
+{
+ git_config *cfg;
+ git_config_entry *entry;
+ git_transaction *transaction;
+
+ (void)p_unlink("home/.gitconfig"); /* No global config */
+
+ cl_git_pass(git_config_open_default(&cfg));
+ cl_git_pass(git_config_lock(&transaction, cfg));
+ cl_git_pass(git_config_set_string(cfg, "assertion.fail", "boom"));
+ cl_git_pass(git_transaction_commit(transaction));
+ git_transaction_free(transaction);
+
+ /* cfg is updated */
+ cl_git_pass(git_config_get_entry(&entry, cfg, "assertion.fail"));
+ cl_assert_equal_s("boom", entry->value);
+
+ git_config_entry_free(entry);
+ git_config_free(cfg);
+
+ /* We can reread the new value from the global config */
+ cl_git_pass(git_config_open_default(&cfg));
+ cl_git_pass(git_config_get_entry(&entry, cfg, "assertion.fail"));
+ cl_assert_equal_s("boom", entry->value);
+
+ git_config_entry_free(entry);
+ git_config_free(cfg);
+}
+
+void test_config_global__open_xdg(void)
+{
+ git_config *cfg, *xdg, *selected;
+ const char *str = "teststring";
+ const char *key = "this.variable";
+ git_buf buf = {0};
+
+ cl_git_mkfile("xdg/git/config", "# XDG config\n[core]\n test = 1\n");
+
+ cl_git_pass(git_config_open_default(&cfg));
+ cl_git_pass(git_config_open_level(&xdg, cfg, GIT_CONFIG_LEVEL_XDG));
+ cl_git_pass(git_config_open_global(&selected, cfg));
+
+ cl_git_pass(git_config_set_string(xdg, key, str));
+ cl_git_pass(git_config_get_string_buf(&buf, selected, key));
+ cl_assert_equal_s(str, buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_config_free(selected);
+ git_config_free(xdg);
+ git_config_free(cfg);
+}
+
+void test_config_global__open_programdata(void)
+{
+ git_config *cfg;
+ git_repository *repo;
+ git_buf dir_path = GIT_BUF_INIT;
+ git_str config_path = GIT_STR_INIT;
+ git_buf var_contents = GIT_BUF_INIT;
+
+ if (cl_is_env_set("GITTEST_INVASIVE_FS_STRUCTURE"))
+ cl_skip();
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH,
+ GIT_CONFIG_LEVEL_PROGRAMDATA, &dir_path));
+
+ if (!git_fs_path_isdir(dir_path.ptr))
+ cl_git_pass(p_mkdir(dir_path.ptr, 0777));
+
+ cl_git_pass(git_str_joinpath(&config_path, dir_path.ptr, "config"));
+
+ cl_git_pass(git_config_open_ondisk(&cfg, config_path.ptr));
+ cl_git_pass(git_config_set_string(cfg, "programdata.var", "even higher level"));
+
+ git_str_dispose(&config_path);
+ git_config_free(cfg);
+
+ git_config_open_default(&cfg);
+ cl_git_pass(git_config_get_string_buf(&var_contents, cfg, "programdata.var"));
+ cl_assert_equal_s("even higher level", var_contents.ptr);
+
+ git_config_free(cfg);
+ git_buf_dispose(&var_contents);
+
+ cl_git_pass(git_repository_init(&repo, "./foo.git", true));
+ cl_git_pass(git_repository_config(&cfg, repo));
+ cl_git_pass(git_config_get_string_buf(&var_contents, cfg, "programdata.var"));
+ cl_assert_equal_s("even higher level", var_contents.ptr);
+
+ git_config_free(cfg);
+ git_buf_dispose(&dir_path);
+ git_buf_dispose(&var_contents);
+ git_repository_free(repo);
+ cl_fixture_cleanup("./foo.git");
+}
diff --git a/tests/libgit2/config/include.c b/tests/libgit2/config/include.c
new file mode 100644
index 0000000..1b55fdc
--- /dev/null
+++ b/tests/libgit2/config/include.c
@@ -0,0 +1,267 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+
+static git_config *cfg;
+static git_buf buf;
+
+void test_config_include__initialize(void)
+{
+ cfg = NULL;
+ memset(&buf, 0, sizeof(git_buf));
+}
+
+void test_config_include__cleanup(void)
+{
+ git_config_free(cfg);
+ git_buf_dispose(&buf);
+}
+
+void test_config_include__relative(void)
+{
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config-include")));
+
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar.baz"));
+ cl_assert_equal_s("huzzah", buf.ptr);
+}
+
+void test_config_include__absolute(void)
+{
+ git_str cfgdata = GIT_STR_INIT;
+ cl_git_pass(git_str_printf(&cfgdata, "[include]\npath = %s/config-included", cl_fixture("config")));
+
+ cl_git_mkfile("config-include-absolute", cfgdata.ptr);
+ git_str_dispose(&cfgdata);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config-include-absolute"));
+
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar.baz"));
+ cl_assert_equal_s("huzzah", buf.ptr);
+
+ cl_git_pass(p_unlink("config-include-absolute"));
+}
+
+void test_config_include__homedir(void)
+{
+ git_str homefile = GIT_STR_INIT;
+
+ cl_fake_homedir(&homefile);
+ cl_git_pass(git_str_joinpath(&homefile, homefile.ptr, "config-included"));
+
+ cl_git_mkfile("config-include-homedir", "[include]\npath = ~/config-included");
+ cl_git_mkfile(homefile.ptr, "[foo \"bar\"]\n\tbaz = huzzah\n");
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config-include-homedir"));
+
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar.baz"));
+ cl_assert_equal_s("huzzah", buf.ptr);
+
+ cl_sandbox_set_search_path_defaults();
+
+ cl_git_pass(p_unlink("config-include-homedir"));
+
+ git_str_dispose(&homefile);
+}
+
+/* We need to pretend that the variables were defined where the file was included */
+void test_config_include__ordering(void)
+{
+ cl_git_mkfile("included", "[foo \"bar\"]\nbaz = hurrah\nfrotz = hiya");
+ cl_git_mkfile("including",
+ "[foo \"bar\"]\nfrotz = hello\n"
+ "[include]\npath = included\n"
+ "[foo \"bar\"]\nbaz = huzzah\n");
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "including"));
+
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar.frotz"));
+ cl_assert_equal_s("hiya", buf.ptr);
+ git_buf_dispose(&buf);
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar.baz"));
+ cl_assert_equal_s("huzzah", buf.ptr);
+
+ cl_git_pass(p_unlink("included"));
+ cl_git_pass(p_unlink("including"));
+}
+
+/* We need to pretend that the variables were defined where the file was included */
+void test_config_include__depth(void)
+{
+ cl_git_mkfile("a", "[include]\npath = b");
+ cl_git_mkfile("b", "[include]\npath = a");
+
+ cl_git_fail(git_config_open_ondisk(&cfg, "a"));
+
+ cl_git_pass(p_unlink("a"));
+ cl_git_pass(p_unlink("b"));
+}
+
+void test_config_include__empty_path_sanely_handled(void)
+{
+ cl_git_mkfile("a", "[include]\npath");
+ cl_git_pass(git_config_open_ondisk(&cfg, "a"));
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "include.path"));
+ cl_assert_equal_s("", buf.ptr);
+
+ cl_git_pass(p_unlink("a"));
+}
+
+void test_config_include__missing(void)
+{
+ cl_git_mkfile("including", "[include]\npath = nonexistentfile\n[foo]\nbar = baz");
+
+ git_error_clear();
+ cl_git_pass(git_config_open_ondisk(&cfg, "including"));
+ cl_assert(git_error_last() == NULL);
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar"));
+ cl_assert_equal_s("baz", buf.ptr);
+
+ cl_git_pass(p_unlink("including"));
+}
+
+void test_config_include__missing_homedir(void)
+{
+ cl_fake_homedir(NULL);
+
+ cl_git_mkfile("including", "[include]\npath = ~/.nonexistentfile\n[foo]\nbar = baz");
+
+ git_error_clear();
+ cl_git_pass(git_config_open_ondisk(&cfg, "including"));
+ cl_assert(git_error_last() == NULL);
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar"));
+ cl_assert_equal_s("baz", buf.ptr);
+
+ cl_sandbox_set_search_path_defaults();
+ cl_git_pass(p_unlink("including"));
+}
+
+#define replicate10(s) s s s s s s s s s s
+void test_config_include__depth2(void)
+{
+ const char *content = "[include]\n" replicate10(replicate10("path=bottom\n"));
+
+ cl_git_mkfile("top-level", "[include]\npath = middle\n[foo]\nbar = baz");
+ cl_git_mkfile("middle", content);
+ cl_git_mkfile("bottom", "[foo]\nbar2 = baz2");
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "top-level"));
+
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar"));
+ cl_assert_equal_s("baz", buf.ptr);
+
+ git_buf_dispose(&buf);
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar2"));
+ cl_assert_equal_s("baz2", buf.ptr);
+
+ cl_git_pass(p_unlink("top-level"));
+ cl_git_pass(p_unlink("middle"));
+ cl_git_pass(p_unlink("bottom"));
+}
+
+void test_config_include__removing_include_removes_values(void)
+{
+ cl_git_mkfile("top-level", "[include]\npath = included");
+ cl_git_mkfile("included", "[foo]\nbar = value");
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "top-level"));
+ cl_git_mkfile("top-level", "");
+ cl_git_fail(git_config_get_string_buf(&buf, cfg, "foo.bar"));
+
+ cl_git_pass(p_unlink("top-level"));
+ cl_git_pass(p_unlink("included"));
+}
+
+void test_config_include__rewriting_include_refreshes_values(void)
+{
+ cl_git_mkfile("top-level", "[include]\npath = first\n[include]\npath = second");
+ cl_git_mkfile("first", "[first]\nfoo = bar");
+ cl_git_mkfile("second", "[second]\nfoo = bar");
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "top-level"));
+ cl_git_mkfile("first", "[first]\nother = value");
+ cl_git_fail(git_config_get_string_buf(&buf, cfg, "foo.bar"));
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "first.other"));
+ cl_assert_equal_s(buf.ptr, "value");
+
+ cl_git_pass(p_unlink("top-level"));
+ cl_git_pass(p_unlink("first"));
+ cl_git_pass(p_unlink("second"));
+}
+
+void test_config_include__rewriting_include_twice_refreshes_values(void)
+{
+ cl_git_mkfile("top-level", "[include]\npath = included");
+ cl_git_mkfile("included", "[foo]\nbar = first-value");
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "top-level"));
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.bar"));
+
+ git_buf_dispose(&buf);
+ cl_git_mkfile("included", "[foo]\nother = value2");
+ cl_git_fail(git_config_get_string_buf(&buf, cfg, "foo.bar"));
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.other"));
+ cl_assert_equal_s(buf.ptr, "value2");
+
+ git_buf_dispose(&buf);
+ cl_git_mkfile("included", "[foo]\nanother = bar");
+ cl_git_fail(git_config_get_string_buf(&buf, cfg, "foo.other"));
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "foo.another"));
+ cl_assert_equal_s(buf.ptr, "bar");
+
+ cl_git_pass(p_unlink("top-level"));
+ cl_git_pass(p_unlink("included"));
+}
+
+void test_config_include__included_variables_cannot_be_deleted(void)
+{
+ cl_git_mkfile("top-level", "[include]\npath = included\n");
+ cl_git_mkfile("included", "[foo]\nbar = value");
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "top-level"));
+ cl_git_fail(git_config_delete_entry(cfg, "foo.bar"));
+
+ cl_git_pass(p_unlink("top-level"));
+ cl_git_pass(p_unlink("included"));
+}
+
+void test_config_include__included_variables_cannot_be_modified(void)
+{
+ cl_git_mkfile("top-level", "[include]\npath = included\n");
+
+ cl_git_mkfile("included", "[foo]\nbar = value");
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "top-level"));
+ cl_git_fail(git_config_set_string(cfg, "foo.bar", "other-value"));
+
+ cl_git_pass(p_unlink("top-level"));
+ cl_git_pass(p_unlink("included"));
+}
+
+void test_config_include__variables_in_included_override_including(void)
+{
+ int i;
+
+ cl_git_mkfile("top-level", "[foo]\nbar = 1\n[include]\npath = included");
+ cl_git_mkfile("included", "[foo]\nbar = 2");
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "top-level"));
+ cl_git_pass(git_config_get_int32(&i, cfg, "foo.bar"));
+ cl_assert_equal_i(i, 2);
+
+ cl_git_pass(p_unlink("top-level"));
+ cl_git_pass(p_unlink("included"));
+}
+
+void test_config_include__variables_in_including_override_included(void)
+{
+ int i;
+
+ cl_git_mkfile("top-level", "[include]\npath = included\n[foo]\nbar = 1");
+ cl_git_mkfile("included", "[foo]\nbar = 2");
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "top-level"));
+ cl_git_pass(git_config_get_int32(&i, cfg, "foo.bar"));
+ cl_assert_equal_i(i, 1);
+
+ cl_git_pass(p_unlink("top-level"));
+ cl_git_pass(p_unlink("included"));
+}
diff --git a/tests/libgit2/config/memory.c b/tests/libgit2/config/memory.c
new file mode 100644
index 0000000..ae66189
--- /dev/null
+++ b/tests/libgit2/config/memory.c
@@ -0,0 +1,139 @@
+#include "clar_libgit2.h"
+
+#include "config_backend.h"
+
+static git_config_backend *backend;
+
+void test_config_memory__initialize(void)
+{
+ backend = NULL;
+}
+
+void test_config_memory__cleanup(void)
+{
+ git_config_backend_free(backend);
+}
+
+static void assert_config_contains(git_config_backend *backend,
+ const char *name, const char *value)
+{
+ git_config_entry *entry;
+ cl_git_pass(git_config_backend_get_string(&entry, backend, name));
+ cl_assert_equal_s(entry->value, value);
+}
+
+struct expected_entry {
+ const char *name;
+ const char *value;
+ int seen;
+};
+
+static int contains_all_cb(const git_config_entry *entry, void *payload)
+{
+ struct expected_entry *entries = (struct expected_entry *) payload;
+ int i;
+
+ for (i = 0; entries[i].name; i++) {
+ if (strcmp(entries[i].name, entry->name) ||
+ strcmp(entries[i].value , entry->value))
+ continue;
+
+ if (entries[i].seen)
+ cl_fail("Entry seen more than once");
+ entries[i].seen = 1;
+ return 0;
+ }
+
+ cl_fail("Unexpected entry");
+ return -1;
+}
+
+static void assert_config_contains_all(git_config_backend *backend,
+ struct expected_entry *entries)
+{
+ int i;
+
+ cl_git_pass(git_config_backend_foreach(backend, contains_all_cb, entries));
+
+ for (i = 0; entries[i].name; i++)
+ cl_assert(entries[i].seen);
+}
+
+static void setup_backend(const char *cfg)
+{
+ cl_git_pass(git_config_backend_from_string(&backend, cfg, strlen(cfg)));
+ cl_git_pass(git_config_backend_open(backend, 0, NULL));
+}
+
+void test_config_memory__write_operations_fail(void)
+{
+ setup_backend("");
+ cl_git_fail(git_config_backend_set_string(backend, "general.foo", "var"));
+ cl_git_fail(git_config_backend_delete(backend, "general.foo"));
+ cl_git_fail(git_config_backend_lock(backend));
+ cl_git_fail(git_config_backend_unlock(backend, 0));
+}
+
+void test_config_memory__simple(void)
+{
+ setup_backend(
+ "[general]\n"
+ "foo=bar\n");
+
+ assert_config_contains(backend, "general.foo", "bar");
+}
+
+void test_config_memory__malformed_fails_to_open(void)
+{
+ const char *cfg =
+ "[general\n"
+ "foo=bar\n";
+ cl_git_pass(git_config_backend_from_string(&backend, cfg, strlen(cfg)));
+ cl_git_fail(git_config_backend_open(backend, 0, NULL));
+}
+
+void test_config_memory__multiple_vars(void)
+{
+ setup_backend(
+ "[general]\n"
+ "foo=bar\n"
+ "key=value\n");
+ assert_config_contains(backend, "general.foo", "bar");
+ assert_config_contains(backend, "general.key", "value");
+}
+
+void test_config_memory__multiple_sections(void)
+{
+ setup_backend(
+ "[general]\n"
+ "foo=bar\n"
+ "\n"
+ "[other]\n"
+ "key=value\n");
+ assert_config_contains(backend, "general.foo", "bar");
+ assert_config_contains(backend, "other.key", "value");
+}
+
+void test_config_memory__multivar_gets_correct_string(void)
+{
+ setup_backend(
+ "[general]\n"
+ "foo=bar1\n"
+ "foo=bar2\n");
+ assert_config_contains(backend, "general.foo", "bar2");
+}
+
+void test_config_memory__foreach_sees_multivar(void)
+{
+ struct expected_entry entries[] = {
+ { "general.foo", "bar1", 0 },
+ { "general.foo", "bar2", 0 },
+ { NULL, NULL, 0 },
+ };
+
+ setup_backend(
+ "[general]\n"
+ "foo=bar1\n"
+ "foo=bar2\n");
+ assert_config_contains_all(backend, entries);
+}
diff --git a/tests/libgit2/config/multivar.c b/tests/libgit2/config/multivar.c
new file mode 100644
index 0000000..244e375
--- /dev/null
+++ b/tests/libgit2/config/multivar.c
@@ -0,0 +1,288 @@
+#include "clar_libgit2.h"
+
+static const char *_name = "remote.ab.url";
+
+void test_config_multivar__initialize(void)
+{
+ cl_fixture_sandbox("config");
+}
+
+void test_config_multivar__cleanup(void)
+{
+ cl_fixture_cleanup("config");
+}
+
+static int mv_read_cb(const git_config_entry *entry, void *data)
+{
+ int *n = (int *) data;
+
+ if (!strcmp(entry->name, _name))
+ (*n)++;
+
+ return 0;
+}
+
+void test_config_multivar__foreach(void)
+{
+ git_config *cfg;
+ int n = 0;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config11")));
+
+ cl_git_pass(git_config_foreach(cfg, mv_read_cb, &n));
+ cl_assert(n == 2);
+
+ git_config_free(cfg);
+}
+
+static int cb(const git_config_entry *entry, void *data)
+{
+ int *n = (int *) data;
+
+ GIT_UNUSED(entry);
+
+ (*n)++;
+
+ return 0;
+}
+
+static void check_get_multivar_foreach(
+ git_config *cfg, int expected, int expected_patterned)
+{
+ int n = 0;
+
+ if (expected > 0) {
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
+ cl_assert_equal_i(expected, n);
+ } else {
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
+ }
+
+ n = 0;
+
+ if (expected_patterned > 0) {
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "example", cb, &n));
+ cl_assert_equal_i(expected_patterned, n);
+ } else {
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_config_get_multivar_foreach(cfg, _name, "example", cb, &n));
+ }
+}
+
+static void check_get_multivar(git_config *cfg, int expected)
+{
+ git_config_iterator *iter;
+ git_config_entry *entry;
+ int n = 0;
+
+ cl_git_pass(git_config_multivar_iterator_new(&iter, cfg, _name, NULL));
+
+ while (git_config_next(&entry, iter) == 0)
+ n++;
+
+ cl_assert_equal_i(expected, n);
+ git_config_iterator_free(iter);
+
+}
+
+void test_config_multivar__get(void)
+{
+ git_config *cfg;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+ check_get_multivar_foreach(cfg, 2, 1);
+
+ /* add another that has the _name entry */
+ cl_git_pass(git_config_add_file_ondisk(cfg, "config/config9", GIT_CONFIG_LEVEL_SYSTEM, NULL, 1));
+ check_get_multivar_foreach(cfg, 3, 2);
+
+ /* add another that does not have the _name entry */
+ cl_git_pass(git_config_add_file_ondisk(cfg, "config/config0", GIT_CONFIG_LEVEL_GLOBAL, NULL, 1));
+ check_get_multivar_foreach(cfg, 3, 2);
+
+ /* add another that does not have the _name entry at the end */
+ cl_git_pass(git_config_add_file_ondisk(cfg, "config/config1", GIT_CONFIG_LEVEL_APP, NULL, 1));
+ check_get_multivar_foreach(cfg, 3, 2);
+
+ /* drop original file */
+ cl_git_pass(git_config_add_file_ondisk(cfg, "config/config2", GIT_CONFIG_LEVEL_LOCAL, NULL, 1));
+ check_get_multivar_foreach(cfg, 1, 1);
+
+ /* drop other file with match */
+ cl_git_pass(git_config_add_file_ondisk(cfg, "config/config3", GIT_CONFIG_LEVEL_SYSTEM, NULL, 1));
+ check_get_multivar_foreach(cfg, 0, 0);
+
+ /* reload original file (add different place in order) */
+ cl_git_pass(git_config_add_file_ondisk(cfg, "config/config11", GIT_CONFIG_LEVEL_SYSTEM, NULL, 1));
+ check_get_multivar_foreach(cfg, 2, 1);
+
+ check_get_multivar(cfg, 2);
+
+ git_config_free(cfg);
+}
+
+void test_config_multivar__add(void)
+{
+ git_config *cfg;
+ int n;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+ cl_git_pass(git_config_set_multivar(cfg, _name, "non-existent", "git://git.otherplace.org/libgit2"));
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
+ cl_assert_equal_i(n, 3);
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "otherplace", cb, &n));
+ cl_assert_equal_i(n, 1);
+
+ git_config_free(cfg);
+
+ /* We know it works in memory, let's see if the file is written correctly */
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
+ cl_assert_equal_i(n, 3);
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "otherplace", cb, &n));
+ cl_assert_equal_i(n, 1);
+
+ git_config_free(cfg);
+}
+
+void test_config_multivar__add_new(void)
+{
+ const char *var = "a.brand.new";
+ git_config *cfg;
+ int n;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+
+ cl_git_pass(git_config_set_multivar(cfg, var, "$^", "variable"));
+ n = 0;
+ cl_git_pass(git_config_get_multivar_foreach(cfg, var, NULL, cb, &n));
+ cl_assert_equal_i(n, 1);
+
+ git_config_free(cfg);
+}
+
+void test_config_multivar__replace(void)
+{
+ git_config *cfg;
+ int n;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
+ cl_assert(n == 2);
+
+ cl_git_pass(git_config_set_multivar(cfg, _name, "github", "git://git.otherplace.org/libgit2"));
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
+ cl_assert(n == 2);
+
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
+ cl_assert(n == 2);
+
+ git_config_free(cfg);
+}
+
+void test_config_multivar__replace_multiple(void)
+{
+ git_config *cfg;
+ int n;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+ cl_git_pass(git_config_set_multivar(cfg, _name, "git://", "git://git.otherplace.org/libgit2"));
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "otherplace", cb, &n));
+ cl_assert_equal_i(n, 2);
+
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, "otherplace", cb, &n));
+ cl_assert_equal_i(n, 2);
+
+ git_config_free(cfg);
+}
+
+void test_config_multivar__delete(void)
+{
+ git_config *cfg;
+ int n;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
+ cl_assert_equal_i(2, n);
+
+ cl_git_pass(git_config_delete_multivar(cfg, _name, "github"));
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
+ cl_assert_equal_i(1, n);
+
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
+ cl_assert_equal_i(1, n);
+
+ git_config_free(cfg);
+}
+
+void test_config_multivar__delete_multiple(void)
+{
+ git_config *cfg;
+ int n;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+
+ n = 0;
+ cl_git_pass(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n));
+ cl_assert(n == 2);
+
+ cl_git_pass(git_config_delete_multivar(cfg, _name, "git"));
+
+ n = 0;
+ cl_git_fail_with(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n), GIT_ENOTFOUND);
+
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+
+ n = 0;
+ cl_git_fail_with(git_config_get_multivar_foreach(cfg, _name, NULL, cb, &n), GIT_ENOTFOUND);
+
+ git_config_free(cfg);
+}
+
+void test_config_multivar__delete_notfound(void)
+{
+ git_config *cfg;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config/config11"));
+
+ cl_git_fail_with(git_config_delete_multivar(cfg, "remote.ab.noturl", "git"), GIT_ENOTFOUND);
+
+ git_config_free(cfg);
+}
diff --git a/tests/libgit2/config/new.c b/tests/libgit2/config/new.c
new file mode 100644
index 0000000..baca053
--- /dev/null
+++ b/tests/libgit2/config/new.c
@@ -0,0 +1,34 @@
+#include "clar_libgit2.h"
+
+#include "filebuf.h"
+#include "futils.h"
+#include "posix.h"
+
+#define TEST_CONFIG "git-new-config"
+
+void test_config_new__write_new_config(void)
+{
+ git_config *config;
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_mkfile(TEST_CONFIG, "");
+ cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG));
+
+ cl_git_pass(git_config_set_string(config, "color.ui", "auto"));
+ cl_git_pass(git_config_set_string(config, "core.editor", "ed"));
+
+ git_config_free(config);
+
+ cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG));
+
+ cl_git_pass(git_config_get_string_buf(&buf, config, "color.ui"));
+ cl_assert_equal_s("auto", buf.ptr);
+ git_buf_dispose(&buf);
+ cl_git_pass(git_config_get_string_buf(&buf, config, "core.editor"));
+ cl_assert_equal_s("ed", buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_config_free(config);
+
+ cl_must_pass(p_unlink(TEST_CONFIG));
+}
diff --git a/tests/libgit2/config/read.c b/tests/libgit2/config/read.c
new file mode 100644
index 0000000..ac6459b
--- /dev/null
+++ b/tests/libgit2/config/read.c
@@ -0,0 +1,1035 @@
+#include "clar_libgit2.h"
+#include "fs_path.h"
+
+static git_buf buf = GIT_BUF_INIT;
+
+void test_config_read__cleanup(void)
+{
+ git_buf_dispose(&buf);
+}
+
+void test_config_read__simple_read(void)
+{
+ git_config *cfg;
+ int32_t i;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config0")));
+
+ cl_git_pass(git_config_get_int32(&i, cfg, "core.repositoryformatversion"));
+ cl_assert(i == 0);
+ cl_git_pass(git_config_get_bool(&i, cfg, "core.filemode"));
+ cl_assert(i == 1);
+ cl_git_pass(git_config_get_bool(&i, cfg, "core.bare"));
+ cl_assert(i == 0);
+ cl_git_pass(git_config_get_bool(&i, cfg, "core.logallrefupdates"));
+ cl_assert(i == 1);
+
+ git_config_free(cfg);
+}
+
+void test_config_read__case_sensitive(void)
+{
+ git_config *cfg;
+ int i;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config1")));
+
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "this.that.other"));
+ cl_assert_equal_s("true", buf.ptr);
+ git_buf_dispose(&buf);
+
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "this.That.other"));
+ cl_assert_equal_s("yes", buf.ptr);
+
+ cl_git_pass(git_config_get_bool(&i, cfg, "this.that.other"));
+ cl_assert(i == 1);
+ cl_git_pass(git_config_get_bool(&i, cfg, "this.That.other"));
+ cl_assert(i == 1);
+
+ /* This one doesn't exist */
+ cl_must_fail(git_config_get_bool(&i, cfg, "this.thaT.other"));
+
+ git_config_free(cfg);
+}
+
+/*
+ * If \ is the last non-space character on the line, we read the next
+ * one, separating each line with SP.
+ */
+void test_config_read__multiline_value(void)
+{
+ git_config *cfg;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config2")));
+
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "this.That.and"));
+ cl_assert_equal_s("one one one two two three three", buf.ptr);
+
+ git_config_free(cfg);
+}
+
+static void clean_test_config(void *unused)
+{
+ GIT_UNUSED(unused);
+ cl_fixture_cleanup("./testconfig");
+}
+
+void test_config_read__multiline_value_and_eof(void)
+{
+ git_config *cfg;
+
+ cl_set_cleanup(&clean_test_config, NULL);
+ cl_git_mkfile("./testconfig", "[header]\n key1 = foo\\\n");
+ cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
+
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "header.key1"));
+ cl_assert_equal_s("foo", buf.ptr);
+
+ git_config_free(cfg);
+}
+
+void test_config_read__multiline_eof(void)
+{
+ git_config *cfg;
+
+ cl_set_cleanup(&clean_test_config, NULL);
+ cl_git_mkfile("./testconfig", "[header]\n key1 = \\\n");
+ cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
+
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "header.key1"));
+ cl_assert_equal_s("", buf.ptr);
+
+ git_config_free(cfg);
+}
+
+/*
+ * This kind of subsection declaration is case-insensitive
+ */
+void test_config_read__subsection_header(void)
+{
+ git_config *cfg;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config3")));
+
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "section.subsection.var"));
+ cl_assert_equal_s("hello", buf.ptr);
+
+ /* The subsection is transformed to lower-case */
+ cl_must_fail(git_config_get_string_buf(&buf, cfg, "section.subSectIon.var"));
+
+ git_config_free(cfg);
+}
+
+void test_config_read__lone_variable(void)
+{
+ git_config *cfg;
+ int i;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config4")));
+
+ cl_git_fail(git_config_get_int32(&i, cfg, "some.section.variable"));
+
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.section.variable"));
+ cl_assert_equal_s("", buf.ptr);
+ git_buf_dispose(&buf);
+
+ cl_git_pass(git_config_get_bool(&i, cfg, "some.section.variable"));
+ cl_assert(i == 1);
+
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.section.variableeq"));
+ cl_assert_equal_s("", buf.ptr);
+
+ cl_git_pass(git_config_get_bool(&i, cfg, "some.section.variableeq"));
+ cl_assert(i == 0);
+
+ git_config_free(cfg);
+}
+
+void test_config_read__number_suffixes(void)
+{
+ git_config *cfg;
+ int64_t i;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config5")));
+
+ cl_git_pass(git_config_get_int64(&i, cfg, "number.simple"));
+ cl_assert(i == 1);
+
+ cl_git_pass(git_config_get_int64(&i, cfg, "number.k"));
+ cl_assert(i == 1 * 1024);
+
+ cl_git_pass(git_config_get_int64(&i, cfg, "number.kk"));
+ cl_assert(i == 1 * 1024);
+
+ cl_git_pass(git_config_get_int64(&i, cfg, "number.m"));
+ cl_assert(i == 1 * 1024 * 1024);
+
+ cl_git_pass(git_config_get_int64(&i, cfg, "number.mm"));
+ cl_assert(i == 1 * 1024 * 1024);
+
+ cl_git_pass(git_config_get_int64(&i, cfg, "number.g"));
+ cl_assert(i == 1 * 1024 * 1024 * 1024);
+
+ cl_git_pass(git_config_get_int64(&i, cfg, "number.gg"));
+ cl_assert(i == 1 * 1024 * 1024 * 1024);
+
+ git_config_free(cfg);
+}
+
+void test_config_read__blank_lines(void)
+{
+ git_config *cfg;
+ int i;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config6")));
+
+ cl_git_pass(git_config_get_bool(&i, cfg, "valid.subsection.something"));
+ cl_assert(i == 1);
+
+ cl_git_pass(git_config_get_bool(&i, cfg, "something.else.something"));
+ cl_assert(i == 0);
+
+ git_config_free(cfg);
+}
+
+void test_config_read__invalid_ext_headers(void)
+{
+ git_config *cfg;
+ cl_must_fail(git_config_open_ondisk(&cfg, cl_fixture("config/config7")));
+}
+
+void test_config_read__empty_files(void)
+{
+ git_config *cfg;
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config8")));
+ git_config_free(cfg);
+}
+
+void test_config_read__symbol_headers(void)
+{
+ git_config *cfg;
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config20")));
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "valid.[subsection].something"));
+ cl_assert_equal_s("a", buf.ptr);
+ git_buf_dispose(&buf);
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "sec.[subsec]/child.parent"));
+ cl_assert_equal_s("grand", buf.ptr);
+ git_buf_dispose(&buf);
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "sec2.[subsec2]/child2.type"));
+ cl_assert_equal_s("dvcs", buf.ptr);
+ git_buf_dispose(&buf);
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "sec3.escape\"quote.vcs"));
+ cl_assert_equal_s("git", buf.ptr);
+ git_buf_dispose(&buf);
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "sec4.escaping\\slash.lib"));
+ cl_assert_equal_s("git2", buf.ptr);
+ git_buf_dispose(&buf);
+ git_config_free(cfg);
+}
+
+void test_config_read__multiline_multiple_quoted_comment_chars(void)
+{
+ git_config *cfg;
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config21")));
+ git_config_free(cfg);
+}
+
+void test_config_read__multiline_multiple_quoted_quote_at_beginning_of_line(void)
+{
+ git_config* cfg;
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config22")));
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "alias.m"));
+ cl_assert_equal_s("cmd ;; ;; bar", buf.ptr);
+ git_buf_dispose(&buf);
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "alias.m2"));
+ cl_assert_equal_s("'; ; something '", buf.ptr);
+ git_buf_dispose(&buf);
+ git_config_free(cfg);
+}
+
+void test_config_read__header_in_last_line(void)
+{
+ git_config *cfg;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config10")));
+ git_config_free(cfg);
+}
+
+void test_config_read__prefixes(void)
+{
+ git_config *cfg;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config9")));
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "remote.ab.url"));
+ cl_assert_equal_s("http://example.com/git/ab", buf.ptr);
+ git_buf_dispose(&buf);
+
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "remote.abba.url"));
+ cl_assert_equal_s("http://example.com/git/abba", buf.ptr);
+
+ git_config_free(cfg);
+}
+
+void test_config_read__escaping_quotes(void)
+{
+ git_config *cfg;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config13")));
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "core.editor"));
+ cl_assert_equal_s("\"C:/Program Files/Nonsense/bah.exe\" \"--some option\"", buf.ptr);
+
+ git_config_free(cfg);
+}
+
+void test_config_read__invalid_escape_sequence(void)
+{
+ git_config *cfg;
+
+ cl_set_cleanup(&clean_test_config, NULL);
+ cl_git_mkfile("./testconfig", "[header]\n key1 = \\\\\\;\n key2 = value2\n");
+ cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
+
+ git_config_free(cfg);
+}
+
+static int count_cfg_entries_and_compare_levels(
+ const git_config_entry *entry, void *payload)
+{
+ int *count = payload;
+
+ if (!strcmp(entry->value, "7") || !strcmp(entry->value, "17"))
+ cl_assert(entry->level == GIT_CONFIG_LEVEL_GLOBAL);
+ else
+ cl_assert(entry->level == GIT_CONFIG_LEVEL_SYSTEM);
+
+ (*count)++;
+ return 0;
+}
+
+static int cfg_callback_countdown(const git_config_entry *entry, void *payload)
+{
+ int *count = payload;
+ GIT_UNUSED(entry);
+ (*count)--;
+ if (*count == 0)
+ return -100;
+ return 0;
+}
+
+void test_config_read__foreach(void)
+{
+ git_config *cfg;
+ int count, ret;
+
+ cl_git_pass(git_config_new(&cfg));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
+ GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
+
+ count = 0;
+ cl_git_pass(git_config_foreach(cfg, count_cfg_entries_and_compare_levels, &count));
+ cl_assert_equal_i(7, count);
+
+ count = 3;
+ cl_git_fail(ret = git_config_foreach(cfg, cfg_callback_countdown, &count));
+ cl_assert_equal_i(-100, ret);
+
+ git_config_free(cfg);
+}
+
+void test_config_read__iterator(void)
+{
+ const char *keys[] = {
+ "core.dummy2",
+ "core.verylong",
+ "core.dummy",
+ "remote.ab.url",
+ "remote.abba.url",
+ "core.dummy2",
+ "core.global"
+ };
+ git_config *cfg;
+ git_config_iterator *iter;
+ git_config_entry *entry;
+ int count, ret;
+
+ cl_git_pass(git_config_new(&cfg));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
+ GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
+
+ count = 0;
+ cl_git_pass(git_config_iterator_new(&iter, cfg));
+
+ while ((ret = git_config_next(&entry, iter)) == 0) {
+ cl_assert_equal_s(entry->name, keys[count]);
+ count++;
+ }
+
+ git_config_iterator_free(iter);
+ cl_assert_equal_i(GIT_ITEROVER, ret);
+ cl_assert_equal_i(7, count);
+
+ count = 3;
+ cl_git_pass(git_config_iterator_new(&iter, cfg));
+
+ git_config_iterator_free(iter);
+ git_config_free(cfg);
+}
+
+static int count_cfg_entries(const git_config_entry *entry, void *payload)
+{
+ int *count = payload;
+ GIT_UNUSED(entry);
+ (*count)++;
+ return 0;
+}
+
+void test_config_read__foreach_match(void)
+{
+ git_config *cfg;
+ int count;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config9")));
+
+ count = 0;
+ cl_git_pass(
+ git_config_foreach_match(cfg, "core.*", count_cfg_entries, &count));
+ cl_assert_equal_i(3, count);
+
+ count = 0;
+ cl_git_pass(
+ git_config_foreach_match(cfg, "remote\\.ab.*", count_cfg_entries, &count));
+ cl_assert_equal_i(2, count);
+
+ count = 0;
+ cl_git_pass(
+ git_config_foreach_match(cfg, ".*url$", count_cfg_entries, &count));
+ cl_assert_equal_i(2, count);
+
+ count = 0;
+ cl_git_pass(
+ git_config_foreach_match(cfg, ".*dummy.*", count_cfg_entries, &count));
+ cl_assert_equal_i(2, count);
+
+ count = 0;
+ cl_git_pass(
+ git_config_foreach_match(cfg, ".*nomatch.*", count_cfg_entries, &count));
+ cl_assert_equal_i(0, count);
+
+ git_config_free(cfg);
+}
+
+static void check_glob_iter(git_config *cfg, const char *regexp, int expected)
+{
+ git_config_iterator *iter;
+ git_config_entry *entry;
+ int count, error;
+
+ cl_git_pass(git_config_iterator_glob_new(&iter, cfg, regexp));
+
+ count = 0;
+ while ((error = git_config_next(&entry, iter)) == 0)
+ count++;
+
+ cl_assert_equal_i(GIT_ITEROVER, error);
+ cl_assert_equal_i(expected, count);
+ git_config_iterator_free(iter);
+}
+
+void test_config_read__iterator_invalid_glob(void)
+{
+ git_config *cfg;
+ git_config_iterator *iter;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config9")));
+
+ cl_git_fail(git_config_iterator_glob_new(&iter, cfg, "*"));
+
+ git_config_free(cfg);
+}
+
+void test_config_read__iterator_glob(void)
+{
+ git_config *cfg;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config9")));
+
+ check_glob_iter(cfg, "core.*", 3);
+ check_glob_iter(cfg, "remote\\.ab.*", 2);
+ check_glob_iter(cfg, ".*url$", 2);
+ check_glob_iter(cfg, ".*dummy.*", 2);
+ check_glob_iter(cfg, ".*nomatch.*", 0);
+
+ git_config_free(cfg);
+}
+
+void test_config_read__whitespace_not_required_around_assignment(void)
+{
+ git_config *cfg;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config14")));
+
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "a.b"));
+ cl_assert_equal_s("c", buf.ptr);
+ git_buf_dispose(&buf);
+
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "d.e"));
+ cl_assert_equal_s("f", buf.ptr);
+
+ git_config_free(cfg);
+}
+
+void test_config_read__read_git_config_entry(void)
+{
+ git_config *cfg;
+ git_config_entry *entry;
+
+ cl_git_pass(git_config_new(&cfg));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
+ GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
+
+ cl_git_pass(git_config_get_entry(&entry, cfg, "core.dummy2"));
+ cl_assert_equal_s("core.dummy2", entry->name);
+ cl_assert_equal_s("42", entry->value);
+ cl_assert_equal_i(GIT_CONFIG_LEVEL_SYSTEM, entry->level);
+
+ git_config_entry_free(entry);
+ git_config_free(cfg);
+}
+
+/*
+ * At the beginning of the test:
+ * - config9 has: core.dummy2=42
+ * - config15 has: core.dummy2=7
+ * - config16 has: core.dummy2=28
+ */
+void test_config_read__local_config_overrides_global_config_overrides_system_config(void)
+{
+ git_config *cfg;
+ int32_t i;
+
+ cl_git_pass(git_config_new(&cfg));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
+ GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config16"),
+ GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
+
+ cl_git_pass(git_config_get_int32(&i, cfg, "core.dummy2"));
+ cl_assert_equal_i(28, i);
+
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_new(&cfg));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
+ GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
+
+ cl_git_pass(git_config_get_int32(&i, cfg, "core.dummy2"));
+ cl_assert_equal_i(7, i);
+
+ git_config_free(cfg);
+}
+
+/*
+ * At the beginning of the test:
+ * - config9 has: core.global does not exist
+ * - config15 has: core.global=17
+ * - config16 has: core.global=29
+ *
+ * And also:
+ * - config9 has: core.system does not exist
+ * - config15 has: core.system does not exist
+ * - config16 has: core.system=11
+ */
+void test_config_read__fallback_from_local_to_global_and_from_global_to_system(void)
+{
+ git_config *cfg;
+ int32_t i;
+
+ cl_git_pass(git_config_new(&cfg));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config9"),
+ GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config15"),
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config16"),
+ GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
+
+ cl_git_pass(git_config_get_int32(&i, cfg, "core.global"));
+ cl_assert_equal_i(17, i);
+ cl_git_pass(git_config_get_int32(&i, cfg, "core.system"));
+ cl_assert_equal_i(11, i);
+
+ git_config_free(cfg);
+}
+
+void test_config_read__parent_dir_is_file(void)
+{
+ git_config *cfg;
+ int count;
+
+ cl_git_pass(git_config_new(&cfg));
+ /*
+ * Verify we can add non-existing files when the parent directory is not
+ * a directory.
+ */
+ cl_git_pass(git_config_add_file_ondisk(cfg, "/dev/null/.gitconfig",
+ GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
+
+ count = 0;
+ cl_git_pass(git_config_foreach(cfg, count_cfg_entries_and_compare_levels, &count));
+ cl_assert_equal_i(0, count);
+
+ git_config_free(cfg);
+}
+
+/*
+ * At the beginning of the test, config18 has:
+ * int32global = 28
+ * int64global = 9223372036854775803
+ * boolglobal = true
+ * stringglobal = I'm a global config value!
+ *
+ * And config19 has:
+ * int32global = -1
+ * int64global = -2
+ * boolglobal = false
+ * stringglobal = don't find me!
+ *
+ */
+void test_config_read__simple_read_from_specific_level(void)
+{
+ git_config *cfg, *cfg_specific;
+ int i;
+ int64_t l, expected = +9223372036854775803;
+
+ cl_git_pass(git_config_new(&cfg));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config18"),
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
+ cl_git_pass(git_config_add_file_ondisk(cfg, cl_fixture("config/config19"),
+ GIT_CONFIG_LEVEL_SYSTEM, NULL, 0));
+
+ cl_git_pass(git_config_open_level(&cfg_specific, cfg, GIT_CONFIG_LEVEL_GLOBAL));
+
+ cl_git_pass(git_config_get_int32(&i, cfg_specific, "core.int32global"));
+ cl_assert_equal_i(28, i);
+ cl_git_pass(git_config_get_int64(&l, cfg_specific, "core.int64global"));
+ cl_assert(l == expected);
+ cl_git_pass(git_config_get_bool(&i, cfg_specific, "core.boolglobal"));
+ cl_assert_equal_b(true, i);
+ cl_git_pass(git_config_get_string_buf(&buf, cfg_specific, "core.stringglobal"));
+ cl_assert_equal_s("I'm a global config value!", buf.ptr);
+
+ git_config_free(cfg_specific);
+ git_config_free(cfg);
+}
+
+void test_config_read__can_load_and_parse_an_empty_config_file(void)
+{
+ git_config *cfg;
+ int i;
+
+ cl_set_cleanup(&clean_test_config, NULL);
+ cl_git_mkfile("./testconfig", "");
+ cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_config_get_int32(&i, cfg, "nope.neither"));
+
+ git_config_free(cfg);
+}
+
+void test_config_read__corrupt_header(void)
+{
+ git_config *cfg;
+
+ cl_set_cleanup(&clean_test_config, NULL);
+ cl_git_mkfile("./testconfig", "[sneaky ] \"quoted closing quote mark\\\"");
+ cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
+
+ git_config_free(cfg);
+}
+
+void test_config_read__corrupt_header2(void)
+{
+ git_config *cfg;
+
+ cl_set_cleanup(&clean_test_config, NULL);
+ cl_git_mkfile("./testconfig", "[unclosed \"bracket\"\n lib = git2\n");
+ cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
+
+ git_config_free(cfg);
+}
+
+void test_config_read__corrupt_header3(void)
+{
+ git_config *cfg;
+
+ cl_set_cleanup(&clean_test_config, NULL);
+ cl_git_mkfile("./testconfig", "[unclosed \"slash\\\"]\n lib = git2\n");
+ cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
+
+ git_config_free(cfg);
+}
+
+void test_config_read__invalid_key_chars(void)
+{
+ git_config *cfg;
+
+ cl_set_cleanup(&clean_test_config, NULL);
+ cl_git_mkfile("./testconfig", "[foo]\n has_underscore = git2\n");
+ cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
+
+ cl_git_rewritefile("./testconfig", "[foo]\n has/slash = git2\n");
+ cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
+
+ cl_git_rewritefile("./testconfig", "[foo]\n has+plus = git2\n");
+ cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
+
+ cl_git_rewritefile("./testconfig", "[no_key]\n = git2\n");
+ cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
+
+ git_config_free(cfg);
+}
+
+void test_config_read__lone_variable_with_trailing_whitespace(void)
+{
+ git_config *cfg;
+ int b;
+
+ cl_set_cleanup(&clean_test_config, NULL);
+ cl_git_mkfile("./testconfig", "[foo]\n lonevariable \n");
+ cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
+
+ cl_git_pass(git_config_get_bool(&b, cfg, "foo.lonevariable"));
+ cl_assert_equal_b(true, b);
+
+ git_config_free(cfg);
+}
+
+void test_config_read__override_variable(void)
+{
+ git_config *cfg;
+
+ cl_set_cleanup(&clean_test_config, NULL);
+ cl_git_mkfile("./testconfig", "[some] var = one\nvar = two");
+ cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
+
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.var"));
+ cl_assert_equal_s("two", buf.ptr);
+
+ git_config_free(cfg);
+}
+
+void test_config_read__path(void)
+{
+ git_config *cfg;
+ git_buf path = GIT_BUF_INIT;
+ git_str home_path = GIT_STR_INIT;
+ git_str expected_path = GIT_STR_INIT;
+
+ cl_fake_homedir(&home_path);
+
+ cl_git_mkfile("./testconfig", "[some]\n path = ~/somefile");
+ cl_git_pass(git_fs_path_join_unrooted(&expected_path, "somefile", home_path.ptr, NULL));
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
+ cl_git_pass(git_config_get_path(&path, cfg, "some.path"));
+ cl_assert_equal_s(expected_path.ptr, path.ptr);
+ git_buf_dispose(&path);
+
+ cl_git_mkfile("./testconfig", "[some]\n path = ~/");
+ cl_git_pass(git_fs_path_join_unrooted(&expected_path, "", home_path.ptr, NULL));
+
+ cl_git_pass(git_config_get_path(&path, cfg, "some.path"));
+ cl_assert_equal_s(expected_path.ptr, path.ptr);
+ git_buf_dispose(&path);
+
+ cl_git_mkfile("./testconfig", "[some]\n path = ~");
+ cl_git_pass(git_str_sets(&expected_path, home_path.ptr));
+
+ cl_git_pass(git_config_get_path(&path, cfg, "some.path"));
+ cl_assert_equal_s(expected_path.ptr, path.ptr);
+ git_buf_dispose(&path);
+
+ cl_git_mkfile("./testconfig", "[some]\n path = ~user/foo");
+ cl_git_fail(git_config_get_path(&path, cfg, "some.path"));
+
+ git_str_dispose(&home_path);
+ git_str_dispose(&expected_path);
+ git_config_free(cfg);
+}
+
+void test_config_read__crlf_style_line_endings(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+ git_config *cfg;
+
+ cl_set_cleanup(&clean_test_config, NULL);
+ cl_git_mkfile("./testconfig", "[some]\r\n var = value\r\n");
+ cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.var"));
+ cl_assert_equal_s(buf.ptr, "value");
+
+ git_config_free(cfg);
+ git_buf_dispose(&buf);
+}
+
+void test_config_read__trailing_crlf(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+ git_config *cfg;
+
+ cl_set_cleanup(&clean_test_config, NULL);
+ cl_git_mkfile("./testconfig", "[some]\r\n var = value\r\n\r\n");
+ cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.var"));
+ cl_assert_equal_s(buf.ptr, "value");
+
+ git_config_free(cfg);
+ git_buf_dispose(&buf);
+}
+
+void test_config_read__bom(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+ git_config *cfg;
+
+ cl_set_cleanup(&clean_test_config, NULL);
+ cl_git_mkfile("./testconfig", "\xEF\xBB\xBF[some]\n var = value\n");
+ cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.var"));
+ cl_assert_equal_s(buf.ptr, "value");
+
+ git_config_free(cfg);
+ git_buf_dispose(&buf);
+}
+
+void test_config_read__arbitrary_whitespace_before_subsection(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+ git_config *cfg;
+
+ cl_set_cleanup(&clean_test_config, NULL);
+ cl_git_mkfile("./testconfig", "[some \t \"subsection\"]\n var = value\n");
+ cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.subsection.var"));
+ cl_assert_equal_s(buf.ptr, "value");
+
+ git_config_free(cfg);
+ git_buf_dispose(&buf);
+}
+
+void test_config_read__no_whitespace_after_subsection(void)
+{
+ git_config *cfg;
+
+ cl_set_cleanup(&clean_test_config, NULL);
+ cl_git_mkfile("./testconfig", "[some \"subsection\" ]\n var = value\n");
+ cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
+
+ git_config_free(cfg);
+}
+
+void test_config_read__invalid_space_section(void)
+{
+ git_config *cfg;
+
+ cl_set_cleanup(&clean_test_config, NULL);
+ cl_git_mkfile("./testconfig", "\xEF\xBB\xBF[some section]\n var = value\n");
+ cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
+
+ git_config_free(cfg);
+}
+
+void test_config_read__invalid_quoted_first_section(void)
+{
+ git_config *cfg;
+
+ cl_set_cleanup(&clean_test_config, NULL);
+ cl_git_mkfile("./testconfig", "\xEF\xBB\xBF[\"some\"]\n var = value\n");
+ cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
+
+ git_config_free(cfg);
+}
+
+void test_config_read__invalid_unquoted_subsection(void)
+{
+ git_config *cfg;
+
+ cl_set_cleanup(&clean_test_config, NULL);
+ cl_git_mkfile("./testconfig", "\xEF\xBB\xBF[some sub section]\n var = value\n");
+ cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
+
+ git_config_free(cfg);
+}
+
+void test_config_read__invalid_quoted_third_section(void)
+{
+ git_config *cfg;
+
+ cl_set_cleanup(&clean_test_config, NULL);
+ cl_git_mkfile("./testconfig", "\xEF\xBB\xBF[some sub \"section\"]\n var = value\n");
+ cl_git_fail(git_config_open_ondisk(&cfg, "./testconfig"));
+
+ git_config_free(cfg);
+}
+
+void test_config_read__unreadable_file_ignored(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+ git_config *cfg;
+ int ret;
+
+ cl_set_cleanup(&clean_test_config, NULL);
+ cl_git_mkfile("./testconfig", "[some] var = value\n[some \"OtheR\"] var = value");
+ cl_git_pass(p_chmod("./testconfig", 0));
+
+ ret = git_config_open_ondisk(&cfg, "./test/config");
+ cl_assert(ret == 0 || ret == GIT_ENOTFOUND);
+
+ git_config_free(cfg);
+ git_buf_dispose(&buf);
+}
+
+void test_config_read__single_line(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+ git_config *cfg;
+
+ cl_set_cleanup(&clean_test_config, NULL);
+ cl_git_mkfile("./testconfig", "[some] var = value\n[some \"OtheR\"] var = value");
+ cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.var"));
+ cl_assert_equal_s(buf.ptr, "value");
+
+ git_buf_dispose(&buf);
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.OtheR.var"));
+ cl_assert_equal_s(buf.ptr, "value");
+
+ git_config_free(cfg);
+ cl_git_mkfile("./testconfig", "[some] var = value\n[some \"OtheR\"]var = value");
+ cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
+ git_buf_dispose(&buf);
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.var"));
+ cl_assert_equal_s(buf.ptr, "value");
+
+ git_buf_dispose(&buf);
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "some.OtheR.var"));
+ cl_assert_equal_s(buf.ptr, "value");
+
+ git_config_free(cfg);
+ git_buf_dispose(&buf);
+}
+
+static int read_nosection_cb(const git_config_entry *entry, void *payload) {
+ int *seen = (int*)payload;
+ if (strcmp(entry->name, "key") == 0) {
+ (*seen)++;
+ }
+ return 0;
+}
+
+/* This would ideally issue a warning, if we had a way to do so. */
+void test_config_read__nosection(void)
+{
+ git_config *cfg;
+ git_buf buf = GIT_BUF_INIT;
+ int seen = 0;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, cl_fixture("config/config-nosection")));
+
+ /*
+ * Given a key with no section, we do not allow reading it,
+ * but we do include it in an iteration over the config
+ * store. This appears to match how git's own APIs (and
+ * git-config(1)) behave.
+ */
+
+ cl_git_fail_with(git_config_get_string_buf(&buf, cfg, "key"), GIT_EINVALIDSPEC);
+
+ cl_git_pass(git_config_foreach(cfg, read_nosection_cb, &seen));
+ cl_assert_equal_i(seen, 1);
+
+ git_buf_dispose(&buf);
+ git_config_free(cfg);
+}
+
+enum {
+ MAP_TRUE = 0,
+ MAP_FALSE = 1,
+ MAP_ALWAYS = 2
+};
+
+static git_configmap _test_map1[] = {
+ {GIT_CONFIGMAP_STRING, "always", MAP_ALWAYS},
+ {GIT_CONFIGMAP_FALSE, NULL, MAP_FALSE},
+ {GIT_CONFIGMAP_TRUE, NULL, MAP_TRUE},
+};
+
+static git_configmap _test_map2[] = {
+ {GIT_CONFIGMAP_INT32, NULL, 0},
+};
+
+void test_config_read__get_mapped(void)
+{
+ git_config *cfg;
+ int val;
+ int known_good;
+
+ cl_set_cleanup(&clean_test_config, NULL);
+ cl_git_mkfile("./testconfig", "[header]\n"
+ " key1 = 1\n"
+ " key2 = true\n"
+ " key3\n"
+ " key4 = always\n"
+ " key5 = false\n"
+ " key6 = 0\n"
+ " key7 = never\n"
+ " key8 = On\n"
+ " key9 = off\n");
+ cl_git_pass(git_config_open_ondisk(&cfg, "./testconfig"));
+
+ /* check parsing bool and string */
+ cl_git_pass(git_config_get_mapped(&val, cfg, "header.key1", _test_map1, ARRAY_SIZE(_test_map1)));
+ cl_assert_equal_i(val, MAP_TRUE);
+ cl_git_pass(git_config_get_mapped(&val, cfg, "header.key2", _test_map1, ARRAY_SIZE(_test_map1)));
+ cl_assert_equal_i(val, MAP_TRUE);
+ cl_git_pass(git_config_get_mapped(&val, cfg, "header.key3", _test_map1, ARRAY_SIZE(_test_map1)));
+ cl_assert_equal_i(val, MAP_TRUE);
+ cl_git_pass(git_config_get_mapped(&val, cfg, "header.key8", _test_map1, ARRAY_SIZE(_test_map1)));
+ cl_assert_equal_i(val, MAP_TRUE);
+
+ cl_git_pass(git_config_get_mapped(&val, cfg, "header.key4", _test_map1, ARRAY_SIZE(_test_map1)));
+ cl_assert_equal_i(val, MAP_ALWAYS);
+
+ cl_git_pass(git_config_get_mapped(&val, cfg, "header.key5", _test_map1, ARRAY_SIZE(_test_map1)));
+ cl_assert_equal_i(val, MAP_FALSE);
+ cl_git_pass(git_config_get_mapped(&val, cfg, "header.key6", _test_map1, ARRAY_SIZE(_test_map1)));
+ cl_assert_equal_i(val, MAP_FALSE);
+ cl_git_pass(git_config_get_mapped(&val, cfg, "header.key9", _test_map1, ARRAY_SIZE(_test_map1)));
+ cl_assert_equal_i(val, MAP_FALSE);
+
+ cl_git_fail(git_config_get_mapped(&val, cfg, "header.key7", _test_map1, ARRAY_SIZE(_test_map1)));
+
+ /* check parsing int values */
+ cl_git_pass(git_config_get_mapped(&val, cfg, "header.key1", _test_map2, ARRAY_SIZE(_test_map2)));
+ cl_git_pass(git_config_get_int32(&known_good, cfg, "header.key1"));
+ cl_assert_equal_i(val, known_good);
+ cl_git_pass(git_config_get_mapped(&val, cfg, "header.key6", _test_map2, ARRAY_SIZE(_test_map2)));
+ cl_git_pass(git_config_get_int32(&known_good, cfg, "header.key6"));
+ cl_assert_equal_i(val, known_good);
+
+ cl_git_fail(git_config_get_mapped(&val, cfg, "header.key2", _test_map2, ARRAY_SIZE(_test_map2)));
+ cl_git_fail(git_config_get_mapped(&val, cfg, "header.key3", _test_map2, ARRAY_SIZE(_test_map2)));
+ cl_git_fail(git_config_get_mapped(&val, cfg, "header.key4", _test_map2, ARRAY_SIZE(_test_map2)));
+ cl_git_fail(git_config_get_mapped(&val, cfg, "header.key5", _test_map2, ARRAY_SIZE(_test_map2)));
+ cl_git_fail(git_config_get_mapped(&val, cfg, "header.key7", _test_map2, ARRAY_SIZE(_test_map2)));
+ cl_git_fail(git_config_get_mapped(&val, cfg, "header.key8", _test_map2, ARRAY_SIZE(_test_map2)));
+ cl_git_fail(git_config_get_mapped(&val, cfg, "header.key9", _test_map2, ARRAY_SIZE(_test_map2)));
+
+ git_config_free(cfg);
+}
diff --git a/tests/libgit2/config/readonly.c b/tests/libgit2/config/readonly.c
new file mode 100644
index 0000000..a8901e3
--- /dev/null
+++ b/tests/libgit2/config/readonly.c
@@ -0,0 +1,65 @@
+#include "clar_libgit2.h"
+#include "config_backend.h"
+#include "config.h"
+#include "path.h"
+
+static git_config *cfg;
+
+void test_config_readonly__initialize(void)
+{
+ cl_git_pass(git_config_new(&cfg));
+}
+
+void test_config_readonly__cleanup(void)
+{
+ git_config_free(cfg);
+ cfg = NULL;
+}
+
+void test_config_readonly__writing_to_readonly_fails(void)
+{
+ git_config_backend *backend;
+
+ cl_git_pass(git_config_backend_from_file(&backend, "global"));
+ backend->readonly = 1;
+ cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
+
+ cl_git_fail_with(GIT_ENOTFOUND, git_config_set_string(cfg, "foo.bar", "baz"));
+ cl_assert(!git_fs_path_exists("global"));
+}
+
+void test_config_readonly__writing_to_cfg_with_rw_precedence_succeeds(void)
+{
+ git_config_backend *backend;
+
+ cl_git_pass(git_config_backend_from_file(&backend, "global"));
+ backend->readonly = 1;
+ cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
+
+ cl_git_pass(git_config_backend_from_file(&backend, "local"));
+ cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
+
+ cl_git_pass(git_config_set_string(cfg, "foo.bar", "baz"));
+
+ cl_assert(git_fs_path_exists("local"));
+ cl_assert(!git_fs_path_exists("global"));
+ cl_git_pass(p_unlink("local"));
+}
+
+void test_config_readonly__writing_to_cfg_with_ro_precedence_succeeds(void)
+{
+ git_config_backend *backend;
+
+ cl_git_pass(git_config_backend_from_file(&backend, "local"));
+ backend->readonly = 1;
+ cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
+
+ cl_git_pass(git_config_backend_from_file(&backend, "global"));
+ cl_git_pass(git_config_add_backend(cfg, backend, GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
+
+ cl_git_pass(git_config_set_string(cfg, "foo.bar", "baz"));
+
+ cl_assert(!git_fs_path_exists("local"));
+ cl_assert(git_fs_path_exists("global"));
+ cl_git_pass(p_unlink("global"));
+}
diff --git a/tests/libgit2/config/rename.c b/tests/libgit2/config/rename.c
new file mode 100644
index 0000000..be620c3
--- /dev/null
+++ b/tests/libgit2/config/rename.c
@@ -0,0 +1,89 @@
+#include "clar_libgit2.h"
+#include "config.h"
+
+static git_repository *g_repo = NULL;
+static git_config *g_config = NULL;
+
+void test_config_rename__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo.git");
+ cl_git_pass(git_repository_config(&g_config, g_repo));
+}
+
+void test_config_rename__cleanup(void)
+{
+ git_config_free(g_config);
+ g_config = NULL;
+
+ cl_git_sandbox_cleanup();
+ g_repo = NULL;
+}
+
+void test_config_rename__can_rename(void)
+{
+ git_config_entry *ce;
+
+ cl_git_pass(git_config_get_entry(
+ &ce, g_config, "branch.track-local.remote"));
+ cl_assert_equal_s(".", ce->value);
+ git_config_entry_free(ce);
+
+ cl_git_fail(git_config_get_entry(
+ &ce, g_config, "branch.local-track.remote"));
+
+ cl_git_pass(git_config_rename_section(
+ g_repo, "branch.track-local", "branch.local-track"));
+
+ cl_git_pass(git_config_get_entry(
+ &ce, g_config, "branch.local-track.remote"));
+ cl_assert_equal_s(".", ce->value);
+ git_config_entry_free(ce);
+
+ cl_git_fail(git_config_get_entry(
+ &ce, g_config, "branch.track-local.remote"));
+}
+
+void test_config_rename__prevent_overwrite(void)
+{
+ git_config_entry *ce;
+
+ cl_git_pass(git_config_set_string(
+ g_config, "branch.local-track.remote", "yellow"));
+
+ cl_git_pass(git_config_get_entry(
+ &ce, g_config, "branch.local-track.remote"));
+ cl_assert_equal_s("yellow", ce->value);
+ git_config_entry_free(ce);
+
+ cl_git_pass(git_config_rename_section(
+ g_repo, "branch.track-local", "branch.local-track"));
+
+ cl_git_pass(git_config_get_entry(
+ &ce, g_config, "branch.local-track.remote"));
+ cl_assert_equal_s(".", ce->value);
+ git_config_entry_free(ce);
+
+ /* so, we don't currently prevent overwrite... */
+ /* {
+ const git_error *err;
+ cl_assert((err = git_error_last()) != NULL);
+ cl_assert(err->message != NULL);
+ } */
+}
+
+static void assert_invalid_config_section_name(
+ git_repository *repo, const char *name)
+{
+ cl_git_fail_with(
+ git_config_rename_section(repo, "branch.remoteless", name),
+ GIT_EINVALIDSPEC);
+}
+
+void test_config_rename__require_a_valid_new_name(void)
+{
+ assert_invalid_config_section_name(g_repo, "");
+ assert_invalid_config_section_name(g_repo, "bra\nch");
+ assert_invalid_config_section_name(g_repo, "branc#");
+ assert_invalid_config_section_name(g_repo, "bra\nch.duh");
+ assert_invalid_config_section_name(g_repo, "branc#.duh");
+}
diff --git a/tests/libgit2/config/snapshot.c b/tests/libgit2/config/snapshot.c
new file mode 100644
index 0000000..5cc08a7
--- /dev/null
+++ b/tests/libgit2/config/snapshot.c
@@ -0,0 +1,139 @@
+#include "clar_libgit2.h"
+
+#include "config_backend.h"
+
+static git_config *cfg;
+static git_config *snapshot;
+
+void test_config_snapshot__cleanup(void)
+{
+ git_config_free(cfg);
+ cfg = NULL;
+ git_config_free(snapshot);
+ snapshot = NULL;
+}
+
+void test_config_snapshot__create_snapshot(void)
+{
+ int32_t i;
+
+ cl_git_mkfile("config", "[old]\nvalue = 5\n");
+ cl_git_pass(git_config_open_ondisk(&cfg, "config"));
+ cl_git_pass(git_config_get_int32(&i, cfg, "old.value"));
+ cl_assert_equal_i(5, i);
+
+ cl_git_pass(git_config_snapshot(&snapshot, cfg));
+
+ /* Change the value on the file itself (simulate external process) */
+ cl_git_mkfile("config", "[old]\nvalue = 56\n");
+
+ cl_git_pass(git_config_get_int32(&i, cfg, "old.value"));
+ cl_assert_equal_i(56, i);
+ cl_git_pass(git_config_get_int32(&i, snapshot, "old.value"));
+ cl_assert_equal_i(5, i);
+
+ /* Change the value on the file itself (simulate external process) */
+ cl_git_mkfile("config", "[old]\nvalue = 999\n");
+
+ /* Old snapshot should still have the old value */
+ cl_git_pass(git_config_get_int32(&i, snapshot, "old.value"));
+ cl_assert_equal_i(5, i);
+
+ /* New snapshot should see new value */
+ git_config_free(snapshot);
+ cl_git_pass(git_config_snapshot(&snapshot, cfg));
+ cl_git_pass(git_config_get_int32(&i, snapshot, "old.value"));
+ cl_assert_equal_i(999, i);
+
+ cl_git_pass(p_unlink("config"));
+}
+
+static int count_me(const git_config_entry *entry, void *payload)
+{
+ int *n = (int *) payload;
+
+ GIT_UNUSED(entry);
+
+ (*n)++;
+
+ return 0;
+}
+
+void test_config_snapshot__multivar(void)
+{
+ int count;
+
+ count = 0;
+ cl_git_mkfile("config", "[old]\nvalue = 5\nvalue = 6\n");
+ cl_git_pass(git_config_open_ondisk(&cfg, "config"));
+ cl_git_pass(git_config_get_multivar_foreach(cfg, "old.value", NULL, count_me, &count));
+ cl_assert_equal_i(2, count);
+
+ count = 0;
+ cl_git_pass(git_config_snapshot(&snapshot, cfg));
+ cl_git_pass(git_config_get_multivar_foreach(snapshot, "old.value", NULL, count_me, &count));
+ cl_assert_equal_i(2, count);
+
+ cl_git_pass(p_unlink("config"));
+}
+
+void test_config_snapshot__includes(void)
+{
+ int i;
+
+ cl_git_mkfile("including", "[include]\npath = included");
+ cl_git_mkfile("included", "[section]\nkey = 1\n");
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "including"));
+ cl_git_pass(git_config_snapshot(&snapshot, cfg));
+
+ cl_git_pass(git_config_get_int32(&i, snapshot, "section.key"));
+ cl_assert_equal_i(i, 1);
+
+ /* Rewrite "included" config */
+ cl_git_mkfile("included", "[section]\nkey = 11\n");
+
+ /* Assert that the live config changed, but snapshot remained the same */
+ cl_git_pass(git_config_get_int32(&i, cfg, "section.key"));
+ cl_assert_equal_i(i, 11);
+ cl_git_pass(git_config_get_int32(&i, snapshot, "section.key"));
+ cl_assert_equal_i(i, 1);
+
+ cl_git_pass(p_unlink("including"));
+ cl_git_pass(p_unlink("included"));
+}
+
+void test_config_snapshot__snapshot(void)
+{
+ git_config *snapshot_snapshot;
+ int i;
+
+ cl_git_mkfile("configfile", "[section]\nkey = 1\n");
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "configfile"));
+ cl_git_pass(git_config_snapshot(&snapshot, cfg));
+
+ cl_git_pass(git_config_snapshot(&snapshot_snapshot, snapshot));
+
+ cl_git_pass(git_config_get_int32(&i, snapshot_snapshot, "section.key"));
+ cl_assert_equal_i(i, 1);
+
+ git_config_free(snapshot_snapshot);
+
+ cl_git_pass(p_unlink("configfile"));
+}
+
+void test_config_snapshot__snapshot_from_in_memony(void)
+{
+ const char *configuration = "[section]\nkey = 1\n";
+ git_config_backend *backend;
+ int i;
+
+ cl_git_pass(git_config_new(&cfg));
+ cl_git_pass(git_config_backend_from_string(&backend, configuration, strlen(configuration)));
+ cl_git_pass(git_config_add_backend(cfg, backend, 0, NULL, 0));
+
+ cl_git_pass(git_config_snapshot(&snapshot, cfg));
+ cl_git_pass(git_config_get_int32(&i, snapshot, "section.key"));
+ cl_assert_equal_i(i, 1);
+}
diff --git a/tests/libgit2/config/stress.c b/tests/libgit2/config/stress.c
new file mode 100644
index 0000000..69e6810
--- /dev/null
+++ b/tests/libgit2/config/stress.c
@@ -0,0 +1,206 @@
+#include "clar_libgit2.h"
+
+#include "filebuf.h"
+#include "futils.h"
+#include "posix.h"
+
+#define TEST_CONFIG "git-test-config"
+
+static git_buf buf = GIT_BUF_INIT;
+
+void test_config_stress__initialize(void)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+
+ cl_git_pass(git_filebuf_open(&file, TEST_CONFIG, 0, 0666));
+
+ git_filebuf_printf(&file, "[color]\n\tui = auto\n");
+ git_filebuf_printf(&file, "[core]\n\teditor = \n");
+
+ cl_git_pass(git_filebuf_commit(&file));
+}
+
+void test_config_stress__cleanup(void)
+{
+ git_buf_dispose(&buf);
+ p_unlink(TEST_CONFIG);
+}
+
+void test_config_stress__dont_break_on_invalid_input(void)
+{
+ git_config *config;
+
+ cl_assert(git_fs_path_exists(TEST_CONFIG));
+ cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG));
+
+ cl_git_pass(git_config_get_string_buf(&buf, config, "color.ui"));
+ cl_git_pass(git_config_get_string_buf(&buf, config, "core.editor"));
+
+ git_config_free(config);
+}
+
+static void assert_config_value(git_config *config, const char *key, const char *value)
+{
+ git_buf_dispose(&buf);
+ cl_git_pass(git_config_get_string_buf(&buf, config, key));
+ cl_assert_equal_s(value, buf.ptr);
+}
+
+void test_config_stress__comments(void)
+{
+ git_config *config;
+
+ cl_git_pass(git_config_open_ondisk(&config, cl_fixture("config/config12")));
+
+ assert_config_value(config, "some.section.test2", "hello");
+ assert_config_value(config, "some.section.test3", "welcome");
+ assert_config_value(config, "some.section.other", "hello! \" ; ; ; ");
+ assert_config_value(config, "some.section.other2", "cool! \" # # # ");
+ assert_config_value(config, "some.section.multi", "hi, this is a ; multiline comment # with ;\n special chars and other stuff !@#");
+ assert_config_value(config, "some.section.multi2", "good, this is a ; multiline comment # with ;\n special chars and other stuff !@#");
+ assert_config_value(config, "some.section.back", "this is \ba phrase");
+ assert_config_value(config, "some.section.dollar", "some $sign");
+ assert_config_value(config, "some.section.multiquotes", "!ls x ls # comment2 $HOME");
+ assert_config_value(config, "some.section.multiquotes2", "!ls x ls \"# comment2 $HOME\"");
+ assert_config_value(config, "some.section.multiquotes3", "hi # ho there are # more quotes");
+ assert_config_value(config, "some.section.quotecomment", "hi # ho there are # more");
+
+ git_config_free(config);
+}
+
+void test_config_stress__escape_subsection_names(void)
+{
+ git_config *config;
+
+ cl_assert(git_fs_path_exists("git-test-config"));
+ cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG));
+
+ cl_git_pass(git_config_set_string(config, "some.sec\\tion.other", "foo"));
+ git_config_free(config);
+
+ cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG));
+
+ assert_config_value(config, "some.sec\\tion.other", "foo");
+
+ git_config_free(config);
+}
+
+void test_config_stress__trailing_backslash(void)
+{
+ git_config *config;
+ const char *path = "C:\\iam\\some\\windows\\path\\";
+
+ cl_assert(git_fs_path_exists("git-test-config"));
+ cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG));
+ cl_git_pass(git_config_set_string(config, "windows.path", path));
+ git_config_free(config);
+
+ cl_git_pass(git_config_open_ondisk(&config, TEST_CONFIG));
+ assert_config_value(config, "windows.path", path);
+
+ git_config_free(config);
+}
+
+void test_config_stress__complex(void)
+{
+ git_config *config;
+ const char *path = "./config-immediate-multiline";
+
+ cl_git_mkfile(path, "[imm]\n multi = \"\\\nfoo\"");
+ cl_git_pass(git_config_open_ondisk(&config, path));
+ assert_config_value(config, "imm.multi", "foo");
+
+ git_config_free(config);
+}
+
+void test_config_stress__quick_write(void)
+{
+ git_config *config_w, *config_r;
+ const char *path = "./config-quick-write";
+ const char *key = "quick.write";
+ int32_t i;
+
+ /* Create an external writer for one instance with the other one */
+ cl_git_pass(git_config_open_ondisk(&config_w, path));
+ cl_git_pass(git_config_open_ondisk(&config_r, path));
+
+ /* Write and read in the same second (repeat to increase the chance of it happening) */
+ for (i = 0; i < 10; i++) {
+ int32_t val;
+ cl_git_pass(git_config_set_int32(config_w, key, i));
+ cl_msleep(1);
+ cl_git_pass(git_config_get_int32(&val, config_r, key));
+ cl_assert_equal_i(i, val);
+ }
+
+ git_config_free(config_r);
+ git_config_free(config_w);
+}
+
+static int foreach_cb(const git_config_entry *entry, void *payload)
+{
+ if (!strcmp(entry->name, "key.value")) {
+ *(char **)payload = git__strdup(entry->value);
+ return 0;
+ }
+ return -1;
+}
+
+void test_config_stress__foreach_refreshes(void)
+{
+ git_config *config_w, *config_r;
+ char *value = NULL;
+
+ cl_git_pass(git_config_open_ondisk(&config_w, "./cfg"));
+ cl_git_pass(git_config_open_ondisk(&config_r, "./cfg"));
+
+ cl_git_pass(git_config_set_string(config_w, "key.value", "1"));
+ cl_git_pass(git_config_foreach_match(config_r, "key.value", foreach_cb, &value));
+
+ cl_assert_equal_s(value, "1");
+
+ git_config_free(config_r);
+ git_config_free(config_w);
+ git__free(value);
+}
+
+void test_config_stress__foreach_refreshes_snapshot(void)
+{
+ git_config *config, *snapshot;
+ char *value = NULL;
+
+ cl_git_pass(git_config_open_ondisk(&config, "./cfg"));
+
+ cl_git_pass(git_config_set_string(config, "key.value", "1"));
+ cl_git_pass(git_config_snapshot(&snapshot, config));
+ cl_git_pass(git_config_foreach_match(snapshot, "key.value", foreach_cb, &value));
+
+ cl_assert_equal_s(value, "1");
+
+ git_config_free(snapshot);
+ git_config_free(config);
+ git__free(value);
+}
+
+void test_config_stress__huge_section_with_many_values(void)
+{
+ git_config *config;
+
+ if (!cl_is_env_set("GITTEST_INVASIVE_SPEED"))
+ cl_skip();
+
+ /*
+ * The config file is structured in such a way that is
+ * has a section header that is approximately 500kb of
+ * size followed by 40k entries. While the resulting
+ * configuration file itself is roughly 650kb in size and
+ * thus considered to be rather small, in the past we'd
+ * balloon to more than 20GB of memory (20000x500kb)
+ * while parsing the file. It thus was a trivial way to
+ * cause an out-of-memory situation and thus cause denial
+ * of service, e.g. via gitmodules.
+ */
+ cl_git_pass(git_config_open_ondisk(&config, cl_fixture("config/config-oom")));
+
+ git_config_free(config);
+}
diff --git a/tests/libgit2/config/validkeyname.c b/tests/libgit2/config/validkeyname.c
new file mode 100644
index 0000000..4b36509
--- /dev/null
+++ b/tests/libgit2/config/validkeyname.c
@@ -0,0 +1,49 @@
+#include "clar_libgit2.h"
+
+#include "config.h"
+
+static git_config *cfg;
+
+void test_config_validkeyname__initialize(void)
+{
+ cl_fixture_sandbox("config/config10");
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config10"));
+}
+
+void test_config_validkeyname__cleanup(void)
+{
+ git_config_free(cfg);
+ cfg = NULL;
+
+ cl_fixture_cleanup("config10");
+}
+
+static void assert_invalid_config_key_name(const char *name)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_fail_with(git_config_get_string_buf(&buf, cfg, name),
+ GIT_EINVALIDSPEC);
+ cl_git_fail_with(git_config_set_string(cfg, name, "42"),
+ GIT_EINVALIDSPEC);
+ cl_git_fail_with(git_config_delete_entry(cfg, name),
+ GIT_EINVALIDSPEC);
+ cl_git_fail_with(git_config_get_multivar_foreach(cfg, name, "*", NULL, NULL),
+ GIT_EINVALIDSPEC);
+ cl_git_fail_with(git_config_set_multivar(cfg, name, "*", "42"),
+ GIT_EINVALIDSPEC);
+}
+
+void test_config_validkeyname__accessing_requires_a_valid_name(void)
+{
+ assert_invalid_config_key_name("");
+ assert_invalid_config_key_name(".");
+ assert_invalid_config_key_name("..");
+ assert_invalid_config_key_name("core.");
+ assert_invalid_config_key_name("d#ff.dirstat.lines");
+ assert_invalid_config_key_name("diff.dirstat.lines#");
+ assert_invalid_config_key_name("dif\nf.dirstat.lines");
+ assert_invalid_config_key_name("dif.dir\nstat.lines");
+ assert_invalid_config_key_name("dif.dirstat.li\nes");
+}
diff --git a/tests/libgit2/config/write.c b/tests/libgit2/config/write.c
new file mode 100644
index 0000000..9d8c3fe
--- /dev/null
+++ b/tests/libgit2/config/write.c
@@ -0,0 +1,764 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+#include "git2/sys/config.h"
+#include "config.h"
+
+void test_config_write__initialize(void)
+{
+ cl_fixture_sandbox("config/config9");
+ cl_fixture_sandbox("config/config15");
+ cl_fixture_sandbox("config/config17");
+ cl_fixture_sandbox("config/config22");
+}
+
+void test_config_write__cleanup(void)
+{
+ cl_fixture_cleanup("config9");
+ cl_fixture_cleanup("config15");
+ cl_fixture_cleanup("config17");
+ cl_fixture_cleanup("config22");
+}
+
+void test_config_write__replace_value(void)
+{
+ git_config *cfg;
+ int i;
+ int64_t l, expected = +9223372036854775803;
+
+ /* By freeing the config, we make sure we flush the values */
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_git_pass(git_config_set_int32(cfg, "core.dummy", 5));
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_git_pass(git_config_get_int32(&i, cfg, "core.dummy"));
+ cl_assert(i == 5);
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_git_pass(git_config_set_int32(cfg, "core.dummy", 1));
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_git_pass(git_config_set_int64(cfg, "core.verylong", expected));
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_git_pass(git_config_get_int64(&l, cfg, "core.verylong"));
+ cl_assert(l == expected);
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_must_fail(git_config_get_int32(&i, cfg, "core.verylong"));
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_git_pass(git_config_set_int64(cfg, "core.verylong", 1));
+ git_config_free(cfg);
+}
+
+void test_config_write__delete_value(void)
+{
+ git_config *cfg;
+ int32_t i;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_git_pass(git_config_set_int32(cfg, "core.dummy", 5));
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_git_pass(git_config_delete_entry(cfg, "core.dummy"));
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_assert(git_config_get_int32(&i, cfg, "core.dummy") == GIT_ENOTFOUND);
+ cl_git_pass(git_config_set_int32(cfg, "core.dummy", 1));
+ git_config_free(cfg);
+}
+
+/*
+ * At the beginning of the test:
+ * - config9 has: core.dummy2=42
+ * - config15 has: core.dummy2=7
+ */
+void test_config_write__delete_value_at_specific_level(void)
+{
+ git_config *cfg, *cfg_specific;
+ int32_t i;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config15"));
+ cl_git_pass(git_config_get_int32(&i, cfg, "core.dummy2"));
+ cl_assert(i == 7);
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_new(&cfg));
+ cl_git_pass(git_config_add_file_ondisk(cfg, "config9",
+ GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
+ cl_git_pass(git_config_add_file_ondisk(cfg, "config15",
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
+
+ cl_git_pass(git_config_open_level(&cfg_specific, cfg, GIT_CONFIG_LEVEL_GLOBAL));
+
+ cl_git_pass(git_config_delete_entry(cfg_specific, "core.dummy2"));
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config15"));
+ cl_assert(git_config_get_int32(&i, cfg, "core.dummy2") == GIT_ENOTFOUND);
+ cl_git_pass(git_config_set_int32(cfg, "core.dummy2", 7));
+
+ git_config_free(cfg_specific);
+ git_config_free(cfg);
+}
+
+/*
+ * This test exposes a bug where duplicate empty section headers could prevent
+ * deletion of config entries.
+ */
+void test_config_write__delete_value_with_duplicate_header(void)
+{
+ const char *file_name = "config-duplicate-header";
+ const char *entry_name = "remote.origin.url";
+ git_config *cfg;
+ git_config_entry *entry;
+
+ /* This config can occur after removing and re-adding the origin remote */
+ const char *file_content =
+ "[remote \"origin\"]\n" \
+ "[branch \"master\"]\n" \
+ " remote = \"origin\"\n" \
+ "[remote \"origin\"]\n" \
+ " url = \"foo\"\n";
+
+ /* Write the test config and make sure the expected entry exists */
+ cl_git_mkfile(file_name, file_content);
+ cl_git_pass(git_config_open_ondisk(&cfg, file_name));
+ cl_git_pass(git_config_get_entry(&entry, cfg, entry_name));
+
+ /* Delete that entry */
+ cl_git_pass(git_config_delete_entry(cfg, entry_name));
+
+ /* Reopen the file and make sure the entry no longer exists */
+ git_config_entry_free(entry);
+ git_config_free(cfg);
+ cl_git_pass(git_config_open_ondisk(&cfg, file_name));
+ cl_git_fail(git_config_get_entry(&entry, cfg, entry_name));
+
+ /* Cleanup */
+ git_config_entry_free(entry);
+ git_config_free(cfg);
+}
+
+/*
+ * This test exposes a bug where duplicate section headers could cause
+ * config_write to add a new entry when one already exists.
+ */
+void test_config_write__add_value_with_duplicate_header(void)
+{
+ const char *file_name = "config-duplicate-insert";
+ const char *entry_name = "foo.c";
+ const char *old_val = "old";
+ const char *new_val = "new";
+ const char *str;
+ git_config *cfg, *snapshot;
+
+ /* c = old should be replaced by c = new.
+ * The bug causes c = new to be inserted under the first 'foo' header.
+ */
+ const char *file_content =
+ "[foo]\n" \
+ " a = b\n" \
+ "[other]\n" \
+ " a = b\n" \
+ "[foo]\n" \
+ " c = old\n";
+
+ /* Write the test config */
+ cl_git_mkfile(file_name, file_content);
+ cl_git_pass(git_config_open_ondisk(&cfg, file_name));
+
+ /* make sure the expected entry (foo.c) exists */
+ cl_git_pass(git_config_snapshot(&snapshot, cfg));
+ cl_git_pass(git_config_get_string(&str, snapshot, entry_name));
+ cl_assert_equal_s(old_val, str);
+ git_config_free(snapshot);
+
+ /* Try setting foo.c to something else */
+ cl_git_pass(git_config_set_string(cfg, entry_name, new_val));
+ git_config_free(cfg);
+
+ /* Reopen the file and make sure the new value was set */
+ cl_git_pass(git_config_open_ondisk(&cfg, file_name));
+ cl_git_pass(git_config_snapshot(&snapshot, cfg));
+ cl_git_pass(git_config_get_string(&str, snapshot, entry_name));
+ cl_assert_equal_s(new_val, str);
+
+ /* Cleanup */
+ git_config_free(snapshot);
+ git_config_free(cfg);
+}
+
+void test_config_write__overwrite_value_with_duplicate_header(void)
+{
+ const char *file_name = "config-duplicate-header";
+ const char *entry_name = "remote.origin.url";
+ git_config *cfg;
+ git_config_entry *entry;
+
+ /* This config can occur after removing and re-adding the origin remote */
+ const char *file_content =
+ "[remote \"origin\"]\n" \
+ "[branch \"master\"]\n" \
+ " remote = \"origin\"\n" \
+ "[remote \"origin\"]\n" \
+ " url = \"foo\"\n";
+
+ /* Write the test config and make sure the expected entry exists */
+ cl_git_mkfile(file_name, file_content);
+ cl_git_pass(git_config_open_ondisk(&cfg, file_name));
+ cl_git_pass(git_config_get_entry(&entry, cfg, entry_name));
+
+ /* Update that entry */
+ cl_git_pass(git_config_set_string(cfg, entry_name, "newurl"));
+
+ /* Reopen the file and make sure the entry was updated */
+ git_config_entry_free(entry);
+ git_config_free(cfg);
+ cl_git_pass(git_config_open_ondisk(&cfg, file_name));
+ cl_git_pass(git_config_get_entry(&entry, cfg, entry_name));
+
+ cl_assert_equal_s("newurl", entry->value);
+
+ /* Cleanup */
+ git_config_entry_free(entry);
+ git_config_free(cfg);
+}
+
+static int multivar_cb(const git_config_entry *entry, void *data)
+{
+ int *n = (int *)data;
+
+ cl_assert_equal_s(entry->value, "newurl");
+
+ (*n)++;
+
+ return 0;
+}
+
+void test_config_write__overwrite_multivar_within_duplicate_header(void)
+{
+ const char *file_name = "config-duplicate-header";
+ const char *entry_name = "remote.origin.url";
+ git_config *cfg;
+ git_config_entry *entry;
+ int n = 0;
+
+ /* This config can occur after removing and re-adding the origin remote */
+ const char *file_content =
+ "[remote \"origin\"]\n" \
+ " url = \"bar\"\n" \
+ "[branch \"master\"]\n" \
+ " remote = \"origin\"\n" \
+ "[remote \"origin\"]\n" \
+ " url = \"foo\"\n";
+
+ /* Write the test config and make sure the expected entry exists */
+ cl_git_mkfile(file_name, file_content);
+ cl_git_pass(git_config_open_ondisk(&cfg, file_name));
+ cl_git_pass(git_config_get_entry(&entry, cfg, entry_name));
+
+ /* Update that entry */
+ cl_git_pass(git_config_set_multivar(cfg, entry_name, ".*", "newurl"));
+ git_config_entry_free(entry);
+ git_config_free(cfg);
+
+ /* Reopen the file and make sure the entry was updated */
+ cl_git_pass(git_config_open_ondisk(&cfg, file_name));
+ cl_git_pass(git_config_get_multivar_foreach(cfg, entry_name, NULL, multivar_cb, &n));
+ cl_assert_equal_i(2, n);
+
+ /* Cleanup */
+ git_config_free(cfg);
+}
+
+void test_config_write__write_subsection(void)
+{
+ git_config *cfg;
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_git_pass(git_config_set_string(cfg, "my.own.var", "works"));
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "my.own.var"));
+ cl_assert_equal_s("works", buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_config_free(cfg);
+}
+
+void test_config_write__delete_inexistent(void)
+{
+ git_config *cfg;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_assert(git_config_delete_entry(cfg, "core.imaginary") == GIT_ENOTFOUND);
+ git_config_free(cfg);
+}
+
+void test_config_write__value_containing_quotes(void)
+{
+ git_config *cfg;
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_git_pass(git_config_set_string(cfg, "core.somevar", "this \"has\" quotes"));
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "core.somevar"));
+ cl_assert_equal_s("this \"has\" quotes", buf.ptr);
+ git_buf_dispose(&buf);
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "core.somevar"));
+ cl_assert_equal_s("this \"has\" quotes", buf.ptr);
+ git_buf_dispose(&buf);
+ git_config_free(cfg);
+
+ /* The code path for values that already exist is different, check that one as well */
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_git_pass(git_config_set_string(cfg, "core.somevar", "this also \"has\" quotes"));
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "core.somevar"));
+ cl_assert_equal_s("this also \"has\" quotes", buf.ptr);
+ git_buf_dispose(&buf);
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "core.somevar"));
+ cl_assert_equal_s("this also \"has\" quotes", buf.ptr);
+ git_buf_dispose(&buf);
+ git_config_free(cfg);
+}
+
+void test_config_write__escape_value(void)
+{
+ git_config *cfg;
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_git_pass(git_config_set_string(cfg, "core.somevar", "this \"has\" quotes and \t"));
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "core.somevar"));
+ cl_assert_equal_s("this \"has\" quotes and \t", buf.ptr);
+ git_buf_dispose(&buf);
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "core.somevar"));
+ cl_assert_equal_s("this \"has\" quotes and \t", buf.ptr);
+ git_buf_dispose(&buf);
+ git_config_free(cfg);
+}
+
+void test_config_write__add_value_at_specific_level(void)
+{
+ git_config *cfg, *cfg_specific;
+ int i;
+ int64_t l, expected = +9223372036854775803;
+ git_buf buf = GIT_BUF_INIT;
+
+ /* open config15 as global level config file */
+ cl_git_pass(git_config_new(&cfg));
+ cl_git_pass(git_config_add_file_ondisk(cfg, "config9",
+ GIT_CONFIG_LEVEL_LOCAL, NULL, 0));
+ cl_git_pass(git_config_add_file_ondisk(cfg, "config15",
+ GIT_CONFIG_LEVEL_GLOBAL, NULL, 0));
+
+ cl_git_pass(git_config_open_level(&cfg_specific, cfg, GIT_CONFIG_LEVEL_GLOBAL));
+
+ cl_git_pass(git_config_set_int32(cfg_specific, "core.int32global", 28));
+ cl_git_pass(git_config_set_int64(cfg_specific, "core.int64global", expected));
+ cl_git_pass(git_config_set_bool(cfg_specific, "core.boolglobal", true));
+ cl_git_pass(git_config_set_string(cfg_specific, "core.stringglobal", "I'm a global config value!"));
+ git_config_free(cfg_specific);
+ git_config_free(cfg);
+
+ /* open config15 as local level config file */
+ cl_git_pass(git_config_open_ondisk(&cfg, "config15"));
+
+ cl_git_pass(git_config_get_int32(&i, cfg, "core.int32global"));
+ cl_assert_equal_i(28, i);
+ cl_git_pass(git_config_get_int64(&l, cfg, "core.int64global"));
+ cl_assert(l == expected);
+ cl_git_pass(git_config_get_bool(&i, cfg, "core.boolglobal"));
+ cl_assert_equal_b(true, i);
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "core.stringglobal"));
+ cl_assert_equal_s("I'm a global config value!", buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_config_free(cfg);
+}
+
+void test_config_write__add_value_at_file_with_no_clrf_at_the_end(void)
+{
+ git_config *cfg;
+ int i;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config17"));
+ cl_git_pass(git_config_set_int32(cfg, "core.newline", 7));
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config17"));
+ cl_git_pass(git_config_get_int32(&i, cfg, "core.newline"));
+ cl_assert_equal_i(7, i);
+
+ git_config_free(cfg);
+}
+
+void test_config_write__add_section_at_file_with_no_clrf_at_the_end(void)
+{
+ git_config *cfg;
+ int i;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config17"));
+ cl_git_pass(git_config_set_int32(cfg, "diff.context", 10));
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config17"));
+ cl_git_pass(git_config_get_int32(&i, cfg, "diff.context"));
+ cl_assert_equal_i(10, i);
+
+ git_config_free(cfg);
+}
+
+void test_config_write__add_value_which_needs_quotes(void)
+{
+ git_config *cfg, *base;
+ const char* str1;
+ const char* str2;
+ const char* str3;
+ const char* str4;
+ const char* str5;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config17"));
+ cl_git_pass(git_config_set_string(cfg, "core.startwithspace", " Something"));
+ cl_git_pass(git_config_set_string(cfg, "core.endwithspace", "Something "));
+ cl_git_pass(git_config_set_string(cfg, "core.containscommentchar1", "some#thing"));
+ cl_git_pass(git_config_set_string(cfg, "core.containscommentchar2", "some;thing"));
+ cl_git_pass(git_config_set_string(cfg, "core.startwhithsapceandcontainsdoublequote", " some\"thing"));
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&base, "config17"));
+ cl_git_pass(git_config_snapshot(&cfg, base));
+ cl_git_pass(git_config_get_string(&str1, cfg, "core.startwithspace"));
+ cl_assert_equal_s(" Something", str1);
+ cl_git_pass(git_config_get_string(&str2, cfg, "core.endwithspace"));
+ cl_assert_equal_s("Something ", str2);
+ cl_git_pass(git_config_get_string(&str3, cfg, "core.containscommentchar1"));
+ cl_assert_equal_s("some#thing", str3);
+ cl_git_pass(git_config_get_string(&str4, cfg, "core.containscommentchar2"));
+ cl_assert_equal_s("some;thing", str4);
+ cl_git_pass(git_config_get_string(&str5, cfg, "core.startwhithsapceandcontainsdoublequote"));
+ cl_assert_equal_s(" some\"thing", str5);
+ git_config_free(cfg);
+ git_config_free(base);
+}
+
+void test_config_write__can_set_a_value_to_NULL(void)
+{
+ git_repository *repository;
+ git_config *config;
+
+ repository = cl_git_sandbox_init("testrepo.git");
+
+ cl_git_pass(git_repository_config(&config, repository));
+ cl_git_fail(git_config_set_string(config, "a.b.c", NULL));
+ git_config_free(config);
+
+ cl_git_sandbox_cleanup();
+}
+
+void test_config_write__can_set_an_empty_value(void)
+{
+ git_repository *repository;
+ git_config *config;
+ git_buf buf = {0};
+
+ repository = cl_git_sandbox_init("testrepo.git");
+ cl_git_pass(git_repository_config(&config, repository));
+
+ cl_git_pass(git_config_set_string(config, "core.somevar", ""));
+ cl_git_pass(git_config_get_string_buf(&buf, config, "core.somevar"));
+ cl_assert_equal_s("", buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_config_free(config);
+ cl_git_sandbox_cleanup();
+}
+
+void test_config_write__updating_a_locked_config_file_returns_ELOCKED(void)
+{
+ git_config *cfg;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config9"));
+
+ cl_git_mkfile("config9.lock", "[core]\n");
+
+ cl_git_fail_with(git_config_set_string(cfg, "core.dump", "boom"), GIT_ELOCKED);
+
+ git_config_free(cfg);
+}
+
+void test_config_write__outside_change(void)
+{
+ int32_t tmp;
+ git_config *cfg;
+ const char *filename = "config-ext-change";
+
+ cl_git_mkfile(filename, "[old]\nvalue = 5\n");
+
+ cl_git_pass(git_config_open_ondisk(&cfg, filename));
+
+ cl_git_pass(git_config_get_int32(&tmp, cfg, "old.value"));
+
+ /* Change the value on the file itself (simulate external process) */
+ cl_git_mkfile(filename, "[old]\nvalue = 6\n");
+
+ cl_git_pass(git_config_set_int32(cfg, "new.value", 7));
+
+ cl_git_pass(git_config_get_int32(&tmp, cfg, "old.value"));
+ cl_assert_equal_i(6, tmp);
+
+ git_config_free(cfg);
+}
+
+#define FOO_COMMENT \
+ "; another comment!\n"
+
+#define SECTION_FOO \
+ "\n" \
+ " \n" \
+ " [section \"foo\"] \n" \
+ " # here's a comment\n" \
+ "\tname = \"value\"\n" \
+ " name2 = \"value2\"\n" \
+
+#define SECTION_FOO_WITH_COMMENT SECTION_FOO FOO_COMMENT
+
+#define SECTION_BAR \
+ "[section \"bar\"]\t\n" \
+ "\t \n" \
+ " barname=\"value\"\n"
+
+
+void test_config_write__preserves_whitespace_and_comments(void)
+{
+ const char *file_name = "config-duplicate-header";
+ const char *n;
+ git_config *cfg;
+ git_str newfile = GIT_STR_INIT;
+
+ /* This config can occur after removing and re-adding the origin remote */
+ const char *file_content = SECTION_FOO_WITH_COMMENT SECTION_BAR;
+
+ /* Write the test config and make sure the expected entry exists */
+ cl_git_mkfile(file_name, file_content);
+ cl_git_pass(git_config_open_ondisk(&cfg, file_name));
+ cl_git_pass(git_config_set_string(cfg, "section.foo.other", "otherval"));
+ cl_git_pass(git_config_set_string(cfg, "newsection.newname", "new_value"));
+
+ /* Ensure that we didn't needlessly mangle the config file */
+ cl_git_pass(git_futils_readbuffer(&newfile, file_name));
+ n = newfile.ptr;
+
+ cl_assert_equal_strn(SECTION_FOO, n, strlen(SECTION_FOO));
+ n += strlen(SECTION_FOO);
+ cl_assert_equal_strn("\tother = otherval\n", n, strlen("\tother = otherval\n"));
+ n += strlen("\tother = otherval\n");
+ cl_assert_equal_strn(FOO_COMMENT, n, strlen(FOO_COMMENT));
+ n += strlen(FOO_COMMENT);
+
+ cl_assert_equal_strn(SECTION_BAR, n, strlen(SECTION_BAR));
+ n += strlen(SECTION_BAR);
+
+ cl_assert_equal_s("[newsection]\n\tnewname = new_value\n", n);
+
+ git_str_dispose(&newfile);
+ git_config_free(cfg);
+}
+
+void test_config_write__preserves_entry_with_name_only(void)
+{
+ const char *file_name = "config-empty-value";
+ git_config *cfg;
+ git_str newfile = GIT_STR_INIT;
+
+ /* Write the test config and make sure the expected entry exists */
+ cl_git_mkfile(file_name, "[section \"foo\"]\n\tname\n");
+ cl_git_pass(git_config_open_ondisk(&cfg, file_name));
+ cl_git_pass(git_config_set_string(cfg, "newsection.newname", "new_value"));
+ cl_git_pass(git_config_set_string(cfg, "section.foo.other", "otherval"));
+
+ cl_git_pass(git_futils_readbuffer(&newfile, file_name));
+ cl_assert_equal_s("[section \"foo\"]\n\tname\n\tother = otherval\n[newsection]\n\tnewname = new_value\n", newfile.ptr);
+
+ git_str_dispose(&newfile);
+ git_config_free(cfg);
+}
+
+void test_config_write__to_empty_file(void)
+{
+ git_config *cfg;
+ const char *filename = "config-file";
+ git_str result = GIT_STR_INIT;
+
+ cl_git_mkfile(filename, "");
+ cl_git_pass(git_config_open_ondisk(&cfg, filename));
+ cl_git_pass(git_config_set_string(cfg, "section.name", "value"));
+ git_config_free(cfg);
+
+ cl_git_pass(git_futils_readbuffer(&result, "config-file"));
+ cl_assert_equal_s("[section]\n\tname = value\n", result.ptr);
+
+ git_str_dispose(&result);
+}
+
+void test_config_write__to_file_with_only_comment(void)
+{
+ git_config *cfg;
+ const char *filename = "config-file";
+ git_str result = GIT_STR_INIT;
+
+ cl_git_mkfile(filename, "\n\n");
+ cl_git_pass(git_config_open_ondisk(&cfg, filename));
+ cl_git_pass(git_config_set_string(cfg, "section.name", "value"));
+ git_config_free(cfg);
+
+ cl_git_pass(git_futils_readbuffer(&result, "config-file"));
+ cl_assert_equal_s("\n\n[section]\n\tname = value\n", result.ptr);
+
+ git_str_dispose(&result);
+}
+
+void test_config_write__locking(void)
+{
+ git_config *cfg, *cfg2;
+ git_config_entry *entry;
+ git_transaction *tx;
+ const char *filename = "locked-file";
+
+ /* Open the config and lock it */
+ cl_git_mkfile(filename, "[section]\n\tname = value\n");
+ cl_git_pass(git_config_open_ondisk(&cfg, filename));
+ cl_git_pass(git_config_get_entry(&entry, cfg, "section.name"));
+ cl_assert_equal_s("value", entry->value);
+ git_config_entry_free(entry);
+ cl_git_pass(git_config_lock(&tx, cfg));
+
+ /* Change entries in the locked backend */
+ cl_git_pass(git_config_set_string(cfg, "section.name", "other value"));
+ cl_git_pass(git_config_set_string(cfg, "section2.name3", "more value"));
+
+ /* We can see that the file we read from hasn't changed */
+ cl_git_pass(git_config_open_ondisk(&cfg2, filename));
+ cl_git_pass(git_config_get_entry(&entry, cfg2, "section.name"));
+ cl_assert_equal_s("value", entry->value);
+ git_config_entry_free(entry);
+ cl_git_fail_with(GIT_ENOTFOUND, git_config_get_entry(&entry, cfg2, "section2.name3"));
+ git_config_free(cfg2);
+
+ /* And we also get the old view when we read from the locked config */
+ cl_git_pass(git_config_get_entry(&entry, cfg, "section.name"));
+ cl_assert_equal_s("value", entry->value);
+ git_config_entry_free(entry);
+ cl_git_fail_with(GIT_ENOTFOUND, git_config_get_entry(&entry, cfg, "section2.name3"));
+
+ cl_git_pass(git_transaction_commit(tx));
+ git_transaction_free(tx);
+
+ /* Now that we've unlocked it, we should see both updates */
+ cl_git_pass(git_config_get_entry(&entry, cfg, "section.name"));
+ cl_assert_equal_s("other value", entry->value);
+ git_config_entry_free(entry);
+ cl_git_pass(git_config_get_entry(&entry, cfg, "section2.name3"));
+ cl_assert_equal_s("more value", entry->value);
+ git_config_entry_free(entry);
+
+ git_config_free(cfg);
+
+ /* We should also see the changes after reopening the config */
+ cl_git_pass(git_config_open_ondisk(&cfg, filename));
+ cl_git_pass(git_config_get_entry(&entry, cfg, "section.name"));
+ cl_assert_equal_s("other value", entry->value);
+ git_config_entry_free(entry);
+ cl_git_pass(git_config_get_entry(&entry, cfg, "section2.name3"));
+ cl_assert_equal_s("more value", entry->value);
+ git_config_entry_free(entry);
+
+ git_config_free(cfg);
+}
+
+void test_config_write__repeated(void)
+{
+ const char *filename = "config-repeated";
+ git_config *cfg;
+ git_str result = GIT_STR_INIT;
+ const char *expected = "[sample \"prefix\"]\n\
+\tsetting1 = someValue1\n\
+\tsetting2 = someValue2\n\
+\tsetting3 = someValue3\n\
+\tsetting4 = someValue4\n\
+";
+ cl_git_pass(git_config_open_ondisk(&cfg, filename));
+ cl_git_pass(git_config_set_string(cfg, "sample.prefix.setting1", "someValue1"));
+ cl_git_pass(git_config_set_string(cfg, "sample.prefix.setting2", "someValue2"));
+ cl_git_pass(git_config_set_string(cfg, "sample.prefix.setting3", "someValue3"));
+ cl_git_pass(git_config_set_string(cfg, "sample.prefix.setting4", "someValue4"));
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, filename));
+
+ cl_git_pass(git_futils_readbuffer(&result, filename));
+ cl_assert_equal_s(expected, result.ptr);
+ git_str_dispose(&result);
+
+ git_config_free(cfg);
+}
+
+void test_config_write__preserve_case(void)
+{
+ const char *filename = "config-preserve-case";
+ git_config *cfg;
+ git_str result = GIT_STR_INIT;
+ const char *expected = "[sOMe]\n" \
+ "\tThInG = foo\n" \
+ "\tOtheR = thing\n";
+
+ cl_git_pass(git_config_open_ondisk(&cfg, filename));
+ cl_git_pass(git_config_set_string(cfg, "sOMe.ThInG", "foo"));
+ cl_git_pass(git_config_set_string(cfg, "SomE.OtheR", "thing"));
+ git_config_free(cfg);
+
+ cl_git_pass(git_config_open_ondisk(&cfg, filename));
+
+ cl_git_pass(git_futils_readbuffer(&result, filename));
+ cl_assert_equal_s(expected, result.ptr);
+ git_str_dispose(&result);
+
+ git_config_free(cfg);
+}
+
+void test_config_write__write_config_file_with_multi_line_value(void)
+{
+ git_config* cfg;
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "config22"));
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "alias.m"));
+ cl_assert_equal_s("cmd ;; ;; bar", buf.ptr);
+ cl_git_pass(git_config_set_string(cfg, "sOMe.ThInG", "foo"));
+ git_buf_dispose(&buf);
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "alias.m"));
+ cl_assert_equal_s("cmd ;; ;; bar", buf.ptr);
+ git_buf_dispose(&buf);
+
+ git_config_free(cfg);
+}
diff --git a/tests/libgit2/core/buf.c b/tests/libgit2/core/buf.c
new file mode 100644
index 0000000..3959fa8
--- /dev/null
+++ b/tests/libgit2/core/buf.c
@@ -0,0 +1,54 @@
+#include "clar_libgit2.h"
+#include "buf.h"
+
+void test_core_buf__sanitize(void)
+{
+ git_buf buf = { (char *)0x42, 0, 16 };
+
+ cl_git_pass(git_buf_sanitize(&buf));
+ cl_assert_equal_s(buf.ptr, "");
+ cl_assert_equal_i(buf.reserved, 0);
+ cl_assert_equal_i(buf.size, 0);
+
+ git_buf_dispose(&buf);
+}
+
+void test_core_buf__tostr(void)
+{
+ git_str str = GIT_STR_INIT;
+ git_buf buf = { (char *)0x42, 0, 16 };
+
+ cl_git_pass(git_buf_tostr(&str, &buf));
+
+ cl_assert_equal_s(buf.ptr, "");
+ cl_assert_equal_i(buf.reserved, 0);
+ cl_assert_equal_i(buf.size, 0);
+
+ cl_assert_equal_s(str.ptr, "");
+ cl_assert_equal_i(str.asize, 0);
+ cl_assert_equal_i(str.size, 0);
+
+ git_buf_dispose(&buf);
+ git_str_dispose(&str);
+}
+
+void test_core_buf__fromstr(void)
+{
+ git_str str = GIT_STR_INIT;
+ git_buf buf = { (char *)0x42, 0, 16 };
+
+ cl_git_pass(git_buf_tostr(&str, &buf));
+ cl_git_pass(git_str_puts(&str, "Hello, world."));
+ cl_git_pass(git_buf_fromstr(&buf, &str));
+
+ cl_assert(buf.reserved > 14);
+ cl_assert_equal_i(buf.size, 13);
+ cl_assert_equal_s(buf.ptr, "Hello, world.");
+
+ cl_assert_equal_s(str.ptr, "");
+ cl_assert_equal_i(str.asize, 0);
+ cl_assert_equal_i(str.size, 0);
+
+ git_buf_dispose(&buf);
+ git_str_dispose(&str);
+}
diff --git a/tests/libgit2/core/env.c b/tests/libgit2/core/env.c
new file mode 100644
index 0000000..8ba9b91
--- /dev/null
+++ b/tests/libgit2/core/env.c
@@ -0,0 +1,320 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+#include "sysdir.h"
+
+#ifdef GIT_WIN32
+#define NUM_VARS 5
+static const char *env_vars[NUM_VARS] = {
+ "HOME", "HOMEDRIVE", "HOMEPATH", "USERPROFILE", "PROGRAMFILES"
+};
+#else
+#define NUM_VARS 1
+static const char *env_vars[NUM_VARS] = { "HOME" };
+#endif
+
+static char *env_save[NUM_VARS];
+
+static char *home_values[] = {
+ "fake_home",
+ "f\xc3\xa1ke_h\xc3\xb5me", /* all in latin-1 supplement */
+ "f\xc4\x80ke_\xc4\xa4ome", /* latin extended */
+ "f\xce\xb1\xce\xba\xce\xb5_h\xce\xbfm\xce\xad", /* having fun with greek */
+ "fa\xe0" "\xb8" "\x87" "e_\xe0" "\xb8" "\x99" "ome", /* thai characters */
+ "f\xe1\x9c\x80ke_\xe1\x9c\x91ome", /* tagalog characters */
+ "\xe1\xb8\x9f\xe1\xba\xa2" "ke_ho" "\xe1" "\xb9" "\x81" "e", /* latin extended additional */
+ "\xf0\x9f\x98\x98\xf0\x9f\x98\x82", /* emoticons */
+ NULL
+};
+
+void test_core_env__initialize(void)
+{
+ int i;
+ for (i = 0; i < NUM_VARS; ++i)
+ env_save[i] = cl_getenv(env_vars[i]);
+}
+
+static void set_global_search_path_from_env(void)
+{
+ cl_git_pass(git_sysdir_set(GIT_SYSDIR_GLOBAL, NULL));
+}
+
+static void set_system_search_path_from_env(void)
+{
+ cl_git_pass(git_sysdir_set(GIT_SYSDIR_SYSTEM, NULL));
+}
+
+void test_core_env__cleanup(void)
+{
+ int i;
+ char **val;
+
+ for (i = 0; i < NUM_VARS; ++i) {
+ cl_setenv(env_vars[i], env_save[i]);
+ git__free(env_save[i]);
+ env_save[i] = NULL;
+ }
+
+ /* these will probably have already been cleaned up, but if a test
+ * fails, then it's probably good to try and clear out these dirs
+ */
+ for (val = home_values; *val != NULL; val++) {
+ if (**val != '\0')
+ (void)p_rmdir(*val);
+ }
+
+ cl_sandbox_set_search_path_defaults();
+}
+
+static void setenv_and_check(const char *name, const char *value)
+{
+ char *check;
+
+ cl_git_pass(cl_setenv(name, value));
+ check = cl_getenv(name);
+
+ if (value)
+ cl_assert_equal_s(value, check);
+ else
+ cl_assert(check == NULL);
+
+ git__free(check);
+}
+
+void test_core_env__0(void)
+{
+ git_str path = GIT_STR_INIT, found = GIT_STR_INIT;
+ char testfile[16], tidx = '0';
+ char **val;
+ const char *testname = "testfile";
+ size_t testlen = strlen(testname);
+
+ strncpy(testfile, testname, sizeof(testfile));
+ cl_assert_equal_s(testname, testfile);
+
+ for (val = home_values; *val != NULL; val++) {
+
+ /* if we can't make the directory, let's just assume
+ * we are on a filesystem that doesn't support the
+ * characters in question and skip this test...
+ */
+ if (p_mkdir(*val, 0777) != 0) {
+ *val = ""; /* mark as not created */
+ continue;
+ }
+
+ cl_git_pass(git_fs_path_prettify(&path, *val, NULL));
+
+ /* vary testfile name in each directory so accidentally leaving
+ * an environment variable set from a previous iteration won't
+ * accidentally make this test pass...
+ */
+ testfile[testlen] = tidx++;
+ cl_git_pass(git_str_joinpath(&path, path.ptr, testfile));
+ cl_git_mkfile(path.ptr, "find me");
+ git_str_rtruncate_at_char(&path, '/');
+
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_sysdir_find_global_file(&found, testfile));
+
+ setenv_and_check("HOME", path.ptr);
+ set_global_search_path_from_env();
+
+ cl_git_pass(git_sysdir_find_global_file(&found, testfile));
+
+ cl_setenv("HOME", env_save[0]);
+ set_global_search_path_from_env();
+
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_sysdir_find_global_file(&found, testfile));
+
+#ifdef GIT_WIN32
+ setenv_and_check("HOMEDRIVE", NULL);
+ setenv_and_check("HOMEPATH", NULL);
+ setenv_and_check("USERPROFILE", path.ptr);
+ set_global_search_path_from_env();
+
+ cl_git_pass(git_sysdir_find_global_file(&found, testfile));
+
+ {
+ int root = git_fs_path_root(path.ptr);
+ char old;
+
+ if (root >= 0) {
+ setenv_and_check("USERPROFILE", NULL);
+ set_global_search_path_from_env();
+
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_sysdir_find_global_file(&found, testfile));
+
+ old = path.ptr[root];
+ path.ptr[root] = '\0';
+ setenv_and_check("HOMEDRIVE", path.ptr);
+ path.ptr[root] = old;
+ setenv_and_check("HOMEPATH", &path.ptr[root]);
+ set_global_search_path_from_env();
+
+ cl_git_pass(git_sysdir_find_global_file(&found, testfile));
+ }
+ }
+#endif
+
+ (void)p_rmdir(*val);
+ }
+
+ git_str_dispose(&path);
+ git_str_dispose(&found);
+}
+
+
+void test_core_env__1(void)
+{
+ git_str path = GIT_STR_INIT;
+
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_sysdir_find_global_file(&path, "nonexistentfile"));
+
+ cl_git_pass(cl_setenv("HOME", "doesnotexist"));
+#ifdef GIT_WIN32
+ cl_git_pass(cl_setenv("HOMEPATH", "doesnotexist"));
+ cl_git_pass(cl_setenv("USERPROFILE", "doesnotexist"));
+#endif
+ set_global_search_path_from_env();
+
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_sysdir_find_global_file(&path, "nonexistentfile"));
+
+ cl_git_pass(cl_setenv("HOME", NULL));
+#ifdef GIT_WIN32
+ cl_git_pass(cl_setenv("HOMEPATH", NULL));
+ cl_git_pass(cl_setenv("USERPROFILE", NULL));
+#endif
+ set_global_search_path_from_env();
+ set_system_search_path_from_env();
+
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_sysdir_find_global_file(&path, "nonexistentfile"));
+
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_sysdir_find_system_file(&path, "nonexistentfile"));
+
+#ifdef GIT_WIN32
+ cl_git_pass(cl_setenv("PROGRAMFILES", NULL));
+ set_system_search_path_from_env();
+
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_sysdir_find_system_file(&path, "nonexistentfile"));
+#endif
+
+ git_str_dispose(&path);
+}
+
+static void check_global_searchpath(
+ const char *path, int position, const char *file, git_str *temp)
+{
+ git_str out = GIT_STR_INIT;
+
+ /* build and set new path */
+ if (position < 0)
+ cl_git_pass(git_str_join(temp, GIT_PATH_LIST_SEPARATOR, path, "$PATH"));
+ else if (position > 0)
+ cl_git_pass(git_str_join(temp, GIT_PATH_LIST_SEPARATOR, "$PATH", path));
+ else
+ cl_git_pass(git_str_sets(temp, path));
+
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, temp->ptr));
+
+ /* get path and make sure $PATH expansion worked */
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, &out));
+
+ if (position < 0)
+ cl_assert(git__prefixcmp(out.ptr, path) == 0);
+ else if (position > 0)
+ cl_assert(git__suffixcmp(out.ptr, path) == 0);
+ else
+ cl_assert_equal_s(out.ptr, path);
+
+ /* find file using new path */
+ cl_git_pass(git_sysdir_find_global_file(temp, file));
+
+ /* reset path and confirm file not found */
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, NULL));
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_sysdir_find_global_file(temp, file));
+
+ git_str_dispose(&out);
+}
+
+void test_core_env__2(void)
+{
+ git_str path = GIT_STR_INIT, found = GIT_STR_INIT;
+ char testfile[16], tidx = '0';
+ char **val;
+ const char *testname = "alternate";
+ size_t testlen = strlen(testname);
+
+ strncpy(testfile, testname, sizeof(testfile));
+ cl_assert_equal_s(testname, testfile);
+
+ for (val = home_values; *val != NULL; val++) {
+
+ /* if we can't make the directory, let's just assume
+ * we are on a filesystem that doesn't support the
+ * characters in question and skip this test...
+ */
+ if (p_mkdir(*val, 0777) != 0 && errno != EEXIST) {
+ *val = ""; /* mark as not created */
+ continue;
+ }
+
+ cl_git_pass(git_fs_path_prettify(&path, *val, NULL));
+
+ /* vary testfile name so any sloppiness is resetting variables or
+ * deleting files won't accidentally make a test pass.
+ */
+ testfile[testlen] = tidx++;
+ cl_git_pass(git_str_joinpath(&path, path.ptr, testfile));
+ cl_git_mkfile(path.ptr, "find me");
+ git_str_rtruncate_at_char(&path, '/');
+
+ /* default should be NOTFOUND */
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_sysdir_find_global_file(&found, testfile));
+
+ /* try plain, append $PATH, and prepend $PATH */
+ check_global_searchpath(path.ptr, 0, testfile, &found);
+ check_global_searchpath(path.ptr, -1, testfile, &found);
+ check_global_searchpath(path.ptr, 1, testfile, &found);
+
+ /* cleanup */
+ cl_git_pass(git_str_joinpath(&path, path.ptr, testfile));
+ (void)p_unlink(path.ptr);
+ (void)p_rmdir(*val);
+ }
+
+ git_str_dispose(&path);
+ git_str_dispose(&found);
+}
+
+void test_core_env__substitution(void)
+{
+ git_str buf = GIT_STR_INIT, expected = GIT_STR_INIT;
+
+ /* Set it to something non-default so we have controllable values */
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, "/tmp/a"));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, &buf));
+ cl_assert_equal_s("/tmp/a", buf.ptr);
+
+ git_str_clear(&buf);
+ cl_git_pass(git_str_join(&buf, GIT_PATH_LIST_SEPARATOR, "$PATH", "/tmp/b"));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, buf.ptr));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, &buf));
+
+ cl_git_pass(git_str_join(&expected, GIT_PATH_LIST_SEPARATOR, "/tmp/a", "/tmp/b"));
+ cl_assert_equal_s(expected.ptr, buf.ptr);
+
+ git_str_dispose(&expected);
+ git_str_dispose(&buf);
+}
diff --git a/tests/libgit2/core/features.c b/tests/libgit2/core/features.c
new file mode 100644
index 0000000..7b28cc0
--- /dev/null
+++ b/tests/libgit2/core/features.c
@@ -0,0 +1,35 @@
+#include "clar_libgit2.h"
+
+void test_core_features__0(void)
+{
+ int major, minor, rev, caps;
+
+ git_libgit2_version(&major, &minor, &rev);
+ cl_assert_equal_i(LIBGIT2_VER_MAJOR, major);
+ cl_assert_equal_i(LIBGIT2_VER_MINOR, minor);
+ cl_assert_equal_i(LIBGIT2_VER_REVISION, rev);
+
+ caps = git_libgit2_features();
+
+#ifdef GIT_THREADS
+ cl_assert((caps & GIT_FEATURE_THREADS) != 0);
+#else
+ cl_assert((caps & GIT_FEATURE_THREADS) == 0);
+#endif
+
+#ifdef GIT_HTTPS
+ cl_assert((caps & GIT_FEATURE_HTTPS) != 0);
+#endif
+
+#if defined(GIT_SSH)
+ cl_assert((caps & GIT_FEATURE_SSH) != 0);
+#else
+ cl_assert((caps & GIT_FEATURE_SSH) == 0);
+#endif
+
+#if defined(GIT_USE_NSEC)
+ cl_assert((caps & GIT_FEATURE_NSEC) != 0);
+#else
+ cl_assert((caps & GIT_FEATURE_NSEC) == 0);
+#endif
+}
diff --git a/tests/libgit2/core/hashsig.c b/tests/libgit2/core/hashsig.c
new file mode 100644
index 0000000..6cadb1c
--- /dev/null
+++ b/tests/libgit2/core/hashsig.c
@@ -0,0 +1,182 @@
+#include "clar_libgit2.h"
+#include "git2/sys/hashsig.h"
+#include "futils.h"
+
+#define SIMILARITY_TEST_DATA_1 \
+ "000\n001\n002\n003\n004\n005\n006\n007\n008\n009\n" \
+ "010\n011\n012\n013\n014\n015\n016\n017\n018\n019\n" \
+ "020\n021\n022\n023\n024\n025\n026\n027\n028\n029\n" \
+ "030\n031\n032\n033\n034\n035\n036\n037\n038\n039\n" \
+ "040\n041\n042\n043\n044\n045\n046\n047\n048\n049\n"
+
+void test_core_hashsig__similarity_metric(void)
+{
+ git_hashsig *a, *b;
+ git_str buf = GIT_STR_INIT;
+ int sim;
+
+ /* in the first case, we compare data to itself and expect 100% match */
+
+ cl_git_pass(git_str_sets(&buf, SIMILARITY_TEST_DATA_1));
+ cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
+ cl_git_pass(git_hashsig_create(&b, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
+
+ cl_assert_equal_i(100, git_hashsig_compare(a, b));
+
+ git_hashsig_free(a);
+ git_hashsig_free(b);
+
+ /* if we change just a single byte, how much does that change magnify? */
+
+ cl_git_pass(git_str_sets(&buf, SIMILARITY_TEST_DATA_1));
+ cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
+ cl_git_pass(git_str_sets(&buf,
+ "000\n001\n002\n003\n004\n005\n006\n007\n008\n009\n" \
+ "010\n011\n012\n013\n014\n015\n016\n017\n018\n019\n" \
+ "x020x\n021\n022\n023\n024\n025\n026\n027\n028\n029\n" \
+ "030\n031\n032\n033\n034\n035\n036\n037\n038\n039\n" \
+ "040\n041\n042\n043\n044\n045\n046\n047\n048\n049\n"
+ ));
+ cl_git_pass(git_hashsig_create(&b, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
+
+ sim = git_hashsig_compare(a, b);
+
+ cl_assert_in_range(95, sim, 100); /* expect >95% similarity */
+
+ git_hashsig_free(a);
+ git_hashsig_free(b);
+
+ /* let's try comparing data to a superset of itself */
+
+ cl_git_pass(git_str_sets(&buf, SIMILARITY_TEST_DATA_1));
+ cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
+ cl_git_pass(git_str_sets(&buf, SIMILARITY_TEST_DATA_1
+ "050\n051\n052\n053\n054\n055\n056\n057\n058\n059\n"));
+ cl_git_pass(git_hashsig_create(&b, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
+
+ sim = git_hashsig_compare(a, b);
+ /* 20% lines added ~= 10% lines changed */
+
+ cl_assert_in_range(85, sim, 95); /* expect similarity around 90% */
+
+ git_hashsig_free(a);
+ git_hashsig_free(b);
+
+ /* what if we keep about half the original data and add half new */
+
+ cl_git_pass(git_str_sets(&buf, SIMILARITY_TEST_DATA_1));
+ cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
+ cl_git_pass(git_str_sets(&buf,
+ "000\n001\n002\n003\n004\n005\n006\n007\n008\n009\n" \
+ "010\n011\n012\n013\n014\n015\n016\n017\n018\n019\n" \
+ "020x\n021\n022\n023\n024\n" \
+ "x25\nx26\nx27\nx28\nx29\n" \
+ "x30\nx31\nx32\nx33\nx34\nx35\nx36\nx37\nx38\nx39\n" \
+ "x40\nx41\nx42\nx43\nx44\nx45\nx46\nx47\nx48\nx49\n"
+ ));
+ cl_git_pass(git_hashsig_create(&b, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
+
+ sim = git_hashsig_compare(a, b);
+ /* 50% lines changed */
+
+ cl_assert_in_range(40, sim, 60); /* expect in the 40-60% similarity range */
+
+ git_hashsig_free(a);
+ git_hashsig_free(b);
+
+ /* lastly, let's check that we can hash file content as well */
+
+ cl_git_pass(git_str_sets(&buf, SIMILARITY_TEST_DATA_1));
+ cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, GIT_HASHSIG_NORMAL));
+
+ cl_git_pass(git_futils_mkdir("scratch", 0755, GIT_MKDIR_PATH));
+ cl_git_mkfile("scratch/testdata", SIMILARITY_TEST_DATA_1);
+ cl_git_pass(git_hashsig_create_fromfile(
+ &b, "scratch/testdata", GIT_HASHSIG_NORMAL));
+
+ cl_assert_equal_i(100, git_hashsig_compare(a, b));
+
+ git_hashsig_free(a);
+ git_hashsig_free(b);
+
+ git_str_dispose(&buf);
+ git_futils_rmdir_r("scratch", NULL, GIT_RMDIR_REMOVE_FILES);
+}
+
+void test_core_hashsig__similarity_metric_whitespace(void)
+{
+ git_hashsig *a, *b;
+ git_str buf = GIT_STR_INIT;
+ int sim, i, j;
+ git_hashsig_option_t opt;
+ const char *tabbed =
+ " for (s = 0; s < sizeof(sep) / sizeof(char); ++s) {\n"
+ " separator = sep[s];\n"
+ " expect = expect_values[s];\n"
+ "\n"
+ " for (j = 0; j < sizeof(b) / sizeof(char*); ++j) {\n"
+ " for (i = 0; i < sizeof(a) / sizeof(char*); ++i) {\n"
+ " git_str_join(&buf, separator, a[i], b[j]);\n"
+ " cl_assert_equal_s(*expect, buf.ptr);\n"
+ " expect++;\n"
+ " }\n"
+ " }\n"
+ " }\n";
+ const char *spaced =
+ " for (s = 0; s < sizeof(sep) / sizeof(char); ++s) {\n"
+ " separator = sep[s];\n"
+ " expect = expect_values[s];\n"
+ "\n"
+ " for (j = 0; j < sizeof(b) / sizeof(char*); ++j) {\n"
+ " for (i = 0; i < sizeof(a) / sizeof(char*); ++i) {\n"
+ " git_str_join(&buf, separator, a[i], b[j]);\n"
+ " cl_assert_equal_s(*expect, buf.ptr);\n"
+ " expect++;\n"
+ " }\n"
+ " }\n"
+ " }\n";
+ const char *crlf_spaced2 =
+ " for (s = 0; s < sizeof(sep) / sizeof(char); ++s) {\r\n"
+ " separator = sep[s];\r\n"
+ " expect = expect_values[s];\r\n"
+ "\r\n"
+ " for (j = 0; j < sizeof(b) / sizeof(char*); ++j) {\r\n"
+ " for (i = 0; i < sizeof(a) / sizeof(char*); ++i) {\r\n"
+ " git_str_join(&buf, separator, a[i], b[j]);\r\n"
+ " cl_assert_equal_s(*expect, buf.ptr);\r\n"
+ " expect++;\r\n"
+ " }\r\n"
+ " }\r\n"
+ " }\r\n";
+ const char *text[3] = { tabbed, spaced, crlf_spaced2 };
+
+ /* let's try variations of our own code with whitespace changes */
+
+ for (opt = GIT_HASHSIG_NORMAL; opt <= GIT_HASHSIG_SMART_WHITESPACE; ++opt) {
+ for (i = 0; i < 3; ++i) {
+ for (j = 0; j < 3; ++j) {
+ cl_git_pass(git_str_sets(&buf, text[i]));
+ cl_git_pass(git_hashsig_create(&a, buf.ptr, buf.size, opt));
+
+ cl_git_pass(git_str_sets(&buf, text[j]));
+ cl_git_pass(git_hashsig_create(&b, buf.ptr, buf.size, opt));
+
+ sim = git_hashsig_compare(a, b);
+
+ if (opt == GIT_HASHSIG_NORMAL) {
+ if (i == j)
+ cl_assert_equal_i(100, sim);
+ else
+ cl_assert_in_range(0, sim, 30); /* pretty different */
+ } else {
+ cl_assert_equal_i(100, sim);
+ }
+
+ git_hashsig_free(a);
+ git_hashsig_free(b);
+ }
+ }
+ }
+
+ git_str_dispose(&buf);
+}
diff --git a/tests/libgit2/core/oid.c b/tests/libgit2/core/oid.c
new file mode 100644
index 0000000..a405b33
--- /dev/null
+++ b/tests/libgit2/core/oid.c
@@ -0,0 +1,213 @@
+#include "clar_libgit2.h"
+#include "oid.h"
+
+static git_oid id_sha1;
+static git_oid idp_sha1;
+static git_oid idm_sha1;
+
+const char *str_oid_sha1 = "ae90f12eea699729ed24555e40b9fd669da12a12";
+const char *str_oid_sha1_p = "ae90f12eea699729ed";
+const char *str_oid_sha1_m = "ae90f12eea699729ed24555e40b9fd669da12a12THIS IS EXTRA TEXT THAT SHOULD GET IGNORED";
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+static git_oid id_sha256;
+static git_oid idp_sha256;
+static git_oid idm_sha256;
+
+const char *str_oid_sha256 = "d3e63d2f2e43d1fee23a74bf19a0ede156cba2d1bd602eba13de433cea1bb512";
+const char *str_oid_sha256_p = "d3e63d2f2e43d1fee2";
+const char *str_oid_sha256_m = "d3e63d2f2e43d1fee23a74bf19a0ede156cba2d1bd602eba13de433cea1bb512 GARBAGE EXTRA TEXT AT THE END";
+#endif
+
+void test_core_oid__initialize(void)
+{
+ cl_git_pass(git_oid__fromstr(&id_sha1, str_oid_sha1, GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstrp(&idp_sha1, str_oid_sha1_p, GIT_OID_SHA1));
+ cl_git_fail(git_oid__fromstrp(&idm_sha1, str_oid_sha1_m, GIT_OID_SHA1));
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_oid__fromstr(&id_sha256, str_oid_sha256, GIT_OID_SHA256));
+ cl_git_pass(git_oid__fromstrp(&idp_sha256, str_oid_sha256_p, GIT_OID_SHA256));
+ cl_git_fail(git_oid__fromstrp(&idm_sha256, str_oid_sha256_m, GIT_OID_SHA256));
+#endif
+}
+
+void test_core_oid__streq_sha1(void)
+{
+ cl_assert_equal_i(0, git_oid_streq(&id_sha1, str_oid_sha1));
+ cl_assert_equal_i(-1, git_oid_streq(&id_sha1, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
+
+ cl_assert_equal_i(-1, git_oid_streq(&id_sha1, "deadbeef"));
+ cl_assert_equal_i(-1, git_oid_streq(&id_sha1, "I'm not an oid.... :)"));
+
+ cl_assert_equal_i(0, git_oid_streq(&idp_sha1, "ae90f12eea699729ed0000000000000000000000"));
+ cl_assert_equal_i(0, git_oid_streq(&idp_sha1, "ae90f12eea699729ed"));
+ cl_assert_equal_i(-1, git_oid_streq(&idp_sha1, "ae90f12eea699729ed1"));
+ cl_assert_equal_i(-1, git_oid_streq(&idp_sha1, "ae90f12eea699729ec"));
+ cl_assert_equal_i(-1, git_oid_streq(&idp_sha1, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
+
+ cl_assert_equal_i(-1, git_oid_streq(&idp_sha1, "deadbeef"));
+ cl_assert_equal_i(-1, git_oid_streq(&idp_sha1, "I'm not an oid.... :)"));
+}
+
+void test_core_oid__streq_sha256(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ cl_assert_equal_i(0, git_oid_streq(&id_sha256, str_oid_sha256));
+ cl_assert_equal_i(-1, git_oid_streq(&id_sha256, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
+
+ cl_assert_equal_i(-1, git_oid_streq(&id_sha256, "deadbeef"));
+ cl_assert_equal_i(-1, git_oid_streq(&id_sha256, "I'm not an oid.... :)"));
+
+ cl_assert_equal_i(0, git_oid_streq(&idp_sha256, "d3e63d2f2e43d1fee20000000000000000000000000000000000000000000000"));
+ cl_assert_equal_i(0, git_oid_streq(&idp_sha256, "d3e63d2f2e43d1fee2"));
+ cl_assert_equal_i(-1, git_oid_streq(&idp_sha1, "d3e63d2f2e43d1fee21"));
+ cl_assert_equal_i(-1, git_oid_streq(&idp_sha1, "d3e63d2f2e43d1fee1"));
+ cl_assert_equal_i(-1, git_oid_streq(&idp_sha256, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"));
+
+ cl_assert_equal_i(-1, git_oid_streq(&idp_sha1, "deadbeef"));
+ cl_assert_equal_i(-1, git_oid_streq(&idp_sha1, "I'm not an oid.... :)"));
+#endif
+}
+
+void test_core_oid__strcmp_sha1(void)
+{
+ cl_assert_equal_i(0, git_oid_strcmp(&id_sha1, str_oid_sha1));
+ cl_assert(git_oid_strcmp(&id_sha1, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef") < 0);
+
+ cl_assert(git_oid_strcmp(&id_sha1, "deadbeef") < 0);
+ cl_assert_equal_i(-1, git_oid_strcmp(&id_sha1, "I'm not an oid.... :)"));
+
+ cl_assert_equal_i(0, git_oid_strcmp(&idp_sha1, "ae90f12eea699729ed0000000000000000000000"));
+ cl_assert_equal_i(0, git_oid_strcmp(&idp_sha1, "ae90f12eea699729ed"));
+ cl_assert(git_oid_strcmp(&idp_sha1, "ae90f12eea699729ed1") < 0);
+ cl_assert(git_oid_strcmp(&idp_sha1, "ae90f12eea699729ec") > 0);
+ cl_assert(git_oid_strcmp(&idp_sha1, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef") < 0);
+
+ cl_assert(git_oid_strcmp(&idp_sha1, "deadbeef") < 0);
+ cl_assert_equal_i(-1, git_oid_strcmp(&idp_sha1, "I'm not an oid.... :)"));
+}
+
+void test_core_oid__strcmp_sha256(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ cl_assert_equal_i(0, git_oid_strcmp(&id_sha256, str_oid_sha256));
+ cl_assert(git_oid_strcmp(&id_sha256, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef") < 0);
+
+ cl_assert(git_oid_strcmp(&id_sha256, "deadbeef") < 0);
+ cl_assert_equal_i(-1, git_oid_strcmp(&id_sha256, "I'm not an oid.... :)"));
+
+ cl_assert_equal_i(0, git_oid_strcmp(&idp_sha256, "d3e63d2f2e43d1fee20000000000000000000000"));
+ cl_assert_equal_i(0, git_oid_strcmp(&idp_sha256, "d3e63d2f2e43d1fee2"));
+ cl_assert(git_oid_strcmp(&idp_sha256, "d3e63d2f2e43d1fee21") < 0);
+ cl_assert(git_oid_strcmp(&idp_sha256, "d3e63d2f2e43d1fee1") > 0);
+ cl_assert(git_oid_strcmp(&idp_sha256, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef") < 0);
+
+ cl_assert(git_oid_strcmp(&idp_sha256, "deadbeef") < 0);
+ cl_assert_equal_i(-1, git_oid_strcmp(&idp_sha256, "I'm not an oid.... :)"));
+#endif
+}
+
+void test_core_oid__ncmp_sha1(void)
+{
+ cl_assert(!git_oid_ncmp(&id_sha1, &idp_sha1, 0));
+ cl_assert(!git_oid_ncmp(&id_sha1, &idp_sha1, 1));
+ cl_assert(!git_oid_ncmp(&id_sha1, &idp_sha1, 2));
+ cl_assert(!git_oid_ncmp(&id_sha1, &idp_sha1, 17));
+ cl_assert(!git_oid_ncmp(&id_sha1, &idp_sha1, 18));
+ cl_assert(git_oid_ncmp(&id_sha1, &idp_sha1, 19));
+ cl_assert(git_oid_ncmp(&id_sha1, &idp_sha1, 40));
+ cl_assert(git_oid_ncmp(&id_sha1, &idp_sha1, 41));
+ cl_assert(git_oid_ncmp(&id_sha1, &idp_sha1, 42));
+
+ cl_assert(!git_oid_ncmp(&id_sha1, &id_sha1, 0));
+ cl_assert(!git_oid_ncmp(&id_sha1, &id_sha1, 1));
+ cl_assert(!git_oid_ncmp(&id_sha1, &id_sha1, 39));
+ cl_assert(!git_oid_ncmp(&id_sha1, &id_sha1, 40));
+ cl_assert(!git_oid_ncmp(&id_sha1, &id_sha1, 41));
+}
+
+void test_core_oid__ncmp_sha256(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ cl_assert(!git_oid_ncmp(&id_sha256, &idp_sha256, 0));
+ cl_assert(!git_oid_ncmp(&id_sha256, &idp_sha256, 1));
+ cl_assert(!git_oid_ncmp(&id_sha256, &idp_sha256, 2));
+ cl_assert(!git_oid_ncmp(&id_sha256, &idp_sha256, 17));
+ cl_assert(!git_oid_ncmp(&id_sha256, &idp_sha256, 18));
+ cl_assert(git_oid_ncmp(&id_sha256, &idp_sha256, 19));
+ cl_assert(git_oid_ncmp(&id_sha256, &idp_sha256, 40));
+ cl_assert(git_oid_ncmp(&id_sha256, &idp_sha256, 41));
+ cl_assert(git_oid_ncmp(&id_sha256, &idp_sha256, 42));
+ cl_assert(git_oid_ncmp(&id_sha256, &idp_sha256, 63));
+ cl_assert(git_oid_ncmp(&id_sha256, &idp_sha256, 64));
+ cl_assert(git_oid_ncmp(&id_sha256, &idp_sha256, 65));
+
+ cl_assert(!git_oid_ncmp(&id_sha256, &id_sha256, 0));
+ cl_assert(!git_oid_ncmp(&id_sha256, &id_sha256, 1));
+ cl_assert(!git_oid_ncmp(&id_sha256, &id_sha256, 39));
+ cl_assert(!git_oid_ncmp(&id_sha256, &id_sha256, 40));
+ cl_assert(!git_oid_ncmp(&id_sha256, &id_sha256, 41));
+ cl_assert(!git_oid_ncmp(&id_sha256, &id_sha256, 63));
+ cl_assert(!git_oid_ncmp(&id_sha256, &id_sha256, 64));
+ cl_assert(!git_oid_ncmp(&id_sha256, &id_sha256, 65));
+#endif
+}
+
+void test_core_oid__is_hexstr(void)
+{
+ cl_assert(git_oid__is_hexstr("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef", GIT_OID_SHA1));
+ cl_assert(!git_oid__is_hexstr("deadbeefdeadbeef", GIT_OID_SHA1));
+ cl_assert(!git_oid__is_hexstr("zeadbeefdeadbeefdeadbeefdeadbeefdeadbeef", GIT_OID_SHA1));
+ cl_assert(!git_oid__is_hexstr("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef1", GIT_OID_SHA1));
+}
+
+void test_core_oid__fmt_substr_sha1(void)
+{
+ char buf[GIT_OID_MAX_HEXSIZE + 1];
+
+ memset(buf, 0, GIT_OID_MAX_HEXSIZE + 1);
+ git_oid_fmt_substr(buf, &id_sha1, 0, 40);
+ cl_assert_equal_s(buf, str_oid_sha1);
+
+ memset(buf, 0, GIT_OID_MAX_HEXSIZE + 1);
+ git_oid_fmt_substr(buf, &id_sha1, 0, 18);
+ cl_assert_equal_s(buf, str_oid_sha1_p);
+
+ memset(buf, 0, GIT_OID_MAX_HEXSIZE + 1);
+ git_oid_fmt_substr(buf, &id_sha1, 0, 5);
+ cl_assert_equal_s(buf, "ae90f");
+
+ memset(buf, 0, GIT_OID_MAX_HEXSIZE + 1);
+ git_oid_fmt_substr(buf, &id_sha1, 5, 5);
+ cl_assert_equal_s(buf, "12eea");
+
+ memset(buf, 0, GIT_OID_MAX_HEXSIZE + 1);
+ git_oid_fmt_substr(buf, &id_sha1, 5, 6);
+ cl_assert_equal_s(buf, "12eea6");
+}
+
+void test_core_oid__type_lookup(void)
+{
+ cl_assert_equal_i(GIT_OID_SHA1, git_oid_type_fromstr("sha1"));
+ cl_assert_equal_i(GIT_OID_SHA1, git_oid_type_fromstrn("sha1...", 4));
+ cl_assert_equal_s("sha1", git_oid_type_name(GIT_OID_SHA1));
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_assert_equal_i(GIT_OID_SHA256, git_oid_type_fromstr("sha256"));
+ cl_assert_equal_i(GIT_OID_SHA256, git_oid_type_fromstrn("sha256...", 6));
+ cl_assert_equal_s("sha256", git_oid_type_name(GIT_OID_SHA256));
+#endif
+
+ cl_assert_equal_i(0, git_oid_type_fromstr("sha42"));
+ cl_assert_equal_i(0, git_oid_type_fromstrn("sha1", 3));
+ cl_assert_equal_i(0, git_oid_type_fromstrn("sha1...", 5));
+ cl_assert_equal_s("unknown", git_oid_type_name(0));
+ cl_assert_equal_s("unknown", git_oid_type_name(42));
+}
diff --git a/tests/libgit2/core/oidarray.c b/tests/libgit2/core/oidarray.c
new file mode 100644
index 0000000..4a9e47c
--- /dev/null
+++ b/tests/libgit2/core/oidarray.c
@@ -0,0 +1,98 @@
+#include "clar_libgit2.h"
+
+#include "git2/oid.h"
+#include "git2/transport.h"
+
+#include "common.h"
+#include "transports/smart.h"
+#include "oid.h"
+#include "oidarray.h"
+
+#include <assert.h>
+
+#define oid_0 "c070ad8c08840c8116da865b2d65593a6bb9cd2a"
+#define oid_1 "0966a434eb1a025db6b71485ab63a3bfbea520b6"
+#define oid_2 "83834a7afdaa1a1260568567f6ad90020389f664"
+#define oid_3 "746fb4c91a7b6190bc4761adf7410afc4b59812c"
+
+void test_core_oidarray__add_and_remove_oid_from_shallowarray(void)
+{
+ git_oid oid_0_obj, oid_1_obj, oid_2_obj, oid_3_obj;
+ git_array_oid_t array = GIT_ARRAY_INIT;
+
+ git_oid__fromstr(&oid_0_obj, oid_0, GIT_OID_SHA1);
+ git_oid__fromstr(&oid_1_obj, oid_1, GIT_OID_SHA1);
+ git_oid__fromstr(&oid_2_obj, oid_2, GIT_OID_SHA1);
+ git_oid__fromstr(&oid_3_obj, oid_3, GIT_OID_SHA1);
+
+ /* add some initial ids */
+ git_oidarray__add(&array, &oid_0_obj);
+ git_oidarray__add(&array, &oid_1_obj);
+ git_oidarray__add(&array, &oid_2_obj);
+
+ cl_assert_equal_i(3, array.size);
+ cl_assert_equal_s("c070ad8c08840c8116da865b2d65593a6bb9cd2a", git_oid_tostr_s(&array.ptr[0]));
+ cl_assert_equal_s("0966a434eb1a025db6b71485ab63a3bfbea520b6", git_oid_tostr_s(&array.ptr[1]));
+ cl_assert_equal_s("83834a7afdaa1a1260568567f6ad90020389f664", git_oid_tostr_s(&array.ptr[2]));
+
+ /* don't duplicate existing ids */
+ git_oidarray__add(&array, &oid_1_obj);
+
+ cl_assert_equal_i(3, array.size);
+ cl_assert_equal_s("c070ad8c08840c8116da865b2d65593a6bb9cd2a", git_oid_tostr_s(&array.ptr[0]));
+ cl_assert_equal_s("0966a434eb1a025db6b71485ab63a3bfbea520b6", git_oid_tostr_s(&array.ptr[1]));
+ cl_assert_equal_s("83834a7afdaa1a1260568567f6ad90020389f664", git_oid_tostr_s(&array.ptr[2]));
+
+ /* remove the last id */
+ cl_assert_equal_i(1, git_oidarray__remove(&array, &oid_2_obj));
+
+ cl_assert_equal_i(2, array.size);
+ cl_assert_equal_s("c070ad8c08840c8116da865b2d65593a6bb9cd2a", git_oid_tostr_s(&array.ptr[0]));
+ cl_assert_equal_s("0966a434eb1a025db6b71485ab63a3bfbea520b6", git_oid_tostr_s(&array.ptr[1]));
+
+ /* add another id */
+ git_oidarray__add(&array, &oid_3_obj);
+
+ cl_assert_equal_i(3, array.size);
+ cl_assert_equal_s("c070ad8c08840c8116da865b2d65593a6bb9cd2a", git_oid_tostr_s(&array.ptr[0]));
+ cl_assert_equal_s("0966a434eb1a025db6b71485ab63a3bfbea520b6", git_oid_tostr_s(&array.ptr[1]));
+ cl_assert_equal_s("746fb4c91a7b6190bc4761adf7410afc4b59812c", git_oid_tostr_s(&array.ptr[2]));
+
+ /* remove the first id */
+ cl_assert_equal_i(1, git_oidarray__remove(&array, &oid_0_obj));
+
+ cl_assert_equal_i(2, array.size);
+ cl_assert_equal_s("0966a434eb1a025db6b71485ab63a3bfbea520b6", git_oid_tostr_s(&array.ptr[0]));
+ cl_assert_equal_s("746fb4c91a7b6190bc4761adf7410afc4b59812c", git_oid_tostr_s(&array.ptr[1]));
+
+ /* removing a nonexistent oid does nothing */
+ cl_assert_equal_i(0, git_oidarray__remove(&array, &oid_2_obj));
+
+ /* add another id */
+ git_oidarray__add(&array, &oid_0_obj);
+
+ cl_assert_equal_i(3, array.size);
+ cl_assert_equal_s("0966a434eb1a025db6b71485ab63a3bfbea520b6", git_oid_tostr_s(&array.ptr[0]));
+ cl_assert_equal_s("746fb4c91a7b6190bc4761adf7410afc4b59812c", git_oid_tostr_s(&array.ptr[1]));
+ cl_assert_equal_s("c070ad8c08840c8116da865b2d65593a6bb9cd2a", git_oid_tostr_s(&array.ptr[2]));
+
+ /* remove another id */
+ cl_assert_equal_i(1, git_oidarray__remove(&array, &oid_3_obj));
+
+ cl_assert_equal_i(2, array.size);
+ cl_assert_equal_s("0966a434eb1a025db6b71485ab63a3bfbea520b6", git_oid_tostr_s(&array.ptr[0]));
+ cl_assert_equal_s("c070ad8c08840c8116da865b2d65593a6bb9cd2a", git_oid_tostr_s(&array.ptr[1]));
+
+ /* remove another id */
+ cl_assert_equal_i(1, git_oidarray__remove(&array, &oid_1_obj));
+
+ cl_assert_equal_i(1, array.size);
+ cl_assert_equal_s("c070ad8c08840c8116da865b2d65593a6bb9cd2a", git_oid_tostr_s(&array.ptr[0]));
+
+ /* remove the final id */
+ cl_assert_equal_i(1, git_oidarray__remove(&array, &oid_0_obj));
+
+ cl_assert_equal_i(0, array.size);
+
+ git_array_clear(array);
+}
diff --git a/tests/libgit2/core/oidmap.c b/tests/libgit2/core/oidmap.c
new file mode 100644
index 0000000..34374ce
--- /dev/null
+++ b/tests/libgit2/core/oidmap.c
@@ -0,0 +1,130 @@
+#include "clar_libgit2.h"
+#include "oidmap.h"
+
+static struct {
+ git_oid oid;
+ size_t extra;
+} test_oids[0x0FFF];
+
+static git_oidmap *g_map;
+
+void test_core_oidmap__initialize(void)
+{
+ uint32_t i, j;
+ for (i = 0; i < ARRAY_SIZE(test_oids); ++i) {
+ uint32_t segment = i / 8;
+ int modi = i - (segment * 8);
+
+ test_oids[i].extra = i;
+
+ for (j = 0; j < GIT_OID_SHA1_SIZE / 4; ++j) {
+ test_oids[i].oid.id[j * 4 ] = (unsigned char)modi;
+ test_oids[i].oid.id[j * 4 + 1] = (unsigned char)(modi >> 8);
+ test_oids[i].oid.id[j * 4 + 2] = (unsigned char)(modi >> 16);
+ test_oids[i].oid.id[j * 4 + 3] = (unsigned char)(modi >> 24);
+ }
+
+ test_oids[i].oid.id[ 8] = (unsigned char)i;
+ test_oids[i].oid.id[ 9] = (unsigned char)(i >> 8);
+ test_oids[i].oid.id[10] = (unsigned char)(i >> 16);
+ test_oids[i].oid.id[11] = (unsigned char)(i >> 24);
+#ifdef GIT_EXPERIMENTAL_SHA256
+ test_oids[i].oid.type = GIT_OID_SHA1;
+#endif
+ }
+
+ cl_git_pass(git_oidmap_new(&g_map));
+}
+
+void test_core_oidmap__cleanup(void)
+{
+ git_oidmap_free(g_map);
+}
+
+void test_core_oidmap__basic(void)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(test_oids); ++i) {
+ cl_assert(!git_oidmap_exists(g_map, &test_oids[i].oid));
+ cl_git_pass(git_oidmap_set(g_map, &test_oids[i].oid, &test_oids[i]));
+ }
+
+ for (i = 0; i < ARRAY_SIZE(test_oids); ++i) {
+ cl_assert(git_oidmap_exists(g_map, &test_oids[i].oid));
+ cl_assert_equal_p(git_oidmap_get(g_map, &test_oids[i].oid), &test_oids[i]);
+ }
+}
+
+void test_core_oidmap__hash_collision(void)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(test_oids); ++i) {
+ cl_assert(!git_oidmap_exists(g_map, &test_oids[i].oid));
+ cl_git_pass(git_oidmap_set(g_map, &test_oids[i].oid, &test_oids[i]));
+ }
+
+ for (i = 0; i < ARRAY_SIZE(test_oids); ++i) {
+ cl_assert(git_oidmap_exists(g_map, &test_oids[i].oid));
+ cl_assert_equal_p(git_oidmap_get(g_map, &test_oids[i].oid), &test_oids[i]);
+ }
+}
+
+void test_core_oidmap__get_succeeds_with_existing_keys(void)
+{
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(test_oids); ++i)
+ cl_git_pass(git_oidmap_set(g_map, &test_oids[i].oid, &test_oids[i]));
+
+ for (i = 0; i < ARRAY_SIZE(test_oids); ++i)
+ cl_assert_equal_p(git_oidmap_get(g_map, &test_oids[i].oid), &test_oids[i]);
+}
+
+void test_core_oidmap__get_fails_with_nonexisting_key(void)
+{
+ size_t i;
+
+ /* Do _not_ add last OID to verify that we cannot look it up */
+ for (i = 0; i < ARRAY_SIZE(test_oids) - 1; ++i)
+ cl_git_pass(git_oidmap_set(g_map, &test_oids[i].oid, &test_oids[i]));
+
+ cl_assert_equal_p(git_oidmap_get(g_map, &test_oids[ARRAY_SIZE(test_oids) - 1].oid), NULL);
+}
+
+void test_core_oidmap__setting_oid_persists(void)
+{
+ git_oid oids[] = {
+ GIT_OID_INIT(GIT_OID_SHA1, { 0x01 }),
+ GIT_OID_INIT(GIT_OID_SHA1, { 0x02 }),
+ GIT_OID_INIT(GIT_OID_SHA1, { 0x03 })
+ };
+
+ cl_git_pass(git_oidmap_set(g_map, &oids[0], "one"));
+ cl_git_pass(git_oidmap_set(g_map, &oids[1], "two"));
+ cl_git_pass(git_oidmap_set(g_map, &oids[2], "three"));
+
+ cl_assert_equal_s(git_oidmap_get(g_map, &oids[0]), "one");
+ cl_assert_equal_s(git_oidmap_get(g_map, &oids[1]), "two");
+ cl_assert_equal_s(git_oidmap_get(g_map, &oids[2]), "three");
+}
+
+void test_core_oidmap__setting_existing_key_updates(void)
+{
+ git_oid oids[] = {
+ GIT_OID_INIT(GIT_OID_SHA1, { 0x01 }),
+ GIT_OID_INIT(GIT_OID_SHA1, { 0x02 }),
+ GIT_OID_INIT(GIT_OID_SHA1, { 0x03 })
+ };
+
+ cl_git_pass(git_oidmap_set(g_map, &oids[0], "one"));
+ cl_git_pass(git_oidmap_set(g_map, &oids[1], "two"));
+ cl_git_pass(git_oidmap_set(g_map, &oids[2], "three"));
+ cl_assert_equal_i(git_oidmap_size(g_map), 3);
+
+ cl_git_pass(git_oidmap_set(g_map, &oids[1], "other"));
+ cl_assert_equal_i(git_oidmap_size(g_map), 3);
+
+ cl_assert_equal_s(git_oidmap_get(g_map, &oids[1]), "other");
+}
diff --git a/tests/libgit2/core/opts.c b/tests/libgit2/core/opts.c
new file mode 100644
index 0000000..1aa095a
--- /dev/null
+++ b/tests/libgit2/core/opts.c
@@ -0,0 +1,91 @@
+#include "clar_libgit2.h"
+#include "cache.h"
+
+void test_core_opts__cleanup(void)
+{
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_EXTENSIONS, NULL, 0));
+}
+
+void test_core_opts__readwrite(void)
+{
+ size_t old_val = 0;
+ size_t new_val = 0;
+
+ git_libgit2_opts(GIT_OPT_GET_MWINDOW_SIZE, &old_val);
+ git_libgit2_opts(GIT_OPT_SET_MWINDOW_SIZE, (size_t)1234);
+ git_libgit2_opts(GIT_OPT_GET_MWINDOW_SIZE, &new_val);
+
+ cl_assert(new_val == 1234);
+
+ git_libgit2_opts(GIT_OPT_SET_MWINDOW_SIZE, old_val);
+ git_libgit2_opts(GIT_OPT_GET_MWINDOW_SIZE, &new_val);
+
+ cl_assert(new_val == old_val);
+}
+
+void test_core_opts__invalid_option(void)
+{
+ cl_git_fail(git_libgit2_opts(-1, "foobar"));
+}
+
+void test_core_opts__extensions_query(void)
+{
+ git_strarray out = { 0 };
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_GET_EXTENSIONS, &out));
+
+ cl_assert_equal_sz(out.count, 2);
+ cl_assert_equal_s("noop", out.strings[0]);
+ cl_assert_equal_s("objectformat", out.strings[1]);
+
+ git_strarray_dispose(&out);
+}
+
+void test_core_opts__extensions_add(void)
+{
+ const char *in[] = { "foo" };
+ git_strarray out = { 0 };
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_EXTENSIONS, in, ARRAY_SIZE(in)));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_GET_EXTENSIONS, &out));
+
+ cl_assert_equal_sz(out.count, 3);
+ cl_assert_equal_s("foo", out.strings[0]);
+ cl_assert_equal_s("noop", out.strings[1]);
+ cl_assert_equal_s("objectformat", out.strings[2]);
+
+ git_strarray_dispose(&out);
+}
+
+void test_core_opts__extensions_remove(void)
+{
+ const char *in[] = { "bar", "!negate", "!noop", "baz" };
+ git_strarray out = { 0 };
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_EXTENSIONS, in, ARRAY_SIZE(in)));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_GET_EXTENSIONS, &out));
+
+ cl_assert_equal_sz(out.count, 3);
+ cl_assert_equal_s("bar", out.strings[0]);
+ cl_assert_equal_s("baz", out.strings[1]);
+ cl_assert_equal_s("objectformat", out.strings[2]);
+
+ git_strarray_dispose(&out);
+}
+
+void test_core_opts__extensions_uniq(void)
+{
+ const char *in[] = { "foo", "noop", "bar", "bar", "foo", "objectformat" };
+ git_strarray out = { 0 };
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_EXTENSIONS, in, ARRAY_SIZE(in)));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_GET_EXTENSIONS, &out));
+
+ cl_assert_equal_sz(out.count, 4);
+ cl_assert_equal_s("bar", out.strings[0]);
+ cl_assert_equal_s("foo", out.strings[1]);
+ cl_assert_equal_s("noop", out.strings[2]);
+ cl_assert_equal_s("objectformat", out.strings[3]);
+
+ git_strarray_dispose(&out);
+}
diff --git a/tests/libgit2/core/pool.c b/tests/libgit2/core/pool.c
new file mode 100644
index 0000000..cf01cb9
--- /dev/null
+++ b/tests/libgit2/core/pool.c
@@ -0,0 +1,38 @@
+#include "clar_libgit2.h"
+#include "pool.h"
+#include "git2/oid.h"
+
+static char to_hex[] = "0123456789abcdef";
+
+void test_core_pool__oid(void)
+{
+ git_pool p;
+ char oid_hex[GIT_OID_SHA1_HEXSIZE];
+ git_oid *oid;
+ int i, j;
+
+ memset(oid_hex, '0', sizeof(oid_hex));
+
+ git_pool_init(&p, sizeof(git_oid));
+ p.page_size = 4000;
+
+ for (i = 1000; i < 10000; i++) {
+ oid = git_pool_malloc(&p, 1);
+ cl_assert(oid != NULL);
+
+ for (j = 0; j < 8; j++)
+ oid_hex[j] = to_hex[(i >> (4 * j)) & 0x0f];
+ cl_git_pass(git_oid__fromstr(oid, oid_hex, GIT_OID_SHA1));
+ }
+
+#ifndef GIT_DEBUG_POOL
+ /* with fixed page size, allocation must end up with these values */
+
+# ifdef GIT_EXPERIMENTAL_SHA256
+ cl_assert_equal_i(sizeof(void *) == 8 ? 90 : 82, git_pool__open_pages(&p));
+# else
+ cl_assert_equal_i(sizeof(void *) == 8 ? 55 : 45, git_pool__open_pages(&p));
+# endif
+#endif
+ git_pool_clear(&p);
+}
diff --git a/tests/libgit2/core/structinit.c b/tests/libgit2/core/structinit.c
new file mode 100644
index 0000000..8a6e48d
--- /dev/null
+++ b/tests/libgit2/core/structinit.c
@@ -0,0 +1,206 @@
+#include "clar_libgit2.h"
+#include <git2/sys/commit_graph.h>
+#include <git2/sys/config.h>
+#include <git2/sys/filter.h>
+#include <git2/sys/odb_backend.h>
+#include <git2/sys/refdb_backend.h>
+#include <git2/sys/transport.h>
+
+#define STRINGIFY(s) #s
+
+/* Checks two conditions for the specified structure:
+ * 1. That the initializers for the latest version produces the same
+ * in-memory representation.
+ * 2. That the function-based initializer supports all versions from 1...n,
+ * where n is the latest version (often represented by GIT_*_VERSION).
+ *
+ * Parameters:
+ * structname: The name of the structure to test, e.g. git_blame_options.
+ * structver: The latest version of the specified structure.
+ * macroinit: The macro that initializes the latest version of the structure.
+ * funcinitname: The function that initializes the structure. Must have the
+ * signature "int (structname* instance, int version)".
+ */
+#define CHECK_MACRO_FUNC_INIT_EQUAL(structname, structver, macroinit, funcinitname) \
+do { \
+ structname structname##_macro_latest = macroinit; \
+ structname structname##_func_latest; \
+ int structname##_curr_ver = structver - 1; \
+ memset(&structname##_func_latest, 0, sizeof(structname##_func_latest)); \
+ cl_git_pass(funcinitname(&structname##_func_latest, structver)); \
+ options_cmp(&structname##_macro_latest, &structname##_func_latest, \
+ sizeof(structname), STRINGIFY(structname)); \
+ \
+ while (structname##_curr_ver > 0) \
+ { \
+ structname macro; \
+ cl_git_pass(funcinitname(&macro, structname##_curr_ver)); \
+ structname##_curr_ver--; \
+ }\
+} while(0)
+
+static void options_cmp(void *one, void *two, size_t size, const char *name)
+{
+ size_t i;
+
+ for (i = 0; i < size; i++) {
+ if (((char *)one)[i] != ((char *)two)[i]) {
+ char desc[1024];
+
+ p_snprintf(desc, 1024, "Difference in %s at byte %" PRIuZ ": macro=%u / func=%u",
+ name, i, ((char *)one)[i], ((char *)two)[i]);
+ clar__fail(__FILE__, __func__, __LINE__,
+ "Difference between macro and function options initializer",
+ desc, 0);
+ return;
+ }
+ }
+}
+
+void test_core_structinit__compare(void)
+{
+ /* These tests assume that they can memcmp() two structures that were
+ * initialized with the same static initializer. Eg,
+ * git_blame_options = GIT_BLAME_OPTIONS_INIT;
+ *
+ * This assumption fails when there is padding between structure members,
+ * which is not guaranteed to be initialized to anything sane at all.
+ *
+ * Assume most compilers, in a debug build, will clear that memory for
+ * us or set it to sentinel markers. Etc.
+ */
+#if !defined(DEBUG) && !defined(_DEBUG)
+ clar__skip();
+#endif
+
+ /* apply */
+ CHECK_MACRO_FUNC_INIT_EQUAL( \
+ git_apply_options, GIT_APPLY_OPTIONS_VERSION, \
+ GIT_APPLY_OPTIONS_INIT, git_apply_options_init);
+
+ /* blame */
+ CHECK_MACRO_FUNC_INIT_EQUAL( \
+ git_blame_options, GIT_BLAME_OPTIONS_VERSION, \
+ GIT_BLAME_OPTIONS_INIT, git_blame_options_init);
+
+ /* blob_filter_options */
+ CHECK_MACRO_FUNC_INIT_EQUAL( \
+ git_blob_filter_options, GIT_BLOB_FILTER_OPTIONS_VERSION, \
+ GIT_BLOB_FILTER_OPTIONS_INIT, git_blob_filter_options_init);
+
+ /* checkout */
+ CHECK_MACRO_FUNC_INIT_EQUAL( \
+ git_checkout_options, GIT_CHECKOUT_OPTIONS_VERSION, \
+ GIT_CHECKOUT_OPTIONS_INIT, git_checkout_options_init);
+
+ /* clone */
+ CHECK_MACRO_FUNC_INIT_EQUAL( \
+ git_clone_options, GIT_CLONE_OPTIONS_VERSION, \
+ GIT_CLONE_OPTIONS_INIT, git_clone_options_init);
+
+ /* commit_graph_writer */
+ CHECK_MACRO_FUNC_INIT_EQUAL( \
+ git_commit_graph_writer_options, \
+ GIT_COMMIT_GRAPH_WRITER_OPTIONS_VERSION, \
+ GIT_COMMIT_GRAPH_WRITER_OPTIONS_INIT, \
+ git_commit_graph_writer_options_init);
+
+ /* diff */
+ CHECK_MACRO_FUNC_INIT_EQUAL( \
+ git_diff_options, GIT_DIFF_OPTIONS_VERSION, \
+ GIT_DIFF_OPTIONS_INIT, git_diff_options_init);
+
+ /* diff_find */
+ CHECK_MACRO_FUNC_INIT_EQUAL( \
+ git_diff_find_options, GIT_DIFF_FIND_OPTIONS_VERSION, \
+ GIT_DIFF_FIND_OPTIONS_INIT, git_diff_find_options_init);
+
+ /* filter */
+ CHECK_MACRO_FUNC_INIT_EQUAL( \
+ git_filter, GIT_FILTER_VERSION, \
+ GIT_FILTER_INIT, git_filter_init);
+
+ /* merge_file_input */
+ CHECK_MACRO_FUNC_INIT_EQUAL( \
+ git_merge_file_input, GIT_MERGE_FILE_INPUT_VERSION, \
+ GIT_MERGE_FILE_INPUT_INIT, git_merge_file_input_init);
+
+ /* merge_file */
+ CHECK_MACRO_FUNC_INIT_EQUAL( \
+ git_merge_file_options, GIT_MERGE_FILE_OPTIONS_VERSION, \
+ GIT_MERGE_FILE_OPTIONS_INIT, git_merge_file_options_init);
+
+ /* merge_tree */
+ CHECK_MACRO_FUNC_INIT_EQUAL( \
+ git_merge_options, GIT_MERGE_OPTIONS_VERSION, \
+ GIT_MERGE_OPTIONS_INIT, git_merge_options_init);
+
+ /* push */
+ CHECK_MACRO_FUNC_INIT_EQUAL( \
+ git_push_options, GIT_PUSH_OPTIONS_VERSION, \
+ GIT_PUSH_OPTIONS_INIT, git_push_options_init);
+
+ /* remote */
+ CHECK_MACRO_FUNC_INIT_EQUAL( \
+ git_remote_callbacks, GIT_REMOTE_CALLBACKS_VERSION, \
+ GIT_REMOTE_CALLBACKS_INIT, git_remote_init_callbacks);
+
+ /* repository_init */
+ CHECK_MACRO_FUNC_INIT_EQUAL( \
+ git_repository_init_options, GIT_REPOSITORY_INIT_OPTIONS_VERSION, \
+ GIT_REPOSITORY_INIT_OPTIONS_INIT, git_repository_init_options_init);
+
+ /* revert */
+ CHECK_MACRO_FUNC_INIT_EQUAL( \
+ git_revert_options, GIT_REVERT_OPTIONS_VERSION, \
+ GIT_REVERT_OPTIONS_INIT, git_revert_options_init);
+
+ /* stash apply */
+ CHECK_MACRO_FUNC_INIT_EQUAL( \
+ git_stash_apply_options, GIT_STASH_APPLY_OPTIONS_VERSION, \
+ GIT_STASH_APPLY_OPTIONS_INIT, git_stash_apply_options_init);
+
+ /* stash save */
+ CHECK_MACRO_FUNC_INIT_EQUAL( \
+ git_stash_save_options, GIT_STASH_SAVE_OPTIONS_VERSION, \
+ GIT_STASH_SAVE_OPTIONS_INIT, git_stash_save_options_init);
+
+ /* status */
+ CHECK_MACRO_FUNC_INIT_EQUAL( \
+ git_status_options, GIT_STATUS_OPTIONS_VERSION, \
+ GIT_STATUS_OPTIONS_INIT, git_status_options_init);
+
+ /* transport */
+ CHECK_MACRO_FUNC_INIT_EQUAL( \
+ git_transport, GIT_TRANSPORT_VERSION, \
+ GIT_TRANSPORT_INIT, git_transport_init);
+
+ /* config_backend */
+ CHECK_MACRO_FUNC_INIT_EQUAL( \
+ git_config_backend, GIT_CONFIG_BACKEND_VERSION, \
+ GIT_CONFIG_BACKEND_INIT, git_config_init_backend);
+
+ /* odb_backend */
+ CHECK_MACRO_FUNC_INIT_EQUAL( \
+ git_odb_backend, GIT_ODB_BACKEND_VERSION, \
+ GIT_ODB_BACKEND_INIT, git_odb_init_backend);
+
+ /* refdb_backend */
+ CHECK_MACRO_FUNC_INIT_EQUAL( \
+ git_refdb_backend, GIT_REFDB_BACKEND_VERSION, \
+ GIT_REFDB_BACKEND_INIT, git_refdb_init_backend);
+
+ /* submodule update */
+ CHECK_MACRO_FUNC_INIT_EQUAL( \
+ git_submodule_update_options, GIT_SUBMODULE_UPDATE_OPTIONS_VERSION, \
+ GIT_SUBMODULE_UPDATE_OPTIONS_INIT, git_submodule_update_options_init);
+
+ /* submodule update */
+ CHECK_MACRO_FUNC_INIT_EQUAL( \
+ git_proxy_options, GIT_PROXY_OPTIONS_VERSION, \
+ GIT_PROXY_OPTIONS_INIT, git_proxy_options_init);
+
+ CHECK_MACRO_FUNC_INIT_EQUAL( \
+ git_diff_patchid_options, GIT_DIFF_PATCHID_OPTIONS_VERSION, \
+ GIT_DIFF_PATCHID_OPTIONS_INIT, git_diff_patchid_options_init);
+}
diff --git a/tests/libgit2/core/useragent.c b/tests/libgit2/core/useragent.c
new file mode 100644
index 0000000..a4ece90
--- /dev/null
+++ b/tests/libgit2/core/useragent.c
@@ -0,0 +1,17 @@
+#include "clar_libgit2.h"
+#include "settings.h"
+
+void test_core_useragent__get(void)
+{
+ const char *custom_name = "super duper git";
+ git_str buf = GIT_STR_INIT;
+
+ cl_assert_equal_p(NULL, git_libgit2__user_agent());
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_USER_AGENT, custom_name));
+ cl_assert_equal_s(custom_name, git_libgit2__user_agent());
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_GET_USER_AGENT, &buf));
+ cl_assert_equal_s(custom_name, buf.ptr);
+
+ git_str_dispose(&buf);
+}
diff --git a/tests/libgit2/date/date.c b/tests/libgit2/date/date.c
new file mode 100644
index 0000000..82b5c67
--- /dev/null
+++ b/tests/libgit2/date/date.c
@@ -0,0 +1,22 @@
+#include "clar_libgit2.h"
+
+#include "date.h"
+
+void test_date_date__overflow(void)
+{
+#ifdef __LP64__
+ git_time_t d2038, d2039;
+
+ /* This is expected to fail on a 32-bit machine. */
+ cl_git_pass(git_date_parse(&d2038, "2038-1-1"));
+ cl_git_pass(git_date_parse(&d2039, "2039-1-1"));
+ cl_assert(d2038 < d2039);
+#endif
+}
+
+void test_date_date__invalid_date(void)
+{
+ git_time_t d;
+ cl_git_fail(git_date_parse(&d, ""));
+ cl_git_fail(git_date_parse(&d, "NEITHER_INTEGER_NOR_DATETIME"));
+}
diff --git a/tests/libgit2/date/rfc2822.c b/tests/libgit2/date/rfc2822.c
new file mode 100644
index 0000000..b0bbcfc
--- /dev/null
+++ b/tests/libgit2/date/rfc2822.c
@@ -0,0 +1,37 @@
+#include "clar_libgit2.h"
+
+#include "date.h"
+
+void test_date_rfc2822__format_rfc2822_no_offset(void)
+{
+ git_time t = {1397031663, 0};
+ git_str buf = GIT_STR_INIT;
+
+ cl_git_pass(git_date_rfc2822_fmt(&buf, t.time, t.offset));
+ cl_assert_equal_s("Wed, 9 Apr 2014 08:21:03 +0000", buf.ptr);
+
+ git_str_dispose(&buf);
+}
+
+void test_date_rfc2822__format_rfc2822_positive_offset(void)
+{
+ git_time t = {1397031663, 120};
+ git_str buf = GIT_STR_INIT;
+
+ cl_git_pass(git_date_rfc2822_fmt(&buf, t.time, t.offset));
+ cl_assert_equal_s("Wed, 9 Apr 2014 10:21:03 +0200", buf.ptr);
+
+ git_str_dispose(&buf);
+}
+
+void test_date_rfc2822__format_rfc2822_negative_offset(void)
+{
+ git_time t = {1397031663, -120};
+ git_str buf = GIT_STR_INIT;
+
+ cl_git_pass(git_date_rfc2822_fmt(&buf, t.time, t.offset));
+ cl_assert_equal_s("Wed, 9 Apr 2014 06:21:03 -0200", buf.ptr);
+
+ git_str_dispose(&buf);
+}
+
diff --git a/tests/libgit2/delta/apply.c b/tests/libgit2/delta/apply.c
new file mode 100644
index 0000000..5bb95a2
--- /dev/null
+++ b/tests/libgit2/delta/apply.c
@@ -0,0 +1,21 @@
+#include "clar_libgit2.h"
+
+#include "delta.h"
+
+void test_delta_apply__read_at_off(void)
+{
+ unsigned char base[16] = { 0 }, delta[] = { 0x10, 0x10, 0xff, 0xff, 0xff, 0xff, 0xff, 0x10, 0x00, 0x00 };
+ void *out;
+ size_t outlen;
+
+ cl_git_fail(git_delta_apply(&out, &outlen, base, sizeof(base), delta, sizeof(delta)));
+}
+
+void test_delta_apply__read_after_limit(void)
+{
+ unsigned char base[16] = { 0 }, delta[] = { 0x10, 0x70, 0xff };
+ void *out;
+ size_t outlen;
+
+ cl_git_fail(git_delta_apply(&out, &outlen, base, sizeof(base), delta, sizeof(delta)));
+}
diff --git a/tests/libgit2/describe/describe.c b/tests/libgit2/describe/describe.c
new file mode 100644
index 0000000..ba67ca4
--- /dev/null
+++ b/tests/libgit2/describe/describe.c
@@ -0,0 +1,55 @@
+#include "clar_libgit2.h"
+#include "describe_helpers.h"
+
+void test_describe_describe__can_describe_against_a_bare_repo(void)
+{
+ git_repository *repo;
+ git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT;
+ git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT;
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+
+ assert_describe("hard_tag", "HEAD", repo, &opts, &fmt_opts);
+
+ opts.show_commit_oid_as_fallback = 1;
+
+ assert_describe("be3563a*", "HEAD^", repo, &opts, &fmt_opts);
+
+ git_repository_free(repo);
+}
+
+static int delete_cb(git_reference *ref, void *payload)
+{
+ GIT_UNUSED(payload);
+
+ cl_git_pass(git_reference_delete(ref));
+ git_reference_free(ref);
+
+ return 0;
+}
+
+void test_describe_describe__describe_a_repo_with_no_refs(void)
+{
+ git_repository *repo;
+ git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT;
+ git_str buf = GIT_STR_INIT;
+ git_object *object;
+ git_describe_result *result = NULL;
+
+ repo = cl_git_sandbox_init("testrepo.git");
+ cl_git_pass(git_revparse_single(&object, repo, "HEAD"));
+
+ cl_git_pass(git_reference_foreach(repo, delete_cb, NULL));
+
+ /* Impossible to describe without falling back to OIDs */
+ cl_git_fail(git_describe_commit(&result, object, &opts));
+
+ /* Try again with OID fallbacks */
+ opts.show_commit_oid_as_fallback = 1;
+ cl_git_pass(git_describe_commit(&result, object, &opts));
+
+ git_describe_result_free(result);
+ git_object_free(object);
+ git_str_dispose(&buf);
+ cl_git_sandbox_cleanup();
+}
diff --git a/tests/libgit2/describe/describe_helpers.c b/tests/libgit2/describe/describe_helpers.c
new file mode 100644
index 0000000..3df3b7c
--- /dev/null
+++ b/tests/libgit2/describe/describe_helpers.c
@@ -0,0 +1,44 @@
+#include "describe_helpers.h"
+
+#include "wildmatch.h"
+
+void assert_describe(
+ const char *expected_output,
+ const char *revparse_spec,
+ git_repository *repo,
+ git_describe_options *opts,
+ git_describe_format_options *fmt_opts)
+{
+ git_object *object;
+ git_buf label = GIT_BUF_INIT;
+ git_describe_result *result;
+
+ cl_git_pass(git_revparse_single(&object, repo, revparse_spec));
+
+ cl_git_pass(git_describe_commit(&result, object, opts));
+ cl_git_pass(git_describe_format(&label, result, fmt_opts));
+
+ cl_must_pass(wildmatch(expected_output, label.ptr, 0));
+
+ git_describe_result_free(result);
+ git_object_free(object);
+ git_buf_dispose(&label);
+}
+
+void assert_describe_workdir(
+ const char *expected_output,
+ git_repository *repo,
+ git_describe_options *opts,
+ git_describe_format_options *fmt_opts)
+{
+ git_buf label = GIT_BUF_INIT;
+ git_describe_result *result;
+
+ cl_git_pass(git_describe_workdir(&result, repo, opts));
+ cl_git_pass(git_describe_format(&label, result, fmt_opts));
+
+ cl_must_pass(wildmatch(expected_output, label.ptr, 0));
+
+ git_describe_result_free(result);
+ git_buf_dispose(&label);
+}
diff --git a/tests/libgit2/describe/describe_helpers.h b/tests/libgit2/describe/describe_helpers.h
new file mode 100644
index 0000000..43e8c5e
--- /dev/null
+++ b/tests/libgit2/describe/describe_helpers.h
@@ -0,0 +1,14 @@
+#include "clar_libgit2.h"
+
+extern void assert_describe(
+ const char *expected_output,
+ const char *revparse_spec,
+ git_repository *repo,
+ git_describe_options *opts,
+ git_describe_format_options *fmt_opts);
+
+extern void assert_describe_workdir(
+ const char *expected_output,
+ git_repository *repo,
+ git_describe_options *opts,
+ git_describe_format_options *fmt_opts);
diff --git a/tests/libgit2/describe/t6120.c b/tests/libgit2/describe/t6120.c
new file mode 100644
index 0000000..5ec176b
--- /dev/null
+++ b/tests/libgit2/describe/t6120.c
@@ -0,0 +1,156 @@
+#include "clar_libgit2.h"
+#include "describe_helpers.h"
+#include "repository.h"
+
+/* Ported from https://github.com/git/git/blob/adfc1857bdb090786fd9d22c1acec39371c76048/t/t6120-describe.sh */
+
+static git_repository *repo;
+
+void test_describe_t6120__initialize(void)
+{
+ repo = cl_git_sandbox_init("describe");
+}
+
+void test_describe_t6120__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_describe_t6120__default(void)
+{
+ git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT;
+ git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT;
+
+ assert_describe("A-*", "HEAD", repo, &opts, &fmt_opts);
+ assert_describe("A-*", "HEAD^", repo, &opts, &fmt_opts);
+ assert_describe("R-*", "HEAD^^", repo, &opts, &fmt_opts);
+ assert_describe("A-*", "HEAD^^2", repo, &opts, &fmt_opts);
+ assert_describe("B", "HEAD^^2^", repo, &opts, &fmt_opts);
+ assert_describe("R-*", "HEAD^^^", repo, &opts, &fmt_opts);
+}
+
+void test_describe_t6120__tags(void)
+{
+ git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT;
+ git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT;
+ opts.describe_strategy = GIT_DESCRIBE_TAGS;
+
+ assert_describe("c-*", "HEAD", repo, &opts, &fmt_opts);
+ assert_describe("c-*", "HEAD^", repo, &opts, &fmt_opts);
+ assert_describe("e-*", "HEAD^^", repo, &opts, &fmt_opts);
+ assert_describe("c-*", "HEAD^^2", repo, &opts, &fmt_opts);
+ assert_describe("B", "HEAD^^2^", repo, &opts, &fmt_opts);
+ assert_describe("e", "HEAD^^^", repo, &opts, &fmt_opts);
+}
+
+void test_describe_t6120__all(void)
+{
+ git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT;
+ git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT;
+ opts.describe_strategy = GIT_DESCRIBE_ALL;
+
+ assert_describe("heads/master", "HEAD", repo, &opts, &fmt_opts);
+ assert_describe("tags/c-*", "HEAD^", repo, &opts, &fmt_opts);
+ assert_describe("tags/e", "HEAD^^^", repo, &opts, &fmt_opts);
+}
+
+void test_describe_t6120__longformat(void)
+{
+ git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT;
+ git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT;
+
+ fmt_opts.always_use_long_format = 1;
+
+ assert_describe("B-0-*", "HEAD^^2^", repo, &opts, &fmt_opts);
+ assert_describe("A-3-*", "HEAD^^2", repo, &opts, &fmt_opts);
+}
+
+void test_describe_t6120__firstparent(void)
+{
+ git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT;
+ git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT;
+ opts.describe_strategy = GIT_DESCRIBE_TAGS;
+
+ assert_describe("c-7-*", "HEAD", repo, &opts, &fmt_opts);
+
+ opts.only_follow_first_parent = 1;
+ assert_describe("e-3-*", "HEAD", repo, &opts, &fmt_opts);
+}
+
+void test_describe_t6120__workdir(void)
+{
+ git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT;
+ git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT;
+
+ assert_describe_workdir("A-*[0-9a-f]", repo, &opts, &fmt_opts);
+ cl_git_mkfile("describe/file", "something different");
+
+ fmt_opts.dirty_suffix = "-dirty";
+ assert_describe_workdir("A-*[0-9a-f]-dirty", repo, &opts, &fmt_opts);
+ fmt_opts.dirty_suffix = ".mod";
+ assert_describe_workdir("A-*[0-9a-f].mod", repo, &opts, &fmt_opts);
+}
+
+static void commit_and_tag(
+ git_time_t *time,
+ const char *commit_msg,
+ const char *tag_name)
+{
+ git_index *index;
+ git_oid commit_id;
+ git_reference *ref;
+
+ cl_git_pass(git_repository_index__weakptr(&index, repo));
+
+ cl_git_append2file("describe/file", "\n");
+
+ cl_git_pass(git_index_add_bypath(index, "file"));
+ cl_git_pass(git_index_write(index));
+
+ *time += 10;
+ cl_repo_commit_from_index(&commit_id, repo, NULL, *time, commit_msg);
+
+ if (tag_name == NULL)
+ return;
+
+ cl_git_pass(git_reference_create(&ref, repo, tag_name, &commit_id, 0, NULL));
+ git_reference_free(ref);
+}
+
+void test_describe_t6120__pattern(void)
+{
+ git_describe_options opts = GIT_DESCRIBE_OPTIONS_INIT;
+ git_describe_format_options fmt_opts = GIT_DESCRIBE_FORMAT_OPTIONS_INIT;
+ git_oid tag_id;
+ git_object *head;
+ git_signature *tagger;
+ git_time_t time;
+
+ /* set-up matching pattern tests */
+ cl_git_pass(git_revparse_single(&head, repo, "HEAD"));
+
+ time = 1380553019;
+ cl_git_pass(git_signature_new(&tagger, "tagger", "tagger@libgit2.org", time, 0));
+ cl_git_pass(git_tag_create(&tag_id, repo, "test-annotated", head, tagger, "test-annotated", 0));
+ git_signature_free(tagger);
+ git_object_free(head);
+
+ commit_and_tag(&time, "one more", "refs/tags/test1-lightweight");
+ commit_and_tag(&time, "yet another", "refs/tags/test2-lightweight");
+ commit_and_tag(&time, "even more", NULL);
+
+
+ /* Exercize */
+ opts.pattern = "test-*";
+ assert_describe("test-annotated-*", "HEAD", repo, &opts, &fmt_opts);
+
+ opts.describe_strategy = GIT_DESCRIBE_TAGS;
+ opts.pattern = "test1-*";
+ assert_describe("test1-lightweight-*", "HEAD", repo, &opts, &fmt_opts);
+
+ opts.pattern = "test2-*";
+ assert_describe("test2-lightweight-*", "HEAD", repo, &opts, &fmt_opts);
+
+ fmt_opts.always_use_long_format = 1;
+ assert_describe("test2-lightweight-*", "HEAD^", repo, &opts, &fmt_opts);
+}
diff --git a/tests/libgit2/diff/binary.c b/tests/libgit2/diff/binary.c
new file mode 100644
index 0000000..3bf39f3
--- /dev/null
+++ b/tests/libgit2/diff/binary.c
@@ -0,0 +1,545 @@
+#include "clar_libgit2.h"
+
+#include "git2/sys/diff.h"
+
+#include "delta.h"
+#include "filebuf.h"
+#include "repository.h"
+
+static git_repository *repo;
+
+void test_diff_binary__initialize(void)
+{
+}
+
+void test_diff_binary__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static void test_patch(
+ const char *one,
+ const char *two,
+ const git_diff_options *opts,
+ const char *expected)
+{
+ git_oid id_one, id_two;
+ git_index *index = NULL;
+ git_commit *commit_one, *commit_two = NULL;
+ git_tree *tree_one, *tree_two;
+ git_diff *diff;
+ git_patch *patch;
+ git_buf actual = GIT_BUF_INIT;
+
+ cl_git_pass(git_oid__fromstr(&id_one, one, GIT_OID_SHA1));
+ cl_git_pass(git_commit_lookup(&commit_one, repo, &id_one));
+ cl_git_pass(git_commit_tree(&tree_one, commit_one));
+
+ if (two) {
+ cl_git_pass(git_oid__fromstr(&id_two, two, GIT_OID_SHA1));
+ cl_git_pass(git_commit_lookup(&commit_two, repo, &id_two));
+ cl_git_pass(git_commit_tree(&tree_two, commit_two));
+ } else {
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_write_tree(&id_two, index));
+ cl_git_pass(git_tree_lookup(&tree_two, repo, &id_two));
+ }
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, repo, tree_one, tree_two, opts));
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_to_buf(&actual, patch));
+
+ cl_assert_equal_s(expected, actual.ptr);
+
+ git_buf_dispose(&actual);
+ cl_git_pass(git_diff_print(diff, GIT_DIFF_FORMAT_PATCH, git_diff_print_callback__to_buf, &actual));
+
+ cl_assert_equal_s(expected, actual.ptr);
+
+ git_buf_dispose(&actual);
+ git_patch_free(patch);
+ git_diff_free(diff);
+ git_tree_free(tree_one);
+ git_tree_free(tree_two);
+ git_commit_free(commit_one);
+ git_commit_free(commit_two);
+ git_index_free(index);
+}
+
+void test_diff_binary__add_normal(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ const char *expected =
+ "diff --git a/binary.bin b/binary.bin\n" \
+ "new file mode 100644\n" \
+ "index 0000000..bd474b2\n" \
+ "Binary files /dev/null and b/binary.bin differ\n";
+
+ repo = cl_git_sandbox_init("diff_format_email");
+ test_patch(
+ "873806f6f27e631eb0b23e4b56bea2bfac14a373",
+ "897d3af16ca9e420cd071b1c4541bd2b91d04c8c",
+ &opts,
+ expected);
+}
+
+void test_diff_binary__add(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ const char *expected =
+ "diff --git a/binary.bin b/binary.bin\n" \
+ "new file mode 100644\n" \
+ "index 0000000000000000000000000000000000000000..bd474b2519cc15eab801ff851cc7d50f0dee49a1\n" \
+ "GIT binary patch\n" \
+ "literal 3\n" \
+ "Kc${Nk-~s>u4FC%O\n"
+ "\n" \
+ "literal 0\n" \
+ "Hc$@<O00001\n" \
+ "\n";
+
+ opts.flags = GIT_DIFF_SHOW_BINARY;
+ opts.id_abbrev = GIT_OID_SHA1_HEXSIZE;
+
+ repo = cl_git_sandbox_init("diff_format_email");
+ test_patch(
+ "873806f6f27e631eb0b23e4b56bea2bfac14a373",
+ "897d3af16ca9e420cd071b1c4541bd2b91d04c8c",
+ &opts,
+ expected);
+}
+
+void test_diff_binary__modify_normal(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ const char *expected =
+ "diff --git a/binary.bin b/binary.bin\n" \
+ "index bd474b2..9ac35ff 100644\n" \
+ "Binary files a/binary.bin and b/binary.bin differ\n";
+
+ repo = cl_git_sandbox_init("diff_format_email");
+ test_patch(
+ "897d3af16ca9e420cd071b1c4541bd2b91d04c8c",
+ "8d7523f6fcb2404257889abe0d96f093d9f524f9",
+ &opts,
+ expected);
+}
+
+void test_diff_binary__modify(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ const char *expected =
+ "diff --git a/binary.bin b/binary.bin\n" \
+ "index bd474b2519cc15eab801ff851cc7d50f0dee49a1..9ac35ff15cd8864aeafd889e4826a3150f0b06c4 100644\n" \
+ "GIT binary patch\n" \
+ "literal 5\n" \
+ "Mc${NkU}WL~000&M4gdfE\n" \
+ "\n" \
+ "literal 3\n" \
+ "Kc${Nk-~s>u4FC%O\n" \
+ "\n";
+
+ opts.flags = GIT_DIFF_SHOW_BINARY;
+
+ repo = cl_git_sandbox_init("diff_format_email");
+ test_patch(
+ "897d3af16ca9e420cd071b1c4541bd2b91d04c8c",
+ "8d7523f6fcb2404257889abe0d96f093d9f524f9",
+ &opts,
+ expected);
+}
+
+void test_diff_binary__delete_normal(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ const char *expected =
+ "diff --git a/binary.bin b/binary.bin\n" \
+ "deleted file mode 100644\n" \
+ "index bd474b2..0000000\n" \
+ "Binary files a/binary.bin and /dev/null differ\n";
+
+ repo = cl_git_sandbox_init("diff_format_email");
+ test_patch(
+ "897d3af16ca9e420cd071b1c4541bd2b91d04c8c",
+ "873806f6f27e631eb0b23e4b56bea2bfac14a373",
+ &opts,
+ expected);
+}
+
+void test_diff_binary__delete(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ const char *expected =
+ "diff --git a/binary.bin b/binary.bin\n" \
+ "deleted file mode 100644\n" \
+ "index bd474b2519cc15eab801ff851cc7d50f0dee49a1..0000000000000000000000000000000000000000\n" \
+ "GIT binary patch\n" \
+ "literal 0\n" \
+ "Hc$@<O00001\n" \
+ "\n" \
+ "literal 3\n" \
+ "Kc${Nk-~s>u4FC%O\n" \
+ "\n";
+
+ opts.flags = GIT_DIFF_SHOW_BINARY;
+ opts.id_abbrev = GIT_OID_SHA1_HEXSIZE;
+
+ repo = cl_git_sandbox_init("diff_format_email");
+ test_patch(
+ "897d3af16ca9e420cd071b1c4541bd2b91d04c8c",
+ "873806f6f27e631eb0b23e4b56bea2bfac14a373",
+ &opts,
+ expected);
+}
+
+void test_diff_binary__delta(void)
+{
+ git_index *index;
+ git_str contents = GIT_STR_INIT;
+ size_t i;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ const char *expected =
+ "diff --git a/songof7cities.txt b/songof7cities.txt\n" \
+ "index 4210ffd5c390b21dd5483375e75288dea9ede512..cc84ec183351c9944ed90a619ca08911924055b5 100644\n" \
+ "GIT binary patch\n" \
+ "delta 198\n" \
+ "zc$}LmI8{(0BqLQJI6p64AwNwaIJGP_Pa)Ye#M3o+qJ$<Jl;sX*mF<MGCYv&*L7AHu\n" \
+ "zGA1*^gt?gYVN82wTbPO_W)+x<&1+cP;HrPHR>PQ;Y(X&QMK*C5^Br3bjG4d=XI^5@\n" \
+ "JfH567LIG)KJdFSV\n" \
+ "\n" \
+ "delta 198\n" \
+ "zc$}LmI8{(0BqLQJI6p64AwNwaIJGP_Pr*5}Br~;mqJ$<Jl;sX*mF<MGCYv&*L7AHu\n" \
+ "zGA1*^gt?gYVN82wTbPO_W)+x<&1+cP;HrPHR>PQ;Y(X&QMK*C5^Br3bjG4d=XI^5@\n" \
+ "JfH567LIF3FM2!Fd\n" \
+ "\n";
+
+ opts.flags = GIT_DIFF_SHOW_BINARY | GIT_DIFF_FORCE_BINARY;
+ opts.id_abbrev = GIT_OID_SHA1_HEXSIZE;
+
+ repo = cl_git_sandbox_init("renames");
+ cl_git_pass(git_repository_index(&index, repo));
+
+ cl_git_pass(git_futils_readbuffer(&contents, "renames/songof7cities.txt"));
+
+ for (i = 0; i < contents.size - 6; i++) {
+ if (strncmp(&contents.ptr[i], "Cities", 6) == 0)
+ memcpy(&contents.ptr[i], "cITIES", 6);
+ }
+
+ cl_git_rewritefile("renames/songof7cities.txt", contents.ptr);
+ cl_git_pass(git_index_add_bypath(index, "songof7cities.txt"));
+ cl_git_pass(git_index_write(index));
+
+ test_patch(
+ "19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13",
+ NULL,
+ &opts,
+ expected);
+
+ git_index_free(index);
+ git_str_dispose(&contents);
+}
+
+void test_diff_binary__delta_append(void)
+{
+ git_index *index;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ const char *expected =
+ "diff --git a/untimely.txt b/untimely.txt\n" \
+ "index 9a69d960ae94b060f56c2a8702545e2bb1abb935..1111d4f11f4b35bf6759e0fb714fe09731ef0840 100644\n" \
+ "GIT binary patch\n" \
+ "delta 32\n" \
+ "nc%1vf+QYWt3zLL@hC)e3Vu?a>QDRl4f_G*?PG(-ZA}<#J$+QbW\n" \
+ "\n" \
+ "delta 7\n" \
+ "Oc%18D`@*{63ljhg(E~C7\n" \
+ "\n";
+
+ opts.flags = GIT_DIFF_SHOW_BINARY | GIT_DIFF_FORCE_BINARY;
+ opts.id_abbrev = GIT_OID_SHA1_HEXSIZE;
+
+ repo = cl_git_sandbox_init("renames");
+ cl_git_pass(git_repository_index(&index, repo));
+
+ cl_git_append2file("renames/untimely.txt", "Oh that crazy Kipling!\r\n");
+ cl_git_pass(git_index_add_bypath(index, "untimely.txt"));
+ cl_git_pass(git_index_write(index));
+
+ test_patch(
+ "19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13",
+ NULL,
+ &opts,
+ expected);
+
+ git_index_free(index);
+}
+
+void test_diff_binary__empty_for_no_diff(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_oid id;
+ git_commit *commit;
+ git_tree *tree;
+ git_diff *diff;
+ git_str actual = GIT_STR_INIT;
+
+ opts.flags = GIT_DIFF_SHOW_BINARY | GIT_DIFF_FORCE_BINARY;
+ opts.id_abbrev = GIT_OID_SHA1_HEXSIZE;
+
+ repo = cl_git_sandbox_init("renames");
+
+ cl_git_pass(git_oid__fromstr(&id, "19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13", GIT_OID_SHA1));
+ cl_git_pass(git_commit_lookup(&commit, repo, &id));
+ cl_git_pass(git_commit_tree(&tree, commit));
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, repo, tree, tree, &opts));
+ cl_git_pass(git_diff_print(diff, GIT_DIFF_FORMAT_PATCH, git_diff_print_callback__to_buf, &actual));
+
+ cl_assert_equal_s("", actual.ptr);
+
+ git_str_dispose(&actual);
+ git_diff_free(diff);
+ git_commit_free(commit);
+ git_tree_free(tree);
+}
+
+void test_diff_binary__index_to_workdir(void)
+{
+ git_index *index;
+ git_diff *diff;
+ git_patch *patch;
+ git_buf actual = GIT_BUF_INIT;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ const char *expected =
+ "diff --git a/untimely.txt b/untimely.txt\n" \
+ "index 9a69d960ae94b060f56c2a8702545e2bb1abb935..1111d4f11f4b35bf6759e0fb714fe09731ef0840 100644\n" \
+ "GIT binary patch\n" \
+ "delta 32\n" \
+ "nc%1vf+QYWt3zLL@hC)e3Vu?a>QDRl4f_G*?PG(-ZA}<#J$+QbW\n" \
+ "\n" \
+ "delta 7\n" \
+ "Oc%18D`@*{63ljhg(E~C7\n" \
+ "\n";
+
+ opts.flags = GIT_DIFF_SHOW_BINARY | GIT_DIFF_FORCE_BINARY;
+ opts.id_abbrev = GIT_OID_SHA1_HEXSIZE;
+
+ repo = cl_git_sandbox_init("renames");
+ cl_git_pass(git_repository_index(&index, repo));
+
+ cl_git_append2file("renames/untimely.txt", "Oh that crazy Kipling!\r\n");
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, repo, index, &opts));
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_to_buf(&actual, patch));
+
+ cl_assert_equal_s(expected, actual.ptr);
+
+ cl_git_pass(git_index_add_bypath(index, "untimely.txt"));
+ cl_git_pass(git_index_write(index));
+
+ test_patch(
+ "19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13",
+ NULL,
+ &opts,
+ expected);
+
+ git_buf_dispose(&actual);
+ git_patch_free(patch);
+ git_diff_free(diff);
+ git_index_free(index);
+}
+
+static int print_cb(
+ const git_diff_delta *delta,
+ const git_diff_hunk *hunk,
+ const git_diff_line *line,
+ void *payload)
+{
+ git_str *buf = (git_str *)payload;
+
+ GIT_UNUSED(delta);
+
+ if (hunk)
+ git_str_put(buf, hunk->header, hunk->header_len);
+
+ if (line)
+ git_str_put(buf, line->content, line->content_len);
+
+ return git_str_oom(buf) ? -1 : 0;
+}
+
+void test_diff_binary__print_patch_from_diff(void)
+{
+ git_index *index;
+ git_diff *diff;
+ git_str actual = GIT_STR_INIT;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ const char *expected =
+ "diff --git a/untimely.txt b/untimely.txt\n" \
+ "index 9a69d960ae94b060f56c2a8702545e2bb1abb935..1111d4f11f4b35bf6759e0fb714fe09731ef0840 100644\n" \
+ "GIT binary patch\n" \
+ "delta 32\n" \
+ "nc%1vf+QYWt3zLL@hC)e3Vu?a>QDRl4f_G*?PG(-ZA}<#J$+QbW\n" \
+ "\n" \
+ "delta 7\n" \
+ "Oc%18D`@*{63ljhg(E~C7\n" \
+ "\n";
+
+ opts.flags = GIT_DIFF_SHOW_BINARY | GIT_DIFF_FORCE_BINARY;
+ opts.id_abbrev = GIT_OID_SHA1_HEXSIZE;
+
+ repo = cl_git_sandbox_init("renames");
+ cl_git_pass(git_repository_index(&index, repo));
+
+ cl_git_append2file("renames/untimely.txt", "Oh that crazy Kipling!\r\n");
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, repo, index, &opts));
+
+ cl_git_pass(git_diff_print(diff, GIT_DIFF_FORMAT_PATCH, print_cb, &actual));
+
+ cl_assert_equal_s(expected, actual.ptr);
+
+ git_str_dispose(&actual);
+ git_diff_free(diff);
+ git_index_free(index);
+}
+
+struct diff_data {
+ char *old_path;
+ git_oid old_id;
+ git_str old_binary_base85;
+ size_t old_binary_inflatedlen;
+ git_diff_binary_t old_binary_type;
+
+ char *new_path;
+ git_oid new_id;
+ git_str new_binary_base85;
+ size_t new_binary_inflatedlen;
+ git_diff_binary_t new_binary_type;
+};
+
+static int file_cb(
+ const git_diff_delta *delta,
+ float progress,
+ void *payload)
+{
+ struct diff_data *diff_data = payload;
+
+ GIT_UNUSED(progress);
+
+ if (delta->old_file.path)
+ diff_data->old_path = git__strdup(delta->old_file.path);
+
+ if (delta->new_file.path)
+ diff_data->new_path = git__strdup(delta->new_file.path);
+
+ git_oid_cpy(&diff_data->old_id, &delta->old_file.id);
+ git_oid_cpy(&diff_data->new_id, &delta->new_file.id);
+
+ return 0;
+}
+
+static int binary_cb(
+ const git_diff_delta *delta,
+ const git_diff_binary *binary,
+ void *payload)
+{
+ struct diff_data *diff_data = payload;
+
+ GIT_UNUSED(delta);
+
+ git_str_encode_base85(&diff_data->old_binary_base85,
+ binary->old_file.data, binary->old_file.datalen);
+ diff_data->old_binary_inflatedlen = binary->old_file.inflatedlen;
+ diff_data->old_binary_type = binary->old_file.type;
+
+ git_str_encode_base85(&diff_data->new_binary_base85,
+ binary->new_file.data, binary->new_file.datalen);
+ diff_data->new_binary_inflatedlen = binary->new_file.inflatedlen;
+ diff_data->new_binary_type = binary->new_file.type;
+
+ return 0;
+}
+
+static int hunk_cb(
+ const git_diff_delta *delta,
+ const git_diff_hunk *hunk,
+ void *payload)
+{
+ GIT_UNUSED(delta);
+ GIT_UNUSED(hunk);
+ GIT_UNUSED(payload);
+
+ cl_fail("did not expect hunk callback");
+ return 0;
+}
+
+static int line_cb(
+ const git_diff_delta *delta,
+ const git_diff_hunk *hunk,
+ const git_diff_line *line,
+ void *payload)
+{
+ GIT_UNUSED(delta);
+ GIT_UNUSED(hunk);
+ GIT_UNUSED(line);
+ GIT_UNUSED(payload);
+
+ cl_fail("did not expect line callback");
+ return 0;
+}
+
+void test_diff_binary__blob_to_blob(void)
+{
+ git_index *index;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_blob *old_blob, *new_blob;
+ git_oid old_id, new_id;
+ struct diff_data diff_data = {0};
+
+ opts.flags = GIT_DIFF_SHOW_BINARY | GIT_DIFF_FORCE_BINARY;
+ opts.id_abbrev = GIT_OID_SHA1_HEXSIZE;
+
+ repo = cl_git_sandbox_init("renames");
+ cl_git_pass(git_repository_index__weakptr(&index, repo));
+
+ cl_git_append2file("renames/untimely.txt", "Oh that crazy Kipling!\r\n");
+ cl_git_pass(git_index_add_bypath(index, "untimely.txt"));
+ cl_git_pass(git_index_write(index));
+
+ git_oid__fromstr(&old_id, "9a69d960ae94b060f56c2a8702545e2bb1abb935", GIT_OID_SHA1);
+ git_oid__fromstr(&new_id, "1111d4f11f4b35bf6759e0fb714fe09731ef0840", GIT_OID_SHA1);
+
+ cl_git_pass(git_blob_lookup(&old_blob, repo, &old_id));
+ cl_git_pass(git_blob_lookup(&new_blob, repo, &new_id));
+
+ cl_git_pass(git_diff_blobs(old_blob,
+ "untimely.txt", new_blob, "untimely.txt", &opts,
+ file_cb, binary_cb, hunk_cb, line_cb, &diff_data));
+
+ cl_assert_equal_s("untimely.txt", diff_data.old_path);
+ cl_assert_equal_oid(&old_id, &diff_data.old_id);
+ cl_assert_equal_i(GIT_DIFF_BINARY_DELTA, diff_data.old_binary_type);
+ cl_assert_equal_i(7, diff_data.old_binary_inflatedlen);
+ cl_assert_equal_s("c%18D`@*{63ljhg(E~C7",
+ diff_data.old_binary_base85.ptr);
+
+ cl_assert_equal_s("untimely.txt", diff_data.new_path);
+ cl_assert_equal_oid(&new_id, &diff_data.new_id);
+ cl_assert_equal_i(GIT_DIFF_BINARY_DELTA, diff_data.new_binary_type);
+ cl_assert_equal_i(32, diff_data.new_binary_inflatedlen);
+ cl_assert_equal_s("c%1vf+QYWt3zLL@hC)e3Vu?a>QDRl4f_G*?PG(-ZA}<#J$+QbW",
+ diff_data.new_binary_base85.ptr);
+
+ git_blob_free(old_blob);
+ git_blob_free(new_blob);
+
+ git__free(diff_data.old_path);
+ git__free(diff_data.new_path);
+
+ git_str_dispose(&diff_data.old_binary_base85);
+ git_str_dispose(&diff_data.new_binary_base85);
+}
diff --git a/tests/libgit2/diff/blob.c b/tests/libgit2/diff/blob.c
new file mode 100644
index 0000000..cb7e48b
--- /dev/null
+++ b/tests/libgit2/diff/blob.c
@@ -0,0 +1,1063 @@
+#include "clar_libgit2.h"
+#include "diff_helpers.h"
+
+#define BLOB_DIFF \
+ "diff --git a/file b/file\n" \
+ "index 45141a7..4d713dc 100644\n" \
+ "--- a/file\n" \
+ "+++ b/file\n" \
+ "@@ -1 +1,6 @@\n" \
+ " Hello from the root\n" \
+ "+\n" \
+ "+Some additional lines\n" \
+ "+\n" \
+ "+Down here below\n" \
+ "+\n"
+
+static git_repository *g_repo = NULL;
+static diff_expects expected;
+static git_diff_options opts;
+static git_blob *d, *alien;
+
+static void quick_diff_blob_to_str(
+ const git_blob *blob, const char *blob_path,
+ const char *str, size_t len, const char *str_path)
+{
+ memset(&expected, 0, sizeof(expected));
+
+ if (str && !len)
+ len = strlen(str);
+
+ cl_git_pass(git_diff_blob_to_buffer(
+ blob, blob_path, str, len, str_path,
+ &opts, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected));
+}
+
+void test_diff_blob__initialize(void)
+{
+ git_oid oid;
+
+ g_repo = cl_git_sandbox_init("attr");
+
+ cl_git_pass(git_diff_options_init(&opts, GIT_DIFF_OPTIONS_VERSION));
+ opts.context_lines = 1;
+
+ memset(&expected, 0, sizeof(expected));
+
+ /* tests/resources/attr/root_test4.txt */
+ cl_git_pass(git_oid__fromstrn(&oid, "a0f7217a", 8, GIT_OID_SHA1));
+ cl_git_pass(git_blob_lookup_prefix(&d, g_repo, &oid, 8));
+
+ /* alien.png */
+ cl_git_pass(git_oid__fromstrn(&oid, "edf3dcee", 8, GIT_OID_SHA1));
+ cl_git_pass(git_blob_lookup_prefix(&alien, g_repo, &oid, 8));
+}
+
+void test_diff_blob__cleanup(void)
+{
+ git_blob_free(d);
+ d = NULL;
+
+ git_blob_free(alien);
+ alien = NULL;
+
+ cl_git_sandbox_cleanup();
+}
+
+static void assert_one_modified(
+ int hunks, int lines, int ctxt, int adds, int dels, diff_expects *exp)
+{
+ cl_assert_equal_i(1, exp->files);
+ cl_assert_equal_i(1, exp->file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, exp->files_binary);
+
+ cl_assert_equal_i(hunks, exp->hunks);
+ cl_assert_equal_i(lines, exp->lines);
+ cl_assert_equal_i(ctxt, exp->line_ctxt);
+ cl_assert_equal_i(adds, exp->line_adds);
+ cl_assert_equal_i(dels, exp->line_dels);
+}
+
+void test_diff_blob__patch_with_freed_blobs(void)
+{
+ git_oid a_oid, b_oid;
+ git_blob *a, *b;
+ git_patch *p;
+ git_buf buf = GIT_BUF_INIT;
+
+ /* tests/resources/attr/root_test1 */
+ cl_git_pass(git_oid__fromstrn(&a_oid, "45141a79", 8, GIT_OID_SHA1));
+ cl_git_pass(git_blob_lookup_prefix(&a, g_repo, &a_oid, 4));
+ /* tests/resources/attr/root_test2 */
+ cl_git_pass(git_oid__fromstrn(&b_oid, "4d713dc4", 8, GIT_OID_SHA1));
+ cl_git_pass(git_blob_lookup_prefix(&b, g_repo, &b_oid, 4));
+
+ cl_git_pass(git_patch_from_blobs(&p, a, NULL, b, NULL, NULL));
+
+ git_blob_free(a);
+ git_blob_free(b);
+
+ cl_git_pass(git_patch_to_buf(&buf, p));
+ cl_assert_equal_s(buf.ptr, BLOB_DIFF);
+
+ git_patch_free(p);
+ git_buf_dispose(&buf);
+}
+
+void test_diff_blob__can_compare_text_blobs(void)
+{
+ git_blob *a, *b, *c;
+ git_oid a_oid, b_oid, c_oid;
+
+ /* tests/resources/attr/root_test1 */
+ cl_git_pass(git_oid__fromstrn(&a_oid, "45141a79", 8, GIT_OID_SHA1));
+ cl_git_pass(git_blob_lookup_prefix(&a, g_repo, &a_oid, 4));
+
+ /* tests/resources/attr/root_test2 */
+ cl_git_pass(git_oid__fromstrn(&b_oid, "4d713dc4", 8, GIT_OID_SHA1));
+ cl_git_pass(git_blob_lookup_prefix(&b, g_repo, &b_oid, 4));
+
+ /* tests/resources/attr/root_test3 */
+ cl_git_pass(git_oid__fromstrn(&c_oid, "c96bbb2c2557a832", 16, GIT_OID_SHA1));
+ cl_git_pass(git_blob_lookup_prefix(&c, g_repo, &c_oid, 16));
+
+ /* Doing the equivalent of a `git diff -U1` on these files */
+
+ /* diff on tests/resources/attr/root_test1 */
+ memset(&expected, 0, sizeof(expected));
+ cl_git_pass(git_diff_blobs(
+ a, NULL, b, NULL, &opts,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected));
+ assert_one_modified(1, 6, 1, 5, 0, &expected);
+
+ /* same diff but use direct buffers */
+ memset(&expected, 0, sizeof(expected));
+ cl_git_pass(git_diff_buffers(
+ git_blob_rawcontent(a), (size_t)git_blob_rawsize(a), NULL,
+ git_blob_rawcontent(b), (size_t)git_blob_rawsize(b), NULL, &opts,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected));
+ assert_one_modified(1, 6, 1, 5, 0, &expected);
+
+ /* diff on tests/resources/attr/root_test2 */
+ memset(&expected, 0, sizeof(expected));
+ cl_git_pass(git_diff_blobs(
+ b, NULL, c, NULL, &opts,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected));
+ assert_one_modified(1, 15, 3, 9, 3, &expected);
+
+ /* diff on tests/resources/attr/root_test3 */
+ memset(&expected, 0, sizeof(expected));
+ cl_git_pass(git_diff_blobs(
+ a, NULL, c, NULL, &opts,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected));
+ assert_one_modified(1, 13, 0, 12, 1, &expected);
+
+ memset(&expected, 0, sizeof(expected));
+ cl_git_pass(git_diff_blobs(
+ c, NULL, d, NULL, &opts,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected));
+ assert_one_modified(2, 14, 4, 6, 4, &expected);
+
+ git_blob_free(a);
+ git_blob_free(b);
+ git_blob_free(c);
+}
+
+static void assert_patch_matches_blobs(
+ git_patch *p, git_blob *a, git_blob *b,
+ int hunks, int l0, int l1, int ctxt, int adds, int dels)
+{
+ const git_diff_delta *delta;
+ size_t tc, ta, td;
+
+ cl_assert(p != NULL);
+
+ delta = git_patch_get_delta(p);
+ cl_assert(delta != NULL);
+
+ cl_assert_equal_i(GIT_DELTA_MODIFIED, delta->status);
+ cl_assert_equal_oid(git_blob_id(a), &delta->old_file.id);
+ cl_assert_equal_sz(git_blob_rawsize(a), delta->old_file.size);
+ cl_assert_equal_oid(git_blob_id(b), &delta->new_file.id);
+ cl_assert_equal_sz(git_blob_rawsize(b), delta->new_file.size);
+
+ cl_assert_equal_i(hunks, (int)git_patch_num_hunks(p));
+
+ if (hunks > 0)
+ cl_assert_equal_i(l0, git_patch_num_lines_in_hunk(p, 0));
+ if (hunks > 1)
+ cl_assert_equal_i(l1, git_patch_num_lines_in_hunk(p, 1));
+
+ cl_git_pass(git_patch_line_stats(&tc, &ta, &td, p));
+ cl_assert_equal_i(ctxt, (int)tc);
+ cl_assert_equal_i(adds, (int)ta);
+ cl_assert_equal_i(dels, (int)td);
+}
+
+void test_diff_blob__can_compare_text_blobs_with_patch(void)
+{
+ git_blob *a, *b, *c;
+ git_oid a_oid, b_oid, c_oid;
+ git_patch *p;
+
+ /* tests/resources/attr/root_test1 */
+ cl_git_pass(git_oid__fromstrn(&a_oid, "45141a79", 8, GIT_OID_SHA1));
+ cl_git_pass(git_blob_lookup_prefix(&a, g_repo, &a_oid, 8));
+
+ /* tests/resources/attr/root_test2 */
+ cl_git_pass(git_oid__fromstrn(&b_oid, "4d713dc4", 8, GIT_OID_SHA1));
+ cl_git_pass(git_blob_lookup_prefix(&b, g_repo, &b_oid, 8));
+
+ /* tests/resources/attr/root_test3 */
+ cl_git_pass(git_oid__fromstrn(&c_oid, "c96bbb2c2557a832", 16, GIT_OID_SHA1));
+ cl_git_pass(git_blob_lookup_prefix(&c, g_repo, &c_oid, 16));
+
+ /* Doing the equivalent of a `git diff -U1` on these files */
+
+ /* diff on tests/resources/attr/root_test1 */
+ cl_git_pass(git_patch_from_blobs(&p, a, NULL, b, NULL, &opts));
+ assert_patch_matches_blobs(p, a, b, 1, 6, 0, 1, 5, 0);
+ git_patch_free(p);
+
+ /* diff on tests/resources/attr/root_test2 */
+ cl_git_pass(git_patch_from_blobs(&p, b, NULL, c, NULL, &opts));
+ assert_patch_matches_blobs(p, b, c, 1, 15, 0, 3, 9, 3);
+ git_patch_free(p);
+
+ /* diff on tests/resources/attr/root_test3 */
+ cl_git_pass(git_patch_from_blobs(&p, a, NULL, c, NULL, &opts));
+ assert_patch_matches_blobs(p, a, c, 1, 13, 0, 0, 12, 1);
+ git_patch_free(p);
+
+ /* one more */
+ cl_git_pass(git_patch_from_blobs(&p, c, NULL, d, NULL, &opts));
+ assert_patch_matches_blobs(p, c, d, 2, 5, 9, 4, 6, 4);
+ git_patch_free(p);
+
+ git_blob_free(a);
+ git_blob_free(b);
+ git_blob_free(c);
+}
+
+void test_diff_blob__can_compare_against_null_blobs(void)
+{
+ git_blob *e = NULL;
+
+ cl_git_pass(git_diff_blobs(
+ d, NULL, e, NULL, &opts,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected));
+
+ cl_assert_equal_i(1, expected.files);
+ cl_assert_equal_i(1, expected.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(0, expected.files_binary);
+
+ cl_assert_equal_i(1, expected.hunks);
+ cl_assert_equal_i(14, expected.hunk_old_lines);
+ cl_assert_equal_i(14, expected.lines);
+ cl_assert_equal_i(14, expected.line_dels);
+
+ opts.flags |= GIT_DIFF_REVERSE;
+ memset(&expected, 0, sizeof(expected));
+
+ cl_git_pass(git_diff_blobs(
+ d, NULL, e, NULL, &opts,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected));
+
+ cl_assert_equal_i(1, expected.files);
+ cl_assert_equal_i(1, expected.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, expected.files_binary);
+
+ cl_assert_equal_i(1, expected.hunks);
+ cl_assert_equal_i(14, expected.hunk_new_lines);
+ cl_assert_equal_i(14, expected.lines);
+ cl_assert_equal_i(14, expected.line_adds);
+
+ opts.flags ^= GIT_DIFF_REVERSE;
+ memset(&expected, 0, sizeof(expected));
+
+ cl_git_pass(git_diff_blobs(
+ alien, NULL, NULL, NULL, &opts,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected));
+
+ cl_assert_equal_i(1, expected.files);
+ cl_assert_equal_i(1, expected.files_binary);
+ cl_assert_equal_i(1, expected.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(0, expected.hunks);
+ cl_assert_equal_i(0, expected.lines);
+
+ memset(&expected, 0, sizeof(expected));
+
+ cl_git_pass(git_diff_blobs(
+ NULL, NULL, alien, NULL, &opts,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected));
+
+ cl_assert_equal_i(1, expected.files);
+ cl_assert_equal_i(1, expected.files_binary);
+ cl_assert_equal_i(1, expected.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, expected.hunks);
+ cl_assert_equal_i(0, expected.lines);
+}
+
+void test_diff_blob__can_compare_against_null_blobs_with_patch(void)
+{
+ git_blob *e = NULL;
+ git_patch *p;
+ const git_diff_delta *delta;
+ const git_diff_line *line;
+ int l, max_l;
+
+ cl_git_pass(git_patch_from_blobs(&p, d, NULL, e, NULL, &opts));
+
+ cl_assert(p != NULL);
+
+ delta = git_patch_get_delta(p);
+ cl_assert(delta != NULL);
+ cl_assert_equal_i(GIT_DELTA_DELETED, delta->status);
+ cl_assert_equal_oid(git_blob_id(d), &delta->old_file.id);
+ cl_assert_equal_sz(git_blob_rawsize(d), delta->old_file.size);
+ cl_assert(git_oid_is_zero(&delta->new_file.id));
+ cl_assert_equal_sz(0, delta->new_file.size);
+
+ cl_assert_equal_i(1, (int)git_patch_num_hunks(p));
+ cl_assert_equal_i(14, git_patch_num_lines_in_hunk(p, 0));
+
+ max_l = git_patch_num_lines_in_hunk(p, 0);
+ for (l = 0; l < max_l; ++l) {
+ cl_git_pass(git_patch_get_line_in_hunk(&line, p, 0, l));
+ cl_assert_equal_i(GIT_DIFF_LINE_DELETION, (int)line->origin);
+ }
+
+ git_patch_free(p);
+
+ opts.flags |= GIT_DIFF_REVERSE;
+
+ cl_git_pass(git_patch_from_blobs(&p, d, NULL, e, NULL, &opts));
+
+ cl_assert(p != NULL);
+
+ delta = git_patch_get_delta(p);
+ cl_assert(delta != NULL);
+ cl_assert_equal_i(GIT_DELTA_ADDED, delta->status);
+ cl_assert(git_oid_is_zero(&delta->old_file.id));
+ cl_assert_equal_sz(0, delta->old_file.size);
+ cl_assert_equal_oid(git_blob_id(d), &delta->new_file.id);
+ cl_assert_equal_sz(git_blob_rawsize(d), delta->new_file.size);
+
+ cl_assert_equal_i(1, (int)git_patch_num_hunks(p));
+ cl_assert_equal_i(14, git_patch_num_lines_in_hunk(p, 0));
+
+ max_l = git_patch_num_lines_in_hunk(p, 0);
+ for (l = 0; l < max_l; ++l) {
+ cl_git_pass(git_patch_get_line_in_hunk(&line, p, 0, l));
+ cl_assert_equal_i(GIT_DIFF_LINE_ADDITION, (int)line->origin);
+ }
+
+ git_patch_free(p);
+
+ opts.flags ^= GIT_DIFF_REVERSE;
+
+ cl_git_pass(git_patch_from_blobs(&p, alien, NULL, NULL, NULL, &opts));
+
+ cl_assert(p != NULL);
+
+ delta = git_patch_get_delta(p);
+ cl_assert(delta != NULL);
+ cl_assert_equal_i(GIT_DELTA_DELETED, delta->status);
+ cl_assert((delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
+
+ cl_assert_equal_i(0, (int)git_patch_num_hunks(p));
+
+ git_patch_free(p);
+
+ cl_git_pass(git_patch_from_blobs(&p, NULL, NULL, alien, NULL, &opts));
+
+ cl_assert(p != NULL);
+
+ delta = git_patch_get_delta(p);
+ cl_assert(delta != NULL);
+ cl_assert_equal_i(GIT_DELTA_ADDED, delta->status);
+ cl_assert((delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
+
+ cl_assert_equal_i(0, (int)git_patch_num_hunks(p));
+
+ git_patch_free(p);
+}
+
+static void assert_identical_blobs_comparison(diff_expects *expected)
+{
+ cl_assert_equal_i(1, expected->files);
+ cl_assert_equal_i(1, expected->file_status[GIT_DELTA_UNMODIFIED]);
+ cl_assert_equal_i(0, expected->hunks);
+ cl_assert_equal_i(0, expected->lines);
+}
+
+void test_diff_blob__can_compare_identical_blobs(void)
+{
+ opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
+
+ cl_git_pass(git_diff_blobs(
+ d, NULL, d, NULL, &opts,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected));
+
+ assert_identical_blobs_comparison(&expected);
+ cl_assert_equal_i(0, expected.files_binary);
+
+ memset(&expected, 0, sizeof(expected));
+ cl_git_pass(git_diff_blobs(
+ NULL, NULL, NULL, NULL, &opts,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected));
+
+ assert_identical_blobs_comparison(&expected);
+ cl_assert_equal_i(0, expected.files_binary);
+
+ memset(&expected, 0, sizeof(expected));
+ cl_git_pass(git_diff_blobs(
+ alien, NULL, alien, NULL, &opts,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected));
+
+ assert_identical_blobs_comparison(&expected);
+ cl_assert(expected.files_binary > 0);
+}
+
+void test_diff_blob__can_compare_identical_blobs_with_patch(void)
+{
+ git_patch *p;
+ const git_diff_delta *delta;
+
+ cl_git_pass(git_patch_from_blobs(&p, d, NULL, d, NULL, &opts));
+ cl_assert(p != NULL);
+
+ delta = git_patch_get_delta(p);
+ cl_assert(delta != NULL);
+ cl_assert_equal_i(GIT_DELTA_UNMODIFIED, delta->status);
+ cl_assert_equal_sz(delta->old_file.size, git_blob_rawsize(d));
+ cl_assert_equal_oid(git_blob_id(d), &delta->old_file.id);
+ cl_assert_equal_sz(delta->new_file.size, git_blob_rawsize(d));
+ cl_assert_equal_oid(git_blob_id(d), &delta->new_file.id);
+
+ cl_assert_equal_i(0, (int)git_patch_num_hunks(p));
+ git_patch_free(p);
+
+ cl_git_pass(git_patch_from_blobs(&p, NULL, NULL, NULL, NULL, &opts));
+ cl_assert(p != NULL);
+
+ delta = git_patch_get_delta(p);
+ cl_assert(delta != NULL);
+ cl_assert_equal_i(GIT_DELTA_UNMODIFIED, delta->status);
+ cl_assert_equal_sz(0, delta->old_file.size);
+ cl_assert(git_oid_is_zero(&delta->old_file.id));
+ cl_assert_equal_sz(0, delta->new_file.size);
+ cl_assert(git_oid_is_zero(&delta->new_file.id));
+
+ cl_assert_equal_i(0, (int)git_patch_num_hunks(p));
+ git_patch_free(p);
+
+ cl_git_pass(git_patch_from_blobs(&p, alien, NULL, alien, NULL, &opts));
+ cl_assert(p != NULL);
+ cl_assert_equal_i(GIT_DELTA_UNMODIFIED, git_patch_get_delta(p)->status);
+ cl_assert_equal_i(0, (int)git_patch_num_hunks(p));
+ git_patch_free(p);
+}
+
+static void assert_binary_blobs_comparison(diff_expects *expected)
+{
+ cl_assert(expected->files_binary > 0);
+
+ cl_assert_equal_i(1, expected->files);
+ cl_assert_equal_i(1, expected->file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, expected->hunks);
+ cl_assert_equal_i(0, expected->lines);
+}
+
+void test_diff_blob__can_compare_two_binary_blobs(void)
+{
+ git_blob *heart;
+ git_oid h_oid;
+
+ /* heart.png */
+ cl_git_pass(git_oid__fromstrn(&h_oid, "de863bff", 8, GIT_OID_SHA1));
+ cl_git_pass(git_blob_lookup_prefix(&heart, g_repo, &h_oid, 8));
+
+ cl_git_pass(git_diff_blobs(
+ alien, NULL, heart, NULL, &opts,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected));
+
+ assert_binary_blobs_comparison(&expected);
+
+ memset(&expected, 0, sizeof(expected));
+
+ cl_git_pass(git_diff_blobs(
+ heart, NULL, alien, NULL, &opts,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected));
+
+ assert_binary_blobs_comparison(&expected);
+
+ git_blob_free(heart);
+}
+
+void test_diff_blob__can_compare_a_binary_blob_and_a_text_blob(void)
+{
+ cl_git_pass(git_diff_blobs(
+ alien, NULL, d, NULL, &opts,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected));
+
+ assert_binary_blobs_comparison(&expected);
+
+ memset(&expected, 0, sizeof(expected));
+
+ cl_git_pass(git_diff_blobs(
+ d, NULL, alien, NULL, &opts,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected));
+
+ assert_binary_blobs_comparison(&expected);
+}
+
+/*
+ * $ git diff fe773770 a0f7217
+ * diff --git a/fe773770 b/a0f7217
+ * index fe77377..a0f7217 100644
+ * --- a/fe773770
+ * +++ b/a0f7217
+ * @@ -1,6 +1,6 @@
+ * Here is some stuff at the start
+ *
+ * -This should go in one hunk
+ * +This should go in one hunk (first)
+ *
+ * Some additional lines
+ *
+ * @@ -8,7 +8,7 @@ Down here below the other lines
+ *
+ * With even more at the end
+ *
+ * -Followed by a second hunk of stuff
+ * +Followed by a second hunk of stuff (second)
+ *
+ * That happens down here
+ */
+void test_diff_blob__comparing_two_text_blobs_honors_interhunkcontext(void)
+{
+ git_blob *old_d;
+ git_oid old_d_oid;
+
+ opts.context_lines = 3;
+
+ /* tests/resources/attr/root_test1 from commit f5b0af1 */
+ cl_git_pass(git_oid__fromstrn(&old_d_oid, "fe773770", 8, GIT_OID_SHA1));
+ cl_git_pass(git_blob_lookup_prefix(&old_d, g_repo, &old_d_oid, 8));
+
+ /* Test with default inter-hunk-context (not set) => default is 0 */
+ cl_git_pass(git_diff_blobs(
+ old_d, NULL, d, NULL, &opts,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected));
+
+ cl_assert_equal_i(2, expected.hunks);
+
+ /* Test with inter-hunk-context explicitly set to 0 */
+ opts.interhunk_lines = 0;
+ memset(&expected, 0, sizeof(expected));
+ cl_git_pass(git_diff_blobs(
+ old_d, NULL, d, NULL, &opts,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected));
+
+ cl_assert_equal_i(2, expected.hunks);
+
+ /* Test with inter-hunk-context explicitly set to 1 */
+ opts.interhunk_lines = 1;
+ memset(&expected, 0, sizeof(expected));
+ cl_git_pass(git_diff_blobs(
+ old_d, NULL, d, NULL, &opts,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected));
+
+ cl_assert_equal_i(1, expected.hunks);
+
+ git_blob_free(old_d);
+}
+
+void test_diff_blob__checks_options_version_too_low(void)
+{
+ const git_error *err;
+
+ opts.version = 0;
+ cl_git_fail(git_diff_blobs(
+ d, NULL, alien, NULL, &opts,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected));
+ err = git_error_last();
+ cl_assert_equal_i(GIT_ERROR_INVALID, err->klass);
+}
+
+void test_diff_blob__checks_options_version_too_high(void)
+{
+ const git_error *err;
+
+ opts.version = 1024;
+ cl_git_fail(git_diff_blobs(
+ d, NULL, alien, NULL, &opts,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected));
+ err = git_error_last();
+ cl_assert_equal_i(GIT_ERROR_INVALID, err->klass);
+}
+
+void test_diff_blob__can_correctly_detect_a_binary_blob_as_binary(void)
+{
+ /* alien.png */
+ cl_assert_equal_i(true, git_blob_is_binary(alien));
+}
+
+void test_diff_blob__can_correctly_detect_binary_blob_data_as_binary(void)
+{
+ /* alien.png */
+ const char *content = git_blob_rawcontent(alien);
+ size_t len = (size_t)git_blob_rawsize(alien);
+ cl_assert_equal_i(true, git_blob_data_is_binary(content, len));
+}
+
+void test_diff_blob__can_correctly_detect_a_textual_blob_as_non_binary(void)
+{
+ /* tests/resources/attr/root_test4.txt */
+ cl_assert_equal_i(false, git_blob_is_binary(d));
+}
+
+void test_diff_blob__can_correctly_detect_textual_blob_data_as_non_binary(void)
+{
+ /* tests/resources/attr/root_test4.txt */
+ const char *content = git_blob_rawcontent(d);
+ size_t len = (size_t)git_blob_rawsize(d);
+ cl_assert_equal_i(false, git_blob_data_is_binary(content, len));
+}
+
+/*
+ * git_diff_blob_to_buffer tests
+ */
+
+static void assert_changed_single_one_line_file(
+ diff_expects *expected, git_delta_t mod)
+{
+ cl_assert_equal_i(1, expected->files);
+ cl_assert_equal_i(1, expected->file_status[mod]);
+ cl_assert_equal_i(1, expected->hunks);
+ cl_assert_equal_i(1, expected->lines);
+
+ if (mod == GIT_DELTA_ADDED)
+ cl_assert_equal_i(1, expected->line_adds);
+ else if (mod == GIT_DELTA_DELETED)
+ cl_assert_equal_i(1, expected->line_dels);
+}
+
+void test_diff_blob__can_compare_blob_to_buffer(void)
+{
+ git_blob *a;
+ git_oid a_oid;
+ const char *a_content = "Hello from the root\n";
+ const char *b_content = "Hello from the root\n\nSome additional lines\n\nDown here below\n\n";
+
+ /* tests/resources/attr/root_test1 */
+ cl_git_pass(git_oid__fromstrn(&a_oid, "45141a79", 8, GIT_OID_SHA1));
+ cl_git_pass(git_blob_lookup_prefix(&a, g_repo, &a_oid, 8));
+
+ /* diff from blob a to content of b */
+ quick_diff_blob_to_str(a, NULL, b_content, 0, NULL);
+ assert_one_modified(1, 6, 1, 5, 0, &expected);
+
+ /* diff from blob a to content of a */
+ opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
+ quick_diff_blob_to_str(a, NULL, a_content, 0, NULL);
+ assert_identical_blobs_comparison(&expected);
+
+ /* diff from NULL blob to content of a */
+ memset(&expected, 0, sizeof(expected));
+ quick_diff_blob_to_str(NULL, NULL, a_content, 0, NULL);
+ assert_changed_single_one_line_file(&expected, GIT_DELTA_ADDED);
+
+ /* diff from blob a to NULL buffer */
+ memset(&expected, 0, sizeof(expected));
+ quick_diff_blob_to_str(a, NULL, NULL, 0, NULL);
+ assert_changed_single_one_line_file(&expected, GIT_DELTA_DELETED);
+
+ /* diff with reverse */
+ opts.flags ^= GIT_DIFF_REVERSE;
+
+ memset(&expected, 0, sizeof(expected));
+ quick_diff_blob_to_str(a, NULL, NULL, 0, NULL);
+ assert_changed_single_one_line_file(&expected, GIT_DELTA_ADDED);
+
+ git_blob_free(a);
+}
+
+void test_diff_blob__can_compare_blob_to_buffer_with_patch(void)
+{
+ git_patch *p;
+ git_blob *a;
+ git_oid a_oid;
+ const char *a_content = "Hello from the root\n";
+ const char *b_content = "Hello from the root\n\nSome additional lines\n\nDown here below\n\n";
+ size_t tc, ta, td;
+
+ /* tests/resources/attr/root_test1 */
+ cl_git_pass(git_oid__fromstrn(&a_oid, "45141a79", 8, GIT_OID_SHA1));
+ cl_git_pass(git_blob_lookup_prefix(&a, g_repo, &a_oid, 8));
+
+ /* diff from blob a to content of b */
+ cl_git_pass(git_patch_from_blob_and_buffer(
+ &p, a, NULL, b_content, strlen(b_content), NULL, &opts));
+
+ cl_assert(p != NULL);
+ cl_assert_equal_i(GIT_DELTA_MODIFIED, git_patch_get_delta(p)->status);
+ cl_assert_equal_i(1, (int)git_patch_num_hunks(p));
+ cl_assert_equal_i(6, git_patch_num_lines_in_hunk(p, 0));
+
+ cl_git_pass(git_patch_line_stats(&tc, &ta, &td, p));
+ cl_assert_equal_i(1, (int)tc);
+ cl_assert_equal_i(5, (int)ta);
+ cl_assert_equal_i(0, (int)td);
+
+ git_patch_free(p);
+
+ /* diff from blob a to content of a */
+ opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
+ cl_git_pass(git_patch_from_blob_and_buffer(
+ &p, a, NULL, a_content, strlen(a_content), NULL, &opts));
+ cl_assert(p != NULL);
+ cl_assert_equal_i(GIT_DELTA_UNMODIFIED, git_patch_get_delta(p)->status);
+ cl_assert_equal_i(0, (int)git_patch_num_hunks(p));
+ git_patch_free(p);
+
+ /* diff from NULL blob to content of a */
+ cl_git_pass(git_patch_from_blob_and_buffer(
+ &p, NULL, NULL, a_content, strlen(a_content), NULL, &opts));
+ cl_assert(p != NULL);
+ cl_assert_equal_i(GIT_DELTA_ADDED, git_patch_get_delta(p)->status);
+ cl_assert_equal_i(1, (int)git_patch_num_hunks(p));
+ cl_assert_equal_i(1, git_patch_num_lines_in_hunk(p, 0));
+ git_patch_free(p);
+
+ /* diff from blob a to NULL buffer */
+ cl_git_pass(git_patch_from_blob_and_buffer(
+ &p, a, NULL, NULL, 0, NULL, &opts));
+ cl_assert(p != NULL);
+ cl_assert_equal_i(GIT_DELTA_DELETED, git_patch_get_delta(p)->status);
+ cl_assert_equal_i(1, (int)git_patch_num_hunks(p));
+ cl_assert_equal_i(1, git_patch_num_lines_in_hunk(p, 0));
+ git_patch_free(p);
+
+ /* diff with reverse */
+ opts.flags ^= GIT_DIFF_REVERSE;
+
+ cl_git_pass(git_patch_from_blob_and_buffer(
+ &p, a, NULL, NULL, 0, NULL, &opts));
+ cl_assert(p != NULL);
+ cl_assert_equal_i(GIT_DELTA_ADDED, git_patch_get_delta(p)->status);
+ cl_assert_equal_i(1, (int)git_patch_num_hunks(p));
+ cl_assert_equal_i(1, git_patch_num_lines_in_hunk(p, 0));
+ git_patch_free(p);
+
+ git_blob_free(a);
+}
+
+static void assert_one_modified_with_lines(diff_expects *expected, int lines)
+{
+ cl_assert_equal_i(1, expected->files);
+ cl_assert_equal_i(1, expected->file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, expected->files_binary);
+ cl_assert_equal_i(lines, expected->lines);
+}
+
+void test_diff_blob__binary_data_comparisons(void)
+{
+ git_blob *bin, *nonbin;
+ git_oid oid;
+ const char *nonbin_content = "Hello from the root\n";
+ size_t nonbin_len = 20;
+ const char *bin_content = "0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00\n0123456789\n";
+ size_t bin_len = 33;
+
+ opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
+
+ cl_git_pass(git_oid__fromstrn(&oid, "45141a79", 8, GIT_OID_SHA1));
+ cl_git_pass(git_blob_lookup_prefix(&nonbin, g_repo, &oid, 8));
+
+ cl_git_pass(git_oid__fromstrn(&oid, "b435cd56", 8, GIT_OID_SHA1));
+ cl_git_pass(git_blob_lookup_prefix(&bin, g_repo, &oid, 8));
+
+ /* non-binary to reference content */
+
+ quick_diff_blob_to_str(nonbin, NULL, nonbin_content, nonbin_len, NULL);
+ assert_identical_blobs_comparison(&expected);
+ cl_assert_equal_i(0, expected.files_binary);
+
+ /* binary to reference content */
+
+ quick_diff_blob_to_str(bin, NULL, bin_content, bin_len, NULL);
+ assert_identical_blobs_comparison(&expected);
+
+ cl_assert_equal_i(1, expected.files_binary);
+
+ /* non-binary to binary content */
+
+ quick_diff_blob_to_str(nonbin, NULL, bin_content, bin_len, NULL);
+ assert_binary_blobs_comparison(&expected);
+
+ /* binary to non-binary content */
+
+ quick_diff_blob_to_str(bin, NULL, nonbin_content, nonbin_len, NULL);
+ assert_binary_blobs_comparison(&expected);
+
+ /* non-binary to binary blob */
+
+ memset(&expected, 0, sizeof(expected));
+ cl_git_pass(git_diff_blobs(
+ bin, NULL, nonbin, NULL, &opts,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected));
+ assert_binary_blobs_comparison(&expected);
+
+ /*
+ * repeat with FORCE_TEXT
+ */
+
+ opts.flags |= GIT_DIFF_FORCE_TEXT;
+
+ quick_diff_blob_to_str(bin, NULL, bin_content, bin_len, NULL);
+ assert_identical_blobs_comparison(&expected);
+
+ quick_diff_blob_to_str(nonbin, NULL, bin_content, bin_len, NULL);
+ assert_one_modified_with_lines(&expected, 4);
+
+ quick_diff_blob_to_str(bin, NULL, nonbin_content, nonbin_len, NULL);
+ assert_one_modified_with_lines(&expected, 4);
+
+ memset(&expected, 0, sizeof(expected));
+ cl_git_pass(git_diff_blobs(
+ bin, NULL, nonbin, NULL, &opts,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected));
+ assert_one_modified_with_lines(&expected, 4);
+
+ /* cleanup */
+ git_blob_free(bin);
+ git_blob_free(nonbin);
+}
+
+void test_diff_blob__using_path_and_attributes(void)
+{
+ git_config *cfg;
+ git_blob *bin, *nonbin;
+ git_oid oid;
+ const char *nonbin_content = "Hello from the root\n";
+ const char *bin_content =
+ "0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00\n0123456789\n";
+ size_t bin_len = 33;
+ const char *changed;
+ git_patch *p;
+ git_buf buf = GIT_BUF_INIT;
+
+ /* set up custom diff drivers and 'diff' attribute mappings for them */
+
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_git_pass(git_config_set_bool(cfg, "diff.iam_binary.binary", 1));
+ cl_git_pass(git_config_set_bool(cfg, "diff.iam_text.binary", 0));
+ cl_git_pass(git_config_set_string(
+ cfg, "diff.iam_alphactx.xfuncname", "^[A-Za-z].*$"));
+ cl_git_pass(git_config_set_bool(cfg, "diff.iam_textalpha.binary", 0));
+ cl_git_pass(git_config_set_string(
+ cfg, "diff.iam_textalpha.xfuncname", "^[A-Za-z].*$"));
+ cl_git_pass(git_config_set_string(
+ cfg, "diff.iam_numctx.funcname", "^[0-9][0-9]*"));
+ cl_git_pass(git_config_set_bool(cfg, "diff.iam_textnum.binary", 0));
+ cl_git_pass(git_config_set_string(
+ cfg, "diff.iam_textnum.funcname", "^[0-9][0-9]*"));
+ git_config_free(cfg);
+
+ cl_git_append2file(
+ "attr/.gitattributes",
+ "\n\n# test_diff_blob__using_path_and_attributes extra\n\n"
+ "*.binary diff=iam_binary\n"
+ "*.textary diff=iam_text\n"
+ "*.alphary diff=iam_alphactx\n"
+ "*.textalphary diff=iam_textalpha\n"
+ "*.textnumary diff=iam_textnum\n"
+ "*.numary diff=iam_numctx\n\n");
+
+ opts.context_lines = 0;
+ opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
+
+ cl_git_pass(git_oid__fromstrn(&oid, "45141a79", 8, GIT_OID_SHA1));
+ cl_git_pass(git_blob_lookup_prefix(&nonbin, g_repo, &oid, 8));
+ /* 20b: "Hello from the root\n" */
+
+ cl_git_pass(git_oid__fromstrn(&oid, "b435cd56", 8, GIT_OID_SHA1));
+ cl_git_pass(git_blob_lookup_prefix(&bin, g_repo, &oid, 8));
+ /* 33b: "0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\n0123456789\n" */
+
+ /* non-binary to reference content */
+
+ quick_diff_blob_to_str(nonbin, NULL, nonbin_content, 0, NULL);
+ assert_identical_blobs_comparison(&expected);
+ cl_assert_equal_i(0, expected.files_binary);
+
+ /* binary to reference content */
+
+ quick_diff_blob_to_str(bin, NULL, bin_content, bin_len, NULL);
+ assert_identical_blobs_comparison(&expected);
+ cl_assert_equal_i(1, expected.files_binary);
+
+ /* add some text */
+
+ changed = "Hello from the root\nMore lines\nAnd more\nGo here\n";
+
+ quick_diff_blob_to_str(nonbin, NULL, changed, 0, NULL);
+ assert_one_modified(1, 3, 0, 3, 0, &expected);
+
+ quick_diff_blob_to_str(nonbin, "foo/bar.binary", changed, 0, NULL);
+ cl_assert_equal_i(1, expected.files);
+ cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, expected.files_binary);
+ cl_assert_equal_i(0, expected.hunks);
+ cl_assert_equal_i(0, expected.lines);
+
+ quick_diff_blob_to_str(nonbin, "foo/bar.textary", changed, 0, NULL);
+ assert_one_modified(1, 3, 0, 3, 0, &expected);
+
+ quick_diff_blob_to_str(nonbin, "foo/bar.alphary", changed, 0, NULL);
+ assert_one_modified(1, 3, 0, 3, 0, &expected);
+
+ cl_git_pass(git_patch_from_blob_and_buffer(
+ &p, nonbin, "zzz.normal", changed, strlen(changed), NULL, &opts));
+ cl_git_pass(git_patch_to_buf(&buf, p));
+ cl_assert_equal_s(
+ "diff --git a/zzz.normal b/zzz.normal\n"
+ "index 45141a7..75b0dbb 100644\n"
+ "--- a/zzz.normal\n"
+ "+++ b/zzz.normal\n"
+ "@@ -1,0 +2,3 @@ Hello from the root\n"
+ "+More lines\n"
+ "+And more\n"
+ "+Go here\n", buf.ptr);
+ git_buf_dispose(&buf);
+ git_patch_free(p);
+
+ cl_git_pass(git_patch_from_blob_and_buffer(
+ &p, nonbin, "zzz.binary", changed, strlen(changed), NULL, &opts));
+ cl_git_pass(git_patch_to_buf(&buf, p));
+ cl_assert_equal_s(
+ "diff --git a/zzz.binary b/zzz.binary\n"
+ "index 45141a7..75b0dbb 100644\n"
+ "Binary files a/zzz.binary and b/zzz.binary differ\n", buf.ptr);
+ git_buf_dispose(&buf);
+ git_patch_free(p);
+
+ cl_git_pass(git_patch_from_blob_and_buffer(
+ &p, nonbin, "zzz.alphary", changed, strlen(changed), NULL, &opts));
+ cl_git_pass(git_patch_to_buf(&buf, p));
+ cl_assert_equal_s(
+ "diff --git a/zzz.alphary b/zzz.alphary\n"
+ "index 45141a7..75b0dbb 100644\n"
+ "--- a/zzz.alphary\n"
+ "+++ b/zzz.alphary\n"
+ "@@ -1,0 +2,3 @@ Hello from the root\n"
+ "+More lines\n"
+ "+And more\n"
+ "+Go here\n", buf.ptr);
+ git_buf_dispose(&buf);
+ git_patch_free(p);
+
+ cl_git_pass(git_patch_from_blob_and_buffer(
+ &p, nonbin, "zzz.numary", changed, strlen(changed), NULL, &opts));
+ cl_git_pass(git_patch_to_buf(&buf, p));
+ cl_assert_equal_s(
+ "diff --git a/zzz.numary b/zzz.numary\n"
+ "index 45141a7..75b0dbb 100644\n"
+ "--- a/zzz.numary\n"
+ "+++ b/zzz.numary\n"
+ "@@ -1,0 +2,3 @@\n"
+ "+More lines\n"
+ "+And more\n"
+ "+Go here\n", buf.ptr);
+ git_buf_dispose(&buf);
+ git_patch_free(p);
+
+ /* "0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00\n0123456789\n"
+ * 33 bytes
+ */
+
+ changed = "0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00\nreplace a line\n";
+
+ cl_git_pass(git_patch_from_blob_and_buffer(
+ &p, bin, "zzz.normal", changed, 37, NULL, &opts));
+ cl_git_pass(git_patch_to_buf(&buf, p));
+ cl_assert_equal_s(
+ "diff --git a/zzz.normal b/zzz.normal\n"
+ "index b435cd5..1604519 100644\n"
+ "Binary files a/zzz.normal and b/zzz.normal differ\n", buf.ptr);
+ git_buf_dispose(&buf);
+ git_patch_free(p);
+
+ cl_git_pass(git_patch_from_blob_and_buffer(
+ &p, bin, "zzz.textary", changed, 37, NULL, &opts));
+ cl_git_pass(git_patch_to_buf(&buf, p));
+ cl_assert_equal_s(
+ "diff --git a/zzz.textary b/zzz.textary\n"
+ "index b435cd5..1604519 100644\n"
+ "--- a/zzz.textary\n"
+ "+++ b/zzz.textary\n"
+ "@@ -3 +3 @@\n"
+ "-0123456789\n"
+ "+replace a line\n", buf.ptr);
+ git_buf_dispose(&buf);
+ git_patch_free(p);
+
+ cl_git_pass(git_patch_from_blob_and_buffer(
+ &p, bin, "zzz.textalphary", changed, 37, NULL, &opts));
+ cl_git_pass(git_patch_to_buf(&buf, p));
+ cl_assert_equal_s(
+ "diff --git a/zzz.textalphary b/zzz.textalphary\n"
+ "index b435cd5..1604519 100644\n"
+ "--- a/zzz.textalphary\n"
+ "+++ b/zzz.textalphary\n"
+ "@@ -3 +3 @@\n"
+ "-0123456789\n"
+ "+replace a line\n", buf.ptr);
+ git_buf_dispose(&buf);
+ git_patch_free(p);
+
+ cl_git_pass(git_patch_from_blob_and_buffer(
+ &p, bin, "zzz.textnumary", changed, 37, NULL, &opts));
+ cl_git_pass(git_patch_to_buf(&buf, p));
+ cl_assert_equal_s(
+ "diff --git a/zzz.textnumary b/zzz.textnumary\n"
+ "index b435cd5..1604519 100644\n"
+ "--- a/zzz.textnumary\n"
+ "+++ b/zzz.textnumary\n"
+ "@@ -3 +3 @@ 0123456789\n"
+ "-0123456789\n"
+ "+replace a line\n", buf.ptr);
+ git_buf_dispose(&buf);
+ git_patch_free(p);
+
+ git_buf_dispose(&buf);
+ git_blob_free(nonbin);
+ git_blob_free(bin);
+}
+
+void test_diff_blob__can_compare_buffer_to_buffer(void)
+{
+ const char *a = "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\n";
+ const char *b = "a\nB\nc\nd\nE\nF\nh\nj\nk\n";
+
+ opts.interhunk_lines = 0;
+ opts.context_lines = 0;
+
+ memset(&expected, 0, sizeof(expected));
+
+ cl_git_pass(git_diff_buffers(
+ a, strlen(a), NULL, b, strlen(b), NULL, &opts,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected));
+ assert_one_modified(4, 9, 0, 4, 5, &expected);
+
+ opts.flags ^= GIT_DIFF_REVERSE;
+
+ memset(&expected, 0, sizeof(expected));
+
+ cl_git_pass(git_diff_buffers(
+ a, strlen(a), NULL, b, strlen(b), NULL, &opts,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expected));
+ assert_one_modified(4, 9, 0, 5, 4, &expected);
+}
diff --git a/tests/libgit2/diff/diff_helpers.c b/tests/libgit2/diff/diff_helpers.c
new file mode 100644
index 0000000..5daebff
--- /dev/null
+++ b/tests/libgit2/diff/diff_helpers.c
@@ -0,0 +1,333 @@
+#include "clar_libgit2.h"
+#include "diff_helpers.h"
+#include "git2/sys/diff.h"
+
+git_tree *resolve_commit_oid_to_tree(
+ git_repository *repo,
+ const char *partial_oid)
+{
+ size_t len = strlen(partial_oid);
+ git_oid oid;
+ git_object *obj = NULL;
+ git_tree *tree = NULL;
+
+ if (git_oid__fromstrn(&oid, partial_oid, len, GIT_OID_SHA1) == 0)
+ cl_git_pass(git_object_lookup_prefix(&obj, repo, &oid, len, GIT_OBJECT_ANY));
+
+ cl_git_pass(git_object_peel((git_object **) &tree, obj, GIT_OBJECT_TREE));
+ git_object_free(obj);
+ return tree;
+}
+
+static char diff_pick_suffix(int mode)
+{
+ if (S_ISDIR(mode))
+ return '/';
+ else if (GIT_PERMS_IS_EXEC(mode))
+ return '*';
+ else
+ return ' ';
+}
+
+static void fprintf_delta(FILE *fp, const git_diff_delta *delta, float progress)
+{
+ char code = git_diff_status_char(delta->status);
+ char old_suffix = diff_pick_suffix(delta->old_file.mode);
+ char new_suffix = diff_pick_suffix(delta->new_file.mode);
+
+ fprintf(fp, "%c\t%s", code, delta->old_file.path);
+
+ if ((delta->old_file.path != delta->new_file.path &&
+ strcmp(delta->old_file.path, delta->new_file.path) != 0) ||
+ (delta->old_file.mode != delta->new_file.mode &&
+ delta->old_file.mode != 0 && delta->new_file.mode != 0))
+ fprintf(fp, "%c %s%c", old_suffix, delta->new_file.path, new_suffix);
+ else if (old_suffix != ' ')
+ fprintf(fp, "%c", old_suffix);
+
+ fprintf(fp, "\t[%.2f]\n", progress);
+}
+
+int diff_file_cb(
+ const git_diff_delta *delta,
+ float progress,
+ void *payload)
+{
+ diff_expects *e = payload;
+
+ if (e->debug)
+ fprintf_delta(stderr, delta, progress);
+
+ if (e->names)
+ cl_assert_equal_s(e->names[e->files], delta->old_file.path);
+ if (e->statuses)
+ cl_assert_equal_i(e->statuses[e->files], (int)delta->status);
+
+ e->files++;
+
+ if ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0)
+ e->files_binary++;
+
+ cl_assert(delta->status <= GIT_DELTA_CONFLICTED);
+
+ e->file_status[delta->status] += 1;
+
+ return 0;
+}
+
+int diff_print_file_cb(
+ const git_diff_delta *delta,
+ float progress,
+ void *payload)
+{
+ if (!payload) {
+ fprintf_delta(stderr, delta, progress);
+ return 0;
+ }
+
+ if (!((diff_expects *)payload)->debug)
+ fprintf_delta(stderr, delta, progress);
+
+ return diff_file_cb(delta, progress, payload);
+}
+
+int diff_binary_cb(
+ const git_diff_delta *delta,
+ const git_diff_binary *binary,
+ void *payload)
+{
+ GIT_UNUSED(delta);
+ GIT_UNUSED(binary);
+ GIT_UNUSED(payload);
+
+ return 0;
+}
+
+int diff_hunk_cb(
+ const git_diff_delta *delta,
+ const git_diff_hunk *hunk,
+ void *payload)
+{
+ diff_expects *e = payload;
+ const char *scan = hunk->header, *scan_end = scan + hunk->header_len;
+
+ GIT_UNUSED(delta);
+
+ /* confirm no NUL bytes in header text */
+ while (scan < scan_end)
+ cl_assert('\0' != *scan++);
+
+ e->hunks++;
+ e->hunk_old_lines += hunk->old_lines;
+ e->hunk_new_lines += hunk->new_lines;
+ return 0;
+}
+
+int diff_line_cb(
+ const git_diff_delta *delta,
+ const git_diff_hunk *hunk,
+ const git_diff_line *line,
+ void *payload)
+{
+ diff_expects *e = payload;
+
+ GIT_UNUSED(delta);
+ GIT_UNUSED(hunk);
+
+ e->lines++;
+ switch (line->origin) {
+ case GIT_DIFF_LINE_CONTEXT:
+ case GIT_DIFF_LINE_CONTEXT_EOFNL: /* techically not a line */
+ e->line_ctxt++;
+ break;
+ case GIT_DIFF_LINE_ADDITION:
+ case GIT_DIFF_LINE_ADD_EOFNL: /* technically not a line add */
+ e->line_adds++;
+ break;
+ case GIT_DIFF_LINE_DELETION:
+ case GIT_DIFF_LINE_DEL_EOFNL: /* technically not a line delete */
+ e->line_dels++;
+ break;
+ default:
+ break;
+ }
+ return 0;
+}
+
+int diff_foreach_via_iterator(
+ git_diff *diff,
+ git_diff_file_cb file_cb,
+ git_diff_binary_cb binary_cb,
+ git_diff_hunk_cb hunk_cb,
+ git_diff_line_cb line_cb,
+ void *data)
+{
+ size_t d, num_d = git_diff_num_deltas(diff);
+
+ GIT_UNUSED(binary_cb);
+
+ for (d = 0; d < num_d; ++d) {
+ git_patch *patch;
+ const git_diff_delta *delta;
+ size_t h, num_h;
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, d));
+ cl_assert((delta = git_patch_get_delta(patch)) != NULL);
+
+ /* call file_cb for this file */
+ if (file_cb != NULL && file_cb(delta, (float)d / num_d, data) != 0) {
+ git_patch_free(patch);
+ goto abort;
+ }
+
+ /* if there are no changes, then the patch will be NULL */
+ if (!patch) {
+ cl_assert(delta->status == GIT_DELTA_UNMODIFIED ||
+ (delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
+ continue;
+ }
+
+ if (!hunk_cb && !line_cb) {
+ git_patch_free(patch);
+ continue;
+ }
+
+ num_h = git_patch_num_hunks(patch);
+
+ for (h = 0; h < num_h; h++) {
+ const git_diff_hunk *hunk;
+ size_t l, num_l;
+
+ cl_git_pass(git_patch_get_hunk(&hunk, &num_l, patch, h));
+
+ if (hunk_cb && hunk_cb(delta, hunk, data) != 0) {
+ git_patch_free(patch);
+ goto abort;
+ }
+
+ for (l = 0; l < num_l; ++l) {
+ const git_diff_line *line;
+
+ cl_git_pass(git_patch_get_line_in_hunk(&line, patch, h, l));
+
+ if (line_cb &&
+ line_cb(delta, hunk, line, data) != 0) {
+ git_patch_free(patch);
+ goto abort;
+ }
+ }
+ }
+
+ git_patch_free(patch);
+ }
+
+ return 0;
+
+abort:
+ git_error_clear();
+ return GIT_EUSER;
+}
+
+void diff_print(FILE *fp, git_diff *diff)
+{
+ cl_git_pass(
+ git_diff_print(diff, GIT_DIFF_FORMAT_PATCH,
+ git_diff_print_callback__to_file_handle, fp ? fp : stderr));
+}
+
+void diff_print_raw(FILE *fp, git_diff *diff)
+{
+ cl_git_pass(
+ git_diff_print(diff, GIT_DIFF_FORMAT_RAW,
+ git_diff_print_callback__to_file_handle, fp ? fp : stderr));
+}
+
+static size_t num_modified_deltas(git_diff *diff)
+{
+ const git_diff_delta *delta;
+ size_t i, cnt = 0;
+
+ for (i = 0; i < git_diff_num_deltas(diff); i++) {
+ delta = git_diff_get_delta(diff, i);
+
+ if (delta->status != GIT_DELTA_UNMODIFIED)
+ cnt++;
+ }
+
+ return cnt;
+}
+
+void diff_assert_equal(git_diff *a, git_diff *b)
+{
+ const git_diff_delta *ad, *bd;
+ size_t i, j;
+
+ assert(a && b);
+
+ cl_assert_equal_i(num_modified_deltas(a), num_modified_deltas(b));
+
+ for (i = 0, j = 0;
+ i < git_diff_num_deltas(a) && j < git_diff_num_deltas(b); ) {
+
+ ad = git_diff_get_delta(a, i);
+ bd = git_diff_get_delta(b, j);
+
+ if (ad->status == GIT_DELTA_UNMODIFIED) {
+ i++;
+ continue;
+ }
+ if (bd->status == GIT_DELTA_UNMODIFIED) {
+ j++;
+ continue;
+ }
+
+ cl_assert_equal_i(ad->status, bd->status);
+ cl_assert_equal_i(ad->flags, bd->flags);
+ cl_assert_equal_i(ad->similarity, bd->similarity);
+ cl_assert_equal_i(ad->nfiles, bd->nfiles);
+
+ /* Don't examine the size or the flags of the deltas;
+ * computed deltas have sizes (parsed deltas do not) and
+ * computed deltas will have flags of `VALID_ID` and
+ * `EXISTS` (parsed deltas will not query the ODB.)
+ */
+
+ /* an empty id indicates that it wasn't presented, because
+ * the diff was identical. (eg, pure rename, mode change only, etc)
+ */
+ if (ad->old_file.id_abbrev && bd->old_file.id_abbrev) {
+ cl_assert_equal_i(ad->old_file.id_abbrev, bd->old_file.id_abbrev);
+ cl_assert_equal_oid(&ad->old_file.id, &bd->old_file.id);
+ cl_assert_equal_i(ad->old_file.mode, bd->old_file.mode);
+ }
+ cl_assert_equal_s(ad->old_file.path, bd->old_file.path);
+
+ if (ad->new_file.id_abbrev && bd->new_file.id_abbrev) {
+ cl_assert_equal_oid(&ad->new_file.id, &bd->new_file.id);
+ cl_assert_equal_i(ad->new_file.id_abbrev, bd->new_file.id_abbrev);
+ cl_assert_equal_i(ad->new_file.mode, bd->new_file.mode);
+ }
+ cl_assert_equal_s(ad->new_file.path, bd->new_file.path);
+
+ i++;
+ j++;
+ }
+}
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+int diff_from_buffer(
+ git_diff **out,
+ const char *content,
+ size_t content_len)
+{
+ return git_diff_from_buffer(out, content, content_len, NULL);
+}
+#else
+int diff_from_buffer(
+ git_diff **out,
+ const char *content,
+ size_t content_len)
+{
+ return git_diff_from_buffer(out, content, content_len);
+}
+#endif
diff --git a/tests/libgit2/diff/diff_helpers.h b/tests/libgit2/diff/diff_helpers.h
new file mode 100644
index 0000000..1be4b47
--- /dev/null
+++ b/tests/libgit2/diff/diff_helpers.h
@@ -0,0 +1,77 @@
+#include "futils.h"
+#include "git2/diff.h"
+
+extern git_tree *resolve_commit_oid_to_tree(
+ git_repository *repo, const char *partial_oid);
+
+typedef struct {
+ int files;
+ int files_binary;
+
+ int file_status[11]; /* indexed by git_delta_t value */
+
+ int hunks;
+ int hunk_new_lines;
+ int hunk_old_lines;
+
+ int lines;
+ int line_ctxt;
+ int line_adds;
+ int line_dels;
+
+ /* optional arrays of expected specific values */
+ const char **names;
+ int *statuses;
+
+ int debug;
+
+} diff_expects;
+
+typedef struct {
+ const char *path;
+ const char *matched_pathspec;
+} notify_expected;
+
+extern int diff_file_cb(
+ const git_diff_delta *delta,
+ float progress,
+ void *cb_data);
+
+extern int diff_print_file_cb(
+ const git_diff_delta *delta,
+ float progress,
+ void *cb_data);
+
+extern int diff_binary_cb(
+ const git_diff_delta *delta,
+ const git_diff_binary *binary,
+ void *cb_data);
+
+extern int diff_hunk_cb(
+ const git_diff_delta *delta,
+ const git_diff_hunk *hunk,
+ void *cb_data);
+
+extern int diff_line_cb(
+ const git_diff_delta *delta,
+ const git_diff_hunk *hunk,
+ const git_diff_line *line,
+ void *cb_data);
+
+extern int diff_foreach_via_iterator(
+ git_diff *diff,
+ git_diff_file_cb file_cb,
+ git_diff_binary_cb binary_cb,
+ git_diff_hunk_cb hunk_cb,
+ git_diff_line_cb line_cb,
+ void *data);
+
+extern void diff_print(FILE *fp, git_diff *diff);
+extern void diff_print_raw(FILE *fp, git_diff *diff);
+
+extern void diff_assert_equal(git_diff *a, git_diff *b);
+
+extern int diff_from_buffer(
+ git_diff **out,
+ const char *content,
+ size_t content_len);
diff --git a/tests/libgit2/diff/diffiter.c b/tests/libgit2/diff/diffiter.c
new file mode 100644
index 0000000..991c73b
--- /dev/null
+++ b/tests/libgit2/diff/diffiter.c
@@ -0,0 +1,453 @@
+#include "clar_libgit2.h"
+#include "diff_helpers.h"
+
+void test_diff_diffiter__initialize(void)
+{
+}
+
+void test_diff_diffiter__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_diff_diffiter__create(void)
+{
+ git_repository *repo = cl_git_sandbox_init("attr");
+ git_diff *diff;
+ size_t d, num_d;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
+
+ num_d = git_diff_num_deltas(diff);
+ for (d = 0; d < num_d; ++d) {
+ const git_diff_delta *delta = git_diff_get_delta(diff, d);
+ cl_assert(delta != NULL);
+ }
+
+ cl_assert(!git_diff_get_delta(diff, num_d));
+
+ git_diff_free(diff);
+}
+
+void test_diff_diffiter__iterate_files_1(void)
+{
+ git_repository *repo = cl_git_sandbox_init("attr");
+ git_diff *diff;
+ size_t d, num_d;
+ diff_expects exp = { 0 };
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
+
+ num_d = git_diff_num_deltas(diff);
+
+ for (d = 0; d < num_d; ++d) {
+ const git_diff_delta *delta = git_diff_get_delta(diff, d);
+ cl_assert(delta != NULL);
+
+ diff_file_cb(delta, (float)d / (float)num_d, &exp);
+ }
+ cl_assert_equal_sz(6, exp.files);
+
+ git_diff_free(diff);
+}
+
+void test_diff_diffiter__iterate_files_2(void)
+{
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_diff *diff;
+ size_t d, num_d;
+ int count = 0;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
+
+ num_d = git_diff_num_deltas(diff);
+ cl_assert_equal_i(8, (int)num_d);
+
+ for (d = 0; d < num_d; ++d) {
+ const git_diff_delta *delta = git_diff_get_delta(diff, d);
+ cl_assert(delta != NULL);
+ count++;
+ }
+ cl_assert_equal_i(8, count);
+
+ git_diff_free(diff);
+}
+
+void test_diff_diffiter__iterate_files_and_hunks(void)
+{
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ size_t d, num_d;
+ int file_count = 0, hunk_count = 0;
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
+
+ num_d = git_diff_num_deltas(diff);
+
+ for (d = 0; d < num_d; ++d) {
+ git_patch *patch;
+ size_t h, num_h;
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, d));
+ cl_assert(patch);
+
+ file_count++;
+
+ num_h = git_patch_num_hunks(patch);
+
+ for (h = 0; h < num_h; h++) {
+ const git_diff_hunk *hunk;
+
+ cl_git_pass(git_patch_get_hunk(&hunk, NULL, patch, h));
+ cl_assert(hunk);
+
+ hunk_count++;
+ }
+
+ git_patch_free(patch);
+ }
+
+ cl_assert_equal_i(13, file_count);
+ cl_assert_equal_i(8, hunk_count);
+
+ git_diff_free(diff);
+}
+
+void test_diff_diffiter__max_size_threshold(void)
+{
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ int file_count = 0, binary_count = 0, hunk_count = 0;
+ size_t d, num_d;
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
+ num_d = git_diff_num_deltas(diff);
+
+ for (d = 0; d < num_d; ++d) {
+ git_patch *patch;
+ const git_diff_delta *delta;
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, d));
+ cl_assert(patch);
+ delta = git_patch_get_delta(patch);
+ cl_assert(delta);
+
+ file_count++;
+ hunk_count += (int)git_patch_num_hunks(patch);
+
+ assert((delta->flags & (GIT_DIFF_FLAG_BINARY|GIT_DIFF_FLAG_NOT_BINARY)) != 0);
+ binary_count += ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
+
+ git_patch_free(patch);
+ }
+
+ cl_assert_equal_i(13, file_count);
+ cl_assert_equal_i(0, binary_count);
+ cl_assert_equal_i(8, hunk_count);
+
+ git_diff_free(diff);
+
+ /* try again with low file size threshold */
+
+ file_count = binary_count = hunk_count = 0;
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+ opts.max_size = 50; /* treat anything over 50 bytes as binary! */
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
+ num_d = git_diff_num_deltas(diff);
+
+ for (d = 0; d < num_d; ++d) {
+ git_patch *patch;
+ const git_diff_delta *delta;
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, d));
+ delta = git_patch_get_delta(patch);
+
+ file_count++;
+ hunk_count += (int)git_patch_num_hunks(patch);
+
+ assert((delta->flags & (GIT_DIFF_FLAG_BINARY|GIT_DIFF_FLAG_NOT_BINARY)) != 0);
+ binary_count += ((delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
+
+ git_patch_free(patch);
+ }
+
+ cl_assert_equal_i(13, file_count);
+ /* Three files are over the 50 byte threshold:
+ * - staged_changes_file_deleted
+ * - staged_changes_modified_file
+ * - staged_new_file_modified_file
+ */
+ cl_assert_equal_i(3, binary_count);
+ cl_assert_equal_i(5, hunk_count);
+
+ git_diff_free(diff);
+}
+
+
+void test_diff_diffiter__iterate_all(void)
+{
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ diff_expects exp = {0};
+ size_t d, num_d;
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
+
+ num_d = git_diff_num_deltas(diff);
+ for (d = 0; d < num_d; ++d) {
+ git_patch *patch;
+ size_t h, num_h;
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, d));
+ cl_assert(patch);
+ exp.files++;
+
+ num_h = git_patch_num_hunks(patch);
+ for (h = 0; h < num_h; h++) {
+ const git_diff_hunk *range;
+ size_t l, num_l;
+
+ cl_git_pass(git_patch_get_hunk(&range, &num_l, patch, h));
+ cl_assert(range);
+ exp.hunks++;
+
+ for (l = 0; l < num_l; ++l) {
+ const git_diff_line *line;
+
+ cl_git_pass(git_patch_get_line_in_hunk(&line, patch, h, l));
+ cl_assert(line && line->content);
+ exp.lines++;
+ }
+ }
+
+ git_patch_free(patch);
+ }
+
+ cl_assert_equal_i(13, exp.files);
+ cl_assert_equal_i(8, exp.hunks);
+ cl_assert_equal_i(14, exp.lines);
+
+ git_diff_free(diff);
+}
+
+static void iterate_over_patch(git_patch *patch, diff_expects *exp)
+{
+ size_t h, num_h = git_patch_num_hunks(patch), num_l;
+
+ exp->files++;
+ exp->hunks += (int)num_h;
+
+ /* let's iterate in reverse, just because we can! */
+ for (h = 1, num_l = 0; h <= num_h; ++h)
+ num_l += git_patch_num_lines_in_hunk(patch, num_h - h);
+
+ exp->lines += (int)num_l;
+}
+
+#define PATCH_CACHE 5
+
+void test_diff_diffiter__iterate_randomly_while_saving_state(void)
+{
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ diff_expects exp = {0};
+ git_patch *patches[PATCH_CACHE];
+ size_t p, d, num_d;
+
+ memset(patches, 0, sizeof(patches));
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
+
+ num_d = git_diff_num_deltas(diff);
+
+ /* To make sure that references counts work for diff and patch objects,
+ * this generates patches and randomly caches them. Only when the patch
+ * is removed from the cache are hunks and lines counted. At the end,
+ * there are still patches in the cache, so free the diff and try to
+ * process remaining patches after the diff is freed.
+ */
+
+ srand(121212);
+ p = rand() % PATCH_CACHE;
+
+ for (d = 0; d < num_d; ++d) {
+ /* take old patch */
+ git_patch *patch = patches[p];
+ patches[p] = NULL;
+
+ /* cache new patch */
+ cl_git_pass(git_patch_from_diff(&patches[p], diff, d));
+ cl_assert(patches[p] != NULL);
+
+ /* process old patch if non-NULL */
+ if (patch != NULL) {
+ iterate_over_patch(patch, &exp);
+ git_patch_free(patch);
+ }
+
+ p = rand() % PATCH_CACHE;
+ }
+
+ /* free diff list now - refcounts should keep things safe */
+ git_diff_free(diff);
+
+ /* process remaining unprocessed patches */
+ for (p = 0; p < PATCH_CACHE; p++) {
+ git_patch *patch = patches[p];
+
+ if (patch != NULL) {
+ iterate_over_patch(patch, &exp);
+ git_patch_free(patch);
+ }
+ }
+
+ /* hopefully it all still added up right */
+ cl_assert_equal_i(13, exp.files);
+ cl_assert_equal_i(8, exp.hunks);
+ cl_assert_equal_i(14, exp.lines);
+}
+
+/* This output is taken directly from `git diff` on the status test data */
+static const char *expected_patch_text[8] = {
+ /* 0 */
+ "diff --git a/file_deleted b/file_deleted\n"
+ "deleted file mode 100644\n"
+ "index 5452d32..0000000\n"
+ "--- a/file_deleted\n"
+ "+++ /dev/null\n"
+ "@@ -1 +0,0 @@\n"
+ "-file_deleted\n",
+ /* 1 */
+ "diff --git a/modified_file b/modified_file\n"
+ "index 452e424..0a53963 100644\n"
+ "--- a/modified_file\n"
+ "+++ b/modified_file\n"
+ "@@ -1 +1,2 @@\n"
+ " modified_file\n"
+ "+modified_file\n",
+ /* 2 */
+ "diff --git a/staged_changes_file_deleted b/staged_changes_file_deleted\n"
+ "deleted file mode 100644\n"
+ "index a6be623..0000000\n"
+ "--- a/staged_changes_file_deleted\n"
+ "+++ /dev/null\n"
+ "@@ -1,2 +0,0 @@\n"
+ "-staged_changes_file_deleted\n"
+ "-staged_changes_file_deleted\n",
+ /* 3 */
+ "diff --git a/staged_changes_modified_file b/staged_changes_modified_file\n"
+ "index 906ee77..011c344 100644\n"
+ "--- a/staged_changes_modified_file\n"
+ "+++ b/staged_changes_modified_file\n"
+ "@@ -1,2 +1,3 @@\n"
+ " staged_changes_modified_file\n"
+ " staged_changes_modified_file\n"
+ "+staged_changes_modified_file\n",
+ /* 4 */
+ "diff --git a/staged_new_file_deleted_file b/staged_new_file_deleted_file\n"
+ "deleted file mode 100644\n"
+ "index 90b8c29..0000000\n"
+ "--- a/staged_new_file_deleted_file\n"
+ "+++ /dev/null\n"
+ "@@ -1 +0,0 @@\n"
+ "-staged_new_file_deleted_file\n",
+ /* 5 */
+ "diff --git a/staged_new_file_modified_file b/staged_new_file_modified_file\n"
+ "index ed06290..8b090c0 100644\n"
+ "--- a/staged_new_file_modified_file\n"
+ "+++ b/staged_new_file_modified_file\n"
+ "@@ -1 +1,2 @@\n"
+ " staged_new_file_modified_file\n"
+ "+staged_new_file_modified_file\n",
+ /* 6 */
+ "diff --git a/subdir/deleted_file b/subdir/deleted_file\n"
+ "deleted file mode 100644\n"
+ "index 1888c80..0000000\n"
+ "--- a/subdir/deleted_file\n"
+ "+++ /dev/null\n"
+ "@@ -1 +0,0 @@\n"
+ "-subdir/deleted_file\n",
+ /* 7 */
+ "diff --git a/subdir/modified_file b/subdir/modified_file\n"
+ "index a619198..57274b7 100644\n"
+ "--- a/subdir/modified_file\n"
+ "+++ b/subdir/modified_file\n"
+ "@@ -1 +1,2 @@\n"
+ " subdir/modified_file\n"
+ "+subdir/modified_file\n"
+};
+
+void test_diff_diffiter__iterate_and_generate_patch_text(void)
+{
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_diff *diff;
+ size_t d, num_d;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
+
+ num_d = git_diff_num_deltas(diff);
+ cl_assert_equal_i(8, (int)num_d);
+
+ for (d = 0; d < num_d; ++d) {
+ git_patch *patch;
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, d));
+ cl_assert(patch != NULL);
+
+ cl_git_pass(git_patch_to_buf(&buf, patch));
+
+ cl_assert_equal_s(expected_patch_text[d], buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_patch_free(patch);
+ }
+
+ git_diff_free(diff);
+}
+
+void test_diff_diffiter__checks_options_version(void)
+{
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ const git_error *err;
+
+ opts.version = 0;
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+
+ cl_git_fail(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
+ err = git_error_last();
+ cl_assert_equal_i(GIT_ERROR_INVALID, err->klass);
+
+ git_error_clear();
+ opts.version = 1024;
+ cl_git_fail(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
+ err = git_error_last();
+ cl_assert_equal_i(GIT_ERROR_INVALID, err->klass);
+}
+
diff --git a/tests/libgit2/diff/drivers.c b/tests/libgit2/diff/drivers.c
new file mode 100644
index 0000000..304a54b
--- /dev/null
+++ b/tests/libgit2/diff/drivers.c
@@ -0,0 +1,279 @@
+#include "clar_libgit2.h"
+#include "diff_helpers.h"
+#include "repository.h"
+#include "diff_driver.h"
+
+static git_repository *g_repo = NULL;
+
+void test_diff_drivers__initialize(void)
+{
+}
+
+void test_diff_drivers__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ g_repo = NULL;
+}
+
+static void overwrite_filemode(const char *expected, git_buf *actual)
+{
+ size_t offset;
+ char *found;
+
+ found = strstr(expected, "100644");
+ if (!found)
+ return;
+
+ offset = ((const char *)found) - expected;
+ if (actual->size < offset + 6)
+ return;
+
+ if (memcmp(&actual->ptr[offset], "100644", 6) != 0)
+ memcpy(&actual->ptr[offset], "100644", 6);
+}
+
+void test_diff_drivers__patterns(void)
+{
+ git_config *cfg;
+ const char *one_sha = "19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13";
+ git_tree *one;
+ git_diff *diff;
+ git_patch *patch;
+ git_buf actual = GIT_BUF_INIT;
+ const char *expected0 = "diff --git a/untimely.txt b/untimely.txt\nindex 9a69d96..57fd0cf 100644\n--- a/untimely.txt\n+++ b/untimely.txt\n@@ -22,3 +22,5 @@ Comes through the blood of the vanguards who\n dreamed--too soon--it had sounded.\r\n \r\n -- Rudyard Kipling\r\n+\r\n+Some new stuff\r\n";
+ const char *expected1 = "diff --git a/untimely.txt b/untimely.txt\nindex 9a69d96..57fd0cf 100644\nBinary files a/untimely.txt and b/untimely.txt differ\n";
+ const char *expected2 = "diff --git a/untimely.txt b/untimely.txt\nindex 9a69d96..57fd0cf 100644\n--- a/untimely.txt\n+++ b/untimely.txt\n@@ -22,3 +22,5 @@ Heaven delivers on earth the Hour that cannot be\n dreamed--too soon--it had sounded.\r\n \r\n -- Rudyard Kipling\r\n+\r\n+Some new stuff\r\n";
+
+ g_repo = cl_git_sandbox_init("renames");
+
+ one = resolve_commit_oid_to_tree(g_repo, one_sha);
+
+ /* no diff */
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
+ cl_assert_equal_i(0, (int)git_diff_num_deltas(diff));
+ git_diff_free(diff);
+
+ /* default diff */
+
+ cl_git_append2file("renames/untimely.txt", "\r\nSome new stuff\r\n");
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
+ cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_to_buf(&actual, patch));
+ cl_assert_equal_s(expected0, actual.ptr);
+
+ git_buf_dispose(&actual);
+ git_patch_free(patch);
+ git_diff_free(diff);
+
+ /* attribute diff set to false */
+
+ cl_git_rewritefile("renames/.gitattributes", "untimely.txt -diff\n");
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
+ cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_to_buf(&actual, patch));
+ cl_assert_equal_s(expected1, actual.ptr);
+
+ git_buf_dispose(&actual);
+ git_patch_free(patch);
+ git_diff_free(diff);
+
+ /* attribute diff set to unconfigured value (should use default) */
+
+ cl_git_rewritefile("renames/.gitattributes", "untimely.txt diff=kipling0\n");
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
+ cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_to_buf(&actual, patch));
+ cl_assert_equal_s(expected0, actual.ptr);
+
+ git_buf_dispose(&actual);
+ git_patch_free(patch);
+ git_diff_free(diff);
+
+ /* let's define that driver */
+
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_git_pass(git_config_set_bool(cfg, "diff.kipling0.binary", 1));
+ git_config_free(cfg);
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
+ cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_to_buf(&actual, patch));
+ cl_assert_equal_s(expected1, actual.ptr);
+
+ git_buf_dispose(&actual);
+ git_patch_free(patch);
+ git_diff_free(diff);
+
+ /* let's use a real driver with some regular expressions */
+
+ git_diff_driver_registry_free(g_repo->diff_drivers);
+ g_repo->diff_drivers = NULL;
+
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_git_pass(git_config_set_bool(cfg, "diff.kipling0.binary", 0));
+ cl_git_pass(git_config_set_string(cfg, "diff.kipling0.xfuncname", "^H.*$"));
+ git_config_free(cfg);
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, one, NULL));
+ cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_to_buf(&actual, patch));
+ cl_assert_equal_s(expected2, actual.ptr);
+
+ git_buf_dispose(&actual);
+ git_patch_free(patch);
+ git_diff_free(diff);
+
+ git_tree_free(one);
+}
+
+void test_diff_drivers__long_lines(void)
+{
+ const char *base = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non nisi ligula. Ut viverra enim sed lobortis suscipit.\nPhasellus eget erat odio. Praesent at est iaculis, ultricies augue vel, dignissim risus. Suspendisse at nisi quis turpis fringilla rutrum id sit amet nulla.\nNam eget dolor fermentum, aliquet nisl at, convallis tellus. Pellentesque rhoncus erat enim, id porttitor elit euismod quis.\nMauris sollicitudin magna odio, non egestas libero vehicula ut. Etiam et quam velit. Fusce eget libero rhoncus, ultricies felis sit amet, egestas purus.\nAliquam in semper tellus. Pellentesque adipiscing rutrum velit, quis malesuada lacus consequat eget.\n";
+ git_index *idx;
+ git_diff *diff;
+ git_patch *patch;
+ git_buf actual = GIT_BUF_INIT;
+ const char *expected = "diff --git a/longlines.txt b/longlines.txt\nindex c1ce6ef..0134431 100644\n--- a/longlines.txt\n+++ b/longlines.txt\n@@ -3,3 +3,5 @@ Phasellus eget erat odio. Praesent at est iaculis, ultricies augue vel, dignissi\n Nam eget dolor fermentum, aliquet nisl at, convallis tellus. Pellentesque rhoncus erat enim, id porttitor elit euismod quis.\n Mauris sollicitudin magna odio, non egestas libero vehicula ut. Etiam et quam velit. Fusce eget libero rhoncus, ultricies felis sit amet, egestas purus.\n Aliquam in semper tellus. Pellentesque adipiscing rutrum velit, quis malesuada lacus consequat eget.\n+newline\n+newline\n";
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_mkfile("empty_standard_repo/longlines.txt", base);
+ cl_git_pass(git_repository_index(&idx, g_repo));
+ cl_git_pass(git_index_add_bypath(idx, "longlines.txt"));
+ cl_git_pass(git_index_write(idx));
+ git_index_free(idx);
+
+ cl_git_append2file("empty_standard_repo/longlines.txt", "newline\nnewline\n");
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
+ cl_assert_equal_sz(1, git_diff_num_deltas(diff));
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_to_buf(&actual, patch));
+
+ /* if chmod not supported, overwrite mode bits since anything is possible */
+ overwrite_filemode(expected, &actual);
+
+ cl_assert_equal_s(expected, actual.ptr);
+
+ git_buf_dispose(&actual);
+ git_patch_free(patch);
+ git_diff_free(diff);
+}
+
+void test_diff_drivers__builtins(void)
+{
+ git_diff *diff;
+ git_patch *patch;
+ git_str file = GIT_STR_INIT, expected = GIT_STR_INIT;
+ git_buf actual = GIT_BUF_INIT;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_vector files = GIT_VECTOR_INIT;
+ size_t i;
+ char *path, *extension;
+
+ g_repo = cl_git_sandbox_init("userdiff");
+
+ cl_git_pass(git_fs_path_dirload(&files, "userdiff/files", 9, 0));
+
+ opts.interhunk_lines = 1;
+ opts.context_lines = 1;
+ opts.pathspec.count = 1;
+
+ git_vector_foreach(&files, i, path) {
+ if (git__prefixcmp(path, "files/file."))
+ continue;
+ extension = path + strlen("files/file.");
+ opts.pathspec.strings = &path;
+
+ /* do diff with no special driver */
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ cl_assert_equal_sz(1, git_diff_num_deltas(diff));
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_to_buf(&actual, patch));
+
+ git_str_sets(&expected, "userdiff/expected/nodriver/diff.");
+ git_str_puts(&expected, extension);
+ cl_git_pass(git_futils_readbuffer(&expected, expected.ptr));
+
+ overwrite_filemode(expected.ptr, &actual);
+
+ cl_assert_equal_s(expected.ptr, actual.ptr);
+
+ git_buf_dispose(&actual);
+ git_patch_free(patch);
+ git_diff_free(diff);
+
+ /* do diff with driver */
+
+ {
+ FILE *fp = fopen("userdiff/.gitattributes", "w");
+ fprintf(fp, "*.%s diff=%s\n", extension, extension);
+ fclose(fp);
+ }
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ cl_assert_equal_sz(1, git_diff_num_deltas(diff));
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_to_buf(&actual, patch));
+
+ git_str_sets(&expected, "userdiff/expected/driver/diff.");
+ git_str_puts(&expected, extension);
+ cl_git_pass(git_futils_readbuffer(&expected, expected.ptr));
+
+ overwrite_filemode(expected.ptr, &actual);
+
+ cl_assert_equal_s(expected.ptr, actual.ptr);
+
+ git_buf_dispose(&actual);
+ git_patch_free(patch);
+ git_diff_free(diff);
+
+ git__free(path);
+ }
+
+ git_buf_dispose(&actual);
+ git_str_dispose(&file);
+ git_str_dispose(&expected);
+ git_vector_free(&files);
+}
+
+void test_diff_drivers__invalid_pattern(void)
+{
+ git_config *cfg;
+ git_index *idx;
+ git_diff *diff;
+ git_patch *patch;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+
+ g_repo = cl_git_sandbox_init("userdiff");
+ cl_git_mkfile("userdiff/.gitattributes", "*.storyboard diff=storyboard\n");
+
+ cl_git_pass(git_repository_config__weakptr(&cfg, g_repo));
+ cl_git_pass(git_config_set_string(cfg, "diff.storyboard.xfuncname", "<!--(.*?)-->"));
+
+ cl_git_mkfile("userdiff/dummy.storyboard", "");
+ cl_git_pass(git_repository_index__weakptr(&idx, g_repo));
+ cl_git_pass(git_index_add_bypath(idx, "dummy.storyboard"));
+ cl_git_mkfile("userdiff/dummy.storyboard", "some content\n");
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+
+ git_patch_free(patch);
+ git_diff_free(diff);
+}
diff --git a/tests/libgit2/diff/externalmodifications.c b/tests/libgit2/diff/externalmodifications.c
new file mode 100644
index 0000000..df62c33
--- /dev/null
+++ b/tests/libgit2/diff/externalmodifications.c
@@ -0,0 +1,133 @@
+#include "clar_libgit2.h"
+#include "../checkout/checkout_helpers.h"
+
+#include "index.h"
+#include "repository.h"
+
+static git_repository *g_repo;
+
+void test_diff_externalmodifications__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo2");
+}
+
+void test_diff_externalmodifications__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ g_repo = NULL;
+}
+
+void test_diff_externalmodifications__file_becomes_smaller(void)
+{
+ git_index *index;
+ git_diff *diff;
+ git_patch* patch;
+ git_str path = GIT_STR_INIT;
+ char big_string[500001];
+
+ cl_git_pass(git_str_joinpath(&path, git_repository_workdir(g_repo), "README"));
+
+ /* Modify the file with a large string */
+ memset(big_string, '\n', sizeof(big_string) - 1);
+ big_string[sizeof(big_string) - 1] = '\0';
+ cl_git_mkfile(path.ptr, big_string);
+
+ /* Get a diff */
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, NULL));
+ cl_assert_equal_i(1, git_diff_num_deltas(diff));
+ cl_assert_equal_i(500000, git_diff_get_delta(diff, 0)->new_file.size);
+
+ /* Simulate file modification after we've gotten the diff.
+ * Write a shorter string to ensure that we don't mmap 500KB from
+ * the previous revision, which would most likely crash. */
+ cl_git_mkfile(path.ptr, "hello");
+
+ /* Attempt to get a patch */
+ cl_git_fail(git_patch_from_diff(&patch, diff, 0));
+
+ git_index_free(index);
+ git_diff_free(diff);
+ git_str_dispose(&path);
+}
+
+void test_diff_externalmodifications__file_becomes_empty(void)
+{
+ git_index *index;
+ git_diff *diff;
+ git_patch* patch;
+ git_str path = GIT_STR_INIT;
+
+ cl_git_pass(git_str_joinpath(&path, git_repository_workdir(g_repo), "README"));
+
+ /* Modify the file */
+ cl_git_mkfile(path.ptr, "hello");
+
+ /* Get a diff */
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, NULL));
+ cl_assert_equal_i(1, git_diff_num_deltas(diff));
+ cl_assert_equal_i(5, git_diff_get_delta(diff, 0)->new_file.size);
+
+ /* Empty out the file after we've gotten the diff */
+ cl_git_mkfile(path.ptr, "");
+
+ /* Attempt to get a patch */
+ cl_git_fail(git_patch_from_diff(&patch, diff, 0));
+
+ git_index_free(index);
+ git_diff_free(diff);
+ git_str_dispose(&path);
+}
+
+void test_diff_externalmodifications__file_deleted(void)
+{
+ git_index *index;
+ git_diff *diff;
+ git_patch* patch;
+ git_str path = GIT_STR_INIT;
+
+ cl_git_pass(git_str_joinpath(&path, git_repository_workdir(g_repo), "README"));
+
+ /* Get a diff */
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, NULL));
+ cl_assert_equal_i(0, git_diff_num_deltas(diff));
+
+ /* Delete the file */
+ cl_git_rmfile(path.ptr);
+
+ /* Attempt to get a patch */
+ cl_git_fail(git_patch_from_diff(&patch, diff, 0));
+
+ git_index_free(index);
+ git_diff_free(diff);
+ git_str_dispose(&path);
+}
+
+void test_diff_externalmodifications__empty_file_becomes_non_empty(void)
+{
+ git_index *index;
+ git_diff *diff;
+ git_patch* patch;
+ git_str path = GIT_STR_INIT;
+
+ cl_git_pass(git_str_joinpath(&path, git_repository_workdir(g_repo), "README"));
+
+ /* Empty out the file */
+ cl_git_mkfile(path.ptr, "");
+
+ /* Get a diff */
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, NULL));
+ cl_assert_equal_i(1, git_diff_num_deltas(diff));
+ cl_assert_equal_i(0, git_diff_get_delta(diff, 0)->new_file.size);
+
+ /* Simulate file modification after we've gotten the diff */
+ cl_git_mkfile(path.ptr, "hello");
+ cl_git_fail(git_patch_from_diff(&patch, diff, 0));
+
+ git_index_free(index);
+ git_diff_free(diff);
+ git_str_dispose(&path);
+}
diff --git a/tests/libgit2/diff/format_email.c b/tests/libgit2/diff/format_email.c
new file mode 100644
index 0000000..2726edb
--- /dev/null
+++ b/tests/libgit2/diff/format_email.c
@@ -0,0 +1,523 @@
+#include "clar.h"
+#include "clar_libgit2.h"
+
+#include "commit.h"
+#include "diff.h"
+#include "diff_generate.h"
+
+static git_repository *repo;
+
+void test_diff_format_email__initialize(void)
+{
+ repo = cl_git_sandbox_init("diff_format_email");
+}
+
+void test_diff_format_email__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+#ifndef GIT_DEPRECATE_HARD
+static void assert_email_match(
+ const char *expected,
+ const char *oidstr,
+ git_diff_format_email_options *opts)
+{
+ git_oid oid;
+ git_commit *commit = NULL;
+ git_diff *diff = NULL;
+ git_buf buf = GIT_BUF_INIT;
+
+ git_oid__fromstr(&oid, oidstr, GIT_OID_SHA1);
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+
+ opts->id = git_commit_id(commit);
+ opts->author = git_commit_author(commit);
+ if (!opts->summary)
+ opts->summary = git_commit_summary(commit);
+
+ cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
+ cl_git_pass(git_diff_format_email(&buf, diff, opts));
+
+ cl_assert_equal_s(expected, buf.ptr);
+ git_buf_dispose(&buf);
+
+ cl_git_pass(git_diff_commit_as_email(
+ &buf, repo, commit, 1, 1, opts->flags, NULL));
+ cl_assert_equal_s(expected, buf.ptr);
+
+ git_diff_free(diff);
+ git_commit_free(commit);
+ git_buf_dispose(&buf);
+}
+#endif
+
+void test_diff_format_email__simple(void)
+{
+#ifndef GIT_DEPRECATE_HARD
+ git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
+ const char *email =
+ "From 9264b96c6d104d0e07ae33d3007b6a48246c6f92 Mon Sep 17 00:00:00 2001\n" \
+ "From: Jacques Germishuys <jacquesg@striata.com>\n" \
+ "Date: Wed, 9 Apr 2014 20:57:01 +0200\n" \
+ "Subject: [PATCH] Modify some content\n" \
+ "\n" \
+ "---\n" \
+ " file1.txt | 8 +++++---\n" \
+ " 1 file changed, 5 insertions(+), 3 deletions(-)\n" \
+ "\n" \
+ "diff --git a/file1.txt b/file1.txt\n" \
+ "index 94aaae8..af8f41d 100644\n" \
+ "--- a/file1.txt\n" \
+ "+++ b/file1.txt\n" \
+ "@@ -1,15 +1,17 @@\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ "+_file1.txt_\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ "+\n" \
+ "+\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ "-file1.txt\n" \
+ "-file1.txt\n" \
+ "-file1.txt\n" \
+ "+_file1.txt_\n" \
+ "+_file1.txt_\n" \
+ " file1.txt\n" \
+ "--\n" \
+ "libgit2 " LIBGIT2_VERSION "\n" \
+ "\n";
+
+ assert_email_match(
+ email, "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts);
+#endif
+}
+
+void test_diff_format_email__with_message(void)
+{
+#ifndef GIT_DEPRECATE_HARD
+ git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
+ const char *email = "From 627e7e12d87e07a83fad5b6bfa25e86ead4a5270 Mon Sep 17 00:00:00 2001\n" \
+ "From: Patrick Steinhardt <ps@pks.im>\n" \
+ "Date: Tue, 24 Nov 2015 13:34:39 +0100\n" \
+ "Subject: [PATCH] Modify content with message\n" \
+ "\n" \
+ "Modify content of file3.txt by appending a new line. Make this\n" \
+ "commit message somewhat longer to test behavior with newlines\n" \
+ "embedded in the message body.\n" \
+ "\n" \
+ "Also test if new paragraphs are included correctly.\n" \
+ "---\n" \
+ " file3.txt | 1 +\n" \
+ " 1 file changed, 1 insertion(+)\n" \
+ "\n" \
+ "diff --git a/file3.txt b/file3.txt\n" \
+ "index 9a2d780..7309653 100644\n" \
+ "--- a/file3.txt\n" \
+ "+++ b/file3.txt\n" \
+ "@@ -3,3 +3,4 @@ file3!\n" \
+ " file3\n" \
+ " file3\n" \
+ " file3\n" \
+ "+file3\n" \
+ "--\n" \
+ "libgit2 " LIBGIT2_VERSION "\n" \
+ "\n";
+
+ opts.body = "Modify content of file3.txt by appending a new line. Make this\n" \
+ "commit message somewhat longer to test behavior with newlines\n" \
+ "embedded in the message body.\n" \
+ "\n" \
+ "Also test if new paragraphs are included correctly.";
+
+ assert_email_match(
+ email, "627e7e12d87e07a83fad5b6bfa25e86ead4a5270", &opts);
+#endif
+}
+
+
+void test_diff_format_email__multiple(void)
+{
+#ifndef GIT_DEPRECATE_HARD
+ git_oid oid;
+ git_commit *commit = NULL;
+ git_diff *diff = NULL;
+ git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
+ git_buf buf = GIT_BUF_INIT;
+
+ const char *email =
+ "From 10808fe9c9be5a190c0ba68d1a002233fb363508 Mon Sep 17 00:00:00 2001\n" \
+ "From: Jacques Germishuys <jacquesg@striata.com>\n" \
+ "Date: Thu, 10 Apr 2014 19:37:05 +0200\n" \
+ "Subject: [PATCH 1/2] Added file2.txt file3.txt\n" \
+ "\n" \
+ "---\n" \
+ " file2.txt | 5 +++++\n" \
+ " file3.txt | 5 +++++\n" \
+ " 2 files changed, 10 insertions(+)\n" \
+ " create mode 100644 file2.txt\n" \
+ " create mode 100644 file3.txt\n" \
+ "\n" \
+ "diff --git a/file2.txt b/file2.txt\n" \
+ "new file mode 100644\n" \
+ "index 0000000..e909123\n" \
+ "--- /dev/null\n" \
+ "+++ b/file2.txt\n" \
+ "@@ -0,0 +1,5 @@\n" \
+ "+file2\n" \
+ "+file2\n" \
+ "+file2\n" \
+ "+file2\n" \
+ "+file2\n" \
+ "diff --git a/file3.txt b/file3.txt\n" \
+ "new file mode 100644\n" \
+ "index 0000000..9435022\n" \
+ "--- /dev/null\n" \
+ "+++ b/file3.txt\n" \
+ "@@ -0,0 +1,5 @@\n" \
+ "+file3\n" \
+ "+file3\n" \
+ "+file3\n" \
+ "+file3\n" \
+ "+file3\n" \
+ "--\n" \
+ "libgit2 " LIBGIT2_VERSION "\n" \
+ "\n" \
+ "From 873806f6f27e631eb0b23e4b56bea2bfac14a373 Mon Sep 17 00:00:00 2001\n" \
+ "From: Jacques Germishuys <jacquesg@striata.com>\n" \
+ "Date: Thu, 10 Apr 2014 19:37:36 +0200\n" \
+ "Subject: [PATCH 2/2] Modified file2.txt, file3.txt\n" \
+ "\n" \
+ "---\n" \
+ " file2.txt | 2 +-\n" \
+ " file3.txt | 2 +-\n" \
+ " 2 files changed, 2 insertions(+), 2 deletions(-)\n" \
+ "\n" \
+ "diff --git a/file2.txt b/file2.txt\n" \
+ "index e909123..7aff11d 100644\n" \
+ "--- a/file2.txt\n" \
+ "+++ b/file2.txt\n" \
+ "@@ -1,5 +1,5 @@\n" \
+ " file2\n" \
+ " file2\n" \
+ " file2\n" \
+ "-file2\n" \
+ "+file2!\n" \
+ " file2\n" \
+ "diff --git a/file3.txt b/file3.txt\n" \
+ "index 9435022..9a2d780 100644\n" \
+ "--- a/file3.txt\n" \
+ "+++ b/file3.txt\n" \
+ "@@ -1,5 +1,5 @@\n" \
+ " file3\n" \
+ "-file3\n" \
+ "+file3!\n" \
+ " file3\n" \
+ " file3\n" \
+ " file3\n" \
+ "--\n" \
+ "libgit2 " LIBGIT2_VERSION "\n" \
+ "\n";
+
+
+ git_oid__fromstr(&oid, "10808fe9c9be5a190c0ba68d1a002233fb363508", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+
+ opts.id = git_commit_id(commit);
+ opts.author = git_commit_author(commit);
+ opts.summary = git_commit_summary(commit);
+ opts.patch_no = 1;
+ opts.total_patches = 2;
+
+ cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
+ cl_git_pass(git_diff_format_email(&buf, diff, &opts));
+
+ git_diff_free(diff);
+ git_commit_free(commit);
+ diff = NULL;
+ commit = NULL;
+
+ git_oid__fromstr(&oid, "873806f6f27e631eb0b23e4b56bea2bfac14a373", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+
+ opts.id = git_commit_id(commit);
+ opts.author = git_commit_author(commit);
+ opts.summary = git_commit_summary(commit);
+ opts.patch_no = 2;
+ opts.total_patches = 2;
+
+ cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
+ cl_git_pass(git_diff_format_email(&buf, diff, &opts));
+
+ cl_assert_equal_s(email, buf.ptr);
+
+ git_diff_free(diff);
+ git_commit_free(commit);
+ git_buf_dispose(&buf);
+#endif
+}
+
+void test_diff_format_email__exclude_marker(void)
+{
+#ifndef GIT_DEPRECATE_HARD
+ git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
+ const char *email =
+ "From 9264b96c6d104d0e07ae33d3007b6a48246c6f92 Mon Sep 17 00:00:00 2001\n" \
+ "From: Jacques Germishuys <jacquesg@striata.com>\n" \
+ "Date: Wed, 9 Apr 2014 20:57:01 +0200\n" \
+ "Subject: Modify some content\n" \
+ "\n" \
+ "---\n" \
+ " file1.txt | 8 +++++---\n" \
+ " 1 file changed, 5 insertions(+), 3 deletions(-)\n" \
+ "\n" \
+ "diff --git a/file1.txt b/file1.txt\n" \
+ "index 94aaae8..af8f41d 100644\n" \
+ "--- a/file1.txt\n" \
+ "+++ b/file1.txt\n" \
+ "@@ -1,15 +1,17 @@\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ "+_file1.txt_\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ "+\n" \
+ "+\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ "-file1.txt\n" \
+ "-file1.txt\n" \
+ "-file1.txt\n" \
+ "+_file1.txt_\n" \
+ "+_file1.txt_\n" \
+ " file1.txt\n" \
+ "--\n" \
+ "libgit2 " LIBGIT2_VERSION "\n" \
+ "\n";
+
+ opts.flags |= GIT_DIFF_FORMAT_EMAIL_EXCLUDE_SUBJECT_PATCH_MARKER;
+
+ assert_email_match(
+ email, "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts);
+#endif
+}
+
+void test_diff_format_email__invalid_no(void)
+{
+#ifndef GIT_DEPRECATE_HARD
+ git_oid oid;
+ git_commit *commit = NULL;
+ git_diff *diff = NULL;
+ git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
+ git_buf buf = GIT_BUF_INIT;
+
+ git_oid__fromstr(&oid, "9264b96c6d104d0e07ae33d3007b6a48246c6f92", GIT_OID_SHA1);
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+
+ opts.id = git_commit_id(commit);
+ opts.author = git_commit_author(commit);
+ opts.summary = git_commit_summary(commit);
+ opts.patch_no = 2;
+ opts.total_patches = 1;
+
+ cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
+ cl_git_fail(git_diff_format_email(&buf, diff, &opts));
+ cl_git_fail(git_diff_commit_as_email(&buf, repo, commit, 2, 1, 0, NULL));
+
+ git_diff_free(diff);
+ git_commit_free(commit);
+ git_buf_dispose(&buf);
+#endif
+}
+
+void test_diff_format_email__mode_change(void)
+{
+#ifndef GIT_DEPRECATE_HARD
+ git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
+ const char *email =
+ "From 7ade76dd34bba4733cf9878079f9fd4a456a9189 Mon Sep 17 00:00:00 2001\n" \
+ "From: Jacques Germishuys <jacquesg@striata.com>\n" \
+ "Date: Thu, 10 Apr 2014 10:05:03 +0200\n" \
+ "Subject: [PATCH] Update permissions\n" \
+ "\n" \
+ "---\n" \
+ " file1.txt.renamed | 0\n" \
+ " 1 file changed, 0 insertions(+), 0 deletions(-)\n" \
+ " mode change 100644 => 100755 file1.txt.renamed\n" \
+ "\n" \
+ "diff --git a/file1.txt.renamed b/file1.txt.renamed\n" \
+ "old mode 100644\n" \
+ "new mode 100755\n" \
+ "--\n" \
+ "libgit2 " LIBGIT2_VERSION "\n" \
+ "\n";
+
+ assert_email_match(
+ email, "7ade76dd34bba4733cf9878079f9fd4a456a9189", &opts);
+#endif
+}
+
+void test_diff_format_email__rename_add_remove(void)
+{
+#ifndef GIT_DEPRECATE_HARD
+ git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
+ const char *email =
+ "From 6e05acc5a5dab507d91a0a0cc0fb05a3dd98892d Mon Sep 17 00:00:00 2001\n" \
+ "From: Jacques Germishuys <jacquesg@striata.com>\n" \
+ "Date: Wed, 9 Apr 2014 21:15:56 +0200\n" \
+ "Subject: [PATCH] Renamed file1.txt -> file1.txt.renamed\n" \
+ "\n" \
+ "---\n" \
+ " file1.txt | 17 -----------------\n" \
+ " file1.txt.renamed | 17 +++++++++++++++++\n" \
+ " 2 files changed, 17 insertions(+), 17 deletions(-)\n" \
+ " delete mode 100644 file1.txt\n" \
+ " create mode 100644 file1.txt.renamed\n" \
+ "\n" \
+ "diff --git a/file1.txt b/file1.txt\n" \
+ "deleted file mode 100644\n" \
+ "index af8f41d..0000000\n" \
+ "--- a/file1.txt\n" \
+ "+++ /dev/null\n" \
+ "@@ -1,17 +0,0 @@\n" \
+ "-file1.txt\n" \
+ "-file1.txt\n" \
+ "-_file1.txt_\n" \
+ "-file1.txt\n" \
+ "-file1.txt\n" \
+ "-file1.txt\n" \
+ "-file1.txt\n" \
+ "-\n" \
+ "-\n" \
+ "-file1.txt\n" \
+ "-file1.txt\n" \
+ "-file1.txt\n" \
+ "-file1.txt\n" \
+ "-file1.txt\n" \
+ "-_file1.txt_\n" \
+ "-_file1.txt_\n" \
+ "-file1.txt\n" \
+ "diff --git a/file1.txt.renamed b/file1.txt.renamed\n" \
+ "new file mode 100644\n" \
+ "index 0000000..a97157a\n" \
+ "--- /dev/null\n" \
+ "+++ b/file1.txt.renamed\n" \
+ "@@ -0,0 +1,17 @@\n" \
+ "+file1.txt\n" \
+ "+file1.txt\n" \
+ "+_file1.txt_\n" \
+ "+file1.txt\n" \
+ "+file1.txt\n" \
+ "+file1.txt_renamed\n" \
+ "+file1.txt\n" \
+ "+\n" \
+ "+\n" \
+ "+file1.txt\n" \
+ "+file1.txt\n" \
+ "+file1.txt_renamed\n" \
+ "+file1.txt\n" \
+ "+file1.txt\n" \
+ "+_file1.txt_\n" \
+ "+_file1.txt_\n" \
+ "+file1.txt\n" \
+ "--\n" \
+ "libgit2 " LIBGIT2_VERSION "\n" \
+ "\n";
+
+ assert_email_match(
+ email, "6e05acc5a5dab507d91a0a0cc0fb05a3dd98892d", &opts);
+#endif
+}
+
+void test_diff_format_email__multiline_summary(void)
+{
+#ifndef GIT_DEPRECATE_HARD
+ git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
+ const char *email =
+ "From 9264b96c6d104d0e07ae33d3007b6a48246c6f92 Mon Sep 17 00:00:00 2001\n" \
+ "From: Jacques Germishuys <jacquesg@striata.com>\n" \
+ "Date: Wed, 9 Apr 2014 20:57:01 +0200\n" \
+ "Subject: [PATCH] Modify some content\n" \
+ "\n" \
+ "---\n" \
+ " file1.txt | 8 +++++---\n" \
+ " 1 file changed, 5 insertions(+), 3 deletions(-)\n" \
+ "\n" \
+ "diff --git a/file1.txt b/file1.txt\n" \
+ "index 94aaae8..af8f41d 100644\n" \
+ "--- a/file1.txt\n" \
+ "+++ b/file1.txt\n" \
+ "@@ -1,15 +1,17 @@\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ "+_file1.txt_\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ "+\n" \
+ "+\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ "-file1.txt\n" \
+ "-file1.txt\n" \
+ "-file1.txt\n" \
+ "+_file1.txt_\n" \
+ "+_file1.txt_\n" \
+ " file1.txt\n" \
+ "--\n" \
+ "libgit2 " LIBGIT2_VERSION "\n" \
+ "\n";
+
+ opts.summary = "Modify some content\nSome extra stuff here";
+
+ assert_email_match(
+ email, "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts);
+#endif
+}
+
+void test_diff_format_email__binary(void)
+{
+#ifndef GIT_DEPRECATE_HARD
+ git_diff_format_email_options opts = GIT_DIFF_FORMAT_EMAIL_OPTIONS_INIT;
+ const char *email =
+ "From 8d7523f6fcb2404257889abe0d96f093d9f524f9 Mon Sep 17 00:00:00 2001\n" \
+ "From: Jacques Germishuys <jacquesg@striata.com>\n" \
+ "Date: Sun, 13 Apr 2014 18:10:18 +0200\n" \
+ "Subject: [PATCH] Modified binary file\n" \
+ "\n" \
+ "---\n" \
+ " binary.bin | Bin 3 -> 5 bytes\n" \
+ " 1 file changed, 0 insertions(+), 0 deletions(-)\n" \
+ "\n" \
+ "diff --git a/binary.bin b/binary.bin\n" \
+ "index bd474b2..9ac35ff 100644\n" \
+ "Binary files a/binary.bin and b/binary.bin differ\n" \
+ "--\n" \
+ "libgit2 " LIBGIT2_VERSION "\n" \
+ "\n";
+
+ opts.summary = "Modified binary file";
+
+ assert_email_match(
+ email, "8d7523f6fcb2404257889abe0d96f093d9f524f9", &opts);
+#endif
+}
+
diff --git a/tests/libgit2/diff/index.c b/tests/libgit2/diff/index.c
new file mode 100644
index 0000000..b786675
--- /dev/null
+++ b/tests/libgit2/diff/index.c
@@ -0,0 +1,303 @@
+#include "clar_libgit2.h"
+#include "diff_helpers.h"
+#include "index.h"
+
+static git_repository *g_repo = NULL;
+
+void test_diff_index__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("status");
+}
+
+void test_diff_index__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_diff_index__0(void)
+{
+ /* grabbed a couple of commit oids from the history of the attr repo */
+ const char *a_commit = "26a125ee1bf"; /* the current HEAD */
+ const char *b_commit = "0017bd4ab1ec3"; /* the start */
+ git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit);
+ git_tree *b = resolve_commit_oid_to_tree(g_repo, b_commit);
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ diff_expects exp;
+
+ cl_assert(a);
+ cl_assert(b);
+
+ opts.context_lines = 1;
+ opts.interhunk_lines = 1;
+
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts));
+
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ /* to generate these values:
+ * - cd to tests/resources/status,
+ * - mv .gitted .git
+ * - git diff --name-status --cached 26a125ee1bf
+ * - git diff -U1 --cached 26a125ee1bf
+ * - mv .git .gitted
+ */
+ cl_assert_equal_i(8, exp.files);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]);
+
+ cl_assert_equal_i(8, exp.hunks);
+
+ cl_assert_equal_i(11, exp.lines);
+ cl_assert_equal_i(3, exp.line_ctxt);
+ cl_assert_equal_i(6, exp.line_adds);
+ cl_assert_equal_i(2, exp.line_dels);
+
+ git_diff_free(diff);
+ diff = NULL;
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, b, NULL, &opts));
+
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ /* to generate these values:
+ * - cd to tests/resources/status,
+ * - mv .gitted .git
+ * - git diff --name-status --cached 0017bd4ab1ec3
+ * - git diff -U1 --cached 0017bd4ab1ec3
+ * - mv .git .gitted
+ */
+ cl_assert_equal_i(12, exp.files);
+ cl_assert_equal_i(7, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]);
+
+ cl_assert_equal_i(12, exp.hunks);
+
+ cl_assert_equal_i(16, exp.lines);
+ cl_assert_equal_i(3, exp.line_ctxt);
+ cl_assert_equal_i(11, exp.line_adds);
+ cl_assert_equal_i(2, exp.line_dels);
+
+ git_diff_free(diff);
+ diff = NULL;
+
+ git_tree_free(a);
+ git_tree_free(b);
+}
+
+static int diff_stop_after_2_files(
+ const git_diff_delta *delta,
+ float progress,
+ void *payload)
+{
+ diff_expects *e = payload;
+
+ GIT_UNUSED(progress);
+ GIT_UNUSED(delta);
+
+ e->files++;
+
+ return (e->files == 2);
+}
+
+void test_diff_index__1(void)
+{
+ /* grabbed a couple of commit oids from the history of the attr repo */
+ const char *a_commit = "26a125ee1bf"; /* the current HEAD */
+ const char *b_commit = "0017bd4ab1ec3"; /* the start */
+ git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit);
+ git_tree *b = resolve_commit_oid_to_tree(g_repo, b_commit);
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ diff_expects exp;
+
+ cl_assert(a);
+ cl_assert(b);
+
+ opts.context_lines = 1;
+ opts.interhunk_lines = 1;
+
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts));
+
+ cl_assert_equal_i(1, git_diff_foreach(
+ diff, diff_stop_after_2_files, NULL, NULL, NULL, &exp) );
+
+ cl_assert_equal_i(2, exp.files);
+
+ git_diff_free(diff);
+ diff = NULL;
+
+ git_tree_free(a);
+ git_tree_free(b);
+}
+
+void test_diff_index__checks_options_version(void)
+{
+ const char *a_commit = "26a125ee1bf";
+ git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit);
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ const git_error *err;
+
+ opts.version = 0;
+ cl_git_fail(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts));
+ err = git_error_last();
+ cl_assert_equal_i(GIT_ERROR_INVALID, err->klass);
+ cl_assert_equal_p(diff, NULL);
+
+ git_error_clear();
+ opts.version = 1024;
+ cl_git_fail(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts));
+ err = git_error_last();
+ cl_assert_equal_i(GIT_ERROR_INVALID, err->klass);
+ cl_assert_equal_p(diff, NULL);
+
+ git_tree_free(a);
+}
+
+static void do_conflicted_diff(diff_expects *exp, unsigned long flags)
+{
+ const char *a_commit = "26a125ee1bf"; /* the current HEAD */
+ git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit);
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_index_entry ancestor = {{0}}, ours = {{0}}, theirs = {{0}};
+ git_diff *diff = NULL;
+ git_index *index;
+
+ cl_assert(a);
+
+ opts.context_lines = 1;
+ opts.interhunk_lines = 1;
+ opts.flags |= flags;
+
+ memset(exp, 0, sizeof(diff_expects));
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ ancestor.path = ours.path = theirs.path = "staged_changes";
+ ancestor.mode = ours.mode = theirs.mode = GIT_FILEMODE_BLOB;
+
+ git_oid__fromstr(&ancestor.id, "d427e0b2e138501a3d15cc376077a3631e15bd46", GIT_OID_SHA1);
+ git_oid__fromstr(&ours.id, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", GIT_OID_SHA1);
+ git_oid__fromstr(&theirs.id, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", GIT_OID_SHA1);
+
+ cl_git_pass(git_index_conflict_add(index, &ancestor, &ours, &theirs));
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, index, &opts));
+
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, exp));
+
+ git_diff_free(diff);
+ git_tree_free(a);
+ git_index_free(index);
+}
+
+void test_diff_index__reports_conflicts(void)
+{
+ diff_expects exp;
+
+ do_conflicted_diff(&exp, 0);
+
+ cl_assert_equal_i(8, exp.files);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_CONFLICTED]);
+
+ cl_assert_equal_i(7, exp.hunks);
+
+ cl_assert_equal_i(9, exp.lines);
+ cl_assert_equal_i(2, exp.line_ctxt);
+ cl_assert_equal_i(5, exp.line_adds);
+ cl_assert_equal_i(2, exp.line_dels);
+}
+
+void test_diff_index__reports_conflicts_when_reversed(void)
+{
+ diff_expects exp;
+
+ do_conflicted_diff(&exp, GIT_DIFF_REVERSE);
+
+ cl_assert_equal_i(8, exp.files);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_CONFLICTED]);
+
+ cl_assert_equal_i(7, exp.hunks);
+
+ cl_assert_equal_i(9, exp.lines);
+ cl_assert_equal_i(2, exp.line_ctxt);
+ cl_assert_equal_i(2, exp.line_adds);
+ cl_assert_equal_i(5, exp.line_dels);
+}
+
+void test_diff_index__not_in_head_conflicted(void)
+{
+ const char *a_commit = "26a125ee1bf"; /* the current HEAD */
+ git_index_entry theirs = {{0}};
+ git_index *index;
+ git_diff *diff;
+ const git_diff_delta *delta;
+
+ git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit);
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_read_tree(index, a));
+
+ theirs.path = "file_not_in_head";
+ theirs.mode = GIT_FILEMODE_BLOB;
+ git_oid__fromstr(&theirs.id, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", GIT_OID_SHA1);
+ cl_git_pass(git_index_conflict_add(index, NULL, NULL, &theirs));
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, index, NULL));
+
+ cl_assert_equal_i(git_diff_num_deltas(diff), 1);
+ delta = git_diff_get_delta(diff, 0);
+ cl_assert_equal_i(delta->status, GIT_DELTA_CONFLICTED);
+
+ git_diff_free(diff);
+ git_index_free(index);
+ git_tree_free(a);
+}
+
+void test_diff_index__to_index(void)
+{
+ const char *a_commit = "26a125ee1bf"; /* the current HEAD */
+ git_tree *old_tree;
+ git_index *old_index;
+ git_index *new_index;
+ git_diff *diff;
+ diff_expects exp;
+
+ cl_git_pass(git_index__new(&old_index, GIT_OID_SHA1));
+ old_tree = resolve_commit_oid_to_tree(g_repo, a_commit);
+ cl_git_pass(git_index_read_tree(old_index, old_tree));
+
+ cl_git_pass(git_repository_index(&new_index, g_repo));
+
+ cl_git_pass(git_diff_index_to_index(&diff, g_repo, old_index, new_index, NULL));
+
+ memset(&exp, 0, sizeof(diff_expects));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(8, exp.files);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_CONFLICTED]);
+
+ git_diff_free(diff);
+ git_index_free(new_index);
+ git_index_free(old_index);
+ git_tree_free(old_tree);
+}
diff --git a/tests/libgit2/diff/notify.c b/tests/libgit2/diff/notify.c
new file mode 100644
index 0000000..6535127
--- /dev/null
+++ b/tests/libgit2/diff/notify.c
@@ -0,0 +1,258 @@
+#include "clar_libgit2.h"
+#include "diff_helpers.h"
+
+static git_repository *g_repo = NULL;
+
+void test_diff_notify__initialize(void)
+{
+}
+
+void test_diff_notify__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static int assert_called_notifications(
+ const git_diff *diff_so_far,
+ const git_diff_delta *delta_to_add,
+ const char *matched_pathspec,
+ void *payload)
+{
+ bool found = false;
+ notify_expected *exp = (notify_expected*)payload;
+ notify_expected *e;
+
+ GIT_UNUSED(diff_so_far);
+
+ for (e = exp; e->path != NULL; e++) {
+ if (strcmp(e->path, delta_to_add->new_file.path))
+ continue;
+
+ cl_assert_equal_s(e->matched_pathspec, matched_pathspec);
+
+ found = true;
+ break;
+ }
+
+ cl_assert(found);
+ return 0;
+}
+
+static void test_notify(
+ char **searched_pathspecs,
+ int pathspecs_count,
+ notify_expected *expected_matched_pathspecs,
+ int expected_diffed_files_count)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ diff_expects exp;
+
+ g_repo = cl_git_sandbox_init("status");
+
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+ opts.notify_cb = assert_called_notifications;
+ opts.pathspec.strings = searched_pathspecs;
+ opts.pathspec.count = pathspecs_count;
+
+ opts.payload = expected_matched_pathspecs;
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
+
+ cl_assert_equal_i(expected_diffed_files_count, exp.files);
+
+ git_diff_free(diff);
+}
+
+void test_diff_notify__notify_single_pathspec(void)
+{
+ char *searched_pathspecs[] = {
+ "*_deleted",
+ };
+ notify_expected expected_matched_pathspecs[] = {
+ { "file_deleted", "*_deleted" },
+ { "staged_changes_file_deleted", "*_deleted" },
+ { NULL, NULL }
+ };
+
+ test_notify(searched_pathspecs, 1, expected_matched_pathspecs, 2);
+}
+
+void test_diff_notify__notify_multiple_pathspec(void)
+{
+ char *searched_pathspecs[] = {
+ "staged_changes_cant_find_me",
+ "subdir/modified_cant_find_me",
+ "subdir/*",
+ "staged*"
+ };
+ notify_expected expected_matched_pathspecs[] = {
+ { "staged_changes_file_deleted", "staged*" },
+ { "staged_changes_modified_file", "staged*" },
+ { "staged_delete_modified_file", "staged*" },
+ { "staged_new_file_deleted_file", "staged*" },
+ { "staged_new_file_modified_file", "staged*" },
+ { "subdir/deleted_file", "subdir/*" },
+ { "subdir/modified_file", "subdir/*" },
+ { "subdir/new_file", "subdir/*" },
+ { NULL, NULL }
+ };
+
+ test_notify(searched_pathspecs, 4, expected_matched_pathspecs, 8);
+}
+
+void test_diff_notify__notify_catchall_with_empty_pathspecs(void)
+{
+ char *searched_pathspecs[] = {
+ "",
+ ""
+ };
+ notify_expected expected_matched_pathspecs[] = {
+ { "file_deleted", NULL },
+ { "ignored_file", NULL },
+ { "modified_file", NULL },
+ { "new_file", NULL },
+ { "\xe8\xbf\x99", NULL },
+ { "staged_changes_file_deleted", NULL },
+ { "staged_changes_modified_file", NULL },
+ { "staged_delete_modified_file", NULL },
+ { "staged_new_file_deleted_file", NULL },
+ { "staged_new_file_modified_file", NULL },
+ { "subdir/deleted_file", NULL },
+ { "subdir/modified_file", NULL },
+ { "subdir/new_file", NULL },
+ { NULL, NULL }
+ };
+
+ test_notify(searched_pathspecs, 1, expected_matched_pathspecs, 13);
+}
+
+void test_diff_notify__notify_catchall(void)
+{
+ char *searched_pathspecs[] = {
+ "*",
+ };
+ notify_expected expected_matched_pathspecs[] = {
+ { "file_deleted", "*" },
+ { "ignored_file", "*" },
+ { "modified_file", "*" },
+ { "new_file", "*" },
+ { "\xe8\xbf\x99", "*" },
+ { "staged_changes_file_deleted", "*" },
+ { "staged_changes_modified_file", "*" },
+ { "staged_delete_modified_file", "*" },
+ { "staged_new_file_deleted_file", "*" },
+ { "staged_new_file_modified_file", "*" },
+ { "subdir/deleted_file", "*" },
+ { "subdir/modified_file", "*" },
+ { "subdir/new_file", "*" },
+ { NULL, NULL }
+ };
+
+ test_notify(searched_pathspecs, 1, expected_matched_pathspecs, 13);
+}
+
+static int abort_diff(
+ const git_diff *diff_so_far,
+ const git_diff_delta *delta_to_add,
+ const char *matched_pathspec,
+ void *payload)
+{
+ GIT_UNUSED(diff_so_far);
+ GIT_UNUSED(delta_to_add);
+ GIT_UNUSED(matched_pathspec);
+ GIT_UNUSED(payload);
+
+ return -42;
+}
+
+void test_diff_notify__notify_cb_can_abort_diff(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ char *pathspec = NULL;
+
+ g_repo = cl_git_sandbox_init("status");
+
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+ opts.notify_cb = abort_diff;
+ opts.pathspec.strings = &pathspec;
+ opts.pathspec.count = 1;
+
+ pathspec = "file_deleted";
+ cl_git_fail_with(
+ git_diff_index_to_workdir(&diff, g_repo, NULL, &opts), -42);
+
+ pathspec = "staged_changes_modified_file";
+ cl_git_fail_with(
+ git_diff_index_to_workdir(&diff, g_repo, NULL, &opts), -42);
+}
+
+static int filter_all(
+ const git_diff *diff_so_far,
+ const git_diff_delta *delta_to_add,
+ const char *matched_pathspec,
+ void *payload)
+{
+ GIT_UNUSED(diff_so_far);
+ GIT_UNUSED(delta_to_add);
+ GIT_UNUSED(matched_pathspec);
+ GIT_UNUSED(payload);
+
+ return 42;
+}
+
+void test_diff_notify__notify_cb_can_be_used_as_filtering_function(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ char *pathspec = NULL;
+ diff_expects exp;
+
+ g_repo = cl_git_sandbox_init("status");
+
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+ opts.notify_cb = filter_all;
+ opts.pathspec.strings = &pathspec;
+ opts.pathspec.count = 1;
+
+ pathspec = "*_deleted";
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
+
+ cl_assert_equal_i(0, exp.files);
+
+ git_diff_free(diff);
+}
+
+static int progress_abort_diff(
+ const git_diff *diff_so_far,
+ const char *old_path,
+ const char *new_path,
+ void *payload)
+{
+ GIT_UNUSED(diff_so_far);
+ GIT_UNUSED(old_path);
+ GIT_UNUSED(new_path);
+ GIT_UNUSED(payload);
+
+ return -42;
+}
+
+void test_diff_notify__progress_cb_can_abort_diff(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+
+ g_repo = cl_git_sandbox_init("status");
+
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+ opts.progress_cb = progress_abort_diff;
+
+ cl_git_fail_with(
+ git_diff_index_to_workdir(&diff, g_repo, NULL, &opts), -42);
+}
diff --git a/tests/libgit2/diff/parse.c b/tests/libgit2/diff/parse.c
new file mode 100644
index 0000000..79745b9
--- /dev/null
+++ b/tests/libgit2/diff/parse.c
@@ -0,0 +1,476 @@
+#include "clar_libgit2.h"
+#include "patch.h"
+#include "patch_parse.h"
+#include "diff_helpers.h"
+
+#include "../patch/patch_common.h"
+
+void test_diff_parse__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_diff_parse__nonpatches_fail_with_notfound(void)
+{
+ git_diff *diff;
+ const char *not = PATCH_NOT_A_PATCH;
+ const char *not_with_leading = "Leading text.\n" PATCH_NOT_A_PATCH;
+ const char *not_with_trailing = PATCH_NOT_A_PATCH "Trailing text.\n";
+ const char *not_with_both = "Lead.\n" PATCH_NOT_A_PATCH "Trail.\n";
+
+ cl_git_fail_with(GIT_ENOTFOUND,
+ diff_from_buffer(&diff,
+ not,
+ strlen(not)));
+ cl_git_fail_with(GIT_ENOTFOUND,
+ diff_from_buffer(&diff,
+ not_with_leading,
+ strlen(not_with_leading)));
+ cl_git_fail_with(GIT_ENOTFOUND,
+ diff_from_buffer(&diff,
+ not_with_trailing,
+ strlen(not_with_trailing)));
+ cl_git_fail_with(GIT_ENOTFOUND,
+ diff_from_buffer(&diff,
+ not_with_both,
+ strlen(not_with_both)));
+}
+
+static void test_parse_invalid_diff(const char *invalid_diff)
+{
+ git_diff *diff;
+ git_str buf = GIT_STR_INIT;
+
+ /* throw some random (legitimate) diffs in with the given invalid
+ * one.
+ */
+ git_str_puts(&buf, PATCH_ORIGINAL_TO_CHANGE_FIRSTLINE);
+ git_str_puts(&buf, PATCH_BINARY_DELTA);
+ git_str_puts(&buf, invalid_diff);
+ git_str_puts(&buf, PATCH_ORIGINAL_TO_CHANGE_MIDDLE);
+ git_str_puts(&buf, PATCH_BINARY_LITERAL);
+
+ cl_git_fail_with(GIT_ERROR,
+ diff_from_buffer(&diff, buf.ptr, buf.size));
+
+ git_str_dispose(&buf);
+}
+
+void test_diff_parse__exact_rename(void)
+{
+ const char *content =
+ "---\n"
+ " old_name.c => new_name.c | 0\n"
+ " 1 file changed, 0 insertions(+), 0 deletions(-)\n"
+ " rename old_name.c => new_name.c (100%)\n"
+ "\n"
+ "diff --git a/old_name.c b/new_name.c\n"
+ "similarity index 100%\n"
+ "rename from old_name.c\n"
+ "rename to new_name.c\n"
+ "-- \n"
+ "2.9.3\n";
+ git_diff *diff;
+
+ cl_git_pass(diff_from_buffer(
+ &diff, content, strlen(content)));
+ git_diff_free(diff);
+}
+
+void test_diff_parse__empty_file(void)
+{
+ const char *content =
+ "---\n"
+ " file | 0\n"
+ " 1 file changed, 0 insertions(+), 0 deletions(-)\n"
+ " created mode 100644 file\n"
+ "\n"
+ "diff --git a/file b/file\n"
+ "new file mode 100644\n"
+ "index 0000000..e69de29\n"
+ "-- \n"
+ "2.20.1\n";
+ git_diff *diff;
+
+ cl_git_pass(diff_from_buffer(
+ &diff, content, strlen(content)));
+ git_diff_free(diff);
+}
+
+void test_diff_parse__no_extended_headers(void)
+{
+ const char *content = PATCH_NO_EXTENDED_HEADERS;
+ git_diff *diff;
+
+ cl_git_pass(diff_from_buffer(
+ &diff, content, strlen(content)));
+ git_diff_free(diff);
+}
+
+void test_diff_parse__add_delete_no_index(void)
+{
+ const char *content =
+ "diff --git a/file.txt b/file.txt\n"
+ "new file mode 100644\n"
+ "--- /dev/null\n"
+ "+++ b/file.txt\n"
+ "@@ -0,0 +1,2 @@\n"
+ "+one\n"
+ "+two\n"
+ "diff --git a/otherfile.txt b/otherfile.txt\n"
+ "deleted file mode 100644\n"
+ "--- a/otherfile.txt\n"
+ "+++ /dev/null\n"
+ "@@ -1,1 +0,0 @@\n"
+ "-three\n";
+ git_diff *diff;
+
+ cl_git_pass(diff_from_buffer(
+ &diff, content, strlen(content)));
+ git_diff_free(diff);
+}
+
+void test_diff_parse__invalid_patches_fails(void)
+{
+ test_parse_invalid_diff(PATCH_CORRUPT_MISSING_NEW_FILE);
+ test_parse_invalid_diff(PATCH_CORRUPT_MISSING_OLD_FILE);
+ test_parse_invalid_diff(PATCH_CORRUPT_NO_CHANGES);
+ test_parse_invalid_diff(PATCH_CORRUPT_MISSING_HUNK_HEADER);
+}
+
+static void test_tree_to_tree_computed_to_parsed(
+ const char *sandbox, const char *a_id, const char *b_id,
+ uint32_t diff_flags, uint32_t find_flags)
+{
+ git_repository *repo;
+ git_diff *computed, *parsed;
+ git_tree *a, *b;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
+ git_buf computed_buf = GIT_BUF_INIT;
+
+ repo = cl_git_sandbox_init(sandbox);
+
+ opts.id_abbrev = GIT_OID_SHA1_HEXSIZE;
+ opts.flags = GIT_DIFF_SHOW_BINARY | diff_flags;
+ findopts.flags = find_flags;
+
+ cl_assert((a = resolve_commit_oid_to_tree(repo, a_id)) != NULL);
+ cl_assert((b = resolve_commit_oid_to_tree(repo, b_id)) != NULL);
+
+ cl_git_pass(git_diff_tree_to_tree(&computed, repo, a, b, &opts));
+
+ if (find_flags)
+ cl_git_pass(git_diff_find_similar(computed, &findopts));
+
+ cl_git_pass(git_diff_to_buf(&computed_buf,
+ computed, GIT_DIFF_FORMAT_PATCH));
+
+ cl_git_pass(diff_from_buffer(&parsed,
+ computed_buf.ptr, computed_buf.size));
+
+ diff_assert_equal(computed, parsed);
+
+ git_tree_free(a);
+ git_tree_free(b);
+
+ git_diff_free(computed);
+ git_diff_free(parsed);
+
+ git_buf_dispose(&computed_buf);
+
+ cl_git_sandbox_cleanup();
+}
+
+void test_diff_parse__can_parse_generated_diff(void)
+{
+ test_tree_to_tree_computed_to_parsed(
+ "diff", "d70d245e", "7a9e0b02", 0, 0);
+ test_tree_to_tree_computed_to_parsed(
+ "unsymlinked.git", "806999", "a8595c", 0, 0);
+ test_tree_to_tree_computed_to_parsed("diff",
+ "d70d245ed97ed2aa596dd1af6536e4bfdb047b69",
+ "7a9e0b02e63179929fed24f0a3e0f19168114d10", 0, 0);
+ test_tree_to_tree_computed_to_parsed(
+ "unsymlinked.git", "7fccd7", "806999", 0, 0);
+ test_tree_to_tree_computed_to_parsed(
+ "unsymlinked.git", "7fccd7", "a8595c", 0, 0);
+ test_tree_to_tree_computed_to_parsed(
+ "attr", "605812a", "370fe9ec22", 0, 0);
+ test_tree_to_tree_computed_to_parsed(
+ "attr", "f5b0af1fb4f5c", "370fe9ec22", 0, 0);
+ test_tree_to_tree_computed_to_parsed(
+ "diff", "d70d245e", "d70d245e", 0, 0);
+ test_tree_to_tree_computed_to_parsed("diff_format_email",
+ "873806f6f27e631eb0b23e4b56bea2bfac14a373",
+ "897d3af16ca9e420cd071b1c4541bd2b91d04c8c",
+ GIT_DIFF_SHOW_BINARY, 0);
+ test_tree_to_tree_computed_to_parsed("diff_format_email",
+ "897d3af16ca9e420cd071b1c4541bd2b91d04c8c",
+ "873806f6f27e631eb0b23e4b56bea2bfac14a373",
+ GIT_DIFF_SHOW_BINARY, 0);
+ test_tree_to_tree_computed_to_parsed("renames",
+ "31e47d8c1fa36d7f8d537b96158e3f024de0a9f2",
+ "2bc7f351d20b53f1c72c16c4b036e491c478c49a",
+ 0, GIT_DIFF_FIND_RENAMES);
+ test_tree_to_tree_computed_to_parsed("renames",
+ "31e47d8c1fa36d7f8d537b96158e3f024de0a9f2",
+ "2bc7f351d20b53f1c72c16c4b036e491c478c49a",
+ GIT_DIFF_INCLUDE_UNMODIFIED,
+ 0);
+ test_tree_to_tree_computed_to_parsed("renames",
+ "31e47d8c1fa36d7f8d537b96158e3f024de0a9f2",
+ "2bc7f351d20b53f1c72c16c4b036e491c478c49a",
+ GIT_DIFF_INCLUDE_UNMODIFIED,
+ GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED | GIT_DIFF_FIND_EXACT_MATCH_ONLY);
+}
+
+void test_diff_parse__get_patch_from_diff(void)
+{
+ git_repository *repo;
+ git_diff *computed, *parsed;
+ git_tree *a, *b;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_buf computed_buf = GIT_BUF_INIT;
+ git_patch *patch_computed, *patch_parsed;
+
+ repo = cl_git_sandbox_init("diff");
+
+ opts.flags = GIT_DIFF_SHOW_BINARY;
+
+ cl_assert((a = resolve_commit_oid_to_tree(repo,
+ "d70d245ed97ed2aa596dd1af6536e4bfdb047b69")) != NULL);
+ cl_assert((b = resolve_commit_oid_to_tree(repo,
+ "7a9e0b02e63179929fed24f0a3e0f19168114d10")) != NULL);
+
+ cl_git_pass(git_diff_tree_to_tree(&computed, repo, a, b, &opts));
+ cl_git_pass(git_diff_to_buf(&computed_buf,
+ computed, GIT_DIFF_FORMAT_PATCH));
+ cl_git_pass(git_patch_from_diff(&patch_computed, computed, 0));
+
+ cl_git_pass(diff_from_buffer(&parsed,
+ computed_buf.ptr, computed_buf.size));
+ cl_git_pass(git_patch_from_diff(&patch_parsed, parsed, 0));
+
+ cl_assert_equal_i(
+ git_patch_num_hunks(patch_computed),
+ git_patch_num_hunks(patch_parsed));
+
+ git_patch_free(patch_computed);
+ git_patch_free(patch_parsed);
+
+ git_tree_free(a);
+ git_tree_free(b);
+
+ git_diff_free(computed);
+ git_diff_free(parsed);
+
+ git_buf_dispose(&computed_buf);
+
+ cl_git_sandbox_cleanup();
+}
+
+static int file_cb(const git_diff_delta *delta, float progress, void *payload)
+{
+ int *called = (int *) payload;
+ GIT_UNUSED(delta);
+ GIT_UNUSED(progress);
+ (*called)++;
+ return 0;
+}
+
+void test_diff_parse__foreach_works_with_parsed_patch(void)
+{
+ const char patch[] =
+ "diff --git a/obj1 b/obj2\n"
+ "index 1234567..7654321 10644\n"
+ "--- a/obj1\n"
+ "+++ b/obj2\n"
+ "@@ -1 +1 @@\n"
+ "-abcde\n"
+ "+12345\n";
+ int called = 0;
+ git_diff *diff;
+
+ cl_git_pass(diff_from_buffer(&diff, patch, strlen(patch)));
+ cl_git_pass(git_diff_foreach(diff, file_cb, NULL, NULL, NULL, &called));
+ cl_assert_equal_i(called, 1);
+
+ git_diff_free(diff);
+}
+
+void test_diff_parse__parsing_minimal_patch_succeeds(void)
+{
+ const char patch[] =
+ "diff --git a/obj1 b/obj2\n"
+ "index 1234567..7654321 10644\n"
+ "--- a/obj1\n"
+ "+++ b/obj2\n"
+ "@@ -1 +1 @@\n"
+ "-a\n"
+ "+\n";
+ git_buf buf = GIT_BUF_INIT;
+ git_diff *diff;
+
+ cl_git_pass(diff_from_buffer(&diff, patch, strlen(patch)));
+ cl_git_pass(git_diff_to_buf(&buf, diff, GIT_DIFF_FORMAT_PATCH));
+ cl_assert_equal_s(patch, buf.ptr);
+
+ git_diff_free(diff);
+ git_buf_dispose(&buf);
+}
+
+void test_diff_parse__patch_roundtrip_succeeds(void)
+{
+ const char buf1[] = "a\n", buf2[] = "b\n";
+ git_buf patchbuf = GIT_BUF_INIT, diffbuf = GIT_BUF_INIT;
+ git_patch *patch;
+ git_diff *diff;
+
+ cl_git_pass(git_patch_from_buffers(&patch, buf1, strlen(buf1), "obj1", buf2, strlen(buf2), "obj2", NULL));
+ cl_git_pass(git_patch_to_buf(&patchbuf, patch));
+
+ cl_git_pass(diff_from_buffer(&diff, patchbuf.ptr, patchbuf.size));
+ cl_git_pass(git_diff_to_buf(&diffbuf, diff, GIT_DIFF_FORMAT_PATCH));
+
+ cl_assert_equal_s(patchbuf.ptr, diffbuf.ptr);
+
+ git_patch_free(patch);
+ git_diff_free(diff);
+ git_buf_dispose(&patchbuf);
+ git_buf_dispose(&diffbuf);
+}
+
+#define cl_assert_equal_i_src(i1,i2,file,func,line) clar__assert_equal(file,func,line,#i1 " != " #i2, 1, "%d", (int)(i1), (int)(i2))
+
+static void cl_git_assert_lineinfo_(int old_lineno, int new_lineno, int num_lines, git_patch *patch, size_t hunk_idx, size_t line_idx, const char *file, const char *func, int lineno)
+{
+ const git_diff_line *line;
+
+ cl_git_expect(git_patch_get_line_in_hunk(&line, patch, hunk_idx, line_idx), 0, file, func, lineno);
+ cl_assert_equal_i_src(old_lineno, line->old_lineno, file, func, lineno);
+ cl_assert_equal_i_src(new_lineno, line->new_lineno, file, func, lineno);
+ cl_assert_equal_i_src(num_lines, line->num_lines, file, func, lineno);
+}
+
+#define cl_git_assert_lineinfo(old, new, num, p, h, l) \
+ cl_git_assert_lineinfo_(old,new,num,p,h,l,__FILE__,__func__,__LINE__)
+
+
+void test_diff_parse__issue4672(void)
+{
+ const char *text = "diff --git a/a b/a\n"
+ "index 7f129fd..af431f2 100644\n"
+ "--- a/a\n"
+ "+++ b/a\n"
+ "@@ -3 +3 @@\n"
+ "-a contents 2\n"
+ "+a contents\n";
+
+ git_diff *diff;
+ git_patch *patch;
+ const git_diff_hunk *hunk;
+ size_t n, l = 0;
+
+ cl_git_pass(diff_from_buffer(&diff, text, strlen(text)));
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_get_hunk(&hunk, &n, patch, 0));
+
+ cl_git_assert_lineinfo(3, -1, 1, patch, 0, l++);
+ cl_git_assert_lineinfo(-1, 3, 1, patch, 0, l++);
+
+ cl_assert_equal_i(n, l);
+
+ git_patch_free(patch);
+ git_diff_free(diff);
+}
+
+void test_diff_parse__lineinfo(void)
+{
+ const char *text = PATCH_ORIGINAL_TO_CHANGE_MIDDLE;
+ git_diff *diff;
+ git_patch *patch;
+ const git_diff_hunk *hunk;
+ size_t n, l = 0;
+
+ cl_git_pass(diff_from_buffer(&diff, text, strlen(text)));
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_get_hunk(&hunk, &n, patch, 0));
+
+ cl_git_assert_lineinfo(3, 3, 1, patch, 0, l++);
+ cl_git_assert_lineinfo(4, 4, 1, patch, 0, l++);
+ cl_git_assert_lineinfo(5, 5, 1, patch, 0, l++);
+ cl_git_assert_lineinfo(6, -1, 1, patch, 0, l++);
+ cl_git_assert_lineinfo(-1, 6, 1, patch, 0, l++);
+ cl_git_assert_lineinfo(7, 7, 1, patch, 0, l++);
+ cl_git_assert_lineinfo(8, 8, 1, patch, 0, l++);
+ cl_git_assert_lineinfo(9, 9, 1, patch, 0, l++);
+
+ cl_assert_equal_i(n, l);
+
+ git_patch_free(patch);
+ git_diff_free(diff);
+}
+
+
+void test_diff_parse__new_file_with_space(void)
+{
+ const char *content = PATCH_ORIGINAL_NEW_FILE_WITH_SPACE;
+ git_patch *patch;
+ git_diff *diff;
+
+ cl_git_pass(diff_from_buffer(&diff, content, strlen(content)));
+ cl_git_pass(git_patch_from_diff((git_patch **) &patch, diff, 0));
+
+ cl_assert_equal_p(patch->diff_opts.old_prefix, NULL);
+ cl_assert_equal_p(patch->delta->old_file.path, NULL);
+ cl_assert_equal_s(patch->diff_opts.new_prefix, "b/");
+ cl_assert_equal_s(patch->delta->new_file.path, "sp ace.txt");
+
+ git_patch_free(patch);
+ git_diff_free(diff);
+}
+
+void test_diff_parse__new_file_with_space_and_regenerate_patch(void)
+{
+ const char *content = PATCH_ORIGINAL_NEW_FILE_WITH_SPACE;
+ git_diff *diff = NULL;
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_pass(diff_from_buffer(&diff, content, strlen(content)));
+ cl_git_pass(git_diff_to_buf(&buf, diff, GIT_DIFF_FORMAT_PATCH));
+
+ git_buf_dispose(&buf);
+ git_diff_free(diff);
+}
+
+void test_diff_parse__delete_file_with_space_and_regenerate_patch(void)
+{
+ const char *content = PATCH_DELETE_FILE_WITH_SPACE;
+ git_diff *diff = NULL;
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_pass(diff_from_buffer(&diff, content, strlen(content)));
+ cl_git_pass(git_diff_to_buf(&buf, diff, GIT_DIFF_FORMAT_PATCH));
+
+ git_buf_dispose(&buf);
+ git_diff_free(diff);
+}
+
+void test_diff_parse__crlf(void)
+{
+ const char *text = PATCH_CRLF;
+ git_diff *diff;
+ git_patch *patch;
+ const git_diff_delta *delta;
+
+ cl_git_pass(diff_from_buffer(&diff, text, strlen(text)));
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ delta = git_patch_get_delta(patch);
+
+ cl_assert_equal_s(delta->old_file.path, "test-file");
+ cl_assert_equal_s(delta->new_file.path, "test-file");
+
+ git_patch_free(patch);
+ git_diff_free(diff);
+}
diff --git a/tests/libgit2/diff/patch.c b/tests/libgit2/diff/patch.c
new file mode 100644
index 0000000..8945afc
--- /dev/null
+++ b/tests/libgit2/diff/patch.c
@@ -0,0 +1,703 @@
+#include "clar_libgit2.h"
+#include "git2/sys/repository.h"
+
+#include "diff_helpers.h"
+#include "diff.h"
+#include "repository.h"
+
+static git_repository *g_repo = NULL;
+
+void test_diff_patch__initialize(void)
+{
+}
+
+void test_diff_patch__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+#define EXPECTED_HEADER "diff --git a/subdir.txt b/subdir.txt\n" \
+ "deleted file mode 100644\n" \
+ "index e8ee89e..0000000\n" \
+ "--- a/subdir.txt\n" \
+ "+++ /dev/null\n"
+
+#define EXPECTED_HUNK "@@ -1,2 +0,0 @@\n"
+
+#define UTF8_HUNK_HEADER "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\n"
+
+#define UTF8_TRUNCATED_A_HUNK_HEADER "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\n"
+
+#define UTF8_TRUNCATED_L_HUNK_HEADER "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E\xE6\x97\xA5\n"
+
+static int check_removal_cb(
+ const git_diff_delta *delta,
+ const git_diff_hunk *hunk,
+ const git_diff_line *line,
+ void *payload)
+{
+ switch (line->origin) {
+ case GIT_DIFF_LINE_FILE_HDR:
+ cl_assert_equal_s(EXPECTED_HEADER, line->content);
+ cl_assert(hunk == NULL);
+ goto check_delta;
+
+ case GIT_DIFF_LINE_HUNK_HDR:
+ cl_assert_equal_s(EXPECTED_HUNK, line->content);
+ goto check_hunk;
+
+ case GIT_DIFF_LINE_CONTEXT:
+ case GIT_DIFF_LINE_DELETION:
+ if (payload != NULL)
+ return *(int *)payload;
+ goto check_hunk;
+
+ default:
+ /* unexpected code path */
+ return -1;
+ }
+
+check_hunk:
+ cl_assert(hunk != NULL);
+ cl_assert_equal_i(1, hunk->old_start);
+ cl_assert_equal_i(2, hunk->old_lines);
+ cl_assert_equal_i(0, hunk->new_start);
+ cl_assert_equal_i(0, hunk->new_lines);
+
+check_delta:
+ cl_assert_equal_s("subdir.txt", delta->old_file.path);
+ cl_assert_equal_s("subdir.txt", delta->new_file.path);
+ cl_assert_equal_i(GIT_DELTA_DELETED, delta->status);
+
+ return 0;
+}
+
+void test_diff_patch__can_properly_display_the_removal_of_a_file(void)
+{
+ /*
+ * $ git diff 26a125e..735b6a2
+ * diff --git a/subdir.txt b/subdir.txt
+ * deleted file mode 100644
+ * index e8ee89e..0000000
+ * --- a/subdir.txt
+ * +++ /dev/null
+ * @@ -1,2 +0,0 @@
+ * -Is it a bird?
+ * -Is it a plane?
+ */
+
+ const char *one_sha = "26a125e";
+ const char *another_sha = "735b6a2";
+ git_tree *one, *another;
+ git_diff *diff;
+
+ g_repo = cl_git_sandbox_init("status");
+
+ one = resolve_commit_oid_to_tree(g_repo, one_sha);
+ another = resolve_commit_oid_to_tree(g_repo, another_sha);
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, one, another, NULL));
+
+ cl_git_pass(git_diff_print(
+ diff, GIT_DIFF_FORMAT_PATCH, check_removal_cb, NULL));
+
+ git_diff_free(diff);
+
+ git_tree_free(another);
+ git_tree_free(one);
+}
+
+void test_diff_patch__can_cancel_diff_print(void)
+{
+ const char *one_sha = "26a125e";
+ const char *another_sha = "735b6a2";
+ git_tree *one, *another;
+ git_diff *diff;
+ int fail_with;
+
+ g_repo = cl_git_sandbox_init("status");
+
+ one = resolve_commit_oid_to_tree(g_repo, one_sha);
+ another = resolve_commit_oid_to_tree(g_repo, another_sha);
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, one, another, NULL));
+
+ fail_with = -2323;
+
+ cl_git_fail_with(git_diff_print(
+ diff, GIT_DIFF_FORMAT_PATCH, check_removal_cb, &fail_with),
+ fail_with);
+
+ fail_with = 45;
+
+ cl_git_fail_with(git_diff_print(
+ diff, GIT_DIFF_FORMAT_PATCH, check_removal_cb, &fail_with),
+ fail_with);
+
+ git_diff_free(diff);
+
+ git_tree_free(another);
+ git_tree_free(one);
+}
+
+void test_diff_patch__to_string(void)
+{
+ const char *one_sha = "26a125e";
+ const char *another_sha = "735b6a2";
+ git_tree *one, *another;
+ git_diff *diff;
+ git_patch *patch;
+ git_buf buf = GIT_BUF_INIT;
+ const char *expected = "diff --git a/subdir.txt b/subdir.txt\ndeleted file mode 100644\nindex e8ee89e..0000000\n--- a/subdir.txt\n+++ /dev/null\n@@ -1,2 +0,0 @@\n-Is it a bird?\n-Is it a plane?\n";
+
+ g_repo = cl_git_sandbox_init("status");
+
+ one = resolve_commit_oid_to_tree(g_repo, one_sha);
+ another = resolve_commit_oid_to_tree(g_repo, another_sha);
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, one, another, NULL));
+
+ cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+
+ cl_git_pass(git_patch_to_buf(&buf, patch));
+
+ cl_assert_equal_s(expected, buf.ptr);
+
+ cl_assert_equal_sz(31, git_patch_size(patch, 0, 0, 0));
+ cl_assert_equal_sz(31, git_patch_size(patch, 1, 0, 0));
+ cl_assert_equal_sz(31 + 16, git_patch_size(patch, 1, 1, 0));
+ cl_assert_equal_sz(strlen(expected), git_patch_size(patch, 1, 1, 1));
+
+ git_buf_dispose(&buf);
+ git_patch_free(patch);
+ git_diff_free(diff);
+ git_tree_free(another);
+ git_tree_free(one);
+}
+
+void test_diff_patch__config_options(void)
+{
+ const char *one_sha = "26a125e"; /* current HEAD */
+ git_tree *one;
+ git_config *cfg;
+ git_diff *diff;
+ git_patch *patch;
+ git_buf buf = GIT_BUF_INIT;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ char *onefile = "staged_changes_modified_file";
+ const char *expected1 = "diff --git c/staged_changes_modified_file i/staged_changes_modified_file\nindex 70bd944..906ee77 100644\n--- c/staged_changes_modified_file\n+++ i/staged_changes_modified_file\n@@ -1 +1,2 @@\n staged_changes_modified_file\n+staged_changes_modified_file\n";
+ const char *expected2 = "diff --git i/staged_changes_modified_file w/staged_changes_modified_file\nindex 906ee77..011c344 100644\n--- i/staged_changes_modified_file\n+++ w/staged_changes_modified_file\n@@ -1,2 +1,3 @@\n staged_changes_modified_file\n staged_changes_modified_file\n+staged_changes_modified_file\n";
+ const char *expected3 = "diff --git staged_changes_modified_file staged_changes_modified_file\nindex 906ee77..011c344 100644\n--- staged_changes_modified_file\n+++ staged_changes_modified_file\n@@ -1,2 +1,3 @@\n staged_changes_modified_file\n staged_changes_modified_file\n+staged_changes_modified_file\n";
+ const char *expected4 = "diff --git staged_changes_modified_file staged_changes_modified_file\nindex 70bd9443ada0..906ee7711f4f 100644\n--- staged_changes_modified_file\n+++ staged_changes_modified_file\n@@ -1 +1,2 @@\n staged_changes_modified_file\n+staged_changes_modified_file\n";
+
+ g_repo = cl_git_sandbox_init("status");
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ one = resolve_commit_oid_to_tree(g_repo, one_sha);
+ opts.pathspec.count = 1;
+ opts.pathspec.strings = &onefile;
+
+
+ cl_git_pass(git_config_set_string(cfg, "diff.mnemonicprefix", "true"));
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, one, NULL, &opts));
+
+ cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_to_buf(&buf, patch));
+ cl_assert_equal_s(expected1, buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_patch_free(patch);
+ git_diff_free(diff);
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_to_buf(&buf, patch));
+ cl_assert_equal_s(expected2, buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_patch_free(patch);
+ git_diff_free(diff);
+
+
+ cl_git_pass(git_config_set_string(cfg, "diff.noprefix", "true"));
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_to_buf(&buf, patch));
+ cl_assert_equal_s(expected3, buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_patch_free(patch);
+ git_diff_free(diff);
+
+
+ cl_git_pass(git_config_set_int32(cfg, "core.abbrev", 12));
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, one, NULL, &opts));
+
+ cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_to_buf(&buf, patch));
+ cl_assert_equal_s(expected4, buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_patch_free(patch);
+ git_diff_free(diff);
+
+ git_buf_dispose(&buf);
+ git_tree_free(one);
+ git_config_free(cfg);
+}
+
+void test_diff_patch__hunks_have_correct_line_numbers(void)
+{
+ git_config *cfg;
+ git_tree *head;
+ git_diff_options opt = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff;
+ git_patch *patch;
+ const git_diff_delta *delta;
+ const git_diff_hunk *hunk;
+ const git_diff_line *line;
+ size_t hunklen;
+ git_str old_content = GIT_STR_INIT, actual = GIT_STR_INIT;
+ const char *new_content = "The Song of Seven Cities\n------------------------\n\nI WAS Lord of Cities very sumptuously builded.\nSeven roaring Cities paid me tribute from afar.\nIvory their outposts were--the guardrooms of them gilded,\nAnd garrisoned with Amazons invincible in war.\n\nThis is some new text;\nNot as good as the old text;\nBut here it is.\n\nSo they warred and trafficked only yesterday, my Cities.\nTo-day there is no mark or mound of where my Cities stood.\nFor the River rose at midnight and it washed away my Cities.\nThey are evened with Atlantis and the towns before the Flood.\n\nRain on rain-gorged channels raised the water-levels round them,\nFreshet backed on freshet swelled and swept their world from sight,\nTill the emboldened floods linked arms and, flashing forward, drowned them--\nDrowned my Seven Cities and their peoples in one night!\n\nLow among the alders lie their derelict foundations,\nThe beams wherein they trusted and the plinths whereon they built--\nMy rulers and their treasure and their unborn populations,\nDead, destroyed, aborted, and defiled with mud and silt!\n\nAnother replacement;\nBreaking up the poem;\nGenerating some hunks.\n\nTo the sound of trumpets shall their seed restore my Cities\nWealthy and well-weaponed, that once more may I behold\nAll the world go softly when it walks before my Cities,\nAnd the horses and the chariots fleeing from them as of old!\n\n -- Rudyard Kipling\n";
+
+ g_repo = cl_git_sandbox_init("renames");
+
+ cl_git_pass(git_config_new(&cfg));
+ git_repository_set_config(g_repo, cfg);
+ git_config_free(cfg);
+
+ git_repository_reinit_filesystem(g_repo, false);
+
+ cl_git_pass(
+ git_futils_readbuffer(&old_content, "renames/songof7cities.txt"));
+
+ cl_git_rewritefile("renames/songof7cities.txt", new_content);
+
+ cl_git_pass(git_repository_head_tree(&head, g_repo));
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, head, &opt));
+
+ cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_assert((delta = git_patch_get_delta(patch)) != NULL);
+
+ cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status);
+ cl_assert_equal_i(2, (int)git_patch_num_hunks(patch));
+
+ /* check hunk 0 */
+
+ cl_git_pass(
+ git_patch_get_hunk(&hunk, &hunklen, patch, 0));
+
+ cl_assert_equal_i(18, (int)hunklen);
+
+ cl_assert_equal_i(6, (int)hunk->old_start);
+ cl_assert_equal_i(15, (int)hunk->old_lines);
+ cl_assert_equal_i(6, (int)hunk->new_start);
+ cl_assert_equal_i(9, (int)hunk->new_lines);
+
+ cl_assert_equal_i(18, (int)git_patch_num_lines_in_hunk(patch, 0));
+
+ cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 0));
+ cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)line->origin);
+ cl_git_pass(git_str_set(&actual, line->content, line->content_len));
+ cl_assert_equal_s("Ivory their outposts were--the guardrooms of them gilded,\n", actual.ptr);
+ cl_assert_equal_i(6, line->old_lineno);
+ cl_assert_equal_i(6, line->new_lineno);
+ cl_assert_equal_i(-1, line->content_offset);
+
+ cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 3));
+ cl_assert_equal_i(GIT_DIFF_LINE_DELETION, (int)line->origin);
+ cl_git_pass(git_str_set(&actual, line->content, line->content_len));
+ cl_assert_equal_s("All the world went softly when it walked before my Cities--\n", actual.ptr);
+ cl_assert_equal_i(9, line->old_lineno);
+ cl_assert_equal_i(-1, line->new_lineno);
+ cl_assert_equal_i(252, line->content_offset);
+
+ cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 12));
+ cl_assert_equal_i(GIT_DIFF_LINE_ADDITION, (int)line->origin);
+ cl_git_pass(git_str_set(&actual, line->content, line->content_len));
+ cl_assert_equal_s("This is some new text;\n", actual.ptr);
+ cl_assert_equal_i(-1, line->old_lineno);
+ cl_assert_equal_i(9, line->new_lineno);
+ cl_assert_equal_i(252, line->content_offset);
+
+ /* check hunk 1 */
+
+ cl_git_pass(git_patch_get_hunk(&hunk, &hunklen, patch, 1));
+
+ cl_assert_equal_i(18, (int)hunklen);
+
+ cl_assert_equal_i(31, (int)hunk->old_start);
+ cl_assert_equal_i(15, (int)hunk->old_lines);
+ cl_assert_equal_i(25, (int)hunk->new_start);
+ cl_assert_equal_i(9, (int)hunk->new_lines);
+
+ cl_assert_equal_i(18, (int)git_patch_num_lines_in_hunk(patch, 1));
+
+ cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 1, 0));
+ cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)line->origin);
+ cl_git_pass(git_str_set(&actual, line->content, line->content_len));
+ cl_assert_equal_s("My rulers and their treasure and their unborn populations,\n", actual.ptr);
+ cl_assert_equal_i(31, line->old_lineno);
+ cl_assert_equal_i(25, line->new_lineno);
+ cl_assert_equal_i(-1, line->content_offset);
+
+ cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 1, 3));
+ cl_assert_equal_i(GIT_DIFF_LINE_DELETION, (int)line->origin);
+ cl_git_pass(git_str_set(&actual, line->content, line->content_len));
+ cl_assert_equal_s("The Daughters of the Palace whom they cherished in my Cities,\n", actual.ptr);
+ cl_assert_equal_i(34, line->old_lineno);
+ cl_assert_equal_i(-1, line->new_lineno);
+ cl_assert_equal_i(1468, line->content_offset);
+
+ cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 1, 12));
+ cl_assert_equal_i(GIT_DIFF_LINE_ADDITION, (int)line->origin);
+ cl_git_pass(git_str_set(&actual, line->content, line->content_len));
+ cl_assert_equal_s("Another replacement;\n", actual.ptr);
+ cl_assert_equal_i(-1, line->old_lineno);
+ cl_assert_equal_i(28, line->new_lineno);
+ cl_assert_equal_i(1066, line->content_offset);
+
+ git_patch_free(patch);
+ git_diff_free(diff);
+
+ /* Let's check line numbers when there is no newline */
+
+ git_str_rtrim(&old_content);
+ cl_git_rewritefile("renames/songof7cities.txt", old_content.ptr);
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, head, &opt));
+
+ cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_assert((delta = git_patch_get_delta(patch)) != NULL);
+
+ cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status);
+ cl_assert_equal_i(1, (int)git_patch_num_hunks(patch));
+
+ /* check hunk 0 */
+
+ cl_git_pass(git_patch_get_hunk(&hunk, &hunklen, patch, 0));
+
+ cl_assert_equal_i(6, (int)hunklen);
+
+ cl_assert_equal_i(46, (int)hunk->old_start);
+ cl_assert_equal_i(4, (int)hunk->old_lines);
+ cl_assert_equal_i(46, (int)hunk->new_start);
+ cl_assert_equal_i(4, (int)hunk->new_lines);
+
+ cl_assert_equal_i(6, (int)git_patch_num_lines_in_hunk(patch, 0));
+
+ cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 1));
+ cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)line->origin);
+ cl_git_pass(git_str_set(&actual, line->content, line->content_len));
+ cl_assert_equal_s("And the horses and the chariots fleeing from them as of old!\n", actual.ptr);
+ cl_assert_equal_i(47, line->old_lineno);
+ cl_assert_equal_i(47, line->new_lineno);
+
+ cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 2));
+ cl_assert_equal_i(GIT_DIFF_LINE_CONTEXT, (int)line->origin);
+ cl_git_pass(git_str_set(&actual, line->content, line->content_len));
+ cl_assert_equal_s("\n", actual.ptr);
+ cl_assert_equal_i(48, line->old_lineno);
+ cl_assert_equal_i(48, line->new_lineno);
+
+ cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 3));
+ cl_assert_equal_i(GIT_DIFF_LINE_DELETION, (int)line->origin);
+ cl_git_pass(git_str_set(&actual, line->content, line->content_len));
+ cl_assert_equal_s(" -- Rudyard Kipling\n", actual.ptr);
+ cl_assert_equal_i(49, line->old_lineno);
+ cl_assert_equal_i(-1, line->new_lineno);
+
+ cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 4));
+ cl_assert_equal_i(GIT_DIFF_LINE_ADDITION, (int)line->origin);
+ cl_git_pass(git_str_set(&actual, line->content, line->content_len));
+ cl_assert_equal_s(" -- Rudyard Kipling", actual.ptr);
+ cl_assert_equal_i(-1, line->old_lineno);
+ cl_assert_equal_i(49, line->new_lineno);
+
+ cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 5));
+ cl_assert_equal_i(GIT_DIFF_LINE_DEL_EOFNL, (int)line->origin);
+ cl_git_pass(git_str_set(&actual, line->content, line->content_len));
+ cl_assert_equal_s("\n\\ No newline at end of file\n", actual.ptr);
+ cl_assert_equal_i(-1, line->old_lineno);
+ cl_assert_equal_i(49, line->new_lineno);
+
+ git_patch_free(patch);
+ git_diff_free(diff);
+
+ git_str_dispose(&actual);
+ git_str_dispose(&old_content);
+ git_tree_free(head);
+}
+
+static void check_single_patch_stats(
+ git_repository *repo, size_t hunks,
+ size_t adds, size_t dels, size_t ctxt, size_t *sizes,
+ const char *expected)
+{
+ git_diff *diff;
+ git_patch *patch;
+ const git_diff_delta *delta;
+ size_t actual_ctxt, actual_adds, actual_dels;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, NULL));
+
+ cl_assert_equal_i(1, (int)git_diff_num_deltas(diff));
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_assert((delta = git_patch_get_delta(patch)) != NULL);
+ cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status);
+
+ cl_assert_equal_i((int)hunks, (int)git_patch_num_hunks(patch));
+
+ cl_git_pass( git_patch_line_stats(
+ &actual_ctxt, &actual_adds, &actual_dels, patch) );
+
+ cl_assert_equal_sz(ctxt, actual_ctxt);
+ cl_assert_equal_sz(adds, actual_adds);
+ cl_assert_equal_sz(dels, actual_dels);
+
+ if (expected != NULL) {
+ git_buf buf = GIT_BUF_INIT;
+ cl_git_pass(git_patch_to_buf(&buf, patch));
+ cl_assert_equal_s(expected, buf.ptr);
+ git_buf_dispose(&buf);
+
+ cl_assert_equal_sz(
+ strlen(expected), git_patch_size(patch, 1, 1, 1));
+ }
+
+ if (sizes) {
+ if (sizes[0])
+ cl_assert_equal_sz(sizes[0], git_patch_size(patch, 0, 0, 0));
+ if (sizes[1])
+ cl_assert_equal_sz(sizes[1], git_patch_size(patch, 1, 0, 0));
+ if (sizes[2])
+ cl_assert_equal_sz(sizes[2], git_patch_size(patch, 1, 1, 0));
+ }
+
+ /* walk lines in hunk with basic sanity checks */
+ for (; hunks > 0; --hunks) {
+ size_t i, max_i;
+ const git_diff_line *line;
+ int last_new_lineno = -1, last_old_lineno = -1;
+
+ max_i = git_patch_num_lines_in_hunk(patch, hunks - 1);
+
+ for (i = 0; i < max_i; ++i) {
+ int expected = 1;
+
+ cl_git_pass(
+ git_patch_get_line_in_hunk(&line, patch, hunks - 1, i));
+
+ if (line->origin == GIT_DIFF_LINE_ADD_EOFNL ||
+ line->origin == GIT_DIFF_LINE_DEL_EOFNL ||
+ line->origin == GIT_DIFF_LINE_CONTEXT_EOFNL)
+ expected = 0;
+
+ if (line->old_lineno >= 0) {
+ if (last_old_lineno >= 0)
+ cl_assert_equal_i(
+ expected, line->old_lineno - last_old_lineno);
+ last_old_lineno = line->old_lineno;
+ }
+
+ if (line->new_lineno >= 0) {
+ if (last_new_lineno >= 0)
+ cl_assert_equal_i(
+ expected, line->new_lineno - last_new_lineno);
+ last_new_lineno = line->new_lineno;
+ }
+ }
+ }
+
+ git_patch_free(patch);
+ git_diff_free(diff);
+}
+
+void test_diff_patch__line_counts_with_eofnl(void)
+{
+ git_config *cfg;
+ git_str content = GIT_STR_INIT;
+ const char *end;
+ git_index *index;
+ const char *expected =
+ /* below is pasted output of 'git diff' with fn context removed */
+ "diff --git a/songof7cities.txt b/songof7cities.txt\n"
+ "index 378a7d9..3d0154e 100644\n"
+ "--- a/songof7cities.txt\n"
+ "+++ b/songof7cities.txt\n"
+ "@@ -42,7 +42,7 @@ With peoples undefeated of the dark, enduring blood.\n"
+ " \n"
+ " To the sound of trumpets shall their seed restore my Cities\n"
+ " Wealthy and well-weaponed, that once more may I behold\n"
+ "-All the world go softly when it walks before my Cities,\n"
+ "+#All the world go softly when it walks before my Cities,\n"
+ " And the horses and the chariots fleeing from them as of old!\n"
+ " \n"
+ " -- Rudyard Kipling\n"
+ "\\ No newline at end of file\n";
+ size_t expected_sizes[3] = { 115, 119 + 115 + 114, 119 + 115 + 114 + 71 };
+
+ g_repo = cl_git_sandbox_init("renames");
+
+ cl_git_pass(git_config_new(&cfg));
+ git_repository_set_config(g_repo, cfg);
+ git_config_free(cfg);
+
+ git_repository_reinit_filesystem(g_repo, false);
+
+ cl_git_pass(git_futils_readbuffer(&content, "renames/songof7cities.txt"));
+
+ /* remove first line */
+
+ end = git_str_cstr(&content) + git_str_find(&content, '\n') + 1;
+ git_str_consume(&content, end);
+ cl_git_rewritefile("renames/songof7cities.txt", content.ptr);
+
+ check_single_patch_stats(g_repo, 1, 0, 1, 3, NULL, NULL);
+
+ /* remove trailing whitespace */
+
+ git_str_rtrim(&content);
+ cl_git_rewritefile("renames/songof7cities.txt", content.ptr);
+
+ check_single_patch_stats(g_repo, 2, 1, 2, 6, NULL, NULL);
+
+ /* add trailing whitespace */
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_add_bypath(index, "songof7cities.txt"));
+ cl_git_pass(git_index_write(index));
+ git_index_free(index);
+
+ cl_git_pass(git_str_putc(&content, '\n'));
+ cl_git_rewritefile("renames/songof7cities.txt", content.ptr);
+
+ check_single_patch_stats(g_repo, 1, 1, 1, 3, NULL, NULL);
+
+ /* no trailing whitespace as context line */
+
+ {
+ /* walk back a couple lines, make space and insert char */
+ char *scan = content.ptr + content.size;
+ int i;
+
+ for (i = 0; i < 5; ++i) {
+ for (--scan; scan > content.ptr && *scan != '\n'; --scan)
+ /* seek to prev \n */;
+ }
+ cl_assert(scan > content.ptr);
+
+ /* overwrite trailing \n with right-shifted content */
+ memmove(scan + 1, scan, content.size - (scan - content.ptr) - 1);
+ /* insert '#' char into space we created */
+ scan[1] = '#';
+ }
+ cl_git_rewritefile("renames/songof7cities.txt", content.ptr);
+
+ check_single_patch_stats(
+ g_repo, 1, 1, 1, 6, expected_sizes, expected);
+
+ git_str_dispose(&content);
+}
+
+void test_diff_patch__can_strip_bad_utf8(void)
+{
+ const char *a = "A " UTF8_HUNK_HEADER
+ " B\n"
+ " C\n"
+ " D\n"
+ " E\n"
+ " F\n"
+ " G\n"
+ " H\n"
+ " I\n"
+ " J\n"
+ " K\n"
+ "L " UTF8_HUNK_HEADER
+ " M\n"
+ " N\n"
+ " O\n"
+ " P\n"
+ " Q\n"
+ " R\n"
+ " S\n"
+ " T\n"
+ " U\n"
+ " V\n";
+
+ const char *b = "A " UTF8_HUNK_HEADER
+ " B\n"
+ " C\n"
+ " D\n"
+ " E modified\n"
+ " F\n"
+ " G\n"
+ " H\n"
+ " I\n"
+ " J\n"
+ " K\n"
+ "L " UTF8_HUNK_HEADER
+ " M\n"
+ " N\n"
+ " O\n"
+ " P modified\n"
+ " Q\n"
+ " R\n"
+ " S\n"
+ " T\n"
+ " U\n"
+ " V\n";
+
+ const char *expected = "diff --git a/file b/file\n"
+ "index d0647c4..7827ce5 100644\n"
+ "--- a/file\n"
+ "+++ b/file\n"
+ "@@ -2,7 +2,7 @@ A " UTF8_TRUNCATED_A_HUNK_HEADER
+ " B\n"
+ " C\n"
+ " D\n"
+ "- E\n"
+ "+ E modified\n"
+ " F\n"
+ " G\n"
+ " H\n"
+ "@@ -13,7 +13,7 @@ L " UTF8_TRUNCATED_L_HUNK_HEADER
+ " M\n"
+ " N\n"
+ " O\n"
+ "- P\n"
+ "+ P modified\n"
+ " Q\n"
+ " R\n"
+ " S\n";
+
+ git_diff_options opts;
+ git_patch *patch;
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_pass(git_diff_options_init(&opts, GIT_DIFF_OPTIONS_VERSION));
+
+ cl_git_pass(git_patch_from_buffers(&patch, a, strlen(a), NULL, b, strlen(b), NULL, &opts));
+ cl_git_pass(git_patch_to_buf(&buf, patch));
+
+ cl_assert_equal_s(expected, buf.ptr);
+
+ git_patch_free(patch);
+ git_buf_dispose(&buf);
+}
diff --git a/tests/libgit2/diff/patchid.c b/tests/libgit2/diff/patchid.c
new file mode 100644
index 0000000..91807e7
--- /dev/null
+++ b/tests/libgit2/diff/patchid.c
@@ -0,0 +1,94 @@
+#include "clar_libgit2.h"
+#include "patch/patch_common.h"
+#include "diff_helpers.h"
+
+static void verify_patch_id(const char *diff_content, const char *expected_id)
+{
+ git_oid expected_oid, actual_oid;
+ git_diff *diff;
+
+ cl_git_pass(git_oid__fromstr(&expected_oid, expected_id, GIT_OID_SHA1));
+ cl_git_pass(diff_from_buffer(&diff, diff_content, strlen(diff_content)));
+ cl_git_pass(git_diff_patchid(&actual_oid, diff, NULL));
+
+ cl_assert_equal_oid(&expected_oid, &actual_oid);
+
+ git_diff_free(diff);
+}
+
+void test_diff_patchid__simple_commit(void)
+{
+ verify_patch_id(PATCH_SIMPLE_COMMIT, "06094b1948b878b7d9ff7560b4eae672a014b0ec");
+}
+
+void test_diff_patchid__deleted_file(void)
+{
+ verify_patch_id(PATCH_DELETE_ORIGINAL, "d18507fe189f49c028b32c8c34e1ad98dd6a1aad");
+ verify_patch_id(PATCH_DELETED_FILE_2_HUNKS, "f31412498a17e6c3fbc635f2c5f9aa3ef4c1a9b7");
+}
+
+void test_diff_patchid__created_file(void)
+{
+ verify_patch_id(PATCH_ADD_ORIGINAL, "a7d39379308021465ae2ce65e338c048a3110db6");
+}
+
+void test_diff_patchid__binary_file(void)
+{
+ verify_patch_id(PATCH_ADD_BINARY_NOT_PRINTED, "2b31236b485faa30cf4dd33e4d6539829996739f");
+}
+
+void test_diff_patchid__renamed_file(void)
+{
+ verify_patch_id(PATCH_RENAME_EXACT, "4666d50cea4976f6f727448046d43461912058fd");
+ verify_patch_id(PATCH_RENAME_SIMILAR, "a795087575fcb940227be524488bedd6b3d3f438");
+}
+
+void test_diff_patchid__modechange(void)
+{
+ verify_patch_id(PATCH_MODECHANGE_UNCHANGED, "dbf3423ee98375ef1c72a79fbd29a049a2bae771");
+ verify_patch_id(PATCH_MODECHANGE_MODIFIED, "93aba696e1bbd2bbb73e3e3e62ed71f232137657");
+}
+
+void test_diff_patchid__shuffle_hunks(void)
+{
+ verify_patch_id(PATCH_DELETED_FILE_2_HUNKS_SHUFFLED, "f31412498a17e6c3fbc635f2c5f9aa3ef4c1a9b7");
+}
+
+void test_diff_patchid__filename_with_spaces(void)
+{
+ verify_patch_id(PATCH_APPEND_NO_NL, "f0ba05413beaef743b630e796153839462ee477a");
+}
+
+void test_diff_patchid__multiple_hunks(void)
+{
+ verify_patch_id(PATCH_MULTIPLE_HUNKS, "81e26c34643d17f521e57c483a6a637e18ba1f57");
+}
+
+void test_diff_patchid__multiple_files(void)
+{
+ verify_patch_id(PATCH_MULTIPLE_FILES, "192d1f49d23f2004517963aecd3f8a6c467f50ff");
+}
+
+void test_diff_patchid__same_diff_with_differing_whitespace_has_same_id(void)
+{
+ const char *tabs =
+ "diff --git a/file.txt b/file.txt\n"
+ "index 8fecc09..1d43a92 100644\n"
+ "--- a/file.txt\n"
+ "+++ b/file.txt\n"
+ "@@ -1 +1 @@\n"
+ "-old text\n"
+ "+ new text\n";
+ const char *spaces =
+ "diff --git a/file.txt b/file.txt\n"
+ "index 8fecc09..1d43a92 100644\n"
+ "--- a/file.txt\n"
+ "+++ b/file.txt\n"
+ "@@ -1 +1 @@\n"
+ "-old text\n"
+ "+ new text\n";
+ const char *id = "11efdd13c30f7a1056eac2ae2fb952da475e2c23";
+
+ verify_patch_id(tabs, id);
+ verify_patch_id(spaces, id);
+}
diff --git a/tests/libgit2/diff/pathspec.c b/tests/libgit2/diff/pathspec.c
new file mode 100644
index 0000000..5761d2d
--- /dev/null
+++ b/tests/libgit2/diff/pathspec.c
@@ -0,0 +1,93 @@
+#include "clar_libgit2.h"
+#include "diff_helpers.h"
+
+static git_repository *g_repo = NULL;
+
+void test_diff_pathspec__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("status");
+}
+
+void test_diff_pathspec__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_diff_pathspec__0(void)
+{
+ const char *a_commit = "26a125ee"; /* the current HEAD */
+ const char *b_commit = "0017bd4a"; /* the start */
+ git_tree *a = resolve_commit_oid_to_tree(g_repo, a_commit);
+ git_tree *b = resolve_commit_oid_to_tree(g_repo, b_commit);
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ git_strarray paths = { NULL, 1 };
+ char *path;
+ git_pathspec *ps;
+ git_pathspec_match_list *matches;
+
+ cl_assert(a);
+ cl_assert(b);
+
+ path = "*_file";
+ paths.strings = &path;
+ cl_git_pass(git_pathspec_new(&ps, &paths));
+
+ cl_git_pass(git_pathspec_match_tree(&matches, a, GIT_PATHSPEC_DEFAULT, ps));
+ cl_assert_equal_i(7, (int)git_pathspec_match_list_entrycount(matches));
+ cl_assert_equal_s("current_file", git_pathspec_match_list_entry(matches,0));
+ cl_assert(git_pathspec_match_list_diff_entry(matches,0) == NULL);
+ git_pathspec_match_list_free(matches);
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, NULL, a, &opts));
+
+ cl_git_pass(git_pathspec_match_diff(
+ &matches, diff, GIT_PATHSPEC_DEFAULT, ps));
+ cl_assert_equal_i(7, (int)git_pathspec_match_list_entrycount(matches));
+ cl_assert(git_pathspec_match_list_diff_entry(matches, 0) != NULL);
+ cl_assert(git_pathspec_match_list_entry(matches, 0) == NULL);
+ cl_assert_equal_s("current_file",
+ git_pathspec_match_list_diff_entry(matches,0)->new_file.path);
+ cl_assert_equal_i(GIT_DELTA_ADDED,
+ (int)git_pathspec_match_list_diff_entry(matches,0)->status);
+ git_pathspec_match_list_free(matches);
+
+ git_diff_free(diff);
+ diff = NULL;
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
+
+ cl_git_pass(git_pathspec_match_diff(
+ &matches, diff, GIT_PATHSPEC_DEFAULT, ps));
+ cl_assert_equal_i(3, (int)git_pathspec_match_list_entrycount(matches));
+ cl_assert(git_pathspec_match_list_diff_entry(matches, 0) != NULL);
+ cl_assert(git_pathspec_match_list_entry(matches, 0) == NULL);
+ cl_assert_equal_s("subdir/current_file",
+ git_pathspec_match_list_diff_entry(matches,0)->new_file.path);
+ cl_assert_equal_i(GIT_DELTA_DELETED,
+ (int)git_pathspec_match_list_diff_entry(matches,0)->status);
+ git_pathspec_match_list_free(matches);
+
+ git_diff_free(diff);
+ diff = NULL;
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, a, &opts));
+
+ cl_git_pass(git_pathspec_match_diff(
+ &matches, diff, GIT_PATHSPEC_DEFAULT, ps));
+ cl_assert_equal_i(4, (int)git_pathspec_match_list_entrycount(matches));
+ cl_assert(git_pathspec_match_list_diff_entry(matches, 0) != NULL);
+ cl_assert(git_pathspec_match_list_entry(matches, 0) == NULL);
+ cl_assert_equal_s("modified_file",
+ git_pathspec_match_list_diff_entry(matches,0)->new_file.path);
+ cl_assert_equal_i(GIT_DELTA_MODIFIED,
+ (int)git_pathspec_match_list_diff_entry(matches,0)->status);
+ git_pathspec_match_list_free(matches);
+
+ git_diff_free(diff);
+ diff = NULL;
+
+ git_tree_free(a);
+ git_tree_free(b);
+ git_pathspec_free(ps);
+}
diff --git a/tests/libgit2/diff/racediffiter.c b/tests/libgit2/diff/racediffiter.c
new file mode 100644
index 0000000..d364d6b
--- /dev/null
+++ b/tests/libgit2/diff/racediffiter.c
@@ -0,0 +1,129 @@
+/* This test exercises the problem described in
+** https://github.com/libgit2/libgit2/pull/3568
+** where deleting a directory during a diff/status
+** operation can cause an access violation.
+**
+** The "test_diff_racediffiter__basic() test confirms
+** the normal operation of diff on the given repo.
+**
+** The "test_diff_racediffiter__racy_rmdir() test
+** uses the new diff progress callback to delete
+** a directory (after the initial readdir() and
+** before the directory itself is visited) causing
+** the recursion and iteration to fail.
+*/
+
+#include "clar_libgit2.h"
+#include "diff_helpers.h"
+
+#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
+
+void test_diff_racediffiter__initialize(void)
+{
+}
+
+void test_diff_racediffiter__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+typedef struct
+{
+ const char *path;
+ git_delta_t t;
+
+} basic_payload;
+
+static int notify_cb__basic(
+ const git_diff *diff_so_far,
+ const git_diff_delta *delta_to_add,
+ const char *matched_pathspec,
+ void *payload)
+{
+ basic_payload *exp = (basic_payload *)payload;
+ basic_payload *e;
+
+ GIT_UNUSED(diff_so_far);
+ GIT_UNUSED(matched_pathspec);
+
+ for (e = exp; e->path; e++) {
+ if (strcmp(e->path, delta_to_add->new_file.path) == 0) {
+ cl_assert_equal_i(e->t, delta_to_add->status);
+ return 0;
+ }
+ }
+ cl_assert(0);
+ return GIT_ENOTFOUND;
+}
+
+void test_diff_racediffiter__basic(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_repository *repo = cl_git_sandbox_init("diff");
+ git_diff *diff;
+
+ basic_payload exp_a[] = {
+ { "another.txt", GIT_DELTA_MODIFIED },
+ { "readme.txt", GIT_DELTA_MODIFIED },
+ { "zzzzz/", GIT_DELTA_IGNORED },
+ { NULL, 0 }
+ };
+
+ cl_must_pass(p_mkdir("diff/zzzzz", 0777));
+
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_RECURSE_UNTRACKED_DIRS;
+ opts.notify_cb = notify_cb__basic;
+ opts.payload = exp_a;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
+
+ git_diff_free(diff);
+}
+
+
+typedef struct {
+ bool first_time;
+ const char *dir;
+ basic_payload *basic_payload;
+} racy_payload;
+
+static int notify_cb__racy_rmdir(
+ const git_diff *diff_so_far,
+ const git_diff_delta *delta_to_add,
+ const char *matched_pathspec,
+ void *payload)
+{
+ racy_payload *pay = (racy_payload *)payload;
+
+ if (pay->first_time) {
+ cl_must_pass(p_rmdir(pay->dir));
+ pay->first_time = false;
+ }
+
+ return notify_cb__basic(diff_so_far, delta_to_add, matched_pathspec, pay->basic_payload);
+}
+
+void test_diff_racediffiter__racy(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_repository *repo = cl_git_sandbox_init("diff");
+ git_diff *diff;
+
+ basic_payload exp_a[] = {
+ { "another.txt", GIT_DELTA_MODIFIED },
+ { "readme.txt", GIT_DELTA_MODIFIED },
+ { NULL, 0 }
+ };
+
+ racy_payload pay = { true, "diff/zzzzz", exp_a };
+
+ cl_must_pass(p_mkdir("diff/zzzzz", 0777));
+
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_RECURSE_UNTRACKED_DIRS;
+ opts.notify_cb = notify_cb__racy_rmdir;
+ opts.payload = &pay;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
+
+ git_diff_free(diff);
+}
diff --git a/tests/libgit2/diff/rename.c b/tests/libgit2/diff/rename.c
new file mode 100644
index 0000000..9d44394
--- /dev/null
+++ b/tests/libgit2/diff/rename.c
@@ -0,0 +1,2034 @@
+#include "clar_libgit2.h"
+#include "diff_helpers.h"
+
+static git_repository *g_repo = NULL;
+
+void test_diff_rename__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("renames");
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", false);
+}
+
+void test_diff_rename__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+#define INITIAL_COMMIT "31e47d8c1fa36d7f8d537b96158e3f024de0a9f2"
+#define COPY_RENAME_COMMIT "2bc7f351d20b53f1c72c16c4b036e491c478c49a"
+#define REWRITE_COPY_COMMIT "1c068dee5790ef1580cfc4cd670915b48d790084"
+#define RENAME_MODIFICATION_COMMIT "19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13"
+#define REWRITE_DELETE_COMMIT "84d8efa38af7ace2b302de0adbda16b1f1cd2e1b"
+#define DELETE_RENAME_COMMIT "be053a189b0bbde545e0a3f59ce00b46ad29ce0d"
+#define BREAK_REWRITE_BASE_COMMIT "db98035f715427eef1f5e17f03e1801c05301e9e"
+#define BREAK_REWRITE_COMMIT "7e7bfb88ba9bc65fd700fee1819cf1c317aafa56"
+
+/*
+ * Renames repo has:
+ *
+ * commit 31e47d8c1fa36d7f8d537b96158e3f024de0a9f2 -
+ * serving.txt (25 lines)
+ * sevencities.txt (50 lines)
+ * commit 2bc7f351d20b53f1c72c16c4b036e491c478c49a -
+ * serving.txt -> sixserving.txt (rename, no change, 100% match)
+ * sevencities.txt -> sevencities.txt (no change)
+ * sevencities.txt -> songofseven.txt (copy, no change, 100% match)
+ * commit 1c068dee5790ef1580cfc4cd670915b48d790084
+ * songofseven.txt -> songofseven.txt (major rewrite, <20% match - split)
+ * sixserving.txt -> sixserving.txt (indentation change)
+ * sixserving.txt -> ikeepsix.txt (copy, add title, >80% match)
+ * sevencities.txt (no change)
+ * commit 19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13
+ * songofseven.txt -> untimely.txt (rename, convert to crlf)
+ * ikeepsix.txt -> ikeepsix.txt (reorder sections in file)
+ * sixserving.txt -> sixserving.txt (whitespace change - not just indent)
+ * sevencities.txt -> songof7cities.txt (rename, small text changes)
+ * commit 84d8efa38af7ace2b302de0adbda16b1f1cd2e1b
+ * songof7cities.txt -> songof7citie.txt (major rewrite, <20% match)
+ * ikeepsix.txt -> (deleted)
+ * untimely.txt (no change)
+ * sixserving.txt (no change)
+ * commit be053a189b0bbde545e0a3f59ce00b46ad29ce0d
+ * ikeepsix.txt -> (deleted)
+ * songof7cities.txt -> ikeepsix.txt (rename, 100% match)
+ * untimely.txt (no change)
+ * sixserving.txt (no change)
+ */
+
+void test_diff_rename__match_oid(void)
+{
+ const char *old_sha = INITIAL_COMMIT;
+ const char *new_sha = COPY_RENAME_COMMIT;
+ git_tree *old_tree, *new_tree;
+ git_diff *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
+ diff_expects exp;
+
+ old_tree = resolve_commit_oid_to_tree(g_repo, old_sha);
+ new_tree = resolve_commit_oid_to_tree(g_repo, new_sha);
+
+ /* Must pass GIT_DIFF_INCLUDE_UNMODIFIED if you expect to emulate
+ * --find-copies-harder during rename transformion...
+ */
+ diffopts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
+
+ cl_git_pass(git_diff_tree_to_tree(
+ &diff, g_repo, old_tree, new_tree, &diffopts));
+
+ /* git diff --no-renames \
+ * 31e47d8c1fa36d7f8d537b96158e3f024de0a9f2 \
+ * 2bc7f351d20b53f1c72c16c4b036e491c478c49a
+ */
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+
+ /* git diff 31e47d8c1fa36d7f8d537b96158e3f024de0a9f2 \
+ * 2bc7f351d20b53f1c72c16c4b036e491c478c49a
+ * don't use NULL opts to avoid config `diff.renames` contamination
+ */
+ opts.flags = GIT_DIFF_FIND_RENAMES;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(3, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
+
+ git_diff_free(diff);
+
+ cl_git_pass(git_diff_tree_to_tree(
+ &diff, g_repo, old_tree, new_tree, &diffopts));
+
+ /* git diff --find-copies-harder \
+ * 31e47d8c1fa36d7f8d537b96158e3f024de0a9f2 \
+ * 2bc7f351d20b53f1c72c16c4b036e491c478c49a
+ */
+ opts.flags = GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(3, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_COPIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
+
+ git_diff_free(diff);
+
+ cl_git_pass(git_diff_tree_to_tree(
+ &diff, g_repo, old_tree, new_tree, &diffopts));
+
+ /* git diff --find-copies-harder -M100 -B100 \
+ * 31e47d8c1fa36d7f8d537b96158e3f024de0a9f2 \
+ * 2bc7f351d20b53f1c72c16c4b036e491c478c49a
+ */
+ opts.flags = GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED |
+ GIT_DIFF_FIND_EXACT_MATCH_ONLY;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(3, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_COPIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
+
+ git_diff_free(diff);
+
+ git_tree_free(old_tree);
+ git_tree_free(new_tree);
+}
+
+void test_diff_rename__checks_options_version(void)
+{
+ const char *old_sha = INITIAL_COMMIT;
+ const char *new_sha = COPY_RENAME_COMMIT;
+ git_tree *old_tree, *new_tree;
+ git_diff *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
+ const git_error *err;
+
+ old_tree = resolve_commit_oid_to_tree(g_repo, old_sha);
+ new_tree = resolve_commit_oid_to_tree(g_repo, new_sha);
+ diffopts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
+ cl_git_pass(git_diff_tree_to_tree(
+ &diff, g_repo, old_tree, new_tree, &diffopts));
+
+ opts.version = 0;
+ cl_git_fail(git_diff_find_similar(diff, &opts));
+ err = git_error_last();
+ cl_assert_equal_i(GIT_ERROR_INVALID, err->klass);
+
+ git_error_clear();
+ opts.version = 1024;
+ cl_git_fail(git_diff_find_similar(diff, &opts));
+ err = git_error_last();
+ cl_assert_equal_i(GIT_ERROR_INVALID, err->klass);
+
+ git_diff_free(diff);
+ git_tree_free(old_tree);
+ git_tree_free(new_tree);
+}
+
+void test_diff_rename__not_exact_match(void)
+{
+ const char *sha0 = COPY_RENAME_COMMIT;
+ const char *sha1 = REWRITE_COPY_COMMIT;
+ const char *sha2 = RENAME_MODIFICATION_COMMIT;
+ git_tree *old_tree, *new_tree;
+ git_diff *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
+ diff_expects exp;
+
+ /* == Changes =====================================================
+ * songofseven.txt -> songofseven.txt (major rewrite, <20% match - split)
+ * sixserving.txt -> sixserving.txt (indentation change)
+ * sixserving.txt -> ikeepsix.txt (copy, add title, >80% match)
+ * sevencities.txt (no change)
+ */
+
+ old_tree = resolve_commit_oid_to_tree(g_repo, sha0);
+ new_tree = resolve_commit_oid_to_tree(g_repo, sha1);
+
+ /* Must pass GIT_DIFF_INCLUDE_UNMODIFIED if you expect to emulate
+ * --find-copies-harder during rename transformion...
+ */
+ diffopts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
+
+ cl_git_pass(git_diff_tree_to_tree(
+ &diff, g_repo, old_tree, new_tree, &diffopts));
+
+ /* git diff --no-renames \
+ * 2bc7f351d20b53f1c72c16c4b036e491c478c49a \
+ * 1c068dee5790ef1580cfc4cd670915b48d790084
+ */
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]);
+
+ /* git diff -M 2bc7f351d20b53f1c72c16c4b036e491c478c49a \
+ * 1c068dee5790ef1580cfc4cd670915b48d790084
+ *
+ * must not pass NULL for opts because it will pick up environment
+ * values for "diff.renames" and test won't be consistent.
+ */
+ opts.flags = GIT_DIFF_FIND_RENAMES;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]);
+
+ git_diff_free(diff);
+
+ /* git diff -M -C \
+ * 2bc7f351d20b53f1c72c16c4b036e491c478c49a \
+ * 1c068dee5790ef1580cfc4cd670915b48d790084
+ */
+ cl_git_pass(git_diff_tree_to_tree(
+ &diff, g_repo, old_tree, new_tree, &diffopts));
+
+ opts.flags = GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_COPIED]);
+
+ git_diff_free(diff);
+
+ /* git diff -M -C --find-copies-harder --break-rewrites \
+ * 2bc7f351d20b53f1c72c16c4b036e491c478c49a \
+ * 1c068dee5790ef1580cfc4cd670915b48d790084
+ */
+ cl_git_pass(git_diff_tree_to_tree(
+ &diff, g_repo, old_tree, new_tree, &diffopts));
+
+ opts.flags = GIT_DIFF_FIND_ALL;
+ opts.break_rewrite_threshold = 70;
+
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(5, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_COPIED]);
+
+ git_diff_free(diff);
+
+ /* == Changes =====================================================
+ * songofseven.txt -> untimely.txt (rename, convert to crlf)
+ * ikeepsix.txt -> ikeepsix.txt (reorder sections in file)
+ * sixserving.txt -> sixserving.txt (whitespace - not just indent)
+ * sevencities.txt -> songof7cities.txt (rename, small text changes)
+ */
+
+ git_tree_free(old_tree);
+ old_tree = new_tree;
+ new_tree = resolve_commit_oid_to_tree(g_repo, sha2);
+
+ cl_git_pass(git_diff_tree_to_tree(
+ &diff, g_repo, old_tree, new_tree, &diffopts));
+
+ /* git diff --no-renames \
+ * 1c068dee5790ef1580cfc4cd670915b48d790084 \
+ * 19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13
+ */
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(6, exp.files);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]);
+ git_diff_free(diff);
+
+ /* git diff -M -C \
+ * 1c068dee5790ef1580cfc4cd670915b48d790084 \
+ * 19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13
+ */
+ cl_git_pass(git_diff_tree_to_tree(
+ &diff, g_repo, old_tree, new_tree, &diffopts));
+
+ opts.flags = GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_RENAMED]);
+
+ git_diff_free(diff);
+
+ /* git diff -M -C --find-copies-harder --break-rewrites \
+ * 1c068dee5790ef1580cfc4cd670915b48d790084 \
+ * 19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13
+ * with libgit2 default similarity comparison...
+ */
+ cl_git_pass(git_diff_tree_to_tree(
+ &diff, g_repo, old_tree, new_tree, &diffopts));
+
+ opts.flags = GIT_DIFF_FIND_ALL;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ /* the default match algorithm is going to find the internal
+ * whitespace differences in the lines of sixserving.txt to be
+ * significant enough that this will decide to split it into an ADD
+ * and a DELETE
+ */
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(5, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_RENAMED]);
+
+ git_diff_free(diff);
+
+ /* git diff -M -C --find-copies-harder --break-rewrites \
+ * 1c068dee5790ef1580cfc4cd670915b48d790084 \
+ * 19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13
+ * with ignore_space whitespace comparison
+ */
+ cl_git_pass(git_diff_tree_to_tree(
+ &diff, g_repo, old_tree, new_tree, &diffopts));
+
+ opts.flags = GIT_DIFF_FIND_ALL | GIT_DIFF_FIND_IGNORE_WHITESPACE;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ /* Ignoring whitespace, this should no longer split sixserver.txt */
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_RENAMED]);
+
+ git_diff_free(diff);
+
+ git_tree_free(old_tree);
+ git_tree_free(new_tree);
+}
+
+void test_diff_rename__test_small_files(void)
+{
+ git_index *index;
+ git_reference *head_reference;
+ git_commit *head_commit;
+ git_tree *head_tree;
+ git_tree *commit_tree;
+ git_signature *signature;
+ git_diff *diff;
+ git_oid oid;
+ const git_diff_delta *delta;
+ git_diff_options diff_options = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options find_options = GIT_DIFF_FIND_OPTIONS_INIT;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_mkfile("renames/small.txt", "Hello World!\n");
+ cl_git_pass(git_index_add_bypath(index, "small.txt"));
+
+ cl_git_pass(git_repository_head(&head_reference, g_repo));
+ cl_git_pass(git_reference_peel((git_object**)&head_commit, head_reference, GIT_OBJECT_COMMIT));
+ cl_git_pass(git_commit_tree(&head_tree, head_commit));
+ cl_git_pass(git_index_write_tree(&oid, index));
+ cl_git_pass(git_tree_lookup(&commit_tree, g_repo, &oid));
+ cl_git_pass(git_signature_new(&signature, "Rename", "rename@example.com", 1404157834, 0));
+ cl_git_pass(git_commit_create(&oid, g_repo, "HEAD", signature, signature, NULL, "Test commit", commit_tree, 1, (const git_commit**)&head_commit));
+
+ cl_git_mkfile("renames/copy.txt", "Hello World!\n");
+ cl_git_rmfile("renames/small.txt");
+
+ diff_options.flags = GIT_DIFF_INCLUDE_UNTRACKED;
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, commit_tree, &diff_options));
+ find_options.flags = GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_FOR_UNTRACKED;
+ cl_git_pass(git_diff_find_similar(diff, &find_options));
+
+ cl_assert_equal_i(git_diff_num_deltas(diff), 1);
+ delta = git_diff_get_delta(diff, 0);
+ cl_assert_equal_i(delta->status, GIT_DELTA_RENAMED);
+ cl_assert_equal_s(delta->old_file.path, "small.txt");
+ cl_assert_equal_s(delta->new_file.path, "copy.txt");
+
+ git_diff_free(diff);
+ git_signature_free(signature);
+ git_tree_free(commit_tree);
+ git_tree_free(head_tree);
+ git_commit_free(head_commit);
+ git_reference_free(head_reference);
+ git_index_free(index);
+}
+
+void test_diff_rename__working_directory_changes(void)
+{
+ const char *sha0 = COPY_RENAME_COMMIT;
+ const char *blobsha = "66311f5cfbe7836c27510a3ba2f43e282e2c8bba";
+ git_oid id;
+ git_tree *tree;
+ git_blob *blob;
+ git_diff *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
+ diff_expects exp;
+ git_str old_content = GIT_STR_INIT, content = GIT_STR_INIT;;
+
+ tree = resolve_commit_oid_to_tree(g_repo, sha0);
+ diffopts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED | GIT_DIFF_INCLUDE_UNTRACKED;
+
+ /*
+ $ git cat-file -p 2bc7f351d20b53f1c72c16c4b036e491c478c49a^{tree}
+
+ 100644 blob 66311f5cfbe7836c27510a3ba2f43e282e2c8bba sevencities.txt
+ 100644 blob ad0a8e55a104ac54a8a29ed4b84b49e76837a113 sixserving.txt
+ 100644 blob 66311f5cfbe7836c27510a3ba2f43e282e2c8bba songofseven.txt
+
+ $ for f in *.txt; do
+ echo `git hash-object -t blob $f` $f
+ done
+
+ eaf4a3e3bfe68585e90cada20736ace491cd100b ikeepsix.txt
+ f90d4fc20ecddf21eebe6a37e9225d244339d2b5 sixserving.txt
+ 4210ffd5c390b21dd5483375e75288dea9ede512 songof7cities.txt
+ 9a69d960ae94b060f56c2a8702545e2bb1abb935 untimely.txt
+ */
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &diffopts));
+
+ /* git diff --no-renames 2bc7f351d20b53f1c72c16c4b036e491c478c49a */
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(6, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ /* git diff -M 2bc7f351d20b53f1c72c16c4b036e491c478c49a */
+ opts.flags = GIT_DIFF_FIND_ALL;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(5, exp.files);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_RENAMED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ git_diff_free(diff);
+
+ /* rewrite files in the working directory with / without CRLF changes */
+
+ cl_git_pass(
+ git_futils_readbuffer(&old_content, "renames/songof7cities.txt"));
+ cl_git_pass(
+ git_str_lf_to_crlf(&content, &old_content));
+ cl_git_pass(
+ git_futils_writebuffer(&content, "renames/songof7cities.txt", 0, 0));
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &diffopts));
+
+ /* git diff -M 2bc7f351d20b53f1c72c16c4b036e491c478c49a */
+ opts.flags = GIT_DIFF_FIND_ALL;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(5, exp.files);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_RENAMED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ git_diff_free(diff);
+
+ /* try a different whitespace option */
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &diffopts));
+
+ opts.flags = GIT_DIFF_FIND_ALL | GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE;
+ opts.rename_threshold = 70;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(6, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ git_diff_free(diff);
+
+ /* try a different matching option */
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &diffopts));
+
+ opts.flags = GIT_DIFF_FIND_ALL | GIT_DIFF_FIND_EXACT_MATCH_ONLY;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(6, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]);
+
+ git_diff_free(diff);
+
+ /* again with exact match blob */
+
+ cl_git_pass(git_oid__fromstr(&id, blobsha, GIT_OID_SHA1));
+ cl_git_pass(git_blob_lookup(&blob, g_repo, &id));
+ cl_git_pass(git_str_set(
+ &content, git_blob_rawcontent(blob), (size_t)git_blob_rawsize(blob)));
+ cl_git_rewritefile("renames/songof7cities.txt", content.ptr);
+ git_blob_free(blob);
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &diffopts));
+
+ opts.flags = GIT_DIFF_FIND_ALL | GIT_DIFF_FIND_EXACT_MATCH_ONLY;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ /*
+ fprintf(stderr, "\n\n");
+ diff_print_raw(stderr, diff);
+ */
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(5, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ git_diff_free(diff);
+
+ git_tree_free(tree);
+ git_str_dispose(&content);
+ git_str_dispose(&old_content);
+}
+
+void test_diff_rename__patch(void)
+{
+ const char *sha0 = COPY_RENAME_COMMIT;
+ const char *sha1 = REWRITE_COPY_COMMIT;
+ git_tree *old_tree, *new_tree;
+ git_diff *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
+ git_patch *patch;
+ const git_diff_delta *delta;
+ git_buf buf = GIT_BUF_INIT;
+ const char *expected = "diff --git a/sixserving.txt b/ikeepsix.txt\nindex ad0a8e5..36020db 100644\n--- a/sixserving.txt\n+++ b/ikeepsix.txt\n@@ -1,3 +1,6 @@\n+I Keep Six Honest Serving-Men\n+=============================\n+\n I KEEP six honest serving-men\n (They taught me all I knew);\n Their names are What and Why and When\n@@ -21,4 +24,4 @@ She sends'em abroad on her own affairs,\n One million Hows, two million Wheres,\n And seven million Whys!\n \n- -- Rudyard Kipling\n+ -- Rudyard Kipling\n";
+
+ old_tree = resolve_commit_oid_to_tree(g_repo, sha0);
+ new_tree = resolve_commit_oid_to_tree(g_repo, sha1);
+
+ diffopts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
+ cl_git_pass(git_diff_tree_to_tree(
+ &diff, g_repo, old_tree, new_tree, &diffopts));
+
+ opts.flags = GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ /* == Changes =====================================================
+ * sixserving.txt -> ikeepsix.txt (copy, add title, >80% match)
+ * sevencities.txt (no change)
+ * sixserving.txt -> sixserving.txt (indentation change)
+ * songofseven.txt -> songofseven.txt (major rewrite, <20% match - split)
+ */
+
+ cl_assert_equal_i(4, (int)git_diff_num_deltas(diff));
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_assert((delta = git_patch_get_delta(patch)) != NULL);
+ cl_assert_equal_i(GIT_DELTA_COPIED, (int)delta->status);
+
+ cl_git_pass(git_patch_to_buf(&buf, patch));
+ cl_assert_equal_s(expected, buf.ptr);
+ git_buf_dispose(&buf);
+
+ git_patch_free(patch);
+
+ cl_assert((delta = git_diff_get_delta(diff, 1)) != NULL);
+ cl_assert_equal_i(GIT_DELTA_UNMODIFIED, (int)delta->status);
+
+ cl_assert((delta = git_diff_get_delta(diff, 2)) != NULL);
+ cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status);
+
+ cl_assert((delta = git_diff_get_delta(diff, 3)) != NULL);
+ cl_assert_equal_i(GIT_DELTA_MODIFIED, (int)delta->status);
+
+ git_diff_free(diff);
+ git_tree_free(old_tree);
+ git_tree_free(new_tree);
+}
+
+void test_diff_rename__file_exchange(void)
+{
+ git_str c1 = GIT_STR_INIT, c2 = GIT_STR_INIT;
+ git_index *index;
+ git_tree *tree;
+ git_diff *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
+ diff_expects exp;
+
+ cl_git_pass(git_futils_readbuffer(&c1, "renames/untimely.txt"));
+ cl_git_pass(git_futils_readbuffer(&c2, "renames/songof7cities.txt"));
+ cl_git_pass(git_futils_writebuffer(&c1, "renames/songof7cities.txt", 0, 0));
+ cl_git_pass(git_futils_writebuffer(&c2, "renames/untimely.txt", 0, 0));
+
+ cl_git_pass(
+ git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_read_tree(index, tree));
+ cl_git_pass(git_index_add_bypath(index, "songof7cities.txt"));
+ cl_git_pass(git_index_add_bypath(index, "untimely.txt"));
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(2, exp.files);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
+
+ opts.flags = GIT_DIFF_FIND_ALL;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(2, exp.files);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_RENAMED]);
+
+ git_diff_free(diff);
+ git_tree_free(tree);
+ git_index_free(index);
+
+ git_str_dispose(&c1);
+ git_str_dispose(&c2);
+}
+
+void test_diff_rename__file_exchange_three(void)
+{
+ git_str c1 = GIT_STR_INIT, c2 = GIT_STR_INIT, c3 = GIT_STR_INIT;
+ git_index *index;
+ git_tree *tree;
+ git_diff *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
+ diff_expects exp;
+
+ cl_git_pass(git_futils_readbuffer(&c1, "renames/untimely.txt"));
+ cl_git_pass(git_futils_readbuffer(&c2, "renames/songof7cities.txt"));
+ cl_git_pass(git_futils_readbuffer(&c3, "renames/ikeepsix.txt"));
+
+ cl_git_pass(git_futils_writebuffer(&c1, "renames/ikeepsix.txt", 0, 0));
+ cl_git_pass(git_futils_writebuffer(&c2, "renames/untimely.txt", 0, 0));
+ cl_git_pass(git_futils_writebuffer(&c3, "renames/songof7cities.txt", 0, 0));
+
+ cl_git_pass(
+ git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_read_tree(index, tree));
+ cl_git_pass(git_index_add_bypath(index, "songof7cities.txt"));
+ cl_git_pass(git_index_add_bypath(index, "untimely.txt"));
+ cl_git_pass(git_index_add_bypath(index, "ikeepsix.txt"));
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(3, exp.files);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]);
+
+ opts.flags = GIT_DIFF_FIND_ALL;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(3, exp.files);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_RENAMED]);
+
+ git_diff_free(diff);
+ git_tree_free(tree);
+ git_index_free(index);
+
+ git_str_dispose(&c1);
+ git_str_dispose(&c2);
+ git_str_dispose(&c3);
+}
+
+void test_diff_rename__file_partial_exchange(void)
+{
+ git_str c1 = GIT_STR_INIT, c2 = GIT_STR_INIT;
+ git_index *index;
+ git_tree *tree;
+ git_diff *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
+ diff_expects exp;
+ int i;
+
+ cl_git_pass(git_futils_readbuffer(&c1, "renames/untimely.txt"));
+ cl_git_pass(git_futils_writebuffer(&c1, "renames/songof7cities.txt", 0, 0));
+ for (i = 0; i < 100; ++i)
+ cl_git_pass(git_str_puts(&c2, "this is not the content you are looking for\n"));
+ cl_git_pass(git_futils_writebuffer(&c2, "renames/untimely.txt", 0, 0));
+
+ cl_git_pass(
+ git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_read_tree(index, tree));
+ cl_git_pass(git_index_add_bypath(index, "songof7cities.txt"));
+ cl_git_pass(git_index_add_bypath(index, "untimely.txt"));
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(2, exp.files);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
+
+ opts.flags = GIT_DIFF_FIND_ALL;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(3, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+
+ git_diff_free(diff);
+ git_tree_free(tree);
+ git_index_free(index);
+
+ git_str_dispose(&c1);
+ git_str_dispose(&c2);
+}
+
+void test_diff_rename__rename_and_copy_from_same_source(void)
+{
+ git_str c1 = GIT_STR_INIT, c2 = GIT_STR_INIT;
+ git_index *index;
+ git_tree *tree;
+ git_diff *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
+ diff_expects exp;
+
+ /* put the first 2/3 of file into one new place
+ * and the second 2/3 of file into another new place
+ */
+ cl_git_pass(git_futils_readbuffer(&c1, "renames/songof7cities.txt"));
+ cl_git_pass(git_str_set(&c2, c1.ptr, c1.size));
+ git_str_truncate(&c1, c1.size * 2 / 3);
+ git_str_consume(&c2, ((char *)c2.ptr) + (c2.size / 3));
+ cl_git_pass(git_futils_writebuffer(&c1, "renames/song_a.txt", 0, 0));
+ cl_git_pass(git_futils_writebuffer(&c2, "renames/song_b.txt", 0, 0));
+
+ cl_git_pass(
+ git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_read_tree(index, tree));
+ cl_git_pass(git_index_add_bypath(index, "song_a.txt"));
+ cl_git_pass(git_index_add_bypath(index, "song_b.txt"));
+
+ diffopts.flags = GIT_DIFF_INCLUDE_UNMODIFIED;
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(6, exp.files);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNMODIFIED]);
+
+ opts.flags = GIT_DIFF_FIND_ALL;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(6, exp.files);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_COPIED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNMODIFIED]);
+
+ git_diff_free(diff);
+ git_tree_free(tree);
+ git_index_free(index);
+
+ git_str_dispose(&c1);
+ git_str_dispose(&c2);
+}
+
+void test_diff_rename__from_deleted_to_split(void)
+{
+ git_str c1 = GIT_STR_INIT;
+ git_index *index;
+ git_tree *tree;
+ git_diff *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
+ diff_expects exp;
+
+ /* old file is missing, new file is actually old file renamed */
+
+ cl_git_pass(git_futils_readbuffer(&c1, "renames/songof7cities.txt"));
+ cl_git_pass(git_futils_writebuffer(&c1, "renames/untimely.txt", 0, 0));
+
+ cl_git_pass(
+ git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_read_tree(index, tree));
+ cl_git_pass(git_index_remove_bypath(index, "songof7cities.txt"));
+ cl_git_pass(git_index_add_bypath(index, "untimely.txt"));
+
+ diffopts.flags = GIT_DIFF_INCLUDE_UNMODIFIED;
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNMODIFIED]);
+
+ opts.flags = GIT_DIFF_FIND_ALL;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNMODIFIED]);
+
+ git_diff_free(diff);
+ git_tree_free(tree);
+ git_index_free(index);
+
+ git_str_dispose(&c1);
+}
+
+struct rename_expected
+{
+ size_t len;
+
+ unsigned int *status;
+ const char **sources;
+ const char **targets;
+
+ size_t idx;
+};
+
+static int test_names_expected(const git_diff_delta *delta, float progress, void *p)
+{
+ struct rename_expected *expected = p;
+
+ GIT_UNUSED(progress);
+
+ cl_assert(expected->idx < expected->len);
+
+ cl_assert_equal_i(delta->status, expected->status[expected->idx]);
+
+ cl_assert(git__strcmp(expected->sources[expected->idx],
+ delta->old_file.path) == 0);
+ cl_assert(git__strcmp(expected->targets[expected->idx],
+ delta->new_file.path) == 0);
+
+ expected->idx++;
+
+ return 0;
+}
+
+void test_diff_rename__rejected_match_can_match_others(void)
+{
+ git_reference *head, *selfsimilar;
+ git_index *index;
+ git_tree *tree;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_diff *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
+ git_str one = GIT_STR_INIT, two = GIT_STR_INIT;
+ unsigned int status[] = { GIT_DELTA_RENAMED, GIT_DELTA_RENAMED };
+ const char *sources[] = { "Class1.cs", "Class2.cs" };
+ const char *targets[] = { "ClassA.cs", "ClassB.cs" };
+ struct rename_expected expect = { 2, status, sources, targets };
+ char *ptr;
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+ findopts.flags = GIT_DIFF_FIND_RENAMES;
+
+ cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
+ cl_git_pass(git_reference_symbolic_set_target(
+ &selfsimilar, head, "refs/heads/renames_similar", NULL));
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_pass(git_futils_readbuffer(&one, "renames/Class1.cs"));
+ cl_git_pass(git_futils_readbuffer(&two, "renames/Class2.cs"));
+
+ cl_git_pass(p_unlink("renames/Class1.cs"));
+ cl_git_pass(p_unlink("renames/Class2.cs"));
+
+ cl_git_pass(git_index_remove_bypath(index, "Class1.cs"));
+ cl_git_pass(git_index_remove_bypath(index, "Class2.cs"));
+
+ cl_assert(ptr = strstr(one.ptr, "Class1"));
+ ptr[5] = 'A';
+
+ cl_assert(ptr = strstr(two.ptr, "Class2"));
+ ptr[5] = 'B';
+
+ cl_git_pass(
+ git_futils_writebuffer(&one, "renames/ClassA.cs", O_RDWR|O_CREAT, 0777));
+ cl_git_pass(
+ git_futils_writebuffer(&two, "renames/ClassB.cs", O_RDWR|O_CREAT, 0777));
+
+ cl_git_pass(git_index_add_bypath(index, "ClassA.cs"));
+ cl_git_pass(git_index_add_bypath(index, "ClassB.cs"));
+
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(
+ git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
+
+ cl_git_pass(
+ git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
+
+ cl_git_pass(git_diff_find_similar(diff, &findopts));
+
+ cl_git_pass(git_diff_foreach(
+ diff, test_names_expected, NULL, NULL, NULL, &expect));
+
+ git_diff_free(diff);
+ git_tree_free(tree);
+ git_index_free(index);
+ git_reference_free(head);
+ git_reference_free(selfsimilar);
+ git_str_dispose(&one);
+ git_str_dispose(&two);
+}
+
+static void write_similarity_file_two(const char *filename, size_t b_lines)
+{
+ git_str contents = GIT_STR_INIT;
+ size_t i;
+
+ for (i = 0; i < b_lines; i++)
+ git_str_printf(&contents, "%02d - bbbbb\r\n", (int)(i+1));
+
+ for (i = b_lines; i < 50; i++)
+ git_str_printf(&contents, "%02d - aaaaa%s", (int)(i+1), (i == 49 ? "" : "\r\n"));
+
+ cl_git_pass(
+ git_futils_writebuffer(&contents, filename, O_RDWR|O_CREAT, 0777));
+
+ git_str_dispose(&contents);
+}
+
+void test_diff_rename__rejected_match_can_match_others_two(void)
+{
+ git_reference *head, *selfsimilar;
+ git_index *index;
+ git_tree *tree;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_diff *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
+ unsigned int status[] = { GIT_DELTA_RENAMED, GIT_DELTA_RENAMED };
+ const char *sources[] = { "a.txt", "b.txt" };
+ const char *targets[] = { "c.txt", "d.txt" };
+ struct rename_expected expect = { 2, status, sources, targets };
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+ findopts.flags = GIT_DIFF_FIND_RENAMES;
+
+ cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
+ cl_git_pass(git_reference_symbolic_set_target(
+ &selfsimilar, head, "refs/heads/renames_similar_two", NULL));
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_pass(p_unlink("renames/a.txt"));
+ cl_git_pass(p_unlink("renames/b.txt"));
+
+ cl_git_pass(git_index_remove_bypath(index, "a.txt"));
+ cl_git_pass(git_index_remove_bypath(index, "b.txt"));
+
+ write_similarity_file_two("renames/c.txt", 7);
+ write_similarity_file_two("renames/d.txt", 8);
+
+ cl_git_pass(git_index_add_bypath(index, "c.txt"));
+ cl_git_pass(git_index_add_bypath(index, "d.txt"));
+
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(
+ git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
+
+ cl_git_pass(
+ git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
+
+ cl_git_pass(git_diff_find_similar(diff, &findopts));
+
+ cl_git_pass(git_diff_foreach(
+ diff, test_names_expected, NULL, NULL, NULL, &expect));
+ cl_assert(expect.idx > 0);
+
+ git_diff_free(diff);
+ git_tree_free(tree);
+ git_index_free(index);
+ git_reference_free(head);
+ git_reference_free(selfsimilar);
+}
+
+void test_diff_rename__rejected_match_can_match_others_three(void)
+{
+ git_reference *head, *selfsimilar;
+ git_index *index;
+ git_tree *tree;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_diff *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
+
+ /* Both cannot be renames from a.txt */
+ unsigned int status[] = { GIT_DELTA_ADDED, GIT_DELTA_RENAMED };
+ const char *sources[] = { "0001.txt", "a.txt" };
+ const char *targets[] = { "0001.txt", "0002.txt" };
+ struct rename_expected expect = { 2, status, sources, targets };
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+ findopts.flags = GIT_DIFF_FIND_RENAMES;
+
+ cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
+ cl_git_pass(git_reference_symbolic_set_target(
+ &selfsimilar, head, "refs/heads/renames_similar_two", NULL));
+ cl_git_pass(git_checkout_head(g_repo, &opts));
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_pass(p_unlink("renames/a.txt"));
+
+ cl_git_pass(git_index_remove_bypath(index, "a.txt"));
+
+ write_similarity_file_two("renames/0001.txt", 7);
+ write_similarity_file_two("renames/0002.txt", 0);
+
+ cl_git_pass(git_index_add_bypath(index, "0001.txt"));
+ cl_git_pass(git_index_add_bypath(index, "0002.txt"));
+
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(
+ git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
+
+ cl_git_pass(
+ git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
+
+ cl_git_pass(git_diff_find_similar(diff, &findopts));
+
+ cl_git_pass(git_diff_foreach(
+ diff, test_names_expected, NULL, NULL, NULL, &expect));
+
+ cl_assert(expect.idx == expect.len);
+
+ git_diff_free(diff);
+ git_tree_free(tree);
+ git_index_free(index);
+ git_reference_free(head);
+ git_reference_free(selfsimilar);
+}
+
+void test_diff_rename__can_rename_from_rewrite(void)
+{
+ git_index *index;
+ git_tree *tree;
+ git_diff *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
+
+ unsigned int status[] = { GIT_DELTA_RENAMED, GIT_DELTA_RENAMED };
+ const char *sources[] = { "ikeepsix.txt", "songof7cities.txt" };
+ const char *targets[] = { "songof7cities.txt", "this-is-a-rename.txt" };
+ struct rename_expected expect = { 2, status, sources, targets };
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_pass(p_rename("renames/songof7cities.txt", "renames/this-is-a-rename.txt"));
+ cl_git_pass(p_rename("renames/ikeepsix.txt", "renames/songof7cities.txt"));
+
+ cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
+
+ cl_git_pass(git_index_add_bypath(index, "songof7cities.txt"));
+ cl_git_pass(git_index_add_bypath(index, "this-is-a-rename.txt"));
+
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(
+ git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
+
+ cl_git_pass(
+ git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
+
+ findopts.flags |= GIT_DIFF_FIND_AND_BREAK_REWRITES |
+ GIT_DIFF_FIND_REWRITES |
+ GIT_DIFF_FIND_RENAMES_FROM_REWRITES;
+
+ cl_git_pass(git_diff_find_similar(diff, &findopts));
+
+ cl_git_pass(git_diff_foreach(
+ diff, test_names_expected, NULL, NULL, NULL, &expect));
+
+ cl_assert(expect.idx == expect.len);
+
+ git_diff_free(diff);
+ git_tree_free(tree);
+ git_index_free(index);
+}
+
+void test_diff_rename__case_changes_are_split(void)
+{
+ git_index *index;
+ git_tree *tree;
+ git_diff *diff = NULL;
+ diff_expects exp;
+ git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_pass(
+ git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
+
+ cl_git_pass(p_rename("renames/ikeepsix.txt", "renames/IKEEPSIX.txt"));
+
+ cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
+ cl_git_pass(git_index_add_bypath(index, "IKEEPSIX.txt"));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, NULL));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(2, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]);
+
+ opts.flags = GIT_DIFF_FIND_ALL;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
+
+ git_diff_free(diff);
+ git_index_free(index);
+ git_tree_free(tree);
+}
+
+void test_diff_rename__unmodified_can_be_renamed(void)
+{
+ git_index *index;
+ git_tree *tree;
+ git_diff *diff = NULL;
+ diff_expects exp;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(
+ git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
+
+ cl_git_pass(p_rename("renames/ikeepsix.txt", "renames/ikeepsix2.txt"));
+
+ cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
+ cl_git_pass(git_index_add_bypath(index, "ikeepsix2.txt"));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(2, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]);
+
+ opts.flags = GIT_DIFF_FIND_ALL;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
+
+ git_diff_free(diff);
+ git_index_free(index);
+ git_tree_free(tree);
+}
+
+void test_diff_rename__rewrite_on_single_file(void)
+{
+ git_index *index;
+ git_diff *diff = NULL;
+ diff_expects exp;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
+
+ diffopts.flags = GIT_DIFF_INCLUDE_UNTRACKED;
+
+ findopts.flags = GIT_DIFF_FIND_FOR_UNTRACKED |
+ GIT_DIFF_FIND_AND_BREAK_REWRITES |
+ GIT_DIFF_FIND_RENAMES_FROM_REWRITES;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_rewritefile("renames/ikeepsix.txt",
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n");
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, &diffopts));
+ cl_git_pass(git_diff_find_similar(diff, &findopts));
+
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(2, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ git_diff_free(diff);
+ git_index_free(index);
+}
+
+void test_diff_rename__can_find_copy_to_split(void)
+{
+ git_str c1 = GIT_STR_INIT;
+ git_index *index;
+ git_tree *tree;
+ git_diff *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
+ diff_expects exp;
+
+ cl_git_pass(git_futils_readbuffer(&c1, "renames/songof7cities.txt"));
+ cl_git_pass(git_futils_writebuffer(&c1, "renames/untimely.txt", 0, 0));
+
+ cl_git_pass(
+ git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_read_tree(index, tree));
+ cl_git_pass(git_index_add_bypath(index, "untimely.txt"));
+
+ diffopts.flags = GIT_DIFF_INCLUDE_UNMODIFIED;
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNMODIFIED]);
+
+ opts.flags = GIT_DIFF_FIND_ALL;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(5, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_COPIED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNMODIFIED]);
+
+ git_diff_free(diff);
+ git_tree_free(tree);
+ git_index_free(index);
+
+ git_str_dispose(&c1);
+}
+
+void test_diff_rename__can_delete_unmodified_deltas(void)
+{
+ git_str c1 = GIT_STR_INIT;
+ git_index *index;
+ git_tree *tree;
+ git_diff *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
+ diff_expects exp;
+
+ cl_git_pass(git_futils_readbuffer(&c1, "renames/songof7cities.txt"));
+ cl_git_pass(git_futils_writebuffer(&c1, "renames/untimely.txt", 0, 0));
+
+ cl_git_pass(
+ git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_read_tree(index, tree));
+ cl_git_pass(git_index_add_bypath(index, "untimely.txt"));
+
+ diffopts.flags = GIT_DIFF_INCLUDE_UNMODIFIED;
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNMODIFIED]);
+
+ opts.flags = GIT_DIFF_FIND_ALL | GIT_DIFF_FIND_REMOVE_UNMODIFIED;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(2, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_COPIED]);
+
+ git_diff_free(diff);
+ git_tree_free(tree);
+ git_index_free(index);
+
+ git_str_dispose(&c1);
+}
+
+void test_diff_rename__matches_config_behavior(void)
+{
+ const char *sha0 = INITIAL_COMMIT;
+ const char *sha1 = COPY_RENAME_COMMIT;
+ const char *sha2 = REWRITE_COPY_COMMIT;
+
+ git_tree *tree0, *tree1, *tree2;
+ git_config *cfg;
+ git_diff *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
+ diff_expects exp;
+
+ opts.flags = GIT_DIFF_FIND_BY_CONFIG;
+ tree0 = resolve_commit_oid_to_tree(g_repo, sha0);
+ tree1 = resolve_commit_oid_to_tree(g_repo, sha1);
+ tree2 = resolve_commit_oid_to_tree(g_repo, sha2);
+
+ diffopts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+
+ /* diff.renames = false; no rename detection should happen */
+ cl_git_pass(git_config_set_bool(cfg, "diff.renames", false));
+ cl_git_pass(git_diff_tree_to_tree(
+ &diff, g_repo, tree0, tree1, &diffopts));
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+ cl_git_pass(git_diff_foreach(diff,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ git_diff_free(diff);
+
+ /* diff.renames = true; should act like -M */
+ cl_git_pass(git_config_set_bool(cfg, "diff.renames", true));
+ cl_git_pass(git_diff_tree_to_tree(
+ &diff, g_repo, tree0, tree1, &diffopts));
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+ cl_git_pass(git_diff_foreach(diff,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(3, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
+ git_diff_free(diff);
+
+ /* diff.renames = copies; should act like -M -C */
+ cl_git_pass(git_config_set_string(cfg, "diff.renames", "copies"));
+ cl_git_pass(git_diff_tree_to_tree(
+ &diff, g_repo, tree1, tree2, &diffopts));
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+ cl_git_pass(git_diff_foreach(diff,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_COPIED]);
+ git_diff_free(diff);
+
+ /* NULL find options is the same as GIT_DIFF_FIND_BY_CONFIG */
+ cl_git_pass(git_diff_tree_to_tree(
+ &diff, g_repo, tree1, tree2, &diffopts));
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_find_similar(diff, NULL));
+ cl_git_pass(git_diff_foreach(diff,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_COPIED]);
+ git_diff_free(diff);
+
+ /* Cleanup */
+ git_tree_free(tree0);
+ git_tree_free(tree1);
+ git_tree_free(tree2);
+ git_config_free(cfg);
+}
+
+void test_diff_rename__can_override_thresholds_when_obeying_config(void)
+{
+ const char *sha1 = COPY_RENAME_COMMIT;
+ const char *sha2 = REWRITE_COPY_COMMIT;
+
+ git_tree *tree1, *tree2;
+ git_config *cfg;
+ git_diff *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
+ diff_expects exp;
+
+ tree1 = resolve_commit_oid_to_tree(g_repo, sha1);
+ tree2 = resolve_commit_oid_to_tree(g_repo, sha2);
+
+ diffopts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
+ opts.flags = GIT_DIFF_FIND_BY_CONFIG;
+
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_git_pass(git_config_set_string(cfg, "diff.renames", "copies"));
+ git_config_free(cfg);
+
+ /* copy threshold = 96%, should see creation of ikeepsix.txt */
+ opts.copy_threshold = 96;
+ cl_git_pass(git_diff_tree_to_tree(
+ &diff, g_repo, tree1, tree2, &diffopts));
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+ cl_git_pass(git_diff_foreach(diff,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]);
+ git_diff_free(diff);
+
+ /* copy threshold = 20%, should see sixserving.txt => ikeepsix.txt */
+ opts.copy_threshold = 20;
+ cl_git_pass(git_diff_tree_to_tree(
+ &diff, g_repo, tree1, tree2, &diffopts));
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+ cl_git_pass(git_diff_foreach(diff,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNMODIFIED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_COPIED]);
+ git_diff_free(diff);
+
+ /* Cleanup */
+ git_tree_free(tree1);
+ git_tree_free(tree2);
+}
+
+void test_diff_rename__by_config_doesnt_mess_with_whitespace_settings(void)
+{
+ const char *sha1 = REWRITE_COPY_COMMIT;
+ const char *sha2 = RENAME_MODIFICATION_COMMIT;
+
+ git_tree *tree1, *tree2;
+ git_config *cfg;
+ git_diff *diff;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
+ diff_expects exp;
+
+ tree1 = resolve_commit_oid_to_tree(g_repo, sha1);
+ tree2 = resolve_commit_oid_to_tree(g_repo, sha2);
+
+ diffopts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
+ opts.flags = GIT_DIFF_FIND_BY_CONFIG;
+
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_git_pass(git_config_set_string(cfg, "diff.renames", "copies"));
+ git_config_free(cfg);
+
+ /* Don't ignore whitespace; this should find a change in sixserving.txt */
+ opts.flags |= 0 | GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE;
+ cl_git_pass(git_diff_tree_to_tree(
+ &diff, g_repo, tree1, tree2, &diffopts));
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+ cl_git_pass(git_diff_foreach(diff,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(5, exp.files);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_ADDED]);
+ git_diff_free(diff);
+
+ /* Cleanup */
+ git_tree_free(tree1);
+ git_tree_free(tree2);
+}
+
+static void expect_files_renamed(const char *one, const char *two, uint32_t whitespace_flags)
+{
+ git_index *index;
+ git_diff *diff = NULL;
+ diff_expects exp;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
+
+ diffopts.flags = GIT_DIFF_INCLUDE_UNTRACKED;
+ findopts.flags = GIT_DIFF_FIND_FOR_UNTRACKED |
+ GIT_DIFF_FIND_AND_BREAK_REWRITES |
+ GIT_DIFF_FIND_RENAMES_FROM_REWRITES |
+ whitespace_flags;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_rewritefile("renames/ikeepsix.txt", one);
+ cl_git_pass(git_index_add_bypath(index, "ikeepsix.txt"));
+
+ cl_git_rmfile("renames/ikeepsix.txt");
+ cl_git_rewritefile("renames/ikeepsix2.txt", two);
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, &diffopts));
+ cl_git_pass(git_diff_find_similar(diff, &findopts));
+
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
+
+ git_diff_free(diff);
+ git_index_free(index);
+}
+
+/* test some variations on empty and blank files */
+void test_diff_rename__empty_files_renamed(void)
+{
+ /* empty files are identical when ignoring whitespace or not */
+ expect_files_renamed("", "", GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE);
+ expect_files_renamed("", "", GIT_DIFF_FIND_IGNORE_WHITESPACE);
+}
+
+/* test that blank files are similar when ignoring whitespace */
+void test_diff_rename__blank_files_renamed_when_ignoring_whitespace(void)
+{
+ expect_files_renamed("", "\n\n", GIT_DIFF_FIND_IGNORE_WHITESPACE);
+ expect_files_renamed("", "\r\n\r\n", GIT_DIFF_FIND_IGNORE_WHITESPACE);
+ expect_files_renamed("\r\n\r\n", "\n\n\n", GIT_DIFF_FIND_IGNORE_WHITESPACE);
+
+ expect_files_renamed(" ", "\n\n", GIT_DIFF_FIND_IGNORE_WHITESPACE);
+ expect_files_renamed(" \n \n", "\n\n", GIT_DIFF_FIND_IGNORE_WHITESPACE);
+}
+
+/* blank files are not similar when whitespace is not ignored */
+static void expect_files_not_renamed(const char *one, const char *two, uint32_t whitespace_flags)
+{
+ git_index *index;
+ git_diff *diff = NULL;
+ diff_expects exp;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT;
+
+ diffopts.flags = GIT_DIFF_INCLUDE_UNTRACKED;
+
+ findopts.flags = GIT_DIFF_FIND_FOR_UNTRACKED |
+ whitespace_flags;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_rewritefile("renames/ikeepsix.txt", one);
+ cl_git_pass(git_index_add_bypath(index, "ikeepsix.txt"));
+
+ cl_git_rmfile("renames/ikeepsix.txt");
+ cl_git_rewritefile("renames/ikeepsix2.txt", two);
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, &diffopts));
+ cl_git_pass(git_diff_find_similar(diff, &findopts));
+
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(2, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ git_diff_free(diff);
+ git_index_free(index);
+}
+
+/* test that blank files are similar when ignoring renames */
+void test_diff_rename__blank_files_not_renamed_when_not_ignoring_whitespace(void)
+{
+ expect_files_not_renamed("", "\r\n\r\n\r\n", GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE);
+ expect_files_not_renamed("", "\n\n\n\n", GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE);
+ expect_files_not_renamed("\n\n\n\n", "\r\n\r\n\r\n", GIT_DIFF_FIND_DONT_IGNORE_WHITESPACE);
+}
+
+/* test that 100% renames and copies emit the correct patch file
+ * git diff --find-copies-harder -M100 -B100 \
+ * 31e47d8c1fa36d7f8d537b96158e3f024de0a9f2 \
+ * 2bc7f351d20b53f1c72c16c4b036e491c478c49a
+ */
+void test_diff_rename__identical(void)
+{
+ const char *old_sha = INITIAL_COMMIT;
+ const char *new_sha = COPY_RENAME_COMMIT;
+ git_tree *old_tree, *new_tree;
+ git_diff *diff;
+ git_diff_options diff_opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options find_opts = GIT_DIFF_FIND_OPTIONS_INIT;
+ git_buf diff_buf = GIT_BUF_INIT;
+ const char *expected =
+ "diff --git a/serving.txt b/sixserving.txt\n"
+ "similarity index 100%\n"
+ "rename from serving.txt\n"
+ "rename to sixserving.txt\n"
+ "diff --git a/sevencities.txt b/songofseven.txt\n"
+ "similarity index 100%\n"
+ "copy from sevencities.txt\n"
+ "copy to songofseven.txt\n";
+
+ old_tree = resolve_commit_oid_to_tree(g_repo, old_sha);
+ new_tree = resolve_commit_oid_to_tree(g_repo, new_sha);
+
+ diff_opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
+ find_opts.flags = GIT_DIFF_FIND_COPIES_FROM_UNMODIFIED |
+ GIT_DIFF_FIND_EXACT_MATCH_ONLY;
+
+ cl_git_pass(git_diff_tree_to_tree(&diff,
+ g_repo, old_tree, new_tree, &diff_opts));
+ cl_git_pass(git_diff_find_similar(diff, &find_opts));
+
+ cl_git_pass(git_diff_to_buf(&diff_buf, diff, GIT_DIFF_FORMAT_PATCH));
+
+ cl_assert_equal_s(expected, diff_buf.ptr);
+
+ git_buf_dispose(&diff_buf);
+ git_diff_free(diff);
+ git_tree_free(old_tree);
+ git_tree_free(new_tree);
+}
+
+void test_diff_rename__rewrite_and_delete(void)
+{
+ const char *old_sha = RENAME_MODIFICATION_COMMIT;
+ const char *new_sha = REWRITE_DELETE_COMMIT;
+ git_tree *old_tree, *new_tree;
+ git_diff *diff;
+ git_diff_find_options find_opts = GIT_DIFF_FIND_OPTIONS_INIT;
+ git_buf diff_buf = GIT_BUF_INIT;
+ const char *expected =
+ "diff --git a/ikeepsix.txt b/ikeepsix.txt\n"
+ "deleted file mode 100644\n"
+ "index eaf4a3e..0000000\n"
+ "--- a/ikeepsix.txt\n"
+ "+++ /dev/null\n"
+ "@@ -1,27 +0,0 @@\n"
+ "-I Keep Six Honest Serving-Men\n"
+ "-=============================\n"
+ "-\n"
+ "-She sends'em abroad on her own affairs,\n"
+ "- From the second she opens her eyes—\n"
+ "-One million Hows, two million Wheres,\n"
+ "-And seven million Whys!\n"
+ "-\n"
+ "-I let them rest from nine till five,\n"
+ "- For I am busy then,\n"
+ "-As well as breakfast, lunch, and tea,\n"
+ "- For they are hungry men.\n"
+ "-But different folk have different views;\n"
+ "-I know a person small—\n"
+ "-She keeps ten million serving-men,\n"
+ "-Who get no rest at all!\n"
+ "-\n"
+ "- -- Rudyard Kipling\n"
+ "-\n"
+ "-I KEEP six honest serving-men\n"
+ "- (They taught me all I knew);\n"
+ "-Their names are What and Why and When\n"
+ "- And How and Where and Who.\n"
+ "-I send them over land and sea,\n"
+ "- I send them east and west;\n"
+ "-But after they have worked for me,\n"
+ "- I give them all a rest.\n"
+ "diff --git a/songof7cities.txt b/songof7cities.txt\n"
+ "index 4210ffd..95ceb12 100644\n"
+ "--- a/songof7cities.txt\n"
+ "+++ b/songof7cities.txt\n"
+ "@@ -1,45 +1,45 @@\n"
+ "-The Song of Seven Cities\n"
+ "+THE SONG OF SEVEN CITIES\n"
+ " ------------------------\n"
+ " \n"
+ "-I WAS Lord of Cities very sumptuously builded.\n"
+ "-Seven roaring Cities paid me tribute from afar.\n"
+ "-Ivory their outposts were--the guardrooms of them gilded,\n"
+ "-And garrisoned with Amazons invincible in war.\n"
+ "-\n"
+ "-All the world went softly when it walked before my Cities--\n"
+ "-Neither King nor Army vexed my peoples at their toil,\n"
+ "-Never horse nor chariot irked or overbore my Cities,\n"
+ "-Never Mob nor Ruler questioned whence they drew their spoil.\n"
+ "-\n"
+ "-Banded, mailed and arrogant from sunrise unto sunset;\n"
+ "-Singing while they sacked it, they possessed the land at large.\n"
+ "-Yet when men would rob them, they resisted, they made onset\n"
+ "-And pierced the smoke of battle with a thousand-sabred charge.\n"
+ "-\n"
+ "-So they warred and trafficked only yesterday, my Cities.\n"
+ "-To-day there is no mark or mound of where my Cities stood.\n"
+ "-For the River rose at midnight and it washed away my Cities.\n"
+ "-They are evened with Atlantis and the towns before the Flood.\n"
+ "-\n"
+ "-Rain on rain-gorged channels raised the water-levels round them,\n"
+ "-Freshet backed on freshet swelled and swept their world from sight,\n"
+ "-Till the emboldened floods linked arms and, flashing forward, drowned them--\n"
+ "-Drowned my Seven Cities and their peoples in one night!\n"
+ "-\n"
+ "-Low among the alders lie their derelict foundations,\n"
+ "-The beams wherein they trusted and the plinths whereon they built--\n"
+ "-My rulers and their treasure and their unborn populations,\n"
+ "-Dead, destroyed, aborted, and defiled with mud and silt!\n"
+ "-\n"
+ "-The Daughters of the Palace whom they cherished in my Cities,\n"
+ "-My silver-tongued Princesses, and the promise of their May--\n"
+ "-Their bridegrooms of the June-tide--all have perished in my Cities,\n"
+ "-With the harsh envenomed virgins that can neither love nor play.\n"
+ "-\n"
+ "-I was Lord of Cities--I will build anew my Cities,\n"
+ "-Seven, set on rocks, above the wrath of any flood.\n"
+ "-Nor will I rest from search till I have filled anew my Cities\n"
+ "-With peoples undefeated of the dark, enduring blood.\n"
+ "+I WAS LORD OF CITIES VERY SUMPTUOUSLY BUILDED.\n"
+ "+SEVEN ROARING CITIES PAID ME TRIBUTE FROM AFAR.\n"
+ "+IVORY THEIR OUTPOSTS WERE--THE GUARDROOMS OF THEM GILDED,\n"
+ "+AND GARRISONED WITH AMAZONS INVINCIBLE IN WAR.\n"
+ "+\n"
+ "+ALL THE WORLD WENT SOFTLY WHEN IT WALKED BEFORE MY CITIES--\n"
+ "+NEITHER KING NOR ARMY VEXED MY PEOPLES AT THEIR TOIL,\n"
+ "+NEVER HORSE NOR CHARIOT IRKED OR OVERBORE MY CITIES,\n"
+ "+NEVER MOB NOR RULER QUESTIONED WHENCE THEY DREW THEIR SPOIL.\n"
+ "+\n"
+ "+BANDED, MAILED AND ARROGANT FROM SUNRISE UNTO SUNSET;\n"
+ "+SINGING WHILE THEY SACKED IT, THEY POSSESSED THE LAND AT LARGE.\n"
+ "+YET WHEN MEN WOULD ROB THEM, THEY RESISTED, THEY MADE ONSET\n"
+ "+AND PIERCED THE SMOKE OF BATTLE WITH A THOUSAND-SABRED CHARGE.\n"
+ "+\n"
+ "+SO THEY WARRED AND TRAFFICKED ONLY YESTERDAY, MY CITIES.\n"
+ "+TO-DAY THERE IS NO MARK OR MOUND OF WHERE MY CITIES STOOD.\n"
+ "+FOR THE RIVER ROSE AT MIDNIGHT AND IT WASHED AWAY MY CITIES.\n"
+ "+THEY ARE EVENED WITH ATLANTIS AND THE TOWNS BEFORE THE FLOOD.\n"
+ "+\n"
+ "+RAIN ON RAIN-GORGED CHANNELS RAISED THE WATER-LEVELS ROUND THEM,\n"
+ "+FRESHET BACKED ON FRESHET SWELLED AND SWEPT THEIR WORLD FROM SIGHT,\n"
+ "+TILL THE EMBOLDENED FLOODS LINKED ARMS AND, FLASHING FORWARD, DROWNED THEM--\n"
+ "+DROWNED MY SEVEN CITIES AND THEIR PEOPLES IN ONE NIGHT!\n"
+ "+\n"
+ "+LOW AMONG THE ALDERS LIE THEIR DERELICT FOUNDATIONS,\n"
+ "+THE BEAMS WHEREIN THEY TRUSTED AND THE PLINTHS WHEREON THEY BUILT--\n"
+ "+MY RULERS AND THEIR TREASURE AND THEIR UNBORN POPULATIONS,\n"
+ "+DEAD, DESTROYED, ABORTED, AND DEFILED WITH MUD AND SILT!\n"
+ "+\n"
+ "+THE DAUGHTERS OF THE PALACE WHOM THEY CHERISHED IN MY CITIES,\n"
+ "+MY SILVER-TONGUED PRINCESSES, AND THE PROMISE OF THEIR MAY--\n"
+ "+THEIR BRIDEGROOMS OF THE JUNE-TIDE--ALL HAVE PERISHED IN MY CITIES,\n"
+ "+WITH THE HARSH ENVENOMED VIRGINS THAT CAN NEITHER LOVE NOR PLAY.\n"
+ "+\n"
+ "+I WAS LORD OF CITIES--I WILL BUILD ANEW MY CITIES,\n"
+ "+SEVEN, SET ON ROCKS, ABOVE THE WRATH OF ANY FLOOD.\n"
+ "+NOR WILL I REST FROM SEARCH TILL I HAVE FILLED ANEW MY CITIES\n"
+ "+WITH PEOPLES UNDEFEATED OF THE DARK, ENDURING BLOOD.\n"
+ " \n"
+ " To the sound of trumpets shall their seed restore my Cities\n"
+ " Wealthy and well-weaponed, that once more may I behold\n";
+
+ old_tree = resolve_commit_oid_to_tree(g_repo, old_sha);
+ new_tree = resolve_commit_oid_to_tree(g_repo, new_sha);
+
+ find_opts.flags = GIT_DIFF_FIND_RENAMES_FROM_REWRITES;
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, old_tree, new_tree, NULL));
+ cl_git_pass(git_diff_find_similar(diff, &find_opts));
+
+ cl_git_pass(git_diff_to_buf(&diff_buf, diff, GIT_DIFF_FORMAT_PATCH));
+
+ cl_assert_equal_s(expected, diff_buf.ptr);
+
+ git_buf_dispose(&diff_buf);
+ git_diff_free(diff);
+ git_tree_free(old_tree);
+ git_tree_free(new_tree);
+}
+
+void test_diff_rename__delete_and_rename(void)
+{
+ const char *old_sha = RENAME_MODIFICATION_COMMIT;
+ const char *new_sha = DELETE_RENAME_COMMIT;
+ git_tree *old_tree, *new_tree;
+ git_diff *diff;
+ git_diff_find_options find_opts = GIT_DIFF_FIND_OPTIONS_INIT;
+ git_buf diff_buf = GIT_BUF_INIT;
+ const char *expected =
+ "diff --git a/sixserving.txt b/sixserving.txt\n"
+ "deleted file mode 100644\n"
+ "index f90d4fc..0000000\n"
+ "--- a/sixserving.txt\n"
+ "+++ /dev/null\n"
+ "@@ -1,25 +0,0 @@\n"
+ "-I KEEP six honest serving-men\n"
+ "- (They taught me all I knew);\n"
+ "-Their names are What and Why and When\n"
+ "- And How and Where and Who.\n"
+ "-I send them over land and sea,\n"
+ "- I send them east and west;\n"
+ "-But after they have worked for me,\n"
+ "- I give them all a rest.\n"
+ "-\n"
+ "-I let them rest from nine till five,\n"
+ "- For I am busy then,\n"
+ "-As well as breakfast, lunch, and tea,\n"
+ "- For they are hungry men.\n"
+ "-But different folk have different views;\n"
+ "-I know a person small—\n"
+ "-She keeps ten million serving-men,\n"
+ "-Who get no rest at all!\n"
+ "-\n"
+ "-She sends'em abroad on her own affairs,\n"
+ "- From the second she opens her eyes—\n"
+ "-One million Hows, two million Wheres,\n"
+ "-And seven million Whys!\n"
+ "-\n"
+ "- -- Rudyard Kipling\n"
+ "-\n"
+ "diff --git a/songof7cities.txt b/sixserving.txt\n"
+ "similarity index 100%\n"
+ "rename from songof7cities.txt\n"
+ "rename to sixserving.txt\n";
+
+ old_tree = resolve_commit_oid_to_tree(g_repo, old_sha);
+ new_tree = resolve_commit_oid_to_tree(g_repo, new_sha);
+
+ find_opts.flags = GIT_DIFF_FIND_RENAMES_FROM_REWRITES;
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, old_tree, new_tree, NULL));
+ cl_git_pass(git_diff_find_similar(diff, &find_opts));
+
+ cl_git_pass(git_diff_to_buf(&diff_buf, diff, GIT_DIFF_FORMAT_PATCH));
+
+ cl_assert_equal_s(expected, diff_buf.ptr);
+
+ git_buf_dispose(&diff_buf);
+ git_diff_free(diff);
+ git_tree_free(old_tree);
+ git_tree_free(new_tree);
+}
+
+/*
+ * The break_rewrite branch contains a testcase reduced from
+ * a real-world scenario, rather than being "constructed" like
+ * the above tests seem to be. There are two commits layered
+ * on top of the repo's initial commit; the base commit which
+ * clears out the files from the initial commit and installs
+ * four files. And then there's the modification commit which
+ * mutates the files in such a way as to trigger the bug in
+ * libgit2.
+ * commit db98035f715427eef1f5e17f03e1801c05301e9e
+ * serving.txt (deleted)
+ * sevencities.txt (deleted)
+ * AAA (313 lines)
+ * BBB (314 lines)
+ * CCC (704 lines)
+ * DDD (314 lines, identical to BBB)
+ * commit 7e7bfb88ba9bc65fd700fee1819cf1c317aafa56
+ * This deletes CCC and makes slight modifications
+ * to AAA, BBB, and DDD. The find_best_matches loop
+ * for git_diff_find_similar computes the following:
+ * CCC moved to AAA (similarity 91)
+ * CCC copied to AAA (similarity 91)
+ * DDD moved to BBB (similarity 52)
+ * CCC copied to BBB (similarity 90)
+ * BBB moved to DDD (similarity 52)
+ * CCC copied to DDD (similarity 90)
+ * The code to rewrite the diffs by resolving these
+ * copies/renames would resolve the BBB <-> DDD moves
+ * but then still leave BBB as a rename target for
+ * the deleted file CCC. Since the split flag on BBB
+ * was cleared, this would trigger an error.
+ */
+void test_diff_rename__break_rewrite(void)
+{
+ const char *old_sha = BREAK_REWRITE_BASE_COMMIT;
+ const char *new_sha = BREAK_REWRITE_COMMIT;
+ git_tree *old_tree, *new_tree;
+ git_diff *diff;
+ git_diff_find_options find_opts = GIT_DIFF_FIND_OPTIONS_INIT;
+
+ old_tree = resolve_commit_oid_to_tree(g_repo, old_sha);
+ new_tree = resolve_commit_oid_to_tree(g_repo, new_sha);
+
+ find_opts.flags = GIT_DIFF_FIND_RENAMES | GIT_DIFF_FIND_COPIES | GIT_DIFF_BREAK_REWRITES | GIT_DIFF_BREAK_REWRITES_FOR_RENAMES_ONLY;
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, old_tree, new_tree, NULL));
+ cl_git_pass(git_diff_find_similar(diff, &find_opts));
+
+ git_diff_free(diff);
+ git_tree_free(old_tree);
+ git_tree_free(new_tree);
+}
diff --git a/tests/libgit2/diff/stats.c b/tests/libgit2/diff/stats.c
new file mode 100644
index 0000000..7af8915
--- /dev/null
+++ b/tests/libgit2/diff/stats.c
@@ -0,0 +1,379 @@
+#include "clar.h"
+#include "clar_libgit2.h"
+
+#include "commit.h"
+#include "diff.h"
+#include "diff_generate.h"
+#include "diff_helpers.h"
+
+static git_repository *_repo;
+static git_diff_stats *_stats;
+
+void test_diff_stats__initialize(void)
+{
+ _repo = cl_git_sandbox_init("diff_format_email");
+}
+
+void test_diff_stats__cleanup(void)
+{
+ git_diff_stats_free(_stats); _stats = NULL;
+ cl_git_sandbox_cleanup();
+}
+
+static void diff_stats_from_commit_oid(
+ git_diff_stats **stats, const char *oidstr, bool rename)
+{
+ git_oid oid;
+ git_commit *commit;
+ git_diff *diff;
+
+ git_oid__fromstr(&oid, oidstr, GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, _repo, &oid));
+ cl_git_pass(git_diff__commit(&diff, _repo, commit, NULL));
+ if (rename)
+ cl_git_pass(git_diff_find_similar(diff, NULL));
+ cl_git_pass(git_diff_get_stats(stats, diff));
+
+ git_diff_free(diff);
+ git_commit_free(commit);
+}
+
+void test_diff_stats__stat(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+ const char *stat =
+ " file1.txt | 8 +++++---\n" \
+ " 1 file changed, 5 insertions(+), 3 deletions(-)\n";
+
+ diff_stats_from_commit_oid(
+ &_stats, "9264b96c6d104d0e07ae33d3007b6a48246c6f92", false);
+
+ cl_assert_equal_sz(1, git_diff_stats_files_changed(_stats));
+ cl_assert_equal_sz(5, git_diff_stats_insertions(_stats));
+ cl_assert_equal_sz(3, git_diff_stats_deletions(_stats));
+
+ cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0));
+ cl_assert(strcmp(buf.ptr, stat) == 0);
+ git_buf_dispose(&buf);
+
+ cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 80));
+ cl_assert(strcmp(buf.ptr, stat) == 0);
+ git_buf_dispose(&buf);
+}
+
+void test_diff_stats__multiple_hunks(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+ const char *stat =
+ " file2.txt | 5 +++--\n" \
+ " file3.txt | 6 ++++--\n" \
+ " 2 files changed, 7 insertions(+), 4 deletions(-)\n";
+
+ diff_stats_from_commit_oid(
+ &_stats, "cd471f0d8770371e1bc78bcbb38db4c7e4106bd2", false);
+
+ cl_assert_equal_sz(2, git_diff_stats_files_changed(_stats));
+ cl_assert_equal_sz(7, git_diff_stats_insertions(_stats));
+ cl_assert_equal_sz(4, git_diff_stats_deletions(_stats));
+
+ cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0));
+ cl_assert_equal_s(stat, buf.ptr);
+ git_buf_dispose(&buf);
+}
+
+void test_diff_stats__numstat(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+ const char *stat =
+ "3 2 file2.txt\n"
+ "4 2 file3.txt\n";
+
+ diff_stats_from_commit_oid(
+ &_stats, "cd471f0d8770371e1bc78bcbb38db4c7e4106bd2", false);
+
+ cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_NUMBER, 0));
+ cl_assert_equal_s(stat, buf.ptr);
+ git_buf_dispose(&buf);
+}
+
+void test_diff_stats__shortstat(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+ const char *stat =
+ " 1 file changed, 5 insertions(+), 3 deletions(-)\n";
+
+ diff_stats_from_commit_oid(
+ &_stats, "9264b96c6d104d0e07ae33d3007b6a48246c6f92", false);
+
+ cl_assert_equal_sz(1, git_diff_stats_files_changed(_stats));
+ cl_assert_equal_sz(5, git_diff_stats_insertions(_stats));
+ cl_assert_equal_sz(3, git_diff_stats_deletions(_stats));
+
+ cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_SHORT, 0));
+ cl_assert_equal_s(stat, buf.ptr);
+ git_buf_dispose(&buf);
+}
+
+void test_diff_stats__shortstat_noinsertions(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+ const char *stat =
+ " 1 file changed, 2 deletions(-)\n";
+
+ diff_stats_from_commit_oid(
+ &_stats, "06b7b69a62cbd1e53c6c4e0c3f16473dcfdb4af6", false);
+
+ cl_assert_equal_sz(1, git_diff_stats_files_changed(_stats));
+ cl_assert_equal_sz(0, git_diff_stats_insertions(_stats));
+ cl_assert_equal_sz(2, git_diff_stats_deletions(_stats));
+
+ cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_SHORT, 0));
+ cl_assert_equal_s(stat, buf.ptr);
+ git_buf_dispose(&buf);
+}
+
+void test_diff_stats__shortstat_nodeletions(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+ const char *stat =
+ " 1 file changed, 3 insertions(+)\n";
+
+ diff_stats_from_commit_oid(
+ &_stats, "5219b9784f9a92d7bd7cb567a6d6a21bfb86697e", false);
+
+ cl_assert_equal_sz(1, git_diff_stats_files_changed(_stats));
+ cl_assert_equal_sz(3, git_diff_stats_insertions(_stats));
+ cl_assert_equal_sz(0, git_diff_stats_deletions(_stats));
+
+ cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_SHORT, 0));
+ cl_assert_equal_s(stat, buf.ptr);
+ git_buf_dispose(&buf);
+}
+
+void test_diff_stats__rename(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+ const char *stat =
+ " file2.txt => file2.txt.renamed | 1 +\n"
+ " file3.txt => file3.txt.renamed | 4 +++-\n"
+ " 2 files changed, 4 insertions(+), 1 deletion(-)\n";
+
+ diff_stats_from_commit_oid(
+ &_stats, "8947a46e2097638ca6040ad4877246f4186ec3bd", true);
+
+ cl_assert_equal_sz(2, git_diff_stats_files_changed(_stats));
+ cl_assert_equal_sz(4, git_diff_stats_insertions(_stats));
+ cl_assert_equal_sz(1, git_diff_stats_deletions(_stats));
+
+ cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0));
+ cl_assert_equal_s(stat, buf.ptr);
+ git_buf_dispose(&buf);
+}
+
+void test_diff_stats__rename_nochanges(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+ const char *stat =
+ " file2.txt.renamed => file2.txt.renamed2 | 0\n"
+ " file3.txt.renamed => file3.txt.renamed2 | 0\n"
+ " 2 files changed, 0 insertions(+), 0 deletions(-)\n";
+
+ diff_stats_from_commit_oid(
+ &_stats, "3991dce9e71a0641ca49a6a4eea6c9e7ff402ed4", true);
+
+ cl_assert_equal_sz(2, git_diff_stats_files_changed(_stats));
+ cl_assert_equal_sz(0, git_diff_stats_insertions(_stats));
+ cl_assert_equal_sz(0, git_diff_stats_deletions(_stats));
+
+ cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0));
+ cl_assert_equal_s(stat, buf.ptr);
+ git_buf_dispose(&buf);
+}
+
+void test_diff_stats__rename_and_modifiy(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+ const char *stat =
+ " file2.txt.renamed2 | 2 +-\n"
+ " file3.txt.renamed2 => file3.txt.renamed | 0\n"
+ " 2 files changed, 1 insertion(+), 1 deletion(-)\n";
+
+ diff_stats_from_commit_oid(
+ &_stats, "4ca10087e696d2ba78d07b146a118e9a7096ed4f", true);
+
+ cl_assert_equal_sz(2, git_diff_stats_files_changed(_stats));
+ cl_assert_equal_sz(1, git_diff_stats_insertions(_stats));
+ cl_assert_equal_sz(1, git_diff_stats_deletions(_stats));
+
+ cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0));
+ cl_assert_equal_s(stat, buf.ptr);
+ git_buf_dispose(&buf);
+}
+
+void test_diff_stats__rename_in_subdirectory(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+ const char *stat =
+ " dir/{orig.txt => renamed.txt} | 0\n"
+ " 1 file changed, 0 insertions(+), 0 deletions(-)\n";
+
+ diff_stats_from_commit_oid(
+ &_stats, "0db2a262bc8c5c3cba55254730045a8258da7a37", true);
+
+ cl_assert_equal_sz(1, git_diff_stats_files_changed(_stats));
+ cl_assert_equal_sz(0, git_diff_stats_insertions(_stats));
+ cl_assert_equal_sz(0, git_diff_stats_deletions(_stats));
+
+ cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0));
+ cl_assert_equal_s(stat, buf.ptr);
+ git_buf_dispose(&buf);
+}
+
+void test_diff_stats__rename_no_find(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+ const char *stat =
+ " file2.txt | 5 -----\n"
+ " file2.txt.renamed | 6 ++++++\n"
+ " file3.txt | 5 -----\n"
+ " file3.txt.renamed | 7 +++++++\n"
+ " 4 files changed, 13 insertions(+), 10 deletions(-)\n";
+
+ diff_stats_from_commit_oid(
+ &_stats, "8947a46e2097638ca6040ad4877246f4186ec3bd", false);
+
+ cl_assert_equal_sz(4, git_diff_stats_files_changed(_stats));
+ cl_assert_equal_sz(13, git_diff_stats_insertions(_stats));
+ cl_assert_equal_sz(10, git_diff_stats_deletions(_stats));
+
+ cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0));
+ cl_assert_equal_s(stat, buf.ptr);
+ git_buf_dispose(&buf);
+}
+
+void test_diff_stats__rename_nochanges_no_find(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+ const char *stat =
+ " file2.txt.renamed | 6 ------\n"
+ " file2.txt.renamed2 | 6 ++++++\n"
+ " file3.txt.renamed | 7 -------\n"
+ " file3.txt.renamed2 | 7 +++++++\n"
+ " 4 files changed, 13 insertions(+), 13 deletions(-)\n";
+
+ diff_stats_from_commit_oid(
+ &_stats, "3991dce9e71a0641ca49a6a4eea6c9e7ff402ed4", false);
+
+ cl_assert_equal_sz(4, git_diff_stats_files_changed(_stats));
+ cl_assert_equal_sz(13, git_diff_stats_insertions(_stats));
+ cl_assert_equal_sz(13, git_diff_stats_deletions(_stats));
+
+ cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0));
+ cl_assert_equal_s(stat, buf.ptr);
+ git_buf_dispose(&buf);
+}
+
+void test_diff_stats__rename_and_modify_no_find(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+ const char *stat =
+ " file2.txt.renamed2 | 2 +-\n"
+ " file3.txt.renamed | 7 +++++++\n"
+ " file3.txt.renamed2 | 7 -------\n"
+ " 3 files changed, 8 insertions(+), 8 deletions(-)\n";
+
+ diff_stats_from_commit_oid(
+ &_stats, "4ca10087e696d2ba78d07b146a118e9a7096ed4f", false);
+
+ cl_assert_equal_sz(3, git_diff_stats_files_changed(_stats));
+ cl_assert_equal_sz(8, git_diff_stats_insertions(_stats));
+ cl_assert_equal_sz(8, git_diff_stats_deletions(_stats));
+
+ cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0));
+ cl_assert_equal_s(stat, buf.ptr);
+ git_buf_dispose(&buf);
+}
+
+void test_diff_stats__binary(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+ const char *stat =
+ " binary.bin | Bin 3 -> 5 bytes\n"
+ " 1 file changed, 0 insertions(+), 0 deletions(-)\n";
+
+ diff_stats_from_commit_oid(
+ &_stats, "8d7523f6fcb2404257889abe0d96f093d9f524f9", false);
+
+ cl_assert_equal_sz(1, git_diff_stats_files_changed(_stats));
+ cl_assert_equal_sz(0, git_diff_stats_insertions(_stats));
+ cl_assert_equal_sz(0, git_diff_stats_deletions(_stats));
+
+ cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL, 0));
+ cl_assert_equal_s(stat, buf.ptr);
+ git_buf_dispose(&buf);
+}
+
+void test_diff_stats__binary_numstat(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+ const char *stat =
+ "- - binary.bin\n";
+
+ diff_stats_from_commit_oid(
+ &_stats, "8d7523f6fcb2404257889abe0d96f093d9f524f9", false);
+
+ cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_NUMBER, 0));
+ cl_assert_equal_s(stat, buf.ptr);
+ git_buf_dispose(&buf);
+}
+
+void test_diff_stats__mode_change(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+ const char *stat =
+ " file1.txt.renamed | 0\n" \
+ " 1 file changed, 0 insertions(+), 0 deletions(-)\n" \
+ " mode change 100644 => 100755 file1.txt.renamed\n";
+
+ diff_stats_from_commit_oid(
+ &_stats, "7ade76dd34bba4733cf9878079f9fd4a456a9189", false);
+
+ cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL | GIT_DIFF_STATS_INCLUDE_SUMMARY, 0));
+ cl_assert_equal_s(stat, buf.ptr);
+ git_buf_dispose(&buf);
+}
+
+void test_diff_stats__new_file(void)
+{
+ git_diff *diff;
+ git_buf buf = GIT_BUF_INIT;
+
+ const char *input =
+ "---\n"
+ " Gurjeet Singh | 1 +\n"
+ " 1 file changed, 1 insertion(+)\n"
+ " create mode 100644 Gurjeet Singh\n"
+ "\n"
+ "diff --git a/Gurjeet Singh b/Gurjeet Singh\n"
+ "new file mode 100644\n"
+ "index 0000000..6d0ecfd\n"
+ "--- /dev/null\n"
+ "+++ b/Gurjeet Singh \n"
+ "@@ -0,0 +1 @@\n"
+ "+I'm about to try git send-email\n"
+ "-- \n"
+ "2.21.0\n";
+
+ const char *stat =
+ " Gurjeet Singh | 1 +\n"
+ " 1 file changed, 1 insertion(+)\n"
+ " create mode 100644 Gurjeet Singh\n";
+
+ cl_git_pass(diff_from_buffer(&diff, input, strlen(input)));
+ cl_git_pass(git_diff_get_stats(&_stats, diff));
+ cl_git_pass(git_diff_stats_to_buf(&buf, _stats, GIT_DIFF_STATS_FULL | GIT_DIFF_STATS_INCLUDE_SUMMARY, 0));
+ cl_assert_equal_s(stat, buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_diff_free(diff);
+}
diff --git a/tests/libgit2/diff/submodules.c b/tests/libgit2/diff/submodules.c
new file mode 100644
index 0000000..0436ca5
--- /dev/null
+++ b/tests/libgit2/diff/submodules.c
@@ -0,0 +1,543 @@
+#include "clar_libgit2.h"
+#include "repository.h"
+#include "posix.h"
+#include "diff_helpers.h"
+#include "../submodule/submodule_helpers.h"
+
+static git_repository *g_repo = NULL;
+
+void test_diff_submodules__initialize(void)
+{
+}
+
+void test_diff_submodules__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+#define get_buf_ptr(buf) ((buf)->size ? (buf)->ptr : NULL)
+
+static void check_diff_patches_at_line(
+ git_diff *diff, const char **expected,
+ const char *file, const char *func, int line)
+{
+ const git_diff_delta *delta;
+ git_patch *patch = NULL;
+ size_t d, num_d = git_diff_num_deltas(diff);
+ git_buf buf = GIT_BUF_INIT;
+
+ for (d = 0; d < num_d; ++d, git_patch_free(patch)) {
+ cl_git_pass(git_patch_from_diff(&patch, diff, d));
+ cl_assert((delta = git_patch_get_delta(patch)) != NULL);
+
+ if (delta->status == GIT_DELTA_UNMODIFIED) {
+ cl_assert_at_line(expected[d] == NULL, file, func, line);
+ continue;
+ }
+
+ if (expected[d] && !strcmp(expected[d], "<SKIP>"))
+ continue;
+ if (expected[d] && !strcmp(expected[d], "<UNTRACKED>")) {
+ cl_assert_at_line(delta->status == GIT_DELTA_UNTRACKED, file, func, line);
+ continue;
+ }
+ if (expected[d] && !strcmp(expected[d], "<END>")) {
+ cl_git_pass(git_patch_to_buf(&buf, patch));
+ cl_assert_at_line(!strcmp(expected[d], "<END>"), file, func, line);
+ }
+
+ cl_git_pass(git_patch_to_buf(&buf, patch));
+
+ clar__assert_equal(
+ file, func, line, "expected diff did not match actual diff", 1,
+ "%s", expected[d], get_buf_ptr(&buf));
+ git_buf_dispose(&buf);
+ }
+
+ cl_assert_at_line(expected[d] && !strcmp(expected[d], "<END>"), file, func, line);
+}
+
+#define check_diff_patches(diff, exp) \
+ check_diff_patches_at_line(diff, exp, __FILE__, __func__, __LINE__)
+
+void test_diff_submodules__unmodified_submodule(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ static const char *expected[] = {
+ "<SKIP>", /* .gitmodules */
+ NULL, /* added */
+ NULL, /* ignored */
+ "diff --git a/modified b/modified\nindex 092bfb9..452216e 100644\n--- a/modified\n+++ b/modified\n@@ -1 +1,2 @@\n-yo\n+changed\n+\n", /* modified */
+ NULL, /* testrepo.git */
+ NULL, /* unmodified */
+ NULL, /* untracked */
+ "<END>"
+ };
+
+ g_repo = setup_fixture_submodules();
+
+ opts.flags = GIT_DIFF_INCLUDE_IGNORED |
+ GIT_DIFF_INCLUDE_UNTRACKED |
+ GIT_DIFF_INCLUDE_UNMODIFIED;
+ opts.old_prefix = "a"; opts.new_prefix = "b";
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected);
+ git_diff_free(diff);
+}
+
+void test_diff_submodules__dirty_submodule(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ static const char *expected[] = {
+ "<SKIP>", /* .gitmodules */
+ NULL, /* added */
+ NULL, /* ignored */
+ "diff --git a/modified b/modified\nindex 092bfb9..452216e 100644\n--- a/modified\n+++ b/modified\n@@ -1 +1,2 @@\n-yo\n+changed\n+\n", /* modified */
+ "diff --git a/testrepo b/testrepo\nindex a65fedf..a65fedf 160000\n--- a/testrepo\n+++ b/testrepo\n@@ -1 +1 @@\n-Subproject commit a65fedf39aefe402d3bb6e24df4d4f5fe4547750\n+Subproject commit a65fedf39aefe402d3bb6e24df4d4f5fe4547750-dirty\n", /* testrepo.git */
+ NULL, /* unmodified */
+ NULL, /* untracked */
+ "<END>"
+ };
+
+ g_repo = setup_fixture_submodules();
+
+ cl_git_rewritefile("submodules/testrepo/README", "heyheyhey");
+ cl_git_mkfile("submodules/testrepo/all_new.txt", "never seen before");
+
+ opts.flags = GIT_DIFF_INCLUDE_IGNORED |
+ GIT_DIFF_INCLUDE_UNTRACKED |
+ GIT_DIFF_INCLUDE_UNMODIFIED;
+ opts.old_prefix = "a"; opts.new_prefix = "b";
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected);
+ git_diff_free(diff);
+}
+
+void test_diff_submodules__dirty_submodule_2(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL, *diff2 = NULL;
+ char *smpath = "testrepo";
+ static const char *expected_none[] = {
+ "<END>"
+ };
+ static const char *expected_dirty[] = {
+ "diff --git a/testrepo b/testrepo\nindex a65fedf..a65fedf 160000\n--- a/testrepo\n+++ b/testrepo\n@@ -1 +1 @@\n-Subproject commit a65fedf39aefe402d3bb6e24df4d4f5fe4547750\n+Subproject commit a65fedf39aefe402d3bb6e24df4d4f5fe4547750-dirty\n", /* testrepo.git */
+ "<END>"
+ };
+
+ g_repo = setup_fixture_submodules();
+
+ opts.flags = GIT_DIFF_INCLUDE_UNTRACKED |
+ GIT_DIFF_SHOW_UNTRACKED_CONTENT |
+ GIT_DIFF_RECURSE_UNTRACKED_DIRS |
+ GIT_DIFF_DISABLE_PATHSPEC_MATCH;
+ opts.old_prefix = "a"; opts.new_prefix = "b";
+ opts.pathspec.count = 1;
+ opts.pathspec.strings = &smpath;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_none);
+ git_diff_free(diff);
+
+ cl_git_rewritefile("submodules/testrepo/README", "heyheyhey");
+ cl_git_mkfile("submodules/testrepo/all_new.txt", "never seen before");
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_dirty);
+
+ {
+ git_tree *head;
+
+ cl_git_pass(git_repository_head_tree(&head, g_repo));
+ cl_git_pass(git_diff_tree_to_index(&diff2, g_repo, head, NULL, &opts));
+ cl_git_pass(git_diff_merge(diff, diff2));
+ git_diff_free(diff2);
+ git_tree_free(head);
+
+ check_diff_patches(diff, expected_dirty);
+ }
+
+ git_diff_free(diff);
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_dirty);
+ git_diff_free(diff);
+}
+
+void test_diff_submodules__submod2_index_to_wd(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ static const char *expected[] = {
+ "<SKIP>", /* .gitmodules */
+ "<UNTRACKED>", /* not-submodule */
+ "<UNTRACKED>", /* not */
+ "diff --git a/sm_changed_file b/sm_changed_file\nindex 4800958..4800958 160000\n--- a/sm_changed_file\n+++ b/sm_changed_file\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_file */
+ "diff --git a/sm_changed_head b/sm_changed_head\nindex 4800958..3d9386c 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n", /* sm_changed_head */
+ "<UNTRACKED>", /* sm_changed_head- */
+ "<UNTRACKED>", /* sm_changed_head_ */
+ "diff --git a/sm_changed_index b/sm_changed_index\nindex 4800958..4800958 160000\n--- a/sm_changed_index\n+++ b/sm_changed_index\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_index */
+ "diff --git a/sm_changed_untracked_file b/sm_changed_untracked_file\nindex 4800958..4800958 160000\n--- a/sm_changed_untracked_file\n+++ b/sm_changed_untracked_file\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_untracked_file */
+ "diff --git a/sm_missing_commits b/sm_missing_commits\nindex 4800958..5e49635 160000\n--- a/sm_missing_commits\n+++ b/sm_missing_commits\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 5e4963595a9774b90524d35a807169049de8ccad\n", /* sm_missing_commits */
+ "<END>"
+ };
+
+ g_repo = setup_fixture_submod2();
+
+ /* bracket existing submodule with similarly named items */
+ cl_git_mkfile("submod2/sm_changed_head-", "hello");
+ cl_git_mkfile("submod2/sm_changed_head_", "hello");
+
+ opts.flags = GIT_DIFF_INCLUDE_UNTRACKED;
+ opts.old_prefix = "a"; opts.new_prefix = "b";
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected);
+ git_diff_free(diff);
+}
+
+void test_diff_submodules__submod2_head_to_index(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_tree *head;
+ git_diff *diff = NULL;
+ static const char *expected[] = {
+ "<SKIP>", /* .gitmodules */
+ "diff --git a/sm_added_and_uncommited b/sm_added_and_uncommited\nnew file mode 160000\nindex 0000000..4800958\n--- /dev/null\n+++ b/sm_added_and_uncommited\n@@ -0,0 +1 @@\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n", /* sm_added_and_uncommited */
+ "<END>"
+ };
+
+ g_repo = setup_fixture_submod2();
+
+ cl_git_pass(git_repository_head_tree(&head, g_repo));
+
+ opts.flags = GIT_DIFF_INCLUDE_UNTRACKED;
+ opts.old_prefix = "a"; opts.new_prefix = "b";
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, head, NULL, &opts));
+ check_diff_patches(diff, expected);
+ git_diff_free(diff);
+
+ git_tree_free(head);
+}
+
+void test_diff_submodules__invalid_cache(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ git_submodule *sm;
+ char *smpath = "sm_changed_head";
+ git_repository *smrepo;
+ git_index *smindex;
+ static const char *expected_baseline[] = {
+ "diff --git a/sm_changed_head b/sm_changed_head\nindex 4800958..3d9386c 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n", /* sm_changed_head */
+ "<END>"
+ };
+ static const char *expected_unchanged[] = { "<END>" };
+ static const char *expected_dirty[] = {
+ "diff --git a/sm_changed_head b/sm_changed_head\nindex 3d9386c..3d9386c 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n+Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247-dirty\n",
+ "<END>"
+ };
+ static const char *expected_moved[] = {
+ "diff --git a/sm_changed_head b/sm_changed_head\nindex 3d9386c..7002348 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n+Subproject commit 700234833f6ccc20d744b238612646be071acaae\n",
+ "<END>"
+ };
+ static const char *expected_moved_dirty[] = {
+ "diff --git a/sm_changed_head b/sm_changed_head\nindex 3d9386c..7002348 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n+Subproject commit 700234833f6ccc20d744b238612646be071acaae-dirty\n",
+ "<END>"
+ };
+
+ g_repo = setup_fixture_submod2();
+
+ opts.flags = GIT_DIFF_INCLUDE_UNTRACKED;
+ opts.old_prefix = "a"; opts.new_prefix = "b";
+ opts.pathspec.count = 1;
+ opts.pathspec.strings = &smpath;
+
+ /* baseline */
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_baseline);
+ git_diff_free(diff);
+
+ /* update index with new HEAD */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, smpath));
+ cl_git_pass(git_submodule_add_to_index(sm, 1));
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_unchanged);
+ git_diff_free(diff);
+
+ /* create untracked file in submodule working directory */
+ cl_git_mkfile("submod2/sm_changed_head/new_around_here", "hello");
+ git_submodule_set_ignore(g_repo, git_submodule_name(sm), GIT_SUBMODULE_IGNORE_NONE);
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_dirty);
+ git_diff_free(diff);
+
+ git_submodule_set_ignore(g_repo, git_submodule_name(sm), GIT_SUBMODULE_IGNORE_UNTRACKED);
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_unchanged);
+ git_diff_free(diff);
+
+ /* modify tracked file in submodule working directory */
+ cl_git_append2file(
+ "submod2/sm_changed_head/file_to_modify", "\nmore stuff\n");
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_dirty);
+ git_diff_free(diff);
+
+ git_submodule_free(sm);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, smpath));
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_dirty);
+ git_diff_free(diff);
+
+ git_submodule_set_ignore(g_repo, git_submodule_name(sm), GIT_SUBMODULE_IGNORE_DIRTY);
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_unchanged);
+ git_diff_free(diff);
+
+ /* add file to index in submodule */
+ cl_git_pass(git_submodule_open(&smrepo, sm));
+ cl_git_pass(git_repository_index(&smindex, smrepo));
+ cl_git_pass(git_index_add_bypath(smindex, "file_to_modify"));
+
+ git_submodule_set_ignore(g_repo, git_submodule_name(sm), GIT_SUBMODULE_IGNORE_UNTRACKED);
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_dirty);
+ git_diff_free(diff);
+
+ git_submodule_set_ignore(g_repo, git_submodule_name(sm), GIT_SUBMODULE_IGNORE_DIRTY);
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_unchanged);
+ git_diff_free(diff);
+
+ /* commit changed index of submodule */
+ cl_repo_commit_from_index(NULL, smrepo, NULL, 1372350000, "Move it");
+
+ git_submodule_set_ignore(g_repo, git_submodule_name(sm), GIT_SUBMODULE_IGNORE_DIRTY);
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_moved);
+ git_diff_free(diff);
+
+ git_submodule_set_ignore(g_repo, git_submodule_name(sm), GIT_SUBMODULE_IGNORE_ALL);
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_unchanged);
+ git_diff_free(diff);
+
+ git_submodule_set_ignore(g_repo, git_submodule_name(sm), GIT_SUBMODULE_IGNORE_NONE);
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_moved_dirty);
+ git_diff_free(diff);
+
+ p_unlink("submod2/sm_changed_head/new_around_here");
+
+ git_submodule_free(sm);
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_moved);
+ git_diff_free(diff);
+
+ git_index_free(smindex);
+ git_repository_free(smrepo);
+}
+
+void test_diff_submodules__diff_ignore_options(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ git_config *cfg;
+ static const char *expected_normal[] = {
+ "<SKIP>", /* .gitmodules */
+ "<UNTRACKED>", /* not-submodule */
+ "<UNTRACKED>", /* not */
+ "diff --git a/sm_changed_file b/sm_changed_file\nindex 4800958..4800958 160000\n--- a/sm_changed_file\n+++ b/sm_changed_file\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_file */
+ "diff --git a/sm_changed_head b/sm_changed_head\nindex 4800958..3d9386c 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n", /* sm_changed_head */
+ "diff --git a/sm_changed_index b/sm_changed_index\nindex 4800958..4800958 160000\n--- a/sm_changed_index\n+++ b/sm_changed_index\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_index */
+ "diff --git a/sm_changed_untracked_file b/sm_changed_untracked_file\nindex 4800958..4800958 160000\n--- a/sm_changed_untracked_file\n+++ b/sm_changed_untracked_file\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0-dirty\n", /* sm_changed_untracked_file */
+ "diff --git a/sm_missing_commits b/sm_missing_commits\nindex 4800958..5e49635 160000\n--- a/sm_missing_commits\n+++ b/sm_missing_commits\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 5e4963595a9774b90524d35a807169049de8ccad\n", /* sm_missing_commits */
+ "<END>"
+ };
+ static const char *expected_ignore_all[] = {
+ "<SKIP>", /* .gitmodules */
+ "<UNTRACKED>", /* not-submodule */
+ "<UNTRACKED>", /* not */
+ "<END>"
+ };
+ static const char *expected_ignore_dirty[] = {
+ "<SKIP>", /* .gitmodules */
+ "<UNTRACKED>", /* not-submodule */
+ "<UNTRACKED>", /* not */
+ "diff --git a/sm_changed_head b/sm_changed_head\nindex 4800958..3d9386c 160000\n--- a/sm_changed_head\n+++ b/sm_changed_head\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 3d9386c507f6b093471a3e324085657a3c2b4247\n", /* sm_changed_head */
+ "diff --git a/sm_missing_commits b/sm_missing_commits\nindex 4800958..5e49635 160000\n--- a/sm_missing_commits\n+++ b/sm_missing_commits\n@@ -1 +1 @@\n-Subproject commit 480095882d281ed676fe5b863569520e54a7d5c0\n+Subproject commit 5e4963595a9774b90524d35a807169049de8ccad\n", /* sm_missing_commits */
+ "<END>"
+ };
+
+ g_repo = setup_fixture_submod2();
+
+ opts.flags = GIT_DIFF_INCLUDE_UNTRACKED;
+ opts.old_prefix = "a"; opts.new_prefix = "b";
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_normal);
+ git_diff_free(diff);
+
+ opts.flags |= GIT_DIFF_IGNORE_SUBMODULES;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_ignore_all);
+ git_diff_free(diff);
+
+ opts.flags &= ~GIT_DIFF_IGNORE_SUBMODULES;
+ opts.ignore_submodules = GIT_SUBMODULE_IGNORE_ALL;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_ignore_all);
+ git_diff_free(diff);
+
+ opts.ignore_submodules = GIT_SUBMODULE_IGNORE_DIRTY;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_ignore_dirty);
+ git_diff_free(diff);
+
+ opts.ignore_submodules = 0;
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_git_pass(git_config_set_bool(cfg, "diff.ignoreSubmodules", false));
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_normal);
+ git_diff_free(diff);
+
+ cl_git_pass(git_config_set_bool(cfg, "diff.ignoreSubmodules", true));
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_ignore_all);
+ git_diff_free(diff);
+
+ cl_git_pass(git_config_set_string(cfg, "diff.ignoreSubmodules", "none"));
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_normal);
+ git_diff_free(diff);
+
+ cl_git_pass(git_config_set_string(cfg, "diff.ignoreSubmodules", "dirty"));
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ check_diff_patches(diff, expected_ignore_dirty);
+ git_diff_free(diff);
+
+ git_config_free(cfg);
+}
+
+void test_diff_submodules__skips_empty_includes_used(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ diff_expects exp;
+
+ /* A side effect of of Git's handling of untracked directories and
+ * auto-ignoring of ".git" entries is that a newly initialized Git
+ * repo inside another repo will be skipped by diff, but one that
+ * actually has a commit it in will show as an untracked directory.
+ * Let's make sure that works.
+ */
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(0, exp.files);
+ git_diff_free(diff);
+
+ {
+ git_repository *r2;
+ cl_git_pass(git_repository_init(&r2, "empty_standard_repo/subrepo", 0));
+ git_repository_free(r2);
+ }
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
+ git_diff_free(diff);
+
+ cl_git_mkfile("empty_standard_repo/subrepo/README.txt", "hello\n");
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
+ git_diff_free(diff);
+}
+
+static void ensure_submodules_found(
+ git_repository *repo,
+ const char **paths,
+ size_t cnt)
+{
+ git_diff *diff = NULL;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ const git_diff_delta *delta;
+ size_t i, pathlen;
+
+ opts.pathspec.strings = (char **)paths;
+ opts.pathspec.count = cnt;
+
+ git_diff_index_to_workdir(&diff, repo, NULL, &opts);
+
+ cl_assert_equal_i(cnt, git_diff_num_deltas(diff));
+
+ for (i = 0; i < cnt; i++) {
+ delta = git_diff_get_delta(diff, i);
+
+ /* ensure that the given path is returned w/o trailing slashes. */
+ pathlen = strlen(opts.pathspec.strings[i]);
+
+ while (pathlen && opts.pathspec.strings[i][pathlen - 1] == '/')
+ pathlen--;
+
+ cl_assert_equal_strn(opts.pathspec.strings[i], delta->new_file.path, pathlen);
+ }
+
+ git_diff_free(diff);
+}
+
+void test_diff_submodules__can_be_identified_by_trailing_slash_in_pathspec(void)
+{
+ const char *one_path_without_slash[] = { "sm_changed_head" };
+ const char *one_path_with_slash[] = { "sm_changed_head/" };
+ const char *many_paths_without_slashes[] = { "sm_changed_head", "sm_changed_index" };
+ const char *many_paths_with_slashes[] = { "sm_changed_head/", "sm_changed_index/" };
+
+ g_repo = setup_fixture_submod2();
+
+ ensure_submodules_found(g_repo, one_path_without_slash, ARRAY_SIZE(one_path_without_slash));
+ ensure_submodules_found(g_repo, one_path_with_slash, ARRAY_SIZE(one_path_with_slash));
+ ensure_submodules_found(g_repo, many_paths_without_slashes, ARRAY_SIZE(many_paths_without_slashes));
+ ensure_submodules_found(g_repo, many_paths_with_slashes, ARRAY_SIZE(many_paths_with_slashes));
+}
diff --git a/tests/libgit2/diff/tree.c b/tests/libgit2/diff/tree.c
new file mode 100644
index 0000000..e03ee7b
--- /dev/null
+++ b/tests/libgit2/diff/tree.c
@@ -0,0 +1,575 @@
+#include "clar_libgit2.h"
+#include "diff_helpers.h"
+
+static git_repository *g_repo = NULL;
+static git_diff_options opts;
+static git_diff *diff;
+static git_tree *a, *b;
+static diff_expects expect;
+
+void test_diff_tree__initialize(void)
+{
+ cl_git_pass(git_diff_options_init(&opts, GIT_DIFF_OPTIONS_VERSION));
+
+ memset(&expect, 0, sizeof(expect));
+
+ diff = NULL;
+ a = NULL;
+ b = NULL;
+}
+
+void test_diff_tree__cleanup(void)
+{
+ git_diff_free(diff);
+ git_tree_free(a);
+ git_tree_free(b);
+
+ cl_git_sandbox_cleanup();
+
+}
+
+void test_diff_tree__0(void)
+{
+ /* grabbed a couple of commit oids from the history of the attr repo */
+ const char *a_commit = "605812a";
+ const char *b_commit = "370fe9ec22";
+ const char *c_commit = "f5b0af1fb4f5c";
+ git_tree *c;
+
+ g_repo = cl_git_sandbox_init("attr");
+
+ cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
+ cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
+ cl_assert((c = resolve_commit_oid_to_tree(g_repo, c_commit)) != NULL);
+
+ opts.context_lines = 1;
+ opts.interhunk_lines = 1;
+
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
+
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expect));
+
+ cl_assert_equal_i(5, expect.files);
+ cl_assert_equal_i(2, expect.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, expect.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(2, expect.file_status[GIT_DELTA_MODIFIED]);
+
+ cl_assert_equal_i(5, expect.hunks);
+
+ cl_assert_equal_i(7 + 24 + 1 + 6 + 6, expect.lines);
+ cl_assert_equal_i(1, expect.line_ctxt);
+ cl_assert_equal_i(24 + 1 + 5 + 5, expect.line_adds);
+ cl_assert_equal_i(7 + 1, expect.line_dels);
+
+ git_diff_free(diff);
+ diff = NULL;
+
+ memset(&expect, 0, sizeof(expect));
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, c, b, &opts));
+
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expect));
+
+ cl_assert_equal_i(2, expect.files);
+ cl_assert_equal_i(0, expect.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, expect.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(2, expect.file_status[GIT_DELTA_MODIFIED]);
+
+ cl_assert_equal_i(2, expect.hunks);
+
+ cl_assert_equal_i(8 + 15, expect.lines);
+ cl_assert_equal_i(1, expect.line_ctxt);
+ cl_assert_equal_i(1, expect.line_adds);
+ cl_assert_equal_i(7 + 14, expect.line_dels);
+
+ git_tree_free(c);
+}
+
+#define DIFF_OPTS(FLAGS, CTXT) \
+ {GIT_DIFF_OPTIONS_VERSION, (FLAGS), GIT_SUBMODULE_IGNORE_UNSPECIFIED, \
+ {NULL,0}, NULL, NULL, NULL, (CTXT), 1}
+
+void test_diff_tree__options(void)
+{
+ /* grabbed a couple of commit oids from the history of the attr repo */
+ const char *a_commit = "6bab5c79cd5140d0";
+ const char *b_commit = "605812ab7fe421fdd";
+ const char *c_commit = "f5b0af1fb4f5";
+ const char *d_commit = "a97cc019851";
+ git_tree *c, *d;
+ diff_expects actual;
+ int test_ab_or_cd[] = { 0, 0, 0, 0, 1, 1, 1, 1, 1 };
+ git_diff_options test_options[] = {
+ /* a vs b tests */
+ DIFF_OPTS(GIT_DIFF_NORMAL, 1),
+ DIFF_OPTS(GIT_DIFF_NORMAL, 3),
+ DIFF_OPTS(GIT_DIFF_REVERSE, 2),
+ DIFF_OPTS(GIT_DIFF_FORCE_TEXT, 2),
+ /* c vs d tests */
+ DIFF_OPTS(GIT_DIFF_NORMAL, 3),
+ DIFF_OPTS(GIT_DIFF_IGNORE_WHITESPACE, 3),
+ DIFF_OPTS(GIT_DIFF_IGNORE_WHITESPACE_CHANGE, 3),
+ DIFF_OPTS(GIT_DIFF_IGNORE_WHITESPACE_EOL, 3),
+ DIFF_OPTS(GIT_DIFF_IGNORE_WHITESPACE | GIT_DIFF_REVERSE, 1),
+ };
+
+ /* to generate these values:
+ * - cd to tests/resources/attr,
+ * - mv .gitted .git
+ * - git diff [options] 6bab5c79cd5140d0 605812ab7fe421fdd
+ * - mv .git .gitted
+ */
+#define EXPECT_STATUS_ADM(ADDS,DELS,MODS) { 0, ADDS, DELS, MODS, 0, 0, 0, 0, 0 }
+
+ diff_expects test_expects[] = {
+ /* a vs b tests */
+ { 5, 0, EXPECT_STATUS_ADM(3, 0, 2), 4, 0, 0, 51, 2, 46, 3 },
+ { 5, 0, EXPECT_STATUS_ADM(3, 0, 2), 4, 0, 0, 53, 4, 46, 3 },
+ { 5, 0, EXPECT_STATUS_ADM(0, 3, 2), 4, 0, 0, 52, 3, 3, 46 },
+ { 5, 0, EXPECT_STATUS_ADM(3, 0, 2), 5, 0, 0, 54, 3, 47, 4 },
+ /* c vs d tests */
+ { 1, 0, EXPECT_STATUS_ADM(0, 0, 1), 1, 0, 0, 22, 9, 10, 3 },
+ { 1, 0, EXPECT_STATUS_ADM(0, 0, 1), 1, 0, 0, 19, 12, 7, 0 },
+ { 1, 0, EXPECT_STATUS_ADM(0, 0, 1), 1, 0, 0, 20, 11, 8, 1 },
+ { 1, 0, EXPECT_STATUS_ADM(0, 0, 1), 1, 0, 0, 20, 11, 8, 1 },
+ { 1, 0, EXPECT_STATUS_ADM(0, 0, 1), 1, 0, 0, 18, 11, 0, 7 },
+ { 0 },
+ };
+ diff_expects *expected;
+ int i, j;
+
+ g_repo = cl_git_sandbox_init("attr");
+
+ cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
+ cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
+ cl_assert((c = resolve_commit_oid_to_tree(g_repo, c_commit)) != NULL);
+ cl_assert((d = resolve_commit_oid_to_tree(g_repo, d_commit)) != NULL);
+
+ for (i = 0; test_expects[i].files > 0; i++) {
+ memset(&actual, 0, sizeof(actual)); /* clear accumulator */
+ opts = test_options[i];
+
+ if (test_ab_or_cd[i] == 0)
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
+ else
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, c, d, &opts));
+
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &actual));
+
+ expected = &test_expects[i];
+ cl_assert_equal_i(actual.files, expected->files);
+ for (j = GIT_DELTA_UNMODIFIED; j <= GIT_DELTA_TYPECHANGE; ++j)
+ cl_assert_equal_i(expected->file_status[j], actual.file_status[j]);
+ cl_assert_equal_i(actual.hunks, expected->hunks);
+ cl_assert_equal_i(actual.lines, expected->lines);
+ cl_assert_equal_i(actual.line_ctxt, expected->line_ctxt);
+ cl_assert_equal_i(actual.line_adds, expected->line_adds);
+ cl_assert_equal_i(actual.line_dels, expected->line_dels);
+
+ git_diff_free(diff);
+ diff = NULL;
+ }
+
+ git_tree_free(c);
+ git_tree_free(d);
+}
+
+void test_diff_tree__bare(void)
+{
+ const char *a_commit = "8496071c1b46c85";
+ const char *b_commit = "be3563ae3f79";
+
+ g_repo = cl_git_sandbox_init("testrepo.git");
+
+ cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
+ cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
+
+ opts.context_lines = 1;
+ opts.interhunk_lines = 1;
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
+
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expect));
+
+ cl_assert_equal_i(3, expect.files);
+ cl_assert_equal_i(2, expect.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, expect.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, expect.file_status[GIT_DELTA_MODIFIED]);
+
+ cl_assert_equal_i(3, expect.hunks);
+
+ cl_assert_equal_i(4, expect.lines);
+ cl_assert_equal_i(0, expect.line_ctxt);
+ cl_assert_equal_i(3, expect.line_adds);
+ cl_assert_equal_i(1, expect.line_dels);
+}
+
+void test_diff_tree__merge(void)
+{
+ /* grabbed a couple of commit oids from the history of the attr repo */
+ const char *a_commit = "605812a";
+ const char *b_commit = "370fe9ec22";
+ const char *c_commit = "f5b0af1fb4f5c";
+ git_tree *c;
+ git_diff *diff1 = NULL, *diff2 = NULL;
+
+ g_repo = cl_git_sandbox_init("attr");
+
+ cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
+ cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
+ cl_assert((c = resolve_commit_oid_to_tree(g_repo, c_commit)) != NULL);
+
+ cl_git_pass(git_diff_tree_to_tree(&diff1, g_repo, a, b, NULL));
+
+ cl_git_pass(git_diff_tree_to_tree(&diff2, g_repo, c, b, NULL));
+
+ git_tree_free(c);
+
+ cl_git_pass(git_diff_merge(diff1, diff2));
+
+ git_diff_free(diff2);
+
+ cl_git_pass(git_diff_foreach(
+ diff1, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expect));
+
+ cl_assert_equal_i(6, expect.files);
+ cl_assert_equal_i(2, expect.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, expect.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(3, expect.file_status[GIT_DELTA_MODIFIED]);
+
+ cl_assert_equal_i(6, expect.hunks);
+
+ cl_assert_equal_i(59, expect.lines);
+ cl_assert_equal_i(1, expect.line_ctxt);
+ cl_assert_equal_i(36, expect.line_adds);
+ cl_assert_equal_i(22, expect.line_dels);
+
+ git_diff_free(diff1);
+}
+
+void test_diff_tree__larger_hunks(void)
+{
+ const char *a_commit = "d70d245ed97ed2aa596dd1af6536e4bfdb047b69";
+ const char *b_commit = "7a9e0b02e63179929fed24f0a3e0f19168114d10";
+ size_t d, num_d, h, num_h, l, num_l;
+ git_patch *patch;
+ const git_diff_hunk *hunk;
+ const git_diff_line *line;
+
+ g_repo = cl_git_sandbox_init("diff");
+
+ cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
+ cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
+
+ opts.context_lines = 1;
+ opts.interhunk_lines = 0;
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
+
+ num_d = git_diff_num_deltas(diff);
+ for (d = 0; d < num_d; ++d) {
+ cl_git_pass(git_patch_from_diff(&patch, diff, d));
+ cl_assert(patch);
+
+ num_h = git_patch_num_hunks(patch);
+ for (h = 0; h < num_h; h++) {
+ cl_git_pass(git_patch_get_hunk(&hunk, &num_l, patch, h));
+
+ for (l = 0; l < num_l; ++l) {
+ cl_git_pass(git_patch_get_line_in_hunk(&line, patch, h, l));
+ cl_assert(line);
+ }
+
+ cl_git_fail(git_patch_get_line_in_hunk(&line, patch, h, num_l));
+ }
+
+ cl_git_fail(git_patch_get_hunk(&hunk, &num_l, patch, num_h));
+
+ git_patch_free(patch);
+ }
+
+ cl_git_fail(git_patch_from_diff(&patch, diff, num_d));
+
+ cl_assert_equal_i(2, (int)num_d);
+}
+
+void test_diff_tree__checks_options_version(void)
+{
+ const char *a_commit = "8496071c1b46c85";
+ const char *b_commit = "be3563ae3f79";
+ const git_error *err;
+
+ g_repo = cl_git_sandbox_init("testrepo.git");
+
+ cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
+ cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
+
+ opts.version = 0;
+ cl_git_fail(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
+ err = git_error_last();
+ cl_assert_equal_i(GIT_ERROR_INVALID, err->klass);
+
+ git_error_clear();
+ opts.version = 1024;
+ cl_git_fail(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
+ err = git_error_last();
+}
+
+static void process_tree_to_tree_diffing(
+ const char *old_commit,
+ const char *new_commit)
+{
+ g_repo = cl_git_sandbox_init("unsymlinked.git");
+
+ cl_assert((a = resolve_commit_oid_to_tree(g_repo, old_commit)) != NULL);
+ cl_assert((b = resolve_commit_oid_to_tree(g_repo, new_commit)) != NULL);
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
+
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, NULL, NULL, NULL, &expect));
+}
+
+void test_diff_tree__symlink_blob_mode_changed_to_regular_file(void)
+{
+ /*
+ * $ git diff 7fccd7..806999
+ * diff --git a/include/Nu/Nu.h b/include/Nu/Nu.h
+ * deleted file mode 120000
+ * index 19bf568..0000000
+ * --- a/include/Nu/Nu.h
+ * +++ /dev/null
+ * @@ -1 +0,0 @@
+ * -../../objc/Nu.h
+ * \ No newline at end of file
+ * diff --git a/include/Nu/Nu.h b/include/Nu/Nu.h
+ * new file mode 100644
+ * index 0000000..f9e6561
+ * --- /dev/null
+ * +++ b/include/Nu/Nu.h
+ * @@ -0,0 +1 @@
+ * +awesome content
+ * diff --git a/objc/Nu.h b/objc/Nu.h
+ * deleted file mode 100644
+ * index f9e6561..0000000
+ * --- a/objc/Nu.h
+ * +++ /dev/null
+ * @@ -1 +0,0 @@
+ * -awesome content
+ */
+
+ process_tree_to_tree_diffing("7fccd7", "806999");
+
+ cl_assert_equal_i(3, expect.files);
+ cl_assert_equal_i(2, expect.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(0, expect.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, expect.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, expect.file_status[GIT_DELTA_TYPECHANGE]);
+}
+
+void test_diff_tree__symlink_blob_mode_changed_to_regular_file_as_typechange(void)
+{
+ /*
+ * $ git diff 7fccd7..a8595c
+ * diff --git a/include/Nu/Nu.h b/include/Nu/Nu.h
+ * deleted file mode 120000
+ * index 19bf568..0000000
+ * --- a/include/Nu/Nu.h
+ * +++ /dev/null
+ * @@ -1 +0,0 @@
+ * -../../objc/Nu.h
+ * \ No newline at end of file
+ * diff --git a/include/Nu/Nu.h b/include/Nu/Nu.h
+ * new file mode 100755
+ * index 0000000..f9e6561
+ * --- /dev/null
+ * +++ b/include/Nu/Nu.h
+ * @@ -0,0 +1 @@
+ * +awesome content
+ * diff --git a/objc/Nu.h b/objc/Nu.h
+ * deleted file mode 100644
+ * index f9e6561..0000000
+ * --- a/objc/Nu.h
+ * +++ /dev/null
+ * @@ -1 +0,0 @@
+ * -awesome content
+ */
+
+ opts.flags = GIT_DIFF_INCLUDE_TYPECHANGE;
+ process_tree_to_tree_diffing("7fccd7", "a8595c");
+
+ cl_assert_equal_i(2, expect.files);
+ cl_assert_equal_i(1, expect.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(0, expect.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, expect.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, expect.file_status[GIT_DELTA_TYPECHANGE]);
+}
+
+void test_diff_tree__regular_blob_mode_changed_to_executable_file(void)
+{
+ /*
+ * $ git diff 806999..a8595c
+ * diff --git a/include/Nu/Nu.h b/include/Nu/Nu.h
+ * old mode 100644
+ * new mode 100755
+ */
+
+ process_tree_to_tree_diffing("806999", "a8595c");
+
+ cl_assert_equal_i(1, expect.files);
+ cl_assert_equal_i(0, expect.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, expect.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, expect.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, expect.file_status[GIT_DELTA_TYPECHANGE]);
+}
+
+void test_diff_tree__issue_1397(void)
+{
+ /* this test shows that it is not needed */
+
+ g_repo = cl_git_sandbox_init("issue_1397");
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+ cl_assert((a = resolve_commit_oid_to_tree(g_repo, "8a7ef04")) != NULL);
+ cl_assert((b = resolve_commit_oid_to_tree(g_repo, "7f483a7")) != NULL);
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, &opts));
+
+ cl_git_pass(git_diff_foreach(diff,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expect));
+
+ cl_assert_equal_i(1, expect.files);
+ cl_assert_equal_i(0, expect.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, expect.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, expect.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, expect.file_status[GIT_DELTA_TYPECHANGE]);
+}
+
+static void set_config_int(git_repository *repo, const char *name, int value)
+{
+ git_config *cfg;
+
+ cl_git_pass(git_repository_config(&cfg, repo));
+ cl_git_pass(git_config_set_int32(cfg, name, value));
+ git_config_free(cfg);
+}
+
+void test_diff_tree__diff_configs(void)
+{
+ const char *a_commit = "d70d245e";
+ const char *b_commit = "7a9e0b02";
+
+ g_repo = cl_git_sandbox_init("diff");
+
+ cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
+ cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, NULL));
+
+ cl_git_pass(git_diff_foreach(diff,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expect));
+
+ cl_assert_equal_i(2, expect.files);
+ cl_assert_equal_i(2, expect.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(6, expect.hunks);
+ cl_assert_equal_i(55, expect.lines);
+ cl_assert_equal_i(33, expect.line_ctxt);
+ cl_assert_equal_i(7, expect.line_adds);
+ cl_assert_equal_i(15, expect.line_dels);
+
+ git_diff_free(diff);
+ diff = NULL;
+
+ set_config_int(g_repo, "diff.context", 1);
+
+ memset(&expect, 0, sizeof(expect));
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, NULL));
+
+ cl_git_pass(git_diff_foreach(diff,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expect));
+
+ cl_assert_equal_i(2, expect.files);
+ cl_assert_equal_i(2, expect.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(7, expect.hunks);
+ cl_assert_equal_i(34, expect.lines);
+ cl_assert_equal_i(12, expect.line_ctxt);
+ cl_assert_equal_i(7, expect.line_adds);
+ cl_assert_equal_i(15, expect.line_dels);
+
+ git_diff_free(diff);
+ diff = NULL;
+
+ set_config_int(g_repo, "diff.context", 0);
+ set_config_int(g_repo, "diff.noprefix", 1);
+
+ memset(&expect, 0, sizeof(expect));
+
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, NULL));
+
+ cl_git_pass(git_diff_foreach(diff,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expect));
+
+ cl_assert_equal_i(2, expect.files);
+ cl_assert_equal_i(2, expect.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(7, expect.hunks);
+ cl_assert_equal_i(22, expect.lines);
+ cl_assert_equal_i(0, expect.line_ctxt);
+ cl_assert_equal_i(7, expect.line_adds);
+ cl_assert_equal_i(15, expect.line_dels);
+}
+
+void test_diff_tree__diff_tree_with_empty_dir_entry_succeeds(void)
+{
+ const char *content = "This is a blob\n";
+ const git_diff_delta *delta;
+ git_oid empty_tree, invalid_tree, blob;
+ git_buf patch = GIT_BUF_INIT;
+ git_treebuilder *builder;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_pass(git_blob_create_from_buffer(&blob, g_repo, content, strlen(content)));
+ cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL));
+ cl_git_pass(git_treebuilder_write(&empty_tree, builder));
+ cl_git_pass(git_treebuilder_insert(NULL, builder, "empty_tree", &empty_tree, GIT_FILEMODE_TREE));
+ cl_git_pass(git_treebuilder_insert(NULL, builder, "blob", &blob, GIT_FILEMODE_BLOB));
+ cl_git_pass(git_treebuilder_write(&invalid_tree, builder));
+
+ cl_git_pass(git_tree_lookup(&a, g_repo, &empty_tree));
+ cl_git_pass(git_tree_lookup(&b, g_repo, &invalid_tree));
+ cl_git_pass(git_diff_tree_to_tree(&diff, g_repo, a, b, NULL));
+
+ cl_git_pass(git_diff_foreach(diff,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &expect));
+ cl_assert_equal_i(1, expect.files);
+ cl_assert_equal_i(0, expect.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, expect.hunks);
+ cl_assert_equal_i(1, expect.lines);
+ cl_assert_equal_i(0, expect.line_ctxt);
+ cl_assert_equal_i(1, expect.line_adds);
+ cl_assert_equal_i(0, expect.line_dels);
+
+ cl_git_pass(git_diff_to_buf(&patch, diff, GIT_DIFF_FORMAT_PATCH));
+ cl_assert_equal_s(patch.ptr,
+ "diff --git a/blob b/blob\n"
+ "new file mode 100644\n"
+ "index 0000000..bbf2e80\n"
+ "--- /dev/null\n"
+ "+++ b/blob\n"
+ "@@ -0,0 +1 @@\n"
+ "+This is a blob\n");
+
+ cl_assert_equal_i(git_diff_num_deltas(diff), 1);
+ delta = git_diff_get_delta(diff, 0);
+ cl_assert_equal_s(delta->new_file.path, "blob");
+
+ git_treebuilder_free(builder);
+ git_buf_dispose(&patch);
+}
diff --git a/tests/libgit2/diff/userdiff.c b/tests/libgit2/diff/userdiff.c
new file mode 100644
index 0000000..2304940
--- /dev/null
+++ b/tests/libgit2/diff/userdiff.c
@@ -0,0 +1,25 @@
+#include "clar_libgit2.h"
+
+#include "userdiff.h"
+
+static git_regexp regex;
+
+void test_diff_userdiff__cleanup(void)
+{
+ git_regexp_dispose(&regex);
+}
+
+void test_diff_userdiff__compile_userdiff_regexps(void)
+{
+ size_t idx;
+
+ for (idx = 0; idx < ARRAY_SIZE(builtin_defs); ++idx) {
+ git_diff_driver_definition ddef = builtin_defs[idx];
+
+ cl_git_pass(git_regexp_compile(&regex, ddef.fns, ddef.flags));
+ git_regexp_dispose(&regex);
+
+ cl_git_pass(git_regexp_compile(&regex, ddef.words, 0));
+ git_regexp_dispose(&regex);
+ }
+}
diff --git a/tests/libgit2/diff/workdir.c b/tests/libgit2/diff/workdir.c
new file mode 100644
index 0000000..c433b75
--- /dev/null
+++ b/tests/libgit2/diff/workdir.c
@@ -0,0 +1,2327 @@
+#include "clar_libgit2.h"
+#include "diff_helpers.h"
+#include "repository.h"
+#include "index.h"
+#include "git2/sys/diff.h"
+#include "../checkout/checkout_helpers.h"
+
+static git_repository *g_repo = NULL;
+
+void test_diff_workdir__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_diff_workdir__to_index(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ diff_expects exp;
+ int use_iterator;
+
+ g_repo = cl_git_sandbox_init("status");
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ /* to generate these values:
+ * - cd to tests/resources/status,
+ * - mv .gitted .git
+ * - git diff --name-status
+ * - git diff
+ * - mv .git .gitted
+ */
+ cl_assert_equal_i(13, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ cl_assert_equal_i(8, exp.hunks);
+
+ cl_assert_equal_i(14, exp.lines);
+ cl_assert_equal_i(5, exp.line_ctxt);
+ cl_assert_equal_i(4, exp.line_adds);
+ cl_assert_equal_i(5, exp.line_dels);
+ }
+
+ {
+ git_diff_perfdata perf = GIT_DIFF_PERFDATA_INIT;
+ cl_git_pass(git_diff_get_perfdata(&perf, diff));
+ cl_assert_equal_sz(
+ 13 /* in root */ + 3 /* in subdir */, perf.stat_calls);
+ cl_assert_equal_sz(5, perf.oid_calculations);
+ }
+
+ git_diff_free(diff);
+}
+
+void test_diff_workdir__to_index_with_conflicts(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ git_index *index;
+ git_index_entry our_entry = {{0}}, their_entry = {{0}};
+ diff_expects exp = {0};
+
+ g_repo = cl_git_sandbox_init("status");
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+
+ /* Adding an entry that represents a rename gets two files in conflict */
+ our_entry.path = "subdir/modified_file";
+ our_entry.mode = 0100644;
+ git_oid__fromstr(&our_entry.id, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", GIT_OID_SHA1);
+
+ their_entry.path = "subdir/rename_conflict";
+ their_entry.mode = 0100644;
+ git_oid__fromstr(&their_entry.id, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", GIT_OID_SHA1);
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_conflict_add(index, NULL, &our_entry, &their_entry));
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, &opts));
+
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(9, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_CONFLICTED]);
+
+ cl_assert_equal_i(7, exp.hunks);
+
+ cl_assert_equal_i(12, exp.lines);
+ cl_assert_equal_i(4, exp.line_ctxt);
+ cl_assert_equal_i(3, exp.line_adds);
+ cl_assert_equal_i(5, exp.line_dels);
+
+ git_diff_free(diff);
+ git_index_free(index);
+}
+
+void test_diff_workdir__to_index_with_assume_unchanged(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ git_index *idx = NULL;
+ diff_expects exp;
+ const git_index_entry *iep;
+ git_index_entry ie;
+
+ g_repo = cl_git_sandbox_init("status");
+
+ /* do initial diff */
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(8, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
+ git_diff_free(diff);
+
+ /* mark a couple of entries with ASSUME_UNCHANGED */
+
+ cl_git_pass(git_repository_index(&idx, g_repo));
+
+ cl_assert((iep = git_index_get_bypath(idx, "modified_file", 0)) != NULL);
+ memcpy(&ie, iep, sizeof(ie));
+ ie.flags |= GIT_INDEX_ENTRY_VALID;
+ cl_git_pass(git_index_add(idx, &ie));
+
+ cl_assert((iep = git_index_get_bypath(idx, "file_deleted", 0)) != NULL);
+ memcpy(&ie, iep, sizeof(ie));
+ ie.flags |= GIT_INDEX_ENTRY_VALID;
+ cl_git_pass(git_index_add(idx, &ie));
+
+ cl_git_pass(git_index_write(idx));
+ git_index_free(idx);
+
+ /* redo diff and see that entries are skipped */
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(6, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]);
+ git_diff_free(diff);
+
+}
+
+void test_diff_workdir__to_tree(void)
+{
+ /* grabbed a couple of commit oids from the history of the attr repo */
+ const char *a_commit = "26a125ee1bf"; /* the current HEAD */
+ const char *b_commit = "0017bd4ab1ec3"; /* the start */
+ git_tree *a, *b;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ git_diff *diff2 = NULL;
+ diff_expects exp;
+ int use_iterator;
+
+ g_repo = cl_git_sandbox_init("status");
+
+ a = resolve_commit_oid_to_tree(g_repo, a_commit);
+ b = resolve_commit_oid_to_tree(g_repo, b_commit);
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+
+ /* You can't really generate the equivalent of git_diff_tree_to_workdir()
+ * using C git. It really wants to interpose the index into the diff.
+ *
+ * To validate the following results with command line git, I ran the
+ * following:
+ * - git ls-tree 26a125
+ * - find . ! -path ./.git/\* -a -type f | git hash-object --stdin-paths
+ * The results are documented at the bottom of this file in the
+ * long comment entitled "PREPARATION OF TEST DATA".
+ */
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, a, &opts));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(14, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(5, exp.file_status[GIT_DELTA_UNTRACKED]);
+ }
+
+ /* Since there is no git diff equivalent, let's just assume that the
+ * text diffs produced by git_diff_foreach are accurate here. We will
+ * do more apples-to-apples test comparison below.
+ */
+
+ git_diff_free(diff);
+ diff = NULL;
+ memset(&exp, 0, sizeof(exp));
+
+ /* This is a compatible emulation of "git diff <sha>" which looks like
+ * a workdir to tree diff (even though it is not really). This is what
+ * you would get from "git diff --name-status 26a125ee1bf"
+ */
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts));
+ cl_git_pass(git_diff_index_to_workdir(&diff2, g_repo, NULL, &opts));
+ cl_git_pass(git_diff_merge(diff, diff2));
+ git_diff_free(diff2);
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(15, exp.files);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(5, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ cl_assert_equal_i(11, exp.hunks);
+
+ cl_assert_equal_i(17, exp.lines);
+ cl_assert_equal_i(4, exp.line_ctxt);
+ cl_assert_equal_i(8, exp.line_adds);
+ cl_assert_equal_i(5, exp.line_dels);
+ }
+
+ git_diff_free(diff);
+ diff = NULL;
+ memset(&exp, 0, sizeof(exp));
+
+ /* Again, emulating "git diff <sha>" for testing purposes using
+ * "git diff --name-status 0017bd4ab1ec3" instead.
+ */
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, b, NULL, &opts));
+ cl_git_pass(git_diff_index_to_workdir(&diff2, g_repo, NULL, &opts));
+ cl_git_pass(git_diff_merge(diff, diff2));
+ git_diff_free(diff2);
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(16, exp.files);
+ cl_assert_equal_i(5, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ cl_assert_equal_i(12, exp.hunks);
+
+ cl_assert_equal_i(19, exp.lines);
+ cl_assert_equal_i(3, exp.line_ctxt);
+ cl_assert_equal_i(12, exp.line_adds);
+ cl_assert_equal_i(4, exp.line_dels);
+ }
+
+ git_diff_free(diff);
+
+ /* Let's try that once more with a reversed diff */
+
+ opts.flags |= GIT_DIFF_REVERSE;
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, b, NULL, &opts));
+ cl_git_pass(git_diff_index_to_workdir(&diff2, g_repo, NULL, &opts));
+ cl_git_pass(git_diff_merge(diff, diff2));
+ git_diff_free(diff2);
+
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(16, exp.files);
+ cl_assert_equal_i(5, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ cl_assert_equal_i(12, exp.hunks);
+
+ cl_assert_equal_i(19, exp.lines);
+ cl_assert_equal_i(3, exp.line_ctxt);
+ cl_assert_equal_i(12, exp.line_dels);
+ cl_assert_equal_i(4, exp.line_adds);
+
+ git_diff_free(diff);
+
+ /* all done now */
+
+ git_tree_free(a);
+ git_tree_free(b);
+}
+
+void test_diff_workdir__to_index_with_pathspec(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ diff_expects exp;
+ char *pathspec = NULL;
+ int use_iterator;
+
+ g_repo = cl_git_sandbox_init("status");
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+ opts.pathspec.strings = &pathspec;
+ opts.pathspec.count = 1;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, NULL, NULL, NULL, &exp));
+ else
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
+
+ cl_assert_equal_i(13, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNTRACKED]);
+ }
+
+ git_diff_free(diff);
+
+ pathspec = "modified_file";
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, NULL, NULL, NULL, &exp));
+ else
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
+
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_UNTRACKED]);
+ }
+
+ git_diff_free(diff);
+
+ pathspec = "subdir";
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, NULL, NULL, NULL, &exp));
+ else
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
+
+ cl_assert_equal_i(3, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
+ }
+
+ git_diff_free(diff);
+
+ pathspec = "*_deleted";
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, NULL, NULL, NULL, &exp));
+ else
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
+
+ cl_assert_equal_i(2, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_UNTRACKED]);
+ }
+
+ git_diff_free(diff);
+}
+
+void test_diff_workdir__to_index_with_pathlist_disabling_fnmatch(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ diff_expects exp;
+ char *pathspec = NULL;
+ int use_iterator;
+
+ g_repo = cl_git_sandbox_init("status");
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED |
+ GIT_DIFF_DISABLE_PATHSPEC_MATCH;
+ opts.pathspec.strings = &pathspec;
+ opts.pathspec.count = 0;
+
+ /* ensure that an empty pathspec list is ignored */
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, NULL, NULL, NULL, &exp));
+ else
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
+
+ cl_assert_equal_i(13, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNTRACKED]);
+ }
+
+ git_diff_free(diff);
+
+ /* ensure that a single NULL pathspec is filtered out (like when using
+ * fnmatch filtering)
+ */
+
+ opts.pathspec.count = 1;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, NULL, NULL, NULL, &exp));
+ else
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
+
+ cl_assert_equal_i(13, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNTRACKED]);
+ }
+
+ git_diff_free(diff);
+
+ pathspec = "modified_file";
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, NULL, NULL, NULL, &exp));
+ else
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
+
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_UNTRACKED]);
+ }
+
+ git_diff_free(diff);
+
+ /* ensure that subdirs can be specified */
+ pathspec = "subdir";
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, NULL, NULL, NULL, &exp));
+ else
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
+
+ cl_assert_equal_i(3, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
+ }
+
+ git_diff_free(diff);
+
+ /* ensure that subdirs can be specified with a trailing slash */
+ pathspec = "subdir/";
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, NULL, NULL, NULL, &exp));
+ else
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
+
+ cl_assert_equal_i(3, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
+ }
+
+ git_diff_free(diff);
+
+ /* ensure that fnmatching is completely disabled */
+ pathspec = "subdir/*";
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, NULL, NULL, NULL, &exp));
+ else
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
+
+ cl_assert_equal_i(0, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_UNTRACKED]);
+ }
+
+ git_diff_free(diff);
+
+ /* ensure that the prefix matching isn't completely braindead */
+ pathspec = "subdi";
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, NULL, NULL, NULL, &exp));
+ else
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
+
+ cl_assert_equal_i(0, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_UNTRACKED]);
+ }
+
+ git_diff_free(diff);
+
+ /* ensure that fnmatching isn't working at all */
+ pathspec = "*_deleted";
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, NULL, NULL, NULL, &exp));
+ else
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
+
+ cl_assert_equal_i(0, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_UNTRACKED]);
+ }
+
+ git_diff_free(diff);
+}
+
+void test_diff_workdir__filemode_changes(void)
+{
+ git_diff *diff = NULL;
+ diff_expects exp;
+ int use_iterator;
+
+ if (!cl_is_chmod_supported())
+ return;
+
+ g_repo = cl_git_sandbox_init("issue_592");
+
+ cl_repo_set_bool(g_repo, "core.filemode", true);
+
+ /* test once with no mods */
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(0, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, exp.hunks);
+ }
+
+ git_diff_free(diff);
+
+ /* chmod file and test again */
+
+ cl_assert(cl_toggle_filemode("issue_592/a.txt"));
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, exp.hunks);
+ }
+
+ git_diff_free(diff);
+
+ cl_assert(cl_toggle_filemode("issue_592/a.txt"));
+}
+
+void test_diff_workdir__filemode_changes_with_filemode_false(void)
+{
+ git_diff *diff = NULL;
+ diff_expects exp;
+
+ if (!cl_is_chmod_supported())
+ return;
+
+ g_repo = cl_git_sandbox_init("issue_592");
+
+ cl_repo_set_bool(g_repo, "core.filemode", false);
+
+ /* test once with no mods */
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(0, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, exp.hunks);
+
+ git_diff_free(diff);
+
+ /* chmod file and test again */
+
+ cl_assert(cl_toggle_filemode("issue_592/a.txt"));
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(diff,
+ diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(0, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, exp.hunks);
+
+ git_diff_free(diff);
+
+ cl_assert(cl_toggle_filemode("issue_592/a.txt"));
+}
+
+void test_diff_workdir__head_index_and_workdir_all_differ(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff_i2t = NULL, *diff_w2i = NULL;
+ diff_expects exp;
+ char *pathspec = "staged_changes_modified_file";
+ git_tree *tree;
+ int use_iterator;
+
+ /* For this file,
+ * - head->index diff has 1 line of context, 1 line of diff
+ * - index->workdir diff has 2 lines of context, 1 line of diff
+ * but
+ * - head->workdir diff has 1 line of context, 2 lines of diff
+ * Let's make sure the right one is returned from each fn.
+ */
+
+ g_repo = cl_git_sandbox_init("status");
+
+ tree = resolve_commit_oid_to_tree(g_repo, "26a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f");
+
+ opts.pathspec.strings = &pathspec;
+ opts.pathspec.count = 1;
+
+ cl_git_pass(git_diff_tree_to_index(&diff_i2t, g_repo, tree, NULL, &opts));
+ cl_git_pass(git_diff_index_to_workdir(&diff_w2i, g_repo, NULL, &opts));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff_i2t, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff_i2t, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.hunks);
+ cl_assert_equal_i(2, exp.lines);
+ cl_assert_equal_i(1, exp.line_ctxt);
+ cl_assert_equal_i(1, exp.line_adds);
+ cl_assert_equal_i(0, exp.line_dels);
+ }
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff_w2i, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff_w2i, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.hunks);
+ cl_assert_equal_i(3, exp.lines);
+ cl_assert_equal_i(2, exp.line_ctxt);
+ cl_assert_equal_i(1, exp.line_adds);
+ cl_assert_equal_i(0, exp.line_dels);
+ }
+
+ cl_git_pass(git_diff_merge(diff_i2t, diff_w2i));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff_i2t, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff_i2t, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.hunks);
+ cl_assert_equal_i(3, exp.lines);
+ cl_assert_equal_i(1, exp.line_ctxt);
+ cl_assert_equal_i(2, exp.line_adds);
+ cl_assert_equal_i(0, exp.line_dels);
+ }
+
+ git_diff_free(diff_i2t);
+ git_diff_free(diff_w2i);
+
+ git_tree_free(tree);
+}
+
+void test_diff_workdir__eof_newline_changes(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ diff_expects exp;
+ char *pathspec = "current_file";
+ int use_iterator;
+
+ g_repo = cl_git_sandbox_init("status");
+
+ opts.pathspec.strings = &pathspec;
+ opts.pathspec.count = 1;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(0, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, exp.hunks);
+ cl_assert_equal_i(0, exp.lines);
+ cl_assert_equal_i(0, exp.line_ctxt);
+ cl_assert_equal_i(0, exp.line_adds);
+ cl_assert_equal_i(0, exp.line_dels);
+ }
+
+ git_diff_free(diff);
+
+ cl_git_append2file("status/current_file", "\n");
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.hunks);
+ cl_assert_equal_i(2, exp.lines);
+ cl_assert_equal_i(1, exp.line_ctxt);
+ cl_assert_equal_i(1, exp.line_adds);
+ cl_assert_equal_i(0, exp.line_dels);
+ }
+
+ git_diff_free(diff);
+
+ cl_git_rewritefile("status/current_file", "current_file");
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.hunks);
+ cl_assert_equal_i(3, exp.lines);
+ cl_assert_equal_i(0, exp.line_ctxt);
+ cl_assert_equal_i(1, exp.line_adds);
+ cl_assert_equal_i(2, exp.line_dels);
+ }
+
+ git_diff_free(diff);
+}
+
+/* PREPARATION OF TEST DATA
+ *
+ * Since there is no command line equivalent of git_diff_tree_to_workdir,
+ * it was a bit of a pain to confirm that I was getting the expected
+ * results in the first part of this tests. Here is what I ended up
+ * doing to set my expectation for the file counts and results:
+ *
+ * Running "git ls-tree 26a125" and "git ls-tree aa27a6" shows:
+ *
+ * A a0de7e0ac200c489c41c59dfa910154a70264e6e current_file
+ * B 5452d32f1dd538eb0405e8a83cc185f79e25e80f file_deleted
+ * C 452e4244b5d083ddf0460acf1ecc74db9dcfa11a modified_file
+ * D 32504b727382542f9f089e24fddac5e78533e96c staged_changes
+ * E 061d42a44cacde5726057b67558821d95db96f19 staged_changes_file_deleted
+ * F 70bd9443ada07063e7fbf0b3ff5c13f7494d89c2 staged_changes_modified_file
+ * G e9b9107f290627c04d097733a10055af941f6bca staged_delete_file_deleted
+ * H dabc8af9bd6e9f5bbe96a176f1a24baf3d1f8916 staged_delete_modified_file
+ * I 53ace0d1cc1145a5f4fe4f78a186a60263190733 subdir/current_file
+ * J 1888c805345ba265b0ee9449b8877b6064592058 subdir/deleted_file
+ * K a6191982709b746d5650e93c2acf34ef74e11504 subdir/modified_file
+ * L e8ee89e15bbe9b20137715232387b3de5b28972e subdir.txt
+ *
+ * --------
+ *
+ * find . ! -path ./.git/\* -a -type f | git hash-object --stdin-paths
+ *
+ * A a0de7e0ac200c489c41c59dfa910154a70264e6e current_file
+ * M 6a79f808a9c6bc9531ac726c184bbcd9351ccf11 ignored_file
+ * C 0a539630525aca2e7bc84975958f92f10a64c9b6 modified_file
+ * N d4fa8600b4f37d7516bef4816ae2c64dbf029e3a new_file
+ * D 55d316c9ba708999f1918e9677d01dfcae69c6b9 staged_changes
+ * F 011c3440d5c596e21d836aa6d7b10eb581f68c49 staged_changes_modified_file
+ * H dabc8af9bd6e9f5bbe96a176f1a24baf3d1f8916 staged_delete_modified_file
+ * O 529a16e8e762d4acb7b9636ff540a00831f9155a staged_new_file
+ * P 8b090c06d14ffa09c4e880088ebad33893f921d1 staged_new_file_modified_file
+ * I 53ace0d1cc1145a5f4fe4f78a186a60263190733 subdir/current_file
+ * K 57274b75eeb5f36fd55527806d567b2240a20c57 subdir/modified_file
+ * Q 80a86a6931b91bc01c2dbf5ca55bdd24ad1ef466 subdir/new_file
+ * L e8ee89e15bbe9b20137715232387b3de5b28972e subdir.txt
+ *
+ * --------
+ *
+ * A - current_file (UNMODIFIED) -> not in results
+ * B D file_deleted
+ * M I ignored_file (IGNORED)
+ * C M modified_file
+ * N U new_file (UNTRACKED)
+ * D M staged_changes
+ * E D staged_changes_file_deleted
+ * F M staged_changes_modified_file
+ * G D staged_delete_file_deleted
+ * H - staged_delete_modified_file (UNMODIFIED) -> not in results
+ * O U staged_new_file
+ * P U staged_new_file_modified_file
+ * I - subdir/current_file (UNMODIFIED) -> not in results
+ * J D subdir/deleted_file
+ * K M subdir/modified_file
+ * Q U subdir/new_file
+ * L - subdir.txt (UNMODIFIED) -> not in results
+ *
+ * Expect 13 files, 0 ADD, 4 DEL, 4 MOD, 1 IGN, 4 UNTR
+ */
+
+
+void test_diff_workdir__larger_hunks(void)
+{
+ const char *a_commit = "d70d245ed97ed2aa596dd1af6536e4bfdb047b69";
+ const char *b_commit = "7a9e0b02e63179929fed24f0a3e0f19168114d10";
+ git_tree *a, *b;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ size_t i, d, num_d, h, num_h, l, num_l;
+
+ g_repo = cl_git_sandbox_init("diff");
+
+ cl_assert((a = resolve_commit_oid_to_tree(g_repo, a_commit)) != NULL);
+ cl_assert((b = resolve_commit_oid_to_tree(g_repo, b_commit)) != NULL);
+
+ opts.context_lines = 1;
+ opts.interhunk_lines = 0;
+
+ for (i = 0; i <= 2; ++i) {
+ git_diff *diff = NULL;
+ git_patch *patch;
+ const git_diff_hunk *hunk;
+ const git_diff_line *line;
+
+ /* okay, this is a bit silly, but oh well */
+ switch (i) {
+ case 0:
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ break;
+ case 1:
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, a, &opts));
+ break;
+ case 2:
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, b, &opts));
+ break;
+ }
+
+ num_d = git_diff_num_deltas(diff);
+ cl_assert_equal_i(2, (int)num_d);
+
+ for (d = 0; d < num_d; ++d) {
+ cl_git_pass(git_patch_from_diff(&patch, diff, d));
+ cl_assert(patch);
+
+ num_h = git_patch_num_hunks(patch);
+ for (h = 0; h < num_h; h++) {
+ cl_git_pass(git_patch_get_hunk(&hunk, &num_l, patch, h));
+
+ for (l = 0; l < num_l; ++l) {
+ cl_git_pass(
+ git_patch_get_line_in_hunk(&line, patch, h, l));
+ cl_assert(line);
+ }
+
+ /* confirm fail after the last item */
+ cl_git_fail(
+ git_patch_get_line_in_hunk(&line, patch, h, num_l));
+ }
+
+ /* confirm fail after the last item */
+ cl_git_fail(git_patch_get_hunk(&hunk, &num_l, patch, num_h));
+
+ git_patch_free(patch);
+ }
+
+ git_diff_free(diff);
+ }
+
+ git_tree_free(a);
+ git_tree_free(b);
+}
+
+/* Set up a test that exercises this code. The easiest test using existing
+ * test data is probably to create a sandbox of submod2 and then run a
+ * git_diff_tree_to_workdir against tree
+ * 873585b94bdeabccea991ea5e3ec1a277895b698. As for what you should actually
+ * test, you can start by just checking that the number of lines of diff
+ * content matches the actual output of git diff. That will at least
+ * demonstrate that the submodule content is being used to generate somewhat
+ * comparable outputs. It is a test that would fail without this code and
+ * will succeed with it.
+ */
+
+#include "../submodule/submodule_helpers.h"
+
+void test_diff_workdir__submodules(void)
+{
+ const char *a_commit = "873585b94bdeabccea991ea5e3ec1a277895b698";
+ git_tree *a;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ diff_expects exp;
+
+ g_repo = setup_fixture_submod2();
+
+ a = resolve_commit_oid_to_tree(g_repo, a_commit);
+
+ opts.flags =
+ GIT_DIFF_INCLUDE_UNTRACKED |
+ GIT_DIFF_INCLUDE_IGNORED |
+ GIT_DIFF_RECURSE_UNTRACKED_DIRS |
+ GIT_DIFF_SHOW_UNTRACKED_CONTENT;
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, a, &opts));
+
+ /* diff_print(stderr, diff); */
+
+ /* essentially doing: git diff 873585b94bdeabccea991ea5e3ec1a277895b698 */
+
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ /* so "git diff 873585" returns:
+ * M .gitmodules
+ * A just_a_dir/contents
+ * A just_a_file
+ * A sm_added_and_uncommited
+ * A sm_changed_file
+ * A sm_changed_head
+ * A sm_changed_index
+ * A sm_changed_untracked_file
+ * M sm_missing_commits
+ * A sm_unchanged
+ * which is a little deceptive because of the difference between the
+ * "git diff <treeish>" results from "git_diff_tree_to_workdir". The
+ * only significant difference is that those Added items will show up
+ * as Untracked items in the pure libgit2 diff.
+ *
+ * Then add in the two extra untracked items "not" and "not-submodule"
+ * to get the 12 files reported here.
+ */
+
+ cl_assert_equal_i(12, exp.files);
+
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(10, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ /* the following numbers match "git diff 873585" exactly */
+
+ cl_assert_equal_i(9, exp.hunks);
+
+ cl_assert_equal_i(33, exp.lines);
+ cl_assert_equal_i(2, exp.line_ctxt);
+ cl_assert_equal_i(30, exp.line_adds);
+ cl_assert_equal_i(1, exp.line_dels);
+
+ git_diff_free(diff);
+ git_tree_free(a);
+}
+
+void test_diff_workdir__cannot_diff_against_a_bare_repository(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ git_tree *tree;
+
+ g_repo = cl_git_sandbox_init("testrepo.git");
+
+ cl_assert_equal_i(
+ GIT_EBAREREPO, git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ cl_git_pass(git_repository_head_tree(&tree, g_repo));
+
+ cl_assert_equal_i(
+ GIT_EBAREREPO, git_diff_tree_to_workdir(&diff, g_repo, tree, &opts));
+
+ git_tree_free(tree);
+}
+
+void test_diff_workdir__to_null_tree(void)
+{
+ git_diff *diff;
+ diff_expects exp;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+
+ opts.flags = GIT_DIFF_INCLUDE_UNTRACKED |
+ GIT_DIFF_RECURSE_UNTRACKED_DIRS;
+
+ g_repo = cl_git_sandbox_init("status");
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, NULL, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(exp.files, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ git_diff_free(diff);
+}
+
+void test_diff_workdir__checks_options_version(void)
+{
+ git_diff *diff;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ const git_error *err;
+
+ g_repo = cl_git_sandbox_init("status");
+
+ opts.version = 0;
+ cl_git_fail(git_diff_tree_to_workdir(&diff, g_repo, NULL, &opts));
+ err = git_error_last();
+ cl_assert_equal_i(GIT_ERROR_INVALID, err->klass);
+
+ git_error_clear();
+ opts.version = 1024;
+ cl_git_fail(git_diff_tree_to_workdir(&diff, g_repo, NULL, &opts));
+ err = git_error_last();
+ cl_assert_equal_i(GIT_ERROR_INVALID, err->klass);
+}
+
+void test_diff_workdir__can_diff_empty_untracked_file(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ git_patch *patch = NULL;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_mkfile("empty_standard_repo/emptyfile.txt", "");
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+ opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED | GIT_DIFF_SHOW_UNTRACKED_CONTENT;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ /* without filters */
+ git_patch_from_diff(&patch, diff, 0);
+ cl_assert(NULL != patch);
+ cl_assert(0 == git_patch_get_delta(patch)->new_file.size);
+ cl_assert(0 == strcmp("emptyfile.txt", git_patch_get_delta(patch)->new_file.path));
+ git_patch_free(patch);
+ patch = NULL;
+
+ /* with a filter */
+ cl_repo_set_bool(g_repo, "core.autocrlf", true); /* install some filter */
+ git_patch_from_diff(&patch, diff, 0);
+ cl_assert(NULL != patch);
+ cl_assert(0 == git_patch_get_delta(patch)->new_file.size);
+ cl_assert(0 == strcmp("emptyfile.txt", git_patch_get_delta(patch)->new_file.path));
+ git_patch_free(patch);
+ patch = NULL;
+
+ git_diff_free(diff);
+}
+
+void test_diff_workdir__can_diff_empty_file(void)
+{
+ git_diff *diff;
+ git_tree *tree;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_patch *patch;
+ struct stat st = {0};
+
+ g_repo = cl_git_sandbox_init("attr_index");
+
+ tree = resolve_commit_oid_to_tree(g_repo, "3812cfef3661"); /* HEAD */
+
+ /* baseline - make sure there are no outstanding diffs */
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &opts));
+ cl_assert_equal_i(2, (int)git_diff_num_deltas(diff));
+ git_diff_free(diff);
+
+ /* empty contents of file */
+ cl_git_rewritefile("attr_index/README.txt", "");
+
+ cl_git_pass(git_fs_path_lstat("attr_index/README.txt", &st));
+
+ if (!cl_is_env_set("GITTEST_FLAKY_STAT"))
+ cl_assert_equal_sz(0, st.st_size);
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &opts));
+ cl_assert_equal_i(3, (int)git_diff_num_deltas(diff));
+ /* diffs are: .gitattributes, README.txt, sub/sub/.gitattributes */
+ cl_git_pass(git_patch_from_diff(&patch, diff, 1));
+ git_patch_free(patch);
+ git_diff_free(diff);
+
+ /* remove a file altogether */
+
+ cl_git_pass(p_unlink("attr_index/README.txt"));
+ cl_assert(!git_fs_path_exists("attr_index/README.txt"));
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &opts));
+ cl_assert_equal_i(3, (int)git_diff_num_deltas(diff));
+ cl_git_pass(git_patch_from_diff(&patch, diff, 1));
+ git_patch_free(patch);
+ git_diff_free(diff);
+
+ git_tree_free(tree);
+}
+
+void test_diff_workdir__to_index_issue_1397(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ diff_expects exp;
+
+ g_repo = cl_git_sandbox_init("issue_1397");
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(0, exp.files);
+ cl_assert_equal_i(0, exp.hunks);
+ cl_assert_equal_i(0, exp.lines);
+
+ git_diff_free(diff);
+ diff = NULL;
+
+ cl_git_rewritefile("issue_1397/crlf_file.txt",
+ "first line\r\nsecond line modified\r\nboth with crlf");
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+
+ cl_assert_equal_i(1, exp.hunks);
+
+ cl_assert_equal_i(5, exp.lines);
+ cl_assert_equal_i(3, exp.line_ctxt);
+ cl_assert_equal_i(1, exp.line_adds);
+ cl_assert_equal_i(1, exp.line_dels);
+
+ git_diff_free(diff);
+}
+
+void test_diff_workdir__to_tree_issue_1397(void)
+{
+ const char *a_commit = "7f483a738"; /* the current HEAD */
+ git_tree *a;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ git_diff *diff2 = NULL;
+ diff_expects exp;
+
+ g_repo = cl_git_sandbox_init("issue_1397");
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+ a = resolve_commit_oid_to_tree(g_repo, a_commit);
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, a, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(0, exp.files);
+ cl_assert_equal_i(0, exp.hunks);
+ cl_assert_equal_i(0, exp.lines);
+
+ git_diff_free(diff);
+ diff = NULL;
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, a, NULL, &opts));
+ cl_git_pass(git_diff_index_to_workdir(&diff2, g_repo, NULL, &opts));
+ cl_git_pass(git_diff_merge(diff, diff2));
+ git_diff_free(diff2);
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(0, exp.files);
+ cl_assert_equal_i(0, exp.hunks);
+ cl_assert_equal_i(0, exp.lines);
+
+ git_diff_free(diff);
+ git_tree_free(a);
+}
+
+void test_diff_workdir__untracked_directory_scenarios(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ diff_expects exp;
+ char *pathspec = NULL;
+ static const char *files0[] = {
+ "subdir/deleted_file",
+ "subdir/modified_file",
+ "subdir/new_file",
+ NULL
+ };
+ static const char *files1[] = {
+ "subdir/deleted_file",
+ "subdir/directory/",
+ "subdir/modified_file",
+ "subdir/new_file",
+ NULL
+ };
+ static const char *files2[] = {
+ "subdir/deleted_file",
+ "subdir/directory/more/notignored",
+ "subdir/modified_file",
+ "subdir/new_file",
+ NULL
+ };
+
+ g_repo = cl_git_sandbox_init("status");
+ cl_git_mkfile("status/.gitignore", "ignored\n");
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+ opts.pathspec.strings = &pathspec;
+ opts.pathspec.count = 1;
+ pathspec = "subdir";
+
+ /* baseline for "subdir" pathspec */
+
+ memset(&exp, 0, sizeof(exp));
+ exp.names = files0;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
+
+ cl_assert_equal_i(3, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ git_diff_free(diff);
+
+ /* empty directory */
+
+ cl_git_pass(p_mkdir("status/subdir/directory", 0777));
+
+ memset(&exp, 0, sizeof(exp));
+ exp.names = files1;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
+
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ git_diff_free(diff);
+
+ /* empty directory in empty directory */
+
+ cl_git_pass(p_mkdir("status/subdir/directory/empty", 0777));
+
+ memset(&exp, 0, sizeof(exp));
+ exp.names = files1;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
+
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ git_diff_free(diff);
+
+ /* directory with only ignored files */
+
+ cl_git_pass(p_mkdir("status/subdir/directory/deeper", 0777));
+ cl_git_mkfile("status/subdir/directory/deeper/ignored", "ignore me\n");
+
+ cl_git_pass(p_mkdir("status/subdir/directory/another", 0777));
+ cl_git_mkfile("status/subdir/directory/another/ignored", "ignore me\n");
+
+ memset(&exp, 0, sizeof(exp));
+ exp.names = files1;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
+
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ git_diff_free(diff);
+
+ /* directory with ignored directory (contents irrelevant) */
+
+ cl_git_pass(p_mkdir("status/subdir/directory/more", 0777));
+ cl_git_pass(p_mkdir("status/subdir/directory/more/ignored", 0777));
+ cl_git_mkfile("status/subdir/directory/more/ignored/notignored",
+ "inside ignored dir\n");
+
+ memset(&exp, 0, sizeof(exp));
+ exp.names = files1;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
+
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ git_diff_free(diff);
+
+ /* quick version avoids directory scan */
+
+ opts.flags = opts.flags | GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS;
+
+ memset(&exp, 0, sizeof(exp));
+ exp.names = files1;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
+
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ git_diff_free(diff);
+
+ /* directory with nested non-ignored content */
+
+ opts.flags = opts.flags & ~GIT_DIFF_ENABLE_FAST_UNTRACKED_DIRS;
+
+ cl_git_mkfile("status/subdir/directory/more/notignored",
+ "not ignored deep under untracked\n");
+
+ memset(&exp, 0, sizeof(exp));
+ exp.names = files1;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
+
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ git_diff_free(diff);
+
+ /* use RECURSE_UNTRACKED_DIRS to get actual untracked files (no ignores) */
+
+ opts.flags = opts.flags & ~GIT_DIFF_INCLUDE_IGNORED;
+ opts.flags = opts.flags | GIT_DIFF_RECURSE_UNTRACKED_DIRS;
+
+ memset(&exp, 0, sizeof(exp));
+ exp.names = files2;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ cl_git_pass(git_diff_foreach(diff, diff_file_cb, NULL, NULL, NULL, &exp));
+
+ cl_assert_equal_i(4, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(2, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ git_diff_free(diff);
+}
+
+
+void test_diff_workdir__untracked_directory_comes_last(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+
+ g_repo = cl_git_sandbox_init("renames");
+
+ cl_git_mkfile("renames/.gitignore", "*.ign\n");
+ cl_git_pass(p_mkdir("renames/zzz_untracked", 0777));
+ cl_git_mkfile("renames/zzz_untracked/an.ign", "ignore me please");
+ cl_git_mkfile("renames/zzz_untracked/skip.ign", "ignore me really");
+ cl_git_mkfile("renames/zzz_untracked/test.ign", "ignore me now");
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ cl_assert(diff != NULL);
+
+ git_diff_free(diff);
+}
+
+void test_diff_workdir__untracked_with_bom(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ const git_diff_delta *delta;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+ cl_git_write2file("empty_standard_repo/bom.txt",
+ "\xFF\xFE\x31\x00\x32\x00\x33\x00\x34\x00", 10, O_WRONLY|O_CREAT, 0664);
+
+ opts.flags =
+ GIT_DIFF_INCLUDE_UNTRACKED | GIT_DIFF_SHOW_UNTRACKED_CONTENT;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ cl_assert_equal_i(1, git_diff_num_deltas(diff));
+ cl_assert((delta = git_diff_get_delta(diff, 0)) != NULL);
+ cl_assert_equal_i(GIT_DELTA_UNTRACKED, delta->status);
+
+ /* not known at this point
+ * cl_assert((delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
+ */
+
+ git_diff_free(diff);
+}
+
+void test_diff_workdir__patience_diff(void)
+{
+ git_index *index;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ git_patch *patch = NULL;
+ git_buf buf = GIT_BUF_INIT;
+ const char *expected_normal = "diff --git a/test.txt b/test.txt\nindex 34a5acc..d52725f 100644\n--- a/test.txt\n+++ b/test.txt\n@@ -1,10 +1,7 @@\n When I wrote this\n I did not know\n-how to create\n-a patience diff\n I did not know\n how to create\n+a patience diff\n another problem\n-I did not know\n-how to create\n a minimal diff\n";
+ const char *expected_patience = "diff --git a/test.txt b/test.txt\nindex 34a5acc..d52725f 100644\n--- a/test.txt\n+++ b/test.txt\n@@ -1,10 +1,7 @@\n When I wrote this\n I did not know\n+I did not know\n how to create\n a patience diff\n-I did not know\n-how to create\n another problem\n-I did not know\n-how to create\n a minimal diff\n";
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_mkfile(
+ "empty_standard_repo/test.txt",
+ "When I wrote this\nI did not know\nhow to create\na patience diff\nI did not know\nhow to create\nanother problem\nI did not know\nhow to create\na minimal diff\n");
+ cl_git_pass(git_index_add_bypath(index, "test.txt"));
+ cl_repo_commit_from_index(NULL, g_repo, NULL, 1372350000, "Base");
+ git_index_free(index);
+
+ cl_git_rewritefile(
+ "empty_standard_repo/test.txt",
+ "When I wrote this\nI did not know\nI did not know\nhow to create\na patience diff\nanother problem\na minimal diff\n");
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ cl_assert_equal_i(1, git_diff_num_deltas(diff));
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_to_buf(&buf, patch));
+
+ cl_assert_equal_s(expected_normal, buf.ptr);
+ git_buf_dispose(&buf);
+ git_patch_free(patch);
+ git_diff_free(diff);
+
+ opts.flags |= GIT_DIFF_PATIENCE;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ cl_assert_equal_i(1, git_diff_num_deltas(diff));
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_to_buf(&buf, patch));
+
+ cl_assert_equal_s(expected_patience, buf.ptr);
+ git_buf_dispose(&buf);
+
+ git_patch_free(patch);
+ git_diff_free(diff);
+}
+
+void test_diff_workdir__with_stale_index(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ git_index *idx = NULL;
+ diff_expects exp;
+
+ g_repo = cl_git_sandbox_init("status");
+ cl_git_pass(git_repository_index(&idx, g_repo));
+
+ /* make the in-memory index invalid */
+ {
+ git_repository *r2;
+ git_index *idx2;
+ cl_git_pass(git_repository_open(&r2, "status"));
+ cl_git_pass(git_repository_index(&idx2, r2));
+ cl_git_pass(git_index_add_bypath(idx2, "new_file"));
+ cl_git_pass(git_index_add_bypath(idx2, "subdir/new_file"));
+ cl_git_pass(git_index_remove_bypath(idx2, "staged_new_file"));
+ cl_git_pass(git_index_remove_bypath(idx2, "staged_changes_file_deleted"));
+ cl_git_pass(git_index_write(idx2));
+ git_index_free(idx2);
+ git_repository_free(r2);
+ }
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+ opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED | GIT_DIFF_INCLUDE_UNMODIFIED;
+
+ /* first try with index pointer which should prevent reload */
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, idx, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(17, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNTRACKED]);
+ cl_assert_equal_i(5, exp.file_status[GIT_DELTA_UNMODIFIED]);
+
+ git_diff_free(diff);
+
+ /* now let's try without the index pointer which should trigger reload */
+
+ /* two files that were UNTRACKED should have become UNMODIFIED */
+ /* one file that was UNMODIFIED should now have become UNTRACKED */
+ /* one file that was DELETED should now be gone completely */
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ git_diff_free(diff);
+
+ cl_assert_equal_i(16, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(3, exp.file_status[GIT_DELTA_UNTRACKED]);
+ cl_assert_equal_i(6, exp.file_status[GIT_DELTA_UNMODIFIED]);
+
+ git_index_free(idx);
+}
+
+static int touch_file(void *payload, git_str *path)
+{
+ struct stat st;
+ struct p_timeval times[2];
+
+ GIT_UNUSED(payload);
+ if (git_fs_path_isdir(path->ptr))
+ return 0;
+
+ cl_must_pass(p_stat(path->ptr, &st));
+
+ times[0].tv_sec = st.st_mtime + 3;
+ times[0].tv_usec = 0;
+ times[1].tv_sec = st.st_mtime + 3;
+ times[1].tv_usec = 0;
+
+ cl_must_pass(p_utimes(path->ptr, times));
+ return 0;
+}
+
+static void basic_diff_status(git_diff **out, const git_diff_options *opts)
+{
+ diff_expects exp;
+
+ cl_git_pass(git_diff_index_to_workdir(out, g_repo, NULL, opts));
+
+ memset(&exp, 0, sizeof(exp));
+
+ cl_git_pass(git_diff_foreach(
+ *out, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(13, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(4, exp.file_status[GIT_DELTA_UNTRACKED]);
+}
+
+void test_diff_workdir__can_update_index(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ git_diff_perfdata perf = GIT_DIFF_PERFDATA_INIT;
+ git_index *index;
+
+ g_repo = cl_git_sandbox_init("status");
+
+ /* touch all the files so stat times are different */
+ {
+ git_str path = GIT_STR_INIT;
+ cl_git_pass(git_str_sets(&path, "status"));
+ cl_git_pass(git_fs_path_direach(&path, 0, touch_file, NULL));
+ git_str_dispose(&path);
+ }
+
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
+
+ basic_diff_status(&diff, &opts);
+
+ cl_git_pass(git_diff_get_perfdata(&perf, diff));
+ cl_assert_equal_sz(13 + 3, perf.stat_calls);
+ cl_assert_equal_sz(5, perf.oid_calculations);
+
+ git_diff_free(diff);
+
+ /* now allow diff to update stat cache */
+ opts.flags |= GIT_DIFF_UPDATE_INDEX;
+
+ /* advance a tick for the index so we don't re-calculate racily-clean entries */
+ cl_git_pass(git_repository_index__weakptr(&index, g_repo));
+ tick_index(index);
+
+ basic_diff_status(&diff, &opts);
+
+ cl_git_pass(git_diff_get_perfdata(&perf, diff));
+ cl_assert_equal_sz(13 + 3, perf.stat_calls);
+ cl_assert_equal_sz(5, perf.oid_calculations);
+
+ git_diff_free(diff);
+
+ /* now if we do it again, we should see fewer OID calculations */
+
+ /* tick again as the index updating from the previous diff might have reset the timestamp */
+ tick_index(index);
+ basic_diff_status(&diff, &opts);
+
+ cl_git_pass(git_diff_get_perfdata(&perf, diff));
+ cl_assert_equal_sz(13 + 3, perf.stat_calls);
+ cl_assert_equal_sz(0, perf.oid_calculations);
+
+ git_diff_free(diff);
+}
+
+#define STR7 "0123456"
+#define STR8 "01234567"
+#define STR40 STR8 STR8 STR8 STR8 STR8
+#define STR200 STR40 STR40 STR40 STR40 STR40
+#define STR999Z STR200 STR200 STR200 STR200 STR40 STR40 STR40 STR40 \
+ STR8 STR8 STR8 STR8 STR7 "\0"
+#define STR1000 STR200 STR200 STR200 STR200 STR200
+#define STR3999Z STR1000 STR1000 STR1000 STR999Z
+#define STR4000 STR1000 STR1000 STR1000 STR1000
+
+static void assert_delta_binary(git_diff *diff, size_t idx, int is_binary)
+{
+ git_patch *patch;
+ const git_diff_delta *delta;
+
+ cl_git_pass(git_patch_from_diff(&patch, diff, idx));
+ delta = git_patch_get_delta(patch);
+ cl_assert_equal_b((delta->flags & GIT_DIFF_FLAG_BINARY), is_binary);
+ git_patch_free(patch);
+}
+
+void test_diff_workdir__binary_detection(void)
+{
+ git_index *idx;
+ git_diff *diff = NULL;
+ git_str b = GIT_STR_INIT;
+ int i;
+ git_str data[10] = {
+ { "1234567890", 0, 10 }, /* 0 - all ascii text control */
+ { "\xC3\x85\xC3\xBC\xE2\x80\xA0\x48\xC3\xB8\xCF\x80\xCE\xA9", 0, 14 }, /* 1 - UTF-8 multibyte text */
+ { "\xEF\xBB\xBF\xC3\x9C\xE2\xA4\x92\xC6\x92\x38\xC2\xA3\xE2\x82\xAC", 0, 16 }, /* 2 - UTF-8 with BOM */
+ { STR999Z, 0, 1000 }, /* 3 - ASCII with NUL at 1000 */
+ { STR3999Z, 0, 4000 }, /* 4 - ASCII with NUL at 4000 */
+ { STR4000 STR3999Z "x", 0, 8001 }, /* 5 - ASCII with NUL at 8000 */
+ { STR4000 STR4000 "\0", 0, 8001 }, /* 6 - ASCII with NUL at 8001 */
+ { "\x00\xDC\x00\x6E\x21\x39\xFE\x0E\x00\x63\x00\xF8"
+ "\x00\x64\x00\x65\x20\x48", 0, 18 }, /* 7 - UTF-16 text */
+ { "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d"
+ "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d",
+ 0, 26 }, /* 8 - All non-printable characters (no NUL) */
+ { "Hello \x01\x02\x03\x04\x05\x06 World!\x01\x02\x03\x04"
+ "\x05\x06\x07", 0, 26 }, /* 9 - 50-50 non-printable (no NUL) */
+ };
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+ cl_git_pass(git_repository_index(&idx, g_repo));
+
+ /* We start with ASCII in index and test data in workdir,
+ * then we will try with test data in index and ASCII in workdir.
+ */
+
+ cl_git_pass(git_str_sets(&b, "empty_standard_repo/0"));
+ for (i = 0; i < 10; ++i) {
+ b.ptr[b.size - 1] = '0' + i;
+ cl_git_mkfile(b.ptr, "baseline");
+ cl_git_pass(git_index_add_bypath(idx, &b.ptr[b.size - 1]));
+
+ if (data[i].size == 0)
+ data[i].size = strlen(data[i].ptr);
+ cl_git_write2file(
+ b.ptr, data[i].ptr, data[i].size, O_WRONLY|O_TRUNC, 0664);
+ }
+ cl_git_pass(git_index_write(idx));
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
+
+ cl_assert_equal_i(10, git_diff_num_deltas(diff));
+
+ /* using diff binary detection (i.e. looking for NUL byte) */
+ assert_delta_binary(diff, 0, false);
+ assert_delta_binary(diff, 1, false);
+ assert_delta_binary(diff, 2, false);
+ assert_delta_binary(diff, 3, true);
+ assert_delta_binary(diff, 4, true);
+ assert_delta_binary(diff, 5, true);
+ assert_delta_binary(diff, 6, false);
+ assert_delta_binary(diff, 7, true);
+ assert_delta_binary(diff, 8, false);
+ assert_delta_binary(diff, 9, false);
+ /* The above have been checked to match command-line Git */
+
+ git_diff_free(diff);
+
+ cl_git_pass(git_str_sets(&b, "empty_standard_repo/0"));
+ for (i = 0; i < 10; ++i) {
+ b.ptr[b.size - 1] = '0' + i;
+ cl_git_pass(git_index_add_bypath(idx, &b.ptr[b.size - 1]));
+
+ cl_git_write2file(b.ptr, "baseline\n", 9, O_WRONLY|O_TRUNC, 0664);
+ }
+ cl_git_pass(git_index_write(idx));
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, NULL));
+
+ cl_assert_equal_i(10, git_diff_num_deltas(diff));
+
+ /* using diff binary detection (i.e. looking for NUL byte) */
+ assert_delta_binary(diff, 0, false);
+ assert_delta_binary(diff, 1, false);
+ assert_delta_binary(diff, 2, false);
+ assert_delta_binary(diff, 3, true);
+ assert_delta_binary(diff, 4, true);
+ assert_delta_binary(diff, 5, true);
+ assert_delta_binary(diff, 6, false);
+ assert_delta_binary(diff, 7, true);
+ assert_delta_binary(diff, 8, false);
+ assert_delta_binary(diff, 9, false);
+
+ git_diff_free(diff);
+
+ git_index_free(idx);
+ git_str_dispose(&b);
+}
+
+void test_diff_workdir__to_index_conflicted(void) {
+ const char *a_commit = "26a125ee1bf"; /* the current HEAD */
+ git_index_entry ancestor = {{0}}, ours = {{0}}, theirs = {{0}};
+ git_tree *a;
+ git_index *index;
+ git_diff *diff1, *diff2;
+ const git_diff_delta *delta;
+
+ g_repo = cl_git_sandbox_init("status");
+ a = resolve_commit_oid_to_tree(g_repo, a_commit);
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ ancestor.path = ours.path = theirs.path = "_file";
+ ancestor.mode = ours.mode = theirs.mode = 0100644;
+ git_oid__fromstr(&ancestor.id, "d427e0b2e138501a3d15cc376077a3631e15bd46", GIT_OID_SHA1);
+ git_oid__fromstr(&ours.id, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", GIT_OID_SHA1);
+ git_oid__fromstr(&theirs.id, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", GIT_OID_SHA1);
+ cl_git_pass(git_index_conflict_add(index, &ancestor, &ours, &theirs));
+
+ cl_git_pass(git_diff_tree_to_index(&diff1, g_repo, a, index, NULL));
+ cl_git_pass(git_diff_index_to_workdir(&diff2, g_repo, index, NULL));
+ cl_git_pass(git_diff_merge(diff1, diff2));
+
+ cl_assert_equal_i(git_diff_num_deltas(diff1), 12);
+ delta = git_diff_get_delta(diff1, 0);
+ cl_assert_equal_s(delta->old_file.path, "_file");
+ cl_assert_equal_i(delta->nfiles, 1);
+ cl_assert_equal_i(delta->status, GIT_DELTA_CONFLICTED);
+
+ git_diff_free(diff2);
+ git_diff_free(diff1);
+ git_index_free(index);
+ git_tree_free(a);
+}
+
+void test_diff_workdir__only_writes_index_when_necessary(void)
+{
+ git_index *index;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ git_reference *head;
+ git_object *head_object;
+ unsigned char initial[GIT_HASH_SHA1_SIZE],
+ first[GIT_HASH_SHA1_SIZE],
+ second[GIT_HASH_SHA1_SIZE];
+ git_str path = GIT_STR_INIT;
+ struct stat st;
+ struct p_timeval times[2];
+
+ opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED | GIT_DIFF_UPDATE_INDEX;
+
+ g_repo = cl_git_sandbox_init("status");
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_repository_head(&head, g_repo));
+ cl_git_pass(git_reference_peel(&head_object, head, GIT_OBJECT_COMMIT));
+
+ cl_git_pass(git_reset(g_repo, head_object, GIT_RESET_HARD, NULL));
+
+ memcpy(initial, git_index__checksum(index), GIT_HASH_SHA1_SIZE);
+
+ /* update the index timestamp to avoid raciness */
+ cl_must_pass(p_stat("status/.git/index", &st));
+
+ times[0].tv_sec = st.st_mtime + 5;
+ times[0].tv_usec = 0;
+ times[1].tv_sec = st.st_mtime + 5;
+ times[1].tv_usec = 0;
+
+ cl_must_pass(p_utimes("status/.git/index", times));
+
+ /* ensure diff doesn't touch the index */
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ git_diff_free(diff);
+
+ memcpy(first, git_index__checksum(index), GIT_HASH_SHA1_SIZE);
+ cl_assert(memcmp(initial, first, GIT_HASH_SHA1_SIZE) != 0);
+
+ /* touch all the files so stat times are different */
+ cl_git_pass(git_str_sets(&path, "status"));
+ cl_git_pass(git_fs_path_direach(&path, 0, touch_file, NULL));
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ git_diff_free(diff);
+
+ /* ensure the second diff did update the index */
+ memcpy(second, git_index__checksum(index), GIT_HASH_SHA1_SIZE);
+ cl_assert(memcmp(first, second, GIT_HASH_SHA1_SIZE) != 0);
+
+ git_str_dispose(&path);
+ git_object_free(head_object);
+ git_reference_free(head);
+ git_index_free(index);
+}
+
+void test_diff_workdir__to_index_pathlist(void)
+{
+ git_index *index;
+ git_diff *diff;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_vector pathlist = GIT_VECTOR_INIT;
+
+ git_vector_insert(&pathlist, "foobar/asdf");
+ git_vector_insert(&pathlist, "subdir/asdf");
+ git_vector_insert(&pathlist, "ignored/asdf");
+
+ g_repo = cl_git_sandbox_init("status");
+
+ cl_git_mkfile("status/.gitignore", ".gitignore\n" "ignored/\n");
+
+ cl_must_pass(p_mkdir("status/foobar", 0777));
+ cl_git_mkfile("status/foobar/one", "one\n");
+
+ cl_must_pass(p_mkdir("status/ignored", 0777));
+ cl_git_mkfile("status/ignored/one", "one\n");
+ cl_git_mkfile("status/ignored/two", "two\n");
+ cl_git_mkfile("status/ignored/three", "three\n");
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ opts.flags = GIT_DIFF_INCLUDE_IGNORED;
+ opts.pathspec.strings = (char **)pathlist.contents;
+ opts.pathspec.count = pathlist.length;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, &opts));
+ cl_assert_equal_i(0, git_diff_num_deltas(diff));
+ git_diff_free(diff);
+
+ opts.flags |= GIT_DIFF_DISABLE_PATHSPEC_MATCH;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, &opts));
+ cl_assert_equal_i(0, git_diff_num_deltas(diff));
+ git_diff_free(diff);
+
+ git_index_free(index);
+ git_vector_free(&pathlist);
+}
+
+void test_diff_workdir__symlink_changed_on_non_symlink_platform(void)
+{
+ git_tree *tree;
+ git_diff *diff;
+ diff_expects exp = {0};
+ const git_diff_delta *delta;
+ const char *commit = "7fccd7";
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_vector pathlist = GIT_VECTOR_INIT;
+ int symlinks;
+
+ g_repo = cl_git_sandbox_init("unsymlinked.git");
+
+ cl_git_pass(git_repository__configmap_lookup(&symlinks, g_repo, GIT_CONFIGMAP_SYMLINKS));
+
+ if (symlinks)
+ cl_skip();
+
+ cl_git_pass(git_vector_insert(&pathlist, "include/Nu/Nu.h"));
+
+ opts.pathspec.strings = (char **)pathlist.contents;
+ opts.pathspec.count = pathlist.length;
+
+ cl_must_pass(p_mkdir("symlink", 0777));
+ cl_git_pass(git_repository_set_workdir(g_repo, "symlink", false));
+
+ cl_assert((tree = resolve_commit_oid_to_tree(g_repo, commit)) != NULL);
+
+ /* first, do the diff with the original contents */
+
+ cl_git_pass(git_futils_mkpath2file("symlink/include/Nu/Nu.h", 0755));
+ cl_git_mkfile("symlink/include/Nu/Nu.h", "../../objc/Nu.h");
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &opts));
+ cl_assert_equal_i(0, git_diff_num_deltas(diff));
+ git_diff_free(diff);
+
+ /* now update the contents and expect a difference, but that the file
+ * mode has persisted as a symbolic link.
+ */
+
+ cl_git_rewritefile("symlink/include/Nu/Nu.h", "awesome content\n");
+
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &opts));
+
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ cl_assert_equal_i(1, exp.files);
+
+ cl_assert_equal_i(1, git_diff_num_deltas(diff));
+ delta = git_diff_get_delta(diff, 0);
+ cl_assert_equal_i(GIT_FILEMODE_LINK, delta->old_file.mode);
+ cl_assert_equal_i(GIT_FILEMODE_LINK, delta->new_file.mode);
+
+ git_diff_free(diff);
+
+ cl_git_pass(git_futils_rmdir_r("symlink", NULL, GIT_RMDIR_REMOVE_FILES));
+
+ git_tree_free(tree);
+ git_vector_free(&pathlist);
+}
+
+void test_diff_workdir__order(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_buf patch = GIT_BUF_INIT;
+ git_oid tree_oid, blob_oid;
+ git_treebuilder *builder;
+ git_tree *tree;
+ git_diff *diff;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ /* Build tree with a single file "abc.txt" */
+ cl_git_pass(git_blob_create_from_buffer(&blob_oid, g_repo, "foo\n", 4));
+ cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL));
+ cl_git_pass(git_treebuilder_insert(NULL, builder, "abc.txt", &blob_oid, GIT_FILEMODE_BLOB));
+ cl_git_pass(git_treebuilder_write(&tree_oid, builder));
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_oid));
+
+ /* Create a directory that sorts before and one that sorts after "abc.txt" */
+ cl_git_mkfile("empty_standard_repo/abc.txt", "bar\n");
+ cl_must_pass(p_mkdir("empty_standard_repo/abb", 0777));
+ cl_must_pass(p_mkdir("empty_standard_repo/abd", 0777));
+
+ opts.flags = GIT_DIFF_INCLUDE_UNTRACKED;
+ cl_git_pass(git_diff_tree_to_workdir(&diff, g_repo, tree, &opts));
+
+ cl_assert_equal_i(1, git_diff_num_deltas(diff));
+ cl_git_pass(git_diff_to_buf(&patch, diff, GIT_DIFF_FORMAT_PATCH));
+ cl_assert_equal_s(patch.ptr,
+ "diff --git a/abc.txt b/abc.txt\n"
+ "index 257cc56..5716ca5 100644\n"
+ "--- a/abc.txt\n"
+ "+++ b/abc.txt\n"
+ "@@ -1 +1 @@\n"
+ "-foo\n"
+ "+bar\n");
+
+ git_treebuilder_free(builder);
+ git_buf_dispose(&patch);
+ git_diff_free(diff);
+ git_tree_free(tree);
+}
+
+void test_diff_workdir__ignore_blank_lines(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff;
+ git_patch *patch;
+ git_buf buf = GIT_BUF_INIT;
+
+ g_repo = cl_git_sandbox_init("rebase");
+ cl_git_rewritefile("rebase/gravy.txt", "GRAVY SOUP.\n\n\nGet eight pounds of coarse lean beef--wash it clean and lay it in your\n\npot, put in the same ingredients as for the shin soup, with the same\nquantity of water, and follow the process directed for that. Strain the\nsoup through a sieve, and serve it up clear, with nothing more than\ntoasted bread in it; two table-spoonsful of mushroom catsup will add a\nfine flavour to the soup!\n");
+
+ /* Perform the diff normally */
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_to_buf(&buf, patch));
+
+ cl_assert_equal_s("diff --git a/gravy.txt b/gravy.txt\nindex c4e6cca..3c617e6 100644\n--- a/gravy.txt\n+++ b/gravy.txt\n@@ -1,8 +1,10 @@\n GRAVY SOUP.\n \n+\n Get eight pounds of coarse lean beef--wash it clean and lay it in your\n+\n pot, put in the same ingredients as for the shin soup, with the same\n quantity of water, and follow the process directed for that. Strain the\n soup through a sieve, and serve it up clear, with nothing more than\n toasted bread in it; two table-spoonsful of mushroom catsup will add a\n-fine flavour to the soup.\n+fine flavour to the soup!\n", buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_patch_free(patch);
+ git_diff_free(diff);
+
+ /* Perform the diff ignoring blank lines */
+ opts.flags |= GIT_DIFF_IGNORE_BLANK_LINES;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+ cl_git_pass(git_patch_from_diff(&patch, diff, 0));
+ cl_git_pass(git_patch_to_buf(&buf, patch));
+
+ cl_assert_equal_s("diff --git a/gravy.txt b/gravy.txt\nindex c4e6cca..3c617e6 100644\n--- a/gravy.txt\n+++ b/gravy.txt\n@@ -5,4 +7,4 @@ pot, put in the same ingredients as for the shin soup, with the same\n quantity of water, and follow the process directed for that. Strain the\n soup through a sieve, and serve it up clear, with nothing more than\n toasted bread in it; two table-spoonsful of mushroom catsup will add a\n-fine flavour to the soup.\n+fine flavour to the soup!\n", buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_patch_free(patch);
+ git_diff_free(diff);
+}
+
+void test_diff_workdir__to_index_reversed_content_loads(void)
+{
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ diff_expects exp;
+ int use_iterator;
+ char *pathspec = "new_file";
+
+ g_repo = cl_git_sandbox_init("status");
+
+ opts.context_lines = 3;
+ opts.interhunk_lines = 1;
+ opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED |
+ GIT_DIFF_SHOW_UNTRACKED_CONTENT | GIT_DIFF_REVERSE;
+ opts.pathspec.strings = &pathspec;
+ opts.pathspec.count = 1;
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, NULL, &opts));
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, diff_binary_cb, diff_hunk_cb, diff_line_cb, &exp));
+
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_MODIFIED]);
+ cl_assert_equal_i(0, exp.file_status[GIT_DELTA_IGNORED]);
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_UNTRACKED]);
+
+ cl_assert_equal_i(1, exp.hunks);
+
+ cl_assert_equal_i(1, exp.lines);
+ cl_assert_equal_i(0, exp.line_ctxt);
+ cl_assert_equal_i(0, exp.line_adds);
+ cl_assert_equal_i(1, exp.line_dels);
+ }
+
+ git_diff_free(diff);
+}
diff --git a/tests/libgit2/email/create.c b/tests/libgit2/email/create.c
new file mode 100644
index 0000000..cf40ff5
--- /dev/null
+++ b/tests/libgit2/email/create.c
@@ -0,0 +1,364 @@
+#include "clar.h"
+#include "clar_libgit2.h"
+
+#include "diff_generate.h"
+
+static git_repository *repo;
+
+void test_email_create__initialize(void)
+{
+ repo = cl_git_sandbox_init("diff_format_email");
+}
+
+void test_email_create__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static void email_for_commit(
+ git_buf *out,
+ const char *commit_id,
+ git_email_create_options *opts)
+{
+ git_oid oid;
+ git_commit *commit = NULL;
+ git_diff *diff = NULL;
+
+ git_oid__fromstr(&oid, commit_id, GIT_OID_SHA1);
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+
+ cl_git_pass(git_email_create_from_commit(out, commit, opts));
+
+ git_diff_free(diff);
+ git_commit_free(commit);
+}
+
+static void assert_email_match(
+ const char *expected,
+ const char *commit_id,
+ git_email_create_options *opts)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ email_for_commit(&buf, commit_id, opts);
+ cl_assert_equal_s(expected, buf.ptr);
+
+ git_buf_dispose(&buf);
+}
+
+static void assert_subject_match(
+ const char *expected,
+ const char *commit_id,
+ git_email_create_options *opts)
+{
+ git_buf buf = GIT_BUF_INIT;
+ char *subject, *nl;
+
+ email_for_commit(&buf, commit_id, opts);
+
+ cl_assert((subject = strstr(buf.ptr, "\nSubject: ")) != NULL);
+ subject += 10;
+
+ if ((nl = strchr(subject, '\n')) != NULL)
+ *nl = '\0';
+
+ cl_assert_equal_s(expected, subject);
+
+ git_buf_dispose(&buf);
+}
+
+void test_email_create__commit(void)
+{
+ const char *expected =
+ "From 9264b96c6d104d0e07ae33d3007b6a48246c6f92 Mon Sep 17 00:00:00 2001\n" \
+ "From: Jacques Germishuys <jacquesg@striata.com>\n" \
+ "Date: Wed, 9 Apr 2014 20:57:01 +0200\n" \
+ "Subject: [PATCH] Modify some content\n" \
+ "\n" \
+ "---\n" \
+ " file1.txt | 8 +++++---\n" \
+ " 1 file changed, 5 insertions(+), 3 deletions(-)\n" \
+ "\n" \
+ "diff --git a/file1.txt b/file1.txt\n" \
+ "index 94aaae8..af8f41d 100644\n" \
+ "--- a/file1.txt\n" \
+ "+++ b/file1.txt\n" \
+ "@@ -1,15 +1,17 @@\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ "+_file1.txt_\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ "+\n" \
+ "+\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ "-file1.txt\n" \
+ "-file1.txt\n" \
+ "-file1.txt\n" \
+ "+_file1.txt_\n" \
+ "+_file1.txt_\n" \
+ " file1.txt\n" \
+ "--\n" \
+ "libgit2 " LIBGIT2_VERSION "\n" \
+ "\n";
+
+ assert_email_match(
+ expected, "9264b96c6d104d0e07ae33d3007b6a48246c6f92", NULL);
+}
+
+void test_email_create__rename(void)
+{
+ const char *expected =
+ "From 6e05acc5a5dab507d91a0a0cc0fb05a3dd98892d Mon Sep 17 00:00:00 2001\n" \
+ "From: Jacques Germishuys <jacquesg@striata.com>\n" \
+ "Date: Wed, 9 Apr 2014 21:15:56 +0200\n" \
+ "Subject: [PATCH] Renamed file1.txt -> file1.txt.renamed\n" \
+ "\n" \
+ "---\n" \
+ " file1.txt => file1.txt.renamed | 4 ++--\n" \
+ " 1 file changed, 2 insertions(+), 2 deletions(-)\n" \
+ "\n" \
+ "diff --git a/file1.txt b/file1.txt.renamed\n" \
+ "similarity index 86%\n" \
+ "rename from file1.txt\n" \
+ "rename to file1.txt.renamed\n" \
+ "index af8f41d..a97157a 100644\n" \
+ "--- a/file1.txt\n" \
+ "+++ b/file1.txt.renamed\n" \
+ "@@ -3,13 +3,13 @@ file1.txt\n" \
+ " _file1.txt_\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ "-file1.txt\n" \
+ "+file1.txt_renamed\n" \
+ " file1.txt\n" \
+ " \n" \
+ " \n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ "-file1.txt\n" \
+ "+file1.txt_renamed\n" \
+ " file1.txt\n" \
+ " file1.txt\n" \
+ " _file1.txt_\n" \
+ "--\n" \
+ "libgit2 " LIBGIT2_VERSION "\n" \
+ "\n";
+
+ assert_email_match(expected, "6e05acc5a5dab507d91a0a0cc0fb05a3dd98892d", NULL);
+}
+
+void test_email_create__rename_as_add_delete(void)
+{
+ const char *expected =
+ "From 6e05acc5a5dab507d91a0a0cc0fb05a3dd98892d Mon Sep 17 00:00:00 2001\n" \
+ "From: Jacques Germishuys <jacquesg@striata.com>\n" \
+ "Date: Wed, 9 Apr 2014 21:15:56 +0200\n" \
+ "Subject: [PATCH] Renamed file1.txt -> file1.txt.renamed\n" \
+ "\n" \
+ "---\n" \
+ " file1.txt | 17 -----------------\n" \
+ " file1.txt.renamed | 17 +++++++++++++++++\n" \
+ " 2 files changed, 17 insertions(+), 17 deletions(-)\n" \
+ " delete mode 100644 file1.txt\n" \
+ " create mode 100644 file1.txt.renamed\n" \
+ "\n" \
+ "diff --git a/file1.txt b/file1.txt\n" \
+ "deleted file mode 100644\n" \
+ "index af8f41d..0000000\n" \
+ "--- a/file1.txt\n" \
+ "+++ /dev/null\n" \
+ "@@ -1,17 +0,0 @@\n" \
+ "-file1.txt\n" \
+ "-file1.txt\n" \
+ "-_file1.txt_\n" \
+ "-file1.txt\n" \
+ "-file1.txt\n" \
+ "-file1.txt\n" \
+ "-file1.txt\n" \
+ "-\n" \
+ "-\n" \
+ "-file1.txt\n" \
+ "-file1.txt\n" \
+ "-file1.txt\n" \
+ "-file1.txt\n" \
+ "-file1.txt\n" \
+ "-_file1.txt_\n" \
+ "-_file1.txt_\n" \
+ "-file1.txt\n" \
+ "diff --git a/file1.txt.renamed b/file1.txt.renamed\n" \
+ "new file mode 100644\n" \
+ "index 0000000..a97157a\n" \
+ "--- /dev/null\n" \
+ "+++ b/file1.txt.renamed\n" \
+ "@@ -0,0 +1,17 @@\n" \
+ "+file1.txt\n" \
+ "+file1.txt\n" \
+ "+_file1.txt_\n" \
+ "+file1.txt\n" \
+ "+file1.txt\n" \
+ "+file1.txt_renamed\n" \
+ "+file1.txt\n" \
+ "+\n" \
+ "+\n" \
+ "+file1.txt\n" \
+ "+file1.txt\n" \
+ "+file1.txt_renamed\n" \
+ "+file1.txt\n" \
+ "+file1.txt\n" \
+ "+_file1.txt_\n" \
+ "+_file1.txt_\n" \
+ "+file1.txt\n" \
+ "--\n" \
+ "libgit2 " LIBGIT2_VERSION "\n" \
+ "\n";
+
+ git_email_create_options opts = GIT_EMAIL_CREATE_OPTIONS_INIT;
+ opts.flags |= GIT_EMAIL_CREATE_NO_RENAMES;
+
+ assert_email_match(expected, "6e05acc5a5dab507d91a0a0cc0fb05a3dd98892d", &opts);
+}
+
+void test_email_create__binary(void)
+{
+ const char *expected =
+ "From 8d7523f6fcb2404257889abe0d96f093d9f524f9 Mon Sep 17 00:00:00 2001\n" \
+ "From: Jacques Germishuys <jacquesg@striata.com>\n" \
+ "Date: Sun, 13 Apr 2014 18:10:18 +0200\n" \
+ "Subject: [PATCH] Modified binary file\n" \
+ "\n" \
+ "---\n" \
+ " binary.bin | Bin 3 -> 5 bytes\n" \
+ " 1 file changed, 0 insertions(+), 0 deletions(-)\n" \
+ "\n" \
+ "diff --git a/binary.bin b/binary.bin\n" \
+ "index bd474b2519cc15eab801ff851cc7d50f0dee49a1..9ac35ff15cd8864aeafd889e4826a3150f0b06c4 100644\n" \
+ "GIT binary patch\n" \
+ "literal 5\n" \
+ "Mc${NkU}WL~000&M4gdfE\n" \
+ "\n" \
+ "literal 3\n" \
+ "Kc${Nk-~s>u4FC%O\n" \
+ "\n" \
+ "--\n" \
+ "libgit2 " LIBGIT2_VERSION "\n" \
+ "\n";
+
+ assert_email_match(expected, "8d7523f6fcb2404257889abe0d96f093d9f524f9", NULL);
+}
+
+void test_email_create__binary_not_included(void)
+{
+ const char *expected =
+ "From 8d7523f6fcb2404257889abe0d96f093d9f524f9 Mon Sep 17 00:00:00 2001\n" \
+ "From: Jacques Germishuys <jacquesg@striata.com>\n" \
+ "Date: Sun, 13 Apr 2014 18:10:18 +0200\n" \
+ "Subject: [PATCH] Modified binary file\n" \
+ "\n" \
+ "---\n" \
+ " binary.bin | Bin 3 -> 5 bytes\n" \
+ " 1 file changed, 0 insertions(+), 0 deletions(-)\n" \
+ "\n" \
+ "diff --git a/binary.bin b/binary.bin\n" \
+ "index bd474b2..9ac35ff 100644\n" \
+ "Binary files a/binary.bin and b/binary.bin differ\n" \
+ "--\n" \
+ "libgit2 " LIBGIT2_VERSION "\n" \
+ "\n";
+
+ git_email_create_options opts = GIT_EMAIL_CREATE_OPTIONS_INIT;
+ opts.diff_opts.flags &= ~GIT_DIFF_SHOW_BINARY;
+
+ assert_email_match(expected, "8d7523f6fcb2404257889abe0d96f093d9f524f9", &opts);
+}
+
+void test_email_create__custom_summary_and_body(void)
+{
+ const char *expected = "From 627e7e12d87e07a83fad5b6bfa25e86ead4a5270 Mon Sep 17 00:00:00 2001\n" \
+ "From: Patrick Steinhardt <ps@pks.im>\n" \
+ "Date: Tue, 24 Nov 2015 13:34:39 +0100\n" \
+ "Subject: [PPPPPATCH 2/4] This is a subject\n" \
+ "\n" \
+ "Modify content of file3.txt by appending a new line. Make this\n" \
+ "commit message somewhat longer to test behavior with newlines\n" \
+ "embedded in the message body.\n" \
+ "\n" \
+ "Also test if new paragraphs are included correctly.\n" \
+ "---\n" \
+ " file3.txt | 1 +\n" \
+ " 1 file changed, 1 insertion(+)\n" \
+ "\n" \
+ "diff --git a/file3.txt b/file3.txt\n" \
+ "index 9a2d780..7309653 100644\n" \
+ "--- a/file3.txt\n" \
+ "+++ b/file3.txt\n" \
+ "@@ -3,3 +3,4 @@ file3!\n" \
+ " file3\n" \
+ " file3\n" \
+ " file3\n" \
+ "+file3\n" \
+ "--\n" \
+ "libgit2 " LIBGIT2_VERSION "\n" \
+ "\n";
+
+ const char *summary = "This is a subject\nwith\nnewlines";
+ const char *body = "Modify content of file3.txt by appending a new line. Make this\n" \
+ "commit message somewhat longer to test behavior with newlines\n" \
+ "embedded in the message body.\n" \
+ "\n" \
+ "Also test if new paragraphs are included correctly.";
+
+ git_oid oid;
+ git_commit *commit = NULL;
+ git_diff *diff = NULL;
+ git_buf buf = GIT_BUF_INIT;
+ git_email_create_options opts = GIT_EMAIL_CREATE_OPTIONS_INIT;
+
+ opts.subject_prefix = "PPPPPATCH";
+
+ git_oid__fromstr(&oid, "627e7e12d87e07a83fad5b6bfa25e86ead4a5270", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+ cl_git_pass(git_diff__commit(&diff, repo, commit, NULL));
+ cl_git_pass(git_email_create_from_diff(&buf, diff, 2, 4, &oid, summary, body, git_commit_author(commit), &opts));
+
+ cl_assert_equal_s(expected, buf.ptr);
+
+ git_diff_free(diff);
+ git_commit_free(commit);
+ git_buf_dispose(&buf);
+}
+
+void test_email_create__commit_subjects(void)
+{
+ git_email_create_options opts = GIT_EMAIL_CREATE_OPTIONS_INIT;
+
+ assert_subject_match("[PATCH] Modify some content", "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts);
+
+ opts.reroll_number = 42;
+ assert_subject_match("[PATCH v42] Modify some content", "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts);
+
+ opts.flags |= GIT_EMAIL_CREATE_ALWAYS_NUMBER;
+ assert_subject_match("[PATCH v42 1/1] Modify some content", "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts);
+
+ opts.start_number = 9;
+ assert_subject_match("[PATCH v42 9/9] Modify some content", "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts);
+
+ opts.subject_prefix = "";
+ assert_subject_match("[v42 9/9] Modify some content", "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts);
+
+ opts.reroll_number = 0;
+ assert_subject_match("[9/9] Modify some content", "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts);
+
+ opts.start_number = 0;
+ assert_subject_match("[1/1] Modify some content", "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts);
+
+ opts.flags = GIT_EMAIL_CREATE_OMIT_NUMBERS;
+ assert_subject_match("Modify some content", "9264b96c6d104d0e07ae33d3007b6a48246c6f92", &opts);
+}
diff --git a/tests/libgit2/fetch/local.c b/tests/libgit2/fetch/local.c
new file mode 100644
index 0000000..5d2417f
--- /dev/null
+++ b/tests/libgit2/fetch/local.c
@@ -0,0 +1,67 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+
+static git_repository *repo;
+
+void test_fetch_local__initialize(void)
+{
+ cl_git_pass(git_repository_init(&repo, "./fetch", 0));
+}
+
+void test_fetch_local__cleanup(void)
+{
+ git_repository_free(repo);
+ repo = NULL;
+
+ cl_fixture_cleanup("./fetch");
+}
+
+void test_fetch_local__defaults(void)
+{
+ git_remote *remote;
+ git_object *obj;
+ git_oid expected_id;
+
+ cl_git_pass(git_remote_create(&remote, repo, "test",
+ cl_fixture("testrepo.git")));
+ cl_git_pass(git_remote_fetch(remote, NULL, NULL, NULL));
+
+ git_oid__fromstr(&expected_id, "258f0e2a959a364e40ed6603d5d44fbb24765b10", GIT_OID_SHA1);
+
+ cl_git_pass(git_revparse_single(&obj, repo, "refs/remotes/test/haacked"));
+ cl_assert_equal_oid(&expected_id, git_object_id(obj));
+
+ git_object_free(obj);
+ git_remote_free(remote);
+}
+
+void test_fetch_local__reachable_commit(void)
+{
+ git_remote *remote;
+ git_strarray refspecs;
+ git_object *obj;
+ git_oid expected_id;
+ git_str fetchhead = GIT_STR_INIT;
+ char *refspec = "+5b5b025afb0b4c913b4c338a42934a3863bf3644:refs/success";
+
+ refspecs.strings = &refspec;
+ refspecs.count = 1;
+
+ git_oid__fromstr(&expected_id, "5b5b025afb0b4c913b4c338a42934a3863bf3644", GIT_OID_SHA1);
+
+ cl_git_pass(git_remote_create(&remote, repo, "test",
+ cl_fixture("testrepo.git")));
+ cl_git_pass(git_remote_fetch(remote, &refspecs, NULL, NULL));
+
+ cl_git_pass(git_revparse_single(&obj, repo, "refs/success"));
+ cl_assert_equal_oid(&expected_id, git_object_id(obj));
+
+ cl_git_pass(git_futils_readbuffer(&fetchhead, "./fetch/.git/FETCH_HEAD"));
+ cl_assert_equal_strn(fetchhead.ptr,
+ "5b5b025afb0b4c913b4c338a42934a3863bf3644\t\t'5b5b025afb0b4c913b4c338a42934a3863bf3644' of ",
+ strlen("5b5b025afb0b4c913b4c338a42934a3863bf3644\t\t'5b5b025afb0b4c913b4c338a42934a3863bf3644' of "));
+
+ git_str_dispose(&fetchhead);
+ git_object_free(obj);
+ git_remote_free(remote);
+}
diff --git a/tests/libgit2/fetchhead/fetchhead_data.h b/tests/libgit2/fetchhead/fetchhead_data.h
new file mode 100644
index 0000000..77c3220
--- /dev/null
+++ b/tests/libgit2/fetchhead/fetchhead_data.h
@@ -0,0 +1,48 @@
+
+#define FETCH_HEAD_WILDCARD_DATA_LOCAL \
+ "49322bb17d3acc9146f98c97d078513228bbf3c0\t\tbranch 'master' of https://github.com/libgit2/TestGitRepository\n" \
+ "0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of https://github.com/libgit2/TestGitRepository\n" \
+ "42e4e7c5e507e113ebbb7801b16b52cf867b7ce1\tnot-for-merge\tbranch 'no-parent' of https://github.com/libgit2/TestGitRepository\n" \
+ "d96c4e80345534eccee5ac7b07fc7603b56124cb\tnot-for-merge\ttag 'annotated_tag' of https://github.com/libgit2/TestGitRepository\n" \
+ "55a1a760df4b86a02094a904dfa511deb5655905\tnot-for-merge\ttag 'blob' of https://github.com/libgit2/TestGitRepository\n" \
+ "8f50ba15d49353813cc6e20298002c0d17b0a9ee\tnot-for-merge\ttag 'commit_tree' of https://github.com/libgit2/TestGitRepository\n"
+
+#define FETCH_HEAD_WILDCARD_DATA \
+ "49322bb17d3acc9146f98c97d078513228bbf3c0\t\tbranch 'master' of https://github.com/libgit2/TestGitRepository\n" \
+ "0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of https://github.com/libgit2/TestGitRepository\n" \
+ "42e4e7c5e507e113ebbb7801b16b52cf867b7ce1\tnot-for-merge\tbranch 'no-parent' of https://github.com/libgit2/TestGitRepository\n" \
+ "d96c4e80345534eccee5ac7b07fc7603b56124cb\tnot-for-merge\ttag 'annotated_tag' of https://github.com/libgit2/TestGitRepository\n" \
+ "55a1a760df4b86a02094a904dfa511deb5655905\tnot-for-merge\ttag 'blob' of https://github.com/libgit2/TestGitRepository\n" \
+ "8f50ba15d49353813cc6e20298002c0d17b0a9ee\tnot-for-merge\ttag 'commit_tree' of https://github.com/libgit2/TestGitRepository\n" \
+ "6e0c7bdb9b4ed93212491ee778ca1c65047cab4e\tnot-for-merge\ttag 'nearly-dangling' of https://github.com/libgit2/TestGitRepository\n"
+
+#define FETCH_HEAD_WILDCARD_DATA2 \
+ "49322bb17d3acc9146f98c97d078513228bbf3c0\t\tbranch 'master' of https://github.com/libgit2/TestGitRepository\n" \
+ "0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of https://github.com/libgit2/TestGitRepository\n" \
+ "42e4e7c5e507e113ebbb7801b16b52cf867b7ce1\tnot-for-merge\tbranch 'no-parent' of https://github.com/libgit2/TestGitRepository\n" \
+
+#define FETCH_HEAD_NO_MERGE_DATA \
+ "0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of https://github.com/libgit2/TestGitRepository\n" \
+ "49322bb17d3acc9146f98c97d078513228bbf3c0\tnot-for-merge\tbranch 'master' of https://github.com/libgit2/TestGitRepository\n" \
+ "42e4e7c5e507e113ebbb7801b16b52cf867b7ce1\tnot-for-merge\tbranch 'no-parent' of https://github.com/libgit2/TestGitRepository\n" \
+ "d96c4e80345534eccee5ac7b07fc7603b56124cb\tnot-for-merge\ttag 'annotated_tag' of https://github.com/libgit2/TestGitRepository\n" \
+ "55a1a760df4b86a02094a904dfa511deb5655905\tnot-for-merge\ttag 'blob' of https://github.com/libgit2/TestGitRepository\n" \
+ "8f50ba15d49353813cc6e20298002c0d17b0a9ee\tnot-for-merge\ttag 'commit_tree' of https://github.com/libgit2/TestGitRepository\n" \
+ "6e0c7bdb9b4ed93212491ee778ca1c65047cab4e\tnot-for-merge\ttag 'nearly-dangling' of https://github.com/libgit2/TestGitRepository\n"
+
+#define FETCH_HEAD_NO_MERGE_DATA2 \
+ "0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of https://github.com/libgit2/TestGitRepository\n" \
+ "49322bb17d3acc9146f98c97d078513228bbf3c0\tnot-for-merge\tbranch 'master' of https://github.com/libgit2/TestGitRepository\n" \
+ "42e4e7c5e507e113ebbb7801b16b52cf867b7ce1\tnot-for-merge\tbranch 'no-parent' of https://github.com/libgit2/TestGitRepository\n" \
+
+#define FETCH_HEAD_NO_MERGE_DATA3 \
+ "0966a434eb1a025db6b71485ab63a3bfbea520b6\tnot-for-merge\tbranch 'first-merge' of https://github.com/libgit2/TestGitRepository\n" \
+ "49322bb17d3acc9146f98c97d078513228bbf3c0\tnot-for-merge\tbranch 'master' of https://github.com/libgit2/TestGitRepository\n" \
+ "42e4e7c5e507e113ebbb7801b16b52cf867b7ce1\tnot-for-merge\tbranch 'no-parent' of https://github.com/libgit2/TestGitRepository\n" \
+ "8f50ba15d49353813cc6e20298002c0d17b0a9ee\tnot-for-merge\ttag 'commit_tree' of https://github.com/libgit2/TestGitRepository\n" \
+
+#define FETCH_HEAD_EXPLICIT_DATA \
+ "0966a434eb1a025db6b71485ab63a3bfbea520b6\t\tbranch 'first-merge' of https://github.com/libgit2/TestGitRepository\n"
+
+#define FETCH_HEAD_QUOTE_DATA \
+ "0966a434eb1a025db6b71485ab63a3bfbea520b6\t\tbranch 'first's-merge' of https://github.com/libgit2/TestGitRepository\n"
diff --git a/tests/libgit2/fetchhead/nonetwork.c b/tests/libgit2/fetchhead/nonetwork.c
new file mode 100644
index 0000000..2519d89
--- /dev/null
+++ b/tests/libgit2/fetchhead/nonetwork.c
@@ -0,0 +1,548 @@
+#include "clar_libgit2.h"
+
+#include "futils.h"
+#include "fetchhead.h"
+
+#include "fetchhead_data.h"
+
+#define DO_LOCAL_TEST 0
+
+static git_repository *g_repo;
+
+void test_fetchhead_nonetwork__initialize(void)
+{
+ g_repo = NULL;
+}
+
+static void cleanup_repository(void *path)
+{
+ if (g_repo) {
+ git_repository_free(g_repo);
+ g_repo = NULL;
+ }
+
+ cl_fixture_cleanup((const char *)path);
+}
+
+static void populate_fetchhead(git_vector *out, git_repository *repo)
+{
+ git_fetchhead_ref *fetchhead_ref;
+ git_oid oid;
+
+ cl_git_pass(git_oid__fromstr(&oid,
+ "49322bb17d3acc9146f98c97d078513228bbf3c0",
+ GIT_OID_SHA1));
+ cl_git_pass(git_fetchhead_ref_create(&fetchhead_ref, &oid, 1,
+ "refs/heads/master",
+ "https://github.com/libgit2/TestGitRepository"));
+ cl_git_pass(git_vector_insert(out, fetchhead_ref));
+
+ cl_git_pass(git_oid__fromstr(&oid,
+ "0966a434eb1a025db6b71485ab63a3bfbea520b6",
+ GIT_OID_SHA1));
+ cl_git_pass(git_fetchhead_ref_create(&fetchhead_ref, &oid, 0,
+ "refs/heads/first-merge",
+ "https://github.com/libgit2/TestGitRepository"));
+ cl_git_pass(git_vector_insert(out, fetchhead_ref));
+
+ cl_git_pass(git_oid__fromstr(&oid,
+ "42e4e7c5e507e113ebbb7801b16b52cf867b7ce1",
+ GIT_OID_SHA1));
+ cl_git_pass(git_fetchhead_ref_create(&fetchhead_ref, &oid, 0,
+ "refs/heads/no-parent",
+ "https://github.com/libgit2/TestGitRepository"));
+ cl_git_pass(git_vector_insert(out, fetchhead_ref));
+
+ cl_git_pass(git_oid__fromstr(&oid,
+ "d96c4e80345534eccee5ac7b07fc7603b56124cb",
+ GIT_OID_SHA1));
+ cl_git_pass(git_fetchhead_ref_create(&fetchhead_ref, &oid, 0,
+ "refs/tags/annotated_tag",
+ "https://github.com/libgit2/TestGitRepository"));
+ cl_git_pass(git_vector_insert(out, fetchhead_ref));
+
+ cl_git_pass(git_oid__fromstr(&oid,
+ "55a1a760df4b86a02094a904dfa511deb5655905",
+ GIT_OID_SHA1));
+ cl_git_pass(git_fetchhead_ref_create(&fetchhead_ref, &oid, 0,
+ "refs/tags/blob",
+ "https://github.com/libgit2/TestGitRepository"));
+ cl_git_pass(git_vector_insert(out, fetchhead_ref));
+
+ cl_git_pass(git_oid__fromstr(&oid,
+ "8f50ba15d49353813cc6e20298002c0d17b0a9ee",
+ GIT_OID_SHA1));
+ cl_git_pass(git_fetchhead_ref_create(&fetchhead_ref, &oid, 0,
+ "refs/tags/commit_tree",
+ "https://github.com/libgit2/TestGitRepository"));
+ cl_git_pass(git_vector_insert(out, fetchhead_ref));
+
+ cl_git_pass(git_fetchhead_write(repo, out));
+}
+
+void test_fetchhead_nonetwork__write(void)
+{
+ git_vector fetchhead_vector = GIT_VECTOR_INIT;
+ git_fetchhead_ref *fetchhead_ref;
+ git_str fetchhead_buf = GIT_STR_INIT;
+ int equals = 0;
+ size_t i;
+
+ cl_git_pass(git_vector_init(&fetchhead_vector, 6, NULL));
+
+ cl_set_cleanup(&cleanup_repository, "./test1");
+ cl_git_pass(git_repository_init(&g_repo, "./test1", 0));
+
+ populate_fetchhead(&fetchhead_vector, g_repo);
+
+ cl_git_pass(git_futils_readbuffer(&fetchhead_buf,
+ "./test1/.git/FETCH_HEAD"));
+
+ equals = (strcmp(fetchhead_buf.ptr, FETCH_HEAD_WILDCARD_DATA_LOCAL) == 0);
+
+ git_str_dispose(&fetchhead_buf);
+
+ git_vector_foreach(&fetchhead_vector, i, fetchhead_ref) {
+ git_fetchhead_ref_free(fetchhead_ref);
+ }
+
+ git_vector_free(&fetchhead_vector);
+
+ cl_assert(equals);
+}
+
+typedef struct {
+ git_vector *fetchhead_vector;
+ size_t idx;
+} fetchhead_ref_cb_data;
+
+static int fetchhead_ref_cb(const char *name, const char *url,
+ const git_oid *oid, unsigned int is_merge, void *payload)
+{
+ fetchhead_ref_cb_data *cb_data = payload;
+ git_fetchhead_ref *expected;
+
+ cl_assert(payload);
+
+ expected = git_vector_get(cb_data->fetchhead_vector, cb_data->idx);
+
+ cl_assert_equal_oid(&expected->oid, oid);
+ cl_assert(expected->is_merge == is_merge);
+
+ if (expected->ref_name)
+ cl_assert_equal_s(expected->ref_name, name);
+ else
+ cl_assert(name == NULL);
+
+ if (expected->remote_url)
+ cl_assert_equal_s(expected->remote_url, url);
+ else
+ cl_assert(url == NULL);
+
+ cb_data->idx++;
+
+ return 0;
+}
+
+void test_fetchhead_nonetwork__read(void)
+{
+ git_vector fetchhead_vector = GIT_VECTOR_INIT;
+ git_fetchhead_ref *fetchhead_ref;
+ fetchhead_ref_cb_data cb_data;
+ size_t i;
+
+ memset(&cb_data, 0x0, sizeof(fetchhead_ref_cb_data));
+
+ cl_set_cleanup(&cleanup_repository, "./test1");
+ cl_git_pass(git_repository_init(&g_repo, "./test1", 0));
+
+ populate_fetchhead(&fetchhead_vector, g_repo);
+
+ cb_data.fetchhead_vector = &fetchhead_vector;
+
+ cl_git_pass(git_repository_fetchhead_foreach(g_repo, fetchhead_ref_cb, &cb_data));
+
+ git_vector_foreach(&fetchhead_vector, i, fetchhead_ref) {
+ git_fetchhead_ref_free(fetchhead_ref);
+ }
+
+ git_vector_free(&fetchhead_vector);
+}
+
+static int read_old_style_cb(const char *name, const char *url,
+ const git_oid *oid, unsigned int is_merge, void *payload)
+{
+ git_oid expected;
+
+ GIT_UNUSED(payload);
+
+ git_oid__fromstr(&expected, "49322bb17d3acc9146f98c97d078513228bbf3c0", GIT_OID_SHA1);
+
+ cl_assert(name == NULL);
+ cl_assert(url == NULL);
+ cl_assert_equal_oid(&expected, oid);
+ cl_assert(is_merge == 1);
+
+ return 0;
+}
+
+void test_fetchhead_nonetwork__read_old_style(void)
+{
+ cl_set_cleanup(&cleanup_repository, "./test1");
+ cl_git_pass(git_repository_init(&g_repo, "./test1", 0));
+
+ cl_git_rewritefile("./test1/.git/FETCH_HEAD", "49322bb17d3acc9146f98c97d078513228bbf3c0\n");
+
+ cl_git_pass(git_repository_fetchhead_foreach(g_repo, read_old_style_cb, NULL));
+}
+
+static int read_type_missing(const char *ref_name, const char *remote_url,
+ const git_oid *oid, unsigned int is_merge, void *payload)
+{
+ git_oid expected;
+
+ GIT_UNUSED(payload);
+
+ git_oid__fromstr(&expected, "49322bb17d3acc9146f98c97d078513228bbf3c0", GIT_OID_SHA1);
+
+ cl_assert_equal_s("name", ref_name);
+ cl_assert_equal_s("remote_url", remote_url);
+ cl_assert_equal_oid(&expected, oid);
+ cl_assert(is_merge == 0);
+
+ return 0;
+}
+
+void test_fetchhead_nonetwork__type_missing(void)
+{
+ cl_set_cleanup(&cleanup_repository, "./test1");
+ cl_git_pass(git_repository_init(&g_repo, "./test1", 0));
+
+ cl_git_rewritefile("./test1/.git/FETCH_HEAD", "49322bb17d3acc9146f98c97d078513228bbf3c0\tnot-for-merge\t'name' of remote_url\n");
+
+ cl_git_pass(git_repository_fetchhead_foreach(g_repo, read_type_missing, NULL));
+}
+
+static int read_name_missing(const char *ref_name, const char *remote_url,
+ const git_oid *oid, unsigned int is_merge, void *payload)
+{
+ git_oid expected;
+
+ GIT_UNUSED(payload);
+
+ git_oid__fromstr(&expected, "49322bb17d3acc9146f98c97d078513228bbf3c0", GIT_OID_SHA1);
+
+ cl_assert(ref_name == NULL);
+ cl_assert_equal_s("remote_url", remote_url);
+ cl_assert_equal_oid(&expected, oid);
+ cl_assert(is_merge == 0);
+
+ return 0;
+}
+
+void test_fetchhead_nonetwork__name_missing(void)
+{
+ cl_set_cleanup(&cleanup_repository, "./test1");
+ cl_git_pass(git_repository_init(&g_repo, "./test1", 0));
+
+ cl_git_rewritefile("./test1/.git/FETCH_HEAD", "49322bb17d3acc9146f98c97d078513228bbf3c0\tnot-for-merge\tremote_url\n");
+
+ cl_git_pass(git_repository_fetchhead_foreach(g_repo, read_name_missing, NULL));
+}
+
+static int read_noop(const char *ref_name, const char *remote_url,
+ const git_oid *oid, unsigned int is_merge, void *payload)
+{
+ GIT_UNUSED(ref_name);
+ GIT_UNUSED(remote_url);
+ GIT_UNUSED(oid);
+ GIT_UNUSED(is_merge);
+ GIT_UNUSED(payload);
+
+ return 0;
+}
+
+void test_fetchhead_nonetwork__nonexistent(void)
+{
+ int error;
+
+ cl_set_cleanup(&cleanup_repository, "./test1");
+ cl_git_pass(git_repository_init(&g_repo, "./test1", 0));
+
+ cl_git_fail((error = git_repository_fetchhead_foreach(g_repo, read_noop, NULL)));
+ cl_assert(error == GIT_ENOTFOUND);
+}
+
+void test_fetchhead_nonetwork__invalid_unterminated_last_line(void)
+{
+ cl_set_cleanup(&cleanup_repository, "./test1");
+ cl_git_pass(git_repository_init(&g_repo, "./test1", 0));
+
+ cl_git_rewritefile("./test1/.git/FETCH_HEAD", "unterminated");
+ cl_git_fail(git_repository_fetchhead_foreach(g_repo, read_noop, NULL));
+}
+
+void test_fetchhead_nonetwork__invalid_oid(void)
+{
+ cl_set_cleanup(&cleanup_repository, "./test1");
+ cl_git_pass(git_repository_init(&g_repo, "./test1", 0));
+
+ cl_git_rewritefile("./test1/.git/FETCH_HEAD", "shortoid\n");
+ cl_git_fail(git_repository_fetchhead_foreach(g_repo, read_noop, NULL));
+}
+
+void test_fetchhead_nonetwork__invalid_for_merge(void)
+{
+ cl_set_cleanup(&cleanup_repository, "./test1");
+ cl_git_pass(git_repository_init(&g_repo, "./test1", 0));
+
+ cl_git_rewritefile("./test1/.git/FETCH_HEAD", "49322bb17d3acc9146f98c97d078513228bbf3c0\tinvalid-merge\t\n");
+ cl_git_fail(git_repository_fetchhead_foreach(g_repo, read_noop, NULL));
+
+ cl_assert(git__prefixcmp(git_error_last()->message, "invalid for-merge") == 0);
+}
+
+void test_fetchhead_nonetwork__invalid_description(void)
+{
+ cl_set_cleanup(&cleanup_repository, "./test1");
+ cl_git_pass(git_repository_init(&g_repo, "./test1", 0));
+
+ cl_git_rewritefile("./test1/.git/FETCH_HEAD", "49322bb17d3acc9146f98c97d078513228bbf3c0\tnot-for-merge\n");
+ cl_git_fail(git_repository_fetchhead_foreach(g_repo, read_noop, NULL));
+
+ cl_assert(git__prefixcmp(git_error_last()->message, "invalid description") == 0);
+}
+
+static int assert_master_for_merge(const char *ref, const char *url, const git_oid *id, unsigned int is_merge, void *data)
+{
+ GIT_UNUSED(url);
+ GIT_UNUSED(id);
+ GIT_UNUSED(data);
+
+ if (!strcmp("refs/heads/master", ref) && !is_merge)
+ return -1;
+
+ return 0;
+}
+
+static int assert_none_for_merge(const char *ref, const char *url, const git_oid *id, unsigned int is_merge, void *data)
+{
+ GIT_UNUSED(ref);
+ GIT_UNUSED(url);
+ GIT_UNUSED(id);
+ GIT_UNUSED(data);
+
+ return is_merge ? -1 : 0;
+}
+
+void test_fetchhead_nonetwork__unborn_with_upstream(void)
+{
+ git_repository *repo;
+ git_remote *remote;
+
+ /* Create an empty repo to clone from */
+ cl_set_cleanup(&cleanup_repository, "./test1");
+ cl_git_pass(git_repository_init(&g_repo, "./test1", 0));
+ cl_set_cleanup(&cleanup_repository, "./repowithunborn");
+ cl_git_pass(git_clone(&repo, "./test1", "./repowithunborn", NULL));
+
+ /* Simulate someone pushing to it by changing to one that has stuff */
+ cl_git_pass(git_remote_set_url(repo, "origin", cl_fixture("testrepo.git")));
+ cl_git_pass(git_remote_lookup(&remote, repo, "origin"));
+
+ cl_git_pass(git_remote_fetch(remote, NULL, NULL, NULL));
+ git_remote_free(remote);
+
+ cl_git_pass(git_repository_fetchhead_foreach(repo, assert_master_for_merge, NULL));
+
+ git_repository_free(repo);
+ cl_fixture_cleanup("./repowithunborn");
+}
+
+void test_fetchhead_nonetwork__fetch_into_repo_with_symrefs(void)
+{
+ git_repository *repo;
+ git_remote *remote;
+ git_reference *symref;
+
+ repo = cl_git_sandbox_init("empty_standard_repo");
+
+ /*
+ * Testing for a specific constellation where the repository has at
+ * least one symbolic reference in its refdb.
+ */
+ cl_git_pass(git_reference_symbolic_create(&symref, repo, "refs/heads/symref", "refs/heads/master", 0, NULL));
+
+ cl_git_pass(git_remote_set_url(repo, "origin", cl_fixture("testrepo.git")));
+ cl_git_pass(git_remote_lookup(&remote, repo, "origin"));
+ cl_git_pass(git_remote_fetch(remote, NULL, NULL, NULL));
+
+ git_remote_free(remote);
+ git_reference_free(symref);
+ cl_git_sandbox_cleanup();
+}
+
+void test_fetchhead_nonetwork__fetch_into_repo_with_invalid_head(void)
+{
+ git_remote *remote;
+ char *strings[] = { "refs/heads/*:refs/remotes/origin/*" };
+ git_strarray refspecs = { strings, 1 };
+
+ cl_set_cleanup(&cleanup_repository, "./test1");
+ cl_git_pass(git_repository_init(&g_repo, "./test1", 0));
+
+ /* HEAD pointing to nonexistent branch */
+ cl_git_rewritefile("./test1/.git/HEAD", "ref: refs/heads/\n");
+
+ cl_git_pass(git_remote_create_anonymous(&remote, g_repo, cl_fixture("testrepo.git")));
+ cl_git_pass(git_remote_fetch(remote, &refspecs, NULL, NULL));
+ cl_git_pass(git_repository_fetchhead_foreach(g_repo, assert_none_for_merge, NULL));
+
+ git_remote_free(remote);
+}
+
+void test_fetchhead_nonetwork__quote_in_branch_name(void)
+{
+ cl_set_cleanup(&cleanup_repository, "./test1");
+ cl_git_pass(git_repository_init(&g_repo, "./test1", 0));
+
+ cl_git_rewritefile("./test1/.git/FETCH_HEAD", FETCH_HEAD_QUOTE_DATA);
+ cl_git_pass(git_repository_fetchhead_foreach(g_repo, read_noop, NULL));
+}
+
+static bool found_master;
+static bool found_haacked;
+static bool find_master_haacked_called;
+
+static int find_master_haacked(const char *ref_name, const char *remote_url, const git_oid *oid, unsigned int is_merge, void *payload)
+{
+ GIT_UNUSED(remote_url);
+ GIT_UNUSED(oid);
+ GIT_UNUSED(payload);
+
+ find_master_haacked_called = true;
+
+ if (!strcmp("refs/heads/master", ref_name)) {
+ cl_assert(is_merge);
+ found_master = true;
+ }
+ if (!strcmp("refs/heads/haacked", ref_name)) {
+ cl_assert(is_merge);
+ found_haacked = true;
+ }
+
+ return 0;
+}
+
+void test_fetchhead_nonetwork__create_when_refpecs_given(void)
+{
+ git_remote *remote;
+ git_str path = GIT_STR_INIT;
+ char *refspec1 = "refs/heads/master";
+ char *refspec2 = "refs/heads/haacked";
+ char *refspecs[] = { refspec1, refspec2 };
+ git_strarray specs = {
+ refspecs,
+ 2,
+ };
+
+ cl_set_cleanup(&cleanup_repository, "./test1");
+ cl_git_pass(git_repository_init(&g_repo, "./test1", 0));
+
+ cl_git_pass(git_str_joinpath(&path, git_repository_path(g_repo), "FETCH_HEAD"));
+ cl_git_pass(git_remote_create(&remote, g_repo, "origin", cl_fixture("testrepo.git")));
+
+ cl_assert(!git_fs_path_exists(path.ptr));
+ cl_git_pass(git_remote_fetch(remote, &specs, NULL, NULL));
+ cl_assert(git_fs_path_exists(path.ptr));
+
+ cl_git_pass(git_repository_fetchhead_foreach(g_repo, find_master_haacked, NULL));
+ cl_assert(find_master_haacked_called);
+ cl_assert(found_master);
+ cl_assert(found_haacked);
+
+ git_remote_free(remote);
+ git_str_dispose(&path);
+}
+
+static bool count_refs_called;
+struct prefix_count {
+ const char *prefix;
+ int count;
+ int expected;
+};
+
+static int count_refs(const char *ref_name, const char *remote_url, const git_oid *oid, unsigned int is_merge, void *payload)
+{
+ int i;
+ struct prefix_count *prefix_counts = (struct prefix_count *) payload;
+
+ GIT_UNUSED(remote_url);
+ GIT_UNUSED(oid);
+ GIT_UNUSED(is_merge);
+
+ count_refs_called = true;
+
+ for (i = 0; prefix_counts[i].prefix; i++) {
+ if (!git__prefixcmp(ref_name, prefix_counts[i].prefix))
+ prefix_counts[i].count++;
+ }
+
+ return 0;
+}
+
+void test_fetchhead_nonetwork__create_with_multiple_refspecs(void)
+{
+ git_remote *remote;
+ git_str path = GIT_STR_INIT;
+
+ cl_set_cleanup(&cleanup_repository, "./test1");
+ cl_git_pass(git_repository_init(&g_repo, "./test1", 0));
+
+ cl_git_pass(git_remote_create(&remote, g_repo, "origin", cl_fixture("testrepo.git")));
+ git_remote_free(remote);
+ cl_git_pass(git_remote_add_fetch(g_repo, "origin", "+refs/notes/*:refs/origin/notes/*"));
+ /* Pick up the new refspec */
+ cl_git_pass(git_remote_lookup(&remote, g_repo, "origin"));
+
+ cl_git_pass(git_str_joinpath(&path, git_repository_path(g_repo), "FETCH_HEAD"));
+ cl_assert(!git_fs_path_exists(path.ptr));
+ cl_git_pass(git_remote_fetch(remote, NULL, NULL, NULL));
+ cl_assert(git_fs_path_exists(path.ptr));
+
+ {
+ int i;
+ struct prefix_count prefix_counts[] = {
+ {"refs/notes/", 0, 1},
+ {"refs/heads/", 0, 13},
+ {"refs/tags/", 0, 7},
+ {NULL, 0, 0},
+ };
+
+ cl_git_pass(git_repository_fetchhead_foreach(g_repo, count_refs, &prefix_counts));
+ cl_assert(count_refs_called);
+ for (i = 0; prefix_counts[i].prefix; i++)
+ cl_assert_equal_i(prefix_counts[i].expected, prefix_counts[i].count);
+ }
+
+ git_remote_free(remote);
+ git_str_dispose(&path);
+}
+
+void test_fetchhead_nonetwork__credentials_are_stripped(void)
+{
+ git_fetchhead_ref *ref;
+ git_oid oid;
+
+ cl_git_pass(git_oid__fromstr(&oid, "49322bb17d3acc9146f98c97d078513228bbf3c0", GIT_OID_SHA1));
+ cl_git_pass(git_fetchhead_ref_create(&ref, &oid, 0,
+ "refs/tags/commit_tree", "http://foo:bar@github.com/libgit2/TestGitRepository"));
+ cl_assert_equal_s(ref->remote_url, "http://github.com/libgit2/TestGitRepository");
+ git_fetchhead_ref_free(ref);
+
+ cl_git_pass(git_oid__fromstr(&oid, "49322bb17d3acc9146f98c97d078513228bbf3c0", GIT_OID_SHA1));
+ cl_git_pass(git_fetchhead_ref_create(&ref, &oid, 0,
+ "refs/tags/commit_tree", "https://foo:bar@github.com/libgit2/TestGitRepository"));
+ cl_assert_equal_s(ref->remote_url, "https://github.com/libgit2/TestGitRepository");
+ git_fetchhead_ref_free(ref);
+}
diff --git a/tests/libgit2/filter/bare.c b/tests/libgit2/filter/bare.c
new file mode 100644
index 0000000..f53cfcb
--- /dev/null
+++ b/tests/libgit2/filter/bare.c
@@ -0,0 +1,188 @@
+#include "clar_libgit2.h"
+#include "crlf.h"
+
+static git_repository *g_repo = NULL;
+static git_blob_filter_options filter_opts = GIT_BLOB_FILTER_OPTIONS_INIT;
+
+void test_filter_bare__initialize(void)
+{
+ cl_fixture_sandbox("crlf.git");
+ cl_git_pass(git_repository_open(&g_repo, "crlf.git"));
+
+ filter_opts.flags |= GIT_BLOB_FILTER_NO_SYSTEM_ATTRIBUTES;
+ filter_opts.flags |= GIT_BLOB_FILTER_ATTRIBUTES_FROM_HEAD;
+}
+
+void test_filter_bare__cleanup(void)
+{
+ git_repository_free(g_repo);
+ cl_fixture_cleanup("crlf.git");
+}
+
+void test_filter_bare__all_crlf(void)
+{
+ git_blob *blob;
+ git_buf buf = { 0 };
+
+ cl_git_pass(git_revparse_single(
+ (git_object **)&blob, g_repo, "a9a2e89")); /* all-crlf */
+
+ cl_assert_equal_s(ALL_CRLF_TEXT_RAW, git_blob_rawcontent(blob));
+
+ cl_git_pass(git_blob_filter(&buf, blob, "file.bin", &filter_opts));
+
+ cl_assert_equal_s(ALL_CRLF_TEXT_RAW, buf.ptr);
+
+ cl_git_pass(git_blob_filter(&buf, blob, "file.crlf", &filter_opts));
+
+ /* in this case, raw content has crlf in it already */
+ cl_assert_equal_s(ALL_CRLF_TEXT_AS_CRLF, buf.ptr);
+
+ cl_git_pass(git_blob_filter(&buf, blob, "file.lf", &filter_opts));
+
+ /* we never convert CRLF -> LF on platforms that have LF */
+ cl_assert_equal_s(ALL_CRLF_TEXT_AS_CRLF, buf.ptr);
+
+ cl_git_pass(git_blob_filter(&buf, blob, "file.txt", &filter_opts));
+
+ /* in this case, raw content has crlf in it already */
+ cl_assert_equal_s(ALL_CRLF_TEXT_AS_CRLF, buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_blob_free(blob);
+}
+
+void test_filter_bare__from_lf(void)
+{
+ git_blob *blob;
+ git_buf buf = { 0 };
+
+ cl_git_pass(git_revparse_single(
+ (git_object **)&blob, g_repo, "799770d")); /* all-lf */
+
+ cl_assert_equal_s(ALL_LF_TEXT_RAW, git_blob_rawcontent(blob));
+
+ cl_git_pass(git_blob_filter(&buf, blob, "file.bin", &filter_opts));
+
+ cl_assert_equal_s(ALL_LF_TEXT_RAW, buf.ptr);
+
+ cl_git_pass(git_blob_filter(&buf, blob, "file.crlf", &filter_opts));
+
+ /* in this case, raw content has crlf in it already */
+ cl_assert_equal_s(ALL_LF_TEXT_AS_CRLF, buf.ptr);
+
+ cl_git_pass(git_blob_filter(&buf, blob, "file.lf", &filter_opts));
+
+ /* we never convert CRLF -> LF on platforms that have LF */
+ cl_assert_equal_s(ALL_LF_TEXT_AS_LF, buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_blob_free(blob);
+}
+
+void test_filter_bare__nested_attributes(void)
+{
+ git_blob *blob;
+ git_buf buf = { 0 };
+
+ cl_git_pass(git_revparse_single(
+ (git_object **)&blob, g_repo, "799770d")); /* all-lf */
+
+ cl_assert_equal_s(ALL_LF_TEXT_RAW, git_blob_rawcontent(blob));
+
+ cl_git_pass(git_blob_filter(&buf, blob, "raw/file.bin", &filter_opts));
+ cl_assert_equal_s(ALL_LF_TEXT_RAW, buf.ptr);
+
+ cl_git_pass(git_blob_filter(&buf, blob, "raw/file.crlf", &filter_opts));
+ cl_assert_equal_s(ALL_LF_TEXT_RAW, buf.ptr);
+
+ cl_git_pass(git_blob_filter(&buf, blob, "raw/file.lf", &filter_opts));
+ cl_assert_equal_s(ALL_LF_TEXT_RAW, buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_blob_free(blob);
+}
+
+void test_filter_bare__sanitizes(void)
+{
+ git_blob *blob;
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_pass(git_revparse_single(
+ (git_object **)&blob, g_repo, "e69de29")); /* zero-byte */
+
+ cl_assert_equal_i(0, git_blob_rawsize(blob));
+ cl_assert_equal_s("", git_blob_rawcontent(blob));
+
+ cl_git_pass(git_blob_filter(&buf, blob, "file.bin", &filter_opts));
+ cl_assert_equal_sz(0, buf.size);
+ cl_assert_equal_s("", buf.ptr);
+ git_buf_dispose(&buf);
+
+ cl_git_pass(git_blob_filter(&buf, blob, "file.crlf", &filter_opts));
+ cl_assert_equal_sz(0, buf.size);
+ cl_assert_equal_s("", buf.ptr);
+ git_buf_dispose(&buf);
+
+ cl_git_pass(git_blob_filter(&buf, blob, "file.lf", &filter_opts));
+ cl_assert_equal_sz(0, buf.size);
+ cl_assert_equal_s("", buf.ptr);
+ git_buf_dispose(&buf);
+
+ git_blob_free(blob);
+}
+
+void test_filter_bare__from_specific_commit_one(void)
+{
+ git_blob_filter_options opts = GIT_BLOB_FILTER_OPTIONS_INIT;
+ git_blob *blob;
+ git_buf buf = { 0 };
+
+ opts.flags |= GIT_BLOB_FILTER_NO_SYSTEM_ATTRIBUTES;
+ opts.flags |= GIT_BLOB_FILTER_ATTRIBUTES_FROM_COMMIT;
+ cl_git_pass(git_oid__fromstr(&opts.attr_commit_id, "b8986fec0f7bde90f78ac72706e782d82f24f2f0", GIT_OID_SHA1));
+
+ cl_git_pass(git_revparse_single(
+ (git_object **)&blob, g_repo, "055c872")); /* ident */
+
+ cl_assert_equal_s("$Id$\n", git_blob_rawcontent(blob));
+
+ cl_git_pass(git_blob_filter(&buf, blob, "ident.bin", &opts));
+ cl_assert_equal_s("$Id$\n", buf.ptr);
+
+ cl_git_pass(git_blob_filter(&buf, blob, "ident.identlf", &opts));
+ cl_assert_equal_s("$Id: 055c8729cdcc372500a08db659c045e16c4409fb $\n", buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_blob_free(blob);
+}
+
+void test_filter_bare__from_specific_commit_with_no_attributes_file(void)
+{
+ git_blob_filter_options opts = GIT_BLOB_FILTER_OPTIONS_INIT;
+ git_blob *blob;
+ git_buf buf = { 0 };
+
+ opts.flags |= GIT_BLOB_FILTER_NO_SYSTEM_ATTRIBUTES;
+ opts.flags |= GIT_BLOB_FILTER_ATTRIBUTES_FROM_COMMIT;
+ cl_git_pass(git_oid__fromstr(&opts.attr_commit_id, "5afb6a14a864e30787857dd92af837e8cdd2cb1b", GIT_OID_SHA1));
+
+ cl_git_pass(git_revparse_single(
+ (git_object **)&blob, g_repo, "799770d")); /* all-lf */
+
+ cl_assert_equal_s(ALL_LF_TEXT_RAW, git_blob_rawcontent(blob));
+
+ cl_git_pass(git_blob_filter(&buf, blob, "file.bin", &opts));
+ cl_assert_equal_s(ALL_LF_TEXT_RAW, buf.ptr);
+
+ /* we never convert CRLF -> LF on platforms that have LF */
+ cl_git_pass(git_blob_filter(&buf, blob, "file.lf", &opts));
+ cl_assert_equal_s(ALL_LF_TEXT_RAW, buf.ptr);
+
+ /* we never convert CRLF -> LF on platforms that have LF */
+ cl_git_pass(git_blob_filter(&buf, blob, "file.crlf", &opts));
+ cl_assert_equal_s(ALL_LF_TEXT_RAW, buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_blob_free(blob);
+}
diff --git a/tests/libgit2/filter/blob.c b/tests/libgit2/filter/blob.c
new file mode 100644
index 0000000..1cc33d4
--- /dev/null
+++ b/tests/libgit2/filter/blob.c
@@ -0,0 +1,145 @@
+#include "clar_libgit2.h"
+#include "crlf.h"
+
+static git_repository *g_repo = NULL;
+
+void test_filter_blob__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("crlf");
+ cl_git_mkfile("crlf/.gitattributes",
+ "*.txt text\n*.bin binary\n"
+ "*.crlf text eol=crlf\n"
+ "*.lf text eol=lf\n"
+ "*.ident text ident\n"
+ "*.identcrlf ident text eol=crlf\n"
+ "*.identlf ident text eol=lf\n");
+}
+
+void test_filter_blob__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_filter_blob__all_crlf(void)
+{
+ git_blob *blob;
+ git_buf buf = { 0 };
+
+ cl_git_pass(git_revparse_single(
+ (git_object **)&blob, g_repo, "a9a2e891")); /* all-crlf */
+
+ cl_assert_equal_s(ALL_CRLF_TEXT_RAW, git_blob_rawcontent(blob));
+
+ cl_git_pass(git_blob_filter(&buf, blob, "file.bin", NULL));
+
+ cl_assert_equal_s(ALL_CRLF_TEXT_RAW, buf.ptr);
+
+ cl_git_pass(git_blob_filter(&buf, blob, "file.crlf", NULL));
+
+ /* in this case, raw content has crlf in it already */
+ cl_assert_equal_s(ALL_CRLF_TEXT_AS_CRLF, buf.ptr);
+
+ cl_git_pass(git_blob_filter(&buf, blob, "file.lf", NULL));
+
+ /* we never convert CRLF -> LF on platforms that have LF */
+ cl_assert_equal_s(ALL_CRLF_TEXT_AS_CRLF, buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_blob_free(blob);
+}
+
+void test_filter_blob__from_lf(void)
+{
+ git_blob *blob;
+ git_buf buf = { 0 };
+
+ cl_git_pass(git_revparse_single(
+ (git_object **)&blob, g_repo, "799770d")); /* all-lf */
+
+ cl_assert_equal_s(ALL_LF_TEXT_RAW, git_blob_rawcontent(blob));
+
+ cl_git_pass(git_blob_filter(&buf, blob, "file.bin", NULL));
+
+ cl_assert_equal_s(ALL_LF_TEXT_RAW, buf.ptr);
+
+ cl_git_pass(git_blob_filter(&buf, blob, "file.crlf", NULL));
+
+ /* in this case, raw content has crlf in it already */
+ cl_assert_equal_s(ALL_LF_TEXT_AS_CRLF, buf.ptr);
+
+ cl_git_pass(git_blob_filter(&buf, blob, "file.lf", NULL));
+
+ /* we never convert CRLF -> LF on platforms that have LF */
+ cl_assert_equal_s(ALL_LF_TEXT_AS_LF, buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_blob_free(blob);
+}
+
+void test_filter_blob__sanitizes(void)
+{
+ git_blob *blob;
+ git_buf buf;
+
+ cl_git_pass(git_revparse_single(
+ (git_object **)&blob, g_repo, "e69de29")); /* zero-byte */
+
+ cl_assert_equal_i(0, git_blob_rawsize(blob));
+ cl_assert_equal_s("", git_blob_rawcontent(blob));
+
+ memset(&buf, 0, sizeof(git_buf));
+ cl_git_pass(git_blob_filter(&buf, blob, "file.bin", NULL));
+ cl_assert_equal_sz(0, buf.size);
+ cl_assert_equal_s("", buf.ptr);
+ git_buf_dispose(&buf);
+
+ memset(&buf, 0, sizeof(git_buf));
+ cl_git_pass(git_blob_filter(&buf, blob, "file.crlf", NULL));
+ cl_assert_equal_sz(0, buf.size);
+ cl_assert_equal_s("", buf.ptr);
+ git_buf_dispose(&buf);
+
+ memset(&buf, 0, sizeof(git_buf));
+ cl_git_pass(git_blob_filter(&buf, blob, "file.lf", NULL));
+ cl_assert_equal_sz(0, buf.size);
+ cl_assert_equal_s("", buf.ptr);
+ git_buf_dispose(&buf);
+
+ git_blob_free(blob);
+}
+
+void test_filter_blob__ident(void)
+{
+ git_oid id;
+ git_blob *blob;
+ git_buf buf = { 0 };
+
+ cl_git_mkfile("crlf/test.ident", "Some text\n$Id$\nGoes there\n");
+ cl_git_pass(git_blob_create_from_workdir(&id, g_repo, "test.ident"));
+ cl_git_pass(git_blob_lookup(&blob, g_repo, &id));
+ cl_assert_equal_s(
+ "Some text\n$Id$\nGoes there\n", git_blob_rawcontent(blob));
+ git_blob_free(blob);
+
+ cl_git_mkfile("crlf/test.ident", "Some text\n$Id: Any old just you want$\nGoes there\n");
+ cl_git_pass(git_blob_create_from_workdir(&id, g_repo, "test.ident"));
+ cl_git_pass(git_blob_lookup(&blob, g_repo, &id));
+ cl_assert_equal_s(
+ "Some text\n$Id$\nGoes there\n", git_blob_rawcontent(blob));
+
+ cl_git_pass(git_blob_filter(&buf, blob, "filter.bin", NULL));
+ cl_assert_equal_s(
+ "Some text\n$Id$\nGoes there\n", buf.ptr);
+
+ cl_git_pass(git_blob_filter(&buf, blob, "filter.identcrlf", NULL));
+ cl_assert_equal_s(
+ "Some text\r\n$Id: 3164f585d548ac68027d22b104f2d8100b2b6845 $\r\nGoes there\r\n", buf.ptr);
+
+ cl_git_pass(git_blob_filter(&buf, blob, "filter.identlf", NULL));
+ cl_assert_equal_s(
+ "Some text\n$Id: 3164f585d548ac68027d22b104f2d8100b2b6845 $\nGoes there\n", buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_blob_free(blob);
+
+}
diff --git a/tests/libgit2/filter/crlf.c b/tests/libgit2/filter/crlf.c
new file mode 100644
index 0000000..925ea58
--- /dev/null
+++ b/tests/libgit2/filter/crlf.c
@@ -0,0 +1,253 @@
+#include "clar_libgit2.h"
+#include "git2/sys/filter.h"
+
+static git_repository *g_repo = NULL;
+
+void test_filter_crlf__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("crlf");
+
+ cl_git_mkfile("crlf/.gitattributes",
+ "*.txt text\n*.bin binary\n*.crlf text eol=crlf\n*.lf text eol=lf\n");
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+}
+
+void test_filter_crlf__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_filter_crlf__to_worktree(void)
+{
+ git_filter_list *fl;
+ git_filter *crlf;
+ git_buf out = GIT_BUF_INIT;
+ const char *in;
+ size_t in_len;
+
+ cl_git_pass(git_filter_list_new(
+ &fl, g_repo, GIT_FILTER_TO_WORKTREE, 0));
+
+ crlf = git_filter_lookup(GIT_FILTER_CRLF);
+ cl_assert(crlf != NULL);
+
+ cl_git_pass(git_filter_list_push(fl, crlf, NULL));
+
+ in = "Some text\nRight here\n";
+ in_len = strlen(in);
+
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, in, in_len));
+
+ cl_assert_equal_s("Some text\r\nRight here\r\n", out.ptr);
+
+ git_filter_list_free(fl);
+ git_buf_dispose(&out);
+}
+
+void test_filter_crlf__to_odb(void)
+{
+ git_filter_list *fl;
+ git_filter *crlf;
+ git_buf out = GIT_BUF_INIT;
+ const char *in;
+ size_t in_len;
+
+ cl_git_pass(git_filter_list_new(
+ &fl, g_repo, GIT_FILTER_TO_ODB, 0));
+
+ crlf = git_filter_lookup(GIT_FILTER_CRLF);
+ cl_assert(crlf != NULL);
+
+ cl_git_pass(git_filter_list_push(fl, crlf, NULL));
+
+ in = "Some text\r\nRight here\r\n";
+ in_len = strlen(in);
+
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, in, in_len));
+
+ cl_assert_equal_s("Some text\nRight here\n", out.ptr);
+
+ git_filter_list_free(fl);
+ git_buf_dispose(&out);
+}
+
+void test_filter_crlf__with_safecrlf(void)
+{
+ git_filter_list *fl;
+ git_filter *crlf;
+ git_buf out = GIT_BUF_INIT;
+ const char *in;
+ size_t in_len;
+
+ cl_repo_set_bool(g_repo, "core.safecrlf", true);
+
+ cl_git_pass(git_filter_list_new(
+ &fl, g_repo, GIT_FILTER_TO_ODB, 0));
+
+ crlf = git_filter_lookup(GIT_FILTER_CRLF);
+ cl_assert(crlf != NULL);
+
+ cl_git_pass(git_filter_list_push(fl, crlf, NULL));
+
+ /* Normalized \r\n succeeds with safecrlf */
+ in = "Normal\r\nCRLF\r\nline-endings.\r\n";
+ in_len = strlen(in);
+
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, in, in_len));
+ cl_assert_equal_s("Normal\nCRLF\nline-endings.\n", out.ptr);
+
+ /* Mix of line endings fails with safecrlf */
+ in = "Mixed\nup\r\nLF\nand\r\nCRLF\nline-endings.\r\n";
+ in_len = strlen(in);
+
+ cl_git_fail(git_filter_list_apply_to_buffer(&out, fl, in, in_len));
+ cl_assert_equal_i(git_error_last()->klass, GIT_ERROR_FILTER);
+
+ /* Normalized \n fails for autocrlf=true when safecrlf=true */
+ in = "Normal\nLF\nonly\nline-endings.\n";
+ in_len = strlen(in);
+
+ cl_git_fail(git_filter_list_apply_to_buffer(&out, fl, in, in_len));
+ cl_assert_equal_i(git_error_last()->klass, GIT_ERROR_FILTER);
+
+ /* String with \r but without \r\n does not fail with safecrlf */
+ in = "Normal\nCR only\rand some more\nline-endings.\n";
+ in_len = strlen(in);
+
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, in, in_len));
+ cl_assert_equal_s("Normal\nCR only\rand some more\nline-endings.\n", out.ptr);
+
+ git_filter_list_free(fl);
+ git_buf_dispose(&out);
+}
+
+void test_filter_crlf__with_safecrlf_and_unsafe_allowed(void)
+{
+ git_filter_list *fl;
+ git_filter *crlf;
+ git_buf out = GIT_BUF_INIT;
+ const char *in;
+ size_t in_len;
+
+ cl_repo_set_bool(g_repo, "core.safecrlf", true);
+
+ cl_git_pass(git_filter_list_new(
+ &fl, g_repo, GIT_FILTER_TO_ODB, GIT_FILTER_ALLOW_UNSAFE));
+
+ crlf = git_filter_lookup(GIT_FILTER_CRLF);
+ cl_assert(crlf != NULL);
+
+ cl_git_pass(git_filter_list_push(fl, crlf, NULL));
+
+ /* Normalized \r\n succeeds with safecrlf */
+ in = "Normal\r\nCRLF\r\nline-endings.\r\n";
+ in_len = strlen(in);
+
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, in, in_len));
+ cl_assert_equal_s("Normal\nCRLF\nline-endings.\n", out.ptr);
+
+ /* Mix of line endings fails with safecrlf, but allowed to pass */
+ in = "Mixed\nup\r\nLF\nand\r\nCRLF\nline-endings.\r\n";
+ in_len = strlen(in);
+
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, in, in_len));
+ /* TODO: check for warning */
+ cl_assert_equal_s("Mixed\nup\nLF\nand\nCRLF\nline-endings.\n", out.ptr);
+
+ /* Normalized \n fails with safecrlf, but allowed to pass */
+ in = "Normal\nLF\nonly\nline-endings.\n";
+ in_len = strlen(in);
+
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, in, in_len));
+ /* TODO: check for warning */
+ cl_assert_equal_s("Normal\nLF\nonly\nline-endings.\n", out.ptr);
+
+ git_filter_list_free(fl);
+ git_buf_dispose(&out);
+}
+
+void test_filter_crlf__no_safecrlf(void)
+{
+ git_filter_list *fl;
+ git_filter *crlf;
+ git_buf out = GIT_BUF_INIT;
+ const char *in;
+ size_t in_len;
+
+ cl_git_pass(git_filter_list_new(
+ &fl, g_repo, GIT_FILTER_TO_ODB, 0));
+
+ crlf = git_filter_lookup(GIT_FILTER_CRLF);
+ cl_assert(crlf != NULL);
+
+ cl_git_pass(git_filter_list_push(fl, crlf, NULL));
+
+ /* Normalized \r\n succeeds with safecrlf */
+ in = "Normal\r\nCRLF\r\nline-endings.\r\n";
+ in_len = strlen(in);
+
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, in, in_len));
+ cl_assert_equal_s("Normal\nCRLF\nline-endings.\n", out.ptr);
+
+ /* Mix of line endings fails with safecrlf */
+ in = "Mixed\nup\r\nLF\nand\r\nCRLF\nline-endings.\r\n";
+ in_len = strlen(in);
+
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, in, in_len));
+ cl_assert_equal_s("Mixed\nup\nLF\nand\nCRLF\nline-endings.\n", out.ptr);
+
+ /* Normalized \n fails with safecrlf */
+ in = "Normal\nLF\nonly\nline-endings.\n";
+ in_len = strlen(in);
+
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, in, in_len));
+ cl_assert_equal_s("Normal\nLF\nonly\nline-endings.\n", out.ptr);
+
+ git_filter_list_free(fl);
+ git_buf_dispose(&out);
+}
+
+void test_filter_crlf__safecrlf_warn(void)
+{
+ git_filter_list *fl;
+ git_filter *crlf;
+ git_buf out = GIT_BUF_INIT;
+ const char *in;
+ size_t in_len;
+
+ cl_repo_set_string(g_repo, "core.safecrlf", "warn");
+
+ cl_git_pass(git_filter_list_new(
+ &fl, g_repo, GIT_FILTER_TO_ODB, 0));
+
+ crlf = git_filter_lookup(GIT_FILTER_CRLF);
+ cl_assert(crlf != NULL);
+
+ cl_git_pass(git_filter_list_push(fl, crlf, NULL));
+
+ /* Normalized \r\n succeeds with safecrlf=warn */
+ in = "Normal\r\nCRLF\r\nline-endings.\r\n";
+ in_len = strlen(in);
+
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, in, in_len));
+ cl_assert_equal_s("Normal\nCRLF\nline-endings.\n", out.ptr);
+
+ /* Mix of line endings succeeds with safecrlf=warn */
+ in = "Mixed\nup\r\nLF\nand\r\nCRLF\nline-endings.\r\n";
+ in_len = strlen(in);
+
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, in, in_len));
+ /* TODO: check for warning */
+ cl_assert_equal_s("Mixed\nup\nLF\nand\nCRLF\nline-endings.\n", out.ptr);
+
+ /* Normalized \n is reversible, so does not fail with safecrlf=warn */
+ in = "Normal\nLF\nonly\nline-endings.\n";
+ in_len = strlen(in);
+
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, in, in_len));
+ cl_assert_equal_s(in, out.ptr);
+
+ git_filter_list_free(fl);
+ git_buf_dispose(&out);
+}
diff --git a/tests/libgit2/filter/crlf.h b/tests/libgit2/filter/crlf.h
new file mode 100644
index 0000000..786edfc
--- /dev/null
+++ b/tests/libgit2/filter/crlf.h
@@ -0,0 +1,30 @@
+#ifndef INCLUDE_filter_crlf_h__
+#define INCLUDE_filter_crlf_h__
+
+/*
+ * file content for files in the resources/crlf repository
+ */
+
+#define UTF8_BOM "\xEF\xBB\xBF"
+
+#define ALL_CRLF_TEXT_RAW "crlf\r\ncrlf\r\ncrlf\r\ncrlf\r\n"
+#define ALL_LF_TEXT_RAW "lf\nlf\nlf\nlf\nlf\n"
+#define MORE_CRLF_TEXT_RAW "crlf\r\ncrlf\r\nlf\ncrlf\r\ncrlf\r\n"
+#define MORE_LF_TEXT_RAW "lf\nlf\ncrlf\r\nlf\nlf\n"
+
+#define ALL_CRLF_TEXT_AS_CRLF ALL_CRLF_TEXT_RAW
+#define ALL_LF_TEXT_AS_CRLF "lf\r\nlf\r\nlf\r\nlf\r\nlf\r\n"
+#define MORE_CRLF_TEXT_AS_CRLF "crlf\r\ncrlf\r\nlf\r\ncrlf\r\ncrlf\r\n"
+#define MORE_LF_TEXT_AS_CRLF "lf\r\nlf\r\ncrlf\r\nlf\r\nlf\r\n"
+
+#define ALL_CRLF_TEXT_AS_LF "crlf\ncrlf\ncrlf\ncrlf\n"
+#define ALL_LF_TEXT_AS_LF ALL_LF_TEXT_RAW
+#define MORE_CRLF_TEXT_AS_LF "crlf\ncrlf\nlf\ncrlf\ncrlf\n"
+#define MORE_LF_TEXT_AS_LF "lf\nlf\ncrlf\nlf\nlf\n"
+
+#define FEW_UTF8_CRLF_RAW "\xe2\x9a\xbdThe rest is ASCII01.\r\nThe rest is ASCII02.\r\nThe rest is ASCII03.\r\nThe rest is ASCII04.\r\nThe rest is ASCII05.\r\nThe rest is ASCII06.\r\nThe rest is ASCII07.\r\nThe rest is ASCII08.\r\nThe rest is ASCII09.\r\nThe rest is ASCII10.\r\nThe rest is ASCII11.\r\nThe rest is ASCII12.\r\nThe rest is ASCII13.\r\nThe rest is ASCII14.\r\nThe rest is ASCII15.\r\nThe rest is ASCII16.\r\nThe rest is ASCII17.\r\nThe rest is ASCII18.\r\nThe rest is ASCII19.\r\nThe rest is ASCII20.\r\nThe rest is ASCII21.\r\nThe rest is ASCII22.\r\n"
+#define FEW_UTF8_LF_RAW "\xe2\x9a\xbdThe rest is ASCII01.\nThe rest is ASCII02.\nThe rest is ASCII03.\nThe rest is ASCII04.\nThe rest is ASCII05.\nThe rest is ASCII06.\nThe rest is ASCII07.\nThe rest is ASCII08.\nThe rest is ASCII09.\nThe rest is ASCII10.\nThe rest is ASCII11.\nThe rest is ASCII12.\nThe rest is ASCII13.\nThe rest is ASCII14.\nThe rest is ASCII15.\nThe rest is ASCII16.\nThe rest is ASCII17.\nThe rest is ASCII18.\nThe rest is ASCII19.\nThe rest is ASCII20.\nThe rest is ASCII21.\nThe rest is ASCII22.\n"
+#define MANY_UTF8_CRLF_RAW "Lets sing!\r\n\xe2\x99\xab\xe2\x99\xaa\xe2\x99\xac\xe2\x99\xa9\r\nEat food\r\n\xf0\x9f\x8d\x85\xf0\x9f\x8d\x95\r\n"
+#define MANY_UTF8_LF_RAW "Lets sing!\n\xe2\x99\xab\xe2\x99\xaa\xe2\x99\xac\xe2\x99\xa9\nEat food\n\xf0\x9f\x8d\x85\xf0\x9f\x8d\x95\n"
+
+#endif
diff --git a/tests/libgit2/filter/custom.c b/tests/libgit2/filter/custom.c
new file mode 100644
index 0000000..57cef33
--- /dev/null
+++ b/tests/libgit2/filter/custom.c
@@ -0,0 +1,268 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "blob.h"
+#include "filter.h"
+#include "git2/sys/filter.h"
+#include "git2/sys/repository.h"
+#include "custom_helpers.h"
+
+/* going TO_WORKDIR, filters are executed low to high
+ * going TO_ODB, filters are executed high to low
+ */
+#define BITFLIP_FILTER_PRIORITY -1
+#define REVERSE_FILTER_PRIORITY -2
+
+#ifdef GIT_WIN32
+# define NEWLINE "\r\n"
+#else
+# define NEWLINE "\n"
+#endif
+
+static char workdir_data[] =
+ "some simple" NEWLINE
+ "data" NEWLINE
+ "that will be" NEWLINE
+ "trivially" NEWLINE
+ "scrambled." NEWLINE;
+
+#define REVERSED_DATA_LEN 51
+
+/* Represents the data above scrambled (bits flipped) after \r\n -> \n
+ * conversion, then bytewise reversed
+ */
+static unsigned char bitflipped_and_reversed_data[] =
+ { 0xf5, 0xd1, 0x9b, 0x9a, 0x93, 0x9d, 0x92, 0x9e, 0x8d, 0x9c, 0x8c,
+ 0xf5, 0x86, 0x93, 0x93, 0x9e, 0x96, 0x89, 0x96, 0x8d, 0x8b, 0xf5,
+ 0x9a, 0x9d, 0xdf, 0x93, 0x93, 0x96, 0x88, 0xdf, 0x8b, 0x9e, 0x97,
+ 0x8b, 0xf5, 0x9e, 0x8b, 0x9e, 0x9b, 0xf5, 0x9a, 0x93, 0x8f, 0x92,
+ 0x96, 0x8c, 0xdf, 0x9a, 0x92, 0x90, 0x8c };
+
+#define BITFLIPPED_AND_REVERSED_DATA_LEN 51
+
+static git_repository *g_repo = NULL;
+
+static void register_custom_filters(void);
+
+void test_filter_custom__initialize(void)
+{
+ register_custom_filters();
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_mkfile(
+ "empty_standard_repo/.gitattributes",
+ "hero* bitflip reverse\n"
+ "herofile text\n"
+ "heroflip -reverse binary\n"
+ "villain erroneous\n"
+ "*.bin binary\n");
+}
+
+void test_filter_custom__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ g_repo = NULL;
+}
+
+static void register_custom_filters(void)
+{
+ static int filters_registered = 0;
+
+ if (!filters_registered) {
+ cl_git_pass(git_filter_register(
+ "bitflip", create_bitflip_filter(), BITFLIP_FILTER_PRIORITY));
+
+ cl_git_pass(git_filter_register(
+ "reverse", create_reverse_filter("+reverse"),
+ REVERSE_FILTER_PRIORITY));
+
+ /* re-register reverse filter with standard filter=xyz priority */
+ cl_git_pass(git_filter_register(
+ "pre-reverse",
+ create_reverse_filter("+prereverse"),
+ GIT_FILTER_DRIVER_PRIORITY));
+
+ cl_git_pass(git_filter_register(
+ "erroneous",
+ create_erroneous_filter("+erroneous"),
+ GIT_FILTER_DRIVER_PRIORITY));
+
+ filters_registered = 1;
+ }
+}
+
+void test_filter_custom__to_odb(void)
+{
+ git_filter_list *fl;
+ git_buf out = GIT_BUF_INIT;
+ const char *in;
+ size_t in_len;
+
+ cl_git_pass(git_filter_list_load(
+ &fl, g_repo, NULL, "herofile", GIT_FILTER_TO_ODB, 0));
+
+ in = workdir_data;
+ in_len = strlen(workdir_data);
+
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, in, in_len));
+
+ cl_assert_equal_i(BITFLIPPED_AND_REVERSED_DATA_LEN, out.size);
+
+ cl_assert_equal_i(
+ 0, memcmp(bitflipped_and_reversed_data, out.ptr, out.size));
+
+ git_filter_list_free(fl);
+ git_buf_dispose(&out);
+}
+
+void test_filter_custom__to_workdir(void)
+{
+ git_filter_list *fl;
+ git_buf out = GIT_BUF_INIT;
+ const char *in;
+ size_t in_len;
+
+ cl_git_pass(git_filter_list_load(
+ &fl, g_repo, NULL, "herofile", GIT_FILTER_TO_WORKTREE, 0));
+
+ in = (char *)bitflipped_and_reversed_data;
+ in_len = BITFLIPPED_AND_REVERSED_DATA_LEN;
+
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, in, in_len));
+
+ cl_assert_equal_i(strlen(workdir_data), out.size);
+
+ cl_assert_equal_i(
+ 0, memcmp(workdir_data, out.ptr, out.size));
+
+ git_filter_list_free(fl);
+ git_buf_dispose(&out);
+}
+
+void test_filter_custom__can_register_a_custom_filter_in_the_repository(void)
+{
+ git_filter_list *fl;
+
+ cl_git_pass(git_filter_list_load(
+ &fl, g_repo, NULL, "herofile", GIT_FILTER_TO_WORKTREE, 0));
+ /* expect: bitflip, reverse, crlf */
+ cl_assert_equal_sz(3, git_filter_list_length(fl));
+ git_filter_list_free(fl);
+
+ cl_git_pass(git_filter_list_load(
+ &fl, g_repo, NULL, "herocorp", GIT_FILTER_TO_WORKTREE, 0));
+ /* expect: bitflip, reverse - possibly crlf depending on global config */
+ {
+ size_t flen = git_filter_list_length(fl);
+ cl_assert(flen == 2 || flen == 3);
+ }
+ git_filter_list_free(fl);
+
+ cl_git_pass(git_filter_list_load(
+ &fl, g_repo, NULL, "hero.bin", GIT_FILTER_TO_WORKTREE, 0));
+ /* expect: bitflip, reverse */
+ cl_assert_equal_sz(2, git_filter_list_length(fl));
+ git_filter_list_free(fl);
+
+ cl_git_pass(git_filter_list_load(
+ &fl, g_repo, NULL, "heroflip", GIT_FILTER_TO_WORKTREE, 0));
+ /* expect: bitflip (because of -reverse) */
+ cl_assert_equal_sz(1, git_filter_list_length(fl));
+ git_filter_list_free(fl);
+
+ cl_git_pass(git_filter_list_load(
+ &fl, g_repo, NULL, "doesntapplytome.bin",
+ GIT_FILTER_TO_WORKTREE, 0));
+ /* expect: none */
+ cl_assert_equal_sz(0, git_filter_list_length(fl));
+ git_filter_list_free(fl);
+}
+
+void test_filter_custom__order_dependency(void)
+{
+ git_index *index;
+ git_blob *blob;
+ git_buf buf = { 0 };
+
+ /* so if ident and reverse are used together, an interesting thing
+ * happens - a reversed "$Id$" string is no longer going to trigger
+ * ident correctly. When checking out, the filters should be applied
+ * in order CLRF, then ident, then reverse, so ident expansion should
+ * work correctly. On check in, the content should be reversed, then
+ * ident, then CRLF filtered. Let's make sure that works...
+ */
+
+ cl_git_mkfile(
+ "empty_standard_repo/.gitattributes",
+ "hero.*.rev-ident text ident prereverse eol=lf\n");
+
+ cl_git_mkfile(
+ "empty_standard_repo/hero.1.rev-ident",
+ "This is a test\n$Id$\nHave fun!\n");
+
+ cl_git_mkfile(
+ "empty_standard_repo/hero.2.rev-ident",
+ "Another test\n$dI$\nCrazy!\n");
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_add_bypath(index, "hero.1.rev-ident"));
+ cl_git_pass(git_index_add_bypath(index, "hero.2.rev-ident"));
+ cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "Filter chains\n");
+ git_index_free(index);
+
+ cl_git_pass(git_blob_lookup(&blob, g_repo,
+ & git_index_get_bypath(index, "hero.1.rev-ident", 0)->id));
+ cl_assert_equal_s(
+ "\n!nuf evaH\n$dI$\ntset a si sihT", git_blob_rawcontent(blob));
+ cl_git_pass(git_blob_filter(&buf, blob, "hero.1.rev-ident", NULL));
+ /* no expansion because id was reversed at checkin and now at ident
+ * time, reverse is not applied yet */
+ cl_assert_equal_s(
+ "This is a test\n$Id$\nHave fun!\n", buf.ptr);
+ git_blob_free(blob);
+
+ cl_git_pass(git_blob_lookup(&blob, g_repo,
+ & git_index_get_bypath(index, "hero.2.rev-ident", 0)->id));
+ cl_assert_equal_s(
+ "\n!yzarC\n$Id$\ntset rehtonA", git_blob_rawcontent(blob));
+ cl_git_pass(git_blob_filter(&buf, blob, "hero.2.rev-ident", NULL));
+ /* expansion because reverse was applied at checkin and at ident time,
+ * reverse is not applied yet */
+ cl_assert_equal_s(
+ "Another test\n$ 59001fe193103b1016b27027c0c827d036fd0ac8 :dI$\nCrazy!\n", buf.ptr);
+ cl_assert_equal_i(0, git_oid_strcmp(
+ git_blob_id(blob), "8ca0df630d728c0c72072b6101b301391ef10095"));
+ git_blob_free(blob);
+
+ git_buf_dispose(&buf);
+}
+
+void test_filter_custom__filter_registry_failure_cases(void)
+{
+ git_filter fake = { GIT_FILTER_VERSION, 0 };
+
+ cl_assert_equal_i(GIT_EEXISTS, git_filter_register("bitflip", &fake, 0));
+
+ cl_git_fail(git_filter_unregister(GIT_FILTER_CRLF));
+ cl_git_fail(git_filter_unregister(GIT_FILTER_IDENT));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_filter_unregister("not-a-filter"));
+}
+
+void test_filter_custom__erroneous_filter_fails(void)
+{
+ git_filter_list *filters;
+ git_buf out = GIT_BUF_INIT;
+ const char *in;
+ size_t in_len;
+
+ cl_git_pass(git_filter_list_load(
+ &filters, g_repo, NULL, "villain", GIT_FILTER_TO_WORKTREE, 0));
+
+ in = workdir_data;
+ in_len = strlen(workdir_data);
+
+ cl_git_fail(git_filter_list_apply_to_buffer(&out, filters, in, in_len));
+
+ git_filter_list_free(filters);
+ git_buf_dispose(&out);
+}
diff --git a/tests/libgit2/filter/custom_helpers.c b/tests/libgit2/filter/custom_helpers.c
new file mode 100644
index 0000000..95a9f97
--- /dev/null
+++ b/tests/libgit2/filter/custom_helpers.c
@@ -0,0 +1,163 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "filter.h"
+#include "git2/sys/filter.h"
+#include "custom_helpers.h"
+
+#define VERY_SECURE_ENCRYPTION(b) ((b) ^ 0xff)
+
+int bitflip_filter_apply(
+ git_filter *self,
+ void **payload,
+ git_str *to,
+ const git_str *from,
+ const git_filter_source *source)
+{
+ const unsigned char *src = (const unsigned char *)from->ptr;
+ unsigned char *dst;
+ size_t i;
+
+ GIT_UNUSED(self); GIT_UNUSED(payload);
+
+ /* verify that attribute path match worked as expected */
+ cl_assert_equal_i(
+ 0, git__strncmp("hero", git_filter_source_path(source), 4));
+
+ if (!from->size)
+ return 0;
+
+ cl_git_pass(git_str_grow(to, from->size));
+
+ dst = (unsigned char *)to->ptr;
+
+ for (i = 0; i < from->size; i++)
+ dst[i] = VERY_SECURE_ENCRYPTION(src[i]);
+
+ to->size = from->size;
+
+ return 0;
+}
+
+static int bitflip_filter_stream(
+ git_writestream **out,
+ git_filter *self,
+ void **payload,
+ const git_filter_source *src,
+ git_writestream *next)
+{
+ return git_filter_buffered_stream_new(out,
+ self, bitflip_filter_apply, NULL, payload, src, next);
+}
+
+static void bitflip_filter_free(git_filter *f)
+{
+ git__free(f);
+}
+
+git_filter *create_bitflip_filter(void)
+{
+ git_filter *filter = git__calloc(1, sizeof(git_filter));
+ cl_assert(filter);
+
+ filter->version = GIT_FILTER_VERSION;
+ filter->attributes = "+bitflip";
+ filter->shutdown = bitflip_filter_free;
+ filter->stream = bitflip_filter_stream;
+
+ return filter;
+}
+
+
+int reverse_filter_apply(
+ git_filter *self,
+ void **payload,
+ git_str *to,
+ const git_str *from,
+ const git_filter_source *source)
+{
+ const unsigned char *src = (const unsigned char *)from->ptr;
+ const unsigned char *end = src + from->size;
+ unsigned char *dst;
+
+ GIT_UNUSED(self); GIT_UNUSED(payload); GIT_UNUSED(source);
+
+ /* verify that attribute path match worked as expected */
+ cl_assert_equal_i(
+ 0, git__strncmp("hero", git_filter_source_path(source), 4));
+
+ if (!from->size)
+ return 0;
+
+ cl_git_pass(git_str_grow(to, from->size));
+
+ dst = (unsigned char *)to->ptr + from->size - 1;
+
+ while (src < end)
+ *dst-- = *src++;
+
+ to->size = from->size;
+
+ return 0;
+}
+
+static int reverse_filter_stream(
+ git_writestream **out,
+ git_filter *self,
+ void **payload,
+ const git_filter_source *src,
+ git_writestream *next)
+{
+ return git_filter_buffered_stream_new(out,
+ self, reverse_filter_apply, NULL, payload, src, next);
+}
+
+static void reverse_filter_free(git_filter *f)
+{
+ git__free(f);
+}
+
+git_filter *create_reverse_filter(const char *attrs)
+{
+ git_filter *filter = git__calloc(1, sizeof(git_filter));
+ cl_assert(filter);
+
+ filter->version = GIT_FILTER_VERSION;
+ filter->attributes = attrs;
+ filter->shutdown = reverse_filter_free;
+ filter->stream = reverse_filter_stream;
+
+ return filter;
+}
+
+static int erroneous_filter_stream(
+ git_writestream **out,
+ git_filter *self,
+ void **payload,
+ const git_filter_source *src,
+ git_writestream *next)
+{
+ GIT_UNUSED(out);
+ GIT_UNUSED(self);
+ GIT_UNUSED(payload);
+ GIT_UNUSED(src);
+ GIT_UNUSED(next);
+ return -1;
+}
+
+static void erroneous_filter_free(git_filter *f)
+{
+ git__free(f);
+}
+
+git_filter *create_erroneous_filter(const char *attrs)
+{
+ git_filter *filter = git__calloc(1, sizeof(git_filter));
+ cl_assert(filter);
+
+ filter->version = GIT_FILTER_VERSION;
+ filter->attributes = attrs;
+ filter->stream = erroneous_filter_stream;
+ filter->shutdown = erroneous_filter_free;
+
+ return filter;
+}
diff --git a/tests/libgit2/filter/custom_helpers.h b/tests/libgit2/filter/custom_helpers.h
new file mode 100644
index 0000000..0936c58
--- /dev/null
+++ b/tests/libgit2/filter/custom_helpers.h
@@ -0,0 +1,19 @@
+#include "git2/sys/filter.h"
+
+extern git_filter *create_bitflip_filter(void);
+extern git_filter *create_reverse_filter(const char *attr);
+extern git_filter *create_erroneous_filter(const char *attr);
+
+extern int bitflip_filter_apply(
+ git_filter *self,
+ void **payload,
+ git_str *to,
+ const git_str *from,
+ const git_filter_source *source);
+
+extern int reverse_filter_apply(
+ git_filter *self,
+ void **payload,
+ git_str *to,
+ const git_str *from,
+ const git_filter_source *source);
diff --git a/tests/libgit2/filter/file.c b/tests/libgit2/filter/file.c
new file mode 100644
index 0000000..14b33ba
--- /dev/null
+++ b/tests/libgit2/filter/file.c
@@ -0,0 +1,98 @@
+#include "clar_libgit2.h"
+#include "git2/sys/filter.h"
+#include "crlf.h"
+
+static git_repository *g_repo = NULL;
+
+void test_filter_file__initialize(void)
+{
+ git_reference *head_ref;
+ git_commit *head;
+
+ g_repo = cl_git_sandbox_init("crlf");
+
+ cl_git_mkfile("crlf/.gitattributes",
+ "*.txt text\n*.bin binary\n*.crlf text eol=crlf\n*.lf text eol=lf\n");
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+ cl_git_pass(git_repository_head(&head_ref, g_repo));
+ cl_git_pass(git_reference_peel((git_object **)&head, head_ref, GIT_OBJECT_COMMIT));
+ cl_git_pass(git_reset(g_repo, (git_object *)head, GIT_RESET_HARD, NULL));
+
+ git_commit_free(head);
+ git_reference_free(head_ref);
+}
+
+void test_filter_file__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_filter_file__apply(void)
+{
+ git_filter_list *fl;
+ git_filter *crlf;
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_pass(git_filter_list_new(
+ &fl, g_repo, GIT_FILTER_TO_ODB, 0));
+
+ crlf = git_filter_lookup(GIT_FILTER_CRLF);
+ cl_assert(crlf != NULL);
+
+ cl_git_pass(git_filter_list_push(fl, crlf, NULL));
+
+ cl_git_pass(git_filter_list_apply_to_file(&buf, fl, g_repo, "all-crlf"));
+ cl_assert_equal_s("crlf\ncrlf\ncrlf\ncrlf\n", buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_filter_list_free(fl);
+}
+
+struct buf_writestream {
+ git_writestream base;
+ git_str buf;
+};
+
+static int buf_writestream_write(git_writestream *s, const char *buf, size_t len)
+{
+ struct buf_writestream *stream = (struct buf_writestream *)s;
+ return git_str_put(&stream->buf, buf, len);
+}
+
+static int buf_writestream_close(git_writestream *s)
+{
+ GIT_UNUSED(s);
+ return 0;
+}
+
+static void buf_writestream_free(git_writestream *s)
+{
+ struct buf_writestream *stream = (struct buf_writestream *)s;
+ git_str_dispose(&stream->buf);
+}
+
+void test_filter_file__apply_stream(void)
+{
+ git_filter_list *fl;
+ git_filter *crlf;
+ struct buf_writestream write_target = { {
+ buf_writestream_write,
+ buf_writestream_close,
+ buf_writestream_free } };
+
+ cl_git_pass(git_filter_list_new(
+ &fl, g_repo, GIT_FILTER_TO_ODB, 0));
+
+ crlf = git_filter_lookup(GIT_FILTER_CRLF);
+ cl_assert(crlf != NULL);
+
+ cl_git_pass(git_filter_list_push(fl, crlf, NULL));
+
+ cl_git_pass(git_filter_list_stream_file(fl, g_repo, "all-crlf", &write_target.base));
+ cl_assert_equal_s("crlf\ncrlf\ncrlf\ncrlf\n", write_target.buf.ptr);
+
+ git_filter_list_free(fl);
+ write_target.base.free(&write_target.base);
+}
diff --git a/tests/libgit2/filter/ident.c b/tests/libgit2/filter/ident.c
new file mode 100644
index 0000000..ed2e467
--- /dev/null
+++ b/tests/libgit2/filter/ident.c
@@ -0,0 +1,133 @@
+#include "clar_libgit2.h"
+#include "git2/sys/filter.h"
+
+static git_repository *g_repo = NULL;
+
+void test_filter_ident__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("crlf");
+}
+
+void test_filter_ident__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static void add_blob_and_filter(
+ const char *data,
+ git_filter_list *fl,
+ const char *expected)
+{
+ git_oid id;
+ git_blob *blob;
+ git_buf out = { 0 };
+
+ cl_git_mkfile("crlf/identtest", data);
+ cl_git_pass(git_blob_create_from_workdir(&id, g_repo, "identtest"));
+ cl_git_pass(git_blob_lookup(&blob, g_repo, &id));
+
+ cl_git_pass(git_filter_list_apply_to_blob(&out, fl, blob));
+
+ cl_assert_equal_s(expected, out.ptr);
+
+ git_blob_free(blob);
+ git_buf_dispose(&out);
+}
+
+void test_filter_ident__to_worktree(void)
+{
+ git_filter_list *fl;
+ git_filter *ident;
+
+ cl_git_pass(git_filter_list_new(
+ &fl, g_repo, GIT_FILTER_TO_WORKTREE, 0));
+
+ ident = git_filter_lookup(GIT_FILTER_IDENT);
+ cl_assert(ident != NULL);
+
+ cl_git_pass(git_filter_list_push(fl, ident, NULL));
+
+ add_blob_and_filter(
+ "Hello\n$Id$\nFun stuff\n", fl,
+ "Hello\n$Id: b69e2387aafcaf73c4de5b9ab59abe27fdadee30 $\nFun stuff\n");
+ add_blob_and_filter(
+ "Hello\n$Id: Junky$\nFun stuff\n", fl,
+ "Hello\n$Id: 45cd107a7102911cb2a7df08404674327fa050b9 $\nFun stuff\n");
+ add_blob_and_filter(
+ "$Id$\nAt the start\n", fl,
+ "$Id: b13415c767abc196fb95bd17070e8c1113e32160 $\nAt the start\n");
+ add_blob_and_filter(
+ "At the end\n$Id$", fl,
+ "At the end\n$Id: 1344925c6bc65b34c5a7b50f86bf688e48e9a272 $");
+ add_blob_and_filter(
+ "$Id$", fl,
+ "$Id: b3f5ebfb5843bc43ceecff6d4f26bb37c615beb1 $");
+ add_blob_and_filter(
+ "$Id: Some sort of junk goes here$", fl,
+ "$Id: ab2dd3853c7c9a4bff55aca2bea077a73c32ac06 $");
+
+ add_blob_and_filter("$Id: ", fl, "$Id: ");
+ add_blob_and_filter("$Id", fl, "$Id");
+ add_blob_and_filter("$I", fl, "$I");
+ add_blob_and_filter("Id$", fl, "Id$");
+
+ git_filter_list_free(fl);
+}
+
+void test_filter_ident__to_odb(void)
+{
+ git_filter_list *fl;
+ git_filter *ident;
+
+ cl_git_pass(git_filter_list_new(
+ &fl, g_repo, GIT_FILTER_TO_ODB, 0));
+
+ ident = git_filter_lookup(GIT_FILTER_IDENT);
+ cl_assert(ident != NULL);
+
+ cl_git_pass(git_filter_list_push(fl, ident, NULL));
+
+ add_blob_and_filter(
+ "Hello\n$Id$\nFun stuff\n",
+ fl, "Hello\n$Id$\nFun stuff\n");
+ add_blob_and_filter(
+ "Hello\n$Id: b69e2387aafcaf73c4de5b9ab59abe27fdadee30$\nFun stuff\n",
+ fl, "Hello\n$Id$\nFun stuff\n");
+ add_blob_and_filter(
+ "Hello\n$Id: Any junk you may have left here$\nFun stuff\n",
+ fl, "Hello\n$Id$\nFun stuff\n");
+ add_blob_and_filter(
+ "Hello\n$Id:$\nFun stuff\n",
+ fl, "Hello\n$Id$\nFun stuff\n");
+ add_blob_and_filter(
+ "Hello\n$Id:x$\nFun stuff\n",
+ fl, "Hello\n$Id$\nFun stuff\n");
+
+ add_blob_and_filter(
+ "$Id$\nAt the start\n", fl, "$Id$\nAt the start\n");
+ add_blob_and_filter(
+ "$Id: lots of random text that should be removed from here$\nAt the start\n", fl, "$Id$\nAt the start\n");
+ add_blob_and_filter(
+ "$Id: lots of random text that should not be removed without a terminator\nAt the start\n", fl, "$Id: lots of random text that should not be removed without a terminator\nAt the start\n");
+
+ add_blob_and_filter(
+ "At the end\n$Id$", fl, "At the end\n$Id$");
+ add_blob_and_filter(
+ "At the end\n$Id:$", fl, "At the end\n$Id$");
+ add_blob_and_filter(
+ "At the end\n$Id:asdfasdf$", fl, "At the end\n$Id$");
+ add_blob_and_filter(
+ "At the end\n$Id", fl, "At the end\n$Id");
+ add_blob_and_filter(
+ "At the end\n$IddI", fl, "At the end\n$IddI");
+
+ add_blob_and_filter("$Id$", fl, "$Id$");
+ add_blob_and_filter("$Id: any$", fl, "$Id$");
+ add_blob_and_filter("$Id: any long stuff goes here you see$", fl, "$Id$");
+ add_blob_and_filter("$Id: ", fl, "$Id: ");
+ add_blob_and_filter("$Id", fl, "$Id");
+ add_blob_and_filter("$I", fl, "$I");
+ add_blob_and_filter("Id$", fl, "Id$");
+
+ git_filter_list_free(fl);
+}
diff --git a/tests/libgit2/filter/query.c b/tests/libgit2/filter/query.c
new file mode 100644
index 0000000..429c104
--- /dev/null
+++ b/tests/libgit2/filter/query.c
@@ -0,0 +1,90 @@
+#include "clar_libgit2.h"
+#include "git2/sys/filter.h"
+#include "crlf.h"
+
+static git_repository *g_repo = NULL;
+
+void test_filter_query__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("crlf");
+
+ cl_git_mkfile("crlf/.gitattributes",
+ "*.txt text\n"
+ "*.bin binary\n"
+ "*.crlf text eol=crlf\n"
+ "*.lf text eol=lf\n"
+ "*.binident binary ident\n"
+ "*.ident text ident\n"
+ "*.identcrlf ident text eol=crlf\n"
+ "*.identlf ident text eol=lf\n"
+ "*.custom custom ident text\n");
+}
+
+void test_filter_query__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static int filter_for(const char *filename, const char *filter)
+{
+ git_filter_list *fl;
+ int filtered;
+
+ cl_git_pass(git_filter_list_load(
+ &fl, g_repo, NULL, filename, GIT_FILTER_TO_WORKTREE, 0));
+ filtered = git_filter_list_contains(fl, filter);
+ git_filter_list_free(fl);
+
+ return filtered;
+}
+
+void test_filter_query__filters(void)
+{
+ cl_assert_equal_i(1, filter_for("text.txt", "crlf"));
+ cl_assert_equal_i(0, filter_for("binary.bin", "crlf"));
+
+ cl_assert_equal_i(1, filter_for("foo.lf", "crlf"));
+ cl_assert_equal_i(0, filter_for("foo.lf", "ident"));
+
+ cl_assert_equal_i(1, filter_for("id.ident", "crlf"));
+ cl_assert_equal_i(1, filter_for("id.ident", "ident"));
+
+ cl_assert_equal_i(0, filter_for("id.binident", "crlf"));
+ cl_assert_equal_i(1, filter_for("id.binident", "ident"));
+}
+
+void test_filter_query__autocrlf_true_implies_crlf(void)
+{
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+ cl_assert_equal_i(1, filter_for("not_in_gitattributes", "crlf"));
+ cl_assert_equal_i(1, filter_for("foo.txt", "crlf"));
+ cl_assert_equal_i(0, filter_for("foo.bin", "crlf"));
+ cl_assert_equal_i(1, filter_for("foo.lf", "crlf"));
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", false);
+ cl_assert_equal_i(0, filter_for("not_in_gitattributes", "crlf"));
+ cl_assert_equal_i(1, filter_for("foo.txt", "crlf"));
+ cl_assert_equal_i(0, filter_for("foo.bin", "crlf"));
+ cl_assert_equal_i(1, filter_for("foo.lf", "crlf"));
+}
+
+void test_filter_query__unknown(void)
+{
+ cl_assert_equal_i(1, filter_for("foo.custom", "crlf"));
+ cl_assert_equal_i(1, filter_for("foo.custom", "ident"));
+ cl_assert_equal_i(0, filter_for("foo.custom", "custom"));
+}
+
+void test_filter_query__custom(void)
+{
+ git_filter custom = { GIT_FILTER_VERSION };
+
+ cl_git_pass(git_filter_register(
+ "custom", &custom, 42));
+
+ cl_assert_equal_i(1, filter_for("foo.custom", "crlf"));
+ cl_assert_equal_i(1, filter_for("foo.custom", "ident"));
+ cl_assert_equal_i(1, filter_for("foo.custom", "custom"));
+
+ git_filter_unregister("custom");
+}
diff --git a/tests/libgit2/filter/stream.c b/tests/libgit2/filter/stream.c
new file mode 100644
index 0000000..0f85f9c
--- /dev/null
+++ b/tests/libgit2/filter/stream.c
@@ -0,0 +1,215 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "blob.h"
+#include "filter.h"
+#include "git2/sys/filter.h"
+#include "git2/sys/repository.h"
+
+static git_repository *g_repo = NULL;
+
+static git_filter *create_compress_filter(void);
+static git_filter *compress_filter;
+
+void test_filter_stream__initialize(void)
+{
+ compress_filter = create_compress_filter();
+
+ cl_git_pass(git_filter_register("compress", compress_filter, 50));
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+}
+
+void test_filter_stream__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ g_repo = NULL;
+
+ git_filter_unregister("compress");
+ git__free(compress_filter);
+}
+
+#define CHUNKSIZE 10240
+
+struct compress_stream {
+ git_writestream parent;
+ git_writestream *next;
+ git_filter_mode_t mode;
+ char current;
+ size_t current_chunk;
+};
+
+static int compress_stream_write__deflated(struct compress_stream *stream, const char *buffer, size_t len)
+{
+ size_t idx = 0;
+
+ while (len > 0) {
+ size_t chunkremain, chunksize;
+
+ if (stream->current_chunk == 0)
+ stream->current = buffer[idx];
+
+ chunkremain = CHUNKSIZE - stream->current_chunk;
+ chunksize = min(chunkremain, len);
+
+ stream->current_chunk += chunksize;
+ len -= chunksize;
+ idx += chunksize;
+
+ if (stream->current_chunk == CHUNKSIZE) {
+ cl_git_pass(stream->next->write(stream->next, &stream->current, 1));
+ stream->current_chunk = 0;
+ }
+ }
+
+ return 0;
+}
+
+static int compress_stream_write__inflated(struct compress_stream *stream, const char *buffer, size_t len)
+{
+ char inflated[CHUNKSIZE];
+ size_t i, j;
+
+ for (i = 0; i < len; i++) {
+ for (j = 0; j < CHUNKSIZE; j++)
+ inflated[j] = buffer[i];
+
+ cl_git_pass(stream->next->write(stream->next, inflated, CHUNKSIZE));
+ }
+
+ return 0;
+}
+
+static int compress_stream_write(git_writestream *s, const char *buffer, size_t len)
+{
+ struct compress_stream *stream = (struct compress_stream *)s;
+
+ return (stream->mode == GIT_FILTER_TO_ODB) ?
+ compress_stream_write__deflated(stream, buffer, len) :
+ compress_stream_write__inflated(stream, buffer, len);
+}
+
+static int compress_stream_close(git_writestream *s)
+{
+ struct compress_stream *stream = (struct compress_stream *)s;
+ cl_assert_equal_i(0, stream->current_chunk);
+ stream->next->close(stream->next);
+ return 0;
+}
+
+static void compress_stream_free(git_writestream *stream)
+{
+ git__free(stream);
+}
+
+static int compress_filter_stream_init(
+ git_writestream **out,
+ git_filter *self,
+ void **payload,
+ const git_filter_source *src,
+ git_writestream *next)
+{
+ struct compress_stream *stream = git__calloc(1, sizeof(struct compress_stream));
+ cl_assert(stream);
+
+ GIT_UNUSED(self);
+ GIT_UNUSED(payload);
+
+ stream->parent.write = compress_stream_write;
+ stream->parent.close = compress_stream_close;
+ stream->parent.free = compress_stream_free;
+ stream->next = next;
+ stream->mode = git_filter_source_mode(src);
+
+ *out = (git_writestream *)stream;
+ return 0;
+}
+
+git_filter *create_compress_filter(void)
+{
+ git_filter *filter = git__calloc(1, sizeof(git_filter));
+ cl_assert(filter);
+
+ filter->version = GIT_FILTER_VERSION;
+ filter->attributes = "+compress";
+ filter->stream = compress_filter_stream_init;
+
+ return filter;
+}
+
+static void writefile(const char *filename, size_t numchunks)
+{
+ git_str path = GIT_STR_INIT;
+ char buf[CHUNKSIZE];
+ size_t i = 0, j = 0;
+ int fd;
+
+ cl_git_pass(git_str_joinpath(&path, "empty_standard_repo", filename));
+
+ fd = p_open(path.ptr, O_RDWR|O_CREAT, 0666);
+ cl_assert(fd >= 0);
+
+ for (i = 0; i < numchunks; i++) {
+ for (j = 0; j < CHUNKSIZE; j++) {
+ buf[j] = i % 256;
+ }
+
+ cl_git_pass(p_write(fd, buf, CHUNKSIZE));
+ }
+ p_close(fd);
+
+ git_str_dispose(&path);
+}
+
+static void test_stream(size_t numchunks)
+{
+ git_index *index;
+ const git_index_entry *entry;
+ git_blob *blob;
+ struct stat st;
+ git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_mkfile(
+ "empty_standard_repo/.gitattributes",
+ "* compress\n");
+
+ /* write a file to disk */
+ writefile("streamed_file", numchunks);
+
+ /* place it in the index */
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_add_bypath(index, "streamed_file"));
+ cl_git_pass(git_index_write(index));
+
+ /* ensure it was appropriately compressed */
+ cl_assert(entry = git_index_get_bypath(index, "streamed_file", 0));
+
+ cl_git_pass(git_blob_lookup(&blob, g_repo, &entry->id));
+ cl_assert_equal_i(numchunks, git_blob_rawsize(blob));
+
+ /* check the file back out */
+ cl_must_pass(p_unlink("empty_standard_repo/streamed_file"));
+ cl_git_pass(git_checkout_index(g_repo, index, &checkout_opts));
+
+ /* ensure it was decompressed */
+ cl_must_pass(p_stat("empty_standard_repo/streamed_file", &st));
+ cl_assert_equal_sz((numchunks * CHUNKSIZE), st.st_size);
+
+ git_index_free(index);
+ git_blob_free(blob);
+}
+
+/* write a 50KB file through the "compression" stream */
+void test_filter_stream__smallfile(void)
+{
+ test_stream(5);
+}
+
+/* optionally write a 500 MB file through the compression stream */
+void test_filter_stream__bigfile(void)
+{
+ if (!cl_is_env_set("GITTEST_INVASIVE_FS_SIZE"))
+ cl_skip();
+
+ test_stream(51200);
+}
diff --git a/tests/libgit2/filter/systemattrs.c b/tests/libgit2/filter/systemattrs.c
new file mode 100644
index 0000000..b687b4b
--- /dev/null
+++ b/tests/libgit2/filter/systemattrs.c
@@ -0,0 +1,85 @@
+#include "clar_libgit2.h"
+#include "crlf.h"
+#include "path.h"
+
+static git_repository *g_repo = NULL;
+static git_str system_attr_path = GIT_STR_INIT;
+
+void test_filter_systemattrs__initialize(void)
+{
+ git_buf system_path = GIT_BUF_INIT;
+
+ g_repo = cl_git_sandbox_init("crlf");
+ cl_must_pass(p_unlink("crlf/.gitattributes"));
+
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, &system_path));
+ cl_git_pass(git_str_joinpath(&system_attr_path,
+ system_path.ptr, "gitattributes"));
+
+ cl_git_mkfile(system_attr_path.ptr,
+ "*.txt text\n"
+ "*.bin binary\n"
+ "*.crlf text eol=crlf\n"
+ "*.lf text eol=lf\n");
+
+ git_buf_dispose(&system_path);
+}
+
+void test_filter_systemattrs__cleanup(void)
+{
+ cl_must_pass(p_unlink(system_attr_path.ptr));
+ git_str_dispose(&system_attr_path);
+
+ cl_git_sandbox_cleanup();
+}
+
+void test_filter_systemattrs__reads_system_attributes(void)
+{
+ git_blob *blob;
+ git_buf buf = { 0 };
+
+ cl_git_pass(git_revparse_single(
+ (git_object **)&blob, g_repo, "799770d")); /* all-lf */
+
+ cl_assert_equal_s(ALL_LF_TEXT_RAW, git_blob_rawcontent(blob));
+
+ cl_git_pass(git_blob_filter(&buf, blob, "file.bin", NULL));
+ cl_assert_equal_s(ALL_LF_TEXT_RAW, buf.ptr);
+
+ cl_git_pass(git_blob_filter(&buf, blob, "file.crlf", NULL));
+ cl_assert_equal_s(ALL_LF_TEXT_AS_CRLF, buf.ptr);
+
+ cl_git_pass(git_blob_filter(&buf, blob, "file.lf", NULL));
+ cl_assert_equal_s(ALL_LF_TEXT_AS_LF, buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_blob_free(blob);
+}
+
+void test_filter_systemattrs__disables_system_attributes(void)
+{
+ git_blob *blob;
+ git_buf buf = { 0 };
+ git_blob_filter_options opts = GIT_BLOB_FILTER_OPTIONS_INIT;
+
+ opts.flags |= GIT_BLOB_FILTER_NO_SYSTEM_ATTRIBUTES;
+
+ cl_git_pass(git_revparse_single(
+ (git_object **)&blob, g_repo, "799770d")); /* all-lf */
+
+ cl_assert_equal_s(ALL_LF_TEXT_RAW, git_blob_rawcontent(blob));
+
+ cl_git_pass(git_blob_filter(&buf, blob, "file.bin", &opts));
+ cl_assert_equal_s(ALL_LF_TEXT_RAW, buf.ptr);
+
+ /* No attributes mean these are all treated literally */
+ cl_git_pass(git_blob_filter(&buf, blob, "file.crlf", &opts));
+ cl_assert_equal_s(ALL_LF_TEXT_RAW, buf.ptr);
+
+ cl_git_pass(git_blob_filter(&buf, blob, "file.lf", &opts));
+ cl_assert_equal_s(ALL_LF_TEXT_RAW, buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_blob_free(blob);
+}
diff --git a/tests/libgit2/filter/wildcard.c b/tests/libgit2/filter/wildcard.c
new file mode 100644
index 0000000..ee61a8d
--- /dev/null
+++ b/tests/libgit2/filter/wildcard.c
@@ -0,0 +1,188 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "blob.h"
+#include "filter.h"
+#include "git2/sys/filter.h"
+#include "git2/sys/repository.h"
+#include "custom_helpers.h"
+
+static git_repository *g_repo = NULL;
+
+static git_filter *create_wildcard_filter(void);
+
+#define DATA_LEN 32
+
+static unsigned char input[] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+};
+
+static unsigned char reversed[] = {
+ 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18,
+ 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10,
+ 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+ 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00,
+};
+
+static unsigned char flipped[] = {
+ 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
+ 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0,
+ 0xef, 0xee, 0xed, 0xec, 0xeb, 0xea, 0xe9, 0xe8,
+ 0xe7, 0xe6, 0xe5, 0xe4, 0xe3, 0xe2, 0xe1, 0xe0,
+};
+
+void test_filter_wildcard__initialize(void)
+{
+ cl_git_pass(git_filter_register(
+ "wildcard", create_wildcard_filter(), GIT_FILTER_DRIVER_PRIORITY));
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_rewritefile(
+ "empty_standard_repo/.gitattributes",
+ "* binary\n"
+ "hero-flip-* filter=wcflip\n"
+ "hero-reverse-* filter=wcreverse\n"
+ "none-* filter=unregistered\n");
+}
+
+void test_filter_wildcard__cleanup(void)
+{
+ cl_git_pass(git_filter_unregister("wildcard"));
+
+ cl_git_sandbox_cleanup();
+ g_repo = NULL;
+}
+
+static int wildcard_filter_check(
+ git_filter *self,
+ void **payload,
+ const git_filter_source *src,
+ const char **attr_values)
+{
+ GIT_UNUSED(self);
+ GIT_UNUSED(src);
+
+ if (strcmp(attr_values[0], "wcflip") == 0 ||
+ strcmp(attr_values[0], "wcreverse") == 0) {
+ *payload = git__strdup(attr_values[0]);
+ GIT_ERROR_CHECK_ALLOC(*payload);
+ return 0;
+ }
+
+ return GIT_PASSTHROUGH;
+}
+
+static int wildcard_filter_apply(
+ git_filter *self,
+ void **payload,
+ git_str *to,
+ const git_str *from,
+ const git_filter_source *source)
+{
+ const char *filtername = *payload;
+
+ if (filtername && strcmp(filtername, "wcflip") == 0)
+ return bitflip_filter_apply(self, payload, to, from, source);
+ else if (filtername && strcmp(filtername, "wcreverse") == 0)
+ return reverse_filter_apply(self, payload, to, from, source);
+
+ cl_fail("Unexpected attribute");
+ return GIT_PASSTHROUGH;
+}
+
+static int wildcard_filter_stream(
+ git_writestream **out,
+ git_filter *self,
+ void **payload,
+ const git_filter_source *src,
+ git_writestream *next)
+{
+ return git_filter_buffered_stream_new(out,
+ self, wildcard_filter_apply, NULL, payload, src, next);
+}
+
+static void wildcard_filter_cleanup(git_filter *self, void *payload)
+{
+ GIT_UNUSED(self);
+ git__free(payload);
+}
+
+static void wildcard_filter_free(git_filter *f)
+{
+ git__free(f);
+}
+
+static git_filter *create_wildcard_filter(void)
+{
+ git_filter *filter = git__calloc(1, sizeof(git_filter));
+ cl_assert(filter);
+
+ filter->version = GIT_FILTER_VERSION;
+ filter->attributes = "filter=*";
+ filter->check = wildcard_filter_check;
+ filter->stream = wildcard_filter_stream;
+ filter->cleanup = wildcard_filter_cleanup;
+ filter->shutdown = wildcard_filter_free;
+
+ return filter;
+}
+
+void test_filter_wildcard__reverse(void)
+{
+ git_filter_list *fl;
+ git_buf out = GIT_BUF_INIT;
+
+ cl_git_pass(git_filter_list_load(
+ &fl, g_repo, NULL, "hero-reverse-foo", GIT_FILTER_TO_ODB, 0));
+
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, (char *)input, DATA_LEN));
+
+ cl_assert_equal_i(DATA_LEN, out.size);
+
+ cl_assert_equal_i(
+ 0, memcmp(reversed, out.ptr, out.size));
+
+ git_filter_list_free(fl);
+ git_buf_dispose(&out);
+}
+
+void test_filter_wildcard__flip(void)
+{
+ git_filter_list *fl;
+ git_buf out = GIT_BUF_INIT;
+
+ cl_git_pass(git_filter_list_load(
+ &fl, g_repo, NULL, "hero-flip-foo", GIT_FILTER_TO_ODB, 0));
+
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, (char *)input, DATA_LEN));
+
+ cl_assert_equal_i(DATA_LEN, out.size);
+
+ cl_assert_equal_i(
+ 0, memcmp(flipped, out.ptr, out.size));
+
+ git_filter_list_free(fl);
+ git_buf_dispose(&out);
+}
+
+void test_filter_wildcard__none(void)
+{
+ git_filter_list *fl;
+ git_buf out = GIT_BUF_INIT;
+
+ cl_git_pass(git_filter_list_load(
+ &fl, g_repo, NULL, "none-foo", GIT_FILTER_TO_ODB, 0));
+
+ cl_git_pass(git_filter_list_apply_to_buffer(&out, fl, (char *)input, DATA_LEN));
+
+ cl_assert_equal_i(DATA_LEN, out.size);
+
+ cl_assert_equal_i(
+ 0, memcmp(input, out.ptr, out.size));
+
+ git_filter_list_free(fl);
+ git_buf_dispose(&out);
+}
diff --git a/tests/libgit2/grafts/basic.c b/tests/libgit2/grafts/basic.c
new file mode 100644
index 0000000..30c87f9
--- /dev/null
+++ b/tests/libgit2/grafts/basic.c
@@ -0,0 +1,121 @@
+#include "clar_libgit2.h"
+
+#include "futils.h"
+#include "grafts.h"
+
+static git_repository *g_repo;
+
+void test_grafts_basic__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("grafted.git");
+}
+
+void test_grafts_basic__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_grafts_basic__graft_add(void)
+{
+ git_array_oid_t parents = GIT_ARRAY_INIT;
+ git_oid oid_src, *oid1;
+ git_commit_graft *graft;
+ git_grafts *grafts;
+
+ cl_git_pass(git_grafts_new(&grafts, GIT_OID_SHA1));
+
+ cl_assert(oid1 = git_array_alloc(parents));
+ cl_git_pass(git_oid__fromstr(&oid_src, "2f3053cbff8a4ca2f0666de364ddb734a28a31a9", GIT_OID_SHA1));
+ git_oid_cpy(oid1, &oid_src);
+
+ git_oid__fromstr(&oid_src, "f503807ffa920e407a600cfaee96b7152259acc7", GIT_OID_SHA1);
+ cl_git_pass(git_grafts_add(grafts, &oid_src, parents));
+ git_array_clear(parents);
+
+ cl_assert_equal_i(1, git_grafts_size(grafts));
+ cl_git_pass(git_grafts_get(&graft, grafts, &oid_src));
+ cl_assert_equal_s("f503807ffa920e407a600cfaee96b7152259acc7", git_oid_tostr_s(&graft->oid));
+ cl_assert_equal_i(1, git_array_size(graft->parents));
+ cl_assert_equal_s("2f3053cbff8a4ca2f0666de364ddb734a28a31a9", git_oid_tostr_s(git_array_get(graft->parents, 0)));
+
+ git_grafts_free(grafts);
+}
+
+void test_grafts_basic__grafted_revwalk(void)
+{
+ git_revwalk *w;
+ git_oid oids[10];
+ size_t i = 0;
+ git_commit *commit;
+
+ cl_git_pass(git_revwalk_new(&w, g_repo));
+ cl_git_pass(git_revwalk_push_ref(w, "refs/heads/branch"));
+
+ cl_git_pass(git_revwalk_next(&oids[i++], w));
+ cl_assert_equal_s(git_oid_tostr_s(&oids[0]), "8a00e91619098618be97c0d2ceabb05a2c58edd9");
+ cl_git_pass(git_revwalk_next(&oids[i++], w));
+ cl_assert_equal_s(git_oid_tostr_s(&oids[1]), "f503807ffa920e407a600cfaee96b7152259acc7");
+ cl_git_pass(git_revwalk_next(&oids[i++], w));
+ cl_assert_equal_s(git_oid_tostr_s(&oids[2]), "2f3053cbff8a4ca2f0666de364ddb734a28a31a9");
+
+ cl_git_fail_with(GIT_ITEROVER, git_revwalk_next(&oids[i++], w));
+
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &oids[0]));
+
+ cl_assert_equal_i(1, git_commit_parentcount(commit));
+
+ git_commit_free(commit);
+ git_revwalk_free(w);
+}
+
+void test_grafts_basic__grafted_objects(void)
+{
+ git_oid oid;
+ git_commit *commit;
+
+ cl_git_pass(git_oid__fromstr(&oid, "f503807ffa920e407a600cfaee96b7152259acc7", GIT_OID_SHA1));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &oid));
+ cl_assert_equal_i(1, git_commit_parentcount(commit));
+ git_commit_free(commit);
+
+ cl_git_pass(git_oid__fromstr(&oid, "0512adebd3782157f0d5c9b22b043f87b4aaff9e", GIT_OID_SHA1));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &oid));
+ cl_assert_equal_i(1, git_commit_parentcount(commit));
+ git_commit_free(commit);
+
+ cl_git_pass(git_oid__fromstr(&oid, "66cc22a015f6ca75b34c82d28f78ba663876bade", GIT_OID_SHA1));
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &oid));
+ cl_assert_equal_i(4, git_commit_parentcount(commit));
+ git_commit_free(commit);
+}
+
+void test_grafts_basic__grafted_merge_revwalk(void)
+{
+ git_revwalk *w;
+ git_oid oids[10];
+ size_t i = 0;
+
+ cl_git_pass(git_revwalk_new(&w, g_repo));
+ cl_git_pass(git_revwalk_push_ref(w, "refs/heads/bottom"));
+
+ cl_git_pass(git_revwalk_next(&oids[i++], w));
+ cl_assert_equal_s(git_oid_tostr_s(&oids[i - 1]), "66cc22a015f6ca75b34c82d28f78ba663876bade");
+ cl_git_pass(git_revwalk_next(&oids[i++], w));
+ cl_assert_equal_s(git_oid_tostr_s(&oids[i - 1]), "e414f42f4e6bc6934563a2349a8600f0ab68618e");
+ cl_git_pass(git_revwalk_next(&oids[i++], w));
+ cl_assert_equal_s(git_oid_tostr_s(&oids[i - 1]), "8a00e91619098618be97c0d2ceabb05a2c58edd9");
+ cl_git_pass(git_revwalk_next(&oids[i++], w));
+ cl_assert_equal_s(git_oid_tostr_s(&oids[i - 1]), "1c18e80a276611bb9b146590616bbc5aebdf2945");
+ cl_git_pass(git_revwalk_next(&oids[i++], w));
+ cl_assert_equal_s(git_oid_tostr_s(&oids[i - 1]), "d7224d49d6d5aff6ade596ed74f4bcd4f77b29e2");
+ cl_git_pass(git_revwalk_next(&oids[i++], w));
+ cl_assert_equal_s(git_oid_tostr_s(&oids[i - 1]), "0512adebd3782157f0d5c9b22b043f87b4aaff9e");
+ cl_git_pass(git_revwalk_next(&oids[i++], w));
+ cl_assert_equal_s(git_oid_tostr_s(&oids[i - 1]), "f503807ffa920e407a600cfaee96b7152259acc7");
+ cl_git_pass(git_revwalk_next(&oids[i++], w));
+ cl_assert_equal_s(git_oid_tostr_s(&oids[i - 1]), "2f3053cbff8a4ca2f0666de364ddb734a28a31a9");
+
+ cl_git_fail_with(GIT_ITEROVER, git_revwalk_next(&oids[i++], w));
+
+ git_revwalk_free(w);
+}
diff --git a/tests/libgit2/grafts/parse.c b/tests/libgit2/grafts/parse.c
new file mode 100644
index 0000000..3b0618a
--- /dev/null
+++ b/tests/libgit2/grafts/parse.c
@@ -0,0 +1,149 @@
+#include "clar_libgit2.h"
+
+#include "grafts.h"
+
+#define OID0 "c0368f9f9743e950e6cfe1f45a649f8a9dfcd97e"
+#define OID1 "cfc50a0db87ce908fb8a8c5b8f7b4ab96eee8643"
+#define OID2 "6914d97cd08b9edf5e855fca211c750fa82fd80a"
+#define OID3 "516521937d0e9ce9d0d836149a0702671f326b4a"
+#define OID4 "e2c29d67ef2f217650196f94c796f0532b8caad6"
+#define OID5 "79bcb936596cb50353fe7be28b7444e66e4a2842"
+#define OID6 "b9c54107d57c17dbcaf646c4d52f66eb9e69d23d"
+#define OID7 "9f8a746e9ad7b58cc840016bc3944d5ad262acb5"
+#define OID8 "392f4beef7d0d15b2bc5b1abe1a754eba0ec36da"
+
+#define OID_TRUNCATED "392f4beef7d0d15b2bc5b1abe1a754eba0ec36d"
+#define OID_NONHEX "9f8a746e9ax7b58cc840016bc3944d5ad262acb5"
+
+static git_grafts *grafts;
+
+void test_grafts_parse__initialize(void)
+{
+ cl_git_pass(git_grafts_new(&grafts, GIT_OID_SHA1));
+}
+
+void test_grafts_parse__cleanup(void)
+{
+ git_grafts_free(grafts);
+ grafts = NULL;
+}
+
+static void assert_parse_succeeds(git_grafts *grafts, const char *string, size_t n)
+{
+ cl_git_pass(git_grafts_parse(grafts, string, strlen(string)));
+ cl_assert_equal_i(git_grafts_size(grafts), n);
+}
+
+static void assert_parse_fails(git_grafts *grafts, const char *string)
+{
+ cl_git_fail(git_grafts_parse(grafts, string, strlen(string)));
+}
+
+static void assert_graft_contains(git_grafts *grafts, const char *graft, size_t n, ...)
+{
+ git_commit_graft *commit;
+ git_oid oid;
+ va_list ap;
+ size_t i = 0;
+
+ cl_git_pass(git_oid__fromstr(&oid, graft, GIT_OID_SHA1));
+ cl_git_pass(git_grafts_get(&commit, grafts, &oid));
+ cl_assert_equal_oid(&commit->oid, &oid);
+ cl_assert_equal_i(commit->parents.size, n);
+
+ va_start(ap, n);
+ while (i < n) {
+ cl_git_pass(git_oid__fromstr(&oid, va_arg(ap, const char *), GIT_OID_SHA1));
+ cl_assert_equal_oid(&commit->parents.ptr[i], &oid);
+ i++;
+ }
+ va_end(ap);
+}
+
+void test_grafts_parse__single_oid(void)
+{
+ assert_parse_succeeds(grafts, OID1, 1);
+ assert_graft_contains(grafts, OID1, 0);
+}
+
+void test_grafts_parse__single_oid_with_newline(void)
+{
+ assert_parse_succeeds(grafts, OID1 "\n", 1);
+ assert_graft_contains(grafts, OID1, 0);
+}
+
+void test_grafts_parse__multiple_oids(void)
+{
+ assert_parse_succeeds(grafts, OID1 "\n" OID2 "\n" OID3, 3);
+ assert_graft_contains(grafts, OID1, 0);
+ assert_graft_contains(grafts, OID2, 0);
+ assert_graft_contains(grafts, OID3, 0);
+}
+
+void test_grafts_parse__same_oid(void)
+{
+ assert_parse_succeeds(grafts, OID1 "\n" OID1, 1);
+ assert_graft_contains(grafts, OID1, 0);
+}
+
+void test_grafts_parse__oid_with_parent(void)
+{
+ assert_parse_succeeds(grafts, OID1 " " OID2, 1);
+ assert_graft_contains(grafts, OID1, 1, OID2);
+}
+
+void test_grafts_parse__oid_with_parent_and_newline(void)
+{
+ assert_parse_succeeds(grafts, OID1 " " OID2 "\n", 1);
+ assert_graft_contains(grafts, OID1, 1, OID2);
+}
+
+void test_grafts_parse__oid_with_multiple_parents(void)
+{
+ assert_parse_succeeds(grafts, OID1 " " OID2 " " OID3 " " OID4 " " OID5, 1);
+ assert_graft_contains(grafts, OID1, 4, OID2, OID3, OID4, OID5);
+}
+
+void test_grafts_parse__multiple_oids_with_multiple_parents(void)
+{
+ assert_parse_succeeds(grafts,
+ OID1 " " OID2 " " OID3 " " OID4 " " OID5 "\n"
+ OID6 " " OID7 " " OID8 "\n" , 2);
+ assert_graft_contains(grafts, OID1, 4, OID2, OID3, OID4, OID5);
+ assert_graft_contains(grafts, OID6, 2, OID7, OID8);
+}
+
+void test_grafts_parse__multiple_spaces_fails(void)
+{
+ assert_parse_fails(grafts, OID1 " " OID2);
+}
+
+void test_grafts_parse__trailing_space_fails(void)
+{
+ assert_parse_fails(grafts, OID1 " " OID2 " ");
+}
+
+void test_grafts_parse__invalid_character_inbetween_fails(void)
+{
+ assert_parse_fails(grafts, OID1 " x " OID2);
+}
+
+void test_grafts_parse__truncated_oid_fails(void)
+{
+ assert_parse_fails(grafts, OID_TRUNCATED);
+}
+
+void test_grafts_parse__truncated_parent_fails(void)
+{
+ assert_parse_fails(grafts, OID1 " " OID_TRUNCATED);
+}
+
+void test_grafts_parse__invalid_oid_fails(void)
+{
+ assert_parse_fails(grafts, OID_NONHEX);
+}
+
+void test_grafts_parse__invalid_parent_fails(void)
+{
+ assert_parse_fails(grafts, OID1 " " OID_NONHEX);
+}
diff --git a/tests/libgit2/grafts/shallow.c b/tests/libgit2/grafts/shallow.c
new file mode 100644
index 0000000..5911a26
--- /dev/null
+++ b/tests/libgit2/grafts/shallow.c
@@ -0,0 +1,134 @@
+#include "clar_libgit2.h"
+
+#include "futils.h"
+#include "grafts.h"
+#include "repository.h"
+
+static git_repository *g_repo;
+static git_oid g_shallow_oid;
+
+void test_grafts_shallow__initialize(void)
+{
+ cl_git_pass(git_oid__fromstr(&g_shallow_oid, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644", GIT_OID_SHA1));
+}
+
+void test_grafts_shallow__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_grafts_shallow__no_shallow_file(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo.git");
+ cl_assert_equal_i(0, git_repository_is_shallow(g_repo));
+}
+
+void test_grafts_shallow__empty_shallow_file(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo.git");
+ cl_git_mkfile("testrepo.git/shallow", "");
+ cl_assert_equal_i(0, git_repository_is_shallow(g_repo));
+}
+
+void test_grafts_shallow__shallow_repo(void)
+{
+ g_repo = cl_git_sandbox_init("shallow.git");
+ cl_assert_equal_i(1, git_repository_is_shallow(g_repo));
+}
+
+void test_grafts_shallow__clears_errors(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo.git");
+ cl_assert_equal_i(0, git_repository_is_shallow(g_repo));
+ cl_assert_equal_p(NULL, git_error_last());
+}
+
+void test_grafts_shallow__shallow_oids(void)
+{
+ git_commit_graft *graft;
+ git_grafts *grafts;
+
+ g_repo = cl_git_sandbox_init("shallow.git");
+
+ cl_git_pass(git_repository_shallow_grafts__weakptr(&grafts, g_repo));
+ cl_assert_equal_i(1, git_grafts_size(grafts));
+ cl_git_pass(git_grafts_get(&graft, grafts, &g_shallow_oid));
+}
+
+void test_grafts_shallow__cache_clearing(void)
+{
+ git_commit_graft *graft;
+ git_grafts *grafts;
+ git_oid tmp_oid;
+
+ cl_git_pass(git_oid__fromstr(&tmp_oid, "0000000000000000000000000000000000000000", GIT_OID_SHA1));
+ g_repo = cl_git_sandbox_init("shallow.git");
+ cl_git_pass(git_repository_shallow_grafts__weakptr(&grafts, g_repo));
+
+ cl_assert_equal_i(1, git_grafts_size(grafts));
+ cl_git_pass(git_grafts_get(&graft, grafts, &g_shallow_oid));
+
+ cl_git_mkfile("shallow.git/shallow",
+ "be3563ae3f795b2b4353bcce3a527ad0a4f7f644\n"
+ "0000000000000000000000000000000000000000\n"
+ );
+
+ cl_git_pass(git_grafts_refresh(grafts));
+ cl_assert_equal_i(2, git_grafts_size(grafts));
+ cl_git_pass(git_grafts_get(&graft, grafts, &g_shallow_oid));
+ cl_git_pass(git_grafts_get(&graft, grafts, &tmp_oid));
+
+ cl_git_pass(p_unlink("shallow.git/shallow"));
+ cl_git_pass(git_grafts_refresh(grafts));
+ cl_assert_equal_i(0, git_grafts_size(grafts));
+}
+
+void test_grafts_shallow__errors_on_borked(void)
+{
+ git_grafts *grafts;
+
+ g_repo = cl_git_sandbox_init("shallow.git");
+
+ cl_git_mkfile("shallow.git/shallow", "lolno");
+ cl_git_pass(git_repository_shallow_grafts__weakptr(&grafts, g_repo));
+ cl_git_fail(git_grafts_refresh(grafts));
+ cl_assert_equal_i(0, git_grafts_size(grafts));
+
+ cl_git_mkfile("shallow.git/shallow", "lolno\n");
+ cl_git_pass(git_repository_shallow_grafts__weakptr(&grafts, g_repo));
+ cl_git_fail(git_grafts_refresh(grafts));
+ cl_assert_equal_i(0, git_grafts_size(grafts));
+}
+
+void test_grafts_shallow__revwalk_behavior(void)
+{
+ git_revwalk *w;
+ git_oid oid_1, oid_2, oid_3;
+
+ g_repo = cl_git_sandbox_init("shallow.git");
+
+ cl_git_pass(git_revwalk_new(&w, g_repo));
+ cl_git_pass(git_revwalk_push_head(w));
+
+ cl_git_pass(git_revwalk_next(&oid_1, w)); // a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+ cl_git_pass(git_revwalk_next(&oid_2, w)); // be3563ae3f795b2b4353bcce3a527ad0a4f7f644
+ cl_git_fail_with(GIT_ITEROVER, git_revwalk_next(&oid_3, w));
+
+ cl_assert_equal_s(git_oid_tostr_s(&oid_1), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ cl_assert_equal_s(git_oid_tostr_s(&oid_2), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+
+ git_revwalk_free(w);
+}
+
+void test_grafts_shallow__grafted_object(void)
+{
+ git_commit *commit;
+
+ g_repo = cl_git_sandbox_init("shallow.git");
+
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &g_shallow_oid));
+
+ cl_assert_equal_i(0, git_commit_parentcount(commit));
+
+ git_commit_free(commit);
+}
diff --git a/tests/libgit2/graph/ahead_behind.c b/tests/libgit2/graph/ahead_behind.c
new file mode 100644
index 0000000..dd828c5
--- /dev/null
+++ b/tests/libgit2/graph/ahead_behind.c
@@ -0,0 +1,58 @@
+#include "clar_libgit2.h"
+
+static git_repository *_repo;
+static git_commit *commit;
+static size_t ahead;
+static size_t behind;
+
+void test_graph_ahead_behind__initialize(void)
+{
+ git_oid oid;
+ cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git")));
+
+ cl_git_pass(git_oid__fromstr(&oid, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644", GIT_OID_SHA1));
+ cl_git_pass(git_commit_lookup(&commit, _repo, &oid));
+}
+
+void test_graph_ahead_behind__cleanup(void)
+{
+ git_commit_free(commit);
+ commit = NULL;
+
+ git_repository_free(_repo);
+ _repo = NULL;
+}
+
+void test_graph_ahead_behind__returns_correct_result(void)
+{
+ git_oid oid;
+ git_oid oid2;
+ git_commit *other;
+
+ cl_git_pass(git_oid__fromstr(&oid, "e90810b8df3e80c413d903f631643c716887138d", GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&oid2, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644", GIT_OID_SHA1));
+
+ cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo, &oid, &oid2));
+ cl_assert_equal_sz(2, ahead);
+ cl_assert_equal_sz(6, behind);
+
+ cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo, git_commit_id(commit), git_commit_id(commit)));
+ cl_assert_equal_sz(ahead, behind);
+ cl_git_pass(git_commit_nth_gen_ancestor(&other, commit, 1));
+
+ cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo, git_commit_id(commit), git_commit_id(other)));
+ cl_assert_equal_sz(ahead, behind + 2);
+ cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo, git_commit_id(other), git_commit_id(commit)));
+ cl_assert_equal_sz(ahead + 2, behind);
+
+ git_commit_free(other);
+
+ cl_git_pass(git_commit_nth_gen_ancestor(&other, commit, 3));
+
+ cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo, git_commit_id(commit), git_commit_id(other)));
+ cl_assert_equal_sz(ahead, behind + 4);
+ cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo, git_commit_id(other), git_commit_id(commit)));
+ cl_assert_equal_sz(ahead + 4, behind);
+
+ git_commit_free(other);
+}
diff --git a/tests/libgit2/graph/commitgraph.c b/tests/libgit2/graph/commitgraph.c
new file mode 100644
index 0000000..53869d6
--- /dev/null
+++ b/tests/libgit2/graph/commitgraph.c
@@ -0,0 +1,180 @@
+#include "clar_libgit2.h"
+
+#include <git2.h>
+#include <git2/sys/commit_graph.h>
+
+#include "commit_graph.h"
+#include "futils.h"
+
+void test_graph_commitgraph__parse(void)
+{
+ git_repository *repo;
+ struct git_commit_graph_file *file;
+ struct git_commit_graph_entry e, parent;
+ git_oid id;
+ git_str commit_graph_path = GIT_STR_INIT;
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+ cl_git_pass(git_str_joinpath(&commit_graph_path, git_repository_path(repo), "objects/info/commit-graph"));
+ cl_git_pass(git_commit_graph_file_open(&file, git_str_cstr(&commit_graph_path), GIT_OID_SHA1));
+ cl_assert_equal_i(git_commit_graph_file_needs_refresh(file, git_str_cstr(&commit_graph_path)), 0);
+
+ cl_git_pass(git_oid__fromstr(&id, "5001298e0c09ad9c34e4249bc5801c75e9754fa5", GIT_OID_SHA1));
+ cl_git_pass(git_commit_graph_entry_find(&e, file, &id, GIT_OID_SHA1_HEXSIZE));
+ cl_assert_equal_oid(&e.sha1, &id);
+ cl_git_pass(git_oid__fromstr(&id, "418382dff1ffb8bdfba833f4d8bbcde58b1e7f47", GIT_OID_SHA1));
+ cl_assert_equal_oid(&e.tree_oid, &id);
+ cl_assert_equal_i(e.generation, 1);
+ cl_assert_equal_i(e.commit_time, UINT64_C(1273610423));
+ cl_assert_equal_i(e.parent_count, 0);
+
+ cl_git_pass(git_oid__fromstr(&id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644", GIT_OID_SHA1));
+ cl_git_pass(git_commit_graph_entry_find(&e, file, &id, GIT_OID_SHA1_HEXSIZE));
+ cl_assert_equal_oid(&e.sha1, &id);
+ cl_assert_equal_i(e.generation, 5);
+ cl_assert_equal_i(e.commit_time, UINT64_C(1274813907));
+ cl_assert_equal_i(e.parent_count, 2);
+
+ cl_git_pass(git_oid__fromstr(&id, "9fd738e8f7967c078dceed8190330fc8648ee56a", GIT_OID_SHA1));
+ cl_git_pass(git_commit_graph_entry_parent(&parent, file, &e, 0));
+ cl_assert_equal_oid(&parent.sha1, &id);
+ cl_assert_equal_i(parent.generation, 4);
+
+ cl_git_pass(git_oid__fromstr(&id, "c47800c7266a2be04c571c04d5a6614691ea99bd", GIT_OID_SHA1));
+ cl_git_pass(git_commit_graph_entry_parent(&parent, file, &e, 1));
+ cl_assert_equal_oid(&parent.sha1, &id);
+ cl_assert_equal_i(parent.generation, 3);
+
+ git_commit_graph_file_free(file);
+ git_repository_free(repo);
+ git_str_dispose(&commit_graph_path);
+}
+
+void test_graph_commitgraph__parse_octopus_merge(void)
+{
+ git_repository *repo;
+ struct git_commit_graph_file *file;
+ struct git_commit_graph_entry e, parent;
+ git_oid id;
+ git_str commit_graph_path = GIT_STR_INIT;
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("merge-recursive/.gitted")));
+ cl_git_pass(git_str_joinpath(&commit_graph_path, git_repository_path(repo), "objects/info/commit-graph"));
+ cl_git_pass(git_commit_graph_file_open(&file, git_str_cstr(&commit_graph_path), GIT_OID_SHA1));
+
+ cl_git_pass(git_oid__fromstr(&id, "d71c24b3b113fd1d1909998c5bfe33b86a65ee03", GIT_OID_SHA1));
+ cl_git_pass(git_commit_graph_entry_find(&e, file, &id, GIT_OID_SHA1_HEXSIZE));
+ cl_assert_equal_oid(&e.sha1, &id);
+ cl_git_pass(git_oid__fromstr(&id, "348f16ffaeb73f319a75cec5b16a0a47d2d5e27c", GIT_OID_SHA1));
+ cl_assert_equal_oid(&e.tree_oid, &id);
+ cl_assert_equal_i(e.generation, 7);
+ cl_assert_equal_i(e.commit_time, UINT64_C(1447083009));
+ cl_assert_equal_i(e.parent_count, 3);
+
+ cl_git_pass(git_oid__fromstr(&id, "ad2ace9e15f66b3d1138922e6ffdc3ea3f967fa6", GIT_OID_SHA1));
+ cl_git_pass(git_commit_graph_entry_parent(&parent, file, &e, 0));
+ cl_assert_equal_oid(&parent.sha1, &id);
+ cl_assert_equal_i(parent.generation, 6);
+
+ cl_git_pass(git_oid__fromstr(&id, "483065df53c0f4a02cdc6b2910b05d388fc17ffb", GIT_OID_SHA1));
+ cl_git_pass(git_commit_graph_entry_parent(&parent, file, &e, 1));
+ cl_assert_equal_oid(&parent.sha1, &id);
+ cl_assert_equal_i(parent.generation, 2);
+
+ cl_git_pass(git_oid__fromstr(&id, "815b5a1c80ca749d705c7aa0cb294a00cbedd340", GIT_OID_SHA1));
+ cl_git_pass(git_commit_graph_entry_parent(&parent, file, &e, 2));
+ cl_assert_equal_oid(&parent.sha1, &id);
+ cl_assert_equal_i(parent.generation, 6);
+
+ git_commit_graph_file_free(file);
+ git_repository_free(repo);
+ git_str_dispose(&commit_graph_path);
+}
+
+void test_graph_commitgraph__writer(void)
+{
+ git_repository *repo;
+ git_commit_graph_writer *w = NULL;
+ git_revwalk *walk;
+ git_commit_graph_writer_options opts = GIT_COMMIT_GRAPH_WRITER_OPTIONS_INIT;
+ git_buf cgraph = GIT_BUF_INIT;
+ git_str expected_cgraph = GIT_STR_INIT, path = GIT_STR_INIT;
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+
+ cl_git_pass(git_str_joinpath(&path, git_repository_path(repo), "objects/info"));
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_commit_graph_writer_new(&w, git_str_cstr(&path), GIT_OID_SHA1));
+#else
+ cl_git_pass(git_commit_graph_writer_new(&w, git_str_cstr(&path)));
+#endif
+
+ /* This is equivalent to `git commit-graph write --reachable`. */
+ cl_git_pass(git_revwalk_new(&walk, repo));
+ cl_git_pass(git_revwalk_push_glob(walk, "refs/*"));
+ cl_git_pass(git_commit_graph_writer_add_revwalk(w, walk));
+ git_revwalk_free(walk);
+
+ cl_git_pass(git_commit_graph_writer_dump(&cgraph, w, &opts));
+ cl_git_pass(git_str_joinpath(&path, git_repository_path(repo), "objects/info/commit-graph"));
+ cl_git_pass(git_futils_readbuffer(&expected_cgraph, git_str_cstr(&path)));
+
+ cl_assert_equal_i(cgraph.size, git_str_len(&expected_cgraph));
+ cl_assert_equal_i(memcmp(cgraph.ptr, git_str_cstr(&expected_cgraph), cgraph.size), 0);
+
+ git_buf_dispose(&cgraph);
+ git_str_dispose(&expected_cgraph);
+ git_str_dispose(&path);
+ git_commit_graph_writer_free(w);
+ git_repository_free(repo);
+}
+
+void test_graph_commitgraph__validate(void)
+{
+ git_repository *repo;
+ struct git_commit_graph *cgraph;
+ git_str objects_dir = GIT_STR_INIT;
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+ cl_git_pass(git_str_joinpath(&objects_dir, git_repository_path(repo), "objects"));
+
+ /* git_commit_graph_open() calls git_commit_graph_validate() */
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_commit_graph_open(&cgraph, git_str_cstr(&objects_dir), GIT_OID_SHA1));
+#else
+ cl_git_pass(git_commit_graph_open(&cgraph, git_str_cstr(&objects_dir)));
+#endif
+
+ git_commit_graph_free(cgraph);
+ git_str_dispose(&objects_dir);
+ git_repository_free(repo);
+}
+
+void test_graph_commitgraph__validate_corrupt(void)
+{
+ git_repository *repo;
+ struct git_commit_graph *cgraph;
+ int fd = -1;
+
+ cl_fixture_sandbox("testrepo.git");
+ cl_git_pass(git_repository_open(&repo, cl_git_sandbox_path(1, "testrepo.git", NULL)));
+
+ /* corrupt commit graph checksum at the end of the file */
+ cl_assert((fd = p_open(cl_git_sandbox_path(0, "testrepo.git", "objects", "info", "commit-graph", NULL), O_WRONLY)) > 0);
+ cl_assert(p_lseek(fd, -5, SEEK_END) > 0);
+ cl_must_pass(p_write(fd, "\0\0", 2));
+ cl_must_pass(p_close(fd));
+
+ /* git_commit_graph_open() calls git_commit_graph_validate() */
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_fail(git_commit_graph_open(&cgraph, cl_git_sandbox_path(1, "testrepo.git", "objects", NULL), GIT_OID_SHA1));
+#else
+ cl_git_fail(git_commit_graph_open(&cgraph, cl_git_sandbox_path(1, "testrepo.git", "objects", NULL)));
+#endif
+
+ git_commit_graph_free(cgraph);
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("testrepo.git");
+}
diff --git a/tests/libgit2/graph/descendant_of.c b/tests/libgit2/graph/descendant_of.c
new file mode 100644
index 0000000..b6dffae
--- /dev/null
+++ b/tests/libgit2/graph/descendant_of.c
@@ -0,0 +1,55 @@
+#include "clar_libgit2.h"
+
+static git_repository *_repo;
+static git_commit *commit;
+
+void test_graph_descendant_of__initialize(void)
+{
+ git_oid oid;
+
+ cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git")));
+
+ git_oid__fromstr(&oid, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, _repo, &oid));
+}
+
+void test_graph_descendant_of__cleanup(void)
+{
+ git_commit_free(commit);
+ commit = NULL;
+
+ git_repository_free(_repo);
+ _repo = NULL;
+}
+
+void test_graph_descendant_of__returns_correct_result(void)
+{
+ git_commit *other;
+
+ cl_assert_equal_i(0, git_graph_descendant_of(_repo, git_commit_id(commit), git_commit_id(commit)));
+
+
+ cl_git_pass(git_commit_nth_gen_ancestor(&other, commit, 1));
+
+ cl_assert_equal_i(1, git_graph_descendant_of(_repo, git_commit_id(commit), git_commit_id(other)));
+ cl_assert_equal_i(0, git_graph_descendant_of(_repo, git_commit_id(other), git_commit_id(commit)));
+
+ git_commit_free(other);
+
+
+ cl_git_pass(git_commit_nth_gen_ancestor(&other, commit, 3));
+
+ cl_assert_equal_i(1, git_graph_descendant_of(_repo, git_commit_id(commit), git_commit_id(other)));
+ cl_assert_equal_i(0, git_graph_descendant_of(_repo, git_commit_id(other), git_commit_id(commit)));
+
+ git_commit_free(other);
+
+}
+
+void test_graph_descendant_of__nopath(void)
+{
+ git_oid oid;
+
+ git_oid__fromstr(&oid, "e90810b8df3e80c413d903f631643c716887138d", GIT_OID_SHA1);
+ cl_assert_equal_i(0, git_graph_descendant_of(_repo, git_commit_id(commit), &oid));
+}
diff --git a/tests/libgit2/graph/reachable_from_any.c b/tests/libgit2/graph/reachable_from_any.c
new file mode 100644
index 0000000..8e1c238
--- /dev/null
+++ b/tests/libgit2/graph/reachable_from_any.c
@@ -0,0 +1,236 @@
+#include "clar_libgit2.h"
+
+#include <git2.h>
+
+#include "commit_graph.h"
+#include "bitvec.h"
+#include "vector.h"
+
+static git_repository *repo;
+
+#define TEST_REPO_PATH "merge-recursive"
+
+void test_graph_reachable_from_any__initialize(void)
+{
+ git_oid oid;
+ git_commit *commit;
+
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+
+ git_oid__fromstr(&oid, "539bd011c4822c560c1d17cab095006b7a10f707", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+ cl_git_pass(git_reset(repo, (git_object *)commit, GIT_RESET_HARD, NULL));
+ git_commit_free(commit);
+}
+
+void test_graph_reachable_from_any__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_graph_reachable_from_any__returns_correct_result(void)
+{
+ git_object *branchA1, *branchA2, *branchB1, *branchB2, *branchC1, *branchC2, *branchH1,
+ *branchH2;
+ git_oid descendants[7];
+
+ cl_git_pass(git_revparse_single(&branchA1, repo, "branchA-1"));
+ cl_git_pass(git_revparse_single(&branchA2, repo, "branchA-2"));
+ cl_git_pass(git_revparse_single(&branchB1, repo, "branchB-1"));
+ cl_git_pass(git_revparse_single(&branchB2, repo, "branchB-2"));
+ cl_git_pass(git_revparse_single(&branchC1, repo, "branchC-1"));
+ cl_git_pass(git_revparse_single(&branchC2, repo, "branchC-2"));
+ cl_git_pass(git_revparse_single(&branchH1, repo, "branchH-1"));
+ cl_git_pass(git_revparse_single(&branchH2, repo, "branchH-2"));
+
+ cl_assert_equal_i(
+ git_graph_reachable_from_any(
+ repo, git_object_id(branchH1), git_object_id(branchA1), 1),
+ 0);
+ cl_assert_equal_i(
+ git_graph_reachable_from_any(
+ repo, git_object_id(branchH1), git_object_id(branchA2), 1),
+ 0);
+
+ cl_git_pass(git_oid_cpy(&descendants[0], git_object_id(branchA1)));
+ cl_git_pass(git_oid_cpy(&descendants[1], git_object_id(branchA2)));
+ cl_git_pass(git_oid_cpy(&descendants[2], git_object_id(branchB1)));
+ cl_git_pass(git_oid_cpy(&descendants[3], git_object_id(branchB2)));
+ cl_git_pass(git_oid_cpy(&descendants[4], git_object_id(branchC1)));
+ cl_git_pass(git_oid_cpy(&descendants[5], git_object_id(branchC2)));
+ cl_git_pass(git_oid_cpy(&descendants[6], git_object_id(branchH2)));
+ cl_assert_equal_i(
+ git_graph_reachable_from_any(repo, git_object_id(branchH2), descendants, 6),
+ 0);
+ cl_assert_equal_i(
+ git_graph_reachable_from_any(repo, git_object_id(branchH2), descendants, 7),
+ 1);
+
+ git_object_free(branchA1);
+ git_object_free(branchA2);
+ git_object_free(branchB1);
+ git_object_free(branchB2);
+ git_object_free(branchC1);
+ git_object_free(branchC2);
+ git_object_free(branchH1);
+ git_object_free(branchH2);
+}
+
+struct exhaustive_state {
+ git_odb *db;
+ git_vector commits;
+};
+
+/** Get all commits from the repository. */
+static int exhaustive_commits(const git_oid *id, void *payload)
+{
+ struct exhaustive_state *mc = (struct exhaustive_state *)payload;
+ size_t header_len;
+ git_object_t header_type;
+ int error = 0;
+
+ error = git_odb_read_header(&header_len, &header_type, mc->db, id);
+ if (error < 0)
+ return error;
+
+ if (header_type == GIT_OBJECT_COMMIT) {
+ git_commit *commit = NULL;
+
+ cl_git_pass(git_commit_lookup(&commit, repo, id));
+ cl_git_pass(git_vector_insert(&mc->commits, commit));
+ }
+
+ return 0;
+}
+
+/** Compare the `git_oid`s of two `git_commit` objects. */
+static int commit_id_cmp(const void *a, const void *b)
+{
+ return git_oid_cmp(
+ git_commit_id((const git_commit *)a), git_commit_id((const git_commit *)b));
+}
+
+/** Find a `git_commit` whose ID matches the provided `git_oid` key. */
+static int id_commit_id_cmp(const void *key, const void *commit)
+{
+ return git_oid_cmp((const git_oid *)key, git_commit_id((const git_commit *)commit));
+}
+
+void test_graph_reachable_from_any__exhaustive(void)
+{
+ struct exhaustive_state mc = {
+ .db = NULL,
+ .commits = GIT_VECTOR_INIT,
+ };
+ size_t child_idx, commit_count;
+ size_t n_descendants;
+ git_commit *child_commit;
+ git_bitvec reachable;
+
+ cl_git_pass(git_repository_odb(&mc.db, repo));
+ cl_git_pass(git_odb_foreach(mc.db, &exhaustive_commits, &mc));
+ git_vector_set_cmp(&mc.commits, commit_id_cmp);
+ git_vector_sort(&mc.commits);
+ cl_git_pass(git_bitvec_init(
+ &reachable,
+ git_vector_length(&mc.commits) * git_vector_length(&mc.commits)));
+
+ commit_count = git_vector_length(&mc.commits);
+ git_vector_foreach (&mc.commits, child_idx, child_commit) {
+ unsigned int parent_i;
+
+ /* We treat each commit as being able to reach itself. */
+ git_bitvec_set(&reachable, child_idx * commit_count + child_idx, true);
+
+ for (parent_i = 0; parent_i < git_commit_parentcount(child_commit); ++parent_i) {
+ size_t parent_idx = -1;
+ cl_git_pass(git_vector_bsearch2(
+ &parent_idx,
+ &mc.commits,
+ id_commit_id_cmp,
+ git_commit_parent_id(child_commit, parent_i)));
+
+ /* We have established that parent_idx is reachable from child_idx */
+ git_bitvec_set(&reachable, parent_idx * commit_count + child_idx, true);
+ }
+ }
+
+ /* Floyd-Warshall */
+ {
+ size_t i, j, k;
+ for (k = 0; k < commit_count; ++k) {
+ for (i = 0; i < commit_count; ++i) {
+ if (!git_bitvec_get(&reachable, i * commit_count + k))
+ continue;
+ for (j = 0; j < commit_count; ++j) {
+ if (!git_bitvec_get(&reachable, k * commit_count + j))
+ continue;
+ git_bitvec_set(&reachable, i * commit_count + j, true);
+ }
+ }
+ }
+ }
+
+ /* Try 1000 subsets of 1 through 10 entries each. */
+ srand(0x223ddc4b);
+ for (n_descendants = 1; n_descendants < 10; ++n_descendants) {
+ size_t test_iteration;
+ git_oid descendants[10];
+
+ for (test_iteration = 0; test_iteration < 1000; ++test_iteration) {
+ size_t descendant_i;
+ size_t child_idx, parent_idx;
+ int expected_reachable = false, actual_reachable;
+ git_commit *child_commit, *parent_commit;
+
+ parent_idx = rand() % commit_count;
+ parent_commit = (git_commit *)git_vector_get(&mc.commits, parent_idx);
+ for (descendant_i = 0; descendant_i < n_descendants; ++descendant_i) {
+ child_idx = rand() % commit_count;
+ child_commit = (git_commit *)git_vector_get(&mc.commits, child_idx);
+ expected_reachable |= git_bitvec_get(
+ &reachable, parent_idx * commit_count + child_idx);
+ git_oid_cpy(&descendants[descendant_i],
+ git_commit_id(child_commit));
+ }
+
+ actual_reachable = git_graph_reachable_from_any(
+ repo,
+ git_commit_id(parent_commit),
+ descendants,
+ n_descendants);
+ if (actual_reachable != expected_reachable) {
+ git_str error_message_buf = GIT_STR_INIT;
+ char parent_oidbuf[9] = {0}, child_oidbuf[9] = {0};
+
+ cl_git_pass(git_oid_nfmt(
+ parent_oidbuf, 8, git_commit_id(parent_commit)));
+ git_str_printf(&error_message_buf,
+ "git_graph_reachable_from_any(\"%s\", %zu, "
+ "{",
+ parent_oidbuf,
+ n_descendants);
+ for (descendant_i = 0; descendant_i < n_descendants;
+ ++descendant_i) {
+ cl_git_pass(
+ git_oid_nfmt(child_oidbuf,
+ 8,
+ &descendants[descendant_i]));
+ git_str_printf(&error_message_buf, " \"%s\"", child_oidbuf);
+ }
+ git_str_printf(&error_message_buf,
+ " }) = %d, expected = %d",
+ actual_reachable,
+ expected_reachable);
+ cl_check_(actual_reachable == expected_reachable,
+ git_str_cstr(&error_message_buf));
+ }
+ }
+ }
+
+ git_vector_foreach (&mc.commits, child_idx, child_commit)
+ git_commit_free(child_commit);
+ git_bitvec_free(&reachable);
+ git_vector_free(&mc.commits);
+ git_odb_free(mc.db);
+}
diff --git a/tests/libgit2/ignore/path.c b/tests/libgit2/ignore/path.c
new file mode 100644
index 0000000..17f28bc
--- /dev/null
+++ b/tests/libgit2/ignore/path.c
@@ -0,0 +1,589 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "path.h"
+#include "futils.h"
+
+static git_repository *g_repo = NULL;
+
+void test_ignore_path__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("attr");
+}
+
+void test_ignore_path__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ g_repo = NULL;
+}
+
+static void assert_is_ignored_(
+ bool expected, const char *filepath,
+ const char *file, const char *func, int line)
+{
+ int is_ignored = 0;
+
+ cl_git_expect(
+ git_ignore_path_is_ignored(&is_ignored, g_repo, filepath), 0, file, func, line);
+
+ clar__assert_equal(
+ file, func, line, "expected != is_ignored", 1, "%d",
+ (int)(expected != 0), (int)(is_ignored != 0));
+}
+#define assert_is_ignored(expected, filepath) \
+ assert_is_ignored_(expected, filepath, __FILE__, __func__, __LINE__)
+
+void test_ignore_path__honor_temporary_rules(void)
+{
+ cl_git_rewritefile("attr/.gitignore", "/NewFolder\n/NewFolder/NewFolder");
+
+ assert_is_ignored(false, "File.txt");
+ assert_is_ignored(true, "NewFolder");
+ assert_is_ignored(true, "NewFolder/NewFolder");
+ assert_is_ignored(true, "NewFolder/NewFolder/File.txt");
+}
+
+void test_ignore_path__allow_root(void)
+{
+ cl_git_rewritefile("attr/.gitignore", "/");
+
+ assert_is_ignored(false, "File.txt");
+ assert_is_ignored(false, "NewFolder");
+ assert_is_ignored(false, "NewFolder/NewFolder");
+ assert_is_ignored(false, "NewFolder/NewFolder/File.txt");
+}
+
+void test_ignore_path__ignore_space(void)
+{
+ cl_git_rewritefile("attr/.gitignore", "/\n\n/NewFolder \n/NewFolder/NewFolder");
+
+ assert_is_ignored(false, "File.txt");
+ assert_is_ignored(true, "NewFolder");
+ assert_is_ignored(true, "NewFolder/NewFolder");
+ assert_is_ignored(true, "NewFolder/NewFolder/File.txt");
+}
+
+void test_ignore_path__intermittent_space(void)
+{
+ cl_git_rewritefile("attr/.gitignore", "foo bar\n");
+
+ assert_is_ignored(false, "foo");
+ assert_is_ignored(false, "bar");
+ assert_is_ignored(true, "foo bar");
+}
+
+void test_ignore_path__trailing_space(void)
+{
+ cl_git_rewritefile(
+ "attr/.gitignore",
+ "foo \n"
+ "bar \n"
+ );
+
+ assert_is_ignored(true, "foo");
+ assert_is_ignored(false, "foo ");
+ assert_is_ignored(true, "bar");
+ assert_is_ignored(false, "bar ");
+ assert_is_ignored(false, "bar ");
+}
+
+void test_ignore_path__escaped_trailing_spaces(void)
+{
+ cl_git_rewritefile(
+ "attr/.gitignore",
+ "foo\\ \n"
+ "bar\\ \\ \n"
+ "baz \\ \n"
+ "qux\\ \n"
+ );
+
+ assert_is_ignored(false, "foo");
+ assert_is_ignored(true, "foo ");
+ assert_is_ignored(false, "bar");
+ assert_is_ignored(false, "bar ");
+ assert_is_ignored(true, "bar ");
+ assert_is_ignored(true, "baz ");
+ assert_is_ignored(false, "baz ");
+ assert_is_ignored(true, "qux ");
+ assert_is_ignored(false, "qux");
+ assert_is_ignored(false, "qux ");
+}
+
+void test_ignore_path__ignore_dir(void)
+{
+ cl_git_rewritefile("attr/.gitignore", "dir/\n");
+
+ assert_is_ignored(true, "dir");
+ assert_is_ignored(true, "dir/file");
+}
+
+void test_ignore_path__ignore_dir_with_trailing_space(void)
+{
+ cl_git_rewritefile("attr/.gitignore", "dir/ \n");
+
+ assert_is_ignored(true, "dir");
+ assert_is_ignored(true, "dir/file");
+}
+
+void test_ignore_path__ignore_root(void)
+{
+ cl_git_rewritefile("attr/.gitignore", "/\n\n/NewFolder\n/NewFolder/NewFolder");
+
+ assert_is_ignored(false, "File.txt");
+ assert_is_ignored(true, "NewFolder");
+ assert_is_ignored(true, "NewFolder/NewFolder");
+ assert_is_ignored(true, "NewFolder/NewFolder/File.txt");
+}
+
+void test_ignore_path__full_paths(void)
+{
+ cl_git_rewritefile("attr/.gitignore", "Folder/*/Contained");
+
+ assert_is_ignored(true, "Folder/Middle/Contained");
+ assert_is_ignored(false, "Folder/Middle/More/More/Contained");
+
+ cl_git_rewritefile("attr/.gitignore", "Folder/**/Contained");
+
+ assert_is_ignored(true, "Folder/Middle/Contained");
+ assert_is_ignored(true, "Folder/Middle/More/More/Contained");
+
+ cl_git_rewritefile("attr/.gitignore", "Folder/**/Contained/*/Child");
+
+ assert_is_ignored(true, "Folder/Middle/Contained/Happy/Child");
+ assert_is_ignored(false, "Folder/Middle/Contained/Not/Happy/Child");
+ assert_is_ignored(true, "Folder/Middle/More/More/Contained/Happy/Child");
+ assert_is_ignored(false, "Folder/Middle/More/More/Contained/Not/Happy/Child");
+}
+
+void test_ignore_path__more_starstar_cases(void)
+{
+ cl_must_pass(p_unlink("attr/.gitignore"));
+ cl_git_mkfile(
+ "attr/dir/.gitignore",
+ "sub/**/*.html\n");
+
+ assert_is_ignored(false, "aaa.html");
+ assert_is_ignored(false, "dir");
+ assert_is_ignored(false, "dir/sub");
+ assert_is_ignored(true, "dir/sub/sub2/aaa.html");
+ assert_is_ignored(true, "dir/sub/aaa.html");
+ assert_is_ignored(false, "dir/aaa.html");
+ assert_is_ignored(false, "sub");
+ assert_is_ignored(false, "sub/aaa.html");
+ assert_is_ignored(false, "sub/sub2/aaa.html");
+}
+
+void test_ignore_path__leading_stars(void)
+{
+ cl_git_rewritefile(
+ "attr/.gitignore",
+ "*/onestar\n"
+ "**/twostars\n"
+ "*/parent1/kid1/*\n"
+ "**/parent2/kid2/*\n");
+
+ assert_is_ignored(true, "dir1/onestar");
+ assert_is_ignored(true, "dir1/onestar/child"); /* in ignored dir */
+ assert_is_ignored(false, "dir1/dir2/onestar");
+
+ assert_is_ignored(true, "dir1/twostars");
+ assert_is_ignored(true, "dir1/twostars/child"); /* in ignored dir */
+ assert_is_ignored(true, "dir1/dir2/twostars");
+ assert_is_ignored(true, "dir1/dir2/twostars/child"); /* in ignored dir */
+ assert_is_ignored(true, "dir1/dir2/dir3/twostars");
+
+ assert_is_ignored(true, "dir1/parent1/kid1/file");
+ assert_is_ignored(true, "dir1/parent1/kid1/file/inside/parent");
+ assert_is_ignored(false, "dir1/dir2/parent1/kid1/file");
+ assert_is_ignored(false, "dir1/parent1/file");
+ assert_is_ignored(false, "dir1/kid1/file");
+
+ assert_is_ignored(true, "dir1/parent2/kid2/file");
+ assert_is_ignored(true, "dir1/parent2/kid2/file/inside/parent");
+ assert_is_ignored(true, "dir1/dir2/parent2/kid2/file");
+ assert_is_ignored(true, "dir1/dir2/dir3/parent2/kid2/file");
+ assert_is_ignored(false, "dir1/parent2/file");
+ assert_is_ignored(false, "dir1/kid2/file");
+}
+
+void test_ignore_path__globs_and_path_delimiters(void)
+{
+ cl_git_rewritefile("attr/.gitignore", "foo/bar/**");
+ assert_is_ignored(true, "foo/bar/baz");
+ assert_is_ignored(true, "foo/bar/baz/quux");
+
+ cl_git_rewritefile("attr/.gitignore", "_*/");
+ assert_is_ignored(true, "sub/_test/a/file");
+ assert_is_ignored(false, "test_folder/file");
+ assert_is_ignored(true, "_test/file");
+ assert_is_ignored(true, "_test/a/file");
+
+ cl_git_rewritefile("attr/.gitignore", "**/_*/");
+ assert_is_ignored(true, "sub/_test/a/file");
+ assert_is_ignored(false, "test_folder/file");
+ assert_is_ignored(true, "_test/file");
+ assert_is_ignored(true, "_test/a/file");
+
+ cl_git_rewritefile("attr/.gitignore", "**/_*/foo/bar/*ux");
+
+ assert_is_ignored(true, "sub/_test/foo/bar/qux/file");
+ assert_is_ignored(true, "_test/foo/bar/qux/file");
+ assert_is_ignored(true, "_test/foo/bar/crux/file");
+ assert_is_ignored(false, "_test/foo/bar/code/file");
+}
+
+void test_ignore_path__globs_without_star(void)
+{
+ cl_git_rewritefile(
+ "attr/.gitignore",
+ "*.foo\n"
+ "**.bar\n"
+ );
+
+ assert_is_ignored(true, ".foo");
+ assert_is_ignored(true, "xyz.foo");
+ assert_is_ignored(true, ".bar");
+ assert_is_ignored(true, "x.bar");
+ assert_is_ignored(true, "xyz.bar");
+
+ assert_is_ignored(true, "test/.foo");
+ assert_is_ignored(true, "test/x.foo");
+ assert_is_ignored(true, "test/xyz.foo");
+ assert_is_ignored(true, "test/.bar");
+ assert_is_ignored(true, "test/x.bar");
+ assert_is_ignored(true, "test/xyz.bar");
+}
+
+void test_ignore_path__skip_gitignore_directory(void)
+{
+ cl_git_rewritefile("attr/.git/info/exclude", "/NewFolder\n/NewFolder/NewFolder");
+ cl_must_pass(p_unlink("attr/.gitignore"));
+ cl_assert(!git_fs_path_exists("attr/.gitignore"));
+ p_mkdir("attr/.gitignore", 0777);
+ cl_git_mkfile("attr/.gitignore/garbage.txt", "new_file\n");
+
+ assert_is_ignored(false, "File.txt");
+ assert_is_ignored(true, "NewFolder");
+ assert_is_ignored(true, "NewFolder/NewFolder");
+ assert_is_ignored(true, "NewFolder/NewFolder/File.txt");
+}
+
+void test_ignore_path__subdirectory_gitignore(void)
+{
+ cl_must_pass(p_unlink("attr/.gitignore"));
+ cl_assert(!git_fs_path_exists("attr/.gitignore"));
+ cl_git_mkfile(
+ "attr/.gitignore",
+ "file1\n");
+ cl_git_mkfile(
+ "attr/dir/.gitignore",
+ "file2/\n");
+
+ assert_is_ignored(true, "file1");
+ assert_is_ignored(true, "dir/file1");
+ assert_is_ignored(true, "dir/file2/actual_file"); /* in ignored dir */
+ assert_is_ignored(false, "dir/file3");
+}
+
+void test_ignore_path__expand_tilde_to_homedir(void)
+{
+ git_str homefile = GIT_STR_INIT;
+ git_config *cfg;
+
+ assert_is_ignored(false, "example.global_with_tilde");
+
+ cl_fake_homedir(&homefile);
+ cl_git_pass(git_str_joinpath(&homefile, homefile.ptr, "globalexclude"));
+
+ /* construct fake home with fake global excludes */
+ cl_git_mkfile(homefile.ptr, "# found me\n*.global_with_tilde\n");
+
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_git_pass(git_config_set_string(cfg, "core.excludesfile", "~/globalexclude"));
+ git_config_free(cfg);
+
+ git_attr_cache_flush(g_repo); /* must reset to pick up change */
+
+ assert_is_ignored(true, "example.global_with_tilde");
+
+ cl_git_pass(git_futils_rmdir_r("home", NULL, GIT_RMDIR_REMOVE_FILES));
+
+ cl_fake_homedir_cleanup(NULL);
+
+ git_attr_cache_flush(g_repo); /* must reset to pick up change */
+
+ assert_is_ignored(false, "example.global_with_tilde");
+
+ git_str_dispose(&homefile);
+}
+
+/* Ensure that the .gitignore in the subdirectory only affects
+ * items in the subdirectory. */
+void test_ignore_path__gitignore_in_subdir(void)
+{
+ cl_git_rmfile("attr/.gitignore");
+
+ cl_must_pass(p_mkdir("attr/dir1", 0777));
+ cl_must_pass(p_mkdir("attr/dir1/dir2", 0777));
+ cl_must_pass(p_mkdir("attr/dir1/dir2/dir3", 0777));
+
+ cl_git_mkfile("attr/dir1/dir2/dir3/.gitignore", "dir1/\ndir1/subdir/");
+
+ assert_is_ignored(false, "dir1/file");
+ assert_is_ignored(false, "dir1/dir2/file");
+ assert_is_ignored(false, "dir1/dir2/dir3/file");
+ assert_is_ignored(true, "dir1/dir2/dir3/dir1/file");
+ assert_is_ignored(true, "dir1/dir2/dir3/dir1/subdir/foo");
+
+ if (cl_repo_get_bool(g_repo, "core.ignorecase")) {
+ cl_git_mkfile("attr/dir1/dir2/dir3/.gitignore", "DiR1/\nDiR1/subdir/\n");
+
+ assert_is_ignored(false, "dir1/file");
+ assert_is_ignored(false, "dir1/dir2/file");
+ assert_is_ignored(false, "dir1/dir2/dir3/file");
+ assert_is_ignored(true, "dir1/dir2/dir3/dir1/file");
+ assert_is_ignored(true, "dir1/dir2/dir3/dir1/subdir/foo");
+ }
+}
+
+/* Ensure that files do not match folder cases */
+void test_ignore_path__dont_ignore_files_for_folder(void)
+{
+ cl_git_rmfile("attr/.gitignore");
+
+ cl_git_mkfile("attr/dir/.gitignore", "test/\n");
+
+ /* Create "test" as a file; ensure it is not ignored. */
+ cl_git_mkfile("attr/dir/test", "This is a file.");
+
+ assert_is_ignored(false, "dir/test");
+ if (cl_repo_get_bool(g_repo, "core.ignorecase"))
+ assert_is_ignored(false, "dir/TeSt");
+
+ /* Create "test" as a directory; ensure it is ignored. */
+ cl_git_rmfile("attr/dir/test");
+ cl_must_pass(p_mkdir("attr/dir/test", 0777));
+
+ assert_is_ignored(true, "dir/test");
+ if (cl_repo_get_bool(g_repo, "core.ignorecase"))
+ assert_is_ignored(true, "dir/TeSt");
+
+ /* Remove "test" entirely; ensure it is not ignored.
+ * (As it doesn't exist, it is not a directory.)
+ */
+ cl_must_pass(p_rmdir("attr/dir/test"));
+
+ assert_is_ignored(false, "dir/test");
+ if (cl_repo_get_bool(g_repo, "core.ignorecase"))
+ assert_is_ignored(false, "dir/TeSt");
+}
+
+void test_ignore_path__symlink_to_outside(void)
+{
+#ifdef GIT_WIN32
+ cl_skip();
+#endif
+
+ cl_git_rewritefile("attr/.gitignore", "symlink\n");
+ cl_git_mkfile("target", "target");
+ cl_git_pass(p_symlink("../target", "attr/symlink"));
+ assert_is_ignored(true, "symlink");
+ assert_is_ignored(true, "lala/../symlink");
+}
+
+void test_ignore_path__test(void)
+{
+ cl_git_rewritefile("attr/.gitignore",
+ "/*/\n"
+ "!/src\n");
+ assert_is_ignored(false, "src/foo.c");
+ assert_is_ignored(false, "src/foo/foo.c");
+ assert_is_ignored(false, "README.md");
+ assert_is_ignored(true, "dist/foo.o");
+ assert_is_ignored(true, "bin/foo");
+}
+
+void test_ignore_path__unignore_dir_succeeds(void)
+{
+ cl_git_rewritefile("attr/.gitignore",
+ "*.c\n"
+ "!src/*.c\n");
+ assert_is_ignored(false, "src/foo.c");
+ assert_is_ignored(true, "src/foo/foo.c");
+}
+
+void test_ignore_path__case_insensitive_unignores_previous_rule(void)
+{
+ git_config *cfg;
+
+ cl_git_rewritefile("attr/.gitignore",
+ "/case\n"
+ "!/Case/\n");
+
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_git_pass(git_config_set_bool(cfg, "core.ignorecase", true));
+
+ cl_must_pass(p_mkdir("attr/case", 0755));
+ cl_git_mkfile("attr/case/file", "content");
+
+ assert_is_ignored(false, "case/file");
+}
+
+void test_ignore_path__case_sensitive_unignore_does_nothing(void)
+{
+ git_config *cfg;
+
+ cl_git_rewritefile("attr/.gitignore",
+ "/case\n"
+ "!/Case/\n");
+
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_git_pass(git_config_set_bool(cfg, "core.ignorecase", false));
+
+ cl_must_pass(p_mkdir("attr/case", 0755));
+ cl_git_mkfile("attr/case/file", "content");
+
+ assert_is_ignored(true, "case/file");
+}
+
+void test_ignore_path__ignored_subdirfiles_with_subdir_rule(void)
+{
+ cl_git_rewritefile(
+ "attr/.gitignore",
+ "dir/*\n"
+ "!dir/sub1/sub2/**\n");
+
+ assert_is_ignored(true, "dir/a.test");
+ assert_is_ignored(true, "dir/sub1/a.test");
+ assert_is_ignored(true, "dir/sub1/sub2");
+}
+
+void test_ignore_path__ignored_subdirfiles_with_negations(void)
+{
+ cl_git_rewritefile(
+ "attr/.gitignore",
+ "dir/*\n"
+ "!dir/a.test\n");
+
+ assert_is_ignored(false, "dir/a.test");
+ assert_is_ignored(true, "dir/b.test");
+ assert_is_ignored(true, "dir/sub1/c.test");
+}
+
+void test_ignore_path__negative_directory_rules_only_match_directories(void)
+{
+ cl_git_rewritefile(
+ "attr/.gitignore",
+ "*\n"
+ "!/**/\n"
+ "!*.keep\n"
+ "!.gitignore\n"
+ );
+
+ assert_is_ignored(true, "src");
+ assert_is_ignored(true, "src/A");
+ assert_is_ignored(false, "src/");
+ assert_is_ignored(false, "src/A.keep");
+ assert_is_ignored(false, ".gitignore");
+}
+
+void test_ignore_path__escaped_character(void)
+{
+ cl_git_rewritefile("attr/.gitignore", "\\c\n");
+ assert_is_ignored(true, "c");
+ assert_is_ignored(false, "\\c");
+}
+
+void test_ignore_path__escaped_newline(void)
+{
+ cl_git_rewritefile(
+ "attr/.gitignore",
+ "\\\nnewline\n"
+ );
+
+ assert_is_ignored(true, "\nnewline");
+}
+
+void test_ignore_path__escaped_glob(void)
+{
+ cl_git_rewritefile("attr/.gitignore", "\\*\n");
+ assert_is_ignored(true, "*");
+ assert_is_ignored(false, "foo");
+}
+
+void test_ignore_path__escaped_comments(void)
+{
+ cl_git_rewritefile(
+ "attr/.gitignore",
+ "#foo\n"
+ "\\#bar\n"
+ "\\##baz\n"
+ "\\#\\\\#qux\n"
+ );
+
+ assert_is_ignored(false, "#foo");
+ assert_is_ignored(true, "#bar");
+ assert_is_ignored(false, "\\#bar");
+ assert_is_ignored(true, "##baz");
+ assert_is_ignored(false, "\\##baz");
+ assert_is_ignored(true, "#\\#qux");
+ assert_is_ignored(false, "##qux");
+ assert_is_ignored(false, "\\##qux");
+}
+
+void test_ignore_path__escaped_slash(void)
+{
+ cl_git_rewritefile(
+ "attr/.gitignore",
+ "\\\\\n"
+ "\\\\preceding\n"
+ "inter\\\\mittent\n"
+ "trailing\\\\\n"
+ );
+
+#ifndef GIT_WIN32
+ assert_is_ignored(true, "\\");
+ assert_is_ignored(true, "\\preceding");
+#endif
+ assert_is_ignored(true, "inter\\mittent");
+ assert_is_ignored(true, "trailing\\");
+}
+
+void test_ignore_path__escaped_space(void)
+{
+ cl_git_rewritefile(
+ "attr/.gitignore",
+ "foo\\\\ \n"
+ "bar\\\\\\ \n");
+ assert_is_ignored(true, "foo\\");
+ assert_is_ignored(false, "foo\\ ");
+ assert_is_ignored(false, "foo\\\\ ");
+ assert_is_ignored(false, "foo\\\\");
+ assert_is_ignored(true, "bar\\ ");
+ assert_is_ignored(false, "bar\\\\");
+ assert_is_ignored(false, "bar\\\\ ");
+ assert_is_ignored(false, "bar\\\\\\");
+ assert_is_ignored(false, "bar\\\\\\ ");
+}
+
+void test_ignore_path__invalid_pattern(void)
+{
+ cl_git_rewritefile("attr/.gitignore", "[");
+ assert_is_ignored(false, "[f");
+ assert_is_ignored(false, "f");
+}
+
+void test_ignore_path__negative_prefix_rule(void)
+{
+ cl_git_rewritefile("attr/.gitignore", "ff*\n!f\n");
+ assert_is_ignored(true, "fff");
+ assert_is_ignored(true, "ff");
+ assert_is_ignored(false, "f");
+}
+
+void test_ignore_path__negative_more_specific(void)
+{
+ cl_git_rewritefile("attr/.gitignore", "*.txt\n!/dir/test.txt\n");
+ assert_is_ignored(true, "test.txt");
+ assert_is_ignored(false, "dir/test.txt");
+ assert_is_ignored(true, "outer/dir/test.txt");
+}
diff --git a/tests/libgit2/ignore/status.c b/tests/libgit2/ignore/status.c
new file mode 100644
index 0000000..a007774
--- /dev/null
+++ b/tests/libgit2/ignore/status.c
@@ -0,0 +1,1341 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+#include "git2/attr.h"
+#include "ignore.h"
+#include "attr.h"
+#include "status/status_helpers.h"
+
+static git_repository *g_repo = NULL;
+
+void test_ignore_status__initialize(void)
+{
+}
+
+void test_ignore_status__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static void assert_ignored_(
+ bool expected, const char *filepath,
+ const char *file, const char *func, int line)
+{
+ int is_ignored = 0;
+ cl_git_expect(
+ git_status_should_ignore(&is_ignored, g_repo, filepath), 0, file, func, line);
+ clar__assert(
+ (expected != 0) == (is_ignored != 0),
+ file, func, line, "expected != is_ignored", filepath, 1);
+}
+#define assert_ignored(expected, filepath) \
+ assert_ignored_(expected, filepath, __FILE__, __func__, __LINE__)
+#define assert_is_ignored(filepath) \
+ assert_ignored_(true, filepath, __FILE__, __func__, __LINE__)
+#define refute_is_ignored(filepath) \
+ assert_ignored_(false, filepath, __FILE__, __func__, __LINE__)
+
+void test_ignore_status__0(void)
+{
+ struct {
+ const char *path;
+ int expected;
+ } test_cases[] = {
+ /* pattern "ign" from .gitignore */
+ { "file", 0 },
+ { "ign", 1 },
+ { "sub", 0 },
+ { "sub/file", 0 },
+ { "sub/ign", 1 },
+ { "sub/ign/file", 1 },
+ { "sub/ign/sub", 1 },
+ { "sub/ign/sub/file", 1 },
+ { "sub/sub", 0 },
+ { "sub/sub/file", 0 },
+ { "sub/sub/ign", 1 },
+ { "sub/sub/sub", 0 },
+ /* pattern "dir/" from .gitignore */
+ { "dir", 1 },
+ { "dir/", 1 },
+ { "sub/dir", 1 },
+ { "sub/dir/", 1 },
+ { "sub/dir/file", 1 }, /* contained in ignored parent */
+ { "sub/sub/dir", 0 }, /* dir is not actually a dir, but a file */
+ { NULL, 0 }
+ }, *one_test;
+
+ g_repo = cl_git_sandbox_init("attr");
+
+ for (one_test = test_cases; one_test->path != NULL; one_test++)
+ assert_ignored(one_test->expected, one_test->path);
+
+ /* confirm that ignore files were cached */
+ cl_assert(git_attr_cache__is_cached(
+ g_repo, GIT_ATTR_FILE_SOURCE_FILE, ".git/info/exclude"));
+ cl_assert(git_attr_cache__is_cached(
+ g_repo, GIT_ATTR_FILE_SOURCE_FILE, ".gitignore"));
+}
+
+
+void test_ignore_status__1(void)
+{
+ g_repo = cl_git_sandbox_init("attr");
+
+ cl_git_rewritefile("attr/.gitignore", "/*.txt\n/dir/\n");
+ git_attr_cache_flush(g_repo);
+
+ assert_is_ignored("root_test4.txt");
+ refute_is_ignored("sub/subdir_test2.txt");
+ assert_is_ignored("dir");
+ assert_is_ignored("dir/");
+ refute_is_ignored("sub/dir");
+ refute_is_ignored("sub/dir/");
+}
+
+void test_ignore_status__empty_repo_with_gitignore_rewrite(void)
+{
+ status_entry_single st;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_mkfile(
+ "empty_standard_repo/look-ma.txt", "I'm going to be ignored!");
+
+ memset(&st, 0, sizeof(st));
+ cl_git_pass(git_status_foreach(g_repo, cb_status__single, &st));
+ cl_assert(st.count == 1);
+ cl_assert(st.status == GIT_STATUS_WT_NEW);
+
+ cl_git_pass(git_status_file(&st.status, g_repo, "look-ma.txt"));
+ cl_assert(st.status == GIT_STATUS_WT_NEW);
+
+ refute_is_ignored("look-ma.txt");
+
+ cl_git_rewritefile("empty_standard_repo/.gitignore", "*.nomatch\n");
+
+ memset(&st, 0, sizeof(st));
+ cl_git_pass(git_status_foreach(g_repo, cb_status__single, &st));
+ cl_assert(st.count == 2);
+ cl_assert(st.status == GIT_STATUS_WT_NEW);
+
+ cl_git_pass(git_status_file(&st.status, g_repo, "look-ma.txt"));
+ cl_assert(st.status == GIT_STATUS_WT_NEW);
+
+ refute_is_ignored("look-ma.txt");
+
+ cl_git_rewritefile("empty_standard_repo/.gitignore", "*.txt\n");
+
+ memset(&st, 0, sizeof(st));
+ cl_git_pass(git_status_foreach(g_repo, cb_status__single, &st));
+ cl_assert(st.count == 2);
+ cl_assert(st.status == GIT_STATUS_IGNORED);
+
+ cl_git_pass(git_status_file(&st.status, g_repo, "look-ma.txt"));
+ cl_assert(st.status == GIT_STATUS_IGNORED);
+
+ assert_is_ignored("look-ma.txt");
+}
+
+void test_ignore_status__ignore_pattern_contains_space(void)
+{
+ unsigned int flags;
+ const mode_t mode = 0777;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+ cl_git_rewritefile("empty_standard_repo/.gitignore", "foo bar.txt\n");
+
+ cl_git_mkfile(
+ "empty_standard_repo/foo bar.txt", "I'm going to be ignored!");
+
+ cl_git_pass(git_status_file(&flags, g_repo, "foo bar.txt"));
+ cl_assert(flags == GIT_STATUS_IGNORED);
+
+ cl_git_pass(git_futils_mkdir_r("empty_standard_repo/foo", mode));
+ cl_git_mkfile("empty_standard_repo/foo/look-ma.txt", "I'm not going to be ignored!");
+
+ cl_git_pass(git_status_file(&flags, g_repo, "foo/look-ma.txt"));
+ cl_assert(flags == GIT_STATUS_WT_NEW);
+}
+
+void test_ignore_status__ignore_pattern_ignorecase(void)
+{
+ unsigned int flags;
+ bool ignore_case;
+ git_index *index;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+ cl_git_rewritefile("empty_standard_repo/.gitignore", "a.txt\n");
+
+ cl_git_mkfile("empty_standard_repo/A.txt", "Differs in case");
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ ignore_case = (git_index_caps(index) & GIT_INDEX_CAPABILITY_IGNORE_CASE) != 0;
+ git_index_free(index);
+
+ cl_git_pass(git_status_file(&flags, g_repo, "A.txt"));
+ cl_assert(flags == ignore_case ? GIT_STATUS_IGNORED : GIT_STATUS_WT_NEW);
+}
+
+void test_ignore_status__subdirectories(void)
+{
+ status_entry_single st;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_mkfile(
+ "empty_standard_repo/ignore_me", "I'm going to be ignored!");
+
+ cl_git_rewritefile("empty_standard_repo/.gitignore", "ignore_me\n");
+
+ memset(&st, 0, sizeof(st));
+ cl_git_pass(git_status_foreach(g_repo, cb_status__single, &st));
+ cl_assert_equal_i(2, st.count);
+ cl_assert(st.status == GIT_STATUS_IGNORED);
+
+ cl_git_pass(git_status_file(&st.status, g_repo, "ignore_me"));
+ cl_assert(st.status == GIT_STATUS_IGNORED);
+
+ assert_is_ignored("ignore_me");
+
+ /* I've changed libgit2 so that the behavior here now differs from
+ * core git but seems to make more sense. In core git, the following
+ * items are skipped completed, even if --ignored is passed to status.
+ * It you mirror these steps and run "git status -uall --ignored" then
+ * you will not see "test/ignore_me/" in the results.
+ *
+ * However, we had a couple reports of this as a bug, plus there is a
+ * similar circumstance where we were differing for core git when you
+ * used a rooted path for an ignore, so I changed this behavior.
+ */
+ cl_git_pass(git_futils_mkdir_r(
+ "empty_standard_repo/test/ignore_me", 0775));
+ cl_git_mkfile(
+ "empty_standard_repo/test/ignore_me/file", "I'm going to be ignored!");
+ cl_git_mkfile(
+ "empty_standard_repo/test/ignore_me/file2", "Me, too!");
+
+ memset(&st, 0, sizeof(st));
+ cl_git_pass(git_status_foreach(g_repo, cb_status__single, &st));
+ cl_assert_equal_i(3, st.count);
+
+ cl_git_pass(git_status_file(&st.status, g_repo, "test/ignore_me/file"));
+ cl_assert(st.status == GIT_STATUS_IGNORED);
+
+ assert_is_ignored("test/ignore_me/file");
+}
+
+static void make_test_data(const char *reponame, const char **files)
+{
+ const char **scan;
+ size_t repolen = strlen(reponame) + 1;
+
+ g_repo = cl_git_sandbox_init(reponame);
+
+ for (scan = files; *scan != NULL; ++scan) {
+ cl_git_pass(git_futils_mkdir_relative(
+ *scan + repolen, reponame,
+ 0777, GIT_MKDIR_PATH | GIT_MKDIR_SKIP_LAST, NULL));
+ cl_git_mkfile(*scan, "contents");
+ }
+}
+
+static const char *test_repo_1 = "empty_standard_repo";
+static const char *test_files_1[] = {
+ "empty_standard_repo/dir/a/ignore_me",
+ "empty_standard_repo/dir/b/ignore_me",
+ "empty_standard_repo/dir/ignore_me",
+ "empty_standard_repo/ignore_also/file",
+ "empty_standard_repo/ignore_me",
+ "empty_standard_repo/test/ignore_me/file",
+ "empty_standard_repo/test/ignore_me/file2",
+ "empty_standard_repo/test/ignore_me/and_me/file",
+ NULL
+};
+
+void test_ignore_status__subdirectories_recursion(void)
+{
+ /* Let's try again with recursing into ignored dirs turned on */
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ status_entry_counts counts;
+ static const char *paths_r[] = {
+ ".gitignore",
+ "dir/a/ignore_me",
+ "dir/b/ignore_me",
+ "dir/ignore_me",
+ "ignore_also/file",
+ "ignore_me",
+ "test/ignore_me/and_me/file",
+ "test/ignore_me/file",
+ "test/ignore_me/file2",
+ };
+ static const unsigned int statuses_r[] = {
+ GIT_STATUS_WT_NEW, GIT_STATUS_IGNORED, GIT_STATUS_IGNORED,
+ GIT_STATUS_IGNORED, GIT_STATUS_IGNORED, GIT_STATUS_IGNORED,
+ GIT_STATUS_IGNORED, GIT_STATUS_IGNORED, GIT_STATUS_IGNORED,
+ };
+ static const char *paths_nr[] = {
+ ".gitignore",
+ "dir/a/ignore_me",
+ "dir/b/ignore_me",
+ "dir/ignore_me",
+ "ignore_also/",
+ "ignore_me",
+ "test/ignore_me/",
+ };
+ static const unsigned int statuses_nr[] = {
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_IGNORED, GIT_STATUS_IGNORED, GIT_STATUS_IGNORED,
+ GIT_STATUS_IGNORED, GIT_STATUS_IGNORED, GIT_STATUS_IGNORED,
+ };
+
+ make_test_data(test_repo_1, test_files_1);
+ cl_git_rewritefile("empty_standard_repo/.gitignore", "ignore_me\n/ignore_also\n");
+
+ memset(&counts, 0x0, sizeof(status_entry_counts));
+ counts.expected_entry_count = 9;
+ counts.expected_paths = paths_r;
+ counts.expected_statuses = statuses_r;
+
+ opts.flags = GIT_STATUS_OPT_DEFAULTS | GIT_STATUS_OPT_RECURSE_IGNORED_DIRS;
+
+ cl_git_pass(git_status_foreach_ext(
+ g_repo, &opts, cb_status__normal, &counts));
+
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+
+
+ memset(&counts, 0x0, sizeof(status_entry_counts));
+ counts.expected_entry_count = 7;
+ counts.expected_paths = paths_nr;
+ counts.expected_statuses = statuses_nr;
+
+ opts.flags = GIT_STATUS_OPT_DEFAULTS;
+
+ cl_git_pass(git_status_foreach_ext(
+ g_repo, &opts, cb_status__normal, &counts));
+
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+}
+
+void test_ignore_status__subdirectories_not_at_root(void)
+{
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ status_entry_counts counts;
+ static const char *paths_1[] = {
+ "dir/.gitignore",
+ "dir/a/ignore_me",
+ "dir/b/ignore_me",
+ "dir/ignore_me",
+ "ignore_also/file",
+ "ignore_me",
+ "test/.gitignore",
+ "test/ignore_me/and_me/file",
+ "test/ignore_me/file",
+ "test/ignore_me/file2",
+ };
+ static const unsigned int statuses_1[] = {
+ GIT_STATUS_WT_NEW, GIT_STATUS_IGNORED, GIT_STATUS_IGNORED,
+ GIT_STATUS_IGNORED, GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_NEW, GIT_STATUS_IGNORED, GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW,
+ };
+
+ make_test_data(test_repo_1, test_files_1);
+ cl_git_rewritefile("empty_standard_repo/dir/.gitignore", "ignore_me\n/ignore_also\n");
+ cl_git_rewritefile("empty_standard_repo/test/.gitignore", "and_me\n");
+
+ memset(&counts, 0x0, sizeof(status_entry_counts));
+ counts.expected_entry_count = 10;
+ counts.expected_paths = paths_1;
+ counts.expected_statuses = statuses_1;
+
+ opts.flags = GIT_STATUS_OPT_DEFAULTS | GIT_STATUS_OPT_RECURSE_IGNORED_DIRS;
+
+ cl_git_pass(git_status_foreach_ext(
+ g_repo, &opts, cb_status__normal, &counts));
+
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+}
+
+void test_ignore_status__leading_slash_ignores(void)
+{
+ git_str homedir = GIT_STR_INIT;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ status_entry_counts counts;
+ static const char *paths_2[] = {
+ "dir/.gitignore",
+ "dir/a/ignore_me",
+ "dir/b/ignore_me",
+ "dir/ignore_me",
+ "ignore_also/file",
+ "ignore_me",
+ "test/.gitignore",
+ "test/ignore_me/and_me/file",
+ "test/ignore_me/file",
+ "test/ignore_me/file2",
+ };
+ static const unsigned int statuses_2[] = {
+ GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW,
+ GIT_STATUS_IGNORED, GIT_STATUS_IGNORED, GIT_STATUS_IGNORED,
+ GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW,
+ };
+
+ make_test_data(test_repo_1, test_files_1);
+
+ cl_fake_homedir(&homedir);
+ cl_git_pass(git_str_joinpath(&homedir, homedir.ptr, ".gitignore"));
+ cl_git_mkfile(homedir.ptr, "/ignore_me\n");
+ {
+ git_config *cfg;
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_git_pass(git_config_set_string(
+ cfg, "core.excludesfile", "~/.gitignore"));
+ git_config_free(cfg);
+ }
+
+ cl_git_rewritefile("empty_standard_repo/.git/info/exclude", "/ignore_also\n");
+ cl_git_rewritefile("empty_standard_repo/dir/.gitignore", "/ignore_me\n");
+ cl_git_rewritefile("empty_standard_repo/test/.gitignore", "/and_me\n");
+
+ memset(&counts, 0x0, sizeof(status_entry_counts));
+ counts.expected_entry_count = 10;
+ counts.expected_paths = paths_2;
+ counts.expected_statuses = statuses_2;
+
+ opts.flags = GIT_STATUS_OPT_DEFAULTS | GIT_STATUS_OPT_RECURSE_IGNORED_DIRS;
+
+ cl_git_pass(git_status_foreach_ext(
+ g_repo, &opts, cb_status__normal, &counts));
+
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+
+ git_str_dispose(&homedir);
+}
+
+void test_ignore_status__multiple_leading_slash(void)
+{
+ static const char *test_files[] = {
+ "empty_standard_repo/a.test",
+ "empty_standard_repo/b.test",
+ "empty_standard_repo/c.test",
+ "empty_standard_repo/d.test",
+ NULL
+ };
+
+ make_test_data("empty_standard_repo", test_files);
+ cl_git_mkfile(
+ "empty_standard_repo/.gitignore",
+ "a.test\n"
+ "/b.test\n"
+ "//c.test\n"
+ "///d.test\n");
+
+ assert_is_ignored("a.test");
+ assert_is_ignored("b.test");
+ refute_is_ignored("c.test");
+ refute_is_ignored("d.test");
+}
+
+void test_ignore_status__contained_dir_with_matching_name(void)
+{
+ static const char *test_files[] = {
+ "empty_standard_repo/subdir_match/aaa/subdir_match/file",
+ "empty_standard_repo/subdir_match/zzz_ignoreme",
+ NULL
+ };
+ static const char *expected_paths[] = {
+ "subdir_match/.gitignore",
+ "subdir_match/aaa/subdir_match/file",
+ "subdir_match/zzz_ignoreme",
+ };
+ static const unsigned int expected_statuses[] = {
+ GIT_STATUS_WT_NEW, GIT_STATUS_WT_NEW, GIT_STATUS_IGNORED
+ };
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ status_entry_counts counts;
+
+ make_test_data("empty_standard_repo", test_files);
+ cl_git_mkfile(
+ "empty_standard_repo/subdir_match/.gitignore", "*_ignoreme\n");
+
+ refute_is_ignored("subdir_match/aaa/subdir_match/file");
+ assert_is_ignored("subdir_match/zzz_ignoreme");
+
+ memset(&counts, 0x0, sizeof(status_entry_counts));
+ counts.expected_entry_count = 3;
+ counts.expected_paths = expected_paths;
+ counts.expected_statuses = expected_statuses;
+
+ opts.flags = GIT_STATUS_OPT_DEFAULTS | GIT_STATUS_OPT_RECURSE_IGNORED_DIRS;
+
+ cl_git_pass(git_status_foreach_ext(
+ g_repo, &opts, cb_status__normal, &counts));
+
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+}
+
+void test_ignore_status__trailing_slash_star(void)
+{
+ static const char *test_files[] = {
+ "empty_standard_repo/file",
+ "empty_standard_repo/subdir/file",
+ "empty_standard_repo/subdir/sub2/sub3/file",
+ NULL
+ };
+
+ make_test_data("empty_standard_repo", test_files);
+ cl_git_mkfile(
+ "empty_standard_repo/subdir/.gitignore", "/**/*\n");
+
+ refute_is_ignored("file");
+ assert_is_ignored("subdir/sub2/sub3/file");
+ assert_is_ignored("subdir/file");
+}
+
+void test_ignore_status__adding_internal_ignores(void)
+{
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ refute_is_ignored("one.txt");
+ refute_is_ignored("two.bar");
+
+ cl_git_pass(git_ignore_add_rule(g_repo, "*.nomatch\n"));
+
+ refute_is_ignored("one.txt");
+ refute_is_ignored("two.bar");
+
+ cl_git_pass(git_ignore_add_rule(g_repo, "*.txt\n"));
+
+ assert_is_ignored("one.txt");
+ refute_is_ignored("two.bar");
+
+ cl_git_pass(git_ignore_add_rule(g_repo, "*.bar\n"));
+
+ assert_is_ignored("one.txt");
+ assert_is_ignored("two.bar");
+
+ cl_git_pass(git_ignore_clear_internal_rules(g_repo));
+
+ refute_is_ignored("one.txt");
+ refute_is_ignored("two.bar");
+
+ cl_git_pass(git_ignore_add_rule(
+ g_repo, "multiple\n*.rules\n# comment line\n*.bar\n"));
+
+ refute_is_ignored("one.txt");
+ assert_is_ignored("two.bar");
+}
+
+void test_ignore_status__add_internal_as_first_thing(void)
+{
+ const char *add_me = "\n#################\n## Eclipse\n#################\n\n*.pydevproject\n.project\n.metadata\nbin/\ntmp/\n*.tmp\n\n";
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_pass(git_ignore_add_rule(g_repo, add_me));
+
+ assert_is_ignored("one.tmp");
+ refute_is_ignored("two.bar");
+}
+
+void test_ignore_status__internal_ignores_inside_deep_paths(void)
+{
+ const char *add_me = "Debug\nthis/is/deep\npatterned*/dir\n";
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_pass(git_ignore_add_rule(g_repo, add_me));
+
+ assert_is_ignored("Debug");
+ assert_is_ignored("and/Debug");
+ assert_is_ignored("really/Debug/this/file");
+ assert_is_ignored("Debug/what/I/say");
+
+ refute_is_ignored("and/NoDebug");
+ refute_is_ignored("NoDebug/this");
+ refute_is_ignored("please/NoDebug/this");
+
+ assert_is_ignored("this/is/deep");
+ /* pattern containing slash gets FNM_PATHNAME so all slashes must match */
+ refute_is_ignored("and/this/is/deep");
+ assert_is_ignored("this/is/deep/too");
+ /* pattern containing slash gets FNM_PATHNAME so all slashes must match */
+ refute_is_ignored("but/this/is/deep/and/ignored");
+
+ refute_is_ignored("this/is/not/deep");
+ refute_is_ignored("is/this/not/as/deep");
+ refute_is_ignored("this/is/deepish");
+ refute_is_ignored("xthis/is/deep");
+}
+
+void test_ignore_status__automatically_ignore_bad_files(void)
+{
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ assert_is_ignored(".git");
+ assert_is_ignored("this/file/.");
+ assert_is_ignored("path/../funky");
+ refute_is_ignored("path/whatever.c");
+
+ cl_git_pass(git_ignore_add_rule(g_repo, "*.c\n"));
+
+ assert_is_ignored(".git");
+ assert_is_ignored("this/file/.");
+ assert_is_ignored("path/../funky");
+ assert_is_ignored("path/whatever.c");
+
+ cl_git_pass(git_ignore_clear_internal_rules(g_repo));
+
+ assert_is_ignored(".git");
+ assert_is_ignored("this/file/.");
+ assert_is_ignored("path/../funky");
+ refute_is_ignored("path/whatever.c");
+}
+
+void test_ignore_status__filenames_with_special_prefixes_do_not_interfere_with_status_retrieval(void)
+{
+ status_entry_single st;
+ char *test_cases[] = {
+ "!file",
+ "#blah",
+ "[blah]",
+ "[attr]",
+ "[attr]blah",
+ NULL
+ };
+ int i;
+
+ for (i = 0; *(test_cases + i) != NULL; i++) {
+ git_str file = GIT_STR_INIT;
+ char *file_name = *(test_cases + i);
+ git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_pass(git_str_joinpath(&file, "empty_standard_repo", file_name));
+ cl_git_mkfile(git_str_cstr(&file), "Please don't ignore me!");
+
+ memset(&st, 0, sizeof(st));
+ cl_git_pass(git_status_foreach(repo, cb_status__single, &st));
+ cl_assert(st.count == 1);
+ cl_assert(st.status == GIT_STATUS_WT_NEW);
+
+ cl_git_pass(git_status_file(&st.status, repo, file_name));
+ cl_assert(st.status == GIT_STATUS_WT_NEW);
+
+ cl_git_sandbox_cleanup();
+ git_str_dispose(&file);
+ }
+}
+
+void test_ignore_status__issue_1766_negated_ignores(void)
+{
+ unsigned int status;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_pass(git_futils_mkdir_r(
+ "empty_standard_repo/a", 0775));
+ cl_git_mkfile(
+ "empty_standard_repo/a/.gitignore", "*\n!.gitignore\n");
+ cl_git_mkfile(
+ "empty_standard_repo/a/ignoreme", "I should be ignored\n");
+
+ refute_is_ignored("a/.gitignore");
+ assert_is_ignored("a/ignoreme");
+
+ cl_git_pass(git_futils_mkdir_r(
+ "empty_standard_repo/b", 0775));
+ cl_git_mkfile(
+ "empty_standard_repo/b/.gitignore", "*\n!.gitignore\n");
+ cl_git_mkfile(
+ "empty_standard_repo/b/ignoreme", "I should be ignored\n");
+
+ refute_is_ignored("b/.gitignore");
+ assert_is_ignored("b/ignoreme");
+
+ /* shouldn't have changed results from first couple either */
+ refute_is_ignored("a/.gitignore");
+ assert_is_ignored("a/ignoreme");
+
+ /* status should find the two ignore files and nothing else */
+
+ cl_git_pass(git_status_file(&status, g_repo, "a/.gitignore"));
+ cl_assert_equal_i(GIT_STATUS_WT_NEW, (int)status);
+
+ cl_git_pass(git_status_file(&status, g_repo, "a/ignoreme"));
+ cl_assert_equal_i(GIT_STATUS_IGNORED, (int)status);
+
+ cl_git_pass(git_status_file(&status, g_repo, "b/.gitignore"));
+ cl_assert_equal_i(GIT_STATUS_WT_NEW, (int)status);
+
+ cl_git_pass(git_status_file(&status, g_repo, "b/ignoreme"));
+ cl_assert_equal_i(GIT_STATUS_IGNORED, (int)status);
+
+ {
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ status_entry_counts counts;
+ static const char *paths[] = {
+ "a/.gitignore",
+ "a/ignoreme",
+ "b/.gitignore",
+ "b/ignoreme",
+ };
+ static const unsigned int statuses[] = {
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_IGNORED,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_IGNORED,
+ };
+
+ memset(&counts, 0x0, sizeof(status_entry_counts));
+ counts.expected_entry_count = 4;
+ counts.expected_paths = paths;
+ counts.expected_statuses = statuses;
+
+ opts.flags = GIT_STATUS_OPT_DEFAULTS;
+
+ cl_git_pass(git_status_foreach_ext(
+ g_repo, &opts, cb_status__normal, &counts));
+
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+ }
+}
+
+static void add_one_to_index(const char *file)
+{
+ git_index *index;
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_add_bypath(index, file));
+ git_index_free(index);
+}
+
+/* Some further broken scenarios that have been reported */
+void test_ignore_status__more_breakage(void)
+{
+ static const char *test_files[] = {
+ "empty_standard_repo/d1/pfx-d2/d3/d4/d5/tracked",
+ "empty_standard_repo/d1/pfx-d2/d3/d4/d5/untracked",
+ "empty_standard_repo/d1/pfx-d2/d3/d4/untracked",
+ NULL
+ };
+
+ make_test_data("empty_standard_repo", test_files);
+ cl_git_mkfile(
+ "empty_standard_repo/.gitignore",
+ "/d1/pfx-*\n"
+ "!/d1/pfx-d2/\n"
+ "/d1/pfx-d2/*\n"
+ "!/d1/pfx-d2/d3/\n"
+ "/d1/pfx-d2/d3/*\n"
+ "!/d1/pfx-d2/d3/d4/\n");
+ add_one_to_index("d1/pfx-d2/d3/d4/d5/tracked");
+
+ {
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ status_entry_counts counts;
+ static const char *files[] = {
+ ".gitignore",
+ "d1/pfx-d2/d3/d4/d5/tracked",
+ "d1/pfx-d2/d3/d4/d5/untracked",
+ "d1/pfx-d2/d3/d4/untracked",
+ };
+ static const unsigned int statuses[] = {
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_NEW,
+ };
+
+ memset(&counts, 0x0, sizeof(status_entry_counts));
+ counts.expected_entry_count = 4;
+ counts.expected_paths = files;
+ counts.expected_statuses = statuses;
+ opts.flags = GIT_STATUS_OPT_DEFAULTS |
+ GIT_STATUS_OPT_INCLUDE_IGNORED |
+ GIT_STATUS_OPT_RECURSE_IGNORED_DIRS;
+ cl_git_pass(git_status_foreach_ext(
+ g_repo, &opts, cb_status__normal, &counts));
+
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+ }
+
+ refute_is_ignored("d1/pfx-d2/d3/d4/d5/tracked");
+ refute_is_ignored("d1/pfx-d2/d3/d4/d5/untracked");
+ refute_is_ignored("d1/pfx-d2/d3/d4/untracked");
+}
+
+void test_ignore_status__negative_ignores_inside_ignores(void)
+{
+ static const char *test_files[] = {
+ "empty_standard_repo/top/mid/btm/tracked",
+ "empty_standard_repo/top/mid/btm/untracked",
+ "empty_standard_repo/zoo/bar",
+ "empty_standard_repo/zoo/foo/bar",
+ NULL
+ };
+
+ make_test_data("empty_standard_repo", test_files);
+ cl_git_mkfile(
+ "empty_standard_repo/.gitignore",
+ "top\n"
+ "!top/mid/btm\n"
+ "zoo/*\n"
+ "!zoo/bar\n"
+ "!zoo/foo/bar\n");
+ add_one_to_index("top/mid/btm/tracked");
+
+ {
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ status_entry_counts counts;
+ static const char *files[] = {
+ ".gitignore", "top/mid/btm/tracked", "top/mid/btm/untracked",
+ "zoo/bar", "zoo/foo/bar",
+ };
+ static const unsigned int statuses[] = {
+ GIT_STATUS_WT_NEW, GIT_STATUS_INDEX_NEW, GIT_STATUS_IGNORED,
+ GIT_STATUS_WT_NEW, GIT_STATUS_IGNORED,
+ };
+
+ memset(&counts, 0x0, sizeof(status_entry_counts));
+ counts.expected_entry_count = 5;
+ counts.expected_paths = files;
+ counts.expected_statuses = statuses;
+ opts.flags = GIT_STATUS_OPT_DEFAULTS |
+ GIT_STATUS_OPT_INCLUDE_IGNORED |
+ GIT_STATUS_OPT_RECURSE_IGNORED_DIRS;
+ cl_git_pass(git_status_foreach_ext(
+ g_repo, &opts, cb_status__normal, &counts));
+
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+ }
+
+ assert_is_ignored("top/mid/btm/tracked");
+ assert_is_ignored("top/mid/btm/untracked");
+ refute_is_ignored("foo/bar");
+}
+
+void test_ignore_status__negative_ignores_in_slash_star(void)
+{
+ git_status_options status_opts = GIT_STATUS_OPTIONS_INIT;
+ git_status_list *list;
+ int found_look_ma = 0, found_what_about = 0;
+ size_t i;
+ static const char *test_files[] = {
+ "empty_standard_repo/bin/look-ma.txt",
+ "empty_standard_repo/bin/what-about-me.txt",
+ NULL
+ };
+
+ make_test_data("empty_standard_repo", test_files);
+ cl_git_mkfile(
+ "empty_standard_repo/.gitignore",
+ "bin/*\n"
+ "!bin/w*\n");
+
+ assert_is_ignored("bin/look-ma.txt");
+ refute_is_ignored("bin/what-about-me.txt");
+
+ status_opts.flags = GIT_STATUS_OPT_DEFAULTS;
+ cl_git_pass(git_status_list_new(&list, g_repo, &status_opts));
+ for (i = 0; i < git_status_list_entrycount(list); i++) {
+ const git_status_entry *entry = git_status_byindex(list, i);
+
+ if (!strcmp("bin/look-ma.txt", entry->index_to_workdir->new_file.path))
+ found_look_ma = 1;
+
+ if (!strcmp("bin/what-about-me.txt", entry->index_to_workdir->new_file.path))
+ found_what_about = 1;
+ }
+ git_status_list_free(list);
+
+ cl_assert(found_look_ma);
+ cl_assert(found_what_about);
+}
+
+void test_ignore_status__negative_ignores_without_trailing_slash_inside_ignores(void)
+{
+ git_status_options status_opts = GIT_STATUS_OPTIONS_INIT;
+ git_status_list *list;
+ int found_parent_file = 0, found_parent_child1_file = 0, found_parent_child2_file = 0;
+ size_t i;
+ static const char *test_files[] = {
+ "empty_standard_repo/parent/file.txt",
+ "empty_standard_repo/parent/force.txt",
+ "empty_standard_repo/parent/child1/file.txt",
+ "empty_standard_repo/parent/child2/file.txt",
+ NULL
+ };
+
+ make_test_data("empty_standard_repo", test_files);
+ cl_git_mkfile(
+ "empty_standard_repo/.gitignore",
+ "parent/*\n"
+ "!parent/force.txt\n"
+ "!parent/child1\n"
+ "!parent/child2/\n");
+
+ add_one_to_index("parent/force.txt");
+
+ assert_is_ignored("parent/file.txt");
+ refute_is_ignored("parent/force.txt");
+ refute_is_ignored("parent/child1/file.txt");
+ refute_is_ignored("parent/child2/file.txt");
+
+ status_opts.flags = GIT_STATUS_OPT_DEFAULTS;
+ cl_git_pass(git_status_list_new(&list, g_repo, &status_opts));
+ for (i = 0; i < git_status_list_entrycount(list); i++) {
+ const git_status_entry *entry = git_status_byindex(list, i);
+
+ if (!entry->index_to_workdir)
+ continue;
+
+ if (!strcmp("parent/file.txt", entry->index_to_workdir->new_file.path))
+ found_parent_file = 1;
+
+ if (!strcmp("parent/force.txt", entry->index_to_workdir->new_file.path))
+ found_parent_file = 1;
+
+ if (!strcmp("parent/child1/file.txt", entry->index_to_workdir->new_file.path))
+ found_parent_child1_file = 1;
+
+ if (!strcmp("parent/child2/file.txt", entry->index_to_workdir->new_file.path))
+ found_parent_child2_file = 1;
+ }
+ git_status_list_free(list);
+
+ cl_assert(found_parent_file);
+ cl_assert(found_parent_child1_file);
+ cl_assert(found_parent_child2_file);
+}
+
+void test_ignore_status__negative_directory_ignores(void)
+{
+ static const char *test_files[] = {
+ "empty_standard_repo/parent/child1/bar.txt",
+ "empty_standard_repo/parent/child2/bar.txt",
+ "empty_standard_repo/parent/child3/foo.txt",
+ "empty_standard_repo/parent/child4/bar.txt",
+ "empty_standard_repo/parent/nested/child5/bar.txt",
+ "empty_standard_repo/parent/nested/child6/bar.txt",
+ "empty_standard_repo/parent/nested/child7/bar.txt",
+ "empty_standard_repo/padded_parent/child8/bar.txt",
+ NULL
+ };
+
+ make_test_data("empty_standard_repo", test_files);
+ cl_git_mkfile(
+ "empty_standard_repo/.gitignore",
+ "foo.txt\n"
+ "parent/child1\n"
+ "parent/child2\n"
+ "parent/child4\n"
+ "parent/nested/child5\n"
+ "nested/child6\n"
+ "nested/child7\n"
+ "padded_parent/child8\n"
+ /* test simple exact match */
+ "!parent/child1\n"
+ /* test negating file without negating dir */
+ "!parent/child2/bar.txt\n"
+ /* test negative pattern on dir with its content
+ * being ignored */
+ "!parent/child3\n"
+ /* test with partial match at end */
+ "!child4\n"
+ /* test with partial match with '/' at end */
+ "!nested/child5\n"
+ /* test with complete match */
+ "!nested/child6\n"
+ /* test with trailing '/' */
+ "!child7/\n"
+ /* test with partial dir match */
+ "!_parent/child8\n");
+
+ refute_is_ignored("parent/child1/bar.txt");
+ assert_is_ignored("parent/child2/bar.txt");
+ assert_is_ignored("parent/child3/foo.txt");
+ refute_is_ignored("parent/child4/bar.txt");
+ assert_is_ignored("parent/nested/child5/bar.txt");
+ refute_is_ignored("parent/nested/child6/bar.txt");
+ refute_is_ignored("parent/nested/child7/bar.txt");
+ assert_is_ignored("padded_parent/child8/bar.txt");
+}
+
+void test_ignore_status__unignore_entry_in_ignored_dir(void)
+{
+ static const char *test_files[] = {
+ "empty_standard_repo/bar.txt",
+ "empty_standard_repo/parent/bar.txt",
+ "empty_standard_repo/parent/child/bar.txt",
+ "empty_standard_repo/nested/parent/child/bar.txt",
+ NULL
+ };
+
+ make_test_data("empty_standard_repo", test_files);
+ cl_git_mkfile(
+ "empty_standard_repo/.gitignore",
+ "bar.txt\n"
+ "!parent/child/bar.txt\n");
+
+ assert_is_ignored("bar.txt");
+ assert_is_ignored("parent/bar.txt");
+ refute_is_ignored("parent/child/bar.txt");
+ assert_is_ignored("nested/parent/child/bar.txt");
+}
+
+void test_ignore_status__do_not_unignore_basename_prefix(void)
+{
+ static const char *test_files[] = {
+ "empty_standard_repo/foo_bar.txt",
+ NULL
+ };
+
+ make_test_data("empty_standard_repo", test_files);
+ cl_git_mkfile(
+ "empty_standard_repo/.gitignore",
+ "foo_bar.txt\n"
+ "!bar.txt\n");
+
+ assert_is_ignored("foo_bar.txt");
+}
+
+void test_ignore_status__filename_with_cr(void)
+{
+ int ignored;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+ cl_git_mkfile("empty_standard_repo/.gitignore", "Icon\r\r\n");
+
+ cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "Icon\r"));
+ cl_assert_equal_i(1, ignored);
+
+ cl_git_mkfile("empty_standard_repo/.gitignore", "Ico\rn\n");
+ cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "Ico\rn"));
+ cl_assert_equal_i(1, ignored);
+
+ cl_git_mkfile("empty_standard_repo/.gitignore", "Ico\rn\r\n");
+ cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "Ico\rn"));
+ cl_assert_equal_i(1, ignored);
+ cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "Ico\rn\r"));
+ cl_assert_equal_i(0, ignored);
+
+ cl_git_mkfile("empty_standard_repo/.gitignore", "Ico\rn\r\r\n");
+ cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "Ico\rn\r"));
+ cl_assert_equal_i(1, ignored);
+ cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "Icon\r"));
+ cl_assert_equal_i(0, ignored);
+
+ cl_git_mkfile("empty_standard_repo/.gitignore", "Icon\r\n");
+ cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "Icon\r"));
+ cl_assert_equal_i(0, ignored);
+ cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "Icon"));
+ cl_assert_equal_i(1, ignored);
+}
+
+void test_ignore_status__subdir_doesnt_match_above(void)
+{
+ int ignored, icase = 0, error;
+ git_config *cfg;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_pass(git_repository_config_snapshot(&cfg, g_repo));
+ error = git_config_get_bool(&icase, cfg, "core.ignorecase");
+ git_config_free(cfg);
+ if (error == GIT_ENOTFOUND)
+ error = 0;
+
+ cl_git_pass(error);
+
+ cl_git_pass(p_mkdir("empty_standard_repo/src", 0777));
+ cl_git_pass(p_mkdir("empty_standard_repo/src/src", 0777));
+ cl_git_mkfile("empty_standard_repo/src/.gitignore", "src\n");
+ cl_git_mkfile("empty_standard_repo/.gitignore", "");
+
+ cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "src/test.txt"));
+ cl_assert_equal_i(0, ignored);
+ cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "src/src/test.txt"));
+ cl_assert_equal_i(1, ignored);
+ cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "src/foo/test.txt"));
+ cl_assert_equal_i(0, ignored);
+
+ cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "SRC/src/test.txt"));
+ cl_assert_equal_i(icase, ignored);
+ cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "src/SRC/test.txt"));
+ cl_assert_equal_i(icase, ignored);
+}
+
+void test_ignore_status__negate_exact_previous(void)
+{
+ int ignored;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_mkfile("empty_standard_repo/.gitignore", "*.com\ntags\n!tags/\n.buildpath");
+ cl_git_mkfile("empty_standard_repo/.buildpath", "");
+ cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, ".buildpath"));
+ cl_assert_equal_i(1, ignored);
+}
+
+void test_ignore_status__negate_starstar(void)
+{
+ int ignored;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_mkfile("empty_standard_repo/.gitignore",
+ "code/projects/**/packages/*\n"
+ "!code/projects/**/packages/repositories.config");
+
+ cl_git_pass(git_futils_mkdir_r("empty_standard_repo/code/projects/foo/bar/packages", 0777));
+ cl_git_mkfile("empty_standard_repo/code/projects/foo/bar/packages/repositories.config", "");
+
+ cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "code/projects/foo/bar/packages/repositories.config"));
+ cl_assert_equal_i(0, ignored);
+}
+
+void test_ignore_status__ignore_all_toplevel_dirs_include_files(void)
+{
+ static const char *test_files[] = {
+ "empty_standard_repo/README.md",
+ "empty_standard_repo/src/main.c",
+ "empty_standard_repo/src/foo/foo.c",
+ "empty_standard_repo/dist/foo.o",
+ "empty_standard_repo/dist/main.o",
+ NULL
+ };
+
+ make_test_data("empty_standard_repo", test_files);
+ cl_git_mkfile(
+ "empty_standard_repo/.gitignore",
+ "/*/\n"
+ "!/src\n");
+
+ assert_is_ignored("dist/foo.o");
+ assert_is_ignored("dist/main.o");
+
+ refute_is_ignored("README.md");
+ refute_is_ignored("src/foo.c");
+ refute_is_ignored("src/foo/foo.c");
+}
+
+void test_ignore_status__subdir_ignore_all_toplevel_dirs_include_files(void)
+{
+ static const char *test_files[] = {
+ "empty_standard_repo/project/README.md",
+ "empty_standard_repo/project/src/main.c",
+ "empty_standard_repo/project/src/foo/foo.c",
+ "empty_standard_repo/project/dist/foo.o",
+ "empty_standard_repo/project/dist/main.o",
+ NULL
+ };
+
+ make_test_data("empty_standard_repo", test_files);
+ cl_git_mkfile(
+ "empty_standard_repo/project/.gitignore",
+ "/*/\n"
+ "!/src\n");
+
+ assert_is_ignored("project/dist/foo.o");
+ assert_is_ignored("project/dist/main.o");
+
+ refute_is_ignored("project/src/foo.c");
+ refute_is_ignored("project/src/foo/foo.c");
+ refute_is_ignored("project/README.md");
+}
+
+void test_ignore_status__subdir_ignore_everything_except_certain_files(void)
+{
+ static const char *test_files[] = {
+ "empty_standard_repo/project/README.md",
+ "empty_standard_repo/project/some_file",
+ "empty_standard_repo/project/src/main.c",
+ "empty_standard_repo/project/src/foo/foo.c",
+ "empty_standard_repo/project/dist/foo.o",
+ "empty_standard_repo/project/dist/main.o",
+ NULL
+ };
+
+ make_test_data("empty_standard_repo", test_files);
+ cl_git_mkfile(
+ "empty_standard_repo/project/.gitignore",
+ "/*\n"
+ "!/src\n"
+ "!README.md\n");
+
+ assert_is_ignored("project/some_file");
+ assert_is_ignored("project/dist/foo.o");
+ assert_is_ignored("project/dist/main.o");
+
+ refute_is_ignored("project/README.md");
+ refute_is_ignored("project/src/foo.c");
+ refute_is_ignored("project/src/foo/foo.c");
+}
+
+void test_ignore_status__deeper(void)
+{
+ const char *test_files[] = {
+ "empty_standard_repo/foo.data",
+ "empty_standard_repo/bar.data",
+ "empty_standard_repo/dont_ignore/foo.data",
+ "empty_standard_repo/dont_ignore/bar.data",
+ NULL
+ };
+
+ make_test_data("empty_standard_repo", test_files);
+ cl_git_mkfile("empty_standard_repo/.gitignore",
+ "*.data\n"
+ "!dont_ignore/*.data\n");
+
+ assert_is_ignored("foo.data");
+ assert_is_ignored("bar.data");
+
+ refute_is_ignored("dont_ignore/foo.data");
+ refute_is_ignored("dont_ignore/bar.data");
+}
+
+void test_ignore_status__unignored_dir_with_ignored_contents(void)
+{
+ static const char *test_files[] = {
+ "empty_standard_repo/dir/a.test",
+ "empty_standard_repo/dir/subdir/a.test",
+ NULL
+ };
+
+ make_test_data("empty_standard_repo", test_files);
+ cl_git_mkfile(
+ "empty_standard_repo/.gitignore",
+ "*.test\n"
+ "!dir/*\n");
+
+ refute_is_ignored("dir/a.test");
+ assert_is_ignored("dir/subdir/a.test");
+}
+
+void test_ignore_status__unignored_subdirs(void)
+{
+ static const char *test_files[] = {
+ "empty_standard_repo/dir/a.test",
+ "empty_standard_repo/dir/subdir/a.test",
+ NULL
+ };
+
+ make_test_data("empty_standard_repo", test_files);
+ cl_git_mkfile(
+ "empty_standard_repo/.gitignore",
+ "dir/*\n"
+ "!dir/*/\n");
+
+ assert_is_ignored("dir/a.test");
+ refute_is_ignored("dir/subdir/a.test");
+}
+
+void test_ignore_status__skips_bom(void)
+{
+ static const char *test_files[] = {
+ "empty_standard_repo/a.test",
+ "empty_standard_repo/b.test",
+ "empty_standard_repo/c.test",
+ "empty_standard_repo/foo.txt",
+ "empty_standard_repo/bar.txt",
+ NULL
+ };
+
+ make_test_data("empty_standard_repo", test_files);
+ cl_git_mkfile(
+ "empty_standard_repo/.gitignore",
+ "\xEF\xBB\xBF*.test\n");
+
+ assert_is_ignored("a.test");
+ assert_is_ignored("b.test");
+ assert_is_ignored("c.test");
+ refute_is_ignored("foo.txt");
+ refute_is_ignored("bar.txt");
+}
+
+void test_ignore_status__leading_spaces_are_significant(void)
+{
+ static const char *test_files[] = {
+ "empty_standard_repo/a.test",
+ "empty_standard_repo/b.test",
+ "empty_standard_repo/c.test",
+ "empty_standard_repo/d.test",
+ NULL
+ };
+
+ make_test_data("empty_standard_repo", test_files);
+ cl_git_mkfile(
+ "empty_standard_repo/.gitignore",
+ " a.test\n"
+ "# this is a comment\n"
+ "b.test\n"
+ "\tc.test\n"
+ " # not a comment\n"
+ "d.test\n");
+
+ refute_is_ignored("a.test");
+ assert_is_ignored(" a.test");
+ refute_is_ignored("# this is a comment");
+ assert_is_ignored("b.test");
+ refute_is_ignored("c.test");
+ assert_is_ignored("\tc.test");
+ assert_is_ignored(" # not a comment");
+ assert_is_ignored("d.test");
+}
+
+void test_ignore_status__override_nested_wildcard_unignore(void)
+{
+ git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
+ git_status_list *statuslist;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ const git_status_entry *status;
+
+ cl_git_pass(git_futils_mkdir_r("empty_standard_repo/dir", 0777));
+ cl_git_pass(git_futils_mkdir_r("empty_standard_repo/dir/subdir", 0777));
+ cl_git_mkfile("empty_standard_repo/.gitignore", "a.test\n");
+ cl_git_mkfile("empty_standard_repo/dir/.gitignore", "!*.test\n");
+ cl_git_mkfile("empty_standard_repo/dir/subdir/.gitignore", "a.test\n");
+ cl_git_mkfile("empty_standard_repo/dir/a.test", "pong");
+ cl_git_mkfile("empty_standard_repo/dir/subdir/a.test", "pong");
+
+ opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
+ opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED | GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
+
+ cl_git_pass(git_status_list_new(&statuslist, repo, &opts));
+ cl_assert_equal_sz(4, git_status_list_entrycount(statuslist));
+
+ status = git_status_byindex(statuslist, 0);
+ cl_assert(status != NULL);
+ cl_assert_equal_s(".gitignore", status->index_to_workdir->old_file.path);
+ cl_assert_equal_i(GIT_STATUS_WT_NEW, status->status);
+
+ status = git_status_byindex(statuslist, 1);
+ cl_assert(status != NULL);
+ cl_assert_equal_s("dir/.gitignore", status->index_to_workdir->old_file.path);
+ cl_assert_equal_i(GIT_STATUS_WT_NEW, status->status);
+
+ status = git_status_byindex(statuslist, 2);
+ cl_assert(status != NULL);
+ cl_assert_equal_s("dir/a.test", status->index_to_workdir->old_file.path);
+ cl_assert_equal_i(GIT_STATUS_WT_NEW, status->status);
+
+ status = git_status_byindex(statuslist, 3);
+ cl_assert(status != NULL);
+ cl_assert_equal_s("dir/subdir/.gitignore", status->index_to_workdir->old_file.path);
+ cl_assert_equal_i(GIT_STATUS_WT_NEW, status->status);
+
+ git_status_list_free(statuslist);
+}
diff --git a/tests/libgit2/index/add.c b/tests/libgit2/index/add.c
new file mode 100644
index 0000000..588a2ad
--- /dev/null
+++ b/tests/libgit2/index/add.c
@@ -0,0 +1,108 @@
+#include "clar_libgit2.h"
+
+static git_repository *g_repo = NULL;
+static git_index *g_index = NULL;
+
+static const char *valid_blob_id = "fa49b077972391ad58037050f2a75f74e3671e92";
+static const char *valid_tree_id = "181037049a54a1eb5fab404658a3a250b44335d7";
+static const char *valid_commit_id = "763d71aadf09a7951596c9746c024e7eece7c7af";
+static const char *invalid_id = "1234567890123456789012345678901234567890";
+
+void test_index_add__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+ cl_git_pass(git_repository_index(&g_index, g_repo));
+}
+
+void test_index_add__cleanup(void)
+{
+ git_index_free(g_index);
+ cl_git_sandbox_cleanup();
+ g_repo = NULL;
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 1));
+}
+
+static void test_add_entry(
+ bool should_succeed, const char *idstr, git_filemode_t mode)
+{
+ git_index_entry entry = {{0}};
+
+ cl_git_pass(git_oid__fromstr(&entry.id, idstr, GIT_OID_SHA1));
+
+ entry.path = mode == GIT_FILEMODE_TREE ? "test_folder" : "test_file";
+ entry.mode = mode;
+
+ if (should_succeed)
+ cl_git_pass(git_index_add(g_index, &entry));
+ else
+ cl_git_fail(git_index_add(g_index, &entry));
+}
+
+void test_index_add__invalid_entries_succeeds_by_default(void)
+{
+ /*
+ * Ensure that there is validation on object ids by default
+ */
+
+ /* ensure that we can add some actually good entries */
+ test_add_entry(true, valid_blob_id, GIT_FILEMODE_BLOB);
+ test_add_entry(true, valid_blob_id, GIT_FILEMODE_BLOB_EXECUTABLE);
+ test_add_entry(true, valid_blob_id, GIT_FILEMODE_LINK);
+
+ /* test that we fail to add some invalid (missing) blobs and trees */
+ test_add_entry(false, invalid_id, GIT_FILEMODE_BLOB);
+ test_add_entry(false, invalid_id, GIT_FILEMODE_BLOB_EXECUTABLE);
+ test_add_entry(false, invalid_id, GIT_FILEMODE_LINK);
+
+ /* test that we validate the types of objects */
+ test_add_entry(false, valid_commit_id, GIT_FILEMODE_BLOB);
+ test_add_entry(false, valid_tree_id, GIT_FILEMODE_BLOB_EXECUTABLE);
+ test_add_entry(false, valid_commit_id, GIT_FILEMODE_LINK);
+
+ /*
+ * Ensure that there we can disable validation
+ */
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 0));
+
+ /* ensure that we can add some actually good entries */
+ test_add_entry(true, valid_blob_id, GIT_FILEMODE_BLOB);
+ test_add_entry(true, valid_blob_id, GIT_FILEMODE_BLOB_EXECUTABLE);
+ test_add_entry(true, valid_blob_id, GIT_FILEMODE_LINK);
+
+ /* test that we can now add some invalid (missing) blobs and trees */
+ test_add_entry(true, invalid_id, GIT_FILEMODE_BLOB);
+ test_add_entry(true, invalid_id, GIT_FILEMODE_BLOB_EXECUTABLE);
+ test_add_entry(true, invalid_id, GIT_FILEMODE_LINK);
+
+ /* test that we do not validate the types of objects */
+ test_add_entry(true, valid_commit_id, GIT_FILEMODE_BLOB);
+ test_add_entry(true, valid_tree_id, GIT_FILEMODE_BLOB_EXECUTABLE);
+ test_add_entry(true, valid_commit_id, GIT_FILEMODE_LINK);
+}
+
+void test_index_add__two_slash_prefixed(void)
+{
+ git_index_entry one = {{0}}, two = {{0}};
+ const git_index_entry *result;
+ size_t orig_count;
+
+ orig_count = git_index_entrycount(g_index);
+
+ cl_git_pass(git_oid__fromstr(&one.id, "fa49b077972391ad58037050f2a75f74e3671e92", GIT_OID_SHA1));
+ one.path = "/a";
+ one.mode = GIT_FILEMODE_BLOB;
+
+ cl_git_pass(git_oid__fromstr(&two.id, "3697d64be941a53d4ae8f6a271e4e3fa56b022cc", GIT_OID_SHA1));
+ two.path = "/a";
+ two.mode = GIT_FILEMODE_BLOB;
+
+ cl_git_pass(git_index_add(g_index, &one));
+ cl_git_pass(git_index_add(g_index, &two));
+
+ cl_assert_equal_i(orig_count + 1, git_index_entrycount(g_index));
+
+ cl_assert(result = git_index_get_bypath(g_index, "/a", 0));
+ cl_assert_equal_oid(&two.id, &result->id);
+}
diff --git a/tests/libgit2/index/addall.c b/tests/libgit2/index/addall.c
new file mode 100644
index 0000000..e76b6e8
--- /dev/null
+++ b/tests/libgit2/index/addall.c
@@ -0,0 +1,542 @@
+#include "clar_libgit2.h"
+#include "../status/status_helpers.h"
+#include "posix.h"
+#include "futils.h"
+
+static git_repository *g_repo = NULL;
+#define TEST_DIR "addall"
+
+void test_index_addall__initialize(void)
+{
+}
+
+void test_index_addall__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+#define STATUS_INDEX_FLAGS \
+ (GIT_STATUS_INDEX_NEW | GIT_STATUS_INDEX_MODIFIED | \
+ GIT_STATUS_INDEX_DELETED | GIT_STATUS_INDEX_RENAMED | \
+ GIT_STATUS_INDEX_TYPECHANGE)
+
+#define STATUS_WT_FLAGS \
+ (GIT_STATUS_WT_NEW | GIT_STATUS_WT_MODIFIED | \
+ GIT_STATUS_WT_DELETED | GIT_STATUS_WT_TYPECHANGE | \
+ GIT_STATUS_WT_RENAMED)
+
+typedef struct {
+ size_t index_adds;
+ size_t index_dels;
+ size_t index_mods;
+ size_t wt_adds;
+ size_t wt_dels;
+ size_t wt_mods;
+ size_t ignores;
+ size_t conflicts;
+} index_status_counts;
+
+static int index_status_cb(
+ const char *path, unsigned int status_flags, void *payload)
+{
+ index_status_counts *vals = payload;
+
+ /* cb_status__print(path, status_flags, NULL); */
+
+ GIT_UNUSED(path);
+
+ if (status_flags & GIT_STATUS_INDEX_NEW)
+ vals->index_adds++;
+ if (status_flags & GIT_STATUS_INDEX_MODIFIED)
+ vals->index_mods++;
+ if (status_flags & GIT_STATUS_INDEX_DELETED)
+ vals->index_dels++;
+ if (status_flags & GIT_STATUS_INDEX_TYPECHANGE)
+ vals->index_mods++;
+
+ if (status_flags & GIT_STATUS_WT_NEW)
+ vals->wt_adds++;
+ if (status_flags & GIT_STATUS_WT_MODIFIED)
+ vals->wt_mods++;
+ if (status_flags & GIT_STATUS_WT_DELETED)
+ vals->wt_dels++;
+ if (status_flags & GIT_STATUS_WT_TYPECHANGE)
+ vals->wt_mods++;
+
+ if (status_flags & GIT_STATUS_IGNORED)
+ vals->ignores++;
+ if (status_flags & GIT_STATUS_CONFLICTED)
+ vals->conflicts++;
+
+ return 0;
+}
+
+static void check_status_at_line(
+ git_repository *repo,
+ size_t index_adds, size_t index_dels, size_t index_mods,
+ size_t wt_adds, size_t wt_dels, size_t wt_mods, size_t ignores,
+ size_t conflicts, const char *file, const char *func, int line)
+{
+ index_status_counts vals;
+
+ memset(&vals, 0, sizeof(vals));
+
+ cl_git_pass(git_status_foreach(repo, index_status_cb, &vals));
+
+ clar__assert_equal(
+ file,func,line,"wrong index adds", 1, "%"PRIuZ, index_adds, vals.index_adds);
+ clar__assert_equal(
+ file,func,line,"wrong index dels", 1, "%"PRIuZ, index_dels, vals.index_dels);
+ clar__assert_equal(
+ file,func,line,"wrong index mods", 1, "%"PRIuZ, index_mods, vals.index_mods);
+ clar__assert_equal(
+ file,func,line,"wrong workdir adds", 1, "%"PRIuZ, wt_adds, vals.wt_adds);
+ clar__assert_equal(
+ file,func,line,"wrong workdir dels", 1, "%"PRIuZ, wt_dels, vals.wt_dels);
+ clar__assert_equal(
+ file,func,line,"wrong workdir mods", 1, "%"PRIuZ, wt_mods, vals.wt_mods);
+ clar__assert_equal(
+ file,func,line,"wrong ignores", 1, "%"PRIuZ, ignores, vals.ignores);
+ clar__assert_equal(
+ file,func,line,"wrong conflicts", 1, "%"PRIuZ, conflicts, vals.conflicts);
+}
+
+#define check_status(R,IA,ID,IM,WA,WD,WM,IG,C) \
+ check_status_at_line(R,IA,ID,IM,WA,WD,WM,IG,C,__FILE__,__func__,__LINE__)
+
+static void check_stat_data(git_index *index, const char *path, bool match)
+{
+ const git_index_entry *entry;
+ struct stat st;
+
+ cl_must_pass(p_lstat(path, &st));
+
+ /* skip repo base dir name */
+ while (*path != '/')
+ ++path;
+ ++path;
+
+ entry = git_index_get_bypath(index, path, 0);
+ cl_assert(entry);
+
+ if (match) {
+ cl_assert(st.st_ctime == entry->ctime.seconds);
+ cl_assert(st.st_mtime == entry->mtime.seconds);
+ cl_assert(st.st_size == entry->file_size);
+ cl_assert((uint32_t)st.st_uid == entry->uid);
+ cl_assert((uint32_t)st.st_gid == entry->gid);
+ cl_assert_equal_i_fmt(
+ GIT_MODE_TYPE(st.st_mode), GIT_MODE_TYPE(entry->mode), "%07o");
+ if (cl_is_chmod_supported())
+ cl_assert_equal_b(
+ GIT_PERMS_IS_EXEC(st.st_mode), GIT_PERMS_IS_EXEC(entry->mode));
+ } else {
+ /* most things will still match */
+ cl_assert(st.st_size != entry->file_size);
+ /* would check mtime, but with second resolution it won't work :( */
+ }
+}
+
+static void addall_create_test_repo(bool check_every_step)
+{
+ g_repo = cl_git_sandbox_init_new(TEST_DIR);
+
+ if (check_every_step)
+ check_status(g_repo, 0, 0, 0, 0, 0, 0, 0, 0);
+
+ cl_git_mkfile(TEST_DIR "/file.foo", "a file");
+ if (check_every_step)
+ check_status(g_repo, 0, 0, 0, 1, 0, 0, 0, 0);
+
+ cl_git_mkfile(TEST_DIR "/.gitignore", "*.foo\n");
+ if (check_every_step)
+ check_status(g_repo, 0, 0, 0, 1, 0, 0, 1, 0);
+
+ cl_git_mkfile(TEST_DIR "/file.bar", "another file");
+ if (check_every_step)
+ check_status(g_repo, 0, 0, 0, 2, 0, 0, 1, 0);
+}
+
+void test_index_addall__repo_lifecycle(void)
+{
+ int error;
+ git_index *index;
+ git_strarray paths = { NULL, 0 };
+ char *strs[1];
+
+ addall_create_test_repo(true);
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ strs[0] = "file.*";
+ paths.strings = strs;
+ paths.count = 1;
+
+ cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL));
+ cl_git_pass(git_index_write(index));
+ check_stat_data(index, TEST_DIR "/file.bar", true);
+ check_status(g_repo, 1, 0, 0, 1, 0, 0, 1, 0);
+
+ cl_git_rewritefile(TEST_DIR "/file.bar", "new content for file");
+ check_stat_data(index, TEST_DIR "/file.bar", false);
+ check_status(g_repo, 1, 0, 0, 1, 0, 1, 1, 0);
+
+ cl_git_mkfile(TEST_DIR "/file.zzz", "yet another one");
+ cl_git_mkfile(TEST_DIR "/other.zzz", "yet another one");
+ cl_git_mkfile(TEST_DIR "/more.zzz", "yet another one");
+ check_status(g_repo, 1, 0, 0, 4, 0, 1, 1, 0);
+
+ cl_git_pass(git_index_update_all(index, NULL, NULL, NULL));
+ check_stat_data(index, TEST_DIR "/file.bar", true);
+ check_status(g_repo, 1, 0, 0, 4, 0, 0, 1, 0);
+
+ cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL));
+ cl_git_pass(git_index_write(index));
+ check_stat_data(index, TEST_DIR "/file.zzz", true);
+ check_status(g_repo, 2, 0, 0, 3, 0, 0, 1, 0);
+
+ cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "first commit");
+ check_status(g_repo, 0, 0, 0, 3, 0, 0, 1, 0);
+
+ if (cl_repo_get_bool(g_repo, "core.filemode")) {
+ cl_git_pass(git_index_update_all(index, NULL, NULL, NULL));
+ cl_must_pass(p_chmod(TEST_DIR "/file.zzz", 0777));
+ cl_git_pass(git_index_update_all(index, NULL, NULL, NULL));
+ check_status(g_repo, 0, 0, 1, 3, 0, 0, 1, 0);
+
+ /* go back to what we had before */
+ cl_must_pass(p_chmod(TEST_DIR "/file.zzz", 0666));
+ cl_git_pass(git_index_update_all(index, NULL, NULL, NULL));
+ check_status(g_repo, 0, 0, 0, 3, 0, 0, 1, 0);
+ }
+
+
+ /* attempt to add an ignored file - does nothing */
+ strs[0] = "file.foo";
+ cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL));
+ cl_git_pass(git_index_write(index));
+ check_status(g_repo, 0, 0, 0, 3, 0, 0, 1, 0);
+
+ /* add with check - should generate error */
+ error = git_index_add_all(
+ index, &paths, GIT_INDEX_ADD_CHECK_PATHSPEC, NULL, NULL);
+ cl_assert_equal_i(GIT_EINVALIDSPEC, error);
+ cl_git_pass(git_index_write(index));
+ check_status(g_repo, 0, 0, 0, 3, 0, 0, 1, 0);
+
+ /* add with force - should allow */
+ cl_git_pass(git_index_add_all(
+ index, &paths, GIT_INDEX_ADD_FORCE, NULL, NULL));
+ cl_git_pass(git_index_write(index));
+ check_stat_data(index, TEST_DIR "/file.foo", true);
+ check_status(g_repo, 1, 0, 0, 3, 0, 0, 0, 0);
+
+ /* now it's in the index, so regular add should work */
+ cl_git_rewritefile(TEST_DIR "/file.foo", "new content for file");
+ check_stat_data(index, TEST_DIR "/file.foo", false);
+ check_status(g_repo, 1, 0, 0, 3, 0, 1, 0, 0);
+
+ cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL));
+ cl_git_pass(git_index_write(index));
+ check_stat_data(index, TEST_DIR "/file.foo", true);
+ check_status(g_repo, 1, 0, 0, 3, 0, 0, 0, 0);
+
+ cl_git_pass(git_index_add_bypath(index, "more.zzz"));
+ check_stat_data(index, TEST_DIR "/more.zzz", true);
+ check_status(g_repo, 2, 0, 0, 2, 0, 0, 0, 0);
+
+ cl_git_rewritefile(TEST_DIR "/file.zzz", "new content for file");
+ check_status(g_repo, 2, 0, 0, 2, 0, 1, 0, 0);
+
+ cl_git_pass(git_index_add_bypath(index, "file.zzz"));
+ check_stat_data(index, TEST_DIR "/file.zzz", true);
+ check_status(g_repo, 2, 0, 1, 2, 0, 0, 0, 0);
+
+ strs[0] = "*.zzz";
+ cl_git_pass(git_index_remove_all(index, &paths, NULL, NULL));
+ check_status(g_repo, 1, 1, 0, 4, 0, 0, 0, 0);
+
+ cl_git_pass(git_index_add_bypath(index, "file.zzz"));
+ check_status(g_repo, 1, 0, 1, 3, 0, 0, 0, 0);
+
+ cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "second commit");
+ check_status(g_repo, 0, 0, 0, 3, 0, 0, 0, 0);
+
+ cl_must_pass(p_unlink(TEST_DIR "/file.zzz"));
+ check_status(g_repo, 0, 0, 0, 3, 1, 0, 0, 0);
+
+ /* update_all should be able to remove entries */
+ cl_git_pass(git_index_update_all(index, NULL, NULL, NULL));
+ check_status(g_repo, 0, 1, 0, 3, 0, 0, 0, 0);
+
+ strs[0] = "*";
+ cl_git_pass(git_index_add_all(index, &paths, 0, NULL, NULL));
+ cl_git_pass(git_index_write(index));
+ check_status(g_repo, 3, 1, 0, 0, 0, 0, 0, 0);
+
+ /* must be able to remove at any position while still updating other files */
+ cl_must_pass(p_unlink(TEST_DIR "/.gitignore"));
+ cl_git_rewritefile(TEST_DIR "/file.zzz", "reconstructed file");
+ cl_git_rewritefile(TEST_DIR "/more.zzz", "altered file reality");
+ check_status(g_repo, 3, 1, 0, 1, 1, 1, 0, 0);
+
+ cl_git_pass(git_index_update_all(index, NULL, NULL, NULL));
+ check_status(g_repo, 2, 1, 0, 1, 0, 0, 0, 0);
+ /* this behavior actually matches 'git add -u' where "file.zzz" has
+ * been removed from the index, so when you go to update, even though
+ * it exists in the HEAD, it is not re-added to the index, leaving it
+ * as a DELETE when comparing HEAD to index and as an ADD comparing
+ * index to worktree
+ */
+
+ git_index_free(index);
+}
+
+void test_index_addall__files_in_folders(void)
+{
+ git_index *index;
+
+ addall_create_test_repo(true);
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_pass(git_index_add_all(index, NULL, 0, NULL, NULL));
+ cl_git_pass(git_index_write(index));
+ check_stat_data(index, TEST_DIR "/file.bar", true);
+ check_status(g_repo, 2, 0, 0, 0, 0, 0, 1, 0);
+
+ cl_must_pass(p_mkdir(TEST_DIR "/subdir", 0777));
+ cl_git_mkfile(TEST_DIR "/subdir/file", "hello!\n");
+ check_status(g_repo, 2, 0, 0, 1, 0, 0, 1, 0);
+
+ cl_git_pass(git_index_add_all(index, NULL, 0, NULL, NULL));
+ cl_git_pass(git_index_write(index));
+ check_status(g_repo, 3, 0, 0, 0, 0, 0, 1, 0);
+
+ git_index_free(index);
+}
+
+void test_index_addall__hidden_files(void)
+{
+#ifdef GIT_WIN32
+ git_index *index;
+
+ addall_create_test_repo(true);
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_pass(git_index_add_all(index, NULL, 0, NULL, NULL));
+ cl_git_pass(git_index_write(index));
+ check_stat_data(index, TEST_DIR "/file.bar", true);
+ check_status(g_repo, 2, 0, 0, 0, 0, 0, 1, 0);
+
+ cl_git_mkfile(TEST_DIR "/file.zzz", "yet another one");
+ cl_git_mkfile(TEST_DIR "/more.zzz", "yet another one");
+ cl_git_mkfile(TEST_DIR "/other.zzz", "yet another one");
+
+ check_status(g_repo, 2, 0, 0, 3, 0, 0, 1, 0);
+
+ cl_git_pass(git_win32__set_hidden(TEST_DIR "/file.zzz", true));
+ cl_git_pass(git_win32__set_hidden(TEST_DIR "/more.zzz", true));
+ cl_git_pass(git_win32__set_hidden(TEST_DIR "/other.zzz", true));
+
+ check_status(g_repo, 2, 0, 0, 3, 0, 0, 1, 0);
+
+ cl_git_pass(git_index_add_all(index, NULL, 0, NULL, NULL));
+ cl_git_pass(git_index_write(index));
+ check_stat_data(index, TEST_DIR "/file.bar", true);
+ check_status(g_repo, 5, 0, 0, 0, 0, 0, 1, 0);
+
+ git_index_free(index);
+#endif
+}
+
+static int addall_match_prefix(
+ const char *path, const char *matched_pathspec, void *payload)
+{
+ GIT_UNUSED(matched_pathspec);
+ return !git__prefixcmp(path, payload) ? 0 : 1;
+}
+
+static int addall_match_suffix(
+ const char *path, const char *matched_pathspec, void *payload)
+{
+ GIT_UNUSED(matched_pathspec);
+ return !git__suffixcmp(path, payload) ? 0 : 1;
+}
+
+static int addall_cancel_at(
+ const char *path, const char *matched_pathspec, void *payload)
+{
+ GIT_UNUSED(matched_pathspec);
+ return !strcmp(path, payload) ? -123 : 0;
+}
+
+void test_index_addall__callback_filtering(void)
+{
+ git_index *index;
+
+ addall_create_test_repo(false);
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_pass(
+ git_index_add_all(index, NULL, 0, addall_match_prefix, "file."));
+ cl_git_pass(git_index_write(index));
+ check_stat_data(index, TEST_DIR "/file.bar", true);
+ check_status(g_repo, 1, 0, 0, 1, 0, 0, 1, 0);
+
+ cl_git_mkfile(TEST_DIR "/file.zzz", "yet another one");
+ cl_git_mkfile(TEST_DIR "/more.zzz", "yet another one");
+ cl_git_mkfile(TEST_DIR "/other.zzz", "yet another one");
+
+ cl_git_pass(git_index_update_all(index, NULL, NULL, NULL));
+ check_stat_data(index, TEST_DIR "/file.bar", true);
+ check_status(g_repo, 1, 0, 0, 4, 0, 0, 1, 0);
+
+ cl_git_pass(
+ git_index_add_all(index, NULL, 0, addall_match_prefix, "other"));
+ cl_git_pass(git_index_write(index));
+ check_stat_data(index, TEST_DIR "/other.zzz", true);
+ check_status(g_repo, 2, 0, 0, 3, 0, 0, 1, 0);
+
+ cl_git_pass(
+ git_index_add_all(index, NULL, 0, addall_match_suffix, ".zzz"));
+ cl_git_pass(git_index_write(index));
+ check_status(g_repo, 4, 0, 0, 1, 0, 0, 1, 0);
+
+ cl_git_pass(
+ git_index_remove_all(index, NULL, addall_match_suffix, ".zzz"));
+ check_status(g_repo, 1, 0, 0, 4, 0, 0, 1, 0);
+
+ cl_git_fail_with(
+ git_index_add_all(index, NULL, 0, addall_cancel_at, "more.zzz"), -123);
+ check_status(g_repo, 3, 0, 0, 2, 0, 0, 1, 0);
+
+ cl_git_fail_with(
+ git_index_add_all(index, NULL, 0, addall_cancel_at, "other.zzz"), -123);
+ check_status(g_repo, 4, 0, 0, 1, 0, 0, 1, 0);
+
+ cl_git_pass(
+ git_index_add_all(index, NULL, 0, addall_match_suffix, ".zzz"));
+ cl_git_pass(git_index_write(index));
+ check_status(g_repo, 5, 0, 0, 0, 0, 0, 1, 0);
+
+ cl_must_pass(p_unlink(TEST_DIR "/file.zzz"));
+ cl_must_pass(p_unlink(TEST_DIR "/more.zzz"));
+ cl_must_pass(p_unlink(TEST_DIR "/other.zzz"));
+
+ cl_git_fail_with(
+ git_index_update_all(index, NULL, addall_cancel_at, "more.zzz"), -123);
+ /* file.zzz removed from index (so Index Adds 5 -> 4) and
+ * more.zzz + other.zzz removed (so Worktree Dels 0 -> 2) */
+ check_status(g_repo, 4, 0, 0, 0, 2, 0, 1, 0);
+
+ cl_git_fail_with(
+ git_index_update_all(index, NULL, addall_cancel_at, "other.zzz"), -123);
+ /* more.zzz removed from index (so Index Adds 4 -> 3) and
+ * Just other.zzz removed (so Worktree Dels 2 -> 1) */
+ check_status(g_repo, 3, 0, 0, 0, 1, 0, 1, 0);
+
+ git_index_free(index);
+}
+
+void test_index_addall__handles_ignored_files_in_directory(void)
+{
+ git_index *index;
+
+ g_repo = cl_git_sandbox_init_new(TEST_DIR);
+
+ cl_git_mkfile(TEST_DIR "/file.foo", "a file");
+ cl_git_mkfile(TEST_DIR "/file.bar", "another file");
+ cl_must_pass(p_mkdir(TEST_DIR "/folder", 0777));
+ cl_git_mkfile(TEST_DIR "/folder/asdf", "yet another file");
+
+ cl_git_mkfile(TEST_DIR "/.gitignore", "folder/\n");
+
+ check_status(g_repo, 0, 0, 0, 3, 0, 0, 1, 0);
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_add_all(index, NULL, 0, NULL, NULL));
+
+ check_status(g_repo, 3, 0, 0, 0, 0, 0, 1, 0);
+
+ git_index_free(index);
+}
+
+void test_index_addall__force_adds_ignored_directories(void)
+{
+ git_index *index;
+
+ g_repo = cl_git_sandbox_init_new(TEST_DIR);
+
+ cl_git_mkfile(TEST_DIR "/file.foo", "a file");
+ cl_git_mkfile(TEST_DIR "/file.bar", "another file");
+ cl_must_pass(p_mkdir(TEST_DIR "/folder", 0777));
+ cl_git_mkfile(TEST_DIR "/folder/asdf", "yet another file");
+
+ cl_git_mkfile(TEST_DIR "/.gitignore", "folder/\n");
+
+ check_status(g_repo, 0, 0, 0, 3, 0, 0, 1, 0);
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_add_all(index, NULL, GIT_INDEX_ADD_FORCE, NULL, NULL));
+
+ check_status(g_repo, 4, 0, 0, 0, 0, 0, 0, 0);
+
+ git_index_free(index);
+}
+
+void test_index_addall__adds_conflicts(void)
+{
+ git_index *index;
+ git_reference *ref;
+ git_annotated_commit *annotated;
+
+ g_repo = cl_git_sandbox_init("merge-resolve");
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ check_status(g_repo, 0, 0, 0, 0, 0, 0, 0, 0);
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/heads/branch"));
+ cl_git_pass(git_annotated_commit_from_ref(&annotated, g_repo, ref));
+
+ cl_git_pass(git_merge(g_repo, (const git_annotated_commit**)&annotated, 1, NULL, NULL));
+ check_status(g_repo, 0, 1, 2, 0, 0, 0, 0, 1);
+
+ cl_git_pass(git_index_add_all(index, NULL, 0, NULL, NULL));
+ cl_git_pass(git_index_write(index));
+ check_status(g_repo, 0, 1, 3, 0, 0, 0, 0, 0);
+
+ git_annotated_commit_free(annotated);
+ git_reference_free(ref);
+ git_index_free(index);
+}
+
+void test_index_addall__removes_deleted_conflicted_files(void)
+{
+ git_index *index;
+ git_reference *ref;
+ git_annotated_commit *annotated;
+
+ g_repo = cl_git_sandbox_init("merge-resolve");
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ check_status(g_repo, 0, 0, 0, 0, 0, 0, 0, 0);
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/heads/branch"));
+ cl_git_pass(git_annotated_commit_from_ref(&annotated, g_repo, ref));
+
+ cl_git_pass(git_merge(g_repo, (const git_annotated_commit**)&annotated, 1, NULL, NULL));
+ check_status(g_repo, 0, 1, 2, 0, 0, 0, 0, 1);
+
+ cl_git_rmfile("merge-resolve/conflicting.txt");
+
+ cl_git_pass(git_index_add_all(index, NULL, 0, NULL, NULL));
+ cl_git_pass(git_index_write(index));
+ check_status(g_repo, 0, 2, 2, 0, 0, 0, 0, 0);
+
+ git_annotated_commit_free(annotated);
+ git_reference_free(ref);
+ git_index_free(index);
+}
diff --git a/tests/libgit2/index/bypath.c b/tests/libgit2/index/bypath.c
new file mode 100644
index 0000000..15c11af
--- /dev/null
+++ b/tests/libgit2/index/bypath.c
@@ -0,0 +1,359 @@
+#include "clar_libgit2.h"
+#include "repository.h"
+#include "../submodule/submodule_helpers.h"
+
+static git_repository *g_repo;
+static git_index *g_idx;
+
+void test_index_bypath__initialize(void)
+{
+ g_repo = setup_fixture_submod2();
+ cl_git_pass(git_repository_index__weakptr(&g_idx, g_repo));
+}
+
+void test_index_bypath__cleanup(void)
+{
+ g_repo = NULL;
+ g_idx = NULL;
+}
+
+void test_index_bypath__add_directory(void)
+{
+ cl_git_fail_with(GIT_EDIRECTORY, git_index_add_bypath(g_idx, "just_a_dir"));
+}
+
+void test_index_bypath__add_submodule(void)
+{
+ unsigned int status;
+ const char *sm_name = "sm_changed_head";
+
+ cl_git_pass(git_submodule_status(&status, g_repo, sm_name, 0));
+ cl_assert_equal_i(GIT_SUBMODULE_STATUS_WD_MODIFIED, status & GIT_SUBMODULE_STATUS_WD_MODIFIED);
+ cl_git_pass(git_index_add_bypath(g_idx, sm_name));
+ cl_git_pass(git_submodule_status(&status, g_repo, sm_name, 0));
+ cl_assert_equal_i(0, status & GIT_SUBMODULE_STATUS_WD_MODIFIED);
+}
+
+void test_index_bypath__add_submodule_unregistered(void)
+{
+ const char *sm_name = "not-submodule";
+ const char *sm_head = "68e92c611b80ee1ed8f38314ff9577f0d15b2444";
+ const git_index_entry *entry;
+
+ cl_git_pass(git_index_add_bypath(g_idx, sm_name));
+
+ cl_assert(entry = git_index_get_bypath(g_idx, sm_name, 0));
+ cl_assert_equal_s(sm_head, git_oid_tostr_s(&entry->id));
+ cl_assert_equal_s(sm_name, entry->path);
+}
+
+void test_index_bypath__add_hidden(void)
+{
+#ifdef GIT_WIN32
+ const git_index_entry *entry;
+ bool hidden;
+
+ cl_git_mkfile("submod2/hidden_file", "you can't see me");
+
+ cl_git_pass(git_win32__hidden(&hidden, "submod2/hidden_file"));
+ cl_assert(!hidden);
+
+ cl_git_pass(git_win32__set_hidden("submod2/hidden_file", true));
+
+ cl_git_pass(git_win32__hidden(&hidden, "submod2/hidden_file"));
+ cl_assert(hidden);
+
+ cl_git_pass(git_index_add_bypath(g_idx, "hidden_file"));
+
+ cl_assert(entry = git_index_get_bypath(g_idx, "hidden_file", 0));
+ cl_assert_equal_i(GIT_FILEMODE_BLOB, entry->mode);
+#endif
+}
+
+void test_index_bypath__add_keeps_existing_case(void)
+{
+ const git_index_entry *entry;
+
+ if (!cl_repo_get_bool(g_repo, "core.ignorecase"))
+ clar__skip();
+
+ cl_git_mkfile("submod2/just_a_dir/file1.txt", "This is a file");
+ cl_git_pass(git_index_add_bypath(g_idx, "just_a_dir/file1.txt"));
+
+ cl_assert(entry = git_index_get_bypath(g_idx, "just_a_dir/file1.txt", 0));
+ cl_assert_equal_s("just_a_dir/file1.txt", entry->path);
+
+ cl_git_rewritefile("submod2/just_a_dir/file1.txt", "Updated!");
+ cl_git_pass(git_index_add_bypath(g_idx, "just_a_dir/FILE1.txt"));
+
+ cl_assert(entry = git_index_get_bypath(g_idx, "just_a_dir/FILE1.txt", 0));
+ cl_assert_equal_s("just_a_dir/file1.txt", entry->path);
+}
+
+void test_index_bypath__add_honors_existing_case(void)
+{
+ const git_index_entry *entry;
+
+ if (!cl_repo_get_bool(g_repo, "core.ignorecase"))
+ clar__skip();
+
+ cl_git_mkfile("submod2/just_a_dir/file1.txt", "This is a file");
+ cl_git_mkfile("submod2/just_a_dir/file2.txt", "This is another file");
+ cl_git_mkfile("submod2/just_a_dir/file3.txt", "This is another file");
+ cl_git_mkfile("submod2/just_a_dir/file4.txt", "And another file");
+
+ cl_git_pass(git_index_add_bypath(g_idx, "just_a_dir/File1.txt"));
+ cl_git_pass(git_index_add_bypath(g_idx, "JUST_A_DIR/file2.txt"));
+ cl_git_pass(git_index_add_bypath(g_idx, "Just_A_Dir/FILE3.txt"));
+
+ cl_assert(entry = git_index_get_bypath(g_idx, "just_a_dir/File1.txt", 0));
+ cl_assert_equal_s("just_a_dir/File1.txt", entry->path);
+
+ cl_assert(entry = git_index_get_bypath(g_idx, "JUST_A_DIR/file2.txt", 0));
+ cl_assert_equal_s("just_a_dir/file2.txt", entry->path);
+
+ cl_assert(entry = git_index_get_bypath(g_idx, "Just_A_Dir/FILE3.txt", 0));
+ cl_assert_equal_s("just_a_dir/FILE3.txt", entry->path);
+
+ cl_git_rewritefile("submod2/just_a_dir/file3.txt", "Rewritten");
+ cl_git_pass(git_index_add_bypath(g_idx, "Just_A_Dir/file3.txt"));
+
+ cl_assert(entry = git_index_get_bypath(g_idx, "Just_A_Dir/file3.txt", 0));
+ cl_assert_equal_s("just_a_dir/FILE3.txt", entry->path);
+}
+
+void test_index_bypath__add_honors_existing_case_2(void)
+{
+ git_index_entry dummy = { { 0 } };
+ const git_index_entry *entry;
+
+ if (!cl_repo_get_bool(g_repo, "core.ignorecase"))
+ clar__skip();
+
+ dummy.mode = GIT_FILEMODE_BLOB;
+ cl_git_pass(git_oid__fromstr(&dummy.id, "f990a25a74d1a8281ce2ab018ea8df66795cd60b", GIT_OID_SHA1));
+
+ /* note that `git_index_add` does no checking to canonical directories */
+ dummy.path = "Just_a_dir/file0.txt";
+ cl_git_pass(git_index_add(g_idx, &dummy));
+
+ dummy.path = "just_a_dir/fileA.txt";
+ cl_git_pass(git_index_add(g_idx, &dummy));
+
+ dummy.path = "Just_A_Dir/fileB.txt";
+ cl_git_pass(git_index_add(g_idx, &dummy));
+
+ dummy.path = "JUST_A_DIR/fileC.txt";
+ cl_git_pass(git_index_add(g_idx, &dummy));
+
+ dummy.path = "just_A_dir/fileD.txt";
+ cl_git_pass(git_index_add(g_idx, &dummy));
+
+ dummy.path = "JUST_a_DIR/fileE.txt";
+ cl_git_pass(git_index_add(g_idx, &dummy));
+
+ cl_git_mkfile("submod2/just_a_dir/file1.txt", "This is a file");
+ cl_git_mkfile("submod2/just_a_dir/file2.txt", "This is another file");
+ cl_git_mkfile("submod2/just_a_dir/file3.txt", "This is another file");
+ cl_git_mkfile("submod2/just_a_dir/file4.txt", "And another file");
+
+ cl_git_pass(git_index_add_bypath(g_idx, "just_a_dir/File1.txt"));
+ cl_git_pass(git_index_add_bypath(g_idx, "JUST_A_DIR/file2.txt"));
+ cl_git_pass(git_index_add_bypath(g_idx, "Just_A_Dir/FILE3.txt"));
+ cl_git_pass(git_index_add_bypath(g_idx, "JusT_A_DIR/FILE4.txt"));
+
+ cl_assert(entry = git_index_get_bypath(g_idx, "just_a_dir/File1.txt", 0));
+ cl_assert_equal_s("just_a_dir/File1.txt", entry->path);
+
+ cl_assert(entry = git_index_get_bypath(g_idx, "JUST_A_DIR/file2.txt", 0));
+ cl_assert_equal_s("JUST_A_DIR/file2.txt", entry->path);
+
+ cl_assert(entry = git_index_get_bypath(g_idx, "Just_A_Dir/FILE3.txt", 0));
+ cl_assert_equal_s("Just_A_Dir/FILE3.txt", entry->path);
+
+ cl_git_rewritefile("submod2/just_a_dir/file3.txt", "Rewritten");
+ cl_git_pass(git_index_add_bypath(g_idx, "Just_A_Dir/file3.txt"));
+
+ cl_assert(entry = git_index_get_bypath(g_idx, "Just_A_Dir/file3.txt", 0));
+ cl_assert_equal_s("Just_A_Dir/FILE3.txt", entry->path);
+}
+
+void test_index_bypath__add_honors_existing_case_3(void)
+{
+ git_index_entry dummy = { { 0 } };
+ const git_index_entry *entry;
+
+ if (!cl_repo_get_bool(g_repo, "core.ignorecase"))
+ clar__skip();
+
+ dummy.mode = GIT_FILEMODE_BLOB;
+ cl_git_pass(git_oid__fromstr(&dummy.id, "f990a25a74d1a8281ce2ab018ea8df66795cd60b", GIT_OID_SHA1));
+
+ dummy.path = "just_a_dir/filea.txt";
+ cl_git_pass(git_index_add(g_idx, &dummy));
+
+ dummy.path = "Just_A_Dir/fileB.txt";
+ cl_git_pass(git_index_add(g_idx, &dummy));
+
+ dummy.path = "just_A_DIR/FILEC.txt";
+ cl_git_pass(git_index_add(g_idx, &dummy));
+
+ dummy.path = "Just_a_DIR/FileD.txt";
+ cl_git_pass(git_index_add(g_idx, &dummy));
+
+ cl_git_mkfile("submod2/JuSt_A_DiR/fILEE.txt", "This is a file");
+
+ cl_git_pass(git_index_add_bypath(g_idx, "just_a_dir/fILEE.txt"));
+
+ cl_assert(entry = git_index_get_bypath(g_idx, "JUST_A_DIR/fILEE.txt", 0));
+ cl_assert_equal_s("just_a_dir/fILEE.txt", entry->path);
+}
+
+void test_index_bypath__add_honors_existing_case_4(void)
+{
+ git_index_entry dummy = { { 0 } };
+ const git_index_entry *entry;
+
+ if (!cl_repo_get_bool(g_repo, "core.ignorecase"))
+ clar__skip();
+
+ dummy.mode = GIT_FILEMODE_BLOB;
+ cl_git_pass(git_oid__fromstr(&dummy.id, "f990a25a74d1a8281ce2ab018ea8df66795cd60b", GIT_OID_SHA1));
+
+ dummy.path = "just_a_dir/a/b/c/d/e/file1.txt";
+ cl_git_pass(git_index_add(g_idx, &dummy));
+
+ dummy.path = "just_a_dir/a/B/C/D/E/file2.txt";
+ cl_git_pass(git_index_add(g_idx, &dummy));
+
+ cl_must_pass(p_mkdir("submod2/just_a_dir/a", 0777));
+ cl_must_pass(p_mkdir("submod2/just_a_dir/a/b", 0777));
+ cl_must_pass(p_mkdir("submod2/just_a_dir/a/b/z", 0777));
+ cl_must_pass(p_mkdir("submod2/just_a_dir/a/b/z/y", 0777));
+ cl_must_pass(p_mkdir("submod2/just_a_dir/a/b/z/y/x", 0777));
+
+ cl_git_mkfile("submod2/just_a_dir/a/b/z/y/x/FOO.txt", "This is a file");
+
+ cl_git_pass(git_index_add_bypath(g_idx, "just_a_dir/A/b/Z/y/X/foo.txt"));
+
+ cl_assert(entry = git_index_get_bypath(g_idx, "just_a_dir/A/b/Z/y/X/foo.txt", 0));
+ cl_assert_equal_s("just_a_dir/a/b/Z/y/X/foo.txt", entry->path);
+}
+
+void test_index_bypath__add_honors_mode(void)
+{
+ const git_index_entry *entry;
+ git_index_entry new_entry;
+
+ cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL);
+
+ memcpy(&new_entry, entry, sizeof(git_index_entry));
+ new_entry.path = "README.txt";
+ new_entry.mode = GIT_FILEMODE_BLOB_EXECUTABLE;
+
+ cl_must_pass(p_chmod("submod2/README.txt", GIT_FILEMODE_BLOB_EXECUTABLE));
+
+ cl_git_pass(git_index_add(g_idx, &new_entry));
+ cl_git_pass(git_index_write(g_idx));
+
+ cl_git_rewritefile("submod2/README.txt", "Modified but still executable");
+
+ cl_git_pass(git_index_add_bypath(g_idx, "README.txt"));
+ cl_git_pass(git_index_write(g_idx));
+
+ cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL);
+ cl_assert_equal_i(GIT_FILEMODE_BLOB_EXECUTABLE, entry->mode);
+}
+
+void test_index_bypath__add_honors_conflict_mode(void)
+{
+ const git_index_entry *entry;
+ git_index_entry new_entry;
+ int stage = 0;
+
+ cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL);
+
+ memcpy(&new_entry, entry, sizeof(git_index_entry));
+ new_entry.path = "README.txt";
+ new_entry.mode = GIT_FILEMODE_BLOB_EXECUTABLE;
+
+ cl_must_pass(p_chmod("submod2/README.txt", GIT_FILEMODE_BLOB_EXECUTABLE));
+
+ cl_git_pass(git_index_remove_bypath(g_idx, "README.txt"));
+
+ for (stage = 1; stage <= 3; stage++) {
+ new_entry.flags = stage << GIT_INDEX_ENTRY_STAGESHIFT;
+ cl_git_pass(git_index_add(g_idx, &new_entry));
+ }
+
+ cl_git_pass(git_index_write(g_idx));
+
+ cl_git_rewritefile("submod2/README.txt", "Modified but still executable");
+
+ cl_git_pass(git_index_add_bypath(g_idx, "README.txt"));
+ cl_git_pass(git_index_write(g_idx));
+
+ cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL);
+ cl_assert_equal_i(GIT_FILEMODE_BLOB_EXECUTABLE, entry->mode);
+}
+
+void test_index_bypath__add_honors_conflict_case(void)
+{
+ const git_index_entry *entry;
+ git_index_entry new_entry;
+ int stage = 0;
+
+ cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL);
+
+ memcpy(&new_entry, entry, sizeof(git_index_entry));
+ new_entry.path = "README.txt";
+ new_entry.mode = GIT_FILEMODE_BLOB_EXECUTABLE;
+
+ cl_must_pass(p_chmod("submod2/README.txt", GIT_FILEMODE_BLOB_EXECUTABLE));
+
+ cl_git_pass(git_index_remove_bypath(g_idx, "README.txt"));
+
+ for (stage = 1; stage <= 3; stage++) {
+ new_entry.flags = stage << GIT_INDEX_ENTRY_STAGESHIFT;
+ cl_git_pass(git_index_add(g_idx, &new_entry));
+ }
+
+ cl_git_pass(git_index_write(g_idx));
+
+ cl_git_rewritefile("submod2/README.txt", "Modified but still executable");
+
+ cl_git_pass(git_index_add_bypath(g_idx, "README.txt"));
+ cl_git_pass(git_index_write(g_idx));
+
+ cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL);
+ cl_assert_equal_i(GIT_FILEMODE_BLOB_EXECUTABLE, entry->mode);
+}
+
+void test_index_bypath__add_honors_symlink(void)
+{
+ const git_index_entry *entry;
+ git_index_entry new_entry;
+ int symlinks;
+
+ cl_git_pass(git_repository__configmap_lookup(&symlinks, g_repo, GIT_CONFIGMAP_SYMLINKS));
+
+ if (symlinks)
+ cl_skip();
+
+ cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL);
+
+ memcpy(&new_entry, entry, sizeof(git_index_entry));
+ new_entry.path = "README.txt";
+ new_entry.mode = GIT_FILEMODE_LINK;
+
+ cl_git_pass(git_index_add(g_idx, &new_entry));
+ cl_git_pass(git_index_write(g_idx));
+
+ cl_git_rewritefile("submod2/README.txt", "Modified but still a (fake) symlink");
+
+ cl_git_pass(git_index_add_bypath(g_idx, "README.txt"));
+ cl_git_pass(git_index_write(g_idx));
+
+ cl_assert((entry = git_index_get_bypath(g_idx, "README.txt", 0)) != NULL);
+ cl_assert_equal_i(GIT_FILEMODE_LINK, entry->mode);
+}
diff --git a/tests/libgit2/index/cache.c b/tests/libgit2/index/cache.c
new file mode 100644
index 0000000..77f19a5
--- /dev/null
+++ b/tests/libgit2/index/cache.c
@@ -0,0 +1,238 @@
+#include "clar_libgit2.h"
+#include "git2.h"
+#include "index.h"
+#include "tree-cache.h"
+
+static git_repository *g_repo;
+
+void test_index_cache__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_index_cache__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ g_repo = NULL;
+}
+
+void test_index_cache__write_extension_at_root(void)
+{
+ git_index *index;
+ git_tree *tree;
+ git_oid id;
+ const char *tree_id_str = "45dd856fdd4d89b884c340ba0e047752d9b085d6";
+ const char *index_file = "index-tree";
+
+ cl_git_pass(git_index__open(&index, index_file, GIT_OID_SHA1));
+ cl_assert(index->tree == NULL);
+ cl_git_pass(git_oid__fromstr(&id, tree_id_str, GIT_OID_SHA1));
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
+ cl_git_pass(git_index_read_tree(index, tree));
+ git_tree_free(tree);
+
+ cl_assert(index->tree);
+ cl_git_pass(git_index_write(index));
+ git_index_free(index);
+
+ cl_git_pass(git_index__open(&index, index_file, GIT_OID_SHA1));
+ cl_assert(index->tree);
+
+ cl_assert_equal_i(git_index_entrycount(index), index->tree->entry_count);
+ cl_assert_equal_i(0, index->tree->children_count);
+
+ cl_assert(git_oid_equal(&id, &index->tree->oid));
+
+ cl_git_pass(p_unlink(index_file));
+ git_index_free(index);
+}
+
+void test_index_cache__write_extension_invalidated_root(void)
+{
+ git_index *index;
+ git_tree *tree;
+ git_oid id;
+ const char *tree_id_str = "45dd856fdd4d89b884c340ba0e047752d9b085d6";
+ const char *index_file = "index-tree-invalidated";
+ git_index_entry entry;
+
+ cl_git_pass(git_index__open(&index, index_file, GIT_OID_SHA1));
+ cl_assert(index->tree == NULL);
+ cl_git_pass(git_oid__fromstr(&id, tree_id_str, GIT_OID_SHA1));
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
+ cl_git_pass(git_index_read_tree(index, tree));
+ git_tree_free(tree);
+
+ cl_assert(index->tree);
+
+ memset(&entry, 0x0, sizeof(git_index_entry));
+ git_oid_cpy(&entry.id, &git_index_get_byindex(index, 0)->id);
+ entry.mode = GIT_FILEMODE_BLOB;
+ entry.path = "some-new-file.txt";
+
+ cl_git_pass(git_index_add(index, &entry));
+
+ cl_assert_equal_i(-1, index->tree->entry_count);
+
+ cl_git_pass(git_index_write(index));
+ git_index_free(index);
+
+ cl_git_pass(git_index__open(&index, index_file, GIT_OID_SHA1));
+ cl_assert(index->tree);
+
+ cl_assert_equal_i(-1, index->tree->entry_count);
+ cl_assert_equal_i(0, index->tree->children_count);
+
+ cl_assert(git_oid_cmp(&id, &index->tree->oid));
+
+ cl_git_pass(p_unlink(index_file));
+ git_index_free(index);
+}
+
+void test_index_cache__read_tree_no_children(void)
+{
+ git_index *index;
+ git_index_entry entry;
+ git_tree *tree;
+ git_oid id;
+
+ cl_git_pass(git_index__new(&index, GIT_OID_SHA1));
+ cl_assert(index->tree == NULL);
+ cl_git_pass(git_oid__fromstr(&id, "45dd856fdd4d89b884c340ba0e047752d9b085d6", GIT_OID_SHA1));
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
+ cl_git_pass(git_index_read_tree(index, tree));
+ git_tree_free(tree);
+
+ cl_assert(index->tree);
+ cl_assert(git_oid_equal(&id, &index->tree->oid));
+ cl_assert_equal_i(0, index->tree->children_count);
+ cl_assert_equal_i(git_index_entrycount(index), index->tree->entry_count);
+
+ memset(&entry, 0x0, sizeof(git_index_entry));
+ entry.path = "new.txt";
+ entry.mode = GIT_FILEMODE_BLOB;
+ git_oid__fromstr(&entry.id, "d4bcc68acd4410bf836a39f20afb2c2ece09584e", GIT_OID_SHA1);
+
+ cl_git_pass(git_index_add(index, &entry));
+ cl_assert_equal_i(-1, index->tree->entry_count);
+
+ git_index_free(index);
+}
+
+void test_index_cache__two_levels(void)
+{
+ git_tree *tree;
+ git_oid tree_id;
+ git_index *index;
+ git_index_entry entry;
+ const git_tree_cache *tree_cache;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_clear(index));
+
+ memset(&entry, 0x0, sizeof(entry));
+ entry.mode = GIT_FILEMODE_BLOB;
+ cl_git_pass(git_oid__fromstr(&entry.id, "a8233120f6ad708f843d861ce2b7228ec4e3dec6", GIT_OID_SHA1));
+ entry.path = "top-level.txt";
+ cl_git_pass(git_index_add(index, &entry));
+
+ entry.path = "subdir/file.txt";
+ cl_git_pass(git_index_add(index, &entry));
+
+ /* the read-tree fills the tree cache */
+ cl_git_pass(git_index_write_tree(&tree_id, index));
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
+ cl_git_pass(git_index_read_tree(index, tree));
+ git_tree_free(tree);
+ cl_git_pass(git_index_write(index));
+
+ /* we now must have cache entries for "" and "subdir" */
+ cl_assert(index->tree);
+ cl_assert(git_tree_cache_get(index->tree, "subdir"));
+
+ cl_git_pass(git_index_read(index, true));
+ /* we must still have cache entries for "" and "subdir", since we wrote it out */
+ cl_assert(index->tree);
+ cl_assert(git_tree_cache_get(index->tree, "subdir"));
+
+ entry.path = "top-level.txt";
+ cl_git_pass(git_oid__fromstr(&entry.id, "3697d64be941a53d4ae8f6a271e4e3fa56b022cc", GIT_OID_SHA1));
+ cl_git_pass(git_index_add(index, &entry));
+
+ /* writ out the index after we invalidate the root */
+ cl_git_pass(git_index_write(index));
+ cl_git_pass(git_index_read(index, true));
+
+ /* the cache for the subtree must still be valid, even if the root isn't */
+ cl_assert(index->tree);
+ cl_assert_equal_i(-1, index->tree->entry_count);
+ cl_assert_equal_i(1, index->tree->children_count);
+ tree_cache = git_tree_cache_get(index->tree, "subdir");
+ cl_assert(tree_cache);
+ cl_assert_equal_i(1, tree_cache->entry_count);
+
+ git_index_free(index);
+}
+
+void test_index_cache__read_tree_children(void)
+{
+ git_index *index;
+ git_index_entry entry;
+ git_tree *tree;
+ const git_tree_cache *cache;
+ git_oid tree_id;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_clear(index));
+ cl_assert(index->tree == NULL);
+
+
+ /* add a bunch of entries at different levels */
+ memset(&entry, 0x0, sizeof(git_index_entry));
+ entry.path = "top-level";
+ entry.mode = GIT_FILEMODE_BLOB;
+ git_oid__fromstr(&entry.id, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", GIT_OID_SHA1);
+ cl_git_pass(git_index_add(index, &entry));
+
+
+ entry.path = "subdir/some-file";
+ cl_git_pass(git_index_add(index, &entry));
+
+ entry.path = "subdir/even-deeper/some-file";
+ cl_git_pass(git_index_add(index, &entry));
+
+ entry.path = "subdir2/some-file";
+ cl_git_pass(git_index_add(index, &entry));
+
+ cl_git_pass(git_index_write_tree(&tree_id, index));
+ cl_git_pass(git_index_clear(index));
+ cl_assert(index->tree == NULL);
+
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
+ cl_git_pass(git_index_read_tree(index, tree));
+ git_tree_free(tree);
+
+ cl_assert(index->tree);
+ cl_assert_equal_i(2, index->tree->children_count);
+
+ /* override with a slightly different id, also dummy */
+ entry.path = "subdir/some-file";
+ git_oid__fromstr(&entry.id, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", GIT_OID_SHA1);
+ cl_git_pass(git_index_add(index, &entry));
+
+ cl_assert_equal_i(-1, index->tree->entry_count);
+
+ cache = git_tree_cache_get(index->tree, "subdir");
+ cl_assert(cache);
+ cl_assert_equal_i(-1, cache->entry_count);
+
+ cache = git_tree_cache_get(index->tree, "subdir/even-deeper");
+ cl_assert(cache);
+ cl_assert_equal_i(1, cache->entry_count);
+
+ cache = git_tree_cache_get(index->tree, "subdir2");
+ cl_assert(cache);
+ cl_assert_equal_i(1, cache->entry_count);
+
+ git_index_free(index);
+}
diff --git a/tests/libgit2/index/collision.c b/tests/libgit2/index/collision.c
new file mode 100644
index 0000000..18d2664
--- /dev/null
+++ b/tests/libgit2/index/collision.c
@@ -0,0 +1,149 @@
+#include "clar_libgit2.h"
+#include "git2/repository.h"
+#include "git2/index.h"
+
+static git_repository *g_repo;
+static git_odb *g_odb;
+static git_index *g_index;
+static git_oid g_empty_id;
+
+void test_index_collision__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+ cl_git_pass(git_repository_odb(&g_odb, g_repo));
+ cl_git_pass(git_repository_index(&g_index, g_repo));
+
+ cl_git_pass(git_odb_write(&g_empty_id, g_odb, "", 0, GIT_OBJECT_BLOB));
+}
+
+void test_index_collision__cleanup(void)
+{
+ git_index_free(g_index);
+ git_odb_free(g_odb);
+ cl_git_sandbox_cleanup();
+}
+
+void test_index_collision__add_blob_with_conflicting_file(void)
+{
+ git_index_entry entry;
+ git_tree_entry *tentry;
+ git_oid tree_id;
+ git_tree *tree;
+
+ memset(&entry, 0, sizeof(entry));
+ entry.ctime.seconds = 12346789;
+ entry.mtime.seconds = 12346789;
+ entry.mode = 0100644;
+ entry.file_size = 0;
+ git_oid_cpy(&entry.id, &g_empty_id);
+
+ entry.path = "a/b";
+ cl_git_pass(git_index_add(g_index, &entry));
+
+ /* Check a/b exists here */
+ cl_git_pass(git_index_write_tree(&tree_id, g_index));
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
+ cl_git_pass(git_tree_entry_bypath(&tentry, tree, "a/b"));
+ git_tree_entry_free(tentry);
+ git_tree_free(tree);
+
+ /* create a tree/blob collision */
+ entry.path = "a/b/c";
+ cl_git_pass(git_index_add(g_index, &entry));
+
+ /* a/b should now be a tree and a/b/c a blob */
+ cl_git_pass(git_index_write_tree(&tree_id, g_index));
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
+ cl_git_pass(git_tree_entry_bypath(&tentry, tree, "a/b/c"));
+ git_tree_entry_free(tentry);
+ git_tree_free(tree);
+}
+
+void test_index_collision__add_blob_with_conflicting_dir(void)
+{
+ git_index_entry entry;
+ git_tree_entry *tentry;
+ git_oid tree_id;
+ git_tree *tree;
+
+ memset(&entry, 0, sizeof(entry));
+ entry.ctime.seconds = 12346789;
+ entry.mtime.seconds = 12346789;
+ entry.mode = 0100644;
+ entry.file_size = 0;
+ git_oid_cpy(&entry.id, &g_empty_id);
+
+ entry.path = "a/b/c";
+ cl_git_pass(git_index_add(g_index, &entry));
+
+ /* Check a/b/c exists here */
+ cl_git_pass(git_index_write_tree(&tree_id, g_index));
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
+ cl_git_pass(git_tree_entry_bypath(&tentry, tree, "a/b/c"));
+ git_tree_entry_free(tentry);
+ git_tree_free(tree);
+
+ /* create a blob/tree collision */
+ entry.path = "a/b";
+ cl_git_pass(git_index_add(g_index, &entry));
+
+ /* a/b should now be a tree and a/b/c a blob */
+ cl_git_pass(git_index_write_tree(&tree_id, g_index));
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
+ cl_git_pass(git_tree_entry_bypath(&tentry, tree, "a/b"));
+ cl_git_fail(git_tree_entry_bypath(&tentry, tree, "a/b/c"));
+ git_tree_entry_free(tentry);
+ git_tree_free(tree);
+}
+
+void test_index_collision__add_with_highstage_1(void)
+{
+ git_index_entry entry;
+
+ memset(&entry, 0, sizeof(entry));
+ entry.ctime.seconds = 12346789;
+ entry.mtime.seconds = 12346789;
+ entry.mode = 0100644;
+ entry.file_size = 0;
+ git_oid_cpy(&entry.id, &g_empty_id);
+
+ entry.path = "a/b";
+ GIT_INDEX_ENTRY_STAGE_SET(&entry, 2);
+ cl_git_pass(git_index_add(g_index, &entry));
+
+ /* create a blob beneath the previous tree entry */
+ entry.path = "a/b/c";
+ entry.flags = 0;
+ cl_git_pass(git_index_add(g_index, &entry));
+
+ /* create another tree entry above the blob */
+ entry.path = "a/b";
+ GIT_INDEX_ENTRY_STAGE_SET(&entry, 1);
+ cl_git_pass(git_index_add(g_index, &entry));
+}
+
+void test_index_collision__add_with_highstage_2(void)
+{
+ git_index_entry entry;
+
+ memset(&entry, 0, sizeof(entry));
+ entry.ctime.seconds = 12346789;
+ entry.mtime.seconds = 12346789;
+ entry.mode = 0100644;
+ entry.file_size = 0;
+ git_oid_cpy(&entry.id, &g_empty_id);
+
+ entry.path = "a/b/c";
+ GIT_INDEX_ENTRY_STAGE_SET(&entry, 1);
+ cl_git_pass(git_index_add(g_index, &entry));
+
+ /* create a blob beneath the previous tree entry */
+ entry.path = "a/b/c";
+ GIT_INDEX_ENTRY_STAGE_SET(&entry, 2);
+ cl_git_pass(git_index_add(g_index, &entry));
+
+ /* create another tree entry above the blob */
+ entry.path = "a/b";
+ GIT_INDEX_ENTRY_STAGE_SET(&entry, 3);
+ cl_git_pass(git_index_add(g_index, &entry));
+}
diff --git a/tests/libgit2/index/conflicts.c b/tests/libgit2/index/conflicts.c
new file mode 100644
index 0000000..353886f
--- /dev/null
+++ b/tests/libgit2/index/conflicts.c
@@ -0,0 +1,462 @@
+#include "clar_libgit2.h"
+#include "index.h"
+#include "git2/repository.h"
+#include "conflicts.h"
+
+static git_repository *repo;
+static git_index *repo_index;
+
+#define TEST_REPO_PATH "mergedrepo"
+#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
+
+/* Fixture setup and teardown */
+void test_index_conflicts__initialize(void)
+{
+ repo = cl_git_sandbox_init("mergedrepo");
+ git_repository_index(&repo_index, repo);
+}
+
+void test_index_conflicts__cleanup(void)
+{
+ git_index_free(repo_index);
+ repo_index = NULL;
+
+ cl_git_sandbox_cleanup();
+}
+
+void test_index_conflicts__add(void)
+{
+ git_index_entry ancestor_entry, our_entry, their_entry;
+
+ cl_assert(git_index_entrycount(repo_index) == 8);
+
+ memset(&ancestor_entry, 0x0, sizeof(git_index_entry));
+ memset(&our_entry, 0x0, sizeof(git_index_entry));
+ memset(&their_entry, 0x0, sizeof(git_index_entry));
+
+ ancestor_entry.path = "test-one.txt";
+ ancestor_entry.mode = 0100644;
+ GIT_INDEX_ENTRY_STAGE_SET(&ancestor_entry, 1);
+ git_oid__fromstr(&ancestor_entry.id, CONFLICTS_ONE_ANCESTOR_OID, GIT_OID_SHA1);
+
+ our_entry.path = "test-one.txt";
+ our_entry.mode = 0100644;
+ GIT_INDEX_ENTRY_STAGE_SET(&our_entry, 2);
+ git_oid__fromstr(&our_entry.id, CONFLICTS_ONE_OUR_OID, GIT_OID_SHA1);
+
+ their_entry.path = "test-one.txt";
+ their_entry.mode = 0100644;
+ GIT_INDEX_ENTRY_STAGE_SET(&ancestor_entry, 2);
+ git_oid__fromstr(&their_entry.id, CONFLICTS_ONE_THEIR_OID, GIT_OID_SHA1);
+
+ cl_git_pass(git_index_conflict_add(repo_index, &ancestor_entry, &our_entry, &their_entry));
+
+ cl_assert(git_index_entrycount(repo_index) == 11);
+}
+
+void test_index_conflicts__add_fixes_incorrect_stage(void)
+{
+ git_index_entry ancestor_entry, our_entry, their_entry;
+ const git_index_entry *conflict_entry[3];
+
+ cl_assert(git_index_entrycount(repo_index) == 8);
+
+ memset(&ancestor_entry, 0x0, sizeof(git_index_entry));
+ memset(&our_entry, 0x0, sizeof(git_index_entry));
+ memset(&their_entry, 0x0, sizeof(git_index_entry));
+
+ ancestor_entry.path = "test-one.txt";
+ ancestor_entry.mode = 0100644;
+ GIT_INDEX_ENTRY_STAGE_SET(&ancestor_entry, 3);
+ git_oid__fromstr(&ancestor_entry.id, CONFLICTS_ONE_ANCESTOR_OID, GIT_OID_SHA1);
+
+ our_entry.path = "test-one.txt";
+ our_entry.mode = 0100644;
+ GIT_INDEX_ENTRY_STAGE_SET(&our_entry, 1);
+ git_oid__fromstr(&our_entry.id, CONFLICTS_ONE_OUR_OID, GIT_OID_SHA1);
+
+ their_entry.path = "test-one.txt";
+ their_entry.mode = 0100644;
+ GIT_INDEX_ENTRY_STAGE_SET(&their_entry, 2);
+ git_oid__fromstr(&their_entry.id, CONFLICTS_ONE_THEIR_OID, GIT_OID_SHA1);
+
+ cl_git_pass(git_index_conflict_add(repo_index, &ancestor_entry, &our_entry, &their_entry));
+
+ cl_assert(git_index_entrycount(repo_index) == 11);
+
+ cl_git_pass(git_index_conflict_get(&conflict_entry[0], &conflict_entry[1], &conflict_entry[2], repo_index, "test-one.txt"));
+
+ cl_assert(git_index_entry_stage(conflict_entry[0]) == 1);
+ cl_assert(git_index_entry_stage(conflict_entry[1]) == 2);
+ cl_assert(git_index_entry_stage(conflict_entry[2]) == 3);
+}
+
+void test_index_conflicts__add_detects_invalid_filemode(void)
+{
+ git_index_entry ancestor_entry, our_entry, their_entry;
+ git_index_entry *conflict_entry[3];
+ int i;
+
+ cl_assert(git_index_entrycount(repo_index) == 8);
+
+ memset(&ancestor_entry, 0x0, sizeof(git_index_entry));
+ memset(&our_entry, 0x0, sizeof(git_index_entry));
+ memset(&their_entry, 0x0, sizeof(git_index_entry));
+
+ conflict_entry[0] = &ancestor_entry;
+ conflict_entry[1] = &our_entry;
+ conflict_entry[2] = &their_entry;
+
+ for (i = 0; i < 3; i++) {
+ ancestor_entry.path = "test-one.txt";
+ ancestor_entry.mode = 0100644;
+ GIT_INDEX_ENTRY_STAGE_SET(&ancestor_entry, 3);
+ git_oid__fromstr(&ancestor_entry.id, CONFLICTS_ONE_ANCESTOR_OID, GIT_OID_SHA1);
+
+ our_entry.path = "test-one.txt";
+ our_entry.mode = 0100644;
+ GIT_INDEX_ENTRY_STAGE_SET(&our_entry, 1);
+ git_oid__fromstr(&our_entry.id, CONFLICTS_ONE_OUR_OID, GIT_OID_SHA1);
+
+ their_entry.path = "test-one.txt";
+ their_entry.mode = 0100644;
+ GIT_INDEX_ENTRY_STAGE_SET(&their_entry, 2);
+ git_oid__fromstr(&their_entry.id, CONFLICTS_ONE_THEIR_OID, GIT_OID_SHA1);
+
+ /* Corrupt the conflict entry's mode */
+ conflict_entry[i]->mode = 027431745;
+
+ cl_git_fail(git_index_conflict_add(repo_index, &ancestor_entry, &our_entry, &their_entry));
+ }
+
+ cl_assert(git_index_entrycount(repo_index) == 8);
+}
+
+
+void test_index_conflicts__add_removes_stage_zero(void)
+{
+ git_index_entry ancestor_entry, our_entry, their_entry;
+ const git_index_entry *conflict_entry[3];
+
+ cl_assert(git_index_entrycount(repo_index) == 8);
+
+ memset(&ancestor_entry, 0x0, sizeof(git_index_entry));
+ memset(&our_entry, 0x0, sizeof(git_index_entry));
+ memset(&their_entry, 0x0, sizeof(git_index_entry));
+
+ cl_git_mkfile("./mergedrepo/test-one.txt", "new-file\n");
+ cl_git_pass(git_index_add_bypath(repo_index, "test-one.txt"));
+ cl_assert(git_index_entrycount(repo_index) == 9);
+
+ ancestor_entry.path = "test-one.txt";
+ ancestor_entry.mode = 0100644;
+ GIT_INDEX_ENTRY_STAGE_SET(&ancestor_entry, 3);
+ git_oid__fromstr(&ancestor_entry.id, CONFLICTS_ONE_ANCESTOR_OID, GIT_OID_SHA1);
+
+ our_entry.path = "test-one.txt";
+ our_entry.mode = 0100644;
+ GIT_INDEX_ENTRY_STAGE_SET(&our_entry, 1);
+ git_oid__fromstr(&our_entry.id, CONFLICTS_ONE_OUR_OID, GIT_OID_SHA1);
+
+ their_entry.path = "test-one.txt";
+ their_entry.mode = 0100644;
+ GIT_INDEX_ENTRY_STAGE_SET(&their_entry, 2);
+ git_oid__fromstr(&their_entry.id, CONFLICTS_ONE_THEIR_OID, GIT_OID_SHA1);
+
+ cl_git_pass(git_index_conflict_add(repo_index, &ancestor_entry, &our_entry, &their_entry));
+
+ cl_assert(git_index_entrycount(repo_index) == 11);
+
+ cl_assert_equal_p(NULL, git_index_get_bypath(repo_index, "test-one.txt", 0));
+
+ cl_git_pass(git_index_conflict_get(&conflict_entry[0], &conflict_entry[1], &conflict_entry[2], repo_index, "test-one.txt"));
+
+ cl_assert_equal_oid(&ancestor_entry.id, &conflict_entry[0]->id);
+ cl_assert_equal_i(1, git_index_entry_stage(conflict_entry[0]));
+ cl_assert_equal_oid(&our_entry.id, &conflict_entry[1]->id);
+ cl_assert_equal_i(2, git_index_entry_stage(conflict_entry[1]));
+ cl_assert_equal_oid(&their_entry.id, &conflict_entry[2]->id);
+ cl_assert_equal_i(3, git_index_entry_stage(conflict_entry[2]));
+}
+
+void test_index_conflicts__get(void)
+{
+ const git_index_entry *conflict_entry[3];
+ git_oid oid;
+
+ cl_git_pass(git_index_conflict_get(&conflict_entry[0], &conflict_entry[1],
+ &conflict_entry[2], repo_index, "conflicts-one.txt"));
+
+ cl_assert_equal_s("conflicts-one.txt", conflict_entry[0]->path);
+
+ git_oid__fromstr(&oid, CONFLICTS_ONE_ANCESTOR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&oid, &conflict_entry[0]->id);
+
+ git_oid__fromstr(&oid, CONFLICTS_ONE_OUR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&oid, &conflict_entry[1]->id);
+
+ git_oid__fromstr(&oid, CONFLICTS_ONE_THEIR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&oid, &conflict_entry[2]->id);
+
+ cl_git_pass(git_index_conflict_get(&conflict_entry[0], &conflict_entry[1],
+ &conflict_entry[2], repo_index, "conflicts-two.txt"));
+
+ cl_assert_equal_s("conflicts-two.txt", conflict_entry[0]->path);
+
+ git_oid__fromstr(&oid, CONFLICTS_TWO_ANCESTOR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&oid, &conflict_entry[0]->id);
+
+ git_oid__fromstr(&oid, CONFLICTS_TWO_OUR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&oid, &conflict_entry[1]->id);
+
+ git_oid__fromstr(&oid, CONFLICTS_TWO_THEIR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&oid, &conflict_entry[2]->id);
+}
+
+void test_index_conflicts__iterate(void)
+{
+ git_index_conflict_iterator *iterator;
+ const git_index_entry *conflict_entry[3];
+ git_oid oid;
+
+ cl_git_pass(git_index_conflict_iterator_new(&iterator, repo_index));
+
+ cl_git_pass(git_index_conflict_next(&conflict_entry[0], &conflict_entry[1], &conflict_entry[2], iterator));
+
+ git_oid__fromstr(&oid, CONFLICTS_ONE_ANCESTOR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&oid, &conflict_entry[0]->id);
+ cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-one.txt") == 0);
+
+ git_oid__fromstr(&oid, CONFLICTS_ONE_OUR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&oid, &conflict_entry[1]->id);
+ cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-one.txt") == 0);
+
+ git_oid__fromstr(&oid, CONFLICTS_ONE_THEIR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&oid, &conflict_entry[2]->id);
+ cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-one.txt") == 0);
+
+ cl_git_pass(git_index_conflict_next(&conflict_entry[0], &conflict_entry[1], &conflict_entry[2], iterator));
+
+ git_oid__fromstr(&oid, CONFLICTS_TWO_ANCESTOR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&oid, &conflict_entry[0]->id);
+ cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-two.txt") == 0);
+
+ git_oid__fromstr(&oid, CONFLICTS_TWO_OUR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&oid, &conflict_entry[1]->id);
+ cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-two.txt") == 0);
+
+ git_oid__fromstr(&oid, CONFLICTS_TWO_THEIR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&oid, &conflict_entry[2]->id);
+ cl_assert(git__strcmp(conflict_entry[0]->path, "conflicts-two.txt") == 0);
+
+ cl_assert(git_index_conflict_next(&conflict_entry[0], &conflict_entry[1], &conflict_entry[2], iterator) == GIT_ITEROVER);
+
+ cl_assert(conflict_entry[0] == NULL);
+ cl_assert(conflict_entry[2] == NULL);
+ cl_assert(conflict_entry[2] == NULL);
+
+ git_index_conflict_iterator_free(iterator);
+}
+
+void test_index_conflicts__remove(void)
+{
+ const git_index_entry *entry;
+ size_t i;
+
+ cl_assert(git_index_entrycount(repo_index) == 8);
+
+ cl_git_pass(git_index_conflict_remove(repo_index, "conflicts-one.txt"));
+ cl_assert(git_index_entrycount(repo_index) == 5);
+
+ for (i = 0; i < git_index_entrycount(repo_index); i++) {
+ cl_assert(entry = git_index_get_byindex(repo_index, i));
+ cl_assert(strcmp(entry->path, "conflicts-one.txt") != 0);
+ }
+
+ cl_git_pass(git_index_conflict_remove(repo_index, "conflicts-two.txt"));
+ cl_assert(git_index_entrycount(repo_index) == 2);
+
+ for (i = 0; i < git_index_entrycount(repo_index); i++) {
+ cl_assert(entry = git_index_get_byindex(repo_index, i));
+ cl_assert(strcmp(entry->path, "conflicts-two.txt") != 0);
+ }
+}
+
+void test_index_conflicts__moved_to_reuc_on_add(void)
+{
+ const git_index_entry *entry;
+ size_t i;
+
+ cl_assert(git_index_entrycount(repo_index) == 8);
+
+ cl_git_mkfile("./mergedrepo/conflicts-one.txt", "new-file\n");
+
+ cl_git_pass(git_index_add_bypath(repo_index, "conflicts-one.txt"));
+
+ cl_assert(git_index_entrycount(repo_index) == 6);
+
+ for (i = 0; i < git_index_entrycount(repo_index); i++) {
+ cl_assert(entry = git_index_get_byindex(repo_index, i));
+
+ if (strcmp(entry->path, "conflicts-one.txt") == 0)
+ cl_assert(!git_index_entry_is_conflict(entry));
+ }
+}
+
+void test_index_conflicts__moved_to_reuc_on_remove(void)
+{
+ const git_index_entry *entry;
+ size_t i;
+
+ cl_assert(git_index_entrycount(repo_index) == 8);
+
+ cl_git_pass(p_unlink("./mergedrepo/conflicts-one.txt"));
+
+ cl_git_pass(git_index_remove_bypath(repo_index, "conflicts-one.txt"));
+
+ cl_assert(git_index_entrycount(repo_index) == 5);
+
+ for (i = 0; i < git_index_entrycount(repo_index); i++) {
+ cl_assert(entry = git_index_get_byindex(repo_index, i));
+ cl_assert(strcmp(entry->path, "conflicts-one.txt") != 0);
+ }
+}
+
+void test_index_conflicts__remove_all_conflicts(void)
+{
+ size_t i;
+ const git_index_entry *entry;
+
+ cl_assert(git_index_entrycount(repo_index) == 8);
+
+ cl_assert_equal_i(true, git_index_has_conflicts(repo_index));
+
+ git_index_conflict_cleanup(repo_index);
+
+ cl_assert_equal_i(false, git_index_has_conflicts(repo_index));
+
+ cl_assert(git_index_entrycount(repo_index) == 2);
+
+ for (i = 0; i < git_index_entrycount(repo_index); i++) {
+ cl_assert(entry = git_index_get_byindex(repo_index, i));
+ cl_assert(!git_index_entry_is_conflict(entry));
+ }
+}
+
+void test_index_conflicts__partial(void)
+{
+ git_index_entry ancestor_entry, our_entry, their_entry;
+ const git_index_entry *conflict_entry[3];
+
+ cl_assert(git_index_entrycount(repo_index) == 8);
+
+ memset(&ancestor_entry, 0x0, sizeof(git_index_entry));
+ memset(&our_entry, 0x0, sizeof(git_index_entry));
+ memset(&their_entry, 0x0, sizeof(git_index_entry));
+
+ ancestor_entry.path = "test-one.txt";
+ ancestor_entry.mode = 0100644;
+ GIT_INDEX_ENTRY_STAGE_SET(&ancestor_entry, 1);
+ git_oid__fromstr(&ancestor_entry.id, CONFLICTS_ONE_ANCESTOR_OID, GIT_OID_SHA1);
+
+ cl_git_pass(git_index_conflict_add(repo_index, &ancestor_entry, NULL, NULL));
+ cl_assert(git_index_entrycount(repo_index) == 9);
+
+ cl_git_pass(git_index_conflict_get(&conflict_entry[0], &conflict_entry[1],
+ &conflict_entry[2], repo_index, "test-one.txt"));
+
+ cl_assert_equal_oid(&ancestor_entry.id, &conflict_entry[0]->id);
+ cl_assert(conflict_entry[1] == NULL);
+ cl_assert(conflict_entry[2] == NULL);
+}
+
+void test_index_conflicts__case_matters(void)
+{
+ const git_index_entry *conflict_entry[3];
+ git_oid oid;
+ const char *upper_case = "DIFFERS-IN-CASE.TXT";
+ const char *mixed_case = "Differs-In-Case.txt";
+ const char *correct_case;
+ bool ignorecase = cl_repo_get_bool(repo, "core.ignorecase");
+
+ git_index_entry ancestor_entry, our_entry, their_entry;
+
+ memset(&ancestor_entry, 0x0, sizeof(git_index_entry));
+ memset(&our_entry, 0x0, sizeof(git_index_entry));
+ memset(&their_entry, 0x0, sizeof(git_index_entry));
+
+ ancestor_entry.path = upper_case;
+ GIT_INDEX_ENTRY_STAGE_SET(&ancestor_entry, GIT_INDEX_STAGE_ANCESTOR);
+ git_oid__fromstr(&ancestor_entry.id, CONFLICTS_ONE_ANCESTOR_OID, GIT_OID_SHA1);
+ ancestor_entry.mode = GIT_FILEMODE_BLOB;
+
+ our_entry.path = upper_case;
+ GIT_INDEX_ENTRY_STAGE_SET(&our_entry, GIT_INDEX_STAGE_OURS);
+ git_oid__fromstr(&our_entry.id, CONFLICTS_ONE_OUR_OID, GIT_OID_SHA1);
+ our_entry.mode = GIT_FILEMODE_BLOB;
+
+ their_entry.path = upper_case;
+ GIT_INDEX_ENTRY_STAGE_SET(&their_entry, GIT_INDEX_STAGE_THEIRS);
+ git_oid__fromstr(&their_entry.id, CONFLICTS_ONE_THEIR_OID, GIT_OID_SHA1);
+ their_entry.mode = GIT_FILEMODE_BLOB;
+
+ cl_git_pass(git_index_conflict_add(repo_index,
+ &ancestor_entry, &our_entry, &their_entry));
+
+ ancestor_entry.path = mixed_case;
+ GIT_INDEX_ENTRY_STAGE_SET(&ancestor_entry, GIT_INDEX_STAGE_ANCESTOR);
+ git_oid__fromstr(&ancestor_entry.id, CONFLICTS_TWO_ANCESTOR_OID, GIT_OID_SHA1);
+ ancestor_entry.mode = GIT_FILEMODE_BLOB;
+
+ our_entry.path = mixed_case;
+ GIT_INDEX_ENTRY_STAGE_SET(&ancestor_entry, GIT_INDEX_STAGE_ANCESTOR);
+ git_oid__fromstr(&our_entry.id, CONFLICTS_TWO_OUR_OID, GIT_OID_SHA1);
+ ancestor_entry.mode = GIT_FILEMODE_BLOB;
+
+ their_entry.path = mixed_case;
+ GIT_INDEX_ENTRY_STAGE_SET(&their_entry, GIT_INDEX_STAGE_THEIRS);
+ git_oid__fromstr(&their_entry.id, CONFLICTS_TWO_THEIR_OID, GIT_OID_SHA1);
+ their_entry.mode = GIT_FILEMODE_BLOB;
+
+ cl_git_pass(git_index_conflict_add(repo_index,
+ &ancestor_entry, &our_entry, &their_entry));
+
+ cl_git_pass(git_index_conflict_get(&conflict_entry[0], &conflict_entry[1],
+ &conflict_entry[2], repo_index, upper_case));
+
+ /*
+ * We inserted with mixed case last, so on a case-insensitive
+ * fs we should get the mixed case.
+ */
+ if (ignorecase)
+ correct_case = mixed_case;
+ else
+ correct_case = upper_case;
+
+ cl_assert_equal_s(correct_case, conflict_entry[0]->path);
+ git_oid__fromstr(&oid, ignorecase ? CONFLICTS_TWO_ANCESTOR_OID : CONFLICTS_ONE_ANCESTOR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&oid, &conflict_entry[0]->id);
+
+ cl_assert_equal_s(correct_case, conflict_entry[1]->path);
+ git_oid__fromstr(&oid, ignorecase ? CONFLICTS_TWO_OUR_OID : CONFLICTS_ONE_OUR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&oid, &conflict_entry[1]->id);
+
+ cl_assert_equal_s(correct_case, conflict_entry[2]->path);
+ git_oid__fromstr(&oid, ignorecase ? CONFLICTS_TWO_THEIR_OID : CONFLICTS_ONE_THEIR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&oid, &conflict_entry[2]->id);
+
+ cl_git_pass(git_index_conflict_get(&conflict_entry[0], &conflict_entry[1],
+ &conflict_entry[2], repo_index, mixed_case));
+
+ cl_assert_equal_s(mixed_case, conflict_entry[0]->path);
+ git_oid__fromstr(&oid, CONFLICTS_TWO_ANCESTOR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&oid, &conflict_entry[0]->id);
+
+ cl_assert_equal_s(mixed_case, conflict_entry[1]->path);
+ git_oid__fromstr(&oid, CONFLICTS_TWO_OUR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&oid, &conflict_entry[1]->id);
+
+ cl_assert_equal_s(mixed_case, conflict_entry[2]->path);
+ git_oid__fromstr(&oid, CONFLICTS_TWO_THEIR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&oid, &conflict_entry[2]->id);
+}
diff --git a/tests/libgit2/index/conflicts.h b/tests/libgit2/index/conflicts.h
new file mode 100644
index 0000000..3e26e6d
--- /dev/null
+++ b/tests/libgit2/index/conflicts.h
@@ -0,0 +1,7 @@
+#define CONFLICTS_ONE_ANCESTOR_OID "1f85ca51b8e0aac893a621b61a9c2661d6aa6d81"
+#define CONFLICTS_ONE_OUR_OID "6aea5f295304c36144ad6e9247a291b7f8112399"
+#define CONFLICTS_ONE_THEIR_OID "516bd85f78061e09ccc714561d7b504672cb52da"
+
+#define CONFLICTS_TWO_ANCESTOR_OID "84af62840be1b1c47b778a8a249f3ff45155038c"
+#define CONFLICTS_TWO_OUR_OID "8b3f43d2402825c200f835ca1762413e386fd0b2"
+#define CONFLICTS_TWO_THEIR_OID "220bd62631c8cf7a83ef39c6b94595f00517211e"
diff --git a/tests/libgit2/index/crlf.c b/tests/libgit2/index/crlf.c
new file mode 100644
index 0000000..666ac1a
--- /dev/null
+++ b/tests/libgit2/index/crlf.c
@@ -0,0 +1,403 @@
+#include "clar_libgit2.h"
+#include "../filter/crlf.h"
+
+#include "git2/checkout.h"
+#include "repository.h"
+#include "posix.h"
+
+#define FILE_CONTENTS_LF "one\ntwo\nthree\nfour\n"
+#define FILE_CONTENTS_CRLF "one\r\ntwo\r\nthree\r\nfour\r\n"
+
+#define FILE_OID_LF "f384549cbeb481e437091320de6d1f2e15e11b4a"
+#define FILE_OID_CRLF "7fbf4d847b191141d80f30c8ab03d2ad4cd543a9"
+
+static git_repository *g_repo;
+static git_index *g_index;
+
+static git_str expected_fixture = GIT_STR_INIT;
+
+void test_index_crlf__initialize(void)
+{
+ g_repo = cl_git_sandbox_init_new("crlf");
+ cl_git_pass(git_repository_index(&g_index, g_repo));
+}
+
+void test_index_crlf__cleanup(void)
+{
+ git_index_free(g_index);
+ cl_git_sandbox_cleanup();
+
+ if (expected_fixture.size) {
+ cl_fixture_cleanup(expected_fixture.ptr);
+ git_str_dispose(&expected_fixture);
+ }
+}
+
+struct compare_data
+{
+ const char *systype;
+ const char *dirname;
+ const char *safecrlf;
+ const char *autocrlf;
+ const char *attrs;
+};
+
+static int add_and_check_file(void *payload, git_str *actual_path)
+{
+ git_str expected_path = GIT_STR_INIT;
+ git_str expected_path_fail = GIT_STR_INIT;
+ git_str expected_contents = GIT_STR_INIT;
+ struct compare_data *cd = payload;
+ char *basename;
+ const git_index_entry *entry;
+ git_blob *blob;
+ bool failed = true;
+
+ basename = git_fs_path_basename(actual_path->ptr);
+
+ if (!strcmp(basename, ".git") || !strcmp(basename, ".gitattributes")) {
+ failed = false;
+ goto done;
+ }
+
+ cl_git_pass(git_str_joinpath(&expected_path, cd->dirname, basename));
+
+ cl_git_pass(git_str_puts(&expected_path_fail, expected_path.ptr));
+ cl_git_pass(git_str_puts(&expected_path_fail, ".fail"));
+
+ if (git_fs_path_isfile(expected_path.ptr)) {
+ cl_git_pass(git_index_add_bypath(g_index, basename));
+
+ cl_assert(entry = git_index_get_bypath(g_index, basename, 0));
+ cl_git_pass(git_blob_lookup(&blob, g_repo, &entry->id));
+
+ cl_git_pass(git_futils_readbuffer(&expected_contents, expected_path.ptr));
+
+ if (strcmp(expected_contents.ptr, git_blob_rawcontent(blob)) != 0)
+ goto done;
+
+ git_blob_free(blob);
+ } else if (git_fs_path_isfile(expected_path_fail.ptr)) {
+ cl_git_pass(git_futils_readbuffer(&expected_contents, expected_path_fail.ptr));
+ git_str_rtrim(&expected_contents);
+
+ if (git_index_add_bypath(g_index, basename) == 0 ||
+ git_error_last()->klass != GIT_ERROR_FILTER ||
+ strcmp(expected_contents.ptr, git_error_last()->message) != 0)
+ goto done;
+ } else {
+ cl_fail("unexpected index failure");
+ }
+
+ failed = false;
+
+done:
+ if (failed) {
+ git_str details = GIT_STR_INIT;
+ git_str_printf(&details, "filename=%s, system=%s, autocrlf=%s, safecrlf=%s, attrs={%s}",
+ basename, cd->systype, cd->autocrlf, cd->safecrlf, cd->attrs);
+ clar__fail(__FILE__, __func__, __LINE__,
+ "index contents did not match expected", details.ptr, 0);
+ git_str_dispose(&details);
+ }
+
+ git__free(basename);
+ git_str_dispose(&expected_contents);
+ git_str_dispose(&expected_path);
+ git_str_dispose(&expected_path_fail);
+ return 0;
+}
+
+static const char *system_type(void)
+{
+ if (GIT_EOL_NATIVE == GIT_EOL_CRLF)
+ return "windows";
+ else
+ return "posix";
+}
+
+static void test_add_index(const char *safecrlf, const char *autocrlf, const char *attrs)
+{
+ git_str attrbuf = GIT_STR_INIT;
+ git_str expected_dirname = GIT_STR_INIT;
+ git_str sandboxname = GIT_STR_INIT;
+ git_str reponame = GIT_STR_INIT;
+ struct compare_data compare_data = { system_type(), NULL, safecrlf, autocrlf, attrs };
+ const char *c;
+
+ git_str_puts(&reponame, "crlf");
+
+ git_str_puts(&sandboxname, "autocrlf_");
+ git_str_puts(&sandboxname, autocrlf);
+
+ git_str_puts(&sandboxname, ",safecrlf_");
+ git_str_puts(&sandboxname, safecrlf);
+
+ if (*attrs) {
+ git_str_puts(&sandboxname, ",");
+
+ for (c = attrs; *c; c++) {
+ if (*c == ' ')
+ git_str_putc(&sandboxname, ',');
+ else if (*c == '=')
+ git_str_putc(&sandboxname, '_');
+ else
+ git_str_putc(&sandboxname, *c);
+ }
+
+ git_str_printf(&attrbuf, "* %s\n", attrs);
+ cl_git_mkfile("crlf/.gitattributes", attrbuf.ptr);
+ }
+
+ cl_repo_set_string(g_repo, "core.safecrlf", safecrlf);
+ cl_repo_set_string(g_repo, "core.autocrlf", autocrlf);
+
+ cl_git_pass(git_index_clear(g_index));
+
+ git_str_joinpath(&expected_dirname, "crlf_data", system_type());
+ git_str_puts(&expected_dirname, "_to_odb");
+
+ git_str_joinpath(&expected_fixture, expected_dirname.ptr, sandboxname.ptr);
+ cl_fixture_sandbox(expected_fixture.ptr);
+
+ compare_data.dirname = sandboxname.ptr;
+ cl_git_pass(git_fs_path_direach(&reponame, 0, add_and_check_file, &compare_data));
+
+ cl_fixture_cleanup(expected_fixture.ptr);
+ git_str_dispose(&expected_fixture);
+
+ git_str_dispose(&attrbuf);
+ git_str_dispose(&expected_fixture);
+ git_str_dispose(&expected_dirname);
+ git_str_dispose(&sandboxname);
+ git_str_dispose(&reponame);
+}
+
+static void set_up_workingdir(const char *name)
+{
+ git_vector contents = GIT_VECTOR_INIT;
+ size_t i;
+ const char *fn;
+
+ git_fs_path_dirload(&contents, name, 0, 0);
+ git_vector_foreach(&contents, i, fn) {
+ char *basename = git_fs_path_basename(fn);
+ bool skip = strncasecmp(basename, ".git", 4) == 0 && strlen(basename) == 4;
+
+ git__free(basename);
+
+ if (skip)
+ continue;
+ p_unlink(fn);
+ }
+ git_vector_free_deep(&contents);
+
+ /* copy input files */
+ git_fs_path_dirload(&contents, cl_fixture("crlf"), 0, 0);
+ git_vector_foreach(&contents, i, fn) {
+ char *basename = git_fs_path_basename(fn);
+ git_str dest_filename = GIT_STR_INIT;
+
+ if (strcmp(basename, ".gitted") &&
+ strcmp(basename, ".gitattributes")) {
+ git_str_joinpath(&dest_filename, name, basename);
+ cl_git_pass(git_futils_cp(fn, dest_filename.ptr, 0644));
+ }
+
+ git__free(basename);
+ git_str_dispose(&dest_filename);
+ }
+ git_vector_free_deep(&contents);
+}
+
+void test_index_crlf__matches_core_git(void)
+{
+ const char *safecrlf[] = { "true", "false", "warn", NULL };
+ const char *autocrlf[] = { "true", "false", "input", NULL };
+ const char *attrs[] = { "", "-crlf", "-text", "eol=crlf", "eol=lf",
+ "text", "text eol=crlf", "text eol=lf",
+ "text=auto", "text=auto eol=crlf", "text=auto eol=lf",
+ NULL };
+ const char **a, **b, **c;
+
+ for (a = safecrlf; *a; a++) {
+ for (b = autocrlf; *b; b++) {
+ for (c = attrs; *c; c++) {
+ set_up_workingdir("crlf");
+ test_add_index(*a, *b, *c);
+ }
+ }
+ }
+}
+
+void test_index_crlf__autocrlf_false_no_attrs(void)
+{
+ const git_index_entry *entry;
+ git_oid oid;
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", false);
+
+ cl_git_mkfile("./crlf/newfile.txt",
+ (GIT_EOL_NATIVE == GIT_EOL_CRLF) ? FILE_CONTENTS_CRLF : FILE_CONTENTS_LF);
+
+ cl_git_pass(git_index_add_bypath(g_index, "newfile.txt"));
+ entry = git_index_get_bypath(g_index, "newfile.txt", 0);
+
+ cl_git_pass(git_oid__fromstr(&oid,
+ (GIT_EOL_NATIVE == GIT_EOL_CRLF) ? FILE_OID_CRLF : FILE_OID_LF,
+ GIT_OID_SHA1));
+ cl_assert_equal_oid(&oid, &entry->id);
+}
+
+void test_index_crlf__autocrlf_true_no_attrs(void)
+{
+ const git_index_entry *entry;
+ git_oid oid;
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+
+ cl_git_mkfile("./crlf/newfile.txt",
+ (GIT_EOL_NATIVE == GIT_EOL_CRLF) ? FILE_CONTENTS_CRLF : FILE_CONTENTS_LF);
+
+ cl_git_pass(git_index_add_bypath(g_index, "newfile.txt"));
+ entry = git_index_get_bypath(g_index, "newfile.txt", 0);
+
+ cl_git_pass(git_oid__fromstr(&oid, FILE_OID_LF, GIT_OID_SHA1));
+ cl_assert_equal_oid(&oid, &entry->id);
+}
+
+void test_index_crlf__autocrlf_input_no_attrs(void)
+{
+ const git_index_entry *entry;
+ git_oid oid;
+
+ cl_repo_set_string(g_repo, "core.autocrlf", "input");
+
+ cl_git_mkfile("./crlf/newfile.txt",
+ (GIT_EOL_NATIVE == GIT_EOL_CRLF) ? FILE_CONTENTS_CRLF : FILE_CONTENTS_LF);
+
+ cl_git_pass(git_index_add_bypath(g_index, "newfile.txt"));
+ entry = git_index_get_bypath(g_index, "newfile.txt", 0);
+
+ cl_git_pass(git_oid__fromstr(&oid, FILE_OID_LF, GIT_OID_SHA1));
+ cl_assert_equal_oid(&oid, &entry->id);
+}
+
+void test_index_crlf__autocrlf_false_text_auto_attr(void)
+{
+ const git_index_entry *entry;
+ git_oid oid;
+
+ cl_git_mkfile("./crlf/.gitattributes", "* text=auto\n");
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", false);
+
+ cl_git_mkfile("./crlf/newfile.txt",
+ (GIT_EOL_NATIVE == GIT_EOL_CRLF) ? FILE_CONTENTS_CRLF : FILE_CONTENTS_LF);
+
+ cl_git_pass(git_index_add_bypath(g_index, "newfile.txt"));
+ entry = git_index_get_bypath(g_index, "newfile.txt", 0);
+
+ cl_git_pass(git_oid__fromstr(&oid, FILE_OID_LF, GIT_OID_SHA1));
+ cl_assert_equal_oid(&oid, &entry->id);
+}
+
+void test_index_crlf__autocrlf_true_text_auto_attr(void)
+{
+ const git_index_entry *entry;
+ git_oid oid;
+
+ cl_git_mkfile("./crlf/.gitattributes", "* text=auto\n");
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", false);
+
+ cl_git_mkfile("./crlf/newfile.txt",
+ (GIT_EOL_NATIVE == GIT_EOL_CRLF) ? FILE_CONTENTS_CRLF : FILE_CONTENTS_LF);
+
+ cl_git_pass(git_index_add_bypath(g_index, "newfile.txt"));
+ entry = git_index_get_bypath(g_index, "newfile.txt", 0);
+
+ cl_git_pass(git_oid__fromstr(&oid, FILE_OID_LF, GIT_OID_SHA1));
+ cl_assert_equal_oid(&oid, &entry->id);
+}
+
+void test_index_crlf__autocrlf_input_text_auto_attr(void)
+{
+ const git_index_entry *entry;
+ git_oid oid;
+
+ cl_git_mkfile("./crlf/.gitattributes", "* text=auto\n");
+
+ cl_repo_set_string(g_repo, "core.autocrlf", "input");
+
+ cl_git_mkfile("./crlf/newfile.txt",
+ (GIT_EOL_NATIVE == GIT_EOL_CRLF) ? FILE_CONTENTS_CRLF : FILE_CONTENTS_LF);
+
+ cl_git_pass(git_index_add_bypath(g_index, "newfile.txt"));
+ entry = git_index_get_bypath(g_index, "newfile.txt", 0);
+
+ cl_git_pass(git_oid__fromstr(&oid, FILE_OID_LF, GIT_OID_SHA1));
+ cl_assert_equal_oid(&oid, &entry->id);
+}
+
+void test_index_crlf__safecrlf_true_autocrlf_input_text_auto_attr(void)
+{
+ const git_index_entry *entry;
+ git_oid oid;
+
+ cl_git_mkfile("./crlf/.gitattributes", "* text=auto\n");
+
+ cl_repo_set_string(g_repo, "core.autocrlf", "input");
+ cl_repo_set_bool(g_repo, "core.safecrlf", true);
+
+ cl_git_mkfile("./crlf/newfile.txt", FILE_CONTENTS_LF);
+
+ cl_git_pass(git_index_add_bypath(g_index, "newfile.txt"));
+ entry = git_index_get_bypath(g_index, "newfile.txt", 0);
+ cl_assert(entry);
+
+ cl_git_pass(git_oid__fromstr(&oid, FILE_OID_LF, GIT_OID_SHA1));
+ cl_assert_equal_oid(&oid, &entry->id);
+
+ cl_git_mkfile("./crlf/newfile2.txt", FILE_CONTENTS_CRLF);
+ cl_git_fail(git_index_add_bypath(g_index, "newfile2.txt"));
+}
+
+void test_index_crlf__safecrlf_true_autocrlf_input_text__no_attr(void)
+{
+ const git_index_entry *entry;
+ git_oid oid;
+
+ cl_repo_set_string(g_repo, "core.autocrlf", "input");
+ cl_repo_set_bool(g_repo, "core.safecrlf", true);
+
+ cl_git_mkfile("./crlf/newfile.txt", FILE_CONTENTS_LF);
+
+ cl_git_pass(git_index_add_bypath(g_index, "newfile.txt"));
+ entry = git_index_get_bypath(g_index, "newfile.txt", 0);
+ cl_assert(entry);
+
+ cl_git_pass(git_oid__fromstr(&oid, FILE_OID_LF, GIT_OID_SHA1));
+ cl_assert_equal_oid(&oid, &entry->id);
+
+ cl_git_mkfile("./crlf/newfile2.txt", FILE_CONTENTS_CRLF);
+ cl_git_fail(git_index_add_bypath(g_index, "newfile2.txt"));
+}
+
+void test_index_crlf__safecrlf_true_no_attrs(void)
+{
+ cl_repo_set_bool(g_repo, "core.autocrlf", true);
+ cl_repo_set_bool(g_repo, "core.safecrlf", true);
+
+ cl_git_mkfile("crlf/newfile.txt", ALL_LF_TEXT_RAW);
+ cl_git_fail(git_index_add_bypath(g_index, "newfile.txt"));
+
+ cl_git_mkfile("crlf/newfile.txt", ALL_CRLF_TEXT_RAW);
+ cl_git_pass(git_index_add_bypath(g_index, "newfile.txt"));
+
+ cl_git_mkfile("crlf/newfile.txt", MORE_CRLF_TEXT_RAW);
+ cl_git_fail(git_index_add_bypath(g_index, "newfile.txt"));
+
+ cl_git_mkfile("crlf/newfile.txt", MORE_LF_TEXT_RAW);
+ cl_git_fail(git_index_add_bypath(g_index, "newfile.txt"));
+}
diff --git a/tests/libgit2/index/filemodes.c b/tests/libgit2/index/filemodes.c
new file mode 100644
index 0000000..1ab8a9a
--- /dev/null
+++ b/tests/libgit2/index/filemodes.c
@@ -0,0 +1,319 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "index.h"
+
+static git_repository *g_repo = NULL;
+
+void test_index_filemodes__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("filemodes");
+}
+
+void test_index_filemodes__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_index_filemodes__read(void)
+{
+ git_index *index;
+ unsigned int i;
+ static bool expected[6] = { 0, 1, 0, 1, 0, 1 };
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_assert_equal_i(6, (int)git_index_entrycount(index));
+
+ for (i = 0; i < 6; ++i) {
+ const git_index_entry *entry = git_index_get_byindex(index, i);
+ cl_assert(entry != NULL);
+ cl_assert(((entry->mode & 0100) ? 1 : 0) == expected[i]);
+ }
+
+ git_index_free(index);
+}
+
+static void replace_file_with_mode(
+ const char *filename, const char *backup, unsigned int create_mode)
+{
+ git_str path = GIT_STR_INIT, content = GIT_STR_INIT;
+
+ cl_git_pass(git_str_joinpath(&path, "filemodes", filename));
+ cl_git_pass(git_str_printf(&content, "%s as %08u (%d)",
+ filename, create_mode, rand()));
+
+ cl_git_pass(p_rename(path.ptr, backup));
+ cl_git_write2file(
+ path.ptr, content.ptr, content.size,
+ O_WRONLY|O_CREAT|O_TRUNC, create_mode);
+
+ git_str_dispose(&path);
+ git_str_dispose(&content);
+}
+
+#define add_and_check_mode(I,F,X) add_and_check_mode_(I,F,X,__FILE__,__func__,__LINE__)
+
+static void add_and_check_mode_(
+ git_index *index, const char *filename, unsigned int expect_mode,
+ const char *file, const char *func, int line)
+{
+ size_t pos;
+ const git_index_entry *entry;
+
+ cl_git_pass(git_index_add_bypath(index, filename));
+
+ clar__assert(!git_index_find(&pos, index, filename),
+ file, func, line, "Cannot find index entry", NULL, 1);
+
+ entry = git_index_get_byindex(index, pos);
+
+ clar__assert_equal(file, func, line, "Expected mode does not match index",
+ 1, "%07o", (unsigned int)entry->mode, (unsigned int)expect_mode);
+}
+
+void test_index_filemodes__untrusted(void)
+{
+ git_index *index;
+
+ cl_repo_set_bool(g_repo, "core.filemode", false);
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_assert((git_index_caps(index) & GIT_INDEX_CAPABILITY_NO_FILEMODE) != 0);
+
+ /* 1 - add 0644 over existing 0644 -> expect 0644 */
+ replace_file_with_mode("exec_off", "filemodes/exec_off.0", 0644);
+ add_and_check_mode(index, "exec_off", GIT_FILEMODE_BLOB);
+
+ /* 2 - add 0644 over existing 0755 -> expect 0755 */
+ replace_file_with_mode("exec_on", "filemodes/exec_on.0", 0644);
+ add_and_check_mode(index, "exec_on", GIT_FILEMODE_BLOB_EXECUTABLE);
+
+ /* 3 - add 0755 over existing 0644 -> expect 0644 */
+ replace_file_with_mode("exec_off", "filemodes/exec_off.1", 0755);
+ add_and_check_mode(index, "exec_off", GIT_FILEMODE_BLOB);
+
+ /* 4 - add 0755 over existing 0755 -> expect 0755 */
+ replace_file_with_mode("exec_on", "filemodes/exec_on.1", 0755);
+ add_and_check_mode(index, "exec_on", GIT_FILEMODE_BLOB_EXECUTABLE);
+
+ /* 5 - add new 0644 -> expect 0644 */
+ cl_git_write2file("filemodes/new_off", "blah", 0,
+ O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ add_and_check_mode(index, "new_off", GIT_FILEMODE_BLOB);
+
+ /* 6 - add new 0755 -> expect 0644 if core.filemode == false */
+ cl_git_write2file("filemodes/new_on", "blah", 0,
+ O_WRONLY | O_CREAT | O_TRUNC, 0755);
+ add_and_check_mode(index, "new_on", GIT_FILEMODE_BLOB);
+
+ git_index_free(index);
+}
+
+void test_index_filemodes__trusted(void)
+{
+ git_index *index;
+
+ /* Only run these tests on platforms where I can actually
+ * chmod a file and get the stat results I expect!
+ */
+ if (!cl_is_chmod_supported())
+ return;
+
+ cl_repo_set_bool(g_repo, "core.filemode", true);
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_assert((git_index_caps(index) & GIT_INDEX_CAPABILITY_NO_FILEMODE) == 0);
+
+ /* 1 - add 0644 over existing 0644 -> expect 0644 */
+ replace_file_with_mode("exec_off", "filemodes/exec_off.0", 0644);
+ add_and_check_mode(index, "exec_off", GIT_FILEMODE_BLOB);
+
+ /* 2 - add 0644 over existing 0755 -> expect 0644 */
+ replace_file_with_mode("exec_on", "filemodes/exec_on.0", 0644);
+ add_and_check_mode(index, "exec_on", GIT_FILEMODE_BLOB);
+
+ /* 3 - add 0755 over existing 0644 -> expect 0755 */
+ replace_file_with_mode("exec_off", "filemodes/exec_off.1", 0755);
+ add_and_check_mode(index, "exec_off", GIT_FILEMODE_BLOB_EXECUTABLE);
+
+ /* 4 - add 0755 over existing 0755 -> expect 0755 */
+ replace_file_with_mode("exec_on", "filemodes/exec_on.1", 0755);
+ add_and_check_mode(index, "exec_on", GIT_FILEMODE_BLOB_EXECUTABLE);
+
+ /* 5 - add new 0644 -> expect 0644 */
+ cl_git_write2file("filemodes/new_off", "blah", 0,
+ O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ add_and_check_mode(index, "new_off", GIT_FILEMODE_BLOB);
+
+ /* 6 - add 0755 -> expect 0755 */
+ cl_git_write2file("filemodes/new_on", "blah", 0,
+ O_WRONLY | O_CREAT | O_TRUNC, 0755);
+ add_and_check_mode(index, "new_on", GIT_FILEMODE_BLOB_EXECUTABLE);
+
+ git_index_free(index);
+}
+
+#define add_entry_and_check_mode(I,FF,X) add_entry_and_check_mode_(I,FF,X,__FILE__,__func__,__LINE__)
+
+static void add_entry_and_check_mode_(
+ git_index *index, bool from_file, git_filemode_t mode,
+ const char *file, const char *func, int line)
+{
+ size_t pos;
+ const git_index_entry* entry;
+ git_index_entry new_entry;
+
+ /* If old_filename exists, we copy that to the new file, and test
+ * git_index_add(), otherwise create a new entry testing git_index_add_from_buffer
+ */
+ if (from_file)
+ {
+ clar__assert(!git_index_find(&pos, index, "exec_off"),
+ file, func, line, "Cannot find original index entry", NULL, 1);
+
+ entry = git_index_get_byindex(index, pos);
+
+ memcpy(&new_entry, entry, sizeof(new_entry));
+ }
+ else
+ memset(&new_entry, 0x0, sizeof(git_index_entry));
+
+ new_entry.path = "filemodes/explicit_test";
+ new_entry.mode = mode;
+
+ if (from_file)
+ {
+ clar__assert(!git_index_add(index, &new_entry),
+ file, func, line, "Cannot add index entry", NULL, 1);
+ }
+ else
+ {
+ const char *content = "hey there\n";
+ clar__assert(!git_index_add_from_buffer(index, &new_entry, content, strlen(content)),
+ file, func, line, "Cannot add index entry from buffer", NULL, 1);
+ }
+
+ clar__assert(!git_index_find(&pos, index, "filemodes/explicit_test"),
+ file, func, line, "Cannot find new index entry", NULL, 1);
+
+ entry = git_index_get_byindex(index, pos);
+
+ clar__assert_equal(file, func, line, "Expected mode does not match index",
+ 1, "%07o", (unsigned int)entry->mode, (unsigned int)mode);
+}
+
+void test_index_filemodes__explicit(void)
+{
+ git_index *index;
+
+ /* These tests should run and work everywhere, as the filemode is
+ * given explicitly to git_index_add or git_index_add_from_buffer
+ */
+ cl_repo_set_bool(g_repo, "core.filemode", false);
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ /* Each of these tests keeps overwriting the same file in the index. */
+ /* 1 - add new 0644 entry */
+ add_entry_and_check_mode(index, true, GIT_FILEMODE_BLOB);
+
+ /* 2 - add 0755 entry over existing 0644 */
+ add_entry_and_check_mode(index, true, GIT_FILEMODE_BLOB_EXECUTABLE);
+
+ /* 3 - add 0644 entry over existing 0755 */
+ add_entry_and_check_mode(index, true, GIT_FILEMODE_BLOB);
+
+ /* 4 - add 0755 buffer entry over existing 0644 */
+ add_entry_and_check_mode(index, false, GIT_FILEMODE_BLOB_EXECUTABLE);
+
+ /* 5 - add 0644 buffer entry over existing 0755 */
+ add_entry_and_check_mode(index, false, GIT_FILEMODE_BLOB);
+
+ git_index_free(index);
+}
+
+void test_index_filemodes__invalid(void)
+{
+ git_index *index;
+ git_index_entry entry;
+ const git_index_entry *dummy;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ /* add a dummy file so that we have a valid id */
+ cl_git_mkfile("./filemodes/dummy-file.txt", "new-file\n");
+ cl_git_pass(git_index_add_bypath(index, "dummy-file.txt"));
+ cl_assert((dummy = git_index_get_bypath(index, "dummy-file.txt", 0)));
+
+ GIT_INDEX_ENTRY_STAGE_SET(&entry, 0);
+ entry.path = "foo";
+ entry.mode = GIT_OBJECT_BLOB;
+ git_oid_cpy(&entry.id, &dummy->id);
+ cl_git_fail(git_index_add(index, &entry));
+
+ entry.mode = GIT_FILEMODE_BLOB;
+ cl_git_pass(git_index_add(index, &entry));
+
+ git_index_free(index);
+}
+
+void test_index_filemodes__frombuffer_requires_files(void)
+{
+ git_index *index;
+ git_index_entry new_entry;
+ const git_index_entry *ret_entry;
+ const char *content = "hey there\n";
+
+ memset(&new_entry, 0, sizeof(new_entry));
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ /* regular blob */
+ new_entry.path = "dummy-file.txt";
+ new_entry.mode = GIT_FILEMODE_BLOB;
+
+ cl_git_pass(git_index_add_from_buffer(index,
+ &new_entry, content, strlen(content)));
+
+ cl_assert((ret_entry = git_index_get_bypath(index, "dummy-file.txt", 0)));
+ cl_assert_equal_s("dummy-file.txt", ret_entry->path);
+ cl_assert_equal_i(GIT_FILEMODE_BLOB, ret_entry->mode);
+
+ /* executable blob */
+ new_entry.path = "dummy-file.txt";
+ new_entry.mode = GIT_FILEMODE_BLOB_EXECUTABLE;
+
+ cl_git_pass(git_index_add_from_buffer(index,
+ &new_entry, content, strlen(content)));
+
+ cl_assert((ret_entry = git_index_get_bypath(index, "dummy-file.txt", 0)));
+ cl_assert_equal_s("dummy-file.txt", ret_entry->path);
+ cl_assert_equal_i(GIT_FILEMODE_BLOB_EXECUTABLE, ret_entry->mode);
+
+ /* links are also acceptable */
+ new_entry.path = "dummy-link.txt";
+ new_entry.mode = GIT_FILEMODE_LINK;
+
+ cl_git_pass(git_index_add_from_buffer(index,
+ &new_entry, content, strlen(content)));
+
+ cl_assert((ret_entry = git_index_get_bypath(index, "dummy-link.txt", 0)));
+ cl_assert_equal_s("dummy-link.txt", ret_entry->path);
+ cl_assert_equal_i(GIT_FILEMODE_LINK, ret_entry->mode);
+
+ /* trees are rejected */
+ new_entry.path = "invalid_mode.txt";
+ new_entry.mode = GIT_FILEMODE_TREE;
+
+ cl_git_fail(git_index_add_from_buffer(index,
+ &new_entry, content, strlen(content)));
+ cl_assert_equal_p(NULL, git_index_get_bypath(index, "invalid_mode.txt", 0));
+
+ /* submodules are rejected */
+ new_entry.path = "invalid_mode.txt";
+ new_entry.mode = GIT_FILEMODE_COMMIT;
+
+ cl_git_fail(git_index_add_from_buffer(index,
+ &new_entry, content, strlen(content)));
+ cl_assert_equal_p(NULL, git_index_get_bypath(index, "invalid_mode.txt", 0));
+
+ git_index_free(index);
+}
diff --git a/tests/libgit2/index/inmemory.c b/tests/libgit2/index/inmemory.c
new file mode 100644
index 0000000..39374af
--- /dev/null
+++ b/tests/libgit2/index/inmemory.c
@@ -0,0 +1,23 @@
+#include "clar_libgit2.h"
+#include "index.h"
+
+void test_index_inmemory__can_create_an_inmemory_index(void)
+{
+ git_index *index;
+
+ cl_git_pass(git_index__new(&index, GIT_OID_SHA1));
+ cl_assert_equal_i(0, (int)git_index_entrycount(index));
+
+ git_index_free(index);
+}
+
+void test_index_inmemory__cannot_add_bypath_to_an_inmemory_index(void)
+{
+ git_index *index;
+
+ cl_git_pass(git_index__new(&index, GIT_OID_SHA1));
+
+ cl_assert_equal_i(GIT_ERROR, git_index_add_bypath(index, "test.txt"));
+
+ git_index_free(index);
+}
diff --git a/tests/libgit2/index/names.c b/tests/libgit2/index/names.c
new file mode 100644
index 0000000..2a41100
--- /dev/null
+++ b/tests/libgit2/index/names.c
@@ -0,0 +1,187 @@
+#include "clar_libgit2.h"
+#include "index.h"
+#include "git2/sys/index.h"
+#include "git2/repository.h"
+#include "../reset/reset_helpers.h"
+
+static git_repository *repo;
+static git_index *repo_index;
+
+#define TEST_REPO_PATH "mergedrepo"
+#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
+
+/* Fixture setup and teardown */
+void test_index_names__initialize(void)
+{
+ repo = cl_git_sandbox_init("mergedrepo");
+ git_repository_index(&repo_index, repo);
+}
+
+void test_index_names__cleanup(void)
+{
+ git_index_free(repo_index);
+ repo_index = NULL;
+
+ cl_git_sandbox_cleanup();
+}
+
+static void index_add_conflicts(void)
+{
+ git_index_entry entry = {{0}};
+ const char *paths[][3] = {
+ { "ancestor", "ours", "theirs" },
+ { "ancestor2", "ours2", "theirs2" },
+ { "ancestor3", "ours3", "theirs3" } };
+ const char **conflict;
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(paths); i++) {
+ conflict = paths[i];
+
+ /* ancestor */
+ entry.path = conflict[0];
+ entry.mode = GIT_FILEMODE_BLOB;
+ GIT_INDEX_ENTRY_STAGE_SET(&entry, GIT_INDEX_STAGE_ANCESTOR);
+ git_oid__fromstr(&entry.id, "1f85ca51b8e0aac893a621b61a9c2661d6aa6d81", GIT_OID_SHA1);
+ cl_git_pass(git_index_add(repo_index, &entry));
+
+ /* ours */
+ entry.path = conflict[1];
+ entry.mode = GIT_FILEMODE_BLOB;
+ GIT_INDEX_ENTRY_STAGE_SET(&entry, GIT_INDEX_STAGE_OURS);
+ git_oid__fromstr(&entry.id, "1f85ca51b8e0aac893a621b61a9c2661d6aa6d81", GIT_OID_SHA1);
+ cl_git_pass(git_index_add(repo_index, &entry));
+
+ /* theirs */
+ entry.path = conflict[2];
+ entry.mode = GIT_FILEMODE_BLOB;
+ GIT_INDEX_ENTRY_STAGE_SET(&entry, GIT_INDEX_STAGE_THEIRS);
+ git_oid__fromstr(&entry.id, "1f85ca51b8e0aac893a621b61a9c2661d6aa6d81", GIT_OID_SHA1);
+ cl_git_pass(git_index_add(repo_index, &entry));
+ }
+}
+
+void test_index_names__add(void)
+{
+ const git_index_name_entry *conflict_name;
+
+ index_add_conflicts();
+ cl_git_pass(git_index_name_add(repo_index, "ancestor", "ours", "theirs"));
+ cl_git_pass(git_index_name_add(repo_index, "ancestor2", "ours2", NULL));
+ cl_git_pass(git_index_name_add(repo_index, "ancestor3", NULL, "theirs3"));
+
+ cl_assert(git_index_name_entrycount(repo_index) == 3);
+
+ conflict_name = git_index_name_get_byindex(repo_index, 0);
+ cl_assert(strcmp(conflict_name->ancestor, "ancestor") == 0);
+ cl_assert(strcmp(conflict_name->ours, "ours") == 0);
+ cl_assert(strcmp(conflict_name->theirs, "theirs") == 0);
+
+ conflict_name = git_index_name_get_byindex(repo_index, 1);
+ cl_assert(strcmp(conflict_name->ancestor, "ancestor2") == 0);
+ cl_assert(strcmp(conflict_name->ours, "ours2") == 0);
+ cl_assert(conflict_name->theirs == NULL);
+
+ conflict_name = git_index_name_get_byindex(repo_index, 2);
+ cl_assert(strcmp(conflict_name->ancestor, "ancestor3") == 0);
+ cl_assert(conflict_name->ours == NULL);
+ cl_assert(strcmp(conflict_name->theirs, "theirs3") == 0);
+
+ cl_git_pass(git_index_write(repo_index));
+}
+
+void test_index_names__roundtrip(void)
+{
+ const git_index_name_entry *conflict_name;
+
+ cl_git_pass(git_index_name_add(repo_index, "ancestor", "ours", "theirs"));
+ cl_git_pass(git_index_name_add(repo_index, "ancestor2", "ours2", NULL));
+ cl_git_pass(git_index_name_add(repo_index, "ancestor3", NULL, "theirs3"));
+
+ cl_git_pass(git_index_write(repo_index));
+ git_index_clear(repo_index);
+ cl_assert(git_index_name_entrycount(repo_index) == 0);
+
+ cl_git_pass(git_index_read(repo_index, true));
+ cl_assert(git_index_name_entrycount(repo_index) == 3);
+
+ conflict_name = git_index_name_get_byindex(repo_index, 0);
+ cl_assert(strcmp(conflict_name->ancestor, "ancestor") == 0);
+ cl_assert(strcmp(conflict_name->ours, "ours") == 0);
+ cl_assert(strcmp(conflict_name->theirs, "theirs") == 0);
+
+ conflict_name = git_index_name_get_byindex(repo_index, 1);
+ cl_assert(strcmp(conflict_name->ancestor, "ancestor2") == 0);
+ cl_assert(strcmp(conflict_name->ours, "ours2") == 0);
+ cl_assert(conflict_name->theirs == NULL);
+
+ conflict_name = git_index_name_get_byindex(repo_index, 2);
+ cl_assert(strcmp(conflict_name->ancestor, "ancestor3") == 0);
+ cl_assert(conflict_name->ours == NULL);
+ cl_assert(strcmp(conflict_name->theirs, "theirs3") == 0);
+}
+
+void test_index_names__cleaned_on_reset_hard(void)
+{
+ git_object *target;
+
+ cl_git_pass(git_revparse_single(&target, repo, "3a34580"));
+
+ test_index_names__add();
+ cl_git_pass(git_reset(repo, target, GIT_RESET_HARD, NULL));
+ cl_assert(git_index_name_entrycount(repo_index) == 0);
+
+ git_object_free(target);
+}
+
+void test_index_names__cleaned_on_reset_mixed(void)
+{
+ git_object *target;
+
+ cl_git_pass(git_revparse_single(&target, repo, "3a34580"));
+
+ test_index_names__add();
+ cl_git_pass(git_reset(repo, target, GIT_RESET_MIXED, NULL));
+ cl_assert(git_index_name_entrycount(repo_index) == 0);
+
+ git_object_free(target);
+}
+
+void test_index_names__cleaned_on_checkout_tree(void)
+{
+ git_oid oid;
+ git_object *obj;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE | GIT_CHECKOUT_UPDATE_ONLY;
+
+ test_index_names__add();
+ cl_git_pass(git_reference_name_to_id(&oid, repo, "refs/heads/master"));
+ cl_git_pass(git_object_lookup(&obj, repo, &oid, GIT_OBJECT_ANY));
+ cl_git_pass(git_checkout_tree(repo, obj, &opts));
+ cl_assert_equal_sz(0, git_index_name_entrycount(repo_index));
+
+ git_object_free(obj);
+}
+
+void test_index_names__cleaned_on_checkout_head(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE | GIT_CHECKOUT_UPDATE_ONLY;
+
+ test_index_names__add();
+ cl_git_pass(git_checkout_head(repo, &opts));
+ cl_assert_equal_sz(0, git_index_name_entrycount(repo_index));
+}
+
+void test_index_names__retained_on_checkout_index(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE | GIT_CHECKOUT_UPDATE_ONLY;
+
+ test_index_names__add();
+ cl_git_pass(git_checkout_index(repo, repo_index, &opts));
+ cl_assert(git_index_name_entrycount(repo_index) > 0);
+}
diff --git a/tests/libgit2/index/nsec.c b/tests/libgit2/index/nsec.c
new file mode 100644
index 0000000..3efd855
--- /dev/null
+++ b/tests/libgit2/index/nsec.c
@@ -0,0 +1,129 @@
+#include "clar_libgit2.h"
+#include "index.h"
+#include "git2/sys/index.h"
+#include "git2/repository.h"
+#include "../reset/reset_helpers.h"
+
+static git_repository *repo;
+static git_index *repo_index;
+
+#define TEST_REPO_PATH "nsecs"
+
+/* Fixture setup and teardown */
+void test_index_nsec__initialize(void)
+{
+ repo = cl_git_sandbox_init("nsecs");
+ git_repository_index(&repo_index, repo);
+}
+
+void test_index_nsec__cleanup(void)
+{
+ git_index_free(repo_index);
+ repo_index = NULL;
+
+ cl_git_sandbox_cleanup();
+}
+
+static bool try_create_file_with_nsec_timestamp(const char *path)
+{
+ struct stat st;
+ int try;
+
+ /* retry a few times to avoid nanos *actually* equal 0 race condition */
+ for (try = 0; try < 3; try++) {
+ cl_git_mkfile(path, "This is hopefully a file with nanoseconds!");
+
+ cl_must_pass(p_stat(path, &st));
+
+ if (st.st_ctime_nsec && st.st_mtime_nsec)
+ return true;
+ }
+
+ return false;
+}
+
+/* try to determine if the underlying filesystem supports a resolution
+ * higher than a single second. (i'm looking at you, hfs+)
+ */
+static bool should_expect_nsecs(void)
+{
+ git_str nsec_path = GIT_STR_INIT;
+ bool expect;
+
+ git_str_joinpath(&nsec_path, clar_sandbox_path(), "nsec_test");
+
+ expect = try_create_file_with_nsec_timestamp(nsec_path.ptr);
+
+ cl_must_pass(p_unlink(nsec_path.ptr));
+
+ git_str_dispose(&nsec_path);
+
+ return expect;
+}
+
+static bool has_nsecs(void)
+{
+ const git_index_entry *entry;
+ size_t i;
+ bool has_nsecs = false;
+
+ for (i = 0; i < git_index_entrycount(repo_index); i++) {
+ entry = git_index_get_byindex(repo_index, i);
+
+ if (entry->ctime.nanoseconds || entry->mtime.nanoseconds) {
+ has_nsecs = true;
+ break;
+ }
+ }
+
+ return has_nsecs;
+}
+
+void test_index_nsec__has_nanos(void)
+{
+ cl_assert_equal_b(true, has_nsecs());
+}
+
+void test_index_nsec__staging_maintains_other_nanos(void)
+{
+ const git_index_entry *entry;
+ bool expect_nsec, test_file_has_nsec;
+
+ expect_nsec = should_expect_nsecs();
+ test_file_has_nsec = try_create_file_with_nsec_timestamp("nsecs/a.txt");
+
+ cl_assert_equal_b(expect_nsec, test_file_has_nsec);
+
+ cl_git_pass(git_index_add_bypath(repo_index, "a.txt"));
+ cl_git_pass(git_index_write(repo_index));
+
+ cl_git_pass(git_index_write(repo_index));
+
+ git_index_read(repo_index, 1);
+ cl_assert_equal_b(true, has_nsecs());
+
+ cl_assert((entry = git_index_get_bypath(repo_index, "a.txt", 0)));
+
+ /* if we are writing nanoseconds to the index, expect them to be
+ * nonzero.
+ */
+ if (expect_nsec) {
+ cl_assert(entry->ctime.nanoseconds != 0);
+ cl_assert(entry->mtime.nanoseconds != 0);
+ } else {
+ cl_assert_equal_i(0, entry->ctime.nanoseconds);
+ cl_assert_equal_i(0, entry->mtime.nanoseconds);
+ }
+}
+
+void test_index_nsec__status_doesnt_clear_nsecs(void)
+{
+ git_status_list *statuslist;
+
+ cl_git_pass(git_status_list_new(&statuslist, repo, NULL));
+
+ git_index_read(repo_index, 1);
+ cl_assert_equal_b(true, has_nsecs());
+
+ git_status_list_free(statuslist);
+}
diff --git a/tests/libgit2/index/racy.c b/tests/libgit2/index/racy.c
new file mode 100644
index 0000000..a1d6f9c
--- /dev/null
+++ b/tests/libgit2/index/racy.c
@@ -0,0 +1,323 @@
+#include "clar_libgit2.h"
+#include "../checkout/checkout_helpers.h"
+
+#include "index.h"
+#include "repository.h"
+
+static git_repository *g_repo;
+
+void test_index_racy__initialize(void)
+{
+ cl_git_pass(git_repository_init(&g_repo, "diff_racy", false));
+}
+
+void test_index_racy__cleanup(void)
+{
+ git_repository_free(g_repo);
+ g_repo = NULL;
+
+ cl_fixture_cleanup("diff_racy");
+}
+
+void test_index_racy__diff(void)
+{
+ git_index *index;
+ git_diff *diff;
+ git_str path = GIT_STR_INIT;
+
+ cl_git_pass(git_str_joinpath(&path, git_repository_workdir(g_repo), "A"));
+ cl_git_mkfile(path.ptr, "A");
+
+ /* Put 'A' into the index */
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_add_bypath(index, "A"));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, NULL));
+ cl_assert_equal_i(0, git_diff_num_deltas(diff));
+ git_diff_free(diff);
+
+ /* Change its contents quickly, so we get the same timestamp */
+ cl_git_mkfile(path.ptr, "B");
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, NULL));
+ cl_assert_equal_i(1, git_diff_num_deltas(diff));
+
+ git_index_free(index);
+ git_diff_free(diff);
+ git_str_dispose(&path);
+}
+
+void test_index_racy__write_index_just_after_file(void)
+{
+ git_index *index;
+ git_diff *diff;
+ git_str path = GIT_STR_INIT;
+ struct p_timeval times[2];
+
+ /* Make sure we do have a timestamp */
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_str_joinpath(&path, git_repository_workdir(g_repo), "A"));
+ cl_git_mkfile(path.ptr, "A");
+ /* Force the file's timestamp to be a second after we wrote the index */
+ times[0].tv_sec = index->stamp.mtime.tv_sec + 1;
+ times[0].tv_usec = index->stamp.mtime.tv_nsec / 1000;
+ times[1].tv_sec = index->stamp.mtime.tv_sec + 1;
+ times[1].tv_usec = index->stamp.mtime.tv_nsec / 1000;
+ cl_git_pass(p_utimes(path.ptr, times));
+
+ /*
+ * Put 'A' into the index, the size field will be filled,
+ * because the index' on-disk timestamp does not match the
+ * file's timestamp.
+ */
+ cl_git_pass(git_index_add_bypath(index, "A"));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_mkfile(path.ptr, "B");
+ /*
+ * Pretend this index' modification happened a second after the
+ * file update, and rewrite the file in that same second.
+ */
+ times[0].tv_sec = index->stamp.mtime.tv_sec + 2;
+ times[0].tv_usec = index->stamp.mtime.tv_nsec / 1000;
+ times[1].tv_sec = index->stamp.mtime.tv_sec + 2;
+ times[0].tv_usec = index->stamp.mtime.tv_nsec / 1000;
+
+ cl_git_pass(p_utimes(git_index_path(index), times));
+ cl_git_pass(p_utimes(path.ptr, times));
+
+ cl_git_pass(git_index_read(index, true));
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, NULL));
+ cl_assert_equal_i(1, git_diff_num_deltas(diff));
+
+ git_str_dispose(&path);
+ git_diff_free(diff);
+ git_index_free(index);
+}
+
+
+static void setup_race(void)
+{
+ git_str path = GIT_STR_INIT;
+ git_index *index;
+ git_index_entry *entry;
+ struct stat st;
+
+ /* Make sure we do have a timestamp */
+ cl_git_pass(git_repository_index__weakptr(&index, g_repo));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_str_joinpath(&path, git_repository_workdir(g_repo), "A"));
+
+ cl_git_mkfile(path.ptr, "A");
+ cl_git_pass(git_index_add_bypath(index, "A"));
+
+ cl_git_mkfile(path.ptr, "B");
+ cl_git_pass(git_index_write(index));
+
+ cl_git_mkfile(path.ptr, "");
+
+ cl_git_pass(p_stat(path.ptr, &st));
+ cl_assert(entry = (git_index_entry *)git_index_get_bypath(index, "A", 0));
+
+ /* force a race */
+ entry->mtime.seconds = (int32_t)st.st_mtime;
+ entry->mtime.nanoseconds = (int32_t)st.st_mtime_nsec;
+
+ git_str_dispose(&path);
+}
+
+void test_index_racy__smudges_index_entry_on_save(void)
+{
+ git_index *index;
+ const git_index_entry *entry;
+
+ setup_race();
+
+ /* write the index, which will smudge anything that had the same timestamp
+ * as the index when the index was loaded. that way future loads of the
+ * index (with the new timestamp) will know that these files were not
+ * clean.
+ */
+
+ cl_git_pass(git_repository_index__weakptr(&index, g_repo));
+ cl_git_pass(git_index_write(index));
+
+ cl_assert(entry = git_index_get_bypath(index, "A", 0));
+ cl_assert_equal_i(0, entry->file_size);
+}
+
+void test_index_racy__detects_diff_of_change_in_identical_timestamp(void)
+{
+ git_index *index;
+ git_diff *diff;
+
+ cl_git_pass(git_repository_index__weakptr(&index, g_repo));
+
+ setup_race();
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, g_repo, index, NULL));
+ cl_assert_equal_i(1, git_diff_num_deltas(diff));
+
+ git_diff_free(diff);
+}
+
+static void setup_uptodate_files(void)
+{
+ git_str path = GIT_STR_INIT;
+ git_index *index;
+ const git_index_entry *a_entry;
+ git_index_entry new_entry = {{0}};
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_pass(git_str_joinpath(&path, git_repository_workdir(g_repo), "A"));
+ cl_git_mkfile(path.ptr, "A");
+
+ /* Put 'A' into the index */
+ cl_git_pass(git_index_add_bypath(index, "A"));
+
+ cl_assert((a_entry = git_index_get_bypath(index, "A", 0)));
+
+ /* Put 'B' into the index */
+ new_entry.path = "B";
+ new_entry.mode = GIT_FILEMODE_BLOB;
+ git_oid_cpy(&new_entry.id, &a_entry->id);
+ cl_git_pass(git_index_add(index, &new_entry));
+
+ /* Put 'C' into the index */
+ new_entry.path = "C";
+ new_entry.mode = GIT_FILEMODE_BLOB;
+ cl_git_pass(git_index_add_from_buffer(index, &new_entry, "hello!\n", 7));
+
+ git_index_free(index);
+ git_str_dispose(&path);
+}
+
+void test_index_racy__adding_to_index_is_uptodate(void)
+{
+ git_index *index;
+ const git_index_entry *entry;
+
+ setup_uptodate_files();
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ /* ensure that they're all uptodate */
+ cl_assert((entry = git_index_get_bypath(index, "A", 0)));
+ cl_assert_equal_i(GIT_INDEX_ENTRY_UPTODATE, (entry->flags_extended & GIT_INDEX_ENTRY_UPTODATE));
+
+ cl_assert((entry = git_index_get_bypath(index, "B", 0)));
+ cl_assert_equal_i(GIT_INDEX_ENTRY_UPTODATE, (entry->flags_extended & GIT_INDEX_ENTRY_UPTODATE));
+
+ cl_assert((entry = git_index_get_bypath(index, "C", 0)));
+ cl_assert_equal_i(GIT_INDEX_ENTRY_UPTODATE, (entry->flags_extended & GIT_INDEX_ENTRY_UPTODATE));
+
+ cl_git_pass(git_index_write(index));
+
+ git_index_free(index);
+}
+
+void test_index_racy__reading_clears_uptodate_bit(void)
+{
+ git_index *index;
+ const git_index_entry *entry;
+
+ setup_uptodate_files();
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_index_read(index, true));
+
+ /* ensure that no files are uptodate */
+ cl_assert((entry = git_index_get_bypath(index, "A", 0)));
+ cl_assert_equal_i(0, (entry->flags_extended & GIT_INDEX_ENTRY_UPTODATE));
+
+ cl_assert((entry = git_index_get_bypath(index, "B", 0)));
+ cl_assert_equal_i(0, (entry->flags_extended & GIT_INDEX_ENTRY_UPTODATE));
+
+ cl_assert((entry = git_index_get_bypath(index, "C", 0)));
+ cl_assert_equal_i(0, (entry->flags_extended & GIT_INDEX_ENTRY_UPTODATE));
+
+ git_index_free(index);
+}
+
+void test_index_racy__read_tree_clears_uptodate_bit(void)
+{
+ git_index *index;
+ git_tree *tree;
+ const git_index_entry *entry;
+ git_oid id;
+
+ setup_uptodate_files();
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_write_tree_to(&id, index, g_repo));
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
+ cl_git_pass(git_index_read_tree(index, tree));
+
+ /* ensure that no files are uptodate */
+ cl_assert((entry = git_index_get_bypath(index, "A", 0)));
+ cl_assert_equal_i(0, (entry->flags_extended & GIT_INDEX_ENTRY_UPTODATE));
+
+ cl_assert((entry = git_index_get_bypath(index, "B", 0)));
+ cl_assert_equal_i(0, (entry->flags_extended & GIT_INDEX_ENTRY_UPTODATE));
+
+ cl_assert((entry = git_index_get_bypath(index, "C", 0)));
+ cl_assert_equal_i(0, (entry->flags_extended & GIT_INDEX_ENTRY_UPTODATE));
+
+ git_tree_free(tree);
+ git_index_free(index);
+}
+
+void test_index_racy__read_index_smudges(void)
+{
+ git_index *index, *newindex;
+ const git_index_entry *entry;
+
+ /* if we are reading an index into our new index, ensure that any
+ * racy entries in the index that we're reading are smudged so that
+ * we don't propagate their timestamps without further investigation.
+ */
+ setup_race();
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index__new(&newindex, GIT_OID_SHA1));
+ cl_git_pass(git_index_read_index(newindex, index));
+
+ cl_assert(entry = git_index_get_bypath(newindex, "A", 0));
+ cl_assert_equal_i(0, entry->file_size);
+
+ git_index_free(index);
+ git_index_free(newindex);
+}
+
+void test_index_racy__read_index_clears_uptodate_bit(void)
+{
+ git_index *index, *newindex;
+ const git_index_entry *entry;
+
+ setup_uptodate_files();
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index__new(&newindex, GIT_OID_SHA1));
+ cl_git_pass(git_index_read_index(newindex, index));
+
+ /* ensure that files brought in from the other index are not uptodate */
+ cl_assert((entry = git_index_get_bypath(newindex, "A", 0)));
+ cl_assert_equal_i(0, (entry->flags_extended & GIT_INDEX_ENTRY_UPTODATE));
+
+ cl_assert((entry = git_index_get_bypath(newindex, "B", 0)));
+ cl_assert_equal_i(0, (entry->flags_extended & GIT_INDEX_ENTRY_UPTODATE));
+
+ cl_assert((entry = git_index_get_bypath(newindex, "C", 0)));
+ cl_assert_equal_i(0, (entry->flags_extended & GIT_INDEX_ENTRY_UPTODATE));
+
+ git_index_free(index);
+ git_index_free(newindex);
+}
diff --git a/tests/libgit2/index/read_index.c b/tests/libgit2/index/read_index.c
new file mode 100644
index 0000000..9c80be2
--- /dev/null
+++ b/tests/libgit2/index/read_index.c
@@ -0,0 +1,232 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "index.h"
+#include "conflicts.h"
+
+static git_repository *_repo;
+static git_index *_index;
+
+void test_index_read_index__initialize(void)
+{
+ git_object *head;
+ git_reference *head_ref;
+
+ _repo = cl_git_sandbox_init("testrepo");
+ cl_git_pass(git_revparse_ext(&head, &head_ref, _repo, "HEAD"));
+ cl_git_pass(git_reset(_repo, head, GIT_RESET_HARD, NULL));
+ cl_git_pass(git_repository_index(&_index, _repo));
+
+ git_reference_free(head_ref);
+ git_object_free(head);
+}
+
+void test_index_read_index__cleanup(void)
+{
+ git_index_free(_index);
+ cl_git_sandbox_cleanup();
+}
+
+void test_index_read_index__maintains_stat_cache(void)
+{
+ git_index *new_index;
+ git_oid index_id;
+ git_index_entry new_entry;
+ const git_index_entry *e;
+ git_tree *tree;
+ size_t i;
+
+ cl_assert_equal_i(4, git_index_entrycount(_index));
+
+ /* write-tree */
+ cl_git_pass(git_index_write_tree(&index_id, _index));
+
+ /* read-tree, then read index */
+ git_tree_lookup(&tree, _repo, &index_id);
+ cl_git_pass(git_index__new(&new_index, GIT_OID_SHA1));
+ cl_git_pass(git_index_read_tree(new_index, tree));
+ git_tree_free(tree);
+
+ /* add a new entry that will not have stat data */
+ memset(&new_entry, 0, sizeof(git_index_entry));
+ new_entry.path = "Hello";
+ git_oid__fromstr(&new_entry.id, "0123456789012345678901234567890123456789", GIT_OID_SHA1);
+ new_entry.file_size = 1234;
+ new_entry.mode = 0100644;
+ cl_git_pass(git_index_add(new_index, &new_entry));
+ cl_assert_equal_i(5, git_index_entrycount(new_index));
+
+ cl_git_pass(git_index_read_index(_index, new_index));
+ git_index_free(new_index);
+
+ cl_assert_equal_i(5, git_index_entrycount(_index));
+
+ for (i = 0; i < git_index_entrycount(_index); i++) {
+ e = git_index_get_byindex(_index, i);
+
+ if (strcmp(e->path, "Hello") == 0) {
+ cl_assert_equal_i(0, e->ctime.seconds);
+ cl_assert_equal_i(0, e->mtime.seconds);
+ } else {
+ cl_assert(0 != e->ctime.seconds);
+ cl_assert(0 != e->mtime.seconds);
+ }
+ }
+}
+
+static bool roundtrip_with_read_index(const char *tree_idstr)
+{
+ git_oid tree_id, new_tree_id;
+ git_tree *tree;
+ git_index *tree_index;
+
+ cl_git_pass(git_oid__fromstr(&tree_id, tree_idstr, GIT_OID_SHA1));
+ cl_git_pass(git_tree_lookup(&tree, _repo, &tree_id));
+ cl_git_pass(git_index__new(&tree_index, GIT_OID_SHA1));
+ cl_git_pass(git_index_read_tree(tree_index, tree));
+ cl_git_pass(git_index_read_index(_index, tree_index));
+ cl_git_pass(git_index_write_tree(&new_tree_id, _index));
+
+ git_tree_free(tree);
+ git_index_free(tree_index);
+
+ return git_oid_equal(&tree_id, &new_tree_id);
+}
+
+void test_index_read_index__produces_treesame_indexes(void)
+{
+ roundtrip_with_read_index("53fc32d17276939fc79ed05badaef2db09990016");
+ roundtrip_with_read_index("944c0f6e4dfa41595e6eb3ceecdb14f50fe18162");
+ roundtrip_with_read_index("1810dff58d8a660512d4832e740f692884338ccd");
+ roundtrip_with_read_index("d52a8fe84ceedf260afe4f0287bbfca04a117e83");
+ roundtrip_with_read_index("c36d8ea75da8cb510fcb0c408c1d7e53f9a99dbe");
+ roundtrip_with_read_index("7b2417a23b63e1fdde88c80e14b33247c6e5785a");
+ roundtrip_with_read_index("f82a8eb4cb20e88d1030fd10d89286215a715396");
+ roundtrip_with_read_index("fd093bff70906175335656e6ce6ae05783708765");
+ roundtrip_with_read_index("ae90f12eea699729ed24555e40b9fd669da12a12");
+}
+
+void test_index_read_index__read_and_writes(void)
+{
+ git_oid tree_id, new_tree_id;
+ git_tree *tree;
+ git_index *tree_index, *new_index;
+
+ cl_git_pass(git_oid__fromstr(&tree_id, "ae90f12eea699729ed24555e40b9fd669da12a12", GIT_OID_SHA1));
+ cl_git_pass(git_tree_lookup(&tree, _repo, &tree_id));
+ cl_git_pass(git_index__new(&tree_index, GIT_OID_SHA1));
+ cl_git_pass(git_index_read_tree(tree_index, tree));
+ cl_git_pass(git_index_read_index(_index, tree_index));
+ cl_git_pass(git_index_write(_index));
+
+ cl_git_pass(git_index__open(&new_index, git_index_path(_index), GIT_OID_SHA1));
+ cl_git_pass(git_index_write_tree_to(&new_tree_id, new_index, _repo));
+
+ cl_assert_equal_oid(&tree_id, &new_tree_id);
+
+ git_tree_free(tree);
+ git_index_free(tree_index);
+ git_index_free(new_index);
+}
+
+static void add_conflicts(git_index *index, const char *filename)
+{
+ git_index_entry ancestor_entry, our_entry, their_entry;
+ static int conflict_idx = 0;
+ char *ancestor_ids[] =
+ { CONFLICTS_ONE_ANCESTOR_OID, CONFLICTS_TWO_ANCESTOR_OID };
+ char *our_ids[] =
+ { CONFLICTS_ONE_OUR_OID, CONFLICTS_TWO_OUR_OID };
+ char *their_ids[] =
+ { CONFLICTS_ONE_THEIR_OID, CONFLICTS_TWO_THEIR_OID };
+
+ conflict_idx = (conflict_idx + 1) % 2;
+
+ memset(&ancestor_entry, 0x0, sizeof(git_index_entry));
+ memset(&our_entry, 0x0, sizeof(git_index_entry));
+ memset(&their_entry, 0x0, sizeof(git_index_entry));
+
+ ancestor_entry.path = filename;
+ ancestor_entry.mode = 0100644;
+ GIT_INDEX_ENTRY_STAGE_SET(&ancestor_entry, 1);
+ git_oid__fromstr(&ancestor_entry.id, ancestor_ids[conflict_idx], GIT_OID_SHA1);
+
+ our_entry.path = filename;
+ our_entry.mode = 0100644;
+ GIT_INDEX_ENTRY_STAGE_SET(&our_entry, 2);
+ git_oid__fromstr(&our_entry.id, our_ids[conflict_idx], GIT_OID_SHA1);
+
+ their_entry.path = filename;
+ their_entry.mode = 0100644;
+ GIT_INDEX_ENTRY_STAGE_SET(&ancestor_entry, 2);
+ git_oid__fromstr(&their_entry.id, their_ids[conflict_idx], GIT_OID_SHA1);
+
+ cl_git_pass(git_index_conflict_add(index, &ancestor_entry,
+ &our_entry, &their_entry));
+}
+
+void test_index_read_index__handles_conflicts(void)
+{
+ git_oid tree_id;
+ git_tree *tree;
+ git_index *index, *new_index;
+ git_index_conflict_iterator *conflict_iterator;
+ const git_index_entry *ancestor, *ours, *theirs;
+
+ cl_git_pass(git_oid__fromstr(&tree_id, "ae90f12eea699729ed24555e40b9fd669da12a12", GIT_OID_SHA1));
+ cl_git_pass(git_tree_lookup(&tree, _repo, &tree_id));
+ cl_git_pass(git_index__new(&index, GIT_OID_SHA1));
+ cl_git_pass(git_index__new(&new_index, GIT_OID_SHA1));
+ cl_git_pass(git_index_read_tree(index, tree));
+ cl_git_pass(git_index_read_tree(new_index, tree));
+
+ /* put some conflicts in only the old side, these should be removed */
+ add_conflicts(index, "orig_side-1.txt");
+ add_conflicts(index, "orig_side-2.txt");
+
+ /* put some conflicts in both indexes, these should be unchanged */
+ add_conflicts(index, "both_sides-1.txt");
+ add_conflicts(new_index, "both_sides-1.txt");
+ add_conflicts(index, "both_sides-2.txt");
+ add_conflicts(new_index, "both_sides-2.txt");
+
+ /* put some conflicts in the new index, these should be added */
+ add_conflicts(new_index, "new_side-1.txt");
+ add_conflicts(new_index, "new_side-2.txt");
+
+ cl_git_pass(git_index_read_index(index, new_index));
+ cl_git_pass(git_index_conflict_iterator_new(&conflict_iterator, index));
+
+ cl_git_pass(git_index_conflict_next(
+ &ancestor, &ours, &theirs, conflict_iterator));
+ cl_assert_equal_s("both_sides-1.txt", ancestor->path);
+ cl_assert_equal_s("both_sides-1.txt", ours->path);
+ cl_assert_equal_s("both_sides-1.txt", theirs->path);
+
+ cl_git_pass(git_index_conflict_next(
+ &ancestor, &ours, &theirs, conflict_iterator));
+ cl_assert_equal_s("both_sides-2.txt", ancestor->path);
+ cl_assert_equal_s("both_sides-2.txt", ours->path);
+ cl_assert_equal_s("both_sides-2.txt", theirs->path);
+
+ cl_git_pass(git_index_conflict_next(
+ &ancestor, &ours, &theirs, conflict_iterator));
+ cl_assert_equal_s("new_side-1.txt", ancestor->path);
+ cl_assert_equal_s("new_side-1.txt", ours->path);
+ cl_assert_equal_s("new_side-1.txt", theirs->path);
+
+ cl_git_pass(git_index_conflict_next(
+ &ancestor, &ours, &theirs, conflict_iterator));
+ cl_assert_equal_s("new_side-2.txt", ancestor->path);
+ cl_assert_equal_s("new_side-2.txt", ours->path);
+ cl_assert_equal_s("new_side-2.txt", theirs->path);
+
+
+ cl_git_fail_with(GIT_ITEROVER, git_index_conflict_next(
+ &ancestor, &ours, &theirs, conflict_iterator));
+
+ git_index_conflict_iterator_free(conflict_iterator);
+
+ git_tree_free(tree);
+ git_index_free(new_index);
+ git_index_free(index);
+}
diff --git a/tests/libgit2/index/read_tree.c b/tests/libgit2/index/read_tree.c
new file mode 100644
index 0000000..0e18828
--- /dev/null
+++ b/tests/libgit2/index/read_tree.c
@@ -0,0 +1,46 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+
+/* Test that reading and writing a tree is a no-op */
+void test_index_read_tree__read_write_involution(void)
+{
+ git_repository *repo;
+ git_index *index;
+ git_oid tree_oid;
+ git_tree *tree;
+ git_oid expected;
+
+ p_mkdir("read_tree", 0700);
+
+ cl_git_pass(git_repository_init(&repo, "./read_tree", 0));
+ cl_git_pass(git_repository_index(&index, repo));
+
+ cl_assert(git_index_entrycount(index) == 0);
+
+ p_mkdir("./read_tree/abc", 0700);
+
+ /* Sort order: '-' < '/' < '_' */
+ cl_git_mkfile("./read_tree/abc-d", NULL);
+ cl_git_mkfile("./read_tree/abc/d", NULL);
+ cl_git_mkfile("./read_tree/abc_d", NULL);
+
+ cl_git_pass(git_index_add_bypath(index, "abc-d"));
+ cl_git_pass(git_index_add_bypath(index, "abc_d"));
+ cl_git_pass(git_index_add_bypath(index, "abc/d"));
+
+ /* write-tree */
+ cl_git_pass(git_index_write_tree(&expected, index));
+
+ /* read-tree */
+ git_tree_lookup(&tree, repo, &expected);
+ cl_git_pass(git_index_read_tree(index, tree));
+ git_tree_free(tree);
+
+ cl_git_pass(git_index_write_tree(&tree_oid, index));
+ cl_assert_equal_oid(&expected, &tree_oid);
+
+ git_index_free(index);
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("read_tree");
+}
diff --git a/tests/libgit2/index/rename.c b/tests/libgit2/index/rename.c
new file mode 100644
index 0000000..9b132cb
--- /dev/null
+++ b/tests/libgit2/index/rename.c
@@ -0,0 +1,86 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+
+void test_index_rename__single_file(void)
+{
+ git_repository *repo;
+ git_index *index;
+ size_t position;
+ git_oid expected;
+ const git_index_entry *entry;
+
+ p_mkdir("rename", 0700);
+
+ cl_git_pass(git_repository_init(&repo, "./rename", 0));
+ cl_git_pass(git_repository_index(&index, repo));
+
+ cl_assert(git_index_entrycount(index) == 0);
+
+ cl_git_mkfile("./rename/lame.name.txt", "new_file\n");
+
+ /* This should add a new blob to the object database in 'd4/fa8600b4f37d7516bef4816ae2c64dbf029e3a' */
+ cl_git_pass(git_index_add_bypath(index, "lame.name.txt"));
+ cl_assert(git_index_entrycount(index) == 1);
+
+ cl_git_pass(git_oid__fromstr(&expected, "d4fa8600b4f37d7516bef4816ae2c64dbf029e3a", GIT_OID_SHA1));
+
+ cl_assert(!git_index_find(&position, index, "lame.name.txt"));
+
+ entry = git_index_get_byindex(index, position);
+ cl_assert_equal_oid(&expected, &entry->id);
+
+ /* This removes the entry from the index, but not from the object database */
+ cl_git_pass(git_index_remove(index, "lame.name.txt", 0));
+ cl_assert(git_index_entrycount(index) == 0);
+
+ p_rename("./rename/lame.name.txt", "./rename/fancy.name.txt");
+
+ cl_git_pass(git_index_add_bypath(index, "fancy.name.txt"));
+ cl_assert(git_index_entrycount(index) == 1);
+
+ cl_assert(!git_index_find(&position, index, "fancy.name.txt"));
+
+ entry = git_index_get_byindex(index, position);
+ cl_assert_equal_oid(&expected, &entry->id);
+
+ git_index_free(index);
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("rename");
+}
+
+void test_index_rename__casechanging(void)
+{
+ git_repository *repo;
+ git_index *index;
+ const git_index_entry *entry;
+ git_index_entry new = {{0}};
+
+ p_mkdir("rename", 0700);
+
+ cl_git_pass(git_repository_init(&repo, "./rename", 0));
+ cl_git_pass(git_repository_index(&index, repo));
+
+ cl_git_mkfile("./rename/lame.name.txt", "new_file\n");
+
+ cl_git_pass(git_index_add_bypath(index, "lame.name.txt"));
+ cl_assert_equal_i(1, git_index_entrycount(index));
+ cl_assert((entry = git_index_get_bypath(index, "lame.name.txt", 0)));
+
+ memcpy(&new, entry, sizeof(git_index_entry));
+ new.path = "LAME.name.TXT";
+
+ cl_git_pass(git_index_add(index, &new));
+ cl_assert((entry = git_index_get_bypath(index, "LAME.name.TXT", 0)));
+
+ if (cl_repo_get_bool(repo, "core.ignorecase"))
+ cl_assert_equal_i(1, git_index_entrycount(index));
+ else
+ cl_assert_equal_i(2, git_index_entrycount(index));
+
+ git_index_free(index);
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("rename");
+}
+
diff --git a/tests/libgit2/index/reuc.c b/tests/libgit2/index/reuc.c
new file mode 100644
index 0000000..7d8766c
--- /dev/null
+++ b/tests/libgit2/index/reuc.c
@@ -0,0 +1,376 @@
+#include "clar_libgit2.h"
+#include "index.h"
+#include "git2/sys/index.h"
+#include "git2/repository.h"
+#include "../reset/reset_helpers.h"
+
+static git_repository *repo;
+static git_index *repo_index;
+
+#define TEST_REPO_PATH "mergedrepo"
+#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
+
+#define ONE_ANCESTOR_OID "478871385b9cd03908c5383acfd568bef023c6b3"
+#define ONE_OUR_OID "4458b8bc9e72b6c8755ae456f60e9844d0538d8c"
+#define ONE_THEIR_OID "8b72416545c7e761b64cecad4f1686eae4078aa8"
+
+#define TWO_ANCESTOR_OID "9d81f82fccc7dcd7de7a1ffead1815294c2e092c"
+#define TWO_OUR_OID "8f3c06cff9a83757cec40c80bc9bf31a2582bde9"
+#define TWO_THEIR_OID "887b153b165d32409c70163e0f734c090f12f673"
+
+/* Fixture setup and teardown */
+void test_index_reuc__initialize(void)
+{
+ repo = cl_git_sandbox_init("mergedrepo");
+ git_repository_index(&repo_index, repo);
+}
+
+void test_index_reuc__cleanup(void)
+{
+ git_index_free(repo_index);
+ repo_index = NULL;
+
+ cl_git_sandbox_cleanup();
+}
+
+void test_index_reuc__add(void)
+{
+ git_oid ancestor_oid, our_oid, their_oid;
+ const git_index_reuc_entry *reuc;
+
+ git_oid__fromstr(&ancestor_oid, ONE_ANCESTOR_OID, GIT_OID_SHA1);
+ git_oid__fromstr(&our_oid, ONE_OUR_OID, GIT_OID_SHA1);
+ git_oid__fromstr(&their_oid, ONE_THEIR_OID, GIT_OID_SHA1);
+
+ cl_git_pass(git_index_reuc_add(repo_index, "newfile.txt",
+ 0100644, &ancestor_oid,
+ 0100644, &our_oid,
+ 0100644, &their_oid));
+
+ cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "newfile.txt"));
+
+ cl_assert_equal_s("newfile.txt", reuc->path);
+ cl_assert(reuc->mode[0] == 0100644);
+ cl_assert(reuc->mode[1] == 0100644);
+ cl_assert(reuc->mode[2] == 0100644);
+ cl_assert_equal_oid(&reuc->oid[0], &ancestor_oid);
+ cl_assert_equal_oid(&reuc->oid[1], &our_oid);
+ cl_assert_equal_oid(&reuc->oid[2], &their_oid);
+
+ cl_git_pass(git_index_write(repo_index));
+}
+
+void test_index_reuc__add_no_ancestor(void)
+{
+ git_oid ancestor_oid, our_oid, their_oid;
+ const git_index_reuc_entry *reuc;
+
+ memset(&ancestor_oid, 0x0, sizeof(git_oid));
+ git_oid__fromstr(&our_oid, ONE_OUR_OID, GIT_OID_SHA1);
+ git_oid__fromstr(&their_oid, ONE_THEIR_OID, GIT_OID_SHA1);
+
+ cl_git_pass(git_index_reuc_add(repo_index, "newfile.txt",
+ 0, NULL,
+ 0100644, &our_oid,
+ 0100644, &their_oid));
+
+ cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "newfile.txt"));
+
+ cl_assert_equal_s("newfile.txt", reuc->path);
+ cl_assert(reuc->mode[0] == 0);
+ cl_assert(reuc->mode[1] == 0100644);
+ cl_assert(reuc->mode[2] == 0100644);
+ cl_assert_equal_oid(&reuc->oid[0], &ancestor_oid);
+ cl_assert_equal_oid(&reuc->oid[1], &our_oid);
+ cl_assert_equal_oid(&reuc->oid[2], &their_oid);
+
+ cl_git_pass(git_index_write(repo_index));
+}
+
+void test_index_reuc__read_bypath(void)
+{
+ const git_index_reuc_entry *reuc;
+ git_oid oid;
+
+ cl_assert_equal_i(2, git_index_reuc_entrycount(repo_index));
+
+ cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "two.txt"));
+
+ cl_assert_equal_s("two.txt", reuc->path);
+ cl_assert(reuc->mode[0] == 0100644);
+ cl_assert(reuc->mode[1] == 0100644);
+ cl_assert(reuc->mode[2] == 0100644);
+ git_oid__fromstr(&oid, TWO_ANCESTOR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&reuc->oid[0], &oid);
+ git_oid__fromstr(&oid, TWO_OUR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&reuc->oid[1], &oid);
+ git_oid__fromstr(&oid, TWO_THEIR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&reuc->oid[2], &oid);
+
+ cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "one.txt"));
+
+ cl_assert_equal_s("one.txt", reuc->path);
+ cl_assert(reuc->mode[0] == 0100644);
+ cl_assert(reuc->mode[1] == 0100644);
+ cl_assert(reuc->mode[2] == 0100644);
+ git_oid__fromstr(&oid, ONE_ANCESTOR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&reuc->oid[0], &oid);
+ git_oid__fromstr(&oid, ONE_OUR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&reuc->oid[1], &oid);
+ git_oid__fromstr(&oid, ONE_THEIR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&reuc->oid[2], &oid);
+}
+
+void test_index_reuc__ignore_case(void)
+{
+ const git_index_reuc_entry *reuc;
+ git_oid oid;
+ int index_caps;
+
+ index_caps = git_index_caps(repo_index);
+
+ index_caps &= ~GIT_INDEX_CAPABILITY_IGNORE_CASE;
+ cl_git_pass(git_index_set_caps(repo_index, index_caps));
+
+ cl_assert(!git_index_reuc_get_bypath(repo_index, "TWO.txt"));
+
+ index_caps |= GIT_INDEX_CAPABILITY_IGNORE_CASE;
+ cl_git_pass(git_index_set_caps(repo_index, index_caps));
+
+ cl_assert_equal_i(2, git_index_reuc_entrycount(repo_index));
+
+ cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "TWO.txt"));
+
+ cl_assert_equal_s("two.txt", reuc->path);
+ cl_assert(reuc->mode[0] == 0100644);
+ cl_assert(reuc->mode[1] == 0100644);
+ cl_assert(reuc->mode[2] == 0100644);
+ git_oid__fromstr(&oid, TWO_ANCESTOR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&reuc->oid[0], &oid);
+ git_oid__fromstr(&oid, TWO_OUR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&reuc->oid[1], &oid);
+ git_oid__fromstr(&oid, TWO_THEIR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&reuc->oid[2], &oid);
+}
+
+void test_index_reuc__read_byindex(void)
+{
+ const git_index_reuc_entry *reuc;
+ git_oid oid;
+
+ cl_assert_equal_i(2, git_index_reuc_entrycount(repo_index));
+
+ cl_assert(reuc = git_index_reuc_get_byindex(repo_index, 0));
+
+ cl_assert_equal_s("one.txt", reuc->path);
+ cl_assert(reuc->mode[0] == 0100644);
+ cl_assert(reuc->mode[1] == 0100644);
+ cl_assert(reuc->mode[2] == 0100644);
+ git_oid__fromstr(&oid, ONE_ANCESTOR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&reuc->oid[0], &oid);
+ git_oid__fromstr(&oid, ONE_OUR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&reuc->oid[1], &oid);
+ git_oid__fromstr(&oid, ONE_THEIR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&reuc->oid[2], &oid);
+
+ cl_assert(reuc = git_index_reuc_get_byindex(repo_index, 1));
+
+ cl_assert_equal_s("two.txt", reuc->path);
+ cl_assert(reuc->mode[0] == 0100644);
+ cl_assert(reuc->mode[1] == 0100644);
+ cl_assert(reuc->mode[2] == 0100644);
+ git_oid__fromstr(&oid, TWO_ANCESTOR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&reuc->oid[0], &oid);
+ git_oid__fromstr(&oid, TWO_OUR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&reuc->oid[1], &oid);
+ git_oid__fromstr(&oid, TWO_THEIR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&reuc->oid[2], &oid);
+}
+
+void test_index_reuc__updates_existing(void)
+{
+ const git_index_reuc_entry *reuc;
+ git_oid ancestor_oid, our_oid, their_oid, oid;
+ int index_caps;
+
+ git_index_clear(repo_index);
+
+ index_caps = git_index_caps(repo_index);
+
+ index_caps |= GIT_INDEX_CAPABILITY_IGNORE_CASE;
+ cl_git_pass(git_index_set_caps(repo_index, index_caps));
+
+ git_oid__fromstr(&ancestor_oid, TWO_ANCESTOR_OID, GIT_OID_SHA1);
+ git_oid__fromstr(&our_oid, TWO_OUR_OID, GIT_OID_SHA1);
+ git_oid__fromstr(&their_oid, TWO_THEIR_OID, GIT_OID_SHA1);
+
+ cl_git_pass(git_index_reuc_add(repo_index, "two.txt",
+ 0100644, &ancestor_oid,
+ 0100644, &our_oid,
+ 0100644, &their_oid));
+
+ cl_git_pass(git_index_reuc_add(repo_index, "TWO.txt",
+ 0100644, &our_oid,
+ 0100644, &their_oid,
+ 0100644, &ancestor_oid));
+
+ cl_assert_equal_i(1, git_index_reuc_entrycount(repo_index));
+
+ cl_assert(reuc = git_index_reuc_get_byindex(repo_index, 0));
+
+ cl_assert_equal_s("TWO.txt", reuc->path);
+ git_oid__fromstr(&oid, TWO_OUR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&reuc->oid[0], &oid);
+ git_oid__fromstr(&oid, TWO_THEIR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&reuc->oid[1], &oid);
+ git_oid__fromstr(&oid, TWO_ANCESTOR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&reuc->oid[2], &oid);
+}
+
+void test_index_reuc__remove(void)
+{
+ git_oid oid;
+ const git_index_reuc_entry *reuc;
+
+ cl_assert_equal_i(2, git_index_reuc_entrycount(repo_index));
+
+ cl_git_pass(git_index_reuc_remove(repo_index, 0));
+ cl_git_fail(git_index_reuc_remove(repo_index, 1));
+
+ cl_assert_equal_i(1, git_index_reuc_entrycount(repo_index));
+
+ cl_assert(reuc = git_index_reuc_get_byindex(repo_index, 0));
+
+ cl_assert_equal_s("two.txt", reuc->path);
+ cl_assert(reuc->mode[0] == 0100644);
+ cl_assert(reuc->mode[1] == 0100644);
+ cl_assert(reuc->mode[2] == 0100644);
+ git_oid__fromstr(&oid, TWO_ANCESTOR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&reuc->oid[0], &oid);
+ git_oid__fromstr(&oid, TWO_OUR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&reuc->oid[1], &oid);
+ git_oid__fromstr(&oid, TWO_THEIR_OID, GIT_OID_SHA1);
+ cl_assert_equal_oid(&reuc->oid[2], &oid);
+}
+
+void test_index_reuc__write(void)
+{
+ git_oid ancestor_oid, our_oid, their_oid;
+ const git_index_reuc_entry *reuc;
+
+ git_index_clear(repo_index);
+
+ /* Write out of order to ensure sorting is correct */
+ git_oid__fromstr(&ancestor_oid, TWO_ANCESTOR_OID, GIT_OID_SHA1);
+ git_oid__fromstr(&our_oid, TWO_OUR_OID, GIT_OID_SHA1);
+ git_oid__fromstr(&their_oid, TWO_THEIR_OID, GIT_OID_SHA1);
+
+ cl_git_pass(git_index_reuc_add(repo_index, "two.txt",
+ 0100644, &ancestor_oid,
+ 0100644, &our_oid,
+ 0100644, &their_oid));
+
+ git_oid__fromstr(&ancestor_oid, ONE_ANCESTOR_OID, GIT_OID_SHA1);
+ git_oid__fromstr(&our_oid, ONE_OUR_OID, GIT_OID_SHA1);
+ git_oid__fromstr(&their_oid, ONE_THEIR_OID, GIT_OID_SHA1);
+
+ cl_git_pass(git_index_reuc_add(repo_index, "one.txt",
+ 0100644, &ancestor_oid,
+ 0100644, &our_oid,
+ 0100644, &their_oid));
+
+ cl_git_pass(git_index_write(repo_index));
+ cl_assert_equal_i(2, git_index_reuc_entrycount(repo_index));
+
+ /* ensure sort order was round-tripped correct */
+ cl_assert(reuc = git_index_reuc_get_byindex(repo_index, 0));
+ cl_assert_equal_s("one.txt", reuc->path);
+
+ cl_assert(reuc = git_index_reuc_get_byindex(repo_index, 1));
+ cl_assert_equal_s("two.txt", reuc->path);
+}
+
+static int reuc_entry_exists(void)
+{
+ return (git_index_reuc_get_bypath(repo_index, "newfile.txt") != NULL);
+}
+
+void test_index_reuc__cleaned_on_reset_hard(void)
+{
+ git_object *target;
+
+ cl_git_pass(git_revparse_single(&target, repo, "3a34580"));
+
+ test_index_reuc__add();
+ cl_git_pass(git_reset(repo, target, GIT_RESET_HARD, NULL));
+ cl_assert(reuc_entry_exists() == false);
+
+ git_object_free(target);
+}
+
+void test_index_reuc__cleaned_on_reset_mixed(void)
+{
+ git_object *target;
+
+ cl_git_pass(git_revparse_single(&target, repo, "3a34580"));
+
+ test_index_reuc__add();
+ cl_git_pass(git_reset(repo, target, GIT_RESET_MIXED, NULL));
+ cl_assert(reuc_entry_exists() == false);
+
+ git_object_free(target);
+}
+
+void test_index_reuc__retained_on_reset_soft(void)
+{
+ git_object *target;
+
+ cl_git_pass(git_revparse_single(&target, repo, "3a34580"));
+
+ cl_git_pass(git_reset(repo, target, GIT_RESET_HARD, NULL));
+
+ test_index_reuc__add();
+ cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT, NULL));
+ cl_assert(reuc_entry_exists() == true);
+
+ git_object_free(target);
+}
+
+void test_index_reuc__cleaned_on_checkout_tree(void)
+{
+ git_oid oid;
+ git_object *obj;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ test_index_reuc__add();
+ cl_git_pass(git_reference_name_to_id(&oid, repo, "refs/heads/master"));
+ cl_git_pass(git_object_lookup(&obj, repo, &oid, GIT_OBJECT_ANY));
+ cl_git_pass(git_checkout_tree(repo, obj, &opts));
+ cl_assert(reuc_entry_exists() == false);
+
+ git_object_free(obj);
+}
+
+void test_index_reuc__cleaned_on_checkout_head(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ test_index_reuc__add();
+ cl_git_pass(git_checkout_head(repo, &opts));
+ cl_assert(reuc_entry_exists() == false);
+}
+
+void test_index_reuc__retained_on_checkout_index(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ test_index_reuc__add();
+ cl_git_pass(git_checkout_index(repo, repo_index, &opts));
+ cl_assert(reuc_entry_exists() == true);
+}
diff --git a/tests/libgit2/index/splitindex.c b/tests/libgit2/index/splitindex.c
new file mode 100644
index 0000000..d32ed10
--- /dev/null
+++ b/tests/libgit2/index/splitindex.c
@@ -0,0 +1,21 @@
+#include "clar_libgit2.h"
+#include "index.h"
+
+static git_repository *g_repo;
+
+void test_index_splitindex__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("splitindex");
+}
+
+void test_index_splitindex__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_index_splitindex__fail_on_open(void)
+{
+ git_index *idx;
+ cl_git_fail_with(-1, git_repository_index(&idx, g_repo));
+ cl_assert_equal_s(git_error_last()->message, "unsupported mandatory extension: 'link'");
+}
diff --git a/tests/libgit2/index/stage.c b/tests/libgit2/index/stage.c
new file mode 100644
index 0000000..cdfe3a8
--- /dev/null
+++ b/tests/libgit2/index/stage.c
@@ -0,0 +1,62 @@
+#include "clar_libgit2.h"
+#include "index.h"
+#include "git2/repository.h"
+
+static git_repository *repo;
+static git_index *repo_index;
+
+#define TEST_REPO_PATH "mergedrepo"
+#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
+
+/* Fixture setup and teardown */
+void test_index_stage__initialize(void)
+{
+ repo = cl_git_sandbox_init("mergedrepo");
+ git_repository_index(&repo_index, repo);
+}
+
+void test_index_stage__cleanup(void)
+{
+ git_index_free(repo_index);
+ repo_index = NULL;
+
+ cl_git_sandbox_cleanup();
+}
+
+
+void test_index_stage__add_always_adds_stage_0(void)
+{
+ size_t entry_idx;
+ const git_index_entry *entry;
+
+ cl_git_mkfile("./mergedrepo/new-file.txt", "new-file\n");
+
+ cl_git_pass(git_index_add_bypath(repo_index, "new-file.txt"));
+
+ cl_assert(!git_index_find(&entry_idx, repo_index, "new-file.txt"));
+ cl_assert((entry = git_index_get_byindex(repo_index, entry_idx)) != NULL);
+ cl_assert(git_index_entry_stage(entry) == 0);
+}
+
+void test_index_stage__find_gets_first_stage(void)
+{
+ size_t entry_idx;
+ const git_index_entry *entry;
+
+ cl_assert(!git_index_find(&entry_idx, repo_index, "one.txt"));
+ cl_assert((entry = git_index_get_byindex(repo_index, entry_idx)) != NULL);
+ cl_assert(git_index_entry_stage(entry) == 0);
+
+ cl_assert(!git_index_find(&entry_idx, repo_index, "two.txt"));
+ cl_assert((entry = git_index_get_byindex(repo_index, entry_idx)) != NULL);
+ cl_assert(git_index_entry_stage(entry) == 0);
+
+ cl_assert(!git_index_find(&entry_idx, repo_index, "conflicts-one.txt"));
+ cl_assert((entry = git_index_get_byindex(repo_index, entry_idx)) != NULL);
+ cl_assert(git_index_entry_stage(entry) == 1);
+
+ cl_assert(!git_index_find(&entry_idx, repo_index, "conflicts-two.txt"));
+ cl_assert((entry = git_index_get_byindex(repo_index, entry_idx)) != NULL);
+ cl_assert(git_index_entry_stage(entry) == 1);
+}
+
diff --git a/tests/libgit2/index/tests.c b/tests/libgit2/index/tests.c
new file mode 100644
index 0000000..b48eb0f
--- /dev/null
+++ b/tests/libgit2/index/tests.c
@@ -0,0 +1,1173 @@
+#include "clar_libgit2.h"
+#include "index.h"
+
+static const size_t index_entry_count = 109;
+static const size_t index_entry_count_2 = 1437;
+#define TEST_INDEX_PATH cl_fixture("testrepo.git/index")
+#define TEST_INDEX2_PATH cl_fixture("gitgit.index")
+#define TEST_INDEXBIG_PATH cl_fixture("big.index")
+#define TEST_INDEXBAD_PATH cl_fixture("bad.index")
+
+
+/* Suite data */
+struct test_entry {
+ size_t index;
+ char path[128];
+ off64_t file_size;
+ git_time_t mtime;
+};
+
+static struct test_entry test_entries[] = {
+ {4, "Makefile", 5064, 0x4C3F7F33},
+ {6, "git.git-authors", 2709, 0x4C3F7F33},
+ {36, "src/index.c", 10014, 0x4C43368D},
+ {48, "src/revobject.h", 1448, 0x4C3F7FE2},
+ {62, "tests/Makefile", 2631, 0x4C3F7F33}
+};
+
+/* Helpers */
+static void copy_file(const char *src, const char *dst)
+{
+ git_str source_buf = GIT_STR_INIT;
+ git_file dst_fd;
+
+ cl_git_pass(git_futils_readbuffer(&source_buf, src));
+
+ dst_fd = git_futils_creat_withpath(dst, 0777, 0666); /* -V536 */
+ if (dst_fd < 0)
+ goto cleanup;
+
+ cl_git_pass(p_write(dst_fd, source_buf.ptr, source_buf.size));
+
+cleanup:
+ git_str_dispose(&source_buf);
+ p_close(dst_fd);
+}
+
+static void files_are_equal(const char *a, const char *b)
+{
+ git_str buf_a = GIT_STR_INIT;
+ git_str buf_b = GIT_STR_INIT;
+ int pass;
+
+ if (git_futils_readbuffer(&buf_a, a) < 0)
+ cl_assert(0);
+
+ if (git_futils_readbuffer(&buf_b, b) < 0) {
+ git_str_dispose(&buf_a);
+ cl_assert(0);
+ }
+
+ pass = (buf_a.size == buf_b.size && !memcmp(buf_a.ptr, buf_b.ptr, buf_a.size));
+
+ git_str_dispose(&buf_a);
+ git_str_dispose(&buf_b);
+
+ cl_assert(pass);
+}
+
+
+/* Fixture setup and teardown */
+void test_index_tests__initialize(void)
+{
+}
+
+void test_index_tests__cleanup(void)
+{
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_UNSAVED_INDEX_SAFETY, 0));
+}
+
+void test_index_tests__empty_index(void)
+{
+ git_index *index;
+
+ cl_git_pass(git_index__open(&index, "in-memory-index", GIT_OID_SHA1));
+ cl_assert(index->on_disk == 0);
+
+ cl_assert(git_index_entrycount(index) == 0);
+ cl_assert(git_vector_is_sorted(&index->entries));
+
+ git_index_free(index);
+}
+
+void test_index_tests__default_test_index(void)
+{
+ git_index *index;
+ unsigned int i;
+ git_index_entry **entries;
+
+ cl_git_pass(git_index__open(&index, TEST_INDEX_PATH, GIT_OID_SHA1));
+ cl_assert(index->on_disk);
+
+ cl_assert(git_index_entrycount(index) == index_entry_count);
+ cl_assert(git_vector_is_sorted(&index->entries));
+
+ entries = (git_index_entry **)index->entries.contents;
+
+ for (i = 0; i < ARRAY_SIZE(test_entries); ++i) {
+ git_index_entry *e = entries[test_entries[i].index];
+
+ cl_assert_equal_s(e->path, test_entries[i].path);
+ cl_assert_equal_i(e->mtime.seconds, test_entries[i].mtime);
+ cl_assert_equal_i(e->file_size, test_entries[i].file_size);
+ }
+
+ git_index_free(index);
+}
+
+void test_index_tests__gitgit_index(void)
+{
+ git_index *index;
+
+ cl_git_pass(git_index__open(&index, TEST_INDEX2_PATH, GIT_OID_SHA1));
+ cl_assert(index->on_disk);
+
+ cl_assert(git_index_entrycount(index) == index_entry_count_2);
+ cl_assert(git_vector_is_sorted(&index->entries));
+ cl_assert(index->tree != NULL);
+
+ git_index_free(index);
+}
+
+void test_index_tests__find_in_existing(void)
+{
+ git_index *index;
+ unsigned int i;
+
+ cl_git_pass(git_index__open(&index, TEST_INDEX_PATH, GIT_OID_SHA1));
+
+ for (i = 0; i < ARRAY_SIZE(test_entries); ++i) {
+ size_t idx;
+
+ cl_assert(!git_index_find(&idx, index, test_entries[i].path));
+ cl_assert(idx == test_entries[i].index);
+ }
+
+ git_index_free(index);
+}
+
+void test_index_tests__find_in_empty(void)
+{
+ git_index *index;
+ unsigned int i;
+
+ cl_git_pass(git_index__open(&index, "fake-index", GIT_OID_SHA1));
+
+ for (i = 0; i < ARRAY_SIZE(test_entries); ++i) {
+ cl_assert(GIT_ENOTFOUND == git_index_find(NULL, index, test_entries[i].path));
+ }
+
+ git_index_free(index);
+}
+
+void test_index_tests__find_prefix(void)
+{
+ git_index *index;
+ const git_index_entry *entry;
+ size_t pos;
+
+ cl_git_pass(git_index__open(&index, TEST_INDEX_PATH, GIT_OID_SHA1));
+
+ cl_git_pass(git_index_find_prefix(&pos, index, "src"));
+ entry = git_index_get_byindex(index, pos);
+ cl_assert(git__strcmp(entry->path, "src/block-sha1/sha1.c") == 0);
+
+ cl_git_pass(git_index_find_prefix(&pos, index, "src/co"));
+ entry = git_index_get_byindex(index, pos);
+ cl_assert(git__strcmp(entry->path, "src/commit.c") == 0);
+
+ cl_assert(GIT_ENOTFOUND == git_index_find_prefix(NULL, index, "blah"));
+
+ git_index_free(index);
+}
+
+void test_index_tests__write(void)
+{
+ git_index *index;
+
+ copy_file(TEST_INDEXBIG_PATH, "index_rewrite");
+
+ cl_git_pass(git_index__open(&index, "index_rewrite", GIT_OID_SHA1));
+ cl_assert(index->on_disk);
+
+ cl_git_pass(git_index_write(index));
+ files_are_equal(TEST_INDEXBIG_PATH, "index_rewrite");
+
+ git_index_free(index);
+
+ p_unlink("index_rewrite");
+}
+
+void test_index_tests__sort0(void)
+{
+ /* sort the entries in an index */
+
+ /*
+ * TODO: This no longer applies:
+ * index sorting in Git uses some specific changes to the way
+ * directories are sorted.
+ *
+ * We need to specifically check for this by creating a new
+ * index, adding entries in random order and then
+ * checking for consistency
+ */
+}
+
+void test_index_tests__sort1(void)
+{
+ /* sort the entries in an empty index */
+ git_index *index;
+
+ cl_git_pass(git_index__open(&index, "fake-index", GIT_OID_SHA1));
+
+ /* FIXME: this test is slightly dumb */
+ cl_assert(git_vector_is_sorted(&index->entries));
+
+ git_index_free(index);
+}
+
+static void cleanup_myrepo(void *opaque)
+{
+ GIT_UNUSED(opaque);
+ cl_fixture_cleanup("myrepo");
+}
+
+void test_index_tests__add(void)
+{
+ git_index *index;
+ git_filebuf file = GIT_FILEBUF_INIT;
+ git_repository *repo;
+ const git_index_entry *entry;
+ git_oid id1;
+
+ cl_set_cleanup(&cleanup_myrepo, NULL);
+
+ /* Initialize a new repository */
+ cl_git_pass(git_repository_init(&repo, "./myrepo", 0));
+
+ /* Ensure we're the only guy in the room */
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert(git_index_entrycount(index) == 0);
+
+ /* Create a new file in the working directory */
+ cl_git_pass(git_futils_mkpath2file("myrepo/test.txt", 0777));
+ cl_git_pass(git_filebuf_open(&file, "myrepo/test.txt", 0, 0666));
+ cl_git_pass(git_filebuf_write(&file, "hey there\n", 10));
+ cl_git_pass(git_filebuf_commit(&file));
+
+ /* Store the expected hash of the file/blob
+ * This has been generated by executing the following
+ * $ echo "hey there" | git hash-object --stdin
+ */
+ cl_git_pass(git_oid__fromstr(&id1, "a8233120f6ad708f843d861ce2b7228ec4e3dec6", GIT_OID_SHA1));
+
+ /* Add the new file to the index */
+ cl_git_pass(git_index_add_bypath(index, "test.txt"));
+
+ /* Wow... it worked! */
+ cl_assert(git_index_entrycount(index) == 1);
+ entry = git_index_get_byindex(index, 0);
+
+ /* And the built-in hashing mechanism worked as expected */
+ cl_assert_equal_oid(&id1, &entry->id);
+
+ /* Test access by path instead of index */
+ cl_assert((entry = git_index_get_bypath(index, "test.txt", 0)) != NULL);
+ cl_assert_equal_oid(&id1, &entry->id);
+
+ git_index_free(index);
+ git_repository_free(repo);
+}
+
+void test_index_tests__add_frombuffer(void)
+{
+ git_index *index;
+ git_repository *repo;
+ git_index_entry entry;
+ const git_index_entry *returned_entry;
+
+ git_oid id1;
+ git_blob *blob;
+
+ const char *content = "hey there\n";
+
+ cl_set_cleanup(&cleanup_myrepo, NULL);
+
+ /* Initialize a new repository */
+ cl_git_pass(git_repository_init(&repo, "./myrepo", 0));
+
+ /* Ensure we're the only guy in the room */
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert(git_index_entrycount(index) == 0);
+
+ /* Store the expected hash of the file/blob
+ * This has been generated by executing the following
+ * $ echo "hey there" | git hash-object --stdin
+ */
+ cl_git_pass(git_oid__fromstr(&id1, "a8233120f6ad708f843d861ce2b7228ec4e3dec6", GIT_OID_SHA1));
+
+ /* Add the new file to the index */
+ memset(&entry, 0x0, sizeof(git_index_entry));
+ entry.mode = GIT_FILEMODE_BLOB;
+ entry.path = "test.txt";
+ cl_git_pass(git_index_add_from_buffer(index, &entry,
+ content, strlen(content)));
+
+ /* Wow... it worked! */
+ cl_assert(git_index_entrycount(index) == 1);
+ returned_entry = git_index_get_byindex(index, 0);
+
+ /* And the built-in hashing mechanism worked as expected */
+ cl_assert_equal_oid(&id1, &returned_entry->id);
+ /* And mode is the one asked */
+ cl_assert_equal_i(GIT_FILEMODE_BLOB, returned_entry->mode);
+
+ /* Test access by path instead of index */
+ cl_assert((returned_entry = git_index_get_bypath(index, "test.txt", 0)) != NULL);
+ cl_assert_equal_oid(&id1, &returned_entry->id);
+
+ /* Test the blob is in the repository */
+ cl_git_pass(git_blob_lookup(&blob, repo, &id1));
+ cl_assert_equal_s(
+ content, git_blob_rawcontent(blob));
+ git_blob_free(blob);
+
+ git_index_free(index);
+ git_repository_free(repo);
+}
+
+void test_index_tests__dirty_and_clean(void)
+{
+ git_repository *repo;
+ git_index *index;
+ git_index_entry entry = {{0}};
+
+ /* Index is not dirty after opening */
+ cl_git_pass(git_repository_init(&repo, "./myrepo", 0));
+ cl_git_pass(git_repository_index(&index, repo));
+
+ cl_assert(git_index_entrycount(index) == 0);
+ cl_assert(!git_index_is_dirty(index));
+
+ /* Index is dirty after adding an entry */
+ entry.mode = GIT_FILEMODE_BLOB;
+ entry.path = "test.txt";
+ cl_git_pass(git_index_add_from_buffer(index, &entry, "Hi.\n", 4));
+ cl_assert(git_index_entrycount(index) == 1);
+ cl_assert(git_index_is_dirty(index));
+
+ /* Index is not dirty after write */
+ cl_git_pass(git_index_write(index));
+ cl_assert(!git_index_is_dirty(index));
+
+ /* Index is dirty after removing an entry */
+ cl_git_pass(git_index_remove_bypath(index, "test.txt"));
+ cl_assert(git_index_entrycount(index) == 0);
+ cl_assert(git_index_is_dirty(index));
+
+ /* Index is not dirty after write */
+ cl_git_pass(git_index_write(index));
+ cl_assert(!git_index_is_dirty(index));
+
+ /* Index remains not dirty after read */
+ cl_git_pass(git_index_read(index, 0));
+ cl_assert(!git_index_is_dirty(index));
+
+ /* Index is dirty when we do an unforced read with dirty content */
+ cl_git_pass(git_index_add_from_buffer(index, &entry, "Hi.\n", 4));
+ cl_assert(git_index_entrycount(index) == 1);
+ cl_assert(git_index_is_dirty(index));
+
+ cl_git_pass(git_index_read(index, 0));
+ cl_assert(git_index_is_dirty(index));
+
+ /* Index is clean when we force a read with dirty content */
+ cl_git_pass(git_index_read(index, 1));
+ cl_assert(!git_index_is_dirty(index));
+
+ git_index_free(index);
+ git_repository_free(repo);
+}
+
+void test_index_tests__dirty_fails_optionally(void)
+{
+ git_repository *repo;
+ git_index *index;
+ git_index_entry entry = {{0}};
+
+ /* Index is not dirty after opening */
+ repo = cl_git_sandbox_init("testrepo");
+ cl_git_pass(git_repository_index(&index, repo));
+
+ /* Index is dirty after adding an entry */
+ entry.mode = GIT_FILEMODE_BLOB;
+ entry.path = "test.txt";
+ cl_git_pass(git_index_add_from_buffer(index, &entry, "Hi.\n", 4));
+ cl_assert(git_index_is_dirty(index));
+
+ cl_git_pass(git_checkout_head(repo, NULL));
+
+ /* Index is dirty (again) after adding an entry */
+ entry.mode = GIT_FILEMODE_BLOB;
+ entry.path = "test.txt";
+ cl_git_pass(git_index_add_from_buffer(index, &entry, "Hi.\n", 4));
+ cl_assert(git_index_is_dirty(index));
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_UNSAVED_INDEX_SAFETY, 1));
+ cl_git_fail_with(GIT_EINDEXDIRTY, git_checkout_head(repo, NULL));
+
+ git_index_free(index);
+ cl_git_sandbox_cleanup();
+}
+
+void test_index_tests__add_frombuffer_reset_entry(void)
+{
+ git_index *index;
+ git_repository *repo;
+ git_index_entry entry;
+ const git_index_entry *returned_entry;
+ git_filebuf file = GIT_FILEBUF_INIT;
+
+ git_oid id1;
+ git_blob *blob;
+ const char *old_content = "here\n";
+ const char *content = "hey there\n";
+
+ cl_set_cleanup(&cleanup_myrepo, NULL);
+
+ /* Initialize a new repository */
+ cl_git_pass(git_repository_init(&repo, "./myrepo", 0));
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_futils_mkpath2file("myrepo/test.txt", 0777));
+ cl_git_pass(git_filebuf_open(&file, "myrepo/test.txt", 0, 0666));
+ cl_git_pass(git_filebuf_write(&file, old_content, strlen(old_content)));
+ cl_git_pass(git_filebuf_commit(&file));
+
+ /* Store the expected hash of the file/blob
+ * This has been generated by executing the following
+ * $ echo "hey there" | git hash-object --stdin
+ */
+ cl_git_pass(git_oid__fromstr(&id1, "a8233120f6ad708f843d861ce2b7228ec4e3dec6", GIT_OID_SHA1));
+
+ cl_git_pass(git_index_add_bypath(index, "test.txt"));
+
+ /* Add the new file to the index */
+ memset(&entry, 0x0, sizeof(git_index_entry));
+ entry.mode = GIT_FILEMODE_BLOB;
+ entry.path = "test.txt";
+ cl_git_pass(git_index_add_from_buffer(index, &entry,
+ content, strlen(content)));
+
+ /* Wow... it worked! */
+ cl_assert(git_index_entrycount(index) == 1);
+ returned_entry = git_index_get_byindex(index, 0);
+
+ /* And the built-in hashing mechanism worked as expected */
+ cl_assert_equal_oid(&id1, &returned_entry->id);
+ /* And mode is the one asked */
+ cl_assert_equal_i(GIT_FILEMODE_BLOB, returned_entry->mode);
+
+ /* Test access by path instead of index */
+ cl_assert((returned_entry = git_index_get_bypath(index, "test.txt", 0)) != NULL);
+ cl_assert_equal_oid(&id1, &returned_entry->id);
+ cl_assert_equal_i(0, returned_entry->dev);
+ cl_assert_equal_i(0, returned_entry->ino);
+ cl_assert_equal_i(0, returned_entry->uid);
+ cl_assert_equal_i(0, returned_entry->uid);
+ cl_assert_equal_i(10, returned_entry->file_size);
+
+ /* Test the blob is in the repository */
+ cl_git_pass(git_blob_lookup(&blob, repo, &id1));
+ cl_assert_equal_s(content, git_blob_rawcontent(blob));
+ git_blob_free(blob);
+
+ git_index_free(index);
+ git_repository_free(repo);
+}
+
+static void cleanup_1397(void *opaque)
+{
+ GIT_UNUSED(opaque);
+ cl_git_sandbox_cleanup();
+}
+
+void test_index_tests__add_issue_1397(void)
+{
+ git_index *index;
+ git_repository *repo;
+ const git_index_entry *entry;
+ git_oid id1;
+
+ cl_set_cleanup(&cleanup_1397, NULL);
+
+ repo = cl_git_sandbox_init("issue_1397");
+
+ cl_repo_set_bool(repo, "core.autocrlf", true);
+
+ /* Ensure we're the only guy in the room */
+ cl_git_pass(git_repository_index(&index, repo));
+
+ /* Store the expected hash of the file/blob
+ * This has been generated by executing the following
+ * $ git hash-object crlf_file.txt
+ */
+ cl_git_pass(git_oid__fromstr(&id1, "8312e0889a9cbab77c732b6bc39b51a683e3a318", GIT_OID_SHA1));
+
+ /* Make sure the initial SHA-1 is correct */
+ cl_assert((entry = git_index_get_bypath(index, "crlf_file.txt", 0)) != NULL);
+ cl_assert_equal_oid(&id1, &entry->id);
+
+ /* Update the index */
+ cl_git_pass(git_index_add_bypath(index, "crlf_file.txt"));
+
+ /* Check the new SHA-1 */
+ cl_assert((entry = git_index_get_bypath(index, "crlf_file.txt", 0)) != NULL);
+ cl_assert_equal_oid(&id1, &entry->id);
+
+ git_index_free(index);
+}
+
+void test_index_tests__add_bypath_to_a_bare_repository_returns_EBAREPO(void)
+{
+ git_repository *bare_repo;
+ git_index *index;
+
+ cl_git_pass(git_repository_open(&bare_repo, cl_fixture("testrepo.git")));
+ cl_git_pass(git_repository_index(&index, bare_repo));
+
+ cl_assert_equal_i(GIT_EBAREREPO, git_index_add_bypath(index, "test.txt"));
+
+ git_index_free(index);
+ git_repository_free(bare_repo);
+}
+
+static void assert_add_bypath_fails(git_repository *repo, const char *fn)
+{
+ git_index *index;
+ git_str path = GIT_STR_INIT;
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert(git_index_entrycount(index) == 0);
+
+ git_str_joinpath(&path, "./invalid", fn);
+
+ cl_git_mkfile(path.ptr, NULL);
+ cl_git_fail(git_index_add_bypath(index, fn));
+ cl_must_pass(p_unlink(path.ptr));
+
+ cl_assert(git_index_entrycount(index) == 0);
+
+ git_str_dispose(&path);
+ git_index_free(index);
+}
+
+/* Test that writing an invalid filename fails */
+void test_index_tests__cannot_add_invalid_filename(void)
+{
+ git_repository *repo;
+
+ cl_must_pass(p_mkdir("invalid", 0700));
+ cl_git_pass(git_repository_init(&repo, "./invalid", 0));
+ cl_must_pass(p_mkdir("./invalid/subdir", 0777));
+
+ /* cl_git_mkfile() needs the dir to exist */
+ if (!git_fs_path_exists("./invalid/.GIT"))
+ cl_must_pass(p_mkdir("./invalid/.GIT", 0777));
+ if (!git_fs_path_exists("./invalid/.GiT"))
+ cl_must_pass(p_mkdir("./invalid/.GiT", 0777));
+
+ assert_add_bypath_fails(repo, ".git/hello");
+ assert_add_bypath_fails(repo, ".GIT/hello");
+ assert_add_bypath_fails(repo, ".GiT/hello");
+ assert_add_bypath_fails(repo, "./.git/hello");
+ assert_add_bypath_fails(repo, "./foo");
+ assert_add_bypath_fails(repo, "./bar");
+ assert_add_bypath_fails(repo, "subdir/../bar");
+
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("invalid");
+}
+
+static void assert_add_fails(git_repository *repo, const char *fn)
+{
+ git_index *index;
+ git_str path = GIT_STR_INIT;
+ git_index_entry entry = {{0}};
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert(git_index_entrycount(index) == 0);
+
+ entry.path = fn;
+ entry.mode = GIT_FILEMODE_BLOB;
+ cl_git_pass(git_oid__fromstr(&entry.id, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", GIT_OID_SHA1));
+
+ cl_git_fail(git_index_add(index, &entry));
+
+ cl_assert(git_index_entrycount(index) == 0);
+
+ git_str_dispose(&path);
+ git_index_free(index);
+}
+
+/*
+ * Test that writing an invalid filename fails on filesystem
+ * specific protected names
+ */
+void test_index_tests__cannot_add_protected_invalid_filename(void)
+{
+ git_repository *repo;
+ git_index *index;
+
+ cl_must_pass(p_mkdir("invalid", 0700));
+
+ cl_git_pass(git_repository_init(&repo, "./invalid", 0));
+
+ /* add a file to the repository so we can reference it later */
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_mkfile("invalid/dummy.txt", "");
+ cl_git_pass(git_index_add_bypath(index, "dummy.txt"));
+ cl_must_pass(p_unlink("invalid/dummy.txt"));
+ cl_git_pass(git_index_remove_bypath(index, "dummy.txt"));
+ git_index_free(index);
+
+ cl_repo_set_bool(repo, "core.protectHFS", true);
+ cl_repo_set_bool(repo, "core.protectNTFS", true);
+
+ assert_add_fails(repo, ".git./hello");
+ assert_add_fails(repo, ".git\xe2\x80\xad/hello");
+ assert_add_fails(repo, "git~1/hello");
+ assert_add_fails(repo, ".git\xe2\x81\xaf/hello");
+ assert_add_fails(repo, ".git::$INDEX_ALLOCATION/dummy-file");
+
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("invalid");
+}
+
+static void replace_char(char *str, char in, char out)
+{
+ char *c = str;
+
+ while (*c++)
+ if (*c == in)
+ *c = out;
+}
+
+static void assert_write_fails(git_repository *repo, const char *fn_orig)
+{
+ git_index *index;
+ git_oid expected;
+ const git_index_entry *entry;
+ git_str path = GIT_STR_INIT;
+ char *fn;
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert(git_index_entrycount(index) == 0);
+
+ /*
+ * Sneak a valid path into the index, we'll update it
+ * to an invalid path when we try to write the index.
+ */
+ fn = git__strdup(fn_orig);
+ replace_char(fn, '/', '_');
+ replace_char(fn, ':', '!');
+
+ git_str_joinpath(&path, "./invalid", fn);
+
+ cl_git_mkfile(path.ptr, NULL);
+
+ cl_git_pass(git_index_add_bypath(index, fn));
+
+ cl_assert(entry = git_index_get_bypath(index, fn, 0));
+
+ /* kids, don't try this at home */
+ replace_char((char *)entry->path, '_', '/');
+ replace_char((char *)entry->path, '!', ':');
+
+ /* write-tree */
+ cl_git_fail(git_index_write_tree(&expected, index));
+
+ p_unlink(path.ptr);
+
+ cl_git_pass(git_index_remove_all(index, NULL, NULL, NULL));
+ git_str_dispose(&path);
+ git_index_free(index);
+ git__free(fn);
+}
+
+void test_index_tests__write_tree_invalid_unowned_index(void)
+{
+ git_index *idx;
+ git_repository *repo;
+ git_index_entry entry = {{0}};
+ git_oid tree_id;
+
+ cl_git_pass(git_index__new(&idx, GIT_OID_SHA1));
+
+ cl_git_pass(git_oid__fromstr(&entry.id, "8312e0a89a9cbab77c732b6bc39b51a783e3a318", GIT_OID_SHA1));
+ entry.path = "foo";
+ entry.mode = GIT_FILEMODE_BLOB;
+ cl_git_pass(git_index_add(idx, &entry));
+
+ cl_git_pass(git_repository_init(&repo, "./invalid-id", 0));
+
+ cl_git_fail(git_index_write_tree_to(&tree_id, idx, repo));
+
+ git_index_free(idx);
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("invalid-id");
+}
+
+/* Test that writing an invalid filename fails */
+void test_index_tests__write_invalid_filename(void)
+{
+ git_repository *repo;
+
+ p_mkdir("invalid", 0700);
+
+ cl_git_pass(git_repository_init(&repo, "./invalid", 0));
+
+ assert_write_fails(repo, ".git/hello");
+ assert_write_fails(repo, ".GIT/hello");
+ assert_write_fails(repo, ".GiT/hello");
+ assert_write_fails(repo, "./.git/hello");
+ assert_write_fails(repo, "./foo");
+ assert_write_fails(repo, "./bar");
+ assert_write_fails(repo, "foo/../bar");
+
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("invalid");
+}
+
+void test_index_tests__honors_protect_filesystems(void)
+{
+ git_repository *repo;
+
+ p_mkdir("invalid", 0700);
+
+ cl_git_pass(git_repository_init(&repo, "./invalid", 0));
+
+ cl_repo_set_bool(repo, "core.protectHFS", true);
+ cl_repo_set_bool(repo, "core.protectNTFS", true);
+
+ assert_write_fails(repo, ".git./hello");
+ assert_write_fails(repo, ".git\xe2\x80\xad/hello");
+ assert_write_fails(repo, "git~1/hello");
+ assert_write_fails(repo, ".git\xe2\x81\xaf/hello");
+ assert_write_fails(repo, ".git::$INDEX_ALLOCATION/dummy-file");
+
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("invalid");
+}
+
+void test_index_tests__protectntfs_on_by_default(void)
+{
+ git_repository *repo;
+
+ p_mkdir("invalid", 0700);
+
+ cl_git_pass(git_repository_init(&repo, "./invalid", 0));
+ assert_write_fails(repo, ".git./hello");
+ assert_write_fails(repo, "git~1/hello");
+
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("invalid");
+}
+
+void test_index_tests__can_disable_protectntfs(void)
+{
+ git_repository *repo;
+ git_index *index;
+
+ cl_must_pass(p_mkdir("valid", 0700));
+ cl_git_rewritefile("valid/git~1", "steal the shortname");
+
+ cl_git_pass(git_repository_init(&repo, "./valid", 0));
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_repo_set_bool(repo, "core.protectNTFS", false);
+
+ cl_git_pass(git_index_add_bypath(index, "git~1"));
+
+ git_index_free(index);
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("valid");
+}
+
+void test_index_tests__remove_entry(void)
+{
+ git_repository *repo;
+ git_index *index;
+
+ p_mkdir("index_test", 0770);
+
+ cl_git_pass(git_repository_init(&repo, "index_test", 0));
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert(git_index_entrycount(index) == 0);
+
+ cl_git_mkfile("index_test/hello", NULL);
+ cl_git_pass(git_index_add_bypath(index, "hello"));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_index_read(index, true)); /* reload */
+ cl_assert(git_index_entrycount(index) == 1);
+ cl_assert(git_index_get_bypath(index, "hello", 0) != NULL);
+
+ cl_git_pass(git_index_remove(index, "hello", 0));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_index_read(index, true)); /* reload */
+ cl_assert(git_index_entrycount(index) == 0);
+ cl_assert(git_index_get_bypath(index, "hello", 0) == NULL);
+
+ git_index_free(index);
+ git_repository_free(repo);
+ cl_fixture_cleanup("index_test");
+}
+
+void test_index_tests__remove_directory(void)
+{
+ git_repository *repo;
+ git_index *index;
+
+ p_mkdir("index_test", 0770);
+
+ cl_git_pass(git_repository_init(&repo, "index_test", 0));
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert_equal_i(0, (int)git_index_entrycount(index));
+
+ p_mkdir("index_test/a", 0770);
+ cl_git_mkfile("index_test/a/1.txt", NULL);
+ cl_git_mkfile("index_test/a/2.txt", NULL);
+ cl_git_mkfile("index_test/a/3.txt", NULL);
+ cl_git_mkfile("index_test/b.txt", NULL);
+
+ cl_git_pass(git_index_add_bypath(index, "a/1.txt"));
+ cl_git_pass(git_index_add_bypath(index, "a/2.txt"));
+ cl_git_pass(git_index_add_bypath(index, "a/3.txt"));
+ cl_git_pass(git_index_add_bypath(index, "b.txt"));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_index_read(index, true)); /* reload */
+ cl_assert_equal_i(4, (int)git_index_entrycount(index));
+ cl_assert(git_index_get_bypath(index, "a/1.txt", 0) != NULL);
+ cl_assert(git_index_get_bypath(index, "a/2.txt", 0) != NULL);
+ cl_assert(git_index_get_bypath(index, "b.txt", 0) != NULL);
+
+ cl_git_pass(git_index_remove(index, "a/1.txt", 0));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_index_read(index, true)); /* reload */
+ cl_assert_equal_i(3, (int)git_index_entrycount(index));
+ cl_assert(git_index_get_bypath(index, "a/1.txt", 0) == NULL);
+ cl_assert(git_index_get_bypath(index, "a/2.txt", 0) != NULL);
+ cl_assert(git_index_get_bypath(index, "b.txt", 0) != NULL);
+
+ cl_git_pass(git_index_remove_directory(index, "a", 0));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_index_read(index, true)); /* reload */
+ cl_assert_equal_i(1, (int)git_index_entrycount(index));
+ cl_assert(git_index_get_bypath(index, "a/1.txt", 0) == NULL);
+ cl_assert(git_index_get_bypath(index, "a/2.txt", 0) == NULL);
+ cl_assert(git_index_get_bypath(index, "b.txt", 0) != NULL);
+
+ git_index_free(index);
+ git_repository_free(repo);
+ cl_fixture_cleanup("index_test");
+}
+
+void test_index_tests__preserves_case(void)
+{
+ git_repository *repo;
+ git_index *index;
+ const git_index_entry *entry;
+ int index_caps;
+
+ cl_set_cleanup(&cleanup_myrepo, NULL);
+
+ cl_git_pass(git_repository_init(&repo, "./myrepo", 0));
+ cl_git_pass(git_repository_index(&index, repo));
+
+ index_caps = git_index_caps(index);
+
+ cl_git_rewritefile("myrepo/test.txt", "hey there\n");
+ cl_git_pass(git_index_add_bypath(index, "test.txt"));
+
+ cl_git_pass(p_rename("myrepo/test.txt", "myrepo/TEST.txt"));
+ cl_git_rewritefile("myrepo/TEST.txt", "hello again\n");
+ cl_git_pass(git_index_add_bypath(index, "TEST.txt"));
+
+ if (index_caps & GIT_INDEX_CAPABILITY_IGNORE_CASE)
+ cl_assert_equal_i(1, (int)git_index_entrycount(index));
+ else
+ cl_assert_equal_i(2, (int)git_index_entrycount(index));
+
+ /* Test access by path instead of index */
+ cl_assert((entry = git_index_get_bypath(index, "test.txt", 0)) != NULL);
+ /* The path should *not* have changed without an explicit remove */
+ cl_assert(git__strcmp(entry->path, "test.txt") == 0);
+
+ cl_assert((entry = git_index_get_bypath(index, "TEST.txt", 0)) != NULL);
+ if (index_caps & GIT_INDEX_CAPABILITY_IGNORE_CASE)
+ /* The path should *not* have changed without an explicit remove */
+ cl_assert(git__strcmp(entry->path, "test.txt") == 0);
+ else
+ cl_assert(git__strcmp(entry->path, "TEST.txt") == 0);
+
+ git_index_free(index);
+ git_repository_free(repo);
+}
+
+void test_index_tests__elocked(void)
+{
+ git_repository *repo;
+ git_index *index;
+ git_filebuf file = GIT_FILEBUF_INIT;
+ const git_error *err;
+ int error;
+
+ cl_set_cleanup(&cleanup_myrepo, NULL);
+
+ cl_git_pass(git_repository_init(&repo, "./myrepo", 0));
+ cl_git_pass(git_repository_index(&index, repo));
+
+ /* Lock the index file so we fail to lock it */
+ cl_git_pass(git_filebuf_open(&file, index->index_file_path, 0, 0666));
+ error = git_index_write(index);
+ cl_assert_equal_i(GIT_ELOCKED, error);
+
+ err = git_error_last();
+ cl_assert_equal_i(err->klass, GIT_ERROR_INDEX);
+
+ git_filebuf_cleanup(&file);
+ git_index_free(index);
+ git_repository_free(repo);
+}
+
+void test_index_tests__reload_from_disk(void)
+{
+ git_repository *repo;
+ git_index *read_index;
+ git_index *write_index;
+
+ cl_set_cleanup(&cleanup_myrepo, NULL);
+
+ cl_git_pass(git_futils_mkdir("./myrepo", 0777, GIT_MKDIR_PATH));
+ cl_git_mkfile("./myrepo/a.txt", "a\n");
+ cl_git_mkfile("./myrepo/b.txt", "b\n");
+
+ cl_git_pass(git_repository_init(&repo, "./myrepo", 0));
+ cl_git_pass(git_repository_index(&write_index, repo));
+ cl_assert_equal_i(false, write_index->on_disk);
+
+ cl_git_pass(git_index__open(&read_index, write_index->index_file_path, GIT_OID_SHA1));
+ cl_assert_equal_i(false, read_index->on_disk);
+
+ /* Stage two new files against the write_index */
+ cl_git_pass(git_index_add_bypath(write_index, "a.txt"));
+ cl_git_pass(git_index_add_bypath(write_index, "b.txt"));
+
+ cl_assert_equal_sz(2, git_index_entrycount(write_index));
+
+ /* Persist the index changes to disk */
+ cl_git_pass(git_index_write(write_index));
+ cl_assert_equal_i(true, write_index->on_disk);
+
+ /* Sync the changes back into the read_index */
+ cl_assert_equal_sz(0, git_index_entrycount(read_index));
+
+ cl_git_pass(git_index_read(read_index, true));
+ cl_assert_equal_i(true, read_index->on_disk);
+
+ cl_assert_equal_sz(2, git_index_entrycount(read_index));
+
+ /* Remove the index file from the filesystem */
+ cl_git_pass(p_unlink(write_index->index_file_path));
+
+ /* Sync the changes back into the read_index */
+ cl_git_pass(git_index_read(read_index, true));
+ cl_assert_equal_i(false, read_index->on_disk);
+ cl_assert_equal_sz(0, git_index_entrycount(read_index));
+
+ git_index_free(read_index);
+ git_index_free(write_index);
+ git_repository_free(repo);
+}
+
+void test_index_tests__corrupted_extension(void)
+{
+ git_index *index;
+
+ cl_git_fail_with(git_index__open(&index, TEST_INDEXBAD_PATH, GIT_OID_SHA1), GIT_ERROR);
+}
+
+void test_index_tests__reload_while_ignoring_case(void)
+{
+ git_index *index;
+ unsigned int caps;
+
+ cl_git_pass(git_index__open(&index, TEST_INDEX_PATH, GIT_OID_SHA1));
+ cl_git_pass(git_vector_verify_sorted(&index->entries));
+
+ caps = git_index_caps(index);
+ cl_git_pass(git_index_set_caps(index, caps &= ~GIT_INDEX_CAPABILITY_IGNORE_CASE));
+ cl_git_pass(git_index_read(index, true));
+ cl_git_pass(git_vector_verify_sorted(&index->entries));
+ cl_assert(git_index_get_bypath(index, ".HEADER", 0));
+ cl_assert_equal_p(NULL, git_index_get_bypath(index, ".header", 0));
+
+ cl_git_pass(git_index_set_caps(index, caps | GIT_INDEX_CAPABILITY_IGNORE_CASE));
+ cl_git_pass(git_index_read(index, true));
+ cl_git_pass(git_vector_verify_sorted(&index->entries));
+ cl_assert(git_index_get_bypath(index, ".HEADER", 0));
+ cl_assert(git_index_get_bypath(index, ".header", 0));
+
+ git_index_free(index);
+}
+
+void test_index_tests__change_icase_on_instance(void)
+{
+ git_index *index;
+ unsigned int caps;
+ const git_index_entry *e;
+
+ cl_git_pass(git_index__open(&index, TEST_INDEX_PATH, GIT_OID_SHA1));
+ cl_git_pass(git_vector_verify_sorted(&index->entries));
+
+ caps = git_index_caps(index);
+ cl_git_pass(git_index_set_caps(index, caps &= ~GIT_INDEX_CAPABILITY_IGNORE_CASE));
+ cl_assert_equal_i(false, index->ignore_case);
+ cl_git_pass(git_vector_verify_sorted(&index->entries));
+ cl_assert(e = git_index_get_bypath(index, "src/common.h", 0));
+ cl_assert_equal_p(NULL, e = git_index_get_bypath(index, "SRC/Common.h", 0));
+ cl_assert(e = git_index_get_bypath(index, "COPYING", 0));
+ cl_assert_equal_p(NULL, e = git_index_get_bypath(index, "copying", 0));
+
+ cl_git_pass(git_index_set_caps(index, caps | GIT_INDEX_CAPABILITY_IGNORE_CASE));
+ cl_assert_equal_i(true, index->ignore_case);
+ cl_git_pass(git_vector_verify_sorted(&index->entries));
+ cl_assert(e = git_index_get_bypath(index, "COPYING", 0));
+ cl_assert_equal_s("COPYING", e->path);
+ cl_assert(e = git_index_get_bypath(index, "copying", 0));
+ cl_assert_equal_s("COPYING", e->path);
+
+ git_index_free(index);
+}
+
+void test_index_tests__can_lock_index(void)
+{
+ git_repository *repo;
+ git_index *index;
+ git_indexwriter one = GIT_INDEXWRITER_INIT,
+ two = GIT_INDEXWRITER_INIT;
+
+ repo = cl_git_sandbox_init("testrepo.git");
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_indexwriter_init(&one, index));
+
+ cl_git_fail_with(GIT_ELOCKED, git_indexwriter_init(&two, index));
+ cl_git_fail_with(GIT_ELOCKED, git_index_write(index));
+
+ cl_git_pass(git_indexwriter_commit(&one));
+
+ cl_git_pass(git_index_write(index));
+
+ git_indexwriter_cleanup(&one);
+ git_indexwriter_cleanup(&two);
+ git_index_free(index);
+ cl_git_sandbox_cleanup();
+}
+
+void test_index_tests__can_iterate(void)
+{
+ git_index *index;
+ git_index_iterator *iterator;
+ const git_index_entry *entry;
+ size_t i, iterator_idx = 0, found = 0;
+ int ret;
+
+ cl_git_pass(git_index__open(&index, TEST_INDEX_PATH, GIT_OID_SHA1));
+ cl_git_pass(git_index_iterator_new(&iterator, index));
+
+ cl_assert(git_vector_is_sorted(&iterator->snap));
+
+ for (i = 0; i < ARRAY_SIZE(test_entries); i++) {
+ /* Advance iterator to next test entry index */
+ do {
+ ret = git_index_iterator_next(&entry, iterator);
+
+ if (ret == GIT_ITEROVER)
+ cl_fail("iterator did not contain all test entries");
+
+ cl_git_pass(ret);
+ } while (iterator_idx++ < test_entries[i].index);
+
+ cl_assert_equal_s(entry->path, test_entries[i].path);
+ cl_assert_equal_i(entry->mtime.seconds, test_entries[i].mtime);
+ cl_assert_equal_i(entry->file_size, test_entries[i].file_size);
+ found++;
+ }
+
+ while ((ret = git_index_iterator_next(&entry, iterator)) == 0)
+ ;
+
+ if (ret != GIT_ITEROVER)
+ cl_git_fail(ret);
+
+ cl_assert_equal_i(found, ARRAY_SIZE(test_entries));
+
+ git_index_iterator_free(iterator);
+ git_index_free(index);
+}
+
+void test_index_tests__can_modify_while_iterating(void)
+{
+ git_index *index;
+ git_index_iterator *iterator;
+ const git_index_entry *entry;
+ git_index_entry new_entry = {{0}};
+ size_t expected = 0, seen = 0;
+ int ret;
+
+ cl_git_pass(git_index__open(&index, TEST_INDEX_PATH, GIT_OID_SHA1));
+ cl_git_pass(git_index_iterator_new(&iterator, index));
+
+ expected = git_index_entrycount(index);
+ cl_assert(git_vector_is_sorted(&iterator->snap));
+
+ /*
+ * After we've counted the entries, add a new one and change another;
+ * ensure that our iterator is backed by a snapshot and thus returns
+ * the number of entries from when the iterator was created.
+ */
+ cl_git_pass(git_oid__fromstr(&new_entry.id, "8312e0a89a9cbab77c732b6bc39b51a783e3a318", GIT_OID_SHA1));
+ new_entry.path = "newfile";
+ new_entry.mode = GIT_FILEMODE_BLOB;
+ cl_git_pass(git_index_add(index, &new_entry));
+
+ cl_git_pass(git_oid__fromstr(&new_entry.id, "4141414141414141414141414141414141414141", GIT_OID_SHA1));
+ new_entry.path = "Makefile";
+ new_entry.mode = GIT_FILEMODE_BLOB;
+ cl_git_pass(git_index_add(index, &new_entry));
+
+ while (true) {
+ ret = git_index_iterator_next(&entry, iterator);
+
+ if (ret == GIT_ITEROVER)
+ break;
+
+ seen++;
+ }
+
+ cl_assert_equal_i(expected, seen);
+
+ git_index_iterator_free(iterator);
+ git_index_free(index);
+}
diff --git a/tests/libgit2/index/tests256.c b/tests/libgit2/index/tests256.c
new file mode 100644
index 0000000..fed8bfb
--- /dev/null
+++ b/tests/libgit2/index/tests256.c
@@ -0,0 +1,1169 @@
+#include "clar_libgit2.h"
+#include "index.h"
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+
+static const size_t index_entry_count = 4344;
+#define TEST_INDEX_PATH cl_fixture("git-sha256.index")
+
+static git_repository_init_options repo_init_opts =
+ GIT_REPOSITORY_INIT_OPTIONS_INIT;
+
+/* Suite data */
+struct test_entry {
+ size_t index;
+ char path[128];
+ off64_t file_size;
+ git_time_t mtime;
+};
+
+static struct test_entry test_entries[] = {
+ { 892, "Makefile", 120084, 0x642c3a6e },
+ { 1542, "git.c", 27432, 0x642c3a6e },
+ { 1737, "perl/Git.pm", 48084, 0x642c3a6e },
+ { 1961, "t/Makefile", 4711, 0x642c3a6e },
+ { 4343, "zlib.c", 6271, 0x642c3a6f }
+};
+
+/* Helpers */
+static void copy_file(const char *src, const char *dst)
+{
+ git_str source_buf = GIT_STR_INIT;
+ git_file dst_fd;
+
+ cl_git_pass(git_futils_readbuffer(&source_buf, src));
+
+ dst_fd = git_futils_creat_withpath(dst, 0777, 0666); /* -V536 */
+ if (dst_fd < 0)
+ goto cleanup;
+
+ cl_git_pass(p_write(dst_fd, source_buf.ptr, source_buf.size));
+
+cleanup:
+ git_str_dispose(&source_buf);
+ p_close(dst_fd);
+}
+
+static void files_are_equal(const char *a, const char *b)
+{
+ git_str buf_a = GIT_STR_INIT;
+ git_str buf_b = GIT_STR_INIT;
+
+ if (git_futils_readbuffer(&buf_a, a) < 0)
+ cl_assert(0);
+
+ if (git_futils_readbuffer(&buf_b, b) < 0) {
+ git_str_dispose(&buf_a);
+ cl_assert(0);
+ }
+
+ cl_assert_equal_sz(buf_a.size, buf_b.size);
+ cl_assert(!memcmp(buf_a.ptr, buf_b.ptr, buf_a.size));
+
+ git_str_dispose(&buf_a);
+ git_str_dispose(&buf_b);
+}
+
+#endif
+
+/* Fixture setup and teardown */
+void test_index_tests256__initialize(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ repo_init_opts.flags |= GIT_REPOSITORY_INIT_MKPATH;
+ repo_init_opts.oid_type = GIT_OID_SHA256;
+#endif
+}
+
+void test_index_tests256__cleanup(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_UNSAVED_INDEX_SAFETY, 0));
+#endif
+}
+
+void test_index_tests256__empty_index(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_index *index;
+
+ cl_git_pass(git_index__open(&index, "in-memory-index", GIT_OID_SHA256));
+ cl_assert(index->on_disk == 0);
+
+ cl_assert(git_index_entrycount(index) == 0);
+ cl_assert(git_vector_is_sorted(&index->entries));
+
+ git_index_free(index);
+#endif
+}
+
+void test_index_tests256__default_test_index(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_index *index;
+ unsigned int i;
+ git_index_entry **entries;
+
+ cl_git_pass(git_index__open(&index, TEST_INDEX_PATH, GIT_OID_SHA256));
+ cl_assert(index->on_disk);
+
+ cl_assert_equal_sz(git_index_entrycount(index), index_entry_count);
+ cl_assert(git_vector_is_sorted(&index->entries));
+
+ entries = (git_index_entry **)index->entries.contents;
+
+ for (i = 0; i < ARRAY_SIZE(test_entries); ++i) {
+ git_index_entry *e = entries[test_entries[i].index];
+
+ cl_assert_equal_s(e->path, test_entries[i].path);
+ cl_assert_equal_i(e->mtime.seconds, test_entries[i].mtime);
+ cl_assert_equal_i(e->file_size, test_entries[i].file_size);
+ }
+
+ git_index_free(index);
+#endif
+}
+
+void test_index_tests256__find_in_existing(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_index *index;
+ unsigned int i;
+
+ cl_git_pass(git_index__open(&index, TEST_INDEX_PATH, GIT_OID_SHA256));
+
+ for (i = 0; i < ARRAY_SIZE(test_entries); ++i) {
+ size_t idx;
+
+ cl_assert(!git_index_find(&idx, index, test_entries[i].path));
+ cl_assert(idx == test_entries[i].index);
+ }
+
+ git_index_free(index);
+#endif
+}
+
+void test_index_tests256__find_in_empty(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_index *index;
+ unsigned int i;
+
+ cl_git_pass(git_index__open(&index, "fake-index", GIT_OID_SHA256));
+
+ for (i = 0; i < ARRAY_SIZE(test_entries); ++i) {
+ cl_assert(GIT_ENOTFOUND == git_index_find(NULL, index, test_entries[i].path));
+ }
+
+ git_index_free(index);
+#endif
+}
+
+void test_index_tests256__find_prefix(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_index *index;
+ const git_index_entry *entry;
+ size_t pos;
+
+ cl_git_pass(git_index__open(&index, TEST_INDEX_PATH, GIT_OID_SHA256));
+
+ cl_git_pass(git_index_find_prefix(&pos, index, "Documentation"));
+ entry = git_index_get_byindex(index, pos);
+ cl_assert(git__strcmp(entry->path, "Documentation/.gitattributes") == 0);
+
+ cl_git_pass(git_index_find_prefix(&pos, index, "contrib/RE"));
+ entry = git_index_get_byindex(index, pos);
+ cl_assert(git__strcmp(entry->path, "contrib/README") == 0);
+
+ cl_assert(GIT_ENOTFOUND == git_index_find_prefix(NULL, index, "blah"));
+
+ git_index_free(index);
+#endif
+}
+
+void test_index_tests256__write(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_index *index;
+
+ copy_file(TEST_INDEX_PATH, "index_rewrite");
+
+ cl_git_pass(git_index__open(&index, "index_rewrite", GIT_OID_SHA256));
+ cl_assert(index->on_disk);
+
+ cl_git_pass(git_index_write(index));
+ files_are_equal(TEST_INDEX_PATH, "index_rewrite");
+
+ git_index_free(index);
+
+ p_unlink("index_rewrite");
+#endif
+}
+
+void test_index_tests256__sort1(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ /* sort the entries in an empty index */
+ git_index *index;
+
+ cl_git_pass(git_index__open(&index, "fake-index", GIT_OID_SHA256));
+
+ /* FIXME: this test is slightly dumb */
+ cl_assert(git_vector_is_sorted(&index->entries));
+
+ git_index_free(index);
+#endif
+}
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+static void cleanup_myrepo(void *opaque)
+{
+ GIT_UNUSED(opaque);
+ cl_fixture_cleanup("myrepo");
+}
+#endif
+
+void test_index_tests256__add(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_index *index;
+ git_filebuf file = GIT_FILEBUF_INIT;
+ git_repository *repo;
+ const git_index_entry *entry;
+ git_oid id1;
+
+ cl_set_cleanup(&cleanup_myrepo, NULL);
+
+ /* Initialize a new repository */
+ cl_git_pass(git_repository_init_ext(&repo, "./myrepo", &repo_init_opts));
+
+ /* Ensure we're the only guy in the room */
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert(git_index_entrycount(index) == 0);
+
+ /* Create a new file in the working directory */
+ cl_git_pass(git_futils_mkpath2file("myrepo/test.txt", 0777));
+ cl_git_pass(git_filebuf_open(&file, "myrepo/test.txt", 0, 0666));
+ cl_git_pass(git_filebuf_write(&file, "hey there\n", 10));
+ cl_git_pass(git_filebuf_commit(&file));
+
+ /* Store the expected hash of the file/blob
+ * This has been generated by executing the following
+ * $ echo "hey there" | git hash-object --stdin
+ */
+ cl_git_pass(git_oid__fromstr(&id1, "aea29dc305d40e362df25c3fdeed5502fd56b182af01b7740d297a24459333c5", GIT_OID_SHA256));
+
+ /* Add the new file to the index */
+ cl_git_pass(git_index_add_bypath(index, "test.txt"));
+
+ /* Wow... it worked! */
+ cl_assert(git_index_entrycount(index) == 1);
+ entry = git_index_get_byindex(index, 0);
+
+ /* And the built-in hashing mechanism worked as expected */
+ cl_assert_equal_oid(&id1, &entry->id);
+
+ /* Test access by path instead of index */
+ cl_assert((entry = git_index_get_bypath(index, "test.txt", 0)) != NULL);
+ cl_assert_equal_oid(&id1, &entry->id);
+
+ git_index_free(index);
+ git_repository_free(repo);
+#endif
+}
+
+void test_index_tests256__add_frombuffer(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_index *index;
+ git_repository *repo;
+ git_index_entry entry;
+ const git_index_entry *returned_entry;
+
+ git_oid id1;
+ git_blob *blob;
+
+ const char *content = "hey there\n";
+
+ cl_set_cleanup(&cleanup_myrepo, NULL);
+
+ /* Initialize a new repository */
+ cl_git_pass(git_repository_init_ext(&repo, "./myrepo", &repo_init_opts));
+
+ /* Ensure we're the only guy in the room */
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert(git_index_entrycount(index) == 0);
+
+ /* Store the expected hash of the file/blob
+ * This has been generated by executing the following
+ * $ echo "hey there" | git hash-object --stdin
+ */
+ cl_git_pass(git_oid__fromstr(&id1, "aea29dc305d40e362df25c3fdeed5502fd56b182af01b7740d297a24459333c5", GIT_OID_SHA256));
+
+ /* Add the new file to the index */
+ memset(&entry, 0x0, sizeof(git_index_entry));
+ entry.mode = GIT_FILEMODE_BLOB;
+ entry.path = "test.txt";
+ cl_git_pass(git_index_add_from_buffer(index, &entry,
+ content, strlen(content)));
+
+ /* Wow... it worked! */
+ cl_assert(git_index_entrycount(index) == 1);
+ returned_entry = git_index_get_byindex(index, 0);
+
+ /* And the built-in hashing mechanism worked as expected */
+ cl_assert_equal_oid(&id1, &returned_entry->id);
+ /* And mode is the one asked */
+ cl_assert_equal_i(GIT_FILEMODE_BLOB, returned_entry->mode);
+
+ /* Test access by path instead of index */
+ cl_assert((returned_entry = git_index_get_bypath(index, "test.txt", 0)) != NULL);
+ cl_assert_equal_oid(&id1, &returned_entry->id);
+
+ /* Test the blob is in the repository */
+ cl_git_pass(git_blob_lookup(&blob, repo, &id1));
+ cl_assert_equal_s(
+ content, git_blob_rawcontent(blob));
+ git_blob_free(blob);
+
+ git_index_free(index);
+ git_repository_free(repo);
+#endif
+}
+
+void test_index_tests256__dirty_and_clean(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_repository *repo;
+ git_index *index;
+ git_index_entry entry = {{0}};
+
+ /* Index is not dirty after opening */
+ cl_git_pass(git_repository_init_ext(&repo, "./myrepo", &repo_init_opts));
+ cl_git_pass(git_repository_index(&index, repo));
+
+ cl_assert(git_index_entrycount(index) == 0);
+ cl_assert(!git_index_is_dirty(index));
+
+ /* Index is dirty after adding an entry */
+ entry.mode = GIT_FILEMODE_BLOB;
+ entry.path = "test.txt";
+ cl_git_pass(git_index_add_from_buffer(index, &entry, "Hi.\n", 4));
+ cl_assert(git_index_entrycount(index) == 1);
+ cl_assert(git_index_is_dirty(index));
+
+ /* Index is not dirty after write */
+ cl_git_pass(git_index_write(index));
+ cl_assert(!git_index_is_dirty(index));
+
+ /* Index is dirty after removing an entry */
+ cl_git_pass(git_index_remove_bypath(index, "test.txt"));
+ cl_assert(git_index_entrycount(index) == 0);
+ cl_assert(git_index_is_dirty(index));
+
+ /* Index is not dirty after write */
+ cl_git_pass(git_index_write(index));
+ cl_assert(!git_index_is_dirty(index));
+
+ /* Index remains not dirty after read */
+ cl_git_pass(git_index_read(index, 0));
+ cl_assert(!git_index_is_dirty(index));
+
+ /* Index is dirty when we do an unforced read with dirty content */
+ cl_git_pass(git_index_add_from_buffer(index, &entry, "Hi.\n", 4));
+ cl_assert(git_index_entrycount(index) == 1);
+ cl_assert(git_index_is_dirty(index));
+
+ cl_git_pass(git_index_read(index, 0));
+ cl_assert(git_index_is_dirty(index));
+
+ /* Index is clean when we force a read with dirty content */
+ cl_git_pass(git_index_read(index, 1));
+ cl_assert(!git_index_is_dirty(index));
+
+ git_index_free(index);
+ git_repository_free(repo);
+#endif
+}
+
+void test_index_tests256__dirty_fails_optionally(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_repository *repo;
+ git_index *index;
+ git_index_entry entry = {{0}};
+
+ /* Index is not dirty after opening */
+ repo = cl_git_sandbox_init("testrepo");
+ cl_git_pass(git_repository_index(&index, repo));
+
+ /* Index is dirty after adding an entry */
+ entry.mode = GIT_FILEMODE_BLOB;
+ entry.path = "test.txt";
+ cl_git_pass(git_index_add_from_buffer(index, &entry, "Hi.\n", 4));
+ cl_assert(git_index_is_dirty(index));
+
+ cl_git_pass(git_checkout_head(repo, NULL));
+
+ /* Index is dirty (again) after adding an entry */
+ entry.mode = GIT_FILEMODE_BLOB;
+ entry.path = "test.txt";
+ cl_git_pass(git_index_add_from_buffer(index, &entry, "Hi.\n", 4));
+ cl_assert(git_index_is_dirty(index));
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_UNSAVED_INDEX_SAFETY, 1));
+ cl_git_fail_with(GIT_EINDEXDIRTY, git_checkout_head(repo, NULL));
+
+ git_index_free(index);
+ cl_git_sandbox_cleanup();
+#endif
+}
+
+void test_index_tests256__add_frombuffer_reset_entry(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_index *index;
+ git_repository *repo;
+ git_index_entry entry;
+ const git_index_entry *returned_entry;
+ git_filebuf file = GIT_FILEBUF_INIT;
+
+ git_oid id1;
+ git_blob *blob;
+ const char *old_content = "here\n";
+ const char *content = "hey there\n";
+
+ cl_set_cleanup(&cleanup_myrepo, NULL);
+
+ /* Initialize a new repository */
+ cl_git_pass(git_repository_init_ext(&repo, "./myrepo", &repo_init_opts));
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_futils_mkpath2file("myrepo/test.txt", 0777));
+ cl_git_pass(git_filebuf_open(&file, "myrepo/test.txt", 0, 0666));
+ cl_git_pass(git_filebuf_write(&file, old_content, strlen(old_content)));
+ cl_git_pass(git_filebuf_commit(&file));
+
+ /* Store the expected hash of the file/blob
+ * This has been generated by executing the following
+ * $ echo "hey there" | git hash-object --stdin
+ */
+ cl_git_pass(git_oid__fromstr(&id1, "aea29dc305d40e362df25c3fdeed5502fd56b182af01b7740d297a24459333c5", GIT_OID_SHA256));
+
+ cl_git_pass(git_index_add_bypath(index, "test.txt"));
+
+ /* Add the new file to the index */
+ memset(&entry, 0x0, sizeof(git_index_entry));
+ entry.mode = GIT_FILEMODE_BLOB;
+ entry.path = "test.txt";
+ cl_git_pass(git_index_add_from_buffer(index, &entry,
+ content, strlen(content)));
+
+ /* Wow... it worked! */
+ cl_assert(git_index_entrycount(index) == 1);
+ returned_entry = git_index_get_byindex(index, 0);
+
+ /* And the built-in hashing mechanism worked as expected */
+ cl_assert_equal_oid(&id1, &returned_entry->id);
+ /* And mode is the one asked */
+ cl_assert_equal_i(GIT_FILEMODE_BLOB, returned_entry->mode);
+
+ /* Test access by path instead of index */
+ cl_assert((returned_entry = git_index_get_bypath(index, "test.txt", 0)) != NULL);
+ cl_assert_equal_oid(&id1, &returned_entry->id);
+ cl_assert_equal_i(0, returned_entry->dev);
+ cl_assert_equal_i(0, returned_entry->ino);
+ cl_assert_equal_i(0, returned_entry->uid);
+ cl_assert_equal_i(0, returned_entry->uid);
+ cl_assert_equal_i(10, returned_entry->file_size);
+
+ /* Test the blob is in the repository */
+ cl_git_pass(git_blob_lookup(&blob, repo, &id1));
+ cl_assert_equal_s(content, git_blob_rawcontent(blob));
+ git_blob_free(blob);
+
+ git_index_free(index);
+ git_repository_free(repo);
+#endif
+}
+
+void test_index_tests256__add_bypath_to_a_bare_repository_returns_EBAREPO(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_repository *bare_repo;
+ git_index *index;
+
+ cl_git_pass(git_repository_open(&bare_repo, cl_fixture("testrepo.git")));
+ cl_git_pass(git_repository_index(&index, bare_repo));
+
+ cl_assert_equal_i(GIT_EBAREREPO, git_index_add_bypath(index, "test.txt"));
+
+ git_index_free(index);
+ git_repository_free(bare_repo);
+#endif
+}
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+static void assert_add_bypath_fails(git_repository *repo, const char *fn)
+{
+ git_index *index;
+ git_str path = GIT_STR_INIT;
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert(git_index_entrycount(index) == 0);
+
+ git_str_joinpath(&path, "./invalid", fn);
+
+ cl_git_mkfile(path.ptr, NULL);
+ cl_git_fail(git_index_add_bypath(index, fn));
+ cl_must_pass(p_unlink(path.ptr));
+
+ cl_assert(git_index_entrycount(index) == 0);
+
+ git_str_dispose(&path);
+ git_index_free(index);
+}
+#endif
+
+/* Test that writing an invalid filename fails */
+void test_index_tests256__cannot_add_invalid_filename(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_repository *repo;
+
+ cl_must_pass(p_mkdir("invalid", 0700));
+ cl_git_pass(git_repository_init_ext(&repo, "./invalid", &repo_init_opts));
+ cl_must_pass(p_mkdir("./invalid/subdir", 0777));
+
+ /* cl_git_mkfile() needs the dir to exist */
+ if (!git_fs_path_exists("./invalid/.GIT"))
+ cl_must_pass(p_mkdir("./invalid/.GIT", 0777));
+ if (!git_fs_path_exists("./invalid/.GiT"))
+ cl_must_pass(p_mkdir("./invalid/.GiT", 0777));
+
+ assert_add_bypath_fails(repo, ".git/hello");
+ assert_add_bypath_fails(repo, ".GIT/hello");
+ assert_add_bypath_fails(repo, ".GiT/hello");
+ assert_add_bypath_fails(repo, "./.git/hello");
+ assert_add_bypath_fails(repo, "./foo");
+ assert_add_bypath_fails(repo, "./bar");
+ assert_add_bypath_fails(repo, "subdir/../bar");
+
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("invalid");
+#endif
+}
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+static void assert_add_fails(git_repository *repo, const char *fn)
+{
+ git_index *index;
+ git_str path = GIT_STR_INIT;
+ git_index_entry entry = {{0}};
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert(git_index_entrycount(index) == 0);
+
+ entry.path = fn;
+ entry.mode = GIT_FILEMODE_BLOB;
+ cl_git_pass(git_oid__fromstr(&entry.id, "aea29dc305d40e362df25c3fdeed5502fd56b182af01b7740d297a24459333c5", GIT_OID_SHA256));
+
+ cl_git_fail(git_index_add(index, &entry));
+
+ cl_assert(git_index_entrycount(index) == 0);
+
+ git_str_dispose(&path);
+ git_index_free(index);
+}
+#endif
+
+/*
+ * Test that writing an invalid filename fails on filesystem
+ * specific protected names
+ */
+void test_index_tests256__cannot_add_protected_invalid_filename(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_repository *repo;
+ git_index *index;
+
+ cl_must_pass(p_mkdir("invalid", 0700));
+
+ cl_git_pass(git_repository_init(&repo, "./invalid", 0));
+
+ /* add a file to the repository so we can reference it later */
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_mkfile("invalid/dummy.txt", "");
+ cl_git_pass(git_index_add_bypath(index, "dummy.txt"));
+ cl_must_pass(p_unlink("invalid/dummy.txt"));
+ cl_git_pass(git_index_remove_bypath(index, "dummy.txt"));
+ git_index_free(index);
+
+ cl_repo_set_bool(repo, "core.protectHFS", true);
+ cl_repo_set_bool(repo, "core.protectNTFS", true);
+
+ assert_add_fails(repo, ".git./hello");
+ assert_add_fails(repo, ".git\xe2\x80\xad/hello");
+ assert_add_fails(repo, "git~1/hello");
+ assert_add_fails(repo, ".git\xe2\x81\xaf/hello");
+ assert_add_fails(repo, ".git::$INDEX_ALLOCATION/dummy-file");
+
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("invalid");
+#endif
+}
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+static void replace_char(char *str, char in, char out)
+{
+ char *c = str;
+
+ while (*c++)
+ if (*c == in)
+ *c = out;
+}
+
+static void assert_write_fails(git_repository *repo, const char *fn_orig)
+{
+ git_index *index;
+ git_oid expected;
+ const git_index_entry *entry;
+ git_str path = GIT_STR_INIT;
+ char *fn;
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert(git_index_entrycount(index) == 0);
+
+ /*
+ * Sneak a valid path into the index, we'll update it
+ * to an invalid path when we try to write the index.
+ */
+ fn = git__strdup(fn_orig);
+ replace_char(fn, '/', '_');
+ replace_char(fn, ':', '!');
+
+ git_str_joinpath(&path, "./invalid", fn);
+
+ cl_git_mkfile(path.ptr, NULL);
+
+ cl_git_pass(git_index_add_bypath(index, fn));
+
+ cl_assert(entry = git_index_get_bypath(index, fn, 0));
+
+ /* kids, don't try this at home */
+ replace_char((char *)entry->path, '_', '/');
+ replace_char((char *)entry->path, '!', ':');
+
+ /* write-tree */
+ cl_git_fail(git_index_write_tree(&expected, index));
+
+ p_unlink(path.ptr);
+
+ cl_git_pass(git_index_remove_all(index, NULL, NULL, NULL));
+ git_str_dispose(&path);
+ git_index_free(index);
+ git__free(fn);
+}
+#endif
+
+void test_index_tests256__write_tree_invalid_unowned_index(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_index *idx;
+ git_repository *repo;
+ git_index_entry entry = {{0}};
+ git_oid tree_id;
+
+ cl_git_pass(git_index__new(&idx, GIT_OID_SHA256));
+
+ // TODO: this one is failing
+ cl_git_pass(git_oid__fromstr(&entry.id, "a8c2e0a89a9cbab77c732b6bc39b51a783e3a318a847f46cba7614cac9814291", GIT_OID_SHA256));
+ entry.path = "foo";
+ entry.mode = GIT_FILEMODE_BLOB;
+ cl_git_pass(git_index_add(idx, &entry));
+
+ cl_git_pass(git_repository_init_ext(&repo, "./invalid-id", &repo_init_opts));
+
+ cl_git_fail(git_index_write_tree_to(&tree_id, idx, repo));
+
+ git_index_free(idx);
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("invalid-id");
+#endif
+}
+
+/* Test that writing an invalid filename fails */
+void test_index_tests256__write_invalid_filename(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_repository *repo;
+
+ p_mkdir("invalid", 0700);
+
+ cl_git_pass(git_repository_init(&repo, "./invalid", 0));
+
+ assert_write_fails(repo, ".git/hello");
+ assert_write_fails(repo, ".GIT/hello");
+ assert_write_fails(repo, ".GiT/hello");
+ assert_write_fails(repo, "./.git/hello");
+ assert_write_fails(repo, "./foo");
+ assert_write_fails(repo, "./bar");
+ assert_write_fails(repo, "foo/../bar");
+
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("invalid");
+#endif
+}
+
+void test_index_tests256__honors_protect_filesystems(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_repository *repo;
+
+ p_mkdir("invalid", 0700);
+
+ cl_git_pass(git_repository_init(&repo, "./invalid", 0));
+
+ cl_repo_set_bool(repo, "core.protectHFS", true);
+ cl_repo_set_bool(repo, "core.protectNTFS", true);
+
+ assert_write_fails(repo, ".git./hello");
+ assert_write_fails(repo, ".git\xe2\x80\xad/hello");
+ assert_write_fails(repo, "git~1/hello");
+ assert_write_fails(repo, ".git\xe2\x81\xaf/hello");
+ assert_write_fails(repo, ".git::$INDEX_ALLOCATION/dummy-file");
+
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("invalid");
+#endif
+}
+
+void test_index_tests256__protectntfs_on_by_default(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_repository *repo;
+
+ p_mkdir("invalid", 0700);
+
+ cl_git_pass(git_repository_init(&repo, "./invalid", 0));
+ assert_write_fails(repo, ".git./hello");
+ assert_write_fails(repo, "git~1/hello");
+
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("invalid");
+#endif
+}
+
+void test_index_tests256__can_disable_protectntfs(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_repository *repo;
+ git_index *index;
+
+ cl_must_pass(p_mkdir("valid", 0700));
+ cl_git_rewritefile("valid/git~1", "steal the shortname");
+
+ cl_git_pass(git_repository_init(&repo, "./valid", 0));
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_repo_set_bool(repo, "core.protectNTFS", false);
+
+ cl_git_pass(git_index_add_bypath(index, "git~1"));
+
+ git_index_free(index);
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("valid");
+#endif
+}
+
+void test_index_tests256__remove_entry(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_repository *repo;
+ git_index *index;
+
+ p_mkdir("index_test", 0770);
+
+ cl_git_pass(git_repository_init(&repo, "index_test", 0));
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert(git_index_entrycount(index) == 0);
+
+ cl_git_mkfile("index_test/hello", NULL);
+ cl_git_pass(git_index_add_bypath(index, "hello"));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_index_read(index, true)); /* reload */
+ cl_assert(git_index_entrycount(index) == 1);
+ cl_assert(git_index_get_bypath(index, "hello", 0) != NULL);
+
+ cl_git_pass(git_index_remove(index, "hello", 0));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_index_read(index, true)); /* reload */
+ cl_assert(git_index_entrycount(index) == 0);
+ cl_assert(git_index_get_bypath(index, "hello", 0) == NULL);
+
+ git_index_free(index);
+ git_repository_free(repo);
+ cl_fixture_cleanup("index_test");
+#endif
+}
+
+void test_index_tests256__remove_directory(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_repository *repo;
+ git_index *index;
+
+ p_mkdir("index_test", 0770);
+
+ cl_git_pass(git_repository_init(&repo, "index_test", 0));
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert_equal_i(0, (int)git_index_entrycount(index));
+
+ p_mkdir("index_test/a", 0770);
+ cl_git_mkfile("index_test/a/1.txt", NULL);
+ cl_git_mkfile("index_test/a/2.txt", NULL);
+ cl_git_mkfile("index_test/a/3.txt", NULL);
+ cl_git_mkfile("index_test/b.txt", NULL);
+
+ cl_git_pass(git_index_add_bypath(index, "a/1.txt"));
+ cl_git_pass(git_index_add_bypath(index, "a/2.txt"));
+ cl_git_pass(git_index_add_bypath(index, "a/3.txt"));
+ cl_git_pass(git_index_add_bypath(index, "b.txt"));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_index_read(index, true)); /* reload */
+ cl_assert_equal_i(4, (int)git_index_entrycount(index));
+ cl_assert(git_index_get_bypath(index, "a/1.txt", 0) != NULL);
+ cl_assert(git_index_get_bypath(index, "a/2.txt", 0) != NULL);
+ cl_assert(git_index_get_bypath(index, "b.txt", 0) != NULL);
+
+ cl_git_pass(git_index_remove(index, "a/1.txt", 0));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_index_read(index, true)); /* reload */
+ cl_assert_equal_i(3, (int)git_index_entrycount(index));
+ cl_assert(git_index_get_bypath(index, "a/1.txt", 0) == NULL);
+ cl_assert(git_index_get_bypath(index, "a/2.txt", 0) != NULL);
+ cl_assert(git_index_get_bypath(index, "b.txt", 0) != NULL);
+
+ cl_git_pass(git_index_remove_directory(index, "a", 0));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_index_read(index, true)); /* reload */
+ cl_assert_equal_i(1, (int)git_index_entrycount(index));
+ cl_assert(git_index_get_bypath(index, "a/1.txt", 0) == NULL);
+ cl_assert(git_index_get_bypath(index, "a/2.txt", 0) == NULL);
+ cl_assert(git_index_get_bypath(index, "b.txt", 0) != NULL);
+
+ git_index_free(index);
+ git_repository_free(repo);
+ cl_fixture_cleanup("index_test");
+#endif
+}
+
+void test_index_tests256__preserves_case(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_repository *repo;
+ git_index *index;
+ const git_index_entry *entry;
+ int index_caps;
+
+ cl_set_cleanup(&cleanup_myrepo, NULL);
+
+ cl_git_pass(git_repository_init_ext(&repo, "./myrepo", &repo_init_opts));
+ cl_git_pass(git_repository_index(&index, repo));
+
+ index_caps = git_index_caps(index);
+
+ cl_git_rewritefile("myrepo/test.txt", "hey there\n");
+ cl_git_pass(git_index_add_bypath(index, "test.txt"));
+
+ cl_git_pass(p_rename("myrepo/test.txt", "myrepo/TEST.txt"));
+ cl_git_rewritefile("myrepo/TEST.txt", "hello again\n");
+ cl_git_pass(git_index_add_bypath(index, "TEST.txt"));
+
+ if (index_caps & GIT_INDEX_CAPABILITY_IGNORE_CASE)
+ cl_assert_equal_i(1, (int)git_index_entrycount(index));
+ else
+ cl_assert_equal_i(2, (int)git_index_entrycount(index));
+
+ /* Test access by path instead of index */
+ cl_assert((entry = git_index_get_bypath(index, "test.txt", 0)) != NULL);
+ /* The path should *not* have changed without an explicit remove */
+ cl_assert(git__strcmp(entry->path, "test.txt") == 0);
+
+ cl_assert((entry = git_index_get_bypath(index, "TEST.txt", 0)) != NULL);
+ if (index_caps & GIT_INDEX_CAPABILITY_IGNORE_CASE)
+ /* The path should *not* have changed without an explicit remove */
+ cl_assert(git__strcmp(entry->path, "test.txt") == 0);
+ else
+ cl_assert(git__strcmp(entry->path, "TEST.txt") == 0);
+
+ git_index_free(index);
+ git_repository_free(repo);
+#endif
+}
+
+void test_index_tests256__elocked(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_repository *repo;
+ git_index *index;
+ git_filebuf file = GIT_FILEBUF_INIT;
+ const git_error *err;
+ int error;
+
+ cl_set_cleanup(&cleanup_myrepo, NULL);
+
+ cl_git_pass(git_repository_init_ext(&repo, "./myrepo", &repo_init_opts));
+ cl_git_pass(git_repository_index(&index, repo));
+
+ /* Lock the index file so we fail to lock it */
+ cl_git_pass(git_filebuf_open(&file, index->index_file_path, 0, 0666));
+ error = git_index_write(index);
+ cl_assert_equal_i(GIT_ELOCKED, error);
+
+ err = git_error_last();
+ cl_assert_equal_i(err->klass, GIT_ERROR_INDEX);
+
+ git_filebuf_cleanup(&file);
+ git_index_free(index);
+ git_repository_free(repo);
+#endif
+}
+
+void test_index_tests256__reload_from_disk(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_repository *repo;
+ git_index *read_index;
+ git_index *write_index;
+
+ cl_set_cleanup(&cleanup_myrepo, NULL);
+
+ cl_git_pass(git_futils_mkdir("./myrepo", 0777, GIT_MKDIR_PATH));
+ cl_git_mkfile("./myrepo/a.txt", "a\n");
+ cl_git_mkfile("./myrepo/b.txt", "b\n");
+
+ cl_git_pass(git_repository_init_ext(&repo, "./myrepo", &repo_init_opts));
+ cl_git_pass(git_repository_index(&write_index, repo));
+ cl_assert_equal_i(false, write_index->on_disk);
+
+ cl_git_pass(git_index__open(&read_index, write_index->index_file_path, GIT_OID_SHA256));
+ cl_assert_equal_i(false, read_index->on_disk);
+
+ /* Stage two new files against the write_index */
+ cl_git_pass(git_index_add_bypath(write_index, "a.txt"));
+ cl_git_pass(git_index_add_bypath(write_index, "b.txt"));
+
+ cl_assert_equal_sz(2, git_index_entrycount(write_index));
+
+ /* Persist the index changes to disk */
+ cl_git_pass(git_index_write(write_index));
+ cl_assert_equal_i(true, write_index->on_disk);
+
+ /* Sync the changes back into the read_index */
+ cl_assert_equal_sz(0, git_index_entrycount(read_index));
+
+ cl_git_pass(git_index_read(read_index, true));
+ cl_assert_equal_i(true, read_index->on_disk);
+
+ cl_assert_equal_sz(2, git_index_entrycount(read_index));
+
+ /* Remove the index file from the filesystem */
+ cl_git_pass(p_unlink(write_index->index_file_path));
+
+ /* Sync the changes back into the read_index */
+ cl_git_pass(git_index_read(read_index, true));
+ cl_assert_equal_i(false, read_index->on_disk);
+ cl_assert_equal_sz(0, git_index_entrycount(read_index));
+
+ git_index_free(read_index);
+ git_index_free(write_index);
+ git_repository_free(repo);
+#endif
+}
+
+void test_index_tests256__reload_while_ignoring_case(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_index *index;
+ unsigned int caps;
+
+ cl_git_pass(git_index__open(&index, TEST_INDEX_PATH, GIT_OID_SHA256));
+ cl_git_pass(git_vector_verify_sorted(&index->entries));
+
+ caps = git_index_caps(index);
+ cl_git_pass(git_index_set_caps(index, caps &= ~GIT_INDEX_CAPABILITY_IGNORE_CASE));
+ cl_git_pass(git_index_read(index, true));
+ cl_git_pass(git_vector_verify_sorted(&index->entries));
+ cl_assert(git_index_get_bypath(index, "contrib/README", 0));
+ cl_assert_equal_p(NULL, git_index_get_bypath(index, "CONTRIB/readme", 0));
+
+ cl_git_pass(git_index_set_caps(index, caps | GIT_INDEX_CAPABILITY_IGNORE_CASE));
+ cl_git_pass(git_index_read(index, true));
+ cl_git_pass(git_vector_verify_sorted(&index->entries));
+ cl_assert(git_index_get_bypath(index, "contrib/README", 0));
+ cl_assert(git_index_get_bypath(index, "CONTRIB/readme", 0));
+
+ git_index_free(index);
+#endif
+}
+
+void test_index_tests256__change_icase_on_instance(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_index *index;
+ unsigned int caps;
+ const git_index_entry *e;
+
+ cl_git_pass(git_index__open(&index, TEST_INDEX_PATH, GIT_OID_SHA256));
+ cl_git_pass(git_vector_verify_sorted(&index->entries));
+
+ caps = git_index_caps(index);
+ cl_git_pass(git_index_set_caps(index, caps &= ~GIT_INDEX_CAPABILITY_IGNORE_CASE));
+ cl_assert_equal_i(false, index->ignore_case);
+ cl_git_pass(git_vector_verify_sorted(&index->entries));
+ cl_assert(e = git_index_get_bypath(index, "contrib/README", 0));
+ cl_assert_equal_p(NULL, e = git_index_get_bypath(index, "CONTRIB/readme", 0));
+ cl_assert(e = git_index_get_bypath(index, "config.h", 0));
+ cl_assert_equal_p(NULL, e = git_index_get_bypath(index, "CONFIG.H", 0));
+
+ cl_git_pass(git_index_set_caps(index, caps | GIT_INDEX_CAPABILITY_IGNORE_CASE));
+ cl_assert_equal_i(true, index->ignore_case);
+ cl_git_pass(git_vector_verify_sorted(&index->entries));
+ cl_assert(e = git_index_get_bypath(index, "config.h", 0));
+ cl_assert_equal_s("config.h", e->path);
+ cl_assert(e = git_index_get_bypath(index, "CONFIG.H", 0));
+ cl_assert_equal_s("config.h", e->path);
+
+ git_index_free(index);
+#endif
+}
+
+void test_index_tests256__can_lock_index(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_repository *repo;
+ git_index *index;
+ git_indexwriter one = GIT_INDEXWRITER_INIT,
+ two = GIT_INDEXWRITER_INIT;
+
+ repo = cl_git_sandbox_init("testrepo.git");
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_indexwriter_init(&one, index));
+
+ cl_git_fail_with(GIT_ELOCKED, git_indexwriter_init(&two, index));
+ cl_git_fail_with(GIT_ELOCKED, git_index_write(index));
+
+ cl_git_pass(git_indexwriter_commit(&one));
+
+ cl_git_pass(git_index_write(index));
+
+ git_indexwriter_cleanup(&one);
+ git_indexwriter_cleanup(&two);
+ git_index_free(index);
+ cl_git_sandbox_cleanup();
+#endif
+}
+
+void test_index_tests256__can_iterate(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_index *index;
+ git_index_iterator *iterator;
+ const git_index_entry *entry;
+ size_t i, iterator_idx = 0, found = 0;
+ int ret;
+
+ cl_git_pass(git_index__open(&index, TEST_INDEX_PATH, GIT_OID_SHA256));
+ cl_git_pass(git_index_iterator_new(&iterator, index));
+
+ cl_assert(git_vector_is_sorted(&iterator->snap));
+
+ for (i = 0; i < ARRAY_SIZE(test_entries); i++) {
+ /* Advance iterator to next test entry index */
+ do {
+ ret = git_index_iterator_next(&entry, iterator);
+
+ if (ret == GIT_ITEROVER)
+ cl_fail("iterator did not contain all test entries");
+
+ cl_git_pass(ret);
+ } while (iterator_idx++ < test_entries[i].index);
+
+ cl_assert_equal_s(entry->path, test_entries[i].path);
+ cl_assert_equal_i(entry->mtime.seconds, test_entries[i].mtime);
+ cl_assert_equal_i(entry->file_size, test_entries[i].file_size);
+ found++;
+ }
+
+ while ((ret = git_index_iterator_next(&entry, iterator)) == 0)
+ ;
+
+ if (ret != GIT_ITEROVER)
+ cl_git_fail(ret);
+
+ cl_assert_equal_i(found, ARRAY_SIZE(test_entries));
+
+ git_index_iterator_free(iterator);
+ git_index_free(index);
+#endif
+}
+
+void test_index_tests256__can_modify_while_iterating(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_index *index;
+ git_index_iterator *iterator;
+ const git_index_entry *entry;
+ git_index_entry new_entry = {{0}};
+ size_t expected = 0, seen = 0;
+ int ret;
+
+ cl_git_pass(git_index__open(&index, TEST_INDEX_PATH, GIT_OID_SHA256));
+ cl_git_pass(git_index_iterator_new(&iterator, index));
+
+ expected = git_index_entrycount(index);
+ cl_assert(git_vector_is_sorted(&iterator->snap));
+
+ /*
+ * After we've counted the entries, add a new one and change another;
+ * ensure that our iterator is backed by a snapshot and thus returns
+ * the number of entries from when the iterator was created.
+ */
+ cl_git_pass(git_oid__fromstr(&new_entry.id, "8312e0a89a9cbab77c732b6bc39b51a783e3a318a847f46cba7614cac9814291", GIT_OID_SHA256));
+ new_entry.path = "newfile";
+ new_entry.mode = GIT_FILEMODE_BLOB;
+ cl_git_pass(git_index_add(index, &new_entry));
+
+ cl_git_pass(git_oid__fromstr(&new_entry.id, "4141414141414141414141414141414141414141414141414141414141414141", GIT_OID_SHA256));
+ new_entry.path = "Makefile";
+ new_entry.mode = GIT_FILEMODE_BLOB;
+ cl_git_pass(git_index_add(index, &new_entry));
+
+ while (true) {
+ ret = git_index_iterator_next(&entry, iterator);
+
+ if (ret == GIT_ITEROVER)
+ break;
+
+ seen++;
+ }
+
+ cl_assert_equal_i(expected, seen);
+
+ git_index_iterator_free(iterator);
+ git_index_free(index);
+#endif
+}
diff --git a/tests/libgit2/index/version.c b/tests/libgit2/index/version.c
new file mode 100644
index 0000000..b6c0b79
--- /dev/null
+++ b/tests/libgit2/index/version.c
@@ -0,0 +1,140 @@
+#include "clar_libgit2.h"
+#include "index.h"
+
+static git_repository *g_repo = NULL;
+
+void test_index_version__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ g_repo = NULL;
+}
+
+void test_index_version__can_read_v4(void)
+{
+ const char *paths[] = {
+ "file.tx", "file.txt", "file.txz", "foo", "zzz",
+ };
+ git_index *index;
+ size_t i;
+
+ g_repo = cl_git_sandbox_init("indexv4");
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_assert_equal_sz(git_index_entrycount(index), 5);
+
+ for (i = 0; i < ARRAY_SIZE(paths); i++) {
+ const git_index_entry *entry =
+ git_index_get_bypath(index, paths[i], GIT_INDEX_STAGE_NORMAL);
+
+ cl_assert(entry != NULL);
+ }
+
+ git_index_free(index);
+}
+
+void test_index_version__can_write_v4(void)
+{
+ const char *paths[] = {
+ "foo",
+ "foox",
+ "foobar",
+ "foobal",
+ "x",
+ "xz",
+ "xyzzyx"
+ };
+ git_repository *repo;
+ git_index_entry entry;
+ git_index *index;
+ size_t i;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_set_version(index, 4));
+
+ for (i = 0; i < ARRAY_SIZE(paths); i++) {
+ memset(&entry, 0, sizeof(entry));
+ entry.path = paths[i];
+ entry.mode = GIT_FILEMODE_BLOB;
+ cl_git_pass(git_index_add_from_buffer(index, &entry, paths[i],
+ strlen(paths[i]) + 1));
+ }
+ cl_assert_equal_sz(git_index_entrycount(index), ARRAY_SIZE(paths));
+
+ cl_git_pass(git_index_write(index));
+ git_index_free(index);
+
+ cl_git_pass(git_repository_open(&repo, git_repository_path(g_repo)));
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert(git_index_version(index) == 4);
+
+ for (i = 0; i < ARRAY_SIZE(paths); i++) {
+ const git_index_entry *e;
+
+ cl_assert(e = git_index_get_bypath(index, paths[i], 0));
+ cl_assert_equal_s(paths[i], e->path);
+ }
+
+ git_index_free(index);
+ git_repository_free(repo);
+}
+
+void test_index_version__v4_uses_path_compression(void)
+{
+ git_index_entry entry;
+ git_index *index;
+ char path[250], buf[1];
+ struct stat st;
+ char i, j;
+
+ memset(path, 'a', sizeof(path));
+ memset(buf, 'a', sizeof(buf));
+
+ memset(&entry, 0, sizeof(entry));
+ entry.path = path;
+ entry.mode = GIT_FILEMODE_BLOB;
+
+ g_repo = cl_git_sandbox_init("indexv4");
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ /* write 676 paths of 250 bytes length */
+ for (i = 'a'; i <= 'z'; i++) {
+ for (j = 'a'; j < 'z'; j++) {
+ path[ARRAY_SIZE(path) - 3] = i;
+ path[ARRAY_SIZE(path) - 2] = j;
+ path[ARRAY_SIZE(path) - 1] = '\0';
+ cl_git_pass(git_index_add_from_buffer(index, &entry, buf, sizeof(buf)));
+ }
+ }
+
+ cl_git_pass(git_index_write(index));
+ cl_git_pass(p_stat(git_index_path(index), &st));
+
+ /*
+ * Without path compression, the written paths would at
+ * least take
+ *
+ * (entries * pathlen) = len
+ * (676 * 250) = 169000
+ *
+ * bytes. As index v4 uses suffix-compression and our
+ * written paths only differ in the last two entries,
+ * this number will be much smaller, e.g.
+ *
+ * (1 * pathlen) + (675 * 2) = len
+ * 676 + 1350 = 2026
+ *
+ * bytes.
+ *
+ * Note that the above calculations do not include
+ * additional metadata of the index, e.g. OIDs or
+ * index extensions. Including those we get an index
+ * of approx. 200kB without compression and 40kB with
+ * compression. As this is a lot smaller than without
+ * compression, we can verify that path compression is
+ * used.
+ */
+ cl_assert_(st.st_size < 75000, "path compression not enabled");
+
+ git_index_free(index);
+}
diff --git a/tests/libgit2/iterator/index.c b/tests/libgit2/iterator/index.c
new file mode 100644
index 0000000..a008347
--- /dev/null
+++ b/tests/libgit2/iterator/index.c
@@ -0,0 +1,1385 @@
+#include "clar_libgit2.h"
+#include "iterator.h"
+#include "repository.h"
+#include "futils.h"
+#include "iterator_helpers.h"
+#include "../submodule/submodule_helpers.h"
+#include <stdarg.h>
+
+static git_repository *g_repo;
+
+void test_iterator_index__initialize(void)
+{
+}
+
+void test_iterator_index__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ g_repo = NULL;
+}
+
+static void index_iterator_test(
+ const char *sandbox,
+ const char *start,
+ const char *end,
+ git_iterator_flag_t flags,
+ int expected_count,
+ const char **expected_names,
+ const char **expected_oids)
+{
+ git_index *index;
+ git_iterator *i;
+ const git_index_entry *entry;
+ int error, count = 0, caps;
+ git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
+
+ g_repo = cl_git_sandbox_init(sandbox);
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ caps = git_index_caps(index);
+
+ iter_opts.flags = flags;
+ iter_opts.start = start;
+ iter_opts.end = end;
+
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &iter_opts));
+
+ while (!(error = git_iterator_advance(&entry, i))) {
+ cl_assert(entry);
+
+ if (expected_names != NULL)
+ cl_assert_equal_s(expected_names[count], entry->path);
+
+ if (expected_oids != NULL) {
+ git_oid oid;
+ cl_git_pass(git_oid__fromstr(&oid, expected_oids[count], GIT_OID_SHA1));
+ cl_assert_equal_oid(&oid, &entry->id);
+ }
+
+ count++;
+ }
+
+ cl_assert_equal_i(GIT_ITEROVER, error);
+ cl_assert(!entry);
+ cl_assert_equal_i(expected_count, count);
+
+ git_iterator_free(i);
+
+ cl_assert(caps == git_index_caps(index));
+ git_index_free(index);
+}
+
+static const char *expected_index_0[] = {
+ "attr0",
+ "attr1",
+ "attr2",
+ "attr3",
+ "binfile",
+ "gitattributes",
+ "macro_bad",
+ "macro_test",
+ "root_test1",
+ "root_test2",
+ "root_test3",
+ "root_test4.txt",
+ "sub/abc",
+ "sub/file",
+ "sub/sub/file",
+ "sub/sub/subsub.txt",
+ "sub/subdir_test1",
+ "sub/subdir_test2.txt",
+ "subdir/.gitattributes",
+ "subdir/abc",
+ "subdir/subdir_test1",
+ "subdir/subdir_test2.txt",
+ "subdir2/subdir2_test1",
+};
+
+static const char *expected_index_oids_0[] = {
+ "556f8c827b8e4a02ad5cab77dca2bcb3e226b0b3",
+ "3b74db7ab381105dc0d28f8295a77f6a82989292",
+ "2c66e14f77196ea763fb1e41612c1aa2bc2d8ed2",
+ "c485abe35abd4aa6fd83b076a78bbea9e2e7e06c",
+ "d800886d9c86731ae5c4a62b0b77c437015e00d2",
+ "2b40c5aca159b04ea8d20ffe36cdf8b09369b14a",
+ "5819a185d77b03325aaf87cafc771db36f6ddca7",
+ "ff69f8639ce2e6010b3f33a74160aad98b48da2b",
+ "45141a79a77842c59a63229403220a4e4be74e3d",
+ "4d713dc48e6b1bd75b0d61ad078ba9ca3a56745d",
+ "108bb4e7fd7b16490dc33ff7d972151e73d7166e",
+ "a0f7217ae99f5ac3e88534f5cea267febc5fa85b",
+ "3e42ffc54a663f9401cc25843d6c0e71a33e4249",
+ "45b983be36b73c0788dc9cbcb76cbb80fc7bb057",
+ "45b983be36b73c0788dc9cbcb76cbb80fc7bb057",
+ "9e5bdc47d6a80f2be0ea3049ad74231b94609242",
+ "e563cf4758f0d646f1b14b76016aa17fa9e549a4",
+ "fb5067b1aef3ac1ada4b379dbcb7d17255df7d78",
+ "99eae476896f4907224978b88e5ecaa6c5bb67a9",
+ "3e42ffc54a663f9401cc25843d6c0e71a33e4249",
+ "e563cf4758f0d646f1b14b76016aa17fa9e549a4",
+ "fb5067b1aef3ac1ada4b379dbcb7d17255df7d78",
+ "dccada462d3df8ac6de596fb8c896aba9344f941"
+};
+
+void test_iterator_index__0(void)
+{
+ index_iterator_test(
+ "attr", NULL, NULL, 0, ARRAY_SIZE(expected_index_0),
+ expected_index_0, expected_index_oids_0);
+}
+
+static const char *expected_index_1[] = {
+ "current_file",
+ "file_deleted",
+ "modified_file",
+ "staged_changes",
+ "staged_changes_file_deleted",
+ "staged_changes_modified_file",
+ "staged_new_file",
+ "staged_new_file_deleted_file",
+ "staged_new_file_modified_file",
+ "subdir.txt",
+ "subdir/current_file",
+ "subdir/deleted_file",
+ "subdir/modified_file",
+};
+
+static const char* expected_index_oids_1[] = {
+ "a0de7e0ac200c489c41c59dfa910154a70264e6e",
+ "5452d32f1dd538eb0405e8a83cc185f79e25e80f",
+ "452e4244b5d083ddf0460acf1ecc74db9dcfa11a",
+ "55d316c9ba708999f1918e9677d01dfcae69c6b9",
+ "a6be623522ce87a1d862128ac42672604f7b468b",
+ "906ee7711f4f4928ddcb2a5f8fbc500deba0d2a8",
+ "529a16e8e762d4acb7b9636ff540a00831f9155a",
+ "90b8c29d8ba39434d1c63e1b093daaa26e5bd972",
+ "ed062903b8f6f3dccb2fa81117ba6590944ef9bd",
+ "e8ee89e15bbe9b20137715232387b3de5b28972e",
+ "53ace0d1cc1145a5f4fe4f78a186a60263190733",
+ "1888c805345ba265b0ee9449b8877b6064592058",
+ "a6191982709b746d5650e93c2acf34ef74e11504"
+};
+
+void test_iterator_index__1(void)
+{
+ index_iterator_test(
+ "status", NULL, NULL, 0, ARRAY_SIZE(expected_index_1),
+ expected_index_1, expected_index_oids_1);
+}
+
+static const char *expected_index_range[] = {
+ "root_test1",
+ "root_test2",
+ "root_test3",
+ "root_test4.txt",
+};
+
+static const char *expected_index_oids_range[] = {
+ "45141a79a77842c59a63229403220a4e4be74e3d",
+ "4d713dc48e6b1bd75b0d61ad078ba9ca3a56745d",
+ "108bb4e7fd7b16490dc33ff7d972151e73d7166e",
+ "a0f7217ae99f5ac3e88534f5cea267febc5fa85b",
+};
+
+void test_iterator_index__range(void)
+{
+ index_iterator_test(
+ "attr", "root", "root", 0, ARRAY_SIZE(expected_index_range),
+ expected_index_range, expected_index_oids_range);
+}
+
+void test_iterator_index__range_empty_0(void)
+{
+ index_iterator_test(
+ "attr", "empty", "empty", 0, 0, NULL, NULL);
+}
+
+void test_iterator_index__range_empty_1(void)
+{
+ index_iterator_test(
+ "attr", "z_empty_after", NULL, 0, 0, NULL, NULL);
+}
+
+void test_iterator_index__range_empty_2(void)
+{
+ index_iterator_test(
+ "attr", NULL, ".aaa_empty_before", 0, 0, NULL, NULL);
+}
+
+static void check_index_range(
+ git_repository *repo,
+ const char *start,
+ const char *end,
+ bool ignore_case,
+ int expected_count)
+{
+ git_index *index;
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+ int error, count, caps;
+ bool is_ignoring_case;
+
+ cl_git_pass(git_repository_index(&index, repo));
+
+ caps = git_index_caps(index);
+ is_ignoring_case = ((caps & GIT_INDEX_CAPABILITY_IGNORE_CASE) != 0);
+
+ if (ignore_case != is_ignoring_case)
+ cl_git_pass(git_index_set_caps(index, caps ^ GIT_INDEX_CAPABILITY_IGNORE_CASE));
+
+ i_opts.flags = 0;
+ i_opts.start = start;
+ i_opts.end = end;
+
+ cl_git_pass(git_iterator_for_index(&i, repo, index, &i_opts));
+
+ cl_assert(git_iterator_ignore_case(i) == ignore_case);
+
+ for (count = 0; !(error = git_iterator_advance(NULL, i)); ++count)
+ /* count em up */;
+
+ cl_assert_equal_i(GIT_ITEROVER, error);
+ cl_assert_equal_i(expected_count, count);
+
+ git_iterator_free(i);
+ git_index_free(index);
+}
+
+void test_iterator_index__range_icase(void)
+{
+ git_index *index;
+ git_tree *head;
+
+ g_repo = cl_git_sandbox_init("testrepo");
+
+ /* reset index to match HEAD */
+ cl_git_pass(git_repository_head_tree(&head, g_repo));
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_read_tree(index, head));
+ cl_git_pass(git_index_write(index));
+ git_tree_free(head);
+ git_index_free(index);
+
+ /* do some ranged iterator checks toggling case sensitivity */
+ check_index_range(g_repo, "B", "C", false, 0);
+ check_index_range(g_repo, "B", "C", true, 1);
+ check_index_range(g_repo, "a", "z", false, 3);
+ check_index_range(g_repo, "a", "z", true, 4);
+}
+
+static const char *expected_index_cs[] = {
+ "B", "D", "F", "H", "J", "L/1", "L/B", "L/D", "L/a", "L/c",
+ "a", "c", "e", "g", "i", "k/1", "k/B", "k/D", "k/a", "k/c",
+};
+
+static const char *expected_index_ci[] = {
+ "a", "B", "c", "D", "e", "F", "g", "H", "i", "J",
+ "k/1", "k/a", "k/B", "k/c", "k/D", "L/1", "L/a", "L/B", "L/c", "L/D",
+};
+
+void test_iterator_index__case_folding(void)
+{
+ git_str path = GIT_STR_INIT;
+ int fs_is_ci = 0;
+
+ cl_git_pass(git_str_joinpath(&path, cl_fixture("icase"), ".gitted/CoNfIg"));
+ fs_is_ci = git_fs_path_exists(path.ptr);
+ git_str_dispose(&path);
+
+ index_iterator_test(
+ "icase", NULL, NULL, 0, ARRAY_SIZE(expected_index_cs),
+ fs_is_ci ? expected_index_ci : expected_index_cs, NULL);
+
+ cl_git_sandbox_cleanup();
+
+ index_iterator_test(
+ "icase", NULL, NULL, GIT_ITERATOR_IGNORE_CASE,
+ ARRAY_SIZE(expected_index_ci), expected_index_ci, NULL);
+
+ cl_git_sandbox_cleanup();
+
+ index_iterator_test(
+ "icase", NULL, NULL, GIT_ITERATOR_DONT_IGNORE_CASE,
+ ARRAY_SIZE(expected_index_cs), expected_index_cs, NULL);
+}
+
+/* Index contents (including pseudotrees):
+ *
+ * 0: a 5: F 10: k/ 16: L/
+ * 1: B 6: g 11: k/1 17: L/1
+ * 2: c 7: H 12: k/a 18: L/a
+ * 3: D 8: i 13: k/B 19: L/B
+ * 4: e 9: J 14: k/c 20: L/c
+ * 15: k/D 21: L/D
+ *
+ * 0: B 5: L/ 11: a 16: k/
+ * 1: D 6: L/1 12: c 17: k/1
+ * 2: F 7: L/B 13: e 18: k/B
+ * 3: H 8: L/D 14: g 19: k/D
+ * 4: J 9: L/a 15: i 20: k/a
+ * 10: L/c 21: k/c
+ */
+
+void test_iterator_index__icase_0(void)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_index *index;
+
+ g_repo = cl_git_sandbox_init("icase");
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ /* autoexpand with no tree entries for index */
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, NULL));
+ expect_iterator_items(i, 20, NULL, 20, NULL);
+ git_iterator_free(i);
+
+ /* auto expand with tree entries */
+ i_opts.flags = GIT_ITERATOR_INCLUDE_TREES;
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, 22, NULL, 22, NULL);
+ git_iterator_free(i);
+
+ /* no auto expand (implies trees included) */
+ i_opts.flags = GIT_ITERATOR_DONT_AUTOEXPAND;
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, 12, NULL, 22, NULL);
+ git_iterator_free(i);
+
+ git_index_free(index);
+}
+
+void test_iterator_index__icase_1(void)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_index *index;
+ int caps;
+
+ g_repo = cl_git_sandbox_init("icase");
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ caps = git_index_caps(index);
+
+ /* force case sensitivity */
+ cl_git_pass(git_index_set_caps(index, caps & ~GIT_INDEX_CAPABILITY_IGNORE_CASE));
+
+ /* autoexpand with no tree entries over range */
+ i_opts.start = "c";
+ i_opts.end = "k/D";
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, 7, NULL, 7, NULL);
+ git_iterator_free(i);
+
+ i_opts.start = "k";
+ i_opts.end = "k/Z";
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, 3, NULL, 3, NULL);
+ git_iterator_free(i);
+
+ /* auto expand with tree entries */
+ i_opts.flags = GIT_ITERATOR_INCLUDE_TREES;
+
+ i_opts.start = "c";
+ i_opts.end = "k/D";
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, 8, NULL, 8, NULL);
+ git_iterator_free(i);
+
+ i_opts.start = "k";
+ i_opts.end = "k/Z";
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, 4, NULL, 4, NULL);
+ git_iterator_free(i);
+
+ /* no auto expand (implies trees included) */
+ i_opts.flags = GIT_ITERATOR_DONT_AUTOEXPAND;
+
+ i_opts.start = "c";
+ i_opts.end = "k/D";
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, 5, NULL, 8, NULL);
+ git_iterator_free(i);
+
+ i_opts.start = "k";
+ i_opts.end = "k/Z";
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, 1, NULL, 4, NULL);
+ git_iterator_free(i);
+
+ /* force case insensitivity */
+ cl_git_pass(git_index_set_caps(index, caps | GIT_INDEX_CAPABILITY_IGNORE_CASE));
+
+ /* autoexpand with no tree entries over range */
+ i_opts.flags = 0;
+
+ i_opts.start = "c";
+ i_opts.end = "k/D";
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, 13, NULL, 13, NULL);
+ git_iterator_free(i);
+
+ i_opts.start = "k";
+ i_opts.end = "k/Z";
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, 5, NULL, 5, NULL);
+ git_iterator_free(i);
+
+ /* auto expand with tree entries */
+ i_opts.flags = GIT_ITERATOR_INCLUDE_TREES;
+
+ i_opts.start = "c";
+ i_opts.end = "k/D";
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, 14, NULL, 14, NULL);
+ git_iterator_free(i);
+
+ i_opts.start = "k";
+ i_opts.end = "k/Z";
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, 6, NULL, 6, NULL);
+ git_iterator_free(i);
+
+ /* no auto expand (implies trees included) */
+ i_opts.flags = GIT_ITERATOR_DONT_AUTOEXPAND;
+
+ i_opts.start = "c";
+ i_opts.end = "k/D";
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, 9, NULL, 14, NULL);
+ git_iterator_free(i);
+
+ i_opts.start = "k";
+ i_opts.end = "k/Z";
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, 1, NULL, 6, NULL);
+ git_iterator_free(i);
+
+ cl_git_pass(git_index_set_caps(index, caps));
+ git_index_free(index);
+}
+
+void test_iterator_index__pathlist(void)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_index *index;
+ git_vector filelist;
+
+ cl_git_pass(git_vector_init(&filelist, 100, &git__strcmp_cb));
+ cl_git_pass(git_vector_insert(&filelist, "a"));
+ cl_git_pass(git_vector_insert(&filelist, "B"));
+ cl_git_pass(git_vector_insert(&filelist, "c"));
+ cl_git_pass(git_vector_insert(&filelist, "D"));
+ cl_git_pass(git_vector_insert(&filelist, "e"));
+ cl_git_pass(git_vector_insert(&filelist, "k/1"));
+ cl_git_pass(git_vector_insert(&filelist, "k/a"));
+ cl_git_pass(git_vector_insert(&filelist, "L/1"));
+
+ g_repo = cl_git_sandbox_init("icase");
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+
+ /* Case sensitive */
+ {
+ const char *expected[] = {
+ "B", "D", "L/1", "a", "c", "e", "k/1", "k/a" };
+ size_t expected_len = 8;
+
+ i_opts.start = NULL;
+ i_opts.end = NULL;
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ /* Case INsensitive */
+ {
+ const char *expected[] = {
+ "a", "B", "c", "D", "e", "k/1", "k/a", "L/1" };
+ size_t expected_len = 8;
+
+ i_opts.start = NULL;
+ i_opts.end = NULL;
+ i_opts.flags = GIT_ITERATOR_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ /* Set a start, but no end. Case sensitive. */
+ {
+ const char *expected[] = { "c", "e", "k/1", "k/a" };
+ size_t expected_len = 4;
+
+ i_opts.start = "c";
+ i_opts.end = NULL;
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ /* Set a start, but no end. Case INsensitive. */
+ {
+ const char *expected[] = { "c", "D", "e", "k/1", "k/a", "L/1" };
+ size_t expected_len = 6;
+
+ i_opts.start = "c";
+ i_opts.end = NULL;
+ i_opts.flags = GIT_ITERATOR_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ /* Set no start, but an end. Case sensitive. */
+ {
+ const char *expected[] = { "B", "D", "L/1", "a", "c", "e" };
+ size_t expected_len = 6;
+
+ i_opts.start = NULL;
+ i_opts.end = "e";
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ /* Set no start, but an end. Case INsensitive. */
+ {
+ const char *expected[] = { "a", "B", "c", "D", "e" };
+ size_t expected_len = 5;
+
+ i_opts.start = NULL;
+ i_opts.end = "e";
+ i_opts.flags = GIT_ITERATOR_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ /* Start and an end, case sensitive */
+ {
+ const char *expected[] = { "c", "e", "k/1" };
+ size_t expected_len = 3;
+
+ i_opts.start = "c";
+ i_opts.end = "k/D";
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ /* Start and an end, case sensitive */
+ {
+ const char *expected[] = { "k/1" };
+ size_t expected_len = 1;
+
+ i_opts.start = "k";
+ i_opts.end = "k/D";
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ /* Start and an end, case INsensitive */
+ {
+ const char *expected[] = { "c", "D", "e", "k/1", "k/a" };
+ size_t expected_len = 5;
+
+ i_opts.start = "c";
+ i_opts.end = "k/D";
+ i_opts.flags = GIT_ITERATOR_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ /* Start and an end, case INsensitive */
+ {
+ const char *expected[] = { "k/1", "k/a" };
+ size_t expected_len = 2;
+
+ i_opts.start = "k";
+ i_opts.end = "k/D";
+ i_opts.flags = GIT_ITERATOR_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ git_index_free(index);
+ git_vector_free(&filelist);
+}
+
+void test_iterator_index__pathlist_with_dirs(void)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_index *index;
+ git_vector filelist;
+
+ cl_git_pass(git_vector_init(&filelist, 5, NULL));
+
+ g_repo = cl_git_sandbox_init("icase");
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ /* Test that a prefix `k` matches folders, even without trailing slash */
+ {
+ const char *expected[] = { "k/1", "k/B", "k/D", "k/a", "k/c" };
+ size_t expected_len = 5;
+
+ git_vector_clear(&filelist);
+ cl_git_pass(git_vector_insert(&filelist, "k"));
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ /* Test that a `k/` matches a folder */
+ {
+ const char *expected[] = { "k/1", "k/B", "k/D", "k/a", "k/c" };
+ size_t expected_len = 5;
+
+ git_vector_clear(&filelist);
+ cl_git_pass(git_vector_insert(&filelist, "k/"));
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ /* When the iterator is case sensitive, ensure we can't lookup the
+ * directory with the wrong case.
+ */
+ {
+ git_vector_clear(&filelist);
+ cl_git_pass(git_vector_insert(&filelist, "K/"));
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ cl_git_fail_with(GIT_ITEROVER, git_iterator_advance(NULL, i));
+ git_iterator_free(i);
+ }
+
+ /* Test that case insensitive matching works. */
+ {
+ const char *expected[] = { "k/1", "k/a", "k/B", "k/c", "k/D" };
+ size_t expected_len = 5;
+
+ git_vector_clear(&filelist);
+ cl_git_pass(git_vector_insert(&filelist, "K/"));
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+ i_opts.flags = GIT_ITERATOR_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ /* Test that case insensitive matching works without trailing slash. */
+ {
+ const char *expected[] = { "k/1", "k/a", "k/B", "k/c", "k/D" };
+ size_t expected_len = 5;
+
+ git_vector_clear(&filelist);
+ cl_git_pass(git_vector_insert(&filelist, "K"));
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+ i_opts.flags = GIT_ITERATOR_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ git_index_free(index);
+ git_vector_free(&filelist);
+}
+
+void test_iterator_index__pathlist_with_dirs_include_trees(void)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_index *index;
+ git_vector filelist;
+
+ const char *expected[] = { "k/", "k/1", "k/B", "k/D", "k/a", "k/c" };
+ size_t expected_len = 6;
+
+ cl_git_pass(git_vector_init(&filelist, 5, NULL));
+
+ g_repo = cl_git_sandbox_init("icase");
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ git_vector_clear(&filelist);
+ cl_git_pass(git_vector_insert(&filelist, "k"));
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE | GIT_ITERATOR_INCLUDE_TREES;
+
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+
+ git_index_free(index);
+ git_vector_free(&filelist);
+}
+
+void test_iterator_index__pathlist_1(void)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_index *index;
+ git_vector filelist = GIT_VECTOR_INIT;
+ int default_icase, expect;
+
+ g_repo = cl_git_sandbox_init("icase");
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_pass(git_vector_init(&filelist, 100, &git__strcmp_cb));
+ cl_git_pass(git_vector_insert(&filelist, "0"));
+ cl_git_pass(git_vector_insert(&filelist, "c"));
+ cl_git_pass(git_vector_insert(&filelist, "D"));
+ cl_git_pass(git_vector_insert(&filelist, "e"));
+ cl_git_pass(git_vector_insert(&filelist, "k/1"));
+ cl_git_pass(git_vector_insert(&filelist, "k/a"));
+
+ /* In this test we DO NOT force a case setting on the index. */
+ default_icase = ((git_index_caps(index) & GIT_INDEX_CAPABILITY_IGNORE_CASE) != 0);
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+
+ i_opts.start = "b";
+ i_opts.end = "k/D";
+
+ /* (c D e k/1 k/a ==> 5) vs (c e k/1 ==> 3) */
+ expect = default_icase ? 5 : 3;
+
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, expect, NULL, expect, NULL);
+ git_iterator_free(i);
+
+ git_index_free(index);
+ git_vector_free(&filelist);
+}
+
+void test_iterator_index__pathlist_2(void)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_index *index;
+ git_vector filelist = GIT_VECTOR_INIT;
+ int default_icase, expect;
+
+ g_repo = cl_git_sandbox_init("icase");
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_pass(git_vector_init(&filelist, 100, &git__strcmp_cb));
+ cl_git_pass(git_vector_insert(&filelist, "0"));
+ cl_git_pass(git_vector_insert(&filelist, "c"));
+ cl_git_pass(git_vector_insert(&filelist, "D"));
+ cl_git_pass(git_vector_insert(&filelist, "e"));
+ cl_git_pass(git_vector_insert(&filelist, "k/"));
+ cl_git_pass(git_vector_insert(&filelist, "k.a"));
+ cl_git_pass(git_vector_insert(&filelist, "k.b"));
+ cl_git_pass(git_vector_insert(&filelist, "kZZZZZZZ"));
+
+ /* In this test we DO NOT force a case setting on the index. */
+ default_icase = ((git_index_caps(index) & GIT_INDEX_CAPABILITY_IGNORE_CASE) != 0);
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+
+ i_opts.start = "b";
+ i_opts.end = "k/D";
+
+ /* (c D e k/1 k/a k/B k/c k/D) vs (c e k/1 k/B k/D) */
+ expect = default_icase ? 8 : 5;
+
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, expect, NULL, expect, NULL);
+ git_iterator_free(i);
+
+ git_index_free(index);
+ git_vector_free(&filelist);
+}
+
+void test_iterator_index__pathlist_four(void)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_index *index;
+ git_vector filelist = GIT_VECTOR_INIT;
+ int default_icase, expect;
+
+ g_repo = cl_git_sandbox_init("icase");
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_pass(git_vector_init(&filelist, 100, &git__strcmp_cb));
+ cl_git_pass(git_vector_insert(&filelist, "0"));
+ cl_git_pass(git_vector_insert(&filelist, "c"));
+ cl_git_pass(git_vector_insert(&filelist, "D"));
+ cl_git_pass(git_vector_insert(&filelist, "e"));
+ cl_git_pass(git_vector_insert(&filelist, "k"));
+ cl_git_pass(git_vector_insert(&filelist, "k.a"));
+ cl_git_pass(git_vector_insert(&filelist, "k.b"));
+ cl_git_pass(git_vector_insert(&filelist, "kZZZZZZZ"));
+
+ /* In this test we DO NOT force a case setting on the index. */
+ default_icase = ((git_index_caps(index) & GIT_INDEX_CAPABILITY_IGNORE_CASE) != 0);
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+
+ i_opts.start = "b";
+ i_opts.end = "k/D";
+
+ /* (c D e k/1 k/a k/B k/c k/D) vs (c e k/1 k/B k/D) */
+ expect = default_icase ? 8 : 5;
+
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, expect, NULL, expect, NULL);
+ git_iterator_free(i);
+
+ git_index_free(index);
+ git_vector_free(&filelist);
+}
+
+void test_iterator_index__pathlist_icase(void)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_index *index;
+ int caps;
+ git_vector filelist;
+
+ cl_git_pass(git_vector_init(&filelist, 100, &git__strcmp_cb));
+ cl_git_pass(git_vector_insert(&filelist, "a"));
+ cl_git_pass(git_vector_insert(&filelist, "B"));
+ cl_git_pass(git_vector_insert(&filelist, "c"));
+ cl_git_pass(git_vector_insert(&filelist, "D"));
+ cl_git_pass(git_vector_insert(&filelist, "e"));
+ cl_git_pass(git_vector_insert(&filelist, "k/1"));
+ cl_git_pass(git_vector_insert(&filelist, "k/a"));
+ cl_git_pass(git_vector_insert(&filelist, "L/1"));
+
+ g_repo = cl_git_sandbox_init("icase");
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ caps = git_index_caps(index);
+
+ /* force case sensitivity */
+ cl_git_pass(git_index_set_caps(index, caps & ~GIT_INDEX_CAPABILITY_IGNORE_CASE));
+
+ /* All indexfilelist iterator tests are "autoexpand with no tree entries" */
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+
+ i_opts.start = "c";
+ i_opts.end = "k/D";
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, 3, NULL, 3, NULL);
+ git_iterator_free(i);
+
+ i_opts.start = "k";
+ i_opts.end = "k/Z";
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, 1, NULL, 1, NULL);
+ git_iterator_free(i);
+
+ /* force case insensitivity */
+ cl_git_pass(git_index_set_caps(index, caps | GIT_INDEX_CAPABILITY_IGNORE_CASE));
+
+ i_opts.start = "c";
+ i_opts.end = "k/D";
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, 5, NULL, 5, NULL);
+ git_iterator_free(i);
+
+ i_opts.start = "k";
+ i_opts.end = "k/Z";
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, 2, NULL, 2, NULL);
+ git_iterator_free(i);
+
+ cl_git_pass(git_index_set_caps(index, caps));
+ git_index_free(index);
+ git_vector_free(&filelist);
+}
+
+void test_iterator_index__pathlist_with_directory(void)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_vector filelist;
+ git_tree *tree;
+ git_index *index;
+
+ g_repo = cl_git_sandbox_init("testrepo2");
+ git_repository_head_tree(&tree, g_repo);
+
+ cl_git_pass(git_vector_init(&filelist, 100, &git__strcmp_cb));
+ cl_git_pass(git_vector_insert(&filelist, "subdir"));
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, 4, NULL, 4, NULL);
+ git_iterator_free(i);
+
+ git_index_free(index);
+ git_tree_free(tree);
+ git_vector_free(&filelist);
+}
+
+static void create_paths(git_index *index, const char *root, int depth)
+{
+ git_str fullpath = GIT_STR_INIT;
+ git_index_entry entry;
+ size_t root_len;
+ int i;
+
+ if (root) {
+ cl_git_pass(git_str_puts(&fullpath, root));
+ cl_git_pass(git_str_putc(&fullpath, '/'));
+ }
+
+ root_len = fullpath.size;
+
+ for (i = 0; i < 8; i++) {
+ bool file = (depth == 0 || (i % 2) == 0);
+ git_str_truncate(&fullpath, root_len);
+ cl_git_pass(git_str_printf(&fullpath, "item%d", i));
+
+ if (file) {
+ memset(&entry, 0, sizeof(git_index_entry));
+ entry.path = fullpath.ptr;
+ entry.mode = GIT_FILEMODE_BLOB;
+ git_oid__fromstr(&entry.id, "d44e18fb93b7107b5cd1b95d601591d77869a1b6", GIT_OID_SHA1);
+
+ cl_git_pass(git_index_add(index, &entry));
+ } else if (depth > 0) {
+ create_paths(index, fullpath.ptr, (depth - 1));
+ }
+ }
+
+ git_str_dispose(&fullpath);
+}
+
+void test_iterator_index__pathlist_for_deeply_nested_item(void)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_index *index;
+ git_vector filelist;
+
+ cl_git_pass(git_vector_init(&filelist, 5, NULL));
+
+ g_repo = cl_git_sandbox_init("icase");
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ create_paths(index, NULL, 3);
+
+ /* Ensure that we find the single path we're interested in */
+ {
+ const char *expected[] = { "item1/item3/item5/item7" };
+ size_t expected_len = 1;
+
+ git_vector_clear(&filelist);
+ cl_git_pass(git_vector_insert(&filelist, "item1/item3/item5/item7"));
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ {
+ const char *expected[] = {
+ "item1/item3/item5/item0", "item1/item3/item5/item1",
+ "item1/item3/item5/item2", "item1/item3/item5/item3",
+ "item1/item3/item5/item4", "item1/item3/item5/item5",
+ "item1/item3/item5/item6", "item1/item3/item5/item7",
+ };
+ size_t expected_len = 8;
+
+ git_vector_clear(&filelist);
+ cl_git_pass(git_vector_insert(&filelist, "item1/item3/item5/"));
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ {
+ const char *expected[] = {
+ "item1/item3/item0",
+ "item1/item3/item1/item0", "item1/item3/item1/item1",
+ "item1/item3/item1/item2", "item1/item3/item1/item3",
+ "item1/item3/item1/item4", "item1/item3/item1/item5",
+ "item1/item3/item1/item6", "item1/item3/item1/item7",
+ "item1/item3/item2",
+ "item1/item3/item3/item0", "item1/item3/item3/item1",
+ "item1/item3/item3/item2", "item1/item3/item3/item3",
+ "item1/item3/item3/item4", "item1/item3/item3/item5",
+ "item1/item3/item3/item6", "item1/item3/item3/item7",
+ "item1/item3/item4",
+ "item1/item3/item5/item0", "item1/item3/item5/item1",
+ "item1/item3/item5/item2", "item1/item3/item5/item3",
+ "item1/item3/item5/item4", "item1/item3/item5/item5",
+ "item1/item3/item5/item6", "item1/item3/item5/item7",
+ "item1/item3/item6",
+ "item1/item3/item7/item0", "item1/item3/item7/item1",
+ "item1/item3/item7/item2", "item1/item3/item7/item3",
+ "item1/item3/item7/item4", "item1/item3/item7/item5",
+ "item1/item3/item7/item6", "item1/item3/item7/item7",
+ };
+ size_t expected_len = 36;
+
+ git_vector_clear(&filelist);
+ cl_git_pass(git_vector_insert(&filelist, "item1/item3/"));
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ /* Ensure that we find the single path we're interested in, and we find
+ * it efficiently, and don't stat the entire world to get there.
+ */
+ {
+ const char *expected[] = {
+ "item0", "item1/item2", "item5/item7/item4", "item6",
+ "item7/item3/item1/item6" };
+ size_t expected_len = 5;
+
+ git_vector_clear(&filelist);
+ cl_git_pass(git_vector_insert(&filelist, "item7/item3/item1/item6"));
+ cl_git_pass(git_vector_insert(&filelist, "item6"));
+ cl_git_pass(git_vector_insert(&filelist, "item5/item7/item4"));
+ cl_git_pass(git_vector_insert(&filelist, "item1/item2"));
+ cl_git_pass(git_vector_insert(&filelist, "item0"));
+
+ /* also add some things that don't exist or don't match the right type */
+ cl_git_pass(git_vector_insert(&filelist, "item2/"));
+ cl_git_pass(git_vector_insert(&filelist, "itemN"));
+ cl_git_pass(git_vector_insert(&filelist, "item1/itemA"));
+ cl_git_pass(git_vector_insert(&filelist, "item5/item3/item4/"));
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ git_index_free(index);
+ git_vector_free(&filelist);
+}
+
+void test_iterator_index__advance_over(void)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_index *index;
+
+ i_opts.flags |= GIT_ITERATOR_DONT_IGNORE_CASE |
+ GIT_ITERATOR_DONT_AUTOEXPAND;
+
+ g_repo = cl_git_sandbox_init("icase");
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ create_paths(index, NULL, 1);
+
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+
+ expect_advance_over(i, "B", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "D", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "F", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "H", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "J", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "L/", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "a", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "c", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "e", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "g", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "i", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "item0", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "item1/", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "item2", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "item3/", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "item4", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "item5/", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "item6", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "item7/", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "k/", GIT_ITERATOR_STATUS_NORMAL);
+
+ cl_git_fail_with(GIT_ITEROVER, git_iterator_advance(NULL, i));
+ git_iterator_free(i);
+ git_index_free(index);
+}
+
+void test_iterator_index__advance_into(void)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_index *index;
+
+ g_repo = cl_git_sandbox_init("icase");
+
+ i_opts.flags |= GIT_ITERATOR_DONT_IGNORE_CASE |
+ GIT_ITERATOR_DONT_AUTOEXPAND;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_advance_into(i, "B");
+ expect_advance_into(i, "D");
+ expect_advance_into(i, "F");
+ expect_advance_into(i, "H");
+ expect_advance_into(i, "J");
+ expect_advance_into(i, "L/");
+ expect_advance_into(i, "L/1");
+ expect_advance_into(i, "L/B");
+ expect_advance_into(i, "L/D");
+ expect_advance_into(i, "L/a");
+ expect_advance_into(i, "L/c");
+ expect_advance_into(i, "a");
+ expect_advance_into(i, "c");
+ expect_advance_into(i, "e");
+ expect_advance_into(i, "g");
+ expect_advance_into(i, "i");
+ expect_advance_into(i, "k/");
+ expect_advance_into(i, "k/1");
+ expect_advance_into(i, "k/B");
+ expect_advance_into(i, "k/D");
+ expect_advance_into(i, "k/a");
+ expect_advance_into(i, "k/c");
+
+ cl_git_fail_with(GIT_ITEROVER, git_iterator_advance(NULL, i));
+ git_iterator_free(i);
+ git_index_free(index);
+}
+
+void test_iterator_index__advance_into_and_over(void)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_index *index;
+
+ g_repo = cl_git_sandbox_init("icase");
+
+ i_opts.flags |= GIT_ITERATOR_DONT_IGNORE_CASE |
+ GIT_ITERATOR_DONT_AUTOEXPAND;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ create_paths(index, NULL, 2);
+
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+ expect_advance_into(i, "B");
+ expect_advance_into(i, "D");
+ expect_advance_into(i, "F");
+ expect_advance_into(i, "H");
+ expect_advance_into(i, "J");
+ expect_advance_into(i, "L/");
+ expect_advance_into(i, "L/1");
+ expect_advance_into(i, "L/B");
+ expect_advance_into(i, "L/D");
+ expect_advance_into(i, "L/a");
+ expect_advance_into(i, "L/c");
+ expect_advance_into(i, "a");
+ expect_advance_into(i, "c");
+ expect_advance_into(i, "e");
+ expect_advance_into(i, "g");
+ expect_advance_into(i, "i");
+ expect_advance_into(i, "item0");
+ expect_advance_into(i, "item1/");
+ expect_advance_into(i, "item1/item0");
+ expect_advance_into(i, "item1/item1/");
+ expect_advance_into(i, "item1/item1/item0");
+ expect_advance_into(i, "item1/item1/item1");
+ expect_advance_into(i, "item1/item1/item2");
+ expect_advance_into(i, "item1/item1/item3");
+ expect_advance_into(i, "item1/item1/item4");
+ expect_advance_into(i, "item1/item1/item5");
+ expect_advance_into(i, "item1/item1/item6");
+ expect_advance_into(i, "item1/item1/item7");
+ expect_advance_into(i, "item1/item2");
+ expect_advance_over(i, "item1/item3/", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "item1/item4", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "item1/item5/", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "item1/item6", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "item1/item7/", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_into(i, "item2");
+ expect_advance_over(i, "item3/", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "item4", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "item5/", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "item6", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "item7/", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_into(i, "k/");
+ expect_advance_into(i, "k/1");
+ expect_advance_into(i, "k/B");
+ expect_advance_into(i, "k/D");
+ expect_advance_into(i, "k/a");
+ expect_advance_into(i, "k/c");
+
+ cl_git_fail_with(GIT_ITEROVER, git_iterator_advance(NULL, i));
+ git_iterator_free(i);
+ git_index_free(index);
+}
+
+static void add_conflict(
+ git_index *index,
+ const char *ancestor_path,
+ const char *our_path,
+ const char *their_path)
+{
+ git_index_entry ancestor = {{0}}, ours = {{0}}, theirs = {{0}};
+
+ ancestor.path = ancestor_path;
+ ancestor.mode = GIT_FILEMODE_BLOB;
+ git_oid__fromstr(&ancestor.id, "d44e18fb93b7107b5cd1b95d601591d77869a1b6", GIT_OID_SHA1);
+ GIT_INDEX_ENTRY_STAGE_SET(&ancestor, 1);
+
+ ours.path = our_path;
+ ours.mode = GIT_FILEMODE_BLOB;
+ git_oid__fromstr(&ours.id, "d44e18fb93b7107b5cd1b95d601591d77869a1b6", GIT_OID_SHA1);
+ GIT_INDEX_ENTRY_STAGE_SET(&ours, 2);
+
+ theirs.path = their_path;
+ theirs.mode = GIT_FILEMODE_BLOB;
+ git_oid__fromstr(&theirs.id, "d44e18fb93b7107b5cd1b95d601591d77869a1b6", GIT_OID_SHA1);
+ GIT_INDEX_ENTRY_STAGE_SET(&theirs, 3);
+
+ cl_git_pass(git_index_conflict_add(index, &ancestor, &ours, &theirs));
+}
+
+void test_iterator_index__include_conflicts(void)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_index *index;
+
+ i_opts.flags |= GIT_ITERATOR_DONT_IGNORE_CASE |
+ GIT_ITERATOR_DONT_AUTOEXPAND;
+
+ g_repo = cl_git_sandbox_init("icase");
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ add_conflict(index, "CONFLICT1", "CONFLICT1" ,"CONFLICT1");
+ add_conflict(index, "ZZZ-CONFLICT2.ancestor", "ZZZ-CONFLICT2.ours", "ZZZ-CONFLICT2.theirs");
+ add_conflict(index, "ancestor.conflict3", "ours.conflict3", "theirs.conflict3");
+ add_conflict(index, "zzz-conflict4", "zzz-conflict4", "zzz-conflict4");
+
+ /* Iterate the index, ensuring that conflicts are not included */
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+
+ expect_advance_over(i, "B", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "D", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "F", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "H", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "J", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "L/", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "a", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "c", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "e", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "g", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "i", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "k/", GIT_ITERATOR_STATUS_NORMAL);
+
+ cl_git_fail_with(GIT_ITEROVER, git_iterator_advance(NULL, i));
+ git_iterator_free(i);
+
+ /* Try again, returning conflicts */
+ i_opts.flags |= GIT_ITERATOR_INCLUDE_CONFLICTS;
+
+ cl_git_pass(git_iterator_for_index(&i, g_repo, index, &i_opts));
+
+ expect_advance_over(i, "B", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "CONFLICT1", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "CONFLICT1", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "CONFLICT1", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "D", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "F", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "H", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "J", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "L/", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "ZZZ-CONFLICT2.ancestor", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "ZZZ-CONFLICT2.ours", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "ZZZ-CONFLICT2.theirs", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "a", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "ancestor.conflict3", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "c", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "e", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "g", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "i", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "k/", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "ours.conflict3", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "theirs.conflict3", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "zzz-conflict4", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "zzz-conflict4", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "zzz-conflict4", GIT_ITERATOR_STATUS_NORMAL);
+
+ cl_git_fail_with(GIT_ITEROVER, git_iterator_advance(NULL, i));
+ git_iterator_free(i);
+
+ git_index_free(index);
+}
diff --git a/tests/libgit2/iterator/iterator_helpers.c b/tests/libgit2/iterator/iterator_helpers.c
new file mode 100644
index 0000000..b210dbb
--- /dev/null
+++ b/tests/libgit2/iterator/iterator_helpers.c
@@ -0,0 +1,143 @@
+#include "clar_libgit2.h"
+#include "iterator.h"
+#include "repository.h"
+#include "futils.h"
+#include "iterator_helpers.h"
+#include <stdarg.h>
+
+static void assert_at_end(git_iterator *i, bool verbose)
+{
+ const git_index_entry *end;
+ int error = git_iterator_advance(&end, i);
+
+ if (verbose && error != GIT_ITEROVER)
+ fprintf(stderr, "Expected end of iterator, got '%s'\n", end->path);
+
+ cl_git_fail_with(GIT_ITEROVER, error);
+}
+
+void expect_iterator_items(
+ git_iterator *i,
+ size_t expected_flat,
+ const char **expected_flat_paths,
+ size_t expected_total,
+ const char **expected_total_paths)
+{
+ const git_index_entry *entry;
+ size_t count;
+ int no_trees = !(git_iterator_flags(i) & GIT_ITERATOR_INCLUDE_TREES);
+ bool v = false;
+ int error;
+
+ if (v) fprintf(stderr, "== %s ==\n", no_trees ? "notrees" : "trees");
+
+ count = 0;
+
+ while (!git_iterator_advance(&entry, i)) {
+ if (v) fprintf(stderr, " %s %07o\n", entry->path, (int)entry->mode);
+
+ if (no_trees)
+ cl_assert(entry->mode != GIT_FILEMODE_TREE);
+
+ if (expected_flat_paths) {
+ const char *expect_path = expected_flat_paths[count];
+ size_t expect_len = strlen(expect_path);
+
+ cl_assert_equal_s(expect_path, entry->path);
+
+ if (expect_path[expect_len - 1] == '/')
+ cl_assert_equal_i(GIT_FILEMODE_TREE, entry->mode);
+ else
+ cl_assert(entry->mode != GIT_FILEMODE_TREE);
+ }
+
+ cl_assert(++count <= expected_flat);
+ }
+
+ assert_at_end(i, v);
+ cl_assert_equal_i(expected_flat, count);
+
+ cl_git_pass(git_iterator_reset(i));
+
+ count = 0;
+ cl_git_pass(git_iterator_current(&entry, i));
+
+ if (v) fprintf(stderr, "-- %s --\n", no_trees ? "notrees" : "trees");
+
+ while (entry != NULL) {
+ if (v) fprintf(stderr, " %s %07o\n", entry->path, (int)entry->mode);
+
+ if (no_trees)
+ cl_assert(entry->mode != GIT_FILEMODE_TREE);
+
+ if (expected_total_paths) {
+ const char *expect_path = expected_total_paths[count];
+ size_t expect_len = strlen(expect_path);
+
+ cl_assert_equal_s(expect_path, entry->path);
+
+ if (expect_path[expect_len - 1] == '/')
+ cl_assert_equal_i(GIT_FILEMODE_TREE, entry->mode);
+ else
+ cl_assert(entry->mode != GIT_FILEMODE_TREE);
+ }
+
+ if (entry->mode == GIT_FILEMODE_TREE) {
+ error = git_iterator_advance_into(&entry, i);
+
+ /* could return NOTFOUND if directory is empty */
+ cl_assert(!error || error == GIT_ENOTFOUND);
+
+ if (error == GIT_ENOTFOUND) {
+ error = git_iterator_advance(&entry, i);
+ cl_assert(!error || error == GIT_ITEROVER);
+ }
+ } else {
+ error = git_iterator_advance(&entry, i);
+ cl_assert(!error || error == GIT_ITEROVER);
+ }
+
+ if (++count >= expected_total)
+ break;
+ }
+
+ assert_at_end(i, v);
+ cl_assert_equal_i(expected_total, count);
+}
+
+
+void expect_advance_over(
+ git_iterator *i,
+ const char *expected_path,
+ git_iterator_status_t expected_status)
+{
+ const git_index_entry *entry;
+ git_iterator_status_t status;
+ int error;
+
+ cl_git_pass(git_iterator_current(&entry, i));
+ cl_assert_equal_s(expected_path, entry->path);
+
+ error = git_iterator_advance_over(&entry, &status, i);
+ cl_assert(!error || error == GIT_ITEROVER);
+ cl_assert_equal_i(expected_status, status);
+}
+
+void expect_advance_into(
+ git_iterator *i,
+ const char *expected_path)
+{
+ const git_index_entry *entry;
+ int error;
+
+ cl_git_pass(git_iterator_current(&entry, i));
+ cl_assert_equal_s(expected_path, entry->path);
+
+ if (S_ISDIR(entry->mode))
+ error = git_iterator_advance_into(&entry, i);
+ else
+ error = git_iterator_advance(&entry, i);
+
+ cl_assert(!error || error == GIT_ITEROVER);
+}
+
diff --git a/tests/libgit2/iterator/iterator_helpers.h b/tests/libgit2/iterator/iterator_helpers.h
new file mode 100644
index 0000000..1884b41
--- /dev/null
+++ b/tests/libgit2/iterator/iterator_helpers.h
@@ -0,0 +1,16 @@
+
+extern void expect_iterator_items(
+ git_iterator *i,
+ size_t expected_flat,
+ const char **expected_flat_paths,
+ size_t expected_total,
+ const char **expected_total_paths);
+
+extern void expect_advance_over(
+ git_iterator *i,
+ const char *expected_path,
+ git_iterator_status_t expected_status);
+
+void expect_advance_into(
+ git_iterator *i,
+ const char *expected_path);
diff --git a/tests/libgit2/iterator/tree.c b/tests/libgit2/iterator/tree.c
new file mode 100644
index 0000000..7dfee28
--- /dev/null
+++ b/tests/libgit2/iterator/tree.c
@@ -0,0 +1,1080 @@
+#include "clar_libgit2.h"
+#include "iterator.h"
+#include "repository.h"
+#include "futils.h"
+#include "tree.h"
+#include "../submodule/submodule_helpers.h"
+#include "../diff/diff_helpers.h"
+#include "iterator_helpers.h"
+#include <stdarg.h>
+
+static git_repository *g_repo;
+
+void test_iterator_tree__initialize(void)
+{
+}
+
+void test_iterator_tree__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ g_repo = NULL;
+}
+
+static void tree_iterator_test(
+ const char *sandbox,
+ const char *treeish,
+ const char *start,
+ const char *end,
+ int expected_count,
+ const char **expected_values)
+{
+ git_tree *t;
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+ const git_index_entry *entry;
+ int error, count = 0, count_post_reset = 0;
+
+ g_repo = cl_git_sandbox_init(sandbox);
+
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+ i_opts.start = start;
+ i_opts.end = end;
+
+ cl_assert(t = resolve_commit_oid_to_tree(g_repo, treeish));
+ cl_git_pass(git_iterator_for_tree(&i, t, &i_opts));
+
+ /* test loop */
+ while (!(error = git_iterator_advance(&entry, i))) {
+ cl_assert(entry);
+ if (expected_values != NULL)
+ cl_assert_equal_s(expected_values[count], entry->path);
+ count++;
+ }
+ cl_assert_equal_i(GIT_ITEROVER, error);
+ cl_assert(!entry);
+ cl_assert_equal_i(expected_count, count);
+
+ /* test reset */
+ cl_git_pass(git_iterator_reset(i));
+
+ while (!(error = git_iterator_advance(&entry, i))) {
+ cl_assert(entry);
+ if (expected_values != NULL)
+ cl_assert_equal_s(expected_values[count_post_reset], entry->path);
+ count_post_reset++;
+ }
+ cl_assert_equal_i(GIT_ITEROVER, error);
+ cl_assert(!entry);
+ cl_assert_equal_i(count, count_post_reset);
+
+ git_iterator_free(i);
+ git_tree_free(t);
+}
+
+/* results of: git ls-tree -r --name-only 605812a */
+const char *expected_tree_0[] = {
+ ".gitattributes",
+ "attr0",
+ "attr1",
+ "attr2",
+ "attr3",
+ "binfile",
+ "macro_test",
+ "root_test1",
+ "root_test2",
+ "root_test3",
+ "root_test4.txt",
+ "subdir/.gitattributes",
+ "subdir/abc",
+ "subdir/subdir_test1",
+ "subdir/subdir_test2.txt",
+ "subdir2/subdir2_test1",
+ NULL
+};
+
+void test_iterator_tree__0(void)
+{
+ tree_iterator_test("attr", "605812a", NULL, NULL, 16, expected_tree_0);
+}
+
+/* results of: git ls-tree -r --name-only 6bab5c79 */
+const char *expected_tree_1[] = {
+ ".gitattributes",
+ "attr0",
+ "attr1",
+ "attr2",
+ "attr3",
+ "root_test1",
+ "root_test2",
+ "root_test3",
+ "root_test4.txt",
+ "subdir/.gitattributes",
+ "subdir/subdir_test1",
+ "subdir/subdir_test2.txt",
+ "subdir2/subdir2_test1",
+ NULL
+};
+
+void test_iterator_tree__1(void)
+{
+ tree_iterator_test("attr", "6bab5c79cd5", NULL, NULL, 13, expected_tree_1);
+}
+
+/* results of: git ls-tree -r --name-only 26a125ee1 */
+const char *expected_tree_2[] = {
+ "current_file",
+ "file_deleted",
+ "modified_file",
+ "staged_changes",
+ "staged_changes_file_deleted",
+ "staged_changes_modified_file",
+ "staged_delete_file_deleted",
+ "staged_delete_modified_file",
+ "subdir.txt",
+ "subdir/current_file",
+ "subdir/deleted_file",
+ "subdir/modified_file",
+ NULL
+};
+
+void test_iterator_tree__2(void)
+{
+ tree_iterator_test("status", "26a125ee1", NULL, NULL, 12, expected_tree_2);
+}
+
+/* $ git ls-tree -r --name-only 0017bd4ab1e */
+const char *expected_tree_3[] = {
+ "current_file",
+ "file_deleted",
+ "modified_file",
+ "staged_changes",
+ "staged_changes_file_deleted",
+ "staged_changes_modified_file",
+ "staged_delete_file_deleted",
+ "staged_delete_modified_file"
+};
+
+void test_iterator_tree__3(void)
+{
+ tree_iterator_test("status", "0017bd4ab1e", NULL, NULL, 8, expected_tree_3);
+}
+
+/* $ git ls-tree -r --name-only 24fa9a9fc4e202313e24b648087495441dab432b */
+const char *expected_tree_4[] = {
+ "attr0",
+ "attr1",
+ "attr2",
+ "attr3",
+ "binfile",
+ "gitattributes",
+ "macro_bad",
+ "macro_test",
+ "root_test1",
+ "root_test2",
+ "root_test3",
+ "root_test4.txt",
+ "sub/abc",
+ "sub/file",
+ "sub/sub/file",
+ "sub/sub/subsub.txt",
+ "sub/subdir_test1",
+ "sub/subdir_test2.txt",
+ "subdir/.gitattributes",
+ "subdir/abc",
+ "subdir/subdir_test1",
+ "subdir/subdir_test2.txt",
+ "subdir2/subdir2_test1",
+ NULL
+};
+
+void test_iterator_tree__4(void)
+{
+ tree_iterator_test(
+ "attr", "24fa9a9fc4e202313e24b648087495441dab432b", NULL, NULL,
+ 23, expected_tree_4);
+}
+
+void test_iterator_tree__4_ranged(void)
+{
+ tree_iterator_test(
+ "attr", "24fa9a9fc4e202313e24b648087495441dab432b",
+ "sub", "sub",
+ 11, &expected_tree_4[12]);
+}
+
+const char *expected_tree_ranged_0[] = {
+ "gitattributes",
+ "macro_bad",
+ "macro_test",
+ "root_test1",
+ "root_test2",
+ "root_test3",
+ "root_test4.txt",
+ NULL
+};
+
+void test_iterator_tree__ranged_0(void)
+{
+ tree_iterator_test(
+ "attr", "24fa9a9fc4e202313e24b648087495441dab432b",
+ "git", "root",
+ 7, expected_tree_ranged_0);
+}
+
+const char *expected_tree_ranged_1[] = {
+ "sub/subdir_test2.txt",
+ NULL
+};
+
+void test_iterator_tree__ranged_1(void)
+{
+ tree_iterator_test(
+ "attr", "24fa9a9fc4e202313e24b648087495441dab432b",
+ "sub/subdir_test2.txt", "sub/subdir_test2.txt",
+ 1, expected_tree_ranged_1);
+}
+
+void test_iterator_tree__range_empty_0(void)
+{
+ tree_iterator_test(
+ "attr", "24fa9a9fc4e202313e24b648087495441dab432b",
+ "empty", "empty", 0, NULL);
+}
+
+void test_iterator_tree__range_empty_1(void)
+{
+ tree_iterator_test(
+ "attr", "24fa9a9fc4e202313e24b648087495441dab432b",
+ "z_empty_after", NULL, 0, NULL);
+}
+
+void test_iterator_tree__range_empty_2(void)
+{
+ tree_iterator_test(
+ "attr", "24fa9a9fc4e202313e24b648087495441dab432b",
+ NULL, ".aaa_empty_before", 0, NULL);
+}
+
+static void check_tree_entry(
+ git_iterator *i,
+ const char *oid,
+ const char *oid_p,
+ const char *oid_pp,
+ const char *oid_ppp)
+{
+ const git_index_entry *ie;
+ const git_tree_entry *te;
+ const git_tree *tree;
+
+ cl_git_pass(git_iterator_current_tree_entry(&te, i));
+ cl_assert(te);
+ cl_assert(git_oid_streq(&te->oid, oid) == 0);
+
+ cl_git_pass(git_iterator_current(&ie, i));
+
+ if (oid_p) {
+ cl_git_pass(git_iterator_current_parent_tree(&tree, i, 0));
+ cl_assert(tree);
+ cl_assert(git_oid_streq(git_tree_id(tree), oid_p) == 0);
+ }
+
+ if (oid_pp) {
+ cl_git_pass(git_iterator_current_parent_tree(&tree, i, 1));
+ cl_assert(tree);
+ cl_assert(git_oid_streq(git_tree_id(tree), oid_pp) == 0);
+ }
+
+ if (oid_ppp) {
+ cl_git_pass(git_iterator_current_parent_tree(&tree, i, 2));
+ cl_assert(tree);
+ cl_assert(git_oid_streq(git_tree_id(tree), oid_ppp) == 0);
+ }
+}
+
+void test_iterator_tree__special_functions(void)
+{
+ git_tree *t;
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+ const git_index_entry *entry;
+ int error, cases = 0;
+ const char *rootoid = "ce39a97a7fb1fa90bcf5e711249c1e507476ae0e";
+
+ g_repo = cl_git_sandbox_init("attr");
+
+ t = resolve_commit_oid_to_tree(
+ g_repo, "24fa9a9fc4e202313e24b648087495441dab432b");
+ cl_assert(t != NULL);
+
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_tree(&i, t, &i_opts));
+
+ while (!(error = git_iterator_advance(&entry, i))) {
+ cl_assert(entry);
+
+ if (strcmp(entry->path, "sub/file") == 0) {
+ cases++;
+ check_tree_entry(
+ i, "45b983be36b73c0788dc9cbcb76cbb80fc7bb057",
+ "ecb97df2a174987475ac816e3847fc8e9f6c596b",
+ rootoid, NULL);
+ }
+ else if (strcmp(entry->path, "sub/sub/subsub.txt") == 0) {
+ cases++;
+ check_tree_entry(
+ i, "9e5bdc47d6a80f2be0ea3049ad74231b94609242",
+ "4e49ba8c5b6c32ff28cd9dcb60be34df50fcc485",
+ "ecb97df2a174987475ac816e3847fc8e9f6c596b", rootoid);
+ }
+ else if (strcmp(entry->path, "subdir/.gitattributes") == 0) {
+ cases++;
+ check_tree_entry(
+ i, "99eae476896f4907224978b88e5ecaa6c5bb67a9",
+ "9fb40b6675dde60b5697afceae91b66d908c02d9",
+ rootoid, NULL);
+ }
+ else if (strcmp(entry->path, "subdir2/subdir2_test1") == 0) {
+ cases++;
+ check_tree_entry(
+ i, "dccada462d3df8ac6de596fb8c896aba9344f941",
+ "2929de282ce999e95183aedac6451d3384559c4b",
+ rootoid, NULL);
+ }
+ }
+ cl_assert_equal_i(GIT_ITEROVER, error);
+ cl_assert(!entry);
+ cl_assert_equal_i(4, cases);
+
+ git_iterator_free(i);
+ git_tree_free(t);
+}
+
+static void check_tree_range(
+ git_repository *repo,
+ const char *start,
+ const char *end,
+ bool ignore_case,
+ int expected_count)
+{
+ git_tree *head;
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+ int error, count;
+
+ i_opts.flags = ignore_case ? GIT_ITERATOR_IGNORE_CASE : GIT_ITERATOR_DONT_IGNORE_CASE;
+ i_opts.start = start;
+ i_opts.end = end;
+
+ cl_git_pass(git_repository_head_tree(&head, repo));
+
+ cl_git_pass(git_iterator_for_tree(&i, head, &i_opts));
+
+ for (count = 0; !(error = git_iterator_advance(NULL, i)); ++count)
+ /* count em up */;
+
+ cl_assert_equal_i(GIT_ITEROVER, error);
+ cl_assert_equal_i(expected_count, count);
+
+ git_iterator_free(i);
+ git_tree_free(head);
+}
+
+void test_iterator_tree__range_icase(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+
+ check_tree_range(g_repo, "B", "C", false, 0);
+ check_tree_range(g_repo, "B", "C", true, 1);
+ check_tree_range(g_repo, "b", "c", false, 1);
+ check_tree_range(g_repo, "b", "c", true, 1);
+
+ check_tree_range(g_repo, "a", "z", false, 3);
+ check_tree_range(g_repo, "a", "z", true, 4);
+ check_tree_range(g_repo, "A", "Z", false, 1);
+ check_tree_range(g_repo, "A", "Z", true, 4);
+ check_tree_range(g_repo, "a", "Z", false, 0);
+ check_tree_range(g_repo, "a", "Z", true, 4);
+ check_tree_range(g_repo, "A", "z", false, 4);
+ check_tree_range(g_repo, "A", "z", true, 4);
+
+ check_tree_range(g_repo, "new.txt", "new.txt", true, 1);
+ check_tree_range(g_repo, "new.txt", "new.txt", false, 1);
+ check_tree_range(g_repo, "README", "README", true, 1);
+ check_tree_range(g_repo, "README", "README", false, 1);
+}
+
+void test_iterator_tree__icase_0(void)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_tree *head;
+
+ g_repo = cl_git_sandbox_init("icase");
+
+ cl_git_pass(git_repository_head_tree(&head, g_repo));
+
+ /* auto expand with no tree entries */
+ cl_git_pass(git_iterator_for_tree(&i, head, NULL));
+ expect_iterator_items(i, 20, NULL, 20, NULL);
+ git_iterator_free(i);
+
+ /* auto expand with tree entries */
+ i_opts.flags = GIT_ITERATOR_INCLUDE_TREES;
+
+ cl_git_pass(git_iterator_for_tree(&i, head, &i_opts));
+ expect_iterator_items(i, 22, NULL, 22, NULL);
+ git_iterator_free(i);
+
+ /* no auto expand (implies trees included) */
+ i_opts.flags = GIT_ITERATOR_DONT_AUTOEXPAND;
+
+ cl_git_pass(git_iterator_for_tree(&i, head, &i_opts));
+ expect_iterator_items(i, 12, NULL, 22, NULL);
+ git_iterator_free(i);
+
+ git_tree_free(head);
+}
+
+void test_iterator_tree__icase_1(void)
+{
+ git_iterator *i;
+ git_tree *head;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+
+ g_repo = cl_git_sandbox_init("icase");
+
+ cl_git_pass(git_repository_head_tree(&head, g_repo));
+
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ /* auto expand with no tree entries */
+ i_opts.start = "c";
+ i_opts.end = "k/D";
+ cl_git_pass(git_iterator_for_tree(&i, head, &i_opts));
+ expect_iterator_items(i, 7, NULL, 7, NULL);
+ git_iterator_free(i);
+
+ i_opts.start = "k";
+ i_opts.end = "k/Z";
+ cl_git_pass(git_iterator_for_tree(&i, head, &i_opts));
+ expect_iterator_items(i, 3, NULL, 3, NULL);
+ git_iterator_free(i);
+
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE | GIT_ITERATOR_INCLUDE_TREES;
+
+ /* auto expand with tree entries */
+ i_opts.start = "c";
+ i_opts.end = "k/Z";
+ cl_git_pass(git_iterator_for_tree(&i, head, &i_opts));
+ expect_iterator_items(i, 8, NULL, 8, NULL);
+ git_iterator_free(i);
+
+ i_opts.start = "k";
+ i_opts.end = "k/Z";
+ cl_git_pass(git_iterator_for_tree(&i, head, &i_opts));
+ expect_iterator_items(i, 4, NULL, 4, NULL);
+ git_iterator_free(i);
+
+ /* no auto expand (implies trees included) */
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE | GIT_ITERATOR_DONT_AUTOEXPAND;
+ i_opts.start = "c";
+ i_opts.end = "k/D";
+ cl_git_pass(git_iterator_for_tree(&i, head, &i_opts));
+ expect_iterator_items(i, 5, NULL, 8, NULL);
+ git_iterator_free(i);
+
+ i_opts.start = "k";
+ i_opts.end = "k/D";
+ cl_git_pass(git_iterator_for_tree(&i, head, &i_opts));
+ expect_iterator_items(i, 1, NULL, 4, NULL);
+ git_iterator_free(i);
+
+ /* auto expand with no tree entries */
+ i_opts.flags = GIT_ITERATOR_IGNORE_CASE;
+
+ i_opts.start = "c";
+ i_opts.end = "k/D";
+ cl_git_pass(git_iterator_for_tree(&i, head, &i_opts));
+ expect_iterator_items(i, 13, NULL, 13, NULL);
+ git_iterator_free(i);
+
+ i_opts.start = "k";
+ i_opts.end = "k/Z";
+ cl_git_pass(git_iterator_for_tree(&i, head, &i_opts));
+ expect_iterator_items(i, 5, NULL, 5, NULL);
+ git_iterator_free(i);
+
+ /* auto expand with tree entries */
+ i_opts.flags = GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_INCLUDE_TREES;
+
+ i_opts.start = "c";
+ i_opts.end = "k/D";
+ cl_git_pass(git_iterator_for_tree(&i, head, &i_opts));
+ expect_iterator_items(i, 14, NULL, 14, NULL);
+ git_iterator_free(i);
+
+ i_opts.start = "k";
+ i_opts.end = "k/Z";
+ cl_git_pass(git_iterator_for_tree(&i, head, &i_opts));
+ expect_iterator_items(i, 6, NULL, 6, NULL);
+ git_iterator_free(i);
+
+ /* no auto expand (implies trees included) */
+ i_opts.flags = GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_DONT_AUTOEXPAND;
+
+ i_opts.start = "c";
+ i_opts.end = "k/D";
+ cl_git_pass(git_iterator_for_tree(&i, head, &i_opts));
+ expect_iterator_items(i, 9, NULL, 14, NULL);
+ git_iterator_free(i);
+
+ i_opts.start = "k";
+ i_opts.end = "k/Z";
+ cl_git_pass(git_iterator_for_tree(&i, head, &i_opts));
+ expect_iterator_items(i, 1, NULL, 6, NULL);
+ git_iterator_free(i);
+
+ git_tree_free(head);
+}
+
+void test_iterator_tree__icase_2(void)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_tree *head;
+ static const char *expect_basic[] = {
+ "current_file",
+ "file_deleted",
+ "modified_file",
+ "staged_changes",
+ "staged_changes_file_deleted",
+ "staged_changes_modified_file",
+ "staged_delete_file_deleted",
+ "staged_delete_modified_file",
+ "subdir.txt",
+ "subdir/current_file",
+ "subdir/deleted_file",
+ "subdir/modified_file",
+ NULL,
+ };
+ static const char *expect_trees[] = {
+ "current_file",
+ "file_deleted",
+ "modified_file",
+ "staged_changes",
+ "staged_changes_file_deleted",
+ "staged_changes_modified_file",
+ "staged_delete_file_deleted",
+ "staged_delete_modified_file",
+ "subdir.txt",
+ "subdir/",
+ "subdir/current_file",
+ "subdir/deleted_file",
+ "subdir/modified_file",
+ NULL,
+ };
+ static const char *expect_noauto[] = {
+ "current_file",
+ "file_deleted",
+ "modified_file",
+ "staged_changes",
+ "staged_changes_file_deleted",
+ "staged_changes_modified_file",
+ "staged_delete_file_deleted",
+ "staged_delete_modified_file",
+ "subdir.txt",
+ "subdir/",
+ NULL
+ };
+
+ g_repo = cl_git_sandbox_init("status");
+
+ cl_git_pass(git_repository_head_tree(&head, g_repo));
+
+ /* auto expand with no tree entries */
+ cl_git_pass(git_iterator_for_tree(&i, head, NULL));
+ expect_iterator_items(i, 12, expect_basic, 12, expect_basic);
+ git_iterator_free(i);
+
+ /* auto expand with tree entries */
+ i_opts.flags = GIT_ITERATOR_INCLUDE_TREES;
+
+ cl_git_pass(git_iterator_for_tree(&i, head, &i_opts));
+ expect_iterator_items(i, 13, expect_trees, 13, expect_trees);
+ git_iterator_free(i);
+
+ /* no auto expand (implies trees included) */
+ i_opts.flags = GIT_ITERATOR_DONT_AUTOEXPAND;
+
+ cl_git_pass(git_iterator_for_tree(&i, head, &i_opts));
+ expect_iterator_items(i, 10, expect_noauto, 13, expect_trees);
+ git_iterator_free(i);
+
+ git_tree_free(head);
+}
+
+/* "b=name,t=name", blob_id, tree_id */
+static void build_test_tree(
+ git_oid *out, git_repository *repo, const char *fmt, ...)
+{
+ git_oid *id;
+ git_treebuilder *builder;
+ const char *scan = fmt, *next;
+ char type, delimiter;
+ git_filemode_t mode = GIT_FILEMODE_BLOB;
+ git_str name = GIT_STR_INIT;
+ va_list arglist;
+
+ cl_git_pass(git_treebuilder_new(&builder, repo, NULL)); /* start builder */
+
+ va_start(arglist, fmt);
+ while (*scan) {
+ switch (type = *scan++) {
+ case 't': case 'T': mode = GIT_FILEMODE_TREE; break;
+ case 'b': case 'B': mode = GIT_FILEMODE_BLOB; break;
+ default:
+ cl_assert(type == 't' || type == 'T' || type == 'b' || type == 'B');
+ }
+
+ delimiter = *scan++; /* read and skip delimiter */
+ for (next = scan; *next && *next != delimiter; ++next)
+ /* seek end */;
+ cl_git_pass(git_str_set(&name, scan, (size_t)(next - scan)));
+ for (scan = next; *scan && (*scan == delimiter || *scan == ','); ++scan)
+ /* skip delimiter and optional comma */;
+
+ id = va_arg(arglist, git_oid *);
+
+ cl_git_pass(git_treebuilder_insert(NULL, builder, name.ptr, id, mode));
+ }
+ va_end(arglist);
+
+ cl_git_pass(git_treebuilder_write(out, builder));
+
+ git_treebuilder_free(builder);
+ git_str_dispose(&name);
+}
+
+void test_iterator_tree__case_conflicts_0(void)
+{
+ const char *blob_sha = "d44e18fb93b7107b5cd1b95d601591d77869a1b6";
+ git_tree *tree;
+ git_oid blob_id, biga_id, littlea_id, tree_id;
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+
+ const char *expect_cs[] = {
+ "A/1.file", "A/3.file", "a/2.file", "a/4.file" };
+ const char *expect_ci[] = {
+ "A/1.file", "a/2.file", "A/3.file", "a/4.file" };
+ const char *expect_cs_trees[] = {
+ "A/", "A/1.file", "A/3.file", "a/", "a/2.file", "a/4.file" };
+ const char *expect_ci_trees[] = {
+ "A/", "A/1.file", "a/2.file", "A/3.file", "a/4.file" };
+
+ g_repo = cl_git_sandbox_init("icase");
+
+ cl_git_pass(git_oid__fromstr(&blob_id, blob_sha, GIT_OID_SHA1)); /* lookup blob */
+
+ /* create tree with: A/1.file, A/3.file, a/2.file, a/4.file */
+ build_test_tree(
+ &biga_id, g_repo, "b|1.file|,b|3.file|", &blob_id, &blob_id);
+ build_test_tree(
+ &littlea_id, g_repo, "b|2.file|,b|4.file|", &blob_id, &blob_id);
+ build_test_tree(
+ &tree_id, g_repo, "t|A|,t|a|", &biga_id, &littlea_id);
+
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
+
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+ cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts));
+ expect_iterator_items(i, 4, expect_cs, 4, expect_cs);
+ git_iterator_free(i);
+
+ i_opts.flags = GIT_ITERATOR_IGNORE_CASE;
+ cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts));
+ expect_iterator_items(i, 4, expect_ci, 4, expect_ci);
+ git_iterator_free(i);
+
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE | GIT_ITERATOR_INCLUDE_TREES;
+ cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts));
+ expect_iterator_items(i, 6, expect_cs_trees, 6, expect_cs_trees);
+ git_iterator_free(i);
+
+ i_opts.flags = GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_INCLUDE_TREES;
+ cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts));
+ expect_iterator_items(i, 5, expect_ci_trees, 5, expect_ci_trees);
+ git_iterator_free(i);
+
+ git_tree_free(tree);
+}
+
+void test_iterator_tree__case_conflicts_1(void)
+{
+ const char *blob_sha = "d44e18fb93b7107b5cd1b95d601591d77869a1b6";
+ git_tree *tree;
+ git_oid blob_id, Ab_id, biga_id, littlea_id, tree_id;
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+
+ const char *expect_cs[] = {
+ "A/a", "A/b/1", "A/c", "a/C", "a/a", "a/b" };
+ const char *expect_ci[] = {
+ "A/a", "a/b", "A/b/1", "A/c" };
+ const char *expect_cs_trees[] = {
+ "A/", "A/a", "A/b/", "A/b/1", "A/c", "a/", "a/C", "a/a", "a/b" };
+ const char *expect_ci_trees[] = {
+ "A/", "A/a", "a/b", "A/b/", "A/b/1", "A/c" };
+
+ g_repo = cl_git_sandbox_init("icase");
+
+ cl_git_pass(git_oid__fromstr(&blob_id, blob_sha, GIT_OID_SHA1)); /* lookup blob */
+
+ /* create: A/a A/b/1 A/c a/a a/b a/C */
+ build_test_tree(&Ab_id, g_repo, "b|1|", &blob_id);
+ build_test_tree(
+ &biga_id, g_repo, "b|a|,t|b|,b|c|", &blob_id, &Ab_id, &blob_id);
+ build_test_tree(
+ &littlea_id, g_repo, "b|a|,b|b|,b|C|", &blob_id, &blob_id, &blob_id);
+ build_test_tree(
+ &tree_id, g_repo, "t|A|,t|a|", &biga_id, &littlea_id);
+
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
+
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+ cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts));
+ expect_iterator_items(i, 6, expect_cs, 6, expect_cs);
+ git_iterator_free(i);
+
+ i_opts.flags = GIT_ITERATOR_IGNORE_CASE;
+ cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts));
+ expect_iterator_items(i, 4, expect_ci, 4, expect_ci);
+ git_iterator_free(i);
+
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE | GIT_ITERATOR_INCLUDE_TREES;
+ cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts));
+ expect_iterator_items(i, 9, expect_cs_trees, 9, expect_cs_trees);
+ git_iterator_free(i);
+
+ i_opts.flags = GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_INCLUDE_TREES;
+ cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts));
+ expect_iterator_items(i, 6, expect_ci_trees, 6, expect_ci_trees);
+ git_iterator_free(i);
+
+ git_tree_free(tree);
+}
+
+void test_iterator_tree__case_conflicts_2(void)
+{
+ const char *blob_sha = "d44e18fb93b7107b5cd1b95d601591d77869a1b6";
+ git_tree *tree;
+ git_oid blob_id, d1, d2, c1, c2, b1, b2, a1, a2, tree_id;
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+
+ const char *expect_cs[] = {
+ "A/B/C/D/16", "A/B/C/D/foo", "A/B/C/d/15", "A/B/C/d/FOO",
+ "A/B/c/D/14", "A/B/c/D/foo", "A/B/c/d/13", "A/B/c/d/FOO",
+ "A/b/C/D/12", "A/b/C/D/foo", "A/b/C/d/11", "A/b/C/d/FOO",
+ "A/b/c/D/10", "A/b/c/D/foo", "A/b/c/d/09", "A/b/c/d/FOO",
+ "a/B/C/D/08", "a/B/C/D/foo", "a/B/C/d/07", "a/B/C/d/FOO",
+ "a/B/c/D/06", "a/B/c/D/foo", "a/B/c/d/05", "a/B/c/d/FOO",
+ "a/b/C/D/04", "a/b/C/D/foo", "a/b/C/d/03", "a/b/C/d/FOO",
+ "a/b/c/D/02", "a/b/c/D/foo", "a/b/c/d/01", "a/b/c/d/FOO", };
+ const char *expect_ci[] = {
+ "a/b/c/d/01", "a/b/c/D/02", "a/b/C/d/03", "a/b/C/D/04",
+ "a/B/c/d/05", "a/B/c/D/06", "a/B/C/d/07", "a/B/C/D/08",
+ "A/b/c/d/09", "A/b/c/D/10", "A/b/C/d/11", "A/b/C/D/12",
+ "A/B/c/d/13", "A/B/c/D/14", "A/B/C/d/15", "A/B/C/D/16",
+ "A/B/C/D/foo", };
+ const char *expect_ci_trees[] = {
+ "A/", "A/B/", "A/B/C/", "A/B/C/D/",
+ "a/b/c/d/01", "a/b/c/D/02", "a/b/C/d/03", "a/b/C/D/04",
+ "a/B/c/d/05", "a/B/c/D/06", "a/B/C/d/07", "a/B/C/D/08",
+ "A/b/c/d/09", "A/b/c/D/10", "A/b/C/d/11", "A/b/C/D/12",
+ "A/B/c/d/13", "A/B/c/D/14", "A/B/C/d/15", "A/B/C/D/16",
+ "A/B/C/D/foo", };
+
+ g_repo = cl_git_sandbox_init("icase");
+
+ cl_git_pass(git_oid__fromstr(&blob_id, blob_sha, GIT_OID_SHA1)); /* lookup blob */
+
+ build_test_tree(&d1, g_repo, "b|16|,b|foo|", &blob_id, &blob_id);
+ build_test_tree(&d2, g_repo, "b|15|,b|FOO|", &blob_id, &blob_id);
+ build_test_tree(&c1, g_repo, "t|D|,t|d|", &d1, &d2);
+ build_test_tree(&d1, g_repo, "b|14|,b|foo|", &blob_id, &blob_id);
+ build_test_tree(&d2, g_repo, "b|13|,b|FOO|", &blob_id, &blob_id);
+ build_test_tree(&c2, g_repo, "t|D|,t|d|", &d1, &d2);
+ build_test_tree(&b1, g_repo, "t|C|,t|c|", &c1, &c2);
+
+ build_test_tree(&d1, g_repo, "b|12|,b|foo|", &blob_id, &blob_id);
+ build_test_tree(&d2, g_repo, "b|11|,b|FOO|", &blob_id, &blob_id);
+ build_test_tree(&c1, g_repo, "t|D|,t|d|", &d1, &d2);
+ build_test_tree(&d1, g_repo, "b|10|,b|foo|", &blob_id, &blob_id);
+ build_test_tree(&d2, g_repo, "b|09|,b|FOO|", &blob_id, &blob_id);
+ build_test_tree(&c2, g_repo, "t|D|,t|d|", &d1, &d2);
+ build_test_tree(&b2, g_repo, "t|C|,t|c|", &c1, &c2);
+
+ build_test_tree(&a1, g_repo, "t|B|,t|b|", &b1, &b2);
+
+ build_test_tree(&d1, g_repo, "b|08|,b|foo|", &blob_id, &blob_id);
+ build_test_tree(&d2, g_repo, "b|07|,b|FOO|", &blob_id, &blob_id);
+ build_test_tree(&c1, g_repo, "t|D|,t|d|", &d1, &d2);
+ build_test_tree(&d1, g_repo, "b|06|,b|foo|", &blob_id, &blob_id);
+ build_test_tree(&d2, g_repo, "b|05|,b|FOO|", &blob_id, &blob_id);
+ build_test_tree(&c2, g_repo, "t|D|,t|d|", &d1, &d2);
+ build_test_tree(&b1, g_repo, "t|C|,t|c|", &c1, &c2);
+
+ build_test_tree(&d1, g_repo, "b|04|,b|foo|", &blob_id, &blob_id);
+ build_test_tree(&d2, g_repo, "b|03|,b|FOO|", &blob_id, &blob_id);
+ build_test_tree(&c1, g_repo, "t|D|,t|d|", &d1, &d2);
+ build_test_tree(&d1, g_repo, "b|02|,b|foo|", &blob_id, &blob_id);
+ build_test_tree(&d2, g_repo, "b|01|,b|FOO|", &blob_id, &blob_id);
+ build_test_tree(&c2, g_repo, "t|D|,t|d|", &d1, &d2);
+ build_test_tree(&b2, g_repo, "t|C|,t|c|", &c1, &c2);
+
+ build_test_tree(&a2, g_repo, "t|B|,t|b|", &b1, &b2);
+
+ build_test_tree(&tree_id, g_repo, "t/A/,t/a/", &a1, &a2);
+
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
+
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+ cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts));
+ expect_iterator_items(i, 32, expect_cs, 32, expect_cs);
+ git_iterator_free(i);
+
+ i_opts.flags = GIT_ITERATOR_IGNORE_CASE;
+ cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts));
+ expect_iterator_items(i, 17, expect_ci, 17, expect_ci);
+ git_iterator_free(i);
+
+ i_opts.flags = GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_INCLUDE_TREES;
+ cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts));
+ expect_iterator_items(i, 21, expect_ci_trees, 21, expect_ci_trees);
+ git_iterator_free(i);
+
+ git_tree_free(tree);
+}
+
+void test_iterator_tree__pathlist(void)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_vector filelist;
+ git_tree *tree;
+ bool default_icase;
+ int expect;
+
+ cl_git_pass(git_vector_init(&filelist, 100, &git__strcmp_cb));
+ cl_git_pass(git_vector_insert(&filelist, "a"));
+ cl_git_pass(git_vector_insert(&filelist, "B"));
+ cl_git_pass(git_vector_insert(&filelist, "c"));
+ cl_git_pass(git_vector_insert(&filelist, "D"));
+ cl_git_pass(git_vector_insert(&filelist, "e"));
+ cl_git_pass(git_vector_insert(&filelist, "k.a"));
+ cl_git_pass(git_vector_insert(&filelist, "k.b"));
+ cl_git_pass(git_vector_insert(&filelist, "k/1"));
+ cl_git_pass(git_vector_insert(&filelist, "k/a"));
+ cl_git_pass(git_vector_insert(&filelist, "kZZZZZZZ"));
+ cl_git_pass(git_vector_insert(&filelist, "L/1"));
+
+ g_repo = cl_git_sandbox_init("icase");
+ git_repository_head_tree(&tree, g_repo);
+
+ /* All indexfilelist iterator tests are "autoexpand with no tree entries" */
+ /* In this test we DO NOT force a case on the iterators and verify default behavior. */
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+
+ cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts));
+ expect_iterator_items(i, 8, NULL, 8, NULL);
+ git_iterator_free(i);
+
+ i_opts.start = "c";
+ i_opts.end = NULL;
+ cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts));
+ default_icase = git_iterator_ignore_case(i);
+ /* (c D e k/1 k/a L ==> 6) vs (c e k/1 k/a ==> 4) */
+ expect = ((default_icase) ? 6 : 4);
+ expect_iterator_items(i, expect, NULL, expect, NULL);
+ git_iterator_free(i);
+
+ i_opts.start = NULL;
+ i_opts.end = "e";
+ cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts));
+ default_icase = git_iterator_ignore_case(i);
+ /* (a B c D e ==> 5) vs (B D L/1 a c e ==> 6) */
+ expect = ((default_icase) ? 5 : 6);
+ expect_iterator_items(i, expect, NULL, expect, NULL);
+ git_iterator_free(i);
+
+ git_vector_free(&filelist);
+ git_tree_free(tree);
+}
+
+void test_iterator_tree__pathlist_icase(void)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_vector filelist;
+ git_tree *tree;
+
+ cl_git_pass(git_vector_init(&filelist, 100, &git__strcmp_cb));
+ cl_git_pass(git_vector_insert(&filelist, "a"));
+ cl_git_pass(git_vector_insert(&filelist, "B"));
+ cl_git_pass(git_vector_insert(&filelist, "c"));
+ cl_git_pass(git_vector_insert(&filelist, "D"));
+ cl_git_pass(git_vector_insert(&filelist, "e"));
+ cl_git_pass(git_vector_insert(&filelist, "k.a"));
+ cl_git_pass(git_vector_insert(&filelist, "k.b"));
+ cl_git_pass(git_vector_insert(&filelist, "k/1"));
+ cl_git_pass(git_vector_insert(&filelist, "k/a"));
+ cl_git_pass(git_vector_insert(&filelist, "kZZZZ"));
+ cl_git_pass(git_vector_insert(&filelist, "L/1"));
+
+ g_repo = cl_git_sandbox_init("icase");
+ git_repository_head_tree(&tree, g_repo);
+
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+
+ i_opts.start = "c";
+ i_opts.end = "k/D";
+ cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts));
+ expect_iterator_items(i, 3, NULL, 3, NULL);
+ git_iterator_free(i);
+
+ i_opts.start = "k";
+ i_opts.end = "k/Z";
+ cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts));
+ expect_iterator_items(i, 1, NULL, 1, NULL);
+ git_iterator_free(i);
+
+ i_opts.flags = GIT_ITERATOR_IGNORE_CASE;
+
+ i_opts.start = "c";
+ i_opts.end = "k/D";
+ cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts));
+ expect_iterator_items(i, 5, NULL, 5, NULL);
+ git_iterator_free(i);
+
+ i_opts.start = "k";
+ i_opts.end = "k/Z";
+ cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts));
+ expect_iterator_items(i, 2, NULL, 2, NULL);
+ git_iterator_free(i);
+
+ git_vector_free(&filelist);
+ git_tree_free(tree);
+}
+
+void test_iterator_tree__pathlist_with_directory(void)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_vector filelist;
+ git_tree *tree;
+
+ const char *expected[] = { "subdir/README", "subdir/new.txt",
+ "subdir/subdir2/README", "subdir/subdir2/new.txt" };
+ size_t expected_len = 4;
+
+ const char *expected2[] = { "subdir/subdir2/README", "subdir/subdir2/new.txt" };
+ size_t expected_len2 = 2;
+
+ g_repo = cl_git_sandbox_init("testrepo2");
+ git_repository_head_tree(&tree, g_repo);
+
+ cl_git_pass(git_vector_init(&filelist, 100, &git__strcmp_cb));
+ cl_git_pass(git_vector_insert(&filelist, "subdir"));
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+ i_opts.flags |= GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+
+ git_vector_clear(&filelist);
+ cl_git_pass(git_vector_insert(&filelist, "subdir/"));
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+
+ cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+
+ git_vector_clear(&filelist);
+ cl_git_pass(git_vector_insert(&filelist, "subdir/subdir2"));
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+
+ cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts));
+ expect_iterator_items(i, expected_len2, expected2, expected_len2, expected2);
+ git_iterator_free(i);
+
+ git_tree_free(tree);
+ git_vector_free(&filelist);
+}
+
+void test_iterator_tree__pathlist_with_directory_include_tree_nodes(void)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_vector filelist;
+ git_tree *tree;
+
+ const char *expected[] = { "subdir/", "subdir/README", "subdir/new.txt",
+ "subdir/subdir2/", "subdir/subdir2/README", "subdir/subdir2/new.txt" };
+ size_t expected_len = 6;
+
+ g_repo = cl_git_sandbox_init("testrepo2");
+ git_repository_head_tree(&tree, g_repo);
+
+ cl_git_pass(git_vector_init(&filelist, 100, &git__strcmp_cb));
+ cl_git_pass(git_vector_insert(&filelist, "subdir"));
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+ i_opts.flags |= GIT_ITERATOR_DONT_IGNORE_CASE | GIT_ITERATOR_INCLUDE_TREES;
+
+ cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+
+ git_tree_free(tree);
+ git_vector_free(&filelist);
+}
+
+void test_iterator_tree__pathlist_no_match(void)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_vector filelist;
+ git_tree *tree;
+ const git_index_entry *entry;
+
+ g_repo = cl_git_sandbox_init("testrepo2");
+ git_repository_head_tree(&tree, g_repo);
+
+ cl_git_pass(git_vector_init(&filelist, 100, &git__strcmp_cb));
+ cl_git_pass(git_vector_insert(&filelist, "nonexistent/"));
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+
+ cl_git_pass(git_iterator_for_tree(&i, tree, &i_opts));
+ cl_assert_equal_i(GIT_ITEROVER, git_iterator_current(&entry, i));
+ git_iterator_free(i);
+
+ git_tree_free(tree);
+ git_vector_free(&filelist);
+}
+
diff --git a/tests/libgit2/iterator/workdir.c b/tests/libgit2/iterator/workdir.c
new file mode 100644
index 0000000..7634997
--- /dev/null
+++ b/tests/libgit2/iterator/workdir.c
@@ -0,0 +1,1523 @@
+#include "clar_libgit2.h"
+#include "iterator.h"
+#include "repository.h"
+#include "futils.h"
+#include "../submodule/submodule_helpers.h"
+#include "../merge/merge_helpers.h"
+#include "iterator_helpers.h"
+#include <stdarg.h>
+
+static git_repository *g_repo;
+
+void test_iterator_workdir__initialize(void)
+{
+}
+
+void test_iterator_workdir__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ g_repo = NULL;
+}
+
+static void workdir_iterator_test(
+ const char *sandbox,
+ const char *start,
+ const char *end,
+ int expected_count,
+ int expected_ignores,
+ const char **expected_names,
+ const char *an_ignored_name)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+ const git_index_entry *entry;
+ int error, count = 0, count_all = 0, count_all_post_reset = 0;
+
+ g_repo = cl_git_sandbox_init(sandbox);
+
+ i_opts.flags = GIT_ITERATOR_DONT_AUTOEXPAND;
+ i_opts.start = start;
+ i_opts.end = end;
+
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+
+ error = git_iterator_current(&entry, i);
+ cl_assert((error == 0 && entry != NULL) ||
+ (error == GIT_ITEROVER && entry == NULL));
+
+ while (entry != NULL) {
+ int ignored = git_iterator_current_is_ignored(i);
+
+ if (S_ISDIR(entry->mode)) {
+ cl_git_pass(git_iterator_advance_into(&entry, i));
+ continue;
+ }
+
+ if (expected_names != NULL)
+ cl_assert_equal_s(expected_names[count_all], entry->path);
+
+ if (an_ignored_name && strcmp(an_ignored_name,entry->path)==0)
+ cl_assert(ignored);
+
+ if (!ignored)
+ count++;
+ count_all++;
+
+ error = git_iterator_advance(&entry, i);
+
+ cl_assert((error == 0 && entry != NULL) ||
+ (error == GIT_ITEROVER && entry == NULL));
+ }
+
+ cl_assert_equal_i(expected_count, count);
+ cl_assert_equal_i(expected_count + expected_ignores, count_all);
+
+ cl_git_pass(git_iterator_reset(i));
+
+ error = git_iterator_current(&entry, i);
+ cl_assert((error == 0 && entry != NULL) ||
+ (error == GIT_ITEROVER && entry == NULL));
+
+ while (entry != NULL) {
+ if (S_ISDIR(entry->mode)) {
+ cl_git_pass(git_iterator_advance_into(&entry, i));
+ continue;
+ }
+
+ if (expected_names != NULL)
+ cl_assert_equal_s(
+ expected_names[count_all_post_reset], entry->path);
+ count_all_post_reset++;
+
+ error = git_iterator_advance(&entry, i);
+ cl_assert(error == 0 || error == GIT_ITEROVER);
+ }
+
+ cl_assert_equal_i(count_all, count_all_post_reset);
+
+ git_iterator_free(i);
+}
+
+void test_iterator_workdir__0(void)
+{
+ workdir_iterator_test("attr", NULL, NULL, 24, 5, NULL, "ign");
+}
+
+static const char *status_paths[] = {
+ "current_file",
+ "ignored_file",
+ "modified_file",
+ "new_file",
+ "staged_changes",
+ "staged_changes_modified_file",
+ "staged_delete_modified_file",
+ "staged_new_file",
+ "staged_new_file_modified_file",
+ "subdir.txt",
+ "subdir/current_file",
+ "subdir/modified_file",
+ "subdir/new_file",
+ "\xe8\xbf\x99",
+ NULL
+};
+
+void test_iterator_workdir__1(void)
+{
+ workdir_iterator_test(
+ "status", NULL, NULL, 13, 1, status_paths, "ignored_file");
+}
+
+static const char *status_paths_range_0[] = {
+ "staged_changes",
+ "staged_changes_modified_file",
+ "staged_delete_modified_file",
+ "staged_new_file",
+ "staged_new_file_modified_file",
+ NULL
+};
+
+void test_iterator_workdir__1_ranged_0(void)
+{
+ workdir_iterator_test(
+ "status", "staged", "staged", 5, 0, status_paths_range_0, NULL);
+}
+
+static const char *status_paths_range_1[] = {
+ "modified_file", NULL
+};
+
+void test_iterator_workdir__1_ranged_1(void)
+{
+ workdir_iterator_test(
+ "status", "modified_file", "modified_file",
+ 1, 0, status_paths_range_1, NULL);
+}
+
+static const char *status_paths_range_3[] = {
+ "subdir.txt",
+ "subdir/current_file",
+ "subdir/modified_file",
+ NULL
+};
+
+void test_iterator_workdir__1_ranged_3(void)
+{
+ workdir_iterator_test(
+ "status", "subdir", "subdir/modified_file",
+ 3, 0, status_paths_range_3, NULL);
+}
+
+static const char *status_paths_range_4[] = {
+ "subdir/current_file",
+ "subdir/modified_file",
+ "subdir/new_file",
+ "\xe8\xbf\x99",
+ NULL
+};
+
+void test_iterator_workdir__1_ranged_4(void)
+{
+ workdir_iterator_test(
+ "status", "subdir/", NULL, 4, 0, status_paths_range_4, NULL);
+}
+
+static const char *status_paths_range_5[] = {
+ "subdir/modified_file",
+ NULL
+};
+
+void test_iterator_workdir__1_ranged_5(void)
+{
+ workdir_iterator_test(
+ "status", "subdir/modified_file", "subdir/modified_file",
+ 1, 0, status_paths_range_5, NULL);
+}
+
+void test_iterator_workdir__1_ranged_5_1_ranged_empty_0(void)
+{
+ workdir_iterator_test(
+ "status", "\xff_does_not_exist", NULL,
+ 0, 0, NULL, NULL);
+}
+
+void test_iterator_workdir__1_ranged_empty_1(void)
+{
+ workdir_iterator_test(
+ "status", "empty", "empty",
+ 0, 0, NULL, NULL);
+}
+
+void test_iterator_workdir__1_ranged_empty_2(void)
+{
+ workdir_iterator_test(
+ "status", NULL, "aaaa_empty_before",
+ 0, 0, NULL, NULL);
+}
+
+void test_iterator_workdir__builtin_ignores(void)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+ const git_index_entry *entry;
+ int idx;
+ static struct {
+ const char *path;
+ bool ignored;
+ } expected[] = {
+ { "dir/", true },
+ { "file", false },
+ { "ign", true },
+ { "macro_bad", false },
+ { "macro_test", false },
+ { "root_test1", false },
+ { "root_test2", false },
+ { "root_test3", false },
+ { "root_test4.txt", false },
+ { "sub/", false },
+ { "sub/.gitattributes", false },
+ { "sub/abc", false },
+ { "sub/dir/", true },
+ { "sub/file", false },
+ { "sub/ign/", true },
+ { "sub/sub/", false },
+ { "sub/sub/.gitattributes", false },
+ { "sub/sub/dir", false }, /* file is not actually a dir */
+ { "sub/sub/file", false },
+ { NULL, false }
+ };
+
+ g_repo = cl_git_sandbox_init("attr");
+
+ cl_git_pass(p_mkdir("attr/sub/sub/.git", 0777));
+ cl_git_mkfile("attr/sub/.git", "whatever");
+
+ i_opts.flags = GIT_ITERATOR_DONT_AUTOEXPAND;
+ i_opts.start = "dir";
+ i_opts.end = "sub/sub/file";
+
+ cl_git_pass(git_iterator_for_workdir(
+ &i, g_repo, NULL, NULL, &i_opts));
+ cl_git_pass(git_iterator_current(&entry, i));
+
+ for (idx = 0; entry != NULL; ++idx) {
+ int ignored = git_iterator_current_is_ignored(i);
+
+ cl_assert_equal_s(expected[idx].path, entry->path);
+ cl_assert_(ignored == expected[idx].ignored, expected[idx].path);
+
+ if (!ignored &&
+ (entry->mode == GIT_FILEMODE_TREE ||
+ entry->mode == GIT_FILEMODE_COMMIT))
+ {
+ /* it is possible to advance "into" a submodule */
+ cl_git_pass(git_iterator_advance_into(&entry, i));
+ } else {
+ int error = git_iterator_advance(&entry, i);
+ cl_assert(!error || error == GIT_ITEROVER);
+ }
+ }
+
+ cl_assert(expected[idx].path == NULL);
+
+ git_iterator_free(i);
+}
+
+static void check_wd_first_through_third_range(
+ git_repository *repo, const char *start, const char *end)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+ const git_index_entry *entry;
+ int error, idx;
+ static const char *expected[] = { "FIRST", "second", "THIRD", NULL };
+
+ i_opts.flags = GIT_ITERATOR_IGNORE_CASE;
+ i_opts.start = start;
+ i_opts.end = end;
+
+ cl_git_pass(git_iterator_for_workdir(
+ &i, repo, NULL, NULL, &i_opts));
+ cl_git_pass(git_iterator_current(&entry, i));
+
+ for (idx = 0; entry != NULL; ++idx) {
+ cl_assert_equal_s(expected[idx], entry->path);
+
+ error = git_iterator_advance(&entry, i);
+ cl_assert(!error || error == GIT_ITEROVER);
+ }
+
+ cl_assert(expected[idx] == NULL);
+
+ git_iterator_free(i);
+}
+
+void test_iterator_workdir__handles_icase_range(void)
+{
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+ cl_git_remove_placeholders(git_repository_path(g_repo), "dummy-marker.txt");
+
+ cl_git_mkfile("empty_standard_repo/before", "whatever\n");
+ cl_git_mkfile("empty_standard_repo/FIRST", "whatever\n");
+ cl_git_mkfile("empty_standard_repo/second", "whatever\n");
+ cl_git_mkfile("empty_standard_repo/THIRD", "whatever\n");
+ cl_git_mkfile("empty_standard_repo/zafter", "whatever\n");
+ cl_git_mkfile("empty_standard_repo/Zlast", "whatever\n");
+
+ check_wd_first_through_third_range(g_repo, "first", "third");
+ check_wd_first_through_third_range(g_repo, "FIRST", "THIRD");
+ check_wd_first_through_third_range(g_repo, "first", "THIRD");
+ check_wd_first_through_third_range(g_repo, "FIRST", "third");
+ check_wd_first_through_third_range(g_repo, "FirSt", "tHiRd");
+}
+
+/*
+ * The workdir iterator is like the filesystem iterator, but honors
+ * special git type constructs (ignores, submodules, etc).
+ */
+
+void test_iterator_workdir__icase(void)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+
+ g_repo = cl_git_sandbox_init("icase");
+
+ /* auto expand with no tree entries */
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_iterator_items(i, 20, NULL, 20, NULL);
+ git_iterator_free(i);
+
+ /* auto expand with tree entries */
+ i_opts.flags = GIT_ITERATOR_INCLUDE_TREES;
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_iterator_items(i, 22, NULL, 22, NULL);
+ git_iterator_free(i);
+
+ /* no auto expand (implies trees included) */
+ i_opts.flags = GIT_ITERATOR_DONT_AUTOEXPAND;
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_iterator_items(i, 12, NULL, 22, NULL);
+ git_iterator_free(i);
+}
+
+void test_iterator_workdir__icase_starts_and_ends(void)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+
+ g_repo = cl_git_sandbox_init("icase");
+
+ /* auto expand with no tree entries */
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ i_opts.start = "c";
+ i_opts.end = "k/D";
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_iterator_items(i, 7, NULL, 7, NULL);
+ git_iterator_free(i);
+
+ i_opts.start = "k";
+ i_opts.end = "k/Z";
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_iterator_items(i, 3, NULL, 3, NULL);
+ git_iterator_free(i);
+
+ /* auto expand with tree entries */
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE | GIT_ITERATOR_INCLUDE_TREES;
+
+ i_opts.start = "c";
+ i_opts.end = "k/D";
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_iterator_items(i, 8, NULL, 8, NULL);
+ git_iterator_free(i);
+
+ i_opts.start = "k";
+ i_opts.end = "k/Z";
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_iterator_items(i, 4, NULL, 4, NULL);
+ git_iterator_free(i);
+
+ /* no auto expand (implies trees included) */
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE | GIT_ITERATOR_DONT_AUTOEXPAND;
+
+ i_opts.start = "c";
+ i_opts.end = "k/D";
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_iterator_items(i, 5, NULL, 8, NULL);
+ git_iterator_free(i);
+
+ i_opts.start = "k";
+ i_opts.end = "k/Z";
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_iterator_items(i, 1, NULL, 4, NULL);
+ git_iterator_free(i);
+
+ /* auto expand with no tree entries */
+ i_opts.flags = GIT_ITERATOR_IGNORE_CASE;
+
+ i_opts.start = "c";
+ i_opts.end = "k/D";
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_iterator_items(i, 13, NULL, 13, NULL);
+ git_iterator_free(i);
+
+ i_opts.start = "k";
+ i_opts.end = "k/Z";
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_iterator_items(i, 5, NULL, 5, NULL);
+ git_iterator_free(i);
+
+ /* auto expand with tree entries */
+ i_opts.flags = GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_INCLUDE_TREES;
+
+ i_opts.start = "c";
+ i_opts.end = "k/D";
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_iterator_items(i, 14, NULL, 14, NULL);
+ git_iterator_free(i);
+
+ i_opts.start = "k";
+ i_opts.end = "k/Z";
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_iterator_items(i, 6, NULL, 6, NULL);
+ git_iterator_free(i);
+
+ /* no auto expand (implies trees included) */
+ i_opts.flags = GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_DONT_AUTOEXPAND;
+
+ i_opts.start = "c";
+ i_opts.end = "k/D";
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_iterator_items(i, 9, NULL, 14, NULL);
+ git_iterator_free(i);
+
+ i_opts.start = "k";
+ i_opts.end = "k/Z";
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_iterator_items(i, 1, NULL, 6, NULL);
+ git_iterator_free(i);
+}
+
+static void build_workdir_tree(const char *root, int dirs, int subs)
+{
+ int i, j;
+ char buf[64], sub[80];
+
+ for (i = 0; i < dirs; ++i) {
+ if (i % 2 == 0) {
+ p_snprintf(buf, sizeof(buf), "%s/dir%02d", root, i);
+ cl_git_pass(git_futils_mkdir(buf, 0775, GIT_MKDIR_PATH));
+
+ p_snprintf(buf, sizeof(buf), "%s/dir%02d/file", root, i);
+ cl_git_mkfile(buf, buf);
+ buf[strlen(buf) - 5] = '\0';
+ } else {
+ p_snprintf(buf, sizeof(buf), "%s/DIR%02d", root, i);
+ cl_git_pass(git_futils_mkdir(buf, 0775, GIT_MKDIR_PATH));
+ }
+
+ for (j = 0; j < subs; ++j) {
+ switch (j % 4) {
+ case 0: p_snprintf(sub, sizeof(sub), "%s/sub%02d", buf, j); break;
+ case 1: p_snprintf(sub, sizeof(sub), "%s/sUB%02d", buf, j); break;
+ case 2: p_snprintf(sub, sizeof(sub), "%s/Sub%02d", buf, j); break;
+ case 3: p_snprintf(sub, sizeof(sub), "%s/SUB%02d", buf, j); break;
+ }
+ cl_git_pass(git_futils_mkdir(sub, 0775, GIT_MKDIR_PATH));
+
+ if (j % 2 == 0) {
+ size_t sublen = strlen(sub);
+ memcpy(&sub[sublen], "/file", sizeof("/file"));
+ cl_git_mkfile(sub, sub);
+ sub[sublen] = '\0';
+ }
+ }
+ }
+}
+
+void test_iterator_workdir__depth(void)
+{
+ git_iterator *iter;
+ git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
+
+ g_repo = cl_git_sandbox_init("icase");
+
+ build_workdir_tree("icase", 10, 10);
+ build_workdir_tree("icase/DIR01/sUB01", 50, 0);
+ build_workdir_tree("icase/dir02/sUB01", 50, 0);
+
+ /* auto expand with no tree entries */
+ cl_git_pass(git_iterator_for_workdir(&iter, g_repo, NULL, NULL, &iter_opts));
+ expect_iterator_items(iter, 125, NULL, 125, NULL);
+ git_iterator_free(iter);
+
+ /* auto expand with tree entries (empty dirs silently skipped) */
+ iter_opts.flags = GIT_ITERATOR_INCLUDE_TREES;
+ cl_git_pass(git_iterator_for_workdir(&iter, g_repo, NULL, NULL, &iter_opts));
+ expect_iterator_items(iter, 337, NULL, 337, NULL);
+ git_iterator_free(iter);
+}
+
+/* The filesystem iterator is a workdir iterator without any special
+ * workdir handling capabilities (ignores, submodules, etc).
+ */
+void test_iterator_workdir__filesystem(void)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+
+ static const char *expect_base[] = {
+ "DIR01/Sub02/file",
+ "DIR01/sub00/file",
+ "current_file",
+ "dir00/Sub02/file",
+ "dir00/file",
+ "dir00/sub00/file",
+ "modified_file",
+ "new_file",
+ NULL,
+ };
+ static const char *expect_trees[] = {
+ "DIR01/",
+ "DIR01/SUB03/",
+ "DIR01/Sub02/",
+ "DIR01/Sub02/file",
+ "DIR01/sUB01/",
+ "DIR01/sub00/",
+ "DIR01/sub00/file",
+ "current_file",
+ "dir00/",
+ "dir00/SUB03/",
+ "dir00/Sub02/",
+ "dir00/Sub02/file",
+ "dir00/file",
+ "dir00/sUB01/",
+ "dir00/sub00/",
+ "dir00/sub00/file",
+ "modified_file",
+ "new_file",
+ NULL,
+ };
+ static const char *expect_noauto[] = {
+ "DIR01/",
+ "current_file",
+ "dir00/",
+ "modified_file",
+ "new_file",
+ NULL,
+ };
+
+ g_repo = cl_git_sandbox_init("status");
+
+ build_workdir_tree("status/subdir", 2, 4);
+
+ cl_git_pass(git_iterator_for_filesystem(&i, "status/subdir", NULL));
+ expect_iterator_items(i, 8, expect_base, 8, expect_base);
+ git_iterator_free(i);
+
+ i_opts.flags = GIT_ITERATOR_INCLUDE_TREES;
+ cl_git_pass(git_iterator_for_filesystem(&i, "status/subdir", &i_opts));
+ expect_iterator_items(i, 18, expect_trees, 18, expect_trees);
+ git_iterator_free(i);
+
+ i_opts.flags = GIT_ITERATOR_DONT_AUTOEXPAND;
+ cl_git_pass(git_iterator_for_filesystem(&i, "status/subdir", &i_opts));
+ expect_iterator_items(i, 5, expect_noauto, 18, expect_trees);
+ git_iterator_free(i);
+
+ git__tsort((void **)expect_base, 8, (git__tsort_cmp)git__strcasecmp);
+ git__tsort((void **)expect_trees, 18, (git__tsort_cmp)git__strcasecmp);
+ git__tsort((void **)expect_noauto, 5, (git__tsort_cmp)git__strcasecmp);
+
+ i_opts.flags = GIT_ITERATOR_IGNORE_CASE;
+ cl_git_pass(git_iterator_for_filesystem(&i, "status/subdir", &i_opts));
+ expect_iterator_items(i, 8, expect_base, 8, expect_base);
+ git_iterator_free(i);
+
+ i_opts.flags = GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_INCLUDE_TREES;
+ cl_git_pass(git_iterator_for_filesystem(&i, "status/subdir", &i_opts));
+ expect_iterator_items(i, 18, expect_trees, 18, expect_trees);
+ git_iterator_free(i);
+
+ i_opts.flags = GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_DONT_AUTOEXPAND;
+ cl_git_pass(git_iterator_for_filesystem(&i, "status/subdir", &i_opts));
+ expect_iterator_items(i, 5, expect_noauto, 18, expect_trees);
+ git_iterator_free(i);
+}
+
+void test_iterator_workdir__filesystem2(void)
+{
+ git_iterator *i;
+ static const char *expect_base[] = {
+ "heads/br2",
+ "heads/dir",
+ "heads/executable",
+ "heads/ident",
+ "heads/long-file-name",
+ "heads/master",
+ "heads/merge-conflict",
+ "heads/packed-test",
+ "heads/subtrees",
+ "heads/test",
+ "heads/testrepo-worktree",
+ "symref",
+ "tags/e90810b",
+ "tags/foo/bar",
+ "tags/foo/foo/bar",
+ "tags/point_to_blob",
+ "tags/test",
+ NULL,
+ };
+
+ g_repo = cl_git_sandbox_init("testrepo");
+
+ cl_git_pass(git_iterator_for_filesystem(
+ &i, "testrepo/.git/refs", NULL));
+ expect_iterator_items(i, 17, expect_base, 17, expect_base);
+ git_iterator_free(i);
+}
+
+/*
+ * Lots of empty dirs, or nearly empty ones, make the old workdir
+ * iterator cry. Also, segfault.
+ */
+void test_iterator_workdir__filesystem_gunk(void)
+{
+ git_str parent = GIT_STR_INIT;
+ git_iterator *i;
+ int n;
+
+ if (!cl_is_env_set("GITTEST_INVASIVE_SPEED"))
+ cl_skip();
+
+ g_repo = cl_git_sandbox_init("testrepo");
+
+ for (n = 0; n < 100000; n++) {
+ git_str_clear(&parent);
+ cl_git_pass(git_str_printf(&parent, "%s/refs/heads/foo/%d/subdir", git_repository_path(g_repo), n));
+ cl_git_pass(git_futils_mkdir(parent.ptr, 0775, GIT_MKDIR_PATH));
+ }
+
+ cl_git_pass(git_iterator_for_filesystem(&i, "testrepo/.git/refs", NULL));
+
+ /*
+ * Should only have 17 items, since we're not asking for trees to be
+ * returned. the goal of this test is simply to not crash.
+ */
+ expect_iterator_items(i, 17, NULL, 16, NULL);
+
+ git_iterator_free(i);
+ git_str_dispose(&parent);
+}
+
+void test_iterator_workdir__skips_unreadable_dirs(void)
+{
+ git_iterator *i;
+ const git_index_entry *e;
+
+ if (!cl_is_chmod_supported())
+ return;
+
+#ifndef GIT_WIN32
+ if (geteuid() == 0)
+ cl_skip();
+#endif
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_must_pass(p_mkdir("empty_standard_repo/r", 0777));
+ cl_git_mkfile("empty_standard_repo/r/a", "hello");
+ cl_must_pass(p_mkdir("empty_standard_repo/r/b", 0777));
+ cl_git_mkfile("empty_standard_repo/r/b/problem", "not me");
+ cl_must_pass(p_chmod("empty_standard_repo/r/b", 0000));
+ cl_must_pass(p_mkdir("empty_standard_repo/r/c", 0777));
+ cl_git_mkfile("empty_standard_repo/r/c/foo", "aloha");
+ cl_git_mkfile("empty_standard_repo/r/d", "final");
+
+ cl_git_pass(git_iterator_for_filesystem(
+ &i, "empty_standard_repo/r", NULL));
+
+ cl_git_pass(git_iterator_advance(&e, i)); /* a */
+ cl_assert_equal_s("a", e->path);
+
+ cl_git_pass(git_iterator_advance(&e, i)); /* c/foo */
+ cl_assert_equal_s("c/foo", e->path);
+
+ cl_git_pass(git_iterator_advance(&e, i)); /* d */
+ cl_assert_equal_s("d", e->path);
+
+ cl_must_pass(p_chmod("empty_standard_repo/r/b", 0777));
+
+ cl_assert_equal_i(GIT_ITEROVER, git_iterator_advance(&e, i));
+ git_iterator_free(i);
+}
+
+void test_iterator_workdir__skips_fifos_and_special_files(void)
+{
+#ifndef GIT_WIN32
+ git_iterator *i;
+ const git_index_entry *e;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_must_pass(p_mkdir("empty_standard_repo/dir", 0777));
+ cl_git_mkfile("empty_standard_repo/file", "not me");
+
+ cl_assert(!mkfifo("empty_standard_repo/fifo", 0777));
+ cl_assert(!access("empty_standard_repo/fifo", F_OK));
+
+ i_opts.flags = GIT_ITERATOR_INCLUDE_TREES |
+ GIT_ITERATOR_DONT_AUTOEXPAND;
+
+ cl_git_pass(git_iterator_for_filesystem(
+ &i, "empty_standard_repo", &i_opts));
+
+ cl_git_pass(git_iterator_advance(&e, i)); /* .git */
+ cl_assert(S_ISDIR(e->mode));
+ cl_git_pass(git_iterator_advance(&e, i)); /* dir */
+ cl_assert(S_ISDIR(e->mode));
+ /* skips fifo */
+ cl_git_pass(git_iterator_advance(&e, i)); /* file */
+ cl_assert(S_ISREG(e->mode));
+
+ cl_assert_equal_i(GIT_ITEROVER, git_iterator_advance(&e, i));
+
+ git_iterator_free(i);
+#else
+ cl_skip();
+#endif
+}
+
+void test_iterator_workdir__pathlist(void)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_vector filelist;
+
+ cl_git_pass(git_vector_init(&filelist, 100, NULL));
+ cl_git_pass(git_vector_insert(&filelist, "a"));
+ cl_git_pass(git_vector_insert(&filelist, "B"));
+ cl_git_pass(git_vector_insert(&filelist, "c"));
+ cl_git_pass(git_vector_insert(&filelist, "D"));
+ cl_git_pass(git_vector_insert(&filelist, "e"));
+ cl_git_pass(git_vector_insert(&filelist, "k.a"));
+ cl_git_pass(git_vector_insert(&filelist, "k.b"));
+ cl_git_pass(git_vector_insert(&filelist, "k/1"));
+ cl_git_pass(git_vector_insert(&filelist, "k/a"));
+ cl_git_pass(git_vector_insert(&filelist, "kZZZZZZZ"));
+ cl_git_pass(git_vector_insert(&filelist, "L/1"));
+
+ g_repo = cl_git_sandbox_init("icase");
+
+ /* Test iterators without returning tree entries (but autoexpanding.) */
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+
+ /* Case sensitive */
+ {
+ const char *expected[] = {
+ "B", "D", "L/1", "a", "c", "e", "k/1", "k/a" };
+ size_t expected_len = 8;
+
+ i_opts.start = NULL;
+ i_opts.end = NULL;
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ /* Case INsensitive */
+ {
+ const char *expected[] = {
+ "a", "B", "c", "D", "e", "k/1", "k/a", "L/1" };
+ size_t expected_len = 8;
+
+ i_opts.start = NULL;
+ i_opts.end = NULL;
+ i_opts.flags = GIT_ITERATOR_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ /* Set a start, but no end. Case sensitive. */
+ {
+ const char *expected[] = { "c", "e", "k/1", "k/a" };
+ size_t expected_len = 4;
+
+ i_opts.start = "c";
+ i_opts.end = NULL;
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ /* Set a start, but no end. Case INsensitive. */
+ {
+ const char *expected[] = { "c", "D", "e", "k/1", "k/a", "L/1" };
+ size_t expected_len = 6;
+
+ i_opts.start = "c";
+ i_opts.end = NULL;
+ i_opts.flags = GIT_ITERATOR_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ /* Set no start, but an end. Case sensitive. */
+ {
+ const char *expected[] = { "B", "D", "L/1", "a", "c", "e" };
+ size_t expected_len = 6;
+
+ i_opts.start = NULL;
+ i_opts.end = "e";
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ /* Set no start, but an end. Case INsensitive. */
+ {
+ const char *expected[] = { "a", "B", "c", "D", "e" };
+ size_t expected_len = 5;
+
+ i_opts.start = NULL;
+ i_opts.end = "e";
+ i_opts.flags = GIT_ITERATOR_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ /* Start and an end, case sensitive */
+ {
+ const char *expected[] = { "c", "e", "k/1" };
+ size_t expected_len = 3;
+
+ i_opts.start = "c";
+ i_opts.end = "k/D";
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ /* Start and an end, case sensitive */
+ {
+ const char *expected[] = { "k/1" };
+ size_t expected_len = 1;
+
+ i_opts.start = "k";
+ i_opts.end = "k/D";
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ /* Start and an end, case INsensitive */
+ {
+ const char *expected[] = { "c", "D", "e", "k/1", "k/a" };
+ size_t expected_len = 5;
+
+ i_opts.start = "c";
+ i_opts.end = "k/D";
+ i_opts.flags = GIT_ITERATOR_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ /* Start and an end, case INsensitive */
+ {
+ const char *expected[] = { "k/1", "k/a" };
+ size_t expected_len = 2;
+
+ i_opts.start = "k";
+ i_opts.end = "k/D";
+ i_opts.flags = GIT_ITERATOR_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ git_vector_free(&filelist);
+}
+
+void test_iterator_workdir__pathlist_with_dirs(void)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_vector filelist;
+
+ cl_git_pass(git_vector_init(&filelist, 5, NULL));
+
+ g_repo = cl_git_sandbox_init("icase");
+
+ /* Test that a prefix `k` matches folders, even without trailing slash */
+ {
+ const char *expected[] = { "k/1", "k/B", "k/D", "k/a", "k/c" };
+ size_t expected_len = 5;
+
+ git_vector_clear(&filelist);
+ cl_git_pass(git_vector_insert(&filelist, "k"));
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ /* Test that a `k/` matches a folder */
+ {
+ const char *expected[] = { "k/1", "k/B", "k/D", "k/a", "k/c" };
+ size_t expected_len = 5;
+
+ git_vector_clear(&filelist);
+ cl_git_pass(git_vector_insert(&filelist, "k/"));
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ /* When the iterator is case sensitive, ensure we can't lookup the
+ * directory with the wrong case.
+ */
+ {
+ git_vector_clear(&filelist);
+ cl_git_pass(git_vector_insert(&filelist, "K/"));
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ cl_git_fail_with(GIT_ITEROVER, git_iterator_advance(NULL, i));
+ git_iterator_free(i);
+ }
+
+ /* Test that case insensitive matching works. */
+ {
+ const char *expected[] = { "k/1", "k/a", "k/B", "k/c", "k/D" };
+ size_t expected_len = 5;
+
+ git_vector_clear(&filelist);
+ cl_git_pass(git_vector_insert(&filelist, "K/"));
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+ i_opts.flags = GIT_ITERATOR_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ /* Test that case insensitive matching works without trailing slash. */
+ {
+ const char *expected[] = { "k/1", "k/a", "k/B", "k/c", "k/D" };
+ size_t expected_len = 5;
+
+ git_vector_clear(&filelist);
+ cl_git_pass(git_vector_insert(&filelist, "K"));
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+ i_opts.flags = GIT_ITERATOR_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ git_vector_free(&filelist);
+}
+
+static void create_paths(const char *root, int depth)
+{
+ git_str fullpath = GIT_STR_INIT;
+ size_t root_len;
+ int i;
+
+ cl_git_pass(git_str_puts(&fullpath, root));
+ cl_git_pass(git_fs_path_to_dir(&fullpath));
+
+ root_len = fullpath.size;
+
+ for (i = 0; i < 8; i++) {
+ bool file = (depth == 0 || (i % 2) == 0);
+ git_str_truncate(&fullpath, root_len);
+ cl_git_pass(git_str_printf(&fullpath, "item%d", i));
+
+ if (file) {
+ cl_git_rewritefile(fullpath.ptr, "This is a file!\n");
+ } else {
+ cl_must_pass(p_mkdir(fullpath.ptr, 0777));
+
+ if (depth > 0)
+ create_paths(fullpath.ptr, (depth - 1));
+ }
+ }
+
+ git_str_dispose(&fullpath);
+}
+
+void test_iterator_workdir__pathlist_for_deeply_nested_item(void)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_vector filelist;
+
+ cl_git_pass(git_vector_init(&filelist, 5, NULL));
+
+ g_repo = cl_git_sandbox_init("icase");
+ create_paths(git_repository_workdir(g_repo), 3);
+
+ /* Ensure that we find the single path we're interested in, and we find
+ * it efficiently, and don't stat the entire world to get there.
+ */
+ {
+ const char *expected[] = { "item1/item3/item5/item7" };
+ size_t expected_len = 1;
+
+ git_vector_clear(&filelist);
+ cl_git_pass(git_vector_insert(&filelist, "item1/item3/item5/item7"));
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ cl_assert_equal_i(4, i->stat_calls);
+ git_iterator_free(i);
+ }
+
+ /* Ensure that we find the single path we're interested in, and we find
+ * it efficiently, and don't stat the entire world to get there.
+ */
+ {
+ const char *expected[] = {
+ "item1/item3/item5/item0", "item1/item3/item5/item1",
+ "item1/item3/item5/item2", "item1/item3/item5/item3",
+ "item1/item3/item5/item4", "item1/item3/item5/item5",
+ "item1/item3/item5/item6", "item1/item3/item5/item7",
+ };
+ size_t expected_len = 8;
+
+ git_vector_clear(&filelist);
+ cl_git_pass(git_vector_insert(&filelist, "item1/item3/item5/"));
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ cl_assert_equal_i(11, i->stat_calls);
+ git_iterator_free(i);
+ }
+
+ /* Ensure that we find the single path we're interested in, and we find
+ * it efficiently, and don't stat the entire world to get there.
+ */
+ {
+ const char *expected[] = {
+ "item1/item3/item0",
+ "item1/item3/item1/item0", "item1/item3/item1/item1",
+ "item1/item3/item1/item2", "item1/item3/item1/item3",
+ "item1/item3/item1/item4", "item1/item3/item1/item5",
+ "item1/item3/item1/item6", "item1/item3/item1/item7",
+ "item1/item3/item2",
+ "item1/item3/item3/item0", "item1/item3/item3/item1",
+ "item1/item3/item3/item2", "item1/item3/item3/item3",
+ "item1/item3/item3/item4", "item1/item3/item3/item5",
+ "item1/item3/item3/item6", "item1/item3/item3/item7",
+ "item1/item3/item4",
+ "item1/item3/item5/item0", "item1/item3/item5/item1",
+ "item1/item3/item5/item2", "item1/item3/item5/item3",
+ "item1/item3/item5/item4", "item1/item3/item5/item5",
+ "item1/item3/item5/item6", "item1/item3/item5/item7",
+ "item1/item3/item6",
+ "item1/item3/item7/item0", "item1/item3/item7/item1",
+ "item1/item3/item7/item2", "item1/item3/item7/item3",
+ "item1/item3/item7/item4", "item1/item3/item7/item5",
+ "item1/item3/item7/item6", "item1/item3/item7/item7",
+ };
+ size_t expected_len = 36;
+
+ git_vector_clear(&filelist);
+ cl_git_pass(git_vector_insert(&filelist, "item1/item3/"));
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ cl_assert_equal_i(42, i->stat_calls);
+ git_iterator_free(i);
+ }
+
+ /* Ensure that we find the single path we're interested in, and we find
+ * it efficiently, and don't stat the entire world to get there.
+ */
+ {
+ const char *expected[] = {
+ "item0", "item1/item2", "item5/item7/item4", "item6",
+ "item7/item3/item1/item6" };
+ size_t expected_len = 5;
+
+ git_vector_clear(&filelist);
+ cl_git_pass(git_vector_insert(&filelist, "item7/item3/item1/item6"));
+ cl_git_pass(git_vector_insert(&filelist, "item6"));
+ cl_git_pass(git_vector_insert(&filelist, "item5/item7/item4"));
+ cl_git_pass(git_vector_insert(&filelist, "item1/item2"));
+ cl_git_pass(git_vector_insert(&filelist, "item0"));
+
+ /* also add some things that don't exist or don't match the right type */
+ cl_git_pass(git_vector_insert(&filelist, "item2/"));
+ cl_git_pass(git_vector_insert(&filelist, "itemN"));
+ cl_git_pass(git_vector_insert(&filelist, "item1/itemA"));
+ cl_git_pass(git_vector_insert(&filelist, "item5/item3/item4/"));
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ cl_assert_equal_i(14, i->stat_calls);
+ git_iterator_free(i);
+ }
+
+ git_vector_free(&filelist);
+}
+
+void test_iterator_workdir__bounded_submodules(void)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_vector filelist;
+ git_index *index;
+ git_tree *head;
+
+ cl_git_pass(git_vector_init(&filelist, 5, NULL));
+
+ g_repo = setup_fixture_submod2();
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_repository_head_tree(&head, g_repo));
+
+ /* Test that a submodule matches */
+ {
+ const char *expected[] = { "sm_changed_head" };
+ size_t expected_len = 1;
+
+ git_vector_clear(&filelist);
+ cl_git_pass(git_vector_insert(&filelist, "sm_changed_head"));
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, index, head, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ /* Test that a submodule still matches when suffixed with a '/' */
+ {
+ const char *expected[] = { "sm_changed_head" };
+ size_t expected_len = 1;
+
+ git_vector_clear(&filelist);
+ cl_git_pass(git_vector_insert(&filelist, "sm_changed_head/"));
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, index, head, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ /* Test that start/end work with a submodule */
+ {
+ const char *expected[] = { "sm_changed_head", "sm_changed_index" };
+ size_t expected_len = 2;
+
+ i_opts.start = "sm_changed_head";
+ i_opts.end = "sm_changed_index";
+ i_opts.pathlist.strings = NULL;
+ i_opts.pathlist.count = 0;
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, index, head, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ /* Test that start and end allow '/' suffixes of submodules */
+ {
+ const char *expected[] = { "sm_changed_head", "sm_changed_index" };
+ size_t expected_len = 2;
+
+ i_opts.start = "sm_changed_head";
+ i_opts.end = "sm_changed_index";
+ i_opts.pathlist.strings = NULL;
+ i_opts.pathlist.count = 0;
+ i_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, index, head, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+ }
+
+ git_vector_free(&filelist);
+ git_index_free(index);
+ git_tree_free(head);
+}
+
+void test_iterator_workdir__advance_over(void)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+
+ i_opts.flags |= GIT_ITERATOR_DONT_IGNORE_CASE |
+ GIT_ITERATOR_DONT_AUTOEXPAND;
+
+ g_repo = cl_git_sandbox_init("icase");
+
+ /* create an empty directory */
+ cl_must_pass(p_mkdir("icase/empty", 0777));
+
+ /* create a directory in which all contents are ignored */
+ cl_must_pass(p_mkdir("icase/all_ignored", 0777));
+ cl_git_rewritefile("icase/all_ignored/one", "This is ignored\n");
+ cl_git_rewritefile("icase/all_ignored/two", "This, too, is ignored\n");
+ cl_git_rewritefile("icase/all_ignored/.gitignore", ".gitignore\none\ntwo\n");
+
+ /* create a directory in which not all contents are ignored */
+ cl_must_pass(p_mkdir("icase/some_ignored", 0777));
+ cl_git_rewritefile("icase/some_ignored/one", "This is ignored\n");
+ cl_git_rewritefile("icase/some_ignored/two", "This is not ignored\n");
+ cl_git_rewritefile("icase/some_ignored/.gitignore", ".gitignore\none\n");
+
+ /* create a directory which has some empty children */
+ cl_must_pass(p_mkdir("icase/empty_children", 0777));
+ cl_must_pass(p_mkdir("icase/empty_children/empty1", 0777));
+ cl_must_pass(p_mkdir("icase/empty_children/empty2", 0777));
+ cl_must_pass(p_mkdir("icase/empty_children/empty3", 0777));
+
+ /* create a directory which will disappear! */
+ cl_must_pass(p_mkdir("icase/missing_directory", 0777));
+
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+
+ cl_must_pass(p_rmdir("icase/missing_directory"));
+
+ expect_advance_over(i, "B", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "D", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "F", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "H", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "J", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "L/", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "a", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "all_ignored/", GIT_ITERATOR_STATUS_IGNORED);
+ expect_advance_over(i, "c", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "e", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "empty/", GIT_ITERATOR_STATUS_EMPTY);
+ expect_advance_over(i, "empty_children/", GIT_ITERATOR_STATUS_EMPTY);
+ expect_advance_over(i, "g", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "i", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "k/", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "missing_directory/", GIT_ITERATOR_STATUS_EMPTY);
+ expect_advance_over(i, "some_ignored/", GIT_ITERATOR_STATUS_NORMAL);
+
+ cl_git_fail_with(GIT_ITEROVER, git_iterator_advance(NULL, i));
+ git_iterator_free(i);
+}
+
+void test_iterator_workdir__advance_over_with_pathlist(void)
+{
+ git_vector pathlist = GIT_VECTOR_INIT;
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+
+ git_vector_insert(&pathlist, "dirA/subdir1/subdir2/file");
+ git_vector_insert(&pathlist, "dirB/subdir1/subdir2");
+ git_vector_insert(&pathlist, "dirC/subdir1/nonexistent");
+ git_vector_insert(&pathlist, "dirD/subdir1/nonexistent");
+ git_vector_insert(&pathlist, "dirD/subdir1/subdir2");
+ git_vector_insert(&pathlist, "dirD/nonexistent");
+
+ i_opts.pathlist.strings = (char **)pathlist.contents;
+ i_opts.pathlist.count = pathlist.length;
+ i_opts.flags |= GIT_ITERATOR_DONT_IGNORE_CASE |
+ GIT_ITERATOR_DONT_AUTOEXPAND;
+
+ g_repo = cl_git_sandbox_init("icase");
+
+ /* Create a directory that has a file that is included in our pathlist */
+ cl_must_pass(p_mkdir("icase/dirA", 0777));
+ cl_must_pass(p_mkdir("icase/dirA/subdir1", 0777));
+ cl_must_pass(p_mkdir("icase/dirA/subdir1/subdir2", 0777));
+ cl_git_rewritefile("icase/dirA/subdir1/subdir2/file", "foo!");
+
+ /* Create a directory that has a directory that is included in our pathlist */
+ cl_must_pass(p_mkdir("icase/dirB", 0777));
+ cl_must_pass(p_mkdir("icase/dirB/subdir1", 0777));
+ cl_must_pass(p_mkdir("icase/dirB/subdir1/subdir2", 0777));
+ cl_git_rewritefile("icase/dirB/subdir1/subdir2/file", "foo!");
+
+ /* Create a directory that would contain an entry in our pathlist, but
+ * that entry does not actually exist. We don't know this until we
+ * advance_over it. We want to distinguish this from an actually empty
+ * or ignored directory.
+ */
+ cl_must_pass(p_mkdir("icase/dirC", 0777));
+ cl_must_pass(p_mkdir("icase/dirC/subdir1", 0777));
+ cl_must_pass(p_mkdir("icase/dirC/subdir1/subdir2", 0777));
+ cl_git_rewritefile("icase/dirC/subdir1/subdir2/file", "foo!");
+
+ /* Create a directory that has a mix of actual and nonexistent paths */
+ cl_must_pass(p_mkdir("icase/dirD", 0777));
+ cl_must_pass(p_mkdir("icase/dirD/subdir1", 0777));
+ cl_must_pass(p_mkdir("icase/dirD/subdir1/subdir2", 0777));
+ cl_git_rewritefile("icase/dirD/subdir1/subdir2/file", "foo!");
+
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+
+ expect_advance_over(i, "dirA/", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "dirB/", GIT_ITERATOR_STATUS_NORMAL);
+ expect_advance_over(i, "dirC/", GIT_ITERATOR_STATUS_FILTERED);
+ expect_advance_over(i, "dirD/", GIT_ITERATOR_STATUS_NORMAL);
+
+ cl_git_fail_with(GIT_ITEROVER, git_iterator_advance(NULL, i));
+ git_iterator_free(i);
+ git_vector_free(&pathlist);
+}
+
+void test_iterator_workdir__advance_into(void)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+
+ g_repo = cl_git_sandbox_init("icase");
+
+ i_opts.flags |= GIT_ITERATOR_DONT_IGNORE_CASE |
+ GIT_ITERATOR_DONT_AUTOEXPAND;
+
+ cl_must_pass(p_mkdir("icase/Empty", 0777));
+
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_advance_into(i, "B");
+ expect_advance_into(i, "D");
+ expect_advance_into(i, "Empty/");
+ expect_advance_into(i, "F");
+ expect_advance_into(i, "H");
+ expect_advance_into(i, "J");
+ expect_advance_into(i, "L/");
+ expect_advance_into(i, "L/1");
+ expect_advance_into(i, "L/B");
+ expect_advance_into(i, "L/D");
+ expect_advance_into(i, "L/a");
+ expect_advance_into(i, "L/c");
+ expect_advance_into(i, "a");
+ expect_advance_into(i, "c");
+ expect_advance_into(i, "e");
+ expect_advance_into(i, "g");
+ expect_advance_into(i, "i");
+ expect_advance_into(i, "k/");
+ expect_advance_into(i, "k/1");
+ expect_advance_into(i, "k/B");
+ expect_advance_into(i, "k/D");
+ expect_advance_into(i, "k/a");
+ expect_advance_into(i, "k/c");
+
+ cl_git_fail_with(GIT_ITEROVER, git_iterator_advance(NULL, i));
+ git_iterator_free(i);
+}
+
+void test_iterator_workdir__pathlist_with_directory(void)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_vector filelist;
+
+ const char *expected[] = { "subdir/README", "subdir/new.txt",
+ "subdir/subdir2/README", "subdir/subdir2/new.txt" };
+ size_t expected_len = 4;
+
+ cl_git_pass(git_vector_init(&filelist, 100, &git__strcmp_cb));
+ cl_git_pass(git_vector_insert(&filelist, "subdir/"));
+
+ g_repo = cl_git_sandbox_init("testrepo2");
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+ i_opts.flags |= GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+
+ git_vector_free(&filelist);
+}
+
+void test_iterator_workdir__pathlist_with_directory_include_trees(void)
+{
+ git_iterator *i;
+ git_iterator_options i_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_vector filelist;
+
+ const char *expected[] = { "subdir/", "subdir/README", "subdir/new.txt",
+ "subdir/subdir2/", "subdir/subdir2/README", "subdir/subdir2/new.txt", };
+ size_t expected_len = 6;
+
+ cl_git_pass(git_vector_init(&filelist, 100, &git__strcmp_cb));
+ cl_git_pass(git_vector_insert(&filelist, "subdir/"));
+
+ g_repo = cl_git_sandbox_init("testrepo2");
+
+ i_opts.pathlist.strings = (char **)filelist.contents;
+ i_opts.pathlist.count = filelist.length;
+ i_opts.flags |= GIT_ITERATOR_DONT_IGNORE_CASE | GIT_ITERATOR_INCLUDE_TREES;
+
+ cl_git_pass(git_iterator_for_workdir(&i, g_repo, NULL, NULL, &i_opts));
+ expect_iterator_items(i, expected_len, expected, expected_len, expected);
+ git_iterator_free(i);
+
+ git_vector_free(&filelist);
+}
+
+void test_iterator_workdir__hash_when_requested(void)
+{
+ git_iterator *iter;
+ const git_index_entry *entry;
+ git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
+ git_oid expected_id = GIT_OID_SHA1_ZERO;
+ size_t i;
+
+ struct merge_index_entry expected[] = {
+ { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "7c7e08f9559d9e1551b91e1cf68f1d0066109add", 0, "oyster.txt" },
+ { 0100644, "898d12687fb35be271c27c795a6b32c8b51da79e", 0, "veal.txt" },
+ };
+
+ g_repo = cl_git_sandbox_init("merge-recursive");
+
+ /* do the iteration normally, ensure there are no hashes */
+ cl_git_pass(git_iterator_for_workdir(&iter, g_repo, NULL, NULL, &iter_opts));
+
+ for (i = 0; i < sizeof(expected) / sizeof(struct merge_index_entry); i++) {
+ cl_git_pass(git_iterator_advance(&entry, iter));
+
+ cl_assert_equal_oid(&expected_id, &entry->id);
+ cl_assert_equal_s(expected[i].path, entry->path);
+ }
+ cl_assert_equal_i(GIT_ITEROVER, git_iterator_advance(&entry, iter));
+ git_iterator_free(iter);
+
+ /* do the iteration requesting hashes */
+ iter_opts.flags |= GIT_ITERATOR_INCLUDE_HASH;
+ cl_git_pass(git_iterator_for_workdir(&iter, g_repo, NULL, NULL, &iter_opts));
+
+ for (i = 0; i < sizeof(expected) / sizeof(struct merge_index_entry); i++) {
+ cl_git_pass(git_iterator_advance(&entry, iter));
+
+ cl_git_pass(git_oid__fromstr(&expected_id, expected[i].oid_str, GIT_OID_SHA1));
+ cl_assert_equal_oid(&expected_id, &entry->id);
+ cl_assert_equal_s(expected[i].path, entry->path);
+ }
+ cl_assert_equal_i(GIT_ITEROVER, git_iterator_advance(&entry, iter));
+ git_iterator_free(iter);
+}
diff --git a/tests/libgit2/mailmap/basic.c b/tests/libgit2/mailmap/basic.c
new file mode 100644
index 0000000..1f8ca56
--- /dev/null
+++ b/tests/libgit2/mailmap/basic.c
@@ -0,0 +1,101 @@
+#include "clar.h"
+#include "clar_libgit2.h"
+
+#include "common.h"
+#include "mailmap.h"
+
+static git_mailmap *mailmap = NULL;
+
+const char TEST_MAILMAP[] =
+ "Foo bar <foo@bar.com> <foo@baz.com> \n"
+ "Blatantly invalid line\n"
+ "Foo bar <foo@bar.com> <foo@bal.com>\n"
+ "<email@foo.com> <otheremail@foo.com>\n"
+ "<email@foo.com> Other Name <yetanotheremail@foo.com>\n";
+
+struct {
+ const char *real_name;
+ const char *real_email;
+ const char *replace_name;
+ const char *replace_email;
+} expected[] = {
+ { "Foo bar", "foo@bar.com", NULL, "foo@baz.com" },
+ { "Foo bar", "foo@bar.com", NULL, "foo@bal.com" },
+ { NULL, "email@foo.com", NULL, "otheremail@foo.com" },
+ { NULL, "email@foo.com", "Other Name", "yetanotheremail@foo.com" }
+};
+
+void test_mailmap_basic__initialize(void)
+{
+ cl_git_pass(git_mailmap_from_buffer(
+ &mailmap, TEST_MAILMAP, strlen(TEST_MAILMAP)));
+}
+
+void test_mailmap_basic__cleanup(void)
+{
+ git_mailmap_free(mailmap);
+ mailmap = NULL;
+}
+
+void test_mailmap_basic__entry(void)
+{
+ size_t idx;
+ const git_mailmap_entry *entry;
+
+ /* Check that we have the expected # of entries */
+ cl_assert_equal_sz(ARRAY_SIZE(expected), git_vector_length(&mailmap->entries));
+
+ for (idx = 0; idx < ARRAY_SIZE(expected); ++idx) {
+ /* Try to look up each entry and make sure they match */
+ entry = git_mailmap_entry_lookup(
+ mailmap, expected[idx].replace_name, expected[idx].replace_email);
+
+ cl_assert(entry);
+ cl_assert_equal_s(entry->real_name, expected[idx].real_name);
+ cl_assert_equal_s(entry->real_email, expected[idx].real_email);
+ cl_assert_equal_s(entry->replace_name, expected[idx].replace_name);
+ cl_assert_equal_s(entry->replace_email, expected[idx].replace_email);
+ }
+}
+
+void test_mailmap_basic__lookup_not_found(void)
+{
+ const git_mailmap_entry *entry = git_mailmap_entry_lookup(
+ mailmap, "Whoever", "doesnotexist@fo.com");
+ cl_assert(!entry);
+}
+
+void test_mailmap_basic__lookup(void)
+{
+ const git_mailmap_entry *entry = git_mailmap_entry_lookup(
+ mailmap, "Typoed the name once", "foo@baz.com");
+ cl_assert(entry);
+ cl_assert_equal_s(entry->real_name, "Foo bar");
+}
+
+void test_mailmap_basic__empty_email_query(void)
+{
+ const char *name;
+ const char *email;
+ cl_git_pass(git_mailmap_resolve(
+ &name, &email, mailmap, "Author name", "otheremail@foo.com"));
+ cl_assert_equal_s(name, "Author name");
+ cl_assert_equal_s(email, "email@foo.com");
+}
+
+void test_mailmap_basic__name_matching(void)
+{
+ const char *name;
+ const char *email;
+ cl_git_pass(git_mailmap_resolve(
+ &name, &email, mailmap, "Other Name", "yetanotheremail@foo.com"));
+
+ cl_assert_equal_s(name, "Other Name");
+ cl_assert_equal_s(email, "email@foo.com");
+
+ cl_git_pass(git_mailmap_resolve(
+ &name, &email, mailmap,
+ "Other Name That Doesn't Match", "yetanotheremail@foo.com"));
+ cl_assert_equal_s(name, "Other Name That Doesn't Match");
+ cl_assert_equal_s(email, "yetanotheremail@foo.com");
+}
diff --git a/tests/libgit2/mailmap/blame.c b/tests/libgit2/mailmap/blame.c
new file mode 100644
index 0000000..e6bc1a4
--- /dev/null
+++ b/tests/libgit2/mailmap/blame.c
@@ -0,0 +1,64 @@
+#include "clar_libgit2.h"
+#include "git2/repository.h"
+#include "git2/blame.h"
+#include "mailmap.h"
+#include "mailmap_testdata.h"
+
+static git_repository *g_repo;
+static git_blame *g_blame;
+
+void test_mailmap_blame__initialize(void)
+{
+ g_repo = NULL;
+ g_blame = NULL;
+}
+
+void test_mailmap_blame__cleanup(void)
+{
+ git_blame_free(g_blame);
+ cl_git_sandbox_cleanup();
+}
+
+void test_mailmap_blame__hunks(void)
+{
+ size_t idx = 0;
+ const git_blame_hunk *hunk = NULL;
+ git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+
+ g_repo = cl_git_sandbox_init("mailmap");
+
+ opts.flags |= GIT_BLAME_USE_MAILMAP;
+
+ cl_git_pass(git_blame_file(&g_blame, g_repo, "file.txt", &opts));
+ cl_assert(g_blame);
+
+ for (idx = 0; idx < ARRAY_SIZE(resolved); ++idx) {
+ hunk = git_blame_get_hunk_byline(g_blame, idx + 1);
+
+ cl_assert(hunk->final_signature != NULL);
+ cl_assert(hunk->orig_signature != NULL);
+ cl_assert_equal_s(hunk->final_signature->name, resolved[idx].real_name);
+ cl_assert_equal_s(hunk->final_signature->email, resolved[idx].real_email);
+ }
+}
+
+void test_mailmap_blame__hunks_no_mailmap(void)
+{
+ size_t idx = 0;
+ const git_blame_hunk *hunk = NULL;
+ git_blame_options opts = GIT_BLAME_OPTIONS_INIT;
+
+ g_repo = cl_git_sandbox_init("mailmap");
+
+ cl_git_pass(git_blame_file(&g_blame, g_repo, "file.txt", &opts));
+ cl_assert(g_blame);
+
+ for (idx = 0; idx < ARRAY_SIZE(resolved); ++idx) {
+ hunk = git_blame_get_hunk_byline(g_blame, idx + 1);
+
+ cl_assert(hunk->final_signature != NULL);
+ cl_assert(hunk->orig_signature != NULL);
+ cl_assert_equal_s(hunk->final_signature->name, resolved[idx].replace_name);
+ cl_assert_equal_s(hunk->final_signature->email, resolved[idx].replace_email);
+ }
+}
diff --git a/tests/libgit2/mailmap/mailmap_testdata.h b/tests/libgit2/mailmap/mailmap_testdata.h
new file mode 100644
index 0000000..a06606b
--- /dev/null
+++ b/tests/libgit2/mailmap/mailmap_testdata.h
@@ -0,0 +1,21 @@
+#include "mailmap.h"
+
+typedef struct mailmap_entry {
+ const char *real_name;
+ const char *real_email;
+ const char *replace_name;
+ const char *replace_email;
+} mailmap_entry;
+
+static const mailmap_entry resolved[] = {
+ { "Brad", "cto@company.xx", "Brad", "cto@coompany.xx" },
+ { "Brad L", "cto@company.xx", "Brad L", "cto@coompany.xx" },
+ { "Some Dude", "some@dude.xx", "nick1", "bugs@company.xx" },
+ { "Other Author", "other@author.xx", "nick2", "bugs@company.xx" },
+ { "nick3", "bugs@company.xx", "nick3", "bugs@company.xx" },
+ { "Other Author", "other@author.xx", "Some Garbage", "nick2@company.xx" },
+ { "Phil Hill", "phil@company.xx", "unknown", "phil@company.xx" },
+ { "Joseph", "joseph@company.xx", "Joseph", "bugs@company.xx" },
+ { "Santa Claus", "santa.claus@northpole.xx", "Clause", "me@company.xx" },
+ { "Charles", "charles@charles.xx", "Charles", "charles@charles.xx" }
+};
diff --git a/tests/libgit2/mailmap/parsing.c b/tests/libgit2/mailmap/parsing.c
new file mode 100644
index 0000000..5ea470f
--- /dev/null
+++ b/tests/libgit2/mailmap/parsing.c
@@ -0,0 +1,269 @@
+#include "clar_libgit2.h"
+#include "repository.h"
+#include "git2/sys/repository.h"
+#include "mailmap_testdata.h"
+
+static git_repository *g_repo;
+static git_mailmap *g_mailmap;
+static git_config *g_config;
+
+static const char string_mailmap[] =
+ "# Simple Comment line\n"
+ "<cto@company.xx> <cto@coompany.xx>\n"
+ "Some Dude <some@dude.xx> nick1 <bugs@company.xx>\n"
+ "Other Author <other@author.xx> nick2 <bugs@company.xx>\n"
+ "Other Author <other@author.xx> <nick2@company.xx>\n"
+ "Phil Hill <phil@company.xx> # Comment at end of line\n"
+ "<joseph@company.xx> Joseph <bugs@company.xx>\n"
+ "Santa Claus <santa.claus@northpole.xx> <me@company.xx>\n"
+ "Untracked <untracked@company.xx>";
+
+static const mailmap_entry entries[] = {
+ { NULL, "cto@company.xx", NULL, "cto@coompany.xx" },
+ { "Some Dude", "some@dude.xx", "nick1", "bugs@company.xx" },
+ { "Other Author", "other@author.xx", "nick2", "bugs@company.xx" },
+ { "Other Author", "other@author.xx", NULL, "nick2@company.xx" },
+ { "Phil Hill", NULL, NULL, "phil@company.xx" },
+ { NULL, "joseph@company.xx", "Joseph", "bugs@company.xx" },
+ { "Santa Claus", "santa.claus@northpole.xx", NULL, "me@company.xx" },
+ /* This entry isn't in the bare repository */
+ { "Untracked", NULL, NULL, "untracked@company.xx" }
+};
+
+void test_mailmap_parsing__initialize(void)
+{
+ g_repo = NULL;
+ g_mailmap = NULL;
+ g_config = NULL;
+}
+
+void test_mailmap_parsing__cleanup(void)
+{
+ git_mailmap_free(g_mailmap);
+ git_config_free(g_config);
+ cl_git_sandbox_cleanup();
+}
+
+static void check_mailmap_entries(
+ const git_mailmap *mailmap, const mailmap_entry *entries, size_t entries_size)
+{
+ const git_mailmap_entry *parsed;
+ size_t idx;
+
+ /* Check the correct # of entries were parsed */
+ cl_assert_equal_sz(entries_size, git_vector_length(&mailmap->entries));
+
+ /* Make sure looking up each entry succeeds */
+ for (idx = 0; idx < entries_size; ++idx) {
+ parsed = git_mailmap_entry_lookup(
+ mailmap, entries[idx].replace_name, entries[idx].replace_email);
+
+ cl_assert(parsed);
+ cl_assert_equal_s(parsed->real_name, entries[idx].real_name);
+ cl_assert_equal_s(parsed->real_email, entries[idx].real_email);
+ cl_assert_equal_s(parsed->replace_name, entries[idx].replace_name);
+ cl_assert_equal_s(parsed->replace_email, entries[idx].replace_email);
+ }
+}
+
+static void check_mailmap_resolve(
+ const git_mailmap *mailmap, const mailmap_entry *resolved, size_t resolved_size)
+{
+ const char *resolved_name = NULL;
+ const char *resolved_email = NULL;
+ size_t idx;
+
+ /* Check that the resolver behaves correctly */
+ for (idx = 0; idx < resolved_size; ++idx) {
+ cl_git_pass(git_mailmap_resolve(
+ &resolved_name, &resolved_email, mailmap,
+ resolved[idx].replace_name, resolved[idx].replace_email));
+ cl_assert_equal_s(resolved_name, resolved[idx].real_name);
+ cl_assert_equal_s(resolved_email, resolved[idx].real_email);
+ }
+}
+
+static const mailmap_entry resolved_untracked[] = {
+ { "Untracked", "untracked@company.xx", "xx", "untracked@company.xx" }
+};
+
+void test_mailmap_parsing__string(void)
+{
+ cl_git_pass(git_mailmap_from_buffer(
+ &g_mailmap, string_mailmap, strlen(string_mailmap)));
+
+ /* We should have parsed all of the entries */
+ check_mailmap_entries(g_mailmap, entries, ARRAY_SIZE(entries));
+
+ /* Check that resolving the entries works */
+ check_mailmap_resolve(g_mailmap, resolved, ARRAY_SIZE(resolved));
+ check_mailmap_resolve(
+ g_mailmap, resolved_untracked, ARRAY_SIZE(resolved_untracked));
+}
+
+void test_mailmap_parsing__windows_string(void)
+{
+ git_str unixbuf = GIT_STR_INIT;
+ git_str winbuf = GIT_STR_INIT;
+
+ /* Parse with windows-style line endings */
+ git_str_attach_notowned(&unixbuf, string_mailmap, strlen(string_mailmap));
+ cl_git_pass(git_str_lf_to_crlf(&winbuf, &unixbuf));
+
+ cl_git_pass(git_mailmap_from_buffer(&g_mailmap, winbuf.ptr, winbuf.size));
+ git_str_dispose(&winbuf);
+
+ /* We should have parsed all of the entries */
+ check_mailmap_entries(g_mailmap, entries, ARRAY_SIZE(entries));
+
+ /* Check that resolving the entries works */
+ check_mailmap_resolve(g_mailmap, resolved, ARRAY_SIZE(resolved));
+ check_mailmap_resolve(
+ g_mailmap, resolved_untracked, ARRAY_SIZE(resolved_untracked));
+}
+
+void test_mailmap_parsing__fromrepo(void)
+{
+ g_repo = cl_git_sandbox_init("mailmap");
+ cl_check(!git_repository_is_bare(g_repo));
+
+ cl_git_pass(git_mailmap_from_repository(&g_mailmap, g_repo));
+
+ /* We should have parsed all of the entries */
+ check_mailmap_entries(g_mailmap, entries, ARRAY_SIZE(entries));
+
+ /* Check that resolving the entries works */
+ check_mailmap_resolve(g_mailmap, resolved, ARRAY_SIZE(resolved));
+ check_mailmap_resolve(
+ g_mailmap, resolved_untracked, ARRAY_SIZE(resolved_untracked));
+}
+
+static const mailmap_entry resolved_bare[] = {
+ { "xx", "untracked@company.xx", "xx", "untracked@company.xx" }
+};
+
+void test_mailmap_parsing__frombare(void)
+{
+ g_repo = cl_git_sandbox_init("mailmap/.gitted");
+ cl_git_pass(git_repository_set_bare(g_repo));
+ cl_check(git_repository_is_bare(g_repo));
+
+ cl_git_pass(git_mailmap_from_repository(&g_mailmap, g_repo));
+
+ /* We should have parsed all of the entries, except for the untracked one */
+ check_mailmap_entries(g_mailmap, entries, ARRAY_SIZE(entries) - 1);
+
+ /* Check that resolving the entries works */
+ check_mailmap_resolve(g_mailmap, resolved, ARRAY_SIZE(resolved));
+ check_mailmap_resolve(
+ g_mailmap, resolved_bare, ARRAY_SIZE(resolved_bare));
+}
+
+static const mailmap_entry resolved_with_file_override[] = {
+ { "Brad", "cto@company.xx", "Brad", "cto@coompany.xx" },
+ { "Brad L", "cto@company.xx", "Brad L", "cto@coompany.xx" },
+ { "Some Dude", "some@dude.xx", "nick1", "bugs@company.xx" },
+ { "Other Author", "other@author.xx", "nick2", "bugs@company.xx" },
+ { "nick3", "bugs@company.xx", "nick3", "bugs@company.xx" },
+ { "Other Author", "other@author.xx", "Some Garbage", "nick2@company.xx" },
+ { "Joseph", "joseph@company.xx", "Joseph", "bugs@company.xx" },
+ { "Santa Claus", "santa.claus@northpole.xx", "Clause", "me@company.xx" },
+ { "Charles", "charles@charles.xx", "Charles", "charles@charles.xx" },
+
+ /* This name is overridden by file_override */
+ { "File Override", "phil@company.xx", "unknown", "phil@company.xx" },
+ { "Other Name", "fileoverridename@company.xx", "override", "fileoverridename@company.xx" }
+};
+
+void test_mailmap_parsing__file_config(void)
+{
+ g_repo = cl_git_sandbox_init("mailmap");
+ cl_git_pass(git_repository_config(&g_config, g_repo));
+
+ cl_git_pass(git_config_set_string(
+ g_config, "mailmap.file", cl_fixture("mailmap/file_override")));
+
+ cl_git_pass(git_mailmap_from_repository(&g_mailmap, g_repo));
+
+ /* Check we don't have duplicate entries */
+ cl_assert_equal_sz(git_vector_length(&g_mailmap->entries), 9);
+
+ /* Check that resolving the entries works */
+ check_mailmap_resolve(
+ g_mailmap, resolved_with_file_override,
+ ARRAY_SIZE(resolved_with_file_override));
+}
+
+static const mailmap_entry resolved_with_blob_override[] = {
+ { "Brad", "cto@company.xx", "Brad", "cto@coompany.xx" },
+ { "Brad L", "cto@company.xx", "Brad L", "cto@coompany.xx" },
+ { "Some Dude", "some@dude.xx", "nick1", "bugs@company.xx" },
+ { "Other Author", "other@author.xx", "nick2", "bugs@company.xx" },
+ { "nick3", "bugs@company.xx", "nick3", "bugs@company.xx" },
+ { "Other Author", "other@author.xx", "Some Garbage", "nick2@company.xx" },
+ { "Joseph", "joseph@company.xx", "Joseph", "bugs@company.xx" },
+ { "Santa Claus", "santa.claus@northpole.xx", "Clause", "me@company.xx" },
+ { "Charles", "charles@charles.xx", "Charles", "charles@charles.xx" },
+
+ /* This name is overridden by blob_override */
+ { "Blob Override", "phil@company.xx", "unknown", "phil@company.xx" },
+ { "Other Name", "bloboverridename@company.xx", "override", "bloboverridename@company.xx" }
+};
+
+void test_mailmap_parsing__blob_config(void)
+{
+ g_repo = cl_git_sandbox_init("mailmap");
+ cl_git_pass(git_repository_config(&g_config, g_repo));
+
+ cl_git_pass(git_config_set_string(
+ g_config, "mailmap.blob", "HEAD:blob_override"));
+
+ cl_git_pass(git_mailmap_from_repository(&g_mailmap, g_repo));
+
+ /* Check we don't have duplicate entries */
+ cl_assert_equal_sz(git_vector_length(&g_mailmap->entries), 9);
+
+ /* Check that resolving the entries works */
+ check_mailmap_resolve(
+ g_mailmap, resolved_with_blob_override,
+ ARRAY_SIZE(resolved_with_blob_override));
+}
+
+static const mailmap_entry bare_resolved_with_blob_override[] = {
+ /* As mailmap.blob is set, we won't load HEAD:.mailmap */
+ { "Brad", "cto@coompany.xx", "Brad", "cto@coompany.xx" },
+ { "Brad L", "cto@coompany.xx", "Brad L", "cto@coompany.xx" },
+ { "nick1", "bugs@company.xx", "nick1", "bugs@company.xx" },
+ { "nick2", "bugs@company.xx", "nick2", "bugs@company.xx" },
+ { "nick3", "bugs@company.xx", "nick3", "bugs@company.xx" },
+ { "Some Garbage", "nick2@company.xx", "Some Garbage", "nick2@company.xx" },
+ { "Joseph", "bugs@company.xx", "Joseph", "bugs@company.xx" },
+ { "Clause", "me@company.xx", "Clause", "me@company.xx" },
+ { "Charles", "charles@charles.xx", "Charles", "charles@charles.xx" },
+
+ /* This name is overridden by blob_override */
+ { "Blob Override", "phil@company.xx", "unknown", "phil@company.xx" },
+ { "Other Name", "bloboverridename@company.xx", "override", "bloboverridename@company.xx" }
+};
+
+void test_mailmap_parsing__bare_blob_config(void)
+{
+ g_repo = cl_git_sandbox_init("mailmap/.gitted");
+ cl_git_pass(git_repository_set_bare(g_repo));
+ cl_check(git_repository_is_bare(g_repo));
+
+ cl_git_pass(git_repository_config(&g_config, g_repo));
+
+ cl_git_pass(git_config_set_string(
+ g_config, "mailmap.blob", "HEAD:blob_override"));
+
+ cl_git_pass(git_mailmap_from_repository(&g_mailmap, g_repo));
+
+ /* Check that we only have the 2 entries */
+ cl_assert_equal_sz(git_vector_length(&g_mailmap->entries), 2);
+
+ /* Check that resolving the entries works */
+ check_mailmap_resolve(
+ g_mailmap, bare_resolved_with_blob_override,
+ ARRAY_SIZE(bare_resolved_with_blob_override));
+}
diff --git a/tests/libgit2/merge/analysis.c b/tests/libgit2/merge/analysis.c
new file mode 100644
index 0000000..8c61303
--- /dev/null
+++ b/tests/libgit2/merge/analysis.c
@@ -0,0 +1,184 @@
+/*
+NOTE: this is the implementation for both merge/trees/analysis.c and merge/workdir/analysis.c
+You probably want to make changes to both files.
+*/
+
+#include "clar_libgit2.h"
+#include "git2/repository.h"
+#include "git2/merge.h"
+#include "git2/annotated_commit.h"
+#include "git2/sys/index.h"
+#include "merge.h"
+#include "merge_helpers.h"
+#include "refs.h"
+#include "posix.h"
+
+#define TEST_REPO_PATH "merge-resolve"
+
+#define UPTODATE_BRANCH "master"
+#define PREVIOUS_BRANCH "previous"
+
+#define FASTFORWARD_BRANCH "ff_branch"
+#define FASTFORWARD_ID "fd89f8cffb663ac89095a0f9764902e93ceaca6a"
+
+#define NOFASTFORWARD_BRANCH "branch"
+#define NOFASTFORWARD_ID "7cb63eed597130ba4abb87b3e544b85021905520"
+
+static git_repository *sandbox;
+static git_repository *repo;
+
+void test_merge_analysis__initialize_with_bare_repository(void)
+{
+ sandbox = cl_git_sandbox_init(TEST_REPO_PATH);
+ cl_git_pass(git_repository_open_ext(&repo, git_repository_path(sandbox),
+ GIT_REPOSITORY_OPEN_BARE, NULL));
+}
+
+void test_merge_analysis__initialize_with_nonbare_repository(void)
+{
+ sandbox = cl_git_sandbox_init(TEST_REPO_PATH);
+ cl_git_pass(git_repository_open_ext(&repo, git_repository_workdir(sandbox),
+ 0, NULL));
+}
+
+void test_merge_analysis__cleanup(void)
+{
+ git_repository_free(repo);
+ cl_git_sandbox_cleanup();
+}
+
+static void analysis_from_branch(
+ git_merge_analysis_t *merge_analysis,
+ git_merge_preference_t *merge_pref,
+ const char *our_branchname,
+ const char *their_branchname)
+{
+ git_str our_refname = GIT_STR_INIT;
+ git_str their_refname = GIT_STR_INIT;
+ git_reference *our_ref;
+ git_reference *their_ref;
+ git_annotated_commit *their_head;
+
+ if (our_branchname != NULL) {
+ cl_git_pass(git_str_printf(&our_refname, "%s%s", GIT_REFS_HEADS_DIR, our_branchname));
+ cl_git_pass(git_reference_lookup(&our_ref, repo, git_str_cstr(&our_refname)));
+ } else {
+ cl_git_pass(git_reference_lookup(&our_ref, repo, GIT_HEAD_FILE));
+ }
+
+ cl_git_pass(git_str_printf(&their_refname, "%s%s", GIT_REFS_HEADS_DIR, their_branchname));
+
+ cl_git_pass(git_reference_lookup(&their_ref, repo, git_str_cstr(&their_refname)));
+ cl_git_pass(git_annotated_commit_from_ref(&their_head, repo, their_ref));
+
+ cl_git_pass(git_merge_analysis_for_ref(merge_analysis, merge_pref, repo, our_ref, (const git_annotated_commit **)&their_head, 1));
+
+ git_str_dispose(&our_refname);
+ git_str_dispose(&their_refname);
+ git_annotated_commit_free(their_head);
+ git_reference_free(our_ref);
+ git_reference_free(their_ref);
+}
+
+void test_merge_analysis__fastforward(void)
+{
+ git_merge_analysis_t merge_analysis;
+ git_merge_preference_t merge_pref;
+
+ analysis_from_branch(&merge_analysis, &merge_pref, NULL, FASTFORWARD_BRANCH);
+ cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL|GIT_MERGE_ANALYSIS_FASTFORWARD, merge_analysis);
+}
+
+void test_merge_analysis__no_fastforward(void)
+{
+ git_merge_analysis_t merge_analysis;
+ git_merge_preference_t merge_pref;
+
+ analysis_from_branch(&merge_analysis, &merge_pref, NULL, NOFASTFORWARD_BRANCH);
+ cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, merge_analysis);
+}
+
+void test_merge_analysis__uptodate(void)
+{
+ git_merge_analysis_t merge_analysis;
+ git_merge_preference_t merge_pref;
+
+ analysis_from_branch(&merge_analysis, &merge_pref, NULL, UPTODATE_BRANCH);
+ cl_assert_equal_i(GIT_MERGE_ANALYSIS_UP_TO_DATE, merge_analysis);
+}
+
+void test_merge_analysis__uptodate_merging_prev_commit(void)
+{
+ git_merge_analysis_t merge_analysis;
+ git_merge_preference_t merge_pref;
+
+ analysis_from_branch(&merge_analysis, &merge_pref, NULL, PREVIOUS_BRANCH);
+ cl_assert_equal_i(GIT_MERGE_ANALYSIS_UP_TO_DATE, merge_analysis);
+}
+
+void test_merge_analysis__unborn(void)
+{
+ git_merge_analysis_t merge_analysis;
+ git_merge_preference_t merge_pref;
+ git_str master = GIT_STR_INIT;
+
+ cl_git_pass(git_str_joinpath(&master, git_repository_path(repo), "refs/heads/master"));
+ cl_must_pass(p_unlink(git_str_cstr(&master)));
+
+ analysis_from_branch(&merge_analysis, &merge_pref, NULL, NOFASTFORWARD_BRANCH);
+ cl_assert_equal_i(GIT_MERGE_ANALYSIS_FASTFORWARD|GIT_MERGE_ANALYSIS_UNBORN, merge_analysis);
+
+ git_str_dispose(&master);
+}
+
+void test_merge_analysis__fastforward_with_config_noff(void)
+{
+ git_config *config;
+ git_merge_analysis_t merge_analysis;
+ git_merge_preference_t merge_pref;
+
+ cl_git_pass(git_repository_config(&config, repo));
+ cl_git_pass(git_config_set_string(config, "merge.ff", "false"));
+
+ analysis_from_branch(&merge_analysis, &merge_pref, NULL, FASTFORWARD_BRANCH);
+ cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL|GIT_MERGE_ANALYSIS_FASTFORWARD, merge_analysis);
+
+ cl_assert_equal_i(GIT_MERGE_PREFERENCE_NO_FASTFORWARD, (merge_pref & GIT_MERGE_PREFERENCE_NO_FASTFORWARD));
+
+ git_config_free(config);
+}
+
+void test_merge_analysis__no_fastforward_with_config_ffonly(void)
+{
+ git_config *config;
+ git_merge_analysis_t merge_analysis;
+ git_merge_preference_t merge_pref;
+
+ cl_git_pass(git_repository_config(&config, repo));
+ cl_git_pass(git_config_set_string(config, "merge.ff", "only"));
+
+ analysis_from_branch(&merge_analysis, &merge_pref, NULL, NOFASTFORWARD_BRANCH);
+ cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, merge_analysis);
+
+ cl_assert_equal_i(GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY, (merge_pref & GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY));
+
+ git_config_free(config);
+}
+
+void test_merge_analysis__between_uptodate_refs(void)
+{
+ git_merge_analysis_t merge_analysis;
+ git_merge_preference_t merge_pref;
+
+ analysis_from_branch(&merge_analysis, &merge_pref, NOFASTFORWARD_BRANCH, PREVIOUS_BRANCH);
+ cl_assert_equal_i(GIT_MERGE_ANALYSIS_UP_TO_DATE, merge_analysis);
+}
+
+void test_merge_analysis__between_noff_refs(void)
+{
+ git_merge_analysis_t merge_analysis;
+ git_merge_preference_t merge_pref;
+
+ analysis_from_branch(&merge_analysis, &merge_pref, "branch", FASTFORWARD_BRANCH);
+ cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, merge_analysis);
+}
diff --git a/tests/libgit2/merge/annotated_commit.c b/tests/libgit2/merge/annotated_commit.c
new file mode 100644
index 0000000..cfdf849
--- /dev/null
+++ b/tests/libgit2/merge/annotated_commit.c
@@ -0,0 +1,26 @@
+#include "clar_libgit2.h"
+
+
+static git_repository *g_repo;
+
+void test_merge_annotated_commit__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_merge_annotated_commit__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_merge_annotated_commit__lookup_annotated_tag(void)
+{
+ git_annotated_commit *commit;
+ git_reference *ref;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/tags/test"));
+ cl_git_pass(git_annotated_commit_from_ref(&commit, g_repo, ref));
+
+ git_annotated_commit_free(commit);
+ git_reference_free(ref);
+}
diff --git a/tests/libgit2/merge/conflict_data.h b/tests/libgit2/merge/conflict_data.h
new file mode 100644
index 0000000..0b1e7ee
--- /dev/null
+++ b/tests/libgit2/merge/conflict_data.h
@@ -0,0 +1,112 @@
+#define AUTOMERGEABLE_MERGED_FILE \
+ "this file is changed in master\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is changed in branch\n"
+
+#define AUTOMERGEABLE_MERGED_FILE_CRLF \
+ "this file is changed in master\r\n" \
+ "this file is automergeable\r\n" \
+ "this file is automergeable\r\n" \
+ "this file is automergeable\r\n" \
+ "this file is automergeable\r\n" \
+ "this file is automergeable\r\n" \
+ "this file is automergeable\r\n" \
+ "this file is automergeable\r\n" \
+ "this file is changed in branch\r\n"
+
+#define CONFLICTING_MERGE_FILE \
+ "<<<<<<< HEAD\n" \
+ "this file is changed in master and branch\n" \
+ "=======\n" \
+ "this file is changed in branch and master\n" \
+ ">>>>>>> 7cb63eed597130ba4abb87b3e544b85021905520\n"
+
+#define CONFLICTING_DIFF3_FILE \
+ "<<<<<<< HEAD\n" \
+ "this file is changed in master and branch\n" \
+ "||||||| initial\n" \
+ "this file is a conflict\n" \
+ "=======\n" \
+ "this file is changed in branch and master\n" \
+ ">>>>>>> 7cb63eed597130ba4abb87b3e544b85021905520\n"
+
+#define CONFLICTING_ZDIFF3_FILE \
+ "<<<<<<< HEAD\n" \
+ "this file is changed in master and branch\n" \
+ "||||||| initial\n" \
+ "this file is a conflict\n" \
+ "=======\n" \
+ "this file is changed in branch and master\n" \
+ ">>>>>>> 7cb63eed597130ba4abb87b3e544b85021905520\n"
+
+#define CONFLICTING_UNION_FILE \
+ "this file is changed in master and branch\n" \
+ "this file is changed in branch and master\n"
+
+#define CONFLICTING_RECURSIVE_F1_TO_F2 \
+ "VEAL SOUP.\n" \
+ "\n" \
+ "<<<<<<< HEAD\n" \
+ "PUT INTO A POT THREE QUARTS OF WATER, three onions cut small, ONE\n" \
+ "=======\n" \
+ "PUT INTO A POT THREE QUARTS OF WATER, three onions cut not too small, one\n" \
+ ">>>>>>> branchF-2\n" \
+ "spoonful of black pepper pounded, and two of salt, with two or three\n" \
+ "slices of lean ham; let it boil steadily two hours; skim it\n" \
+ "occasionally, then put into it a shin of veal, let it boil two hours\n" \
+ "longer; take out the slices of ham, and skim off the grease if any\n" \
+ "should rise, take a gill of good cream, mix with it two table-spoonsful\n" \
+ "of flour very nicely, and the yelks of two eggs beaten well, strain this\n" \
+ "mixture, and add some chopped parsley; pour some soup on by degrees,\n" \
+ "stir it well, and pour it into the pot, continuing to stir until it has\n" \
+ "boiled two or three minutes to take off the raw taste of the eggs. If\n" \
+ "the cream be not perfectly sweet, and the eggs quite new, the thickening\n" \
+ "will curdle in the soup. For a change you may put a dozen ripe tomatos\n" \
+ "in, first taking off their skins, by letting them stand a few minutes in\n" \
+ "hot water, when they may be easily peeled. When made in this way you\n" \
+ "must thicken it with the flour only. Any part of the veal may be used,\n" \
+ "but the shin or knuckle is the nicest.\n" \
+ "\n" \
+ "<<<<<<< HEAD\n" \
+ "This certainly is a mighty fine recipe.\n" \
+ "=======\n" \
+ "This is a mighty fine recipe!\n" \
+ ">>>>>>> branchF-2\n"
+
+#define CONFLICTING_RECURSIVE_H2_TO_H1_WITH_DIFF3 \
+ "VEAL SOUP.\n" \
+ "\n" \
+ "<<<<<<< HEAD\n" \
+ "Put Into A Pot Three Quarts of Water, Three Onions Cut Small, One\n" \
+ "||||||| merged common ancestors\n" \
+ "<<<<<<<<< Temporary merge branch 1\n" \
+ "PUT INTO A POT three quarts of water, three onions cut small, one\n" \
+ "||||||||| merged common ancestors\n" \
+ "Put into a pot three quarts of water, three onions cut small, one\n" \
+ "=========\n" \
+ "Put into a pot three quarts of water, THREE ONIONS CUT SMALL, one\n" \
+ ">>>>>>>>> Temporary merge branch 2\n" \
+ "=======\n" \
+ "put into a pot three quarts of water, three onions cut small, one\n" \
+ ">>>>>>> branchH-1\n" \
+ "spoonful of black pepper pounded, and two of salt, with two or three\n" \
+ "slices of lean ham; let it boil steadily two hours; skim it\n" \
+ "occasionally, then put into it a shin of veal, let it boil two hours\n" \
+ "longer; take out the slices of ham, and skim off the grease if any\n" \
+ "should rise, take a gill of good cream, mix with it two table-spoonsful\n" \
+ "of flour very nicely, and the yelks of two eggs beaten well, strain this\n" \
+ "mixture, and add some chopped parsley; pour some soup on by degrees,\n" \
+ "stir it well, and pour it into the pot, continuing to stir until it has\n" \
+ "boiled two or three minutes to take off the raw taste of the eggs. If\n" \
+ "the cream be not perfectly sweet, and the eggs quite new, the thickening\n" \
+ "will curdle in the soup. For a change you may put a dozen ripe tomatos\n" \
+ "in, first taking off their skins, by letting them stand a few minutes in\n" \
+ "hot water, when they may be easily peeled. When made in this way you\n" \
+ "must thicken it with the flour only. Any part of the veal may be used,\n" \
+ "but the shin or knuckle is the nicest.\n"
diff --git a/tests/libgit2/merge/driver.c b/tests/libgit2/merge/driver.c
new file mode 100644
index 0000000..fd73c37
--- /dev/null
+++ b/tests/libgit2/merge/driver.c
@@ -0,0 +1,396 @@
+#include "clar_libgit2.h"
+#include "git2/repository.h"
+#include "git2/merge.h"
+#include "merge.h"
+
+#define TEST_REPO_PATH "merge-resolve"
+#define BRANCH_ID "7cb63eed597130ba4abb87b3e544b85021905520"
+
+#define AUTOMERGEABLE_IDSTR "f2e1550a0c9e53d5811175864a29536642ae3821"
+
+static git_repository *repo;
+static git_index *repo_index;
+static git_oid automergeable_id;
+
+static void test_drivers_register(void);
+static void test_drivers_unregister(void);
+
+void test_merge_driver__initialize(void)
+{
+ git_config *cfg;
+
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+ git_repository_index(&repo_index, repo);
+
+ git_oid__fromstr(&automergeable_id, AUTOMERGEABLE_IDSTR, GIT_OID_SHA1);
+
+ /* Ensure that the user's merge.conflictstyle doesn't interfere */
+ cl_git_pass(git_repository_config(&cfg, repo));
+
+ cl_git_pass(git_config_set_string(cfg, "merge.conflictstyle", "merge"));
+ cl_git_pass(git_config_set_bool(cfg, "core.autocrlf", false));
+
+ test_drivers_register();
+
+ git_config_free(cfg);
+}
+
+void test_merge_driver__cleanup(void)
+{
+ test_drivers_unregister();
+
+ git_index_free(repo_index);
+ cl_git_sandbox_cleanup();
+}
+
+struct test_merge_driver {
+ git_merge_driver base;
+ int initialized;
+ int shutdown;
+};
+
+static int test_driver_init(git_merge_driver *s)
+{
+ struct test_merge_driver *self = (struct test_merge_driver *)s;
+ self->initialized = 1;
+ return 0;
+}
+
+static void test_driver_shutdown(git_merge_driver *s)
+{
+ struct test_merge_driver *self = (struct test_merge_driver *)s;
+ self->shutdown = 1;
+}
+
+static int test_driver_apply(
+ git_merge_driver *s,
+ const char **path_out,
+ uint32_t *mode_out,
+ git_buf *merged_out,
+ const char *filter_name,
+ const git_merge_driver_source *src)
+{
+ git_str str = GIT_STR_INIT;
+ int error;
+
+ GIT_UNUSED(s);
+ GIT_UNUSED(src);
+
+ *path_out = "applied.txt";
+ *mode_out = GIT_FILEMODE_BLOB;
+
+ error = git_str_printf(&str, "This is the `%s` driver.\n",
+ filter_name);
+
+ merged_out->ptr = str.ptr;
+ merged_out->size = str.size;
+ merged_out->reserved = 0;
+
+ return error;
+}
+
+static struct test_merge_driver test_driver_custom = {
+ {
+ GIT_MERGE_DRIVER_VERSION,
+ test_driver_init,
+ test_driver_shutdown,
+ test_driver_apply,
+ },
+ 0,
+ 0,
+};
+
+static struct test_merge_driver test_driver_wildcard = {
+ {
+ GIT_MERGE_DRIVER_VERSION,
+ test_driver_init,
+ test_driver_shutdown,
+ test_driver_apply,
+ },
+ 0,
+ 0,
+};
+
+static void test_drivers_register(void)
+{
+ cl_git_pass(git_merge_driver_register("custom", &test_driver_custom.base));
+ cl_git_pass(git_merge_driver_register("*", &test_driver_wildcard.base));
+}
+
+static void test_drivers_unregister(void)
+{
+ cl_git_pass(git_merge_driver_unregister("custom"));
+ cl_git_pass(git_merge_driver_unregister("*"));
+}
+
+static void set_gitattributes_to(const char *driver)
+{
+ git_str line = GIT_STR_INIT;
+
+ if (driver && strcmp(driver, ""))
+ git_str_printf(&line, "automergeable.txt merge=%s\n", driver);
+ else if (driver)
+ git_str_printf(&line, "automergeable.txt merge\n");
+ else
+ git_str_printf(&line, "automergeable.txt -merge\n");
+
+ cl_assert(!git_str_oom(&line));
+
+ cl_git_mkfile(TEST_REPO_PATH "/.gitattributes", line.ptr);
+ git_str_dispose(&line);
+}
+
+static void merge_branch(void)
+{
+ git_oid their_id;
+ git_annotated_commit *their_head;
+
+ cl_git_pass(git_oid__fromstr(&their_id, BRANCH_ID, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&their_head, repo, &their_id));
+
+ cl_git_pass(git_merge(repo, (const git_annotated_commit **)&their_head,
+ 1, NULL, NULL));
+
+ git_annotated_commit_free(their_head);
+}
+
+void test_merge_driver__custom(void)
+{
+ const char *expected = "This is the `custom` driver.\n";
+ set_gitattributes_to("custom");
+ merge_branch();
+
+ cl_assert_equal_file(expected, strlen(expected),
+ TEST_REPO_PATH "/applied.txt");
+}
+
+void test_merge_driver__wildcard(void)
+{
+ const char *expected = "This is the `foobar` driver.\n";
+ set_gitattributes_to("foobar");
+ merge_branch();
+
+ cl_assert_equal_file(expected, strlen(expected),
+ TEST_REPO_PATH "/applied.txt");
+}
+
+void test_merge_driver__shutdown_is_called(void)
+{
+ test_driver_custom.initialized = 0;
+ test_driver_custom.shutdown = 0;
+ test_driver_wildcard.initialized = 0;
+ test_driver_wildcard.shutdown = 0;
+
+ /* run the merge with the custom driver */
+ set_gitattributes_to("custom");
+ merge_branch();
+
+ /* unregister the drivers, ensure their shutdown function is called */
+ test_drivers_unregister();
+
+ /* since the `custom` driver was used, it should have been initialized and
+ * shutdown, but the wildcard driver was not used at all and should not
+ * have been initialized or shutdown.
+ */
+ cl_assert(test_driver_custom.initialized);
+ cl_assert(test_driver_custom.shutdown);
+ cl_assert(!test_driver_wildcard.initialized);
+ cl_assert(!test_driver_wildcard.shutdown);
+
+ test_drivers_register();
+}
+
+static int defer_driver_apply(
+ git_merge_driver *s,
+ const char **path_out,
+ uint32_t *mode_out,
+ git_buf *merged_out,
+ const char *filter_name,
+ const git_merge_driver_source *src)
+{
+ GIT_UNUSED(s);
+ GIT_UNUSED(path_out);
+ GIT_UNUSED(mode_out);
+ GIT_UNUSED(merged_out);
+ GIT_UNUSED(filter_name);
+ GIT_UNUSED(src);
+
+ return GIT_PASSTHROUGH;
+}
+
+static struct test_merge_driver test_driver_defer_apply = {
+ {
+ GIT_MERGE_DRIVER_VERSION,
+ test_driver_init,
+ test_driver_shutdown,
+ defer_driver_apply,
+ },
+ 0,
+ 0,
+};
+
+void test_merge_driver__apply_can_defer(void)
+{
+ const git_index_entry *idx;
+
+ cl_git_pass(git_merge_driver_register("defer",
+ &test_driver_defer_apply.base));
+
+ set_gitattributes_to("defer");
+ merge_branch();
+
+ cl_assert((idx = git_index_get_bypath(repo_index, "automergeable.txt", 0)));
+ cl_assert_equal_oid(&automergeable_id, &idx->id);
+
+ git_merge_driver_unregister("defer");
+}
+
+static int conflict_driver_apply(
+ git_merge_driver *s,
+ const char **path_out,
+ uint32_t *mode_out,
+ git_buf *merged_out,
+ const char *filter_name,
+ const git_merge_driver_source *src)
+{
+ GIT_UNUSED(s);
+ GIT_UNUSED(path_out);
+ GIT_UNUSED(mode_out);
+ GIT_UNUSED(merged_out);
+ GIT_UNUSED(filter_name);
+ GIT_UNUSED(src);
+
+ return GIT_EMERGECONFLICT;
+}
+
+static struct test_merge_driver test_driver_conflict_apply = {
+ {
+ GIT_MERGE_DRIVER_VERSION,
+ test_driver_init,
+ test_driver_shutdown,
+ conflict_driver_apply,
+ },
+ 0,
+ 0,
+};
+
+void test_merge_driver__apply_can_conflict(void)
+{
+ const git_index_entry *ancestor, *ours, *theirs;
+
+ cl_git_pass(git_merge_driver_register("conflict",
+ &test_driver_conflict_apply.base));
+
+ set_gitattributes_to("conflict");
+ merge_branch();
+
+ cl_git_pass(git_index_conflict_get(&ancestor, &ours, &theirs,
+ repo_index, "automergeable.txt"));
+
+ git_merge_driver_unregister("conflict");
+}
+
+void test_merge_driver__default_can_be_specified(void)
+{
+ git_oid their_id;
+ git_annotated_commit *their_head;
+ git_merge_options merge_opts = GIT_MERGE_OPTIONS_INIT;
+ const char *expected = "This is the `custom` driver.\n";
+
+ merge_opts.default_driver = "custom";
+
+ cl_git_pass(git_oid__fromstr(&their_id, BRANCH_ID, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&their_head, repo, &their_id));
+
+ cl_git_pass(git_merge(repo, (const git_annotated_commit **)&their_head,
+ 1, &merge_opts, NULL));
+
+ git_annotated_commit_free(their_head);
+
+ cl_assert_equal_file(expected, strlen(expected),
+ TEST_REPO_PATH "/applied.txt");
+}
+
+void test_merge_driver__honors_builtin_mergedefault(void)
+{
+ const git_index_entry *ancestor, *ours, *theirs;
+
+ cl_repo_set_string(repo, "merge.default", "binary");
+ merge_branch();
+
+ cl_git_pass(git_index_conflict_get(&ancestor, &ours, &theirs,
+ repo_index, "automergeable.txt"));
+}
+
+void test_merge_driver__honors_custom_mergedefault(void)
+{
+ const char *expected = "This is the `custom` driver.\n";
+
+ cl_repo_set_string(repo, "merge.default", "custom");
+ merge_branch();
+
+ cl_assert_equal_file(expected, strlen(expected),
+ TEST_REPO_PATH "/applied.txt");
+}
+
+void test_merge_driver__mergedefault_deferring_falls_back_to_text(void)
+{
+ const git_index_entry *idx;
+
+ cl_git_pass(git_merge_driver_register("defer",
+ &test_driver_defer_apply.base));
+
+ cl_repo_set_string(repo, "merge.default", "defer");
+ merge_branch();
+
+ cl_assert((idx = git_index_get_bypath(repo_index, "automergeable.txt", 0)));
+ cl_assert_equal_oid(&automergeable_id, &idx->id);
+
+ git_merge_driver_unregister("defer");
+}
+
+void test_merge_driver__set_forces_text(void)
+{
+ const git_index_entry *idx;
+
+ /* `merge` without specifying a driver indicates `text` */
+ set_gitattributes_to("");
+ cl_repo_set_string(repo, "merge.default", "custom");
+
+ merge_branch();
+
+ cl_assert((idx = git_index_get_bypath(repo_index, "automergeable.txt", 0)));
+ cl_assert_equal_oid(&automergeable_id, &idx->id);
+}
+
+void test_merge_driver__unset_forces_binary(void)
+{
+ const git_index_entry *ancestor, *ours, *theirs;
+
+ /* `-merge` without specifying a driver indicates `binary` */
+ set_gitattributes_to(NULL);
+ cl_repo_set_string(repo, "merge.default", "custom");
+
+ merge_branch();
+
+ cl_git_pass(git_index_conflict_get(&ancestor, &ours, &theirs,
+ repo_index, "automergeable.txt"));
+}
+
+void test_merge_driver__not_configured_driver_falls_back(void)
+{
+ const git_index_entry *idx;
+
+ test_drivers_unregister();
+
+ /* `merge` without specifying a driver indicates `text` */
+ set_gitattributes_to("notfound");
+
+ merge_branch();
+
+ cl_assert((idx = git_index_get_bypath(repo_index, "automergeable.txt", 0)));
+ cl_assert_equal_oid(&automergeable_id, &idx->id);
+
+ test_drivers_register();
+}
+
diff --git a/tests/libgit2/merge/files.c b/tests/libgit2/merge/files.c
new file mode 100644
index 0000000..6c1c2e1
--- /dev/null
+++ b/tests/libgit2/merge/files.c
@@ -0,0 +1,465 @@
+#include "clar_libgit2.h"
+#include "git2/repository.h"
+#include "git2/merge.h"
+#include "merge.h"
+#include "merge_helpers.h"
+#include "conflict_data.h"
+#include "refs.h"
+#include "futils.h"
+#include "diff_xdiff.h"
+
+#define TEST_REPO_PATH "merge-resolve"
+#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
+
+static git_repository *repo;
+static git_index *repo_index;
+
+/* Fixture setup and teardown */
+void test_merge_files__initialize(void)
+{
+ git_config *cfg;
+
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+ git_repository_index(&repo_index, repo);
+
+ /* Ensure that the user's merge.conflictstyle doesn't interfere */
+ cl_git_pass(git_repository_config(&cfg, repo));
+ cl_git_pass(git_config_set_string(cfg, "merge.conflictstyle", "merge"));
+ git_config_free(cfg);
+}
+
+void test_merge_files__cleanup(void)
+{
+ git_index_free(repo_index);
+ cl_git_sandbox_cleanup();
+}
+
+void test_merge_files__automerge_from_bufs(void)
+{
+ git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT,
+ ours = GIT_MERGE_FILE_INPUT_INIT,
+ theirs = GIT_MERGE_FILE_INPUT_INIT;
+ git_merge_file_result result = {0};
+ const char *expected = "Zero\n1\n2\n3\n4\n5\n6\n7\n8\n9\nTen\n";
+
+ ancestor.ptr = "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n";
+ ancestor.size = strlen(ancestor.ptr);
+ ancestor.path = "testfile.txt";
+ ancestor.mode = 0100755;
+
+ ours.ptr = "Zero\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n";
+ ours.size = strlen(ours.ptr);
+ ours.path = "testfile.txt";
+ ours.mode = 0100755;
+
+ theirs.ptr = "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\nTen\n";
+ theirs.size = strlen(theirs.ptr);
+ theirs.path = "testfile.txt";
+ theirs.mode = 0100755;
+
+ cl_git_pass(git_merge_file(&result, &ancestor, &ours, &theirs, 0));
+
+ cl_assert_equal_i(1, result.automergeable);
+
+ cl_assert_equal_s("testfile.txt", result.path);
+ cl_assert_equal_i(0100755, result.mode);
+
+ cl_assert_equal_i(strlen(expected), result.len);
+ cl_assert_equal_strn(expected, result.ptr, result.len);
+
+ git_merge_file_result_free(&result);
+}
+
+void test_merge_files__automerge_use_best_path_and_mode(void)
+{
+ git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT,
+ ours = GIT_MERGE_FILE_INPUT_INIT,
+ theirs = GIT_MERGE_FILE_INPUT_INIT;
+ git_merge_file_result result = {0};
+ const char *expected = "Zero\n1\n2\n3\n4\n5\n6\n7\n8\n9\nTen\n";
+
+ ancestor.ptr = "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n";
+ ancestor.size = strlen(ancestor.ptr);
+ ancestor.path = "testfile.txt";
+ ancestor.mode = 0100755;
+
+ ours.ptr = "Zero\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n";
+ ours.size = strlen(ours.ptr);
+ ours.path = "testfile.txt";
+ ours.mode = 0100644;
+
+ theirs.ptr = "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\nTen\n";
+ theirs.size = strlen(theirs.ptr);
+ theirs.path = "theirs.txt";
+ theirs.mode = 0100755;
+
+ cl_git_pass(git_merge_file(&result, &ancestor, &ours, &theirs, 0));
+
+ cl_assert_equal_i(1, result.automergeable);
+
+ cl_assert_equal_s("theirs.txt", result.path);
+ cl_assert_equal_i(0100644, result.mode);
+
+ cl_assert_equal_i(strlen(expected), result.len);
+ cl_assert_equal_strn(expected, result.ptr, result.len);
+
+ git_merge_file_result_free(&result);
+}
+
+void test_merge_files__conflict_from_bufs(void)
+{
+ git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT,
+ ours = GIT_MERGE_FILE_INPUT_INIT,
+ theirs = GIT_MERGE_FILE_INPUT_INIT;
+ git_merge_file_result result = {0};
+
+ const char *expected = "<<<<<<< testfile.txt\nAloha!\nOurs.\n=======\nHi!\nTheirs.\n>>>>>>> theirs.txt\n";
+ size_t expected_len = strlen(expected);
+
+ ancestor.ptr = "Hello!\nAncestor!\n";
+ ancestor.size = strlen(ancestor.ptr);
+ ancestor.path = "testfile.txt";
+ ancestor.mode = 0100755;
+
+ ours.ptr = "Aloha!\nOurs.\n";
+ ours.size = strlen(ours.ptr);
+ ours.path = "testfile.txt";
+ ours.mode = 0100644;
+
+ theirs.ptr = "Hi!\nTheirs.\n";
+ theirs.size = strlen(theirs.ptr);
+ theirs.path = "theirs.txt";
+ theirs.mode = 0100755;
+
+ cl_git_pass(git_merge_file(&result, &ancestor, &ours, &theirs, NULL));
+
+ cl_assert_equal_i(0, result.automergeable);
+
+ cl_assert_equal_s("theirs.txt", result.path);
+ cl_assert_equal_i(0100644, result.mode);
+
+ cl_assert_equal_i(expected_len, result.len);
+ cl_assert_equal_strn(expected, result.ptr, expected_len);
+
+ git_merge_file_result_free(&result);
+}
+
+void test_merge_files__automerge_from_index(void)
+{
+ git_merge_file_result result = {0};
+ git_index_entry ancestor, ours, theirs;
+
+ git_oid__fromstr(&ancestor.id, "6212c31dab5e482247d7977e4f0dd3601decf13b", GIT_OID_SHA1);
+ ancestor.path = "automergeable.txt";
+ ancestor.mode = 0100644;
+
+ git_oid__fromstr(&ours.id, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", GIT_OID_SHA1);
+ ours.path = "automergeable.txt";
+ ours.mode = 0100755;
+
+ git_oid__fromstr(&theirs.id, "058541fc37114bfc1dddf6bd6bffc7fae5c2e6fe", GIT_OID_SHA1);
+ theirs.path = "newname.txt";
+ theirs.mode = 0100644;
+
+ cl_git_pass(git_merge_file_from_index(&result, repo,
+ &ancestor, &ours, &theirs, 0));
+
+ cl_assert_equal_i(1, result.automergeable);
+
+ cl_assert_equal_s("newname.txt", result.path);
+ cl_assert_equal_i(0100755, result.mode);
+
+ cl_assert_equal_i(strlen(AUTOMERGEABLE_MERGED_FILE), result.len);
+ cl_assert_equal_strn(AUTOMERGEABLE_MERGED_FILE, result.ptr, result.len);
+
+ git_merge_file_result_free(&result);
+}
+
+void test_merge_files__automerge_whitespace_eol(void)
+{
+ git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT,
+ ours = GIT_MERGE_FILE_INPUT_INIT,
+ theirs = GIT_MERGE_FILE_INPUT_INIT;
+ git_merge_file_options opts = GIT_MERGE_FILE_OPTIONS_INIT;
+ git_merge_file_result result = {0};
+ const char *expected = "Zero\n1\n2\n3\n4\n5\n6\n7\n8\n9\nTen\n";
+
+ ancestor.ptr = "0 \n1\n2\n3\n4\n5\n6\n7\n8\n9\n10 \n";
+ ancestor.size = strlen(ancestor.ptr);
+ ancestor.path = "testfile.txt";
+ ancestor.mode = 0100755;
+
+ ours.ptr = "Zero\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n";
+ ours.size = strlen(ours.ptr);
+ ours.path = "testfile.txt";
+ ours.mode = 0100755;
+
+ theirs.ptr = "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\nTen\n";
+ theirs.size = strlen(theirs.ptr);
+ theirs.path = "testfile.txt";
+ theirs.mode = 0100755;
+
+ opts.flags |= GIT_MERGE_FILE_IGNORE_WHITESPACE_EOL;
+ cl_git_pass(git_merge_file(&result, &ancestor, &ours, &theirs, &opts));
+
+ cl_assert_equal_i(1, result.automergeable);
+
+ cl_assert_equal_s("testfile.txt", result.path);
+ cl_assert_equal_i(0100755, result.mode);
+
+ cl_assert_equal_i(strlen(expected), result.len);
+ cl_assert_equal_strn(expected, result.ptr, result.len);
+
+ git_merge_file_result_free(&result);
+}
+
+void test_merge_files__automerge_whitespace_change(void)
+{
+ git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT,
+ ours = GIT_MERGE_FILE_INPUT_INIT,
+ theirs = GIT_MERGE_FILE_INPUT_INIT;
+ git_merge_file_options opts = GIT_MERGE_FILE_OPTIONS_INIT;
+ git_merge_file_result result = {0};
+ const char *expected = "Zero\n1\n2\n3\n4\n5 XXX\n6 YYY\n7\n8\n9\nTen\n";
+
+ ancestor.ptr = "0\n1\n2\n3\n4\n5 XXX\n6YYY\n7\n8\n9\n10\n";
+ ancestor.size = strlen(ancestor.ptr);
+ ancestor.path = "testfile.txt";
+ ancestor.mode = 0100755;
+
+ ours.ptr = "Zero\n1\n2\n3\n4\n5 XXX\n6 YYY\n7\n8\n9\n10\n";
+ ours.size = strlen(ours.ptr);
+ ours.path = "testfile.txt";
+ ours.mode = 0100755;
+
+ theirs.ptr = "0\n1\n2\n3\n4\n5 XXX\n6 YYY\n7\n8\n9\nTen\n";
+ theirs.size = strlen(theirs.ptr);
+ theirs.path = "testfile.txt";
+ theirs.mode = 0100755;
+
+ opts.flags |= GIT_MERGE_FILE_IGNORE_WHITESPACE_CHANGE;
+ cl_git_pass(git_merge_file(&result, &ancestor, &ours, &theirs, &opts));
+
+ cl_assert_equal_i(1, result.automergeable);
+
+ cl_assert_equal_s("testfile.txt", result.path);
+ cl_assert_equal_i(0100755, result.mode);
+
+ cl_assert_equal_i(strlen(expected), result.len);
+ cl_assert_equal_strn(expected, result.ptr, result.len);
+
+ git_merge_file_result_free(&result);
+}
+
+void test_merge_files__doesnt_add_newline(void)
+{
+ git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT,
+ ours = GIT_MERGE_FILE_INPUT_INIT,
+ theirs = GIT_MERGE_FILE_INPUT_INIT;
+ git_merge_file_options opts = GIT_MERGE_FILE_OPTIONS_INIT;
+ git_merge_file_result result = {0};
+ const char *expected = "Zero\n1\n2\n3\n4\n5 XXX\n6 YYY\n7\n8\n9\nTen";
+
+ ancestor.ptr = "0\n1\n2\n3\n4\n5 XXX\n6YYY\n7\n8\n9\n10";
+ ancestor.size = strlen(ancestor.ptr);
+ ancestor.path = "testfile.txt";
+ ancestor.mode = 0100755;
+
+ ours.ptr = "Zero\n1\n2\n3\n4\n5 XXX\n6 YYY\n7\n8\n9\n10";
+ ours.size = strlen(ours.ptr);
+ ours.path = "testfile.txt";
+ ours.mode = 0100755;
+
+ theirs.ptr = "0\n1\n2\n3\n4\n5 XXX\n6 YYY\n7\n8\n9\nTen";
+ theirs.size = strlen(theirs.ptr);
+ theirs.path = "testfile.txt";
+ theirs.mode = 0100755;
+
+ opts.flags |= GIT_MERGE_FILE_IGNORE_WHITESPACE_CHANGE;
+ cl_git_pass(git_merge_file(&result, &ancestor, &ours, &theirs, &opts));
+
+ cl_assert_equal_i(1, result.automergeable);
+
+ cl_assert_equal_s("testfile.txt", result.path);
+ cl_assert_equal_i(0100755, result.mode);
+
+ cl_assert_equal_i(strlen(expected), result.len);
+ cl_assert_equal_strn(expected, result.ptr, result.len);
+
+ git_merge_file_result_free(&result);
+}
+
+void test_merge_files__skips_large_files(void)
+{
+ git_merge_file_input ours = GIT_MERGE_FILE_INPUT_INIT,
+ theirs = GIT_MERGE_FILE_INPUT_INIT;
+ git_merge_file_options opts = GIT_MERGE_FILE_OPTIONS_INIT;
+ git_merge_file_result result = {0};
+
+ ours.size = GIT_XDIFF_MAX_SIZE + 1;
+ ours.path = "testfile.txt";
+ ours.mode = 0100755;
+
+ theirs.size = GIT_XDIFF_MAX_SIZE + 1;
+ theirs.path = "testfile.txt";
+ theirs.mode = 0100755;
+
+ cl_git_pass(git_merge_file(&result, NULL, &ours, &theirs, &opts));
+
+ cl_assert_equal_i(0, result.automergeable);
+
+ git_merge_file_result_free(&result);
+}
+
+void test_merge_files__skips_binaries(void)
+{
+ git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT,
+ ours = GIT_MERGE_FILE_INPUT_INIT,
+ theirs = GIT_MERGE_FILE_INPUT_INIT;
+ git_merge_file_result result = {0};
+
+ ancestor.ptr = "ance\0stor\0";
+ ancestor.size = 10;
+ ancestor.path = "ancestor.txt";
+ ancestor.mode = 0100755;
+
+ ours.ptr = "foo\0bar\0";
+ ours.size = 8;
+ ours.path = "ours.txt";
+ ours.mode = 0100755;
+
+ theirs.ptr = "bar\0foo\0";
+ theirs.size = 8;
+ theirs.path = "theirs.txt";
+ theirs.mode = 0100644;
+
+ cl_git_pass(git_merge_file(&result, &ancestor, &ours, &theirs, NULL));
+
+ cl_assert_equal_i(0, result.automergeable);
+
+ git_merge_file_result_free(&result);
+}
+
+void test_merge_files__handles_binaries_when_favored(void)
+{
+ git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT,
+ ours = GIT_MERGE_FILE_INPUT_INIT,
+ theirs = GIT_MERGE_FILE_INPUT_INIT;
+ git_merge_file_options opts = GIT_MERGE_FILE_OPTIONS_INIT;
+ git_merge_file_result result = {0};
+
+ ancestor.ptr = "ance\0stor\0";
+ ancestor.size = 10;
+ ancestor.path = "ancestor.txt";
+ ancestor.mode = 0100755;
+
+ ours.ptr = "foo\0bar\0";
+ ours.size = 8;
+ ours.path = "ours.txt";
+ ours.mode = 0100755;
+
+ theirs.ptr = "bar\0foo\0";
+ theirs.size = 8;
+ theirs.path = "theirs.txt";
+ theirs.mode = 0100644;
+
+ opts.favor = GIT_MERGE_FILE_FAVOR_OURS;
+ cl_git_pass(git_merge_file(&result, &ancestor, &ours, &theirs, &opts));
+
+ cl_assert_equal_i(1, result.automergeable);
+
+ cl_assert_equal_s("ours.txt", result.path);
+ cl_assert_equal_i(0100755, result.mode);
+
+ cl_assert_equal_i(ours.size, result.len);
+ cl_assert(memcmp(result.ptr, ours.ptr, ours.size) == 0);
+
+ git_merge_file_result_free(&result);
+}
+
+void test_merge_files__crlf_conflict_markers_for_crlf_files(void)
+{
+ git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT,
+ ours = GIT_MERGE_FILE_INPUT_INIT,
+ theirs = GIT_MERGE_FILE_INPUT_INIT;
+ git_merge_file_options opts = GIT_MERGE_FILE_OPTIONS_INIT;
+ git_merge_file_result result = {0};
+
+ const char *expected =
+ "<<<<<<< file.txt\r\nThis file\r\ndoes, too.\r\n"
+ "=======\r\nAnd so does\r\nthis one.\r\n>>>>>>> file.txt\r\n";
+ size_t expected_len = strlen(expected);
+
+ const char *expected_diff3 =
+ "<<<<<<< file.txt\r\nThis file\r\ndoes, too.\r\n"
+ "||||||| file.txt\r\nThis file has\r\nCRLF line endings.\r\n"
+ "=======\r\nAnd so does\r\nthis one.\r\n>>>>>>> file.txt\r\n";
+ size_t expected_diff3_len = strlen(expected_diff3);
+
+ ancestor.ptr = "This file has\r\nCRLF line endings.\r\n";
+ ancestor.size = 35;
+ ancestor.path = "file.txt";
+ ancestor.mode = 0100644;
+
+ ours.ptr = "This file\r\ndoes, too.\r\n";
+ ours.size = 23;
+ ours.path = "file.txt";
+ ours.mode = 0100644;
+
+ theirs.ptr = "And so does\r\nthis one.\r\n";
+ theirs.size = 24;
+ theirs.path = "file.txt";
+ theirs.mode = 0100644;
+
+ cl_git_pass(git_merge_file(&result, &ancestor, &ours, &theirs, &opts));
+ cl_assert_equal_i(0, result.automergeable);
+ cl_assert_equal_i(expected_len, result.len);
+ cl_assert(memcmp(expected, result.ptr, expected_len) == 0);
+ git_merge_file_result_free(&result);
+
+ opts.flags |= GIT_MERGE_FILE_STYLE_DIFF3;
+ cl_git_pass(git_merge_file(&result, &ancestor, &ours, &theirs, &opts));
+ cl_assert_equal_i(0, result.automergeable);
+ cl_assert_equal_i(expected_diff3_len, result.len);
+ cl_assert(memcmp(expected_diff3, result.ptr, expected_len) == 0);
+ git_merge_file_result_free(&result);
+}
+
+void test_merge_files__conflicts_in_zdiff3(void)
+{
+ git_merge_file_input ancestor = GIT_MERGE_FILE_INPUT_INIT,
+ ours = GIT_MERGE_FILE_INPUT_INIT,
+ theirs = GIT_MERGE_FILE_INPUT_INIT;
+ git_merge_file_options opts = GIT_MERGE_FILE_OPTIONS_INIT;
+ git_merge_file_result result = {0};
+
+ const char *expected_zdiff3 =
+ "1,\nfoo,\nbar,\n" \
+ "<<<<<<< file.txt\n" \
+ "||||||| file.txt\n# add more here\n" \
+ "=======\nquux,\nwoot,\n" \
+ ">>>>>>> file.txt\nbaz,\n3,\n";
+ size_t expected_zdiff3_len = strlen(expected_zdiff3);
+
+ ancestor.ptr = "1,\n# add more here\n3,\n";
+ ancestor.size = strlen(ancestor.ptr);
+ ancestor.path = "file.txt";
+ ancestor.mode = 0100644;
+
+ ours.ptr = "1,\nfoo,\nbar,\nbaz,\n3,\n";
+ ours.size = strlen(ours.ptr);
+ ours.path = "file.txt";
+ ours.mode = 0100644;
+
+ theirs.ptr = "1,\nfoo,\nbar,\nquux,\nwoot,\nbaz,\n3,\n";
+ theirs.size = strlen(theirs.ptr);
+ theirs.path = "file.txt";
+ theirs.mode = 0100644;
+
+ opts.flags |= GIT_MERGE_FILE_STYLE_ZDIFF3;
+ cl_git_pass(git_merge_file(&result, &ancestor, &ours, &theirs, &opts));
+ cl_assert_equal_i(0, result.automergeable);
+ cl_assert_equal_i(expected_zdiff3_len, result.len);
+ cl_assert(memcmp(expected_zdiff3, result.ptr, expected_zdiff3_len) == 0);
+ git_merge_file_result_free(&result);
+}
diff --git a/tests/libgit2/merge/merge_helpers.c b/tests/libgit2/merge/merge_helpers.c
new file mode 100644
index 0000000..1406987
--- /dev/null
+++ b/tests/libgit2/merge/merge_helpers.c
@@ -0,0 +1,365 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+#include "refs.h"
+#include "tree.h"
+#include "merge_helpers.h"
+#include "merge.h"
+#include "index.h"
+#include "git2/merge.h"
+#include "git2/sys/index.h"
+#include "git2/annotated_commit.h"
+
+int merge_trees_from_branches(
+ git_index **index, git_repository *repo,
+ const char *ours_name, const char *theirs_name,
+ git_merge_options *opts)
+{
+ git_commit *our_commit, *their_commit, *ancestor_commit = NULL;
+ git_tree *our_tree, *their_tree, *ancestor_tree = NULL;
+ git_oid our_oid, their_oid, ancestor_oid;
+ git_str branch_buf = GIT_STR_INIT;
+ int error;
+
+ git_str_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, ours_name);
+ cl_git_pass(git_reference_name_to_id(&our_oid, repo, branch_buf.ptr));
+ cl_git_pass(git_commit_lookup(&our_commit, repo, &our_oid));
+
+ git_str_clear(&branch_buf);
+ git_str_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, theirs_name);
+ cl_git_pass(git_reference_name_to_id(&their_oid, repo, branch_buf.ptr));
+ cl_git_pass(git_commit_lookup(&their_commit, repo, &their_oid));
+
+ error = git_merge_base(&ancestor_oid, repo, git_commit_id(our_commit), git_commit_id(their_commit));
+
+ if (error != GIT_ENOTFOUND) {
+ cl_git_pass(error);
+
+ cl_git_pass(git_commit_lookup(&ancestor_commit, repo, &ancestor_oid));
+ cl_git_pass(git_commit_tree(&ancestor_tree, ancestor_commit));
+ }
+
+ cl_git_pass(git_commit_tree(&our_tree, our_commit));
+ cl_git_pass(git_commit_tree(&their_tree, their_commit));
+
+ error = git_merge_trees(index, repo, ancestor_tree, our_tree, their_tree, opts);
+
+ git_str_dispose(&branch_buf);
+ git_tree_free(our_tree);
+ git_tree_free(their_tree);
+ git_tree_free(ancestor_tree);
+ git_commit_free(our_commit);
+ git_commit_free(their_commit);
+ git_commit_free(ancestor_commit);
+
+ return error;
+}
+
+int merge_commits_from_branches(
+ git_index **index, git_repository *repo,
+ const char *ours_name, const char *theirs_name,
+ git_merge_options *opts)
+{
+ git_commit *our_commit, *their_commit;
+ git_oid our_oid, their_oid;
+ git_str branch_buf = GIT_STR_INIT;
+ int error;
+
+ git_str_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, ours_name);
+ cl_git_pass(git_reference_name_to_id(&our_oid, repo, branch_buf.ptr));
+ cl_git_pass(git_commit_lookup(&our_commit, repo, &our_oid));
+
+ git_str_clear(&branch_buf);
+ git_str_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, theirs_name);
+ cl_git_pass(git_reference_name_to_id(&their_oid, repo, branch_buf.ptr));
+ cl_git_pass(git_commit_lookup(&their_commit, repo, &their_oid));
+
+ error = git_merge_commits(index, repo, our_commit, their_commit, opts);
+
+ git_str_dispose(&branch_buf);
+ git_commit_free(our_commit);
+ git_commit_free(their_commit);
+
+ return error;
+}
+
+int merge_branches(git_repository *repo,
+ const char *ours_branch, const char *theirs_branch,
+ git_merge_options *merge_opts, git_checkout_options *checkout_opts)
+{
+ git_reference *head_ref, *theirs_ref;
+ git_annotated_commit *theirs_head;
+ git_checkout_options head_checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ head_checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_reference_symbolic_create(&head_ref, repo, "HEAD", ours_branch, 1, NULL));
+ cl_git_pass(git_checkout_head(repo, &head_checkout_opts));
+
+ cl_git_pass(git_reference_lookup(&theirs_ref, repo, theirs_branch));
+ cl_git_pass(git_annotated_commit_from_ref(&theirs_head, repo, theirs_ref));
+
+ cl_git_pass(git_merge(repo, (const git_annotated_commit **)&theirs_head, 1, merge_opts, checkout_opts));
+
+ git_reference_free(head_ref);
+ git_reference_free(theirs_ref);
+ git_annotated_commit_free(theirs_head);
+
+ return 0;
+}
+
+void merge__dump_index_entries(git_vector *index_entries)
+{
+ size_t i;
+ const git_index_entry *index_entry;
+
+ printf ("\nINDEX [%"PRIuZ"]:\n", index_entries->length);
+ for (i = 0; i < index_entries->length; i++) {
+ index_entry = index_entries->contents[i];
+
+ printf("%o ", index_entry->mode);
+ printf("%s ", git_oid_allocfmt(&index_entry->id));
+ printf("%d ", git_index_entry_stage(index_entry));
+ printf("%s ", index_entry->path);
+ printf("\n");
+ }
+ printf("\n");
+}
+
+void merge__dump_names(git_index *index)
+{
+ size_t i;
+ const git_index_name_entry *conflict_name;
+
+ for (i = 0; i < git_index_name_entrycount(index); i++) {
+ conflict_name = git_index_name_get_byindex(index, i);
+
+ printf("%s %s %s\n", conflict_name->ancestor, conflict_name->ours, conflict_name->theirs);
+ }
+ printf("\n");
+}
+
+void merge__dump_reuc(git_index *index)
+{
+ size_t i;
+ const git_index_reuc_entry *reuc;
+
+ printf ("\nREUC:\n");
+ for (i = 0; i < git_index_reuc_entrycount(index); i++) {
+ reuc = git_index_reuc_get_byindex(index, i);
+
+ printf("%s ", reuc->path);
+ printf("%o ", reuc->mode[0]);
+ printf("%s\n", git_oid_allocfmt(&reuc->oid[0]));
+ printf(" %o ", reuc->mode[1]);
+ printf(" %s\n", git_oid_allocfmt(&reuc->oid[1]));
+ printf(" %o ", reuc->mode[2]);
+ printf(" %s ", git_oid_allocfmt(&reuc->oid[2]));
+ printf("\n");
+ }
+ printf("\n");
+}
+
+static int index_entry_eq_merge_index_entry(const struct merge_index_entry *expected, const git_index_entry *actual)
+{
+ git_oid expected_oid;
+ bool test_oid;
+
+ if (strlen(expected->oid_str) != 0) {
+ cl_git_pass(git_oid__fromstr(&expected_oid, expected->oid_str, GIT_OID_SHA1));
+ test_oid = 1;
+ } else
+ test_oid = 0;
+
+ if (actual->mode != expected->mode ||
+ (test_oid && git_oid_cmp(&actual->id, &expected_oid) != 0) ||
+ git_index_entry_stage(actual) != expected->stage)
+ return 0;
+
+ if (actual->mode == 0 && (actual->path != NULL || strlen(expected->path) > 0))
+ return 0;
+
+ if (actual->mode != 0 && (strcmp(actual->path, expected->path) != 0))
+ return 0;
+
+ return 1;
+}
+
+static int name_entry_eq(const char *expected, const char *actual)
+{
+ if (strlen(expected) == 0)
+ return (actual == NULL) ? 1 : 0;
+
+ return (strcmp(expected, actual) == 0) ? 1 : 0;
+}
+
+static int name_entry_eq_merge_name_entry(const struct merge_name_entry *expected, const git_index_name_entry *actual)
+{
+ if (name_entry_eq(expected->ancestor_path, actual->ancestor) == 0 ||
+ name_entry_eq(expected->our_path, actual->ours) == 0 ||
+ name_entry_eq(expected->their_path, actual->theirs) == 0)
+ return 0;
+
+ return 1;
+}
+
+static int index_conflict_data_eq_merge_diff(const struct merge_index_conflict_data *expected, git_merge_diff *actual)
+{
+ if (!index_entry_eq_merge_index_entry(&expected->ancestor.entry, &actual->ancestor_entry) ||
+ !index_entry_eq_merge_index_entry(&expected->ours.entry, &actual->our_entry) ||
+ !index_entry_eq_merge_index_entry(&expected->theirs.entry, &actual->their_entry))
+ return 0;
+
+ if (expected->ours.status != actual->our_status ||
+ expected->theirs.status != actual->their_status)
+ return 0;
+
+ return 1;
+}
+
+int merge_test_merge_conflicts(git_vector *conflicts, const struct merge_index_conflict_data expected[], size_t expected_len)
+{
+ git_merge_diff *actual;
+ size_t i;
+
+ if (conflicts->length != expected_len)
+ return 0;
+
+ for (i = 0; i < expected_len; i++) {
+ actual = conflicts->contents[i];
+
+ if (!index_conflict_data_eq_merge_diff(&expected[i], actual))
+ return 0;
+ }
+
+ return 1;
+}
+
+int merge_test_index(git_index *index, const struct merge_index_entry expected[], size_t expected_len)
+{
+ size_t i;
+ const git_index_entry *index_entry;
+
+ /*
+ merge__dump_index_entries(&index->entries);
+ */
+
+ if (git_index_entrycount(index) != expected_len)
+ return 0;
+
+ for (i = 0; i < expected_len; i++) {
+ if ((index_entry = git_index_get_byindex(index, i)) == NULL)
+ return 0;
+
+ if (!index_entry_eq_merge_index_entry(&expected[i], index_entry))
+ return 0;
+ }
+
+ return 1;
+}
+
+int merge_test_names(git_index *index, const struct merge_name_entry expected[], size_t expected_len)
+{
+ size_t i;
+ const git_index_name_entry *name_entry;
+
+ /*
+ dump_names(index);
+ */
+
+ if (git_index_name_entrycount(index) != expected_len)
+ return 0;
+
+ for (i = 0; i < expected_len; i++) {
+ if ((name_entry = git_index_name_get_byindex(index, i)) == NULL)
+ return 0;
+
+ if (! name_entry_eq_merge_name_entry(&expected[i], name_entry))
+ return 0;
+ }
+
+ return 1;
+}
+
+int merge_test_reuc(git_index *index, const struct merge_reuc_entry expected[], size_t expected_len)
+{
+ size_t i;
+ const git_index_reuc_entry *reuc_entry;
+ git_oid expected_oid;
+
+ /*
+ dump_reuc(index);
+ */
+
+ if (git_index_reuc_entrycount(index) != expected_len)
+ return 0;
+
+ for (i = 0; i < expected_len; i++) {
+ if ((reuc_entry = git_index_reuc_get_byindex(index, i)) == NULL)
+ return 0;
+
+ if (strcmp(reuc_entry->path, expected[i].path) != 0 ||
+ reuc_entry->mode[0] != expected[i].ancestor_mode ||
+ reuc_entry->mode[1] != expected[i].our_mode ||
+ reuc_entry->mode[2] != expected[i].their_mode)
+ return 0;
+
+ if (expected[i].ancestor_mode > 0) {
+ cl_git_pass(git_oid__fromstr(&expected_oid, expected[i].ancestor_oid_str, GIT_OID_SHA1));
+
+ if (git_oid_cmp(&reuc_entry->oid[0], &expected_oid) != 0)
+ return 0;
+ }
+
+ if (expected[i].our_mode > 0) {
+ cl_git_pass(git_oid__fromstr(&expected_oid, expected[i].our_oid_str, GIT_OID_SHA1));
+
+ if (git_oid_cmp(&reuc_entry->oid[1], &expected_oid) != 0)
+ return 0;
+ }
+
+ if (expected[i].their_mode > 0) {
+ cl_git_pass(git_oid__fromstr(&expected_oid, expected[i].their_oid_str, GIT_OID_SHA1));
+
+ if (git_oid_cmp(&reuc_entry->oid[2], &expected_oid) != 0)
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+static int dircount(void *payload, git_str *pathbuf)
+{
+ size_t *entries = payload;
+ size_t len = git_str_len(pathbuf);
+
+ if (len < 5 || strcmp(pathbuf->ptr + (git_str_len(pathbuf) - 5), "/.git") != 0)
+ (*entries)++;
+
+ return 0;
+}
+
+int merge_test_workdir(git_repository *repo, const struct merge_index_entry expected[], size_t expected_len)
+{
+ size_t actual_len = 0, i;
+ git_oid actual_oid, expected_oid;
+ git_str wd = GIT_STR_INIT;
+
+ git_str_puts(&wd, repo->workdir);
+ git_fs_path_direach(&wd, 0, dircount, &actual_len);
+
+ if (actual_len != expected_len)
+ return 0;
+
+ for (i = 0; i < expected_len; i++) {
+ git_blob_create_from_workdir(&actual_oid, repo, expected[i].path);
+ git_oid__fromstr(&expected_oid, expected[i].oid_str, GIT_OID_SHA1);
+
+ if (git_oid_cmp(&actual_oid, &expected_oid) != 0)
+ return 0;
+ }
+
+ git_str_dispose(&wd);
+
+ return 1;
+}
diff --git a/tests/libgit2/merge/merge_helpers.h b/tests/libgit2/merge/merge_helpers.h
new file mode 100644
index 0000000..72c6e61
--- /dev/null
+++ b/tests/libgit2/merge/merge_helpers.h
@@ -0,0 +1,72 @@
+#ifndef INCLUDE_cl_merge_helpers_h__
+#define INCLUDE_cl_merge_helpers_h__
+
+#include "merge.h"
+#include "git2/merge.h"
+
+struct merge_index_entry {
+ uint16_t mode;
+ char oid_str[GIT_OID_SHA1_HEXSIZE+1];
+ int stage;
+ char path[128];
+};
+
+struct merge_name_entry {
+ char ancestor_path[128];
+ char our_path[128];
+ char their_path[128];
+};
+
+struct merge_index_with_status {
+ struct merge_index_entry entry;
+ unsigned int status;
+};
+
+struct merge_reuc_entry {
+ char path[128];
+ unsigned int ancestor_mode;
+ unsigned int our_mode;
+ unsigned int their_mode;
+ char ancestor_oid_str[GIT_OID_SHA1_HEXSIZE+1];
+ char our_oid_str[GIT_OID_SHA1_HEXSIZE+1];
+ char their_oid_str[GIT_OID_SHA1_HEXSIZE+1];
+};
+
+struct merge_index_conflict_data {
+ struct merge_index_with_status ancestor;
+ struct merge_index_with_status ours;
+ struct merge_index_with_status theirs;
+ git_merge_diff_t change_type;
+};
+
+int merge_trees_from_branches(
+ git_index **index, git_repository *repo,
+ const char *ours_name, const char *theirs_name,
+ git_merge_options *opts);
+
+int merge_commits_from_branches(
+ git_index **index, git_repository *repo,
+ const char *ours_name, const char *theirs_name,
+ git_merge_options *opts);
+
+int merge_branches(git_repository *repo,
+ const char *ours_branch, const char *theirs_branch,
+ git_merge_options *merge_opts, git_checkout_options *checkout_opts);
+
+int merge_test_diff_list(git_merge_diff_list *diff_list, const struct merge_index_entry expected[], size_t expected_len);
+
+int merge_test_merge_conflicts(git_vector *conflicts, const struct merge_index_conflict_data expected[], size_t expected_len);
+
+int merge_test_index(git_index *index, const struct merge_index_entry expected[], size_t expected_len);
+
+int merge_test_names(git_index *index, const struct merge_name_entry expected[], size_t expected_len);
+
+int merge_test_reuc(git_index *index, const struct merge_reuc_entry expected[], size_t expected_len);
+
+int merge_test_workdir(git_repository *repo, const struct merge_index_entry expected[], size_t expected_len);
+
+void merge__dump_names(git_index *index);
+void merge__dump_index_entries(git_vector *index_entries);
+void merge__dump_reuc(git_index *index);
+
+#endif
diff --git a/tests/libgit2/merge/trees/automerge.c b/tests/libgit2/merge/trees/automerge.c
new file mode 100644
index 0000000..3bf6c52
--- /dev/null
+++ b/tests/libgit2/merge/trees/automerge.c
@@ -0,0 +1,195 @@
+#include "clar_libgit2.h"
+#include "git2/repository.h"
+#include "git2/merge.h"
+#include "merge.h"
+#include "futils.h"
+#include "../merge_helpers.h"
+#include "../conflict_data.h"
+
+static git_repository *repo;
+
+#define TEST_REPO_PATH "merge-resolve"
+#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
+
+#define THEIRS_AUTOMERGE_BRANCH "branch"
+
+#define THEIRS_UNRELATED_BRANCH "unrelated"
+#define THEIRS_UNRELATED_OID "55b4e4687e7a0d9ca367016ed930f385d4022e6f"
+#define THEIRS_UNRELATED_PARENT "d6cf6c7741b3316826af1314042550c97ded1d50"
+
+#define OURS_DIRECTORY_FILE "df_side1"
+#define THEIRS_DIRECTORY_FILE "df_side2"
+
+/* Non-conflicting files, index entries are common to every merge operation */
+#define ADDED_IN_MASTER_INDEX_ENTRY \
+ { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" }
+#define AUTOMERGEABLE_INDEX_ENTRY \
+ { 0100644, "f2e1550a0c9e53d5811175864a29536642ae3821", 0, "automergeable.txt" }
+#define CHANGED_IN_BRANCH_INDEX_ENTRY \
+ { 0100644, "4eb04c9e79e88f6640d01ff5b25ca2a60764f216", 0, "changed-in-branch.txt" }
+#define CHANGED_IN_MASTER_INDEX_ENTRY \
+ { 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt" }
+#define UNCHANGED_INDEX_ENTRY \
+ { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt" }
+
+/* Expected REUC entries */
+#define AUTOMERGEABLE_REUC_ENTRY \
+ { "automergeable.txt", 0100644, 0100644, 0100644, \
+ "6212c31dab5e482247d7977e4f0dd3601decf13b", \
+ "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", \
+ "058541fc37114bfc1dddf6bd6bffc7fae5c2e6fe" }
+#define CONFLICTING_REUC_ENTRY \
+ { "conflicting.txt", 0100644, 0100644, 0100644, \
+ "d427e0b2e138501a3d15cc376077a3631e15bd46", \
+ "4e886e602529caa9ab11d71f86634bd1b6e0de10", \
+ "2bd0a343aeef7a2cf0d158478966a6e587ff3863" }
+#define REMOVED_IN_BRANCH_REUC_ENTRY \
+ { "removed-in-branch.txt", 0100644, 0100644, 0, \
+ "dfe3f22baa1f6fce5447901c3086bae368de6bdd", \
+ "dfe3f22baa1f6fce5447901c3086bae368de6bdd", \
+ "" }
+#define REMOVED_IN_MASTER_REUC_ENTRY \
+ { "removed-in-master.txt", 0100644, 0, 0100644, \
+ "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", \
+ "", \
+ "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5" }
+
+/* Fixture setup and teardown */
+void test_merge_trees_automerge__initialize(void)
+{
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+}
+
+void test_merge_trees_automerge__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_merge_trees_automerge__automerge(void)
+{
+ git_index *index;
+ const git_index_entry *entry;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+ git_blob *blob;
+
+ struct merge_index_entry merge_index_entries[] = {
+ ADDED_IN_MASTER_INDEX_ENTRY,
+ AUTOMERGEABLE_INDEX_ENTRY,
+ CHANGED_IN_BRANCH_INDEX_ENTRY,
+ CHANGED_IN_MASTER_INDEX_ENTRY,
+
+ { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 1, "conflicting.txt" },
+ { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" },
+ { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 3, "conflicting.txt" },
+
+ UNCHANGED_INDEX_ENTRY,
+ };
+
+ struct merge_reuc_entry merge_reuc_entries[] = {
+ AUTOMERGEABLE_REUC_ENTRY,
+ REMOVED_IN_BRANCH_REUC_ENTRY,
+ REMOVED_IN_MASTER_REUC_ENTRY
+ };
+
+ cl_git_pass(merge_trees_from_branches(&index, repo, "master", THEIRS_AUTOMERGE_BRANCH, &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 8));
+ cl_assert(merge_test_reuc(index, merge_reuc_entries, 3));
+
+ cl_assert((entry = git_index_get_bypath(index, "automergeable.txt", 0)) != NULL);
+ cl_assert(entry->file_size == strlen(AUTOMERGEABLE_MERGED_FILE));
+
+ cl_git_pass(git_object_lookup((git_object **)&blob, repo, &entry->id, GIT_OBJECT_BLOB));
+ cl_assert(memcmp(git_blob_rawcontent(blob), AUTOMERGEABLE_MERGED_FILE, (size_t)entry->file_size) == 0);
+
+ git_index_free(index);
+ git_blob_free(blob);
+}
+
+void test_merge_trees_automerge__favor_ours(void)
+{
+ git_index *index;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ ADDED_IN_MASTER_INDEX_ENTRY,
+ AUTOMERGEABLE_INDEX_ENTRY,
+ CHANGED_IN_BRANCH_INDEX_ENTRY,
+ CHANGED_IN_MASTER_INDEX_ENTRY,
+ { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 0, "conflicting.txt" },
+ UNCHANGED_INDEX_ENTRY,
+ };
+
+ struct merge_reuc_entry merge_reuc_entries[] = {
+ AUTOMERGEABLE_REUC_ENTRY,
+ CONFLICTING_REUC_ENTRY,
+ REMOVED_IN_BRANCH_REUC_ENTRY,
+ REMOVED_IN_MASTER_REUC_ENTRY,
+ };
+
+ opts.file_favor = GIT_MERGE_FILE_FAVOR_OURS;
+
+ cl_git_pass(merge_trees_from_branches(&index, repo, "master", THEIRS_AUTOMERGE_BRANCH, &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 6));
+ cl_assert(merge_test_reuc(index, merge_reuc_entries, 4));
+
+ git_index_free(index);
+}
+
+void test_merge_trees_automerge__favor_theirs(void)
+{
+ git_index *index;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ ADDED_IN_MASTER_INDEX_ENTRY,
+ AUTOMERGEABLE_INDEX_ENTRY,
+ CHANGED_IN_BRANCH_INDEX_ENTRY,
+ CHANGED_IN_MASTER_INDEX_ENTRY,
+ { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 0, "conflicting.txt" },
+ UNCHANGED_INDEX_ENTRY,
+ };
+
+ struct merge_reuc_entry merge_reuc_entries[] = {
+ AUTOMERGEABLE_REUC_ENTRY,
+ CONFLICTING_REUC_ENTRY,
+ REMOVED_IN_BRANCH_REUC_ENTRY,
+ REMOVED_IN_MASTER_REUC_ENTRY,
+ };
+
+ opts.file_favor = GIT_MERGE_FILE_FAVOR_THEIRS;
+
+ cl_git_pass(merge_trees_from_branches(&index, repo, "master", THEIRS_AUTOMERGE_BRANCH, &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 6));
+ cl_assert(merge_test_reuc(index, merge_reuc_entries, 4));
+
+ git_index_free(index);
+}
+
+void test_merge_trees_automerge__unrelated(void)
+{
+ git_index *index;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" },
+ { 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 2, "automergeable.txt" },
+ { 0100644, "d07ec190c306ec690bac349e87d01c4358e49bb2", 3, "automergeable.txt" },
+ { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-branch.txt" },
+ { 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt" },
+ { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" },
+ { 0100644, "4b253da36a0ae8bfce63aeabd8c5b58429925594", 3, "conflicting.txt" },
+ { 0100644, "ef58fdd8086c243bdc81f99e379acacfd21d32d6", 0, "new-in-unrelated1.txt" },
+ { 0100644, "948ba6e701c1edab0c2d394fb7c5538334129793", 0, "new-in-unrelated2.txt" },
+ { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt" },
+ { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt" },
+ };
+
+ cl_git_pass(merge_trees_from_branches(&index, repo, "master", THEIRS_UNRELATED_BRANCH, &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 11));
+
+ git_index_free(index);
+}
diff --git a/tests/libgit2/merge/trees/commits.c b/tests/libgit2/merge/trees/commits.c
new file mode 100644
index 0000000..79fba8a
--- /dev/null
+++ b/tests/libgit2/merge/trees/commits.c
@@ -0,0 +1,148 @@
+#include "clar_libgit2.h"
+#include "git2/repository.h"
+#include "git2/merge.h"
+#include "merge.h"
+#include "../merge_helpers.h"
+#include "../conflict_data.h"
+
+static git_repository *repo;
+
+#define TEST_REPO_PATH "merge-resolve"
+
+void test_merge_trees_commits__initialize(void)
+{
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+}
+
+void test_merge_trees_commits__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_merge_trees_commits__automerge(void)
+{
+ git_index *index;
+ const git_index_entry *entry;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+ git_blob *blob;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" },
+ { 0100644, "f2e1550a0c9e53d5811175864a29536642ae3821", 0, "automergeable.txt" },
+ { 0100644, "4eb04c9e79e88f6640d01ff5b25ca2a60764f216", 0, "changed-in-branch.txt" },
+ { 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt" },
+
+ { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 1, "conflicting.txt" },
+ { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" },
+ { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 3, "conflicting.txt" },
+
+ { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt" },
+ };
+
+ struct merge_reuc_entry merge_reuc_entries[] = {
+ { "automergeable.txt", 0100644, 0100644, 0100644, \
+ "6212c31dab5e482247d7977e4f0dd3601decf13b", \
+ "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", \
+ "058541fc37114bfc1dddf6bd6bffc7fae5c2e6fe" },
+ { "removed-in-branch.txt", 0100644, 0100644, 0, \
+ "dfe3f22baa1f6fce5447901c3086bae368de6bdd", \
+ "dfe3f22baa1f6fce5447901c3086bae368de6bdd", \
+ "" },
+ { "removed-in-master.txt", 0100644, 0, 0100644, \
+ "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", \
+ "", \
+ "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5" },
+ };
+
+ cl_git_pass(merge_commits_from_branches(&index, repo, "master", "branch", &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 8));
+ cl_assert(merge_test_reuc(index, merge_reuc_entries, 3));
+
+ cl_assert((entry = git_index_get_bypath(index, "automergeable.txt", 0)) != NULL);
+ cl_assert(entry->file_size == strlen(AUTOMERGEABLE_MERGED_FILE));
+
+ cl_git_pass(git_object_lookup((git_object **)&blob, repo, &entry->id, GIT_OBJECT_BLOB));
+ cl_assert(memcmp(git_blob_rawcontent(blob), AUTOMERGEABLE_MERGED_FILE, (size_t)entry->file_size) == 0);
+
+ git_index_free(index);
+ git_blob_free(blob);
+}
+
+void test_merge_trees_commits__no_ancestor(void)
+{
+ git_index *index;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" },
+ { 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 2, "automergeable.txt" },
+ { 0100644, "d07ec190c306ec690bac349e87d01c4358e49bb2", 3, "automergeable.txt" },
+ { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-branch.txt" },
+ { 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt" },
+ { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" },
+ { 0100644, "4b253da36a0ae8bfce63aeabd8c5b58429925594", 3, "conflicting.txt" },
+ { 0100644, "ef58fdd8086c243bdc81f99e379acacfd21d32d6", 0, "new-in-unrelated1.txt" },
+ { 0100644, "948ba6e701c1edab0c2d394fb7c5538334129793", 0, "new-in-unrelated2.txt" },
+ { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt" },
+ { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt" },
+ };
+
+ cl_git_pass(merge_commits_from_branches(&index, repo, "master", "unrelated", &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 11));
+
+ git_index_free(index);
+}
+
+void test_merge_trees_commits__df_conflict(void)
+{
+ git_index *index;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "49130a28ef567af9a6a6104c38773fedfa5f9742", 2, "dir-10" },
+ { 0100644, "6c06dcd163587c2cc18be44857e0b71116382aeb", 3, "dir-10" },
+ { 0100644, "43aafd43bea779ec74317dc361f45ae3f532a505", 0, "dir-6" },
+ { 0100644, "a031a28ae70e33a641ce4b8a8f6317f1ab79dee4", 3, "dir-7" },
+ { 0100644, "5012fd565b1393bdfda1805d4ec38ce6619e1fd1", 1, "dir-7/file.txt" },
+ { 0100644, "a5563304ddf6caba25cb50323a2ea6f7dbfcadca", 2, "dir-7/file.txt" },
+ { 0100644, "e9ad6ec3e38364a3d07feda7c4197d4d845c53b5", 0, "dir-8" },
+ { 0100644, "3ef4d30382ca33fdeba9fda895a99e0891ba37aa", 2, "dir-9" },
+ { 0100644, "fc4c636d6515e9e261f9260dbcf3cc6eca97ea08", 1, "dir-9/file.txt" },
+ { 0100644, "76ab0e2868197ec158ddd6c78d8a0d2fd73d38f9", 3, "dir-9/file.txt" },
+ { 0100644, "5c2411f8075f48a6b2fdb85ebc0d371747c4df15", 0, "file-1/new" },
+ { 0100644, "a39a620dae5bc8b4e771cd4d251b7d080401a21e", 1, "file-2" },
+ { 0100644, "d963979c237d08b6ba39062ee7bf64c7d34a27f8", 2, "file-2" },
+ { 0100644, "5c341ead2ba6f2af98ce5ec3fe84f6b6d2899c0d", 0, "file-2/new" },
+ { 0100644, "9efe7723802d4305142eee177e018fee1572c4f4", 0, "file-3/new" },
+ { 0100644, "bacac9b3493509aa15e1730e1545fc0919d1dae0", 1, "file-4" },
+ { 0100644, "7663fce0130db092936b137cabd693ec234eb060", 3, "file-4" },
+ { 0100644, "e49f917b448d1340b31d76e54ba388268fd4c922", 0, "file-4/new" },
+ { 0100644, "cab2cf23998b40f1af2d9d9a756dc9e285a8df4b", 2, "file-5/new" },
+ { 0100644, "f5504f36e6f4eb797a56fc5bac6c6c7f32969bf2", 3, "file-5/new" },
+ };
+
+ cl_git_pass(merge_trees_from_branches(&index, repo, "df_side1", "df_side2", &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 20));
+
+ git_index_free(index);
+}
+
+void test_merge_trees_commits__fail_on_conflict(void)
+{
+ git_index *index;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+
+ opts.flags |= GIT_MERGE_FAIL_ON_CONFLICT;
+
+ cl_git_fail_with(GIT_EMERGECONFLICT,
+ merge_trees_from_branches(&index, repo, "df_side1", "df_side2", &opts));
+
+ cl_git_fail_with(GIT_EMERGECONFLICT,
+ merge_commits_from_branches(&index, repo, "master", "unrelated", &opts));
+ cl_git_fail_with(GIT_EMERGECONFLICT,
+ merge_commits_from_branches(&index, repo, "master", "branch", &opts));
+}
+
diff --git a/tests/libgit2/merge/trees/modeconflict.c b/tests/libgit2/merge/trees/modeconflict.c
new file mode 100644
index 0000000..a0521c4
--- /dev/null
+++ b/tests/libgit2/merge/trees/modeconflict.c
@@ -0,0 +1,58 @@
+#include "clar_libgit2.h"
+#include "git2/repository.h"
+#include "git2/merge.h"
+#include "merge.h"
+#include "../merge_helpers.h"
+#include "futils.h"
+
+static git_repository *repo;
+
+#define TEST_REPO_PATH "merge-resolve"
+
+#define DF_SIDE1_BRANCH "df_side1"
+#define DF_SIDE2_BRANCH "df_side2"
+
+/* Fixture setup and teardown */
+void test_merge_trees_modeconflict__initialize(void)
+{
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+}
+
+void test_merge_trees_modeconflict__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_merge_trees_modeconflict__df_conflict(void)
+{
+ git_index *index;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "49130a28ef567af9a6a6104c38773fedfa5f9742", 2, "dir-10" },
+ { 0100644, "6c06dcd163587c2cc18be44857e0b71116382aeb", 3, "dir-10" },
+ { 0100644, "43aafd43bea779ec74317dc361f45ae3f532a505", 0, "dir-6" },
+ { 0100644, "a031a28ae70e33a641ce4b8a8f6317f1ab79dee4", 3, "dir-7" },
+ { 0100644, "5012fd565b1393bdfda1805d4ec38ce6619e1fd1", 1, "dir-7/file.txt" },
+ { 0100644, "a5563304ddf6caba25cb50323a2ea6f7dbfcadca", 2, "dir-7/file.txt" },
+ { 0100644, "e9ad6ec3e38364a3d07feda7c4197d4d845c53b5", 0, "dir-8" },
+ { 0100644, "3ef4d30382ca33fdeba9fda895a99e0891ba37aa", 2, "dir-9" },
+ { 0100644, "fc4c636d6515e9e261f9260dbcf3cc6eca97ea08", 1, "dir-9/file.txt" },
+ { 0100644, "76ab0e2868197ec158ddd6c78d8a0d2fd73d38f9", 3, "dir-9/file.txt" },
+ { 0100644, "5c2411f8075f48a6b2fdb85ebc0d371747c4df15", 0, "file-1/new" },
+ { 0100644, "a39a620dae5bc8b4e771cd4d251b7d080401a21e", 1, "file-2" },
+ { 0100644, "d963979c237d08b6ba39062ee7bf64c7d34a27f8", 2, "file-2" },
+ { 0100644, "5c341ead2ba6f2af98ce5ec3fe84f6b6d2899c0d", 0, "file-2/new" },
+ { 0100644, "9efe7723802d4305142eee177e018fee1572c4f4", 0, "file-3/new" },
+ { 0100644, "bacac9b3493509aa15e1730e1545fc0919d1dae0", 1, "file-4" },
+ { 0100644, "7663fce0130db092936b137cabd693ec234eb060", 3, "file-4" },
+ { 0100644, "e49f917b448d1340b31d76e54ba388268fd4c922", 0, "file-4/new" },
+ { 0100644, "cab2cf23998b40f1af2d9d9a756dc9e285a8df4b", 2, "file-5/new" },
+ { 0100644, "f5504f36e6f4eb797a56fc5bac6c6c7f32969bf2", 3, "file-5/new" },
+ };
+
+ cl_git_pass(merge_trees_from_branches(&index, repo, DF_SIDE1_BRANCH, DF_SIDE2_BRANCH, NULL));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 20));
+
+ git_index_free(index);
+}
diff --git a/tests/libgit2/merge/trees/recursive.c b/tests/libgit2/merge/trees/recursive.c
new file mode 100644
index 0000000..71f5af1
--- /dev/null
+++ b/tests/libgit2/merge/trees/recursive.c
@@ -0,0 +1,458 @@
+#include "clar_libgit2.h"
+#include "git2/repository.h"
+#include "git2/merge.h"
+#include "merge.h"
+#include "../merge_helpers.h"
+
+static git_repository *repo;
+
+#define TEST_REPO_PATH "merge-recursive"
+
+void test_merge_trees_recursive__initialize(void)
+{
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+}
+
+void test_merge_trees_recursive__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_merge_trees_recursive__one_base_commit(void)
+{
+ git_index *index;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "dea7215f259b2cced87d1bda6c72f8b4ce37a2ff", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" },
+ };
+
+ cl_git_pass(merge_commits_from_branches(&index, repo, "branchA-1", "branchA-2", &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 6));
+
+ git_index_free(index);
+}
+
+void test_merge_trees_recursive__one_base_commit_norecursive(void)
+{
+ git_index *index;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "dea7215f259b2cced87d1bda6c72f8b4ce37a2ff", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "94d2c01087f48213bd157222d54edfefd77c9bba", 0, "veal.txt" },
+ };
+
+ opts.flags |= GIT_MERGE_NO_RECURSIVE;
+
+ cl_git_pass(merge_commits_from_branches(&index, repo, "branchA-1", "branchA-2", &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 6));
+
+ git_index_free(index);
+}
+
+void test_merge_trees_recursive__two_base_commits(void)
+{
+ git_index *index;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "666ffdfcf1eaa5641fa31064bf2607327e843c09", 0, "veal.txt" },
+ };
+
+ cl_git_pass(merge_commits_from_branches(&index, repo, "branchB-1", "branchB-2", &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 6));
+
+ git_index_free(index);
+}
+
+void test_merge_trees_recursive__two_base_commits_norecursive(void)
+{
+ git_index *index;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "cb49ad76147f5f9439cbd6133708b76142660660", 1, "veal.txt" },
+ { 0100644, "b2a81ead9e722af0099fccfb478cea88eea749a2", 2, "veal.txt" },
+ { 0100644, "4e21d2d63357bde5027d1625f5ec6b430cdeb143", 3, "veal.txt" },
+ };
+
+ opts.flags |= GIT_MERGE_NO_RECURSIVE;
+
+ cl_git_pass(merge_commits_from_branches(&index, repo, "branchB-1", "branchB-2", &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 8));
+
+ git_index_free(index);
+}
+
+void test_merge_trees_recursive__two_levels_of_multiple_bases(void)
+{
+ git_index *index;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "15faa0c9991f2d65686e844651faa2ff9827887b", 0, "veal.txt" },
+ };
+
+ cl_git_pass(merge_commits_from_branches(&index, repo, "branchC-1", "branchC-2", &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 6));
+
+ git_index_free(index);
+}
+
+void test_merge_trees_recursive__two_levels_of_multiple_bases_norecursive(void)
+{
+ git_index *index;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "b2a81ead9e722af0099fccfb478cea88eea749a2", 1, "veal.txt" },
+ { 0100644, "898d12687fb35be271c27c795a6b32c8b51da79e", 2, "veal.txt" },
+ { 0100644, "68a2e1ee61a23a4728fe6b35580fbbbf729df370", 3, "veal.txt" },
+ };
+
+ opts.flags |= GIT_MERGE_NO_RECURSIVE;
+
+ cl_git_pass(merge_commits_from_branches(&index, repo, "branchC-1", "branchC-2", &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 8));
+
+ git_index_free(index);
+}
+
+void test_merge_trees_recursive__three_levels_of_multiple_bases(void)
+{
+ git_index *index;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "d55e5dc038c52f1a36548625bcb666cbc06db9e6", 0, "veal.txt" },
+ };
+
+ cl_git_pass(merge_commits_from_branches(&index, repo, "branchD-2", "branchD-1", &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 6));
+
+ git_index_free(index);
+}
+
+void test_merge_trees_recursive__three_levels_of_multiple_bases_norecursive(void)
+{
+ git_index *index;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "898d12687fb35be271c27c795a6b32c8b51da79e", 1, "veal.txt" },
+ { 0100644, "f1b44c04989a3a1c14b036cfadfa328d53a7bc5e", 2, "veal.txt" },
+ { 0100644, "5e8747f5200fac0f945a07daf6163ca9cb1a8da9", 3, "veal.txt" },
+ };
+
+ opts.flags |= GIT_MERGE_NO_RECURSIVE;
+
+ cl_git_pass(merge_commits_from_branches(&index, repo, "branchD-2", "branchD-1", &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 8));
+
+ git_index_free(index);
+}
+
+void test_merge_trees_recursive__three_base_commits(void)
+{
+ git_index *index;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4f7269b07c76d02755d75ccaf05c0b4c36cdc6c", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
+ };
+
+ cl_git_pass(merge_commits_from_branches(&index, repo, "branchE-1", "branchE-2", &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 6));
+
+ git_index_free(index);
+}
+
+void test_merge_trees_recursive__three_base_commits_norecursive(void)
+{
+ git_index *index;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "9e12bce04446d097ae1782967a5888c2e2a0d35b", 1, "gravy.txt" },
+ { 0100644, "d8dd349b78f19a4ebe3357bacb8138f00bf5ed41", 2, "gravy.txt" },
+ { 0100644, "e50fbbd701458757bdfe9815f58ed717c588d1b5", 3, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "a7b066537e6be7109abfe4ff97b675d4e077da20", 0, "veal.txt" },
+ };
+
+ opts.flags |= GIT_MERGE_NO_RECURSIVE;
+
+ cl_git_pass(merge_commits_from_branches(&index, repo, "branchE-1", "branchE-2", &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 8));
+
+ git_index_free(index);
+}
+
+void test_merge_trees_recursive__conflict(void)
+{
+ git_index *index;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "fa567f568ed72157c0c617438d077695b99d9aac", 1, "veal.txt" },
+ { 0100644, "21950d5e4e4d1a871b4dfcf72ecb6b9c162c434e", 2, "veal.txt" },
+ { 0100644, "3855170cef875708da06ab9ad7fc6a73b531cda1", 3, "veal.txt" },
+ };
+
+ cl_git_pass(merge_commits_from_branches(&index, repo, "branchF-1", "branchF-2", &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 8));
+
+ git_index_free(index);
+}
+
+/*
+ * Branch G-1 and G-2 have three common ancestors (815b5a1, ad2ace9, 483065d).
+ * The merge-base of the first two has two common ancestors (723181f, a34e5a1)
+ * which themselves have two common ancestors (8f35f30, 3a3f5a6), which
+ * finally has a common ancestor of 7c7bf85. This virtual merge base will
+ * be computed and merged with 483065d which also has a common ancestor of
+ * 7c7bf85.
+ */
+void test_merge_trees_recursive__oh_so_many_levels_of_recursion(void)
+{
+ git_index *index;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "7c7e08f9559d9e1551b91e1cf68f1d0066109add", 0, "oyster.txt" },
+ { 0100644, "898d12687fb35be271c27c795a6b32c8b51da79e", 0, "veal.txt" },
+ };
+
+ cl_git_pass(merge_commits_from_branches(&index, repo, "branchG-1", "branchG-2", &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 6));
+
+ git_index_free(index);
+}
+
+/* Branch H-1 and H-2 have two common ancestors (aa9e263, 6ef31d3). The two
+ * ancestors themselves conflict.
+ */
+void test_merge_trees_recursive__conflicting_merge_base(void)
+{
+ git_index *index;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "cfc01b0976122eae42a82064440bbf534eddd7a0", 1, "veal.txt" },
+ { 0100644, "d604c75019c282144bdbbf3fd3462ba74b240efc", 2, "veal.txt" },
+ { 0100644, "37a5054a9f9b4628e3924c5cb8f2147c6e2a3efc", 3, "veal.txt" },
+ };
+
+ cl_git_pass(merge_commits_from_branches(&index, repo, "branchH-1", "branchH-2", &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 8));
+
+ git_index_free(index);
+}
+
+/* Branch H-1 and H-2 have two common ancestors (aa9e263, 6ef31d3). The two
+ * ancestors themselves conflict. The generated common ancestor file will
+ * have diff3 style conflicts inside it.
+ */
+void test_merge_trees_recursive__conflicting_merge_base_with_diff3(void)
+{
+ git_index *index;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "0b01d2f70a1c6b9ab60c382f3f9cdc8173da6736", 1, "veal.txt" },
+ { 0100644, "37a5054a9f9b4628e3924c5cb8f2147c6e2a3efc", 2, "veal.txt" },
+ { 0100644, "d604c75019c282144bdbbf3fd3462ba74b240efc", 3, "veal.txt" },
+ };
+
+ opts.file_flags |= GIT_MERGE_FILE_STYLE_DIFF3;
+
+ cl_git_pass(merge_commits_from_branches(&index, repo, "branchH-2", "branchH-1", &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 8));
+
+ git_index_free(index);
+}
+
+/* Branch I-1 and I-2 have two common ancestors (aa9e263, 6ef31d3). The two
+ * ancestors themselves conflict, but when each was merged, the conflicts were
+ * resolved identically, thus merging I-1 into I-2 does not conflict.
+ */
+void test_merge_trees_recursive__conflicting_merge_base_since_resolved(void)
+{
+ git_index *index;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "a02d4fd126e0cc8fb46ee48cf38bad36d44f2dbc", 0, "veal.txt" },
+ };
+
+ cl_git_pass(merge_commits_from_branches(&index, repo, "branchI-1", "branchI-2", &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 6));
+
+ git_index_free(index);
+}
+
+/* There are multiple levels of criss-cross merges, and multiple recursive
+ * merges would create a common ancestor that allows the merge to complete
+ * successfully. Test that we can build a single virtual base, then stop,
+ * which will produce a conflicting merge.
+ */
+void test_merge_trees_recursive__recursionlimit(void)
+{
+ git_index *index;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "53217e8ac3f52bccf7603b8fff0ed0f4817f9bb7", 1, "veal.txt" },
+ { 0100644, "898d12687fb35be271c27c795a6b32c8b51da79e", 2, "veal.txt" },
+ { 0100644, "68a2e1ee61a23a4728fe6b35580fbbbf729df370", 3, "veal.txt" },
+ };
+
+ opts.recursion_limit = 1;
+
+ cl_git_pass(merge_commits_from_branches(&index, repo, "branchC-1", "branchC-2", &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 8));
+
+ git_index_free(index);
+}
+
+/* There are multiple levels of criss-cross merges. This ensures
+ * that the virtual merge base parents are compared in the same
+ * order as git. If the base parents are created in the order as
+ * git does, then the file `targetfile.txt` is automerged. If not,
+ * `targetfile.txt` will be in conflict due to the virtual merge
+ * base.
+ */
+void test_merge_trees_recursive__merge_base_for_virtual_commit(void)
+{
+ git_index *index;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "1bde1883de4977ea3e664b315da951d1f614c3b1", 0, "targetfile.txt" },
+ { 0100644, "b7de2b52ba055688061355fad1599a5d214ce8f8", 1, "version.txt" },
+ { 0100644, "358efd6f589384fa8baf92234db9c7899a53916e", 2, "version.txt" },
+ { 0100644, "a664873b1c0b9a1ed300f8644dde536fdaa3a34f", 3, "version.txt" },
+ };
+
+ cl_git_pass(merge_commits_from_branches(&index, repo, "branchJ-1", "branchJ-2", &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 4));
+
+ git_index_free(index);
+}
+
+/* This test is the same as above, but the graph is constructed such
+ * that the 1st-recursion merge bases of the two heads are
+ * in a different order.
+ */
+void test_merge_trees_recursive__merge_base_for_virtual_commit_2(void)
+{
+ git_index *index;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "4a06b258fed8a4d15967ec4253ae7366b70f727d", 0, "targetfile.txt" },
+ { 0100644, "b6bd0f9952f396e757d3f91e08c59a7e91707201", 1, "version.txt" },
+ { 0100644, "f0856993e005c0d8ed2dc7cdc222cc1d89fb3c77", 2, "version.txt" },
+ { 0100644, "2cba583804a4a6fad1baf97c959be447238d1489", 3, "version.txt" },
+ };
+
+ cl_git_pass(merge_commits_from_branches(&index, repo, "branchK-1", "branchK-2", &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 4));
+
+ git_index_free(index);
+}
diff --git a/tests/libgit2/merge/trees/renames.c b/tests/libgit2/merge/trees/renames.c
new file mode 100644
index 0000000..a27945e
--- /dev/null
+++ b/tests/libgit2/merge/trees/renames.c
@@ -0,0 +1,352 @@
+#include "clar_libgit2.h"
+#include "git2/repository.h"
+#include "git2/merge.h"
+#include "merge.h"
+#include "../merge_helpers.h"
+#include "futils.h"
+
+static git_repository *repo;
+
+#define TEST_REPO_PATH "merge-resolve"
+
+#define BRANCH_RENAME_OURS "rename_conflict_ours"
+#define BRANCH_RENAME_THEIRS "rename_conflict_theirs"
+
+/* Fixture setup and teardown */
+void test_merge_trees_renames__initialize(void)
+{
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+}
+
+void test_merge_trees_renames__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_merge_trees_renames__index(void)
+{
+ git_index *index;
+ git_merge_options *opts = NULL;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e", 0, "0a-no-change.txt" },
+ { 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-duplicated-in-ours.txt" },
+ { 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 1, "0b-rewritten-in-ours.txt" },
+ { 0100644, "e376fbdd06ebf021c92724da9f26f44212734e3e", 2, "0b-rewritten-in-ours.txt" },
+ { 0100644, "b2d399ae15224e1d58066e3c8df70ce37de7a656", 3, "0b-rewritten-in-ours.txt" },
+ { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-duplicated-in-theirs.txt" },
+ { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 1, "0c-rewritten-in-theirs.txt" },
+ { 0100644, "efc9121fdedaf08ba180b53ebfbcf71bd488ed09", 2, "0c-rewritten-in-theirs.txt" },
+ { 0100644, "712ebba6669ea847d9829e4f1059d6c830c8b531", 3, "0c-rewritten-in-theirs.txt" },
+ { 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638", 0, "1a-newname-in-ours-edited-in-theirs.txt" },
+ { 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-newname-in-ours.txt" },
+ { 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a", 0, "1b-newname-in-theirs-edited-in-ours.txt" },
+ { 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-newname-in-theirs.txt" },
+ { 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt" },
+ { 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 2, "3a-newname-in-ours-deleted-in-theirs.txt" },
+ { 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 1, "3a-renamed-in-ours-deleted-in-theirs.txt" },
+ { 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 3, "3b-newname-in-theirs-deleted-in-ours.txt" },
+ { 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 1, "3b-renamed-in-theirs-deleted-in-ours.txt" },
+ { 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 2, "4a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "8b5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a", 3, "4a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 1, "4a-renamed-in-ours-added-in-theirs.txt" },
+ { 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9", 2, "4b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 3, "4b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 1, "4b-renamed-in-theirs-added-in-ours.txt" },
+ { 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 2, "5a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "98ba4205fcf31f5dd93c916d35fe3f3b3d0e6714", 3, "5a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 1, "5a-renamed-in-ours-added-in-theirs.txt" },
+ { 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 3, "5a-renamed-in-ours-added-in-theirs.txt" },
+ { 0100644, "385c8a0f26ddf79e9041e15e17dc352ed2c4cced", 2, "5b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 3, "5b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 1, "5b-renamed-in-theirs-added-in-ours.txt" },
+ { 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 2, "5b-renamed-in-theirs-added-in-ours.txt" },
+ { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 2, "6-both-renamed-1-to-2-ours.txt" },
+ { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 3, "6-both-renamed-1-to-2-theirs.txt" },
+ { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 1, "6-both-renamed-1-to-2.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 1, "7-both-renamed-side-1.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 3, "7-both-renamed-side-1.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 1, "7-both-renamed-side-2.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 2, "7-both-renamed-side-2.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 2, "7-both-renamed.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 3, "7-both-renamed.txt" },
+ };
+
+ struct merge_name_entry merge_name_entries[] = {
+ {
+ "3a-renamed-in-ours-deleted-in-theirs.txt",
+ "3a-newname-in-ours-deleted-in-theirs.txt",
+ ""
+ },
+
+ {
+ "3b-renamed-in-theirs-deleted-in-ours.txt",
+ "",
+ "3b-newname-in-theirs-deleted-in-ours.txt",
+ },
+
+ {
+ "4a-renamed-in-ours-added-in-theirs.txt",
+ "4a-newname-in-ours-added-in-theirs.txt",
+ "",
+ },
+
+ {
+ "4b-renamed-in-theirs-added-in-ours.txt",
+ "",
+ "4b-newname-in-theirs-added-in-ours.txt",
+ },
+
+ {
+ "5a-renamed-in-ours-added-in-theirs.txt",
+ "5a-newname-in-ours-added-in-theirs.txt",
+ "5a-renamed-in-ours-added-in-theirs.txt",
+ },
+
+ {
+ "5b-renamed-in-theirs-added-in-ours.txt",
+ "5b-renamed-in-theirs-added-in-ours.txt",
+ "5b-newname-in-theirs-added-in-ours.txt",
+ },
+
+ {
+ "6-both-renamed-1-to-2.txt",
+ "6-both-renamed-1-to-2-ours.txt",
+ "6-both-renamed-1-to-2-theirs.txt",
+ },
+
+ {
+ "7-both-renamed-side-1.txt",
+ "7-both-renamed.txt",
+ "7-both-renamed-side-1.txt",
+ },
+
+ {
+ "7-both-renamed-side-2.txt",
+ "7-both-renamed-side-2.txt",
+ "7-both-renamed.txt",
+ },
+ };
+
+ struct merge_reuc_entry merge_reuc_entries[] = {
+ { "1a-newname-in-ours-edited-in-theirs.txt",
+ 0, 0100644, 0,
+ "",
+ "c3d02eeef75183df7584d8d13ac03053910c1301",
+ "" },
+
+ { "1a-newname-in-ours.txt",
+ 0, 0100644, 0,
+ "",
+ "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb",
+ "" },
+
+ { "1a-renamed-in-ours-edited-in-theirs.txt",
+ 0100644, 0, 0100644,
+ "c3d02eeef75183df7584d8d13ac03053910c1301",
+ "",
+ "0d872f8e871a30208305978ecbf9e66d864f1638" },
+
+ { "1a-renamed-in-ours.txt",
+ 0100644, 0, 0100644,
+ "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb",
+ "",
+ "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb" },
+
+ { "1b-newname-in-theirs-edited-in-ours.txt",
+ 0, 0, 0100644,
+ "",
+ "",
+ "241a1005cd9b980732741b74385b891142bcba28" },
+
+ { "1b-newname-in-theirs.txt",
+ 0, 0, 0100644,
+ "",
+ "",
+ "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136" },
+
+ { "1b-renamed-in-theirs-edited-in-ours.txt",
+ 0100644, 0100644, 0,
+ "241a1005cd9b980732741b74385b891142bcba28",
+ "ed9523e62e453e50dd9be1606af19399b96e397a",
+ "" },
+
+ { "1b-renamed-in-theirs.txt",
+ 0100644, 0100644, 0,
+ "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136",
+ "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136",
+ "" },
+
+ { "2-newname-in-both.txt",
+ 0, 0100644, 0100644,
+ "",
+ "178940b450f238a56c0d75b7955cb57b38191982",
+ "178940b450f238a56c0d75b7955cb57b38191982" },
+
+ { "2-renamed-in-both.txt",
+ 0100644, 0, 0,
+ "178940b450f238a56c0d75b7955cb57b38191982",
+ "",
+ "" },
+ };
+
+ cl_git_pass(merge_trees_from_branches(&index, repo,
+ BRANCH_RENAME_OURS, BRANCH_RENAME_THEIRS,
+ opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 41));
+ cl_assert(merge_test_names(index, merge_name_entries, 9));
+ cl_assert(merge_test_reuc(index, merge_reuc_entries, 10));
+
+ git_index_free(index);
+}
+
+void test_merge_trees_renames__no_rename_index(void)
+{
+ git_index *index;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e", 0, "0a-no-change.txt" },
+ { 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-duplicated-in-ours.txt" },
+ { 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 1, "0b-rewritten-in-ours.txt" },
+ { 0100644, "e376fbdd06ebf021c92724da9f26f44212734e3e", 2, "0b-rewritten-in-ours.txt" },
+ { 0100644, "b2d399ae15224e1d58066e3c8df70ce37de7a656", 3, "0b-rewritten-in-ours.txt" },
+ { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-duplicated-in-theirs.txt" },
+ { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 1, "0c-rewritten-in-theirs.txt" },
+ { 0100644, "efc9121fdedaf08ba180b53ebfbcf71bd488ed09", 2, "0c-rewritten-in-theirs.txt" },
+ { 0100644, "712ebba6669ea847d9829e4f1059d6c830c8b531", 3, "0c-rewritten-in-theirs.txt" },
+ { 0100644, "c3d02eeef75183df7584d8d13ac03053910c1301", 0, "1a-newname-in-ours-edited-in-theirs.txt" },
+ { 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-newname-in-ours.txt" },
+ { 0100644, "c3d02eeef75183df7584d8d13ac03053910c1301", 1, "1a-renamed-in-ours-edited-in-theirs.txt" },
+ { 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638", 3, "1a-renamed-in-ours-edited-in-theirs.txt" },
+ { 0100644, "241a1005cd9b980732741b74385b891142bcba28", 0, "1b-newname-in-theirs-edited-in-ours.txt" },
+ { 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-newname-in-theirs.txt" },
+ { 0100644, "241a1005cd9b980732741b74385b891142bcba28", 1, "1b-renamed-in-theirs-edited-in-ours.txt" },
+ { 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a", 2, "1b-renamed-in-theirs-edited-in-ours.txt" },
+ { 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt" },
+ { 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 0, "3a-newname-in-ours-deleted-in-theirs.txt" },
+ { 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 0, "3b-newname-in-theirs-deleted-in-ours.txt" },
+ { 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 2, "4a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "8b5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a", 3, "4a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9", 2, "4b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 3, "4b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 2, "5a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "98ba4205fcf31f5dd93c916d35fe3f3b3d0e6714", 3, "5a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "385c8a0f26ddf79e9041e15e17dc352ed2c4cced", 2, "5b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 3, "5b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "6-both-renamed-1-to-2-ours.txt" },
+ { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "6-both-renamed-1-to-2-theirs.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 2, "7-both-renamed.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 3, "7-both-renamed.txt" },
+ };
+
+ opts.flags &= ~GIT_MERGE_FIND_RENAMES;
+
+ cl_git_pass(merge_trees_from_branches(&index, repo,
+ BRANCH_RENAME_OURS, BRANCH_RENAME_THEIRS,
+ &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 32));
+
+ git_index_free(index);
+}
+
+void test_merge_trees_renames__submodules(void)
+{
+ git_index *index;
+ git_merge_options *opts = NULL;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "cd3e8d4aa06bdc781f264171030bc28f2b370fee", 0, ".gitmodules" },
+ { 0100644, "4dd1ef7569b18d92d93c0a35bb6b93049137b355", 1, "file.txt" },
+ { 0100644, "a2d8d1824c68541cca94ffb90f79291eba495921", 2, "file.txt" },
+ { 0100644, "63ec604d491161ddafdae4179843c26d54bd999a", 3, "file.txt" },
+ { 0160000, "0000000000000000000000000000000000000001", 1, "submodule1" },
+ { 0160000, "0000000000000000000000000000000000000002", 3, "submodule1" },
+ { 0160000, "0000000000000000000000000000000000000003", 0, "submodule2" },
+ };
+
+ cl_git_pass(merge_trees_from_branches(&index, repo,
+ "submodule_rename1", "submodule_rename2",
+ opts));
+ cl_assert(merge_test_index(index, merge_index_entries, 7));
+ git_index_free(index);
+}
+
+void test_merge_trees_renames__cache_recomputation(void)
+{
+ git_oid blob, binary, ancestor_oid, theirs_oid, ours_oid;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+ git_str path = GIT_STR_INIT;
+ git_treebuilder *builder;
+ git_tree *ancestor_tree, *their_tree, *our_tree;
+ git_index *index;
+ size_t blob_size;
+ void *data;
+ size_t i;
+
+ cl_git_pass(git_oid__fromstr(&blob, "a2d8d1824c68541cca94ffb90f79291eba495921", GIT_OID_SHA1));
+
+ /*
+ * Create a 50MB blob that consists of NUL bytes only. It is important
+ * that this blob is of a special format, most importantly it cannot
+ * contain more than four non-consecutive newlines or NUL bytes. This
+ * is because of git_hashsig's inner workings where all files with less
+ * than four "lines" are deemed to small.
+ */
+ blob_size = 50 * 1024 * 1024;
+ cl_assert(data = git__calloc(blob_size, 1));
+ cl_git_pass(git_blob_create_from_buffer(&binary, repo, data, blob_size));
+
+ /*
+ * Create the common ancestor, which has 1000 dummy blobs and the binary
+ * blob. The dummy blobs serve as potential rename targets for the
+ * dummy blob.
+ */
+ cl_git_pass(git_treebuilder_new(&builder, repo, NULL));
+ for (i = 0; i < 1000; i++) {
+ cl_git_pass(git_str_printf(&path, "%"PRIuZ".txt", i));
+ cl_git_pass(git_treebuilder_insert(NULL, builder, path.ptr, &blob, GIT_FILEMODE_BLOB));
+ git_str_clear(&path);
+ }
+ cl_git_pass(git_treebuilder_insert(NULL, builder, "original.bin", &binary, GIT_FILEMODE_BLOB));
+ cl_git_pass(git_treebuilder_write(&ancestor_oid, builder));
+
+ /* We now the binary blob in our tree. */
+ cl_git_pass(git_treebuilder_remove(builder, "original.bin"));
+ cl_git_pass(git_treebuilder_insert(NULL, builder, "renamed.bin", &binary, GIT_FILEMODE_BLOB));
+ cl_git_pass(git_treebuilder_write(&ours_oid, builder));
+
+ git_treebuilder_free(builder);
+
+ /* And move everything into a subdirectory in their tree. */
+ cl_git_pass(git_treebuilder_new(&builder, repo, NULL));
+ cl_git_pass(git_treebuilder_insert(NULL, builder, "subdir", &ancestor_oid, GIT_FILEMODE_TREE));
+ cl_git_pass(git_treebuilder_write(&theirs_oid, builder));
+
+ /*
+ * Now merge ancestor, ours and theirs. As `git_hashsig` refuses to
+ * create a hash signature for the 50MB binary file, we historically
+ * didn't cache the hashsig computation for it. As a result, we now
+ * started looking up the 50MB blob and scanning it at least 1000
+ * times, which takes a long time.
+ *
+ * The number of 1000 blobs is chosen in such a way that it's
+ * noticeable when the bug creeps in again, as it takes around 12
+ * minutes on my machine to compute the following merge.
+ */
+ opts.target_limit = 5000;
+ cl_git_pass(git_tree_lookup(&ancestor_tree, repo, &ancestor_oid));
+ cl_git_pass(git_tree_lookup(&their_tree, repo, &theirs_oid));
+ cl_git_pass(git_tree_lookup(&our_tree, repo, &ours_oid));
+ cl_git_pass(git_merge_trees(&index, repo, ancestor_tree, our_tree, their_tree, &opts));
+
+ git_treebuilder_free(builder);
+ git_str_dispose(&path);
+ git_index_free(index);
+ git_tree_free(ancestor_tree);
+ git_tree_free(their_tree);
+ git_tree_free(our_tree);
+ git__free(data);
+}
diff --git a/tests/libgit2/merge/trees/treediff.c b/tests/libgit2/merge/trees/treediff.c
new file mode 100644
index 0000000..094018d
--- /dev/null
+++ b/tests/libgit2/merge/trees/treediff.c
@@ -0,0 +1,555 @@
+#include "clar_libgit2.h"
+#include "git2/tree.h"
+#include "merge.h"
+#include "../merge_helpers.h"
+#include "diff.h"
+#include "diff_tform.h"
+#include "git2/sys/hashsig.h"
+
+static git_repository *repo;
+
+#define TEST_REPO_PATH "merge-resolve"
+
+#define TREE_OID_ANCESTOR "0d52e3a556e189ba0948ae56780918011c1b167d"
+#define TREE_OID_MASTER "1f81433e3161efbf250576c58fede7f6b836f3d3"
+#define TREE_OID_BRANCH "eea9286df54245fea72c5b557291470eb825f38f"
+#define TREE_OID_RENAMES1 "f5f9dd5886a6ee20272be0aafc790cba43b31931"
+#define TREE_OID_RENAMES2 "5fbfbdc04b4eca46f54f4853a3c5a1dce28f5165"
+
+#define TREE_OID_DF_ANCESTOR "b8a3a806d3950e8c0a03a34f234a92eff0e2c68d"
+#define TREE_OID_DF_SIDE1 "ee1d6f164893c1866a323f072eeed36b855656be"
+#define TREE_OID_DF_SIDE2 "6178885b38fe96e825ac0f492c0a941f288b37f6"
+
+#define TREE_OID_RENAME_CONFLICT_ANCESTOR "476dbb3e207313d1d8aaa120c6ad204bf1295e53"
+#define TREE_OID_RENAME_CONFLICT_OURS "c4efe31e9decccc8b2b4d3df9aac2cdfe2995618"
+#define TREE_OID_RENAME_CONFLICT_THEIRS "9e7f4359c469f309b6057febf4c6e80742cbed5b"
+
+void test_merge_trees_treediff__initialize(void)
+{
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+}
+
+void test_merge_trees_treediff__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static void test_find_differences(
+ const char *ancestor_oidstr,
+ const char *ours_oidstr,
+ const char *theirs_oidstr,
+ struct merge_index_conflict_data *treediff_conflict_data,
+ size_t treediff_conflict_data_len)
+{
+ git_merge_diff_list *merge_diff_list = git_merge_diff_list__alloc(repo);
+ git_oid ancestor_oid, ours_oid, theirs_oid;
+ git_tree *ancestor_tree, *ours_tree, *theirs_tree;
+ git_iterator *ancestor_iter, *ours_iter, *theirs_iter;
+ git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
+
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+ opts.flags |= GIT_MERGE_FIND_RENAMES;
+ opts.target_limit = 1000;
+ opts.rename_threshold = 50;
+
+ opts.metric = git__malloc(sizeof(git_diff_similarity_metric));
+ cl_assert(opts.metric != NULL);
+
+ opts.metric->file_signature = git_diff_find_similar__hashsig_for_file;
+ opts.metric->buffer_signature = git_diff_find_similar__hashsig_for_buf;
+ opts.metric->free_signature = git_diff_find_similar__hashsig_free;
+ opts.metric->similarity = git_diff_find_similar__calc_similarity;
+ opts.metric->payload = (void *)GIT_HASHSIG_SMART_WHITESPACE;
+
+ cl_git_pass(git_oid__fromstr(&ancestor_oid, ancestor_oidstr, GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&ours_oid, ours_oidstr, GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&theirs_oid, theirs_oidstr, GIT_OID_SHA1));
+
+ cl_git_pass(git_tree_lookup(&ancestor_tree, repo, &ancestor_oid));
+ cl_git_pass(git_tree_lookup(&ours_tree, repo, &ours_oid));
+ cl_git_pass(git_tree_lookup(&theirs_tree, repo, &theirs_oid));
+
+ iter_opts.flags = GIT_ITERATOR_DONT_IGNORE_CASE;
+
+ cl_git_pass(git_iterator_for_tree(&ancestor_iter, ancestor_tree, &iter_opts));
+ cl_git_pass(git_iterator_for_tree(&ours_iter, ours_tree, &iter_opts));
+ cl_git_pass(git_iterator_for_tree(&theirs_iter, theirs_tree, &iter_opts));
+
+ cl_git_pass(git_merge_diff_list__find_differences(merge_diff_list, ancestor_iter, ours_iter, theirs_iter));
+ cl_git_pass(git_merge_diff_list__find_renames(repo, merge_diff_list, &opts));
+
+ /*
+ dump_merge_index(merge_index);
+ */
+
+ cl_assert(treediff_conflict_data_len == merge_diff_list->conflicts.length);
+
+ cl_assert(merge_test_merge_conflicts(&merge_diff_list->conflicts, treediff_conflict_data, treediff_conflict_data_len));
+
+ git_iterator_free(ancestor_iter);
+ git_iterator_free(ours_iter);
+ git_iterator_free(theirs_iter);
+
+ git_tree_free(ancestor_tree);
+ git_tree_free(ours_tree);
+ git_tree_free(theirs_tree);
+
+ git_merge_diff_list__free(merge_diff_list);
+
+ git__free(opts.metric);
+}
+
+void test_merge_trees_treediff__simple(void)
+{
+ struct merge_index_conflict_data treediff_conflict_data[] = {
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" }, GIT_DELTA_ADDED },
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_NONE
+ },
+
+ {
+ { { 0100644, "6212c31dab5e482247d7977e4f0dd3601decf13b", 0, "automergeable.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 0, "automergeable.txt" }, GIT_DELTA_MODIFIED },
+ { { 0100644, "058541fc37114bfc1dddf6bd6bffc7fae5c2e6fe", 0, "automergeable.txt" }, GIT_DELTA_MODIFIED },
+ GIT_MERGE_DIFF_BOTH_MODIFIED
+ },
+
+ {
+ { { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-branch.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-branch.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "4eb04c9e79e88f6640d01ff5b25ca2a60764f216", 0, "changed-in-branch.txt" }, GIT_DELTA_MODIFIED },
+ GIT_MERGE_DIFF_NONE
+ },
+
+ {
+ { { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt" }, GIT_DELTA_MODIFIED },
+ { { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_NONE
+ },
+
+ {
+ { { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 0, "conflicting.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 0, "conflicting.txt" }, GIT_DELTA_MODIFIED },
+ { { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 0, "conflicting.txt" }, GIT_DELTA_MODIFIED },
+ GIT_MERGE_DIFF_BOTH_MODIFIED
+ },
+
+ {
+ { { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ GIT_MERGE_DIFF_NONE
+ },
+
+ {
+ { { 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ { { 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_NONE
+ },
+ };
+
+ test_find_differences(TREE_OID_ANCESTOR, TREE_OID_MASTER, TREE_OID_BRANCH, treediff_conflict_data, 7);
+}
+
+void test_merge_trees_treediff__df_conflicts(void)
+{
+ struct merge_index_conflict_data treediff_conflict_data[] = {
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "49130a28ef567af9a6a6104c38773fedfa5f9742", 0, "dir-10" }, GIT_DELTA_ADDED },
+ { { 0100644, "6c06dcd163587c2cc18be44857e0b71116382aeb", 0, "dir-10" }, GIT_DELTA_ADDED },
+ GIT_MERGE_DIFF_BOTH_ADDED,
+ },
+
+ {
+ { { 0100644, "242591eb280ee9eeb2ce63524b9a8b9bc4cb515d", 0, "dir-10/file.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ GIT_MERGE_DIFF_BOTH_DELETED,
+ },
+
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "43aafd43bea779ec74317dc361f45ae3f532a505", 0, "dir-6" }, GIT_DELTA_ADDED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0100644, "cf8c5cc8a85a1ff5a4ba51e0bc7cf5665669924d", 0, "dir-6/file.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "cf8c5cc8a85a1ff5a4ba51e0bc7cf5665669924d", 0, "dir-6/file.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "a031a28ae70e33a641ce4b8a8f6317f1ab79dee4", 0, "dir-7" }, GIT_DELTA_ADDED },
+ GIT_MERGE_DIFF_DIRECTORY_FILE,
+ },
+
+ {
+ { { 0100644, "5012fd565b1393bdfda1805d4ec38ce6619e1fd1", 0, "dir-7/file.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "a5563304ddf6caba25cb50323a2ea6f7dbfcadca", 0, "dir-7/file.txt" }, GIT_DELTA_MODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ GIT_MERGE_DIFF_DF_CHILD,
+ },
+
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "e9ad6ec3e38364a3d07feda7c4197d4d845c53b5", 0, "dir-8" }, GIT_DELTA_ADDED },
+ { {0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0100644, "f20c9063fa0bda9a397c96947a7b687305c49753", 0, "dir-8/file.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ { { 0100644, "f20c9063fa0bda9a397c96947a7b687305c49753", 0, "dir-8/file.txt" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "3ef4d30382ca33fdeba9fda895a99e0891ba37aa", 0, "dir-9" }, GIT_DELTA_ADDED },
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_DIRECTORY_FILE,
+ },
+
+ {
+ { { 0100644, "fc4c636d6515e9e261f9260dbcf3cc6eca97ea08", 0, "dir-9/file.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ { { 0100644, "76ab0e2868197ec158ddd6c78d8a0d2fd73d38f9", 0, "dir-9/file.txt" }, GIT_DELTA_MODIFIED },
+ GIT_MERGE_DIFF_DF_CHILD,
+ },
+
+ {
+ { { 0100644, "1e4ff029aee68d0d69ef9eb6efa6cbf1ec732f99", 0, "file-1" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "1e4ff029aee68d0d69ef9eb6efa6cbf1ec732f99", 0, "file-1" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "5c2411f8075f48a6b2fdb85ebc0d371747c4df15", 0, "file-1/new" }, GIT_DELTA_ADDED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0100644, "a39a620dae5bc8b4e771cd4d251b7d080401a21e", 0, "file-2" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "d963979c237d08b6ba39062ee7bf64c7d34a27f8", 0, "file-2" }, GIT_DELTA_MODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ GIT_MERGE_DIFF_DIRECTORY_FILE,
+ },
+
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "5c341ead2ba6f2af98ce5ec3fe84f6b6d2899c0d", 0, "file-2/new" }, GIT_DELTA_ADDED },
+ GIT_MERGE_DIFF_DF_CHILD,
+ },
+
+ {
+ { { 0100644, "032ebc5ab85d9553bb187d3cd40875ff23a63ed0", 0, "file-3" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ { { 0100644, "032ebc5ab85d9553bb187d3cd40875ff23a63ed0", 0, "file-3" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "9efe7723802d4305142eee177e018fee1572c4f4", 0, "file-3/new" }, GIT_DELTA_ADDED },
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0100644, "bacac9b3493509aa15e1730e1545fc0919d1dae0", 0, "file-4" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ { { 0100644, "7663fce0130db092936b137cabd693ec234eb060", 0, "file-4" }, GIT_DELTA_MODIFIED },
+ GIT_MERGE_DIFF_DIRECTORY_FILE,
+ },
+
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "e49f917b448d1340b31d76e54ba388268fd4c922", 0, "file-4/new" }, GIT_DELTA_ADDED },
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_DF_CHILD,
+ },
+
+ {
+ { { 0100644, "ac4045f965119e6998f4340ed0f411decfb3ec05", 0, "file-5" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ GIT_MERGE_DIFF_BOTH_DELETED,
+ },
+
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "cab2cf23998b40f1af2d9d9a756dc9e285a8df4b", 0, "file-5/new" }, GIT_DELTA_ADDED },
+ { { 0100644, "f5504f36e6f4eb797a56fc5bac6c6c7f32969bf2", 0, "file-5/new" }, GIT_DELTA_ADDED },
+ GIT_MERGE_DIFF_BOTH_ADDED,
+ },
+ };
+
+ test_find_differences(TREE_OID_DF_ANCESTOR, TREE_OID_DF_SIDE1, TREE_OID_DF_SIDE2, treediff_conflict_data, 20);
+}
+
+void test_merge_trees_treediff__strict_renames(void)
+{
+ struct merge_index_conflict_data treediff_conflict_data[] = {
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" }, GIT_DELTA_ADDED },
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0100644, "6212c31dab5e482247d7977e4f0dd3601decf13b", 0, "automergeable.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 0, "automergeable.txt" }, GIT_DELTA_MODIFIED },
+ { { 0100644, "6212c31dab5e482247d7977e4f0dd3601decf13b", 0, "automergeable.txt" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt" }, GIT_DELTA_MODIFIED },
+ { { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 0, "conflicting.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 0, "conflicting.txt" }, GIT_DELTA_MODIFIED },
+ { { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 0, "conflicting.txt" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "renamed-in-branch.txt" }, GIT_DELTA_RENAMED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ { { 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "renamed.txt" }, GIT_DELTA_ADDED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "copied.txt" }, GIT_DELTA_RENAMED },
+ GIT_MERGE_DIFF_NONE,
+ },
+ };
+
+ test_find_differences(TREE_OID_ANCESTOR, TREE_OID_MASTER, TREE_OID_RENAMES1, treediff_conflict_data, 8);
+}
+
+void test_merge_trees_treediff__rename_conflicts(void)
+{
+ struct merge_index_conflict_data treediff_conflict_data[] = {
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-duplicated-in-ours.txt" }, GIT_DELTA_ADDED },
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-rewritten-in-ours.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "e376fbdd06ebf021c92724da9f26f44212734e3e", 0, "0b-rewritten-in-ours.txt" }, GIT_DELTA_MODIFIED },
+ { { 0100644, "b2d399ae15224e1d58066e3c8df70ce37de7a656", 0, "0b-rewritten-in-ours.txt" }, GIT_DELTA_MODIFIED },
+ GIT_MERGE_DIFF_BOTH_MODIFIED,
+ },
+
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-duplicated-in-theirs.txt" }, GIT_DELTA_ADDED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-rewritten-in-theirs.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "efc9121fdedaf08ba180b53ebfbcf71bd488ed09", 0, "0c-rewritten-in-theirs.txt" }, GIT_DELTA_MODIFIED },
+ { { 0100644, "712ebba6669ea847d9829e4f1059d6c830c8b531", 0, "0c-rewritten-in-theirs.txt" }, GIT_DELTA_MODIFIED },
+ GIT_MERGE_DIFF_BOTH_MODIFIED,
+ },
+
+ {
+ { { 0100644, "c3d02eeef75183df7584d8d13ac03053910c1301", 0, "1a-renamed-in-ours-edited-in-theirs.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "c3d02eeef75183df7584d8d13ac03053910c1301", 0, "1a-newname-in-ours-edited-in-theirs.txt" }, GIT_DELTA_RENAMED },
+ { { 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638", 0, "1a-renamed-in-ours-edited-in-theirs.txt" }, GIT_DELTA_MODIFIED },
+ GIT_MERGE_DIFF_RENAMED_MODIFIED,
+ },
+
+ {
+ { { 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-renamed-in-ours.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-newname-in-ours.txt" }, GIT_DELTA_RENAMED },
+ { { 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-renamed-in-ours.txt" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0100644, "241a1005cd9b980732741b74385b891142bcba28", 0, "1b-renamed-in-theirs-edited-in-ours.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a", 0, "1b-renamed-in-theirs-edited-in-ours.txt" }, GIT_DELTA_MODIFIED },
+ { { 0100644, "241a1005cd9b980732741b74385b891142bcba28", 0, "1b-newname-in-theirs-edited-in-ours.txt" }, GIT_DELTA_RENAMED },
+ GIT_MERGE_DIFF_RENAMED_MODIFIED,
+ },
+
+ {
+ { { 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-renamed-in-theirs.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-renamed-in-theirs.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-newname-in-theirs.txt" }, GIT_DELTA_RENAMED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-renamed-in-both.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt" }, GIT_DELTA_RENAMED },
+ { { 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt" }, GIT_DELTA_RENAMED },
+ GIT_MERGE_DIFF_BOTH_RENAMED,
+ },
+
+ {
+ { { 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 0, "3a-renamed-in-ours-deleted-in-theirs.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 0, "3a-newname-in-ours-deleted-in-theirs.txt" }, GIT_DELTA_RENAMED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ GIT_MERGE_DIFF_RENAMED_DELETED,
+ },
+
+ {
+ { { 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 0, "3b-renamed-in-theirs-deleted-in-ours.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ { { 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 0, "3b-newname-in-theirs-deleted-in-ours.txt" }, GIT_DELTA_RENAMED },
+ GIT_MERGE_DIFF_RENAMED_DELETED,
+ },
+
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "8b5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a", 0, "4a-newname-in-ours-added-in-theirs.txt" }, GIT_DELTA_ADDED },
+ GIT_MERGE_DIFF_RENAMED_ADDED,
+ },
+
+ {
+ { { 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 0, "4a-renamed-in-ours-added-in-theirs.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 0, "4a-newname-in-ours-added-in-theirs.txt" }, GIT_DELTA_RENAMED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ GIT_MERGE_DIFF_RENAMED_ADDED,
+ },
+
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9", 0, "4b-newname-in-theirs-added-in-ours.txt" }, GIT_DELTA_ADDED },
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_RENAMED_ADDED,
+ },
+
+ {
+ { { 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 0, "4b-renamed-in-theirs-added-in-ours.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ { { 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 0, "4b-newname-in-theirs-added-in-ours.txt" }, GIT_DELTA_RENAMED },
+ GIT_MERGE_DIFF_RENAMED_ADDED,
+ },
+
+ {
+ { { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "5-both-renamed-1-to-2.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "5-both-renamed-1-to-2-ours.txt" }, GIT_DELTA_RENAMED },
+ { { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "5-both-renamed-1-to-2-theirs.txt" }, GIT_DELTA_RENAMED },
+ GIT_MERGE_DIFF_BOTH_RENAMED_1_TO_2,
+ },
+
+ {
+ { { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "6-both-renamed-side-1.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "6-both-renamed.txt" }, GIT_DELTA_RENAMED },
+ { { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "6-both-renamed-side-1.txt" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1,
+ },
+
+ {
+ { { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "6-both-renamed-side-2.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "6-both-renamed-side-2.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "6-both-renamed.txt" }, GIT_DELTA_RENAMED },
+ GIT_MERGE_DIFF_BOTH_RENAMED_2_TO_1,
+ },
+ };
+ test_find_differences(TREE_OID_RENAME_CONFLICT_ANCESTOR,
+ TREE_OID_RENAME_CONFLICT_OURS, TREE_OID_RENAME_CONFLICT_THEIRS, treediff_conflict_data, 18);
+}
+
+void test_merge_trees_treediff__best_renames(void)
+{
+ struct merge_index_conflict_data treediff_conflict_data[] = {
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" }, GIT_DELTA_ADDED },
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0100644, "6212c31dab5e482247d7977e4f0dd3601decf13b", 0, "automergeable.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 0, "automergeable.txt" }, GIT_DELTA_MODIFIED },
+ { { 0100644, "45299c1ca5e07bba1fd90843056fb559f96b1f5a", 0, "renamed-90.txt" }, GIT_DELTA_RENAMED },
+ GIT_MERGE_DIFF_RENAMED_MODIFIED,
+ },
+
+ {
+ { { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt" }, GIT_DELTA_MODIFIED },
+ { { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-master.txt" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 0, "conflicting.txt" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 0, "conflicting.txt" }, GIT_DELTA_MODIFIED },
+ { { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 0, "conflicting.txt" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt" },GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_DELETED },
+ { { 0100644, "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", 0, "removed-in-master.txt" }, GIT_DELTA_UNMODIFIED },
+ GIT_MERGE_DIFF_MODIFIED_DELETED,
+ },
+
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "5843febcb23480df0b5edb22a21c59c772bb8e29", 0, "renamed-50.txt" }, GIT_DELTA_ADDED },
+ GIT_MERGE_DIFF_NONE,
+ },
+
+ {
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0, "", 0, "" }, GIT_DELTA_UNMODIFIED },
+ { { 0100644, "a77a56a49f8f3ae242e02717f18ebbc60c5cc543", 0, "renamed-75.txt" }, GIT_DELTA_ADDED },
+ GIT_MERGE_DIFF_NONE,
+ },
+ };
+
+ test_find_differences(TREE_OID_ANCESTOR, TREE_OID_MASTER, TREE_OID_RENAMES2, treediff_conflict_data, 7);
+}
diff --git a/tests/libgit2/merge/trees/trivial.c b/tests/libgit2/merge/trees/trivial.c
new file mode 100644
index 0000000..287a53c
--- /dev/null
+++ b/tests/libgit2/merge/trees/trivial.c
@@ -0,0 +1,306 @@
+#include "clar_libgit2.h"
+#include "git2/repository.h"
+#include "git2/merge.h"
+#include "merge.h"
+#include "../merge_helpers.h"
+#include "refs.h"
+#include "futils.h"
+#include "git2/sys/index.h"
+
+static git_repository *repo;
+
+#define TEST_REPO_PATH "merge-resolve"
+#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
+
+
+/* Fixture setup and teardown */
+void test_merge_trees_trivial__initialize(void)
+{
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+}
+
+void test_merge_trees_trivial__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+
+static int merge_trivial(git_index **index, const char *ours, const char *theirs)
+{
+ git_commit *our_commit, *their_commit, *ancestor_commit;
+ git_tree *our_tree, *their_tree, *ancestor_tree;
+ git_oid our_oid, their_oid, ancestor_oid;
+ git_str branch_buf = GIT_STR_INIT;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+
+ git_str_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, ours);
+ cl_git_pass(git_reference_name_to_id(&our_oid, repo, branch_buf.ptr));
+ cl_git_pass(git_commit_lookup(&our_commit, repo, &our_oid));
+
+ git_str_clear(&branch_buf);
+ git_str_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, theirs);
+ cl_git_pass(git_reference_name_to_id(&their_oid, repo, branch_buf.ptr));
+ cl_git_pass(git_commit_lookup(&their_commit, repo, &their_oid));
+
+ cl_git_pass(git_merge_base(&ancestor_oid, repo, git_commit_id(our_commit), git_commit_id(their_commit)));
+ cl_git_pass(git_commit_lookup(&ancestor_commit, repo, &ancestor_oid));
+
+ cl_git_pass(git_commit_tree(&ancestor_tree, ancestor_commit));
+ cl_git_pass(git_commit_tree(&our_tree, our_commit));
+ cl_git_pass(git_commit_tree(&their_tree, their_commit));
+
+ cl_git_pass(git_merge_trees(index, repo, ancestor_tree, our_tree, their_tree, &opts));
+
+ git_str_dispose(&branch_buf);
+ git_tree_free(our_tree);
+ git_tree_free(their_tree);
+ git_tree_free(ancestor_tree);
+ git_commit_free(our_commit);
+ git_commit_free(their_commit);
+ git_commit_free(ancestor_commit);
+
+ return 0;
+}
+
+static int merge_trivial_conflict_entrycount(git_index *index)
+{
+ const git_index_entry *entry;
+ int count = 0;
+ size_t i;
+
+ for (i = 0; i < git_index_entrycount(index); i++) {
+ cl_assert(entry = git_index_get_byindex(index, i));
+
+ if (git_index_entry_is_conflict(entry))
+ count++;
+ }
+
+ return count;
+}
+
+/* 2ALT: ancest:(empty)+, head:*empty*, remote:remote = result:remote */
+void test_merge_trees_trivial__2alt(void)
+{
+ git_index *result;
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial(&result, "trivial-2alt", "trivial-2alt-branch"));
+
+ cl_assert(entry = git_index_get_bypath(result, "new-in-branch.txt", 0));
+ cl_assert(git_index_reuc_entrycount(result) == 0);
+ cl_assert(merge_trivial_conflict_entrycount(result) == 0);
+
+ git_index_free(result);
+}
+
+/* 3ALT: ancest:(empty)+, head:head, remote:*empty* = result:head */
+void test_merge_trees_trivial__3alt(void)
+{
+ git_index *result;
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial(&result, "trivial-3alt", "trivial-3alt-branch"));
+
+ cl_assert(entry = git_index_get_bypath(result, "new-in-3alt.txt", 0));
+ cl_assert(git_index_reuc_entrycount(result) == 0);
+ cl_assert(merge_trivial_conflict_entrycount(result) == 0);
+
+ git_index_free(result);
+}
+
+/* 4: ancest:(empty)^, head:head, remote:remote = result:no merge */
+void test_merge_trees_trivial__4(void)
+{
+ git_index *result;
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial(&result, "trivial-4", "trivial-4-branch"));
+
+ cl_assert((entry = git_index_get_bypath(result, "new-and-different.txt", 0)) == NULL);
+ cl_assert(git_index_reuc_entrycount(result) == 0);
+
+ cl_assert(merge_trivial_conflict_entrycount(result) == 2);
+ cl_assert(entry = git_index_get_bypath(result, "new-and-different.txt", 2));
+ cl_assert(entry = git_index_get_bypath(result, "new-and-different.txt", 3));
+
+ git_index_free(result);
+}
+
+/* 5ALT: ancest:*, head:head, remote:head = result:head */
+void test_merge_trees_trivial__5alt_1(void)
+{
+ git_index *result;
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial(&result, "trivial-5alt-1", "trivial-5alt-1-branch"));
+
+ cl_assert(entry = git_index_get_bypath(result, "new-and-same.txt", 0));
+ cl_assert(git_index_reuc_entrycount(result) == 0);
+ cl_assert(merge_trivial_conflict_entrycount(result) == 0);
+
+ git_index_free(result);
+}
+
+/* 5ALT: ancest:*, head:head, remote:head = result:head */
+void test_merge_trees_trivial__5alt_2(void)
+{
+ git_index *result;
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial(&result, "trivial-5alt-2", "trivial-5alt-2-branch"));
+
+ cl_assert(entry = git_index_get_bypath(result, "modified-to-same.txt", 0));
+ cl_assert(git_index_reuc_entrycount(result) == 0);
+ cl_assert(merge_trivial_conflict_entrycount(result) == 0);
+
+ git_index_free(result);
+}
+
+/* 6: ancest:ancest+, head:(empty), remote:(empty) = result:no merge */
+void test_merge_trees_trivial__6(void)
+{
+ git_index *result;
+ const git_index_entry *entry;
+ const git_index_reuc_entry *reuc;
+
+ cl_git_pass(merge_trivial(&result, "trivial-6", "trivial-6-branch"));
+
+ cl_assert((entry = git_index_get_bypath(result, "removed-in-both.txt", 0)) == NULL);
+ cl_assert(git_index_reuc_entrycount(result) == 1);
+ cl_assert(reuc = git_index_reuc_get_bypath(result, "removed-in-both.txt"));
+
+ cl_assert(merge_trivial_conflict_entrycount(result) == 0);
+
+ git_index_free(result);
+}
+
+/* 8: ancest:ancest^, head:(empty), remote:ancest = result:no merge */
+void test_merge_trees_trivial__8(void)
+{
+ git_index *result;
+ const git_index_entry *entry;
+ const git_index_reuc_entry *reuc;
+
+ cl_git_pass(merge_trivial(&result, "trivial-8", "trivial-8-branch"));
+
+ cl_assert((entry = git_index_get_bypath(result, "removed-in-8.txt", 0)) == NULL);
+
+ cl_assert(git_index_reuc_entrycount(result) == 1);
+ cl_assert(reuc = git_index_reuc_get_bypath(result, "removed-in-8.txt"));
+
+ cl_assert(merge_trivial_conflict_entrycount(result) == 0);
+
+ git_index_free(result);
+}
+
+/* 7: ancest:ancest+, head:(empty), remote:remote = result:no merge */
+void test_merge_trees_trivial__7(void)
+{
+ git_index *result;
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial(&result, "trivial-7", "trivial-7-branch"));
+
+ cl_assert((entry = git_index_get_bypath(result, "removed-in-7.txt", 0)) == NULL);
+ cl_assert(git_index_reuc_entrycount(result) == 0);
+
+ cl_assert(merge_trivial_conflict_entrycount(result) == 2);
+ cl_assert(entry = git_index_get_bypath(result, "removed-in-7.txt", 1));
+ cl_assert(entry = git_index_get_bypath(result, "removed-in-7.txt", 3));
+
+ git_index_free(result);
+}
+
+/* 10: ancest:ancest^, head:ancest, remote:(empty) = result:no merge */
+void test_merge_trees_trivial__10(void)
+{
+ git_index *result;
+ const git_index_entry *entry;
+ const git_index_reuc_entry *reuc;
+
+ cl_git_pass(merge_trivial(&result, "trivial-10", "trivial-10-branch"));
+
+ cl_assert((entry = git_index_get_bypath(result, "removed-in-10-branch.txt", 0)) == NULL);
+
+ cl_assert(git_index_reuc_entrycount(result) == 1);
+ cl_assert(reuc = git_index_reuc_get_bypath(result, "removed-in-10-branch.txt"));
+
+ cl_assert(merge_trivial_conflict_entrycount(result) == 0);
+
+ git_index_free(result);
+}
+
+/* 9: ancest:ancest+, head:head, remote:(empty) = result:no merge */
+void test_merge_trees_trivial__9(void)
+{
+ git_index *result;
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial(&result, "trivial-9", "trivial-9-branch"));
+
+ cl_assert((entry = git_index_get_bypath(result, "removed-in-9-branch.txt", 0)) == NULL);
+ cl_assert(git_index_reuc_entrycount(result) == 0);
+
+ cl_assert(merge_trivial_conflict_entrycount(result) == 2);
+ cl_assert(entry = git_index_get_bypath(result, "removed-in-9-branch.txt", 1));
+ cl_assert(entry = git_index_get_bypath(result, "removed-in-9-branch.txt", 2));
+
+ git_index_free(result);
+}
+
+/* 13: ancest:ancest+, head:head, remote:ancest = result:head */
+void test_merge_trees_trivial__13(void)
+{
+ git_index *result;
+ const git_index_entry *entry;
+ git_oid expected_oid;
+
+ cl_git_pass(merge_trivial(&result, "trivial-13", "trivial-13-branch"));
+
+ cl_assert(entry = git_index_get_bypath(result, "modified-in-13.txt", 0));
+ cl_git_pass(git_oid__fromstr(&expected_oid, "1cff9ec6a47a537380dedfdd17c9e76d74259a2b", GIT_OID_SHA1));
+ cl_assert_equal_oid(&expected_oid, &entry->id);
+
+ cl_assert(git_index_reuc_entrycount(result) == 0);
+ cl_assert(merge_trivial_conflict_entrycount(result) == 0);
+
+ git_index_free(result);
+}
+
+/* 14: ancest:ancest+, head:ancest, remote:remote = result:remote */
+void test_merge_trees_trivial__14(void)
+{
+ git_index *result;
+ const git_index_entry *entry;
+ git_oid expected_oid;
+
+ cl_git_pass(merge_trivial(&result, "trivial-14", "trivial-14-branch"));
+
+ cl_assert(entry = git_index_get_bypath(result, "modified-in-14-branch.txt", 0));
+ cl_git_pass(git_oid__fromstr(&expected_oid, "26153a3ff3649b6c2bb652d3f06878c6e0a172f9", GIT_OID_SHA1));
+ cl_assert(git_oid_cmp(&entry->id, &expected_oid) == 0);
+
+ cl_assert(git_index_reuc_entrycount(result) == 0);
+ cl_assert(merge_trivial_conflict_entrycount(result) == 0);
+
+ git_index_free(result);
+}
+
+/* 11: ancest:ancest+, head:head, remote:remote = result:no merge */
+void test_merge_trees_trivial__11(void)
+{
+ git_index *result;
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial(&result, "trivial-11", "trivial-11-branch"));
+
+ cl_assert((entry = git_index_get_bypath(result, "modified-in-both.txt", 0)) == NULL);
+ cl_assert(git_index_reuc_entrycount(result) == 0);
+
+ cl_assert(merge_trivial_conflict_entrycount(result) == 3);
+ cl_assert(entry = git_index_get_bypath(result, "modified-in-both.txt", 1));
+ cl_assert(entry = git_index_get_bypath(result, "modified-in-both.txt", 2));
+ cl_assert(entry = git_index_get_bypath(result, "modified-in-both.txt", 3));
+
+ git_index_free(result);
+}
diff --git a/tests/libgit2/merge/trees/whitespace.c b/tests/libgit2/merge/trees/whitespace.c
new file mode 100644
index 0000000..9917df5
--- /dev/null
+++ b/tests/libgit2/merge/trees/whitespace.c
@@ -0,0 +1,81 @@
+#include "clar_libgit2.h"
+#include "git2/repository.h"
+#include "git2/merge.h"
+#include "merge.h"
+#include "../merge_helpers.h"
+#include "futils.h"
+
+static git_repository *repo;
+
+#define TEST_REPO_PATH "merge-whitespace"
+
+#define BRANCH_A_EOL "branch_a_eol"
+#define BRANCH_B_EOL "branch_b_eol"
+
+#define BRANCH_A_CHANGE "branch_a_change"
+#define BRANCH_B_CHANGE "branch_b_change"
+
+/* Fixture setup and teardown */
+void test_merge_trees_whitespace__initialize(void)
+{
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+}
+
+void test_merge_trees_whitespace__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_merge_trees_whitespace__conflict(void)
+{
+ git_index *index;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "4026a6c83f39c56881c9ac62e7582db9e3d33a4f", 1, "test.txt" },
+ { 0100644, "c3b1fb31424c98072542cc8e42b48c92e52f494a", 2, "test.txt" },
+ { 0100644, "262f67de0de2e535a59ae1bc3c739601e98c354d", 3, "test.txt" },
+ };
+
+ cl_git_pass(merge_trees_from_branches(&index, repo, BRANCH_A_EOL, BRANCH_B_EOL, &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 3));
+
+ git_index_free(index);
+}
+
+void test_merge_trees_whitespace__eol(void)
+{
+ git_index *index;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "ee3c2aac8e03224c323b58ecb1f9eef616745467", 0, "test.txt" },
+ };
+
+ opts.file_flags |= GIT_MERGE_FILE_IGNORE_WHITESPACE_EOL;
+
+ cl_git_pass(merge_trees_from_branches(&index, repo, BRANCH_A_EOL, BRANCH_B_EOL, &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 1));
+
+ git_index_free(index);
+}
+
+void test_merge_trees_whitespace__change(void)
+{
+ git_index *index;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "a827eab4fd66ab37a6ebcfaa7b7e341abfd55947", 0, "test.txt" },
+ };
+
+ opts.file_flags |= GIT_MERGE_FILE_IGNORE_WHITESPACE_CHANGE;
+
+ cl_git_pass(merge_trees_from_branches(&index, repo, BRANCH_A_CHANGE, BRANCH_B_CHANGE, &opts));
+
+ cl_assert(merge_test_index(index, merge_index_entries, 1));
+
+ git_index_free(index);
+}
diff --git a/tests/libgit2/merge/workdir/dirty.c b/tests/libgit2/merge/workdir/dirty.c
new file mode 100644
index 0000000..36b42a1
--- /dev/null
+++ b/tests/libgit2/merge/workdir/dirty.c
@@ -0,0 +1,351 @@
+#include "clar_libgit2.h"
+#include "git2/merge.h"
+#include "merge.h"
+#include "index.h"
+#include "../merge_helpers.h"
+#include "posix.h"
+
+#define TEST_REPO_PATH "merge-resolve"
+#define MERGE_BRANCH_OID "7cb63eed597130ba4abb87b3e544b85021905520"
+
+#define AUTOMERGEABLE_MERGED_FILE \
+ "this file is changed in master\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is automergeable\n" \
+ "this file is changed in branch\n"
+
+#define CHANGED_IN_BRANCH_FILE \
+ "changed in branch\n"
+
+static git_repository *repo;
+static git_index *repo_index;
+
+static char *unaffected[][4] = {
+ { "added-in-master.txt", NULL },
+ { "changed-in-master.txt", NULL },
+ { "unchanged.txt", NULL },
+ { "added-in-master.txt", "changed-in-master.txt", NULL },
+ { "added-in-master.txt", "unchanged.txt", NULL },
+ { "changed-in-master.txt", "unchanged.txt", NULL },
+ { "added-in-master.txt", "changed-in-master.txt", "unchanged.txt", NULL },
+ { "new_file.txt", NULL },
+ { "new_file.txt", "unchanged.txt", NULL },
+ { NULL },
+};
+
+static char *affected[][5] = {
+ { "automergeable.txt", NULL },
+ { "changed-in-branch.txt", NULL },
+ { "conflicting.txt", NULL },
+ { "removed-in-branch.txt", NULL },
+ { "automergeable.txt", "changed-in-branch.txt", NULL },
+ { "automergeable.txt", "conflicting.txt", NULL },
+ { "automergeable.txt", "removed-in-branch.txt", NULL },
+ { "changed-in-branch.txt", "conflicting.txt", NULL },
+ { "changed-in-branch.txt", "removed-in-branch.txt", NULL },
+ { "conflicting.txt", "removed-in-branch.txt", NULL },
+ { "automergeable.txt", "changed-in-branch.txt", "conflicting.txt", NULL },
+ { "automergeable.txt", "changed-in-branch.txt", "removed-in-branch.txt", NULL },
+ { "automergeable.txt", "conflicting.txt", "removed-in-branch.txt", NULL },
+ { "changed-in-branch.txt", "conflicting.txt", "removed-in-branch.txt", NULL },
+ { "automergeable.txt", "changed-in-branch.txt", "conflicting.txt", "removed-in-branch.txt", NULL },
+ { NULL },
+};
+
+static char *result_contents[4][6] = {
+ { "automergeable.txt", AUTOMERGEABLE_MERGED_FILE, NULL, NULL },
+ { "changed-in-branch.txt", CHANGED_IN_BRANCH_FILE, NULL, NULL },
+ { "automergeable.txt", AUTOMERGEABLE_MERGED_FILE, "changed-in-branch.txt", CHANGED_IN_BRANCH_FILE, NULL, NULL },
+ { NULL }
+};
+
+void test_merge_workdir_dirty__initialize(void)
+{
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+ git_repository_index(&repo_index, repo);
+}
+
+void test_merge_workdir_dirty__cleanup(void)
+{
+ git_index_free(repo_index);
+ cl_git_sandbox_cleanup();
+}
+
+static void set_core_autocrlf_to(git_repository *repo, bool value)
+{
+ git_config *cfg;
+
+ cl_git_pass(git_repository_config(&cfg, repo));
+ cl_git_pass(git_config_set_bool(cfg, "core.autocrlf", value));
+
+ git_config_free(cfg);
+}
+
+static int merge_branch(void)
+{
+ git_oid their_oids[1];
+ git_annotated_commit *their_head;
+ git_merge_options merge_opts = GIT_MERGE_OPTIONS_INIT;
+ git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
+ int error;
+
+ cl_git_pass(git_oid__fromstr(&their_oids[0], MERGE_BRANCH_OID, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&their_head, repo, &their_oids[0]));
+
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+ error = git_merge(repo, (const git_annotated_commit **)&their_head, 1, &merge_opts, &checkout_opts);
+
+ git_annotated_commit_free(their_head);
+
+ return error;
+}
+
+static void write_files(char *files[])
+{
+ char *filename;
+ git_str path = GIT_STR_INIT, content = GIT_STR_INIT;
+ size_t i;
+
+ for (i = 0, filename = files[i]; filename; filename = files[++i]) {
+ git_str_clear(&path);
+ git_str_clear(&content);
+
+ git_str_printf(&path, "%s/%s", TEST_REPO_PATH, filename);
+ git_str_printf(&content, "This is a dirty file in the working directory!\n\n"
+ "It will not be staged! Its filename is %s.\n", filename);
+
+ cl_git_mkfile(path.ptr, content.ptr);
+ }
+
+ git_str_dispose(&path);
+ git_str_dispose(&content);
+}
+
+static void hack_index(char *files[])
+{
+ char *filename;
+ struct stat statbuf;
+ git_str path = GIT_STR_INIT;
+ git_index_entry *entry;
+ struct p_timeval times[2];
+ time_t now;
+ size_t i;
+
+ /* Update the index to suggest that checkout placed these files on
+ * disk, keeping the object id but updating the cache, which will
+ * emulate a Git implementation's different filter.
+ *
+ * We set the file's timestamp to before now to pretend that
+ * it was an old checkout so we don't trigger the racy
+ * protections would would check the content.
+ */
+
+ now = time(NULL);
+ times[0].tv_sec = now - 5;
+ times[0].tv_usec = 0;
+ times[1].tv_sec = now - 5;
+ times[1].tv_usec = 0;
+
+ for (i = 0, filename = files[i]; filename; filename = files[++i]) {
+ git_str_clear(&path);
+
+ cl_assert(entry = (git_index_entry *)
+ git_index_get_bypath(repo_index, filename, 0));
+
+ cl_git_pass(git_str_printf(&path, "%s/%s", TEST_REPO_PATH, filename));
+ cl_git_pass(p_utimes(path.ptr, times));
+ cl_git_pass(p_stat(path.ptr, &statbuf));
+
+ entry->ctime.seconds = (int32_t)statbuf.st_ctime;
+ entry->mtime.seconds = (int32_t)statbuf.st_mtime;
+#if defined(GIT_USE_NSEC)
+ entry->ctime.nanoseconds = statbuf.st_ctime_nsec;
+ entry->mtime.nanoseconds = statbuf.st_mtime_nsec;
+#else
+ entry->ctime.nanoseconds = 0;
+ entry->mtime.nanoseconds = 0;
+#endif
+ entry->dev = statbuf.st_dev;
+ entry->ino = statbuf.st_ino;
+ entry->uid = statbuf.st_uid;
+ entry->gid = statbuf.st_gid;
+ entry->file_size = (uint32_t)statbuf.st_size;
+ }
+
+ git_str_dispose(&path);
+}
+
+static void stage_random_files(char *files[])
+{
+ char *filename;
+ size_t i;
+
+ write_files(files);
+
+ for (i = 0, filename = files[i]; filename; filename = files[++i])
+ cl_git_pass(git_index_add_bypath(repo_index, filename));
+}
+
+static void stage_content(char *content[])
+{
+ git_reference *head;
+ git_object *head_object;
+ git_str path = GIT_STR_INIT;
+ char *filename, *text;
+ size_t i;
+
+ cl_git_pass(git_repository_head(&head, repo));
+ cl_git_pass(git_reference_peel(&head_object, head, GIT_OBJECT_COMMIT));
+ cl_git_pass(git_reset(repo, head_object, GIT_RESET_HARD, NULL));
+
+ for (i = 0, filename = content[i], text = content[++i];
+ filename && text;
+ filename = content[++i], text = content[++i]) {
+
+ git_str_clear(&path);
+
+ cl_git_pass(git_str_printf(&path, "%s/%s", TEST_REPO_PATH, filename));
+
+ cl_git_mkfile(path.ptr, text);
+ cl_git_pass(git_index_add_bypath(repo_index, filename));
+ }
+
+ git_object_free(head_object);
+ git_reference_free(head);
+ git_str_dispose(&path);
+}
+
+static int merge_dirty_files(char *dirty_files[])
+{
+ git_reference *head;
+ git_object *head_object;
+ int error;
+
+ cl_git_pass(git_repository_head(&head, repo));
+ cl_git_pass(git_reference_peel(&head_object, head, GIT_OBJECT_COMMIT));
+ cl_git_pass(git_reset(repo, head_object, GIT_RESET_HARD, NULL));
+
+ write_files(dirty_files);
+
+ error = merge_branch();
+
+ git_object_free(head_object);
+ git_reference_free(head);
+
+ return error;
+}
+
+static int merge_differently_filtered_files(char *files[])
+{
+ git_reference *head;
+ git_object *head_object;
+ int error;
+
+ cl_git_pass(git_repository_head(&head, repo));
+ cl_git_pass(git_reference_peel(&head_object, head, GIT_OBJECT_COMMIT));
+ cl_git_pass(git_reset(repo, head_object, GIT_RESET_HARD, NULL));
+
+ /* Emulate checkout with a broken or misconfigured filter: modify some
+ * files on-disk and then update the index with the updated file size
+ * and time, as if some filter applied them. These files should not be
+ * treated as dirty since we created them.
+ *
+ * (Make sure to update the index stamp to defeat racy-git protections
+ * trying to sanity check the files in the index; those would rehash the
+ * files, showing them as dirty, the exact mechanism we're trying to avoid.)
+ */
+
+ write_files(files);
+ hack_index(files);
+
+ cl_git_pass(git_index_write(repo_index));
+
+ error = merge_branch();
+
+ git_object_free(head_object);
+ git_reference_free(head);
+
+ return error;
+}
+
+static int merge_staged_files(char *staged_files[])
+{
+ stage_random_files(staged_files);
+ return merge_branch();
+}
+
+void test_merge_workdir_dirty__unaffected_dirty_files_allowed(void)
+{
+ char **files;
+ size_t i;
+
+ for (i = 0, files = unaffected[i]; files[0]; files = unaffected[++i])
+ cl_git_pass(merge_dirty_files(files));
+}
+
+void test_merge_workdir_dirty__unstaged_deletes_maintained(void)
+{
+ git_reference *head;
+ git_object *head_object;
+
+ cl_git_pass(git_repository_head(&head, repo));
+ cl_git_pass(git_reference_peel(&head_object, head, GIT_OBJECT_COMMIT));
+ cl_git_pass(git_reset(repo, head_object, GIT_RESET_HARD, NULL));
+
+ cl_git_pass(p_unlink("merge-resolve/unchanged.txt"));
+
+ cl_git_pass(merge_branch());
+
+ git_object_free(head_object);
+ git_reference_free(head);
+}
+
+void test_merge_workdir_dirty__affected_dirty_files_disallowed(void)
+{
+ char **files;
+ size_t i;
+
+ for (i = 0, files = affected[i]; files[0]; files = affected[++i])
+ cl_git_fail(merge_dirty_files(files));
+}
+
+void test_merge_workdir_dirty__staged_files_in_index_disallowed(void)
+{
+ char **files;
+ size_t i;
+
+ for (i = 0, files = unaffected[i]; files[0]; files = unaffected[++i])
+ cl_git_fail(merge_staged_files(files));
+
+ for (i = 0, files = affected[i]; files[0]; files = affected[++i])
+ cl_git_fail(merge_staged_files(files));
+}
+
+void test_merge_workdir_dirty__identical_staged_files_allowed(void)
+{
+ char **content;
+ size_t i;
+
+ set_core_autocrlf_to(repo, false);
+
+ for (i = 0, content = result_contents[i]; content[0]; content = result_contents[++i]) {
+ stage_content(content);
+
+ cl_git_pass(git_index_write(repo_index));
+ cl_git_pass(merge_branch());
+ }
+}
+
+void test_merge_workdir_dirty__honors_cache(void)
+{
+ char **files;
+ size_t i;
+
+ for (i = 0, files = affected[i]; files[0]; files = affected[++i])
+ cl_git_pass(merge_differently_filtered_files(files));
+}
diff --git a/tests/libgit2/merge/workdir/recursive.c b/tests/libgit2/merge/workdir/recursive.c
new file mode 100644
index 0000000..7669e1b
--- /dev/null
+++ b/tests/libgit2/merge/workdir/recursive.c
@@ -0,0 +1,84 @@
+#include "clar_libgit2.h"
+#include "git2/repository.h"
+#include "git2/merge.h"
+#include "merge.h"
+#include "../merge_helpers.h"
+#include "../conflict_data.h"
+
+static git_repository *repo;
+
+#define TEST_REPO_PATH "merge-recursive"
+
+void test_merge_workdir_recursive__initialize(void)
+{
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+}
+
+void test_merge_workdir_recursive__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_merge_workdir_recursive__writes_conflict_with_virtual_base(void)
+{
+ git_index *index;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+ git_str conflicting_buf = GIT_STR_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "fa567f568ed72157c0c617438d077695b99d9aac", 1, "veal.txt" },
+ { 0100644, "21950d5e4e4d1a871b4dfcf72ecb6b9c162c434e", 2, "veal.txt" },
+ { 0100644, "3855170cef875708da06ab9ad7fc6a73b531cda1", 3, "veal.txt" },
+ };
+
+ cl_git_pass(merge_branches(repo, GIT_REFS_HEADS_DIR "branchF-1", GIT_REFS_HEADS_DIR "branchF-2", &opts, NULL));
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert(merge_test_index(index, merge_index_entries, 8));
+
+ cl_git_pass(git_futils_readbuffer(&conflicting_buf, "merge-recursive/veal.txt"));
+
+ cl_assert_equal_s(CONFLICTING_RECURSIVE_F1_TO_F2, conflicting_buf.ptr);
+
+ git_index_free(index);
+ git_str_dispose(&conflicting_buf);
+}
+
+void test_merge_workdir_recursive__conflicting_merge_base_with_diff3(void)
+{
+ git_index *index;
+ git_merge_options opts = GIT_MERGE_OPTIONS_INIT;
+ git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_str conflicting_buf = GIT_STR_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "ffb36e513f5fdf8a6ba850a20142676a2ac4807d", 0, "asparagus.txt" },
+ { 0100644, "68f6182f4c85d39e1309d97c7e456156dc9c0096", 0, "beef.txt" },
+ { 0100644, "4b7c5650008b2e747fe1809eeb5a1dde0e80850a", 0, "bouilli.txt" },
+ { 0100644, "c4e6cca3ec6ae0148ed231f97257df8c311e015f", 0, "gravy.txt" },
+ { 0100644, "68af1fc7407fd9addf1701a87eb1c95c7494c598", 0, "oyster.txt" },
+ { 0100644, "0b01d2f70a1c6b9ab60c382f3f9cdc8173da6736", 1, "veal.txt" },
+ { 0100644, "37a5054a9f9b4628e3924c5cb8f2147c6e2a3efc", 2, "veal.txt" },
+ { 0100644, "d604c75019c282144bdbbf3fd3462ba74b240efc", 3, "veal.txt" },
+ };
+
+ opts.file_flags |= GIT_MERGE_FILE_STYLE_DIFF3;
+ checkout_opts.checkout_strategy |= GIT_CHECKOUT_CONFLICT_STYLE_DIFF3;
+
+ cl_git_pass(merge_branches(repo, GIT_REFS_HEADS_DIR "branchH-2", GIT_REFS_HEADS_DIR "branchH-1", &opts, &checkout_opts));
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert(merge_test_index(index, merge_index_entries, 8));
+
+ cl_git_pass(git_futils_readbuffer(&conflicting_buf, "merge-recursive/veal.txt"));
+
+ cl_assert_equal_s(CONFLICTING_RECURSIVE_H2_TO_H1_WITH_DIFF3, conflicting_buf.ptr);
+
+ git_index_free(index);
+ git_str_dispose(&conflicting_buf);
+}
diff --git a/tests/libgit2/merge/workdir/renames.c b/tests/libgit2/merge/workdir/renames.c
new file mode 100644
index 0000000..1b5128c
--- /dev/null
+++ b/tests/libgit2/merge/workdir/renames.c
@@ -0,0 +1,155 @@
+#include "clar_libgit2.h"
+#include "git2/repository.h"
+#include "git2/merge.h"
+#include "merge.h"
+#include "../merge_helpers.h"
+#include "futils.h"
+#include "refs.h"
+
+static git_repository *repo;
+
+#define TEST_REPO_PATH "merge-resolve"
+
+#define BRANCH_RENAME_OURS "rename_conflict_ours"
+#define BRANCH_RENAME_THEIRS "rename_conflict_theirs"
+
+/* Fixture setup and teardown */
+void test_merge_workdir_renames__initialize(void)
+{
+ git_config *cfg;
+
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+
+ /* Ensure that the user's merge.conflictstyle doesn't interfere */
+ cl_git_pass(git_repository_config(&cfg, repo));
+ cl_git_pass(git_config_set_string(cfg, "merge.conflictstyle", "merge"));
+ git_config_free(cfg);
+}
+
+void test_merge_workdir_renames__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_merge_workdir_renames__renames(void)
+{
+ git_merge_options merge_opts = GIT_MERGE_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e", 0, "0a-no-change.txt" },
+ { 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-duplicated-in-ours.txt" },
+ { 0100644, "8aac75de2a34b4d340bf62a6e58197269cb55797", 0, "0b-rewritten-in-ours.txt" },
+ { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-duplicated-in-theirs.txt" },
+ { 0100644, "7edc726325da726751a4195e434e4377b0f67f9a", 0, "0c-rewritten-in-theirs.txt" },
+ { 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638", 0, "1a-newname-in-ours-edited-in-theirs.txt" },
+ { 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-newname-in-ours.txt" },
+ { 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a", 0, "1b-newname-in-theirs-edited-in-ours.txt" },
+ { 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-newname-in-theirs.txt" },
+ { 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt" },
+ { 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 0, "3a-newname-in-ours-deleted-in-theirs.txt" },
+ { 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 0, "3b-newname-in-theirs-deleted-in-ours.txt" },
+ { 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 0, "4a-newname-in-ours-added-in-theirs.txt~HEAD" },
+ { 0100644, "8b5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a", 0, "4a-newname-in-ours-added-in-theirs.txt~rename_conflict_theirs" },
+ { 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9", 0, "4b-newname-in-theirs-added-in-ours.txt~HEAD" },
+ { 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 0, "4b-newname-in-theirs-added-in-ours.txt~rename_conflict_theirs" },
+ { 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 0, "5a-newname-in-ours-added-in-theirs.txt~HEAD" },
+ { 0100644, "98ba4205fcf31f5dd93c916d35fe3f3b3d0e6714", 0, "5a-newname-in-ours-added-in-theirs.txt~rename_conflict_theirs" },
+ { 0100644, "385c8a0f26ddf79e9041e15e17dc352ed2c4cced", 0, "5b-newname-in-theirs-added-in-ours.txt~HEAD" },
+ { 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 0, "5b-newname-in-theirs-added-in-ours.txt~rename_conflict_theirs" },
+ { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "6-both-renamed-1-to-2-ours.txt" },
+ { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "6-both-renamed-1-to-2-theirs.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "7-both-renamed.txt~HEAD" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "7-both-renamed.txt~rename_conflict_theirs" },
+ };
+
+ merge_opts.flags |= GIT_MERGE_FIND_RENAMES;
+ merge_opts.rename_threshold = 50;
+
+ cl_git_pass(merge_branches(repo, GIT_REFS_HEADS_DIR BRANCH_RENAME_OURS, GIT_REFS_HEADS_DIR BRANCH_RENAME_THEIRS, &merge_opts, NULL));
+ cl_assert(merge_test_workdir(repo, merge_index_entries, 24));
+}
+
+void test_merge_workdir_renames__ours(void)
+{
+ git_index *index;
+ git_merge_options merge_opts = GIT_MERGE_OPTIONS_INIT;
+ git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e", 0, "0a-no-change.txt" },
+ { 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-duplicated-in-ours.txt" },
+ { 0100644, "e376fbdd06ebf021c92724da9f26f44212734e3e", 0, "0b-rewritten-in-ours.txt" },
+ { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-duplicated-in-theirs.txt" },
+ { 0100644, "efc9121fdedaf08ba180b53ebfbcf71bd488ed09", 0, "0c-rewritten-in-theirs.txt" },
+ { 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638", 0, "1a-newname-in-ours-edited-in-theirs.txt" },
+ { 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-newname-in-ours.txt" },
+ { 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a", 0, "1b-newname-in-theirs-edited-in-ours.txt" },
+ { 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-newname-in-theirs.txt" },
+ { 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt" },
+ { 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 0, "3a-newname-in-ours-deleted-in-theirs.txt" },
+ { 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 0, "3b-newname-in-theirs-deleted-in-ours.txt" },
+ { 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 0, "4a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9", 0, "4b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 0, "5a-newname-in-ours-added-in-theirs.txt" },
+ { 0100644, "385c8a0f26ddf79e9041e15e17dc352ed2c4cced", 0, "5b-newname-in-theirs-added-in-ours.txt" },
+ { 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 0, "5b-renamed-in-theirs-added-in-ours.txt" },
+ { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "6-both-renamed-1-to-2-ours.txt" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "7-both-renamed-side-2.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "7-both-renamed.txt" },
+ };
+
+ merge_opts.flags |= GIT_MERGE_FIND_RENAMES;
+ merge_opts.rename_threshold = 50;
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_USE_OURS;
+
+ cl_git_pass(merge_branches(repo, GIT_REFS_HEADS_DIR BRANCH_RENAME_OURS, GIT_REFS_HEADS_DIR BRANCH_RENAME_THEIRS, &merge_opts, &checkout_opts));
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_write(index));
+ cl_assert(merge_test_workdir(repo, merge_index_entries, 20));
+
+ git_index_free(index);
+}
+
+void test_merge_workdir_renames__similar(void)
+{
+ git_merge_options merge_opts = GIT_MERGE_OPTIONS_INIT;
+
+ /*
+ * Note: this differs slightly from the core git merge result - there, 4a is
+ * tracked as a rename/delete instead of a rename/add and the theirs side
+ * is not placed in workdir in any form.
+ */
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "68c6c84b091926c7d90aa6a79b2bc3bb6adccd8e", 0, "0a-no-change.txt" },
+ { 0100644, "f0ce2b8e4986084d9b308fb72709e414c23eb5e6", 0, "0b-duplicated-in-ours.txt" },
+ { 0100644, "8aac75de2a34b4d340bf62a6e58197269cb55797", 0, "0b-rewritten-in-ours.txt" },
+ { 0100644, "2f56120107d680129a5d9791b521cb1e73a2ed31", 0, "0c-duplicated-in-theirs.txt" },
+ { 0100644, "7edc726325da726751a4195e434e4377b0f67f9a", 0, "0c-rewritten-in-theirs.txt" },
+ { 0100644, "0d872f8e871a30208305978ecbf9e66d864f1638", 0, "1a-newname-in-ours-edited-in-theirs.txt" },
+ { 0100644, "d0d4594e16f2e19107e3fa7ea63e7aaaff305ffb", 0, "1a-newname-in-ours.txt" },
+ { 0100644, "ed9523e62e453e50dd9be1606af19399b96e397a", 0, "1b-newname-in-theirs-edited-in-ours.txt" },
+ { 0100644, "2b5f1f181ee3b58ea751f5dd5d8f9b445520a136", 0, "1b-newname-in-theirs.txt" },
+ { 0100644, "178940b450f238a56c0d75b7955cb57b38191982", 0, "2-newname-in-both.txt" },
+ { 0100644, "18cb316b1cefa0f8a6946f0e201a8e1a6f845ab9", 0, "3a-newname-in-ours-deleted-in-theirs.txt" },
+ { 0100644, "36219b49367146cb2e6a1555b5a9ebd4d0328495", 0, "3b-newname-in-theirs-deleted-in-ours.txt" },
+ { 0100644, "227792b52aaa0b238bea00ec7e509b02623f168c", 0, "4a-newname-in-ours-added-in-theirs.txt~HEAD" },
+ { 0100644, "8b5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a", 0, "4a-newname-in-ours-added-in-theirs.txt~rename_conflict_theirs" },
+ { 0100644, "de872ee3618b894992e9d1e18ba2ebe256a112f9", 0, "4b-newname-in-theirs-added-in-ours.txt~HEAD" },
+ { 0100644, "98d52d07c0b0bbf2b46548f6aa521295c2cb55db", 0, "4b-newname-in-theirs-added-in-ours.txt~rename_conflict_theirs" },
+ { 0100644, "d3719a5ae8e4d92276b5313ce976f6ee5af2b436", 0, "5a-newname-in-ours-added-in-theirs.txt~HEAD" },
+ { 0100644, "98ba4205fcf31f5dd93c916d35fe3f3b3d0e6714", 0, "5a-newname-in-ours-added-in-theirs.txt~rename_conflict_theirs" },
+ { 0100644, "385c8a0f26ddf79e9041e15e17dc352ed2c4cced", 0, "5b-newname-in-theirs-added-in-ours.txt~HEAD" },
+ { 0100644, "63247125386de9ec90a27ad36169307bf8a11a38", 0, "5b-newname-in-theirs-added-in-ours.txt~rename_conflict_theirs" },
+ { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "6-both-renamed-1-to-2-ours.txt" },
+ { 0100644, "d8fa77b6833082c1ea36b7828a582d4c43882450", 0, "6-both-renamed-1-to-2-theirs.txt" },
+ { 0100644, "b42712cfe99a1a500b2a51fe984e0b8a7702ba11", 0, "7-both-renamed.txt~HEAD" },
+ { 0100644, "b69fe837e4cecfd4c9a40cdca7c138468687df07", 0, "7-both-renamed.txt~rename_conflict_theirs" },
+ };
+
+ merge_opts.flags |= GIT_MERGE_FIND_RENAMES;
+ merge_opts.rename_threshold = 50;
+
+ cl_git_pass(merge_branches(repo, GIT_REFS_HEADS_DIR BRANCH_RENAME_OURS, GIT_REFS_HEADS_DIR BRANCH_RENAME_THEIRS, &merge_opts, NULL));
+ cl_assert(merge_test_workdir(repo, merge_index_entries, 24));
+}
+
diff --git a/tests/libgit2/merge/workdir/setup.c b/tests/libgit2/merge/workdir/setup.c
new file mode 100644
index 0000000..98ccdf7
--- /dev/null
+++ b/tests/libgit2/merge/workdir/setup.c
@@ -0,0 +1,1096 @@
+#include "clar_libgit2.h"
+#include "git2/repository.h"
+#include "git2/merge.h"
+#include "merge.h"
+#include "refs.h"
+#include "futils.h"
+
+static git_repository *repo;
+static git_index *repo_index;
+
+#define TEST_REPO_PATH "merge-resolve"
+#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
+
+#define ORIG_HEAD "bd593285fc7fe4ca18ccdbabf027f5d689101452"
+
+#define THEIRS_SIMPLE_BRANCH "branch"
+#define THEIRS_SIMPLE_OID "7cb63eed597130ba4abb87b3e544b85021905520"
+
+#define OCTO1_BRANCH "octo1"
+#define OCTO1_OID "16f825815cfd20a07a75c71554e82d8eede0b061"
+
+#define OCTO2_BRANCH "octo2"
+#define OCTO2_OID "158dc7bedb202f5b26502bf3574faa7f4238d56c"
+
+#define OCTO3_BRANCH "octo3"
+#define OCTO3_OID "50ce7d7d01217679e26c55939eef119e0c93e272"
+
+#define OCTO4_BRANCH "octo4"
+#define OCTO4_OID "54269b3f6ec3d7d4ede24dd350dd5d605495c3ae"
+
+#define OCTO5_BRANCH "octo5"
+#define OCTO5_OID "e4f618a2c3ed0669308735727df5ebf2447f022f"
+
+/* Fixture setup and teardown */
+void test_merge_workdir_setup__initialize(void)
+{
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+ git_repository_index(&repo_index, repo);
+}
+
+void test_merge_workdir_setup__cleanup(void)
+{
+ git_index_free(repo_index);
+ cl_git_sandbox_cleanup();
+}
+
+static bool test_file_contents(const char *filename, const char *expected)
+{
+ git_str file_path_buf = GIT_STR_INIT, file_buf = GIT_STR_INIT;
+ bool equals;
+
+ git_str_joinpath(&file_path_buf, git_repository_path(repo), filename);
+
+ cl_git_pass(git_futils_readbuffer(&file_buf, file_path_buf.ptr));
+ equals = (strcmp(file_buf.ptr, expected) == 0);
+
+ git_str_dispose(&file_path_buf);
+ git_str_dispose(&file_buf);
+
+ return equals;
+}
+
+static void write_file_contents(const char *filename, const char *output)
+{
+ git_str file_path_buf = GIT_STR_INIT;
+
+ git_str_joinpath(&file_path_buf, git_repository_path(repo),
+ filename);
+ cl_git_rewritefile(file_path_buf.ptr, output);
+
+ git_str_dispose(&file_path_buf);
+}
+
+/* git merge --no-ff octo1 */
+void test_merge_workdir_setup__one_branch(void)
+{
+ git_oid our_oid;
+ git_reference *octo1_ref;
+ git_annotated_commit *our_head, *their_heads[1];
+
+ cl_git_pass(git_oid__fromstr(&our_oid, ORIG_HEAD, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[0], repo, octo1_ref));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 1));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff"));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch '" OCTO1_BRANCH "'\n"));
+
+ git_reference_free(octo1_ref);
+
+ git_annotated_commit_free(our_head);
+ git_annotated_commit_free(their_heads[0]);
+}
+
+/* git merge --no-ff 16f825815cfd20a07a75c71554e82d8eede0b061 */
+void test_merge_workdir_setup__one_oid(void)
+{
+ git_oid our_oid;
+ git_oid octo1_oid;
+ git_annotated_commit *our_head, *their_heads[1];
+
+ cl_git_pass(git_oid__fromstr(&our_oid, ORIG_HEAD, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_oid__fromstr(&octo1_oid, OCTO1_OID, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&their_heads[0], repo, &octo1_oid));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 1));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff"));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge commit '" OCTO1_OID "'\n"));
+
+ git_annotated_commit_free(our_head);
+ git_annotated_commit_free(their_heads[0]);
+}
+
+/* git merge octo1 octo2 */
+void test_merge_workdir_setup__two_branches(void)
+{
+ git_oid our_oid;
+ git_reference *octo1_ref;
+ git_reference *octo2_ref;
+ git_annotated_commit *our_head, *their_heads[2];
+
+ cl_git_pass(git_oid__fromstr(&our_oid, ORIG_HEAD, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[0], repo, octo1_ref));
+
+ cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[1], repo, octo2_ref));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 2));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff"));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "' and '" OCTO2_BRANCH "'\n"));
+
+ git_reference_free(octo1_ref);
+ git_reference_free(octo2_ref);
+
+ git_annotated_commit_free(our_head);
+ git_annotated_commit_free(their_heads[0]);
+ git_annotated_commit_free(their_heads[1]);
+}
+
+/* git merge octo1 octo2 octo3 */
+void test_merge_workdir_setup__three_branches(void)
+{
+ git_oid our_oid;
+ git_reference *octo1_ref;
+ git_reference *octo2_ref;
+ git_reference *octo3_ref;
+ git_annotated_commit *our_head, *their_heads[3];
+
+ cl_git_pass(git_oid__fromstr(&our_oid, ORIG_HEAD, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[0], repo, octo1_ref));
+
+ cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[1], repo, octo2_ref));
+
+ cl_git_pass(git_reference_lookup(&octo3_ref, repo, GIT_REFS_HEADS_DIR OCTO3_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[2], repo, octo3_ref));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 3));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff"));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "', '" OCTO2_BRANCH "' and '" OCTO3_BRANCH "'\n"));
+
+ git_reference_free(octo1_ref);
+ git_reference_free(octo2_ref);
+ git_reference_free(octo3_ref);
+
+ git_annotated_commit_free(our_head);
+ git_annotated_commit_free(their_heads[0]);
+ git_annotated_commit_free(their_heads[1]);
+ git_annotated_commit_free(their_heads[2]);
+}
+
+/* git merge 16f825815cfd20a07a75c71554e82d8eede0b061 158dc7bedb202f5b26502bf3574faa7f4238d56c 50ce7d7d01217679e26c55939eef119e0c93e272 */
+void test_merge_workdir_setup__three_oids(void)
+{
+ git_oid our_oid;
+ git_oid octo1_oid;
+ git_oid octo2_oid;
+ git_oid octo3_oid;
+ git_annotated_commit *our_head, *their_heads[3];
+
+ cl_git_pass(git_oid__fromstr(&our_oid, ORIG_HEAD, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_oid__fromstr(&octo1_oid, OCTO1_OID, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&their_heads[0], repo, &octo1_oid));
+
+ cl_git_pass(git_oid__fromstr(&octo2_oid, OCTO2_OID, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&their_heads[1], repo, &octo2_oid));
+
+ cl_git_pass(git_oid__fromstr(&octo3_oid, OCTO3_OID, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&their_heads[2], repo, &octo3_oid));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 3));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff"));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge commit '" OCTO1_OID "'; commit '" OCTO2_OID "'; commit '" OCTO3_OID "'\n"));
+
+ git_annotated_commit_free(our_head);
+ git_annotated_commit_free(their_heads[0]);
+ git_annotated_commit_free(their_heads[1]);
+ git_annotated_commit_free(their_heads[2]);
+}
+
+/* git merge octo1 158dc7bedb202f5b26502bf3574faa7f4238d56c */
+void test_merge_workdir_setup__branches_and_oids_1(void)
+{
+ git_oid our_oid;
+ git_reference *octo1_ref;
+ git_oid octo2_oid;
+ git_annotated_commit *our_head, *their_heads[2];
+
+ cl_git_pass(git_oid__fromstr(&our_oid, ORIG_HEAD, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[0], repo, octo1_ref));
+
+ cl_git_pass(git_oid__fromstr(&octo2_oid, OCTO2_OID, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&their_heads[1], repo, &octo2_oid));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 2));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff"));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch '" OCTO1_BRANCH "'; commit '" OCTO2_OID "'\n"));
+
+ git_reference_free(octo1_ref);
+
+ git_annotated_commit_free(our_head);
+ git_annotated_commit_free(their_heads[0]);
+ git_annotated_commit_free(their_heads[1]);
+}
+
+/* git merge octo1 158dc7bedb202f5b26502bf3574faa7f4238d56c octo3 54269b3f6ec3d7d4ede24dd350dd5d605495c3ae */
+void test_merge_workdir_setup__branches_and_oids_2(void)
+{
+ git_oid our_oid;
+ git_reference *octo1_ref;
+ git_oid octo2_oid;
+ git_reference *octo3_ref;
+ git_oid octo4_oid;
+ git_annotated_commit *our_head, *their_heads[4];
+
+ cl_git_pass(git_oid__fromstr(&our_oid, ORIG_HEAD, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[0], repo, octo1_ref));
+
+ cl_git_pass(git_oid__fromstr(&octo2_oid, OCTO2_OID, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&their_heads[1], repo, &octo2_oid));
+
+ cl_git_pass(git_reference_lookup(&octo3_ref, repo, GIT_REFS_HEADS_DIR OCTO3_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[2], repo, octo3_ref));
+
+ cl_git_pass(git_oid__fromstr(&octo4_oid, OCTO4_OID, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&their_heads[3], repo, &octo4_oid));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 4));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n" OCTO4_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff"));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "' and '" OCTO3_BRANCH "'; commit '" OCTO2_OID "'; commit '" OCTO4_OID "'\n"));
+
+ git_reference_free(octo1_ref);
+ git_reference_free(octo3_ref);
+
+ git_annotated_commit_free(our_head);
+ git_annotated_commit_free(their_heads[0]);
+ git_annotated_commit_free(their_heads[1]);
+ git_annotated_commit_free(their_heads[2]);
+ git_annotated_commit_free(their_heads[3]);
+}
+
+/* git merge 16f825815cfd20a07a75c71554e82d8eede0b061 octo2 50ce7d7d01217679e26c55939eef119e0c93e272 octo4 */
+void test_merge_workdir_setup__branches_and_oids_3(void)
+{
+ git_oid our_oid;
+ git_oid octo1_oid;
+ git_reference *octo2_ref;
+ git_oid octo3_oid;
+ git_reference *octo4_ref;
+ git_annotated_commit *our_head, *their_heads[4];
+
+ cl_git_pass(git_oid__fromstr(&our_oid, ORIG_HEAD, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_oid__fromstr(&octo1_oid, OCTO1_OID, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&their_heads[0], repo, &octo1_oid));
+
+ cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[1], repo, octo2_ref));
+
+ cl_git_pass(git_oid__fromstr(&octo3_oid, OCTO3_OID, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&their_heads[2], repo, &octo3_oid));
+
+ cl_git_pass(git_reference_lookup(&octo4_ref, repo, GIT_REFS_HEADS_DIR OCTO4_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[3], repo, octo4_ref));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 4));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n" OCTO4_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff"));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge commit '" OCTO1_OID "'; branches '" OCTO2_BRANCH "' and '" OCTO4_BRANCH "'; commit '" OCTO3_OID "'\n"));
+
+ git_reference_free(octo2_ref);
+ git_reference_free(octo4_ref);
+
+ git_annotated_commit_free(our_head);
+ git_annotated_commit_free(their_heads[0]);
+ git_annotated_commit_free(their_heads[1]);
+ git_annotated_commit_free(their_heads[2]);
+ git_annotated_commit_free(their_heads[3]);
+}
+
+/* git merge 16f825815cfd20a07a75c71554e82d8eede0b061 octo2 50ce7d7d01217679e26c55939eef119e0c93e272 octo4 octo5 */
+void test_merge_workdir_setup__branches_and_oids_4(void)
+{
+ git_oid our_oid;
+ git_oid octo1_oid;
+ git_reference *octo2_ref;
+ git_oid octo3_oid;
+ git_reference *octo4_ref;
+ git_reference *octo5_ref;
+ git_annotated_commit *our_head, *their_heads[5];
+
+ cl_git_pass(git_oid__fromstr(&our_oid, ORIG_HEAD, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_oid__fromstr(&octo1_oid, OCTO1_OID, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&their_heads[0], repo, &octo1_oid));
+
+ cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[1], repo, octo2_ref));
+
+ cl_git_pass(git_oid__fromstr(&octo3_oid, OCTO3_OID, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&their_heads[2], repo, &octo3_oid));
+
+ cl_git_pass(git_reference_lookup(&octo4_ref, repo, GIT_REFS_HEADS_DIR OCTO4_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[3], repo, octo4_ref));
+
+ cl_git_pass(git_reference_lookup(&octo5_ref, repo, GIT_REFS_HEADS_DIR OCTO5_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[4], repo, octo5_ref));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 5));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n" OCTO4_OID "\n" OCTO5_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff"));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge commit '" OCTO1_OID "'; branches '" OCTO2_BRANCH "', '" OCTO4_BRANCH "' and '" OCTO5_BRANCH "'; commit '" OCTO3_OID "'\n"));
+
+ git_reference_free(octo2_ref);
+ git_reference_free(octo4_ref);
+ git_reference_free(octo5_ref);
+
+ git_annotated_commit_free(our_head);
+ git_annotated_commit_free(their_heads[0]);
+ git_annotated_commit_free(their_heads[1]);
+ git_annotated_commit_free(their_heads[2]);
+ git_annotated_commit_free(their_heads[3]);
+ git_annotated_commit_free(their_heads[4]);
+}
+
+/* git merge octo1 octo1 octo1 */
+void test_merge_workdir_setup__three_same_branches(void)
+{
+ git_oid our_oid;
+ git_reference *octo1_1_ref;
+ git_reference *octo1_2_ref;
+ git_reference *octo1_3_ref;
+ git_annotated_commit *our_head, *their_heads[3];
+
+ cl_git_pass(git_oid__fromstr(&our_oid, ORIG_HEAD, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_reference_lookup(&octo1_1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[0], repo, octo1_1_ref));
+
+ cl_git_pass(git_reference_lookup(&octo1_2_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[1], repo, octo1_2_ref));
+
+ cl_git_pass(git_reference_lookup(&octo1_3_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[2], repo, octo1_3_ref));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 3));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO1_OID "\n" OCTO1_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff"));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "', '" OCTO1_BRANCH "' and '" OCTO1_BRANCH "'\n"));
+
+ git_reference_free(octo1_1_ref);
+ git_reference_free(octo1_2_ref);
+ git_reference_free(octo1_3_ref);
+
+ git_annotated_commit_free(our_head);
+ git_annotated_commit_free(their_heads[0]);
+ git_annotated_commit_free(their_heads[1]);
+ git_annotated_commit_free(their_heads[2]);
+}
+
+/* git merge 16f825815cfd20a07a75c71554e82d8eede0b061 16f825815cfd20a07a75c71554e82d8eede0b061 16f825815cfd20a07a75c71554e82d8eede0b061 */
+void test_merge_workdir_setup__three_same_oids(void)
+{
+ git_oid our_oid;
+ git_oid octo1_1_oid;
+ git_oid octo1_2_oid;
+ git_oid octo1_3_oid;
+ git_annotated_commit *our_head, *their_heads[3];
+
+ cl_git_pass(git_oid__fromstr(&our_oid, ORIG_HEAD, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_oid__fromstr(&octo1_1_oid, OCTO1_OID, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&their_heads[0], repo, &octo1_1_oid));
+
+ cl_git_pass(git_oid__fromstr(&octo1_2_oid, OCTO1_OID, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&their_heads[1], repo, &octo1_2_oid));
+
+ cl_git_pass(git_oid__fromstr(&octo1_3_oid, OCTO1_OID, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&their_heads[2], repo, &octo1_3_oid));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 3));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO1_OID "\n" OCTO1_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff"));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge commit '" OCTO1_OID "'; commit '" OCTO1_OID "'; commit '" OCTO1_OID "'\n"));
+
+ git_annotated_commit_free(our_head);
+ git_annotated_commit_free(their_heads[0]);
+ git_annotated_commit_free(their_heads[1]);
+ git_annotated_commit_free(their_heads[2]);
+}
+
+static int create_remote_tracking_branch(const char *branch_name, const char *oid_str)
+{
+ int error = 0;
+
+ git_str remotes_path = GIT_STR_INIT,
+ origin_path = GIT_STR_INIT,
+ filename = GIT_STR_INIT,
+ data = GIT_STR_INIT;
+
+ if ((error = git_str_puts(&remotes_path, git_repository_path(repo))) < 0 ||
+ (error = git_str_puts(&remotes_path, GIT_REFS_REMOTES_DIR)) < 0)
+ goto done;
+
+ if (!git_fs_path_exists(git_str_cstr(&remotes_path)) &&
+ (error = p_mkdir(git_str_cstr(&remotes_path), 0777)) < 0)
+ goto done;
+
+ if ((error = git_str_puts(&origin_path, git_str_cstr(&remotes_path))) < 0 ||
+ (error = git_str_puts(&origin_path, "origin")) < 0)
+ goto done;
+
+ if (!git_fs_path_exists(git_str_cstr(&origin_path)) &&
+ (error = p_mkdir(git_str_cstr(&origin_path), 0777)) < 0)
+ goto done;
+
+ if ((error = git_str_puts(&filename, git_str_cstr(&origin_path))) < 0 ||
+ (error = git_str_puts(&filename, "/")) < 0 ||
+ (error = git_str_puts(&filename, branch_name)) < 0 ||
+ (error = git_str_puts(&data, oid_str)) < 0 ||
+ (error = git_str_puts(&data, "\n")) < 0)
+ goto done;
+
+ cl_git_rewritefile(git_str_cstr(&filename), git_str_cstr(&data));
+
+done:
+ git_str_dispose(&remotes_path);
+ git_str_dispose(&origin_path);
+ git_str_dispose(&filename);
+ git_str_dispose(&data);
+
+ return error;
+}
+
+/* git merge refs/remotes/origin/octo1 */
+void test_merge_workdir_setup__remote_tracking_one_branch(void)
+{
+ git_oid our_oid;
+ git_reference *octo1_ref;
+ git_annotated_commit *our_head, *their_heads[1];
+
+ cl_git_pass(create_remote_tracking_branch(OCTO1_BRANCH, OCTO1_OID));
+
+ cl_git_pass(git_oid__fromstr(&our_oid, ORIG_HEAD, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO1_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[0], repo, octo1_ref));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 1));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff"));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge remote-tracking branch 'refs/remotes/origin/" OCTO1_BRANCH "'\n"));
+
+ git_reference_free(octo1_ref);
+
+ git_annotated_commit_free(our_head);
+ git_annotated_commit_free(their_heads[0]);
+}
+
+/* git merge refs/remotes/origin/octo1 refs/remotes/origin/octo2 */
+void test_merge_workdir_setup__remote_tracking_two_branches(void)
+{
+ git_oid our_oid;
+ git_reference *octo1_ref;
+ git_reference *octo2_ref;
+ git_annotated_commit *our_head, *their_heads[2];
+
+ cl_git_pass(create_remote_tracking_branch(OCTO1_BRANCH, OCTO1_OID));
+ cl_git_pass(create_remote_tracking_branch(OCTO2_BRANCH, OCTO2_OID));
+
+ cl_git_pass(git_oid__fromstr(&our_oid, ORIG_HEAD, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO1_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[0], repo, octo1_ref));
+
+ cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO2_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[1], repo, octo2_ref));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 2));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff"));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge remote-tracking branches 'refs/remotes/origin/" OCTO1_BRANCH "' and 'refs/remotes/origin/" OCTO2_BRANCH "'\n"));
+
+ git_reference_free(octo1_ref);
+ git_reference_free(octo2_ref);
+
+ git_annotated_commit_free(our_head);
+ git_annotated_commit_free(their_heads[0]);
+ git_annotated_commit_free(their_heads[1]);
+}
+
+/* git merge refs/remotes/origin/octo1 refs/remotes/origin/octo2 refs/remotes/origin/octo3 */
+void test_merge_workdir_setup__remote_tracking_three_branches(void)
+{
+ git_oid our_oid;
+ git_reference *octo1_ref;
+ git_reference *octo2_ref;
+ git_reference *octo3_ref;
+ git_annotated_commit *our_head, *their_heads[3];
+
+ cl_git_pass(create_remote_tracking_branch(OCTO1_BRANCH, OCTO1_OID));
+ cl_git_pass(create_remote_tracking_branch(OCTO2_BRANCH, OCTO2_OID));
+ cl_git_pass(create_remote_tracking_branch(OCTO3_BRANCH, OCTO3_OID));
+
+ cl_git_pass(git_oid__fromstr(&our_oid, ORIG_HEAD, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO1_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[0], repo, octo1_ref));
+
+ cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO2_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[1], repo, octo2_ref));
+
+ cl_git_pass(git_reference_lookup(&octo3_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO3_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[2], repo, octo3_ref));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 3));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff"));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge remote-tracking branches 'refs/remotes/origin/" OCTO1_BRANCH "', 'refs/remotes/origin/" OCTO2_BRANCH "' and 'refs/remotes/origin/" OCTO3_BRANCH "'\n"));
+
+ git_reference_free(octo1_ref);
+ git_reference_free(octo2_ref);
+ git_reference_free(octo3_ref);
+
+ git_annotated_commit_free(our_head);
+ git_annotated_commit_free(their_heads[0]);
+ git_annotated_commit_free(their_heads[1]);
+ git_annotated_commit_free(their_heads[2]);
+}
+
+/* git merge octo1 refs/remotes/origin/octo2 */
+void test_merge_workdir_setup__normal_branch_and_remote_tracking_branch(void)
+{
+ git_oid our_oid;
+ git_reference *octo1_ref;
+ git_reference *octo2_ref;
+ git_annotated_commit *our_head, *their_heads[2];
+
+ cl_git_pass(create_remote_tracking_branch(OCTO2_BRANCH, OCTO2_OID));
+
+ cl_git_pass(git_oid__fromstr(&our_oid, ORIG_HEAD, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[0], repo, octo1_ref));
+
+ cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO2_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[1], repo, octo2_ref));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 2));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff"));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch '" OCTO1_BRANCH "', remote-tracking branch 'refs/remotes/origin/" OCTO2_BRANCH "'\n"));
+
+ git_reference_free(octo1_ref);
+ git_reference_free(octo2_ref);
+
+ git_annotated_commit_free(our_head);
+ git_annotated_commit_free(their_heads[0]);
+ git_annotated_commit_free(their_heads[1]);
+}
+
+/* git merge refs/remotes/origin/octo1 octo2 */
+void test_merge_workdir_setup__remote_tracking_branch_and_normal_branch(void)
+{
+ git_oid our_oid;
+ git_reference *octo1_ref;
+ git_reference *octo2_ref;
+ git_annotated_commit *our_head, *their_heads[2];
+
+ cl_git_pass(create_remote_tracking_branch(OCTO1_BRANCH, OCTO1_OID));
+
+ cl_git_pass(git_oid__fromstr(&our_oid, ORIG_HEAD, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO1_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[0], repo, octo1_ref));
+
+ cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[1], repo, octo2_ref));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 2));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff"));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch '" OCTO2_BRANCH "', remote-tracking branch 'refs/remotes/origin/" OCTO1_BRANCH "'\n"));
+
+ git_reference_free(octo1_ref);
+ git_reference_free(octo2_ref);
+
+ git_annotated_commit_free(our_head);
+ git_annotated_commit_free(their_heads[0]);
+ git_annotated_commit_free(their_heads[1]);
+}
+
+/* git merge octo1 refs/remotes/origin/octo2 octo3 refs/remotes/origin/octo4 */
+void test_merge_workdir_setup__two_remote_tracking_branch_and_two_normal_branches(void)
+{
+ git_oid our_oid;
+ git_reference *octo1_ref;
+ git_reference *octo2_ref;
+ git_reference *octo3_ref;
+ git_reference *octo4_ref;
+ git_annotated_commit *our_head, *their_heads[4];
+
+ cl_git_pass(create_remote_tracking_branch(OCTO2_BRANCH, OCTO2_OID));
+ cl_git_pass(create_remote_tracking_branch(OCTO4_BRANCH, OCTO4_OID));
+
+ cl_git_pass(git_oid__fromstr(&our_oid, ORIG_HEAD, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[0], repo, octo1_ref));
+
+ cl_git_pass(git_reference_lookup(&octo2_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO2_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[1], repo, octo2_ref));
+
+ cl_git_pass(git_reference_lookup(&octo3_ref, repo, GIT_REFS_HEADS_DIR OCTO3_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[2], repo, octo3_ref));
+
+ cl_git_pass(git_reference_lookup(&octo4_ref, repo, GIT_REFS_REMOTES_DIR "origin/" OCTO4_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[3], repo, octo4_ref));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 4));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n" OCTO4_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff"));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "' and '" OCTO3_BRANCH "', remote-tracking branches 'refs/remotes/origin/" OCTO2_BRANCH "' and 'refs/remotes/origin/" OCTO4_BRANCH "'\n"));
+
+ git_reference_free(octo1_ref);
+ git_reference_free(octo2_ref);
+ git_reference_free(octo3_ref);
+ git_reference_free(octo4_ref);
+
+ git_annotated_commit_free(our_head);
+ git_annotated_commit_free(their_heads[0]);
+ git_annotated_commit_free(their_heads[1]);
+ git_annotated_commit_free(their_heads[2]);
+ git_annotated_commit_free(their_heads[3]);
+}
+
+/* git pull origin branch octo1 */
+void test_merge_workdir_setup__pull_one(void)
+{
+ git_oid our_oid;
+ git_oid octo1_1_oid;
+ git_annotated_commit *our_head, *their_heads[1];
+
+ cl_git_pass(git_oid__fromstr(&our_oid, ORIG_HEAD, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_oid__fromstr(&octo1_1_oid, OCTO1_OID, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_from_fetchhead(&their_heads[0], repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH, "http://remote.url/repo.git", &octo1_1_oid));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 1));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff"));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch 'octo1' of http://remote.url/repo.git\n"));
+
+ git_annotated_commit_free(our_head);
+ git_annotated_commit_free(their_heads[0]);
+}
+
+/* git pull origin octo1 octo2 */
+void test_merge_workdir_setup__pull_two(void)
+{
+ git_oid our_oid;
+ git_oid octo1_oid;
+ git_oid octo2_oid;
+ git_annotated_commit *our_head, *their_heads[2];
+
+ cl_git_pass(git_oid__fromstr(&our_oid, ORIG_HEAD, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_oid__fromstr(&octo1_oid, OCTO1_OID, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_from_fetchhead(&their_heads[0], repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH, "http://remote.url/repo.git", &octo1_oid));
+
+ cl_git_pass(git_oid__fromstr(&octo2_oid, OCTO2_OID, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_from_fetchhead(&their_heads[1], repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH, "http://remote.url/repo.git", &octo2_oid));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 2));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff"));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "' and '" OCTO2_BRANCH "' of http://remote.url/repo.git\n"));
+
+ git_annotated_commit_free(our_head);
+ git_annotated_commit_free(their_heads[0]);
+ git_annotated_commit_free(their_heads[1]);
+}
+
+/* git pull origin octo1 octo2 octo3 */
+void test_merge_workdir_setup__pull_three(void)
+{
+ git_oid our_oid;
+ git_oid octo1_oid;
+ git_oid octo2_oid;
+ git_oid octo3_oid;
+ git_annotated_commit *our_head, *their_heads[3];
+
+ cl_git_pass(git_oid__fromstr(&our_oid, ORIG_HEAD, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_oid__fromstr(&octo1_oid, OCTO1_OID, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_from_fetchhead(&their_heads[0], repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH, "http://remote.url/repo.git", &octo1_oid));
+
+ cl_git_pass(git_oid__fromstr(&octo2_oid, OCTO2_OID, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_from_fetchhead(&their_heads[1], repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH, "http://remote.url/repo.git", &octo2_oid));
+
+ cl_git_pass(git_oid__fromstr(&octo3_oid, OCTO3_OID, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_from_fetchhead(&their_heads[2], repo, GIT_REFS_HEADS_DIR OCTO3_BRANCH, "http://remote.url/repo.git", &octo3_oid));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 3));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff"));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "', '" OCTO2_BRANCH "' and '" OCTO3_BRANCH "' of http://remote.url/repo.git\n"));
+
+ git_annotated_commit_free(our_head);
+ git_annotated_commit_free(their_heads[0]);
+ git_annotated_commit_free(their_heads[1]);
+ git_annotated_commit_free(their_heads[2]);
+}
+
+void test_merge_workdir_setup__three_remotes(void)
+{
+ git_oid our_oid;
+ git_oid octo1_oid;
+ git_oid octo2_oid;
+ git_oid octo3_oid;
+ git_annotated_commit *our_head, *their_heads[3];
+
+ cl_git_pass(git_oid__fromstr(&our_oid, ORIG_HEAD, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_oid__fromstr(&octo1_oid, OCTO1_OID, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_from_fetchhead(&their_heads[0], repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH, "http://remote.first/repo.git", &octo1_oid));
+
+ cl_git_pass(git_oid__fromstr(&octo2_oid, OCTO2_OID, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_from_fetchhead(&their_heads[1], repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH, "http://remote.second/repo.git", &octo2_oid));
+
+ cl_git_pass(git_oid__fromstr(&octo3_oid, OCTO3_OID, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_from_fetchhead(&their_heads[2], repo, GIT_REFS_HEADS_DIR OCTO3_BRANCH, "http://remote.third/repo.git", &octo3_oid));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 3));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff"));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch '" OCTO1_BRANCH "' of http://remote.first/repo.git, branch '" OCTO2_BRANCH "' of http://remote.second/repo.git, branch '" OCTO3_BRANCH "' of http://remote.third/repo.git\n"));
+
+ git_annotated_commit_free(our_head);
+ git_annotated_commit_free(their_heads[0]);
+ git_annotated_commit_free(their_heads[1]);
+ git_annotated_commit_free(their_heads[2]);
+}
+
+void test_merge_workdir_setup__two_remotes(void)
+{
+ git_oid our_oid;
+ git_oid octo1_oid;
+ git_oid octo2_oid;
+ git_oid octo3_oid;
+ git_oid octo4_oid;
+ git_annotated_commit *our_head, *their_heads[4];
+
+ cl_git_pass(git_oid__fromstr(&our_oid, ORIG_HEAD, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_oid__fromstr(&octo1_oid, OCTO1_OID, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_from_fetchhead(&their_heads[0], repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH, "http://remote.first/repo.git", &octo1_oid));
+
+ cl_git_pass(git_oid__fromstr(&octo2_oid, OCTO2_OID, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_from_fetchhead(&their_heads[1], repo, GIT_REFS_HEADS_DIR OCTO2_BRANCH, "http://remote.second/repo.git", &octo2_oid));
+
+ cl_git_pass(git_oid__fromstr(&octo3_oid, OCTO3_OID, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_from_fetchhead(&their_heads[2], repo, GIT_REFS_HEADS_DIR OCTO3_BRANCH, "http://remote.first/repo.git", &octo3_oid));
+
+ cl_git_pass(git_oid__fromstr(&octo4_oid, OCTO4_OID, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_from_fetchhead(&their_heads[3], repo, GIT_REFS_HEADS_DIR OCTO4_BRANCH, "http://remote.second/repo.git", &octo4_oid));
+
+ cl_git_pass(git_merge__setup(repo, our_head, (const git_annotated_commit **)their_heads, 4));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n" OCTO2_OID "\n" OCTO3_OID "\n" OCTO4_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff"));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branches '" OCTO1_BRANCH "' and '" OCTO3_BRANCH "' of http://remote.first/repo.git, branches '" OCTO2_BRANCH "' and '" OCTO4_BRANCH "' of http://remote.second/repo.git\n"));
+
+ git_annotated_commit_free(our_head);
+ git_annotated_commit_free(their_heads[0]);
+ git_annotated_commit_free(their_heads[1]);
+ git_annotated_commit_free(their_heads[2]);
+ git_annotated_commit_free(their_heads[3]);
+}
+
+void test_merge_workdir_setup__id_from_head(void)
+{
+ git_oid expected_id;
+ const git_oid *id;
+ git_reference *ref;
+ git_annotated_commit *heads[3];
+
+ cl_git_pass(git_oid__fromstr(&expected_id, OCTO1_OID, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_from_fetchhead(&heads[0], repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH, "http://remote.url/repo.git", &expected_id));
+ id = git_annotated_commit_id(heads[0]);
+ cl_assert_equal_i(1, git_oid_equal(id, &expected_id));
+
+ cl_git_pass(git_annotated_commit_lookup(&heads[1], repo, &expected_id));
+ id = git_annotated_commit_id(heads[1]);
+ cl_assert_equal_i(1, git_oid_equal(id, &expected_id));
+
+ cl_git_pass(git_reference_lookup(&ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&heads[2], repo, ref));
+ id = git_annotated_commit_id(heads[2]);
+ cl_assert_equal_i(1, git_oid_equal(id, &expected_id));
+
+ git_reference_free(ref);
+ git_annotated_commit_free(heads[0]);
+ git_annotated_commit_free(heads[1]);
+ git_annotated_commit_free(heads[2]);
+}
+
+struct annotated_commit_cb_data {
+ const char **oid_str;
+ unsigned int len;
+
+ unsigned int i;
+};
+
+static int annotated_commit_foreach_cb(const git_oid *oid, void *payload)
+{
+ git_oid expected_oid;
+ struct annotated_commit_cb_data *cb_data = payload;
+
+ git_oid__fromstr(&expected_oid, cb_data->oid_str[cb_data->i], GIT_OID_SHA1);
+ cl_assert(git_oid_cmp(&expected_oid, oid) == 0);
+ cb_data->i++;
+ return 0;
+}
+
+void test_merge_workdir_setup__head_notfound(void)
+{
+ int error;
+
+ cl_git_fail((error = git_repository_mergehead_foreach(repo,
+ annotated_commit_foreach_cb, NULL)));
+ cl_assert(error == GIT_ENOTFOUND);
+}
+
+void test_merge_workdir_setup__head_invalid_oid(void)
+{
+ int error;
+
+ write_file_contents(GIT_MERGE_HEAD_FILE, "invalid-oid\n");
+
+ cl_git_fail((error = git_repository_mergehead_foreach(repo,
+ annotated_commit_foreach_cb, NULL)));
+ cl_assert(error == -1);
+}
+
+void test_merge_workdir_setup__head_foreach_nonewline(void)
+{
+ int error;
+
+ write_file_contents(GIT_MERGE_HEAD_FILE, THEIRS_SIMPLE_OID);
+
+ cl_git_fail((error = git_repository_mergehead_foreach(repo,
+ annotated_commit_foreach_cb, NULL)));
+ cl_assert(error == -1);
+}
+
+void test_merge_workdir_setup__head_foreach_one(void)
+{
+ const char *expected = THEIRS_SIMPLE_OID;
+
+ struct annotated_commit_cb_data cb_data = { &expected, 1 };
+
+ write_file_contents(GIT_MERGE_HEAD_FILE, THEIRS_SIMPLE_OID "\n");
+
+ cl_git_pass(git_repository_mergehead_foreach(repo,
+ annotated_commit_foreach_cb, &cb_data));
+
+ cl_assert(cb_data.i == cb_data.len);
+}
+
+void test_merge_workdir_setup__head_foreach_octopus(void)
+{
+ const char *expected[] = { THEIRS_SIMPLE_OID,
+ OCTO1_OID, OCTO2_OID, OCTO3_OID, OCTO4_OID, OCTO5_OID };
+
+ struct annotated_commit_cb_data cb_data = { expected, 6 };
+
+ write_file_contents(GIT_MERGE_HEAD_FILE,
+ THEIRS_SIMPLE_OID "\n"
+ OCTO1_OID "\n"
+ OCTO2_OID "\n"
+ OCTO3_OID "\n"
+ OCTO4_OID "\n"
+ OCTO5_OID "\n");
+
+ cl_git_pass(git_repository_mergehead_foreach(repo,
+ annotated_commit_foreach_cb, &cb_data));
+
+ cl_assert(cb_data.i == cb_data.len);
+}
+
+void test_merge_workdir_setup__retained_after_success(void)
+{
+ git_oid our_oid;
+ git_reference *octo1_ref;
+ git_annotated_commit *our_head, *their_heads[1];
+
+ cl_git_pass(git_oid__fromstr(&our_oid, ORIG_HEAD, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
+
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[0], repo, octo1_ref));
+
+ cl_git_pass(git_merge(repo, (const git_annotated_commit **)&their_heads[0], 1, NULL, NULL));
+
+ cl_assert(test_file_contents(GIT_MERGE_HEAD_FILE, OCTO1_OID "\n"));
+ cl_assert(test_file_contents(GIT_ORIG_HEAD_FILE, ORIG_HEAD "\n"));
+ cl_assert(test_file_contents(GIT_MERGE_MODE_FILE, "no-ff"));
+ cl_assert(test_file_contents(GIT_MERGE_MSG_FILE, "Merge branch '" OCTO1_BRANCH "'\n"));
+
+ git_reference_free(octo1_ref);
+
+ git_annotated_commit_free(our_head);
+ git_annotated_commit_free(their_heads[0]);
+}
+
+
+void test_merge_workdir_setup__removed_after_failure(void)
+{
+ git_oid our_oid;
+ git_reference *octo1_ref;
+ git_annotated_commit *our_head, *their_heads[1];
+
+ cl_git_pass(git_oid__fromstr(&our_oid, ORIG_HEAD, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[0], repo, octo1_ref));
+
+ cl_git_write2file("merge-resolve/.git/index.lock", "foo\n", 4, O_RDWR|O_CREAT, 0666);
+
+ cl_git_fail(git_merge(
+ repo, (const git_annotated_commit **)&their_heads[0], 1, NULL, NULL));
+
+ cl_assert(!git_fs_path_exists("merge-resolve/.git/" GIT_MERGE_HEAD_FILE));
+ cl_assert(!git_fs_path_exists("merge-resolve/.git/" GIT_MERGE_MODE_FILE));
+ cl_assert(!git_fs_path_exists("merge-resolve/.git/" GIT_MERGE_MSG_FILE));
+
+ git_reference_free(octo1_ref);
+
+ git_annotated_commit_free(our_head);
+ git_annotated_commit_free(their_heads[0]);
+}
+
+void test_merge_workdir_setup__unlocked_after_success(void)
+{
+ git_oid our_oid;
+ git_reference *octo1_ref;
+ git_annotated_commit *our_head, *their_heads[1];
+
+ cl_git_pass(git_oid__fromstr(&our_oid, ORIG_HEAD, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[0], repo, octo1_ref));
+
+ cl_git_pass(git_merge(
+ repo, (const git_annotated_commit **)&their_heads[0], 1, NULL, NULL));
+
+ cl_assert(!git_fs_path_exists("merge-resolve/.git/index.lock"));
+
+ git_reference_free(octo1_ref);
+
+ git_annotated_commit_free(our_head);
+ git_annotated_commit_free(their_heads[0]);
+}
+
+void test_merge_workdir_setup__unlocked_after_conflict(void)
+{
+ git_oid our_oid;
+ git_reference *octo1_ref;
+ git_annotated_commit *our_head, *their_heads[1];
+
+ cl_git_pass(git_oid__fromstr(&our_oid, ORIG_HEAD, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&our_head, repo, &our_oid));
+
+ cl_git_pass(git_reference_lookup(&octo1_ref, repo, GIT_REFS_HEADS_DIR OCTO1_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[0], repo, octo1_ref));
+
+ cl_git_rewritefile("merge-resolve/new-in-octo1.txt",
+ "Conflicting file!\n\nMerge will fail!\n");
+
+ cl_git_fail(git_merge(
+ repo, (const git_annotated_commit **)&their_heads[0], 1, NULL, NULL));
+
+ cl_assert(!git_fs_path_exists("merge-resolve/.git/index.lock"));
+
+ git_reference_free(octo1_ref);
+
+ git_annotated_commit_free(our_head);
+ git_annotated_commit_free(their_heads[0]);
+}
diff --git a/tests/libgit2/merge/workdir/simple.c b/tests/libgit2/merge/workdir/simple.c
new file mode 100644
index 0000000..e9cffee
--- /dev/null
+++ b/tests/libgit2/merge/workdir/simple.c
@@ -0,0 +1,778 @@
+#include "clar_libgit2.h"
+#include "git2/repository.h"
+#include "git2/merge.h"
+#include "merge.h"
+#include "../merge_helpers.h"
+#include "../conflict_data.h"
+#include "refs.h"
+#include "futils.h"
+
+static git_repository *repo;
+static git_index *repo_index;
+
+#define TEST_REPO_PATH "merge-resolve"
+#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
+
+#define THEIRS_SIMPLE_BRANCH "branch"
+#define THEIRS_SIMPLE_OID "7cb63eed597130ba4abb87b3e544b85021905520"
+
+#define THEIRS_UNRELATED_BRANCH "unrelated"
+#define THEIRS_UNRELATED_OID "55b4e4687e7a0d9ca367016ed930f385d4022e6f"
+#define THEIRS_UNRELATED_PARENT "d6cf6c7741b3316826af1314042550c97ded1d50"
+
+#define OURS_DIRECTORY_FILE "df_side1"
+#define THEIRS_DIRECTORY_FILE "fc90237dc4891fa6c69827fc465632225e391618"
+
+
+/* Non-conflicting files, index entries are common to every merge operation */
+#define ADDED_IN_MASTER_INDEX_ENTRY \
+ { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, \
+ "added-in-master.txt" }
+#define AUTOMERGEABLE_INDEX_ENTRY \
+ { 0100644, "f2e1550a0c9e53d5811175864a29536642ae3821", 0, \
+ "automergeable.txt" }
+#define CHANGED_IN_BRANCH_INDEX_ENTRY \
+ { 0100644, "4eb04c9e79e88f6640d01ff5b25ca2a60764f216", 0, \
+ "changed-in-branch.txt" }
+#define CHANGED_IN_MASTER_INDEX_ENTRY \
+ { 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, \
+ "changed-in-master.txt" }
+#define UNCHANGED_INDEX_ENTRY \
+ { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, \
+ "unchanged.txt" }
+
+/* Unrelated files */
+#define UNRELATED_NEW1 \
+ { 0100644, "ef58fdd8086c243bdc81f99e379acacfd21d32d6", 0, \
+ "new-in-unrelated1.txt" }
+#define UNRELATED_NEW2 \
+ { 0100644, "948ba6e701c1edab0c2d394fb7c5538334129793", 0, \
+ "new-in-unrelated2.txt" }
+
+/* Expected REUC entries */
+#define AUTOMERGEABLE_REUC_ENTRY \
+ { "automergeable.txt", 0100644, 0100644, 0100644, \
+ "6212c31dab5e482247d7977e4f0dd3601decf13b", \
+ "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", \
+ "058541fc37114bfc1dddf6bd6bffc7fae5c2e6fe" }
+#define CONFLICTING_REUC_ENTRY \
+ { "conflicting.txt", 0100644, 0100644, 0100644, \
+ "d427e0b2e138501a3d15cc376077a3631e15bd46", \
+ "4e886e602529caa9ab11d71f86634bd1b6e0de10", \
+ "2bd0a343aeef7a2cf0d158478966a6e587ff3863" }
+#define REMOVED_IN_BRANCH_REUC_ENTRY \
+ { "removed-in-branch.txt", 0100644, 0100644, 0, \
+ "dfe3f22baa1f6fce5447901c3086bae368de6bdd", \
+ "dfe3f22baa1f6fce5447901c3086bae368de6bdd", \
+ "" }
+#define REMOVED_IN_MASTER_REUC_ENTRY \
+ { "removed-in-master.txt", 0100644, 0, 0100644, \
+ "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5", \
+ "", \
+ "5c3b68a71fc4fa5d362fd3875e53137c6a5ab7a5" }
+
+
+/* Fixture setup and teardown */
+void test_merge_workdir_simple__initialize(void)
+{
+ git_config *cfg;
+
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+ git_repository_index(&repo_index, repo);
+
+ /* Ensure that the user's merge.conflictstyle doesn't interfere */
+ cl_git_pass(git_repository_config(&cfg, repo));
+ cl_git_pass(git_config_set_string(cfg, "merge.conflictstyle", "merge"));
+ git_config_free(cfg);
+}
+
+void test_merge_workdir_simple__cleanup(void)
+{
+ git_index_free(repo_index);
+ cl_git_sandbox_cleanup();
+}
+
+static void merge_simple_branch(int merge_file_favor, int addl_checkout_strategy)
+{
+ git_oid their_oids[1];
+ git_annotated_commit *their_heads[1];
+ git_merge_options merge_opts = GIT_MERGE_OPTIONS_INIT;
+ git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ cl_git_pass(git_oid__fromstr(&their_oids[0], THEIRS_SIMPLE_OID, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&their_heads[0], repo, &their_oids[0]));
+
+ merge_opts.file_favor = merge_file_favor;
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_ALLOW_CONFLICTS |
+ addl_checkout_strategy;
+
+ cl_git_pass(git_merge(repo, (const git_annotated_commit **)their_heads, 1, &merge_opts, &checkout_opts));
+
+ git_annotated_commit_free(their_heads[0]);
+}
+
+static void set_core_autocrlf_to(git_repository *repo, bool value)
+{
+ git_config *cfg;
+
+ cl_git_pass(git_repository_config(&cfg, repo));
+ cl_git_pass(git_config_set_bool(cfg, "core.autocrlf", value));
+
+ git_config_free(cfg);
+}
+
+void test_merge_workdir_simple__automerge(void)
+{
+ git_index *index;
+ const git_index_entry *entry;
+ git_str automergeable_buf = GIT_STR_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ ADDED_IN_MASTER_INDEX_ENTRY,
+ AUTOMERGEABLE_INDEX_ENTRY,
+ CHANGED_IN_BRANCH_INDEX_ENTRY,
+ CHANGED_IN_MASTER_INDEX_ENTRY,
+
+ { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 1, "conflicting.txt" },
+ { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" },
+ { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 3, "conflicting.txt" },
+
+ UNCHANGED_INDEX_ENTRY,
+ };
+
+ struct merge_reuc_entry merge_reuc_entries[] = {
+ AUTOMERGEABLE_REUC_ENTRY,
+ REMOVED_IN_BRANCH_REUC_ENTRY,
+ REMOVED_IN_MASTER_REUC_ENTRY
+ };
+
+
+ set_core_autocrlf_to(repo, false);
+
+ merge_simple_branch(0, 0);
+
+ cl_git_pass(git_futils_readbuffer(&automergeable_buf,
+ TEST_REPO_PATH "/automergeable.txt"));
+ cl_assert(strcmp(automergeable_buf.ptr, AUTOMERGEABLE_MERGED_FILE) == 0);
+ git_str_dispose(&automergeable_buf);
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 8));
+ cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 3));
+
+ git_repository_index(&index, repo);
+
+ cl_assert((entry = git_index_get_bypath(index, "automergeable.txt", 0)) != NULL);
+ cl_assert(entry->file_size == strlen(AUTOMERGEABLE_MERGED_FILE));
+
+ git_index_free(index);
+}
+
+void test_merge_workdir_simple__index_reload(void)
+{
+ git_repository *tmp_repo;
+ git_annotated_commit *their_heads[1];
+ git_oid their_oid;
+ git_index_entry entry = {{0}};
+ git_index *tmp_index;
+
+ cl_git_pass(git_repository_open(&tmp_repo, git_repository_workdir(repo)));
+ cl_git_pass(git_repository_index(&tmp_index, tmp_repo));
+ cl_git_pass(git_index_read(repo_index, 0));
+
+ entry.mode = GIT_FILEMODE_BLOB;
+ cl_git_pass(git_oid__fromstr(&entry.id, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", GIT_OID_SHA1));
+ entry.path = "automergeable.txt";
+ cl_git_pass(git_index_add(repo_index, &entry));
+
+ cl_git_pass(git_index_add_bypath(tmp_index, "automergeable.txt"));
+ cl_git_pass(git_index_write(tmp_index));
+
+ cl_git_pass(git_oid__fromstr(&their_oid, THEIRS_SIMPLE_OID, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&their_heads[0], repo, &their_oid));
+ cl_git_pass(git_merge(repo, (const git_annotated_commit **)their_heads, 1, NULL, NULL));
+
+ git_index_free(tmp_index);
+ git_repository_free(tmp_repo);
+ git_annotated_commit_free(their_heads[0]);
+}
+
+void test_merge_workdir_simple__automerge_crlf(void)
+{
+#ifdef GIT_WIN32
+ git_index *index;
+ const git_index_entry *entry;
+ git_str automergeable_buf = GIT_STR_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ ADDED_IN_MASTER_INDEX_ENTRY,
+ AUTOMERGEABLE_INDEX_ENTRY,
+ CHANGED_IN_BRANCH_INDEX_ENTRY,
+ CHANGED_IN_MASTER_INDEX_ENTRY,
+
+ { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 1, "conflicting.txt" },
+ { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" },
+ { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 3, "conflicting.txt" },
+
+ UNCHANGED_INDEX_ENTRY,
+ };
+
+ struct merge_reuc_entry merge_reuc_entries[] = {
+ AUTOMERGEABLE_REUC_ENTRY,
+ REMOVED_IN_BRANCH_REUC_ENTRY,
+ REMOVED_IN_MASTER_REUC_ENTRY
+ };
+
+ set_core_autocrlf_to(repo, true);
+
+ merge_simple_branch(0, 0);
+
+ cl_git_pass(git_futils_readbuffer(&automergeable_buf,
+ TEST_REPO_PATH "/automergeable.txt"));
+ cl_assert(strcmp(automergeable_buf.ptr, AUTOMERGEABLE_MERGED_FILE_CRLF) == 0);
+ git_str_dispose(&automergeable_buf);
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 8));
+ cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 3));
+
+ git_repository_index(&index, repo);
+
+ cl_assert((entry = git_index_get_bypath(index, "automergeable.txt", 0)) != NULL);
+ cl_assert(entry->file_size == strlen(AUTOMERGEABLE_MERGED_FILE_CRLF));
+
+ git_index_free(index);
+#endif /* GIT_WIN32 */
+}
+
+void test_merge_workdir_simple__mergefile(void)
+{
+ git_str conflicting_buf = GIT_STR_INIT, mergemsg_buf = GIT_STR_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ ADDED_IN_MASTER_INDEX_ENTRY,
+ AUTOMERGEABLE_INDEX_ENTRY,
+ CHANGED_IN_BRANCH_INDEX_ENTRY,
+ CHANGED_IN_MASTER_INDEX_ENTRY,
+
+ { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 1, "conflicting.txt" },
+ { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" },
+ { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 3, "conflicting.txt" },
+
+ UNCHANGED_INDEX_ENTRY,
+ };
+
+ struct merge_reuc_entry merge_reuc_entries[] = {
+ AUTOMERGEABLE_REUC_ENTRY,
+ REMOVED_IN_BRANCH_REUC_ENTRY,
+ REMOVED_IN_MASTER_REUC_ENTRY
+ };
+
+ set_core_autocrlf_to(repo, false);
+
+ merge_simple_branch(0, 0);
+
+ cl_git_pass(git_futils_readbuffer(&conflicting_buf,
+ TEST_REPO_PATH "/conflicting.txt"));
+ cl_assert(strcmp(conflicting_buf.ptr, CONFLICTING_MERGE_FILE) == 0);
+ cl_git_pass(git_futils_readbuffer(&mergemsg_buf,
+ TEST_REPO_PATH "/.git/MERGE_MSG"));
+ cl_assert(strcmp(git_str_cstr(&mergemsg_buf),
+ "Merge commit '7cb63eed597130ba4abb87b3e544b85021905520'\n" \
+ "\n" \
+ "#Conflicts:\n" \
+ "#\tconflicting.txt\n") == 0);
+ git_str_dispose(&conflicting_buf);
+ git_str_dispose(&mergemsg_buf);
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 8));
+ cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 3));
+}
+
+void test_merge_workdir_simple__diff3(void)
+{
+ git_str conflicting_buf = GIT_STR_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ ADDED_IN_MASTER_INDEX_ENTRY,
+ AUTOMERGEABLE_INDEX_ENTRY,
+ CHANGED_IN_BRANCH_INDEX_ENTRY,
+ CHANGED_IN_MASTER_INDEX_ENTRY,
+
+ { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 1, "conflicting.txt" },
+ { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" },
+ { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 3, "conflicting.txt" },
+
+ UNCHANGED_INDEX_ENTRY,
+ };
+
+ struct merge_reuc_entry merge_reuc_entries[] = {
+ AUTOMERGEABLE_REUC_ENTRY,
+ REMOVED_IN_BRANCH_REUC_ENTRY,
+ REMOVED_IN_MASTER_REUC_ENTRY
+ };
+
+ set_core_autocrlf_to(repo, false);
+
+ merge_simple_branch(0, GIT_CHECKOUT_CONFLICT_STYLE_DIFF3);
+
+ cl_git_pass(git_futils_readbuffer(&conflicting_buf,
+ TEST_REPO_PATH "/conflicting.txt"));
+ cl_assert(strcmp(conflicting_buf.ptr, CONFLICTING_DIFF3_FILE) == 0);
+ git_str_dispose(&conflicting_buf);
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 8));
+ cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 3));
+}
+
+void test_merge_workdir_simple__zdiff3(void)
+{
+ git_str conflicting_buf = GIT_STR_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ ADDED_IN_MASTER_INDEX_ENTRY,
+ AUTOMERGEABLE_INDEX_ENTRY,
+ CHANGED_IN_BRANCH_INDEX_ENTRY,
+ CHANGED_IN_MASTER_INDEX_ENTRY,
+
+ { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 1, "conflicting.txt" },
+ { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" },
+ { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 3, "conflicting.txt" },
+
+ UNCHANGED_INDEX_ENTRY,
+ };
+
+ struct merge_reuc_entry merge_reuc_entries[] = {
+ AUTOMERGEABLE_REUC_ENTRY,
+ REMOVED_IN_BRANCH_REUC_ENTRY,
+ REMOVED_IN_MASTER_REUC_ENTRY
+ };
+
+ set_core_autocrlf_to(repo, false);
+
+ merge_simple_branch(0, GIT_CHECKOUT_CONFLICT_STYLE_ZDIFF3);
+
+ cl_git_pass(git_futils_readbuffer(&conflicting_buf,
+ TEST_REPO_PATH "/conflicting.txt"));
+ cl_assert_equal_s(CONFLICTING_ZDIFF3_FILE, conflicting_buf.ptr);
+ git_str_dispose(&conflicting_buf);
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 8));
+ cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 3));
+}
+
+void test_merge_workdir_simple__union(void)
+{
+ git_str conflicting_buf = GIT_STR_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ ADDED_IN_MASTER_INDEX_ENTRY,
+ AUTOMERGEABLE_INDEX_ENTRY,
+ CHANGED_IN_BRANCH_INDEX_ENTRY,
+ CHANGED_IN_MASTER_INDEX_ENTRY,
+
+ { 0100644, "72cdb057b340205164478565e91eb71647e66891", 0, "conflicting.txt" },
+
+ UNCHANGED_INDEX_ENTRY,
+ };
+
+ struct merge_reuc_entry merge_reuc_entries[] = {
+ AUTOMERGEABLE_REUC_ENTRY,
+ CONFLICTING_REUC_ENTRY,
+ REMOVED_IN_BRANCH_REUC_ENTRY,
+ REMOVED_IN_MASTER_REUC_ENTRY
+ };
+
+ set_core_autocrlf_to(repo, false);
+
+ merge_simple_branch(GIT_MERGE_FILE_FAVOR_UNION, 0);
+
+ cl_git_pass(git_futils_readbuffer(&conflicting_buf,
+ TEST_REPO_PATH "/conflicting.txt"));
+ cl_assert(strcmp(conflicting_buf.ptr, CONFLICTING_UNION_FILE) == 0);
+ git_str_dispose(&conflicting_buf);
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 6));
+ cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 4));
+}
+
+void test_merge_workdir_simple__gitattributes_union(void)
+{
+ git_str conflicting_buf = GIT_STR_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ ADDED_IN_MASTER_INDEX_ENTRY,
+ AUTOMERGEABLE_INDEX_ENTRY,
+ CHANGED_IN_BRANCH_INDEX_ENTRY,
+ CHANGED_IN_MASTER_INDEX_ENTRY,
+
+ { 0100644, "72cdb057b340205164478565e91eb71647e66891", 0, "conflicting.txt" },
+
+ UNCHANGED_INDEX_ENTRY,
+ };
+
+ struct merge_reuc_entry merge_reuc_entries[] = {
+ AUTOMERGEABLE_REUC_ENTRY,
+ CONFLICTING_REUC_ENTRY,
+ REMOVED_IN_BRANCH_REUC_ENTRY,
+ REMOVED_IN_MASTER_REUC_ENTRY
+ };
+
+ set_core_autocrlf_to(repo, false);
+ cl_git_mkfile(TEST_REPO_PATH "/.gitattributes", "conflicting.txt merge=union\n");
+
+ merge_simple_branch(GIT_MERGE_FILE_FAVOR_NORMAL, 0);
+
+ cl_git_pass(git_futils_readbuffer(&conflicting_buf,
+ TEST_REPO_PATH "/conflicting.txt"));
+ cl_assert(strcmp(conflicting_buf.ptr, CONFLICTING_UNION_FILE) == 0);
+ git_str_dispose(&conflicting_buf);
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 6));
+ cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 4));
+}
+
+void test_merge_workdir_simple__diff3_from_config(void)
+{
+ git_config *config;
+ git_str conflicting_buf = GIT_STR_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ ADDED_IN_MASTER_INDEX_ENTRY,
+ AUTOMERGEABLE_INDEX_ENTRY,
+ CHANGED_IN_BRANCH_INDEX_ENTRY,
+ CHANGED_IN_MASTER_INDEX_ENTRY,
+
+ { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 1, "conflicting.txt" },
+ { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" },
+ { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 3, "conflicting.txt" },
+
+ UNCHANGED_INDEX_ENTRY,
+ };
+
+ struct merge_reuc_entry merge_reuc_entries[] = {
+ AUTOMERGEABLE_REUC_ENTRY,
+ REMOVED_IN_BRANCH_REUC_ENTRY,
+ REMOVED_IN_MASTER_REUC_ENTRY
+ };
+
+ cl_git_pass(git_repository_config(&config, repo));
+ cl_git_pass(git_config_set_string(config, "merge.conflictstyle", "diff3"));
+
+ set_core_autocrlf_to(repo, false);
+
+ merge_simple_branch(0, 0);
+
+ cl_git_pass(git_futils_readbuffer(&conflicting_buf,
+ TEST_REPO_PATH "/conflicting.txt"));
+ cl_assert(strcmp(conflicting_buf.ptr, CONFLICTING_DIFF3_FILE) == 0);
+ git_str_dispose(&conflicting_buf);
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 8));
+ cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 3));
+
+ git_config_free(config);
+}
+
+void test_merge_workdir_simple__zdiff3_from_config(void)
+{
+ git_config *config;
+ git_str conflicting_buf = GIT_STR_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ ADDED_IN_MASTER_INDEX_ENTRY,
+ AUTOMERGEABLE_INDEX_ENTRY,
+ CHANGED_IN_BRANCH_INDEX_ENTRY,
+ CHANGED_IN_MASTER_INDEX_ENTRY,
+
+ { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 1, "conflicting.txt" },
+ { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" },
+ { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 3, "conflicting.txt" },
+
+ UNCHANGED_INDEX_ENTRY,
+ };
+
+ struct merge_reuc_entry merge_reuc_entries[] = {
+ AUTOMERGEABLE_REUC_ENTRY,
+ REMOVED_IN_BRANCH_REUC_ENTRY,
+ REMOVED_IN_MASTER_REUC_ENTRY
+ };
+
+ cl_git_pass(git_repository_config(&config, repo));
+ cl_git_pass(git_config_set_string(config, "merge.conflictstyle", "zdiff3"));
+
+ set_core_autocrlf_to(repo, false);
+
+ merge_simple_branch(0, 0);
+
+ cl_git_pass(git_futils_readbuffer(&conflicting_buf,
+ TEST_REPO_PATH "/conflicting.txt"));
+ cl_assert(strcmp(conflicting_buf.ptr, CONFLICTING_ZDIFF3_FILE) == 0);
+ git_str_dispose(&conflicting_buf);
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 8));
+ cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 3));
+
+ git_config_free(config);
+}
+
+void test_merge_workdir_simple__merge_overrides_config(void)
+{
+ git_config *config;
+ git_str conflicting_buf = GIT_STR_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ ADDED_IN_MASTER_INDEX_ENTRY,
+ AUTOMERGEABLE_INDEX_ENTRY,
+ CHANGED_IN_BRANCH_INDEX_ENTRY,
+ CHANGED_IN_MASTER_INDEX_ENTRY,
+
+ { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 1, "conflicting.txt" },
+ { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" },
+ { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 3, "conflicting.txt" },
+
+ UNCHANGED_INDEX_ENTRY,
+ };
+
+ struct merge_reuc_entry merge_reuc_entries[] = {
+ AUTOMERGEABLE_REUC_ENTRY,
+ REMOVED_IN_BRANCH_REUC_ENTRY,
+ REMOVED_IN_MASTER_REUC_ENTRY
+ };
+
+ cl_git_pass(git_repository_config(&config, repo));
+ cl_git_pass(git_config_set_string(config, "merge.conflictstyle", "diff3"));
+
+ set_core_autocrlf_to(repo, false);
+
+ merge_simple_branch(0, GIT_CHECKOUT_CONFLICT_STYLE_MERGE);
+
+ cl_git_pass(git_futils_readbuffer(&conflicting_buf,
+ TEST_REPO_PATH "/conflicting.txt"));
+ cl_assert(strcmp(conflicting_buf.ptr, CONFLICTING_MERGE_FILE) == 0);
+ git_str_dispose(&conflicting_buf);
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 8));
+ cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 3));
+
+ git_config_free(config);
+}
+
+void test_merge_workdir_simple__checkout_ours(void)
+{
+ struct merge_index_entry merge_index_entries[] = {
+ ADDED_IN_MASTER_INDEX_ENTRY,
+ AUTOMERGEABLE_INDEX_ENTRY,
+ CHANGED_IN_BRANCH_INDEX_ENTRY,
+ CHANGED_IN_MASTER_INDEX_ENTRY,
+
+ { 0100644, "d427e0b2e138501a3d15cc376077a3631e15bd46", 1, "conflicting.txt" },
+ { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" },
+ { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 3, "conflicting.txt" },
+
+ UNCHANGED_INDEX_ENTRY,
+ };
+
+ struct merge_reuc_entry merge_reuc_entries[] = {
+ AUTOMERGEABLE_REUC_ENTRY,
+ REMOVED_IN_BRANCH_REUC_ENTRY,
+ REMOVED_IN_MASTER_REUC_ENTRY
+ };
+
+ merge_simple_branch(0, GIT_CHECKOUT_SAFE | GIT_CHECKOUT_USE_OURS);
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 8));
+ cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 3));
+
+ cl_assert(git_fs_path_exists(TEST_REPO_PATH "/conflicting.txt"));
+}
+
+void test_merge_workdir_simple__favor_ours(void)
+{
+ struct merge_index_entry merge_index_entries[] = {
+ ADDED_IN_MASTER_INDEX_ENTRY,
+ AUTOMERGEABLE_INDEX_ENTRY,
+ CHANGED_IN_BRANCH_INDEX_ENTRY,
+ CHANGED_IN_MASTER_INDEX_ENTRY,
+ { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 0, "conflicting.txt" },
+ UNCHANGED_INDEX_ENTRY,
+ };
+
+ struct merge_reuc_entry merge_reuc_entries[] = {
+ AUTOMERGEABLE_REUC_ENTRY,
+ CONFLICTING_REUC_ENTRY,
+ REMOVED_IN_BRANCH_REUC_ENTRY,
+ REMOVED_IN_MASTER_REUC_ENTRY,
+ };
+
+ merge_simple_branch(GIT_MERGE_FILE_FAVOR_OURS, 0);
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 6));
+ cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 4));
+}
+
+void test_merge_workdir_simple__favor_theirs(void)
+{
+ struct merge_index_entry merge_index_entries[] = {
+ ADDED_IN_MASTER_INDEX_ENTRY,
+ AUTOMERGEABLE_INDEX_ENTRY,
+ CHANGED_IN_BRANCH_INDEX_ENTRY,
+ CHANGED_IN_MASTER_INDEX_ENTRY,
+ { 0100644, "2bd0a343aeef7a2cf0d158478966a6e587ff3863", 0, "conflicting.txt" },
+ UNCHANGED_INDEX_ENTRY,
+ };
+
+ struct merge_reuc_entry merge_reuc_entries[] = {
+ AUTOMERGEABLE_REUC_ENTRY,
+ CONFLICTING_REUC_ENTRY,
+ REMOVED_IN_BRANCH_REUC_ENTRY,
+ REMOVED_IN_MASTER_REUC_ENTRY,
+ };
+
+ merge_simple_branch(GIT_MERGE_FILE_FAVOR_THEIRS, 0);
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 6));
+ cl_assert(merge_test_reuc(repo_index, merge_reuc_entries, 4));
+}
+
+void test_merge_workdir_simple__directory_file(void)
+{
+ git_reference *head;
+ git_oid their_oids[1], head_commit_id;
+ git_annotated_commit *their_heads[1];
+ git_merge_options merge_opts = GIT_MERGE_OPTIONS_INIT;
+ git_commit *head_commit;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "49130a28ef567af9a6a6104c38773fedfa5f9742", 2, "dir-10" },
+ { 0100644, "6c06dcd163587c2cc18be44857e0b71116382aeb", 3, "dir-10" },
+ { 0100644, "43aafd43bea779ec74317dc361f45ae3f532a505", 0, "dir-6" },
+ { 0100644, "a031a28ae70e33a641ce4b8a8f6317f1ab79dee4", 3, "dir-7" },
+ { 0100644, "5012fd565b1393bdfda1805d4ec38ce6619e1fd1", 1, "dir-7/file.txt" },
+ { 0100644, "a5563304ddf6caba25cb50323a2ea6f7dbfcadca", 2, "dir-7/file.txt" },
+ { 0100644, "e9ad6ec3e38364a3d07feda7c4197d4d845c53b5", 0, "dir-8" },
+ { 0100644, "3ef4d30382ca33fdeba9fda895a99e0891ba37aa", 2, "dir-9" },
+ { 0100644, "fc4c636d6515e9e261f9260dbcf3cc6eca97ea08", 1, "dir-9/file.txt" },
+ { 0100644, "76ab0e2868197ec158ddd6c78d8a0d2fd73d38f9", 3, "dir-9/file.txt" },
+ { 0100644, "5c2411f8075f48a6b2fdb85ebc0d371747c4df15", 0, "file-1/new" },
+ { 0100644, "a39a620dae5bc8b4e771cd4d251b7d080401a21e", 1, "file-2" },
+ { 0100644, "d963979c237d08b6ba39062ee7bf64c7d34a27f8", 2, "file-2" },
+ { 0100644, "5c341ead2ba6f2af98ce5ec3fe84f6b6d2899c0d", 0, "file-2/new" },
+ { 0100644, "9efe7723802d4305142eee177e018fee1572c4f4", 0, "file-3/new" },
+ { 0100644, "bacac9b3493509aa15e1730e1545fc0919d1dae0", 1, "file-4" },
+ { 0100644, "7663fce0130db092936b137cabd693ec234eb060", 3, "file-4" },
+ { 0100644, "e49f917b448d1340b31d76e54ba388268fd4c922", 0, "file-4/new" },
+ { 0100644, "cab2cf23998b40f1af2d9d9a756dc9e285a8df4b", 2, "file-5/new" },
+ { 0100644, "f5504f36e6f4eb797a56fc5bac6c6c7f32969bf2", 3, "file-5/new" },
+ };
+
+ cl_git_pass(git_reference_symbolic_create(&head, repo, GIT_HEAD_FILE, GIT_REFS_HEADS_DIR OURS_DIRECTORY_FILE, 1, NULL));
+ cl_git_pass(git_reference_name_to_id(&head_commit_id, repo, GIT_HEAD_FILE));
+ cl_git_pass(git_commit_lookup(&head_commit, repo, &head_commit_id));
+ cl_git_pass(git_reset(repo, (git_object *)head_commit, GIT_RESET_HARD, NULL));
+
+ cl_git_pass(git_oid__fromstr(&their_oids[0], THEIRS_DIRECTORY_FILE, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&their_heads[0], repo, &their_oids[0]));
+
+ merge_opts.file_favor = 0;
+ cl_git_pass(git_merge(repo, (const git_annotated_commit **)their_heads, 1, &merge_opts, NULL));
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 20));
+
+ git_reference_free(head);
+ git_commit_free(head_commit);
+ git_annotated_commit_free(their_heads[0]);
+}
+
+void test_merge_workdir_simple__unrelated(void)
+{
+ git_oid their_oids[1];
+ git_annotated_commit *their_heads[1];
+ git_merge_options merge_opts = GIT_MERGE_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" },
+ { 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 0, "automergeable.txt" },
+ { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-branch.txt" },
+ { 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt" },
+ { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 0, "conflicting.txt" },
+ { 0100644, "ef58fdd8086c243bdc81f99e379acacfd21d32d6", 0, "new-in-unrelated1.txt" },
+ { 0100644, "948ba6e701c1edab0c2d394fb7c5538334129793", 0, "new-in-unrelated2.txt" },
+ { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt" },
+ { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt" },
+ };
+
+ cl_git_pass(git_oid__fromstr(&their_oids[0], THEIRS_UNRELATED_PARENT, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&their_heads[0], repo, &their_oids[0]));
+
+ merge_opts.file_favor = 0;
+ cl_git_pass(git_merge(repo, (const git_annotated_commit **)their_heads, 1, &merge_opts, NULL));
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 9));
+
+ git_annotated_commit_free(their_heads[0]);
+}
+
+void test_merge_workdir_simple__unrelated_with_conflicts(void)
+{
+ git_oid their_oids[1];
+ git_annotated_commit *their_heads[1];
+ git_merge_options merge_opts = GIT_MERGE_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "233c0919c998ed110a4b6ff36f353aec8b713487", 0, "added-in-master.txt" },
+ { 0100644, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf", 2, "automergeable.txt" },
+ { 0100644, "d07ec190c306ec690bac349e87d01c4358e49bb2", 3, "automergeable.txt" },
+ { 0100644, "ab6c44a2e84492ad4b41bb6bac87353e9d02ac8b", 0, "changed-in-branch.txt" },
+ { 0100644, "11deab00b2d3a6f5a3073988ac050c2d7b6655e2", 0, "changed-in-master.txt" },
+ { 0100644, "4e886e602529caa9ab11d71f86634bd1b6e0de10", 2, "conflicting.txt" },
+ { 0100644, "4b253da36a0ae8bfce63aeabd8c5b58429925594", 3, "conflicting.txt" },
+ { 0100644, "ef58fdd8086c243bdc81f99e379acacfd21d32d6", 0, "new-in-unrelated1.txt" },
+ { 0100644, "948ba6e701c1edab0c2d394fb7c5538334129793", 0, "new-in-unrelated2.txt" },
+ { 0100644, "dfe3f22baa1f6fce5447901c3086bae368de6bdd", 0, "removed-in-branch.txt" },
+ { 0100644, "c8f06f2e3bb2964174677e91f0abead0e43c9e5d", 0, "unchanged.txt" },
+ };
+
+ cl_git_pass(git_oid__fromstr(&their_oids[0], THEIRS_UNRELATED_OID, GIT_OID_SHA1));
+ cl_git_pass(git_annotated_commit_lookup(&their_heads[0], repo, &their_oids[0]));
+
+ merge_opts.file_favor = 0;
+ cl_git_pass(git_merge(repo, (const git_annotated_commit **)their_heads, 1, &merge_opts, NULL));
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 11));
+
+ git_annotated_commit_free(their_heads[0]);
+}
+
+void test_merge_workdir_simple__binary(void)
+{
+ git_oid our_oid, their_oid, our_file_oid;
+ git_commit *our_commit;
+ git_annotated_commit *their_head;
+ const git_index_entry *binary_entry;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "1c51d885170f57a0c4e8c69ff6363d91a5b51f85", 1, "binary" },
+ { 0100644, "23ed141a6ae1e798b2f721afedbe947c119111ba", 2, "binary" },
+ { 0100644, "836b8b82b26cab22eaaed8820877c76d6c8bca19", 3, "binary" },
+ };
+
+ cl_git_pass(git_oid__fromstr(&our_oid, "cc338e4710c9b257106b8d16d82f86458d5beaf1", GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&their_oid, "ad01aebfdf2ac13145efafe3f9fcf798882f1730", GIT_OID_SHA1));
+
+ cl_git_pass(git_commit_lookup(&our_commit, repo, &our_oid));
+ cl_git_pass(git_reset(repo, (git_object *)our_commit, GIT_RESET_HARD, NULL));
+
+ cl_git_pass(git_annotated_commit_lookup(&their_head, repo, &their_oid));
+
+ cl_git_pass(git_merge(repo, (const git_annotated_commit **)&their_head, 1, NULL, NULL));
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 3));
+
+ cl_git_pass(git_index_add_bypath(repo_index, "binary"));
+ cl_assert((binary_entry = git_index_get_bypath(repo_index, "binary", 0)) != NULL);
+
+ cl_git_pass(git_oid__fromstr(&our_file_oid, "23ed141a6ae1e798b2f721afedbe947c119111ba", GIT_OID_SHA1));
+ cl_assert(git_oid_cmp(&binary_entry->id, &our_file_oid) == 0);
+
+ git_annotated_commit_free(their_head);
+ git_commit_free(our_commit);
+}
diff --git a/tests/libgit2/merge/workdir/submodules.c b/tests/libgit2/merge/workdir/submodules.c
new file mode 100644
index 0000000..5117be7
--- /dev/null
+++ b/tests/libgit2/merge/workdir/submodules.c
@@ -0,0 +1,130 @@
+#include "clar_libgit2.h"
+#include "git2/repository.h"
+#include "git2/merge.h"
+#include "merge.h"
+#include "../merge_helpers.h"
+
+static git_repository *repo;
+
+#define TEST_REPO_PATH "merge-resolve"
+
+#define SUBMODULE_MAIN_BRANCH "submodules"
+#define SUBMODULE_OTHER_BRANCH "submodules-branch"
+#define SUBMODULE_OTHER2_BRANCH "submodules-branch2"
+#define SUBMODULE_DELETE_BRANCH "delete-submodule"
+
+#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
+
+/* Fixture setup and teardown */
+void test_merge_workdir_submodules__initialize(void)
+{
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+}
+
+void test_merge_workdir_submodules__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_merge_workdir_submodules__automerge(void)
+{
+ git_reference *our_ref, *their_ref;
+ git_commit *our_commit;
+ git_annotated_commit *their_head;
+ git_index *index;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "caff6b7d44973f53e3e0cf31d0d695188b19aec6", 0, ".gitmodules" },
+ { 0100644, "950a663a6a7b2609eed1ed1ba9f41eb1a3192a9f", 0, "file1.txt" },
+ { 0100644, "343e660b9cb4bee5f407c2e33fcb9df24d9407a4", 0, "file2.txt" },
+ { 0160000, "d3d806a4bef96889117fd7ebac0e3cb5ec152932", 1, "submodule" },
+ { 0160000, "297aa6cd028b3336c7802c7a6f49143da4e1602d", 2, "submodule" },
+ { 0160000, "ae39c77c70cb6bad18bb471912460c4e1ba0f586", 3, "submodule" },
+ };
+
+ cl_git_pass(git_reference_lookup(&our_ref, repo, "refs/heads/" SUBMODULE_MAIN_BRANCH));
+ cl_git_pass(git_commit_lookup(&our_commit, repo, git_reference_target(our_ref)));
+ cl_git_pass(git_reset(repo, (git_object *)our_commit, GIT_RESET_HARD, NULL));
+
+ cl_git_pass(git_reference_lookup(&their_ref, repo, "refs/heads/" SUBMODULE_OTHER_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_head, repo, their_ref));
+
+ cl_git_pass(git_merge(repo, (const git_annotated_commit **)&their_head, 1, NULL, NULL));
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert(merge_test_index(index, merge_index_entries, 6));
+
+ git_index_free(index);
+ git_annotated_commit_free(their_head);
+ git_commit_free(our_commit);
+ git_reference_free(their_ref);
+ git_reference_free(our_ref);
+}
+
+void test_merge_workdir_submodules__take_changed(void)
+{
+ git_reference *our_ref, *their_ref;
+ git_commit *our_commit;
+ git_annotated_commit *their_head;
+ git_index *index;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "caff6b7d44973f53e3e0cf31d0d695188b19aec6", 0, ".gitmodules" },
+ { 0100644, "b438ff23300b2e0f80b84a6f30140dfa91e71423", 0, "file1.txt" },
+ { 0100644, "f27fbafdfa6693f8f7a5128506fe3e338dbfcad2", 0, "file2.txt" },
+ { 0160000, "297aa6cd028b3336c7802c7a6f49143da4e1602d", 0, "submodule" },
+ };
+
+ cl_git_pass(git_reference_lookup(&our_ref, repo, "refs/heads/" SUBMODULE_MAIN_BRANCH));
+ cl_git_pass(git_commit_lookup(&our_commit, repo, git_reference_target(our_ref)));
+ cl_git_pass(git_reset(repo, (git_object *)our_commit, GIT_RESET_HARD, NULL));
+
+ cl_git_pass(git_reference_lookup(&their_ref, repo, "refs/heads/" SUBMODULE_OTHER2_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_head, repo, their_ref));
+
+ cl_git_pass(git_merge(repo, (const git_annotated_commit **)&their_head, 1, NULL, NULL));
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert(merge_test_index(index, merge_index_entries, 4));
+
+ git_index_free(index);
+ git_annotated_commit_free(their_head);
+ git_commit_free(our_commit);
+ git_reference_free(their_ref);
+ git_reference_free(our_ref);
+}
+
+
+void test_merge_workdir_submodules__update_delete_conflict(void)
+{
+ git_reference *our_ref, *their_ref;
+ git_commit *our_commit;
+ git_annotated_commit *their_head;
+ git_index *index;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", 0, ".gitmodules" },
+ { 0100644, "5887a5e516c53bd58efb0f02ec6aa031b6fe9ad7", 0, "file1.txt" },
+ { 0100644, "4218670ab81cc219a9f94befb5c5dad90ec52648", 0, "file2.txt" },
+ { 0160000, "d3d806a4bef96889117fd7ebac0e3cb5ec152932", 1, "submodule"},
+ { 0160000, "297aa6cd028b3336c7802c7a6f49143da4e1602d", 3, "submodule" },
+ };
+
+ cl_git_pass(git_reference_lookup(&our_ref, repo, "refs/heads/" SUBMODULE_DELETE_BRANCH));
+ cl_git_pass(git_commit_lookup(&our_commit, repo, git_reference_target(our_ref)));
+ cl_git_pass(git_reset(repo, (git_object *)our_commit, GIT_RESET_HARD, NULL));
+
+ cl_git_pass(git_reference_lookup(&their_ref, repo, "refs/heads/" SUBMODULE_MAIN_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&their_head, repo, their_ref));
+
+ cl_git_pass(git_merge(repo, (const git_annotated_commit **)&their_head, 1, NULL, NULL));
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert(merge_test_index(index, merge_index_entries, 5));
+
+ git_index_free(index);
+ git_annotated_commit_free(their_head);
+ git_commit_free(our_commit);
+ git_reference_free(their_ref);
+ git_reference_free(our_ref);
+}
diff --git a/tests/libgit2/merge/workdir/trivial.c b/tests/libgit2/merge/workdir/trivial.c
new file mode 100644
index 0000000..364182c
--- /dev/null
+++ b/tests/libgit2/merge/workdir/trivial.c
@@ -0,0 +1,262 @@
+#include "clar_libgit2.h"
+#include "git2/repository.h"
+#include "git2/merge.h"
+#include "git2/sys/index.h"
+#include "merge.h"
+#include "../merge_helpers.h"
+#include "refs.h"
+#include "futils.h"
+
+static git_repository *repo;
+static git_index *repo_index;
+
+#define TEST_REPO_PATH "merge-resolve"
+#define TEST_INDEX_PATH TEST_REPO_PATH "/.git/index"
+
+
+/* Fixture setup and teardown */
+void test_merge_workdir_trivial__initialize(void)
+{
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+ git_repository_index(&repo_index, repo);
+}
+
+void test_merge_workdir_trivial__cleanup(void)
+{
+ git_index_free(repo_index);
+ cl_git_sandbox_cleanup();
+}
+
+
+static int merge_trivial(const char *ours, const char *theirs)
+{
+ git_str branch_buf = GIT_STR_INIT;
+ git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_reference *our_ref, *their_ref;
+ git_annotated_commit *their_heads[1];
+
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ git_str_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, ours);
+ cl_git_pass(git_reference_symbolic_create(&our_ref, repo, "HEAD", branch_buf.ptr, 1, NULL));
+
+ cl_git_pass(git_checkout_head(repo, &checkout_opts));
+
+ git_str_clear(&branch_buf);
+ git_str_printf(&branch_buf, "%s%s", GIT_REFS_HEADS_DIR, theirs);
+ cl_git_pass(git_reference_lookup(&their_ref, repo, branch_buf.ptr));
+ cl_git_pass(git_annotated_commit_from_ref(&their_heads[0], repo, their_ref));
+
+ cl_git_pass(git_merge(repo, (const git_annotated_commit **)their_heads, 1, NULL, NULL));
+
+ git_str_dispose(&branch_buf);
+ git_reference_free(our_ref);
+ git_reference_free(their_ref);
+ git_annotated_commit_free(their_heads[0]);
+
+ return 0;
+}
+
+static size_t merge_trivial_conflict_entrycount(void)
+{
+ const git_index_entry *entry;
+ size_t count = 0;
+ size_t i;
+
+ for (i = 0; i < git_index_entrycount(repo_index); i++) {
+ cl_assert(entry = git_index_get_byindex(repo_index, i));
+
+ if (git_index_entry_is_conflict(entry))
+ count++;
+ }
+
+ return count;
+}
+
+/* 2ALT: ancest:(empty)+, head:*empty*, remote:remote = result:remote */
+void test_merge_workdir_trivial__2alt(void)
+{
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial("trivial-2alt", "trivial-2alt-branch"));
+
+ cl_assert(entry = git_index_get_bypath(repo_index, "new-in-branch.txt", 0));
+ cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+ cl_assert(merge_trivial_conflict_entrycount() == 0);
+}
+
+/* 3ALT: ancest:(empty)+, head:head, remote:*empty* = result:head */
+void test_merge_workdir_trivial__3alt(void)
+{
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial("trivial-3alt", "trivial-3alt-branch"));
+
+ cl_assert(entry = git_index_get_bypath(repo_index, "new-in-3alt.txt", 0));
+ cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+ cl_assert(merge_trivial_conflict_entrycount() == 0);
+}
+
+/* 4: ancest:(empty)^, head:head, remote:remote = result:no merge */
+void test_merge_workdir_trivial__4(void)
+{
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial("trivial-4", "trivial-4-branch"));
+
+ cl_assert((entry = git_index_get_bypath(repo_index, "new-and-different.txt", 0)) == NULL);
+ cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+
+ cl_assert(merge_trivial_conflict_entrycount() == 2);
+ cl_assert(entry = git_index_get_bypath(repo_index, "new-and-different.txt", 2));
+ cl_assert(entry = git_index_get_bypath(repo_index, "new-and-different.txt", 3));
+}
+
+/* 5ALT: ancest:*, head:head, remote:head = result:head */
+void test_merge_workdir_trivial__5alt_1(void)
+{
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial("trivial-5alt-1", "trivial-5alt-1-branch"));
+
+ cl_assert(entry = git_index_get_bypath(repo_index, "new-and-same.txt", 0));
+ cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+ cl_assert(merge_trivial_conflict_entrycount() == 0);
+}
+
+/* 5ALT: ancest:*, head:head, remote:head = result:head */
+void test_merge_workdir_trivial__5alt_2(void)
+{
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial("trivial-5alt-2", "trivial-5alt-2-branch"));
+
+ cl_assert(entry = git_index_get_bypath(repo_index, "modified-to-same.txt", 0));
+ cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+ cl_assert(merge_trivial_conflict_entrycount() == 0);
+}
+
+/* 6: ancest:ancest+, head:(empty), remote:(empty) = result:no merge */
+void test_merge_workdir_trivial__6(void)
+{
+ const git_index_entry *entry;
+ const git_index_reuc_entry *reuc;
+
+ cl_git_pass(merge_trivial("trivial-6", "trivial-6-branch"));
+
+ cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-both.txt", 0)) == NULL);
+ cl_assert(git_index_reuc_entrycount(repo_index) == 1);
+ cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "removed-in-both.txt"));
+
+ cl_assert(merge_trivial_conflict_entrycount() == 0);
+}
+
+/* 8: ancest:ancest^, head:(empty), remote:ancest = result:no merge */
+void test_merge_workdir_trivial__8(void)
+{
+ const git_index_entry *entry;
+ const git_index_reuc_entry *reuc;
+
+ cl_git_pass(merge_trivial("trivial-8", "trivial-8-branch"));
+
+ cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-8.txt", 0)) == NULL);
+
+ cl_assert(git_index_reuc_entrycount(repo_index) == 1);
+ cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "removed-in-8.txt"));
+
+ cl_assert(merge_trivial_conflict_entrycount() == 0);
+}
+
+/* 7: ancest:ancest+, head:(empty), remote:remote = result:no merge */
+void test_merge_workdir_trivial__7(void)
+{
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial("trivial-7", "trivial-7-branch"));
+
+ cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-7.txt", 0)) == NULL);
+ cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+
+ cl_assert(merge_trivial_conflict_entrycount() == 2);
+ cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-7.txt", 1));
+ cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-7.txt", 3));
+}
+
+/* 10: ancest:ancest^, head:ancest, remote:(empty) = result:no merge */
+void test_merge_workdir_trivial__10(void)
+{
+ const git_index_entry *entry;
+ const git_index_reuc_entry *reuc;
+
+ cl_git_pass(merge_trivial("trivial-10", "trivial-10-branch"));
+
+ cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-10-branch.txt", 0)) == NULL);
+
+ cl_assert(git_index_reuc_entrycount(repo_index) == 1);
+ cl_assert(reuc = git_index_reuc_get_bypath(repo_index, "removed-in-10-branch.txt"));
+
+ cl_assert(merge_trivial_conflict_entrycount() == 0);
+}
+
+/* 9: ancest:ancest+, head:head, remote:(empty) = result:no merge */
+void test_merge_workdir_trivial__9(void)
+{
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial("trivial-9", "trivial-9-branch"));
+
+ cl_assert((entry = git_index_get_bypath(repo_index, "removed-in-9-branch.txt", 0)) == NULL);
+ cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+
+ cl_assert(merge_trivial_conflict_entrycount() == 2);
+ cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-9-branch.txt", 1));
+ cl_assert(entry = git_index_get_bypath(repo_index, "removed-in-9-branch.txt", 2));
+}
+
+/* 13: ancest:ancest+, head:head, remote:ancest = result:head */
+void test_merge_workdir_trivial__13(void)
+{
+ const git_index_entry *entry;
+ git_oid expected_oid;
+
+ cl_git_pass(merge_trivial("trivial-13", "trivial-13-branch"));
+
+ cl_assert(entry = git_index_get_bypath(repo_index, "modified-in-13.txt", 0));
+ cl_git_pass(git_oid__fromstr(&expected_oid, "1cff9ec6a47a537380dedfdd17c9e76d74259a2b", GIT_OID_SHA1));
+ cl_assert(git_oid_cmp(&entry->id, &expected_oid) == 0);
+
+ cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+ cl_assert(merge_trivial_conflict_entrycount() == 0);
+}
+
+/* 14: ancest:ancest+, head:ancest, remote:remote = result:remote */
+void test_merge_workdir_trivial__14(void)
+{
+ const git_index_entry *entry;
+ git_oid expected_oid;
+
+ cl_git_pass(merge_trivial("trivial-14", "trivial-14-branch"));
+
+ cl_assert(entry = git_index_get_bypath(repo_index, "modified-in-14-branch.txt", 0));
+ cl_git_pass(git_oid__fromstr(&expected_oid, "26153a3ff3649b6c2bb652d3f06878c6e0a172f9", GIT_OID_SHA1));
+ cl_assert(git_oid_cmp(&entry->id, &expected_oid) == 0);
+
+ cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+ cl_assert(merge_trivial_conflict_entrycount() == 0);
+}
+
+/* 11: ancest:ancest+, head:head, remote:remote = result:no merge */
+void test_merge_workdir_trivial__11(void)
+{
+ const git_index_entry *entry;
+
+ cl_git_pass(merge_trivial("trivial-11", "trivial-11-branch"));
+
+ cl_assert((entry = git_index_get_bypath(repo_index, "modified-in-both.txt", 0)) == NULL);
+ cl_assert(git_index_reuc_entrycount(repo_index) == 0);
+
+ cl_assert(merge_trivial_conflict_entrycount() == 3);
+ cl_assert(entry = git_index_get_bypath(repo_index, "modified-in-both.txt", 1));
+ cl_assert(entry = git_index_get_bypath(repo_index, "modified-in-both.txt", 2));
+ cl_assert(entry = git_index_get_bypath(repo_index, "modified-in-both.txt", 3));
+}
diff --git a/tests/libgit2/message/trailer.c b/tests/libgit2/message/trailer.c
new file mode 100644
index 0000000..919e10a
--- /dev/null
+++ b/tests/libgit2/message/trailer.c
@@ -0,0 +1,164 @@
+#include "clar_libgit2.h"
+
+static void assert_trailers(const char *message, git_message_trailer *trailers)
+{
+ git_message_trailer_array arr;
+ size_t i;
+
+ int rc = git_message_trailers(&arr, message);
+
+ cl_assert_equal_i(0, rc);
+
+ for(i=0; i<arr.count; i++) {
+ cl_assert_equal_s(arr.trailers[i].key, trailers[i].key);
+ cl_assert_equal_s(arr.trailers[i].value, trailers[i].value);
+ }
+
+ cl_assert_equal_i(0, rc);
+
+ git_message_trailer_array_free(&arr);
+}
+
+void test_message_trailer__simple(void)
+{
+ git_message_trailer trailers[] = {
+ {"Signed-off-by", "foo@bar.com"},
+ {"Signed-off-by", "someone@else.com"},
+ {NULL, NULL},
+ };
+
+ assert_trailers(
+ "Message\n"
+ "\n"
+ "Signed-off-by: foo@bar.com\n"
+ "Signed-off-by: someone@else.com\n"
+ , trailers);
+}
+
+void test_message_trailer__no_whitespace(void)
+{
+ git_message_trailer trailers[] = {
+ {"Key", "value"},
+ {NULL, NULL},
+ };
+
+ assert_trailers(
+ "Message\n"
+ "\n"
+ "Key:value\n"
+ , trailers);
+}
+
+void test_message_trailer__extra_whitespace(void)
+{
+ git_message_trailer trailers[] = {
+ {"Key", "value"},
+ {NULL, NULL},
+ };
+
+ assert_trailers(
+ "Message\n"
+ "\n"
+ "Key : value\n"
+ , trailers);
+}
+
+void test_message_trailer__no_newline(void)
+{
+ git_message_trailer trailers[] = {
+ {"Key", "value"},
+ {NULL, NULL},
+ };
+
+ assert_trailers(
+ "Message\n"
+ "\n"
+ "Key: value"
+ , trailers);
+}
+
+void test_message_trailer__not_last_paragraph(void)
+{
+ git_message_trailer trailers[] = {
+ {NULL, NULL},
+ };
+
+ assert_trailers(
+ "Message\n"
+ "\n"
+ "Key: value\n"
+ "\n"
+ "More stuff\n"
+ , trailers);
+}
+
+void test_message_trailer__conflicts(void)
+{
+ git_message_trailer trailers[] = {
+ {"Key", "value"},
+ {NULL, NULL},
+ };
+
+ assert_trailers(
+ "Message\n"
+ "\n"
+ "Key: value\n"
+ "\n"
+ "Conflicts:\n"
+ "\tfoo.c\n"
+ , trailers);
+}
+
+void test_message_trailer__patch(void)
+{
+ git_message_trailer trailers[] = {
+ {"Key", "value"},
+ {NULL, NULL},
+ };
+
+ assert_trailers(
+ "Message\n"
+ "\n"
+ "Key: value\n"
+ "\n"
+ "---\n"
+ "More: stuff\n"
+ , trailers);
+}
+
+void test_message_trailer__continuation(void)
+{
+ git_message_trailer trailers[] = {
+ {"A", "b\n c"},
+ {"D", "e\n f: g h"},
+ {"I", "j"},
+ {NULL, NULL},
+ };
+
+ assert_trailers(
+ "Message\n"
+ "\n"
+ "A: b\n"
+ " c\n"
+ "D: e\n"
+ " f: g h\n"
+ "I: j\n"
+ , trailers);
+}
+
+void test_message_trailer__invalid(void)
+{
+ git_message_trailer trailers[] = {
+ {"Signed-off-by", "some@one.com"},
+ {"Another", "trailer"},
+ {NULL, NULL},
+ };
+
+ assert_trailers(
+ "Message\n"
+ "\n"
+ "Signed-off-by: some@one.com\n"
+ "Not a trailer\n"
+ "Another: trailer\n"
+ , trailers);
+}
diff --git a/tests/libgit2/network/cred.c b/tests/libgit2/network/cred.c
new file mode 100644
index 0000000..5e4db75
--- /dev/null
+++ b/tests/libgit2/network/cred.c
@@ -0,0 +1,46 @@
+#include "clar_libgit2.h"
+
+#include "git2/cred_helpers.h"
+
+void test_network_cred__stock_userpass_validates_args(void)
+{
+ git_credential_userpass_payload payload = {0};
+
+ cl_git_fail(git_credential_userpass(NULL, NULL, NULL, 0, NULL));
+
+ payload.username = "user";
+ cl_git_fail(git_credential_userpass(NULL, NULL, NULL, 0, &payload));
+
+ payload.username = NULL;
+ payload.username = "pass";
+ cl_git_fail(git_credential_userpass(NULL, NULL, NULL, 0, &payload));
+}
+
+void test_network_cred__stock_userpass_validates_that_method_is_allowed(void)
+{
+ git_credential *cred;
+ git_credential_userpass_payload payload = {"user", "pass"};
+
+ cl_git_fail(git_credential_userpass(&cred, NULL, NULL, 0, &payload));
+ cl_git_pass(git_credential_userpass(&cred, NULL, NULL, GIT_CREDENTIAL_USERPASS_PLAINTEXT, &payload));
+ git_credential_free(cred);
+}
+
+void test_network_cred__stock_userpass_properly_handles_username_in_url(void)
+{
+ git_credential *cred;
+ git_credential_userpass_payload payload = {"alice", "password"};
+
+ cl_git_pass(git_credential_userpass(&cred, NULL, NULL, GIT_CREDENTIAL_USERPASS_PLAINTEXT, &payload));
+ cl_assert_equal_s("alice", git_credential_get_username(cred));
+ git_credential_free(cred);
+
+ cl_git_pass(git_credential_userpass(&cred, NULL, "bob", GIT_CREDENTIAL_USERPASS_PLAINTEXT, &payload));
+ cl_assert_equal_s("alice", git_credential_get_username(cred));
+ git_credential_free(cred);
+
+ payload.username = NULL;
+ cl_git_pass(git_credential_userpass(&cred, NULL, "bob", GIT_CREDENTIAL_USERPASS_PLAINTEXT, &payload));
+ cl_assert_equal_s("bob", git_credential_get_username(cred));
+ git_credential_free(cred);
+}
diff --git a/tests/libgit2/network/fetchlocal.c b/tests/libgit2/network/fetchlocal.c
new file mode 100644
index 0000000..dc37c38
--- /dev/null
+++ b/tests/libgit2/network/fetchlocal.c
@@ -0,0 +1,552 @@
+#include "clar_libgit2.h"
+
+#include "path.h"
+#include "remote.h"
+
+static const char* tagger_name = "Vicent Marti";
+static const char* tagger_email = "vicent@github.com";
+static const char* tagger_message = "This is my tag.\n\nThere are many tags, but this one is mine\n";
+
+static int transfer_cb(const git_indexer_progress *stats, void *payload)
+{
+ int *callcount = (int*)payload;
+ GIT_UNUSED(stats);
+ (*callcount)++;
+ return 0;
+}
+
+static void cleanup_local_repo(void *path)
+{
+ cl_fixture_cleanup((char *)path);
+}
+
+void test_network_fetchlocal__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_network_fetchlocal__complete(void)
+{
+ git_repository *repo;
+ git_remote *origin;
+ int callcount = 0;
+ git_strarray refnames = {0};
+
+ const char *url = cl_git_fixture_url("testrepo.git");
+ git_fetch_options options = GIT_FETCH_OPTIONS_INIT;
+
+ options.callbacks.transfer_progress = transfer_cb;
+ options.callbacks.payload = &callcount;
+
+ cl_set_cleanup(&cleanup_local_repo, "foo");
+ cl_git_pass(git_repository_init(&repo, "foo", true));
+
+ cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url));
+ cl_git_pass(git_remote_fetch(origin, NULL, &options, NULL));
+
+ cl_git_pass(git_reference_list(&refnames, repo));
+ cl_assert_equal_i(20, (int)refnames.count);
+ cl_assert(callcount > 0);
+
+ git_strarray_dispose(&refnames);
+ git_remote_free(origin);
+ git_repository_free(repo);
+}
+
+void test_network_fetchlocal__prune(void)
+{
+ git_repository *repo;
+ git_remote *origin;
+ int callcount = 0;
+ git_strarray refnames = {0};
+ git_reference *ref;
+ git_repository *remote_repo = cl_git_sandbox_init("testrepo.git");
+ const char *url = cl_git_path_url(git_repository_path(remote_repo));
+ git_fetch_options options = GIT_FETCH_OPTIONS_INIT;
+
+ options.callbacks.transfer_progress = transfer_cb;
+ options.callbacks.payload = &callcount;
+
+ cl_set_cleanup(&cleanup_local_repo, "foo");
+ cl_git_pass(git_repository_init(&repo, "foo", true));
+
+ cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url));
+ cl_git_pass(git_remote_fetch(origin, NULL, &options, NULL));
+
+ cl_git_pass(git_reference_list(&refnames, repo));
+ cl_assert_equal_i(20, (int)refnames.count);
+ cl_assert(callcount > 0);
+ git_strarray_dispose(&refnames);
+ git_remote_free(origin);
+
+ cl_git_pass(git_reference_lookup(&ref, remote_repo, "refs/heads/br2"));
+ cl_git_pass(git_reference_delete(ref));
+ git_reference_free(ref);
+
+ cl_git_pass(git_remote_lookup(&origin, repo, GIT_REMOTE_ORIGIN));
+ cl_git_pass(git_remote_fetch(origin, NULL, &options, NULL));
+ cl_git_pass(git_remote_prune(origin, &options.callbacks));
+
+ cl_git_pass(git_reference_list(&refnames, repo));
+ cl_assert_equal_i(19, (int)refnames.count);
+ git_strarray_dispose(&refnames);
+ git_remote_free(origin);
+
+ cl_git_pass(git_reference_lookup(&ref, remote_repo, "refs/heads/packed"));
+ cl_git_pass(git_reference_delete(ref));
+ git_reference_free(ref);
+
+ cl_git_pass(git_remote_lookup(&origin, repo, GIT_REMOTE_ORIGIN));
+ cl_git_pass(git_remote_fetch(origin, NULL, &options, NULL));
+ cl_git_pass(git_remote_prune(origin, &options.callbacks));
+
+ cl_git_pass(git_reference_list(&refnames, repo));
+ cl_assert_equal_i(18, (int)refnames.count);
+ git_strarray_dispose(&refnames);
+ git_remote_free(origin);
+
+ git_repository_free(repo);
+}
+
+static int update_tips_fail_on_call(const char *ref, const git_oid *old, const git_oid *new, void *data)
+{
+ GIT_UNUSED(ref);
+ GIT_UNUSED(old);
+ GIT_UNUSED(new);
+ GIT_UNUSED(data);
+
+ cl_fail("update tips called");
+ return 0;
+}
+
+static void assert_ref_exists(git_repository *repo, const char *name)
+{
+ git_reference *ref;
+
+ cl_git_pass(git_reference_lookup(&ref, repo, name));
+ git_reference_free(ref);
+}
+
+void test_network_fetchlocal__prune_overlapping(void)
+{
+ git_repository *repo;
+ git_remote *origin;
+ int callcount = 0;
+ git_strarray refnames = {0};
+ git_reference *ref;
+ git_config *config;
+ git_oid target;
+
+ git_repository *remote_repo = cl_git_sandbox_init("testrepo.git");
+ const char *url = cl_git_path_url(git_repository_path(remote_repo));
+
+ git_fetch_options options = GIT_FETCH_OPTIONS_INIT;
+ options.callbacks.transfer_progress = transfer_cb;
+ options.callbacks.payload = &callcount;
+
+ cl_git_pass(git_reference_lookup(&ref, remote_repo, "refs/heads/master"));
+ git_oid_cpy(&target, git_reference_target(ref));
+ git_reference_free(ref);
+ cl_git_pass(git_reference_create(&ref, remote_repo, "refs/pull/42/head", &target, 1, NULL));
+ git_reference_free(ref);
+
+ cl_set_cleanup(&cleanup_local_repo, "foo");
+ cl_git_pass(git_repository_init(&repo, "foo", true));
+
+ cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url));
+
+ cl_git_pass(git_repository_config(&config, repo));
+ cl_git_pass(git_config_set_bool(config, "remote.origin.prune", true));
+ cl_git_pass(git_config_set_multivar(config, "remote.origin.fetch", "^$", "refs/pull/*/head:refs/remotes/origin/pr/*"));
+
+ git_remote_free(origin);
+ cl_git_pass(git_remote_lookup(&origin, repo, GIT_REMOTE_ORIGIN));
+ cl_git_pass(git_remote_fetch(origin, NULL, &options, NULL));
+
+ assert_ref_exists(repo, "refs/remotes/origin/master");
+ assert_ref_exists(repo, "refs/remotes/origin/pr/42");
+ cl_git_pass(git_reference_list(&refnames, repo));
+ cl_assert_equal_i(21, (int)refnames.count);
+ git_strarray_dispose(&refnames);
+
+ cl_git_pass(git_config_delete_multivar(config, "remote.origin.fetch", "refs"));
+ cl_git_pass(git_config_set_multivar(config, "remote.origin.fetch", "^$", "refs/pull/*/head:refs/remotes/origin/pr/*"));
+ cl_git_pass(git_config_set_multivar(config, "remote.origin.fetch", "^$", "refs/heads/*:refs/remotes/origin/*"));
+
+ git_remote_free(origin);
+ cl_git_pass(git_remote_lookup(&origin, repo, GIT_REMOTE_ORIGIN));
+ options.callbacks.update_tips = update_tips_fail_on_call;
+ cl_git_pass(git_remote_fetch(origin, NULL, &options, NULL));
+
+ assert_ref_exists(repo, "refs/remotes/origin/master");
+ assert_ref_exists(repo, "refs/remotes/origin/pr/42");
+ cl_git_pass(git_reference_list(&refnames, repo));
+ cl_assert_equal_i(21, (int)refnames.count);
+ git_strarray_dispose(&refnames);
+
+ cl_git_pass(git_config_delete_multivar(config, "remote.origin.fetch", "refs"));
+ cl_git_pass(git_config_set_multivar(config, "remote.origin.fetch", "^$", "refs/heads/*:refs/remotes/origin/*"));
+ cl_git_pass(git_config_set_multivar(config, "remote.origin.fetch", "^$", "refs/pull/*/head:refs/remotes/origin/pr/*"));
+
+ git_remote_free(origin);
+ cl_git_pass(git_remote_lookup(&origin, repo, GIT_REMOTE_ORIGIN));
+ options.callbacks.update_tips = update_tips_fail_on_call;
+ cl_git_pass(git_remote_fetch(origin, NULL, &options, NULL));
+
+ git_config_free(config);
+ git_strarray_dispose(&refnames);
+ git_remote_free(origin);
+ git_repository_free(repo);
+}
+
+void test_network_fetchlocal__fetchprune(void)
+{
+ git_repository *repo;
+ git_remote *origin;
+ int callcount = 0;
+ git_strarray refnames = {0};
+ git_reference *ref;
+ git_config *config;
+ git_repository *remote_repo = cl_git_sandbox_init("testrepo.git");
+ const char *url = cl_git_path_url(git_repository_path(remote_repo));
+ git_fetch_options options = GIT_FETCH_OPTIONS_INIT;
+
+ options.callbacks.transfer_progress = transfer_cb;
+ options.callbacks.payload = &callcount;
+
+ cl_set_cleanup(&cleanup_local_repo, "foo");
+ cl_git_pass(git_repository_init(&repo, "foo", true));
+
+ cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url));
+ cl_git_pass(git_remote_fetch(origin, NULL, &options, NULL));
+
+ cl_git_pass(git_reference_list(&refnames, repo));
+ cl_assert_equal_i(20, (int)refnames.count);
+ cl_assert(callcount > 0);
+ git_strarray_dispose(&refnames);
+ git_remote_free(origin);
+
+ cl_git_pass(git_reference_lookup(&ref, remote_repo, "refs/heads/br2"));
+ cl_git_pass(git_reference_delete(ref));
+ git_reference_free(ref);
+
+ cl_git_pass(git_remote_lookup(&origin, repo, GIT_REMOTE_ORIGIN));
+ cl_git_pass(git_remote_fetch(origin, NULL, &options, NULL));
+ cl_git_pass(git_remote_prune(origin, &options.callbacks));
+
+ cl_git_pass(git_reference_list(&refnames, repo));
+ cl_assert_equal_i(19, (int)refnames.count);
+ git_strarray_dispose(&refnames);
+ git_remote_free(origin);
+
+ cl_git_pass(git_reference_lookup(&ref, remote_repo, "refs/heads/packed"));
+ cl_git_pass(git_reference_delete(ref));
+ git_reference_free(ref);
+
+ cl_git_pass(git_repository_config(&config, repo));
+ cl_git_pass(git_config_set_bool(config, "remote.origin.prune", 1));
+ git_config_free(config);
+ cl_git_pass(git_remote_lookup(&origin, repo, GIT_REMOTE_ORIGIN));
+ cl_assert_equal_i(1, git_remote_prune_refs(origin));
+ cl_git_pass(git_remote_fetch(origin, NULL, &options, NULL));
+
+ cl_git_pass(git_reference_list(&refnames, repo));
+ cl_assert_equal_i(18, (int)refnames.count);
+ git_strarray_dispose(&refnames);
+ git_remote_free(origin);
+
+ git_repository_free(repo);
+}
+
+void test_network_fetchlocal__prune_tag(void)
+{
+ git_repository *repo;
+ git_remote *origin;
+ int callcount = 0;
+ git_reference *ref;
+ git_config *config;
+ git_oid tag_id;
+ git_signature *tagger;
+ git_object *obj;
+
+ git_repository *remote_repo = cl_git_sandbox_init("testrepo.git");
+ const char *url = cl_git_path_url(git_repository_path(remote_repo));
+ git_fetch_options options = GIT_FETCH_OPTIONS_INIT;
+
+ options.callbacks.transfer_progress = transfer_cb;
+ options.callbacks.payload = &callcount;
+
+ cl_set_cleanup(&cleanup_local_repo, "foo");
+ cl_git_pass(git_repository_init(&repo, "foo", true));
+
+ cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url));
+ cl_git_pass(git_remote_fetch(origin, NULL, &options, NULL));
+ git_remote_free(origin);
+
+ cl_git_pass(git_revparse_single(&obj, repo, "origin/master"));
+
+ cl_git_pass(git_reference_create(&ref, repo, "refs/remotes/origin/fake-remote", git_object_id(obj), 1, NULL));
+ git_reference_free(ref);
+
+ /* create signature */
+ cl_git_pass(git_signature_new(&tagger, tagger_name, tagger_email, 123456789, 60));
+
+ cl_git_pass(
+ git_tag_create(&tag_id, repo,
+ "some-tag", obj, tagger, tagger_message, 0)
+ );
+ git_signature_free(tagger);
+
+ cl_git_pass(git_repository_config(&config, repo));
+ cl_git_pass(git_config_set_bool(config, "remote.origin.prune", 1));
+ git_config_free(config);
+ cl_git_pass(git_remote_lookup(&origin, repo, GIT_REMOTE_ORIGIN));
+ cl_assert_equal_i(1, git_remote_prune_refs(origin));
+ cl_git_pass(git_remote_fetch(origin, NULL, &options, NULL));
+
+ assert_ref_exists(repo, "refs/tags/some-tag");
+ cl_git_fail_with(GIT_ENOTFOUND, git_reference_lookup(&ref, repo, "refs/remotes/origin/fake-remote"));
+
+ git_object_free(obj);
+ git_remote_free(origin);
+
+ git_repository_free(repo);
+}
+
+void test_network_fetchlocal__partial(void)
+{
+ git_repository *repo = cl_git_sandbox_init("partial-testrepo");
+ git_remote *origin;
+ int callcount = 0;
+ git_strarray refnames = {0};
+ const char *url;
+ git_fetch_options options = GIT_FETCH_OPTIONS_INIT;
+
+ options.callbacks.transfer_progress = transfer_cb;
+ options.callbacks.payload = &callcount;
+
+ cl_git_pass(git_reference_list(&refnames, repo));
+ cl_assert_equal_i(1, (int)refnames.count);
+
+ url = cl_git_fixture_url("testrepo.git");
+ cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url));
+ cl_git_pass(git_remote_fetch(origin, NULL, &options, NULL));
+
+ git_strarray_dispose(&refnames);
+
+ cl_git_pass(git_reference_list(&refnames, repo));
+ cl_assert_equal_i(21, (int)refnames.count); /* 18 remote + 1 local */
+ cl_assert(callcount > 0);
+
+ git_strarray_dispose(&refnames);
+ git_remote_free(origin);
+}
+
+static int remote_mirror_cb(git_remote **out, git_repository *repo,
+ const char *name, const char *url, void *payload)
+{
+ int error;
+ git_remote *remote;
+
+ GIT_UNUSED(payload);
+
+ if ((error = git_remote_create_with_fetchspec(&remote, repo, name, url, "+refs/*:refs/*")) < 0)
+ return error;
+
+ *out = remote;
+ return 0;
+}
+
+void test_network_fetchlocal__clone_into_mirror(void)
+{
+ git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
+ git_repository *repo;
+ git_reference *ref;
+
+ opts.bare = true;
+ opts.remote_cb = remote_mirror_cb;
+ cl_git_pass(git_clone(&repo, cl_git_fixture_url("testrepo.git"), "./foo.git", &opts));
+
+ cl_git_pass(git_reference_lookup(&ref, repo, "HEAD"));
+ cl_assert_equal_i(GIT_REFERENCE_SYMBOLIC, git_reference_type(ref));
+ cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(ref));
+
+ git_reference_free(ref);
+ cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/test/master"));
+
+ git_reference_free(ref);
+ git_repository_free(repo);
+ cl_fixture_cleanup("./foo.git");
+}
+
+void test_network_fetchlocal__all_refs(void)
+{
+ git_repository *repo;
+ git_remote *remote;
+ git_reference *ref;
+ char *allrefs = "+refs/*:refs/*";
+ git_strarray refspecs = {
+ &allrefs,
+ 1,
+ };
+
+ cl_git_pass(git_repository_init(&repo, "./foo.git", true));
+ cl_git_pass(git_remote_create_anonymous(&remote, repo, cl_git_fixture_url("testrepo.git")));
+ cl_git_pass(git_remote_fetch(remote, &refspecs, NULL, NULL));
+
+ cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/test/master"));
+ git_reference_free(ref);
+
+ cl_git_pass(git_reference_lookup(&ref, repo, "refs/tags/test"));
+ git_reference_free(ref);
+
+ git_remote_free(remote);
+ git_repository_free(repo);
+ cl_fixture_cleanup("./foo.git");
+}
+
+void test_network_fetchlocal__multi_remotes(void)
+{
+ git_repository *repo = cl_git_sandbox_init("testrepo.git");
+ git_remote *test, *test2;
+ git_strarray refnames = {0};
+ git_fetch_options options = GIT_FETCH_OPTIONS_INIT;
+
+ options.callbacks.transfer_progress = transfer_cb;
+ cl_git_pass(git_remote_set_url(repo, "test", cl_git_fixture_url("testrepo.git")));
+ cl_git_pass(git_remote_lookup(&test, repo, "test"));
+ cl_git_pass(git_remote_fetch(test, NULL, &options, NULL));
+
+ cl_git_pass(git_reference_list(&refnames, repo));
+ cl_assert_equal_i(35, (int)refnames.count);
+ git_strarray_dispose(&refnames);
+
+ cl_git_pass(git_remote_set_url(repo, "test_with_pushurl", cl_git_fixture_url("testrepo.git")));
+ cl_git_pass(git_remote_lookup(&test2, repo, "test_with_pushurl"));
+ cl_git_pass(git_remote_fetch(test2, NULL, &options, NULL));
+
+ cl_git_pass(git_reference_list(&refnames, repo));
+ cl_assert_equal_i(48, (int)refnames.count);
+
+ git_strarray_dispose(&refnames);
+ git_remote_free(test);
+ git_remote_free(test2);
+}
+
+static int sideband_cb(const char *str, int len, void *payload)
+{
+ int *count = (int *) payload;
+
+ GIT_UNUSED(str);
+ GIT_UNUSED(len);
+
+ (*count)++;
+ return 0;
+}
+
+void test_network_fetchlocal__call_progress(void)
+{
+ git_repository *repo;
+ git_remote *remote;
+ git_fetch_options options = GIT_FETCH_OPTIONS_INIT;
+ int callcount = 0;
+
+ cl_git_pass(git_repository_init(&repo, "foo.git", true));
+ cl_set_cleanup(cleanup_local_repo, "foo.git");
+
+ cl_git_pass(git_remote_create_with_fetchspec(&remote, repo, "origin", cl_git_fixture_url("testrepo.git"), "+refs/heads/*:refs/heads/*"));
+
+ options.callbacks.sideband_progress = sideband_cb;
+ options.callbacks.payload = &callcount;
+
+ cl_git_pass(git_remote_fetch(remote, NULL, &options, NULL));
+ cl_assert(callcount != 0);
+
+ git_remote_free(remote);
+ git_repository_free(repo);
+}
+
+void test_network_fetchlocal__prune_load_remote_prune_config(void)
+{
+ git_repository *repo;
+ git_remote *origin;
+ git_config *config;
+ git_repository *remote_repo = cl_git_sandbox_init("testrepo.git");
+ const char *url = cl_git_path_url(git_repository_path(remote_repo));
+
+ cl_set_cleanup(&cleanup_local_repo, "foo");
+ cl_git_pass(git_repository_init(&repo, "foo", true));
+
+ cl_git_pass(git_repository_config(&config, repo));
+ cl_git_pass(git_config_set_bool(config, "remote.origin.prune", 1));
+
+ cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url));
+ cl_assert_equal_i(1, git_remote_prune_refs(origin));
+
+ git_config_free(config);
+ git_remote_free(origin);
+ git_repository_free(repo);
+}
+
+void test_network_fetchlocal__prune_load_fetch_prune_config(void)
+{
+ git_repository *repo;
+ git_remote *origin;
+ git_config *config;
+ git_repository *remote_repo = cl_git_sandbox_init("testrepo.git");
+ const char *url = cl_git_path_url(git_repository_path(remote_repo));
+
+ cl_set_cleanup(&cleanup_local_repo, "foo");
+ cl_git_pass(git_repository_init(&repo, "foo", true));
+
+ cl_git_pass(git_repository_config(&config, repo));
+ cl_git_pass(git_config_set_bool(config, "fetch.prune", 1));
+
+ cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url));
+ cl_assert_equal_i(1, git_remote_prune_refs(origin));
+
+ git_config_free(config);
+ git_remote_free(origin);
+ git_repository_free(repo);
+}
+
+static int update_tips_error(const char *ref, const git_oid *old, const git_oid *new, void *data)
+{
+ int *callcount = (int *) data;
+
+ GIT_UNUSED(ref);
+ GIT_UNUSED(old);
+ GIT_UNUSED(new);
+
+ (*callcount)++;
+
+ return -1;
+}
+
+void test_network_fetchlocal__update_tips_error_is_propagated(void)
+{
+ git_repository *repo;
+ git_reference_iterator *iterator;
+ git_reference *ref;
+ git_remote *remote;
+ git_fetch_options options = GIT_FETCH_OPTIONS_INIT;
+ int callcount = 0;
+
+ cl_git_pass(git_repository_init(&repo, "foo.git", true));
+ cl_set_cleanup(cleanup_local_repo, "foo.git");
+
+ cl_git_pass(git_remote_create_with_fetchspec(&remote, repo, "origin", cl_git_fixture_url("testrepo.git"), "+refs/heads/*:refs/remotes/update-tips/*"));
+
+ options.callbacks.update_tips = update_tips_error;
+ options.callbacks.payload = &callcount;
+
+ cl_git_fail(git_remote_fetch(remote, NULL, &options, NULL));
+ cl_assert_equal_i(1, callcount);
+
+ cl_git_pass(git_reference_iterator_glob_new(&iterator, repo, "refs/remotes/update-tips/**/"));
+ cl_assert_equal_i(GIT_ITEROVER, git_reference_next(&ref, iterator));
+
+ git_reference_iterator_free(iterator);
+ git_remote_free(remote);
+ git_repository_free(repo);
+}
diff --git a/tests/libgit2/network/refspecs.c b/tests/libgit2/network/refspecs.c
new file mode 100644
index 0000000..d9e0d9e
--- /dev/null
+++ b/tests/libgit2/network/refspecs.c
@@ -0,0 +1,191 @@
+#include "clar_libgit2.h"
+#include "refspec.h"
+#include "remote.h"
+
+static void assert_refspec(unsigned int direction, const char *input, bool is_expected_to_be_valid)
+{
+ git_refspec refspec;
+ int error;
+
+ error = git_refspec__parse(&refspec, input, direction == GIT_DIRECTION_FETCH);
+ git_refspec__dispose(&refspec);
+
+ if (is_expected_to_be_valid)
+ cl_assert_equal_i(0, error);
+ else
+ cl_assert_equal_i(GIT_EINVALIDSPEC, error);
+}
+
+void test_network_refspecs__parsing(void)
+{
+ /* Ported from https://github.com/git/git/blob/abd2bde78bd994166900290434a2048e660dabed/t/t5511-refspec.sh */
+
+ assert_refspec(GIT_DIRECTION_PUSH, "", false);
+ assert_refspec(GIT_DIRECTION_PUSH, ":", true);
+ assert_refspec(GIT_DIRECTION_PUSH, "::", false);
+ assert_refspec(GIT_DIRECTION_PUSH, "+:", true);
+
+ assert_refspec(GIT_DIRECTION_FETCH, "", true);
+ assert_refspec(GIT_DIRECTION_PUSH, ":", true);
+ assert_refspec(GIT_DIRECTION_FETCH, "::", false);
+
+ assert_refspec(GIT_DIRECTION_PUSH, "refs/heads/*:refs/remotes/frotz/*", true);
+ assert_refspec(GIT_DIRECTION_PUSH, "refs/heads/*:refs/remotes/frotz", false);
+ assert_refspec(GIT_DIRECTION_PUSH, "refs/heads:refs/remotes/frotz/*", false);
+ assert_refspec(GIT_DIRECTION_PUSH, "refs/heads/master:refs/remotes/frotz/xyzzy", true);
+
+ /*
+ * These have invalid LHS, but we do not have a formal "valid sha-1
+ * expression syntax checker" so they are not checked with the current
+ * code. They will be caught downstream anyway, but we may want to
+ * have tighter check later...
+ */
+ /*assert_refspec(GIT_DIRECTION_PUSH, "refs/heads/master::refs/remotes/frotz/xyzzy", false); */
+ /*assert_refspec(GIT_DIRECTION_PUSH, "refs/heads/maste :refs/remotes/frotz/xyzzy", false); */
+
+ assert_refspec(GIT_DIRECTION_FETCH, "refs/heads/*:refs/remotes/frotz/*", true);
+ assert_refspec(GIT_DIRECTION_FETCH, "refs/heads/*:refs/remotes/frotz", false);
+ assert_refspec(GIT_DIRECTION_FETCH, "refs/heads:refs/remotes/frotz/*", false);
+ assert_refspec(GIT_DIRECTION_FETCH, "refs/heads/master:refs/remotes/frotz/xyzzy", true);
+ assert_refspec(GIT_DIRECTION_FETCH, "refs/heads/master::refs/remotes/frotz/xyzzy", false);
+ assert_refspec(GIT_DIRECTION_FETCH, "refs/heads/maste :refs/remotes/frotz/xyzzy", false);
+
+ assert_refspec(GIT_DIRECTION_PUSH, "master~1:refs/remotes/frotz/backup", true);
+ assert_refspec(GIT_DIRECTION_FETCH, "master~1:refs/remotes/frotz/backup", false);
+ assert_refspec(GIT_DIRECTION_PUSH, "HEAD~4:refs/remotes/frotz/new", true);
+ assert_refspec(GIT_DIRECTION_FETCH, "HEAD~4:refs/remotes/frotz/new", false);
+
+ assert_refspec(GIT_DIRECTION_PUSH, "HEAD", true);
+ assert_refspec(GIT_DIRECTION_FETCH, "HEAD", true);
+ assert_refspec(GIT_DIRECTION_PUSH, "refs/heads/ nitfol", false);
+ assert_refspec(GIT_DIRECTION_FETCH, "refs/heads/ nitfol", false);
+
+ assert_refspec(GIT_DIRECTION_PUSH, "HEAD:", false);
+ assert_refspec(GIT_DIRECTION_FETCH, "HEAD:", true);
+ assert_refspec(GIT_DIRECTION_PUSH, "refs/heads/ nitfol:", false);
+ assert_refspec(GIT_DIRECTION_FETCH, "refs/heads/ nitfol:", false);
+
+ assert_refspec(GIT_DIRECTION_PUSH, ":refs/remotes/frotz/deleteme", true);
+ assert_refspec(GIT_DIRECTION_FETCH, ":refs/remotes/frotz/HEAD-to-me", true);
+ assert_refspec(GIT_DIRECTION_PUSH, ":refs/remotes/frotz/delete me", false);
+ assert_refspec(GIT_DIRECTION_FETCH, ":refs/remotes/frotz/HEAD to me", false);
+
+ assert_refspec(GIT_DIRECTION_FETCH, "refs/heads/*/for-linus:refs/remotes/mine/*-blah", true);
+ assert_refspec(GIT_DIRECTION_PUSH, "refs/heads/*/for-linus:refs/remotes/mine/*-blah", true);
+
+ assert_refspec(GIT_DIRECTION_FETCH, "refs/heads*/for-linus:refs/remotes/mine/*", true);
+ assert_refspec(GIT_DIRECTION_PUSH, "refs/heads*/for-linus:refs/remotes/mine/*", true);
+
+ assert_refspec(GIT_DIRECTION_FETCH, "refs/heads/*/*/for-linus:refs/remotes/mine/*", false);
+ assert_refspec(GIT_DIRECTION_PUSH, "refs/heads/*/*/for-linus:refs/remotes/mine/*", false);
+
+ assert_refspec(GIT_DIRECTION_FETCH, "refs/heads/*g*/for-linus:refs/remotes/mine/*", false);
+ assert_refspec(GIT_DIRECTION_PUSH, "refs/heads/*g*/for-linus:refs/remotes/mine/*", false);
+
+ assert_refspec(GIT_DIRECTION_FETCH, "refs/heads/*/for-linus:refs/remotes/mine/*", true);
+ assert_refspec(GIT_DIRECTION_PUSH, "refs/heads/*/for-linus:refs/remotes/mine/*", true);
+
+ assert_refspec(GIT_DIRECTION_FETCH, "master", true);
+ assert_refspec(GIT_DIRECTION_PUSH, "master", true);
+
+ assert_refspec(GIT_DIRECTION_FETCH, "refs/pull/*/head:refs/remotes/origin/pr/*", true);
+}
+
+static void assert_valid_transform(const char *refspec, const char *name, const char *result)
+{
+ git_refspec spec;
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_pass(git_refspec__parse(&spec, refspec, true));
+ cl_git_pass(git_refspec_transform(&buf, &spec, name));
+ cl_assert_equal_s(result, buf.ptr);
+
+ git_buf_dispose(&buf);
+ git_refspec__dispose(&spec);
+}
+
+void test_network_refspecs__transform_mid_star(void)
+{
+ assert_valid_transform("refs/pull/*/head:refs/remotes/origin/pr/*", "refs/pull/23/head", "refs/remotes/origin/pr/23");
+ assert_valid_transform("refs/heads/*:refs/remotes/origin/*", "refs/heads/master", "refs/remotes/origin/master");
+ assert_valid_transform("refs/heads/*:refs/remotes/origin/*", "refs/heads/user/feature", "refs/remotes/origin/user/feature");
+ assert_valid_transform("refs/heads/*:refs/heads/*", "refs/heads/master", "refs/heads/master");
+ assert_valid_transform("refs/heads/*:refs/heads/*", "refs/heads/user/feature", "refs/heads/user/feature");
+ assert_valid_transform("refs/*:refs/*", "refs/heads/master", "refs/heads/master");
+}
+
+void test_network_refspecs__transform_loosened_star(void)
+{
+ assert_valid_transform("refs/heads/branch-*:refs/remotes/origin/branch-*", "refs/heads/branch-a", "refs/remotes/origin/branch-a");
+ assert_valid_transform("refs/heads/branch-*/head:refs/remotes/origin/branch-*/head", "refs/heads/branch-a/head", "refs/remotes/origin/branch-a/head");
+}
+
+void test_network_refspecs__transform_nested_star(void)
+{
+ assert_valid_transform("refs/heads/x*x/for-linus:refs/remotes/mine/*", "refs/heads/xbranchx/for-linus", "refs/remotes/mine/branch");
+}
+
+void test_network_refspecs__no_dst(void)
+{
+ assert_valid_transform("refs/heads/master:", "refs/heads/master", "");
+}
+
+static void assert_invalid_transform(const char *refspec, const char *name)
+{
+ git_refspec spec;
+ git_buf buf = GIT_BUF_INIT;
+
+ git_refspec__parse(&spec, refspec, true);
+ cl_git_fail(git_refspec_transform(&buf, &spec, name));
+
+ git_buf_dispose(&buf);
+ git_refspec__dispose(&spec);
+}
+
+void test_network_refspecs__invalid(void)
+{
+ assert_invalid_transform("refs/heads/*:refs/remotes/origin/*", "master");
+ assert_invalid_transform("refs/heads/*:refs/remotes/origin/*", "refs/headz/master");
+}
+
+static void assert_invalid_rtransform(const char *refspec, const char *name)
+{
+ git_refspec spec;
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_pass(git_refspec__parse(&spec, refspec, true));
+ cl_git_fail(git_refspec_rtransform(&buf, &spec, name));
+
+ git_buf_dispose(&buf);
+ git_refspec__dispose(&spec);
+}
+
+void test_network_refspecs__invalid_reverse(void)
+{
+ assert_invalid_rtransform("refs/heads/*:refs/remotes/origin/*", "master");
+ assert_invalid_rtransform("refs/heads/*:refs/remotes/origin/*", "refs/remotes/o/master");
+}
+
+void test_network_refspecs__matching(void)
+{
+ git_refspec spec;
+
+ cl_git_pass(git_refspec__parse(&spec, ":", false));
+ cl_assert_equal_s(":", spec.string);
+ cl_assert_equal_s("", spec.src);
+ cl_assert_equal_s("", spec.dst);
+
+ git_refspec__dispose(&spec);
+}
+
+void test_network_refspecs__parse_free(void)
+{
+ git_refspec *spec = NULL;
+
+ cl_git_fail(git_refspec_parse(&spec, "", 0));
+ cl_git_fail(git_refspec_parse(&spec, ":::", 0));
+ cl_git_pass(git_refspec_parse(&spec, "HEAD:", 1));
+
+ cl_assert(spec != NULL);
+ git_refspec_free(spec);
+}
diff --git a/tests/libgit2/network/remote/defaultbranch.c b/tests/libgit2/network/remote/defaultbranch.c
new file mode 100644
index 0000000..a7c0d81
--- /dev/null
+++ b/tests/libgit2/network/remote/defaultbranch.c
@@ -0,0 +1,107 @@
+#include "clar_libgit2.h"
+#include "refspec.h"
+#include "remote.h"
+
+static git_remote *g_remote;
+static git_repository *g_repo_a, *g_repo_b;
+
+void test_network_remote_defaultbranch__initialize(void)
+{
+ g_repo_a = cl_git_sandbox_init("testrepo.git");
+ cl_git_pass(git_repository_init(&g_repo_b, "repo-b.git", true));
+ cl_git_pass(git_remote_create(&g_remote, g_repo_b, "origin", git_repository_path(g_repo_a)));
+}
+
+void test_network_remote_defaultbranch__cleanup(void)
+{
+ git_remote_free(g_remote);
+ git_repository_free(g_repo_b);
+
+ cl_git_sandbox_cleanup();
+ cl_fixture_cleanup("repo-b.git");
+}
+
+static void assert_default_branch(const char *should)
+{
+ git_buf name = GIT_BUF_INIT;
+
+ cl_git_pass(git_remote_connect(g_remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL));
+ cl_git_pass(git_remote_default_branch(&name, g_remote));
+ cl_assert_equal_s(should, name.ptr);
+ git_buf_dispose(&name);
+}
+
+void test_network_remote_defaultbranch__master(void)
+{
+ assert_default_branch("refs/heads/master");
+}
+
+void test_network_remote_defaultbranch__master_does_not_win(void)
+{
+ cl_git_pass(git_repository_set_head(g_repo_a, "refs/heads/not-good"));
+ assert_default_branch("refs/heads/not-good");
+}
+
+void test_network_remote_defaultbranch__master_on_detached(void)
+{
+ cl_git_pass(git_repository_detach_head(g_repo_a));
+ assert_default_branch("refs/heads/master");
+}
+
+void test_network_remote_defaultbranch__no_default_branch(void)
+{
+ git_remote *remote_b;
+ const git_remote_head **heads;
+ size_t len;
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_pass(git_remote_create(&remote_b, g_repo_b, "self", git_repository_path(g_repo_b)));
+ cl_git_pass(git_remote_connect(remote_b, GIT_DIRECTION_FETCH, NULL, NULL, NULL));
+ cl_git_pass(git_remote_ls(&heads, &len, remote_b));
+ cl_assert_equal_i(0, len);
+
+ cl_git_fail_with(GIT_ENOTFOUND, git_remote_default_branch(&buf, remote_b));
+
+ git_remote_free(remote_b);
+}
+
+void test_network_remote_defaultbranch__detached_sharing_nonbranch_id(void)
+{
+ git_oid id, id_cloned;
+ git_reference *ref;
+ git_buf buf = GIT_BUF_INIT;
+ git_repository *cloned_repo;
+
+ cl_git_pass(git_reference_name_to_id(&id, g_repo_a, "HEAD"));
+ cl_git_pass(git_repository_detach_head(g_repo_a));
+ cl_git_pass(git_reference_remove(g_repo_a, "refs/heads/master"));
+ cl_git_pass(git_reference_remove(g_repo_a, "refs/heads/not-good"));
+ cl_git_pass(git_reference_create(&ref, g_repo_a, "refs/foo/bar", &id, 1, NULL));
+ git_reference_free(ref);
+
+ cl_git_pass(git_remote_connect(g_remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL));
+ cl_git_fail_with(GIT_ENOTFOUND, git_remote_default_branch(&buf, g_remote));
+
+ cl_git_pass(git_clone(&cloned_repo, git_repository_path(g_repo_a), "./local-detached", NULL));
+
+ cl_assert(git_repository_head_detached(cloned_repo));
+ cl_git_pass(git_reference_name_to_id(&id_cloned, g_repo_a, "HEAD"));
+ cl_assert(git_oid_equal(&id, &id_cloned));
+
+ git_repository_free(cloned_repo);
+}
+
+void test_network_remote_defaultbranch__unborn_HEAD_with_branches(void)
+{
+ git_reference *ref;
+ git_repository *cloned_repo;
+
+ cl_git_pass(git_reference_symbolic_create(&ref, g_repo_a, "HEAD", "refs/heads/i-dont-exist", 1, NULL));
+ git_reference_free(ref);
+
+ cl_git_pass(git_clone(&cloned_repo, git_repository_path(g_repo_a), "./semi-empty", NULL));
+
+ cl_assert(git_repository_head_unborn(cloned_repo));
+
+ git_repository_free(cloned_repo);
+}
diff --git a/tests/libgit2/network/remote/delete.c b/tests/libgit2/network/remote/delete.c
new file mode 100644
index 0000000..f23a638
--- /dev/null
+++ b/tests/libgit2/network/remote/delete.c
@@ -0,0 +1,46 @@
+#include "clar_libgit2.h"
+#include "config/config_helpers.h"
+
+#include "repository.h"
+
+static git_repository *_repo;
+
+void test_network_remote_delete__initialize(void)
+{
+ _repo = cl_git_sandbox_init("testrepo.git");
+}
+
+void test_network_remote_delete__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_network_remote_delete__remove_remote_tracking_branches(void)
+{
+ git_reference *ref;
+
+ cl_git_pass(git_remote_delete(_repo, "test"));
+ cl_git_fail_with(GIT_ENOTFOUND, git_reference_lookup(&ref, _repo, "refs/remotes/test/master"));
+}
+
+void test_network_remote_delete__remove_remote_configuration_settings(void)
+{
+ cl_assert(count_config_entries_match(_repo, "remote\\.test\\.+") > 0);
+
+ cl_git_pass(git_remote_delete(_repo, "test"));
+
+ cl_assert_equal_i(0, count_config_entries_match(_repo, "remote\\.test\\.+"));
+}
+
+void test_network_remote_delete__remove_branch_upstream_configuration_settings(void)
+{
+ assert_config_entry_existence(_repo, "branch.mergeless.remote", true);
+ assert_config_entry_existence(_repo, "branch.master.remote", true);
+
+ cl_git_pass(git_remote_delete(_repo, "test"));
+
+ assert_config_entry_existence(_repo, "branch.mergeless.remote", false);
+ assert_config_entry_existence(_repo, "branch.mergeless.merge", false);
+ assert_config_entry_existence(_repo, "branch.master.remote", false);
+ assert_config_entry_existence(_repo, "branch.master.merge", false);
+}
diff --git a/tests/libgit2/network/remote/isvalidname.c b/tests/libgit2/network/remote/isvalidname.c
new file mode 100644
index 0000000..a3080f6
--- /dev/null
+++ b/tests/libgit2/network/remote/isvalidname.c
@@ -0,0 +1,24 @@
+#include "clar_libgit2.h"
+
+static int is_valid_name(const char *name)
+{
+ int valid = 0;
+ cl_git_pass(git_remote_name_is_valid(&valid, name));
+ return valid;
+}
+
+void test_network_remote_isvalidname__can_detect_invalid_formats(void)
+{
+ cl_assert_equal_i(false, is_valid_name("/"));
+ cl_assert_equal_i(false, is_valid_name("//"));
+ cl_assert_equal_i(false, is_valid_name(".lock"));
+ cl_assert_equal_i(false, is_valid_name("a.lock"));
+ cl_assert_equal_i(false, is_valid_name("/no/leading/slash"));
+ cl_assert_equal_i(false, is_valid_name("no/trailing/slash/"));
+}
+
+void test_network_remote_isvalidname__wont_hopefully_choke_on_valid_formats(void)
+{
+ cl_assert_equal_i(true, is_valid_name("webmatrix"));
+ cl_assert_equal_i(true, is_valid_name("yishaigalatzer/rules"));
+}
diff --git a/tests/libgit2/network/remote/local.c b/tests/libgit2/network/remote/local.c
new file mode 100644
index 0000000..2007f37
--- /dev/null
+++ b/tests/libgit2/network/remote/local.c
@@ -0,0 +1,483 @@
+#include "clar_libgit2.h"
+#include "path.h"
+#include "posix.h"
+#include "git2/sys/repository.h"
+
+static git_repository *repo;
+static git_str file_path_buf = GIT_STR_INIT;
+static git_remote *remote;
+
+static char *push_refspec_strings[] = {
+ "refs/heads/master",
+};
+static git_strarray push_array = {
+ push_refspec_strings,
+ 1,
+};
+
+void test_network_remote_local__initialize(void)
+{
+ cl_git_pass(git_repository_init(&repo, "remotelocal/", 0));
+ cl_git_pass(git_repository_set_ident(repo, "Foo Bar", "foo@example.com"));
+ cl_assert(repo != NULL);
+}
+
+void test_network_remote_local__cleanup(void)
+{
+ git_str_dispose(&file_path_buf);
+
+ git_remote_free(remote);
+ remote = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+
+ cl_fixture_cleanup("remotelocal");
+}
+
+static void connect_to_local_repository(const char *local_repository)
+{
+ git_str_sets(&file_path_buf, cl_git_path_url(local_repository));
+
+ cl_git_pass(git_remote_create_anonymous(&remote, repo, git_str_cstr(&file_path_buf)));
+ cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL));
+}
+
+void test_network_remote_local__connected(void)
+{
+ connect_to_local_repository(cl_fixture("testrepo.git"));
+ cl_assert(git_remote_connected(remote));
+
+ git_remote_disconnect(remote);
+ cl_assert(!git_remote_connected(remote));
+}
+
+void test_network_remote_local__retrieve_advertised_references(void)
+{
+ const git_remote_head **refs;
+ size_t refs_len;
+
+ connect_to_local_repository(cl_fixture("testrepo.git"));
+
+ cl_git_pass(git_remote_ls(&refs, &refs_len, remote));
+
+ cl_assert_equal_i(refs_len, 30);
+}
+
+void test_network_remote_local__retrieve_advertised_before_connect(void)
+{
+ const git_remote_head **refs;
+ size_t refs_len = 0;
+
+ git_str_sets(&file_path_buf, cl_git_path_url(cl_fixture("testrepo.git")));
+
+ cl_git_pass(git_remote_create_anonymous(&remote, repo, git_str_cstr(&file_path_buf)));
+ cl_git_fail(git_remote_ls(&refs, &refs_len, remote));
+}
+
+void test_network_remote_local__retrieve_advertised_references_after_disconnect(void)
+{
+ const git_remote_head **refs;
+ size_t refs_len;
+
+ connect_to_local_repository(cl_fixture("testrepo.git"));
+ git_remote_disconnect(remote);
+
+ cl_git_pass(git_remote_ls(&refs, &refs_len, remote));
+
+ cl_assert_equal_i(refs_len, 30);
+}
+
+void test_network_remote_local__retrieve_advertised_references_from_spaced_repository(void)
+{
+ const git_remote_head **refs;
+ size_t refs_len;
+
+ cl_fixture_sandbox("testrepo.git");
+ cl_git_pass(p_rename("testrepo.git", "spaced testrepo.git"));
+
+ connect_to_local_repository("spaced testrepo.git");
+
+ cl_git_pass(git_remote_ls(&refs, &refs_len, remote));
+
+ cl_assert_equal_i(refs_len, 30);
+
+ git_remote_free(remote); /* Disconnect from the "spaced repo" before the cleanup */
+ remote = NULL;
+
+ cl_fixture_cleanup("spaced testrepo.git");
+}
+
+void test_network_remote_local__nested_tags_are_completely_peeled(void)
+{
+ const git_remote_head **refs;
+ size_t refs_len, i;
+
+ connect_to_local_repository(cl_fixture("testrepo.git"));
+
+ cl_git_pass(git_remote_ls(&refs, &refs_len, remote));
+
+ for (i = 0; i < refs_len; i++) {
+ if (!strcmp(refs[i]->name, "refs/tags/test^{}"))
+ cl_git_pass(git_oid_streq(&refs[i]->oid, "e90810b8df3e80c413d903f631643c716887138d"));
+ }
+}
+
+void test_network_remote_local__shorthand_fetch_refspec0(void)
+{
+ char *refspec_strings[] = {
+ "master:remotes/sloppy/master",
+ "master:boh/sloppy/master",
+ };
+ git_strarray array = {
+ refspec_strings,
+ 2,
+ };
+
+ git_reference *ref;
+
+ connect_to_local_repository(cl_fixture("testrepo.git"));
+
+ cl_git_pass(git_remote_fetch(remote, &array, NULL, NULL));
+
+ cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/sloppy/master"));
+ git_reference_free(ref);
+
+ cl_git_pass(git_reference_lookup(&ref, repo, "refs/heads/boh/sloppy/master"));
+ git_reference_free(ref);
+}
+
+void test_network_remote_local__shorthand_fetch_refspec1(void)
+{
+ char *refspec_strings[] = {
+ "master",
+ "hard_tag",
+ };
+ git_strarray array = {
+ refspec_strings,
+ 2,
+ };
+
+ git_reference *ref;
+
+ connect_to_local_repository(cl_fixture("testrepo.git"));
+
+ cl_git_pass(git_remote_fetch(remote, &array, NULL, NULL));
+
+ cl_git_fail(git_reference_lookup(&ref, repo, "refs/remotes/origin/master"));
+ cl_git_fail(git_reference_lookup(&ref, repo, "refs/tags/hard_tag"));
+}
+
+void test_network_remote_local__tagopt(void)
+{
+ git_reference *ref;
+ git_fetch_options fetch_opts = GIT_FETCH_OPTIONS_INIT;
+
+ cl_git_pass(git_remote_create(&remote, repo, "tagopt", cl_git_path_url(cl_fixture("testrepo.git"))));
+ fetch_opts.download_tags = GIT_REMOTE_DOWNLOAD_TAGS_ALL;
+ cl_git_pass(git_remote_fetch(remote, NULL, &fetch_opts, NULL));
+
+ cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/tagopt/master"));
+ git_reference_free(ref);
+ cl_git_pass(git_reference_lookup(&ref, repo, "refs/tags/hard_tag"));
+ git_reference_free(ref);
+
+ fetch_opts.download_tags = GIT_REMOTE_DOWNLOAD_TAGS_AUTO;
+ cl_git_pass(git_remote_fetch(remote, NULL, &fetch_opts, NULL));
+ cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/tagopt/master"));
+ git_reference_free(ref);
+}
+
+void test_network_remote_local__push_to_bare_remote(void)
+{
+ char *refspec_strings[] = {
+ "master:master",
+ };
+ git_strarray array = {
+ refspec_strings,
+ 1,
+ };
+
+ /* Should be able to push to a bare remote */
+ git_remote *localremote;
+
+ /* Get some commits */
+ connect_to_local_repository(cl_fixture("testrepo.git"));
+ cl_git_pass(git_remote_fetch(remote, &array, NULL, NULL));
+
+ /* Set up an empty bare repo to push into */
+ {
+ git_repository *localbarerepo;
+ cl_git_pass(git_repository_init(&localbarerepo, "./localbare.git", 1));
+ git_repository_free(localbarerepo);
+ }
+
+ /* Connect to the bare repo */
+ cl_git_pass(git_remote_create_anonymous(&localremote, repo, "./localbare.git"));
+ cl_git_pass(git_remote_connect(localremote, GIT_DIRECTION_PUSH, NULL, NULL, NULL));
+
+ /* Try to push */
+ cl_git_pass(git_remote_upload(localremote, &push_array, NULL));
+
+ /* Clean up */
+ git_remote_free(localremote);
+ cl_fixture_cleanup("localbare.git");
+}
+
+void test_network_remote_local__push_to_bare_remote_with_file_url(void)
+{
+ char *refspec_strings[] = {
+ "master:master",
+ };
+ git_strarray array = {
+ refspec_strings,
+ 1,
+ };
+ /* Should be able to push to a bare remote */
+ git_remote *localremote;
+ const char *url;
+
+ /* Get some commits */
+ connect_to_local_repository(cl_fixture("testrepo.git"));
+ cl_git_pass(git_remote_fetch(remote, &array, NULL, NULL));
+
+ /* Set up an empty bare repo to push into */
+ {
+ git_repository *localbarerepo;
+ cl_git_pass(git_repository_init(&localbarerepo, "./localbare.git", 1));
+ git_repository_free(localbarerepo);
+ }
+
+ /* Create a file URL */
+ url = cl_git_path_url("./localbare.git");
+
+ /* Connect to the bare repo */
+ cl_git_pass(git_remote_create_anonymous(&localremote, repo, url));
+ cl_git_pass(git_remote_connect(localremote, GIT_DIRECTION_PUSH, NULL, NULL, NULL));
+
+ /* Try to push */
+ cl_git_pass(git_remote_upload(localremote, &push_array, NULL));
+
+ /* Clean up */
+ git_remote_free(localremote);
+ cl_fixture_cleanup("localbare.git");
+}
+
+
+void test_network_remote_local__push_to_non_bare_remote(void)
+{
+ char *refspec_strings[] = {
+ "master:master",
+ };
+ git_strarray array = {
+ refspec_strings,
+ 1,
+ };
+ /* Shouldn't be able to push to a non-bare remote */
+ git_remote *localremote;
+ git_fetch_options fetch_opts = GIT_FETCH_OPTIONS_INIT;
+
+ /* Get some commits */
+ connect_to_local_repository(cl_fixture("testrepo.git"));
+ cl_git_pass(git_remote_fetch(remote, &array, &fetch_opts, NULL));
+
+ /* Set up an empty non-bare repo to push into */
+ {
+ git_repository *remoterepo = NULL;
+ cl_git_pass(git_repository_init(&remoterepo, "localnonbare", 0));
+ git_repository_free(remoterepo);
+ }
+
+ /* Connect to the bare repo */
+ cl_git_pass(git_remote_create_anonymous(&localremote, repo, "./localnonbare"));
+ cl_git_pass(git_remote_connect(localremote, GIT_DIRECTION_PUSH, NULL, NULL, NULL));
+
+ /* Try to push */
+ cl_git_fail_with(GIT_EBAREREPO, git_remote_upload(localremote, &push_array, NULL));
+
+ /* Clean up */
+ git_remote_free(localremote);
+ cl_fixture_cleanup("localbare.git");
+}
+
+void test_network_remote_local__fetch(void)
+{
+ char *refspec_strings[] = {
+ "master:remotes/sloppy/master",
+ };
+ git_strarray array = {
+ refspec_strings,
+ 1,
+ };
+
+ git_reflog *log;
+ const git_reflog_entry *entry;
+ git_reference *ref;
+
+ connect_to_local_repository(cl_fixture("testrepo.git"));
+
+ cl_git_pass(git_remote_fetch(remote, &array, NULL, "UPDAAAAAATE!!"));
+
+ cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/sloppy/master"));
+ git_reference_free(ref);
+
+ cl_git_pass(git_reflog_read(&log, repo, "refs/remotes/sloppy/master"));
+ cl_assert_equal_i(1, git_reflog_entrycount(log));
+ entry = git_reflog_entry_byindex(log, 0);
+ cl_assert_equal_s("foo@example.com", git_reflog_entry_committer(entry)->email);
+ cl_assert_equal_s("UPDAAAAAATE!!", git_reflog_entry_message(entry));
+
+ git_reflog_free(log);
+}
+
+void test_network_remote_local__reflog(void)
+{
+ char *refspec_strings[] = {
+ "master:remotes/sloppy/master",
+ };
+ git_strarray array = {
+ refspec_strings,
+ 1,
+ };
+
+ git_reflog *log;
+ const git_reflog_entry *entry;
+
+ connect_to_local_repository(cl_fixture("testrepo.git"));
+
+ cl_git_pass(git_remote_fetch(remote, &array, NULL, "UPDAAAAAATE!!"));
+
+ cl_git_pass(git_reflog_read(&log, repo, "refs/remotes/sloppy/master"));
+ cl_assert_equal_i(1, git_reflog_entrycount(log));
+ entry = git_reflog_entry_byindex(log, 0);
+ cl_assert_equal_s("foo@example.com", git_reflog_entry_committer(entry)->email);
+ cl_assert_equal_s("UPDAAAAAATE!!", git_reflog_entry_message(entry));
+
+ git_reflog_free(log);
+}
+
+void test_network_remote_local__fetch_default_reflog_message(void)
+{
+ char *refspec_strings[] = {
+ "master:remotes/sloppy/master",
+ };
+ git_strarray array = {
+ refspec_strings,
+ 1,
+ };
+
+ git_reflog *log;
+ const git_reflog_entry *entry;
+ char expected_reflog_msg[1024];
+
+ connect_to_local_repository(cl_fixture("testrepo.git"));
+
+ cl_git_pass(git_remote_fetch(remote, &array, NULL, NULL));
+
+ cl_git_pass(git_reflog_read(&log, repo, "refs/remotes/sloppy/master"));
+ cl_assert_equal_i(1, git_reflog_entrycount(log));
+ entry = git_reflog_entry_byindex(log, 0);
+ cl_assert_equal_s("foo@example.com", git_reflog_entry_committer(entry)->email);
+
+ sprintf(expected_reflog_msg, "fetch %s", git_remote_url(remote));
+ cl_assert_equal_s(expected_reflog_msg, git_reflog_entry_message(entry));
+
+ git_reflog_free(log);
+}
+
+void test_network_remote_local__opportunistic_update(void)
+{
+ git_reference *ref;
+ char *refspec_strings[] = {
+ "master",
+ };
+ git_strarray array = {
+ refspec_strings,
+ 1,
+ };
+
+ /* this remote has a passive refspec of "refs/heads/<star>:refs/remotes/origin/<star>" */
+ cl_git_pass(git_remote_create(&remote, repo, "origin", cl_git_fixture_url("testrepo.git")));
+ /* and we pass the active refspec "master" */
+ cl_git_pass(git_remote_fetch(remote, &array, NULL, NULL));
+
+ /* and we expect that to update our copy of origin's master */
+ cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/origin/master"));
+ git_reference_free(ref);
+}
+
+void test_network_remote_local__update_tips_for_new_remote(void) {
+ git_repository *src_repo;
+ git_repository *dst_repo;
+ git_remote *new_remote;
+ git_reference* branch;
+
+ /* Copy test repo */
+ cl_fixture_sandbox("testrepo.git");
+ cl_git_pass(git_repository_open(&src_repo, "testrepo.git"));
+
+ /* Set up an empty bare repo to push into */
+ cl_git_pass(git_repository_init(&dst_repo, "./localbare.git", 1));
+
+ /* Push to bare repo */
+ cl_git_pass(git_remote_create(&new_remote, src_repo, "bare", "./localbare.git"));
+ cl_git_pass(git_remote_push(new_remote, &push_array, NULL));
+ /* Make sure remote branch has been created */
+ cl_git_pass(git_branch_lookup(&branch, src_repo, "bare/master", GIT_BRANCH_REMOTE));
+
+ git_reference_free(branch);
+ git_remote_free(new_remote);
+ git_repository_free(dst_repo);
+ cl_fixture_cleanup("localbare.git");
+ git_repository_free(src_repo);
+ cl_fixture_cleanup("testrepo.git");
+}
+
+void test_network_remote_local__push_delete(void)
+{
+ git_repository *src_repo;
+ git_repository *dst_repo;
+ git_remote *remote;
+ git_reference *ref;
+ char *spec_push[] = { "refs/heads/master" };
+ char *spec_delete[] = { ":refs/heads/master" };
+ git_strarray specs = {
+ spec_push,
+ 1,
+ };
+
+ src_repo = cl_git_sandbox_init("testrepo.git");
+ cl_git_pass(git_repository_init(&dst_repo, "target.git", 1));
+
+ cl_git_pass(git_remote_create(&remote, src_repo, "origin", "./target.git"));
+
+ /* Push the master branch and verify it's there */
+ cl_git_pass(git_remote_push(remote, &specs, NULL));
+ cl_git_pass(git_reference_lookup(&ref, dst_repo, "refs/heads/master"));
+ git_reference_free(ref);
+
+ specs.strings = spec_delete;
+ cl_git_pass(git_remote_push(remote, &specs, NULL));
+ cl_git_fail(git_reference_lookup(&ref, dst_repo, "refs/heads/master"));
+
+ git_remote_free(remote);
+ git_repository_free(dst_repo);
+ cl_fixture_cleanup("target.git");
+ cl_git_sandbox_cleanup();
+}
+
+void test_network_remote_local__anonymous_remote_inmemory_repo(void)
+{
+ git_repository *inmemory;
+ git_remote *remote;
+
+ git_str_sets(&file_path_buf, cl_git_path_url(cl_fixture("testrepo.git")));
+
+ cl_git_pass(git_repository_new(&inmemory));
+ cl_git_pass(git_remote_create_anonymous(&remote, inmemory, git_str_cstr(&file_path_buf)));
+ cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL));
+ cl_assert(git_remote_connected(remote));
+ git_remote_disconnect(remote);
+ git_remote_free(remote);
+ git_repository_free(inmemory);
+}
diff --git a/tests/libgit2/network/remote/push.c b/tests/libgit2/network/remote/push.c
new file mode 100644
index 0000000..16588a2
--- /dev/null
+++ b/tests/libgit2/network/remote/push.c
@@ -0,0 +1,115 @@
+#include "clar_libgit2.h"
+#include "git2/sys/commit.h"
+#include "oid.h"
+
+static git_remote *_remote;
+static git_repository *_repo, *_dummy;
+
+void test_network_remote_push__initialize(void)
+{
+ cl_fixture_sandbox("testrepo.git");
+ git_repository_open(&_repo, "testrepo.git");
+
+ /* We need a repository to have a remote */
+ cl_git_pass(git_repository_init(&_dummy, "dummy.git", true));
+ cl_git_pass(git_remote_create(&_remote, _dummy, "origin", cl_git_path_url("testrepo.git")));
+}
+
+void test_network_remote_push__cleanup(void)
+{
+ git_remote_free(_remote);
+ _remote = NULL;
+
+ git_repository_free(_repo);
+ _repo = NULL;
+
+ git_repository_free(_dummy);
+ _dummy = NULL;
+
+ cl_fixture_cleanup("testrepo.git");
+ cl_fixture_cleanup("dummy.git");
+}
+
+static int negotiation_cb(const git_push_update **updates, size_t len, void *payload)
+{
+ const git_push_update *expected = payload;
+
+ cl_assert_equal_i(1, len);
+ cl_assert_equal_s(expected->src_refname, updates[0]->src_refname);
+ cl_assert_equal_s(expected->dst_refname, updates[0]->dst_refname);
+ cl_assert_equal_oid(&expected->src, &updates[0]->src);
+ cl_assert_equal_oid(&expected->dst, &updates[0]->dst);
+
+ return 0;
+}
+
+void test_network_remote_push__delete_notification(void)
+{
+ git_push_options opts = GIT_PUSH_OPTIONS_INIT;
+ git_reference *ref;
+ git_push_update expected;
+ char *refspec = ":refs/heads/master";
+ const git_strarray refspecs = {
+ &refspec,
+ 1,
+ };
+
+ cl_git_pass(git_reference_lookup(&ref, _repo, "refs/heads/master"));
+
+ expected.src_refname = "";
+ expected.dst_refname = "refs/heads/master";
+ git_oid_clear(&expected.dst, GIT_OID_SHA1);
+ git_oid_cpy(&expected.src, git_reference_target(ref));
+
+ opts.callbacks.push_negotiation = negotiation_cb;
+ opts.callbacks.payload = &expected;
+ cl_git_pass(git_remote_push(_remote, &refspecs, &opts));
+
+ git_reference_free(ref);
+ cl_git_fail_with(GIT_ENOTFOUND, git_reference_lookup(&ref, _repo, "refs/heads/master"));
+
+}
+
+static void create_dummy_commit(git_reference **out, git_repository *repo)
+{
+ git_index *index;
+ git_oid tree_id, commit_id;
+ git_signature *sig;
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_write_tree(&tree_id, index));
+ git_index_free(index);
+
+ cl_git_pass(git_signature_now(&sig, "Pusher Joe", "pjoe"));
+ cl_git_pass(git_commit_create_from_ids(&commit_id, repo, NULL, sig, sig,
+ NULL, "Empty tree\n", &tree_id, 0, NULL));
+ cl_git_pass(git_reference_create(out, repo, "refs/heads/empty-tree", &commit_id, true, "commit yo"));
+ git_signature_free(sig);
+}
+
+void test_network_remote_push__create_notification(void)
+{
+ git_push_options opts = GIT_PUSH_OPTIONS_INIT;
+ git_reference *ref;
+ git_push_update expected;
+ char *refspec = "refs/heads/empty-tree";
+ const git_strarray refspecs = {
+ &refspec,
+ 1,
+ };
+
+ create_dummy_commit(&ref, _dummy);
+
+ expected.src_refname = "refs/heads/empty-tree";
+ expected.dst_refname = "refs/heads/empty-tree";
+ git_oid_cpy(&expected.dst, git_reference_target(ref));
+ git_oid_clear(&expected.src, GIT_OID_SHA1);
+
+ opts.callbacks.push_negotiation = negotiation_cb;
+ opts.callbacks.payload = &expected;
+ cl_git_pass(git_remote_push(_remote, &refspecs, &opts));
+
+ git_reference_free(ref);
+ cl_git_pass(git_reference_lookup(&ref, _repo, "refs/heads/empty-tree"));
+ git_reference_free(ref);
+}
diff --git a/tests/libgit2/network/remote/remotes.c b/tests/libgit2/network/remote/remotes.c
new file mode 100644
index 0000000..79c4f39
--- /dev/null
+++ b/tests/libgit2/network/remote/remotes.c
@@ -0,0 +1,575 @@
+#include "clar_libgit2.h"
+#include "config/config_helpers.h"
+#include "refspec.h"
+#include "remote.h"
+
+static git_remote *_remote;
+static git_repository *_repo;
+static const git_refspec *_refspec;
+
+void test_network_remote_remotes__initialize(void)
+{
+ _repo = cl_git_sandbox_init("testrepo.git");
+
+ cl_git_pass(git_remote_lookup(&_remote, _repo, "test"));
+
+ _refspec = git_remote_get_refspec(_remote, 0);
+ cl_assert(_refspec != NULL);
+}
+
+void test_network_remote_remotes__cleanup(void)
+{
+ git_remote_free(_remote);
+ _remote = NULL;
+
+ cl_git_sandbox_cleanup();
+}
+
+void test_network_remote_remotes__parsing(void)
+{
+ git_str url = GIT_STR_INIT;
+ git_remote *_remote2 = NULL;
+
+ cl_assert_equal_s(git_remote_name(_remote), "test");
+ cl_assert_equal_s(git_remote_url(_remote), "git://github.com/libgit2/libgit2");
+ cl_assert(git_remote_pushurl(_remote) == NULL);
+
+ cl_git_pass(git_remote__urlfordirection(&url, _remote, GIT_DIRECTION_FETCH, NULL));
+ cl_assert_equal_s(url.ptr, "git://github.com/libgit2/libgit2");
+
+ cl_git_pass(git_remote__urlfordirection(&url, _remote, GIT_DIRECTION_PUSH, NULL));
+ cl_assert_equal_s(url.ptr, "git://github.com/libgit2/libgit2");
+
+ cl_git_pass(git_remote_lookup(&_remote2, _repo, "test_with_pushurl"));
+ cl_assert_equal_s(git_remote_name(_remote2), "test_with_pushurl");
+ cl_assert_equal_s(git_remote_url(_remote2), "git://github.com/libgit2/fetchlibgit2");
+ cl_assert_equal_s(git_remote_pushurl(_remote2), "git://github.com/libgit2/pushlibgit2");
+
+ cl_git_pass(git_remote__urlfordirection(&url, _remote2, GIT_DIRECTION_FETCH, NULL));
+ cl_assert_equal_s(url.ptr, "git://github.com/libgit2/fetchlibgit2");
+
+ cl_git_pass(git_remote__urlfordirection(&url, _remote2, GIT_DIRECTION_PUSH, NULL));
+ cl_assert_equal_s(url.ptr, "git://github.com/libgit2/pushlibgit2");
+
+ git_remote_free(_remote2);
+ git_str_dispose(&url);
+}
+
+static int remote_ready_callback(git_remote *remote, int direction, void *payload)
+{
+ if (direction == GIT_DIRECTION_PUSH) {
+ const char *url = git_remote_pushurl(remote);
+
+ cl_assert_equal_p(url, NULL);;
+ cl_assert_equal_s(payload, "payload");
+ return git_remote_set_instance_pushurl(remote, "push_url");
+ }
+
+ if (direction == GIT_DIRECTION_FETCH) {
+ const char *url = git_remote_url(remote);
+
+ cl_assert_equal_s(url, "git://github.com/libgit2/libgit2");
+ cl_assert_equal_s(payload, "payload");
+ return git_remote_set_instance_url(remote, "fetch_url");
+ }
+
+ return -1;
+}
+
+void test_network_remote_remotes__remote_ready(void)
+{
+ git_str url = GIT_STR_INIT;
+
+ git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
+ callbacks.remote_ready = remote_ready_callback;
+ callbacks.payload = "payload";
+
+ cl_assert_equal_s(git_remote_name(_remote), "test");
+ cl_assert_equal_s(git_remote_url(_remote), "git://github.com/libgit2/libgit2");
+ cl_assert(git_remote_pushurl(_remote) == NULL);
+
+ cl_git_pass(git_remote__urlfordirection(&url, _remote, GIT_DIRECTION_FETCH, &callbacks));
+ cl_assert_equal_s(url.ptr, "fetch_url");
+
+ cl_git_pass(git_remote__urlfordirection(&url, _remote, GIT_DIRECTION_PUSH, &callbacks));
+ cl_assert_equal_s(url.ptr, "push_url");
+
+ git_str_dispose(&url);
+}
+
+#ifndef GIT_DEPRECATE_HARD
+static int urlresolve_callback(git_buf *url_resolved, const char *url, int direction, void *payload)
+{
+ int error = -1;
+
+ cl_assert(strcmp(url, "git://github.com/libgit2/libgit2") == 0);
+ cl_assert(strcmp(payload, "payload") == 0);
+ cl_assert(url_resolved->size == 0);
+
+ if (direction == GIT_DIRECTION_PUSH)
+ error = git_buf_set(url_resolved, "pushresolve", strlen("pushresolve") + 1);
+ if (direction == GIT_DIRECTION_FETCH)
+ error = git_buf_set(url_resolved, "fetchresolve", strlen("fetchresolve") + 1);
+
+ return error;
+}
+#endif
+
+void test_network_remote_remotes__urlresolve(void)
+{
+#ifndef GIT_DEPRECATE_HARD
+ git_str url = GIT_STR_INIT;
+
+ git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
+ callbacks.resolve_url = urlresolve_callback;
+ callbacks.payload = "payload";
+
+ cl_assert_equal_s(git_remote_name(_remote), "test");
+ cl_assert_equal_s(git_remote_url(_remote), "git://github.com/libgit2/libgit2");
+ cl_assert(git_remote_pushurl(_remote) == NULL);
+
+ cl_git_pass(git_remote__urlfordirection(&url, _remote, GIT_DIRECTION_FETCH, &callbacks));
+ cl_assert_equal_s(url.ptr, "fetchresolve");
+
+ cl_git_pass(git_remote__urlfordirection(&url, _remote, GIT_DIRECTION_PUSH, &callbacks));
+ cl_assert_equal_s(url.ptr, "pushresolve");
+
+ git_str_dispose(&url);
+#endif
+}
+
+#ifndef GIT_DEPRECATE_HARD
+static int urlresolve_passthrough_callback(git_buf *url_resolved, const char *url, int direction, void *payload)
+{
+ GIT_UNUSED(url_resolved);
+ GIT_UNUSED(url);
+ GIT_UNUSED(direction);
+ GIT_UNUSED(payload);
+ return GIT_PASSTHROUGH;
+}
+#endif
+
+void test_network_remote_remotes__urlresolve_passthrough(void)
+{
+#ifndef GIT_DEPRECATE_HARD
+ git_str url = GIT_STR_INIT;
+ const char *orig_url = "git://github.com/libgit2/libgit2";
+
+ git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
+ callbacks.resolve_url = urlresolve_passthrough_callback;
+
+ cl_assert_equal_s(git_remote_name(_remote), "test");
+ cl_assert_equal_s(git_remote_url(_remote), orig_url);
+ cl_assert(git_remote_pushurl(_remote) == NULL);
+
+ cl_git_pass(git_remote__urlfordirection(&url, _remote, GIT_DIRECTION_FETCH, &callbacks));
+ cl_assert_equal_s(url.ptr, orig_url);
+
+ cl_git_pass(git_remote__urlfordirection(&url, _remote, GIT_DIRECTION_PUSH, &callbacks));
+ cl_assert_equal_s(url.ptr, orig_url);
+
+ git_str_dispose(&url);
+#endif
+}
+
+void test_network_remote_remotes__instance_url(void)
+{
+ git_str url = GIT_STR_INIT;
+ const char *orig_url = "git://github.com/libgit2/libgit2";
+
+ cl_assert_equal_s(git_remote_name(_remote), "test");
+ cl_assert_equal_s(git_remote_url(_remote), orig_url);
+
+ cl_git_pass(git_remote__urlfordirection(&url, _remote, GIT_DIRECTION_FETCH, NULL));
+ cl_assert_equal_s(url.ptr, orig_url);
+ git_str_clear(&url);
+
+ cl_git_pass(git_remote__urlfordirection(&url, _remote, GIT_DIRECTION_PUSH, NULL));
+ cl_assert_equal_s(url.ptr, orig_url);
+ git_str_clear(&url);
+
+ /* Setting the instance url updates the fetch and push URLs */
+ git_remote_set_instance_url(_remote, "https://github.com/new/remote/url");
+ cl_assert_equal_s(git_remote_url(_remote), "https://github.com/new/remote/url");
+ cl_assert_equal_p(git_remote_pushurl(_remote), NULL);
+
+ cl_git_pass(git_remote__urlfordirection(&url, _remote, GIT_DIRECTION_FETCH, NULL));
+ cl_assert_equal_s(url.ptr, "https://github.com/new/remote/url");
+ git_str_clear(&url);
+
+ cl_git_pass(git_remote__urlfordirection(&url, _remote, GIT_DIRECTION_PUSH, NULL));
+ cl_assert_equal_s(url.ptr, "https://github.com/new/remote/url");
+ git_str_clear(&url);
+
+ /* Setting the instance push url updates only the push URL */
+ git_remote_set_instance_pushurl(_remote, "https://github.com/new/push/url");
+ cl_assert_equal_s(git_remote_url(_remote), "https://github.com/new/remote/url");
+ cl_assert_equal_s(git_remote_pushurl(_remote), "https://github.com/new/push/url");
+
+ cl_git_pass(git_remote__urlfordirection(&url, _remote, GIT_DIRECTION_FETCH, NULL));
+ cl_assert_equal_s(url.ptr, "https://github.com/new/remote/url");
+ git_str_clear(&url);
+
+ cl_git_pass(git_remote__urlfordirection(&url, _remote, GIT_DIRECTION_PUSH, NULL));
+ cl_assert_equal_s(url.ptr, "https://github.com/new/push/url");
+ git_str_clear(&url);
+
+ git_str_dispose(&url);
+}
+
+void test_network_remote_remotes__pushurl(void)
+{
+ const char *name = git_remote_name(_remote);
+ git_remote *mod;
+
+ cl_git_pass(git_remote_set_pushurl(_repo, name, "git://github.com/libgit2/notlibgit2"));
+ cl_git_pass(git_remote_lookup(&mod, _repo, name));
+ cl_assert_equal_s(git_remote_pushurl(mod), "git://github.com/libgit2/notlibgit2");
+ git_remote_free(mod);
+
+ cl_git_pass(git_remote_set_pushurl(_repo, name, NULL));
+ cl_git_pass(git_remote_lookup(&mod, _repo, name));
+ cl_assert(git_remote_pushurl(mod) == NULL);
+ git_remote_free(mod);
+}
+
+void test_network_remote_remotes__error_when_not_found(void)
+{
+ git_remote *r;
+ cl_git_fail_with(git_remote_lookup(&r, _repo, "does-not-exist"), GIT_ENOTFOUND);
+
+ cl_assert(git_error_last() != NULL);
+ cl_assert(git_error_last()->klass == GIT_ERROR_CONFIG);
+}
+
+void test_network_remote_remotes__error_when_no_push_available(void)
+{
+ git_remote *r;
+ git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
+ char *specs = {
+ "refs/heads/master",
+ };
+ git_strarray arr = {
+ &specs,
+ 1,
+ };
+
+
+ cl_git_pass(git_remote_create_anonymous(&r, _repo, cl_fixture("testrepo.git")));
+
+ callbacks.transport = git_transport_local;
+ cl_git_pass(git_remote_connect(r, GIT_DIRECTION_PUSH, &callbacks, NULL, NULL));
+
+ /* Make sure that push is really not available */
+ r->transport->push = NULL;
+
+ cl_git_fail_with(-1, git_remote_upload(r, &arr, NULL));
+
+ git_remote_free(r);
+}
+
+void test_network_remote_remotes__refspec_parsing(void)
+{
+ cl_assert_equal_s(git_refspec_src(_refspec), "refs/heads/*");
+ cl_assert_equal_s(git_refspec_dst(_refspec), "refs/remotes/test/*");
+}
+
+void test_network_remote_remotes__add_fetchspec(void)
+{
+ size_t size;
+
+ size = git_remote_refspec_count(_remote);
+
+ cl_git_pass(git_remote_add_fetch(_repo, "test", "refs/*:refs/*"));
+ size++;
+
+ git_remote_free(_remote);
+ cl_git_pass(git_remote_lookup(&_remote, _repo, "test"));
+
+ cl_assert_equal_i((int)size, (int)git_remote_refspec_count(_remote));
+
+ _refspec = git_remote_get_refspec(_remote, size - 1);
+ cl_assert_equal_s(git_refspec_src(_refspec), "refs/*");
+ cl_assert_equal_s(git_refspec_dst(_refspec), "refs/*");
+ cl_assert_equal_s(git_refspec_string(_refspec), "refs/*:refs/*");
+ cl_assert_equal_b(_refspec->push, false);
+
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_remote_add_fetch(_repo, "test", "refs/*/foo/*:refs/*"));
+}
+
+void test_network_remote_remotes__dup(void)
+{
+ git_strarray array;
+ git_remote *dup;
+
+ cl_git_pass(git_remote_dup(&dup, _remote));
+
+ cl_assert_equal_s(git_remote_name(dup), git_remote_name(_remote));
+ cl_assert_equal_s(git_remote_url(dup), git_remote_url(_remote));
+ cl_assert_equal_s(git_remote_pushurl(dup), git_remote_pushurl(_remote));
+
+ cl_git_pass(git_remote_get_fetch_refspecs(&array, _remote));
+ cl_assert_equal_i(1, (int)array.count);
+ cl_assert_equal_s("+refs/heads/*:refs/remotes/test/*", array.strings[0]);
+ git_strarray_dispose(&array);
+
+ cl_git_pass(git_remote_get_push_refspecs(&array, _remote));
+ cl_assert_equal_i(0, (int)array.count);
+ git_strarray_dispose(&array);
+
+ git_remote_free(dup);
+}
+
+void test_network_remote_remotes__add_pushspec(void)
+{
+ size_t size;
+
+ size = git_remote_refspec_count(_remote);
+
+ cl_git_pass(git_remote_add_push(_repo, "test", "refs/*:refs/*"));
+ size++;
+
+ git_remote_free(_remote);
+ cl_git_pass(git_remote_lookup(&_remote, _repo, "test"));
+
+ cl_assert_equal_i((int)size, (int)git_remote_refspec_count(_remote));
+
+ _refspec = git_remote_get_refspec(_remote, size - 1);
+ cl_assert_equal_s(git_refspec_src(_refspec), "refs/*");
+ cl_assert_equal_s(git_refspec_dst(_refspec), "refs/*");
+ cl_assert_equal_s(git_refspec_string(_refspec), "refs/*:refs/*");
+
+ cl_assert_equal_b(_refspec->push, true);
+}
+
+void test_network_remote_remotes__fnmatch(void)
+{
+ cl_assert(git_refspec_src_matches(_refspec, "refs/heads/master"));
+ cl_assert(git_refspec_src_matches(_refspec, "refs/heads/multi/level/branch"));
+}
+
+void test_network_remote_remotes__transform(void)
+{
+ git_buf ref = GIT_BUF_INIT;
+
+ cl_git_pass(git_refspec_transform(&ref, _refspec, "refs/heads/master"));
+ cl_assert_equal_s(ref.ptr, "refs/remotes/test/master");
+ git_buf_dispose(&ref);
+}
+
+void test_network_remote_remotes__transform_destination_to_source(void)
+{
+ git_buf ref = GIT_BUF_INIT;
+
+ cl_git_pass(git_refspec_rtransform(&ref, _refspec, "refs/remotes/test/master"));
+ cl_assert_equal_s(ref.ptr, "refs/heads/master");
+ git_buf_dispose(&ref);
+}
+
+void test_network_remote_remotes__missing_refspecs(void)
+{
+ git_config *cfg;
+
+ git_remote_free(_remote);
+ _remote = NULL;
+
+ cl_git_pass(git_repository_config(&cfg, _repo));
+ cl_git_pass(git_config_set_string(cfg, "remote.specless.url", "http://example.com"));
+ cl_git_pass(git_remote_lookup(&_remote, _repo, "specless"));
+
+ git_config_free(cfg);
+}
+
+void test_network_remote_remotes__nonmatch_upstream_refspec(void)
+{
+ git_config *config;
+ git_remote *remote;
+ char *specstr[] = {
+ "refs/tags/*:refs/tags/*",
+ };
+ git_strarray specs = {
+ specstr,
+ 1,
+ };
+
+ cl_git_pass(git_remote_create(&remote, _repo, "taggy", git_repository_path(_repo)));
+
+ /*
+ * Set the current branch's upstream remote to a dummy ref so we call into the code
+ * which tries to check for the current branch's upstream in the refspecs
+ */
+ cl_git_pass(git_repository_config(&config, _repo));
+ cl_git_pass(git_config_set_string(config, "branch.master.remote", "taggy"));
+ cl_git_pass(git_config_set_string(config, "branch.master.merge", "refs/heads/foo"));
+
+ cl_git_pass(git_remote_fetch(remote, &specs, NULL, NULL));
+
+ git_remote_free(remote);
+}
+
+void test_network_remote_remotes__list(void)
+{
+ git_strarray list;
+ git_config *cfg;
+
+ cl_git_pass(git_remote_list(&list, _repo));
+ cl_assert(list.count == 5);
+ git_strarray_dispose(&list);
+
+ cl_git_pass(git_repository_config(&cfg, _repo));
+
+ /* Create a new remote */
+ cl_git_pass(git_config_set_string(cfg, "remote.specless.url", "http://example.com"));
+
+ /* Update a remote (previously without any url/pushurl entry) */
+ cl_git_pass(git_config_set_string(cfg, "remote.no-remote-url.pushurl", "http://example.com"));
+
+ cl_git_pass(git_remote_list(&list, _repo));
+ cl_assert(list.count == 7);
+ git_strarray_dispose(&list);
+
+ git_config_free(cfg);
+}
+
+void test_network_remote_remotes__loading_a_missing_remote_returns_ENOTFOUND(void)
+{
+ git_remote_free(_remote);
+ _remote = NULL;
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_remote_lookup(&_remote, _repo, "just-left-few-minutes-ago"));
+}
+
+void test_network_remote_remotes__loading_with_an_invalid_name_returns_EINVALIDSPEC(void)
+{
+ git_remote_free(_remote);
+ _remote = NULL;
+
+ cl_assert_equal_i(GIT_EINVALIDSPEC, git_remote_lookup(&_remote, _repo, "Inv@{id"));
+}
+
+/*
+ * $ git remote add addtest http://github.com/libgit2/libgit2
+ *
+ * $ cat .git/config
+ * [...]
+ * [remote "addtest"]
+ * url = http://github.com/libgit2/libgit2
+ * fetch = +refs/heads/\*:refs/remotes/addtest/\*
+ */
+void test_network_remote_remotes__add(void)
+{
+ git_remote_free(_remote);
+ _remote = NULL;
+
+ cl_git_pass(git_remote_create(&_remote, _repo, "addtest", "http://github.com/libgit2/libgit2"));
+ cl_assert_equal_i(GIT_REMOTE_DOWNLOAD_TAGS_AUTO, git_remote_autotag(_remote));
+
+ git_remote_free(_remote);
+ _remote = NULL;
+
+ cl_git_pass(git_remote_lookup(&_remote, _repo, "addtest"));
+ cl_assert_equal_i(GIT_REMOTE_DOWNLOAD_TAGS_AUTO, git_remote_autotag(_remote));
+
+ _refspec = git_vector_get(&_remote->refspecs, 0);
+ cl_assert_equal_s("refs/heads/*", git_refspec_src(_refspec));
+ cl_assert(git_refspec_force(_refspec) == 1);
+ cl_assert_equal_s("refs/remotes/addtest/*", git_refspec_dst(_refspec));
+ cl_assert_equal_s(git_remote_url(_remote), "http://github.com/libgit2/libgit2");
+}
+
+void test_network_remote_remotes__tagopt(void)
+{
+ const char *name = git_remote_name(_remote);
+
+ git_remote_set_autotag(_repo, name, GIT_REMOTE_DOWNLOAD_TAGS_ALL);
+ assert_config_entry_value(_repo, "remote.test.tagopt", "--tags");
+
+ git_remote_set_autotag(_repo, name, GIT_REMOTE_DOWNLOAD_TAGS_NONE);
+ assert_config_entry_value(_repo, "remote.test.tagopt", "--no-tags");
+
+ git_remote_set_autotag(_repo, name, GIT_REMOTE_DOWNLOAD_TAGS_AUTO);
+ assert_config_entry_existence(_repo, "remote.test.tagopt", false);
+}
+
+void test_network_remote_remotes__can_load_with_an_empty_url(void)
+{
+ git_remote *remote = NULL;
+
+ cl_git_pass(git_remote_lookup(&remote, _repo, "empty-remote-url"));
+
+ cl_assert(remote->url == NULL);
+ cl_assert(remote->pushurl == NULL);
+
+ cl_git_fail(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL));
+
+ cl_assert(git_error_last() != NULL);
+ cl_assert(git_error_last()->klass == GIT_ERROR_INVALID);
+
+ git_remote_free(remote);
+}
+
+void test_network_remote_remotes__can_load_with_only_an_empty_pushurl(void)
+{
+ git_remote *remote = NULL;
+
+ cl_git_pass(git_remote_lookup(&remote, _repo, "empty-remote-pushurl"));
+
+ cl_assert(remote->url == NULL);
+ cl_assert(remote->pushurl == NULL);
+
+ cl_git_fail(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL));
+
+ git_remote_free(remote);
+}
+
+void test_network_remote_remotes__returns_ENOTFOUND_when_neither_url_nor_pushurl(void)
+{
+ git_remote *remote = NULL;
+
+ cl_git_fail_with(
+ git_remote_lookup(&remote, _repo, "no-remote-url"), GIT_ENOTFOUND);
+}
+
+static const char *fetch_refspecs[] = {
+ "+refs/heads/*:refs/remotes/origin/*",
+ "refs/tags/*:refs/tags/*",
+ "+refs/pull/*:refs/pull/*",
+};
+
+static const char *push_refspecs[] = {
+ "refs/heads/*:refs/heads/*",
+ "refs/tags/*:refs/tags/*",
+ "refs/notes/*:refs/notes/*",
+};
+
+void test_network_remote_remotes__query_refspecs(void)
+{
+ git_remote *remote;
+ git_strarray array;
+ int i;
+
+ cl_git_pass(git_remote_create_with_fetchspec(&remote, _repo, "query", "git://github.com/libgit2/libgit2", NULL));
+ git_remote_free(remote);
+
+ for (i = 0; i < 3; i++) {
+ cl_git_pass(git_remote_add_fetch(_repo, "query", fetch_refspecs[i]));
+ cl_git_pass(git_remote_add_push(_repo, "query", push_refspecs[i]));
+ }
+
+ cl_git_pass(git_remote_lookup(&remote, _repo, "query"));
+
+ cl_git_pass(git_remote_get_fetch_refspecs(&array, remote));
+ for (i = 0; i < 3; i++) {
+ cl_assert_equal_s(fetch_refspecs[i], array.strings[i]);
+ }
+ git_strarray_dispose(&array);
+
+ cl_git_pass(git_remote_get_push_refspecs(&array, remote));
+ for (i = 0; i < 3; i++) {
+ cl_assert_equal_s(push_refspecs[i], array.strings[i]);
+ }
+ git_strarray_dispose(&array);
+
+ git_remote_free(remote);
+ git_remote_delete(_repo, "test");
+}
diff --git a/tests/libgit2/network/remote/rename.c b/tests/libgit2/network/remote/rename.c
new file mode 100644
index 0000000..b071f9c
--- /dev/null
+++ b/tests/libgit2/network/remote/rename.c
@@ -0,0 +1,245 @@
+#include "clar_libgit2.h"
+#include "config/config_helpers.h"
+
+#include "repository.h"
+
+static git_repository *_repo;
+static const char *_remote_name = "test";
+
+void test_network_remote_rename__initialize(void)
+{
+ _repo = cl_git_sandbox_init("testrepo.git");
+}
+
+void test_network_remote_rename__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_network_remote_rename__renaming_a_remote_moves_related_configuration_section(void)
+{
+ git_strarray problems = {0};
+
+ assert_config_entry_existence(_repo, "remote.test.fetch", true);
+ assert_config_entry_existence(_repo, "remote.just/renamed.fetch", false);
+
+ cl_git_pass(git_remote_rename(&problems, _repo, _remote_name, "just/renamed"));
+ cl_assert_equal_i(0, problems.count);
+ git_strarray_dispose(&problems);
+
+ assert_config_entry_existence(_repo, "remote.test.fetch", false);
+ assert_config_entry_existence(_repo, "remote.just/renamed.fetch", true);
+}
+
+void test_network_remote_rename__renaming_a_remote_updates_branch_related_configuration_entries(void)
+{
+ git_strarray problems = {0};
+
+ assert_config_entry_value(_repo, "branch.master.remote", "test");
+
+ cl_git_pass(git_remote_rename(&problems, _repo, _remote_name, "just/renamed"));
+ cl_assert_equal_i(0, problems.count);
+ git_strarray_dispose(&problems);
+
+ assert_config_entry_value(_repo, "branch.master.remote", "just/renamed");
+}
+
+void test_network_remote_rename__renaming_a_remote_updates_default_fetchrefspec(void)
+{
+ git_strarray problems = {0};
+
+ cl_git_pass(git_remote_rename(&problems, _repo, _remote_name, "just/renamed"));
+ cl_assert_equal_i(0, problems.count);
+ git_strarray_dispose(&problems);
+
+ assert_config_entry_value(_repo, "remote.just/renamed.fetch", "+refs/heads/*:refs/remotes/just/renamed/*");
+}
+
+void test_network_remote_rename__renaming_a_remote_without_a_fetchrefspec_doesnt_create_one(void)
+{
+ git_config *config;
+ git_remote *remote;
+ git_strarray problems = {0};
+
+ cl_git_pass(git_repository_config__weakptr(&config, _repo));
+ cl_git_pass(git_config_delete_entry(config, "remote.test.fetch"));
+
+ cl_git_pass(git_remote_lookup(&remote, _repo, "test"));
+ git_remote_free(remote);
+
+ assert_config_entry_existence(_repo, "remote.test.fetch", false);
+
+ cl_git_pass(git_remote_rename(&problems, _repo, _remote_name, "just/renamed"));
+ cl_assert_equal_i(0, problems.count);
+ git_strarray_dispose(&problems);
+
+ assert_config_entry_existence(_repo, "remote.just/renamed.fetch", false);
+}
+
+void test_network_remote_rename__renaming_a_remote_notifies_of_non_default_fetchrefspec(void)
+{
+ git_config *config;
+ git_remote *remote;
+ git_strarray problems = {0};
+
+ cl_git_pass(git_repository_config__weakptr(&config, _repo));
+ cl_git_pass(git_config_set_string(config, "remote.test.fetch", "+refs/*:refs/*"));
+ cl_git_pass(git_remote_lookup(&remote, _repo, "test"));
+ git_remote_free(remote);
+
+ cl_git_pass(git_remote_rename(&problems, _repo, _remote_name, "just/renamed"));
+ cl_assert_equal_i(1, problems.count);
+ cl_assert_equal_s("+refs/*:refs/*", problems.strings[0]);
+ git_strarray_dispose(&problems);
+
+ assert_config_entry_value(_repo, "remote.just/renamed.fetch", "+refs/*:refs/*");
+
+ git_strarray_dispose(&problems);
+}
+
+void test_network_remote_rename__new_name_can_contain_dots(void)
+{
+ git_strarray problems = {0};
+
+ cl_git_pass(git_remote_rename(&problems, _repo, _remote_name, "just.renamed"));
+ cl_assert_equal_i(0, problems.count);
+ git_strarray_dispose(&problems);
+ assert_config_entry_existence(_repo, "remote.just.renamed.fetch", true);
+}
+
+void test_network_remote_rename__new_name_must_conform_to_reference_naming_conventions(void)
+{
+ git_strarray problems = {0};
+
+ cl_assert_equal_i(
+ GIT_EINVALIDSPEC,
+ git_remote_rename(&problems, _repo, _remote_name, "new@{name"));
+}
+
+void test_network_remote_rename__renamed_name_is_persisted(void)
+{
+ git_remote *renamed;
+ git_repository *another_repo;
+ git_strarray problems = {0};
+
+ cl_git_fail(git_remote_lookup(&renamed, _repo, "just/renamed"));
+
+ cl_git_pass(git_remote_rename(&problems, _repo, _remote_name, "just/renamed"));
+ cl_assert_equal_i(0, problems.count);
+ git_strarray_dispose(&problems);
+
+ cl_git_pass(git_repository_open(&another_repo, "testrepo.git"));
+ cl_git_pass(git_remote_lookup(&renamed, _repo, "just/renamed"));
+
+ git_remote_free(renamed);
+ git_repository_free(another_repo);
+}
+
+void test_network_remote_rename__cannot_overwrite_an_existing_remote(void)
+{
+ git_strarray problems = {0};
+
+ cl_assert_equal_i(GIT_EEXISTS, git_remote_rename(&problems, _repo, _remote_name, "test"));
+ cl_assert_equal_i(GIT_EEXISTS, git_remote_rename(&problems, _repo, _remote_name, "test_with_pushurl"));
+}
+
+void test_network_remote_rename__renaming_a_remote_moves_the_underlying_reference(void)
+{
+ git_reference *underlying;
+ git_strarray problems = {0};
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&underlying, _repo, "refs/remotes/just/renamed"));
+ cl_git_pass(git_reference_lookup(&underlying, _repo, "refs/remotes/test/master"));
+ git_reference_free(underlying);
+
+ cl_git_pass(git_remote_rename(&problems, _repo, _remote_name, "just/renamed"));
+ cl_assert_equal_i(0, problems.count);
+ git_strarray_dispose(&problems);
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_reference_lookup(&underlying, _repo, "refs/remotes/test/master"));
+ cl_git_pass(git_reference_lookup(&underlying, _repo, "refs/remotes/just/renamed/master"));
+ git_reference_free(underlying);
+}
+
+void test_network_remote_rename__overwrite_ref_in_target(void)
+{
+ git_oid id;
+ char idstr[GIT_OID_SHA1_HEXSIZE + 1] = {0};
+ git_reference *ref;
+ git_branch_t btype;
+ git_branch_iterator *iter;
+ git_strarray problems = {0};
+
+ cl_git_pass(git_oid__fromstr(&id, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", GIT_OID_SHA1));
+ cl_git_pass(git_reference_create(&ref, _repo, "refs/remotes/renamed/master", &id, 1, NULL));
+ git_reference_free(ref);
+
+ cl_git_pass(git_remote_rename(&problems, _repo, _remote_name, "renamed"));
+ cl_assert_equal_i(0, problems.count);
+ git_strarray_dispose(&problems);
+
+ /* make sure there's only one remote-tracking branch */
+ cl_git_pass(git_branch_iterator_new(&iter, _repo, GIT_BRANCH_REMOTE));
+ cl_git_pass(git_branch_next(&ref, &btype, iter));
+ cl_assert_equal_s("refs/remotes/renamed/master", git_reference_name(ref));
+ git_oid_fmt(idstr, git_reference_target(ref));
+ cl_assert_equal_s("be3563ae3f795b2b4353bcce3a527ad0a4f7f644", idstr);
+ git_reference_free(ref);
+
+ cl_git_fail_with(GIT_ITEROVER, git_branch_next(&ref, &btype, iter));
+ git_branch_iterator_free(iter);
+}
+
+void test_network_remote_rename__nonexistent_returns_enotfound(void)
+{
+ git_strarray problems = {0};
+
+ int err = git_remote_rename(&problems, _repo, "nonexistent", "renamed");
+
+ cl_assert_equal_i(GIT_ENOTFOUND, err);
+}
+
+void test_network_remote_rename__symref_head(void)
+{
+ int error;
+ git_reference *ref;
+ git_branch_t btype;
+ git_branch_iterator *iter;
+ git_strarray problems = {0};
+ char idstr[GIT_OID_SHA1_HEXSIZE + 1] = {0};
+ git_vector refs;
+
+ cl_git_pass(git_reference_symbolic_create(&ref, _repo, "refs/remotes/test/HEAD", "refs/remotes/test/master", 0, NULL));
+ git_reference_free(ref);
+
+ cl_git_pass(git_remote_rename(&problems, _repo, _remote_name, "renamed"));
+ cl_assert_equal_i(0, problems.count);
+ git_strarray_dispose(&problems);
+
+ cl_git_pass(git_vector_init(&refs, 2, (git_vector_cmp) git_reference_cmp));
+ cl_git_pass(git_branch_iterator_new(&iter, _repo, GIT_BRANCH_REMOTE));
+
+ while ((error = git_branch_next(&ref, &btype, iter)) == 0) {
+ cl_git_pass(git_vector_insert(&refs, ref));
+ }
+ cl_assert_equal_i(GIT_ITEROVER, error);
+ git_vector_sort(&refs);
+
+ cl_assert_equal_i(2, refs.length);
+
+ ref = git_vector_get(&refs, 0);
+ cl_assert_equal_s("refs/remotes/renamed/HEAD", git_reference_name(ref));
+ cl_assert_equal_s("refs/remotes/renamed/master", git_reference_symbolic_target(ref));
+ git_reference_free(ref);
+
+ ref = git_vector_get(&refs, 1);
+ cl_assert_equal_s("refs/remotes/renamed/master", git_reference_name(ref));
+ git_oid_fmt(idstr, git_reference_target(ref));
+ cl_assert_equal_s("be3563ae3f795b2b4353bcce3a527ad0a4f7f644", idstr);
+ git_reference_free(ref);
+
+ git_vector_free(&refs);
+
+ cl_git_fail_with(GIT_ITEROVER, git_branch_next(&ref, &btype, iter));
+ git_branch_iterator_free(iter);
+}
diff --git a/tests/libgit2/notes/notes.c b/tests/libgit2/notes/notes.c
new file mode 100644
index 0000000..06c4409
--- /dev/null
+++ b/tests/libgit2/notes/notes.c
@@ -0,0 +1,658 @@
+#include "clar_libgit2.h"
+
+static git_repository *_repo;
+static git_signature *_sig;
+
+void test_notes_notes__initialize(void)
+{
+ _repo = cl_git_sandbox_init("testrepo.git");
+ cl_git_pass(git_signature_now(&_sig, "alice", "alice@example.com"));
+}
+
+void test_notes_notes__cleanup(void)
+{
+ git_signature_free(_sig);
+ _sig = NULL;
+
+ cl_git_sandbox_cleanup();
+}
+
+static void assert_note_equal(git_note *note, char *message, git_oid *note_oid) {
+ git_blob *blob;
+
+ cl_assert_equal_s(git_note_message(note), message);
+ cl_assert_equal_oid(git_note_id(note), note_oid);
+
+ cl_git_pass(git_blob_lookup(&blob, _repo, note_oid));
+ cl_assert_equal_s(git_note_message(note), (const char *)git_blob_rawcontent(blob));
+
+ git_blob_free(blob);
+}
+
+static void create_note(git_oid *note_oid, const char *canonical_namespace, const char *target_sha, const char *message)
+{
+ git_oid oid;
+
+ cl_git_pass(git_oid__fromstr(&oid, target_sha, GIT_OID_SHA1));
+ cl_git_pass(git_note_create(note_oid, _repo, canonical_namespace, _sig, _sig, &oid, message, 0));
+}
+
+static struct {
+ const char *note_sha;
+ const char *annotated_object_sha;
+}
+list_expectations[] = {
+ { "1c73b1f51762155d357bcd1fd4f2c409ef80065b", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045" },
+ { "1c73b1f51762155d357bcd1fd4f2c409ef80065b", "9fd738e8f7967c078dceed8190330fc8648ee56a" },
+ { "257b43746b6b46caa4aa788376c647cce0a33e2b", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750" },
+ { "1ec1c8e03f461f4f5d3f3702172483662e7223f3", "c47800c7266a2be04c571c04d5a6614691ea99bd" },
+ { NULL, NULL }
+};
+
+#define EXPECTATIONS_COUNT (sizeof(list_expectations)/sizeof(list_expectations[0])) - 1
+
+static int note_list_cb(
+ const git_oid *blob_id, const git_oid *annotated_obj_id, void *payload)
+{
+ git_oid expected_note_oid, expected_target_oid;
+
+ unsigned int *count = (unsigned int *)payload;
+
+ cl_assert(*count < EXPECTATIONS_COUNT);
+
+ cl_git_pass(git_oid__fromstr(&expected_note_oid, list_expectations[*count].note_sha, GIT_OID_SHA1));
+ cl_assert_equal_oid(&expected_note_oid, blob_id);
+
+ cl_git_pass(git_oid__fromstr(&expected_target_oid, list_expectations[*count].annotated_object_sha, GIT_OID_SHA1));
+ cl_assert_equal_oid(&expected_target_oid, annotated_obj_id);
+
+ (*count)++;
+
+ return 0;
+}
+
+struct note_create_payload {
+ const char *note_oid;
+ const char *object_oid;
+ unsigned seen;
+};
+
+static int note_list_create_cb(
+ const git_oid *blob_oid, const git_oid *annotated_obj_id, void *payload)
+{
+ git_oid expected_note_oid, expected_target_oid;
+ struct note_create_payload *notes = payload;
+ size_t i;
+
+ for (i = 0; notes[i].note_oid != NULL; i++) {
+ cl_git_pass(git_oid__fromstr(&expected_note_oid, notes[i].note_oid, GIT_OID_SHA1));
+
+ if (git_oid_cmp(&expected_note_oid, blob_oid) != 0)
+ continue;
+
+ cl_git_pass(git_oid__fromstr(&expected_target_oid, notes[i].object_oid, GIT_OID_SHA1));
+
+ if (git_oid_cmp(&expected_target_oid, annotated_obj_id) != 0)
+ continue;
+
+ notes[i].seen = 1;
+ return 0;
+ }
+
+ cl_fail("Did not see expected note");
+ return 0;
+}
+
+static void assert_notes_seen(struct note_create_payload payload[], size_t n)
+{
+ size_t seen = 0, i;
+
+ for (i = 0; payload[i].note_oid != NULL; i++) {
+ if (payload[i].seen)
+ seen++;
+ }
+
+ cl_assert_equal_i(seen, n);
+}
+
+void test_notes_notes__can_create_a_note(void)
+{
+ git_oid note_oid;
+ static struct note_create_payload can_create_a_note[] = {
+ { "1c9b1bc36730582a42d56eeee0dc58673d7ae869", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", 0 },
+ { NULL, NULL, 0 }
+ };
+
+ create_note(&note_oid, "refs/notes/i-can-see-dead-notes", can_create_a_note[0].object_oid, "I decorate 4a20\n");
+
+ cl_git_pass(git_note_foreach(_repo, "refs/notes/i-can-see-dead-notes", note_list_create_cb, &can_create_a_note));
+
+ assert_notes_seen(can_create_a_note, 1);
+}
+
+void test_notes_notes__can_create_a_note_from_commit(void)
+{
+ git_oid oid;
+ git_oid notes_commit_out;
+ git_reference *ref;
+ static struct note_create_payload can_create_a_note_from_commit[] = {
+ { "1c9b1bc36730582a42d56eeee0dc58673d7ae869", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", 0 },
+ { NULL, NULL, 0 }
+ };
+
+ cl_git_pass(git_oid__fromstr(&oid, can_create_a_note_from_commit[0].object_oid, GIT_OID_SHA1));
+
+ cl_git_pass(git_note_commit_create(&notes_commit_out, NULL, _repo, NULL, _sig, _sig, &oid, "I decorate 4a20\n", 1));
+
+ /* create_from_commit will not update any ref,
+ * so we must manually create the ref, that points to the commit */
+ cl_git_pass(git_reference_create(&ref, _repo, "refs/notes/i-can-see-dead-notes", &notes_commit_out, 0, NULL));
+
+ cl_git_pass(git_note_foreach(_repo, "refs/notes/i-can-see-dead-notes", note_list_create_cb, &can_create_a_note_from_commit));
+
+ assert_notes_seen(can_create_a_note_from_commit, 1);
+
+ git_reference_free(ref);
+}
+
+
+/* Test that we can create a note from a commit, given an existing commit */
+void test_notes_notes__can_create_a_note_from_commit_given_an_existing_commit(void)
+{
+ git_oid oid;
+ git_oid notes_commit_out;
+ git_commit *existing_notes_commit = NULL;
+ git_reference *ref;
+ static struct note_create_payload can_create_a_note_from_commit_given_an_existing_commit[] = {
+ { "1c9b1bc36730582a42d56eeee0dc58673d7ae869", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", 0 },
+ { "1aaf94147c21f981e0a20bf57b89137c5a6aae52", "9fd738e8f7967c078dceed8190330fc8648ee56a", 0 },
+ { NULL, NULL, 0 }
+ };
+
+ cl_git_pass(git_oid__fromstr(&oid, "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", GIT_OID_SHA1));
+
+ cl_git_pass(git_note_commit_create(&notes_commit_out, NULL, _repo, NULL, _sig, _sig, &oid, "I decorate 4a20\n", 0));
+
+ cl_git_pass(git_oid__fromstr(&oid, "9fd738e8f7967c078dceed8190330fc8648ee56a", GIT_OID_SHA1));
+
+ git_commit_lookup(&existing_notes_commit, _repo, &notes_commit_out);
+
+ cl_assert(existing_notes_commit);
+
+ cl_git_pass(git_note_commit_create(&notes_commit_out, NULL, _repo, existing_notes_commit, _sig, _sig, &oid, "I decorate 9fd7\n", 0));
+
+ /* create_from_commit will not update any ref,
+ * so we must manually create the ref, that points to the commit */
+ cl_git_pass(git_reference_create(&ref, _repo, "refs/notes/i-can-see-dead-notes", &notes_commit_out, 0, NULL));
+
+ cl_git_pass(git_note_foreach(_repo, "refs/notes/i-can-see-dead-notes", note_list_create_cb, &can_create_a_note_from_commit_given_an_existing_commit));
+
+ assert_notes_seen(can_create_a_note_from_commit_given_an_existing_commit, 2);
+
+ git_commit_free(existing_notes_commit);
+ git_reference_free(ref);
+}
+
+/*
+ * $ git notes --ref i-can-see-dead-notes add -m "I decorate a65f" a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+ * $ git notes --ref i-can-see-dead-notes add -m "I decorate c478" c47800c7266a2be04c571c04d5a6614691ea99bd
+ * $ git notes --ref i-can-see-dead-notes add -m "I decorate 9fd7 and 4a20" 9fd738e8f7967c078dceed8190330fc8648ee56a
+ * $ git notes --ref i-can-see-dead-notes add -m "I decorate 9fd7 and 4a20" 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
+ *
+ * $ git notes --ref i-can-see-dead-notes list
+ * 1c73b1f51762155d357bcd1fd4f2c409ef80065b 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
+ * 1c73b1f51762155d357bcd1fd4f2c409ef80065b 9fd738e8f7967c078dceed8190330fc8648ee56a
+ * 257b43746b6b46caa4aa788376c647cce0a33e2b a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+ * 1ec1c8e03f461f4f5d3f3702172483662e7223f3 c47800c7266a2be04c571c04d5a6614691ea99bd
+ *
+ * $ git ls-tree refs/notes/i-can-see-dead-notes
+ * 100644 blob 1c73b1f51762155d357bcd1fd4f2c409ef80065b 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
+ * 100644 blob 1c73b1f51762155d357bcd1fd4f2c409ef80065b 9fd738e8f7967c078dceed8190330fc8648ee56a
+ * 100644 blob 257b43746b6b46caa4aa788376c647cce0a33e2b a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+ * 100644 blob 1ec1c8e03f461f4f5d3f3702172483662e7223f3 c47800c7266a2be04c571c04d5a6614691ea99bd
+*/
+void test_notes_notes__can_retrieve_a_list_of_notes_for_a_given_namespace(void)
+{
+ git_oid note_oid1, note_oid2, note_oid3, note_oid4;
+ unsigned int retrieved_notes = 0;
+
+ create_note(&note_oid1, "refs/notes/i-can-see-dead-notes", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", "I decorate a65f\n");
+ create_note(&note_oid2, "refs/notes/i-can-see-dead-notes", "c47800c7266a2be04c571c04d5a6614691ea99bd", "I decorate c478\n");
+ create_note(&note_oid3, "refs/notes/i-can-see-dead-notes", "9fd738e8f7967c078dceed8190330fc8648ee56a", "I decorate 9fd7 and 4a20\n");
+ create_note(&note_oid4, "refs/notes/i-can-see-dead-notes", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", "I decorate 9fd7 and 4a20\n");
+
+ cl_git_pass(git_note_foreach
+(_repo, "refs/notes/i-can-see-dead-notes", note_list_cb, &retrieved_notes));
+
+ cl_assert_equal_i(4, retrieved_notes);
+}
+
+static int note_cancel_cb(
+ const git_oid *blob_id, const git_oid *annotated_obj_id, void *payload)
+{
+ unsigned int *count = (unsigned int *)payload;
+
+ GIT_UNUSED(blob_id);
+ GIT_UNUSED(annotated_obj_id);
+
+ (*count)++;
+
+ return (*count > 2);
+}
+
+void test_notes_notes__can_cancel_foreach(void)
+{
+ git_oid note_oid1, note_oid2, note_oid3, note_oid4;
+ unsigned int retrieved_notes = 0;
+
+ create_note(&note_oid1, "refs/notes/i-can-see-dead-notes", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", "I decorate a65f\n");
+ create_note(&note_oid2, "refs/notes/i-can-see-dead-notes", "c47800c7266a2be04c571c04d5a6614691ea99bd", "I decorate c478\n");
+ create_note(&note_oid3, "refs/notes/i-can-see-dead-notes", "9fd738e8f7967c078dceed8190330fc8648ee56a", "I decorate 9fd7 and 4a20\n");
+ create_note(&note_oid4, "refs/notes/i-can-see-dead-notes", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", "I decorate 9fd7 and 4a20\n");
+
+ cl_assert_equal_i(
+ 1,
+ git_note_foreach(_repo, "refs/notes/i-can-see-dead-notes",
+ note_cancel_cb, &retrieved_notes));
+}
+
+void test_notes_notes__retrieving_a_list_of_notes_for_an_unknown_namespace_returns_ENOTFOUND(void)
+{
+ int error;
+ unsigned int retrieved_notes = 0;
+
+ error = git_note_foreach(_repo, "refs/notes/i-am-not", note_list_cb, &retrieved_notes);
+ cl_git_fail(error);
+ cl_assert_equal_i(GIT_ENOTFOUND, error);
+
+ cl_assert_equal_i(0, retrieved_notes);
+}
+
+void test_notes_notes__inserting_a_note_without_passing_a_namespace_uses_the_default_namespace(void)
+{
+ git_oid note_oid, target_oid;
+ git_note *note, *default_namespace_note;
+ git_buf default_ref = GIT_BUF_INIT;
+
+ cl_git_pass(git_oid__fromstr(&target_oid, "08b041783f40edfe12bb406c9c9a8a040177c125", GIT_OID_SHA1));
+ cl_git_pass(git_note_default_ref(&default_ref, _repo));
+
+ create_note(&note_oid, NULL, "08b041783f40edfe12bb406c9c9a8a040177c125", "hello world\n");
+
+ cl_git_pass(git_note_read(&note, _repo, NULL, &target_oid));
+ cl_git_pass(git_note_read(&default_namespace_note, _repo, default_ref.ptr, &target_oid));
+
+ assert_note_equal(note, "hello world\n", &note_oid);
+ assert_note_equal(default_namespace_note, "hello world\n", &note_oid);
+
+ git_buf_dispose(&default_ref);
+ git_note_free(note);
+ git_note_free(default_namespace_note);
+}
+
+void test_notes_notes__can_insert_a_note_with_a_custom_namespace(void)
+{
+ git_oid note_oid, target_oid;
+ git_note *note;
+
+ cl_git_pass(git_oid__fromstr(&target_oid, "08b041783f40edfe12bb406c9c9a8a040177c125", GIT_OID_SHA1));
+
+ create_note(&note_oid, "refs/notes/some/namespace", "08b041783f40edfe12bb406c9c9a8a040177c125", "hello world on a custom namespace\n");
+
+ cl_git_pass(git_note_read(&note, _repo, "refs/notes/some/namespace", &target_oid));
+
+ assert_note_equal(note, "hello world on a custom namespace\n", &note_oid);
+
+ git_note_free(note);
+}
+
+/*
+ * $ git notes --ref fanout list 8496071c1b46c854b31185ea97743be6a8774479
+ * 08b041783f40edfe12bb406c9c9a8a040177c125
+ */
+void test_notes_notes__creating_a_note_on_a_target_which_already_has_one_returns_EEXISTS(void)
+{
+ int error;
+ git_oid note_oid, target_oid;
+
+ cl_git_pass(git_oid__fromstr(&target_oid, "08b041783f40edfe12bb406c9c9a8a040177c125", GIT_OID_SHA1));
+
+ create_note(&note_oid, NULL, "08b041783f40edfe12bb406c9c9a8a040177c125", "hello world\n");
+ error = git_note_create(&note_oid, _repo, NULL, _sig, _sig, &target_oid, "hello world\n", 0);
+ cl_git_fail(error);
+ cl_assert_equal_i(GIT_EEXISTS, error);
+
+ create_note(&note_oid, "refs/notes/some/namespace", "08b041783f40edfe12bb406c9c9a8a040177c125", "hello world\n");
+ error = git_note_create(&note_oid, _repo, "refs/notes/some/namespace", _sig, _sig, &target_oid, "hello world\n", 0);
+ cl_git_fail(error);
+ cl_assert_equal_i(GIT_EEXISTS, error);
+}
+
+
+void test_notes_notes__creating_a_note_on_a_target_can_overwrite_existing_note(void)
+{
+ git_oid note_oid, target_oid;
+ git_note *note, *namespace_note;
+
+ cl_git_pass(git_oid__fromstr(&target_oid, "08b041783f40edfe12bb406c9c9a8a040177c125", GIT_OID_SHA1));
+
+ create_note(&note_oid, NULL, "08b041783f40edfe12bb406c9c9a8a040177c125", "hello old world\n");
+ cl_git_pass(git_note_create(&note_oid, _repo, NULL, _sig, _sig, &target_oid, "hello new world\n", 1));
+
+ cl_git_pass(git_note_read(&note, _repo, NULL, &target_oid));
+ assert_note_equal(note, "hello new world\n", &note_oid);
+
+ create_note(&note_oid, "refs/notes/some/namespace", "08b041783f40edfe12bb406c9c9a8a040177c125", "hello old world\n");
+ cl_git_pass(git_note_create(&note_oid, _repo, "refs/notes/some/namespace", _sig, _sig, &target_oid, "hello new ref world\n", 1));
+
+ cl_git_pass(git_note_read(&namespace_note, _repo, "refs/notes/some/namespace", &target_oid));
+ assert_note_equal(namespace_note, "hello new ref world\n", &note_oid);
+
+ git_note_free(note);
+ git_note_free(namespace_note);
+}
+
+static char *messages[] = {
+ "08c041783f40edfe12bb406c9c9a8a040177c125",
+ "96c45fbe09ab7445fc7c60fd8d17f32494399343",
+ "48cc7e38dcfc1ec87e70ec03e08c3e83d7a16aa1",
+ "24c3eaafb681c3df668f9df96f58e7b8c756eb04",
+ "96ca1b6ccc7858ae94684777f85ac0e7447f7040",
+ "7ac2db4378a08bb244a427c357e0082ee0d57ac6",
+ "e6cba23dbf4ef84fe35e884f017f4e24dc228572",
+ "c8cf3462c7d8feba716deeb2ebe6583bd54589e2",
+ "39c16b9834c2d665ac5f68ad91dc5b933bad8549",
+ "f3c582b1397df6a664224ebbaf9d4cc952706597",
+ "29cec67037fe8e89977474988219016ae7f342a6",
+ "36c4cd238bf8e82e27b740e0741b025f2e8c79ab",
+ "f1c45a47c02e01d5a9a326f1d9f7f756373387f8",
+ "4aca84406f5daee34ab513a60717c8d7b1763ead",
+ "84ce167da452552f63ed8407b55d5ece4901845f",
+ NULL
+};
+
+#define MESSAGES_COUNT (sizeof(messages)/sizeof(messages[0])) - 1
+
+/* Test that we can read a note */
+void test_notes_notes__can_read_a_note(void)
+{
+ git_oid note_oid, target_oid;
+ git_note *note;
+
+ create_note(&note_oid, "refs/notes/i-can-see-dead-notes", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", "I decorate 4a20\n");
+
+ cl_git_pass(git_oid__fromstr(&target_oid, "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", GIT_OID_SHA1));
+
+ cl_git_pass(git_note_read(&note, _repo, "refs/notes/i-can-see-dead-notes", &target_oid));
+
+ cl_assert_equal_s(git_note_message(note), "I decorate 4a20\n");
+
+ git_note_free(note);
+}
+
+/* Test that we can read a note with from commit api */
+void test_notes_notes__can_read_a_note_from_a_commit(void)
+{
+ git_oid oid, notes_commit_oid;
+ git_commit *notes_commit;
+ git_note *note;
+
+ cl_git_pass(git_oid__fromstr(&oid, "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", GIT_OID_SHA1));
+ cl_git_pass(git_note_commit_create(&notes_commit_oid, NULL, _repo, NULL, _sig, _sig, &oid, "I decorate 4a20\n", 1));
+ cl_git_pass(git_commit_lookup(&notes_commit, _repo, &notes_commit_oid));
+ cl_assert(notes_commit);
+
+ cl_git_pass(git_note_commit_read(&note, _repo, notes_commit, &oid));
+ cl_assert_equal_s(git_note_message(note), "I decorate 4a20\n");
+
+ git_commit_free(notes_commit);
+ git_note_free(note);
+}
+
+/* Test that we can read a commit with no note fails */
+void test_notes_notes__attempt_to_read_a_note_from_a_commit_with_no_note_fails(void)
+{
+ git_oid oid, notes_commit_oid;
+ git_commit *notes_commit;
+ git_note *note;
+
+ cl_git_pass(git_oid__fromstr(&oid, "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", GIT_OID_SHA1));
+
+ cl_git_pass(git_note_commit_create(&notes_commit_oid, NULL, _repo, NULL, _sig, _sig, &oid, "I decorate 4a20\n", 1));
+
+ git_commit_lookup(&notes_commit, _repo, &notes_commit_oid);
+
+ cl_git_pass(git_note_commit_remove(&notes_commit_oid, _repo, notes_commit, _sig, _sig, &oid));
+ git_commit_free(notes_commit);
+
+ git_commit_lookup(&notes_commit, _repo, &notes_commit_oid);
+
+ cl_assert(notes_commit);
+
+ cl_git_fail_with(GIT_ENOTFOUND, git_note_commit_read(&note, _repo, notes_commit, &oid));
+
+ git_commit_free(notes_commit);
+}
+
+/*
+ * $ git ls-tree refs/notes/fanout
+ * 040000 tree 4b22b35d44b5a4f589edf3dc89196399771796ea 84
+ *
+ * $ git ls-tree 4b22b35
+ * 040000 tree d71aab4f9b04b45ce09bcaa636a9be6231474759 96
+ *
+ * $ git ls-tree d71aab4
+ * 100644 blob 08b041783f40edfe12bb406c9c9a8a040177c125 071c1b46c854b31185ea97743be6a8774479
+ */
+void test_notes_notes__can_insert_a_note_in_an_existing_fanout(void)
+{
+ size_t i;
+ git_oid note_oid, target_oid;
+ git_note *_note;
+
+ cl_git_pass(git_oid__fromstr(&target_oid, "08b041783f40edfe12bb406c9c9a8a040177c125", GIT_OID_SHA1));
+
+ for (i = 0; i < MESSAGES_COUNT; i++) {
+ cl_git_pass(git_note_create(&note_oid, _repo, "refs/notes/fanout", _sig, _sig, &target_oid, messages[i], 0));
+ cl_git_pass(git_note_read(&_note, _repo, "refs/notes/fanout", &target_oid));
+ git_note_free(_note);
+
+ git_oid_cpy(&target_oid, &note_oid);
+ }
+}
+
+/*
+ * $ git notes --ref fanout list 8496071c1b46c854b31185ea97743be6a8774479
+ * 08b041783f40edfe12bb406c9c9a8a040177c125
+ */
+void test_notes_notes__can_read_a_note_in_an_existing_fanout(void)
+{
+ git_oid note_oid, target_oid;
+ git_note *note;
+
+ cl_git_pass(git_oid__fromstr(&target_oid, "8496071c1b46c854b31185ea97743be6a8774479", GIT_OID_SHA1));
+ cl_git_pass(git_note_read(&note, _repo, "refs/notes/fanout", &target_oid));
+
+ cl_git_pass(git_oid__fromstr(&note_oid, "08b041783f40edfe12bb406c9c9a8a040177c125", GIT_OID_SHA1));
+ cl_assert_equal_oid(git_note_id(note), &note_oid);
+
+ git_note_free(note);
+}
+
+/* Can remove a note */
+void test_notes_notes__can_remove_a_note(void)
+{
+ git_oid note_oid, target_oid;
+ git_note *note;
+
+ create_note(&note_oid, "refs/notes/i-can-see-dead-notes", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", "I decorate 4a20\n");
+
+ cl_git_pass(git_oid__fromstr(&target_oid, "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", GIT_OID_SHA1));
+ cl_git_pass(git_note_remove(_repo, "refs/notes/i-can-see-dead-notes", _sig, _sig, &target_oid));
+
+ cl_git_fail(git_note_read(&note, _repo, "refs/notes/i-can-see-dead-notes", &target_oid));
+}
+
+/* Can remove a note from a commit */
+void test_notes_notes__can_remove_a_note_from_commit(void)
+{
+ git_oid oid, notes_commit_oid;
+ git_note *note = NULL;
+ git_commit *existing_notes_commit;
+ git_reference *ref;
+
+ cl_git_pass(git_oid__fromstr(&oid, "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", GIT_OID_SHA1));
+
+ cl_git_pass(git_note_commit_create(&notes_commit_oid, NULL, _repo, NULL, _sig, _sig, &oid, "I decorate 4a20\n", 0));
+
+ cl_git_pass(git_commit_lookup(&existing_notes_commit, _repo, &notes_commit_oid));
+
+ cl_assert(existing_notes_commit);
+
+ cl_git_pass(git_note_commit_remove(&notes_commit_oid, _repo, existing_notes_commit, _sig, _sig, &oid));
+
+ /* remove_from_commit will not update any ref,
+ * so we must manually create the ref, that points to the commit */
+ cl_git_pass(git_reference_create(&ref, _repo, "refs/notes/i-can-see-dead-notes", &notes_commit_oid, 0, NULL));
+
+ cl_git_fail(git_note_read(&note, _repo, "refs/notes/i-can-see-dead-notes", &oid));
+
+ git_commit_free(existing_notes_commit);
+ git_reference_free(ref);
+ git_note_free(note);
+}
+
+
+void test_notes_notes__can_remove_a_note_in_an_existing_fanout(void)
+{
+ git_oid target_oid;
+ git_note *note;
+
+ cl_git_pass(git_oid__fromstr(&target_oid, "8496071c1b46c854b31185ea97743be6a8774479", GIT_OID_SHA1));
+ cl_git_pass(git_note_remove(_repo, "refs/notes/fanout", _sig, _sig, &target_oid));
+
+ cl_git_fail(git_note_read(&note, _repo, "refs/notes/fanout", &target_oid));
+}
+
+void test_notes_notes__removing_a_note_which_doesnt_exists_returns_ENOTFOUND(void)
+{
+ int error;
+ git_oid target_oid;
+
+ cl_git_pass(git_oid__fromstr(&target_oid, "8496071c1b46c854b31185ea97743be6a8774479", GIT_OID_SHA1));
+ cl_git_pass(git_note_remove(_repo, "refs/notes/fanout", _sig, _sig, &target_oid));
+
+ error = git_note_remove(_repo, "refs/notes/fanout", _sig, _sig, &target_oid);
+ cl_git_fail(error);
+ cl_assert_equal_i(GIT_ENOTFOUND, error);
+}
+
+void test_notes_notes__can_iterate_default_namespace(void)
+{
+ git_note_iterator *iter;
+ git_note *note;
+ git_oid note_id, annotated_id;
+ git_oid note_created[2];
+ const char* note_message[] = {
+ "I decorate a65f\n",
+ "I decorate c478\n"
+ };
+ int i, err;
+
+ create_note(&note_created[0], "refs/notes/commits",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", note_message[0]);
+ create_note(&note_created[1], "refs/notes/commits",
+ "c47800c7266a2be04c571c04d5a6614691ea99bd", note_message[1]);
+
+ cl_git_pass(git_note_iterator_new(&iter, _repo, NULL));
+
+ for (i = 0; (err = git_note_next(&note_id, &annotated_id, iter)) >= 0; ++i) {
+ cl_git_pass(git_note_read(&note, _repo, NULL, &annotated_id));
+ cl_assert_equal_s(git_note_message(note), note_message[i]);
+ git_note_free(note);
+ }
+
+ cl_assert_equal_i(GIT_ITEROVER, err);
+ cl_assert_equal_i(2, i);
+ git_note_iterator_free(iter);
+}
+
+void test_notes_notes__can_iterate_custom_namespace(void)
+{
+ git_note_iterator *iter;
+ git_note *note;
+ git_oid note_id, annotated_id;
+ git_oid note_created[2];
+ const char* note_message[] = {
+ "I decorate a65f\n",
+ "I decorate c478\n"
+ };
+ int i, err;
+
+ create_note(&note_created[0], "refs/notes/beer",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", note_message[0]);
+ create_note(&note_created[1], "refs/notes/beer",
+ "c47800c7266a2be04c571c04d5a6614691ea99bd", note_message[1]);
+
+ cl_git_pass(git_note_iterator_new(&iter, _repo, "refs/notes/beer"));
+
+ for (i = 0; (err = git_note_next(&note_id, &annotated_id, iter)) >= 0; ++i) {
+ cl_git_pass(git_note_read(&note, _repo, "refs/notes/beer", &annotated_id));
+ cl_assert_equal_s(git_note_message(note), note_message[i]);
+ git_note_free(note);
+ }
+
+ cl_assert_equal_i(GIT_ITEROVER, err);
+ cl_assert_equal_i(2, i);
+ git_note_iterator_free(iter);
+}
+
+void test_notes_notes__empty_iterate(void)
+{
+ git_note_iterator *iter;
+
+ cl_git_fail(git_note_iterator_new(&iter, _repo, "refs/notes/commits"));
+}
+
+void test_notes_notes__iterate_from_commit(void)
+{
+ git_note_iterator *iter;
+ git_note *note;
+ git_oid note_id, annotated_id;
+ git_oid oids[2];
+ git_oid notes_commit_oids[2];
+ git_commit *notes_commits[2];
+ const char* note_message[] = {
+ "I decorate a65f\n",
+ "I decorate c478\n"
+ };
+ int i, err;
+
+ cl_git_pass(git_oid__fromstr(&(oids[0]), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&(oids[1]), "c47800c7266a2be04c571c04d5a6614691ea99bd", GIT_OID_SHA1));
+
+ cl_git_pass(git_note_commit_create(&notes_commit_oids[0], NULL, _repo, NULL, _sig, _sig, &(oids[0]), note_message[0], 0));
+
+ git_commit_lookup(&notes_commits[0], _repo, &notes_commit_oids[0]);
+ cl_assert(notes_commits[0]);
+
+ cl_git_pass(git_note_commit_create(&notes_commit_oids[1], NULL, _repo, notes_commits[0], _sig, _sig, &(oids[1]), note_message[1], 0));
+
+ git_commit_lookup(&notes_commits[1], _repo, &notes_commit_oids[1]);
+ cl_assert(notes_commits[1]);
+
+ cl_git_pass(git_note_commit_iterator_new(&iter, notes_commits[1]));
+
+ for (i = 0; (err = git_note_next(&note_id, &annotated_id, iter)) >= 0; ++i) {
+ cl_git_pass(git_note_commit_read(&note, _repo, notes_commits[1], &annotated_id));
+ cl_assert_equal_s(git_note_message(note), note_message[i]);
+ git_note_free(note);
+ }
+
+ cl_assert_equal_i(GIT_ITEROVER, err);
+ cl_assert_equal_i(2, i);
+
+ git_note_iterator_free(iter);
+ git_commit_free(notes_commits[0]);
+ git_commit_free(notes_commits[1]);
+}
diff --git a/tests/libgit2/notes/notesref.c b/tests/libgit2/notes/notesref.c
new file mode 100644
index 0000000..8696ff6
--- /dev/null
+++ b/tests/libgit2/notes/notesref.c
@@ -0,0 +1,67 @@
+#include "clar_libgit2.h"
+
+#include "notes.h"
+
+static git_repository *_repo;
+static git_note *_note;
+static git_signature *_sig;
+static git_config *_cfg;
+
+void test_notes_notesref__initialize(void)
+{
+ cl_fixture_sandbox("testrepo.git");
+ cl_git_pass(git_repository_open(&_repo, "testrepo.git"));
+}
+
+void test_notes_notesref__cleanup(void)
+{
+ git_note_free(_note);
+ _note = NULL;
+
+ git_signature_free(_sig);
+ _sig = NULL;
+
+ git_config_free(_cfg);
+ _cfg = NULL;
+
+ git_repository_free(_repo);
+ _repo = NULL;
+
+ cl_fixture_cleanup("testrepo.git");
+}
+
+void test_notes_notesref__config_corenotesref(void)
+{
+ git_oid oid, note_oid;
+ git_buf default_ref = GIT_BUF_INIT;
+
+ cl_git_pass(git_signature_now(&_sig, "alice", "alice@example.com"));
+ cl_git_pass(git_oid__fromstr(&oid, "8496071c1b46c854b31185ea97743be6a8774479", GIT_OID_SHA1));
+
+ cl_git_pass(git_repository_config(&_cfg, _repo));
+
+ cl_git_pass(git_config_set_string(_cfg, "core.notesRef", "refs/notes/mydefaultnotesref"));
+
+ cl_git_pass(git_note_create(&note_oid, _repo, NULL, _sig, _sig, &oid, "test123test\n", 0));
+
+ cl_git_pass(git_note_read(&_note, _repo, NULL, &oid));
+ cl_assert_equal_s("test123test\n", git_note_message(_note));
+ cl_assert_equal_oid(git_note_id(_note), &note_oid);
+
+ git_note_free(_note);
+
+ cl_git_pass(git_note_read(&_note, _repo, "refs/notes/mydefaultnotesref", &oid));
+ cl_assert_equal_s("test123test\n", git_note_message(_note));
+ cl_assert_equal_oid(git_note_id(_note), &note_oid);
+
+ cl_git_pass(git_note_default_ref(&default_ref, _repo));
+ cl_assert_equal_s("refs/notes/mydefaultnotesref", default_ref.ptr);
+ git_buf_dispose(&default_ref);
+
+ cl_git_pass(git_config_delete_entry(_cfg, "core.notesRef"));
+
+ cl_git_pass(git_note_default_ref(&default_ref, _repo));
+ cl_assert_equal_s(GIT_NOTES_DEFAULT_REF, default_ref.ptr);
+
+ git_buf_dispose(&default_ref);
+}
diff --git a/tests/libgit2/object/blob/filter.c b/tests/libgit2/object/blob/filter.c
new file mode 100644
index 0000000..00b553e
--- /dev/null
+++ b/tests/libgit2/object/blob/filter.c
@@ -0,0 +1,149 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "blob.h"
+
+static git_repository *g_repo = NULL;
+
+#define CRLF_NUM_TEST_OBJECTS 9
+
+static const char *g_crlf_raw[CRLF_NUM_TEST_OBJECTS] = {
+ "",
+ "foo\nbar\n",
+ "foo\rbar\r",
+ "foo\r\nbar\r\n",
+ "foo\nbar\rboth\r\nreversed\n\ragain\nproblems\r",
+ "123\n\000\001\002\003\004abc\255\254\253\r\n",
+ "\xEF\xBB\xBFThis is UTF-8\n",
+ "\xEF\xBB\xBF\xE3\x81\xBB\xE3\x81\x92\xE3\x81\xBB\xE3\x81\x92\r\n\xE3\x81\xBB\xE3\x81\x92\xE3\x81\xBB\xE3\x81\x92\r\n",
+ "\xFE\xFF\x00T\x00h\x00i\x00s\x00!"
+};
+
+static off64_t g_crlf_raw_len[CRLF_NUM_TEST_OBJECTS] = {
+ -1, -1, -1, -1, -1, 17, -1, -1, 12
+};
+
+static git_oid g_crlf_oids[CRLF_NUM_TEST_OBJECTS];
+
+static git_str g_crlf_filtered[CRLF_NUM_TEST_OBJECTS] = {
+ { "", 0, 0 },
+ { "foo\nbar\n", 0, 8 },
+ { "foo\rbar\r", 0, 8 },
+ { "foo\nbar\n", 0, 8 },
+ { "foo\nbar\rboth\nreversed\n\ragain\nproblems\r", 0, 38 },
+ { "123\n\000\001\002\003\004abc\255\254\253\n", 0, 16 },
+ { "\xEF\xBB\xBFThis is UTF-8\n", 0, 17 },
+ { "\xEF\xBB\xBF\xE3\x81\xBB\xE3\x81\x92\xE3\x81\xBB\xE3\x81\x92\n\xE3\x81\xBB\xE3\x81\x92\xE3\x81\xBB\xE3\x81\x92\n", 0, 29 },
+ { "\xFE\xFF\x00T\x00h\x00i\x00s\x00!", 0, 12 }
+};
+
+static git_str_text_stats g_crlf_filtered_stats[CRLF_NUM_TEST_OBJECTS] = {
+ { 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 2, 0, 6, 0 },
+ { 0, 0, 2, 0, 0, 6, 0 },
+ { 0, 0, 2, 2, 2, 6, 0 },
+ { 0, 0, 4, 4, 1, 31, 0 },
+ { 0, 1, 1, 2, 1, 9, 5 },
+ { GIT_STR_BOM_UTF8, 0, 0, 1, 0, 16, 0 },
+ { GIT_STR_BOM_UTF8, 0, 2, 2, 2, 27, 0 },
+ { GIT_STR_BOM_UTF16_BE, 5, 0, 0, 0, 7, 5 },
+};
+
+void test_object_blob_filter__initialize(void)
+{
+ int i;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ for (i = 0; i < CRLF_NUM_TEST_OBJECTS; i++) {
+ if (g_crlf_raw_len[i] < 0)
+ g_crlf_raw_len[i] = strlen(g_crlf_raw[i]);
+
+ cl_git_pass(git_blob_create_from_buffer(
+ &g_crlf_oids[i], g_repo, g_crlf_raw[i], (size_t)g_crlf_raw_len[i]));
+ }
+}
+
+void test_object_blob_filter__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_object_blob_filter__unfiltered(void)
+{
+ int i;
+ git_blob *blob;
+
+ for (i = 0; i < CRLF_NUM_TEST_OBJECTS; i++) {
+ size_t raw_len = (size_t)g_crlf_raw_len[i];
+
+ cl_git_pass(git_blob_lookup(&blob, g_repo, &g_crlf_oids[i]));
+
+ cl_assert_equal_sz(raw_len, (size_t)git_blob_rawsize(blob));
+ cl_assert_equal_i(
+ 0, memcmp(g_crlf_raw[i], git_blob_rawcontent(blob), raw_len));
+
+ git_blob_free(blob);
+ }
+}
+
+void test_object_blob_filter__stats(void)
+{
+ int i;
+ git_blob *blob;
+ git_str buf = GIT_STR_INIT;
+ git_str_text_stats stats;
+
+ for (i = 0; i < CRLF_NUM_TEST_OBJECTS; i++) {
+ cl_git_pass(git_blob_lookup(&blob, g_repo, &g_crlf_oids[i]));
+ cl_git_pass(git_blob__getbuf(&buf, blob));
+ git_str_gather_text_stats(&stats, &buf, false);
+ cl_assert_equal_i(
+ 0, memcmp(&g_crlf_filtered_stats[i], &stats, sizeof(stats)));
+ git_blob_free(blob);
+ }
+
+ git_str_dispose(&buf);
+}
+
+void test_object_blob_filter__to_odb(void)
+{
+ git_filter_list *fl = NULL;
+ git_config *cfg;
+ int i;
+ git_blob *blob;
+ git_buf out = GIT_BUF_INIT, zeroed;
+
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_assert(cfg);
+
+ git_attr_cache_flush(g_repo);
+ cl_git_append2file("empty_standard_repo/.gitattributes", "*.txt text\n");
+
+ cl_git_pass(git_filter_list_load(
+ &fl, g_repo, NULL, "filename.txt", GIT_FILTER_TO_ODB, 0));
+ cl_assert(fl != NULL);
+
+ for (i = 0; i < CRLF_NUM_TEST_OBJECTS; i++) {
+ cl_git_pass(git_blob_lookup(&blob, g_repo, &g_crlf_oids[i]));
+
+ /* try once with allocated blob */
+ cl_git_pass(git_filter_list_apply_to_blob(&out, fl, blob));
+ cl_assert_equal_sz(g_crlf_filtered[i].size, out.size);
+ cl_assert_equal_i(
+ 0, memcmp(out.ptr, g_crlf_filtered[i].ptr, out.size));
+
+ /* try again with zeroed blob */
+ memset(&zeroed, 0, sizeof(zeroed));
+ cl_git_pass(git_filter_list_apply_to_blob(&zeroed, fl, blob));
+ cl_assert_equal_sz(g_crlf_filtered[i].size, zeroed.size);
+ cl_assert_equal_i(
+ 0, memcmp(zeroed.ptr, g_crlf_filtered[i].ptr, zeroed.size));
+ git_buf_dispose(&zeroed);
+
+ git_blob_free(blob);
+ }
+
+ git_filter_list_free(fl);
+ git_buf_dispose(&out);
+ git_config_free(cfg);
+}
diff --git a/tests/libgit2/object/blob/fromstream.c b/tests/libgit2/object/blob/fromstream.c
new file mode 100644
index 0000000..dad0b52
--- /dev/null
+++ b/tests/libgit2/object/blob/fromstream.c
@@ -0,0 +1,86 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "path.h"
+#include "futils.h"
+
+static git_repository *repo;
+static char textual_content[] = "libgit2\n\r\n\0";
+
+void test_object_blob_fromstream__initialize(void)
+{
+ repo = cl_git_sandbox_init("testrepo.git");
+}
+
+void test_object_blob_fromstream__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_object_blob_fromstream__multiple_write(void)
+{
+ git_oid expected_id, id;
+ git_object *blob;
+ git_writestream *stream;
+ int i, howmany = 6;
+
+ cl_git_pass(git_oid__fromstr(&expected_id, "321cbdf08803c744082332332838df6bd160f8f9", GIT_OID_SHA1));
+
+ cl_git_fail_with(GIT_ENOTFOUND,
+ git_object_lookup(&blob, repo, &expected_id, GIT_OBJECT_ANY));
+
+ cl_git_pass(git_blob_create_from_stream(&stream, repo, NULL));
+
+ for (i = 0; i < howmany; i++)
+ cl_git_pass(stream->write(stream, textual_content, strlen(textual_content)));
+
+ cl_git_pass(git_blob_create_from_stream_commit(&id, stream));
+ cl_assert_equal_oid(&expected_id, &id);
+
+ cl_git_pass(git_object_lookup(&blob, repo, &expected_id, GIT_OBJECT_BLOB));
+
+ git_object_free(blob);
+}
+
+#define GITATTR "* text=auto\n" \
+ "*.txt text\n" \
+ "*.data binary\n"
+
+static void write_attributes(git_repository *repo)
+{
+ git_str buf = GIT_STR_INIT;
+
+ cl_git_pass(git_str_joinpath(&buf, git_repository_path(repo), "info"));
+ cl_git_pass(git_str_joinpath(&buf, git_str_cstr(&buf), "attributes"));
+
+ cl_git_pass(git_futils_mkpath2file(git_str_cstr(&buf), 0777));
+ cl_git_rewritefile(git_str_cstr(&buf), GITATTR);
+
+ git_str_dispose(&buf);
+}
+
+static void assert_named_chunked_blob(const char *expected_sha, const char *fake_name)
+{
+ git_oid expected_id, id;
+ git_writestream *stream;
+ int i, howmany = 6;
+
+ cl_git_pass(git_oid__fromstr(&expected_id, expected_sha, GIT_OID_SHA1));
+
+ cl_git_pass(git_blob_create_from_stream(&stream, repo, fake_name));
+
+ for (i = 0; i < howmany; i++)
+ cl_git_pass(stream->write(stream, textual_content, strlen(textual_content)));
+
+ cl_git_pass(git_blob_create_from_stream_commit(&id, stream));
+
+ cl_assert_equal_oid(&expected_id, &id);
+}
+
+void test_object_blob_fromstream__creating_a_blob_from_chunks_honors_the_attributes_directives(void)
+{
+ write_attributes(repo);
+
+ assert_named_chunked_blob("321cbdf08803c744082332332838df6bd160f8f9", "dummy.data");
+ assert_named_chunked_blob("e9671e138a780833cb689753570fd10a55be84fb", "dummy.txt");
+ assert_named_chunked_blob("e9671e138a780833cb689753570fd10a55be84fb", "dummy.dunno");
+}
diff --git a/tests/libgit2/object/blob/write.c b/tests/libgit2/object/blob/write.c
new file mode 100644
index 0000000..422258d
--- /dev/null
+++ b/tests/libgit2/object/blob/write.c
@@ -0,0 +1,68 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "path.h"
+#include "futils.h"
+
+static git_repository *repo;
+
+#define WORKDIR "empty_standard_repo"
+#define BARE_REPO "testrepo.git"
+#define ELSEWHERE "elsewhere"
+
+typedef int (*blob_creator_fn)(
+ git_oid *,
+ git_repository *,
+ const char *);
+
+void test_object_blob_write__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static void assert_blob_creation(const char *path_to_file, const char *blob_from_path, blob_creator_fn creator)
+{
+ git_oid oid;
+ cl_git_mkfile(path_to_file, "1..2...3... Can you hear me?\n");
+
+ cl_must_pass(creator(&oid, repo, blob_from_path));
+ cl_assert(git_oid_streq(&oid, "da5e4f20c91c81b44a7e298f3d3fb3fe2f178e32") == 0);
+}
+
+void test_object_blob_write__can_create_a_blob_in_a_standard_repo_from_a_file_located_in_the_working_directory(void)
+{
+ repo = cl_git_sandbox_init(WORKDIR);
+
+ assert_blob_creation(WORKDIR "/test.txt", "test.txt", &git_blob_create_from_workdir);
+}
+
+void test_object_blob_write__can_create_a_blob_in_a_standard_repo_from_a_absolute_filepath_pointing_outside_of_the_working_directory(void)
+{
+ git_str full_path = GIT_STR_INIT;
+
+ repo = cl_git_sandbox_init(WORKDIR);
+
+ cl_must_pass(p_mkdir(ELSEWHERE, 0777));
+ cl_must_pass(git_fs_path_prettify_dir(&full_path, ELSEWHERE, NULL));
+ cl_must_pass(git_str_puts(&full_path, "test.txt"));
+
+ assert_blob_creation(ELSEWHERE "/test.txt", git_str_cstr(&full_path), &git_blob_create_from_disk);
+
+ git_str_dispose(&full_path);
+ cl_must_pass(git_futils_rmdir_r(ELSEWHERE, NULL, GIT_RMDIR_REMOVE_FILES));
+}
+
+void test_object_blob_write__can_create_a_blob_in_a_bare_repo_from_a_absolute_filepath(void)
+{
+ git_str full_path = GIT_STR_INIT;
+
+ repo = cl_git_sandbox_init(BARE_REPO);
+
+ cl_must_pass(p_mkdir(ELSEWHERE, 0777));
+ cl_must_pass(git_fs_path_prettify_dir(&full_path, ELSEWHERE, NULL));
+ cl_must_pass(git_str_puts(&full_path, "test.txt"));
+
+ assert_blob_creation(ELSEWHERE "/test.txt", git_str_cstr(&full_path), &git_blob_create_from_disk);
+
+ git_str_dispose(&full_path);
+ cl_must_pass(git_futils_rmdir_r(ELSEWHERE, NULL, GIT_RMDIR_REMOVE_FILES));
+}
diff --git a/tests/libgit2/object/cache.c b/tests/libgit2/object/cache.c
new file mode 100644
index 0000000..bf8c6fb
--- /dev/null
+++ b/tests/libgit2/object/cache.c
@@ -0,0 +1,276 @@
+#include "clar_libgit2.h"
+#include "repository.h"
+
+static git_repository *g_repo;
+static size_t cache_limit;
+static int object_type;
+
+void test_object_cache__initialize_cache_no_blobs(void)
+{
+ g_repo = NULL;
+ object_type = GIT_OBJECT_BLOB;
+ cache_limit = 0;
+}
+
+void test_object_cache__initialize_cache_tiny_blobs(void)
+{
+ g_repo = NULL;
+ object_type = GIT_OBJECT_BLOB;
+ cache_limit = 10;
+}
+
+void test_object_cache__initialize_cache_all_blobs(void)
+{
+ g_repo = NULL;
+ object_type = GIT_OBJECT_BLOB;
+ cache_limit = 32767;
+}
+
+void test_object_cache__initialize_cache_no_trees(void)
+{
+ g_repo = NULL;
+ object_type = GIT_OBJECT_TREE;
+ cache_limit = 0;
+}
+
+void test_object_cache__cleanup(void)
+{
+ git_repository_free(g_repo);
+ g_repo = NULL;
+
+ git_libgit2_opts(GIT_OPT_SET_CACHE_OBJECT_LIMIT, (int)GIT_OBJECT_BLOB, (size_t)0);
+ git_libgit2_opts(GIT_OPT_SET_CACHE_OBJECT_LIMIT, (int)GIT_OBJECT_TREE, (size_t)4096);
+ git_libgit2_opts(GIT_OPT_SET_CACHE_OBJECT_LIMIT, (int)GIT_OBJECT_COMMIT, (size_t)4096);
+}
+
+static struct {
+ git_object_t type;
+ const char *sha;
+ size_t size;
+} g_data[] = {
+ /* HEAD */
+ { GIT_OBJECT_BLOB, "a8233120f6ad708f843d861ce2b7228ec4e3dec6", 10 }, /* README */
+ { GIT_OBJECT_BLOB, "3697d64be941a53d4ae8f6a271e4e3fa56b022cc", 8 }, /* branch_file.txt */
+ { GIT_OBJECT_BLOB, "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd", 12 }, /* new.txt */
+
+ /* refs/heads/subtrees */
+ { GIT_OBJECT_BLOB, "1385f264afb75a56a5bec74243be9b367ba4ca08", 4 }, /* README */
+ { GIT_OBJECT_TREE, "f1425cef211cc08caa31e7b545ffb232acb098c3", 90 }, /* ab */
+ { GIT_OBJECT_BLOB, "d6c93164c249c8000205dd4ec5cbca1b516d487f", 6 }, /* ab/4.txt */
+ { GIT_OBJECT_TREE, "9a03079b8a8ee85a0bee58bf9be3da8b62414ed4", 33 }, /* ab/c */
+ { GIT_OBJECT_BLOB, "270b8ea76056d5cad83af921837702d3e3c2924d", 6 }, /* ab/c/3.txt */
+ { GIT_OBJECT_TREE, "b6361fc6a97178d8fc8639fdeed71c775ab52593", 63 }, /* ab/de */
+ { GIT_OBJECT_BLOB, "e7b4ad382349ff96dd8199000580b9b1e2042eb0", 6 }, /* ab/de/2.txt */
+ { GIT_OBJECT_TREE, "3259a6bd5b57fb9c1281bb7ed3167b50f224cb54", 33 }, /* ab/de/fgh */
+ { GIT_OBJECT_BLOB, "1f67fc4386b2d171e0d21be1c447e12660561f9b", 6 }, /* ab/de/fgh/1.txt */
+ { GIT_OBJECT_BLOB, "45b983be36b73c0788dc9cbcb76cbb80fc7bb057", 3 }, /* branch_file.txt */
+ { GIT_OBJECT_BLOB, "fa49b077972391ad58037050f2a75f74e3671e92", 9 }, /* new.txt */
+
+ /* refs/heads/chomped */
+ { GIT_OBJECT_BLOB, "0266163a49e280c4f5ed1e08facd36a2bd716bcf", 51 }, /* readme.txt */
+
+ { 0, NULL, 0 },
+ { 0, NULL, 0 }
+};
+
+void test_object_cache__cache_counts(void)
+{
+ int i, start, nonmatching = 0;
+ git_oid oid;
+ git_odb_object *odb_obj;
+ git_object *obj;
+ git_odb *odb;
+
+ git_libgit2_opts(GIT_OPT_SET_CACHE_OBJECT_LIMIT, object_type, cache_limit);
+
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
+ cl_git_pass(git_repository_odb(&odb, g_repo));
+
+ start = (int)git_cache_size(&g_repo->objects);
+
+ for (i = 0; g_data[i].sha != NULL; ++i) {
+ int count = (int)git_cache_size(&g_repo->objects);
+
+ cl_git_pass(git_oid__fromstr(&oid, g_data[i].sha, GIT_OID_SHA1));
+
+ /* alternate between loading raw and parsed objects */
+ if ((i & 1) == 0) {
+ cl_git_pass(git_odb_read(&odb_obj, odb, &oid));
+ cl_assert(g_data[i].type == git_odb_object_type(odb_obj));
+ git_odb_object_free(odb_obj);
+ } else {
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
+ cl_assert(g_data[i].type == git_object_type(obj));
+ git_object_free(obj);
+ }
+
+ if ((g_data[i].type == object_type && g_data[i].size >= cache_limit) ||
+ (g_data[i].type != object_type && g_data[i].type == GIT_OBJECT_BLOB))
+ cl_assert_equal_i(count, (int)git_cache_size(&g_repo->objects));
+ else {
+ cl_assert_equal_i(count + 1, (int)git_cache_size(&g_repo->objects));
+ nonmatching++;
+ }
+ }
+
+ cl_assert_equal_i(nonmatching, (int)git_cache_size(&g_repo->objects) - start);
+
+ for (i = 0; g_data[i].sha != NULL; ++i) {
+ int count = (int)git_cache_size(&g_repo->objects);
+
+ cl_git_pass(git_oid__fromstr(&oid, g_data[i].sha, GIT_OID_SHA1));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
+ cl_assert(g_data[i].type == git_object_type(obj));
+ git_object_free(obj);
+
+ cl_assert_equal_i(count, (int)git_cache_size(&g_repo->objects));
+ }
+
+ git_odb_free(odb);
+}
+
+static void *cache_parsed(void *arg)
+{
+ int i;
+ git_oid oid;
+ git_object *obj;
+
+ for (i = ((int *)arg)[1]; g_data[i].sha != NULL; i += 2) {
+ cl_git_pass(git_oid__fromstr(&oid, g_data[i].sha, GIT_OID_SHA1));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
+ cl_assert(g_data[i].type == git_object_type(obj));
+ git_object_free(obj);
+ }
+
+ for (i = 0; i < ((int *)arg)[1]; i += 2) {
+ cl_git_pass(git_oid__fromstr(&oid, g_data[i].sha, GIT_OID_SHA1));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
+ cl_assert(g_data[i].type == git_object_type(obj));
+ git_object_free(obj);
+ }
+
+ return arg;
+}
+
+static void *cache_raw(void *arg)
+{
+ int i;
+ git_oid oid;
+ git_odb *odb;
+ git_odb_object *odb_obj;
+
+ cl_git_pass(git_repository_odb(&odb, g_repo));
+
+ for (i = ((int *)arg)[1]; g_data[i].sha != NULL; i += 2) {
+ cl_git_pass(git_oid__fromstr(&oid, g_data[i].sha, GIT_OID_SHA1));
+ cl_git_pass(git_odb_read(&odb_obj, odb, &oid));
+ cl_assert(g_data[i].type == git_odb_object_type(odb_obj));
+ git_odb_object_free(odb_obj);
+ }
+
+ for (i = 0; i < ((int *)arg)[1]; i += 2) {
+ cl_git_pass(git_oid__fromstr(&oid, g_data[i].sha, GIT_OID_SHA1));
+ cl_git_pass(git_odb_read(&odb_obj, odb, &oid));
+ cl_assert(g_data[i].type == git_odb_object_type(odb_obj));
+ git_odb_object_free(odb_obj);
+ }
+
+ git_odb_free(odb);
+
+ return arg;
+}
+
+#define REPEAT 20
+#define THREADCOUNT 50
+
+void test_object_cache__threadmania(void)
+{
+ int try, th, max_i;
+ void *data;
+ void *(*fn)(void *);
+
+#ifdef GIT_THREADS
+ git_thread t[THREADCOUNT];
+#endif
+
+ for (max_i = 0; g_data[max_i].sha != NULL; ++max_i)
+ /* count up */;
+
+ for (try = 0; try < REPEAT; ++try) {
+
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
+
+ for (th = 0; th < THREADCOUNT; ++th) {
+ data = git__malloc(2 * sizeof(int));
+
+ ((int *)data)[0] = th;
+ ((int *)data)[1] = th % max_i;
+
+ fn = (th & 1) ? cache_parsed : cache_raw;
+
+#ifdef GIT_THREADS
+ cl_git_pass(git_thread_create(&t[th], fn, data));
+#else
+ cl_assert(fn(data) == data);
+ git__free(data);
+#endif
+ }
+
+#ifdef GIT_THREADS
+ for (th = 0; th < THREADCOUNT; ++th) {
+ cl_git_pass(git_thread_join(&t[th], &data));
+ cl_assert_equal_i(th, ((int *)data)[0]);
+ git__free(data);
+ }
+#endif
+
+ git_repository_free(g_repo);
+ g_repo = NULL;
+ }
+}
+
+static void *cache_quick(void *arg)
+{
+ git_oid oid;
+ git_object *obj;
+
+ cl_git_pass(git_oid__fromstr(&oid, g_data[4].sha, GIT_OID_SHA1));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
+ cl_assert(g_data[4].type == git_object_type(obj));
+ git_object_free(obj);
+
+ return arg;
+}
+
+void test_object_cache__fast_thread_rush(void)
+{
+ int try, th, data[THREADCOUNT];
+#ifdef GIT_THREADS
+ git_thread t[THREADCOUNT];
+#endif
+
+ for (try = 0; try < REPEAT; ++try) {
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
+
+ for (th = 0; th < THREADCOUNT; ++th) {
+ data[th] = th;
+#ifdef GIT_THREADS
+ cl_git_pass(
+ git_thread_create(&t[th], cache_quick, &data[th]));
+#else
+ cl_assert(cache_quick(&data[th]) == &data[th]);
+#endif
+ }
+
+#ifdef GIT_THREADS
+ for (th = 0; th < THREADCOUNT; ++th) {
+ void *rval;
+ cl_git_pass(git_thread_join(&t[th], &rval));
+ cl_assert_equal_i(th, *((int *)rval));
+ }
+#endif
+
+ git_repository_free(g_repo);
+ g_repo = NULL;
+ }
+}
diff --git a/tests/libgit2/object/commit/commitstagedfile.c b/tests/libgit2/object/commit/commitstagedfile.c
new file mode 100644
index 0000000..61f50b2
--- /dev/null
+++ b/tests/libgit2/object/commit/commitstagedfile.c
@@ -0,0 +1,218 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+
+static git_repository *repo;
+
+void test_object_commit_commitstagedfile__initialize(void)
+{
+ cl_fixture("treebuilder");
+ cl_git_pass(git_repository_init(&repo, "treebuilder/", 0));
+ cl_assert(repo != NULL);
+}
+
+void test_object_commit_commitstagedfile__cleanup(void)
+{
+ git_repository_free(repo);
+ repo = NULL;
+
+ cl_fixture_cleanup("treebuilder");
+}
+
+void test_object_commit_commitstagedfile__generate_predictable_object_ids(void)
+{
+ git_index *index;
+ const git_index_entry *entry;
+ git_oid expected_blob_oid, tree_oid, expected_tree_oid, commit_oid, expected_commit_oid;
+ git_signature *signature;
+ git_tree *tree;
+ git_buf buffer = GIT_BUF_INIT;
+
+ /*
+ * The test below replicates the following git scenario
+ *
+ * $ echo "test" > test.txt
+ * $ git hash-object test.txt
+ * 9daeafb9864cf43055ae93beb0afd6c7d144bfa4
+ *
+ * $ git add .
+ * $ git commit -m "Initial commit"
+ *
+ * $ git log
+ * commit 1fe3126578fc4eca68c193e4a3a0a14a0704624d
+ * Author: nulltoken <emeric.fermas@gmail.com>
+ * Date: Wed Dec 14 08:29:03 2011 +0100
+ *
+ * Initial commit
+ *
+ * $ git show 1fe3 --format=raw
+ * commit 1fe3126578fc4eca68c193e4a3a0a14a0704624d
+ * tree 2b297e643c551e76cfa1f93810c50811382f9117
+ * author nulltoken <emeric.fermas@gmail.com> 1323847743 +0100
+ * committer nulltoken <emeric.fermas@gmail.com> 1323847743 +0100
+ *
+ * Initial commit
+ *
+ * diff --git a/test.txt b/test.txt
+ * new file mode 100644
+ * index 0000000..9daeafb
+ * --- /dev/null
+ * +++ b/test.txt
+ * @@ -0,0 +1 @@
+ * +test
+ *
+ * $ git ls-tree 2b297
+ * 100644 blob 9daeafb9864cf43055ae93beb0afd6c7d144bfa4 test.txt
+ */
+
+ cl_git_pass(git_oid__fromstr(&expected_commit_oid, "1fe3126578fc4eca68c193e4a3a0a14a0704624d", GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&expected_tree_oid, "2b297e643c551e76cfa1f93810c50811382f9117", GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&expected_blob_oid, "9daeafb9864cf43055ae93beb0afd6c7d144bfa4", GIT_OID_SHA1));
+
+ /*
+ * Add a new file to the index
+ */
+ cl_git_mkfile("treebuilder/test.txt", "test\n");
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_add_bypath(index, "test.txt"));
+
+ entry = git_index_get_byindex(index, 0);
+
+ cl_assert(git_oid_cmp(&expected_blob_oid, &entry->id) == 0);
+
+ /*
+ * Information about index entry should match test file
+ */
+ {
+ struct stat st;
+ cl_must_pass(p_lstat("treebuilder/test.txt", &st));
+ cl_assert(entry->file_size == st.st_size);
+#ifndef _WIN32
+ /*
+ * Windows doesn't populate these fields, and the signage is
+ * wrong in the Windows version of the struct, so lets avoid
+ * the "comparing signed and unsigned" compilation warning in
+ * that case.
+ */
+ cl_assert(entry->uid == st.st_uid);
+ cl_assert(entry->gid == st.st_gid);
+#endif
+ }
+
+ /*
+ * Build the tree from the index
+ */
+ cl_git_pass(git_index_write_tree(&tree_oid, index));
+
+ cl_assert(git_oid_cmp(&expected_tree_oid, &tree_oid) == 0);
+
+ /*
+ * Commit the staged file
+ */
+ cl_git_pass(git_signature_new(&signature, "nulltoken", "emeric.fermas@gmail.com", 1323847743, 60));
+ cl_git_pass(git_tree_lookup(&tree, repo, &tree_oid));
+
+ cl_git_pass(git_message_prettify(&buffer, "Initial commit", 0, '#'));
+
+ cl_git_pass(git_commit_create_v(
+ &commit_oid,
+ repo,
+ "HEAD",
+ signature,
+ signature,
+ NULL,
+ buffer.ptr,
+ tree,
+ 0));
+
+ cl_assert(git_oid_cmp(&expected_commit_oid, &commit_oid) == 0);
+
+ git_buf_dispose(&buffer);
+ git_signature_free(signature);
+ git_tree_free(tree);
+ git_index_free(index);
+}
+
+static void assert_commit_tree_has_n_entries(git_commit *c, int count)
+{
+ git_tree *tree;
+ cl_git_pass(git_commit_tree(&tree, c));
+ cl_assert_equal_i(count, git_tree_entrycount(tree));
+ git_tree_free(tree);
+}
+
+static void assert_commit_is_head_(git_commit *c, const char *file, const char *func, int line)
+{
+ git_commit *head;
+ cl_git_pass(git_revparse_single((git_object **)&head, repo, "HEAD"));
+ clar__assert(git_oid_equal(git_commit_id(c), git_commit_id(head)), file, func, line, "Commit is not the HEAD", NULL, 1);
+ git_commit_free(head);
+}
+#define assert_commit_is_head(C) assert_commit_is_head_((C),__FILE__,__func__,__LINE__)
+
+void test_object_commit_commitstagedfile__amend_commit(void)
+{
+ git_index *index;
+ git_oid old_oid, new_oid, tree_oid;
+ git_commit *old_commit, *new_commit;
+ git_tree *tree;
+
+ /* make a commit */
+
+ cl_git_mkfile("treebuilder/myfile", "This is a file\n");
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_add_bypath(index, "myfile"));
+ cl_repo_commit_from_index(&old_oid, repo, NULL, 0, "first commit");
+
+ cl_git_pass(git_commit_lookup(&old_commit, repo, &old_oid));
+
+ cl_assert_equal_i(0, git_commit_parentcount(old_commit));
+ assert_commit_tree_has_n_entries(old_commit, 1);
+ assert_commit_is_head(old_commit);
+
+ /* let's amend the message of the HEAD commit */
+
+ cl_git_pass(git_commit_amend(
+ &new_oid, old_commit, "HEAD", NULL, NULL, NULL, "Initial commit", NULL));
+
+ /* fail because the commit isn't the tip of the branch anymore */
+ cl_git_fail(git_commit_amend(
+ &new_oid, old_commit, "HEAD", NULL, NULL, NULL, "Initial commit", NULL));
+
+ cl_git_pass(git_commit_lookup(&new_commit, repo, &new_oid));
+
+ cl_assert_equal_i(0, git_commit_parentcount(new_commit));
+ assert_commit_tree_has_n_entries(new_commit, 1);
+ assert_commit_is_head(new_commit);
+
+ git_commit_free(old_commit);
+
+ old_commit = new_commit;
+
+ /* let's amend the tree of that last commit */
+
+ cl_git_mkfile("treebuilder/anotherfile", "This is another file\n");
+ cl_git_pass(git_index_add_bypath(index, "anotherfile"));
+ cl_git_pass(git_index_write_tree(&tree_oid, index));
+ cl_git_pass(git_tree_lookup(&tree, repo, &tree_oid));
+ cl_assert_equal_i(2, git_tree_entrycount(tree));
+
+ /* fail to amend on a ref which does not exist */
+ cl_git_fail_with(GIT_ENOTFOUND, git_commit_amend(
+ &new_oid, old_commit, "refs/heads/nope", NULL, NULL, NULL, "Initial commit", tree));
+
+ cl_git_pass(git_commit_amend(
+ &new_oid, old_commit, "HEAD", NULL, NULL, NULL, "Initial commit", tree));
+ git_tree_free(tree);
+
+ cl_git_pass(git_commit_lookup(&new_commit, repo, &new_oid));
+
+ cl_assert_equal_i(0, git_commit_parentcount(new_commit));
+ assert_commit_tree_has_n_entries(new_commit, 2);
+ assert_commit_is_head(new_commit);
+
+ /* cleanup */
+
+ git_commit_free(old_commit);
+ git_commit_free(new_commit);
+ git_index_free(index);
+}
diff --git a/tests/libgit2/object/commit/parse.c b/tests/libgit2/object/commit/parse.c
new file mode 100644
index 0000000..6f9a655
--- /dev/null
+++ b/tests/libgit2/object/commit/parse.c
@@ -0,0 +1,476 @@
+#include "clar_libgit2.h"
+#include "commit.h"
+#include "object.h"
+#include "signature.h"
+
+static void assert_commit_parses(
+ const char *data,
+ size_t datalen,
+ git_oid_t oid_type,
+ const char *expected_treeid,
+ const char *expected_author,
+ const char *expected_committer,
+ const char *expected_encoding,
+ const char *expected_message,
+ size_t expected_parents)
+{
+ git_commit *commit;
+ if (!datalen)
+ datalen = strlen(data);
+ cl_git_pass(git_object__from_raw((git_object **) &commit, data, datalen, GIT_OBJECT_COMMIT, oid_type));
+
+ if (expected_author) {
+ git_signature *author;
+ cl_git_pass(git_signature_from_buffer(&author, expected_author));
+ cl_assert(git_signature__equal(author, commit->author));
+ cl_assert_equal_s(author->name, commit->author->name);
+ cl_assert_equal_s(author->email, commit->author->email);
+ cl_assert_equal_i(author->when.time, commit->author->when.time);
+ cl_assert_equal_i(author->when.offset, commit->author->when.offset);
+ cl_assert_equal_i(author->when.sign, commit->author->when.sign);
+ git_signature_free(author);
+ }
+
+ if (expected_committer) {
+ git_signature *committer;
+ cl_git_pass(git_signature_from_buffer(&committer, expected_committer));
+ cl_assert_equal_s(committer->name, commit->committer->name);
+ cl_assert_equal_s(committer->email, commit->committer->email);
+ cl_assert_equal_i(committer->when.time, commit->committer->when.time);
+ cl_assert_equal_i(committer->when.offset, commit->committer->when.offset);
+ cl_assert_equal_i(committer->when.sign, commit->committer->when.sign);
+ git_signature_free(committer);
+ }
+
+ if (expected_encoding)
+ cl_assert_equal_s(commit->message_encoding, expected_encoding);
+ else
+ cl_assert_equal_p(commit->message_encoding, NULL);
+
+ if (expected_message)
+ cl_assert_equal_s(commit->raw_message, expected_message);
+ else
+ cl_assert_equal_p(commit->message_encoding, NULL);
+
+ if (expected_treeid) {
+ git_oid tree_oid;
+ cl_git_pass(git_oid__fromstr(&tree_oid, expected_treeid, oid_type));
+ cl_assert_equal_oid(&tree_oid, &commit->tree_id);
+ }
+
+ cl_assert_equal_i(commit->parent_ids.size, expected_parents);
+
+ git_object__free(&commit->object);
+}
+
+static void assert_commit_fails(
+ const char *data,
+ size_t datalen,
+ git_oid_t oid_type)
+{
+ git_object *object;
+ if (!datalen)
+ datalen = strlen(data);
+ cl_git_fail(git_object__from_raw(&object, data, datalen, GIT_OBJECT_COMMIT, oid_type));
+}
+
+void test_object_commit_parse__sha1_parsing_commit_succeeds(void)
+{
+ const char *commit =
+ "tree 3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8\n"
+ "author Author <author@example.com>\n"
+ "committer Committer <committer@example.com>\n"
+ "encoding Encoding\n"
+ "\n"
+ "Message";
+ assert_commit_parses(commit, 0, GIT_OID_SHA1,
+ "3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8",
+ "Author <author@example.com>",
+ "Committer <committer@example.com>",
+ "Encoding",
+ "Message", 0);
+}
+
+void test_object_commit_parse__sha1_parsing_commit_without_encoding_succeeds(void)
+{
+ const char *commit =
+ "tree 3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8\n"
+ "author Author <author@example.com>\n"
+ "committer Committer <committer@example.com>\n"
+ "\n"
+ "Message";
+ assert_commit_parses(commit, 0, GIT_OID_SHA1,
+ "3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8",
+ "Author <author@example.com>",
+ "Committer <committer@example.com>",
+ NULL,
+ "Message", 0);
+}
+
+void test_object_commit_parse__sha1_parsing_commit_with_multiple_authors_succeeds(void)
+{
+ const char *commit =
+ "tree 3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8\n"
+ "author Author1 <author@example.com>\n"
+ "author Author2 <author@example.com>\n"
+ "author Author3 <author@example.com>\n"
+ "author Author4 <author@example.com>\n"
+ "committer Committer <committer@example.com>\n"
+ "\n"
+ "Message";
+ assert_commit_parses(commit, 0, GIT_OID_SHA1,
+ "3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8",
+ "Author1 <author@example.com>",
+ "Committer <committer@example.com>",
+ NULL,
+ "Message", 0);
+}
+
+void test_object_commit_parse__sha1_parsing_commit_with_multiple_committers_succeeds(void)
+{
+ const char *commit =
+ "tree 3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8\n"
+ "author Author <author@example.com>\n"
+ "committer Committer1 <committer@example.com>\n"
+ "committer Committer2 <committer@example.com>\n"
+ "committer Committer3 <committer@example.com>\n"
+ "committer Committer4 <committer@example.com>\n"
+ "\n"
+ "Message";
+ assert_commit_parses(commit, 0, GIT_OID_SHA1,
+ "3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8",
+ "Author <author@example.com>",
+ "Committer1 <committer@example.com>",
+ NULL,
+ "Message", 0);
+}
+
+void test_object_commit_parse__sha1_parsing_commit_without_message_succeeds(void)
+{
+ const char *commit =
+ "tree 3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8\n"
+ "author Author <author@example.com>\n"
+ "committer Committer <committer@example.com>\n";
+ assert_commit_parses(commit, 0, GIT_OID_SHA1,
+ "3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8",
+ "Author <author@example.com>",
+ "Committer <committer@example.com>",
+ NULL,
+ "", 0);
+}
+
+void test_object_commit_parse__sha1_parsing_commit_with_unknown_fields_succeeds(void)
+{
+ const char *commit =
+ "tree 3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8\n"
+ "author Author <author@example.com>\n"
+ "committer Committer <committer@example.com>\n"
+ "foo bar\n"
+ "more garbage\n"
+ "\n"
+ "Message";
+ assert_commit_parses(commit, 0, GIT_OID_SHA1,
+ "3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8",
+ "Author <author@example.com>",
+ "Committer <committer@example.com>",
+ NULL,
+ "Message", 0);
+}
+
+void test_object_commit_parse__sha1_parsing_commit_with_invalid_tree_fails(void)
+{
+ const char *commit =
+ "tree 3e7ac388cadacccdf1xxx5f3445895b71d9cb0f8\n"
+ "author Author <author@example.com>\n"
+ "committer Committer <committer@example.com>\n"
+ "\n"
+ "Message";
+ assert_commit_fails(commit, 0, GIT_OID_SHA1);
+}
+
+void test_object_commit_parse__sha1_parsing_commit_with_sha256_tree_fails(void)
+{
+ const char *commit =
+ "tree f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736\n"
+ "author Author <author@example.com>\n"
+ "committer Committer <committer@example.com>\n"
+ "\n"
+ "Message";
+ assert_commit_fails(commit, 0, GIT_OID_SHA1);
+}
+
+void test_object_commit_parse__sha1_parsing_commit_without_tree_fails(void)
+{
+ const char *commit =
+ "author Author <author@example.com>\n"
+ "committer Committer <committer@example.com>\n"
+ "\n"
+ "Message";
+ assert_commit_fails(commit, 0, GIT_OID_SHA1);
+}
+
+void test_object_commit_parse__sha1_parsing_commit_without_author_fails(void)
+{
+ const char *commit =
+ "tree 3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8\n"
+ "committer Committer <committer@example.com>\n"
+ "\n"
+ "Message";
+ assert_commit_fails(commit, 0, GIT_OID_SHA1);
+}
+
+void test_object_commit_parse__sha1_parsing_commit_without_committer_fails(void)
+{
+ const char *commit =
+ "tree 3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8\n"
+ "author Author <author@example.com>\n"
+ "\n"
+ "Message";
+ assert_commit_fails(commit, 0, GIT_OID_SHA1);
+}
+
+void test_object_commit_parse__sha1_parsing_encoding_will_not_cause_oob_read(void)
+{
+ const char *commit =
+ "tree 3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8\n"
+ "author <>\n"
+ "committer <>\n"
+ "encoding foo\n";
+ /*
+ * As we ignore unknown fields, the cut-off encoding field will be
+ * parsed just fine.
+ */
+ assert_commit_parses(
+ commit, strlen(commit) - strlen("ncoding foo\n"),
+ GIT_OID_SHA1,
+ "3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8",
+ "<>",
+ "<>",
+ NULL,
+ "", 0);
+}
+
+
+void test_object_commit_parse__sha256_parsing_commit_succeeds(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *commit =
+ "tree f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736\n"
+ "author Author <author@example.com>\n"
+ "committer Committer <committer@example.com>\n"
+ "encoding Encoding\n"
+ "\n"
+ "Message";
+ assert_commit_parses(commit, 0, GIT_OID_SHA256,
+ "f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736",
+ "Author <author@example.com>",
+ "Committer <committer@example.com>",
+ "Encoding",
+ "Message", 0);
+#endif
+}
+
+void test_object_commit_parse__sha256_parsing_commit_without_encoding_succeeds(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *commit =
+ "tree f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736\n"
+ "author Author <author@example.com>\n"
+ "committer Committer <committer@example.com>\n"
+ "\n"
+ "Message";
+ assert_commit_parses(commit, 0, GIT_OID_SHA256,
+ "f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736",
+ "Author <author@example.com>",
+ "Committer <committer@example.com>",
+ NULL,
+ "Message", 0);
+#endif
+}
+
+void test_object_commit_parse__sha256_parsing_commit_with_multiple_authors_succeeds(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *commit =
+ "tree f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736\n"
+ "author Author1 <author@example.com>\n"
+ "author Author2 <author@example.com>\n"
+ "author Author3 <author@example.com>\n"
+ "author Author4 <author@example.com>\n"
+ "committer Committer <committer@example.com>\n"
+ "\n"
+ "Message";
+ assert_commit_parses(commit, 0, GIT_OID_SHA256,
+ "f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736",
+ "Author1 <author@example.com>",
+ "Committer <committer@example.com>",
+ NULL,
+ "Message", 0);
+#endif
+}
+
+void test_object_commit_parse__sha256_parsing_commit_with_multiple_committers_succeeds(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *commit =
+ "tree f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736\n"
+ "author Author <author@example.com>\n"
+ "committer Committer1 <committer@example.com>\n"
+ "committer Committer2 <committer@example.com>\n"
+ "committer Committer3 <committer@example.com>\n"
+ "committer Committer4 <committer@example.com>\n"
+ "\n"
+ "Message";
+ assert_commit_parses(commit, 0, GIT_OID_SHA256,
+ "f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736",
+ "Author <author@example.com>",
+ "Committer1 <committer@example.com>",
+ NULL,
+ "Message", 0);
+#endif
+}
+
+void test_object_commit_parse__sha256_parsing_commit_without_message_succeeds(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *commit =
+ "tree f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736\n"
+ "author Author <author@example.com>\n"
+ "committer Committer <committer@example.com>\n";
+ assert_commit_parses(commit, 0, GIT_OID_SHA256,
+ "f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736",
+ "Author <author@example.com>",
+ "Committer <committer@example.com>",
+ NULL,
+ "", 0);
+#endif
+}
+
+void test_object_commit_parse__sha256_parsing_commit_with_unknown_fields_succeeds(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *commit =
+ "tree f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736\n"
+ "author Author <author@example.com>\n"
+ "committer Committer <committer@example.com>\n"
+ "foo bar\n"
+ "more garbage\n"
+ "\n"
+ "Message";
+ assert_commit_parses(commit, 0, GIT_OID_SHA256,
+ "f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736",
+ "Author <author@example.com>",
+ "Committer <committer@example.com>",
+ NULL,
+ "Message", 0);
+#endif
+}
+
+void test_object_commit_parse__sha256_parsing_commit_with_invalid_tree_fails(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *commit =
+ "tree f2a108f86a3b4fd9adxxxd55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736\n"
+ "author Author <author@example.com>\n"
+ "committer Committer <committer@example.com>\n"
+ "\n"
+ "Message";
+ assert_commit_fails(commit, 0, GIT_OID_SHA256);
+#endif
+}
+
+void test_object_commit_parse__sha256_parsing_commit_with_sha1_tree_fails(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *commit =
+ "tree 3e7ac388cadacccdf1c6c5f3445895b71d9cb0f8\n"
+ "author Author <author@example.com>\n"
+ "committer Committer <committer@example.com>\n"
+ "\n"
+ "Message";
+ assert_commit_fails(commit, 0, GIT_OID_SHA256);
+#endif
+}
+
+void test_object_commit_parse__sha256_parsing_commit_without_tree_fails(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *commit =
+ "author Author <author@example.com>\n"
+ "committer Committer <committer@example.com>\n"
+ "\n"
+ "Message";
+ assert_commit_fails(commit, 0, GIT_OID_SHA256);
+#endif
+}
+
+void test_object_commit_parse__sha256_parsing_commit_without_author_fails(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *commit =
+ "tree f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736\n"
+ "committer Committer <committer@example.com>\n"
+ "\n"
+ "Message";
+ assert_commit_fails(commit, 0, GIT_OID_SHA256);
+#endif
+}
+
+void test_object_commit_parse__sha256_parsing_commit_without_committer_fails(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *commit =
+ "tree f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736\n"
+ "author Author <author@example.com>\n"
+ "\n"
+ "Message";
+ assert_commit_fails(commit, 0, GIT_OID_SHA256);
+#endif
+}
+
+void test_object_commit_parse__sha256_parsing_encoding_will_not_cause_oob_read(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *commit =
+ "tree f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736\n"
+ "author <>\n"
+ "committer <>\n"
+ "encoding foo\n";
+ /*
+ * As we ignore unknown fields, the cut-off encoding field will be
+ * parsed just fine.
+ */
+ assert_commit_parses(
+ commit, strlen(commit) - strlen("ncoding foo\n"),
+ GIT_OID_SHA256,
+ "f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736",
+ "<>",
+ "<>",
+ NULL,
+ "", 0);
+#endif
+}
diff --git a/tests/libgit2/object/lookup.c b/tests/libgit2/object/lookup.c
new file mode 100644
index 0000000..7ef1dbd
--- /dev/null
+++ b/tests/libgit2/object/lookup.c
@@ -0,0 +1,122 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+
+static git_repository *g_repo;
+
+void test_object_lookup__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo.git");
+}
+
+void test_object_lookup__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_object_lookup__lookup_wrong_type_returns_enotfound(void)
+{
+ const char *commit = "e90810b8df3e80c413d903f631643c716887138d";
+ git_oid oid;
+ git_object *object;
+
+ cl_git_pass(git_oid__fromstr(&oid, commit, GIT_OID_SHA1));
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_object_lookup(&object, g_repo, &oid, GIT_OBJECT_TAG));
+}
+
+void test_object_lookup__lookup_nonexisting_returns_enotfound(void)
+{
+ const char *unknown = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
+ git_oid oid;
+ git_object *object;
+
+ cl_git_pass(git_oid__fromstr(&oid, unknown, GIT_OID_SHA1));
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_object_lookup(&object, g_repo, &oid, GIT_OBJECT_ANY));
+}
+
+void test_object_lookup__lookup_wrong_type_by_abbreviated_id_returns_enotfound(void)
+{
+ const char *commit = "e90810b";
+ git_oid oid;
+ git_object *object;
+
+ cl_git_pass(git_oid__fromstrn(&oid, commit, strlen(commit), GIT_OID_SHA1));
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_object_lookup_prefix(&object, g_repo, &oid, strlen(commit), GIT_OBJECT_TAG));
+}
+
+void test_object_lookup__lookup_wrong_type_eventually_returns_enotfound(void)
+{
+ const char *commit = "e90810b8df3e80c413d903f631643c716887138d";
+ git_oid oid;
+ git_object *object;
+
+ cl_git_pass(git_oid__fromstr(&oid, commit, GIT_OID_SHA1));
+
+ cl_git_pass(git_object_lookup(&object, g_repo, &oid, GIT_OBJECT_COMMIT));
+ git_object_free(object);
+
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_object_lookup(&object, g_repo, &oid, GIT_OBJECT_TAG));
+}
+
+void test_object_lookup__lookup_corrupt_object_returns_error(void)
+{
+ const char *commit = "8e73b769e97678d684b809b163bebdae2911720f",
+ *file = "objects/8e/73b769e97678d684b809b163bebdae2911720f";
+ git_str path = GIT_STR_INIT, contents = GIT_STR_INIT;
+ git_oid oid;
+ git_object *object;
+ size_t i;
+
+ cl_git_pass(git_oid__fromstr(&oid, commit, GIT_OID_SHA1));
+ cl_git_pass(git_str_joinpath(&path, git_repository_path(g_repo), file));
+ cl_git_pass(git_futils_readbuffer(&contents, path.ptr));
+
+ /* Corrupt and try to read the object */
+ for (i = 0; i < contents.size; i++) {
+ contents.ptr[i] ^= 0x1;
+ cl_git_pass(git_futils_writebuffer(&contents, path.ptr, O_RDWR, 0644));
+ cl_git_fail(git_object_lookup(&object, g_repo, &oid, GIT_OBJECT_COMMIT));
+ contents.ptr[i] ^= 0x1;
+ }
+
+ /* Restore original content and assert we can read the object */
+ cl_git_pass(git_futils_writebuffer(&contents, path.ptr, O_RDWR, 0644));
+ cl_git_pass(git_object_lookup(&object, g_repo, &oid, GIT_OBJECT_COMMIT));
+
+ git_object_free(object);
+ git_str_dispose(&path);
+ git_str_dispose(&contents);
+}
+
+void test_object_lookup__lookup_object_with_wrong_hash_returns_error(void)
+{
+ const char *oldloose = "objects/8e/73b769e97678d684b809b163bebdae2911720f",
+ *newloose = "objects/8e/73b769e97678d684b809b163bebdae2911720e",
+ *commit = "8e73b769e97678d684b809b163bebdae2911720e";
+ git_str oldpath = GIT_STR_INIT, newpath = GIT_STR_INIT;
+ git_object *object;
+ git_oid oid;
+
+ cl_git_pass(git_oid__fromstr(&oid, commit, GIT_OID_SHA1));
+
+ /* Copy object to another location with wrong hash */
+ cl_git_pass(git_str_joinpath(&oldpath, git_repository_path(g_repo), oldloose));
+ cl_git_pass(git_str_joinpath(&newpath, git_repository_path(g_repo), newloose));
+ cl_git_pass(git_futils_cp(oldpath.ptr, newpath.ptr, 0644));
+
+ /* Verify that lookup fails due to a hashsum mismatch */
+ cl_git_fail_with(GIT_EMISMATCH, git_object_lookup(&object, g_repo, &oid, GIT_OBJECT_COMMIT));
+
+ /* Disable verification and try again */
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 0));
+ cl_git_pass(git_object_lookup(&object, g_repo, &oid, GIT_OBJECT_COMMIT));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 1));
+
+ git_object_free(object);
+ git_str_dispose(&oldpath);
+ git_str_dispose(&newpath);
+}
diff --git a/tests/libgit2/object/lookup256.c b/tests/libgit2/object/lookup256.c
new file mode 100644
index 0000000..3e1dab6
--- /dev/null
+++ b/tests/libgit2/object/lookup256.c
@@ -0,0 +1,153 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+static git_repository *g_repo;
+#endif
+
+void test_object_lookup256__initialize(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ g_repo = cl_git_sandbox_init("testrepo_256.git");
+#endif
+}
+
+void test_object_lookup256__cleanup(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_sandbox_cleanup();
+#endif
+}
+
+void test_object_lookup256__lookup_wrong_type_returns_enotfound(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *commit = "4d46d9719e425ef2dfb5bfba098d0b62e21b2b92d0731892eef70db0870e3744";
+ git_oid oid;
+ git_object *object;
+
+ cl_git_pass(git_oid__fromstr(&oid, commit, GIT_OID_SHA256));
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_object_lookup(&object, g_repo, &oid, GIT_OBJECT_TAG));
+#endif
+}
+
+void test_object_lookup256__lookup_nonexisting_returns_enotfound(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *unknown = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
+ git_oid oid;
+ git_object *object;
+
+ cl_git_pass(git_oid__fromstr(&oid, unknown, GIT_OID_SHA256));
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_object_lookup(&object, g_repo, &oid, GIT_OBJECT_ANY));
+#endif
+}
+
+void test_object_lookup256__lookup_wrong_type_by_abbreviated_id_returns_enotfound(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *commit = "4d46d97";
+ git_oid oid;
+ git_object *object;
+
+ cl_git_pass(git_oid__fromstrn(&oid, commit, strlen(commit), GIT_OID_SHA256));
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_object_lookup_prefix(&object, g_repo, &oid, strlen(commit), GIT_OBJECT_TAG));
+#endif
+}
+
+void test_object_lookup256__lookup_wrong_type_eventually_returns_enotfound(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *commit = "4d46d9719e425ef2dfb5bfba098d0b62e21b2b92d0731892eef70db0870e3744";
+ git_oid oid;
+ git_object *object;
+
+ cl_git_pass(git_oid__fromstr(&oid, commit, GIT_OID_SHA256));
+
+ cl_git_pass(git_object_lookup(&object, g_repo, &oid, GIT_OBJECT_COMMIT));
+ git_object_free(object);
+
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_object_lookup(&object, g_repo, &oid, GIT_OBJECT_TAG));
+#endif
+}
+
+void test_object_lookup256__lookup_corrupt_object_returns_error(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *commit = "5ca8959deb2b8327458e0344523eb1ddeeef4bce03e35864640b452f84d26848",
+ *file = "objects/5c/a8959deb2b8327458e0344523eb1ddeeef4bce03e35864640b452f84d26848";
+ git_str path = GIT_STR_INIT, contents = GIT_STR_INIT;
+ git_oid oid;
+ git_object *object;
+ size_t i;
+
+ cl_git_pass(git_oid__fromstr(&oid, commit, GIT_OID_SHA256));
+ cl_git_pass(git_str_joinpath(&path, git_repository_path(g_repo), file));
+ cl_git_pass(git_futils_readbuffer(&contents, path.ptr));
+
+ /* Corrupt and try to read the object */
+ for (i = 0; i < contents.size; i++) {
+ contents.ptr[i] ^= 0x1;
+ cl_git_pass(git_futils_writebuffer(&contents, path.ptr, O_RDWR, 0644));
+ cl_git_fail(git_object_lookup(&object, g_repo, &oid, GIT_OBJECT_COMMIT));
+ contents.ptr[i] ^= 0x1;
+ }
+
+ /* Restore original content and assert we can read the object */
+ cl_git_pass(git_futils_writebuffer(&contents, path.ptr, O_RDWR, 0644));
+ cl_git_pass(git_object_lookup(&object, g_repo, &oid, GIT_OBJECT_COMMIT));
+
+ git_object_free(object);
+ git_str_dispose(&path);
+ git_str_dispose(&contents);
+#endif
+}
+
+void test_object_lookup256__lookup_object_with_wrong_hash_returns_error(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ const char *oldloose = "objects/5c/a8959deb2b8327458e0344523eb1ddeeef4bce03e35864640b452f84d26848",
+ *newloose = "objects/5c/a8959deb2b8327458e0344523eb1ddeeef4bce03e35864640b452f84d26840",
+ *commit = "5ca8959deb2b8327458e0344523eb1ddeeef4bce03e35864640b452f84d26840";
+
+ git_str oldpath = GIT_STR_INIT, newpath = GIT_STR_INIT;
+ git_object *object;
+ git_oid oid;
+
+ cl_git_pass(git_oid__fromstr(&oid, commit, GIT_OID_SHA256));
+
+ /* Copy object to another location with wrong hash */
+ cl_git_pass(git_str_joinpath(&oldpath, git_repository_path(g_repo), oldloose));
+ cl_git_pass(git_str_joinpath(&newpath, git_repository_path(g_repo), newloose));
+ cl_git_pass(git_futils_cp(oldpath.ptr, newpath.ptr, 0644));
+
+ /* Verify that lookup fails due to a hashsum mismatch */
+ cl_git_fail_with(GIT_EMISMATCH, git_object_lookup(&object, g_repo, &oid, GIT_OBJECT_COMMIT));
+
+ /* Disable verification and try again */
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 0));
+ cl_git_pass(git_object_lookup(&object, g_repo, &oid, GIT_OBJECT_COMMIT));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 1));
+
+ git_object_free(object);
+ git_str_dispose(&oldpath);
+ git_str_dispose(&newpath);
+#endif
+}
diff --git a/tests/libgit2/object/lookupbypath.c b/tests/libgit2/object/lookupbypath.c
new file mode 100644
index 0000000..f2257f5
--- /dev/null
+++ b/tests/libgit2/object/lookupbypath.c
@@ -0,0 +1,83 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+
+static git_repository *g_repo;
+static git_tree *g_root_tree;
+static git_commit *g_head_commit;
+static git_object *g_expectedobject,
+ *g_actualobject;
+
+void test_object_lookupbypath__initialize(void)
+{
+ git_reference *head;
+ git_tree_entry *tree_entry;
+
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("attr/.gitted")));
+
+ cl_git_pass(git_repository_head(&head, g_repo));
+ cl_git_pass(git_reference_peel((git_object**)&g_head_commit, head, GIT_OBJECT_COMMIT));
+ cl_git_pass(git_commit_tree(&g_root_tree, g_head_commit));
+ cl_git_pass(git_tree_entry_bypath(&tree_entry, g_root_tree, "subdir/subdir_test2.txt"));
+ cl_git_pass(git_object_lookup(&g_expectedobject, g_repo, git_tree_entry_id(tree_entry),
+ GIT_OBJECT_ANY));
+
+ git_tree_entry_free(tree_entry);
+ git_reference_free(head);
+
+ g_actualobject = NULL;
+}
+void test_object_lookupbypath__cleanup(void)
+{
+ git_object_free(g_actualobject);
+ git_object_free(g_expectedobject);
+ git_tree_free(g_root_tree);
+ git_commit_free(g_head_commit);
+ g_expectedobject = NULL;
+ git_repository_free(g_repo);
+ g_repo = NULL;
+}
+
+void test_object_lookupbypath__errors(void)
+{
+ cl_assert_equal_i(GIT_EINVALIDSPEC,
+ git_object_lookup_bypath(&g_actualobject, (git_object*)g_root_tree,
+ "subdir/subdir_test2.txt", GIT_OBJECT_TREE)); /* It's not a tree */
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_object_lookup_bypath(&g_actualobject, (git_object*)g_root_tree,
+ "file/doesnt/exist", GIT_OBJECT_ANY));
+}
+
+void test_object_lookupbypath__from_root_tree(void)
+{
+ cl_git_pass(git_object_lookup_bypath(&g_actualobject, (git_object*)g_root_tree,
+ "subdir/subdir_test2.txt", GIT_OBJECT_BLOB));
+ cl_assert_equal_oid(git_object_id(g_expectedobject),
+ git_object_id(g_actualobject));
+}
+
+void test_object_lookupbypath__from_head_commit(void)
+{
+ cl_git_pass(git_object_lookup_bypath(&g_actualobject, (git_object*)g_head_commit,
+ "subdir/subdir_test2.txt", GIT_OBJECT_BLOB));
+ cl_assert_equal_oid(git_object_id(g_expectedobject),
+ git_object_id(g_actualobject));
+}
+
+void test_object_lookupbypath__from_subdir_tree(void)
+{
+ git_tree_entry *entry = NULL;
+ git_tree *tree = NULL;
+
+ cl_git_pass(git_tree_entry_bypath(&entry, g_root_tree, "subdir"));
+ cl_git_pass(git_tree_lookup(&tree, g_repo, git_tree_entry_id(entry)));
+
+ cl_git_pass(git_object_lookup_bypath(&g_actualobject, (git_object*)tree,
+ "subdir_test2.txt", GIT_OBJECT_BLOB));
+ cl_assert_equal_oid(git_object_id(g_expectedobject),
+ git_object_id(g_actualobject));
+
+ git_tree_entry_free(entry);
+ git_tree_free(tree);
+}
+
diff --git a/tests/libgit2/object/message.c b/tests/libgit2/object/message.c
new file mode 100644
index 0000000..d87c8ef
--- /dev/null
+++ b/tests/libgit2/object/message.c
@@ -0,0 +1,197 @@
+#include "clar_libgit2.h"
+
+static void assert_message_prettifying(char *expected_output, char *input, int strip_comments)
+{
+ git_buf prettified_message = GIT_BUF_INIT;
+
+ git_message_prettify(&prettified_message, input, strip_comments, '#');
+ cl_assert_equal_s(expected_output, prettified_message.ptr);
+
+ git_buf_dispose(&prettified_message);
+}
+
+#define t40 "A quick brown fox jumps over the lazy do"
+#define s40 " "
+#define sss s40 s40 s40 s40 s40 s40 s40 s40 s40 s40 /* # 400 */
+#define ttt t40 t40 t40 t40 t40 t40 t40 t40 t40 t40 /* # 400 */
+
+/* Ported from git.git */
+/* see https://github.com/git/git/blob/master/t/t0030-stripspace.sh */
+void test_object_message__long_lines_without_spaces_should_be_unchanged(void)
+{
+ assert_message_prettifying(ttt "\n", ttt, 0);
+ assert_message_prettifying(ttt ttt "\n", ttt ttt, 0);
+ assert_message_prettifying(ttt ttt ttt "\n", ttt ttt ttt, 0);
+ assert_message_prettifying(ttt ttt ttt ttt "\n", ttt ttt ttt ttt, 0);
+}
+
+void test_object_message__lines_with_spaces_at_the_beginning_should_be_unchanged(void)
+{
+ assert_message_prettifying(sss ttt "\n", sss ttt, 0);
+ assert_message_prettifying(sss sss ttt "\n", sss sss ttt, 0);
+ assert_message_prettifying(sss sss sss ttt "\n", sss sss sss ttt, 0);
+}
+
+void test_object_message__lines_with_intermediate_spaces_should_be_unchanged(void)
+{
+ assert_message_prettifying(ttt sss ttt "\n", ttt sss ttt, 0);
+ assert_message_prettifying(ttt sss sss ttt "\n", ttt sss sss ttt, 0);
+}
+
+void test_object_message__consecutive_blank_lines_should_be_unified(void)
+{
+ assert_message_prettifying(ttt "\n\n" ttt "\n", ttt "\n\n\n\n\n" ttt "\n", 0);
+ assert_message_prettifying(ttt ttt "\n\n" ttt "\n", ttt ttt "\n\n\n\n\n" ttt "\n", 0);
+ assert_message_prettifying(ttt ttt ttt "\n\n" ttt "\n", ttt ttt ttt "\n\n\n\n\n" ttt "\n", 0);
+
+ assert_message_prettifying(ttt "\n\n" ttt ttt "\n", ttt "\n\n\n\n\n" ttt ttt "\n", 0);
+ assert_message_prettifying(ttt "\n\n" ttt ttt ttt "\n", ttt "\n\n\n\n\n" ttt ttt ttt "\n", 0);
+
+ assert_message_prettifying(ttt "\n\n" ttt "\n", ttt "\n\t\n \n\n \t\t\n" ttt "\n", 0);
+ assert_message_prettifying(ttt ttt "\n\n" ttt "\n", ttt ttt "\n\t\n \n\n \t\t\n" ttt "\n", 0);
+ assert_message_prettifying(ttt ttt ttt "\n\n" ttt "\n", ttt ttt ttt "\n\t\n \n\n \t\t\n" ttt "\n", 0);
+
+ assert_message_prettifying(ttt "\n\n" ttt ttt "\n", ttt "\n\t\n \n\n \t\t\n" ttt ttt "\n", 0);
+ assert_message_prettifying(ttt "\n\n" ttt ttt ttt "\n", ttt "\n\t\n \n\n \t\t\n" ttt ttt ttt "\n", 0);
+}
+
+void test_object_message__only_consecutive_blank_lines_should_be_completely_removed(void)
+{
+ assert_message_prettifying("", "\n", 0);
+ assert_message_prettifying("", "\n\n\n", 0);
+ assert_message_prettifying("", sss "\n" sss "\n" sss "\n", 0);
+ assert_message_prettifying("", sss sss "\n" sss "\n\n", 0);
+}
+
+void test_object_message__consecutive_blank_lines_at_the_beginning_should_be_removed(void)
+{
+ assert_message_prettifying(ttt "\n", "\n" ttt "\n", 0);
+ assert_message_prettifying(ttt "\n", "\n\n\n" ttt "\n", 0);
+ assert_message_prettifying(ttt ttt "\n", "\n\n\n" ttt ttt "\n", 0);
+ assert_message_prettifying(ttt ttt ttt "\n", "\n\n\n" ttt ttt ttt "\n", 0);
+ assert_message_prettifying(ttt ttt ttt ttt "\n", "\n\n\n" ttt ttt ttt ttt "\n", 0);
+ assert_message_prettifying(ttt "\n", sss "\n" sss "\n" sss "\n" ttt "\n", 0);
+ assert_message_prettifying(ttt "\n", "\n" sss "\n" sss sss "\n" ttt "\n", 0);
+ assert_message_prettifying(ttt "\n", sss sss "\n" sss "\n\n" ttt "\n", 0);
+ assert_message_prettifying(ttt "\n", sss sss sss "\n\n\n" ttt "\n", 0);
+ assert_message_prettifying(ttt "\n", "\n" sss sss sss "\n\n" ttt "\n", 0);
+ assert_message_prettifying(ttt "\n", "\n\n" sss sss sss "\n" ttt "\n", 0);
+}
+
+void test_object_message__consecutive_blank_lines_at_the_end_should_be_removed(void)
+{
+ assert_message_prettifying(ttt "\n", ttt "\n\n", 0);
+ assert_message_prettifying(ttt "\n", ttt "\n\n\n\n", 0);
+ assert_message_prettifying(ttt ttt "\n", ttt ttt "\n\n\n\n", 0);
+ assert_message_prettifying(ttt ttt ttt "\n", ttt ttt ttt "\n\n\n\n", 0);
+ assert_message_prettifying(ttt ttt ttt ttt "\n", ttt ttt ttt ttt "\n\n\n\n", 0);
+ assert_message_prettifying(ttt "\n", ttt "\n" sss "\n" sss "\n" sss "\n", 0);
+ assert_message_prettifying(ttt "\n", ttt "\n\n" sss "\n" sss sss "\n", 0);
+ assert_message_prettifying(ttt "\n", ttt "\n" sss sss "\n" sss "\n\n", 0);
+ assert_message_prettifying(ttt "\n", ttt "\n" sss sss sss "\n\n\n", 0);
+ assert_message_prettifying(ttt "\n", ttt "\n\n" sss sss sss "\n\n", 0);
+ assert_message_prettifying(ttt "\n", ttt "\n\n\n" sss sss sss "\n\n", 0);
+}
+
+void test_object_message__text_without_newline_at_end_should_end_with_newline(void)
+{
+ assert_message_prettifying(ttt "\n", ttt, 0);
+ assert_message_prettifying(ttt ttt "\n", ttt ttt, 0);
+ assert_message_prettifying(ttt ttt ttt "\n", ttt ttt ttt, 0);
+ assert_message_prettifying(ttt ttt ttt ttt "\n", ttt ttt ttt ttt, 0);
+}
+
+void test_object_message__text_plus_spaces_without_newline_should_not_show_spaces_and_end_with_newline(void)
+{
+ assert_message_prettifying(ttt "\n", ttt sss, 0);
+ assert_message_prettifying(ttt ttt "\n", ttt ttt sss, 0);
+ assert_message_prettifying(ttt ttt ttt "\n", ttt ttt ttt sss, 0);
+ assert_message_prettifying(ttt "\n", ttt sss sss, 0);
+ assert_message_prettifying(ttt ttt "\n", ttt ttt sss sss, 0);
+ assert_message_prettifying(ttt "\n", ttt sss sss sss, 0);
+}
+
+void test_object_message__text_plus_spaces_ending_with_newline_should_be_cleaned_and_newline_must_remain(void){
+ assert_message_prettifying(ttt "\n", ttt sss "\n", 0);
+ assert_message_prettifying(ttt "\n", ttt sss sss "\n", 0);
+ assert_message_prettifying(ttt "\n", ttt sss sss sss "\n", 0);
+ assert_message_prettifying(ttt ttt "\n", ttt ttt sss "\n", 0);
+ assert_message_prettifying(ttt ttt "\n", ttt ttt sss sss "\n", 0);
+ assert_message_prettifying(ttt ttt ttt "\n", ttt ttt ttt sss "\n", 0);
+}
+
+void test_object_message__spaces_with_newline_at_end_should_be_replaced_with_empty_string(void)
+{
+ assert_message_prettifying("", sss "\n", 0);
+ assert_message_prettifying("", sss sss "\n", 0);
+ assert_message_prettifying("", sss sss sss "\n", 0);
+ assert_message_prettifying("", sss sss sss sss "\n", 0);
+}
+
+void test_object_message__spaces_without_newline_at_end_should_be_replaced_with_empty_string(void)
+{
+ assert_message_prettifying("", "", 0);
+ assert_message_prettifying("", sss sss, 0);
+ assert_message_prettifying("", sss sss sss, 0);
+ assert_message_prettifying("", sss sss sss sss, 0);
+}
+
+void test_object_message__consecutive_text_lines_should_be_unchanged(void)
+{
+ assert_message_prettifying(ttt ttt "\n" ttt "\n", ttt ttt "\n" ttt "\n", 0);
+ assert_message_prettifying(ttt "\n" ttt ttt "\n" ttt "\n", ttt "\n" ttt ttt "\n" ttt "\n", 0);
+ assert_message_prettifying(ttt "\n" ttt "\n" ttt "\n" ttt ttt "\n", ttt "\n" ttt "\n" ttt "\n" ttt ttt "\n", 0);
+ assert_message_prettifying(ttt "\n" ttt "\n\n" ttt ttt "\n" ttt "\n", ttt "\n" ttt "\n\n" ttt ttt "\n" ttt "\n", 0);
+ assert_message_prettifying(ttt ttt "\n\n" ttt "\n" ttt ttt "\n", ttt ttt "\n\n" ttt "\n" ttt ttt "\n", 0);
+ assert_message_prettifying(ttt "\n" ttt ttt "\n\n" ttt "\n", ttt "\n" ttt ttt "\n\n" ttt "\n", 0);
+}
+
+void test_object_message__strip_comments(void)
+{
+ assert_message_prettifying("", "# comment", 1);
+ assert_message_prettifying("", "# comment\n", 1);
+ assert_message_prettifying("", "# comment \n", 1);
+
+ assert_message_prettifying(ttt "\n", ttt "\n" "# comment\n", 1);
+ assert_message_prettifying(ttt "\n", "# comment\n" ttt "\n", 1);
+ assert_message_prettifying(ttt "\n" ttt "\n", ttt "\n" "# comment\n" ttt "\n", 1);
+}
+
+void test_object_message__keep_comments(void)
+{
+ assert_message_prettifying("# comment\n", "# comment", 0);
+ assert_message_prettifying("# comment\n", "# comment\n", 0);
+ assert_message_prettifying("# comment\n", "# comment \n", 0);
+
+ assert_message_prettifying(ttt "\n" "# comment\n", ttt "\n" "# comment\n", 0);
+ assert_message_prettifying("# comment\n" ttt "\n", "# comment\n" ttt "\n", 0);
+ assert_message_prettifying(ttt "\n" "# comment\n" ttt "\n", ttt "\n" "# comment\n" ttt "\n", 0);
+}
+
+void test_object_message__message_prettify(void)
+{
+ git_buf buffer;
+
+ memset(&buffer, 0, sizeof(buffer));
+ cl_git_pass(git_message_prettify(&buffer, "", 0, '#'));
+ cl_assert_equal_s(buffer.ptr, "");
+ git_buf_dispose(&buffer);
+ cl_git_pass(git_message_prettify(&buffer, "", 1, '#'));
+ cl_assert_equal_s(buffer.ptr, "");
+ git_buf_dispose(&buffer);
+
+ cl_git_pass(git_message_prettify(&buffer, "Short", 0, '#'));
+ cl_assert_equal_s("Short\n", buffer.ptr);
+ git_buf_dispose(&buffer);
+ cl_git_pass(git_message_prettify(&buffer, "Short", 1, '#'));
+ cl_assert_equal_s("Short\n", buffer.ptr);
+ git_buf_dispose(&buffer);
+
+ cl_git_pass(git_message_prettify(&buffer, "This is longer\nAnd multiline\n# with some comments still in\n", 0, '#'));
+ cl_assert_equal_s(buffer.ptr, "This is longer\nAnd multiline\n# with some comments still in\n");
+ git_buf_dispose(&buffer);
+
+ cl_git_pass(git_message_prettify(&buffer, "This is longer\nAnd multiline\n# with some comments still in\n", 1, '#'));
+ cl_assert_equal_s(buffer.ptr, "This is longer\nAnd multiline\n");
+ git_buf_dispose(&buffer);
+}
diff --git a/tests/libgit2/object/peel.c b/tests/libgit2/object/peel.c
new file mode 100644
index 0000000..da3ef17
--- /dev/null
+++ b/tests/libgit2/object/peel.c
@@ -0,0 +1,118 @@
+#include "clar_libgit2.h"
+
+static git_repository *g_repo;
+
+void test_object_peel__initialize(void)
+{
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
+}
+
+void test_object_peel__cleanup(void)
+{
+ git_repository_free(g_repo);
+ g_repo = NULL;
+}
+
+static void assert_peel(
+ const char *sha,
+ git_object_t requested_type,
+ const char* expected_sha,
+ git_object_t expected_type)
+{
+ git_oid oid, expected_oid;
+ git_object *obj;
+ git_object *peeled;
+
+ cl_git_pass(git_oid__fromstr(&oid, sha, GIT_OID_SHA1));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
+
+ cl_git_pass(git_object_peel(&peeled, obj, requested_type));
+
+ cl_git_pass(git_oid__fromstr(&expected_oid, expected_sha, GIT_OID_SHA1));
+ cl_assert_equal_oid(&expected_oid, git_object_id(peeled));
+
+ cl_assert_equal_i(expected_type, git_object_type(peeled));
+
+ git_object_free(peeled);
+ git_object_free(obj);
+}
+
+static void assert_peel_error(int error, const char *sha, git_object_t requested_type)
+{
+ git_oid oid;
+ git_object *obj;
+ git_object *peeled;
+
+ cl_git_pass(git_oid__fromstr(&oid, sha, GIT_OID_SHA1));
+ cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
+
+ cl_assert_equal_i(error, git_object_peel(&peeled, obj, requested_type));
+
+ git_object_free(obj);
+}
+
+void test_object_peel__peeling_an_object_into_its_own_type_returns_another_instance_of_it(void)
+{
+ assert_peel("e90810b8df3e80c413d903f631643c716887138d", GIT_OBJECT_COMMIT,
+ "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJECT_COMMIT);
+ assert_peel("7b4384978d2493e851f9cca7858815fac9b10980", GIT_OBJECT_TAG,
+ "7b4384978d2493e851f9cca7858815fac9b10980", GIT_OBJECT_TAG);
+ assert_peel("53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJECT_TREE,
+ "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJECT_TREE);
+ assert_peel("0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJECT_BLOB,
+ "0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJECT_BLOB);
+}
+
+void test_object_peel__tag(void)
+{
+ assert_peel("7b4384978d2493e851f9cca7858815fac9b10980", GIT_OBJECT_COMMIT,
+ "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJECT_COMMIT);
+ assert_peel("7b4384978d2493e851f9cca7858815fac9b10980", GIT_OBJECT_TREE,
+ "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJECT_TREE);
+ assert_peel_error(GIT_EPEEL, "7b4384978d2493e851f9cca7858815fac9b10980", GIT_OBJECT_BLOB);
+ assert_peel("7b4384978d2493e851f9cca7858815fac9b10980", GIT_OBJECT_ANY,
+ "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJECT_COMMIT);
+}
+
+void test_object_peel__commit(void)
+{
+ assert_peel_error(GIT_EINVALIDSPEC, "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJECT_BLOB);
+ assert_peel("e90810b8df3e80c413d903f631643c716887138d", GIT_OBJECT_TREE,
+ "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJECT_TREE);
+ assert_peel("e90810b8df3e80c413d903f631643c716887138d", GIT_OBJECT_COMMIT,
+ "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJECT_COMMIT);
+ assert_peel_error(GIT_EINVALIDSPEC, "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJECT_TAG);
+ assert_peel("e90810b8df3e80c413d903f631643c716887138d", GIT_OBJECT_ANY,
+ "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJECT_TREE);
+}
+
+void test_object_peel__tree(void)
+{
+ assert_peel_error(GIT_EINVALIDSPEC, "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJECT_BLOB);
+ assert_peel("53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJECT_TREE,
+ "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJECT_TREE);
+ assert_peel_error(GIT_EINVALIDSPEC, "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJECT_COMMIT);
+ assert_peel_error(GIT_EINVALIDSPEC, "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJECT_TAG);
+ assert_peel_error(GIT_EINVALIDSPEC, "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJECT_ANY);
+}
+
+void test_object_peel__blob(void)
+{
+ assert_peel("0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJECT_BLOB,
+ "0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJECT_BLOB);
+ assert_peel_error(GIT_EINVALIDSPEC, "0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJECT_TREE);
+ assert_peel_error(GIT_EINVALIDSPEC, "0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJECT_COMMIT);
+ assert_peel_error(GIT_EINVALIDSPEC, "0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJECT_TAG);
+ assert_peel_error(GIT_EINVALIDSPEC, "0266163a49e280c4f5ed1e08facd36a2bd716bcf", GIT_OBJECT_ANY);
+}
+
+void test_object_peel__target_any_object_for_type_change(void)
+{
+ /* tag to commit */
+ assert_peel("7b4384978d2493e851f9cca7858815fac9b10980", GIT_OBJECT_ANY,
+ "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJECT_COMMIT);
+
+ /* commit to tree */
+ assert_peel("e90810b8df3e80c413d903f631643c716887138d", GIT_OBJECT_ANY,
+ "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJECT_TREE);
+}
diff --git a/tests/libgit2/object/raw/chars.c b/tests/libgit2/object/raw/chars.c
new file mode 100644
index 0000000..cb159e9
--- /dev/null
+++ b/tests/libgit2/object/raw/chars.c
@@ -0,0 +1,41 @@
+
+#include "clar_libgit2.h"
+
+#include "odb.h"
+
+void test_object_raw_chars__find_invalid_chars_in_oid(void)
+{
+ git_oid out;
+ unsigned char exp[] = {
+ 0x16, 0xa6, 0x77, 0x70, 0xb7,
+ 0xd8, 0xd7, 0x23, 0x17, 0xc4,
+ 0xb7, 0x75, 0x21, 0x3c, 0x23,
+ 0xa8, 0xbd, 0x74, 0xf5, 0xe0,
+ };
+ char in[] = "16a67770b7d8d72317c4b775213c23a8bd74f5e0";
+ unsigned int i;
+
+ for (i = 0; i < 256; i++) {
+ in[38] = (char)i;
+ if (git__fromhex(i) >= 0) {
+ exp[19] = (unsigned char)(git__fromhex(i) << 4);
+ cl_git_pass(git_oid__fromstr(&out, in, GIT_OID_SHA1));
+ cl_assert(memcmp(out.id, exp, GIT_OID_SHA1_SIZE) == 0);
+ } else {
+ cl_git_fail(git_oid__fromstr(&out, in, GIT_OID_SHA1));
+ }
+ }
+}
+
+void test_object_raw_chars__build_valid_oid_from_raw_bytes(void)
+{
+ git_oid out;
+ unsigned char exp[] = {
+ 0x16, 0xa6, 0x77, 0x70, 0xb7,
+ 0xd8, 0xd7, 0x23, 0x17, 0xc4,
+ 0xb7, 0x75, 0x21, 0x3c, 0x23,
+ 0xa8, 0xbd, 0x74, 0xf5, 0xe0,
+ };
+ git_oid__fromraw(&out, exp, GIT_OID_SHA1);
+ cl_git_pass(memcmp(out.id, exp, GIT_OID_SHA1_SIZE));
+}
diff --git a/tests/libgit2/object/raw/compare.c b/tests/libgit2/object/raw/compare.c
new file mode 100644
index 0000000..9258eef
--- /dev/null
+++ b/tests/libgit2/object/raw/compare.c
@@ -0,0 +1,123 @@
+
+#include "clar_libgit2.h"
+
+#include "odb.h"
+
+void test_object_raw_compare__succeed_on_copy_oid(void)
+{
+ git_oid a, b;
+ unsigned char exp[] = {
+ 0x16, 0xa6, 0x77, 0x70, 0xb7,
+ 0xd8, 0xd7, 0x23, 0x17, 0xc4,
+ 0xb7, 0x75, 0x21, 0x3c, 0x23,
+ 0xa8, 0xbd, 0x74, 0xf5, 0xe0,
+ };
+ memset(&b, 0, sizeof(b));
+ git_oid__fromraw(&a, exp, GIT_OID_SHA1);
+ git_oid_cpy(&b, &a);
+ cl_git_pass(memcmp(a.id, exp, GIT_OID_SHA1_SIZE));
+}
+
+void test_object_raw_compare__succeed_on_oid_comparison_lesser(void)
+{
+ git_oid a, b;
+ unsigned char a_in[] = {
+ 0x16, 0xa6, 0x77, 0x70, 0xb7,
+ 0xd8, 0xd7, 0x23, 0x17, 0xc4,
+ 0xb7, 0x75, 0x21, 0x3c, 0x23,
+ 0xa8, 0xbd, 0x74, 0xf5, 0xe0,
+ };
+ unsigned char b_in[] = {
+ 0x16, 0xa6, 0x77, 0x70, 0xb7,
+ 0xd8, 0xd7, 0x23, 0x17, 0xc4,
+ 0xb7, 0x75, 0x21, 0x3c, 0x23,
+ 0xa8, 0xbd, 0x74, 0xf5, 0xf0,
+ };
+ git_oid__fromraw(&a, a_in, GIT_OID_SHA1);
+ git_oid__fromraw(&b, b_in, GIT_OID_SHA1);
+ cl_assert(git_oid_cmp(&a, &b) < 0);
+}
+
+void test_object_raw_compare__succeed_on_oid_comparison_equal(void)
+{
+ git_oid a, b;
+ unsigned char a_in[] = {
+ 0x16, 0xa6, 0x77, 0x70, 0xb7,
+ 0xd8, 0xd7, 0x23, 0x17, 0xc4,
+ 0xb7, 0x75, 0x21, 0x3c, 0x23,
+ 0xa8, 0xbd, 0x74, 0xf5, 0xe0,
+ };
+ git_oid__fromraw(&a, a_in, GIT_OID_SHA1);
+ git_oid__fromraw(&b, a_in, GIT_OID_SHA1);
+ cl_assert(git_oid_cmp(&a, &b) == 0);
+}
+
+void test_object_raw_compare__succeed_on_oid_comparison_greater(void)
+{
+ git_oid a, b;
+ unsigned char a_in[] = {
+ 0x16, 0xa6, 0x77, 0x70, 0xb7,
+ 0xd8, 0xd7, 0x23, 0x17, 0xc4,
+ 0xb7, 0x75, 0x21, 0x3c, 0x23,
+ 0xa8, 0xbd, 0x74, 0xf5, 0xe0,
+ };
+ unsigned char b_in[] = {
+ 0x16, 0xa6, 0x77, 0x70, 0xb7,
+ 0xd8, 0xd7, 0x23, 0x17, 0xc4,
+ 0xb7, 0x75, 0x21, 0x3c, 0x23,
+ 0xa8, 0xbd, 0x74, 0xf5, 0xd0,
+ };
+ git_oid__fromraw(&a, a_in, GIT_OID_SHA1);
+ git_oid__fromraw(&b, b_in, GIT_OID_SHA1);
+ cl_assert(git_oid_cmp(&a, &b) > 0);
+}
+
+void test_object_raw_compare__compare_fmt_oids(void)
+{
+ const char *exp = "16a0123456789abcdef4b775213c23a8bd74f5e0";
+ git_oid in;
+ char out[GIT_OID_SHA1_HEXSIZE + 1];
+
+ cl_git_pass(git_oid__fromstr(&in, exp, GIT_OID_SHA1));
+
+ /* Format doesn't touch the last byte */
+ out[GIT_OID_SHA1_HEXSIZE] = 'Z';
+ git_oid_fmt(out, &in);
+ cl_assert(out[GIT_OID_SHA1_HEXSIZE] == 'Z');
+
+ /* Format produced the right result */
+ out[GIT_OID_SHA1_HEXSIZE] = '\0';
+ cl_assert_equal_s(exp, out);
+}
+
+void test_object_raw_compare__compare_static_oids(void)
+{
+ const char *exp = "16a0123456789abcdef4b775213c23a8bd74f5e0";
+ git_oid in;
+ char *out;
+
+ cl_git_pass(git_oid__fromstr(&in, exp, GIT_OID_SHA1));
+
+ out = git_oid_tostr_s(&in);
+ cl_assert(out);
+ cl_assert_equal_s(exp, out);
+}
+
+void test_object_raw_compare__compare_pathfmt_oids(void)
+{
+ const char *exp1 = "16a0123456789abcdef4b775213c23a8bd74f5e0";
+ const char *exp2 = "16/a0123456789abcdef4b775213c23a8bd74f5e0";
+ git_oid in;
+ char out[GIT_OID_SHA1_HEXSIZE + 2];
+
+ cl_git_pass(git_oid__fromstr(&in, exp1, GIT_OID_SHA1));
+
+ /* Format doesn't touch the last byte */
+ out[GIT_OID_SHA1_HEXSIZE + 1] = 'Z';
+ git_oid_pathfmt(out, &in);
+ cl_assert(out[GIT_OID_SHA1_HEXSIZE + 1] == 'Z');
+
+ /* Format produced the right result */
+ out[GIT_OID_SHA1_HEXSIZE + 1] = '\0';
+ cl_assert_equal_s(exp2, out);
+}
diff --git a/tests/libgit2/object/raw/convert.c b/tests/libgit2/object/raw/convert.c
new file mode 100644
index 0000000..962476b
--- /dev/null
+++ b/tests/libgit2/object/raw/convert.c
@@ -0,0 +1,112 @@
+
+#include "clar_libgit2.h"
+
+#include "odb.h"
+
+void test_object_raw_convert__succeed_on_oid_to_string_conversion(void)
+{
+ const char *exp = "16a0123456789abcdef4b775213c23a8bd74f5e0";
+ git_oid in;
+ char out[GIT_OID_SHA1_HEXSIZE + 1];
+ char *str;
+ int i;
+
+ cl_git_pass(git_oid__fromstr(&in, exp, GIT_OID_SHA1));
+
+ /* NULL buffer pointer, returns static empty string */
+ str = git_oid_tostr(NULL, sizeof(out), &in);
+ cl_assert(str && *str == '\0' && str != out);
+
+ /* zero buffer size, returns static empty string */
+ str = git_oid_tostr(out, 0, &in);
+ cl_assert(str && *str == '\0' && str != out);
+
+ /* NULL oid pointer, sets existing buffer to empty string */
+ str = git_oid_tostr(out, sizeof(out), NULL);
+ cl_assert(str && *str == '\0' && str == out);
+
+ /* n == 1, returns out as an empty string */
+ str = git_oid_tostr(out, 1, &in);
+ cl_assert(str && *str == '\0' && str == out);
+
+ for (i = 1; i < GIT_OID_SHA1_HEXSIZE; i++) {
+ out[i+1] = 'Z';
+ str = git_oid_tostr(out, i+1, &in);
+ /* returns out containing c-string */
+ cl_assert(str && str == out);
+ /* must be '\0' terminated */
+ cl_assert(*(str+i) == '\0');
+ /* must not touch bytes past end of string */
+ cl_assert(*(str+(i+1)) == 'Z');
+ /* i == n-1 characters of string */
+ cl_git_pass(strncmp(exp, out, i));
+ }
+
+ /* returns out as hex formatted c-string */
+ str = git_oid_tostr(out, sizeof(out), &in);
+ cl_assert(str && str == out && *(str+GIT_OID_SHA1_HEXSIZE) == '\0');
+ cl_assert_equal_s(exp, out);
+}
+
+void test_object_raw_convert__succeed_on_oid_to_string_conversion_big(void)
+{
+ const char *exp = "16a0123456789abcdef4b775213c23a8bd74f5e0";
+ git_oid in;
+ char big[GIT_OID_SHA1_HEXSIZE + 1 + 3]; /* note + 4 => big buffer */
+ char *str;
+
+ cl_git_pass(git_oid__fromstr(&in, exp, GIT_OID_SHA1));
+
+ /* place some tail material */
+ big[GIT_OID_SHA1_HEXSIZE+0] = 'W'; /* should be '\0' afterwards */
+ big[GIT_OID_SHA1_HEXSIZE+1] = 'X'; /* should remain untouched */
+ big[GIT_OID_SHA1_HEXSIZE+2] = 'Y'; /* ditto */
+ big[GIT_OID_SHA1_HEXSIZE+3] = 'Z'; /* ditto */
+
+ /* returns big as hex formatted c-string */
+ str = git_oid_tostr(big, sizeof(big), &in);
+ cl_assert(str && str == big && *(str+GIT_OID_SHA1_HEXSIZE) == '\0');
+ cl_assert_equal_s(exp, big);
+
+ /* check tail material is untouched */
+ cl_assert(str && str == big && *(str+GIT_OID_SHA1_HEXSIZE+1) == 'X');
+ cl_assert(str && str == big && *(str+GIT_OID_SHA1_HEXSIZE+2) == 'Y');
+ cl_assert(str && str == big && *(str+GIT_OID_SHA1_HEXSIZE+3) == 'Z');
+}
+
+static void check_partial_oid(
+ char *buffer, size_t count, const git_oid *oid, const char *expected)
+{
+ git_oid_nfmt(buffer, count, oid);
+ buffer[count] = '\0';
+ cl_assert_equal_s(expected, buffer);
+}
+
+void test_object_raw_convert__convert_oid_partially(void)
+{
+ const char *exp = "16a0123456789abcdef4b775213c23a8bd74f5e0";
+ git_oid in;
+ char big[GIT_OID_SHA1_HEXSIZE + 1 + 3]; /* note + 4 => big buffer */
+
+ cl_git_pass(git_oid__fromstr(&in, exp, GIT_OID_SHA1));
+
+ git_oid_nfmt(big, sizeof(big), &in);
+ cl_assert_equal_s(exp, big);
+
+ git_oid_nfmt(big, GIT_OID_SHA1_HEXSIZE + 1, &in);
+ cl_assert_equal_s(exp, big);
+
+ check_partial_oid(big, 1, &in, "1");
+ check_partial_oid(big, 2, &in, "16");
+ check_partial_oid(big, 3, &in, "16a");
+ check_partial_oid(big, 4, &in, "16a0");
+ check_partial_oid(big, 5, &in, "16a01");
+
+ check_partial_oid(big, GIT_OID_SHA1_HEXSIZE, &in, exp);
+ check_partial_oid(
+ big, GIT_OID_SHA1_HEXSIZE - 1, &in, "16a0123456789abcdef4b775213c23a8bd74f5e");
+ check_partial_oid(
+ big, GIT_OID_SHA1_HEXSIZE - 2, &in, "16a0123456789abcdef4b775213c23a8bd74f5");
+ check_partial_oid(
+ big, GIT_OID_SHA1_HEXSIZE - 3, &in, "16a0123456789abcdef4b775213c23a8bd74f");
+}
diff --git a/tests/libgit2/object/raw/data.h b/tests/libgit2/object/raw/data.h
new file mode 100644
index 0000000..57431e7
--- /dev/null
+++ b/tests/libgit2/object/raw/data.h
@@ -0,0 +1,323 @@
+
+/*
+ * Raw data
+ */
+static unsigned char commit_data[] = {
+ 0x74, 0x72, 0x65, 0x65, 0x20, 0x64, 0x66, 0x66,
+ 0x32, 0x64, 0x61, 0x39, 0x30, 0x62, 0x32, 0x35,
+ 0x34, 0x65, 0x31, 0x62, 0x65, 0x62, 0x38, 0x38,
+ 0x39, 0x64, 0x31, 0x66, 0x31, 0x66, 0x31, 0x32,
+ 0x38, 0x38, 0x62, 0x65, 0x31, 0x38, 0x30, 0x33,
+ 0x37, 0x38, 0x32, 0x64, 0x66, 0x0a, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x20, 0x41, 0x20, 0x55,
+ 0x20, 0x54, 0x68, 0x6f, 0x72, 0x20, 0x3c, 0x61,
+ 0x75, 0x74, 0x68, 0x6f, 0x72, 0x40, 0x65, 0x78,
+ 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x3e, 0x20, 0x31, 0x32, 0x32, 0x37, 0x38,
+ 0x31, 0x34, 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30,
+ 0x30, 0x30, 0x30, 0x0a, 0x63, 0x6f, 0x6d, 0x6d,
+ 0x69, 0x74, 0x74, 0x65, 0x72, 0x20, 0x43, 0x20,
+ 0x4f, 0x20, 0x4d, 0x69, 0x74, 0x74, 0x65, 0x72,
+ 0x20, 0x3c, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
+ 0x74, 0x65, 0x72, 0x40, 0x65, 0x78, 0x61, 0x6d,
+ 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e,
+ 0x20, 0x31, 0x32, 0x32, 0x37, 0x38, 0x31, 0x34,
+ 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30, 0x30, 0x30,
+ 0x30, 0x0a, 0x0a, 0x41, 0x20, 0x6f, 0x6e, 0x65,
+ 0x2d, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x63, 0x6f,
+ 0x6d, 0x6d, 0x69, 0x74, 0x20, 0x73, 0x75, 0x6d,
+ 0x6d, 0x61, 0x72, 0x79, 0x0a, 0x0a, 0x54, 0x68,
+ 0x65, 0x20, 0x62, 0x6f, 0x64, 0x79, 0x20, 0x6f,
+ 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f,
+ 0x6d, 0x6d, 0x69, 0x74, 0x20, 0x6d, 0x65, 0x73,
+ 0x73, 0x61, 0x67, 0x65, 0x2c, 0x20, 0x63, 0x6f,
+ 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67,
+ 0x20, 0x66, 0x75, 0x72, 0x74, 0x68, 0x65, 0x72,
+ 0x20, 0x65, 0x78, 0x70, 0x6c, 0x61, 0x6e, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x6f, 0x66, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x70, 0x75, 0x72, 0x70,
+ 0x6f, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67,
+ 0x65, 0x73, 0x20, 0x69, 0x6e, 0x74, 0x72, 0x6f,
+ 0x64, 0x75, 0x63, 0x65, 0x64, 0x20, 0x62, 0x79,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6d,
+ 0x6d, 0x69, 0x74, 0x2e, 0x0a, 0x0a, 0x53, 0x69,
+ 0x67, 0x6e, 0x65, 0x64, 0x2d, 0x6f, 0x66, 0x2d,
+ 0x62, 0x79, 0x3a, 0x20, 0x41, 0x20, 0x55, 0x20,
+ 0x54, 0x68, 0x6f, 0x72, 0x20, 0x3c, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x40, 0x65, 0x78, 0x61,
+ 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x3e, 0x0a,
+};
+
+
+static unsigned char tree_data[] = {
+ 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x6f,
+ 0x6e, 0x65, 0x00, 0x8b, 0x13, 0x78, 0x91, 0x79,
+ 0x1f, 0xe9, 0x69, 0x27, 0xad, 0x78, 0xe6, 0x4b,
+ 0x0a, 0xad, 0x7b, 0xde, 0xd0, 0x8b, 0xdc, 0x31,
+ 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x73, 0x6f,
+ 0x6d, 0x65, 0x00, 0xfd, 0x84, 0x30, 0xbc, 0x86,
+ 0x4c, 0xfc, 0xd5, 0xf1, 0x0e, 0x55, 0x90, 0xf8,
+ 0xa4, 0x47, 0xe0, 0x1b, 0x94, 0x2b, 0xfe, 0x31,
+ 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x77,
+ 0x6f, 0x00, 0x78, 0x98, 0x19, 0x22, 0x61, 0x3b,
+ 0x2a, 0xfb, 0x60, 0x25, 0x04, 0x2f, 0xf6, 0xbd,
+ 0x87, 0x8a, 0xc1, 0x99, 0x4e, 0x85, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x7a, 0x65, 0x72,
+ 0x6f, 0x00, 0xe6, 0x9d, 0xe2, 0x9b, 0xb2, 0xd1,
+ 0xd6, 0x43, 0x4b, 0x8b, 0x29, 0xae, 0x77, 0x5a,
+ 0xd8, 0xc2, 0xe4, 0x8c, 0x53, 0x91,
+};
+
+static unsigned char tag_data[] = {
+ 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x33,
+ 0x64, 0x37, 0x66, 0x38, 0x61, 0x36, 0x61, 0x66,
+ 0x30, 0x37, 0x36, 0x63, 0x38, 0x63, 0x33, 0x66,
+ 0x32, 0x30, 0x30, 0x37, 0x31, 0x61, 0x38, 0x39,
+ 0x33, 0x35, 0x63, 0x64, 0x62, 0x65, 0x38, 0x32,
+ 0x32, 0x38, 0x35, 0x39, 0x34, 0x64, 0x31, 0x0a,
+ 0x74, 0x79, 0x70, 0x65, 0x20, 0x63, 0x6f, 0x6d,
+ 0x6d, 0x69, 0x74, 0x0a, 0x74, 0x61, 0x67, 0x20,
+ 0x76, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x0a, 0x74,
+ 0x61, 0x67, 0x67, 0x65, 0x72, 0x20, 0x43, 0x20,
+ 0x4f, 0x20, 0x4d, 0x69, 0x74, 0x74, 0x65, 0x72,
+ 0x20, 0x3c, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
+ 0x74, 0x65, 0x72, 0x40, 0x65, 0x78, 0x61, 0x6d,
+ 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e,
+ 0x20, 0x31, 0x32, 0x32, 0x37, 0x38, 0x31, 0x34,
+ 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30, 0x30, 0x30,
+ 0x30, 0x0a, 0x0a, 0x54, 0x68, 0x69, 0x73, 0x20,
+ 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74,
+ 0x61, 0x67, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63,
+ 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x72, 0x65,
+ 0x6c, 0x65, 0x61, 0x73, 0x65, 0x20, 0x76, 0x30,
+ 0x2e, 0x30, 0x2e, 0x31, 0x0a,
+};
+
+/*
+ * Dummy data
+ */
+static unsigned char zero_data[] = {
+ 0x00,
+};
+
+static unsigned char one_data[] = {
+ 0x0a,
+};
+
+static unsigned char two_data[] = {
+ 0x61, 0x0a,
+};
+
+static unsigned char some_data[] = {
+ 0x2f, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x54, 0x68,
+ 0x69, 0x73, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20,
+ 0x69, 0x73, 0x20, 0x66, 0x72, 0x65, 0x65, 0x20,
+ 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65,
+ 0x3b, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x63, 0x61,
+ 0x6e, 0x20, 0x72, 0x65, 0x64, 0x69, 0x73, 0x74,
+ 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x69,
+ 0x74, 0x20, 0x61, 0x6e, 0x64, 0x2f, 0x6f, 0x72,
+ 0x20, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0a,
+ 0x20, 0x2a, 0x20, 0x69, 0x74, 0x20, 0x75, 0x6e,
+ 0x64, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x74, 0x65, 0x72, 0x6d, 0x73, 0x20, 0x6f, 0x66,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x47, 0x4e, 0x55,
+ 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
+ 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x2c,
+ 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+ 0x20, 0x32, 0x2c, 0x0a, 0x20, 0x2a, 0x20, 0x61,
+ 0x73, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73,
+ 0x68, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x46, 0x72, 0x65, 0x65, 0x20,
+ 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65,
+ 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x2a, 0x0a,
+ 0x20, 0x2a, 0x20, 0x49, 0x6e, 0x20, 0x61, 0x64,
+ 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74,
+ 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x65,
+ 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e,
+ 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x47, 0x4e, 0x55, 0x20, 0x47, 0x65, 0x6e,
+ 0x65, 0x72, 0x61, 0x6c, 0x20, 0x50, 0x75, 0x62,
+ 0x6c, 0x69, 0x63, 0x20, 0x4c, 0x69, 0x63, 0x65,
+ 0x6e, 0x73, 0x65, 0x2c, 0x0a, 0x20, 0x2a, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x61, 0x75, 0x74, 0x68,
+ 0x6f, 0x72, 0x73, 0x20, 0x67, 0x69, 0x76, 0x65,
+ 0x20, 0x79, 0x6f, 0x75, 0x20, 0x75, 0x6e, 0x6c,
+ 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x20, 0x70,
+ 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f,
+ 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x6c, 0x69, 0x6e,
+ 0x6b, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f,
+ 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x64, 0x0a, 0x20,
+ 0x2a, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
+ 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x69,
+ 0x73, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x69,
+ 0x6e, 0x74, 0x6f, 0x20, 0x63, 0x6f, 0x6d, 0x62,
+ 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+ 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6f, 0x74,
+ 0x68, 0x65, 0x72, 0x20, 0x70, 0x72, 0x6f, 0x67,
+ 0x72, 0x61, 0x6d, 0x73, 0x2c, 0x0a, 0x20, 0x2a,
+ 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x6f, 0x20,
+ 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75,
+ 0x74, 0x65, 0x20, 0x74, 0x68, 0x6f, 0x73, 0x65,
+ 0x20, 0x63, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x77, 0x69,
+ 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x61, 0x6e,
+ 0x79, 0x20, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x2a,
+ 0x20, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x20,
+ 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x66, 0x69, 0x6c,
+ 0x65, 0x2e, 0x20, 0x20, 0x28, 0x54, 0x68, 0x65,
+ 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
+ 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x0a,
+ 0x20, 0x2a, 0x20, 0x72, 0x65, 0x73, 0x74, 0x72,
+ 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20,
+ 0x64, 0x6f, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x79,
+ 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x74, 0x68, 0x65,
+ 0x72, 0x20, 0x72, 0x65, 0x73, 0x70, 0x65, 0x63,
+ 0x74, 0x73, 0x3b, 0x20, 0x66, 0x6f, 0x72, 0x20,
+ 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2c,
+ 0x20, 0x74, 0x68, 0x65, 0x79, 0x20, 0x63, 0x6f,
+ 0x76, 0x65, 0x72, 0x0a, 0x20, 0x2a, 0x20, 0x6d,
+ 0x6f, 0x64, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x2c,
+ 0x20, 0x61, 0x6e, 0x64, 0x20, 0x64, 0x69, 0x73,
+ 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x6e,
+ 0x6f, 0x74, 0x20, 0x6c, 0x69, 0x6e, 0x6b, 0x65,
+ 0x64, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x0a, 0x20,
+ 0x2a, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x62,
+ 0x69, 0x6e, 0x65, 0x64, 0x20, 0x65, 0x78, 0x65,
+ 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2e,
+ 0x29, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20,
+ 0x54, 0x68, 0x69, 0x73, 0x20, 0x66, 0x69, 0x6c,
+ 0x65, 0x20, 0x69, 0x73, 0x20, 0x64, 0x69, 0x73,
+ 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x64,
+ 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x68, 0x6f, 0x70, 0x65, 0x20, 0x74, 0x68, 0x61,
+ 0x74, 0x20, 0x69, 0x74, 0x20, 0x77, 0x69, 0x6c,
+ 0x6c, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65,
+ 0x66, 0x75, 0x6c, 0x2c, 0x20, 0x62, 0x75, 0x74,
+ 0x0a, 0x20, 0x2a, 0x20, 0x57, 0x49, 0x54, 0x48,
+ 0x4f, 0x55, 0x54, 0x20, 0x41, 0x4e, 0x59, 0x20,
+ 0x57, 0x41, 0x52, 0x52, 0x41, 0x4e, 0x54, 0x59,
+ 0x3b, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75,
+ 0x74, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x69,
+ 0x65, 0x64, 0x20, 0x77, 0x61, 0x72, 0x72, 0x61,
+ 0x6e, 0x74, 0x79, 0x20, 0x6f, 0x66, 0x0a, 0x20,
+ 0x2a, 0x20, 0x4d, 0x45, 0x52, 0x43, 0x48, 0x41,
+ 0x4e, 0x54, 0x41, 0x42, 0x49, 0x4c, 0x49, 0x54,
+ 0x59, 0x20, 0x6f, 0x72, 0x20, 0x46, 0x49, 0x54,
+ 0x4e, 0x45, 0x53, 0x53, 0x20, 0x46, 0x4f, 0x52,
+ 0x20, 0x41, 0x20, 0x50, 0x41, 0x52, 0x54, 0x49,
+ 0x43, 0x55, 0x4c, 0x41, 0x52, 0x20, 0x50, 0x55,
+ 0x52, 0x50, 0x4f, 0x53, 0x45, 0x2e, 0x20, 0x20,
+ 0x53, 0x65, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x47, 0x4e, 0x55, 0x0a, 0x20, 0x2a, 0x20, 0x47,
+ 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x20, 0x50,
+ 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x4c, 0x69,
+ 0x63, 0x65, 0x6e, 0x73, 0x65, 0x20, 0x66, 0x6f,
+ 0x72, 0x20, 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x64,
+ 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x0a,
+ 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x59, 0x6f,
+ 0x75, 0x20, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64,
+ 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x72, 0x65,
+ 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x20, 0x61,
+ 0x20, 0x63, 0x6f, 0x70, 0x79, 0x20, 0x6f, 0x66,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x47, 0x4e, 0x55,
+ 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
+ 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x0a,
+ 0x20, 0x2a, 0x20, 0x61, 0x6c, 0x6f, 0x6e, 0x67,
+ 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68,
+ 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72,
+ 0x61, 0x6d, 0x3b, 0x20, 0x73, 0x65, 0x65, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x6c, 0x65,
+ 0x20, 0x43, 0x4f, 0x50, 0x59, 0x49, 0x4e, 0x47,
+ 0x2e, 0x20, 0x20, 0x49, 0x66, 0x20, 0x6e, 0x6f,
+ 0x74, 0x2c, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65,
+ 0x20, 0x74, 0x6f, 0x0a, 0x20, 0x2a, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x46, 0x72, 0x65, 0x65, 0x20,
+ 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65,
+ 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x35, 0x31, 0x20,
+ 0x46, 0x72, 0x61, 0x6e, 0x6b, 0x6c, 0x69, 0x6e,
+ 0x20, 0x53, 0x74, 0x72, 0x65, 0x65, 0x74, 0x2c,
+ 0x20, 0x46, 0x69, 0x66, 0x74, 0x68, 0x20, 0x46,
+ 0x6c, 0x6f, 0x6f, 0x72, 0x2c, 0x0a, 0x20, 0x2a,
+ 0x20, 0x42, 0x6f, 0x73, 0x74, 0x6f, 0x6e, 0x2c,
+ 0x20, 0x4d, 0x41, 0x20, 0x30, 0x32, 0x31, 0x31,
+ 0x30, 0x2d, 0x31, 0x33, 0x30, 0x31, 0x2c, 0x20,
+ 0x55, 0x53, 0x41, 0x2e, 0x0a, 0x20, 0x2a, 0x2f,
+ 0x0a,
+};
+
+/*
+ * SHA1 Hashes
+ */
+static char *commit_id = "3d7f8a6af076c8c3f20071a8935cdbe8228594d1";
+static char *tree_id = "dff2da90b254e1beb889d1f1f1288be1803782df";
+static char *tag_id = "09d373e1dfdc16b129ceec6dd649739911541e05";
+static char *zero_id = "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391";
+static char *one_id = "8b137891791fe96927ad78e64b0aad7bded08bdc";
+static char *two_id = "78981922613b2afb6025042ff6bd878ac1994e85";
+static char *some_id = "fd8430bc864cfcd5f10e5590f8a447e01b942bfe";
+
+/*
+ * In-memory objects
+ */
+static git_rawobj tree_obj = {
+ tree_data,
+ sizeof(tree_data),
+ GIT_OBJECT_TREE
+};
+
+static git_rawobj tag_obj = {
+ tag_data,
+ sizeof(tag_data),
+ GIT_OBJECT_TAG
+};
+
+static git_rawobj zero_obj = {
+ zero_data,
+ 0,
+ GIT_OBJECT_BLOB
+};
+
+static git_rawobj one_obj = {
+ one_data,
+ sizeof(one_data),
+ GIT_OBJECT_BLOB
+};
+
+static git_rawobj two_obj = {
+ two_data,
+ sizeof(two_data),
+ GIT_OBJECT_BLOB
+};
+
+static git_rawobj commit_obj = {
+ commit_data,
+ sizeof(commit_data),
+ GIT_OBJECT_COMMIT
+};
+
+static git_rawobj some_obj = {
+ some_data,
+ sizeof(some_data),
+ GIT_OBJECT_BLOB
+};
+
+static git_rawobj junk_obj = {
+ NULL,
+ 0,
+ GIT_OBJECT_INVALID
+};
diff --git a/tests/libgit2/object/raw/fromstr.c b/tests/libgit2/object/raw/fromstr.c
new file mode 100644
index 0000000..302d362
--- /dev/null
+++ b/tests/libgit2/object/raw/fromstr.c
@@ -0,0 +1,29 @@
+
+#include "clar_libgit2.h"
+
+#include "odb.h"
+
+void test_object_raw_fromstr__fail_on_invalid_oid_string(void)
+{
+ git_oid out;
+ cl_git_fail(git_oid__fromstr(&out, "", GIT_OID_SHA1));
+ cl_git_fail(git_oid__fromstr(&out, "moo", GIT_OID_SHA1));
+ cl_git_fail(git_oid__fromstr(&out, "16a67770b7d8d72317c4b775213c23a8bd74f5ez", GIT_OID_SHA1));
+}
+
+void test_object_raw_fromstr__succeed_on_valid_oid_string(void)
+{
+ git_oid out;
+ unsigned char exp[] = {
+ 0x16, 0xa6, 0x77, 0x70, 0xb7,
+ 0xd8, 0xd7, 0x23, 0x17, 0xc4,
+ 0xb7, 0x75, 0x21, 0x3c, 0x23,
+ 0xa8, 0xbd, 0x74, 0xf5, 0xe0,
+ };
+
+ cl_git_pass(git_oid__fromstr(&out, "16a67770b7d8d72317c4b775213c23a8bd74f5e0", GIT_OID_SHA1));
+ cl_git_pass(memcmp(out.id, exp, GIT_OID_SHA1_SIZE));
+
+ cl_git_pass(git_oid__fromstr(&out, "16A67770B7D8D72317C4b775213C23A8BD74F5E0", GIT_OID_SHA1));
+ cl_git_pass(memcmp(out.id, exp, GIT_OID_SHA1_SIZE));
+}
diff --git a/tests/libgit2/object/raw/hash.c b/tests/libgit2/object/raw/hash.c
new file mode 100644
index 0000000..0df1afc
--- /dev/null
+++ b/tests/libgit2/object/raw/hash.c
@@ -0,0 +1,176 @@
+
+#include "clar_libgit2.h"
+
+#include "odb.h"
+#include "hash.h"
+
+#include "data.h"
+
+static void hash_object_pass(git_oid *oid, git_rawobj *obj)
+{
+ cl_git_pass(git_odb__hash(oid, obj->data, obj->len, obj->type, GIT_OID_SHA1));
+}
+static void hash_object_fail(git_oid *oid, git_rawobj *obj)
+{
+ cl_git_fail(git_odb__hash(oid, obj->data, obj->len, obj->type, GIT_OID_SHA1));
+}
+
+static char *hello_id = "22596363b3de40b06f981fb85d82312e8c0ed511";
+static char *hello_text = "hello world\n";
+
+static char *bye_id = "ce08fe4884650f067bd5703b6a59a8b3b3c99a09";
+static char *bye_text = "bye world\n";
+
+void test_object_raw_hash__hash_by_blocks(void)
+{
+ git_hash_ctx ctx;
+ unsigned char hash[GIT_HASH_SHA1_SIZE];
+ git_oid id1, id2;
+
+ cl_git_pass(git_hash_ctx_init(&ctx, GIT_HASH_ALGORITHM_SHA1));
+
+ /* should already be init'd */
+ cl_git_pass(git_hash_update(&ctx, hello_text, strlen(hello_text)));
+ cl_git_pass(git_hash_final(hash, &ctx));
+ cl_git_pass(git_oid__fromraw(&id2, hash, GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&id1, hello_id, GIT_OID_SHA1));
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+
+ /* reinit should permit reuse */
+ cl_git_pass(git_hash_init(&ctx));
+ cl_git_pass(git_hash_update(&ctx, bye_text, strlen(bye_text)));
+ cl_git_pass(git_hash_final(hash, &ctx));
+ cl_git_pass(git_oid__fromraw(&id2, hash, GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&id1, bye_id, GIT_OID_SHA1));
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+
+ git_hash_ctx_cleanup(&ctx);
+}
+
+void test_object_raw_hash__hash_buffer_in_single_call(void)
+{
+ git_oid id1, id2;
+ unsigned char hash[GIT_HASH_SHA1_SIZE];
+
+ cl_git_pass(git_oid__fromstr(&id1, hello_id, GIT_OID_SHA1));
+ cl_git_pass(git_hash_buf(hash, hello_text, strlen(hello_text), GIT_HASH_ALGORITHM_SHA1));
+ cl_git_pass(git_oid__fromraw(&id2, hash, GIT_OID_SHA1));
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+}
+
+void test_object_raw_hash__hash_vector(void)
+{
+ git_oid id1, id2;
+ git_str_vec vec[2];
+ unsigned char hash[GIT_HASH_SHA1_SIZE];
+
+ cl_git_pass(git_oid__fromstr(&id1, hello_id, GIT_OID_SHA1));
+
+ vec[0].data = hello_text;
+ vec[0].len = 4;
+ vec[1].data = hello_text+4;
+ vec[1].len = strlen(hello_text)-4;
+
+ git_hash_vec(hash, vec, 2, GIT_HASH_ALGORITHM_SHA1);
+ git_oid__fromraw(&id2, hash, GIT_OID_SHA1);
+
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+}
+
+void test_object_raw_hash__hash_junk_data(void)
+{
+ git_oid id, id_zero;
+
+ cl_git_pass(git_oid__fromstr(&id_zero, zero_id, GIT_OID_SHA1));
+
+ /* invalid types: */
+ junk_obj.data = some_data;
+ hash_object_fail(&id, &junk_obj);
+
+ junk_obj.type = 0; /* EXT1 */
+ hash_object_fail(&id, &junk_obj);
+
+ junk_obj.type = 5; /* EXT2 */
+ hash_object_fail(&id, &junk_obj);
+
+ junk_obj.type = GIT_OBJECT_OFS_DELTA;
+ hash_object_fail(&id, &junk_obj);
+
+ junk_obj.type = GIT_OBJECT_REF_DELTA;
+ hash_object_fail(&id, &junk_obj);
+
+ junk_obj.type = 42;
+ hash_object_fail(&id, &junk_obj);
+
+ /* data can be NULL only if len is zero: */
+ junk_obj.type = GIT_OBJECT_BLOB;
+ junk_obj.data = NULL;
+ hash_object_pass(&id, &junk_obj);
+ cl_assert(git_oid_cmp(&id, &id_zero) == 0);
+
+ junk_obj.len = 1;
+ hash_object_fail(&id, &junk_obj);
+}
+
+void test_object_raw_hash__hash_commit_object(void)
+{
+ git_oid id1, id2;
+
+ cl_git_pass(git_oid__fromstr(&id1, commit_id, GIT_OID_SHA1));
+ hash_object_pass(&id2, &commit_obj);
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+}
+
+void test_object_raw_hash__hash_tree_object(void)
+{
+ git_oid id1, id2;
+
+ cl_git_pass(git_oid__fromstr(&id1, tree_id, GIT_OID_SHA1));
+ hash_object_pass(&id2, &tree_obj);
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+}
+
+void test_object_raw_hash__hash_tag_object(void)
+{
+ git_oid id1, id2;
+
+ cl_git_pass(git_oid__fromstr(&id1, tag_id, GIT_OID_SHA1));
+ hash_object_pass(&id2, &tag_obj);
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+}
+
+void test_object_raw_hash__hash_zero_length_object(void)
+{
+ git_oid id1, id2;
+
+ cl_git_pass(git_oid__fromstr(&id1, zero_id, GIT_OID_SHA1));
+ hash_object_pass(&id2, &zero_obj);
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+}
+
+void test_object_raw_hash__hash_one_byte_object(void)
+{
+ git_oid id1, id2;
+
+ cl_git_pass(git_oid__fromstr(&id1, one_id, GIT_OID_SHA1));
+ hash_object_pass(&id2, &one_obj);
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+}
+
+void test_object_raw_hash__hash_two_byte_object(void)
+{
+ git_oid id1, id2;
+
+ cl_git_pass(git_oid__fromstr(&id1, two_id, GIT_OID_SHA1));
+ hash_object_pass(&id2, &two_obj);
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+}
+
+void test_object_raw_hash__hash_multi_byte_object(void)
+{
+ git_oid id1, id2;
+
+ cl_git_pass(git_oid__fromstr(&id1, some_id, GIT_OID_SHA1));
+ hash_object_pass(&id2, &some_obj);
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+}
diff --git a/tests/libgit2/object/raw/short.c b/tests/libgit2/object/raw/short.c
new file mode 100644
index 0000000..f867e6b
--- /dev/null
+++ b/tests/libgit2/object/raw/short.c
@@ -0,0 +1,140 @@
+
+#include "clar_libgit2.h"
+
+#include "odb.h"
+#include "hash.h"
+
+void test_object_raw_short__oid_shortener_no_duplicates(void)
+{
+ git_oid_shorten *os;
+ int min_len;
+
+ os = git_oid_shorten_new(0);
+ cl_assert(os != NULL);
+
+ git_oid_shorten_add(os, "22596363b3de40b06f981fb85d82312e8c0ed511");
+ git_oid_shorten_add(os, "ce08fe4884650f067bd5703b6a59a8b3b3c99a09");
+ git_oid_shorten_add(os, "16a0123456789abcdef4b775213c23a8bd74f5e0");
+ min_len = git_oid_shorten_add(os, "ce08fe4884650f067bd5703b6a59a8b3b3c99a09");
+
+ cl_assert(min_len == GIT_OID_SHA1_HEXSIZE + 1);
+
+ git_oid_shorten_free(os);
+}
+
+static int insert_sequential_oids(
+ char ***out, git_oid_shorten *os, int n, int fail)
+{
+ int i, min_len = 0;
+ char numbuf[16];
+ git_oid oid;
+ unsigned char hashbuf[GIT_HASH_SHA1_SIZE];
+ char **oids = git__calloc(n, sizeof(char *));
+ cl_assert(oids != NULL);
+
+ for (i = 0; i < n; ++i) {
+ p_snprintf(numbuf, sizeof(numbuf), "%u", (unsigned int)i);
+ git_hash_buf(hashbuf, numbuf, strlen(numbuf), GIT_HASH_ALGORITHM_SHA1);
+
+ git_oid__fromraw(&oid, hashbuf, GIT_OID_SHA1);
+
+ oids[i] = git__malloc(GIT_OID_SHA1_HEXSIZE + 1);
+ cl_assert(oids[i]);
+ git_oid_nfmt(oids[i], GIT_OID_SHA1_HEXSIZE + 1, &oid);
+
+ min_len = git_oid_shorten_add(os, oids[i]);
+
+ /* After "fail", we expect git_oid_shorten_add to fail */
+ if (fail >= 0 && i >= fail)
+ cl_assert(min_len < 0);
+ else
+ cl_assert(min_len >= 0);
+ }
+
+ *out = oids;
+
+ return min_len;
+}
+
+static void free_oids(int n, char **oids)
+{
+ int i;
+
+ for (i = 0; i < n; ++i) {
+ git__free(oids[i]);
+ }
+ git__free(oids);
+}
+
+void test_object_raw_short__oid_shortener_stresstest_git_oid_shorten(void)
+{
+#define MAX_OIDS 1000
+
+ git_oid_shorten *os;
+ size_t i, j;
+ int min_len = 0, found_collision;
+ char **oids;
+
+ os = git_oid_shorten_new(0);
+ cl_assert(os != NULL);
+
+ /*
+ * Insert in the shortener 1000 unique SHA1 ids
+ */
+ min_len = insert_sequential_oids(&oids, os, MAX_OIDS, MAX_OIDS);
+ cl_assert(min_len > 0);
+
+ /*
+ * Compare the first `min_char - 1` characters of each
+ * SHA1 OID. If the minimizer worked, we should find at
+ * least one collision
+ */
+ found_collision = 0;
+ for (i = 0; i < MAX_OIDS; ++i) {
+ for (j = i + 1; j < MAX_OIDS; ++j) {
+ if (memcmp(oids[i], oids[j], min_len - 1) == 0)
+ found_collision = 1;
+ }
+ }
+ cl_assert_equal_b(true, found_collision);
+
+ /*
+ * Compare the first `min_char` characters of each
+ * SHA1 OID. If the minimizer worked, every single preffix
+ * should be unique.
+ */
+ found_collision = 0;
+ for (i = 0; i < MAX_OIDS; ++i) {
+ for (j = i + 1; j < MAX_OIDS; ++j) {
+ if (memcmp(oids[i], oids[j], min_len) == 0)
+ found_collision = 1;
+ }
+ }
+ cl_assert_equal_b(false, found_collision);
+
+ /* cleanup */
+ free_oids(MAX_OIDS, oids);
+ git_oid_shorten_free(os);
+
+#undef MAX_OIDS
+}
+
+void test_object_raw_short__oid_shortener_too_much_oids(void)
+{
+ /* The magic number of oids at which an oid_shortener will fail.
+ * This was experimentally established. */
+#define MAX_OIDS 24556
+
+ git_oid_shorten *os;
+ char **oids;
+
+ os = git_oid_shorten_new(0);
+ cl_assert(os != NULL);
+
+ cl_assert(insert_sequential_oids(&oids, os, MAX_OIDS, MAX_OIDS - 1) < 0);
+
+ free_oids(MAX_OIDS, oids);
+ git_oid_shorten_free(os);
+
+#undef MAX_OIDS
+}
diff --git a/tests/libgit2/object/raw/size.c b/tests/libgit2/object/raw/size.c
new file mode 100644
index 0000000..a45e1d2
--- /dev/null
+++ b/tests/libgit2/object/raw/size.c
@@ -0,0 +1,13 @@
+
+#include "clar_libgit2.h"
+
+#include "odb.h"
+
+void test_object_raw_size__validate_oid_size(void)
+{
+ git_oid out;
+
+ cl_assert(20 == GIT_OID_SHA1_SIZE);
+ cl_assert(40 == GIT_OID_SHA1_HEXSIZE);
+ cl_assert(sizeof(out.id) == GIT_OID_MAX_SIZE);
+}
diff --git a/tests/libgit2/object/raw/type2string.c b/tests/libgit2/object/raw/type2string.c
new file mode 100644
index 0000000..ebd81f5
--- /dev/null
+++ b/tests/libgit2/object/raw/type2string.c
@@ -0,0 +1,54 @@
+
+#include "clar_libgit2.h"
+
+#include "odb.h"
+#include "hash.h"
+
+void test_object_raw_type2string__convert_type_to_string(void)
+{
+ cl_assert_equal_s(git_object_type2string(GIT_OBJECT_INVALID), "");
+ cl_assert_equal_s(git_object_type2string(0), ""); /* EXT1 */
+ cl_assert_equal_s(git_object_type2string(GIT_OBJECT_COMMIT), "commit");
+ cl_assert_equal_s(git_object_type2string(GIT_OBJECT_TREE), "tree");
+ cl_assert_equal_s(git_object_type2string(GIT_OBJECT_BLOB), "blob");
+ cl_assert_equal_s(git_object_type2string(GIT_OBJECT_TAG), "tag");
+ cl_assert_equal_s(git_object_type2string(5), ""); /* EXT2 */
+ cl_assert_equal_s(git_object_type2string(GIT_OBJECT_OFS_DELTA), "OFS_DELTA");
+ cl_assert_equal_s(git_object_type2string(GIT_OBJECT_REF_DELTA), "REF_DELTA");
+
+ cl_assert_equal_s(git_object_type2string(-2), "");
+ cl_assert_equal_s(git_object_type2string(8), "");
+ cl_assert_equal_s(git_object_type2string(1234), "");
+}
+
+void test_object_raw_type2string__convert_string_to_type(void)
+{
+ cl_assert(git_object_string2type(NULL) == GIT_OBJECT_INVALID);
+ cl_assert(git_object_string2type("") == GIT_OBJECT_INVALID);
+ cl_assert(git_object_string2type("commit") == GIT_OBJECT_COMMIT);
+ cl_assert(git_object_string2type("tree") == GIT_OBJECT_TREE);
+ cl_assert(git_object_string2type("blob") == GIT_OBJECT_BLOB);
+ cl_assert(git_object_string2type("tag") == GIT_OBJECT_TAG);
+ cl_assert(git_object_string2type("OFS_DELTA") == GIT_OBJECT_OFS_DELTA);
+ cl_assert(git_object_string2type("REF_DELTA") == GIT_OBJECT_REF_DELTA);
+
+ cl_assert(git_object_string2type("CoMmIt") == GIT_OBJECT_INVALID);
+ cl_assert(git_object_string2type("hohoho") == GIT_OBJECT_INVALID);
+}
+
+void test_object_raw_type2string__check_type_is_loose(void)
+{
+ cl_assert(git_object_typeisloose(GIT_OBJECT_INVALID) == 0);
+ cl_assert(git_object_typeisloose(0) == 0); /* EXT1 */
+ cl_assert(git_object_typeisloose(GIT_OBJECT_COMMIT) == 1);
+ cl_assert(git_object_typeisloose(GIT_OBJECT_TREE) == 1);
+ cl_assert(git_object_typeisloose(GIT_OBJECT_BLOB) == 1);
+ cl_assert(git_object_typeisloose(GIT_OBJECT_TAG) == 1);
+ cl_assert(git_object_typeisloose(5) == 0); /* EXT2 */
+ cl_assert(git_object_typeisloose(GIT_OBJECT_OFS_DELTA) == 0);
+ cl_assert(git_object_typeisloose(GIT_OBJECT_REF_DELTA) == 0);
+
+ cl_assert(git_object_typeisloose(-2) == 0);
+ cl_assert(git_object_typeisloose(8) == 0);
+ cl_assert(git_object_typeisloose(1234) == 0);
+}
diff --git a/tests/libgit2/object/raw/write.c b/tests/libgit2/object/raw/write.c
new file mode 100644
index 0000000..346053d
--- /dev/null
+++ b/tests/libgit2/object/raw/write.c
@@ -0,0 +1,462 @@
+#include "clar_libgit2.h"
+#include "git2/odb_backend.h"
+
+#include "futils.h"
+#include "odb.h"
+
+typedef struct object_data {
+ char *id; /* object id (sha1) */
+ char *dir; /* object store (fan-out) directory name */
+ char *file; /* object store filename */
+} object_data;
+
+static const char *odb_dir = "test-objects";
+
+void test_body(object_data *d, git_rawobj *o);
+
+
+
+/* Helpers */
+static void remove_object_files(object_data *d)
+{
+ cl_git_pass(p_unlink(d->file));
+ cl_git_pass(p_rmdir(d->dir));
+ cl_assert(errno != ENOTEMPTY);
+ cl_git_pass(p_rmdir(odb_dir) < 0);
+}
+
+static void streaming_write(git_oid *oid, git_odb *odb, git_rawobj *raw)
+{
+ git_odb_stream *stream;
+ int error;
+
+ cl_git_pass(git_odb_open_wstream(&stream, odb, raw->len, raw->type));
+ git_odb_stream_write(stream, raw->data, raw->len);
+ error = git_odb_stream_finalize_write(oid, stream);
+ git_odb_stream_free(stream);
+ cl_git_pass(error);
+}
+
+static void check_object_files(object_data *d)
+{
+ cl_assert(git_fs_path_exists(d->dir));
+ cl_assert(git_fs_path_exists(d->file));
+}
+
+static void cmp_objects(git_rawobj *o1, git_rawobj *o2)
+{
+ cl_assert(o1->type == o2->type);
+ cl_assert(o1->len == o2->len);
+ if (o1->len > 0)
+ cl_assert(memcmp(o1->data, o2->data, o1->len) == 0);
+}
+
+static void make_odb_dir(void)
+{
+ cl_git_pass(p_mkdir(odb_dir, GIT_OBJECT_DIR_MODE));
+}
+
+
+/* Standard test form */
+void test_body(object_data *d, git_rawobj *o)
+{
+ git_odb *db;
+ git_oid id1, id2;
+ git_odb_object *obj;
+ git_rawobj tmp;
+
+ make_odb_dir();
+ cl_git_pass(git_odb__open(&db, odb_dir, NULL));
+ cl_git_pass(git_oid__fromstr(&id1, d->id, GIT_OID_SHA1));
+
+ streaming_write(&id2, db, o);
+ cl_assert(git_oid_cmp(&id1, &id2) == 0);
+ check_object_files(d);
+
+ cl_git_pass(git_odb_read(&obj, db, &id1));
+
+ tmp.data = obj->buffer;
+ tmp.len = obj->cached.size;
+ tmp.type = obj->cached.type;
+
+ cmp_objects(&tmp, o);
+
+ git_odb_object_free(obj);
+ git_odb_free(db);
+ remove_object_files(d);
+}
+
+
+void test_object_raw_write__loose_object(void)
+{
+ object_data commit = {
+ "3d7f8a6af076c8c3f20071a8935cdbe8228594d1",
+ "test-objects/3d",
+ "test-objects/3d/7f8a6af076c8c3f20071a8935cdbe8228594d1",
+ };
+
+ unsigned char commit_data[] = {
+ 0x74, 0x72, 0x65, 0x65, 0x20, 0x64, 0x66, 0x66,
+ 0x32, 0x64, 0x61, 0x39, 0x30, 0x62, 0x32, 0x35,
+ 0x34, 0x65, 0x31, 0x62, 0x65, 0x62, 0x38, 0x38,
+ 0x39, 0x64, 0x31, 0x66, 0x31, 0x66, 0x31, 0x32,
+ 0x38, 0x38, 0x62, 0x65, 0x31, 0x38, 0x30, 0x33,
+ 0x37, 0x38, 0x32, 0x64, 0x66, 0x0a, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x20, 0x41, 0x20, 0x55,
+ 0x20, 0x54, 0x68, 0x6f, 0x72, 0x20, 0x3c, 0x61,
+ 0x75, 0x74, 0x68, 0x6f, 0x72, 0x40, 0x65, 0x78,
+ 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x3e, 0x20, 0x31, 0x32, 0x32, 0x37, 0x38,
+ 0x31, 0x34, 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30,
+ 0x30, 0x30, 0x30, 0x0a, 0x63, 0x6f, 0x6d, 0x6d,
+ 0x69, 0x74, 0x74, 0x65, 0x72, 0x20, 0x43, 0x20,
+ 0x4f, 0x20, 0x4d, 0x69, 0x74, 0x74, 0x65, 0x72,
+ 0x20, 0x3c, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
+ 0x74, 0x65, 0x72, 0x40, 0x65, 0x78, 0x61, 0x6d,
+ 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e,
+ 0x20, 0x31, 0x32, 0x32, 0x37, 0x38, 0x31, 0x34,
+ 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30, 0x30, 0x30,
+ 0x30, 0x0a, 0x0a, 0x41, 0x20, 0x6f, 0x6e, 0x65,
+ 0x2d, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x63, 0x6f,
+ 0x6d, 0x6d, 0x69, 0x74, 0x20, 0x73, 0x75, 0x6d,
+ 0x6d, 0x61, 0x72, 0x79, 0x0a, 0x0a, 0x54, 0x68,
+ 0x65, 0x20, 0x62, 0x6f, 0x64, 0x79, 0x20, 0x6f,
+ 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f,
+ 0x6d, 0x6d, 0x69, 0x74, 0x20, 0x6d, 0x65, 0x73,
+ 0x73, 0x61, 0x67, 0x65, 0x2c, 0x20, 0x63, 0x6f,
+ 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67,
+ 0x20, 0x66, 0x75, 0x72, 0x74, 0x68, 0x65, 0x72,
+ 0x20, 0x65, 0x78, 0x70, 0x6c, 0x61, 0x6e, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x6f, 0x66, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x70, 0x75, 0x72, 0x70,
+ 0x6f, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67,
+ 0x65, 0x73, 0x20, 0x69, 0x6e, 0x74, 0x72, 0x6f,
+ 0x64, 0x75, 0x63, 0x65, 0x64, 0x20, 0x62, 0x79,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6d,
+ 0x6d, 0x69, 0x74, 0x2e, 0x0a, 0x0a, 0x53, 0x69,
+ 0x67, 0x6e, 0x65, 0x64, 0x2d, 0x6f, 0x66, 0x2d,
+ 0x62, 0x79, 0x3a, 0x20, 0x41, 0x20, 0x55, 0x20,
+ 0x54, 0x68, 0x6f, 0x72, 0x20, 0x3c, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x40, 0x65, 0x78, 0x61,
+ 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x3e, 0x0a,
+ };
+
+ git_rawobj commit_obj = {
+ commit_data,
+ sizeof(commit_data),
+ GIT_OBJECT_COMMIT
+ };
+
+ test_body(&commit, &commit_obj);
+}
+
+void test_object_raw_write__loose_tree(void)
+{
+ static object_data tree = {
+ "dff2da90b254e1beb889d1f1f1288be1803782df",
+ "test-objects/df",
+ "test-objects/df/f2da90b254e1beb889d1f1f1288be1803782df",
+ };
+
+ static unsigned char tree_data[] = {
+ 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x6f,
+ 0x6e, 0x65, 0x00, 0x8b, 0x13, 0x78, 0x91, 0x79,
+ 0x1f, 0xe9, 0x69, 0x27, 0xad, 0x78, 0xe6, 0x4b,
+ 0x0a, 0xad, 0x7b, 0xde, 0xd0, 0x8b, 0xdc, 0x31,
+ 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x73, 0x6f,
+ 0x6d, 0x65, 0x00, 0xfd, 0x84, 0x30, 0xbc, 0x86,
+ 0x4c, 0xfc, 0xd5, 0xf1, 0x0e, 0x55, 0x90, 0xf8,
+ 0xa4, 0x47, 0xe0, 0x1b, 0x94, 0x2b, 0xfe, 0x31,
+ 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x77,
+ 0x6f, 0x00, 0x78, 0x98, 0x19, 0x22, 0x61, 0x3b,
+ 0x2a, 0xfb, 0x60, 0x25, 0x04, 0x2f, 0xf6, 0xbd,
+ 0x87, 0x8a, 0xc1, 0x99, 0x4e, 0x85, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x7a, 0x65, 0x72,
+ 0x6f, 0x00, 0xe6, 0x9d, 0xe2, 0x9b, 0xb2, 0xd1,
+ 0xd6, 0x43, 0x4b, 0x8b, 0x29, 0xae, 0x77, 0x5a,
+ 0xd8, 0xc2, 0xe4, 0x8c, 0x53, 0x91,
+ };
+
+ static git_rawobj tree_obj = {
+ tree_data,
+ sizeof(tree_data),
+ GIT_OBJECT_TREE
+ };
+
+ test_body(&tree, &tree_obj);
+}
+
+void test_object_raw_write__loose_tag(void)
+{
+ static object_data tag = {
+ "09d373e1dfdc16b129ceec6dd649739911541e05",
+ "test-objects/09",
+ "test-objects/09/d373e1dfdc16b129ceec6dd649739911541e05",
+ };
+
+ static unsigned char tag_data[] = {
+ 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x33,
+ 0x64, 0x37, 0x66, 0x38, 0x61, 0x36, 0x61, 0x66,
+ 0x30, 0x37, 0x36, 0x63, 0x38, 0x63, 0x33, 0x66,
+ 0x32, 0x30, 0x30, 0x37, 0x31, 0x61, 0x38, 0x39,
+ 0x33, 0x35, 0x63, 0x64, 0x62, 0x65, 0x38, 0x32,
+ 0x32, 0x38, 0x35, 0x39, 0x34, 0x64, 0x31, 0x0a,
+ 0x74, 0x79, 0x70, 0x65, 0x20, 0x63, 0x6f, 0x6d,
+ 0x6d, 0x69, 0x74, 0x0a, 0x74, 0x61, 0x67, 0x20,
+ 0x76, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x0a, 0x74,
+ 0x61, 0x67, 0x67, 0x65, 0x72, 0x20, 0x43, 0x20,
+ 0x4f, 0x20, 0x4d, 0x69, 0x74, 0x74, 0x65, 0x72,
+ 0x20, 0x3c, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
+ 0x74, 0x65, 0x72, 0x40, 0x65, 0x78, 0x61, 0x6d,
+ 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e,
+ 0x20, 0x31, 0x32, 0x32, 0x37, 0x38, 0x31, 0x34,
+ 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30, 0x30, 0x30,
+ 0x30, 0x0a, 0x0a, 0x54, 0x68, 0x69, 0x73, 0x20,
+ 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74,
+ 0x61, 0x67, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63,
+ 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x72, 0x65,
+ 0x6c, 0x65, 0x61, 0x73, 0x65, 0x20, 0x76, 0x30,
+ 0x2e, 0x30, 0x2e, 0x31, 0x0a,
+ };
+
+ static git_rawobj tag_obj = {
+ tag_data,
+ sizeof(tag_data),
+ GIT_OBJECT_TAG
+ };
+
+
+ test_body(&tag, &tag_obj);
+}
+
+void test_object_raw_write__zero_length(void)
+{
+ static object_data zero = {
+ "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
+ "test-objects/e6",
+ "test-objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391",
+ };
+
+ static unsigned char zero_data[] = {
+ 0x00 /* dummy data */
+ };
+
+ static git_rawobj zero_obj = {
+ zero_data,
+ 0,
+ GIT_OBJECT_BLOB
+ };
+
+ test_body(&zero, &zero_obj);
+}
+
+void test_object_raw_write__one_byte(void)
+{
+ static object_data one = {
+ "8b137891791fe96927ad78e64b0aad7bded08bdc",
+ "test-objects/8b",
+ "test-objects/8b/137891791fe96927ad78e64b0aad7bded08bdc",
+ };
+
+ static unsigned char one_data[] = {
+ 0x0a,
+ };
+
+ static git_rawobj one_obj = {
+ one_data,
+ sizeof(one_data),
+ GIT_OBJECT_BLOB
+ };
+
+ test_body(&one, &one_obj);
+}
+
+void test_object_raw_write__two_byte(void)
+{
+ static object_data two = {
+ "78981922613b2afb6025042ff6bd878ac1994e85",
+ "test-objects/78",
+ "test-objects/78/981922613b2afb6025042ff6bd878ac1994e85",
+ };
+
+ static unsigned char two_data[] = {
+ 0x61, 0x0a,
+ };
+
+ static git_rawobj two_obj = {
+ two_data,
+ sizeof(two_data),
+ GIT_OBJECT_BLOB
+ };
+
+ test_body(&two, &two_obj);
+}
+
+void test_object_raw_write__several_bytes(void)
+{
+ static object_data some = {
+ "fd8430bc864cfcd5f10e5590f8a447e01b942bfe",
+ "test-objects/fd",
+ "test-objects/fd/8430bc864cfcd5f10e5590f8a447e01b942bfe",
+ };
+
+ static unsigned char some_data[] = {
+ 0x2f, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x54, 0x68,
+ 0x69, 0x73, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20,
+ 0x69, 0x73, 0x20, 0x66, 0x72, 0x65, 0x65, 0x20,
+ 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65,
+ 0x3b, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x63, 0x61,
+ 0x6e, 0x20, 0x72, 0x65, 0x64, 0x69, 0x73, 0x74,
+ 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x69,
+ 0x74, 0x20, 0x61, 0x6e, 0x64, 0x2f, 0x6f, 0x72,
+ 0x20, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0a,
+ 0x20, 0x2a, 0x20, 0x69, 0x74, 0x20, 0x75, 0x6e,
+ 0x64, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x74, 0x65, 0x72, 0x6d, 0x73, 0x20, 0x6f, 0x66,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x47, 0x4e, 0x55,
+ 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
+ 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x2c,
+ 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+ 0x20, 0x32, 0x2c, 0x0a, 0x20, 0x2a, 0x20, 0x61,
+ 0x73, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73,
+ 0x68, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x46, 0x72, 0x65, 0x65, 0x20,
+ 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65,
+ 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x2a, 0x0a,
+ 0x20, 0x2a, 0x20, 0x49, 0x6e, 0x20, 0x61, 0x64,
+ 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74,
+ 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x65,
+ 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e,
+ 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x47, 0x4e, 0x55, 0x20, 0x47, 0x65, 0x6e,
+ 0x65, 0x72, 0x61, 0x6c, 0x20, 0x50, 0x75, 0x62,
+ 0x6c, 0x69, 0x63, 0x20, 0x4c, 0x69, 0x63, 0x65,
+ 0x6e, 0x73, 0x65, 0x2c, 0x0a, 0x20, 0x2a, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x61, 0x75, 0x74, 0x68,
+ 0x6f, 0x72, 0x73, 0x20, 0x67, 0x69, 0x76, 0x65,
+ 0x20, 0x79, 0x6f, 0x75, 0x20, 0x75, 0x6e, 0x6c,
+ 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x20, 0x70,
+ 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f,
+ 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x6c, 0x69, 0x6e,
+ 0x6b, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f,
+ 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x64, 0x0a, 0x20,
+ 0x2a, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
+ 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x69,
+ 0x73, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x69,
+ 0x6e, 0x74, 0x6f, 0x20, 0x63, 0x6f, 0x6d, 0x62,
+ 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+ 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6f, 0x74,
+ 0x68, 0x65, 0x72, 0x20, 0x70, 0x72, 0x6f, 0x67,
+ 0x72, 0x61, 0x6d, 0x73, 0x2c, 0x0a, 0x20, 0x2a,
+ 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x6f, 0x20,
+ 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75,
+ 0x74, 0x65, 0x20, 0x74, 0x68, 0x6f, 0x73, 0x65,
+ 0x20, 0x63, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x77, 0x69,
+ 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x61, 0x6e,
+ 0x79, 0x20, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x2a,
+ 0x20, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x20,
+ 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x66, 0x69, 0x6c,
+ 0x65, 0x2e, 0x20, 0x20, 0x28, 0x54, 0x68, 0x65,
+ 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
+ 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x0a,
+ 0x20, 0x2a, 0x20, 0x72, 0x65, 0x73, 0x74, 0x72,
+ 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20,
+ 0x64, 0x6f, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x79,
+ 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x74, 0x68, 0x65,
+ 0x72, 0x20, 0x72, 0x65, 0x73, 0x70, 0x65, 0x63,
+ 0x74, 0x73, 0x3b, 0x20, 0x66, 0x6f, 0x72, 0x20,
+ 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2c,
+ 0x20, 0x74, 0x68, 0x65, 0x79, 0x20, 0x63, 0x6f,
+ 0x76, 0x65, 0x72, 0x0a, 0x20, 0x2a, 0x20, 0x6d,
+ 0x6f, 0x64, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x2c,
+ 0x20, 0x61, 0x6e, 0x64, 0x20, 0x64, 0x69, 0x73,
+ 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x6e,
+ 0x6f, 0x74, 0x20, 0x6c, 0x69, 0x6e, 0x6b, 0x65,
+ 0x64, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x0a, 0x20,
+ 0x2a, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x62,
+ 0x69, 0x6e, 0x65, 0x64, 0x20, 0x65, 0x78, 0x65,
+ 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2e,
+ 0x29, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20,
+ 0x54, 0x68, 0x69, 0x73, 0x20, 0x66, 0x69, 0x6c,
+ 0x65, 0x20, 0x69, 0x73, 0x20, 0x64, 0x69, 0x73,
+ 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x64,
+ 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x68, 0x6f, 0x70, 0x65, 0x20, 0x74, 0x68, 0x61,
+ 0x74, 0x20, 0x69, 0x74, 0x20, 0x77, 0x69, 0x6c,
+ 0x6c, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65,
+ 0x66, 0x75, 0x6c, 0x2c, 0x20, 0x62, 0x75, 0x74,
+ 0x0a, 0x20, 0x2a, 0x20, 0x57, 0x49, 0x54, 0x48,
+ 0x4f, 0x55, 0x54, 0x20, 0x41, 0x4e, 0x59, 0x20,
+ 0x57, 0x41, 0x52, 0x52, 0x41, 0x4e, 0x54, 0x59,
+ 0x3b, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75,
+ 0x74, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x69,
+ 0x65, 0x64, 0x20, 0x77, 0x61, 0x72, 0x72, 0x61,
+ 0x6e, 0x74, 0x79, 0x20, 0x6f, 0x66, 0x0a, 0x20,
+ 0x2a, 0x20, 0x4d, 0x45, 0x52, 0x43, 0x48, 0x41,
+ 0x4e, 0x54, 0x41, 0x42, 0x49, 0x4c, 0x49, 0x54,
+ 0x59, 0x20, 0x6f, 0x72, 0x20, 0x46, 0x49, 0x54,
+ 0x4e, 0x45, 0x53, 0x53, 0x20, 0x46, 0x4f, 0x52,
+ 0x20, 0x41, 0x20, 0x50, 0x41, 0x52, 0x54, 0x49,
+ 0x43, 0x55, 0x4c, 0x41, 0x52, 0x20, 0x50, 0x55,
+ 0x52, 0x50, 0x4f, 0x53, 0x45, 0x2e, 0x20, 0x20,
+ 0x53, 0x65, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x47, 0x4e, 0x55, 0x0a, 0x20, 0x2a, 0x20, 0x47,
+ 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x20, 0x50,
+ 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x4c, 0x69,
+ 0x63, 0x65, 0x6e, 0x73, 0x65, 0x20, 0x66, 0x6f,
+ 0x72, 0x20, 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x64,
+ 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x0a,
+ 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x59, 0x6f,
+ 0x75, 0x20, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64,
+ 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x72, 0x65,
+ 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x20, 0x61,
+ 0x20, 0x63, 0x6f, 0x70, 0x79, 0x20, 0x6f, 0x66,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x47, 0x4e, 0x55,
+ 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
+ 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x0a,
+ 0x20, 0x2a, 0x20, 0x61, 0x6c, 0x6f, 0x6e, 0x67,
+ 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68,
+ 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72,
+ 0x61, 0x6d, 0x3b, 0x20, 0x73, 0x65, 0x65, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x6c, 0x65,
+ 0x20, 0x43, 0x4f, 0x50, 0x59, 0x49, 0x4e, 0x47,
+ 0x2e, 0x20, 0x20, 0x49, 0x66, 0x20, 0x6e, 0x6f,
+ 0x74, 0x2c, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65,
+ 0x20, 0x74, 0x6f, 0x0a, 0x20, 0x2a, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x46, 0x72, 0x65, 0x65, 0x20,
+ 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65,
+ 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x35, 0x31, 0x20,
+ 0x46, 0x72, 0x61, 0x6e, 0x6b, 0x6c, 0x69, 0x6e,
+ 0x20, 0x53, 0x74, 0x72, 0x65, 0x65, 0x74, 0x2c,
+ 0x20, 0x46, 0x69, 0x66, 0x74, 0x68, 0x20, 0x46,
+ 0x6c, 0x6f, 0x6f, 0x72, 0x2c, 0x0a, 0x20, 0x2a,
+ 0x20, 0x42, 0x6f, 0x73, 0x74, 0x6f, 0x6e, 0x2c,
+ 0x20, 0x4d, 0x41, 0x20, 0x30, 0x32, 0x31, 0x31,
+ 0x30, 0x2d, 0x31, 0x33, 0x30, 0x31, 0x2c, 0x20,
+ 0x55, 0x53, 0x41, 0x2e, 0x0a, 0x20, 0x2a, 0x2f,
+ 0x0a,
+ };
+
+ static git_rawobj some_obj = {
+ some_data,
+ sizeof(some_data),
+ GIT_OBJECT_BLOB
+ };
+
+ test_body(&some, &some_obj);
+}
diff --git a/tests/libgit2/object/shortid.c b/tests/libgit2/object/shortid.c
new file mode 100644
index 0000000..69fceee
--- /dev/null
+++ b/tests/libgit2/object/shortid.c
@@ -0,0 +1,51 @@
+#include "clar_libgit2.h"
+
+git_repository *_repo;
+
+void test_object_shortid__initialize(void)
+{
+ cl_git_pass(git_repository_open(&_repo, cl_fixture("duplicate.git")));
+}
+
+void test_object_shortid__cleanup(void)
+{
+ git_repository_free(_repo);
+ _repo = NULL;
+}
+
+void test_object_shortid__select(void)
+{
+ git_oid full;
+ git_object *obj;
+ git_buf shorty = {0};
+
+ git_oid__fromstr(&full, "ce013625030ba8dba906f756967f9e9ca394464a", GIT_OID_SHA1);
+ cl_git_pass(git_object_lookup(&obj, _repo, &full, GIT_OBJECT_ANY));
+ cl_git_pass(git_object_short_id(&shorty, obj));
+ cl_assert_equal_i(7, shorty.size);
+ cl_assert_equal_s("ce01362", shorty.ptr);
+ git_object_free(obj);
+
+ git_oid__fromstr(&full, "038d718da6a1ebbc6a7780a96ed75a70cc2ad6e2", GIT_OID_SHA1);
+ cl_git_pass(git_object_lookup(&obj, _repo, &full, GIT_OBJECT_ANY));
+ cl_git_pass(git_object_short_id(&shorty, obj));
+ cl_assert_equal_i(7, shorty.size);
+ cl_assert_equal_s("038d718", shorty.ptr);
+ git_object_free(obj);
+
+ git_oid__fromstr(&full, "dea509d097ce692e167dfc6a48a7a280cc5e877e", GIT_OID_SHA1);
+ cl_git_pass(git_object_lookup(&obj, _repo, &full, GIT_OBJECT_ANY));
+ cl_git_pass(git_object_short_id(&shorty, obj));
+ cl_assert_equal_i(9, shorty.size);
+ cl_assert_equal_s("dea509d09", shorty.ptr);
+ git_object_free(obj);
+
+ git_oid__fromstr(&full, "dea509d0b3cb8ee0650f6ca210bc83f4678851ba", GIT_OID_SHA1);
+ cl_git_pass(git_object_lookup(&obj, _repo, &full, GIT_OBJECT_ANY));
+ cl_git_pass(git_object_short_id(&shorty, obj));
+ cl_assert_equal_i(9, shorty.size);
+ cl_assert_equal_s("dea509d0b", shorty.ptr);
+ git_object_free(obj);
+
+ git_buf_dispose(&shorty);
+}
diff --git a/tests/libgit2/object/tag/list.c b/tests/libgit2/object/tag/list.c
new file mode 100644
index 0000000..d15f092
--- /dev/null
+++ b/tests/libgit2/object/tag/list.c
@@ -0,0 +1,117 @@
+#include "clar_libgit2.h"
+
+#include "tag.h"
+
+static git_repository *g_repo;
+
+#define MAX_USED_TAGS 6
+
+struct pattern_match_t
+{
+ const char* pattern;
+ const size_t expected_matches;
+ const char* expected_results[MAX_USED_TAGS];
+};
+
+/* Helpers */
+static void ensure_tag_pattern_match(git_repository *repo,
+ const struct pattern_match_t* data)
+{
+ int already_found[MAX_USED_TAGS] = { 0 };
+ git_strarray tag_list;
+ int error = 0;
+ size_t successfully_found = 0;
+ size_t i, j;
+
+ cl_assert(data->expected_matches <= MAX_USED_TAGS);
+
+ if ((error = git_tag_list_match(&tag_list, data->pattern, repo)) < 0)
+ goto exit;
+
+ if (tag_list.count != data->expected_matches)
+ {
+ error = GIT_ERROR;
+ goto exit;
+ }
+
+ /* we have to be prepared that tags come in any order. */
+ for (i = 0; i < tag_list.count; i++)
+ {
+ for (j = 0; j < data->expected_matches; j++)
+ {
+ if (!already_found[j] && !strcmp(data->expected_results[j], tag_list.strings[i]))
+ {
+ already_found[j] = 1;
+ successfully_found++;
+ break;
+ }
+ }
+ }
+ cl_assert_equal_i((int)successfully_found, (int)data->expected_matches);
+
+exit:
+ git_strarray_dispose(&tag_list);
+ cl_git_pass(error);
+}
+
+/* Fixture setup and teardown */
+void test_object_tag_list__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_object_tag_list__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_object_tag_list__list_all(void)
+{
+ /* list all tag names from the repository */
+ git_strarray tag_list;
+
+ cl_git_pass(git_tag_list(&tag_list, g_repo));
+
+ cl_assert_equal_i((int)tag_list.count, 6);
+
+ git_strarray_dispose(&tag_list);
+}
+
+static const struct pattern_match_t matches[] = {
+ /* All tags, including a packed one and two namespaced ones. */
+ { "", 6, { "e90810b", "point_to_blob", "test", "packed-tag", "foo/bar", "foo/foo/bar" } },
+
+ /* beginning with */
+ { "t*", 1, { "test" } },
+
+ /* ending with */
+ { "*b", 2, { "e90810b", "point_to_blob" } },
+
+ /* exact match */
+ { "e", 0 },
+ { "e90810b", 1, { "e90810b" } },
+
+ /* either or */
+ { "e90810[ab]", 1, { "e90810b" } },
+
+ /* glob in the middle */
+ { "foo/*/bar", 1, { "foo/foo/bar" } },
+
+ /*
+ * The matching of '*' is based on plain string matching analog to the regular expression ".*"
+ * => a '/' in the tag name has no special meaning.
+ * Compare to `git tag -l "*bar"`
+ */
+ { "*bar", 2, { "foo/bar", "foo/foo/bar" } },
+
+ /* End of list */
+ { NULL }
+};
+
+void test_object_tag_list__list_by_pattern(void)
+{
+ /* list all tag names from the repository matching a specified pattern */
+ size_t i = 0;
+ while (matches[i].pattern)
+ ensure_tag_pattern_match(g_repo, &matches[i++]);
+}
diff --git a/tests/libgit2/object/tag/parse.c b/tests/libgit2/object/tag/parse.c
new file mode 100644
index 0000000..d7a4d85
--- /dev/null
+++ b/tests/libgit2/object/tag/parse.c
@@ -0,0 +1,218 @@
+#include "clar_libgit2.h"
+#include "object.h"
+#include "signature.h"
+#include "tag.h"
+
+static void assert_tag_parses(const char *data, size_t datalen,
+ const char *expected_oid,
+ const char *expected_name,
+ const char *expected_tagger,
+ const char *expected_message)
+{
+ git_tag *tag;
+
+ if (!datalen)
+ datalen = strlen(data);
+
+ cl_git_pass(git_object__from_raw((git_object **) &tag, data, datalen, GIT_OBJECT_TAG, GIT_OID_SHA1));
+ cl_assert_equal_i(tag->type, GIT_OBJECT_TAG);
+
+ if (expected_oid) {
+ git_oid oid;
+ cl_git_pass(git_oid__fromstr(&oid, expected_oid, GIT_OID_SHA1));
+ cl_assert_equal_oid(&oid, &tag->target);
+ }
+
+ if (expected_name)
+ cl_assert_equal_s(expected_name, tag->tag_name);
+ else
+ cl_assert_equal_s(tag->message, NULL);
+
+ if (expected_tagger) {
+ git_signature *tagger;
+ cl_git_pass(git_signature_from_buffer(&tagger, expected_tagger));
+ cl_assert_equal_s(tagger->name, tag->tagger->name);
+ cl_assert_equal_s(tagger->email, tag->tagger->email);
+ cl_assert_equal_i(tagger->when.time, tag->tagger->when.time);
+ cl_assert_equal_i(tagger->when.offset, tag->tagger->when.offset);
+ cl_assert_equal_i(tagger->when.sign, tag->tagger->when.sign);
+ git_signature_free(tagger);
+ } else {
+ cl_assert_equal_s(tag->tagger, NULL);
+ }
+
+ if (expected_message)
+ cl_assert_equal_s(expected_message, tag->message);
+ else
+ cl_assert_equal_s(tag->message, NULL);
+
+ git_object__free(&tag->object);
+}
+
+static void assert_tag_fails(const char *data, size_t datalen)
+{
+ git_object *object;
+ if (!datalen)
+ datalen = strlen(data);
+ cl_git_fail(git_object__from_raw(&object, data, datalen, GIT_OBJECT_TAG, GIT_OID_SHA1));
+}
+
+void test_object_tag_parse__valid_tag_parses(void)
+{
+ const char *tag =
+ "object a8d447f68076d1520f69649bb52629941be7031f\n"
+ "type tag\n"
+ "tag tagname\n"
+ "tagger Taggy Mr. Taggart <taggy@taggart.com>\n"
+ "\n"
+ "Message";
+ assert_tag_parses(tag, 0,
+ "a8d447f68076d1520f69649bb52629941be7031f",
+ "tagname",
+ "Taggy Mr. Taggart <taggy@taggart.com>",
+ "Message");
+}
+
+void test_object_tag_parse__missing_tagger_parses(void)
+{
+ const char *tag =
+ "object a8d447f68076d1520f69649bb52629941be7031f\n"
+ "type tag\n"
+ "tag tagname\n"
+ "\n"
+ "Message";
+ assert_tag_parses(tag, 0,
+ "a8d447f68076d1520f69649bb52629941be7031f",
+ "tagname",
+ NULL,
+ "Message");
+}
+
+void test_object_tag_parse__missing_message_parses(void)
+{
+ const char *tag =
+ "object a8d447f68076d1520f69649bb52629941be7031f\n"
+ "type tag\n"
+ "tag tagname\n"
+ "tagger Taggy Mr. Taggart <taggy@taggart.com>\n";
+ assert_tag_parses(tag, 0,
+ "a8d447f68076d1520f69649bb52629941be7031f",
+ "tagname",
+ "Taggy Mr. Taggart <taggy@taggart.com>",
+ NULL);
+}
+
+void test_object_tag_parse__unknown_field_parses(void)
+{
+ const char *tag =
+ "object a8d447f68076d1520f69649bb52629941be7031f\n"
+ "type tag\n"
+ "tag tagname\n"
+ "tagger Taggy Mr. Taggart <taggy@taggart.com>\n"
+ "foo bar\n"
+ "frubble frabble\n"
+ "\n"
+ "Message";
+ assert_tag_parses(tag, 0,
+ "a8d447f68076d1520f69649bb52629941be7031f",
+ "tagname",
+ "Taggy Mr. Taggart <taggy@taggart.com>",
+ "Message");
+}
+
+void test_object_tag_parse__missing_object_fails(void)
+{
+ const char *tag =
+ "type tag\n"
+ "tag tagname\n"
+ "tagger Taggy Mr. Taggart <taggy@taggart.com>\n"
+ "\n"
+ "Message";
+ assert_tag_fails(tag, 0);
+}
+
+void test_object_tag_parse__malformatted_object_fails(void)
+{
+ const char *tag =
+ "object a8d447f68076d15xxxxxxxxxxxxxxxx41be7031f\n"
+ "type tag\n"
+ "tag tagname\n"
+ "tagger Taggy Mr. Taggart <taggy@taggart.com>\n"
+ "\n"
+ "Message";
+ assert_tag_fails(tag, 0);
+}
+
+void test_object_tag_parse__missing_type_fails(void)
+{
+ const char *tag =
+ "object a8d447f68076d1520f69649bb52629941be7031f\n"
+ "tag tagname\n"
+ "tagger Taggy Mr. Taggart <taggy@taggart.com>\n"
+ "\n"
+ "Message";
+ assert_tag_fails(tag, 0);
+}
+
+void test_object_tag_parse__invalid_type_fails(void)
+{
+ const char *tag =
+ "object a8d447f68076d1520f69649bb52629941be7031f\n"
+ "type garbage\n"
+ "tag tagname\n"
+ "tagger Taggy Mr. Taggart <taggy@taggart.com>\n"
+ "\n"
+ "Message";
+ assert_tag_fails(tag, 0);
+}
+
+void test_object_tag_parse__missing_tagname_fails(void)
+{
+ const char *tag =
+ "object a8d447f68076d1520f69649bb52629941be7031f\n"
+ "type tag\n"
+ "tagger Taggy Mr. Taggart <taggy@taggart.com>\n"
+ "\n"
+ "Message";
+ assert_tag_fails(tag, 0);
+}
+
+void test_object_tag_parse__misformatted_tagger_fails(void)
+{
+ const char *tag =
+ "object a8d447f68076d1520f69649bb52629941be7031f\n"
+ "type tag\n"
+ "tag Tag\n"
+ "tagger taggy@taggart.com>\n"
+ "\n"
+ "Message";
+ assert_tag_fails(tag, 0);
+}
+
+void test_object_tag_parse__missing_message_fails(void)
+{
+ const char *tag =
+ "object a8d447f68076d1520f69649bb52629941be7031f\n"
+ "type tag\n"
+ "tag Tag\n"
+ "tagger taggy@taggart.com>\n";
+ assert_tag_fails(tag, 0);
+}
+
+void test_object_tag_parse__no_oob_read_when_searching_message(void)
+{
+ const char *tag =
+ "object a8d447f68076d1520f69649bb52629941be7031f\n"
+ "type tag\n"
+ "tag \n"
+ "tagger <>\n"
+ " \n\n"
+ "Message";
+ /*
+ * The OOB read previously resulted in an OOM error. We
+ * thus want to make sure that the resulting error is the
+ * expected one.
+ */
+ assert_tag_fails(tag, strlen(tag) - strlen("\n\nMessage"));
+ cl_assert(strstr(git_error_last()->message, "tag contains no message"));
+}
diff --git a/tests/libgit2/object/tag/peel.c b/tests/libgit2/object/tag/peel.c
new file mode 100644
index 0000000..db81c5a
--- /dev/null
+++ b/tests/libgit2/object/tag/peel.c
@@ -0,0 +1,61 @@
+#include "clar_libgit2.h"
+#include "tag.h"
+
+static git_repository *repo;
+static git_tag *tag;
+static git_object *target;
+
+void test_object_tag_peel__initialize(void)
+{
+ cl_fixture_sandbox("testrepo.git");
+ cl_git_pass(git_repository_open(&repo, "testrepo.git"));
+}
+
+void test_object_tag_peel__cleanup(void)
+{
+ git_tag_free(tag);
+ tag = NULL;
+
+ git_object_free(target);
+ target = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+
+ cl_fixture_cleanup("testrepo.git");
+}
+
+static void retrieve_tag_from_oid(git_tag **tag_out, git_repository *repo, const char *sha)
+{
+ git_oid oid;
+
+ cl_git_pass(git_oid__fromstr(&oid, sha, GIT_OID_SHA1));
+ cl_git_pass(git_tag_lookup(tag_out, repo, &oid));
+}
+
+void test_object_tag_peel__can_peel_to_a_commit(void)
+{
+ retrieve_tag_from_oid(&tag, repo, "7b4384978d2493e851f9cca7858815fac9b10980");
+
+ cl_git_pass(git_tag_peel(&target, tag));
+ cl_assert(git_object_type(target) == GIT_OBJECT_COMMIT);
+ cl_git_pass(git_oid_streq(git_object_id(target), "e90810b8df3e80c413d903f631643c716887138d"));
+}
+
+void test_object_tag_peel__can_peel_several_nested_tags_to_a_commit(void)
+{
+ retrieve_tag_from_oid(&tag, repo, "b25fa35b38051e4ae45d4222e795f9df2e43f1d1");
+
+ cl_git_pass(git_tag_peel(&target, tag));
+ cl_assert(git_object_type(target) == GIT_OBJECT_COMMIT);
+ cl_git_pass(git_oid_streq(git_object_id(target), "e90810b8df3e80c413d903f631643c716887138d"));
+}
+
+void test_object_tag_peel__can_peel_to_a_non_commit(void)
+{
+ retrieve_tag_from_oid(&tag, repo, "521d87c1ec3aef9824daf6d96cc0ae3710766d91");
+
+ cl_git_pass(git_tag_peel(&target, tag));
+ cl_assert(git_object_type(target) == GIT_OBJECT_BLOB);
+ cl_git_pass(git_oid_streq(git_object_id(target), "1385f264afb75a56a5bec74243be9b367ba4ca08"));
+}
diff --git a/tests/libgit2/object/tag/read.c b/tests/libgit2/object/tag/read.c
new file mode 100644
index 0000000..6270b56
--- /dev/null
+++ b/tests/libgit2/object/tag/read.c
@@ -0,0 +1,179 @@
+#include "clar_libgit2.h"
+
+#include "tag.h"
+
+static const char *tag1_id = "b25fa35b38051e4ae45d4222e795f9df2e43f1d1";
+static const char *tag2_id = "7b4384978d2493e851f9cca7858815fac9b10980";
+static const char *tagged_commit = "e90810b8df3e80c413d903f631643c716887138d";
+static const char *bad_tag_id = "eda9f45a2a98d4c17a09d681d88569fa4ea91755";
+static const char *badly_tagged_commit = "e90810b8df3e80c413d903f631643c716887138d";
+static const char *short_tag_id = "5da7760512a953e3c7c4e47e4392c7a4338fb729";
+static const char *short_tagged_commit = "4a5ed60bafcf4638b7c8356bd4ce1916bfede93c";
+static const char *taggerless = "4a23e2e65ad4e31c4c9db7dc746650bfad082679";
+
+static git_repository *g_repo;
+
+/* Fixture setup and teardown */
+void test_object_tag_read__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_object_tag_read__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+
+void test_object_tag_read__parse(void)
+{
+ /* read and parse a tag from the repository */
+ git_tag *tag1, *tag2;
+ git_commit *commit;
+ git_oid id1, id2, id_commit;
+
+ git_oid__fromstr(&id1, tag1_id, GIT_OID_SHA1);
+ git_oid__fromstr(&id2, tag2_id, GIT_OID_SHA1);
+ git_oid__fromstr(&id_commit, tagged_commit, GIT_OID_SHA1);
+
+ cl_git_pass(git_tag_lookup(&tag1, g_repo, &id1));
+
+ cl_assert_equal_s(git_tag_name(tag1), "test");
+ cl_assert(git_tag_target_type(tag1) == GIT_OBJECT_TAG);
+
+ cl_git_pass(git_tag_target((git_object **)&tag2, tag1));
+ cl_assert(tag2 != NULL);
+
+ cl_assert(git_oid_cmp(&id2, git_tag_id(tag2)) == 0);
+
+ cl_git_pass(git_tag_target((git_object **)&commit, tag2));
+ cl_assert(commit != NULL);
+
+ cl_assert(git_oid_cmp(&id_commit, git_commit_id(commit)) == 0);
+
+ git_tag_free(tag1);
+ git_tag_free(tag2);
+ git_commit_free(commit);
+}
+
+void test_object_tag_read__parse_without_tagger(void)
+{
+ /* read and parse a tag without a tagger field */
+ git_repository *bad_tag_repo;
+ git_tag *bad_tag;
+ git_commit *commit;
+ git_oid id, id_commit;
+
+ /* TODO: This is a little messy */
+ cl_git_pass(git_repository_open(&bad_tag_repo, cl_fixture("bad_tag.git")));
+
+ git_oid__fromstr(&id, bad_tag_id, GIT_OID_SHA1);
+ git_oid__fromstr(&id_commit, badly_tagged_commit, GIT_OID_SHA1);
+
+ cl_git_pass(git_tag_lookup(&bad_tag, bad_tag_repo, &id));
+ cl_assert(bad_tag != NULL);
+
+ cl_assert_equal_s(git_tag_name(bad_tag), "e90810b");
+ cl_assert(git_oid_cmp(&id, git_tag_id(bad_tag)) == 0);
+ cl_assert(bad_tag->tagger == NULL);
+
+ cl_git_pass(git_tag_target((git_object **)&commit, bad_tag));
+ cl_assert(commit != NULL);
+
+ cl_assert(git_oid_cmp(&id_commit, git_commit_id(commit)) == 0);
+
+
+ git_tag_free(bad_tag);
+ git_commit_free(commit);
+ git_repository_free(bad_tag_repo);
+}
+
+void test_object_tag_read__parse_without_message(void)
+{
+ /* read and parse a tag without a message field */
+ git_repository *short_tag_repo;
+ git_tag *short_tag;
+ git_commit *commit;
+ git_oid id, id_commit;
+
+ /* TODO: This is a little messy */
+ cl_git_pass(git_repository_open(&short_tag_repo, cl_fixture("short_tag.git")));
+
+ git_oid__fromstr(&id, short_tag_id, GIT_OID_SHA1);
+ git_oid__fromstr(&id_commit, short_tagged_commit, GIT_OID_SHA1);
+
+ cl_git_pass(git_tag_lookup(&short_tag, short_tag_repo, &id));
+ cl_assert(short_tag != NULL);
+
+ cl_assert_equal_s(git_tag_name(short_tag), "no_description");
+ cl_assert(git_oid_cmp(&id, git_tag_id(short_tag)) == 0);
+ cl_assert(short_tag->message == NULL);
+
+ cl_git_pass(git_tag_target((git_object **)&commit, short_tag));
+ cl_assert(commit != NULL);
+
+ cl_assert(git_oid_cmp(&id_commit, git_commit_id(commit)) == 0);
+
+ git_tag_free(short_tag);
+ git_commit_free(commit);
+ git_repository_free(short_tag_repo);
+}
+
+void test_object_tag_read__without_tagger_nor_message(void)
+{
+ git_tag *tag;
+ git_oid id;
+ git_repository *repo;
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+
+ cl_git_pass(git_oid__fromstr(&id, taggerless, GIT_OID_SHA1));
+
+ cl_git_pass(git_tag_lookup(&tag, repo, &id));
+
+ cl_assert_equal_s(git_tag_name(tag), "taggerless");
+ cl_assert(git_tag_target_type(tag) == GIT_OBJECT_COMMIT);
+
+ cl_assert(tag->message == NULL);
+ cl_assert(tag->tagger == NULL);
+
+ git_tag_free(tag);
+ git_repository_free(repo);
+}
+
+static const char *silly_tag = "object c054ccaefbf2da31c3b19178f9e3ef20a3867924\n\
+type commit\n\
+tag v1_0_1\n\
+tagger Jamis Buck <jamis@37signals.com> 1107717917\n\
+diff --git a/lib/sqlite3/version.rb b/lib/sqlite3/version.rb\n\
+index 0b3bf69..4ee8fc2 100644\n\
+--- a/lib/sqlite3/version.rb\n\
++++ b/lib/sqlite3/version.rb\n\
+@@ -36,7 +36,7 @@ module SQLite3\n\
+ \n\
+ MAJOR = 1\n\
+ MINOR = 0\n\
+- TINY = 0\n\
++ TINY = 1\n\
+ \n\
+ STRING = [ MAJOR, MINOR, TINY ].join( \".\" )\n\
+ \n\
+ -0600\n\
+\n\
+v1_0_1 release\n";
+
+void test_object_tag_read__extra_header_fields(void)
+{
+ git_tag *tag;
+ git_odb *odb;
+ git_oid id;
+
+ cl_git_pass(git_repository_odb__weakptr(&odb, g_repo));
+
+ cl_git_pass(git_odb_write(&id, odb, silly_tag, strlen(silly_tag), GIT_OBJECT_TAG));
+ cl_git_pass(git_tag_lookup(&tag, g_repo, &id));
+
+ cl_assert_equal_s("v1_0_1 release\n", git_tag_message(tag));
+
+ git_tag_free(tag);
+}
diff --git a/tests/libgit2/object/tag/write.c b/tests/libgit2/object/tag/write.c
new file mode 100644
index 0000000..79e0145
--- /dev/null
+++ b/tests/libgit2/object/tag/write.c
@@ -0,0 +1,279 @@
+#include "clar_libgit2.h"
+
+static const char* tagger_name = "Vicent Marti";
+static const char* tagger_email = "vicent@github.com";
+static const char* tagger_message = "This is my tag.\n\nThere are many tags, but this one is mine\n";
+
+static const char *tag2_id = "7b4384978d2493e851f9cca7858815fac9b10980";
+static const char *tagged_commit = "e90810b8df3e80c413d903f631643c716887138d";
+
+static git_repository *g_repo;
+
+/* Fixture setup and teardown */
+void test_object_tag_write__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_object_tag_write__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_object_tag_write__basic(void)
+{
+ /* write a tag to the repository and read it again */
+ git_tag *tag;
+ git_oid target_id, tag_id;
+ git_signature *tagger;
+ const git_signature *tagger1;
+ git_reference *ref_tag;
+ git_object *target;
+
+ git_oid__fromstr(&target_id, tagged_commit, GIT_OID_SHA1);
+ cl_git_pass(git_object_lookup(&target, g_repo, &target_id, GIT_OBJECT_COMMIT));
+
+ /* create signature */
+ cl_git_pass(git_signature_new(&tagger, tagger_name, tagger_email, 123456789, 60));
+
+ cl_git_pass(
+ git_tag_create(&tag_id, g_repo,
+ "the-tag", target, tagger, tagger_message, 0)
+ );
+
+ git_object_free(target);
+ git_signature_free(tagger);
+
+ cl_git_pass(git_tag_lookup(&tag, g_repo, &tag_id));
+ cl_assert(git_oid_cmp(git_tag_target_id(tag), &target_id) == 0);
+
+ /* Check attributes were set correctly */
+ tagger1 = git_tag_tagger(tag);
+ cl_assert(tagger1 != NULL);
+ cl_assert_equal_s(tagger1->name, tagger_name);
+ cl_assert_equal_s(tagger1->email, tagger_email);
+ cl_assert(tagger1->when.time == 123456789);
+ cl_assert(tagger1->when.offset == 60);
+
+ cl_assert_equal_s(git_tag_message(tag), tagger_message);
+
+ cl_git_pass(git_reference_lookup(&ref_tag, g_repo, "refs/tags/the-tag"));
+ cl_assert(git_oid_cmp(git_reference_target(ref_tag), &tag_id) == 0);
+ cl_git_pass(git_reference_delete(ref_tag));
+ git_reference_free(ref_tag);
+
+ git_tag_free(tag);
+}
+
+void test_object_tag_write__overwrite(void)
+{
+ /* Attempt to write a tag bearing the same name than an already existing tag */
+ git_oid target_id, tag_id;
+ git_signature *tagger;
+ git_object *target;
+
+ git_oid__fromstr(&target_id, tagged_commit, GIT_OID_SHA1);
+ cl_git_pass(git_object_lookup(&target, g_repo, &target_id, GIT_OBJECT_COMMIT));
+
+ /* create signature */
+ cl_git_pass(git_signature_new(&tagger, tagger_name, tagger_email, 123456789, 60));
+
+ cl_assert_equal_i(GIT_EEXISTS, git_tag_create(
+ &tag_id, /* out id */
+ g_repo,
+ "e90810b",
+ target,
+ tagger,
+ tagger_message,
+ 0));
+
+ git_object_free(target);
+ git_signature_free(tagger);
+}
+
+void test_object_tag_write__replace(void)
+{
+ /* Replace an already existing tag */
+ git_oid target_id, tag_id, old_tag_id;
+ git_signature *tagger;
+ git_reference *ref_tag;
+ git_object *target;
+
+ git_oid__fromstr(&target_id, tagged_commit, GIT_OID_SHA1);
+ cl_git_pass(git_object_lookup(&target, g_repo, &target_id, GIT_OBJECT_COMMIT));
+
+ cl_git_pass(git_reference_lookup(&ref_tag, g_repo, "refs/tags/e90810b"));
+ git_oid_cpy(&old_tag_id, git_reference_target(ref_tag));
+ git_reference_free(ref_tag);
+
+ /* create signature */
+ cl_git_pass(git_signature_new(&tagger, tagger_name, tagger_email, 123456789, 60));
+
+ cl_git_pass(git_tag_create(
+ &tag_id, /* out id */
+ g_repo,
+ "e90810b",
+ target,
+ tagger,
+ tagger_message,
+ 1));
+
+ git_object_free(target);
+ git_signature_free(tagger);
+
+ cl_git_pass(git_reference_lookup(&ref_tag, g_repo, "refs/tags/e90810b"));
+ cl_assert(git_oid_cmp(git_reference_target(ref_tag), &tag_id) == 0);
+ cl_assert(git_oid_cmp(git_reference_target(ref_tag), &old_tag_id) != 0);
+
+ git_reference_free(ref_tag);
+}
+
+void test_object_tag_write__lightweight(void)
+{
+ /* write a lightweight tag to the repository and read it again */
+ git_oid target_id, object_id;
+ git_reference *ref_tag;
+ git_object *target;
+
+ git_oid__fromstr(&target_id, tagged_commit, GIT_OID_SHA1);
+ cl_git_pass(git_object_lookup(&target, g_repo, &target_id, GIT_OBJECT_COMMIT));
+
+ cl_git_pass(git_tag_create_lightweight(
+ &object_id,
+ g_repo,
+ "light-tag",
+ target,
+ 0));
+
+ git_object_free(target);
+
+ cl_assert(git_oid_cmp(&object_id, &target_id) == 0);
+
+ cl_git_pass(git_reference_lookup(&ref_tag, g_repo, "refs/tags/light-tag"));
+ cl_assert(git_oid_cmp(git_reference_target(ref_tag), &target_id) == 0);
+
+ cl_git_pass(git_tag_delete(g_repo, "light-tag"));
+
+ git_reference_free(ref_tag);
+}
+
+void test_object_tag_write__lightweight_over_existing(void)
+{
+ /* Attempt to write a lightweight tag bearing the same name than an already existing tag */
+ git_oid target_id, object_id, existing_object_id;
+ git_object *target;
+
+ git_oid__fromstr(&target_id, tagged_commit, GIT_OID_SHA1);
+ cl_git_pass(git_object_lookup(&target, g_repo, &target_id, GIT_OBJECT_COMMIT));
+
+ cl_assert_equal_i(GIT_EEXISTS, git_tag_create_lightweight(
+ &object_id,
+ g_repo,
+ "e90810b",
+ target,
+ 0));
+
+ git_oid__fromstr(&existing_object_id, tag2_id, GIT_OID_SHA1);
+ cl_assert(git_oid_cmp(&object_id, &existing_object_id) == 0);
+
+ git_object_free(target);
+}
+
+void test_object_tag_write__delete(void)
+{
+ /* Delete an already existing tag */
+ git_reference *ref_tag;
+
+ cl_git_pass(git_tag_delete(g_repo, "e90810b"));
+
+ cl_git_fail(git_reference_lookup(&ref_tag, g_repo, "refs/tags/e90810b"));
+
+ git_reference_free(ref_tag);
+}
+
+void test_object_tag_write__creating_with_an_invalid_name_returns_EINVALIDSPEC(void)
+{
+ git_oid target_id, tag_id;
+ git_signature *tagger;
+ git_object *target;
+
+ git_oid__fromstr(&target_id, tagged_commit, GIT_OID_SHA1);
+ cl_git_pass(git_object_lookup(&target, g_repo, &target_id, GIT_OBJECT_COMMIT));
+
+ cl_git_pass(git_signature_new(&tagger, tagger_name, tagger_email, 123456789, 60));
+
+ cl_assert_equal_i(GIT_EINVALIDSPEC,
+ git_tag_create(&tag_id, g_repo,
+ "Inv@{id", target, tagger, tagger_message, 0)
+ );
+
+ cl_assert_equal_i(GIT_EINVALIDSPEC,
+ git_tag_create_lightweight(&tag_id, g_repo,
+ "Inv@{id", target, 0)
+ );
+
+ git_object_free(target);
+ git_signature_free(tagger);
+}
+
+void test_object_tag_write__deleting_with_an_invalid_name_returns_EINVALIDSPEC(void)
+{
+ cl_assert_equal_i(GIT_EINVALIDSPEC, git_tag_delete(g_repo, "Inv@{id"));
+}
+
+static void create_annotation(git_oid *tag_id, const char *name)
+{
+ git_object *target;
+ git_oid target_id;
+ git_signature *tagger;
+
+ cl_git_pass(git_signature_new(&tagger, tagger_name, tagger_email, 123456789, 60));
+
+ git_oid__fromstr(&target_id, tagged_commit, GIT_OID_SHA1);
+ cl_git_pass(git_object_lookup(&target, g_repo, &target_id, GIT_OBJECT_COMMIT));
+
+ cl_git_pass(git_tag_annotation_create(tag_id, g_repo, name, target, tagger, "boom!"));
+ git_object_free(target);
+ git_signature_free(tagger);
+}
+
+void test_object_tag_write__creating_an_annotation_stores_the_new_object_in_the_odb(void)
+{
+ git_oid tag_id;
+ git_tag *tag;
+
+ create_annotation(&tag_id, "new_tag");
+
+ cl_git_pass(git_tag_lookup(&tag, g_repo, &tag_id));
+ cl_assert_equal_s("new_tag", git_tag_name(tag));
+
+ git_tag_free(tag);
+}
+
+void test_object_tag_write__creating_an_annotation_does_not_create_a_reference(void)
+{
+ git_oid tag_id;
+ git_reference *tag_ref;
+
+ create_annotation(&tag_id, "new_tag");
+ cl_git_fail_with(git_reference_lookup(&tag_ref, g_repo, "refs/tags/new_tag"), GIT_ENOTFOUND);
+}
+
+void test_object_tag_write__error_when_create_tag_with_invalid_name(void)
+{
+ git_oid target_id, tag_id;
+ git_signature *tagger;
+ git_object *target;
+
+ git_oid__fromstr(&target_id, tagged_commit, GIT_OID_SHA1);
+ cl_git_pass(git_object_lookup(&target, g_repo, &target_id, GIT_OBJECT_COMMIT));
+ cl_git_pass(git_signature_new(&tagger, tagger_name, tagger_email, 123456789, 60));
+
+ cl_git_fail(
+ git_tag_create(&tag_id, g_repo,
+ "-dash", target, tagger, tagger_message, 0)
+ );
+
+ git_object_free(target);
+ git_signature_free(tagger);
+}
diff --git a/tests/libgit2/object/tree/attributes.c b/tests/libgit2/object/tree/attributes.c
new file mode 100644
index 0000000..285d198
--- /dev/null
+++ b/tests/libgit2/object/tree/attributes.c
@@ -0,0 +1,118 @@
+#include "clar_libgit2.h"
+#include "tree.h"
+
+static git_repository *repo;
+
+static const char *blob_oid = "3d0970ec547fc41ef8a5882dde99c6adce65b021";
+static const char *tree_oid = "1b05fdaa881ee45b48cbaa5e9b037d667a47745e";
+
+void test_object_tree_attributes__initialize(void)
+{
+ repo = cl_git_sandbox_init("deprecated-mode.git");
+}
+
+void test_object_tree_attributes__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_object_tree_attributes__ensure_correctness_of_attributes_on_insertion(void)
+{
+ git_treebuilder *builder;
+ git_oid oid;
+
+ cl_git_pass(git_oid__fromstr(&oid, blob_oid, GIT_OID_SHA1));
+
+ cl_git_pass(git_treebuilder_new(&builder, repo, NULL));
+
+ cl_git_fail(git_treebuilder_insert(NULL, builder, "one.txt", &oid, (git_filemode_t)0777777));
+ cl_git_fail(git_treebuilder_insert(NULL, builder, "one.txt", &oid, (git_filemode_t)0100666));
+ cl_git_fail(git_treebuilder_insert(NULL, builder, "one.txt", &oid, (git_filemode_t)0000001));
+
+ git_treebuilder_free(builder);
+}
+
+void test_object_tree_attributes__group_writable_tree_entries_created_with_an_antique_git_version_can_still_be_accessed(void)
+{
+ git_oid tid;
+ git_tree *tree;
+ const git_tree_entry *entry;
+
+
+ cl_git_pass(git_oid__fromstr(&tid, tree_oid, GIT_OID_SHA1));
+ cl_git_pass(git_tree_lookup(&tree, repo, &tid));
+
+ entry = git_tree_entry_byname(tree, "old_mode.txt");
+ cl_assert_equal_i(
+ GIT_FILEMODE_BLOB,
+ git_tree_entry_filemode(entry));
+
+ git_tree_free(tree);
+}
+
+void test_object_tree_attributes__treebuilder_reject_invalid_filemode(void)
+{
+ git_treebuilder *builder;
+ git_oid bid;
+ const git_tree_entry *entry;
+
+ cl_git_pass(git_oid__fromstr(&bid, blob_oid, GIT_OID_SHA1));
+ cl_git_pass(git_treebuilder_new(&builder, repo, NULL));
+
+ cl_git_fail(git_treebuilder_insert(
+ &entry,
+ builder,
+ "normalized.txt",
+ &bid,
+ GIT_FILEMODE_BLOB_GROUP_WRITABLE));
+
+ git_treebuilder_free(builder);
+}
+
+void test_object_tree_attributes__normalize_attributes_when_creating_a_tree_from_an_existing_one(void)
+{
+ git_treebuilder *builder;
+ git_oid tid, tid2;
+ git_tree *tree;
+ const git_tree_entry *entry;
+
+ cl_git_pass(git_oid__fromstr(&tid, tree_oid, GIT_OID_SHA1));
+ cl_git_pass(git_tree_lookup(&tree, repo, &tid));
+
+ cl_git_pass(git_treebuilder_new(&builder, repo, tree));
+
+ entry = git_treebuilder_get(builder, "old_mode.txt");
+ cl_assert(entry != NULL);
+ cl_assert_equal_i(
+ GIT_FILEMODE_BLOB,
+ git_tree_entry_filemode(entry));
+
+ cl_git_pass(git_treebuilder_write(&tid2, builder));
+ git_treebuilder_free(builder);
+ git_tree_free(tree);
+
+ cl_git_pass(git_tree_lookup(&tree, repo, &tid2));
+ entry = git_tree_entry_byname(tree, "old_mode.txt");
+ cl_assert(entry != NULL);
+ cl_assert_equal_i(
+ GIT_FILEMODE_BLOB,
+ git_tree_entry_filemode(entry));
+
+ git_tree_free(tree);
+}
+
+void test_object_tree_attributes__normalize_600(void)
+{
+ git_oid id;
+ git_tree *tree;
+ const git_tree_entry *entry;
+
+ git_oid__fromstr(&id, "0810fb7818088ff5ac41ee49199b51473b1bd6c7", GIT_OID_SHA1);
+ cl_git_pass(git_tree_lookup(&tree, repo, &id));
+
+ entry = git_tree_entry_byname(tree, "ListaTeste.xml");
+ cl_assert_equal_i(git_tree_entry_filemode(entry), GIT_FILEMODE_BLOB);
+ cl_assert_equal_i(git_tree_entry_filemode_raw(entry), 0100600);
+
+ git_tree_free(tree);
+}
diff --git a/tests/libgit2/object/tree/duplicateentries.c b/tests/libgit2/object/tree/duplicateentries.c
new file mode 100644
index 0000000..e9774ca
--- /dev/null
+++ b/tests/libgit2/object/tree/duplicateentries.c
@@ -0,0 +1,161 @@
+#include "clar_libgit2.h"
+#include "tree.h"
+
+static git_repository *_repo;
+
+void test_object_tree_duplicateentries__initialize(void) {
+ _repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_object_tree_duplicateentries__cleanup(void) {
+ cl_git_sandbox_cleanup();
+}
+
+/*
+ * $ git show --format=raw refs/heads/dir
+ * commit 144344043ba4d4a405da03de3844aa829ae8be0e
+ * tree d52a8fe84ceedf260afe4f0287bbfca04a117e83
+ * parent cf80f8de9f1185bf3a05f993f6121880dd0cfbc9
+ * author Ben Straub <bstraub@github.com> 1343755506 -0700
+ * committer Ben Straub <bstraub@github.com> 1343755506 -0700
+ *
+ * Change a file mode
+ *
+ * diff --git a/a/b.txt b/a/b.txt
+ * old mode 100644
+ * new mode 100755
+ *
+ * $ git ls-tree d52a8fe84ceedf260afe4f0287bbfca04a117e83
+ * 100644 blob a8233120f6ad708f843d861ce2b7228ec4e3dec6 README
+ * 040000 tree 4e0883eeeeebc1fb1735161cea82f7cb5fab7e63 a
+ * 100644 blob 45b983be36b73c0788dc9cbcb76cbb80fc7bb057 branch_file.txt
+ * 100644 blob a71586c1dfe8a71c6cbf6c129f404c5642ff31bd new.txt
+ */
+
+static void tree_checker(
+ git_oid *tid,
+ const char *expected_sha,
+ git_filemode_t expected_filemode)
+{
+ git_tree *tree;
+ const git_tree_entry *entry;
+ git_oid oid;
+
+ cl_git_pass(git_tree_lookup(&tree, _repo, tid));
+ cl_assert_equal_i(1, (int)git_tree_entrycount(tree));
+ entry = git_tree_entry_byindex(tree, 0);
+
+ cl_git_pass(git_oid__fromstr(&oid, expected_sha, GIT_OID_SHA1));
+
+ cl_assert_equal_i(0, git_oid_cmp(&oid, git_tree_entry_id(entry)));
+ cl_assert_equal_i(expected_filemode, git_tree_entry_filemode(entry));
+
+ git_tree_free(tree);
+}
+
+static void tree_creator(git_oid *out, void (*fn)(git_treebuilder *))
+{
+ git_treebuilder *builder;
+
+ cl_git_pass(git_treebuilder_new(&builder, _repo, NULL));
+
+ fn(builder);
+
+ cl_git_pass(git_treebuilder_write(out, builder));
+ git_treebuilder_free(builder);
+}
+
+static void two_blobs(git_treebuilder *bld)
+{
+ git_oid oid;
+ const git_tree_entry *entry;
+
+ cl_git_pass(git_oid__fromstr(&oid,
+ "a8233120f6ad708f843d861ce2b7228ec4e3dec6",
+ GIT_OID_SHA1)); /* blob oid (README) */
+
+ cl_git_pass(git_treebuilder_insert(
+ &entry, bld, "duplicate", &oid,
+ GIT_FILEMODE_BLOB));
+
+ cl_git_pass(git_oid__fromstr(&oid,
+ "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd",
+ GIT_OID_SHA1)); /* blob oid (new.txt) */
+
+ cl_git_pass(git_treebuilder_insert(
+ &entry, bld, "duplicate", &oid,
+ GIT_FILEMODE_BLOB));
+}
+
+static void one_blob_and_one_tree(git_treebuilder *bld)
+{
+ git_oid oid;
+ const git_tree_entry *entry;
+
+ cl_git_pass(git_oid__fromstr(&oid,
+ "a8233120f6ad708f843d861ce2b7228ec4e3dec6",
+ GIT_OID_SHA1)); /* blob oid (README) */
+
+ cl_git_pass(git_treebuilder_insert(
+ &entry, bld, "duplicate", &oid,
+ GIT_FILEMODE_BLOB));
+
+ cl_git_pass(git_oid__fromstr(&oid,
+ "4e0883eeeeebc1fb1735161cea82f7cb5fab7e63",
+ GIT_OID_SHA1)); /* tree oid (a) */
+
+ cl_git_pass(git_treebuilder_insert(
+ &entry, bld, "duplicate", &oid,
+ GIT_FILEMODE_TREE));
+}
+
+void test_object_tree_duplicateentries__cannot_create_a_duplicate_entry_through_the_treebuilder(void)
+{
+ git_oid tid;
+
+ tree_creator(&tid, two_blobs);
+ tree_checker(&tid, "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd", GIT_FILEMODE_BLOB);
+
+ tree_creator(&tid, one_blob_and_one_tree);
+ tree_checker(&tid, "4e0883eeeeebc1fb1735161cea82f7cb5fab7e63", GIT_FILEMODE_TREE);
+}
+
+static void add_fake_conflicts(git_index *index)
+{
+ git_index_entry ancestor_entry, our_entry, their_entry;
+
+ memset(&ancestor_entry, 0x0, sizeof(git_index_entry));
+ memset(&our_entry, 0x0, sizeof(git_index_entry));
+ memset(&their_entry, 0x0, sizeof(git_index_entry));
+
+ ancestor_entry.path = "duplicate";
+ ancestor_entry.mode = GIT_FILEMODE_BLOB;
+ GIT_INDEX_ENTRY_STAGE_SET(&ancestor_entry, 1);
+ git_oid__fromstr(&ancestor_entry.id, "a8233120f6ad708f843d861ce2b7228ec4e3dec6", GIT_OID_SHA1);
+
+ our_entry.path = "duplicate";
+ our_entry.mode = GIT_FILEMODE_BLOB;
+ GIT_INDEX_ENTRY_STAGE_SET(&our_entry, 2);
+ git_oid__fromstr(&our_entry.id, "45b983be36b73c0788dc9cbcb76cbb80fc7bb057", GIT_OID_SHA1);
+
+ their_entry.path = "duplicate";
+ their_entry.mode = GIT_FILEMODE_BLOB;
+ GIT_INDEX_ENTRY_STAGE_SET(&their_entry, 3);
+ git_oid__fromstr(&their_entry.id, "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd", GIT_OID_SHA1);
+
+ cl_git_pass(git_index_conflict_add(index, &ancestor_entry, &our_entry, &their_entry));
+}
+
+void test_object_tree_duplicateentries__cannot_create_a_duplicate_entry_building_a_tree_from_a_index_with_conflicts(void)
+{
+ git_index *index;
+ git_oid tid;
+
+ cl_git_pass(git_repository_index(&index, _repo));
+
+ add_fake_conflicts(index);
+
+ cl_assert_equal_i(GIT_EUNMERGED, git_index_write_tree(&tid, index));
+
+ git_index_free(index);
+}
diff --git a/tests/libgit2/object/tree/frompath.c b/tests/libgit2/object/tree/frompath.c
new file mode 100644
index 0000000..147e53e
--- /dev/null
+++ b/tests/libgit2/object/tree/frompath.c
@@ -0,0 +1,68 @@
+#include "clar_libgit2.h"
+
+static git_repository *repo;
+static git_tree *tree;
+
+void test_object_tree_frompath__initialize(void)
+{
+ git_oid id;
+ const char *tree_with_subtrees_oid = "ae90f12eea699729ed24555e40b9fd669da12a12";
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+ cl_assert(repo != NULL);
+
+ cl_git_pass(git_oid__fromstr(&id, tree_with_subtrees_oid, GIT_OID_SHA1));
+ cl_git_pass(git_tree_lookup(&tree, repo, &id));
+ cl_assert(tree != NULL);
+}
+
+void test_object_tree_frompath__cleanup(void)
+{
+ git_tree_free(tree);
+ tree = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+}
+
+static void assert_tree_from_path(
+ git_tree *root,
+ const char *path,
+ const char *expected_entry_name)
+{
+ git_tree_entry *entry;
+
+ cl_git_pass(git_tree_entry_bypath(&entry, root, path));
+ cl_assert_equal_s(git_tree_entry_name(entry), expected_entry_name);
+ git_tree_entry_free(entry);
+}
+
+void test_object_tree_frompath__retrieve_tree_from_path_to_treeentry(void)
+{
+ git_tree_entry *e;
+
+ assert_tree_from_path(tree, "README", "README");
+ assert_tree_from_path(tree, "ab/de/fgh/1.txt", "1.txt");
+ assert_tree_from_path(tree, "ab/de/fgh", "fgh");
+ assert_tree_from_path(tree, "ab/de/fgh/", "fgh");
+ assert_tree_from_path(tree, "ab/de", "de");
+ assert_tree_from_path(tree, "ab/", "ab");
+ assert_tree_from_path(tree, "ab/de/", "de");
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_tree_entry_bypath(&e, tree, "i-do-not-exist.txt"));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_tree_entry_bypath(&e, tree, "README/"));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_tree_entry_bypath(&e, tree, "ab/de/fgh/i-do-not-exist.txt"));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_tree_entry_bypath(&e, tree, "nope/de/fgh/1.txt"));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_tree_entry_bypath(&e, tree, "ab/me-neither/fgh/2.txt"));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_tree_entry_bypath(&e, tree, "ab/me-neither/fgh/2.txt/"));
+}
+
+void test_object_tree_frompath__fail_when_processing_an_invalid_path(void)
+{
+ git_tree_entry *e;
+
+ cl_must_fail(git_tree_entry_bypath(&e, tree, "/"));
+ cl_must_fail(git_tree_entry_bypath(&e, tree, "/ab"));
+ cl_must_fail(git_tree_entry_bypath(&e, tree, "/ab/de"));
+ cl_must_fail(git_tree_entry_bypath(&e, tree, "ab//de"));
+}
diff --git a/tests/libgit2/object/tree/parse.c b/tests/libgit2/object/tree/parse.c
new file mode 100644
index 0000000..fc985d6
--- /dev/null
+++ b/tests/libgit2/object/tree/parse.c
@@ -0,0 +1,164 @@
+#include "clar_libgit2.h"
+#include "tree.h"
+#include "object.h"
+
+#define OID1_HEX \
+ "\xae\x90\xf1\x2e\xea\x69\x97\x29\xed\x24" \
+ "\x55\x5e\x40\xb9\xfd\x66\x9d\xa1\x2a\x12"
+#define OID1_STR "ae90f12eea699729ed24555e40b9fd669da12a12"
+
+#define OID2_HEX \
+ "\xe8\xbf\xe5\xaf\x39\x57\x9a\x7e\x48\x98" \
+ "\xbb\x23\xf3\xa7\x6a\x72\xc3\x68\xce\xe6"
+#define OID2_STR "e8bfe5af39579a7e4898bb23f3a76a72c368cee6"
+
+typedef struct {
+ const char *filename;
+ uint16_t attr;
+ const char *oid;
+} expected_entry;
+
+static void assert_tree_parses(const char *data, size_t datalen,
+ expected_entry *expected_entries, size_t expected_nentries)
+{
+ git_tree *tree;
+ size_t n;
+
+ if (!datalen)
+ datalen = strlen(data);
+ cl_git_pass(git_object__from_raw((git_object **) &tree, data, datalen, GIT_OBJECT_TREE, GIT_OID_SHA1));
+
+ cl_assert_equal_i(git_tree_entrycount(tree), expected_nentries);
+
+ for (n = 0; n < expected_nentries; n++) {
+ expected_entry *expected = expected_entries + n;
+ const git_tree_entry *entry;
+ git_oid oid;
+
+ cl_git_pass(git_oid__fromstr(&oid, expected->oid, GIT_OID_SHA1));
+
+ cl_assert(entry = git_tree_entry_byname(tree, expected->filename));
+ cl_assert_equal_s(expected->filename, entry->filename);
+ cl_assert_equal_i(expected->attr, entry->attr);
+ cl_assert_equal_oid(&oid, &entry->oid);
+ }
+
+ git_object_free(&tree->object);
+}
+
+static void assert_tree_fails(const char *data, size_t datalen)
+{
+ git_object *object;
+ if (!datalen)
+ datalen = strlen(data);
+ cl_git_fail(git_object__from_raw(&object, data, datalen, GIT_OBJECT_TREE, GIT_OID_SHA1));
+}
+
+void test_object_tree_parse__single_blob_parses(void)
+{
+ expected_entry entries[] = {
+ { "foo", 0100644, OID1_STR },
+ };
+ const char data[] = "100644 foo\x00" OID1_HEX;
+
+ assert_tree_parses(data, ARRAY_SIZE(data) - 1, entries, ARRAY_SIZE(entries));
+}
+
+void test_object_tree_parse__single_tree_parses(void)
+{
+ expected_entry entries[] = {
+ { "foo", 040000, OID1_STR },
+ };
+ const char data[] = "040000 foo\x00" OID1_HEX;
+
+ assert_tree_parses(data, ARRAY_SIZE(data) - 1, entries, ARRAY_SIZE(entries));
+}
+
+void test_object_tree_parse__leading_filename_spaces_parse(void)
+{
+ expected_entry entries[] = {
+ { " bar", 0100644, OID1_STR },
+ };
+ const char data[] = "100644 bar\x00" OID1_HEX;
+
+ assert_tree_parses(data, ARRAY_SIZE(data) - 1, entries, ARRAY_SIZE(entries));
+}
+
+void test_object_tree_parse__multiple_entries_parse(void)
+{
+ expected_entry entries[] = {
+ { "bar", 0100644, OID1_STR },
+ { "foo", 040000, OID2_STR },
+ };
+ const char data[] =
+ "100644 bar\x00" OID1_HEX
+ "040000 foo\x00" OID2_HEX;
+
+ assert_tree_parses(data, ARRAY_SIZE(data) - 1, entries, ARRAY_SIZE(entries));
+}
+
+void test_object_tree_parse__invalid_mode_fails(void)
+{
+ const char data[] = "10x644 bar\x00" OID1_HEX;
+ assert_tree_fails(data, ARRAY_SIZE(data) - 1);
+}
+
+void test_object_tree_parse__missing_mode_fails(void)
+{
+ const char data[] = " bar\x00" OID1_HEX;
+ assert_tree_fails(data, ARRAY_SIZE(data) - 1);
+}
+
+void test_object_tree_parse__mode_doesnt_cause_oob_read(void)
+{
+ const char data[] = "100644 bar\x00" OID1_HEX;
+ assert_tree_fails(data, 2);
+ /*
+ * An oob-read would correctly parse the filename and
+ * later fail to parse the OID with a different error
+ * message
+ */
+ cl_assert_equal_s(git_error_last()->message, "failed to parse tree: missing space after filemode");
+}
+
+void test_object_tree_parse__unreasonably_large_mode_fails(void)
+{
+ const char data[] = "10000000000000000000000000 bar\x00" OID1_HEX;
+ assert_tree_fails(data, ARRAY_SIZE(data) - 1);
+}
+
+void test_object_tree_parse__missing_filename_separator_fails(void)
+{
+ const char data[] = "100644bar\x00" OID1_HEX;
+ assert_tree_fails(data, ARRAY_SIZE(data) - 1);
+}
+
+void test_object_tree_parse__missing_filename_terminator_fails(void)
+{
+ const char data[] = "100644 bar" OID1_HEX;
+ assert_tree_fails(data, ARRAY_SIZE(data) - 1);
+}
+
+void test_object_tree_parse__empty_filename_fails(void)
+{
+ const char data[] = "100644 \x00" OID1_HEX;
+ assert_tree_fails(data, ARRAY_SIZE(data) - 1);
+}
+
+void test_object_tree_parse__trailing_garbage_fails(void)
+{
+ const char data[] = "100644 bar\x00" OID1_HEX "x";
+ assert_tree_fails(data, ARRAY_SIZE(data) - 1);
+}
+
+void test_object_tree_parse__leading_space_fails(void)
+{
+ const char data[] = " 100644 bar\x00" OID1_HEX;
+ assert_tree_fails(data, ARRAY_SIZE(data) - 1);
+}
+
+void test_object_tree_parse__truncated_oid_fails(void)
+{
+ const char data[] = " 100644 bar\x00" OID1_HEX;
+ assert_tree_fails(data, ARRAY_SIZE(data) - 2);
+}
diff --git a/tests/libgit2/object/tree/read.c b/tests/libgit2/object/tree/read.c
new file mode 100644
index 0000000..e18637f
--- /dev/null
+++ b/tests/libgit2/object/tree/read.c
@@ -0,0 +1,119 @@
+#include "clar_libgit2.h"
+
+#include "tree.h"
+
+static const char *tree_oid = "1810dff58d8a660512d4832e740f692884338ccd";
+
+static git_repository *g_repo;
+
+/* Fixture setup and teardown */
+void test_object_tree_read__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_object_tree_read__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+
+
+void test_object_tree_read__loaded(void)
+{
+ /* access randomly the entries on a loaded tree */
+ git_oid id;
+ git_tree *tree;
+
+ git_oid__fromstr(&id, tree_oid, GIT_OID_SHA1);
+
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
+
+ cl_assert(git_tree_entry_byname(tree, "README") != NULL);
+ cl_assert(git_tree_entry_byname(tree, "NOTEXISTS") == NULL);
+ cl_assert(git_tree_entry_byname(tree, "") == NULL);
+ cl_assert(git_tree_entry_byindex(tree, 0) != NULL);
+ cl_assert(git_tree_entry_byindex(tree, 2) != NULL);
+ cl_assert(git_tree_entry_byindex(tree, 3) == NULL);
+ cl_assert(git_tree_entry_byindex(tree, (unsigned int)-1) == NULL);
+
+ git_tree_free(tree);
+}
+
+void test_object_tree_read__two(void)
+{
+ /* read a tree from the repository */
+ git_oid id;
+ git_tree *tree;
+ const git_tree_entry *entry;
+ git_object *obj;
+
+ git_oid__fromstr(&id, tree_oid, GIT_OID_SHA1);
+
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
+
+ cl_assert(git_tree_entrycount(tree) == 3);
+
+ /* GH-86: git_object_lookup() should also check the type if the object comes from the cache */
+ cl_assert(git_object_lookup(&obj, g_repo, &id, GIT_OBJECT_TREE) == 0);
+ cl_assert(obj != NULL);
+ git_object_free(obj);
+ obj = NULL;
+ cl_git_fail(git_object_lookup(&obj, g_repo, &id, GIT_OBJECT_BLOB));
+ cl_assert(obj == NULL);
+
+ entry = git_tree_entry_byname(tree, "README");
+ cl_assert(entry != NULL);
+
+ cl_assert_equal_s(git_tree_entry_name(entry), "README");
+
+ cl_git_pass(git_tree_entry_to_object(&obj, g_repo, entry));
+ cl_assert(obj != NULL);
+
+ git_object_free(obj);
+ git_tree_free(tree);
+}
+
+#define BIGFILE "bigfile"
+
+#ifdef GIT_ARCH_64
+#define BIGFILE_SIZE (off_t)4294967296
+#else
+# define BIGFILE_SIZE SIZE_MAX
+#endif
+
+void test_object_tree_read__largefile(void)
+{
+ const git_tree_entry *entry;
+ git_index_entry ie;
+ git_commit *commit;
+ git_object *object;
+ git_index *index;
+ git_tree *tree;
+ git_oid oid;
+ char *buf;
+
+ if (!cl_is_env_set("GITTEST_INVASIVE_FS_SIZE"))
+ cl_skip();
+
+ cl_assert(buf = git__calloc(1, BIGFILE_SIZE));
+
+ memset(&ie, 0, sizeof(ie));
+ ie.mode = GIT_FILEMODE_BLOB;
+ ie.path = BIGFILE;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_add_from_buffer(index, &ie, buf, BIGFILE_SIZE));
+ cl_repo_commit_from_index(&oid, g_repo, NULL, 0, BIGFILE);
+
+ cl_git_pass(git_commit_lookup(&commit, g_repo, &oid));
+ cl_git_pass(git_commit_tree(&tree, commit));
+ cl_assert(entry = git_tree_entry_byname(tree, BIGFILE));
+ cl_git_pass(git_tree_entry_to_object(&object, g_repo, entry));
+
+ git_object_free(object);
+ git_tree_free(tree);
+ git_index_free(index);
+ git_commit_free(commit);
+ git__free(buf);
+}
diff --git a/tests/libgit2/object/tree/update.c b/tests/libgit2/object/tree/update.c
new file mode 100644
index 0000000..1e82bdc
--- /dev/null
+++ b/tests/libgit2/object/tree/update.c
@@ -0,0 +1,303 @@
+#include "clar_libgit2.h"
+#include "tree.h"
+#include "index.h"
+
+static git_repository *g_repo;
+
+void test_object_tree_update__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo2");
+}
+
+void test_object_tree_update__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_object_tree_update__remove_blob(void)
+{
+ git_oid tree_index_id, tree_updater_id, base_id;
+ git_tree *base_tree;
+ git_index *idx;
+ const char *path = "README";
+
+ git_tree_update updates[] = {
+ { GIT_TREE_UPDATE_REMOVE, GIT_OID_SHA1_ZERO, GIT_FILEMODE_BLOB /* ignored */, path},
+ };
+
+ cl_git_pass(git_oid__fromstr(&base_id, "c4dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b", GIT_OID_SHA1));
+ cl_git_pass(git_tree_lookup(&base_tree, g_repo, &base_id));
+
+ /* Create it with an index */
+ cl_git_pass(git_index__new(&idx, GIT_OID_SHA1));
+ cl_git_pass(git_index_read_tree(idx, base_tree));
+ cl_git_pass(git_index_remove(idx, path, 0));
+ cl_git_pass(git_index_write_tree_to(&tree_index_id, idx, g_repo));
+ git_index_free(idx);
+
+ /* Perform the same operation via the tree updater */
+ cl_git_pass(git_tree_create_updated(&tree_updater_id, g_repo, base_tree, 1, updates));
+
+ cl_assert_equal_oid(&tree_index_id, &tree_updater_id);
+
+ git_tree_free(base_tree);
+}
+
+void test_object_tree_update__remove_blob_deeper(void)
+{
+ git_oid tree_index_id, tree_updater_id, base_id;
+ git_tree *base_tree;
+ git_index *idx;
+ const char *path = "subdir/README";
+
+ git_tree_update updates[] = {
+ { GIT_TREE_UPDATE_REMOVE, GIT_OID_SHA1_ZERO, GIT_FILEMODE_BLOB /* ignored */, path},
+ };
+
+ cl_git_pass(git_oid__fromstr(&base_id, "c4dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b", GIT_OID_SHA1));
+ cl_git_pass(git_tree_lookup(&base_tree, g_repo, &base_id));
+
+ /* Create it with an index */
+ cl_git_pass(git_index__new(&idx, GIT_OID_SHA1));
+ cl_git_pass(git_index_read_tree(idx, base_tree));
+ cl_git_pass(git_index_remove(idx, path, 0));
+ cl_git_pass(git_index_write_tree_to(&tree_index_id, idx, g_repo));
+ git_index_free(idx);
+
+ /* Perform the same operation via the tree updater */
+ cl_git_pass(git_tree_create_updated(&tree_updater_id, g_repo, base_tree, 1, updates));
+
+ cl_assert_equal_oid(&tree_index_id, &tree_updater_id);
+
+ git_tree_free(base_tree);
+}
+
+void test_object_tree_update__remove_all_entries(void)
+{
+ git_oid tree_index_id, tree_updater_id, base_id;
+ git_tree *base_tree;
+ git_index *idx;
+ const char *path1 = "subdir/subdir2/README";
+ const char *path2 = "subdir/subdir2/new.txt";
+
+ git_tree_update updates[] = {
+ { GIT_TREE_UPDATE_REMOVE, GIT_OID_SHA1_ZERO, GIT_FILEMODE_BLOB /* ignored */, path1},
+ { GIT_TREE_UPDATE_REMOVE, GIT_OID_SHA1_ZERO, GIT_FILEMODE_BLOB /* ignored */, path2},
+ };
+
+ cl_git_pass(git_oid__fromstr(&base_id, "c4dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b", GIT_OID_SHA1));
+ cl_git_pass(git_tree_lookup(&base_tree, g_repo, &base_id));
+
+ /* Create it with an index */
+ cl_git_pass(git_index__new(&idx, GIT_OID_SHA1));
+ cl_git_pass(git_index_read_tree(idx, base_tree));
+ cl_git_pass(git_index_remove(idx, path1, 0));
+ cl_git_pass(git_index_remove(idx, path2, 0));
+ cl_git_pass(git_index_write_tree_to(&tree_index_id, idx, g_repo));
+ git_index_free(idx);
+
+ /* Perform the same operation via the tree updater */
+ cl_git_pass(git_tree_create_updated(&tree_updater_id, g_repo, base_tree, 2, updates));
+
+ cl_assert_equal_oid(&tree_index_id, &tree_updater_id);
+
+ git_tree_free(base_tree);
+}
+
+void test_object_tree_update__replace_blob(void)
+{
+ git_oid tree_index_id, tree_updater_id, base_id;
+ git_tree *base_tree;
+ git_index *idx;
+ const char *path = "README";
+ git_index_entry entry = { {0} };
+
+ git_tree_update updates[] = {
+ { GIT_TREE_UPDATE_UPSERT, GIT_OID_SHA1_ZERO, GIT_FILEMODE_BLOB, path},
+ };
+
+ cl_git_pass(git_oid__fromstr(&base_id, "c4dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b", GIT_OID_SHA1));
+ cl_git_pass(git_tree_lookup(&base_tree, g_repo, &base_id));
+
+ /* Create it with an index */
+ cl_git_pass(git_index__new(&idx, GIT_OID_SHA1));
+ cl_git_pass(git_index_read_tree(idx, base_tree));
+
+ entry.path = path;
+ cl_git_pass(git_oid__fromstr(&entry.id, "fa49b077972391ad58037050f2a75f74e3671e92", GIT_OID_SHA1));
+ entry.mode = GIT_FILEMODE_BLOB;
+ cl_git_pass(git_index_add(idx, &entry));
+
+ cl_git_pass(git_index_write_tree_to(&tree_index_id, idx, g_repo));
+ git_index_free(idx);
+
+ /* Perform the same operation via the tree updater */
+ cl_git_pass(git_oid__fromstr(&updates[0].id, "fa49b077972391ad58037050f2a75f74e3671e92", GIT_OID_SHA1));
+ cl_git_pass(git_tree_create_updated(&tree_updater_id, g_repo, base_tree, 1, updates));
+
+ cl_assert_equal_oid(&tree_index_id, &tree_updater_id);
+
+ git_tree_free(base_tree);
+}
+
+void test_object_tree_update__add_blobs(void)
+{
+ git_oid tree_index_id, tree_updater_id, base_id;
+ git_tree *base_tree;
+ git_index *idx;
+ git_index_entry entry = { {0} };
+ int i;
+ const char *paths[] = {
+ "some/deep/path",
+ "some/other/path",
+ "a/path/elsewhere",
+ };
+
+ git_tree_update updates[] = {
+ { GIT_TREE_UPDATE_UPSERT, GIT_OID_SHA1_ZERO, GIT_FILEMODE_BLOB, paths[0]},
+ { GIT_TREE_UPDATE_UPSERT, GIT_OID_SHA1_ZERO, GIT_FILEMODE_BLOB, paths[1]},
+ { GIT_TREE_UPDATE_UPSERT, GIT_OID_SHA1_ZERO, GIT_FILEMODE_BLOB, paths[2]},
+ };
+
+ cl_git_pass(git_oid__fromstr(&base_id, "c4dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b", GIT_OID_SHA1));
+
+ entry.mode = GIT_FILEMODE_BLOB;
+ cl_git_pass(git_oid__fromstr(&entry.id, "fa49b077972391ad58037050f2a75f74e3671e92", GIT_OID_SHA1));
+
+ for (i = 0; i < 3; i++) {
+ cl_git_pass(git_oid__fromstr(&updates[i].id, "fa49b077972391ad58037050f2a75f74e3671e92", GIT_OID_SHA1));
+ }
+
+ for (i = 0; i < 2; i++) {
+ int j;
+
+ /* Create it with an index */
+ cl_git_pass(git_index__new(&idx, GIT_OID_SHA1));
+
+ base_tree = NULL;
+ if (i == 1) {
+ cl_git_pass(git_tree_lookup(&base_tree, g_repo, &base_id));
+ cl_git_pass(git_index_read_tree(idx, base_tree));
+ }
+
+ for (j = 0; j < 3; j++) {
+ entry.path = paths[j];
+ cl_git_pass(git_index_add(idx, &entry));
+ }
+
+ cl_git_pass(git_index_write_tree_to(&tree_index_id, idx, g_repo));
+ git_index_free(idx);
+
+ /* Perform the same operations via the tree updater */
+ cl_git_pass(git_tree_create_updated(&tree_updater_id, g_repo, base_tree, 3, updates));
+
+ cl_assert_equal_oid(&tree_index_id, &tree_updater_id);
+ }
+
+ git_tree_free(base_tree);
+}
+
+void test_object_tree_update__add_blobs_unsorted(void)
+{
+ git_oid tree_index_id, tree_updater_id, base_id;
+ git_tree *base_tree;
+ git_index *idx;
+ git_index_entry entry = { {0} };
+ int i;
+ const char *paths[] = {
+ "some/deep/path",
+ "a/path/elsewhere",
+ "some/other/path",
+ };
+
+ git_tree_update updates[] = {
+ { GIT_TREE_UPDATE_UPSERT, GIT_OID_SHA1_ZERO, GIT_FILEMODE_BLOB, paths[0]},
+ { GIT_TREE_UPDATE_UPSERT, GIT_OID_SHA1_ZERO, GIT_FILEMODE_BLOB, paths[1]},
+ { GIT_TREE_UPDATE_UPSERT, GIT_OID_SHA1_ZERO, GIT_FILEMODE_BLOB, paths[2]},
+ };
+
+ cl_git_pass(git_oid__fromstr(&base_id, "c4dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b", GIT_OID_SHA1));
+
+ entry.mode = GIT_FILEMODE_BLOB;
+ cl_git_pass(git_oid__fromstr(&entry.id, "fa49b077972391ad58037050f2a75f74e3671e92", GIT_OID_SHA1));
+
+ for (i = 0; i < 3; i++) {
+ cl_git_pass(git_oid__fromstr(&updates[i].id, "fa49b077972391ad58037050f2a75f74e3671e92", GIT_OID_SHA1));
+ }
+
+ for (i = 0; i < 2; i++) {
+ int j;
+
+ /* Create it with an index */
+ cl_git_pass(git_index__new(&idx, GIT_OID_SHA1));
+
+ base_tree = NULL;
+ if (i == 1) {
+ cl_git_pass(git_tree_lookup(&base_tree, g_repo, &base_id));
+ cl_git_pass(git_index_read_tree(idx, base_tree));
+ }
+
+ for (j = 0; j < 3; j++) {
+ entry.path = paths[j];
+ cl_git_pass(git_index_add(idx, &entry));
+ }
+
+ cl_git_pass(git_index_write_tree_to(&tree_index_id, idx, g_repo));
+ git_index_free(idx);
+
+ /* Perform the same operations via the tree updater */
+ cl_git_pass(git_tree_create_updated(&tree_updater_id, g_repo, base_tree, 3, updates));
+
+ cl_assert_equal_oid(&tree_index_id, &tree_updater_id);
+ }
+
+ git_tree_free(base_tree);
+}
+
+void test_object_tree_update__add_conflict(void)
+{
+ int i;
+ git_oid tree_updater_id;
+ git_tree_update updates[] = {
+ { GIT_TREE_UPDATE_UPSERT, GIT_OID_SHA1_ZERO, GIT_FILEMODE_BLOB, "a/dir/blob"},
+ { GIT_TREE_UPDATE_UPSERT, GIT_OID_SHA1_ZERO, GIT_FILEMODE_BLOB, "a/dir"},
+ };
+
+ for (i = 0; i < 2; i++) {
+ cl_git_pass(git_oid__fromstr(&updates[i].id, "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd", GIT_OID_SHA1));
+ }
+
+ cl_git_fail(git_tree_create_updated(&tree_updater_id, g_repo, NULL, 2, updates));
+}
+
+void test_object_tree_update__add_conflict2(void)
+{
+ int i;
+ git_oid tree_updater_id;
+ git_tree_update updates[] = {
+ { GIT_TREE_UPDATE_UPSERT, GIT_OID_SHA1_ZERO, GIT_FILEMODE_BLOB, "a/dir/blob"},
+ { GIT_TREE_UPDATE_UPSERT, GIT_OID_SHA1_ZERO, GIT_FILEMODE_TREE, "a/dir/blob"},
+ };
+
+ for (i = 0; i < 2; i++) {
+ cl_git_pass(git_oid__fromstr(&updates[i].id, "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd", GIT_OID_SHA1));
+ }
+
+ cl_git_fail(git_tree_create_updated(&tree_updater_id, g_repo, NULL, 2, updates));
+}
+
+void test_object_tree_update__remove_invalid_submodule(void)
+{
+ git_tree *baseline;
+ git_oid updated_tree_id, baseline_id;
+ git_tree_update updates[] = {
+ {GIT_TREE_UPDATE_REMOVE, GIT_OID_SHA1_ZERO, GIT_FILEMODE_BLOB, "submodule"},
+ };
+
+ /* This tree contains a submodule with an all-zero commit for a submodule named 'submodule' */
+ cl_git_pass(git_oid__fromstr(&baseline_id, "396c7f1adb7925f51ba13a75f48252f44c5a14a2", GIT_OID_SHA1));
+ cl_git_pass(git_tree_lookup(&baseline, g_repo, &baseline_id));
+ cl_git_pass(git_tree_create_updated(&updated_tree_id, g_repo, baseline, 1, updates));
+
+ git_tree_free(baseline);
+}
diff --git a/tests/libgit2/object/tree/walk.c b/tests/libgit2/object/tree/walk.c
new file mode 100644
index 0000000..573a278
--- /dev/null
+++ b/tests/libgit2/object/tree/walk.c
@@ -0,0 +1,177 @@
+#include "clar_libgit2.h"
+#include "tree.h"
+
+static const char *tree_oid = "1810dff58d8a660512d4832e740f692884338ccd";
+static git_repository *g_repo;
+
+void test_object_tree_walk__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_object_tree_walk__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static int treewalk_count_cb(
+ const char *root, const git_tree_entry *entry, void *payload)
+{
+ int *count = payload;
+
+ GIT_UNUSED(root);
+ GIT_UNUSED(entry);
+
+ (*count) += 1;
+
+ return 0;
+}
+
+void test_object_tree_walk__0(void)
+{
+ git_oid id;
+ git_tree *tree;
+ int ct;
+
+ git_oid__fromstr(&id, tree_oid, GIT_OID_SHA1);
+
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
+
+ ct = 0;
+ cl_git_pass(git_tree_walk(tree, GIT_TREEWALK_PRE, treewalk_count_cb, &ct));
+ cl_assert_equal_i(3, ct);
+
+ ct = 0;
+ cl_git_pass(git_tree_walk(tree, GIT_TREEWALK_POST, treewalk_count_cb, &ct));
+ cl_assert_equal_i(3, ct);
+
+ git_tree_free(tree);
+}
+
+
+static int treewalk_stop_cb(
+ const char *root, const git_tree_entry *entry, void *payload)
+{
+ int *count = payload;
+
+ GIT_UNUSED(root);
+ GIT_UNUSED(entry);
+
+ (*count) += 1;
+
+ return (*count == 2) ? -123 : 0;
+}
+
+static int treewalk_stop_immediately_cb(
+ const char *root, const git_tree_entry *entry, void *payload)
+{
+ GIT_UNUSED(root);
+ GIT_UNUSED(entry);
+ GIT_UNUSED(payload);
+ return -100;
+}
+
+void test_object_tree_walk__1(void)
+{
+ git_oid id;
+ git_tree *tree;
+ int ct;
+
+ git_oid__fromstr(&id, tree_oid, GIT_OID_SHA1);
+
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
+
+ ct = 0;
+ cl_assert_equal_i(
+ -123, git_tree_walk(tree, GIT_TREEWALK_PRE, treewalk_stop_cb, &ct));
+ cl_assert_equal_i(2, ct);
+
+ ct = 0;
+ cl_assert_equal_i(
+ -123, git_tree_walk(tree, GIT_TREEWALK_POST, treewalk_stop_cb, &ct));
+ cl_assert_equal_i(2, ct);
+
+ cl_assert_equal_i(
+ -100, git_tree_walk(
+ tree, GIT_TREEWALK_PRE, treewalk_stop_immediately_cb, NULL));
+
+ cl_assert_equal_i(
+ -100, git_tree_walk(
+ tree, GIT_TREEWALK_POST, treewalk_stop_immediately_cb, NULL));
+
+ git_tree_free(tree);
+}
+
+
+struct treewalk_skip_data {
+ int files;
+ int dirs;
+ const char *skip;
+ const char *stop;
+};
+
+static int treewalk_skip_de_cb(
+ const char *root, const git_tree_entry *entry, void *payload)
+{
+ struct treewalk_skip_data *data = payload;
+ const char *name = git_tree_entry_name(entry);
+
+ GIT_UNUSED(root);
+
+ if (git_tree_entry_type(entry) == GIT_OBJECT_TREE)
+ data->dirs++;
+ else
+ data->files++;
+
+ if (data->skip && !strcmp(name, data->skip))
+ return 1;
+ else if (data->stop && !strcmp(name, data->stop))
+ return -1;
+ else
+ return 0;
+}
+
+void test_object_tree_walk__2(void)
+{
+ git_oid id;
+ git_tree *tree;
+ struct treewalk_skip_data data;
+
+ /* look up a deep tree */
+ git_oid__fromstr(&id, "ae90f12eea699729ed24555e40b9fd669da12a12", GIT_OID_SHA1);
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
+
+ memset(&data, 0, sizeof(data));
+ data.skip = "de";
+
+ cl_assert_equal_i(0, git_tree_walk(
+ tree, GIT_TREEWALK_PRE, treewalk_skip_de_cb, &data));
+ cl_assert_equal_i(5, data.files);
+ cl_assert_equal_i(3, data.dirs);
+
+ memset(&data, 0, sizeof(data));
+ data.stop = "3.txt";
+
+ cl_assert_equal_i(-1, git_tree_walk(
+ tree, GIT_TREEWALK_PRE, treewalk_skip_de_cb, &data));
+ cl_assert_equal_i(3, data.files);
+ cl_assert_equal_i(2, data.dirs);
+
+ memset(&data, 0, sizeof(data));
+ data.skip = "new.txt";
+
+ cl_assert_equal_i(0, git_tree_walk(
+ tree, GIT_TREEWALK_PRE, treewalk_skip_de_cb, &data));
+ cl_assert_equal_i(7, data.files);
+ cl_assert_equal_i(4, data.dirs);
+
+ memset(&data, 0, sizeof(data));
+ data.stop = "new.txt";
+
+ cl_assert_equal_i(-1, git_tree_walk(
+ tree, GIT_TREEWALK_PRE, treewalk_skip_de_cb, &data));
+ cl_assert_equal_i(7, data.files);
+ cl_assert_equal_i(4, data.dirs);
+
+ git_tree_free(tree);
+}
diff --git a/tests/libgit2/object/tree/write.c b/tests/libgit2/object/tree/write.c
new file mode 100644
index 0000000..71a4c54
--- /dev/null
+++ b/tests/libgit2/object/tree/write.c
@@ -0,0 +1,528 @@
+#include "clar_libgit2.h"
+
+#include "tree.h"
+
+static const char *blob_oid = "fa49b077972391ad58037050f2a75f74e3671e92";
+static const char *first_tree = "181037049a54a1eb5fab404658a3a250b44335d7";
+static const char *second_tree = "f60079018b664e4e79329a7ef9559c8d9e0378d1";
+static const char *third_tree = "eb86d8b81d6adbd5290a935d6c9976882de98488";
+
+static git_repository *g_repo;
+
+/* Fixture setup and teardown */
+void test_object_tree_write__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_object_tree_write__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 1));
+}
+
+void test_object_tree_write__from_memory(void)
+{
+ /* write a tree from a memory */
+ git_treebuilder *builder;
+ git_tree *tree;
+ git_oid id, bid, rid, id2;
+
+ git_oid__fromstr(&id, first_tree, GIT_OID_SHA1);
+ git_oid__fromstr(&id2, second_tree, GIT_OID_SHA1);
+ git_oid__fromstr(&bid, blob_oid, GIT_OID_SHA1);
+
+ /* create a second tree from first tree using `git_treebuilder_insert`
+ * on REPOSITORY_FOLDER.
+ */
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
+ cl_git_pass(git_treebuilder_new(&builder, g_repo, tree));
+
+ cl_git_fail(git_treebuilder_insert(NULL, builder, "",
+ &bid, GIT_FILEMODE_BLOB));
+ cl_git_fail(git_treebuilder_insert(NULL, builder, "/",
+ &bid, GIT_FILEMODE_BLOB));
+ cl_git_fail(git_treebuilder_insert(NULL, builder, ".git",
+ &bid, GIT_FILEMODE_BLOB));
+ cl_git_fail(git_treebuilder_insert(NULL, builder, "..",
+ &bid, GIT_FILEMODE_BLOB));
+ cl_git_fail(git_treebuilder_insert(NULL, builder, ".",
+ &bid, GIT_FILEMODE_BLOB));
+ cl_git_fail(git_treebuilder_insert(NULL, builder, "folder/new.txt",
+ &bid, GIT_FILEMODE_BLOB));
+
+ cl_git_pass(git_treebuilder_insert(
+ NULL, builder, "new.txt", &bid, GIT_FILEMODE_BLOB));
+
+ cl_git_pass(git_treebuilder_write(&rid, builder));
+
+ cl_assert(git_oid_cmp(&rid, &id2) == 0);
+
+ git_treebuilder_free(builder);
+ git_tree_free(tree);
+}
+
+void test_object_tree_write__subtree(void)
+{
+ /* write a hierarchical tree from a memory */
+ git_treebuilder *builder;
+ git_tree *tree;
+ git_oid id, bid, subtree_id, id2, id3;
+ git_oid id_hiearar;
+
+ git_oid__fromstr(&id, first_tree, GIT_OID_SHA1);
+ git_oid__fromstr(&id2, second_tree, GIT_OID_SHA1);
+ git_oid__fromstr(&id3, third_tree, GIT_OID_SHA1);
+ git_oid__fromstr(&bid, blob_oid, GIT_OID_SHA1);
+
+ /* create subtree */
+ cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL));
+ cl_git_pass(git_treebuilder_insert(
+ NULL, builder, "new.txt", &bid, GIT_FILEMODE_BLOB)); /* -V536 */
+ cl_git_pass(git_treebuilder_write(&subtree_id, builder));
+ git_treebuilder_free(builder);
+
+ /* create parent tree */
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
+ cl_git_pass(git_treebuilder_new(&builder, g_repo, tree));
+ cl_git_pass(git_treebuilder_insert(
+ NULL, builder, "new", &subtree_id, GIT_FILEMODE_TREE)); /* -V536 */
+ cl_git_pass(git_treebuilder_write(&id_hiearar, builder));
+ git_treebuilder_free(builder);
+ git_tree_free(tree);
+
+ cl_assert(git_oid_cmp(&id_hiearar, &id3) == 0);
+
+ /* check data is correct */
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &id_hiearar));
+ cl_assert(2 == git_tree_entrycount(tree));
+ git_tree_free(tree);
+}
+
+/*
+ * And the Lord said: Is this tree properly sorted?
+ */
+void test_object_tree_write__sorted_subtrees(void)
+{
+ git_treebuilder *builder;
+ git_tree *tree;
+ unsigned int i;
+ int position_c = -1, position_cake = -1, position_config = -1;
+
+ struct {
+ unsigned int attr;
+ const char *filename;
+ } entries[] = {
+ { GIT_FILEMODE_BLOB, ".gitattributes" },
+ { GIT_FILEMODE_BLOB, ".gitignore" },
+ { GIT_FILEMODE_BLOB, ".htaccess" },
+ { GIT_FILEMODE_BLOB, "Capfile" },
+ { GIT_FILEMODE_BLOB, "Makefile"},
+ { GIT_FILEMODE_BLOB, "README"},
+ { GIT_FILEMODE_TREE, "app"},
+ { GIT_FILEMODE_TREE, "cake"},
+ { GIT_FILEMODE_TREE, "config"},
+ { GIT_FILEMODE_BLOB, "c"},
+ { GIT_FILEMODE_BLOB, "git_test.txt"},
+ { GIT_FILEMODE_BLOB, "htaccess.htaccess"},
+ { GIT_FILEMODE_BLOB, "index.php"},
+ { GIT_FILEMODE_TREE, "plugins"},
+ { GIT_FILEMODE_TREE, "schemas"},
+ { GIT_FILEMODE_TREE, "ssl-certs"},
+ { GIT_FILEMODE_TREE, "vendors"}
+ };
+
+ git_oid bid, tid, tree_oid;
+
+ cl_git_pass(git_oid__fromstr(&bid, blob_oid, GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&tid, first_tree, GIT_OID_SHA1));
+
+ cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL));
+
+ for (i = 0; i < ARRAY_SIZE(entries); ++i) {
+ git_oid *id = entries[i].attr == GIT_FILEMODE_TREE ? &tid : &bid;
+
+ cl_git_pass(git_treebuilder_insert(NULL,
+ builder, entries[i].filename, id, entries[i].attr));
+ }
+
+ cl_git_pass(git_treebuilder_write(&tree_oid, builder));
+
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_oid));
+ for (i = 0; i < git_tree_entrycount(tree); i++) {
+ const git_tree_entry *entry = git_tree_entry_byindex(tree, i);
+
+ if (strcmp(entry->filename, "c") == 0)
+ position_c = i;
+
+ if (strcmp(entry->filename, "cake") == 0)
+ position_cake = i;
+
+ if (strcmp(entry->filename, "config") == 0)
+ position_config = i;
+ }
+
+ git_tree_free(tree);
+
+ cl_assert(position_c != -1);
+ cl_assert(position_cake != -1);
+ cl_assert(position_config != -1);
+
+ cl_assert(position_c < position_cake);
+ cl_assert(position_cake < position_config);
+
+ git_treebuilder_free(builder);
+}
+
+static struct {
+ unsigned int attr;
+ const char *filename;
+} _entries[] = {
+ { GIT_FILEMODE_BLOB, "aardvark" },
+ { GIT_FILEMODE_BLOB, ".first" },
+ { GIT_FILEMODE_BLOB, "apple" },
+ { GIT_FILEMODE_BLOB, "last"},
+ { GIT_FILEMODE_BLOB, "apple_after"},
+ { GIT_FILEMODE_BLOB, "after_aardvark"},
+ { 0, NULL },
+};
+
+void test_object_tree_write__removing_and_re_adding_in_treebuilder(void)
+{
+ git_treebuilder *builder;
+ int i, aardvark_i, apple_i, apple_after_i, apple_extra_i, last_i;
+ git_oid entry_oid, tree_oid;
+ git_tree *tree;
+
+ cl_git_pass(git_oid__fromstr(&entry_oid, blob_oid, GIT_OID_SHA1));
+
+ cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL));
+
+ cl_assert_equal_i(0, (int)git_treebuilder_entrycount(builder));
+
+ for (i = 0; _entries[i].filename; ++i)
+ cl_git_pass(git_treebuilder_insert(NULL,
+ builder, _entries[i].filename, &entry_oid, _entries[i].attr));
+
+ cl_assert_equal_i(6, (int)git_treebuilder_entrycount(builder));
+
+ cl_git_pass(git_treebuilder_remove(builder, "apple"));
+ cl_assert_equal_i(5, (int)git_treebuilder_entrycount(builder));
+
+ cl_git_pass(git_treebuilder_remove(builder, "apple_after"));
+ cl_assert_equal_i(4, (int)git_treebuilder_entrycount(builder));
+
+ cl_git_pass(git_treebuilder_insert(
+ NULL, builder, "before_last", &entry_oid, GIT_FILEMODE_BLOB));
+ cl_assert_equal_i(5, (int)git_treebuilder_entrycount(builder));
+
+ /* reinsert apple_after */
+ cl_git_pass(git_treebuilder_insert(
+ NULL, builder, "apple_after", &entry_oid, GIT_FILEMODE_BLOB));
+ cl_assert_equal_i(6, (int)git_treebuilder_entrycount(builder));
+
+ cl_git_pass(git_treebuilder_remove(builder, "last"));
+ cl_assert_equal_i(5, (int)git_treebuilder_entrycount(builder));
+
+ /* reinsert last */
+ cl_git_pass(git_treebuilder_insert(
+ NULL, builder, "last", &entry_oid, GIT_FILEMODE_BLOB));
+ cl_assert_equal_i(6, (int)git_treebuilder_entrycount(builder));
+
+ cl_git_pass(git_treebuilder_insert(
+ NULL, builder, "apple_extra", &entry_oid, GIT_FILEMODE_BLOB));
+ cl_assert_equal_i(7, (int)git_treebuilder_entrycount(builder));
+
+ cl_git_pass(git_treebuilder_write(&tree_oid, builder));
+
+ git_treebuilder_free(builder);
+
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_oid));
+
+ cl_assert_equal_i(7, (int)git_tree_entrycount(tree));
+
+ cl_assert(git_tree_entry_byname(tree, ".first") != NULL);
+ cl_assert(git_tree_entry_byname(tree, "apple") == NULL);
+ cl_assert(git_tree_entry_byname(tree, "apple_after") != NULL);
+ cl_assert(git_tree_entry_byname(tree, "apple_extra") != NULL);
+ cl_assert(git_tree_entry_byname(tree, "last") != NULL);
+
+ aardvark_i = apple_i = apple_after_i = apple_extra_i = last_i = -1;
+
+ for (i = 0; i < 7; ++i) {
+ const git_tree_entry *entry = git_tree_entry_byindex(tree, i);
+
+ if (!strcmp(entry->filename, "aardvark"))
+ aardvark_i = i;
+ else if (!strcmp(entry->filename, "apple"))
+ apple_i = i;
+ else if (!strcmp(entry->filename, "apple_after"))
+ apple_after_i = i;
+ else if (!strcmp(entry->filename, "apple_extra"))
+ apple_extra_i = i;
+ else if (!strcmp(entry->filename, "last"))
+ last_i = i;
+ }
+
+ cl_assert_equal_i(-1, apple_i);
+ cl_assert_equal_i(6, last_i);
+ cl_assert(aardvark_i < apple_after_i);
+ cl_assert(apple_after_i < apple_extra_i);
+
+ git_tree_free(tree);
+}
+
+static int treebuilder_filter_prefixed(
+ const git_tree_entry *entry, void *payload)
+{
+ return !git__prefixcmp(git_tree_entry_name(entry), payload);
+}
+
+void test_object_tree_write__filtering(void)
+{
+ git_treebuilder *builder;
+ int i;
+ git_oid entry_oid, tree_oid;
+ git_tree *tree;
+
+ git_oid__fromstr(&entry_oid, blob_oid, GIT_OID_SHA1);
+
+ cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL));
+
+ for (i = 0; _entries[i].filename; ++i)
+ cl_git_pass(git_treebuilder_insert(NULL,
+ builder, _entries[i].filename, &entry_oid, _entries[i].attr));
+
+ cl_assert_equal_i(6, (int)git_treebuilder_entrycount(builder));
+
+ cl_assert(git_treebuilder_get(builder, "apple") != NULL);
+ cl_assert(git_treebuilder_get(builder, "aardvark") != NULL);
+ cl_assert(git_treebuilder_get(builder, "last") != NULL);
+
+ git_treebuilder_filter(builder, treebuilder_filter_prefixed, "apple");
+
+ cl_assert_equal_i(4, (int)git_treebuilder_entrycount(builder));
+
+ cl_assert(git_treebuilder_get(builder, "apple") == NULL);
+ cl_assert(git_treebuilder_get(builder, "aardvark") != NULL);
+ cl_assert(git_treebuilder_get(builder, "last") != NULL);
+
+ git_treebuilder_filter(builder, treebuilder_filter_prefixed, "a");
+
+ cl_assert_equal_i(2, (int)git_treebuilder_entrycount(builder));
+
+ cl_assert(git_treebuilder_get(builder, "aardvark") == NULL);
+ cl_assert(git_treebuilder_get(builder, "last") != NULL);
+
+ cl_git_pass(git_treebuilder_write(&tree_oid, builder));
+
+ git_treebuilder_free(builder);
+
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_oid));
+
+ cl_assert_equal_i(2, (int)git_tree_entrycount(tree));
+
+ git_tree_free(tree);
+}
+
+void test_object_tree_write__cruel_paths(void)
+{
+ static const char *the_paths[] = {
+ "C:\\",
+ " : * ? \" \n < > |",
+ "a\\b",
+ "\\\\b\a",
+ ":\\",
+ "COM1",
+ "foo.aux",
+ REP1024("1234"), /* 4096 char string */
+ REP1024("12345678"), /* 8192 char string */
+ "\xC5\xAA\x6E\xC4\xAD\x63\xC5\x8D\x64\x65\xCC\xBD", /* ŪnÄ­cÅde̽ */
+ NULL
+ };
+ git_treebuilder *builder;
+ git_tree *tree;
+ git_oid id, bid, subid;
+ const char **scan;
+ int count = 0, i, j;
+ git_tree_entry *te;
+
+ git_oid__fromstr(&bid, blob_oid, GIT_OID_SHA1);
+
+ /* create tree */
+ cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL));
+ for (scan = the_paths; *scan; ++scan) {
+ cl_git_pass(git_treebuilder_insert(
+ NULL, builder, *scan, &bid, GIT_FILEMODE_BLOB));
+ count++;
+ }
+ cl_git_pass(git_treebuilder_write(&id, builder));
+ git_treebuilder_free(builder);
+
+ /* check data is correct */
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
+
+ cl_assert_equal_i(count, git_tree_entrycount(tree));
+
+ for (scan = the_paths; *scan; ++scan) {
+ const git_tree_entry *cte = git_tree_entry_byname(tree, *scan);
+ cl_assert(cte != NULL);
+ cl_assert_equal_s(*scan, git_tree_entry_name(cte));
+ }
+ for (scan = the_paths; *scan; ++scan) {
+ cl_git_pass(git_tree_entry_bypath(&te, tree, *scan));
+ cl_assert_equal_s(*scan, git_tree_entry_name(te));
+ git_tree_entry_free(te);
+ }
+
+ git_tree_free(tree);
+
+ /* let's try longer paths */
+ cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL));
+ for (scan = the_paths; *scan; ++scan) {
+ cl_git_pass(git_treebuilder_insert(
+ NULL, builder, *scan, &id, GIT_FILEMODE_TREE));
+ }
+ cl_git_pass(git_treebuilder_write(&subid, builder));
+ git_treebuilder_free(builder);
+
+ /* check data is correct */
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &subid));
+
+ cl_assert_equal_i(count, git_tree_entrycount(tree));
+
+ for (i = 0; i < count; ++i) {
+ for (j = 0; j < count; ++j) {
+ git_str b = GIT_STR_INIT;
+ cl_git_pass(git_str_joinpath(&b, the_paths[i], the_paths[j]));
+ cl_git_pass(git_tree_entry_bypath(&te, tree, b.ptr));
+ cl_assert_equal_s(the_paths[j], git_tree_entry_name(te));
+ git_tree_entry_free(te);
+ git_str_dispose(&b);
+ }
+ }
+
+ git_tree_free(tree);
+}
+
+void test_object_tree_write__protect_filesystems(void)
+{
+ git_treebuilder *builder;
+ git_oid bid;
+
+ cl_git_pass(git_oid__fromstr(&bid, "fa49b077972391ad58037050f2a75f74e3671e92", GIT_OID_SHA1));
+
+ /* Ensure that (by default) we can write objects with funny names on
+ * platforms that are not affected.
+ */
+ cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL));
+
+ cl_git_fail(git_treebuilder_insert(NULL, builder, ".git.", &bid, GIT_FILEMODE_BLOB));
+ cl_git_fail(git_treebuilder_insert(NULL, builder, "git~1", &bid, GIT_FILEMODE_BLOB));
+
+#ifndef __APPLE__
+ cl_git_pass(git_treebuilder_insert(NULL, builder, ".git\xef\xbb\xbf", &bid, GIT_FILEMODE_BLOB));
+ cl_git_pass(git_treebuilder_insert(NULL, builder, ".git\xe2\x80\xad", &bid, GIT_FILEMODE_BLOB));
+#endif
+
+ git_treebuilder_free(builder);
+
+ /* Now turn on core.protectHFS and core.protectNTFS and validate that these
+ * paths are rejected.
+ */
+
+ cl_repo_set_bool(g_repo, "core.protectHFS", true);
+ cl_repo_set_bool(g_repo, "core.protectNTFS", true);
+
+ cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL));
+
+ cl_git_fail(git_treebuilder_insert(NULL, builder, ".git.", &bid, GIT_FILEMODE_BLOB));
+ cl_git_fail(git_treebuilder_insert(NULL, builder, "git~1", &bid, GIT_FILEMODE_BLOB));
+
+ cl_git_fail(git_treebuilder_insert(NULL, builder, ".git\xef\xbb\xbf", &bid, GIT_FILEMODE_BLOB));
+ cl_git_fail(git_treebuilder_insert(NULL, builder, ".git\xe2\x80\xad", &bid, GIT_FILEMODE_BLOB));
+ cl_git_fail(git_treebuilder_insert(NULL, builder, ".git::$INDEX_ALLOCATION/dummy-file", &bid, GIT_FILEMODE_BLOB));
+
+ git_treebuilder_free(builder);
+}
+
+static void test_invalid_objects(bool should_allow_invalid)
+{
+ git_treebuilder *builder;
+ git_oid valid_blob_id, invalid_blob_id, valid_tree_id, invalid_tree_id;
+
+#define assert_allowed(expr) \
+ clar__assert(!(expr) == should_allow_invalid, \
+ __FILE__, __func__, __LINE__, \
+ (should_allow_invalid ? \
+ "Expected function call to succeed: " #expr : \
+ "Expected function call to fail: " #expr), \
+ NULL, 1)
+
+ cl_git_pass(git_oid__fromstr(&valid_blob_id, blob_oid, GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&invalid_blob_id,
+ "1234567890123456789012345678901234567890",
+ GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&valid_tree_id, first_tree, GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&invalid_tree_id,
+ "0000000000111111111122222222223333333333",
+ GIT_OID_SHA1));
+
+ cl_git_pass(git_treebuilder_new(&builder, g_repo, NULL));
+
+ /* test valid blobs and trees (these should always pass) */
+ cl_git_pass(git_treebuilder_insert(NULL, builder, "file.txt", &valid_blob_id, GIT_FILEMODE_BLOB));
+ cl_git_pass(git_treebuilder_insert(NULL, builder, "folder", &valid_tree_id, GIT_FILEMODE_TREE));
+
+ /* replace valid files and folders with invalid ones */
+ assert_allowed(git_treebuilder_insert(NULL, builder, "file.txt", &invalid_blob_id, GIT_FILEMODE_BLOB));
+ assert_allowed(git_treebuilder_insert(NULL, builder, "folder", &invalid_blob_id, GIT_FILEMODE_BLOB));
+
+ /* insert new invalid files and folders */
+ assert_allowed(git_treebuilder_insert(NULL, builder, "invalid_file.txt", &invalid_blob_id, GIT_FILEMODE_BLOB));
+ assert_allowed(git_treebuilder_insert(NULL, builder, "invalid_folder", &invalid_blob_id, GIT_FILEMODE_BLOB));
+
+ /* insert valid blobs as trees and trees as blobs */
+ assert_allowed(git_treebuilder_insert(NULL, builder, "file_as_folder", &valid_blob_id, GIT_FILEMODE_TREE));
+ assert_allowed(git_treebuilder_insert(NULL, builder, "folder_as_file.txt", &valid_tree_id, GIT_FILEMODE_BLOB));
+
+#undef assert_allowed
+
+ git_treebuilder_free(builder);
+}
+
+static void test_inserting_submodule(void)
+{
+ git_treebuilder *bld;
+ git_oid sm_id;
+
+ cl_git_pass(git_oid__fromstr(&sm_id, "da39a3ee5e6b4b0d3255bfef95601890afd80709", GIT_OID_SHA1));
+ cl_git_pass(git_treebuilder_new(&bld, g_repo, NULL));
+ cl_git_pass(git_treebuilder_insert(NULL, bld, "sm", &sm_id, GIT_FILEMODE_COMMIT));
+ git_treebuilder_free(bld);
+}
+
+void test_object_tree_write__object_validity(void)
+{
+ /* Ensure that we cannot add invalid objects by default */
+ test_invalid_objects(false);
+ test_inserting_submodule();
+
+ /* Ensure that we can turn off validation */
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 0));
+ test_invalid_objects(true);
+ test_inserting_submodule();
+}
+
+void test_object_tree_write__invalid_null_oid(void)
+{
+ git_treebuilder *bld;
+ git_oid null_oid = GIT_OID_SHA1_ZERO;
+
+ cl_git_pass(git_treebuilder_new(&bld, g_repo, NULL));
+ cl_git_fail(git_treebuilder_insert(NULL, bld, "null_oid_file", &null_oid, GIT_FILEMODE_BLOB));
+ cl_assert(git_error_last() && strstr(git_error_last()->message, "null OID") != NULL);
+
+ git_treebuilder_free(bld);
+}
diff --git a/tests/libgit2/object/validate.c b/tests/libgit2/object/validate.c
new file mode 100644
index 0000000..e110381
--- /dev/null
+++ b/tests/libgit2/object/validate.c
@@ -0,0 +1,154 @@
+#include "clar_libgit2.h"
+
+#define VALID_COMMIT_SHA1 \
+ "tree bdd24e358576f1baa275df98cdcaf3ac9a3f4233\n" \
+ "parent d6d956f1d66210bfcd0484166befab33b5987a39\n" \
+ "author Edward Thomson <ethomson@edwardthomson.com> 1638286404 -0500\n" \
+ "committer Edward Thomson <ethomson@edwardthomson.com> 1638324642 -0500\n" \
+ "\n" \
+ "commit go here.\n"
+
+#define VALID_TREE_SHA1 \
+ "100644 HEADER\0\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42"
+
+#define INVALID_COMMIT_SHA1 \
+ "tree bdd24e358576f1baa275df98cdcaf3ac9a3f4233\n" \
+ "parent d6d956f1d66210bfcd0484166befab33b5987a39\n" \
+ "committer Edward Thomson <ethomson@edwardthomson.com> 1638324642 -0500\n" \
+ "\n" \
+ "commit go here.\n"
+
+#define INVALID_TREE_SHA1 \
+ "100644 HEADER \x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42"
+
+#define VALID_COMMIT_SHA256 \
+ "tree d0fc7f52dc42358506e7f3f3be72f5271994abb104b9397ab3e19bb42361504d\n" \
+ "parent 652412419a24ba62a1d897f40aeb80eecbf873797b04a1bbb8d71918653ef65b\n" \
+ "author Edward Thomson <ethomson@edwardthomson.com> 1638286404 -0500\n" \
+ "committer Edward Thomson <ethomson@edwardthomson.com> 1638324642 -0500\n" \
+ "\n" \
+ "commit go here.\n"
+
+#define VALID_TREE_SHA256 \
+ "100644 HEADER\0\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42"
+
+#define INVALID_COMMIT_SHA256 \
+ "tree d0fc7f52dc42358506e7f3f3be72f5271994abb104b9397ab3e19bb42361504d\n" \
+ "parent 652412419a24ba62a1d897f40aeb80eecbf873797b04a1bbb8d71918653ef65b\n" \
+ "committer Edward Thomson <ethomson@edwardthomson.com> 1638324642 -0500\n" \
+ "\n" \
+ "commit go here.\n"
+
+#define INVALID_TREE_SHA256 \
+ "100644 HEADER \x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42\x42"
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+# define sha1_rawcontent_is_valid(v, c, l, t) \
+ git_object_rawcontent_is_valid(v, c, l, t, GIT_OID_SHA1)
+#else
+# define sha1_rawcontent_is_valid(v, c, l, t) \
+ git_object_rawcontent_is_valid(v, c, l, t)
+#endif
+
+void test_object_validate__valid_sha1(void)
+{
+ int valid;
+
+ cl_git_pass(sha1_rawcontent_is_valid(&valid, "", 0, GIT_OBJECT_BLOB));
+ cl_assert_equal_i(1, valid);
+
+ cl_git_pass(sha1_rawcontent_is_valid(&valid, "foobar", 0, GIT_OBJECT_BLOB));
+ cl_assert_equal_i(1, valid);
+
+ cl_git_pass(sha1_rawcontent_is_valid(&valid, VALID_COMMIT_SHA1, CONST_STRLEN(VALID_COMMIT_SHA1), GIT_OBJECT_COMMIT));
+ cl_assert_equal_i(1, valid);
+
+ cl_git_pass(sha1_rawcontent_is_valid(&valid, VALID_TREE_SHA1, CONST_STRLEN(VALID_TREE_SHA1), GIT_OBJECT_TREE));
+ cl_assert_equal_i(1, valid);
+}
+
+void test_object_validate__cannot_parse_sha256_as_sha1(void)
+{
+ int valid;
+
+ cl_git_pass(sha1_rawcontent_is_valid(&valid, VALID_COMMIT_SHA256, CONST_STRLEN(INVALID_COMMIT_SHA256), GIT_OBJECT_COMMIT));
+ cl_assert_equal_i(0, valid);
+
+ cl_git_pass(sha1_rawcontent_is_valid(&valid, INVALID_TREE_SHA256, CONST_STRLEN(INVALID_TREE_SHA256), GIT_OBJECT_TREE));
+ cl_assert_equal_i(0, valid);
+}
+
+void test_object_validate__invalid_sha1(void)
+{
+ int valid;
+
+ cl_git_pass(sha1_rawcontent_is_valid(&valid, "", 0, GIT_OBJECT_COMMIT));
+ cl_assert_equal_i(0, valid);
+
+ cl_git_pass(sha1_rawcontent_is_valid(&valid, "foobar", 0, GIT_OBJECT_COMMIT));
+ cl_assert_equal_i(0, valid);
+
+ cl_git_pass(sha1_rawcontent_is_valid(&valid, INVALID_COMMIT_SHA1, CONST_STRLEN(INVALID_COMMIT_SHA1), GIT_OBJECT_COMMIT));
+ cl_assert_equal_i(0, valid);
+
+ cl_git_pass(sha1_rawcontent_is_valid(&valid, INVALID_TREE_SHA1, CONST_STRLEN(INVALID_TREE_SHA1), GIT_OBJECT_TREE));
+ cl_assert_equal_i(0, valid);
+}
+
+
+void test_object_validate__valid_sha256(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ int valid;
+
+ cl_git_pass(git_object_rawcontent_is_valid(&valid, "", 0, GIT_OBJECT_BLOB, GIT_OID_SHA256));
+ cl_assert_equal_i(1, valid);
+
+ cl_git_pass(git_object_rawcontent_is_valid(&valid, "foobar", 0, GIT_OBJECT_BLOB, GIT_OID_SHA256));
+ cl_assert_equal_i(1, valid);
+
+ cl_git_pass(git_object_rawcontent_is_valid(&valid, VALID_COMMIT_SHA256, CONST_STRLEN(VALID_COMMIT_SHA256), GIT_OBJECT_COMMIT, GIT_OID_SHA256));
+ cl_assert_equal_i(1, valid);
+
+ cl_git_pass(git_object_rawcontent_is_valid(&valid, VALID_TREE_SHA256, CONST_STRLEN(VALID_TREE_SHA256), GIT_OBJECT_TREE, GIT_OID_SHA256));
+ cl_assert_equal_i(1, valid);
+#endif
+}
+
+void test_object_validate__invalid_sha256(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ int valid;
+
+ cl_git_pass(git_object_rawcontent_is_valid(&valid, "", 0, GIT_OBJECT_COMMIT, GIT_OID_SHA256));
+ cl_assert_equal_i(0, valid);
+
+ cl_git_pass(git_object_rawcontent_is_valid(&valid, "foobar", 0, GIT_OBJECT_COMMIT, GIT_OID_SHA256));
+ cl_assert_equal_i(0, valid);
+
+ cl_git_pass(git_object_rawcontent_is_valid(&valid, INVALID_COMMIT_SHA256, CONST_STRLEN(INVALID_COMMIT_SHA256), GIT_OBJECT_COMMIT, GIT_OID_SHA256));
+ cl_assert_equal_i(0, valid);
+
+ cl_git_pass(git_object_rawcontent_is_valid(&valid, INVALID_TREE_SHA256, CONST_STRLEN(INVALID_TREE_SHA256), GIT_OBJECT_TREE, GIT_OID_SHA256));
+ cl_assert_equal_i(0, valid);
+#endif
+}
+
+void test_object_validate__cannot_parse_sha1_as_sha256(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ int valid;
+
+ cl_git_pass(git_object_rawcontent_is_valid(&valid, VALID_COMMIT_SHA1, CONST_STRLEN(INVALID_COMMIT_SHA1), GIT_OBJECT_COMMIT, GIT_OID_SHA256));
+ cl_assert_equal_i(0, valid);
+
+ cl_git_pass(git_object_rawcontent_is_valid(&valid, INVALID_TREE_SHA1, CONST_STRLEN(INVALID_TREE_SHA1), GIT_OBJECT_TREE, GIT_OID_SHA256));
+ cl_assert_equal_i(0, valid);
+#endif
+}
diff --git a/tests/libgit2/odb/alternates.c b/tests/libgit2/odb/alternates.c
new file mode 100644
index 0000000..4d2da6b
--- /dev/null
+++ b/tests/libgit2/odb/alternates.c
@@ -0,0 +1,80 @@
+#include "clar_libgit2.h"
+#include "odb.h"
+#include "filebuf.h"
+
+static git_str destpath, filepath;
+static const char *paths[] = {
+ "A.git", "B.git", "C.git", "D.git", "E.git", "F.git", "G.git"
+};
+static git_filebuf file;
+static git_repository *repo;
+
+void test_odb_alternates__cleanup(void)
+{
+ size_t i;
+
+ git_str_dispose(&destpath);
+ git_str_dispose(&filepath);
+
+ for (i = 0; i < ARRAY_SIZE(paths); i++)
+ cl_fixture_cleanup(paths[i]);
+}
+
+static void init_linked_repo(const char *path, const char *alternate)
+{
+ git_str_clear(&destpath);
+ git_str_clear(&filepath);
+
+ cl_git_pass(git_repository_init(&repo, path, 1));
+ cl_git_pass(git_fs_path_prettify(&destpath, alternate, NULL));
+ cl_git_pass(git_str_joinpath(&destpath, destpath.ptr, "objects"));
+ cl_git_pass(git_str_joinpath(&filepath, git_repository_path(repo), "objects/info"));
+ cl_git_pass(git_futils_mkdir(filepath.ptr, 0755, GIT_MKDIR_PATH));
+ cl_git_pass(git_str_joinpath(&filepath, filepath.ptr , "alternates"));
+
+ cl_git_pass(git_filebuf_open(&file, git_str_cstr(&filepath), 0, 0666));
+ git_filebuf_printf(&file, "%s\n", git_str_cstr(&destpath));
+ cl_git_pass(git_filebuf_commit(&file));
+
+ git_repository_free(repo);
+}
+
+void test_odb_alternates__chained(void)
+{
+ git_commit *commit;
+ git_oid oid;
+
+ /* Set the alternate A -> testrepo.git */
+ init_linked_repo(paths[0], cl_fixture("testrepo.git"));
+
+ /* Set the alternate B -> A */
+ init_linked_repo(paths[1], paths[0]);
+
+ /* Now load B and see if we can find an object from testrepo.git */
+ cl_git_pass(git_repository_open(&repo, paths[1]));
+ git_oid__fromstr(&oid, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+ git_commit_free(commit);
+ git_repository_free(repo);
+}
+
+void test_odb_alternates__long_chain(void)
+{
+ git_commit *commit;
+ git_oid oid;
+ size_t i;
+
+ /* Set the alternate A -> testrepo.git */
+ init_linked_repo(paths[0], cl_fixture("testrepo.git"));
+
+ /* Set up the five-element chain */
+ for (i = 1; i < ARRAY_SIZE(paths); i++) {
+ init_linked_repo(paths[i], paths[i-1]);
+ }
+
+ /* Now load the last one and see if we can find an object from testrepo.git */
+ cl_git_pass(git_repository_open(&repo, paths[ARRAY_SIZE(paths)-1]));
+ git_oid__fromstr(&oid, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", GIT_OID_SHA1);
+ cl_git_fail(git_commit_lookup(&commit, repo, &oid));
+ git_repository_free(repo);
+}
diff --git a/tests/libgit2/odb/backend/backend_helpers.c b/tests/libgit2/odb/backend/backend_helpers.c
new file mode 100644
index 0000000..c1a0070
--- /dev/null
+++ b/tests/libgit2/odb/backend/backend_helpers.c
@@ -0,0 +1,172 @@
+#include "clar_libgit2.h"
+#include "git2/sys/odb_backend.h"
+#include "backend_helpers.h"
+
+static int search_object(const fake_object **out, fake_backend *fake, const git_oid *oid, size_t len)
+{
+ const fake_object *obj = fake->objects, *found = NULL;
+
+ while (obj && obj->oid) {
+ git_oid current_oid;
+
+ git_oid__fromstr(&current_oid, obj->oid, GIT_OID_SHA1);
+
+ if (git_oid_ncmp(&current_oid, oid, len) == 0) {
+ if (found)
+ return GIT_EAMBIGUOUS;
+ found = obj;
+ }
+
+ obj++;
+ }
+
+ if (found && out)
+ *out = found;
+
+ return found ? GIT_OK : GIT_ENOTFOUND;
+}
+
+static int fake_backend__exists(git_odb_backend *backend, const git_oid *oid)
+{
+ fake_backend *fake;
+
+ fake = (fake_backend *)backend;
+
+ fake->exists_calls++;
+
+ return search_object(NULL, fake, oid, GIT_OID_SHA1_HEXSIZE) == GIT_OK;
+}
+
+static int fake_backend__exists_prefix(
+ git_oid *out, git_odb_backend *backend, const git_oid *oid, size_t len)
+{
+ const fake_object *obj;
+ fake_backend *fake;
+ int error;
+
+ fake = (fake_backend *)backend;
+
+ fake->exists_prefix_calls++;
+
+ if ((error = search_object(&obj, fake, oid, len)) < 0)
+ return error;
+
+ if (out)
+ git_oid__fromstr(out, obj->oid, GIT_OID_SHA1);
+
+ return 0;
+}
+
+static int fake_backend__read(
+ void **buffer_p, size_t *len_p, git_object_t *type_p,
+ git_odb_backend *backend, const git_oid *oid)
+{
+ const fake_object *obj;
+ fake_backend *fake;
+ int error;
+
+ fake = (fake_backend *)backend;
+
+ fake->read_calls++;
+
+ if ((error = search_object(&obj, fake, oid, GIT_OID_SHA1_HEXSIZE)) < 0)
+ return error;
+
+ *len_p = strlen(obj->content);
+ *buffer_p = git__strdup(obj->content);
+ *type_p = GIT_OBJECT_BLOB;
+
+ return 0;
+}
+
+static int fake_backend__read_header(
+ size_t *len_p, git_object_t *type_p,
+ git_odb_backend *backend, const git_oid *oid)
+{
+ const fake_object *obj;
+ fake_backend *fake;
+ int error;
+
+ fake = (fake_backend *)backend;
+
+ fake->read_header_calls++;
+
+ if ((error = search_object(&obj, fake, oid, GIT_OID_SHA1_HEXSIZE)) < 0)
+ return error;
+
+ *len_p = strlen(obj->content);
+ *type_p = GIT_OBJECT_BLOB;
+
+ return 0;
+}
+
+static int fake_backend__read_prefix(
+ git_oid *out_oid, void **buffer_p, size_t *len_p, git_object_t *type_p,
+ git_odb_backend *backend, const git_oid *short_oid, size_t len)
+{
+ const fake_object *obj;
+ fake_backend *fake;
+ int error;
+
+ fake = (fake_backend *)backend;
+
+ fake->read_prefix_calls++;
+
+ if ((error = search_object(&obj, fake, short_oid, len)) < 0)
+ return error;
+
+ git_oid__fromstr(out_oid, obj->oid, GIT_OID_SHA1);
+ *len_p = strlen(obj->content);
+ *buffer_p = git__strdup(obj->content);
+ *type_p = GIT_OBJECT_BLOB;
+
+ return 0;
+}
+
+static int fake_backend__refresh(git_odb_backend *backend)
+{
+ fake_backend *fake;
+
+ fake = (fake_backend *)backend;
+
+ fake->refresh_calls++;
+
+ return 0;
+}
+
+
+static void fake_backend__free(git_odb_backend *_backend)
+{
+ fake_backend *backend;
+
+ backend = (fake_backend *)_backend;
+
+ git__free(backend);
+}
+
+int build_fake_backend(
+ git_odb_backend **out,
+ const fake_object *objects,
+ bool support_refresh)
+{
+ fake_backend *backend;
+
+ backend = git__calloc(1, sizeof(fake_backend));
+ GIT_ERROR_CHECK_ALLOC(backend);
+
+ backend->parent.version = GIT_ODB_BACKEND_VERSION;
+
+ backend->objects = objects;
+
+ backend->parent.read = fake_backend__read;
+ backend->parent.read_prefix = fake_backend__read_prefix;
+ backend->parent.read_header = fake_backend__read_header;
+ backend->parent.refresh = support_refresh ? fake_backend__refresh : NULL;
+ backend->parent.exists = fake_backend__exists;
+ backend->parent.exists_prefix = fake_backend__exists_prefix;
+ backend->parent.free = &fake_backend__free;
+
+ *out = (git_odb_backend *)backend;
+
+ return 0;
+}
diff --git a/tests/libgit2/odb/backend/backend_helpers.h b/tests/libgit2/odb/backend/backend_helpers.h
new file mode 100644
index 0000000..32d7a8b
--- /dev/null
+++ b/tests/libgit2/odb/backend/backend_helpers.h
@@ -0,0 +1,24 @@
+#include "git2/sys/odb_backend.h"
+
+typedef struct {
+ const char *oid;
+ const char *content;
+} fake_object;
+
+typedef struct {
+ git_odb_backend parent;
+
+ int exists_calls;
+ int exists_prefix_calls;
+ int read_calls;
+ int read_header_calls;
+ int read_prefix_calls;
+ int refresh_calls;
+
+ const fake_object *objects;
+} fake_backend;
+
+int build_fake_backend(
+ git_odb_backend **out,
+ const fake_object *objects,
+ bool support_refresh);
diff --git a/tests/libgit2/odb/backend/loose.c b/tests/libgit2/odb/backend/loose.c
new file mode 100644
index 0000000..0222794
--- /dev/null
+++ b/tests/libgit2/odb/backend/loose.c
@@ -0,0 +1,62 @@
+#include "clar_libgit2.h"
+#include "repository.h"
+#include "odb.h"
+#include "backend_helpers.h"
+#include "git2/sys/mempack.h"
+
+static git_repository *_repo;
+static git_odb *_odb;
+
+void test_odb_backend_loose__initialize(void)
+{
+ git_odb_backend *backend;
+
+ cl_fixture_sandbox("testrepo.git");
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_odb_backend_loose(&backend, "testrepo.git/objects", NULL));
+#else
+ cl_git_pass(git_odb_backend_loose(&backend, "testrepo.git/objects", 0, 0, 0, 0));
+#endif
+
+ cl_git_pass(git_odb__new(&_odb, NULL));
+ cl_git_pass(git_odb_add_backend(_odb, backend, 10));
+ cl_git_pass(git_repository__wrap_odb(&_repo, _odb, GIT_OID_SHA1));
+}
+
+void test_odb_backend_loose__cleanup(void)
+{
+ git_odb_free(_odb);
+ git_repository_free(_repo);
+
+ cl_fixture_cleanup("testrepo.git");
+}
+
+void test_odb_backend_loose__read_from_odb(void)
+{
+ git_oid oid;
+ git_odb_object *obj;
+
+ cl_git_pass(git_oid__fromstr(&oid, "1385f264afb75a56a5bec74243be9b367ba4ca08", GIT_OID_SHA1));
+ cl_git_pass(git_odb_read(&obj, _odb, &oid));
+ git_odb_object_free(obj);
+
+ cl_git_pass(git_oid__fromstr(&oid, "fd093bff70906175335656e6ce6ae05783708765", GIT_OID_SHA1));
+ cl_git_pass(git_odb_read(&obj, _odb, &oid));
+ git_odb_object_free(obj);
+}
+
+void test_odb_backend_loose__read_from_repo(void)
+{
+ git_oid oid;
+ git_blob *blob;
+ git_tree *tree;
+
+ cl_git_pass(git_oid__fromstr(&oid, "1385f264afb75a56a5bec74243be9b367ba4ca08", GIT_OID_SHA1));
+ cl_git_pass(git_blob_lookup(&blob, _repo, &oid));
+ git_blob_free(blob);
+
+ cl_git_pass(git_oid__fromstr(&oid, "fd093bff70906175335656e6ce6ae05783708765", GIT_OID_SHA1));
+ cl_git_pass(git_tree_lookup(&tree, _repo, &oid));
+ git_tree_free(tree);
+}
diff --git a/tests/libgit2/odb/backend/mempack.c b/tests/libgit2/odb/backend/mempack.c
new file mode 100644
index 0000000..c8a86a2
--- /dev/null
+++ b/tests/libgit2/odb/backend/mempack.c
@@ -0,0 +1,61 @@
+#include "clar_libgit2.h"
+#include "repository.h"
+#include "odb.h"
+#include "backend_helpers.h"
+#include "git2/sys/mempack.h"
+
+static git_odb *_odb;
+static git_oid _oid;
+static git_odb_object *_obj;
+static git_repository *_repo;
+
+void test_odb_backend_mempack__initialize(void)
+{
+ git_odb_backend *backend;
+
+ cl_git_pass(git_mempack_new(&backend));
+ cl_git_pass(git_odb__new(&_odb, NULL));
+ cl_git_pass(git_odb_add_backend(_odb, backend, 10));
+ cl_git_pass(git_repository__wrap_odb(&_repo, _odb, GIT_OID_SHA1));
+}
+
+void test_odb_backend_mempack__cleanup(void)
+{
+ git_odb_object_free(_obj);
+ git_odb_free(_odb);
+ git_repository_free(_repo);
+}
+
+void test_odb_backend_mempack__write_succeeds(void)
+{
+ const char *data = "data";
+ cl_git_pass(git_odb_write(&_oid, _odb, data, strlen(data) + 1, GIT_OBJECT_BLOB));
+ cl_git_pass(git_odb_read(&_obj, _odb, &_oid));
+}
+
+void test_odb_backend_mempack__read_of_missing_object_fails(void)
+{
+ cl_git_pass(git_oid__fromstr(&_oid, "f6ea0495187600e7b2288c8ac19c5886383a4633", GIT_OID_SHA1));
+ cl_git_fail_with(GIT_ENOTFOUND, git_odb_read(&_obj, _odb, &_oid));
+}
+
+void test_odb_backend_mempack__exists_of_missing_object_fails(void)
+{
+ cl_git_pass(git_oid__fromstr(&_oid, "f6ea0495187600e7b2288c8ac19c5886383a4633", GIT_OID_SHA1));
+ cl_assert(git_odb_exists(_odb, &_oid) == 0);
+}
+
+void test_odb_backend_mempack__exists_with_existing_objects_succeeds(void)
+{
+ const char *data = "data";
+ cl_git_pass(git_odb_write(&_oid, _odb, data, strlen(data) + 1, GIT_OBJECT_BLOB));
+ cl_assert(git_odb_exists(_odb, &_oid) == 1);
+}
+
+void test_odb_backend_mempack__blob_create_from_buffer_succeeds(void)
+{
+ const char *data = "data";
+
+ cl_git_pass(git_blob_create_from_buffer(&_oid, _repo, data, strlen(data) + 1));
+ cl_assert(git_odb_exists(_odb, &_oid) == 1);
+}
diff --git a/tests/libgit2/odb/backend/multiple.c b/tests/libgit2/odb/backend/multiple.c
new file mode 100644
index 0000000..9758816
--- /dev/null
+++ b/tests/libgit2/odb/backend/multiple.c
@@ -0,0 +1,121 @@
+#include "clar_libgit2.h"
+#include "repository.h"
+#include "backend_helpers.h"
+
+#define EXISTING_HASH "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"
+
+static git_repository *_repo;
+static git_odb_object *_obj;
+static fake_backend *_fake_empty;
+static fake_backend *_fake_filled;
+
+static git_oid _existing_oid;
+
+static const fake_object _objects_filled[] = {
+ { EXISTING_HASH, "" },
+ { NULL, NULL }
+};
+
+static const fake_object _objects_empty[] = {
+ { NULL, NULL }
+};
+
+void test_odb_backend_multiple__initialize(void)
+{
+ git_odb_backend *backend;
+
+ git_oid__fromstr(&_existing_oid, EXISTING_HASH, GIT_OID_SHA1);
+
+ _obj = NULL;
+ _repo = cl_git_sandbox_init("testrepo.git");
+
+ cl_git_pass(build_fake_backend(&backend, _objects_filled, false));
+ _fake_filled = (fake_backend *)backend;
+
+ cl_git_pass(build_fake_backend(&backend, _objects_empty, false));
+ _fake_empty = (fake_backend *)backend;
+}
+
+void test_odb_backend_multiple__cleanup(void)
+{
+ git_odb_object_free(_obj);
+ cl_git_sandbox_cleanup();
+}
+
+void test_odb_backend_multiple__read_with_empty_first_succeeds(void)
+{
+ git_odb *odb;
+
+ cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
+ cl_git_pass(git_odb_add_backend(odb, (git_odb_backend *)_fake_filled, 10));
+ cl_git_pass(git_odb_add_backend(odb, (git_odb_backend *)_fake_empty, 50));
+
+ cl_git_pass(git_odb_read(&_obj, odb, &_existing_oid));
+
+ cl_assert_equal_i(1, _fake_filled->read_calls);
+ cl_assert_equal_i(1, _fake_empty->read_calls);
+}
+
+void test_odb_backend_multiple__read_with_first_matching_stops(void)
+{
+ git_odb *odb;
+
+ cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
+ cl_git_pass(git_odb_add_backend(odb, (git_odb_backend *)_fake_empty, 10));
+ cl_git_pass(git_odb_add_backend(odb, (git_odb_backend *)_fake_filled, 50));
+
+ cl_git_pass(git_odb_read(&_obj, odb, &_existing_oid));
+
+ cl_assert_equal_i(1, _fake_filled->read_calls);
+ cl_assert_equal_i(0, _fake_empty->read_calls);
+}
+
+void test_odb_backend_multiple__read_prefix_with_first_empty_succeeds(void)
+{
+ git_odb *odb;
+
+ cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
+ cl_git_pass(git_odb_add_backend(odb, (git_odb_backend *)_fake_filled, 10));
+ cl_git_pass(git_odb_add_backend(odb, (git_odb_backend *)_fake_empty, 50));
+
+ cl_git_pass(git_odb_read_prefix(&_obj, odb, &_existing_oid, 7));
+
+ cl_assert_equal_i(1, _fake_filled->read_prefix_calls);
+ cl_assert_equal_i(1, _fake_empty->read_prefix_calls);
+}
+
+void test_odb_backend_multiple__read_prefix_with_first_matching_reads_both(void)
+{
+ git_odb *odb;
+
+ cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
+ cl_git_pass(git_odb_add_backend(odb, (git_odb_backend *)_fake_empty, -10));
+ cl_git_pass(git_odb_add_backend(odb, (git_odb_backend *)_fake_filled, 50));
+
+ cl_git_pass(git_odb_read_prefix(&_obj, odb, &_existing_oid, 7));
+
+ cl_assert_equal_i(1, _fake_filled->read_prefix_calls);
+ cl_assert_equal_i(1, _fake_empty->read_prefix_calls);
+}
+
+void test_odb_backend_multiple__read_prefix_with_first_matching_succeeds_without_hash_verification(void)
+{
+ git_odb *odb;
+
+ git_libgit2_opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 0);
+
+ cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
+ cl_git_pass(git_odb_add_backend(odb, (git_odb_backend *)_fake_empty, -10));
+ cl_git_pass(git_odb_add_backend(odb, (git_odb_backend *)_fake_filled, 50));
+
+ cl_git_pass(git_odb_read_prefix(&_obj, odb, &_existing_oid, 7));
+
+ /*
+ * Both backends should be checked as we have to check
+ * for collisions
+ */
+ cl_assert_equal_i(1, _fake_filled->read_prefix_calls);
+ cl_assert_equal_i(1, _fake_empty->read_prefix_calls);
+
+ git_libgit2_opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 1);
+}
diff --git a/tests/libgit2/odb/backend/nobackend.c b/tests/libgit2/odb/backend/nobackend.c
new file mode 100644
index 0000000..7d9394c
--- /dev/null
+++ b/tests/libgit2/odb/backend/nobackend.c
@@ -0,0 +1,47 @@
+#include "clar_libgit2.h"
+#include "repository.h"
+#include "odb.h"
+#include "git2/sys/repository.h"
+
+static git_repository *_repo;
+
+void test_odb_backend_nobackend__initialize(void)
+{
+ git_config *config;
+ git_odb *odb;
+ git_refdb *refdb;
+
+ cl_git_pass(git_repository_new(&_repo));
+ cl_git_pass(git_config_new(&config));
+ cl_git_pass(git_odb__new(&odb, NULL));
+ cl_git_pass(git_refdb_new(&refdb, _repo));
+
+ git_repository_set_config(_repo, config);
+ git_repository_set_odb(_repo, odb);
+ git_repository_set_refdb(_repo, refdb);
+
+ /* The set increases the refcount and we don't want them anymore */
+ git_config_free(config);
+ git_odb_free(odb);
+ git_refdb_free(refdb);
+}
+
+void test_odb_backend_nobackend__cleanup(void)
+{
+ git_repository_free(_repo);
+}
+
+void test_odb_backend_nobackend__write_fails_gracefully(void)
+{
+ git_oid id;
+ git_odb *odb;
+ const git_error *err;
+
+ git_repository_odb(&odb, _repo);
+ cl_git_fail(git_odb_write(&id, odb, "Hello world!\n", 13, GIT_OBJECT_BLOB));
+
+ err = git_error_last();
+ cl_assert_equal_s(err->message, "cannot write object - unsupported in the loaded odb backends");
+
+ git_odb_free(odb);
+}
diff --git a/tests/libgit2/odb/backend/nonrefreshing.c b/tests/libgit2/odb/backend/nonrefreshing.c
new file mode 100644
index 0000000..5084eb7
--- /dev/null
+++ b/tests/libgit2/odb/backend/nonrefreshing.c
@@ -0,0 +1,147 @@
+#include "clar_libgit2.h"
+#include "repository.h"
+#include "backend_helpers.h"
+
+static git_repository *_repo;
+static fake_backend *_fake;
+
+#define NONEXISTING_HASH "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
+#define EXISTING_HASH "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"
+
+static const fake_object _objects[] = {
+ { EXISTING_HASH, "" },
+ { NULL, NULL }
+};
+
+static git_oid _nonexisting_oid;
+static git_oid _existing_oid;
+
+static void setup_repository_and_backend(void)
+{
+ git_odb *odb = NULL;
+ git_odb_backend *backend = NULL;
+
+ _repo = cl_git_sandbox_init("testrepo.git");
+
+ cl_git_pass(build_fake_backend(&backend, _objects, false));
+
+ cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
+ cl_git_pass(git_odb_add_backend(odb, backend, 10));
+
+ _fake = (fake_backend *)backend;
+}
+
+void test_odb_backend_nonrefreshing__initialize(void)
+{
+ git_oid__fromstr(&_nonexisting_oid, NONEXISTING_HASH, GIT_OID_SHA1);
+ git_oid__fromstr(&_existing_oid, EXISTING_HASH, GIT_OID_SHA1);
+ setup_repository_and_backend();
+}
+
+void test_odb_backend_nonrefreshing__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_odb_backend_nonrefreshing__exists_is_invoked_once_on_failure(void)
+{
+ git_odb *odb;
+
+ cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
+ cl_assert_equal_b(false, git_odb_exists(odb, &_nonexisting_oid));
+
+ cl_assert_equal_i(1, _fake->exists_calls);
+}
+
+void test_odb_backend_nonrefreshing__read_is_invoked_once_on_failure(void)
+{
+ git_object *obj;
+
+ cl_git_fail_with(
+ git_object_lookup(&obj, _repo, &_nonexisting_oid, GIT_OBJECT_ANY),
+ GIT_ENOTFOUND);
+
+ cl_assert_equal_i(1, _fake->read_calls);
+}
+
+void test_odb_backend_nonrefreshing__readprefix_is_invoked_once_on_failure(void)
+{
+ git_object *obj;
+
+ cl_git_fail_with(
+ git_object_lookup_prefix(&obj, _repo, &_nonexisting_oid, 7, GIT_OBJECT_ANY),
+ GIT_ENOTFOUND);
+
+ cl_assert_equal_i(1, _fake->read_prefix_calls);
+}
+
+void test_odb_backend_nonrefreshing__readheader_is_invoked_once_on_failure(void)
+{
+ git_odb *odb;
+ size_t len;
+ git_object_t type;
+
+ cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
+
+ cl_git_fail_with(
+ git_odb_read_header(&len, &type, odb, &_nonexisting_oid),
+ GIT_ENOTFOUND);
+
+ cl_assert_equal_i(1, _fake->read_header_calls);
+}
+
+void test_odb_backend_nonrefreshing__exists_is_invoked_once_on_success(void)
+{
+ git_odb *odb;
+
+ cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
+ cl_assert_equal_b(true, git_odb_exists(odb, &_existing_oid));
+
+ cl_assert_equal_i(1, _fake->exists_calls);
+}
+
+void test_odb_backend_nonrefreshing__read_is_invoked_once_on_success(void)
+{
+ git_object *obj;
+
+ cl_git_pass(git_object_lookup(&obj, _repo, &_existing_oid, GIT_OBJECT_ANY));
+
+ cl_assert_equal_i(1, _fake->read_calls);
+
+ git_object_free(obj);
+}
+
+void test_odb_backend_nonrefreshing__readprefix_is_invoked_once_on_success(void)
+{
+ git_object *obj;
+
+ cl_git_pass(git_object_lookup_prefix(&obj, _repo, &_existing_oid, 7, GIT_OBJECT_ANY));
+
+ cl_assert_equal_i(1, _fake->read_prefix_calls);
+
+ git_object_free(obj);
+}
+
+void test_odb_backend_nonrefreshing__readheader_is_invoked_once_on_success(void)
+{
+ git_odb *odb;
+ size_t len;
+ git_object_t type;
+
+ cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
+
+ cl_git_pass(git_odb_read_header(&len, &type, odb, &_existing_oid));
+
+ cl_assert_equal_i(1, _fake->read_header_calls);
+}
+
+void test_odb_backend_nonrefreshing__read_is_invoked_once_when_revparsing_a_full_oid(void)
+{
+ git_object *obj;
+
+ cl_git_fail_with(
+ git_revparse_single(&obj, _repo, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"),
+ GIT_ENOTFOUND);
+
+ cl_assert_equal_i(1, _fake->read_calls);
+}
diff --git a/tests/libgit2/odb/backend/refreshing.c b/tests/libgit2/odb/backend/refreshing.c
new file mode 100644
index 0000000..fcba748
--- /dev/null
+++ b/tests/libgit2/odb/backend/refreshing.c
@@ -0,0 +1,176 @@
+#include "clar_libgit2.h"
+#include "repository.h"
+#include "backend_helpers.h"
+
+static git_repository *_repo;
+static fake_backend *_fake;
+
+#define NONEXISTING_HASH "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
+#define EXISTING_HASH "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"
+
+static const fake_object _objects[] = {
+ { EXISTING_HASH, "" },
+ { NULL, NULL }
+};
+
+static git_oid _nonexisting_oid;
+static git_oid _existing_oid;
+
+static void setup_repository_and_backend(void)
+{
+ git_odb *odb = NULL;
+ git_odb_backend *backend = NULL;
+
+ _repo = cl_git_sandbox_init("testrepo.git");
+
+ cl_git_pass(build_fake_backend(&backend, _objects, true));
+
+ cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
+ cl_git_pass(git_odb_add_backend(odb, backend, 10));
+
+ _fake = (fake_backend *)backend;
+}
+
+void test_odb_backend_refreshing__initialize(void)
+{
+ git_oid__fromstr(&_nonexisting_oid, NONEXISTING_HASH, GIT_OID_SHA1);
+ git_oid__fromstr(&_existing_oid, EXISTING_HASH, GIT_OID_SHA1);
+ setup_repository_and_backend();
+}
+
+void test_odb_backend_refreshing__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_odb_backend_refreshing__exists_is_invoked_twice_on_failure(void)
+{
+ git_odb *odb;
+
+ cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
+ cl_assert_equal_b(false, git_odb_exists(odb, &_nonexisting_oid));
+
+ cl_assert_equal_i(2, _fake->exists_calls);
+ cl_assert_equal_i(1, _fake->refresh_calls);
+}
+
+void test_odb_backend_refreshing__read_is_invoked_twice_on_failure(void)
+{
+ git_object *obj;
+
+ cl_git_fail_with(
+ git_object_lookup(&obj, _repo, &_nonexisting_oid, GIT_OBJECT_ANY),
+ GIT_ENOTFOUND);
+
+ cl_assert_equal_i(2, _fake->read_calls);
+ cl_assert_equal_i(1, _fake->refresh_calls);
+}
+
+void test_odb_backend_refreshing__readprefix_is_invoked_twice_on_failure(void)
+{
+ git_object *obj;
+
+ cl_git_fail_with(
+ git_object_lookup_prefix(&obj, _repo, &_nonexisting_oid, 7, GIT_OBJECT_ANY),
+ GIT_ENOTFOUND);
+
+ cl_assert_equal_i(2, _fake->read_prefix_calls);
+ cl_assert_equal_i(1, _fake->refresh_calls);
+}
+
+void test_odb_backend_refreshing__readheader_is_invoked_twice_on_failure(void)
+{
+ git_odb *odb;
+ size_t len;
+ git_object_t type;
+
+ cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
+
+ cl_git_fail_with(
+ git_odb_read_header(&len, &type, odb, &_nonexisting_oid),
+ GIT_ENOTFOUND);
+
+ cl_assert_equal_i(2, _fake->read_header_calls);
+ cl_assert_equal_i(1, _fake->refresh_calls);
+}
+
+void test_odb_backend_refreshing__exists_is_invoked_once_on_success(void)
+{
+ git_odb *odb;
+
+ cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
+ cl_assert_equal_b(true, git_odb_exists(odb, &_existing_oid));
+
+ cl_assert_equal_i(1, _fake->exists_calls);
+ cl_assert_equal_i(0, _fake->refresh_calls);
+}
+
+void test_odb_backend_refreshing__read_is_invoked_once_on_success(void)
+{
+ git_object *obj;
+
+ cl_git_pass(git_object_lookup(&obj, _repo, &_existing_oid, GIT_OBJECT_ANY));
+
+ cl_assert_equal_i(1, _fake->read_calls);
+ cl_assert_equal_i(0, _fake->refresh_calls);
+
+ git_object_free(obj);
+}
+
+void test_odb_backend_refreshing__readprefix_is_invoked_once_on_success(void)
+{
+ git_object *obj;
+
+ cl_git_pass(git_object_lookup_prefix(&obj, _repo, &_existing_oid, 7, GIT_OBJECT_ANY));
+
+ cl_assert_equal_i(1, _fake->read_prefix_calls);
+ cl_assert_equal_i(0, _fake->refresh_calls);
+
+ git_object_free(obj);
+}
+
+void test_odb_backend_refreshing__readheader_is_invoked_once_on_success(void)
+{
+ git_odb *odb;
+ size_t len;
+ git_object_t type;
+
+ cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
+
+ cl_git_pass(git_odb_read_header(&len, &type, odb, &_existing_oid));
+
+ cl_assert_equal_i(1, _fake->read_header_calls);
+ cl_assert_equal_i(0, _fake->refresh_calls);
+}
+
+void test_odb_backend_refreshing__read_is_invoked_twice_when_revparsing_a_full_oid(void)
+{
+ git_object *obj;
+
+ cl_git_fail_with(
+ git_revparse_single(&obj, _repo, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"),
+ GIT_ENOTFOUND);
+
+ cl_assert_equal_i(2, _fake->read_calls);
+ cl_assert_equal_i(1, _fake->refresh_calls);
+}
+
+void test_odb_backend_refreshing__refresh_is_invoked(void)
+{
+ git_odb *odb;
+
+ cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
+ cl_assert_equal_i(0, git_odb_refresh(odb));
+
+ cl_assert_equal_i(1, _fake->refresh_calls);
+}
+
+void test_odb_backend_refreshing__refresh_suppressed_with_no_refresh(void)
+{
+ git_odb *odb;
+
+ cl_git_pass(git_repository_odb__weakptr(&odb, _repo));
+ cl_assert_equal_b(false, git_odb_exists_ext(odb, &_nonexisting_oid, GIT_ODB_LOOKUP_NO_REFRESH));
+
+ cl_assert_equal_i(0, _fake->refresh_calls);
+}
diff --git a/tests/libgit2/odb/backend/simple.c b/tests/libgit2/odb/backend/simple.c
new file mode 100644
index 0000000..25dfd90
--- /dev/null
+++ b/tests/libgit2/odb/backend/simple.c
@@ -0,0 +1,250 @@
+#include "clar_libgit2.h"
+#include "repository.h"
+#include "backend_helpers.h"
+
+#define EMPTY_HASH "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"
+
+static git_repository *_repo;
+static git_odb *_odb;
+static git_odb_object *_obj;
+static git_oid _oid;
+
+static void setup_backend(const fake_object *objs)
+{
+ git_odb_backend *backend;
+
+ cl_git_pass(build_fake_backend(&backend, objs, false));
+
+ cl_git_pass(git_repository_odb__weakptr(&_odb, _repo));
+ cl_git_pass(git_odb_add_backend(_odb, backend, 10));
+}
+
+static void assert_object_contains(git_odb_object *obj, const char *expected)
+{
+ const char *actual = (const char *) git_odb_object_data(obj);
+
+ cl_assert_equal_s(actual, expected);
+}
+
+void test_odb_backend_simple__initialize(void)
+{
+ _repo = cl_git_sandbox_init("testrepo.git");
+ _odb = NULL;
+ _obj = NULL;
+}
+
+void test_odb_backend_simple__cleanup(void)
+{
+ git_odb_object_free(_obj);
+ cl_git_sandbox_cleanup();
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 1));
+}
+
+void test_odb_backend_simple__read_of_object_succeeds(void)
+{
+ const fake_object objs[] = {
+ { "f6ea0495187600e7b2288c8ac19c5886383a4632", "foobar" },
+ { NULL, NULL }
+ };
+
+ setup_backend(objs);
+
+ cl_git_pass(git_oid__fromstr(&_oid, objs[0].oid, GIT_OID_SHA1));
+ cl_git_pass(git_odb_read(&_obj, _odb, &_oid));
+
+ assert_object_contains(_obj, objs[0].content);
+}
+
+void test_odb_backend_simple__read_of_nonexisting_object_fails(void)
+{
+ const fake_object objs[] = {
+ { "f6ea0495187600e7b2288c8ac19c5886383a4632", "foobar" },
+ { NULL, NULL }
+ };
+
+ setup_backend(objs);
+
+ cl_git_pass(git_oid__fromstr(&_oid, "f6ea0495187600e7b2288c8ac19c5886383a4633", GIT_OID_SHA1));
+ cl_git_fail_with(GIT_ENOTFOUND, git_odb_read(&_obj, _odb, &_oid));
+}
+
+void test_odb_backend_simple__read_with_hash_mismatch_fails(void)
+{
+ const fake_object objs[] = {
+ { "1234567890123456789012345678901234567890", "nonmatching content" },
+ { NULL, NULL }
+ };
+
+ setup_backend(objs);
+
+ cl_git_pass(git_oid__fromstr(&_oid, objs[0].oid, GIT_OID_SHA1));
+ cl_git_fail_with(GIT_EMISMATCH, git_odb_read(&_obj, _odb, &_oid));
+}
+
+void test_odb_backend_simple__read_with_hash_mismatch_succeeds_without_verification(void)
+{
+ const fake_object objs[] = {
+ { "1234567890123456789012345678901234567890", "nonmatching content" },
+ { NULL, NULL }
+ };
+
+ setup_backend(objs);
+ cl_git_pass(git_oid__fromstr(&_oid, objs[0].oid, GIT_OID_SHA1));
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 0));
+ cl_git_pass(git_odb_read(&_obj, _odb, &_oid));
+
+ assert_object_contains(_obj, objs[0].content);
+}
+
+void test_odb_backend_simple__read_prefix_succeeds(void)
+{
+ const fake_object objs[] = {
+ { "f6ea0495187600e7b2288c8ac19c5886383a4632", "foobar" },
+ { NULL, NULL }
+ };
+
+ setup_backend(objs);
+
+ cl_git_pass(git_oid__fromstr(&_oid, "f6ea0495187600e7b2288c8ac19c5886383a4632", GIT_OID_SHA1));
+ cl_git_pass(git_odb_read(&_obj, _odb, &_oid));
+
+ assert_object_contains(_obj, objs[0].content);
+}
+
+void test_odb_backend_simple__read_prefix_of_nonexisting_object_fails(void)
+{
+ const fake_object objs[] = {
+ { "f6ea0495187600e7b2288c8ac19c5886383a4632", "foobar" },
+ { NULL, NULL }
+ };
+ char *hash = "f6ea0495187600e8";
+
+ setup_backend(objs);
+
+ cl_git_pass(git_oid__fromstrn(&_oid, hash, strlen(hash), GIT_OID_SHA1));
+ cl_git_fail_with(GIT_ENOTFOUND, git_odb_read(&_obj, _odb, &_oid));
+}
+
+void test_odb_backend_simple__read_with_ambiguous_prefix_fails(void)
+{
+ const fake_object objs[] = {
+ { "1234567890111111111111111111111111111111", "first content" },
+ { "1234567890222222222222222222222222222222", "second content" },
+ { NULL, NULL }
+ };
+
+ setup_backend(objs);
+
+ cl_git_pass(git_oid__fromstr(&_oid, objs[0].oid, GIT_OID_SHA1));
+ cl_git_fail_with(GIT_EAMBIGUOUS, git_odb_read_prefix(&_obj, _odb, &_oid, 7));
+}
+
+void test_odb_backend_simple__read_with_highly_ambiguous_prefix(void)
+{
+ const fake_object objs[] = {
+ { "1234567890111111111111111111111111111111", "first content" },
+ { "1234567890111111111111111111111111111112", "second content" },
+ { NULL, NULL }
+ };
+
+ setup_backend(objs);
+
+ cl_git_pass(git_oid__fromstr(&_oid, objs[0].oid, GIT_OID_SHA1));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 0));
+ cl_git_fail_with(GIT_EAMBIGUOUS, git_odb_read_prefix(&_obj, _odb, &_oid, 39));
+ cl_git_pass(git_odb_read_prefix(&_obj, _odb, &_oid, 40));
+ assert_object_contains(_obj, objs[0].content);
+}
+
+void test_odb_backend_simple__exists_succeeds(void)
+{
+ const fake_object objs[] = {
+ { "f6ea0495187600e7b2288c8ac19c5886383a4632", "foobar" },
+ { NULL, NULL }
+ };
+
+ setup_backend(objs);
+
+ cl_git_pass(git_oid__fromstr(&_oid, objs[0].oid, GIT_OID_SHA1));
+ cl_assert(git_odb_exists(_odb, &_oid));
+}
+
+void test_odb_backend_simple__exists_fails_for_nonexisting_object(void)
+{
+ const fake_object objs[] = {
+ { "f6ea0495187600e7b2288c8ac19c5886383a4632", "foobar" },
+ { NULL, NULL }
+ };
+
+ setup_backend(objs);
+
+ cl_git_pass(git_oid__fromstr(&_oid, "f6ea0495187600e7b2288c8ac19c5886383a4633", GIT_OID_SHA1));
+ cl_assert(git_odb_exists(_odb, &_oid) == 0);
+}
+
+void test_odb_backend_simple__exists_prefix_succeeds(void)
+{
+ const fake_object objs[] = {
+ { "1234567890111111111111111111111111111111", "first content" },
+ { "1234567890222222222222222222222222222222", "second content" },
+ { NULL, NULL }
+ };
+ git_oid found;
+
+ setup_backend(objs);
+
+ cl_git_pass(git_oid__fromstr(&_oid, objs[0].oid, GIT_OID_SHA1));
+ cl_git_pass(git_odb_exists_prefix(&found, _odb, &_oid, 12));
+ cl_assert(git_oid_equal(&found, &_oid));
+}
+
+void test_odb_backend_simple__exists_with_ambiguous_prefix_fails(void)
+{
+ const fake_object objs[] = {
+ { "1234567890111111111111111111111111111111", "first content" },
+ { "1234567890222222222222222222222222222222", "second content" },
+ { NULL, NULL }
+ };
+
+ setup_backend(objs);
+
+ cl_git_pass(git_oid__fromstr(&_oid, objs[0].oid, GIT_OID_SHA1));
+ cl_git_fail_with(GIT_EAMBIGUOUS, git_odb_exists_prefix(NULL, _odb, &_oid, 7));
+}
+
+void test_odb_backend_simple__exists_with_highly_ambiguous_prefix(void)
+{
+ const fake_object objs[] = {
+ { "1234567890111111111111111111111111111111", "first content" },
+ { "1234567890111111111111111111111111111112", "second content" },
+ { NULL, NULL }
+ };
+ git_oid found;
+
+ setup_backend(objs);
+
+ cl_git_pass(git_oid__fromstr(&_oid, objs[0].oid, GIT_OID_SHA1));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 0));
+ cl_git_fail_with(GIT_EAMBIGUOUS, git_odb_exists_prefix(&found, _odb, &_oid, 39));
+ cl_git_pass(git_odb_exists_prefix(&found, _odb, &_oid, 40));
+ cl_assert(git_oid_equal(&found, &_oid));
+}
+
+void test_odb_backend_simple__null_oid_is_ignored(void)
+{
+ const fake_object objs[] = {
+ { "0000000000000000000000000000000000000000", "null oid content" },
+ { NULL, NULL }
+ };
+ git_oid null_oid = GIT_OID_SHA1_ZERO;
+ git_odb_object *obj;
+
+ setup_backend(objs);
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_HASH_VERIFICATION, 0));
+ cl_assert(!git_odb_exists(_odb, &null_oid));
+
+ cl_git_fail_with(GIT_ENOTFOUND, git_odb_read(&obj, _odb, &null_oid));
+ cl_assert(git_error_last() && strstr(git_error_last()->message, "null OID"));
+}
diff --git a/tests/libgit2/odb/emptyobjects.c b/tests/libgit2/odb/emptyobjects.c
new file mode 100644
index 0000000..e7cc668
--- /dev/null
+++ b/tests/libgit2/odb/emptyobjects.c
@@ -0,0 +1,58 @@
+#include "clar_libgit2.h"
+#include "odb.h"
+#include "filebuf.h"
+
+#define TEST_REPO_PATH "redundant.git"
+
+git_repository *g_repo;
+git_odb *g_odb;
+
+void test_odb_emptyobjects__initialize(void)
+{
+ g_repo = cl_git_sandbox_init(TEST_REPO_PATH);
+ cl_git_pass(git_repository_odb(&g_odb, g_repo));
+}
+
+void test_odb_emptyobjects__cleanup(void)
+{
+ git_odb_free(g_odb);
+ cl_git_sandbox_cleanup();
+}
+
+void test_odb_emptyobjects__blob_notfound(void)
+{
+ git_oid id, written_id;
+ git_blob *blob;
+
+ cl_git_pass(git_oid__fromstr(&id, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391", GIT_OID_SHA1));
+ cl_git_fail_with(GIT_ENOTFOUND, git_blob_lookup(&blob, g_repo, &id));
+
+ cl_git_pass(git_odb_write(&written_id, g_odb, "", 0, GIT_OBJECT_BLOB));
+ cl_assert(git_fs_path_exists(TEST_REPO_PATH "/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391"));
+}
+
+void test_odb_emptyobjects__read_tree(void)
+{
+ git_oid id;
+ git_tree *tree;
+
+ cl_git_pass(git_oid__fromstr(&id, "4b825dc642cb6eb9a060e54bf8d69288fbee4904", GIT_OID_SHA1));
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &id));
+ cl_assert_equal_i(GIT_OBJECT_TREE, git_object_type((git_object *) tree));
+ cl_assert_equal_i(0, git_tree_entrycount(tree));
+ cl_assert_equal_p(NULL, git_tree_entry_byname(tree, "foo"));
+ git_tree_free(tree);
+}
+
+void test_odb_emptyobjects__read_tree_odb(void)
+{
+ git_oid id;
+ git_odb_object *tree_odb;
+
+ cl_git_pass(git_oid__fromstr(&id, "4b825dc642cb6eb9a060e54bf8d69288fbee4904", GIT_OID_SHA1));
+ cl_git_pass(git_odb_read(&tree_odb, g_odb, &id));
+ cl_assert(git_odb_object_data(tree_odb));
+ cl_assert_equal_s("", git_odb_object_data(tree_odb));
+ cl_assert_equal_i(0, git_odb_object_size(tree_odb));
+ git_odb_object_free(tree_odb);
+}
diff --git a/tests/libgit2/odb/foreach.c b/tests/libgit2/odb/foreach.c
new file mode 100644
index 0000000..56b3e88
--- /dev/null
+++ b/tests/libgit2/odb/foreach.c
@@ -0,0 +1,149 @@
+#include "clar_libgit2.h"
+#include "odb.h"
+#include "git2/odb_backend.h"
+#include "pack.h"
+
+static git_odb *_odb;
+static git_repository *_repo;
+
+void test_odb_foreach__cleanup(void)
+{
+ git_odb_free(_odb);
+ git_repository_free(_repo);
+
+ _odb = NULL;
+ _repo = NULL;
+}
+
+static int foreach_cb(const git_oid *oid, void *data)
+{
+ int *nobj = data;
+ (*nobj)++;
+
+ GIT_UNUSED(oid);
+
+ return 0;
+}
+
+/*
+ * $ git --git-dir tests/resources/testrepo.git count-objects --verbose
+ * count: 60
+ * size: 240
+ * in-pack: 1640
+ * packs: 3
+ * size-pack: 425
+ * prune-packable: 0
+ * garbage: 0
+ */
+void test_odb_foreach__foreach(void)
+{
+ int nobj = 0;
+
+ cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git")));
+ git_repository_odb(&_odb, _repo);
+
+ cl_git_pass(git_odb_foreach(_odb, foreach_cb, &nobj));
+ cl_assert_equal_i(60 + 1640, nobj); /* count + in-pack */
+}
+
+void test_odb_foreach__one_pack(void)
+{
+ git_odb_backend *backend = NULL;
+ int nobj = 0;
+
+ cl_git_pass(git_odb__new(&_odb, NULL));
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_odb_backend_one_pack(&backend,
+ cl_fixture("testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx"),
+ NULL));
+#else
+ cl_git_pass(git_odb_backend_one_pack(&backend,
+ cl_fixture("testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx")));
+#endif
+
+ cl_git_pass(git_odb_add_backend(_odb, backend, 1));
+ _repo = NULL;
+
+ cl_git_pass(git_odb_foreach(_odb, foreach_cb, &nobj));
+ cl_assert(nobj == 1628);
+}
+
+static int foreach_stop_cb(const git_oid *oid, void *data)
+{
+ int *nobj = data;
+ (*nobj)++;
+
+ GIT_UNUSED(oid);
+
+ return (*nobj == 1000) ? -321 : 0;
+}
+
+static int foreach_stop_first_cb(const git_oid *oid, void *data)
+{
+ int *nobj = data;
+ (*nobj)++;
+
+ GIT_UNUSED(oid);
+
+ return -123;
+}
+
+static int foreach_stop_cb_positive_ret(const git_oid *oid, void *data)
+{
+ int *nobj = data;
+ (*nobj)++;
+
+ GIT_UNUSED(oid);
+
+ return (*nobj == 1000) ? 321 : 0;
+}
+
+void test_odb_foreach__interrupt_foreach(void)
+{
+ int nobj = 0;
+ git_oid id;
+
+ cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git")));
+ git_repository_odb(&_odb, _repo);
+
+ cl_assert_equal_i(-321, git_odb_foreach(_odb, foreach_stop_cb, &nobj));
+ cl_assert(nobj == 1000);
+
+ nobj = 0;
+
+ cl_assert_equal_i(321, git_odb_foreach(_odb, foreach_stop_cb_positive_ret, &nobj));
+ cl_assert(nobj == 1000);
+
+ git_odb_free(_odb);
+ git_repository_free(_repo);
+
+ cl_git_pass(git_repository_init(&_repo, "onlyloose.git", true));
+ git_repository_odb(&_odb, _repo);
+
+ cl_git_pass(git_odb_write(&id, _odb, "", 0, GIT_OBJECT_BLOB));
+ cl_assert_equal_i(-123, git_odb_foreach(_odb, foreach_stop_first_cb, &nobj));
+}
+
+void test_odb_foreach__files_in_objects_dir(void)
+{
+ git_repository *repo;
+ git_odb *odb;
+ git_str buf = GIT_STR_INIT;
+ int nobj = 0;
+
+ cl_fixture_sandbox("testrepo.git");
+ cl_git_pass(git_repository_open(&repo, "testrepo.git"));
+
+ cl_git_pass(git_str_joinpath(&buf, git_repository_path(repo), "objects/somefile"));
+ cl_git_mkfile(buf.ptr, "");
+ git_str_dispose(&buf);
+
+ cl_git_pass(git_repository_odb(&odb, repo));
+ cl_git_pass(git_odb_foreach(odb, foreach_cb, &nobj));
+ cl_assert_equal_i(60 + 1640, nobj); /* count + in-pack */
+
+ git_odb_free(odb);
+ git_repository_free(repo);
+ cl_fixture_cleanup("testrepo.git");
+}
diff --git a/tests/libgit2/odb/freshen.c b/tests/libgit2/odb/freshen.c
new file mode 100644
index 0000000..e337c82
--- /dev/null
+++ b/tests/libgit2/odb/freshen.c
@@ -0,0 +1,183 @@
+#include "clar_libgit2.h"
+#include "odb.h"
+#include "posix.h"
+
+static git_repository *repo;
+static git_odb *odb;
+
+void test_odb_freshen__initialize(void)
+{
+ repo = cl_git_sandbox_init("testrepo.git");
+ cl_git_pass(git_repository_odb(&odb, repo));
+}
+
+void test_odb_freshen__cleanup(void)
+{
+ git_odb_free(odb);
+ cl_git_sandbox_cleanup();
+}
+
+static void set_time_wayback(struct stat *out, const char *fn)
+{
+ git_str fullpath = GIT_STR_INIT;
+ struct p_timeval old[2];
+
+ old[0].tv_sec = 1234567890;
+ old[0].tv_usec = 0;
+ old[1].tv_sec = 1234567890;
+ old[1].tv_usec = 0;
+
+ git_str_joinpath(&fullpath, "testrepo.git/objects", fn);
+
+ cl_must_pass(p_utimes(git_str_cstr(&fullpath), old));
+ cl_must_pass(p_lstat(git_str_cstr(&fullpath), out));
+ git_str_dispose(&fullpath);
+}
+
+#define LOOSE_STR "my new file\n"
+#define LOOSE_BLOB_ID "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd"
+#define LOOSE_BLOB_FN "a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd"
+
+void test_odb_freshen__loose_blob(void)
+{
+ git_oid expected_id, id;
+ struct stat before, after;
+
+ cl_git_pass(git_oid__fromstr(&expected_id, LOOSE_BLOB_ID, GIT_OID_SHA1));
+ set_time_wayback(&before, LOOSE_BLOB_FN);
+
+ /* make sure we freshen a blob */
+ cl_git_pass(git_blob_create_from_buffer(&id, repo, LOOSE_STR, CONST_STRLEN(LOOSE_STR)));
+ cl_assert_equal_oid(&expected_id, &id);
+ cl_must_pass(p_lstat("testrepo.git/objects/" LOOSE_BLOB_FN, &after));
+
+ cl_assert(before.st_atime < after.st_atime);
+ cl_assert(before.st_mtime < after.st_mtime);
+}
+
+#define UNIQUE_STR "doesnt exist in the odb yet\n"
+#define UNIQUE_BLOB_ID "78a87d0b8878c5953b9a63015ff4e22a3d898826"
+#define UNIQUE_BLOB_FN "78/a87d0b8878c5953b9a63015ff4e22a3d898826"
+
+void test_odb_freshen__readonly_object(void)
+{
+ git_oid expected_id, id;
+ struct stat before, after;
+
+ cl_git_pass(git_oid__fromstr(&expected_id, UNIQUE_BLOB_ID, GIT_OID_SHA1));
+
+ cl_git_pass(git_blob_create_from_buffer(&id, repo, UNIQUE_STR, CONST_STRLEN(UNIQUE_STR)));
+ cl_assert_equal_oid(&expected_id, &id);
+
+ set_time_wayback(&before, UNIQUE_BLOB_FN);
+ cl_assert((before.st_mode & S_IWUSR) == 0);
+
+ cl_git_pass(git_blob_create_from_buffer(&id, repo, UNIQUE_STR, CONST_STRLEN(UNIQUE_STR)));
+ cl_assert_equal_oid(&expected_id, &id);
+ cl_must_pass(p_lstat("testrepo.git/objects/" UNIQUE_BLOB_FN, &after));
+
+ cl_assert(before.st_atime < after.st_atime);
+ cl_assert(before.st_mtime < after.st_mtime);
+}
+
+#define LOOSE_TREE_ID "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162"
+#define LOOSE_TREE_FN "94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162"
+
+void test_odb_freshen__loose_tree(void)
+{
+ git_oid expected_id, id;
+ git_tree *tree;
+ struct stat before, after;
+
+ cl_git_pass(git_oid__fromstr(&expected_id, LOOSE_TREE_ID, GIT_OID_SHA1));
+ set_time_wayback(&before, LOOSE_TREE_FN);
+
+ cl_git_pass(git_tree_lookup(&tree, repo, &expected_id));
+ cl_git_pass(git_tree_create_updated(&id, repo, tree, 0, NULL));
+
+ /* make sure we freshen a tree */
+ cl_assert_equal_oid(&expected_id, &id);
+ cl_must_pass(p_lstat("testrepo.git/objects/" LOOSE_TREE_FN, &after));
+
+ cl_assert(before.st_atime < after.st_atime);
+ cl_assert(before.st_mtime < after.st_mtime);
+
+ git_tree_free(tree);
+}
+
+void test_odb_freshen__tree_during_commit(void)
+{
+ git_oid tree_id, parent_id, commit_id;
+ git_tree *tree;
+ git_commit *parent;
+ git_signature *signature;
+ struct stat before, after;
+
+ cl_git_pass(git_oid__fromstr(&tree_id, LOOSE_TREE_ID, GIT_OID_SHA1));
+ cl_git_pass(git_tree_lookup(&tree, repo, &tree_id));
+ set_time_wayback(&before, LOOSE_TREE_FN);
+
+ cl_git_pass(git_oid__fromstr(&parent_id, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", GIT_OID_SHA1));
+ cl_git_pass(git_commit_lookup(&parent, repo, &parent_id));
+
+ cl_git_pass(git_signature_new(&signature,
+ "Refresher", "refresher@example.com", 1488547083, 0));
+
+ cl_git_pass(git_commit_create(&commit_id, repo, NULL,
+ signature, signature, NULL, "New commit pointing to old tree",
+ tree, 1, (const git_commit **)&parent));
+
+ /* make sure we freshen the tree the commit points to */
+ cl_must_pass(p_lstat("testrepo.git/objects/" LOOSE_TREE_FN, &after));
+ cl_assert(before.st_atime < after.st_atime);
+ cl_assert(before.st_mtime < after.st_mtime);
+
+ git_signature_free(signature);
+ git_commit_free(parent);
+ git_tree_free(tree);
+}
+
+#define PACKED_STR "Testing a readme.txt\n"
+#define PACKED_ID "6336846bd5c88d32f93ae57d846683e61ab5c530"
+#define PACKED_FN "pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack"
+
+void test_odb_freshen__packed_object(void)
+{
+ git_oid expected_id, id;
+ struct stat before, after;
+ struct p_timeval old_times[2];
+
+ cl_git_pass(git_oid__fromstr(&expected_id, PACKED_ID, GIT_OID_SHA1));
+
+ old_times[0].tv_sec = 1234567890;
+ old_times[0].tv_usec = 0;
+ old_times[1].tv_sec = 1234567890;
+ old_times[1].tv_usec = 0;
+
+ /* set time to way back */
+ cl_must_pass(p_utimes("testrepo.git/objects/pack/" PACKED_FN, old_times));
+ cl_must_pass(p_lstat("testrepo.git/objects/pack/" PACKED_FN, &before));
+
+ /* ensure that packfile is freshened */
+ cl_git_pass(git_odb_write(&id, odb, PACKED_STR,
+ CONST_STRLEN(PACKED_STR), GIT_OBJECT_BLOB));
+ cl_assert_equal_oid(&expected_id, &id);
+ cl_must_pass(p_lstat("testrepo.git/objects/pack/" PACKED_FN, &after));
+
+ cl_assert(before.st_atime < after.st_atime);
+ cl_assert(before.st_mtime < after.st_mtime);
+
+ memcpy(&before, &after, sizeof(struct stat));
+
+ /* ensure that the pack file is not freshened again immediately */
+ cl_git_pass(git_odb_write(&id, odb, PACKED_STR,
+ CONST_STRLEN(PACKED_STR), GIT_OBJECT_BLOB));
+ cl_assert_equal_oid(&expected_id, &id);
+ cl_must_pass(p_lstat("testrepo.git/objects/pack/" PACKED_FN, &after));
+
+ cl_assert(before.st_atime == after.st_atime);
+ cl_assert(before.st_atime_nsec == after.st_atime_nsec);
+ cl_assert(before.st_mtime == after.st_mtime);
+ cl_assert(before.st_mtime_nsec == after.st_mtime_nsec);
+}
+
diff --git a/tests/libgit2/odb/largefiles.c b/tests/libgit2/odb/largefiles.c
new file mode 100644
index 0000000..2ec4810
--- /dev/null
+++ b/tests/libgit2/odb/largefiles.c
@@ -0,0 +1,189 @@
+#include "clar_libgit2.h"
+#include "git2/odb_backend.h"
+#include "hash.h"
+#include "odb.h"
+
+#define LARGEFILE_SIZE 5368709122
+
+static git_repository *repo;
+static git_odb *odb;
+
+void test_odb_largefiles__initialize(void)
+{
+ repo = cl_git_sandbox_init("testrepo.git");
+ cl_git_pass(git_repository_odb(&odb, repo));
+}
+
+void test_odb_largefiles__cleanup(void)
+{
+ git_odb_free(odb);
+ cl_git_sandbox_cleanup();
+}
+
+static void writefile(git_oid *oid)
+{
+ static git_odb_stream *stream;
+ git_str buf = GIT_STR_INIT;
+ size_t i;
+
+ for (i = 0; i < 3041; i++)
+ cl_git_pass(git_str_puts(&buf, "Hello, world.\n"));
+
+ cl_git_pass(git_odb_open_wstream(&stream, odb, LARGEFILE_SIZE, GIT_OBJECT_BLOB));
+ for (i = 0; i < 126103; i++)
+ cl_git_pass(git_odb_stream_write(stream, buf.ptr, buf.size));
+
+ cl_git_pass(git_odb_stream_finalize_write(oid, stream));
+
+ git_odb_stream_free(stream);
+ git_str_dispose(&buf);
+}
+
+void test_odb_largefiles__write_from_memory(void)
+{
+ git_oid expected, oid;
+ git_str buf = GIT_STR_INIT;
+ size_t i;
+
+#ifndef GIT_ARCH_64
+ cl_skip();
+#endif
+
+ if (!cl_is_env_set("GITTEST_INVASIVE_FS_SIZE") ||
+ !cl_is_env_set("GITTEST_INVASIVE_MEMORY") ||
+ !cl_is_env_set("GITTEST_SLOW"))
+ cl_skip();
+
+ for (i = 0; i < (3041*126103); i++)
+ cl_git_pass(git_str_puts(&buf, "Hello, world.\n"));
+
+ git_oid__fromstr(&expected, "3fb56989cca483b21ba7cb0a6edb229d10e1c26c", GIT_OID_SHA1);
+ cl_git_pass(git_odb_write(&oid, odb, buf.ptr, buf.size, GIT_OBJECT_BLOB));
+
+ cl_assert_equal_oid(&expected, &oid);
+}
+
+void test_odb_largefiles__streamwrite(void)
+{
+ git_oid expected, oid;
+
+#ifndef GIT_ARCH_64
+ cl_skip();
+#endif
+
+ if (!cl_is_env_set("GITTEST_INVASIVE_FS_SIZE") ||
+ !cl_is_env_set("GITTEST_SLOW"))
+ cl_skip();
+
+ git_oid__fromstr(&expected, "3fb56989cca483b21ba7cb0a6edb229d10e1c26c", GIT_OID_SHA1);
+ writefile(&oid);
+
+ cl_assert_equal_oid(&expected, &oid);
+}
+
+void test_odb_largefiles__streamread(void)
+{
+ git_oid oid, read_oid;
+ git_odb_stream *stream;
+ char buf[10240];
+ char hdr[64];
+ size_t len, hdr_len, total = 0;
+ git_hash_ctx hash;
+ git_object_t type;
+ int ret;
+
+#ifndef GIT_ARCH_64
+ cl_skip();
+#endif
+
+ if (!cl_is_env_set("GITTEST_INVASIVE_FS_SIZE") ||
+ !cl_is_env_set("GITTEST_SLOW"))
+ cl_skip();
+
+ writefile(&oid);
+
+ cl_git_pass(git_odb_open_rstream(&stream, &len, &type, odb, &oid));
+
+ cl_assert_equal_sz(LARGEFILE_SIZE, len);
+ cl_assert_equal_i(GIT_OBJECT_BLOB, type);
+
+ cl_git_pass(git_hash_ctx_init(&hash, GIT_HASH_ALGORITHM_SHA1));
+ cl_git_pass(git_odb__format_object_header(&hdr_len, hdr, sizeof(hdr), len, type));
+
+ cl_git_pass(git_hash_update(&hash, hdr, hdr_len));
+
+ while ((ret = git_odb_stream_read(stream, buf, 10240)) > 0) {
+ cl_git_pass(git_hash_update(&hash, buf, ret));
+ total += ret;
+ }
+
+ cl_assert_equal_sz(LARGEFILE_SIZE, total);
+
+ git_hash_final(read_oid.id, &hash);
+
+ cl_assert_equal_oid(&oid, &read_oid);
+
+ git_hash_ctx_cleanup(&hash);
+ git_odb_stream_free(stream);
+}
+
+void test_odb_largefiles__read_into_memory(void)
+{
+ git_oid oid;
+ git_odb_object *obj;
+
+#ifndef GIT_ARCH_64
+ cl_skip();
+#endif
+
+ if (!cl_is_env_set("GITTEST_INVASIVE_FS_SIZE") ||
+ !cl_is_env_set("GITTEST_INVASIVE_MEMORY") ||
+ !cl_is_env_set("GITTEST_SLOW"))
+ cl_skip();
+
+ writefile(&oid);
+ cl_git_pass(git_odb_read(&obj, odb, &oid));
+
+ git_odb_object_free(obj);
+}
+
+void test_odb_largefiles__read_into_memory_rejected_on_32bit(void)
+{
+ git_oid oid;
+ git_odb_object *obj = NULL;
+
+#ifdef GIT_ARCH_64
+ cl_skip();
+#endif
+
+ if (!cl_is_env_set("GITTEST_INVASIVE_FS_SIZE") ||
+ !cl_is_env_set("GITTEST_INVASIVE_MEMORY") ||
+ !cl_is_env_set("GITTEST_SLOW"))
+ cl_skip();
+
+ writefile(&oid);
+ cl_git_fail(git_odb_read(&obj, odb, &oid));
+
+ git_odb_object_free(obj);
+}
+
+void test_odb_largefiles__read_header(void)
+{
+ git_oid oid;
+ size_t len;
+ git_object_t type;
+
+#ifndef GIT_ARCH_64
+ cl_skip();
+#endif
+
+ if (!cl_is_env_set("GITTEST_INVASIVE_FS_SIZE") ||
+ !cl_is_env_set("GITTEST_SLOW"))
+ cl_skip();
+
+ writefile(&oid);
+ cl_git_pass(git_odb_read_header(&len, &type, odb, &oid));
+
+ cl_assert_equal_sz(LARGEFILE_SIZE, len);
+ cl_assert_equal_i(GIT_OBJECT_BLOB, type);
+}
diff --git a/tests/libgit2/odb/loose.c b/tests/libgit2/odb/loose.c
new file mode 100644
index 0000000..0409dfb
--- /dev/null
+++ b/tests/libgit2/odb/loose.c
@@ -0,0 +1,393 @@
+#include "clar_libgit2.h"
+#include "odb.h"
+#include "git2/odb_backend.h"
+#include "posix.h"
+#include "loose_data.h"
+#include "repository.h"
+
+#ifdef __ANDROID_API__
+# define S_IREAD S_IRUSR
+# define S_IWRITE S_IWUSR
+#endif
+
+static void write_object_files(object_data *d)
+{
+ int fd;
+
+ if (p_mkdir(d->dir, GIT_OBJECT_DIR_MODE) < 0)
+ cl_assert(errno == EEXIST);
+
+ cl_assert((fd = p_creat(d->file, S_IREAD | S_IWRITE)) >= 0);
+ cl_must_pass(p_write(fd, d->bytes, d->blen));
+
+ p_close(fd);
+}
+
+static void cmp_objects(git_rawobj *o, object_data *d)
+{
+ cl_assert(o->type == git_object_string2type(d->type));
+ cl_assert(o->len == d->dlen);
+
+ if (o->len > 0)
+ cl_assert(memcmp(o->data, d->data, o->len) == 0);
+}
+
+static void test_read_object(object_data *data)
+{
+ git_oid id;
+ git_odb_object *obj;
+ git_odb *odb;
+ git_rawobj tmp;
+ git_odb_options opts = GIT_ODB_OPTIONS_INIT;
+
+ opts.oid_type = data->id_type;
+
+ write_object_files(data);
+
+ cl_git_pass(git_odb__open(&odb, "test-objects", &opts));
+ cl_git_pass(git_oid__fromstr(&id, data->id, data->id_type));
+ cl_git_pass(git_odb_read(&obj, odb, &id));
+
+ tmp.data = obj->buffer;
+ tmp.len = obj->cached.size;
+ tmp.type = obj->cached.type;
+
+ cmp_objects(&tmp, data);
+
+ git_odb_object_free(obj);
+ git_odb_free(odb);
+}
+
+static void test_read_header(object_data *data)
+{
+ git_oid id;
+ git_odb *odb;
+ size_t len;
+ git_object_t type;
+ git_odb_options opts = GIT_ODB_OPTIONS_INIT;
+
+ opts.oid_type = data->id_type;
+
+ write_object_files(data);
+
+ cl_git_pass(git_odb__open(&odb, "test-objects", &opts));
+ cl_git_pass(git_oid__fromstr(&id, data->id, data->id_type));
+ cl_git_pass(git_odb_read_header(&len, &type, odb, &id));
+
+ cl_assert_equal_sz(data->dlen, len);
+ cl_assert_equal_i(git_object_string2type(data->type), type);
+
+ git_odb_free(odb);
+}
+
+static void test_readstream_object(object_data *data, size_t blocksize)
+{
+ git_oid id;
+ git_odb *odb;
+ git_odb_stream *stream;
+ git_rawobj tmp;
+ char buf[2048], *ptr = buf;
+ size_t remain;
+ int ret;
+ git_odb_options opts = GIT_ODB_OPTIONS_INIT;
+
+ opts.oid_type = data->id_type;
+
+ write_object_files(data);
+
+ cl_git_pass(git_odb__open(&odb, "test-objects", &opts));
+ cl_git_pass(git_oid__fromstr(&id, data->id, data->id_type));
+ cl_git_pass(git_odb_open_rstream(&stream, &tmp.len, &tmp.type, odb, &id));
+
+ remain = tmp.len;
+
+ while (remain) {
+ cl_assert((ret = git_odb_stream_read(stream, ptr, blocksize)) >= 0);
+ if (ret == 0)
+ break;
+
+ cl_assert(remain >= (size_t)ret);
+ remain -= ret;
+ ptr += ret;
+ }
+
+ cl_assert(remain == 0);
+
+ tmp.data = buf;
+
+ cmp_objects(&tmp, data);
+
+ git_odb_stream_free(stream);
+ git_odb_free(odb);
+}
+
+void test_odb_loose__initialize(void)
+{
+ p_fsync__cnt = 0;
+ cl_must_pass(p_mkdir("test-objects", GIT_OBJECT_DIR_MODE));
+}
+
+void test_odb_loose__cleanup(void)
+{
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_FSYNC_GITDIR, 0));
+ cl_fixture_cleanup("test-objects");
+}
+
+void test_odb_loose__exists_sha1(void)
+{
+ git_oid id, id2;
+ git_odb *odb;
+
+ write_object_files(&one);
+ cl_git_pass(git_odb__open(&odb, "test-objects", NULL));
+
+ cl_git_pass(git_oid__fromstr(&id, one.id, GIT_OID_SHA1));
+ cl_assert(git_odb_exists(odb, &id));
+
+ cl_git_pass(git_oid__fromstrp(&id, "8b137891", GIT_OID_SHA1));
+ cl_git_pass(git_odb_exists_prefix(&id2, odb, &id, 8));
+ cl_assert_equal_i(0, git_oid_streq(&id2, one.id));
+
+ /* Test for a missing object */
+ cl_git_pass(git_oid__fromstr(&id, "8b137891791fe96927ad78e64b0aad7bded08baa", GIT_OID_SHA1));
+ cl_assert(!git_odb_exists(odb, &id));
+
+ cl_git_pass(git_oid__fromstrp(&id, "8b13789a", GIT_OID_SHA1));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_odb_exists_prefix(&id2, odb, &id, 8));
+
+ git_odb_free(odb);
+}
+
+void test_odb_loose__exists_sha256(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ git_oid id, id2;
+ git_odb *odb;
+ git_odb_options odb_opts = GIT_ODB_OPTIONS_INIT;
+
+ odb_opts.oid_type = GIT_OID_SHA256;
+
+ write_object_files(&one_sha256);
+ cl_git_pass(git_odb__open(&odb, "test-objects", &odb_opts));
+
+ cl_git_pass(git_oid__fromstr(&id, one_sha256.id, GIT_OID_SHA256));
+ cl_assert(git_odb_exists(odb, &id));
+
+ cl_git_pass(git_oid__fromstrp(&id, "4c0d52d1", GIT_OID_SHA256));
+ cl_git_pass(git_odb_exists_prefix(&id2, odb, &id, 8));
+ cl_assert_equal_i(0, git_oid_streq(&id2, one_sha256.id));
+
+ /* Test for a missing object */
+ cl_git_pass(git_oid__fromstr(&id, "4c0d52d180c61d01ce1a91dec5ee58f0cbe65fd59433aea803ab927965493faa", GIT_OID_SHA256));
+ cl_assert(!git_odb_exists(odb, &id));
+
+ cl_git_pass(git_oid__fromstrp(&id, "4c0d52da", GIT_OID_SHA256));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_odb_exists_prefix(&id2, odb, &id, 8));
+
+ git_odb_free(odb);
+#endif
+}
+
+void test_odb_loose__simple_reads_sha1(void)
+{
+ test_read_object(&commit);
+ test_read_object(&tree);
+ test_read_object(&tag);
+ test_read_object(&zero);
+ test_read_object(&one);
+ test_read_object(&two);
+ test_read_object(&some);
+}
+
+void test_odb_loose__simple_reads_sha256(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ test_read_object(&commit_sha256);
+ test_read_object(&tree_sha256);
+ test_read_object(&tag_sha256);
+ test_read_object(&zero_sha256);
+ test_read_object(&one_sha256);
+ test_read_object(&two_sha256);
+ test_read_object(&some_sha256);
+#endif
+}
+
+void test_odb_loose__streaming_reads_sha1(void)
+{
+ size_t blocksizes[] = { 1, 2, 4, 16, 99, 1024, 123456789 };
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(blocksizes); i++) {
+ test_readstream_object(&commit, blocksizes[i]);
+ test_readstream_object(&tree, blocksizes[i]);
+ test_readstream_object(&tag, blocksizes[i]);
+ test_readstream_object(&zero, blocksizes[i]);
+ test_readstream_object(&one, blocksizes[i]);
+ test_readstream_object(&two, blocksizes[i]);
+ test_readstream_object(&some, blocksizes[i]);
+ }
+}
+
+void test_odb_loose__streaming_reads_sha256(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ size_t blocksizes[] = { 1, 2, 4, 16, 99, 1024, 123456789 };
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(blocksizes); i++) {
+ test_readstream_object(&commit_sha256, blocksizes[i]);
+ test_readstream_object(&tree_sha256, blocksizes[i]);
+ test_readstream_object(&tag_sha256, blocksizes[i]);
+ test_readstream_object(&zero_sha256, blocksizes[i]);
+ test_readstream_object(&one_sha256, blocksizes[i]);
+ test_readstream_object(&two_sha256, blocksizes[i]);
+ test_readstream_object(&some_sha256, blocksizes[i]);
+ }
+#endif
+}
+
+void test_odb_loose__read_header_sha1(void)
+{
+ test_read_header(&commit);
+ test_read_header(&tree);
+ test_read_header(&tag);
+ test_read_header(&zero);
+ test_read_header(&one);
+ test_read_header(&two);
+ test_read_header(&some);
+}
+
+void test_odb_loose__read_header_sha256(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ test_read_header(&commit_sha256);
+ test_read_header(&tree_sha256);
+ test_read_header(&tag_sha256);
+ test_read_header(&zero_sha256);
+ test_read_header(&one_sha256);
+ test_read_header(&two_sha256);
+ test_read_header(&some_sha256);
+#endif
+}
+
+static void test_write_object_permission(
+ mode_t dir_mode, mode_t file_mode,
+ mode_t expected_dir_mode, mode_t expected_file_mode)
+{
+ git_odb *odb;
+ git_odb_backend *backend;
+ git_oid oid;
+ struct stat statbuf;
+ mode_t mask, os_mask;
+ git_odb_backend_loose_options opts = GIT_ODB_BACKEND_LOOSE_OPTIONS_INIT;
+
+ /* Windows does not return group/user bits from stat,
+ * files are never executable.
+ */
+#ifdef GIT_WIN32
+ os_mask = 0600;
+#else
+ os_mask = 0777;
+#endif
+
+ mask = p_umask(0);
+ p_umask(mask);
+
+ opts.dir_mode = dir_mode;
+ opts.file_mode = file_mode;
+
+ cl_git_pass(git_odb__new(&odb, NULL));
+ cl_git_pass(git_odb__backend_loose(&backend, "test-objects", &opts));
+ cl_git_pass(git_odb_add_backend(odb, backend, 1));
+ cl_git_pass(git_odb_write(&oid, odb, "Test data\n", 10, GIT_OBJECT_BLOB));
+
+ cl_git_pass(p_stat("test-objects/67", &statbuf));
+ cl_assert_equal_i(statbuf.st_mode & os_mask, (expected_dir_mode & ~mask) & os_mask);
+
+ cl_git_pass(p_stat("test-objects/67/b808feb36201507a77f85e6d898f0a2836e4a5", &statbuf));
+ cl_assert_equal_i(statbuf.st_mode & os_mask, (expected_file_mode & ~mask) & os_mask);
+
+ git_odb_free(odb);
+}
+
+void test_odb_loose__permissions_standard(void)
+{
+ test_write_object_permission(0, 0, GIT_OBJECT_DIR_MODE, GIT_OBJECT_FILE_MODE);
+}
+
+void test_odb_loose__permissions_readonly(void)
+{
+ test_write_object_permission(0777, 0444, 0777, 0444);
+}
+
+void test_odb_loose__permissions_readwrite(void)
+{
+ test_write_object_permission(0777, 0666, 0777, 0666);
+}
+
+static void write_object_to_loose_odb(int fsync)
+{
+ git_odb *odb;
+ git_odb_backend *backend;
+ git_oid oid;
+ git_odb_backend_loose_options opts = GIT_ODB_BACKEND_LOOSE_OPTIONS_INIT;
+
+ if (fsync)
+ opts.flags |= GIT_ODB_BACKEND_LOOSE_FSYNC;
+
+ opts.dir_mode = 0777;
+ opts.file_mode = 0666;
+
+ cl_git_pass(git_odb__new(&odb, NULL));
+ cl_git_pass(git_odb__backend_loose(&backend, "test-objects", &opts));
+ cl_git_pass(git_odb_add_backend(odb, backend, 1));
+ cl_git_pass(git_odb_write(&oid, odb, "Test data\n", 10, GIT_OBJECT_BLOB));
+ git_odb_free(odb);
+}
+
+void test_odb_loose__does_not_fsync_by_default(void)
+{
+ write_object_to_loose_odb(0);
+ cl_assert_equal_sz(0, p_fsync__cnt);
+}
+
+void test_odb_loose__fsync_obeys_odb_option(void)
+{
+ write_object_to_loose_odb(1);
+ cl_assert(p_fsync__cnt > 0);
+}
+
+void test_odb_loose__fsync_obeys_global_setting(void)
+{
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_FSYNC_GITDIR, 1));
+ write_object_to_loose_odb(0);
+ cl_assert(p_fsync__cnt > 0);
+}
+
+void test_odb_loose__fsync_obeys_repo_setting(void)
+{
+ git_repository *repo;
+ git_odb *odb;
+ git_oid oid;
+
+ cl_git_pass(git_repository_init(&repo, "test-objects", 1));
+ cl_git_pass(git_repository_odb__weakptr(&odb, repo));
+ cl_git_pass(git_odb_write(&oid, odb, "No fsync here\n", 14, GIT_OBJECT_BLOB));
+ cl_assert(p_fsync__cnt == 0);
+ git_repository_free(repo);
+
+ cl_git_pass(git_repository_open(&repo, "test-objects"));
+ cl_repo_set_bool(repo, "core.fsyncObjectFiles", true);
+ cl_git_pass(git_repository_odb__weakptr(&odb, repo));
+ cl_git_pass(git_odb_write(&oid, odb, "Now fsync\n", 10, GIT_OBJECT_BLOB));
+ cl_assert(p_fsync__cnt > 0);
+ git_repository_free(repo);
+}
diff --git a/tests/libgit2/odb/loose_data.h b/tests/libgit2/odb/loose_data.h
new file mode 100644
index 0000000..1a83074
--- /dev/null
+++ b/tests/libgit2/odb/loose_data.h
@@ -0,0 +1,893 @@
+typedef struct object_data {
+ unsigned char *bytes; /* (compressed) bytes stored in object store */
+ size_t blen; /* length of data in object store */
+ char *id; /* object id (hex chars) */
+ git_oid_t id_type; /* type of object id (sha1 or sha256) */
+ char *type; /* object type */
+ char *dir; /* object store (fan-out) directory name */
+ char *file; /* object store filename */
+ unsigned char *data; /* (uncompressed) object data */
+ size_t dlen; /* length of (uncompressed) object data */
+} object_data;
+
+/*
+ * one == 8b137891791fe96927ad78e64b0aad7bded08bdc (sha1)
+ * 4c0d52d180c61d01ce1a91dec5ee58f0cbe65fd59433aea803ab927965493fd7 (sha256)
+ */
+static unsigned char one_bytes[] = {
+ 0x31, 0x78, 0x9c, 0xe3, 0x02, 0x00, 0x00, 0x0b,
+ 0x00, 0x0b,
+};
+
+static unsigned char one_data[] = {
+ 0x0a,
+};
+
+static object_data one = {
+ one_bytes,
+ sizeof(one_bytes),
+ "8b137891791fe96927ad78e64b0aad7bded08bdc",
+ GIT_OID_SHA1,
+ "blob",
+ "test-objects/8b",
+ "test-objects/8b/137891791fe96927ad78e64b0aad7bded08bdc",
+ one_data,
+ sizeof(one_data),
+};
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+static object_data one_sha256 = {
+ one_bytes,
+ sizeof(one_bytes),
+ "4c0d52d180c61d01ce1a91dec5ee58f0cbe65fd59433aea803ab927965493fd7",
+ GIT_OID_SHA256,
+ "blob",
+ "test-objects/4c",
+ "test-objects/4c/0d52d180c61d01ce1a91dec5ee58f0cbe65fd59433aea803ab927965493fd7",
+ one_data,
+ sizeof(one_data),
+};
+#endif
+
+
+/*
+ * commit == 3d7f8a6af076c8c3f20071a8935cdbe8228594d1 (sha1)
+ * a2a430fb63b294f868af4ef6ccc9c3e8256e370859ce578a23837ac85337f562 (sha256)
+ */
+static unsigned char commit_bytes[] = {
+ 0x78, 0x01, 0x85, 0x50, 0xc1, 0x6a, 0xc3, 0x30,
+ 0x0c, 0xdd, 0xd9, 0x5f, 0xa1, 0xfb, 0x96, 0x12,
+ 0xbb, 0x29, 0x71, 0x46, 0x19, 0x2b, 0x3d, 0x97,
+ 0x1d, 0xd6, 0x7d, 0x80, 0x1d, 0xcb, 0x89, 0x21,
+ 0xb6, 0x82, 0xed, 0x40, 0xf3, 0xf7, 0xf3, 0x48,
+ 0x29, 0x3b, 0x6d, 0xd2, 0xe5, 0xbd, 0x27, 0xbd,
+ 0x27, 0x50, 0x4f, 0xde, 0xbb, 0x0c, 0xfb, 0x43,
+ 0xf3, 0x94, 0x23, 0x22, 0x18, 0x6b, 0x85, 0x51,
+ 0x5d, 0xad, 0xc5, 0xa1, 0x41, 0xae, 0x51, 0x4b,
+ 0xd9, 0x19, 0x6e, 0x4b, 0x0b, 0x29, 0x35, 0x72,
+ 0x59, 0xef, 0x5b, 0x29, 0x8c, 0x65, 0x6a, 0xc9,
+ 0x23, 0x45, 0x38, 0xc1, 0x17, 0x5c, 0x7f, 0xc0,
+ 0x71, 0x13, 0xde, 0xf1, 0xa6, 0xfc, 0x3c, 0xe1,
+ 0xae, 0x27, 0xff, 0x06, 0x5c, 0x88, 0x56, 0xf2,
+ 0x46, 0x74, 0x2d, 0x3c, 0xd7, 0xa5, 0x58, 0x51,
+ 0xcb, 0xb9, 0x8c, 0x11, 0xce, 0xf0, 0x01, 0x97,
+ 0x0d, 0x1e, 0x1f, 0xea, 0x3f, 0x6e, 0x76, 0x02,
+ 0x0a, 0x58, 0x4d, 0x2e, 0x20, 0x6c, 0x1e, 0x48,
+ 0x8b, 0xf7, 0x2a, 0xae, 0x8c, 0x5d, 0x47, 0x04,
+ 0x4d, 0x66, 0x05, 0xb2, 0x90, 0x0b, 0xbe, 0xcf,
+ 0x3d, 0xa6, 0xa4, 0x06, 0x7c, 0x29, 0x3c, 0x64,
+ 0xe5, 0x82, 0x0b, 0x03, 0xd8, 0x25, 0x96, 0x8d,
+ 0x08, 0x78, 0x9b, 0x27, 0x15, 0x54, 0x76, 0x14,
+ 0xd8, 0xdd, 0x35, 0x2f, 0x71, 0xa6, 0x84, 0x8f,
+ 0x90, 0x51, 0x85, 0x01, 0x13, 0xb8, 0x90, 0x23,
+ 0x99, 0xa5, 0x47, 0x03, 0x7a, 0xfd, 0x15, 0xbf,
+ 0x63, 0xec, 0xd3, 0x0d, 0x01, 0x4d, 0x45, 0xb6,
+ 0xd2, 0xeb, 0xeb, 0xdf, 0xef, 0x60, 0xdf, 0xef,
+ 0x1f, 0x78, 0x35,
+};
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+static unsigned char commit_bytes_sha256[] = {
+ 0x78, 0x01, 0x85, 0x90, 0xc1, 0x4e, 0xc3, 0x30,
+ 0x0c, 0x86, 0x39, 0xe7, 0x29, 0x7c, 0x87, 0x4e,
+ 0x5d, 0x93, 0xa6, 0x2d, 0x9a, 0x10, 0x13, 0x67,
+ 0xc4, 0x81, 0xf1, 0x00, 0x4e, 0xe3, 0xb4, 0x91,
+ 0x9a, 0xa4, 0x4a, 0x53, 0x69, 0x7d, 0x7b, 0x82,
+ 0x3a, 0x4d, 0x9c, 0xc0, 0xa7, 0xcf, 0xbf, 0xfd,
+ 0xff, 0xb2, 0xdc, 0x07, 0xe7, 0x6c, 0x02, 0xde,
+ 0xb4, 0x0f, 0x29, 0x12, 0x01, 0x17, 0x28, 0xda,
+ 0x5a, 0xa8, 0x5a, 0x54, 0xd2, 0x74, 0x95, 0x90,
+ 0xa5, 0x12, 0x48, 0xbc, 0x26, 0xa9, 0x9b, 0xae,
+ 0x11, 0x52, 0x91, 0x94, 0x3d, 0x6f, 0x95, 0x31,
+ 0x5a, 0x92, 0xe1, 0xaa, 0x17, 0xa6, 0xac, 0x39,
+ 0xe9, 0xa6, 0x45, 0x2e, 0x15, 0x0a, 0x86, 0x6b,
+ 0x1a, 0x43, 0x84, 0x33, 0x7c, 0xc1, 0xe5, 0x07,
+ 0x4e, 0xbb, 0xf0, 0x4a, 0x57, 0x74, 0xf3, 0x44,
+ 0x87, 0x3e, 0xb8, 0x17, 0x38, 0x56, 0x55, 0xd3,
+ 0x1e, 0x45, 0xd5, 0x35, 0xf0, 0x58, 0xe6, 0x62,
+ 0x59, 0xcd, 0x67, 0x24, 0x8a, 0xf0, 0x06, 0x1f,
+ 0xf0, 0xbe, 0xe3, 0xe9, 0xae, 0xfe, 0xe3, 0x66,
+ 0x67, 0x08, 0x9e, 0x8a, 0xc9, 0x7a, 0x82, 0xdd,
+ 0x03, 0xcb, 0xea, 0x1c, 0xc6, 0x8d, 0xb1, 0xcb,
+ 0x48, 0xa0, 0x82, 0xde, 0x20, 0x18, 0x48, 0x99,
+ 0x6f, 0x73, 0x47, 0xcb, 0x82, 0x03, 0x3d, 0xe5,
+ 0xde, 0x27, 0xb4, 0xde, 0xfa, 0x01, 0xcc, 0x1a,
+ 0xf3, 0x46, 0x04, 0xba, 0xce, 0x13, 0x7a, 0x4c,
+ 0x36, 0x78, 0x76, 0x73, 0xcd, 0x6b, 0x9c, 0xc3,
+ 0x42, 0xf7, 0x90, 0x11, 0xfd, 0x40, 0x0b, 0x58,
+ 0x9f, 0x62, 0xd0, 0x6b, 0x4f, 0x1a, 0xd4, 0xf6,
+ 0x2b, 0xfe, 0xc0, 0xd8, 0xa7, 0x1d, 0x3c, 0xe9,
+ 0x22, 0x98, 0x42, 0x6d, 0xcf, 0x7f, 0xbf, 0x83,
+ 0x7d, 0x03, 0x6d, 0x1e, 0x7e, 0xa9
+};
+#endif
+
+static unsigned char commit_data[] = {
+ 0x74, 0x72, 0x65, 0x65, 0x20, 0x64, 0x66, 0x66,
+ 0x32, 0x64, 0x61, 0x39, 0x30, 0x62, 0x32, 0x35,
+ 0x34, 0x65, 0x31, 0x62, 0x65, 0x62, 0x38, 0x38,
+ 0x39, 0x64, 0x31, 0x66, 0x31, 0x66, 0x31, 0x32,
+ 0x38, 0x38, 0x62, 0x65, 0x31, 0x38, 0x30, 0x33,
+ 0x37, 0x38, 0x32, 0x64, 0x66, 0x0a, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x20, 0x41, 0x20, 0x55,
+ 0x20, 0x54, 0x68, 0x6f, 0x72, 0x20, 0x3c, 0x61,
+ 0x75, 0x74, 0x68, 0x6f, 0x72, 0x40, 0x65, 0x78,
+ 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x3e, 0x20, 0x31, 0x32, 0x32, 0x37, 0x38,
+ 0x31, 0x34, 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30,
+ 0x30, 0x30, 0x30, 0x0a, 0x63, 0x6f, 0x6d, 0x6d,
+ 0x69, 0x74, 0x74, 0x65, 0x72, 0x20, 0x43, 0x20,
+ 0x4f, 0x20, 0x4d, 0x69, 0x74, 0x74, 0x65, 0x72,
+ 0x20, 0x3c, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
+ 0x74, 0x65, 0x72, 0x40, 0x65, 0x78, 0x61, 0x6d,
+ 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e,
+ 0x20, 0x31, 0x32, 0x32, 0x37, 0x38, 0x31, 0x34,
+ 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30, 0x30, 0x30,
+ 0x30, 0x0a, 0x0a, 0x41, 0x20, 0x6f, 0x6e, 0x65,
+ 0x2d, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x63, 0x6f,
+ 0x6d, 0x6d, 0x69, 0x74, 0x20, 0x73, 0x75, 0x6d,
+ 0x6d, 0x61, 0x72, 0x79, 0x0a, 0x0a, 0x54, 0x68,
+ 0x65, 0x20, 0x62, 0x6f, 0x64, 0x79, 0x20, 0x6f,
+ 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f,
+ 0x6d, 0x6d, 0x69, 0x74, 0x20, 0x6d, 0x65, 0x73,
+ 0x73, 0x61, 0x67, 0x65, 0x2c, 0x20, 0x63, 0x6f,
+ 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67,
+ 0x20, 0x66, 0x75, 0x72, 0x74, 0x68, 0x65, 0x72,
+ 0x20, 0x65, 0x78, 0x70, 0x6c, 0x61, 0x6e, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x6f, 0x66, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x70, 0x75, 0x72, 0x70,
+ 0x6f, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67,
+ 0x65, 0x73, 0x20, 0x69, 0x6e, 0x74, 0x72, 0x6f,
+ 0x64, 0x75, 0x63, 0x65, 0x64, 0x20, 0x62, 0x79,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6d,
+ 0x6d, 0x69, 0x74, 0x2e, 0x0a, 0x0a, 0x53, 0x69,
+ 0x67, 0x6e, 0x65, 0x64, 0x2d, 0x6f, 0x66, 0x2d,
+ 0x62, 0x79, 0x3a, 0x20, 0x41, 0x20, 0x55, 0x20,
+ 0x54, 0x68, 0x6f, 0x72, 0x20, 0x3c, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x40, 0x65, 0x78, 0x61,
+ 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x3e, 0x0a,
+};
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+static unsigned char commit_data_sha256[] = {
+ 0x74, 0x72, 0x65, 0x65, 0x20, 0x33, 0x34, 0x61,
+ 0x34, 0x38, 0x35, 0x34, 0x62, 0x35, 0x34, 0x32,
+ 0x36, 0x66, 0x39, 0x32, 0x34, 0x36, 0x30, 0x62,
+ 0x34, 0x61, 0x65, 0x33, 0x35, 0x65, 0x36, 0x64,
+ 0x37, 0x39, 0x37, 0x34, 0x36, 0x62, 0x65, 0x36,
+ 0x36, 0x63, 0x33, 0x38, 0x62, 0x66, 0x66, 0x64,
+ 0x36, 0x65, 0x66, 0x33, 0x62, 0x63, 0x34, 0x66,
+ 0x30, 0x35, 0x33, 0x65, 0x64, 0x37, 0x38, 0x61,
+ 0x33, 0x36, 0x62, 0x61, 0x34, 0x0a, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x20, 0x41, 0x20, 0x55,
+ 0x20, 0x54, 0x68, 0x6f, 0x72, 0x20, 0x3c, 0x61,
+ 0x75, 0x74, 0x68, 0x6f, 0x72, 0x40, 0x65, 0x78,
+ 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f,
+ 0x6d, 0x3e, 0x20, 0x31, 0x32, 0x32, 0x37, 0x38,
+ 0x31, 0x34, 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30,
+ 0x30, 0x30, 0x30, 0x0a, 0x63, 0x6f, 0x6d, 0x6d,
+ 0x69, 0x74, 0x74, 0x65, 0x72, 0x20, 0x43, 0x20,
+ 0x4f, 0x20, 0x4d, 0x69, 0x74, 0x74, 0x65, 0x72,
+ 0x20, 0x3c, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
+ 0x74, 0x65, 0x72, 0x40, 0x65, 0x78, 0x61, 0x6d,
+ 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e,
+ 0x20, 0x31, 0x32, 0x32, 0x37, 0x38, 0x31, 0x34,
+ 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30, 0x30, 0x30,
+ 0x30, 0x0a, 0x0a, 0x41, 0x20, 0x6f, 0x6e, 0x65,
+ 0x2d, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x63, 0x6f,
+ 0x6d, 0x6d, 0x69, 0x74, 0x20, 0x73, 0x75, 0x6d,
+ 0x6d, 0x61, 0x72, 0x79, 0x0a, 0x0a, 0x54, 0x68,
+ 0x65, 0x20, 0x62, 0x6f, 0x64, 0x79, 0x20, 0x6f,
+ 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f,
+ 0x6d, 0x6d, 0x69, 0x74, 0x20, 0x6d, 0x65, 0x73,
+ 0x73, 0x61, 0x67, 0x65, 0x2c, 0x20, 0x63, 0x6f,
+ 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67,
+ 0x20, 0x66, 0x75, 0x72, 0x74, 0x68, 0x65, 0x72,
+ 0x20, 0x65, 0x78, 0x70, 0x6c, 0x61, 0x6e, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x6f, 0x66, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x70, 0x75, 0x72, 0x70,
+ 0x6f, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67,
+ 0x65, 0x73, 0x20, 0x69, 0x6e, 0x74, 0x72, 0x6f,
+ 0x64, 0x75, 0x63, 0x65, 0x64, 0x20, 0x62, 0x79,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6d,
+ 0x6d, 0x69, 0x74, 0x2e, 0x0a, 0x0a, 0x53, 0x69,
+ 0x67, 0x6e, 0x65, 0x64, 0x2d, 0x6f, 0x66, 0x2d,
+ 0x62, 0x79, 0x3a, 0x20, 0x41, 0x20, 0x55, 0x20,
+ 0x54, 0x68, 0x6f, 0x72, 0x20, 0x3c, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x40, 0x65, 0x78, 0x61,
+ 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x3e, 0x0a
+};
+#endif
+
+static object_data commit = {
+ commit_bytes,
+ sizeof(commit_bytes),
+ "3d7f8a6af076c8c3f20071a8935cdbe8228594d1",
+ GIT_OID_SHA1,
+ "commit",
+ "test-objects/3d",
+ "test-objects/3d/7f8a6af076c8c3f20071a8935cdbe8228594d1",
+ commit_data,
+ sizeof(commit_data),
+};
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+static object_data commit_sha256 = {
+ commit_bytes_sha256,
+ sizeof(commit_bytes_sha256),
+ "a2a430fb63b294f868af4ef6ccc9c3e8256e370859ce578a23837ac85337f562",
+ GIT_OID_SHA256,
+ "commit",
+ "test-objects/a2",
+ "test-objects/a2/a430fb63b294f868af4ef6ccc9c3e8256e370859ce578a23837ac85337f562",
+ commit_data_sha256,
+ sizeof(commit_data_sha256),
+};
+#endif
+
+/*
+ * tree == dff2da90b254e1beb889d1f1f1288be1803782df (sha1)
+ * 34a4854b5426f92460b4ae35e6d79746be66c38bffd6ef3bc4f053ed78a36ba4 (sha256)
+ */
+static unsigned char tree_bytes[] = {
+ 0x78, 0x01, 0x2b, 0x29, 0x4a, 0x4d, 0x55, 0x30,
+ 0x34, 0x32, 0x63, 0x30, 0x34, 0x30, 0x30, 0x33,
+ 0x31, 0x51, 0xc8, 0xcf, 0x4b, 0x65, 0xe8, 0x16,
+ 0xae, 0x98, 0x58, 0x29, 0xff, 0x32, 0x53, 0x7d,
+ 0x6d, 0xc5, 0x33, 0x6f, 0xae, 0xb5, 0xd5, 0xf7,
+ 0x2e, 0x74, 0xdf, 0x81, 0x4a, 0x17, 0xe7, 0xe7,
+ 0xa6, 0x32, 0xfc, 0x6d, 0x31, 0xd8, 0xd3, 0xe6,
+ 0xf3, 0xe7, 0xea, 0x47, 0xbe, 0xd0, 0x09, 0x3f,
+ 0x96, 0xb8, 0x3f, 0x90, 0x9e, 0xa2, 0xfd, 0x0f,
+ 0x2a, 0x5f, 0x52, 0x9e, 0xcf, 0x50, 0x31, 0x43,
+ 0x52, 0x29, 0xd1, 0x5a, 0xeb, 0x77, 0x82, 0x2a,
+ 0x8b, 0xfe, 0xb7, 0xbd, 0xed, 0x5d, 0x07, 0x67,
+ 0xfa, 0xb5, 0x42, 0xa5, 0xab, 0x52, 0x8b, 0xf2,
+ 0x19, 0x9e, 0xcd, 0x7d, 0x34, 0x7b, 0xd3, 0xc5,
+ 0x6b, 0xce, 0xde, 0xdd, 0x9a, 0xeb, 0xca, 0xa3,
+ 0x6e, 0x1c, 0x7a, 0xd2, 0x13, 0x3c, 0x11, 0x00,
+ 0xe2, 0xaa, 0x38, 0x57,
+};
+
+static unsigned char tree_data[] = {
+ 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x6f,
+ 0x6e, 0x65, 0x00, 0x8b, 0x13, 0x78, 0x91, 0x79,
+ 0x1f, 0xe9, 0x69, 0x27, 0xad, 0x78, 0xe6, 0x4b,
+ 0x0a, 0xad, 0x7b, 0xde, 0xd0, 0x8b, 0xdc, 0x31,
+ 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x73, 0x6f,
+ 0x6d, 0x65, 0x00, 0xfd, 0x84, 0x30, 0xbc, 0x86,
+ 0x4c, 0xfc, 0xd5, 0xf1, 0x0e, 0x55, 0x90, 0xf8,
+ 0xa4, 0x47, 0xe0, 0x1b, 0x94, 0x2b, 0xfe, 0x31,
+ 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x74, 0x77,
+ 0x6f, 0x00, 0x78, 0x98, 0x19, 0x22, 0x61, 0x3b,
+ 0x2a, 0xfb, 0x60, 0x25, 0x04, 0x2f, 0xf6, 0xbd,
+ 0x87, 0x8a, 0xc1, 0x99, 0x4e, 0x85, 0x31, 0x30,
+ 0x30, 0x36, 0x34, 0x34, 0x20, 0x7a, 0x65, 0x72,
+ 0x6f, 0x00, 0xe6, 0x9d, 0xe2, 0x9b, 0xb2, 0xd1,
+ 0xd6, 0x43, 0x4b, 0x8b, 0x29, 0xae, 0x77, 0x5a,
+ 0xd8, 0xc2, 0xe4, 0x8c, 0x53, 0x91,
+};
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+static unsigned char tree_bytes_sha256[] = {
+ 0x78, 0x01, 0x2b, 0x29, 0x4a, 0x4d, 0x55, 0x30,
+ 0x32, 0x32, 0x66, 0x30, 0x34, 0x30, 0x30, 0x33,
+ 0x31, 0x51, 0xc8, 0x48, 0xcd, 0xc9, 0xc9, 0xd7,
+ 0x2b, 0xa9, 0x28, 0x61, 0x28, 0x65, 0x3b, 0x7d,
+ 0xde, 0x27, 0x5c, 0xfb, 0xe5, 0x83, 0x2c, 0xf9,
+ 0xb7, 0xa6, 0x6b, 0xa2, 0x65, 0x7f, 0x6c, 0x5d,
+ 0xee, 0xab, 0x76, 0xa0, 0x9e, 0x49, 0xcd, 0xe3,
+ 0xe9, 0xcd, 0xa8, 0xf9, 0xf9, 0x5a, 0x50, 0x0d,
+ 0xf9, 0x79, 0xa9, 0x0c, 0x3e, 0xbc, 0x41, 0x17,
+ 0x1b, 0x8e, 0xc9, 0x32, 0x9e, 0x93, 0x9a, 0x78,
+ 0xef, 0xe8, 0xbb, 0x88, 0x0f, 0xa7, 0x9f, 0xc5,
+ 0x5f, 0x9d, 0x62, 0xbc, 0x6e, 0x05, 0xf3, 0xea,
+ 0x49, 0x95, 0xa9, 0x9e, 0xf6, 0xd7, 0xa1, 0x4a,
+ 0x8b, 0xf3, 0x73, 0x53, 0x19, 0x38, 0x6c, 0xb4,
+ 0xbb, 0x5d, 0xc2, 0x1c, 0x2e, 0x16, 0x3e, 0x5f,
+ 0x95, 0x56, 0xcd, 0x6d, 0xc4, 0x50, 0xc0, 0xf6,
+ 0xbd, 0xad, 0x50, 0xc0, 0xe8, 0xf5, 0x0e, 0x4d,
+ 0xc3, 0x33, 0xcb, 0xe6, 0x1c, 0x8c, 0x86, 0xaa,
+ 0x2d, 0x29, 0xcf, 0x67, 0xf8, 0x91, 0x14, 0xe7,
+ 0xfc, 0xf3, 0x81, 0xbf, 0x8a, 0xa6, 0x7c, 0xf9,
+ 0xd9, 0x7d, 0x3e, 0x85, 0x9b, 0x0f, 0x2d, 0xde,
+ 0xc0, 0x60, 0x9f, 0xe0, 0x38, 0xdb, 0xee, 0x42,
+ 0x16, 0x6b, 0x6f, 0x59, 0x4e, 0x37, 0x54, 0x69,
+ 0x55, 0x6a, 0x51, 0x3e, 0x83, 0xcb, 0xbc, 0xd9,
+ 0x95, 0x21, 0x0a, 0x67, 0xc5, 0xfe, 0x25, 0xac,
+ 0x0d, 0x9a, 0x71, 0x3e, 0x83, 0x5f, 0x74, 0xf9,
+ 0x59, 0xad, 0x93, 0x5b, 0xbc, 0x6e, 0x7d, 0x7f,
+ 0x6b, 0x77, 0x87, 0x97, 0xe3, 0x6e, 0x05, 0x00,
+ 0xba, 0xd1, 0x5f, 0x75
+};
+
+static unsigned char tree_data_sha256[] = {
+ 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x68,
+ 0x65, 0x6c, 0x6c, 0x6f, 0x2e, 0x74, 0x78, 0x74,
+ 0x00, 0x75, 0x06, 0xcb, 0xcf, 0x4c, 0x57, 0x2b,
+ 0xe9, 0xe0, 0x6a, 0x1f, 0xed, 0x35, 0xac, 0x5b,
+ 0x1d, 0xf8, 0xb5, 0xa7, 0x4d, 0x26, 0xc0, 0x7f,
+ 0x02, 0x26, 0x48, 0xe5, 0xd9, 0x5a, 0x9f, 0x6f,
+ 0x2a, 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20,
+ 0x6f, 0x6e, 0x65, 0x00, 0x4c, 0x0d, 0x52, 0xd1,
+ 0x80, 0xc6, 0x1d, 0x01, 0xce, 0x1a, 0x91, 0xde,
+ 0xc5, 0xee, 0x58, 0xf0, 0xcb, 0xe6, 0x5f, 0xd5,
+ 0x94, 0x33, 0xae, 0xa8, 0x03, 0xab, 0x92, 0x79,
+ 0x65, 0x49, 0x3f, 0xd7, 0x31, 0x30, 0x30, 0x36,
+ 0x34, 0x34, 0x20, 0x73, 0x6f, 0x6d, 0x65, 0x00,
+ 0x08, 0x3c, 0x2b, 0x8b, 0x44, 0x56, 0x40, 0xd1,
+ 0x71, 0xe7, 0xaa, 0x66, 0x7b, 0x0b, 0x32, 0x00,
+ 0x70, 0x06, 0xf7, 0x86, 0x71, 0x10, 0x32, 0xeb,
+ 0xb8, 0x29, 0x31, 0xcc, 0xa6, 0x9c, 0xc1, 0x5b,
+ 0x31, 0x30, 0x30, 0x36, 0x34, 0x34, 0x20, 0x74,
+ 0x77, 0x6f, 0x00, 0xf8, 0x62, 0x5e, 0x43, 0xf9,
+ 0xe0, 0x4f, 0x24, 0x29, 0x1f, 0x77, 0xcd, 0xbe,
+ 0x4c, 0x71, 0xb3, 0xc2, 0xa3, 0xb0, 0x00, 0x3f,
+ 0x60, 0x41, 0x9b, 0x3e, 0xd0, 0x6a, 0x05, 0x8d,
+ 0x76, 0x6c, 0x8b, 0x31, 0x30, 0x30, 0x36, 0x34,
+ 0x34, 0x20, 0x7a, 0x65, 0x72, 0x6f, 0x00, 0x44,
+ 0x9e, 0x9b, 0x79, 0x54, 0x20, 0xcd, 0x16, 0xfe,
+ 0x60, 0xad, 0x52, 0x98, 0xcf, 0x68, 0x0f, 0x15,
+ 0xa7, 0xcd, 0x2a, 0xc9, 0xb4, 0x4a, 0xda, 0xf7,
+ 0xed, 0x3e, 0xdc, 0x0d, 0x08, 0xdd, 0x78
+};
+#endif
+
+static object_data tree = {
+ tree_bytes,
+ sizeof(tree_bytes),
+ "dff2da90b254e1beb889d1f1f1288be1803782df",
+ GIT_OID_SHA1,
+ "tree",
+ "test-objects/df",
+ "test-objects/df/f2da90b254e1beb889d1f1f1288be1803782df",
+ tree_data,
+ sizeof(tree_data),
+};
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+static object_data tree_sha256 = {
+ tree_bytes_sha256,
+ sizeof(tree_bytes_sha256),
+ "34a4854b5426f92460b4ae35e6d79746be66c38bffd6ef3bc4f053ed78a36ba4",
+ GIT_OID_SHA256,
+ "tree",
+ "test-objects/34",
+ "test-objects/34/a4854b5426f92460b4ae35e6d79746be66c38bffd6ef3bc4f053ed78a36ba4",
+ tree_data_sha256,
+ sizeof(tree_data_sha256),
+};
+#endif
+
+/*
+ * tag == 09d373e1dfdc16b129ceec6dd649739911541e05 (sha1)
+ * f535d7595d5d0e5e530b5deb34542c96491fea300a1318036b605306548cb225 (sha256)
+ */
+static unsigned char tag_bytes[] = {
+ 0x78, 0x01, 0x35, 0x4e, 0xcb, 0x0a, 0xc2, 0x40,
+ 0x10, 0xf3, 0xbc, 0x5f, 0x31, 0x77, 0xa1, 0xec,
+ 0xa3, 0xed, 0x6e, 0x41, 0x44, 0xf0, 0x2c, 0x5e,
+ 0xfc, 0x81, 0xe9, 0x76, 0xb6, 0xad, 0xb4, 0xb4,
+ 0x6c, 0x07, 0xd1, 0xbf, 0x77, 0x44, 0x0d, 0x39,
+ 0x84, 0x10, 0x92, 0x30, 0xf6, 0x60, 0xbc, 0xdb,
+ 0x2d, 0xed, 0x9d, 0x22, 0x83, 0xeb, 0x7c, 0x0a,
+ 0x58, 0x63, 0xd2, 0xbe, 0x8e, 0x21, 0xba, 0x64,
+ 0xb5, 0xf6, 0x06, 0x43, 0xe3, 0xaa, 0xd8, 0xb5,
+ 0x14, 0xac, 0x0d, 0x55, 0x53, 0x76, 0x46, 0xf1,
+ 0x6b, 0x25, 0x88, 0xcb, 0x3c, 0x8f, 0xac, 0x58,
+ 0x3a, 0x1e, 0xba, 0xd0, 0x85, 0xd8, 0xd8, 0xf7,
+ 0x94, 0xe1, 0x0c, 0x57, 0xb8, 0x8c, 0xcc, 0x22,
+ 0x0f, 0xdf, 0x90, 0xc8, 0x13, 0x3d, 0x71, 0x5e,
+ 0x27, 0x2a, 0xc4, 0x39, 0x82, 0xb1, 0xd6, 0x07,
+ 0x53, 0xda, 0xc6, 0xc3, 0x5e, 0x0b, 0x94, 0xba,
+ 0x0d, 0xe3, 0x06, 0x42, 0x1e, 0x08, 0x3e, 0x95,
+ 0xbf, 0x4b, 0x69, 0xc9, 0x90, 0x69, 0x22, 0xdc,
+ 0xe8, 0xbf, 0xf2, 0x06, 0x42, 0x9a, 0x36, 0xb1,
+};
+
+static unsigned char tag_data[] = {
+ 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x33,
+ 0x64, 0x37, 0x66, 0x38, 0x61, 0x36, 0x61, 0x66,
+ 0x30, 0x37, 0x36, 0x63, 0x38, 0x63, 0x33, 0x66,
+ 0x32, 0x30, 0x30, 0x37, 0x31, 0x61, 0x38, 0x39,
+ 0x33, 0x35, 0x63, 0x64, 0x62, 0x65, 0x38, 0x32,
+ 0x32, 0x38, 0x35, 0x39, 0x34, 0x64, 0x31, 0x0a,
+ 0x74, 0x79, 0x70, 0x65, 0x20, 0x63, 0x6f, 0x6d,
+ 0x6d, 0x69, 0x74, 0x0a, 0x74, 0x61, 0x67, 0x20,
+ 0x76, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x0a, 0x74,
+ 0x61, 0x67, 0x67, 0x65, 0x72, 0x20, 0x43, 0x20,
+ 0x4f, 0x20, 0x4d, 0x69, 0x74, 0x74, 0x65, 0x72,
+ 0x20, 0x3c, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
+ 0x74, 0x65, 0x72, 0x40, 0x65, 0x78, 0x61, 0x6d,
+ 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e,
+ 0x20, 0x31, 0x32, 0x32, 0x37, 0x38, 0x31, 0x34,
+ 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30, 0x30, 0x30,
+ 0x30, 0x0a, 0x0a, 0x54, 0x68, 0x69, 0x73, 0x20,
+ 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74,
+ 0x61, 0x67, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63,
+ 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x72, 0x65,
+ 0x6c, 0x65, 0x61, 0x73, 0x65, 0x20, 0x76, 0x30,
+ 0x2e, 0x30, 0x2e, 0x31, 0x0a,
+};
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+static unsigned char tag_bytes_sha256[] = {
+ 0x78, 0x01, 0x55, 0x8f, 0xd1, 0x4e, 0x84, 0x30,
+ 0x10, 0x45, 0x7d, 0xee, 0x57, 0xcc, 0xbb, 0x2e,
+ 0x81, 0x16, 0x68, 0x31, 0x1b, 0xa3, 0xf1, 0xd9,
+ 0xf8, 0xe0, 0xfa, 0x01, 0x43, 0x99, 0x42, 0x0d,
+ 0xb4, 0xa4, 0x14, 0xb3, 0xfc, 0xbd, 0xc5, 0xdd,
+ 0x4d, 0xb4, 0x69, 0xd2, 0x9b, 0xc9, 0xdc, 0x7b,
+ 0x6e, 0x23, 0xf6, 0x20, 0xa4, 0xba, 0xf3, 0xed,
+ 0x17, 0xe9, 0x08, 0xc8, 0xb1, 0x14, 0xb9, 0x69,
+ 0x6b, 0xd1, 0xf2, 0xa6, 0x34, 0xaa, 0x56, 0x68,
+ 0x4a, 0x32, 0xb5, 0xd6, 0xba, 0xd1, 0x82, 0x14,
+ 0xaf, 0x6a, 0x12, 0x32, 0x57, 0x55, 0xa3, 0xa9,
+ 0x92, 0x0a, 0xb9, 0x50, 0x42, 0xa2, 0x56, 0x95,
+ 0x10, 0xd2, 0x54, 0x35, 0x67, 0x71, 0x9b, 0x09,
+ 0xb4, 0x9f, 0x26, 0x1b, 0x59, 0x4c, 0xd9, 0xdf,
+ 0x79, 0x96, 0x67, 0xc5, 0x2e, 0x7b, 0x0a, 0xf0,
+ 0x0a, 0xef, 0xf0, 0x66, 0x63, 0x4c, 0xf2, 0x78,
+ 0x59, 0x4a, 0xf2, 0x99, 0xce, 0x38, 0xcd, 0x23,
+ 0x65, 0x69, 0xf2, 0x04, 0x05, 0xe7, 0x52, 0x15,
+ 0x25, 0x6f, 0x24, 0xdc, 0xe7, 0xe9, 0x30, 0x76,
+ 0x1a, 0xec, 0x02, 0xe9, 0xc6, 0x81, 0x60, 0x8f,
+ 0xbc, 0x56, 0x35, 0x3e, 0x40, 0xa0, 0x91, 0x70,
+ 0xa1, 0x1b, 0xe5, 0x0a, 0x86, 0x65, 0x9d, 0x26,
+ 0x0c, 0xdb, 0x6e, 0x25, 0x68, 0x7d, 0xb7, 0x81,
+ 0x37, 0xbf, 0xf6, 0x0b, 0x13, 0x26, 0x5a, 0x16,
+ 0xec, 0xe9, 0x21, 0xed, 0xbb, 0x88, 0xd6, 0x59,
+ 0xd7, 0x83, 0x59, 0x43, 0x02, 0x04, 0xa0, 0xf3,
+ 0x3c, 0xa2, 0xc3, 0x68, 0xbd, 0x63, 0x57, 0xd7,
+ 0xbc, 0x86, 0xd9, 0x27, 0xca, 0x2d, 0x64, 0x40,
+ 0xd7, 0x53, 0xaa, 0xe4, 0x62, 0xf0, 0xdd, 0xaa,
+ 0xa9, 0x83, 0x76, 0xfb, 0x13, 0x9f, 0x31, 0xf6,
+ 0x61, 0x7b, 0x47, 0xdd, 0xc1, 0x9b, 0x43, 0xbb,
+ 0x3d, 0xc2, 0x0b, 0x7c, 0xc2, 0x69, 0x48, 0x75,
+ 0x8f, 0xb8, 0xc6, 0xf4, 0xfe, 0xfb, 0x30, 0xfb,
+ 0x01, 0xc9, 0x32, 0x7d, 0xbb
+};
+
+static unsigned char tag_data_sha256[] = {
+ 0x6f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x20, 0x61,
+ 0x32, 0x61, 0x34, 0x33, 0x30, 0x66, 0x62, 0x36,
+ 0x33, 0x62, 0x32, 0x39, 0x34, 0x66, 0x38, 0x36,
+ 0x38, 0x61, 0x66, 0x34, 0x65, 0x66, 0x36, 0x63,
+ 0x63, 0x63, 0x39, 0x63, 0x33, 0x65, 0x38, 0x32,
+ 0x35, 0x36, 0x65, 0x33, 0x37, 0x30, 0x38, 0x35,
+ 0x39, 0x63, 0x65, 0x35, 0x37, 0x38, 0x61, 0x32,
+ 0x33, 0x38, 0x33, 0x37, 0x61, 0x63, 0x38, 0x35,
+ 0x33, 0x33, 0x37, 0x66, 0x35, 0x36, 0x32, 0x0a,
+ 0x74, 0x79, 0x70, 0x65, 0x20, 0x63, 0x6f, 0x6d,
+ 0x6d, 0x69, 0x74, 0x0a, 0x74, 0x61, 0x67, 0x20,
+ 0x76, 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x0a, 0x74,
+ 0x61, 0x67, 0x67, 0x65, 0x72, 0x20, 0x43, 0x20,
+ 0x4f, 0x20, 0x4d, 0x69, 0x74, 0x74, 0x65, 0x72,
+ 0x20, 0x3c, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74,
+ 0x74, 0x65, 0x72, 0x40, 0x65, 0x78, 0x61, 0x6d,
+ 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x3e,
+ 0x20, 0x31, 0x32, 0x32, 0x37, 0x38, 0x31, 0x34,
+ 0x32, 0x39, 0x37, 0x20, 0x2b, 0x30, 0x30, 0x30,
+ 0x30, 0x0a, 0x0a, 0x54, 0x68, 0x69, 0x73, 0x20,
+ 0x69, 0x73, 0x20, 0x74, 0x68, 0x65, 0x20, 0x74,
+ 0x61, 0x67, 0x20, 0x6f, 0x62, 0x6a, 0x65, 0x63,
+ 0x74, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x72, 0x65,
+ 0x6c, 0x65, 0x61, 0x73, 0x65, 0x20, 0x76, 0x30,
+ 0x2e, 0x30, 0x2e, 0x31, 0x0a, 0x20, 0x63, 0x6f,
+ 0x6d, 0x6d, 0x69, 0x74, 0x20, 0x73, 0x75, 0x6d,
+ 0x6d, 0x61, 0x72, 0x79, 0x0a, 0x0a, 0x54, 0x68,
+ 0x65, 0x20, 0x62, 0x6f, 0x64, 0x79, 0x20, 0x6f,
+ 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f,
+ 0x6d, 0x6d, 0x69, 0x74, 0x20, 0x6d, 0x65, 0x73,
+ 0x73, 0x61, 0x67, 0x65, 0x2c, 0x20, 0x63, 0x6f,
+ 0x6e, 0x74, 0x61, 0x69, 0x6e, 0x69, 0x6e, 0x67,
+ 0x20, 0x66, 0x75, 0x72, 0x74, 0x68, 0x65, 0x72,
+ 0x20, 0x65, 0x78, 0x70, 0x6c, 0x61, 0x6e, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x6f, 0x66, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x70, 0x75, 0x72, 0x70,
+ 0x6f, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x63, 0x68, 0x61, 0x6e, 0x67,
+ 0x65, 0x73, 0x20, 0x69, 0x6e, 0x74, 0x72, 0x6f,
+ 0x64, 0x75, 0x63, 0x65, 0x64, 0x20, 0x62, 0x79,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f, 0x6d,
+ 0x6d, 0x69, 0x74, 0x2e, 0x0a, 0x0a, 0x53, 0x69,
+ 0x67, 0x6e, 0x65, 0x64, 0x2d, 0x6f, 0x66, 0x2d,
+ 0x62, 0x79, 0x3a, 0x20, 0x41, 0x20, 0x55, 0x20,
+ 0x54, 0x68, 0x6f, 0x72, 0x20, 0x3c, 0x61, 0x75,
+ 0x74, 0x68, 0x6f, 0x72, 0x40, 0x65, 0x78, 0x61,
+ 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d,
+ 0x3e, 0x0a
+};
+#endif
+
+static object_data tag = {
+ tag_bytes,
+ sizeof(tag_bytes),
+ "09d373e1dfdc16b129ceec6dd649739911541e05",
+ GIT_OID_SHA1,
+ "tag",
+ "test-objects/09",
+ "test-objects/09/d373e1dfdc16b129ceec6dd649739911541e05",
+ tag_data,
+ sizeof(tag_data),
+};
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+static object_data tag_sha256 = {
+ tag_bytes_sha256,
+ sizeof(tag_bytes_sha256),
+ "f535d7595d5d0e5e530b5deb34542c96491fea300a1318036b605306548cb225",
+ GIT_OID_SHA256,
+ "tag",
+ "test-objects/f5",
+ "test-objects/f5/35d7595d5d0e5e530b5deb34542c96491fea300a1318036b605306548cb225",
+ tag_data_sha256,
+ sizeof(tag_data_sha256),
+};
+#endif
+
+/*
+ * zero == e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 (sha1)
+ * 473a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813 (sha256)
+ */
+static unsigned char zero_bytes[] = {
+ 0x78, 0x01, 0x4b, 0xca, 0xc9, 0x4f, 0x52, 0x30,
+ 0x60, 0x00, 0x00, 0x09, 0xb0, 0x01, 0xf0,
+};
+
+static unsigned char zero_data[] = {
+ 0x00 /* dummy data */
+};
+
+static object_data zero = {
+ zero_bytes,
+ sizeof(zero_bytes),
+ "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
+ GIT_OID_SHA1,
+ "blob",
+ "test-objects/e6",
+ "test-objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391",
+ zero_data,
+ 0,
+};
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+static object_data zero_sha256 = {
+ zero_bytes,
+ sizeof(zero_bytes),
+ "473a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813",
+ GIT_OID_SHA256,
+ "blob",
+ "test-objects/47",
+ "test-objects/47/3a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813",
+ zero_data,
+ 0,
+};
+#endif
+
+/*
+ * two == 78981922613b2afb6025042ff6bd878ac1994e85 (sha1)
+ * f8625e43f9e04f24291f77cdbe4c71b3c2a3b0003f60419b3ed06a058d766c8b (sha256)
+ */
+static unsigned char two_bytes[] = {
+ 0x78, 0x01, 0x4b, 0xca, 0xc9, 0x4f, 0x52, 0x30,
+ 0x62, 0x48, 0xe4, 0x02, 0x00, 0x0e, 0x64, 0x02,
+ 0x5d,
+};
+
+static unsigned char two_data[] = {
+ 0x61, 0x0a,
+};
+
+static object_data two = {
+ two_bytes,
+ sizeof(two_bytes),
+ "78981922613b2afb6025042ff6bd878ac1994e85",
+ GIT_OID_SHA1,
+ "blob",
+ "test-objects/78",
+ "test-objects/78/981922613b2afb6025042ff6bd878ac1994e85",
+ two_data,
+ sizeof(two_data),
+};
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+static object_data two_sha256 = {
+ two_bytes,
+ sizeof(two_bytes),
+ "f8625e43f9e04f24291f77cdbe4c71b3c2a3b0003f60419b3ed06a058d766c8b",
+ GIT_OID_SHA256,
+ "blob",
+ "test-objects/f8",
+ "test-objects/f8/625e43f9e04f24291f77cdbe4c71b3c2a3b0003f60419b3ed06a058d766c8b",
+ two_data,
+ sizeof(two_data),
+};
+#endif
+
+/*
+ * some == fd8430bc864cfcd5f10e5590f8a447e01b942bfe (sha1)
+ * 083c2b8b445640d171e7aa667b0b32007006f786711032ebb82931cca69cc15b (sha256)
+ */
+static unsigned char some_bytes[] = {
+ 0x78, 0x01, 0x7d, 0x54, 0xc1, 0x4e, 0xe3, 0x30,
+ 0x10, 0xdd, 0x33, 0x5f, 0x31, 0xc7, 0x5d, 0x94,
+ 0xa5, 0x84, 0xd5, 0x22, 0xad, 0x7a, 0x0a, 0x15,
+ 0x85, 0x48, 0xd0, 0x56, 0x49, 0x2a, 0xd4, 0xa3,
+ 0x13, 0x4f, 0x88, 0x85, 0x63, 0x47, 0xb6, 0x43,
+ 0xc9, 0xdf, 0xef, 0x8c, 0x69, 0x17, 0x56, 0x0b,
+ 0x7b, 0xaa, 0x62, 0x7b, 0xde, 0xbc, 0xf7, 0xe6,
+ 0x4d, 0x6b, 0x6d, 0x6b, 0x48, 0xd3, 0xcb, 0x5f,
+ 0x5f, 0x66, 0xa7, 0x27, 0x70, 0x0a, 0x55, 0xa7,
+ 0x3c, 0xb4, 0x4a, 0x23, 0xf0, 0xaf, 0x43, 0x04,
+ 0x6f, 0xdb, 0xb0, 0x17, 0x0e, 0xe7, 0x30, 0xd9,
+ 0x11, 0x1a, 0x61, 0xc0, 0xa1, 0x54, 0x3e, 0x38,
+ 0x55, 0x8f, 0x81, 0x9e, 0x05, 0x10, 0x46, 0xce,
+ 0xac, 0x83, 0xde, 0x4a, 0xd5, 0x4e, 0x0c, 0x42,
+ 0x67, 0xa3, 0x91, 0xe8, 0x20, 0x74, 0x08, 0x01,
+ 0x5d, 0xef, 0xc1, 0xb6, 0xf1, 0xe3, 0x66, 0xb5,
+ 0x85, 0x1b, 0x34, 0xe8, 0x84, 0x86, 0xcd, 0x58,
+ 0x6b, 0xd5, 0xc0, 0x9d, 0x6a, 0xd0, 0x78, 0x4c,
+ 0xe0, 0x19, 0x9d, 0x57, 0xd6, 0xc0, 0x45, 0xc2,
+ 0x18, 0xc2, 0xc3, 0xc0, 0x0f, 0x7c, 0x87, 0x12,
+ 0xea, 0x29, 0x56, 0x2f, 0x99, 0x4f, 0x79, 0xe0,
+ 0x03, 0x4b, 0x4b, 0x4d, 0x44, 0xa0, 0x92, 0x33,
+ 0x2a, 0xe0, 0x9a, 0xdc, 0x80, 0x90, 0x52, 0xf1,
+ 0x11, 0x04, 0x1b, 0x4b, 0x06, 0xea, 0xae, 0x3c,
+ 0xe3, 0x7a, 0x50, 0x74, 0x4a, 0x84, 0xfe, 0xc3,
+ 0x81, 0x41, 0xf8, 0x89, 0x18, 0x43, 0x67, 0x9d,
+ 0x87, 0x47, 0xf5, 0x8c, 0x51, 0xf6, 0x68, 0xb4,
+ 0xea, 0x55, 0x20, 0x2a, 0x6f, 0x80, 0xdc, 0x42,
+ 0x2b, 0xf3, 0x14, 0x2b, 0x1a, 0xdb, 0x0f, 0xe4,
+ 0x9a, 0x64, 0x84, 0xa3, 0x90, 0xa8, 0xf9, 0x8f,
+ 0x9d, 0x86, 0x9e, 0xd3, 0xab, 0x5a, 0x99, 0xc8,
+ 0xd9, 0xc3, 0x5e, 0x85, 0x0e, 0x2c, 0xb5, 0x73,
+ 0x30, 0x38, 0xfb, 0xe8, 0x44, 0xef, 0x5f, 0x95,
+ 0x1b, 0xc9, 0xd0, 0xef, 0x3c, 0x26, 0x32, 0x1e,
+ 0xff, 0x2d, 0xb6, 0x23, 0x7b, 0x3f, 0xd1, 0x3c,
+ 0x78, 0x1a, 0x0d, 0xcb, 0xe6, 0xf6, 0xd4, 0x44,
+ 0x99, 0x47, 0x1a, 0x9e, 0xed, 0x23, 0xb5, 0x91,
+ 0x6a, 0xdf, 0x53, 0x39, 0x03, 0xf8, 0x5a, 0xb1,
+ 0x0f, 0x1f, 0xce, 0x81, 0x11, 0xde, 0x01, 0x7a,
+ 0x90, 0x16, 0xc4, 0x30, 0xe8, 0x89, 0xed, 0x7b,
+ 0x65, 0x4b, 0xd7, 0x03, 0x36, 0xc1, 0xcf, 0xa1,
+ 0xa5, 0xb1, 0xe3, 0x8b, 0xe8, 0x07, 0x4d, 0xf3,
+ 0x23, 0x25, 0x13, 0x35, 0x27, 0xf5, 0x8c, 0x11,
+ 0xd3, 0xa0, 0x9a, 0xa8, 0xf5, 0x38, 0x7d, 0xce,
+ 0x55, 0xc2, 0x71, 0x79, 0x13, 0xc7, 0xa3, 0xda,
+ 0x77, 0x68, 0xc0, 0xd8, 0x10, 0xdd, 0x24, 0x8b,
+ 0x15, 0x59, 0xc5, 0x10, 0xe2, 0x20, 0x99, 0x8e,
+ 0xf0, 0x05, 0x9b, 0x31, 0x88, 0x5a, 0xe3, 0xd9,
+ 0x37, 0xba, 0xe2, 0xdb, 0xbf, 0x92, 0xfa, 0x66,
+ 0x16, 0x97, 0x47, 0xd9, 0x9d, 0x1d, 0x28, 0x7c,
+ 0x9d, 0x08, 0x1c, 0xc7, 0xbd, 0xd2, 0x1a, 0x6a,
+ 0x04, 0xf2, 0xa2, 0x1d, 0x75, 0x02, 0x14, 0x5d,
+ 0xc6, 0x78, 0xc8, 0xab, 0xdb, 0xf5, 0xb6, 0x82,
+ 0x6c, 0xb5, 0x83, 0x87, 0xac, 0x28, 0xb2, 0x55,
+ 0xb5, 0x9b, 0xc7, 0xc1, 0xb0, 0xb7, 0xf8, 0x4c,
+ 0xbc, 0x38, 0x0e, 0x8a, 0x04, 0x2a, 0x62, 0x41,
+ 0x6b, 0xe0, 0x84, 0x09, 0x13, 0xe9, 0xe1, 0xea,
+ 0xfb, 0xeb, 0x62, 0x71, 0x4b, 0x25, 0xd9, 0x55,
+ 0x7e, 0x97, 0x57, 0x3b, 0x20, 0x33, 0x96, 0x79,
+ 0xb5, 0xba, 0x2e, 0x4b, 0x58, 0xae, 0x0b, 0xc8,
+ 0x60, 0x93, 0x15, 0x55, 0xbe, 0xd8, 0xde, 0x65,
+ 0x05, 0x6c, 0xb6, 0xc5, 0x66, 0x5d, 0x5e, 0x93,
+ 0xf7, 0x25, 0x65, 0x98, 0x41, 0x29, 0x86, 0x0c,
+ 0xf2, 0xf1, 0x14, 0xa2, 0xb3, 0xbd, 0x75, 0x08,
+ 0x12, 0x83, 0x50, 0xda, 0x1f, 0x23, 0xbe, 0xa3,
+ 0x1d, 0xf4, 0x9d, 0x1d, 0xb5, 0x84, 0x4e, 0x50,
+ 0x38, 0x1d, 0x36, 0x48, 0x21, 0x95, 0xd1, 0xac,
+ 0x81, 0x99, 0x1d, 0xc1, 0x3f, 0x41, 0xe6, 0x9e,
+ 0x42, 0x5b, 0x0a, 0x48, 0xcc, 0x5f, 0xe0, 0x7d,
+ 0x3f, 0xc4, 0x6f, 0x0e, 0xfe, 0xc0, 0x2d, 0xfe,
+ 0x01, 0x2c, 0xd6, 0x9b, 0x5d, 0xbe, 0xba, 0x21,
+ 0xca, 0x79, 0xcb, 0xe3, 0x49, 0x60, 0xef, 0x68,
+ 0x05, 0x28, 0x9b, 0x8c, 0xc1, 0x12, 0x3e, 0xdb,
+ 0xc7, 0x04, 0x7e, 0xa6, 0x74, 0x29, 0xcc, 0x13,
+ 0xed, 0x07, 0x94, 0x81, 0xd6, 0x96, 0xaa, 0x97,
+ 0xaa, 0xa5, 0xc0, 0x2f, 0xb5, 0xb5, 0x2e, 0xe6,
+ 0xfc, 0xca, 0xfa, 0x60, 0x4d, 0x02, 0xf7, 0x19,
+ 0x9c, 0x5f, 0xa4, 0xe9, 0xf9, 0xf7, 0xf4, 0xc7,
+ 0x79, 0x9a, 0xc0, 0xb6, 0xcc, 0x58, 0xec, 0xec,
+ 0xe4, 0x37, 0x22, 0xfa, 0x8b, 0x53,
+};
+
+static unsigned char some_data[] = {
+ 0x2f, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x54, 0x68,
+ 0x69, 0x73, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20,
+ 0x69, 0x73, 0x20, 0x66, 0x72, 0x65, 0x65, 0x20,
+ 0x73, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65,
+ 0x3b, 0x20, 0x79, 0x6f, 0x75, 0x20, 0x63, 0x61,
+ 0x6e, 0x20, 0x72, 0x65, 0x64, 0x69, 0x73, 0x74,
+ 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x20, 0x69,
+ 0x74, 0x20, 0x61, 0x6e, 0x64, 0x2f, 0x6f, 0x72,
+ 0x20, 0x6d, 0x6f, 0x64, 0x69, 0x66, 0x79, 0x0a,
+ 0x20, 0x2a, 0x20, 0x69, 0x74, 0x20, 0x75, 0x6e,
+ 0x64, 0x65, 0x72, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x74, 0x65, 0x72, 0x6d, 0x73, 0x20, 0x6f, 0x66,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x47, 0x4e, 0x55,
+ 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
+ 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x2c,
+ 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e,
+ 0x20, 0x32, 0x2c, 0x0a, 0x20, 0x2a, 0x20, 0x61,
+ 0x73, 0x20, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x73,
+ 0x68, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x46, 0x72, 0x65, 0x65, 0x20,
+ 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65,
+ 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x2e, 0x0a, 0x20, 0x2a, 0x0a,
+ 0x20, 0x2a, 0x20, 0x49, 0x6e, 0x20, 0x61, 0x64,
+ 0x64, 0x69, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x74,
+ 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x70, 0x65,
+ 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e,
+ 0x73, 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x47, 0x4e, 0x55, 0x20, 0x47, 0x65, 0x6e,
+ 0x65, 0x72, 0x61, 0x6c, 0x20, 0x50, 0x75, 0x62,
+ 0x6c, 0x69, 0x63, 0x20, 0x4c, 0x69, 0x63, 0x65,
+ 0x6e, 0x73, 0x65, 0x2c, 0x0a, 0x20, 0x2a, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x61, 0x75, 0x74, 0x68,
+ 0x6f, 0x72, 0x73, 0x20, 0x67, 0x69, 0x76, 0x65,
+ 0x20, 0x79, 0x6f, 0x75, 0x20, 0x75, 0x6e, 0x6c,
+ 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x20, 0x70,
+ 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f,
+ 0x6e, 0x20, 0x74, 0x6f, 0x20, 0x6c, 0x69, 0x6e,
+ 0x6b, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6f,
+ 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x64, 0x0a, 0x20,
+ 0x2a, 0x20, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f,
+ 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x69,
+ 0x73, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x69,
+ 0x6e, 0x74, 0x6f, 0x20, 0x63, 0x6f, 0x6d, 0x62,
+ 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73,
+ 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x6f, 0x74,
+ 0x68, 0x65, 0x72, 0x20, 0x70, 0x72, 0x6f, 0x67,
+ 0x72, 0x61, 0x6d, 0x73, 0x2c, 0x0a, 0x20, 0x2a,
+ 0x20, 0x61, 0x6e, 0x64, 0x20, 0x74, 0x6f, 0x20,
+ 0x64, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75,
+ 0x74, 0x65, 0x20, 0x74, 0x68, 0x6f, 0x73, 0x65,
+ 0x20, 0x63, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x61,
+ 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20, 0x77, 0x69,
+ 0x74, 0x68, 0x6f, 0x75, 0x74, 0x20, 0x61, 0x6e,
+ 0x79, 0x20, 0x72, 0x65, 0x73, 0x74, 0x72, 0x69,
+ 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x0a, 0x20, 0x2a,
+ 0x20, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x20,
+ 0x66, 0x72, 0x6f, 0x6d, 0x20, 0x74, 0x68, 0x65,
+ 0x20, 0x75, 0x73, 0x65, 0x20, 0x6f, 0x66, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x66, 0x69, 0x6c,
+ 0x65, 0x2e, 0x20, 0x20, 0x28, 0x54, 0x68, 0x65,
+ 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
+ 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x0a,
+ 0x20, 0x2a, 0x20, 0x72, 0x65, 0x73, 0x74, 0x72,
+ 0x69, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x20,
+ 0x64, 0x6f, 0x20, 0x61, 0x70, 0x70, 0x6c, 0x79,
+ 0x20, 0x69, 0x6e, 0x20, 0x6f, 0x74, 0x68, 0x65,
+ 0x72, 0x20, 0x72, 0x65, 0x73, 0x70, 0x65, 0x63,
+ 0x74, 0x73, 0x3b, 0x20, 0x66, 0x6f, 0x72, 0x20,
+ 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2c,
+ 0x20, 0x74, 0x68, 0x65, 0x79, 0x20, 0x63, 0x6f,
+ 0x76, 0x65, 0x72, 0x0a, 0x20, 0x2a, 0x20, 0x6d,
+ 0x6f, 0x64, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x20, 0x6f, 0x66, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x66, 0x69, 0x6c, 0x65, 0x2c,
+ 0x20, 0x61, 0x6e, 0x64, 0x20, 0x64, 0x69, 0x73,
+ 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f,
+ 0x6e, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x6e,
+ 0x6f, 0x74, 0x20, 0x6c, 0x69, 0x6e, 0x6b, 0x65,
+ 0x64, 0x20, 0x69, 0x6e, 0x74, 0x6f, 0x0a, 0x20,
+ 0x2a, 0x20, 0x61, 0x20, 0x63, 0x6f, 0x6d, 0x62,
+ 0x69, 0x6e, 0x65, 0x64, 0x20, 0x65, 0x78, 0x65,
+ 0x63, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2e,
+ 0x29, 0x0a, 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20,
+ 0x54, 0x68, 0x69, 0x73, 0x20, 0x66, 0x69, 0x6c,
+ 0x65, 0x20, 0x69, 0x73, 0x20, 0x64, 0x69, 0x73,
+ 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x64,
+ 0x20, 0x69, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x68, 0x6f, 0x70, 0x65, 0x20, 0x74, 0x68, 0x61,
+ 0x74, 0x20, 0x69, 0x74, 0x20, 0x77, 0x69, 0x6c,
+ 0x6c, 0x20, 0x62, 0x65, 0x20, 0x75, 0x73, 0x65,
+ 0x66, 0x75, 0x6c, 0x2c, 0x20, 0x62, 0x75, 0x74,
+ 0x0a, 0x20, 0x2a, 0x20, 0x57, 0x49, 0x54, 0x48,
+ 0x4f, 0x55, 0x54, 0x20, 0x41, 0x4e, 0x59, 0x20,
+ 0x57, 0x41, 0x52, 0x52, 0x41, 0x4e, 0x54, 0x59,
+ 0x3b, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f, 0x75,
+ 0x74, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x69, 0x6d, 0x70, 0x6c, 0x69,
+ 0x65, 0x64, 0x20, 0x77, 0x61, 0x72, 0x72, 0x61,
+ 0x6e, 0x74, 0x79, 0x20, 0x6f, 0x66, 0x0a, 0x20,
+ 0x2a, 0x20, 0x4d, 0x45, 0x52, 0x43, 0x48, 0x41,
+ 0x4e, 0x54, 0x41, 0x42, 0x49, 0x4c, 0x49, 0x54,
+ 0x59, 0x20, 0x6f, 0x72, 0x20, 0x46, 0x49, 0x54,
+ 0x4e, 0x45, 0x53, 0x53, 0x20, 0x46, 0x4f, 0x52,
+ 0x20, 0x41, 0x20, 0x50, 0x41, 0x52, 0x54, 0x49,
+ 0x43, 0x55, 0x4c, 0x41, 0x52, 0x20, 0x50, 0x55,
+ 0x52, 0x50, 0x4f, 0x53, 0x45, 0x2e, 0x20, 0x20,
+ 0x53, 0x65, 0x65, 0x20, 0x74, 0x68, 0x65, 0x20,
+ 0x47, 0x4e, 0x55, 0x0a, 0x20, 0x2a, 0x20, 0x47,
+ 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c, 0x20, 0x50,
+ 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20, 0x4c, 0x69,
+ 0x63, 0x65, 0x6e, 0x73, 0x65, 0x20, 0x66, 0x6f,
+ 0x72, 0x20, 0x6d, 0x6f, 0x72, 0x65, 0x20, 0x64,
+ 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x2e, 0x0a,
+ 0x20, 0x2a, 0x0a, 0x20, 0x2a, 0x20, 0x59, 0x6f,
+ 0x75, 0x20, 0x73, 0x68, 0x6f, 0x75, 0x6c, 0x64,
+ 0x20, 0x68, 0x61, 0x76, 0x65, 0x20, 0x72, 0x65,
+ 0x63, 0x65, 0x69, 0x76, 0x65, 0x64, 0x20, 0x61,
+ 0x20, 0x63, 0x6f, 0x70, 0x79, 0x20, 0x6f, 0x66,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x47, 0x4e, 0x55,
+ 0x20, 0x47, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x6c,
+ 0x20, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x20,
+ 0x4c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x0a,
+ 0x20, 0x2a, 0x20, 0x61, 0x6c, 0x6f, 0x6e, 0x67,
+ 0x20, 0x77, 0x69, 0x74, 0x68, 0x20, 0x74, 0x68,
+ 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x67, 0x72,
+ 0x61, 0x6d, 0x3b, 0x20, 0x73, 0x65, 0x65, 0x20,
+ 0x74, 0x68, 0x65, 0x20, 0x66, 0x69, 0x6c, 0x65,
+ 0x20, 0x43, 0x4f, 0x50, 0x59, 0x49, 0x4e, 0x47,
+ 0x2e, 0x20, 0x20, 0x49, 0x66, 0x20, 0x6e, 0x6f,
+ 0x74, 0x2c, 0x20, 0x77, 0x72, 0x69, 0x74, 0x65,
+ 0x20, 0x74, 0x6f, 0x0a, 0x20, 0x2a, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x46, 0x72, 0x65, 0x65, 0x20,
+ 0x53, 0x6f, 0x66, 0x74, 0x77, 0x61, 0x72, 0x65,
+ 0x20, 0x46, 0x6f, 0x75, 0x6e, 0x64, 0x61, 0x74,
+ 0x69, 0x6f, 0x6e, 0x2c, 0x20, 0x35, 0x31, 0x20,
+ 0x46, 0x72, 0x61, 0x6e, 0x6b, 0x6c, 0x69, 0x6e,
+ 0x20, 0x53, 0x74, 0x72, 0x65, 0x65, 0x74, 0x2c,
+ 0x20, 0x46, 0x69, 0x66, 0x74, 0x68, 0x20, 0x46,
+ 0x6c, 0x6f, 0x6f, 0x72, 0x2c, 0x0a, 0x20, 0x2a,
+ 0x20, 0x42, 0x6f, 0x73, 0x74, 0x6f, 0x6e, 0x2c,
+ 0x20, 0x4d, 0x41, 0x20, 0x30, 0x32, 0x31, 0x31,
+ 0x30, 0x2d, 0x31, 0x33, 0x30, 0x31, 0x2c, 0x20,
+ 0x55, 0x53, 0x41, 0x2e, 0x0a, 0x20, 0x2a, 0x2f,
+ 0x0a,
+};
+
+static object_data some = {
+ some_bytes,
+ sizeof(some_bytes),
+ "fd8430bc864cfcd5f10e5590f8a447e01b942bfe",
+ GIT_OID_SHA1,
+ "blob",
+ "test-objects/fd",
+ "test-objects/fd/8430bc864cfcd5f10e5590f8a447e01b942bfe",
+ some_data,
+ sizeof(some_data),
+};
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+static object_data some_sha256 = {
+ some_bytes,
+ sizeof(some_bytes),
+ "083c2b8b445640d171e7aa667b0b32007006f786711032ebb82931cca69cc15b",
+ GIT_OID_SHA256,
+ "blob",
+ "test-objects/08",
+ "test-objects/08/3c2b8b445640d171e7aa667b0b32007006f786711032ebb82931cca69cc15b",
+ some_data,
+ sizeof(some_data),
+};
+#endif
diff --git a/tests/libgit2/odb/mixed.c b/tests/libgit2/odb/mixed.c
new file mode 100644
index 0000000..19e6dce
--- /dev/null
+++ b/tests/libgit2/odb/mixed.c
@@ -0,0 +1,286 @@
+#include "clar_libgit2.h"
+#include "odb.h"
+
+static git_odb *_odb;
+
+void test_odb_mixed__initialize(void)
+{
+ cl_git_pass(git_odb__open(&_odb, cl_fixture("duplicate.git/objects"), NULL));
+}
+
+void test_odb_mixed__cleanup(void)
+{
+ git_odb_free(_odb);
+ _odb = NULL;
+}
+
+void test_odb_mixed__dup_oid(void) {
+ const char hex[] = "ce013625030ba8dba906f756967f9e9ca394464a";
+ const char short_hex[] = "ce01362";
+ git_oid oid;
+ git_odb_object *obj;
+
+ cl_git_pass(git_oid__fromstr(&oid, hex, GIT_OID_SHA1));
+ cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, GIT_OID_SHA1_HEXSIZE));
+ git_odb_object_free(obj);
+
+ cl_git_pass(git_odb_exists_prefix(NULL, _odb, &oid, GIT_OID_SHA1_HEXSIZE));
+
+ cl_git_pass(git_oid__fromstrn(&oid, short_hex, sizeof(short_hex) - 1, GIT_OID_SHA1));
+ cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, sizeof(short_hex) - 1));
+ git_odb_object_free(obj);
+
+ cl_git_pass(git_odb_exists_prefix(NULL, _odb, &oid, sizeof(short_hex) - 1));
+}
+
+/* some known sha collisions of file content:
+ * 'aabqhq' and 'aaazvc' with prefix 'dea509d0' (+ '9' and + 'b')
+ * 'aaeufo' and 'aaaohs' with prefix '81b5bff5' (+ 'f' and + 'b')
+ * 'aafewy' and 'aaepta' with prefix '739e3c4c'
+ * 'aahsyn' and 'aadrjg' with prefix '0ddeaded' (+ '9' and + 'e')
+ */
+
+void test_odb_mixed__dup_oid_prefix_0(void) {
+ char hex[10];
+ git_oid oid, found;
+ git_odb_object *obj;
+
+ /* ambiguous in the same pack file */
+
+ strncpy(hex, "dea509d0", sizeof(hex));
+ cl_git_pass(git_oid__fromstrn(&oid, hex, strlen(hex), GIT_OID_SHA1));
+ cl_assert_equal_i(
+ GIT_EAMBIGUOUS, git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
+ cl_assert_equal_i(
+ GIT_EAMBIGUOUS, git_odb_exists_prefix(&found, _odb, &oid, strlen(hex)));
+
+ strncpy(hex, "dea509d09", sizeof(hex));
+ cl_git_pass(git_oid__fromstrn(&oid, hex, strlen(hex), GIT_OID_SHA1));
+ cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
+ cl_git_pass(git_odb_exists_prefix(&found, _odb, &oid, strlen(hex)));
+ cl_assert_equal_oid(&found, git_odb_object_id(obj));
+ git_odb_object_free(obj);
+
+ strncpy(hex, "dea509d0b", sizeof(hex));
+ cl_git_pass(git_oid__fromstrn(&oid, hex, strlen(hex), GIT_OID_SHA1));
+ cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
+ git_odb_object_free(obj);
+
+ /* ambiguous in different pack files */
+
+ strncpy(hex, "81b5bff5", sizeof(hex));
+ cl_git_pass(git_oid__fromstrn(&oid, hex, strlen(hex), GIT_OID_SHA1));
+ cl_assert_equal_i(
+ GIT_EAMBIGUOUS, git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
+ cl_assert_equal_i(
+ GIT_EAMBIGUOUS, git_odb_exists_prefix(&found, _odb, &oid, strlen(hex)));
+
+ strncpy(hex, "81b5bff5b", sizeof(hex));
+ cl_git_pass(git_oid__fromstrn(&oid, hex, strlen(hex), GIT_OID_SHA1));
+ cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
+ cl_git_pass(git_odb_exists_prefix(&found, _odb, &oid, strlen(hex)));
+ cl_assert_equal_oid(&found, git_odb_object_id(obj));
+ git_odb_object_free(obj);
+
+ strncpy(hex, "81b5bff5f", sizeof(hex));
+ cl_git_pass(git_oid__fromstrn(&oid, hex, strlen(hex), GIT_OID_SHA1));
+ cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
+ git_odb_object_free(obj);
+
+ /* ambiguous in pack file and loose */
+
+ strncpy(hex, "0ddeaded", sizeof(hex));
+ cl_git_pass(git_oid__fromstrn(&oid, hex, strlen(hex), GIT_OID_SHA1));
+ cl_assert_equal_i(
+ GIT_EAMBIGUOUS, git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
+ cl_assert_equal_i(
+ GIT_EAMBIGUOUS, git_odb_exists_prefix(&found, _odb, &oid, strlen(hex)));
+
+ strncpy(hex, "0ddeaded9", sizeof(hex));
+ cl_git_pass(git_oid__fromstrn(&oid, hex, strlen(hex), GIT_OID_SHA1));
+ cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
+ cl_git_pass(git_odb_exists_prefix(&found, _odb, &oid, strlen(hex)));
+ cl_assert_equal_oid(&found, git_odb_object_id(obj));
+ git_odb_object_free(obj);
+
+ strncpy(hex, "0ddeadede", sizeof(hex));
+ cl_git_pass(git_oid__fromstrn(&oid, hex, strlen(hex), GIT_OID_SHA1));
+ cl_git_pass(git_odb_read_prefix(&obj, _odb, &oid, strlen(hex)));
+ git_odb_object_free(obj);
+}
+
+struct expand_id_test_data {
+ char *lookup_id;
+ char *expected_id;
+ git_object_t expected_type;
+};
+
+struct expand_id_test_data expand_id_test_data[] = {
+ /* some prefixes and their expected values */
+ { "dea509d0", NULL, GIT_OBJECT_ANY },
+ { "00000000", NULL, GIT_OBJECT_ANY },
+ { "dea509d0", NULL, GIT_OBJECT_ANY },
+ { "dea509d09", "dea509d097ce692e167dfc6a48a7a280cc5e877e", GIT_OBJECT_BLOB },
+ { "dea509d0b", "dea509d0b3cb8ee0650f6ca210bc83f4678851ba", GIT_OBJECT_BLOB },
+ { "ce0136250", "ce013625030ba8dba906f756967f9e9ca394464a", GIT_OBJECT_BLOB },
+ { "0ddeaded", NULL, GIT_OBJECT_ANY },
+ { "4d5979b", "4d5979b468252190cb572ae758aca36928e8a91e", GIT_OBJECT_TREE },
+ { "0ddeaded", NULL, GIT_OBJECT_ANY },
+ { "0ddeadede", "0ddeadede9e6d6ccddce0ee1e5749eed0485e5ea", GIT_OBJECT_BLOB },
+ { "0ddeaded9", "0ddeaded9502971eefe1e41e34d0e536853ae20f", GIT_OBJECT_BLOB },
+ { "f00b4e", NULL, GIT_OBJECT_ANY },
+
+ /* this OID is too short and should be ambiguous! */
+ { "f00", NULL, GIT_OBJECT_ANY },
+
+ /* some full-length object ids */
+ { "0000000000000000000000000000000000000000", NULL, GIT_OBJECT_ANY },
+ {
+ "dea509d097ce692e167dfc6a48a7a280cc5e877e",
+ "dea509d097ce692e167dfc6a48a7a280cc5e877e",
+ GIT_OBJECT_BLOB
+ },
+ { "f00f00f00f00f00f00f00f00f00f00f00f00f00f", NULL, GIT_OBJECT_ANY },
+ {
+ "4d5979b468252190cb572ae758aca36928e8a91e",
+ "4d5979b468252190cb572ae758aca36928e8a91e",
+ GIT_OBJECT_TREE
+ },
+
+ /*
+ * ensure we're not leaking the return error code for the
+ * last lookup if the last object is invalid
+ */
+ { "0ddeadedfff", NULL, GIT_OBJECT_ANY },
+};
+
+static void setup_prefix_query(
+ git_odb_expand_id **out_ids,
+ size_t *out_num)
+{
+ git_odb_expand_id *ids;
+ size_t num, i;
+
+ num = ARRAY_SIZE(expand_id_test_data);
+
+ cl_assert((ids = git__calloc(num, sizeof(git_odb_expand_id))));
+
+ for (i = 0; i < num; i++) {
+ git_odb_expand_id *id = &ids[i];
+
+ size_t len = strlen(expand_id_test_data[i].lookup_id);
+
+ git_oid__fromstrn(&id->id, expand_id_test_data[i].lookup_id, len, GIT_OID_SHA1);
+ id->length = (unsigned short)len;
+ id->type = expand_id_test_data[i].expected_type;
+ }
+
+ *out_ids = ids;
+ *out_num = num;
+}
+
+static void assert_found_objects(git_odb_expand_id *ids)
+{
+ size_t num, i;
+
+ num = ARRAY_SIZE(expand_id_test_data);
+
+ for (i = 0; i < num; i++) {
+ git_oid expected_id = GIT_OID_SHA1_ZERO;
+ size_t expected_len = 0;
+ git_object_t expected_type = 0;
+
+ if (expand_id_test_data[i].expected_id) {
+ git_oid__fromstr(&expected_id, expand_id_test_data[i].expected_id, GIT_OID_SHA1);
+ expected_len = GIT_OID_SHA1_HEXSIZE;
+ expected_type = expand_id_test_data[i].expected_type;
+ }
+
+ cl_assert_equal_oid(&expected_id, &ids[i].id);
+ cl_assert_equal_i(expected_len, ids[i].length);
+ cl_assert_equal_i(expected_type, ids[i].type);
+ }
+}
+
+static void assert_notfound_objects(git_odb_expand_id *ids)
+{
+ git_oid expected_id = GIT_OID_SHA1_ZERO;
+ size_t num, i;
+
+ num = ARRAY_SIZE(expand_id_test_data);
+
+ for (i = 0; i < num; i++) {
+ cl_assert_equal_oid(&expected_id, &ids[i].id);
+ cl_assert_equal_i(0, ids[i].length);
+ cl_assert_equal_i(0, ids[i].type);
+ }
+}
+
+void test_odb_mixed__expand_ids(void)
+{
+ git_odb_expand_id *ids;
+ size_t i, num;
+
+ /* test looking for the actual (correct) types */
+
+ setup_prefix_query(&ids, &num);
+ cl_git_pass(git_odb_expand_ids(_odb, ids, num));
+ assert_found_objects(ids);
+ git__free(ids);
+
+ /* test looking for an explicit `type == 0` */
+
+ setup_prefix_query(&ids, &num);
+
+ for (i = 0; i < num; i++)
+ ids[i].type = 0;
+
+ cl_git_pass(git_odb_expand_ids(_odb, ids, num));
+ assert_found_objects(ids);
+ git__free(ids);
+
+ /* test looking for an explicit GIT_OBJECT_ANY */
+
+ setup_prefix_query(&ids, &num);
+
+ for (i = 0; i < num; i++)
+ ids[i].type = GIT_OBJECT_ANY;
+
+ cl_git_pass(git_odb_expand_ids(_odb, ids, num));
+ assert_found_objects(ids);
+ git__free(ids);
+
+ /* test looking for the completely wrong type */
+
+ setup_prefix_query(&ids, &num);
+
+ for (i = 0; i < num; i++)
+ ids[i].type = (ids[i].type == GIT_OBJECT_BLOB) ?
+ GIT_OBJECT_TREE : GIT_OBJECT_BLOB;
+
+ cl_git_pass(git_odb_expand_ids(_odb, ids, num));
+ assert_notfound_objects(ids);
+ git__free(ids);
+}
+
+void test_odb_mixed__expand_ids_cached(void)
+{
+ git_odb_expand_id *ids;
+ size_t i, num;
+
+ /* test looking for the actual (correct) types after accessing the object */
+
+ setup_prefix_query(&ids, &num);
+
+ for (i = 0; i < num; i++) {
+ git_odb_object *obj;
+ if (ids[i].type == GIT_OBJECT_ANY)
+ continue;
+ cl_git_pass(git_odb_read_prefix(&obj, _odb, &ids[i].id, ids[i].length));
+ git_odb_object_free(obj);
+ }
+
+ cl_git_pass(git_odb_expand_ids(_odb, ids, num));
+ assert_found_objects(ids);
+ git__free(ids);
+}
diff --git a/tests/libgit2/odb/open.c b/tests/libgit2/odb/open.c
new file mode 100644
index 0000000..395406d
--- /dev/null
+++ b/tests/libgit2/odb/open.c
@@ -0,0 +1,34 @@
+#include "clar_libgit2.h"
+
+void test_odb_open__initialize(void)
+{
+ cl_fixture_sandbox("testrepo.git");
+}
+
+void test_odb_open__cleanup(void)
+{
+ cl_fixture_cleanup("testrepo.git");
+}
+
+void test_odb_open__exists(void)
+{
+ git_odb *odb;
+ git_oid one, two;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_odb_options opts = GIT_ODB_OPTIONS_INIT;
+
+ cl_git_pass(git_odb_open(&odb, "testrepo.git/objects", &opts));
+ cl_git_pass(git_oid_fromstr(&one, "1385f264afb75a56a5bec74243be9b367ba4ca08", GIT_OID_SHA1));
+ cl_git_pass(git_oid_fromstr(&two, "00112233445566778899aabbccddeeff00112233", GIT_OID_SHA1));
+#else
+ cl_git_pass(git_odb_open(&odb, "testrepo.git/objects"));
+ cl_git_pass(git_oid_fromstr(&one, "1385f264afb75a56a5bec74243be9b367ba4ca08"));
+ cl_git_pass(git_oid_fromstr(&two, "00112233445566778899aabbccddeeff00112233"));
+#endif
+
+ cl_assert(git_odb_exists(odb, &one));
+ cl_assert(!git_odb_exists(odb, &two));
+
+ git_odb_free(odb);
+}
diff --git a/tests/libgit2/odb/pack_data.h b/tests/libgit2/odb/pack_data.h
new file mode 100644
index 0000000..e6371be
--- /dev/null
+++ b/tests/libgit2/odb/pack_data.h
@@ -0,0 +1,151 @@
+
+static const char *packed_objects[] = {
+ "0266163a49e280c4f5ed1e08facd36a2bd716bcf",
+ "53fc32d17276939fc79ed05badaef2db09990016",
+ "6336846bd5c88d32f93ae57d846683e61ab5c530",
+ "6dcf9bf7541ee10456529833502442f385010c3d",
+ "bed08a0b30b72a9d4aed7f1af8c8ca124e8d64b9",
+ "e90810b8df3e80c413d903f631643c716887138d",
+ "fc3c3a2083e9f6f89e6bd53e9420e70d1e357c9b",
+ "fc58168adf502d0c0ef614c3111a7038fc8c09c8",
+ "fd0ec0333948dfe23265ac46be0205a436a8c3a5",
+ "fd8430bc864cfcd5f10e5590f8a447e01b942bfe",
+ "fd899f45951c15c1c5f7c34b1c864e91bd6556c6",
+ "fda23b974899e7e1f938619099280bfda13bdca9",
+ "fdbec189efb657c8325962b494875987881a356b",
+ "fe1ca6bd22b5d8353ce6c2f3aba80805c438a7a5",
+ "fe3a6a42c87ff1239370c741a265f3997add87c1",
+ "deb106bfd2d36ecf9f0079224c12022201a39ad1",
+ "dec93efc79e60f2680de3e666755d335967eec30",
+ "def425bf8568b9c1e20879bf5be6f9c52b7361c4",
+ "df48000ac4f48570054e3a71a81916357997b680",
+ "dfae6ed8f6dd8acc3b40a31811ea316239223559",
+ "dff79e27d3d2cdc09790ded80fe2ea8ff5d61034",
+ "e00e46abe4c542e17c8bc83d72cf5be8018d7b0e",
+ "e01b107b4f77f8f98645adac0206a504f2d29d7c",
+ "e032d863f512c47b479bd984f8b6c8061f66b7d4",
+ "e044baa468a1c74f9f9da36805445f6888358b49",
+ "e04529998989ba8ae3419538dd57969af819b241",
+ "e0637ddfbea67c8d7f557c709e095af8906e9176",
+ "e0743ad4031231e71700abdc6fdbe94f189d20e5",
+ "cf33ac7a3d8b2b8f6bb266518aadbf59de397608",
+ "cf5f7235b9c9689b133f6ea12015720b411329bd",
+ "cf6cccf1297284833a9a03138a1f5738fa1c6c94",
+ "cf7992bde17ce7a79cab5f0c1fcbe8a0108721ed",
+ "cfe3a027ab12506d4144ee8a35669ae8fc4b7ab1",
+ "cfe96f31dfad7bab49977aa1df7302f7fafcb025",
+ "cff54d138945ef4de384e9d2759291d0c13ea90a",
+ "d01f7573ac34c2f502bd1cf18cde73480c741151",
+ "d03f567593f346a1ca96a57f8191def098d126e3",
+ "d047b47aadf88501238f36f5c17dd0a50dc62087",
+ "d0a0d63086fae3b0682af7261df21f7d0f7f066d",
+ "d0a44bd6ed0be21b725a96c0891bbc79bc1a540c",
+ "d0d7e736e536a41bcb885005f8bf258c61cad682",
+ "d0e7959d4b95ffec6198df6f5a7ae259b23a5f50",
+ "bf2fe2acca17d13356ce802ba9dc8343f710dfb7",
+ "bf55f407d6d9418e51f42ea7a3a6aadf17388349",
+ "bf92206f8b633b88a66dca4a911777630b06fbac",
+ "bfaf8c42eb8842abe206179fee864cfba87e3ca9",
+ "bfe05675d4e8f6b59d50932add8790f1a06b10ee",
+ "bff8618112330763327cfa6ce6e914db84f51ddf",
+ "bff873e9853ed99fed52c25f7ad29f78b27dcec2",
+ "c01c3fae7251098d7af1b459bcd0786e81d4616d",
+ "c0220fca67f48b8a5d4163d53b1486224be3a198",
+ "c02d0b160b82ee72469c269f13de4c26a7ea09cb",
+ "c059510ad1b45ab58390e042d7dee1ac46703854",
+ "c07204a1897aeeaa3c248d29dbfa9b033baf9755",
+ "c073337a4dd7276931b4b3fdbc3f0040e9441793",
+ "0fd7e4bfba5b3a82be88d1057757ca8b2c5e6d26",
+ "100746511cc45c9f1ad6721c4ef5be49222fee4d",
+ "1088490171d9b984d68b8b9be9ca003f4eafff59",
+ "1093c8ff4cb78fcf5f79dbbeedcb6e824bd4e253",
+ "10aa3fa72afab7ee31e116ae06442fe0f7b79df2",
+ "10b759e734e8299aa0dca08be935d95d886127b6",
+ "111d5ccf0bb010c4e8d7af3eedfa12ef4c5e265b",
+ "11261fbff21758444d426356ff6327ee01e90752",
+ "112998d425717bb922ce74e8f6f0f831d8dc4510",
+ "2ef4e5d838b6507bd61d457cf6466662b791c5c0",
+ "2ef4faa0f82efa00eeac6cae9e8b2abccc8566ee",
+ "2f06098183b0d7be350acbe39cdbaccff2df0c4a",
+ "2f1c5d509ac5bffb3c62f710a1c2c542e126dfd1",
+ "2f205b20fc16423c42b3ba51b2ea78d7b9ff3578",
+ "2f9b6b6e3d9250ba09360734aa47973a993b59d1",
+ "30c62a2d5a8d644f1311d4f7fe3f6a788e4c8188",
+ "31438e245492d85fd6da4d1406eba0fbde8332a4",
+ "3184a3abdfea231992254929ff4e275898e5bbf6",
+ "3188ffdbb3a3d52e0f78f30c484533899224436e",
+ "32581d0093429770d044a60eb0e9cc0462bedb13",
+ "32679a9544d83e5403202c4d5efb61ad02492847",
+ "4e7e9f60b7e2049b7f5697daf133161a18ef688f",
+ "4e8cda27ddc8be7db875ceb0f360c37734724c6d",
+ "4ea481c61c59ab55169b7cbaae536ad50b49d6f0",
+ "4f0adcd0e61eabe06fe32be66b16559537124b7a",
+ "4f1355c91100d12f9e7202f91b245df0c110867c",
+ "4f6eadeb08b9d0d1e8b1b3eac8a34940adf29a2d",
+ "4f9339df943c53117a5fc8e86e2f38716ff3a668",
+ "4fc3874b118752e40de556b1c3e7b4a9f1737d00",
+ "4ff1dd0992dd6baafdb5e166be6f9f23b59bdf87",
+ "5018a35e0b7e2eec7ce5050baf9c7343f3f74164",
+ "50298f44a45eda3a29dae82dbe911b5aa176ac07",
+ "502acd164fb115768d723144da2e7bb5a24891bb",
+ "50330c02bd4fd95c9db1fcf2f97f4218e42b7226",
+ "5052bf355d9f8c52446561a39733a8767bf31e37",
+ "6f2cd729ae42988c1dd43588d3a6661ba48ad7a0",
+ "6f4e2c42d9138bfbf3e0f908f1308828cc6f2178",
+ "6f6a17db05a83620cef4572761831c20a70ba9b9",
+ "6faad60901e36538634f0d8b8ff3f21f83503c71",
+ "6fc72e46de3df0c3842dab302bbacf697a63abab",
+ "6fdccd49f442a7204399ca9b418f017322dbded8",
+ "6fe7568fc3861c334cb008fd85d57d9647249ef5",
+ "700f55d91d7b55665594676a4bada1f1457a0598",
+ "702bd70595a7b19afc48a1f784a6505be68469d4",
+ "7033f9ee0e52b08cb5679cd49b7b7999eaf9eaf8",
+ "70957110ce446c4e250f865760fb3da513cdcc92",
+ "8ec696a4734f16479d091bc70574d23dd9fe7443",
+ "8ed341c55ed4d6f4cdc8bf4f0ca18a08c93f6962",
+ "8edc2805f1f11b63e44bf81f4557f8b473612b69",
+ "8ef9060a954118a698fc10e20acdc430566a100f",
+ "8f0c4b543f4bb6eb1518ecfc3d4699e43108d393",
+ "8fac94df3035405c2e60b3799153ce7c428af6b9",
+ "904c0ac12b23548de524adae712241b423d765a3",
+ "90bbaa9a809c3a768d873a9cc7d52b4f3bf3d1b9",
+ "90d4d2f0fc362beabbbf76b4ffda0828229c198d",
+ "90f9ff6755330b685feff6c3d81782ee3592ab04",
+ "91822c50ebe4f9bf5bbb8308ecf9f6557062775c",
+ "91d973263a55708fa8255867b3202d81ef9c2868",
+ "af292c99c6148d772af3315a1c74e83330e7ead7",
+ "af3b99d5be330dbbce0b9250c3a5fb05911908cc",
+ "af55d0cdeb280af2db8697e5afa506e081012719",
+ "af795e498d411142ddb073e8ca2c5447c3295a4c",
+ "afadc73a392f8cc8e2cc77dd62a7433dd3bafa8c",
+ "affd84ed8ec7ce67612fe3c12a80f8164b101f6a",
+ "b0941f9c70ffe67f0387a827b338e64ecf3190f0",
+ "b0a3077f9ef6e093f8d9869bdb0c07095bd722cb",
+ "b0a8568a7614806378a54db5706ee3b06ae58693",
+ "b0fb7372f242233d1d35ce7d8e74d3990cbc5841",
+ "b10489944b9ead17427551759d180d10203e06ba",
+ "b196a807b323f2748ffc6b1d42cd0812d04c9a40",
+ "b1bb1d888f0c5e19278536d49fa77db035fac7ae"
+};
+
+static const char *loose_objects[] = {
+ "45b983be36b73c0788dc9cbcb76cbb80fc7bb057",
+ "a8233120f6ad708f843d861ce2b7228ec4e3dec6",
+ "fd093bff70906175335656e6ce6ae05783708765",
+ "c47800c7266a2be04c571c04d5a6614691ea99bd",
+ "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd",
+ "8496071c1b46c854b31185ea97743be6a8774479",
+ "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391",
+ "814889a078c031f61ed08ab5fa863aea9314344d",
+ "5b5b025afb0b4c913b4c338a42934a3863bf3644",
+ "1385f264afb75a56a5bec74243be9b367ba4ca08",
+ "f60079018b664e4e79329a7ef9559c8d9e0378d1",
+ "be3563ae3f795b2b4353bcce3a527ad0a4f7f644",
+ "75057dd4114e74cca1d750d0aee1647c903cb60a",
+ "fa49b077972391ad58037050f2a75f74e3671e92",
+ "9fd738e8f7967c078dceed8190330fc8648ee56a",
+ "1810dff58d8a660512d4832e740f692884338ccd",
+ "181037049a54a1eb5fab404658a3a250b44335d7",
+ "a4a7dce85cf63874e984719f4fdd239f5145052f",
+ "4a202b346bb0fb0db7eff3cffeb3c70babbd2045"
+};
diff --git a/tests/libgit2/odb/pack_data_256.h b/tests/libgit2/odb/pack_data_256.h
new file mode 100644
index 0000000..b631882
--- /dev/null
+++ b/tests/libgit2/odb/pack_data_256.h
@@ -0,0 +1,154 @@
+#ifdef GIT_EXPERIMENTAL_SHA256
+
+static const char *packed_objects_256[] = {
+ "99f3b405443221141eb0fd1e0cca5d355f893983749b7fb455769fba434e7945",
+ "d0fc7f52dc42358506e7f3f3be72f5271994abb104b9397ab3e19bb42361504d",
+ "86e228d9904af64586e9a8378005ba654681ff5be3c43ca930bf6b1f28d4395f",
+ "652412419a24ba62a1d897f40aeb80eecbf873797b04a1bbb8d71918653ef65b",
+ "ad90f638cb67720b20b904478471504acebacc7bb36e5dcad3e882acec496fed",
+ "4d46d9719e425ef2dfb5bfba098d0b62e21b2b92d0731892eef70db0870e3744",
+ "80ec1e36b983e68664e8357c538cd35b30638bb0cb99626f906d145e2d2e2558",
+ "e8bbf40ee280bc43b33c04df2250903b75e92f2497e91759cf1cad753c23be6c",
+ "8864b5746d7c5780083bb98449a3f5bf78d8281e8c5e3fd12a8ccd9103eb3a1f",
+ "083c2b8b445640d171e7aa667b0b32007006f786711032ebb82931cca69cc15b",
+ "5c8bea399f78d3d6a037a41cee2e763d00024180b66f2ec738d443b6a3dd7081",
+ "281c36286eab5e534f1c2121a4cf2cd48a32b3773a3e78df500bed3f3c9747f3",
+ "c9dc53358a0d83bee1caae40ee81d752abf4962a9f206702e24a447b766b5bd7",
+ "fafc05a1d0b7614ba32f428eb52f3439ffbfed9a817e5ae069364cfc3fa3e4d4",
+ "25a4efebb38c55b8f2309ce5e3116b2b9287239952cc2fa174074e05c6e5875a",
+ "c223f1b579bad18635efdeffa7a8ff40567d03fa427e08bb90d9878f958d8021",
+ "8a0042d434a2f8a2e8d47caa4eb454f388752fb3fe71150c1cea12e807cfdf1c",
+ "d79e913b4137117b7f8fc2a8d184373f657d6f71bbeaca0fe83b7757ce486108",
+ "375f69d3d41e36d6904bfa86221690ec49de2a030664a362abaf86c426f9f7e0",
+ "22b6705b86e4aa9120eff203af24709d483698d9f78695e86e82de121784b570",
+ "4516b0e63349c81abc6584cb11ce84ab8ba38b105f9de39d0d0a1455dba2478d",
+ "c2b535bfc3501f0b4e179d5d6f0e2cea613940fa3813be5923db7e71e190849f",
+ "aa793f0e9d9d746eba8a7a60cb4981f7e24ce9691910350d7df9b9e94c7567b9",
+ "890bb959ac8c20db603bf083bc82f55f9f42b6dca6581d941d0b361188abae3b",
+ "bfc0ee6fe04854c11011539f38cc6b9b73e0c445bac2008de2fb877123efc2e3",
+ "5ba7253f47d390ee2c7c7afe8fd9a963a7a2674bbdadeb9a927665c9246306c4",
+ "79bc735b91f8dfa9379d1d6c21e2d519ac1bf0d04d48534864c9ff571df5297d",
+ "f362826c827aa3bcbeb3ff8b71bba08d7440b89ab53fe95d61b8922d01f46e28",
+ "4a1b9c078e7bb20759a2d75e3a4b96827c851446c0261750b96aa5f286efe378",
+ "2e2aa456dbbb8889923eb6713672427854020298a764967c50235a9a76d7ce4b",
+ "97b0e3661fa0caa6200c50381233f8320b907540ceb9d17ac94fedc66fd093c2",
+ "ade7d297ede7bb58008da582de1253f0a55cb76e82d1cd376f82ccf97f70bced",
+ "3315093132a8f28bd202c0a9562d04eebea4943dcd1e1c754341b0389722042f",
+ "6fae137ba81d0d81c2a85759b99322e7ea8103bd7c8b85be3163b7e91e18c125",
+ "f8b45f792840019909fd35f9dcb98082b3bc39373268d467e1b00f1da5ac71e5",
+ "d2ab425d6092770366bc3dff5276e3a869221bc7b6d22e99c089d556e7eb8331",
+ "4dd37b9df07bcc7b45ce72a44e3f5fcaf5f0a9a5f3148963714eac4c99a60388",
+ "8c53c0f9f0972a1c77e40549170b9ae51d365c200ffc4cb220628c5bce3dd0b2",
+ "11d0463a82345c2512bd704dd00211aefb7d5b8590ca92809122fd09486a9f06",
+ "1ab4fa663d22416f45cb1a007d767a58d0abf5255bf86f888393dae637b37c3b",
+ "345d2fbf1d306c7ea46a05f497793308357e9e17ab0e866446d2b9894378ddce",
+ "304352ddad641770fcc94ee4d9f957cde7aaf4c107dbf8b5ba14d543640bf7dd",
+ "cfdd565f4cbc315760c287d57714852e1a4894eef9c715332fd556f2e114a9f1",
+ "d6d2e87e6de8690efd26b8c5b58de28dcb3c9bd2b7659eccf34468d71a7a4478",
+ "aa61d4adf622265ec814c1a97198d2bcd3f58fb08989cb9beda32a4d0aab6697",
+ "40799f33b8cd9ca41f36a2f89d8ac8550537ad01dfb21fbc76f01eeb62f512d4",
+ "faa7ca59426e17f6b34fa407d06cd634faaabeb4abe26df12296e05a17c98eef",
+ "1f6ea2cab887a2ff4bb1557a36dd6bac9931ef1f36794ddd22b4b7b7276051fb",
+ "b38a7a2ee69d55f021efa91caf59e23bccdaf6b8a9c3f83acd978aa177587537",
+ "f66691f32eb9b23a029b43251bfa994d16481fe97903057e121b76e4e78f6ff4",
+ "afe1fad6f6b22eea530ff7b373d6b9b787b39792f720e6fcd0692ba6ef99e02f",
+ "b71ee6c8837efa5b3ba3361f88c321d391ac05f41d5b2506cad39319e80716e4",
+ "e207a266e228414023223530eb77c64b10f2f5124f3354deb45aff04c1db98b9",
+ "696503760a18787240ba52ad1abec3be6517bb802238e9469b3a8999cbd6432c",
+ "c30d06fd49797e3135652d654db0de122dd83f8400df1c7a0e95ca3720defb0d",
+ "8a9b600a21987e6ffddbad745f38c115797eacef9117043bd9d2da4835ee3cc4",
+ "d509fa76ff5944e25f48c2476736b6239a53f0463cba6ebc488464d087951951",
+ "3e00d8cae2726dc33879adf876b30c306f50e7a85b15c8add4a27f84d88616d7",
+ "ce55f9f5ab1d799a9ffaa839539af196d13f35677b3d0761b0fe034e764f8d07",
+ "84005e38a65f4115d5c94790010dc57e1a3297f4aa89744f5927f208af758bd0",
+ "75b0ed5f4a2d5c810f34d867dbca51db6a596d4739abfaf24fcc0f05d99097a8",
+ "3eab74a6894d790767f3d92615a6887dcccaffa9e48fdd2ce482b5f17efcf9b9",
+ "8e61988c998c96a131cfe72225fce43e555bec4e590fa8c239373172a9d485ce",
+ "5f45a18f90f2934e7c7985d05b2b5b3584886fd057c9202f26f562d6c3080038",
+ "7609c608c1097270356a6fe336a8756ff124d4a9c2e941bba26a6e8c3becdeb1",
+ "81ba4e67aa59ccb078e3dc9ff3075f50084ec1696bd867e8e4284fcfc34fe3cf",
+ "3b1b991ae70d1f5388ad16b63d2285e99ada7a618c6f5d01e50a6d4b33c4767d",
+ "ac49410f64fad10760838866a40107573e42c86908c83ece433d64b7a8b57f7f",
+ "72c472319cf7d5d59bf9fea9e90b9785d7ab39340003fbc68619c11a9e583c2f",
+ "3a0cd33a47fcd0c7b8f8c1d407aaa53f648e25f2ffe2533a7e9c09c3d1b9da75",
+ "bf409f1aa76256282ebf1a7cc6c9f4220be9ffc47b2cc42248fcc5cbb67bce1f",
+ "13c8c9dae9fa63b1ec48b8abb12312fc8df61b9414678f504fa68164a48eef28",
+ "078baf54914fab56842798c90fec863f15f67a22041e8aa54a88c43f059da050",
+ "41c166c241a4e878f444932a193501e12ab38ba0634747291df70e619dccee1c",
+ "9db520a5a88c7d75b86a4faa1ce9010edf38af922f400c69a4a93aef69e25c4c",
+ "edf6e0dbfaf8a7ad89b05b5768981eeedd7a2bda4b1d0fae07aa2a9d49bb39df",
+ "ec1866b39026366e69ab8e167c15312c27f5eda4c0afdf9367ac3d76f56bf8e7",
+ "695bdb545f636d454b4825effd29db96c46d81772dda6c104f97dffe2ec509f1",
+ "07c31b4be5f3ec8f82c7ad6c91cbdf07ae876bb73041903d64fe8bac64bbcb6e",
+ "06665948a581e547ac1dc883b35bab54682fc311f7a87d5563a76501ecba55fc",
+ "fa6a2b7e588e57115d2772c0d5250d886757404fc510a5956be8de4926e94c01",
+ "28aec9a4450d9de98db98a021f97026bf12f1328f66e53319669eb5adce5ed3e",
+ "290d84a2cf108c074d3764ca8dc56e1215a5a7482837f098dff7a55e23d89d5f",
+ "ed10f9520d5f0d6bbd9c467ed83a239df0cba94ae9031602e02f6ad6256c459e",
+ "ad05c66a177f844f8239a1d186a1f803a4daa6286c959538838e222bc6337dc9",
+ "88fecc715077807bd7ee9e6a1dff65fde000379e0bf2a15cc2404469cef4c82b",
+ "bcc4fa242e55c5e08f64900407dca1cf2451806830905be616f339bf7a5580aa",
+ "4a4ebb837e3c9883f35d0ecdc26d4bc76a0f665568b08f3e967096f2fd3fa537",
+ "9abb7801e72353060a1490ffa3331674d6882558e7d6458de397b4b00d31ff8c",
+ "466cdfec4c74f3ed4fe53165e468a52df5dff9c6533ec433cdf235a73e099d32",
+ "8a1e1a6cc00519c4df8ce404c987751446fa299662308622cb63576fb52996d7",
+ "e5ea38108d603a6ed8dbb0b8455abe6d971f2d60920207e67ba646d267ece305",
+ "97e00ba4c8028ea91e7131c8c7596227cd03d3d2d14ed2d179fe0f305ebbca39",
+ "807faa3586b7f1aff2797a7c39c135b0196647c9ae7ee8843322accc960b2f22",
+ "b9134c331629e9cb29d1bbc03904d911a942b451e087543fc16deb0116391297",
+ "23cb97c200205dc84109c248be5bd719c23bdab52b52c51ec92fef9a48790833",
+ "b6e8427e3afe1cf0095d0f5aeca0642c4fb12d4e529f62e8264b6c4db72f04b4",
+ "92128dfb792caea934f5218807ec993867b0c8487a3de69ebac33e067f64d38a",
+ "a13ecbc514b571721a9a1c92af7f89d473a5fe13228904f8d17368e71f273ea1",
+ "3f5dca26a2f512d6681ce1957b8afba5e031bf63d52fd52d8f57093bf92391a4",
+ "1aa24b7ebd910f39a676f2ccab8e9a79f14842c20b55ed18e3dc297bdbaca279",
+ "1e0ce38d00e8f3e613febc0f8b275e0fd7fbef8af293fd62698be46dfbbc937d",
+ "dd9b1d5ca653752cef167e034324198971ab6f2f38f3db9db571cb2985759f00",
+ "efc4b8fd4b0b2586fce256b107ee2a052d11d26f99d85ce0478c3d49d1b2186b",
+ "da38f65b32fb03ce332e0c4238edae0a733e4cd793d849522bb4e0bdd0af608c",
+ "bf66badfda7b5d2157db65c5310cfdc4e904d7d5da57ac5abe17542de612f856",
+ "2c0f52f9ae1f34f280dfc1c755cbfaf2b9968fd3bab1f1eb16d3ac0fafd71940",
+ "165ec90aa4190bbe12ee415b294fd6d204c64afcf1ca64dd815782872b24ea26",
+ "cc3d272d457c7e26c5d611923841511a1766bcd58e5be66433698627e6fb3f9d",
+ "d970d1a6296d149bfb8283b8b4a9a6f7e9ee320c5d46a5ef216e10400df2d281",
+ "bdc530e0b98dd736cd812408eeed9aa0d393bbd0630b355eb7601e61f0dbc7c4",
+ "94db24aca3f8e07f481744f62633730feb4fc47605280381a08be510ae971ffe",
+ "151c7527acc5b731199a03a932ae374331e16e5ae29256e98cb652f37669889b",
+ "ac88849a26c126b03fc6fcb17cb23ec563e87a5f63b7afe800ad0f436128ce98",
+ "26a2dac21f8f0939566570e48f7f4fcf89239c2746ef8d3dbf31d179c691808f",
+ "619f24a7f37f8ca922c83b3a1b9a384eb6a444ff3a2a52c712f3c60dde6f24fb",
+ "5329dd2fd8557be5ad06b57882cf42e23d767cdc8a4b25e464fdb00890649e07",
+ "fff5cbc10ffea865d69aba64082ae17479c522e8e0305e678469749282bf0a18",
+ "d0c992aae9cde855b17ac826234a73255ecb09534cdcfa633d90640f6a4324d9",
+ "6129f1672465ee7b9e2edef53fbf3846ab5d06e8e6a1d7fb51e31666a8b411f8",
+ "0a8c0add81b065b97451f7b47a9935f0e240251c5f90a89ced885cb7d4efc2ba",
+ "471fa4fc2da467dd94e57babb1912bbdb5e40b96c8129d46fc709c0bfc009bca",
+ "23cea2b49eed4993c5b92a9d5f0b82efe4fae3837ea707d921de645d04479015",
+ "c85e35eec23dea4089aee7a2dc7f6d937ad7e13c66bdbd7eef37bd6336418609",
+ "a0f3dac8fa0e22dcf356aecbbdf79440715687c1053bc59b83354f276d688ceb",
+ "1cfb8ae71e9e576d1b16b7bd1a62156d0641c6e51f5d76877be6de4f26410623"
+};
+
+static const char *loose_objects_256[] = {
+ "96c18f0297e38d01f4b2dacddea4259aea6b2961eb0822bd2c0c3f6029030045",
+ "aea29dc305d40e362df25c3fdeed5502fd56b182af01b7740d297a24459333c5",
+ "73b4f3c4f3182e6c8dd2c98aeb2c7811556538e7673e4b325307c71685fbf5b6",
+ "901505c3355518bee35475c5d3f23bac1dded688b2bd314cc32b7f157e100724",
+ "4bc142808884e472ee6cc331b132e66ef18f564d41efb055804ec1dd28efb3f5",
+ "7e4633ae1b0e83503dbea4417f9d5ccaf22b877c5a4522b6d1d2b16090ee2f6f",
+ "473a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813",
+ "7030f925768d9beb65654ab8f436e3ca0a82b25eddefd237bf5a26a0441c2aa7",
+ "cb282e7c15fd8aeb2265cd621f5a228cb33dc84192980ca426cf9ab2a48cb9f0",
+ "33e415b835a670bb5c3c760efa0433ac0cbd2d44679f68f2df3a9ae7014cf2a8",
+ "8155958bbda08eed88c8ac908dc44452ed38911cffa54ccc06076f30a1ffb1bf",
+ "1b4b74772bd83ff28bf44cda9be93f4afc2279623bb5b36c9194a660b7623c24",
+ "f31459efb9367c5a19c9dd24c75107423d5773066922ea5e55eaeb6490979562",
+ "6d5fd291bb0f67444e99ab492f1bf1fcdf5dca09dab24cf331e05111b4cfc1a3",
+ "b83624f6ac0995273c0034a7ab8c68929bdc91b69ad54ef94979b93eba3f6022",
+ "61489e9e831f1d9001084d39b79f964c293db8620d679ea3596673c8a326446e",
+ "abee32b3339d1566d75613ea61f40c14bdfc5b101b60fde4f44b58dd06667640",
+ "a4813ef6708e6011e8187224297e83e4a285f58bf5eabb1db270351388603c95",
+ "43e084a4599ca42c476919917e3db8fde0045ee66305fd5e634b0c793c536a1b"
+};
+
+#endif
diff --git a/tests/libgit2/odb/pack_data_one.h b/tests/libgit2/odb/pack_data_one.h
new file mode 100644
index 0000000..13570ba
--- /dev/null
+++ b/tests/libgit2/odb/pack_data_one.h
@@ -0,0 +1,19 @@
+/* Just a few to make sure it's working, the rest is tested already */
+static const char *packed_objects_one[] = {
+ "9fcf811e00fa469688943a9152c16d4ee90fb9a9",
+ "a93f42a5b5e9de40fa645a9ff1e276a021c9542b",
+ "12bf5f3e3470d90db177ccf1b5e8126409377fc6",
+ "ed1ea164cdbe3c4b200fb4fa19861ea90eaee222",
+ "dfae6ed8f6dd8acc3b40a31811ea316239223559",
+ "aefe66d192771201e369fde830530f4475beec30",
+ "775e4b4c1296e9e3104f2a36ca9cf9356a130959",
+ "412ec4e4a6a7419bc1be00561fe474e54cb499fe",
+ "236e7579fed7763be77209efb8708960982f3cb3",
+ "09fe9364461cf60dd1c46b0e9545b1e47bb1a297",
+ "d76d8a6390d1cf32138d98a91b1eb7e0275a12f5",
+ "d0fdf2dcff2f548952eec536ccc6d266550041bc",
+ "a20d733a9fa79fa5b4cbb9639864f93325ec27a6",
+ "785d3fe8e7db5ade2c2242fecd46c32a7f4dc59f",
+ "4d8d0fd9cb6045075385701c3f933ec13345e9c4",
+ "0cfd861bd547b6520d1fc2e190e8359e0a9c9b90"
+};
diff --git a/tests/libgit2/odb/pack_data_one256.h b/tests/libgit2/odb/pack_data_one256.h
new file mode 100644
index 0000000..98a8747
--- /dev/null
+++ b/tests/libgit2/odb/pack_data_one256.h
@@ -0,0 +1,21 @@
+/* Just a few to make sure it's working, the rest is tested already */
+#ifdef GIT_EXPERIMENTAL_SHA256
+static const char *packed_objects_one256[] = {
+ "ea926306b1bab6d3f25f45609907eb6dff91a1460b25e63bf4a0494c70e7a269",
+ "d048ba2ef4fafa502a44cbc1a50cd58359b9bc243b84a08f541a08ca5f621137",
+ "a66bda0109d2b3c9bc87970da81bd91076b5f871febbc860f09ae997668b6800",
+ "3609a41c0506fe19d01fb8b4729923362675f191fe5f63fab3111ef804c48fdf",
+ "22b6705b86e4aa9120eff203af24709d483698d9f78695e86e82de121784b570",
+ "6f11d93bfb269ee8c7a506178f60c430abfac5d424acfd9c0b0b27b98e6ab49b",
+ "0aefd477d9e5b3f8d708a3cf6d78be6b670dfa2e2ec41244634f3b8f115d8e04",
+ "580474d948cd2ebd2e5ce7a5b81b872d87ba4639c1ac4c0fa7a11a8eddf9827c",
+ "0636b4292bfdd7274a977cb6f8b2ded8f315ea1bcd8dbedfca37964c2ed3d085",
+ "19fb1c78b11f0f8bda658d6fa6cc63c0b573c0f6760ee5a9c2df6ce2cde00c5c",
+ "7f2f7afccb317bb3fdd28555f126846dc4eebe5d9ae7b8d8a1456e8ff85422ce",
+ "4066249c68b0d3c8b39ebe02c9188935900465acad02a49269710f56720fa58e",
+ "a560d1fa1edf114f57b402e16d662c17b1e3b7e8601ff7dcea6615ba7f1e48ef",
+ "58923faa87c7d559d308a114ec2b164e5d6046c889420ed1def6eef2d55106a2",
+ "753ddabab8ae9c1e733cda15e8e3c83dd43f5a2d939ae32cc3b30b0be1e91f96",
+ "46333d32b3801cf11d9f80b557245c9e32b0e05deca61dae968914fde159f0e5"
+};
+#endif
diff --git a/tests/libgit2/odb/packed.c b/tests/libgit2/odb/packed.c
new file mode 100644
index 0000000..b41041f
--- /dev/null
+++ b/tests/libgit2/odb/packed.c
@@ -0,0 +1,79 @@
+#include "clar_libgit2.h"
+#include "odb.h"
+#include "pack_data.h"
+
+static git_odb *_odb;
+
+void test_odb_packed__initialize(void)
+{
+ cl_git_pass(git_odb__open(&_odb, cl_fixture("testrepo.git/objects"), NULL));
+}
+
+void test_odb_packed__cleanup(void)
+{
+ git_odb_free(_odb);
+ _odb = NULL;
+}
+
+void test_odb_packed__mass_read(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(packed_objects); ++i) {
+ git_oid id;
+ git_odb_object *obj;
+
+ cl_git_pass(git_oid__fromstr(&id, packed_objects[i], GIT_OID_SHA1));
+ cl_assert(git_odb_exists(_odb, &id) == 1);
+ cl_git_pass(git_odb_read(&obj, _odb, &id));
+
+ git_odb_object_free(obj);
+ }
+}
+
+void test_odb_packed__read_header_0(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(packed_objects); ++i) {
+ git_oid id;
+ git_odb_object *obj;
+ size_t len;
+ git_object_t type;
+
+ cl_git_pass(git_oid__fromstr(&id, packed_objects[i], GIT_OID_SHA1));
+
+ cl_git_pass(git_odb_read(&obj, _odb, &id));
+ cl_git_pass(git_odb_read_header(&len, &type, _odb, &id));
+
+ cl_assert(obj->cached.size == len);
+ cl_assert(obj->cached.type == type);
+
+ git_odb_object_free(obj);
+ }
+}
+
+void test_odb_packed__read_header_1(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(loose_objects); ++i) {
+ git_oid id;
+ git_odb_object *obj;
+ size_t len;
+ git_object_t type;
+
+ cl_git_pass(git_oid__fromstr(&id, loose_objects[i], GIT_OID_SHA1));
+
+ cl_assert(git_odb_exists(_odb, &id) == 1);
+
+ cl_git_pass(git_odb_read(&obj, _odb, &id));
+ cl_git_pass(git_odb_read_header(&len, &type, _odb, &id));
+
+ cl_assert(obj->cached.size == len);
+ cl_assert(obj->cached.type == type);
+
+ git_odb_object_free(obj);
+ }
+}
+
diff --git a/tests/libgit2/odb/packed256.c b/tests/libgit2/odb/packed256.c
new file mode 100644
index 0000000..65220fd
--- /dev/null
+++ b/tests/libgit2/odb/packed256.c
@@ -0,0 +1,98 @@
+#include "clar_libgit2.h"
+#include "odb.h"
+#include "pack_data_256.h"
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+static git_odb *_odb;
+#endif
+
+void test_odb_packed256__initialize(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_odb_options opts = GIT_ODB_OPTIONS_INIT;
+
+ opts.oid_type = GIT_OID_SHA256;
+
+ cl_git_pass(git_odb__open(
+ &_odb,
+ cl_fixture("testrepo_256.git/objects"),
+ &opts));
+#endif
+}
+
+void test_odb_packed256__cleanup(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_odb_free(_odb);
+ _odb = NULL;
+#endif
+}
+
+void test_odb_packed256__mass_read(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(packed_objects_256); ++i) {
+ git_oid id;
+ git_odb_object *obj;
+
+ cl_git_pass(git_oid__fromstr(&id, packed_objects_256[i], GIT_OID_SHA256));
+ cl_assert(git_odb_exists(_odb, &id) == 1);
+ cl_git_pass(git_odb_read(&obj, _odb, &id));
+
+ git_odb_object_free(obj);
+ }
+#endif
+}
+
+void test_odb_packed256__read_header_0(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(packed_objects_256); ++i) {
+ git_oid id;
+ git_odb_object *obj;
+ size_t len;
+ git_object_t type;
+
+ cl_git_pass(git_oid__fromstr(&id, packed_objects_256[i], GIT_OID_SHA256));
+
+ cl_git_pass(git_odb_read(&obj, _odb, &id));
+ cl_git_pass(git_odb_read_header(&len, &type, _odb, &id));
+
+ cl_assert(obj->cached.size == len);
+ cl_assert(obj->cached.type == type);
+
+ git_odb_object_free(obj);
+ }
+#endif
+}
+
+void test_odb_packed256__read_header_1(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(loose_objects_256); ++i) {
+ git_oid id;
+ git_odb_object *obj;
+ size_t len;
+ git_object_t type;
+
+ cl_git_pass(git_oid__fromstr(&id, loose_objects_256[i], GIT_OID_SHA256));
+
+ cl_assert(git_odb_exists(_odb, &id) == 1);
+
+ cl_git_pass(git_odb_read(&obj, _odb, &id));
+ cl_git_pass(git_odb_read_header(&len, &type, _odb, &id));
+
+ cl_assert(obj->cached.size == len);
+ cl_assert(obj->cached.type == type);
+
+ git_odb_object_free(obj);
+ }
+#endif
+}
+
diff --git a/tests/libgit2/odb/packedone.c b/tests/libgit2/odb/packedone.c
new file mode 100644
index 0000000..8637001
--- /dev/null
+++ b/tests/libgit2/odb/packedone.c
@@ -0,0 +1,67 @@
+#include "clar_libgit2.h"
+#include "git2/odb_backend.h"
+
+#include "pack_data_one.h"
+#include "pack.h"
+
+static git_odb *_odb;
+
+void test_odb_packedone__initialize(void)
+{
+ git_odb_backend *backend = NULL;
+
+ cl_git_pass(git_odb__new(&_odb, NULL));
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_odb_backend_one_pack(&backend,
+ cl_fixture("testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx"),
+ NULL));
+#else
+ cl_git_pass(git_odb_backend_one_pack(&backend,
+ cl_fixture("testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx")));
+#endif
+ cl_git_pass(git_odb_add_backend(_odb, backend, 1));
+}
+
+void test_odb_packedone__cleanup(void)
+{
+ git_odb_free(_odb);
+ _odb = NULL;
+}
+
+void test_odb_packedone__mass_read(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(packed_objects_one); ++i) {
+ git_oid id;
+ git_odb_object *obj;
+
+ cl_git_pass(git_oid__fromstr(&id, packed_objects_one[i], GIT_OID_SHA1));
+ cl_assert(git_odb_exists(_odb, &id) == 1);
+ cl_git_pass(git_odb_read(&obj, _odb, &id));
+
+ git_odb_object_free(obj);
+ }
+}
+
+void test_odb_packedone__read_header_0(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(packed_objects_one); ++i) {
+ git_oid id;
+ git_odb_object *obj;
+ size_t len;
+ git_object_t type;
+
+ cl_git_pass(git_oid__fromstr(&id, packed_objects_one[i], GIT_OID_SHA1));
+
+ cl_git_pass(git_odb_read(&obj, _odb, &id));
+ cl_git_pass(git_odb_read_header(&len, &type, _odb, &id));
+
+ cl_assert(obj->cached.size == len);
+ cl_assert(obj->cached.type == type);
+
+ git_odb_object_free(obj);
+ }
+}
diff --git a/tests/libgit2/odb/packedone256.c b/tests/libgit2/odb/packedone256.c
new file mode 100644
index 0000000..fdeac42
--- /dev/null
+++ b/tests/libgit2/odb/packedone256.c
@@ -0,0 +1,78 @@
+#include "clar_libgit2.h"
+#include "git2/odb_backend.h"
+
+#include "pack_data_one256.h"
+#include "pack.h"
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+static git_odb *_odb;
+#endif
+
+void test_odb_packedone256__initialize(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_odb_backend *backend = NULL;
+ git_odb_options odb_opts = GIT_ODB_OPTIONS_INIT;
+ git_odb_backend_pack_options backend_opts = GIT_ODB_BACKEND_PACK_OPTIONS_INIT;
+
+ odb_opts.oid_type = GIT_OID_SHA256;
+ backend_opts.oid_type = GIT_OID_SHA256;
+
+ cl_git_pass(git_odb__new(&_odb, &odb_opts));
+ cl_git_pass(git_odb_backend_one_pack(
+ &backend,
+ cl_fixture("testrepo_256.git/objects/pack/pack-e2f07f30db7e480ea84a0e64ee791b9b270067124b2609019b74f33f256f33fa.idx"),
+ &backend_opts));
+ cl_git_pass(git_odb_add_backend(_odb, backend, 1));
+#endif
+}
+
+void test_odb_packedone256__cleanup(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ git_odb_free(_odb);
+ _odb = NULL;
+#endif
+}
+
+void test_odb_packedone256__mass_read(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(packed_objects_one256); ++i) {
+ git_oid id;
+ git_odb_object *obj;
+
+ cl_git_pass(git_oid__fromstr(&id, packed_objects_one256[i], GIT_OID_SHA256));
+ cl_assert(git_odb_exists(_odb, &id) == 1);
+ cl_git_pass(git_odb_read(&obj, _odb, &id));
+
+ git_odb_object_free(obj);
+ }
+#endif
+}
+
+void test_odb_packedone256__read_header_0(void)
+{
+#ifdef GIT_EXPERIMENTAL_SHA256
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(packed_objects_one256); ++i) {
+ git_oid id;
+ git_odb_object *obj;
+ size_t len;
+ git_object_t type;
+
+ cl_git_pass(git_oid__fromstr(&id, packed_objects_one256[i], GIT_OID_SHA256));
+
+ cl_git_pass(git_odb_read(&obj, _odb, &id));
+ cl_git_pass(git_odb_read_header(&len, &type, _odb, &id));
+
+ cl_assert(obj->cached.size == len);
+ cl_assert(obj->cached.type == type);
+
+ git_odb_object_free(obj);
+ }
+#endif
+}
diff --git a/tests/libgit2/odb/sorting.c b/tests/libgit2/odb/sorting.c
new file mode 100644
index 0000000..ec4e369
--- /dev/null
+++ b/tests/libgit2/odb/sorting.c
@@ -0,0 +1,104 @@
+#include "clar_libgit2.h"
+#include "git2/sys/odb_backend.h"
+#include "odb.h"
+
+typedef struct {
+ git_odb_backend base;
+ size_t position;
+} fake_backend;
+
+static git_odb_backend *new_backend(size_t position)
+{
+ fake_backend *b;
+
+ b = git__calloc(1, sizeof(fake_backend));
+ if (b == NULL)
+ return NULL;
+
+ b->base.free = (void (*)(git_odb_backend *)) git__free;
+ b->base.version = GIT_ODB_BACKEND_VERSION;
+ b->position = position;
+ return (git_odb_backend *)b;
+}
+
+static void check_backend_sorting(git_odb *odb)
+{
+ size_t i, max_i = git_odb_num_backends(odb);
+ fake_backend *internal;
+
+ for (i = 0; i < max_i; ++i) {
+ cl_git_pass(git_odb_get_backend((git_odb_backend **)&internal, odb, i));
+ cl_assert(internal != NULL);
+ cl_assert_equal_sz(i, internal->position);
+ }
+}
+
+static git_odb *_odb;
+
+void test_odb_sorting__initialize(void)
+{
+ cl_git_pass(git_odb__new(&_odb, NULL));
+}
+
+void test_odb_sorting__cleanup(void)
+{
+ git_odb_free(_odb);
+ _odb = NULL;
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_ODB_LOOSE_PRIORITY,
+ GIT_ODB_DEFAULT_LOOSE_PRIORITY));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_ODB_PACKED_PRIORITY,
+ GIT_ODB_DEFAULT_PACKED_PRIORITY));
+}
+
+void test_odb_sorting__basic_backends_sorting(void)
+{
+ cl_git_pass(git_odb_add_backend(_odb, new_backend(0), 5));
+ cl_git_pass(git_odb_add_backend(_odb, new_backend(2), 3));
+ cl_git_pass(git_odb_add_backend(_odb, new_backend(1), 4));
+ cl_git_pass(git_odb_add_backend(_odb, new_backend(3), 1));
+
+ check_backend_sorting(_odb);
+}
+
+void test_odb_sorting__alternate_backends_sorting(void)
+{
+ cl_git_pass(git_odb_add_backend(_odb, new_backend(1), 5));
+ cl_git_pass(git_odb_add_backend(_odb, new_backend(5), 3));
+ cl_git_pass(git_odb_add_backend(_odb, new_backend(3), 4));
+ cl_git_pass(git_odb_add_backend(_odb, new_backend(7), 1));
+ cl_git_pass(git_odb_add_alternate(_odb, new_backend(0), 5));
+ cl_git_pass(git_odb_add_alternate(_odb, new_backend(4), 3));
+ cl_git_pass(git_odb_add_alternate(_odb, new_backend(2), 4));
+ cl_git_pass(git_odb_add_alternate(_odb, new_backend(6), 1));
+
+ check_backend_sorting(_odb);
+}
+
+void test_odb_sorting__override_default_backend_priority(void)
+{
+ git_odb *new_odb;
+ git_odb_backend *loose, *packed, *backend;
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_ODB_LOOSE_PRIORITY, 5));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_ODB_PACKED_PRIORITY, 3));
+ git_odb_backend_pack(&packed, "./testrepo.git/objects"
+#ifdef GIT_EXPERIMENTAL_SHA256
+ , NULL
+#endif
+ );
+ git_odb__backend_loose(&loose, "./testrepo.git/objects", NULL);
+
+ cl_git_pass(git_odb__open(&new_odb, cl_fixture("testrepo.git/objects"), NULL));
+ cl_assert_equal_sz(2, git_odb_num_backends(new_odb));
+
+ cl_git_pass(git_odb_get_backend(&backend, new_odb, 0));
+ cl_assert_equal_p(loose->read, backend->read);
+
+ cl_git_pass(git_odb_get_backend(&backend, new_odb, 1));
+ cl_assert_equal_p(packed->read, backend->read);
+
+ git_odb_free(new_odb);
+ loose->free(loose);
+ packed->free(packed);
+}
diff --git a/tests/libgit2/odb/streamwrite.c b/tests/libgit2/odb/streamwrite.c
new file mode 100644
index 0000000..c174f69
--- /dev/null
+++ b/tests/libgit2/odb/streamwrite.c
@@ -0,0 +1,56 @@
+#include "clar_libgit2.h"
+#include "git2/odb_backend.h"
+
+static git_repository *repo;
+static git_odb *odb;
+static git_odb_stream *stream;
+
+void test_odb_streamwrite__initialize(void)
+{
+ repo = cl_git_sandbox_init("testrepo.git");
+ cl_git_pass(git_repository_odb(&odb, repo));
+
+ cl_git_pass(git_odb_open_wstream(&stream, odb, 14, GIT_OBJECT_BLOB));
+ cl_assert_equal_sz(14, stream->declared_size);
+}
+
+void test_odb_streamwrite__cleanup(void)
+{
+ git_odb_stream_free(stream);
+ git_odb_free(odb);
+ cl_git_sandbox_cleanup();
+}
+
+void test_odb_streamwrite__can_accept_chunks(void)
+{
+ git_oid oid;
+
+ cl_git_pass(git_odb_stream_write(stream, "deadbeef", 8));
+ cl_assert_equal_sz(8, stream->received_bytes);
+
+ cl_git_pass(git_odb_stream_write(stream, "deadbeef", 6));
+ cl_assert_equal_sz(8 + 6, stream->received_bytes);
+
+ cl_git_pass(git_odb_stream_finalize_write(&oid, stream));
+}
+
+void test_odb_streamwrite__can_detect_missing_bytes(void)
+{
+ git_oid oid;
+
+ cl_git_pass(git_odb_stream_write(stream, "deadbeef", 8));
+ cl_assert_equal_sz(8, stream->received_bytes);
+
+ cl_git_pass(git_odb_stream_write(stream, "deadbeef", 4));
+ cl_assert_equal_sz(8 + 4, stream->received_bytes);
+
+ cl_git_fail(git_odb_stream_finalize_write(&oid, stream));
+}
+
+void test_odb_streamwrite__can_detect_additional_bytes(void)
+{
+ cl_git_pass(git_odb_stream_write(stream, "deadbeef", 8));
+ cl_assert_equal_sz(8, stream->received_bytes);
+
+ cl_git_fail(git_odb_stream_write(stream, "deadbeef", 7));
+}
diff --git a/tests/libgit2/online/badssl.c b/tests/libgit2/online/badssl.c
new file mode 100644
index 0000000..6735e9c
--- /dev/null
+++ b/tests/libgit2/online/badssl.c
@@ -0,0 +1,75 @@
+#include "clar_libgit2.h"
+
+#include "git2/clone.h"
+
+static git_repository *g_repo;
+
+#ifdef GIT_HTTPS
+static bool g_has_ssl = true;
+#else
+static bool g_has_ssl = false;
+#endif
+
+static int cert_check_assert_invalid(git_cert *cert, int valid, const char* host, void *payload)
+{
+ GIT_UNUSED(cert); GIT_UNUSED(host); GIT_UNUSED(payload);
+
+ cl_assert_equal_i(0, valid);
+
+ return GIT_ECERTIFICATE;
+}
+
+void test_online_badssl__expired(void)
+{
+ git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
+ opts.fetch_opts.callbacks.certificate_check = cert_check_assert_invalid;
+
+ if (!g_has_ssl)
+ cl_skip();
+
+ cl_git_fail_with(GIT_ECERTIFICATE,
+ git_clone(&g_repo, "https://expired.badssl.com/fake.git", "./fake", NULL));
+
+ cl_git_fail_with(GIT_ECERTIFICATE,
+ git_clone(&g_repo, "https://expired.badssl.com/fake.git", "./fake", &opts));
+}
+
+void test_online_badssl__wrong_host(void)
+{
+ git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
+ opts.fetch_opts.callbacks.certificate_check = cert_check_assert_invalid;
+
+ if (!g_has_ssl)
+ cl_skip();
+
+ cl_git_fail_with(GIT_ECERTIFICATE,
+ git_clone(&g_repo, "https://wrong.host.badssl.com/fake.git", "./fake", NULL));
+ cl_git_fail_with(GIT_ECERTIFICATE,
+ git_clone(&g_repo, "https://wrong.host.badssl.com/fake.git", "./fake", &opts));
+}
+
+void test_online_badssl__self_signed(void)
+{
+ git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
+ opts.fetch_opts.callbacks.certificate_check = cert_check_assert_invalid;
+
+ if (!g_has_ssl)
+ cl_skip();
+
+ cl_git_fail_with(GIT_ECERTIFICATE,
+ git_clone(&g_repo, "https://self-signed.badssl.com/fake.git", "./fake", NULL));
+ cl_git_fail_with(GIT_ECERTIFICATE,
+ git_clone(&g_repo, "https://self-signed.badssl.com/fake.git", "./fake", &opts));
+}
+
+void test_online_badssl__old_cipher(void)
+{
+ git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
+ opts.fetch_opts.callbacks.certificate_check = cert_check_assert_invalid;
+
+ if (!g_has_ssl)
+ cl_skip();
+
+ cl_git_fail(git_clone(&g_repo, "https://rc4.badssl.com/fake.git", "./fake", NULL));
+ cl_git_fail(git_clone(&g_repo, "https://rc4.badssl.com/fake.git", "./fake", &opts));
+}
diff --git a/tests/libgit2/online/clone.c b/tests/libgit2/online/clone.c
new file mode 100644
index 0000000..5789e96
--- /dev/null
+++ b/tests/libgit2/online/clone.c
@@ -0,0 +1,1388 @@
+#include "clar_libgit2.h"
+
+#include "git2/clone.h"
+#include "git2/cred_helpers.h"
+#include "remote.h"
+#include "futils.h"
+#include "refs.h"
+
+#define LIVE_REPO_URL "http://github.com/libgit2/TestGitRepository"
+#define LIVE_EMPTYREPO_URL "http://github.com/libgit2/TestEmptyRepository"
+#define BB_REPO_URL "https://libgit2-test@bitbucket.org/libgit2-test/testgitrepository.git"
+#define BB_REPO_URL_WITH_PASS "https://libgit2-test:YT77Ppm2nq8w4TYjGS8U@bitbucket.org/libgit2-test/testgitrepository.git"
+#define BB_REPO_URL_WITH_WRONG_PASS "https://libgit2-test:wrong@bitbucket.org/libgit2-test/testgitrepository.git"
+#define GOOGLESOURCE_REPO_URL "https://chromium.googlesource.com/external/github.com/sergi/go-diff"
+
+#define SSH_REPO_URL "ssh://github.com/libgit2/TestGitRepository"
+
+static git_repository *g_repo;
+static git_clone_options g_options;
+
+static char *_remote_url = NULL;
+static char *_remote_user = NULL;
+static char *_remote_pass = NULL;
+static char *_remote_branch = NULL;
+static char *_remote_sslnoverify = NULL;
+static char *_remote_ssh_pubkey = NULL;
+static char *_remote_ssh_privkey = NULL;
+static char *_remote_ssh_passphrase = NULL;
+static char *_remote_ssh_fingerprint = NULL;
+static char *_remote_proxy_scheme = NULL;
+static char *_remote_proxy_host = NULL;
+static char *_remote_proxy_user = NULL;
+static char *_remote_proxy_pass = NULL;
+static char *_remote_proxy_selfsigned = NULL;
+static char *_remote_expectcontinue = NULL;
+static char *_remote_redirect_initial = NULL;
+static char *_remote_redirect_subsequent = NULL;
+static char *_remote_speed_timesout = NULL;
+static char *_remote_speed_slow = NULL;
+
+static char *_github_ssh_pubkey = NULL;
+static char *_github_ssh_privkey = NULL;
+static char *_github_ssh_passphrase = NULL;
+static char *_github_ssh_remotehostkey = NULL;
+
+static char *_orig_http_proxy = NULL;
+static char *_orig_https_proxy = NULL;
+static char *_orig_no_proxy = NULL;
+
+static int ssl_cert(git_cert *cert, int valid, const char *host, void *payload)
+{
+ GIT_UNUSED(cert);
+ GIT_UNUSED(host);
+ GIT_UNUSED(payload);
+
+ if (_remote_sslnoverify != NULL)
+ valid = 1;
+
+ return valid ? 0 : GIT_ECERTIFICATE;
+}
+
+void test_online_clone__initialize(void)
+{
+ git_checkout_options dummy_opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_fetch_options dummy_fetch = GIT_FETCH_OPTIONS_INIT;
+
+ g_repo = NULL;
+
+ memset(&g_options, 0, sizeof(git_clone_options));
+ g_options.version = GIT_CLONE_OPTIONS_VERSION;
+ g_options.checkout_opts = dummy_opts;
+ g_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+ g_options.fetch_opts = dummy_fetch;
+ g_options.fetch_opts.callbacks.certificate_check = ssl_cert;
+
+ _remote_url = cl_getenv("GITTEST_REMOTE_URL");
+ _remote_user = cl_getenv("GITTEST_REMOTE_USER");
+ _remote_pass = cl_getenv("GITTEST_REMOTE_PASS");
+ _remote_branch = cl_getenv("GITTEST_REMOTE_BRANCH");
+ _remote_sslnoverify = cl_getenv("GITTEST_REMOTE_SSL_NOVERIFY");
+ _remote_ssh_pubkey = cl_getenv("GITTEST_REMOTE_SSH_PUBKEY");
+ _remote_ssh_privkey = cl_getenv("GITTEST_REMOTE_SSH_KEY");
+ _remote_ssh_passphrase = cl_getenv("GITTEST_REMOTE_SSH_PASSPHRASE");
+ _remote_ssh_fingerprint = cl_getenv("GITTEST_REMOTE_SSH_FINGERPRINT");
+ _remote_proxy_scheme = cl_getenv("GITTEST_REMOTE_PROXY_SCHEME");
+ _remote_proxy_host = cl_getenv("GITTEST_REMOTE_PROXY_HOST");
+ _remote_proxy_user = cl_getenv("GITTEST_REMOTE_PROXY_USER");
+ _remote_proxy_pass = cl_getenv("GITTEST_REMOTE_PROXY_PASS");
+ _remote_proxy_selfsigned = cl_getenv("GITTEST_REMOTE_PROXY_SELFSIGNED");
+ _remote_expectcontinue = cl_getenv("GITTEST_REMOTE_EXPECTCONTINUE");
+ _remote_redirect_initial = cl_getenv("GITTEST_REMOTE_REDIRECT_INITIAL");
+ _remote_redirect_subsequent = cl_getenv("GITTEST_REMOTE_REDIRECT_SUBSEQUENT");
+ _remote_speed_timesout = cl_getenv("GITTEST_REMOTE_SPEED_TIMESOUT");
+ _remote_speed_slow = cl_getenv("GITTEST_REMOTE_SPEED_SLOW");
+
+ _github_ssh_pubkey = cl_getenv("GITTEST_GITHUB_SSH_PUBKEY");
+ _github_ssh_privkey = cl_getenv("GITTEST_GITHUB_SSH_KEY");
+ _github_ssh_passphrase = cl_getenv("GITTEST_GITHUB_SSH_PASSPHRASE");
+ _github_ssh_remotehostkey = cl_getenv("GITTEST_GITHUB_SSH_REMOTE_HOSTKEY");
+
+ _orig_http_proxy = cl_getenv("HTTP_PROXY");
+ _orig_https_proxy = cl_getenv("HTTPS_PROXY");
+ _orig_no_proxy = cl_getenv("NO_PROXY");
+
+ if (_remote_expectcontinue)
+ git_libgit2_opts(GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE, 1);
+}
+
+void test_online_clone__cleanup(void)
+{
+ if (g_repo) {
+ git_repository_free(g_repo);
+ g_repo = NULL;
+ }
+ cl_fixture_cleanup("./foo");
+ cl_fixture_cleanup("./initial");
+ cl_fixture_cleanup("./subsequent");
+
+ git__free(_remote_url);
+ git__free(_remote_user);
+ git__free(_remote_pass);
+ git__free(_remote_branch);
+ git__free(_remote_sslnoverify);
+ git__free(_remote_ssh_pubkey);
+ git__free(_remote_ssh_privkey);
+ git__free(_remote_ssh_passphrase);
+ git__free(_remote_ssh_fingerprint);
+ git__free(_remote_proxy_scheme);
+ git__free(_remote_proxy_host);
+ git__free(_remote_proxy_user);
+ git__free(_remote_proxy_pass);
+ git__free(_remote_proxy_selfsigned);
+ git__free(_remote_expectcontinue);
+ git__free(_remote_redirect_initial);
+ git__free(_remote_redirect_subsequent);
+ git__free(_remote_speed_timesout);
+ git__free(_remote_speed_slow);
+
+ git__free(_github_ssh_pubkey);
+ git__free(_github_ssh_privkey);
+ git__free(_github_ssh_passphrase);
+ git__free(_github_ssh_remotehostkey);
+
+ cl_setenv("HTTP_PROXY", _orig_http_proxy);
+ cl_setenv("HTTPS_PROXY", _orig_https_proxy);
+ cl_setenv("NO_PROXY", _orig_no_proxy);
+
+ git__free(_orig_http_proxy);
+ git__free(_orig_https_proxy);
+ git__free(_orig_no_proxy);
+
+ git_libgit2_opts(GIT_OPT_SET_SSL_CERT_LOCATIONS, NULL, NULL);
+ git_libgit2_opts(GIT_OPT_SET_SERVER_TIMEOUT, 0);
+ git_libgit2_opts(GIT_OPT_SET_SERVER_CONNECT_TIMEOUT, 0);
+}
+
+void test_online_clone__network_full(void)
+{
+ git_remote *origin;
+
+ cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
+ cl_assert(!git_repository_is_bare(g_repo));
+ cl_git_pass(git_remote_lookup(&origin, g_repo, "origin"));
+
+ cl_assert_equal_i(GIT_REMOTE_DOWNLOAD_TAGS_AUTO, origin->download_tags);
+
+ git_remote_free(origin);
+}
+
+void test_online_clone__network_bare(void)
+{
+ git_remote *origin;
+
+ g_options.bare = true;
+
+ cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
+ cl_assert(git_repository_is_bare(g_repo));
+ cl_git_pass(git_remote_lookup(&origin, g_repo, "origin"));
+
+ git_remote_free(origin);
+}
+
+void test_online_clone__empty_repository(void)
+{
+ git_reference *head;
+
+ cl_git_pass(git_clone(&g_repo, LIVE_EMPTYREPO_URL, "./foo", &g_options));
+
+ cl_assert_equal_i(true, git_repository_is_empty(g_repo));
+ cl_assert_equal_i(true, git_repository_head_unborn(g_repo));
+
+ cl_git_pass(git_reference_lookup(&head, g_repo, GIT_HEAD_FILE));
+ cl_assert_equal_i(GIT_REFERENCE_SYMBOLIC, git_reference_type(head));
+ cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head));
+
+ git_reference_free(head);
+}
+
+static void checkout_progress(const char *path, size_t cur, size_t tot, void *payload)
+{
+ bool *was_called = (bool*)payload;
+ GIT_UNUSED(path); GIT_UNUSED(cur); GIT_UNUSED(tot);
+ (*was_called) = true;
+}
+
+static int fetch_progress(const git_indexer_progress *stats, void *payload)
+{
+ bool *was_called = (bool*)payload;
+ GIT_UNUSED(stats);
+ (*was_called) = true;
+ return 0;
+}
+
+void test_online_clone__can_checkout_a_cloned_repo(void)
+{
+ git_str path = GIT_STR_INIT;
+ git_reference *head, *remote_head;
+ bool checkout_progress_cb_was_called = false,
+ fetch_progress_cb_was_called = false;
+
+ g_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+ g_options.checkout_opts.progress_cb = &checkout_progress;
+ g_options.checkout_opts.progress_payload = &checkout_progress_cb_was_called;
+ g_options.fetch_opts.callbacks.transfer_progress = &fetch_progress;
+ g_options.fetch_opts.callbacks.payload = &fetch_progress_cb_was_called;
+
+ cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
+
+ cl_git_pass(git_str_joinpath(&path, git_repository_workdir(g_repo), "master.txt"));
+ cl_assert_equal_i(true, git_fs_path_isfile(git_str_cstr(&path)));
+
+ cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
+ cl_assert_equal_i(GIT_REFERENCE_SYMBOLIC, git_reference_type(head));
+ cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head));
+
+ cl_git_pass(git_reference_lookup(&remote_head, g_repo, "refs/remotes/origin/HEAD"));
+ cl_assert_equal_i(GIT_REFERENCE_SYMBOLIC, git_reference_type(remote_head));
+ cl_assert_equal_s("refs/remotes/origin/master", git_reference_symbolic_target(remote_head));
+
+ cl_assert_equal_i(true, checkout_progress_cb_was_called);
+ cl_assert_equal_i(true, fetch_progress_cb_was_called);
+
+ git_reference_free(remote_head);
+ git_reference_free(head);
+ git_str_dispose(&path);
+}
+
+static int remote_mirror_cb(git_remote **out, git_repository *repo,
+ const char *name, const char *url, void *payload)
+{
+ int error;
+ git_remote *remote;
+
+ GIT_UNUSED(payload);
+
+ if ((error = git_remote_create_with_fetchspec(&remote, repo, name, url, "+refs/*:refs/*")) < 0)
+ return error;
+
+ *out = remote;
+ return 0;
+}
+
+void test_online_clone__clone_mirror(void)
+{
+ git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
+ git_reference *head;
+
+ bool fetch_progress_cb_was_called = false;
+
+ opts.fetch_opts.callbacks.transfer_progress = &fetch_progress;
+ opts.fetch_opts.callbacks.payload = &fetch_progress_cb_was_called;
+
+ opts.bare = true;
+ opts.remote_cb = remote_mirror_cb;
+
+ cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo.git", &opts));
+
+ cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
+ cl_assert_equal_i(GIT_REFERENCE_SYMBOLIC, git_reference_type(head));
+ cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head));
+
+ cl_assert_equal_i(true, fetch_progress_cb_was_called);
+
+ git_reference_free(head);
+ git_repository_free(g_repo);
+ g_repo = NULL;
+
+ cl_fixture_cleanup("./foo.git");
+}
+
+static int update_tips(const char *refname, const git_oid *a, const git_oid *b, void *payload)
+{
+ int *callcount = (int*)payload;
+ GIT_UNUSED(refname); GIT_UNUSED(a); GIT_UNUSED(b);
+ *callcount = *callcount + 1;
+ return 0;
+}
+
+void test_online_clone__custom_remote_callbacks(void)
+{
+ int callcount = 0;
+
+ g_options.fetch_opts.callbacks.update_tips = update_tips;
+ g_options.fetch_opts.callbacks.payload = &callcount;
+
+ cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
+ cl_assert(callcount > 0);
+}
+
+void test_online_clone__custom_headers(void)
+{
+ char *empty_header = "";
+ char *unnamed_header = "this is a header about nothing";
+ char *newlines = "X-Custom: almost OK\n";
+ char *conflict = "Accept: defined-by-git";
+ char *ok = "X-Custom: this should be ok";
+
+ g_options.fetch_opts.custom_headers.count = 1;
+
+ g_options.fetch_opts.custom_headers.strings = &empty_header;
+ cl_git_fail(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
+
+ g_options.fetch_opts.custom_headers.strings = &unnamed_header;
+ cl_git_fail(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
+
+ g_options.fetch_opts.custom_headers.strings = &newlines;
+ cl_git_fail(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
+
+ g_options.fetch_opts.custom_headers.strings = &conflict;
+ cl_git_fail(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
+
+ /* Finally, we got it right! */
+ g_options.fetch_opts.custom_headers.strings = &ok;
+ cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
+}
+
+void test_online_clone__long_custom_header(void)
+{
+ /* Long custom header with 1500 characters */
+ char *ok = "X-Custom: a0MsqH2bXV9lILn7zkAHqKpGrOVvkik7SfoyqfXbFTxccsymN5SG9hEB0RLD9koTXKWtaI1vI9jHf5ViwLHq6xvkveFX9GiqaIhe3TRu5KDZrOBgeufdBYsTTONALPlpni9XVq71bR6x3AlVEqHdXi9qiq0TRuNiujMy0ZKs8LQkQVSE8kxWZXqLsO2IJtAPw5aqsUEenK5ec12GOeOTOYlSChGllzvl2Ow4SKlVg3t8NHVWvc8HyPGmBQ79l3qUMU30P0hnUXaIrhIzGgleYWnwhGFLpryxsQfCdwkdBMuvtLH0DnkhLoAkCmnCZItEExtHBOCirEzztoFMX3lH4lM4wMqePCU8II0qloNvzPgt6cBThQJP66FYUDSCwsSb63bcTWdVx7TCa6mAplkP49PKi5pFSvFKKbs5se5MPcBVG03GiatKszIQkii0vp6OV5b54Aym4N8hQJHFMhIChKiQM91tB7PQu9vPJE6h2bzAnQsn34bBPFZHT7pBplqkASiHDjw69YV6k3M8ffTOTr2ibQnTKxh1NH3ZRx6u0KxRty9i4YLMniZUZAfFgqbSW2xXk49e8J9VNFm7j2bgHp3t813wUzqnQL4NEc0CQlF0e6pId5ADXikoH6S7aMfuYUYi1Kn1i9m7UGtaB0U7dVC65uH9vIWKnyAcmBt0mN1aikRnjz7oBKjD65SRZrKWXeCDJkpgWlXnD5JjekDCyB9m3yGkaxy1FflI1kaa4kcVbPRfs6XebHRDl9golPBUyazRG1V1iOi1mKki9ClUNO8wviNfKm5eMbWW6hU8wMXh388EotRA73TUdL4JIfNpkC4XBFLNFbFtltzO34kxXBKvhj8t0XVZOp4AWpHEL3pUtuyKhNWaWlDF6ZhjCeO8vT1akKoYaA7t6nFyqawq5nPoB0iXEHQ7YugfYfgjzpNGLgvPJ6aLg9YIKZBqfi7J9xWb356IJvTQFswi7qm6Mu7IVXarS9m84b5IfT6UCVq84u4VcdBlDswNPTw6SbBtzg9vrLLs3MoTCzJY6fHPqnKt6YthgQwOOB1ig7GTSDiX3W3SMeaz5jTASociHrUS3HrwVSgjrODnF86962cv4s3DGYjiX2cIuNfq9mZVJlNsylZjFYFV9LzOjNLlSHZVJrrGQJLjmyOCwOMkG9u2xKdSvfjxTJzqhjhTvQSQZWhKt44hA9EidUqPqjc3MhfnZ6aeAIP232gtRHoRc7FdjRSan4Q3PWy02YiRodvKAafonwCOtMcm4MASrXBiBE1tibHLSTtK4UrodFNVhymtBCRnJdVRSgrCQcr2B5Jzs4Iv6uJlJqwwyuq6In54zcmecgJZezta84B3eFoSGJhCbI6Zza0khulccglCcppciWDStAHFhncePsCQL4tup0Z8fS01RksRQ7X1xgskVvQAKELThDqbJB4FJZwrwPXOpCweAoSONntp7Ly0lAUabw75gK5sR387IxNVdISmfP";
+
+ g_options.fetch_opts.custom_headers.count = 1;
+ g_options.fetch_opts.custom_headers.strings = &ok;
+ cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
+}
+
+static int cred_failure_cb(
+ git_credential **cred,
+ const char *url,
+ const char *username_from_url,
+ unsigned int allowed_types,
+ void *data)
+{
+ GIT_UNUSED(cred); GIT_UNUSED(url); GIT_UNUSED(username_from_url);
+ GIT_UNUSED(allowed_types); GIT_UNUSED(data);
+ return -172;
+}
+
+void test_online_clone__cred_callback_failure_return_code_is_tunnelled(void)
+{
+ git__free(_remote_url);
+ git__free(_remote_user);
+
+ _remote_url = git__strdup("https://github.com/libgit2/non-existent");
+ _remote_user = git__strdup("libgit2test");
+
+ g_options.fetch_opts.callbacks.credentials = cred_failure_cb;
+
+ cl_git_fail_with(-172, git_clone(&g_repo, _remote_url, "./foo", &g_options));
+}
+
+static int cred_count_calls_cb(git_credential **cred, const char *url, const char *user,
+ unsigned int allowed_types, void *data)
+{
+ size_t *counter = (size_t *) data;
+
+ GIT_UNUSED(url); GIT_UNUSED(user); GIT_UNUSED(allowed_types);
+
+ if (allowed_types == GIT_CREDENTIAL_USERNAME)
+ return git_credential_username_new(cred, "foo");
+
+ (*counter)++;
+
+ if (*counter == 3)
+ return GIT_EUSER;
+
+ return git_credential_userpass_plaintext_new(cred, "foo", "bar");
+}
+
+void test_online_clone__cred_callback_called_again_on_auth_failure(void)
+{
+ size_t counter = 0;
+
+ git__free(_remote_url);
+ git__free(_remote_user);
+
+ _remote_url = git__strdup("https://gitlab.com/libgit2/non-existent");
+ _remote_user = git__strdup("libgit2test");
+
+ g_options.fetch_opts.callbacks.credentials = cred_count_calls_cb;
+ g_options.fetch_opts.callbacks.payload = &counter;
+
+ cl_git_fail_with(GIT_EUSER, git_clone(&g_repo, _remote_url, "./foo", &g_options));
+ cl_assert_equal_i(3, counter);
+}
+
+static int cred_default(
+ git_credential **cred,
+ const char *url,
+ const char *user_from_url,
+ unsigned int allowed_types,
+ void *payload)
+{
+ GIT_UNUSED(url);
+ GIT_UNUSED(user_from_url);
+ GIT_UNUSED(payload);
+
+ if (!(allowed_types & GIT_CREDENTIAL_DEFAULT))
+ return 0;
+
+ return git_credential_default_new(cred);
+}
+
+void test_online_clone__credentials(void)
+{
+ /* Remote URL environment variable must be set.
+ * User and password are optional.
+ */
+ git_credential_userpass_payload user_pass = {
+ _remote_user,
+ _remote_pass
+ };
+
+ if (!_remote_url)
+ clar__skip();
+
+ if (cl_is_env_set("GITTEST_REMOTE_DEFAULT")) {
+ g_options.fetch_opts.callbacks.credentials = cred_default;
+ } else {
+ g_options.fetch_opts.callbacks.credentials = git_credential_userpass;
+ g_options.fetch_opts.callbacks.payload = &user_pass;
+ }
+
+ cl_git_pass(git_clone(&g_repo, _remote_url, "./foo", &g_options));
+ git_repository_free(g_repo); g_repo = NULL;
+ cl_fixture_cleanup("./foo");
+}
+
+void test_online_clone__credentials_via_custom_headers(void)
+{
+ const char *creds = "libgit2-test:YT77Ppm2nq8w4TYjGS8U";
+ git_str auth = GIT_STR_INIT;
+
+ cl_git_pass(git_str_puts(&auth, "Authorization: Basic "));
+ cl_git_pass(git_str_encode_base64(&auth, creds, strlen(creds)));
+ g_options.fetch_opts.custom_headers.count = 1;
+ g_options.fetch_opts.custom_headers.strings = &auth.ptr;
+
+ cl_git_pass(git_clone(&g_repo, "https://bitbucket.org/libgit2-test/testgitrepository.git", "./foo", &g_options));
+
+ git_str_dispose(&auth);
+}
+
+void test_online_clone__bitbucket_style(void)
+{
+ git_credential_userpass_payload user_pass = {
+ "libgit2-test", "YT77Ppm2nq8w4TYjGS8U"
+ };
+
+ g_options.fetch_opts.callbacks.credentials = git_credential_userpass;
+ g_options.fetch_opts.callbacks.payload = &user_pass;
+
+ cl_git_pass(git_clone(&g_repo, BB_REPO_URL, "./foo", &g_options));
+ git_repository_free(g_repo); g_repo = NULL;
+ cl_fixture_cleanup("./foo");
+}
+
+void test_online_clone__bitbucket_uses_creds_in_url(void)
+{
+ git_credential_userpass_payload user_pass = {
+ "libgit2-test", "wrong"
+ };
+
+ g_options.fetch_opts.callbacks.credentials = git_credential_userpass;
+ g_options.fetch_opts.callbacks.payload = &user_pass;
+
+ /*
+ * Correct user and pass are in the URL; the (incorrect) creds in
+ * the `git_credential_userpass_payload` should be ignored.
+ */
+ cl_git_pass(git_clone(&g_repo, BB_REPO_URL_WITH_PASS, "./foo", &g_options));
+ git_repository_free(g_repo); g_repo = NULL;
+ cl_fixture_cleanup("./foo");
+}
+
+void test_online_clone__bitbucket_falls_back_to_specified_creds(void)
+{
+ git_credential_userpass_payload user_pass = {
+ "libgit2-test", "libgit2"
+ };
+
+ g_options.fetch_opts.callbacks.credentials = git_credential_userpass;
+ g_options.fetch_opts.callbacks.payload = &user_pass;
+
+ /*
+ * TODO: as of March 2018, bitbucket sporadically fails with
+ * 403s instead of replying with a 401 - but only sometimes.
+ */
+ cl_skip();
+
+ /*
+ * Incorrect user and pass are in the URL; the (correct) creds in
+ * the `git_credential_userpass_payload` should be used as a fallback.
+ */
+ cl_git_pass(git_clone(&g_repo, BB_REPO_URL_WITH_WRONG_PASS, "./foo", &g_options));
+ git_repository_free(g_repo); g_repo = NULL;
+ cl_fixture_cleanup("./foo");
+}
+
+void test_online_clone__googlesource(void)
+{
+#ifdef __APPLE__
+ cl_skip();
+#else
+ cl_git_pass(git_clone(&g_repo, GOOGLESOURCE_REPO_URL, "./foo", &g_options));
+ git_repository_free(g_repo); g_repo = NULL;
+ cl_fixture_cleanup("./foo");
+#endif
+}
+
+static int cancel_at_half(const git_indexer_progress *stats, void *payload)
+{
+ GIT_UNUSED(payload);
+
+ if (stats->received_objects > (stats->total_objects/2))
+ return 4321;
+ return 0;
+}
+
+void test_online_clone__can_cancel(void)
+{
+ g_options.fetch_opts.callbacks.transfer_progress = cancel_at_half;
+
+ cl_git_fail_with(4321,
+ git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
+}
+
+static int cred_cb(git_credential **cred, const char *url, const char *user_from_url,
+ unsigned int allowed_types, void *payload)
+{
+ GIT_UNUSED(url); GIT_UNUSED(user_from_url); GIT_UNUSED(payload);
+
+ if (allowed_types & GIT_CREDENTIAL_USERNAME)
+ return git_credential_username_new(cred, _remote_user);
+
+ if (allowed_types & GIT_CREDENTIAL_SSH_KEY)
+ return git_credential_ssh_key_new(cred,
+ _remote_user, _remote_ssh_pubkey,
+ _remote_ssh_privkey, _remote_ssh_passphrase);
+
+ git_error_set(GIT_ERROR_NET, "unexpected cred type");
+ return -1;
+}
+
+static int check_ssh_auth_methods(git_credential **cred, const char *url, const char *username_from_url,
+ unsigned int allowed_types, void *data)
+{
+ int *with_user = (int *) data;
+ GIT_UNUSED(cred); GIT_UNUSED(url); GIT_UNUSED(username_from_url); GIT_UNUSED(data);
+
+ if (!*with_user)
+ cl_assert_equal_i(GIT_CREDENTIAL_USERNAME, allowed_types);
+ else
+ cl_assert(!(allowed_types & GIT_CREDENTIAL_USERNAME));
+
+ return GIT_EUSER;
+}
+
+static int succeed_certificate_check(git_cert *cert, int valid, const char *host, void *payload)
+{
+ GIT_UNUSED(cert);
+ GIT_UNUSED(valid);
+ GIT_UNUSED(payload);
+
+ cl_assert_equal_s("github.com", host);
+
+ return 0;
+}
+
+static int x509_succeed_certificate_check(git_cert *cert, int valid, const char *host, void *payload)
+{
+ GIT_UNUSED(valid);
+ GIT_UNUSED(payload);
+
+ cl_assert_equal_s("github.com", host);
+ cl_assert_equal_i(GIT_CERT_X509, cert->cert_type);
+
+ return 0;
+}
+
+static int fail_certificate_check(git_cert *cert, int valid, const char *host, void *payload)
+{
+ GIT_UNUSED(cert);
+ GIT_UNUSED(valid);
+ GIT_UNUSED(host);
+ GIT_UNUSED(payload);
+
+ return GIT_ECERTIFICATE;
+}
+
+static int github_credentials(
+ git_credential **cred,
+ const char *url,
+ const char *username_from_url,
+ unsigned int allowed_types,
+ void *data)
+{
+ GIT_UNUSED(url);
+ GIT_UNUSED(username_from_url);
+ GIT_UNUSED(data);
+
+ if ((allowed_types & GIT_CREDENTIAL_USERNAME) != 0) {
+ return git_credential_username_new(cred, "git");
+ }
+
+ cl_assert((allowed_types & GIT_CREDENTIAL_SSH_KEY) != 0);
+
+ return git_credential_ssh_key_memory_new(cred,
+ "git",
+ _github_ssh_pubkey,
+ _github_ssh_privkey,
+ _github_ssh_passphrase);
+}
+
+void test_online_clone__ssh_github(void)
+{
+#if !defined(GIT_SSH) || !defined(GIT_SSH_MEMORY_CREDENTIALS)
+ clar__skip();
+#endif
+
+ if (!_github_ssh_pubkey || !_github_ssh_privkey)
+ clar__skip();
+
+ cl_fake_homedir(NULL);
+
+ g_options.fetch_opts.callbacks.credentials = github_credentials;
+ g_options.fetch_opts.callbacks.certificate_check = succeed_certificate_check;
+
+ cl_git_pass(git_clone(&g_repo, SSH_REPO_URL, "./foo", &g_options));
+}
+
+void test_online_clone__ssh_auth_methods(void)
+{
+ int with_user;
+
+#ifndef GIT_SSH
+ clar__skip();
+#endif
+ g_options.fetch_opts.callbacks.credentials = check_ssh_auth_methods;
+ g_options.fetch_opts.callbacks.payload = &with_user;
+ g_options.fetch_opts.callbacks.certificate_check = succeed_certificate_check;
+
+ with_user = 0;
+ cl_git_fail_with(GIT_EUSER,
+ git_clone(&g_repo, SSH_REPO_URL, "./foo", &g_options));
+
+ with_user = 1;
+ cl_git_fail_with(GIT_EUSER,
+ git_clone(&g_repo, "ssh://git@github.com/libgit2/TestGitRepository", "./foo", &g_options));
+}
+
+/*
+ * Ensure that the certificate check callback is still called, and
+ * can accept a host key that is not in the known hosts file.
+ */
+void test_online_clone__ssh_certcheck_accepts_unknown(void)
+{
+#if !defined(GIT_SSH) || !defined(GIT_SSH_MEMORY_CREDENTIALS)
+ clar__skip();
+#endif
+
+ if (!_github_ssh_pubkey || !_github_ssh_privkey)
+ clar__skip();
+
+ cl_fake_homedir(NULL);
+
+ g_options.fetch_opts.callbacks.credentials = github_credentials;
+
+ /* Ensure we fail without the certificate check */
+ cl_git_fail_with(GIT_ECERTIFICATE,
+ git_clone(&g_repo, SSH_REPO_URL, "./foo", NULL));
+
+ /* Set the callback to accept the certificate */
+ g_options.fetch_opts.callbacks.certificate_check = succeed_certificate_check;
+
+ cl_git_pass(git_clone(&g_repo, SSH_REPO_URL, "./foo", &g_options));
+}
+
+/*
+ * Ensure that the known hosts file is read and the certificate check
+ * callback is still called after that.
+ */
+void test_online_clone__ssh_certcheck_override_knownhosts(void)
+{
+ git_str knownhostsfile = GIT_STR_INIT;
+
+#if !defined(GIT_SSH) || !defined(GIT_SSH_MEMORY_CREDENTIALS)
+ clar__skip();
+#endif
+
+ if (!_github_ssh_pubkey || !_github_ssh_privkey || !_github_ssh_remotehostkey)
+ clar__skip();
+
+ g_options.fetch_opts.callbacks.credentials = github_credentials;
+
+ cl_fake_homedir(&knownhostsfile);
+ cl_git_pass(git_str_joinpath(&knownhostsfile, knownhostsfile.ptr, ".ssh"));
+ cl_git_pass(p_mkdir(knownhostsfile.ptr, 0777));
+
+ cl_git_pass(git_str_joinpath(&knownhostsfile, knownhostsfile.ptr, "known_hosts"));
+ cl_git_rewritefile(knownhostsfile.ptr, _github_ssh_remotehostkey);
+
+ /* Ensure we succeed without the certificate check */
+ cl_git_pass(git_clone(&g_repo, SSH_REPO_URL, "./foo", &g_options));
+ git_repository_free(g_repo);
+ g_repo = NULL;
+
+ /* Set the callback to reject the certificate */
+ g_options.fetch_opts.callbacks.certificate_check = fail_certificate_check;
+ cl_git_fail_with(GIT_ECERTIFICATE, git_clone(&g_repo, SSH_REPO_URL, "./bar", &g_options));
+
+ git_str_dispose(&knownhostsfile);
+}
+
+static int custom_remote_ssh_with_paths(
+ git_remote **out,
+ git_repository *repo,
+ const char *name,
+ const char *url,
+ void *payload)
+{
+ int error;
+
+ GIT_UNUSED(payload);
+
+ if ((error = git_remote_create(out, repo, name, url)) < 0)
+ return error;
+
+ return 0;
+}
+
+void test_online_clone__ssh_with_paths(void)
+{
+ char *bad_paths[] = {
+ "/bin/yes",
+ "/bin/false",
+ };
+ char *good_paths[] = {
+ "/usr/bin/git-upload-pack",
+ "/usr/bin/git-receive-pack",
+ };
+ git_strarray arr = {
+ bad_paths,
+ 2,
+ };
+
+#ifndef GIT_SSH
+ clar__skip();
+#endif
+ if (!_remote_url || !_remote_user || strncmp(_remote_url, "ssh://", 5) != 0)
+ clar__skip();
+
+ g_options.remote_cb = custom_remote_ssh_with_paths;
+ g_options.fetch_opts.callbacks.transport = git_transport_ssh_with_paths;
+ g_options.fetch_opts.callbacks.credentials = cred_cb;
+ g_options.fetch_opts.callbacks.payload = &arr;
+ g_options.fetch_opts.callbacks.certificate_check = NULL;
+
+ cl_git_fail(git_clone(&g_repo, _remote_url, "./foo", &g_options));
+
+ arr.strings = good_paths;
+ cl_git_pass(git_clone(&g_repo, _remote_url, "./foo", &g_options));
+}
+
+static int cred_foo_bar(git_credential **cred, const char *url, const char *username_from_url,
+ unsigned int allowed_types, void *data)
+
+{
+ GIT_UNUSED(url); GIT_UNUSED(username_from_url); GIT_UNUSED(allowed_types); GIT_UNUSED(data);
+
+ return git_credential_userpass_plaintext_new(cred, "foo", "bar");
+}
+
+void test_online_clone__ssh_cannot_change_username(void)
+{
+#ifndef GIT_SSH
+ clar__skip();
+#endif
+ g_options.fetch_opts.callbacks.credentials = cred_foo_bar;
+
+ cl_git_fail(git_clone(&g_repo, "ssh://git@github.com/libgit2/TestGitRepository", "./foo", &g_options));
+}
+
+static int ssh_certificate_check(git_cert *cert, int valid, const char *host, void *payload)
+{
+ git_cert_hostkey *key;
+ git_oid expected = GIT_OID_SHA1_ZERO, actual = GIT_OID_SHA1_ZERO;
+
+ GIT_UNUSED(valid);
+ GIT_UNUSED(payload);
+
+ cl_assert(_remote_ssh_fingerprint);
+
+ cl_git_pass(git_oid__fromstrp(&expected, _remote_ssh_fingerprint, GIT_OID_SHA1));
+ cl_assert_equal_i(GIT_CERT_HOSTKEY_LIBSSH2, cert->cert_type);
+ key = (git_cert_hostkey *) cert;
+
+ /*
+ * We need to figure out how long our input was to check for
+ * the type. Here we abuse the fact that both hashes fit into
+ * our git_oid type.
+ */
+ if (strlen(_remote_ssh_fingerprint) == 32 && key->type & GIT_CERT_SSH_MD5) {
+ memcpy(&actual.id, key->hash_md5, 16);
+ } else if (strlen(_remote_ssh_fingerprint) == 40 && key->type & GIT_CERT_SSH_SHA1) {
+ memcpy(&actual, key->hash_sha1, 20);
+ } else {
+ cl_fail("Cannot find a usable SSH hash");
+ }
+
+ cl_assert(!memcmp(&expected, &actual, 20));
+
+ cl_assert_equal_s("localhost", host);
+
+ return GIT_EUSER;
+}
+
+void test_online_clone__ssh_cert(void)
+{
+ g_options.fetch_opts.callbacks.certificate_check = ssh_certificate_check;
+
+ if (!_remote_ssh_fingerprint)
+ cl_skip();
+
+ cl_git_fail_with(GIT_EUSER, git_clone(&g_repo, _remote_url, "./foo", &g_options));
+}
+
+static char *read_key_file(const char *path)
+{
+ FILE *f;
+ char *buf;
+ long key_length;
+
+ if (!path || !*path)
+ return NULL;
+
+ cl_assert((f = fopen(path, "r")) != NULL);
+ cl_assert(fseek(f, 0, SEEK_END) != -1);
+ cl_assert((key_length = ftell(f)) != -1);
+ cl_assert(fseek(f, 0, SEEK_SET) != -1);
+ cl_assert((buf = malloc(key_length)) != NULL);
+ cl_assert(fread(buf, key_length, 1, f) == 1);
+ fclose(f);
+
+ return buf;
+}
+
+static int ssh_memory_cred_cb(git_credential **cred, const char *url, const char *user_from_url,
+ unsigned int allowed_types, void *payload)
+{
+ GIT_UNUSED(url); GIT_UNUSED(user_from_url); GIT_UNUSED(payload);
+
+ if (allowed_types & GIT_CREDENTIAL_USERNAME)
+ return git_credential_username_new(cred, _remote_user);
+
+ if (allowed_types & GIT_CREDENTIAL_SSH_KEY)
+ {
+ char *pubkey = read_key_file(_remote_ssh_pubkey);
+ char *privkey = read_key_file(_remote_ssh_privkey);
+
+ int ret = git_credential_ssh_key_memory_new(cred, _remote_user, pubkey, privkey, _remote_ssh_passphrase);
+
+ if (privkey)
+ free(privkey);
+ if (pubkey)
+ free(pubkey);
+ return ret;
+ }
+
+ git_error_set(GIT_ERROR_NET, "unexpected cred type");
+ return -1;
+}
+
+void test_online_clone__ssh_memory_auth(void)
+{
+#ifndef GIT_SSH_MEMORY_CREDENTIALS
+ clar__skip();
+#endif
+ if (!_remote_url || !_remote_user || !_remote_ssh_privkey || strncmp(_remote_url, "ssh://", 5) != 0)
+ clar__skip();
+
+ g_options.fetch_opts.callbacks.credentials = ssh_memory_cred_cb;
+
+ cl_git_pass(git_clone(&g_repo, _remote_url, "./foo", &g_options));
+}
+
+void test_online_clone__certificate_invalid(void)
+{
+ g_options.fetch_opts.callbacks.certificate_check = fail_certificate_check;
+
+ cl_git_fail_with(git_clone(&g_repo, "https://github.com/libgit2/TestGitRepository", "./foo", &g_options),
+ GIT_ECERTIFICATE);
+
+#ifdef GIT_SSH
+ cl_git_fail_with(git_clone(&g_repo, "ssh://github.com/libgit2/TestGitRepository", "./foo", &g_options),
+ GIT_ECERTIFICATE);
+#endif
+}
+
+void test_online_clone__certificate_valid(void)
+{
+ g_options.fetch_opts.callbacks.certificate_check = x509_succeed_certificate_check;
+
+ cl_git_pass(git_clone(&g_repo, "https://github.com/libgit2/TestGitRepository", "./foo", &g_options));
+}
+
+void test_online_clone__start_with_http(void)
+{
+ g_options.fetch_opts.callbacks.certificate_check = succeed_certificate_check;
+
+ cl_git_pass(git_clone(&g_repo, "http://github.com/libgit2/TestGitRepository", "./foo", &g_options));
+}
+
+static int called_proxy_creds;
+static int proxy_cred_cb(git_credential **out, const char *url, const char *username, unsigned int allowed, void *payload)
+{
+ GIT_UNUSED(url);
+ GIT_UNUSED(username);
+ GIT_UNUSED(allowed);
+ GIT_UNUSED(payload);
+
+ called_proxy_creds = 1;
+ return git_credential_userpass_plaintext_new(out, _remote_proxy_user, _remote_proxy_pass);
+}
+
+static int proxy_cert_cb(git_cert *cert, int valid, const char *host, void *payload)
+{
+ char *colon;
+ size_t host_len;
+
+ GIT_UNUSED(cert);
+ GIT_UNUSED(valid);
+ GIT_UNUSED(payload);
+
+ cl_assert(_remote_proxy_host);
+
+ if ((colon = strchr(_remote_proxy_host, ':')) != NULL)
+ host_len = (colon - _remote_proxy_host);
+ else
+ host_len = strlen(_remote_proxy_host);
+
+ if (_remote_proxy_selfsigned != NULL &&
+ strlen(host) == host_len &&
+ strncmp(_remote_proxy_host, host, host_len) == 0)
+ valid = 1;
+
+ return valid ? 0 : GIT_ECERTIFICATE;
+}
+
+void test_online_clone__proxy_http_host_port_in_opts(void)
+{
+ if (!_remote_proxy_host || !_remote_proxy_user || !_remote_proxy_pass)
+ cl_skip();
+
+ if (_remote_proxy_scheme && strcmp(_remote_proxy_scheme, "http") != 0)
+ cl_skip();
+
+ g_options.fetch_opts.proxy_opts.type = GIT_PROXY_SPECIFIED;
+ g_options.fetch_opts.proxy_opts.url = _remote_proxy_host;
+ g_options.fetch_opts.proxy_opts.credentials = proxy_cred_cb;
+
+ called_proxy_creds = 0;
+ cl_git_pass(git_clone(&g_repo, "https://github.com/libgit2/TestGitRepository", "./foo", &g_options));
+ cl_assert(called_proxy_creds == 1);
+}
+
+void test_online_clone__proxy_http_host_port_in_env(void)
+{
+ if (!_remote_proxy_host || !_remote_proxy_user || !_remote_proxy_pass)
+ cl_skip();
+
+ if (_remote_proxy_scheme && strcmp(_remote_proxy_scheme, "http") != 0)
+ cl_skip();
+
+ cl_setenv("HTTP_PROXY", _remote_proxy_host);
+ cl_setenv("HTTPS_PROXY", _remote_proxy_host);
+ cl_setenv("NO_PROXY", NULL);
+
+ g_options.fetch_opts.proxy_opts.type = GIT_PROXY_AUTO;
+ g_options.fetch_opts.proxy_opts.credentials = proxy_cred_cb;
+
+ called_proxy_creds = 0;
+ cl_git_pass(git_clone(&g_repo, "https://github.com/libgit2/TestGitRepository", "./foo", &g_options));
+ cl_assert(called_proxy_creds == 1);
+}
+
+static int repository_create_with_proxy(
+ git_repository **out,
+ const char *path,
+ int bare,
+ void *payload)
+{
+ git_repository *repo;
+ git_config *config;
+ char *value = (char *)payload;
+
+ cl_git_pass(git_repository_init(&repo, path, bare));
+ cl_git_pass(git_repository_config(&config, repo));
+
+ cl_git_pass(git_config_set_string(config, "http.proxy", value));
+
+ git_config_free(config);
+
+ *out = repo;
+ return 0;
+}
+
+void test_online_clone__proxy_http_host_port_in_config(void)
+{
+ if (!_remote_proxy_host || !_remote_proxy_user || !_remote_proxy_pass)
+ cl_skip();
+
+ g_options.fetch_opts.proxy_opts.type = GIT_PROXY_AUTO;
+ g_options.fetch_opts.proxy_opts.credentials = proxy_cred_cb;
+ g_options.repository_cb = repository_create_with_proxy;
+ g_options.repository_cb_payload = _remote_proxy_host;
+
+ called_proxy_creds = 0;
+ cl_git_pass(git_clone(&g_repo, "https://github.com/libgit2/TestGitRepository", "./foo", &g_options));
+ cl_assert(called_proxy_creds == 1);
+}
+
+void test_online_clone__proxy_invalid_url(void)
+{
+ g_options.fetch_opts.proxy_opts.type = GIT_PROXY_SPECIFIED;
+ g_options.fetch_opts.proxy_opts.credentials = proxy_cred_cb;
+ g_options.fetch_opts.proxy_opts.certificate_check = proxy_cert_cb;
+
+ g_options.fetch_opts.proxy_opts.url = "noschemeorport";
+ cl_git_fail(git_clone(&g_repo, "http://github.com/libgit2/TestGitRepository", "./foo", &g_options));
+
+ g_options.fetch_opts.proxy_opts.url = "noscheme:8080";
+ cl_git_fail(git_clone(&g_repo, "http://github.com/libgit2/TestGitRepository", "./foo", &g_options));
+}
+
+void test_online_clone__proxy_credentials_request(void)
+{
+ git_str url = GIT_STR_INIT;
+
+ if (!_remote_proxy_host || !_remote_proxy_user || !_remote_proxy_pass)
+ cl_skip();
+
+ cl_git_pass(git_str_printf(&url, "%s://%s/",
+ _remote_proxy_scheme ? _remote_proxy_scheme : "http",
+ _remote_proxy_host));
+
+ g_options.fetch_opts.proxy_opts.type = GIT_PROXY_SPECIFIED;
+ g_options.fetch_opts.proxy_opts.url = url.ptr;
+ g_options.fetch_opts.proxy_opts.credentials = proxy_cred_cb;
+ g_options.fetch_opts.proxy_opts.certificate_check = proxy_cert_cb;
+ called_proxy_creds = 0;
+ cl_git_pass(git_clone(&g_repo, "http://github.com/libgit2/TestGitRepository", "./foo", &g_options));
+ cl_assert(called_proxy_creds);
+
+ git_str_dispose(&url);
+}
+
+void test_online_clone__proxy_credentials_in_well_formed_url(void)
+{
+ git_str url = GIT_STR_INIT;
+
+ if (!_remote_proxy_host || !_remote_proxy_user || !_remote_proxy_pass)
+ cl_skip();
+
+ cl_git_pass(git_str_printf(&url, "%s://%s:%s@%s/",
+ _remote_proxy_scheme ? _remote_proxy_scheme : "http",
+ _remote_proxy_user, _remote_proxy_pass, _remote_proxy_host));
+
+ g_options.fetch_opts.proxy_opts.type = GIT_PROXY_SPECIFIED;
+ g_options.fetch_opts.proxy_opts.url = url.ptr;
+ g_options.fetch_opts.proxy_opts.certificate_check = proxy_cert_cb;
+ called_proxy_creds = 0;
+ cl_git_pass(git_clone(&g_repo, "http://github.com/libgit2/TestGitRepository", "./foo", &g_options));
+ cl_assert(called_proxy_creds == 0);
+
+ git_str_dispose(&url);
+}
+
+void test_online_clone__proxy_credentials_in_host_port_format(void)
+{
+ git_str url = GIT_STR_INIT;
+
+ if (!_remote_proxy_host || !_remote_proxy_user || !_remote_proxy_pass)
+ cl_skip();
+
+ if (_remote_proxy_scheme && strcmp(_remote_proxy_scheme, "http") != 0)
+ cl_skip();
+
+ cl_git_pass(git_str_printf(&url, "%s:%s@%s",
+ _remote_proxy_user, _remote_proxy_pass, _remote_proxy_host));
+
+ g_options.fetch_opts.proxy_opts.type = GIT_PROXY_SPECIFIED;
+ g_options.fetch_opts.proxy_opts.url = url.ptr;
+ g_options.fetch_opts.proxy_opts.certificate_check = proxy_cert_cb;
+ called_proxy_creds = 0;
+ cl_git_pass(git_clone(&g_repo, "http://github.com/libgit2/TestGitRepository", "./foo", &g_options));
+ cl_assert(called_proxy_creds == 0);
+
+ git_str_dispose(&url);
+}
+
+void test_online_clone__proxy_credentials_in_environment(void)
+{
+ git_str url = GIT_STR_INIT;
+
+ if (!_remote_proxy_host || !_remote_proxy_user || !_remote_proxy_pass)
+ cl_skip();
+
+ g_options.fetch_opts.proxy_opts.type = GIT_PROXY_AUTO;
+ g_options.fetch_opts.proxy_opts.certificate_check = proxy_cert_cb;
+
+ cl_git_pass(git_str_printf(&url, "%s://%s:%s@%s/",
+ _remote_proxy_scheme ? _remote_proxy_scheme : "http",
+ _remote_proxy_user, _remote_proxy_pass, _remote_proxy_host));
+
+ cl_setenv("HTTP_PROXY", url.ptr);
+ cl_setenv("HTTPS_PROXY", url.ptr);
+ cl_setenv("NO_PROXY", NULL);
+
+ cl_git_pass(git_clone(&g_repo, "http://github.com/libgit2/TestGitRepository", "./foo", &g_options));
+
+ git_str_dispose(&url);
+}
+
+void test_online_clone__proxy_credentials_in_url_https(void)
+{
+ git_str url = GIT_STR_INIT;
+
+ if (!_remote_proxy_host || !_remote_proxy_user || !_remote_proxy_pass)
+ cl_skip();
+
+ cl_git_pass(git_str_printf(&url, "%s://%s:%s@%s/",
+ _remote_proxy_scheme ? _remote_proxy_scheme : "http",
+ _remote_proxy_user, _remote_proxy_pass, _remote_proxy_host));
+
+ g_options.fetch_opts.proxy_opts.type = GIT_PROXY_SPECIFIED;
+ g_options.fetch_opts.proxy_opts.url = url.ptr;
+ g_options.fetch_opts.proxy_opts.certificate_check = proxy_cert_cb;
+ g_options.fetch_opts.callbacks.certificate_check = ssl_cert;
+ called_proxy_creds = 0;
+ cl_git_pass(git_clone(&g_repo, "https://github.com/libgit2/TestGitRepository", "./foo", &g_options));
+ cl_assert(called_proxy_creds == 0);
+
+ git_str_dispose(&url);
+}
+
+void test_online_clone__proxy_auto_not_detected(void)
+{
+ g_options.fetch_opts.proxy_opts.type = GIT_PROXY_AUTO;
+
+ cl_git_pass(git_clone(&g_repo, "http://github.com/libgit2/TestGitRepository", "./foo", &g_options));
+}
+
+void test_online_clone__proxy_cred_callback_after_failed_url_creds(void)
+{
+ git_str url = GIT_STR_INIT;
+
+ if (!_remote_proxy_host || !_remote_proxy_user || !_remote_proxy_pass)
+ cl_skip();
+
+ cl_git_pass(git_str_printf(&url, "%s://invalid_user_name:INVALID_pass_WORD@%s/",
+ _remote_proxy_scheme ? _remote_proxy_scheme : "http",
+ _remote_proxy_host));
+
+ g_options.fetch_opts.proxy_opts.type = GIT_PROXY_SPECIFIED;
+ g_options.fetch_opts.proxy_opts.url = url.ptr;
+ g_options.fetch_opts.proxy_opts.credentials = proxy_cred_cb;
+ g_options.fetch_opts.proxy_opts.certificate_check = proxy_cert_cb;
+ called_proxy_creds = 0;
+ cl_git_pass(git_clone(&g_repo, "http://github.com/libgit2/TestGitRepository", "./foo", &g_options));
+ cl_assert(called_proxy_creds);
+
+ git_str_dispose(&url);
+}
+
+void test_online_clone__azurerepos(void)
+{
+ cl_git_pass(git_clone(&g_repo, "https://libgit2@dev.azure.com/libgit2/test/_git/test", "./foo", &g_options));
+ cl_assert(git_fs_path_exists("./foo/master.txt"));
+}
+
+void test_online_clone__path_whitespace(void)
+{
+ cl_git_pass(git_clone(&g_repo, "https://libgit2@dev.azure.com/libgit2/test/_git/spaces%20in%20the%20name", "./foo", &g_options));
+ cl_assert(git_fs_path_exists("./foo/master.txt"));
+}
+
+void test_online_clone__redirect_default_succeeds_for_initial(void)
+{
+ git_clone_options options = GIT_CLONE_OPTIONS_INIT;
+
+ if (!_remote_redirect_initial || !_remote_redirect_subsequent)
+ cl_skip();
+
+ cl_git_pass(git_clone(&g_repo, _remote_redirect_initial, "./initial", &options));
+}
+
+void test_online_clone__redirect_default_fails_for_subsequent(void)
+{
+ git_clone_options options = GIT_CLONE_OPTIONS_INIT;
+
+ if (!_remote_redirect_initial || !_remote_redirect_subsequent)
+ cl_skip();
+
+ cl_git_fail(git_clone(&g_repo, _remote_redirect_subsequent, "./fail", &options));
+}
+
+void test_online_clone__redirect_none(void)
+{
+ git_clone_options options = GIT_CLONE_OPTIONS_INIT;
+
+ if (!_remote_redirect_initial)
+ cl_skip();
+
+ options.fetch_opts.follow_redirects = GIT_REMOTE_REDIRECT_NONE;
+
+ cl_git_fail(git_clone(&g_repo, _remote_redirect_initial, "./fail", &options));
+}
+
+void test_online_clone__redirect_initial_succeeds_for_initial(void)
+{
+ git_clone_options options = GIT_CLONE_OPTIONS_INIT;
+
+ if (!_remote_redirect_initial || !_remote_redirect_subsequent)
+ cl_skip();
+
+ options.fetch_opts.follow_redirects = GIT_REMOTE_REDIRECT_INITIAL;
+
+ cl_git_pass(git_clone(&g_repo, _remote_redirect_initial, "./initial", &options));
+}
+
+void test_online_clone__redirect_initial_fails_for_subsequent(void)
+{
+ git_clone_options options = GIT_CLONE_OPTIONS_INIT;
+
+ if (!_remote_redirect_initial || !_remote_redirect_subsequent)
+ cl_skip();
+
+ options.fetch_opts.follow_redirects = GIT_REMOTE_REDIRECT_INITIAL;
+
+ cl_git_fail(git_clone(&g_repo, _remote_redirect_subsequent, "./fail", &options));
+}
+
+void test_online_clone__namespace_bare(void)
+{
+ git_clone_options options = GIT_CLONE_OPTIONS_INIT;
+ git_reference *head;
+
+ if (!_remote_url)
+ cl_skip();
+
+ options.bare = true;
+
+ cl_git_pass(git_clone(&g_repo, _remote_url, "./namespaced.git", &options));
+
+ cl_git_pass(git_reference_lookup(&head, g_repo, GIT_HEAD_FILE));
+ cl_assert_equal_i(GIT_REFERENCE_SYMBOLIC, git_reference_type(head));
+ cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head));
+
+ git_reference_free(head);
+}
+
+void test_online_clone__namespace_with_specified_branch(void)
+{
+ git_clone_options options = GIT_CLONE_OPTIONS_INIT;
+ git_reference *head;
+
+ if (!_remote_url || !_remote_branch)
+ cl_skip();
+
+ options.checkout_branch = _remote_branch;
+
+ cl_git_pass(git_clone(&g_repo, _remote_url, "./namespaced", &options));
+
+ cl_git_pass(git_reference_lookup(&head, g_repo, GIT_HEAD_FILE));
+ cl_assert_equal_i(GIT_REFERENCE_SYMBOLIC, git_reference_type(head));
+ cl_assert_equal_strn("refs/heads/", git_reference_symbolic_target(head), 11);
+ cl_assert_equal_s(_remote_branch, git_reference_symbolic_target(head) + 11);
+
+ git_reference_free(head);
+}
+
+void test_online_clone__sha256(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ git_clone_options options = GIT_CLONE_OPTIONS_INIT;
+ git_reference *head;
+
+ if (!_remote_url)
+ cl_skip();
+
+ cl_git_pass(git_clone(&g_repo, _remote_url, "./sha256", &options));
+ cl_git_pass(git_reference_lookup(&head, g_repo, GIT_HEAD_FILE));
+ cl_assert_equal_i(GIT_REFERENCE_SYMBOLIC, git_reference_type(head));
+
+ git_reference_free(head);
+#endif
+}
+
+void test_online_clone__connect_timeout_configurable(void)
+{
+#ifdef GIT_WINHTTP
+ cl_skip();
+#else
+ uint64_t start, finish;
+
+ start = git_time_monotonic();
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SERVER_CONNECT_TIMEOUT, 1));
+ cl_git_fail(git_clone(&g_repo, "http://www.google.com:8000/", "./timedout", NULL));
+ cl_assert(git_error_last() && strstr(git_error_last()->message, "timed out"));
+
+ finish = git_time_monotonic();
+
+ cl_assert(finish - start < 1000);
+#endif
+}
+
+void test_online_clone__connect_timeout_default(void)
+{
+#ifdef GIT_WINHTTP
+ cl_skip();
+#else
+ /* This test takes ~ 75 seconds on Unix. */
+ if (!cl_is_env_set("GITTEST_INVASIVE_SPEED"))
+ cl_skip();
+
+ /*
+ * Use a host/port pair that blackholes packets and does not
+ * send an RST.
+ */
+ cl_git_fail_with(GIT_TIMEOUT, git_clone(&g_repo, "http://www.google.com:8000/", "./refused", NULL));
+ cl_assert(git_error_last() && strstr(git_error_last()->message, "timed out"));
+#endif
+}
+
+void test_online_clone__timeout_configurable_times_out(void)
+{
+#ifdef GIT_WINHTTP
+ cl_skip();
+#else
+ git_repository *failed_repo;
+
+ if (!_remote_speed_timesout)
+ cl_skip();
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SERVER_TIMEOUT, 1000));
+
+ cl_git_fail_with(GIT_TIMEOUT, git_clone(&failed_repo, _remote_speed_timesout, "./timedout", NULL));
+ cl_assert(git_error_last() && strstr(git_error_last()->message, "timed out"));
+#endif
+}
+
+void test_online_clone__timeout_configurable_succeeds_slowly(void)
+{
+#ifdef GIT_WINHTTP
+ cl_skip();
+#else
+ if (!_remote_speed_slow)
+ cl_skip();
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SERVER_TIMEOUT, 1000));
+
+ cl_git_pass(git_clone(&g_repo, _remote_speed_slow, "./slow-but-successful", NULL));
+#endif
+}
diff --git a/tests/libgit2/online/customcert.c b/tests/libgit2/online/customcert.c
new file mode 100644
index 0000000..7932a9e
--- /dev/null
+++ b/tests/libgit2/online/customcert.c
@@ -0,0 +1,79 @@
+#include "clar_libgit2.h"
+
+#include "path.h"
+#include "git2/clone.h"
+#include "git2/cred_helpers.h"
+#include "remote.h"
+#include "futils.h"
+#include "refs.h"
+
+/*
+ * Certificate one is in the `certs` folder; certificate two is in the
+ * `self-signed.pem` file.
+ */
+#define CUSTOM_CERT_ONE_URL "https://test.libgit2.org:1443/anonymous/test.git"
+#define CUSTOM_CERT_ONE_PATH "certs"
+
+#define CUSTOM_CERT_TWO_URL "https://test.libgit2.org:2443/anonymous/test.git"
+#define CUSTOM_CERT_TWO_FILE "self-signed.pem"
+
+#if (GIT_OPENSSL || GIT_MBEDTLS)
+static git_repository *g_repo;
+static int initialized = false;
+#endif
+
+void test_online_customcert__initialize(void)
+{
+#if (GIT_OPENSSL || GIT_MBEDTLS)
+ g_repo = NULL;
+
+ if (!initialized) {
+ git_str path = GIT_STR_INIT, file = GIT_STR_INIT;
+ char cwd[GIT_PATH_MAX];
+
+ cl_fixture_sandbox(CUSTOM_CERT_ONE_PATH);
+ cl_fixture_sandbox(CUSTOM_CERT_TWO_FILE);
+
+ cl_must_pass(p_getcwd(cwd, GIT_PATH_MAX));
+ cl_git_pass(git_str_joinpath(&path, cwd, CUSTOM_CERT_ONE_PATH));
+ cl_git_pass(git_str_joinpath(&file, cwd, CUSTOM_CERT_TWO_FILE));
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SSL_CERT_LOCATIONS,
+ file.ptr, path.ptr));
+ initialized = true;
+
+ git_str_dispose(&file);
+ git_str_dispose(&path);
+ }
+#endif
+}
+
+void test_online_customcert__cleanup(void)
+{
+#if (GIT_OPENSSL || GIT_MBEDTLS)
+ if (g_repo) {
+ git_repository_free(g_repo);
+ g_repo = NULL;
+ }
+
+ cl_fixture_cleanup("./cloned");
+ cl_fixture_cleanup(CUSTOM_CERT_ONE_PATH);
+ cl_fixture_cleanup(CUSTOM_CERT_TWO_FILE);
+#endif
+}
+
+void test_online_customcert__file(void)
+{
+#if (GIT_OPENSSL || GIT_MBEDTLS)
+ cl_git_pass(git_clone(&g_repo, CUSTOM_CERT_ONE_URL, "./cloned", NULL));
+ cl_assert(git_fs_path_exists("./cloned/master.txt"));
+#endif
+}
+
+void test_online_customcert__path(void)
+{
+#if (GIT_OPENSSL || GIT_MBEDTLS)
+ cl_git_pass(git_clone(&g_repo, CUSTOM_CERT_TWO_URL, "./cloned", NULL));
+ cl_assert(git_fs_path_exists("./cloned/master.txt"));
+#endif
+}
diff --git a/tests/libgit2/online/fetch.c b/tests/libgit2/online/fetch.c
new file mode 100644
index 0000000..a557bbf
--- /dev/null
+++ b/tests/libgit2/online/fetch.c
@@ -0,0 +1,352 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+
+static git_repository *_repo;
+static int counter;
+
+static char *_remote_proxy_scheme = NULL;
+static char *_remote_proxy_host = NULL;
+static char *_remote_proxy_user = NULL;
+static char *_remote_proxy_pass = NULL;
+static char *_remote_redirect_initial = NULL;
+static char *_remote_redirect_subsequent = NULL;
+
+void test_online_fetch__initialize(void)
+{
+ cl_git_pass(git_repository_init(&_repo, "./fetch", 0));
+
+ _remote_proxy_scheme = cl_getenv("GITTEST_REMOTE_PROXY_SCHEME");
+ _remote_proxy_host = cl_getenv("GITTEST_REMOTE_PROXY_HOST");
+ _remote_proxy_user = cl_getenv("GITTEST_REMOTE_PROXY_USER");
+ _remote_proxy_pass = cl_getenv("GITTEST_REMOTE_PROXY_PASS");
+ _remote_redirect_initial = cl_getenv("GITTEST_REMOTE_REDIRECT_INITIAL");
+ _remote_redirect_subsequent = cl_getenv("GITTEST_REMOTE_REDIRECT_SUBSEQUENT");
+}
+
+void test_online_fetch__cleanup(void)
+{
+ git_repository_free(_repo);
+ _repo = NULL;
+
+ cl_fixture_cleanup("./fetch");
+ cl_fixture_cleanup("./redirected");
+
+ git__free(_remote_proxy_scheme);
+ git__free(_remote_proxy_host);
+ git__free(_remote_proxy_user);
+ git__free(_remote_proxy_pass);
+ git__free(_remote_redirect_initial);
+ git__free(_remote_redirect_subsequent);
+}
+
+static int update_tips(const char *refname, const git_oid *a, const git_oid *b, void *data)
+{
+ GIT_UNUSED(refname); GIT_UNUSED(a); GIT_UNUSED(b); GIT_UNUSED(data);
+
+ ++counter;
+
+ return 0;
+}
+
+static int progress(const git_indexer_progress *stats, void *payload)
+{
+ size_t *bytes_received = (size_t *)payload;
+ *bytes_received = stats->received_bytes;
+ return 0;
+}
+
+static void do_fetch(const char *url, git_remote_autotag_option_t flag, int n)
+{
+ git_remote *remote;
+ git_fetch_options options = GIT_FETCH_OPTIONS_INIT;
+ size_t bytes_received = 0;
+
+ options.callbacks.transfer_progress = progress;
+ options.callbacks.update_tips = update_tips;
+ options.callbacks.payload = &bytes_received;
+ options.download_tags = flag;
+ counter = 0;
+
+ cl_git_pass(git_remote_create(&remote, _repo, "test", url));
+ cl_git_pass(git_remote_fetch(remote, NULL, &options, NULL));
+ cl_assert_equal_i(counter, n);
+ cl_assert(bytes_received > 0);
+
+ git_remote_free(remote);
+}
+
+void test_online_fetch__default_http(void)
+{
+ do_fetch("http://github.com/libgit2/TestGitRepository.git", GIT_REMOTE_DOWNLOAD_TAGS_AUTO, 6);
+}
+
+void test_online_fetch__default_https(void)
+{
+ do_fetch("https://github.com/libgit2/TestGitRepository.git", GIT_REMOTE_DOWNLOAD_TAGS_AUTO, 6);
+}
+
+void test_online_fetch__no_tags_git(void)
+{
+ do_fetch("https://github.com/libgit2/TestGitRepository.git", GIT_REMOTE_DOWNLOAD_TAGS_NONE, 3);
+}
+
+void test_online_fetch__no_tags_http(void)
+{
+ do_fetch("http://github.com/libgit2/TestGitRepository.git", GIT_REMOTE_DOWNLOAD_TAGS_NONE, 3);
+}
+
+void test_online_fetch__fetch_twice(void)
+{
+ git_remote *remote;
+ cl_git_pass(git_remote_create(&remote, _repo, "test", "https://github.com/libgit2/TestGitRepository.git"));
+ cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL));
+ cl_git_pass(git_remote_download(remote, NULL, NULL));
+ git_remote_disconnect(remote);
+
+ git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL);
+ cl_git_pass(git_remote_download(remote, NULL, NULL));
+ git_remote_disconnect(remote);
+
+ git_remote_free(remote);
+}
+
+static int transferProgressCallback(const git_indexer_progress *stats, void *payload)
+{
+ bool *invoked = (bool *)payload;
+
+ GIT_UNUSED(stats);
+ *invoked = true;
+ return 0;
+}
+
+void test_online_fetch__doesnt_retrieve_a_pack_when_the_repository_is_up_to_date(void)
+{
+ git_repository *_repository;
+ bool invoked = false;
+ git_remote *remote;
+ git_fetch_options options = GIT_FETCH_OPTIONS_INIT;
+ git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
+ opts.bare = true;
+
+ cl_git_pass(git_clone(&_repository, "https://github.com/libgit2/TestGitRepository.git",
+ "./fetch/lg2", &opts));
+ git_repository_free(_repository);
+
+ cl_git_pass(git_repository_open(&_repository, "./fetch/lg2"));
+
+ cl_git_pass(git_remote_lookup(&remote, _repository, "origin"));
+ cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL));
+
+ cl_assert_equal_i(false, invoked);
+
+ options.callbacks.transfer_progress = &transferProgressCallback;
+ options.callbacks.payload = &invoked;
+ cl_git_pass(git_remote_download(remote, NULL, &options));
+
+ cl_assert_equal_i(false, invoked);
+
+ cl_git_pass(git_remote_update_tips(remote, &options.callbacks, 1, options.download_tags, NULL));
+ git_remote_disconnect(remote);
+
+ git_remote_free(remote);
+ git_repository_free(_repository);
+}
+
+static int cancel_at_half(const git_indexer_progress *stats, void *payload)
+{
+ GIT_UNUSED(payload);
+
+ if (stats->received_objects > (stats->total_objects/2))
+ return -4321;
+ return 0;
+}
+
+void test_online_fetch__can_cancel(void)
+{
+ git_remote *remote;
+ size_t bytes_received = 0;
+ git_fetch_options options = GIT_FETCH_OPTIONS_INIT;
+
+ cl_git_pass(git_remote_create(&remote, _repo, "test",
+ "http://github.com/libgit2/TestGitRepository.git"));
+
+ options.callbacks.transfer_progress = cancel_at_half;
+ options.callbacks.payload = &bytes_received;
+
+ cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL));
+ cl_git_fail_with(git_remote_download(remote, NULL, &options), -4321);
+ git_remote_disconnect(remote);
+ git_remote_free(remote);
+}
+
+void test_online_fetch__ls_disconnected(void)
+{
+ const git_remote_head **refs;
+ size_t refs_len_before, refs_len_after;
+ git_remote *remote;
+
+ cl_git_pass(git_remote_create(&remote, _repo, "test",
+ "http://github.com/libgit2/TestGitRepository.git"));
+ cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL));
+ cl_git_pass(git_remote_ls(&refs, &refs_len_before, remote));
+ git_remote_disconnect(remote);
+ cl_git_pass(git_remote_ls(&refs, &refs_len_after, remote));
+
+ cl_assert_equal_i(refs_len_before, refs_len_after);
+
+ git_remote_free(remote);
+}
+
+void test_online_fetch__remote_symrefs(void)
+{
+ const git_remote_head **refs;
+ size_t refs_len;
+ git_remote *remote;
+
+ cl_git_pass(git_remote_create(&remote, _repo, "test",
+ "http://github.com/libgit2/TestGitRepository.git"));
+ cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL));
+ git_remote_disconnect(remote);
+ cl_git_pass(git_remote_ls(&refs, &refs_len, remote));
+
+ cl_assert_equal_s("HEAD", refs[0]->name);
+ cl_assert_equal_s("refs/heads/master", refs[0]->symref_target);
+
+ git_remote_free(remote);
+}
+
+void test_online_fetch__twice(void)
+{
+ git_remote *remote;
+
+ cl_git_pass(git_remote_create(&remote, _repo, "test", "http://github.com/libgit2/TestGitRepository.git"));
+ cl_git_pass(git_remote_fetch(remote, NULL, NULL, NULL));
+ cl_git_pass(git_remote_fetch(remote, NULL, NULL, NULL));
+
+ git_remote_free(remote);
+}
+
+void test_online_fetch__proxy(void)
+{
+ git_remote *remote;
+ git_str url = GIT_STR_INIT;
+ git_fetch_options fetch_opts;
+
+ if (!_remote_proxy_host || !_remote_proxy_user || !_remote_proxy_pass)
+ cl_skip();
+
+ cl_git_pass(git_str_printf(&url, "%s://%s:%s@%s/",
+ _remote_proxy_scheme ? _remote_proxy_scheme : "http",
+ _remote_proxy_user, _remote_proxy_pass, _remote_proxy_host));
+
+ cl_git_pass(git_fetch_options_init(&fetch_opts, GIT_FETCH_OPTIONS_VERSION));
+ fetch_opts.proxy_opts.type = GIT_PROXY_SPECIFIED;
+ fetch_opts.proxy_opts.url = url.ptr;
+
+ cl_git_pass(git_remote_create(&remote, _repo, "test", "https://github.com/libgit2/TestGitRepository.git"));
+ cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, &fetch_opts.proxy_opts, NULL));
+ cl_git_pass(git_remote_fetch(remote, NULL, &fetch_opts, NULL));
+
+ git_remote_free(remote);
+ git_str_dispose(&url);
+}
+
+static int do_redirected_fetch(const char *url, const char *name, const char *config)
+{
+ git_repository *repo;
+ git_remote *remote;
+ int error;
+
+ cl_git_pass(git_repository_init(&repo, "./redirected", 0));
+ cl_fixture_cleanup(name);
+
+ if (config)
+ cl_repo_set_string(repo, "http.followRedirects", config);
+
+ cl_git_pass(git_remote_create(&remote, repo, name, url));
+ error = git_remote_fetch(remote, NULL, NULL, NULL);
+
+ git_remote_free(remote);
+ git_repository_free(repo);
+
+ cl_fixture_cleanup("./redirected");
+
+ return error;
+}
+
+void test_online_fetch__redirect_config(void)
+{
+ if (!_remote_redirect_initial || !_remote_redirect_subsequent)
+ cl_skip();
+
+ /* config defaults */
+ cl_git_pass(do_redirected_fetch(_remote_redirect_initial, "initial", NULL));
+ cl_git_fail(do_redirected_fetch(_remote_redirect_subsequent, "subsequent", NULL));
+
+ /* redirect=initial */
+ cl_git_pass(do_redirected_fetch(_remote_redirect_initial, "initial", "initial"));
+ cl_git_fail(do_redirected_fetch(_remote_redirect_subsequent, "subsequent", "initial"));
+
+ /* redirect=false */
+ cl_git_fail(do_redirected_fetch(_remote_redirect_initial, "initial", "false"));
+ cl_git_fail(do_redirected_fetch(_remote_redirect_subsequent, "subsequent", "false"));
+}
+
+void test_online_fetch__reachable_commit(void)
+{
+ git_remote *remote;
+ git_strarray refspecs;
+ git_object *obj;
+ git_oid expected_id;
+ git_str fetchhead = GIT_STR_INIT;
+ char *refspec = "+2c349335b7f797072cf729c4f3bb0914ecb6dec9:refs/success";
+
+ refspecs.strings = &refspec;
+ refspecs.count = 1;
+
+ git_oid__fromstr(&expected_id, "2c349335b7f797072cf729c4f3bb0914ecb6dec9", GIT_OID_SHA1);
+
+ cl_git_pass(git_remote_create(&remote, _repo, "test",
+ "https://github.com/libgit2/TestGitRepository"));
+ cl_git_pass(git_remote_fetch(remote, &refspecs, NULL, NULL));
+
+ cl_git_pass(git_revparse_single(&obj, _repo, "refs/success"));
+ cl_assert_equal_oid(&expected_id, git_object_id(obj));
+
+ cl_git_pass(git_futils_readbuffer(&fetchhead, "./fetch/.git/FETCH_HEAD"));
+ cl_assert_equal_s(fetchhead.ptr,
+ "2c349335b7f797072cf729c4f3bb0914ecb6dec9\t\t'2c349335b7f797072cf729c4f3bb0914ecb6dec9' of https://github.com/libgit2/TestGitRepository\n");
+
+ git_str_dispose(&fetchhead);
+ git_object_free(obj);
+ git_remote_free(remote);
+}
+
+void test_online_fetch__reachable_commit_without_destination(void)
+{
+ git_remote *remote;
+ git_strarray refspecs;
+ git_object *obj;
+ git_oid expected_id;
+ git_str fetchhead = GIT_STR_INIT;
+ char *refspec = "2c349335b7f797072cf729c4f3bb0914ecb6dec9";
+
+ refspecs.strings = &refspec;
+ refspecs.count = 1;
+
+ git_oid__fromstr(&expected_id, "2c349335b7f797072cf729c4f3bb0914ecb6dec9", GIT_OID_SHA1);
+
+ cl_git_pass(git_remote_create(&remote, _repo, "test",
+ "https://github.com/libgit2/TestGitRepository"));
+ cl_git_pass(git_remote_fetch(remote, &refspecs, NULL, NULL));
+
+ cl_git_fail_with(GIT_ENOTFOUND, git_revparse_single(&obj, _repo, "refs/success"));
+
+ cl_git_pass(git_futils_readbuffer(&fetchhead, "./fetch/.git/FETCH_HEAD"));
+ cl_assert_equal_s(fetchhead.ptr,
+ "2c349335b7f797072cf729c4f3bb0914ecb6dec9\t\t'2c349335b7f797072cf729c4f3bb0914ecb6dec9' of https://github.com/libgit2/TestGitRepository\n");
+
+ git_str_dispose(&fetchhead);
+ git_object_free(obj);
+ git_remote_free(remote);
+}
diff --git a/tests/libgit2/online/fetchhead.c b/tests/libgit2/online/fetchhead.c
new file mode 100644
index 0000000..1b66c52
--- /dev/null
+++ b/tests/libgit2/online/fetchhead.c
@@ -0,0 +1,169 @@
+#include "clar_libgit2.h"
+
+#include "futils.h"
+#include "fetchhead.h"
+#include "../fetchhead/fetchhead_data.h"
+#include "git2/clone.h"
+
+#define LIVE_REPO_URL "https://github.com/libgit2/TestGitRepository"
+
+static git_repository *g_repo;
+static git_clone_options g_options;
+
+void test_online_fetchhead__initialize(void)
+{
+ git_fetch_options dummy_fetch = GIT_FETCH_OPTIONS_INIT;
+ g_repo = NULL;
+
+ memset(&g_options, 0, sizeof(git_clone_options));
+ g_options.version = GIT_CLONE_OPTIONS_VERSION;
+ g_options.fetch_opts = dummy_fetch;
+}
+
+void test_online_fetchhead__cleanup(void)
+{
+ if (g_repo) {
+ git_repository_free(g_repo);
+ g_repo = NULL;
+ }
+
+ cl_fixture_cleanup("./foo");
+}
+
+static void fetchhead_test_clone(void)
+{
+ cl_git_pass(git_clone(&g_repo, LIVE_REPO_URL, "./foo", &g_options));
+}
+
+static size_t count_references(void)
+{
+ git_strarray array;
+ size_t refs;
+
+ cl_git_pass(git_reference_list(&array, g_repo));
+ refs = array.count;
+
+ git_strarray_dispose(&array);
+
+ return refs;
+}
+
+static void fetchhead_test_fetch(const char *fetchspec, const char *expected_fetchhead)
+{
+ git_remote *remote;
+ git_fetch_options fetch_opts = GIT_FETCH_OPTIONS_INIT;
+ git_str fetchhead_buf = GIT_STR_INIT;
+ git_strarray array, *active_refs = NULL;
+
+ cl_git_pass(git_remote_lookup(&remote, g_repo, "origin"));
+ fetch_opts.download_tags = GIT_REMOTE_DOWNLOAD_TAGS_AUTO;
+
+ if(fetchspec != NULL) {
+ array.count = 1;
+ array.strings = (char **) &fetchspec;
+ active_refs = &array;
+ }
+
+ cl_git_pass(git_remote_fetch(remote, active_refs, &fetch_opts, NULL));
+ git_remote_free(remote);
+
+ cl_git_pass(git_futils_readbuffer(&fetchhead_buf, "./foo/.git/FETCH_HEAD"));
+
+ cl_assert_equal_s(fetchhead_buf.ptr, expected_fetchhead);
+ git_str_dispose(&fetchhead_buf);
+}
+
+void test_online_fetchhead__wildcard_spec(void)
+{
+ fetchhead_test_clone();
+ fetchhead_test_fetch(NULL, FETCH_HEAD_WILDCARD_DATA2);
+ cl_git_pass(git_tag_delete(g_repo, "annotated_tag"));
+ cl_git_pass(git_tag_delete(g_repo, "blob"));
+ cl_git_pass(git_tag_delete(g_repo, "commit_tree"));
+ cl_git_pass(git_tag_delete(g_repo, "nearly-dangling"));
+ fetchhead_test_fetch(NULL, FETCH_HEAD_WILDCARD_DATA);
+}
+
+void test_online_fetchhead__explicit_spec(void)
+{
+ fetchhead_test_clone();
+ fetchhead_test_fetch("refs/heads/first-merge:refs/remotes/origin/first-merge", FETCH_HEAD_EXPLICIT_DATA);
+}
+
+void test_online_fetchhead__no_merges(void)
+{
+ git_config *config;
+
+ fetchhead_test_clone();
+
+ cl_git_pass(git_repository_config(&config, g_repo));
+ cl_git_pass(git_config_delete_entry(config, "branch.master.remote"));
+ cl_git_pass(git_config_delete_entry(config, "branch.master.merge"));
+ git_config_free(config);
+
+ fetchhead_test_fetch(NULL, FETCH_HEAD_NO_MERGE_DATA2);
+ cl_git_pass(git_tag_delete(g_repo, "annotated_tag"));
+ cl_git_pass(git_tag_delete(g_repo, "blob"));
+ cl_git_pass(git_tag_delete(g_repo, "commit_tree"));
+ cl_git_pass(git_tag_delete(g_repo, "nearly-dangling"));
+ fetchhead_test_fetch(NULL, FETCH_HEAD_NO_MERGE_DATA);
+ cl_git_pass(git_tag_delete(g_repo, "commit_tree"));
+ fetchhead_test_fetch(NULL, FETCH_HEAD_NO_MERGE_DATA3);
+}
+
+void test_online_fetchhead__explicit_dst_refspec_creates_branch(void)
+{
+ git_reference *ref;
+ size_t refs;
+
+ fetchhead_test_clone();
+ refs = count_references();
+ fetchhead_test_fetch("refs/heads/first-merge:refs/heads/explicit-refspec", FETCH_HEAD_EXPLICIT_DATA);
+
+ cl_git_pass(git_branch_lookup(&ref, g_repo, "explicit-refspec", GIT_BRANCH_ALL));
+ cl_assert_equal_i(refs + 1, count_references());
+
+ git_reference_free(ref);
+}
+
+void test_online_fetchhead__empty_dst_refspec_creates_no_branch(void)
+{
+ git_reference *ref;
+ size_t refs;
+
+ fetchhead_test_clone();
+ refs = count_references();
+
+ fetchhead_test_fetch("refs/heads/first-merge", FETCH_HEAD_EXPLICIT_DATA);
+ cl_git_fail(git_branch_lookup(&ref, g_repo, "first-merge", GIT_BRANCH_ALL));
+
+ cl_assert_equal_i(refs, count_references());
+}
+
+void test_online_fetchhead__colon_only_dst_refspec_creates_no_branch(void)
+{
+ size_t refs;
+
+ fetchhead_test_clone();
+ refs = count_references();
+ fetchhead_test_fetch("refs/heads/first-merge:", FETCH_HEAD_EXPLICIT_DATA);
+
+ cl_assert_equal_i(refs, count_references());
+}
+
+void test_online_fetchhead__creds_get_stripped(void)
+{
+ git_str buf = GIT_STR_INIT;
+ git_remote *remote;
+
+ cl_git_pass(git_repository_init(&g_repo, "./foo", 0));
+ cl_git_pass(git_remote_create_anonymous(&remote, g_repo, "https://foo:bar@github.com/libgit2/TestGitRepository"));
+ cl_git_pass(git_remote_fetch(remote, NULL, NULL, NULL));
+
+ cl_git_pass(git_futils_readbuffer(&buf, "./foo/.git/FETCH_HEAD"));
+ cl_assert_equal_s(buf.ptr,
+ "49322bb17d3acc9146f98c97d078513228bbf3c0\t\thttps://github.com/libgit2/TestGitRepository\n");
+
+ git_remote_free(remote);
+ git_str_dispose(&buf);
+}
diff --git a/tests/libgit2/online/push.c b/tests/libgit2/online/push.c
new file mode 100644
index 0000000..204572c
--- /dev/null
+++ b/tests/libgit2/online/push.c
@@ -0,0 +1,932 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "vector.h"
+#include "../submodule/submodule_helpers.h"
+#include "push_util.h"
+#include "refspec.h"
+#include "remote.h"
+
+static git_repository *_repo;
+
+static char *_remote_url = NULL;
+
+static char *_remote_user = NULL;
+static char *_remote_pass = NULL;
+
+static char *_remote_ssh_key = NULL;
+static char *_remote_ssh_pubkey = NULL;
+static char *_remote_ssh_passphrase = NULL;
+
+static char *_remote_default = NULL;
+static char *_remote_expectcontinue = NULL;
+
+static int cred_acquire_cb(git_credential **, const char *, const char *, unsigned int, void *);
+
+static git_remote *_remote;
+static record_callbacks_data _record_cbs_data = {{ 0 }};
+static git_remote_callbacks _record_cbs = RECORD_CALLBACKS_INIT(&_record_cbs_data);
+
+static git_oid _oid_b6;
+static git_oid _oid_b5;
+static git_oid _oid_b4;
+static git_oid _oid_b3;
+static git_oid _oid_b2;
+static git_oid _oid_b1;
+
+static git_oid _tag_commit;
+static git_oid _tag_tree;
+static git_oid _tag_blob;
+static git_oid _tag_lightweight;
+static git_oid _tag_tag;
+
+static int cred_acquire_cb(
+ git_credential **cred,
+ const char *url,
+ const char *user_from_url,
+ unsigned int allowed_types,
+ void *payload)
+{
+ GIT_UNUSED(url);
+ GIT_UNUSED(user_from_url);
+ GIT_UNUSED(payload);
+
+ if (GIT_CREDENTIAL_USERNAME & allowed_types) {
+ if (!_remote_user) {
+ printf("GITTEST_REMOTE_USER must be set\n");
+ return -1;
+ }
+
+ return git_credential_username_new(cred, _remote_user);
+ }
+
+ if (GIT_CREDENTIAL_DEFAULT & allowed_types) {
+ if (!_remote_default) {
+ printf("GITTEST_REMOTE_DEFAULT must be set to use NTLM/Negotiate credentials\n");
+ return -1;
+ }
+
+ return git_credential_default_new(cred);
+ }
+
+ if (GIT_CREDENTIAL_SSH_KEY & allowed_types) {
+ if (!_remote_user || !_remote_ssh_pubkey || !_remote_ssh_key || !_remote_ssh_passphrase) {
+ printf("GITTEST_REMOTE_USER, GITTEST_REMOTE_SSH_PUBKEY, GITTEST_REMOTE_SSH_KEY and GITTEST_REMOTE_SSH_PASSPHRASE must be set\n");
+ return -1;
+ }
+
+ return git_credential_ssh_key_new(cred, _remote_user, _remote_ssh_pubkey, _remote_ssh_key, _remote_ssh_passphrase);
+ }
+
+ if (GIT_CREDENTIAL_USERPASS_PLAINTEXT & allowed_types) {
+ if (!_remote_user || !_remote_pass) {
+ printf("GITTEST_REMOTE_USER and GITTEST_REMOTE_PASS must be set\n");
+ return -1;
+ }
+
+ return git_credential_userpass_plaintext_new(cred, _remote_user, _remote_pass);
+ }
+
+ return -1;
+}
+
+/**
+ * git_push_status_foreach callback that records status entries.
+ */
+static int record_push_status_cb(const char *ref, const char *msg, void *payload)
+{
+ record_callbacks_data *data = (record_callbacks_data *) payload;
+ push_status *s;
+
+ cl_assert(s = git__calloc(1, sizeof(*s)));
+ if (ref)
+ cl_assert(s->ref = git__strdup(ref));
+ s->success = (msg == NULL);
+ if (msg)
+ cl_assert(s->msg = git__strdup(msg));
+
+ git_vector_insert(&data->statuses, s);
+
+ return 0;
+}
+
+static void do_verify_push_status(record_callbacks_data *data, const push_status expected[], const size_t expected_len)
+{
+ git_vector *actual = &data->statuses;
+ push_status *iter;
+ bool failed = false;
+ size_t i;
+
+ if (expected_len != actual->length)
+ failed = true;
+ else
+ git_vector_foreach(actual, i, iter)
+ if (strcmp(expected[i].ref, iter->ref) ||
+ (expected[i].success != iter->success) ||
+ (expected[i].msg && (!iter->msg || strcmp(expected[i].msg, iter->msg)))) {
+ failed = true;
+ break;
+ }
+
+ if (failed) {
+ git_str msg = GIT_STR_INIT;
+
+ git_str_puts(&msg, "Expected and actual push statuses differ:\nEXPECTED:\n");
+
+ for(i = 0; i < expected_len; i++) {
+ git_str_printf(&msg, "%s: %s\n",
+ expected[i].ref,
+ expected[i].success ? "success" : "failed");
+ }
+
+ git_str_puts(&msg, "\nACTUAL:\n");
+
+ git_vector_foreach(actual, i, iter) {
+ if (iter->success)
+ git_str_printf(&msg, "%s: success\n", iter->ref);
+ else
+ git_str_printf(&msg, "%s: failed with message: %s", iter->ref, iter->msg);
+ }
+
+ cl_fail(git_str_cstr(&msg));
+
+ git_str_dispose(&msg);
+ }
+
+ git_vector_foreach(actual, i, iter) {
+ push_status *s = (push_status *)iter;
+ git__free(s->ref);
+ git__free(s->msg);
+ git__free(s);
+ }
+
+ git_vector_free(actual);
+}
+
+/**
+ * Verifies that after git_push_finish(), refs on a remote have the expected
+ * names, oids, and order.
+ *
+ * @param remote remote to verify
+ * @param expected_refs expected remote refs after push
+ * @param expected_refs_len length of expected_refs
+ */
+static void verify_refs(git_remote *remote, expected_ref expected_refs[], size_t expected_refs_len)
+{
+ const git_remote_head **actual_refs;
+ size_t actual_refs_len;
+
+ git_remote_ls(&actual_refs, &actual_refs_len, remote);
+ verify_remote_refs(actual_refs, actual_refs_len, expected_refs, expected_refs_len);
+}
+
+/**
+ * Verifies that after git_push_update_tips(), remote tracking branches have the expected
+ * names and oids.
+ *
+ * @param remote remote to verify
+ * @param expected_refs expected remote refs after push
+ * @param expected_refs_len length of expected_refs
+ */
+static void verify_tracking_branches(git_remote *remote, expected_ref expected_refs[], size_t expected_refs_len)
+{
+ git_refspec *fetch_spec;
+ size_t i, j;
+ git_str msg = GIT_STR_INIT;
+ git_buf ref_name = GIT_BUF_INIT;
+ git_vector actual_refs = GIT_VECTOR_INIT;
+ git_branch_iterator *iter;
+ char *actual_ref;
+ git_oid oid;
+ int failed = 0, error;
+ git_branch_t branch_type;
+ git_reference *ref;
+
+ /* Get current remote-tracking branches */
+ cl_git_pass(git_branch_iterator_new(&iter, remote->repo, GIT_BRANCH_REMOTE));
+
+ while ((error = git_branch_next(&ref, &branch_type, iter)) == 0) {
+ cl_assert_equal_i(branch_type, GIT_BRANCH_REMOTE);
+
+ cl_git_pass(git_vector_insert(&actual_refs, git__strdup(git_reference_name(ref))));
+
+ git_reference_free(ref);
+ }
+
+ cl_assert_equal_i(error, GIT_ITEROVER);
+ git_branch_iterator_free(iter);
+
+ /* Loop through expected refs, make sure they exist */
+ for (i = 0; i < expected_refs_len; i++) {
+
+ /* Convert remote reference name into remote-tracking branch name.
+ * If the spec is not under refs/heads/, then skip.
+ */
+ fetch_spec = git_remote__matching_refspec(remote, expected_refs[i].name);
+ if (!fetch_spec)
+ continue;
+
+ cl_git_pass(git_refspec_transform(&ref_name, fetch_spec, expected_refs[i].name));
+
+ /* Find matching remote branch */
+ git_vector_foreach(&actual_refs, j, actual_ref) {
+ if (!strcmp(ref_name.ptr, actual_ref))
+ break;
+ }
+
+ if (j == actual_refs.length) {
+ git_str_printf(&msg, "Did not find expected tracking branch '%s'.", ref_name.ptr);
+ failed = 1;
+ goto failed;
+ }
+
+ /* Make sure tracking branch is at expected commit ID */
+ cl_git_pass(git_reference_name_to_id(&oid, remote->repo, actual_ref));
+
+ if (git_oid_cmp(expected_refs[i].oid, &oid) != 0) {
+ git_str_puts(&msg, "Tracking branch commit does not match expected ID.");
+ failed = 1;
+ goto failed;
+ }
+
+ git__free(actual_ref);
+ cl_git_pass(git_vector_remove(&actual_refs, j));
+ }
+
+ /* Make sure there are no extra branches */
+ if (actual_refs.length > 0) {
+ git_str_puts(&msg, "Unexpected remote tracking branches exist.");
+ failed = 1;
+ goto failed;
+ }
+
+failed:
+ if (failed)
+ cl_fail(git_str_cstr(&msg));
+
+ git_vector_foreach(&actual_refs, i, actual_ref)
+ git__free(actual_ref);
+
+ git_vector_free(&actual_refs);
+ git_str_dispose(&msg);
+ git_buf_dispose(&ref_name);
+}
+
+static void verify_update_tips_callback(git_remote *remote, expected_ref expected_refs[], size_t expected_refs_len)
+{
+ git_refspec *fetch_spec;
+ git_str msg = GIT_STR_INIT;
+ git_buf ref_name = GIT_BUF_INIT;
+ updated_tip *tip = NULL;
+ size_t i, j;
+ int failed = 0;
+
+ for (i = 0; i < expected_refs_len; ++i) {
+ /* Convert remote reference name into tracking branch name.
+ * If the spec is not under refs/heads/, then skip.
+ */
+ fetch_spec = git_remote__matching_refspec(remote, expected_refs[i].name);
+ if (!fetch_spec)
+ continue;
+
+ cl_git_pass(git_refspec_transform(&ref_name, fetch_spec, expected_refs[i].name));
+
+ /* Find matching update_tip entry */
+ git_vector_foreach(&_record_cbs_data.updated_tips, j, tip) {
+ if (!strcmp(ref_name.ptr, tip->name))
+ break;
+ }
+
+ if (j == _record_cbs_data.updated_tips.length) {
+ git_str_printf(&msg, "Did not find expected updated tip entry for branch '%s'.", ref_name.ptr);
+ failed = 1;
+ goto failed;
+ }
+
+ if (git_oid_cmp(expected_refs[i].oid, &tip->new_oid) != 0) {
+ git_str_printf(&msg, "Updated tip ID does not match expected ID");
+ failed = 1;
+ goto failed;
+ }
+ }
+
+failed:
+ if (failed)
+ cl_fail(git_str_cstr(&msg));
+
+ git_buf_dispose(&ref_name);
+ git_str_dispose(&msg);
+}
+
+void test_online_push__initialize(void)
+{
+ git_vector delete_specs = GIT_VECTOR_INIT;
+ const git_remote_head **heads;
+ size_t heads_len;
+ git_push_options push_opts = GIT_PUSH_OPTIONS_INIT;
+ git_fetch_options fetch_opts = GIT_FETCH_OPTIONS_INIT;
+
+ _repo = cl_git_sandbox_init("push_src");
+
+ cl_git_pass(git_repository_set_ident(_repo, "Random J. Hacker", "foo@example.com"));
+ cl_fixture_sandbox("testrepo.git");
+ cl_rename("push_src/submodule/.gitted", "push_src/submodule/.git");
+
+ rewrite_gitmodules(git_repository_workdir(_repo));
+
+ /* git log --format=oneline --decorate --graph
+ * *-. 951bbbb90e2259a4c8950db78946784fb53fcbce (HEAD, b6) merge b3, b4, and b5 to b6
+ * |\ \
+ * | | * fa38b91f199934685819bea316186d8b008c52a2 (b5) added submodule named 'submodule' pointing to '../testrepo.git'
+ * | * | 27b7ce66243eb1403862d05f958c002312df173d (b4) edited fold\b.txt
+ * | |/
+ * * | d9b63a88223d8367516f50bd131a5f7349b7f3e4 (b3) edited a.txt
+ * |/
+ * * a78705c3b2725f931d3ee05348d83cc26700f247 (b2, b1) added fold and fold/b.txt
+ * * 5c0bb3d1b9449d1cc69d7519fd05166f01840915 added a.txt
+ */
+ git_oid__fromstr(&_oid_b6, "951bbbb90e2259a4c8950db78946784fb53fcbce", GIT_OID_SHA1);
+ git_oid__fromstr(&_oid_b5, "fa38b91f199934685819bea316186d8b008c52a2", GIT_OID_SHA1);
+ git_oid__fromstr(&_oid_b4, "27b7ce66243eb1403862d05f958c002312df173d", GIT_OID_SHA1);
+ git_oid__fromstr(&_oid_b3, "d9b63a88223d8367516f50bd131a5f7349b7f3e4", GIT_OID_SHA1);
+ git_oid__fromstr(&_oid_b2, "a78705c3b2725f931d3ee05348d83cc26700f247", GIT_OID_SHA1);
+ git_oid__fromstr(&_oid_b1, "a78705c3b2725f931d3ee05348d83cc26700f247", GIT_OID_SHA1);
+
+ git_oid__fromstr(&_tag_commit, "805c54522e614f29f70d2413a0470247d8b424ac", GIT_OID_SHA1);
+ git_oid__fromstr(&_tag_tree, "ff83aa4c5e5d28e3bcba2f5c6e2adc61286a4e5e", GIT_OID_SHA1);
+ git_oid__fromstr(&_tag_blob, "b483ae7ba66decee9aee971f501221dea84b1498", GIT_OID_SHA1);
+ git_oid__fromstr(&_tag_lightweight, "951bbbb90e2259a4c8950db78946784fb53fcbce", GIT_OID_SHA1);
+ git_oid__fromstr(&_tag_tag, "eea4f2705eeec2db3813f2430829afce99cd00b5", GIT_OID_SHA1);
+
+ /* Remote URL environment variable must be set. User and password are optional. */
+
+ _remote_url = cl_getenv("GITTEST_REMOTE_URL");
+ _remote_user = cl_getenv("GITTEST_REMOTE_USER");
+ _remote_pass = cl_getenv("GITTEST_REMOTE_PASS");
+ _remote_ssh_key = cl_getenv("GITTEST_REMOTE_SSH_KEY");
+ _remote_ssh_pubkey = cl_getenv("GITTEST_REMOTE_SSH_PUBKEY");
+ _remote_ssh_passphrase = cl_getenv("GITTEST_REMOTE_SSH_PASSPHRASE");
+ _remote_default = cl_getenv("GITTEST_REMOTE_DEFAULT");
+ _remote_expectcontinue = cl_getenv("GITTEST_REMOTE_EXPECTCONTINUE");
+ _remote = NULL;
+
+ /* Skip the test if we're missing the remote URL */
+ if (!_remote_url)
+ cl_skip();
+
+ if (_remote_expectcontinue)
+ git_libgit2_opts(GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE, 1);
+
+ cl_git_pass(git_remote_create(&_remote, _repo, "test", _remote_url));
+
+ record_callbacks_data_clear(&_record_cbs_data);
+
+ cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_PUSH, &_record_cbs, NULL, NULL));
+
+ /* Clean up previously pushed branches. Fails if receive.denyDeletes is
+ * set on the remote. Also, on Git 1.7.0 and newer, you must run
+ * 'git config receive.denyDeleteCurrent ignore' in the remote repo in
+ * order to delete the remote branch pointed to by HEAD (usually master).
+ * See: https://raw.github.com/git/git/master/Documentation/RelNotes/1.7.0.txt
+ */
+ cl_git_pass(git_remote_ls(&heads, &heads_len, _remote));
+ cl_git_pass(create_deletion_refspecs(&delete_specs, heads, heads_len));
+ if (delete_specs.length) {
+ git_strarray arr = {
+ (char **) delete_specs.contents,
+ delete_specs.length,
+ };
+
+ memcpy(&push_opts.callbacks, &_record_cbs, sizeof(git_remote_callbacks));
+ cl_git_pass(git_remote_upload(_remote, &arr, &push_opts));
+ }
+
+ git_remote_disconnect(_remote);
+ git_vector_free_deep(&delete_specs);
+
+ /* Now that we've deleted everything, fetch from the remote */
+ memcpy(&fetch_opts.callbacks, &_record_cbs, sizeof(git_remote_callbacks));
+ cl_git_pass(git_remote_fetch(_remote, NULL, &fetch_opts, NULL));
+}
+
+void test_online_push__cleanup(void)
+{
+ if (_remote)
+ git_remote_free(_remote);
+ _remote = NULL;
+
+ git__free(_remote_url);
+ git__free(_remote_user);
+ git__free(_remote_pass);
+ git__free(_remote_ssh_key);
+ git__free(_remote_ssh_pubkey);
+ git__free(_remote_ssh_passphrase);
+ git__free(_remote_default);
+ git__free(_remote_expectcontinue);
+
+ /* Freed by cl_git_sandbox_cleanup */
+ _repo = NULL;
+
+ git_libgit2_opts(GIT_OPT_ENABLE_HTTP_EXPECT_CONTINUE, 0);
+
+ record_callbacks_data_clear(&_record_cbs_data);
+
+ cl_fixture_cleanup("testrepo.git");
+ cl_git_sandbox_cleanup();
+}
+
+static int push_pack_progress_cb(
+ int stage, unsigned int current, unsigned int total, void* payload)
+{
+ record_callbacks_data *data = (record_callbacks_data *) payload;
+ GIT_UNUSED(stage); GIT_UNUSED(current); GIT_UNUSED(total);
+ if (data->pack_progress_calls < 0)
+ return data->pack_progress_calls;
+
+ data->pack_progress_calls++;
+ return 0;
+}
+
+static int push_transfer_progress_cb(
+ unsigned int current, unsigned int total, size_t bytes, void* payload)
+{
+ record_callbacks_data *data = (record_callbacks_data *) payload;
+ GIT_UNUSED(current); GIT_UNUSED(total); GIT_UNUSED(bytes);
+ if (data->transfer_progress_calls < 0)
+ return data->transfer_progress_calls;
+
+ data->transfer_progress_calls++;
+ return 0;
+}
+
+/**
+ * Calls push and relists refs on remote to verify success.
+ *
+ * @param refspecs refspecs to push
+ * @param refspecs_len length of refspecs
+ * @param expected_refs expected remote refs after push
+ * @param expected_refs_len length of expected_refs
+ * @param expected_ret expected return value from git_push_finish()
+ * @param check_progress_cb Check that the push progress callbacks are called
+ */
+static void do_push(
+ const char *refspecs[], size_t refspecs_len,
+ push_status expected_statuses[], size_t expected_statuses_len,
+ expected_ref expected_refs[], size_t expected_refs_len,
+ int expected_ret, int check_progress_cb, int check_update_tips_cb)
+{
+ git_push_options opts = GIT_PUSH_OPTIONS_INIT;
+ size_t i;
+ int error;
+ git_strarray specs = {0};
+ record_callbacks_data *data;
+
+ if (_remote) {
+ /* Auto-detect the number of threads to use */
+ opts.pb_parallelism = 0;
+
+ memcpy(&opts.callbacks, &_record_cbs, sizeof(git_remote_callbacks));
+ data = opts.callbacks.payload;
+
+ opts.callbacks.pack_progress = push_pack_progress_cb;
+ opts.callbacks.push_transfer_progress = push_transfer_progress_cb;
+ opts.callbacks.push_update_reference = record_push_status_cb;
+
+ if (refspecs_len) {
+ specs.count = refspecs_len;
+ specs.strings = git__calloc(refspecs_len, sizeof(char *));
+ cl_assert(specs.strings);
+ }
+
+ for (i = 0; i < refspecs_len; i++)
+ specs.strings[i] = (char *) refspecs[i];
+
+ /* if EUSER, then abort in transfer */
+ if (check_progress_cb && expected_ret == GIT_EUSER)
+ data->transfer_progress_calls = GIT_EUSER;
+
+ error = git_remote_push(_remote, &specs, &opts);
+ git__free(specs.strings);
+
+ if (expected_ret < 0) {
+ cl_git_fail_with(expected_ret, error);
+ } else {
+ cl_git_pass(error);
+ }
+
+ if (check_progress_cb && expected_ret == 0) {
+ cl_assert(data->pack_progress_calls > 0);
+ cl_assert(data->transfer_progress_calls > 0);
+ }
+
+ do_verify_push_status(data, expected_statuses, expected_statuses_len);
+
+ verify_refs(_remote, expected_refs, expected_refs_len);
+ verify_tracking_branches(_remote, expected_refs, expected_refs_len);
+
+ if (check_update_tips_cb)
+ verify_update_tips_callback(_remote, expected_refs, expected_refs_len);
+
+ }
+
+}
+
+/* Call push_finish() without ever calling git_push_add_refspec() */
+void test_online_push__noop(void)
+{
+ do_push(NULL, 0, NULL, 0, NULL, 0, 0, 0, 1);
+}
+
+void test_online_push__b1(void)
+{
+ const char *specs[] = { "refs/heads/b1:refs/heads/b1" };
+ push_status exp_stats[] = { { "refs/heads/b1", 1 } };
+ expected_ref exp_refs[] = { { "refs/heads/b1", &_oid_b1 } };
+ do_push(specs, ARRAY_SIZE(specs),
+ exp_stats, ARRAY_SIZE(exp_stats),
+ exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
+}
+
+void test_online_push__b2(void)
+{
+ const char *specs[] = { "refs/heads/b2:refs/heads/b2" };
+ push_status exp_stats[] = { { "refs/heads/b2", 1 } };
+ expected_ref exp_refs[] = { { "refs/heads/b2", &_oid_b2 } };
+ do_push(specs, ARRAY_SIZE(specs),
+ exp_stats, ARRAY_SIZE(exp_stats),
+ exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
+}
+
+void test_online_push__b3(void)
+{
+ const char *specs[] = { "refs/heads/b3:refs/heads/b3" };
+ push_status exp_stats[] = { { "refs/heads/b3", 1 } };
+ expected_ref exp_refs[] = { { "refs/heads/b3", &_oid_b3 } };
+ do_push(specs, ARRAY_SIZE(specs),
+ exp_stats, ARRAY_SIZE(exp_stats),
+ exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
+}
+
+void test_online_push__b4(void)
+{
+ const char *specs[] = { "refs/heads/b4:refs/heads/b4" };
+ push_status exp_stats[] = { { "refs/heads/b4", 1 } };
+ expected_ref exp_refs[] = { { "refs/heads/b4", &_oid_b4 } };
+ do_push(specs, ARRAY_SIZE(specs),
+ exp_stats, ARRAY_SIZE(exp_stats),
+ exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
+}
+
+void test_online_push__b5(void)
+{
+ const char *specs[] = { "refs/heads/b5:refs/heads/b5" };
+ push_status exp_stats[] = { { "refs/heads/b5", 1 } };
+ expected_ref exp_refs[] = { { "refs/heads/b5", &_oid_b5 } };
+ do_push(specs, ARRAY_SIZE(specs),
+ exp_stats, ARRAY_SIZE(exp_stats),
+ exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
+}
+
+void test_online_push__b5_cancel(void)
+{
+ const char *specs[] = { "refs/heads/b5:refs/heads/b5" };
+ do_push(specs, ARRAY_SIZE(specs), NULL, 0, NULL, 0, GIT_EUSER, 1, 1);
+}
+
+void test_online_push__multi(void)
+{
+ git_reflog *log;
+ const git_reflog_entry *entry;
+
+ const char *specs[] = {
+ "refs/heads/b1:refs/heads/b1",
+ "refs/heads/b2:refs/heads/b2",
+ "refs/heads/b3:refs/heads/b3",
+ "refs/heads/b4:refs/heads/b4",
+ "refs/heads/b5:refs/heads/b5"
+ };
+ push_status exp_stats[] = {
+ { "refs/heads/b1", 1 },
+ { "refs/heads/b2", 1 },
+ { "refs/heads/b3", 1 },
+ { "refs/heads/b4", 1 },
+ { "refs/heads/b5", 1 }
+ };
+ expected_ref exp_refs[] = {
+ { "refs/heads/b1", &_oid_b1 },
+ { "refs/heads/b2", &_oid_b2 },
+ { "refs/heads/b3", &_oid_b3 },
+ { "refs/heads/b4", &_oid_b4 },
+ { "refs/heads/b5", &_oid_b5 }
+ };
+ do_push(specs, ARRAY_SIZE(specs),
+ exp_stats, ARRAY_SIZE(exp_stats),
+ exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
+
+ cl_git_pass(git_reflog_read(&log, _repo, "refs/remotes/test/b1"));
+ entry = git_reflog_entry_byindex(log, 0);
+ if (entry) {
+ cl_assert_equal_s("update by push", git_reflog_entry_message(entry));
+ cl_assert_equal_s("foo@example.com", git_reflog_entry_committer(entry)->email);
+ }
+
+ git_reflog_free(log);
+}
+
+void test_online_push__implicit_tgt(void)
+{
+ const char *specs1[] = { "refs/heads/b1" };
+ push_status exp_stats1[] = { { "refs/heads/b1", 1 } };
+ expected_ref exp_refs1[] = { { "refs/heads/b1", &_oid_b1 } };
+
+ const char *specs2[] = { "refs/heads/b2" };
+ push_status exp_stats2[] = { { "refs/heads/b2", 1 } };
+ expected_ref exp_refs2[] = {
+ { "refs/heads/b1", &_oid_b1 },
+ { "refs/heads/b2", &_oid_b2 }
+ };
+
+ do_push(specs1, ARRAY_SIZE(specs1),
+ exp_stats1, ARRAY_SIZE(exp_stats1),
+ exp_refs1, ARRAY_SIZE(exp_refs1), 0, 1, 1);
+ do_push(specs2, ARRAY_SIZE(specs2),
+ exp_stats2, ARRAY_SIZE(exp_stats2),
+ exp_refs2, ARRAY_SIZE(exp_refs2), 0, 0, 0);
+}
+
+void test_online_push__fast_fwd(void)
+{
+ /* Fast forward b1 in tgt from _oid_b1 to _oid_b6. */
+
+ const char *specs_init[] = { "refs/heads/b1:refs/heads/b1" };
+ push_status exp_stats_init[] = { { "refs/heads/b1", 1 } };
+ expected_ref exp_refs_init[] = { { "refs/heads/b1", &_oid_b1 } };
+
+ const char *specs_ff[] = { "refs/heads/b6:refs/heads/b1" };
+ push_status exp_stats_ff[] = { { "refs/heads/b1", 1 } };
+ expected_ref exp_refs_ff[] = { { "refs/heads/b1", &_oid_b6 } };
+
+ /* Do a force push to reset b1 in target back to _oid_b1 */
+ const char *specs_reset[] = { "+refs/heads/b1:refs/heads/b1" };
+ /* Force should have no effect on a fast forward push */
+ const char *specs_ff_force[] = { "+refs/heads/b6:refs/heads/b1" };
+
+ do_push(specs_init, ARRAY_SIZE(specs_init),
+ exp_stats_init, ARRAY_SIZE(exp_stats_init),
+ exp_refs_init, ARRAY_SIZE(exp_refs_init), 0, 1, 1);
+
+ do_push(specs_ff, ARRAY_SIZE(specs_ff),
+ exp_stats_ff, ARRAY_SIZE(exp_stats_ff),
+ exp_refs_ff, ARRAY_SIZE(exp_refs_ff), 0, 0, 0);
+
+ do_push(specs_reset, ARRAY_SIZE(specs_reset),
+ exp_stats_init, ARRAY_SIZE(exp_stats_init),
+ exp_refs_init, ARRAY_SIZE(exp_refs_init), 0, 0, 0);
+
+ do_push(specs_ff_force, ARRAY_SIZE(specs_ff_force),
+ exp_stats_ff, ARRAY_SIZE(exp_stats_ff),
+ exp_refs_ff, ARRAY_SIZE(exp_refs_ff), 0, 0, 0);
+}
+
+void test_online_push__tag_commit(void)
+{
+ const char *specs[] = { "refs/tags/tag-commit:refs/tags/tag-commit" };
+ push_status exp_stats[] = { { "refs/tags/tag-commit", 1 } };
+ expected_ref exp_refs[] = { { "refs/tags/tag-commit", &_tag_commit } };
+ do_push(specs, ARRAY_SIZE(specs),
+ exp_stats, ARRAY_SIZE(exp_stats),
+ exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
+}
+
+void test_online_push__tag_tree(void)
+{
+ const char *specs[] = { "refs/tags/tag-tree:refs/tags/tag-tree" };
+ push_status exp_stats[] = { { "refs/tags/tag-tree", 1 } };
+ expected_ref exp_refs[] = { { "refs/tags/tag-tree", &_tag_tree } };
+ do_push(specs, ARRAY_SIZE(specs),
+ exp_stats, ARRAY_SIZE(exp_stats),
+ exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
+}
+
+void test_online_push__tag_blob(void)
+{
+ const char *specs[] = { "refs/tags/tag-blob:refs/tags/tag-blob" };
+ push_status exp_stats[] = { { "refs/tags/tag-blob", 1 } };
+ expected_ref exp_refs[] = { { "refs/tags/tag-blob", &_tag_blob } };
+ do_push(specs, ARRAY_SIZE(specs),
+ exp_stats, ARRAY_SIZE(exp_stats),
+ exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
+}
+
+void test_online_push__tag_lightweight(void)
+{
+ const char *specs[] = { "refs/tags/tag-lightweight:refs/tags/tag-lightweight" };
+ push_status exp_stats[] = { { "refs/tags/tag-lightweight", 1 } };
+ expected_ref exp_refs[] = { { "refs/tags/tag-lightweight", &_tag_lightweight } };
+ do_push(specs, ARRAY_SIZE(specs),
+ exp_stats, ARRAY_SIZE(exp_stats),
+ exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
+}
+
+void test_online_push__tag_to_tag(void)
+{
+ const char *specs[] = { "refs/tags/tag-tag:refs/tags/tag-tag" };
+ push_status exp_stats[] = { { "refs/tags/tag-tag", 1 } };
+ expected_ref exp_refs[] = { { "refs/tags/tag-tag", &_tag_tag } };
+ do_push(specs, ARRAY_SIZE(specs),
+ exp_stats, ARRAY_SIZE(exp_stats),
+ exp_refs, ARRAY_SIZE(exp_refs), 0, 0, 0);
+}
+
+void test_online_push__force(void)
+{
+ const char *specs1[] = {"refs/heads/b3:refs/heads/tgt"};
+ push_status exp_stats1[] = { { "refs/heads/tgt", 1 } };
+ expected_ref exp_refs1[] = { { "refs/heads/tgt", &_oid_b3 } };
+
+ const char *specs2[] = {"refs/heads/b4:refs/heads/tgt"};
+
+ const char *specs2_force[] = {"+refs/heads/b4:refs/heads/tgt"};
+ push_status exp_stats2_force[] = { { "refs/heads/tgt", 1 } };
+ expected_ref exp_refs2_force[] = { { "refs/heads/tgt", &_oid_b4 } };
+
+ do_push(specs1, ARRAY_SIZE(specs1),
+ exp_stats1, ARRAY_SIZE(exp_stats1),
+ exp_refs1, ARRAY_SIZE(exp_refs1), 0, 1, 1);
+
+ do_push(specs2, ARRAY_SIZE(specs2),
+ NULL, 0,
+ exp_refs1, ARRAY_SIZE(exp_refs1), GIT_ENONFASTFORWARD, 0, 0);
+
+ /* Non-fast-forward update with force should pass. */
+ record_callbacks_data_clear(&_record_cbs_data);
+ do_push(specs2_force, ARRAY_SIZE(specs2_force),
+ exp_stats2_force, ARRAY_SIZE(exp_stats2_force),
+ exp_refs2_force, ARRAY_SIZE(exp_refs2_force), 0, 1, 1);
+}
+
+void test_online_push__delete(void)
+{
+ const char *specs1[] = {
+ "refs/heads/b1:refs/heads/tgt1",
+ "refs/heads/b1:refs/heads/tgt2"
+ };
+ push_status exp_stats1[] = {
+ { "refs/heads/tgt1", 1 },
+ { "refs/heads/tgt2", 1 }
+ };
+ expected_ref exp_refs1[] = {
+ { "refs/heads/tgt1", &_oid_b1 },
+ { "refs/heads/tgt2", &_oid_b1 }
+ };
+
+ const char *specs_del_fake[] = { ":refs/heads/fake" };
+ /* Force has no effect for delete. */
+ const char *specs_del_fake_force[] = { "+:refs/heads/fake" };
+ push_status exp_stats_fake[] = { { "refs/heads/fake", 1 } };
+
+ const char *specs_delete[] = { ":refs/heads/tgt1" };
+ push_status exp_stats_delete[] = { { "refs/heads/tgt1", 1 } };
+ expected_ref exp_refs_delete[] = { { "refs/heads/tgt2", &_oid_b1 } };
+ /* Force has no effect for delete. */
+ const char *specs_delete_force[] = { "+:refs/heads/tgt1" };
+
+ do_push(specs1, ARRAY_SIZE(specs1),
+ exp_stats1, ARRAY_SIZE(exp_stats1),
+ exp_refs1, ARRAY_SIZE(exp_refs1), 0, 1, 1);
+
+ /* When deleting a non-existent branch, the git client sends zero for both
+ * the old and new commit id. This should succeed on the server with the
+ * same status report as if the branch were actually deleted. The server
+ * returns a warning on the side-band iff the side-band is supported.
+ * Since libgit2 doesn't support the side-band yet, there are no warnings.
+ */
+ do_push(specs_del_fake, ARRAY_SIZE(specs_del_fake),
+ exp_stats_fake, 1,
+ exp_refs1, ARRAY_SIZE(exp_refs1), 0, 0, 0);
+ do_push(specs_del_fake_force, ARRAY_SIZE(specs_del_fake_force),
+ exp_stats_fake, 1,
+ exp_refs1, ARRAY_SIZE(exp_refs1), 0, 0, 0);
+
+ /* Delete one of the pushed branches. */
+ do_push(specs_delete, ARRAY_SIZE(specs_delete),
+ exp_stats_delete, ARRAY_SIZE(exp_stats_delete),
+ exp_refs_delete, ARRAY_SIZE(exp_refs_delete), 0, 0, 0);
+
+ /* Re-push branches and retry delete with force. */
+ do_push(specs1, ARRAY_SIZE(specs1),
+ exp_stats1, ARRAY_SIZE(exp_stats1),
+ exp_refs1, ARRAY_SIZE(exp_refs1), 0, 0, 0);
+ do_push(specs_delete_force, ARRAY_SIZE(specs_delete_force),
+ exp_stats_delete, ARRAY_SIZE(exp_stats_delete),
+ exp_refs_delete, ARRAY_SIZE(exp_refs_delete), 0, 0, 0);
+}
+
+void test_online_push__bad_refspecs(void)
+{
+ /* All classes of refspecs that should be rejected by
+ * git_push_add_refspec() should go in this test.
+ */
+ char *specs = {
+ "b6:b6",
+ };
+ git_strarray arr = {
+ &specs,
+ 1,
+ };
+
+ if (_remote) {
+ cl_git_fail(git_remote_upload(_remote, &arr, NULL));
+ }
+}
+
+void test_online_push__expressions(void)
+{
+ const char *specs_left_expr[] = {
+ "refs/heads/b3~1:refs/heads/b2",
+ "b4:refs/heads/b4",
+ "fa38b91f199934685819bea316186d8b008c52a2:refs/heads/b5",
+ "951bbbb:refs/heads/b6"
+ };
+ push_status exp_stats[] = {
+ { "refs/heads/b2", 1 },
+ { "refs/heads/b4", 1 },
+ { "refs/heads/b5", 1 },
+ { "refs/heads/b6", 1 }
+ };
+ expected_ref exp_refs[] = {
+ { "refs/heads/b2", &_oid_b2 },
+ { "refs/heads/b4", &_oid_b4 },
+ { "refs/heads/b5", &_oid_b5 },
+ { "refs/heads/b6", &_oid_b6 }
+ };
+
+ do_push(specs_left_expr, ARRAY_SIZE(specs_left_expr),
+ exp_stats, ARRAY_SIZE(exp_stats),
+ exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
+}
+
+void test_online_push__notes(void)
+{
+ git_oid note_oid, *target_oid, expected_oid;
+ git_signature *signature;
+ const char *specs[] = { "refs/notes/commits:refs/notes/commits" };
+ push_status exp_stats[] = { { "refs/notes/commits", 1 } };
+ expected_ref exp_refs[] = { { "refs/notes/commits", &expected_oid } };
+ const char *specs_del[] = { ":refs/notes/commits" };
+
+ git_oid__fromstr(&expected_oid, "8461a99b27b7043e58ff6e1f5d2cf07d282534fb", GIT_OID_SHA1);
+
+ target_oid = &_oid_b6;
+
+ /* Create note to push */
+ cl_git_pass(git_signature_new(&signature, "nulltoken", "emeric.fermas@gmail.com", 1323847743, 60)); /* Wed Dec 14 08:29:03 2011 +0100 */
+ cl_git_pass(git_note_create(&note_oid, _repo, NULL, signature, signature, target_oid, "hello world\n", 0));
+
+ do_push(specs, ARRAY_SIZE(specs),
+ exp_stats, ARRAY_SIZE(exp_stats),
+ exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
+
+ /* And make sure to delete the note */
+
+ do_push(specs_del, ARRAY_SIZE(specs_del),
+ exp_stats, 1,
+ NULL, 0, 0, 0, 0);
+
+ git_signature_free(signature);
+}
+
+void test_online_push__configured(void)
+{
+ git_oid note_oid, *target_oid, expected_oid;
+ git_signature *signature;
+ git_remote *old_remote;
+ const char *specs[] = { "refs/notes/commits:refs/notes/commits" };
+ push_status exp_stats[] = { { "refs/notes/commits", 1 } };
+ expected_ref exp_refs[] = { { "refs/notes/commits", &expected_oid } };
+ const char *specs_del[] = { ":refs/notes/commits" };
+
+ git_oid__fromstr(&expected_oid, "8461a99b27b7043e58ff6e1f5d2cf07d282534fb", GIT_OID_SHA1);
+
+ target_oid = &_oid_b6;
+
+ cl_git_pass(git_remote_add_push(_repo, git_remote_name(_remote), specs[0]));
+ old_remote = _remote;
+ cl_git_pass(git_remote_lookup(&_remote, _repo, git_remote_name(_remote)));
+ git_remote_free(old_remote);
+
+ /* Create note to push */
+ cl_git_pass(git_signature_new(&signature, "nulltoken", "emeric.fermas@gmail.com", 1323847743, 60)); /* Wed Dec 14 08:29:03 2011 +0100 */
+ cl_git_pass(git_note_create(&note_oid, _repo, NULL, signature, signature, target_oid, "hello world\n", 0));
+
+ do_push(NULL, 0,
+ exp_stats, ARRAY_SIZE(exp_stats),
+ exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1);
+
+ /* And make sure to delete the note */
+
+ do_push(specs_del, ARRAY_SIZE(specs_del),
+ exp_stats, 1,
+ NULL, 0, 0, 0, 0);
+
+ git_signature_free(signature);
+}
diff --git a/tests/libgit2/online/push_util.c b/tests/libgit2/online/push_util.c
new file mode 100644
index 0000000..94919e9
--- /dev/null
+++ b/tests/libgit2/online/push_util.c
@@ -0,0 +1,139 @@
+#include "clar_libgit2.h"
+#include "vector.h"
+#include "push_util.h"
+
+void updated_tip_free(updated_tip *t)
+{
+ git__free(t->name);
+ git__free(t);
+}
+
+static void push_status_free(push_status *s)
+{
+ git__free(s->ref);
+ git__free(s->msg);
+ git__free(s);
+}
+
+void record_callbacks_data_clear(record_callbacks_data *data)
+{
+ size_t i;
+ updated_tip *tip;
+ push_status *status;
+
+ git_vector_foreach(&data->updated_tips, i, tip)
+ updated_tip_free(tip);
+
+ git_vector_free(&data->updated_tips);
+
+ git_vector_foreach(&data->statuses, i, status)
+ push_status_free(status);
+
+ git_vector_free(&data->statuses);
+
+ data->pack_progress_calls = 0;
+ data->transfer_progress_calls = 0;
+}
+
+int record_update_tips_cb(const char *refname, const git_oid *a, const git_oid *b, void *data)
+{
+ updated_tip *t;
+ record_callbacks_data *record_data = (record_callbacks_data *)data;
+
+ cl_assert(t = git__calloc(1, sizeof(*t)));
+
+ cl_assert(t->name = git__strdup(refname));
+ git_oid_cpy(&t->old_oid, a);
+ git_oid_cpy(&t->new_oid, b);
+
+ git_vector_insert(&record_data->updated_tips, t);
+
+ return 0;
+}
+
+int create_deletion_refspecs(git_vector *out, const git_remote_head **heads, size_t heads_len)
+{
+ git_str del_spec = GIT_STR_INIT;
+ int valid;
+ size_t i;
+
+ for (i = 0; i < heads_len; i++) {
+ const git_remote_head *head = heads[i];
+ /* Ignore malformed ref names (which also saves us from tag^{} */
+ cl_git_pass(git_reference_name_is_valid(&valid, head->name));
+ if (!valid)
+ return 0;
+
+ /* Create a refspec that deletes a branch in the remote */
+ if (strcmp(head->name, "refs/heads/master")) {
+ cl_git_pass(git_str_putc(&del_spec, ':'));
+ cl_git_pass(git_str_puts(&del_spec, head->name));
+ cl_git_pass(git_vector_insert(out, git_str_detach(&del_spec)));
+ }
+ }
+
+ return 0;
+}
+
+int record_ref_cb(git_remote_head *head, void *payload)
+{
+ git_vector *refs = (git_vector *) payload;
+ return git_vector_insert(refs, head);
+}
+
+void verify_remote_refs(const git_remote_head *actual_refs[], size_t actual_refs_len, const expected_ref expected_refs[], size_t expected_refs_len)
+{
+ size_t i, j = 0;
+ git_str msg = GIT_STR_INIT;
+ const git_remote_head *actual;
+ char *oid_str;
+ bool master_present = false;
+
+ /* We don't care whether "master" is present on the other end or not */
+ for (i = 0; i < actual_refs_len; i++) {
+ actual = actual_refs[i];
+ if (!strcmp(actual->name, "refs/heads/master")) {
+ master_present = true;
+ break;
+ }
+ }
+
+ if (expected_refs_len + (master_present ? 1 : 0) != actual_refs_len)
+ goto failed;
+
+ for (i = 0; i < actual_refs_len; i++) {
+ actual = actual_refs[i];
+ if (master_present && !strcmp(actual->name, "refs/heads/master"))
+ continue;
+
+ if (strcmp(expected_refs[j].name, actual->name) ||
+ git_oid_cmp(expected_refs[j].oid, &actual->oid))
+ goto failed;
+
+ j++;
+ }
+
+ return;
+
+failed:
+ git_str_puts(&msg, "Expected and actual refs differ:\nEXPECTED:\n");
+
+ for(i = 0; i < expected_refs_len; i++) {
+ oid_str = git_oid_tostr_s(expected_refs[i].oid);
+ cl_git_pass(git_str_printf(&msg, "%s = %s\n", expected_refs[i].name, oid_str));
+ }
+
+ git_str_puts(&msg, "\nACTUAL:\n");
+ for (i = 0; i < actual_refs_len; i++) {
+ actual = actual_refs[i];
+ if (master_present && !strcmp(actual->name, "refs/heads/master"))
+ continue;
+
+ oid_str = git_oid_tostr_s(&actual->oid);
+ cl_git_pass(git_str_printf(&msg, "%s = %s\n", actual->name, oid_str));
+ }
+
+ cl_fail(git_str_cstr(&msg));
+
+ git_str_dispose(&msg);
+}
diff --git a/tests/libgit2/online/push_util.h b/tests/libgit2/online/push_util.h
new file mode 100644
index 0000000..5f669fe
--- /dev/null
+++ b/tests/libgit2/online/push_util.h
@@ -0,0 +1,83 @@
+#ifndef INCLUDE_cl_push_util_h__
+#define INCLUDE_cl_push_util_h__
+
+#include "git2/oid.h"
+
+/* Constant for zero oid */
+extern const git_oid OID_ZERO;
+
+/**
+ * Macro for initializing git_remote_callbacks to use test helpers that
+ * record data in a record_callbacks_data instance.
+ * @param data pointer to a record_callbacks_data instance
+ */
+#define RECORD_CALLBACKS_INIT(data) \
+ { GIT_REMOTE_CALLBACKS_VERSION, NULL, NULL, cred_acquire_cb, NULL, NULL, record_update_tips_cb, NULL, NULL, NULL, NULL, NULL, NULL, data, NULL }
+
+typedef struct {
+ char *name;
+ git_oid old_oid;
+ git_oid new_oid;
+} updated_tip;
+
+typedef struct {
+ git_vector updated_tips;
+ git_vector statuses;
+ int pack_progress_calls;
+ int transfer_progress_calls;
+} record_callbacks_data;
+
+typedef struct {
+ const char *name;
+ const git_oid *oid;
+} expected_ref;
+
+/* the results of a push status. when used for expected values, msg may be NULL
+ * to indicate that it should not be matched. */
+typedef struct {
+ char *ref;
+ int success;
+ char *msg;
+} push_status;
+
+
+void updated_tip_free(updated_tip *t);
+
+void record_callbacks_data_clear(record_callbacks_data *data);
+
+/**
+ * Callback for git_remote_update_tips that records updates
+ *
+ * @param data (git_vector *) of updated_tip instances
+ */
+int record_update_tips_cb(const char *refname, const git_oid *a, const git_oid *b, void *data);
+
+/**
+ * Create a set of refspecs that deletes each of the inputs
+ *
+ * @param out the vector in which to store the refspecs
+ * @param heads the remote heads
+ * @param heads_len the size of the array
+ */
+int create_deletion_refspecs(git_vector *out, const git_remote_head **heads, size_t heads_len);
+
+/**
+ * Callback for git_remote_list that adds refspecs to vector
+ *
+ * @param head a ref on the remote
+ * @param payload (git_vector *) of git_remote_head instances
+ */
+int record_ref_cb(git_remote_head *head, void *payload);
+
+/**
+ * Verifies that refs on remote stored by record_ref_cb match the expected
+ * names, oids, and order.
+ *
+ * @param actual_refs actual refs in the remote
+ * @param actual_refs_len length of actual_refs
+ * @param expected_refs expected remote refs
+ * @param expected_refs_len length of expected_refs
+ */
+void verify_remote_refs(const git_remote_head *actual_refs[], size_t actual_refs_len, const expected_ref expected_refs[], size_t expected_refs_len);
+
+#endif /* INCLUDE_cl_push_util_h__ */
diff --git a/tests/libgit2/online/remotes.c b/tests/libgit2/online/remotes.c
new file mode 100644
index 0000000..887874d
--- /dev/null
+++ b/tests/libgit2/online/remotes.c
@@ -0,0 +1,127 @@
+#include "clar_libgit2.h"
+
+#define URL "https://github.com/libgit2/TestGitRepository"
+#define REFSPEC "refs/heads/first-merge:refs/remotes/origin/first-merge"
+
+static int remote_single_branch(git_remote **out, git_repository *repo, const char *name, const char *url, void *payload)
+{
+ GIT_UNUSED(payload);
+
+ cl_git_pass(git_remote_create_with_fetchspec(out, repo, name, url, REFSPEC));
+
+ return 0;
+}
+
+void test_online_remotes__single_branch(void)
+{
+ git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
+ git_repository *repo;
+ git_remote *remote;
+ git_strarray refs;
+ size_t i, count = 0;
+
+ opts.remote_cb = remote_single_branch;
+ opts.checkout_branch = "first-merge";
+
+ cl_git_pass(git_clone(&repo, URL, "./single-branch", &opts));
+ cl_git_pass(git_reference_list(&refs, repo));
+
+ for (i = 0; i < refs.count; i++) {
+ if (!git__prefixcmp(refs.strings[i], "refs/heads/"))
+ count++;
+ }
+ cl_assert_equal_i(1, count);
+
+ git_strarray_dispose(&refs);
+
+ cl_git_pass(git_remote_lookup(&remote, repo, "origin"));
+ cl_git_pass(git_remote_get_fetch_refspecs(&refs, remote));
+
+ cl_assert_equal_i(1, refs.count);
+ cl_assert_equal_s(REFSPEC, refs.strings[0]);
+
+ git_strarray_dispose(&refs);
+ git_remote_free(remote);
+ git_repository_free(repo);
+}
+
+void test_online_remotes__restricted_refspecs(void)
+{
+ git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
+ git_repository *repo;
+
+ opts.remote_cb = remote_single_branch;
+
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_clone(&repo, URL, "./restrict-refspec", &opts));
+}
+
+void test_online_remotes__detached_remote_fails_downloading(void)
+{
+ git_remote *remote;
+
+ cl_git_pass(git_remote_create_detached(&remote, URL));
+ cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL));
+ cl_git_fail(git_remote_download(remote, NULL, NULL));
+
+ git_remote_free(remote);
+}
+
+void test_online_remotes__detached_remote_fails_uploading(void)
+{
+ git_remote *remote;
+
+ cl_git_pass(git_remote_create_detached(&remote, URL));
+ cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL));
+ cl_git_fail(git_remote_upload(remote, NULL, NULL));
+
+ git_remote_free(remote);
+}
+
+void test_online_remotes__detached_remote_fails_pushing(void)
+{
+ git_remote *remote;
+
+ cl_git_pass(git_remote_create_detached(&remote, URL));
+ cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL));
+ cl_git_fail(git_remote_push(remote, NULL, NULL));
+
+ git_remote_free(remote);
+}
+
+void test_online_remotes__detached_remote_succeeds_ls(void)
+{
+ const char *refs[] = {
+ "HEAD",
+ "refs/heads/first-merge",
+ "refs/heads/master",
+ "refs/heads/no-parent",
+ "refs/tags/annotated_tag",
+ "refs/tags/annotated_tag^{}",
+ "refs/tags/blob",
+ "refs/tags/commit_tree",
+ "refs/tags/nearly-dangling",
+ };
+ const git_remote_head **heads;
+ git_remote *remote;
+ size_t i, j, n;
+
+ cl_git_pass(git_remote_create_detached(&remote, URL));
+ cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH, NULL, NULL, NULL));
+ cl_git_pass(git_remote_ls(&heads, &n, remote));
+
+ cl_assert_equal_sz(n, 9);
+ for (i = 0; i < n; i++) {
+ char found = false;
+
+ for (j = 0; j < ARRAY_SIZE(refs); j++) {
+ if (!strcmp(heads[i]->name, refs[j])) {
+ found = true;
+ break;
+ }
+ }
+
+ cl_assert_(found, heads[i]->name);
+ }
+
+ git_remote_free(remote);
+}
diff --git a/tests/libgit2/online/shallow.c b/tests/libgit2/online/shallow.c
new file mode 100644
index 0000000..5c0e656
--- /dev/null
+++ b/tests/libgit2/online/shallow.c
@@ -0,0 +1,166 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+#include "repository.h"
+
+static int remote_single_branch(git_remote **out, git_repository *repo, const char *name, const char *url, void *payload)
+{
+ GIT_UNUSED(payload);
+
+ cl_git_pass(git_remote_create_with_fetchspec(out, repo, name, url, "+refs/heads/master:refs/remotes/origin/master"));
+
+ return 0;
+}
+
+void test_online_shallow__clone_depth_zero(void)
+{
+ git_str path = GIT_STR_INIT;
+ git_repository *repo;
+ git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT;
+ git_oid *roots;
+ size_t roots_len;
+
+ clone_opts.fetch_opts.depth = 0;
+ clone_opts.remote_cb = remote_single_branch;
+
+ git_str_joinpath(&path, clar_sandbox_path(), "shallowclone_0");
+
+ cl_git_pass(git_clone(&repo, "https://github.com/libgit2/TestGitRepository", git_str_cstr(&path), &clone_opts));
+
+ /* cloning with depth 0 results in a full clone. */
+ cl_assert_equal_b(false, git_repository_is_shallow(repo));
+
+ /* full clones do not have shallow roots. */
+ cl_git_pass(git_repository__shallow_roots(&roots, &roots_len, repo));
+ cl_assert_equal_i(0, roots_len);
+
+ git__free(roots);
+ git_str_dispose(&path);
+ git_repository_free(repo);
+}
+
+void test_online_shallow__clone_depth_one(void)
+{
+ git_str path = GIT_STR_INIT;
+ git_repository *repo;
+ git_revwalk *walk;
+ git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT;
+ git_oid oid;
+ git_oid *roots;
+ size_t roots_len;
+ size_t num_commits = 0;
+ int error = 0;
+
+ clone_opts.fetch_opts.depth = 1;
+ clone_opts.remote_cb = remote_single_branch;
+
+ git_str_joinpath(&path, clar_sandbox_path(), "shallowclone_1");
+
+ cl_git_pass(git_clone(&repo, "https://github.com/libgit2/TestGitRepository", git_str_cstr(&path), &clone_opts));
+
+ cl_assert_equal_b(true, git_repository_is_shallow(repo));
+
+ cl_git_pass(git_repository__shallow_roots(&roots, &roots_len, repo));
+ cl_assert_equal_i(1, roots_len);
+ cl_assert_equal_s("49322bb17d3acc9146f98c97d078513228bbf3c0", git_oid_tostr_s(&roots[0]));
+
+ git_revwalk_new(&walk, repo);
+
+ git_revwalk_push_head(walk);
+
+ while ((error = git_revwalk_next(&oid, walk)) == GIT_OK) {
+ num_commits++;
+ }
+
+ cl_assert_equal_i(num_commits, 1);
+ cl_assert_equal_i(error, GIT_ITEROVER);
+
+ git__free(roots);
+ git_str_dispose(&path);
+ git_revwalk_free(walk);
+ git_repository_free(repo);
+}
+
+void test_online_shallow__clone_depth_five(void)
+{
+ git_str path = GIT_STR_INIT;
+ git_repository *repo;
+ git_revwalk *walk;
+ git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT;
+ git_oid oid;
+ git_oid *roots;
+ size_t roots_len;
+ size_t num_commits = 0;
+ int error = 0;
+
+ clone_opts.fetch_opts.depth = 5;
+ clone_opts.remote_cb = remote_single_branch;
+
+ git_str_joinpath(&path, clar_sandbox_path(), "shallowclone_5");
+
+ cl_git_pass(git_clone(&repo, "https://github.com/libgit2/TestGitRepository", git_str_cstr(&path), &clone_opts));
+
+ cl_assert_equal_b(true, git_repository_is_shallow(repo));
+
+ cl_git_pass(git_repository__shallow_roots(&roots, &roots_len, repo));
+ cl_assert_equal_i(3, roots_len);
+ cl_assert_equal_s("c070ad8c08840c8116da865b2d65593a6bb9cd2a", git_oid_tostr_s(&roots[0]));
+ cl_assert_equal_s("0966a434eb1a025db6b71485ab63a3bfbea520b6", git_oid_tostr_s(&roots[1]));
+ cl_assert_equal_s("83834a7afdaa1a1260568567f6ad90020389f664", git_oid_tostr_s(&roots[2]));
+
+ git_revwalk_new(&walk, repo);
+
+ git_revwalk_push_head(walk);
+
+ while ((error = git_revwalk_next(&oid, walk)) == GIT_OK) {
+ num_commits++;
+ }
+
+ cl_assert_equal_i(num_commits, 13);
+ cl_assert_equal_i(error, GIT_ITEROVER);
+
+ git__free(roots);
+ git_str_dispose(&path);
+ git_revwalk_free(walk);
+ git_repository_free(repo);
+}
+
+void test_online_shallow__unshallow(void)
+{
+ git_str path = GIT_STR_INIT;
+ git_repository *repo;
+ git_revwalk *walk;
+ git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT;
+ git_fetch_options fetch_opts = GIT_FETCH_OPTIONS_INIT;
+ git_remote *origin = NULL;
+ git_oid oid;
+ size_t num_commits = 0;
+ int error = 0;
+
+ clone_opts.fetch_opts.depth = 5;
+ clone_opts.remote_cb = remote_single_branch;
+
+ git_str_joinpath(&path, clar_sandbox_path(), "unshallow");
+ cl_git_pass(git_clone(&repo, "https://github.com/libgit2/TestGitRepository", git_str_cstr(&path), &clone_opts));
+ cl_assert_equal_b(true, git_repository_is_shallow(repo));
+
+ fetch_opts.depth = GIT_FETCH_DEPTH_UNSHALLOW;
+ cl_git_pass(git_remote_lookup(&origin, repo, "origin"));
+
+ cl_git_pass(git_remote_fetch(origin, NULL, &fetch_opts, NULL));
+ cl_assert_equal_b(false, git_repository_is_shallow(repo));
+
+ git_revwalk_new(&walk, repo);
+ git_revwalk_push_head(walk);
+
+ while ((error = git_revwalk_next(&oid, walk)) == GIT_OK) {
+ num_commits++;
+ }
+
+ cl_assert_equal_i(num_commits, 21);
+ cl_assert_equal_i(error, GIT_ITEROVER);
+
+ git_remote_free(origin);
+ git_str_dispose(&path);
+ git_revwalk_free(walk);
+ git_repository_free(repo);
+}
diff --git a/tests/libgit2/pack/filelimit.c b/tests/libgit2/pack/filelimit.c
new file mode 100644
index 0000000..fa08485
--- /dev/null
+++ b/tests/libgit2/pack/filelimit.c
@@ -0,0 +1,136 @@
+#include "clar_libgit2.h"
+#include "mwindow.h"
+
+#include <git2.h>
+#include "git2/sys/commit.h"
+#include "git2/sys/mempack.h"
+
+static size_t expected_open_mwindow_files = 0;
+static size_t original_mwindow_file_limit = 0;
+
+extern git_mutex git__mwindow_mutex;
+extern git_mwindow_ctl git_mwindow__mem_ctl;
+
+void test_pack_filelimit__initialize_tiny(void)
+{
+ expected_open_mwindow_files = 1;
+ cl_git_pass(git_libgit2_opts(GIT_OPT_GET_MWINDOW_FILE_LIMIT, &original_mwindow_file_limit));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_MWINDOW_FILE_LIMIT, expected_open_mwindow_files));
+}
+
+void test_pack_filelimit__initialize_medium(void)
+{
+ expected_open_mwindow_files = 10;
+ cl_git_pass(git_libgit2_opts(GIT_OPT_GET_MWINDOW_FILE_LIMIT, &original_mwindow_file_limit));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_MWINDOW_FILE_LIMIT, expected_open_mwindow_files));
+}
+
+void test_pack_filelimit__initialize_unlimited(void)
+{
+ expected_open_mwindow_files = 15;
+ cl_git_pass(git_libgit2_opts(GIT_OPT_GET_MWINDOW_FILE_LIMIT, &original_mwindow_file_limit));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_MWINDOW_FILE_LIMIT, 0));
+}
+
+void test_pack_filelimit__cleanup(void)
+{
+ git_str path = GIT_STR_INIT;
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_MWINDOW_FILE_LIMIT, original_mwindow_file_limit));
+
+ cl_git_pass(git_str_joinpath(&path, clar_sandbox_path(), "repo.git"));
+ cl_fixture_cleanup(path.ptr);
+ git_str_dispose(&path);
+}
+
+/*
+ * Create a packfile with one commit, one tree, and two blobs. The first blob
+ * (README.md) has the same content in all commits, but the second one
+ * (file.txt) has a different content in each commit.
+ */
+static void create_packfile_commit(
+ git_repository *repo,
+ git_oid *out_commit_id,
+ git_oid *parent_id,
+ size_t commit_index,
+ size_t commit_count)
+{
+ git_str file_contents = GIT_STR_INIT;
+ git_treebuilder *treebuilder;
+ git_packbuilder *packbuilder;
+ git_signature *s;
+ git_oid oid, tree_id, commit_id;
+ const git_oid *parents[] = { parent_id };
+ size_t parent_count = parent_id ? 1 : 0;
+
+ cl_git_pass(git_treebuilder_new(&treebuilder, repo, NULL));
+
+ cl_git_pass(git_blob_create_from_buffer(&oid, repo, "", 0));
+ cl_git_pass(git_treebuilder_insert(NULL, treebuilder, "README.md", &oid, 0100644));
+
+ cl_git_pass(git_str_printf(&file_contents, "Commit %zd/%zd", commit_index, commit_count));
+ cl_git_pass(git_blob_create_from_buffer(&oid, repo, file_contents.ptr, file_contents.size));
+ cl_git_pass(git_treebuilder_insert(NULL, treebuilder, "file.txt", &oid, 0100644));
+
+ cl_git_pass(git_treebuilder_write(&tree_id, treebuilder));
+ cl_git_pass(git_signature_now(&s, "alice", "alice@example.com"));
+ cl_git_pass(git_commit_create_from_ids(&commit_id, repo, "refs/heads/master", s, s,
+ NULL, file_contents.ptr, &tree_id, parent_count, parents));
+
+ cl_git_pass(git_packbuilder_new(&packbuilder, repo));
+ cl_git_pass(git_packbuilder_insert_commit(packbuilder, &commit_id));
+ cl_git_pass(git_packbuilder_write(packbuilder, NULL, 0, NULL, NULL));
+
+ cl_git_pass(git_oid_cpy(out_commit_id, &commit_id));
+
+ git_str_dispose(&file_contents);
+ git_treebuilder_free(treebuilder);
+ git_packbuilder_free(packbuilder);
+ git_signature_free(s);
+}
+
+void test_pack_filelimit__open_repo_with_multiple_packfiles(void)
+{
+ git_str path = GIT_STR_INIT;
+ git_mwindow_ctl *ctl = &git_mwindow__mem_ctl;
+ git_repository *repo;
+ git_revwalk *walk;
+ git_oid id, *parent_id = NULL;
+ size_t i;
+ const size_t commit_count = 16;
+ unsigned int open_windows;
+
+ /*
+ * Create a repository and populate it with 16 commits, each in its own
+ * packfile.
+ */
+ cl_git_pass(git_str_joinpath(&path, clar_sandbox_path(), "repo.git"));
+ cl_git_pass(git_repository_init(&repo, path.ptr, true));
+ for (i = 0; i < commit_count; ++i) {
+ create_packfile_commit(repo, &id, parent_id, i + 1, commit_count);
+ parent_id = &id;
+ }
+
+ cl_git_pass(git_revwalk_new(&walk, repo));
+ cl_git_pass(git_revwalk_sorting(walk, GIT_SORT_TOPOLOGICAL));
+ cl_git_pass(git_revwalk_push_ref(walk, "refs/heads/master"));
+
+ /* Walking the repository requires eventually opening each of the packfiles. */
+ i = 0;
+ while (git_revwalk_next(&id, walk) == 0)
+ ++i;
+ cl_assert_equal_i(commit_count, i);
+
+ cl_git_pass(git_mutex_lock(&git__mwindow_mutex));
+ /*
+ * Adding an assert while holding a lock will cause the whole process to
+ * deadlock. Copy the value and do the assert after releasing the lock.
+ */
+ open_windows = ctl->open_windows;
+ cl_git_pass(git_mutex_unlock(&git__mwindow_mutex));
+
+ cl_assert_equal_i(expected_open_mwindow_files, open_windows);
+
+ git_str_dispose(&path);
+ git_revwalk_free(walk);
+ git_repository_free(repo);
+}
diff --git a/tests/libgit2/pack/indexer.c b/tests/libgit2/pack/indexer.c
new file mode 100644
index 0000000..9722dec
--- /dev/null
+++ b/tests/libgit2/pack/indexer.c
@@ -0,0 +1,369 @@
+#include "clar_libgit2.h"
+#include <git2.h>
+#include "futils.h"
+#include "hash.h"
+#include "iterator.h"
+#include "vector.h"
+#include "posix.h"
+
+
+/*
+ * This is a packfile with three objects. The second is a delta which
+ * depends on the third, which is also a delta.
+ */
+static const unsigned char out_of_order_pack[] = {
+ 0x50, 0x41, 0x43, 0x4b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03,
+ 0x32, 0x78, 0x9c, 0x63, 0x67, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, 0x76,
+ 0xe6, 0x8f, 0xe8, 0x12, 0x9b, 0x54, 0x6b, 0x10, 0x1a, 0xee, 0x95, 0x10,
+ 0xc5, 0x32, 0x8e, 0x7f, 0x21, 0xca, 0x1d, 0x18, 0x78, 0x9c, 0x63, 0x62,
+ 0x66, 0x4e, 0xcb, 0xcf, 0x07, 0x00, 0x02, 0xac, 0x01, 0x4d, 0x75, 0x01,
+ 0xd7, 0x71, 0x36, 0x66, 0xf4, 0xde, 0x82, 0x27, 0x76, 0xc7, 0x62, 0x2c,
+ 0x10, 0xf1, 0xb0, 0x7d, 0xe2, 0x80, 0xdc, 0x78, 0x9c, 0x63, 0x62, 0x62,
+ 0x62, 0xb7, 0x03, 0x00, 0x00, 0x69, 0x00, 0x4c, 0xde, 0x7d, 0xaa, 0xe4,
+ 0x19, 0x87, 0x58, 0x80, 0x61, 0x09, 0x9a, 0x33, 0xca, 0x7a, 0x31, 0x92,
+ 0x6f, 0xae, 0x66, 0x75
+};
+static const unsigned int out_of_order_pack_len = 112;
+
+/*
+ * Packfile with two objects. The second is a delta against an object
+ * which is not in the packfile
+ */
+static const unsigned char thin_pack[] = {
+ 0x50, 0x41, 0x43, 0x4b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02,
+ 0x32, 0x78, 0x9c, 0x63, 0x67, 0x00, 0x00, 0x00, 0x10, 0x00, 0x08, 0x76,
+ 0xe6, 0x8f, 0xe8, 0x12, 0x9b, 0x54, 0x6b, 0x10, 0x1a, 0xee, 0x95, 0x10,
+ 0xc5, 0x32, 0x8e, 0x7f, 0x21, 0xca, 0x1d, 0x18, 0x78, 0x9c, 0x63, 0x62,
+ 0x66, 0x4e, 0xcb, 0xcf, 0x07, 0x00, 0x02, 0xac, 0x01, 0x4d, 0x42, 0x52,
+ 0x3a, 0x6f, 0x39, 0xd1, 0xfe, 0x66, 0x68, 0x6b, 0xa5, 0xe5, 0xe2, 0x97,
+ 0xac, 0x94, 0x6c, 0x76, 0x0b, 0x04
+};
+static const unsigned int thin_pack_len = 78;
+
+/*
+ * Packfile with one object. It references an object which is not in the
+ * packfile and has a corrupt length (states the deltified stream is 1 byte
+ * long, where it is actually 6).
+ */
+static const unsigned char corrupt_thin_pack[] = {
+ 0x50, 0x41, 0x43, 0x4b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01,
+ 0x71, 0xe6, 0x8f, 0xe8, 0x12, 0x9b, 0x54, 0x6b, 0x10, 0x1a, 0xee, 0x95,
+ 0x10, 0xc5, 0x32, 0x8e, 0x7f, 0x21, 0xca, 0x1d, 0x18, 0x78, 0x9c, 0x63,
+ 0x62, 0x66, 0x4e, 0xcb, 0xcf, 0x07, 0x00, 0x02, 0xac, 0x01, 0x4d, 0x07,
+ 0x67, 0x03, 0xc5, 0x40, 0x99, 0x49, 0xb1, 0x3b, 0x7d, 0xae, 0x9b, 0x0e,
+ 0xdd, 0xde, 0xc6, 0x76, 0x43, 0x24, 0x64
+};
+static const unsigned int corrupt_thin_pack_len = 67;
+
+/*
+ * Packfile with a missing trailer.
+ */
+static const unsigned char missing_trailer_pack[] = {
+ 0x50, 0x41, 0x43, 0x4b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x50, 0xf4, 0x3b,
+};
+static const unsigned int missing_trailer_pack_len = 12;
+
+/*
+ * Packfile that causes the packfile stream to open in a way in which it leaks
+ * the stream reader.
+ */
+static const unsigned char leaky_pack[] = {
+ 0x50, 0x41, 0x43, 0x4b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x03,
+ 0xf4, 0xbd, 0x51, 0x51, 0x51, 0x51, 0x51, 0x72, 0x65, 0x41, 0x4b, 0x63,
+ 0x5f, 0x64, 0x65, 0x70, 0x74, 0x68, 0xbd, 0x41, 0x4b
+};
+static const unsigned int leaky_pack_len = 33;
+
+/*
+ * Packfile with a three objects. The first one is a tree referencing two blobs,
+ * the second object is one of those blobs. The second blob is missing.
+ */
+unsigned char incomplete_pack[] = {
+ 0x50, 0x41, 0x43, 0x4b, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02,
+ 0xae, 0x03, 0x78, 0x9c, 0x33, 0x34, 0x30, 0x30, 0x33, 0x31, 0x51, 0x48,
+ 0x4a, 0x2c, 0x62, 0x08, 0x17, 0x3b, 0x15, 0xd9, 0x7e, 0xfa, 0x67, 0x6d,
+ 0xf6, 0x56, 0x4f, 0x85, 0x7d, 0xcb, 0xd6, 0xde, 0x53, 0xd1, 0x6d, 0x7f,
+ 0x66, 0x08, 0x91, 0x4e, 0xcb, 0xcf, 0x67, 0x50, 0xad, 0x39, 0x9a, 0xa2,
+ 0xb3, 0x71, 0x41, 0xc8, 0x87, 0x9e, 0x13, 0xf6, 0xba, 0x53, 0xec, 0xc2,
+ 0xfe, 0xda, 0xed, 0x9b, 0x09, 0x00, 0xe8, 0xc8, 0x19, 0xab, 0x34, 0x78,
+ 0x9c, 0x4b, 0x4a, 0x2c, 0xe2, 0x02, 0x00, 0x03, 0x9d, 0x01, 0x40, 0x4b,
+ 0x72, 0xa2, 0x6f, 0xb6, 0x88, 0x2d, 0x6c, 0xa5, 0x07, 0xb2, 0xa5, 0x45,
+ 0xe8, 0xdb, 0xe6, 0x53, 0xb3, 0x52, 0xe2
+};
+unsigned int incomplete_pack_len = 115;
+
+static const unsigned char base_obj[] = { 07, 076 };
+static const unsigned int base_obj_len = 2;
+
+void test_pack_indexer__out_of_order(void)
+{
+ git_indexer *idx = 0;
+ git_indexer_progress stats = { 0 };
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_indexer_new(&idx, ".", GIT_OID_SHA1, NULL));
+#else
+ cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL));
+#endif
+
+ cl_git_pass(git_indexer_append(
+ idx, out_of_order_pack, out_of_order_pack_len, &stats));
+ cl_git_pass(git_indexer_commit(idx, &stats));
+
+ cl_assert_equal_i(stats.total_objects, 3);
+ cl_assert_equal_i(stats.received_objects, 3);
+ cl_assert_equal_i(stats.indexed_objects, 3);
+
+ git_indexer_free(idx);
+}
+
+void test_pack_indexer__missing_trailer(void)
+{
+ git_indexer *idx = 0;
+ git_indexer_progress stats = { 0 };
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_indexer_new(&idx, ".", GIT_OID_SHA1, NULL));
+#else
+ cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL));
+#endif
+
+ cl_git_pass(git_indexer_append(
+ idx, missing_trailer_pack, missing_trailer_pack_len, &stats));
+ cl_git_fail(git_indexer_commit(idx, &stats));
+
+ cl_assert(git_error_last() != NULL);
+ cl_assert_equal_i(git_error_last()->klass, GIT_ERROR_INDEXER);
+
+ git_indexer_free(idx);
+}
+
+void test_pack_indexer__leaky(void)
+{
+ git_indexer *idx = 0;
+ git_indexer_progress stats = { 0 };
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_indexer_new(&idx, ".", GIT_OID_SHA1, NULL));
+#else
+ cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL));
+#endif
+
+ cl_git_pass(git_indexer_append(
+ idx, leaky_pack, leaky_pack_len, &stats));
+ cl_git_fail(git_indexer_commit(idx, &stats));
+
+ cl_assert(git_error_last() != NULL);
+ cl_assert_equal_i(git_error_last()->klass, GIT_ERROR_INDEXER);
+
+ git_indexer_free(idx);
+}
+
+void test_pack_indexer__fix_thin(void)
+{
+ git_indexer *idx = NULL;
+ git_indexer_progress stats = { 0 };
+ git_repository *repo;
+ git_odb *odb;
+ git_oid id, should_id;
+ git_indexer_options opts = GIT_INDEXER_OPTIONS_INIT;
+
+ cl_git_pass(git_repository_init(&repo, "thin.git", true));
+ cl_git_pass(git_repository_odb(&odb, repo));
+
+ /* Store the missing base into your ODB so the indexer can fix the pack */
+ cl_git_pass(git_odb_write(&id, odb, base_obj, base_obj_len, GIT_OBJECT_BLOB));
+ git_oid__fromstr(&should_id, "e68fe8129b546b101aee9510c5328e7f21ca1d18", GIT_OID_SHA1);
+ cl_assert_equal_oid(&should_id, &id);
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ opts.odb = odb;
+ cl_git_pass(git_indexer_new(&idx, ".", GIT_OID_SHA1, &opts));
+#else
+ cl_git_pass(git_indexer_new(&idx, ".", 0, odb, &opts));
+#endif
+
+ cl_git_pass(git_indexer_append(idx, thin_pack, thin_pack_len, &stats));
+ cl_git_pass(git_indexer_commit(idx, &stats));
+
+ cl_assert_equal_i(stats.total_objects, 2);
+ cl_assert_equal_i(stats.received_objects, 2);
+ cl_assert_equal_i(stats.indexed_objects, 2);
+ cl_assert_equal_i(stats.local_objects, 1);
+
+ cl_assert_equal_s("fefdb2d740a3a6b6c03a0c7d6ce431c6d5810e13", git_indexer_name(idx));
+
+ git_indexer_free(idx);
+ git_odb_free(odb);
+ git_repository_free(repo);
+
+ /*
+ * The pack's name/hash only tells us what objects there are,
+ * so we need to go through the packfile again in order to
+ * figure out whether we calculated the trailer correctly.
+ */
+ {
+ unsigned char buffer[128];
+ int fd;
+ ssize_t read;
+ struct stat st;
+ const char *name = "pack-fefdb2d740a3a6b6c03a0c7d6ce431c6d5810e13.pack";
+
+ fd = p_open(name, O_RDONLY);
+ cl_assert(fd != -1);
+
+ cl_git_pass(p_stat(name, &st));
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_indexer_new(&idx, ".", GIT_OID_SHA1, NULL));
+#else
+ cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL));
+#endif
+
+ read = p_read(fd, buffer, sizeof(buffer));
+ cl_assert(read != -1);
+ p_close(fd);
+
+ cl_git_pass(git_indexer_append(idx, buffer, read, &stats));
+ cl_git_pass(git_indexer_commit(idx, &stats));
+
+ cl_assert_equal_i(stats.total_objects, 3);
+ cl_assert_equal_i(stats.received_objects, 3);
+ cl_assert_equal_i(stats.indexed_objects, 3);
+ cl_assert_equal_i(stats.local_objects, 0);
+
+ git_indexer_free(idx);
+ }
+}
+
+void test_pack_indexer__corrupt_length(void)
+{
+ git_indexer *idx = NULL;
+ git_indexer_progress stats = { 0 };
+ git_repository *repo;
+ git_odb *odb;
+ git_oid id, should_id;
+ git_indexer_options opts = GIT_INDEXER_OPTIONS_INIT;
+
+ cl_git_pass(git_repository_init(&repo, "thin.git", true));
+ cl_git_pass(git_repository_odb(&odb, repo));
+
+ /* Store the missing base into your ODB so the indexer can fix the pack */
+ cl_git_pass(git_odb_write(&id, odb, base_obj, base_obj_len, GIT_OBJECT_BLOB));
+ git_oid__fromstr(&should_id, "e68fe8129b546b101aee9510c5328e7f21ca1d18", GIT_OID_SHA1);
+ cl_assert_equal_oid(&should_id, &id);
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ opts.odb = odb;
+ cl_git_pass(git_indexer_new(&idx, ".", GIT_OID_SHA1, &opts));
+#else
+ cl_git_pass(git_indexer_new(&idx, ".", 0, odb, &opts));
+#endif
+
+ cl_git_pass(git_indexer_append(
+ idx, corrupt_thin_pack, corrupt_thin_pack_len, &stats));
+ cl_git_fail(git_indexer_commit(idx, &stats));
+
+ cl_assert(git_error_last() != NULL);
+ cl_assert_equal_i(git_error_last()->klass, GIT_ERROR_ZLIB);
+
+ git_indexer_free(idx);
+ git_odb_free(odb);
+ git_repository_free(repo);
+}
+
+void test_pack_indexer__incomplete_pack_fails_with_strict(void)
+{
+ git_indexer_options opts = GIT_INDEXER_OPTIONS_INIT;
+ git_indexer *idx = 0;
+ git_indexer_progress stats = { 0 };
+
+ opts.verify = 1;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_indexer_new(&idx, ".", GIT_OID_SHA1, &opts));
+#else
+ cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, &opts));
+#endif
+
+ cl_git_pass(git_indexer_append(
+ idx, incomplete_pack, incomplete_pack_len, &stats));
+ cl_git_fail(git_indexer_commit(idx, &stats));
+
+ cl_assert_equal_i(stats.total_objects, 2);
+ cl_assert_equal_i(stats.received_objects, 2);
+ cl_assert_equal_i(stats.indexed_objects, 2);
+
+ git_indexer_free(idx);
+}
+
+void test_pack_indexer__out_of_order_with_connectivity_checks(void)
+{
+ git_indexer_options opts = GIT_INDEXER_OPTIONS_INIT;
+ git_indexer *idx = 0;
+ git_indexer_progress stats = { 0 };
+
+ opts.verify = 1;
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_indexer_new(&idx, ".", GIT_OID_SHA1, &opts));
+#else
+ cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, &opts));
+#endif
+
+ cl_git_pass(git_indexer_append(
+ idx, out_of_order_pack, out_of_order_pack_len, &stats));
+ cl_git_pass(git_indexer_commit(idx, &stats));
+
+ cl_assert_equal_i(stats.total_objects, 3);
+ cl_assert_equal_i(stats.received_objects, 3);
+ cl_assert_equal_i(stats.indexed_objects, 3);
+
+ git_indexer_free(idx);
+}
+
+static int find_tmp_file_recurs(void *opaque, git_str *path)
+{
+ int error = 0;
+ git_str *first_tmp_file = opaque;
+ struct stat st;
+
+ if ((error = p_lstat_posixly(path->ptr, &st)) < 0)
+ return error;
+
+ if (S_ISDIR(st.st_mode))
+ return git_fs_path_direach(path, 0, find_tmp_file_recurs, opaque);
+
+ /* This is the template that's used in git_futils_mktmp. */
+ if (strstr(git_str_cstr(path), "_git2_") != NULL)
+ return git_str_sets(first_tmp_file, git_str_cstr(path));
+
+ return 0;
+}
+
+void test_pack_indexer__no_tmp_files(void)
+{
+ git_indexer *idx = NULL;
+ git_str path = GIT_STR_INIT;
+ git_str first_tmp_file = GIT_STR_INIT;
+
+ /* Precondition: there are no temporary files. */
+ cl_git_pass(git_str_sets(&path, clar_sandbox_path()));
+ cl_git_pass(find_tmp_file_recurs(&first_tmp_file, &path));
+ git_str_dispose(&path);
+ cl_assert(git_str_len(&first_tmp_file) == 0);
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_indexer_new(&idx, ".", GIT_OID_SHA1, NULL));
+#else
+ cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL));
+#endif
+
+ git_indexer_free(idx);
+
+ cl_git_pass(git_str_sets(&path, clar_sandbox_path()));
+ cl_git_pass(find_tmp_file_recurs(&first_tmp_file, &path));
+ git_str_dispose(&path);
+ cl_assert(git_str_len(&first_tmp_file) == 0);
+ git_str_dispose(&first_tmp_file);
+}
diff --git a/tests/libgit2/pack/midx.c b/tests/libgit2/pack/midx.c
new file mode 100644
index 0000000..4c4dfc5
--- /dev/null
+++ b/tests/libgit2/pack/midx.c
@@ -0,0 +1,116 @@
+#include "clar_libgit2.h"
+
+#include <git2.h>
+#include <git2/sys/midx.h>
+
+#include "futils.h"
+#include "midx.h"
+
+void test_pack_midx__parse(void)
+{
+ git_repository *repo;
+ struct git_midx_file *idx;
+ struct git_midx_entry e;
+ git_oid id;
+ git_str midx_path = GIT_STR_INIT;
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+ cl_git_pass(git_str_joinpath(&midx_path, git_repository_path(repo), "objects/pack/multi-pack-index"));
+ cl_git_pass(git_midx_open(&idx, git_str_cstr(&midx_path), GIT_OID_SHA1));
+ cl_assert_equal_i(git_midx_needs_refresh(idx, git_str_cstr(&midx_path)), 0);
+
+ cl_git_pass(git_oid__fromstr(&id, "5001298e0c09ad9c34e4249bc5801c75e9754fa5", GIT_OID_SHA1));
+ cl_git_pass(git_midx_entry_find(&e, idx, &id, GIT_OID_SHA1_HEXSIZE));
+ cl_assert_equal_oid(&e.sha1, &id);
+ cl_assert_equal_s(
+ (const char *)git_vector_get(&idx->packfile_names, e.pack_index),
+ "pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx");
+
+ git_midx_free(idx);
+ git_repository_free(repo);
+ git_str_dispose(&midx_path);
+}
+
+void test_pack_midx__lookup(void)
+{
+ git_repository *repo;
+ git_commit *commit;
+ git_oid id;
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+
+ cl_git_pass(git_oid__fromstr(&id, "5001298e0c09ad9c34e4249bc5801c75e9754fa5", GIT_OID_SHA1));
+ cl_git_pass(git_commit_lookup_prefix(&commit, repo, &id, GIT_OID_SHA1_HEXSIZE));
+ cl_assert_equal_s(git_commit_message(commit), "packed commit one\n");
+
+ git_commit_free(commit);
+ git_repository_free(repo);
+}
+
+void test_pack_midx__writer(void)
+{
+ git_repository *repo;
+ git_midx_writer *w = NULL;
+ git_buf midx = GIT_BUF_INIT;
+ git_str expected_midx = GIT_STR_INIT, path = GIT_STR_INIT;
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+
+ cl_git_pass(git_str_joinpath(&path, git_repository_path(repo), "objects/pack"));
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_midx_writer_new(&w, git_str_cstr(&path), GIT_OID_SHA1));
+#else
+ cl_git_pass(git_midx_writer_new(&w, git_str_cstr(&path)));
+#endif
+
+ cl_git_pass(git_midx_writer_add(w, "pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx"));
+ cl_git_pass(git_midx_writer_add(w, "pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx"));
+ cl_git_pass(git_midx_writer_add(w, "pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx"));
+
+ cl_git_pass(git_midx_writer_dump(&midx, w));
+ cl_git_pass(git_str_joinpath(&path, git_repository_path(repo), "objects/pack/multi-pack-index"));
+ cl_git_pass(git_futils_readbuffer(&expected_midx, git_str_cstr(&path)));
+
+ cl_assert_equal_i(midx.size, git_str_len(&expected_midx));
+ cl_assert_equal_strn(midx.ptr, git_str_cstr(&expected_midx), midx.size);
+
+ git_buf_dispose(&midx);
+ git_str_dispose(&expected_midx);
+ git_str_dispose(&path);
+ git_midx_writer_free(w);
+ git_repository_free(repo);
+}
+
+void test_pack_midx__odb_create(void)
+{
+ git_repository *repo;
+ git_odb *odb;
+ git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
+ git_str midx = GIT_STR_INIT, expected_midx = GIT_STR_INIT, midx_path = GIT_STR_INIT;
+ struct stat st;
+
+ opts.bare = true;
+ opts.local = GIT_CLONE_LOCAL;
+ cl_git_pass(git_clone(&repo, cl_fixture("testrepo/.gitted"), "./clone.git", &opts));
+ cl_git_pass(git_str_joinpath(&midx_path, git_repository_path(repo), "objects/pack/multi-pack-index"));
+ cl_git_fail(p_stat(git_str_cstr(&midx_path), &st));
+
+ cl_git_pass(git_repository_odb(&odb, repo));
+ cl_git_pass(git_odb_write_multi_pack_index(odb));
+ git_odb_free(odb);
+
+ cl_git_pass(p_stat(git_str_cstr(&midx_path), &st));
+
+ cl_git_pass(git_futils_readbuffer(&expected_midx, cl_fixture("testrepo.git/objects/pack/multi-pack-index")));
+ cl_git_pass(git_futils_readbuffer(&midx, git_str_cstr(&midx_path)));
+ cl_assert_equal_i(git_str_len(&midx), git_str_len(&expected_midx));
+ cl_assert_equal_strn(git_str_cstr(&midx), git_str_cstr(&expected_midx), git_str_len(&midx));
+
+ git_repository_free(repo);
+ git_str_dispose(&midx);
+ git_str_dispose(&midx_path);
+ git_str_dispose(&expected_midx);
+
+ cl_git_pass(git_futils_rmdir_r("./clone.git", NULL, GIT_RMDIR_REMOVE_FILES));
+}
diff --git a/tests/libgit2/pack/packbuilder.c b/tests/libgit2/pack/packbuilder.c
new file mode 100644
index 0000000..ff3dc1f
--- /dev/null
+++ b/tests/libgit2/pack/packbuilder.c
@@ -0,0 +1,293 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+#include "pack.h"
+#include "hash.h"
+#include "iterator.h"
+#include "vector.h"
+#include "posix.h"
+#include "hash.h"
+
+static git_repository *_repo;
+static git_revwalk *_revwalker;
+static git_packbuilder *_packbuilder;
+static git_indexer *_indexer;
+static git_vector _commits;
+static int _commits_is_initialized;
+static git_indexer_progress _stats;
+
+extern bool git_disable_pack_keep_file_checks;
+
+void test_pack_packbuilder__initialize(void)
+{
+ _repo = cl_git_sandbox_init("testrepo.git");
+ cl_git_pass(p_chdir("testrepo.git"));
+ cl_git_pass(git_revwalk_new(&_revwalker, _repo));
+ cl_git_pass(git_packbuilder_new(&_packbuilder, _repo));
+ cl_git_pass(git_vector_init(&_commits, 0, NULL));
+ _commits_is_initialized = 1;
+ memset(&_stats, 0, sizeof(_stats));
+ p_fsync__cnt = 0;
+}
+
+void test_pack_packbuilder__cleanup(void)
+{
+ git_oid *o;
+ unsigned int i;
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_FSYNC_GITDIR, 0));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_DISABLE_PACK_KEEP_FILE_CHECKS, false));
+
+ if (_commits_is_initialized) {
+ _commits_is_initialized = 0;
+ git_vector_foreach(&_commits, i, o) {
+ git__free(o);
+ }
+ git_vector_free(&_commits);
+ }
+
+ git_packbuilder_free(_packbuilder);
+ _packbuilder = NULL;
+
+ git_revwalk_free(_revwalker);
+ _revwalker = NULL;
+
+ git_indexer_free(_indexer);
+ _indexer = NULL;
+
+ cl_git_pass(p_chdir(".."));
+ cl_git_sandbox_cleanup();
+ _repo = NULL;
+}
+
+static void seed_packbuilder(void)
+{
+ git_oid oid, *o;
+ unsigned int i;
+
+ git_revwalk_sorting(_revwalker, GIT_SORT_TIME);
+ cl_git_pass(git_revwalk_push_ref(_revwalker, "HEAD"));
+
+ while (git_revwalk_next(&oid, _revwalker) == 0) {
+ o = git__malloc(sizeof(git_oid));
+ cl_assert(o != NULL);
+ git_oid_cpy(o, &oid);
+ cl_git_pass(git_vector_insert(&_commits, o));
+ }
+
+ git_vector_foreach(&_commits, i, o) {
+ cl_git_pass(git_packbuilder_insert(_packbuilder, o, NULL));
+ }
+
+ git_vector_foreach(&_commits, i, o) {
+ git_object *obj;
+ cl_git_pass(git_object_lookup(&obj, _repo, o, GIT_OBJECT_COMMIT));
+ cl_git_pass(git_packbuilder_insert_tree(_packbuilder,
+ git_commit_tree_id((git_commit *)obj)));
+ git_object_free(obj);
+ }
+}
+
+static int feed_indexer(void *ptr, size_t len, void *payload)
+{
+ git_indexer_progress *stats = (git_indexer_progress *)payload;
+
+ return git_indexer_append(_indexer, ptr, len, stats);
+}
+
+void test_pack_packbuilder__create_pack(void)
+{
+ git_indexer_progress stats;
+ git_str buf = GIT_STR_INIT, path = GIT_STR_INIT;
+ git_hash_ctx ctx;
+ unsigned char hash[GIT_HASH_SHA1_SIZE];
+ char hex[(GIT_HASH_SHA1_SIZE * 2) + 1];
+
+ seed_packbuilder();
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_indexer_new(&_indexer, ".", GIT_OID_SHA1, NULL));
+#else
+ cl_git_pass(git_indexer_new(&_indexer, ".", 0, NULL, NULL));
+#endif
+
+ cl_git_pass(git_packbuilder_foreach(_packbuilder, feed_indexer, &stats));
+ cl_git_pass(git_indexer_commit(_indexer, &stats));
+
+ git_str_printf(&path, "pack-%s.pack", git_indexer_name(_indexer));
+
+ /*
+ * By default, packfiles are created with only one thread.
+ * Therefore we can predict the object ordering and make sure
+ * we create exactly the same pack as git.git does when *not*
+ * reusing existing deltas (as libgit2).
+ *
+ * $ cd tests/resources/testrepo.git
+ * $ git rev-list --objects HEAD | \
+ * git pack-objects -q --no-reuse-delta --threads=1 pack
+ * $ sha1sum pack-7f5fa362c664d68ba7221259be1cbd187434b2f0.pack
+ * 5d410bdf97cf896f9007681b92868471d636954b
+ *
+ */
+
+ cl_git_pass(git_futils_readbuffer(&buf, git_str_cstr(&path)));
+
+ cl_git_pass(git_hash_ctx_init(&ctx, GIT_HASH_ALGORITHM_SHA1));
+ cl_git_pass(git_hash_update(&ctx, buf.ptr, buf.size));
+ cl_git_pass(git_hash_final(hash, &ctx));
+ git_hash_ctx_cleanup(&ctx);
+
+ git_str_dispose(&path);
+ git_str_dispose(&buf);
+
+ git_hash_fmt(hex, hash, GIT_HASH_SHA1_SIZE);
+ cl_assert_equal_s(hex, "5d410bdf97cf896f9007681b92868471d636954b");
+}
+
+void test_pack_packbuilder__get_name(void)
+{
+ seed_packbuilder();
+
+ cl_git_pass(git_packbuilder_write(_packbuilder, ".", 0, NULL, NULL));
+ cl_assert_equal_s("7f5fa362c664d68ba7221259be1cbd187434b2f0", git_packbuilder_name(_packbuilder));
+}
+
+void test_pack_packbuilder__write_default_path(void)
+{
+ seed_packbuilder();
+
+ cl_git_pass(git_packbuilder_write(_packbuilder, NULL, 0, NULL, NULL));
+ cl_assert(git_fs_path_exists("objects/pack/pack-7f5fa362c664d68ba7221259be1cbd187434b2f0.idx"));
+ cl_assert(git_fs_path_exists("objects/pack/pack-7f5fa362c664d68ba7221259be1cbd187434b2f0.pack"));
+}
+
+static void test_write_pack_permission(mode_t given, mode_t expected)
+{
+ struct stat statbuf;
+ mode_t mask, os_mask;
+
+ seed_packbuilder();
+
+ cl_git_pass(git_packbuilder_write(_packbuilder, ".", given, NULL, NULL));
+
+ /* Windows does not return group/user bits from stat,
+ * files are never executable.
+ */
+#ifdef GIT_WIN32
+ os_mask = 0600;
+#else
+ os_mask = 0777;
+#endif
+
+ mask = p_umask(0);
+ p_umask(mask);
+
+ cl_git_pass(p_stat("pack-7f5fa362c664d68ba7221259be1cbd187434b2f0.idx", &statbuf));
+ cl_assert_equal_i(statbuf.st_mode & os_mask, (expected & ~mask) & os_mask);
+
+ cl_git_pass(p_stat("pack-7f5fa362c664d68ba7221259be1cbd187434b2f0.pack", &statbuf));
+ cl_assert_equal_i(statbuf.st_mode & os_mask, (expected & ~mask) & os_mask);
+}
+
+void test_pack_packbuilder__permissions_standard(void)
+{
+ test_write_pack_permission(0, GIT_PACK_FILE_MODE);
+}
+
+void test_pack_packbuilder__permissions_readonly(void)
+{
+ test_write_pack_permission(0444, 0444);
+}
+
+void test_pack_packbuilder__permissions_readwrite(void)
+{
+ test_write_pack_permission(0666, 0666);
+}
+
+void test_pack_packbuilder__does_not_fsync_by_default(void)
+{
+ seed_packbuilder();
+ cl_git_pass(git_packbuilder_write(_packbuilder, ".", 0666, NULL, NULL));
+ cl_assert_equal_sz(0, p_fsync__cnt);
+}
+
+/* We fsync the packfile and index. On non-Windows, we also fsync
+ * the parent directories.
+ */
+#ifdef GIT_WIN32
+static int expected_fsyncs = 2;
+#else
+static int expected_fsyncs = 4;
+#endif
+
+void test_pack_packbuilder__fsync_global_setting(void)
+{
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_FSYNC_GITDIR, 1));
+ p_fsync__cnt = 0;
+ seed_packbuilder();
+ cl_git_pass(git_packbuilder_write(_packbuilder, ".", 0666, NULL, NULL));
+ cl_assert_equal_sz(expected_fsyncs, p_fsync__cnt);
+}
+
+void test_pack_packbuilder__fsync_repo_setting(void)
+{
+ cl_repo_set_bool(_repo, "core.fsyncObjectFiles", true);
+ p_fsync__cnt = 0;
+ seed_packbuilder();
+ cl_git_pass(git_packbuilder_write(_packbuilder, ".", 0666, NULL, NULL));
+ cl_assert_equal_sz(expected_fsyncs, p_fsync__cnt);
+}
+
+static int foreach_cb(void *buf, size_t len, void *payload)
+{
+ git_indexer *idx = (git_indexer *) payload;
+ cl_git_pass(git_indexer_append(idx, buf, len, &_stats));
+ return 0;
+}
+
+void test_pack_packbuilder__foreach(void)
+{
+ git_indexer *idx;
+
+ seed_packbuilder();
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_indexer_new(&idx, ".", GIT_OID_SHA1, NULL));
+#else
+ cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL));
+#endif
+
+ cl_git_pass(git_packbuilder_foreach(_packbuilder, foreach_cb, idx));
+ cl_git_pass(git_indexer_commit(idx, &_stats));
+ git_indexer_free(idx);
+}
+
+static int foreach_cancel_cb(void *buf, size_t len, void *payload)
+{
+ git_indexer *idx = (git_indexer *)payload;
+ cl_git_pass(git_indexer_append(idx, buf, len, &_stats));
+ return (_stats.total_objects > 2) ? -1111 : 0;
+}
+
+void test_pack_packbuilder__foreach_with_cancel(void)
+{
+ git_indexer *idx;
+
+ seed_packbuilder();
+
+#ifdef GIT_EXPERIMENTAL_SHA256
+ cl_git_pass(git_indexer_new(&idx, ".", GIT_OID_SHA1, NULL));
+#else
+ cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL));
+#endif
+
+ cl_git_fail_with(
+ git_packbuilder_foreach(_packbuilder, foreach_cancel_cb, idx), -1111);
+ git_indexer_free(idx);
+}
+
+void test_pack_packbuilder__keep_file_check(void)
+{
+ assert(!git_disable_pack_keep_file_checks);
+ cl_git_pass(git_libgit2_opts(GIT_OPT_DISABLE_PACK_KEEP_FILE_CHECKS, true));
+ assert(git_disable_pack_keep_file_checks);
+}
diff --git a/tests/libgit2/pack/sharing.c b/tests/libgit2/pack/sharing.c
new file mode 100644
index 0000000..2e2042d
--- /dev/null
+++ b/tests/libgit2/pack/sharing.c
@@ -0,0 +1,42 @@
+#include "clar_libgit2.h"
+#include <git2.h>
+#include "strmap.h"
+#include "mwindow.h"
+#include "pack.h"
+
+extern git_strmap *git__pack_cache;
+
+void test_pack_sharing__open_two_repos(void)
+{
+ git_repository *repo1, *repo2;
+ git_object *obj1, *obj2;
+ git_oid id;
+ size_t pos;
+ void *data;
+ int error;
+
+ cl_git_pass(git_repository_open(&repo1, cl_fixture("testrepo.git")));
+ cl_git_pass(git_repository_open(&repo2, cl_fixture("testrepo.git")));
+
+ git_oid__fromstr(&id, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", GIT_OID_SHA1);
+
+ cl_git_pass(git_object_lookup(&obj1, repo1, &id, GIT_OBJECT_ANY));
+ cl_git_pass(git_object_lookup(&obj2, repo2, &id, GIT_OBJECT_ANY));
+
+ pos = 0;
+ while ((error = git_strmap_iterate(&data, git__pack_cache, &pos, NULL)) == 0) {
+ struct git_pack_file *pack = (struct git_pack_file *) data;
+
+ cl_assert_equal_i(2, pack->refcount.val);
+ }
+
+ cl_assert_equal_i(3, git_strmap_size(git__pack_cache));
+
+ git_object_free(obj1);
+ git_object_free(obj2);
+ git_repository_free(repo1);
+ git_repository_free(repo2);
+
+ /* we don't want to keep the packs open after the repos go away */
+ cl_assert_equal_i(0, git_strmap_size(git__pack_cache));
+}
diff --git a/tests/libgit2/pack/threadsafety.c b/tests/libgit2/pack/threadsafety.c
new file mode 100644
index 0000000..fd6a61f
--- /dev/null
+++ b/tests/libgit2/pack/threadsafety.c
@@ -0,0 +1,62 @@
+#include "clar_libgit2.h"
+#include "pool.h"
+
+#include <git2.h>
+#include "git2/sys/commit.h"
+#include "git2/sys/mempack.h"
+
+static size_t original_mwindow_file_limit = 0;
+
+void test_pack_threadsafety__initialize(void)
+{
+ size_t open_mwindow_files = 1;
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_GET_MWINDOW_FILE_LIMIT, &original_mwindow_file_limit));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_MWINDOW_FILE_LIMIT, open_mwindow_files));
+}
+
+void test_pack_threadsafety__cleanup(void)
+{
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_MWINDOW_FILE_LIMIT, original_mwindow_file_limit));
+}
+
+#ifdef GIT_THREADS
+static void *get_status(void *arg)
+{
+ const char *repo_path = (const char *)arg;
+ git_repository *repo;
+ git_status_list *status;
+
+ cl_git_pass(git_repository_open(&repo, repo_path));
+ cl_git_pass(git_status_list_new(&status, repo, NULL));
+ git_status_list_free(status);
+ git_repository_free(repo);
+
+ return NULL;
+}
+#endif
+
+void test_pack_threadsafety__open_repo_in_multiple_threads(void)
+{
+#ifdef GIT_THREADS
+ const char *repo_path = cl_fixture("../..");
+ git_repository *repo;
+ git_thread threads[8];
+ size_t i;
+
+ /* If we can't open the libgit2 repo or if it isn't a full repo
+ * with proper history, just skip this test */
+ if (git_repository_open(&repo, repo_path) < 0)
+ cl_skip();
+ if (git_repository_is_shallow(repo))
+ cl_skip();
+ git_repository_free(repo);
+
+ for (i = 0; i < ARRAY_SIZE(threads); i++)
+ git_thread_create(&threads[i], get_status, (void *)repo_path);
+ for (i = 0; i < ARRAY_SIZE(threads); i++)
+ git_thread_join(&threads[i], NULL);
+#else
+ cl_skip();
+#endif
+}
diff --git a/tests/libgit2/patch/parse.c b/tests/libgit2/patch/parse.c
new file mode 100644
index 0000000..d90576e
--- /dev/null
+++ b/tests/libgit2/patch/parse.c
@@ -0,0 +1,221 @@
+#include "clar_libgit2.h"
+#include "patch.h"
+#include "patch_parse.h"
+
+#include "patch_common.h"
+
+static void ensure_patch_validity(git_patch *patch)
+{
+ const git_diff_delta *delta;
+ char idstr[GIT_OID_SHA1_HEXSIZE+1] = {0};
+
+ cl_assert((delta = git_patch_get_delta(patch)) != NULL);
+ cl_assert_equal_i(2, delta->nfiles);
+
+ cl_assert_equal_s(delta->old_file.path, "file.txt");
+ cl_assert(delta->old_file.mode == GIT_FILEMODE_BLOB);
+ cl_assert_equal_i(7, delta->old_file.id_abbrev);
+ git_oid_nfmt(idstr, delta->old_file.id_abbrev, &delta->old_file.id);
+ cl_assert_equal_s(idstr, "9432026");
+ cl_assert_equal_i(0, delta->old_file.size);
+
+ cl_assert_equal_s(delta->new_file.path, "file.txt");
+ cl_assert(delta->new_file.mode == GIT_FILEMODE_BLOB);
+ cl_assert_equal_i(7, delta->new_file.id_abbrev);
+ git_oid_nfmt(idstr, delta->new_file.id_abbrev, &delta->new_file.id);
+ cl_assert_equal_s(idstr, "cd8fd12");
+ cl_assert_equal_i(0, delta->new_file.size);
+}
+
+static void ensure_identical_patch_inout(const char *content)
+{
+ git_buf buf = GIT_BUF_INIT;
+ git_patch *patch;
+
+ cl_git_pass(git_patch_from_buffer(&patch, content, strlen(content), NULL));
+ cl_git_pass(git_patch_to_buf(&buf, patch));
+ cl_assert_equal_strn(buf.ptr, content, strlen(content));
+
+ git_patch_free(patch);
+ git_buf_dispose(&buf);
+}
+
+void test_patch_parse__original_to_change_middle(void)
+{
+ git_patch *patch;
+
+ cl_git_pass(git_patch_from_buffer(
+ &patch, PATCH_ORIGINAL_TO_CHANGE_MIDDLE,
+ strlen(PATCH_ORIGINAL_TO_CHANGE_MIDDLE), NULL));
+ ensure_patch_validity(patch);
+ git_patch_free(patch);
+}
+
+void test_patch_parse__leading_and_trailing_garbage(void)
+{
+ git_patch *patch;
+ const char *leading = "This is some leading garbage.\n"
+ "Maybe it's email headers?\n"
+ "\n"
+ PATCH_ORIGINAL_TO_CHANGE_MIDDLE;
+ const char *trailing = PATCH_ORIGINAL_TO_CHANGE_MIDDLE
+ "\n"
+ "This is some trailing garbage.\n"
+ "Maybe it's an email signature?\n";
+ const char *both = "Here's some leading garbage\n"
+ PATCH_ORIGINAL_TO_CHANGE_MIDDLE
+ "And here's some trailing.\n";
+
+ cl_git_pass(git_patch_from_buffer(&patch, leading, strlen(leading),
+ NULL));
+ ensure_patch_validity(patch);
+ git_patch_free(patch);
+
+ cl_git_pass(git_patch_from_buffer(&patch, trailing, strlen(trailing),
+ NULL));
+ ensure_patch_validity(patch);
+ git_patch_free(patch);
+
+ cl_git_pass(git_patch_from_buffer(&patch, both, strlen(both),
+ NULL));
+ ensure_patch_validity(patch);
+ git_patch_free(patch);
+}
+
+void test_patch_parse__nonpatches_fail_with_notfound(void)
+{
+ git_patch *patch;
+
+ cl_git_fail_with(GIT_ENOTFOUND,
+ git_patch_from_buffer(&patch, PATCH_NOT_A_PATCH,
+ strlen(PATCH_NOT_A_PATCH), NULL));
+}
+
+void test_patch_parse__invalid_patches_fails(void)
+{
+ git_patch *patch;
+
+ cl_git_fail_with(GIT_ERROR,
+ git_patch_from_buffer(&patch, PATCH_CORRUPT_GIT_HEADER,
+ strlen(PATCH_CORRUPT_GIT_HEADER), NULL));
+ cl_git_fail_with(GIT_ERROR,
+ git_patch_from_buffer(&patch,
+ PATCH_CORRUPT_MISSING_NEW_FILE,
+ strlen(PATCH_CORRUPT_MISSING_NEW_FILE), NULL));
+ cl_git_fail_with(GIT_ERROR,
+ git_patch_from_buffer(&patch,
+ PATCH_CORRUPT_MISSING_OLD_FILE,
+ strlen(PATCH_CORRUPT_MISSING_OLD_FILE), NULL));
+ cl_git_fail_with(GIT_ERROR,
+ git_patch_from_buffer(&patch, PATCH_CORRUPT_NO_CHANGES,
+ strlen(PATCH_CORRUPT_NO_CHANGES), NULL));
+ cl_git_fail_with(GIT_ERROR,
+ git_patch_from_buffer(&patch,
+ PATCH_CORRUPT_MISSING_HUNK_HEADER,
+ strlen(PATCH_CORRUPT_MISSING_HUNK_HEADER), NULL));
+}
+
+void test_patch_parse__no_newline_at_end_of_new_file(void)
+{
+ ensure_identical_patch_inout(PATCH_APPEND_NO_NL);
+}
+
+void test_patch_parse__no_newline_at_end_of_old_file(void)
+{
+ ensure_identical_patch_inout(PATCH_APPEND_NO_NL_IN_OLD_FILE);
+}
+
+void test_patch_parse__files_with_whitespaces_succeeds(void)
+{
+ ensure_identical_patch_inout(PATCH_NAME_WHITESPACE);
+}
+
+void test_patch_parse__lifetime_of_patch_does_not_depend_on_buffer(void)
+{
+ git_str diff = GIT_STR_INIT;
+ git_buf rendered = GIT_BUF_INIT;
+ git_patch *patch;
+
+ cl_git_pass(git_str_sets(&diff, PATCH_ORIGINAL_TO_CHANGE_MIDDLE));
+ cl_git_pass(git_patch_from_buffer(&patch, diff.ptr, diff.size, NULL));
+ git_str_dispose(&diff);
+
+ cl_git_pass(git_patch_to_buf(&rendered, patch));
+ cl_assert_equal_s(PATCH_ORIGINAL_TO_CHANGE_MIDDLE, rendered.ptr);
+ git_buf_dispose(&rendered);
+
+ cl_git_pass(git_patch_to_buf(&rendered, patch));
+ cl_assert_equal_s(PATCH_ORIGINAL_TO_CHANGE_MIDDLE, rendered.ptr);
+ git_buf_dispose(&rendered);
+
+ git_patch_free(patch);
+}
+
+void test_patch_parse__binary_file_with_missing_paths(void)
+{
+ git_patch *patch;
+ cl_git_fail(git_patch_from_buffer(&patch, PATCH_BINARY_FILE_WITH_MISSING_PATHS,
+ strlen(PATCH_BINARY_FILE_WITH_MISSING_PATHS), NULL));
+}
+
+void test_patch_parse__binary_file_with_whitespace_paths(void)
+{
+ git_patch *patch;
+ cl_git_fail(git_patch_from_buffer(&patch, PATCH_BINARY_FILE_WITH_WHITESPACE_PATHS,
+ strlen(PATCH_BINARY_FILE_WITH_WHITESPACE_PATHS), NULL));
+}
+
+void test_patch_parse__binary_file_with_empty_quoted_paths(void)
+{
+ git_patch *patch;
+ cl_git_fail(git_patch_from_buffer(&patch, PATCH_BINARY_FILE_WITH_QUOTED_EMPTY_PATHS,
+ strlen(PATCH_BINARY_FILE_WITH_QUOTED_EMPTY_PATHS), NULL));
+}
+
+void test_patch_parse__binary_file_path_with_spaces(void)
+{
+ git_patch *patch;
+ cl_git_fail(git_patch_from_buffer(&patch, PATCH_BINARY_FILE_PATH_WITH_SPACES,
+ strlen(PATCH_BINARY_FILE_PATH_WITH_SPACES), NULL));
+}
+
+void test_patch_parse__binary_file_path_without_body_paths(void)
+{
+ git_patch *patch;
+ cl_git_fail(git_patch_from_buffer(&patch, PATCH_BINARY_FILE_PATH_WITHOUT_BODY_PATHS,
+ strlen(PATCH_BINARY_FILE_PATH_WITHOUT_BODY_PATHS), NULL));
+}
+
+void test_patch_parse__binary_file_with_truncated_delta(void)
+{
+ git_patch *patch;
+ cl_git_fail(git_patch_from_buffer(&patch, PATCH_BINARY_FILE_WITH_TRUNCATED_DELTA,
+ strlen(PATCH_BINARY_FILE_WITH_TRUNCATED_DELTA), NULL));
+ cl_assert_equal_s(git_error_last()->message, "truncated binary data at line 5");
+}
+
+void test_patch_parse__memory_leak_on_multiple_paths(void)
+{
+ git_patch *patch;
+ cl_git_fail(git_patch_from_buffer(&patch, PATCH_MULTIPLE_OLD_PATHS, strlen(PATCH_MULTIPLE_OLD_PATHS), NULL));
+}
+
+void test_patch_parse__truncated_no_newline_at_end_of_file(void)
+{
+ size_t len = strlen(PATCH_APPEND_NO_NL) - strlen("at end of file\n");
+ const git_diff_line *line;
+ git_patch *patch;
+
+ cl_git_pass(git_patch_from_buffer(&patch, PATCH_APPEND_NO_NL, len, NULL));
+ cl_git_pass(git_patch_get_line_in_hunk(&line, patch, 0, 4));
+ cl_assert_equal_s(line->content, "\\ No newline ");
+
+ git_patch_free(patch);
+}
+
+void test_patch_parse__line_number_overflow(void)
+{
+ git_patch *patch;
+ cl_git_fail(git_patch_from_buffer(&patch, PATCH_INTMAX_NEW_LINES, strlen(PATCH_INTMAX_NEW_LINES), NULL));
+ git_patch_free(patch);
+}
diff --git a/tests/libgit2/patch/patch_common.h b/tests/libgit2/patch/patch_common.h
new file mode 100644
index 0000000..7e2cb6a
--- /dev/null
+++ b/tests/libgit2/patch/patch_common.h
@@ -0,0 +1,1014 @@
+/* The original file contents */
+
+#define FILE_ORIGINAL \
+ "hey!\n" \
+ "this is some context!\n" \
+ "around some lines\n" \
+ "that will change\n" \
+ "yes it is!\n" \
+ "(this line is changed)\n" \
+ "and this\n" \
+ "is additional context\n" \
+ "below it!\n"
+
+/* A change in the middle of the file (and the resultant patch) */
+
+#define FILE_CHANGE_MIDDLE \
+ "hey!\n" \
+ "this is some context!\n" \
+ "around some lines\n" \
+ "that will change\n" \
+ "yes it is!\n" \
+ "(THIS line is changed!)\n" \
+ "and this\n" \
+ "is additional context\n" \
+ "below it!\n"
+
+#define PATCH_ORIGINAL_TO_CHANGE_MIDDLE \
+ "diff --git a/file.txt b/file.txt\n" \
+ "index 9432026..cd8fd12 100644\n" \
+ "--- a/file.txt\n" \
+ "+++ b/file.txt\n" \
+ "@@ -3,7 +3,7 @@ this is some context!\n" \
+ " around some lines\n" \
+ " that will change\n" \
+ " yes it is!\n" \
+ "-(this line is changed)\n" \
+ "+(THIS line is changed!)\n" \
+ " and this\n" \
+ " is additional context\n" \
+ " below it!\n"
+
+#define PATCH_ORIGINAL_TO_CHANGE_MIDDLE_NOCONTEXT \
+ "diff --git a/file.txt b/file.txt\n" \
+ "index 9432026..cd8fd12 100644\n" \
+ "--- a/file.txt\n" \
+ "+++ b/file.txt\n" \
+ "@@ -6 +6 @@ yes it is!\n" \
+ "-(this line is changed)\n" \
+ "+(THIS line is changed!)\n"
+
+/* A change of the first line (and the resultant patch) */
+
+#define FILE_CHANGE_FIRSTLINE \
+ "hey, change in head!\n" \
+ "this is some context!\n" \
+ "around some lines\n" \
+ "that will change\n" \
+ "yes it is!\n" \
+ "(this line is changed)\n" \
+ "and this\n" \
+ "is additional context\n" \
+ "below it!\n"
+
+#define PATCH_ORIGINAL_TO_CHANGE_FIRSTLINE \
+ "diff --git a/file.txt b/file.txt\n" \
+ "index 9432026..c81df1d 100644\n" \
+ "--- a/file.txt\n" \
+ "+++ b/file.txt\n" \
+ "@@ -1,4 +1,4 @@\n" \
+ "-hey!\n" \
+ "+hey, change in head!\n" \
+ " this is some context!\n" \
+ " around some lines\n" \
+ " that will change\n"
+
+/* A change of the last line (and the resultant patch) */
+
+#define FILE_CHANGE_LASTLINE \
+ "hey!\n" \
+ "this is some context!\n" \
+ "around some lines\n" \
+ "that will change\n" \
+ "yes it is!\n" \
+ "(this line is changed)\n" \
+ "and this\n" \
+ "is additional context\n" \
+ "change to the last line.\n"
+
+#define PATCH_ORIGINAL_TO_CHANGE_LASTLINE \
+ "diff --git a/file.txt b/file.txt\n" \
+ "index 9432026..f70db1c 100644\n" \
+ "--- a/file.txt\n" \
+ "+++ b/file.txt\n" \
+ "@@ -6,4 +6,4 @@ yes it is!\n" \
+ " (this line is changed)\n" \
+ " and this\n" \
+ " is additional context\n" \
+ "-below it!\n" \
+ "+change to the last line.\n"
+
+/* A change of the middle where we remove many lines */
+
+#define FILE_CHANGE_MIDDLE_SHRINK \
+ "hey!\n" \
+ "i've changed a lot, but left the line\n" \
+ "below it!\n"
+
+#define PATCH_ORIGINAL_TO_CHANGE_MIDDLE_SHRINK \
+ "diff --git a/file.txt b/file.txt\n" \
+ "index 9432026..629cd35 100644\n" \
+ "--- a/file.txt\n" \
+ "+++ b/file.txt\n" \
+ "@@ -1,9 +1,3 @@\n" \
+ " hey!\n" \
+ "-this is some context!\n" \
+ "-around some lines\n" \
+ "-that will change\n" \
+ "-yes it is!\n" \
+ "-(this line is changed)\n" \
+ "-and this\n" \
+ "-is additional context\n" \
+ "+i've changed a lot, but left the line\n" \
+ " below it!\n"
+
+#define PATCH_ORIGINAL_TO_MIDDLE_SHRINK_NOCONTEXT \
+ "diff --git a/file.txt b/file.txt\n" \
+ "index 9432026..629cd35 100644\n" \
+ "--- a/file.txt\n" \
+ "+++ b/file.txt\n" \
+ "@@ -2,7 +2 @@ hey!\n" \
+ "-this is some context!\n" \
+ "-around some lines\n" \
+ "-that will change\n" \
+ "-yes it is!\n" \
+ "-(this line is changed)\n" \
+ "-and this\n" \
+ "-is additional context\n" \
+ "+i've changed a lot, but left the line\n"
+
+/* A change to the middle where we grow many lines */
+
+#define FILE_CHANGE_MIDDLE_GROW \
+ "hey!\n" \
+ "this is some context!\n" \
+ "around some lines\n" \
+ "that will change\n" \
+ "yes it is!\n" \
+ "this line is changed\n" \
+ "and this line is added\n" \
+ "so is this\n" \
+ "(this too)\n" \
+ "whee...\n" \
+ "and this\n" \
+ "is additional context\n" \
+ "below it!\n"
+
+#define PATCH_ORIGINAL_TO_CHANGE_MIDDLE_GROW \
+ "diff --git a/file.txt b/file.txt\n" \
+ "index 9432026..207ebca 100644\n" \
+ "--- a/file.txt\n" \
+ "+++ b/file.txt\n" \
+ "@@ -3,7 +3,11 @@ this is some context!\n" \
+ " around some lines\n" \
+ " that will change\n" \
+ " yes it is!\n" \
+ "-(this line is changed)\n" \
+ "+this line is changed\n" \
+ "+and this line is added\n" \
+ "+so is this\n" \
+ "+(this too)\n" \
+ "+whee...\n" \
+ " and this\n" \
+ " is additional context\n" \
+ " below it!\n"
+
+
+#define PATCH_ORIGINAL_TO_MIDDLE_GROW_NOCONTEXT \
+ "diff --git a/file.txt b/file.txt\n" \
+ "index 9432026..207ebca 100644\n" \
+ "--- a/file.txt\n" \
+ "+++ b/file.txt\n" \
+ "@@ -6 +6,5 @@ yes it is!\n" \
+ "-(this line is changed)\n" \
+ "+this line is changed\n" \
+ "+and this line is added\n" \
+ "+so is this\n" \
+ "+(this too)\n" \
+ "+whee...\n"
+
+/* An insertion at the beginning of the file (and the resultant patch) */
+
+#define FILE_PREPEND \
+ "insert at front\n" \
+ "hey!\n" \
+ "this is some context!\n" \
+ "around some lines\n" \
+ "that will change\n" \
+ "yes it is!\n" \
+ "(this line is changed)\n" \
+ "and this\n" \
+ "is additional context\n" \
+ "below it!\n"
+
+#define PATCH_ORIGINAL_TO_PREPEND \
+ "diff --git a/file.txt b/file.txt\n" \
+ "index 9432026..0f39b9a 100644\n" \
+ "--- a/file.txt\n" \
+ "+++ b/file.txt\n" \
+ "@@ -1,3 +1,4 @@\n" \
+ "+insert at front\n" \
+ " hey!\n" \
+ " this is some context!\n" \
+ " around some lines\n"
+
+#define PATCH_ORIGINAL_TO_PREPEND_NOCONTEXT \
+ "diff --git a/file.txt b/file.txt\n" \
+ "index 9432026..0f39b9a 100644\n" \
+ "--- a/file.txt\n" \
+ "+++ b/file.txt\n" \
+ "@@ -0,0 +1 @@\n" \
+ "+insert at front\n"
+
+/* An insertion at the beginning of the file and change in the middle */
+
+#define FILE_PREPEND_AND_CHANGE \
+ "insert at front\n" \
+ "hey!\n" \
+ "this is some context!\n" \
+ "around some lines\n" \
+ "that will change\n" \
+ "yes it is!\n" \
+ "(THIS line is changed!)\n" \
+ "and this\n" \
+ "is additional context\n" \
+ "below it!\n"
+
+#define PATCH_ORIGINAL_TO_PREPEND_AND_CHANGE \
+ "diff --git a/file.txt b/file.txt\n" \
+ "index 9432026..f73c8bb 100644\n" \
+ "--- a/file.txt\n" \
+ "+++ b/file.txt\n" \
+ "@@ -1,9 +1,10 @@\n" \
+ "+insert at front\n" \
+ " hey!\n" \
+ " this is some context!\n" \
+ " around some lines\n" \
+ " that will change\n" \
+ " yes it is!\n" \
+ "-(this line is changed)\n" \
+ "+(THIS line is changed!)\n" \
+ " and this\n" \
+ " is additional context\n" \
+ " below it!\n"
+
+#define PATCH_ORIGINAL_TO_PREPEND_AND_CHANGE_NOCONTEXT \
+ "diff --git a/file.txt b/file.txt\n" \
+ "index 9432026..f73c8bb 100644\n" \
+ "--- a/file.txt\n" \
+ "+++ b/file.txt\n" \
+ "@@ -0,0 +1 @@\n" \
+ "+insert at front\n" \
+ "@@ -6 +7 @@ yes it is!\n" \
+ "-(this line is changed)\n" \
+ "+(THIS line is changed!)\n"
+
+/* A change in the middle and a deletion of the newline at the end of the file */
+
+#define FILE_CHANGE_MIDDLE_AND_LASTLINE \
+ "hey!\n" \
+ "this is some context!\n" \
+ "around some lines\n" \
+ "that will change\n" \
+ "yes it is!\n" \
+ "(THIS line is changed!)\n" \
+ "and this\n" \
+ "is additional context\n" \
+ "BELOW it! - (THIS line is changed!)"
+
+#define PATCH_ORIGINAL_TO_CHANGE_MIDDLE_AND_LASTLINE_NOCONTEXT \
+ "diff --git a/file.txt b/file.txt\n" \
+ "index 9432026..e05d36c 100644\n" \
+ "--- a/file.txt\n" \
+ "+++ b/file.txt\n" \
+ "@@ -6 +6 @@ yes it is!\n" \
+ "-(this line is changed)\n" \
+ "+(THIS line is changed!)\n" \
+ "@@ -9 +9 @@ is additional context\n" \
+ "-below it!\n" \
+ "+BELOW it! - (THIS line is changed!)\n" \
+ "\\ No newline at end of file\n"
+
+/* A deletion at the beginning of the file and a change in the middle */
+
+#define FILE_DELETE_AND_CHANGE \
+ "this is some context!\n" \
+ "around some lines\n" \
+ "that will change\n" \
+ "yes it is!\n" \
+ "(THIS line is changed!)\n" \
+ "and this\n" \
+ "is additional context\n" \
+ "below it!\n"
+
+#define PATCH_ORIGINAL_TO_DELETE_AND_CHANGE \
+ "diff --git a/file.txt b/file.txt\n" \
+ "index 9432026..1e2dfa6 100644\n" \
+ "--- a/file.txt\n" \
+ "+++ b/file.txt\n" \
+ "@@ -1,9 +1,8 @@\n" \
+ "-hey!\n" \
+ " this is some context!\n" \
+ " around some lines\n" \
+ " that will change\n" \
+ " yes it is!\n" \
+ "-(this line is changed)\n" \
+ "+(THIS line is changed!)\n" \
+ " and this\n" \
+ " is additional context\n" \
+ " below it!\n"
+
+#define PATCH_ORIGINAL_TO_DELETE_AND_CHANGE_NOCONTEXT \
+ "diff --git a/file.txt b/file.txt\n" \
+ "index 9432026..1e2dfa6 100644\n" \
+ "--- a/file.txt\n" \
+ "+++ b/file.txt\n" \
+ "@@ -1 +0,0 @@\n" \
+ "-hey!\n" \
+ "@@ -6 +5 @@ yes it is!\n" \
+ "-(this line is changed)\n" \
+ "+(THIS line is changed!)\n"
+
+/* A deletion at the beginning of the file */
+
+#define FILE_DELETE_FIRSTLINE \
+ "this is some context!\n" \
+ "around some lines\n" \
+ "that will change\n" \
+ "yes it is!\n" \
+ "(this line is changed)\n" \
+ "and this\n" \
+ "is additional context\n" \
+ "below it!\n"
+
+#define PATCH_ORIGINAL_TO_DELETE_FIRSTLINE \
+ "diff --git a/file.txt b/file.txt\n" \
+ "index 9432026..f31fa13 100644\n" \
+ "--- a/file.txt\n" \
+ "+++ b/file.txt\n" \
+ "@@ -1,4 +1,3 @@\n" \
+ "-hey!\n" \
+ " this is some context!\n" \
+ " around some lines\n" \
+ " that will change\n"
+
+/* An insertion at the end of the file (and the resultant patch) */
+
+#define FILE_APPEND \
+ "hey!\n" \
+ "this is some context!\n" \
+ "around some lines\n" \
+ "that will change\n" \
+ "yes it is!\n" \
+ "(this line is changed)\n" \
+ "and this\n" \
+ "is additional context\n" \
+ "below it!\n" \
+ "insert at end\n"
+
+#define PATCH_ORIGINAL_TO_APPEND \
+ "diff --git a/file.txt b/file.txt\n" \
+ "index 9432026..72788bb 100644\n" \
+ "--- a/file.txt\n" \
+ "+++ b/file.txt\n" \
+ "@@ -7,3 +7,4 @@ yes it is!\n" \
+ " and this\n" \
+ " is additional context\n" \
+ " below it!\n" \
+ "+insert at end\n"
+
+#define PATCH_ORIGINAL_TO_APPEND_NOCONTEXT \
+ "diff --git a/file.txt b/file.txt\n" \
+ "index 9432026..72788bb 100644\n" \
+ "--- a/file.txt\n" \
+ "+++ b/file.txt\n" \
+ "@@ -9,0 +10 @@ below it!\n" \
+ "+insert at end\n"
+
+#define PATCH_DELETED_FILE_2_HUNKS \
+ "diff --git a/a b/a\n" \
+ "index 7f129fd..af431f2 100644\n" \
+ "--- a/a\n" \
+ "+++ b/a\n" \
+ "@@ -1 +1 @@\n" \
+ "-a contents 2\n" \
+ "+a contents\n" \
+ "diff --git a/c/d b/c/d\n" \
+ "deleted file mode 100644\n" \
+ "index 297efb8..0000000\n" \
+ "--- a/c/d\n" \
+ "+++ /dev/null\n" \
+ "@@ -1 +0,0 @@\n" \
+ "-c/d contents\n"
+
+#define PATCH_DELETED_FILE_2_HUNKS_SHUFFLED \
+ "diff --git a/c/d b/c/d\n" \
+ "deleted file mode 100644\n" \
+ "index 297efb8..0000000\n" \
+ "--- a/c/d\n" \
+ "+++ /dev/null\n" \
+ "@@ -1 +0,0 @@\n" \
+ "-c/d contents\n" \
+ "diff --git a/a b/a\n" \
+ "index 7f129fd..af431f2 100644\n" \
+ "--- a/a\n" \
+ "+++ b/a\n" \
+ "@@ -1 +1 @@\n" \
+ "-a contents 2\n" \
+ "+a contents\n"
+
+#define PATCH_SIMPLE_COMMIT \
+ "commit 15e119375018fba121cf58e02a9f17fe22df0df8\n" \
+ "Author: Edward Thomson <ethomson@edwardthomson.com>\n" \
+ "Date: Wed Jun 14 13:31:20 2017 +0200\n" \
+ "\n" \
+ " CHANGELOG: document git_filter_init and GIT_FILTER_INIT\n" \
+ "\n" \
+ "diff --git a/CHANGELOG.md b/CHANGELOG.md\n" \
+ "index 1b9e0c90a..24ecba426 100644\n" \
+ "--- a/CHANGELOG.md\n" \
+ "+++ b/CHANGELOG.md\n" \
+ "@@ -96,6 +96,9 @@ v0.26\n" \
+ " * `git_transport_smart_proxy_options()' enables you to get the proxy options for\n" \
+ " smart transports.\n" \
+ "\n" \
+ "+* The `GIT_FILTER_INIT` macro and the `git_filter_init` function are provided\n" \
+ "+ to initialize a `git_filter` structure.\n" \
+ "+\n" \
+ " ### Breaking API changes\n" \
+ "\n" \
+ " * `clone_checkout_strategy` has been removed from\n"
+
+#define PATCH_MULTIPLE_HUNKS \
+ "diff --git a/x b/x\n" \
+ "index 0719398..fa0350c 100644\n" \
+ "--- a/x\n" \
+ "+++ b/x\n" \
+ "@@ -1,5 +1,4 @@\n" \
+ " 1\n" \
+ "-2\n" \
+ " 3\n" \
+ " 4\n" \
+ " 5\n" \
+ "@@ -7,3 +6,4 @@\n" \
+ " 7\n" \
+ " 8\n" \
+ " 9\n" \
+ "+10\n"
+
+#define PATCH_MULTIPLE_FILES \
+ "diff --git a/x b/x\n" \
+ "index 8a1218a..7059ba5 100644\n" \
+ "--- a/x\n" \
+ "+++ b/x\n" \
+ "@@ -1,5 +1,4 @@\n" \
+ " 1\n" \
+ " 2\n" \
+ "-3\n" \
+ " 4\n" \
+ " 5\n" \
+ "diff --git a/y b/y\n" \
+ "index e006065..9405325 100644\n" \
+ "--- a/y\n" \
+ "+++ b/y\n" \
+ "@@ -1,4 +1,5 @@\n" \
+ " a\n" \
+ " b\n" \
+ "+c\n" \
+ " d\n" \
+ " e\n"
+
+#define FILE_PREPEND_AND_APPEND \
+ "first and\n" \
+ "this is some context!\n" \
+ "around some lines\n" \
+ "that will change\n" \
+ "yes it is!\n" \
+ "(this line is changed)\n" \
+ "and this\n" \
+ "is additional context\n" \
+ "last lines\n"
+
+#define PATCH_ORIGINAL_TO_PREPEND_AND_APPEND \
+ "diff --git a/file.txt b/file.txt\n" \
+ "index 9432026..f282430 100644\n" \
+ "--- a/file.txt\n" \
+ "+++ b/file.txt\n" \
+ "@@ -1,4 +1,4 @@\n" \
+ "-hey!\n" \
+ "+first and\n" \
+ " this is some context!\n" \
+ " around some lines\n" \
+ " that will change\n" \
+ "@@ -6,4 +6,4 @@ yes it is!\n" \
+ " (this line is changed)\n" \
+ " and this\n" \
+ " is additional context\n" \
+ "-below it!\n" \
+ "+last lines\n"
+
+#define PATCH_ORIGINAL_TO_EMPTY_FILE \
+ "diff --git a/file.txt b/file.txt\n" \
+ "index 9432026..e69de29 100644\n" \
+ "--- a/file.txt\n" \
+ "+++ b/file.txt\n" \
+ "@@ -1,9 +0,0 @@\n" \
+ "-hey!\n" \
+ "-this is some context!\n" \
+ "-around some lines\n" \
+ "-that will change\n" \
+ "-yes it is!\n" \
+ "-(this line is changed)\n" \
+ "-and this\n" \
+ "-is additional context\n" \
+ "-below it!\n"
+
+#define PATCH_EMPTY_FILE_TO_ORIGINAL \
+ "diff --git a/file.txt b/file.txt\n" \
+ "index e69de29..9432026 100644\n" \
+ "--- a/file.txt\n" \
+ "+++ b/file.txt\n" \
+ "@@ -0,0 +1,9 @@\n" \
+ "+hey!\n" \
+ "+this is some context!\n" \
+ "+around some lines\n" \
+ "+that will change\n" \
+ "+yes it is!\n" \
+ "+(this line is changed)\n" \
+ "+and this\n" \
+ "+is additional context\n" \
+ "+below it!\n"
+
+#define PATCH_ADD_ORIGINAL \
+ "diff --git a/file.txt b/file.txt\n" \
+ "new file mode 100644\n" \
+ "index 0000000..9432026\n" \
+ "--- /dev/null\n" \
+ "+++ b/file.txt\n" \
+ "@@ -0,0 +1,9 @@\n" \
+ "+hey!\n" \
+ "+this is some context!\n" \
+ "+around some lines\n" \
+ "+that will change\n" \
+ "+yes it is!\n" \
+ "+(this line is changed)\n" \
+ "+and this\n" \
+ "+is additional context\n" \
+ "+below it!\n"
+
+#define PATCH_DELETE_ORIGINAL \
+ "diff --git a/file.txt b/file.txt\n" \
+ "deleted file mode 100644\n" \
+ "index 9432026..0000000\n" \
+ "--- a/file.txt\n" \
+ "+++ /dev/null\n" \
+ "@@ -1,9 +0,0 @@\n" \
+ "-hey!\n" \
+ "-this is some context!\n" \
+ "-around some lines\n" \
+ "-that will change\n" \
+ "-yes it is!\n" \
+ "-(this line is changed)\n" \
+ "-and this\n" \
+ "-is additional context\n" \
+ "-below it!\n"
+
+#define PATCH_RENAME_EXACT \
+ "diff --git a/file.txt b/newfile.txt\n" \
+ "similarity index 100%\n" \
+ "rename from file.txt\n" \
+ "rename to newfile.txt\n"
+
+#define PATCH_RENAME_EXACT_WITH_MODE \
+ "diff --git a/RENAMED.md b/README.md\n" \
+ "old mode 100644\n" \
+ "new mode 100755\n" \
+ "similarity index 100%\n" \
+ "rename from RENAMED.md\n" \
+ "rename to README.md\n"
+
+#define PATCH_RENAME_SIMILAR \
+ "diff --git a/file.txt b/newfile.txt\n" \
+ "similarity index 77%\n" \
+ "rename from file.txt\n" \
+ "rename to newfile.txt\n" \
+ "index 9432026..cd8fd12 100644\n" \
+ "--- a/file.txt\n" \
+ "+++ b/newfile.txt\n" \
+ "@@ -3,7 +3,7 @@ this is some context!\n" \
+ " around some lines\n" \
+ " that will change\n" \
+ " yes it is!\n" \
+ "-(this line is changed)\n" \
+ "+(THIS line is changed!)\n" \
+ " and this\n" \
+ " is additional context\n" \
+ " below it!\n"
+
+#define PATCH_RENAME_EXACT_QUOTEDNAME \
+ "diff --git a/file.txt \"b/foo\\\"bar.txt\"\n" \
+ "similarity index 100%\n" \
+ "rename from file.txt\n" \
+ "rename to \"foo\\\"bar.txt\"\n"
+
+#define PATCH_RENAME_SIMILAR_QUOTEDNAME \
+ "diff --git a/file.txt \"b/foo\\\"bar.txt\"\n" \
+ "similarity index 77%\n" \
+ "rename from file.txt\n" \
+ "rename to \"foo\\\"bar.txt\"\n" \
+ "index 9432026..cd8fd12 100644\n" \
+ "--- a/file.txt\n" \
+ "+++ \"b/foo\\\"bar.txt\"\n" \
+ "@@ -3,7 +3,7 @@ this is some context!\n" \
+ " around some lines\n" \
+ " that will change\n" \
+ " yes it is!\n" \
+ "-(this line is changed)\n" \
+ "+(THIS line is changed!)\n" \
+ " and this\n" \
+ " is additional context\n" \
+ " below it!\n"
+
+#define PATCH_MODECHANGE_UNCHANGED \
+ "diff --git a/file.txt b/file.txt\n" \
+ "old mode 100644\n" \
+ "new mode 100755\n"
+
+#define PATCH_MODECHANGE_MODIFIED \
+ "diff --git a/file.txt b/file.txt\n" \
+ "old mode 100644\n" \
+ "new mode 100755\n" \
+ "index 9432026..cd8fd12\n" \
+ "--- a/file.txt\n" \
+ "+++ b/file.txt\n" \
+ "@@ -3,7 +3,7 @@ this is some context!\n" \
+ " around some lines\n" \
+ " that will change\n" \
+ " yes it is!\n" \
+ "-(this line is changed)\n" \
+ "+(THIS line is changed!)\n" \
+ " and this\n" \
+ " is additional context\n" \
+ " below it!\n"
+
+#define PATCH_NOISY \
+ "This is some\nleading noise\n@@ - that\nlooks like a hunk header\n" \
+ "but actually isn't and should parse ok\n" \
+ PATCH_ORIGINAL_TO_CHANGE_MIDDLE \
+ "plus some trailing garbage for good measure\n"
+
+#define PATCH_NOISY_NOCONTEXT \
+ "This is some\nleading noise\n@@ - that\nlooks like a hunk header\n" \
+ "but actually isn't and should parse ok\n" \
+ PATCH_ORIGINAL_TO_CHANGE_MIDDLE_NOCONTEXT \
+ "plus some trailing garbage for good measure\n"
+
+#define PATCH_TRUNCATED_1 \
+ "diff --git a/file.txt b/file.txt\n" \
+ "index 9432026..cd8fd12 100644\n" \
+ "--- a/file.txt\n" \
+ "+++ b/file.txt\n" \
+ "@@ -3,7 +3,7 @@ this is some context!\n" \
+ " around some lines\n" \
+ " that will change\n" \
+ " yes it is!\n" \
+ "-(this line is changed)\n" \
+ "+(THIS line is changed!)\n" \
+ " and this\n"
+
+#define PATCH_TRUNCATED_2 \
+ "diff --git a/file.txt b/file.txt\n" \
+ "index 9432026..cd8fd12 100644\n" \
+ "--- a/file.txt\n" \
+ "+++ b/file.txt\n" \
+ "@@ -3,7 +3,7 @@ this is some context!\n" \
+ " around some lines\n" \
+ "-(this line is changed)\n" \
+ "+(THIS line is changed!)\n" \
+ " and this\n" \
+ " is additional context\n" \
+ " below it!\n"
+
+#define PATCH_TRUNCATED_3 \
+ "diff --git a/file.txt b/file.txt\n" \
+ "index 9432026..cd8fd12 100644\n" \
+ "--- a/file.txt\n" \
+ "+++ b/file.txt\n" \
+ "@@ -3,7 +3,7 @@ this is some context!\n" \
+ " around some lines\n" \
+ " that will change\n" \
+ " yes it is!\n" \
+ "+(THIS line is changed!)\n" \
+ " and this\n" \
+ " is additional context\n" \
+ " below it!\n"
+
+#define FILE_EMPTY_CONTEXT_ORIGINAL \
+ "this\nhas\nan\n\nempty\ncontext\nline\n"
+
+#define FILE_EMPTY_CONTEXT_MODIFIED \
+ "this\nhas\nan\n\nempty...\ncontext\nline\n"
+
+#define PATCH_EMPTY_CONTEXT \
+ "diff --git a/file.txt b/file.txt\n" \
+ "index 398d2df..bb15234 100644\n" \
+ "--- a/file.txt\n" \
+ "+++ b/file.txt\n" \
+ "@@ -2,6 +2,6 @@ this\n" \
+ " has\n" \
+ " an\n" \
+ "\n" \
+ "-empty\n" \
+ "+empty...\n" \
+ " context\n" \
+ " line\n"
+
+#define FILE_APPEND_NO_NL \
+ "hey!\n" \
+ "this is some context!\n" \
+ "around some lines\n" \
+ "that will change\n" \
+ "yes it is!\n" \
+ "(this line is changed)\n" \
+ "and this\n" \
+ "is additional context\n" \
+ "below it!\n" \
+ "added line with no nl"
+
+#define PATCH_APPEND_NO_NL \
+ "diff --git a/file.txt b/file.txt\n" \
+ "index 9432026..83759c0 100644\n" \
+ "--- a/file.txt\n" \
+ "+++ b/file.txt\n" \
+ "@@ -7,3 +7,4 @@ yes it is!\n" \
+ " and this\n" \
+ " is additional context\n" \
+ " below it!\n" \
+ "+added line with no nl\n" \
+ "\\ No newline at end of file\n"
+
+#define PATCH_APPEND_NO_NL_IN_OLD_FILE \
+ "diff --git a/file.txt b/file.txt\n" \
+ "index 9432026..83759c0 100644\n" \
+ "--- a/file.txt\n" \
+ "+++ b/file.txt\n" \
+ "@@ -1,1 +1,1 @@\n" \
+ "-foo\n" \
+ "\\ No newline at end of file\n" \
+ "+foo\n"
+
+#define PATCH_NAME_WHITESPACE \
+ "diff --git a/file with spaces.txt b/file with spaces.txt\n" \
+ "index 9432026..83759c0 100644\n" \
+ "--- a/file with spaces.txt\n" \
+ "+++ b/file with spaces.txt\n" \
+ "@@ -0,3 +0,2 @@\n" \
+ " and this\n" \
+ "-is additional context\n" \
+ " below it!\n" \
+
+#define PATCH_CORRUPT_GIT_HEADER \
+ "diff --git a/file.txt\n" \
+ "index 9432026..0f39b9a 100644\n" \
+ "--- a/file.txt\n" \
+ "+++ b/file.txt\n" \
+ "@@ -0,0 +1 @@\n" \
+ "+insert at front\n"
+
+#define PATCH_CORRUPT_MISSING_NEW_FILE \
+ "diff --git a/file.txt b/file.txt\n" \
+ "index 9432026..cd8fd12 100644\n" \
+ "--- a/file.txt\n" \
+ "@@ -6 +6 @@ yes it is!\n" \
+ "-(this line is changed)\n" \
+ "+(THIS line is changed!)\n"
+
+#define PATCH_CORRUPT_MISSING_OLD_FILE \
+ "diff --git a/file.txt b/file.txt\n" \
+ "index 9432026..cd8fd12 100644\n" \
+ "+++ b/file.txt\n" \
+ "@@ -6 +6 @@ yes it is!\n" \
+ "-(this line is changed)\n" \
+ "+(THIS line is changed!)\n"
+
+#define PATCH_CORRUPT_NO_CHANGES \
+ "diff --git a/file.txt b/file.txt\n" \
+ "index 9432026..cd8fd12 100644\n" \
+ "--- a/file.txt\n" \
+ "+++ b/file.txt\n" \
+ "@@ -0,0 +0,0 @@ yes it is!\n"
+
+#define PATCH_CORRUPT_MISSING_HUNK_HEADER \
+ "diff --git a/file.txt b/file.txt\n" \
+ "index 9432026..cd8fd12 100644\n" \
+ "--- a/file.txt\n" \
+ "+++ b/file.txt\n" \
+ "-(this line is changed)\n" \
+ "+(THIS line is changed!)\n"
+
+#define PATCH_NOT_A_PATCH \
+ "+++this is not\n" \
+ "--actually even\n" \
+ " a legitimate \n" \
+ "+patch file\n" \
+ "-it's something else\n" \
+ " entirely!"
+
+/* binary contents */
+
+#define FILE_BINARY_LITERAL_ORIGINAL "\x00\x00\x0a"
+#define FILE_BINARY_LITERAL_ORIGINAL_LEN 3
+
+#define FILE_BINARY_LITERAL_MODIFIED "\x00\x00\x01\x02\x0a"
+#define FILE_BINARY_LITERAL_MODIFIED_LEN 5
+
+#define PATCH_BINARY_LITERAL \
+ "diff --git a/binary.bin b/binary.bin\n" \
+ "index bd474b2519cc15eab801ff851cc7d50f0dee49a1..9ac35ff15cd8864aeafd889e4826a3150f0b06c4 100644\n" \
+ "GIT binary patch\n" \
+ "literal 5\n" \
+ "Mc${NkU}WL~000&M4gdfE\n" \
+ "\n" \
+ "literal 3\n" \
+ "Kc${Nk-~s>u4FC%O\n\n"
+
+#define FILE_BINARY_DELTA_ORIGINAL \
+ "\x00\x00\x01\x02\x00\x00\x01\x02\x00\x00\x01\x02\x0a\x54\x68\x69" \
+ "\x73\x20\x69\x73\x20\x61\x20\x62\x69\x6e\x61\x72\x79\x20\x66\x69" \
+ "\x6c\x65\x2c\x20\x62\x79\x20\x76\x69\x72\x74\x75\x65\x20\x6f\x66" \
+ "\x20\x68\x61\x76\x69\x6e\x67\x20\x73\x6f\x6d\x65\x20\x6e\x75\x6c" \
+ "\x6c\x73\x2e\x0a\x00\x00\x01\x02\x00\x00\x01\x02\x00\x00\x01\x02" \
+ "\x0a\x57\x65\x27\x72\x65\x20\x67\x6f\x69\x6e\x67\x20\x74\x6f\x20" \
+ "\x63\x68\x61\x6e\x67\x65\x20\x70\x6f\x72\x74\x69\x6f\x6e\x73\x20" \
+ "\x6f\x66\x20\x69\x74\x2e\x0a\x00\x00\x01\x02\x00\x00\x01\x02\x00" \
+ "\x00\x01\x02\x0a\x53\x6f\x20\x74\x68\x61\x74\x20\x77\x65\x20\x67" \
+ "\x69\x74\x20\x61\x20\x62\x69\x6e\x61\x72\x79\x20\x64\x65\x6c\x74" \
+ "\x61\x20\x69\x6e\x73\x74\x65\x61\x64\x20\x6f\x66\x20\x74\x68\x65" \
+ "\x20\x64\x65\x66\x6c\x61\x74\x65\x64\x20\x63\x6f\x6e\x74\x65\x6e" \
+ "\x74\x73\x2e\x0a\x00\x00\x01\x02\x00\x00\x01\x02\x00\x00\x01\x02" \
+ "\x0a"
+#define FILE_BINARY_DELTA_ORIGINAL_LEN 209
+
+#define FILE_BINARY_DELTA_MODIFIED \
+ "\x00\x00\x01\x02\x00\x00\x01\x02\x00\x00\x01\x02\x0a\x5a\x5a\x5a" \
+ "\x5a\x20\x69\x73\x20\x61\x20\x62\x69\x6e\x61\x72\x79\x20\x66\x69" \
+ "\x6c\x65\x2c\x20\x62\x79\x20\x76\x69\x72\x74\x75\x65\x20\x6f\x66" \
+ "\x20\x68\x61\x76\x69\x6e\x67\x20\x73\x6f\x6d\x65\x20\x6e\x75\x6c" \
+ "\x6c\x73\x2e\x0a\x00\x00\x01\x02\x00\x00\x01\x02\x00\x00\x01\x02" \
+ "\x0a\x57\x65\x27\x72\x65\x20\x67\x6f\x69\x6e\x67\x20\x74\x6f\x20" \
+ "\x63\x68\x61\x6e\x67\x65\x20\x70\x6f\x72\x74\x69\x6f\x6e\x73\x20" \
+ "\x6f\x66\x20\x49\x54\x2e\x0a\x00\x00\x01\x02\x00\x00\x01\x02\x00" \
+ "\x00\x01\x02\x0a\x53\x4f\x20\x74\x68\x61\x74\x20\x77\x65\x20\x67" \
+ "\x69\x74\x20\x61\x20\x62\x69\x6e\x61\x72\x79\x20\x64\x65\x6c\x74" \
+ "\x61\x20\x69\x6e\x73\x74\x65\x61\x64\x20\x6f\x66\x20\x74\x68\x65" \
+ "\x20\x64\x65\x66\x6c\x61\x74\x65\x64\x20\x63\x6f\x6e\x74\x65\x6e" \
+ "\x74\x73\x2e\x0a\x00\x00\x01\x02\x00\x00\x01\x02\x00\x00\x01\x02" \
+ "\x0a"
+#define FILE_BINARY_DELTA_MODIFIED_LEN 209
+
+#define PATCH_BINARY_DELTA \
+ "diff --git a/binary.bin b/binary.bin\n" \
+ "index 27184d9883b12c4c9c54b4a31137603586169f51..7c94f9e60bf366033d98e0d551ae37d30faef74a 100644\n" \
+ "GIT binary patch\n" \
+ "delta 48\n" \
+ "kc$~Y)c#%<%fq{_;hPk4EV4`4>uxE%K7m7r%|HL+L0In7XGynhq\n" \
+ "\n" \
+ "delta 48\n" \
+ "mc$~Y)c#%<%fq{_;hPgsAGK(h)CJASj=y9P)1m{m|^9BI99|yz$\n\n"
+
+#define PATCH_BINARY_ADD \
+ "diff --git a/binary.bin b/binary.bin\n" \
+ "new file mode 100644\n" \
+ "index 0000000000000000000000000000000000000000..7c94f9e60bf366033d98e0d551ae37d30faef74a\n" \
+ "GIT binary patch\n" \
+ "literal 209\n" \
+ "zc${60u?oUK5JXSQe8qG&;(u6KC<u0&+$Ohh?#kUJlD{_rLCL^0!@QXgcKh&k^H>C_\n" \
+ "zAhe=XX7rNzh<3&##YcwqNHmEKsP<&&m~%Zf;eX@Khr$?aExDmfqyyt+#l^I)3+LMg\n" \
+ "kxnAIj9Pfn_|Gh`fP7tlm6j#y{FJYg_IifRlR^R@A08f862mk;8\n" \
+ "\n" \
+ "literal 0\n" \
+ "Hc$@<O00001\n\n"
+
+#define PATCH_BINARY_DELETE \
+ "diff --git a/binary.bin b/binary.bin\n" \
+ "deleted file mode 100644\n" \
+ "index 7c94f9e60bf366033d98e0d551ae37d30faef74a..0000000000000000000000000000000000000000\n" \
+ "GIT binary patch\n" \
+ "literal 0\n" \
+ "Hc$@<O00001\n" \
+ "\n" \
+ "literal 209\n" \
+ "zc${60u?oUK5JXSQe8qG&;(u6KC<u0&+$Ohh?#kUJlD{_rLCL^0!@QXgcKh&k^H>C_\n" \
+ "zAhe=XX7rNzh<3&##YcwqNHmEKsP<&&m~%Zf;eX@Khr$?aExDmfqyyt+#l^I)3+LMg\n" \
+ "kxnAIj9Pfn_|Gh`fP7tlm6j#y{FJYg_IifRlR^R@A08f862mk;8\n\n"
+
+/* contains an old side that does not match the expected source */
+#define PATCH_BINARY_NOT_REVERSIBLE \
+ "diff --git a/binary.bin b/binary.bin\n" \
+ "index 27184d9883b12c4c9c54b4a31137603586169f51..7c94f9e60bf366033d98e0d551ae37d30faef74a 100644\n" \
+ "GIT binary patch\n" \
+ "literal 5\n" \
+ "Mc${NkU}WL~000&M4gdfE\n" \
+ "\n" \
+ "delta 48\n" \
+ "mc$~Y)c#%<%fq{_;hPgsAGK(h)CJASj=y9P)1m{m|^9BI99|yz$\n\n"
+
+#define PATCH_BINARY_NOT_PRINTED \
+ "diff --git a/binary.bin b/binary.bin\n" \
+ "index 27184d9..7c94f9e 100644\n" \
+ "Binary files a/binary.bin and b/binary.bin differ\n"
+
+#define PATCH_ADD_BINARY_NOT_PRINTED \
+ "diff --git a/test.bin b/test.bin\n" \
+ "new file mode 100644\n" \
+ "index 0000000..9e0f96a\n" \
+ "Binary files /dev/null and b/test.bin differ\n"
+
+#define PATCH_ORIGINAL_NEW_FILE_WITH_SPACE \
+ "diff --git a/sp ace.txt b/sp ace.txt\n" \
+ "new file mode 100644\n" \
+ "index 000000000..789819226\n" \
+ "--- /dev/null\n" \
+ "+++ b/sp ace.txt\n" \
+ "@@ -0,0 +1 @@\n" \
+ "+a\n"
+
+#define PATCH_DELETE_FILE_WITH_SPACE \
+ "diff --git a/sp ace.txt b/sp ace.txt\n" \
+ "deleted file mode 100644\n" \
+ "index 789819226..000000000\n" \
+ "--- a/sp ace.txt\n" \
+ "+++ /dev/null\n" \
+ "@@ -1 +0,0 @@\n" \
+ "-a\n"
+
+#define PATCH_CRLF \
+ "diff --git a/test-file b/test-file\r\n" \
+ "new file mode 100644\r\n" \
+ "index 0000000..af431f2 100644\r\n" \
+ "--- /dev/null\r\n" \
+ "+++ b/test-file\r\n" \
+ "@@ -0,0 +1 @@\r\n" \
+ "+a contents\r\n"
+
+#define PATCH_NO_EXTENDED_HEADERS \
+ "diff --git a/file b/file\n" \
+ "--- a/file\n" \
+ "+++ b/file\n" \
+ "@@ -1,3 +1,3 @@\n" \
+ " a\n" \
+ "-b\n" \
+ "+bb\n" \
+ " c\n"
+
+#define PATCH_BINARY_FILE_WITH_MISSING_PATHS \
+ "diff --git \n" \
+ "--- \n" \
+ "+++ \n" \
+ "Binary files "
+
+#define PATCH_BINARY_FILE_WITH_WHITESPACE_PATHS \
+ "diff --git a/file b/file\n" \
+ "--- \n" \
+ "+++ \n" \
+ "Binary files "
+
+#define PATCH_BINARY_FILE_WITH_QUOTED_EMPTY_PATHS \
+ "diff --git a/file b/file\n" \
+ "--- \"\"\n" \
+ "+++ \"\"\n" \
+ "Binary files "
+
+#define PATCH_BINARY_FILE_PATH_WITH_SPACES \
+ "diff --git a b c d e f\n" \
+ "--- a b c\n" \
+ "+++ d e f\n" \
+ "Binary files a b c and d e f differ"
+
+#define PATCH_BINARY_FILE_PATH_WITHOUT_BODY_PATHS \
+ "diff --git a b c d e f\n" \
+ "--- \n" \
+ "+++ \n" \
+ "Binary files a b c and d e f differ"
+
+#define PATCH_BINARY_FILE_WITH_TRUNCATED_DELTA \
+ "diff --git a/file b/file\n" \
+ "index 1420..b71f\n" \
+ "GIT binary patch\n" \
+ "delta 7\n" \
+ "d"
+
+#define PATCH_MULTIPLE_OLD_PATHS \
+ "diff --git \n" \
+ "--- \n" \
+ "+++ \n" \
+ "index 0000..7DDb\n" \
+ "--- \n"
+
+#define PATCH_INTMAX_NEW_LINES \
+ "diff --git a/file b/file\n" \
+ "--- a/file\n" \
+ "+++ b/file\n" \
+ "@@ -0 +2147483647 @@\n" \
+ "\n" \
+ " "
diff --git a/tests/libgit2/patch/print.c b/tests/libgit2/patch/print.c
new file mode 100644
index 0000000..33cf27d
--- /dev/null
+++ b/tests/libgit2/patch/print.c
@@ -0,0 +1,186 @@
+#include "clar_libgit2.h"
+#include "patch.h"
+#include "patch_parse.h"
+
+#include "patch_common.h"
+
+
+/* sanity check the round-trip of patch parsing: ensure that we can parse
+ * and then print a variety of patch files.
+ */
+
+static void patch_print_from_patchfile(const char *data, size_t len)
+{
+ git_patch *patch;
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_pass(git_patch_from_buffer(&patch, data, len, NULL));
+ cl_git_pass(git_patch_to_buf(&buf, patch));
+
+ cl_assert_equal_s(data, buf.ptr);
+
+ git_patch_free(patch);
+ git_buf_dispose(&buf);
+}
+
+void test_patch_print__change_middle(void)
+{
+ patch_print_from_patchfile(PATCH_ORIGINAL_TO_CHANGE_MIDDLE,
+ strlen(PATCH_ORIGINAL_TO_CHANGE_MIDDLE));
+}
+
+void test_patch_print__change_middle_nocontext(void)
+{
+ patch_print_from_patchfile(PATCH_ORIGINAL_TO_CHANGE_MIDDLE_NOCONTEXT,
+ strlen(PATCH_ORIGINAL_TO_CHANGE_MIDDLE_NOCONTEXT));
+}
+
+void test_patch_print__change_firstline(void)
+{
+ patch_print_from_patchfile(PATCH_ORIGINAL_TO_CHANGE_FIRSTLINE,
+ strlen(PATCH_ORIGINAL_TO_CHANGE_FIRSTLINE));
+}
+
+void test_patch_print__change_lastline(void)
+{
+ patch_print_from_patchfile(PATCH_ORIGINAL_TO_CHANGE_LASTLINE,
+ strlen(PATCH_ORIGINAL_TO_CHANGE_LASTLINE));
+}
+
+void test_patch_print__prepend(void)
+{
+ patch_print_from_patchfile(PATCH_ORIGINAL_TO_PREPEND,
+ strlen(PATCH_ORIGINAL_TO_PREPEND));
+}
+
+void test_patch_print__prepend_nocontext(void)
+{
+ patch_print_from_patchfile(PATCH_ORIGINAL_TO_PREPEND_NOCONTEXT,
+ strlen(PATCH_ORIGINAL_TO_PREPEND_NOCONTEXT));
+}
+
+void test_patch_print__append(void)
+{
+ patch_print_from_patchfile(PATCH_ORIGINAL_TO_APPEND,
+ strlen(PATCH_ORIGINAL_TO_APPEND));
+}
+
+void test_patch_print__append_nocontext(void)
+{
+ patch_print_from_patchfile(PATCH_ORIGINAL_TO_APPEND_NOCONTEXT,
+ strlen(PATCH_ORIGINAL_TO_APPEND_NOCONTEXT));
+}
+
+void test_patch_print__prepend_and_append(void)
+{
+ patch_print_from_patchfile(PATCH_ORIGINAL_TO_PREPEND_AND_APPEND,
+ strlen(PATCH_ORIGINAL_TO_PREPEND_AND_APPEND));
+}
+
+void test_patch_print__to_empty_file(void)
+{
+ patch_print_from_patchfile(PATCH_ORIGINAL_TO_EMPTY_FILE,
+ strlen(PATCH_ORIGINAL_TO_EMPTY_FILE));
+}
+
+void test_patch_print__from_empty_file(void)
+{
+ patch_print_from_patchfile(PATCH_EMPTY_FILE_TO_ORIGINAL,
+ strlen(PATCH_EMPTY_FILE_TO_ORIGINAL));
+}
+
+void test_patch_print__add(void)
+{
+ patch_print_from_patchfile(PATCH_ADD_ORIGINAL,
+ strlen(PATCH_ADD_ORIGINAL));
+}
+
+void test_patch_print__delete(void)
+{
+ patch_print_from_patchfile(PATCH_DELETE_ORIGINAL,
+ strlen(PATCH_DELETE_ORIGINAL));
+}
+
+void test_patch_print__rename_exact(void)
+{
+ patch_print_from_patchfile(PATCH_RENAME_EXACT,
+ strlen(PATCH_RENAME_EXACT));
+}
+
+void test_patch_print__rename_exact_with_mode(void)
+{
+ patch_print_from_patchfile(PATCH_RENAME_EXACT_WITH_MODE,
+ strlen(PATCH_RENAME_EXACT_WITH_MODE));
+}
+
+void test_patch_print__rename_similar(void)
+{
+ patch_print_from_patchfile(PATCH_RENAME_SIMILAR,
+ strlen(PATCH_RENAME_SIMILAR));
+}
+
+void test_patch_print__rename_exact_quotedname(void)
+{
+ patch_print_from_patchfile(PATCH_RENAME_EXACT_QUOTEDNAME,
+ strlen(PATCH_RENAME_EXACT_QUOTEDNAME));
+}
+
+void test_patch_print__rename_similar_quotedname(void)
+{
+ patch_print_from_patchfile(PATCH_RENAME_SIMILAR_QUOTEDNAME,
+ strlen(PATCH_RENAME_SIMILAR_QUOTEDNAME));
+}
+
+void test_patch_print__modechange_unchanged(void)
+{
+ patch_print_from_patchfile(PATCH_MODECHANGE_UNCHANGED,
+ strlen(PATCH_MODECHANGE_UNCHANGED));
+}
+
+void test_patch_print__modechange_modified(void)
+{
+ patch_print_from_patchfile(PATCH_MODECHANGE_MODIFIED,
+ strlen(PATCH_MODECHANGE_MODIFIED));
+}
+
+void test_patch_print__binary_literal(void)
+{
+ patch_print_from_patchfile(PATCH_BINARY_LITERAL,
+ strlen(PATCH_BINARY_LITERAL));
+}
+
+void test_patch_print__binary_delta(void)
+{
+ patch_print_from_patchfile(PATCH_BINARY_DELTA,
+ strlen(PATCH_BINARY_DELTA));
+}
+
+void test_patch_print__binary_add(void)
+{
+ patch_print_from_patchfile(PATCH_BINARY_ADD,
+ strlen(PATCH_BINARY_ADD));
+}
+
+void test_patch_print__binary_delete(void)
+{
+ patch_print_from_patchfile(PATCH_BINARY_DELETE,
+ strlen(PATCH_BINARY_DELETE));
+}
+
+void test_patch_print__not_reversible(void)
+{
+ patch_print_from_patchfile(PATCH_BINARY_NOT_REVERSIBLE,
+ strlen(PATCH_BINARY_NOT_REVERSIBLE));
+}
+
+void test_patch_print__binary_not_shown(void)
+{
+ patch_print_from_patchfile(PATCH_BINARY_NOT_PRINTED,
+ strlen(PATCH_BINARY_NOT_PRINTED));
+}
+
+void test_patch_print__binary_add_not_shown(void)
+{
+ patch_print_from_patchfile(PATCH_ADD_BINARY_NOT_PRINTED,
+ strlen(PATCH_ADD_BINARY_NOT_PRINTED));
+}
diff --git a/tests/libgit2/path/validate.c b/tests/libgit2/path/validate.c
new file mode 100644
index 0000000..df92194
--- /dev/null
+++ b/tests/libgit2/path/validate.c
@@ -0,0 +1,63 @@
+#include "clar_libgit2.h"
+#include "path.h"
+
+void test_path_validate__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_path_validate__length(void)
+{
+ cl_must_pass(git_path_validate_length(NULL, "/foo/bar"));
+ cl_must_pass(git_path_validate_length(NULL, "C:\\Foo\\Bar"));
+ cl_must_pass(git_path_validate_length(NULL, "\\\\?\\C:\\Foo\\Bar"));
+ cl_must_pass(git_path_validate_length(NULL, "\\\\?\\C:\\Foo\\Bar"));
+ cl_must_pass(git_path_validate_length(NULL, "\\\\?\\UNC\\server\\C$\\folder"));
+
+#ifdef GIT_WIN32
+ /*
+ * In the absence of a repo configuration, 259 character paths
+ * succeed. >= 260 character paths fail.
+ */
+ cl_must_pass(git_path_validate_length(NULL, "C:\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\ok.txt"));
+ cl_must_pass(git_path_validate_length(NULL, "C:\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\260.txt"));
+ cl_must_fail(git_path_validate_length(NULL, "C:\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\longer_than_260.txt"));
+
+ /* count characters, not bytes */
+ cl_must_pass(git_path_validate_length(NULL, "C:\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\\260.txt"));
+ cl_must_fail(git_path_validate_length(NULL, "C:\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\\long.txt"));
+#else
+ cl_must_pass(git_path_validate_length(NULL, "/c/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/ok.txt"));
+ cl_must_pass(git_path_validate_length(NULL, "/c/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/260.txt"));
+ cl_must_pass(git_path_validate_length(NULL, "/c/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/longer_than_260.txt"));
+ cl_must_pass(git_path_validate_length(NULL, "C:\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\\260.txt"));
+ cl_must_pass(git_path_validate_length(NULL, "C:\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\aaaaaaaaa\\\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\xc2\xa2\\long.txt"));
+#endif
+}
+
+void test_path_validate__length_with_core_longpath(void)
+{
+#ifdef GIT_WIN32
+ git_repository *repo;
+ git_config *config;
+
+ repo = cl_git_sandbox_init("empty_bare.git");
+
+ cl_git_pass(git_repository_open(&repo, "empty_bare.git"));
+ cl_git_pass(git_repository_config(&config, repo));
+
+ /* fail by default */
+ cl_must_fail(git_path_validate_length(repo, "/c/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/longer_than_260.txt"));
+
+ /* set core.longpaths explicitly on */
+ cl_git_pass(git_config_set_bool(config, "core.longpaths", 1));
+ cl_must_pass(git_path_validate_length(repo, "/c/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/longer_than_260.txt"));
+
+ /* set core.longpaths explicitly off */
+ cl_git_pass(git_config_set_bool(config, "core.longpaths", 0));
+ cl_must_fail(git_path_validate_length(repo, "/c/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/aaaaaaaaa/longer_than_260.txt"));
+
+ git_config_free(config);
+ git_repository_free(repo);
+#endif
+}
diff --git a/tests/libgit2/perf/helper__perf__do_merge.c b/tests/libgit2/perf/helper__perf__do_merge.c
new file mode 100644
index 0000000..eb10524
--- /dev/null
+++ b/tests/libgit2/perf/helper__perf__do_merge.c
@@ -0,0 +1,75 @@
+#include "clar_libgit2.h"
+#include "helper__perf__do_merge.h"
+#include "helper__perf__timer.h"
+
+static git_repository * g_repo;
+
+void perf__do_merge(const char *fixture,
+ const char *test_name,
+ const char *id_a,
+ const char *id_b)
+{
+ git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_clone_options clone_opts = GIT_CLONE_OPTIONS_INIT;
+ git_merge_options merge_opts = GIT_MERGE_OPTIONS_INIT;
+ git_oid oid_a;
+ git_oid oid_b;
+ git_reference *ref_branch_a = NULL;
+ git_reference *ref_branch_b = NULL;
+ git_commit *commit_a = NULL;
+ git_commit *commit_b = NULL;
+ git_annotated_commit *annotated_commits[1] = { NULL };
+ perf_timer t_total = PERF_TIMER_INIT;
+ perf_timer t_clone = PERF_TIMER_INIT;
+ perf_timer t_checkout = PERF_TIMER_INIT;
+ perf_timer t_merge = PERF_TIMER_INIT;
+
+ perf__timer__start(&t_total);
+
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
+ clone_opts.checkout_opts = checkout_opts;
+
+ perf__timer__start(&t_clone);
+ cl_git_pass(git_clone(&g_repo, fixture, test_name, &clone_opts));
+ perf__timer__stop(&t_clone);
+
+ git_oid__fromstr(&oid_a, id_a, GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit_a, g_repo, &oid_a));
+ cl_git_pass(git_branch_create(&ref_branch_a, g_repo,
+ "A", commit_a,
+ 0));
+
+ perf__timer__start(&t_checkout);
+ cl_git_pass(git_checkout_tree(g_repo, (git_object*)commit_a, &checkout_opts));
+ perf__timer__stop(&t_checkout);
+
+ cl_git_pass(git_repository_set_head(g_repo, git_reference_name(ref_branch_a)));
+
+ git_oid__fromstr(&oid_b, id_b, GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit_b, g_repo, &oid_b));
+ cl_git_pass(git_branch_create(&ref_branch_b, g_repo,
+ "B", commit_b,
+ 0));
+
+ cl_git_pass(git_annotated_commit_lookup(&annotated_commits[0], g_repo, &oid_b));
+
+ perf__timer__start(&t_merge);
+ cl_git_pass(git_merge(g_repo,
+ (const git_annotated_commit **)annotated_commits, 1,
+ &merge_opts, &checkout_opts));
+ perf__timer__stop(&t_merge);
+
+ git_reference_free(ref_branch_a);
+ git_reference_free(ref_branch_b);
+ git_commit_free(commit_a);
+ git_commit_free(commit_b);
+ git_annotated_commit_free(annotated_commits[0]);
+ git_repository_free(g_repo);
+
+ perf__timer__stop(&t_total);
+
+ perf__timer__report(&t_clone, "%s: clone", test_name);
+ perf__timer__report(&t_checkout, "%s: checkout", test_name);
+ perf__timer__report(&t_merge, "%s: merge", test_name);
+ perf__timer__report(&t_total, "%s: total", test_name);
+}
diff --git a/tests/libgit2/perf/helper__perf__do_merge.h b/tests/libgit2/perf/helper__perf__do_merge.h
new file mode 100644
index 0000000..4a4723d
--- /dev/null
+++ b/tests/libgit2/perf/helper__perf__do_merge.h
@@ -0,0 +1,4 @@
+void perf__do_merge(const char *fixture,
+ const char *test_name,
+ const char *id_a,
+ const char *id_b);
diff --git a/tests/libgit2/perf/helper__perf__timer.c b/tests/libgit2/perf/helper__perf__timer.c
new file mode 100644
index 0000000..8a7ed09
--- /dev/null
+++ b/tests/libgit2/perf/helper__perf__timer.c
@@ -0,0 +1,73 @@
+#include "clar_libgit2.h"
+#include "helper__perf__timer.h"
+
+#if defined(GIT_WIN32)
+
+void perf__timer__start(perf_timer *t)
+{
+ QueryPerformanceCounter(&t->time_started);
+}
+
+void perf__timer__stop(perf_timer *t)
+{
+ LARGE_INTEGER time_now;
+ QueryPerformanceCounter(&time_now);
+
+ t->sum.QuadPart += (time_now.QuadPart - t->time_started.QuadPart);
+}
+
+void perf__timer__report(perf_timer *t, const char *fmt, ...)
+{
+ va_list arglist;
+ LARGE_INTEGER freq;
+ double fraction;
+
+ QueryPerformanceFrequency(&freq);
+
+ fraction = ((double)t->sum.QuadPart) / ((double)freq.QuadPart);
+
+ printf("%10.3f: ", fraction);
+
+ va_start(arglist, fmt);
+ vprintf(fmt, arglist);
+ va_end(arglist);
+
+ printf("\n");
+}
+
+#else
+
+#include <sys/time.h>
+
+static uint32_t now_in_ms(void)
+{
+ struct timeval now;
+ gettimeofday(&now, NULL);
+ return (uint32_t)((now.tv_sec * 1000) + (now.tv_usec / 1000));
+}
+
+void perf__timer__start(perf_timer *t)
+{
+ t->time_started = now_in_ms();
+}
+
+void perf__timer__stop(perf_timer *t)
+{
+ uint32_t now = now_in_ms();
+ t->sum += (now - t->time_started);
+}
+
+void perf__timer__report(perf_timer *t, const char *fmt, ...)
+{
+ va_list arglist;
+
+ printf("%10.3f: ", ((double)t->sum) / 1000);
+
+ va_start(arglist, fmt);
+ vprintf(fmt, arglist);
+ va_end(arglist);
+
+ printf("\n");
+}
+
+#endif
diff --git a/tests/libgit2/perf/helper__perf__timer.h b/tests/libgit2/perf/helper__perf__timer.h
new file mode 100644
index 0000000..5aff4b1
--- /dev/null
+++ b/tests/libgit2/perf/helper__perf__timer.h
@@ -0,0 +1,27 @@
+#if defined(GIT_WIN32)
+
+struct perf__timer
+{
+ LARGE_INTEGER sum;
+ LARGE_INTEGER time_started;
+};
+
+#define PERF_TIMER_INIT {0}
+
+#else
+
+struct perf__timer
+{
+ uint32_t sum;
+ uint32_t time_started;
+};
+
+#define PERF_TIMER_INIT {0}
+
+#endif
+
+typedef struct perf__timer perf_timer;
+
+void perf__timer__start(perf_timer *t);
+void perf__timer__stop(perf_timer *t);
+void perf__timer__report(perf_timer *t, const char *fmt, ...);
diff --git a/tests/libgit2/perf/merge.c b/tests/libgit2/perf/merge.c
new file mode 100644
index 0000000..721902d
--- /dev/null
+++ b/tests/libgit2/perf/merge.c
@@ -0,0 +1,31 @@
+#include "clar_libgit2.h"
+#include "helper__perf__do_merge.h"
+
+/* This test requires a large repo with many files.
+ * It doesn't care about the contents, just the size.
+ *
+ * For now, we use the LibGit2 repo containing the
+ * source tree because it is already here.
+ *
+ * `find . | wc -l` reports 5128.
+ *
+ */
+#define SRC_REPO (cl_fixture("../.."))
+
+/* We need 2 arbitrary commits within that repo
+ * that have a large number of changed files.
+ * Again, we don't care about the actual contents,
+ * just the size.
+ *
+ * For now, we use these public branches:
+ * maint/v0.21 d853fb9f24e0fe63b3dce9fbc04fd9cfe17a030b Always checkout with case sensitive iterator
+ * maint/v0.22 1ce9ea3ba9b4fa666602d52a5281d41a482cc58b checkout tests: cleanup realpath impl on Win32
+ *
+ */
+#define ID_BRANCH_A "d853fb9f24e0fe63b3dce9fbc04fd9cfe17a030b"
+#define ID_BRANCH_B "1ce9ea3ba9b4fa666602d52a5281d41a482cc58b"
+
+void test_perf_merge__m1(void)
+{
+ perf__do_merge(SRC_REPO, "m1", ID_BRANCH_A, ID_BRANCH_B);
+}
diff --git a/tests/libgit2/precompiled.c b/tests/libgit2/precompiled.c
new file mode 100644
index 0000000..5f656a4
--- /dev/null
+++ b/tests/libgit2/precompiled.c
@@ -0,0 +1 @@
+#include "precompiled.h"
diff --git a/tests/libgit2/precompiled.h b/tests/libgit2/precompiled.h
new file mode 100644
index 0000000..ea53a60
--- /dev/null
+++ b/tests/libgit2/precompiled.h
@@ -0,0 +1,4 @@
+#include "common.h"
+#include "git2.h"
+#include "clar.h"
+#include "clar_libgit2.h"
diff --git a/tests/libgit2/rebase/abort.c b/tests/libgit2/rebase/abort.c
new file mode 100644
index 0000000..da0dfe8
--- /dev/null
+++ b/tests/libgit2/rebase/abort.c
@@ -0,0 +1,250 @@
+#include "clar_libgit2.h"
+#include "git2/rebase.h"
+#include "merge.h"
+#include "posix.h"
+#include "annotated_commit.h"
+
+#include <fcntl.h>
+
+static git_repository *repo;
+
+/* Fixture setup and teardown */
+void test_rebase_abort__initialize(void)
+{
+ repo = cl_git_sandbox_init("rebase");
+}
+
+void test_rebase_abort__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static void ensure_aborted(
+ git_annotated_commit *branch,
+ git_annotated_commit *onto)
+{
+ git_reference *head_ref, *branch_ref = NULL;
+ git_status_list *statuslist;
+ git_reflog *reflog;
+ const git_reflog_entry *reflog_entry;
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
+
+ /* Make sure the refs are updated appropriately */
+ cl_git_pass(git_reference_lookup(&head_ref, repo, "HEAD"));
+
+ if (branch->ref_name == NULL)
+ cl_assert_equal_oid(git_annotated_commit_id(branch), git_reference_target(head_ref));
+ else {
+ cl_assert_equal_s("refs/heads/beef", git_reference_symbolic_target(head_ref));
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, git_reference_symbolic_target(head_ref)));
+ cl_assert_equal_oid(git_annotated_commit_id(branch), git_reference_target(branch_ref));
+ }
+
+ git_status_list_new(&statuslist, repo, NULL);
+ cl_assert_equal_i(0, git_status_list_entrycount(statuslist));
+ git_status_list_free(statuslist);
+
+ /* Make sure the reflogs are updated appropriately */
+ cl_git_pass(git_reflog_read(&reflog, repo, "HEAD"));
+
+ cl_assert(reflog_entry = git_reflog_entry_byindex(reflog, 0));
+ cl_assert_equal_oid(git_annotated_commit_id(onto), git_reflog_entry_id_old(reflog_entry));
+ cl_assert_equal_oid(git_annotated_commit_id(branch), git_reflog_entry_id_new(reflog_entry));
+ cl_assert_equal_s("rebase: aborting", git_reflog_entry_message(reflog_entry));
+
+ git_reflog_free(reflog);
+ git_reference_free(head_ref);
+ git_reference_free(branch_ref);
+}
+
+static void test_abort(
+ git_annotated_commit *branch, git_annotated_commit *onto)
+{
+ git_rebase *rebase;
+
+ cl_git_pass(git_rebase_open(&rebase, repo, NULL));
+ cl_git_pass(git_rebase_abort(rebase));
+
+ ensure_aborted(branch, onto);
+
+ git_rebase_free(rebase);
+}
+
+void test_rebase_abort__merge(void)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref, *onto_ref;
+ git_annotated_commit *branch_head, *onto_head;
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef"));
+ cl_git_pass(git_reference_lookup(&onto_ref, repo, "refs/heads/master"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&onto_head, repo, onto_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, NULL, onto_head, NULL));
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
+
+ test_abort(branch_head, onto_head);
+
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(onto_head);
+
+ git_reference_free(branch_ref);
+ git_reference_free(onto_ref);
+ git_rebase_free(rebase);
+}
+
+void test_rebase_abort__merge_immediately_after_init(void)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref, *onto_ref;
+ git_annotated_commit *branch_head, *onto_head;
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef"));
+ cl_git_pass(git_reference_lookup(&onto_ref, repo, "refs/heads/master"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&onto_head, repo, onto_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, NULL, onto_head, NULL));
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
+
+ cl_git_pass(git_rebase_abort(rebase));
+ ensure_aborted(branch_head, onto_head);
+
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(onto_head);
+
+ git_reference_free(branch_ref);
+ git_reference_free(onto_ref);
+ git_rebase_free(rebase);
+}
+
+void test_rebase_abort__merge_by_id(void)
+{
+ git_rebase *rebase;
+ git_oid branch_id, onto_id;
+ git_annotated_commit *branch_head, *onto_head;
+
+ cl_git_pass(git_oid__fromstr(&branch_id, "b146bd7608eac53d9bf9e1a6963543588b555c64", GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&onto_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00", GIT_OID_SHA1));
+
+ cl_git_pass(git_annotated_commit_lookup(&branch_head, repo, &branch_id));
+ cl_git_pass(git_annotated_commit_lookup(&onto_head, repo, &onto_id));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, NULL, onto_head, NULL));
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
+
+ test_abort(branch_head, onto_head);
+
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(onto_head);
+
+ git_rebase_free(rebase);
+}
+
+void test_rebase_abort__merge_by_revspec(void)
+{
+ git_rebase *rebase;
+ git_annotated_commit *branch_head, *onto_head;
+
+ cl_git_pass(git_annotated_commit_from_revspec(&branch_head, repo, "b146bd7"));
+ cl_git_pass(git_annotated_commit_from_revspec(&onto_head, repo, "efad0b1"));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, NULL, onto_head, NULL));
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
+
+ test_abort(branch_head, onto_head);
+
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(onto_head);
+
+ git_rebase_free(rebase);
+}
+
+void test_rebase_abort__merge_by_id_immediately_after_init(void)
+{
+ git_rebase *rebase;
+ git_oid branch_id, onto_id;
+ git_annotated_commit *branch_head, *onto_head;
+
+ cl_git_pass(git_oid__fromstr(&branch_id, "b146bd7608eac53d9bf9e1a6963543588b555c64", GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&onto_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00", GIT_OID_SHA1));
+
+ cl_git_pass(git_annotated_commit_lookup(&branch_head, repo, &branch_id));
+ cl_git_pass(git_annotated_commit_lookup(&onto_head, repo, &onto_id));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, NULL, onto_head, NULL));
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
+
+ cl_git_pass(git_rebase_abort(rebase));
+ ensure_aborted(branch_head, onto_head);
+
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(onto_head);
+
+ git_rebase_free(rebase);
+}
+
+void test_rebase_abort__detached_head(void)
+{
+ git_rebase *rebase;
+ git_oid branch_id, onto_id;
+ git_signature *signature;
+ git_annotated_commit *branch_head, *onto_head;
+
+ git_oid__fromstr(&branch_id, "b146bd7608eac53d9bf9e1a6963543588b555c64", GIT_OID_SHA1);
+ git_oid__fromstr(&onto_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00", GIT_OID_SHA1);
+
+ cl_git_pass(git_annotated_commit_lookup(&branch_head, repo, &branch_id));
+ cl_git_pass(git_annotated_commit_lookup(&onto_head, repo, &onto_id));
+
+ cl_git_pass(git_signature_new(&signature, "Rebaser", "rebaser@example.com", 1404157834, -400));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, NULL, onto_head, NULL));
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
+
+ test_abort(branch_head, onto_head);
+
+ git_signature_free(signature);
+
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(onto_head);
+
+ git_rebase_free(rebase);
+}
+
+void test_rebase_abort__old_style_head_file(void)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref, *onto_ref;
+ git_signature *signature;
+ git_annotated_commit *branch_head, *onto_head;
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef"));
+ cl_git_pass(git_reference_lookup(&onto_ref, repo, "refs/heads/master"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&onto_head, repo, onto_ref));
+
+ cl_git_pass(git_signature_new(&signature, "Rebaser", "rebaser@example.com", 1404157834, -400));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, NULL, onto_head, NULL));
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
+
+ p_rename("rebase-merge/.git/rebase-merge/orig-head",
+ "rebase-merge/.git/rebase-merge/head");
+
+ test_abort(branch_head, onto_head);
+
+ git_signature_free(signature);
+
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(onto_head);
+
+ git_reference_free(branch_ref);
+ git_reference_free(onto_ref);
+ git_rebase_free(rebase);
+}
diff --git a/tests/libgit2/rebase/inmemory.c b/tests/libgit2/rebase/inmemory.c
new file mode 100644
index 0000000..287dd99
--- /dev/null
+++ b/tests/libgit2/rebase/inmemory.c
@@ -0,0 +1,210 @@
+#include "clar_libgit2.h"
+#include "git2/rebase.h"
+#include "posix.h"
+
+#include <fcntl.h>
+
+static git_repository *repo;
+static git_signature *signature;
+
+/* Fixture setup and teardown */
+void test_rebase_inmemory__initialize(void)
+{
+ repo = cl_git_sandbox_init("rebase");
+
+ cl_git_pass(git_signature_new(&signature,
+ "Rebaser", "rebaser@rebaser.rb", 1405694510, 0));
+}
+
+void test_rebase_inmemory__cleanup(void)
+{
+ git_signature_free(signature);
+ cl_git_sandbox_cleanup();
+}
+
+void test_rebase_inmemory__not_in_rebase_state(void)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref, *upstream_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_rebase_options opts = GIT_REBASE_OPTIONS_INIT;
+
+ opts.inmemory = true;
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, &opts));
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
+
+ git_rebase_free(rebase);
+
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+
+ git_reference_free(branch_ref);
+ git_reference_free(upstream_ref);
+}
+
+void test_rebase_inmemory__can_resolve_conflicts(void)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref, *upstream_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_rebase_operation *rebase_operation;
+ git_status_list *status_list;
+ git_oid pick_id, commit_id, expected_commit_id;
+ git_index *rebase_index, *repo_index;
+ git_index_entry resolution = {{0}};
+ git_rebase_options opts = GIT_REBASE_OPTIONS_INIT;
+
+ opts.inmemory = true;
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/asparagus"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, &opts));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+
+ git_oid__fromstr(&pick_id, "33f915f9e4dbd9f4b24430e48731a59b45b15500", GIT_OID_SHA1);
+
+ cl_assert_equal_i(GIT_REBASE_OPERATION_PICK, rebase_operation->type);
+ cl_assert_equal_oid(&pick_id, &rebase_operation->id);
+
+ /* ensure that we did not do anything stupid to the workdir or repo index */
+ cl_git_pass(git_repository_index(&repo_index, repo));
+ cl_assert(!git_index_has_conflicts(repo_index));
+
+ cl_git_pass(git_status_list_new(&status_list, repo, NULL));
+ cl_assert_equal_i(0, git_status_list_entrycount(status_list));
+
+ /* but that the index returned from rebase does have conflicts */
+ cl_git_pass(git_rebase_inmemory_index(&rebase_index, rebase));
+ cl_assert(git_index_has_conflicts(rebase_index));
+
+ cl_git_fail_with(GIT_EUNMERGED, git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL));
+
+ /* ensure that we can work with the in-memory index to resolve the conflict */
+ resolution.path = "asparagus.txt";
+ resolution.mode = GIT_FILEMODE_BLOB;
+ git_oid__fromstr(&resolution.id, "414dfc71ead79c07acd4ea47fecf91f289afc4b9", GIT_OID_SHA1);
+ cl_git_pass(git_index_conflict_remove(rebase_index, "asparagus.txt"));
+ cl_git_pass(git_index_add(rebase_index, &resolution));
+
+ /* and finally create a commit for the resolved rebase operation */
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL));
+
+ cl_git_pass(git_oid__fromstr(&expected_commit_id, "db7af47222181e548810da2ab5fec0e9357c5637", GIT_OID_SHA1));
+ cl_assert_equal_oid(&commit_id, &expected_commit_id);
+
+ git_status_list_free(status_list);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_reference_free(branch_ref);
+ git_reference_free(upstream_ref);
+ git_index_free(repo_index);
+ git_index_free(rebase_index);
+ git_rebase_free(rebase);
+}
+
+void test_rebase_inmemory__no_common_ancestor(void)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref, *upstream_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_rebase_operation *rebase_operation;
+ git_oid commit_id, expected_final_id;
+ git_rebase_options opts = GIT_REBASE_OPTIONS_INIT;
+
+ opts.inmemory = true;
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/barley"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, &opts));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+
+ cl_git_pass(git_rebase_finish(rebase, signature));
+
+ git_oid__fromstr(&expected_final_id, "71e7ee8d4fe7d8bf0d107355197e0a953dfdb7f3", GIT_OID_SHA1);
+ cl_assert_equal_oid(&expected_final_id, &commit_id);
+
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_reference_free(branch_ref);
+ git_reference_free(upstream_ref);
+ git_rebase_free(rebase);
+}
+
+void test_rebase_inmemory__with_directories(void)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref, *upstream_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_rebase_operation *rebase_operation;
+ git_oid commit_id, tree_id;
+ git_commit *commit;
+ git_rebase_options opts = GIT_REBASE_OPTIONS_INIT;
+
+ opts.inmemory = true;
+
+ git_oid__fromstr(&tree_id, "a4d6d9c3d57308fd8e320cf2525bae8f1adafa57", GIT_OID_SHA1);
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/deep_gravy"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/veal"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, &opts));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+
+ cl_git_fail_with(GIT_ITEROVER, git_rebase_next(&rebase_operation, rebase));
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &commit_id));
+ cl_assert_equal_oid(&tree_id, git_commit_tree_id(commit));
+
+ git_commit_free(commit);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_reference_free(branch_ref);
+ git_reference_free(upstream_ref);
+ git_rebase_free(rebase);
+}
diff --git a/tests/libgit2/rebase/iterator.c b/tests/libgit2/rebase/iterator.c
new file mode 100644
index 0000000..79e9f3e
--- /dev/null
+++ b/tests/libgit2/rebase/iterator.c
@@ -0,0 +1,140 @@
+#include "clar_libgit2.h"
+#include "git2/rebase.h"
+#include "posix.h"
+
+#include <fcntl.h>
+
+static git_repository *repo;
+static git_index *_index;
+static git_signature *signature;
+
+/* Fixture setup and teardown */
+void test_rebase_iterator__initialize(void)
+{
+ repo = cl_git_sandbox_init("rebase");
+ cl_git_pass(git_repository_index(&_index, repo));
+ cl_git_pass(git_signature_new(&signature, "Rebaser",
+ "rebaser@rebaser.rb", 1405694510, 0));
+}
+
+void test_rebase_iterator__cleanup(void)
+{
+ git_signature_free(signature);
+ git_index_free(_index);
+ cl_git_sandbox_cleanup();
+}
+
+static void test_operations(git_rebase *rebase, size_t expected_current)
+{
+ size_t i, expected_count = 5;
+ git_oid expected_oid[5];
+ git_rebase_operation *operation;
+
+ git_oid__fromstr(&expected_oid[0], "da9c51a23d02d931a486f45ad18cda05cf5d2b94", GIT_OID_SHA1);
+ git_oid__fromstr(&expected_oid[1], "8d1f13f93c4995760ac07d129246ac1ff64c0be9", GIT_OID_SHA1);
+ git_oid__fromstr(&expected_oid[2], "3069cc907e6294623e5917ef6de663928c1febfb", GIT_OID_SHA1);
+ git_oid__fromstr(&expected_oid[3], "588e5d2f04d49707fe4aab865e1deacaf7ef6787", GIT_OID_SHA1);
+ git_oid__fromstr(&expected_oid[4], "b146bd7608eac53d9bf9e1a6963543588b555c64", GIT_OID_SHA1);
+
+ cl_assert_equal_i(expected_count, git_rebase_operation_entrycount(rebase));
+ cl_assert_equal_i(expected_current, git_rebase_operation_current(rebase));
+
+ for (i = 0; i < expected_count; i++) {
+ operation = git_rebase_operation_byindex(rebase, i);
+ cl_assert_equal_i(GIT_REBASE_OPERATION_PICK, operation->type);
+ cl_assert_equal_oid(&expected_oid[i], &operation->id);
+ cl_assert_equal_p(NULL, operation->exec);
+ }
+}
+
+static void test_iterator(bool inmemory)
+{
+ git_rebase *rebase;
+ git_rebase_options opts = GIT_REBASE_OPTIONS_INIT;
+ git_reference *branch_ref, *upstream_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_rebase_operation *rebase_operation;
+ git_oid commit_id, expected_id;
+ int error;
+
+ opts.inmemory = inmemory;
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, &opts));
+ test_operations(rebase, GIT_REBASE_NO_OPERATION);
+
+ if (!inmemory) {
+ git_rebase_free(rebase);
+ cl_git_pass(git_rebase_open(&rebase, repo, NULL));
+ }
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+ test_operations(rebase, 0);
+
+ git_oid__fromstr(&expected_id, "776e4c48922799f903f03f5f6e51da8b01e4cce0", GIT_OID_SHA1);
+ cl_assert_equal_oid(&expected_id, &commit_id);
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+ test_operations(rebase, 1);
+
+ git_oid__fromstr(&expected_id, "ba1f9b4fd5cf8151f7818be2111cc0869f1eb95a", GIT_OID_SHA1);
+ cl_assert_equal_oid(&expected_id, &commit_id);
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+ test_operations(rebase, 2);
+
+ git_oid__fromstr(&expected_id, "948b12fe18b84f756223a61bece4c307787cd5d4", GIT_OID_SHA1);
+ cl_assert_equal_oid(&expected_id, &commit_id);
+
+ if (!inmemory) {
+ git_rebase_free(rebase);
+ cl_git_pass(git_rebase_open(&rebase, repo, NULL));
+ }
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+ test_operations(rebase, 3);
+
+ git_oid__fromstr(&expected_id, "d9d5d59d72c9968687f9462578d79878cd80e781", GIT_OID_SHA1);
+ cl_assert_equal_oid(&expected_id, &commit_id);
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+ test_operations(rebase, 4);
+
+ git_oid__fromstr(&expected_id, "9cf383c0a125d89e742c5dec58ed277dd07588b3", GIT_OID_SHA1);
+ cl_assert_equal_oid(&expected_id, &commit_id);
+
+ cl_git_fail(error = git_rebase_next(&rebase_operation, rebase));
+ cl_assert_equal_i(GIT_ITEROVER, error);
+ test_operations(rebase, 4);
+
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_reference_free(branch_ref);
+ git_reference_free(upstream_ref);
+ git_rebase_free(rebase);
+}
+
+void test_rebase_iterator__iterates(void)
+{
+ test_iterator(false);
+}
+
+void test_rebase_iterator__iterates_inmemory(void)
+{
+ test_iterator(true);
+}
diff --git a/tests/libgit2/rebase/merge.c b/tests/libgit2/rebase/merge.c
new file mode 100644
index 0000000..870c3ea
--- /dev/null
+++ b/tests/libgit2/rebase/merge.c
@@ -0,0 +1,855 @@
+#include "clar_libgit2.h"
+#include "git2/checkout.h"
+#include "git2/rebase.h"
+#include "posix.h"
+#include "signature.h"
+
+#include <fcntl.h>
+
+static git_repository *repo;
+static git_signature *signature;
+
+static void set_core_autocrlf_to(git_repository *repo, bool value)
+{
+ git_config *cfg;
+
+ cl_git_pass(git_repository_config(&cfg, repo));
+ cl_git_pass(git_config_set_bool(cfg, "core.autocrlf", value));
+
+ git_config_free(cfg);
+}
+
+/* Fixture setup and teardown */
+void test_rebase_merge__initialize(void)
+{
+ repo = cl_git_sandbox_init("rebase");
+ cl_git_pass(git_signature_new(&signature,
+ "Rebaser", "rebaser@rebaser.rb", 1405694510, 0));
+
+ set_core_autocrlf_to(repo, false);
+}
+
+void test_rebase_merge__cleanup(void)
+{
+ git_signature_free(signature);
+ cl_git_sandbox_cleanup();
+}
+
+void test_rebase_merge__next(void)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref, *upstream_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_rebase_operation *rebase_operation;
+ git_status_list *status_list;
+ const git_status_entry *status_entry;
+ git_oid pick_id, file1_id;
+ git_oid master_id, beef_id;
+
+ git_oid__fromstr(&master_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00", GIT_OID_SHA1);
+ git_oid__fromstr(&beef_id, "b146bd7608eac53d9bf9e1a6963543588b555c64", GIT_OID_SHA1);
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, NULL));
+
+ cl_assert_equal_s("refs/heads/beef", git_rebase_orig_head_name(rebase));
+ cl_assert_equal_oid(&beef_id, git_rebase_orig_head_id(rebase));
+
+ cl_assert_equal_s("master", git_rebase_onto_name(rebase));
+ cl_assert_equal_oid(&master_id, git_rebase_onto_id(rebase));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+
+ git_oid__fromstr(&pick_id, "da9c51a23d02d931a486f45ad18cda05cf5d2b94", GIT_OID_SHA1);
+
+ cl_assert_equal_i(GIT_REBASE_OPERATION_PICK, rebase_operation->type);
+ cl_assert_equal_oid(&pick_id, &rebase_operation->id);
+ cl_assert_equal_file("da9c51a23d02d931a486f45ad18cda05cf5d2b94\n", 41, "rebase/.git/rebase-merge/current");
+ cl_assert_equal_file("1\n", 2, "rebase/.git/rebase-merge/msgnum");
+
+ cl_git_pass(git_status_list_new(&status_list, repo, NULL));
+ cl_assert_equal_i(1, git_status_list_entrycount(status_list));
+ cl_assert(status_entry = git_status_byindex(status_list, 0));
+
+ cl_assert_equal_s("beef.txt", status_entry->head_to_index->new_file.path);
+
+ git_oid__fromstr(&file1_id, "8d95ea62e621f1d38d230d9e7d206e41096d76af", GIT_OID_SHA1);
+ cl_assert_equal_oid(&file1_id, &status_entry->head_to_index->new_file.id);
+
+ git_status_list_free(status_list);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_reference_free(branch_ref);
+ git_reference_free(upstream_ref);
+ git_rebase_free(rebase);
+}
+
+void test_rebase_merge__next_with_conflicts(void)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref, *upstream_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_rebase_operation *rebase_operation;
+ git_status_list *status_list;
+ const git_status_entry *status_entry;
+ git_oid pick_id, commit_id;
+
+ const char *expected_merge =
+"ASPARAGUS SOUP.\n"
+"\n"
+"<<<<<<< master\n"
+"TAKE FOUR LARGE BUNCHES of asparagus, scrape it nicely, cut off one inch\n"
+"OF THE TOPS, and lay them in water, chop the stalks and put them on the\n"
+"FIRE WITH A PIECE OF BACON, a large onion cut up, and pepper and salt;\n"
+"ADD TWO QUARTS OF WATER, boil them till the stalks are quite soft, then\n"
+"PULP THEM THROUGH A SIEVE, and strain the water to it, which must be put\n"
+"=======\n"
+"Take four large bunches of asparagus, scrape it nicely, CUT OFF ONE INCH\n"
+"of the tops, and lay them in water, chop the stalks and PUT THEM ON THE\n"
+"fire with a piece of bacon, a large onion cut up, and pepper and salt;\n"
+"add two quarts of water, boil them till the stalks are quite soft, then\n"
+"pulp them through a sieve, and strain the water to it, which must be put\n"
+">>>>>>> Conflicting modification 1 to asparagus\n"
+"back in the pot; put into it a chicken cut up, with the tops of\n"
+"asparagus which had been laid by, boil it until these last articles are\n"
+"sufficiently done, thicken with flour, butter and milk, and serve it up.\n";
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/asparagus"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, NULL));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+
+ git_oid__fromstr(&pick_id, "33f915f9e4dbd9f4b24430e48731a59b45b15500", GIT_OID_SHA1);
+
+ cl_assert_equal_i(GIT_REBASE_OPERATION_PICK, rebase_operation->type);
+ cl_assert_equal_oid(&pick_id, &rebase_operation->id);
+ cl_assert_equal_file("33f915f9e4dbd9f4b24430e48731a59b45b15500\n", 41, "rebase/.git/rebase-merge/current");
+ cl_assert_equal_file("1\n", 2, "rebase/.git/rebase-merge/msgnum");
+
+ cl_git_pass(git_status_list_new(&status_list, repo, NULL));
+ cl_assert_equal_i(1, git_status_list_entrycount(status_list));
+ cl_assert(status_entry = git_status_byindex(status_list, 0));
+
+ cl_assert_equal_s("asparagus.txt", status_entry->head_to_index->new_file.path);
+
+ cl_assert_equal_file(expected_merge, strlen(expected_merge), "rebase/asparagus.txt");
+
+ cl_git_fail_with(GIT_EUNMERGED, git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL));
+
+ git_status_list_free(status_list);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_reference_free(branch_ref);
+ git_reference_free(upstream_ref);
+ git_rebase_free(rebase);
+}
+
+void test_rebase_merge__next_stops_with_iterover(void)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref, *upstream_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_rebase_operation *rebase_operation;
+ git_oid commit_id;
+ int error;
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, NULL));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+
+ cl_git_fail(error = git_rebase_next(&rebase_operation, rebase));
+ cl_assert_equal_i(GIT_ITEROVER, error);
+
+ cl_assert_equal_file("5\n", 2, "rebase/.git/rebase-merge/end");
+ cl_assert_equal_file("5\n", 2, "rebase/.git/rebase-merge/msgnum");
+
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_reference_free(branch_ref);
+ git_reference_free(upstream_ref);
+ git_rebase_free(rebase);
+}
+
+void test_rebase_merge__commit(void)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref, *upstream_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_rebase_operation *rebase_operation;
+ git_oid commit_id, tree_id, parent_id;
+ git_signature *author;
+ git_commit *commit;
+ git_reflog *reflog;
+ const git_reflog_entry *reflog_entry;
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, NULL));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &commit_id));
+
+ git_oid__fromstr(&parent_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00", GIT_OID_SHA1);
+ cl_assert_equal_i(1, git_commit_parentcount(commit));
+ cl_assert_equal_oid(&parent_id, git_commit_parent_id(commit, 0));
+
+ git_oid__fromstr(&tree_id, "4461379789c777d2a6c1f2ee0e9d6c86731b9992", GIT_OID_SHA1);
+ cl_assert_equal_oid(&tree_id, git_commit_tree_id(commit));
+
+ cl_assert_equal_s(NULL, git_commit_message_encoding(commit));
+ cl_assert_equal_s("Modification 1 to beef\n", git_commit_message(commit));
+
+ cl_git_pass(git_signature_new(&author,
+ "Edward Thomson", "ethomson@edwardthomson.com", 1405621769, 0-(4*60)));
+ cl_assert(git_signature__equal(author, git_commit_author(commit)));
+
+ cl_assert(git_signature__equal(signature, git_commit_committer(commit)));
+
+ /* Make sure the reflogs are updated appropriately */
+ cl_git_pass(git_reflog_read(&reflog, repo, "HEAD"));
+ cl_assert(reflog_entry = git_reflog_entry_byindex(reflog, 0));
+ cl_assert_equal_oid(&parent_id, git_reflog_entry_id_old(reflog_entry));
+ cl_assert_equal_oid(&commit_id, git_reflog_entry_id_new(reflog_entry));
+ cl_assert_equal_s("rebase: Modification 1 to beef", git_reflog_entry_message(reflog_entry));
+
+ git_reflog_free(reflog);
+ git_signature_free(author);
+ git_commit_free(commit);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_reference_free(branch_ref);
+ git_reference_free(upstream_ref);
+ git_rebase_free(rebase);
+}
+
+void test_rebase_merge__commit_with_id(void)
+{
+ git_rebase *rebase;
+ git_oid branch_id, upstream_id;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_rebase_operation *rebase_operation;
+ git_oid commit_id, tree_id, parent_id;
+ git_signature *author;
+ git_commit *commit;
+ git_reflog *reflog;
+ const git_reflog_entry *reflog_entry;
+
+ cl_git_pass(git_oid__fromstr(&branch_id, "b146bd7608eac53d9bf9e1a6963543588b555c64", GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&upstream_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00", GIT_OID_SHA1));
+
+ cl_git_pass(git_annotated_commit_lookup(&branch_head, repo, &branch_id));
+ cl_git_pass(git_annotated_commit_lookup(&upstream_head, repo, &upstream_id));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, NULL));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &commit_id));
+
+ git_oid__fromstr(&parent_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00", GIT_OID_SHA1);
+ cl_assert_equal_i(1, git_commit_parentcount(commit));
+ cl_assert_equal_oid(&parent_id, git_commit_parent_id(commit, 0));
+
+ git_oid__fromstr(&tree_id, "4461379789c777d2a6c1f2ee0e9d6c86731b9992", GIT_OID_SHA1);
+ cl_assert_equal_oid(&tree_id, git_commit_tree_id(commit));
+
+ cl_assert_equal_s(NULL, git_commit_message_encoding(commit));
+ cl_assert_equal_s("Modification 1 to beef\n", git_commit_message(commit));
+
+ cl_git_pass(git_signature_new(&author,
+ "Edward Thomson", "ethomson@edwardthomson.com", 1405621769, 0-(4*60)));
+ cl_assert(git_signature__equal(author, git_commit_author(commit)));
+
+ cl_assert(git_signature__equal(signature, git_commit_committer(commit)));
+
+ /* Make sure the reflogs are updated appropriately */
+ cl_git_pass(git_reflog_read(&reflog, repo, "HEAD"));
+ cl_assert(reflog_entry = git_reflog_entry_byindex(reflog, 0));
+ cl_assert_equal_oid(&parent_id, git_reflog_entry_id_old(reflog_entry));
+ cl_assert_equal_oid(&commit_id, git_reflog_entry_id_new(reflog_entry));
+ cl_assert_equal_s("rebase: Modification 1 to beef", git_reflog_entry_message(reflog_entry));
+
+ git_reflog_free(reflog);
+ git_signature_free(author);
+ git_commit_free(commit);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_rebase_free(rebase);
+}
+
+void test_rebase_merge__blocked_when_dirty(void)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref, *upstream_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_rebase_operation *rebase_operation;
+ git_oid commit_id;
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, NULL));
+
+ /* Allow untracked files */
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_mkfile("rebase/untracked_file.txt", "This is untracked\n");
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+
+ /* Do not allow unstaged */
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_mkfile("rebase/veal.txt", "This is an unstaged change\n");
+ cl_git_fail_with(GIT_EUNMERGED, git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_reference_free(branch_ref);
+ git_reference_free(upstream_ref);
+ git_rebase_free(rebase);
+}
+
+void test_rebase_merge__commit_updates_rewritten(void)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref, *upstream_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_rebase_operation *rebase_operation;
+ git_oid commit_id;
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, NULL));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+
+ cl_assert_equal_file(
+ "da9c51a23d02d931a486f45ad18cda05cf5d2b94 776e4c48922799f903f03f5f6e51da8b01e4cce0\n"
+ "8d1f13f93c4995760ac07d129246ac1ff64c0be9 ba1f9b4fd5cf8151f7818be2111cc0869f1eb95a\n",
+ 164, "rebase/.git/rebase-merge/rewritten");
+
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_reference_free(branch_ref);
+ git_reference_free(upstream_ref);
+ git_rebase_free(rebase);
+}
+
+void test_rebase_merge__commit_drops_already_applied(void)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref, *upstream_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_rebase_operation *rebase_operation;
+ git_oid commit_id;
+ int error;
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/green_pea"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, NULL));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_fail(error = git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+
+ cl_assert_equal_i(GIT_EAPPLIED, error);
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+
+ cl_assert_equal_file(
+ "8d1f13f93c4995760ac07d129246ac1ff64c0be9 2ac4fb7b74c1287f6c792acad759e1ec01e18dae\n",
+ 82, "rebase/.git/rebase-merge/rewritten");
+
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_reference_free(branch_ref);
+ git_reference_free(upstream_ref);
+ git_rebase_free(rebase);
+}
+
+void test_rebase_merge__finish(void)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref, *upstream_ref, *head_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_rebase_operation *rebase_operation;
+ git_oid commit_id;
+ git_reflog *reflog;
+ const git_reflog_entry *reflog_entry;
+ int error;
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/gravy"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/veal"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, NULL));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+
+ cl_git_fail(error = git_rebase_next(&rebase_operation, rebase));
+ cl_assert_equal_i(GIT_ITEROVER, error);
+
+ cl_git_pass(git_rebase_finish(rebase, signature));
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
+
+ cl_git_pass(git_reference_lookup(&head_ref, repo, "HEAD"));
+ cl_assert_equal_i(GIT_REFERENCE_SYMBOLIC, git_reference_type(head_ref));
+ cl_assert_equal_s("refs/heads/gravy", git_reference_symbolic_target(head_ref));
+
+ /* Make sure the reflogs are updated appropriately */
+ cl_git_pass(git_reflog_read(&reflog, repo, "HEAD"));
+ cl_assert(reflog_entry = git_reflog_entry_byindex(reflog, 0));
+ cl_assert_equal_oid(&commit_id, git_reflog_entry_id_old(reflog_entry));
+ cl_assert_equal_oid(&commit_id, git_reflog_entry_id_new(reflog_entry));
+ cl_assert_equal_s("rebase finished: returning to refs/heads/gravy", git_reflog_entry_message(reflog_entry));
+ git_reflog_free(reflog);
+
+ cl_git_pass(git_reflog_read(&reflog, repo, "refs/heads/gravy"));
+ cl_assert(reflog_entry = git_reflog_entry_byindex(reflog, 0));
+ cl_assert_equal_oid(git_annotated_commit_id(branch_head), git_reflog_entry_id_old(reflog_entry));
+ cl_assert_equal_oid(&commit_id, git_reflog_entry_id_new(reflog_entry));
+ cl_assert_equal_s("rebase finished: refs/heads/gravy onto f87d14a4a236582a0278a916340a793714256864", git_reflog_entry_message(reflog_entry));
+
+ git_reflog_free(reflog);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_reference_free(head_ref);
+ git_reference_free(branch_ref);
+ git_reference_free(upstream_ref);
+ git_rebase_free(rebase);
+}
+
+void test_rebase_merge__detached_finish(void)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref, *upstream_ref, *head_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_rebase_operation *rebase_operation;
+ git_oid commit_id;
+ git_reflog *reflog;
+ const git_reflog_entry *reflog_entry;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ int error;
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/gravy"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/veal"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_repository_set_head_detached_from_annotated(repo, branch_head));
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+ git_checkout_head(repo, &opts);
+
+ cl_git_pass(git_rebase_init(&rebase, repo, NULL, upstream_head, NULL, NULL));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+
+ cl_git_fail(error = git_rebase_next(&rebase_operation, rebase));
+ cl_assert_equal_i(GIT_ITEROVER, error);
+
+ cl_git_pass(git_rebase_finish(rebase, signature));
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
+
+ cl_git_pass(git_reference_lookup(&head_ref, repo, "HEAD"));
+ cl_assert_equal_i(GIT_REFERENCE_DIRECT, git_reference_type(head_ref));
+
+ /* Make sure the reflogs are updated appropriately */
+ cl_git_pass(git_reflog_read(&reflog, repo, "HEAD"));
+ cl_assert(reflog_entry = git_reflog_entry_byindex(reflog, 0));
+ cl_assert_equal_oid(git_annotated_commit_id(upstream_head), git_reflog_entry_id_old(reflog_entry));
+ cl_assert_equal_oid(&commit_id, git_reflog_entry_id_new(reflog_entry));
+
+ git_reflog_free(reflog);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_reference_free(head_ref);
+ git_reference_free(branch_ref);
+ git_reference_free(upstream_ref);
+ git_rebase_free(rebase);
+}
+
+void test_rebase_merge__finish_with_ids(void)
+{
+ git_rebase *rebase;
+ git_reference *head_ref;
+ git_oid branch_id, upstream_id;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_rebase_operation *rebase_operation;
+ git_oid commit_id;
+ git_reflog *reflog;
+ const git_reflog_entry *reflog_entry;
+ int error;
+
+ cl_git_pass(git_oid__fromstr(&branch_id, "d616d97082eb7bb2dc6f180a7cca940993b7a56f", GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&upstream_id, "f87d14a4a236582a0278a916340a793714256864", GIT_OID_SHA1));
+
+ cl_git_pass(git_annotated_commit_lookup(&branch_head, repo, &branch_id));
+ cl_git_pass(git_annotated_commit_lookup(&upstream_head, repo, &upstream_id));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, NULL));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+
+ cl_git_fail(error = git_rebase_next(&rebase_operation, rebase));
+ cl_assert_equal_i(GIT_ITEROVER, error);
+
+ cl_git_pass(git_rebase_finish(rebase, signature));
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
+
+ cl_git_pass(git_reference_lookup(&head_ref, repo, "HEAD"));
+ cl_assert_equal_i(GIT_REFERENCE_DIRECT, git_reference_type(head_ref));
+ cl_assert_equal_oid(&commit_id, git_reference_target(head_ref));
+
+ /* reflogs are not updated as if we were operating on proper
+ * branches. check that the last reflog entry is the rebase.
+ */
+ cl_git_pass(git_reflog_read(&reflog, repo, "HEAD"));
+ cl_assert(reflog_entry = git_reflog_entry_byindex(reflog, 0));
+ cl_assert_equal_oid(&commit_id, git_reflog_entry_id_new(reflog_entry));
+ cl_assert_equal_s("rebase: Modification 3 to gravy", git_reflog_entry_message(reflog_entry));
+ git_reflog_free(reflog);
+
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_reference_free(head_ref);
+ git_rebase_free(rebase);
+}
+
+void test_rebase_merge__no_common_ancestor(void)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref, *upstream_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_rebase_operation *rebase_operation;
+ git_oid commit_id, expected_final_id;
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/barley"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, NULL));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+
+ cl_git_pass(git_rebase_finish(rebase, signature));
+
+ git_oid__fromstr(&expected_final_id, "71e7ee8d4fe7d8bf0d107355197e0a953dfdb7f3", GIT_OID_SHA1);
+ cl_assert_equal_oid(&expected_final_id, &commit_id);
+
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_reference_free(branch_ref);
+ git_reference_free(upstream_ref);
+ git_rebase_free(rebase);
+}
+
+static void test_copy_note(
+ const git_rebase_options *opts,
+ bool should_exist)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref, *upstream_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_commit *branch_commit;
+ git_rebase_operation *rebase_operation;
+ git_oid note_id, commit_id;
+ git_note *note = NULL;
+ int error;
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/gravy"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/veal"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_reference_peel((git_object **)&branch_commit,
+ branch_ref, GIT_OBJECT_COMMIT));
+
+ /* Add a note to a commit */
+ cl_git_pass(git_note_create(&note_id, repo, "refs/notes/test",
+ git_commit_author(branch_commit), git_commit_committer(branch_commit),
+ git_commit_id(branch_commit),
+ "This is a commit note.", 0));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, opts));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+
+ cl_git_pass(git_rebase_finish(rebase, signature));
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
+
+ if (should_exist) {
+ cl_git_pass(git_note_read(&note, repo, "refs/notes/test", &commit_id));
+ cl_assert_equal_s("This is a commit note.", git_note_message(note));
+ } else {
+ cl_git_fail(error =
+ git_note_read(&note, repo, "refs/notes/test", &commit_id));
+ cl_assert_equal_i(GIT_ENOTFOUND, error);
+ }
+
+ git_note_free(note);
+ git_commit_free(branch_commit);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_reference_free(branch_ref);
+ git_reference_free(upstream_ref);
+ git_rebase_free(rebase);
+}
+
+void test_rebase_merge__copy_notes_off_by_default(void)
+{
+ test_copy_note(NULL, 0);
+}
+
+void test_rebase_merge__copy_notes_specified_in_options(void)
+{
+ git_rebase_options opts = GIT_REBASE_OPTIONS_INIT;
+ opts.rewrite_notes_ref = "refs/notes/test";
+
+ test_copy_note(&opts, 1);
+}
+
+void test_rebase_merge__copy_notes_specified_in_config(void)
+{
+ git_config *config;
+
+ cl_git_pass(git_repository_config(&config, repo));
+ cl_git_pass(git_config_set_string(config,
+ "notes.rewriteRef", "refs/notes/test"));
+
+ test_copy_note(NULL, 1);
+}
+
+void test_rebase_merge__copy_notes_disabled_in_config(void)
+{
+ git_config *config;
+
+ cl_git_pass(git_repository_config(&config, repo));
+ cl_git_pass(git_config_set_bool(config, "notes.rewrite.rebase", 0));
+ cl_git_pass(git_config_set_string(config,
+ "notes.rewriteRef", "refs/notes/test"));
+
+ test_copy_note(NULL, 0);
+}
+
+static void rebase_checkout_progress_cb(
+ const char *path,
+ size_t completed_steps,
+ size_t total_steps,
+ void *payload)
+{
+ int *called = payload;
+
+ GIT_UNUSED(path);
+ GIT_UNUSED(completed_steps);
+ GIT_UNUSED(total_steps);
+
+ *called = 1;
+}
+
+void test_rebase_merge__custom_checkout_options(void)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref, *upstream_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_rebase_options rebase_options = GIT_REBASE_OPTIONS_INIT;
+ git_checkout_options checkout_options = GIT_CHECKOUT_OPTIONS_INIT;
+ git_rebase_operation *rebase_operation;
+ int called = 0;
+
+ checkout_options.progress_cb = rebase_checkout_progress_cb;
+ checkout_options.progress_payload = &called;
+
+ memcpy(&rebase_options.checkout_options, &checkout_options,
+ sizeof(git_checkout_options));
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ called = 0;
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, &rebase_options));
+ cl_assert_equal_i(1, called);
+
+ called = 0;
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_assert_equal_i(1, called);
+
+ called = 0;
+ cl_git_pass(git_rebase_abort(rebase));
+ cl_assert_equal_i(1, called);
+
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_reference_free(branch_ref);
+ git_reference_free(upstream_ref);
+ git_rebase_free(rebase);
+}
+
+void test_rebase_merge__custom_merge_options(void)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref, *upstream_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_rebase_options rebase_options = GIT_REBASE_OPTIONS_INIT;
+ git_rebase_operation *rebase_operation;
+
+ rebase_options.merge_options.flags |=
+ GIT_MERGE_FAIL_ON_CONFLICT |
+ GIT_MERGE_SKIP_REUC;
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/asparagus"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, &rebase_options));
+
+ cl_git_fail_with(GIT_EMERGECONFLICT, git_rebase_next(&rebase_operation, rebase));
+
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_reference_free(branch_ref);
+ git_reference_free(upstream_ref);
+ git_rebase_free(rebase);
+}
+
+void test_rebase_merge__with_directories(void)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref, *upstream_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_rebase_operation *rebase_operation;
+ git_oid commit_id, tree_id;
+ git_commit *commit;
+
+ git_oid__fromstr(&tree_id, "a4d6d9c3d57308fd8e320cf2525bae8f1adafa57", GIT_OID_SHA1);
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/deep_gravy"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/veal"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, NULL));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature,
+ NULL, NULL));
+
+ cl_git_fail_with(GIT_ITEROVER, git_rebase_next(&rebase_operation, rebase));
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &commit_id));
+ cl_assert_equal_oid(&tree_id, git_commit_tree_id(commit));
+
+ git_commit_free(commit);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_reference_free(branch_ref);
+ git_reference_free(upstream_ref);
+ git_rebase_free(rebase);
+}
diff --git a/tests/libgit2/rebase/setup.c b/tests/libgit2/rebase/setup.c
new file mode 100644
index 0000000..ac0d087
--- /dev/null
+++ b/tests/libgit2/rebase/setup.c
@@ -0,0 +1,596 @@
+#include "clar_libgit2.h"
+#include "git2/rebase.h"
+#include "posix.h"
+
+#include <fcntl.h>
+
+static git_repository *repo;
+static git_index *_index;
+static git_signature *signature;
+
+/* Fixture setup and teardown */
+void test_rebase_setup__initialize(void)
+{
+ repo = cl_git_sandbox_init("rebase");
+ cl_git_pass(git_repository_index(&_index, repo));
+ cl_git_pass(git_signature_now(&signature, "Rebaser", "rebaser@rebaser.rb"));
+}
+
+void test_rebase_setup__cleanup(void)
+{
+ git_signature_free(signature);
+ git_index_free(_index);
+ cl_git_sandbox_cleanup();
+}
+
+/* git checkout beef ; git rebase --merge master
+ * git checkout beef ; git rebase --merge master */
+void test_rebase_setup__blocked_when_in_progress(void)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref, *upstream_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, NULL));
+ git_rebase_free(rebase);
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
+
+ cl_git_fail(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, NULL));
+
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_reference_free(branch_ref);
+ git_reference_free(upstream_ref);
+}
+
+/* git checkout beef ; git rebase --merge master */
+void test_rebase_setup__merge(void)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref, *upstream_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_reference *head;
+ git_commit *head_commit;
+ git_oid head_id;
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, NULL));
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
+
+ git_oid__fromstr(&head_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00", GIT_OID_SHA1);
+ cl_git_pass(git_repository_head(&head, repo));
+ cl_git_pass(git_reference_peel((git_object **)&head_commit, head, GIT_OBJECT_COMMIT));
+ cl_assert_equal_oid(&head_id, git_commit_id(head_commit));
+
+ cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/ORIG_HEAD");
+
+ cl_assert_equal_file("da9c51a23d02d931a486f45ad18cda05cf5d2b94\n", 41, "rebase/.git/rebase-merge/cmt.1");
+ cl_assert_equal_file("8d1f13f93c4995760ac07d129246ac1ff64c0be9\n", 41, "rebase/.git/rebase-merge/cmt.2");
+ cl_assert_equal_file("3069cc907e6294623e5917ef6de663928c1febfb\n", 41, "rebase/.git/rebase-merge/cmt.3");
+ cl_assert_equal_file("588e5d2f04d49707fe4aab865e1deacaf7ef6787\n", 41, "rebase/.git/rebase-merge/cmt.4");
+ cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/rebase-merge/cmt.5");
+ cl_assert_equal_file("5\n", 2, "rebase/.git/rebase-merge/end");
+ cl_assert_equal_file("efad0b11c47cb2f0220cbd6f5b0f93bb99064b00\n", 41, "rebase/.git/rebase-merge/onto");
+ cl_assert_equal_file("master\n", 7, "rebase/.git/rebase-merge/onto_name");
+ cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/rebase-merge/orig-head");
+
+ git_commit_free(head_commit);
+ git_reference_free(head);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_reference_free(branch_ref);
+ git_reference_free(upstream_ref);
+ git_rebase_free(rebase);
+}
+
+/* git checkout beef && git rebase --merge --root --onto master */
+void test_rebase_setup__merge_root(void)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref, *onto_ref;
+ git_annotated_commit *branch_head, *onto_head;
+ git_reference *head;
+ git_commit *head_commit;
+ git_oid head_id;
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef"));
+ cl_git_pass(git_reference_lookup(&onto_ref, repo, "refs/heads/master"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&onto_head, repo, onto_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, NULL, onto_head, NULL));
+
+ git_oid__fromstr(&head_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00", GIT_OID_SHA1);
+ cl_git_pass(git_repository_head(&head, repo));
+ cl_git_pass(git_reference_peel((git_object **)&head_commit, head, GIT_OBJECT_COMMIT));
+ cl_assert_equal_oid(&head_id, git_commit_id(head_commit));
+
+ cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/ORIG_HEAD");
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
+
+ cl_assert_equal_file("da9c51a23d02d931a486f45ad18cda05cf5d2b94\n", 41, "rebase/.git/rebase-merge/cmt.1");
+ cl_assert_equal_file("8d1f13f93c4995760ac07d129246ac1ff64c0be9\n", 41, "rebase/.git/rebase-merge/cmt.2");
+ cl_assert_equal_file("3069cc907e6294623e5917ef6de663928c1febfb\n", 41, "rebase/.git/rebase-merge/cmt.3");
+ cl_assert_equal_file("588e5d2f04d49707fe4aab865e1deacaf7ef6787\n", 41, "rebase/.git/rebase-merge/cmt.4");
+ cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/rebase-merge/cmt.5");
+ cl_assert_equal_file("5\n", 2, "rebase/.git/rebase-merge/end");
+ cl_assert_equal_file("efad0b11c47cb2f0220cbd6f5b0f93bb99064b00\n", 41, "rebase/.git/rebase-merge/onto");
+ cl_assert_equal_file("master\n", 7, "rebase/.git/rebase-merge/onto_name");
+ cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/rebase-merge/orig-head");
+
+ git_commit_free(head_commit);
+ git_reference_free(head);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(onto_head);
+ git_reference_free(branch_ref);
+ git_reference_free(onto_ref);
+ git_rebase_free(rebase);
+}
+
+/* git checkout gravy && git rebase --merge --onto master veal */
+void test_rebase_setup__merge_onto_and_upstream(void)
+{
+ git_rebase *rebase;
+ git_reference *branch1_ref, *branch2_ref, *onto_ref;
+ git_annotated_commit *branch1_head, *branch2_head, *onto_head;
+ git_reference *head;
+ git_commit *head_commit;
+ git_oid head_id;
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
+
+ cl_git_pass(git_reference_lookup(&branch1_ref, repo, "refs/heads/gravy"));
+ cl_git_pass(git_reference_lookup(&branch2_ref, repo, "refs/heads/veal"));
+ cl_git_pass(git_reference_lookup(&onto_ref, repo, "refs/heads/master"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch1_head, repo, branch1_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&branch2_head, repo, branch2_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&onto_head, repo, onto_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch1_head, branch2_head, onto_head, NULL));
+
+ git_oid__fromstr(&head_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00", GIT_OID_SHA1);
+ cl_git_pass(git_repository_head(&head, repo));
+ cl_git_pass(git_reference_peel((git_object **)&head_commit, head, GIT_OBJECT_COMMIT));
+ cl_assert_equal_oid(&head_id, git_commit_id(head_commit));
+
+ cl_assert_equal_file("d616d97082eb7bb2dc6f180a7cca940993b7a56f\n", 41, "rebase/.git/ORIG_HEAD");
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
+
+ cl_assert_equal_file("d616d97082eb7bb2dc6f180a7cca940993b7a56f\n", 41, "rebase/.git/rebase-merge/cmt.1");
+ cl_assert_equal_file("1\n", 2, "rebase/.git/rebase-merge/end");
+ cl_assert_equal_file("efad0b11c47cb2f0220cbd6f5b0f93bb99064b00\n", 41, "rebase/.git/rebase-merge/onto");
+ cl_assert_equal_file("master\n", 7, "rebase/.git/rebase-merge/onto_name");
+ cl_assert_equal_file("d616d97082eb7bb2dc6f180a7cca940993b7a56f\n", 41, "rebase/.git/rebase-merge/orig-head");
+
+ git_commit_free(head_commit);
+ git_reference_free(head);
+ git_annotated_commit_free(branch1_head);
+ git_annotated_commit_free(branch2_head);
+ git_annotated_commit_free(onto_head);
+ git_reference_free(branch1_ref);
+ git_reference_free(branch2_ref);
+ git_reference_free(onto_ref);
+ git_rebase_free(rebase);
+}
+
+/* git checkout beef && git rebase --merge --onto master gravy veal */
+void test_rebase_setup__merge_onto_upstream_and_branch(void)
+{
+ git_rebase *rebase;
+ git_reference *upstream_ref, *branch_ref, *onto_ref;
+ git_annotated_commit *upstream_head, *branch_head, *onto_head;
+ git_reference *head;
+ git_commit *head_commit;
+ git_oid head_id;
+ git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
+
+ cl_git_pass(git_repository_set_head(repo, "refs/heads/beef"));
+ cl_git_pass(git_checkout_head(repo, &checkout_opts));
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/veal"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/gravy"));
+ cl_git_pass(git_reference_lookup(&onto_ref, repo, "refs/heads/master"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&onto_head, repo, onto_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, onto_head, NULL));
+
+ git_oid__fromstr(&head_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00", GIT_OID_SHA1);
+ cl_git_pass(git_repository_head(&head, repo));
+ cl_git_pass(git_reference_peel((git_object **)&head_commit, head, GIT_OBJECT_COMMIT));
+ cl_assert_equal_oid(&head_id, git_commit_id(head_commit));
+
+ cl_assert_equal_file("f87d14a4a236582a0278a916340a793714256864\n", 41, "rebase/.git/ORIG_HEAD");
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
+
+ cl_assert_equal_file("3e8989b5a16d5258c935d998ef0e6bb139cc4757\n", 41, "rebase/.git/rebase-merge/cmt.1");
+ cl_assert_equal_file("4cacc6f6e740a5bc64faa33e04b8ef0733d8a127\n", 41, "rebase/.git/rebase-merge/cmt.2");
+ cl_assert_equal_file("f87d14a4a236582a0278a916340a793714256864\n", 41, "rebase/.git/rebase-merge/cmt.3");
+ cl_assert_equal_file("3\n", 2, "rebase/.git/rebase-merge/end");
+ cl_assert_equal_file("efad0b11c47cb2f0220cbd6f5b0f93bb99064b00\n", 41, "rebase/.git/rebase-merge/onto");
+ cl_assert_equal_file("master\n", 7, "rebase/.git/rebase-merge/onto_name");
+ cl_assert_equal_file("f87d14a4a236582a0278a916340a793714256864\n", 41, "rebase/.git/rebase-merge/orig-head");
+
+ git_commit_free(head_commit);
+ git_reference_free(head);
+ git_annotated_commit_free(upstream_head);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(onto_head);
+ git_reference_free(upstream_ref);
+ git_reference_free(branch_ref);
+ git_reference_free(onto_ref);
+ git_rebase_free(rebase);
+}
+
+/* git checkout beef && git rebase --merge --onto `git rev-parse master`
+ * `git rev-parse veal` `git rev-parse gravy`
+ */
+void test_rebase_setup__merge_onto_upstream_and_branch_by_id(void)
+{
+ git_rebase *rebase;
+ git_oid upstream_id, branch_id, onto_id;
+ git_annotated_commit *upstream_head, *branch_head, *onto_head;
+ git_reference *head;
+ git_commit *head_commit;
+ git_oid head_id;
+ git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
+
+ cl_git_pass(git_repository_set_head(repo, "refs/heads/beef"));
+ cl_git_pass(git_checkout_head(repo, &checkout_opts));
+
+ cl_git_pass(git_oid__fromstr(&upstream_id, "f87d14a4a236582a0278a916340a793714256864", GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&branch_id, "d616d97082eb7bb2dc6f180a7cca940993b7a56f", GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&onto_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00", GIT_OID_SHA1));
+
+ cl_git_pass(git_annotated_commit_lookup(&upstream_head, repo, &upstream_id));
+ cl_git_pass(git_annotated_commit_lookup(&branch_head, repo, &branch_id));
+ cl_git_pass(git_annotated_commit_lookup(&onto_head, repo, &onto_id));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, onto_head, NULL));
+
+ git_oid__fromstr(&head_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00", GIT_OID_SHA1);
+ cl_git_pass(git_repository_head(&head, repo));
+ cl_git_pass(git_reference_peel((git_object **)&head_commit, head, GIT_OBJECT_COMMIT));
+ cl_assert_equal_oid(&head_id, git_commit_id(head_commit));
+
+ cl_assert_equal_file("d616d97082eb7bb2dc6f180a7cca940993b7a56f\n", 41, "rebase/.git/ORIG_HEAD");
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
+
+ cl_assert_equal_file("d616d97082eb7bb2dc6f180a7cca940993b7a56f\n", 41, "rebase/.git/rebase-merge/cmt.1");
+ cl_assert_equal_file("1\n", 2, "rebase/.git/rebase-merge/end");
+ cl_assert_equal_file("efad0b11c47cb2f0220cbd6f5b0f93bb99064b00\n", 41, "rebase/.git/rebase-merge/onto");
+ cl_assert_equal_file("efad0b11c47cb2f0220cbd6f5b0f93bb99064b00\n", 41, "rebase/.git/rebase-merge/onto_name");
+ cl_assert_equal_file("d616d97082eb7bb2dc6f180a7cca940993b7a56f\n", 41, "rebase/.git/rebase-merge/orig-head");
+
+ git_commit_free(head_commit);
+ git_reference_free(head);
+ git_annotated_commit_free(upstream_head);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(onto_head);
+ git_rebase_free(rebase);
+}
+
+/* Ensure merge commits are dropped in a rebase */
+/* git checkout veal && git rebase --merge master */
+void test_rebase_setup__branch_with_merges(void)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref, *upstream_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_reference *head;
+ git_commit *head_commit;
+ git_oid head_id;
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/veal"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, NULL));
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
+
+ git_oid__fromstr(&head_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00", GIT_OID_SHA1);
+ cl_git_pass(git_repository_head(&head, repo));
+ cl_git_pass(git_reference_peel((git_object **)&head_commit, head, GIT_OBJECT_COMMIT));
+ cl_assert_equal_oid(&head_id, git_commit_id(head_commit));
+
+ cl_assert_equal_file("f87d14a4a236582a0278a916340a793714256864\n", 41, "rebase/.git/ORIG_HEAD");
+
+ cl_assert_equal_file("4bed71df7017283cac61bbf726197ad6a5a18b84\n", 41, "rebase/.git/rebase-merge/cmt.1");
+ cl_assert_equal_file("2aa3ce842094e08ebac152b3d6d5b0fff39f9c6e\n", 41, "rebase/.git/rebase-merge/cmt.2");
+ cl_assert_equal_file("3e8989b5a16d5258c935d998ef0e6bb139cc4757\n", 41, "rebase/.git/rebase-merge/cmt.3");
+ cl_assert_equal_file("4cacc6f6e740a5bc64faa33e04b8ef0733d8a127\n", 41, "rebase/.git/rebase-merge/cmt.4");
+ cl_assert_equal_file("f87d14a4a236582a0278a916340a793714256864\n", 41, "rebase/.git/rebase-merge/cmt.5");
+ cl_assert_equal_file("5\n", 2, "rebase/.git/rebase-merge/end");
+ cl_assert_equal_file("efad0b11c47cb2f0220cbd6f5b0f93bb99064b00\n", 41, "rebase/.git/rebase-merge/onto");
+ cl_assert_equal_file("master\n", 7, "rebase/.git/rebase-merge/onto_name");
+ cl_assert_equal_file("f87d14a4a236582a0278a916340a793714256864\n", 41, "rebase/.git/rebase-merge/orig-head");
+
+ git_commit_free(head_commit);
+ git_reference_free(head);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_reference_free(branch_ref);
+ git_reference_free(upstream_ref);
+ git_rebase_free(rebase);
+}
+
+/* git checkout barley && git rebase --merge master */
+void test_rebase_setup__orphan_branch(void)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref, *upstream_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_reference *head;
+ git_commit *head_commit;
+ git_oid head_id;
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/barley"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, NULL));
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
+
+ git_oid__fromstr(&head_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00", GIT_OID_SHA1);
+ cl_git_pass(git_repository_head(&head, repo));
+ cl_git_pass(git_reference_peel((git_object **)&head_commit, head, GIT_OBJECT_COMMIT));
+ cl_assert_equal_oid(&head_id, git_commit_id(head_commit));
+
+ cl_assert_equal_file("12c084412b952396962eb420716df01022b847cc\n", 41, "rebase/.git/ORIG_HEAD");
+
+ cl_assert_equal_file("aa4c42aecdfc7cd989bbc3209934ea7cda3f4d88\n", 41, "rebase/.git/rebase-merge/cmt.1");
+ cl_assert_equal_file("e4f809f826c1a9fc929874bc0e4644dd2f2a1af4\n", 41, "rebase/.git/rebase-merge/cmt.2");
+ cl_assert_equal_file("9539b2cc291d6a6b1b266df8474d31fdd344dd79\n", 41, "rebase/.git/rebase-merge/cmt.3");
+ cl_assert_equal_file("013cc32d341bab0e6f039f50f153c18986f16c58\n", 41, "rebase/.git/rebase-merge/cmt.4");
+ cl_assert_equal_file("12c084412b952396962eb420716df01022b847cc\n", 41, "rebase/.git/rebase-merge/cmt.5");
+ cl_assert_equal_file("5\n", 2, "rebase/.git/rebase-merge/end");
+ cl_assert_equal_file("efad0b11c47cb2f0220cbd6f5b0f93bb99064b00\n", 41, "rebase/.git/rebase-merge/onto");
+ cl_assert_equal_file("master\n", 7, "rebase/.git/rebase-merge/onto_name");
+ cl_assert_equal_file("12c084412b952396962eb420716df01022b847cc\n", 41, "rebase/.git/rebase-merge/orig-head");
+
+ git_commit_free(head_commit);
+ git_reference_free(head);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_reference_free(branch_ref);
+ git_reference_free(upstream_ref);
+ git_rebase_free(rebase);
+}
+
+/* git checkout beef && git rebase --merge master */
+void test_rebase_setup__merge_null_branch_uses_HEAD(void)
+{
+ git_rebase *rebase;
+ git_reference *upstream_ref;
+ git_annotated_commit *upstream_head;
+ git_reference *head;
+ git_commit *head_commit;
+ git_oid head_id;
+ git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
+
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
+
+ cl_git_pass(git_repository_set_head(repo, "refs/heads/beef"));
+ cl_git_pass(git_checkout_head(repo, &checkout_opts));
+
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master"));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, NULL, upstream_head, NULL, NULL));
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
+
+ git_oid__fromstr(&head_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00", GIT_OID_SHA1);
+ cl_git_pass(git_repository_head(&head, repo));
+ cl_git_pass(git_reference_peel((git_object **)&head_commit, head, GIT_OBJECT_COMMIT));
+ cl_assert_equal_oid(&head_id, git_commit_id(head_commit));
+
+ cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/ORIG_HEAD");
+
+ cl_assert_equal_file("da9c51a23d02d931a486f45ad18cda05cf5d2b94\n", 41, "rebase/.git/rebase-merge/cmt.1");
+ cl_assert_equal_file("8d1f13f93c4995760ac07d129246ac1ff64c0be9\n", 41, "rebase/.git/rebase-merge/cmt.2");
+ cl_assert_equal_file("3069cc907e6294623e5917ef6de663928c1febfb\n", 41, "rebase/.git/rebase-merge/cmt.3");
+ cl_assert_equal_file("588e5d2f04d49707fe4aab865e1deacaf7ef6787\n", 41, "rebase/.git/rebase-merge/cmt.4");
+ cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/rebase-merge/cmt.5");
+ cl_assert_equal_file("5\n", 2, "rebase/.git/rebase-merge/end");
+ cl_assert_equal_file("efad0b11c47cb2f0220cbd6f5b0f93bb99064b00\n", 41, "rebase/.git/rebase-merge/onto");
+ cl_assert_equal_file("master\n", 7, "rebase/.git/rebase-merge/onto_name");
+ cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/rebase-merge/orig-head");
+
+ git_commit_free(head_commit);
+ git_reference_free(head);
+ git_annotated_commit_free(upstream_head);
+ git_reference_free(upstream_ref);
+ git_rebase_free(rebase);
+}
+
+/* git checkout b146bd7608eac53d9bf9e1a6963543588b555c64 && git rebase --merge master */
+void test_rebase_setup__merge_from_detached(void)
+{
+ git_rebase *rebase;
+ git_reference *upstream_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_reference *head;
+ git_commit *head_commit;
+ git_oid branch_id, head_id;
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
+
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master"));
+
+ cl_git_pass(git_oid__fromstr(&branch_id, "b146bd7608eac53d9bf9e1a6963543588b555c64", GIT_OID_SHA1));
+
+ cl_git_pass(git_annotated_commit_lookup(&branch_head, repo, &branch_id));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, NULL));
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
+
+ git_oid__fromstr(&head_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00", GIT_OID_SHA1);
+ cl_git_pass(git_repository_head(&head, repo));
+ cl_git_pass(git_reference_peel((git_object **)&head_commit, head, GIT_OBJECT_COMMIT));
+ cl_assert_equal_oid(&head_id, git_commit_id(head_commit));
+
+ cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/ORIG_HEAD");
+
+ cl_assert_equal_file("da9c51a23d02d931a486f45ad18cda05cf5d2b94\n", 41, "rebase/.git/rebase-merge/cmt.1");
+ cl_assert_equal_file("8d1f13f93c4995760ac07d129246ac1ff64c0be9\n", 41, "rebase/.git/rebase-merge/cmt.2");
+ cl_assert_equal_file("3069cc907e6294623e5917ef6de663928c1febfb\n", 41, "rebase/.git/rebase-merge/cmt.3");
+ cl_assert_equal_file("588e5d2f04d49707fe4aab865e1deacaf7ef6787\n", 41, "rebase/.git/rebase-merge/cmt.4");
+ cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/rebase-merge/cmt.5");
+ cl_assert_equal_file("5\n", 2, "rebase/.git/rebase-merge/end");
+ cl_assert_equal_file("efad0b11c47cb2f0220cbd6f5b0f93bb99064b00\n", 41, "rebase/.git/rebase-merge/onto");
+ cl_assert_equal_file("master\n", 7, "rebase/.git/rebase-merge/onto_name");
+ cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/rebase-merge/orig-head");
+
+ git_commit_free(head_commit);
+ git_reference_free(head);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_reference_free(upstream_ref);
+ git_rebase_free(rebase);
+}
+
+/* git checkout beef && git rebase --merge efad0b11c47cb2f0220cbd6f5b0f93bb99064b00 */
+void test_rebase_setup__merge_branch_by_id(void)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_reference *head;
+ git_commit *head_commit;
+ git_oid head_id, upstream_id;
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef"));
+
+ cl_git_pass(git_oid__fromstr(&upstream_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00", GIT_OID_SHA1));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_lookup(&upstream_head, repo, &upstream_id));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, NULL));
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_REBASE_MERGE, git_repository_state(repo));
+
+ git_oid__fromstr(&head_id, "efad0b11c47cb2f0220cbd6f5b0f93bb99064b00", GIT_OID_SHA1);
+ cl_git_pass(git_repository_head(&head, repo));
+ cl_git_pass(git_reference_peel((git_object **)&head_commit, head, GIT_OBJECT_COMMIT));
+ cl_assert_equal_oid(&head_id, git_commit_id(head_commit));
+
+ cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/ORIG_HEAD");
+
+ cl_assert_equal_file("da9c51a23d02d931a486f45ad18cda05cf5d2b94\n", 41, "rebase/.git/rebase-merge/cmt.1");
+ cl_assert_equal_file("8d1f13f93c4995760ac07d129246ac1ff64c0be9\n", 41, "rebase/.git/rebase-merge/cmt.2");
+ cl_assert_equal_file("3069cc907e6294623e5917ef6de663928c1febfb\n", 41, "rebase/.git/rebase-merge/cmt.3");
+ cl_assert_equal_file("588e5d2f04d49707fe4aab865e1deacaf7ef6787\n", 41, "rebase/.git/rebase-merge/cmt.4");
+ cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/rebase-merge/cmt.5");
+ cl_assert_equal_file("5\n", 2, "rebase/.git/rebase-merge/end");
+ cl_assert_equal_file("efad0b11c47cb2f0220cbd6f5b0f93bb99064b00\n", 41, "rebase/.git/rebase-merge/onto");
+ cl_assert_equal_file("efad0b11c47cb2f0220cbd6f5b0f93bb99064b00\n", 41, "rebase/.git/rebase-merge/onto_name");
+ cl_assert_equal_file("b146bd7608eac53d9bf9e1a6963543588b555c64\n", 41, "rebase/.git/rebase-merge/orig-head");
+
+ git_commit_free(head_commit);
+ git_reference_free(head);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_reference_free(branch_ref);
+ git_rebase_free(rebase);
+}
+
+static int rebase_is_blocked(void)
+{
+ git_rebase *rebase = NULL;
+ int error;
+
+ git_reference *branch_ref, *upstream_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+
+ cl_assert_equal_i(GIT_REPOSITORY_STATE_NONE, git_repository_state(repo));
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/beef"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ error = git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, NULL);
+
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+
+ git_reference_free(branch_ref);
+ git_reference_free(upstream_ref);
+ git_rebase_free(rebase);
+
+ return error;
+}
+
+void test_rebase_setup__blocked_for_staged_change(void)
+{
+ cl_git_rewritefile("rebase/newfile.txt", "Stage an add");
+ git_index_add_bypath(_index, "newfile.txt");
+ cl_git_fail(rebase_is_blocked());
+}
+
+void test_rebase_setup__blocked_for_unstaged_change(void)
+{
+ cl_git_rewritefile("rebase/asparagus.txt", "Unstaged change");
+ cl_git_fail(rebase_is_blocked());
+}
+
+void test_rebase_setup__not_blocked_for_untracked_add(void)
+{
+ cl_git_rewritefile("rebase/newfile.txt", "Untracked file");
+ cl_git_pass(rebase_is_blocked());
+}
+
diff --git a/tests/libgit2/rebase/sign.c b/tests/libgit2/rebase/sign.c
new file mode 100644
index 0000000..69bb1c6
--- /dev/null
+++ b/tests/libgit2/rebase/sign.c
@@ -0,0 +1,491 @@
+#include "clar_libgit2.h"
+#include "git2/rebase.h"
+
+static git_repository *repo;
+static git_signature *signature;
+
+/* Fixture setup and teardown */
+void test_rebase_sign__initialize(void)
+{
+ repo = cl_git_sandbox_init("rebase");
+ cl_git_pass(git_signature_new(&signature, "Rebaser",
+ "rebaser@rebaser.rb", 1405694510, 0));
+}
+
+void test_rebase_sign__cleanup(void)
+{
+ git_signature_free(signature);
+ cl_git_sandbox_cleanup();
+}
+
+static int create_cb_passthrough(
+ git_oid *out,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *message_encoding,
+ const char *message,
+ const git_tree *tree,
+ size_t parent_count,
+ const git_commit *parents[],
+ void *payload)
+{
+ GIT_UNUSED(out);
+ GIT_UNUSED(author);
+ GIT_UNUSED(committer);
+ GIT_UNUSED(message_encoding);
+ GIT_UNUSED(message);
+ GIT_UNUSED(tree);
+ GIT_UNUSED(parent_count);
+ GIT_UNUSED(parents);
+ GIT_UNUSED(payload);
+
+ return GIT_PASSTHROUGH;
+}
+
+/* git checkout gravy ; git rebase --merge veal */
+void test_rebase_sign__passthrough_create_cb(void)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref, *upstream_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_rebase_operation *rebase_operation;
+ git_oid commit_id, expected_id;
+ git_rebase_options rebase_opts = GIT_REBASE_OPTIONS_INIT;
+ git_commit *commit;
+ const char *expected_commit_raw_header = "tree cd99b26250099fc38d30bfaed7797a7275ed3366\n\
+parent f87d14a4a236582a0278a916340a793714256864\n\
+author Edward Thomson <ethomson@edwardthomson.com> 1405625055 -0400\n\
+committer Rebaser <rebaser@rebaser.rb> 1405694510 +0000\n";
+
+ rebase_opts.commit_create_cb = create_cb_passthrough;
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/gravy"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/veal"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, &rebase_opts));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL));
+
+ git_oid__fromstr(&expected_id, "129183968a65abd6c52da35bff43325001bfc630", GIT_OID_SHA1);
+ cl_assert_equal_oid(&expected_id, &commit_id);
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &commit_id));
+ cl_assert_equal_s(expected_commit_raw_header, git_commit_raw_header(commit));
+
+ cl_git_fail_with(GIT_ITEROVER, git_rebase_next(&rebase_operation, rebase));
+
+ git_reference_free(branch_ref);
+ git_reference_free(upstream_ref);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_commit_free(commit);
+ git_rebase_free(rebase);
+}
+
+static int create_cb_signed_gpg(
+ git_oid *out,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *message_encoding,
+ const char *message,
+ const git_tree *tree,
+ size_t parent_count,
+ const git_commit *parents[],
+ void *payload)
+{
+ git_buf commit_content = GIT_BUF_INIT;
+ const char *gpg_signature = "-----BEGIN PGP SIGNATURE-----\n\
+\n\
+iQIzBAEBCgAdFiEEgVlDEfSlmKn0fvGgK++h5T2/ctIFAlwZcrAACgkQK++h5T2/\n\
+ctIPVhAA42RyZhMdKl5Bm0KtQco2scsukIg2y7tjSwhti91zDu3HQgpusjjo0fQx\n\
+ZzB+OrmlvQ9CDcGpZ0THIzXD8GRJoDMPqdrvZVrBWkGcHvw7/YPA8skzsjkauJ8W\n\
+7lzF5LCuHSS6OUmPT/+5hEHPin5PB3zhfszyC+Q7aujnIuPJMrKiMnUa+w1HWifM\n\
+km49OOygQ9S6NQoVuEQede22+c76DlDL7yFghGoo1f0sKCE/9LW6SEnwI/bWv9eo\n\
+nom5vOPrvQeJiYCQk+2DyWo8RdSxINtY+G9bPE4RXm+6ZgcXECPm9TYDIWpL36fC\n\
+jvtGLs98woWFElOziBMp5Tb630GMcSI+q5ivHfJ3WS5NKLYLHBNK4iSFN0/dgAnB\n\
+dj6GcKXKWnIBWn6ZM4o40pcM5KSRUUCLtA0ZmjJH4c4zx3X5fUxd+enwkf3e9VZO\n\
+fNKC/+xfq6NfoPUPK9+UnchHpJaJw7RG5tZS+sWCz2xpQ1y3/o49xImNyM3wnpvB\n\
+cRAZabqIHpZa9/DIUkELOtCzln6niqkjRgg3M/YCCNznwV+0RNgz87VtyTPerdef\n\
+xrqn0+ROMF6ebVqIs6PPtuPkxnAJu7TMKXVB5rFnAewS24e6cIGFzeIA7810py3l\n\
+cttVRsdOoego+fiy08eFE+aJIeYiINRGhqOBTsuqG4jIdpdKxPE=\n\
+=KbsY\n\
+-----END PGP SIGNATURE-----";
+
+ git_repository *repo = (git_repository *)payload;
+ int error;
+
+ if ((error = git_commit_create_buffer(&commit_content,
+ repo, author, committer, message_encoding, message,
+ tree, parent_count, parents)) < 0)
+ goto done;
+
+ error = git_commit_create_with_signature(out, repo,
+ commit_content.ptr,
+ gpg_signature,
+ NULL);
+
+done:
+ git_buf_dispose(&commit_content);
+ return error;
+}
+
+/* git checkout gravy ; git rebase --merge veal */
+void test_rebase_sign__create_gpg_signed(void)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref, *upstream_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_rebase_operation *rebase_operation;
+ git_oid commit_id, expected_id;
+ git_rebase_options rebase_opts = GIT_REBASE_OPTIONS_INIT;
+ git_commit *commit;
+ const char *expected_commit_raw_header = "tree cd99b26250099fc38d30bfaed7797a7275ed3366\n\
+parent f87d14a4a236582a0278a916340a793714256864\n\
+author Edward Thomson <ethomson@edwardthomson.com> 1405625055 -0400\n\
+committer Rebaser <rebaser@rebaser.rb> 1405694510 +0000\n\
+gpgsig -----BEGIN PGP SIGNATURE-----\n\
+ \n\
+ iQIzBAEBCgAdFiEEgVlDEfSlmKn0fvGgK++h5T2/ctIFAlwZcrAACgkQK++h5T2/\n\
+ ctIPVhAA42RyZhMdKl5Bm0KtQco2scsukIg2y7tjSwhti91zDu3HQgpusjjo0fQx\n\
+ ZzB+OrmlvQ9CDcGpZ0THIzXD8GRJoDMPqdrvZVrBWkGcHvw7/YPA8skzsjkauJ8W\n\
+ 7lzF5LCuHSS6OUmPT/+5hEHPin5PB3zhfszyC+Q7aujnIuPJMrKiMnUa+w1HWifM\n\
+ km49OOygQ9S6NQoVuEQede22+c76DlDL7yFghGoo1f0sKCE/9LW6SEnwI/bWv9eo\n\
+ nom5vOPrvQeJiYCQk+2DyWo8RdSxINtY+G9bPE4RXm+6ZgcXECPm9TYDIWpL36fC\n\
+ jvtGLs98woWFElOziBMp5Tb630GMcSI+q5ivHfJ3WS5NKLYLHBNK4iSFN0/dgAnB\n\
+ dj6GcKXKWnIBWn6ZM4o40pcM5KSRUUCLtA0ZmjJH4c4zx3X5fUxd+enwkf3e9VZO\n\
+ fNKC/+xfq6NfoPUPK9+UnchHpJaJw7RG5tZS+sWCz2xpQ1y3/o49xImNyM3wnpvB\n\
+ cRAZabqIHpZa9/DIUkELOtCzln6niqkjRgg3M/YCCNznwV+0RNgz87VtyTPerdef\n\
+ xrqn0+ROMF6ebVqIs6PPtuPkxnAJu7TMKXVB5rFnAewS24e6cIGFzeIA7810py3l\n\
+ cttVRsdOoego+fiy08eFE+aJIeYiINRGhqOBTsuqG4jIdpdKxPE=\n\
+ =KbsY\n\
+ -----END PGP SIGNATURE-----\n";
+
+ rebase_opts.commit_create_cb = create_cb_signed_gpg;
+ rebase_opts.payload = repo;
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/gravy"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/veal"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, &rebase_opts));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL));
+
+ git_oid__fromstr(&expected_id, "bf78348e45c8286f52b760f1db15cb6da030f2ef", GIT_OID_SHA1);
+ cl_assert_equal_oid(&expected_id, &commit_id);
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &commit_id));
+ cl_assert_equal_s(expected_commit_raw_header, git_commit_raw_header(commit));
+
+ cl_git_fail_with(GIT_ITEROVER, git_rebase_next(&rebase_operation, rebase));
+
+ git_reference_free(branch_ref);
+ git_reference_free(upstream_ref);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_commit_free(commit);
+ git_rebase_free(rebase);
+}
+
+static int create_cb_error(
+ git_oid *out,
+ const git_signature *author,
+ const git_signature *committer,
+ const char *message_encoding,
+ const char *message,
+ const git_tree *tree,
+ size_t parent_count,
+ const git_commit *parents[],
+ void *payload)
+{
+ GIT_UNUSED(out);
+ GIT_UNUSED(author);
+ GIT_UNUSED(committer);
+ GIT_UNUSED(message_encoding);
+ GIT_UNUSED(message);
+ GIT_UNUSED(tree);
+ GIT_UNUSED(parent_count);
+ GIT_UNUSED(parents);
+ GIT_UNUSED(payload);
+
+ return GIT_EUSER;
+}
+
+/* git checkout gravy ; git rebase --merge veal */
+void test_rebase_sign__create_propagates_error(void)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref, *upstream_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_oid commit_id;
+ git_rebase_operation *rebase_operation;
+ git_rebase_options rebase_opts = GIT_REBASE_OPTIONS_INIT;
+
+ rebase_opts.commit_create_cb = create_cb_error;
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/gravy"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/veal"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, &rebase_opts));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_fail_with(GIT_EUSER, git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL));
+
+ git_reference_free(branch_ref);
+ git_reference_free(upstream_ref);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_rebase_free(rebase);
+}
+
+#ifndef GIT_DEPRECATE_HARD
+static const char *expected_commit_content = "\
+tree cd99b26250099fc38d30bfaed7797a7275ed3366\n\
+parent f87d14a4a236582a0278a916340a793714256864\n\
+author Edward Thomson <ethomson@edwardthomson.com> 1405625055 -0400\n\
+committer Rebaser <rebaser@rebaser.rb> 1405694510 +0000\n\
+\n\
+Modification 3 to gravy\n";
+
+int signing_cb_passthrough(
+ git_buf *signature,
+ git_buf *signature_field,
+ const char *commit_content,
+ void *payload)
+{
+ cl_assert_equal_i(0, signature->size);
+ cl_assert_equal_i(0, signature_field->size);
+ cl_assert_equal_s(expected_commit_content, commit_content);
+ cl_assert_equal_p(NULL, payload);
+ return GIT_PASSTHROUGH;
+}
+#endif /* !GIT_DEPRECATE_HARD */
+
+/* git checkout gravy ; git rebase --merge veal */
+void test_rebase_sign__passthrough_signing_cb(void)
+{
+#ifndef GIT_DEPRECATE_HARD
+ git_rebase *rebase;
+ git_reference *branch_ref, *upstream_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_rebase_operation *rebase_operation;
+ git_oid commit_id, expected_id;
+ git_rebase_options rebase_opts = GIT_REBASE_OPTIONS_INIT;
+ git_commit *commit;
+ const char *expected_commit_raw_header = "tree cd99b26250099fc38d30bfaed7797a7275ed3366\n\
+parent f87d14a4a236582a0278a916340a793714256864\n\
+author Edward Thomson <ethomson@edwardthomson.com> 1405625055 -0400\n\
+committer Rebaser <rebaser@rebaser.rb> 1405694510 +0000\n";
+
+ rebase_opts.signing_cb = signing_cb_passthrough;
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/gravy"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/veal"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, &rebase_opts));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL));
+
+ git_oid__fromstr(&expected_id, "129183968a65abd6c52da35bff43325001bfc630", GIT_OID_SHA1);
+ cl_assert_equal_oid(&expected_id, &commit_id);
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &commit_id));
+ cl_assert_equal_s(expected_commit_raw_header, git_commit_raw_header(commit));
+
+ cl_git_fail_with(GIT_ITEROVER, git_rebase_next(&rebase_operation, rebase));
+
+ git_reference_free(branch_ref);
+ git_reference_free(upstream_ref);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_commit_free(commit);
+ git_rebase_free(rebase);
+#endif /* !GIT_DEPRECATE_HARD */
+}
+
+#ifndef GIT_DEPRECATE_HARD
+int signing_cb_gpg(
+ git_buf *signature,
+ git_buf *signature_field,
+ const char *commit_content,
+ void *payload)
+{
+ const char *gpg_signature = "\
+-----BEGIN PGP SIGNATURE-----\n\
+\n\
+iQIzBAEBCgAdFiEEgVlDEfSlmKn0fvGgK++h5T2/ctIFAlwZcrAACgkQK++h5T2/\n\
+ctIPVhAA42RyZhMdKl5Bm0KtQco2scsukIg2y7tjSwhti91zDu3HQgpusjjo0fQx\n\
+ZzB+OrmlvQ9CDcGpZ0THIzXD8GRJoDMPqdrvZVrBWkGcHvw7/YPA8skzsjkauJ8W\n\
+7lzF5LCuHSS6OUmPT/+5hEHPin5PB3zhfszyC+Q7aujnIuPJMrKiMnUa+w1HWifM\n\
+km49OOygQ9S6NQoVuEQede22+c76DlDL7yFghGoo1f0sKCE/9LW6SEnwI/bWv9eo\n\
+nom5vOPrvQeJiYCQk+2DyWo8RdSxINtY+G9bPE4RXm+6ZgcXECPm9TYDIWpL36fC\n\
+jvtGLs98woWFElOziBMp5Tb630GMcSI+q5ivHfJ3WS5NKLYLHBNK4iSFN0/dgAnB\n\
+dj6GcKXKWnIBWn6ZM4o40pcM5KSRUUCLtA0ZmjJH4c4zx3X5fUxd+enwkf3e9VZO\n\
+fNKC/+xfq6NfoPUPK9+UnchHpJaJw7RG5tZS+sWCz2xpQ1y3/o49xImNyM3wnpvB\n\
+cRAZabqIHpZa9/DIUkELOtCzln6niqkjRgg3M/YCCNznwV+0RNgz87VtyTPerdef\n\
+xrqn0+ROMF6ebVqIs6PPtuPkxnAJu7TMKXVB5rFnAewS24e6cIGFzeIA7810py3l\n\
+cttVRsdOoego+fiy08eFE+aJIeYiINRGhqOBTsuqG4jIdpdKxPE=\n\
+=KbsY\n\
+-----END PGP SIGNATURE-----";
+
+ cl_assert_equal_i(0, signature->size);
+ cl_assert_equal_i(0, signature_field->size);
+ cl_assert_equal_s(expected_commit_content, commit_content);
+ cl_assert_equal_p(NULL, payload);
+
+ cl_git_pass(git_buf_set(signature, gpg_signature, strlen(gpg_signature) + 1));
+ return GIT_OK;
+}
+#endif /* !GIT_DEPRECATE_HARD */
+
+/* git checkout gravy ; git rebase --merge veal */
+void test_rebase_sign__gpg_with_no_field(void)
+{
+#ifndef GIT_DEPRECATE_HARD
+ git_rebase *rebase;
+ git_reference *branch_ref, *upstream_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_rebase_operation *rebase_operation;
+ git_oid commit_id, expected_id;
+ git_rebase_options rebase_opts = GIT_REBASE_OPTIONS_INIT;
+ git_commit *commit;
+ const char *expected_commit_raw_header = "\
+tree cd99b26250099fc38d30bfaed7797a7275ed3366\n\
+parent f87d14a4a236582a0278a916340a793714256864\n\
+author Edward Thomson <ethomson@edwardthomson.com> 1405625055 -0400\n\
+committer Rebaser <rebaser@rebaser.rb> 1405694510 +0000\n\
+gpgsig -----BEGIN PGP SIGNATURE-----\n\
+ \n\
+ iQIzBAEBCgAdFiEEgVlDEfSlmKn0fvGgK++h5T2/ctIFAlwZcrAACgkQK++h5T2/\n\
+ ctIPVhAA42RyZhMdKl5Bm0KtQco2scsukIg2y7tjSwhti91zDu3HQgpusjjo0fQx\n\
+ ZzB+OrmlvQ9CDcGpZ0THIzXD8GRJoDMPqdrvZVrBWkGcHvw7/YPA8skzsjkauJ8W\n\
+ 7lzF5LCuHSS6OUmPT/+5hEHPin5PB3zhfszyC+Q7aujnIuPJMrKiMnUa+w1HWifM\n\
+ km49OOygQ9S6NQoVuEQede22+c76DlDL7yFghGoo1f0sKCE/9LW6SEnwI/bWv9eo\n\
+ nom5vOPrvQeJiYCQk+2DyWo8RdSxINtY+G9bPE4RXm+6ZgcXECPm9TYDIWpL36fC\n\
+ jvtGLs98woWFElOziBMp5Tb630GMcSI+q5ivHfJ3WS5NKLYLHBNK4iSFN0/dgAnB\n\
+ dj6GcKXKWnIBWn6ZM4o40pcM5KSRUUCLtA0ZmjJH4c4zx3X5fUxd+enwkf3e9VZO\n\
+ fNKC/+xfq6NfoPUPK9+UnchHpJaJw7RG5tZS+sWCz2xpQ1y3/o49xImNyM3wnpvB\n\
+ cRAZabqIHpZa9/DIUkELOtCzln6niqkjRgg3M/YCCNznwV+0RNgz87VtyTPerdef\n\
+ xrqn0+ROMF6ebVqIs6PPtuPkxnAJu7TMKXVB5rFnAewS24e6cIGFzeIA7810py3l\n\
+ cttVRsdOoego+fiy08eFE+aJIeYiINRGhqOBTsuqG4jIdpdKxPE=\n\
+ =KbsY\n\
+ -----END PGP SIGNATURE-----\n";
+
+ rebase_opts.signing_cb = signing_cb_gpg;
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/gravy"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/veal"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, &rebase_opts));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL));
+
+ git_oid__fromstr(&expected_id, "bf78348e45c8286f52b760f1db15cb6da030f2ef", GIT_OID_SHA1);
+ cl_assert_equal_oid(&expected_id, &commit_id);
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &commit_id));
+ cl_assert_equal_s(expected_commit_raw_header, git_commit_raw_header(commit));
+
+ cl_git_fail_with(GIT_ITEROVER, git_rebase_next(&rebase_operation, rebase));
+
+ git_reference_free(branch_ref);
+ git_reference_free(upstream_ref);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_commit_free(commit);
+ git_rebase_free(rebase);
+#endif /* !GIT_DEPRECATE_HARD */
+}
+
+
+#ifndef GIT_DEPRECATE_HARD
+int signing_cb_magic_field(
+ git_buf *signature,
+ git_buf *signature_field,
+ const char *commit_content,
+ void *payload)
+{
+ const char *signature_content = "magic word: pretty please";
+ const char *signature_field_content = "magicsig";
+
+ cl_assert_equal_p(NULL, signature->ptr);
+ cl_assert_equal_i(0, signature->size);
+ cl_assert_equal_p(NULL, signature_field->ptr);
+ cl_assert_equal_i(0, signature_field->size);
+ cl_assert_equal_s(expected_commit_content, commit_content);
+ cl_assert_equal_p(NULL, payload);
+
+ cl_git_pass(git_buf_set(signature, signature_content,
+ strlen(signature_content) + 1));
+ cl_git_pass(git_buf_set(signature_field, signature_field_content,
+ strlen(signature_field_content) + 1));
+
+ return GIT_OK;
+}
+#endif /* !GIT_DEPRECATE_HARD */
+
+/* git checkout gravy ; git rebase --merge veal */
+void test_rebase_sign__custom_signature_field(void)
+{
+#ifndef GIT_DEPRECATE_HARD
+ git_rebase *rebase;
+ git_reference *branch_ref, *upstream_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_rebase_operation *rebase_operation;
+ git_oid commit_id, expected_id;
+ git_rebase_options rebase_opts = GIT_REBASE_OPTIONS_INIT;
+ git_commit *commit;
+ const char *expected_commit_raw_header = "\
+tree cd99b26250099fc38d30bfaed7797a7275ed3366\n\
+parent f87d14a4a236582a0278a916340a793714256864\n\
+author Edward Thomson <ethomson@edwardthomson.com> 1405625055 -0400\n\
+committer Rebaser <rebaser@rebaser.rb> 1405694510 +0000\n\
+magicsig magic word: pretty please\n";
+
+ rebase_opts.signing_cb = signing_cb_magic_field;
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/gravy"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/veal"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, &rebase_opts));
+
+ cl_git_pass(git_rebase_next(&rebase_operation, rebase));
+ cl_git_pass(git_rebase_commit(&commit_id, rebase, NULL, signature, NULL, NULL));
+
+ git_oid__fromstr(&expected_id, "f46a4a8d26ae411b02aa61b7d69576627f4a1e1c", GIT_OID_SHA1);
+ cl_assert_equal_oid(&expected_id, &commit_id);
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &commit_id));
+ cl_assert_equal_s(expected_commit_raw_header, git_commit_raw_header(commit));
+
+ cl_git_fail_with(GIT_ITEROVER, git_rebase_next(&rebase_operation, rebase));
+
+ git_reference_free(branch_ref);
+ git_reference_free(upstream_ref);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_commit_free(commit);
+ git_rebase_free(rebase);
+#endif /* !GIT_DEPRECATE_HARD */
+}
diff --git a/tests/libgit2/rebase/submodule.c b/tests/libgit2/rebase/submodule.c
new file mode 100644
index 0000000..0b3c2d5
--- /dev/null
+++ b/tests/libgit2/rebase/submodule.c
@@ -0,0 +1,95 @@
+#include "clar_libgit2.h"
+#include "git2/checkout.h"
+#include "git2/rebase.h"
+#include "posix.h"
+#include "signature.h"
+#include "../submodule/submodule_helpers.h"
+
+#include <fcntl.h>
+
+static git_repository *repo;
+static git_signature *signature;
+
+/* Fixture setup and teardown */
+void test_rebase_submodule__initialize(void)
+{
+ git_index *index;
+ git_oid tree_oid, commit_id;
+ git_tree *tree;
+ git_commit *parent;
+ git_object *obj;
+ git_reference *master_ref;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ repo = cl_git_sandbox_init("rebase-submodule");
+ cl_git_pass(git_signature_new(&signature,
+ "Rebaser", "rebaser@rebaser.rb", 1405694510, 0));
+
+ rewrite_gitmodules(git_repository_workdir(repo));
+
+ cl_git_pass(git_submodule_set_url(repo, "my-submodule", git_repository_path(repo)));
+
+ /* We have to commit the rewritten .gitmodules file */
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_add_bypath(index, ".gitmodules"));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_index_write_tree(&tree_oid, index));
+ cl_git_pass(git_tree_lookup(&tree, repo, &tree_oid));
+
+ cl_git_pass(git_repository_head(&master_ref, repo));
+ cl_git_pass(git_commit_lookup(&parent, repo, git_reference_target(master_ref)));
+
+ cl_git_pass(git_commit_create_v(&commit_id, repo, git_reference_name(master_ref), signature, signature, NULL, "Fixup .gitmodules", tree, 1, parent));
+
+ /* And a final reset, for good measure */
+ cl_git_pass(git_object_lookup(&obj, repo, &commit_id, GIT_OBJECT_COMMIT));
+ cl_git_pass(git_reset(repo, obj, GIT_RESET_HARD, &opts));
+
+ git_index_free(index);
+ git_object_free(obj);
+ git_commit_free(parent);
+ git_reference_free(master_ref);
+ git_tree_free(tree);
+}
+
+void test_rebase_submodule__cleanup(void)
+{
+ git_signature_free(signature);
+ cl_git_sandbox_cleanup();
+}
+
+void test_rebase_submodule__init_untracked(void)
+{
+ git_rebase *rebase;
+ git_reference *branch_ref, *upstream_ref;
+ git_annotated_commit *branch_head, *upstream_head;
+ git_str untracked_path = GIT_STR_INIT;
+ FILE *fp;
+ git_submodule *submodule;
+
+ cl_git_pass(git_reference_lookup(&branch_ref, repo, "refs/heads/asparagus"));
+ cl_git_pass(git_reference_lookup(&upstream_ref, repo, "refs/heads/master"));
+
+ cl_git_pass(git_annotated_commit_from_ref(&branch_head, repo, branch_ref));
+ cl_git_pass(git_annotated_commit_from_ref(&upstream_head, repo, upstream_ref));
+
+ cl_git_pass(git_submodule_lookup(&submodule, repo, "my-submodule"));
+ cl_git_pass(git_submodule_update(submodule, 1, NULL));
+
+ git_str_printf(&untracked_path, "%s/my-submodule/untracked", git_repository_workdir(repo));
+ fp = fopen(git_str_cstr(&untracked_path), "w");
+ fprintf(fp, "An untracked file in a submodule should not block a rebase\n");
+ fclose(fp);
+ git_str_dispose(&untracked_path);
+
+ cl_git_pass(git_rebase_init(&rebase, repo, branch_head, upstream_head, NULL, NULL));
+
+ git_submodule_free(submodule);
+ git_annotated_commit_free(branch_head);
+ git_annotated_commit_free(upstream_head);
+ git_reference_free(branch_ref);
+ git_reference_free(upstream_ref);
+ git_rebase_free(rebase);
+}
diff --git a/tests/libgit2/refs/basic.c b/tests/libgit2/refs/basic.c
new file mode 100644
index 0000000..5e41ca0
--- /dev/null
+++ b/tests/libgit2/refs/basic.c
@@ -0,0 +1,85 @@
+#include "clar_libgit2.h"
+
+#include "futils.h"
+#include "refs.h"
+#include "ref_helpers.h"
+
+static git_repository *g_repo;
+
+static const char *loose_tag_ref_name = "refs/tags/e90810b";
+
+void test_refs_basic__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+ cl_git_pass(git_repository_set_ident(g_repo, "me", "foo@example.com"));
+}
+
+void test_refs_basic__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_basic__reference_realloc(void)
+{
+ git_reference *ref;
+ git_reference *new_ref;
+ const char *new_name = "refs/tags/awful/name-which-is/clearly/really-that-much/longer-than/the-old-one";
+
+ /* Retrieval of the reference to rename */
+ cl_git_pass(git_reference_lookup(&ref, g_repo, loose_tag_ref_name));
+
+ new_ref = git_reference__realloc(&ref, new_name);
+ cl_assert(new_ref != NULL);
+ git_reference_free(new_ref);
+ git_reference_free(ref);
+
+ /* Reload, so we restore the value */
+ cl_git_pass(git_reference_lookup(&ref, g_repo, loose_tag_ref_name));
+
+ cl_git_pass(git_reference_rename(&new_ref, ref, new_name, 1, "log message"));
+ cl_assert(ref != NULL);
+ cl_assert(new_ref != NULL);
+ git_reference_free(new_ref);
+ git_reference_free(ref);
+}
+
+void test_refs_basic__longpaths(void)
+{
+#ifdef GIT_WIN32
+ const char *base;
+ size_t base_len, extra_len;
+ ssize_t remain_len, i;
+ git_str refname = GIT_STR_INIT;
+ git_reference *one = NULL, *two = NULL;
+ git_oid id;
+
+ cl_git_pass(git_oid__fromstr(&id, "099fabac3a9ea935598528c27f866e34089c2eff", GIT_OID_SHA1));
+
+ base = git_repository_path(g_repo);
+ base_len = git_utf8_char_length(base, strlen(base));
+ extra_len = CONST_STRLEN("logs/refs/heads/") + CONST_STRLEN(".lock");
+
+ remain_len = (ssize_t)MAX_PATH - (base_len + extra_len);
+ cl_assert(remain_len > 0);
+
+ cl_git_pass(git_str_puts(&refname, "refs/heads/"));
+
+ for (i = 0; i < remain_len; i++) {
+ cl_git_pass(git_str_putc(&refname, 'a'));
+ }
+
+ /*
+ * The full path to the reflog lockfile is 260 characters,
+ * this is permitted.
+ */
+ cl_git_pass(git_reference_create(&one, g_repo, refname.ptr, &id, 0, NULL));
+
+ /* Adding one more character gives us a path that is too long. */
+ cl_git_pass(git_str_putc(&refname, 'z'));
+ cl_git_fail(git_reference_create(&two, g_repo, refname.ptr, &id, 0, NULL));
+
+ git_reference_free(one);
+ git_reference_free(two);
+ git_str_dispose(&refname);
+#endif
+}
diff --git a/tests/libgit2/refs/branches/checkedout.c b/tests/libgit2/refs/branches/checkedout.c
new file mode 100644
index 0000000..d6dab2c
--- /dev/null
+++ b/tests/libgit2/refs/branches/checkedout.c
@@ -0,0 +1,53 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+#include "worktree/worktree_helpers.h"
+
+static git_repository *repo;
+
+static void assert_checked_out(git_repository *repo, const char *branch, int checked_out)
+{
+ git_reference *ref;
+
+ cl_git_pass(git_reference_lookup(&ref, repo, branch));
+ cl_assert(git_branch_is_checked_out(ref) == checked_out);
+
+ git_reference_free(ref);
+}
+
+void test_refs_branches_checkedout__simple_repo(void)
+{
+ repo = cl_git_sandbox_init("testrepo");
+ assert_checked_out(repo, "refs/heads/master", 1);
+ assert_checked_out(repo, "refs/heads/executable", 0);
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_branches_checkedout__worktree(void)
+{
+ static worktree_fixture fixture =
+ WORKTREE_FIXTURE_INIT("testrepo", "testrepo-worktree");
+
+ setup_fixture_worktree(&fixture);
+
+ assert_checked_out(fixture.repo, "refs/heads/master", 1);
+ assert_checked_out(fixture.repo, "refs/heads/testrepo-worktree", 1);
+
+ assert_checked_out(fixture.worktree, "refs/heads/master", 1);
+ assert_checked_out(fixture.worktree, "refs/heads/testrepo-worktree", 1);
+
+ cleanup_fixture_worktree(&fixture);
+}
+
+void test_refs_branches_checkedout__head_is_not_checked_out(void)
+{
+ repo = cl_git_sandbox_init("testrepo");
+ assert_checked_out(repo, "HEAD", 0);
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_branches_checkedout__master_in_bare_repo_is_not_checked_out(void)
+{
+ repo = cl_git_sandbox_init("testrepo.git");
+ assert_checked_out(repo, "refs/heads/master", 0);
+ cl_git_sandbox_cleanup();
+}
diff --git a/tests/libgit2/refs/branches/create.c b/tests/libgit2/refs/branches/create.c
new file mode 100644
index 0000000..356bad4
--- /dev/null
+++ b/tests/libgit2/refs/branches/create.c
@@ -0,0 +1,287 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+#include "path.h"
+
+static git_repository *repo;
+static git_commit *target;
+static git_reference *branch;
+
+void test_refs_branches_create__initialize(void)
+{
+ repo = cl_git_sandbox_init("testrepo.git");
+ branch = NULL;
+ target = NULL;
+}
+
+void test_refs_branches_create__cleanup(void)
+{
+ git_reference_free(branch);
+ branch = NULL;
+
+ git_commit_free(target);
+ target = NULL;
+
+ cl_git_sandbox_cleanup();
+ repo = NULL;
+}
+
+static void retrieve_target_from_oid(git_commit **out, git_repository *repo, const char *sha)
+{
+ git_object *obj;
+
+ cl_git_pass(git_revparse_single(&obj, repo, sha));
+ cl_git_pass(git_commit_lookup(out, repo, git_object_id(obj)));
+ git_object_free(obj);
+}
+
+static void retrieve_known_commit(git_commit **commit, git_repository *repo)
+{
+ retrieve_target_from_oid(commit, repo, "e90810b8df3");
+}
+
+#define NEW_BRANCH_NAME "new-branch-on-the-block"
+
+void test_refs_branches_create__can_create_a_local_branch(void)
+{
+ retrieve_known_commit(&target, repo);
+
+ cl_git_pass(git_branch_create(&branch, repo, NEW_BRANCH_NAME, target, 0));
+ cl_git_pass(git_oid_cmp(git_reference_target(branch), git_commit_id(target)));
+}
+
+void test_refs_branches_create__can_not_create_a_branch_if_its_name_collide_with_an_existing_one(void)
+{
+ retrieve_known_commit(&target, repo);
+
+ cl_assert_equal_i(GIT_EEXISTS, git_branch_create(&branch, repo, "br2", target, 0));
+}
+
+void test_refs_branches_create__can_force_create_over_an_existing_branch(void)
+{
+ retrieve_known_commit(&target, repo);
+
+ cl_git_pass(git_branch_create(&branch, repo, "br2", target, 1));
+ cl_git_pass(git_oid_cmp(git_reference_target(branch), git_commit_id(target)));
+ cl_assert_equal_s("refs/heads/br2", git_reference_name(branch));
+}
+
+void test_refs_branches_create__cannot_force_create_over_current_branch_in_nonbare_repo(void)
+{
+ const git_oid *oid;
+ git_reference *branch2;
+
+ /* Default repo for these tests is a bare repo, but this test requires a non-bare one */
+ cl_git_sandbox_cleanup();
+ repo = cl_git_sandbox_init("testrepo");
+ retrieve_known_commit(&target, repo);
+
+ cl_git_pass(git_branch_lookup(&branch2, repo, "master", GIT_BRANCH_LOCAL));
+ cl_assert_equal_s("refs/heads/master", git_reference_name(branch2));
+ cl_assert_equal_i(true, git_branch_is_head(branch2));
+ oid = git_reference_target(branch2);
+
+ cl_git_fail_with(-1, git_branch_create(&branch, repo, "master", target, 1));
+ branch = NULL;
+ cl_git_pass(git_branch_lookup(&branch, repo, "master", GIT_BRANCH_LOCAL));
+ cl_assert_equal_s("refs/heads/master", git_reference_name(branch));
+ cl_git_pass(git_oid_cmp(git_reference_target(branch), oid));
+ git_reference_free(branch2);
+}
+
+void test_refs_branches_create__can_force_create_over_current_branch_in_bare_repo(void)
+{
+ const git_oid *oid;
+ git_reference *branch2;
+ retrieve_known_commit(&target, repo);
+
+ cl_git_pass(git_branch_lookup(&branch2, repo, "master", GIT_BRANCH_LOCAL));
+ cl_assert_equal_s("refs/heads/master", git_reference_name(branch2));
+ cl_assert_equal_i(true, git_branch_is_head(branch2));
+ oid = git_commit_id(target);
+
+ cl_git_pass(git_branch_create(&branch, repo, "master", target, 1));
+ git_reference_free(branch);
+ branch = NULL;
+ cl_git_pass(git_branch_lookup(&branch, repo, "master", GIT_BRANCH_LOCAL));
+ cl_assert_equal_s("refs/heads/master", git_reference_name(branch));
+ cl_git_pass(git_oid_cmp(git_reference_target(branch), oid));
+ git_reference_free(branch2);
+}
+
+void test_refs_branches_create__creating_a_branch_with_an_invalid_name_returns_EINVALIDSPEC(void)
+{
+ retrieve_known_commit(&target, repo);
+
+ cl_assert_equal_i(GIT_EINVALIDSPEC,
+ git_branch_create(&branch, repo, "inv@{id", target, 0));
+}
+
+static void assert_branch_matches_name(
+ const char *expected, const char *lookup_as)
+{
+ git_reference *ref;
+ git_str b = GIT_STR_INIT;
+
+ cl_git_pass(git_branch_lookup(&ref, repo, lookup_as, GIT_BRANCH_LOCAL));
+
+ cl_git_pass(git_str_sets(&b, "refs/heads/"));
+ cl_git_pass(git_str_puts(&b, expected));
+ cl_assert_equal_s(b.ptr, git_reference_name(ref));
+
+ cl_git_pass(
+ git_oid_cmp(git_reference_target(ref), git_commit_id(target)));
+
+ git_reference_free(ref);
+ git_str_dispose(&b);
+}
+
+void test_refs_branches_create__can_create_branch_with_unicode(void)
+{
+ const char *nfc = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D";
+ const char *nfd = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D";
+ const char *emoji = "\xF0\x9F\x8D\xB7";
+ const char *names[] = { nfc, nfd, emoji };
+ const char *alt[] = { nfd, nfc, NULL };
+ const char *expected[] = { nfc, nfd, emoji };
+ unsigned int i;
+ bool fs_decompose_unicode =
+ git_fs_path_does_decompose_unicode(git_repository_path(repo));
+
+ retrieve_known_commit(&target, repo);
+
+ if (cl_repo_get_bool(repo, "core.precomposeunicode"))
+ expected[1] = nfc;
+ /* test decomp. because not all Mac filesystems decompose unicode */
+ else if (fs_decompose_unicode)
+ expected[0] = nfd;
+
+ for (i = 0; i < ARRAY_SIZE(names); ++i) {
+ const char *name;
+ cl_git_pass(git_branch_create(
+ &branch, repo, names[i], target, 0));
+ cl_git_pass(git_oid_cmp(
+ git_reference_target(branch), git_commit_id(target)));
+
+ cl_git_pass(git_branch_name(&name, branch));
+ cl_assert_equal_s(expected[i], name);
+ assert_branch_matches_name(expected[i], names[i]);
+ if (fs_decompose_unicode && alt[i])
+ assert_branch_matches_name(expected[i], alt[i]);
+
+ cl_git_pass(git_branch_delete(branch));
+ git_reference_free(branch);
+ branch = NULL;
+ }
+}
+
+/**
+ * Verify that we can create a branch with a name that matches the
+ * namespace of a previously delete branch.
+ *
+ * git branch level_one/level_two
+ * git branch -D level_one/level_two
+ * git branch level_one
+ *
+ * We expect the delete to have deleted the files:
+ * ".git/refs/heads/level_one/level_two"
+ * ".git/logs/refs/heads/level_one/level_two"
+ * It may or may not have deleted the (now empty)
+ * containing directories. To match git.git behavior,
+ * the second create needs to implicilty delete the
+ * directories and create the new files.
+ * "refs/heads/level_one"
+ * "logs/refs/heads/level_one"
+ *
+ * We should not fail to create the branch or its
+ * reflog because of an obsolete namespace container
+ * directory.
+ */
+void test_refs_branches_create__name_vs_namespace(void)
+{
+ const char * name;
+ struct item {
+ const char *first;
+ const char *second;
+ };
+ static const struct item item[] = {
+ { "level_one/level_two", "level_one" },
+ { "a/b/c/d/e", "a/b/c/d" },
+ { "ss/tt/uu/vv/ww", "ss" },
+ /* And one test case that is deeper. */
+ { "xx1/xx2/xx3/xx4", "xx1/xx2/xx3/xx4/xx5/xx6" },
+ { NULL, NULL },
+ };
+ const struct item *p;
+
+ retrieve_known_commit(&target, repo);
+
+ for (p=item; p->first; p++) {
+ cl_git_pass(git_branch_create(&branch, repo, p->first, target, 0));
+ cl_git_pass(git_oid_cmp(git_reference_target(branch), git_commit_id(target)));
+ cl_git_pass(git_branch_name(&name, branch));
+ cl_assert_equal_s(name, p->first);
+
+ cl_git_pass(git_branch_delete(branch));
+ git_reference_free(branch);
+ branch = NULL;
+
+ cl_git_pass(git_branch_create(&branch, repo, p->second, target, 0));
+ git_reference_free(branch);
+ branch = NULL;
+ }
+}
+
+/**
+ * We still need to fail if part of the namespace is
+ * still in use.
+ */
+void test_refs_branches_create__name_vs_namespace_fail(void)
+{
+ const char * name;
+ struct item {
+ const char *first;
+ const char *first_alternate;
+ const char *second;
+ };
+ static const struct item item[] = {
+ { "level_one/level_two", "level_one/alternate", "level_one" },
+ { "a/b/c/d/e", "a/b/c/d/alternate", "a/b/c/d" },
+ { "ss/tt/uu/vv/ww", "ss/alternate", "ss" },
+ { NULL, NULL, NULL },
+ };
+ const struct item *p;
+
+ retrieve_known_commit(&target, repo);
+
+ for (p=item; p->first; p++) {
+ cl_git_pass(git_branch_create(&branch, repo, p->first, target, 0));
+ cl_git_pass(git_oid_cmp(git_reference_target(branch), git_commit_id(target)));
+ cl_git_pass(git_branch_name(&name, branch));
+ cl_assert_equal_s(name, p->first);
+
+ cl_git_pass(git_branch_delete(branch));
+ git_reference_free(branch);
+ branch = NULL;
+
+ cl_git_pass(git_branch_create(&branch, repo, p->first_alternate, target, 0));
+ cl_git_pass(git_oid_cmp(git_reference_target(branch), git_commit_id(target)));
+ cl_git_pass(git_branch_name(&name, branch));
+ cl_assert_equal_s(name, p->first_alternate);
+
+ /* we do not delete the alternate. */
+ git_reference_free(branch);
+ branch = NULL;
+
+ cl_git_fail(git_branch_create(&branch, repo, p->second, target, 0));
+ git_reference_free(branch);
+ branch = NULL;
+ }
+}
+
+void test_refs_branches_create__error_when_create_branch_with_invalid_name(void)
+{
+ retrieve_known_commit(&target, repo);
+
+ cl_git_fail(git_branch_create(&branch, repo, "HEAD", target, 0));
+ cl_git_fail(git_branch_create(&branch, repo, "-dash", target, 0));
+}
diff --git a/tests/libgit2/refs/branches/delete.c b/tests/libgit2/refs/branches/delete.c
new file mode 100644
index 0000000..6b3d507
--- /dev/null
+++ b/tests/libgit2/refs/branches/delete.c
@@ -0,0 +1,188 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+#include "repo/repo_helpers.h"
+#include "config/config_helpers.h"
+#include "futils.h"
+#include "reflog.h"
+
+static git_repository *repo;
+static git_reference *fake_remote;
+
+void test_refs_branches_delete__initialize(void)
+{
+ git_oid id;
+
+ repo = cl_git_sandbox_init("testrepo.git");
+
+ cl_git_pass(git_oid__fromstr(&id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644", GIT_OID_SHA1));
+ cl_git_pass(git_reference_create(&fake_remote, repo, "refs/remotes/nulltoken/master", &id, 0, NULL));
+}
+
+void test_refs_branches_delete__cleanup(void)
+{
+ git_reference_free(fake_remote);
+ fake_remote = NULL;
+
+ cl_git_sandbox_cleanup();
+ repo = NULL;
+}
+
+void test_refs_branches_delete__can_not_delete_a_branch_pointed_at_by_HEAD(void)
+{
+ git_reference *head;
+ git_reference *branch;
+
+ /* Ensure HEAD targets the local master branch */
+ cl_git_pass(git_reference_lookup(&head, repo, GIT_HEAD_FILE));
+ cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head));
+ git_reference_free(head);
+
+ cl_git_pass(git_branch_lookup(&branch, repo, "master", GIT_BRANCH_LOCAL));
+ cl_git_fail(git_branch_delete(branch));
+ git_reference_free(branch);
+}
+
+void test_refs_branches_delete__can_delete_a_branch_even_if_HEAD_is_missing(void)
+{
+ git_reference *head;
+ git_reference *branch;
+
+ cl_git_pass(git_reference_lookup(&head, repo, GIT_HEAD_FILE));
+ git_reference_delete(head);
+ git_reference_free(head);
+
+ cl_git_pass(git_branch_lookup(&branch, repo, "br2", GIT_BRANCH_LOCAL));
+ cl_git_pass(git_branch_delete(branch));
+ git_reference_free(branch);
+}
+
+void test_refs_branches_delete__can_delete_a_branch_when_HEAD_is_unborn(void)
+{
+ git_reference *branch;
+
+ make_head_unborn(repo, NON_EXISTING_HEAD);
+
+ cl_git_pass(git_branch_lookup(&branch, repo, "br2", GIT_BRANCH_LOCAL));
+ cl_git_pass(git_branch_delete(branch));
+ git_reference_free(branch);
+}
+
+void test_refs_branches_delete__can_delete_a_branch_pointed_at_by_detached_HEAD(void)
+{
+ git_reference *head, *branch;
+
+ cl_git_pass(git_reference_lookup(&head, repo, GIT_HEAD_FILE));
+ cl_assert_equal_i(GIT_REFERENCE_SYMBOLIC, git_reference_type(head));
+ cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head));
+ git_reference_free(head);
+
+ /* Detach HEAD and make it target the commit that "master" points to */
+ git_repository_detach_head(repo);
+
+ cl_git_pass(git_branch_lookup(&branch, repo, "master", GIT_BRANCH_LOCAL));
+ cl_git_pass(git_branch_delete(branch));
+ git_reference_free(branch);
+}
+
+void test_refs_branches_delete__can_delete_a_local_branch(void)
+{
+ git_reference *branch;
+ cl_git_pass(git_branch_lookup(&branch, repo, "br2", GIT_BRANCH_LOCAL));
+ cl_git_pass(git_branch_delete(branch));
+ git_reference_free(branch);
+}
+
+void test_refs_branches_delete__can_delete_a_remote_branch(void)
+{
+ git_reference *branch;
+ cl_git_pass(git_branch_lookup(&branch, repo, "nulltoken/master", GIT_BRANCH_REMOTE));
+ cl_git_pass(git_branch_delete(branch));
+ git_reference_free(branch);
+}
+
+void test_refs_branches_delete__deleting_a_branch_removes_related_configuration_data(void)
+{
+ git_reference *branch;
+
+ assert_config_entry_existence(repo, "branch.track-local.remote", true);
+ assert_config_entry_existence(repo, "branch.track-local.merge", true);
+
+ cl_git_pass(git_branch_lookup(&branch, repo, "track-local", GIT_BRANCH_LOCAL));
+ cl_git_pass(git_branch_delete(branch));
+ git_reference_free(branch);
+
+ assert_config_entry_existence(repo, "branch.track-local.remote", false);
+ assert_config_entry_existence(repo, "branch.track-local.merge", false);
+}
+
+void test_refs_branches_delete__removes_reflog(void)
+{
+ git_reference *branch;
+ git_reflog *log;
+ git_oid oidzero = GIT_OID_SHA1_ZERO;
+ git_signature *sig;
+
+ /* Ensure the reflog has at least one entry */
+ cl_git_pass(git_signature_now(&sig, "Me", "user@example.com"));
+ cl_git_pass(git_reflog_read(&log, repo, "refs/heads/track-local"));
+ cl_git_pass(git_reflog_append(log, &oidzero, sig, "message"));
+ cl_assert(git_reflog_entrycount(log) > 0);
+ git_signature_free(sig);
+ git_reflog_free(log);
+
+ cl_git_pass(git_branch_lookup(&branch, repo, "track-local", GIT_BRANCH_LOCAL));
+ cl_git_pass(git_branch_delete(branch));
+ git_reference_free(branch);
+
+ cl_assert_equal_i(false, git_reference_has_log(repo, "refs/heads/track-local"));
+
+ /* Reading a non-existent reflog creates it, but it should be empty */
+ cl_git_pass(git_reflog_read(&log, repo, "refs/heads/track-local"));
+ cl_assert_equal_i(0, git_reflog_entrycount(log));
+ git_reflog_free(log);
+}
+
+void test_refs_branches_delete__removes_empty_folders(void)
+{
+ const char *commondir = git_repository_commondir(repo);
+ git_oid commit_id;
+ git_commit *commit;
+ git_reference *branch;
+
+ git_reflog *log;
+ git_oid oidzero = GIT_OID_SHA1_ZERO;
+ git_signature *sig;
+
+ git_str ref_folder = GIT_STR_INIT;
+ git_str reflog_folder = GIT_STR_INIT;
+
+ /* Create a new branch with a nested name */
+ cl_git_pass(git_oid__fromstr(&commit_id, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", GIT_OID_SHA1));
+ cl_git_pass(git_commit_lookup(&commit, repo, &commit_id));
+ cl_git_pass(git_branch_create(&branch, repo, "some/deep/ref", commit, 0));
+ git_commit_free(commit);
+
+ /* Ensure the reflog has at least one entry */
+ cl_git_pass(git_signature_now(&sig, "Me", "user@example.com"));
+ cl_git_pass(git_reflog_read(&log, repo, "refs/heads/some/deep/ref"));
+ cl_git_pass(git_reflog_append(log, &oidzero, sig, "message"));
+ cl_assert(git_reflog_entrycount(log) > 0);
+ git_signature_free(sig);
+ git_reflog_free(log);
+
+ cl_git_pass(git_str_joinpath(&ref_folder, commondir, "refs/heads/some/deep"));
+ cl_git_pass(git_str_join3(&reflog_folder, '/', commondir, GIT_REFLOG_DIR, "refs/heads/some/deep"));
+
+ cl_assert(git_fs_path_exists(git_str_cstr(&ref_folder)) == true);
+ cl_assert(git_fs_path_exists(git_str_cstr(&reflog_folder)) == true);
+
+ cl_git_pass(git_branch_delete(branch));
+
+ cl_assert(git_fs_path_exists(git_str_cstr(&ref_folder)) == false);
+ cl_assert(git_fs_path_exists(git_str_cstr(&reflog_folder)) == false);
+
+ git_reference_free(branch);
+ git_str_dispose(&ref_folder);
+ git_str_dispose(&reflog_folder);
+}
+
diff --git a/tests/libgit2/refs/branches/ishead.c b/tests/libgit2/refs/branches/ishead.c
new file mode 100644
index 0000000..1df70b7
--- /dev/null
+++ b/tests/libgit2/refs/branches/ishead.c
@@ -0,0 +1,98 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+#include "repo/repo_helpers.h"
+
+static git_repository *repo;
+static git_reference *branch;
+
+void test_refs_branches_ishead__initialize(void)
+{
+ repo = cl_git_sandbox_init("testrepo.git");
+ branch = NULL;
+}
+
+void test_refs_branches_ishead__cleanup(void)
+{
+ git_reference_free(branch);
+ branch = NULL;
+
+ cl_git_sandbox_cleanup();
+ repo = NULL;
+}
+
+void test_refs_branches_ishead__can_tell_if_a_branch_is_pointed_at_by_HEAD(void)
+{
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master"));
+
+ cl_assert_equal_i(true, git_branch_is_head(branch));
+}
+
+void test_refs_branches_ishead__can_properly_handle_unborn_HEAD(void)
+{
+ make_head_unborn(repo, NON_EXISTING_HEAD);
+
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master"));
+
+ cl_assert_equal_i(false, git_branch_is_head(branch));
+}
+
+void test_refs_branches_ishead__can_properly_handle_missing_HEAD(void)
+{
+ delete_head(repo);
+
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master"));
+
+ cl_assert_equal_i(false, git_branch_is_head(branch));
+}
+
+void test_refs_branches_ishead__can_tell_if_a_branch_is_not_pointed_at_by_HEAD(void)
+{
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/br2"));
+
+ cl_assert_equal_i(false, git_branch_is_head(branch));
+}
+
+void test_refs_branches_ishead__wont_be_fooled_by_a_non_branch(void)
+{
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/tags/e90810b"));
+
+ cl_assert_equal_i(false, git_branch_is_head(branch));
+}
+
+/*
+ * $ git init .
+ * Initialized empty Git repository in d:/temp/tempee/.git/
+ *
+ * $ touch a && git add a
+ * $ git commit -m" boom"
+ * [master (root-commit) b47b758] boom
+ * 0 files changed
+ * create mode 100644 a
+ *
+ * $ echo "ref: refs/heads/master" > .git/refs/heads/linked
+ * $ echo "ref: refs/heads/linked" > .git/refs/heads/super
+ * $ echo "ref: refs/heads/super" > .git/HEAD
+ *
+ * $ git branch
+ * linked -> master
+ * * master
+ * super -> master
+ */
+void test_refs_branches_ishead__only_direct_references_are_considered(void)
+{
+ git_reference *linked, *super, *head;
+
+ cl_git_pass(git_reference_symbolic_create(&linked, repo, "refs/heads/linked", "refs/heads/master", 0, NULL));
+ cl_git_pass(git_reference_symbolic_create(&super, repo, "refs/heads/super", "refs/heads/linked", 0, NULL));
+ cl_git_pass(git_reference_symbolic_create(&head, repo, GIT_HEAD_FILE, "refs/heads/super", 1, NULL));
+
+ cl_assert_equal_i(false, git_branch_is_head(linked));
+ cl_assert_equal_i(false, git_branch_is_head(super));
+
+ cl_git_pass(git_repository_head(&branch, repo));
+ cl_assert_equal_s("refs/heads/master", git_reference_name(branch));
+
+ git_reference_free(linked);
+ git_reference_free(super);
+ git_reference_free(head);
+}
diff --git a/tests/libgit2/refs/branches/iterator.c b/tests/libgit2/refs/branches/iterator.c
new file mode 100644
index 0000000..a295d55
--- /dev/null
+++ b/tests/libgit2/refs/branches/iterator.c
@@ -0,0 +1,151 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+
+static git_repository *repo;
+static git_reference *fake_remote;
+
+void test_refs_branches_iterator__initialize(void)
+{
+ git_oid id;
+
+ cl_fixture_sandbox("testrepo.git");
+ cl_git_pass(git_repository_open(&repo, "testrepo.git"));
+
+ cl_git_pass(git_oid__fromstr(&id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644", GIT_OID_SHA1));
+ cl_git_pass(git_reference_create(&fake_remote, repo, "refs/remotes/nulltoken/master", &id, 0, NULL));
+}
+
+void test_refs_branches_iterator__cleanup(void)
+{
+ git_reference_free(fake_remote);
+ fake_remote = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+
+ cl_fixture_cleanup("testrepo.git");
+
+ cl_git_sandbox_cleanup();
+}
+
+static void assert_retrieval(unsigned int flags, unsigned int expected_count)
+{
+ git_branch_iterator *iter;
+ git_reference *ref;
+ int count = 0, error;
+ git_branch_t type;
+
+ cl_git_pass(git_branch_iterator_new(&iter, repo, flags));
+ while ((error = git_branch_next(&ref, &type, iter)) == 0) {
+ count++;
+ git_reference_free(ref);
+ }
+
+ git_branch_iterator_free(iter);
+ cl_assert_equal_i(error, GIT_ITEROVER);
+ cl_assert_equal_i(expected_count, count);
+}
+
+void test_refs_branches_iterator__retrieve_all_branches(void)
+{
+ assert_retrieval(GIT_BRANCH_ALL, 15);
+}
+
+void test_refs_branches_iterator__retrieve_remote_branches(void)
+{
+ assert_retrieval(GIT_BRANCH_REMOTE, 2);
+}
+
+void test_refs_branches_iterator__retrieve_local_branches(void)
+{
+ assert_retrieval(GIT_BRANCH_LOCAL, 13);
+}
+
+struct expectations {
+ const char *branch_name;
+ int encounters;
+};
+
+static void assert_branch_has_been_found(struct expectations *findings, const char* expected_branch_name)
+{
+ int pos = 0;
+
+ for (pos = 0; findings[pos].branch_name; ++pos) {
+ if (strcmp(expected_branch_name, findings[pos].branch_name) == 0) {
+ cl_assert_equal_i(1, findings[pos].encounters);
+ return;
+ }
+ }
+
+ cl_fail("expected branch not found in list.");
+}
+
+static void contains_branches(struct expectations exp[], git_branch_iterator *iter)
+{
+ git_reference *ref;
+ git_branch_t type;
+ int error, pos = 0;
+
+ while ((error = git_branch_next(&ref, &type, iter)) == 0) {
+ for (pos = 0; exp[pos].branch_name; ++pos) {
+ if (strcmp(git_reference_shorthand(ref), exp[pos].branch_name) == 0)
+ exp[pos].encounters++;
+ }
+
+ git_reference_free(ref);
+ }
+
+ cl_assert_equal_i(error, GIT_ITEROVER);
+}
+
+/*
+ * $ git branch -r
+ * nulltoken/HEAD -> nulltoken/master
+ * nulltoken/master
+ */
+void test_refs_branches_iterator__retrieve_remote_symbolic_HEAD_when_present(void)
+{
+ git_branch_iterator *iter;
+ struct expectations exp[] = {
+ { "nulltoken/HEAD", 0 },
+ { "nulltoken/master", 0 },
+ { NULL, 0 }
+ };
+
+ git_reference_free(fake_remote);
+ cl_git_pass(git_reference_symbolic_create(&fake_remote, repo, "refs/remotes/nulltoken/HEAD", "refs/remotes/nulltoken/master", 0, NULL));
+
+ assert_retrieval(GIT_BRANCH_REMOTE, 3);
+
+ cl_git_pass(git_branch_iterator_new(&iter, repo, GIT_BRANCH_REMOTE));
+ contains_branches(exp, iter);
+ git_branch_iterator_free(iter);
+
+ assert_branch_has_been_found(exp, "nulltoken/HEAD");
+ assert_branch_has_been_found(exp, "nulltoken/master");
+}
+
+void test_refs_branches_iterator__mix_of_packed_and_loose(void)
+{
+ git_branch_iterator *iter;
+ struct expectations exp[] = {
+ { "master", 0 },
+ { "origin/HEAD", 0 },
+ { "origin/master", 0 },
+ { "origin/packed", 0 },
+ { NULL, 0 }
+ };
+ git_repository *r2;
+
+ r2 = cl_git_sandbox_init("testrepo2");
+
+ cl_git_pass(git_branch_iterator_new(&iter, r2, GIT_BRANCH_ALL));
+ contains_branches(exp, iter);
+
+ git_branch_iterator_free(iter);
+
+ assert_branch_has_been_found(exp, "master");
+ assert_branch_has_been_found(exp, "origin/HEAD");
+ assert_branch_has_been_found(exp, "origin/master");
+ assert_branch_has_been_found(exp, "origin/packed");
+}
diff --git a/tests/libgit2/refs/branches/lookup.c b/tests/libgit2/refs/branches/lookup.c
new file mode 100644
index 0000000..ef0c1f9
--- /dev/null
+++ b/tests/libgit2/refs/branches/lookup.c
@@ -0,0 +1,68 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+
+static git_repository *repo;
+static git_reference *branch;
+
+void test_refs_branches_lookup__initialize(void)
+{
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+
+ branch = NULL;
+}
+
+void test_refs_branches_lookup__cleanup(void)
+{
+ git_reference_free(branch);
+ branch = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+}
+
+void test_refs_branches_lookup__can_retrieve_a_local_branch_local(void)
+{
+ cl_git_pass(git_branch_lookup(&branch, repo, "br2", GIT_BRANCH_LOCAL));
+}
+
+void test_refs_branches_lookup__can_retrieve_a_local_branch_all(void)
+{
+ cl_git_pass(git_branch_lookup(&branch, repo, "br2", GIT_BRANCH_ALL));
+}
+
+void test_refs_branches_lookup__trying_to_retrieve_a_local_branch_remote(void)
+{
+ cl_git_fail(git_branch_lookup(&branch, repo, "br2", GIT_BRANCH_REMOTE));
+}
+
+void test_refs_branches_lookup__can_retrieve_a_remote_tracking_branch_remote(void)
+{
+ cl_git_pass(git_branch_lookup(&branch, repo, "test/master", GIT_BRANCH_REMOTE));
+}
+
+void test_refs_branches_lookup__can_retrieve_a_remote_tracking_branch_all(void)
+{
+ cl_git_pass(git_branch_lookup(&branch, repo, "test/master", GIT_BRANCH_ALL));
+}
+
+void test_refs_branches_lookup__trying_to_retrieve_a_remote_tracking_branch_local(void)
+{
+ cl_git_fail(git_branch_lookup(&branch, repo, "test/master", GIT_BRANCH_LOCAL));
+}
+
+void test_refs_branches_lookup__trying_to_retrieve_an_unknown_branch_returns_ENOTFOUND(void)
+{
+ cl_assert_equal_i(GIT_ENOTFOUND, git_branch_lookup(&branch, repo, "where/are/you", GIT_BRANCH_LOCAL));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_branch_lookup(&branch, repo, "over/here", GIT_BRANCH_REMOTE));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_branch_lookup(&branch, repo, "maybe/here", GIT_BRANCH_ALL));
+}
+
+void test_refs_branches_lookup__trying_to_retrieve_a_branch_with_an_invalid_name_returns_EINVALIDSPEC(void)
+{
+ cl_assert_equal_i(GIT_EINVALIDSPEC,
+ git_branch_lookup(&branch, repo, "are/you/inv@{id", GIT_BRANCH_LOCAL));
+ cl_assert_equal_i(GIT_EINVALIDSPEC,
+ git_branch_lookup(&branch, repo, "yes/i am", GIT_BRANCH_REMOTE));
+ cl_assert_equal_i(GIT_EINVALIDSPEC,
+ git_branch_lookup(&branch, repo, "inv al/id", GIT_BRANCH_ALL));
+}
diff --git a/tests/libgit2/refs/branches/move.c b/tests/libgit2/refs/branches/move.c
new file mode 100644
index 0000000..4cfb7b8
--- /dev/null
+++ b/tests/libgit2/refs/branches/move.c
@@ -0,0 +1,250 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+#include "config/config_helpers.h"
+
+static git_repository *repo;
+
+void test_refs_branches_move__initialize(void)
+{
+ repo = cl_git_sandbox_init("testrepo.git");
+}
+
+void test_refs_branches_move__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+#define NEW_BRANCH_NAME "new-branch-on-the-block"
+
+void test_refs_branches_move__can_move_a_local_branch(void)
+{
+ git_reference *original_ref, *new_ref;
+
+ cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2"));
+
+ cl_git_pass(git_branch_move(&new_ref, original_ref, NEW_BRANCH_NAME, 0));
+ cl_assert_equal_s(GIT_REFS_HEADS_DIR NEW_BRANCH_NAME, git_reference_name(new_ref));
+
+ git_reference_free(original_ref);
+ git_reference_free(new_ref);
+}
+
+void test_refs_branches_move__can_move_a_local_branch_to_a_different_namespace(void)
+{
+ git_reference *original_ref, *new_ref, *newer_ref;
+
+ cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2"));
+
+ /* Downward */
+ cl_git_pass(git_branch_move(&new_ref, original_ref, "somewhere/" NEW_BRANCH_NAME, 0));
+ git_reference_free(original_ref);
+
+ /* Upward */
+ cl_git_pass(git_branch_move(&newer_ref, new_ref, "br2", 0));
+ git_reference_free(new_ref);
+
+ git_reference_free(newer_ref);
+}
+
+void test_refs_branches_move__can_move_a_local_branch_to_a_partially_colliding_namespace(void)
+{
+ git_reference *original_ref, *new_ref, *newer_ref;
+
+ cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2"));
+
+ /* Downward */
+ cl_git_pass(git_branch_move(&new_ref, original_ref, "br2/" NEW_BRANCH_NAME, 0));
+ git_reference_free(original_ref);
+
+ /* Upward */
+ cl_git_pass(git_branch_move(&newer_ref, new_ref, "br2", 0));
+ git_reference_free(new_ref);
+
+ git_reference_free(newer_ref);
+}
+
+void test_refs_branches_move__can_not_move_a_branch_if_its_destination_name_collide_with_an_existing_one(void)
+{
+ git_reference *original_ref, *new_ref;
+ git_config *config;
+ git_buf original_remote = GIT_BUF_INIT,
+ original_merge = GIT_BUF_INIT;
+ const char *str;
+
+ cl_git_pass(git_repository_config_snapshot(&config, repo));
+
+ cl_git_pass(git_config_get_string_buf(&original_remote, config, "branch.master.remote"));
+ cl_git_pass(git_config_get_string_buf(&original_merge, config, "branch.master.merge"));
+ git_config_free(config);
+
+ cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2"));
+
+ cl_assert_equal_i(GIT_EEXISTS,
+ git_branch_move(&new_ref, original_ref, "master", 0));
+
+ cl_assert(git_error_last()->message != NULL);
+
+ cl_git_pass(git_repository_config_snapshot(&config, repo));
+ cl_git_pass(git_config_get_string(&str, config, "branch.master.remote"));
+ cl_assert_equal_s(original_remote.ptr, str);
+ cl_git_pass(git_config_get_string(&str, config, "branch.master.merge"));
+ cl_assert_equal_s(original_merge.ptr, str);
+ git_config_free(config);
+
+ cl_assert_equal_i(GIT_EEXISTS,
+ git_branch_move(&new_ref, original_ref, "cannot-fetch", 0));
+
+ cl_assert(git_error_last()->message != NULL);
+
+ cl_git_pass(git_repository_config_snapshot(&config, repo));
+ cl_git_pass(git_config_get_string(&str, config, "branch.master.remote"));
+ cl_assert_equal_s(original_remote.ptr, str);
+ cl_git_pass(git_config_get_string(&str, config, "branch.master.merge"));
+ cl_assert_equal_s(original_merge.ptr, str);
+ git_config_free(config);
+
+ git_reference_free(original_ref);
+ cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/track-local"));
+
+ cl_assert_equal_i(GIT_EEXISTS,
+ git_branch_move(&new_ref, original_ref, "master", 0));
+
+ cl_assert(git_error_last()->message != NULL);
+
+ cl_git_pass(git_repository_config_snapshot(&config, repo));
+ cl_git_pass(git_config_get_string(&str, config, "branch.master.remote"));
+ cl_assert_equal_s(original_remote.ptr, str);
+ cl_git_pass(git_config_get_string(&str, config, "branch.master.merge"));
+ cl_assert_equal_s(original_merge.ptr, str);
+
+ git_buf_dispose(&original_remote);
+ git_buf_dispose(&original_merge);
+ git_reference_free(original_ref);
+ git_config_free(config);
+}
+
+void test_refs_branches_move__moving_a_branch_with_an_invalid_name_returns_EINVALIDSPEC(void)
+{
+ git_reference *original_ref, *new_ref;
+
+ cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2"));
+
+ cl_assert_equal_i(GIT_EINVALIDSPEC, git_branch_move(&new_ref, original_ref, "Inv@{id", 0));
+
+ git_reference_free(original_ref);
+}
+
+void test_refs_branches_move__can_not_move_a_non_branch(void)
+{
+ git_reference *tag, *new_ref;
+
+ cl_git_pass(git_reference_lookup(&tag, repo, "refs/tags/e90810b"));
+ cl_git_fail(git_branch_move(&new_ref, tag, NEW_BRANCH_NAME, 0));
+
+ git_reference_free(tag);
+}
+
+void test_refs_branches_move__can_force_move_over_an_existing_branch(void)
+{
+ git_reference *original_ref, *new_ref;
+
+ cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2"));
+
+ cl_git_pass(git_branch_move(&new_ref, original_ref, "master", 1));
+
+ git_reference_free(original_ref);
+ git_reference_free(new_ref);
+}
+
+void test_refs_branches_move__moving_a_branch_moves_related_configuration_data(void)
+{
+ git_reference *branch;
+ git_reference *new_branch;
+
+ cl_git_pass(git_branch_lookup(&branch, repo, "track-local", GIT_BRANCH_LOCAL));
+
+ assert_config_entry_existence(repo, "branch.track-local.remote", true);
+ assert_config_entry_existence(repo, "branch.track-local.merge", true);
+ assert_config_entry_existence(repo, "branch.moved.remote", false);
+ assert_config_entry_existence(repo, "branch.moved.merge", false);
+
+ cl_git_pass(git_branch_move(&new_branch, branch, "moved", 0));
+ git_reference_free(branch);
+
+ assert_config_entry_existence(repo, "branch.track-local.remote", false);
+ assert_config_entry_existence(repo, "branch.track-local.merge", false);
+ assert_config_entry_existence(repo, "branch.moved.remote", true);
+ assert_config_entry_existence(repo, "branch.moved.merge", true);
+
+ git_reference_free(new_branch);
+}
+
+void test_refs_branches_move__moving_the_branch_pointed_at_by_HEAD_updates_HEAD(void)
+{
+ git_reference *branch;
+ git_reference *new_branch;
+
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master"));
+ cl_git_pass(git_branch_move(&new_branch, branch, "master2", 0));
+ git_reference_free(branch);
+ git_reference_free(new_branch);
+
+ cl_git_pass(git_repository_head(&branch, repo));
+ cl_assert_equal_s("refs/heads/master2", git_reference_name(branch));
+ git_reference_free(branch);
+}
+
+void test_refs_branches_move__can_move_with_unicode(void)
+{
+ git_reference *original_ref, *new_ref;
+ const char *new_branch_name = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D";
+
+ cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2"));
+ cl_git_pass(git_branch_move(&new_ref, original_ref, new_branch_name, 0));
+
+ if (cl_repo_get_bool(repo, "core.precomposeunicode"))
+ cl_assert_equal_s(GIT_REFS_HEADS_DIR "\xC3\x85\x73\x74\x72\xC3\xB6\x6D", git_reference_name(new_ref));
+ else
+ cl_assert_equal_s(GIT_REFS_HEADS_DIR "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D", git_reference_name(new_ref));
+
+ git_reference_free(original_ref);
+ git_reference_free(new_ref);
+}
+
+void test_refs_branches_move__moves_reflog_correctly(void)
+{
+ git_reference *original_ref, *new_ref;
+ git_reflog *original_reflog, *new_reflog;
+
+ cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2"));
+
+ cl_git_pass(git_reflog_read(&original_reflog, repo, "refs/heads/br2"));
+ cl_assert_equal_i(2, git_reflog_entrycount(original_reflog));
+
+ cl_git_pass(git_branch_move(&new_ref, original_ref, NEW_BRANCH_NAME, 0));
+ cl_assert_equal_s(GIT_REFS_HEADS_DIR NEW_BRANCH_NAME, git_reference_name(new_ref));
+
+ cl_git_pass(git_reflog_read(&new_reflog, repo, GIT_REFS_HEADS_DIR NEW_BRANCH_NAME));
+ cl_assert_equal_i(3, git_reflog_entrycount(new_reflog));
+
+ git_reference_free(original_ref);
+ git_reference_free(new_ref);
+ git_reflog_free(original_reflog);
+ git_reflog_free(new_reflog);
+}
+
+void test_refs_branches_move__failed_move_restores_reflog(void)
+{
+ git_reference *original_ref, *new_ref;
+ git_reflog *recovered_reflog;
+
+ cl_git_pass(git_reference_lookup(&original_ref, repo, "refs/heads/br2"));
+
+ cl_assert_equal_i(GIT_EINVALIDSPEC, git_branch_move(&new_ref, original_ref, "Inv@{id", 0));
+
+ cl_git_pass(git_reflog_read(&recovered_reflog, repo, "refs/heads/br2"));
+ cl_assert_equal_i(2, git_reflog_entrycount(recovered_reflog));
+
+ git_reference_free(original_ref);
+ git_reflog_free(recovered_reflog);
+}
diff --git a/tests/libgit2/refs/branches/name.c b/tests/libgit2/refs/branches/name.c
new file mode 100644
index 0000000..efa68e3
--- /dev/null
+++ b/tests/libgit2/refs/branches/name.c
@@ -0,0 +1,62 @@
+#include "clar_libgit2.h"
+#include "branch.h"
+
+static git_repository *repo;
+static git_reference *ref;
+
+void test_refs_branches_name__initialize(void)
+{
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+}
+
+void test_refs_branches_name__cleanup(void)
+{
+ git_reference_free(ref);
+ ref = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+}
+
+void test_refs_branches_name__can_get_local_branch_name(void)
+{
+ const char *name;
+
+ cl_git_pass(git_branch_lookup(&ref,repo,"master",GIT_BRANCH_LOCAL));
+ cl_git_pass(git_branch_name(&name,ref));
+ cl_assert_equal_s("master",name);
+}
+
+void test_refs_branches_name__can_get_remote_branch_name(void)
+{
+ const char *name;
+
+ cl_git_pass(git_branch_lookup(&ref,repo,"test/master",GIT_BRANCH_REMOTE));
+ cl_git_pass(git_branch_name(&name,ref));
+ cl_assert_equal_s("test/master",name);
+}
+
+void test_refs_branches_name__error_when_ref_is_no_branch(void)
+{
+ const char *name;
+
+ cl_git_pass(git_reference_lookup(&ref,repo,"refs/notes/fanout"));
+ cl_git_fail(git_branch_name(&name,ref));
+}
+
+static int name_is_valid(const char *name)
+{
+ int valid;
+ cl_git_pass(git_branch_name_is_valid(&valid, name));
+ return valid;
+}
+
+void test_refs_branches_name__is_name_valid(void)
+{
+ cl_assert_equal_i(true, name_is_valid("master"));
+ cl_assert_equal_i(true, name_is_valid("test/master"));
+
+ cl_assert_equal_i(false, name_is_valid(""));
+ cl_assert_equal_i(false, name_is_valid("HEAD"));
+ cl_assert_equal_i(false, name_is_valid("-dash"));
+}
diff --git a/tests/libgit2/refs/branches/remote.c b/tests/libgit2/refs/branches/remote.c
new file mode 100644
index 0000000..e2bd348
--- /dev/null
+++ b/tests/libgit2/refs/branches/remote.c
@@ -0,0 +1,65 @@
+#include "clar_libgit2.h"
+#include "branch.h"
+#include "remote.h"
+
+static git_repository *g_repo;
+static const char *remote_tracking_branch_name = "refs/remotes/test/master";
+static const char *expected_remote_name = "test";
+static int expected_remote_name_length;
+
+void test_refs_branches_remote__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+
+ expected_remote_name_length = (int)strlen(expected_remote_name) + 1;
+}
+
+void test_refs_branches_remote__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_branches_remote__can_get_remote_for_branch(void)
+{
+ git_buf remotename = {0};
+
+ cl_git_pass(git_branch_remote_name(&remotename, g_repo, remote_tracking_branch_name));
+
+ cl_assert_equal_s("test", remotename.ptr);
+ git_buf_dispose(&remotename);
+}
+
+void test_refs_branches_remote__no_matching_remote_returns_error(void)
+{
+ const char *unknown = "refs/remotes/nonexistent/master";
+ git_buf buf = GIT_BUF_INIT;
+
+ git_error_clear();
+ cl_git_fail_with(git_branch_remote_name(&buf, g_repo, unknown), GIT_ENOTFOUND);
+ cl_assert(git_error_last() != NULL);
+}
+
+void test_refs_branches_remote__local_remote_returns_error(void)
+{
+ const char *local = "refs/heads/master";
+ git_buf buf = GIT_BUF_INIT;
+
+ git_error_clear();
+ cl_git_fail_with(git_branch_remote_name(&buf, g_repo, local), GIT_ERROR);
+ cl_assert(git_error_last() != NULL);
+}
+
+void test_refs_branches_remote__ambiguous_remote_returns_error(void)
+{
+ git_remote *remote;
+ git_buf buf = GIT_BUF_INIT;
+
+ /* Create the remote */
+ cl_git_pass(git_remote_create_with_fetchspec(&remote, g_repo, "addtest", "http://github.com/libgit2/libgit2", "refs/heads/*:refs/remotes/test/*"));
+
+ git_remote_free(remote);
+
+ git_error_clear();
+ cl_git_fail_with(git_branch_remote_name(&buf, g_repo, remote_tracking_branch_name), GIT_EAMBIGUOUS);
+ cl_assert(git_error_last() != NULL);
+}
diff --git a/tests/libgit2/refs/branches/upstream.c b/tests/libgit2/refs/branches/upstream.c
new file mode 100644
index 0000000..919705e
--- /dev/null
+++ b/tests/libgit2/refs/branches/upstream.c
@@ -0,0 +1,218 @@
+#include "clar_libgit2.h"
+#include "config/config_helpers.h"
+#include "refs.h"
+
+static git_repository *repo;
+static git_reference *branch, *upstream;
+
+void test_refs_branches_upstream__initialize(void)
+{
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+
+ branch = NULL;
+ upstream = NULL;
+}
+
+void test_refs_branches_upstream__cleanup(void)
+{
+ git_reference_free(upstream);
+ git_reference_free(branch);
+ branch = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+}
+
+void test_refs_branches_upstream__can_retrieve_the_remote_tracking_reference_of_a_local_branch(void)
+{
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/master"));
+
+ cl_git_pass(git_branch_upstream(&upstream, branch));
+
+ cl_assert_equal_s("refs/remotes/test/master", git_reference_name(upstream));
+}
+
+void test_refs_branches_upstream__can_retrieve_the_local_upstream_reference_of_a_local_branch(void)
+{
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/track-local"));
+
+ cl_git_pass(git_branch_upstream(&upstream, branch));
+
+ cl_assert_equal_s("refs/heads/master", git_reference_name(upstream));
+}
+
+void test_refs_branches_upstream__cannot_retrieve_a_remote_upstream_reference_from_a_non_branch(void)
+{
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/tags/e90810b"));
+
+ cl_git_fail(git_branch_upstream(&upstream, branch));
+}
+
+void test_refs_branches_upstream__trying_to_retrieve_a_remote_tracking_reference_from_a_plain_local_branch_returns_GIT_ENOTFOUND(void)
+{
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/subtrees"));
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_branch_upstream(&upstream, branch));
+}
+
+void test_refs_branches_upstream__trying_to_retrieve_a_remote_tracking_reference_from_a_branch_with_no_fetchspec_returns_GIT_ENOTFOUND(void)
+{
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/cannot-fetch"));
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_branch_upstream(&upstream, branch));
+}
+
+void test_refs_branches_upstream__upstream_remote(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_git_pass(git_branch_upstream_remote(&buf, repo, "refs/heads/master"));
+ cl_assert_equal_s("test", buf.ptr);
+ git_buf_dispose(&buf);
+}
+
+void test_refs_branches_upstream__upstream_merge(void)
+{
+ git_reference *branch;
+ git_repository *repository;
+ git_buf buf = GIT_BUF_INIT;
+
+ repository = cl_git_sandbox_init("testrepo.git");
+
+ /* check repository */
+ cl_git_pass(git_reference_lookup(&branch, repository, "refs/heads/test"));
+ cl_git_pass(git_branch_set_upstream(branch, "test/master"));
+
+ assert_config_entry_value(repository, "branch.test.remote", "test");
+ assert_config_entry_value(repository, "branch.test.merge", "refs/heads/master");
+
+ git_reference_free(branch);
+
+ /* check merge branch */
+ cl_git_pass(git_branch_upstream_merge(&buf, repository, "refs/heads/test"));
+ cl_assert_equal_s("refs/heads/master", buf.ptr);
+ git_buf_dispose(&buf);
+
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_branches_upstream__upstream_remote_empty_value(void)
+{
+ git_repository *repository;
+ git_config *cfg;
+ git_buf buf = GIT_BUF_INIT;
+
+ repository = cl_git_sandbox_init("testrepo.git");
+ cl_git_pass(git_repository_config(&cfg, repository));
+ cl_git_pass(git_config_set_string(cfg, "branch.master.remote", ""));
+ cl_git_fail_with(GIT_ENOTFOUND, git_branch_upstream_remote(&buf, repository, "refs/heads/master"));
+
+ cl_git_pass(git_config_delete_entry(cfg, "branch.master.remote"));
+ cl_git_fail_with(GIT_ENOTFOUND, git_branch_upstream_remote(&buf, repository, "refs/heads/master"));
+ cl_git_sandbox_cleanup();
+}
+
+static void assert_merge_and_or_remote_key_missing(git_repository *repository, const git_commit *target, const char *entry_name)
+{
+ git_reference *branch;
+
+ cl_assert_equal_i(GIT_OBJECT_COMMIT, git_object_type((git_object*)target));
+ cl_git_pass(git_branch_create(&branch, repository, entry_name, (git_commit*)target, 0));
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_branch_upstream(&upstream, branch));
+
+ git_reference_free(branch);
+}
+
+void test_refs_branches_upstream__retrieve_a_remote_tracking_reference_from_a_branch_with_no_remote_returns_GIT_ENOTFOUND(void)
+{
+ git_reference *head;
+ git_repository *repository;
+ git_commit *target;
+
+ repository = cl_git_sandbox_init("testrepo.git");
+
+ cl_git_pass(git_repository_head(&head, repository));
+ cl_git_pass(git_reference_peel(((git_object **)&target), head, GIT_OBJECT_COMMIT));
+ git_reference_free(head);
+
+ assert_merge_and_or_remote_key_missing(repository, target, "remoteless");
+ assert_merge_and_or_remote_key_missing(repository, target, "mergeless");
+ assert_merge_and_or_remote_key_missing(repository, target, "mergeandremoteless");
+
+ git_commit_free(target);
+
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_branches_upstream__set_unset_upstream(void)
+{
+ git_reference *branch;
+ git_repository *repository;
+
+ repository = cl_git_sandbox_init("testrepo.git");
+
+ /* remote */
+ cl_git_pass(git_reference_lookup(&branch, repository, "refs/heads/test"));
+ cl_git_pass(git_branch_set_upstream(branch, "test/master"));
+
+ assert_config_entry_value(repository, "branch.test.remote", "test");
+ assert_config_entry_value(repository, "branch.test.merge", "refs/heads/master");
+
+ git_reference_free(branch);
+
+ /* local */
+ cl_git_pass(git_reference_lookup(&branch, repository, "refs/heads/test"));
+ cl_git_pass(git_branch_set_upstream(branch, "master"));
+
+ assert_config_entry_value(repository, "branch.test.remote", ".");
+ assert_config_entry_value(repository, "branch.test.merge", "refs/heads/master");
+
+ /* unset */
+ cl_git_pass(git_branch_set_upstream(branch, NULL));
+ assert_config_entry_existence(repository, "branch.test.remote", false);
+ assert_config_entry_existence(repository, "branch.test.merge", false);
+
+ git_reference_free(branch);
+
+ cl_git_pass(git_reference_lookup(&branch, repository, "refs/heads/master"));
+ cl_git_pass(git_branch_set_upstream(branch, NULL));
+ assert_config_entry_existence(repository, "branch.test.remote", false);
+ assert_config_entry_existence(repository, "branch.test.merge", false);
+
+ git_reference_free(branch);
+
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_branches_upstream__no_fetch_refspec(void)
+{
+ git_reference *ref, *branch;
+ git_repository *repo;
+ git_remote *remote;
+ git_config *cfg;
+
+ repo = cl_git_sandbox_init("testrepo.git");
+
+ cl_git_pass(git_remote_create_with_fetchspec(&remote, repo, "matching", ".", NULL));
+ cl_git_pass(git_remote_add_push(repo, "matching", ":"));
+
+ cl_git_pass(git_reference_lookup(&branch, repo, "refs/heads/test"));
+ cl_git_pass(git_reference_create(&ref, repo, "refs/remotes/matching/master", git_reference_target(branch), 1, "fetch"));
+ cl_git_fail(git_branch_set_upstream(branch, "matching/master"));
+ cl_assert_equal_s("could not determine remote for 'refs/remotes/matching/master'",
+ git_error_last()->message);
+
+ /* we can't set it automatically, so let's test the user setting it by hand */
+ cl_git_pass(git_repository_config(&cfg, repo));
+ cl_git_pass(git_config_set_string(cfg, "branch.test.remote", "matching"));
+ cl_git_pass(git_config_set_string(cfg, "branch.test.merge", "refs/heads/master"));
+ /* we still can't find it because there is no rule for that reference */
+ cl_git_fail_with(GIT_ENOTFOUND, git_branch_upstream(&ref, branch));
+
+ git_reference_free(ref);
+ git_reference_free(branch);
+ git_remote_free(remote);
+
+ cl_git_sandbox_cleanup();
+}
diff --git a/tests/libgit2/refs/branches/upstreamname.c b/tests/libgit2/refs/branches/upstreamname.c
new file mode 100644
index 0000000..5bae154
--- /dev/null
+++ b/tests/libgit2/refs/branches/upstreamname.c
@@ -0,0 +1,35 @@
+#include "clar_libgit2.h"
+#include "branch.h"
+
+static git_repository *repo;
+static git_buf upstream_name;
+
+void test_refs_branches_upstreamname__initialize(void)
+{
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+
+}
+
+void test_refs_branches_upstreamname__cleanup(void)
+{
+ git_buf_dispose(&upstream_name);
+
+ git_repository_free(repo);
+ repo = NULL;
+}
+
+void test_refs_branches_upstreamname__can_retrieve_the_remote_tracking_reference_name_of_a_local_branch(void)
+{
+ cl_git_pass(git_branch_upstream_name(
+ &upstream_name, repo, "refs/heads/master"));
+
+ cl_assert_equal_s("refs/remotes/test/master", upstream_name.ptr);
+}
+
+void test_refs_branches_upstreamname__can_retrieve_the_local_upstream_reference_name_of_a_local_branch(void)
+{
+ cl_git_pass(git_branch_upstream_name(
+ &upstream_name, repo, "refs/heads/track-local"));
+
+ cl_assert_equal_s("refs/heads/master", upstream_name.ptr);
+}
diff --git a/tests/libgit2/refs/crashes.c b/tests/libgit2/refs/crashes.c
new file mode 100644
index 0000000..4f508ae
--- /dev/null
+++ b/tests/libgit2/refs/crashes.c
@@ -0,0 +1,44 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+
+void test_refs_crashes__double_free(void)
+{
+ git_repository *repo;
+ git_reference *ref, *ref2;
+ const char *REFNAME = "refs/heads/xxx";
+
+ repo = cl_git_sandbox_init("testrepo.git");
+ cl_git_pass(git_reference_symbolic_create(&ref, repo, REFNAME, "refs/heads/master", 0, NULL));
+ cl_git_pass(git_reference_lookup(&ref2, repo, REFNAME));
+ cl_git_pass(git_reference_delete(ref));
+ git_reference_free(ref);
+ git_reference_free(ref2);
+
+ /* reference is gone from disk, so reloading it will fail */
+ cl_git_fail(git_reference_lookup(&ref2, repo, REFNAME));
+
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_crashes__empty_packedrefs(void)
+{
+ git_repository *repo;
+ git_reference *ref;
+ const char *REFNAME = "refs/heads/xxx";
+ git_str temp_path = GIT_STR_INIT;
+ int fd = 0;
+
+ repo = cl_git_sandbox_init("empty_bare.git");
+
+ /* create zero-length packed-refs file */
+ cl_git_pass(git_str_joinpath(&temp_path, git_repository_path(repo), GIT_PACKEDREFS_FILE));
+ cl_git_pass(((fd = p_creat(temp_path.ptr, 0644)) < 0));
+ cl_git_pass(p_close(fd));
+
+ /* should fail gracefully */
+ cl_git_fail_with(
+ GIT_ENOTFOUND, git_reference_lookup(&ref, repo, REFNAME));
+
+ cl_git_sandbox_cleanup();
+ git_str_dispose(&temp_path);
+}
diff --git a/tests/libgit2/refs/create.c b/tests/libgit2/refs/create.c
new file mode 100644
index 0000000..1dafcf6
--- /dev/null
+++ b/tests/libgit2/refs/create.c
@@ -0,0 +1,362 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+#include "git2/reflog.h"
+#include "reflog.h"
+#include "ref_helpers.h"
+
+static const char *current_master_tip = "099fabac3a9ea935598528c27f866e34089c2eff";
+static const char *current_head_target = "refs/heads/master";
+
+static git_repository *g_repo;
+
+void test_refs_create__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+ p_fsync__cnt = 0;
+}
+
+void test_refs_create__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 1));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION, 1));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_FSYNC_GITDIR, 0));
+}
+
+void test_refs_create__symbolic(void)
+{
+ /* create a new symbolic reference */
+ git_reference *new_reference, *looked_up_ref, *resolved_ref;
+ git_repository *repo2;
+ git_oid id;
+
+ const char *new_head_tracker = "ANOTHER_HEAD_TRACKER";
+
+ git_oid__fromstr(&id, current_master_tip, GIT_OID_SHA1);
+
+ /* Create and write the new symbolic reference */
+ cl_git_pass(git_reference_symbolic_create(&new_reference, g_repo, new_head_tracker, current_head_target, 0, NULL));
+
+ /* Ensure the reference can be looked-up... */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, new_head_tracker));
+ cl_assert(git_reference_type(looked_up_ref) & GIT_REFERENCE_SYMBOLIC);
+ cl_assert(reference_is_packed(looked_up_ref) == 0);
+ cl_assert_equal_s(looked_up_ref->name, new_head_tracker);
+
+ /* ...peeled.. */
+ cl_git_pass(git_reference_resolve(&resolved_ref, looked_up_ref));
+ cl_assert(git_reference_type(resolved_ref) == GIT_REFERENCE_DIRECT);
+
+ /* ...and that it points to the current master tip */
+ cl_assert_equal_oid(&id, git_reference_target(resolved_ref));
+ git_reference_free(looked_up_ref);
+ git_reference_free(resolved_ref);
+
+ /* Similar test with a fresh new repository */
+ cl_git_pass(git_repository_open(&repo2, "testrepo"));
+
+ cl_git_pass(git_reference_lookup(&looked_up_ref, repo2, new_head_tracker));
+ cl_git_pass(git_reference_resolve(&resolved_ref, looked_up_ref));
+ cl_assert_equal_oid(&id, git_reference_target(resolved_ref));
+
+ git_repository_free(repo2);
+
+ git_reference_free(new_reference);
+ git_reference_free(looked_up_ref);
+ git_reference_free(resolved_ref);
+}
+
+void test_refs_create__symbolic_with_arbitrary_content(void)
+{
+ git_reference *new_reference, *looked_up_ref;
+ git_repository *repo2;
+ git_oid id;
+
+ const char *new_head_tracker = "ANOTHER_HEAD_TRACKER";
+ const char *arbitrary_target = "ARBITRARY DATA";
+
+ git_oid__fromstr(&id, current_master_tip, GIT_OID_SHA1);
+
+ /* Attempt to create symbolic ref with arbitrary data in target
+ * fails by default
+ */
+ cl_git_fail(git_reference_symbolic_create(&new_reference, g_repo, new_head_tracker, arbitrary_target, 0, NULL));
+
+ git_libgit2_opts(GIT_OPT_ENABLE_STRICT_SYMBOLIC_REF_CREATION, 0);
+
+ /* With strict target validation disabled, ref creation succeeds */
+ cl_git_pass(git_reference_symbolic_create(&new_reference, g_repo, new_head_tracker, arbitrary_target, 0, NULL));
+
+ /* Ensure the reference can be looked-up... */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, new_head_tracker));
+ cl_assert(git_reference_type(looked_up_ref) & GIT_REFERENCE_SYMBOLIC);
+ cl_assert(reference_is_packed(looked_up_ref) == 0);
+ cl_assert_equal_s(looked_up_ref->name, new_head_tracker);
+ git_reference_free(looked_up_ref);
+
+ /* Ensure the target is what we expect it to be */
+ cl_assert_equal_s(git_reference_symbolic_target(new_reference), arbitrary_target);
+
+ /* Similar test with a fresh new repository object */
+ cl_git_pass(git_repository_open(&repo2, "testrepo"));
+
+ /* Ensure the reference can be looked-up... */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, repo2, new_head_tracker));
+ cl_assert(git_reference_type(looked_up_ref) & GIT_REFERENCE_SYMBOLIC);
+ cl_assert(reference_is_packed(looked_up_ref) == 0);
+ cl_assert_equal_s(looked_up_ref->name, new_head_tracker);
+
+ /* Ensure the target is what we expect it to be */
+ cl_assert_equal_s(git_reference_symbolic_target(new_reference), arbitrary_target);
+
+ git_repository_free(repo2);
+ git_reference_free(new_reference);
+ git_reference_free(looked_up_ref);
+}
+
+void test_refs_create__deep_symbolic(void)
+{
+ /* create a deep symbolic reference */
+ git_reference *new_reference, *looked_up_ref, *resolved_ref;
+ git_oid id;
+
+ const char *new_head_tracker = "deep/rooted/tracker";
+
+ git_oid__fromstr(&id, current_master_tip, GIT_OID_SHA1);
+
+ cl_git_pass(git_reference_symbolic_create(&new_reference, g_repo, new_head_tracker, current_head_target, 0, NULL));
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, new_head_tracker));
+ cl_git_pass(git_reference_resolve(&resolved_ref, looked_up_ref));
+ cl_assert_equal_oid(&id, git_reference_target(resolved_ref));
+
+ git_reference_free(new_reference);
+ git_reference_free(looked_up_ref);
+ git_reference_free(resolved_ref);
+}
+
+void test_refs_create__oid(void)
+{
+ /* create a new OID reference */
+ git_reference *new_reference, *looked_up_ref;
+ git_repository *repo2;
+ git_oid id;
+
+ const char *new_head = "refs/heads/new-head";
+
+ git_oid__fromstr(&id, current_master_tip, GIT_OID_SHA1);
+
+ /* Create and write the new object id reference */
+ cl_git_pass(git_reference_create(&new_reference, g_repo, new_head, &id, 0, NULL));
+
+ /* Ensure the reference can be looked-up... */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, new_head));
+ cl_assert(git_reference_type(looked_up_ref) & GIT_REFERENCE_DIRECT);
+ cl_assert(reference_is_packed(looked_up_ref) == 0);
+ cl_assert_equal_s(looked_up_ref->name, new_head);
+
+ /* ...and that it points to the current master tip */
+ cl_assert_equal_oid(&id, git_reference_target(looked_up_ref));
+ git_reference_free(looked_up_ref);
+
+ /* Similar test with a fresh new repository */
+ cl_git_pass(git_repository_open(&repo2, "testrepo"));
+
+ cl_git_pass(git_reference_lookup(&looked_up_ref, repo2, new_head));
+ cl_assert_equal_oid(&id, git_reference_target(looked_up_ref));
+
+ git_repository_free(repo2);
+
+ git_reference_free(new_reference);
+ git_reference_free(looked_up_ref);
+}
+
+/* Can by default create a reference that targets at an unknown id */
+void test_refs_create__oid_unknown_succeeds_without_strict(void)
+{
+ git_reference *new_reference, *looked_up_ref;
+ git_oid id;
+
+ const char *new_head = "refs/heads/new-head";
+
+ git_oid__fromstr(&id, "deadbeef3f795b2b4353bcce3a527ad0a4f7f644", GIT_OID_SHA1);
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_STRICT_OBJECT_CREATION, 0));
+
+ /* Create and write the new object id reference */
+ cl_git_pass(git_reference_create(&new_reference, g_repo, new_head, &id, 0, NULL));
+ git_reference_free(new_reference);
+
+ /* Ensure the reference can't be looked-up... */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, new_head));
+ git_reference_free(looked_up_ref);
+}
+
+/* Strict object enforcement enforces valid object id */
+void test_refs_create__oid_unknown_fails_by_default(void)
+{
+ git_reference *new_reference, *looked_up_ref;
+ git_oid id;
+
+ const char *new_head = "refs/heads/new-head";
+
+ git_oid__fromstr(&id, "deadbeef3f795b2b4353bcce3a527ad0a4f7f644", GIT_OID_SHA1);
+
+ /* Create and write the new object id reference */
+ cl_git_fail(git_reference_create(&new_reference, g_repo, new_head, &id, 0, NULL));
+
+ /* Ensure the reference can't be looked-up... */
+ cl_git_fail(git_reference_lookup(&looked_up_ref, g_repo, new_head));
+}
+
+void test_refs_create__propagate_eexists(void)
+{
+ git_oid oid;
+
+ /* Make sure it works for oid and for symbolic both */
+ cl_git_pass(git_oid__fromstr(&oid, current_master_tip, GIT_OID_SHA1));
+ cl_git_fail_with(GIT_EEXISTS, git_reference_create(NULL, g_repo, current_head_target, &oid, false, NULL));
+ cl_git_fail_with(GIT_EEXISTS, git_reference_symbolic_create(NULL, g_repo, "HEAD", current_head_target, false, NULL));
+}
+
+void test_refs_create__existing_dir_propagates_edirectory(void)
+{
+ git_reference *new_reference, *fail_reference;
+ git_oid id;
+ const char *dir_head = "refs/heads/new-dir/new-head",
+ *fail_head = "refs/heads/new-dir";
+
+ git_oid__fromstr(&id, current_master_tip, GIT_OID_SHA1);
+
+ /* Create and write the new object id reference */
+ cl_git_pass(git_reference_create(&new_reference, g_repo, dir_head, &id, 1, NULL));
+ cl_git_fail_with(GIT_EDIRECTORY,
+ git_reference_create(&fail_reference, g_repo, fail_head, &id, false, NULL));
+
+ git_reference_free(new_reference);
+}
+
+static void test_invalid_name(const char *name)
+{
+ git_reference *new_reference;
+ git_oid id;
+
+ git_oid__fromstr(&id, current_master_tip, GIT_OID_SHA1);
+
+ cl_assert_equal_i(GIT_EINVALIDSPEC, git_reference_create(
+ &new_reference, g_repo, name, &id, 0, NULL));
+
+ cl_assert_equal_i(GIT_EINVALIDSPEC, git_reference_symbolic_create(
+ &new_reference, g_repo, name, current_head_target, 0, NULL));
+}
+
+void test_refs_create__creating_a_reference_with_an_invalid_name_returns_EINVALIDSPEC(void)
+{
+ test_invalid_name("refs/heads/inv@{id");
+ test_invalid_name("refs/heads/back\\slash");
+
+ test_invalid_name("refs/heads/foo ");
+ test_invalid_name("refs/heads/foo /bar");
+ test_invalid_name("refs/heads/com1:bar/foo");
+
+ test_invalid_name("refs/heads/e:");
+ test_invalid_name("refs/heads/c:/foo");
+
+ test_invalid_name("refs/heads/foo.");
+}
+
+static void test_win32_name(const char *name)
+{
+ git_reference *new_reference = NULL;
+ git_oid id;
+ int ret;
+
+ git_oid__fromstr(&id, current_master_tip, GIT_OID_SHA1);
+
+ ret = git_reference_create(&new_reference, g_repo, name, &id, 0, NULL);
+
+#ifdef GIT_WIN32
+ cl_assert_equal_i(GIT_EINVALIDSPEC, ret);
+#else
+ cl_git_pass(ret);
+#endif
+
+ git_reference_free(new_reference);
+}
+
+void test_refs_create__creating_a_loose_ref_with_invalid_windows_name(void)
+{
+ test_win32_name("refs/heads/foo./bar");
+
+ test_win32_name("refs/heads/aux");
+ test_win32_name("refs/heads/aux.foo/bar");
+
+ test_win32_name("refs/heads/com1");
+}
+
+/* Creating a loose ref involves fsync'ing the reference, the
+ * reflog and (on non-Windows) the containing directories.
+ * Creating a packed ref involves fsync'ing the packed ref file
+ * and (on non-Windows) the containing directory.
+ */
+#ifdef GIT_WIN32
+static int expected_fsyncs_create = 2, expected_fsyncs_compress = 1;
+#else
+static int expected_fsyncs_create = 4, expected_fsyncs_compress = 2;
+#endif
+
+static void count_fsyncs(size_t *create_count, size_t *compress_count)
+{
+ git_reference *ref = NULL;
+ git_refdb *refdb;
+ git_oid id;
+
+ p_fsync__cnt = 0;
+
+ git_oid__fromstr(&id, current_master_tip, GIT_OID_SHA1);
+ cl_git_pass(git_reference_create(&ref, g_repo, "refs/heads/fsync_test", &id, 0, "log message"));
+ git_reference_free(ref);
+
+ *create_count = p_fsync__cnt;
+ p_fsync__cnt = 0;
+
+ cl_git_pass(git_repository_refdb(&refdb, g_repo));
+ cl_git_pass(git_refdb_compress(refdb));
+ git_refdb_free(refdb);
+
+ *compress_count = p_fsync__cnt;
+ p_fsync__cnt = 0;
+}
+
+void test_refs_create__does_not_fsync_by_default(void)
+{
+ size_t create_count, compress_count;
+ count_fsyncs(&create_count, &compress_count);
+
+ cl_assert_equal_i(0, create_count);
+ cl_assert_equal_i(0, compress_count);
+}
+
+void test_refs_create__fsyncs_when_global_opt_set(void)
+{
+ size_t create_count, compress_count;
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_ENABLE_FSYNC_GITDIR, 1));
+ count_fsyncs(&create_count, &compress_count);
+
+ cl_assert_equal_i(expected_fsyncs_create, create_count);
+ cl_assert_equal_i(expected_fsyncs_compress, compress_count);
+}
+
+void test_refs_create__fsyncs_when_repo_config_set(void)
+{
+ size_t create_count, compress_count;
+
+ cl_repo_set_bool(g_repo, "core.fsyncObjectFiles", true);
+
+ count_fsyncs(&create_count, &compress_count);
+
+ cl_assert_equal_i(expected_fsyncs_create, create_count);
+ cl_assert_equal_i(expected_fsyncs_compress, compress_count);
+}
diff --git a/tests/libgit2/refs/delete.c b/tests/libgit2/refs/delete.c
new file mode 100644
index 0000000..edb521f
--- /dev/null
+++ b/tests/libgit2/refs/delete.c
@@ -0,0 +1,118 @@
+#include "clar_libgit2.h"
+
+#include "futils.h"
+#include "git2/reflog.h"
+#include "git2/refdb.h"
+#include "reflog.h"
+#include "ref_helpers.h"
+
+static const char *packed_test_head_name = "refs/heads/packed-test";
+static const char *current_master_tip = "a65fedf39aefe402d3bb6e24df4d4f5fe4547750";
+
+static git_repository *g_repo;
+
+
+
+void test_refs_delete__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_refs_delete__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+
+
+void test_refs_delete__packed_loose(void)
+{
+ /* deleting a ref which is both packed and loose should remove both tracks in the filesystem */
+ git_reference *looked_up_ref, *another_looked_up_ref;
+ git_str temp_path = GIT_STR_INIT;
+
+ /* Ensure the loose reference exists on the file system */
+ cl_git_pass(git_str_joinpath(&temp_path, git_repository_path(g_repo), packed_test_head_name));
+ cl_assert(git_fs_path_exists(temp_path.ptr));
+
+ /* Lookup the reference */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_test_head_name));
+
+ /* Ensure it's the loose version that has been found */
+ cl_assert(reference_is_packed(looked_up_ref) == 0);
+
+ /* Now that the reference is deleted... */
+ cl_git_pass(git_reference_delete(looked_up_ref));
+ git_reference_free(looked_up_ref);
+
+ /* Looking up the reference once again should not retrieve it */
+ cl_git_fail(git_reference_lookup(&another_looked_up_ref, g_repo, packed_test_head_name));
+
+ /* Ensure the loose reference doesn't exist any longer on the file system */
+ cl_assert(!git_fs_path_exists(temp_path.ptr));
+
+ git_reference_free(another_looked_up_ref);
+ git_str_dispose(&temp_path);
+}
+
+void test_refs_delete__packed_only(void)
+{
+ /* can delete a just packed reference */
+ git_reference *ref;
+ git_refdb *refdb;
+ git_oid id;
+ const char *new_ref = "refs/heads/new_ref";
+
+ git_oid__fromstr(&id, current_master_tip, GIT_OID_SHA1);
+
+ /* Create and write the new object id reference */
+ cl_git_pass(git_reference_create(&ref, g_repo, new_ref, &id, 0, NULL));
+ git_reference_free(ref);
+
+ /* Lookup the reference */
+ cl_git_pass(git_reference_lookup(&ref, g_repo, new_ref));
+
+ /* Ensure it's a loose reference */
+ cl_assert(reference_is_packed(ref) == 0);
+
+ /* Pack all existing references */
+ cl_git_pass(git_repository_refdb(&refdb, g_repo));
+ cl_git_pass(git_refdb_compress(refdb));
+
+ /* Reload the reference from disk */
+ git_reference_free(ref);
+ cl_git_pass(git_reference_lookup(&ref, g_repo, new_ref));
+
+ /* Ensure it's a packed reference */
+ cl_assert(reference_is_packed(ref) == 1);
+
+ /* This should pass */
+ cl_git_pass(git_reference_delete(ref));
+ git_reference_free(ref);
+ git_refdb_free(refdb);
+}
+
+void test_refs_delete__remove(void)
+{
+ git_reference *ref;
+
+ /* Check that passing no old values lets us delete */
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, packed_test_head_name));
+ git_reference_free(ref);
+
+ cl_git_pass(git_reference_remove(g_repo, packed_test_head_name));
+
+ cl_git_fail(git_reference_lookup(&ref, g_repo, packed_test_head_name));
+}
+
+void test_refs_delete__head(void)
+{
+ git_reference *ref;
+
+ /* Check that it is not possible to delete HEAD */
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, "HEAD"));
+ cl_git_fail(git_reference_delete(ref));
+ git_reference_free(ref);
+}
diff --git a/tests/libgit2/refs/dup.c b/tests/libgit2/refs/dup.c
new file mode 100644
index 0000000..8a89cd9
--- /dev/null
+++ b/tests/libgit2/refs/dup.c
@@ -0,0 +1,42 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+
+static git_repository *g_repo;
+
+void test_refs_dup__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo.git");
+}
+
+void test_refs_dup__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_dup__direct(void)
+{
+ git_reference *a, *b;
+
+ cl_git_pass(git_reference_lookup(&a, g_repo, "refs/heads/master"));
+ cl_git_pass(git_reference_dup(&b, a));
+
+ cl_assert(git_reference_cmp(a, b) == 0);
+ cl_assert(git_reference_owner(b) == g_repo);
+
+ git_reference_free(b);
+ git_reference_free(a);
+}
+
+void test_refs_dup__symbolic(void)
+{
+ git_reference *a, *b;
+
+ cl_git_pass(git_reference_lookup(&a, g_repo, "HEAD"));
+ cl_git_pass(git_reference_dup(&b, a));
+
+ cl_assert(git_reference_cmp(a, b) == 0);
+ cl_assert(git_reference_owner(b) == g_repo);
+
+ git_reference_free(b);
+ git_reference_free(a);
+}
diff --git a/tests/libgit2/refs/foreachglob.c b/tests/libgit2/refs/foreachglob.c
new file mode 100644
index 0000000..a586ca0
--- /dev/null
+++ b/tests/libgit2/refs/foreachglob.c
@@ -0,0 +1,100 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+
+static git_repository *repo;
+static git_reference *fake_remote;
+
+void test_refs_foreachglob__initialize(void)
+{
+ git_oid id;
+
+ cl_fixture_sandbox("testrepo.git");
+ cl_git_pass(git_repository_open(&repo, "testrepo.git"));
+
+ cl_git_pass(git_oid__fromstr(&id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644", GIT_OID_SHA1));
+ cl_git_pass(git_reference_create(&fake_remote, repo, "refs/remotes/nulltoken/master", &id, 0, NULL));
+}
+
+void test_refs_foreachglob__cleanup(void)
+{
+ git_reference_free(fake_remote);
+ fake_remote = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+
+ cl_fixture_cleanup("testrepo.git");
+}
+
+static int count_cb(const char *reference_name, void *payload)
+{
+ int *count = (int *)payload;
+
+ GIT_UNUSED(reference_name);
+
+ (*count)++;
+
+ return 0;
+}
+
+static void assert_retrieval(const char *glob, int expected_count)
+{
+ int count = 0;
+
+ cl_git_pass(git_reference_foreach_glob(repo, glob, count_cb, &count));
+
+ cl_assert_equal_i(expected_count, count);
+}
+
+void test_refs_foreachglob__retrieve_all_refs(void)
+{
+ /* 13 heads (including one packed head) + 1 note + 2 remotes + 7 tags + 1 blob */
+ assert_retrieval("*", 24);
+}
+
+void test_refs_foreachglob__retrieve_remote_branches(void)
+{
+ assert_retrieval("refs/remotes/*", 2);
+}
+
+void test_refs_foreachglob__retrieve_local_branches(void)
+{
+ assert_retrieval("refs/heads/*", 13);
+}
+
+void test_refs_foreachglob__retrieve_nonexistant(void)
+{
+ assert_retrieval("refs/nonexistent/*", 0);
+}
+
+void test_refs_foreachglob__retrieve_partially_named_references(void)
+{
+ /*
+ * refs/heads/packed-test, refs/heads/test
+ * refs/remotes/test/master, refs/tags/test
+ */
+
+ assert_retrieval("*test*", 4);
+}
+
+
+static int interrupt_cb(const char *reference_name, void *payload)
+{
+ int *count = (int *)payload;
+
+ GIT_UNUSED(reference_name);
+
+ (*count)++;
+
+ return (*count == 11) ? -1000 : 0;
+}
+
+void test_refs_foreachglob__can_cancel(void)
+{
+ int count = 0;
+
+ cl_assert_equal_i(-1000, git_reference_foreach_glob(
+ repo, "*", interrupt_cb, &count) );
+
+ cl_assert_equal_i(11, count);
+}
diff --git a/tests/libgit2/refs/isvalidname.c b/tests/libgit2/refs/isvalidname.c
new file mode 100644
index 0000000..063f0f7
--- /dev/null
+++ b/tests/libgit2/refs/isvalidname.c
@@ -0,0 +1,38 @@
+#include "clar_libgit2.h"
+
+static bool is_valid_name(const char *name)
+{
+ int valid;
+ cl_git_pass(git_reference_name_is_valid(&valid, name));
+ return valid;
+}
+
+void test_refs_isvalidname__can_detect_invalid_formats(void)
+{
+ cl_assert_equal_i(false, is_valid_name("refs/tags/0.17.0^{}"));
+ cl_assert_equal_i(false, is_valid_name("TWO/LEVELS"));
+ cl_assert_equal_i(false, is_valid_name("ONE.LEVEL"));
+ cl_assert_equal_i(false, is_valid_name("HEAD/"));
+ cl_assert_equal_i(false, is_valid_name("NO_TRAILING_UNDERSCORE_"));
+ cl_assert_equal_i(false, is_valid_name("_NO_LEADING_UNDERSCORE"));
+ cl_assert_equal_i(false, is_valid_name("HEAD/aa"));
+ cl_assert_equal_i(false, is_valid_name("lower_case"));
+ cl_assert_equal_i(false, is_valid_name("/stupid/name/master"));
+ cl_assert_equal_i(false, is_valid_name("/"));
+ cl_assert_equal_i(false, is_valid_name("//"));
+ cl_assert_equal_i(false, is_valid_name(""));
+ cl_assert_equal_i(false, is_valid_name("refs/heads/sub.lock/webmatrix"));
+}
+
+void test_refs_isvalidname__wont_hopefully_choke_on_valid_formats(void)
+{
+ cl_assert_equal_i(true, is_valid_name("refs/tags/0.17.0"));
+ cl_assert_equal_i(true, is_valid_name("refs/LEVELS"));
+ cl_assert_equal_i(true, is_valid_name("HEAD"));
+ cl_assert_equal_i(true, is_valid_name("ONE_LEVEL"));
+ cl_assert_equal_i(true, is_valid_name("refs/stash"));
+ cl_assert_equal_i(true, is_valid_name("refs/remotes/origin/bim_with_3d@11296"));
+ cl_assert_equal_i(true, is_valid_name("refs/master{yesterday"));
+ cl_assert_equal_i(true, is_valid_name("refs/master}yesterday"));
+ cl_assert_equal_i(true, is_valid_name("refs/master{yesterday}"));
+}
diff --git a/tests/libgit2/refs/iterator.c b/tests/libgit2/refs/iterator.c
new file mode 100644
index 0000000..706fd1e
--- /dev/null
+++ b/tests/libgit2/refs/iterator.c
@@ -0,0 +1,276 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+#include "vector.h"
+#include "odb.h"
+#include "repository.h"
+
+static git_repository *repo;
+
+void test_refs_iterator__initialize(void)
+{
+ repo = cl_git_sandbox_init("testrepo.git");
+}
+
+void test_refs_iterator__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static const char *refnames[] = {
+ "refs/blobs/annotated_tag_to_blob",
+ "refs/heads/br2",
+ "refs/heads/cannot-fetch",
+ "refs/heads/chomped",
+ "refs/heads/haacked",
+ "refs/heads/master",
+ "refs/heads/not-good",
+ "refs/heads/packed",
+ "refs/heads/packed-test",
+ "refs/heads/subtrees",
+ "refs/heads/test",
+ "refs/heads/track-local",
+ "refs/heads/trailing",
+ "refs/heads/with-empty-log",
+ "refs/notes/fanout",
+ "refs/remotes/test/master",
+ "refs/tags/annotated_tag_to_blob",
+ "refs/tags/e90810b",
+ "refs/tags/hard_tag",
+ "refs/tags/point_to_blob",
+ "refs/tags/taggerless",
+ "refs/tags/test",
+ "refs/tags/wrapped_tag",
+ NULL
+};
+
+static const char *refnames_with_symlink[] = {
+ "refs/blobs/annotated_tag_to_blob",
+ "refs/heads/br2",
+ "refs/heads/cannot-fetch",
+ "refs/heads/chomped",
+ "refs/heads/haacked",
+ "refs/heads/link/a",
+ "refs/heads/link/b",
+ "refs/heads/link/c",
+ "refs/heads/link/d",
+ "refs/heads/master",
+ "refs/heads/not-good",
+ "refs/heads/packed",
+ "refs/heads/packed-test",
+ "refs/heads/subtrees",
+ "refs/heads/test",
+ "refs/heads/track-local",
+ "refs/heads/trailing",
+ "refs/heads/with-empty-log",
+ "refs/notes/fanout",
+ "refs/remotes/test/master",
+ "refs/tags/annotated_tag_to_blob",
+ "refs/tags/e90810b",
+ "refs/tags/hard_tag",
+ "refs/tags/point_to_blob",
+ "refs/tags/taggerless",
+ "refs/tags/test",
+ "refs/tags/wrapped_tag",
+ NULL
+};
+
+static int refcmp_cb(const void *a, const void *b)
+{
+ const git_reference *refa = (const git_reference *)a;
+ const git_reference *refb = (const git_reference *)b;
+
+ return strcmp(refa->name, refb->name);
+}
+
+static void assert_all_refnames_match(const char **expected, git_vector *names)
+{
+ size_t i;
+ git_reference *ref;
+
+ git_vector_sort(names);
+
+ git_vector_foreach(names, i, ref) {
+ cl_assert(expected[i] != NULL);
+ cl_assert_equal_s(expected[i], ref->name);
+ git_reference_free(ref);
+ }
+ cl_assert(expected[i] == NULL);
+
+ git_vector_free(names);
+}
+
+void test_refs_iterator__list(void)
+{
+ git_reference_iterator *iter;
+ git_vector output;
+ git_reference *ref;
+
+ cl_git_pass(git_vector_init(&output, 33, &refcmp_cb));
+ cl_git_pass(git_reference_iterator_new(&iter, repo));
+
+ while (1) {
+ int error = git_reference_next(&ref, iter);
+ if (error == GIT_ITEROVER)
+ break;
+ cl_git_pass(error);
+ cl_git_pass(git_vector_insert(&output, ref));
+ }
+
+ git_reference_iterator_free(iter);
+
+ assert_all_refnames_match(refnames, &output);
+}
+
+void test_refs_iterator__empty(void)
+{
+ git_reference_iterator *iter;
+ git_odb *odb;
+ git_reference *ref;
+ git_repository *empty;
+
+ cl_git_pass(git_odb__new(&odb, NULL));
+ cl_git_pass(git_repository__wrap_odb(&empty, odb, GIT_OID_SHA1));
+
+ cl_git_pass(git_reference_iterator_new(&iter, empty));
+ cl_assert_equal_i(GIT_ITEROVER, git_reference_next(&ref, iter));
+
+ git_reference_iterator_free(iter);
+ git_odb_free(odb);
+ git_repository_free(empty);
+}
+
+static int refs_foreach_cb(git_reference *reference, void *payload)
+{
+ git_vector *output = payload;
+ cl_git_pass(git_vector_insert(output, reference));
+ return 0;
+}
+
+void test_refs_iterator__foreach(void)
+{
+ git_vector output;
+ cl_git_pass(git_vector_init(&output, 33, &refcmp_cb));
+ cl_git_pass(git_reference_foreach(repo, refs_foreach_cb, &output));
+ assert_all_refnames_match(refnames, &output);
+}
+
+void test_refs_iterator__foreach_through_symlink(void)
+{
+ git_vector output;
+
+#ifdef GIT_WIN32
+ cl_skip();
+#endif
+
+ cl_git_pass(git_vector_init(&output, 32, &refcmp_cb));
+
+ cl_git_pass(p_mkdir("refs", 0777));
+ cl_git_mkfile("refs/a", "1234567890123456789012345678901234567890");
+ cl_git_mkfile("refs/b", "1234567890123456789012345678901234567890");
+ cl_git_mkfile("refs/c", "1234567890123456789012345678901234567890");
+ cl_git_mkfile("refs/d", "1234567890123456789012345678901234567890");
+
+ cl_git_pass(p_symlink("../../../refs", "testrepo.git/refs/heads/link"));
+
+ cl_git_pass(git_reference_foreach(repo, refs_foreach_cb, &output));
+ assert_all_refnames_match(refnames_with_symlink, &output);
+}
+
+static int refs_foreach_cancel_cb(git_reference *reference, void *payload)
+{
+ int *cancel_after = payload;
+
+ git_reference_free(reference);
+
+ if (!*cancel_after)
+ return -333;
+ (*cancel_after)--;
+ return 0;
+}
+
+void test_refs_iterator__foreach_can_cancel(void)
+{
+ int cancel_after = 3;
+ cl_git_fail_with(
+ git_reference_foreach(repo, refs_foreach_cancel_cb, &cancel_after),
+ -333);
+ cl_assert_equal_i(0, cancel_after);
+}
+
+static int refs_foreach_name_cb(const char *name, void *payload)
+{
+ git_vector *output = payload;
+ cl_git_pass(git_vector_insert(output, git__strdup(name)));
+ return 0;
+}
+
+void test_refs_iterator__foreach_name(void)
+{
+ git_vector output;
+ size_t i;
+ char *name;
+
+ cl_git_pass(git_vector_init(&output, 32, &git__strcmp_cb));
+ cl_git_pass(
+ git_reference_foreach_name(repo, refs_foreach_name_cb, &output));
+
+ git_vector_sort(&output);
+
+ git_vector_foreach(&output, i, name) {
+ cl_assert(refnames[i] != NULL);
+ cl_assert_equal_s(refnames[i], name);
+ git__free(name);
+ }
+
+ git_vector_free(&output);
+}
+
+static int refs_foreach_name_cancel_cb(const char *name, void *payload)
+{
+ int *cancel_after = payload;
+ if (!*cancel_after)
+ return -333;
+ GIT_UNUSED(name);
+ (*cancel_after)--;
+ return 0;
+}
+
+void test_refs_iterator__foreach_name_can_cancel(void)
+{
+ int cancel_after = 5;
+ cl_git_fail_with(
+ git_reference_foreach_name(
+ repo, refs_foreach_name_cancel_cb, &cancel_after),
+ -333);
+ cl_assert_equal_i(0, cancel_after);
+}
+
+void test_refs_iterator__concurrent_delete(void)
+{
+ git_reference_iterator *iter;
+ size_t full_count = 0, concurrent_count = 0;
+ const char *name;
+ int error;
+
+ cl_git_sandbox_cleanup();
+ repo = cl_git_sandbox_init("testrepo");
+
+ cl_git_pass(git_reference_iterator_new(&iter, repo));
+ while ((error = git_reference_next_name(&name, iter)) == 0) {
+ full_count++;
+ }
+
+ git_reference_iterator_free(iter);
+ cl_assert_equal_i(GIT_ITEROVER, error);
+
+ cl_git_pass(git_reference_iterator_new(&iter, repo));
+ while ((error = git_reference_next_name(&name, iter)) == 0) {
+ cl_git_pass(git_reference_remove(repo, name));
+ concurrent_count++;
+ }
+
+ git_reference_iterator_free(iter);
+ cl_assert_equal_i(GIT_ITEROVER, error);
+
+ cl_assert_equal_i(full_count, concurrent_count);
+}
diff --git a/tests/libgit2/refs/list.c b/tests/libgit2/refs/list.c
new file mode 100644
index 0000000..8085ff8
--- /dev/null
+++ b/tests/libgit2/refs/list.c
@@ -0,0 +1,57 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+#include "git2/reflog.h"
+#include "reflog.h"
+
+static git_repository *g_repo;
+
+
+
+void test_refs_list__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_refs_list__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+
+
+void test_refs_list__all(void)
+{
+ /* try to list all the references in our test repo */
+ git_strarray ref_list;
+
+ cl_git_pass(git_reference_list(&ref_list, g_repo));
+
+ /*{
+ unsigned short i;
+ for (i = 0; i < ref_list.count; ++i)
+ printf("# %s\n", ref_list.strings[i]);
+ }*/
+
+ /* We have exactly 12 refs in total if we include the packed ones:
+ * there is a reference that exists both in the packfile and as
+ * loose, but we only list it once */
+ cl_assert_equal_i((int)ref_list.count, 19);
+
+ git_strarray_dispose(&ref_list);
+}
+
+void test_refs_list__do_not_retrieve_references_which_name_end_with_a_lock_extension(void)
+{
+ git_strarray ref_list;
+
+ /* Create a fake locked reference */
+ cl_git_mkfile(
+ "./testrepo/.git/refs/heads/hanwen.lock",
+ "144344043ba4d4a405da03de3844aa829ae8be0e\n");
+
+ cl_git_pass(git_reference_list(&ref_list, g_repo));
+ cl_assert_equal_i((int)ref_list.count, 19);
+
+ git_strarray_dispose(&ref_list);
+}
diff --git a/tests/libgit2/refs/listall.c b/tests/libgit2/refs/listall.c
new file mode 100644
index 0000000..9da8d1a
--- /dev/null
+++ b/tests/libgit2/refs/listall.c
@@ -0,0 +1,47 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+
+static git_repository *repo;
+static git_strarray ref_list;
+
+static void ensure_no_refname_starts_with_a_forward_slash(const char *path)
+{
+ size_t i;
+
+ cl_git_pass(git_repository_open(&repo, path));
+ cl_git_pass(git_reference_list(&ref_list, repo));
+
+ cl_assert(ref_list.count > 0);
+
+ for (i = 0; i < ref_list.count; i++)
+ cl_assert(git__prefixcmp(ref_list.strings[i], "/") != 0);
+
+ git_strarray_dispose(&ref_list);
+ git_repository_free(repo);
+}
+
+void test_refs_listall__from_repository_opened_through_workdir_path(void)
+{
+ cl_fixture_sandbox("status");
+ cl_git_pass(p_rename("status/.gitted", "status/.git"));
+
+ ensure_no_refname_starts_with_a_forward_slash("status");
+
+ cl_fixture_cleanup("status");
+}
+
+void test_refs_listall__from_repository_opened_through_gitdir_path(void)
+{
+ ensure_no_refname_starts_with_a_forward_slash(cl_fixture("testrepo.git"));
+}
+
+void test_refs_listall__from_repository_with_no_trailing_newline(void)
+{
+ cl_git_pass(git_repository_open(&repo, cl_fixture("bad_tag.git")));
+ cl_git_pass(git_reference_list(&ref_list, repo));
+
+ cl_assert(ref_list.count > 0);
+
+ git_strarray_dispose(&ref_list);
+ git_repository_free(repo);
+}
diff --git a/tests/libgit2/refs/lookup.c b/tests/libgit2/refs/lookup.c
new file mode 100644
index 0000000..2b8be77
--- /dev/null
+++ b/tests/libgit2/refs/lookup.c
@@ -0,0 +1,68 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+
+static git_repository *g_repo;
+
+void test_refs_lookup__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo.git");
+}
+
+void test_refs_lookup__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_lookup__with_resolve(void)
+{
+ git_reference *a, *b, *temp;
+
+ cl_git_pass(git_reference_lookup(&temp, g_repo, "HEAD"));
+ cl_git_pass(git_reference_resolve(&a, temp));
+ git_reference_free(temp);
+
+ cl_git_pass(git_reference_lookup_resolved(&b, g_repo, "HEAD", 5));
+ cl_assert(git_reference_cmp(a, b) == 0);
+ git_reference_free(b);
+
+ cl_git_pass(git_reference_lookup_resolved(&b, g_repo, "HEAD_TRACKER", 5));
+ cl_assert(git_reference_cmp(a, b) == 0);
+ git_reference_free(b);
+
+ git_reference_free(a);
+}
+
+void test_refs_lookup__invalid_name(void)
+{
+ git_oid oid;
+ cl_git_fail(git_reference_name_to_id(&oid, g_repo, "/refs/tags/point_to_blob"));
+}
+
+void test_refs_lookup__oid(void)
+{
+ git_oid tag, expected;
+
+ cl_git_pass(git_reference_name_to_id(&tag, g_repo, "refs/tags/point_to_blob"));
+ cl_git_pass(git_oid__fromstr(&expected, "1385f264afb75a56a5bec74243be9b367ba4ca08", GIT_OID_SHA1));
+ cl_assert_equal_oid(&expected, &tag);
+}
+
+void test_refs_lookup__namespace(void)
+{
+ int error;
+ git_reference *ref;
+
+ error = git_reference_lookup(&ref, g_repo, "refs/heads");
+ cl_assert_equal_i(error, GIT_ENOTFOUND);
+
+ error = git_reference_lookup(&ref, g_repo, "refs/heads/");
+ cl_assert_equal_i(error, GIT_EINVALIDSPEC);
+}
+
+void test_refs_lookup__dwim_notfound(void)
+{
+ git_reference *ref;
+
+ cl_git_fail_with(GIT_ENOTFOUND, git_reference_dwim(&ref, g_repo, "idontexist"));
+ cl_assert_equal_s("no reference found for shorthand 'idontexist'", git_error_last()->message);
+}
diff --git a/tests/libgit2/refs/namespaces.c b/tests/libgit2/refs/namespaces.c
new file mode 100644
index 0000000..19456b5
--- /dev/null
+++ b/tests/libgit2/refs/namespaces.c
@@ -0,0 +1,36 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+
+static git_repository *g_repo;
+
+void test_refs_namespaces__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_refs_namespaces__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_namespaces__get_and_set(void)
+{
+ cl_assert_equal_s(NULL, git_repository_get_namespace(g_repo));
+
+ cl_git_pass(git_repository_set_namespace(g_repo, "namespace"));
+ cl_assert_equal_s("namespace", git_repository_get_namespace(g_repo));
+
+ cl_git_pass(git_repository_set_namespace(g_repo, NULL));
+ cl_assert_equal_s(NULL, git_repository_get_namespace(g_repo));
+}
+
+void test_refs_namespaces__namespace_doesnt_show_normal_refs(void)
+{
+ static git_strarray ref_list;
+
+ cl_git_pass(git_repository_set_namespace(g_repo, "namespace"));
+ cl_git_pass(git_reference_list(&ref_list, g_repo));
+ cl_assert_equal_i(0, ref_list.count);
+ git_strarray_dispose(&ref_list);
+}
diff --git a/tests/libgit2/refs/normalize.c b/tests/libgit2/refs/normalize.c
new file mode 100644
index 0000000..ff81500
--- /dev/null
+++ b/tests/libgit2/refs/normalize.c
@@ -0,0 +1,401 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+#include "git2/reflog.h"
+#include "reflog.h"
+
+/* Helpers */
+static void ensure_refname_normalized(
+ unsigned int flags,
+ const char *input_refname,
+ const char *expected_refname)
+{
+ char buffer_out[GIT_REFNAME_MAX];
+
+ cl_git_pass(git_reference_normalize_name(buffer_out, sizeof(buffer_out), input_refname, flags));
+
+ cl_assert_equal_s(expected_refname, buffer_out);
+}
+
+static void ensure_refname_invalid(unsigned int flags, const char *input_refname)
+{
+ char buffer_out[GIT_REFNAME_MAX];
+
+ cl_assert_equal_i(
+ GIT_EINVALIDSPEC,
+ git_reference_normalize_name(buffer_out, sizeof(buffer_out), input_refname, flags));
+}
+
+void test_refs_normalize__can_normalize_a_direct_reference_name(void)
+{
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_NORMAL, "refs/dummy/a", "refs/dummy/a");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_NORMAL, "refs/stash", "refs/stash");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_NORMAL, "refs/tags/a", "refs/tags/a");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_NORMAL, "refs/heads/a/b", "refs/heads/a/b");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_NORMAL, "refs/heads/a./b", "refs/heads/a./b");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_NORMAL, "refs/heads/v@ation", "refs/heads/v@ation");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_NORMAL, "refs///heads///a", "refs/heads/a");
+}
+
+void test_refs_normalize__cannot_normalize_any_direct_reference_name(void)
+{
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_NORMAL, "a");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_NORMAL, "/a");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_NORMAL, "//a");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_NORMAL, "");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_NORMAL, "/refs/heads/a/");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_NORMAL, "refs/heads/a/");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_NORMAL, "refs/heads/a.");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_NORMAL, "refs/heads/a.lock");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_NORMAL, "refs/heads/foo?bar");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_NORMAL, "refs/heads\foo");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_NORMAL, "refs/heads/v@ation", "refs/heads/v@ation");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_NORMAL, "refs///heads///a", "refs/heads/a");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_NORMAL, "refs/heads/.a/b");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_NORMAL, "refs/heads/foo/../bar");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_NORMAL, "refs/heads/foo..bar");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_NORMAL, "refs/heads/./foo");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_NORMAL, "refs/heads/v@{ation");
+}
+
+void test_refs_normalize__symbolic(void)
+{
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "heads\foo");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "/");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "///");
+
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "ALL_CAPS_AND_UNDERSCORES", "ALL_CAPS_AND_UNDERSCORES");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/MixedCasing", "refs/MixedCasing");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs///heads///a", "refs/heads/a");
+
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "HEAD", "HEAD");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "MERGE_HEAD", "MERGE_HEAD");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "FETCH_HEAD", "FETCH_HEAD");
+}
+
+/* Ported from JGit, BSD licence.
+ * See https://github.com/spearce/JGit/commit/e4bf8f6957bbb29362575d641d1e77a02d906739
+ *
+ * Copyright (C) 2009, Google Inc.
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Git Development Community nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+void test_refs_normalize__jgit_suite(void)
+{
+ /* tests borrowed from JGit */
+
+/* EmptyString */
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "/");
+
+/* MustHaveTwoComponents */
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_NORMAL, "master");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_NORMAL, "heads/master", "heads/master");
+
+/* ValidHead */
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/master", "refs/heads/master");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/pu", "refs/heads/pu");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/z", "refs/heads/z");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/FoO", "refs/heads/FoO");
+
+/* ValidTag */
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/tags/v1.0", "refs/tags/v1.0");
+
+/* NoLockSuffix */
+ ensure_refname_invalid(GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/master.lock");
+
+/* NoDirectorySuffix */
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/master/");
+
+/* NoSpace */
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/i haz space");
+
+/* NoAsciiControlCharacters */
+ {
+ char c;
+ char buffer[GIT_REFNAME_MAX];
+ for (c = '\1'; c < ' '; c++) {
+ p_snprintf(buffer, sizeof(buffer), "refs/heads/mast%cer", c);
+ ensure_refname_invalid(GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, buffer);
+ }
+ }
+
+/* NoBareDot */
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/.");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/..");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/./master");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/../master");
+
+/* NoLeadingOrTrailingDot */
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, ".");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/.bar");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/..bar");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/bar.");
+
+/* ContainsDot */
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/m.a.s.t.e.r", "refs/heads/m.a.s.t.e.r");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/master..pu");
+
+/* NoMagicRefCharacters */
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/master^");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/^master");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "^refs/heads/master");
+
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/master~");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/~master");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "~refs/heads/master");
+
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/master:");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/:master");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, ":refs/heads/master");
+
+/* ShellGlob */
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/master?");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/?master");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "?refs/heads/master");
+
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/master[");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/[master");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "[refs/heads/master");
+
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/master*");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/*master");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "*refs/heads/master");
+
+/* ValidSpecialCharacters */
+ ensure_refname_normalized
+ (GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/!", "refs/heads/!");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/\"", "refs/heads/\"");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/#", "refs/heads/#");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/$", "refs/heads/$");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/%", "refs/heads/%");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/&", "refs/heads/&");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/'", "refs/heads/'");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/(", "refs/heads/(");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/)", "refs/heads/)");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/+", "refs/heads/+");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/,", "refs/heads/,");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/-", "refs/heads/-");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/;", "refs/heads/;");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/<", "refs/heads/<");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/=", "refs/heads/=");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/>", "refs/heads/>");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/@", "refs/heads/@");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/]", "refs/heads/]");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/_", "refs/heads/_");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/`", "refs/heads/`");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/{", "refs/heads/{");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/|", "refs/heads/|");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/}", "refs/heads/}");
+
+ /*
+ * This is valid on UNIX, but not on Windows
+ * hence we make in invalid due to non-portability
+ */
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/\\");
+
+/* UnicodeNames */
+ /*
+ * Currently this fails.
+ * ensure_refname_normalized(GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/\u00e5ngstr\u00f6m", "refs/heads/\u00e5ngstr\u00f6m");
+ */
+
+/* RefLogQueryIsValidRef */
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/master@{1}");
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL, "refs/heads/master@{1.hour.ago}");
+}
+
+void test_refs_normalize__buffer_has_to_be_big_enough_to_hold_the_normalized_version(void)
+{
+ char buffer_out[21];
+
+ cl_git_pass(git_reference_normalize_name(
+ buffer_out, 21, "refs//heads///long///name", GIT_REFERENCE_FORMAT_NORMAL));
+ cl_git_fail(git_reference_normalize_name(
+ buffer_out, 20, "refs//heads///long///name", GIT_REFERENCE_FORMAT_NORMAL));
+}
+
+#define ONE_LEVEL_AND_REFSPEC \
+ GIT_REFERENCE_FORMAT_ALLOW_ONELEVEL \
+ | GIT_REFERENCE_FORMAT_REFSPEC_PATTERN
+
+void test_refs_normalize__refspec_pattern(void)
+{
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_REFSPEC_PATTERN, "heads/*foo/bar", "heads/*foo/bar");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_REFSPEC_PATTERN, "heads/foo*/bar", "heads/foo*/bar");
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_REFSPEC_PATTERN, "heads/f*o/bar", "heads/f*o/bar");
+
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_REFSPEC_PATTERN, "foo");
+ ensure_refname_normalized(
+ ONE_LEVEL_AND_REFSPEC, "FOO", "FOO");
+
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_REFSPEC_PATTERN, "foo/bar", "foo/bar");
+ ensure_refname_normalized(
+ ONE_LEVEL_AND_REFSPEC, "foo/bar", "foo/bar");
+
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_REFSPEC_PATTERN, "*/foo", "*/foo");
+ ensure_refname_normalized(
+ ONE_LEVEL_AND_REFSPEC, "*/foo", "*/foo");
+
+ ensure_refname_normalized(
+ GIT_REFERENCE_FORMAT_REFSPEC_PATTERN, "foo/*/bar", "foo/*/bar");
+ ensure_refname_normalized(
+ ONE_LEVEL_AND_REFSPEC, "foo/*/bar", "foo/*/bar");
+
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_REFSPEC_PATTERN, "*");
+ ensure_refname_normalized(
+ ONE_LEVEL_AND_REFSPEC, "*", "*");
+
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_REFSPEC_PATTERN, "foo/*/*");
+ ensure_refname_invalid(
+ ONE_LEVEL_AND_REFSPEC, "foo/*/*");
+
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_REFSPEC_PATTERN, "*/foo/*");
+ ensure_refname_invalid(
+ ONE_LEVEL_AND_REFSPEC, "*/foo/*");
+
+ ensure_refname_invalid(
+ GIT_REFERENCE_FORMAT_REFSPEC_PATTERN, "*/*/foo");
+ ensure_refname_invalid(
+ ONE_LEVEL_AND_REFSPEC, "*/*/foo");
+}
diff --git a/tests/libgit2/refs/overwrite.c b/tests/libgit2/refs/overwrite.c
new file mode 100644
index 0000000..1826b12
--- /dev/null
+++ b/tests/libgit2/refs/overwrite.c
@@ -0,0 +1,136 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+#include "git2/reflog.h"
+#include "reflog.h"
+
+static const char *ref_name = "refs/heads/other";
+static const char *ref_master_name = "refs/heads/master";
+static const char *ref_branch_name = "refs/heads/branch";
+static const char *ref_test_name = "refs/heads/test";
+
+static git_repository *g_repo;
+
+void test_refs_overwrite__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_refs_overwrite__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_overwrite__symbolic(void)
+{
+ /* Overwrite an existing symbolic reference */
+ git_reference *ref, *branch_ref;
+
+ /* The target needds to exist and we need to check the name has changed */
+ cl_git_pass(git_reference_symbolic_create(&branch_ref, g_repo, ref_branch_name, ref_master_name, 0, NULL));
+ cl_git_pass(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_branch_name, 0, NULL));
+ git_reference_free(ref);
+
+ /* Ensure it points to the right place*/
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_name));
+ cl_assert(git_reference_type(ref) & GIT_REFERENCE_SYMBOLIC);
+ cl_assert_equal_s(git_reference_symbolic_target(ref), ref_branch_name);
+ git_reference_free(ref);
+
+ /* Ensure we can't create it unless we force it to */
+ cl_git_fail(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_master_name, 0, NULL));
+ cl_git_pass(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_master_name, 1, NULL));
+ git_reference_free(ref);
+
+ /* Ensure it points to the right place */
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_name));
+ cl_assert(git_reference_type(ref) & GIT_REFERENCE_SYMBOLIC);
+ cl_assert_equal_s(git_reference_symbolic_target(ref), ref_master_name);
+
+ git_reference_free(ref);
+ git_reference_free(branch_ref);
+}
+
+void test_refs_overwrite__object_id(void)
+{
+ /* Overwrite an existing object id reference */
+ git_reference *ref;
+ git_oid id;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name));
+ cl_assert(git_reference_type(ref) & GIT_REFERENCE_DIRECT);
+ git_oid_cpy(&id, git_reference_target(ref));
+ git_reference_free(ref);
+
+ /* Create it */
+ cl_git_pass(git_reference_create(&ref, g_repo, ref_name, &id, 0, NULL));
+ git_reference_free(ref);
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_test_name));
+ cl_assert(git_reference_type(ref) & GIT_REFERENCE_DIRECT);
+ git_oid_cpy(&id, git_reference_target(ref));
+ git_reference_free(ref);
+
+ /* Ensure we can't overwrite unless we force it */
+ cl_git_fail(git_reference_create(&ref, g_repo, ref_name, &id, 0, NULL));
+ cl_git_pass(git_reference_create(&ref, g_repo, ref_name, &id, 1, NULL));
+ git_reference_free(ref);
+
+ /* Ensure it has been overwritten */
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_name));
+ cl_assert_equal_oid(&id, git_reference_target(ref));
+
+ git_reference_free(ref);
+}
+
+void test_refs_overwrite__object_id_with_symbolic(void)
+{
+ /* Overwrite an existing object id reference with a symbolic one */
+ git_reference *ref;
+ git_oid id;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name));
+ cl_assert(git_reference_type(ref) & GIT_REFERENCE_DIRECT);
+ git_oid_cpy(&id, git_reference_target(ref));
+ git_reference_free(ref);
+
+ cl_git_pass(git_reference_create(&ref, g_repo, ref_name, &id, 0, NULL));
+ git_reference_free(ref);
+ cl_git_fail(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_master_name, 0, NULL));
+ cl_git_pass(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_master_name, 1, NULL));
+ git_reference_free(ref);
+
+ /* Ensure it points to the right place */
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_name));
+ cl_assert(git_reference_type(ref) & GIT_REFERENCE_SYMBOLIC);
+ cl_assert_equal_s(git_reference_symbolic_target(ref), ref_master_name);
+
+ git_reference_free(ref);
+}
+
+void test_refs_overwrite__symbolic_with_object_id(void)
+{
+ /* Overwrite an existing symbolic reference with an object id one */
+ git_reference *ref;
+ git_oid id;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name));
+ cl_assert(git_reference_type(ref) & GIT_REFERENCE_DIRECT);
+ git_oid_cpy(&id, git_reference_target(ref));
+ git_reference_free(ref);
+
+ /* Create the symbolic ref */
+ cl_git_pass(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_master_name, 0, NULL));
+ git_reference_free(ref);
+ /* It shouldn't overwrite unless we tell it to */
+ cl_git_fail(git_reference_create(&ref, g_repo, ref_name, &id, 0, NULL));
+ cl_git_pass(git_reference_create(&ref, g_repo, ref_name, &id, 1, NULL));
+ git_reference_free(ref);
+
+ /* Ensure it points to the right place */
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_name));
+ cl_assert(git_reference_type(ref) & GIT_REFERENCE_DIRECT);
+ cl_assert_equal_oid(&id, git_reference_target(ref));
+
+ git_reference_free(ref);
+}
diff --git a/tests/libgit2/refs/pack.c b/tests/libgit2/refs/pack.c
new file mode 100644
index 0000000..1c1cd51
--- /dev/null
+++ b/tests/libgit2/refs/pack.c
@@ -0,0 +1,105 @@
+#include "clar_libgit2.h"
+
+#include "futils.h"
+#include "git2/reflog.h"
+#include "git2/refdb.h"
+#include "reflog.h"
+#include "refs.h"
+#include "ref_helpers.h"
+
+static const char *loose_tag_ref_name = "refs/tags/e90810b";
+
+static git_repository *g_repo;
+
+void test_refs_pack__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_refs_pack__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static void packall(void)
+{
+ git_refdb *refdb;
+
+ cl_git_pass(git_repository_refdb(&refdb, g_repo));
+ cl_git_pass(git_refdb_compress(refdb));
+ git_refdb_free(refdb);
+}
+
+void test_refs_pack__empty(void)
+{
+ /* create a packfile for an empty folder */
+ git_str temp_path = GIT_STR_INIT;
+
+ cl_git_pass(git_str_join_n(&temp_path, '/', 3, git_repository_path(g_repo), GIT_REFS_HEADS_DIR, "empty_dir"));
+ cl_git_pass(git_futils_mkdir_r(temp_path.ptr, GIT_REFS_DIR_MODE));
+ git_str_dispose(&temp_path);
+
+ packall();
+}
+
+void test_refs_pack__loose(void)
+{
+ /* create a packfile from all the loose refs in a repo */
+ git_reference *reference;
+ git_str temp_path = GIT_STR_INIT;
+
+ /* Ensure a known loose ref can be looked up */
+ cl_git_pass(git_reference_lookup(&reference, g_repo, loose_tag_ref_name));
+ cl_assert(reference_is_packed(reference) == 0);
+ cl_assert_equal_s(reference->name, loose_tag_ref_name);
+ git_reference_free(reference);
+
+ /*
+ * We are now trying to pack also a loose reference
+ * called `points_to_blob`, to make sure we can properly
+ * pack weak tags
+ */
+ packall();
+
+ /* Ensure the packed-refs file exists */
+ cl_git_pass(git_str_joinpath(&temp_path, git_repository_path(g_repo), GIT_PACKEDREFS_FILE));
+ cl_assert(git_fs_path_exists(temp_path.ptr));
+
+ /* Ensure the known ref can still be looked up but is now packed */
+ cl_git_pass(git_reference_lookup(&reference, g_repo, loose_tag_ref_name));
+ cl_assert(reference_is_packed(reference));
+ cl_assert_equal_s(reference->name, loose_tag_ref_name);
+
+ /* Ensure the known ref has been removed from the loose folder structure */
+ cl_git_pass(git_str_joinpath(&temp_path, git_repository_path(g_repo), loose_tag_ref_name));
+ cl_assert(!git_fs_path_exists(temp_path.ptr));
+
+ git_reference_free(reference);
+ git_str_dispose(&temp_path);
+}
+
+void test_refs_pack__symbolic(void)
+{
+ /* create a packfile from loose refs skipping symbolic refs */
+ int i;
+ git_oid head;
+ git_reference *ref;
+ char name[128];
+
+ cl_git_pass(git_reference_name_to_id(&head, g_repo, "HEAD"));
+
+ /* make a bunch of references */
+
+ for (i = 0; i < 100; ++i) {
+ p_snprintf(name, sizeof(name), "refs/heads/symbolic-%03d", i);
+ cl_git_pass(git_reference_symbolic_create(
+ &ref, g_repo, name, "refs/heads/master", 0, NULL));
+ git_reference_free(ref);
+
+ p_snprintf(name, sizeof(name), "refs/heads/direct-%03d", i);
+ cl_git_pass(git_reference_create(&ref, g_repo, name, &head, 0, NULL));
+ git_reference_free(ref);
+ }
+
+ packall();
+}
diff --git a/tests/libgit2/refs/peel.c b/tests/libgit2/refs/peel.c
new file mode 100644
index 0000000..fefd2d2
--- /dev/null
+++ b/tests/libgit2/refs/peel.c
@@ -0,0 +1,131 @@
+#include "clar_libgit2.h"
+
+static git_repository *g_repo;
+static git_repository *g_peel_repo;
+
+void test_refs_peel__initialize(void)
+{
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
+ cl_git_pass(git_repository_open(&g_peel_repo, cl_fixture("peeled.git")));
+}
+
+void test_refs_peel__cleanup(void)
+{
+ git_repository_free(g_repo);
+ g_repo = NULL;
+ git_repository_free(g_peel_repo);
+ g_peel_repo = NULL;
+}
+
+static void assert_peel_generic(
+ git_repository *repo,
+ const char *ref_name,
+ git_object_t requested_type,
+ const char* expected_sha,
+ git_object_t expected_type)
+{
+ git_oid expected_oid;
+ git_reference *ref;
+ git_object *peeled;
+
+ cl_git_pass(git_reference_lookup(&ref, repo, ref_name));
+
+ cl_git_pass(git_reference_peel(&peeled, ref, requested_type));
+
+ cl_git_pass(git_oid__fromstr(&expected_oid, expected_sha, GIT_OID_SHA1));
+ cl_assert_equal_oid(&expected_oid, git_object_id(peeled));
+
+ cl_assert_equal_i(expected_type, git_object_type(peeled));
+
+ git_object_free(peeled);
+ git_reference_free(ref);
+}
+
+static void assert_peel(
+ const char *ref_name,
+ git_object_t requested_type,
+ const char* expected_sha,
+ git_object_t expected_type)
+{
+ assert_peel_generic(g_repo, ref_name, requested_type,
+ expected_sha, expected_type);
+}
+
+static void assert_peel_error(int error, const char *ref_name, git_object_t requested_type)
+{
+ git_reference *ref;
+ git_object *peeled;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_name));
+
+ cl_assert_equal_i(error, git_reference_peel(&peeled, ref, requested_type));
+
+ git_reference_free(ref);
+}
+
+void test_refs_peel__can_peel_a_tag(void)
+{
+ assert_peel("refs/tags/test", GIT_OBJECT_TAG,
+ "b25fa35b38051e4ae45d4222e795f9df2e43f1d1", GIT_OBJECT_TAG);
+ assert_peel("refs/tags/test", GIT_OBJECT_COMMIT,
+ "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJECT_COMMIT);
+ assert_peel("refs/tags/test", GIT_OBJECT_TREE,
+ "53fc32d17276939fc79ed05badaef2db09990016", GIT_OBJECT_TREE);
+ assert_peel("refs/tags/point_to_blob", GIT_OBJECT_BLOB,
+ "1385f264afb75a56a5bec74243be9b367ba4ca08", GIT_OBJECT_BLOB);
+}
+
+void test_refs_peel__can_peel_a_branch(void)
+{
+ assert_peel("refs/heads/master", GIT_OBJECT_COMMIT,
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", GIT_OBJECT_COMMIT);
+ assert_peel("refs/heads/master", GIT_OBJECT_TREE,
+ "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162", GIT_OBJECT_TREE);
+}
+
+void test_refs_peel__can_peel_a_symbolic_reference(void)
+{
+ assert_peel("HEAD", GIT_OBJECT_COMMIT,
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", GIT_OBJECT_COMMIT);
+ assert_peel("HEAD", GIT_OBJECT_TREE,
+ "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162", GIT_OBJECT_TREE);
+}
+
+void test_refs_peel__cannot_peel_into_a_non_existing_target(void)
+{
+ assert_peel_error(GIT_EINVALIDSPEC, "refs/tags/point_to_blob", GIT_OBJECT_TAG);
+}
+
+void test_refs_peel__can_peel_into_any_non_tag_object(void)
+{
+ assert_peel("refs/heads/master", GIT_OBJECT_ANY,
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", GIT_OBJECT_COMMIT);
+ assert_peel("refs/tags/point_to_blob", GIT_OBJECT_ANY,
+ "1385f264afb75a56a5bec74243be9b367ba4ca08", GIT_OBJECT_BLOB);
+ assert_peel("refs/tags/test", GIT_OBJECT_ANY,
+ "e90810b8df3e80c413d903f631643c716887138d", GIT_OBJECT_COMMIT);
+}
+
+void test_refs_peel__can_peel_fully_peeled_packed_refs(void)
+{
+ assert_peel_generic(g_peel_repo,
+ "refs/tags/tag-inside-tags", GIT_OBJECT_ANY,
+ "0df1a5865c8abfc09f1f2182e6a31be550e99f07",
+ GIT_OBJECT_COMMIT);
+ assert_peel_generic(g_peel_repo,
+ "refs/foo/tag-outside-tags", GIT_OBJECT_ANY,
+ "0df1a5865c8abfc09f1f2182e6a31be550e99f07",
+ GIT_OBJECT_COMMIT);
+}
+
+void test_refs_peel__can_peel_fully_peeled_tag_to_tag(void)
+{
+ assert_peel_generic(g_peel_repo,
+ "refs/tags/tag-inside-tags", GIT_OBJECT_TAG,
+ "c2596aa0151888587ec5c0187f261e63412d9e11",
+ GIT_OBJECT_TAG);
+ assert_peel_generic(g_peel_repo,
+ "refs/foo/tag-outside-tags", GIT_OBJECT_TAG,
+ "c2596aa0151888587ec5c0187f261e63412d9e11",
+ GIT_OBJECT_TAG);
+}
diff --git a/tests/libgit2/refs/races.c b/tests/libgit2/refs/races.c
new file mode 100644
index 0000000..bf46b76
--- /dev/null
+++ b/tests/libgit2/refs/races.c
@@ -0,0 +1,170 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+#include "git2/reflog.h"
+#include "reflog.h"
+#include "ref_helpers.h"
+
+static const char *commit_id = "099fabac3a9ea935598528c27f866e34089c2eff";
+static const char *refname = "refs/heads/master";
+static const char *other_refname = "refs/heads/foo";
+static const char *other_commit_id = "a65fedf39aefe402d3bb6e24df4d4f5fe4547750";
+
+static git_repository *g_repo;
+
+void test_refs_races__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_refs_races__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_races__create_matching_zero_old(void)
+{
+ git_reference *ref;
+ git_oid id, zero_id;
+
+ git_oid__fromstr(&id, commit_id, GIT_OID_SHA1);
+ git_oid__fromstr(&zero_id, "0000000000000000000000000000000000000000", GIT_OID_SHA1);
+
+ cl_git_fail(git_reference_create_matching(&ref, g_repo, refname, &id, 1, &zero_id, NULL));
+ git_reference_free(ref);
+
+ cl_git_pass(git_reference_create_matching(&ref, g_repo, other_refname, &id, 1, &zero_id, NULL));
+ git_reference_free(ref);
+
+ cl_git_fail(git_reference_create_matching(&ref, g_repo, other_refname, &id, 1, &zero_id, NULL));
+ git_reference_free(ref);
+}
+
+void test_refs_races__create_matching(void)
+{
+ git_reference *ref, *ref2, *ref3;
+ git_oid id, other_id;
+
+ git_oid__fromstr(&id, commit_id, GIT_OID_SHA1);
+ git_oid__fromstr(&other_id, other_commit_id, GIT_OID_SHA1);
+
+ cl_git_fail_with(GIT_EMODIFIED, git_reference_create_matching(&ref, g_repo, refname, &other_id, 1, &other_id, NULL));
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, refname));
+ cl_git_pass(git_reference_create_matching(&ref2, g_repo, refname, &other_id, 1, &id, NULL));
+ cl_git_fail_with(GIT_EMODIFIED, git_reference_set_target(&ref3, ref, &other_id, NULL));
+
+ git_reference_free(ref);
+ git_reference_free(ref2);
+ git_reference_free(ref3);
+}
+
+void test_refs_races__symbolic_create_matching(void)
+{
+ git_reference *ref, *ref2, *ref3;
+ git_oid id, other_id;
+
+ git_oid__fromstr(&id, commit_id, GIT_OID_SHA1);
+ git_oid__fromstr(&other_id, other_commit_id, GIT_OID_SHA1);
+
+ cl_git_fail_with(GIT_EMODIFIED, git_reference_symbolic_create_matching(&ref, g_repo, "HEAD", other_refname, 1, other_refname, NULL));
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, "HEAD"));
+ cl_git_pass(git_reference_symbolic_create_matching(&ref2, g_repo, "HEAD", other_refname, 1, NULL, refname));
+ cl_git_fail_with(GIT_EMODIFIED, git_reference_symbolic_set_target(&ref3, ref, other_refname, NULL));
+
+ git_reference_free(ref);
+ git_reference_free(ref2);
+ git_reference_free(ref3);
+}
+
+void test_refs_races__delete(void)
+{
+ git_reference *ref, *ref2;
+ git_oid id, other_id;
+
+ git_oid__fromstr(&id, commit_id, GIT_OID_SHA1);
+ git_oid__fromstr(&other_id, other_commit_id, GIT_OID_SHA1);
+
+ /* We can delete a value that matches */
+ cl_git_pass(git_reference_lookup(&ref, g_repo, refname));
+ cl_git_pass(git_reference_delete(ref));
+ git_reference_free(ref);
+
+ /* We cannot delete a symbolic value that doesn't match */
+ cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/symref"));
+ cl_git_pass(git_reference_symbolic_create_matching(&ref2, g_repo, "refs/symref", other_refname, 1, NULL, refname));
+ cl_git_fail_with(GIT_EMODIFIED, git_reference_delete(ref));
+
+ git_reference_free(ref);
+ git_reference_free(ref2);
+
+ cl_git_pass(git_reference_create(&ref, g_repo, refname, &id, 1, NULL));
+ git_reference_free(ref);
+
+ /* We cannot delete an oid value that doesn't match */
+ cl_git_pass(git_reference_lookup(&ref, g_repo, refname));
+ cl_git_pass(git_reference_create_matching(&ref2, g_repo, refname, &other_id, 1, &id, NULL));
+ cl_git_fail_with(GIT_EMODIFIED, git_reference_delete(ref));
+
+ git_reference_free(ref);
+ git_reference_free(ref2);
+}
+
+void test_refs_races__switch_oid_to_symbolic(void)
+{
+ git_reference *ref, *ref2, *ref3;
+ git_oid id, other_id;
+
+ git_oid__fromstr(&id, commit_id, GIT_OID_SHA1);
+ git_oid__fromstr(&other_id, other_commit_id, GIT_OID_SHA1);
+
+ /* Removing a direct ref when it's currently symbolic should fail */
+ cl_git_pass(git_reference_lookup(&ref, g_repo, refname));
+ cl_git_pass(git_reference_symbolic_create(&ref2, g_repo, refname, other_refname, 1, NULL));
+ cl_git_fail_with(GIT_EMODIFIED, git_reference_delete(ref));
+
+ git_reference_free(ref);
+ git_reference_free(ref2);
+
+ cl_git_pass(git_reference_create(&ref, g_repo, refname, &id, 1, NULL));
+ git_reference_free(ref);
+
+ /* Updating a direct ref when it's currently symbolic should fail */
+ cl_git_pass(git_reference_lookup(&ref, g_repo, refname));
+ cl_git_pass(git_reference_symbolic_create(&ref2, g_repo, refname, other_refname, 1, NULL));
+ cl_git_fail_with(GIT_EMODIFIED, git_reference_set_target(&ref3, ref, &other_id, NULL));
+
+ git_reference_free(ref);
+ git_reference_free(ref2);
+ git_reference_free(ref3);
+}
+
+void test_refs_races__switch_symbolic_to_oid(void)
+{
+ git_reference *ref, *ref2, *ref3;
+ git_oid id, other_id;
+
+ git_oid__fromstr(&id, commit_id, GIT_OID_SHA1);
+ git_oid__fromstr(&other_id, other_commit_id, GIT_OID_SHA1);
+
+ /* Removing a symbolic ref when it's currently direct should fail */
+ cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/symref"));
+ cl_git_pass(git_reference_create(&ref2, g_repo, "refs/symref", &id, 1, NULL));
+ cl_git_fail_with(GIT_EMODIFIED, git_reference_delete(ref));
+
+ git_reference_free(ref);
+ git_reference_free(ref2);
+
+ cl_git_pass(git_reference_symbolic_create(&ref, g_repo, "refs/symref", refname, 1, NULL));
+ git_reference_free(ref);
+
+ /* Updating a symbolic ref when it's currently direct should fail */
+ cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/symref"));
+ cl_git_pass(git_reference_create(&ref2, g_repo, "refs/symref", &id, 1, NULL));
+ cl_git_fail_with(GIT_EMODIFIED, git_reference_symbolic_set_target(&ref3, ref, other_refname, NULL));
+
+ git_reference_free(ref);
+ git_reference_free(ref2);
+ git_reference_free(ref3);
+}
diff --git a/tests/libgit2/refs/read.c b/tests/libgit2/refs/read.c
new file mode 100644
index 0000000..7253cf1
--- /dev/null
+++ b/tests/libgit2/refs/read.c
@@ -0,0 +1,299 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+#include "git2/reflog.h"
+#include "reflog.h"
+#include "ref_helpers.h"
+
+static const char *loose_tag_ref_name = "refs/tags/e90810b";
+static const char *non_existing_tag_ref_name = "refs/tags/i-do-not-exist";
+static const char *head_tracker_sym_ref_name = "HEAD_TRACKER";
+static const char *current_head_target = "refs/heads/master";
+static const char *current_master_tip = "a65fedf39aefe402d3bb6e24df4d4f5fe4547750";
+static const char *packed_head_name = "refs/heads/packed";
+static const char *packed_test_head_name = "refs/heads/packed-test";
+
+static git_repository *g_repo;
+
+void test_refs_read__initialize(void)
+{
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
+}
+
+void test_refs_read__cleanup(void)
+{
+ git_repository_free(g_repo);
+ g_repo = NULL;
+}
+
+void test_refs_read__loose_tag(void)
+{
+ /* lookup a loose tag reference */
+ git_reference *reference;
+ git_object *object;
+ git_str ref_name_from_tag_name = GIT_STR_INIT;
+
+ cl_git_pass(git_reference_lookup(&reference, g_repo, loose_tag_ref_name));
+ cl_assert(git_reference_type(reference) & GIT_REFERENCE_DIRECT);
+ cl_assert(reference_is_packed(reference) == 0);
+ cl_assert_equal_s(reference->name, loose_tag_ref_name);
+
+ cl_git_pass(git_object_lookup(&object, g_repo, git_reference_target(reference), GIT_OBJECT_ANY));
+ cl_assert(object != NULL);
+ cl_assert(git_object_type(object) == GIT_OBJECT_TAG);
+
+ /* Ensure the name of the tag matches the name of the reference */
+ cl_git_pass(git_str_joinpath(&ref_name_from_tag_name, GIT_REFS_TAGS_DIR, git_tag_name((git_tag *)object)));
+ cl_assert_equal_s(ref_name_from_tag_name.ptr, loose_tag_ref_name);
+ git_str_dispose(&ref_name_from_tag_name);
+
+ git_object_free(object);
+
+ git_reference_free(reference);
+}
+
+void test_refs_read__nonexisting_tag(void)
+{
+ /* lookup a loose tag reference that doesn't exist */
+ git_reference *reference;
+
+ cl_git_fail(git_reference_lookup(&reference, g_repo, non_existing_tag_ref_name));
+
+ git_reference_free(reference);
+}
+
+
+void test_refs_read__symbolic(void)
+{
+ /* lookup a symbolic reference */
+ git_reference *reference, *resolved_ref;
+ git_object *object;
+ git_oid id;
+
+ cl_git_pass(git_reference_lookup(&reference, g_repo, GIT_HEAD_FILE));
+ cl_assert(git_reference_type(reference) & GIT_REFERENCE_SYMBOLIC);
+ cl_assert(reference_is_packed(reference) == 0);
+ cl_assert_equal_s(reference->name, GIT_HEAD_FILE);
+
+ cl_git_pass(git_reference_resolve(&resolved_ref, reference));
+ cl_assert(git_reference_type(resolved_ref) == GIT_REFERENCE_DIRECT);
+
+ cl_git_pass(git_object_lookup(&object, g_repo, git_reference_target(resolved_ref), GIT_OBJECT_ANY));
+ cl_assert(object != NULL);
+ cl_assert(git_object_type(object) == GIT_OBJECT_COMMIT);
+
+ git_oid__fromstr(&id, current_master_tip, GIT_OID_SHA1);
+ cl_assert_equal_oid(&id, git_object_id(object));
+
+ git_object_free(object);
+
+ git_reference_free(reference);
+ git_reference_free(resolved_ref);
+}
+
+void test_refs_read__nested_symbolic(void)
+{
+ /* lookup a nested symbolic reference */
+ git_reference *reference, *resolved_ref;
+ git_object *object;
+ git_oid id;
+
+ cl_git_pass(git_reference_lookup(&reference, g_repo, head_tracker_sym_ref_name));
+ cl_assert(git_reference_type(reference) & GIT_REFERENCE_SYMBOLIC);
+ cl_assert(reference_is_packed(reference) == 0);
+ cl_assert_equal_s(reference->name, head_tracker_sym_ref_name);
+
+ cl_git_pass(git_reference_resolve(&resolved_ref, reference));
+ cl_assert(git_reference_type(resolved_ref) == GIT_REFERENCE_DIRECT);
+
+ cl_git_pass(git_object_lookup(&object, g_repo, git_reference_target(resolved_ref), GIT_OBJECT_ANY));
+ cl_assert(object != NULL);
+ cl_assert(git_object_type(object) == GIT_OBJECT_COMMIT);
+
+ git_oid__fromstr(&id, current_master_tip, GIT_OID_SHA1);
+ cl_assert_equal_oid(&id, git_object_id(object));
+
+ git_object_free(object);
+
+ git_reference_free(reference);
+ git_reference_free(resolved_ref);
+}
+
+void test_refs_read__head_then_master(void)
+{
+ /* lookup the HEAD and resolve the master branch */
+ git_reference *reference, *resolved_ref, *comp_base_ref;
+
+ cl_git_pass(git_reference_lookup(&reference, g_repo, head_tracker_sym_ref_name));
+ cl_git_pass(git_reference_resolve(&comp_base_ref, reference));
+ git_reference_free(reference);
+
+ cl_git_pass(git_reference_lookup(&reference, g_repo, GIT_HEAD_FILE));
+ cl_git_pass(git_reference_resolve(&resolved_ref, reference));
+ cl_assert_equal_oid(git_reference_target(comp_base_ref), git_reference_target(resolved_ref));
+ git_reference_free(reference);
+ git_reference_free(resolved_ref);
+
+ cl_git_pass(git_reference_lookup(&reference, g_repo, current_head_target));
+ cl_git_pass(git_reference_resolve(&resolved_ref, reference));
+ cl_assert_equal_oid(git_reference_target(comp_base_ref), git_reference_target(resolved_ref));
+ git_reference_free(reference);
+ git_reference_free(resolved_ref);
+
+ git_reference_free(comp_base_ref);
+}
+
+void test_refs_read__master_then_head(void)
+{
+ /* lookup the master branch and then the HEAD */
+ git_reference *reference, *master_ref, *resolved_ref;
+
+ cl_git_pass(git_reference_lookup(&master_ref, g_repo, current_head_target));
+ cl_git_pass(git_reference_lookup(&reference, g_repo, GIT_HEAD_FILE));
+
+ cl_git_pass(git_reference_resolve(&resolved_ref, reference));
+ cl_assert_equal_oid(git_reference_target(master_ref), git_reference_target(resolved_ref));
+
+ git_reference_free(reference);
+ git_reference_free(resolved_ref);
+ git_reference_free(master_ref);
+}
+
+
+void test_refs_read__packed(void)
+{
+ /* lookup a packed reference */
+ git_reference *reference;
+ git_object *object;
+
+ cl_git_pass(git_reference_lookup(&reference, g_repo, packed_head_name));
+ cl_assert(git_reference_type(reference) & GIT_REFERENCE_DIRECT);
+ cl_assert(reference_is_packed(reference));
+ cl_assert_equal_s(reference->name, packed_head_name);
+
+ cl_git_pass(git_object_lookup(&object, g_repo, git_reference_target(reference), GIT_OBJECT_ANY));
+ cl_assert(object != NULL);
+ cl_assert(git_object_type(object) == GIT_OBJECT_COMMIT);
+
+ git_object_free(object);
+
+ git_reference_free(reference);
+}
+
+void test_refs_read__loose_first(void)
+{
+ /* assure that a loose reference is looked up before a packed reference */
+ git_reference *reference;
+
+ cl_git_pass(git_reference_lookup(&reference, g_repo, packed_head_name));
+ git_reference_free(reference);
+ cl_git_pass(git_reference_lookup(&reference, g_repo, packed_test_head_name));
+ cl_assert(git_reference_type(reference) & GIT_REFERENCE_DIRECT);
+ cl_assert(reference_is_packed(reference) == 0);
+ cl_assert_equal_s(reference->name, packed_test_head_name);
+
+ git_reference_free(reference);
+}
+
+void test_refs_read__chomped(void)
+{
+ git_reference *test, *chomped;
+
+ cl_git_pass(git_reference_lookup(&test, g_repo, "refs/heads/test"));
+ cl_git_pass(git_reference_lookup(&chomped, g_repo, "refs/heads/chomped"));
+ cl_assert_equal_oid(git_reference_target(test), git_reference_target(chomped));
+
+ git_reference_free(test);
+ git_reference_free(chomped);
+}
+
+void test_refs_read__trailing(void)
+{
+ git_reference *test, *trailing;
+
+ cl_git_pass(git_reference_lookup(&test, g_repo, "refs/heads/test"));
+ cl_git_pass(git_reference_lookup(&trailing, g_repo, "refs/heads/trailing"));
+ cl_assert_equal_oid(git_reference_target(test), git_reference_target(trailing));
+ git_reference_free(trailing);
+ cl_git_pass(git_reference_lookup(&trailing, g_repo, "FETCH_HEAD"));
+
+ git_reference_free(test);
+ git_reference_free(trailing);
+}
+
+void test_refs_read__unfound_return_ENOTFOUND(void)
+{
+ git_reference *reference;
+ git_oid id;
+
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_reference_lookup(&reference, g_repo, "TEST_MASTER"));
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_reference_lookup(&reference, g_repo, "refs/test/master"));
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_reference_lookup(&reference, g_repo, "refs/tags/test/master"));
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_reference_lookup(&reference, g_repo, "refs/tags/test/farther/master"));
+
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_reference_name_to_id(&id, g_repo, "refs/tags/test/farther/master"));
+}
+
+static void assert_is_branch(const char *name, bool expected_branchness)
+{
+ git_reference *reference;
+ cl_git_pass(git_reference_lookup(&reference, g_repo, name));
+ cl_assert_equal_i(expected_branchness, git_reference_is_branch(reference));
+ git_reference_free(reference);
+}
+
+void test_refs_read__can_determine_if_a_reference_is_a_local_branch(void)
+{
+ assert_is_branch("refs/heads/master", true);
+ assert_is_branch("refs/heads/packed", true);
+ assert_is_branch("refs/remotes/test/master", false);
+ assert_is_branch("refs/tags/e90810b", false);
+}
+
+static void assert_is_tag(const char *name, bool expected_tagness)
+{
+ git_reference *reference;
+ cl_git_pass(git_reference_lookup(&reference, g_repo, name));
+ cl_assert_equal_i(expected_tagness, git_reference_is_tag(reference));
+ git_reference_free(reference);
+}
+
+void test_refs_read__can_determine_if_a_reference_is_a_tag(void)
+{
+ assert_is_tag("refs/tags/e90810b", true);
+ assert_is_tag("refs/tags/test", true);
+ assert_is_tag("refs/heads/packed", false);
+ assert_is_tag("refs/remotes/test/master", false);
+}
+
+static void assert_is_note(const char *name, bool expected_noteness)
+{
+ git_reference *reference;
+ cl_git_pass(git_reference_lookup(&reference, g_repo, name));
+ cl_assert_equal_i(expected_noteness, git_reference_is_note(reference));
+ git_reference_free(reference);
+}
+
+void test_refs_read__can_determine_if_a_reference_is_a_note(void)
+{
+ assert_is_note("refs/notes/fanout", true);
+ assert_is_note("refs/heads/packed", false);
+ assert_is_note("refs/remotes/test/master", false);
+}
+
+void test_refs_read__invalid_name_returns_EINVALIDSPEC(void)
+{
+ git_reference *reference;
+ git_oid id;
+
+ cl_assert_equal_i(GIT_EINVALIDSPEC,
+ git_reference_lookup(&reference, g_repo, "refs/heads/Inv@{id"));
+
+ cl_assert_equal_i(GIT_EINVALIDSPEC,
+ git_reference_name_to_id(&id, g_repo, "refs/heads/Inv@{id"));
+}
diff --git a/tests/libgit2/refs/ref_helpers.c b/tests/libgit2/refs/ref_helpers.c
new file mode 100644
index 0000000..70d5d36
--- /dev/null
+++ b/tests/libgit2/refs/ref_helpers.c
@@ -0,0 +1,25 @@
+#include "git2/repository.h"
+#include "git2/refs.h"
+#include "common.h"
+#include "util.h"
+#include "path.h"
+#include "ref_helpers.h"
+
+int reference_is_packed(git_reference *ref)
+{
+ git_str ref_path = GIT_STR_INIT;
+ int packed;
+
+ assert(ref);
+
+ if (git_str_joinpath(&ref_path,
+ git_repository_path(git_reference_owner(ref)),
+ git_reference_name(ref)) < 0)
+ return -1;
+
+ packed = !git_fs_path_isfile(ref_path.ptr);
+
+ git_str_dispose(&ref_path);
+
+ return packed;
+}
diff --git a/tests/libgit2/refs/ref_helpers.h b/tests/libgit2/refs/ref_helpers.h
new file mode 100644
index 0000000..0ef55bf
--- /dev/null
+++ b/tests/libgit2/refs/ref_helpers.h
@@ -0,0 +1 @@
+int reference_is_packed(git_reference *ref);
diff --git a/tests/libgit2/refs/reflog/drop.c b/tests/libgit2/refs/reflog/drop.c
new file mode 100644
index 0000000..2d71933
--- /dev/null
+++ b/tests/libgit2/refs/reflog/drop.c
@@ -0,0 +1,115 @@
+#include "clar_libgit2.h"
+
+#include "reflog.h"
+
+static git_repository *g_repo;
+static git_reflog *g_reflog;
+static size_t entrycount;
+
+void test_refs_reflog_drop__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo.git");
+
+ git_reflog_read(&g_reflog, g_repo, "HEAD");
+ entrycount = git_reflog_entrycount(g_reflog);
+}
+
+void test_refs_reflog_drop__cleanup(void)
+{
+ git_reflog_free(g_reflog);
+ g_reflog = NULL;
+
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_reflog_drop__dropping_a_non_exisiting_entry_from_the_log_returns_ENOTFOUND(void)
+{
+ cl_assert_equal_i(GIT_ENOTFOUND, git_reflog_drop(g_reflog, entrycount, 0));
+
+ cl_assert_equal_sz(entrycount, git_reflog_entrycount(g_reflog));
+}
+
+void test_refs_reflog_drop__can_drop_an_entry(void)
+{
+ cl_assert(entrycount > 4);
+
+ cl_git_pass(git_reflog_drop(g_reflog, 2, 0));
+ cl_assert_equal_sz(entrycount - 1, git_reflog_entrycount(g_reflog));
+}
+
+void test_refs_reflog_drop__can_drop_an_entry_and_rewrite_the_log_history(void)
+{
+ const git_reflog_entry *before_current;
+ const git_reflog_entry *after_current;
+ git_oid before_current_old_oid, before_current_cur_oid;
+
+ cl_assert(entrycount > 4);
+
+ before_current = git_reflog_entry_byindex(g_reflog, 1);
+
+ git_oid_cpy(&before_current_old_oid, &before_current->oid_old);
+ git_oid_cpy(&before_current_cur_oid, &before_current->oid_cur);
+
+ cl_git_pass(git_reflog_drop(g_reflog, 1, 1));
+
+ cl_assert_equal_sz(entrycount - 1, git_reflog_entrycount(g_reflog));
+
+ after_current = git_reflog_entry_byindex(g_reflog, 0);
+
+ cl_assert_equal_i(0, git_oid_cmp(&before_current_old_oid, &after_current->oid_old));
+ cl_assert(0 != git_oid_cmp(&before_current_cur_oid, &after_current->oid_cur));
+}
+
+void test_refs_reflog_drop__can_drop_the_oldest_entry(void)
+{
+ const git_reflog_entry *entry;
+
+ cl_assert(entrycount > 2);
+
+ cl_git_pass(git_reflog_drop(g_reflog, entrycount - 1, 0));
+ cl_assert_equal_sz(entrycount - 1, git_reflog_entrycount(g_reflog));
+
+ entry = git_reflog_entry_byindex(g_reflog, entrycount - 2);
+ cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_SHA1_HEXZERO) != 0);
+}
+
+void test_refs_reflog_drop__can_drop_the_oldest_entry_and_rewrite_the_log_history(void)
+{
+ const git_reflog_entry *entry;
+
+ cl_assert(entrycount > 2);
+
+ cl_git_pass(git_reflog_drop(g_reflog, entrycount - 1, 1));
+ cl_assert_equal_sz(entrycount - 1, git_reflog_entrycount(g_reflog));
+
+ entry = git_reflog_entry_byindex(g_reflog, entrycount - 2);
+ cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_SHA1_HEXZERO) == 0);
+}
+
+void test_refs_reflog_drop__can_drop_all_the_entries(void)
+{
+ cl_assert(--entrycount > 0);
+
+ do {
+ cl_git_pass(git_reflog_drop(g_reflog, 0, 1));
+ } while (--entrycount > 0);
+
+ cl_git_pass(git_reflog_drop(g_reflog, 0, 1));
+
+ cl_assert_equal_i(0, (int)git_reflog_entrycount(g_reflog));
+}
+
+void test_refs_reflog_drop__can_persist_deletion_on_disk(void)
+{
+ cl_assert(entrycount > 2);
+
+ cl_git_pass(git_reflog_drop(g_reflog, 0, 1));
+ cl_assert_equal_sz(entrycount - 1, git_reflog_entrycount(g_reflog));
+ cl_git_pass(git_reflog_write(g_reflog));
+
+ git_reflog_free(g_reflog);
+
+ git_reflog_read(&g_reflog, g_repo, "HEAD");
+
+ cl_assert_equal_sz(entrycount - 1, git_reflog_entrycount(g_reflog));
+}
diff --git a/tests/libgit2/refs/reflog/messages.c b/tests/libgit2/refs/reflog/messages.c
new file mode 100644
index 0000000..647c00d
--- /dev/null
+++ b/tests/libgit2/refs/reflog/messages.c
@@ -0,0 +1,421 @@
+#include "clar_libgit2.h"
+
+#include "futils.h"
+#include "git2/reflog.h"
+#include "reflog.h"
+#include "refs.h"
+#include "reflog_helpers.h"
+
+static const char *g_email = "foo@example.com";
+static git_repository *g_repo;
+
+/* Fixture setup and teardown */
+void test_refs_reflog_messages__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo.git");
+ cl_git_pass(git_repository_set_ident(g_repo, "Foo Bar", g_email));
+}
+
+void test_refs_reflog_messages__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_reflog_messages__setting_head_updates_reflog(void)
+{
+ git_object *tag;
+ git_annotated_commit *annotated;
+
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/haacked")); /* 4 */
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/unborn"));
+ cl_git_pass(git_revparse_single(&tag, g_repo, "tags/test"));
+ cl_git_pass(git_repository_set_head_detached(g_repo, git_object_id(tag))); /* 3 */
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/haacked")); /* 2 */
+ cl_git_pass(git_repository_set_head(g_repo, "refs/tags/test")); /* 1 */
+ cl_git_pass(git_repository_set_head(g_repo, "refs/remotes/test/master")); /* 0 */
+
+ cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 4,
+ NULL, "refs/heads/haacked",
+ "foo@example.com",
+ "checkout: moving from master to haacked");
+ cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 3,
+ NULL, "tags/test^{commit}",
+ "foo@example.com",
+ "checkout: moving from unborn to e90810b8df3e80c413d903f631643c716887138d");
+ cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 2,
+ "tags/test^{commit}", "refs/heads/haacked",
+ "foo@example.com",
+ "checkout: moving from e90810b8df3e80c413d903f631643c716887138d to haacked");
+ cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 1,
+ "refs/heads/haacked", "tags/test^{commit}",
+ "foo@example.com",
+ "checkout: moving from haacked to test");
+ cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0,
+ "tags/test^{commit}", "refs/remotes/test/master",
+ "foo@example.com",
+ "checkout: moving from e90810b8df3e80c413d903f631643c716887138d to test/master");
+
+ cl_git_pass(git_annotated_commit_from_revspec(&annotated, g_repo, "haacked~0"));
+ cl_git_pass(git_repository_set_head_detached_from_annotated(g_repo, annotated));
+
+ cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0,
+ NULL, "refs/heads/haacked",
+ "foo@example.com",
+ "checkout: moving from be3563ae3f795b2b4353bcce3a527ad0a4f7f644 to haacked~0");
+
+ git_annotated_commit_free(annotated);
+ git_object_free(tag);
+}
+
+void test_refs_reflog_messages__setting_head_to_same_target_ignores_reflog(void)
+{
+ size_t nentries, nentries_after;
+
+ nentries = reflog_entrycount(g_repo, GIT_HEAD_FILE);
+
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/haacked"));
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/haacked"));
+
+ nentries_after = reflog_entrycount(g_repo, GIT_HEAD_FILE);
+
+ cl_assert_equal_i(nentries + 1, nentries_after);
+}
+
+void test_refs_reflog_messages__detaching_writes_reflog(void)
+{
+ git_oid id;
+ const char *msg;
+
+ msg = "checkout: moving from master to e90810b8df3e80c413d903f631643c716887138d";
+ git_oid__fromstr(&id, "e90810b8df3e80c413d903f631643c716887138d", GIT_OID_SHA1);
+ cl_git_pass(git_repository_set_head_detached(g_repo, &id));
+ cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0,
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ "e90810b8df3e80c413d903f631643c716887138d",
+ NULL, msg);
+
+ msg = "checkout: moving from e90810b8df3e80c413d903f631643c716887138d to haacked";
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/haacked"));
+ cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0,
+ "e90810b8df3e80c413d903f631643c716887138d",
+ "258f0e2a959a364e40ed6603d5d44fbb24765b10",
+ NULL, msg);
+}
+
+void test_refs_reflog_messages__orphan_branch_does_not_count(void)
+{
+ git_oid id;
+ const char *msg;
+
+ /* Have something known */
+ msg = "checkout: moving from master to e90810b8df3e80c413d903f631643c716887138d";
+ git_oid__fromstr(&id, "e90810b8df3e80c413d903f631643c716887138d", GIT_OID_SHA1);
+ cl_git_pass(git_repository_set_head_detached(g_repo, &id));
+ cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0,
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ "e90810b8df3e80c413d903f631643c716887138d",
+ NULL, msg);
+
+ /* Switching to an orphan branch does not write to the reflog */
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/orphan"));
+ cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0,
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ "e90810b8df3e80c413d903f631643c716887138d",
+ NULL, msg);
+
+ /* And coming back, we set the source to zero */
+ msg = "checkout: moving from orphan to haacked";
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/haacked"));
+ cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0,
+ "0000000000000000000000000000000000000000",
+ "258f0e2a959a364e40ed6603d5d44fbb24765b10",
+ NULL, msg);
+}
+
+void test_refs_reflog_messages__branch_birth(void)
+{
+ git_signature *sig;
+ git_oid id;
+ git_tree *tree;
+ git_reference *ref;
+ const char *msg;
+ size_t nentries, nentries_after;
+
+ nentries = reflog_entrycount(g_repo, GIT_HEAD_FILE);
+
+ cl_git_pass(git_signature_now(&sig, "me", "foo@example.com"));
+
+ cl_git_pass(git_repository_head(&ref, g_repo));
+ cl_git_pass(git_reference_peel((git_object **) &tree, ref, GIT_OBJECT_TREE));
+
+ cl_git_pass(git_repository_set_head(g_repo, "refs/heads/orphan"));
+
+ nentries_after = reflog_entrycount(g_repo, GIT_HEAD_FILE);
+
+ cl_assert_equal_i(nentries, nentries_after);
+
+ msg = "message 2";
+ cl_git_pass(git_commit_create(&id, g_repo, "HEAD", sig, sig, NULL, msg, tree, 0, NULL));
+
+ cl_assert_equal_i(1, reflog_entrycount(g_repo, "refs/heads/orphan"));
+
+ nentries_after = reflog_entrycount(g_repo, GIT_HEAD_FILE);
+
+ cl_assert_equal_i(nentries + 1, nentries_after);
+
+ git_signature_free(sig);
+ git_tree_free(tree);
+ git_reference_free(ref);
+}
+
+void test_refs_reflog_messages__commit_on_symbolic_ref_updates_head_reflog(void)
+{
+ git_signature *sig;
+ git_oid id;
+ git_tree *tree;
+ git_reference *ref1, *ref2;
+ const char *msg;
+ size_t nentries_head, nentries_master;
+
+ nentries_head = reflog_entrycount(g_repo, GIT_HEAD_FILE);
+
+ cl_git_pass(git_signature_now(&sig, "me", "foo@example.com"));
+
+ cl_git_pass(git_repository_head(&ref1, g_repo));
+ cl_git_pass(git_reference_peel((git_object **) &tree, ref1, GIT_OBJECT_TREE));
+
+ nentries_master = reflog_entrycount(g_repo, "refs/heads/master");
+
+ msg = "message 1";
+ cl_git_pass(git_reference_symbolic_create(&ref2, g_repo, "refs/heads/master", "refs/heads/foo", 1, msg));
+
+ cl_assert_equal_i(0, reflog_entrycount(g_repo, "refs/heads/foo"));
+ cl_assert_equal_i(nentries_head, reflog_entrycount(g_repo, GIT_HEAD_FILE));
+ cl_assert_equal_i(nentries_master, reflog_entrycount(g_repo, "refs/heads/master"));
+
+ msg = "message 2";
+ cl_git_pass(git_commit_create(&id, g_repo, "HEAD", sig, sig, NULL, msg, tree, 0, NULL));
+
+ cl_assert_equal_i(1, reflog_entrycount(g_repo, "refs/heads/foo"));
+ cl_assert_equal_i(nentries_head + 1, reflog_entrycount(g_repo, GIT_HEAD_FILE));
+ cl_assert_equal_i(nentries_master, reflog_entrycount(g_repo, "refs/heads/master"));
+
+ git_signature_free(sig);
+ git_reference_free(ref1);
+ git_reference_free(ref2);
+ git_tree_free(tree);
+}
+
+void test_refs_reflog_messages__show_merge_for_merge_commits(void)
+{
+ git_oid b1_oid;
+ git_oid b2_oid;
+ git_oid merge_commit_oid;
+ git_commit *b1_commit;
+ git_commit *b2_commit;
+ git_signature *s;
+ git_commit *parent_commits[2];
+ git_tree *tree;
+
+ cl_git_pass(git_signature_now(&s, "alice", "alice@example.com"));
+
+ cl_git_pass(git_reference_name_to_id(&b1_oid, g_repo, "HEAD"));
+ cl_git_pass(git_reference_name_to_id(&b2_oid, g_repo, "refs/heads/test"));
+
+ cl_git_pass(git_commit_lookup(&b1_commit, g_repo, &b1_oid));
+ cl_git_pass(git_commit_lookup(&b2_commit, g_repo, &b2_oid));
+
+ parent_commits[0] = b1_commit;
+ parent_commits[1] = b2_commit;
+
+ cl_git_pass(git_commit_tree(&tree, b1_commit));
+
+ cl_git_pass(git_commit_create(&merge_commit_oid,
+ g_repo, "HEAD", s, s, NULL,
+ "Merge commit", tree,
+ 2, (const struct git_commit **) parent_commits));
+
+ cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0,
+ NULL,
+ git_oid_tostr_s(&merge_commit_oid),
+ NULL, "commit (merge): Merge commit");
+
+ git_tree_free(tree);
+ git_commit_free(b1_commit);
+ git_commit_free(b2_commit);
+ git_signature_free(s);
+}
+
+void test_refs_reflog_messages__creating_a_direct_reference(void)
+{
+ git_reference *reference;
+ git_oid id;
+ git_reflog *reflog;
+ const git_reflog_entry *entry;
+
+ const char *name = "refs/heads/new-head";
+ const char *message = "You've been logged, mate!";
+
+ cl_git_pass(git_reference_name_to_id(&id, g_repo, "HEAD"));
+
+ cl_git_pass(git_reference_create(&reference, g_repo, name, &id, 0, message));
+
+ cl_git_pass(git_reflog_read(&reflog, g_repo, name));
+ cl_assert_equal_sz(1, git_reflog_entrycount(reflog));
+
+ entry = git_reflog_entry_byindex(reflog, 0);
+ cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_SHA1_HEXZERO) == 0);
+ cl_assert_equal_oid(&id, &entry->oid_cur);
+ cl_assert_equal_s(message, entry->msg);
+
+ git_reflog_free(reflog);
+ git_reference_free(reference);
+}
+
+void test_refs_reflog_messages__newline_gets_replaced(void)
+{
+ const git_reflog_entry *entry;
+ git_signature *signature;
+ git_reflog *reflog;
+ git_oid oid;
+
+ cl_git_pass(git_signature_now(&signature, "me", "foo@example.com"));
+ cl_git_pass(git_oid__fromstr(&oid, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", GIT_OID_SHA1));
+
+ cl_git_pass(git_reflog_read(&reflog, g_repo, "HEAD"));
+ cl_assert_equal_sz(7, git_reflog_entrycount(reflog));
+ cl_git_pass(git_reflog_append(reflog, &oid, signature, "inner\nnewline"));
+ cl_assert_equal_sz(8, git_reflog_entrycount(reflog));
+
+ cl_assert(entry = git_reflog_entry_byindex(reflog, 0));
+ cl_assert_equal_s(git_reflog_entry_message(entry), "inner newline");
+
+ git_signature_free(signature);
+ git_reflog_free(reflog);
+}
+
+void test_refs_reflog_messages__renaming_ref(void)
+{
+ git_reference *ref, *new_ref;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/heads/master"));
+ cl_git_pass(git_reference_rename(&new_ref, ref, "refs/heads/renamed", false,
+ "message"));
+
+ cl_reflog_check_entry(g_repo, git_reference_name(new_ref), 0,
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ "foo@example.com", "message");
+
+ git_reference_free(ref);
+ git_reference_free(new_ref);
+}
+
+void test_refs_reflog_messages__updating_a_direct_reference(void)
+{
+ git_reference *ref, *ref_out, *target_ref;
+ git_oid target_id;
+ const char *message = "You've been logged, mate!";
+
+ git_reference_name_to_id(&target_id, g_repo, "refs/heads/haacked");
+ cl_git_pass(git_reference_lookup(&target_ref, g_repo, "refs/heads/haacked"));
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/heads/master"));
+
+ cl_git_pass(git_reference_set_target(&ref_out, ref, &target_id, message));
+
+ cl_reflog_check_entry(g_repo, "refs/heads/master", 0,
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ "258f0e2a959a364e40ed6603d5d44fbb24765b10",
+ NULL, message);
+
+ git_reference_free(target_ref);
+ git_reference_free(ref);
+ git_reference_free(ref_out);
+}
+
+#define NEW_BRANCH_NAME "new-branch-on-the-block"
+
+void test_refs_reflog_messages__creating_branches_default_messages(void)
+{
+ git_str buf = GIT_STR_INIT;
+ git_annotated_commit *annotated;
+ git_object *obj;
+ git_commit *target;
+ git_reference *branch1, *branch2;
+
+ cl_git_pass(git_revparse_single(&obj, g_repo, "e90810b8df3"));
+ cl_git_pass(git_commit_lookup(&target, g_repo, git_object_id(obj)));
+ git_object_free(obj);
+
+ cl_git_pass(git_branch_create(&branch1, g_repo, NEW_BRANCH_NAME, target, false));
+
+ cl_git_pass(git_str_printf(&buf, "branch: Created from %s", git_oid_tostr_s(git_commit_id(target))));
+ cl_reflog_check_entry(g_repo, "refs/heads/" NEW_BRANCH_NAME, 0,
+ GIT_OID_SHA1_HEXZERO,
+ git_oid_tostr_s(git_commit_id(target)),
+ g_email, git_str_cstr(&buf));
+
+ cl_git_pass(git_reference_remove(g_repo, "refs/heads/" NEW_BRANCH_NAME));
+
+ cl_git_pass(git_annotated_commit_from_revspec(&annotated, g_repo, "e90810b8df3"));
+ cl_git_pass(git_branch_create_from_annotated(&branch2, g_repo, NEW_BRANCH_NAME, annotated, true));
+
+ cl_reflog_check_entry(g_repo, "refs/heads/" NEW_BRANCH_NAME, 0,
+ GIT_OID_SHA1_HEXZERO,
+ git_oid_tostr_s(git_commit_id(target)),
+ g_email, "branch: Created from e90810b8df3");
+
+ git_annotated_commit_free(annotated);
+ git_str_dispose(&buf);
+ git_commit_free(target);
+ git_reference_free(branch1);
+ git_reference_free(branch2);
+}
+
+void test_refs_reflog_messages__moving_branch_default_message(void)
+{
+ git_reference *branch;
+ git_reference *new_branch;
+ git_oid id;
+
+ cl_git_pass(git_reference_lookup(&branch, g_repo, "refs/heads/master"));
+ git_oid_cpy(&id, git_reference_target(branch));
+ cl_git_pass(git_branch_move(&new_branch, branch, "master2", 0));
+
+ cl_reflog_check_entry(g_repo, git_reference_name(new_branch), 0,
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ g_email,
+ "branch: renamed refs/heads/master to refs/heads/master2");
+
+ git_reference_free(branch);
+ git_reference_free(new_branch);
+}
+
+void test_refs_reflog_messages__detaching_head_default_message(void)
+{
+ git_reference *ref;
+
+ cl_assert_equal_i(false, git_repository_head_detached(g_repo));
+
+ cl_git_pass(git_repository_detach_head(g_repo));
+ cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0,
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ NULL, "checkout: moving from master to a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ cl_assert_equal_i(true, git_repository_head_detached(g_repo));
+
+ /* take the repo back to its original state */
+ cl_git_pass(git_reference_symbolic_create(&ref, g_repo, "HEAD", "refs/heads/master",
+ true, "REATTACH"));
+
+ cl_reflog_check_entry(g_repo, GIT_HEAD_FILE, 0,
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ NULL, "REATTACH");
+
+ cl_assert_equal_i(false, git_repository_head_detached(g_repo));
+
+ git_reference_free(ref);
+}
diff --git a/tests/libgit2/refs/reflog/reflog.c b/tests/libgit2/refs/reflog/reflog.c
new file mode 100644
index 0000000..774ec6c
--- /dev/null
+++ b/tests/libgit2/refs/reflog/reflog.c
@@ -0,0 +1,469 @@
+#include "clar_libgit2.h"
+
+#include "futils.h"
+#include "git2/reflog.h"
+#include "reflog.h"
+
+static const char *new_ref = "refs/heads/test-reflog";
+static const char *current_master_tip = "a65fedf39aefe402d3bb6e24df4d4f5fe4547750";
+#define commit_msg "commit: bla bla"
+
+static git_repository *g_repo;
+
+
+/* helpers */
+static void assert_signature(const git_signature *expected, const git_signature *actual)
+{
+ cl_assert(actual);
+ cl_assert_equal_s(expected->name, actual->name);
+ cl_assert_equal_s(expected->email, actual->email);
+ cl_assert(expected->when.offset == actual->when.offset);
+ cl_assert(expected->when.time == actual->when.time);
+}
+
+
+/* Fixture setup and teardown */
+void test_refs_reflog_reflog__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo.git");
+}
+
+void test_refs_reflog_reflog__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static void assert_appends(const git_signature *committer, const git_oid *oid)
+{
+ git_repository *repo2;
+ git_reference *lookedup_ref;
+ git_reflog *reflog;
+ const git_reflog_entry *entry;
+
+ /* Reopen a new instance of the repository */
+ cl_git_pass(git_repository_open(&repo2, "testrepo.git"));
+
+ /* Lookup the previously created branch */
+ cl_git_pass(git_reference_lookup(&lookedup_ref, repo2, new_ref));
+
+ /* Read and parse the reflog for this branch */
+ cl_git_pass(git_reflog_read(&reflog, repo2, new_ref));
+ cl_assert_equal_i(3, (int)git_reflog_entrycount(reflog));
+
+ /* The first one was the creation of the branch */
+ entry = git_reflog_entry_byindex(reflog, 2);
+ cl_assert(git_oid_streq(&entry->oid_old, GIT_OID_SHA1_HEXZERO) == 0);
+
+ entry = git_reflog_entry_byindex(reflog, 1);
+ assert_signature(committer, entry->committer);
+ cl_assert(git_oid_cmp(oid, &entry->oid_old) == 0);
+ cl_assert(git_oid_cmp(oid, &entry->oid_cur) == 0);
+ cl_assert(entry->msg == NULL);
+
+ entry = git_reflog_entry_byindex(reflog, 0);
+ assert_signature(committer, entry->committer);
+ cl_assert(git_oid_cmp(oid, &entry->oid_cur) == 0);
+ cl_assert_equal_s(commit_msg, entry->msg);
+
+ git_reflog_free(reflog);
+ git_repository_free(repo2);
+
+ git_reference_free(lookedup_ref);
+}
+
+void test_refs_reflog_reflog__append_then_read(void)
+{
+ /* write a reflog for a given reference and ensure it can be read back */
+ git_reference *ref;
+ git_oid oid;
+ git_signature *committer;
+ git_reflog *reflog;
+
+ /* Create a new branch pointing at the HEAD */
+ git_oid__fromstr(&oid, current_master_tip, GIT_OID_SHA1);
+ cl_git_pass(git_reference_create(&ref, g_repo, new_ref, &oid, 0, NULL));
+ git_reference_free(ref);
+
+ cl_git_pass(git_signature_now(&committer, "foo", "foo@bar"));
+
+ cl_git_pass(git_reflog_read(&reflog, g_repo, new_ref));
+ cl_git_pass(git_reflog_append(reflog, &oid, committer, NULL));
+ cl_git_pass(git_reflog_append(reflog, &oid, committer, commit_msg "\n"));
+ cl_git_pass(git_reflog_write(reflog));
+
+ assert_appends(committer, &oid);
+
+ git_reflog_free(reflog);
+ git_signature_free(committer);
+}
+
+void test_refs_reflog_reflog__renaming_the_reference_moves_the_reflog(void)
+{
+ git_reference *master, *new_master;
+ git_str master_log_path = GIT_STR_INIT, moved_log_path = GIT_STR_INIT;
+
+ git_str_joinpath(&master_log_path, git_repository_path(g_repo), GIT_REFLOG_DIR);
+ git_str_puts(&moved_log_path, git_str_cstr(&master_log_path));
+ git_str_joinpath(&master_log_path, git_str_cstr(&master_log_path), "refs/heads/master");
+ git_str_joinpath(&moved_log_path, git_str_cstr(&moved_log_path), "refs/moved");
+
+ cl_assert_equal_i(true, git_fs_path_isfile(git_str_cstr(&master_log_path)));
+ cl_assert_equal_i(false, git_fs_path_isfile(git_str_cstr(&moved_log_path)));
+
+ cl_git_pass(git_reference_lookup(&master, g_repo, "refs/heads/master"));
+ cl_git_pass(git_reference_rename(&new_master, master, "refs/moved", 0, NULL));
+ git_reference_free(master);
+
+ cl_assert_equal_i(false, git_fs_path_isfile(git_str_cstr(&master_log_path)));
+ cl_assert_equal_i(true, git_fs_path_isfile(git_str_cstr(&moved_log_path)));
+
+ git_reference_free(new_master);
+ git_str_dispose(&moved_log_path);
+ git_str_dispose(&master_log_path);
+}
+
+void test_refs_reflog_reflog__deleting_the_reference_deletes_the_reflog(void)
+{
+ git_reference *master;
+ git_str master_log_path = GIT_STR_INIT;
+
+ git_str_joinpath(&master_log_path, git_repository_path(g_repo), GIT_REFLOG_DIR);
+ git_str_joinpath(&master_log_path, git_str_cstr(&master_log_path), "refs/heads/master");
+
+ cl_assert_equal_i(true, git_fs_path_isfile(git_str_cstr(&master_log_path)));
+
+ cl_git_pass(git_reference_lookup(&master, g_repo, "refs/heads/master"));
+ cl_git_pass(git_reference_delete(master));
+ git_reference_free(master);
+
+ cl_assert_equal_i(false, git_fs_path_isfile(git_str_cstr(&master_log_path)));
+ git_str_dispose(&master_log_path);
+}
+
+void test_refs_reflog_reflog__removes_empty_reflog_dir(void)
+{
+ git_reference *ref;
+ git_str log_path = GIT_STR_INIT;
+ git_oid id;
+
+ /* Create a new branch pointing at the HEAD */
+ git_oid__fromstr(&id, current_master_tip, GIT_OID_SHA1);
+ cl_git_pass(git_reference_create(&ref, g_repo, "refs/heads/new-dir/new-head", &id, 0, NULL));
+
+ git_str_joinpath(&log_path, git_repository_path(g_repo), GIT_REFLOG_DIR);
+ git_str_joinpath(&log_path, git_str_cstr(&log_path), "refs/heads/new-dir/new-head");
+
+ cl_assert_equal_i(true, git_fs_path_isfile(git_str_cstr(&log_path)));
+
+ cl_git_pass(git_reference_delete(ref));
+ git_reference_free(ref);
+
+ /* new ref creation should succeed since new-dir is empty */
+ git_oid__fromstr(&id, current_master_tip, GIT_OID_SHA1);
+ cl_git_pass(git_reference_create(&ref, g_repo, "refs/heads/new-dir", &id, 0, NULL));
+ git_reference_free(ref);
+
+ git_str_dispose(&log_path);
+}
+
+void test_refs_reflog_reflog__fails_gracefully_on_nonempty_reflog_dir(void)
+{
+ git_reference *ref;
+ git_str log_path = GIT_STR_INIT;
+ git_oid id;
+
+ /* Create a new branch pointing at the HEAD */
+ git_oid__fromstr(&id, current_master_tip, GIT_OID_SHA1);
+ cl_git_pass(git_reference_create(&ref, g_repo, "refs/heads/new-dir/new-head", &id, 0, NULL));
+ git_reference_free(ref);
+
+ git_str_joinpath(&log_path, git_repository_path(g_repo), GIT_REFLOG_DIR);
+ git_str_joinpath(&log_path, git_str_cstr(&log_path), "refs/heads/new-dir/new-head");
+
+ cl_assert_equal_i(true, git_fs_path_isfile(git_str_cstr(&log_path)));
+
+ /* delete the ref manually, leave the reflog */
+ cl_must_pass(p_unlink("testrepo.git/refs/heads/new-dir/new-head"));
+
+ /* new ref creation should fail since new-dir contains reflogs still */
+ git_oid__fromstr(&id, current_master_tip, GIT_OID_SHA1);
+ cl_git_fail_with(GIT_EDIRECTORY, git_reference_create(&ref, g_repo, "refs/heads/new-dir", &id, 0, NULL));
+ git_reference_free(ref);
+
+ git_str_dispose(&log_path);
+}
+
+static void assert_has_reflog(bool expected_result, const char *name)
+{
+ cl_assert_equal_i(expected_result, git_reference_has_log(g_repo, name));
+}
+
+void test_refs_reflog_reflog__reference_has_reflog(void)
+{
+ assert_has_reflog(true, "HEAD");
+ assert_has_reflog(true, "refs/heads/master");
+ assert_has_reflog(false, "refs/heads/subtrees");
+}
+
+void test_refs_reflog_reflog__reading_the_reflog_from_a_reference_with_no_log_returns_an_empty_one(void)
+{
+ git_reflog *reflog;
+ const char *refname = "refs/heads/subtrees";
+ git_str subtrees_log_path = GIT_STR_INIT;
+
+ git_str_join_n(&subtrees_log_path, '/', 3, git_repository_path(g_repo), GIT_REFLOG_DIR, refname);
+ cl_assert_equal_i(false, git_fs_path_isfile(git_str_cstr(&subtrees_log_path)));
+
+ cl_git_pass(git_reflog_read(&reflog, g_repo, refname));
+
+ cl_assert_equal_i(0, (int)git_reflog_entrycount(reflog));
+
+ git_reflog_free(reflog);
+ git_str_dispose(&subtrees_log_path);
+}
+
+void test_refs_reflog_reflog__reading_a_reflog_with_invalid_format_succeeds(void)
+{
+ git_reflog *reflog;
+ const char *refname = "refs/heads/newline";
+ const char *refmessage =
+ "Reflog*message with a newline and enough content after it to pass the GIT_REFLOG_SIZE_MIN check inside reflog_parse.";
+ const git_reflog_entry *entry;
+ git_reference *ref;
+ git_oid id;
+ git_str logpath = GIT_STR_INIT, logcontents = GIT_STR_INIT;
+ char *star;
+
+ /* Create a new branch. */
+ cl_git_pass(git_oid__fromstr(&id, current_master_tip, GIT_OID_SHA1));
+ cl_git_pass(git_reference_create(&ref, g_repo, refname, &id, 1, refmessage));
+
+ /*
+ * Corrupt the branch reflog by introducing a newline inside the reflog message.
+ * We do this by replacing '*' with '\n'
+ */
+ cl_git_pass(git_str_join_n(&logpath, '/', 3, git_repository_path(g_repo), GIT_REFLOG_DIR, refname));
+ cl_git_pass(git_futils_readbuffer(&logcontents, git_str_cstr(&logpath)));
+ cl_assert((star = strchr(git_str_cstr(&logcontents), '*')) != NULL);
+ *star = '\n';
+ cl_git_rewritefile(git_str_cstr(&logpath), git_str_cstr(&logcontents));
+
+ /*
+ * Confirm that the file was rewritten successfully
+ * and now contains a '\n' in the expected location
+ */
+ cl_git_pass(git_futils_readbuffer(&logcontents, git_str_cstr(&logpath)));
+ cl_assert(strstr(git_str_cstr(&logcontents), "Reflog\nmessage") != NULL);
+
+ cl_git_pass(git_reflog_read(&reflog, g_repo, refname));
+ cl_assert(entry = git_reflog_entry_byindex(reflog, 0));
+ cl_assert_equal_s(git_reflog_entry_message(entry), "Reflog");
+
+ git_reference_free(ref);
+ git_reflog_free(reflog);
+ git_str_dispose(&logpath);
+ git_str_dispose(&logcontents);
+}
+
+void test_refs_reflog_reflog__cannot_write_a_moved_reflog(void)
+{
+ git_reference *master, *new_master;
+ git_str master_log_path = GIT_STR_INIT, moved_log_path = GIT_STR_INIT;
+ git_reflog *reflog;
+
+ cl_git_pass(git_reference_lookup(&master, g_repo, "refs/heads/master"));
+ cl_git_pass(git_reflog_read(&reflog, g_repo, "refs/heads/master"));
+
+ cl_git_pass(git_reflog_write(reflog));
+
+ cl_git_pass(git_reference_rename(&new_master, master, "refs/moved", 0, NULL));
+ git_reference_free(master);
+
+ cl_git_fail(git_reflog_write(reflog));
+
+ git_reflog_free(reflog);
+ git_reference_free(new_master);
+ git_str_dispose(&moved_log_path);
+ git_str_dispose(&master_log_path);
+}
+
+void test_refs_reflog_reflog__renaming_with_an_invalid_name_returns_EINVALIDSPEC(void)
+{
+ cl_assert_equal_i(GIT_EINVALIDSPEC,
+ git_reflog_rename(g_repo, "refs/heads/master", "refs/heads/Inv@{id"));
+}
+
+void test_refs_reflog_reflog__write_only_std_locations(void)
+{
+ git_reference *ref;
+ git_oid id;
+
+ git_oid__fromstr(&id, current_master_tip, GIT_OID_SHA1);
+
+ cl_git_pass(git_reference_create(&ref, g_repo, "refs/heads/foo", &id, 1, NULL));
+ git_reference_free(ref);
+ cl_git_pass(git_reference_create(&ref, g_repo, "refs/tags/foo", &id, 1, NULL));
+ git_reference_free(ref);
+ cl_git_pass(git_reference_create(&ref, g_repo, "refs/notes/foo", &id, 1, NULL));
+ git_reference_free(ref);
+
+ assert_has_reflog(true, "refs/heads/foo");
+ assert_has_reflog(false, "refs/tags/foo");
+ assert_has_reflog(true, "refs/notes/foo");
+
+}
+
+void test_refs_reflog_reflog__write_when_explicitly_active(void)
+{
+ git_reference *ref;
+ git_oid id;
+
+ git_oid__fromstr(&id, current_master_tip, GIT_OID_SHA1);
+ git_reference_ensure_log(g_repo, "refs/tags/foo");
+
+ cl_git_pass(git_reference_create(&ref, g_repo, "refs/tags/foo", &id, 1, NULL));
+ git_reference_free(ref);
+ assert_has_reflog(true, "refs/tags/foo");
+}
+
+void test_refs_reflog_reflog__append_to_HEAD_when_changing_current_branch(void)
+{
+ size_t nlogs, nlogs_after;
+ git_reference *ref;
+ git_reflog *log;
+ git_oid id;
+
+ cl_git_pass(git_reflog_read(&log, g_repo, "HEAD"));
+ nlogs = git_reflog_entrycount(log);
+ git_reflog_free(log);
+
+ /* Move it back */
+ git_oid__fromstr(&id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644", GIT_OID_SHA1);
+ cl_git_pass(git_reference_create(&ref, g_repo, "refs/heads/master", &id, 1, NULL));
+ git_reference_free(ref);
+
+ cl_git_pass(git_reflog_read(&log, g_repo, "HEAD"));
+ nlogs_after = git_reflog_entrycount(log);
+ git_reflog_free(log);
+
+ cl_assert_equal_i(nlogs_after, nlogs + 1);
+}
+
+void test_refs_reflog_reflog__do_not_append_when_no_update(void)
+{
+ size_t nlogs, nlogs_after;
+ git_reference *ref, *ref2;
+ git_reflog *log;
+
+ cl_git_pass(git_reflog_read(&log, g_repo, "HEAD"));
+ nlogs = git_reflog_entrycount(log);
+ git_reflog_free(log);
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/heads/master"));
+ cl_git_pass(git_reference_create(&ref2, g_repo, "refs/heads/master",
+ git_reference_target(ref), 1, NULL));
+
+ git_reference_free(ref);
+ git_reference_free(ref2);
+
+ cl_git_pass(git_reflog_read(&log, g_repo, "HEAD"));
+ nlogs_after = git_reflog_entrycount(log);
+ git_reflog_free(log);
+
+ cl_assert_equal_i(nlogs_after, nlogs);
+}
+
+static void assert_no_reflog_update(void)
+{
+ size_t nlogs, nlogs_after;
+ size_t nlogs_master, nlogs_master_after;
+ git_reference *ref;
+ git_reflog *log;
+ git_oid id;
+
+ cl_git_pass(git_reflog_read(&log, g_repo, "HEAD"));
+ nlogs = git_reflog_entrycount(log);
+ git_reflog_free(log);
+
+ cl_git_pass(git_reflog_read(&log, g_repo, "refs/heads/master"));
+ nlogs_master = git_reflog_entrycount(log);
+ git_reflog_free(log);
+
+ /* Move it back */
+ git_oid__fromstr(&id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644", GIT_OID_SHA1);
+ cl_git_pass(git_reference_create(&ref, g_repo, "refs/heads/master", &id, 1, NULL));
+ git_reference_free(ref);
+
+ cl_git_pass(git_reflog_read(&log, g_repo, "HEAD"));
+ nlogs_after = git_reflog_entrycount(log);
+ git_reflog_free(log);
+
+ cl_assert_equal_i(nlogs_after, nlogs);
+
+ cl_git_pass(git_reflog_read(&log, g_repo, "refs/heads/master"));
+ nlogs_master_after = git_reflog_entrycount(log);
+ git_reflog_free(log);
+
+ cl_assert_equal_i(nlogs_after, nlogs);
+ cl_assert_equal_i(nlogs_master_after, nlogs_master);
+
+}
+
+void test_refs_reflog_reflog__logallrefupdates_bare_set_false(void)
+{
+ git_config *config;
+
+ cl_git_pass(git_repository_config(&config, g_repo));
+ cl_git_pass(git_config_set_bool(config, "core.logallrefupdates", false));
+ git_config_free(config);
+
+ assert_no_reflog_update();
+}
+
+void test_refs_reflog_reflog__logallrefupdates_bare_set_always(void)
+{
+ git_config *config;
+ git_reference *ref;
+ git_reflog *log;
+ git_oid id;
+
+ cl_git_pass(git_repository_config(&config, g_repo));
+ cl_git_pass(git_config_set_string(config, "core.logallrefupdates", "always"));
+ git_config_free(config);
+
+ git_oid__fromstr(&id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644", GIT_OID_SHA1);
+ cl_git_pass(git_reference_create(&ref, g_repo, "refs/bork", &id, 1, "message"));
+
+ cl_git_pass(git_reflog_read(&log, g_repo, "refs/bork"));
+ cl_assert_equal_i(1, git_reflog_entrycount(log));
+ cl_assert_equal_s("message", git_reflog_entry_byindex(log, 0)->msg);
+
+ git_reflog_free(log);
+ git_reference_free(ref);
+}
+
+void test_refs_reflog_reflog__logallrefupdates_bare_unset(void)
+{
+ git_config *config;
+
+ cl_git_pass(git_repository_config(&config, g_repo));
+ cl_git_pass(git_config_delete_entry(config, "core.logallrefupdates"));
+ git_config_free(config);
+
+ assert_no_reflog_update();
+}
+
+void test_refs_reflog_reflog__logallrefupdates_nonbare_set_false(void)
+{
+ git_config *config;
+
+ cl_git_sandbox_cleanup();
+ g_repo = cl_git_sandbox_init("testrepo");
+
+
+ cl_git_pass(git_repository_config(&config, g_repo));
+ cl_git_pass(git_config_set_bool(config, "core.logallrefupdates", false));
+ git_config_free(config);
+
+ assert_no_reflog_update();
+}
diff --git a/tests/libgit2/refs/reflog/reflog_helpers.c b/tests/libgit2/refs/reflog/reflog_helpers.c
new file mode 100644
index 0000000..6a7e706
--- /dev/null
+++ b/tests/libgit2/refs/reflog/reflog_helpers.c
@@ -0,0 +1,120 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+#include "reflog.h"
+#include "reflog_helpers.h"
+
+int reflog_entry_tostr(git_str *out, const git_reflog_entry *entry)
+{
+ char old_oid[GIT_OID_SHA1_HEXSIZE], new_oid[GIT_OID_SHA1_HEXSIZE];
+
+ assert(out && entry);
+
+ git_oid_tostr((char *)&old_oid, GIT_OID_SHA1_HEXSIZE, git_reflog_entry_id_old(entry));
+ git_oid_tostr((char *)&new_oid, GIT_OID_SHA1_HEXSIZE, git_reflog_entry_id_new(entry));
+
+ return git_str_printf(out, "%s %s %s %s", old_oid, new_oid, "somesig", git_reflog_entry_message(entry));
+}
+
+size_t reflog_entrycount(git_repository *repo, const char *name)
+{
+ git_reflog *log;
+ size_t ret;
+
+ cl_git_pass(git_reflog_read(&log, repo, name));
+ ret = git_reflog_entrycount(log);
+ git_reflog_free(log);
+
+ return ret;
+}
+
+void cl_reflog_check_entry_(git_repository *repo, const char *reflog, size_t idx,
+ const char *old_spec, const char *new_spec,
+ const char *email, const char *message,
+ const char *file, const char *func, int line)
+{
+ git_reflog *log;
+ const git_reflog_entry *entry;
+ git_str result = GIT_STR_INIT;
+
+ cl_git_pass(git_reflog_read(&log, repo, reflog));
+ entry = git_reflog_entry_byindex(log, idx);
+ if (entry == NULL)
+ clar__fail(file, func, line, "Reflog has no such entry", NULL, 1);
+
+ if (old_spec) {
+ git_object *obj = NULL;
+ if (git_revparse_single(&obj, repo, old_spec) == GIT_OK) {
+ if (git_oid_cmp(git_object_id(obj), git_reflog_entry_id_old(entry)) != 0) {
+ git_object__write_oid_header(&result, "\tOld OID: \"", git_object_id(obj));
+ git_object__write_oid_header(&result, "\" != \"", git_reflog_entry_id_old(entry));
+ git_str_puts(&result, "\"\n");
+ }
+ git_object_free(obj);
+ } else {
+ git_oid *oid = git__calloc(1, sizeof(*oid));
+ git_oid__fromstr(oid, old_spec, GIT_OID_SHA1);
+ if (git_oid_cmp(oid, git_reflog_entry_id_old(entry)) != 0) {
+ git_object__write_oid_header(&result, "\tOld OID: \"", oid);
+ git_object__write_oid_header(&result, "\" != \"", git_reflog_entry_id_old(entry));
+ git_str_puts(&result, "\"\n");
+ }
+ git__free(oid);
+ }
+ }
+ if (new_spec) {
+ git_object *obj = NULL;
+ if (git_revparse_single(&obj, repo, new_spec) == GIT_OK) {
+ if (git_oid_cmp(git_object_id(obj), git_reflog_entry_id_new(entry)) != 0) {
+ git_object__write_oid_header(&result, "\tNew OID: \"", git_object_id(obj));
+ git_object__write_oid_header(&result, "\" != \"", git_reflog_entry_id_new(entry));
+ git_str_puts(&result, "\"\n");
+ }
+ git_object_free(obj);
+ } else {
+ git_oid *oid = git__calloc(1, sizeof(*oid));
+ git_oid__fromstr(oid, new_spec, GIT_OID_SHA1);
+ if (git_oid_cmp(oid, git_reflog_entry_id_new(entry)) != 0) {
+ git_object__write_oid_header(&result, "\tNew OID: \"", oid);
+ git_object__write_oid_header(&result, "\" != \"", git_reflog_entry_id_new(entry));
+ git_str_puts(&result, "\"\n");
+ }
+ git__free(oid);
+ }
+ }
+
+ if (email && strcmp(email, git_reflog_entry_committer(entry)->email) != 0)
+ git_str_printf(&result, "\tEmail: \"%s\" != \"%s\"\n", email, git_reflog_entry_committer(entry)->email);
+
+ if (message) {
+ const char *entry_msg = git_reflog_entry_message(entry);
+ if (entry_msg == NULL) entry_msg = "";
+
+ if (entry_msg && strcmp(message, entry_msg) != 0)
+ git_str_printf(&result, "\tMessage: \"%s\" != \"%s\"\n", message, entry_msg);
+ }
+ if (git_str_len(&result) != 0)
+ clar__fail(file, func, line, "Reflog entry mismatch", git_str_cstr(&result), 1);
+
+ git_str_dispose(&result);
+ git_reflog_free(log);
+}
+
+void reflog_print(git_repository *repo, const char *reflog_name)
+{
+ git_reflog *reflog;
+ size_t idx;
+ git_str out = GIT_STR_INIT;
+
+ git_reflog_read(&reflog, repo, reflog_name);
+
+ for (idx = 0; idx < git_reflog_entrycount(reflog); idx++) {
+ const git_reflog_entry *entry = git_reflog_entry_byindex(reflog, idx);
+ reflog_entry_tostr(&out, entry);
+ git_str_putc(&out, '\n');
+ }
+
+ fprintf(stderr, "%s", git_str_cstr(&out));
+ git_str_dispose(&out);
+ git_reflog_free(reflog);
+}
diff --git a/tests/libgit2/refs/reflog/reflog_helpers.h b/tests/libgit2/refs/reflog/reflog_helpers.h
new file mode 100644
index 0000000..4cd92ca
--- /dev/null
+++ b/tests/libgit2/refs/reflog/reflog_helpers.h
@@ -0,0 +1,12 @@
+size_t reflog_entrycount(git_repository *repo, const char *name);
+
+#define cl_reflog_check_entry(repo, reflog, idx, old_spec, new_spec, email, message) \
+ cl_reflog_check_entry_(repo, reflog, idx, old_spec, new_spec, email, message, __FILE__, __FUNCTION__, __LINE__)
+
+void cl_reflog_check_entry_(git_repository *repo, const char *reflog, size_t idx,
+ const char *old_spec, const char *new_spec,
+ const char *email, const char *message,
+ const char *file, const char *func, int line);
+
+void reflog_print(git_repository *repo, const char *reflog_name);
+int reflog_entry_tostr(git_str *out, const git_reflog_entry *entry);
diff --git a/tests/libgit2/refs/rename.c b/tests/libgit2/refs/rename.c
new file mode 100644
index 0000000..f71e657
--- /dev/null
+++ b/tests/libgit2/refs/rename.c
@@ -0,0 +1,368 @@
+#include "clar_libgit2.h"
+
+#include "futils.h"
+#include "git2/reflog.h"
+#include "reflog.h"
+#include "refs.h"
+#include "ref_helpers.h"
+
+static const char *loose_tag_ref_name = "refs/tags/e90810b";
+static const char *packed_head_name = "refs/heads/packed";
+static const char *packed_test_head_name = "refs/heads/packed-test";
+static const char *ref_one_name = "refs/heads/one/branch";
+static const char *ref_one_name_new = "refs/heads/two/branch";
+static const char *ref_two_name = "refs/heads/two";
+static const char *ref_master_name = "refs/heads/master";
+static const char *ref_two_name_new = "refs/heads/two/two";
+
+static git_repository *g_repo;
+
+
+
+void test_refs_rename__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+ cl_git_pass(git_repository_set_ident(g_repo, "me", "foo@example.com"));
+}
+
+void test_refs_rename__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+
+
+void test_refs_rename__loose(void)
+{
+ /* rename a loose reference */
+ git_reference *looked_up_ref, *new_ref, *another_looked_up_ref;
+ git_str temp_path = GIT_STR_INIT;
+ const char *new_name = "refs/tags/Nemo/knows/refs.kung-fu";
+
+ /* Ensure the ref doesn't exist on the file system */
+ cl_git_pass(git_str_joinpath(&temp_path, git_repository_path(g_repo), new_name));
+ cl_assert(!git_fs_path_exists(temp_path.ptr));
+
+ /* Retrieval of the reference to rename */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, loose_tag_ref_name));
+
+ /* ... which is indeed loose */
+ cl_assert(reference_is_packed(looked_up_ref) == 0);
+
+ /* Now that the reference is renamed... */
+ cl_git_pass(git_reference_rename(&new_ref, looked_up_ref, new_name, 0, NULL));
+ cl_assert_equal_s(new_ref->name, new_name);
+ git_reference_free(looked_up_ref);
+
+ /* ...It can't be looked-up with the old name... */
+ cl_git_fail(git_reference_lookup(&another_looked_up_ref, g_repo, loose_tag_ref_name));
+
+ /* ...but the new name works ok... */
+ cl_git_pass(git_reference_lookup(&another_looked_up_ref, g_repo, new_name));
+ cl_assert_equal_s(new_ref->name, new_name);
+
+ /* .. the new ref is loose... */
+ cl_assert(reference_is_packed(another_looked_up_ref) == 0);
+ cl_assert(reference_is_packed(new_ref) == 0);
+
+ /* ...and the ref can be found in the file system */
+ cl_git_pass(git_str_joinpath(&temp_path, git_repository_path(g_repo), new_name));
+ cl_assert(git_fs_path_exists(temp_path.ptr));
+
+ git_reference_free(new_ref);
+ git_reference_free(another_looked_up_ref);
+ git_str_dispose(&temp_path);
+}
+
+void test_refs_rename__packed(void)
+{
+ /* rename a packed reference (should make it loose) */
+ git_reference *looked_up_ref, *new_ref, *another_looked_up_ref;
+ git_str temp_path = GIT_STR_INIT;
+ const char *brand_new_name = "refs/heads/brand_new_name";
+
+ /* Ensure the ref doesn't exist on the file system */
+ cl_git_pass(git_str_joinpath(&temp_path, git_repository_path(g_repo), packed_head_name));
+ cl_assert(!git_fs_path_exists(temp_path.ptr));
+
+ /* The reference can however be looked-up... */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_head_name));
+
+ /* .. and it's packed */
+ cl_assert(reference_is_packed(looked_up_ref) != 0);
+
+ /* Now that the reference is renamed... */
+ cl_git_pass(git_reference_rename(&new_ref, looked_up_ref, brand_new_name, 0, NULL));
+ cl_assert_equal_s(new_ref->name, brand_new_name);
+ git_reference_free(looked_up_ref);
+
+ /* ...It can't be looked-up with the old name... */
+ cl_git_fail(git_reference_lookup(&another_looked_up_ref, g_repo, packed_head_name));
+
+ /* ...but the new name works ok... */
+ cl_git_pass(git_reference_lookup(&another_looked_up_ref, g_repo, brand_new_name));
+ cl_assert_equal_s(another_looked_up_ref->name, brand_new_name);
+
+ /* .. the ref is no longer packed... */
+ cl_assert(reference_is_packed(another_looked_up_ref) == 0);
+ cl_assert(reference_is_packed(new_ref) == 0);
+
+ /* ...and the ref now happily lives in the file system */
+ cl_git_pass(git_str_joinpath(&temp_path, git_repository_path(g_repo), brand_new_name));
+ cl_assert(git_fs_path_exists(temp_path.ptr));
+
+ git_reference_free(new_ref);
+ git_reference_free(another_looked_up_ref);
+ git_str_dispose(&temp_path);
+}
+
+void test_refs_rename__packed_doesnt_pack_others(void)
+{
+ /* renaming a packed reference does not pack another reference which happens to be in both loose and pack state */
+ git_reference *looked_up_ref, *another_looked_up_ref, *renamed_ref;
+ git_str temp_path = GIT_STR_INIT;
+ const char *brand_new_name = "refs/heads/brand_new_name";
+
+ /* Ensure the other reference exists on the file system */
+ cl_git_pass(git_str_joinpath(&temp_path, git_repository_path(g_repo), packed_test_head_name));
+ cl_assert(git_fs_path_exists(temp_path.ptr));
+
+ /* Lookup the other reference */
+ cl_git_pass(git_reference_lookup(&another_looked_up_ref, g_repo, packed_test_head_name));
+
+ /* Ensure it's loose */
+ cl_assert(reference_is_packed(another_looked_up_ref) == 0);
+ git_reference_free(another_looked_up_ref);
+
+ /* Lookup the reference to rename */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_head_name));
+
+ /* Ensure it's packed */
+ cl_assert(reference_is_packed(looked_up_ref) != 0);
+
+ /* Now that the reference is renamed... */
+ cl_git_pass(git_reference_rename(&renamed_ref, looked_up_ref, brand_new_name, 0, NULL));
+ git_reference_free(looked_up_ref);
+
+ /* Lookup the other reference */
+ cl_git_pass(git_reference_lookup(&another_looked_up_ref, g_repo, packed_test_head_name));
+
+ /* Ensure it's loose */
+ cl_assert(reference_is_packed(another_looked_up_ref) == 0);
+
+ /* Ensure the other ref still exists on the file system */
+ cl_assert(git_fs_path_exists(temp_path.ptr));
+
+ git_reference_free(renamed_ref);
+ git_reference_free(another_looked_up_ref);
+ git_str_dispose(&temp_path);
+}
+
+void test_refs_rename__name_collision(void)
+{
+ /* can not rename a reference with the name of an existing reference */
+ git_reference *looked_up_ref, *renamed_ref;
+
+ /* An existing reference... */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_head_name));
+
+ /* Can not be renamed to the name of another existing reference. */
+ cl_git_fail(git_reference_rename(&renamed_ref, looked_up_ref, packed_test_head_name, 0, NULL));
+ git_reference_free(looked_up_ref);
+
+ /* Failure to rename it hasn't corrupted its state */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_head_name));
+ cl_assert_equal_s(looked_up_ref->name, packed_head_name);
+
+ git_reference_free(looked_up_ref);
+}
+
+void test_refs_rename__invalid_name(void)
+{
+ /* can not rename a reference with an invalid name */
+ git_reference *looked_up_ref, *renamed_ref;
+
+ /* An existing oid reference... */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_test_head_name));
+
+ /* Can not be renamed with an invalid name. */
+ cl_assert_equal_i(
+ GIT_EINVALIDSPEC,
+ git_reference_rename(&renamed_ref, looked_up_ref, "Hello! I'm a very invalid name.", 0, NULL));
+
+ /* Can not be renamed outside of the refs hierarchy
+ * unless it's ALL_CAPS_AND_UNDERSCORES.
+ */
+ cl_assert_equal_i(GIT_EINVALIDSPEC, git_reference_rename(&renamed_ref, looked_up_ref, "i-will-sudo-you", 0, NULL));
+
+ /* Failure to rename it hasn't corrupted its state */
+ git_reference_free(looked_up_ref);
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_test_head_name));
+ cl_assert_equal_s(looked_up_ref->name, packed_test_head_name);
+
+ git_reference_free(looked_up_ref);
+}
+
+void test_refs_rename__force_loose_packed(void)
+{
+ /* can force-rename a packed reference with the name of an existing loose and packed reference */
+ git_reference *looked_up_ref, *renamed_ref;
+ git_oid oid;
+
+ /* An existing reference... */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_head_name));
+ git_oid_cpy(&oid, git_reference_target(looked_up_ref));
+
+ /* Can be force-renamed to the name of another existing reference. */
+ cl_git_pass(git_reference_rename(&renamed_ref, looked_up_ref, packed_test_head_name, 1, NULL));
+ git_reference_free(looked_up_ref);
+ git_reference_free(renamed_ref);
+
+ /* Check we actually renamed it */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, packed_test_head_name));
+ cl_assert_equal_s(looked_up_ref->name, packed_test_head_name);
+ cl_assert_equal_oid(&oid, git_reference_target(looked_up_ref));
+ git_reference_free(looked_up_ref);
+
+ /* And that the previous one doesn't exist any longer */
+ cl_git_fail(git_reference_lookup(&looked_up_ref, g_repo, packed_head_name));
+}
+
+void test_refs_rename__force_loose(void)
+{
+ /* can force-rename a loose reference with the name of an existing loose reference */
+ git_reference *looked_up_ref, *renamed_ref;
+ git_oid oid;
+
+ /* An existing reference... */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, "refs/heads/br2"));
+ git_oid_cpy(&oid, git_reference_target(looked_up_ref));
+
+ /* Can be force-renamed to the name of another existing reference. */
+ cl_git_pass(git_reference_rename(&renamed_ref, looked_up_ref, "refs/heads/test", 1, NULL));
+ git_reference_free(looked_up_ref);
+ git_reference_free(renamed_ref);
+
+ /* Check we actually renamed it */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, "refs/heads/test"));
+ cl_assert_equal_s(looked_up_ref->name, "refs/heads/test");
+ cl_assert_equal_oid(&oid, git_reference_target(looked_up_ref));
+ git_reference_free(looked_up_ref);
+
+ /* And that the previous one doesn't exist any longer */
+ cl_git_fail(git_reference_lookup(&looked_up_ref, g_repo, "refs/heads/br2"));
+
+ git_reference_free(looked_up_ref);
+}
+
+
+void test_refs_rename__overwrite(void)
+{
+ /* can not overwrite name of existing reference */
+ git_reference *ref, *ref_one, *ref_one_new, *ref_two;
+ git_refdb *refdb;
+ git_oid id;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name));
+ cl_assert(git_reference_type(ref) & GIT_REFERENCE_DIRECT);
+
+ git_oid_cpy(&id, git_reference_target(ref));
+
+ /* Create loose references */
+ cl_git_pass(git_reference_create(&ref_one, g_repo, ref_one_name, &id, 0, NULL));
+ cl_git_pass(git_reference_create(&ref_two, g_repo, ref_two_name, &id, 0, NULL));
+
+ /* Pack everything */
+ cl_git_pass(git_repository_refdb(&refdb, g_repo));
+ cl_git_pass(git_refdb_compress(refdb));
+
+ /* Attempt to create illegal reference */
+ cl_git_fail(git_reference_create(&ref_one_new, g_repo, ref_one_name_new, &id, 0, NULL));
+
+ /* Illegal reference couldn't be created so this is supposed to fail */
+ cl_git_fail(git_reference_lookup(&ref_one_new, g_repo, ref_one_name_new));
+
+ git_reference_free(ref);
+ git_reference_free(ref_one);
+ git_reference_free(ref_one_new);
+ git_reference_free(ref_two);
+ git_refdb_free(refdb);
+}
+
+
+void test_refs_rename__prefix(void)
+{
+ /* can be renamed to a new name prefixed with the old name */
+ git_reference *ref, *ref_two, *looked_up_ref, *renamed_ref;
+ git_oid id;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name));
+ cl_assert(git_reference_type(ref) & GIT_REFERENCE_DIRECT);
+
+ git_oid_cpy(&id, git_reference_target(ref));
+
+ /* Create loose references */
+ cl_git_pass(git_reference_create(&ref_two, g_repo, ref_two_name, &id, 0, NULL));
+
+ /* An existing reference... */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, ref_two_name));
+
+ /* Can be rename to a new name starting with the old name. */
+ cl_git_pass(git_reference_rename(&renamed_ref, looked_up_ref, ref_two_name_new, 0, NULL));
+ git_reference_free(looked_up_ref);
+ git_reference_free(renamed_ref);
+
+ /* Check we actually renamed it */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, ref_two_name_new));
+ cl_assert_equal_s(looked_up_ref->name, ref_two_name_new);
+ git_reference_free(looked_up_ref);
+ cl_git_fail(git_reference_lookup(&looked_up_ref, g_repo, ref_two_name));
+
+ git_reference_free(ref);
+ git_reference_free(ref_two);
+ git_reference_free(looked_up_ref);
+}
+
+void test_refs_rename__move_up(void)
+{
+ /* can move a reference to a upper reference hierarchy */
+ git_reference *ref, *ref_two, *looked_up_ref, *renamed_ref;
+ git_oid id;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name));
+ cl_assert(git_reference_type(ref) & GIT_REFERENCE_DIRECT);
+
+ git_oid_cpy(&id, git_reference_target(ref));
+
+ /* Create loose references */
+ cl_git_pass(git_reference_create(&ref_two, g_repo, ref_two_name_new, &id, 0, NULL));
+ git_reference_free(ref_two);
+
+ /* An existing reference... */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, ref_two_name_new));
+
+ /* Can be renamed upward the reference tree. */
+ cl_git_pass(git_reference_rename(&renamed_ref, looked_up_ref, ref_two_name, 0, NULL));
+ git_reference_free(looked_up_ref);
+ git_reference_free(renamed_ref);
+
+ /* Check we actually renamed it */
+ cl_git_pass(git_reference_lookup(&looked_up_ref, g_repo, ref_two_name));
+ cl_assert_equal_s(looked_up_ref->name, ref_two_name);
+ git_reference_free(looked_up_ref);
+
+ cl_git_fail(git_reference_lookup(&looked_up_ref, g_repo, ref_two_name_new));
+ git_reference_free(ref);
+ git_reference_free(looked_up_ref);
+}
+
+void test_refs_rename__propagate_eexists(void)
+{
+ git_reference *ref, *new_ref;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, packed_head_name));
+
+ cl_assert_equal_i(GIT_EEXISTS, git_reference_rename(&new_ref, ref, packed_test_head_name, 0, NULL));
+
+ git_reference_free(ref);
+}
diff --git a/tests/libgit2/refs/revparse.c b/tests/libgit2/refs/revparse.c
new file mode 100644
index 0000000..d2f4648
--- /dev/null
+++ b/tests/libgit2/refs/revparse.c
@@ -0,0 +1,891 @@
+#include "clar_libgit2.h"
+
+#include "git2/revparse.h"
+#include "refs.h"
+#include "path.h"
+
+static git_repository *g_repo;
+static git_object *g_obj;
+
+/* Helpers */
+static void test_object_and_ref_inrepo(
+ const char *spec,
+ const char *expected_oid,
+ const char *expected_refname,
+ git_repository *repo,
+ bool assert_reference_retrieval)
+{
+ char objstr[64] = {0};
+ git_object *obj = NULL;
+ git_reference *ref = NULL;
+ int error;
+
+ error = git_revparse_ext(&obj, &ref, repo, spec);
+
+ if (expected_oid != NULL) {
+ cl_git_pass(error);
+ git_oid_fmt(objstr, git_object_id(obj));
+ cl_assert_equal_s(objstr, expected_oid);
+ } else
+ cl_git_fail(error);
+
+ if (assert_reference_retrieval) {
+ if (expected_refname == NULL)
+ cl_assert(NULL == ref);
+ else
+ cl_assert_equal_s(expected_refname, git_reference_name(ref));
+ }
+
+ git_object_free(obj);
+ git_reference_free(ref);
+}
+
+static void test_object_inrepo(const char *spec, const char *expected_oid, git_repository *repo)
+{
+ test_object_and_ref_inrepo(spec, expected_oid, NULL, repo, false);
+}
+
+static void test_id_inrepo(
+ const char *spec,
+ const char *expected_left,
+ const char *expected_right,
+ git_revspec_t expected_flags,
+ git_repository *repo)
+{
+ git_revspec revspec;
+ int error = git_revparse(&revspec, repo, spec);
+
+ if (expected_left) {
+ char str[64] = {0};
+ cl_assert_equal_i(0, error);
+ git_oid_fmt(str, git_object_id(revspec.from));
+ cl_assert_equal_s(str, expected_left);
+ git_object_free(revspec.from);
+ } else {
+ cl_assert_equal_i(GIT_ENOTFOUND, error);
+ }
+
+ if (expected_right) {
+ char str[64] = {0};
+ git_oid_fmt(str, git_object_id(revspec.to));
+ cl_assert_equal_s(str, expected_right);
+ git_object_free(revspec.to);
+ }
+
+ if (expected_flags)
+ cl_assert_equal_i(expected_flags, revspec.flags);
+}
+
+static void test_object(const char *spec, const char *expected_oid)
+{
+ test_object_inrepo(spec, expected_oid, g_repo);
+}
+
+static void test_object_and_ref(const char *spec, const char *expected_oid, const char *expected_refname)
+{
+ test_object_and_ref_inrepo(spec, expected_oid, expected_refname, g_repo, true);
+}
+
+static void test_rangelike(const char *rangelike,
+ const char *expected_left,
+ const char *expected_right,
+ git_revspec_t expected_revparseflags)
+{
+ char objstr[64] = {0};
+ git_revspec revspec;
+ int error;
+
+ error = git_revparse(&revspec, g_repo, rangelike);
+
+ if (expected_left != NULL) {
+ cl_assert_equal_i(0, error);
+ cl_assert_equal_i(revspec.flags, expected_revparseflags);
+ git_oid_fmt(objstr, git_object_id(revspec.from));
+ cl_assert_equal_s(objstr, expected_left);
+ git_oid_fmt(objstr, git_object_id(revspec.to));
+ cl_assert_equal_s(objstr, expected_right);
+ } else
+ cl_assert(error != 0);
+
+ git_object_free(revspec.from);
+ git_object_free(revspec.to);
+}
+
+
+static void test_id(
+ const char *spec,
+ const char *expected_left,
+ const char *expected_right,
+ git_revspec_t expected_flags)
+{
+ test_id_inrepo(spec, expected_left, expected_right, expected_flags, g_repo);
+}
+
+static void test_invalid_revspec(const char* invalid_spec)
+{
+ git_revspec revspec;
+
+ cl_assert_equal_i(
+ GIT_EINVALIDSPEC, git_revparse(&revspec, g_repo, invalid_spec));
+}
+
+void test_refs_revparse__initialize(void)
+{
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture("testrepo.git")));
+}
+
+void test_refs_revparse__cleanup(void)
+{
+ git_repository_free(g_repo);
+}
+
+void test_refs_revparse__nonexistant_object(void)
+{
+ test_object("this-does-not-exist", NULL);
+ test_object("this-does-not-exist^1", NULL);
+ test_object("this-does-not-exist~2", NULL);
+}
+
+static void assert_invalid_single_spec(const char *invalid_spec)
+{
+ cl_assert_equal_i(
+ GIT_EINVALIDSPEC, git_revparse_single(&g_obj, g_repo, invalid_spec));
+}
+
+void test_refs_revparse__invalid_reference_name(void)
+{
+ assert_invalid_single_spec("this doesn't make sense");
+ assert_invalid_single_spec("Inv@{id");
+ assert_invalid_single_spec("");
+}
+
+void test_refs_revparse__shas(void)
+{
+ test_object("c47800c7266a2be04c571c04d5a6614691ea99bd", "c47800c7266a2be04c571c04d5a6614691ea99bd");
+ test_object("c47800c", "c47800c7266a2be04c571c04d5a6614691ea99bd");
+}
+
+void test_refs_revparse__head(void)
+{
+ test_object("HEAD", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("HEAD^0", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("HEAD~0", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("master", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+}
+
+void test_refs_revparse__full_refs(void)
+{
+ test_object("refs/heads/master", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("refs/heads/test", "e90810b8df3e80c413d903f631643c716887138d");
+ test_object("refs/tags/test", "b25fa35b38051e4ae45d4222e795f9df2e43f1d1");
+}
+
+void test_refs_revparse__partial_refs(void)
+{
+ test_object("point_to_blob", "1385f264afb75a56a5bec74243be9b367ba4ca08");
+ test_object("packed-test", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045");
+ test_object("br2", "a4a7dce85cf63874e984719f4fdd239f5145052f");
+}
+
+void test_refs_revparse__describe_output(void)
+{
+ test_object("blah-7-gc47800c", "c47800c7266a2be04c571c04d5a6614691ea99bd");
+ test_object("not-good", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+}
+
+void test_refs_revparse__nth_parent(void)
+{
+ assert_invalid_single_spec("be3563a^-1");
+ assert_invalid_single_spec("^");
+ assert_invalid_single_spec("be3563a^{tree}^");
+ assert_invalid_single_spec("point_to_blob^{blob}^");
+ assert_invalid_single_spec("this doesn't make sense^1");
+
+ test_object("be3563a^1", "9fd738e8f7967c078dceed8190330fc8648ee56a");
+ test_object("be3563a^", "9fd738e8f7967c078dceed8190330fc8648ee56a");
+ test_object("be3563a^2", "c47800c7266a2be04c571c04d5a6614691ea99bd");
+ test_object("be3563a^1^1", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045");
+ test_object("be3563a^^", "4a202b346bb0fb0db7eff3cffeb3c70babbd2045");
+ test_object("be3563a^2^1", "5b5b025afb0b4c913b4c338a42934a3863bf3644");
+ test_object("be3563a^0", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("be3563a^{commit}^", "9fd738e8f7967c078dceed8190330fc8648ee56a");
+
+ test_object("be3563a^42", NULL);
+}
+
+void test_refs_revparse__not_tag(void)
+{
+ test_object("point_to_blob^{}", "1385f264afb75a56a5bec74243be9b367ba4ca08");
+ test_object("wrapped_tag^{}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("master^{}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("master^{tree}^{}", "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162");
+ test_object("e90810b^{}", "e90810b8df3e80c413d903f631643c716887138d");
+ test_object("tags/e90810b^{}", "e90810b8df3e80c413d903f631643c716887138d");
+ test_object("e908^{}", "e90810b8df3e80c413d903f631643c716887138d");
+}
+
+void test_refs_revparse__to_type(void)
+{
+ assert_invalid_single_spec("wrapped_tag^{trip}");
+ test_object("point_to_blob^{commit}", NULL);
+ cl_assert_equal_i(
+ GIT_EPEEL, git_revparse_single(&g_obj, g_repo, "wrapped_tag^{blob}"));
+
+ test_object("wrapped_tag^{commit}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("wrapped_tag^{tree}", "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162");
+ test_object("point_to_blob^{blob}", "1385f264afb75a56a5bec74243be9b367ba4ca08");
+ test_object("master^{commit}^{commit}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+}
+
+void test_refs_revparse__linear_history(void)
+{
+ assert_invalid_single_spec("~");
+ test_object("foo~bar", NULL);
+
+ assert_invalid_single_spec("master~bar");
+ assert_invalid_single_spec("master~-1");
+ assert_invalid_single_spec("master~0bar");
+ assert_invalid_single_spec("this doesn't make sense~2");
+ assert_invalid_single_spec("be3563a^{tree}~");
+ assert_invalid_single_spec("point_to_blob^{blob}~");
+
+ test_object("master~0", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("master~1", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("master~2", "9fd738e8f7967c078dceed8190330fc8648ee56a");
+ test_object("master~1~1", "9fd738e8f7967c078dceed8190330fc8648ee56a");
+ test_object("master~~", "9fd738e8f7967c078dceed8190330fc8648ee56a");
+}
+
+void test_refs_revparse__chaining(void)
+{
+ assert_invalid_single_spec("master@{0}@{0}");
+ assert_invalid_single_spec("@{u}@{-1}");
+ assert_invalid_single_spec("@{-1}@{-1}");
+ assert_invalid_single_spec("@{-3}@{0}");
+
+ test_object("master@{0}~1^1", "9fd738e8f7967c078dceed8190330fc8648ee56a");
+ test_object("@{u}@{0}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("@{-1}@{0}", "a4a7dce85cf63874e984719f4fdd239f5145052f");
+ test_object("@{-4}@{1}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("master~1^1", "9fd738e8f7967c078dceed8190330fc8648ee56a");
+ test_object("master~1^2", "c47800c7266a2be04c571c04d5a6614691ea99bd");
+ test_object("master^1^2~1", "5b5b025afb0b4c913b4c338a42934a3863bf3644");
+ test_object("master^^2^", "5b5b025afb0b4c913b4c338a42934a3863bf3644");
+ test_object("master^1^1^1^1^1", "8496071c1b46c854b31185ea97743be6a8774479");
+ test_object("master^^1^2^1", NULL);
+}
+
+void test_refs_revparse__upstream(void)
+{
+ assert_invalid_single_spec("e90810b@{u}");
+ assert_invalid_single_spec("refs/tags/e90810b@{u}");
+ test_object("refs/heads/e90810b@{u}", NULL);
+
+ test_object("master@{upstream}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("@{u}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("master@{u}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("heads/master@{u}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("refs/heads/master@{u}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+}
+
+void test_refs_revparse__ordinal(void)
+{
+ assert_invalid_single_spec("master@{-2}");
+
+ /* TODO: make the test below actually fail
+ * cl_git_fail(git_revparse_single(&g_obj, g_repo, "master@{1a}"));
+ */
+
+ test_object("nope@{0}", NULL);
+ test_object("master@{31415}", NULL);
+ test_object("@{1000}", NULL);
+ test_object("@{2}", NULL);
+
+ test_object("@{0}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("@{1}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+
+ test_object("HEAD@{0}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("HEAD@{4}", "5b5b025afb0b4c913b4c338a42934a3863bf3644");
+
+ test_object("master@{0}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("master@{1}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("heads/master@{1}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("refs/heads/master@{1}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+}
+
+void test_refs_revparse__previous_head(void)
+{
+ assert_invalid_single_spec("@{-xyz}");
+ assert_invalid_single_spec("@{-0}");
+ assert_invalid_single_spec("@{-1b}");
+
+ test_object("@{-42}", NULL);
+
+ test_object("@{-2}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("@{-1}", "a4a7dce85cf63874e984719f4fdd239f5145052f");
+}
+
+static void create_fake_stash_reference_and_reflog(git_repository *repo)
+{
+ git_reference *master, *new_master;
+ git_str log_path = GIT_STR_INIT;
+
+ git_str_joinpath(&log_path, git_repository_path(repo), "logs/refs/fakestash");
+
+ cl_assert_equal_i(false, git_fs_path_isfile(git_str_cstr(&log_path)));
+
+ cl_git_pass(git_reference_lookup(&master, repo, "refs/heads/master"));
+ cl_git_pass(git_reference_rename(&new_master, master, "refs/fakestash", 0, NULL));
+ git_reference_free(master);
+
+ cl_assert_equal_i(true, git_fs_path_isfile(git_str_cstr(&log_path)));
+
+ git_str_dispose(&log_path);
+ git_reference_free(new_master);
+}
+
+void test_refs_revparse__reflog_of_a_ref_under_refs(void)
+{
+ git_repository *repo = cl_git_sandbox_init("testrepo.git");
+
+ test_object_inrepo("refs/fakestash", NULL, repo);
+
+ create_fake_stash_reference_and_reflog(repo);
+
+ /*
+ * $ git reflog -1 refs/fakestash
+ * a65fedf refs/fakestash@{0}: commit: checking in
+ *
+ * $ git reflog -1 refs/fakestash@{0}
+ * a65fedf refs/fakestash@{0}: commit: checking in
+ *
+ * $ git reflog -1 fakestash
+ * a65fedf fakestash@{0}: commit: checking in
+ *
+ * $ git reflog -1 fakestash@{0}
+ * a65fedf fakestash@{0}: commit: checking in
+ */
+ test_object_inrepo("refs/fakestash", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", repo);
+ test_object_inrepo("refs/fakestash@{0}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", repo);
+ test_object_inrepo("fakestash", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", repo);
+ test_object_inrepo("fakestash@{0}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", repo);
+
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_revparse__revwalk(void)
+{
+ test_object("master^{/not found in any commit}", NULL);
+ test_object("master^{/merge}", NULL);
+ assert_invalid_single_spec("master^{/((}");
+
+ test_object("master^{/anoth}", "5b5b025afb0b4c913b4c338a42934a3863bf3644");
+ test_object("master^{/Merge}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("br2^{/Merge}", "a4a7dce85cf63874e984719f4fdd239f5145052f");
+ test_object("master^{/fo.rth}", "9fd738e8f7967c078dceed8190330fc8648ee56a");
+}
+
+void test_refs_revparse__date(void)
+{
+ /*
+ * $ git reflog HEAD --date=iso
+ * a65fedf HEAD@{2012-04-30 08:23:41 -0900}: checkout: moving from br2 to master
+ * a4a7dce HEAD@{2012-04-30 08:23:37 -0900}: commit: checking in
+ * c47800c HEAD@{2012-04-30 08:23:28 -0900}: checkout: moving from master to br2
+ * a65fedf HEAD@{2012-04-30 08:23:23 -0900}: commit:
+ * be3563a HEAD@{2012-04-30 10:22:43 -0700}: clone: from /Users/ben/src/libgit2/tes
+ *
+ * $ git reflog HEAD --date=raw
+ * a65fedf HEAD@{1335806621 -0900}: checkout: moving from br2 to master
+ * a4a7dce HEAD@{1335806617 -0900}: commit: checking in
+ * c47800c HEAD@{1335806608 -0900}: checkout: moving from master to br2
+ * a65fedf HEAD@{1335806603 -0900}: commit:
+ * be3563a HEAD@{1335806563 -0700}: clone: from /Users/ben/src/libgit2/tests/resour
+ */
+ test_object("HEAD@{1 second}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("HEAD@{1 second ago}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("HEAD@{2 days ago}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+
+ /*
+ * $ git reflog master --date=iso
+ * a65fedf master@{2012-04-30 09:23:23 -0800}: commit: checking in
+ * be3563a master@{2012-04-30 09:22:43 -0800}: clone: from /Users/ben/src...
+ *
+ * $ git reflog master --date=raw
+ * a65fedf master@{1335806603 -0800}: commit: checking in
+ * be3563a master@{1335806563 -0800}: clone: from /Users/ben/src/libgit2/tests/reso
+ */
+
+
+ /*
+ * $ git rev-parse "master@{2012-04-30 17:22:42 +0000}"
+ * warning: log for 'master' only goes back to Mon, 30 Apr 2012 09:22:43 -0800
+ * be3563ae3f795b2b4353bcce3a527ad0a4f7f644
+ */
+ test_object("master@{2012-04-30 17:22:42 +0000}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("master@{2012-04-30 09:22:42 -0800}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+
+ /*
+ * $ git reflog -1 "master@{2012-04-30 17:22:43 +0000}"
+ * be3563a master@{Mon Apr 30 09:22:43 2012 -0800}: clone: from /Users/ben/src/libg
+ */
+ test_object("master@{2012-04-30 17:22:43 +0000}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+ test_object("master@{2012-04-30 09:22:43 -0800}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+
+ /*
+ * $ git reflog -1 "master@{2012-4-30 09:23:27 -0800}"
+ * a65fedf master@{Mon Apr 30 09:23:23 2012 -0800}: commit: checking in
+ */
+ test_object("master@{2012-4-30 09:23:27 -0800}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+
+ /*
+ * $ git reflog -1 master@{2012-05-03}
+ * a65fedf master@{Mon Apr 30 09:23:23 2012 -0800}: commit: checking in
+ */
+ test_object("master@{2012-05-03}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+
+ /*
+ * $ git reflog -1 "master@{1335806603}"
+ * a65fedf
+ *
+ * $ git reflog -1 "master@{1335806602}"
+ * be3563a
+ */
+ test_object("master@{1335806603}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+ test_object("master@{1335806602}", "be3563ae3f795b2b4353bcce3a527ad0a4f7f644");
+
+ /*
+ * $ git rev-parse "with-empty-log@{2 days ago}" --
+ * fatal: log for refs/heads/with-empty-log is empty
+ */
+ test_object("with-empty-log@{2 days ago}", NULL);
+}
+
+void test_refs_revparse__invalid_date(void)
+{
+ /*
+ * $ git rev-parse HEAD@{} --
+ * fatal: bad revision 'HEAD@{}'
+ *
+ * $ git rev-parse HEAD@{NEITHER_INTEGER_NOR_DATETIME} --
+ * fatal: bad revision 'HEAD@{NEITHER_INTEGER_NOR_DATETIME}'
+ */
+ test_object("HEAD@{}", NULL);
+ test_object("HEAD@{NEITHER_INTEGER_NOR_DATETIME}", NULL);
+}
+
+void test_refs_revparse__colon(void)
+{
+ assert_invalid_single_spec(":/");
+ assert_invalid_single_spec("point_to_blob:readme.txt");
+ cl_git_fail(git_revparse_single(&g_obj, g_repo, ":2:README")); /* Not implemented */
+
+ test_object(":/not found in any commit", NULL);
+ test_object("subtrees:ab/42.txt", NULL);
+ test_object("subtrees:ab/4.txt/nope", NULL);
+ test_object("subtrees:nope", NULL);
+ test_object("test/master^1:branch_file.txt", NULL);
+
+ /* From tags */
+ test_object("test:readme.txt", "0266163a49e280c4f5ed1e08facd36a2bd716bcf");
+ test_object("tags/test:readme.txt", "0266163a49e280c4f5ed1e08facd36a2bd716bcf");
+ test_object("e90810b:readme.txt", "0266163a49e280c4f5ed1e08facd36a2bd716bcf");
+ test_object("tags/e90810b:readme.txt", "0266163a49e280c4f5ed1e08facd36a2bd716bcf");
+
+ /* From commits */
+ test_object("a65f:branch_file.txt", "3697d64be941a53d4ae8f6a271e4e3fa56b022cc");
+
+ /* From trees */
+ test_object("a65f^{tree}:branch_file.txt", "3697d64be941a53d4ae8f6a271e4e3fa56b022cc");
+ test_object("944c:branch_file.txt", "3697d64be941a53d4ae8f6a271e4e3fa56b022cc");
+
+ /* Retrieving trees */
+ test_object("master:", "944c0f6e4dfa41595e6eb3ceecdb14f50fe18162");
+ test_object("subtrees:", "ae90f12eea699729ed24555e40b9fd669da12a12");
+ test_object("subtrees:ab", "f1425cef211cc08caa31e7b545ffb232acb098c3");
+ test_object("subtrees:ab/", "f1425cef211cc08caa31e7b545ffb232acb098c3");
+
+ /* Retrieving blobs */
+ test_object("subtrees:ab/4.txt", "d6c93164c249c8000205dd4ec5cbca1b516d487f");
+ test_object("subtrees:ab/de/fgh/1.txt", "1f67fc4386b2d171e0d21be1c447e12660561f9b");
+ test_object("master:README", "a8233120f6ad708f843d861ce2b7228ec4e3dec6");
+ test_object("master:new.txt", "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd");
+ test_object(":/Merge", "a4a7dce85cf63874e984719f4fdd239f5145052f");
+ test_object(":/one", "c47800c7266a2be04c571c04d5a6614691ea99bd");
+ test_object(":/packed commit t", "41bc8c69075bbdb46c5c6f0566cc8cc5b46e8bd9");
+ test_object("test/master^2:branch_file.txt", "45b983be36b73c0788dc9cbcb76cbb80fc7bb057");
+ test_object("test/master@{1}:branch_file.txt", "3697d64be941a53d4ae8f6a271e4e3fa56b022cc");
+}
+
+void test_refs_revparse__disambiguation(void)
+{
+ /*
+ * $ git show e90810b
+ * tag e90810b
+ * Tagger: Vicent Marti <tanoku@gmail.com>
+ * Date: Thu Aug 12 03:59:17 2010 +0200
+ *
+ * This is a very simple tag.
+ *
+ * commit e90810b8df3e80c413d903f631643c716887138d
+ * Author: Vicent Marti <tanoku@gmail.com>
+ * Date: Thu Aug 5 18:42:20 2010 +0200
+ *
+ * Test commit 2
+ *
+ * diff --git a/readme.txt b/readme.txt
+ * index 6336846..0266163 100644
+ * --- a/readme.txt
+ * +++ b/readme.txt
+ * @@ -1 +1,2 @@
+ * Testing a readme.txt
+ * +Now we add a single line here
+ *
+ * $ git show-ref e90810b
+ * 7b4384978d2493e851f9cca7858815fac9b10980 refs/tags/e90810b
+ *
+ */
+ test_object("e90810b", "7b4384978d2493e851f9cca7858815fac9b10980");
+
+ /*
+ * $ git show e90810
+ * commit e90810b8df3e80c413d903f631643c716887138d
+ * Author: Vicent Marti <tanoku@gmail.com>
+ * Date: Thu Aug 5 18:42:20 2010 +0200
+ *
+ * Test commit 2
+ *
+ * diff --git a/readme.txt b/readme.txt
+ * index 6336846..0266163 100644
+ * --- a/readme.txt
+ * +++ b/readme.txt
+ * @@ -1 +1,2 @@
+ * Testing a readme.txt
+ * +Now we add a single line here
+ */
+ test_object("e90810", "e90810b8df3e80c413d903f631643c716887138d");
+}
+
+void test_refs_revparse__a_too_short_objectid_returns_EAMBIGUOUS(void)
+{
+ cl_assert_equal_i(
+ GIT_EAMBIGUOUS, git_revparse_single(&g_obj, g_repo, "e90"));
+}
+
+/*
+ * $ echo "aabqhq" | git hash-object -t blob --stdin
+ * dea509d0b3cb8ee0650f6ca210bc83f4678851ba
+ *
+ * $ echo "aaazvc" | git hash-object -t blob --stdin
+ * dea509d097ce692e167dfc6a48a7a280cc5e877e
+ */
+void test_refs_revparse__a_not_precise_enough_objectid_returns_EAMBIGUOUS(void)
+{
+ git_repository *repo;
+ git_index *index;
+ git_object *obj;
+
+ repo = cl_git_sandbox_init("testrepo");
+
+ cl_git_mkfile("testrepo/one.txt", "aabqhq\n");
+ cl_git_mkfile("testrepo/two.txt", "aaazvc\n");
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_add_bypath(index, "one.txt"));
+ cl_git_pass(git_index_add_bypath(index, "two.txt"));
+
+ cl_git_fail_with(git_revparse_single(&obj, repo, "dea509d0"), GIT_EAMBIGUOUS);
+
+ cl_git_pass(git_revparse_single(&obj, repo, "dea509d09"));
+
+ git_object_free(obj);
+ git_index_free(index);
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_revparse__issue_994(void)
+{
+ git_repository *repo;
+ git_reference *head, *with_at;
+ git_object *target;
+
+ repo = cl_git_sandbox_init("testrepo.git");
+
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_revparse_single(&target, repo, "origin/bim_with_3d@11296"));
+
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_revparse_single(&target, repo, "refs/remotes/origin/bim_with_3d@11296"));
+
+
+ cl_git_pass(git_repository_head(&head, repo));
+ cl_git_pass(git_reference_create(
+ &with_at,
+ repo,
+ "refs/remotes/origin/bim_with_3d@11296",
+ git_reference_target(head),
+ 0,
+ NULL));
+
+ cl_git_pass(git_revparse_single(&target, repo, "origin/bim_with_3d@11296"));
+ git_object_free(target);
+
+ cl_git_pass(git_revparse_single(&target, repo, "refs/remotes/origin/bim_with_3d@11296"));
+ git_object_free(target);
+
+ git_reference_free(with_at);
+ git_reference_free(head);
+ cl_git_sandbox_cleanup();
+}
+
+/**
+ * $ git rev-parse blah-7-gc47800c
+ * c47800c7266a2be04c571c04d5a6614691ea99bd
+ *
+ * $ git rev-parse HEAD~3
+ * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
+ *
+ * $ git branch blah-7-gc47800c HEAD~3
+ *
+ * $ git rev-parse blah-7-gc47800c
+ * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
+ */
+void test_refs_revparse__try_to_retrieve_branch_before_described_tag(void)
+{
+ git_repository *repo;
+ git_reference *branch;
+ git_object *target;
+ char sha[GIT_OID_SHA1_HEXSIZE + 1];
+
+ repo = cl_git_sandbox_init("testrepo.git");
+
+ test_object_inrepo("blah-7-gc47800c", "c47800c7266a2be04c571c04d5a6614691ea99bd", repo);
+
+ cl_git_pass(git_revparse_single(&target, repo, "HEAD~3"));
+ cl_git_pass(git_branch_create(&branch, repo, "blah-7-gc47800c", (git_commit *)target, 0));
+
+ git_oid_tostr(sha, GIT_OID_SHA1_HEXSIZE + 1, git_object_id(target));
+
+ test_object_inrepo("blah-7-gc47800c", sha, repo);
+
+ git_reference_free(branch);
+ git_object_free(target);
+ cl_git_sandbox_cleanup();
+}
+
+/**
+ * $ git rev-parse a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+ * a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+ *
+ * $ git rev-parse HEAD~3
+ * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
+ *
+ * $ git branch a65fedf39aefe402d3bb6e24df4d4f5fe4547750 HEAD~3
+ *
+ * $ git rev-parse a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+ * a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+ *
+ * $ git rev-parse heads/a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+ * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
+ */
+void test_refs_revparse__try_to_retrieve_sha_before_branch(void)
+{
+ git_repository *repo;
+ git_reference *branch;
+ git_object *target;
+ char sha[GIT_OID_SHA1_HEXSIZE + 1];
+
+ repo = cl_git_sandbox_init("testrepo.git");
+
+ test_object_inrepo("a65fedf39aefe402d3bb6e24df4d4f5fe4547750", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", repo);
+
+ cl_git_pass(git_revparse_single(&target, repo, "HEAD~3"));
+ cl_git_pass(git_branch_create(&branch, repo, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", (git_commit *)target, 0));
+
+ git_oid_tostr(sha, GIT_OID_SHA1_HEXSIZE + 1, git_object_id(target));
+
+ test_object_inrepo("a65fedf39aefe402d3bb6e24df4d4f5fe4547750", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", repo);
+ test_object_inrepo("heads/a65fedf39aefe402d3bb6e24df4d4f5fe4547750", sha, repo);
+
+ git_reference_free(branch);
+ git_object_free(target);
+ cl_git_sandbox_cleanup();
+}
+
+/**
+ * $ git rev-parse c47800
+ * c47800c7266a2be04c571c04d5a6614691ea99bd
+ *
+ * $ git rev-parse HEAD~3
+ * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
+ *
+ * $ git branch c47800 HEAD~3
+ *
+ * $ git rev-parse c47800
+ * 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
+ */
+void test_refs_revparse__try_to_retrieve_branch_before_abbrev_sha(void)
+{
+ git_repository *repo;
+ git_reference *branch;
+ git_object *target;
+ char sha[GIT_OID_SHA1_HEXSIZE + 1];
+
+ repo = cl_git_sandbox_init("testrepo.git");
+
+ test_object_inrepo("c47800", "c47800c7266a2be04c571c04d5a6614691ea99bd", repo);
+
+ cl_git_pass(git_revparse_single(&target, repo, "HEAD~3"));
+ cl_git_pass(git_branch_create(&branch, repo, "c47800", (git_commit *)target, 0));
+
+ git_oid_tostr(sha, GIT_OID_SHA1_HEXSIZE + 1, git_object_id(target));
+
+ test_object_inrepo("c47800", sha, repo);
+
+ git_reference_free(branch);
+ git_object_free(target);
+ cl_git_sandbox_cleanup();
+}
+
+
+void test_refs_revparse__range(void)
+{
+ assert_invalid_single_spec("be3563a^1..be3563a");
+
+ test_rangelike("be3563a^1..be3563a",
+ "9fd738e8f7967c078dceed8190330fc8648ee56a",
+ "be3563ae3f795b2b4353bcce3a527ad0a4f7f644",
+ GIT_REVSPEC_RANGE);
+
+ test_rangelike("be3563a^1...be3563a",
+ "9fd738e8f7967c078dceed8190330fc8648ee56a",
+ "be3563ae3f795b2b4353bcce3a527ad0a4f7f644",
+ GIT_REVSPEC_RANGE | GIT_REVSPEC_MERGE_BASE);
+
+ test_rangelike("be3563a^1.be3563a", NULL, NULL, 0);
+}
+
+void test_refs_revparse__parses_range_operator(void)
+{
+ test_id("HEAD", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", NULL, GIT_REVSPEC_SINGLE);
+ test_id("HEAD~3..HEAD",
+ "4a202b346bb0fb0db7eff3cffeb3c70babbd2045",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ GIT_REVSPEC_RANGE);
+
+ test_id("HEAD~3...HEAD",
+ "4a202b346bb0fb0db7eff3cffeb3c70babbd2045",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ GIT_REVSPEC_RANGE | GIT_REVSPEC_MERGE_BASE);
+
+ test_id("HEAD~3..",
+ "4a202b346bb0fb0db7eff3cffeb3c70babbd2045",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ GIT_REVSPEC_RANGE);
+
+ test_id("HEAD~3...",
+ "4a202b346bb0fb0db7eff3cffeb3c70babbd2045",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ GIT_REVSPEC_RANGE | GIT_REVSPEC_MERGE_BASE);
+
+ test_id("..HEAD~3",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ "4a202b346bb0fb0db7eff3cffeb3c70babbd2045",
+ GIT_REVSPEC_RANGE);
+
+ test_id("...HEAD~3",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ "4a202b346bb0fb0db7eff3cffeb3c70babbd2045",
+ GIT_REVSPEC_RANGE | GIT_REVSPEC_MERGE_BASE);
+
+ test_id("...",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ GIT_REVSPEC_RANGE | GIT_REVSPEC_MERGE_BASE);
+
+ test_invalid_revspec("..");
+}
+
+void test_refs_revparse__ext_retrieves_both_the_reference_and_its_target(void)
+{
+ test_object_and_ref(
+ "master@{upstream}",
+ "be3563ae3f795b2b4353bcce3a527ad0a4f7f644",
+ "refs/remotes/test/master");
+
+ test_object_and_ref(
+ "@{-1}",
+ "a4a7dce85cf63874e984719f4fdd239f5145052f",
+ "refs/heads/br2");
+}
+
+void test_refs_revparse__ext_can_expand_short_reference_names(void)
+{
+ test_object_and_ref(
+ "master",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ "refs/heads/master");
+
+ test_object_and_ref(
+ "HEAD",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ "refs/heads/master");
+
+ test_object_and_ref(
+ "tags/test",
+ "b25fa35b38051e4ae45d4222e795f9df2e43f1d1",
+ "refs/tags/test");
+}
+
+void test_refs_revparse__ext_returns_NULL_reference_when_expression_points_at_a_revision(void)
+{
+ test_object_and_ref(
+ "HEAD~3",
+ "4a202b346bb0fb0db7eff3cffeb3c70babbd2045",
+ NULL);
+
+ test_object_and_ref(
+ "HEAD~0",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ NULL);
+
+ test_object_and_ref(
+ "HEAD^0",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750",
+ NULL);
+
+ test_object_and_ref(
+ "@{-1}@{0}",
+ "a4a7dce85cf63874e984719f4fdd239f5145052f",
+ NULL);
+}
+
+void test_refs_revparse__ext_returns_NULL_reference_when_expression_points_at_a_tree_content(void)
+{
+ test_object_and_ref(
+ "tags/test:readme.txt",
+ "0266163a49e280c4f5ed1e08facd36a2bd716bcf",
+ NULL);
+}
+
+void test_refs_revparse__uneven_sizes(void)
+{
+ test_object("a65fedf39aefe402d3bb6e24df4d4f5fe454775",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+
+ test_object("a65fedf39aefe402d3bb6e24df4d4f5fe45477",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+
+ test_object("a65fedf39aefe402d3bb6e24df4d4f5fe4547",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+
+ test_object("a65fedf39aefe402d3bb6e24df4d",
+ "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
+}
+
+void test_refs_revparse__parses_at_head(void)
+{
+ test_id("HEAD", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", NULL, GIT_REVSPEC_SINGLE);
+ test_id("@{0}", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", NULL, GIT_REVSPEC_SINGLE);
+ test_id("@", "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", NULL, GIT_REVSPEC_SINGLE);
+}
diff --git a/tests/libgit2/refs/setter.c b/tests/libgit2/refs/setter.c
new file mode 100644
index 0000000..b34c71e
--- /dev/null
+++ b/tests/libgit2/refs/setter.c
@@ -0,0 +1,99 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+#include "git2/reflog.h"
+#include "reflog.h"
+#include "git2/refs.h"
+
+static const char *ref_name = "refs/heads/other";
+static const char *ref_master_name = "refs/heads/master";
+static const char *ref_test_name = "refs/heads/test";
+
+static git_repository *g_repo;
+
+void test_refs_setter__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_refs_setter__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_setter__update_direct(void)
+{
+ git_reference *ref, *test_ref, *new_ref;
+ git_oid id;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name));
+ cl_assert(git_reference_type(ref) == GIT_REFERENCE_DIRECT);
+ git_oid_cpy(&id, git_reference_target(ref));
+ git_reference_free(ref);
+
+ cl_git_pass(git_reference_lookup(&test_ref, g_repo, ref_test_name));
+ cl_assert(git_reference_type(test_ref) == GIT_REFERENCE_DIRECT);
+
+ cl_git_pass(git_reference_set_target(&new_ref, test_ref, &id, NULL));
+
+ git_reference_free(test_ref);
+ git_reference_free(new_ref);
+
+ cl_git_pass(git_reference_lookup(&test_ref, g_repo, ref_test_name));
+ cl_assert(git_reference_type(test_ref) == GIT_REFERENCE_DIRECT);
+ cl_assert_equal_oid(&id, git_reference_target(test_ref));
+ git_reference_free(test_ref);
+}
+
+void test_refs_setter__update_symbolic(void)
+{
+ git_reference *head, *new_head;
+
+ cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
+ cl_assert(git_reference_type(head) == GIT_REFERENCE_SYMBOLIC);
+ cl_assert(strcmp(git_reference_symbolic_target(head), ref_master_name) == 0);
+
+ cl_git_pass(git_reference_symbolic_set_target(&new_head, head, ref_test_name, NULL));
+ git_reference_free(new_head);
+ git_reference_free(head);
+
+ cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
+ cl_assert(git_reference_type(head) == GIT_REFERENCE_SYMBOLIC);
+ cl_assert(strcmp(git_reference_symbolic_target(head), ref_test_name) == 0);
+ git_reference_free(head);
+}
+
+void test_refs_setter__cant_update_direct_with_symbolic(void)
+{
+ /* Overwrite an existing object id reference with a symbolic one */
+ git_reference *ref, *new;
+ git_oid id;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name));
+ cl_assert(git_reference_type(ref) == GIT_REFERENCE_DIRECT);
+ git_oid_cpy(&id, git_reference_target(ref));
+
+ cl_git_fail(git_reference_symbolic_set_target(&new, ref, ref_name, NULL));
+
+ git_reference_free(ref);
+}
+
+void test_refs_setter__cant_update_symbolic_with_direct(void)
+{
+ /* Overwrite an existing symbolic reference with an object id one */
+ git_reference *ref, *new;
+ git_oid id;
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, ref_master_name));
+ cl_assert(git_reference_type(ref) == GIT_REFERENCE_DIRECT);
+ git_oid_cpy(&id, git_reference_target(ref));
+ git_reference_free(ref);
+
+ /* Create the symbolic ref */
+ cl_git_pass(git_reference_symbolic_create(&ref, g_repo, ref_name, ref_master_name, 0, NULL));
+
+ /* Can't set an OID on a direct ref */
+ cl_git_fail(git_reference_set_target(&new, ref, &id, NULL));
+
+ git_reference_free(ref);
+}
diff --git a/tests/libgit2/refs/shorthand.c b/tests/libgit2/refs/shorthand.c
new file mode 100644
index 0000000..e008adc
--- /dev/null
+++ b/tests/libgit2/refs/shorthand.c
@@ -0,0 +1,27 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+
+static void assert_shorthand(git_repository *repo, const char *refname, const char *shorthand)
+{
+ git_reference *ref;
+
+ cl_git_pass(git_reference_lookup(&ref, repo, refname));
+ cl_assert_equal_s(git_reference_shorthand(ref), shorthand);
+ git_reference_free(ref);
+}
+
+void test_refs_shorthand__0(void)
+{
+ git_repository *repo;
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+
+
+ assert_shorthand(repo, "refs/heads/master", "master");
+ assert_shorthand(repo, "refs/tags/test", "test");
+ assert_shorthand(repo, "refs/remotes/test/master", "test/master");
+ assert_shorthand(repo, "refs/notes/fanout", "notes/fanout");
+
+ git_repository_free(repo);
+}
diff --git a/tests/libgit2/refs/tags/name.c b/tests/libgit2/refs/tags/name.c
new file mode 100644
index 0000000..1dd1760
--- /dev/null
+++ b/tests/libgit2/refs/tags/name.c
@@ -0,0 +1,17 @@
+#include "clar_libgit2.h"
+
+static int name_is_valid(const char *name)
+{
+ int valid;
+ cl_git_pass(git_tag_name_is_valid(&valid, name));
+ return valid;
+}
+
+void test_refs_tags_name__is_name_valid(void)
+{
+ cl_assert_equal_i(true, name_is_valid("sometag"));
+ cl_assert_equal_i(true, name_is_valid("test/sometag"));
+
+ cl_assert_equal_i(false, name_is_valid(""));
+ cl_assert_equal_i(false, name_is_valid("-dash"));
+}
diff --git a/tests/libgit2/refs/transactions.c b/tests/libgit2/refs/transactions.c
new file mode 100644
index 0000000..98ae6f7
--- /dev/null
+++ b/tests/libgit2/refs/transactions.c
@@ -0,0 +1,157 @@
+#include "clar_libgit2.h"
+#include "git2/transaction.h"
+
+static git_repository *g_repo;
+static git_transaction *g_tx;
+
+void test_refs_transactions__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+ cl_git_pass(git_transaction_new(&g_tx, g_repo));
+}
+
+void test_refs_transactions__cleanup(void)
+{
+ git_transaction_free(g_tx);
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_transactions__single_ref_oid(void)
+{
+ git_reference *ref;
+ git_oid id;
+
+ git_oid__fromstr(&id, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", GIT_OID_SHA1);
+
+ cl_git_pass(git_transaction_lock_ref(g_tx, "refs/heads/master"));
+ cl_git_pass(git_transaction_set_target(g_tx, "refs/heads/master", &id, NULL, NULL));
+ cl_git_pass(git_transaction_commit(g_tx));
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/heads/master"));
+
+ cl_assert(!git_oid_cmp(&id, git_reference_target(ref)));
+ git_reference_free(ref);
+}
+
+void test_refs_transactions__single_ref_symbolic(void)
+{
+ git_reference *ref;
+
+ cl_git_pass(git_transaction_lock_ref(g_tx, "HEAD"));
+ cl_git_pass(git_transaction_set_symbolic_target(g_tx, "HEAD", "refs/heads/foo", NULL, NULL));
+ cl_git_pass(git_transaction_commit(g_tx));
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, "HEAD"));
+
+ cl_assert_equal_s("refs/heads/foo", git_reference_symbolic_target(ref));
+ git_reference_free(ref);
+}
+
+void test_refs_transactions__single_ref_mix_types(void)
+{
+ git_reference *ref;
+ git_oid id;
+
+ git_oid__fromstr(&id, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", GIT_OID_SHA1);
+
+ cl_git_pass(git_transaction_lock_ref(g_tx, "refs/heads/master"));
+ cl_git_pass(git_transaction_lock_ref(g_tx, "HEAD"));
+ cl_git_pass(git_transaction_set_symbolic_target(g_tx, "refs/heads/master", "refs/heads/foo", NULL, NULL));
+ cl_git_pass(git_transaction_set_target(g_tx, "HEAD", &id, NULL, NULL));
+ cl_git_pass(git_transaction_commit(g_tx));
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, "refs/heads/master"));
+ cl_assert_equal_s("refs/heads/foo", git_reference_symbolic_target(ref));
+ git_reference_free(ref);
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, "HEAD"));
+ cl_assert(!git_oid_cmp(&id, git_reference_target(ref)));
+ git_reference_free(ref);
+}
+
+void test_refs_transactions__single_ref_delete(void)
+{
+ git_reference *ref;
+
+ cl_git_pass(git_transaction_lock_ref(g_tx, "refs/heads/master"));
+ cl_git_pass(git_transaction_remove(g_tx, "refs/heads/master"));
+ cl_git_pass(git_transaction_commit(g_tx));
+
+ cl_git_fail_with(GIT_ENOTFOUND, git_reference_lookup(&ref, g_repo, "refs/heads/master"));
+}
+
+void test_refs_transactions__single_create(void)
+{
+ git_reference *ref;
+ const char *name = "refs/heads/new-branch";
+ git_oid id;
+
+ cl_git_fail_with(GIT_ENOTFOUND, git_reference_lookup(&ref, g_repo, name));
+
+ cl_git_pass(git_transaction_lock_ref(g_tx, name));
+
+ git_oid__fromstr(&id, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", GIT_OID_SHA1);
+ cl_git_pass(git_transaction_set_target(g_tx, name, &id, NULL, NULL));
+ cl_git_pass(git_transaction_commit(g_tx));
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, name));
+ cl_assert(!git_oid_cmp(&id, git_reference_target(ref)));
+ git_reference_free(ref);
+}
+
+void test_refs_transactions__unlocked_set(void)
+{
+ git_oid id;
+
+ cl_git_pass(git_transaction_lock_ref(g_tx, "refs/heads/master"));
+ git_oid__fromstr(&id, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", GIT_OID_SHA1);
+ cl_git_fail_with(GIT_ENOTFOUND, git_transaction_set_target(g_tx, "refs/heads/foo", &id, NULL, NULL));
+ cl_git_pass(git_transaction_commit(g_tx));
+}
+
+void test_refs_transactions__error_on_locking_locked_ref(void)
+{
+ git_oid id;
+ git_transaction *g_tx_with_lock;
+ git_repository *g_repo_with_locking_tx;
+ const char *g_repo_path = git_repository_path(g_repo);
+
+ /* prepare a separate transaction in another instance of testrepo and lock master */
+ cl_git_pass(git_repository_open(&g_repo_with_locking_tx, g_repo_path));
+ cl_git_pass(git_transaction_new(&g_tx_with_lock, g_repo_with_locking_tx));
+ cl_git_pass(git_transaction_lock_ref(g_tx_with_lock, "refs/heads/master"));
+
+ /* lock reference for set_target */
+ cl_git_pass(git_oid__fromstr(&id, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", GIT_OID_SHA1));
+ cl_git_fail_with(GIT_ELOCKED, git_transaction_lock_ref(g_tx, "refs/heads/master"));
+ cl_git_fail_with(GIT_ENOTFOUND, git_transaction_set_target(g_tx, "refs/heads/master", &id, NULL, NULL));
+
+ git_transaction_free(g_tx_with_lock);
+ git_repository_free(g_repo_with_locking_tx);
+}
+
+void test_refs_transactions__commit_unlocks_unmodified_ref(void)
+{
+ git_transaction *second_tx;
+
+ cl_git_pass(git_transaction_new(&second_tx, g_repo));
+ cl_git_pass(git_transaction_lock_ref(second_tx, "refs/heads/master"));
+ cl_git_pass(git_transaction_commit(second_tx));
+
+ /* a transaction must now be able to get the lock */
+ cl_git_pass(git_transaction_lock_ref(g_tx, "refs/heads/master"));
+
+ git_transaction_free(second_tx);
+}
+
+void test_refs_transactions__free_unlocks_unmodified_ref(void)
+{
+ git_transaction *second_tx;
+
+ cl_git_pass(git_transaction_new(&second_tx, g_repo));
+ cl_git_pass(git_transaction_lock_ref(second_tx, "refs/heads/master"));
+ git_transaction_free(second_tx);
+
+ /* a transaction must now be able to get the lock */
+ cl_git_pass(git_transaction_lock_ref(g_tx, "refs/heads/master"));
+}
diff --git a/tests/libgit2/refs/unicode.c b/tests/libgit2/refs/unicode.c
new file mode 100644
index 0000000..a279d50
--- /dev/null
+++ b/tests/libgit2/refs/unicode.c
@@ -0,0 +1,54 @@
+#include "clar_libgit2.h"
+
+static git_repository *repo;
+
+void test_refs_unicode__initialize(void)
+{
+ repo = cl_git_sandbox_init("testrepo.git");
+}
+
+void test_refs_unicode__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ repo = NULL;
+}
+
+void test_refs_unicode__create_and_lookup(void)
+{
+ git_reference *ref0, *ref1, *ref2;
+ git_repository *repo2;
+
+ const char *REFNAME = "refs/heads/" "\303\205" "ngstr" "\303\266" "m";
+ const char *master = "refs/heads/master";
+
+ /* Create the reference */
+ cl_git_pass(git_reference_lookup(&ref0, repo, master));
+ cl_git_pass(git_reference_create(
+ &ref1, repo, REFNAME, git_reference_target(ref0), 0, NULL));
+ cl_assert_equal_s(REFNAME, git_reference_name(ref1));
+ git_reference_free(ref0);
+
+ /* Lookup the reference in a different instance of the repository */
+ cl_git_pass(git_repository_open(&repo2, "testrepo.git"));
+
+ cl_git_pass(git_reference_lookup(&ref2, repo2, REFNAME));
+ cl_assert_equal_oid(git_reference_target(ref1), git_reference_target(ref2));
+ cl_assert_equal_s(REFNAME, git_reference_name(ref2));
+ git_reference_free(ref2);
+
+#if GIT_USE_ICONV
+ /* Lookup reference by decomposed unicode name */
+
+#define REFNAME_DECOMPOSED "refs/heads/" "A" "\314\212" "ngstro" "\314\210" "m"
+
+ cl_git_pass(git_reference_lookup(&ref2, repo2, REFNAME_DECOMPOSED));
+ cl_assert_equal_oid(git_reference_target(ref1), git_reference_target(ref2));
+ cl_assert_equal_s(REFNAME, git_reference_name(ref2));
+ git_reference_free(ref2);
+#endif
+
+ /* Cleanup */
+
+ git_reference_free(ref1);
+ git_repository_free(repo2);
+}
diff --git a/tests/libgit2/refs/update.c b/tests/libgit2/refs/update.c
new file mode 100644
index 0000000..1c5127d
--- /dev/null
+++ b/tests/libgit2/refs/update.c
@@ -0,0 +1,26 @@
+#include "clar_libgit2.h"
+
+#include "refs.h"
+
+static git_repository *g_repo;
+
+void test_refs_update__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo.git");
+}
+
+void test_refs_update__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_refs_update__updating_the_target_of_a_symref_with_an_invalid_name_returns_EINVALIDSPEC(void)
+{
+ git_reference *head;
+
+ cl_git_pass(git_reference_lookup(&head, g_repo, GIT_HEAD_FILE));
+ cl_assert_equal_i(GIT_REFERENCE_SYMBOLIC, git_reference_type(head));
+ git_reference_free(head);
+
+ cl_assert_equal_i(GIT_EINVALIDSPEC, git_reference_symbolic_create(&head, g_repo, GIT_HEAD_FILE, "refs/heads/inv@{id", 1, NULL));
+}
diff --git a/tests/libgit2/remote/create.c b/tests/libgit2/remote/create.c
new file mode 100644
index 0000000..f92be9d
--- /dev/null
+++ b/tests/libgit2/remote/create.c
@@ -0,0 +1,388 @@
+#include "clar_libgit2.h"
+#include "config/config_helpers.h"
+
+static git_repository *_repo;
+static git_config *_config;
+
+#define TEST_URL "http://github.com/libgit2/libgit2.git"
+
+void test_remote_create__initialize(void)
+{
+ cl_fixture_sandbox("testrepo.git");
+
+ cl_git_pass(git_repository_open(&_repo, "testrepo.git"));
+
+ cl_git_pass(git_repository_config(&_config, _repo));
+}
+
+void test_remote_create__cleanup(void)
+{
+ git_config_free(_config);
+
+ git_repository_free(_repo);
+
+ cl_fixture_cleanup("testrepo.git");
+}
+
+void test_remote_create__manual(void)
+{
+ git_remote *remote;
+ cl_git_pass(git_config_set_string(_config, "remote.origin.fetch", "+refs/heads/*:refs/remotes/origin/*"));
+ cl_git_pass(git_config_set_string(_config, "remote.origin.url", TEST_URL));
+
+ cl_git_pass(git_remote_lookup(&remote, _repo, "origin"));
+ cl_assert_equal_s(git_remote_name(remote), "origin");
+ cl_assert_equal_s(git_remote_url(remote), TEST_URL);
+
+ git_remote_free(remote);
+}
+
+void test_remote_create__named(void)
+{
+ git_remote *remote;
+ git_config *cfg;
+ const char *cfg_val;
+
+ size_t section_count = count_config_entries_match(_repo, "remote\\.");
+
+ cl_git_pass(git_remote_create(&remote, _repo, "valid-name", TEST_URL));
+
+ cl_assert_equal_s(git_remote_name(remote), "valid-name");
+ cl_assert_equal_s(git_remote_url(remote), TEST_URL);
+ cl_assert_equal_p(git_remote_owner(remote), _repo);
+
+ cl_git_pass(git_repository_config_snapshot(&cfg, _repo));
+
+ cl_git_pass(git_config_get_string(&cfg_val, cfg, "remote.valid-name.fetch"));
+ cl_assert_equal_s(cfg_val, "+refs/heads/*:refs/remotes/valid-name/*");
+
+ cl_git_pass(git_config_get_string(&cfg_val, cfg, "remote.valid-name.url"));
+ cl_assert_equal_s(cfg_val, TEST_URL);
+
+ cl_assert_equal_i(section_count + 2, count_config_entries_match(_repo, "remote\\."));
+
+ git_config_free(cfg);
+ git_remote_free(remote);
+}
+
+void test_remote_create__named_fail_on_invalid_name(void)
+{
+ const char *names[] = {
+ NULL,
+ "Inv@{id",
+ "",
+ "/",
+ "//",
+ ".lock",
+ "a.lock",
+ };
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(names); i++) {
+ git_remote *remote = NULL;
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_remote_create(&remote, _repo, names[i], TEST_URL));
+ cl_assert_equal_p(remote, NULL);
+ }
+}
+
+void test_remote_create__named_fail_on_invalid_url(void)
+{
+ git_remote *remote = NULL;
+
+ cl_git_fail_with(GIT_ERROR, git_remote_create(&remote, _repo, "bad-url", ""));
+ cl_assert_equal_p(remote, NULL);
+}
+
+void test_remote_create__named_fail_on_conflicting_name(void)
+{
+ git_remote *remote = NULL;
+
+ cl_git_fail_with(GIT_EEXISTS, git_remote_create(&remote, _repo, "test", TEST_URL));
+ cl_assert_equal_p(remote, NULL);
+}
+
+void test_remote_create__with_fetchspec(void)
+{
+ git_remote *remote;
+ git_strarray array;
+ size_t section_count = count_config_entries_match(_repo, "remote\\.");
+
+ cl_git_pass(git_remote_create_with_fetchspec(&remote, _repo, "test-new", "git://github.com/libgit2/libgit2", "+refs/*:refs/*"));
+ cl_assert_equal_s(git_remote_name(remote), "test-new");
+ cl_assert_equal_s(git_remote_url(remote), "git://github.com/libgit2/libgit2");
+ cl_assert_equal_p(git_remote_owner(remote), _repo);
+
+ cl_git_pass(git_remote_get_fetch_refspecs(&array, remote));
+ cl_assert_equal_s("+refs/*:refs/*", array.strings[0]);
+ cl_assert_equal_i(1, array.count);
+ cl_assert_equal_i(section_count + 2, count_config_entries_match(_repo, "remote\\."));
+
+ git_strarray_dispose(&array);
+ git_remote_free(remote);
+}
+
+void test_remote_create__with_empty_fetchspec(void)
+{
+ git_remote *remote;
+ git_strarray array;
+ size_t section_count = count_config_entries_match(_repo, "remote\\.");
+
+ cl_git_pass(git_remote_create_with_fetchspec(&remote, _repo, "test-new", "git://github.com/libgit2/libgit2", NULL));
+ cl_git_pass(git_remote_get_fetch_refspecs(&array, remote));
+ cl_assert_equal_i(0, array.count);
+ cl_assert_equal_i(section_count + 1, count_config_entries_match(_repo, "remote\\."));
+
+ git_strarray_dispose(&array);
+ git_remote_free(remote);
+}
+
+void test_remote_create__with_fetchspec_invalid_name(void)
+{
+ git_remote *remote = NULL;
+
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_remote_create_with_fetchspec(&remote, _repo, NULL, TEST_URL, NULL));
+ cl_assert_equal_p(remote, NULL);
+}
+
+void test_remote_create__with_fetchspec_invalid_url(void)
+{
+ git_remote *remote = NULL;
+
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_remote_create_with_fetchspec(&remote, _repo, NULL, "", NULL));
+ cl_assert_equal_p(remote, NULL);
+}
+
+void test_remote_create__anonymous(void)
+{
+ git_remote *remote;
+ git_strarray array;
+ size_t section_count = count_config_entries_match(_repo, "remote\\.");
+
+ cl_git_pass(git_remote_create_anonymous(&remote, _repo, TEST_URL));
+ cl_assert_equal_s(git_remote_name(remote), NULL);
+ cl_assert_equal_s(git_remote_url(remote), TEST_URL);
+ cl_assert_equal_p(git_remote_owner(remote), _repo);
+
+ cl_git_pass(git_remote_get_fetch_refspecs(&array, remote));
+ cl_assert_equal_i(0, array.count);
+ cl_assert_equal_i(section_count, count_config_entries_match(_repo, "remote\\."));
+
+ git_strarray_dispose(&array);
+ git_remote_free(remote);
+}
+
+void test_remote_create__anonymous_invalid_url(void)
+{
+ git_remote *remote = NULL;
+
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_remote_create_anonymous(&remote, _repo, ""));
+ cl_assert_equal_p(remote, NULL);
+}
+
+void test_remote_create__detached(void)
+{
+ git_remote *remote;
+ git_strarray array;
+
+ size_t section_count = count_config_entries_match(_repo, "remote\\.");
+
+ cl_git_pass(git_remote_create_detached(&remote, TEST_URL));
+ cl_assert_equal_s(git_remote_name(remote), NULL);
+ cl_assert_equal_s(git_remote_url(remote), TEST_URL);
+ cl_assert_equal_p(git_remote_owner(remote), NULL);
+
+ cl_git_pass(git_remote_get_fetch_refspecs(&array, remote));
+ cl_assert_equal_i(0, array.count);
+ cl_assert_equal_i(section_count, count_config_entries_match(_repo, "remote\\."));
+
+ git_strarray_dispose(&array);
+ git_remote_free(remote);
+}
+
+void test_remote_create__detached_invalid_url(void)
+{
+ git_remote *remote = NULL;
+
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_remote_create_detached(&remote, ""));
+ cl_assert_equal_p(remote, NULL);
+}
+
+void test_remote_create__with_opts_named(void)
+{
+ git_remote *remote;
+ git_strarray array;
+ git_remote_create_options opts = GIT_REMOTE_CREATE_OPTIONS_INIT;
+
+ opts.name = "test-new";
+ opts.repository = _repo;
+
+ cl_git_pass(git_remote_create_with_opts(&remote, TEST_URL, &opts));
+ cl_assert_equal_s(git_remote_name(remote), "test-new");
+ cl_assert_equal_s(git_remote_url(remote), TEST_URL);
+ cl_assert_equal_p(git_remote_owner(remote), _repo);
+
+ cl_git_pass(git_remote_get_fetch_refspecs(&array, remote));
+ cl_assert_equal_i(1, array.count);
+ cl_assert_equal_s("+refs/heads/*:refs/remotes/test-new/*", array.strings[0]);
+
+ git_strarray_dispose(&array);
+ git_remote_free(remote);
+}
+
+void test_remote_create__with_opts_named_and_fetchspec(void)
+{
+ git_remote *remote;
+ git_strarray array;
+ git_remote_create_options opts = GIT_REMOTE_CREATE_OPTIONS_INIT;
+
+ opts.name = "test-new";
+ opts.repository = _repo;
+ opts.fetchspec = "+refs/*:refs/*";
+
+ cl_git_pass(git_remote_create_with_opts(&remote, TEST_URL, &opts));
+ cl_assert_equal_s(git_remote_name(remote), "test-new");
+ cl_assert_equal_s(git_remote_url(remote), TEST_URL);
+ cl_assert_equal_p(git_remote_owner(remote), _repo);
+
+ cl_git_pass(git_remote_get_fetch_refspecs(&array, remote));
+ cl_assert_equal_i(1, array.count);
+ cl_assert_equal_s("+refs/*:refs/*", array.strings[0]);
+
+ git_strarray_dispose(&array);
+ git_remote_free(remote);
+}
+
+void test_remote_create__with_opts_named_no_fetchspec(void)
+{
+ git_remote *remote;
+ git_strarray array;
+ git_remote_create_options opts = GIT_REMOTE_CREATE_OPTIONS_INIT;
+
+ opts.name = "test-new";
+ opts.repository = _repo;
+ opts.flags = GIT_REMOTE_CREATE_SKIP_DEFAULT_FETCHSPEC;
+
+ cl_git_pass(git_remote_create_with_opts(&remote, TEST_URL, &opts));
+ cl_assert_equal_s(git_remote_name(remote), "test-new");
+ cl_assert_equal_s(git_remote_url(remote), TEST_URL);
+ cl_assert_equal_p(git_remote_owner(remote), _repo);
+
+ cl_git_pass(git_remote_get_fetch_refspecs(&array, remote));
+ cl_assert_equal_i(0, array.count);
+
+ git_strarray_dispose(&array);
+ git_remote_free(remote);
+}
+
+void test_remote_create__with_opts_anonymous(void)
+{
+ git_remote *remote;
+ git_strarray array;
+ git_remote_create_options opts = GIT_REMOTE_CREATE_OPTIONS_INIT;
+
+ opts.repository = _repo;
+
+ cl_git_pass(git_remote_create_with_opts(&remote, TEST_URL, &opts));
+ cl_assert_equal_s(git_remote_name(remote), NULL);
+ cl_assert_equal_s(git_remote_url(remote), TEST_URL);
+ cl_assert_equal_p(git_remote_owner(remote), _repo);
+
+ cl_git_pass(git_remote_get_fetch_refspecs(&array, remote));
+ cl_assert_equal_i(0, array.count);
+
+ git_strarray_dispose(&array);
+ git_remote_free(remote);
+}
+
+void test_remote_create__with_opts_detached(void)
+{
+ git_remote *remote;
+ git_strarray array;
+ git_remote_create_options opts = GIT_REMOTE_CREATE_OPTIONS_INIT;
+
+ cl_git_pass(git_remote_create_with_opts(&remote, TEST_URL, &opts));
+ cl_assert_equal_s(git_remote_name(remote), NULL);
+ cl_assert_equal_s(git_remote_url(remote), TEST_URL);
+ cl_assert_equal_p(git_remote_owner(remote), NULL);
+
+ cl_git_pass(git_remote_get_fetch_refspecs(&array, remote));
+ cl_assert_equal_i(0, array.count);
+
+ git_strarray_dispose(&array);
+
+ git_remote_free(remote);
+
+ cl_git_pass(git_remote_create_with_opts(&remote, TEST_URL, NULL));
+ cl_assert_equal_s(git_remote_name(remote), NULL);
+ cl_assert_equal_s(git_remote_url(remote), TEST_URL);
+ cl_assert_equal_p(git_remote_owner(remote), NULL);
+
+ cl_git_pass(git_remote_get_fetch_refspecs(&array, remote));
+ cl_assert_equal_i(0, array.count);
+
+ git_strarray_dispose(&array);
+
+ git_remote_free(remote);
+}
+
+
+void test_remote_create__with_opts_insteadof_disabled(void)
+{
+ git_remote *remote;
+ git_remote_create_options opts = GIT_REMOTE_CREATE_OPTIONS_INIT;
+
+ opts.repository = _repo;
+ opts.flags = GIT_REMOTE_CREATE_SKIP_INSTEADOF;
+
+ cl_git_pass(git_remote_create_with_opts(&remote, "http://example.com/libgit2/libgit2", &opts));
+
+ cl_assert_equal_s(git_remote_url(remote), "http://example.com/libgit2/libgit2");
+ cl_assert_equal_p(git_remote_pushurl(remote), NULL);
+
+ git_remote_free(remote);
+}
+
+static int create_with_name(git_remote **remote, git_repository *repo, const char *name, const char *url)
+{
+ git_remote_create_options opts = GIT_REMOTE_CREATE_OPTIONS_INIT;
+
+ opts.repository = repo;
+ opts.name = name;
+
+ return git_remote_create_with_opts(remote, url, &opts);
+}
+
+void test_remote_create__with_opts_invalid_name(void)
+{
+ const char *names[] = {
+ "Inv@{id",
+ "",
+ "/",
+ "//",
+ ".lock",
+ "a.lock",
+ };
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(names); i++) {
+ git_remote *remote = NULL;
+ cl_git_fail_with(GIT_EINVALIDSPEC, create_with_name(&remote, _repo, names[i], TEST_URL));
+ cl_assert_equal_p(remote, NULL);
+ }
+}
+
+void test_remote_create__with_opts_conflicting_name(void)
+{
+ git_remote *remote = NULL;
+
+ cl_git_fail_with(GIT_EEXISTS, create_with_name(&remote, _repo, "test", TEST_URL));
+ cl_assert_equal_p(remote, NULL);
+}
+
+void test_remote_create__with_opts_invalid_url(void)
+{
+ git_remote *remote = NULL;
+
+ cl_git_fail_with(GIT_EINVALIDSPEC, create_with_name(&remote, _repo, "test-new", ""));
+ cl_assert_equal_p(remote, NULL);
+}
diff --git a/tests/libgit2/remote/fetch.c b/tests/libgit2/remote/fetch.c
new file mode 100644
index 0000000..85e9920
--- /dev/null
+++ b/tests/libgit2/remote/fetch.c
@@ -0,0 +1,169 @@
+#include "clar_libgit2.h"
+
+#include "remote.h"
+#include "repository.h"
+
+static git_repository *repo1;
+static git_repository *repo2;
+static char* repo1_path;
+static char* repo2_path;
+
+static const char *REPO1_REFNAME = "refs/heads/main";
+static const char *REPO2_REFNAME = "refs/remotes/repo1/main";
+static char *FORCE_FETCHSPEC = "+refs/heads/main:refs/remotes/repo1/main";
+static char *NON_FORCE_FETCHSPEC = "refs/heads/main:refs/remotes/repo1/main";
+
+void test_remote_fetch__initialize(void) {
+ git_config *c;
+ git_str repo1_path_buf = GIT_STR_INIT;
+ git_str repo2_path_buf = GIT_STR_INIT;
+ const char *sandbox = clar_sandbox_path();
+
+ cl_git_pass(git_str_joinpath(&repo1_path_buf, sandbox, "fetchtest_repo1"));
+ repo1_path = git_str_detach(&repo1_path_buf);
+ cl_git_pass(git_repository_init(&repo1, repo1_path, true));
+
+ cl_git_pass(git_str_joinpath(&repo2_path_buf, sandbox, "fetchtest_repo2"));
+ repo2_path = git_str_detach(&repo2_path_buf);
+ cl_git_pass(git_repository_init(&repo2, repo2_path, true));
+
+ cl_git_pass(git_repository_config(&c, repo1));
+ cl_git_pass(git_config_set_string(c, "user.email", "some@email"));
+ cl_git_pass(git_config_set_string(c, "user.name", "some@name"));
+ git_config_free(c);
+ git_str_dispose(&repo1_path_buf);
+ git_str_dispose(&repo2_path_buf);
+}
+
+void test_remote_fetch__cleanup(void) {
+ git_repository_free(repo1);
+ git_repository_free(repo2);
+
+ cl_git_pass(git_futils_rmdir_r(repo1_path, NULL, GIT_RMDIR_REMOVE_FILES));
+ free(repo1_path);
+
+ cl_git_pass(git_futils_rmdir_r(repo2_path, NULL, GIT_RMDIR_REMOVE_FILES));
+ free(repo2_path);
+}
+
+
+/**
+ * This checks that the '+' flag on fetchspecs is respected. We create a
+ * repository that has a reference to two commits, one a child of the other.
+ * We fetch this repository into a second repository. Then we reset the
+ * reference in the first repository and run the fetch again. If the '+' flag
+ * is used then the reference in the second repository will change, but if it
+ * is not then it should stay the same.
+ *
+ * @param commit1id A pointer to an OID which will be populated with the first
+ * commit.
+ * @param commit2id A pointer to an OID which will be populated with the second
+ * commit, which is a descendant of the first.
+ * @param force Whether to use a spec with '+' prefixed to force the refs
+ * to update
+ */
+static void do_time_travelling_fetch(git_oid *commit1id, git_oid *commit2id,
+ bool force) {
+ char *refspec_strs = {
+ force ? FORCE_FETCHSPEC : NON_FORCE_FETCHSPEC,
+ };
+ git_strarray refspecs = {
+ .count = 1,
+ .strings = &refspec_strs,
+ };
+
+ /* create two commits in repo 1 and a reference to them */
+ {
+ git_oid empty_tree_id;
+ git_tree *empty_tree;
+ git_signature *sig;
+ git_treebuilder *tb;
+ cl_git_pass(git_treebuilder_new(&tb, repo1, NULL));
+ cl_git_pass(git_treebuilder_write(&empty_tree_id, tb));
+ cl_git_pass(git_tree_lookup(&empty_tree, repo1, &empty_tree_id));
+ cl_git_pass(git_signature_default(&sig, repo1));
+ cl_git_pass(git_commit_create(commit1id, repo1, REPO1_REFNAME, sig,
+ sig, NULL, "one", empty_tree, 0, NULL));
+ cl_git_pass(git_commit_create_v(commit2id, repo1, REPO1_REFNAME, sig,
+ sig, NULL, "two", empty_tree, 1, commit1id));
+
+ git_tree_free(empty_tree);
+ git_signature_free(sig);
+ git_treebuilder_free(tb);
+ }
+
+ /* fetch the reference via the remote */
+ {
+ git_remote *remote;
+
+ cl_git_pass(git_remote_create_anonymous(&remote, repo2,
+ git_repository_path(repo1)));
+ cl_git_pass(git_remote_fetch(remote, &refspecs, NULL, "some message"));
+
+ git_remote_free(remote);
+ }
+
+ /* assert that repo2 references the second commit */
+ {
+ const git_oid *target;
+ git_reference *ref;
+ cl_git_pass(git_reference_lookup(&ref, repo2, REPO2_REFNAME));
+ target = git_reference_target(ref);
+ cl_assert_equal_b(git_oid_cmp(target, commit2id), 0);
+ git_reference_free(ref);
+ }
+
+ /* set the reference in repo1 to point to the older commit */
+ {
+ git_reference *ref;
+ git_reference *ref2;
+ cl_git_pass(git_reference_lookup(&ref, repo1, REPO1_REFNAME));
+ cl_git_pass(git_reference_set_target(&ref2, ref, commit1id,
+ "rollback"));
+ git_reference_free(ref);
+ git_reference_free(ref2);
+ }
+
+ /* fetch the reference again */
+ {
+ git_remote *remote;
+
+ cl_git_pass(git_remote_create_anonymous(&remote, repo2,
+ git_repository_path(repo1)));
+ cl_git_pass(git_remote_fetch(remote, &refspecs, NULL, "some message"));
+
+ git_remote_free(remote);
+ }
+}
+
+void test_remote_fetch__dont_update_refs_if_not_descendant_and_not_force(void) {
+ const git_oid *target;
+ git_oid commit1id;
+ git_oid commit2id;
+ git_reference *ref;
+
+ do_time_travelling_fetch(&commit1id, &commit2id, false);
+
+ /* assert that the reference in repo2 has not changed */
+ cl_git_pass(git_reference_lookup(&ref, repo2, REPO2_REFNAME));
+ target = git_reference_target(ref);
+ cl_assert_equal_b(git_oid_cmp(target, &commit2id), 0);
+
+ git_reference_free(ref);
+}
+
+void test_remote_fetch__do_update_refs_if_not_descendant_and_force(void) {
+ const git_oid *target;
+ git_oid commit1id;
+ git_oid commit2id;
+ git_reference *ref;
+
+ do_time_travelling_fetch(&commit1id, &commit2id, true);
+
+ /* assert that the reference in repo2 has changed */
+ cl_git_pass(git_reference_lookup(&ref, repo2, REPO2_REFNAME));
+ target = git_reference_target(ref);
+ cl_assert_equal_b(git_oid_cmp(target, &commit1id), 0);
+
+ git_reference_free(ref);
+}
diff --git a/tests/libgit2/remote/httpproxy.c b/tests/libgit2/remote/httpproxy.c
new file mode 100644
index 0000000..60fc67d
--- /dev/null
+++ b/tests/libgit2/remote/httpproxy.c
@@ -0,0 +1,180 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+#include "net.h"
+#include "remote.h"
+
+static git_repository *repo;
+static git_net_url url = GIT_NET_URL_INIT;
+
+static char *orig_http_proxy = NULL;
+static char *orig_https_proxy = NULL;
+static char *orig_no_proxy = NULL;
+
+void test_remote_httpproxy__initialize(void)
+{
+ git_remote *remote;
+
+ repo = cl_git_sandbox_init("testrepo");
+ cl_git_pass(git_remote_create(&remote, repo, "lg2", "https://github.com/libgit2/libgit2"));
+ cl_git_pass(git_net_url_parse(&url, "https://github.com/libgit2/libgit2"));
+
+ git_remote_free(remote);
+
+ /* Clear everything for a fresh start */
+ orig_http_proxy = cl_getenv("HTTP_PROXY");
+ orig_https_proxy = cl_getenv("HTTPS_PROXY");
+ orig_no_proxy = cl_getenv("NO_PROXY");
+
+ cl_setenv("HTTP_PROXY", NULL);
+ cl_setenv("HTTPS_PROXY", NULL);
+ cl_setenv("NO_PROXY", NULL);
+}
+
+void test_remote_httpproxy__cleanup(void)
+{
+ cl_setenv("HTTP_PROXY", orig_http_proxy);
+ cl_setenv("HTTPS_PROXY", orig_https_proxy);
+ cl_setenv("NO_PROXY", orig_no_proxy);
+
+ git__free(orig_http_proxy);
+ git__free(orig_https_proxy);
+ git__free(orig_no_proxy);
+
+ git_net_url_dispose(&url);
+ cl_git_sandbox_cleanup();
+}
+
+static void assert_proxy_is(const char *expected)
+{
+ git_remote *remote;
+ char *proxy;
+
+ cl_git_pass(git_remote_lookup(&remote, repo, "lg2"));
+ cl_git_pass(git_remote__http_proxy(&proxy, remote, &url));
+
+ if (expected)
+ cl_assert_equal_s(proxy, expected);
+ else
+ cl_assert_equal_p(proxy, expected);
+
+ git_remote_free(remote);
+ git__free(proxy);
+}
+
+static void assert_config_match(const char *config, const char *expected)
+{
+ git_remote *remote;
+ char *proxy;
+
+ if (config)
+ cl_repo_set_string(repo, config, expected);
+
+ cl_git_pass(git_remote_lookup(&remote, repo, "lg2"));
+ cl_git_pass(git_remote__http_proxy(&proxy, remote, &url));
+
+ if (expected)
+ cl_assert_equal_s(proxy, expected);
+ else
+ cl_assert_equal_p(proxy, expected);
+
+ git_remote_free(remote);
+ git__free(proxy);
+}
+
+void test_remote_httpproxy__config_overrides(void)
+{
+ /*
+ * http.proxy should be honored, then http.<url>.proxy should
+ * be honored in increasing specificity of the url. finally,
+ * remote.<name>.proxy is the most specific.
+ */
+ assert_config_match(NULL, NULL);
+ assert_config_match("http.proxy", "http://localhost:1/");
+ assert_config_match("http.https://github.com.proxy", "http://localhost:2/");
+ assert_config_match("http.https://github.com/.proxy", "http://localhost:3/");
+ assert_config_match("http.https://github.com/libgit2.proxy", "http://localhost:4/");
+ assert_config_match("http.https://github.com/libgit2/.proxy", "http://localhost:5/");
+ assert_config_match("http.https://github.com/libgit2/libgit2.proxy", "http://localhost:6/");
+ assert_config_match("remote.lg2.proxy", "http://localhost:7/");
+}
+
+void test_remote_httpproxy__config_empty_overrides(void)
+{
+ /*
+ * with greater specificity, an empty config entry overrides
+ * a set one
+ */
+ assert_config_match("http.proxy", "http://localhost:1/");
+ assert_config_match("http.https://github.com.proxy", "");
+ assert_config_match("http.https://github.com/libgit2/libgit2.proxy", "http://localhost:2/");
+ assert_config_match("remote.lg2.proxy", "");
+}
+
+static void assert_global_config_match(const char *config, const char *expected)
+{
+ git_remote *remote;
+ char *proxy;
+ git_config* cfg;
+
+ if (config) {
+ cl_git_pass(git_config_open_default(&cfg));
+ git_config_set_string(cfg, config, expected);
+ git_config_free(cfg);
+ }
+
+ cl_git_pass(git_remote_create_detached(&remote, "https://github.com/libgit2/libgit2"));
+ cl_git_pass(git_remote__http_proxy(&proxy, remote, &url));
+
+ if (expected)
+ cl_assert_equal_s(proxy, expected);
+ else
+ cl_assert_equal_p(proxy, expected);
+
+ git_remote_free(remote);
+ git__free(proxy);
+}
+
+void test_remote_httpproxy__config_overrides_detached_remote(void)
+{
+ cl_fake_globalconfig(NULL);
+
+ assert_global_config_match(NULL, NULL);
+ assert_global_config_match("http.proxy", "http://localhost:1/");
+ assert_global_config_match("http.https://github.com.proxy", "http://localhost:2/");
+ assert_global_config_match("http.https://github.com/.proxy", "http://localhost:3/");
+ assert_global_config_match("http.https://github.com/libgit2.proxy", "http://localhost:4/");
+ assert_global_config_match("http.https://github.com/libgit2/.proxy", "http://localhost:5/");
+ assert_global_config_match("http.https://github.com/libgit2/libgit2.proxy", "http://localhost:6/");
+}
+
+void test_remote_httpproxy__env(void)
+{
+ /* HTTP proxy is ignored for HTTPS */
+ cl_setenv("HTTP_PROXY", "http://localhost:9/");
+ assert_proxy_is(NULL);
+
+ /* HTTPS proxy is honored for HTTPS */
+ cl_setenv("HTTPS_PROXY", "http://localhost:10/");
+ assert_proxy_is("http://localhost:10/");
+
+ /* NO_PROXY is honored */
+ cl_setenv("NO_PROXY", "github.com:443");
+ assert_proxy_is(NULL);
+
+ cl_setenv("NO_PROXY", "github.com:80");
+ assert_proxy_is("http://localhost:10/");
+
+ cl_setenv("NO_PROXY", "github.com");
+ assert_proxy_is(NULL);
+
+ cl_setenv("NO_PROXY", "github.dev,github.com,github.foo");
+ assert_proxy_is(NULL);
+
+ cl_setenv("HTTPS_PROXY", "");
+ assert_proxy_is(NULL);
+
+ /* configuration overrides environment variables */
+ cl_setenv("HTTPS_PROXY", "http://localhost:10/");
+ cl_setenv("NO_PROXY", "github.none");
+ assert_config_match("http.https://github.com.proxy", "http://localhost:11/");
+}
diff --git a/tests/libgit2/remote/insteadof.c b/tests/libgit2/remote/insteadof.c
new file mode 100644
index 0000000..c39df4b
--- /dev/null
+++ b/tests/libgit2/remote/insteadof.c
@@ -0,0 +1,154 @@
+#include "clar_libgit2.h"
+#include "remote.h"
+#include "repository.h"
+
+#define REPO_PATH "testrepo2/.gitted"
+#define REMOTE_ORIGIN "origin"
+#define REMOTE_INSTEADOF_URL_FETCH "insteadof-url-fetch"
+#define REMOTE_INSTEADOF_URL_PUSH "insteadof-url-push"
+#define REMOTE_INSTEADOF_URL_BOTH "insteadof-url-both"
+#define REMOTE_INSTEADOF_PUSHURL_FETCH "insteadof-pushurl-fetch"
+#define REMOTE_INSTEADOF_PUSHURL_PUSH "insteadof-pushurl-push"
+#define REMOTE_INSTEADOF_PUSHURL_BOTH "insteadof-pushurl-both"
+
+static git_repository *g_repo;
+static git_remote *g_remote;
+
+void test_remote_insteadof__initialize(void)
+{
+ g_repo = NULL;
+ g_remote = NULL;
+}
+
+void test_remote_insteadof__cleanup(void)
+{
+ git_repository_free(g_repo);
+ git_remote_free(g_remote);
+}
+
+void test_remote_insteadof__not_applicable(void)
+{
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture(REPO_PATH)));
+ cl_git_pass(git_remote_lookup(&g_remote, g_repo, REMOTE_ORIGIN));
+
+ cl_assert_equal_s(
+ git_remote_url(g_remote),
+ "https://github.com/libgit2/false.git");
+ cl_assert_equal_p(git_remote_pushurl(g_remote), NULL);
+}
+
+void test_remote_insteadof__url_insteadof_fetch(void)
+{
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture(REPO_PATH)));
+ cl_git_pass(git_remote_lookup(&g_remote, g_repo, REMOTE_INSTEADOF_URL_FETCH));
+
+ cl_assert_equal_s(
+ git_remote_url(g_remote),
+ "http://github.com/url/fetch/libgit2");
+ cl_assert_equal_p(git_remote_pushurl(g_remote), NULL);
+}
+
+void test_remote_insteadof__url_insteadof_push(void)
+{
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture(REPO_PATH)));
+ cl_git_pass(git_remote_lookup(&g_remote, g_repo, REMOTE_INSTEADOF_URL_PUSH));
+
+ cl_assert_equal_s(
+ git_remote_url(g_remote),
+ "http://example.com/url/push/libgit2");
+ cl_assert_equal_s(
+ git_remote_pushurl(g_remote),
+ "git@github.com:url/push/libgit2");
+}
+
+void test_remote_insteadof__url_insteadof_both(void)
+{
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture(REPO_PATH)));
+ cl_git_pass(git_remote_lookup(&g_remote, g_repo, REMOTE_INSTEADOF_URL_BOTH));
+
+ cl_assert_equal_s(
+ git_remote_url(g_remote),
+ "http://github.com/url/both/libgit2");
+ cl_assert_equal_s(
+ git_remote_pushurl(g_remote),
+ "git@github.com:url/both/libgit2");
+}
+
+void test_remote_insteadof__pushurl_insteadof_fetch(void)
+{
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture(REPO_PATH)));
+ cl_git_pass(git_remote_lookup(&g_remote, g_repo, REMOTE_INSTEADOF_PUSHURL_FETCH));
+
+ cl_assert_equal_s(
+ git_remote_url(g_remote),
+ "http://github.com/url/fetch/libgit2");
+ cl_assert_equal_s(
+ git_remote_pushurl(g_remote),
+ "http://github.com/url/fetch/libgit2-push");
+}
+
+void test_remote_insteadof__pushurl_insteadof_push(void)
+{
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture(REPO_PATH)));
+ cl_git_pass(git_remote_lookup(&g_remote, g_repo, REMOTE_INSTEADOF_PUSHURL_PUSH));
+
+ cl_assert_equal_s(
+ git_remote_url(g_remote),
+ "http://example.com/url/push/libgit2");
+ cl_assert_equal_s(
+ git_remote_pushurl(g_remote),
+ "http://example.com/url/push/libgit2-push");
+}
+
+void test_remote_insteadof__pushurl_insteadof_both(void)
+{
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture(REPO_PATH)));
+ cl_git_pass(git_remote_lookup(&g_remote, g_repo, REMOTE_INSTEADOF_PUSHURL_BOTH));
+
+ cl_assert_equal_s(
+ git_remote_url(g_remote),
+ "http://github.com/url/both/libgit2");
+ cl_assert_equal_s(
+ git_remote_pushurl(g_remote),
+ "http://github.com/url/both/libgit2-push");
+}
+
+void test_remote_insteadof__anonymous_remote_fetch(void)
+{
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture(REPO_PATH)));
+ cl_git_pass(git_remote_create_anonymous(&g_remote, g_repo,
+ "http://example.com/url/fetch/libgit2"));
+
+ cl_assert_equal_s(
+ git_remote_url(g_remote),
+ "http://github.com/url/fetch/libgit2");
+ cl_assert_equal_p(git_remote_pushurl(g_remote), NULL);
+}
+
+void test_remote_insteadof__anonymous_remote_push(void)
+{
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture(REPO_PATH)));
+ cl_git_pass(git_remote_create_anonymous(&g_remote, g_repo,
+ "http://example.com/url/push/libgit2"));
+
+ cl_assert_equal_s(
+ git_remote_url(g_remote),
+ "http://example.com/url/push/libgit2");
+ cl_assert_equal_s(
+ git_remote_pushurl(g_remote),
+ "git@github.com:url/push/libgit2");
+}
+
+void test_remote_insteadof__anonymous_remote_both(void)
+{
+ cl_git_pass(git_repository_open(&g_repo, cl_fixture(REPO_PATH)));
+ cl_git_pass(git_remote_create_anonymous(&g_remote, g_repo,
+ "http://example.com/url/both/libgit2"));
+
+ cl_assert_equal_s(
+ git_remote_url(g_remote),
+ "http://github.com/url/both/libgit2");
+ cl_assert_equal_s(
+ git_remote_pushurl(g_remote),
+ "git@github.com:url/both/libgit2");
+}
diff --git a/tests/libgit2/remote/list.c b/tests/libgit2/remote/list.c
new file mode 100644
index 0000000..4a6be3d
--- /dev/null
+++ b/tests/libgit2/remote/list.c
@@ -0,0 +1,43 @@
+#include "clar_libgit2.h"
+#include "config/config_helpers.h"
+
+static git_repository *_repo;
+
+#define TEST_URL "http://github.com/libgit2/libgit2.git"
+
+void test_remote_list__initialize(void)
+{
+ _repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_remote_list__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_remote_list__always_checks_disk_config(void)
+{
+ git_repository *repo;
+ git_strarray remotes;
+ git_remote *remote;
+
+ cl_git_pass(git_repository_open(&repo, git_repository_path(_repo)));
+
+ cl_git_pass(git_remote_list(&remotes, _repo));
+ cl_assert_equal_sz(remotes.count, 1);
+ git_strarray_dispose(&remotes);
+
+ cl_git_pass(git_remote_create(&remote, _repo, "valid-name", TEST_URL));
+
+ cl_git_pass(git_remote_list(&remotes, _repo));
+ cl_assert_equal_sz(remotes.count, 2);
+ git_strarray_dispose(&remotes);
+
+ cl_git_pass(git_remote_list(&remotes, repo));
+ cl_assert_equal_sz(remotes.count, 2);
+ git_strarray_dispose(&remotes);
+
+ git_repository_free(repo);
+ git_remote_free(remote);
+}
+
diff --git a/tests/libgit2/repo/config.c b/tests/libgit2/repo/config.c
new file mode 100644
index 0000000..37f6b52
--- /dev/null
+++ b/tests/libgit2/repo/config.c
@@ -0,0 +1,210 @@
+#include "clar_libgit2.h"
+#include "sysdir.h"
+#include "futils.h"
+#include <ctype.h>
+
+static git_str path = GIT_STR_INIT;
+
+void test_repo_config__initialize(void)
+{
+ cl_fixture_sandbox("empty_standard_repo");
+ cl_git_pass(cl_rename(
+ "empty_standard_repo/.gitted", "empty_standard_repo/.git"));
+
+ git_str_clear(&path);
+
+ cl_must_pass(p_mkdir("alternate", 0777));
+ cl_git_pass(git_fs_path_prettify(&path, "alternate", NULL));
+}
+
+void test_repo_config__cleanup(void)
+{
+ cl_sandbox_set_search_path_defaults();
+
+ git_str_dispose(&path);
+
+ cl_git_pass(
+ git_futils_rmdir_r("alternate", NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_assert(!git_fs_path_isdir("alternate"));
+
+ cl_fixture_cleanup("empty_standard_repo");
+}
+
+void test_repo_config__can_open_global_when_there_is_no_file(void)
+{
+ git_repository *repo;
+ git_config *config, *global;
+
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr));
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr));
+
+ cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
+ cl_git_pass(git_repository_config(&config, repo));
+ cl_git_pass(git_config_open_level(
+ &global, config, GIT_CONFIG_LEVEL_GLOBAL));
+
+ cl_git_pass(git_config_set_string(global, "test.set", "42"));
+
+ git_config_free(global);
+ git_config_free(config);
+ git_repository_free(repo);
+}
+
+void test_repo_config__can_open_missing_global_with_separators(void)
+{
+ git_repository *repo;
+ git_config *config, *global;
+
+ cl_git_pass(git_str_printf(
+ &path, "%c%s", GIT_PATH_LIST_SEPARATOR, "dummy"));
+
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr));
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr));
+
+ git_str_dispose(&path);
+
+ cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
+ cl_git_pass(git_repository_config(&config, repo));
+ cl_git_pass(git_config_open_level(
+ &global, config, GIT_CONFIG_LEVEL_GLOBAL));
+
+ cl_git_pass(git_config_set_string(global, "test.set", "42"));
+
+ git_config_free(global);
+ git_config_free(config);
+ git_repository_free(repo);
+}
+
+#include "repository.h"
+
+void test_repo_config__read_with_no_configs_at_all(void)
+{
+ git_repository *repo;
+ int val;
+
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr));
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr));
+
+ /* with none */
+
+ cl_must_pass(p_unlink("empty_standard_repo/.git/config"));
+ cl_assert(!git_fs_path_isfile("empty_standard_repo/.git/config"));
+
+ cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
+ git_repository__configmap_lookup_cache_clear(repo);
+ val = -1;
+ cl_git_pass(git_repository__configmap_lookup(&val, repo, GIT_CONFIGMAP_ABBREV));
+ cl_assert_equal_i(GIT_ABBREV_DEFAULT, val);
+ git_repository_free(repo);
+
+ /* with no local config, just system */
+
+ cl_sandbox_set_search_path_defaults();
+
+ cl_must_pass(p_mkdir("alternate/1", 0777));
+ cl_git_pass(git_str_joinpath(&path, path.ptr, "1"));
+ cl_git_rewritefile("alternate/1/gitconfig", "[core]\n\tabbrev = 10\n");
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr));
+
+ cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
+ git_repository__configmap_lookup_cache_clear(repo);
+ val = -1;
+ cl_git_pass(git_repository__configmap_lookup(&val, repo, GIT_CONFIGMAP_ABBREV));
+ cl_assert_equal_i(10, val);
+ git_repository_free(repo);
+
+ /* with just xdg + system */
+
+ cl_must_pass(p_mkdir("alternate/2", 0777));
+ path.ptr[path.size - 1] = '2';
+ cl_git_rewritefile("alternate/2/config", "[core]\n\tabbrev = 20\n");
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr));
+
+ cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
+ git_repository__configmap_lookup_cache_clear(repo);
+ val = -1;
+ cl_git_pass(git_repository__configmap_lookup(&val, repo, GIT_CONFIGMAP_ABBREV));
+ cl_assert_equal_i(20, val);
+ git_repository_free(repo);
+
+ /* with global + xdg + system */
+
+ cl_must_pass(p_mkdir("alternate/3", 0777));
+ path.ptr[path.size - 1] = '3';
+ cl_git_rewritefile("alternate/3/.gitconfig", "[core]\n\tabbrev = 30\n");
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
+
+ cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
+ git_repository__configmap_lookup_cache_clear(repo);
+ val = -1;
+ cl_git_pass(git_repository__configmap_lookup(&val, repo, GIT_CONFIGMAP_ABBREV));
+ cl_assert_equal_i(30, val);
+ git_repository_free(repo);
+
+ /* with all configs */
+
+ cl_git_rewritefile("empty_standard_repo/.git/config", "[core]\n\tabbrev = 40\n");
+
+ cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
+ git_repository__configmap_lookup_cache_clear(repo);
+ val = -1;
+ cl_git_pass(git_repository__configmap_lookup(&val, repo, GIT_CONFIGMAP_ABBREV));
+ cl_assert_equal_i(40, val);
+ git_repository_free(repo);
+
+ /* with all configs but delete the files ? */
+
+ cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
+ git_repository__configmap_lookup_cache_clear(repo);
+ val = -1;
+ cl_git_pass(git_repository__configmap_lookup(&val, repo, GIT_CONFIGMAP_ABBREV));
+ cl_assert_equal_i(40, val);
+
+ cl_must_pass(p_unlink("empty_standard_repo/.git/config"));
+ cl_assert(!git_fs_path_isfile("empty_standard_repo/.git/config"));
+
+ cl_must_pass(p_unlink("alternate/1/gitconfig"));
+ cl_assert(!git_fs_path_isfile("alternate/1/gitconfig"));
+
+ cl_must_pass(p_unlink("alternate/2/config"));
+ cl_assert(!git_fs_path_isfile("alternate/2/config"));
+
+ cl_must_pass(p_unlink("alternate/3/.gitconfig"));
+ cl_assert(!git_fs_path_isfile("alternate/3/.gitconfig"));
+
+ git_repository__configmap_lookup_cache_clear(repo);
+ val = -1;
+ cl_git_pass(git_repository__configmap_lookup(&val, repo, GIT_CONFIGMAP_ABBREV));
+ cl_assert_equal_i(40, val);
+ git_repository_free(repo);
+
+ /* reopen */
+
+ cl_assert(!git_fs_path_isfile("empty_standard_repo/.git/config"));
+ cl_assert(!git_fs_path_isfile("alternate/3/.gitconfig"));
+
+ cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
+ git_repository__configmap_lookup_cache_clear(repo);
+ val = -1;
+ cl_git_pass(git_repository__configmap_lookup(&val, repo, GIT_CONFIGMAP_ABBREV));
+ cl_assert_equal_i(7, val);
+ git_repository_free(repo);
+
+ cl_assert(!git_fs_path_exists("empty_standard_repo/.git/config"));
+ cl_assert(!git_fs_path_exists("alternate/3/.gitconfig"));
+}
diff --git a/tests/libgit2/repo/discover.c b/tests/libgit2/repo/discover.c
new file mode 100644
index 0000000..983d75e
--- /dev/null
+++ b/tests/libgit2/repo/discover.c
@@ -0,0 +1,213 @@
+#include "clar_libgit2.h"
+
+#include "odb.h"
+#include "futils.h"
+#include "repository.h"
+
+#define TEMP_REPO_FOLDER "temprepo/"
+#define DISCOVER_FOLDER TEMP_REPO_FOLDER "discover.git"
+
+#define SUB_REPOSITORY_FOLDER_NAME "sub_repo"
+#define SUB_REPOSITORY_FOLDER DISCOVER_FOLDER "/" SUB_REPOSITORY_FOLDER_NAME
+#define SUB_REPOSITORY_GITDIR SUB_REPOSITORY_FOLDER "/.git"
+#define SUB_REPOSITORY_FOLDER_SUB SUB_REPOSITORY_FOLDER "/sub"
+#define SUB_REPOSITORY_FOLDER_SUB_SUB SUB_REPOSITORY_FOLDER_SUB "/subsub"
+#define SUB_REPOSITORY_FOLDER_SUB_SUB_SUB SUB_REPOSITORY_FOLDER_SUB_SUB "/subsubsub"
+
+#define REPOSITORY_ALTERNATE_FOLDER DISCOVER_FOLDER "/alternate_sub_repo"
+#define REPOSITORY_ALTERNATE_FOLDER_SUB REPOSITORY_ALTERNATE_FOLDER "/sub"
+#define REPOSITORY_ALTERNATE_FOLDER_SUB_SUB REPOSITORY_ALTERNATE_FOLDER_SUB "/subsub"
+#define REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB REPOSITORY_ALTERNATE_FOLDER_SUB_SUB "/subsubsub"
+
+#define ALTERNATE_MALFORMED_FOLDER1 DISCOVER_FOLDER "/alternate_malformed_repo1"
+#define ALTERNATE_MALFORMED_FOLDER2 DISCOVER_FOLDER "/alternate_malformed_repo2"
+#define ALTERNATE_MALFORMED_FOLDER3 DISCOVER_FOLDER "/alternate_malformed_repo3"
+#define ALTERNATE_NOT_FOUND_FOLDER DISCOVER_FOLDER "/alternate_not_found_repo"
+
+static void ensure_repository_discover(const char *start_path,
+ const char *ceiling_dirs,
+ const char *expected_path)
+{
+ git_buf found_path = GIT_BUF_INIT;
+ git_str resolved = GIT_STR_INIT;
+
+ git_str_attach(&resolved, p_realpath(expected_path, NULL), 0);
+ cl_assert(resolved.size > 0);
+ cl_git_pass(git_fs_path_to_dir(&resolved));
+ cl_git_pass(git_repository_discover(&found_path, start_path, 1, ceiling_dirs));
+
+ cl_assert_equal_s(found_path.ptr, resolved.ptr);
+
+ git_str_dispose(&resolved);
+ git_buf_dispose(&found_path);
+}
+
+static void write_file(const char *path, const char *content)
+{
+ git_file file;
+ int error;
+
+ if (git_fs_path_exists(path)) {
+ cl_git_pass(p_unlink(path));
+ }
+
+ file = git_futils_creat_withpath(path, 0777, 0666);
+ cl_assert(file >= 0);
+
+ error = p_write(file, content, strlen(content) * sizeof(char));
+ p_close(file);
+ cl_git_pass(error);
+}
+
+/*no check is performed on ceiling_dirs length, so be sure it's long enough */
+static void append_ceiling_dir(git_str *ceiling_dirs, const char *path)
+{
+ git_str pretty_path = GIT_STR_INIT;
+ char ceiling_separator[2] = { GIT_PATH_LIST_SEPARATOR, '\0' };
+
+ cl_git_pass(git_fs_path_prettify_dir(&pretty_path, path, NULL));
+
+ if (ceiling_dirs->size > 0)
+ git_str_puts(ceiling_dirs, ceiling_separator);
+
+ git_str_puts(ceiling_dirs, pretty_path.ptr);
+
+ git_str_dispose(&pretty_path);
+ cl_assert(git_str_oom(ceiling_dirs) == 0);
+}
+
+static git_buf discovered;
+static git_str ceiling_dirs;
+
+void test_repo_discover__initialize(void)
+{
+ git_repository *repo;
+ const mode_t mode = 0777;
+ git_futils_mkdir_r(DISCOVER_FOLDER, mode);
+
+ git_str_init(&ceiling_dirs, 0);
+ append_ceiling_dir(&ceiling_dirs, TEMP_REPO_FOLDER);
+
+ cl_git_pass(git_repository_init(&repo, DISCOVER_FOLDER, 1));
+ git_repository_free(repo);
+
+ cl_git_pass(git_repository_init(&repo, SUB_REPOSITORY_FOLDER, 0));
+ cl_git_pass(git_futils_mkdir_r(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, mode));
+ cl_git_pass(git_futils_mkdir_r(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, mode));
+
+ cl_git_pass(git_futils_mkdir_r(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, mode));
+ write_file(REPOSITORY_ALTERNATE_FOLDER "/" DOT_GIT, "gitdir: ../" SUB_REPOSITORY_FOLDER_NAME "/" DOT_GIT);
+ write_file(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB "/" DOT_GIT, "gitdir: ../../../" SUB_REPOSITORY_FOLDER_NAME "/" DOT_GIT);
+ write_file(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB "/" DOT_GIT, "gitdir: ../../../../");
+
+ cl_git_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER1, mode));
+ write_file(ALTERNATE_MALFORMED_FOLDER1 "/" DOT_GIT, "Anything but not gitdir:");
+ cl_git_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER2, mode));
+ write_file(ALTERNATE_MALFORMED_FOLDER2 "/" DOT_GIT, "gitdir:");
+ cl_git_pass(git_futils_mkdir_r(ALTERNATE_MALFORMED_FOLDER3, mode));
+ write_file(ALTERNATE_MALFORMED_FOLDER3 "/" DOT_GIT, "gitdir: \n\n\n");
+ cl_git_pass(git_futils_mkdir_r(ALTERNATE_NOT_FOUND_FOLDER, mode));
+ write_file(ALTERNATE_NOT_FOUND_FOLDER "/" DOT_GIT, "gitdir: a_repository_that_surely_does_not_exist");
+
+ git_repository_free(repo);
+}
+
+void test_repo_discover__cleanup(void)
+{
+ git_buf_dispose(&discovered);
+ git_str_dispose(&ceiling_dirs);
+ cl_git_pass(git_futils_rmdir_r(TEMP_REPO_FOLDER, NULL, GIT_RMDIR_REMOVE_FILES));
+}
+
+void test_repo_discover__discovering_repo_with_exact_path_succeeds(void)
+{
+ cl_git_pass(git_repository_discover(&discovered, DISCOVER_FOLDER, 0, ceiling_dirs.ptr));
+ git_buf_dispose(&discovered);
+
+ cl_git_pass(git_repository_discover(&discovered, SUB_REPOSITORY_FOLDER, 0, ceiling_dirs.ptr));
+ git_buf_dispose(&discovered);
+}
+
+void test_repo_discover__discovering_nonexistent_dir_fails(void)
+{
+ cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&discovered, DISCOVER_FOLDER "-nonexistent", 0, NULL));
+}
+
+void test_repo_discover__discovering_repo_with_subdirectory_succeeds(void)
+{
+ ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR);
+ ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB_SUB, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR);
+ ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR);
+}
+
+void test_repo_discover__discovering_repository_with_alternative_gitdir_succeeds(void)
+{
+ ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR);
+ ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR);
+ ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR);
+ ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, ceiling_dirs.ptr, DISCOVER_FOLDER);
+}
+
+void test_repo_discover__discovering_repository_with_malformed_alternative_gitdir_fails(void)
+{
+ cl_git_fail(git_repository_discover(&discovered, ALTERNATE_MALFORMED_FOLDER1, 0, ceiling_dirs.ptr));
+ cl_git_fail(git_repository_discover(&discovered, ALTERNATE_MALFORMED_FOLDER2, 0, ceiling_dirs.ptr));
+ cl_git_fail(git_repository_discover(&discovered, ALTERNATE_MALFORMED_FOLDER3, 0, ceiling_dirs.ptr));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&discovered, ALTERNATE_NOT_FOUND_FOLDER, 0, ceiling_dirs.ptr));
+}
+
+void test_repo_discover__discovering_repository_with_ceiling(void)
+{
+ append_ceiling_dir(&ceiling_dirs, SUB_REPOSITORY_FOLDER_SUB);
+
+ /* this must pass as ceiling_directories cannot prevent the current
+ * working directory to be checked */
+ ensure_repository_discover(SUB_REPOSITORY_FOLDER, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR);
+
+ ensure_repository_discover(SUB_REPOSITORY_FOLDER_SUB, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR);
+ cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&discovered, SUB_REPOSITORY_FOLDER_SUB_SUB, 0, ceiling_dirs.ptr));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&discovered, SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, 0, ceiling_dirs.ptr));
+}
+
+void test_repo_discover__other_ceiling(void)
+{
+ append_ceiling_dir(&ceiling_dirs, SUB_REPOSITORY_FOLDER);
+
+ /* this must pass as ceiling_directories cannot predent the current
+ * working directory to be checked */
+ ensure_repository_discover(SUB_REPOSITORY_FOLDER, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR);
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&discovered, SUB_REPOSITORY_FOLDER_SUB, 0, ceiling_dirs.ptr));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&discovered, SUB_REPOSITORY_FOLDER_SUB_SUB, 0, ceiling_dirs.ptr));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_repository_discover(&discovered, SUB_REPOSITORY_FOLDER_SUB_SUB_SUB, 0, ceiling_dirs.ptr));
+}
+
+void test_repo_discover__ceiling_should_not_affect_gitdir_redirection(void)
+{
+ append_ceiling_dir(&ceiling_dirs, SUB_REPOSITORY_FOLDER);
+
+ /* gitfile redirection should not be affected by ceiling directories */
+ ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR);
+ ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR);
+ ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB, ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR);
+ ensure_repository_discover(REPOSITORY_ALTERNATE_FOLDER_SUB_SUB_SUB, ceiling_dirs.ptr, DISCOVER_FOLDER);
+}
+
+void test_repo_discover__discovery_starting_at_file_succeeds(void)
+{
+ int fd;
+
+ cl_assert((fd = p_creat(SUB_REPOSITORY_FOLDER "/file", 0600)) >= 0);
+ cl_assert(p_close(fd) == 0);
+
+ ensure_repository_discover(SUB_REPOSITORY_FOLDER "/file", ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR);
+}
+
+void test_repo_discover__discovery_starting_at_system_root_causes_no_hang(void)
+{
+#ifdef GIT_WIN32
+ git_buf out = GIT_BUF_INIT;
+ cl_git_fail(git_repository_discover(&out, "C:/", 0, NULL));
+ cl_git_fail(git_repository_discover(&out, "//localhost/", 0, NULL));
+#endif
+}
diff --git a/tests/libgit2/repo/env.c b/tests/libgit2/repo/env.c
new file mode 100644
index 0000000..0e6cc59
--- /dev/null
+++ b/tests/libgit2/repo/env.c
@@ -0,0 +1,369 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+#include "sysdir.h"
+#include <ctype.h>
+
+static void clear_git_env(void)
+{
+ cl_setenv("GIT_DIR", NULL);
+ cl_setenv("GIT_CEILING_DIRECTORIES", NULL);
+ cl_setenv("GIT_INDEX_FILE", NULL);
+ cl_setenv("GIT_NAMESPACE", NULL);
+ cl_setenv("GIT_OBJECT_DIRECTORY", NULL);
+ cl_setenv("GIT_ALTERNATE_OBJECT_DIRECTORIES", NULL);
+ cl_setenv("GIT_WORK_TREE", NULL);
+ cl_setenv("GIT_COMMON_DIR", NULL);
+}
+
+void test_repo_env__initialize(void)
+{
+ clear_git_env();
+}
+
+void test_repo_env__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+
+ if (git_fs_path_isdir("attr"))
+ git_futils_rmdir_r("attr", NULL, GIT_RMDIR_REMOVE_FILES);
+ if (git_fs_path_isdir("testrepo.git"))
+ git_futils_rmdir_r("testrepo.git", NULL, GIT_RMDIR_REMOVE_FILES);
+ if (git_fs_path_isdir("peeled.git"))
+ git_futils_rmdir_r("peeled.git", NULL, GIT_RMDIR_REMOVE_FILES);
+
+ cl_fixture_cleanup("test_workdir");
+ cl_fixture_cleanup("test_global_conf");
+ cl_fixture_cleanup("test_system_conf");
+
+ clear_git_env();
+}
+
+static int GIT_FORMAT_PRINTF(2, 3) cl_setenv_printf(const char *name, const char *fmt, ...)
+{
+ int ret;
+ va_list args;
+ git_str buf = GIT_STR_INIT;
+
+ va_start(args, fmt);
+ cl_git_pass(git_str_vprintf(&buf, fmt, args));
+ va_end(args);
+
+ ret = cl_setenv(name, git_str_cstr(&buf));
+ git_str_dispose(&buf);
+ return ret;
+}
+
+/* Helper functions for test_repo_open__env, passing through the file and line
+ * from the caller rather than those of the helper. The expression strings
+ * distinguish between the possible failures within the helper. */
+
+static void env_pass_(const char *path, const char *file, const char *func, int line)
+{
+ git_repository *repo;
+ cl_git_expect(git_repository_open_ext(NULL, path, GIT_REPOSITORY_OPEN_FROM_ENV, NULL), 0, file, func, line);
+ cl_git_expect(git_repository_open_ext(&repo, path, GIT_REPOSITORY_OPEN_FROM_ENV, NULL), 0, file, func, line);
+ cl_assert_at_line(git__suffixcmp(git_repository_path(repo), "attr/.git/") == 0, file, func, line);
+ cl_assert_at_line(git__suffixcmp(git_repository_workdir(repo), "attr/") == 0, file, func, line);
+ cl_assert_at_line(!git_repository_is_bare(repo), file, func, line);
+ git_repository_free(repo);
+}
+#define env_pass(path) env_pass_((path), __FILE__, __func__, __LINE__)
+
+#define cl_git_fail_at_line(expr, file, func, line) clar__assert((expr) < 0, file, func, line, "Expected function call to fail: " #expr, NULL, 1)
+
+static void env_fail_(const char *path, const char *file, const char *func, int line)
+{
+ git_repository *repo;
+ cl_git_fail_at_line(git_repository_open_ext(NULL, path, GIT_REPOSITORY_OPEN_FROM_ENV, NULL), file, func, line);
+ cl_git_fail_at_line(git_repository_open_ext(&repo, path, GIT_REPOSITORY_OPEN_FROM_ENV, NULL), file, func, line);
+}
+#define env_fail(path) env_fail_((path), __FILE__, __func__, __LINE__)
+
+static void env_cd_(
+ const char *path,
+ void (*passfail_)(const char *, const char *, const char *, int),
+ const char *file, const char *func, int line)
+{
+ git_str cwd_buf = GIT_STR_INIT;
+ cl_git_pass(git_fs_path_prettify_dir(&cwd_buf, ".", NULL));
+ cl_must_pass(p_chdir(path));
+ passfail_(NULL, file, func, line);
+ cl_must_pass(p_chdir(git_str_cstr(&cwd_buf)));
+ git_str_dispose(&cwd_buf);
+}
+#define env_cd_pass(path) env_cd_((path), env_pass_, __FILE__, __func__, __LINE__)
+#define env_cd_fail(path) env_cd_((path), env_fail_, __FILE__, __func__, __LINE__)
+
+static void env_check_objects_(bool a, bool t, bool p, const char *file, const char *func, int line)
+{
+ git_repository *repo;
+ git_oid oid_a, oid_t, oid_p;
+ git_object *object;
+ cl_git_pass(git_oid__fromstr(&oid_a, "45141a79a77842c59a63229403220a4e4be74e3d", GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&oid_t, "1385f264afb75a56a5bec74243be9b367ba4ca08", GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&oid_p, "0df1a5865c8abfc09f1f2182e6a31be550e99f07", GIT_OID_SHA1));
+ cl_git_expect(git_repository_open_ext(&repo, "attr", GIT_REPOSITORY_OPEN_FROM_ENV, NULL), 0, file, func, line);
+
+ if (a) {
+ cl_git_expect(git_object_lookup(&object, repo, &oid_a, GIT_OBJECT_BLOB), 0, file, func, line);
+ git_object_free(object);
+ } else {
+ cl_git_fail_at_line(git_object_lookup(&object, repo, &oid_a, GIT_OBJECT_BLOB), file, func, line);
+ }
+
+ if (t) {
+ cl_git_expect(git_object_lookup(&object, repo, &oid_t, GIT_OBJECT_BLOB), 0, file, func, line);
+ git_object_free(object);
+ } else {
+ cl_git_fail_at_line(git_object_lookup(&object, repo, &oid_t, GIT_OBJECT_BLOB), file, func, line);
+ }
+
+ if (p) {
+ cl_git_expect(git_object_lookup(&object, repo, &oid_p, GIT_OBJECT_COMMIT), 0, file, func, line);
+ git_object_free(object);
+ } else {
+ cl_git_fail_at_line(git_object_lookup(&object, repo, &oid_p, GIT_OBJECT_COMMIT), file, func, line);
+ }
+
+ git_repository_free(repo);
+}
+#define env_check_objects(a, t, t2) env_check_objects_((a), (t), (t2), __FILE__, __func__, __LINE__)
+
+void test_repo_env__open(void)
+{
+ git_repository *repo = NULL;
+ git_str repo_dir_buf = GIT_STR_INIT;
+ const char *repo_dir = NULL;
+ git_index *index = NULL;
+ const char *t_obj = "testrepo.git/objects";
+ const char *p_obj = "peeled.git/objects";
+
+ clear_git_env();
+
+ cl_fixture_sandbox("attr");
+ cl_fixture_sandbox("testrepo.git");
+ cl_fixture_sandbox("peeled.git");
+ cl_git_pass(p_rename("attr/.gitted", "attr/.git"));
+
+ cl_git_pass(git_fs_path_prettify_dir(&repo_dir_buf, "attr", NULL));
+ repo_dir = git_str_cstr(&repo_dir_buf);
+
+ /* GIT_DIR that doesn't exist */
+ cl_setenv("GIT_DIR", "does-not-exist");
+ env_fail(NULL);
+ /* Explicit start_path overrides GIT_DIR */
+ env_pass("attr");
+ env_pass("attr/.git");
+ env_pass("attr/sub");
+ env_pass("attr/sub/sub");
+
+ /* GIT_DIR with relative paths */
+ cl_setenv("GIT_DIR", "attr/.git");
+ env_pass(NULL);
+ cl_setenv("GIT_DIR", "attr");
+ env_fail(NULL);
+ cl_setenv("GIT_DIR", "attr/sub");
+ env_fail(NULL);
+ cl_setenv("GIT_DIR", "attr/sub/sub");
+ env_fail(NULL);
+
+ /* GIT_DIR with absolute paths */
+ cl_setenv_printf("GIT_DIR", "%s/.git", repo_dir);
+ env_pass(NULL);
+ cl_setenv("GIT_DIR", repo_dir);
+ env_fail(NULL);
+ cl_setenv_printf("GIT_DIR", "%s/sub", repo_dir);
+ env_fail(NULL);
+ cl_setenv_printf("GIT_DIR", "%s/sub/sub", repo_dir);
+ env_fail(NULL);
+ cl_setenv("GIT_DIR", NULL);
+
+ /* Searching from the current directory */
+ env_cd_pass("attr");
+ env_cd_pass("attr/.git");
+ env_cd_pass("attr/sub");
+ env_cd_pass("attr/sub/sub");
+
+ /* A ceiling directory blocks searches from ascending into that
+ * directory, but doesn't block the start_path itself. */
+ cl_setenv("GIT_CEILING_DIRECTORIES", repo_dir);
+ env_cd_pass("attr");
+ env_cd_fail("attr/sub");
+ env_cd_fail("attr/sub/sub");
+
+ cl_setenv_printf("GIT_CEILING_DIRECTORIES", "%s/sub", repo_dir);
+ env_cd_pass("attr");
+ env_cd_pass("attr/sub");
+ env_cd_fail("attr/sub/sub");
+
+ /* Multiple ceiling directories */
+ cl_setenv_printf("GIT_CEILING_DIRECTORIES", "123%c%s/sub%cabc",
+ GIT_PATH_LIST_SEPARATOR, repo_dir, GIT_PATH_LIST_SEPARATOR);
+ env_cd_pass("attr");
+ env_cd_pass("attr/sub");
+ env_cd_fail("attr/sub/sub");
+
+ cl_setenv_printf("GIT_CEILING_DIRECTORIES", "%s%c%s/sub",
+ repo_dir, GIT_PATH_LIST_SEPARATOR, repo_dir);
+ env_cd_pass("attr");
+ env_cd_fail("attr/sub");
+ env_cd_fail("attr/sub/sub");
+
+ cl_setenv_printf("GIT_CEILING_DIRECTORIES", "%s/sub%c%s",
+ repo_dir, GIT_PATH_LIST_SEPARATOR, repo_dir);
+ env_cd_pass("attr");
+ env_cd_fail("attr/sub");
+ env_cd_fail("attr/sub/sub");
+
+ cl_setenv_printf("GIT_CEILING_DIRECTORIES", "%s%c%s/sub/sub",
+ repo_dir, GIT_PATH_LIST_SEPARATOR, repo_dir);
+ env_cd_pass("attr");
+ env_cd_fail("attr/sub");
+ env_cd_fail("attr/sub/sub");
+
+ cl_setenv("GIT_CEILING_DIRECTORIES", NULL);
+
+ /* Index files */
+ cl_setenv("GIT_INDEX_FILE", cl_fixture("gitgit.index"));
+ cl_git_pass(git_repository_open_ext(&repo, "attr", GIT_REPOSITORY_OPEN_FROM_ENV, NULL));
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert_equal_s(git_index_path(index), cl_fixture("gitgit.index"));
+ cl_assert_equal_i(git_index_entrycount(index), 1437);
+ git_index_free(index);
+ git_repository_free(repo);
+ cl_setenv("GIT_INDEX_FILE", NULL);
+
+ /* Namespaces */
+ cl_setenv("GIT_NAMESPACE", "some-namespace");
+ cl_git_pass(git_repository_open_ext(&repo, "attr", GIT_REPOSITORY_OPEN_FROM_ENV, NULL));
+ cl_assert_equal_s(git_repository_get_namespace(repo), "some-namespace");
+ git_repository_free(repo);
+ cl_setenv("GIT_NAMESPACE", NULL);
+
+ /* Object directories and alternates */
+ env_check_objects(true, false, false);
+
+ cl_setenv("GIT_OBJECT_DIRECTORY", t_obj);
+ env_check_objects(false, true, false);
+ cl_setenv("GIT_OBJECT_DIRECTORY", NULL);
+
+ cl_setenv("GIT_ALTERNATE_OBJECT_DIRECTORIES", t_obj);
+ env_check_objects(true, true, false);
+ cl_setenv("GIT_ALTERNATE_OBJECT_DIRECTORIES", NULL);
+
+ cl_setenv("GIT_OBJECT_DIRECTORY", p_obj);
+ env_check_objects(false, false, true);
+ cl_setenv("GIT_OBJECT_DIRECTORY", NULL);
+
+ cl_setenv("GIT_OBJECT_DIRECTORY", t_obj);
+ cl_setenv("GIT_ALTERNATE_OBJECT_DIRECTORIES", p_obj);
+ env_check_objects(false, true, true);
+ cl_setenv("GIT_ALTERNATE_OBJECT_DIRECTORIES", NULL);
+ cl_setenv("GIT_OBJECT_DIRECTORY", NULL);
+
+ cl_setenv_printf("GIT_ALTERNATE_OBJECT_DIRECTORIES",
+ "%s%c%s", t_obj, GIT_PATH_LIST_SEPARATOR, p_obj);
+ env_check_objects(true, true, true);
+ cl_setenv("GIT_ALTERNATE_OBJECT_DIRECTORIES", NULL);
+
+ cl_setenv_printf("GIT_ALTERNATE_OBJECT_DIRECTORIES",
+ "%s%c%s", p_obj, GIT_PATH_LIST_SEPARATOR, t_obj);
+ env_check_objects(true, true, true);
+ cl_setenv("GIT_ALTERNATE_OBJECT_DIRECTORIES", NULL);
+
+ cl_fixture_cleanup("peeled.git");
+ cl_fixture_cleanup("testrepo.git");
+ cl_fixture_cleanup("attr");
+
+ git_str_dispose(&repo_dir_buf);
+
+ clear_git_env();
+}
+
+void test_repo_env__work_tree(void)
+{
+ git_repository *repo;
+ const char *test_path;
+
+ cl_fixture_sandbox("attr");
+ cl_git_pass(p_rename("attr/.gitted", "attr/.git"));
+
+ cl_must_pass(p_mkdir("test_workdir", 0777));
+ test_path = cl_git_sandbox_path(1, "test_workdir", NULL);
+
+ cl_setenv("GIT_WORK_TREE", test_path);
+ cl_git_pass(git_repository_open_ext(&repo, "attr", GIT_REPOSITORY_OPEN_FROM_ENV, NULL));
+ cl_assert_equal_s(test_path, git_repository_workdir(repo));
+ git_repository_free(repo);
+ cl_setenv("GIT_WORK_TREE", NULL);
+}
+
+void test_repo_env__commondir(void)
+{
+ git_repository *repo;
+ const char *test_path;
+
+ cl_fixture_sandbox("attr");
+ cl_git_pass(p_rename("attr/.gitted", "attr/.git"));
+
+ cl_fixture_sandbox("testrepo.git");
+ cl_git_pass(p_rename("testrepo.git", "test_commondir"));
+
+ test_path = cl_git_sandbox_path(1, "test_commondir", NULL);
+
+ cl_setenv("GIT_COMMON_DIR", test_path);
+ cl_git_pass(git_repository_open_ext(&repo, "attr", GIT_REPOSITORY_OPEN_FROM_ENV, NULL));
+ cl_assert_equal_s(test_path, git_repository_commondir(repo));
+ git_repository_free(repo);
+ cl_setenv("GIT_COMMON_DIR", NULL);
+}
+
+void test_repo_env__config(void)
+{
+ git_repository *repo;
+ git_config *config;
+ const char *system_path, *global_path;
+ int s, g;
+
+ cl_fixture_sandbox("attr");
+ cl_git_pass(p_rename("attr/.gitted", "attr/.git"));
+
+ cl_git_rewritefile("test_system_conf", "[tttest]\n\tsys = true\n");
+ cl_git_rewritefile("test_global_conf", "[tttest]\n\tglb = true\n");
+
+ system_path = cl_git_sandbox_path(0, "test_system_conf", NULL);
+ cl_setenv("GIT_CONFIG_SYSTEM", system_path);
+
+ global_path = cl_git_sandbox_path(0, "test_global_conf", NULL);
+ cl_setenv("GIT_CONFIG_GLOBAL", global_path);
+
+ /* Ensure we can override the system and global files */
+
+ cl_git_pass(git_repository_open_ext(&repo, "attr", GIT_REPOSITORY_OPEN_FROM_ENV, NULL));
+ cl_git_pass(git_repository_config(&config, repo));
+
+ cl_git_pass(git_config_get_bool(&s, config, "tttest.sys"));
+ cl_assert_equal_i(1, s);
+ cl_git_pass(git_config_get_bool(&g, config, "tttest.glb"));
+ cl_assert_equal_i(1, g);
+
+ git_config_free(config);
+ git_repository_free(repo);
+
+ /* Further ensure we can ignore the system file. */
+ cl_setenv("GIT_CONFIG_NOSYSTEM", "TrUe");
+
+ cl_git_pass(git_repository_open_ext(&repo, "attr", GIT_REPOSITORY_OPEN_FROM_ENV, NULL));
+ cl_git_pass(git_repository_config(&config, repo));
+
+ cl_git_fail_with(GIT_ENOTFOUND, git_config_get_bool(&s, config, "tttest.sys"));
+ cl_git_pass(git_config_get_bool(&g, config, "tttest.glb"));
+ cl_assert_equal_i(1, g);
+
+ git_config_free(config);
+ git_repository_free(repo);
+
+ cl_setenv("GIT_CONFIG_NOSYSTEM", NULL);
+ cl_setenv("GIT_CONFIG_SYSTEM", NULL);
+ cl_setenv("GIT_CONFIG_GLOBAL", NULL);
+}
diff --git a/tests/libgit2/repo/extensions.c b/tests/libgit2/repo/extensions.c
new file mode 100644
index 0000000..105cdae
--- /dev/null
+++ b/tests/libgit2/repo/extensions.c
@@ -0,0 +1,72 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+#include "sysdir.h"
+#include <ctype.h>
+
+static git_repository *repo;
+
+void test_repo_extensions__initialize(void)
+{
+ git_config *config;
+
+ repo = cl_git_sandbox_init("empty_bare.git");
+
+ cl_git_pass(git_repository_config(&config, repo));
+ cl_git_pass(git_config_set_int32(config, "core.repositoryformatversion", 1));
+ git_config_free(config);
+}
+
+void test_repo_extensions__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_EXTENSIONS, NULL, 0));
+}
+
+void test_repo_extensions__builtin(void)
+{
+ git_repository *extended;
+
+ cl_repo_set_string(repo, "extensions.noop", "foobar");
+
+ cl_git_pass(git_repository_open(&extended, "empty_bare.git"));
+ cl_assert(git_repository_path(extended) != NULL);
+ cl_assert(git__suffixcmp(git_repository_path(extended), "/") == 0);
+ git_repository_free(extended);
+}
+
+void test_repo_extensions__negate_builtin(void)
+{
+ const char *in[] = { "foo", "!noop", "baz" };
+ git_repository *extended;
+
+ cl_repo_set_string(repo, "extensions.noop", "foobar");
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_EXTENSIONS, in, ARRAY_SIZE(in)));
+
+ cl_git_fail(git_repository_open(&extended, "empty_bare.git"));
+ git_repository_free(extended);
+}
+
+void test_repo_extensions__unsupported(void)
+{
+ git_repository *extended = NULL;
+
+ cl_repo_set_string(repo, "extensions.unknown", "foobar");
+
+ cl_git_fail(git_repository_open(&extended, "empty_bare.git"));
+ git_repository_free(extended);
+}
+
+void test_repo_extensions__adds_extension(void)
+{
+ const char *in[] = { "foo", "!noop", "newextension", "baz" };
+ git_repository *extended;
+
+ cl_repo_set_string(repo, "extensions.newextension", "foobar");
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_EXTENSIONS, in, ARRAY_SIZE(in)));
+
+ cl_git_pass(git_repository_open(&extended, "empty_bare.git"));
+ cl_assert(git_repository_path(extended) != NULL);
+ cl_assert(git__suffixcmp(git_repository_path(extended), "/") == 0);
+ git_repository_free(extended);
+}
diff --git a/tests/libgit2/repo/getters.c b/tests/libgit2/repo/getters.c
new file mode 100644
index 0000000..d401bb8
--- /dev/null
+++ b/tests/libgit2/repo/getters.c
@@ -0,0 +1,53 @@
+#include "clar_libgit2.h"
+#include "repo/repo_helpers.h"
+
+void test_repo_getters__is_empty_correctly_deals_with_pristine_looking_repos(void)
+{
+ git_repository *repo;
+
+ repo = cl_git_sandbox_init("empty_bare.git");
+ cl_git_remove_placeholders(git_repository_path(repo), "dummy-marker.txt");
+
+ cl_assert_equal_i(true, git_repository_is_empty(repo));
+
+ cl_git_sandbox_cleanup();
+}
+
+void test_repo_getters__is_empty_can_detect_used_repositories(void)
+{
+ git_repository *repo;
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+
+ cl_assert_equal_i(false, git_repository_is_empty(repo));
+
+ git_repository_free(repo);
+}
+
+void test_repo_getters__is_empty_can_detect_repositories_with_defaultbranch_config_empty(void)
+{
+ git_repository *repo;
+
+ create_tmp_global_config("tmp_global_path", "init.defaultBranch", "");
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+ cl_assert_equal_i(false, git_repository_is_empty(repo));
+
+ git_repository_free(repo);
+}
+
+void test_repo_getters__retrieving_the_odb_honors_the_refcount(void)
+{
+ git_odb *odb;
+ git_repository *repo;
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+
+ cl_git_pass(git_repository_odb(&odb, repo));
+ cl_assert(((git_refcount *)odb)->refcount.val == 2);
+
+ git_repository_free(repo);
+ cl_assert(((git_refcount *)odb)->refcount.val == 1);
+
+ git_odb_free(odb);
+}
diff --git a/tests/libgit2/repo/hashfile.c b/tests/libgit2/repo/hashfile.c
new file mode 100644
index 0000000..f053cb9
--- /dev/null
+++ b/tests/libgit2/repo/hashfile.c
@@ -0,0 +1,172 @@
+#include "clar_libgit2.h"
+#include "odb.h"
+
+static git_repository *_repo;
+
+void test_repo_hashfile__initialize(void)
+{
+ _repo = cl_git_sandbox_init("status");
+}
+
+void test_repo_hashfile__cleanup(void)
+{
+ cl_fixture_cleanup("absolute");
+ cl_git_sandbox_cleanup();
+ _repo = NULL;
+}
+
+void test_repo_hashfile__simple(void)
+{
+ git_oid a, b;
+ git_str full = GIT_STR_INIT;
+
+ /* hash with repo relative path */
+ cl_git_pass(git_odb__hashfile(&a, "status/current_file", GIT_OBJECT_BLOB, GIT_OID_SHA1));
+ cl_git_pass(git_repository_hashfile(&b, _repo, "current_file", GIT_OBJECT_BLOB, NULL));
+ cl_assert_equal_oid(&a, &b);
+
+ cl_git_pass(git_str_joinpath(&full, git_repository_workdir(_repo), "current_file"));
+
+ /* hash with full path */
+ cl_git_pass(git_odb__hashfile(&a, full.ptr, GIT_OBJECT_BLOB, GIT_OID_SHA1));
+ cl_git_pass(git_repository_hashfile(&b, _repo, full.ptr, GIT_OBJECT_BLOB, NULL));
+ cl_assert_equal_oid(&a, &b);
+
+ /* hash with invalid type */
+ cl_git_fail(git_odb__hashfile(&a, full.ptr, GIT_OBJECT_ANY, GIT_OID_SHA1));
+ cl_git_fail(git_repository_hashfile(&b, _repo, full.ptr, GIT_OBJECT_OFS_DELTA, NULL));
+
+ git_str_dispose(&full);
+}
+
+void test_repo_hashfile__filtered_in_workdir(void)
+{
+ git_str root = GIT_STR_INIT, txt = GIT_STR_INIT, bin = GIT_STR_INIT;
+ char cwd[GIT_PATH_MAX];
+ git_oid a, b;
+
+ cl_must_pass(p_getcwd(cwd, GIT_PATH_MAX));
+ cl_must_pass(p_mkdir("absolute", 0777));
+ cl_git_pass(git_str_joinpath(&root, cwd, "status"));
+ cl_git_pass(git_str_joinpath(&txt, root.ptr, "testfile.txt"));
+ cl_git_pass(git_str_joinpath(&bin, root.ptr, "testfile.bin"));
+
+ cl_repo_set_bool(_repo, "core.autocrlf", true);
+
+ cl_git_append2file("status/.gitattributes", "*.txt text\n*.bin binary\n\n");
+
+ /* create some sample content with CRLF in it */
+ cl_git_mkfile("status/testfile.txt", "content\r\n");
+ cl_git_mkfile("status/testfile.bin", "other\r\nstuff\r\n");
+
+ /* not equal hashes because of filtering */
+ cl_git_pass(git_odb__hashfile(&a, "status/testfile.txt", GIT_OBJECT_BLOB, GIT_OID_SHA1));
+ cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.txt", GIT_OBJECT_BLOB, NULL));
+ cl_assert(git_oid_cmp(&a, &b));
+
+ /* not equal hashes because of filtering when specified by absolute path */
+ cl_git_pass(git_odb__hashfile(&a, "status/testfile.txt", GIT_OBJECT_BLOB, GIT_OID_SHA1));
+ cl_git_pass(git_repository_hashfile(&b, _repo, txt.ptr, GIT_OBJECT_BLOB, NULL));
+ cl_assert(git_oid_cmp(&a, &b));
+
+ /* equal hashes because filter is binary */
+ cl_git_pass(git_odb__hashfile(&a, "status/testfile.bin", GIT_OBJECT_BLOB, GIT_OID_SHA1));
+ cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.bin", GIT_OBJECT_BLOB, NULL));
+ cl_assert_equal_oid(&a, &b);
+
+ /* equal hashes because filter is binary when specified by absolute path */
+ cl_git_pass(git_odb__hashfile(&a, "status/testfile.bin", GIT_OBJECT_BLOB, GIT_OID_SHA1));
+ cl_git_pass(git_repository_hashfile(&b, _repo, bin.ptr, GIT_OBJECT_BLOB, NULL));
+ cl_assert_equal_oid(&a, &b);
+
+ /* equal hashes when 'as_file' points to binary filtering */
+ cl_git_pass(git_odb__hashfile(&a, "status/testfile.txt", GIT_OBJECT_BLOB, GIT_OID_SHA1));
+ cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.txt", GIT_OBJECT_BLOB, "foo.bin"));
+ cl_assert_equal_oid(&a, &b);
+
+ /* equal hashes when 'as_file' points to binary filtering (absolute path) */
+ cl_git_pass(git_odb__hashfile(&a, "status/testfile.txt", GIT_OBJECT_BLOB, GIT_OID_SHA1));
+ cl_git_pass(git_repository_hashfile(&b, _repo, txt.ptr, GIT_OBJECT_BLOB, "foo.bin"));
+ cl_assert_equal_oid(&a, &b);
+
+ /* not equal hashes when 'as_file' points to text filtering */
+ cl_git_pass(git_odb__hashfile(&a, "status/testfile.bin", GIT_OBJECT_BLOB, GIT_OID_SHA1));
+ cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.bin", GIT_OBJECT_BLOB, "foo.txt"));
+ cl_assert(git_oid_cmp(&a, &b));
+
+ /* not equal hashes when 'as_file' points to text filtering */
+ cl_git_pass(git_odb__hashfile(&a, "status/testfile.bin", GIT_OBJECT_BLOB, GIT_OID_SHA1));
+ cl_git_pass(git_repository_hashfile(&b, _repo, bin.ptr, GIT_OBJECT_BLOB, "foo.txt"));
+ cl_assert(git_oid_cmp(&a, &b));
+
+ /* equal hashes when 'as_file' is empty and turns off filtering */
+ cl_git_pass(git_odb__hashfile(&a, "status/testfile.txt", GIT_OBJECT_BLOB, GIT_OID_SHA1));
+ cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.txt", GIT_OBJECT_BLOB, ""));
+ cl_assert_equal_oid(&a, &b);
+
+ cl_git_pass(git_odb__hashfile(&a, "status/testfile.bin", GIT_OBJECT_BLOB, GIT_OID_SHA1));
+ cl_git_pass(git_repository_hashfile(&b, _repo, "testfile.bin", GIT_OBJECT_BLOB, ""));
+ cl_assert_equal_oid(&a, &b);
+
+ cl_git_pass(git_odb__hashfile(&a, "status/testfile.txt", GIT_OBJECT_BLOB, GIT_OID_SHA1));
+ cl_git_pass(git_repository_hashfile(&b, _repo, txt.ptr, GIT_OBJECT_BLOB, ""));
+ cl_assert_equal_oid(&a, &b);
+
+ cl_git_pass(git_odb__hashfile(&a, "status/testfile.bin", GIT_OBJECT_BLOB, GIT_OID_SHA1));
+ cl_git_pass(git_repository_hashfile(&b, _repo, bin.ptr, GIT_OBJECT_BLOB, ""));
+ cl_assert_equal_oid(&a, &b);
+
+ /* some hash type failures */
+ cl_git_fail(git_odb__hashfile(&a, "status/testfile.txt", 0, GIT_OID_SHA1));
+ cl_git_fail(git_repository_hashfile(&b, _repo, "testfile.txt", GIT_OBJECT_ANY, NULL));
+
+ git_str_dispose(&txt);
+ git_str_dispose(&bin);
+ git_str_dispose(&root);
+}
+
+void test_repo_hashfile__filtered_outside_workdir(void)
+{
+ git_str root = GIT_STR_INIT, txt = GIT_STR_INIT, bin = GIT_STR_INIT;
+ char cwd[GIT_PATH_MAX];
+ git_oid a, b;
+
+ cl_must_pass(p_getcwd(cwd, GIT_PATH_MAX));
+ cl_must_pass(p_mkdir("absolute", 0777));
+ cl_git_pass(git_str_joinpath(&root, cwd, "absolute"));
+ cl_git_pass(git_str_joinpath(&txt, root.ptr, "testfile.txt"));
+ cl_git_pass(git_str_joinpath(&bin, root.ptr, "testfile.bin"));
+
+ cl_repo_set_bool(_repo, "core.autocrlf", true);
+ cl_git_append2file("status/.gitattributes", "*.txt text\n*.bin binary\n\n");
+
+ /* create some sample content with CRLF in it */
+ cl_git_mkfile("absolute/testfile.txt", "content\r\n");
+ cl_git_mkfile("absolute/testfile.bin", "other\r\nstuff\r\n");
+
+ /* not equal hashes because of filtering */
+ cl_git_pass(git_odb__hashfile(&a, "absolute/testfile.txt", GIT_OBJECT_BLOB, GIT_OID_SHA1));
+ cl_git_pass(git_repository_hashfile(&b, _repo, txt.ptr, GIT_OBJECT_BLOB, "testfile.txt"));
+ cl_assert(git_oid_cmp(&a, &b));
+
+ /* equal hashes because filter is binary */
+ cl_git_pass(git_odb__hashfile(&a, "absolute/testfile.bin", GIT_OBJECT_BLOB, GIT_OID_SHA1));
+ cl_git_pass(git_repository_hashfile(&b, _repo, bin.ptr, GIT_OBJECT_BLOB, "testfile.bin"));
+ cl_assert_equal_oid(&a, &b);
+
+ /*
+ * equal hashes because no filtering occurs for absolute paths outside the working
+ * directory unless as_path is specified
+ */
+ cl_git_pass(git_odb__hashfile(&a, "absolute/testfile.txt", GIT_OBJECT_BLOB, GIT_OID_SHA1));
+ cl_git_pass(git_repository_hashfile(&b, _repo, txt.ptr, GIT_OBJECT_BLOB, NULL));
+ cl_assert_equal_oid(&a, &b);
+
+ cl_git_pass(git_odb__hashfile(&a, "absolute/testfile.bin", GIT_OBJECT_BLOB, GIT_OID_SHA1));
+ cl_git_pass(git_repository_hashfile(&b, _repo, bin.ptr, GIT_OBJECT_BLOB, NULL));
+ cl_assert_equal_oid(&a, &b);
+
+ git_str_dispose(&txt);
+ git_str_dispose(&bin);
+ git_str_dispose(&root);
+}
diff --git a/tests/libgit2/repo/head.c b/tests/libgit2/repo/head.c
new file mode 100644
index 0000000..c886c3f
--- /dev/null
+++ b/tests/libgit2/repo/head.c
@@ -0,0 +1,182 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+#include "repo_helpers.h"
+#include "posix.h"
+#include "git2/annotated_commit.h"
+
+static const char *g_email = "foo@example.com";
+static git_repository *repo;
+
+void test_repo_head__initialize(void)
+{
+ repo = cl_git_sandbox_init("testrepo.git");
+ cl_git_pass(git_repository_set_ident(repo, "Foo Bar", g_email));
+}
+
+void test_repo_head__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_repo_head__unborn_head(void)
+{
+ git_reference *ref;
+
+ cl_git_pass(git_repository_head_detached(repo));
+
+ make_head_unborn(repo, NON_EXISTING_HEAD);
+
+ cl_assert(git_repository_head_unborn(repo) == 1);
+
+
+ /* take the repo back to it's original state */
+ cl_git_pass(git_reference_symbolic_create(&ref, repo, "HEAD", "refs/heads/master", 1, NULL));
+ cl_assert(git_repository_head_unborn(repo) == 0);
+
+ git_reference_free(ref);
+}
+
+void test_repo_head__set_head_Attaches_HEAD_to_un_unborn_branch_when_the_branch_doesnt_exist(void)
+{
+ git_reference *head;
+
+ cl_git_pass(git_repository_set_head(repo, "refs/heads/doesnt/exist/yet"));
+
+ cl_assert_equal_i(false, git_repository_head_detached(repo));
+
+ cl_assert_equal_i(GIT_EUNBORNBRANCH, git_repository_head(&head, repo));
+}
+
+void test_repo_head__set_head_Returns_ENOTFOUND_when_the_reference_doesnt_exist(void)
+{
+ cl_assert_equal_i(GIT_ENOTFOUND, git_repository_set_head(repo, "refs/tags/doesnt/exist/yet"));
+}
+
+void test_repo_head__set_head_Fails_when_the_reference_points_to_a_non_commitish(void)
+{
+ cl_git_fail(git_repository_set_head(repo, "refs/tags/point_to_blob"));
+}
+
+void test_repo_head__set_head_Attaches_HEAD_when_the_reference_points_to_a_branch(void)
+{
+ git_reference *head;
+
+ cl_git_pass(git_repository_set_head(repo, "refs/heads/br2"));
+
+ cl_assert_equal_i(false, git_repository_head_detached(repo));
+
+ cl_git_pass(git_repository_head(&head, repo));
+ cl_assert_equal_s("refs/heads/br2", git_reference_name(head));
+
+ git_reference_free(head);
+}
+
+static void assert_head_is_correctly_detached(void)
+{
+ git_reference *head;
+ git_object *commit;
+
+ cl_assert_equal_i(true, git_repository_head_detached(repo));
+
+ cl_git_pass(git_repository_head(&head, repo));
+
+ cl_git_pass(git_object_lookup(&commit, repo, git_reference_target(head), GIT_OBJECT_COMMIT));
+
+ git_object_free(commit);
+ git_reference_free(head);
+}
+
+void test_repo_head__set_head_Detaches_HEAD_when_the_reference_doesnt_point_to_a_branch(void)
+{
+ cl_git_pass(git_repository_set_head(repo, "refs/tags/test"));
+
+ cl_assert_equal_i(true, git_repository_head_detached(repo));
+
+ assert_head_is_correctly_detached();
+}
+
+void test_repo_head__set_head_detached_Return_ENOTFOUND_when_the_object_doesnt_exist(void)
+{
+ git_oid oid;
+
+ cl_git_pass(git_oid__fromstr(&oid, "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef", GIT_OID_SHA1));
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_repository_set_head_detached(repo, &oid));
+}
+
+void test_repo_head__set_head_detached_Fails_when_the_object_isnt_a_commitish(void)
+{
+ git_object *blob;
+
+ cl_git_pass(git_revparse_single(&blob, repo, "point_to_blob"));
+
+ cl_git_fail(git_repository_set_head_detached(repo, git_object_id(blob)));
+
+ git_object_free(blob);
+}
+
+void test_repo_head__set_head_detached_Detaches_HEAD_and_make_it_point_to_the_peeled_commit(void)
+{
+ git_object *tag;
+
+ cl_git_pass(git_revparse_single(&tag, repo, "tags/test"));
+ cl_assert_equal_i(GIT_OBJECT_TAG, git_object_type(tag));
+
+ cl_git_pass(git_repository_set_head_detached(repo, git_object_id(tag)));
+
+ assert_head_is_correctly_detached();
+
+ git_object_free(tag);
+}
+
+void test_repo_head__detach_head_Detaches_HEAD_and_make_it_point_to_the_peeled_commit(void)
+{
+ cl_assert_equal_i(false, git_repository_head_detached(repo));
+
+ cl_git_pass(git_repository_detach_head(repo));
+
+ assert_head_is_correctly_detached();
+}
+
+void test_repo_head__detach_head_Fails_if_HEAD_and_point_to_a_non_commitish(void)
+{
+ git_reference *head;
+
+ cl_git_pass(git_reference_symbolic_create(&head, repo, GIT_HEAD_FILE, "refs/tags/point_to_blob", 1, NULL));
+
+ cl_git_fail(git_repository_detach_head(repo));
+
+ git_reference_free(head);
+}
+
+void test_repo_head__detaching_an_unborn_branch_returns_GIT_EUNBORNBRANCH(void)
+{
+ make_head_unborn(repo, NON_EXISTING_HEAD);
+
+ cl_assert_equal_i(GIT_EUNBORNBRANCH, git_repository_detach_head(repo));
+}
+
+void test_repo_head__retrieving_an_unborn_branch_returns_GIT_EUNBORNBRANCH(void)
+{
+ git_reference *head;
+
+ make_head_unborn(repo, NON_EXISTING_HEAD);
+
+ cl_assert_equal_i(GIT_EUNBORNBRANCH, git_repository_head(&head, repo));
+}
+
+void test_repo_head__retrieving_a_missing_head_returns_GIT_ENOTFOUND(void)
+{
+ git_reference *head;
+
+ delete_head(repo);
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_repository_head(&head, repo));
+}
+
+void test_repo_head__can_tell_if_an_unborn_head_is_detached(void)
+{
+ make_head_unborn(repo, NON_EXISTING_HEAD);
+
+ cl_assert_equal_i(false, git_repository_head_detached(repo));
+}
diff --git a/tests/libgit2/repo/headtree.c b/tests/libgit2/repo/headtree.c
new file mode 100644
index 0000000..e899ac3
--- /dev/null
+++ b/tests/libgit2/repo/headtree.c
@@ -0,0 +1,53 @@
+#include "clar_libgit2.h"
+#include "repository.h"
+#include "repo_helpers.h"
+#include "posix.h"
+
+static git_repository *repo;
+static git_tree *tree;
+
+void test_repo_headtree__initialize(void)
+{
+ repo = cl_git_sandbox_init("testrepo.git");
+ tree = NULL;
+}
+
+void test_repo_headtree__cleanup(void)
+{
+ git_tree_free(tree);
+ cl_git_sandbox_cleanup();
+}
+
+void test_repo_headtree__can_retrieve_the_root_tree_from_a_detached_head(void)
+{
+ cl_git_pass(git_repository_detach_head(repo));
+
+ cl_git_pass(git_repository_head_tree(&tree, repo));
+
+ cl_assert(git_oid_streq(git_tree_id(tree), "az"));
+}
+
+void test_repo_headtree__can_retrieve_the_root_tree_from_a_non_detached_head(void)
+{
+ cl_assert_equal_i(false, git_repository_head_detached(repo));
+
+ cl_git_pass(git_repository_head_tree(&tree, repo));
+
+ cl_assert(git_oid_streq(git_tree_id(tree), "az"));
+}
+
+void test_repo_headtree__when_head_is_unborn_returns_EUNBORNBRANCH(void)
+{
+ make_head_unborn(repo, NON_EXISTING_HEAD);
+
+ cl_assert_equal_i(true, git_repository_head_unborn(repo));
+
+ cl_assert_equal_i(GIT_EUNBORNBRANCH, git_repository_head_tree(&tree, repo));
+}
+
+void test_repo_headtree__when_head_is_missing_returns_ENOTFOUND(void)
+{
+ delete_head(repo);
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_repository_head_tree(&tree, repo));
+}
diff --git a/tests/libgit2/repo/init.c b/tests/libgit2/repo/init.c
new file mode 100644
index 0000000..d78ec06
--- /dev/null
+++ b/tests/libgit2/repo/init.c
@@ -0,0 +1,757 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+#include "repository.h"
+#include "config.h"
+#include "path.h"
+#include "config/config_helpers.h"
+#include "repo/repo_helpers.h"
+
+enum repo_mode {
+ STANDARD_REPOSITORY = 0,
+ BARE_REPOSITORY = 1
+};
+
+static git_repository *g_repo = NULL;
+static git_str g_global_path = GIT_STR_INIT;
+
+void test_repo_init__initialize(void)
+{
+ g_repo = NULL;
+
+ git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL,
+ &g_global_path);
+}
+
+void test_repo_init__cleanup(void)
+{
+ git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL,
+ g_global_path.ptr);
+ git_str_dispose(&g_global_path);
+
+ cl_fixture_cleanup("tmp_global_path");
+}
+
+static void cleanup_repository(void *path)
+{
+ git_repository_free(g_repo);
+ g_repo = NULL;
+
+ cl_fixture_cleanup((const char *)path);
+}
+
+static void ensure_repository_init(
+ const char *working_directory,
+ int is_bare,
+ const char *expected_path_repository,
+ const char *expected_working_directory)
+{
+ const char *workdir;
+
+ cl_assert(!git_fs_path_isdir(working_directory));
+
+ cl_git_pass(git_repository_init(&g_repo, working_directory, is_bare));
+
+ workdir = git_repository_workdir(g_repo);
+ if (workdir != NULL || expected_working_directory != NULL) {
+ cl_assert(
+ git__suffixcmp(workdir, expected_working_directory) == 0
+ );
+ }
+
+ cl_assert(
+ git__suffixcmp(git_repository_path(g_repo), expected_path_repository) == 0
+ );
+
+ cl_assert(git_repository_is_bare(g_repo) == is_bare);
+
+#ifdef GIT_WIN32
+ if (!is_bare) {
+ DWORD fattrs = GetFileAttributes(git_repository_path(g_repo));
+ cl_assert((fattrs & FILE_ATTRIBUTE_HIDDEN) != 0);
+ }
+#endif
+
+ cl_assert(git_repository_is_empty(g_repo));
+}
+
+void test_repo_init__standard_repo(void)
+{
+ cl_set_cleanup(&cleanup_repository, "testrepo");
+ ensure_repository_init("testrepo/", 0, "testrepo/.git/", "testrepo/");
+}
+
+void test_repo_init__standard_repo_noslash(void)
+{
+ cl_set_cleanup(&cleanup_repository, "testrepo");
+ ensure_repository_init("testrepo", 0, "testrepo/.git/", "testrepo/");
+}
+
+void test_repo_init__bare_repo(void)
+{
+ cl_set_cleanup(&cleanup_repository, "testrepo.git");
+ ensure_repository_init("testrepo.git/", 1, "testrepo.git/", NULL);
+}
+
+void test_repo_init__bare_repo_noslash(void)
+{
+ cl_set_cleanup(&cleanup_repository, "testrepo.git");
+ ensure_repository_init("testrepo.git", 1, "testrepo.git/", NULL);
+}
+
+void test_repo_init__bare_repo_escaping_current_workdir(void)
+{
+ git_str path_repository = GIT_STR_INIT;
+ git_str path_current_workdir = GIT_STR_INIT;
+
+ cl_git_pass(git_fs_path_prettify_dir(&path_current_workdir, ".", NULL));
+
+ cl_git_pass(git_str_joinpath(&path_repository, git_str_cstr(&path_current_workdir), "a/b/c"));
+ cl_git_pass(git_futils_mkdir_r(git_str_cstr(&path_repository), GIT_DIR_MODE));
+
+ /* Change the current working directory */
+ cl_git_pass(chdir(git_str_cstr(&path_repository)));
+
+ /* Initialize a bare repo with a relative path escaping out of the current working directory */
+ cl_git_pass(git_repository_init(&g_repo, "../d/e.git", 1));
+ cl_git_pass(git__suffixcmp(git_repository_path(g_repo), "/a/b/d/e.git/"));
+
+ git_repository_free(g_repo);
+ g_repo = NULL;
+
+ /* Open a bare repo with a relative path escaping out of the current working directory */
+ cl_git_pass(git_repository_open(&g_repo, "../d/e.git"));
+
+ cl_git_pass(chdir(git_str_cstr(&path_current_workdir)));
+
+ git_str_dispose(&path_current_workdir);
+ git_str_dispose(&path_repository);
+
+ cleanup_repository("a");
+}
+
+void test_repo_init__reinit_bare_repo(void)
+{
+ cl_set_cleanup(&cleanup_repository, "reinit.git");
+
+ /* Initialize the repository */
+ cl_git_pass(git_repository_init(&g_repo, "reinit.git", 1));
+ git_repository_free(g_repo);
+ g_repo = NULL;
+
+ /* Reinitialize the repository */
+ cl_git_pass(git_repository_init(&g_repo, "reinit.git", 1));
+}
+
+void test_repo_init__reinit_nondefault_version(void)
+{
+ git_config *config;
+
+ cl_set_cleanup(&cleanup_repository, "reinit.git");
+
+ /* Initialize the repository */
+ cl_git_pass(git_repository_init(&g_repo, "reinit.git", 1));
+ git_repository_config(&config, g_repo);
+
+ /* Set the config to a supported but not default version */
+ cl_repo_set_string(g_repo, "core.repositoryformatversion", "1");
+ git_config_free(config);
+ git_repository_free(g_repo);
+ g_repo = NULL;
+
+ /* Try to reinitialize the repository */
+ cl_git_pass(git_repository_init(&g_repo, "reinit.git", 1));
+ cl_assert_equal_i(1, cl_repo_get_int(g_repo, "core.repositoryformatversion"));
+
+ cl_fixture_cleanup("reinit.git");
+}
+
+void test_repo_init__reinit_unsupported_version(void)
+{
+ cl_set_cleanup(&cleanup_repository, "reinit.git");
+
+ /* Initialize the repository */
+ cl_git_pass(git_repository_init(&g_repo, "reinit.git", 1));
+
+ /*
+ * Hack the config of the repository to make it look like it has
+ * been created by a too new and unsupported version of git/libgit2
+ */
+ cl_repo_set_string(g_repo, "core.repositoryformatversion", "42");
+ git_repository_free(g_repo);
+ g_repo = NULL;
+
+ /* Try and fail to reinitialize the repository */
+ cl_git_fail(git_repository_init(&g_repo, "reinit.git", 1));
+ cl_fixture_cleanup("reinit.git");
+}
+
+void test_repo_init__additional_templates(void)
+{
+ git_str path = GIT_STR_INIT;
+
+ cl_set_cleanup(&cleanup_repository, "tester");
+
+ ensure_repository_init("tester", 0, "tester/.git/", "tester/");
+
+ cl_git_pass(
+ git_str_joinpath(&path, git_repository_path(g_repo), "description"));
+ cl_assert(git_fs_path_isfile(git_str_cstr(&path)));
+
+ cl_git_pass(
+ git_str_joinpath(&path, git_repository_path(g_repo), "info/exclude"));
+ cl_assert(git_fs_path_isfile(git_str_cstr(&path)));
+
+ cl_git_pass(
+ git_str_joinpath(&path, git_repository_path(g_repo), "hooks"));
+ cl_assert(git_fs_path_isdir(git_str_cstr(&path)));
+ /* won't confirm specific contents of hooks dir since it may vary */
+
+ git_str_dispose(&path);
+}
+
+static void assert_config_entry_on_init_bytype(
+ const char *config_key, int expected_value, bool is_bare)
+{
+ git_config *config;
+ int error, current_value;
+ const char *repo_path = is_bare ?
+ "config_entry/test.bare.git" : "config_entry/test.non.bare.git";
+
+ cl_set_cleanup(&cleanup_repository, "config_entry");
+
+ cl_git_pass(git_repository_init(&g_repo, repo_path, is_bare));
+
+ cl_git_pass(git_repository_config(&config, g_repo));
+ error = git_config_get_bool(&current_value, config, config_key);
+ git_config_free(config);
+
+ if (expected_value >= 0) {
+ cl_assert_equal_i(0, error);
+ cl_assert_equal_i(expected_value, current_value);
+ } else {
+ cl_assert_equal_i(expected_value, error);
+ }
+}
+
+static void assert_config_entry_on_init(
+ const char *config_key, int expected_value)
+{
+ assert_config_entry_on_init_bytype(config_key, expected_value, true);
+ git_repository_free(g_repo);
+ g_repo = NULL;
+
+ assert_config_entry_on_init_bytype(config_key, expected_value, false);
+}
+
+void test_repo_init__detect_filemode(void)
+{
+ assert_config_entry_on_init("core.filemode", cl_is_chmod_supported());
+}
+
+void test_repo_init__detect_ignorecase(void)
+{
+ struct stat st;
+ bool found_without_match;
+
+ cl_git_write2file("testCAPS", "whatever\n", 0, O_CREAT | O_WRONLY, 0666);
+ found_without_match = (p_stat("Testcaps", &st) == 0);
+ cl_must_pass(p_unlink("testCAPS"));
+
+ assert_config_entry_on_init(
+ "core.ignorecase", found_without_match ? true : GIT_ENOTFOUND);
+}
+
+/*
+ * Windows: if the filesystem supports symlinks (because we're running
+ * as administrator, or because the user has opted into it for normal
+ * users) then we can also opt-in explicitly by settings `core.symlinks`
+ * in the global config. Symlinks remain off by default.
+ */
+
+void test_repo_init__symlinks_win32_enabled_by_global_config(void)
+{
+#ifndef GIT_WIN32
+ cl_skip();
+#else
+ git_config *config, *repo_config;
+ int val;
+
+ if (!git_fs_path_supports_symlinks("link"))
+ cl_skip();
+
+ create_tmp_global_config("tmp_global_config", "core.symlinks", "true");
+
+ /*
+ * Create a new repository (can't use `assert_config_on_init` since we
+ * want to examine configuration levels with more granularity.)
+ */
+ cl_git_pass(git_repository_init(&g_repo, "config_entry/test.non.bare.git", false));
+
+ /* Ensure that core.symlinks remains set (via the global config). */
+ cl_git_pass(git_repository_config(&config, g_repo));
+ cl_git_pass(git_config_get_bool(&val, config, "core.symlinks"));
+ cl_assert_equal_i(1, val);
+
+ /*
+ * Ensure that the repository config does not set core.symlinks.
+ * It should remain inherited.
+ */
+ cl_git_pass(git_config_open_level(&repo_config, config, GIT_CONFIG_LEVEL_LOCAL));
+ cl_git_fail_with(GIT_ENOTFOUND, git_config_get_bool(&val, repo_config, "core.symlinks"));
+ git_config_free(repo_config);
+
+ git_config_free(config);
+
+ git_repository_free(g_repo);
+ g_repo = NULL;
+#endif
+}
+
+void test_repo_init__symlinks_win32_off_by_default(void)
+{
+#ifndef GIT_WIN32
+ cl_skip();
+#else
+ assert_config_entry_on_init("core.symlinks", false);
+#endif
+}
+
+void test_repo_init__symlinks_posix_detected(void)
+{
+#ifdef GIT_WIN32
+ cl_skip();
+#else
+ assert_config_entry_on_init(
+ "core.symlinks", git_fs_path_supports_symlinks("link") ? GIT_ENOTFOUND : false);
+#endif
+}
+
+void test_repo_init__detect_precompose_unicode_required(void)
+{
+#ifdef GIT_USE_ICONV
+ char *composed = "ḱṷṓn", *decomposed = "kÌuÌ­oÌ„Ìn";
+ struct stat st;
+ bool found_with_nfd;
+
+ cl_git_write2file(composed, "whatever\n", 0, O_CREAT | O_WRONLY, 0666);
+ found_with_nfd = (p_stat(decomposed, &st) == 0);
+ cl_must_pass(p_unlink(composed));
+
+ assert_config_entry_on_init("core.precomposeunicode", found_with_nfd);
+#else
+ assert_config_entry_on_init("core.precomposeunicode", GIT_ENOTFOUND);
+#endif
+}
+
+void test_repo_init__reinit_doesnot_overwrite_ignorecase(void)
+{
+ git_config *config;
+ int current_value;
+
+ /* Init a new repo */
+ cl_set_cleanup(&cleanup_repository, "not.overwrite.git");
+ cl_git_pass(git_repository_init(&g_repo, "not.overwrite.git", 1));
+
+ /* Change the "core.ignorecase" config value to something unlikely */
+ git_repository_config(&config, g_repo);
+ git_config_set_int32(config, "core.ignorecase", 42);
+ git_config_free(config);
+ git_repository_free(g_repo);
+ g_repo = NULL;
+
+ /* Reinit the repository */
+ cl_git_pass(git_repository_init(&g_repo, "not.overwrite.git", 1));
+ git_repository_config(&config, g_repo);
+
+ /* Ensure the "core.ignorecase" config value hasn't been updated */
+ cl_git_pass(git_config_get_int32(&current_value, config, "core.ignorecase"));
+ cl_assert_equal_i(42, current_value);
+
+ git_config_free(config);
+}
+
+void test_repo_init__reinit_overwrites_filemode(void)
+{
+ int expected = cl_is_chmod_supported(), current_value;
+
+ /* Init a new repo */
+ cl_set_cleanup(&cleanup_repository, "overwrite.git");
+ cl_git_pass(git_repository_init(&g_repo, "overwrite.git", 1));
+
+ /* Change the "core.filemode" config value to something unlikely */
+ cl_repo_set_bool(g_repo, "core.filemode", !expected);
+
+ git_repository_free(g_repo);
+ g_repo = NULL;
+
+ /* Reinit the repository */
+ cl_git_pass(git_repository_init(&g_repo, "overwrite.git", 1));
+
+ /* Ensure the "core.filemode" config value has been reset */
+ current_value = cl_repo_get_bool(g_repo, "core.filemode");
+ cl_assert_equal_i(expected, current_value);
+}
+
+void test_repo_init__sets_logAllRefUpdates_according_to_type_of_repository(void)
+{
+ assert_config_entry_on_init_bytype("core.logallrefupdates", GIT_ENOTFOUND, true);
+ git_repository_free(g_repo);
+ assert_config_entry_on_init_bytype("core.logallrefupdates", true, false);
+}
+
+void test_repo_init__extended_0(void)
+{
+ git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+
+ /* without MKDIR this should fail */
+ cl_git_fail(git_repository_init_ext(&g_repo, "extended", &opts));
+
+ /* make the directory first, then it should succeed */
+ cl_git_pass(git_futils_mkdir("extended", 0775, 0));
+ cl_git_pass(git_repository_init_ext(&g_repo, "extended", &opts));
+
+ cl_assert(!git__suffixcmp(git_repository_workdir(g_repo), "/extended/"));
+ cl_assert(!git__suffixcmp(git_repository_path(g_repo), "/extended/.git/"));
+ cl_assert(!git_repository_is_bare(g_repo));
+ cl_assert(git_repository_is_empty(g_repo));
+
+ cleanup_repository("extended");
+}
+
+void test_repo_init__extended_1(void)
+{
+ git_reference *ref;
+ git_remote *remote;
+ struct stat st;
+ git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+
+ opts.flags = GIT_REPOSITORY_INIT_MKPATH |
+ GIT_REPOSITORY_INIT_NO_DOTGIT_DIR;
+ opts.mode = GIT_REPOSITORY_INIT_SHARED_GROUP;
+ opts.workdir_path = "../c_wd";
+ opts.description = "Awesomest test repository evah";
+ opts.initial_head = "development";
+ opts.origin_url = "https://github.com/libgit2/libgit2.git";
+
+ cl_git_pass(git_repository_init_ext(&g_repo, "root/b/c.git", &opts));
+
+ cl_assert(!git__suffixcmp(git_repository_workdir(g_repo), "/c_wd/"));
+ cl_assert(!git__suffixcmp(git_repository_path(g_repo), "/c.git/"));
+ cl_assert(git_fs_path_isfile("root/b/c_wd/.git"));
+ cl_assert(!git_repository_is_bare(g_repo));
+ /* repo will not be counted as empty because we set head to "development" */
+ cl_assert(!git_repository_is_empty(g_repo));
+
+ cl_git_pass(git_fs_path_lstat(git_repository_path(g_repo), &st));
+ cl_assert(S_ISDIR(st.st_mode));
+ if (cl_is_chmod_supported())
+ cl_assert((S_ISGID & st.st_mode) == S_ISGID);
+ else
+ cl_assert((S_ISGID & st.st_mode) == 0);
+
+ cl_git_pass(git_reference_lookup(&ref, g_repo, "HEAD"));
+ cl_assert(git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC);
+ cl_assert_equal_s("refs/heads/development", git_reference_symbolic_target(ref));
+ git_reference_free(ref);
+
+ cl_git_pass(git_remote_lookup(&remote, g_repo, "origin"));
+ cl_assert_equal_s("origin", git_remote_name(remote));
+ cl_assert_equal_s(opts.origin_url, git_remote_url(remote));
+ git_remote_free(remote);
+
+ git_repository_free(g_repo);
+ cl_fixture_cleanup("root");
+}
+
+void test_repo_init__relative_gitdir(void)
+{
+ git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+ git_str dot_git_content = GIT_STR_INIT;
+
+ opts.workdir_path = "../c_wd";
+ opts.flags =
+ GIT_REPOSITORY_INIT_MKPATH |
+ GIT_REPOSITORY_INIT_RELATIVE_GITLINK |
+ GIT_REPOSITORY_INIT_NO_DOTGIT_DIR;
+
+ /* make the directory first, then it should succeed */
+ cl_git_pass(git_repository_init_ext(&g_repo, "root/b/my_repository", &opts));
+
+ cl_assert(!git__suffixcmp(git_repository_workdir(g_repo), "root/b/c_wd/"));
+ cl_assert(!git__suffixcmp(git_repository_path(g_repo), "root/b/my_repository/"));
+ cl_assert(!git_repository_is_bare(g_repo));
+ cl_assert(git_repository_is_empty(g_repo));
+
+ /* Verify that the gitlink and worktree entries are relative */
+
+ /* Verify worktree */
+ assert_config_entry_value(g_repo, "core.worktree", "../c_wd/");
+
+ /* Verify gitlink */
+ cl_git_pass(git_futils_readbuffer(&dot_git_content, "root/b/c_wd/.git"));
+ cl_assert_equal_s("gitdir: ../my_repository/", dot_git_content.ptr);
+
+ git_str_dispose(&dot_git_content);
+ cleanup_repository("root");
+}
+
+void test_repo_init__relative_gitdir_2(void)
+{
+ git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+ git_str dot_git_content = GIT_STR_INIT;
+ git_str full_path = GIT_STR_INIT;
+
+ cl_git_pass(git_fs_path_prettify(&full_path, ".", NULL));
+ cl_git_pass(git_str_joinpath(&full_path, full_path.ptr, "root/b/c_wd"));
+
+ opts.workdir_path = full_path.ptr;
+ opts.flags =
+ GIT_REPOSITORY_INIT_MKPATH |
+ GIT_REPOSITORY_INIT_RELATIVE_GITLINK |
+ GIT_REPOSITORY_INIT_NO_DOTGIT_DIR;
+
+ /* make the directory first, then it should succeed */
+ cl_git_pass(git_repository_init_ext(&g_repo, "root/b/my_repository", &opts));
+ git_str_dispose(&full_path);
+
+ cl_assert(!git__suffixcmp(git_repository_workdir(g_repo), "root/b/c_wd/"));
+ cl_assert(!git__suffixcmp(git_repository_path(g_repo), "root/b/my_repository/"));
+ cl_assert(!git_repository_is_bare(g_repo));
+ cl_assert(git_repository_is_empty(g_repo));
+
+ /* Verify that the gitlink and worktree entries are relative */
+
+ /* Verify worktree */
+ assert_config_entry_value(g_repo, "core.worktree", "../c_wd/");
+
+ /* Verify gitlink */
+ cl_git_pass(git_futils_readbuffer(&dot_git_content, "root/b/c_wd/.git"));
+ cl_assert_equal_s("gitdir: ../my_repository/", dot_git_content.ptr);
+
+ git_str_dispose(&dot_git_content);
+ cleanup_repository("root");
+}
+
+void test_repo_init__can_reinit_an_initialized_repository(void)
+{
+ git_repository *reinit;
+
+ cl_set_cleanup(&cleanup_repository, "extended");
+
+ cl_git_pass(git_futils_mkdir("extended", 0775, 0));
+ cl_git_pass(git_repository_init(&g_repo, "extended", false));
+
+ cl_git_pass(git_repository_init(&reinit, "extended", false));
+
+ cl_assert_equal_s(git_repository_path(g_repo), git_repository_path(reinit));
+
+ git_repository_free(reinit);
+}
+
+void test_repo_init__init_with_initial_commit(void)
+{
+ git_index *index;
+
+ cl_set_cleanup(&cleanup_repository, "committed");
+
+ /* Initialize the repository */
+ cl_git_pass(git_repository_init(&g_repo, "committed", 0));
+
+ /* Index will be automatically created when requested for a new repo */
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ /* Create a file so we can commit it
+ *
+ * If you are writing code outside the test suite, you can create this
+ * file any way that you like, such as:
+ * FILE *fp = fopen("committed/file.txt", "w");
+ * fputs("some stuff\n", fp);
+ * fclose(fp);
+ * We like to use the help functions because they do error detection
+ * in a way that's easily compatible with our test suite.
+ */
+ cl_git_mkfile("committed/file.txt", "some stuff\n");
+
+ /* Add file to the index */
+ cl_git_pass(git_index_add_bypath(index, "file.txt"));
+ cl_git_pass(git_index_write(index));
+
+ /* Intentionally not using cl_repo_commit_from_index here so this code
+ * can be used as an example of how an initial commit is typically
+ * made to a repository...
+ */
+
+ /* Make sure we're ready to use git_signature_default :-) */
+ {
+ git_config *cfg, *local;
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_git_pass(git_config_open_level(&local, cfg, GIT_CONFIG_LEVEL_LOCAL));
+ cl_git_pass(git_config_set_string(local, "user.name", "Test User"));
+ cl_git_pass(git_config_set_string(local, "user.email", "t@example.com"));
+ git_config_free(local);
+ git_config_free(cfg);
+ }
+
+ /* Create a commit with the new contents of the index */
+ {
+ git_signature *sig;
+ git_oid tree_id, commit_id;
+ git_tree *tree;
+
+ cl_git_pass(git_signature_default(&sig, g_repo));
+ cl_git_pass(git_index_write_tree(&tree_id, index));
+ cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
+
+ cl_git_pass(git_commit_create_v(
+ &commit_id, g_repo, "HEAD", sig, sig,
+ NULL, "First", tree, 0));
+
+ git_tree_free(tree);
+ git_signature_free(sig);
+ }
+
+ git_index_free(index);
+}
+
+void test_repo_init__at_filesystem_root(void)
+{
+ git_repository *repo;
+ const char *sandbox = clar_sandbox_path();
+ git_str root = GIT_STR_INIT;
+ int root_len;
+
+ if (!cl_is_env_set("GITTEST_INVASIVE_FS_STRUCTURE"))
+ cl_skip();
+
+ root_len = git_fs_path_root(sandbox);
+ cl_assert(root_len >= 0);
+
+ git_str_put(&root, sandbox, root_len+1);
+ git_str_joinpath(&root, root.ptr, "libgit2_test_dir");
+
+ cl_assert(!git_fs_path_exists(root.ptr));
+
+ cl_git_pass(git_repository_init(&repo, root.ptr, 0));
+ cl_assert(git_fs_path_isdir(root.ptr));
+ cl_git_pass(git_futils_rmdir_r(root.ptr, NULL, GIT_RMDIR_REMOVE_FILES));
+
+ git_str_dispose(&root);
+ git_repository_free(repo);
+}
+
+void test_repo_init__nonexisting_directory(void)
+{
+ git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+ git_repository *repo;
+
+ /*
+ * If creating a repo with non-existing parent directories, then libgit2
+ * will by default create the complete directory hierarchy if using
+ * `git_repository_init`. Thus, let's use the extended version and not
+ * set the `GIT_REPOSITORY_INIT_MKPATH` flag.
+ */
+ cl_git_fail(git_repository_init_ext(&repo, "nonexisting/path", &opts));
+}
+
+void test_repo_init__nonexisting_root(void)
+{
+#ifdef GIT_WIN32
+ git_repository *repo;
+
+ /*
+ * This really only depends on the nonexistence of the Q: drive. We
+ * cannot implement the equivalent test on Unix systems, as there is
+ * fundamentally no path that is disconnected from the root directory.
+ */
+ cl_git_fail(git_repository_init(&repo, "Q:/non/existent/path", 0));
+ cl_git_fail(git_repository_init(&repo, "Q:\\non\\existent\\path", 0));
+#else
+ clar__skip();
+#endif
+}
+
+void test_repo_init__unwriteable_directory(void)
+{
+#ifndef GIT_WIN32
+ git_repository *repo;
+
+ if (geteuid() == 0)
+ clar__skip();
+
+ /*
+ * Create a non-writeable directory so that we cannot create directories
+ * inside of it. The root user has CAP_DAC_OVERRIDE, so he doesn't care
+ * for the directory permissions and thus we need to skip the test if
+ * run as root user.
+ */
+ cl_must_pass(p_mkdir("unwriteable", 0444));
+ cl_git_fail(git_repository_init(&repo, "unwriteable/repo", 0));
+ cl_must_pass(p_rmdir("unwriteable"));
+#else
+ clar__skip();
+#endif
+}
+
+void test_repo_init__defaultbranch_config(void)
+{
+ git_reference *head;
+
+ cl_set_cleanup(&cleanup_repository, "repo");
+
+ create_tmp_global_config("tmp_global_path", "init.defaultbranch", "my_default_branch");
+
+ cl_git_pass(git_repository_init(&g_repo, "repo", 0));
+ cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
+
+ cl_assert_equal_s("refs/heads/my_default_branch", git_reference_symbolic_target(head));
+
+ git_reference_free(head);
+}
+
+void test_repo_init__defaultbranch_config_empty(void)
+{
+ git_reference *head;
+
+ cl_set_cleanup(&cleanup_repository, "repo");
+
+ create_tmp_global_config("tmp_global_path", "init.defaultbranch", "");
+
+ cl_git_pass(git_repository_init(&g_repo, "repo", 0));
+ cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
+
+ cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head));
+
+ git_reference_free(head);
+}
+
+void test_repo_init__longpath(void)
+{
+#ifdef GIT_WIN32
+ size_t padding = CONST_STRLEN("objects/pack/pack-.pack.lock") + GIT_OID_MAX_HEXSIZE;
+ size_t max, i;
+ git_str path = GIT_STR_INIT;
+ git_repository *one = NULL, *two = NULL;
+
+ /*
+ * Files within repositories need to fit within MAX_PATH;
+ * that means a repo path must be at most (MAX_PATH - 18).
+ */
+ cl_git_pass(git_str_puts(&path, clar_sandbox_path()));
+ cl_git_pass(git_str_putc(&path, '/'));
+
+ max = ((MAX_PATH) - path.size) - padding;
+
+ for (i = 0; i < max - 1; i++)
+ cl_git_pass(git_str_putc(&path, 'a'));
+
+ cl_git_pass(git_repository_init(&one, path.ptr, 1));
+
+ /* Paths longer than this are rejected */
+ cl_git_pass(git_str_putc(&path, 'z'));
+ cl_git_fail(git_repository_init(&two, path.ptr, 1));
+
+ git_repository_free(one);
+ git_repository_free(two);
+ git_str_dispose(&path);
+#endif
+}
diff --git a/tests/libgit2/repo/message.c b/tests/libgit2/repo/message.c
new file mode 100644
index 0000000..6241f48
--- /dev/null
+++ b/tests/libgit2/repo/message.c
@@ -0,0 +1,39 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+#include "posix.h"
+
+static git_repository *_repo;
+
+void test_repo_message__initialize(void)
+{
+ _repo = cl_git_sandbox_init("testrepo.git");
+}
+
+void test_repo_message__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_repo_message__none(void)
+{
+ git_buf actual = GIT_BUF_INIT;
+ cl_assert_equal_i(GIT_ENOTFOUND, git_repository_message(&actual, _repo));
+}
+
+void test_repo_message__message(void)
+{
+ git_str path = GIT_STR_INIT;
+ git_buf actual = GIT_BUF_INIT;
+ const char expected[] = "Test\n\nThis is a test of the emergency broadcast system\n";
+
+ cl_git_pass(git_str_joinpath(&path, git_repository_path(_repo), "MERGE_MSG"));
+ cl_git_mkfile(git_str_cstr(&path), expected);
+
+ cl_git_pass(git_repository_message(&actual, _repo));
+ cl_assert_equal_s(expected, actual.ptr);
+ git_buf_dispose(&actual);
+
+ cl_git_pass(p_unlink(git_str_cstr(&path)));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_repository_message(&actual, _repo));
+ git_str_dispose(&path);
+}
diff --git a/tests/libgit2/repo/new.c b/tests/libgit2/repo/new.c
new file mode 100644
index 0000000..d77e903
--- /dev/null
+++ b/tests/libgit2/repo/new.c
@@ -0,0 +1,27 @@
+#include "clar_libgit2.h"
+#include "git2/sys/repository.h"
+
+void test_repo_new__has_nothing(void)
+{
+ git_repository *repo;
+
+ cl_git_pass(git_repository_new(&repo));
+ cl_assert_equal_b(true, git_repository_is_bare(repo));
+ cl_assert_equal_p(NULL, git_repository_path(repo));
+ cl_assert_equal_p(NULL, git_repository_workdir(repo));
+ git_repository_free(repo);
+}
+
+void test_repo_new__is_bare_until_workdir_set(void)
+{
+ git_repository *repo;
+
+ cl_git_pass(git_repository_new(&repo));
+ cl_assert_equal_b(true, git_repository_is_bare(repo));
+
+ cl_git_pass(git_repository_set_workdir(repo, clar_sandbox_path(), 0));
+ cl_assert_equal_b(false, git_repository_is_bare(repo));
+
+ git_repository_free(repo);
+}
+
diff --git a/tests/libgit2/repo/objectformat.c b/tests/libgit2/repo/objectformat.c
new file mode 100644
index 0000000..d278e10
--- /dev/null
+++ b/tests/libgit2/repo/objectformat.c
@@ -0,0 +1,69 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+#include "sysdir.h"
+#include "repository.h"
+#include <ctype.h>
+
+static git_repository *repo;
+static git_config *config;
+
+void test_repo_objectformat__initialize(void)
+{
+ repo = cl_git_sandbox_init("empty_bare.git");
+
+ cl_git_pass(git_repository_config(&config, repo));
+ cl_git_pass(git_config_set_int32(config, "core.repositoryformatversion", 1));
+}
+
+void test_repo_objectformat__cleanup(void)
+{
+ git_config_free(config);
+ cl_git_sandbox_cleanup();
+}
+
+void test_repo_objectformat__unspecified(void)
+{
+ git_repository *other;
+
+ cl_git_pass(git_repository_open(&other, "empty_bare.git"));
+ cl_assert_equal_i(GIT_OID_SHA1, git_repository_oid_type(other));
+ git_repository_free(other);
+}
+
+void test_repo_objectformat__sha1(void)
+{
+ git_repository *other;
+
+ cl_git_pass(git_config_set_string(config, "extensions.objectformat", "sha1"));
+
+ cl_git_pass(git_repository_open(&other, "empty_bare.git"));
+ cl_assert_equal_i(GIT_OID_SHA1, git_repository_oid_type(other));
+ git_repository_free(other);
+}
+
+void test_repo_objectformat__sha256(void)
+{
+#ifndef GIT_EXPERIMENTAL_SHA256
+ cl_skip();
+#else
+ git_repository *other;
+
+ cl_git_pass(git_config_set_string(config, "extensions.objectformat", "sha256"));
+
+ cl_git_pass(git_repository_open(&other, "empty_bare.git"));
+ cl_assert_equal_i(GIT_OID_SHA256, git_repository_oid_type(other));
+ git_repository_free(other);
+#endif
+}
+
+void test_repo_objectformat__invalid(void)
+{
+ git_repository *other;
+
+ cl_git_pass(git_config_set_string(config, "extensions.objectformat", "bogus"));
+
+ cl_git_fail_with(GIT_EINVALID, git_repository_open(&other, "empty_bare.git"));
+ cl_assert_equal_s("unknown object format 'bogus'", git_error_last()->message);
+ git_repository_free(other);
+}
+
diff --git a/tests/libgit2/repo/open.c b/tests/libgit2/repo/open.c
new file mode 100644
index 0000000..3d1a062
--- /dev/null
+++ b/tests/libgit2/repo/open.c
@@ -0,0 +1,762 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+#include "sysdir.h"
+#include <ctype.h>
+
+static int validate_ownership = 0;
+static git_buf config_path = GIT_BUF_INIT;
+
+void test_repo_open__initialize(void)
+{
+ cl_git_pass(git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, &config_path));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_GET_OWNER_VALIDATION, &validate_ownership));
+}
+
+void test_repo_open__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ cl_fixture_cleanup("empty_standard_repo");
+ cl_fixture_cleanup("testrepo.git");
+ cl_fixture_cleanup("__global_config");
+
+ if (git_fs_path_isdir("alternate"))
+ git_futils_rmdir_r("alternate", NULL, GIT_RMDIR_REMOVE_FILES);
+
+ git_fs_path__set_owner(GIT_FS_PATH_OWNER_NONE);
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, config_path.ptr));
+ git_buf_dispose(&config_path);
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_OWNER_VALIDATION, validate_ownership));
+}
+
+void test_repo_open__bare_empty_repo(void)
+{
+ git_repository *repo = cl_git_sandbox_init("empty_bare.git");
+
+ cl_assert(git_repository_path(repo) != NULL);
+ cl_assert(git__suffixcmp(git_repository_path(repo), "/") == 0);
+ cl_assert(git_repository_workdir(repo) == NULL);
+}
+
+void test_repo_open__format_version_1(void)
+{
+ git_repository *repo;
+ git_config *config;
+
+ repo = cl_git_sandbox_init("empty_bare.git");
+
+ cl_git_pass(git_repository_open(&repo, "empty_bare.git"));
+ cl_git_pass(git_repository_config(&config, repo));
+
+ cl_git_pass(git_config_set_int32(config, "core.repositoryformatversion", 1));
+
+ git_config_free(config);
+ git_repository_free(repo);
+
+ cl_git_pass(git_repository_open(&repo, "empty_bare.git"));
+ cl_assert(git_repository_path(repo) != NULL);
+ cl_assert(git__suffixcmp(git_repository_path(repo), "/") == 0);
+ git_repository_free(repo);
+}
+
+void test_repo_open__standard_empty_repo_through_gitdir(void)
+{
+ git_repository *repo;
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("empty_standard_repo/.gitted")));
+
+ cl_assert(git_repository_path(repo) != NULL);
+ cl_assert(git__suffixcmp(git_repository_path(repo), "/") == 0);
+
+ cl_assert(git_repository_workdir(repo) != NULL);
+ cl_assert(git__suffixcmp(git_repository_workdir(repo), "/") == 0);
+
+ git_repository_free(repo);
+}
+
+void test_repo_open__standard_empty_repo_through_workdir(void)
+{
+ git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_assert(git_repository_path(repo) != NULL);
+ cl_assert(git__suffixcmp(git_repository_path(repo), "/") == 0);
+
+ cl_assert(git_repository_workdir(repo) != NULL);
+ cl_assert(git__suffixcmp(git_repository_workdir(repo), "/") == 0);
+}
+
+
+void test_repo_open__open_with_discover(void)
+{
+ static const char *variants[] = {
+ "attr", "attr/", "attr/.git", "attr/.git/",
+ "attr/sub", "attr/sub/", "attr/sub/sub", "attr/sub/sub/",
+ NULL
+ };
+ git_repository *repo;
+ const char **scan;
+
+ cl_fixture_sandbox("attr");
+ cl_git_pass(p_rename("attr/.gitted", "attr/.git"));
+
+ for (scan = variants; *scan != NULL; scan++) {
+ cl_git_pass(git_repository_open_ext(&repo, *scan, 0, NULL));
+ cl_assert(git__suffixcmp(git_repository_path(repo), "attr/.git/") == 0);
+ cl_assert(git__suffixcmp(git_repository_workdir(repo), "attr/") == 0);
+ git_repository_free(repo);
+ }
+
+ cl_fixture_cleanup("attr");
+}
+
+void test_repo_open__check_if_repository(void)
+{
+ cl_git_sandbox_init("empty_standard_repo");
+
+ /* Pass NULL for the output parameter to check for but not open the repo */
+ cl_git_pass(git_repository_open_ext(NULL, "empty_standard_repo", 0, NULL));
+ cl_git_fail(git_repository_open_ext(NULL, "repo_does_not_exist", 0, NULL));
+
+ cl_fixture_cleanup("empty_standard_repo");
+}
+
+static void make_gitlink_dir(const char *dir, const char *linktext)
+{
+ git_str path = GIT_STR_INIT;
+
+ cl_git_pass(git_futils_mkdir(dir, 0777, GIT_MKDIR_VERIFY_DIR));
+ cl_git_pass(git_str_joinpath(&path, dir, ".git"));
+ cl_git_rewritefile(path.ptr, linktext);
+ git_str_dispose(&path);
+}
+
+void test_repo_open__gitlinked(void)
+{
+ /* need to have both repo dir and workdir set up correctly */
+ git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
+ git_repository *repo2;
+
+ make_gitlink_dir("alternate", "gitdir: ../empty_standard_repo/.git");
+
+ cl_git_pass(git_repository_open(&repo2, "alternate"));
+
+ cl_assert(git_repository_path(repo2) != NULL);
+ cl_assert_(git__suffixcmp(git_repository_path(repo2), "empty_standard_repo/.git/") == 0, git_repository_path(repo2));
+ cl_assert_equal_s(git_repository_path(repo), git_repository_path(repo2));
+
+ cl_assert(git_repository_workdir(repo2) != NULL);
+ cl_assert_(git__suffixcmp(git_repository_workdir(repo2), "alternate/") == 0, git_repository_workdir(repo2));
+
+ git_repository_free(repo2);
+}
+
+void test_repo_open__with_symlinked_config(void)
+{
+#ifndef GIT_WIN32
+ git_str path = GIT_STR_INIT;
+ git_repository *repo;
+ git_config *cfg;
+ int32_t value;
+
+ cl_git_sandbox_init("empty_standard_repo");
+
+ /* Setup .gitconfig as symlink */
+ cl_git_pass(git_futils_mkdir_r("home", 0777));
+ cl_git_mkfile("home/.gitconfig.linked", "[global]\ntest = 4567\n");
+ cl_must_pass(symlink(".gitconfig.linked", "home/.gitconfig"));
+ cl_git_pass(git_fs_path_prettify(&path, "home", NULL));
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
+
+ cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
+ cl_git_pass(git_config_open_default(&cfg));
+ cl_git_pass(git_config_get_int32(&value, cfg, "global.test"));
+ cl_assert_equal_i(4567, value);
+
+ git_config_free(cfg);
+ git_repository_free(repo);
+ cl_git_pass(git_futils_rmdir_r(git_str_cstr(&path), NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_sandbox_set_search_path_defaults();
+ git_str_dispose(&path);
+#endif
+}
+
+void test_repo_open__from_git_new_workdir(void)
+{
+#ifndef GIT_WIN32
+ /* The git-new-workdir script that ships with git sets up a bunch of
+ * symlinks to create a second workdir that shares the object db with
+ * another checkout. Libgit2 can open a repo that has been configured
+ * this way.
+ */
+
+ git_repository *repo2;
+ git_str link_tgt = GIT_STR_INIT, link = GIT_STR_INIT, body = GIT_STR_INIT;
+ const char **scan;
+ int link_fd;
+ static const char *links[] = {
+ "config", "refs", "logs/refs", "objects", "info", "hooks",
+ "packed-refs", "remotes", "rr-cache", "svn", NULL
+ };
+ static const char *copies[] = {
+ "HEAD", NULL
+ };
+
+ cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_pass(p_mkdir("alternate", 0777));
+ cl_git_pass(p_mkdir("alternate/.git", 0777));
+
+ for (scan = links; *scan != NULL; scan++) {
+ git_str_joinpath(&link_tgt, "empty_standard_repo/.git", *scan);
+ if (git_fs_path_exists(link_tgt.ptr)) {
+ git_str_joinpath(&link_tgt, "../../empty_standard_repo/.git", *scan);
+ git_str_joinpath(&link, "alternate/.git", *scan);
+ if (strchr(*scan, '/'))
+ git_futils_mkpath2file(link.ptr, 0777);
+ cl_assert_(symlink(link_tgt.ptr, link.ptr) == 0, strerror(errno));
+ }
+ }
+ for (scan = copies; *scan != NULL; scan++) {
+ git_str_joinpath(&link_tgt, "empty_standard_repo/.git", *scan);
+ if (git_fs_path_exists(link_tgt.ptr)) {
+ git_str_joinpath(&link, "alternate/.git", *scan);
+ cl_git_pass(git_futils_readbuffer(&body, link_tgt.ptr));
+
+ cl_assert((link_fd = git_futils_creat_withpath(link.ptr, 0777, 0666)) >= 0);
+ cl_must_pass(p_write(link_fd, body.ptr, body.size));
+ p_close(link_fd);
+ }
+ }
+
+ git_str_dispose(&link_tgt);
+ git_str_dispose(&link);
+ git_str_dispose(&body);
+
+
+ cl_git_pass(git_repository_open(&repo2, "alternate"));
+
+ cl_assert(git_repository_path(repo2) != NULL);
+ cl_assert_(git__suffixcmp(git_repository_path(repo2), "alternate/.git/") == 0, git_repository_path(repo2));
+
+ cl_assert(git_repository_workdir(repo2) != NULL);
+ cl_assert_(git__suffixcmp(git_repository_workdir(repo2), "alternate/") == 0, git_repository_workdir(repo2));
+
+ git_repository_free(repo2);
+#else
+ cl_skip();
+#endif
+}
+
+void test_repo_open__failures(void)
+{
+ git_repository *base, *repo;
+ git_str ceiling = GIT_STR_INIT;
+
+ base = cl_git_sandbox_init("attr");
+ cl_git_pass(git_str_sets(&ceiling, git_repository_workdir(base)));
+
+ /* fail with no searching */
+ cl_git_fail(git_repository_open(&repo, "attr/sub"));
+ cl_git_fail(git_repository_open_ext(
+ &repo, "attr/sub", GIT_REPOSITORY_OPEN_NO_SEARCH, NULL));
+
+ /* fail with ceiling too low */
+ cl_git_fail(git_repository_open_ext(&repo, "attr/sub", 0, ceiling.ptr));
+ cl_git_pass(git_str_joinpath(&ceiling, ceiling.ptr, "sub"));
+ cl_git_fail(git_repository_open_ext(&repo, "attr/sub/sub", 0, ceiling.ptr));
+
+ /* fail with no repo */
+ cl_git_pass(p_mkdir("alternate", 0777));
+ cl_git_pass(p_mkdir("alternate/.git", 0777));
+ cl_git_fail(git_repository_open_ext(&repo, "alternate", 0, NULL));
+ cl_git_fail(git_repository_open_ext(&repo, "alternate/.git", 0, NULL));
+
+ /* fail with no searching and no appending .git */
+ cl_git_fail(git_repository_open_ext(
+ &repo, "attr",
+ GIT_REPOSITORY_OPEN_NO_SEARCH | GIT_REPOSITORY_OPEN_NO_DOTGIT,
+ NULL));
+
+ git_str_dispose(&ceiling);
+}
+
+void test_repo_open__bad_gitlinks(void)
+{
+ git_repository *repo;
+ static const char *bad_links[] = {
+ "garbage\n", "gitdir", "gitdir:\n", "gitdir: foobar",
+ "gitdir: ../invalid", "gitdir: ../invalid2",
+ "gitdir: ../attr/.git with extra stuff",
+ NULL
+ };
+ const char **scan;
+
+ cl_git_sandbox_init("attr");
+
+ cl_git_pass(p_mkdir("invalid", 0777));
+ cl_git_pass(git_futils_mkdir_r("invalid2/.git", 0777));
+
+ for (scan = bad_links; *scan != NULL; scan++) {
+ make_gitlink_dir("alternate", *scan);
+ repo = NULL;
+ cl_git_fail(git_repository_open_ext(&repo, "alternate", 0, NULL));
+ cl_assert(repo == NULL);
+ }
+
+ git_futils_rmdir_r("invalid", NULL, GIT_RMDIR_REMOVE_FILES);
+ git_futils_rmdir_r("invalid2", NULL, GIT_RMDIR_REMOVE_FILES);
+}
+
+#ifdef GIT_WIN32
+static void unposix_path(git_str *path)
+{
+ char *src, *tgt;
+
+ src = tgt = path->ptr;
+
+ /* convert "/d/..." to "d:\..." */
+ if (src[0] == '/' && isalpha(src[1]) && src[2] == '/') {
+ *tgt++ = src[1];
+ *tgt++ = ':';
+ *tgt++ = '\\';
+ src += 3;
+ }
+
+ while (*src) {
+ *tgt++ = (*src == '/') ? '\\' : *src;
+ src++;
+ }
+
+ *tgt = '\0';
+}
+#endif
+
+void test_repo_open__win32_path(void)
+{
+#ifdef GIT_WIN32
+ git_repository *repo = cl_git_sandbox_init("empty_standard_repo"), *repo2;
+ git_str winpath = GIT_STR_INIT;
+ static const char *repo_path = "empty_standard_repo/.git/";
+ static const char *repo_wd = "empty_standard_repo/";
+
+ cl_assert(git__suffixcmp(git_repository_path(repo), repo_path) == 0);
+ cl_assert(git__suffixcmp(git_repository_workdir(repo), repo_wd) == 0);
+
+ cl_git_pass(git_str_sets(&winpath, git_repository_path(repo)));
+ unposix_path(&winpath);
+ cl_git_pass(git_repository_open(&repo2, winpath.ptr));
+ cl_assert(git__suffixcmp(git_repository_path(repo2), repo_path) == 0);
+ cl_assert(git__suffixcmp(git_repository_workdir(repo2), repo_wd) == 0);
+ git_repository_free(repo2);
+
+ cl_git_pass(git_str_sets(&winpath, git_repository_path(repo)));
+ git_str_truncate(&winpath, winpath.size - 1); /* remove trailing '/' */
+ unposix_path(&winpath);
+ cl_git_pass(git_repository_open(&repo2, winpath.ptr));
+ cl_assert(git__suffixcmp(git_repository_path(repo2), repo_path) == 0);
+ cl_assert(git__suffixcmp(git_repository_workdir(repo2), repo_wd) == 0);
+ git_repository_free(repo2);
+
+ cl_git_pass(git_str_sets(&winpath, git_repository_workdir(repo)));
+ unposix_path(&winpath);
+ cl_git_pass(git_repository_open(&repo2, winpath.ptr));
+ cl_assert(git__suffixcmp(git_repository_path(repo2), repo_path) == 0);
+ cl_assert(git__suffixcmp(git_repository_workdir(repo2), repo_wd) == 0);
+ git_repository_free(repo2);
+
+ cl_git_pass(git_str_sets(&winpath, git_repository_workdir(repo)));
+ git_str_truncate(&winpath, winpath.size - 1); /* remove trailing '/' */
+ unposix_path(&winpath);
+ cl_git_pass(git_repository_open(&repo2, winpath.ptr));
+ cl_assert(git__suffixcmp(git_repository_path(repo2), repo_path) == 0);
+ cl_assert(git__suffixcmp(git_repository_workdir(repo2), repo_wd) == 0);
+ git_repository_free(repo2);
+
+ git_str_dispose(&winpath);
+#endif
+}
+
+void test_repo_open__opening_a_non_existing_repository_returns_ENOTFOUND(void)
+{
+ git_repository *repo;
+ cl_assert_equal_i(GIT_ENOTFOUND, git_repository_open(&repo, "i-do-not/exist"));
+}
+
+void test_repo_open__no_config(void)
+{
+ git_str path = GIT_STR_INIT;
+ git_repository *repo;
+ git_config *config;
+
+ cl_fixture_sandbox("empty_standard_repo");
+ cl_git_pass(cl_rename(
+ "empty_standard_repo/.gitted", "empty_standard_repo/.git"));
+
+ /* remove local config */
+ cl_git_pass(git_futils_rmdir_r(
+ "empty_standard_repo/.git/config", NULL, GIT_RMDIR_REMOVE_FILES));
+
+ /* isolate from system level configs */
+ cl_must_pass(p_mkdir("alternate", 0777));
+ cl_git_pass(git_fs_path_prettify(&path, "alternate", NULL));
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, path.ptr));
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_SYSTEM, path.ptr));
+ cl_git_pass(git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_XDG, path.ptr));
+
+ git_str_dispose(&path);
+
+ cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
+ cl_assert(git_repository_oid_type(repo) == GIT_OID_SHA1);
+ cl_git_pass(git_repository_config(&config, repo));
+
+ cl_git_pass(git_config_set_string(config, "test.set", "42"));
+
+ git_config_free(config);
+ git_repository_free(repo);
+ cl_fixture_cleanup("empty_standard_repo");
+
+ cl_sandbox_set_search_path_defaults();
+}
+
+void test_repo_open__force_bare(void)
+{
+ /* need to have both repo dir and workdir set up correctly */
+ git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
+ git_repository *barerepo;
+
+ make_gitlink_dir("alternate", "gitdir: ../empty_standard_repo/.git");
+
+ cl_assert(!git_repository_is_bare(repo));
+
+ cl_git_pass(git_repository_open(&barerepo, "alternate"));
+ cl_assert(!git_repository_is_bare(barerepo));
+ cl_assert(git_repository_oid_type(barerepo) == GIT_OID_SHA1);
+ git_repository_free(barerepo);
+
+ cl_git_pass(git_repository_open_bare(
+ &barerepo, "empty_standard_repo/.git"));
+ cl_assert(git_repository_is_bare(barerepo));
+ cl_assert(git_repository_oid_type(barerepo) == GIT_OID_SHA1);
+ git_repository_free(barerepo);
+
+ cl_git_fail(git_repository_open_bare(&barerepo, "alternate/.git"));
+
+ cl_git_pass(git_repository_open_ext(
+ &barerepo, "alternate/.git", GIT_REPOSITORY_OPEN_BARE, NULL));
+ cl_assert(git_repository_is_bare(barerepo));
+ git_repository_free(barerepo);
+
+ cl_git_pass(p_mkdir("empty_standard_repo/subdir", 0777));
+ cl_git_mkfile("empty_standard_repo/subdir/something.txt", "something");
+
+ cl_git_fail(git_repository_open_bare(
+ &barerepo, "empty_standard_repo/subdir"));
+
+ cl_git_pass(git_repository_open_ext(
+ &barerepo, "empty_standard_repo/subdir", GIT_REPOSITORY_OPEN_BARE, NULL));
+ cl_assert(git_repository_is_bare(barerepo));
+ git_repository_free(barerepo);
+
+ cl_git_pass(p_mkdir("alternate/subdir", 0777));
+ cl_git_pass(p_mkdir("alternate/subdir/sub2", 0777));
+ cl_git_mkfile("alternate/subdir/sub2/something.txt", "something");
+
+ cl_git_fail(git_repository_open_bare(&barerepo, "alternate/subdir/sub2"));
+
+ cl_git_pass(git_repository_open_ext(
+ &barerepo, "alternate/subdir/sub2",
+ GIT_REPOSITORY_OPEN_BARE|GIT_REPOSITORY_OPEN_CROSS_FS, NULL));
+ cl_assert(git_repository_is_bare(barerepo));
+ git_repository_free(barerepo);
+}
+
+void test_repo_open__validates_dir_ownership(void)
+{
+ git_repository *repo;
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_OWNER_VALIDATION, 1));
+
+ cl_fixture_sandbox("empty_standard_repo");
+ cl_git_pass(cl_rename("empty_standard_repo/.gitted", "empty_standard_repo/.git"));
+
+ /* When the current user owns the repo config, that's acceptable */
+ git_fs_path__set_owner(GIT_FS_PATH_OWNER_CURRENT_USER);
+ cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
+ git_repository_free(repo);
+
+ /* When the system user owns the repo config, fail */
+ git_fs_path__set_owner(GIT_FS_PATH_OWNER_ADMINISTRATOR);
+ cl_git_fail_with(GIT_EOWNER, git_repository_open(&repo, "empty_standard_repo"));
+
+#ifdef GIT_WIN32
+ /* When the user is an administrator, succeed on Windows. */
+ git_fs_path__set_owner(GIT_FS_PATH_USER_IS_ADMINISTRATOR);
+ cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
+ git_repository_free(repo);
+#endif
+
+ /* When an unknown user owns the repo config, fail */
+ git_fs_path__set_owner(GIT_FS_PATH_OWNER_OTHER);
+ cl_git_fail_with(GIT_EOWNER, git_repository_open(&repo, "empty_standard_repo"));
+}
+
+void test_repo_open__validates_bare_repo_ownership(void)
+{
+ git_repository *repo;
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_OWNER_VALIDATION, 1));
+
+ cl_fixture_sandbox("testrepo.git");
+
+ /* When the current user owns the repo config, that's acceptable */
+ git_fs_path__set_owner(GIT_FS_PATH_OWNER_CURRENT_USER);
+ cl_git_pass(git_repository_open(&repo, "testrepo.git"));
+ git_repository_free(repo);
+
+ /* When the system user owns the repo config, fail */
+ git_fs_path__set_owner(GIT_FS_PATH_OWNER_ADMINISTRATOR);
+ cl_git_fail_with(GIT_EOWNER, git_repository_open(&repo, "testrepo.git"));
+
+#ifdef GIT_WIN32
+ /* When the user is an administrator, succeed on Windows. */
+ git_fs_path__set_owner(GIT_FS_PATH_USER_IS_ADMINISTRATOR);
+ cl_git_pass(git_repository_open(&repo, "testrepo.git"));
+ git_repository_free(repo);
+#endif
+
+ /* When an unknown user owns the repo config, fail */
+ git_fs_path__set_owner(GIT_FS_PATH_OWNER_OTHER);
+ cl_git_fail_with(GIT_EOWNER, git_repository_open(&repo, "testrepo.git"));
+}
+
+void test_repo_open__can_allowlist_dirs_with_problematic_ownership(void)
+{
+ git_repository *repo;
+ git_str config_path = GIT_STR_INIT,
+ config_filename = GIT_STR_INIT,
+ config_data = GIT_STR_INIT;
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_OWNER_VALIDATION, 1));
+
+ cl_fixture_sandbox("empty_standard_repo");
+ cl_git_pass(cl_rename("empty_standard_repo/.gitted", "empty_standard_repo/.git"));
+
+ git_fs_path__set_owner(GIT_FS_PATH_OWNER_OTHER);
+ cl_git_fail_with(GIT_EOWNER, git_repository_open(&repo, "empty_standard_repo"));
+
+ /* Add safe.directory options to the global configuration */
+ git_str_joinpath(&config_path, clar_sandbox_path(), "__global_config");
+ cl_must_pass(p_mkdir(config_path.ptr, 0777));
+ git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, config_path.ptr);
+
+ git_str_joinpath(&config_filename, config_path.ptr, ".gitconfig");
+
+ git_str_printf(&config_data,
+ "[foo]\n" \
+ "\tbar = Foobar\n" \
+ "\tbaz = Baz!\n" \
+ "[safe]\n" \
+ "\tdirectory = /non/existent/path\n" \
+ "\tdirectory = /\n" \
+ "\tdirectory = c:\\\\temp\n" \
+ "\tdirectory = %s/%s\n" \
+ "\tdirectory = /tmp\n" \
+ "[bar]\n" \
+ "\tfoo = barfoo\n",
+ clar_sandbox_path(), "empty_standard_repo");
+ cl_git_rewritefile(config_filename.ptr, config_data.ptr);
+
+ cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
+ git_repository_free(repo);
+
+ git_str_dispose(&config_path);
+ git_str_dispose(&config_filename);
+ git_str_dispose(&config_data);
+}
+
+void test_repo_open__can_wildcard_allowlist_with_problematic_ownership(void)
+{
+ git_repository *repo;
+ git_str config_path = GIT_STR_INIT, config_filename = GIT_STR_INIT;
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_OWNER_VALIDATION, 1));
+
+ cl_fixture_sandbox("empty_standard_repo");
+ cl_git_pass(cl_rename(
+ "empty_standard_repo/.gitted", "empty_standard_repo/.git"));
+
+ git_fs_path__set_owner(GIT_FS_PATH_OWNER_OTHER);
+ cl_git_fail_with(
+ GIT_EOWNER, git_repository_open(&repo, "empty_standard_repo"));
+
+ /* Add safe.directory options to the global configuration */
+ git_str_joinpath(&config_path, clar_sandbox_path(), "__global_config");
+ cl_must_pass(p_mkdir(config_path.ptr, 0777));
+ git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL,
+ config_path.ptr);
+
+ git_str_joinpath(&config_filename, config_path.ptr, ".gitconfig");
+
+ cl_git_rewritefile(config_filename.ptr, "[foo]\n"
+ "\tbar = Foobar\n"
+ "\tbaz = Baz!\n"
+ "[safe]\n"
+ "\tdirectory = *\n"
+ "[bar]\n"
+ "\tfoo = barfoo\n");
+
+ cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
+ git_repository_free(repo);
+
+ git_str_dispose(&config_path);
+ git_str_dispose(&config_filename);
+}
+
+void test_repo_open__can_allowlist_bare_gitdir(void)
+{
+ git_repository *repo;
+ git_str config_path = GIT_STR_INIT,
+ config_filename = GIT_STR_INIT,
+ config_data = GIT_STR_INIT;
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_OWNER_VALIDATION, 1));
+
+ cl_fixture_sandbox("testrepo.git");
+
+ git_fs_path__set_owner(GIT_FS_PATH_OWNER_OTHER);
+ cl_git_fail_with(GIT_EOWNER, git_repository_open(&repo, "testrepo.git"));
+
+ /* Add safe.directory options to the global configuration */
+ git_str_joinpath(&config_path, clar_sandbox_path(), "__global_config");
+ cl_must_pass(p_mkdir(config_path.ptr, 0777));
+ git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, config_path.ptr);
+
+ git_str_joinpath(&config_filename, config_path.ptr, ".gitconfig");
+
+ git_str_printf(&config_data,
+ "[foo]\n" \
+ "\tbar = Foobar\n" \
+ "\tbaz = Baz!\n" \
+ "[safe]\n" \
+ "\tdirectory = /non/existent/path\n" \
+ "\tdirectory = /\n" \
+ "\tdirectory = c:\\\\temp\n" \
+ "\tdirectory = %s/%s\n" \
+ "\tdirectory = /tmp\n" \
+ "[bar]\n" \
+ "\tfoo = barfoo\n",
+ clar_sandbox_path(), "testrepo.git");
+ cl_git_rewritefile(config_filename.ptr, config_data.ptr);
+
+ cl_git_pass(git_repository_open(&repo, "testrepo.git"));
+ git_repository_free(repo);
+
+ git_str_dispose(&config_path);
+ git_str_dispose(&config_filename);
+ git_str_dispose(&config_data);
+}
+
+void test_repo_open__can_wildcard_allowlist_bare_gitdir(void)
+{
+ git_repository *repo;
+ git_str config_path = GIT_STR_INIT, config_filename = GIT_STR_INIT;
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_OWNER_VALIDATION, 1));
+
+ cl_fixture_sandbox("testrepo.git");
+
+ git_fs_path__set_owner(GIT_FS_PATH_OWNER_OTHER);
+ cl_git_fail_with(
+ GIT_EOWNER, git_repository_open(&repo, "testrepo.git"));
+
+ /* Add safe.directory options to the global configuration */
+ git_str_joinpath(&config_path, clar_sandbox_path(), "__global_config");
+ cl_must_pass(p_mkdir(config_path.ptr, 0777));
+ git_libgit2_opts(
+ GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL,
+ config_path.ptr);
+
+ git_str_joinpath(&config_filename, config_path.ptr, ".gitconfig");
+
+ cl_git_rewritefile(config_filename.ptr, "[foo]\n"
+ "\tbar = Foobar\n"
+ "\tbaz = Baz!\n"
+ "[safe]\n"
+ "\tdirectory = *\n"
+ "[bar]\n"
+ "\tfoo = barfoo\n");
+
+ cl_git_pass(git_repository_open(&repo, "testrepo.git"));
+ git_repository_free(repo);
+
+ git_str_dispose(&config_path);
+ git_str_dispose(&config_filename);
+}
+
+void test_repo_open__can_reset_safe_directory_list(void)
+{
+ git_repository *repo;
+ git_str config_path = GIT_STR_INIT,
+ config_filename = GIT_STR_INIT,
+ config_data = GIT_STR_INIT;
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_OWNER_VALIDATION, 1));
+
+ cl_fixture_sandbox("empty_standard_repo");
+ cl_git_pass(cl_rename("empty_standard_repo/.gitted", "empty_standard_repo/.git"));
+
+ git_fs_path__set_owner(GIT_FS_PATH_OWNER_OTHER);
+ cl_git_fail_with(GIT_EOWNER, git_repository_open(&repo, "empty_standard_repo"));
+
+ /* Add safe.directory options to the global configuration */
+ git_str_joinpath(&config_path, clar_sandbox_path(), "__global_config");
+ cl_must_pass(p_mkdir(config_path.ptr, 0777));
+ git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL, config_path.ptr);
+
+ git_str_joinpath(&config_filename, config_path.ptr, ".gitconfig");
+
+ /* The blank resets our sandbox directory and opening fails */
+
+ git_str_printf(&config_data,
+ "[foo]\n" \
+ "\tbar = Foobar\n" \
+ "\tbaz = Baz!\n" \
+ "[safe]\n" \
+ "\tdirectory = %s/%s\n" \
+ "\tdirectory = \n" \
+ "\tdirectory = /tmp\n" \
+ "[bar]\n" \
+ "\tfoo = barfoo\n",
+ clar_sandbox_path(), "empty_standard_repo");
+ cl_git_rewritefile(config_filename.ptr, config_data.ptr);
+
+ cl_git_fail_with(GIT_EOWNER, git_repository_open(&repo, "empty_standard_repo"));
+
+ /* The blank resets tmp and allows subsequent declarations to succeed */
+
+ git_str_clear(&config_data);
+ git_str_printf(&config_data,
+ "[foo]\n" \
+ "\tbar = Foobar\n" \
+ "\tbaz = Baz!\n" \
+ "[safe]\n" \
+ "\tdirectory = /tmp\n" \
+ "\tdirectory = \n" \
+ "\tdirectory = %s/%s\n" \
+ "[bar]\n" \
+ "\tfoo = barfoo\n",
+ clar_sandbox_path(), "empty_standard_repo");
+ cl_git_rewritefile(config_filename.ptr, config_data.ptr);
+
+ cl_git_pass(git_repository_open(&repo, "empty_standard_repo"));
+ git_repository_free(repo);
+
+ git_str_dispose(&config_path);
+ git_str_dispose(&config_filename);
+ git_str_dispose(&config_data);
+}
diff --git a/tests/libgit2/repo/pathspec.c b/tests/libgit2/repo/pathspec.c
new file mode 100644
index 0000000..5b86662
--- /dev/null
+++ b/tests/libgit2/repo/pathspec.c
@@ -0,0 +1,385 @@
+#include "clar_libgit2.h"
+#include "git2/pathspec.h"
+
+static git_repository *g_repo;
+
+void test_repo_pathspec__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("status");
+}
+
+void test_repo_pathspec__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ g_repo = NULL;
+}
+
+static char *str0[] = { "*_file", "new_file", "garbage" };
+static char *str1[] = { "*_FILE", "NEW_FILE", "GARBAGE" };
+static char *str2[] = { "staged_*" };
+static char *str3[] = { "!subdir", "*_file", "new_file" };
+static char *str4[] = { "*" };
+static char *str5[] = { "S*" };
+
+void test_repo_pathspec__workdir0(void)
+{
+ git_strarray s;
+ git_pathspec *ps;
+ git_pathspec_match_list *m;
+
+ /* { "*_file", "new_file", "garbage" } */
+ s.strings = str0; s.count = ARRAY_SIZE(str0);
+ cl_git_pass(git_pathspec_new(&ps, &s));
+
+ cl_git_pass(git_pathspec_match_workdir(&m, g_repo, 0, ps));
+ cl_assert_equal_sz(10, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_sz(0, git_pathspec_match_list_failed_entrycount(m));
+ git_pathspec_match_list_free(m);
+
+ cl_git_pass(git_pathspec_match_workdir(&m, g_repo,
+ GIT_PATHSPEC_FIND_FAILURES, ps));
+ cl_assert_equal_sz(10, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_sz(1, git_pathspec_match_list_failed_entrycount(m));
+ cl_assert_equal_s("garbage", git_pathspec_match_list_failed_entry(m, 0));
+ git_pathspec_match_list_free(m);
+
+ cl_git_pass(git_pathspec_match_workdir(&m, g_repo,
+ GIT_PATHSPEC_FIND_FAILURES | GIT_PATHSPEC_FAILURES_ONLY, ps));
+ cl_assert_equal_sz(0, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_sz(1, git_pathspec_match_list_failed_entrycount(m));
+ git_pathspec_match_list_free(m);
+
+ git_pathspec_free(ps);
+}
+
+void test_repo_pathspec__workdir1(void)
+{
+ git_strarray s;
+ git_pathspec *ps;
+ git_pathspec_match_list *m;
+
+ /* { "*_FILE", "NEW_FILE", "GARBAGE" } */
+ s.strings = str1; s.count = ARRAY_SIZE(str1);
+ cl_git_pass(git_pathspec_new(&ps, &s));
+
+ cl_git_pass(git_pathspec_match_workdir(&m, g_repo,
+ GIT_PATHSPEC_IGNORE_CASE, ps));
+ cl_assert_equal_sz(10, git_pathspec_match_list_entrycount(m));
+ git_pathspec_match_list_free(m);
+
+ cl_git_pass(git_pathspec_match_workdir(&m, g_repo,
+ GIT_PATHSPEC_USE_CASE, ps));
+ cl_assert_equal_sz(0, git_pathspec_match_list_entrycount(m));
+ git_pathspec_match_list_free(m);
+
+ cl_git_fail(git_pathspec_match_workdir(&m, g_repo,
+ GIT_PATHSPEC_USE_CASE | GIT_PATHSPEC_NO_MATCH_ERROR, ps));
+
+ cl_git_pass(git_pathspec_match_workdir(&m, g_repo,
+ GIT_PATHSPEC_IGNORE_CASE | GIT_PATHSPEC_FIND_FAILURES, ps));
+ cl_assert_equal_sz(10, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_sz(1, git_pathspec_match_list_failed_entrycount(m));
+ git_pathspec_match_list_free(m);
+
+ cl_git_pass(git_pathspec_match_workdir(&m, g_repo,
+ GIT_PATHSPEC_USE_CASE | GIT_PATHSPEC_FIND_FAILURES, ps));
+ cl_assert_equal_sz(0, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_sz(3, git_pathspec_match_list_failed_entrycount(m));
+ git_pathspec_match_list_free(m);
+
+ git_pathspec_free(ps);
+}
+
+void test_repo_pathspec__workdir2(void)
+{
+ git_strarray s;
+ git_pathspec *ps;
+ git_pathspec_match_list *m;
+
+ /* { "staged_*" } */
+ s.strings = str2; s.count = ARRAY_SIZE(str2);
+ cl_git_pass(git_pathspec_new(&ps, &s));
+
+ cl_git_pass(git_pathspec_match_workdir(&m, g_repo, 0, ps));
+ cl_assert_equal_sz(5, git_pathspec_match_list_entrycount(m));
+ git_pathspec_match_list_free(m);
+
+ cl_git_pass(git_pathspec_match_workdir(&m, g_repo,
+ GIT_PATHSPEC_FIND_FAILURES, ps));
+ cl_assert_equal_sz(5, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_sz(0, git_pathspec_match_list_failed_entrycount(m));
+ git_pathspec_match_list_free(m);
+
+ cl_git_fail(git_pathspec_match_workdir(&m, g_repo,
+ GIT_PATHSPEC_NO_GLOB | GIT_PATHSPEC_NO_MATCH_ERROR, ps));
+
+ cl_git_pass(git_pathspec_match_workdir(&m, g_repo,
+ GIT_PATHSPEC_NO_GLOB | GIT_PATHSPEC_FIND_FAILURES, ps));
+ cl_assert_equal_sz(0, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_sz(1, git_pathspec_match_list_failed_entrycount(m));
+ git_pathspec_match_list_free(m);
+
+ git_pathspec_free(ps);
+}
+
+void test_repo_pathspec__workdir3(void)
+{
+ git_strarray s;
+ git_pathspec *ps;
+ git_pathspec_match_list *m;
+
+ /* { "!subdir", "*_file", "new_file" } */
+ s.strings = str3; s.count = ARRAY_SIZE(str3);
+ cl_git_pass(git_pathspec_new(&ps, &s));
+
+ cl_git_pass(git_pathspec_match_workdir(&m, g_repo, 0, ps));
+ cl_assert_equal_sz(7, git_pathspec_match_list_entrycount(m));
+ git_pathspec_match_list_free(m);
+
+ cl_git_pass(git_pathspec_match_workdir(&m, g_repo,
+ GIT_PATHSPEC_FIND_FAILURES, ps));
+ cl_assert_equal_sz(7, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_sz(0, git_pathspec_match_list_failed_entrycount(m));
+
+ cl_assert_equal_s("current_file", git_pathspec_match_list_entry(m, 0));
+ cl_assert_equal_s("modified_file", git_pathspec_match_list_entry(m, 1));
+ cl_assert_equal_s("new_file", git_pathspec_match_list_entry(m, 2));
+ cl_assert_equal_s("staged_changes_modified_file", git_pathspec_match_list_entry(m, 3));
+ cl_assert_equal_s("staged_delete_modified_file", git_pathspec_match_list_entry(m, 4));
+ cl_assert_equal_s("staged_new_file", git_pathspec_match_list_entry(m, 5));
+ cl_assert_equal_s("staged_new_file_modified_file", git_pathspec_match_list_entry(m, 6));
+ cl_assert_equal_s(NULL, git_pathspec_match_list_entry(m, 7));
+
+ git_pathspec_match_list_free(m);
+
+ git_pathspec_free(ps);
+}
+
+void test_repo_pathspec__workdir4(void)
+{
+ git_strarray s;
+ git_pathspec *ps;
+ git_pathspec_match_list *m;
+
+ /* { "*" } */
+ s.strings = str4; s.count = ARRAY_SIZE(str4);
+ cl_git_pass(git_pathspec_new(&ps, &s));
+
+ cl_git_pass(git_pathspec_match_workdir(&m, g_repo, 0, ps));
+ cl_assert_equal_sz(13, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_s("\xE8\xBF\x99", git_pathspec_match_list_entry(m, 12));
+ git_pathspec_match_list_free(m);
+
+ git_pathspec_free(ps);
+}
+
+
+void test_repo_pathspec__index0(void)
+{
+ git_index *idx;
+ git_strarray s;
+ git_pathspec *ps;
+ git_pathspec_match_list *m;
+
+ cl_git_pass(git_repository_index(&idx, g_repo));
+
+ /* { "*_file", "new_file", "garbage" } */
+ s.strings = str0; s.count = ARRAY_SIZE(str0);
+ cl_git_pass(git_pathspec_new(&ps, &s));
+
+ cl_git_pass(git_pathspec_match_index(&m, idx, 0, ps));
+ cl_assert_equal_sz(9, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_sz(0, git_pathspec_match_list_failed_entrycount(m));
+ cl_assert_equal_s("current_file", git_pathspec_match_list_entry(m, 0));
+ cl_assert_equal_s("modified_file", git_pathspec_match_list_entry(m, 1));
+ cl_assert_equal_s("staged_changes_modified_file", git_pathspec_match_list_entry(m, 2));
+ cl_assert_equal_s("staged_new_file", git_pathspec_match_list_entry(m, 3));
+ cl_assert_equal_s("staged_new_file_deleted_file", git_pathspec_match_list_entry(m, 4));
+ cl_assert_equal_s("staged_new_file_modified_file", git_pathspec_match_list_entry(m, 5));
+ cl_assert_equal_s("subdir/current_file", git_pathspec_match_list_entry(m, 6));
+ cl_assert_equal_s("subdir/deleted_file", git_pathspec_match_list_entry(m, 7));
+ cl_assert_equal_s("subdir/modified_file", git_pathspec_match_list_entry(m, 8));
+ cl_assert_equal_s(NULL, git_pathspec_match_list_entry(m, 9));
+ git_pathspec_match_list_free(m);
+
+ cl_git_pass(git_pathspec_match_index(&m, idx,
+ GIT_PATHSPEC_FIND_FAILURES, ps));
+ cl_assert_equal_sz(9, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_sz(2, git_pathspec_match_list_failed_entrycount(m));
+ cl_assert_equal_s("new_file", git_pathspec_match_list_failed_entry(m, 0));
+ cl_assert_equal_s("garbage", git_pathspec_match_list_failed_entry(m, 1));
+ cl_assert_equal_s(NULL, git_pathspec_match_list_failed_entry(m, 2));
+ git_pathspec_match_list_free(m);
+
+ git_pathspec_free(ps);
+ git_index_free(idx);
+}
+
+void test_repo_pathspec__index1(void)
+{
+ /* Currently the USE_CASE and IGNORE_CASE flags don't work on the
+ * index because the index sort order for the index iterator is
+ * set by the index itself. I think the correct fix is for the
+ * index not to embed a global sort order but to support traversal
+ * in either case sensitive or insensitive order in a stateless
+ * manner.
+ *
+ * Anyhow, as it is, there is no point in doing this test.
+ */
+#if 0
+ git_index *idx;
+ git_strarray s;
+ git_pathspec *ps;
+ git_pathspec_match_list *m;
+
+ cl_git_pass(git_repository_index(&idx, g_repo));
+
+ /* { "*_FILE", "NEW_FILE", "GARBAGE" } */
+ s.strings = str1; s.count = ARRAY_SIZE(str1);
+ cl_git_pass(git_pathspec_new(&ps, &s));
+
+ cl_git_pass(git_pathspec_match_index(&m, idx,
+ GIT_PATHSPEC_USE_CASE, ps));
+ cl_assert_equal_sz(0, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_sz(0, git_pathspec_match_list_failed_entrycount(m));
+ git_pathspec_match_list_free(m);
+
+ cl_git_pass(git_pathspec_match_index(&m, idx,
+ GIT_PATHSPEC_USE_CASE | GIT_PATHSPEC_FIND_FAILURES, ps));
+ cl_assert_equal_sz(0, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_sz(3, git_pathspec_match_list_failed_entrycount(m));
+ git_pathspec_match_list_free(m);
+
+ cl_git_pass(git_pathspec_match_index(&m, idx,
+ GIT_PATHSPEC_IGNORE_CASE | GIT_PATHSPEC_FIND_FAILURES, ps));
+ cl_assert_equal_sz(10, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_sz(2, git_pathspec_match_list_failed_entrycount(m));
+ git_pathspec_match_list_free(m);
+
+ git_pathspec_free(ps);
+ git_index_free(idx);
+#endif
+}
+
+void test_repo_pathspec__tree0(void)
+{
+ git_object *tree;
+ git_strarray s;
+ git_pathspec *ps;
+ git_pathspec_match_list *m;
+
+ /* { "*_file", "new_file", "garbage" } */
+ s.strings = str0; s.count = ARRAY_SIZE(str0);
+ cl_git_pass(git_pathspec_new(&ps, &s));
+
+ cl_git_pass(git_revparse_single(&tree, g_repo, "HEAD~2^{tree}"));
+
+ cl_git_pass(git_pathspec_match_tree(&m, (git_tree *)tree,
+ GIT_PATHSPEC_FIND_FAILURES, ps));
+ cl_assert_equal_sz(4, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_s("current_file", git_pathspec_match_list_entry(m, 0));
+ cl_assert_equal_s("modified_file", git_pathspec_match_list_entry(m, 1));
+ cl_assert_equal_s("staged_changes_modified_file", git_pathspec_match_list_entry(m, 2));
+ cl_assert_equal_s("staged_delete_modified_file", git_pathspec_match_list_entry(m, 3));
+ cl_assert_equal_s(NULL, git_pathspec_match_list_entry(m, 4));
+ cl_assert_equal_sz(2, git_pathspec_match_list_failed_entrycount(m));
+ cl_assert_equal_s("new_file", git_pathspec_match_list_failed_entry(m, 0));
+ cl_assert_equal_s("garbage", git_pathspec_match_list_failed_entry(m, 1));
+ cl_assert_equal_s(NULL, git_pathspec_match_list_failed_entry(m, 2));
+ git_pathspec_match_list_free(m);
+
+ git_object_free(tree);
+
+ cl_git_pass(git_revparse_single(&tree, g_repo, "HEAD^{tree}"));
+
+ cl_git_pass(git_pathspec_match_tree(&m, (git_tree *)tree,
+ GIT_PATHSPEC_FIND_FAILURES, ps));
+ cl_assert_equal_sz(7, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_s("current_file", git_pathspec_match_list_entry(m, 0));
+ cl_assert_equal_s("modified_file", git_pathspec_match_list_entry(m, 1));
+ cl_assert_equal_s("staged_changes_modified_file", git_pathspec_match_list_entry(m, 2));
+ cl_assert_equal_s("staged_delete_modified_file", git_pathspec_match_list_entry(m, 3));
+ cl_assert_equal_s("subdir/current_file", git_pathspec_match_list_entry(m, 4));
+ cl_assert_equal_s("subdir/deleted_file", git_pathspec_match_list_entry(m, 5));
+ cl_assert_equal_s("subdir/modified_file", git_pathspec_match_list_entry(m, 6));
+ cl_assert_equal_s(NULL, git_pathspec_match_list_entry(m, 7));
+ cl_assert_equal_sz(2, git_pathspec_match_list_failed_entrycount(m));
+ cl_assert_equal_s("new_file", git_pathspec_match_list_failed_entry(m, 0));
+ cl_assert_equal_s("garbage", git_pathspec_match_list_failed_entry(m, 1));
+ cl_assert_equal_s(NULL, git_pathspec_match_list_failed_entry(m, 2));
+ git_pathspec_match_list_free(m);
+
+ git_object_free(tree);
+
+ git_pathspec_free(ps);
+}
+
+void test_repo_pathspec__tree5(void)
+{
+ git_object *tree;
+ git_strarray s;
+ git_pathspec *ps;
+ git_pathspec_match_list *m;
+
+ /* { "S*" } */
+ s.strings = str5; s.count = ARRAY_SIZE(str5);
+ cl_git_pass(git_pathspec_new(&ps, &s));
+
+ cl_git_pass(git_revparse_single(&tree, g_repo, "HEAD~2^{tree}"));
+
+ cl_git_pass(git_pathspec_match_tree(&m, (git_tree *)tree,
+ GIT_PATHSPEC_USE_CASE | GIT_PATHSPEC_FIND_FAILURES, ps));
+ cl_assert_equal_sz(0, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_sz(1, git_pathspec_match_list_failed_entrycount(m));
+ git_pathspec_match_list_free(m);
+
+ cl_git_pass(git_pathspec_match_tree(&m, (git_tree *)tree,
+ GIT_PATHSPEC_IGNORE_CASE | GIT_PATHSPEC_FIND_FAILURES, ps));
+ cl_assert_equal_sz(5, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_s("staged_changes", git_pathspec_match_list_entry(m, 0));
+ cl_assert_equal_s("staged_delete_modified_file", git_pathspec_match_list_entry(m, 4));
+ cl_assert_equal_sz(0, git_pathspec_match_list_failed_entrycount(m));
+ git_pathspec_match_list_free(m);
+
+ git_object_free(tree);
+
+ cl_git_pass(git_revparse_single(&tree, g_repo, "HEAD^{tree}"));
+
+ cl_git_pass(git_pathspec_match_tree(&m, (git_tree *)tree,
+ GIT_PATHSPEC_IGNORE_CASE | GIT_PATHSPEC_FIND_FAILURES, ps));
+ cl_assert_equal_sz(9, git_pathspec_match_list_entrycount(m));
+ cl_assert_equal_s("staged_changes", git_pathspec_match_list_entry(m, 0));
+ cl_assert_equal_s("subdir.txt", git_pathspec_match_list_entry(m, 5));
+ cl_assert_equal_s("subdir/current_file", git_pathspec_match_list_entry(m, 6));
+ cl_assert_equal_sz(0, git_pathspec_match_list_failed_entrycount(m));
+ git_pathspec_match_list_free(m);
+
+ git_object_free(tree);
+
+ git_pathspec_free(ps);
+}
+
+void test_repo_pathspec__in_memory(void)
+{
+ static char *strings[] = { "one", "two*", "!three*", "*four" };
+ git_strarray s = { strings, ARRAY_SIZE(strings) };
+ git_pathspec *ps;
+
+ cl_git_pass(git_pathspec_new(&ps, &s));
+
+ cl_assert(git_pathspec_matches_path(ps, 0, "one"));
+ cl_assert(!git_pathspec_matches_path(ps, 0, "ONE"));
+ cl_assert(git_pathspec_matches_path(ps, GIT_PATHSPEC_IGNORE_CASE, "ONE"));
+ cl_assert(git_pathspec_matches_path(ps, 0, "two"));
+ cl_assert(git_pathspec_matches_path(ps, 0, "two.txt"));
+ cl_assert(!git_pathspec_matches_path(ps, 0, "three.txt"));
+ cl_assert(git_pathspec_matches_path(ps, 0, "anything.four"));
+ cl_assert(!git_pathspec_matches_path(ps, 0, "three.four"));
+ cl_assert(!git_pathspec_matches_path(ps, 0, "nomatch"));
+ cl_assert(!git_pathspec_matches_path(ps, GIT_PATHSPEC_NO_GLOB, "two"));
+ cl_assert(git_pathspec_matches_path(ps, GIT_PATHSPEC_NO_GLOB, "two*"));
+ cl_assert(!git_pathspec_matches_path(ps, GIT_PATHSPEC_NO_GLOB, "anyfour"));
+ cl_assert(git_pathspec_matches_path(ps, GIT_PATHSPEC_NO_GLOB, "*four"));
+
+ git_pathspec_free(ps);
+}
diff --git a/tests/libgit2/repo/repo_helpers.c b/tests/libgit2/repo/repo_helpers.c
new file mode 100644
index 0000000..1efde70
--- /dev/null
+++ b/tests/libgit2/repo/repo_helpers.c
@@ -0,0 +1,37 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+#include "repo_helpers.h"
+#include "posix.h"
+
+void make_head_unborn(git_repository* repo, const char *target)
+{
+ git_reference *head;
+
+ cl_git_pass(git_reference_symbolic_create(&head, repo, GIT_HEAD_FILE, target, 1, NULL));
+ git_reference_free(head);
+}
+
+void delete_head(git_repository* repo)
+{
+ git_str head_path = GIT_STR_INIT;
+
+ cl_git_pass(git_str_joinpath(&head_path, git_repository_path(repo), GIT_HEAD_FILE));
+ cl_git_pass(p_unlink(git_str_cstr(&head_path)));
+
+ git_str_dispose(&head_path);
+}
+
+void create_tmp_global_config(const char *dirname, const char *key, const char *val)
+{
+ git_str path = GIT_STR_INIT;
+ git_config *config;
+
+ cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH,
+ GIT_CONFIG_LEVEL_GLOBAL, dirname));
+ cl_must_pass(p_mkdir(dirname, 0777));
+ cl_git_pass(git_str_joinpath(&path, dirname, ".gitconfig"));
+ cl_git_pass(git_config_open_ondisk(&config, path.ptr));
+ cl_git_pass(git_config_set_string(config, key, val));
+ git_config_free(config);
+ git_str_dispose(&path);
+}
diff --git a/tests/libgit2/repo/repo_helpers.h b/tests/libgit2/repo/repo_helpers.h
new file mode 100644
index 0000000..a93bf36
--- /dev/null
+++ b/tests/libgit2/repo/repo_helpers.h
@@ -0,0 +1,7 @@
+#include "common.h"
+
+#define NON_EXISTING_HEAD "refs/heads/hide/and/seek"
+
+extern void make_head_unborn(git_repository* repo, const char *target);
+extern void delete_head(git_repository* repo);
+extern void create_tmp_global_config(const char *path, const char *key, const char *val);
diff --git a/tests/libgit2/repo/reservedname.c b/tests/libgit2/repo/reservedname.c
new file mode 100644
index 0000000..245d862
--- /dev/null
+++ b/tests/libgit2/repo/reservedname.c
@@ -0,0 +1,132 @@
+#include "clar_libgit2.h"
+#include "../submodule/submodule_helpers.h"
+#include "repository.h"
+
+void test_repo_reservedname__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_repo_reservedname__includes_shortname_on_win32(void)
+{
+ git_repository *repo;
+ git_str *reserved;
+ size_t reserved_len;
+
+ repo = cl_git_sandbox_init("nasty");
+ cl_assert(git_repository__reserved_names(&reserved, &reserved_len, repo, false));
+
+#ifdef GIT_WIN32
+ cl_assert_equal_i(2, reserved_len);
+ cl_assert_equal_s(".git", reserved[0].ptr);
+ cl_assert_equal_s("GIT~1", reserved[1].ptr);
+#else
+ cl_assert_equal_i(1, reserved_len);
+ cl_assert_equal_s(".git", reserved[0].ptr);
+#endif
+}
+
+void test_repo_reservedname__includes_shortname_when_requested(void)
+{
+ git_repository *repo;
+ git_str *reserved;
+ size_t reserved_len;
+
+ repo = cl_git_sandbox_init("nasty");
+ cl_assert(git_repository__reserved_names(&reserved, &reserved_len, repo, true));
+
+ cl_assert_equal_i(2, reserved_len);
+ cl_assert_equal_s(".git", reserved[0].ptr);
+ cl_assert_equal_s("GIT~1", reserved[1].ptr);
+}
+
+/* Ensures that custom shortnames are included: creates a GIT~1 so that the
+ * .git folder itself will have to be named GIT~2
+ */
+void test_repo_reservedname__custom_shortname_recognized(void)
+{
+#ifdef GIT_WIN32
+ git_repository *repo;
+ git_str *reserved;
+ size_t reserved_len;
+
+ if (!cl_sandbox_supports_8dot3())
+ clar__skip();
+
+ repo = cl_git_sandbox_init("nasty");
+
+ cl_must_pass(p_rename("nasty/.git", "nasty/_temp"));
+ cl_git_write2file("nasty/git~1", "", 0, O_RDWR|O_CREAT, 0666);
+ cl_must_pass(p_rename("nasty/_temp", "nasty/.git"));
+
+ cl_assert(git_repository__reserved_names(&reserved, &reserved_len, repo, true));
+
+ cl_assert_equal_i(3, reserved_len);
+ cl_assert_equal_s(".git", reserved[0].ptr);
+ cl_assert_equal_s("GIT~1", reserved[1].ptr);
+ cl_assert_equal_s("GIT~2", reserved[2].ptr);
+#endif
+}
+
+/* When looking at the short name for a submodule, we need to prevent
+ * people from overwriting the `.git` file in the submodule working
+ * directory itself. We don't want to look at the actual repository
+ * path, since it will be in the super's repository above us, and
+ * typically named with the name of our subrepository. Consequently,
+ * preventing access to the short name of the actual repository path
+ * would prevent us from creating files with the same name as the
+ * subrepo. (Eg, a submodule named "libgit2" could not contain a file
+ * named "libgit2", which would be unfortunate.)
+ */
+void test_repo_reservedname__submodule_pointer(void)
+{
+#ifdef GIT_WIN32
+ git_repository *super_repo, *sub_repo;
+ git_submodule *sub;
+ git_str *sub_reserved;
+ size_t sub_reserved_len;
+
+ if (!cl_sandbox_supports_8dot3())
+ clar__skip();
+
+ super_repo = setup_fixture_submod2();
+
+ assert_submodule_exists(super_repo, "sm_unchanged");
+
+ cl_git_pass(git_submodule_lookup(&sub, super_repo, "sm_unchanged"));
+ cl_git_pass(git_submodule_open(&sub_repo, sub));
+
+ cl_assert(git_repository__reserved_names(&sub_reserved, &sub_reserved_len, sub_repo, true));
+
+ cl_assert_equal_i(2, sub_reserved_len);
+ cl_assert_equal_s(".git", sub_reserved[0].ptr);
+ cl_assert_equal_s("GIT~1", sub_reserved[1].ptr);
+
+ git_submodule_free(sub);
+ git_repository_free(sub_repo);
+#endif
+}
+
+/* Like the `submodule_pointer` test (above), this ensures that we do not
+ * follow the gitlink to the submodule's repository location and treat that
+ * as a reserved name. This tests at an initial submodule update, where the
+ * submodule repo is being created.
+ */
+void test_repo_reservedname__submodule_pointer_during_create(void)
+{
+ git_repository *repo;
+ git_submodule *sm;
+ git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
+ git_str url = GIT_STR_INIT;
+
+ repo = setup_fixture_super();
+
+ cl_git_pass(git_str_joinpath(&url, clar_sandbox_path(), "sub.git"));
+ cl_repo_set_string(repo, "submodule.sub.url", url.ptr);
+
+ cl_git_pass(git_submodule_lookup(&sm, repo, "sub"));
+ cl_git_pass(git_submodule_update(sm, 1, &update_options));
+
+ git_submodule_free(sm);
+ git_str_dispose(&url);
+}
diff --git a/tests/libgit2/repo/setters.c b/tests/libgit2/repo/setters.c
new file mode 100644
index 0000000..5c91ed3
--- /dev/null
+++ b/tests/libgit2/repo/setters.c
@@ -0,0 +1,110 @@
+#include "clar_libgit2.h"
+#include "git2/sys/repository.h"
+
+#include "index.h"
+#include "odb.h"
+#include "posix.h"
+#include "util.h"
+#include "path.h"
+#include "futils.h"
+
+static git_repository *repo;
+
+void test_repo_setters__initialize(void)
+{
+ cl_fixture_sandbox("testrepo.git");
+ cl_git_pass(git_repository_open(&repo, "testrepo.git"));
+ cl_must_pass(p_mkdir("new_workdir", 0777));
+}
+
+void test_repo_setters__cleanup(void)
+{
+ git_repository_free(repo);
+ repo = NULL;
+
+ cl_fixture_cleanup("testrepo.git");
+ cl_fixture_cleanup("new_workdir");
+}
+
+void test_repo_setters__setting_a_workdir_turns_a_bare_repository_into_a_standard_one(void)
+{
+ cl_assert(git_repository_is_bare(repo) == 1);
+
+ cl_assert(git_repository_workdir(repo) == NULL);
+ cl_git_pass(git_repository_set_workdir(repo, "./new_workdir", false));
+
+ cl_assert(git_repository_workdir(repo) != NULL);
+ cl_assert(git_repository_is_bare(repo) == 0);
+}
+
+void test_repo_setters__setting_a_workdir_prettifies_its_path(void)
+{
+ cl_git_pass(git_repository_set_workdir(repo, "./new_workdir", false));
+
+ cl_assert(git__suffixcmp(git_repository_workdir(repo), "new_workdir/") == 0);
+}
+
+void test_repo_setters__setting_a_workdir_creates_a_gitlink(void)
+{
+ git_config *cfg;
+ git_buf buf = GIT_BUF_INIT;
+ git_str content = GIT_STR_INIT;
+
+ cl_git_pass(git_repository_set_workdir(repo, "./new_workdir", true));
+
+ cl_assert(git_fs_path_isfile("./new_workdir/.git"));
+
+ cl_git_pass(git_futils_readbuffer(&content, "./new_workdir/.git"));
+ cl_assert(git__prefixcmp(git_str_cstr(&content), "gitdir: ") == 0);
+ cl_assert(git__suffixcmp(git_str_cstr(&content), "testrepo.git/") == 0);
+ git_str_dispose(&content);
+
+ cl_git_pass(git_repository_config(&cfg, repo));
+ cl_git_pass(git_config_get_string_buf(&buf, cfg, "core.worktree"));
+ cl_assert(git__suffixcmp(buf.ptr, "new_workdir/") == 0);
+
+ git_buf_dispose(&buf);
+ git_config_free(cfg);
+}
+
+void test_repo_setters__setting_a_new_index_on_a_repo_which_has_already_loaded_one_properly_honors_the_refcount(void)
+{
+ git_index *new_index;
+
+ cl_git_pass(git_index__open(&new_index, "./my-index", GIT_OID_SHA1));
+ cl_assert(((git_refcount *)new_index)->refcount.val == 1);
+
+ git_repository_set_index(repo, new_index);
+ cl_assert(((git_refcount *)new_index)->refcount.val == 2);
+
+ git_repository_free(repo);
+ cl_assert(((git_refcount *)new_index)->refcount.val == 1);
+
+ git_index_free(new_index);
+
+ /*
+ * Ensure the cleanup method won't try to free the repo as it's already been taken care of
+ */
+ repo = NULL;
+}
+
+void test_repo_setters__setting_a_new_odb_on_a_repo_which_already_loaded_one_properly_honors_the_refcount(void)
+{
+ git_odb *new_odb;
+
+ cl_git_pass(git_odb__open(&new_odb, "./testrepo.git/objects", NULL));
+ cl_assert(((git_refcount *)new_odb)->refcount.val == 1);
+
+ git_repository_set_odb(repo, new_odb);
+ cl_assert(((git_refcount *)new_odb)->refcount.val == 2);
+
+ git_repository_free(repo);
+ cl_assert(((git_refcount *)new_odb)->refcount.val == 1);
+
+ git_odb_free(new_odb);
+
+ /*
+ * Ensure the cleanup method won't try to free the repo as it's already been taken care of
+ */
+ repo = NULL;
+}
diff --git a/tests/libgit2/repo/shallow.c b/tests/libgit2/repo/shallow.c
new file mode 100644
index 0000000..adb7a9e
--- /dev/null
+++ b/tests/libgit2/repo/shallow.c
@@ -0,0 +1,39 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+
+static git_repository *g_repo;
+
+void test_repo_shallow__initialize(void)
+{
+}
+
+void test_repo_shallow__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_repo_shallow__no_shallow_file(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo.git");
+ cl_assert_equal_i(0, git_repository_is_shallow(g_repo));
+}
+
+void test_repo_shallow__empty_shallow_file(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo.git");
+ cl_git_mkfile("testrepo.git/shallow", "");
+ cl_assert_equal_i(0, git_repository_is_shallow(g_repo));
+}
+
+void test_repo_shallow__shallow_repo(void)
+{
+ g_repo = cl_git_sandbox_init("shallow.git");
+ cl_assert_equal_i(1, git_repository_is_shallow(g_repo));
+}
+
+void test_repo_shallow__clears_errors(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo.git");
+ cl_assert_equal_i(0, git_repository_is_shallow(g_repo));
+ cl_assert_equal_p(NULL, git_error_last());
+}
diff --git a/tests/libgit2/repo/state.c b/tests/libgit2/repo/state.c
new file mode 100644
index 0000000..92b272d
--- /dev/null
+++ b/tests/libgit2/repo/state.c
@@ -0,0 +1,131 @@
+#include "clar_libgit2.h"
+#include "refs.h"
+#include "posix.h"
+#include "futils.h"
+
+static git_repository *_repo;
+static git_str _path;
+
+void test_repo_state__initialize(void)
+{
+ _repo = cl_git_sandbox_init("testrepo.git");
+}
+
+void test_repo_state__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ git_str_dispose(&_path);
+}
+
+static void setup_simple_state(const char *filename)
+{
+ cl_git_pass(git_str_joinpath(&_path, git_repository_path(_repo), filename));
+ git_futils_mkpath2file(git_str_cstr(&_path), 0777);
+ cl_git_mkfile(git_str_cstr(&_path), "dummy");
+}
+
+static void assert_repo_state(git_repository_state_t state)
+{
+ cl_assert_equal_i(state, git_repository_state(_repo));
+}
+
+void test_repo_state__none_with_HEAD_attached(void)
+{
+ assert_repo_state(GIT_REPOSITORY_STATE_NONE);
+}
+
+void test_repo_state__none_with_HEAD_detached(void)
+{
+ cl_git_pass(git_repository_detach_head(_repo));
+ assert_repo_state(GIT_REPOSITORY_STATE_NONE);
+}
+
+void test_repo_state__merge(void)
+{
+ setup_simple_state(GIT_MERGE_HEAD_FILE);
+ assert_repo_state(GIT_REPOSITORY_STATE_MERGE);
+ cl_git_pass(git_repository_state_cleanup(_repo));
+ assert_repo_state(GIT_REPOSITORY_STATE_NONE);
+}
+
+void test_repo_state__revert(void)
+{
+ setup_simple_state(GIT_REVERT_HEAD_FILE);
+ assert_repo_state(GIT_REPOSITORY_STATE_REVERT);
+ cl_git_pass(git_repository_state_cleanup(_repo));
+ assert_repo_state(GIT_REPOSITORY_STATE_NONE);
+}
+
+void test_repo_state__revert_sequence(void)
+{
+ setup_simple_state(GIT_REVERT_HEAD_FILE);
+ setup_simple_state(GIT_SEQUENCER_TODO_FILE);
+ assert_repo_state(GIT_REPOSITORY_STATE_REVERT_SEQUENCE);
+ cl_git_pass(git_repository_state_cleanup(_repo));
+ assert_repo_state(GIT_REPOSITORY_STATE_NONE);
+}
+
+void test_repo_state__cherry_pick(void)
+{
+ setup_simple_state(GIT_CHERRYPICK_HEAD_FILE);
+ assert_repo_state(GIT_REPOSITORY_STATE_CHERRYPICK);
+ cl_git_pass(git_repository_state_cleanup(_repo));
+ assert_repo_state(GIT_REPOSITORY_STATE_NONE);
+}
+
+void test_repo_state__cherrypick_sequence(void)
+{
+ setup_simple_state(GIT_CHERRYPICK_HEAD_FILE);
+ setup_simple_state(GIT_SEQUENCER_TODO_FILE);
+ assert_repo_state(GIT_REPOSITORY_STATE_CHERRYPICK_SEQUENCE);
+ cl_git_pass(git_repository_state_cleanup(_repo));
+ assert_repo_state(GIT_REPOSITORY_STATE_NONE);
+}
+
+void test_repo_state__bisect(void)
+{
+ setup_simple_state(GIT_BISECT_LOG_FILE);
+ assert_repo_state(GIT_REPOSITORY_STATE_BISECT);
+ cl_git_pass(git_repository_state_cleanup(_repo));
+ assert_repo_state(GIT_REPOSITORY_STATE_NONE);
+}
+
+void test_repo_state__rebase_interactive(void)
+{
+ setup_simple_state(GIT_REBASE_MERGE_INTERACTIVE_FILE);
+ assert_repo_state(GIT_REPOSITORY_STATE_REBASE_INTERACTIVE);
+ cl_git_pass(git_repository_state_cleanup(_repo));
+ assert_repo_state(GIT_REPOSITORY_STATE_NONE);
+}
+
+void test_repo_state__rebase_merge(void)
+{
+ setup_simple_state(GIT_REBASE_MERGE_DIR "whatever");
+ assert_repo_state(GIT_REPOSITORY_STATE_REBASE_MERGE);
+ cl_git_pass(git_repository_state_cleanup(_repo));
+ assert_repo_state(GIT_REPOSITORY_STATE_NONE);
+}
+
+void test_repo_state__rebase(void)
+{
+ setup_simple_state(GIT_REBASE_APPLY_REBASING_FILE);
+ assert_repo_state(GIT_REPOSITORY_STATE_REBASE);
+ cl_git_pass(git_repository_state_cleanup(_repo));
+ assert_repo_state(GIT_REPOSITORY_STATE_NONE);
+}
+
+void test_repo_state__apply_mailbox(void)
+{
+ setup_simple_state(GIT_REBASE_APPLY_APPLYING_FILE);
+ assert_repo_state(GIT_REPOSITORY_STATE_APPLY_MAILBOX);
+ cl_git_pass(git_repository_state_cleanup(_repo));
+ assert_repo_state(GIT_REPOSITORY_STATE_NONE);
+}
+
+void test_repo_state__apply_mailbox_or_rebase(void)
+{
+ setup_simple_state(GIT_REBASE_APPLY_DIR "whatever");
+ assert_repo_state(GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE);
+ cl_git_pass(git_repository_state_cleanup(_repo));
+ assert_repo_state(GIT_REPOSITORY_STATE_NONE);
+}
diff --git a/tests/libgit2/repo/template.c b/tests/libgit2/repo/template.c
new file mode 100644
index 0000000..e8fe266
--- /dev/null
+++ b/tests/libgit2/repo/template.c
@@ -0,0 +1,305 @@
+#include "clar_libgit2.h"
+
+#include "futils.h"
+#include "repo/repo_helpers.h"
+
+#define CLEAR_FOR_CORE_FILEMODE(M) ((M) &= ~0177)
+
+static git_repository *_repo = NULL;
+static mode_t g_umask = 0;
+static git_str _global_path = GIT_STR_INIT;
+
+static const char *fixture_repo;
+static const char *fixture_templates;
+
+void test_repo_template__initialize(void)
+{
+ _repo = NULL;
+
+ /* load umask if not already loaded */
+ if (!g_umask) {
+ g_umask = p_umask(022);
+ (void)p_umask(g_umask);
+ }
+}
+
+void test_repo_template__cleanup(void)
+{
+ git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL,
+ _global_path.ptr);
+ git_str_dispose(&_global_path);
+
+ cl_fixture_cleanup("tmp_global_path");
+
+ if (fixture_repo) {
+ cl_fixture_cleanup(fixture_repo);
+ fixture_repo = NULL;
+ }
+
+ if (fixture_templates) {
+ cl_fixture_cleanup(fixture_templates);
+ fixture_templates = NULL;
+ }
+
+ git_repository_free(_repo);
+ _repo = NULL;
+}
+
+static void assert_hooks_match(
+ const char *template_dir,
+ const char *repo_dir,
+ const char *hook_path,
+ bool core_filemode)
+{
+ git_str expected = GIT_STR_INIT;
+ git_str actual = GIT_STR_INIT;
+ struct stat expected_st, st;
+
+ cl_git_pass(git_str_joinpath(&expected, template_dir, hook_path));
+ cl_git_pass(git_fs_path_lstat(expected.ptr, &expected_st));
+
+ cl_git_pass(git_str_joinpath(&actual, repo_dir, hook_path));
+ cl_git_pass(git_fs_path_lstat(actual.ptr, &st));
+
+ cl_assert(expected_st.st_size == st.st_size);
+
+ if (GIT_MODE_TYPE(expected_st.st_mode) != GIT_FILEMODE_LINK) {
+ mode_t expected_mode =
+ GIT_MODE_TYPE(expected_st.st_mode) |
+ (GIT_PERMS_FOR_WRITE(expected_st.st_mode) & ~g_umask);
+
+ if (!core_filemode) {
+ CLEAR_FOR_CORE_FILEMODE(expected_mode);
+ CLEAR_FOR_CORE_FILEMODE(st.st_mode);
+ }
+
+ cl_assert_equal_i_fmt(expected_mode, st.st_mode, "%07o");
+ }
+
+ git_str_dispose(&expected);
+ git_str_dispose(&actual);
+}
+
+static void assert_mode_seems_okay(
+ const char *base, const char *path,
+ git_filemode_t expect_mode, bool expect_setgid, bool core_filemode)
+{
+ git_str full = GIT_STR_INIT;
+ struct stat st;
+
+ cl_git_pass(git_str_joinpath(&full, base, path));
+ cl_git_pass(git_fs_path_lstat(full.ptr, &st));
+ git_str_dispose(&full);
+
+ if (!core_filemode) {
+ CLEAR_FOR_CORE_FILEMODE(expect_mode);
+ CLEAR_FOR_CORE_FILEMODE(st.st_mode);
+ expect_setgid = false;
+ }
+
+ if (S_ISGID != 0)
+ cl_assert_equal_b(expect_setgid, (st.st_mode & S_ISGID) != 0);
+
+ cl_assert_equal_b(
+ GIT_PERMS_IS_EXEC(expect_mode), GIT_PERMS_IS_EXEC(st.st_mode));
+
+ cl_assert_equal_i_fmt(
+ GIT_MODE_TYPE(expect_mode), GIT_MODE_TYPE(st.st_mode), "%07o");
+}
+
+static void setup_repo(const char *name, git_repository_init_options *opts)
+{
+ cl_git_pass(git_repository_init_ext(&_repo, name, opts));
+ fixture_repo = name;
+}
+
+static void setup_templates(const char *name, bool setup_globally)
+{
+ git_str path = GIT_STR_INIT;
+
+ cl_fixture_sandbox("template");
+ if (strcmp(name, "template"))
+ cl_must_pass(p_rename("template", name));
+
+ fixture_templates = name;
+
+ /*
+ * Create a symlink from link.sample to update.sample if the filesystem
+ * supports it.
+ */
+ cl_git_pass(git_str_join3(&path, '/', name, "hooks", "link.sample"));
+#ifdef GIT_WIN32
+ cl_git_mkfile(path.ptr, "#!/bin/sh\necho hello, world\n");
+#else
+ cl_must_pass(p_symlink("update.sample", path.ptr));
+#endif
+
+ git_str_clear(&path);
+
+ /* Create a file starting with a dot */
+ cl_git_pass(git_str_join3(&path, '/', name, "hooks", ".dotfile"));
+ cl_git_mkfile(path.ptr, "something\n");
+
+ git_str_clear(&path);
+
+ if (setup_globally) {
+ cl_git_pass(git_str_joinpath(&path, clar_sandbox_path(), name));
+ create_tmp_global_config("tmp_global_path", "init.templatedir", path.ptr);
+ }
+
+ git_str_dispose(&path);
+}
+
+static void validate_templates(git_repository *repo, const char *template_path)
+{
+ git_str path = GIT_STR_INIT, expected = GIT_STR_INIT, actual = GIT_STR_INIT;
+ int filemode;
+
+ cl_git_pass(git_str_joinpath(&path, template_path, "description"));
+ cl_git_pass(git_futils_readbuffer(&expected, path.ptr));
+
+ git_str_clear(&path);
+
+ cl_git_pass(git_str_joinpath(&path, git_repository_path(repo), "description"));
+ cl_git_pass(git_futils_readbuffer(&actual, path.ptr));
+
+ cl_assert_equal_s(expected.ptr, actual.ptr);
+
+ filemode = cl_repo_get_bool(repo, "core.filemode");
+
+ assert_hooks_match(
+ template_path, git_repository_path(repo),
+ "hooks/update.sample", filemode);
+ assert_hooks_match(
+ template_path, git_repository_path(repo),
+ "hooks/link.sample", filemode);
+ assert_hooks_match(
+ template_path, git_repository_path(repo),
+ "hooks/.dotfile", filemode);
+
+ git_str_dispose(&expected);
+ git_str_dispose(&actual);
+ git_str_dispose(&path);
+}
+
+void test_repo_template__external_templates_specified_in_options(void)
+{
+ git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+
+ opts.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_BARE |
+ GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;
+ opts.template_path = "template";
+
+ setup_templates("template", false);
+ setup_repo("templated.git", &opts);
+
+ validate_templates(_repo, "template");
+}
+
+void test_repo_template__external_templates_specified_in_config(void)
+{
+ git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+
+ opts.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_BARE |
+ GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;
+
+ setup_templates("template", true);
+ setup_repo("templated.git", &opts);
+
+ validate_templates(_repo, "template");
+}
+
+void test_repo_template__external_templates_with_leading_dot(void)
+{
+ git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+
+ opts.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_BARE |
+ GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;
+
+ setup_templates(".template_with_leading_dot", true);
+ setup_repo("templated.git", &opts);
+
+ validate_templates(_repo, ".template_with_leading_dot");
+}
+
+void test_repo_template__extended_with_template_and_shared_mode(void)
+{
+ git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+ const char *repo_path;
+ int filemode;
+
+ opts.flags = GIT_REPOSITORY_INIT_MKPATH |
+ GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;
+ opts.template_path = "template";
+ opts.mode = GIT_REPOSITORY_INIT_SHARED_GROUP;
+
+ setup_templates("template", false);
+ setup_repo("init_shared_from_tpl", &opts);
+
+ filemode = cl_repo_get_bool(_repo, "core.filemode");
+
+ repo_path = git_repository_path(_repo);
+ assert_mode_seems_okay(repo_path, "hooks",
+ GIT_FILEMODE_TREE | GIT_REPOSITORY_INIT_SHARED_GROUP, true, filemode);
+ assert_mode_seems_okay(repo_path, "info",
+ GIT_FILEMODE_TREE | GIT_REPOSITORY_INIT_SHARED_GROUP, true, filemode);
+ assert_mode_seems_okay(repo_path, "description",
+ GIT_FILEMODE_BLOB, false, filemode);
+
+ validate_templates(_repo, "template");
+}
+
+void test_repo_template__templated_head_is_used(void)
+{
+ git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+ git_str head = GIT_STR_INIT;
+
+ opts.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;
+
+ setup_templates("template", true);
+ cl_git_mkfile("template/HEAD", "foobar\n");
+ setup_repo("repo", &opts);
+
+ cl_git_pass(git_futils_readbuffer(&head, "repo/.git/HEAD"));
+ cl_assert_equal_s("foobar\n", head.ptr);
+
+ git_str_dispose(&head);
+}
+
+void test_repo_template__initial_head_option_overrides_template_head(void)
+{
+ git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+ git_str head = GIT_STR_INIT;
+
+ opts.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;
+ opts.initial_head = "manual";
+
+ setup_templates("template", true);
+ cl_git_mkfile("template/HEAD", "foobar\n");
+ setup_repo("repo", &opts);
+
+ cl_git_pass(git_futils_readbuffer(&head, "repo/.git/HEAD"));
+ cl_assert_equal_s("ref: refs/heads/manual\n", head.ptr);
+
+ git_str_dispose(&head);
+}
+
+void test_repo_template__empty_template_path(void)
+{
+ git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+
+ opts.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;
+ opts.template_path = "";
+
+ setup_repo("foo", &opts);
+}
+
+void test_repo_template__nonexistent_template_path(void)
+{
+ git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
+
+ opts.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;
+ opts.template_path = "/tmp/path/that/does/not/exist/for/libgit2/test";
+
+ setup_repo("bar", &opts);
+}
diff --git a/tests/libgit2/reset/default.c b/tests/libgit2/reset/default.c
new file mode 100644
index 0000000..c76f148
--- /dev/null
+++ b/tests/libgit2/reset/default.c
@@ -0,0 +1,212 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "reset_helpers.h"
+#include "path.h"
+
+static git_repository *_repo;
+static git_object *_target;
+static git_strarray _pathspecs;
+static git_index *_index;
+
+static void initialize(const char *repo_name)
+{
+ _repo = cl_git_sandbox_init(repo_name);
+ cl_git_pass(git_repository_index(&_index, _repo));
+
+ _target = NULL;
+
+ _pathspecs.strings = NULL;
+ _pathspecs.count = 0;
+}
+
+void test_reset_default__initialize(void)
+{
+}
+
+void test_reset_default__cleanup(void)
+{
+ git_object_free(_target);
+ _target = NULL;
+
+ git_index_free(_index);
+ _index = NULL;
+
+ cl_git_sandbox_cleanup();
+}
+
+static void assert_content_in_index(
+ git_strarray *pathspecs,
+ bool should_exist,
+ git_strarray *expected_shas)
+{
+ size_t i, pos;
+ int error;
+
+ for (i = 0; i < pathspecs->count; i++) {
+ error = git_index_find(&pos, _index, pathspecs->strings[i]);
+
+ if (should_exist) {
+ const git_index_entry *entry;
+
+ cl_assert(error != GIT_ENOTFOUND);
+
+ entry = git_index_get_byindex(_index, pos);
+ cl_assert(entry != NULL);
+
+ if (!expected_shas)
+ continue;
+
+ cl_git_pass(git_oid_streq(&entry->id, expected_shas->strings[i]));
+ } else
+ cl_assert_equal_i(should_exist, error != GIT_ENOTFOUND);
+ }
+}
+
+void test_reset_default__resetting_filepaths_against_a_null_target_removes_them_from_the_index(void)
+{
+ char *paths[] = { "staged_changes", "staged_new_file" };
+
+ initialize("status");
+
+ _pathspecs.strings = paths;
+ _pathspecs.count = 2;
+
+ assert_content_in_index(&_pathspecs, true, NULL);
+
+ cl_git_pass(git_reset_default(_repo, NULL, &_pathspecs));
+
+ assert_content_in_index(&_pathspecs, false, NULL);
+}
+
+/*
+ * $ git ls-files --cached -s --abbrev=7 -- "staged*"
+ * 100644 55d316c 0 staged_changes
+ * 100644 a6be623 0 staged_changes_file_deleted
+ * ...
+ *
+ * $ git reset 0017bd4 -- staged_changes staged_changes_file_deleted
+ * Unstaged changes after reset:
+ * ...
+ *
+ * $ git ls-files --cached -s --abbrev=7 -- "staged*"
+ * 100644 32504b7 0 staged_changes
+ * 100644 061d42a 0 staged_changes_file_deleted
+ * ...
+ */
+void test_reset_default__resetting_filepaths_replaces_their_corresponding_index_entries(void)
+{
+ git_strarray before, after;
+
+ char *paths[] = { "staged_changes", "staged_changes_file_deleted" };
+ char *before_shas[] = { "55d316c9ba708999f1918e9677d01dfcae69c6b9",
+ "a6be623522ce87a1d862128ac42672604f7b468b" };
+ char *after_shas[] = { "32504b727382542f9f089e24fddac5e78533e96c",
+ "061d42a44cacde5726057b67558821d95db96f19" };
+
+ initialize("status");
+
+ _pathspecs.strings = paths;
+ _pathspecs.count = 2;
+ before.strings = before_shas;
+ before.count = 2;
+ after.strings = after_shas;
+ after.count = 2;
+
+ cl_git_pass(git_revparse_single(&_target, _repo, "0017bd4"));
+ assert_content_in_index(&_pathspecs, true, &before);
+
+ cl_git_pass(git_reset_default(_repo, _target, &_pathspecs));
+
+ assert_content_in_index(&_pathspecs, true, &after);
+}
+
+/*
+ * $ git ls-files --cached -s --abbrev=7 -- conflicts-one.txt
+ * 100644 1f85ca5 1 conflicts-one.txt
+ * 100644 6aea5f2 2 conflicts-one.txt
+ * 100644 516bd85 3 conflicts-one.txt
+ *
+ * $ git reset 9a05ccb -- conflicts-one.txt
+ * Unstaged changes after reset:
+ * ...
+ *
+ * $ git ls-files --cached -s --abbrev=7 -- conflicts-one.txt
+ * 100644 1f85ca5 0 conflicts-one.txt
+ *
+ */
+void test_reset_default__resetting_filepaths_clears_previous_conflicts(void)
+{
+ const git_index_entry *conflict_entry[3];
+ git_strarray after;
+
+ char *paths[] = { "conflicts-one.txt" };
+ char *after_shas[] = { "1f85ca51b8e0aac893a621b61a9c2661d6aa6d81" };
+
+ initialize("mergedrepo");
+
+ _pathspecs.strings = paths;
+ _pathspecs.count = 1;
+ after.strings = after_shas;
+ after.count = 1;
+
+ cl_git_pass(git_index_conflict_get(&conflict_entry[0], &conflict_entry[1],
+ &conflict_entry[2], _index, "conflicts-one.txt"));
+
+ cl_git_pass(git_revparse_single(&_target, _repo, "9a05ccb"));
+ cl_git_pass(git_reset_default(_repo, _target, &_pathspecs));
+
+ assert_content_in_index(&_pathspecs, true, &after);
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_index_conflict_get(&conflict_entry[0],
+ &conflict_entry[1], &conflict_entry[2], _index, "conflicts-one.txt"));
+}
+
+/*
+$ git reset HEAD -- "I_am_not_there.txt" "me_neither.txt"
+Unstaged changes after reset:
+...
+*/
+void test_reset_default__resetting_unknown_filepaths_does_not_fail(void)
+{
+ char *paths[] = { "I_am_not_there.txt", "me_neither.txt" };
+
+ initialize("status");
+
+ _pathspecs.strings = paths;
+ _pathspecs.count = 2;
+
+ assert_content_in_index(&_pathspecs, false, NULL);
+
+ cl_git_pass(git_revparse_single(&_target, _repo, "HEAD"));
+ cl_git_pass(git_reset_default(_repo, _target, &_pathspecs));
+
+ assert_content_in_index(&_pathspecs, false, NULL);
+}
+
+void test_reset_default__staged_rename_reset_delete(void)
+{
+ git_index_entry entry;
+ const git_index_entry *existing;
+ char *paths[] = { "new.txt" };
+
+ initialize("testrepo2");
+
+ existing = git_index_get_bypath(_index, "new.txt", 0);
+ cl_assert(existing);
+ memcpy(&entry, existing, sizeof(entry));
+
+ cl_git_pass(git_index_remove_bypath(_index, "new.txt"));
+
+ entry.path = "renamed.txt";
+ cl_git_pass(git_index_add(_index, &entry));
+
+ _pathspecs.strings = paths;
+ _pathspecs.count = 1;
+
+ assert_content_in_index(&_pathspecs, false, NULL);
+
+ cl_git_pass(git_revparse_single(&_target, _repo, "HEAD"));
+ cl_git_pass(git_reset_default(_repo, _target, &_pathspecs));
+
+ assert_content_in_index(&_pathspecs, true, NULL);
+}
diff --git a/tests/libgit2/reset/hard.c b/tests/libgit2/reset/hard.c
new file mode 100644
index 0000000..06a8a04
--- /dev/null
+++ b/tests/libgit2/reset/hard.c
@@ -0,0 +1,294 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "reset_helpers.h"
+#include "path.h"
+#include "futils.h"
+#include "index.h"
+
+static git_repository *repo;
+static git_object *target;
+
+void test_reset_hard__initialize(void)
+{
+ repo = cl_git_sandbox_init("status");
+ target = NULL;
+}
+
+void test_reset_hard__cleanup(void)
+{
+ if (target != NULL) {
+ git_object_free(target);
+ target = NULL;
+ }
+
+ cl_git_sandbox_cleanup();
+}
+
+static int strequal_ignore_eol(const char *exp, const char *str)
+{
+ while (*exp && *str) {
+ if (*exp != *str) {
+ while (*exp == '\r' || *exp == '\n') ++exp;
+ while (*str == '\r' || *str == '\n') ++str;
+ if (*exp != *str)
+ return false;
+ } else {
+ exp++; str++;
+ }
+ }
+ return (!*exp && !*str);
+}
+
+void test_reset_hard__resetting_reverts_modified_files(void)
+{
+ git_str path = GIT_STR_INIT, content = GIT_STR_INIT;
+ int i;
+ static const char *files[4] = {
+ "current_file",
+ "modified_file",
+ "staged_new_file",
+ "staged_changes_modified_file" };
+ static const char *before[4] = {
+ "current_file\n",
+ "modified_file\nmodified_file\n",
+ "staged_new_file\n",
+ "staged_changes_modified_file\nstaged_changes_modified_file\nstaged_changes_modified_file\n"
+ };
+ static const char *after[4] = {
+ "current_file\n",
+ "modified_file\n",
+ NULL,
+ "staged_changes_modified_file\n"
+ };
+ const char *wd = git_repository_workdir(repo);
+
+ cl_assert(wd);
+
+ for (i = 0; i < 4; ++i) {
+ cl_git_pass(git_str_joinpath(&path, wd, files[i]));
+ cl_git_pass(git_futils_readbuffer(&content, path.ptr));
+ cl_assert_equal_s(before[i], content.ptr);
+ }
+
+ cl_git_pass(git_revparse_single(&target, repo, "26a125e"));
+
+ cl_git_pass(git_reset(repo, target, GIT_RESET_HARD, NULL));
+
+ for (i = 0; i < 4; ++i) {
+ cl_git_pass(git_str_joinpath(&path, wd, files[i]));
+ if (after[i]) {
+ cl_git_pass(git_futils_readbuffer(&content, path.ptr));
+ cl_assert(strequal_ignore_eol(after[i], content.ptr));
+ } else {
+ cl_assert(!git_fs_path_exists(path.ptr));
+ }
+ }
+
+ git_str_dispose(&content);
+ git_str_dispose(&path);
+}
+
+void test_reset_hard__cannot_reset_in_a_bare_repository(void)
+{
+ git_repository *bare;
+
+ cl_git_pass(git_repository_open(&bare, cl_fixture("testrepo.git")));
+ cl_assert(git_repository_is_bare(bare) == true);
+
+ cl_git_pass(git_revparse_single(&target, bare, KNOWN_COMMIT_IN_BARE_REPO));
+
+ cl_assert_equal_i(GIT_EBAREREPO, git_reset(bare, target, GIT_RESET_HARD, NULL));
+
+ git_repository_free(bare);
+}
+
+static void index_entry_init(git_index *index, int side, git_oid *oid)
+{
+ git_index_entry entry;
+
+ memset(&entry, 0x0, sizeof(git_index_entry));
+
+ entry.path = "conflicting_file";
+ GIT_INDEX_ENTRY_STAGE_SET(&entry, side);
+ entry.mode = 0100644;
+ git_oid_cpy(&entry.id, oid);
+
+ cl_git_pass(git_index_add(index, &entry));
+}
+
+static void unmerged_index_init(git_index *index, int entries)
+{
+ int write_ancestor = 1;
+ int write_ours = 2;
+ int write_theirs = 4;
+ git_oid ancestor, ours, theirs;
+
+ git_oid__fromstr(&ancestor, "452e4244b5d083ddf0460acf1ecc74db9dcfa11a", GIT_OID_SHA1);
+ git_oid__fromstr(&ours, "32504b727382542f9f089e24fddac5e78533e96c", GIT_OID_SHA1);
+ git_oid__fromstr(&theirs, "061d42a44cacde5726057b67558821d95db96f19", GIT_OID_SHA1);
+
+ cl_git_rewritefile("status/conflicting_file", "conflicting file\n");
+
+ if (entries & write_ancestor)
+ index_entry_init(index, 1, &ancestor);
+
+ if (entries & write_ours)
+ index_entry_init(index, 2, &ours);
+
+ if (entries & write_theirs)
+ index_entry_init(index, 3, &theirs);
+}
+
+void test_reset_hard__resetting_reverts_unmerged(void)
+{
+ git_index *index;
+ int entries;
+
+ /* Ensure every permutation of non-zero stage entries results in the
+ * path being cleaned up. */
+ for (entries = 1; entries < 8; entries++) {
+ cl_git_pass(git_repository_index(&index, repo));
+
+ unmerged_index_init(index, entries);
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_revparse_single(&target, repo, "26a125e"));
+ cl_git_pass(git_reset(repo, target, GIT_RESET_HARD, NULL));
+
+ cl_assert(git_fs_path_exists("status/conflicting_file") == 0);
+
+ git_object_free(target);
+ target = NULL;
+
+ git_index_free(index);
+ }
+}
+
+void test_reset_hard__cleans_up_merge(void)
+{
+ git_str merge_head_path = GIT_STR_INIT,
+ merge_msg_path = GIT_STR_INIT,
+ merge_mode_path = GIT_STR_INIT,
+ orig_head_path = GIT_STR_INIT;
+
+ cl_git_pass(git_str_joinpath(&merge_head_path, git_repository_path(repo), "MERGE_HEAD"));
+ cl_git_mkfile(git_str_cstr(&merge_head_path), "beefbeefbeefbeefbeefbeefbeefbeefbeefbeef\n");
+
+ cl_git_pass(git_str_joinpath(&merge_msg_path, git_repository_path(repo), "MERGE_MSG"));
+ cl_git_mkfile(git_str_cstr(&merge_msg_path), "Merge commit 0017bd4ab1ec30440b17bae1680cff124ab5f1f6\n");
+
+ cl_git_pass(git_str_joinpath(&merge_mode_path, git_repository_path(repo), "MERGE_MODE"));
+ cl_git_mkfile(git_str_cstr(&merge_mode_path), "");
+
+ cl_git_pass(git_str_joinpath(&orig_head_path, git_repository_path(repo), "ORIG_HEAD"));
+ cl_git_mkfile(git_str_cstr(&orig_head_path), "0017bd4ab1ec30440b17bae1680cff124ab5f1f6");
+
+ cl_git_pass(git_revparse_single(&target, repo, "0017bd4"));
+ cl_git_pass(git_reset(repo, target, GIT_RESET_HARD, NULL));
+
+ cl_assert(!git_fs_path_exists(git_str_cstr(&merge_head_path)));
+ cl_assert(!git_fs_path_exists(git_str_cstr(&merge_msg_path)));
+ cl_assert(!git_fs_path_exists(git_str_cstr(&merge_mode_path)));
+
+ cl_assert(git_fs_path_exists(git_str_cstr(&orig_head_path)));
+ cl_git_pass(p_unlink(git_str_cstr(&orig_head_path)));
+
+ git_str_dispose(&merge_head_path);
+ git_str_dispose(&merge_msg_path);
+ git_str_dispose(&merge_mode_path);
+ git_str_dispose(&orig_head_path);
+}
+
+void test_reset_hard__reflog_is_correct(void)
+{
+ git_str buf = GIT_STR_INIT;
+ git_annotated_commit *annotated;
+ const char *exp_msg = "commit: Add a file which name should appear before the "
+ "\"subdir/\" folder while being dealt with by the treewalker";
+
+ reflog_check(repo, "HEAD", 3, "emeric.fermas@gmail.com", exp_msg);
+ reflog_check(repo, "refs/heads/master", 3, "emeric.fermas@gmail.com", exp_msg);
+
+ /* Branch not moving, no reflog entry */
+ cl_git_pass(git_revparse_single(&target, repo, "HEAD^{commit}"));
+ cl_git_pass(git_reset(repo, target, GIT_RESET_HARD, NULL));
+ reflog_check(repo, "HEAD", 3, "emeric.fermas@gmail.com", exp_msg);
+ reflog_check(repo, "refs/heads/master", 3, "emeric.fermas@gmail.com", exp_msg);
+
+ git_object_free(target);
+
+ /* Moved branch, expect id in message */
+ cl_git_pass(git_revparse_single(&target, repo, "HEAD~^{commit}"));
+ cl_git_pass(git_str_printf(&buf, "reset: moving to %s", git_oid_tostr_s(git_object_id(target))));
+ cl_git_pass(git_reset(repo, target, GIT_RESET_HARD, NULL));
+ reflog_check(repo, "HEAD", 4, NULL, git_str_cstr(&buf));
+ reflog_check(repo, "refs/heads/master", 4, NULL, git_str_cstr(&buf));
+
+ git_str_dispose(&buf);
+
+ /* Moved branch, expect revspec in message */
+ exp_msg = "reset: moving to HEAD~^{commit}";
+ cl_git_pass(git_annotated_commit_from_revspec(&annotated, repo, "HEAD~^{commit}"));
+ cl_git_pass(git_reset_from_annotated(repo, annotated, GIT_RESET_HARD, NULL));
+ reflog_check(repo, "HEAD", 5, NULL, exp_msg);
+ reflog_check(repo, "refs/heads/master", 5, NULL, exp_msg);
+
+ git_annotated_commit_free(annotated);
+
+}
+
+void test_reset_hard__switch_file_to_dir(void)
+{
+ git_index_entry entry = {{ 0 }};
+ git_index *idx;
+ git_odb *odb;
+ git_object *commit;
+ git_tree *tree;
+ git_signature *sig;
+ git_oid src_tree_id, tgt_tree_id;
+ git_oid src_id, tgt_id;
+
+ cl_git_pass(git_repository_odb(&odb, repo));
+ cl_git_pass(git_odb_write(&entry.id, odb, "", 0, GIT_OBJECT_BLOB));
+ git_odb_free(odb);
+
+ entry.mode = GIT_FILEMODE_BLOB;
+ cl_git_pass(git_index__new(&idx, GIT_OID_SHA1));
+ cl_git_pass(git_signature_now(&sig, "foo", "bar"));
+
+ /* Create the old tree */
+ entry.path = "README";
+ cl_git_pass(git_index_add(idx, &entry));
+ entry.path = "dir";
+ cl_git_pass(git_index_add(idx, &entry));
+
+ cl_git_pass(git_index_write_tree_to(&src_tree_id, idx, repo));
+ cl_git_pass(git_index_clear(idx));
+
+ cl_git_pass(git_tree_lookup(&tree, repo, &src_tree_id));
+ cl_git_pass(git_commit_create(&src_id, repo, NULL, sig, sig, NULL, "foo", tree, 0, NULL));
+ git_tree_free(tree);
+
+ /* Create the new tree */
+ entry.path = "README";
+ cl_git_pass(git_index_add(idx, &entry));
+ entry.path = "dir/FILE";
+ cl_git_pass(git_index_add(idx, &entry));
+
+ cl_git_pass(git_index_write_tree_to(&tgt_tree_id, idx, repo));
+ cl_git_pass(git_tree_lookup(&tree, repo, &tgt_tree_id));
+ cl_git_pass(git_commit_create(&tgt_id, repo, NULL, sig, sig, NULL, "foo", tree, 0, NULL));
+ git_tree_free(tree);
+ git_index_free(idx);
+ git_signature_free(sig);
+
+ /* Let's go to a known state of the src commit with the file named 'dir' */
+ cl_git_pass(git_object_lookup(&commit, repo, &src_id, GIT_OBJECT_COMMIT));
+ cl_git_pass(git_reset(repo, commit, GIT_RESET_HARD, NULL));
+ git_object_free(commit);
+
+ /* And now we move over to the commit with the directory named 'dir' */
+ cl_git_pass(git_object_lookup(&commit, repo, &tgt_id, GIT_OBJECT_COMMIT));
+ cl_git_pass(git_reset(repo, commit, GIT_RESET_HARD, NULL));
+ git_object_free(commit);
+}
diff --git a/tests/libgit2/reset/mixed.c b/tests/libgit2/reset/mixed.c
new file mode 100644
index 0000000..4a78d4c
--- /dev/null
+++ b/tests/libgit2/reset/mixed.c
@@ -0,0 +1,85 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "reset_helpers.h"
+#include "path.h"
+
+static git_repository *repo;
+static git_object *target;
+
+void test_reset_mixed__initialize(void)
+{
+ repo = cl_git_sandbox_init("attr");
+ target = NULL;
+}
+
+void test_reset_mixed__cleanup(void)
+{
+ git_object_free(target);
+ target = NULL;
+
+ cl_git_sandbox_cleanup();
+}
+
+void test_reset_mixed__cannot_reset_in_a_bare_repository(void)
+{
+ git_repository *bare;
+
+ cl_git_pass(git_repository_open(&bare, cl_fixture("testrepo.git")));
+ cl_assert(git_repository_is_bare(bare) == true);
+
+ cl_git_pass(git_revparse_single(&target, bare, KNOWN_COMMIT_IN_BARE_REPO));
+
+ cl_assert_equal_i(GIT_EBAREREPO, git_reset(bare, target, GIT_RESET_MIXED, NULL));
+
+ git_repository_free(bare);
+}
+
+void test_reset_mixed__resetting_refreshes_the_index_to_the_commit_tree(void)
+{
+ unsigned int status;
+
+ cl_git_pass(git_status_file(&status, repo, "macro_bad"));
+ cl_assert(status == GIT_STATUS_CURRENT);
+ cl_git_pass(git_revparse_single(&target, repo, "605812a"));
+
+ cl_git_pass(git_reset(repo, target, GIT_RESET_MIXED, NULL));
+
+ cl_git_pass(git_status_file(&status, repo, "macro_bad"));
+ cl_assert(status == GIT_STATUS_WT_NEW);
+}
+
+void test_reset_mixed__reflog_is_correct(void)
+{
+ git_str buf = GIT_STR_INIT;
+ git_annotated_commit *annotated;
+ const char *exp_msg = "commit: Updating test data so we can test inter-hunk-context";
+
+ reflog_check(repo, "HEAD", 9, "yoram.harmelin@gmail.com", exp_msg);
+ reflog_check(repo, "refs/heads/master", 9, "yoram.harmelin@gmail.com", exp_msg);
+
+ /* Branch not moving, no reflog entry */
+ cl_git_pass(git_revparse_single(&target, repo, "HEAD^{commit}"));
+ cl_git_pass(git_reset(repo, target, GIT_RESET_MIXED, NULL));
+ reflog_check(repo, "HEAD", 9, "yoram.harmelin@gmail.com", exp_msg);
+ reflog_check(repo, "refs/heads/master", 9, "yoram.harmelin@gmail.com", exp_msg);
+
+ git_object_free(target);
+ target = NULL;
+
+ /* Moved branch, expect id in message */
+ cl_git_pass(git_revparse_single(&target, repo, "HEAD~^{commit}"));
+ git_str_clear(&buf);
+ cl_git_pass(git_str_printf(&buf, "reset: moving to %s", git_oid_tostr_s(git_object_id(target))));
+ cl_git_pass(git_reset(repo, target, GIT_RESET_MIXED, NULL));
+ reflog_check(repo, "HEAD", 10, NULL, git_str_cstr(&buf));
+ reflog_check(repo, "refs/heads/master", 10, NULL, git_str_cstr(&buf));
+ git_str_dispose(&buf);
+
+ /* Moved branch, expect revspec in message */
+ exp_msg = "reset: moving to HEAD~^{commit}";
+ cl_git_pass(git_annotated_commit_from_revspec(&annotated, repo, "HEAD~^{commit}"));
+ cl_git_pass(git_reset_from_annotated(repo, annotated, GIT_RESET_MIXED, NULL));
+ reflog_check(repo, "HEAD", 11, NULL, exp_msg);
+ reflog_check(repo, "refs/heads/master", 11, NULL, exp_msg);
+ git_annotated_commit_free(annotated);
+}
diff --git a/tests/libgit2/reset/reset_helpers.c b/tests/libgit2/reset/reset_helpers.c
new file mode 100644
index 0000000..e6acec9
--- /dev/null
+++ b/tests/libgit2/reset/reset_helpers.c
@@ -0,0 +1,20 @@
+#include "clar_libgit2.h"
+#include "reset_helpers.h"
+
+void reflog_check(git_repository *repo, const char *refname,
+ size_t exp_count, const char *exp_email, const char *exp_msg)
+{
+ git_reflog *log;
+ const git_reflog_entry *entry;
+
+ GIT_UNUSED(exp_email);
+
+ cl_git_pass(git_reflog_read(&log, repo, refname));
+ cl_assert_equal_i(exp_count, git_reflog_entrycount(log));
+ entry = git_reflog_entry_byindex(log, 0);
+
+ if (exp_msg)
+ cl_assert_equal_s(exp_msg, git_reflog_entry_message(entry));
+
+ git_reflog_free(log);
+}
diff --git a/tests/libgit2/reset/reset_helpers.h b/tests/libgit2/reset/reset_helpers.h
new file mode 100644
index 0000000..e7e0485
--- /dev/null
+++ b/tests/libgit2/reset/reset_helpers.h
@@ -0,0 +1,7 @@
+#include "common.h"
+
+#define KNOWN_COMMIT_IN_BARE_REPO "e90810b8df3e80c413d903f631643c716887138d"
+#define KNOWN_COMMIT_IN_ATTR_REPO "217878ab49e1314388ea2e32dc6fdb58a1b969e0"
+
+void reflog_check(git_repository *repo, const char *refname,
+ size_t exp_count, const char *exp_email, const char *exp_msg);
diff --git a/tests/libgit2/reset/soft.c b/tests/libgit2/reset/soft.c
new file mode 100644
index 0000000..1289050
--- /dev/null
+++ b/tests/libgit2/reset/soft.c
@@ -0,0 +1,189 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "reset_helpers.h"
+#include "path.h"
+#include "repo/repo_helpers.h"
+
+static git_repository *repo;
+static git_object *target;
+
+void test_reset_soft__initialize(void)
+{
+ repo = cl_git_sandbox_init("testrepo.git");
+}
+
+void test_reset_soft__cleanup(void)
+{
+ git_object_free(target);
+ target = NULL;
+
+ cl_git_sandbox_cleanup();
+}
+
+static void assert_reset_soft(bool should_be_detached)
+{
+ git_oid oid;
+
+ cl_git_pass(git_reference_name_to_id(&oid, repo, "HEAD"));
+ cl_git_fail(git_oid_streq(&oid, KNOWN_COMMIT_IN_BARE_REPO));
+ cl_git_pass(git_revparse_single(&target, repo, KNOWN_COMMIT_IN_BARE_REPO));
+
+ cl_assert(git_repository_head_detached(repo) == should_be_detached);
+
+ cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT, NULL));
+
+ cl_assert(git_repository_head_detached(repo) == should_be_detached);
+
+ cl_git_pass(git_reference_name_to_id(&oid, repo, "HEAD"));
+ cl_git_pass(git_oid_streq(&oid, KNOWN_COMMIT_IN_BARE_REPO));
+}
+
+void test_reset_soft__can_reset_the_non_detached_Head_to_the_specified_commit(void)
+{
+ assert_reset_soft(false);
+}
+
+void test_reset_soft__can_reset_the_detached_Head_to_the_specified_commit(void)
+{
+ git_repository_detach_head(repo);
+
+ assert_reset_soft(true);
+}
+
+void test_reset_soft__resetting_to_the_commit_pointed_at_by_the_Head_does_not_change_the_target_of_the_Head(void)
+{
+ git_oid oid;
+ char raw_head_oid[GIT_OID_SHA1_HEXSIZE + 1];
+
+ cl_git_pass(git_reference_name_to_id(&oid, repo, "HEAD"));
+ git_oid_fmt(raw_head_oid, &oid);
+ raw_head_oid[GIT_OID_SHA1_HEXSIZE] = '\0';
+
+ cl_git_pass(git_revparse_single(&target, repo, raw_head_oid));
+
+ cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT, NULL));
+
+ cl_git_pass(git_reference_name_to_id(&oid, repo, "HEAD"));
+ cl_git_pass(git_oid_streq(&oid, raw_head_oid));
+}
+
+void test_reset_soft__resetting_to_a_tag_sets_the_Head_to_the_peeled_commit(void)
+{
+ git_oid oid;
+
+ /* b25fa35 is a tag, pointing to another tag which points to commit e90810b */
+ cl_git_pass(git_revparse_single(&target, repo, "b25fa35"));
+
+ cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT, NULL));
+
+ cl_assert(git_repository_head_detached(repo) == false);
+ cl_git_pass(git_reference_name_to_id(&oid, repo, "HEAD"));
+ cl_git_pass(git_oid_streq(&oid, KNOWN_COMMIT_IN_BARE_REPO));
+}
+
+void test_reset_soft__cannot_reset_to_a_tag_not_pointing_at_a_commit(void)
+{
+ /* 53fc32d is the tree of commit e90810b */
+ cl_git_pass(git_revparse_single(&target, repo, "53fc32d"));
+
+ cl_git_fail(git_reset(repo, target, GIT_RESET_SOFT, NULL));
+ git_object_free(target);
+
+ /* 521d87c is an annotated tag pointing to a blob */
+ cl_git_pass(git_revparse_single(&target, repo, "521d87c"));
+ cl_git_fail(git_reset(repo, target, GIT_RESET_SOFT, NULL));
+}
+
+void test_reset_soft__resetting_against_an_unborn_head_repo_makes_the_head_no_longer_unborn(void)
+{
+ git_reference *head;
+
+ cl_git_pass(git_revparse_single(&target, repo, KNOWN_COMMIT_IN_BARE_REPO));
+
+ make_head_unborn(repo, NON_EXISTING_HEAD);
+
+ cl_assert_equal_i(true, git_repository_head_unborn(repo));
+
+ cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT, NULL));
+
+ cl_assert_equal_i(false, git_repository_head_unborn(repo));
+
+ cl_git_pass(git_reference_lookup(&head, repo, NON_EXISTING_HEAD));
+ cl_assert_equal_i(0, git_oid_streq(git_reference_target(head), KNOWN_COMMIT_IN_BARE_REPO));
+
+ git_reference_free(head);
+}
+
+void test_reset_soft__fails_when_merging(void)
+{
+ git_str merge_head_path = GIT_STR_INIT;
+
+ cl_git_pass(git_repository_detach_head(repo));
+ cl_git_pass(git_str_joinpath(&merge_head_path, git_repository_path(repo), "MERGE_HEAD"));
+ cl_git_mkfile(git_str_cstr(&merge_head_path), "beefbeefbeefbeefbeefbeefbeefbeefbeefbeef\n");
+
+ cl_git_pass(git_revparse_single(&target, repo, KNOWN_COMMIT_IN_BARE_REPO));
+
+ cl_assert_equal_i(GIT_EUNMERGED, git_reset(repo, target, GIT_RESET_SOFT, NULL));
+ cl_git_pass(p_unlink(git_str_cstr(&merge_head_path)));
+
+ git_str_dispose(&merge_head_path);
+}
+
+void test_reset_soft__fails_when_index_contains_conflicts_independently_of_MERGE_HEAD_file_existence(void)
+{
+ git_index *index;
+ git_reference *head;
+ git_str merge_head_path = GIT_STR_INIT;
+
+ cl_git_sandbox_cleanup();
+
+ repo = cl_git_sandbox_init("mergedrepo");
+
+ cl_git_pass(git_str_joinpath(&merge_head_path, git_repository_path(repo), "MERGE_HEAD"));
+ cl_git_pass(p_unlink(git_str_cstr(&merge_head_path)));
+ git_str_dispose(&merge_head_path);
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert_equal_i(true, git_index_has_conflicts(index));
+ git_index_free(index);
+
+ cl_git_pass(git_repository_head(&head, repo));
+ cl_git_pass(git_reference_peel(&target, head, GIT_OBJECT_COMMIT));
+ git_reference_free(head);
+
+ cl_assert_equal_i(GIT_EUNMERGED, git_reset(repo, target, GIT_RESET_SOFT, NULL));
+}
+
+void test_reset_soft__reflog_is_correct(void)
+{
+ git_annotated_commit *annotated;
+ const char *exp_msg = "checkout: moving from br2 to master";
+ const char *master_msg = "commit: checking in";
+
+ reflog_check(repo, "HEAD", 7, "yoram.harmelin@gmail.com", exp_msg);
+ reflog_check(repo, "refs/heads/master", 2, "yoram.harmelin@gmail.com", master_msg);
+
+ /* Branch not moving, no reflog entry */
+ cl_git_pass(git_revparse_single(&target, repo, "HEAD^{commit}"));
+ cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT, NULL));
+ reflog_check(repo, "HEAD", 7, "yoram.harmelin@gmail.com", exp_msg);
+ reflog_check(repo, "refs/heads/master", 2, "yoram.harmelin@gmail.com", master_msg);
+ git_object_free(target);
+
+ /* Moved branch, expect id in message */
+ exp_msg = "reset: moving to be3563ae3f795b2b4353bcce3a527ad0a4f7f644";
+ cl_git_pass(git_revparse_single(&target, repo, "HEAD~^{commit}"));
+ cl_git_pass(git_reset(repo, target, GIT_RESET_SOFT, NULL));
+ reflog_check(repo, "HEAD", 8, "yoram.harmelin@gmail.com", exp_msg);
+ reflog_check(repo, "refs/heads/master", 3, NULL, exp_msg);
+
+ /* Moved branch, expect message with annotated string */
+ exp_msg = "reset: moving to HEAD~^{commit}";
+ cl_git_pass(git_annotated_commit_from_revspec(&annotated, repo, "HEAD~^{commit}"));
+ cl_git_pass(git_reset_from_annotated(repo, annotated, GIT_RESET_SOFT, NULL));
+ reflog_check(repo, "HEAD", 9, "yoram.harmelin@gmail.com", exp_msg);
+ reflog_check(repo, "refs/heads/master", 4, NULL, exp_msg);
+
+ git_annotated_commit_free(annotated);
+}
diff --git a/tests/libgit2/revert/bare.c b/tests/libgit2/revert/bare.c
new file mode 100644
index 0000000..db44302
--- /dev/null
+++ b/tests/libgit2/revert/bare.c
@@ -0,0 +1,106 @@
+#include "clar.h"
+#include "clar_libgit2.h"
+
+#include "futils.h"
+#include "git2/revert.h"
+
+#include "../merge/merge_helpers.h"
+
+#define TEST_REPO_PATH "revert"
+
+static git_repository *repo;
+
+/* Fixture setup and teardown */
+void test_revert_bare__initialize(void)
+{
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+}
+
+void test_revert_bare__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_revert_bare__automerge(void)
+{
+ git_commit *head_commit, *revert_commit;
+ git_oid head_oid, revert_oid;
+ git_index *index;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "caf99de3a49827117bb66721010eac461b06a80c", 0, "file1.txt" },
+ { 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" },
+ { 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" },
+ { 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 0, "file6.txt" },
+ };
+
+ git_oid__fromstr(&head_oid, "72333f47d4e83616630ff3b0ffe4c0faebcc3c45", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&head_commit, repo, &head_oid));
+
+ git_oid__fromstr(&revert_oid, "d1d403d22cbe24592d725f442835cf46fe60c8ac", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&revert_commit, repo, &revert_oid));
+
+ cl_git_pass(git_revert_commit(&index, repo, revert_commit, head_commit, 0, NULL));
+ cl_assert(merge_test_index(index, merge_index_entries, 4));
+
+ git_commit_free(revert_commit);
+ git_commit_free(head_commit);
+ git_index_free(index);
+}
+
+void test_revert_bare__conflicts(void)
+{
+ git_reference *head_ref;
+ git_commit *head_commit, *revert_commit;
+ git_oid revert_oid;
+ git_index *index;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "7731926a337c4eaba1e2187d90ebfa0a93659382", 1, "file1.txt" },
+ { 0100644, "4b8fcff56437e60f58e9a6bc630dd242ebf6ea2c", 2, "file1.txt" },
+ { 0100644, "3a3ef367eaf3fe79effbfb0a56b269c04c2b59fe", 3, "file1.txt" },
+ { 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" },
+ { 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" },
+ { 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 0, "file6.txt" },
+ };
+
+ git_oid__fromstr(&revert_oid, "72333f47d4e83616630ff3b0ffe4c0faebcc3c45", GIT_OID_SHA1);
+
+ cl_git_pass(git_repository_head(&head_ref, repo));
+ cl_git_pass(git_reference_peel((git_object **)&head_commit, head_ref, GIT_OBJECT_COMMIT));
+
+ cl_git_pass(git_commit_lookup(&revert_commit, repo, &revert_oid));
+ cl_git_pass(git_revert_commit(&index, repo, revert_commit, head_commit, 0, NULL));
+
+ cl_assert(git_index_has_conflicts(index));
+ cl_assert(merge_test_index(index, merge_index_entries, 6));
+
+ git_commit_free(revert_commit);
+ git_commit_free(head_commit);
+ git_reference_free(head_ref);
+ git_index_free(index);
+}
+
+void test_revert_bare__orphan(void)
+{
+ git_commit *head_commit, *revert_commit;
+ git_oid head_oid, revert_oid;
+ git_index *index;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "296a6d3be1dff05c5d1f631d2459389fa7b619eb", 0, "file-mainline.txt" },
+ };
+
+ git_oid__fromstr(&head_oid, "39467716290f6df775a91cdb9a4eb39295018145", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&head_commit, repo, &head_oid));
+
+ git_oid__fromstr(&revert_oid, "ebb03002cee5d66c7732dd06241119fe72ab96a5", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&revert_commit, repo, &revert_oid));
+
+ cl_git_pass(git_revert_commit(&index, repo, revert_commit, head_commit, 0, NULL));
+ cl_assert(merge_test_index(index, merge_index_entries, 1));
+
+ git_commit_free(revert_commit);
+ git_commit_free(head_commit);
+ git_index_free(index);
+}
diff --git a/tests/libgit2/revert/rename.c b/tests/libgit2/revert/rename.c
new file mode 100644
index 0000000..be7a980
--- /dev/null
+++ b/tests/libgit2/revert/rename.c
@@ -0,0 +1,49 @@
+#include "clar.h"
+#include "clar_libgit2.h"
+
+#include "git2/revert.h"
+#include "../merge/merge_helpers.h"
+
+#define TEST_REPO_PATH "revert-rename.git"
+
+static git_repository *repo;
+
+/* Fixture setup and teardown */
+void test_revert_rename__initialize(void)
+{
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+}
+
+void test_revert_rename__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+/* Attempt a revert when there is a file rename AND change of file mode,
+ * but the file contents remain the same. Check that the file mode doesn't
+ * change following the revert.
+ */
+void test_revert_rename__automerge(void)
+{
+ git_commit *head_commit, *revert_commit;
+ git_oid revert_oid;
+ git_index *index;
+ git_reference *head_ref;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "f0f64c618e1646d2948a456ed7c4bcfad5536d68", 0, "goodmode" }};
+
+ cl_git_pass(git_repository_head(&head_ref, repo));
+ cl_git_pass(git_reference_peel((git_object **)&head_commit, head_ref, GIT_OBJECT_COMMIT));
+
+ cl_git_pass(git_oid__fromstr(&revert_oid, "7b4d7c3789b3581973c04087cb774c3c3576de2f", GIT_OID_SHA1));
+ cl_git_pass(git_commit_lookup(&revert_commit, repo, &revert_oid));
+
+ cl_git_pass(git_revert_commit(&index, repo, revert_commit, head_commit, 0, NULL));
+ cl_assert(merge_test_index(index, merge_index_entries, 1));
+
+ git_commit_free(revert_commit);
+ git_commit_free(head_commit);
+ git_index_free(index);
+ git_reference_free(head_ref);
+}
diff --git a/tests/libgit2/revert/workdir.c b/tests/libgit2/revert/workdir.c
new file mode 100644
index 0000000..3e790b7
--- /dev/null
+++ b/tests/libgit2/revert/workdir.c
@@ -0,0 +1,576 @@
+#include "clar.h"
+#include "clar_libgit2.h"
+
+#include "futils.h"
+#include "git2/revert.h"
+
+#include "../merge/merge_helpers.h"
+
+#define TEST_REPO_PATH "revert"
+
+static git_repository *repo;
+static git_index *repo_index;
+
+/* Fixture setup and teardown */
+void test_revert_workdir__initialize(void)
+{
+ git_config *cfg;
+
+ repo = cl_git_sandbox_init(TEST_REPO_PATH);
+ git_repository_index(&repo_index, repo);
+
+ /* Ensure that the user's merge.conflictstyle doesn't interfere */
+ cl_git_pass(git_repository_config(&cfg, repo));
+ cl_git_pass(git_config_set_string(cfg, "merge.conflictstyle", "merge"));
+ git_config_free(cfg);
+}
+
+void test_revert_workdir__cleanup(void)
+{
+ git_index_free(repo_index);
+ cl_git_sandbox_cleanup();
+}
+
+/* git reset --hard 72333f47d4e83616630ff3b0ffe4c0faebcc3c45
+ * git revert --no-commit d1d403d22cbe24592d725f442835cf46fe60c8ac */
+void test_revert_workdir__automerge(void)
+{
+ git_commit *head, *commit;
+ git_oid head_oid, revert_oid;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "caf99de3a49827117bb66721010eac461b06a80c", 0, "file1.txt" },
+ { 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" },
+ { 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" },
+ { 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 0, "file6.txt" },
+ };
+
+ git_oid__fromstr(&head_oid, "72333f47d4e83616630ff3b0ffe4c0faebcc3c45", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
+ cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL));
+
+ git_oid__fromstr(&revert_oid, "d1d403d22cbe24592d725f442835cf46fe60c8ac", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, repo, &revert_oid));
+ cl_git_pass(git_revert(repo, commit, NULL));
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 4));
+
+ git_commit_free(commit);
+ git_commit_free(head);
+}
+
+/* git revert --no-commit 72333f47d4e83616630ff3b0ffe4c0faebcc3c45 */
+void test_revert_workdir__conflicts(void)
+{
+ git_reference *head_ref;
+ git_commit *head, *commit;
+ git_oid revert_oid;
+ git_str conflicting_buf = GIT_STR_INIT, mergemsg_buf = GIT_STR_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "7731926a337c4eaba1e2187d90ebfa0a93659382", 1, "file1.txt" },
+ { 0100644, "4b8fcff56437e60f58e9a6bc630dd242ebf6ea2c", 2, "file1.txt" },
+ { 0100644, "3a3ef367eaf3fe79effbfb0a56b269c04c2b59fe", 3, "file1.txt" },
+ { 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" },
+ { 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" },
+ { 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 0, "file6.txt" },
+ };
+
+ git_oid__fromstr(&revert_oid, "72333f47d4e83616630ff3b0ffe4c0faebcc3c45", GIT_OID_SHA1);
+
+ cl_git_pass(git_repository_head(&head_ref, repo));
+ cl_git_pass(git_reference_peel((git_object **)&head, head_ref, GIT_OBJECT_COMMIT));
+ cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL));
+
+ cl_git_pass(git_commit_lookup(&commit, repo, &revert_oid));
+ cl_git_pass(git_revert(repo, commit, NULL));
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 6));
+
+ cl_git_pass(git_futils_readbuffer(&conflicting_buf,
+ TEST_REPO_PATH "/file1.txt"));
+ cl_assert(strcmp(conflicting_buf.ptr, "!File one!\n" \
+ "!File one!\n" \
+ "File one!\n" \
+ "File one\n" \
+ "File one\n" \
+ "File one\n" \
+ "File one\n" \
+ "File one\n" \
+ "File one\n" \
+ "File one\n" \
+ "<<<<<<< HEAD\n" \
+ "File one!\n" \
+ "!File one!\n" \
+ "!File one!\n" \
+ "!File one!\n" \
+ "=======\n" \
+ "File one\n" \
+ "File one\n" \
+ "File one\n" \
+ "File one\n" \
+ ">>>>>>> parent of 72333f4... automergeable changes\n") == 0);
+
+ cl_assert(git_fs_path_exists(TEST_REPO_PATH "/.git/MERGE_MSG"));
+ cl_git_pass(git_futils_readbuffer(&mergemsg_buf,
+ TEST_REPO_PATH "/.git/MERGE_MSG"));
+ cl_assert(strcmp(mergemsg_buf.ptr,
+ "Revert \"automergeable changes\"\n" \
+ "\n" \
+ "This reverts commit 72333f47d4e83616630ff3b0ffe4c0faebcc3c45.\n"
+ "\n" \
+ "#Conflicts:\n" \
+ "#\tfile1.txt\n") == 0);
+
+ git_commit_free(commit);
+ git_commit_free(head);
+ git_reference_free(head_ref);
+ git_str_dispose(&mergemsg_buf);
+ git_str_dispose(&conflicting_buf);
+}
+
+/* git reset --hard 39467716290f6df775a91cdb9a4eb39295018145
+ * git revert --no-commit ebb03002cee5d66c7732dd06241119fe72ab96a5
+*/
+void test_revert_workdir__orphan(void)
+{
+ git_commit *head, *commit;
+ git_oid head_oid, revert_oid;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "296a6d3be1dff05c5d1f631d2459389fa7b619eb", 0, "file-mainline.txt" },
+ };
+
+ git_oid__fromstr(&head_oid, "39467716290f6df775a91cdb9a4eb39295018145", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
+ cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL));
+
+ git_oid__fromstr(&revert_oid, "ebb03002cee5d66c7732dd06241119fe72ab96a5", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, repo, &revert_oid));
+ cl_git_pass(git_revert(repo, commit, NULL));
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 1));
+
+ git_commit_free(commit);
+ git_commit_free(head);
+}
+
+/*
+ * revert the same commit twice (when the first reverts cleanly):
+ *
+ * git revert 2d440f2
+ * git revert 2d440f2
+ */
+void test_revert_workdir__again(void)
+{
+ git_reference *head_ref;
+ git_commit *orig_head;
+ git_tree *reverted_tree;
+ git_oid reverted_tree_oid, reverted_commit_oid;
+ git_signature *signature;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "7731926a337c4eaba1e2187d90ebfa0a93659382", 0, "file1.txt" },
+ { 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" },
+ { 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" },
+ { 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 0, "file6.txt" },
+ };
+
+ cl_git_pass(git_repository_head(&head_ref, repo));
+ cl_git_pass(git_reference_peel((git_object **)&orig_head, head_ref, GIT_OBJECT_COMMIT));
+ cl_git_pass(git_reset(repo, (git_object *)orig_head, GIT_RESET_HARD, NULL));
+
+ cl_git_pass(git_revert(repo, orig_head, NULL));
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 4));
+
+ cl_git_pass(git_index_write_tree(&reverted_tree_oid, repo_index));
+ cl_git_pass(git_tree_lookup(&reverted_tree, repo, &reverted_tree_oid));
+
+ cl_git_pass(git_signature_new(&signature, "Reverter", "reverter@example.org", time(NULL), 0));
+ cl_git_pass(git_commit_create(&reverted_commit_oid, repo, "HEAD", signature, signature, NULL, "Reverted!", reverted_tree, 1, (const git_commit **)&orig_head));
+
+ cl_git_pass(git_revert(repo, orig_head, NULL));
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 4));
+
+ git_signature_free(signature);
+ git_tree_free(reverted_tree);
+ git_commit_free(orig_head);
+ git_reference_free(head_ref);
+}
+
+/* git reset --hard 72333f47d4e83616630ff3b0ffe4c0faebcc3c45
+ * git revert --no-commit d1d403d22cbe24592d725f442835cf46fe60c8ac */
+void test_revert_workdir__again_after_automerge(void)
+{
+ git_commit *head, *commit;
+ git_tree *reverted_tree;
+ git_oid head_oid, revert_oid, reverted_tree_oid, reverted_commit_oid;
+ git_signature *signature;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "caf99de3a49827117bb66721010eac461b06a80c", 0, "file1.txt" },
+ { 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" },
+ { 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" },
+ { 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 0, "file6.txt" },
+ };
+
+ struct merge_index_entry second_revert_entries[] = {
+ { 0100644, "3a3ef367eaf3fe79effbfb0a56b269c04c2b59fe", 1, "file1.txt" },
+ { 0100644, "caf99de3a49827117bb66721010eac461b06a80c", 2, "file1.txt" },
+ { 0100644, "747726e021bc5f44b86de60e3032fd6f9f1b8383", 3, "file1.txt" },
+ { 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" },
+ { 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" },
+ { 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 0, "file6.txt" },
+ };
+
+ git_oid__fromstr(&head_oid, "72333f47d4e83616630ff3b0ffe4c0faebcc3c45", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
+ cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL));
+
+ git_oid__fromstr(&revert_oid, "d1d403d22cbe24592d725f442835cf46fe60c8ac", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, repo, &revert_oid));
+ cl_git_pass(git_revert(repo, commit, NULL));
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 4));
+
+ cl_git_pass(git_index_write_tree(&reverted_tree_oid, repo_index));
+ cl_git_pass(git_tree_lookup(&reverted_tree, repo, &reverted_tree_oid));
+
+ cl_git_pass(git_signature_new(&signature, "Reverter", "reverter@example.org", time(NULL), 0));
+ cl_git_pass(git_commit_create(&reverted_commit_oid, repo, "HEAD", signature, signature, NULL, "Reverted!", reverted_tree, 1, (const git_commit **)&head));
+
+ cl_git_pass(git_revert(repo, commit, NULL));
+ cl_assert(merge_test_index(repo_index, second_revert_entries, 6));
+
+ git_signature_free(signature);
+ git_tree_free(reverted_tree);
+ git_commit_free(commit);
+ git_commit_free(head);
+}
+
+/*
+ * revert the same commit twice (when the first reverts cleanly):
+ *
+ * git revert 2d440f2
+ * git revert 2d440f2
+ */
+void test_revert_workdir__again_after_edit(void)
+{
+ git_reference *head_ref;
+ git_commit *orig_head, *commit;
+ git_tree *reverted_tree;
+ git_oid orig_head_oid, revert_oid, reverted_tree_oid, reverted_commit_oid;
+ git_signature *signature;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "3721552e06c4bdc7d478e0674e6304888545d5fd", 0, "file1.txt" },
+ { 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" },
+ { 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" },
+ { 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 0, "file6.txt" },
+ };
+
+ cl_git_pass(git_repository_head(&head_ref, repo));
+
+ cl_git_pass(git_oid__fromstr(&orig_head_oid, "399fb3aba3d9d13f7d40a9254ce4402067ef3149", GIT_OID_SHA1));
+ cl_git_pass(git_commit_lookup(&orig_head, repo, &orig_head_oid));
+ cl_git_pass(git_reset(repo, (git_object *)orig_head, GIT_RESET_HARD, NULL));
+
+ cl_git_pass(git_oid__fromstr(&revert_oid, "2d440f2b3147d3dc7ad1085813478d6d869d5a4d", GIT_OID_SHA1));
+ cl_git_pass(git_commit_lookup(&commit, repo, &revert_oid));
+
+ cl_git_pass(git_revert(repo, commit, NULL));
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 4));
+
+ cl_git_pass(git_index_write_tree(&reverted_tree_oid, repo_index));
+ cl_git_pass(git_tree_lookup(&reverted_tree, repo, &reverted_tree_oid));
+
+ cl_git_pass(git_signature_new(&signature, "Reverter", "reverter@example.org", time(NULL), 0));
+ cl_git_pass(git_commit_create(&reverted_commit_oid, repo, "HEAD", signature, signature, NULL, "Reverted!", reverted_tree, 1, (const git_commit **)&orig_head));
+
+ cl_git_pass(git_revert(repo, commit, NULL));
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 4));
+
+ git_signature_free(signature);
+ git_tree_free(reverted_tree);
+ git_commit_free(commit);
+ git_commit_free(orig_head);
+ git_reference_free(head_ref);
+}
+
+/*
+ * revert the same commit twice (when the first reverts cleanly):
+ *
+ * git reset --hard 75ec9929465623f17ff3ad68c0438ea56faba815
+ * git revert 97e52d5e81f541080cd6b92829fb85bc4d81d90b
+ */
+void test_revert_workdir__again_after_edit_two(void)
+{
+ git_str diff_buf = GIT_STR_INIT;
+ git_config *config;
+ git_oid head_commit_oid, revert_commit_oid;
+ git_commit *head_commit, *revert_commit;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "a8c86221b400b836010567cc3593db6e96c1a83a", 1, "file.txt" },
+ { 0100644, "46ff0854663aeb2182b9838c8da68e33ac23bc1e", 2, "file.txt" },
+ { 0100644, "21a96a98ed84d45866e1de6e266fd3a61a4ae9dc", 3, "file.txt" },
+ };
+
+ cl_git_pass(git_repository_config(&config, repo));
+ cl_git_pass(git_config_set_bool(config, "core.autocrlf", 0));
+
+ cl_git_pass(git_oid__fromstr(&head_commit_oid, "75ec9929465623f17ff3ad68c0438ea56faba815", GIT_OID_SHA1));
+ cl_git_pass(git_commit_lookup(&head_commit, repo, &head_commit_oid));
+ cl_git_pass(git_reset(repo, (git_object *)head_commit, GIT_RESET_HARD, NULL));
+
+ cl_git_pass(git_oid__fromstr(&revert_commit_oid, "97e52d5e81f541080cd6b92829fb85bc4d81d90b", GIT_OID_SHA1));
+ cl_git_pass(git_commit_lookup(&revert_commit, repo, &revert_commit_oid));
+
+ cl_git_pass(git_revert(repo, revert_commit, NULL));
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 3));
+
+ cl_git_pass(git_futils_readbuffer(&diff_buf, "revert/file.txt"));
+ cl_assert_equal_s(
+ "a\n" \
+ "<<<<<<< HEAD\n" \
+ "=======\n" \
+ "a\n" \
+ ">>>>>>> parent of 97e52d5... Revert me\n" \
+ "a\n" \
+ "a\n" \
+ "a\n" \
+ "a\n" \
+ "ab",
+ diff_buf.ptr);
+
+ git_commit_free(revert_commit);
+ git_commit_free(head_commit);
+ git_config_free(config);
+ git_str_dispose(&diff_buf);
+}
+
+/* git reset --hard 72333f47d4e83616630ff3b0ffe4c0faebcc3c45
+ * git revert --no-commit d1d403d22cbe24592d725f442835cf46fe60c8ac */
+void test_revert_workdir__conflict_use_ours(void)
+{
+ git_commit *head, *commit;
+ git_oid head_oid, revert_oid;
+ git_revert_options opts = GIT_REVERT_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "caf99de3a49827117bb66721010eac461b06a80c", 0, "file1.txt" },
+ { 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" },
+ { 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" },
+ { 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 0, "file6.txt" },
+ };
+
+ struct merge_index_entry merge_filesystem_entries[] = {
+ { 0100644, "caf99de3a49827117bb66721010eac461b06a80c", 0, "file1.txt" },
+ { 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" },
+ { 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" },
+ { 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 0, "file6.txt" },
+ };
+
+ opts.checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_USE_OURS;
+
+ git_oid__fromstr(&head_oid, "72333f47d4e83616630ff3b0ffe4c0faebcc3c45", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
+ cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL));
+
+ git_oid__fromstr(&revert_oid, "d1d403d22cbe24592d725f442835cf46fe60c8ac", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, repo, &revert_oid));
+ cl_git_pass(git_revert(repo, commit, &opts));
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 4));
+ cl_assert(merge_test_workdir(repo, merge_filesystem_entries, 4));
+
+ git_commit_free(commit);
+ git_commit_free(head);
+}
+
+/* git reset --hard cef56612d71a6af8d8015691e4865f7fece905b5
+ * git revert --no-commit 55568c8de5322ff9a95d72747a239cdb64a19965
+ */
+void test_revert_workdir__rename_1_of_2(void)
+{
+ git_commit *head, *commit;
+ git_oid head_oid, revert_oid;
+ git_revert_options opts = GIT_REVERT_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "747726e021bc5f44b86de60e3032fd6f9f1b8383", 0, "file1.txt" },
+ { 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" },
+ { 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" },
+ { 0100644, "55acf326a69f0aab7a974ec53ffa55a50bcac14e", 3, "file4.txt" },
+ { 0100644, "55acf326a69f0aab7a974ec53ffa55a50bcac14e", 1, "file5.txt" },
+ { 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 2, "file6.txt" },
+ };
+
+ opts.merge_opts.flags |= GIT_MERGE_FIND_RENAMES;
+ opts.merge_opts.rename_threshold = 50;
+
+ git_oid__fromstr(&head_oid, "cef56612d71a6af8d8015691e4865f7fece905b5", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
+ cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL));
+
+ git_oid__fromstr(&revert_oid, "55568c8de5322ff9a95d72747a239cdb64a19965", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, repo, &revert_oid));
+ cl_git_pass(git_revert(repo, commit, &opts));
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 6));
+
+ git_commit_free(commit);
+ git_commit_free(head);
+}
+
+/* git reset --hard 55568c8de5322ff9a95d72747a239cdb64a19965
+ * git revert --no-commit HEAD~1 */
+void test_revert_workdir__rename(void)
+{
+ git_commit *head, *commit;
+ git_oid head_oid, revert_oid;
+ git_revert_options opts = GIT_REVERT_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "55acf326a69f0aab7a974ec53ffa55a50bcac14e", 1, "file4.txt" },
+ { 0100644, "55acf326a69f0aab7a974ec53ffa55a50bcac14e", 2, "file5.txt" },
+ };
+
+ struct merge_name_entry merge_name_entries[] = {
+ { "file4.txt", "file5.txt", "" },
+ };
+
+ opts.merge_opts.flags |= GIT_MERGE_FIND_RENAMES;
+ opts.merge_opts.rename_threshold = 50;
+
+ git_oid__fromstr(&head_oid, "55568c8de5322ff9a95d72747a239cdb64a19965", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
+ cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL));
+
+ git_oid__fromstr(&revert_oid, "0aa8c7e40d342fff78d60b29a4ba8e993ed79c51", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&commit, repo, &revert_oid));
+ cl_git_pass(git_revert(repo, commit, &opts));
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 2));
+ cl_assert(merge_test_names(repo_index, merge_name_entries, 1));
+
+ git_commit_free(commit);
+ git_commit_free(head);
+}
+
+/* git revert --no-commit HEAD */
+void test_revert_workdir__head(void)
+{
+ git_reference *head;
+ git_commit *commit;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "7731926a337c4eaba1e2187d90ebfa0a93659382", 0, "file1.txt" },
+ { 0100644, "0ab09ea6d4c3634bdf6c221626d8b6f7dd890767", 0, "file2.txt" },
+ { 0100644, "f4e107c230d08a60fb419d19869f1f282b272d9c", 0, "file3.txt" },
+ { 0100644, "0f5bfcf58c558d865da6be0281d7795993646cee", 0, "file6.txt" },
+ };
+
+ /* HEAD is 2d440f2b3147d3dc7ad1085813478d6d869d5a4d */
+ cl_git_pass(git_repository_head(&head, repo));
+ cl_git_pass(git_reference_peel((git_object **)&commit, head, GIT_OBJECT_COMMIT));
+ cl_git_pass(git_reset(repo, (git_object *)commit, GIT_RESET_HARD, NULL));
+ cl_git_pass(git_revert(repo, commit, NULL));
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 4));
+ cl_assert(merge_test_workdir(repo, merge_index_entries, 4));
+
+ git_reference_free(head);
+ git_commit_free(commit);
+}
+
+void test_revert_workdir__nonmerge_fails_mainline_specified(void)
+{
+ git_reference *head;
+ git_commit *commit;
+ git_revert_options opts = GIT_REVERT_OPTIONS_INIT;
+
+ cl_git_pass(git_repository_head(&head, repo));
+ cl_git_pass(git_reference_peel((git_object **)&commit, head, GIT_OBJECT_COMMIT));
+
+ opts.mainline = 1;
+ cl_must_fail(git_revert(repo, commit, &opts));
+ cl_assert(!git_fs_path_exists(TEST_REPO_PATH "/.git/MERGE_MSG"));
+ cl_assert(!git_fs_path_exists(TEST_REPO_PATH "/.git/REVERT_HEAD"));
+
+ git_reference_free(head);
+ git_commit_free(commit);
+}
+
+/* git reset --hard 5acdc74af27172ec491d213ee36cea7eb9ef2579
+ * git revert HEAD */
+void test_revert_workdir__merge_fails_without_mainline_specified(void)
+{
+ git_commit *head;
+ git_oid head_oid;
+
+ git_oid__fromstr(&head_oid, "5acdc74af27172ec491d213ee36cea7eb9ef2579", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
+ cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL));
+
+ cl_must_fail(git_revert(repo, head, NULL));
+ cl_assert(!git_fs_path_exists(TEST_REPO_PATH "/.git/MERGE_MSG"));
+ cl_assert(!git_fs_path_exists(TEST_REPO_PATH "/.git/REVERT_HEAD"));
+
+ git_commit_free(head);
+}
+
+/* git reset --hard 5acdc74af27172ec491d213ee36cea7eb9ef2579
+ * git revert HEAD -m1 --no-commit */
+void test_revert_workdir__merge_first_parent(void)
+{
+ git_commit *head;
+ git_oid head_oid;
+ git_revert_options opts = GIT_REVERT_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "296a6d3be1dff05c5d1f631d2459389fa7b619eb", 0, "file-mainline.txt" },
+ { 0100644, "0cdb66192ee192f70f891f05a47636057420e871", 0, "file1.txt" },
+ { 0100644, "73ec36fa120f8066963a0bc9105bb273dbd903d7", 0, "file2.txt" },
+ };
+
+ opts.mainline = 1;
+
+ git_oid__fromstr(&head_oid, "5acdc74af27172ec491d213ee36cea7eb9ef2579", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
+ cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL));
+
+ cl_git_pass(git_revert(repo, head, &opts));
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 3));
+
+ git_commit_free(head);
+}
+
+void test_revert_workdir__merge_second_parent(void)
+{
+ git_commit *head;
+ git_oid head_oid;
+ git_revert_options opts = GIT_REVERT_OPTIONS_INIT;
+
+ struct merge_index_entry merge_index_entries[] = {
+ { 0100644, "33c6fd981c49a2abf2971482089350bfc5cda8ea", 0, "file-branch.txt" },
+ { 0100644, "0cdb66192ee192f70f891f05a47636057420e871", 0, "file1.txt" },
+ { 0100644, "73ec36fa120f8066963a0bc9105bb273dbd903d7", 0, "file2.txt" },
+ };
+
+ opts.mainline = 2;
+
+ git_oid__fromstr(&head_oid, "5acdc74af27172ec491d213ee36cea7eb9ef2579", GIT_OID_SHA1);
+ cl_git_pass(git_commit_lookup(&head, repo, &head_oid));
+ cl_git_pass(git_reset(repo, (git_object *)head, GIT_RESET_HARD, NULL));
+
+ cl_git_pass(git_revert(repo, head, &opts));
+
+ cl_assert(merge_test_index(repo_index, merge_index_entries, 3));
+
+ git_commit_free(head);
+}
diff --git a/tests/libgit2/revwalk/basic.c b/tests/libgit2/revwalk/basic.c
new file mode 100644
index 0000000..41090a1
--- /dev/null
+++ b/tests/libgit2/revwalk/basic.c
@@ -0,0 +1,644 @@
+#include "clar_libgit2.h"
+
+/*
+ * a4a7dce [0] Merge branch 'master' into br2
+ |\
+ | * 9fd738e [1] a fourth commit
+ | * 4a202b3 [2] a third commit
+ * | c47800c [3] branch commit one
+ |/
+ * 5b5b025 [5] another commit
+ * 8496071 [4] testing
+*/
+static const char *commit_head = "a4a7dce85cf63874e984719f4fdd239f5145052f";
+
+static const char *commit_ids[] = {
+ "a4a7dce85cf63874e984719f4fdd239f5145052f", /* 0 */
+ "9fd738e8f7967c078dceed8190330fc8648ee56a", /* 1 */
+ "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", /* 2 */
+ "c47800c7266a2be04c571c04d5a6614691ea99bd", /* 3 */
+ "8496071c1b46c854b31185ea97743be6a8774479", /* 4 */
+ "5b5b025afb0b4c913b4c338a42934a3863bf3644", /* 5 */
+};
+
+/* Careful: there are two possible topological sorts */
+static const int commit_sorting_topo[][6] = {
+ {0, 1, 2, 3, 5, 4}, {0, 3, 1, 2, 5, 4}
+};
+
+static const int commit_sorting_time[][6] = {
+ {0, 3, 1, 2, 5, 4}
+};
+
+static const int commit_sorting_topo_reverse[][6] = {
+ {4, 5, 3, 2, 1, 0}, {4, 5, 2, 1, 3, 0}
+};
+
+static const int commit_sorting_time_reverse[][6] = {
+ {4, 5, 2, 1, 3, 0}
+};
+
+/* This is specified unsorted, so both combinations are possible */
+static const int commit_sorting_segment[][6] = {
+ {1, 2, -1, -1, -1, -1}, {2, 1, -1, -1, -1, -1}
+};
+
+#define commit_count 6
+static const int result_bytes = 24;
+
+
+static int get_commit_index(git_oid *raw_oid)
+{
+ int i;
+ char oid[GIT_OID_SHA1_HEXSIZE];
+
+ git_oid_fmt(oid, raw_oid);
+
+ for (i = 0; i < commit_count; ++i)
+ if (memcmp(oid, commit_ids[i], GIT_OID_SHA1_HEXSIZE) == 0)
+ return i;
+
+ return -1;
+}
+
+static int test_walk_only(git_revwalk *walk,
+ const int possible_results[][commit_count], int results_count)
+{
+ git_oid oid;
+ int i;
+ int result_array[commit_count];
+
+ for (i = 0; i < commit_count; ++i)
+ result_array[i] = -1;
+
+ i = 0;
+ while (git_revwalk_next(&oid, walk) == 0) {
+ result_array[i++] = get_commit_index(&oid);
+ /*{
+ char str[GIT_OID_SHA1_HEXSIZE+1];
+ git_oid_fmt(str, &oid);
+ str[GIT_OID_SHA1_HEXSIZE] = 0;
+ printf(" %d) %s\n", i, str);
+ }*/
+ }
+
+ for (i = 0; i < results_count; ++i)
+ if (memcmp(possible_results[i],
+ result_array, result_bytes) == 0)
+ return 0;
+
+ return GIT_ERROR;
+}
+
+static int test_walk(git_revwalk *walk, const git_oid *root,
+ int flags, const int possible_results[][6], int results_count)
+{
+ git_revwalk_sorting(walk, flags);
+ git_revwalk_push(walk, root);
+
+ return test_walk_only(walk, possible_results, results_count);
+}
+
+static git_repository *_repo = NULL;
+static git_revwalk *_walk = NULL;
+static const char *_fixture = NULL;
+
+void test_revwalk_basic__initialize(void)
+{
+}
+
+void test_revwalk_basic__cleanup(void)
+{
+ git_revwalk_free(_walk);
+
+ if (_fixture)
+ cl_git_sandbox_cleanup();
+ else
+ git_repository_free(_repo);
+
+ _fixture = NULL;
+ _repo = NULL;
+ _walk = NULL;
+}
+
+static void revwalk_basic_setup_walk(const char *fixture)
+{
+ if (fixture) {
+ _fixture = fixture;
+ _repo = cl_git_sandbox_init(fixture);
+ } else {
+ cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git")));
+ }
+
+ cl_git_pass(git_revwalk_new(&_walk, _repo));
+}
+
+void test_revwalk_basic__sorting_modes(void)
+{
+ git_oid id;
+
+ revwalk_basic_setup_walk(NULL);
+
+ git_oid__fromstr(&id, commit_head, GIT_OID_SHA1);
+
+ cl_git_pass(test_walk(_walk, &id, GIT_SORT_TIME, commit_sorting_time, 1));
+ cl_git_pass(test_walk(_walk, &id, GIT_SORT_TOPOLOGICAL, commit_sorting_topo, 2));
+ cl_git_pass(test_walk(_walk, &id, GIT_SORT_TIME | GIT_SORT_REVERSE, commit_sorting_time_reverse, 1));
+ cl_git_pass(test_walk(_walk, &id, GIT_SORT_TOPOLOGICAL | GIT_SORT_REVERSE, commit_sorting_topo_reverse, 2));
+}
+
+void test_revwalk_basic__glob_heads(void)
+{
+ int i = 0;
+ git_oid oid;
+
+ revwalk_basic_setup_walk(NULL);
+
+ cl_git_pass(git_revwalk_push_glob(_walk, "heads"));
+
+ while (git_revwalk_next(&oid, _walk) == 0)
+ i++;
+
+ /* git log --branches --oneline | wc -l => 14 */
+ cl_assert_equal_i(i, 14);
+}
+
+void test_revwalk_basic__glob_heads_with_invalid(void)
+{
+ int i;
+ git_oid oid;
+
+ revwalk_basic_setup_walk("testrepo");
+
+ cl_git_mkfile("testrepo/.git/refs/heads/garbage", "not-a-ref");
+ cl_git_pass(git_revwalk_push_glob(_walk, "heads"));
+
+ for (i = 0; !git_revwalk_next(&oid, _walk); ++i)
+ /* walking */;
+
+ /* git log --branches --oneline | wc -l => 16 */
+ cl_assert_equal_i(20, i);
+}
+
+void test_revwalk_basic__glob_invalid_symbolic_ref(void)
+{
+ int i;
+ git_oid oid;
+
+ revwalk_basic_setup_walk("testrepo");
+
+ cl_git_mkfile("testrepo/.git/refs/heads/broken-sym-ref", "ref: refs/heads/does-not-exist");
+ cl_git_pass(git_revwalk_push_glob(_walk, "heads"));
+
+ for (i = 0; !git_revwalk_next(&oid, _walk); ++i)
+ /* walking */;
+
+ /* git log --branches --oneline | wc -l => 16 */
+ cl_assert_equal_i(20, i);
+}
+
+void test_revwalk_basic__push_head(void)
+{
+ int i = 0;
+ git_oid oid;
+
+ revwalk_basic_setup_walk(NULL);
+
+ cl_git_pass(git_revwalk_push_head(_walk));
+
+ while (git_revwalk_next(&oid, _walk) == 0) {
+ i++;
+ }
+
+ /* git log HEAD --oneline | wc -l => 7 */
+ cl_assert_equal_i(i, 7);
+}
+
+void test_revwalk_basic__sorted_after_reset(void)
+{
+ int i = 0;
+ git_oid oid;
+
+ revwalk_basic_setup_walk(NULL);
+
+ git_oid__fromstr(&oid, commit_head, GIT_OID_SHA1);
+
+ /* push, sort, and test the walk */
+ cl_git_pass(git_revwalk_push(_walk, &oid));
+ git_revwalk_sorting(_walk, GIT_SORT_TIME);
+
+ cl_git_pass(test_walk_only(_walk, commit_sorting_time, 2));
+
+ /* reset, push, and test again - we should see all entries */
+ git_revwalk_reset(_walk);
+ cl_git_pass(git_revwalk_push(_walk, &oid));
+
+ while (git_revwalk_next(&oid, _walk) == 0)
+ i++;
+
+ cl_assert_equal_i(i, commit_count);
+}
+
+void test_revwalk_basic__push_head_hide_ref(void)
+{
+ int i = 0;
+ git_oid oid;
+
+ revwalk_basic_setup_walk(NULL);
+
+ cl_git_pass(git_revwalk_push_head(_walk));
+ cl_git_pass(git_revwalk_hide_ref(_walk, "refs/heads/packed-test"));
+
+ while (git_revwalk_next(&oid, _walk) == 0) {
+ i++;
+ }
+
+ /* git log HEAD --oneline --not refs/heads/packed-test | wc -l => 4 */
+ cl_assert_equal_i(i, 4);
+}
+
+void test_revwalk_basic__push_head_hide_ref_nobase(void)
+{
+ int i = 0;
+ git_oid oid;
+
+ revwalk_basic_setup_walk(NULL);
+
+ cl_git_pass(git_revwalk_push_head(_walk));
+ cl_git_pass(git_revwalk_hide_ref(_walk, "refs/heads/packed"));
+
+ while (git_revwalk_next(&oid, _walk) == 0) {
+ i++;
+ }
+
+ /* git log HEAD --oneline --not refs/heads/packed | wc -l => 7 */
+ cl_assert_equal_i(i, 7);
+}
+
+/*
+* $ git rev-list HEAD 5b5b02 ^refs/heads/packed-test
+* a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+* be3563ae3f795b2b4353bcce3a527ad0a4f7f644
+* c47800c7266a2be04c571c04d5a6614691ea99bd
+* 9fd738e8f7967c078dceed8190330fc8648ee56a
+
+* $ git log HEAD 5b5b02 --oneline --not refs/heads/packed-test | wc -l => 4
+* a65fedf
+* be3563a Merge branch 'br2'
+* c47800c branch commit one
+* 9fd738e a fourth commit
+*/
+void test_revwalk_basic__multiple_push_1(void)
+{
+ int i = 0;
+ git_oid oid;
+
+ revwalk_basic_setup_walk(NULL);
+
+ cl_git_pass(git_revwalk_push_head(_walk));
+
+ cl_git_pass(git_revwalk_hide_ref(_walk, "refs/heads/packed-test"));
+
+ cl_git_pass(git_oid__fromstr(&oid, "5b5b025afb0b4c913b4c338a42934a3863bf3644", GIT_OID_SHA1));
+ cl_git_pass(git_revwalk_push(_walk, &oid));
+
+ while (git_revwalk_next(&oid, _walk) == 0)
+ i++;
+
+ cl_assert_equal_i(i, 4);
+}
+
+/*
+* Difference between test_revwalk_basic__multiple_push_1 and
+* test_revwalk_basic__multiple_push_2 is in the order reference
+* refs/heads/packed-test and commit 5b5b02 are pushed.
+* revwalk should return same commits in both the tests.
+
+* $ git rev-list 5b5b02 HEAD ^refs/heads/packed-test
+* a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+* be3563ae3f795b2b4353bcce3a527ad0a4f7f644
+* c47800c7266a2be04c571c04d5a6614691ea99bd
+* 9fd738e8f7967c078dceed8190330fc8648ee56a
+
+* $ git log 5b5b02 HEAD --oneline --not refs/heads/packed-test | wc -l => 4
+* a65fedf
+* be3563a Merge branch 'br2'
+* c47800c branch commit one
+* 9fd738e a fourth commit
+*/
+void test_revwalk_basic__multiple_push_2(void)
+{
+ int i = 0;
+ git_oid oid;
+
+ revwalk_basic_setup_walk(NULL);
+
+ cl_git_pass(git_oid__fromstr(&oid, "5b5b025afb0b4c913b4c338a42934a3863bf3644", GIT_OID_SHA1));
+ cl_git_pass(git_revwalk_push(_walk, &oid));
+
+ cl_git_pass(git_revwalk_hide_ref(_walk, "refs/heads/packed-test"));
+
+ cl_git_pass(git_revwalk_push_head(_walk));
+
+ while (git_revwalk_next(&oid, _walk) == 0)
+ i++;
+
+ cl_assert_equal_i(i, 4);
+}
+
+void test_revwalk_basic__disallow_non_commit(void)
+{
+ git_oid oid;
+
+ revwalk_basic_setup_walk(NULL);
+
+ cl_git_pass(git_oid__fromstr(&oid, "521d87c1ec3aef9824daf6d96cc0ae3710766d91", GIT_OID_SHA1));
+ cl_git_fail(git_revwalk_push(_walk, &oid));
+}
+
+void test_revwalk_basic__hide_then_push(void)
+{
+ git_oid oid;
+ int i = 0;
+
+ revwalk_basic_setup_walk(NULL);
+ cl_git_pass(git_oid__fromstr(&oid, "5b5b025afb0b4c913b4c338a42934a3863bf3644", GIT_OID_SHA1));
+
+ cl_git_pass(git_revwalk_hide(_walk, &oid));
+ cl_git_pass(git_revwalk_push(_walk, &oid));
+
+ while (git_revwalk_next(&oid, _walk) == 0)
+ i++;
+
+ cl_assert_equal_i(i, 0);
+}
+
+void test_revwalk_basic__topo_crash(void)
+{
+ git_oid oid;
+ git_oid__fromstr(&oid, "5b5b025afb0b4c913b4c338a42934a3863bf3644", GIT_OID_SHA1);
+
+ revwalk_basic_setup_walk(NULL);
+ git_revwalk_sorting(_walk, GIT_SORT_TOPOLOGICAL);
+
+ cl_git_pass(git_revwalk_push(_walk, &oid));
+ cl_git_pass(git_revwalk_hide(_walk, &oid));
+
+ git_revwalk_next(&oid, _walk);
+}
+
+void test_revwalk_basic__from_new_to_old(void)
+{
+ git_oid from_oid, to_oid, oid;
+ int i = 0;
+
+ revwalk_basic_setup_walk(NULL);
+ git_revwalk_sorting(_walk, GIT_SORT_TIME);
+
+ cl_git_pass(git_oid__fromstr(&to_oid, "5b5b025afb0b4c913b4c338a42934a3863bf3644", GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&from_oid, "a4a7dce85cf63874e984719f4fdd239f5145052f", GIT_OID_SHA1));
+
+ cl_git_pass(git_revwalk_push(_walk, &to_oid));
+ cl_git_pass(git_revwalk_hide(_walk, &from_oid));
+
+ while (git_revwalk_next(&oid, _walk) == 0)
+ i++;
+
+ cl_assert_equal_i(i, 0);
+}
+
+void test_revwalk_basic__push_range(void)
+{
+ revwalk_basic_setup_walk(NULL);
+
+ git_revwalk_reset(_walk);
+ git_revwalk_sorting(_walk, 0);
+ cl_git_pass(git_revwalk_push_range(_walk, "9fd738e~2..9fd738e"));
+ cl_git_pass(test_walk_only(_walk, commit_sorting_segment, 2));
+}
+
+void test_revwalk_basic__push_range_merge_base(void)
+{
+ revwalk_basic_setup_walk(NULL);
+
+ git_revwalk_reset(_walk);
+ git_revwalk_sorting(_walk, 0);
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_revwalk_push_range(_walk, "HEAD...HEAD~2"));
+}
+
+void test_revwalk_basic__push_range_no_range(void)
+{
+ revwalk_basic_setup_walk(NULL);
+
+ git_revwalk_reset(_walk);
+ git_revwalk_sorting(_walk, 0);
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_revwalk_push_range(_walk, "HEAD"));
+}
+
+void test_revwalk_basic__push_mixed(void)
+{
+ git_oid oid;
+ int i = 0;
+
+ revwalk_basic_setup_walk(NULL);
+
+ git_revwalk_reset(_walk);
+ git_revwalk_sorting(_walk, 0);
+ cl_git_pass(git_revwalk_push_glob(_walk, "tags"));
+
+ while (git_revwalk_next(&oid, _walk) == 0) {
+ i++;
+ }
+
+ /* git rev-list --count --glob=tags #=> 9 */
+ cl_assert_equal_i(9, i);
+}
+
+void test_revwalk_basic__push_all(void)
+{
+ git_oid oid;
+ int i = 0;
+
+ revwalk_basic_setup_walk(NULL);
+
+ git_revwalk_reset(_walk);
+ git_revwalk_sorting(_walk, 0);
+ cl_git_pass(git_revwalk_push_glob(_walk, "*"));
+
+ while (git_revwalk_next(&oid, _walk) == 0) {
+ i++;
+ }
+
+ /* git rev-list --count --all #=> 15 */
+ cl_assert_equal_i(15, i);
+}
+
+/*
+* $ git rev-list br2 master e908
+* a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+* e90810b8df3e80c413d903f631643c716887138d
+* 6dcf9bf7541ee10456529833502442f385010c3d
+* a4a7dce85cf63874e984719f4fdd239f5145052f
+* be3563ae3f795b2b4353bcce3a527ad0a4f7f644
+* c47800c7266a2be04c571c04d5a6614691ea99bd
+* 9fd738e8f7967c078dceed8190330fc8648ee56a
+* 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
+* 5b5b025afb0b4c913b4c338a42934a3863bf3644
+* 8496071c1b46c854b31185ea97743be6a8774479
+*/
+
+void test_revwalk_basic__mimic_git_rev_list(void)
+{
+ git_oid oid;
+
+ revwalk_basic_setup_walk(NULL);
+ git_revwalk_sorting(_walk, GIT_SORT_TIME);
+
+ cl_git_pass(git_revwalk_push_ref(_walk, "refs/heads/br2"));
+ cl_git_pass(git_revwalk_push_ref(_walk, "refs/heads/master"));
+ cl_git_pass(git_oid__fromstr(&oid, "e90810b8df3e80c413d903f631643c716887138d", GIT_OID_SHA1));
+ cl_git_pass(git_revwalk_push(_walk, &oid));
+
+ cl_git_pass(git_revwalk_next(&oid, _walk));
+ cl_assert(!git_oid_streq(&oid, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750"));
+
+ cl_git_pass(git_revwalk_next(&oid, _walk));
+ cl_assert(!git_oid_streq(&oid, "e90810b8df3e80c413d903f631643c716887138d"));
+
+ cl_git_pass(git_revwalk_next(&oid, _walk));
+ cl_assert(!git_oid_streq(&oid, "6dcf9bf7541ee10456529833502442f385010c3d"));
+
+ cl_git_pass(git_revwalk_next(&oid, _walk));
+ cl_assert(!git_oid_streq(&oid, "a4a7dce85cf63874e984719f4fdd239f5145052f"));
+
+ cl_git_pass(git_revwalk_next(&oid, _walk));
+ cl_assert(!git_oid_streq(&oid, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
+
+ cl_git_pass(git_revwalk_next(&oid, _walk));
+ cl_assert(!git_oid_streq(&oid, "c47800c7266a2be04c571c04d5a6614691ea99bd"));
+
+ cl_git_pass(git_revwalk_next(&oid, _walk));
+ cl_assert(!git_oid_streq(&oid, "9fd738e8f7967c078dceed8190330fc8648ee56a"));
+
+ cl_git_pass(git_revwalk_next(&oid, _walk));
+ cl_assert(!git_oid_streq(&oid, "4a202b346bb0fb0db7eff3cffeb3c70babbd2045"));
+
+ cl_git_pass(git_revwalk_next(&oid, _walk));
+ cl_assert(!git_oid_streq(&oid, "5b5b025afb0b4c913b4c338a42934a3863bf3644"));
+
+ cl_git_pass(git_revwalk_next(&oid, _walk));
+ cl_assert(!git_oid_streq(&oid, "8496071c1b46c854b31185ea97743be6a8774479"));
+
+ cl_git_fail_with(git_revwalk_next(&oid, _walk), GIT_ITEROVER);
+}
+
+void test_revwalk_basic__big_timestamp(void)
+{
+ git_reference *head;
+ git_commit *tip;
+ git_signature *sig;
+ git_tree *tree;
+ git_oid id;
+ int error;
+
+ revwalk_basic_setup_walk("testrepo.git");
+
+ cl_git_pass(git_repository_head(&head, _repo));
+ cl_git_pass(git_reference_peel((git_object **) &tip, head, GIT_OBJECT_COMMIT));
+
+ /* Commit with a far-ahead timestamp, we should be able to parse it in the revwalk */
+ cl_git_pass(git_signature_new(&sig, "Joe", "joe@example.com", INT64_C(2399662595), 0));
+ cl_git_pass(git_commit_tree(&tree, tip));
+
+ cl_git_pass(git_commit_create(&id, _repo, "HEAD", sig, sig, NULL, "some message", tree, 1,
+ (const git_commit **)&tip));
+
+ cl_git_pass(git_revwalk_push_head(_walk));
+
+ while ((error = git_revwalk_next(&id, _walk)) == 0) {
+ /* nothing */
+ }
+
+ cl_assert_equal_i(GIT_ITEROVER, error);
+
+ git_tree_free(tree);
+ git_commit_free(tip);
+ git_reference_free(head);
+ git_signature_free(sig);
+
+}
+
+/* Ensure that we correctly hide a commit that is (timewise) older
+ * than the commits that we are showing.
+ *
+ * % git rev-list 8e73b76..bd75801
+ * bd758010071961f28336333bc41e9c64c9a64866
+ */
+void test_revwalk_basic__old_hidden_commit_one(void)
+{
+ git_oid new_id, old_id, oid;
+
+ revwalk_basic_setup_walk("testrepo.git");
+
+ cl_git_pass(git_oid__fromstr(&new_id, "bd758010071961f28336333bc41e9c64c9a64866", GIT_OID_SHA1));
+ cl_git_pass(git_revwalk_push(_walk, &new_id));
+
+ cl_git_pass(git_oid__fromstr(&old_id, "8e73b769e97678d684b809b163bebdae2911720f", GIT_OID_SHA1));
+ cl_git_pass(git_revwalk_hide(_walk, &old_id));
+
+ cl_git_pass(git_revwalk_next(&oid, _walk));
+ cl_assert(!git_oid_streq(&oid, "bd758010071961f28336333bc41e9c64c9a64866"));
+
+ cl_git_fail_with(GIT_ITEROVER, git_revwalk_next(&oid, _walk));
+}
+
+/* Ensure that we correctly hide a commit that is (timewise) older
+ * than the commits that we are showing.
+ *
+ * % git rev-list bd75801 ^b91e763
+ * bd758010071961f28336333bc41e9c64c9a64866
+ */
+void test_revwalk_basic__old_hidden_commit_two(void)
+{
+ git_oid new_id, old_id, oid;
+
+ revwalk_basic_setup_walk("testrepo.git");
+
+ cl_git_pass(git_oid__fromstr(&new_id, "bd758010071961f28336333bc41e9c64c9a64866", GIT_OID_SHA1));
+ cl_git_pass(git_revwalk_push(_walk, &new_id));
+
+ cl_git_pass(git_oid__fromstr(&old_id, "b91e763008b10db366442469339f90a2b8400d0a", GIT_OID_SHA1));
+ cl_git_pass(git_revwalk_hide(_walk, &old_id));
+
+ cl_git_pass(git_revwalk_next(&oid, _walk));
+ cl_assert(!git_oid_streq(&oid, "bd758010071961f28336333bc41e9c64c9a64866"));
+
+ cl_git_fail_with(GIT_ITEROVER, git_revwalk_next(&oid, _walk));
+}
+
+/*
+ * Ensure that we correctly hide all parent commits of a newer
+ * commit when first hiding older commits.
+ *
+ * % git rev-list D ^B ^A ^E
+ * 790ba0facf6fd103699a5c40cd19dad277ff49cd
+ * b82cee5004151ae0c4f82b69fb71b87477664b6f
+ */
+void test_revwalk_basic__newer_hidden_commit_hides_old_commits(void)
+{
+ git_oid oid;
+
+ revwalk_basic_setup_walk("revwalk.git");
+
+ cl_git_pass(git_revwalk_push_ref(_walk, "refs/heads/D"));
+ cl_git_pass(git_revwalk_hide_ref(_walk, "refs/heads/B"));
+ cl_git_pass(git_revwalk_hide_ref(_walk, "refs/heads/A"));
+ cl_git_pass(git_revwalk_hide_ref(_walk, "refs/heads/E"));
+
+ cl_git_pass(git_revwalk_next(&oid, _walk));
+ cl_assert(git_oid_streq(&oid, "b82cee5004151ae0c4f82b69fb71b87477664b6f"));
+ cl_git_pass(git_revwalk_next(&oid, _walk));
+ cl_assert(git_oid_streq(&oid, "790ba0facf6fd103699a5c40cd19dad277ff49cd"));
+
+ cl_git_fail_with(GIT_ITEROVER, git_revwalk_next(&oid, _walk));
+}
diff --git a/tests/libgit2/revwalk/hidecb.c b/tests/libgit2/revwalk/hidecb.c
new file mode 100644
index 0000000..2100a54
--- /dev/null
+++ b/tests/libgit2/revwalk/hidecb.c
@@ -0,0 +1,230 @@
+#include "clar_libgit2.h"
+/*
+* a4a7dce [0] Merge branch 'master' into br2
+|\
+| * 9fd738e [1] a fourth commit
+| * 4a202b3 [2] a third commit
+* | c47800c [3] branch commit one
+|/
+* 5b5b025 [5] another commit
+* 8496071 [4] testing
+*/
+static const char *commit_head = "a4a7dce85cf63874e984719f4fdd239f5145052f";
+
+static const char *commit_strs[] = {
+ "a4a7dce85cf63874e984719f4fdd239f5145052f", /* 0 */
+ "9fd738e8f7967c078dceed8190330fc8648ee56a", /* 1 */
+ "4a202b346bb0fb0db7eff3cffeb3c70babbd2045", /* 2 */
+ "c47800c7266a2be04c571c04d5a6614691ea99bd", /* 3 */
+ "8496071c1b46c854b31185ea97743be6a8774479", /* 4 */
+ "5b5b025afb0b4c913b4c338a42934a3863bf3644", /* 5 */
+};
+
+#define commit_count 6
+
+static git_oid commit_ids[commit_count];
+static git_oid _head_id;
+static git_repository *_repo;
+
+
+void test_revwalk_hidecb__initialize(void)
+{
+ int i;
+
+ cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git")));
+ cl_git_pass(git_oid__fromstr(&_head_id, commit_head, GIT_OID_SHA1));
+
+ for (i = 0; i < commit_count; i++)
+ cl_git_pass(git_oid__fromstr(&commit_ids[i], commit_strs[i], GIT_OID_SHA1));
+
+}
+
+void test_revwalk_hidecb__cleanup(void)
+{
+ git_repository_free(_repo);
+ _repo = NULL;
+}
+
+/* Hide all commits */
+static int hide_every_commit_cb(const git_oid *commit_id, void *data)
+{
+ GIT_UNUSED(commit_id);
+ GIT_UNUSED(data);
+
+ return 1;
+}
+
+/* Do not hide anything */
+static int hide_none_cb(const git_oid *commit_id, void *data)
+{
+ GIT_UNUSED(commit_id);
+ GIT_UNUSED(data);
+
+ return 0;
+}
+
+/* Hide some commits */
+static int hide_commit_cb(const git_oid *commit_id, void *data)
+{
+ GIT_UNUSED(commit_id);
+ GIT_UNUSED(data);
+
+ return (git_oid_cmp(commit_id, &commit_ids[5]) == 0);
+}
+
+/* In payload data, pointer to a commit id is passed */
+static int hide_commit_use_payload_cb(const git_oid *commit_id, void *data)
+{
+ git_oid *hide_commit_id = data;
+
+ return (git_oid_cmp(commit_id, hide_commit_id) == 0);
+}
+
+void test_revwalk_hidecb__hide_all_cb(void)
+{
+ git_revwalk *walk;
+ git_oid id;
+
+ cl_git_pass(git_revwalk_new(&walk, _repo));
+ cl_git_pass(git_revwalk_add_hide_cb(walk, hide_every_commit_cb, NULL));
+ cl_git_pass(git_revwalk_push(walk, &_head_id));
+
+ /* First call to git_revwalk_next should return GIT_ITEROVER */
+ cl_assert_equal_i(GIT_ITEROVER, git_revwalk_next(&id, walk));
+
+ git_revwalk_free(walk);
+}
+
+
+void test_revwalk_hidecb__hide_none_cb(void)
+{
+ git_revwalk *walk;
+ int i, error;
+ git_oid id;
+
+ cl_git_pass(git_revwalk_new(&walk, _repo));
+ cl_git_pass(git_revwalk_add_hide_cb(walk, hide_none_cb, NULL));
+ cl_git_pass(git_revwalk_push(walk, &_head_id));
+
+ /* It should return all 6 commits */
+ i = 0;
+ while ((error = git_revwalk_next(&id, walk)) == 0)
+ i++;
+
+ cl_assert_equal_i(i, 6);
+ cl_assert_equal_i(error, GIT_ITEROVER);
+
+ git_revwalk_free(walk);
+}
+
+void test_revwalk_hidecb__unset_cb_before_walk(void)
+{
+ git_revwalk *walk;
+ git_oid id;
+ int i, error;
+
+ cl_git_pass(git_revwalk_new(&walk, _repo));
+ cl_git_pass(git_revwalk_add_hide_cb(walk, hide_every_commit_cb, NULL));
+ cl_git_pass(git_revwalk_add_hide_cb(walk, NULL, NULL));
+ cl_git_pass(git_revwalk_push(walk, &_head_id));
+
+ /* It should return all 6 commits */
+ i = 0;
+ while ((error = git_revwalk_next(&id, walk)) == 0)
+ i++;
+
+ cl_assert_equal_i(i, 6);
+ cl_assert_equal_i(error, GIT_ITEROVER);
+
+ git_revwalk_free(walk);
+}
+
+void test_revwalk_hidecb__change_cb_before_walk(void)
+{
+ git_revwalk *walk;
+ git_oid id;
+
+ cl_git_pass(git_revwalk_new(&walk, _repo));
+ cl_git_pass(git_revwalk_add_hide_cb(walk, hide_none_cb, NULL));
+ cl_git_pass(git_revwalk_add_hide_cb(walk, hide_every_commit_cb, NULL));
+ cl_git_pass(git_revwalk_push(walk, &_head_id));
+
+ /* First call to git_revwalk_next should return GIT_ITEROVER */
+ cl_assert_equal_i(GIT_ITEROVER, git_revwalk_next(&id, walk));
+
+ git_revwalk_free(walk);
+}
+
+void test_revwalk_hidecb__add_hide_cb_during_walking(void)
+{
+ git_revwalk *walk;
+ git_oid id;
+ int error;
+
+ cl_git_pass(git_revwalk_new(&walk, _repo));
+ cl_git_pass(git_revwalk_push(walk, &_head_id));
+
+ /* Start walking without adding hide callback */
+ cl_git_pass(git_revwalk_next(&id, walk));
+
+ /* Now add hide callback */
+ cl_git_pass(git_revwalk_add_hide_cb(walk, hide_none_cb, NULL));
+
+ /* walk should be reset */
+ error = git_revwalk_next(&id, walk);
+ cl_assert_equal_i(error, GIT_ITEROVER);
+
+ git_revwalk_free(walk);
+}
+
+void test_revwalk_hidecb__hide_some_commits(void)
+{
+ git_revwalk *walk;
+ git_oid id;
+ int i, error;
+
+ cl_git_pass(git_revwalk_new(&walk, _repo));
+ cl_git_pass(git_revwalk_push(walk, &_head_id));
+ git_revwalk_sorting(walk, GIT_SORT_TOPOLOGICAL);
+
+ /* Add hide callback */
+ cl_git_pass(git_revwalk_add_hide_cb(walk, hide_commit_cb, NULL));
+
+ i = 0;
+ while ((error = git_revwalk_next(&id, walk)) == 0) {
+ cl_assert_equal_oid(&commit_ids[i], &id);
+ i++;
+ }
+
+ cl_assert_equal_i(i, 4);
+ cl_assert_equal_i(error, GIT_ITEROVER);
+
+ git_revwalk_free(walk);
+}
+
+void test_revwalk_hidecb__test_payload(void)
+{
+ git_revwalk *walk;
+ git_oid id;
+ int i, error;
+
+ cl_git_pass(git_revwalk_new(&walk, _repo));
+ cl_git_pass(git_revwalk_push(walk, &_head_id));
+ git_revwalk_sorting(walk, GIT_SORT_TOPOLOGICAL);
+
+ /* Add hide callback, pass id of parent of initial commit as payload data */
+ cl_git_pass(git_revwalk_add_hide_cb(walk, hide_commit_use_payload_cb, &commit_ids[5]));
+
+ i = 0;
+ while ((error = git_revwalk_next(&id, walk)) == 0) {
+ cl_assert_equal_oid(&commit_ids[i], &id);
+ i++;
+ }
+
+ /* walker should return four commits */
+ cl_assert_equal_i(i, 4);
+ cl_assert_equal_i(error, GIT_ITEROVER);
+
+ git_revwalk_free(walk);
+}
+
diff --git a/tests/libgit2/revwalk/mergebase.c b/tests/libgit2/revwalk/mergebase.c
new file mode 100644
index 0000000..d413a1f
--- /dev/null
+++ b/tests/libgit2/revwalk/mergebase.c
@@ -0,0 +1,514 @@
+#include "clar_libgit2.h"
+#include "vector.h"
+#include <stdarg.h>
+
+static git_repository *_repo;
+static git_repository *_repo2;
+
+void test_revwalk_mergebase__initialize(void)
+{
+ cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git")));
+ cl_git_pass(git_repository_open(&_repo2, cl_fixture("twowaymerge.git")));
+}
+
+void test_revwalk_mergebase__cleanup(void)
+{
+ git_repository_free(_repo);
+ _repo = NULL;
+
+ git_repository_free(_repo2);
+ _repo2 = NULL;
+}
+
+void test_revwalk_mergebase__single1(void)
+{
+ git_oid result, one, two, expected;
+ size_t ahead, behind;
+
+ cl_git_pass(git_oid__fromstr(&one, "c47800c7266a2be04c571c04d5a6614691ea99bd ", GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&two, "9fd738e8f7967c078dceed8190330fc8648ee56a", GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&expected, "5b5b025afb0b4c913b4c338a42934a3863bf3644", GIT_OID_SHA1));
+
+ cl_git_pass(git_merge_base(&result, _repo, &one, &two));
+ cl_assert_equal_oid(&expected, &result);
+
+ cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo, &one, &two));
+ cl_assert_equal_sz(ahead, 1);
+ cl_assert_equal_sz(behind, 2);
+
+ cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo, &two, &one));
+ cl_assert_equal_sz(ahead, 2);
+ cl_assert_equal_sz(behind, 1);
+}
+
+void test_revwalk_mergebase__single2(void)
+{
+ git_oid result, one, two, expected;
+ size_t ahead, behind;
+
+ cl_git_pass(git_oid__fromstr(&one, "763d71aadf09a7951596c9746c024e7eece7c7af", GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&two, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&expected, "c47800c7266a2be04c571c04d5a6614691ea99bd", GIT_OID_SHA1));
+
+ cl_git_pass(git_merge_base(&result, _repo, &one, &two));
+ cl_assert_equal_oid(&expected, &result);
+
+ cl_git_pass(git_graph_ahead_behind( &ahead, &behind, _repo, &one, &two));
+ cl_assert_equal_sz(ahead, 1);
+ cl_assert_equal_sz(behind, 4);
+
+ cl_git_pass(git_graph_ahead_behind( &ahead, &behind, _repo, &two, &one));
+ cl_assert_equal_sz(ahead, 4);
+ cl_assert_equal_sz(behind, 1);
+}
+
+void test_revwalk_mergebase__merged_branch(void)
+{
+ git_oid result, one, two, expected;
+ size_t ahead, behind;
+
+ cl_git_pass(git_oid__fromstr(&one, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750", GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&two, "9fd738e8f7967c078dceed8190330fc8648ee56a", GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&expected, "9fd738e8f7967c078dceed8190330fc8648ee56a", GIT_OID_SHA1));
+
+ cl_git_pass(git_merge_base(&result, _repo, &one, &two));
+ cl_assert_equal_oid(&expected, &result);
+
+ cl_git_pass(git_merge_base(&result, _repo, &two, &one));
+ cl_assert_equal_oid(&expected, &result);
+
+ cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo, &one, &two));
+ cl_assert_equal_sz(ahead, 3);
+ cl_assert_equal_sz(behind, 0);
+
+ cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo, &two, &one));
+ cl_assert_equal_sz(ahead, 0);
+ cl_assert_equal_sz(behind, 3);
+}
+
+void test_revwalk_mergebase__two_way_merge(void)
+{
+ git_oid one, two;
+ size_t ahead, behind;
+
+ cl_git_pass(git_oid__fromstr(&one, "9b219343610c88a1187c996d0dc58330b55cee28", GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&two, "a953a018c5b10b20c86e69fef55ebc8ad4c5a417", GIT_OID_SHA1));
+ cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo2, &one, &two));
+
+ cl_assert_equal_sz(ahead, 8);
+ cl_assert_equal_sz(behind, 2);
+
+ cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo2, &two, &one));
+
+ cl_assert_equal_sz(ahead, 2);
+ cl_assert_equal_sz(behind, 8);
+}
+
+void test_revwalk_mergebase__no_common_ancestor_returns_ENOTFOUND(void)
+{
+ git_oid result, one, two;
+ size_t ahead, behind;
+ int error;
+
+ cl_git_pass(git_oid__fromstr(&one, "763d71aadf09a7951596c9746c024e7eece7c7af", GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&two, "e90810b8df3e80c413d903f631643c716887138d", GIT_OID_SHA1));
+
+ error = git_merge_base(&result, _repo, &one, &two);
+ cl_git_fail(error);
+
+ cl_assert_equal_i(GIT_ENOTFOUND, error);
+
+ cl_git_pass(git_graph_ahead_behind(&ahead, &behind, _repo, &one, &two));
+ cl_assert_equal_sz(4, ahead);
+ cl_assert_equal_sz(2, behind);
+}
+
+void test_revwalk_mergebase__prefer_youngest_merge_base(void)
+{
+ git_oid result, one, two, expected;
+
+ cl_git_pass(git_oid__fromstr(&one, "a4a7dce85cf63874e984719f4fdd239f5145052f", GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&two, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644", GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&expected, "c47800c7266a2be04c571c04d5a6614691ea99bd", GIT_OID_SHA1));
+
+ cl_git_pass(git_merge_base(&result, _repo, &one, &two));
+ cl_assert_equal_oid(&expected, &result);
+}
+
+void test_revwalk_mergebase__multiple_merge_bases(void)
+{
+ git_oid one, two, expected1, expected2;
+ git_oidarray result = {NULL, 0};
+
+ cl_git_pass(git_oid__fromstr(&one, "a4a7dce85cf63874e984719f4fdd239f5145052f", GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&two, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644", GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&expected1, "c47800c7266a2be04c571c04d5a6614691ea99bd", GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&expected2, "9fd738e8f7967c078dceed8190330fc8648ee56a", GIT_OID_SHA1));
+
+ cl_git_pass(git_merge_bases(&result, _repo, &one, &two));
+ cl_assert_equal_i(2, result.count);
+ cl_assert_equal_oid(&expected1, &result.ids[0]);
+ cl_assert_equal_oid(&expected2, &result.ids[1]);
+
+ git_oidarray_dispose(&result);
+}
+
+void test_revwalk_mergebase__multiple_merge_bases_many_commits(void)
+{
+ git_oid expected1, expected2;
+ git_oidarray result = {NULL, 0};
+
+ git_oid *input = git__malloc(sizeof(git_oid) * 2);
+
+ cl_git_pass(git_oid__fromstr(&input[0], "a4a7dce85cf63874e984719f4fdd239f5145052f", GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&input[1], "be3563ae3f795b2b4353bcce3a527ad0a4f7f644", GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&expected1, "c47800c7266a2be04c571c04d5a6614691ea99bd", GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&expected2, "9fd738e8f7967c078dceed8190330fc8648ee56a", GIT_OID_SHA1));
+
+ cl_git_pass(git_merge_bases_many(&result, _repo, 2, input));
+ cl_assert_equal_i(2, result.count);
+ cl_assert_equal_oid(&expected1, &result.ids[0]);
+ cl_assert_equal_oid(&expected2, &result.ids[1]);
+
+ git_oidarray_dispose(&result);
+ git__free(input);
+}
+
+void test_revwalk_mergebase__no_off_by_one_missing(void)
+{
+ git_oid result, one, two;
+
+ cl_git_pass(git_oid__fromstr(&one, "1a443023183e3f2bfbef8ac923cd81c1018a18fd", GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&two, "9f13f7d0a9402c681f91dc590cf7b5470e6a77d2", GIT_OID_SHA1));
+ cl_git_pass(git_merge_base(&result, _repo, &one, &two));
+}
+
+static void assert_mergebase_many(const char *expected_sha, int count, ...)
+{
+ va_list ap;
+ int i;
+ git_oid *oids;
+ git_oid oid, expected;
+ char *partial_oid;
+ git_object *object;
+
+ oids = git__malloc(count * sizeof(git_oid));
+ cl_assert(oids != NULL);
+
+ memset(oids, 0x0, count * sizeof(git_oid));
+
+ va_start(ap, count);
+
+ for (i = 0; i < count; ++i) {
+ partial_oid = va_arg(ap, char *);
+ cl_git_pass(git_oid__fromstrn(&oid, partial_oid, strlen(partial_oid), GIT_OID_SHA1));
+
+ cl_git_pass(git_object_lookup_prefix(&object, _repo, &oid, strlen(partial_oid), GIT_OBJECT_COMMIT));
+ git_oid_cpy(&oids[i], git_object_id(object));
+ git_object_free(object);
+ }
+
+ va_end(ap);
+
+ if (expected_sha == NULL)
+ cl_assert_equal_i(GIT_ENOTFOUND, git_merge_base_many(&oid, _repo, count, oids));
+ else {
+ cl_git_pass(git_merge_base_many(&oid, _repo, count, oids));
+ cl_git_pass(git_oid__fromstr(&expected, expected_sha, GIT_OID_SHA1));
+
+ cl_assert_equal_oid(&expected, &oid);
+ }
+
+ git__free(oids);
+}
+
+void test_revwalk_mergebase__many_no_common_ancestor_returns_ENOTFOUND(void)
+{
+ assert_mergebase_many(NULL, 3, "41bc8c", "e90810", "a65fed");
+ assert_mergebase_many(NULL, 3, "e90810", "41bc8c", "a65fed");
+ assert_mergebase_many(NULL, 3, "e90810", "a65fed", "41bc8c");
+ assert_mergebase_many(NULL, 3, "a65fed", "e90810", "41bc8c");
+ assert_mergebase_many(NULL, 3, "a65fed", "41bc8c", "e90810");
+
+ assert_mergebase_many(NULL, 3, "e90810", "763d71", "a65fed");
+}
+
+void test_revwalk_mergebase__many_merge_branch(void)
+{
+ assert_mergebase_many("c47800c7266a2be04c571c04d5a6614691ea99bd", 3, "a65fed", "763d71", "849607");
+
+ assert_mergebase_many("c47800c7266a2be04c571c04d5a6614691ea99bd", 3, "763d71", "e90810", "a65fed");
+ assert_mergebase_many("c47800c7266a2be04c571c04d5a6614691ea99bd", 3, "763d71", "a65fed", "e90810");
+
+ assert_mergebase_many("c47800c7266a2be04c571c04d5a6614691ea99bd", 3, "a65fed", "763d71", "849607");
+ assert_mergebase_many("c47800c7266a2be04c571c04d5a6614691ea99bd", 3, "a65fed", "849607", "763d71");
+ assert_mergebase_many("8496071c1b46c854b31185ea97743be6a8774479", 3, "849607", "a65fed", "763d71");
+
+ assert_mergebase_many("5b5b025afb0b4c913b4c338a42934a3863bf3644", 5, "5b5b02", "763d71", "a4a7dc", "a65fed", "41bc8c");
+}
+
+static void assert_mergebase_octopus(const char *expected_sha, int count, ...)
+{
+ va_list ap;
+ int i;
+ git_oid *oids;
+ git_oid oid, expected;
+ char *partial_oid;
+ git_object *object;
+
+ oids = git__malloc(count * sizeof(git_oid));
+ cl_assert(oids != NULL);
+
+ memset(oids, 0x0, count * sizeof(git_oid));
+
+ va_start(ap, count);
+
+ for (i = 0; i < count; ++i) {
+ partial_oid = va_arg(ap, char *);
+ cl_git_pass(git_oid__fromstrn(&oid, partial_oid, strlen(partial_oid), GIT_OID_SHA1));
+
+ cl_git_pass(git_object_lookup_prefix(&object, _repo, &oid, strlen(partial_oid), GIT_OBJECT_COMMIT));
+ git_oid_cpy(&oids[i], git_object_id(object));
+ git_object_free(object);
+ }
+
+ va_end(ap);
+
+ if (expected_sha == NULL)
+ cl_assert_equal_i(GIT_ENOTFOUND, git_merge_base_octopus(&oid, _repo, count, oids));
+ else {
+ cl_git_pass(git_merge_base_octopus(&oid, _repo, count, oids));
+ cl_git_pass(git_oid__fromstr(&expected, expected_sha, GIT_OID_SHA1));
+
+ cl_assert_equal_oid(&expected, &oid);
+ }
+
+ git__free(oids);
+}
+
+void test_revwalk_mergebase__octopus_no_common_ancestor_returns_ENOTFOUND(void)
+{
+ assert_mergebase_octopus(NULL, 3, "41bc8c", "e90810", "a65fed");
+ assert_mergebase_octopus(NULL, 3, "e90810", "41bc8c", "a65fed");
+ assert_mergebase_octopus(NULL, 3, "e90810", "a65fed", "41bc8c");
+ assert_mergebase_octopus(NULL, 3, "a65fed", "e90810", "41bc8c");
+ assert_mergebase_octopus(NULL, 3, "a65fed", "41bc8c", "e90810");
+
+ assert_mergebase_octopus(NULL, 3, "e90810", "763d71", "a65fed");
+
+ assert_mergebase_octopus(NULL, 3, "763d71", "e90810", "a65fed");
+ assert_mergebase_octopus(NULL, 3, "763d71", "a65fed", "e90810");
+
+ assert_mergebase_octopus(NULL, 5, "5b5b02", "763d71", "a4a7dc", "a65fed", "41bc8c");
+}
+
+void test_revwalk_mergebase__octopus_merge_branch(void)
+{
+ assert_mergebase_octopus("8496071c1b46c854b31185ea97743be6a8774479", 3, "a65fed", "763d71", "849607");
+
+ assert_mergebase_octopus("8496071c1b46c854b31185ea97743be6a8774479", 3, "a65fed", "763d71", "849607");
+ assert_mergebase_octopus("8496071c1b46c854b31185ea97743be6a8774479", 3, "a65fed", "849607", "763d71");
+ assert_mergebase_octopus("8496071c1b46c854b31185ea97743be6a8774479", 3, "849607", "a65fed", "763d71");
+}
+
+/*
+ * testrepo.git $ git log --graph --all
+ * * commit 763d71aadf09a7951596c9746c024e7eece7c7af
+ * | Author: nulltoken <emeric.fermas@gmail.com>
+ * | Date: Sun Oct 9 12:54:47 2011 +0200
+ * |
+ * | Add some files into subdirectories
+ * |
+ * | * commit a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+ * | | Author: Scott Chacon <schacon@gmail.com>
+ * | | Date: Tue Aug 9 19:33:46 2011 -0700
+ * | |
+ * | * commit be3563ae3f795b2b4353bcce3a527ad0a4f7f644
+ * | |\ Merge: 9fd738e c47800c
+ * | |/ Author: Scott Chacon <schacon@gmail.com>
+ * |/| Date: Tue May 25 11:58:27 2010 -0700
+ * | |
+ * | | Merge branch 'br2'
+ * | |
+ * | | * commit e90810b8df3e80c413d903f631643c716887138d
+ * | | | Author: Vicent Marti <tanoku@gmail.com>
+ * | | | Date: Thu Aug 5 18:42:20 2010 +0200
+ * | | |
+ * | | | Test commit 2
+ * | | |
+ * | | * commit 6dcf9bf7541ee10456529833502442f385010c3d
+ * | | Author: Vicent Marti <tanoku@gmail.com>
+ * | | Date: Thu Aug 5 18:41:33 2010 +0200
+ * | |
+ * | | Test commit 1
+ * | |
+ * | | * commit a4a7dce85cf63874e984719f4fdd239f5145052f
+ * | | |\ Merge: c47800c 9fd738e
+ * | |/ / Author: Scott Chacon <schacon@gmail.com>
+ * |/| / Date: Tue May 25 12:00:23 2010 -0700
+ * | |/
+ * | | Merge branch 'master' into br2
+ * | |
+ * | * commit 9fd738e8f7967c078dceed8190330fc8648ee56a
+ * | | Author: Scott Chacon <schacon@gmail.com>
+ * | | Date: Mon May 24 10:19:19 2010 -0700
+ * | |
+ * | | a fourth commit
+ * | |
+ * | * commit 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
+ * | | Author: Scott Chacon <schacon@gmail.com>
+ * | | Date: Mon May 24 10:19:04 2010 -0700
+ * | |
+ * | | a third commit
+ * | |
+ * * | commit c47800c7266a2be04c571c04d5a6614691ea99bd
+ * |/ Author: Scott Chacon <schacon@gmail.com>
+ * | Date: Tue May 25 11:58:14 2010 -0700
+ * |
+ * | branch commit one
+ * |
+ * * commit 5b5b025afb0b4c913b4c338a42934a3863bf3644
+ * | Author: Scott Chacon <schacon@gmail.com>
+ * | Date: Tue May 11 13:38:42 2010 -0700
+ * |
+ * | another commit
+ * |
+ * * commit 8496071c1b46c854b31185ea97743be6a8774479
+ * Author: Scott Chacon <schacon@gmail.com>
+ * Date: Sat May 8 16:13:06 2010 -0700
+ *
+ * testing
+ *
+ * * commit 41bc8c69075bbdb46c5c6f0566cc8cc5b46e8bd9
+ * | Author: Scott Chacon <schacon@gmail.com>
+ * | Date: Tue May 11 13:40:41 2010 -0700
+ * |
+ * | packed commit two
+ * |
+ * * commit 5001298e0c09ad9c34e4249bc5801c75e9754fa5
+ * Author: Scott Chacon <schacon@gmail.com>
+ * Date: Tue May 11 13:40:23 2010 -0700
+ *
+ * packed commit one
+ */
+
+/*
+ * twowaymerge.git $ git log --graph --all
+ * * commit 9b219343610c88a1187c996d0dc58330b55cee28
+ * |\ Merge: c37a783 2224e19
+ * | | Author: Scott J. Goldman <scottjg@github.com>
+ * | | Date: Tue Nov 27 20:31:04 2012 -0800
+ * | |
+ * | | Merge branch 'first-branch' into second-branch
+ * | |
+ * | * commit 2224e191514cb4bd8c566d80dac22dfcb1e9bb83
+ * | | Author: Scott J. Goldman <scottjg@github.com>
+ * | | Date: Tue Nov 27 20:28:51 2012 -0800
+ * | |
+ * | | j
+ * | |
+ * | * commit a41a49f8f5cd9b6cb14a076bf8394881ed0b4d19
+ * | | Author: Scott J. Goldman <scottjg@github.com>
+ * | | Date: Tue Nov 27 20:28:39 2012 -0800
+ * | |
+ * | | i
+ * | |
+ * | * commit 82bf9a1a10a4b25c1f14c9607b60970705e92545
+ * | | Author: Scott J. Goldman <scottjg@github.com>
+ * | | Date: Tue Nov 27 20:28:28 2012 -0800
+ * | |
+ * | | h
+ * | |
+ * * | commit c37a783c20d92ac92362a78a32860f7eebf938ef
+ * | | Author: Scott J. Goldman <scottjg@github.com>
+ * | | Date: Tue Nov 27 20:30:57 2012 -0800
+ * | |
+ * | | n
+ * | |
+ * * | commit 8b82fb1794cb1c8c7f172ec730a4c2db0ae3e650
+ * | | Author: Scott J. Goldman <scottjg@github.com>
+ * | | Date: Tue Nov 27 20:30:43 2012 -0800
+ * | |
+ * | | m
+ * | |
+ * * | commit 6ab5d28acbf3c3bdff276f7ccfdf29c1520e542f
+ * | | Author: Scott J. Goldman <scottjg@github.com>
+ * | | Date: Tue Nov 27 20:30:38 2012 -0800
+ * | |
+ * | | l
+ * | |
+ * * | commit 7b8c336c45fc6895c1c60827260fe5d798e5d247
+ * | | Author: Scott J. Goldman <scottjg@github.com>
+ * | | Date: Tue Nov 27 20:30:24 2012 -0800
+ * | |
+ * | | k
+ * | |
+ * | | * commit 1c30b88f5f3ee66d78df6520a7de9e89b890818b
+ * | | | Author: Scott J. Goldman <scottjg@github.com>
+ * | | | Date: Tue Nov 27 20:28:10 2012 -0800
+ * | | |
+ * | | | e
+ * | | |
+ * | | * commit 42b7311aa626e712891940c1ec5d5cba201946a4
+ * | | | Author: Scott J. Goldman <scottjg@github.com>
+ * | | | Date: Tue Nov 27 20:28:06 2012 -0800
+ * | | |
+ * | | | d
+ * | | |
+ * | | * commit a953a018c5b10b20c86e69fef55ebc8ad4c5a417
+ * | | |\ Merge: bd1732c cdf97fd
+ * | | |/ Author: Scott J. Goldman <scottjg@github.com>
+ * | |/| Date: Tue Nov 27 20:26:43 2012 -0800
+ * | | |
+ * | | | Merge branch 'first-branch'
+ * | | |
+ * | * | commit cdf97fd3bb48eb3827638bb33d208f5fd32d0aa6
+ * | | | Author: Scott J. Goldman <scottjg@github.com>
+ * | | | Date: Tue Nov 27 20:24:46 2012 -0800
+ * | | |
+ * | | | g
+ * | | |
+ * | * | commit ef0488f0b722f0be8bcb90a7730ac7efafd1d694
+ * | | | Author: Scott J. Goldman <scottjg@github.com>
+ * | | | Date: Tue Nov 27 20:24:39 2012 -0800
+ * | | |
+ * | | | f
+ * | | |
+ * | | * commit bd1732c43c68d712ad09e1d872b9be6d4b9efdc4
+ * | |/ Author: Scott J. Goldman <scottjg@github.com>
+ * | | Date: Tue Nov 27 17:43:58 2012 -0800
+ * | |
+ * | | c
+ * | |
+ * | * commit 0c8a3f1f3d5f421cf83048c7c73ee3b55a5e0f29
+ * |/ Author: Scott J. Goldman <scottjg@github.com>
+ * | Date: Tue Nov 27 17:43:48 2012 -0800
+ * |
+ * | b
+ * |
+ * * commit 1f4c0311a24b63f6fc209a59a1e404942d4a5006
+ * Author: Scott J. Goldman <scottjg@github.com>
+ * Date: Tue Nov 27 17:43:41 2012 -0800
+ *
+ * a
+ */
+
+void test_revwalk_mergebase__remove_redundant(void)
+{
+ git_repository *repo;
+ git_oid one, two, base;
+ git_oidarray result = {NULL, 0};
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("redundant.git")));
+
+ cl_git_pass(git_oid__fromstr(&one, "d89137c93ba1ee749214ff4ce52ae9137bc833f9", GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&two, "91f4b95df4a59504a9813ba66912562931d990e3", GIT_OID_SHA1));
+ cl_git_pass(git_oid__fromstr(&base, "6cb1f2352d974e1c5a776093017e8772416ac97a", GIT_OID_SHA1));
+
+ cl_git_pass(git_merge_bases(&result, repo, &one, &two));
+ cl_assert_equal_i(1, result.count);
+ cl_assert_equal_oid(&base, &result.ids[0]);
+
+ git_oidarray_dispose(&result);
+ git_repository_free(repo);
+}
diff --git a/tests/libgit2/revwalk/signatureparsing.c b/tests/libgit2/revwalk/signatureparsing.c
new file mode 100644
index 0000000..b312bad
--- /dev/null
+++ b/tests/libgit2/revwalk/signatureparsing.c
@@ -0,0 +1,47 @@
+#include "clar_libgit2.h"
+
+static git_repository *_repo;
+static git_revwalk *_walk;
+
+void test_revwalk_signatureparsing__initialize(void)
+{
+ cl_git_pass(git_repository_open(&_repo, cl_fixture("testrepo.git")));
+ cl_git_pass(git_revwalk_new(&_walk, _repo));
+}
+
+void test_revwalk_signatureparsing__cleanup(void)
+{
+ git_revwalk_free(_walk);
+ _walk = NULL;
+
+ git_repository_free(_repo);
+ _repo = NULL;
+}
+
+void test_revwalk_signatureparsing__do_not_choke_when_name_contains_angle_brackets(void)
+{
+ git_reference *ref;
+ git_oid commit_oid;
+ git_commit *commit;
+ const git_signature *signature;
+
+ /*
+ * The branch below points at a commit with angle brackets in the committer/author name
+ * committer <Yu V. Bin Haacked> <foo@example.com> 1323847743 +0100
+ */
+ cl_git_pass(git_reference_lookup(&ref, _repo, "refs/heads/haacked"));
+
+ git_revwalk_push(_walk, git_reference_target(ref));
+ cl_git_pass(git_revwalk_next(&commit_oid, _walk));
+
+ cl_git_pass(git_commit_lookup(&commit, _repo, git_reference_target(ref)));
+
+ signature = git_commit_committer(commit);
+ cl_assert_equal_s("foo@example.com", signature->email);
+ cl_assert_equal_s("Yu V. Bin Haacked", signature->name);
+ cl_assert_equal_i(1323847743, (int)signature->when.time);
+ cl_assert_equal_i(60, signature->when.offset);
+
+ git_commit_free(commit);
+ git_reference_free(ref);
+}
diff --git a/tests/libgit2/revwalk/simplify.c b/tests/libgit2/revwalk/simplify.c
new file mode 100644
index 0000000..824496d
--- /dev/null
+++ b/tests/libgit2/revwalk/simplify.c
@@ -0,0 +1,56 @@
+#include "clar_libgit2.h"
+
+void test_revwalk_simplify__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+/*
+ * a4a7dce [0] Merge branch 'master' into br2
+ |\
+ | * 9fd738e [1] a fourth commit
+ | * 4a202b3 [2] a third commit
+ * | c47800c [3] branch commit one
+ |/
+ * 5b5b025 [5] another commit
+ * 8496071 [4] testing
+*/
+static const char *commit_head = "a4a7dce85cf63874e984719f4fdd239f5145052f";
+
+static const char *expected_str[] = {
+ "a4a7dce85cf63874e984719f4fdd239f5145052f", /* 0 */
+ "c47800c7266a2be04c571c04d5a6614691ea99bd", /* 3 */
+ "5b5b025afb0b4c913b4c338a42934a3863bf3644", /* 4 */
+ "8496071c1b46c854b31185ea97743be6a8774479", /* 5 */
+};
+
+void test_revwalk_simplify__first_parent(void)
+{
+ git_repository *repo;
+ git_revwalk *walk;
+ git_oid id, expected[4];
+ int i, error;
+
+ for (i = 0; i < 4; i++) {
+ git_oid__fromstr(&expected[i], expected_str[i], GIT_OID_SHA1);
+ }
+
+ repo = cl_git_sandbox_init("testrepo.git");
+ cl_git_pass(git_revwalk_new(&walk, repo));
+
+ git_oid__fromstr(&id, commit_head, GIT_OID_SHA1);
+ cl_git_pass(git_revwalk_push(walk, &id));
+ git_revwalk_sorting(walk, GIT_SORT_TOPOLOGICAL);
+ git_revwalk_simplify_first_parent(walk);
+
+ i = 0;
+ while ((error = git_revwalk_next(&id, walk)) == 0) {
+ cl_assert_equal_oid(&expected[i], &id);
+ i++;
+ }
+
+ cl_assert_equal_i(i, 4);
+ cl_assert_equal_i(error, GIT_ITEROVER);
+
+ git_revwalk_free(walk);
+}
diff --git a/tests/libgit2/stash/apply.c b/tests/libgit2/stash/apply.c
new file mode 100644
index 0000000..5125ae6
--- /dev/null
+++ b/tests/libgit2/stash/apply.c
@@ -0,0 +1,449 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+#include "stash_helpers.h"
+
+static git_signature *signature;
+static git_repository *repo;
+static git_index *repo_index;
+
+void test_stash_apply__initialize(void)
+{
+ git_oid oid;
+
+ repo = cl_git_sandbox_init_new("stash");
+ cl_git_pass(git_repository_index(&repo_index, repo));
+ cl_git_pass(git_signature_new(&signature, "nulltoken", "emeric.fermas@gmail.com", 1323847743, 60)); /* Wed Dec 14 08:29:03 2011 +0100 */
+
+ cl_git_mkfile("stash/what", "hello\n");
+ cl_git_mkfile("stash/how", "small\n");
+ cl_git_mkfile("stash/who", "world\n");
+ cl_git_mkfile("stash/where", "meh\n");
+
+ cl_git_pass(git_index_add_bypath(repo_index, "what"));
+ cl_git_pass(git_index_add_bypath(repo_index, "how"));
+ cl_git_pass(git_index_add_bypath(repo_index, "who"));
+
+ cl_repo_commit_from_index(NULL, repo, signature, 0, "Initial commit");
+
+ cl_git_rewritefile("stash/what", "goodbye\n");
+ cl_git_rewritefile("stash/who", "funky world\n");
+ cl_git_mkfile("stash/when", "tomorrow\n");
+ cl_git_mkfile("stash/why", "would anybody use stash?\n");
+ cl_git_mkfile("stash/where", "????\n");
+
+ cl_git_pass(git_index_add_bypath(repo_index, "who"));
+ cl_git_pass(git_index_add_bypath(repo_index, "why"));
+ cl_git_pass(git_index_add_bypath(repo_index, "where"));
+ cl_git_pass(git_index_write(repo_index));
+
+ cl_git_rewritefile("stash/where", "....\n");
+
+ /* Pre-stash state */
+ assert_status(repo, "what", GIT_STATUS_WT_MODIFIED);
+ assert_status(repo, "how", GIT_STATUS_CURRENT);
+ assert_status(repo, "who", GIT_STATUS_INDEX_MODIFIED);
+ assert_status(repo, "when", GIT_STATUS_WT_NEW);
+ assert_status(repo, "why", GIT_STATUS_INDEX_NEW);
+ assert_status(repo, "where", GIT_STATUS_INDEX_NEW|GIT_STATUS_WT_MODIFIED);
+
+ cl_git_pass(git_stash_save(&oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
+
+ /* Post-stash state */
+ assert_status(repo, "what", GIT_STATUS_CURRENT);
+ assert_status(repo, "how", GIT_STATUS_CURRENT);
+ assert_status(repo, "who", GIT_STATUS_CURRENT);
+ assert_status(repo, "when", GIT_ENOTFOUND);
+ assert_status(repo, "why", GIT_ENOTFOUND);
+ assert_status(repo, "where", GIT_ENOTFOUND);
+}
+
+void test_stash_apply__cleanup(void)
+{
+ git_signature_free(signature);
+ signature = NULL;
+
+ git_index_free(repo_index);
+ repo_index = NULL;
+
+ cl_git_sandbox_cleanup();
+}
+
+void test_stash_apply__with_default(void)
+{
+ git_str where = GIT_STR_INIT;
+
+ cl_git_pass(git_stash_apply(repo, 0, NULL));
+
+ cl_assert_equal_i(git_index_has_conflicts(repo_index), 0);
+ assert_status(repo, "what", GIT_STATUS_WT_MODIFIED);
+ assert_status(repo, "how", GIT_STATUS_CURRENT);
+ assert_status(repo, "who", GIT_STATUS_WT_MODIFIED);
+ assert_status(repo, "when", GIT_STATUS_WT_NEW);
+ assert_status(repo, "why", GIT_STATUS_INDEX_NEW);
+ assert_status(repo, "where", GIT_STATUS_INDEX_NEW);
+
+ cl_git_pass(git_futils_readbuffer(&where, "stash/where"));
+ cl_assert_equal_s("....\n", where.ptr);
+
+ git_str_dispose(&where);
+}
+
+void test_stash_apply__with_existing_file(void)
+{
+ cl_git_mkfile("stash/where", "oops!\n");
+ cl_git_fail(git_stash_apply(repo, 0, NULL));
+}
+
+void test_stash_apply__merges_new_file(void)
+{
+ const git_index_entry *ancestor, *our, *their;
+
+ cl_git_mkfile("stash/where", "committed before stash\n");
+ cl_git_pass(git_index_add_bypath(repo_index, "where"));
+ cl_repo_commit_from_index(NULL, repo, signature, 0, "Other commit");
+
+ cl_git_pass(git_stash_apply(repo, 0, NULL));
+
+ cl_assert_equal_i(1, git_index_has_conflicts(repo_index));
+ assert_status(repo, "what", GIT_STATUS_INDEX_MODIFIED);
+ cl_git_pass(git_index_conflict_get(&ancestor, &our, &their, repo_index, "where")); /* unmerged */
+ assert_status(repo, "who", GIT_STATUS_INDEX_MODIFIED);
+ assert_status(repo, "when", GIT_STATUS_WT_NEW);
+ assert_status(repo, "why", GIT_STATUS_INDEX_NEW);
+}
+
+void test_stash_apply__with_reinstate_index(void)
+{
+ git_str where = GIT_STR_INIT;
+ git_stash_apply_options opts = GIT_STASH_APPLY_OPTIONS_INIT;
+
+ opts.flags = GIT_STASH_APPLY_REINSTATE_INDEX;
+
+ cl_git_pass(git_stash_apply(repo, 0, &opts));
+
+ cl_assert_equal_i(git_index_has_conflicts(repo_index), 0);
+ assert_status(repo, "what", GIT_STATUS_WT_MODIFIED);
+ assert_status(repo, "how", GIT_STATUS_CURRENT);
+ assert_status(repo, "who", GIT_STATUS_INDEX_MODIFIED);
+ assert_status(repo, "when", GIT_STATUS_WT_NEW);
+ assert_status(repo, "why", GIT_STATUS_INDEX_NEW);
+ assert_status(repo, "where", GIT_STATUS_INDEX_NEW | GIT_STATUS_WT_MODIFIED);
+
+ cl_git_pass(git_futils_readbuffer(&where, "stash/where"));
+ cl_assert_equal_s("....\n", where.ptr);
+
+ git_str_dispose(&where);
+}
+
+void test_stash_apply__conflict_index_with_default(void)
+{
+ const git_index_entry *ancestor;
+ const git_index_entry *our;
+ const git_index_entry *their;
+
+ cl_git_rewritefile("stash/who", "nothing\n");
+ cl_git_pass(git_index_add_bypath(repo_index, "who"));
+ cl_git_pass(git_index_write(repo_index));
+ cl_repo_commit_from_index(NULL, repo, signature, 0, "Other commit");
+
+ cl_git_pass(git_stash_apply(repo, 0, NULL));
+
+ cl_assert_equal_i(git_index_has_conflicts(repo_index), 1);
+ assert_status(repo, "what", GIT_STATUS_INDEX_MODIFIED);
+ assert_status(repo, "how", GIT_STATUS_CURRENT);
+ cl_git_pass(git_index_conflict_get(&ancestor, &our, &their, repo_index, "who")); /* unmerged */
+ assert_status(repo, "when", GIT_STATUS_WT_NEW);
+ assert_status(repo, "why", GIT_STATUS_INDEX_NEW);
+}
+
+void test_stash_apply__conflict_index_with_reinstate_index(void)
+{
+ git_stash_apply_options opts = GIT_STASH_APPLY_OPTIONS_INIT;
+
+ opts.flags = GIT_STASH_APPLY_REINSTATE_INDEX;
+
+ cl_git_rewritefile("stash/who", "nothing\n");
+ cl_git_pass(git_index_add_bypath(repo_index, "who"));
+ cl_git_pass(git_index_write(repo_index));
+ cl_repo_commit_from_index(NULL, repo, signature, 0, "Other commit");
+
+ cl_git_fail_with(git_stash_apply(repo, 0, &opts), GIT_ECONFLICT);
+
+ cl_assert_equal_i(git_index_has_conflicts(repo_index), 0);
+ assert_status(repo, "what", GIT_STATUS_CURRENT);
+ assert_status(repo, "how", GIT_STATUS_CURRENT);
+ assert_status(repo, "who", GIT_STATUS_CURRENT);
+ assert_status(repo, "when", GIT_ENOTFOUND);
+ assert_status(repo, "why", GIT_ENOTFOUND);
+}
+
+void test_stash_apply__conflict_untracked_with_default(void)
+{
+ git_stash_apply_options opts = GIT_STASH_APPLY_OPTIONS_INIT;
+
+ cl_git_mkfile("stash/when", "nothing\n");
+
+ cl_git_fail_with(git_stash_apply(repo, 0, &opts), GIT_ECONFLICT);
+
+ cl_assert_equal_i(git_index_has_conflicts(repo_index), 0);
+ assert_status(repo, "what", GIT_STATUS_CURRENT);
+ assert_status(repo, "how", GIT_STATUS_CURRENT);
+ assert_status(repo, "who", GIT_STATUS_CURRENT);
+ assert_status(repo, "when", GIT_STATUS_WT_NEW);
+ assert_status(repo, "why", GIT_ENOTFOUND);
+}
+
+void test_stash_apply__conflict_untracked_with_reinstate_index(void)
+{
+ git_stash_apply_options opts = GIT_STASH_APPLY_OPTIONS_INIT;
+
+ opts.flags = GIT_STASH_APPLY_REINSTATE_INDEX;
+
+ cl_git_mkfile("stash/when", "nothing\n");
+
+ cl_git_fail_with(git_stash_apply(repo, 0, &opts), GIT_ECONFLICT);
+
+ cl_assert_equal_i(git_index_has_conflicts(repo_index), 0);
+ assert_status(repo, "what", GIT_STATUS_CURRENT);
+ assert_status(repo, "how", GIT_STATUS_CURRENT);
+ assert_status(repo, "who", GIT_STATUS_CURRENT);
+ assert_status(repo, "when", GIT_STATUS_WT_NEW);
+ assert_status(repo, "why", GIT_ENOTFOUND);
+}
+
+void test_stash_apply__conflict_workdir_with_default(void)
+{
+ cl_git_rewritefile("stash/what", "ciao\n");
+
+ cl_git_fail_with(git_stash_apply(repo, 0, NULL), GIT_ECONFLICT);
+
+ cl_assert_equal_i(git_index_has_conflicts(repo_index), 0);
+ assert_status(repo, "what", GIT_STATUS_WT_MODIFIED);
+ assert_status(repo, "how", GIT_STATUS_CURRENT);
+ assert_status(repo, "who", GIT_STATUS_CURRENT);
+ assert_status(repo, "when", GIT_STATUS_WT_NEW);
+ assert_status(repo, "why", GIT_ENOTFOUND);
+}
+
+void test_stash_apply__conflict_workdir_with_reinstate_index(void)
+{
+ git_stash_apply_options opts = GIT_STASH_APPLY_OPTIONS_INIT;
+
+ opts.flags = GIT_STASH_APPLY_REINSTATE_INDEX;
+
+ cl_git_rewritefile("stash/what", "ciao\n");
+
+ cl_git_fail_with(git_stash_apply(repo, 0, &opts), GIT_ECONFLICT);
+
+ cl_assert_equal_i(git_index_has_conflicts(repo_index), 0);
+ assert_status(repo, "what", GIT_STATUS_WT_MODIFIED);
+ assert_status(repo, "how", GIT_STATUS_CURRENT);
+ assert_status(repo, "who", GIT_STATUS_CURRENT);
+ assert_status(repo, "when", GIT_STATUS_WT_NEW);
+ assert_status(repo, "why", GIT_ENOTFOUND);
+}
+
+void test_stash_apply__conflict_commit_with_default(void)
+{
+ const git_index_entry *ancestor;
+ const git_index_entry *our;
+ const git_index_entry *their;
+
+ cl_git_rewritefile("stash/what", "ciao\n");
+ cl_git_pass(git_index_add_bypath(repo_index, "what"));
+ cl_repo_commit_from_index(NULL, repo, signature, 0, "Other commit");
+
+ cl_git_pass(git_stash_apply(repo, 0, NULL));
+
+ cl_assert_equal_i(git_index_has_conflicts(repo_index), 1);
+ cl_git_pass(git_index_conflict_get(&ancestor, &our, &their, repo_index, "what")); /* unmerged */
+ assert_status(repo, "how", GIT_STATUS_CURRENT);
+ assert_status(repo, "who", GIT_STATUS_INDEX_MODIFIED);
+ assert_status(repo, "when", GIT_STATUS_WT_NEW);
+ assert_status(repo, "why", GIT_STATUS_INDEX_NEW);
+}
+
+void test_stash_apply__conflict_commit_with_reinstate_index(void)
+{
+ git_stash_apply_options opts = GIT_STASH_APPLY_OPTIONS_INIT;
+ const git_index_entry *ancestor;
+ const git_index_entry *our;
+ const git_index_entry *their;
+
+ opts.flags = GIT_STASH_APPLY_REINSTATE_INDEX;
+
+ cl_git_rewritefile("stash/what", "ciao\n");
+ cl_git_pass(git_index_add_bypath(repo_index, "what"));
+ cl_repo_commit_from_index(NULL, repo, signature, 0, "Other commit");
+
+ cl_git_pass(git_stash_apply(repo, 0, &opts));
+
+ cl_assert_equal_i(git_index_has_conflicts(repo_index), 1);
+ cl_git_pass(git_index_conflict_get(&ancestor, &our, &their, repo_index, "what")); /* unmerged */
+ assert_status(repo, "how", GIT_STATUS_CURRENT);
+ assert_status(repo, "who", GIT_STATUS_INDEX_MODIFIED);
+ assert_status(repo, "when", GIT_STATUS_WT_NEW);
+ assert_status(repo, "why", GIT_STATUS_INDEX_NEW);
+}
+
+void test_stash_apply__fails_with_uncommitted_changes_in_index(void)
+{
+ cl_git_rewritefile("stash/who", "nothing\n");
+ cl_git_pass(git_index_add_bypath(repo_index, "who"));
+ cl_git_pass(git_index_write(repo_index));
+
+ cl_git_fail_with(git_stash_apply(repo, 0, NULL), GIT_EUNCOMMITTED);
+
+ cl_assert_equal_i(git_index_has_conflicts(repo_index), 0);
+ assert_status(repo, "what", GIT_STATUS_CURRENT);
+ assert_status(repo, "how", GIT_STATUS_CURRENT);
+ assert_status(repo, "who", GIT_STATUS_INDEX_MODIFIED);
+ assert_status(repo, "when", GIT_ENOTFOUND);
+ assert_status(repo, "why", GIT_ENOTFOUND);
+}
+
+void test_stash_apply__pop(void)
+{
+ cl_git_pass(git_stash_pop(repo, 0, NULL));
+
+ cl_git_fail_with(git_stash_pop(repo, 0, NULL), GIT_ENOTFOUND);
+}
+
+struct seen_paths {
+ bool what;
+ bool how;
+ bool who;
+ bool when;
+};
+
+static int checkout_notify(
+ git_checkout_notify_t why,
+ const char *path,
+ const git_diff_file *baseline,
+ const git_diff_file *target,
+ const git_diff_file *workdir,
+ void *payload)
+{
+ struct seen_paths *seen_paths = (struct seen_paths *)payload;
+
+ GIT_UNUSED(why);
+ GIT_UNUSED(baseline);
+ GIT_UNUSED(target);
+ GIT_UNUSED(workdir);
+
+ if (strcmp(path, "what") == 0)
+ seen_paths->what = 1;
+ else if (strcmp(path, "how") == 0)
+ seen_paths->how = 1;
+ else if (strcmp(path, "who") == 0)
+ seen_paths->who = 1;
+ else if (strcmp(path, "when") == 0)
+ seen_paths->when = 1;
+
+ return 0;
+}
+
+void test_stash_apply__executes_notify_cb(void)
+{
+ git_stash_apply_options opts = GIT_STASH_APPLY_OPTIONS_INIT;
+ struct seen_paths seen_paths = {0};
+
+ opts.checkout_options.notify_cb = checkout_notify;
+ opts.checkout_options.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
+ opts.checkout_options.notify_payload = &seen_paths;
+
+ cl_git_pass(git_stash_apply(repo, 0, &opts));
+
+ cl_assert_equal_i(git_index_has_conflicts(repo_index), 0);
+ assert_status(repo, "what", GIT_STATUS_WT_MODIFIED);
+ assert_status(repo, "how", GIT_STATUS_CURRENT);
+ assert_status(repo, "who", GIT_STATUS_WT_MODIFIED);
+ assert_status(repo, "when", GIT_STATUS_WT_NEW);
+ assert_status(repo, "why", GIT_STATUS_INDEX_NEW);
+ assert_status(repo, "where", GIT_STATUS_INDEX_NEW);
+
+ cl_assert_equal_b(true, seen_paths.what);
+ cl_assert_equal_b(false, seen_paths.how);
+ cl_assert_equal_b(true, seen_paths.who);
+ cl_assert_equal_b(true, seen_paths.when);
+}
+
+static int progress_cb(
+ git_stash_apply_progress_t progress,
+ void *payload)
+{
+ git_stash_apply_progress_t *p = (git_stash_apply_progress_t *)payload;
+
+ cl_assert_equal_i((*p)+1, progress);
+
+ *p = progress;
+
+ return 0;
+}
+
+void test_stash_apply__calls_progress_cb(void)
+{
+ git_stash_apply_options opts = GIT_STASH_APPLY_OPTIONS_INIT;
+ git_stash_apply_progress_t progress = GIT_STASH_APPLY_PROGRESS_NONE;
+
+ opts.progress_cb = progress_cb;
+ opts.progress_payload = &progress;
+
+ cl_git_pass(git_stash_apply(repo, 0, &opts));
+ cl_assert_equal_i(progress, GIT_STASH_APPLY_PROGRESS_DONE);
+}
+
+static int aborting_progress_cb(
+ git_stash_apply_progress_t progress,
+ void *payload)
+{
+ GIT_UNUSED(payload);
+
+ if (progress == GIT_STASH_APPLY_PROGRESS_ANALYZE_MODIFIED)
+ return -44;
+
+ return 0;
+}
+
+void test_stash_apply__progress_cb_can_abort(void)
+{
+ git_stash_apply_options opts = GIT_STASH_APPLY_OPTIONS_INIT;
+
+ opts.progress_cb = aborting_progress_cb;
+
+ cl_git_fail_with(-44, git_stash_apply(repo, 0, &opts));
+}
+
+void test_stash_apply__uses_reflog_like_indices_1(void)
+{
+ git_oid oid;
+
+ cl_git_mkfile("stash/untracked", "untracked\n");
+ cl_git_pass(git_stash_save(&oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
+ assert_status(repo, "untracked", GIT_ENOTFOUND);
+
+ /* stash@{1} is the oldest (first) stash we made */
+ cl_git_pass(git_stash_apply(repo, 1, NULL));
+ cl_assert_equal_i(git_index_has_conflicts(repo_index), 0);
+ assert_status(repo, "what", GIT_STATUS_WT_MODIFIED);
+ assert_status(repo, "how", GIT_STATUS_CURRENT);
+ assert_status(repo, "who", GIT_STATUS_WT_MODIFIED);
+ assert_status(repo, "when", GIT_STATUS_WT_NEW);
+ assert_status(repo, "why", GIT_STATUS_INDEX_NEW);
+ assert_status(repo, "where", GIT_STATUS_INDEX_NEW);
+}
+
+void test_stash_apply__uses_reflog_like_indices_2(void)
+{
+ git_oid oid;
+
+ cl_git_mkfile("stash/untracked", "untracked\n");
+ cl_git_pass(git_stash_save(&oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
+ assert_status(repo, "untracked", GIT_ENOTFOUND);
+
+ /* stash@{0} is the newest stash we made immediately above */
+ cl_git_pass(git_stash_apply(repo, 0, NULL));
+
+ cl_assert_equal_i(git_index_has_conflicts(repo_index), 0);
+ assert_status(repo, "untracked", GIT_STATUS_WT_NEW);
+}
diff --git a/tests/libgit2/stash/drop.c b/tests/libgit2/stash/drop.c
new file mode 100644
index 0000000..a571471
--- /dev/null
+++ b/tests/libgit2/stash/drop.c
@@ -0,0 +1,174 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+#include "stash_helpers.h"
+#include "refs.h"
+
+static git_repository *repo;
+static git_signature *signature;
+
+void test_stash_drop__initialize(void)
+{
+ cl_git_pass(git_repository_init(&repo, "stash", 0));
+ cl_git_pass(git_signature_new(&signature, "nulltoken", "emeric.fermas@gmail.com", 1323847743, 60)); /* Wed Dec 14 08:29:03 2011 +0100 */
+}
+
+void test_stash_drop__cleanup(void)
+{
+ git_signature_free(signature);
+ signature = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+
+ cl_git_pass(git_futils_rmdir_r("stash", NULL, GIT_RMDIR_REMOVE_FILES));
+}
+
+void test_stash_drop__cannot_drop_from_an_empty_stash(void)
+{
+ cl_git_fail_with(git_stash_drop(repo, 0), GIT_ENOTFOUND);
+}
+
+static void push_three_states(void)
+{
+ git_oid oid;
+ git_index *index;
+
+ cl_git_mkfile("stash/zero.txt", "content\n");
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_add_bypath(index, "zero.txt"));
+ cl_repo_commit_from_index(NULL, repo, signature, 0, "Initial commit");
+ cl_assert(git_fs_path_exists("stash/zero.txt"));
+ git_index_free(index);
+
+ cl_git_mkfile("stash/one.txt", "content\n");
+ cl_git_pass(git_stash_save(
+ &oid, repo, signature, "First", GIT_STASH_INCLUDE_UNTRACKED));
+ cl_assert(!git_fs_path_exists("stash/one.txt"));
+ cl_assert(git_fs_path_exists("stash/zero.txt"));
+
+ cl_git_mkfile("stash/two.txt", "content\n");
+ cl_git_pass(git_stash_save(
+ &oid, repo, signature, "Second", GIT_STASH_INCLUDE_UNTRACKED));
+ cl_assert(!git_fs_path_exists("stash/two.txt"));
+ cl_assert(git_fs_path_exists("stash/zero.txt"));
+
+ cl_git_mkfile("stash/three.txt", "content\n");
+ cl_git_pass(git_stash_save(
+ &oid, repo, signature, "Third", GIT_STASH_INCLUDE_UNTRACKED));
+ cl_assert(!git_fs_path_exists("stash/three.txt"));
+ cl_assert(git_fs_path_exists("stash/zero.txt"));
+}
+
+void test_stash_drop__cannot_drop_a_non_existing_stashed_state(void)
+{
+ push_three_states();
+
+ cl_git_fail_with(git_stash_drop(repo, 666), GIT_ENOTFOUND);
+ cl_git_fail_with(git_stash_drop(repo, 42), GIT_ENOTFOUND);
+ cl_git_fail_with(git_stash_drop(repo, 3), GIT_ENOTFOUND);
+}
+
+void test_stash_drop__can_purge_the_stash_from_the_top(void)
+{
+ push_three_states();
+
+ cl_git_pass(git_stash_drop(repo, 0));
+ cl_git_pass(git_stash_drop(repo, 0));
+ cl_git_pass(git_stash_drop(repo, 0));
+
+ cl_git_fail_with(git_stash_drop(repo, 0), GIT_ENOTFOUND);
+}
+
+void test_stash_drop__can_purge_the_stash_from_the_bottom(void)
+{
+ push_three_states();
+
+ cl_git_pass(git_stash_drop(repo, 2));
+ cl_git_pass(git_stash_drop(repo, 1));
+ cl_git_pass(git_stash_drop(repo, 0));
+
+ cl_git_fail_with(git_stash_drop(repo, 0), GIT_ENOTFOUND);
+}
+
+void test_stash_drop__dropping_an_entry_rewrites_reflog_history(void)
+{
+ git_reference *stash;
+ git_reflog *reflog;
+ const git_reflog_entry *entry;
+ git_oid oid;
+ size_t count;
+
+ push_three_states();
+
+ cl_git_pass(git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE));
+
+ cl_git_pass(git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE));
+ entry = git_reflog_entry_byindex(reflog, 1);
+
+ git_oid_cpy(&oid, git_reflog_entry_id_old(entry));
+ count = git_reflog_entrycount(reflog);
+
+ git_reflog_free(reflog);
+
+ cl_git_pass(git_stash_drop(repo, 1));
+
+ cl_git_pass(git_reflog_read(&reflog, repo, GIT_REFS_STASH_FILE));
+ entry = git_reflog_entry_byindex(reflog, 0);
+
+ cl_assert_equal_oid(&oid, git_reflog_entry_id_old(entry));
+ cl_assert_equal_sz(count - 1, git_reflog_entrycount(reflog));
+
+ git_reflog_free(reflog);
+
+ git_reference_free(stash);
+}
+
+void test_stash_drop__dropping_the_last_entry_removes_the_stash(void)
+{
+ git_reference *stash;
+
+ push_three_states();
+
+ cl_git_pass(git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE));
+ git_reference_free(stash);
+
+ cl_git_pass(git_stash_drop(repo, 0));
+ cl_git_pass(git_stash_drop(repo, 0));
+ cl_git_pass(git_stash_drop(repo, 0));
+
+ cl_git_fail_with(
+ git_reference_lookup(&stash, repo, GIT_REFS_STASH_FILE), GIT_ENOTFOUND);
+}
+
+static void retrieve_top_stash_id(git_oid *out)
+{
+ git_object *top_stash;
+
+ cl_git_pass(git_revparse_single(&top_stash, repo, "stash@{0}"));
+ cl_git_pass(git_reference_name_to_id(out, repo, GIT_REFS_STASH_FILE));
+
+ cl_assert_equal_oid(out, git_object_id(top_stash));
+
+ git_object_free(top_stash);
+}
+
+void test_stash_drop__dropping_the_top_stash_updates_the_stash_reference(void)
+{
+ git_object *next_top_stash;
+ git_oid oid;
+
+ push_three_states();
+
+ retrieve_top_stash_id(&oid);
+
+ cl_git_pass(git_revparse_single(&next_top_stash, repo, "stash@{1}"));
+ cl_assert(git_oid_cmp(&oid, git_object_id(next_top_stash)));
+
+ cl_git_pass(git_stash_drop(repo, 0));
+
+ retrieve_top_stash_id(&oid);
+
+ cl_assert_equal_oid(&oid, git_object_id(next_top_stash));
+
+ git_object_free(next_top_stash);
+}
diff --git a/tests/libgit2/stash/foreach.c b/tests/libgit2/stash/foreach.c
new file mode 100644
index 0000000..fa3a9c9
--- /dev/null
+++ b/tests/libgit2/stash/foreach.c
@@ -0,0 +1,126 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+#include "stash_helpers.h"
+
+struct callback_data
+{
+ char **oids;
+ int invokes;
+};
+
+static git_repository *repo;
+static git_signature *signature;
+static git_oid stash_tip_oid;
+struct callback_data data;
+
+#define REPO_NAME "stash"
+
+void test_stash_foreach__initialize(void)
+{
+ cl_git_pass(git_signature_new(
+ &signature,
+ "nulltoken",
+ "emeric.fermas@gmail.com",
+ 1323847743, 60)); /* Wed Dec 14 08:29:03 2011 +0100 */
+
+ memset(&data, 0, sizeof(struct callback_data));
+}
+
+void test_stash_foreach__cleanup(void)
+{
+ git_signature_free(signature);
+ signature = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+
+ cl_git_pass(git_futils_rmdir_r(REPO_NAME, NULL, GIT_RMDIR_REMOVE_FILES));
+}
+
+static int callback_cb(
+ size_t index,
+ const char* message,
+ const git_oid *stash_oid,
+ void *payload)
+{
+ struct callback_data *data = (struct callback_data *)payload;
+
+ GIT_UNUSED(index);
+ GIT_UNUSED(message);
+
+ cl_assert_equal_i(0, git_oid_streq(stash_oid, data->oids[data->invokes++]));
+
+ return 0;
+}
+
+void test_stash_foreach__enumerating_a_empty_repository_doesnt_fail(void)
+{
+ char *oids[] = { NULL };
+
+ data.oids = oids;
+
+ cl_git_pass(git_repository_init(&repo, REPO_NAME, 0));
+
+ cl_git_pass(git_stash_foreach(repo, callback_cb, &data));
+
+ cl_assert_equal_i(0, data.invokes);
+}
+
+void test_stash_foreach__can_enumerate_a_repository(void)
+{
+ char *oids_default[] = {
+ "493568b7a2681187aaac8a58d3f1eab1527cba84", NULL };
+
+ char *oids_untracked[] = {
+ "7f89a8b15c878809c5c54d1ff8f8c9674154017b",
+ "493568b7a2681187aaac8a58d3f1eab1527cba84", NULL };
+
+ char *oids_ignored[] = {
+ "c95599a8fef20a7e57582c6727b1a0d02e0a5828",
+ "7f89a8b15c878809c5c54d1ff8f8c9674154017b",
+ "493568b7a2681187aaac8a58d3f1eab1527cba84", NULL };
+
+ cl_git_pass(git_repository_init(&repo, REPO_NAME, 0));
+
+ setup_stash(repo, signature);
+
+ cl_git_pass(git_stash_save(
+ &stash_tip_oid,
+ repo,
+ signature,
+ NULL,
+ GIT_STASH_DEFAULT));
+
+ data.oids = oids_default;
+
+ cl_git_pass(git_stash_foreach(repo, callback_cb, &data));
+ cl_assert_equal_i(1, data.invokes);
+
+ /* ensure stash_foreach operates with INCLUDE_UNTRACKED */
+ cl_git_pass(git_stash_save(
+ &stash_tip_oid,
+ repo,
+ signature,
+ NULL,
+ GIT_STASH_INCLUDE_UNTRACKED));
+
+ data.oids = oids_untracked;
+ data.invokes = 0;
+
+ cl_git_pass(git_stash_foreach(repo, callback_cb, &data));
+ cl_assert_equal_i(2, data.invokes);
+
+ /* ensure stash_foreach operates with INCLUDE_IGNORED */
+ cl_git_pass(git_stash_save(
+ &stash_tip_oid,
+ repo,
+ signature,
+ NULL,
+ GIT_STASH_INCLUDE_IGNORED));
+
+ data.oids = oids_ignored;
+ data.invokes = 0;
+
+ cl_git_pass(git_stash_foreach(repo, callback_cb, &data));
+ cl_assert_equal_i(3, data.invokes);
+}
diff --git a/tests/libgit2/stash/save.c b/tests/libgit2/stash/save.c
new file mode 100644
index 0000000..23f3c1c
--- /dev/null
+++ b/tests/libgit2/stash/save.c
@@ -0,0 +1,527 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+#include "stash_helpers.h"
+
+static git_repository *repo;
+static git_signature *signature;
+static git_oid stash_tip_oid;
+
+/*
+ * Friendly reminder, in order to ease the reading of the following tests:
+ *
+ * "stash" points to the worktree commit
+ * "stash^1" points to the base commit (HEAD when the stash was created)
+ * "stash^2" points to the index commit
+ * "stash^3" points to the untracked commit
+ */
+
+void test_stash_save__initialize(void)
+{
+ cl_git_pass(git_repository_init(&repo, "stash", 0));
+ cl_git_pass(git_signature_new(&signature, "nulltoken", "emeric.fermas@gmail.com", 1323847743, 60)); /* Wed Dec 14 08:29:03 2011 +0100 */
+
+ setup_stash(repo, signature);
+}
+
+void test_stash_save__cleanup(void)
+{
+ git_signature_free(signature);
+ signature = NULL;
+
+ git_repository_free(repo);
+ repo = NULL;
+
+ cl_git_pass(git_futils_rmdir_r("stash", NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_fixture_cleanup("sorry-it-is-a-non-bare-only-party");
+}
+
+static void assert_object_oid(const char* revision, const char* expected_oid, git_object_t type)
+{
+ int result;
+ git_object *obj;
+
+ result = git_revparse_single(&obj, repo, revision);
+
+ if (!expected_oid) {
+ cl_assert_equal_i(GIT_ENOTFOUND, result);
+ return;
+ } else
+ cl_assert_equal_i(0, result);
+
+ cl_git_pass(git_oid_streq(git_object_id(obj), expected_oid));
+ cl_assert_equal_i(type, git_object_type(obj));
+ git_object_free(obj);
+}
+
+static void assert_blob_oid(const char* revision, const char* expected_oid)
+{
+ assert_object_oid(revision, expected_oid, GIT_OBJECT_BLOB);
+}
+
+void test_stash_save__does_not_keep_index_by_default(void)
+{
+/*
+$ git stash
+
+$ git show refs/stash:what
+see you later
+
+$ git show refs/stash:how
+not so small and
+
+$ git show refs/stash:who
+funky world
+
+$ git show refs/stash:when
+fatal: Path 'when' exists on disk, but not in 'stash'.
+
+$ git show refs/stash^2:what
+goodbye
+
+$ git show refs/stash^2:how
+not so small and
+
+$ git show refs/stash^2:who
+world
+
+$ git show refs/stash^2:when
+fatal: Path 'when' exists on disk, but not in 'stash^2'.
+
+$ git status --short
+?? when
+
+*/
+ unsigned int status;
+
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
+ cl_git_pass(git_status_file(&status, repo, "when"));
+
+ assert_blob_oid("refs/stash:what", "bc99dc98b3eba0e9157e94769cd4d49cb49de449"); /* see you later */
+ assert_blob_oid("refs/stash:how", "e6d64adb2c7f3eb8feb493b556cc8070dca379a3"); /* not so small and */
+ assert_blob_oid("refs/stash:who", "a0400d4954659306a976567af43125a0b1aa8595"); /* funky world */
+ assert_blob_oid("refs/stash:when", NULL);
+ assert_blob_oid("refs/stash:why", "88c2533e21f098b89c91a431d8075cbdbe422a51"); /* would anybody use stash? */
+ assert_blob_oid("refs/stash:where", "e3d6434ec12eb76af8dfa843a64ba6ab91014a0b"); /* .... */
+ assert_blob_oid("refs/stash:.gitignore", "ac4d88de61733173d9959e4b77c69b9f17a00980");
+ assert_blob_oid("refs/stash:just.ignore", NULL);
+
+ assert_blob_oid("refs/stash^2:what", "dd7e1c6f0fefe118f0b63d9f10908c460aa317a6"); /* goodbye */
+ assert_blob_oid("refs/stash^2:how", "e6d64adb2c7f3eb8feb493b556cc8070dca379a3"); /* not so small and */
+ assert_blob_oid("refs/stash^2:who", "cc628ccd10742baea8241c5924df992b5c019f71"); /* world */
+ assert_blob_oid("refs/stash^2:when", NULL);
+ assert_blob_oid("refs/stash^2:why", "88c2533e21f098b89c91a431d8075cbdbe422a51"); /* would anybody use stash? */
+ assert_blob_oid("refs/stash^2:where", "e08f7fbb9a42a0c5367cf8b349f1f08c3d56bd72"); /* ???? */
+ assert_blob_oid("refs/stash^2:.gitignore", "ac4d88de61733173d9959e4b77c69b9f17a00980");
+ assert_blob_oid("refs/stash^2:just.ignore", NULL);
+
+ assert_blob_oid("refs/stash^3", NULL);
+
+ cl_assert_equal_i(GIT_STATUS_WT_NEW, status);
+}
+
+void test_stash_save__can_keep_index(void)
+{
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_KEEP_INDEX));
+
+ assert_status(repo, "what", GIT_STATUS_INDEX_MODIFIED);
+ assert_status(repo, "how", GIT_STATUS_INDEX_MODIFIED);
+ assert_status(repo, "who", GIT_STATUS_CURRENT);
+ assert_status(repo, "when", GIT_STATUS_WT_NEW);
+ assert_status(repo, "just.ignore", GIT_STATUS_IGNORED);
+}
+
+void test_stash_save__can_keep_all(void)
+{
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_KEEP_ALL));
+
+ assert_status(repo, "what", GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED);
+ assert_status(repo, "how", GIT_STATUS_INDEX_MODIFIED);
+ assert_status(repo, "who", GIT_STATUS_WT_MODIFIED);
+ assert_status(repo, "when", GIT_STATUS_WT_NEW);
+ assert_status(repo, "why", GIT_STATUS_INDEX_NEW);
+ assert_status(repo, "where", GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_NEW);
+ assert_status(repo, "just.ignore", GIT_STATUS_IGNORED);
+}
+
+static void assert_commit_message_contains(const char *revision, const char *fragment)
+{
+ git_commit *commit;
+
+ cl_git_pass(git_revparse_single((git_object**)&commit, repo, revision));
+
+ cl_assert(strstr(git_commit_message(commit), fragment) != NULL);
+
+ git_commit_free(commit);
+}
+
+void test_stash_save__can_include_untracked_files(void)
+{
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
+
+ assert_commit_message_contains("refs/stash^3", "untracked files on master: ");
+
+ assert_blob_oid("refs/stash^3:what", NULL);
+ assert_blob_oid("refs/stash^3:how", NULL);
+ assert_blob_oid("refs/stash^3:who", NULL);
+ assert_blob_oid("refs/stash^3:when", "b6ed15e81e2593d7bb6265eb4a991d29dc3e628b");
+ assert_blob_oid("refs/stash^3:just.ignore", NULL);
+}
+
+void test_stash_save__untracked_skips_ignored(void)
+{
+ cl_git_append2file("stash/.gitignore", "bundle/vendor/\n");
+ cl_must_pass(p_mkdir("stash/bundle", 0777));
+ cl_must_pass(p_mkdir("stash/bundle/vendor", 0777));
+ cl_git_mkfile("stash/bundle/vendor/blah", "contents\n");
+
+ cl_assert(git_fs_path_exists("stash/when")); /* untracked */
+ cl_assert(git_fs_path_exists("stash/just.ignore")); /* ignored */
+ cl_assert(git_fs_path_exists("stash/bundle/vendor/blah")); /* ignored */
+
+ cl_git_pass(git_stash_save(
+ &stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
+
+ cl_assert(!git_fs_path_exists("stash/when"));
+ cl_assert(git_fs_path_exists("stash/bundle/vendor/blah"));
+ cl_assert(git_fs_path_exists("stash/just.ignore"));
+}
+
+void test_stash_save__can_include_untracked_and_ignored_files(void)
+{
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED | GIT_STASH_INCLUDE_IGNORED));
+
+ assert_commit_message_contains("refs/stash^3", "untracked files on master: ");
+
+ assert_blob_oid("refs/stash^3:what", NULL);
+ assert_blob_oid("refs/stash^3:how", NULL);
+ assert_blob_oid("refs/stash^3:who", NULL);
+ assert_blob_oid("refs/stash^3:when", "b6ed15e81e2593d7bb6265eb4a991d29dc3e628b");
+ assert_blob_oid("refs/stash^3:just.ignore", "78925fb1236b98b37a35e9723033e627f97aa88b");
+
+ cl_assert(!git_fs_path_exists("stash/just.ignore"));
+}
+
+/*
+ * Note: this test was flaky prior to fixing #4101 -- run it several
+ * times to get a failure. The issues is that whether the fast
+ * (stat-only) codepath is used inside stash's diff operation depends
+ * on whether files are "racily clean", and there doesn't seem to be
+ * an easy way to force the exact required state.
+ */
+void test_stash_save__untracked_regression(void)
+{
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ const char *paths[] = {"what", "where", "how", "why"};
+ git_reference *head;
+ git_commit *head_commit;
+ git_str untracked_dir;
+
+ const char* workdir = git_repository_workdir(repo);
+
+ git_str_init(&untracked_dir, 0);
+ git_str_printf(&untracked_dir, "%sz", workdir);
+
+ cl_assert(!p_mkdir(untracked_dir.ptr, 0777));
+
+ cl_git_pass(git_repository_head(&head, repo));
+
+ cl_git_pass(git_reference_peel((git_object **)&head_commit, head, GIT_OBJECT_COMMIT));
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ opts.paths.strings = (char **)paths;
+ opts.paths.count = 4;
+
+ cl_git_pass(git_checkout_tree(repo, (git_object*)head_commit, &opts));
+
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
+
+ assert_commit_message_contains("refs/stash", "WIP on master");
+
+ git_reference_free(head);
+ git_commit_free(head_commit);
+ git_str_dispose(&untracked_dir);
+}
+
+#define MESSAGE "Look Ma! I'm on TV!"
+void test_stash_save__can_accept_a_message(void)
+{
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, MESSAGE, GIT_STASH_DEFAULT));
+
+ assert_commit_message_contains("refs/stash^2", "index on master: ");
+ assert_commit_message_contains("refs/stash", "On master: " MESSAGE);
+}
+
+void test_stash_save__cannot_stash_against_an_unborn_branch(void)
+{
+ git_reference *head;
+
+ cl_git_pass(git_reference_symbolic_create(&head, repo, "HEAD", "refs/heads/unborn", 1, NULL));
+
+ cl_assert_equal_i(GIT_EUNBORNBRANCH,
+ git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
+
+ git_reference_free(head);
+}
+
+void test_stash_save__cannot_stash_against_a_bare_repository(void)
+{
+ git_repository *local;
+
+ cl_git_pass(git_repository_init(&local, "sorry-it-is-a-non-bare-only-party", 1));
+
+ cl_assert_equal_i(GIT_EBAREREPO,
+ git_stash_save(&stash_tip_oid, local, signature, NULL, GIT_STASH_DEFAULT));
+
+ git_repository_free(local);
+}
+
+void test_stash_save__can_stash_against_a_detached_head(void)
+{
+ git_repository_detach_head(repo);
+
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
+
+ assert_commit_message_contains("refs/stash^2", "index on (no branch): ");
+ assert_commit_message_contains("refs/stash", "WIP on (no branch): ");
+}
+
+void test_stash_save__stashing_updates_the_reflog(void)
+{
+ assert_object_oid("refs/stash@{0}", NULL, GIT_OBJECT_COMMIT);
+
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
+
+ assert_object_oid("refs/stash@{0}", git_oid_tostr_s(&stash_tip_oid), GIT_OBJECT_COMMIT);
+ assert_object_oid("refs/stash@{1}", NULL, GIT_OBJECT_COMMIT);
+}
+
+void test_stash_save__multiline_message(void)
+{
+ const char *msg = "This\n\nis a multiline message\n";
+ const git_reflog_entry *entry;
+ git_reflog *reflog;
+
+ assert_object_oid("refs/stash@{0}", NULL, GIT_OBJECT_COMMIT);
+
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, msg, GIT_STASH_DEFAULT));
+
+ cl_git_pass(git_reflog_read(&reflog, repo, "refs/stash"));
+ cl_assert(entry = git_reflog_entry_byindex(reflog, 0));
+ cl_assert_equal_s(git_reflog_entry_message(entry), "On master: This is a multiline message");
+
+ assert_object_oid("refs/stash@{0}", git_oid_tostr_s(&stash_tip_oid), GIT_OBJECT_COMMIT);
+ assert_commit_message_contains("refs/stash@{0}", msg);
+
+ git_reflog_free(reflog);
+}
+
+void test_stash_save__cannot_stash_when_there_are_no_local_change(void)
+{
+ git_index *index;
+ git_oid stash_tip_oid;
+
+ cl_git_pass(git_repository_index(&index, repo));
+
+ /*
+ * 'what', 'where' and 'who' are being committed.
+ * 'when' remains untracked.
+ */
+ cl_git_pass(git_index_add_bypath(index, "what"));
+ cl_git_pass(git_index_add_bypath(index, "where"));
+ cl_git_pass(git_index_add_bypath(index, "who"));
+
+ cl_repo_commit_from_index(NULL, repo, signature, 0, "Initial commit");
+ git_index_free(index);
+
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
+
+ p_unlink("stash/when");
+ cl_assert_equal_i(GIT_ENOTFOUND,
+ git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
+}
+
+void test_stash_save__can_stage_normal_then_stage_untracked(void)
+{
+ /*
+ * $ git ls-tree stash@{1}^0
+ * 100644 blob ac4d88de61733173d9959e4b77c69b9f17a00980 .gitignore
+ * 100644 blob e6d64adb2c7f3eb8feb493b556cc8070dca379a3 how
+ * 100644 blob bc99dc98b3eba0e9157e94769cd4d49cb49de449 what
+ * 100644 blob a0400d4954659306a976567af43125a0b1aa8595 who
+ *
+ * $ git ls-tree stash@{1}^1
+ * 100644 blob ac4d88de61733173d9959e4b77c69b9f17a00980 .gitignore
+ * 100644 blob ac790413e2d7a26c3767e78c57bb28716686eebc how
+ * 100644 blob ce013625030ba8dba906f756967f9e9ca394464a what
+ * 100644 blob cc628ccd10742baea8241c5924df992b5c019f71 who
+ *
+ * $ git ls-tree stash@{1}^2
+ * 100644 blob ac4d88de61733173d9959e4b77c69b9f17a00980 .gitignore
+ * 100644 blob e6d64adb2c7f3eb8feb493b556cc8070dca379a3 how
+ * 100644 blob dd7e1c6f0fefe118f0b63d9f10908c460aa317a6 what
+ * 100644 blob cc628ccd10742baea8241c5924df992b5c019f71 who
+ *
+ * $ git ls-tree stash@{1}^3
+ * fatal: Not a valid object name stash@{1}^3
+ *
+ * $ git ls-tree stash@{0}^0
+ * 100644 blob ac4d88de61733173d9959e4b77c69b9f17a00980 .gitignore
+ * 100644 blob ac790413e2d7a26c3767e78c57bb28716686eebc how
+ * 100644 blob ce013625030ba8dba906f756967f9e9ca394464a what
+ * 100644 blob cc628ccd10742baea8241c5924df992b5c019f71 who
+ *
+ * $ git ls-tree stash@{0}^1
+ * 100644 blob ac4d88de61733173d9959e4b77c69b9f17a00980 .gitignore
+ * 100644 blob ac790413e2d7a26c3767e78c57bb28716686eebc how
+ * 100644 blob ce013625030ba8dba906f756967f9e9ca394464a what
+ * 100644 blob cc628ccd10742baea8241c5924df992b5c019f71 who
+ *
+ * $ git ls-tree stash@{0}^2
+ * 100644 blob ac4d88de61733173d9959e4b77c69b9f17a00980 .gitignore
+ * 100644 blob ac790413e2d7a26c3767e78c57bb28716686eebc how
+ * 100644 blob ce013625030ba8dba906f756967f9e9ca394464a what
+ * 100644 blob cc628ccd10742baea8241c5924df992b5c019f71 who
+ *
+ * $ git ls-tree stash@{0}^3
+ * 100644 blob b6ed15e81e2593d7bb6265eb4a991d29dc3e628b when
+ */
+
+ assert_status(repo, "what", GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED);
+ assert_status(repo, "how", GIT_STATUS_INDEX_MODIFIED);
+ assert_status(repo, "who", GIT_STATUS_WT_MODIFIED);
+ assert_status(repo, "when", GIT_STATUS_WT_NEW);
+ assert_status(repo, "just.ignore", GIT_STATUS_IGNORED);
+
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
+ assert_status(repo, "what", GIT_STATUS_CURRENT);
+ assert_status(repo, "how", GIT_STATUS_CURRENT);
+ assert_status(repo, "who", GIT_STATUS_CURRENT);
+ assert_status(repo, "when", GIT_STATUS_WT_NEW);
+ assert_status(repo, "just.ignore", GIT_STATUS_IGNORED);
+
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
+ assert_status(repo, "what", GIT_STATUS_CURRENT);
+ assert_status(repo, "how", GIT_STATUS_CURRENT);
+ assert_status(repo, "who", GIT_STATUS_CURRENT);
+ assert_status(repo, "when", GIT_ENOTFOUND);
+ assert_status(repo, "just.ignore", GIT_STATUS_IGNORED);
+
+
+ assert_blob_oid("stash@{1}^0:what", "bc99dc98b3eba0e9157e94769cd4d49cb49de449"); /* see you later */
+ assert_blob_oid("stash@{1}^0:how", "e6d64adb2c7f3eb8feb493b556cc8070dca379a3"); /* not so small and */
+ assert_blob_oid("stash@{1}^0:who", "a0400d4954659306a976567af43125a0b1aa8595"); /* funky world */
+ assert_blob_oid("stash@{1}^0:when", NULL);
+
+ assert_blob_oid("stash@{1}^2:what", "dd7e1c6f0fefe118f0b63d9f10908c460aa317a6"); /* goodbye */
+ assert_blob_oid("stash@{1}^2:how", "e6d64adb2c7f3eb8feb493b556cc8070dca379a3"); /* not so small and */
+ assert_blob_oid("stash@{1}^2:who", "cc628ccd10742baea8241c5924df992b5c019f71"); /* world */
+ assert_blob_oid("stash@{1}^2:when", NULL);
+
+ assert_object_oid("stash@{1}^3", NULL, GIT_OBJECT_COMMIT);
+
+ assert_blob_oid("stash@{0}^0:what", "ce013625030ba8dba906f756967f9e9ca394464a"); /* hello */
+ assert_blob_oid("stash@{0}^0:how", "ac790413e2d7a26c3767e78c57bb28716686eebc"); /* small */
+ assert_blob_oid("stash@{0}^0:who", "cc628ccd10742baea8241c5924df992b5c019f71"); /* world */
+ assert_blob_oid("stash@{0}^0:when", NULL);
+
+ assert_blob_oid("stash@{0}^2:what", "ce013625030ba8dba906f756967f9e9ca394464a"); /* hello */
+ assert_blob_oid("stash@{0}^2:how", "ac790413e2d7a26c3767e78c57bb28716686eebc"); /* small */
+ assert_blob_oid("stash@{0}^2:who", "cc628ccd10742baea8241c5924df992b5c019f71"); /* world */
+ assert_blob_oid("stash@{0}^2:when", NULL);
+
+ assert_blob_oid("stash@{0}^3:when", "b6ed15e81e2593d7bb6265eb4a991d29dc3e628b"); /* now */
+}
+
+#define EMPTY_TREE "4b825dc642cb6eb9a060e54bf8d69288fbee4904"
+
+void test_stash_save__including_untracked_without_any_untracked_file_creates_an_empty_tree(void)
+{
+ cl_must_pass(p_unlink("stash/when"));
+
+ assert_status(repo, "what", GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED);
+ assert_status(repo, "how", GIT_STATUS_INDEX_MODIFIED);
+ assert_status(repo, "who", GIT_STATUS_WT_MODIFIED);
+ assert_status(repo, "when", GIT_ENOTFOUND);
+ assert_status(repo, "just.ignore", GIT_STATUS_IGNORED);
+
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
+
+ assert_object_oid("stash^3^{tree}", EMPTY_TREE, GIT_OBJECT_TREE);
+}
+
+void test_stash_save__ignored_directory(void)
+{
+ cl_git_pass(p_mkdir("stash/ignored_directory", 0777));
+ cl_git_pass(p_mkdir("stash/ignored_directory/sub", 0777));
+ cl_git_mkfile("stash/ignored_directory/sub/some_file", "stuff");
+
+ assert_status(repo, "ignored_directory/sub/some_file", GIT_STATUS_WT_NEW);
+ cl_git_pass(git_ignore_add_rule(repo, "ignored_directory/"));
+ assert_status(repo, "ignored_directory/sub/some_file", GIT_STATUS_IGNORED);
+
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED | GIT_STASH_INCLUDE_IGNORED));
+
+ cl_assert(!git_fs_path_exists("stash/ignored_directory/sub/some_file"));
+ cl_assert(!git_fs_path_exists("stash/ignored_directory/sub"));
+ cl_assert(!git_fs_path_exists("stash/ignored_directory"));
+}
+
+void test_stash_save__skip_submodules(void)
+{
+ git_repository *untracked_repo;
+ cl_git_pass(git_repository_init(&untracked_repo, "stash/untracked_repo", false));
+ cl_git_mkfile("stash/untracked_repo/content", "stuff");
+ git_repository_free(untracked_repo);
+
+ assert_status(repo, "untracked_repo/", GIT_STATUS_WT_NEW);
+
+ cl_git_pass(git_stash_save(
+ &stash_tip_oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
+
+ assert_status(repo, "untracked_repo/", GIT_STATUS_WT_NEW);
+}
+
+void test_stash_save__deleted_in_index_modified_in_workdir(void)
+{
+ git_index *index;
+
+ git_repository_index(&index, repo);
+
+ cl_git_pass(git_index_remove_bypath(index, "who"));
+ cl_git_pass(git_index_write(index));
+
+ assert_status(repo, "who", GIT_STATUS_WT_NEW | GIT_STATUS_INDEX_DELETED);
+
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
+
+ assert_blob_oid("stash@{0}^0:who", "a0400d4954659306a976567af43125a0b1aa8595");
+ assert_blob_oid("stash@{0}^2:who", NULL);
+
+ git_index_free(index);
+}
+
+void test_stash_save__option_paths(void)
+{
+ git_stash_save_options options = GIT_STASH_SAVE_OPTIONS_INIT;
+ char *paths[2] = { "who", "where" };
+
+ options.paths = (git_strarray){
+ paths,
+ 2
+ };
+ options.stasher = signature;
+
+ cl_git_pass(git_stash_save_with_opts(&stash_tip_oid, repo, &options));
+
+ assert_blob_oid("refs/stash:who", "a0400d4954659306a976567af43125a0b1aa8595");
+ assert_blob_oid("refs/stash:where", "e3d6434ec12eb76af8dfa843a64ba6ab91014a0b");
+
+ assert_blob_oid("refs/stash:what", "ce013625030ba8dba906f756967f9e9ca394464a");
+ assert_blob_oid("refs/stash:how", "ac790413e2d7a26c3767e78c57bb28716686eebc");
+ assert_blob_oid("refs/stash:when", NULL);
+ assert_blob_oid("refs/stash:why", NULL);
+ assert_blob_oid("refs/stash:.gitignore", "ac4d88de61733173d9959e4b77c69b9f17a00980");
+ assert_blob_oid("refs/stash:just.ignore", NULL);
+}
diff --git a/tests/libgit2/stash/stash_helpers.c b/tests/libgit2/stash/stash_helpers.c
new file mode 100644
index 0000000..cd0cfbd
--- /dev/null
+++ b/tests/libgit2/stash/stash_helpers.c
@@ -0,0 +1,57 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+#include "stash_helpers.h"
+
+void setup_stash(git_repository *repo, git_signature *signature)
+{
+ git_index *index;
+
+ cl_git_pass(git_repository_index(&index, repo));
+
+ cl_git_mkfile("stash/what", "hello\n"); /* ce013625030ba8dba906f756967f9e9ca394464a */
+ cl_git_mkfile("stash/how", "small\n"); /* ac790413e2d7a26c3767e78c57bb28716686eebc */
+ cl_git_mkfile("stash/who", "world\n"); /* cc628ccd10742baea8241c5924df992b5c019f71 */
+ cl_git_mkfile("stash/when", "now\n"); /* b6ed15e81e2593d7bb6265eb4a991d29dc3e628b */
+ cl_git_mkfile("stash/just.ignore", "me\n"); /* 78925fb1236b98b37a35e9723033e627f97aa88b */
+
+ cl_git_mkfile("stash/.gitignore", "*.ignore\n");
+
+ cl_git_pass(git_index_add_bypath(index, "what"));
+ cl_git_pass(git_index_add_bypath(index, "how"));
+ cl_git_pass(git_index_add_bypath(index, "who"));
+ cl_git_pass(git_index_add_bypath(index, ".gitignore"));
+
+ cl_repo_commit_from_index(NULL, repo, signature, 0, "Initial commit");
+
+ cl_git_rewritefile("stash/what", "goodbye\n"); /* dd7e1c6f0fefe118f0b63d9f10908c460aa317a6 */
+ cl_git_rewritefile("stash/how", "not so small and\n"); /* e6d64adb2c7f3eb8feb493b556cc8070dca379a3 */
+ cl_git_rewritefile("stash/who", "funky world\n"); /* a0400d4954659306a976567af43125a0b1aa8595 */
+ cl_git_mkfile("stash/why", "would anybody use stash?\n"); /* 88c2533e21f098b89c91a431d8075cbde422a51 */
+ cl_git_mkfile("stash/where", "????\n"); /* e08f7fbb9a42a0c5367cf8b349f1f08c3d56bd72 */
+
+ cl_git_pass(git_index_add_bypath(index, "what"));
+ cl_git_pass(git_index_add_bypath(index, "how"));
+ cl_git_pass(git_index_add_bypath(index, "why"));
+ cl_git_pass(git_index_add_bypath(index, "where"));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_rewritefile("stash/what", "see you later\n"); /* bc99dc98b3eba0e9157e94769cd4d49cb49de449 */
+ cl_git_mkfile("stash/where", "....\n"); /* e3d6434ec12eb76af8dfa843a64ba6ab91014a0b */
+
+ git_index_free(index);
+}
+
+void assert_status(
+ git_repository *repo,
+ const char *path,
+ int status_flags)
+{
+ unsigned int status;
+
+ if (status_flags < 0)
+ cl_assert_equal_i(status_flags, git_status_file(&status, repo, path));
+ else {
+ cl_git_pass(git_status_file(&status, repo, path));
+ cl_assert_equal_i((unsigned int)status_flags, status);
+ }
+}
diff --git a/tests/libgit2/stash/stash_helpers.h b/tests/libgit2/stash/stash_helpers.h
new file mode 100644
index 0000000..66d758f
--- /dev/null
+++ b/tests/libgit2/stash/stash_helpers.h
@@ -0,0 +1,8 @@
+void setup_stash(
+ git_repository *repo,
+ git_signature *signature);
+
+void assert_status(
+ git_repository *repo,
+ const char *path,
+ int status_flags);
diff --git a/tests/libgit2/stash/submodules.c b/tests/libgit2/stash/submodules.c
new file mode 100644
index 0000000..8cadca0
--- /dev/null
+++ b/tests/libgit2/stash/submodules.c
@@ -0,0 +1,83 @@
+#include "clar_libgit2.h"
+#include "stash_helpers.h"
+#include "../submodule/submodule_helpers.h"
+
+static git_repository *repo;
+static git_signature *signature;
+static git_oid stash_tip_oid;
+
+static git_submodule *sm;
+
+void test_stash_submodules__initialize(void)
+{
+ cl_git_pass(git_signature_new(&signature, "nulltoken", "emeric.fermas@gmail.com", 1323847743, 60)); /* Wed Dec 14 08:29:03 2011 +0100 */
+
+ repo = setup_fixture_submodules();
+
+ cl_git_pass(git_submodule_lookup(&sm, repo, "testrepo"));
+}
+
+void test_stash_submodules__cleanup(void)
+{
+ git_submodule_free(sm);
+ sm = NULL;
+
+ git_signature_free(signature);
+ signature = NULL;
+}
+
+void test_stash_submodules__does_not_stash_modified_submodules(void)
+{
+ static git_index *smindex;
+ static git_repository *smrepo;
+
+ assert_status(repo, "modified", GIT_STATUS_WT_MODIFIED);
+
+ /* modify file in submodule */
+ cl_git_rewritefile("submodules/testrepo/README", "heyheyhey");
+ assert_status(repo, "testrepo", GIT_STATUS_WT_MODIFIED);
+
+ /* add file to index in submodule */
+ cl_git_pass(git_submodule_open(&smrepo, sm));
+ cl_git_pass(git_repository_index(&smindex, smrepo));
+ cl_git_pass(git_index_add_bypath(smindex, "README"));
+
+ /* commit changed index of submodule */
+ cl_repo_commit_from_index(NULL, smrepo, NULL, 1372350000, "Modify it");
+ assert_status(repo, "testrepo", GIT_STATUS_WT_MODIFIED);
+
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
+
+ assert_status(repo, "testrepo", GIT_STATUS_WT_MODIFIED);
+ assert_status(repo, "modified", GIT_STATUS_CURRENT);
+
+ git_index_free(smindex);
+ git_repository_free(smrepo);
+}
+
+void test_stash_submodules__stash_is_empty_with_modified_submodules(void)
+{
+ static git_index *smindex;
+ static git_repository *smrepo;
+
+ cl_git_pass(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT));
+ assert_status(repo, "modified", GIT_STATUS_CURRENT);
+
+ /* modify file in submodule */
+ cl_git_rewritefile("submodules/testrepo/README", "heyheyhey");
+ assert_status(repo, "testrepo", GIT_STATUS_WT_MODIFIED);
+
+ /* add file to index in submodule */
+ cl_git_pass(git_submodule_open(&smrepo, sm));
+ cl_git_pass(git_repository_index(&smindex, smrepo));
+ cl_git_pass(git_index_add_bypath(smindex, "README"));
+
+ /* commit changed index of submodule */
+ cl_repo_commit_from_index(NULL, smrepo, NULL, 1372350000, "Modify it");
+ assert_status(repo, "testrepo", GIT_STATUS_WT_MODIFIED);
+
+ cl_git_fail_with(git_stash_save(&stash_tip_oid, repo, signature, NULL, GIT_STASH_DEFAULT), GIT_ENOTFOUND);
+
+ git_index_free(smindex);
+ git_repository_free(smrepo);
+}
diff --git a/tests/libgit2/status/renames.c b/tests/libgit2/status/renames.c
new file mode 100644
index 0000000..d5cf87d
--- /dev/null
+++ b/tests/libgit2/status/renames.c
@@ -0,0 +1,844 @@
+#include "clar_libgit2.h"
+#include "path.h"
+#include "posix.h"
+#include "status_helpers.h"
+#include "util.h"
+#include "status.h"
+
+static git_repository *g_repo = NULL;
+
+void test_status_renames__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("renames");
+
+ cl_repo_set_bool(g_repo, "core.autocrlf", false);
+}
+
+void test_status_renames__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static void _rename_helper(
+ git_repository *repo, const char *from, const char *to, const char *extra)
+{
+ git_str oldpath = GIT_STR_INIT, newpath = GIT_STR_INIT;
+
+ cl_git_pass(git_str_joinpath(
+ &oldpath, git_repository_workdir(repo), from));
+ cl_git_pass(git_str_joinpath(
+ &newpath, git_repository_workdir(repo), to));
+
+ cl_git_pass(p_rename(oldpath.ptr, newpath.ptr));
+
+ if (extra)
+ cl_git_append2file(newpath.ptr, extra);
+
+ git_str_dispose(&oldpath);
+ git_str_dispose(&newpath);
+}
+
+#define rename_file(R,O,N) _rename_helper((R), (O), (N), NULL)
+#define rename_and_edit_file(R,O,N) \
+ _rename_helper((R), (O), (N), "Added at the end to keep similarity!")
+
+struct status_entry {
+ git_status_t status;
+ const char *oldname;
+ const char *newname;
+};
+
+static void check_status(
+ git_status_list *status_list,
+ struct status_entry *expected_list,
+ size_t expected_len)
+{
+ const git_status_entry *actual;
+ const struct status_entry *expected;
+ const char *oldname, *newname;
+ size_t i, files_in_status = git_status_list_entrycount(status_list);
+
+ cl_assert_equal_sz(expected_len, files_in_status);
+
+ for (i = 0; i < expected_len; i++) {
+ actual = git_status_byindex(status_list, i);
+ expected = &expected_list[i];
+
+ oldname = actual->head_to_index ? actual->head_to_index->old_file.path :
+ actual->index_to_workdir ? actual->index_to_workdir->old_file.path : NULL;
+
+ newname = actual->index_to_workdir ? actual->index_to_workdir->new_file.path :
+ actual->head_to_index ? actual->head_to_index->new_file.path : NULL;
+
+ cl_assert_equal_i_fmt(expected->status, actual->status, "%04x");
+
+ if (expected->oldname) {
+ cl_assert(oldname != NULL);
+ cl_assert_equal_s(oldname, expected->oldname);
+ } else {
+ cl_assert(oldname == NULL);
+ }
+
+ if (actual->status & (GIT_STATUS_INDEX_RENAMED|GIT_STATUS_WT_RENAMED)) {
+ if (expected->newname) {
+ cl_assert(newname != NULL);
+ cl_assert_equal_s(newname, expected->newname);
+ } else {
+ cl_assert(newname == NULL);
+ }
+ }
+ }
+}
+
+void test_status_renames__head2index_one(void)
+{
+ git_index *index;
+ git_status_list *statuslist;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ struct status_entry expected[] = {
+ { GIT_STATUS_INDEX_RENAMED, "ikeepsix.txt", "newname.txt" },
+ };
+
+ opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ rename_file(g_repo, "ikeepsix.txt", "newname.txt");
+
+ cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
+ cl_git_pass(git_index_add_bypath(index, "newname.txt"));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ check_status(statuslist, expected, 1);
+ git_status_list_free(statuslist);
+
+ git_index_free(index);
+}
+
+void test_status_renames__head2index_two(void)
+{
+ git_index *index;
+ git_status_list *statuslist;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ struct status_entry expected[] = {
+ { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED,
+ "sixserving.txt", "aaa.txt" },
+ { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED,
+ "untimely.txt", "bbb.txt" },
+ { GIT_STATUS_INDEX_RENAMED, "songof7cities.txt", "ccc.txt" },
+ { GIT_STATUS_INDEX_RENAMED, "ikeepsix.txt", "ddd.txt" },
+ };
+
+ opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ rename_file(g_repo, "ikeepsix.txt", "ddd.txt");
+ rename_and_edit_file(g_repo, "sixserving.txt", "aaa.txt");
+ rename_file(g_repo, "songof7cities.txt", "ccc.txt");
+ rename_and_edit_file(g_repo, "untimely.txt", "bbb.txt");
+
+ cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
+ cl_git_pass(git_index_remove_bypath(index, "sixserving.txt"));
+ cl_git_pass(git_index_remove_bypath(index, "songof7cities.txt"));
+ cl_git_pass(git_index_remove_bypath(index, "untimely.txt"));
+ cl_git_pass(git_index_add_bypath(index, "ddd.txt"));
+ cl_git_pass(git_index_add_bypath(index, "aaa.txt"));
+ cl_git_pass(git_index_add_bypath(index, "ccc.txt"));
+ cl_git_pass(git_index_add_bypath(index, "bbb.txt"));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ check_status(statuslist, expected, 4);
+ git_status_list_free(statuslist);
+
+ git_index_free(index);
+}
+
+void test_status_renames__head2index_no_rename_from_rewrite(void)
+{
+ git_index *index;
+ git_status_list *statuslist;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ struct status_entry expected[] = {
+ { GIT_STATUS_INDEX_MODIFIED, "ikeepsix.txt", "ikeepsix.txt" },
+ { GIT_STATUS_INDEX_MODIFIED, "sixserving.txt", "sixserving.txt" },
+ };
+
+ opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ rename_file(g_repo, "ikeepsix.txt", "_temp_.txt");
+ rename_file(g_repo, "sixserving.txt", "ikeepsix.txt");
+ rename_file(g_repo, "_temp_.txt", "sixserving.txt");
+
+ cl_git_pass(git_index_add_bypath(index, "ikeepsix.txt"));
+ cl_git_pass(git_index_add_bypath(index, "sixserving.txt"));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ check_status(statuslist, expected, 2);
+ git_status_list_free(statuslist);
+
+ git_index_free(index);
+}
+
+void test_status_renames__head2index_rename_from_rewrite(void)
+{
+ git_index *index;
+ git_status_list *statuslist;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ struct status_entry expected[] = {
+ { GIT_STATUS_INDEX_RENAMED, "sixserving.txt", "ikeepsix.txt" },
+ { GIT_STATUS_INDEX_RENAMED, "ikeepsix.txt", "sixserving.txt" },
+ };
+
+ opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_FROM_REWRITES;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ rename_file(g_repo, "ikeepsix.txt", "_temp_.txt");
+ rename_file(g_repo, "sixserving.txt", "ikeepsix.txt");
+ rename_file(g_repo, "_temp_.txt", "sixserving.txt");
+
+ cl_git_pass(git_index_add_bypath(index, "ikeepsix.txt"));
+ cl_git_pass(git_index_add_bypath(index, "sixserving.txt"));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ check_status(statuslist, expected, 2);
+ git_status_list_free(statuslist);
+
+ git_index_free(index);
+}
+
+void test_status_renames__index2workdir_one(void)
+{
+ git_status_list *statuslist;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ struct status_entry expected[] = {
+ { GIT_STATUS_WT_RENAMED, "ikeepsix.txt", "newname.txt" },
+ };
+
+ opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
+
+ rename_file(g_repo, "ikeepsix.txt", "newname.txt");
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ check_status(statuslist, expected, 1);
+ git_status_list_free(statuslist);
+}
+
+void test_status_renames__index2workdir_two(void)
+{
+ git_status_list *statuslist;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ struct status_entry expected[] = {
+ { GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED,
+ "sixserving.txt", "aaa.txt" },
+ { GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED,
+ "untimely.txt", "bbb.txt" },
+ { GIT_STATUS_WT_RENAMED, "songof7cities.txt", "ccc.txt" },
+ { GIT_STATUS_WT_RENAMED, "ikeepsix.txt", "ddd.txt" },
+ };
+
+ opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
+
+ rename_file(g_repo, "ikeepsix.txt", "ddd.txt");
+ rename_and_edit_file(g_repo, "sixserving.txt", "aaa.txt");
+ rename_file(g_repo, "songof7cities.txt", "ccc.txt");
+ rename_and_edit_file(g_repo, "untimely.txt", "bbb.txt");
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ check_status(statuslist, expected, 4);
+ git_status_list_free(statuslist);
+}
+
+void test_status_renames__index2workdir_rename_from_rewrite(void)
+{
+ git_index *index;
+ git_status_list *statuslist;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ struct status_entry expected[] = {
+ { GIT_STATUS_WT_RENAMED, "sixserving.txt", "ikeepsix.txt" },
+ { GIT_STATUS_WT_RENAMED, "ikeepsix.txt", "sixserving.txt" },
+ };
+
+ opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_FROM_REWRITES;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ rename_file(g_repo, "ikeepsix.txt", "_temp_.txt");
+ rename_file(g_repo, "sixserving.txt", "ikeepsix.txt");
+ rename_file(g_repo, "_temp_.txt", "sixserving.txt");
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ check_status(statuslist, expected, 2);
+ git_status_list_free(statuslist);
+
+ git_index_free(index);
+}
+
+void test_status_renames__both_one(void)
+{
+ git_index *index;
+ git_status_list *statuslist;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ struct status_entry expected[] = {
+ { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
+ "ikeepsix.txt", "newname-workdir.txt" },
+ };
+
+ opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ rename_file(g_repo, "ikeepsix.txt", "newname-index.txt");
+
+ cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
+ cl_git_pass(git_index_add_bypath(index, "newname-index.txt"));
+ cl_git_pass(git_index_write(index));
+
+ rename_file(g_repo, "newname-index.txt", "newname-workdir.txt");
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ check_status(statuslist, expected, 1);
+ git_status_list_free(statuslist);
+
+ git_index_free(index);
+}
+
+void test_status_renames__both_two(void)
+{
+ git_index *index;
+ git_status_list *statuslist;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ struct status_entry expected[] = {
+ { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED |
+ GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED,
+ "ikeepsix.txt", "ikeepsix-both.txt" },
+ { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED,
+ "sixserving.txt", "sixserving-index.txt" },
+ { GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED,
+ "songof7cities.txt", "songof7cities-workdir.txt" },
+ { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
+ "untimely.txt", "untimely-both.txt" },
+ };
+
+ opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ rename_and_edit_file(g_repo, "ikeepsix.txt", "ikeepsix-index.txt");
+ rename_and_edit_file(g_repo, "sixserving.txt", "sixserving-index.txt");
+ rename_file(g_repo, "untimely.txt", "untimely-index.txt");
+
+ cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
+ cl_git_pass(git_index_remove_bypath(index, "sixserving.txt"));
+ cl_git_pass(git_index_remove_bypath(index, "untimely.txt"));
+ cl_git_pass(git_index_add_bypath(index, "ikeepsix-index.txt"));
+ cl_git_pass(git_index_add_bypath(index, "sixserving-index.txt"));
+ cl_git_pass(git_index_add_bypath(index, "untimely-index.txt"));
+ cl_git_pass(git_index_write(index));
+
+ rename_and_edit_file(g_repo, "ikeepsix-index.txt", "ikeepsix-both.txt");
+ rename_and_edit_file(g_repo, "songof7cities.txt", "songof7cities-workdir.txt");
+ rename_file(g_repo, "untimely-index.txt", "untimely-both.txt");
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ check_status(statuslist, expected, 4);
+ git_status_list_free(statuslist);
+
+ git_index_free(index);
+}
+
+
+void test_status_renames__both_rename_from_rewrite(void)
+{
+ git_index *index;
+ git_status_list *statuslist;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ struct status_entry expected[] = {
+ { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
+ "songof7cities.txt", "ikeepsix.txt" },
+ { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
+ "ikeepsix.txt", "sixserving.txt" },
+ { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
+ "sixserving.txt", "songof7cities.txt" },
+ };
+
+ opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_FROM_REWRITES;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ rename_file(g_repo, "ikeepsix.txt", "_temp_.txt");
+ rename_file(g_repo, "sixserving.txt", "ikeepsix.txt");
+ rename_file(g_repo, "songof7cities.txt", "sixserving.txt");
+ rename_file(g_repo, "_temp_.txt", "songof7cities.txt");
+
+ cl_git_pass(git_index_add_bypath(index, "ikeepsix.txt"));
+ cl_git_pass(git_index_add_bypath(index, "sixserving.txt"));
+ cl_git_pass(git_index_add_bypath(index, "songof7cities.txt"));
+ cl_git_pass(git_index_write(index));
+
+ rename_file(g_repo, "songof7cities.txt", "_temp_.txt");
+ rename_file(g_repo, "ikeepsix.txt", "songof7cities.txt");
+ rename_file(g_repo, "sixserving.txt", "ikeepsix.txt");
+ rename_file(g_repo, "_temp_.txt", "sixserving.txt");
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ check_status(statuslist, expected, 3);
+ git_status_list_free(statuslist);
+
+ git_index_free(index);
+}
+
+void test_status_renames__rewrites_only_for_renames(void)
+{
+ git_index *index;
+ git_status_list *statuslist;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ struct status_entry expected[] = {
+ { GIT_STATUS_WT_MODIFIED, "ikeepsix.txt", "ikeepsix.txt" },
+ };
+
+ opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_FROM_REWRITES;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ cl_git_rewritefile("renames/ikeepsix.txt",
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n" \
+ "This is enough content for the file to be rewritten.\n");
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ check_status(statuslist, expected, 1);
+ git_status_list_free(statuslist);
+
+ git_index_free(index);
+}
+
+void test_status_renames__both_casechange_one(void)
+{
+ git_index *index;
+ git_status_list *statuslist;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ int index_caps;
+ struct status_entry expected_icase[] = {
+ { GIT_STATUS_INDEX_RENAMED,
+ "ikeepsix.txt", "IKeepSix.txt" },
+ };
+ struct status_entry expected_case[] = {
+ { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
+ "ikeepsix.txt", "IKEEPSIX.txt" },
+ };
+
+ opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ index_caps = git_index_caps(index);
+
+ rename_file(g_repo, "ikeepsix.txt", "IKeepSix.txt");
+
+ cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
+ cl_git_pass(git_index_add_bypath(index, "IKeepSix.txt"));
+ cl_git_pass(git_index_write(index));
+
+ /* on a case-insensitive file system, this change won't matter.
+ * on a case-sensitive one, it will.
+ */
+ rename_file(g_repo, "IKeepSix.txt", "IKEEPSIX.txt");
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+
+ check_status(statuslist, (index_caps & GIT_INDEX_CAPABILITY_IGNORE_CASE) ?
+ expected_icase : expected_case, 1);
+
+ git_status_list_free(statuslist);
+
+ git_index_free(index);
+}
+
+void test_status_renames__both_casechange_two(void)
+{
+ git_index *index;
+ git_status_list *statuslist;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ int index_caps;
+ struct status_entry expected_icase[] = {
+ { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED |
+ GIT_STATUS_WT_MODIFIED,
+ "ikeepsix.txt", "IKeepSix.txt" },
+ { GIT_STATUS_INDEX_MODIFIED,
+ "sixserving.txt", "sixserving.txt" },
+ { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_MODIFIED,
+ "songof7cities.txt", "songof7.txt" },
+ { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
+ "untimely.txt", "untimeliest.txt" }
+ };
+ struct status_entry expected_case[] = {
+ { GIT_STATUS_INDEX_RENAMED |
+ GIT_STATUS_WT_MODIFIED | GIT_STATUS_WT_RENAMED,
+ "songof7cities.txt", "SONGOF7.txt" },
+ { GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_RENAMED,
+ "sixserving.txt", "SixServing.txt" },
+ { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_INDEX_MODIFIED |
+ GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED,
+ "ikeepsix.txt", "ikeepsix.txt" },
+ { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED,
+ "untimely.txt", "untimeliest.txt" }
+ };
+
+ opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ index_caps = git_index_caps(index);
+
+ rename_and_edit_file(g_repo, "ikeepsix.txt", "IKeepSix.txt");
+ rename_and_edit_file(g_repo, "sixserving.txt", "sixserving.txt");
+ rename_file(g_repo, "songof7cities.txt", "songof7.txt");
+ rename_file(g_repo, "untimely.txt", "untimelier.txt");
+
+ cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
+ cl_git_pass(git_index_remove_bypath(index, "sixserving.txt"));
+ cl_git_pass(git_index_remove_bypath(index, "songof7cities.txt"));
+ cl_git_pass(git_index_remove_bypath(index, "untimely.txt"));
+ cl_git_pass(git_index_add_bypath(index, "IKeepSix.txt"));
+ cl_git_pass(git_index_add_bypath(index, "sixserving.txt"));
+ cl_git_pass(git_index_add_bypath(index, "songof7.txt"));
+ cl_git_pass(git_index_add_bypath(index, "untimelier.txt"));
+ cl_git_pass(git_index_write(index));
+
+ rename_and_edit_file(g_repo, "IKeepSix.txt", "ikeepsix.txt");
+ rename_file(g_repo, "sixserving.txt", "SixServing.txt");
+ rename_and_edit_file(g_repo, "songof7.txt", "SONGOF7.txt");
+ rename_file(g_repo, "untimelier.txt", "untimeliest.txt");
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+
+ check_status(statuslist, (index_caps & GIT_INDEX_CAPABILITY_IGNORE_CASE) ?
+ expected_icase : expected_case, 4);
+
+ git_status_list_free(statuslist);
+
+ git_index_free(index);
+}
+
+void test_status_renames__zero_byte_file_does_not_fail(void)
+{
+ git_status_list *statuslist;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+
+ struct status_entry expected[] = {
+ { GIT_STATUS_WT_DELETED, "ikeepsix.txt", "ikeepsix.txt" },
+ { GIT_STATUS_WT_NEW, "zerobyte.txt", "zerobyte.txt" },
+ };
+
+ opts.flags |= GIT_STATUS_OPT_RENAMES_FROM_REWRITES |
+ GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX |
+ GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR |
+ GIT_STATUS_OPT_INCLUDE_IGNORED |
+ GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS |
+ GIT_STATUS_SHOW_INDEX_AND_WORKDIR |
+ GIT_STATUS_OPT_RECURSE_IGNORED_DIRS;
+
+ p_unlink("renames/ikeepsix.txt");
+ cl_git_mkfile("renames/zerobyte.txt", "");
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ check_status(statuslist, expected, 2);
+ git_status_list_free(statuslist);
+}
+
+#ifdef GIT_USE_ICONV
+static char *nfc = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D";
+static char *nfd = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D";
+#endif
+
+/*
+ * Create a file in NFD (canonically decomposed) format. Ensure
+ * that when core.precomposeunicode is false that we return paths
+ * in NFD, but when core.precomposeunicode is true, then we
+ * return paths precomposed (in NFC).
+ */
+void test_status_renames__precomposed_unicode_rename(void)
+{
+#ifdef GIT_USE_ICONV
+ git_status_list *statuslist;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ struct status_entry expected0[] = {
+ { GIT_STATUS_WT_NEW, nfd, NULL },
+ { GIT_STATUS_WT_DELETED, "sixserving.txt", NULL },
+ };
+ struct status_entry expected1[] = {
+ { GIT_STATUS_WT_RENAMED, "sixserving.txt", nfd },
+ };
+ struct status_entry expected2[] = {
+ { GIT_STATUS_WT_DELETED, "sixserving.txt", NULL },
+ { GIT_STATUS_WT_NEW, nfc, NULL },
+ };
+ struct status_entry expected3[] = {
+ { GIT_STATUS_WT_RENAMED, "sixserving.txt", nfc },
+ };
+
+ rename_file(g_repo, "sixserving.txt", nfd);
+
+ opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
+
+ cl_repo_set_bool(g_repo, "core.precomposeunicode", false);
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ check_status(statuslist, expected0, ARRAY_SIZE(expected0));
+ git_status_list_free(statuslist);
+
+ opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ check_status(statuslist, expected1, ARRAY_SIZE(expected1));
+ git_status_list_free(statuslist);
+
+ cl_repo_set_bool(g_repo, "core.precomposeunicode", true);
+
+ opts.flags &= ~GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ check_status(statuslist, expected2, ARRAY_SIZE(expected2));
+ git_status_list_free(statuslist);
+
+ opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ check_status(statuslist, expected3, ARRAY_SIZE(expected3));
+ git_status_list_free(statuslist);
+#endif
+}
+
+void test_status_renames__precomposed_unicode_toggle_is_rename(void)
+{
+#ifdef GIT_USE_ICONV
+ git_status_list *statuslist;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ struct status_entry expected0[] = {
+ { GIT_STATUS_INDEX_RENAMED, "ikeepsix.txt", nfd },
+ };
+ struct status_entry expected1[] = {
+ { GIT_STATUS_WT_RENAMED, nfd, nfc },
+ };
+ struct status_entry expected2[] = {
+ { GIT_STATUS_INDEX_RENAMED, nfd, nfc },
+ };
+ struct status_entry expected3[] = {
+ { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_RENAMED, nfd, nfd },
+ };
+
+ cl_repo_set_bool(g_repo, "core.precomposeunicode", false);
+ rename_file(g_repo, "ikeepsix.txt", nfd);
+
+ {
+ git_index *index;
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
+ cl_git_pass(git_index_add_bypath(index, nfd));
+ cl_git_pass(git_index_write(index));
+ git_index_free(index);
+ }
+
+ opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX |
+ GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ check_status(statuslist, expected0, ARRAY_SIZE(expected0));
+ git_status_list_free(statuslist);
+
+ cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "commit nfd");
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ cl_assert_equal_sz(0, git_status_list_entrycount(statuslist));
+ git_status_list_free(statuslist);
+
+ cl_repo_set_bool(g_repo, "core.precomposeunicode", true);
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ check_status(statuslist, expected1, ARRAY_SIZE(expected1));
+ git_status_list_free(statuslist);
+
+ {
+ git_index *index;
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_remove_bypath(index, nfd));
+ cl_git_pass(git_index_add_bypath(index, nfc));
+ cl_git_pass(git_index_write(index));
+ git_index_free(index);
+ }
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ check_status(statuslist, expected2, ARRAY_SIZE(expected2));
+ git_status_list_free(statuslist);
+
+ cl_repo_set_bool(g_repo, "core.precomposeunicode", false);
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ check_status(statuslist, expected3, ARRAY_SIZE(expected3));
+ git_status_list_free(statuslist);
+#endif
+}
+
+void test_status_renames__rename_threshold(void)
+{
+ git_index *index;
+ git_status_list *statuslist;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+
+ _rename_helper(g_repo, "ikeepsix.txt", "newname.txt",
+ "Line 1\n" \
+ "Line 2\n" \
+ "Line 3\n" \
+ "Line 4\n" \
+ "Line 5\n" \
+ "Line 6\n" \
+ "Line 7\n" \
+ "Line 8\n" \
+ "Line 9\n"
+ );
+
+ opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
+ opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ /* Default threshold */
+ {
+ struct status_entry expected[] = {
+ { GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED, "ikeepsix.txt", "newname.txt" },
+ };
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ check_status(statuslist, expected, 1);
+ git_status_list_free(statuslist);
+ }
+
+ /* Threshold set to 90 */
+ {
+ struct status_entry expected[] = {
+ { GIT_STATUS_WT_DELETED, "ikeepsix.txt", NULL },
+ { GIT_STATUS_WT_NEW, "newname.txt", NULL }
+ };
+
+ opts.rename_threshold = 90;
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ check_status(statuslist, expected, 2);
+ git_status_list_free(statuslist);
+ }
+
+ /* Threshold set to 25 */
+ {
+ struct status_entry expected[] = {
+ { GIT_STATUS_WT_RENAMED | GIT_STATUS_WT_MODIFIED, "ikeepsix.txt", "newname.txt" },
+ };
+
+ opts.rename_threshold = 25;
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ check_status(statuslist, expected, 1);
+ git_status_list_free(statuslist);
+ }
+
+ git_index_free(index);
+}
+
+void test_status_renames__case_insensitive_h2i_and_i2wc(void)
+{
+ git_status_list *statuslist;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ git_reference *head, *test_branch;
+ git_checkout_options checkout_opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_str path_to_delete = GIT_STR_INIT;
+ git_str path_to_edit = GIT_STR_INIT;
+ git_index *index;
+ git_strarray paths = { NULL, 0 };
+
+ struct status_entry expected[] = {
+ { GIT_STATUS_INDEX_RENAMED | GIT_STATUS_WT_MODIFIED, "sixserving.txt", "sixserving-renamed.txt" },
+ { GIT_STATUS_INDEX_DELETED, "Wow.txt", "Wow.txt" }
+ };
+
+
+ /* Checkout the correct branch */
+ checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+ cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
+ cl_git_pass(git_reference_symbolic_set_target(
+ &test_branch, head, "refs/heads/case-insensitive-status", NULL));
+ cl_git_pass(git_checkout_head(g_repo, &checkout_opts));
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+
+ /* Rename sixserving.txt, delete Wow.txt, and stage those changes */
+ rename_file(g_repo, "sixserving.txt", "sixserving-renamed.txt");
+ cl_git_pass(git_str_joinpath(
+ &path_to_delete, git_repository_workdir(g_repo), "Wow.txt"));
+ cl_git_rmfile(path_to_delete.ptr);
+
+ cl_git_pass(git_index_add_all(index, &paths, GIT_INDEX_ADD_FORCE, NULL, NULL));
+ cl_git_pass(git_index_write(index));
+
+
+ /* Change content of sixserving-renamed.txt */
+ cl_git_pass(git_str_joinpath(
+ &path_to_edit, git_repository_workdir(g_repo), "sixserving-renamed.txt"));
+ cl_git_append2file(path_to_edit.ptr, "New content\n");
+
+ /* Run status */
+ opts.flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR;
+ opts.flags |= GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX;
+ opts.flags |= GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY;
+
+ cl_git_pass(git_status_list_new(&statuslist, g_repo, &opts));
+ check_status(statuslist, expected, 2);
+ git_status_list_free(statuslist);
+
+ git_index_free(index);
+
+ git_str_dispose(&path_to_delete);
+ git_str_dispose(&path_to_edit);
+
+ git_reference_free(head);
+ git_reference_free(test_branch);
+}
diff --git a/tests/libgit2/status/single.c b/tests/libgit2/status/single.c
new file mode 100644
index 0000000..b9659a0
--- /dev/null
+++ b/tests/libgit2/status/single.c
@@ -0,0 +1,46 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "odb.h"
+
+static void
+cleanup__remove_file(void *_file)
+{
+ cl_must_pass(p_unlink((char *)_file));
+}
+
+/* test retrieving OID from a file apart from the ODB */
+void test_status_single__hash_single_file(void)
+{
+ static const char file_name[] = "new_file";
+ static const char file_contents[] = "new_file\n";
+ static const char file_hash[] = "d4fa8600b4f37d7516bef4816ae2c64dbf029e3a";
+
+ git_oid expected_id, actual_id;
+
+ /* initialization */
+ git_oid__fromstr(&expected_id, file_hash, GIT_OID_SHA1);
+ cl_git_mkfile(file_name, file_contents);
+ cl_set_cleanup(&cleanup__remove_file, (void *)file_name);
+
+ cl_git_pass(git_odb__hashfile(&actual_id, file_name, GIT_OBJECT_BLOB, GIT_OID_SHA1));
+ cl_assert_equal_oid(&expected_id, &actual_id);
+}
+
+/* test retrieving OID from an empty file apart from the ODB */
+void test_status_single__hash_single_empty_file(void)
+{
+ static const char file_name[] = "new_empty_file";
+ static const char file_contents[] = "";
+ static const char file_hash[] = "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391";
+
+ git_oid expected_id, actual_id;
+
+ /* initialization */
+ git_oid__fromstr(&expected_id, file_hash, GIT_OID_SHA1);
+ cl_git_mkfile(file_name, file_contents);
+ cl_set_cleanup(&cleanup__remove_file, (void *)file_name);
+
+ cl_git_pass(git_odb__hashfile(&actual_id, file_name, GIT_OBJECT_BLOB, GIT_OID_SHA1));
+ cl_assert_equal_oid(&expected_id, &actual_id);
+}
+
diff --git a/tests/libgit2/status/status_data.h b/tests/libgit2/status/status_data.h
new file mode 100644
index 0000000..09b9827
--- /dev/null
+++ b/tests/libgit2/status/status_data.h
@@ -0,0 +1,326 @@
+#include "status_helpers.h"
+
+/* A utf-8 string with 83 characters, but 249 bytes. */
+static const char *longname = "\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97";
+
+
+/* entries for a plain copy of tests/resources/status */
+
+static const char *entry_paths0[] = {
+ "file_deleted",
+ "ignored_file",
+ "modified_file",
+ "new_file",
+ "staged_changes",
+ "staged_changes_file_deleted",
+ "staged_changes_modified_file",
+ "staged_delete_file_deleted",
+ "staged_delete_modified_file",
+ "staged_new_file",
+ "staged_new_file_deleted_file",
+ "staged_new_file_modified_file",
+
+ "subdir/deleted_file",
+ "subdir/modified_file",
+ "subdir/new_file",
+
+ "\xe8\xbf\x99",
+};
+
+static const unsigned int entry_statuses0[] = {
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_IGNORED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_DELETED,
+ GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_INDEX_DELETED | GIT_STATUS_WT_NEW,
+ GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_INDEX_NEW | GIT_STATUS_WT_DELETED,
+ GIT_STATUS_INDEX_NEW | GIT_STATUS_WT_MODIFIED,
+
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_NEW,
+
+ GIT_STATUS_WT_NEW,
+};
+
+static const int entry_count0 = 16;
+
+/* entries for a copy of tests/resources/status with all content
+ * deleted from the working directory
+ */
+
+static const char *entry_paths2[] = {
+ "current_file",
+ "file_deleted",
+ "modified_file",
+ "staged_changes",
+ "staged_changes_file_deleted",
+ "staged_changes_modified_file",
+ "staged_delete_file_deleted",
+ "staged_delete_modified_file",
+ "staged_new_file",
+ "staged_new_file_deleted_file",
+ "staged_new_file_modified_file",
+ "subdir.txt",
+ "subdir/current_file",
+ "subdir/deleted_file",
+ "subdir/modified_file",
+};
+
+static const unsigned int entry_statuses2[] = {
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED,
+};
+
+static const int entry_count2 = 15;
+
+/* entries for a copy of tests/resources/status with some mods */
+
+static const char *entry_paths3_icase[] = {
+ ".HEADER",
+ "42-is-not-prime.sigh",
+ "current_file",
+ "current_file/",
+ "file_deleted",
+ "ignored_file",
+ "modified_file",
+ "new_file",
+ "README.md",
+ "staged_changes",
+ "staged_changes_file_deleted",
+ "staged_changes_modified_file",
+ "staged_delete_file_deleted",
+ "staged_delete_modified_file",
+ "staged_new_file",
+ "staged_new_file_deleted_file",
+ "staged_new_file_modified_file",
+ "subdir",
+ "subdir/current_file",
+ "subdir/deleted_file",
+ "subdir/modified_file",
+ "\xe8\xbf\x99",
+};
+
+static const unsigned int entry_statuses3_icase[] = {
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_IGNORED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_WT_NEW | GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_NEW,
+};
+
+static const char *entry_paths3[] = {
+ ".HEADER",
+ "42-is-not-prime.sigh",
+ "README.md",
+ "current_file",
+ "current_file/",
+ "file_deleted",
+ "ignored_file",
+ "modified_file",
+ "new_file",
+ "staged_changes",
+ "staged_changes_file_deleted",
+ "staged_changes_modified_file",
+ "staged_delete_file_deleted",
+ "staged_delete_modified_file",
+ "staged_new_file",
+ "staged_new_file_deleted_file",
+ "staged_new_file_modified_file",
+ "subdir",
+ "subdir/current_file",
+ "subdir/deleted_file",
+ "subdir/modified_file",
+ "\xe8\xbf\x99",
+};
+
+static const unsigned int entry_statuses3[] = {
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_IGNORED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_WT_NEW | GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_NEW,
+};
+
+static const int entry_count3 = 22;
+
+
+/* entries for a copy of tests/resources/status with some mods
+ * and different options to the status call
+ */
+
+static const char *entry_paths4[] = {
+ ".new_file",
+ "current_file",
+ "current_file/current_file",
+ "current_file/modified_file",
+ "current_file/new_file",
+ "file_deleted",
+ "modified_file",
+ "new_file",
+ "staged_changes",
+ "staged_changes_file_deleted",
+ "staged_changes_modified_file",
+ "staged_delete_file_deleted",
+ "staged_delete_modified_file",
+ "staged_new_file",
+ "staged_new_file_deleted_file",
+ "staged_new_file_modified_file",
+ "subdir",
+ "subdir/current_file",
+ "subdir/deleted_file",
+ "subdir/modified_file",
+ "zzz_new_dir/new_file",
+ "zzz_new_file",
+ "\xe8\xbf\x99",
+};
+
+static const unsigned int entry_statuses4[] = {
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_WT_NEW | GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_NEW,
+};
+
+static const int entry_count4 = 23;
+
+
+/* entries for a copy of tests/resources/status with options
+ * passed to the status call in order to only get the differences
+ * between the HEAD and the index (changes to be committed)
+ */
+
+static const char *entry_paths5[] = {
+ "staged_changes",
+ "staged_changes_file_deleted",
+ "staged_changes_modified_file",
+ "staged_delete_file_deleted",
+ "staged_delete_modified_file",
+ "staged_new_file",
+ "staged_new_file_deleted_file",
+ "staged_new_file_modified_file",
+};
+
+static const unsigned int entry_statuses5[] = {
+ GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_INDEX_NEW,
+};
+
+static const int entry_count5 = 8;
+
+
+/* entries for a copy of tests/resources/status with options
+ * passed to the status call in order to only get the differences
+ * between the workdir and the index (changes not staged, untracked files)
+ */
+
+static const char *entry_paths6[] = {
+ "file_deleted",
+ "ignored_file",
+ "modified_file",
+ "new_file",
+ "staged_changes_file_deleted",
+ "staged_changes_modified_file",
+ "staged_delete_modified_file",
+ "staged_new_file_deleted_file",
+ "staged_new_file_modified_file",
+ "subdir/deleted_file",
+ "subdir/modified_file",
+ "subdir/new_file",
+ "\xe8\xbf\x99",
+};
+
+static const unsigned int entry_statuses6[] = {
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_IGNORED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_NEW,
+};
+
+static const int entry_count6 = 13;
diff --git a/tests/libgit2/status/status_helpers.c b/tests/libgit2/status/status_helpers.c
new file mode 100644
index 0000000..5d13caa
--- /dev/null
+++ b/tests/libgit2/status/status_helpers.c
@@ -0,0 +1,97 @@
+#include "clar_libgit2.h"
+#include "status_helpers.h"
+
+int cb_status__normal(
+ const char *path, unsigned int status_flags, void *payload)
+{
+ status_entry_counts *counts = payload;
+
+ if (counts->debug)
+ cb_status__print(path, status_flags, NULL);
+
+ if (counts->entry_count >= counts->expected_entry_count)
+ counts->wrong_status_flags_count++;
+ else if (strcmp(path, counts->expected_paths[counts->entry_count]))
+ counts->wrong_sorted_path++;
+ else if (status_flags != counts->expected_statuses[counts->entry_count])
+ counts->wrong_status_flags_count++;
+
+ counts->entry_count++;
+ return 0;
+}
+
+int cb_status__count(const char *p, unsigned int s, void *payload)
+{
+ volatile int *count = (int *)payload;
+
+ GIT_UNUSED(p);
+ GIT_UNUSED(s);
+
+ (*count)++;
+
+ return 0;
+}
+
+int cb_status__single(const char *p, unsigned int s, void *payload)
+{
+ status_entry_single *data = (status_entry_single *)payload;
+
+ if (data->debug)
+ fprintf(stderr, "%02d: %s (%04x)\n", data->count, p, s);
+
+ data->count++;
+ data->status = s;
+
+ return 0;
+}
+
+int cb_status__print(
+ const char *path, unsigned int status_flags, void *payload)
+{
+ char istatus = ' ', wstatus = ' ';
+ int icount = 0, wcount = 0;
+
+ if (status_flags & GIT_STATUS_INDEX_NEW) {
+ istatus = 'A'; icount++;
+ }
+ if (status_flags & GIT_STATUS_INDEX_MODIFIED) {
+ istatus = 'M'; icount++;
+ }
+ if (status_flags & GIT_STATUS_INDEX_DELETED) {
+ istatus = 'D'; icount++;
+ }
+ if (status_flags & GIT_STATUS_INDEX_RENAMED) {
+ istatus = 'R'; icount++;
+ }
+ if (status_flags & GIT_STATUS_INDEX_TYPECHANGE) {
+ istatus = 'T'; icount++;
+ }
+
+ if (status_flags & GIT_STATUS_WT_NEW) {
+ wstatus = 'A'; wcount++;
+ }
+ if (status_flags & GIT_STATUS_WT_MODIFIED) {
+ wstatus = 'M'; wcount++;
+ }
+ if (status_flags & GIT_STATUS_WT_DELETED) {
+ wstatus = 'D'; wcount++;
+ }
+ if (status_flags & GIT_STATUS_WT_TYPECHANGE) {
+ wstatus = 'T'; wcount++;
+ }
+ if (status_flags & GIT_STATUS_IGNORED) {
+ wstatus = 'I'; wcount++;
+ }
+ if (status_flags & GIT_STATUS_WT_UNREADABLE) {
+ wstatus = 'X'; wcount++;
+ }
+
+ fprintf(stderr, "%c%c %s (%d/%d%s)\n",
+ istatus, wstatus, path, icount, wcount,
+ (icount > 1 || wcount > 1) ? " INVALID COMBO" : "");
+
+ if (payload)
+ *((int *)payload) += 1;
+
+ return 0;
+}
diff --git a/tests/libgit2/status/status_helpers.h b/tests/libgit2/status/status_helpers.h
new file mode 100644
index 0000000..464266a
--- /dev/null
+++ b/tests/libgit2/status/status_helpers.h
@@ -0,0 +1,51 @@
+#ifndef INCLUDE_cl_status_helpers_h__
+#define INCLUDE_cl_status_helpers_h__
+
+typedef struct {
+ int wrong_status_flags_count;
+ int wrong_sorted_path;
+ int entry_count;
+ const unsigned int* expected_statuses;
+ const char** expected_paths;
+ int expected_entry_count;
+ const char *file;
+ const char *func;
+ int line;
+ bool debug;
+} status_entry_counts;
+
+#define status_counts_init(counts, paths, statuses) do { \
+ memset(&(counts), 0, sizeof(counts)); \
+ (counts).expected_statuses = (statuses); \
+ (counts).expected_paths = (paths); \
+ (counts).file = __FILE__; \
+ (counts).func = __func__; \
+ (counts).line = __LINE__; \
+ } while (0)
+
+/* cb_status__normal takes payload of "status_entry_counts *" */
+
+extern int cb_status__normal(
+ const char *path, unsigned int status_flags, void *payload);
+
+
+/* cb_status__count takes payload of "int *" */
+
+extern int cb_status__count(const char *p, unsigned int s, void *payload);
+
+
+typedef struct {
+ int count;
+ unsigned int status;
+ bool debug;
+} status_entry_single;
+
+/* cb_status__single takes payload of "status_entry_single *" */
+
+extern int cb_status__single(const char *p, unsigned int s, void *payload);
+
+/* cb_status__print takes optional payload of "int *" */
+
+extern int cb_status__print(const char *p, unsigned int s, void *payload);
+
+#endif
diff --git a/tests/libgit2/status/submodules.c b/tests/libgit2/status/submodules.c
new file mode 100644
index 0000000..90d56d9
--- /dev/null
+++ b/tests/libgit2/status/submodules.c
@@ -0,0 +1,563 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+#include "status_helpers.h"
+#include "../submodule/submodule_helpers.h"
+
+static git_repository *g_repo = NULL;
+
+void test_status_submodules__initialize(void)
+{
+}
+
+void test_status_submodules__cleanup(void)
+{
+}
+
+void test_status_submodules__api(void)
+{
+ git_submodule *sm;
+
+ g_repo = setup_fixture_submodules();
+
+ cl_assert(git_submodule_lookup(NULL, g_repo, "nonexistent") == GIT_ENOTFOUND);
+
+ cl_assert(git_submodule_lookup(NULL, g_repo, "modified") == GIT_ENOTFOUND);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
+ cl_assert(sm != NULL);
+ cl_assert_equal_s("testrepo", git_submodule_name(sm));
+ cl_assert_equal_s("testrepo", git_submodule_path(sm));
+ git_submodule_free(sm);
+}
+
+void test_status_submodules__0(void)
+{
+ int counts = 0;
+
+ g_repo = setup_fixture_submodules();
+
+ cl_assert(git_fs_path_isdir("submodules/.git"));
+ cl_assert(git_fs_path_isdir("submodules/testrepo/.git"));
+ cl_assert(git_fs_path_isfile("submodules/.gitmodules"));
+
+ cl_git_pass(
+ git_status_foreach(g_repo, cb_status__count, &counts)
+ );
+
+ cl_assert_equal_i(6, counts);
+}
+
+static const char *expected_files[] = {
+ ".gitmodules",
+ "added",
+ "deleted",
+ "ignored",
+ "modified",
+ "untracked"
+};
+
+static unsigned int expected_status[] = {
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_IGNORED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_NEW
+};
+
+static int cb_status__match(const char *p, unsigned int s, void *payload)
+{
+ status_entry_counts *counts = payload;
+ int idx = counts->entry_count++;
+
+ clar__assert_equal(
+ counts->file, counts->func, counts->line,
+ "Status path mismatch", 1,
+ "%s", counts->expected_paths[idx], p);
+
+ clar__assert_equal(
+ counts->file, counts->func, counts->line,
+ "Status code mismatch", 1,
+ "%o", counts->expected_statuses[idx], s);
+
+ return 0;
+}
+
+void test_status_submodules__1(void)
+{
+ status_entry_counts counts;
+
+ g_repo = setup_fixture_submodules();
+
+ cl_assert(git_fs_path_isdir("submodules/.git"));
+ cl_assert(git_fs_path_isdir("submodules/testrepo/.git"));
+ cl_assert(git_fs_path_isfile("submodules/.gitmodules"));
+
+ status_counts_init(counts, expected_files, expected_status);
+
+ cl_git_pass( git_status_foreach(g_repo, cb_status__match, &counts) );
+
+ cl_assert_equal_i(6, counts.entry_count);
+}
+
+void test_status_submodules__single_file(void)
+{
+ unsigned int status = 0;
+ g_repo = setup_fixture_submodules();
+ cl_git_pass( git_status_file(&status, g_repo, "testrepo") );
+ cl_assert(!status);
+}
+
+void test_status_submodules__moved_head(void)
+{
+ git_submodule *sm;
+ git_repository *smrepo;
+ git_oid oid;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ status_entry_counts counts;
+ static const char *expected_files_with_sub[] = {
+ ".gitmodules",
+ "added",
+ "deleted",
+ "ignored",
+ "modified",
+ "testrepo",
+ "untracked"
+ };
+ static unsigned int expected_status_with_sub[] = {
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_IGNORED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_NEW
+ };
+
+ g_repo = setup_fixture_submodules();
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
+ cl_git_pass(git_submodule_open(&smrepo, sm));
+ git_submodule_free(sm);
+
+ /* move submodule HEAD to c47800c7266a2be04c571c04d5a6614691ea99bd */
+ cl_git_pass(
+ git_oid__fromstr(&oid, "c47800c7266a2be04c571c04d5a6614691ea99bd", GIT_OID_SHA1));
+ cl_git_pass(git_repository_set_head_detached(smrepo, &oid));
+
+ /* first do a normal status, which should now include the submodule */
+
+ opts.flags = GIT_STATUS_OPT_DEFAULTS;
+
+ status_counts_init(
+ counts, expected_files_with_sub, expected_status_with_sub);
+ cl_git_pass(
+ git_status_foreach_ext(g_repo, &opts, cb_status__match, &counts));
+ cl_assert_equal_i(7, counts.entry_count);
+
+ /* try again with EXCLUDE_SUBMODULES which should skip it */
+
+ opts.flags = GIT_STATUS_OPT_DEFAULTS | GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
+
+ status_counts_init(counts, expected_files, expected_status);
+ cl_git_pass(
+ git_status_foreach_ext(g_repo, &opts, cb_status__match, &counts));
+ cl_assert_equal_i(6, counts.entry_count);
+
+ git_repository_free(smrepo);
+}
+
+void test_status_submodules__dirty_workdir_only(void)
+{
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ status_entry_counts counts;
+ static const char *expected_files_with_sub[] = {
+ ".gitmodules",
+ "added",
+ "deleted",
+ "ignored",
+ "modified",
+ "testrepo",
+ "untracked"
+ };
+ static unsigned int expected_status_with_sub[] = {
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_IGNORED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_NEW
+ };
+
+ g_repo = setup_fixture_submodules();
+
+ cl_git_rewritefile("submodules/testrepo/README", "heyheyhey");
+ cl_git_mkfile("submodules/testrepo/all_new.txt", "never seen before");
+
+ /* first do a normal status, which should now include the submodule */
+
+ opts.flags = GIT_STATUS_OPT_DEFAULTS;
+
+ status_counts_init(
+ counts, expected_files_with_sub, expected_status_with_sub);
+ cl_git_pass(
+ git_status_foreach_ext(g_repo, &opts, cb_status__match, &counts));
+ cl_assert_equal_i(7, counts.entry_count);
+
+ /* try again with EXCLUDE_SUBMODULES which should skip it */
+
+ opts.flags = GIT_STATUS_OPT_DEFAULTS | GIT_STATUS_OPT_EXCLUDE_SUBMODULES;
+
+ status_counts_init(counts, expected_files, expected_status);
+ cl_git_pass(
+ git_status_foreach_ext(g_repo, &opts, cb_status__match, &counts));
+ cl_assert_equal_i(6, counts.entry_count);
+}
+
+void test_status_submodules__uninitialized(void)
+{
+ git_repository *cloned_repo;
+ git_status_list *statuslist;
+
+ g_repo = cl_git_sandbox_init("submod2");
+
+ cl_git_pass(git_clone(&cloned_repo, "submod2", "submod2-clone", NULL));
+
+ cl_git_pass(git_status_list_new(&statuslist, cloned_repo, NULL));
+ cl_assert_equal_i(0, git_status_list_entrycount(statuslist));
+
+ git_status_list_free(statuslist);
+ git_repository_free(cloned_repo);
+ cl_git_sandbox_cleanup();
+}
+
+void test_status_submodules__contained_untracked_repo(void)
+{
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ status_entry_counts counts;
+ git_repository *contained;
+ static const char *expected_files_not_ignored[] = {
+ ".gitmodules",
+ "added",
+ "deleted",
+ "modified",
+ "untracked"
+ };
+ static unsigned int expected_status_not_ignored[] = {
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_NEW,
+ };
+ static const char *expected_files_with_untracked[] = {
+ ".gitmodules",
+ "added",
+ "deleted",
+ "dir/file.md",
+ "modified",
+ "untracked"
+ };
+ static const char *expected_files_with_untracked_dir[] = {
+ ".gitmodules",
+ "added",
+ "deleted",
+ "dir/",
+ "modified",
+ "untracked"
+ };
+ static unsigned int expected_status_with_untracked[] = {
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_NEW
+ };
+
+ g_repo = setup_fixture_submodules();
+
+ /* skip empty directory */
+
+ cl_must_pass(p_mkdir("submodules/dir", 0777));
+ opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED;
+
+ status_counts_init(
+ counts, expected_files_not_ignored, expected_status_not_ignored);
+ cl_git_pass(git_status_foreach_ext(
+ g_repo, &opts, cb_status__match, &counts));
+ cl_assert_equal_i(5, counts.entry_count);
+
+ /* still skipping because empty == ignored */
+
+ opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
+
+ status_counts_init(
+ counts, expected_files_not_ignored, expected_status_not_ignored);
+ cl_git_pass(git_status_foreach_ext(
+ g_repo, &opts, cb_status__match, &counts));
+ cl_assert_equal_i(5, counts.entry_count);
+
+ /* find non-ignored contents of directory */
+
+ cl_git_mkfile("submodules/dir/file.md", "hello");
+ opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
+
+ status_counts_init(
+ counts, expected_files_with_untracked, expected_status_with_untracked);
+ cl_git_pass(git_status_foreach_ext(
+ g_repo, &opts, cb_status__match, &counts));
+ cl_assert_equal_i(6, counts.entry_count);
+
+ /* but skip if all content is ignored */
+
+ cl_git_append2file("submodules/.git/info/exclude", "\n*.md\n\n");
+ opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
+
+ status_counts_init(
+ counts, expected_files_not_ignored, expected_status_not_ignored);
+ cl_git_pass(git_status_foreach_ext(
+ g_repo, &opts, cb_status__match, &counts));
+ cl_assert_equal_i(5, counts.entry_count);
+
+ /* same is true if it contains a git link */
+
+ cl_git_mkfile("submodules/dir/.git", "gitlink: ../.git");
+ opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
+
+ status_counts_init(
+ counts, expected_files_not_ignored, expected_status_not_ignored);
+ cl_git_pass(git_status_foreach_ext(
+ g_repo, &opts, cb_status__match, &counts));
+ cl_assert_equal_i(5, counts.entry_count);
+
+ /* but if it contains tracked files, it should just show up as a
+ * directory and exclude the files in it
+ */
+
+ cl_git_mkfile("submodules/dir/another_file", "hello");
+ opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
+
+ status_counts_init(
+ counts, expected_files_with_untracked_dir,
+ expected_status_with_untracked);
+ cl_git_pass(git_status_foreach_ext(
+ g_repo, &opts, cb_status__match, &counts));
+ cl_assert_equal_i(6, counts.entry_count);
+
+ /* that applies to a git repo with a .git directory too */
+
+ cl_must_pass(p_unlink("submodules/dir/.git"));
+ cl_git_pass(git_repository_init(&contained, "submodules/dir", false));
+ git_repository_free(contained);
+ opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
+
+ status_counts_init(
+ counts, expected_files_with_untracked_dir,
+ expected_status_with_untracked);
+ cl_git_pass(git_status_foreach_ext(
+ g_repo, &opts, cb_status__match, &counts));
+ cl_assert_equal_i(6, counts.entry_count);
+
+ /* same result even if we don't recurse into subdirectories */
+
+ opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED;
+
+ status_counts_init(
+ counts, expected_files_with_untracked_dir,
+ expected_status_with_untracked);
+ cl_git_pass(git_status_foreach_ext(
+ g_repo, &opts, cb_status__match, &counts));
+ cl_assert_equal_i(6, counts.entry_count);
+
+ /* and if we remove the untracked file, it goes back to ignored */
+
+ cl_must_pass(p_unlink("submodules/dir/another_file"));
+
+ status_counts_init(
+ counts, expected_files_not_ignored, expected_status_not_ignored);
+ cl_git_pass(git_status_foreach_ext(
+ g_repo, &opts, cb_status__match, &counts));
+ cl_assert_equal_i(5, counts.entry_count);
+}
+
+void test_status_submodules__broken_stuff_that_git_allows(void)
+{
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ status_entry_counts counts;
+ git_repository *contained;
+ static const char *expected_files_with_broken[] = {
+ ".gitmodules",
+ "added",
+ "broken/tracked",
+ "deleted",
+ "ignored",
+ "modified",
+ "untracked"
+ };
+ static unsigned int expected_status_with_broken[] = {
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_INDEX_NEW,
+ GIT_STATUS_INDEX_DELETED,
+ GIT_STATUS_IGNORED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_NEW,
+ };
+
+ g_repo = setup_fixture_submodules();
+
+ opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS |
+ GIT_STATUS_OPT_INCLUDE_IGNORED;
+
+ /* make a directory and stick a tracked item into the index */
+ {
+ git_index *idx;
+ cl_must_pass(p_mkdir("submodules/broken", 0777));
+ cl_git_mkfile("submodules/broken/tracked", "tracked content");
+ cl_git_pass(git_repository_index(&idx, g_repo));
+ cl_git_pass(git_index_add_bypath(idx, "broken/tracked"));
+ cl_git_pass(git_index_write(idx));
+ git_index_free(idx);
+ }
+
+ status_counts_init(
+ counts, expected_files_with_broken, expected_status_with_broken);
+ cl_git_pass(git_status_foreach_ext(
+ g_repo, &opts, cb_status__match, &counts));
+ cl_assert_equal_i(7, counts.entry_count);
+
+ /* directory with tracked items that looks a little bit like a repo */
+
+ cl_must_pass(p_mkdir("submodules/broken/.git", 0777));
+ cl_must_pass(p_mkdir("submodules/broken/.git/info", 0777));
+ cl_git_mkfile("submodules/broken/.git/info/exclude", "# bogus");
+
+ status_counts_init(
+ counts, expected_files_with_broken, expected_status_with_broken);
+ cl_git_pass(git_status_foreach_ext(
+ g_repo, &opts, cb_status__match, &counts));
+ cl_assert_equal_i(7, counts.entry_count);
+
+ /* directory with tracked items that is a repo */
+
+ cl_git_pass(git_futils_rmdir_r(
+ "submodules/broken/.git", NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_git_pass(git_repository_init(&contained, "submodules/broken", false));
+ git_repository_free(contained);
+
+ status_counts_init(
+ counts, expected_files_with_broken, expected_status_with_broken);
+ cl_git_pass(git_status_foreach_ext(
+ g_repo, &opts, cb_status__match, &counts));
+ cl_assert_equal_i(7, counts.entry_count);
+
+ /* directory with tracked items that claims to be a submodule but is not */
+
+ cl_git_pass(git_futils_rmdir_r(
+ "submodules/broken/.git", NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_git_append2file("submodules/.gitmodules",
+ "\n[submodule \"broken\"]\n"
+ "\tpath = broken\n"
+ "\turl = https://github.com/not/used\n\n");
+
+ status_counts_init(
+ counts, expected_files_with_broken, expected_status_with_broken);
+ cl_git_pass(git_status_foreach_ext(
+ g_repo, &opts, cb_status__match, &counts));
+ cl_assert_equal_i(7, counts.entry_count);
+}
+
+void test_status_submodules__entry_but_dir_tracked(void)
+{
+ git_repository *repo;
+ git_status_list *status;
+ git_diff *diff;
+ git_index *index;
+ git_tree *tree;
+
+ cl_git_pass(git_repository_init(&repo, "mixed-submodule", 0));
+ cl_git_mkfile("mixed-submodule/.gitmodules", "[submodule \"sub\"]\n path = sub\n url = ../foo\n");
+ cl_git_pass(p_mkdir("mixed-submodule/sub", 0777));
+ cl_git_mkfile("mixed-submodule/sub/file", "");
+
+ /* Create the commit with sub/file as a file, and an entry for sub in the modules list */
+ {
+ git_oid tree_id, commit_id;
+ git_signature *sig;
+ git_reference *ref;
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_add_bypath(index, ".gitmodules"));
+ cl_git_pass(git_index_add_bypath(index, "sub/file"));
+ cl_git_pass(git_index_write(index));
+ cl_git_pass(git_index_write_tree(&tree_id, index));
+ cl_git_pass(git_signature_now(&sig, "Sloppy Submoduler", "sloppy@example.com"));
+ cl_git_pass(git_tree_lookup(&tree, repo, &tree_id));
+ cl_git_pass(git_commit_create(&commit_id, repo, NULL, sig, sig, NULL, "message", tree, 0, NULL));
+ cl_git_pass(git_reference_create(&ref, repo, "refs/heads/master", &commit_id, 1, "commit: foo"));
+ git_reference_free(ref);
+ git_signature_free(sig);
+ }
+
+ cl_git_pass(git_diff_tree_to_index(&diff, repo, tree, index, NULL));
+ cl_assert_equal_i(0, git_diff_num_deltas(diff));
+ git_diff_free(diff);
+
+ cl_git_pass(git_diff_index_to_workdir(&diff, repo, index, NULL));
+ cl_assert_equal_i(0, git_diff_num_deltas(diff));
+ git_diff_free(diff);
+
+ cl_git_pass(git_status_list_new(&status, repo, NULL));
+ cl_assert_equal_i(0, git_status_list_entrycount(status));
+
+ git_status_list_free(status);
+ git_index_free(index);
+ git_tree_free(tree);
+ git_repository_free(repo);
+}
+
+void test_status_submodules__mixed_case(void)
+{
+ git_status_list *status;
+ git_status_options status_opts = GIT_STATUS_OPTIONS_INIT;
+ const git_status_entry *s;
+ size_t i;
+
+ status_opts.flags =
+ GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_INCLUDE_IGNORED |
+ GIT_STATUS_OPT_INCLUDE_UNMODIFIED |
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS |
+ GIT_STATUS_OPT_RECURSE_IGNORED_DIRS |
+ GIT_STATUS_OPT_RENAMES_HEAD_TO_INDEX |
+ GIT_STATUS_OPT_RENAMES_INDEX_TO_WORKDIR |
+ GIT_STATUS_OPT_RENAMES_FROM_REWRITES |
+ GIT_STATUS_OPT_INCLUDE_UNREADABLE |
+ GIT_STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED;
+
+ g_repo = setup_fixture_submod3();
+
+ cl_git_pass(git_status_list_new(&status, g_repo, &status_opts));
+
+ for (i = 0; i < git_status_list_entrycount(status); i++) {
+ s = git_status_byindex(status, i);
+
+ if (s->head_to_index &&
+ strcmp(s->head_to_index->old_file.path, ".gitmodules") == 0)
+ continue;
+
+ cl_assert_equal_i(0, s->status);
+ }
+
+ git_status_list_free(status);
+}
+
diff --git a/tests/libgit2/status/worktree.c b/tests/libgit2/status/worktree.c
new file mode 100644
index 0000000..efbf597
--- /dev/null
+++ b/tests/libgit2/status/worktree.c
@@ -0,0 +1,1362 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+#include "ignore.h"
+#include "status_data.h"
+#include "posix.h"
+#include "util.h"
+#include "path.h"
+#include "../diff/diff_helpers.h"
+#include "../checkout/checkout_helpers.h"
+#include "git2/sys/diff.h"
+
+/**
+ * Cleanup
+ *
+ * This will be called once after each test finishes, even
+ * if the test failed
+ */
+void test_status_worktree__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+/**
+ * Tests - Status determination on a working tree
+ */
+/* this test is equivalent to t18-status.c:statuscb0 */
+void test_status_worktree__whole_repository(void)
+{
+ status_entry_counts counts;
+ git_repository *repo = cl_git_sandbox_init("status");
+
+ memset(&counts, 0x0, sizeof(status_entry_counts));
+ counts.expected_entry_count = entry_count0;
+ counts.expected_paths = entry_paths0;
+ counts.expected_statuses = entry_statuses0;
+
+ cl_git_pass(
+ git_status_foreach(repo, cb_status__normal, &counts)
+ );
+
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+}
+
+static void assert_show(
+ const int entry_counts,
+ const char *entry_paths[],
+ const unsigned int entry_statuses[],
+ git_repository *repo,
+ git_status_show_t show,
+ unsigned int extra_flags)
+{
+ status_entry_counts counts;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+
+ memset(&counts, 0x0, sizeof(status_entry_counts));
+ counts.expected_entry_count = entry_counts;
+ counts.expected_paths = entry_paths;
+ counts.expected_statuses = entry_statuses;
+
+ opts.flags = GIT_STATUS_OPT_DEFAULTS | extra_flags;
+ opts.show = show;
+
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__normal, &counts)
+ );
+
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+}
+
+void test_status_worktree__show_index_and_workdir(void)
+{
+ assert_show(entry_count0, entry_paths0, entry_statuses0,
+ cl_git_sandbox_init("status"), GIT_STATUS_SHOW_INDEX_AND_WORKDIR, 0);
+}
+
+void test_status_worktree__show_index_only(void)
+{
+ assert_show(entry_count5, entry_paths5, entry_statuses5,
+ cl_git_sandbox_init("status"), GIT_STATUS_SHOW_INDEX_ONLY, 0);
+}
+
+void test_status_worktree__show_workdir_only(void)
+{
+ assert_show(entry_count6, entry_paths6, entry_statuses6,
+ cl_git_sandbox_init("status"), GIT_STATUS_SHOW_WORKDIR_ONLY, 0);
+}
+
+/* this test is equivalent to t18-status.c:statuscb1 */
+void test_status_worktree__empty_repository(void)
+{
+ int count = 0;
+ git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_pass(git_status_foreach(repo, cb_status__count, &count));
+
+ cl_assert_equal_i(0, count);
+}
+
+static int remove_file_cb(void *data, git_str *file)
+{
+ const char *filename = git_str_cstr(file);
+
+ GIT_UNUSED(data);
+
+ if (git__suffixcmp(filename, ".git") == 0)
+ return 0;
+
+ if (git_fs_path_isdir(filename))
+ cl_git_pass(git_futils_rmdir_r(filename, NULL, GIT_RMDIR_REMOVE_FILES));
+ else
+ cl_git_pass(p_unlink(git_str_cstr(file)));
+
+ return 0;
+}
+
+/* this test is equivalent to t18-status.c:statuscb2 */
+void test_status_worktree__purged_worktree(void)
+{
+ status_entry_counts counts;
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_str workdir = GIT_STR_INIT;
+
+ /* first purge the contents of the worktree */
+ cl_git_pass(git_str_sets(&workdir, git_repository_workdir(repo)));
+ cl_git_pass(git_fs_path_direach(&workdir, 0, remove_file_cb, NULL));
+ git_str_dispose(&workdir);
+
+ /* now get status */
+ memset(&counts, 0x0, sizeof(status_entry_counts));
+ counts.expected_entry_count = entry_count2;
+ counts.expected_paths = entry_paths2;
+ counts.expected_statuses = entry_statuses2;
+
+ cl_git_pass(
+ git_status_foreach(repo, cb_status__normal, &counts)
+ );
+
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+}
+
+/* this test is similar to t18-status.c:statuscb3 */
+void test_status_worktree__swap_subdir_and_file(void)
+{
+ status_entry_counts counts;
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_index *index;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ bool ignore_case;
+
+ cl_git_pass(git_repository_index(&index, repo));
+ ignore_case = (git_index_caps(index) & GIT_INDEX_CAPABILITY_IGNORE_CASE) != 0;
+ git_index_free(index);
+
+ /* first alter the contents of the worktree */
+ cl_git_pass(p_rename("status/current_file", "status/swap"));
+ cl_git_pass(p_rename("status/subdir", "status/current_file"));
+ cl_git_pass(p_rename("status/swap", "status/subdir"));
+
+ cl_git_mkfile("status/.HEADER", "dummy");
+ cl_git_mkfile("status/42-is-not-prime.sigh", "dummy");
+ cl_git_mkfile("status/README.md", "dummy");
+
+ /* now get status */
+ memset(&counts, 0x0, sizeof(status_entry_counts));
+ counts.expected_entry_count = entry_count3;
+ counts.expected_paths = ignore_case ? entry_paths3_icase : entry_paths3;
+ counts.expected_statuses = ignore_case ? entry_statuses3_icase : entry_statuses3;
+
+ opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_INCLUDE_IGNORED;
+
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__normal, &counts)
+ );
+
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+}
+
+void test_status_worktree__swap_subdir_with_recurse_and_pathspec(void)
+{
+ status_entry_counts counts;
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+
+ /* first alter the contents of the worktree */
+ cl_git_pass(p_rename("status/current_file", "status/swap"));
+ cl_git_pass(p_rename("status/subdir", "status/current_file"));
+ cl_git_pass(p_rename("status/swap", "status/subdir"));
+ cl_git_mkfile("status/.new_file", "dummy");
+ cl_git_pass(git_futils_mkdir_r("status/zzz_new_dir", 0777));
+ cl_git_mkfile("status/zzz_new_dir/new_file", "dummy");
+ cl_git_mkfile("status/zzz_new_file", "dummy");
+
+ /* now get status */
+ memset(&counts, 0x0, sizeof(status_entry_counts));
+ counts.expected_entry_count = entry_count4;
+ counts.expected_paths = entry_paths4;
+ counts.expected_statuses = entry_statuses4;
+
+ opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
+ /* TODO: set pathspec to "current_file" eventually */
+
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__normal, &counts)
+ );
+
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+}
+
+static void stage_and_commit(git_repository *repo, const char *path)
+{
+ git_index *index;
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_add_bypath(index, path));
+ cl_repo_commit_from_index(NULL, repo, NULL, 1323847743, "Initial commit\n");
+ git_index_free(index);
+}
+
+void test_status_worktree__within_subdir(void)
+{
+ status_entry_counts counts;
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ char *paths[] = { "zzz_new_dir" };
+ git_strarray pathsArray;
+
+ /* first alter the contents of the worktree */
+ cl_git_mkfile("status/.new_file", "dummy");
+ cl_git_pass(git_futils_mkdir_r("status/zzz_new_dir", 0777));
+ cl_git_mkfile("status/zzz_new_dir/new_file", "dummy");
+ cl_git_mkfile("status/zzz_new_file", "dummy");
+ cl_git_mkfile("status/wut", "dummy");
+
+ stage_and_commit(repo, "zzz_new_dir/new_file");
+
+ /* now get status */
+ memset(&counts, 0x0, sizeof(status_entry_counts));
+ counts.expected_entry_count = entry_count4;
+ counts.expected_paths = entry_paths4;
+ counts.expected_statuses = entry_statuses4;
+ counts.debug = true;
+
+ opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS |
+ GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH;
+
+ pathsArray.count = 1;
+ pathsArray.strings = paths;
+ opts.pathspec = pathsArray;
+
+ /* We committed zzz_new_dir/new_file above. It shouldn't be reported. */
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__normal, &counts)
+ );
+
+ cl_assert_equal_i(0, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+}
+
+/* this test is equivalent to t18-status.c:singlestatus0 */
+void test_status_worktree__single_file(void)
+{
+ int i;
+ unsigned int status_flags;
+ git_repository *repo = cl_git_sandbox_init("status");
+
+ for (i = 0; i < (int)entry_count0; i++) {
+ cl_git_pass(
+ git_status_file(&status_flags, repo, entry_paths0[i])
+ );
+ cl_assert(entry_statuses0[i] == status_flags);
+ }
+}
+
+/* this test is equivalent to t18-status.c:singlestatus1 */
+void test_status_worktree__single_nonexistent_file(void)
+{
+ int error;
+ unsigned int status_flags;
+ git_repository *repo = cl_git_sandbox_init("status");
+
+ error = git_status_file(&status_flags, repo, "nonexistent");
+ cl_git_fail(error);
+ cl_assert(error == GIT_ENOTFOUND);
+}
+
+/* this test is equivalent to t18-status.c:singlestatus2 */
+void test_status_worktree__single_nonexistent_file_empty_repo(void)
+{
+ int error;
+ unsigned int status_flags;
+ git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
+
+ error = git_status_file(&status_flags, repo, "nonexistent");
+ cl_git_fail(error);
+ cl_assert(error == GIT_ENOTFOUND);
+}
+
+/* this test is equivalent to t18-status.c:singlestatus3 */
+void test_status_worktree__single_file_empty_repo(void)
+{
+ unsigned int status_flags;
+ git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_mkfile("empty_standard_repo/new_file", "new_file\n");
+
+ cl_git_pass(git_status_file(&status_flags, repo, "new_file"));
+ cl_assert(status_flags == GIT_STATUS_WT_NEW);
+}
+
+/* this test is equivalent to t18-status.c:singlestatus4 */
+void test_status_worktree__single_folder(void)
+{
+ int error;
+ unsigned int status_flags;
+ git_repository *repo = cl_git_sandbox_init("status");
+
+ error = git_status_file(&status_flags, repo, "subdir");
+ cl_git_fail(error);
+ cl_assert(error != GIT_ENOTFOUND);
+}
+
+
+void test_status_worktree__ignores(void)
+{
+ int i, ignored;
+ git_repository *repo = cl_git_sandbox_init("status");
+
+ for (i = 0; i < (int)entry_count0; i++) {
+ cl_git_pass(
+ git_status_should_ignore(&ignored, repo, entry_paths0[i])
+ );
+ cl_assert(ignored == (entry_statuses0[i] == GIT_STATUS_IGNORED));
+ }
+
+ cl_git_pass(
+ git_status_should_ignore(&ignored, repo, "nonexistent_file")
+ );
+ cl_assert(!ignored);
+
+ cl_git_pass(
+ git_status_should_ignore(&ignored, repo, "ignored_nonexistent_file")
+ );
+ cl_assert(ignored);
+}
+
+static int cb_status__check_592(const char *p, unsigned int s, void *payload)
+{
+ if (s != GIT_STATUS_WT_DELETED ||
+ (payload != NULL && strcmp(p, (const char *)payload) != 0))
+ return -1;
+
+ return 0;
+}
+
+void test_status_worktree__issue_592(void)
+{
+ git_repository *repo;
+ git_str path = GIT_STR_INIT;
+
+ repo = cl_git_sandbox_init("issue_592");
+ cl_git_pass(git_str_joinpath(&path, git_repository_workdir(repo), "l.txt"));
+ cl_git_pass(p_unlink(git_str_cstr(&path)));
+ cl_assert(!git_fs_path_exists("issue_592/l.txt"));
+
+ cl_git_pass(git_status_foreach(repo, cb_status__check_592, "l.txt"));
+
+ git_str_dispose(&path);
+}
+
+void test_status_worktree__issue_592_2(void)
+{
+ git_repository *repo;
+ git_str path = GIT_STR_INIT;
+
+ repo = cl_git_sandbox_init("issue_592");
+ cl_git_pass(git_str_joinpath(&path, git_repository_workdir(repo), "c/a.txt"));
+ cl_git_pass(p_unlink(git_str_cstr(&path)));
+ cl_assert(!git_fs_path_exists("issue_592/c/a.txt"));
+
+ cl_git_pass(git_status_foreach(repo, cb_status__check_592, "c/a.txt"));
+
+ git_str_dispose(&path);
+}
+
+void test_status_worktree__issue_592_3(void)
+{
+ git_repository *repo;
+ git_str path = GIT_STR_INIT;
+
+ repo = cl_git_sandbox_init("issue_592");
+
+ cl_git_pass(git_str_joinpath(&path, git_repository_workdir(repo), "c"));
+ cl_git_pass(git_futils_rmdir_r(git_str_cstr(&path), NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_assert(!git_fs_path_exists("issue_592/c/a.txt"));
+
+ cl_git_pass(git_status_foreach(repo, cb_status__check_592, "c/a.txt"));
+
+ git_str_dispose(&path);
+}
+
+void test_status_worktree__issue_592_4(void)
+{
+ git_repository *repo;
+ git_str path = GIT_STR_INIT;
+
+ repo = cl_git_sandbox_init("issue_592");
+
+ cl_git_pass(git_str_joinpath(&path, git_repository_workdir(repo), "t/b.txt"));
+ cl_git_pass(p_unlink(git_str_cstr(&path)));
+
+ cl_git_pass(git_status_foreach(repo, cb_status__check_592, "t/b.txt"));
+
+ git_str_dispose(&path);
+}
+
+void test_status_worktree__issue_592_5(void)
+{
+ git_repository *repo;
+ git_str path = GIT_STR_INIT;
+
+ repo = cl_git_sandbox_init("issue_592");
+
+ cl_git_pass(git_str_joinpath(&path, git_repository_workdir(repo), "t"));
+ cl_git_pass(git_futils_rmdir_r(git_str_cstr(&path), NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_git_pass(p_mkdir(git_str_cstr(&path), 0777));
+
+ cl_git_pass(git_status_foreach(repo, cb_status__check_592, NULL));
+
+ git_str_dispose(&path);
+}
+
+void test_status_worktree__issue_592_ignores_0(void)
+{
+ int count = 0;
+ status_entry_single st;
+ git_repository *repo = cl_git_sandbox_init("issue_592");
+
+ cl_git_pass(git_status_foreach(repo, cb_status__count, &count));
+ cl_assert_equal_i(0, count);
+
+ cl_git_rewritefile("issue_592/.gitignore",
+ ".gitignore\n*.txt\nc/\n[tT]*/\n");
+
+ cl_git_pass(git_status_foreach(repo, cb_status__count, &count));
+ cl_assert_equal_i(1, count);
+
+ /* This is a situation where the behavior of libgit2 is
+ * different from core git. Core git will show ignored.txt
+ * in the list of ignored files, even though the directory
+ * "t" is ignored and the file is untracked because we have
+ * the explicit "*.txt" ignore rule. Libgit2 just excludes
+ * all untracked files that are contained within ignored
+ * directories without explicitly listing them.
+ */
+ cl_git_rewritefile("issue_592/t/ignored.txt", "ping");
+
+ memset(&st, 0, sizeof(st));
+ cl_git_pass(git_status_foreach(repo, cb_status__single, &st));
+ cl_assert_equal_i(1, st.count);
+ cl_assert(st.status == GIT_STATUS_IGNORED);
+
+ cl_git_rewritefile("issue_592/c/ignored_by_dir", "ping");
+
+ memset(&st, 0, sizeof(st));
+ cl_git_pass(git_status_foreach(repo, cb_status__single, &st));
+ cl_assert_equal_i(1, st.count);
+ cl_assert(st.status == GIT_STATUS_IGNORED);
+
+ cl_git_rewritefile("issue_592/t/ignored_by_dir_pattern", "ping");
+
+ memset(&st, 0, sizeof(st));
+ cl_git_pass(git_status_foreach(repo, cb_status__single, &st));
+ cl_assert_equal_i(1, st.count);
+ cl_assert(st.status == GIT_STATUS_IGNORED);
+}
+
+void test_status_worktree__issue_592_ignored_dirs_with_tracked_content(void)
+{
+ int count = 0;
+ git_repository *repo = cl_git_sandbox_init("issue_592b");
+
+ cl_git_pass(git_status_foreach(repo, cb_status__count, &count));
+ cl_assert_equal_i(1, count);
+
+ /* if we are really mimicking core git, then only ignored1.txt
+ * at the top level will show up in the ignores list here.
+ * everything else will be unmodified or skipped completely.
+ */
+}
+
+void test_status_worktree__conflict_with_diff3(void)
+{
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_index *index;
+ unsigned int status;
+ git_index_entry ancestor_entry, our_entry, their_entry;
+
+ memset(&ancestor_entry, 0x0, sizeof(git_index_entry));
+ memset(&our_entry, 0x0, sizeof(git_index_entry));
+ memset(&their_entry, 0x0, sizeof(git_index_entry));
+
+ ancestor_entry.path = "modified_file";
+ ancestor_entry.mode = 0100644;
+ git_oid__fromstr(&ancestor_entry.id,
+ "452e4244b5d083ddf0460acf1ecc74db9dcfa11a",
+ GIT_OID_SHA1);
+
+ our_entry.path = "modified_file";
+ our_entry.mode = 0100644;
+ git_oid__fromstr(&our_entry.id,
+ "452e4244b5d083ddf0460acf1ecc74db9dcfa11a",
+ GIT_OID_SHA1);
+
+ their_entry.path = "modified_file";
+ their_entry.mode = 0100644;
+ git_oid__fromstr(&their_entry.id,
+ "452e4244b5d083ddf0460acf1ecc74db9dcfa11a",
+ GIT_OID_SHA1);
+
+ cl_git_pass(git_status_file(&status, repo, "modified_file"));
+ cl_assert_equal_i(GIT_STATUS_WT_MODIFIED, status);
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_remove(index, "modified_file", 0));
+ cl_git_pass(git_index_conflict_add(
+ index, &ancestor_entry, &our_entry, &their_entry));
+ cl_git_pass(git_index_write(index));
+ git_index_free(index);
+
+ cl_git_pass(git_status_file(&status, repo, "modified_file"));
+
+ cl_assert_equal_i(GIT_STATUS_CONFLICTED, status);
+}
+
+static const char *filemode_paths[] = {
+ "exec_off",
+ "exec_off2on_staged",
+ "exec_off2on_workdir",
+ "exec_off_untracked",
+ "exec_on",
+ "exec_on2off_staged",
+ "exec_on2off_workdir",
+ "exec_on_untracked",
+};
+
+static unsigned int filemode_statuses[] = {
+ GIT_STATUS_CURRENT,
+ GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_NEW,
+ GIT_STATUS_CURRENT,
+ GIT_STATUS_INDEX_MODIFIED,
+ GIT_STATUS_WT_MODIFIED,
+ GIT_STATUS_WT_NEW
+};
+
+static const int filemode_count = 8;
+
+void test_status_worktree__filemode_changes(void)
+{
+ git_repository *repo = cl_git_sandbox_init("filemodes");
+ status_entry_counts counts;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+
+ /* overwrite stored filemode with platform appropriate value */
+ if (cl_is_chmod_supported())
+ cl_repo_set_bool(repo, "core.filemode", true);
+ else {
+ int i;
+
+ cl_repo_set_bool(repo, "core.filemode", false);
+
+ /* won't trust filesystem mode diffs, so these will appear unchanged */
+ for (i = 0; i < filemode_count; ++i)
+ if (filemode_statuses[i] == GIT_STATUS_WT_MODIFIED)
+ filemode_statuses[i] = GIT_STATUS_CURRENT;
+ }
+
+ opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_INCLUDE_IGNORED |
+ GIT_STATUS_OPT_INCLUDE_UNMODIFIED;
+
+ memset(&counts, 0, sizeof(counts));
+ counts.expected_entry_count = filemode_count;
+ counts.expected_paths = filemode_paths;
+ counts.expected_statuses = filemode_statuses;
+
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__normal, &counts)
+ );
+
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+}
+
+void test_status_worktree__filemode_non755(void)
+{
+ git_repository *repo = cl_git_sandbox_init("filemodes");
+ status_entry_counts counts;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ git_str executable_path = GIT_STR_INIT;
+ git_str nonexecutable_path = GIT_STR_INIT;
+
+ if (!cl_is_chmod_supported())
+ return;
+
+ opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_INCLUDE_IGNORED |
+ GIT_STATUS_OPT_INCLUDE_UNMODIFIED;
+
+ git_str_joinpath(&executable_path, git_repository_workdir(repo), "exec_on");
+ cl_must_pass(p_chmod(git_str_cstr(&executable_path), 0744));
+ git_str_dispose(&executable_path);
+
+ git_str_joinpath(&nonexecutable_path, git_repository_workdir(repo), "exec_off");
+
+ cl_must_pass(p_chmod(git_str_cstr(&nonexecutable_path), 0655));
+ git_str_dispose(&nonexecutable_path);
+
+ memset(&counts, 0, sizeof(counts));
+ counts.expected_entry_count = filemode_count;
+ counts.expected_paths = filemode_paths;
+ counts.expected_statuses = filemode_statuses;
+
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__normal, &counts)
+ );
+
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+}
+
+
+static int cb_status__interrupt(const char *p, unsigned int s, void *payload)
+{
+ volatile int *count = (int *)payload;
+
+ GIT_UNUSED(p);
+ GIT_UNUSED(s);
+
+ (*count)++;
+
+ return (*count == 8) ? -111 : 0;
+}
+
+void test_status_worktree__interruptable_foreach(void)
+{
+ int count = 0;
+ git_repository *repo = cl_git_sandbox_init("status");
+
+ cl_assert_equal_i(
+ -111, git_status_foreach(repo, cb_status__interrupt, &count)
+ );
+
+ cl_assert_equal_i(8, count);
+}
+
+void test_status_worktree__line_endings_dont_count_as_changes_with_autocrlf(void)
+{
+ git_repository *repo = cl_git_sandbox_init("status");
+ unsigned int status;
+
+ cl_repo_set_bool(repo, "core.autocrlf", true);
+
+ cl_git_rewritefile("status/current_file", "current_file\r\n");
+
+ cl_git_pass(git_status_file(&status, repo, "current_file"));
+
+ /* stat data on file should no longer match stat cache, even though
+ * file diff will be empty because of line-ending conversion - matches
+ * the Git command-line behavior here.
+ */
+ cl_assert_equal_i(GIT_STATUS_WT_MODIFIED, status);
+}
+
+void test_status_worktree__line_endings_dont_count_as_changes_with_autocrlf_issue_1397(void)
+{
+ git_repository *repo = cl_git_sandbox_init("issue_1397");
+ unsigned int status;
+
+ cl_repo_set_bool(repo, "core.autocrlf", true);
+
+ cl_git_pass(git_status_file(&status, repo, "crlf_file.txt"));
+
+ cl_assert_equal_i(GIT_STATUS_CURRENT, status);
+}
+
+void test_status_worktree__conflicted_item(void)
+{
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_index *index;
+ unsigned int status;
+ git_index_entry ancestor_entry, our_entry, their_entry;
+
+ memset(&ancestor_entry, 0x0, sizeof(git_index_entry));
+ memset(&our_entry, 0x0, sizeof(git_index_entry));
+ memset(&their_entry, 0x0, sizeof(git_index_entry));
+
+ ancestor_entry.mode = 0100644;
+ ancestor_entry.path = "modified_file";
+ git_oid__fromstr(&ancestor_entry.id,
+ "452e4244b5d083ddf0460acf1ecc74db9dcfa11a",
+ GIT_OID_SHA1);
+
+ our_entry.mode = 0100644;
+ our_entry.path = "modified_file";
+ git_oid__fromstr(&our_entry.id,
+ "452e4244b5d083ddf0460acf1ecc74db9dcfa11a",
+ GIT_OID_SHA1);
+
+ their_entry.mode = 0100644;
+ their_entry.path = "modified_file";
+ git_oid__fromstr(&their_entry.id,
+ "452e4244b5d083ddf0460acf1ecc74db9dcfa11a",
+ GIT_OID_SHA1);
+
+ cl_git_pass(git_status_file(&status, repo, "modified_file"));
+ cl_assert_equal_i(GIT_STATUS_WT_MODIFIED, status);
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_conflict_add(index, &ancestor_entry,
+ &our_entry, &their_entry));
+
+ cl_git_pass(git_status_file(&status, repo, "modified_file"));
+ cl_assert_equal_i(GIT_STATUS_CONFLICTED, status);
+
+ git_index_free(index);
+}
+
+void test_status_worktree__conflict_has_no_oid(void)
+{
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_index *index;
+ git_index_entry entry = {{0}};
+ git_status_list *statuslist;
+ const git_status_entry *status;
+ git_oid zero_id = GIT_OID_SHA1_ZERO;
+
+ entry.mode = 0100644;
+ entry.path = "modified_file";
+ git_oid__fromstr(&entry.id, "452e4244b5d083ddf0460acf1ecc74db9dcfa11a", GIT_OID_SHA1);
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_conflict_add(index, &entry, &entry, &entry));
+
+ git_status_list_new(&statuslist, repo, NULL);
+
+ cl_assert_equal_i(16, git_status_list_entrycount(statuslist));
+
+ status = git_status_byindex(statuslist, 2);
+
+ cl_assert_equal_i(GIT_STATUS_CONFLICTED, status->status);
+ cl_assert_equal_s("modified_file", status->head_to_index->old_file.path);
+ cl_assert(!git_oid_equal(&zero_id, &status->head_to_index->old_file.id));
+ cl_assert(0 != status->head_to_index->old_file.mode);
+ cl_assert_equal_s("modified_file", status->head_to_index->new_file.path);
+ cl_assert_equal_oid(&zero_id, &status->head_to_index->new_file.id);
+ cl_assert_equal_i(0, status->head_to_index->new_file.mode);
+ cl_assert_equal_i(0, status->head_to_index->new_file.size);
+
+ cl_assert_equal_s("modified_file", status->index_to_workdir->old_file.path);
+ cl_assert_equal_oid(&zero_id, &status->index_to_workdir->old_file.id);
+ cl_assert_equal_i(0, status->index_to_workdir->old_file.mode);
+ cl_assert_equal_i(0, status->index_to_workdir->old_file.size);
+ cl_assert_equal_s("modified_file", status->index_to_workdir->new_file.path);
+ cl_assert(
+ !git_oid_equal(&zero_id, &status->index_to_workdir->new_file.id) ||
+ !(status->index_to_workdir->new_file.flags & GIT_DIFF_FLAG_VALID_ID));
+ cl_assert(0 != status->index_to_workdir->new_file.mode);
+ cl_assert(0 != status->index_to_workdir->new_file.size);
+
+ git_index_free(index);
+ git_status_list_free(statuslist);
+}
+
+static void assert_ignore_case(
+ bool should_ignore_case,
+ int expected_lower_cased_file_status,
+ int expected_camel_cased_file_status)
+{
+ unsigned int status;
+ git_str lower_case_path = GIT_STR_INIT, camel_case_path = GIT_STR_INIT;
+ git_repository *repo, *repo2;
+
+ repo = cl_git_sandbox_init("empty_standard_repo");
+ cl_git_remove_placeholders(git_repository_path(repo), "dummy-marker.txt");
+
+ cl_repo_set_bool(repo, "core.ignorecase", should_ignore_case);
+
+ cl_git_pass(git_str_joinpath(&lower_case_path,
+ git_repository_workdir(repo), "plop"));
+
+ cl_git_mkfile(git_str_cstr(&lower_case_path), "");
+
+ stage_and_commit(repo, "plop");
+
+ cl_git_pass(git_repository_open(&repo2, "./empty_standard_repo"));
+
+ cl_git_pass(git_status_file(&status, repo2, "plop"));
+ cl_assert_equal_i(GIT_STATUS_CURRENT, status);
+
+ cl_git_pass(git_str_joinpath(&camel_case_path,
+ git_repository_workdir(repo), "Plop"));
+
+ cl_git_pass(p_rename(git_str_cstr(&lower_case_path), git_str_cstr(&camel_case_path)));
+
+ cl_git_pass(git_status_file(&status, repo2, "plop"));
+ cl_assert_equal_i(expected_lower_cased_file_status, status);
+
+ cl_git_pass(git_status_file(&status, repo2, "Plop"));
+ cl_assert_equal_i(expected_camel_cased_file_status, status);
+
+ git_repository_free(repo2);
+ git_str_dispose(&lower_case_path);
+ git_str_dispose(&camel_case_path);
+}
+
+void test_status_worktree__file_status_honors_core_ignorecase_true(void)
+{
+ assert_ignore_case(true, GIT_STATUS_CURRENT, GIT_STATUS_CURRENT);
+}
+
+void test_status_worktree__file_status_honors_core_ignorecase_false(void)
+{
+ assert_ignore_case(false, GIT_STATUS_WT_DELETED, GIT_STATUS_WT_NEW);
+}
+
+void test_status_worktree__file_status_honors_case_ignorecase_regarding_untracked_files(void)
+{
+ git_repository *repo = cl_git_sandbox_init("status");
+ unsigned int status;
+ git_index *index;
+
+ cl_repo_set_bool(repo, "core.ignorecase", false);
+
+ repo = cl_git_sandbox_reopen();
+
+ /* Actually returns GIT_STATUS_IGNORED on Windows */
+ cl_git_fail_with(git_status_file(&status, repo, "NEW_FILE"), GIT_ENOTFOUND);
+
+ cl_git_pass(git_repository_index(&index, repo));
+
+ cl_git_pass(git_index_add_bypath(index, "new_file"));
+ cl_git_pass(git_index_write(index));
+ git_index_free(index);
+
+ /* Actually returns GIT_STATUS_IGNORED on Windows */
+ cl_git_fail_with(git_status_file(&status, repo, "NEW_FILE"), GIT_ENOTFOUND);
+}
+
+void test_status_worktree__simple_delete(void)
+{
+ git_repository *repo = cl_git_sandbox_init("renames");
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ int count;
+
+ opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH |
+ GIT_STATUS_OPT_EXCLUDE_SUBMODULES |
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
+
+ count = 0;
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__count, &count) );
+ cl_assert_equal_i(0, count);
+
+ cl_must_pass(p_unlink("renames/untimely.txt"));
+
+ count = 0;
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__count, &count) );
+ cl_assert_equal_i(1, count);
+}
+
+void test_status_worktree__simple_delete_indexed(void)
+{
+ git_repository *repo = cl_git_sandbox_init("renames");
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ git_status_list *status;
+
+ opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH |
+ GIT_STATUS_OPT_EXCLUDE_SUBMODULES |
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS;
+
+ cl_git_pass(git_status_list_new(&status, repo, &opts));
+ cl_assert_equal_sz(0, git_status_list_entrycount(status));
+ git_status_list_free(status);
+
+ cl_must_pass(p_unlink("renames/untimely.txt"));
+
+ cl_git_pass(git_status_list_new(&status, repo, &opts));
+ cl_assert_equal_sz(1, git_status_list_entrycount(status));
+ cl_assert_equal_i(
+ GIT_STATUS_WT_DELETED, git_status_byindex(status, 0)->status);
+ git_status_list_free(status);
+}
+
+static const char *icase_paths[] = { "B", "c", "g", "H" };
+static unsigned int icase_statuses[] = {
+ GIT_STATUS_WT_MODIFIED, GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_MODIFIED, GIT_STATUS_WT_DELETED,
+};
+
+static const char *case_paths[] = { "B", "H", "c", "g" };
+static unsigned int case_statuses[] = {
+ GIT_STATUS_WT_MODIFIED, GIT_STATUS_WT_DELETED,
+ GIT_STATUS_WT_DELETED, GIT_STATUS_WT_MODIFIED,
+};
+
+void test_status_worktree__sorting_by_case(void)
+{
+ git_repository *repo = cl_git_sandbox_init("icase");
+ git_index *index;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ bool native_ignore_case;
+ status_entry_counts counts;
+
+ cl_git_pass(git_repository_index(&index, repo));
+ native_ignore_case =
+ (git_index_caps(index) & GIT_INDEX_CAPABILITY_IGNORE_CASE) != 0;
+ git_index_free(index);
+
+ memset(&counts, 0, sizeof(counts));
+ counts.expected_entry_count = 0;
+ counts.expected_paths = NULL;
+ counts.expected_statuses = NULL;
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__normal, &counts));
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+
+ cl_git_rewritefile("icase/B", "new stuff");
+ cl_must_pass(p_unlink("icase/c"));
+ cl_git_rewritefile("icase/g", "new stuff");
+ cl_must_pass(p_unlink("icase/H"));
+
+ memset(&counts, 0, sizeof(counts));
+ counts.expected_entry_count = 4;
+ if (native_ignore_case) {
+ counts.expected_paths = icase_paths;
+ counts.expected_statuses = icase_statuses;
+ } else {
+ counts.expected_paths = case_paths;
+ counts.expected_statuses = case_statuses;
+ }
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__normal, &counts));
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+
+ opts.flags = GIT_STATUS_OPT_SORT_CASE_SENSITIVELY;
+
+ memset(&counts, 0, sizeof(counts));
+ counts.expected_entry_count = 4;
+ counts.expected_paths = case_paths;
+ counts.expected_statuses = case_statuses;
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__normal, &counts));
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+
+ opts.flags = GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY;
+
+ memset(&counts, 0, sizeof(counts));
+ counts.expected_entry_count = 4;
+ counts.expected_paths = icase_paths;
+ counts.expected_statuses = icase_statuses;
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__normal, &counts));
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+}
+
+void test_status_worktree__long_filenames(void)
+{
+ char path[260*4+1] = {0};
+ const char *expected_paths[] = {path};
+ const unsigned int expected_statuses[] = {GIT_STATUS_WT_NEW};
+
+ git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ status_entry_counts counts = {0};
+
+ /* Create directory with amazingly long filename */
+ sprintf(path, "empty_standard_repo/%s", longname);
+ cl_git_pass(git_futils_mkdir_r(path, 0777));
+ sprintf(path, "empty_standard_repo/%s/foo", longname);
+ cl_git_mkfile(path, "dummy");
+
+ sprintf(path, "%s/foo", longname);
+ counts.expected_entry_count = 1;
+ counts.expected_paths = expected_paths;
+ counts.expected_statuses = expected_statuses;
+
+ opts.show = GIT_STATUS_SHOW_WORKDIR_ONLY;
+ opts.flags = GIT_STATUS_OPT_DEFAULTS;
+
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__normal, &counts) );
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+}
+
+/* The update stat cache tests mostly just mirror other tests and try
+ * to make sure that updating the stat cache doesn't change the results
+ * while reducing the amount of work that needs to be done
+ */
+
+static void check_status0(git_status_list *status)
+{
+ size_t i, max_i = git_status_list_entrycount(status);
+ cl_assert_equal_sz(entry_count0, max_i);
+ for (i = 0; i < max_i; ++i) {
+ const git_status_entry *entry = git_status_byindex(status, i);
+ cl_assert_equal_i(entry_statuses0[i], entry->status);
+ }
+}
+
+void test_status_worktree__update_stat_cache_0(void)
+{
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ git_status_list *status;
+ git_diff_perfdata perf = GIT_DIFF_PERFDATA_INIT;
+ git_index *index;
+
+ opts.flags = GIT_STATUS_OPT_DEFAULTS;
+
+ cl_git_pass(git_status_list_new(&status, repo, &opts));
+ check_status0(status);
+ cl_git_pass(git_status_list_get_perfdata(&perf, status));
+ cl_assert_equal_sz(13 + 3, perf.stat_calls);
+ cl_assert_equal_sz(5, perf.oid_calculations);
+
+ git_status_list_free(status);
+
+ /* tick the index so we avoid recalculating racily-clean entries */
+ cl_git_pass(git_repository_index__weakptr(&index, repo));
+ tick_index(index);
+
+ opts.flags |= GIT_STATUS_OPT_UPDATE_INDEX;
+
+ cl_git_pass(git_status_list_new(&status, repo, &opts));
+ check_status0(status);
+ cl_git_pass(git_status_list_get_perfdata(&perf, status));
+ cl_assert_equal_sz(13 + 3, perf.stat_calls);
+ cl_assert_equal_sz(5, perf.oid_calculations);
+
+ git_status_list_free(status);
+
+ opts.flags &= ~GIT_STATUS_OPT_UPDATE_INDEX;
+
+ /* tick again as the index updating from the previous diff might have reset the timestamp */
+ tick_index(index);
+ cl_git_pass(git_status_list_new(&status, repo, &opts));
+ check_status0(status);
+ cl_git_pass(git_status_list_get_perfdata(&perf, status));
+ cl_assert_equal_sz(13 + 3, perf.stat_calls);
+ cl_assert_equal_sz(0, perf.oid_calculations);
+
+ git_status_list_free(status);
+}
+
+void test_status_worktree__unreadable(void)
+{
+#ifndef GIT_WIN32
+ const char *expected_paths[] = { "no_permission/foo" };
+ const unsigned int expected_statuses[] = {GIT_STATUS_WT_UNREADABLE};
+
+ git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ status_entry_counts counts = {0};
+
+ if (geteuid() == 0)
+ cl_skip();
+
+ /* Create directory with no read permission */
+ cl_git_pass(git_futils_mkdir_r("empty_standard_repo/no_permission", 0777));
+ cl_git_mkfile("empty_standard_repo/no_permission/foo", "dummy");
+ p_chmod("empty_standard_repo/no_permission", 0644);
+
+ counts.expected_entry_count = 1;
+ counts.expected_paths = expected_paths;
+ counts.expected_statuses = expected_statuses;
+
+ opts.show = GIT_STATUS_SHOW_WORKDIR_ONLY;
+ opts.flags = GIT_STATUS_OPT_DEFAULTS | GIT_STATUS_OPT_INCLUDE_UNREADABLE;
+
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__normal, &counts) );
+
+ /* Restore permissions so we can cleanup :) */
+ p_chmod("empty_standard_repo/no_permission", 0777);
+
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+#else
+ cl_skip();
+#endif
+}
+
+void test_status_worktree__unreadable_not_included(void)
+{
+#ifndef GIT_WIN32
+ const char *expected_paths[] = { "no_permission/" };
+ const unsigned int expected_statuses[] = {GIT_STATUS_WT_NEW};
+
+ git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ status_entry_counts counts = {0};
+
+ /* Create directory with no read permission */
+ cl_git_pass(git_futils_mkdir_r("empty_standard_repo/no_permission", 0777));
+ cl_git_mkfile("empty_standard_repo/no_permission/foo", "dummy");
+ p_chmod("empty_standard_repo/no_permission", 0644);
+
+ counts.expected_entry_count = 1;
+ counts.expected_paths = expected_paths;
+ counts.expected_statuses = expected_statuses;
+
+ opts.show = GIT_STATUS_SHOW_WORKDIR_ONLY;
+ opts.flags = (GIT_STATUS_OPT_INCLUDE_IGNORED | GIT_STATUS_OPT_INCLUDE_UNTRACKED);
+
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__normal, &counts) );
+
+ /* Restore permissions so we can cleanup :) */
+ p_chmod("empty_standard_repo/no_permission", 0777);
+
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+#else
+ cl_skip();
+#endif
+}
+
+void test_status_worktree__unreadable_as_untracked(void)
+{
+ const char *expected_paths[] = { "no_permission/foo" };
+ const unsigned int expected_statuses[] = {GIT_STATUS_WT_NEW};
+
+ git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ status_entry_counts counts = {0};
+
+ /* Create directory with no read permission */
+ cl_git_pass(git_futils_mkdir_r("empty_standard_repo/no_permission", 0777));
+ cl_git_mkfile("empty_standard_repo/no_permission/foo", "dummy");
+ p_chmod("empty_standard_repo/no_permission", 0644);
+
+ counts.expected_entry_count = 1;
+ counts.expected_paths = expected_paths;
+ counts.expected_statuses = expected_statuses;
+
+ opts.show = GIT_STATUS_SHOW_WORKDIR_ONLY;
+ opts.flags = GIT_STATUS_OPT_DEFAULTS |
+ GIT_STATUS_OPT_INCLUDE_UNREADABLE |
+ GIT_STATUS_OPT_INCLUDE_UNREADABLE_AS_UNTRACKED;
+
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__normal, &counts) );
+
+ /* Restore permissions so we can cleanup :) */
+ p_chmod("empty_standard_repo/no_permission", 0777);
+
+ cl_assert_equal_i(counts.expected_entry_count, counts.entry_count);
+ cl_assert_equal_i(0, counts.wrong_status_flags_count);
+ cl_assert_equal_i(0, counts.wrong_sorted_path);
+}
+
+void test_status_worktree__update_index_with_symlink_doesnt_change_mode(void)
+{
+ git_repository *repo = cl_git_sandbox_init("testrepo");
+ git_reference *head;
+ git_object *head_object;
+ git_index *index;
+ const git_index_entry *idx_entry;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ status_entry_counts counts = {0};
+ const char *expected_paths[] = { "README" };
+ const unsigned int expected_statuses[] = {GIT_STATUS_WT_NEW};
+
+ opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
+ opts.flags = GIT_STATUS_OPT_DEFAULTS | GIT_STATUS_OPT_UPDATE_INDEX;
+
+ cl_git_pass(git_repository_head(&head, repo));
+ cl_git_pass(git_reference_peel(&head_object, head, GIT_OBJECT_COMMIT));
+
+ cl_git_pass(git_reset(repo, head_object, GIT_RESET_HARD, NULL));
+
+ cl_git_rewritefile("testrepo/README", "This was rewritten.");
+
+ /* this status rewrites the index because we have changed the
+ * contents of a tracked file
+ */
+ counts.expected_entry_count = 1;
+ counts.expected_paths = expected_paths;
+ counts.expected_statuses = expected_statuses;
+
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__normal, &counts));
+ cl_assert_equal_i(1, counts.entry_count);
+
+ /* now ensure that the status's rewrite of the index did not screw
+ * up the mode of the symlink `link_to_new.txt`, particularly
+ * on platforms that don't support symlinks
+ */
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_read(index, true));
+
+ cl_assert(idx_entry = git_index_get_bypath(index, "link_to_new.txt", 0));
+ cl_assert(S_ISLNK(idx_entry->mode));
+
+ git_index_free(index);
+ git_object_free(head_object);
+ git_reference_free(head);
+}
+
+static const char *testrepo2_subdir_paths[] = {
+ "subdir/README",
+ "subdir/new.txt",
+ "subdir/subdir2/README",
+ "subdir/subdir2/new.txt",
+};
+
+static const char *testrepo2_subdir_paths_icase[] = {
+ "subdir/new.txt",
+ "subdir/README",
+ "subdir/subdir2/new.txt",
+ "subdir/subdir2/README"
+};
+
+void test_status_worktree__with_directory_in_pathlist(void)
+{
+ git_repository *repo = cl_git_sandbox_init("testrepo2");
+ git_index *index;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ git_status_list *statuslist;
+ const git_status_entry *status;
+ size_t i, entrycount;
+ bool native_ignore_case;
+ char *subdir_path = "subdir";
+
+ cl_git_pass(git_repository_index(&index, repo));
+ native_ignore_case =
+ (git_index_caps(index) & GIT_INDEX_CAPABILITY_IGNORE_CASE) != 0;
+ git_index_free(index);
+
+ opts.pathspec.strings = &subdir_path;
+ opts.pathspec.count = 1;
+ opts.flags =
+ GIT_STATUS_OPT_DEFAULTS |
+ GIT_STATUS_OPT_INCLUDE_UNMODIFIED |
+ GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH;
+
+ opts.show = GIT_STATUS_SHOW_WORKDIR_ONLY;
+ git_status_list_new(&statuslist, repo, &opts);
+
+ entrycount = git_status_list_entrycount(statuslist);
+ cl_assert_equal_i(4, entrycount);
+
+ for (i = 0; i < entrycount; i++) {
+ status = git_status_byindex(statuslist, i);
+ cl_assert_equal_i(0, status->status);
+ cl_assert_equal_s(native_ignore_case ?
+ testrepo2_subdir_paths_icase[i] :
+ testrepo2_subdir_paths[i],
+ status->index_to_workdir->old_file.path);
+ }
+
+ git_status_list_free(statuslist);
+
+ opts.show = GIT_STATUS_SHOW_INDEX_ONLY;
+ git_status_list_new(&statuslist, repo, &opts);
+
+ entrycount = git_status_list_entrycount(statuslist);
+ cl_assert_equal_i(4, entrycount);
+
+ for (i = 0; i < entrycount; i++) {
+ status = git_status_byindex(statuslist, i);
+ cl_assert_equal_i(0, status->status);
+ cl_assert_equal_s(native_ignore_case ?
+ testrepo2_subdir_paths_icase[i] :
+ testrepo2_subdir_paths[i],
+ status->head_to_index->old_file.path);
+ }
+
+ git_status_list_free(statuslist);
+
+ opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
+ git_status_list_new(&statuslist, repo, &opts);
+
+ entrycount = git_status_list_entrycount(statuslist);
+ cl_assert_equal_i(4, entrycount);
+
+ for (i = 0; i < entrycount; i++) {
+ status = git_status_byindex(statuslist, i);
+ cl_assert_equal_i(0, status->status);
+ cl_assert_equal_s(native_ignore_case ?
+ testrepo2_subdir_paths_icase[i] :
+ testrepo2_subdir_paths[i],
+ status->index_to_workdir->old_file.path);
+ }
+
+ git_status_list_free(statuslist);
+}
+
+void test_status_worktree__at_head_parent(void)
+{
+ git_repository *repo = cl_git_sandbox_init("empty_standard_repo");
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ git_status_list *statuslist;
+ git_tree *parent_tree;
+ const git_status_entry *status;
+
+ cl_git_mkfile("empty_standard_repo/file1", "ping");
+ stage_and_commit(repo, "file1");
+
+ cl_git_pass(git_repository_head_tree(&parent_tree, repo));
+
+ cl_git_mkfile("empty_standard_repo/file2", "pong");
+ stage_and_commit(repo, "file2");
+
+ cl_git_rewritefile("empty_standard_repo/file2", "pyng");
+
+ opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR;
+ opts.baseline = parent_tree;
+ cl_git_pass(git_status_list_new(&statuslist, repo, &opts));
+
+ cl_assert_equal_sz(1, git_status_list_entrycount(statuslist));
+ status = git_status_byindex(statuslist, 0);
+ cl_assert(status != NULL);
+ cl_assert_equal_s("file2", status->index_to_workdir->old_file.path);
+ cl_assert_equal_i(GIT_STATUS_WT_MODIFIED | GIT_STATUS_INDEX_NEW, status->status);
+
+ git_tree_free(parent_tree);
+ git_status_list_free(statuslist);
+}
diff --git a/tests/libgit2/status/worktree_init.c b/tests/libgit2/status/worktree_init.c
new file mode 100644
index 0000000..db6e71f
--- /dev/null
+++ b/tests/libgit2/status/worktree_init.c
@@ -0,0 +1,339 @@
+#include "clar_libgit2.h"
+#include "git2/sys/repository.h"
+
+#include "futils.h"
+#include "ignore.h"
+#include "status_helpers.h"
+#include "posix.h"
+#include "util.h"
+#include "path.h"
+#include "index.h"
+
+static void cleanup_new_repo(void *path)
+{
+ cl_fixture_cleanup((char *)path);
+}
+
+void test_status_worktree_init__cannot_retrieve_the_status_of_a_bare_repository(void)
+{
+ git_repository *repo;
+ unsigned int status = 0;
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+ cl_assert_equal_i(GIT_EBAREREPO, git_status_file(&status, repo, "dummy"));
+ git_repository_free(repo);
+}
+
+void test_status_worktree_init__first_commit_in_progress(void)
+{
+ git_repository *repo;
+ git_index *index;
+ status_entry_single result;
+
+ cl_set_cleanup(&cleanup_new_repo, "getting_started");
+
+ cl_git_pass(git_repository_init(&repo, "getting_started", 0));
+ cl_git_mkfile("getting_started/testfile.txt", "content\n");
+
+ memset(&result, 0, sizeof(result));
+ cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
+ cl_assert_equal_i(1, result.count);
+ cl_assert(result.status == GIT_STATUS_WT_NEW);
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_add_bypath(index, "testfile.txt"));
+ cl_git_pass(git_index_write(index));
+
+ memset(&result, 0, sizeof(result));
+ cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
+ cl_assert_equal_i(1, result.count);
+ cl_assert(result.status == GIT_STATUS_INDEX_NEW);
+
+ git_index_free(index);
+ git_repository_free(repo);
+}
+
+
+
+void test_status_worktree_init__status_file_without_index_or_workdir(void)
+{
+ git_repository *repo;
+ unsigned int status = 0;
+ git_index *index;
+
+ cl_git_pass(p_mkdir("wd", 0777));
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+ cl_git_pass(git_repository_set_workdir(repo, "wd", false));
+
+ cl_git_pass(git_index__open(&index, "empty-index", GIT_OID_SHA1));
+ cl_assert_equal_i(0, (int)git_index_entrycount(index));
+ git_repository_set_index(repo, index);
+
+ cl_git_pass(git_status_file(&status, repo, "branch_file.txt"));
+
+ cl_assert_equal_i(GIT_STATUS_INDEX_DELETED, status);
+
+ git_repository_free(repo);
+ git_index_free(index);
+ cl_git_pass(p_rmdir("wd"));
+}
+
+static void fill_index_wth_head_entries(git_repository *repo, git_index *index)
+{
+ git_oid oid;
+ git_commit *commit;
+ git_tree *tree;
+
+ cl_git_pass(git_reference_name_to_id(&oid, repo, "HEAD"));
+ cl_git_pass(git_commit_lookup(&commit, repo, &oid));
+ cl_git_pass(git_commit_tree(&tree, commit));
+
+ cl_git_pass(git_index_read_tree(index, tree));
+ cl_git_pass(git_index_write(index));
+
+ git_tree_free(tree);
+ git_commit_free(commit);
+}
+
+void test_status_worktree_init__status_file_with_clean_index_and_empty_workdir(void)
+{
+ git_repository *repo;
+ unsigned int status = 0;
+ git_index *index;
+
+ cl_git_pass(p_mkdir("wd", 0777));
+
+ cl_git_pass(git_repository_open(&repo, cl_fixture("testrepo.git")));
+ cl_git_pass(git_repository_set_workdir(repo, "wd", false));
+
+ cl_git_pass(git_index__open(&index, "my-index", GIT_OID_SHA1));
+ fill_index_wth_head_entries(repo, index);
+
+ git_repository_set_index(repo, index);
+
+ cl_git_pass(git_status_file(&status, repo, "branch_file.txt"));
+
+ cl_assert_equal_i(GIT_STATUS_WT_DELETED, status);
+
+ git_repository_free(repo);
+ git_index_free(index);
+ cl_git_pass(p_rmdir("wd"));
+ cl_git_pass(p_unlink("my-index"));
+}
+
+void test_status_worktree_init__bracket_in_filename(void)
+{
+ git_repository *repo;
+ git_index *index;
+ status_entry_single result;
+ unsigned int status_flags;
+
+ #define FILE_WITH_BRACKET "LICENSE[1].md"
+ #define FILE_WITHOUT_BRACKET "LICENSE1.md"
+
+ cl_set_cleanup(&cleanup_new_repo, "with_bracket");
+
+ cl_git_pass(git_repository_init(&repo, "with_bracket", 0));
+ cl_git_mkfile("with_bracket/" FILE_WITH_BRACKET, "I have a bracket in my name\n");
+
+ /* file is new to working directory */
+
+ memset(&result, 0, sizeof(result));
+ cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
+ cl_assert_equal_i(1, result.count);
+ cl_assert(result.status == GIT_STATUS_WT_NEW);
+
+ cl_git_pass(git_status_file(&status_flags, repo, FILE_WITH_BRACKET));
+ cl_assert(status_flags == GIT_STATUS_WT_NEW);
+
+ /* ignore the file */
+
+ cl_git_rewritefile("with_bracket/.gitignore", "*.md\n.gitignore\n");
+
+ memset(&result, 0, sizeof(result));
+ cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
+ cl_assert_equal_i(2, result.count);
+ cl_assert(result.status == GIT_STATUS_IGNORED);
+
+ cl_git_pass(git_status_file(&status_flags, repo, FILE_WITH_BRACKET));
+ cl_assert(status_flags == GIT_STATUS_IGNORED);
+
+ /* don't ignore the file */
+
+ cl_git_rewritefile("with_bracket/.gitignore", ".gitignore\n");
+
+ memset(&result, 0, sizeof(result));
+ cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
+ cl_assert_equal_i(2, result.count);
+ cl_assert(result.status == GIT_STATUS_WT_NEW);
+
+ cl_git_pass(git_status_file(&status_flags, repo, FILE_WITH_BRACKET));
+ cl_assert(status_flags == GIT_STATUS_WT_NEW);
+
+ /* add the file to the index */
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_add_bypath(index, FILE_WITH_BRACKET));
+ cl_git_pass(git_index_write(index));
+
+ memset(&result, 0, sizeof(result));
+ cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
+ cl_assert_equal_i(2, result.count);
+ cl_assert(result.status == GIT_STATUS_INDEX_NEW);
+
+ cl_git_pass(git_status_file(&status_flags, repo, FILE_WITH_BRACKET));
+ cl_assert(status_flags == GIT_STATUS_INDEX_NEW);
+
+ /* Create file without bracket */
+
+ cl_git_mkfile("with_bracket/" FILE_WITHOUT_BRACKET, "I have no bracket in my name!\n");
+
+ cl_git_pass(git_status_file(&status_flags, repo, FILE_WITHOUT_BRACKET));
+ cl_assert(status_flags == GIT_STATUS_WT_NEW);
+
+ cl_git_fail_with(git_status_file(&status_flags, repo, "LICENSE\\[1\\].md"), GIT_ENOTFOUND);
+
+ cl_git_pass(git_status_file(&status_flags, repo, FILE_WITH_BRACKET));
+ cl_assert(status_flags == GIT_STATUS_INDEX_NEW);
+
+ git_index_free(index);
+ git_repository_free(repo);
+}
+
+void test_status_worktree_init__space_in_filename(void)
+{
+ git_repository *repo;
+ git_index *index;
+ status_entry_single result;
+ unsigned int status_flags;
+
+#define FILE_WITH_SPACE "LICENSE - copy.md"
+
+ cl_set_cleanup(&cleanup_new_repo, "with_space");
+ cl_git_pass(git_repository_init(&repo, "with_space", 0));
+ cl_git_mkfile("with_space/" FILE_WITH_SPACE, "I have a space in my name\n");
+
+ /* file is new to working directory */
+
+ memset(&result, 0, sizeof(result));
+ cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
+ cl_assert_equal_i(1, result.count);
+ cl_assert(result.status == GIT_STATUS_WT_NEW);
+
+ cl_git_pass(git_status_file(&status_flags, repo, FILE_WITH_SPACE));
+ cl_assert(status_flags == GIT_STATUS_WT_NEW);
+
+ /* ignore the file */
+
+ cl_git_rewritefile("with_space/.gitignore", "*.md\n.gitignore\n");
+
+ memset(&result, 0, sizeof(result));
+ cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
+ cl_assert_equal_i(2, result.count);
+ cl_assert(result.status == GIT_STATUS_IGNORED);
+
+ cl_git_pass(git_status_file(&status_flags, repo, FILE_WITH_SPACE));
+ cl_assert(status_flags == GIT_STATUS_IGNORED);
+
+ /* don't ignore the file */
+
+ cl_git_rewritefile("with_space/.gitignore", ".gitignore\n");
+
+ memset(&result, 0, sizeof(result));
+ cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
+ cl_assert_equal_i(2, result.count);
+ cl_assert(result.status == GIT_STATUS_WT_NEW);
+
+ cl_git_pass(git_status_file(&status_flags, repo, FILE_WITH_SPACE));
+ cl_assert(status_flags == GIT_STATUS_WT_NEW);
+
+ /* add the file to the index */
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_add_bypath(index, FILE_WITH_SPACE));
+ cl_git_pass(git_index_write(index));
+
+ memset(&result, 0, sizeof(result));
+ cl_git_pass(git_status_foreach(repo, cb_status__single, &result));
+ cl_assert_equal_i(2, result.count);
+ cl_assert(result.status == GIT_STATUS_INDEX_NEW);
+
+ cl_git_pass(git_status_file(&status_flags, repo, FILE_WITH_SPACE));
+ cl_assert(status_flags == GIT_STATUS_INDEX_NEW);
+
+ git_index_free(index);
+ git_repository_free(repo);
+}
+
+static int cb_status__expected_path(const char *p, unsigned int s, void *payload)
+{
+ const char *expected_path = (const char *)payload;
+
+ GIT_UNUSED(s);
+
+ if (payload == NULL)
+ cl_fail("Unexpected path");
+
+ cl_assert_equal_s(expected_path, p);
+
+ return 0;
+}
+
+void test_status_worktree_init__disable_pathspec_match(void)
+{
+ git_repository *repo;
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ char *file_with_bracket = "LICENSE[1].md",
+ *imaginary_file_with_bracket = "LICENSE[1-2].md";
+
+ cl_set_cleanup(&cleanup_new_repo, "pathspec");
+ cl_git_pass(git_repository_init(&repo, "pathspec", 0));
+ cl_git_mkfile("pathspec/LICENSE[1].md", "screaming bracket\n");
+ cl_git_mkfile("pathspec/LICENSE1.md", "no bracket\n");
+
+ opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH;
+ opts.pathspec.count = 1;
+ opts.pathspec.strings = &file_with_bracket;
+
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__expected_path,
+ file_with_bracket)
+ );
+
+ /* Test passing a pathspec matching files in the workdir. */
+ /* Must not match because pathspecs are disabled. */
+ opts.pathspec.strings = &imaginary_file_with_bracket;
+ cl_git_pass(
+ git_status_foreach_ext(repo, &opts, cb_status__expected_path, NULL)
+ );
+
+ git_repository_free(repo);
+}
+
+void test_status_worktree_init__new_staged_file_must_handle_crlf(void)
+{
+ git_repository *repo;
+ git_index *index;
+ unsigned int status;
+
+ cl_set_cleanup(&cleanup_new_repo, "getting_started");
+ cl_git_pass(git_repository_init(&repo, "getting_started", 0));
+
+ /* Ensure that repo has core.autocrlf=true */
+ cl_repo_set_bool(repo, "core.autocrlf", true);
+
+ cl_git_mkfile("getting_started/testfile.txt", "content\r\n"); /* Content with CRLF */
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_add_bypath(index, "testfile.txt"));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_status_file(&status, repo, "testfile.txt"));
+ cl_assert_equal_i(GIT_STATUS_INDEX_NEW, status);
+
+ git_index_free(index);
+ git_repository_free(repo);
+}
+
diff --git a/tests/libgit2/stream/deprecated.c b/tests/libgit2/stream/deprecated.c
new file mode 100644
index 0000000..fecd1be
--- /dev/null
+++ b/tests/libgit2/stream/deprecated.c
@@ -0,0 +1,60 @@
+#include "clar_libgit2.h"
+#include "git2/sys/stream.h"
+#include "streams/tls.h"
+#include "streams/socket.h"
+#include "stream.h"
+
+void test_stream_deprecated__cleanup(void)
+{
+ cl_git_pass(git_stream_register(GIT_STREAM_TLS | GIT_STREAM_STANDARD, NULL));
+}
+
+#ifndef GIT_DEPRECATE_HARD
+static git_stream test_stream;
+static int ctor_called;
+
+static int test_stream_init(git_stream **out, const char *host, const char *port)
+{
+ GIT_UNUSED(host);
+ GIT_UNUSED(port);
+
+ ctor_called = 1;
+ *out = &test_stream;
+
+ return 0;
+}
+#endif
+
+void test_stream_deprecated__register_tls(void)
+{
+#ifndef GIT_DEPRECATE_HARD
+ git_stream *stream;
+ int error;
+
+ ctor_called = 0;
+ cl_git_pass(git_stream_register_tls(test_stream_init));
+ cl_git_pass(git_tls_stream_new(&stream, "localhost", "443"));
+ cl_assert_equal_i(1, ctor_called);
+ cl_assert_equal_p(&test_stream, stream);
+
+ ctor_called = 0;
+ stream = NULL;
+ cl_git_pass(git_stream_register_tls(NULL));
+ error = git_tls_stream_new(&stream, "localhost", "443");
+
+ /*
+ * We don't have TLS support enabled, or we're on Windows,
+ * which has no arbitrary TLS stream support.
+ */
+#if defined(GIT_WIN32) || !defined(GIT_HTTPS)
+ cl_git_fail_with(-1, error);
+#else
+ cl_git_pass(error);
+#endif
+
+ cl_assert_equal_i(0, ctor_called);
+ cl_assert(&test_stream != stream);
+
+ git_stream_free(stream);
+#endif
+}
diff --git a/tests/libgit2/stream/registration.c b/tests/libgit2/stream/registration.c
new file mode 100644
index 0000000..ccaecee
--- /dev/null
+++ b/tests/libgit2/stream/registration.c
@@ -0,0 +1,119 @@
+#include "clar_libgit2.h"
+#include "git2/sys/stream.h"
+#include "streams/tls.h"
+#include "streams/socket.h"
+#include "stream.h"
+
+static git_stream test_stream;
+static int ctor_called;
+
+void test_stream_registration__cleanup(void)
+{
+ cl_git_pass(git_stream_register(GIT_STREAM_TLS | GIT_STREAM_STANDARD, NULL));
+}
+
+static int test_stream_init(git_stream **out, const char *host, const char *port)
+{
+ GIT_UNUSED(host);
+ GIT_UNUSED(port);
+
+ ctor_called = 1;
+ *out = &test_stream;
+
+ return 0;
+}
+
+static int test_stream_wrap(git_stream **out, git_stream *in, const char *host)
+{
+ GIT_UNUSED(in);
+ GIT_UNUSED(host);
+
+ ctor_called = 1;
+ *out = &test_stream;
+
+ return 0;
+}
+
+void test_stream_registration__insecure(void)
+{
+ git_stream *stream;
+ git_stream_registration registration = {0};
+
+ registration.version = 1;
+ registration.init = test_stream_init;
+ registration.wrap = test_stream_wrap;
+
+ ctor_called = 0;
+ cl_git_pass(git_stream_register(GIT_STREAM_STANDARD, &registration));
+ cl_git_pass(git_socket_stream_new(&stream, "localhost", "80"));
+ cl_assert_equal_i(1, ctor_called);
+ cl_assert_equal_p(&test_stream, stream);
+
+ ctor_called = 0;
+ stream = NULL;
+ cl_git_pass(git_stream_register(GIT_STREAM_STANDARD, NULL));
+ cl_git_pass(git_socket_stream_new(&stream, "localhost", "80"));
+
+ cl_assert_equal_i(0, ctor_called);
+ cl_assert(&test_stream != stream);
+
+ git_stream_free(stream);
+}
+
+void test_stream_registration__tls(void)
+{
+ git_stream *stream;
+ git_stream_registration registration = {0};
+ int error;
+
+ registration.version = 1;
+ registration.init = test_stream_init;
+ registration.wrap = test_stream_wrap;
+
+ ctor_called = 0;
+ cl_git_pass(git_stream_register(GIT_STREAM_TLS, &registration));
+ cl_git_pass(git_tls_stream_new(&stream, "localhost", "443"));
+ cl_assert_equal_i(1, ctor_called);
+ cl_assert_equal_p(&test_stream, stream);
+
+ ctor_called = 0;
+ stream = NULL;
+ cl_git_pass(git_stream_register(GIT_STREAM_TLS, NULL));
+ error = git_tls_stream_new(&stream, "localhost", "443");
+
+ /* We don't have TLS support enabled, or we're on Windows
+ * with WinHTTP, which is not actually TLS stream support.
+ */
+#if defined(GIT_WINHTTP) || !defined(GIT_HTTPS)
+ cl_git_fail_with(-1, error);
+#else
+ cl_git_pass(error);
+#endif
+
+ cl_assert_equal_i(0, ctor_called);
+ cl_assert(&test_stream != stream);
+
+ git_stream_free(stream);
+}
+
+void test_stream_registration__both(void)
+{
+ git_stream *stream;
+ git_stream_registration registration = {0};
+
+ registration.version = 1;
+ registration.init = test_stream_init;
+ registration.wrap = test_stream_wrap;
+
+ cl_git_pass(git_stream_register(GIT_STREAM_STANDARD | GIT_STREAM_TLS, &registration));
+
+ ctor_called = 0;
+ cl_git_pass(git_tls_stream_new(&stream, "localhost", "443"));
+ cl_assert_equal_i(1, ctor_called);
+ cl_assert_equal_p(&test_stream, stream);
+
+ ctor_called = 0;
+ cl_git_pass(git_socket_stream_new(&stream, "localhost", "80"));
+ cl_assert_equal_i(1, ctor_called);
+ cl_assert_equal_p(&test_stream, stream);
+}
diff --git a/tests/libgit2/stress/diff.c b/tests/libgit2/stress/diff.c
new file mode 100644
index 0000000..aecf08b
--- /dev/null
+++ b/tests/libgit2/stress/diff.c
@@ -0,0 +1,146 @@
+#include "clar_libgit2.h"
+#include "../diff/diff_helpers.h"
+
+static git_repository *g_repo = NULL;
+
+void test_stress_diff__initialize(void)
+{
+}
+
+void test_stress_diff__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+#define ANOTHER_POEM \
+"OH, glorious are the guarded heights\nWhere guardian souls abide—\nSelf-exiled from our gross delights—\nAbove, beyond, outside:\nAn ampler arc their spirit swings—\nCommands a juster view—\nWe have their word for all these things,\nNo doubt their words are true.\n\nYet we, the bond slaves of our day,\nWhom dirt and danger press—\nCo-heirs of insolence, delay,\nAnd leagued unfaithfulness—\nSuch is our need must seek indeed\nAnd, having found, engage\nThe men who merely do the work\nFor which they draw the wage.\n\nFrom forge and farm and mine and bench,\nDeck, altar, outpost lone—\nMill, school, battalion, counter, trench,\nRail, senate, sheepfold, throne—\nCreation's cry goes up on high\nFrom age to cheated age:\n\"Send us the men who do the work\n\"For which they draw the wage!\"\n"
+
+static void test_with_many(int expected_new)
+{
+ git_index *index;
+ git_tree *tree, *new_tree;
+ git_diff *diff = NULL;
+ diff_expects exp;
+ git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT;
+ git_diff_find_options opts = GIT_DIFF_FIND_OPTIONS_INIT;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(
+ git_revparse_single((git_object **)&tree, g_repo, "HEAD^{tree}"));
+
+ cl_git_pass(p_rename("renames/ikeepsix.txt", "renames/ikeepsix2.txt"));
+ cl_git_pass(git_index_remove_bypath(index, "ikeepsix.txt"));
+ cl_git_pass(git_index_add_bypath(index, "ikeepsix2.txt"));
+ cl_git_pass(git_index_write(index));
+
+ cl_git_pass(git_diff_tree_to_index(&diff, g_repo, tree, index, &diffopts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, NULL, NULL, NULL, &exp));
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(expected_new + 1, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(expected_new + 2, exp.files);
+
+ opts.flags = GIT_DIFF_FIND_ALL;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, NULL, NULL, NULL, &exp));
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
+ cl_assert_equal_i(expected_new, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(expected_new + 1, exp.files);
+
+ git_diff_free(diff);
+
+ cl_repo_commit_from_index(NULL, g_repo, NULL, 1372350000, "yoyoyo");
+ cl_git_pass(git_revparse_single(
+ (git_object **)&new_tree, g_repo, "HEAD^{tree}"));
+
+ cl_git_pass(git_diff_tree_to_tree(
+ &diff, g_repo, tree, new_tree, &diffopts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, NULL, NULL, NULL, &exp));
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_DELETED]);
+ cl_assert_equal_i(expected_new + 1, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(expected_new + 2, exp.files);
+
+ opts.flags = GIT_DIFF_FIND_ALL;
+ cl_git_pass(git_diff_find_similar(diff, &opts));
+
+ memset(&exp, 0, sizeof(exp));
+ cl_git_pass(git_diff_foreach(
+ diff, diff_file_cb, NULL, NULL, NULL, &exp));
+ cl_assert_equal_i(1, exp.file_status[GIT_DELTA_RENAMED]);
+ cl_assert_equal_i(expected_new, exp.file_status[GIT_DELTA_ADDED]);
+ cl_assert_equal_i(expected_new + 1, exp.files);
+
+ git_diff_free(diff);
+
+ git_tree_free(new_tree);
+ git_tree_free(tree);
+ git_index_free(index);
+}
+
+void test_stress_diff__rename_big_files(void)
+{
+ git_index *index;
+ char tmp[64];
+ int i, j;
+ git_str b = GIT_STR_INIT;
+
+ g_repo = cl_git_sandbox_init("renames");
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ for (i = 0; i < 100; i += 1) {
+ p_snprintf(tmp, sizeof(tmp), "renames/newfile%03d", i);
+ for (j = i * 256; j > 0; --j)
+ git_str_printf(&b, "more content %d\n", i);
+ cl_git_mkfile(tmp, b.ptr);
+ }
+
+ for (i = 0; i < 100; i += 1) {
+ p_snprintf(tmp, sizeof(tmp), "renames/newfile%03d", i);
+ cl_git_pass(git_index_add_bypath(index, tmp + strlen("renames/")));
+ }
+
+ git_str_dispose(&b);
+ git_index_free(index);
+
+ test_with_many(100);
+}
+
+void test_stress_diff__rename_many_files(void)
+{
+ git_index *index;
+ char tmp[64];
+ int i;
+ git_str b = GIT_STR_INIT;
+
+ g_repo = cl_git_sandbox_init("renames");
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+
+ git_str_printf(&b, "%08d\n" ANOTHER_POEM "%08d\n" ANOTHER_POEM ANOTHER_POEM, 0, 0);
+
+ for (i = 0; i < 2500; i += 1) {
+ p_snprintf(tmp, sizeof(tmp), "renames/newfile%03d", i);
+ p_snprintf(b.ptr, 9, "%08d", i);
+ b.ptr[8] = '\n';
+ cl_git_mkfile(tmp, b.ptr);
+ }
+ git_str_dispose(&b);
+
+ for (i = 0; i < 2500; i += 1) {
+ p_snprintf(tmp, sizeof(tmp), "renames/newfile%03d", i);
+ cl_git_pass(git_index_add_bypath(index, tmp + strlen("renames/")));
+ }
+
+ git_index_free(index);
+
+ test_with_many(2500);
+}
diff --git a/tests/libgit2/submodule/add.c b/tests/libgit2/submodule/add.c
new file mode 100644
index 0000000..a2a66e7
--- /dev/null
+++ b/tests/libgit2/submodule/add.c
@@ -0,0 +1,251 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "path.h"
+#include "submodule_helpers.h"
+#include "config/config_helpers.h"
+#include "futils.h"
+#include "repository.h"
+#include "git2/sys/commit.h"
+
+static git_repository *g_repo = NULL;
+static const char *valid_blob_id = "fa49b077972391ad58037050f2a75f74e3671e92";
+
+void test_submodule_add__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static void assert_submodule_url(const char* name, const char *url)
+{
+ git_str key = GIT_STR_INIT;
+
+
+ cl_git_pass(git_str_printf(&key, "submodule.%s.url", name));
+ assert_config_entry_value(g_repo, git_str_cstr(&key), url);
+
+ git_str_dispose(&key);
+}
+
+void test_submodule_add__url_absolute(void)
+{
+ git_submodule *sm;
+ git_repository *repo;
+ git_str dot_git_content = GIT_STR_INIT;
+
+ g_repo = setup_fixture_submod2();
+
+ /* re-add existing submodule */
+ cl_git_fail_with(
+ GIT_EEXISTS,
+ git_submodule_add_setup(NULL, g_repo, "whatever", "sm_unchanged", 1));
+
+ /* add a submodule using a gitlink */
+
+ cl_git_pass(
+ git_submodule_add_setup(&sm, g_repo, "https://github.com/libgit2/libgit2.git", "sm_libgit2", 1)
+ );
+ git_submodule_free(sm);
+
+ cl_assert(git_fs_path_isfile("submod2/" "sm_libgit2" "/.git"));
+
+ cl_assert(git_fs_path_isdir("submod2/.git/modules"));
+ cl_assert(git_fs_path_isdir("submod2/.git/modules/" "sm_libgit2"));
+ cl_assert(git_fs_path_isfile("submod2/.git/modules/" "sm_libgit2" "/HEAD"));
+ assert_submodule_url("sm_libgit2", "https://github.com/libgit2/libgit2.git");
+
+ cl_git_pass(git_repository_open(&repo, "submod2/" "sm_libgit2"));
+
+ /* Verify worktree path is relative */
+ assert_config_entry_value(repo, "core.worktree", "../../../sm_libgit2/");
+
+ /* Verify gitdir path is relative */
+ cl_git_pass(git_futils_readbuffer(&dot_git_content, "submod2/" "sm_libgit2" "/.git"));
+ cl_assert_equal_s("gitdir: ../.git/modules/sm_libgit2/", dot_git_content.ptr);
+
+ git_repository_free(repo);
+ git_str_dispose(&dot_git_content);
+
+ /* add a submodule not using a gitlink */
+
+ cl_git_pass(
+ git_submodule_add_setup(&sm, g_repo, "https://github.com/libgit2/libgit2.git", "sm_libgit2b", 0)
+ );
+ git_submodule_free(sm);
+
+ cl_assert(git_fs_path_isdir("submod2/" "sm_libgit2b" "/.git"));
+ cl_assert(git_fs_path_isfile("submod2/" "sm_libgit2b" "/.git/HEAD"));
+ cl_assert(!git_fs_path_exists("submod2/.git/modules/" "sm_libgit2b"));
+ assert_submodule_url("sm_libgit2b", "https://github.com/libgit2/libgit2.git");
+}
+
+void test_submodule_add__url_relative(void)
+{
+ git_submodule *sm;
+ git_remote *remote;
+ git_strarray problems = {0};
+
+ /* default remote url is https://github.com/libgit2/false.git */
+ g_repo = cl_git_sandbox_init("testrepo2");
+
+ /* make sure we don't default to origin - rename origin -> test_remote */
+ cl_git_pass(git_remote_rename(&problems, g_repo, "origin", "test_remote"));
+ cl_assert_equal_i(0, problems.count);
+ git_strarray_dispose(&problems);
+ cl_git_fail(git_remote_lookup(&remote, g_repo, "origin"));
+
+ cl_git_pass(
+ git_submodule_add_setup(&sm, g_repo, "../TestGitRepository", "TestGitRepository", 1)
+ );
+ git_submodule_free(sm);
+
+ assert_submodule_url("TestGitRepository", "https://github.com/libgit2/TestGitRepository");
+}
+
+void test_submodule_add__url_relative_to_origin(void)
+{
+ git_submodule *sm;
+
+ /* default remote url is https://github.com/libgit2/false.git */
+ g_repo = cl_git_sandbox_init("testrepo2");
+
+ cl_git_pass(
+ git_submodule_add_setup(&sm, g_repo, "../TestGitRepository", "TestGitRepository", 1)
+ );
+ git_submodule_free(sm);
+
+ assert_submodule_url("TestGitRepository", "https://github.com/libgit2/TestGitRepository");
+}
+
+void test_submodule_add__url_relative_to_workdir(void)
+{
+ git_submodule *sm;
+
+ /* In this repo, HEAD (master) has no remote tracking branc h*/
+ g_repo = cl_git_sandbox_init("testrepo");
+
+ cl_git_pass(
+ git_submodule_add_setup(&sm, g_repo, "./", "TestGitRepository", 1)
+ );
+ git_submodule_free(sm);
+
+ assert_submodule_url("TestGitRepository", git_repository_workdir(g_repo));
+}
+
+static void test_add_entry(
+ git_index *index,
+ const char *idstr,
+ const char *path,
+ git_filemode_t mode)
+{
+ git_index_entry entry = {{0}};
+
+ cl_git_pass(git_oid__fromstr(&entry.id, idstr, GIT_OID_SHA1));
+
+ entry.path = path;
+ entry.mode = mode;
+
+ cl_git_pass(git_index_add(index, &entry));
+}
+
+void test_submodule_add__path_exists_in_index(void)
+{
+ git_index *index;
+ git_submodule *sm;
+ git_str filename = GIT_STR_INIT;
+
+ g_repo = cl_git_sandbox_init("testrepo");
+
+ cl_git_pass(git_str_joinpath(&filename, "subdirectory", "test.txt"));
+
+ cl_git_pass(git_repository_index__weakptr(&index, g_repo));
+
+ test_add_entry(index, valid_blob_id, filename.ptr, GIT_FILEMODE_BLOB);
+
+ cl_git_fail_with(git_submodule_add_setup(&sm, g_repo, "./", "subdirectory", 1), GIT_EEXISTS);
+
+ git_submodule_free(sm);
+ git_str_dispose(&filename);
+}
+
+void test_submodule_add__file_exists_in_index(void)
+{
+ git_index *index;
+ git_submodule *sm;
+ git_str name = GIT_STR_INIT;
+
+ g_repo = cl_git_sandbox_init("testrepo");
+
+ cl_git_pass(git_repository_index__weakptr(&index, g_repo));
+
+ test_add_entry(index, valid_blob_id, "subdirectory", GIT_FILEMODE_BLOB);
+
+ cl_git_fail_with(git_submodule_add_setup(&sm, g_repo, "./", "subdirectory", 1), GIT_EEXISTS);
+
+ git_submodule_free(sm);
+ git_str_dispose(&name);
+}
+
+void test_submodule_add__submodule_clone(void)
+{
+ git_oid tree_id, commit_id;
+ git_signature *sig;
+ git_submodule *sm;
+ git_index *index;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ /* Create the submodule structure, clone into it and finalize */
+ cl_git_pass(git_submodule_add_setup(&sm, g_repo, cl_fixture("testrepo.git"), "testrepo-add", true));
+ cl_git_pass(git_submodule_clone(NULL, sm, NULL));
+ cl_git_pass(git_submodule_add_finalize(sm));
+
+ /* Create the submodule commit */
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_index_write_tree(&tree_id, index));
+ cl_git_pass(git_signature_now(&sig, "Submoduler", "submoduler@local"));
+ cl_git_pass(git_commit_create_from_ids(&commit_id, g_repo, "HEAD", sig, sig, NULL, "A submodule\n",
+ &tree_id, 0, NULL));
+
+ assert_submodule_exists(g_repo, "testrepo-add");
+
+ git_signature_free(sig);
+ git_submodule_free(sm);
+ git_index_free(index);
+}
+
+void test_submodule_add__submodule_clone_into_nonempty_dir_succeeds(void)
+{
+ git_submodule *sm;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ cl_git_pass(p_mkdir("empty_standard_repo/sm", 0777));
+ cl_git_mkfile("empty_standard_repo/sm/foobar", "");
+
+ /* Create the submodule structure, clone into it and finalize */
+ cl_git_pass(git_submodule_add_setup(&sm, g_repo, cl_fixture("testrepo.git"), "sm", true));
+ cl_git_pass(git_submodule_clone(NULL, sm, NULL));
+ cl_git_pass(git_submodule_add_finalize(sm));
+
+ cl_assert(git_fs_path_exists("empty_standard_repo/sm/foobar"));
+
+ assert_submodule_exists(g_repo, "sm");
+
+ git_submodule_free(sm);
+}
+
+void test_submodule_add__submodule_clone_twice_fails(void)
+{
+ git_submodule *sm;
+
+ g_repo = cl_git_sandbox_init("empty_standard_repo");
+
+ /* Create the submodule structure, clone into it and finalize */
+ cl_git_pass(git_submodule_add_setup(&sm, g_repo, cl_fixture("testrepo.git"), "sm", true));
+ cl_git_pass(git_submodule_clone(NULL, sm, NULL));
+ cl_git_pass(git_submodule_add_finalize(sm));
+
+ cl_git_fail(git_submodule_clone(NULL, sm, NULL));
+
+ git_submodule_free(sm);
+}
diff --git a/tests/libgit2/submodule/escape.c b/tests/libgit2/submodule/escape.c
new file mode 100644
index 0000000..bcd52b5
--- /dev/null
+++ b/tests/libgit2/submodule/escape.c
@@ -0,0 +1,98 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "path.h"
+#include "submodule_helpers.h"
+#include "futils.h"
+#include "repository.h"
+
+static git_repository *g_repo = NULL;
+
+void test_submodule_escape__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+#define EVIL_SM_NAME "../../modules/evil"
+#define EVIL_SM_NAME_WINDOWS "..\\\\..\\\\modules\\\\evil"
+#define EVIL_SM_NAME_WINDOWS_UNESC "..\\..\\modules\\evil"
+
+static int find_evil(git_submodule *sm, const char *name, void *payload)
+{
+ int *foundit = (int *) payload;
+
+ GIT_UNUSED(sm);
+
+ if (!git__strcmp(EVIL_SM_NAME, name) ||
+ !git__strcmp(EVIL_SM_NAME_WINDOWS_UNESC, name))
+ *foundit = true;
+
+ return 0;
+}
+
+void test_submodule_escape__from_gitdir(void)
+{
+ int foundit;
+ git_submodule *sm;
+ git_str buf = GIT_STR_INIT;
+ unsigned int sm_location;
+
+ g_repo = setup_fixture_submodule_simple();
+
+ cl_git_pass(git_str_joinpath(&buf, git_repository_workdir(g_repo), ".gitmodules"));
+ cl_git_rewritefile(buf.ptr,
+ "[submodule \"" EVIL_SM_NAME "\"]\n"
+ " path = testrepo\n"
+ " url = ../testrepo.git\n");
+ git_str_dispose(&buf);
+
+ /* Find it all the different ways we know about it */
+ foundit = 0;
+ cl_git_pass(git_submodule_foreach(g_repo, find_evil, &foundit));
+ cl_assert_equal_i(0, foundit);
+ cl_git_fail_with(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, EVIL_SM_NAME));
+ /*
+ * We do know about this as it's in the index and HEAD, but the data is
+ * incomplete as there is no configured data for it (we pretend it
+ * doesn't exist). This leaves us with an odd situation but it's
+ * consistent with what we would do if we did add a submodule with no
+ * configuration.
+ */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
+ cl_git_pass(git_submodule_location(&sm_location, sm));
+ cl_assert_equal_i(GIT_SUBMODULE_STATUS_IN_INDEX | GIT_SUBMODULE_STATUS_IN_HEAD, sm_location);
+ git_submodule_free(sm);
+}
+
+void test_submodule_escape__from_gitdir_windows(void)
+{
+ int foundit;
+ git_submodule *sm;
+ git_str buf = GIT_STR_INIT;
+ unsigned int sm_location;
+
+ g_repo = setup_fixture_submodule_simple();
+
+ cl_git_pass(git_str_joinpath(&buf, git_repository_workdir(g_repo), ".gitmodules"));
+ cl_git_rewritefile(buf.ptr,
+ "[submodule \"" EVIL_SM_NAME_WINDOWS "\"]\n"
+ " path = testrepo\n"
+ " url = ../testrepo.git\n");
+ git_str_dispose(&buf);
+
+ /* Find it all the different ways we know about it */
+ foundit = 0;
+ cl_git_pass(git_submodule_foreach(g_repo, find_evil, &foundit));
+ cl_assert_equal_i(0, foundit);
+ cl_git_fail_with(GIT_ENOTFOUND, git_submodule_lookup(&sm, g_repo, EVIL_SM_NAME_WINDOWS_UNESC));
+ /*
+ * We do know about this as it's in the index and HEAD, but the data is
+ * incomplete as there is no configured data for it (we pretend it
+ * doesn't exist). This leaves us with an odd situation but it's
+ * consistent with what we would do if we did add a submodule with no
+ * configuration.
+ */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
+ cl_git_pass(git_submodule_location(&sm_location, sm));
+ cl_assert_equal_i(GIT_SUBMODULE_STATUS_IN_INDEX | GIT_SUBMODULE_STATUS_IN_HEAD, sm_location);
+ git_submodule_free(sm);
+}
diff --git a/tests/libgit2/submodule/init.c b/tests/libgit2/submodule/init.c
new file mode 100644
index 0000000..a8e1291
--- /dev/null
+++ b/tests/libgit2/submodule/init.c
@@ -0,0 +1,115 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "path.h"
+#include "submodule_helpers.h"
+#include "futils.h"
+
+static git_repository *g_repo = NULL;
+
+void test_submodule_init__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_submodule_init__absolute_url(void)
+{
+ git_submodule *sm;
+ git_config *cfg;
+ git_str absolute_url = GIT_STR_INIT;
+ const char *config_url;
+
+ g_repo = setup_fixture_submodule_simple();
+
+ cl_assert(git_fs_path_dirname_r(&absolute_url, git_repository_workdir(g_repo)) > 0);
+ cl_git_pass(git_str_joinpath(&absolute_url, absolute_url.ptr, "testrepo.git"));
+
+ /* write the absolute url to the .gitmodules file*/
+ cl_git_pass(git_submodule_set_url(g_repo, "testrepo", absolute_url.ptr));
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
+
+ /* verify that the .gitmodules is set with an absolute path*/
+ cl_assert_equal_s(absolute_url.ptr, git_submodule_url(sm));
+
+ /* init and verify that absolute path is written to .git/config */
+ cl_git_pass(git_submodule_init(sm, false));
+
+ cl_git_pass(git_repository_config_snapshot(&cfg, g_repo));
+
+ cl_git_pass(git_config_get_string(&config_url, cfg, "submodule.testrepo.url"));
+ cl_assert_equal_s(absolute_url.ptr, config_url);
+
+ git_str_dispose(&absolute_url);
+ git_config_free(cfg);
+ git_submodule_free(sm);
+}
+
+void test_submodule_init__relative_url(void)
+{
+ git_submodule *sm;
+ git_config *cfg;
+ git_str absolute_url = GIT_STR_INIT;
+ const char *config_url;
+
+ g_repo = setup_fixture_submodule_simple();
+
+ cl_assert(git_fs_path_dirname_r(&absolute_url, git_repository_workdir(g_repo)) > 0);
+ cl_git_pass(git_str_joinpath(&absolute_url, absolute_url.ptr, "testrepo.git"));
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
+
+ /* verify that the .gitmodules is set with an absolute path*/
+ cl_assert_equal_s("../testrepo.git", git_submodule_url(sm));
+
+ /* init and verify that absolute path is written to .git/config */
+ cl_git_pass(git_submodule_init(sm, false));
+
+ cl_git_pass(git_repository_config_snapshot(&cfg, g_repo));
+
+ cl_git_pass(git_config_get_string(&config_url, cfg, "submodule.testrepo.url"));
+ cl_assert_equal_s(absolute_url.ptr, config_url);
+
+ git_str_dispose(&absolute_url);
+ git_config_free(cfg);
+ git_submodule_free(sm);
+}
+
+void test_submodule_init__relative_url_detached_head(void)
+{
+ git_submodule *sm;
+ git_config *cfg;
+ git_str absolute_url = GIT_STR_INIT;
+ const char *config_url;
+ git_reference *head_ref = NULL;
+ git_object *head_commit = NULL;
+
+ g_repo = setup_fixture_submodule_simple();
+
+ /* Put the parent repository into a detached head state. */
+ cl_git_pass(git_repository_head(&head_ref, g_repo));
+ cl_git_pass(git_reference_peel(&head_commit, head_ref, GIT_OBJECT_COMMIT));
+
+ cl_git_pass(git_repository_set_head_detached(g_repo, git_commit_id((git_commit *)head_commit)));
+
+ cl_assert(git_fs_path_dirname_r(&absolute_url, git_repository_workdir(g_repo)) > 0);
+ cl_git_pass(git_str_joinpath(&absolute_url, absolute_url.ptr, "testrepo.git"));
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
+
+ /* verify that the .gitmodules is set with an absolute path*/
+ cl_assert_equal_s("../testrepo.git", git_submodule_url(sm));
+
+ /* init and verify that absolute path is written to .git/config */
+ cl_git_pass(git_submodule_init(sm, false));
+
+ cl_git_pass(git_repository_config_snapshot(&cfg, g_repo));
+
+ cl_git_pass(git_config_get_string(&config_url, cfg, "submodule.testrepo.url"));
+ cl_assert_equal_s(absolute_url.ptr, config_url);
+
+ git_str_dispose(&absolute_url);
+ git_config_free(cfg);
+ git_object_free(head_commit);
+ git_reference_free(head_ref);
+ git_submodule_free(sm);
+}
diff --git a/tests/libgit2/submodule/inject_option.c b/tests/libgit2/submodule/inject_option.c
new file mode 100644
index 0000000..e28ff84
--- /dev/null
+++ b/tests/libgit2/submodule/inject_option.c
@@ -0,0 +1,80 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "path.h"
+#include "submodule_helpers.h"
+#include "futils.h"
+#include "repository.h"
+
+static git_repository *g_repo = NULL;
+
+void test_submodule_inject_option__initialize(void)
+{
+ g_repo = setup_fixture_submodule_simple();
+}
+
+void test_submodule_inject_option__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static int find_naughty(git_submodule *sm, const char *name, void *payload)
+{
+ int *foundit = (int *) payload;
+
+ GIT_UNUSED(sm);
+
+ if (!git__strcmp("naughty", name))
+ *foundit = true;
+
+ return 0;
+}
+
+void test_submodule_inject_option__url(void)
+{
+ int foundit;
+ git_submodule *sm;
+ git_str buf = GIT_STR_INIT;
+
+ cl_git_pass(git_str_joinpath(&buf, git_repository_workdir(g_repo), ".gitmodules"));
+ cl_git_rewritefile(buf.ptr,
+ "[submodule \"naughty\"]\n"
+ " path = testrepo\n"
+ " url = -u./payload\n");
+ git_str_dispose(&buf);
+
+ /* We do want to find it, but with the appropriate field empty */
+ foundit = 0;
+ cl_git_pass(git_submodule_foreach(g_repo, find_naughty, &foundit));
+ cl_assert_equal_i(1, foundit);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "naughty"));
+ cl_assert_equal_s("testrepo", git_submodule_path(sm));
+ cl_assert_equal_p(NULL, git_submodule_url(sm));
+
+ git_submodule_free(sm);
+}
+
+void test_submodule_inject_option__path(void)
+{
+ int foundit;
+ git_submodule *sm;
+ git_str buf = GIT_STR_INIT;
+
+ cl_git_pass(git_str_joinpath(&buf, git_repository_workdir(g_repo), ".gitmodules"));
+ cl_git_rewritefile(buf.ptr,
+ "[submodule \"naughty\"]\n"
+ " path = --something\n"
+ " url = blah.git\n");
+ git_str_dispose(&buf);
+
+ /* We do want to find it, but with the appropriate field empty */
+ foundit = 0;
+ cl_git_pass(git_submodule_foreach(g_repo, find_naughty, &foundit));
+ cl_assert_equal_i(1, foundit);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "naughty"));
+ cl_assert_equal_s("naughty", git_submodule_path(sm));
+ cl_assert_equal_s("blah.git", git_submodule_url(sm));
+
+ git_submodule_free(sm);
+}
diff --git a/tests/libgit2/submodule/lookup.c b/tests/libgit2/submodule/lookup.c
new file mode 100644
index 0000000..febb7df
--- /dev/null
+++ b/tests/libgit2/submodule/lookup.c
@@ -0,0 +1,517 @@
+#include "clar_libgit2.h"
+#include "submodule_helpers.h"
+#include "git2/sys/repository.h"
+#include "repository.h"
+#include "futils.h"
+#include "index.h"
+
+static git_repository *g_repo = NULL;
+
+void test_submodule_lookup__initialize(void)
+{
+ g_repo = setup_fixture_submod2();
+}
+
+void test_submodule_lookup__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_submodule_lookup__simple_lookup(void)
+{
+ assert_submodule_exists(g_repo, "sm_unchanged");
+
+ /* lookup pending change in .gitmodules that is not in HEAD */
+ assert_submodule_exists(g_repo, "sm_added_and_uncommited");
+
+ /* lookup pending change in .gitmodules that is not in HEAD nor index */
+ assert_submodule_exists(g_repo, "sm_gitmodules_only");
+
+ /* lookup git repo subdir that is not added as submodule */
+ refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS);
+
+ /* lookup existing directory that is not a submodule */
+ refute_submodule_exists(g_repo, "just_a_dir", GIT_ENOTFOUND);
+
+ /* lookup existing file that is not a submodule */
+ refute_submodule_exists(g_repo, "just_a_file", GIT_ENOTFOUND);
+
+ /* lookup non-existent item */
+ refute_submodule_exists(g_repo, "no_such_file", GIT_ENOTFOUND);
+
+ /* lookup a submodule by path with a trailing slash */
+ assert_submodule_exists(g_repo, "sm_added_and_uncommited/");
+}
+
+void test_submodule_lookup__can_be_dupped(void)
+{
+ git_submodule *sm;
+ git_submodule *sm_duplicate;
+ const char *oid = "480095882d281ed676fe5b863569520e54a7d5c0";
+
+ /* Check original */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+ cl_assert(git_submodule_owner(sm) == g_repo);
+ cl_assert_equal_s("sm_unchanged", git_submodule_name(sm));
+ cl_assert(git__suffixcmp(git_submodule_path(sm), "sm_unchanged") == 0);
+ cl_assert(git__suffixcmp(git_submodule_url(sm), "/submod2_target") == 0);
+
+ cl_assert(git_oid_streq(git_submodule_index_id(sm), oid) == 0);
+ cl_assert(git_oid_streq(git_submodule_head_id(sm), oid) == 0);
+ cl_assert(git_oid_streq(git_submodule_wd_id(sm), oid) == 0);
+
+ cl_assert(git_submodule_ignore(sm) == GIT_SUBMODULE_IGNORE_NONE);
+ cl_assert(git_submodule_update_strategy(sm) == GIT_SUBMODULE_UPDATE_CHECKOUT);
+
+ /* Duplicate and free original */
+ cl_assert(git_submodule_dup(&sm_duplicate, sm) == 0);
+ git_submodule_free(sm);
+
+ /* Check duplicate */
+ cl_assert(git_submodule_owner(sm_duplicate) == g_repo);
+ cl_assert_equal_s("sm_unchanged", git_submodule_name(sm_duplicate));
+ cl_assert(git__suffixcmp(git_submodule_path(sm_duplicate), "sm_unchanged") == 0);
+ cl_assert(git__suffixcmp(git_submodule_url(sm_duplicate), "/submod2_target") == 0);
+
+ cl_assert(git_oid_streq(git_submodule_index_id(sm_duplicate), oid) == 0);
+ cl_assert(git_oid_streq(git_submodule_head_id(sm_duplicate), oid) == 0);
+ cl_assert(git_oid_streq(git_submodule_wd_id(sm_duplicate), oid) == 0);
+
+ cl_assert(git_submodule_ignore(sm_duplicate) == GIT_SUBMODULE_IGNORE_NONE);
+ cl_assert(git_submodule_update_strategy(sm_duplicate) == GIT_SUBMODULE_UPDATE_CHECKOUT);
+
+ git_submodule_free(sm_duplicate);
+}
+
+void test_submodule_lookup__accessors(void)
+{
+ git_submodule *sm;
+ const char *oid = "480095882d281ed676fe5b863569520e54a7d5c0";
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+ cl_assert(git_submodule_owner(sm) == g_repo);
+ cl_assert_equal_s("sm_unchanged", git_submodule_name(sm));
+ cl_assert(git__suffixcmp(git_submodule_path(sm), "sm_unchanged") == 0);
+ cl_assert(git__suffixcmp(git_submodule_url(sm), "/submod2_target") == 0);
+
+ cl_assert(git_oid_streq(git_submodule_index_id(sm), oid) == 0);
+ cl_assert(git_oid_streq(git_submodule_head_id(sm), oid) == 0);
+ cl_assert(git_oid_streq(git_submodule_wd_id(sm), oid) == 0);
+
+ cl_assert(git_submodule_ignore(sm) == GIT_SUBMODULE_IGNORE_NONE);
+ cl_assert(git_submodule_update_strategy(sm) == GIT_SUBMODULE_UPDATE_CHECKOUT);
+
+ git_submodule_free(sm);
+
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
+ cl_assert_equal_s("sm_changed_head", git_submodule_name(sm));
+
+ cl_assert(git_oid_streq(git_submodule_index_id(sm), oid) == 0);
+ cl_assert(git_oid_streq(git_submodule_head_id(sm), oid) == 0);
+ cl_assert(git_oid_streq(git_submodule_wd_id(sm),
+ "3d9386c507f6b093471a3e324085657a3c2b4247") == 0);
+
+ git_submodule_free(sm);
+
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_added_and_uncommited"));
+ cl_assert_equal_s("sm_added_and_uncommited", git_submodule_name(sm));
+
+ cl_assert(git_oid_streq(git_submodule_index_id(sm), oid) == 0);
+ cl_assert(git_submodule_head_id(sm) == NULL);
+ cl_assert(git_oid_streq(git_submodule_wd_id(sm), oid) == 0);
+
+ git_submodule_free(sm);
+
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_missing_commits"));
+ cl_assert_equal_s("sm_missing_commits", git_submodule_name(sm));
+
+ cl_assert(git_oid_streq(git_submodule_index_id(sm), oid) == 0);
+ cl_assert(git_oid_streq(git_submodule_head_id(sm), oid) == 0);
+ cl_assert(git_oid_streq(git_submodule_wd_id(sm),
+ "5e4963595a9774b90524d35a807169049de8ccad") == 0);
+
+ git_submodule_free(sm);
+}
+
+typedef struct {
+ int count;
+} sm_lookup_data;
+
+static int sm_lookup_cb(git_submodule *sm, const char *name, void *payload)
+{
+ sm_lookup_data *data = payload;
+ data->count += 1;
+ cl_assert_equal_s(git_submodule_name(sm), name);
+ return 0;
+}
+
+void test_submodule_lookup__foreach(void)
+{
+ git_config *cfg;
+ sm_lookup_data data;
+
+ memset(&data, 0, sizeof(data));
+ cl_git_pass(git_submodule_foreach(g_repo, sm_lookup_cb, &data));
+ cl_assert_equal_i(8, data.count);
+
+ memset(&data, 0, sizeof(data));
+
+ /* Change the path for a submodule so it doesn't match the name */
+ cl_git_pass(git_config_open_ondisk(&cfg, "submod2/.gitmodules"));
+
+ cl_git_pass(git_config_set_string(cfg, "submodule.smchangedindex.path", "sm_changed_index"));
+ cl_git_pass(git_config_set_string(cfg, "submodule.smchangedindex.url", "../submod2_target"));
+ cl_git_pass(git_config_delete_entry(cfg, "submodule.sm_changed_index.path"));
+ cl_git_pass(git_config_delete_entry(cfg, "submodule.sm_changed_index.url"));
+
+ git_config_free(cfg);
+
+ cl_git_pass(git_submodule_foreach(g_repo, sm_lookup_cb, &data));
+ cl_assert_equal_i(8, data.count);
+}
+
+static int foreach_cb(git_submodule *sm, const char *name, void *payload)
+{
+ GIT_UNUSED(sm);
+ GIT_UNUSED(name);
+ GIT_UNUSED(payload);
+ return 0;
+}
+
+void test_submodule_lookup__duplicated_path(void)
+{
+ cl_git_rewritefile("submod2/.gitmodules",
+ "[submodule \"sm1\"]\n"
+ " path = duplicated-path\n"
+ " url = sm1\n"
+ "[submodule \"sm2\"]\n"
+ " path = duplicated-path\n"
+ " url = sm2\n");
+
+ cl_git_fail(git_submodule_foreach(g_repo, foreach_cb, NULL));
+}
+
+void test_submodule_lookup__lookup_even_with_unborn_head(void)
+{
+ git_reference *head;
+
+ /* put us on an unborn branch */
+ cl_git_pass(git_reference_symbolic_create(
+ &head, g_repo, "HEAD", "refs/heads/garbage", 1, NULL));
+ git_reference_free(head);
+
+ test_submodule_lookup__simple_lookup(); /* baseline should still pass */
+}
+
+void test_submodule_lookup__lookup_even_with_missing_index(void)
+{
+ git_index *idx;
+
+ /* give the repo an empty index */
+ cl_git_pass(git_index__new(&idx, GIT_OID_SHA1));
+ git_repository_set_index(g_repo, idx);
+ git_index_free(idx);
+
+ test_submodule_lookup__simple_lookup(); /* baseline should still pass */
+}
+
+void test_submodule_lookup__backslashes(void)
+{
+ git_config *cfg;
+ git_submodule *sm;
+ git_repository *subrepo;
+ git_buf buf = GIT_BUF_INIT;
+ const char *backslashed_path = "..\\submod2_target";
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "submod2/.gitmodules"));
+ cl_git_pass(git_config_set_string(cfg, "submodule.sm_unchanged.url", backslashed_path));
+ git_config_free(cfg);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+ cl_assert_equal_s(backslashed_path, git_submodule_url(sm));
+ cl_git_pass(git_submodule_open(&subrepo, sm));
+
+ cl_git_pass(git_submodule_resolve_url(&buf, g_repo, backslashed_path));
+
+ git_buf_dispose(&buf);
+ git_submodule_free(sm);
+ git_repository_free(subrepo);
+}
+
+static void baseline_tests(void)
+{
+ /* small baseline that should work even if we change the index or make
+ * commits from the index
+ */
+ assert_submodule_exists(g_repo, "sm_unchanged");
+ assert_submodule_exists(g_repo, "sm_gitmodules_only");
+ refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS);
+}
+
+static void add_submodule_with_commit(const char *name)
+{
+ git_submodule *sm;
+ git_repository *smrepo;
+ git_index *idx;
+ git_str p = GIT_STR_INIT;
+
+ cl_git_pass(git_submodule_add_setup(&sm, g_repo,
+ "https://github.com/libgit2/libgit2.git", name, 1));
+
+ assert_submodule_exists(g_repo, name);
+
+ cl_git_pass(git_submodule_open(&smrepo, sm));
+ cl_git_pass(git_repository_index(&idx, smrepo));
+
+ cl_git_pass(git_str_joinpath(&p, git_repository_workdir(smrepo), "file"));
+ cl_git_mkfile(p.ptr, "new file");
+ git_str_dispose(&p);
+
+ cl_git_pass(git_index_add_bypath(idx, "file"));
+ cl_git_pass(git_index_write(idx));
+ git_index_free(idx);
+
+ cl_repo_commit_from_index(NULL, smrepo, NULL, 0, "initial commit");
+ git_repository_free(smrepo);
+
+ cl_git_pass(git_submodule_add_finalize(sm));
+
+ git_submodule_free(sm);
+}
+
+void test_submodule_lookup__just_added(void)
+{
+ git_submodule *sm;
+ git_str snap1 = GIT_STR_INIT, snap2 = GIT_STR_INIT;
+ git_reference *original_head = NULL;
+
+ refute_submodule_exists(g_repo, "sm_just_added", GIT_ENOTFOUND);
+ refute_submodule_exists(g_repo, "sm_just_added_2", GIT_ENOTFOUND);
+ refute_submodule_exists(g_repo, "sm_just_added_idx", GIT_ENOTFOUND);
+ refute_submodule_exists(g_repo, "sm_just_added_head", GIT_ENOTFOUND);
+ refute_submodule_exists(g_repo, "mismatch_name", GIT_ENOTFOUND);
+ refute_submodule_exists(g_repo, "mismatch_path", GIT_ENOTFOUND);
+ baseline_tests();
+
+ cl_git_pass(git_futils_readbuffer(&snap1, "submod2/.gitmodules"));
+ cl_git_pass(git_repository_head(&original_head, g_repo));
+
+ cl_git_pass(git_submodule_add_setup(&sm, g_repo,
+ "https://github.com/libgit2/libgit2.git", "sm_just_added", 1));
+ git_submodule_free(sm);
+ assert_submodule_exists(g_repo, "sm_just_added");
+
+ cl_git_pass(git_submodule_add_setup(&sm, g_repo,
+ "https://github.com/libgit2/libgit2.git", "sm_just_added_2", 1));
+ assert_submodule_exists(g_repo, "sm_just_added_2");
+ cl_git_fail(git_submodule_add_finalize(sm)); /* fails if no HEAD */
+ git_submodule_free(sm);
+
+ add_submodule_with_commit("sm_just_added_head");
+ cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "commit new sm to head");
+ assert_submodule_exists(g_repo, "sm_just_added_head");
+
+ add_submodule_with_commit("sm_just_added_idx");
+ assert_submodule_exists(g_repo, "sm_just_added_idx");
+
+ cl_git_pass(git_futils_readbuffer(&snap2, "submod2/.gitmodules"));
+
+ cl_git_append2file(
+ "submod2/.gitmodules",
+ "\n[submodule \"mismatch_name\"]\n"
+ "\tpath = mismatch_path\n"
+ "\turl = https://example.com/example.git\n\n");
+
+ assert_submodule_exists(g_repo, "mismatch_name");
+ assert_submodule_exists(g_repo, "mismatch_path");
+ assert_submodule_exists(g_repo, "sm_just_added");
+ assert_submodule_exists(g_repo, "sm_just_added_2");
+ assert_submodule_exists(g_repo, "sm_just_added_idx");
+ assert_submodule_exists(g_repo, "sm_just_added_head");
+ baseline_tests();
+
+ cl_git_rewritefile("submod2/.gitmodules", snap2.ptr);
+ git_str_dispose(&snap2);
+
+ refute_submodule_exists(g_repo, "mismatch_name", GIT_ENOTFOUND);
+ refute_submodule_exists(g_repo, "mismatch_path", GIT_ENOTFOUND);
+ assert_submodule_exists(g_repo, "sm_just_added");
+ assert_submodule_exists(g_repo, "sm_just_added_2");
+ assert_submodule_exists(g_repo, "sm_just_added_idx");
+ assert_submodule_exists(g_repo, "sm_just_added_head");
+ baseline_tests();
+
+ cl_git_rewritefile("submod2/.gitmodules", snap1.ptr);
+ git_str_dispose(&snap1);
+
+ refute_submodule_exists(g_repo, "mismatch_name", GIT_ENOTFOUND);
+ refute_submodule_exists(g_repo, "mismatch_path", GIT_ENOTFOUND);
+ /* note error code change, because add_setup made a repo in the workdir */
+ refute_submodule_exists(g_repo, "sm_just_added", GIT_EEXISTS);
+ refute_submodule_exists(g_repo, "sm_just_added_2", GIT_EEXISTS);
+ /* these still exist in index and head respectively */
+ assert_submodule_exists(g_repo, "sm_just_added_idx");
+ assert_submodule_exists(g_repo, "sm_just_added_head");
+ baseline_tests();
+
+ {
+ git_index *idx;
+ cl_git_pass(git_repository_index(&idx, g_repo));
+ cl_git_pass(git_index_remove_bypath(idx, "sm_just_added_idx"));
+ cl_git_pass(git_index_remove_bypath(idx, "sm_just_added_head"));
+ cl_git_pass(git_index_write(idx));
+ git_index_free(idx);
+ }
+
+ refute_submodule_exists(g_repo, "sm_just_added_idx", GIT_EEXISTS);
+ assert_submodule_exists(g_repo, "sm_just_added_head");
+
+ {
+ cl_git_pass(git_reference_create(NULL, g_repo, "refs/heads/master", git_reference_target(original_head), 1, "move head back"));
+ git_reference_free(original_head);
+ }
+
+ refute_submodule_exists(g_repo, "sm_just_added_head", GIT_EEXISTS);
+}
+
+/* Test_App and Test_App2 are fairly similar names, make sure we load the right one */
+void test_submodule_lookup__prefix_name(void)
+{
+ git_submodule *sm;
+
+ cl_git_rewritefile("submod2/.gitmodules",
+ "[submodule \"Test_App\"]\n"
+ " path = Test_App\n"
+ " url = ../Test_App\n"
+ "[submodule \"Test_App2\"]\n"
+ " path = Test_App2\n"
+ " url = ../Test_App\n");
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "Test_App"));
+ cl_assert_equal_s("Test_App", git_submodule_name(sm));
+
+ git_submodule_free(sm);
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "Test_App2"));
+ cl_assert_equal_s("Test_App2", git_submodule_name(sm));
+
+ git_submodule_free(sm);
+}
+
+void test_submodule_lookup__renamed(void)
+{
+ const char *newpath = "sm_actually_changed";
+ git_index *idx;
+ sm_lookup_data data;
+
+ cl_git_pass(git_repository_index__weakptr(&idx, g_repo));
+
+ /* We're replicating 'git mv sm_unchanged sm_actually_changed' in this test */
+
+ cl_git_pass(p_rename("submod2/sm_unchanged", "submod2/sm_actually_changed"));
+
+ /* Change the path in .gitmodules and stage it*/
+ {
+ git_config *cfg;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "submod2/.gitmodules"));
+ cl_git_pass(git_config_set_string(cfg, "submodule.sm_unchanged.path", newpath));
+ git_config_free(cfg);
+
+ cl_git_pass(git_index_add_bypath(idx, ".gitmodules"));
+ }
+
+ /* Change the worktree info in the submodule's config */
+ {
+ git_config *cfg;
+
+ cl_git_pass(git_config_open_ondisk(&cfg, "submod2/.git/modules/sm_unchanged/config"));
+ cl_git_pass(git_config_set_string(cfg, "core.worktree", "../../../sm_actually_changed"));
+ git_config_free(cfg);
+ }
+
+ /* Rename the entry in the index */
+ {
+ const git_index_entry *e;
+ git_index_entry entry = {{ 0 }};
+
+ e = git_index_get_bypath(idx, "sm_unchanged", 0);
+ cl_assert(e);
+ cl_assert_equal_i(GIT_FILEMODE_COMMIT, e->mode);
+
+ entry.path = newpath;
+ entry.mode = GIT_FILEMODE_COMMIT;
+ git_oid_cpy(&entry.id, &e->id);
+
+ cl_git_pass(git_index_remove(idx, "sm_unchanged", 0));
+ cl_git_pass(git_index_add(idx, &entry));
+ cl_git_pass(git_index_write(idx));
+ }
+
+ memset(&data, 0, sizeof(data));
+ cl_git_pass(git_submodule_foreach(g_repo, sm_lookup_cb, &data));
+ cl_assert_equal_i(8, data.count);
+}
+
+void test_submodule_lookup__cached(void)
+{
+ git_submodule *sm;
+ git_submodule *sm2;
+ /* See that the simple tests still pass. */
+
+ git_repository_submodule_cache_all(g_repo);
+ test_submodule_lookup__simple_lookup();
+ git_repository_submodule_cache_clear(g_repo);
+ test_submodule_lookup__simple_lookup();
+
+ /* Check that subsequent calls return different objects when cached. */
+ git_repository_submodule_cache_all(g_repo);
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_unchanged"));
+ cl_git_pass(git_submodule_lookup(&sm2, g_repo, "sm_unchanged"));
+ cl_assert_equal_p(sm, sm2);
+ git_submodule_free(sm2);
+
+ /* and that we get new objects again after clearing the cache. */
+ git_repository_submodule_cache_clear(g_repo);
+ cl_git_pass(git_submodule_lookup(&sm2, g_repo, "sm_unchanged"));
+ cl_assert(sm != sm2);
+ git_submodule_free(sm);
+ git_submodule_free(sm2);
+}
+
+void test_submodule_lookup__lookup_in_bare_repository_fails(void)
+{
+ git_submodule *sm;
+
+ cl_git_sandbox_cleanup();
+ g_repo = cl_git_sandbox_init("submodules.git");
+
+ cl_git_fail(git_submodule_lookup(&sm, g_repo, "nonexisting"));
+}
+
+void test_submodule_lookup__foreach_in_bare_repository_fails(void)
+{
+ cl_git_sandbox_cleanup();
+ g_repo = cl_git_sandbox_init("submodules.git");
+
+ cl_git_fail(git_submodule_foreach(g_repo, foreach_cb, NULL));
+}
+
+void test_submodule_lookup__fail_invalid_gitmodules(void)
+{
+ git_submodule *sm;
+ sm_lookup_data data;
+ memset(&data, 0, sizeof(data));
+
+ cl_git_rewritefile("submod2/.gitmodules",
+ "[submodule \"Test_App\"\n"
+ " path = Test_App\n"
+ " url = ../Test_App\n");
+
+ cl_git_fail(git_submodule_lookup(&sm, g_repo, "Test_App"));
+
+ cl_git_fail(git_submodule_foreach(g_repo, sm_lookup_cb, &data));
+}
diff --git a/tests/libgit2/submodule/modify.c b/tests/libgit2/submodule/modify.c
new file mode 100644
index 0000000..7e7f0ca
--- /dev/null
+++ b/tests/libgit2/submodule/modify.c
@@ -0,0 +1,233 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "path.h"
+#include "submodule_helpers.h"
+#include "config/config_helpers.h"
+
+static git_repository *g_repo = NULL;
+
+#define SM_LIBGIT2_URL "https://github.com/libgit2/libgit2.git"
+#define SM_LIBGIT2_BRANCH "github-branch"
+#define SM_LIBGIT2 "sm_libgit2"
+
+void test_submodule_modify__initialize(void)
+{
+ g_repo = setup_fixture_submod2();
+}
+
+static int delete_one_config(const git_config_entry *entry, void *payload)
+{
+ git_config *cfg = payload;
+ return git_config_delete_entry(cfg, entry->name);
+}
+
+static int init_one_submodule(
+ git_submodule *sm, const char *name, void *payload)
+{
+ GIT_UNUSED(name);
+ GIT_UNUSED(payload);
+ return git_submodule_init(sm, false);
+}
+
+void test_submodule_modify__init(void)
+{
+ git_config *cfg;
+ const char *str;
+
+ /* erase submodule data from .git/config */
+ cl_git_pass(git_repository_config(&cfg, g_repo));
+ cl_git_pass(
+ git_config_foreach_match(cfg, "submodule\\..*", delete_one_config, cfg));
+ git_config_free(cfg);
+
+ /* confirm no submodule data in config */
+ cl_git_pass(git_repository_config_snapshot(&cfg, g_repo));
+ cl_git_fail_with(GIT_ENOTFOUND, git_config_get_string(&str, cfg, "submodule.sm_unchanged.url"));
+ cl_git_fail_with(GIT_ENOTFOUND, git_config_get_string(&str, cfg, "submodule.sm_changed_head.url"));
+ cl_git_fail_with(GIT_ENOTFOUND, git_config_get_string(&str, cfg, "submodule.sm_added_and_uncommited.url"));
+ git_config_free(cfg);
+
+ /* call init and see that settings are copied */
+ cl_git_pass(git_submodule_foreach(g_repo, init_one_submodule, NULL));
+
+ /* confirm submodule data in config */
+ cl_git_pass(git_repository_config_snapshot(&cfg, g_repo));
+ cl_git_pass(git_config_get_string(&str, cfg, "submodule.sm_unchanged.url"));
+ cl_assert(git__suffixcmp(str, "/submod2_target") == 0);
+ cl_git_pass(git_config_get_string(&str, cfg, "submodule.sm_changed_head.url"));
+ cl_assert(git__suffixcmp(str, "/submod2_target") == 0);
+ cl_git_pass(git_config_get_string(&str, cfg, "submodule.sm_added_and_uncommited.url"));
+ cl_assert(git__suffixcmp(str, "/submod2_target") == 0);
+ git_config_free(cfg);
+}
+
+static int sync_one_submodule(
+ git_submodule *sm, const char *name, void *payload)
+{
+ GIT_UNUSED(name);
+ GIT_UNUSED(payload);
+ return git_submodule_sync(sm);
+}
+
+static void assert_submodule_url_is_synced(
+ git_submodule *sm, const char *parent_key, const char *child_key)
+{
+ git_repository *smrepo;
+
+ assert_config_entry_value(g_repo, parent_key, git_submodule_url(sm));
+
+ cl_git_pass(git_submodule_open(&smrepo, sm));
+ assert_config_entry_value(smrepo, child_key, git_submodule_url(sm));
+ git_repository_free(smrepo);
+}
+
+void test_submodule_modify__sync(void)
+{
+ git_submodule *sm1, *sm2, *sm3;
+ git_config *cfg;
+ const char *str;
+
+#define SM1 "sm_unchanged"
+#define SM2 "sm_changed_head"
+#define SM3 "sm_added_and_uncommited"
+
+ /* look up some submodules */
+ cl_git_pass(git_submodule_lookup(&sm1, g_repo, SM1));
+ cl_git_pass(git_submodule_lookup(&sm2, g_repo, SM2));
+ cl_git_pass(git_submodule_lookup(&sm3, g_repo, SM3));
+
+ /* At this point, the .git/config URLs for the submodules have
+ * not be rewritten with the absolute paths (although the
+ * .gitmodules have. Let's confirm that they DO NOT match
+ * yet, then we can do a sync to make them match...
+ */
+
+ /* check submodule info does not match before sync */
+ cl_git_pass(git_repository_config_snapshot(&cfg, g_repo));
+ cl_git_pass(git_config_get_string(&str, cfg, "submodule."SM1".url"));
+ cl_assert(strcmp(git_submodule_url(sm1), str) != 0);
+ cl_git_pass(git_config_get_string(&str, cfg, "submodule."SM2".url"));
+ cl_assert(strcmp(git_submodule_url(sm2), str) != 0);
+ cl_git_pass(git_config_get_string(&str, cfg, "submodule."SM3".url"));
+ cl_assert(strcmp(git_submodule_url(sm3), str) != 0);
+ git_config_free(cfg);
+
+ /* sync all the submodules */
+ cl_git_pass(git_submodule_foreach(g_repo, sync_one_submodule, NULL));
+
+ /* check that submodule config is updated */
+ assert_submodule_url_is_synced(
+ sm1, "submodule."SM1".url", "remote.origin.url");
+ assert_submodule_url_is_synced(
+ sm2, "submodule."SM2".url", "remote.origin.url");
+ assert_submodule_url_is_synced(
+ sm3, "submodule."SM3".url", "remote.origin.url");
+
+ git_submodule_free(sm1);
+ git_submodule_free(sm2);
+ git_submodule_free(sm3);
+}
+
+static void assert_ignore_change(git_submodule_ignore_t ignore)
+{
+ git_submodule *sm;
+
+ cl_git_pass(git_submodule_set_ignore(g_repo, "sm_changed_head", ignore));
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
+ cl_assert_equal_i(ignore, git_submodule_ignore(sm));
+ git_submodule_free(sm);
+}
+
+void test_submodule_modify__set_ignore(void)
+{
+ assert_ignore_change(GIT_SUBMODULE_IGNORE_UNTRACKED);
+ assert_ignore_change(GIT_SUBMODULE_IGNORE_NONE);
+ assert_ignore_change(GIT_SUBMODULE_IGNORE_ALL);
+}
+
+static void assert_update_change(git_submodule_update_t update)
+{
+ git_submodule *sm;
+
+ cl_git_pass(git_submodule_set_update(g_repo, "sm_changed_head", update));
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
+ cl_assert_equal_i(update, git_submodule_update_strategy(sm));
+ git_submodule_free(sm);
+}
+
+void test_submodule_modify__set_update(void)
+{
+ assert_update_change(GIT_SUBMODULE_UPDATE_REBASE);
+ assert_update_change(GIT_SUBMODULE_UPDATE_NONE);
+ assert_update_change(GIT_SUBMODULE_UPDATE_CHECKOUT);
+}
+
+static void assert_recurse_change(git_submodule_recurse_t recurse)
+{
+ git_submodule *sm;
+
+ cl_git_pass(git_submodule_set_fetch_recurse_submodules(g_repo, "sm_changed_head", recurse));
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
+ cl_assert_equal_i(recurse, git_submodule_fetch_recurse_submodules(sm));
+ git_submodule_free(sm);
+}
+
+void test_submodule_modify__set_fetch_recurse_submodules(void)
+{
+ assert_recurse_change(GIT_SUBMODULE_RECURSE_YES);
+ assert_recurse_change(GIT_SUBMODULE_RECURSE_NO);
+ assert_recurse_change(GIT_SUBMODULE_RECURSE_ONDEMAND);
+}
+
+void test_submodule_modify__set_branch(void)
+{
+ git_submodule *sm;
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
+ cl_assert(git_submodule_branch(sm) == NULL);
+ git_submodule_free(sm);
+
+ cl_git_pass(git_submodule_set_branch(g_repo, "sm_changed_head", SM_LIBGIT2_BRANCH));
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
+ cl_assert_equal_s(SM_LIBGIT2_BRANCH, git_submodule_branch(sm));
+ git_submodule_free(sm);
+
+ cl_git_pass(git_submodule_set_branch(g_repo, "sm_changed_head", NULL));
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
+ cl_assert(git_submodule_branch(sm) == NULL);
+ git_submodule_free(sm);
+}
+
+void test_submodule_modify__set_url(void)
+{
+ git_submodule *sm;
+
+ cl_git_pass(git_submodule_set_url(g_repo, "sm_changed_head", SM_LIBGIT2_URL));
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_changed_head"));
+ cl_assert_equal_s(SM_LIBGIT2_URL, git_submodule_url(sm));
+ git_submodule_free(sm);
+}
+
+void test_submodule_modify__set_relative_url(void)
+{
+ git_str path = GIT_STR_INIT;
+ git_repository *repo;
+ git_submodule *sm;
+
+ cl_git_pass(git_submodule_set_url(g_repo, SM1, "../relative-url"));
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, SM1));
+ cl_git_pass(git_submodule_sync(sm));
+ cl_git_pass(git_submodule_open(&repo, sm));
+
+ cl_git_pass(git_str_joinpath(&path, clar_sandbox_path(), "relative-url"));
+
+ assert_config_entry_value(g_repo, "submodule."SM1".url", path.ptr);
+ assert_config_entry_value(repo, "remote.origin.url", path.ptr);
+
+ git_repository_free(repo);
+ git_submodule_free(sm);
+ git_str_dispose(&path);
+}
diff --git a/tests/libgit2/submodule/nosubs.c b/tests/libgit2/submodule/nosubs.c
new file mode 100644
index 0000000..e82230e
--- /dev/null
+++ b/tests/libgit2/submodule/nosubs.c
@@ -0,0 +1,130 @@
+/* test the submodule APIs on repositories where there are no submodules */
+
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "futils.h"
+
+void test_submodule_nosubs__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_submodule_nosubs__lookup(void)
+{
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_submodule *sm = NULL;
+
+ p_mkdir("status/subrepo", 0777);
+ cl_git_mkfile("status/subrepo/.git", "gitdir: ../.git");
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, repo, "subdir"));
+
+ cl_assert_equal_i(GIT_EEXISTS, git_submodule_lookup(&sm, repo, "subrepo"));
+
+ cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(&sm, repo, "subdir"));
+
+ cl_assert_equal_i(GIT_EEXISTS, git_submodule_lookup(&sm, repo, "subrepo"));
+}
+
+static int fake_submod_cb(git_submodule *sm, const char *n, void *p)
+{
+ GIT_UNUSED(sm); GIT_UNUSED(n); GIT_UNUSED(p);
+ return 0;
+}
+
+void test_submodule_nosubs__foreach(void)
+{
+ git_repository *repo = cl_git_sandbox_init("status");
+ cl_git_pass(git_submodule_foreach(repo, fake_submod_cb, NULL));
+}
+
+void test_submodule_nosubs__add(void)
+{
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_submodule *sm, *sm2;
+
+ cl_git_pass(git_submodule_add_setup(&sm, repo, "https://github.com/libgit2/libgit2.git", "submodules/libgit2", 1));
+
+ cl_git_pass(git_submodule_lookup(&sm2, repo, "submodules/libgit2"));
+ git_submodule_free(sm2);
+
+ cl_git_pass(git_submodule_foreach(repo, fake_submod_cb, NULL));
+
+ git_submodule_free(sm);
+}
+
+void test_submodule_nosubs__bad_gitmodules(void)
+{
+ git_repository *repo = cl_git_sandbox_init("status");
+
+ cl_git_mkfile("status/.gitmodules", "[submodule \"foobar\"]\tpath=blargle\n\turl=\n\tbranch=\n\tupdate=flooble\n\n");
+
+ cl_git_rewritefile("status/.gitmodules", "[submodule \"foobar\"]\tpath=blargle\n\turl=\n\tbranch=\n\tupdate=rebase\n\n");
+
+ cl_git_pass(git_submodule_lookup(NULL, repo, "foobar"));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_submodule_lookup(NULL, repo, "subdir"));
+}
+
+void test_submodule_nosubs__add_and_delete(void)
+{
+ git_repository *repo = cl_git_sandbox_init("status");
+ git_submodule *sm;
+ git_str buf = GIT_STR_INIT;
+
+ cl_git_fail(git_submodule_lookup(NULL, repo, "libgit2"));
+ cl_git_fail(git_submodule_lookup(NULL, repo, "submodules/libgit2"));
+
+ /* create */
+
+ cl_git_pass(git_submodule_add_setup(
+ &sm, repo, "https://github.com/libgit2/libgit2.git", "submodules/libgit2", 1));
+ cl_assert_equal_s("submodules/libgit2", git_submodule_name(sm));
+ cl_assert_equal_s("submodules/libgit2", git_submodule_path(sm));
+ git_submodule_free(sm);
+
+ cl_git_pass(git_futils_readbuffer(&buf, "status/.gitmodules"));
+ cl_assert(strstr(buf.ptr, "[submodule \"submodules/libgit2\"]") != NULL);
+ cl_assert(strstr(buf.ptr, "path = submodules/libgit2") != NULL);
+ git_str_dispose(&buf);
+
+ /* lookup */
+
+ cl_git_fail(git_submodule_lookup(&sm, repo, "libgit2"));
+ cl_git_pass(git_submodule_lookup(&sm, repo, "submodules/libgit2"));
+ cl_assert_equal_s("submodules/libgit2", git_submodule_name(sm));
+ cl_assert_equal_s("submodules/libgit2", git_submodule_path(sm));
+ git_submodule_free(sm);
+
+ /* update name */
+
+ cl_git_rewritefile(
+ "status/.gitmodules",
+ "[submodule \"libgit2\"]\n"
+ " path = submodules/libgit2\n"
+ " url = https://github.com/libgit2/libgit2.git\n");
+
+ cl_git_pass(git_submodule_lookup(&sm, repo, "libgit2"));
+ cl_assert_equal_s("libgit2", git_submodule_name(sm));
+ cl_assert_equal_s("submodules/libgit2", git_submodule_path(sm));
+ git_submodule_free(sm);
+ cl_git_pass(git_submodule_lookup(&sm, repo, "submodules/libgit2"));
+ git_submodule_free(sm);
+
+ /* revert name update */
+
+ cl_git_rewritefile(
+ "status/.gitmodules",
+ "[submodule \"submodules/libgit2\"]\n"
+ " path = submodules/libgit2\n"
+ " url = https://github.com/libgit2/libgit2.git\n");
+
+ cl_git_fail(git_submodule_lookup(&sm, repo, "libgit2"));
+ cl_git_pass(git_submodule_lookup(&sm, repo, "submodules/libgit2"));
+ git_submodule_free(sm);
+
+ /* remove completely */
+
+ cl_must_pass(p_unlink("status/.gitmodules"));
+ cl_git_fail(git_submodule_lookup(&sm, repo, "libgit2"));
+ cl_git_fail(git_submodule_lookup(&sm, repo, "submodules/libgit2"));
+}
diff --git a/tests/libgit2/submodule/open.c b/tests/libgit2/submodule/open.c
new file mode 100644
index 0000000..e6883d2
--- /dev/null
+++ b/tests/libgit2/submodule/open.c
@@ -0,0 +1,90 @@
+#include "clar_libgit2.h"
+#include "submodule_helpers.h"
+#include "path.h"
+
+static git_repository *g_parent;
+static git_repository *g_child;
+static git_submodule *g_module;
+
+void test_submodule_open__initialize(void)
+{
+ g_parent = setup_fixture_submod2();
+}
+
+void test_submodule_open__cleanup(void)
+{
+ git_submodule_free(g_module);
+ git_repository_free(g_child);
+ cl_git_sandbox_cleanup();
+ g_parent = NULL;
+ g_child = NULL;
+ g_module = NULL;
+}
+
+static void assert_sm_valid(git_repository *parent, git_repository *child, const char *sm_name)
+{
+ git_str expected = GIT_STR_INIT, actual = GIT_STR_INIT;
+
+ /* assert working directory */
+ cl_git_pass(git_str_joinpath(&expected, git_repository_workdir(parent), sm_name));
+ cl_git_pass(git_fs_path_prettify_dir(&expected, expected.ptr, NULL));
+ cl_git_pass(git_str_sets(&actual, git_repository_workdir(child)));
+ cl_git_pass(git_fs_path_prettify_dir(&actual, actual.ptr, NULL));
+ cl_assert_equal_s(expected.ptr, actual.ptr);
+
+ git_str_clear(&expected);
+ git_str_clear(&actual);
+
+ /* assert common directory */
+ cl_git_pass(git_str_joinpath(&expected, git_repository_commondir(parent), "modules"));
+ cl_git_pass(git_str_joinpath(&expected, expected.ptr, sm_name));
+ cl_git_pass(git_fs_path_prettify_dir(&expected, expected.ptr, NULL));
+ cl_git_pass(git_str_sets(&actual, git_repository_commondir(child)));
+ cl_git_pass(git_fs_path_prettify_dir(&actual, actual.ptr, NULL));
+ cl_assert_equal_s(expected.ptr, actual.ptr);
+
+ /* assert git directory */
+ cl_git_pass(git_str_sets(&actual, git_repository_path(child)));
+ cl_git_pass(git_fs_path_prettify_dir(&actual, actual.ptr, NULL));
+ cl_assert_equal_s(expected.ptr, actual.ptr);
+
+ git_str_dispose(&expected);
+ git_str_dispose(&actual);
+}
+
+void test_submodule_open__opening_via_lookup_succeeds(void)
+{
+ cl_git_pass(git_submodule_lookup(&g_module, g_parent, "sm_unchanged"));
+ cl_git_pass(git_submodule_open(&g_child, g_module));
+ assert_sm_valid(g_parent, g_child, "sm_unchanged");
+}
+
+void test_submodule_open__direct_open_succeeds(void)
+{
+ git_str path = GIT_STR_INIT;
+
+ cl_git_pass(git_str_joinpath(&path, git_repository_workdir(g_parent), "sm_unchanged"));
+ cl_git_pass(git_repository_open(&g_child, path.ptr));
+ assert_sm_valid(g_parent, g_child, "sm_unchanged");
+
+ git_str_dispose(&path);
+}
+
+void test_submodule_open__direct_open_succeeds_for_broken_sm_with_gitdir(void)
+{
+ git_str path = GIT_STR_INIT;
+
+ /*
+ * This is actually not a valid submodule, but we
+ * encountered at least one occasion where the gitdir
+ * file existed inside of a submodule's gitdir. As we are
+ * now able to open these submodules correctly, we still
+ * add a test for this.
+ */
+ cl_git_mkfile("submod2/.git/modules/sm_unchanged/gitdir", ".git");
+ cl_git_pass(git_str_joinpath(&path, git_repository_workdir(g_parent), "sm_unchanged"));
+ cl_git_pass(git_repository_open(&g_child, path.ptr));
+ assert_sm_valid(g_parent, g_child, "sm_unchanged");
+
+ git_str_dispose(&path);
+}
diff --git a/tests/libgit2/submodule/repository_init.c b/tests/libgit2/submodule/repository_init.c
new file mode 100644
index 0000000..39b55c4
--- /dev/null
+++ b/tests/libgit2/submodule/repository_init.c
@@ -0,0 +1,38 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "path.h"
+#include "submodule_helpers.h"
+#include "config/config_helpers.h"
+#include "futils.h"
+
+static git_repository *g_repo = NULL;
+
+void test_submodule_repository_init__basic(void)
+{
+ git_submodule *sm;
+ git_repository *repo;
+ git_str dot_git_content = GIT_STR_INIT;
+
+ g_repo = setup_fixture_submod2();
+
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_gitmodules_only"));
+ cl_git_pass(git_submodule_init(sm, 0));
+ cl_git_pass(git_submodule_repo_init(&repo, sm, 1));
+
+ /* Verify worktree */
+ assert_config_entry_value(repo, "core.worktree", "../../../sm_gitmodules_only/");
+
+ /* Verify gitlink */
+ cl_git_pass(git_futils_readbuffer(&dot_git_content, "submod2/" "sm_gitmodules_only" "/.git"));
+ cl_assert_equal_s("gitdir: ../.git/modules/sm_gitmodules_only/", dot_git_content.ptr);
+
+ cl_assert(git_fs_path_isfile("submod2/" "sm_gitmodules_only" "/.git"));
+
+ cl_assert(git_fs_path_isdir("submod2/.git/modules"));
+ cl_assert(git_fs_path_isdir("submod2/.git/modules/" "sm_gitmodules_only"));
+ cl_assert(git_fs_path_isfile("submod2/.git/modules/" "sm_gitmodules_only" "/HEAD"));
+
+ git_submodule_free(sm);
+ git_repository_free(repo);
+ git_str_dispose(&dot_git_content);
+}
diff --git a/tests/libgit2/submodule/status.c b/tests/libgit2/submodule/status.c
new file mode 100644
index 0000000..1d41337
--- /dev/null
+++ b/tests/libgit2/submodule/status.c
@@ -0,0 +1,354 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "path.h"
+#include "submodule_helpers.h"
+#include "futils.h"
+#include "iterator.h"
+
+static git_repository *g_repo = NULL;
+
+void test_submodule_status__initialize(void)
+{
+ g_repo = setup_fixture_submod2();
+}
+
+void test_submodule_status__cleanup(void)
+{
+}
+
+void test_submodule_status__unchanged(void)
+{
+ unsigned int status = get_submodule_status(g_repo, "sm_unchanged");
+ unsigned int expected =
+ GIT_SUBMODULE_STATUS_IN_HEAD |
+ GIT_SUBMODULE_STATUS_IN_INDEX |
+ GIT_SUBMODULE_STATUS_IN_CONFIG |
+ GIT_SUBMODULE_STATUS_IN_WD;
+
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+ cl_assert(expected == status);
+}
+
+static void rm_submodule(const char *name)
+{
+ git_str path = GIT_STR_INIT;
+ cl_git_pass(git_str_joinpath(&path, git_repository_workdir(g_repo), name));
+ cl_git_pass(git_futils_rmdir_r(path.ptr, NULL, GIT_RMDIR_REMOVE_FILES));
+ git_str_dispose(&path);
+}
+
+static void add_submodule_to_index(const char *name)
+{
+ git_submodule *sm;
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, name));
+ cl_git_pass(git_submodule_add_to_index(sm, true));
+ git_submodule_free(sm);
+}
+
+static void rm_submodule_from_index(const char *name)
+{
+ git_index *index;
+ size_t pos;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_assert(!git_index_find(&pos, index, name));
+ cl_git_pass(git_index_remove(index, name, 0));
+ cl_git_pass(git_index_write(index));
+ git_index_free(index);
+}
+
+/* 4 values of GIT_SUBMODULE_IGNORE to check */
+
+void test_submodule_status__ignore_none(void)
+{
+ unsigned int status;
+
+ rm_submodule("sm_unchanged");
+
+ refute_submodule_exists(g_repo, "just_a_dir", GIT_ENOTFOUND);
+ refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS);
+ refute_submodule_exists(g_repo, "not", GIT_EEXISTS);
+
+ status = get_submodule_status(g_repo, "sm_changed_index");
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED) != 0);
+
+ status = get_submodule_status(g_repo, "sm_changed_head");
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
+
+ status = get_submodule_status(g_repo, "sm_changed_file");
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED) != 0);
+
+ status = get_submodule_status(g_repo, "sm_changed_untracked_file");
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNTRACKED) != 0);
+
+ status = get_submodule_status(g_repo, "sm_missing_commits");
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
+
+ status = get_submodule_status(g_repo, "sm_added_and_uncommited");
+ cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_ADDED) != 0);
+
+ /* removed sm_unchanged for deleted workdir */
+ status = get_submodule_status(g_repo, "sm_unchanged");
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_DELETED) != 0);
+
+ /* now mkdir sm_unchanged to test uninitialized */
+ cl_git_pass(git_futils_mkdir_relative("sm_unchanged", "submod2", 0755, 0, NULL));
+ status = get_submodule_status(g_repo, "sm_unchanged");
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) != 0);
+
+ /* update sm_changed_head in index */
+ add_submodule_to_index("sm_changed_head");
+ status = get_submodule_status(g_repo, "sm_changed_head");
+ cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_MODIFIED) != 0);
+
+ /* remove sm_changed_head from index */
+ rm_submodule_from_index("sm_changed_head");
+ status = get_submodule_status(g_repo, "sm_changed_head");
+ cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_DELETED) != 0);
+}
+
+void test_submodule_status__ignore_untracked(void)
+{
+ unsigned int status;
+ git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_UNTRACKED;
+
+ rm_submodule("sm_unchanged");
+
+ refute_submodule_exists(g_repo, "just_a_dir", GIT_ENOTFOUND);
+ refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS);
+ refute_submodule_exists(g_repo, "not", GIT_EEXISTS);
+
+ cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_index", ign));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_INDEX_MODIFIED) != 0);
+
+ cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_head", ign));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
+
+ cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_file", ign));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_WD_MODIFIED) != 0);
+
+ cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_untracked_file", ign));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ cl_git_pass(git_submodule_status(&status, g_repo,"sm_missing_commits", ign));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
+
+ cl_git_pass(git_submodule_status(&status, g_repo,"sm_added_and_uncommited", ign));
+ cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_ADDED) != 0);
+
+ /* removed sm_unchanged for deleted workdir */
+ cl_git_pass(git_submodule_status(&status, g_repo,"sm_unchanged", ign));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_DELETED) != 0);
+
+ /* now mkdir sm_unchanged to test uninitialized */
+ cl_git_pass(git_futils_mkdir_relative("sm_unchanged", "submod2", 0755, 0, NULL));
+ cl_git_pass(git_submodule_status(&status, g_repo,"sm_unchanged", ign));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) != 0);
+
+ /* update sm_changed_head in index */
+ add_submodule_to_index("sm_changed_head");
+ cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_head", ign));
+ cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_MODIFIED) != 0);
+}
+
+void test_submodule_status__ignore_dirty(void)
+{
+ unsigned int status;
+ git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_DIRTY;
+
+ rm_submodule("sm_unchanged");
+
+ refute_submodule_exists(g_repo, "just_a_dir", GIT_ENOTFOUND);
+ refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS);
+ refute_submodule_exists(g_repo, "not", GIT_EEXISTS);
+
+ cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_index", ign));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_head", ign));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
+
+ cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_file", ign));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_untracked_file", ign));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ cl_git_pass(git_submodule_status(&status, g_repo,"sm_missing_commits", ign));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_MODIFIED) != 0);
+
+ cl_git_pass(git_submodule_status(&status, g_repo,"sm_added_and_uncommited", ign));
+ cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_ADDED) != 0);
+
+ /* removed sm_unchanged for deleted workdir */
+ cl_git_pass(git_submodule_status(&status, g_repo,"sm_unchanged", ign));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_DELETED) != 0);
+
+ /* now mkdir sm_unchanged to test uninitialized */
+ cl_git_pass(git_futils_mkdir_relative("sm_unchanged", "submod2", 0755, 0, NULL));
+ cl_git_pass(git_submodule_status(&status, g_repo,"sm_unchanged", ign));
+ cl_assert((status & GIT_SUBMODULE_STATUS_WD_UNINITIALIZED) != 0);
+
+ /* update sm_changed_head in index */
+ add_submodule_to_index("sm_changed_head");
+ cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_head", ign));
+ cl_assert((status & GIT_SUBMODULE_STATUS_INDEX_MODIFIED) != 0);
+}
+
+void test_submodule_status__ignore_all(void)
+{
+ unsigned int status;
+ git_submodule_ignore_t ign = GIT_SUBMODULE_IGNORE_ALL;
+
+ rm_submodule("sm_unchanged");
+
+ refute_submodule_exists(g_repo, "just_a_dir", GIT_ENOTFOUND);
+ refute_submodule_exists(g_repo, "not-submodule", GIT_EEXISTS);
+ refute_submodule_exists(g_repo, "not", GIT_EEXISTS);
+
+ cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_index", ign));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_head", ign));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_file", ign));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_untracked_file", ign));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ cl_git_pass(git_submodule_status(&status, g_repo,"sm_missing_commits", ign));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ cl_git_pass(git_submodule_status(&status, g_repo,"sm_added_and_uncommited", ign));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ /* removed sm_unchanged for deleted workdir */
+ cl_git_pass(git_submodule_status(&status, g_repo,"sm_unchanged", ign));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ /* now mkdir sm_unchanged to test uninitialized */
+ cl_git_pass(git_futils_mkdir_relative("sm_unchanged", "submod2", 0755, 0, NULL));
+ cl_git_pass(git_submodule_status(&status, g_repo,"sm_unchanged", ign));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ /* update sm_changed_head in index */
+ add_submodule_to_index("sm_changed_head");
+ cl_git_pass(git_submodule_status(&status, g_repo,"sm_changed_head", ign));
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+}
+
+typedef struct {
+ size_t counter;
+ const char **paths;
+ int *statuses;
+} submodule_expectations;
+
+static int confirm_submodule_status(
+ const char *path, unsigned int status_flags, void *payload)
+{
+ submodule_expectations *exp = payload;
+
+ while (exp->statuses[exp->counter] < 0)
+ exp->counter++;
+
+ cl_assert_equal_i(exp->statuses[exp->counter], (int)status_flags);
+ cl_assert_equal_s(exp->paths[exp->counter++], path);
+
+ GIT_UNUSED(status_flags);
+
+ return 0;
+}
+
+void test_submodule_status__iterator(void)
+{
+ git_iterator *iter;
+ git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
+ const git_index_entry *entry;
+ size_t i;
+ static const char *expected[] = {
+ ".gitmodules",
+ "just_a_dir/",
+ "just_a_dir/contents",
+ "just_a_file",
+ "not-submodule/",
+ "not-submodule/README.txt",
+ "not/",
+ "not/README.txt",
+ "README.txt",
+ "sm_added_and_uncommited",
+ "sm_changed_file",
+ "sm_changed_head",
+ "sm_changed_index",
+ "sm_changed_untracked_file",
+ "sm_missing_commits",
+ "sm_unchanged",
+ NULL
+ };
+ static int expected_flags[] = {
+ GIT_STATUS_INDEX_MODIFIED | GIT_STATUS_WT_MODIFIED, /* ".gitmodules" */
+ -1, /* "just_a_dir/" will be skipped */
+ GIT_STATUS_CURRENT, /* "just_a_dir/contents" */
+ GIT_STATUS_CURRENT, /* "just_a_file" */
+ GIT_STATUS_WT_NEW, /* "not-submodule/" untracked item */
+ -1, /* "not-submodule/README.txt" */
+ GIT_STATUS_WT_NEW, /* "not/" untracked item */
+ -1, /* "not/README.txt" */
+ GIT_STATUS_CURRENT, /* "README.txt */
+ GIT_STATUS_INDEX_NEW, /* "sm_added_and_uncommited" */
+ GIT_STATUS_WT_MODIFIED, /* "sm_changed_file" */
+ GIT_STATUS_WT_MODIFIED, /* "sm_changed_head" */
+ GIT_STATUS_WT_MODIFIED, /* "sm_changed_index" */
+ GIT_STATUS_WT_MODIFIED, /* "sm_changed_untracked_file" */
+ GIT_STATUS_WT_MODIFIED, /* "sm_missing_commits" */
+ GIT_STATUS_CURRENT, /* "sm_unchanged" */
+ 0
+ };
+ submodule_expectations exp = { 0, expected, expected_flags };
+ git_status_options opts = GIT_STATUS_OPTIONS_INIT;
+ git_index *index;
+
+ iter_opts.flags = GIT_ITERATOR_IGNORE_CASE | GIT_ITERATOR_INCLUDE_TREES;
+
+ cl_git_pass(git_repository_index(&index, g_repo));
+ cl_git_pass(git_iterator_for_workdir(&iter, g_repo, index, NULL, &iter_opts));
+
+ for (i = 0; !git_iterator_advance(&entry, iter); ++i)
+ cl_assert_equal_s(expected[i], entry->path);
+
+ git_iterator_free(iter);
+ git_index_free(index);
+
+ opts.flags = GIT_STATUS_OPT_INCLUDE_UNTRACKED |
+ GIT_STATUS_OPT_INCLUDE_UNMODIFIED |
+ GIT_STATUS_OPT_INCLUDE_IGNORED |
+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS |
+ GIT_STATUS_OPT_SORT_CASE_INSENSITIVELY;
+
+ cl_git_pass(git_status_foreach_ext(
+ g_repo, &opts, confirm_submodule_status, &exp));
+}
+
+void test_submodule_status__untracked_dirs_containing_ignored_files(void)
+{
+ unsigned int status, expected;
+
+ cl_git_append2file(
+ "submod2/.git/modules/sm_unchanged/info/exclude", "\n*.ignored\n");
+
+ cl_git_pass(
+ git_futils_mkdir_relative("sm_unchanged/directory", "submod2", 0755, 0, NULL));
+ cl_git_mkfile(
+ "submod2/sm_unchanged/directory/i_am.ignored",
+ "ignore this file, please\n");
+
+ status = get_submodule_status(g_repo, "sm_unchanged");
+ cl_assert(GIT_SUBMODULE_STATUS_IS_UNMODIFIED(status));
+
+ expected = GIT_SUBMODULE_STATUS_IN_HEAD |
+ GIT_SUBMODULE_STATUS_IN_INDEX |
+ GIT_SUBMODULE_STATUS_IN_CONFIG |
+ GIT_SUBMODULE_STATUS_IN_WD;
+ cl_assert(status == expected);
+}
diff --git a/tests/libgit2/submodule/submodule_helpers.c b/tests/libgit2/submodule/submodule_helpers.c
new file mode 100644
index 0000000..b8fc9f6
--- /dev/null
+++ b/tests/libgit2/submodule/submodule_helpers.c
@@ -0,0 +1,245 @@
+#include "clar_libgit2.h"
+#include "path.h"
+#include "util.h"
+#include "posix.h"
+#include "submodule_helpers.h"
+#include "git2/sys/repository.h"
+
+/* rewrite gitmodules -> .gitmodules
+ * rewrite the empty or relative urls inside each module
+ * rename the .gitted directory inside any submodule to .git
+ */
+void rewrite_gitmodules(const char *workdir)
+{
+ git_str in_f = GIT_STR_INIT, out_f = GIT_STR_INIT, path = GIT_STR_INIT;
+ FILE *in, *out;
+ char line[256];
+
+ cl_git_pass(git_str_joinpath(&in_f, workdir, "gitmodules"));
+ cl_git_pass(git_str_joinpath(&out_f, workdir, ".gitmodules"));
+
+ cl_assert((in = fopen(in_f.ptr, "rb")) != NULL);
+ cl_assert((out = fopen(out_f.ptr, "wb")) != NULL);
+
+ while (fgets(line, sizeof(line), in) != NULL) {
+ char *scan = line;
+
+ while (*scan == ' ' || *scan == '\t') scan++;
+
+ /* rename .gitted -> .git in submodule directories */
+ if (git__prefixcmp(scan, "path =") == 0) {
+ scan += strlen("path =");
+ while (*scan == ' ') scan++;
+
+ git_str_joinpath(&path, workdir, scan);
+ git_str_rtrim(&path);
+ git_str_joinpath(&path, path.ptr, ".gitted");
+
+ if (!git_str_oom(&path) && p_access(path.ptr, F_OK) == 0) {
+ git_str_joinpath(&out_f, workdir, scan);
+ git_str_rtrim(&out_f);
+ git_str_joinpath(&out_f, out_f.ptr, ".git");
+
+ if (!git_str_oom(&out_f))
+ p_rename(path.ptr, out_f.ptr);
+ }
+ }
+
+ /* copy non-"url =" lines verbatim */
+ if (git__prefixcmp(scan, "url =") != 0) {
+ fputs(line, out);
+ continue;
+ }
+
+ /* convert relative URLs in "url =" lines */
+ scan += strlen("url =");
+ while (*scan == ' ') scan++;
+
+ if (*scan == '.') {
+ git_str_joinpath(&path, workdir, scan);
+ git_str_rtrim(&path);
+ } else if (!*scan || *scan == '\n') {
+ git_str_joinpath(&path, workdir, "../testrepo.git");
+ } else {
+ fputs(line, out);
+ continue;
+ }
+
+ git_fs_path_prettify(&path, path.ptr, NULL);
+ git_str_putc(&path, '\n');
+ cl_assert(!git_str_oom(&path));
+
+ fwrite(line, scan - line, sizeof(char), out);
+ fputs(path.ptr, out);
+ }
+
+ fclose(in);
+ fclose(out);
+
+ cl_must_pass(p_unlink(in_f.ptr));
+
+ git_str_dispose(&in_f);
+ git_str_dispose(&out_f);
+ git_str_dispose(&path);
+}
+
+static void cleanup_fixture_submodules(void *payload)
+{
+ cl_git_sandbox_cleanup(); /* either "submodules" or "submod2" */
+
+ if (payload)
+ cl_fixture_cleanup(payload);
+}
+
+git_repository *setup_fixture_submodules(void)
+{
+ git_repository *repo = cl_git_sandbox_init("submodules");
+
+ cl_fixture_sandbox("testrepo.git");
+
+ rewrite_gitmodules(git_repository_workdir(repo));
+ p_rename("submodules/testrepo/.gitted", "submodules/testrepo/.git");
+
+ cl_set_cleanup(cleanup_fixture_submodules, "testrepo.git");
+
+ cl_git_pass(git_repository_reinit_filesystem(repo, 1));
+
+ return repo;
+}
+
+git_repository *setup_fixture_submod2(void)
+{
+ git_repository *repo = cl_git_sandbox_init("submod2");
+
+ cl_fixture_sandbox("submod2_target");
+ p_rename("submod2_target/.gitted", "submod2_target/.git");
+
+ rewrite_gitmodules(git_repository_workdir(repo));
+ p_rename("submod2/not-submodule/.gitted", "submod2/not-submodule/.git");
+ p_rename("submod2/not/.gitted", "submod2/not/.git");
+
+ cl_set_cleanup(cleanup_fixture_submodules, "submod2_target");
+
+ cl_git_pass(git_repository_reinit_filesystem(repo, 1));
+
+ return repo;
+}
+
+git_repository *setup_fixture_submod3(void)
+{
+ git_repository *repo = cl_git_sandbox_init("submod3");
+
+ cl_fixture_sandbox("submod2_target");
+ p_rename("submod2_target/.gitted", "submod2_target/.git");
+
+ rewrite_gitmodules(git_repository_workdir(repo));
+ p_rename("submod3/One/.gitted", "submod3/One/.git");
+ p_rename("submod3/TWO/.gitted", "submod3/TWO/.git");
+ p_rename("submod3/three/.gitted", "submod3/three/.git");
+ p_rename("submod3/FoUr/.gitted", "submod3/FoUr/.git");
+ p_rename("submod3/Five/.gitted", "submod3/Five/.git");
+ p_rename("submod3/six/.gitted", "submod3/six/.git");
+ p_rename("submod3/sEvEn/.gitted", "submod3/sEvEn/.git");
+ p_rename("submod3/EIGHT/.gitted", "submod3/EIGHT/.git");
+ p_rename("submod3/nine/.gitted", "submod3/nine/.git");
+ p_rename("submod3/TEN/.gitted", "submod3/TEN/.git");
+
+ cl_set_cleanup(cleanup_fixture_submodules, "submod2_target");
+
+ cl_git_pass(git_repository_reinit_filesystem(repo, 1));
+
+ return repo;
+}
+
+git_repository *setup_fixture_super(void)
+{
+ git_repository *repo = cl_git_sandbox_init("super");
+
+ cl_fixture_sandbox("sub.git");
+ p_mkdir("super/sub", 0777);
+
+ rewrite_gitmodules(git_repository_workdir(repo));
+
+ cl_set_cleanup(cleanup_fixture_submodules, "sub.git");
+
+ cl_git_pass(git_repository_reinit_filesystem(repo, 1));
+
+ return repo;
+}
+
+git_repository *setup_fixture_submodule_simple(void)
+{
+ git_repository *repo = cl_git_sandbox_init("submodule_simple");
+
+ cl_fixture_sandbox("testrepo.git");
+ p_mkdir("submodule_simple/testrepo", 0777);
+
+ cl_set_cleanup(cleanup_fixture_submodules, "testrepo.git");
+
+ cl_git_pass(git_repository_reinit_filesystem(repo, 1));
+
+ return repo;
+}
+
+git_repository *setup_fixture_submodule_with_path(void)
+{
+ git_repository *repo = cl_git_sandbox_init("submodule_with_path");
+
+ cl_fixture_sandbox("testrepo.git");
+ p_mkdir("submodule_with_path/lib", 0777);
+ p_mkdir("submodule_with_path/lib/testrepo", 0777);
+
+ cl_set_cleanup(cleanup_fixture_submodules, "testrepo.git");
+
+ cl_git_pass(git_repository_reinit_filesystem(repo, 1));
+
+ return repo;
+}
+
+void assert__submodule_exists(
+ git_repository *repo, const char *name,
+ const char *msg, const char *file, const char *func, int line)
+{
+ git_submodule *sm;
+ int error = git_submodule_lookup(&sm, repo, name);
+ if (error)
+ cl_git_report_failure(error, 0, file, func, line, msg);
+ cl_assert_at_line(sm != NULL, file, func, line);
+ git_submodule_free(sm);
+}
+
+void refute__submodule_exists(
+ git_repository *repo, const char *name, int expected_error,
+ const char *msg, const char *file, const char *func, int line)
+{
+ clar__assert_equal(
+ file, func, line, msg, 1, "%i",
+ expected_error, (int)(git_submodule_lookup(NULL, repo, name)));
+}
+
+unsigned int get_submodule_status(git_repository *repo, const char *name)
+{
+ unsigned int status = 0;
+
+ assert(repo && name);
+
+ cl_git_pass(git_submodule_status(&status, repo, name, GIT_SUBMODULE_IGNORE_UNSPECIFIED));
+
+ return status;
+}
+
+static int print_submodules(git_submodule *sm, const char *name, void *p)
+{
+ unsigned int loc = 0;
+ GIT_UNUSED(p);
+ git_submodule_location(&loc, sm);
+ fprintf(stderr, "# submodule %s (at %s) flags %x\n",
+ name, git_submodule_path(sm), loc);
+ return 0;
+}
+
+void dump_submodules(git_repository *repo)
+{
+ git_submodule_foreach(repo, print_submodules, NULL);
+}
+
diff --git a/tests/libgit2/submodule/submodule_helpers.h b/tests/libgit2/submodule/submodule_helpers.h
new file mode 100644
index 0000000..3c3f062
--- /dev/null
+++ b/tests/libgit2/submodule/submodule_helpers.h
@@ -0,0 +1,25 @@
+extern void rewrite_gitmodules(const char *workdir);
+
+/* these will automatically set a cleanup callback */
+extern git_repository *setup_fixture_submodules(void);
+extern git_repository *setup_fixture_submod2(void);
+extern git_repository *setup_fixture_submod3(void);
+extern git_repository *setup_fixture_submodule_simple(void);
+extern git_repository *setup_fixture_super(void);
+extern git_repository *setup_fixture_submodule_with_path(void);
+
+extern unsigned int get_submodule_status(git_repository *, const char *);
+
+extern void assert__submodule_exists(git_repository *, const char *,
+ const char *, const char *, const char *, int);
+
+#define assert_submodule_exists(repo,name) \
+ assert__submodule_exists(repo, name, "git_submodule_lookup(" #name ") failed", __FILE__, __func__, __LINE__)
+
+extern void refute__submodule_exists(git_repository *, const char *,
+ int err, const char *, const char *, const char *, int);
+
+#define refute_submodule_exists(repo,name,code) \
+ refute__submodule_exists(repo, name, code, "expected git_submodule_lookup(" #name ") to fail with error " #code, __FILE__, __func__, __LINE__)
+
+extern void dump_submodules(git_repository *repo);
diff --git a/tests/libgit2/submodule/update.c b/tests/libgit2/submodule/update.c
new file mode 100644
index 0000000..052a4a1
--- /dev/null
+++ b/tests/libgit2/submodule/update.c
@@ -0,0 +1,460 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+#include "path.h"
+#include "submodule_helpers.h"
+#include "futils.h"
+
+static git_repository *g_repo = NULL;
+
+void test_submodule_update__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_submodule_update__uninitialized_submodule_no_init(void)
+{
+ git_submodule *sm;
+ git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
+
+ g_repo = setup_fixture_submodule_simple();
+
+ /* get the submodule */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
+
+ /* updating an uninitialized repository throws */
+ cl_git_fail_with(
+ GIT_ERROR,
+ git_submodule_update(sm, 0, &update_options));
+
+ git_submodule_free(sm);
+}
+
+struct update_submodule_cb_payload {
+ int update_tips_called;
+ int checkout_progress_called;
+ int checkout_notify_called;
+};
+
+static void checkout_progress_cb(
+ const char *path,
+ size_t completed_steps,
+ size_t total_steps,
+ void *payload)
+{
+ struct update_submodule_cb_payload *update_payload = payload;
+
+ GIT_UNUSED(path);
+ GIT_UNUSED(completed_steps);
+ GIT_UNUSED(total_steps);
+
+ update_payload->checkout_progress_called = 1;
+}
+
+static int checkout_notify_cb(
+ git_checkout_notify_t why,
+ const char *path,
+ const git_diff_file *baseline,
+ const git_diff_file *target,
+ const git_diff_file *workdir,
+ void *payload)
+{
+ struct update_submodule_cb_payload *update_payload = payload;
+
+ GIT_UNUSED(why);
+ GIT_UNUSED(path);
+ GIT_UNUSED(baseline);
+ GIT_UNUSED(target);
+ GIT_UNUSED(workdir);
+
+ update_payload->checkout_notify_called = 1;
+
+ return 0;
+}
+
+static int update_tips(const char *refname, const git_oid *a, const git_oid *b, void *data)
+{
+ struct update_submodule_cb_payload *update_payload = data;
+
+ GIT_UNUSED(refname);
+ GIT_UNUSED(a);
+ GIT_UNUSED(b);
+
+ update_payload->update_tips_called = 1;
+
+ return 1;
+}
+
+void test_submodule_update__update_submodule(void)
+{
+ git_submodule *sm;
+ git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
+ unsigned int submodule_status = 0;
+ struct update_submodule_cb_payload update_payload = { 0 };
+
+ g_repo = setup_fixture_submodule_simple();
+
+ update_options.checkout_opts.progress_cb = checkout_progress_cb;
+ update_options.checkout_opts.progress_payload = &update_payload;
+
+ update_options.fetch_opts.callbacks.update_tips = update_tips;
+ update_options.fetch_opts.callbacks.payload = &update_payload;
+
+ /* get the submodule */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
+
+ /* verify the initial state of the submodule */
+ cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
+ cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
+ GIT_SUBMODULE_STATUS_IN_INDEX |
+ GIT_SUBMODULE_STATUS_IN_CONFIG |
+ GIT_SUBMODULE_STATUS_WD_UNINITIALIZED);
+
+ /* initialize and update the submodule */
+ cl_git_pass(git_submodule_init(sm, 0));
+ cl_git_pass(git_submodule_update(sm, 0, &update_options));
+
+ /* verify state */
+ cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
+ cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
+ GIT_SUBMODULE_STATUS_IN_INDEX |
+ GIT_SUBMODULE_STATUS_IN_CONFIG |
+ GIT_SUBMODULE_STATUS_IN_WD);
+
+ cl_assert(git_oid_streq(git_submodule_head_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
+ cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
+ cl_assert(git_oid_streq(git_submodule_index_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
+
+ /* verify that the expected callbacks have been called. */
+ cl_assert_equal_i(1, update_payload.checkout_progress_called);
+ cl_assert_equal_i(1, update_payload.update_tips_called);
+
+ git_submodule_free(sm);
+}
+
+void test_submodule_update__update_submodule_with_path(void)
+{
+ git_submodule *sm;
+ git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
+ unsigned int submodule_status = 0;
+ struct update_submodule_cb_payload update_payload = { 0 };
+
+ g_repo = setup_fixture_submodule_with_path();
+
+ update_options.checkout_opts.progress_cb = checkout_progress_cb;
+ update_options.checkout_opts.progress_payload = &update_payload;
+
+ update_options.fetch_opts.callbacks.update_tips = update_tips;
+ update_options.fetch_opts.callbacks.payload = &update_payload;
+
+ /* get the submodule */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
+
+ /* verify the initial state of the submodule */
+ cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
+ cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
+ GIT_SUBMODULE_STATUS_IN_INDEX |
+ GIT_SUBMODULE_STATUS_IN_CONFIG |
+ GIT_SUBMODULE_STATUS_WD_UNINITIALIZED);
+
+ /* initialize and update the submodule */
+ cl_git_pass(git_submodule_init(sm, 0));
+ cl_git_pass(git_submodule_update(sm, 0, &update_options));
+
+ /* verify state */
+ cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
+ cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
+ GIT_SUBMODULE_STATUS_IN_INDEX |
+ GIT_SUBMODULE_STATUS_IN_CONFIG |
+ GIT_SUBMODULE_STATUS_IN_WD);
+
+ cl_assert(git_oid_streq(git_submodule_head_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
+ cl_assert(git_oid_streq(git_submodule_wd_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
+ cl_assert(git_oid_streq(git_submodule_index_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
+
+ /* verify that the expected callbacks have been called. */
+ cl_assert_equal_i(1, update_payload.checkout_progress_called);
+ cl_assert_equal_i(1, update_payload.update_tips_called);
+
+ git_submodule_free(sm);
+}
+
+void test_submodule_update__update_and_init_submodule(void)
+{
+ git_submodule *sm;
+ git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
+ unsigned int submodule_status = 0;
+
+ g_repo = setup_fixture_submodule_simple();
+
+ /* get the submodule */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
+
+ cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
+ cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
+ GIT_SUBMODULE_STATUS_IN_INDEX |
+ GIT_SUBMODULE_STATUS_IN_CONFIG |
+ GIT_SUBMODULE_STATUS_WD_UNINITIALIZED);
+
+ /* update (with option to initialize sub repo) */
+ cl_git_pass(git_submodule_update(sm, 1, &update_options));
+
+ /* verify expected state */
+ cl_assert(git_oid_streq(git_submodule_head_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
+ cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
+ cl_assert(git_oid_streq(git_submodule_index_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
+
+ git_submodule_free(sm);
+}
+
+void test_submodule_update__update_skip_configured_missing_submodule(void)
+{
+ git_submodule *sm;
+ git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
+ unsigned int submodule_status = 0;
+
+ g_repo = setup_fixture_submod2();
+
+ /* get the submodule */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "sm_gitmodules_only"));
+
+ cl_git_pass(git_submodule_status(&submodule_status, g_repo, "sm_gitmodules_only", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
+ cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_CONFIG);
+
+ /* update (with option to initialize sub repo) */
+ cl_git_pass(git_submodule_update(sm, 1, &update_options));
+
+ git_submodule_free(sm);
+}
+
+void test_submodule_update__update_already_checked_out_submodule(void)
+{
+ git_submodule *sm = NULL;
+ git_checkout_options checkout_options = GIT_CHECKOUT_OPTIONS_INIT;
+ git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
+ unsigned int submodule_status = 0;
+ git_reference *branch_reference = NULL;
+ git_object *branch_commit = NULL;
+ struct update_submodule_cb_payload update_payload = { 0 };
+
+ g_repo = setup_fixture_submodule_simple();
+
+ update_options.checkout_opts.progress_cb = checkout_progress_cb;
+ update_options.checkout_opts.progress_payload = &update_payload;
+
+ /* Initialize and update the sub repository */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
+
+ cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
+ cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
+ GIT_SUBMODULE_STATUS_IN_INDEX |
+ GIT_SUBMODULE_STATUS_IN_CONFIG |
+ GIT_SUBMODULE_STATUS_WD_UNINITIALIZED);
+
+ cl_git_pass(git_submodule_update(sm, 1, &update_options));
+
+ /* verify expected state */
+ cl_assert(git_oid_streq(git_submodule_head_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
+ cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
+ cl_assert(git_oid_streq(git_submodule_index_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
+
+ /* checkout the alternate_1 branch */
+ checkout_options.checkout_strategy = GIT_CHECKOUT_SAFE;
+
+ cl_git_pass(git_reference_lookup(&branch_reference, g_repo, "refs/heads/alternate_1"));
+ cl_git_pass(git_reference_peel(&branch_commit, branch_reference, GIT_OBJECT_COMMIT));
+ cl_git_pass(git_checkout_tree(g_repo, branch_commit, &checkout_options));
+ cl_git_pass(git_repository_set_head(g_repo, git_reference_name(branch_reference)));
+
+ /*
+ * Verify state after checkout of parent repository. The submodule ID in the
+ * HEAD commit and index should be updated, but not the workdir.
+ */
+
+ cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
+
+ git_submodule_free(sm);
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
+
+ cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
+ GIT_SUBMODULE_STATUS_IN_INDEX |
+ GIT_SUBMODULE_STATUS_IN_CONFIG |
+ GIT_SUBMODULE_STATUS_IN_WD |
+ GIT_SUBMODULE_STATUS_WD_MODIFIED);
+
+ cl_assert(git_oid_streq(git_submodule_head_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
+ cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
+ cl_assert(git_oid_streq(git_submodule_index_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
+
+ /*
+ * Update the submodule and verify the state.
+ * Now, the HEAD, index, and Workdir commits should all be updated to
+ * the new commit.
+ */
+ cl_git_pass(git_submodule_update(sm, 0, &update_options));
+ cl_assert(git_oid_streq(git_submodule_head_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
+ cl_assert(git_oid_streq(git_submodule_wd_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
+ cl_assert(git_oid_streq(git_submodule_index_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
+
+ /* verify that the expected callbacks have been called. */
+ cl_assert_equal_i(1, update_payload.checkout_progress_called);
+
+ git_submodule_free(sm);
+ git_object_free(branch_commit);
+ git_reference_free(branch_reference);
+}
+
+void test_submodule_update__update_blocks_on_dirty_wd(void)
+{
+ git_submodule *sm = NULL;
+ git_checkout_options checkout_options = GIT_CHECKOUT_OPTIONS_INIT;
+ git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
+ unsigned int submodule_status = 0;
+ git_reference *branch_reference = NULL;
+ git_object *branch_commit = NULL;
+ struct update_submodule_cb_payload update_payload = { 0 };
+
+ g_repo = setup_fixture_submodule_simple();
+
+ update_options.checkout_opts.notify_flags = GIT_CHECKOUT_NOTIFY_CONFLICT;
+ update_options.checkout_opts.notify_cb = checkout_notify_cb;
+ update_options.checkout_opts.notify_payload = &update_payload;
+
+ /* Initialize and update the sub repository */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
+
+ cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
+ cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
+ GIT_SUBMODULE_STATUS_IN_INDEX |
+ GIT_SUBMODULE_STATUS_IN_CONFIG |
+ GIT_SUBMODULE_STATUS_WD_UNINITIALIZED);
+
+ cl_git_pass(git_submodule_update(sm, 1, &update_options));
+
+ /* verify expected state */
+ cl_assert(git_oid_streq(git_submodule_head_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
+ cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
+ cl_assert(git_oid_streq(git_submodule_index_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
+
+ /* checkout the alternate_1 branch */
+ checkout_options.checkout_strategy = GIT_CHECKOUT_SAFE;
+
+ cl_git_pass(git_reference_lookup(&branch_reference, g_repo, "refs/heads/alternate_1"));
+ cl_git_pass(git_reference_peel(&branch_commit, branch_reference, GIT_OBJECT_COMMIT));
+ cl_git_pass(git_checkout_tree(g_repo, branch_commit, &checkout_options));
+ cl_git_pass(git_repository_set_head(g_repo, git_reference_name(branch_reference)));
+
+ /*
+ * Verify state after checkout of parent repository. The submodule ID in the
+ * HEAD commit and index should be updated, but not the workdir.
+ */
+
+ cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
+
+ git_submodule_free(sm);
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
+
+ cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
+ GIT_SUBMODULE_STATUS_IN_INDEX |
+ GIT_SUBMODULE_STATUS_IN_CONFIG |
+ GIT_SUBMODULE_STATUS_IN_WD |
+ GIT_SUBMODULE_STATUS_WD_MODIFIED);
+
+ cl_assert(git_oid_streq(git_submodule_head_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
+ cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
+ cl_assert(git_oid_streq(git_submodule_index_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
+
+ /*
+ * Create a conflicting edit in the subrepository to verify that
+ * the submodule update action is blocked.
+ */
+ cl_git_write2file("submodule_simple/testrepo/branch_file.txt", "a conflicting edit", 0,
+ O_WRONLY | O_CREAT | O_TRUNC, 0755);
+
+ cl_git_fail(git_submodule_update(sm, 0, &update_options));
+
+ /* verify that the expected callbacks have been called. */
+ cl_assert_equal_i(1, update_payload.checkout_notify_called);
+
+ /* verify that the submodule state has not changed. */
+ cl_assert(git_oid_streq(git_submodule_head_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
+ cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
+ cl_assert(git_oid_streq(git_submodule_index_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
+
+ git_submodule_free(sm);
+ git_object_free(branch_commit);
+ git_reference_free(branch_reference);
+}
+
+void test_submodule_update__can_force_update(void)
+{
+ git_submodule *sm = NULL;
+ git_checkout_options checkout_options = GIT_CHECKOUT_OPTIONS_INIT;
+ git_submodule_update_options update_options = GIT_SUBMODULE_UPDATE_OPTIONS_INIT;
+ unsigned int submodule_status = 0;
+ git_reference *branch_reference = NULL;
+ git_object *branch_commit = NULL;
+
+ g_repo = setup_fixture_submodule_simple();
+
+ /* Initialize and update the sub repository */
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
+
+ cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
+ cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
+ GIT_SUBMODULE_STATUS_IN_INDEX |
+ GIT_SUBMODULE_STATUS_IN_CONFIG |
+ GIT_SUBMODULE_STATUS_WD_UNINITIALIZED);
+
+ cl_git_pass(git_submodule_update(sm, 1, &update_options));
+
+ /* verify expected state */
+ cl_assert(git_oid_streq(git_submodule_head_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
+ cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
+ cl_assert(git_oid_streq(git_submodule_index_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
+
+ /* checkout the alternate_1 branch */
+ checkout_options.checkout_strategy = GIT_CHECKOUT_SAFE;
+
+ cl_git_pass(git_reference_lookup(&branch_reference, g_repo, "refs/heads/alternate_1"));
+ cl_git_pass(git_reference_peel(&branch_commit, branch_reference, GIT_OBJECT_COMMIT));
+ cl_git_pass(git_checkout_tree(g_repo, branch_commit, &checkout_options));
+ cl_git_pass(git_repository_set_head(g_repo, git_reference_name(branch_reference)));
+
+ /*
+ * Verify state after checkout of parent repository. The submodule ID in the
+ * HEAD commit and index should be updated, but not the workdir.
+ */
+ cl_git_pass(git_submodule_status(&submodule_status, g_repo, "testrepo", GIT_SUBMODULE_IGNORE_UNSPECIFIED));
+
+ git_submodule_free(sm);
+ cl_git_pass(git_submodule_lookup(&sm, g_repo, "testrepo"));
+
+ cl_assert_equal_i(submodule_status, GIT_SUBMODULE_STATUS_IN_HEAD |
+ GIT_SUBMODULE_STATUS_IN_INDEX |
+ GIT_SUBMODULE_STATUS_IN_CONFIG |
+ GIT_SUBMODULE_STATUS_IN_WD |
+ GIT_SUBMODULE_STATUS_WD_MODIFIED);
+
+ cl_assert(git_oid_streq(git_submodule_head_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
+ cl_assert(git_oid_streq(git_submodule_wd_id(sm), "be3563ae3f795b2b4353bcce3a527ad0a4f7f644") == 0);
+ cl_assert(git_oid_streq(git_submodule_index_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
+
+ /*
+ * Create a conflicting edit in the subrepository to verify that
+ * the submodule update action is blocked.
+ */
+ cl_git_write2file("submodule_simple/testrepo/branch_file.txt", "a conflicting edit", 0,
+ O_WRONLY | O_CREAT | O_TRUNC, 0777);
+
+ /* forcefully checkout and verify the submodule state was updated. */
+ update_options.checkout_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+ cl_git_pass(git_submodule_update(sm, 0, &update_options));
+ cl_assert(git_oid_streq(git_submodule_head_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
+ cl_assert(git_oid_streq(git_submodule_wd_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
+ cl_assert(git_oid_streq(git_submodule_index_id(sm), "a65fedf39aefe402d3bb6e24df4d4f5fe4547750") == 0);
+
+ git_submodule_free(sm);
+ git_object_free(branch_commit);
+ git_reference_free(branch_reference);
+}
+
diff --git a/tests/libgit2/threads/atomic.c b/tests/libgit2/threads/atomic.c
new file mode 100644
index 0000000..4d04a77
--- /dev/null
+++ b/tests/libgit2/threads/atomic.c
@@ -0,0 +1,125 @@
+#include "clar_libgit2.h"
+
+void test_threads_atomic__atomic32_set(void)
+{
+ git_atomic32 v = {0};
+ git_atomic32_set(&v, 1);
+ cl_assert_equal_i(v.val, 1);
+}
+
+void test_threads_atomic__atomic32_get(void)
+{
+ git_atomic32 v = {1};
+ cl_assert_equal_i(git_atomic32_get(&v), 1);
+}
+
+void test_threads_atomic__atomic32_inc(void)
+{
+ git_atomic32 v = {0};
+ cl_assert_equal_i(git_atomic32_inc(&v), 1);
+ cl_assert_equal_i(v.val, 1);
+}
+
+void test_threads_atomic__atomic32_add(void)
+{
+ git_atomic32 v = {0};
+ cl_assert_equal_i(git_atomic32_add(&v, 1), 1);
+ cl_assert_equal_i(v.val, 1);
+}
+
+void test_threads_atomic__atomic32_dec(void)
+{
+ git_atomic32 v = {1};
+ cl_assert_equal_i(git_atomic32_dec(&v), 0);
+ cl_assert_equal_i(v.val, 0);
+}
+
+void test_threads_atomic__atomic64_set(void)
+{
+#ifndef GIT_ARCH_64
+ cl_skip();
+#else
+ git_atomic64 v = {0};
+ git_atomic64_set(&v, 1);
+ cl_assert_equal_i(v.val, 1);
+#endif
+}
+
+void test_threads_atomic__atomic64_get(void)
+{
+#ifndef GIT_ARCH_64
+ cl_skip();
+#else
+ git_atomic64 v = {1};
+ cl_assert_equal_i(git_atomic64_get(&v), 1);
+#endif
+}
+
+void test_threads_atomic__atomic64_add(void)
+{
+#ifndef GIT_ARCH_64
+ cl_skip();
+#else
+ git_atomic64 v = {0};
+ cl_assert_equal_i(git_atomic64_add(&v, 1), 1);
+ cl_assert_equal_i(v.val, 1);
+#endif
+}
+
+void test_threads_atomic__cas_pointer(void)
+{
+ int *value = NULL;
+ int newvalue1 = 1, newvalue2 = 2;
+
+ /* value is updated */
+ cl_assert_equal_p(git_atomic_compare_and_swap(&value, NULL, &newvalue1), NULL);
+ cl_assert_equal_p(value, &newvalue1);
+
+ /* value is not updated */
+ cl_assert_equal_p(git_atomic_compare_and_swap(&value, NULL, &newvalue2), &newvalue1);
+ cl_assert_equal_p(value, &newvalue1);
+}
+
+void test_threads_atomic__cas_intptr(void)
+{
+ intptr_t value = 0;
+ intptr_t oldvalue;
+ intptr_t newvalue;
+
+ /* value is updated */
+ oldvalue = 0;
+ newvalue = 1;
+ cl_assert_equal_i((intptr_t)git_atomic_compare_and_swap(&value, (void *)oldvalue, (void *)newvalue), 0);
+ cl_assert_equal_i(value, 1);
+
+ /* value is not updated */
+ oldvalue = 0;
+ newvalue = 2;
+ cl_assert_equal_i((intptr_t)git_atomic_compare_and_swap(&value, (void *)oldvalue, (void *)newvalue), 1);
+ cl_assert_equal_i(value, 1);
+}
+
+void test_threads_atomic__swap(void)
+{
+ int *value = NULL;
+ int newvalue = 1;
+
+ cl_assert_equal_p(git_atomic_swap(value, &newvalue), NULL);
+ cl_assert_equal_p(value, &newvalue);
+
+ cl_assert_equal_p(git_atomic_swap(value, NULL), &newvalue);
+ cl_assert_equal_p(value, NULL);
+}
+
+void test_threads_atomic__load_ptr(void)
+{
+ int value = 1;
+ int *ptr = &value;
+ cl_assert_equal_p(git_atomic_load(ptr), &value);
+}
+
+void test_threads_atomic__load_intptr(void)
+{
+ intptr_t value = 1;
+ cl_assert_equal_i((intptr_t)git_atomic_load(value), 1);
+}
diff --git a/tests/libgit2/threads/basic.c b/tests/libgit2/threads/basic.c
new file mode 100644
index 0000000..2d7ddc2
--- /dev/null
+++ b/tests/libgit2/threads/basic.c
@@ -0,0 +1,83 @@
+#include "clar_libgit2.h"
+
+#include "thread_helpers.h"
+#include "cache.h"
+
+
+static git_repository *g_repo;
+
+void test_threads_basic__initialize(void)
+{
+ g_repo = cl_git_sandbox_init("testrepo");
+}
+
+void test_threads_basic__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+
+void test_threads_basic__cache(void)
+{
+ /* run several threads polling the cache at the same time */
+ cl_assert(1 == 1);
+}
+
+void test_threads_basic__multiple_init(void)
+{
+ git_repository *nested_repo;
+
+ git_libgit2_init();
+ cl_git_pass(git_repository_open(&nested_repo, cl_fixture("testrepo.git")));
+ git_repository_free(nested_repo);
+
+ git_libgit2_shutdown();
+ cl_git_pass(git_repository_open(&nested_repo, cl_fixture("testrepo.git")));
+ git_repository_free(nested_repo);
+}
+
+static void *set_error(void *dummy)
+{
+ git_error_set(GIT_ERROR_INVALID, "oh no, something happened!\n");
+
+ return dummy;
+}
+
+/* Set errors so we can check that we free it */
+void test_threads_basic__set_error(void)
+{
+ run_in_parallel(1, 4, set_error, NULL, NULL);
+}
+
+#ifdef GIT_THREADS
+static void *return_normally(void *param)
+{
+ return param;
+}
+
+static void *exit_abruptly(void *param)
+{
+ git_thread_exit(param);
+ return NULL;
+}
+#endif
+
+void test_threads_basic__exit(void)
+{
+#ifndef GIT_THREADS
+ clar__skip();
+#else
+ git_thread thread;
+ void *result;
+
+ /* Ensure that the return value of the threadproc is returned. */
+ cl_git_pass(git_thread_create(&thread, return_normally, (void *)424242));
+ cl_git_pass(git_thread_join(&thread, &result));
+ cl_assert_equal_sz(424242, (size_t)result);
+
+ /* Ensure that the return value of `git_thread_exit` is returned. */
+ cl_git_pass(git_thread_create(&thread, exit_abruptly, (void *)232323));
+ cl_git_pass(git_thread_join(&thread, &result));
+ cl_assert_equal_sz(232323, (size_t)result);
+#endif
+}
diff --git a/tests/libgit2/threads/diff.c b/tests/libgit2/threads/diff.c
new file mode 100644
index 0000000..04c8cb9
--- /dev/null
+++ b/tests/libgit2/threads/diff.c
@@ -0,0 +1,218 @@
+#include "clar_libgit2.h"
+#include "thread_helpers.h"
+
+#ifdef GIT_THREADS
+
+# if defined(GIT_WIN32)
+# define git_thread_yield() Sleep(0)
+# elif defined(__FreeBSD__) || defined(__MidnightBSD__) || defined(__DragonFly__)
+# define git_thread_yield() pthread_yield()
+# else
+# define git_thread_yield() sched_yield()
+# endif
+
+#else
+# define git_thread_yield() (void)0
+#endif
+
+static git_repository *_repo;
+static git_tree *_a, *_b;
+static git_atomic32 _counts[4];
+static int _check_counts;
+#ifdef GIT_WIN32
+static int _retries;
+#endif
+
+#define THREADS 20
+
+void test_threads_diff__initialize(void)
+{
+#ifdef GIT_WIN32
+ _retries = git_win32__retries;
+ git_win32__retries = 1;
+#endif
+}
+
+void test_threads_diff__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+
+#ifdef GIT_WIN32
+ git_win32__retries = _retries;
+#endif
+}
+
+static void setup_trees(void)
+{
+ git_index *idx;
+
+ _repo = cl_git_sandbox_reopen(); /* reopen sandbox to flush caches */
+
+ /* avoid competing to load initial index */
+ cl_git_pass(git_repository_index(&idx, _repo));
+ git_index_free(idx);
+
+ cl_git_pass(git_revparse_single(
+ (git_object **)&_a, _repo, "0017bd4ab1^{tree}"));
+ cl_git_pass(git_revparse_single(
+ (git_object **)&_b, _repo, "26a125ee1b^{tree}"));
+
+ memset(_counts, 0, sizeof(_counts));
+}
+
+static void free_trees(void)
+{
+ git_tree_free(_a); _a = NULL;
+ git_tree_free(_b); _b = NULL;
+
+ if (_check_counts) {
+ cl_assert_equal_i(288, git_atomic32_get(&_counts[0]));
+ cl_assert_equal_i(112, git_atomic32_get(&_counts[1]));
+ cl_assert_equal_i( 80, git_atomic32_get(&_counts[2]));
+ cl_assert_equal_i( 96, git_atomic32_get(&_counts[3]));
+ }
+}
+
+static void *run_index_diffs(void *arg)
+{
+ int thread = *(int *)arg;
+ git_repository *repo;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ size_t i;
+ int exp[4] = { 0, 0, 0, 0 };
+
+ cl_git_pass(git_repository_open(&repo, git_repository_path(_repo)));
+
+ switch (thread & 0x03) {
+ case 0: /* diff index to workdir */;
+ cl_git_pass(git_diff_index_to_workdir(&diff, repo, NULL, &opts));
+ break;
+ case 1: /* diff tree 'a' to index */;
+ cl_git_pass(git_diff_tree_to_index(&diff, repo, _a, NULL, &opts));
+ break;
+ case 2: /* diff tree 'b' to index */;
+ cl_git_pass(git_diff_tree_to_index(&diff, repo, _b, NULL, &opts));
+ break;
+ case 3: /* diff index to workdir (explicit index) */;
+ {
+ git_index *idx;
+ cl_git_pass(git_repository_index(&idx, repo));
+ cl_git_pass(git_diff_index_to_workdir(&diff, repo, idx, &opts));
+ git_index_free(idx);
+ break;
+ }
+ }
+
+ /* keep some diff stats to make sure results are as expected */
+
+ i = git_diff_num_deltas(diff);
+ git_atomic32_add(&_counts[0], (int32_t)i);
+ exp[0] = (int)i;
+
+ while (i > 0) {
+ switch (git_diff_get_delta(diff, --i)->status) {
+ case GIT_DELTA_MODIFIED: exp[1]++; git_atomic32_inc(&_counts[1]); break;
+ case GIT_DELTA_ADDED: exp[2]++; git_atomic32_inc(&_counts[2]); break;
+ case GIT_DELTA_DELETED: exp[3]++; git_atomic32_inc(&_counts[3]); break;
+ default: break;
+ }
+ }
+
+ switch (thread & 0x03) {
+ case 0: case 3:
+ cl_assert_equal_i(8, exp[0]); cl_assert_equal_i(4, exp[1]);
+ cl_assert_equal_i(0, exp[2]); cl_assert_equal_i(4, exp[3]);
+ break;
+ case 1:
+ cl_assert_equal_i(12, exp[0]); cl_assert_equal_i(3, exp[1]);
+ cl_assert_equal_i(7, exp[2]); cl_assert_equal_i(2, exp[3]);
+ break;
+ case 2:
+ cl_assert_equal_i(8, exp[0]); cl_assert_equal_i(3, exp[1]);
+ cl_assert_equal_i(3, exp[2]); cl_assert_equal_i(2, exp[3]);
+ break;
+ }
+
+ git_diff_free(diff);
+ git_repository_free(repo);
+ git_error_clear();
+
+ return arg;
+}
+
+void test_threads_diff__concurrent_diffs(void)
+{
+ _repo = cl_git_sandbox_init("status");
+ _check_counts = 1;
+
+ run_in_parallel(
+ 5, 32, run_index_diffs, setup_trees, free_trees);
+}
+
+static void *run_index_diffs_with_modifier(void *arg)
+{
+ int thread = *(int *)arg;
+ git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
+ git_diff *diff = NULL;
+ git_index *idx = NULL;
+ git_repository *repo;
+
+ cl_git_pass(git_repository_open(&repo, git_repository_path(_repo)));
+ cl_git_pass(git_repository_index(&idx, repo));
+
+ /* have first thread altering the index as we go */
+ if (thread == 0) {
+ int i;
+
+ for (i = 0; i < 300; ++i) {
+ switch (i & 0x03) {
+ case 0: (void)git_index_add_bypath(idx, "new_file"); break;
+ case 1: (void)git_index_remove_bypath(idx, "modified_file"); break;
+ case 2: (void)git_index_remove_bypath(idx, "new_file"); break;
+ case 3: (void)git_index_add_bypath(idx, "modified_file"); break;
+ }
+ git_thread_yield();
+ }
+
+ goto done;
+ }
+
+ /* only use explicit index in this test to prevent reloading */
+
+ switch (thread & 0x03) {
+ case 0: /* diff index to workdir */;
+ cl_git_pass(git_diff_index_to_workdir(&diff, repo, idx, &opts));
+ break;
+ case 1: /* diff tree 'a' to index */;
+ cl_git_pass(git_diff_tree_to_index(&diff, repo, _a, idx, &opts));
+ break;
+ case 2: /* diff tree 'b' to index */;
+ cl_git_pass(git_diff_tree_to_index(&diff, repo, _b, idx, &opts));
+ break;
+ case 3: /* diff index to workdir reversed */;
+ opts.flags |= GIT_DIFF_REVERSE;
+ cl_git_pass(git_diff_index_to_workdir(&diff, repo, idx, &opts));
+ break;
+ }
+
+ /* results will be unpredictable with index modifier thread running */
+
+ git_diff_free(diff);
+
+done:
+ git_index_free(idx);
+ git_repository_free(repo);
+ git_error_clear();
+
+ return arg;
+}
+
+void test_threads_diff__with_concurrent_index_modified(void)
+{
+ _repo = cl_git_sandbox_init("status");
+ _check_counts = 0;
+
+ run_in_parallel(
+ 5, 16, run_index_diffs_with_modifier, setup_trees, free_trees);
+}
diff --git a/tests/libgit2/threads/iterator.c b/tests/libgit2/threads/iterator.c
new file mode 100644
index 0000000..33d1bda
--- /dev/null
+++ b/tests/libgit2/threads/iterator.c
@@ -0,0 +1,55 @@
+#include "clar_libgit2.h"
+#include "thread_helpers.h"
+#include "iterator.h"
+
+static git_repository *_repo;
+
+void test_threads_iterator__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+static void *run_workdir_iterator(void *arg)
+{
+ int error = 0;
+ git_repository *repo;
+ git_iterator *iter;
+ git_iterator_options iter_opts = GIT_ITERATOR_OPTIONS_INIT;
+ const git_index_entry *entry = NULL;
+
+ iter_opts.flags = GIT_ITERATOR_DONT_AUTOEXPAND;
+
+ cl_git_pass(git_repository_open(&repo, git_repository_path(_repo)));
+ cl_git_pass(git_iterator_for_workdir(
+ &iter, repo, NULL, NULL, &iter_opts));
+
+ while (!error) {
+ if (entry && entry->mode == GIT_FILEMODE_TREE) {
+ error = git_iterator_advance_into(&entry, iter);
+
+ if (error == GIT_ENOTFOUND)
+ error = git_iterator_advance(&entry, iter);
+ } else {
+ error = git_iterator_advance(&entry, iter);
+ }
+
+ if (!error)
+ (void)git_iterator_current_is_ignored(iter);
+ }
+
+ cl_assert_equal_i(GIT_ITEROVER, error);
+
+ git_iterator_free(iter);
+ git_repository_free(repo);
+ git_error_clear();
+ return arg;
+}
+
+
+void test_threads_iterator__workdir(void)
+{
+ _repo = cl_git_sandbox_init("status");
+
+ run_in_parallel(
+ 1, 20, run_workdir_iterator, NULL, NULL);
+}
diff --git a/tests/libgit2/threads/refdb.c b/tests/libgit2/threads/refdb.c
new file mode 100644
index 0000000..a4630df
--- /dev/null
+++ b/tests/libgit2/threads/refdb.c
@@ -0,0 +1,220 @@
+#include "clar_libgit2.h"
+#include "git2/refdb.h"
+#include "refdb.h"
+
+static git_repository *g_repo;
+static int g_expected = 0;
+
+#ifdef GIT_WIN32
+static bool concurrent_compress = false;
+#else
+static bool concurrent_compress = true;
+#endif
+
+void test_threads_refdb__initialize(void)
+{
+ g_repo = NULL;
+}
+
+void test_threads_refdb__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+ g_repo = NULL;
+}
+
+#define REPEAT 20
+#define THREADS 20
+/* Number of references to create or delete in each thread */
+#define NREFS 10
+
+struct th_data {
+ cl_git_thread_err error;
+ int id;
+ const char *path;
+};
+
+static void *iterate_refs(void *arg)
+{
+ struct th_data *data = (struct th_data *) arg;
+ git_reference_iterator *i;
+ git_reference *ref;
+ int count = 0, error;
+ git_repository *repo;
+
+ cl_git_thread_pass(data, git_repository_open(&repo, data->path));
+ do {
+ error = git_reference_iterator_new(&i, repo);
+ } while (error == GIT_ELOCKED);
+ cl_git_thread_pass(data, error);
+
+ for (count = 0; !git_reference_next(&ref, i); ++count) {
+ cl_assert(ref != NULL);
+ git_reference_free(ref);
+ }
+
+ if (g_expected > 0)
+ cl_assert_equal_i(g_expected, count);
+
+ git_reference_iterator_free(i);
+
+ git_repository_free(repo);
+ git_error_clear();
+ return arg;
+}
+
+static void *create_refs(void *arg)
+{
+ int i, error;
+ struct th_data *data = (struct th_data *) arg;
+ git_oid head;
+ char name[128];
+ git_reference *ref[NREFS];
+ git_repository *repo;
+
+ cl_git_thread_pass(data, git_repository_open(&repo, data->path));
+
+ do {
+ error = git_reference_name_to_id(&head, repo, "HEAD");
+ } while (error == GIT_ELOCKED);
+ cl_git_thread_pass(data, error);
+
+ for (i = 0; i < NREFS; ++i) {
+ p_snprintf(name, sizeof(name), "refs/heads/thread-%03d-%02d", data->id, i);
+ do {
+ error = git_reference_create(&ref[i], repo, name, &head, 0, NULL);
+ } while (error == GIT_ELOCKED);
+ cl_git_thread_pass(data, error);
+
+ if (concurrent_compress && i == NREFS/2) {
+ git_refdb *refdb;
+ cl_git_thread_pass(data, git_repository_refdb(&refdb, repo));
+ do {
+ error = git_refdb_compress(refdb);
+ } while (error == GIT_ELOCKED);
+ cl_git_thread_pass(data, error);
+ git_refdb_free(refdb);
+ }
+ }
+
+ for (i = 0; i < NREFS; ++i)
+ git_reference_free(ref[i]);
+
+ git_repository_free(repo);
+
+ git_error_clear();
+ return arg;
+}
+
+static void *delete_refs(void *arg)
+{
+ int i, error;
+ struct th_data *data = (struct th_data *) arg;
+ git_reference *ref;
+ char name[128];
+ git_repository *repo;
+
+ cl_git_thread_pass(data, git_repository_open(&repo, data->path));
+
+ for (i = 0; i < NREFS; ++i) {
+ p_snprintf(
+ name, sizeof(name), "refs/heads/thread-%03d-%02d", (data->id) & ~0x3, i);
+
+ if (!git_reference_lookup(&ref, repo, name)) {
+ do {
+ error = git_reference_delete(ref);
+ } while (error == GIT_ELOCKED);
+ /* Sometimes we race with other deleter threads */
+ if (error == GIT_ENOTFOUND)
+ error = 0;
+
+ cl_git_thread_pass(data, error);
+ git_reference_free(ref);
+ }
+
+ if (concurrent_compress && i == NREFS/2) {
+ git_refdb *refdb;
+ cl_git_thread_pass(data, git_repository_refdb(&refdb, repo));
+ do {
+ error = git_refdb_compress(refdb);
+ } while (error == GIT_ELOCKED);
+ cl_git_thread_pass(data, error);
+ git_refdb_free(refdb);
+ }
+ }
+
+ git_repository_free(repo);
+ git_error_clear();
+ return arg;
+}
+
+void test_threads_refdb__edit_while_iterate(void)
+{
+ int r, t;
+ struct th_data th_data[THREADS];
+ git_oid head;
+ git_reference *ref;
+ char name[128];
+ git_refdb *refdb;
+
+#ifdef GIT_THREADS
+ git_thread th[THREADS];
+#endif
+
+ g_repo = cl_git_sandbox_init("testrepo2");
+
+ cl_git_pass(git_reference_name_to_id(&head, g_repo, "HEAD"));
+
+ /* make a bunch of references */
+
+ for (r = 0; r < 50; ++r) {
+ p_snprintf(name, sizeof(name), "refs/heads/starter-%03d", r);
+ cl_git_pass(git_reference_create(&ref, g_repo, name, &head, 0, NULL));
+ git_reference_free(ref);
+ }
+
+ cl_git_pass(git_repository_refdb(&refdb, g_repo));
+ cl_git_pass(git_refdb_compress(refdb));
+ git_refdb_free(refdb);
+
+ g_expected = -1;
+
+ g_repo = cl_git_sandbox_reopen(); /* reopen to flush caches */
+
+ for (t = 0; t < THREADS; ++t) {
+ void *(*fn)(void *arg);
+
+ switch (t & 0x3) {
+ case 0: fn = create_refs; break;
+ case 1: fn = delete_refs; break;
+ default: fn = iterate_refs; break;
+ }
+
+ th_data[t].id = t;
+ th_data[t].path = git_repository_path(g_repo);
+
+#ifdef GIT_THREADS
+ cl_git_pass(git_thread_create(&th[t], fn, &th_data[t]));
+#else
+ fn(&th_data[t]);
+#endif
+ }
+
+#ifdef GIT_THREADS
+ for (t = 0; t < THREADS; ++t) {
+ cl_git_pass(git_thread_join(&th[t], NULL));
+ cl_git_thread_check(&th_data[t]);
+ }
+
+ memset(th, 0, sizeof(th));
+
+ for (t = 0; t < THREADS; ++t) {
+ th_data[t].id = t;
+ cl_git_pass(git_thread_create(&th[t], iterate_refs, &th_data[t]));
+ }
+
+ for (t = 0; t < THREADS; ++t) {
+ cl_git_pass(git_thread_join(&th[t], NULL));
+ cl_git_thread_check(&th_data[t]);
+ }
+#endif
+}
diff --git a/tests/libgit2/threads/thread_helpers.c b/tests/libgit2/threads/thread_helpers.c
new file mode 100644
index 0000000..54bf609
--- /dev/null
+++ b/tests/libgit2/threads/thread_helpers.c
@@ -0,0 +1,44 @@
+#include "clar_libgit2.h"
+#include "thread_helpers.h"
+
+void run_in_parallel(
+ int repeats,
+ int threads,
+ void *(*func)(void *),
+ void (*before_test)(void),
+ void (*after_test)(void))
+{
+ int r, t, *id = git__calloc(threads, sizeof(int));
+#ifdef GIT_THREADS
+ git_thread *th = git__calloc(threads, sizeof(git_thread));
+ cl_assert(th != NULL);
+#else
+ void *th = NULL;
+#endif
+
+ cl_assert(id != NULL);
+
+ for (r = 0; r < repeats; ++r) {
+ if (before_test) before_test();
+
+ for (t = 0; t < threads; ++t) {
+ id[t] = t;
+#ifdef GIT_THREADS
+ cl_git_pass(git_thread_create(&th[t], func, &id[t]));
+#else
+ cl_assert(func(&id[t]) == &id[t]);
+#endif
+ }
+
+#ifdef GIT_THREADS
+ for (t = 0; t < threads; ++t)
+ cl_git_pass(git_thread_join(&th[t], NULL));
+ memset(th, 0, threads * sizeof(git_thread));
+#endif
+
+ if (after_test) after_test();
+ }
+
+ git__free(id);
+ git__free(th);
+}
diff --git a/tests/libgit2/threads/thread_helpers.h b/tests/libgit2/threads/thread_helpers.h
new file mode 100644
index 0000000..0f23a4c
--- /dev/null
+++ b/tests/libgit2/threads/thread_helpers.h
@@ -0,0 +1,8 @@
+#include "thread.h"
+
+void run_in_parallel(
+ int repeats,
+ int threads,
+ void *(*func)(void *),
+ void (*before_test)(void),
+ void (*after_test)(void));
diff --git a/tests/libgit2/threads/tlsdata.c b/tests/libgit2/threads/tlsdata.c
new file mode 100644
index 0000000..7c69b44
--- /dev/null
+++ b/tests/libgit2/threads/tlsdata.c
@@ -0,0 +1,65 @@
+#include "clar_libgit2.h"
+
+#include "thread_helpers.h"
+
+void test_threads_tlsdata__can_set_and_get(void)
+{
+ git_tlsdata_key key_one, key_two, key_three;
+
+ cl_git_pass(git_tlsdata_init(&key_one, NULL));
+ cl_git_pass(git_tlsdata_init(&key_two, NULL));
+ cl_git_pass(git_tlsdata_init(&key_three, NULL));
+
+ cl_git_pass(git_tlsdata_set(key_one, (void *)(size_t)42424242));
+ cl_git_pass(git_tlsdata_set(key_two, (void *)(size_t)0xdeadbeef));
+ cl_git_pass(git_tlsdata_set(key_three, (void *)(size_t)98761234));
+
+ cl_assert_equal_sz((size_t)42424242, git_tlsdata_get(key_one));
+ cl_assert_equal_sz((size_t)0xdeadbeef, git_tlsdata_get(key_two));
+ cl_assert_equal_sz((size_t)98761234, git_tlsdata_get(key_three));
+
+ cl_git_pass(git_tlsdata_dispose(key_one));
+ cl_git_pass(git_tlsdata_dispose(key_two));
+ cl_git_pass(git_tlsdata_dispose(key_three));
+}
+
+#ifdef GIT_THREADS
+
+static void *set_and_get(void *param)
+{
+ git_tlsdata_key *tlsdata_key = (git_tlsdata_key *)param;
+ int val;
+
+ if (git_tlsdata_set(*tlsdata_key, &val) != 0 ||
+ git_tlsdata_get(*tlsdata_key) != &val)
+ return (void *)0;
+
+ return (void *)1;
+}
+
+#endif
+
+#define THREAD_COUNT 10
+
+void test_threads_tlsdata__threads(void)
+{
+#ifdef GIT_THREADS
+ git_thread thread[THREAD_COUNT];
+ git_tlsdata_key tlsdata;
+ int i;
+
+ cl_git_pass(git_tlsdata_init(&tlsdata, NULL));
+
+ for (i = 0; i < THREAD_COUNT; i++)
+ cl_git_pass(git_thread_create(&thread[i], set_and_get, &tlsdata));
+
+ for (i = 0; i < THREAD_COUNT; i++) {
+ void *result;
+
+ cl_git_pass(git_thread_join(&thread[i], &result));
+ cl_assert_equal_sz(1, (size_t)result);
+ }
+
+ cl_git_pass(git_tlsdata_dispose(tlsdata));
+#endif
+}
diff --git a/tests/libgit2/trace/trace.c b/tests/libgit2/trace/trace.c
new file mode 100644
index 0000000..097208b
--- /dev/null
+++ b/tests/libgit2/trace/trace.c
@@ -0,0 +1,106 @@
+#include "clar_libgit2.h"
+#include "clar_libgit2_trace.h"
+#include "trace.h"
+
+static int written = 0;
+
+static void trace_callback(git_trace_level_t level, const char *message)
+{
+ GIT_UNUSED(level);
+
+ cl_assert(strcmp(message, "Hello world!") == 0);
+
+ written = 1;
+}
+
+void test_trace_trace__initialize(void)
+{
+ /* If global tracing is enabled, disable for the duration of this test. */
+ cl_global_trace_disable();
+
+ git_trace_set(GIT_TRACE_INFO, trace_callback);
+ written = 0;
+}
+
+void test_trace_trace__cleanup(void)
+{
+ git_trace_set(GIT_TRACE_NONE, NULL);
+
+ /* If global tracing was enabled, restart it. */
+ cl_global_trace_register();
+}
+
+void test_trace_trace__sets(void)
+{
+#ifdef GIT_TRACE
+ cl_assert(git_trace_level() == GIT_TRACE_INFO);
+#else
+ cl_skip();
+#endif
+}
+
+void test_trace_trace__can_reset(void)
+{
+#ifdef GIT_TRACE
+ cl_assert(git_trace_level() == GIT_TRACE_INFO);
+ cl_git_pass(git_trace_set(GIT_TRACE_ERROR, trace_callback));
+
+ cl_assert(written == 0);
+ git_trace(GIT_TRACE_INFO, "Hello %s!", "world");
+ cl_assert(written == 0);
+
+ git_trace(GIT_TRACE_ERROR, "Hello %s!", "world");
+ cl_assert(written == 1);
+#else
+ cl_skip();
+#endif
+}
+
+void test_trace_trace__can_unset(void)
+{
+#ifdef GIT_TRACE
+ cl_assert(git_trace_level() == GIT_TRACE_INFO);
+ cl_git_pass(git_trace_set(GIT_TRACE_NONE, NULL));
+
+ cl_assert(git_trace_level() == GIT_TRACE_NONE);
+
+ cl_assert(written == 0);
+ git_trace(GIT_TRACE_FATAL, "Hello %s!", "world");
+ cl_assert(written == 0);
+#else
+ cl_skip();
+#endif
+}
+
+void test_trace_trace__skips_higher_level(void)
+{
+#ifdef GIT_TRACE
+ cl_assert(written == 0);
+ git_trace(GIT_TRACE_DEBUG, "Hello %s!", "world");
+ cl_assert(written == 0);
+#else
+ cl_skip();
+#endif
+}
+
+void test_trace_trace__writes(void)
+{
+#ifdef GIT_TRACE
+ cl_assert(written == 0);
+ git_trace(GIT_TRACE_INFO, "Hello %s!", "world");
+ cl_assert(written == 1);
+#else
+ cl_skip();
+#endif
+}
+
+void test_trace_trace__writes_lower_level(void)
+{
+#ifdef GIT_TRACE
+ cl_assert(written == 0);
+ git_trace(GIT_TRACE_ERROR, "Hello %s!", "world");
+ cl_assert(written == 1);
+#else
+ cl_skip();
+#endif
+}
diff --git a/tests/libgit2/trace/windows/stacktrace.c b/tests/libgit2/trace/windows/stacktrace.c
new file mode 100644
index 0000000..0a77ef9
--- /dev/null
+++ b/tests/libgit2/trace/windows/stacktrace.c
@@ -0,0 +1,152 @@
+#include "clar_libgit2.h"
+#include "win32/w32_leakcheck.h"
+
+#if defined(GIT_WIN32_LEAKCHECK)
+static void a(void)
+{
+ char buf[10000];
+
+ cl_assert(git_win32_leakcheck_stack(buf, sizeof(buf), 0, NULL, NULL) == 0);
+
+#if 0
+ fprintf(stderr, "Stacktrace from [%s:%d]:\n%s\n", __FILE__, __LINE__, buf);
+#endif
+}
+
+static void b(void)
+{
+ a();
+}
+
+static void c(void)
+{
+ b();
+}
+#endif
+
+void test_trace_windows_stacktrace__basic(void)
+{
+#if defined(GIT_WIN32_LEAKCHECK)
+ c();
+#endif
+}
+
+
+void test_trace_windows_stacktrace__leaks(void)
+{
+#if defined(GIT_WIN32_LEAKCHECK)
+ void * p1;
+ void * p2;
+ void * p3;
+ void * p4;
+ int before, after;
+ int leaks;
+ int error;
+
+ /* remember outstanding leaks due to set setup
+ * and set mark/checkpoint.
+ */
+ before = git_win32_leakcheck_stacktrace_dump(
+ GIT_WIN32_LEAKCHECK_STACKTRACE_QUIET |
+ GIT_WIN32_LEAKCHECK_STACKTRACE_LEAKS_TOTAL |
+ GIT_WIN32_LEAKCHECK_STACKTRACE_SET_MARK,
+ NULL);
+
+ p1 = git__malloc(5);
+ leaks = git_win32_leakcheck_stacktrace_dump(
+ GIT_WIN32_LEAKCHECK_STACKTRACE_QUIET |
+ GIT_WIN32_LEAKCHECK_STACKTRACE_LEAKS_SINCE_MARK,
+ "p1");
+ cl_assert_equal_i(1, leaks);
+
+ p2 = git__malloc(5);
+ leaks = git_win32_leakcheck_stacktrace_dump(
+ GIT_WIN32_LEAKCHECK_STACKTRACE_QUIET |
+ GIT_WIN32_LEAKCHECK_STACKTRACE_LEAKS_SINCE_MARK,
+ "p1,p2");
+ cl_assert_equal_i(2, leaks);
+
+ p3 = git__malloc(5);
+ leaks = git_win32_leakcheck_stacktrace_dump(
+ GIT_WIN32_LEAKCHECK_STACKTRACE_QUIET |
+ GIT_WIN32_LEAKCHECK_STACKTRACE_LEAKS_SINCE_MARK,
+ "p1,p2,p3");
+ cl_assert_equal_i(3, leaks);
+
+ git__free(p2);
+ leaks = git_win32_leakcheck_stacktrace_dump(
+ GIT_WIN32_LEAKCHECK_STACKTRACE_QUIET |
+ GIT_WIN32_LEAKCHECK_STACKTRACE_LEAKS_SINCE_MARK,
+ "p1,p3");
+ cl_assert_equal_i(2, leaks);
+
+ /* move the mark. only new leaks should appear afterwards */
+ error = git_win32_leakcheck_stacktrace_dump(
+ GIT_WIN32_LEAKCHECK_STACKTRACE_SET_MARK,
+ NULL);
+ /* cannot use cl_git_pass() since that may allocate memory. */
+ cl_assert_equal_i(0, error);
+
+ leaks = git_win32_leakcheck_stacktrace_dump(
+ GIT_WIN32_LEAKCHECK_STACKTRACE_QUIET |
+ GIT_WIN32_LEAKCHECK_STACKTRACE_LEAKS_SINCE_MARK,
+ "not_p1,not_p3");
+ cl_assert_equal_i(0, leaks);
+
+ p4 = git__malloc(5);
+ leaks = git_win32_leakcheck_stacktrace_dump(
+ GIT_WIN32_LEAKCHECK_STACKTRACE_QUIET |
+ GIT_WIN32_LEAKCHECK_STACKTRACE_LEAKS_SINCE_MARK,
+ "p4,not_p1,not_p3");
+ cl_assert_equal_i(1, leaks);
+
+ git__free(p1);
+ git__free(p3);
+ leaks = git_win32_leakcheck_stacktrace_dump(
+ GIT_WIN32_LEAKCHECK_STACKTRACE_QUIET |
+ GIT_WIN32_LEAKCHECK_STACKTRACE_LEAKS_SINCE_MARK,
+ "p4");
+ cl_assert_equal_i(1, leaks);
+
+ git__free(p4);
+ leaks = git_win32_leakcheck_stacktrace_dump(
+ GIT_WIN32_LEAKCHECK_STACKTRACE_QUIET |
+ GIT_WIN32_LEAKCHECK_STACKTRACE_LEAKS_SINCE_MARK,
+ "end");
+ cl_assert_equal_i(0, leaks);
+
+ /* confirm current absolute leaks count matches beginning value. */
+ after = git_win32_leakcheck_stacktrace_dump(
+ GIT_WIN32_LEAKCHECK_STACKTRACE_QUIET |
+ GIT_WIN32_LEAKCHECK_STACKTRACE_LEAKS_TOTAL,
+ "total");
+ cl_assert_equal_i(before, after);
+#endif
+}
+
+#if defined(GIT_WIN32_LEAKCHECK)
+static void aux_cb_alloc__1(unsigned int *aux_id)
+{
+ static unsigned int aux_counter = 0;
+
+ *aux_id = aux_counter++;
+}
+
+static void aux_cb_lookup__1(unsigned int aux_id, char *aux_msg, size_t aux_msg_len)
+{
+ p_snprintf(aux_msg, aux_msg_len, "\tQQ%08x\n", aux_id);
+}
+
+#endif
+
+void test_trace_windows_stacktrace__aux1(void)
+{
+#if defined(GIT_WIN32_LEAKCHECK)
+ git_win32_leakcheck_stack_set_aux_cb(aux_cb_alloc__1, aux_cb_lookup__1);
+ c();
+ c();
+ c();
+ c();
+ git_win32_leakcheck_stack_set_aux_cb(NULL, NULL);
+#endif
+}
diff --git a/tests/libgit2/transport/register.c b/tests/libgit2/transport/register.c
new file mode 100644
index 0000000..4d3c097
--- /dev/null
+++ b/tests/libgit2/transport/register.c
@@ -0,0 +1,224 @@
+#include "clar_libgit2.h"
+#include "git2/sys/remote.h"
+#include "git2/sys/transport.h"
+
+static const char *proxy_url = "https://proxy";
+static git_transport _transport = GIT_TRANSPORT_INIT;
+
+static int dummy_transport(git_transport **transport, git_remote *owner, void *param)
+{
+ *transport = &_transport;
+ GIT_UNUSED(owner);
+ GIT_UNUSED(param);
+ return 0;
+}
+
+void test_transport_register__custom_transport(void)
+{
+ git_transport *transport;
+
+ cl_git_pass(git_transport_register("something", dummy_transport, NULL));
+
+ cl_git_pass(git_transport_new(&transport, NULL, "something://somepath"));
+
+ cl_assert(transport == &_transport);
+
+ cl_git_pass(git_transport_unregister("something"));
+}
+
+void test_transport_register__custom_transport_error_doubleregister(void)
+{
+ cl_git_pass(git_transport_register("something", dummy_transport, NULL));
+
+ cl_git_fail_with(git_transport_register("something", dummy_transport, NULL), GIT_EEXISTS);
+
+ cl_git_pass(git_transport_unregister("something"));
+}
+
+void test_transport_register__custom_transport_error_remove_non_existing(void)
+{
+ cl_git_fail_with(git_transport_unregister("something"), GIT_ENOTFOUND);
+}
+
+void test_transport_register__custom_transport_ssh(void)
+{
+ const char *urls[] = {
+ "ssh://somehost:somepath",
+ "ssh+git://somehost:somepath",
+ "git+ssh://somehost:somepath",
+ "git@somehost:somepath",
+ "ssh://somehost:somepath%20with%20%spaces",
+ "ssh://somehost:somepath with spaces"
+ };
+ git_transport *transport;
+ unsigned i;
+
+ for (i = 0; i < ARRAY_SIZE(urls); i++) {
+#ifndef GIT_SSH
+ cl_git_fail_with(git_transport_new(&transport, NULL, urls[i]), -1);
+#else
+ cl_git_pass(git_transport_new(&transport, NULL, urls[i]));
+ transport->free(transport);
+#endif
+ }
+
+ cl_git_pass(git_transport_register("ssh", dummy_transport, NULL));
+
+ cl_git_pass(git_transport_new(&transport, NULL, "git@somehost:somepath"));
+
+ cl_assert(transport == &_transport);
+
+ cl_git_pass(git_transport_unregister("ssh"));
+
+ for (i = 0; i < ARRAY_SIZE(urls); i++) {
+#ifndef GIT_SSH
+ cl_git_fail_with(git_transport_new(&transport, NULL, urls[i]), -1);
+#else
+ cl_git_pass(git_transport_new(&transport, NULL, urls[i]));
+ transport->free(transport);
+#endif
+ }
+}
+
+static int custom_subtransport_stream__read(
+ git_smart_subtransport_stream *stream,
+ char *buffer,
+ size_t buf_size,
+ size_t *bytes_read)
+{
+ GIT_UNUSED(stream);
+ GIT_UNUSED(buffer);
+ GIT_UNUSED(buf_size);
+
+ *bytes_read = 0;
+
+ git_error_set_str(42, "unimplemented");
+ return GIT_EUSER;
+}
+
+static int custom_subtransport_stream__write(
+ git_smart_subtransport_stream *stream,
+ const char *buffer,
+ size_t len)
+{
+ GIT_UNUSED(stream);
+ GIT_UNUSED(buffer);
+ GIT_UNUSED(len);
+
+ git_error_set_str(42, "unimplemented");
+ return GIT_EUSER;
+}
+
+static void custom_subtransport_stream__free(
+ git_smart_subtransport_stream *stream)
+{
+ git__free(stream);
+}
+
+struct custom_subtransport {
+ git_smart_subtransport subtransport;
+ git_transport *owner;
+ int *called;
+};
+
+static int custom_subtransport__action(
+ git_smart_subtransport_stream **out,
+ git_smart_subtransport *transport,
+ const char *url,
+ git_smart_service_t action)
+{
+ struct custom_subtransport *t = (struct custom_subtransport *)transport;
+ git_remote_connect_options opts = GIT_REMOTE_CONNECT_OPTIONS_INIT;
+ int ret;
+
+ GIT_UNUSED(url);
+ GIT_UNUSED(action);
+
+ ret = git_transport_remote_connect_options(&opts, t->owner);
+
+ /* increase the counter once if this function was called at all and once more if the URL matches. */
+ (*t->called)++;
+ if (strcmp(proxy_url, opts.proxy_opts.url) == 0)
+ (*t->called)++;
+
+ git_remote_connect_options_dispose(&opts);
+
+ *out = git__calloc(1, sizeof(git_smart_subtransport_stream));
+ (*out)->subtransport = transport;
+ (*out)->read = custom_subtransport_stream__read;
+ (*out)->write = custom_subtransport_stream__write;
+ (*out)->free = custom_subtransport_stream__free;
+
+ return ret;
+}
+
+static int custom_subtransport__close(git_smart_subtransport *transport)
+{
+ GIT_UNUSED(transport);
+
+ return 0;
+}
+
+static void custom_subtransport__free(git_smart_subtransport *transport)
+{
+ GIT_UNUSED(transport);
+
+ git__free(transport);
+}
+
+static int custom_transport_callback(git_smart_subtransport **out, git_transport *owner, void *param)
+{
+ struct custom_subtransport *subtransport = git__calloc(1, sizeof(struct custom_subtransport));
+ subtransport->called = (int *)param;
+ subtransport->owner = owner;
+ subtransport->subtransport.action = custom_subtransport__action;
+ subtransport->subtransport.close = custom_subtransport__close;
+ subtransport->subtransport.free = custom_subtransport__free;
+
+ *out = &subtransport->subtransport;
+
+ return 0;
+}
+
+
+static int custom_transport(git_transport **out, git_remote *owner, void *param)
+{
+ struct git_smart_subtransport_definition definition;
+ definition.callback = custom_transport_callback;
+ definition.rpc = false;
+ definition.param = param;
+ return git_transport_smart(out, owner, &definition);
+}
+
+void test_transport_register__custom_transport_callbacks(void)
+{
+ git_transport *transport;
+ int called = 0;
+ const char *url = "custom://somepath";
+ git_remote_connect_options opts = GIT_REMOTE_CONNECT_OPTIONS_INIT;
+ git_repository *repo;
+ git_remote *remote;
+
+ cl_git_pass(git_repository_init(&repo, "./transport", 0));
+ cl_git_pass(git_remote_create(&remote, repo, "test",
+ cl_fixture("testrepo.git")));
+
+ cl_git_pass(git_transport_register("custom", custom_transport, &called));
+
+ cl_git_pass(git_transport_new(&transport, remote, url));
+
+ opts.follow_redirects = GIT_REMOTE_REDIRECT_NONE;
+ opts.proxy_opts.url = proxy_url;
+
+ /* This is expected to fail, since the subtransport_stream is not implemented */
+ transport->connect(transport, url, GIT_SERVICE_UPLOADPACK_LS, &opts);
+ /* the counter is increased twice if everything goes as planned */
+ cl_assert_equal_i(2, called);
+ cl_git_pass(transport->close(transport));
+ transport->free(transport);
+
+ cl_git_pass(git_transport_unregister("custom"));
+ git_remote_free(remote);
+ git_repository_free(repo);
+ cl_fixture_cleanup("testrepo.git");
+}
diff --git a/tests/libgit2/transports/smart/packet.c b/tests/libgit2/transports/smart/packet.c
new file mode 100644
index 0000000..a775a4c
--- /dev/null
+++ b/tests/libgit2/transports/smart/packet.c
@@ -0,0 +1,353 @@
+#include "clar_libgit2.h"
+#include "transports/smart.h"
+
+enum expected_status {
+ PARSE_SUCCESS,
+ PARSE_FAILURE
+};
+
+static void assert_flush_parses(const char *line)
+{
+ size_t linelen = strlen(line) + 1;
+ const char *endptr;
+ git_pkt *pkt;
+ git_pkt_parse_data pkt_parse_data = { 0 };
+
+ cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen, &pkt_parse_data));
+ cl_assert_equal_i(pkt->type, GIT_PKT_FLUSH);
+ cl_assert_equal_strn(endptr, line + 4, linelen - 4);
+
+ git_pkt_free((git_pkt *) pkt);
+}
+
+static void assert_data_pkt_parses(const char *line, const char *expected_data, size_t expected_len)
+{
+ size_t linelen = strlen(line) + 1;
+ const char *endptr;
+ git_pkt_data *pkt;
+ git_pkt_parse_data pkt_parse_data = { 1, GIT_OID_SHA1 };
+
+ cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen, &pkt_parse_data));
+ cl_assert_equal_i(pkt->type, GIT_PKT_DATA);
+ cl_assert_equal_i(pkt->len, expected_len);
+ cl_assert_equal_strn(pkt->data, expected_data, expected_len);
+
+ git_pkt_free((git_pkt *) pkt);
+}
+
+static void assert_sideband_progress_parses(const char *line, const char *expected_data, size_t expected_len)
+{
+ size_t linelen = strlen(line) + 1;
+ const char *endptr;
+ git_pkt_progress *pkt;
+ git_pkt_parse_data pkt_parse_data = { 0 };
+
+ cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen, &pkt_parse_data));
+ cl_assert_equal_i(pkt->type, GIT_PKT_PROGRESS);
+ cl_assert_equal_i(pkt->len, expected_len);
+ cl_assert_equal_strn(pkt->data, expected_data, expected_len);
+
+ git_pkt_free((git_pkt *) pkt);
+}
+
+static void assert_error_parses(const char *line, const char *expected_error, size_t expected_len)
+{
+ size_t linelen = strlen(line) + 1;
+ const char *endptr;
+ git_pkt_err *pkt;
+ git_pkt_parse_data pkt_parse_data = { 0 };
+
+ cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen, &pkt_parse_data));
+ cl_assert_equal_i(pkt->type, GIT_PKT_ERR);
+ cl_assert_equal_i(pkt->len, expected_len);
+ cl_assert_equal_strn(pkt->error, expected_error, expected_len);
+
+ git_pkt_free((git_pkt *) pkt);
+}
+
+static void assert_ack_parses(const char *line, const char *expected_oid, enum git_ack_status expected_status)
+{
+ size_t linelen = strlen(line) + 1;
+ const char *endptr;
+ git_pkt_ack *pkt;
+ git_oid oid;
+ git_pkt_parse_data pkt_parse_data = { 1, GIT_OID_SHA1 };
+
+ cl_git_pass(git_oid__fromstr(&oid, expected_oid, GIT_OID_SHA1));
+
+ cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen, &pkt_parse_data));
+ cl_assert_equal_i(pkt->type, GIT_PKT_ACK);
+ cl_assert_equal_oid(&pkt->oid, &oid);
+ cl_assert_equal_i(pkt->status, expected_status);
+
+ git_pkt_free((git_pkt *) pkt);
+}
+
+static void assert_nak_parses(const char *line)
+{
+ size_t linelen = strlen(line) + 1;
+ const char *endptr;
+ git_pkt *pkt;
+ git_pkt_parse_data pkt_parse_data = { 0 };
+
+ cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen, &pkt_parse_data));
+ cl_assert_equal_i(pkt->type, GIT_PKT_NAK);
+ cl_assert_equal_strn(endptr, line + 7, linelen - 7);
+
+ git_pkt_free((git_pkt *) pkt);
+}
+
+static void assert_comment_parses(const char *line, const char *expected_comment)
+{
+ size_t linelen = strlen(line) + 1;
+ const char *endptr;
+ git_pkt_comment *pkt;
+ git_pkt_parse_data pkt_parse_data = { 0 };
+
+ cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen, &pkt_parse_data));
+ cl_assert_equal_i(pkt->type, GIT_PKT_COMMENT);
+ cl_assert_equal_strn(pkt->comment, expected_comment, strlen(expected_comment));
+
+ git_pkt_free((git_pkt *) pkt);
+}
+
+static void assert_ok_parses(const char *line, const char *expected_ref)
+{
+ size_t linelen = strlen(line) + 1;
+ const char *endptr;
+ git_pkt_ok *pkt;
+ git_pkt_parse_data pkt_parse_data = { 0 };
+
+ cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen, &pkt_parse_data));
+ cl_assert_equal_i(pkt->type, GIT_PKT_OK);
+ cl_assert_equal_strn(pkt->ref, expected_ref, strlen(expected_ref));
+
+ git_pkt_free((git_pkt *) pkt);
+}
+
+static void assert_unpack_parses(const char *line, bool ok)
+{
+ size_t linelen = strlen(line) + 1;
+ const char *endptr;
+ git_pkt_unpack *pkt;
+ git_pkt_parse_data pkt_parse_data = { 0 };
+
+ cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen, &pkt_parse_data));
+ cl_assert_equal_i(pkt->type, GIT_PKT_UNPACK);
+ cl_assert_equal_i(pkt->unpack_ok, ok);
+
+ git_pkt_free((git_pkt *) pkt);
+}
+
+static void assert_ng_parses(const char *line, const char *expected_ref, const char *expected_msg)
+{
+ size_t linelen = strlen(line) + 1;
+ const char *endptr;
+ git_pkt_ng *pkt;
+ git_pkt_parse_data pkt_parse_data = { 0 };
+
+ cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen, &pkt_parse_data));
+ cl_assert_equal_i(pkt->type, GIT_PKT_NG);
+ cl_assert_equal_strn(pkt->ref, expected_ref, strlen(expected_ref));
+ cl_assert_equal_strn(pkt->msg, expected_msg, strlen(expected_msg));
+
+ git_pkt_free((git_pkt *) pkt);
+}
+
+#define assert_ref_parses(line, expected_oid, expected_ref, expected_capabilities) \
+ assert_ref_parses_(line, sizeof(line), expected_oid, expected_ref, expected_capabilities)
+
+static void assert_ref_parses_(const char *line, size_t linelen, const char *expected_oid,
+ const char *expected_ref, const char *expected_capabilities)
+{
+ const char *endptr;
+ git_pkt_ref *pkt;
+ git_oid oid;
+ git_pkt_parse_data pkt_parse_data = { 0 };
+
+ cl_git_pass(git_oid__fromstr(&oid, expected_oid, GIT_OID_SHA1));
+
+ cl_git_pass(git_pkt_parse_line((git_pkt **) &pkt, &endptr, line, linelen, &pkt_parse_data));
+ cl_assert_equal_i(pkt->type, GIT_PKT_REF);
+ cl_assert_equal_oid(&pkt->head.oid, &oid);
+ cl_assert_equal_strn(pkt->head.name, expected_ref, strlen(expected_ref));
+ if (expected_capabilities)
+ cl_assert_equal_strn(pkt->capabilities, expected_capabilities, strlen(expected_capabilities));
+ else
+ cl_assert_equal_p(NULL, pkt->capabilities);
+
+ git_pkt_free((git_pkt *) pkt);
+}
+
+static void assert_pkt_fails(const char *line)
+{
+ const char *endptr;
+ git_pkt_parse_data pkt_parse_data = { 0 };
+
+ git_pkt *pkt;
+ cl_git_fail(git_pkt_parse_line(&pkt, &endptr, line, strlen(line) + 1, &pkt_parse_data));
+}
+
+void test_transports_smart_packet__parsing_garbage_fails(void)
+{
+ assert_pkt_fails("0foobar");
+ assert_pkt_fails("00foobar");
+ assert_pkt_fails("000foobar");
+ assert_pkt_fails("0001");
+ assert_pkt_fails("");
+ assert_pkt_fails("0");
+ assert_pkt_fails("0i00");
+ assert_pkt_fails("f");
+}
+
+void test_transports_smart_packet__flush_parses(void)
+{
+ assert_flush_parses("0000");
+ assert_flush_parses("0000foobar");
+}
+
+void test_transports_smart_packet__data_pkt(void)
+{
+ assert_pkt_fails("000foobar");
+ assert_pkt_fails("0001o");
+ assert_pkt_fails("0001\1");
+ assert_data_pkt_parses("0005\1", "", 0);
+ assert_pkt_fails("0009\1o");
+ assert_data_pkt_parses("0009\1data", "data", 4);
+ assert_data_pkt_parses("000a\1data", "data", 5);
+}
+
+void test_transports_smart_packet__sideband_progress_pkt(void)
+{
+ assert_pkt_fails("0001\2");
+ assert_sideband_progress_parses("0005\2", "", 0);
+ assert_pkt_fails("0009\2o");
+ assert_sideband_progress_parses("0009\2data", "data", 4);
+ assert_sideband_progress_parses("000a\2data", "data", 5);
+}
+
+void test_transports_smart_packet__sideband_err_pkt(void)
+{
+ assert_pkt_fails("0001\3");
+ assert_error_parses("0005\3", "", 0);
+ assert_pkt_fails("0009\3o");
+ assert_error_parses("0009\3data", "data", 4);
+ assert_error_parses("000a\3data", "data", 5);
+}
+
+void test_transports_smart_packet__ack_pkt(void)
+{
+ assert_ack_parses("0030ACK 0000000000000000000000000000000000000000",
+ "0000000000000000000000000000000000000000", 0);
+ assert_ack_parses("0039ACK 0000000000000000000000000000000000000000 continue",
+ "0000000000000000000000000000000000000000",
+ GIT_ACK_CONTINUE);
+ assert_ack_parses("0037ACK 0000000000000000000000000000000000000000 common",
+ "0000000000000000000000000000000000000000",
+ GIT_ACK_COMMON);
+ assert_ack_parses("0037ACK 0000000000000000000000000000000000000000 ready",
+ "0000000000000000000000000000000000000000",
+ GIT_ACK_READY);
+
+ /* these should fail as they don't have OIDs */
+ assert_pkt_fails("0007ACK");
+ assert_pkt_fails("0008ACK ");
+
+ /* this one is missing a space and should thus fail */
+ assert_pkt_fails("0036ACK00000000000000000x0000000000000000000000 ready");
+
+ /* the following ones have invalid OIDs and should thus fail */
+ assert_pkt_fails("0037ACK 00000000000000000x0000000000000000000000 ready");
+ assert_pkt_fails("0036ACK 000000000000000000000000000000000000000 ready");
+ assert_pkt_fails("0036ACK 00000000000000000x0000000000000000000000ready");
+
+ /* this one has an invalid status and should thus fail */
+ assert_pkt_fails("0036ACK 0000000000000000000000000000000000000000 read");
+}
+
+void test_transports_smart_packet__nak_pkt(void)
+{
+ assert_nak_parses("0007NAK");
+ assert_pkt_fails("0007NaK");
+ assert_pkt_fails("0007nak");
+ assert_nak_parses("0007NAKfoobar");
+ assert_pkt_fails("0007nakfoobar");
+ assert_pkt_fails("0007 NAK");
+}
+
+void test_transports_smart_packet__error_pkt(void)
+{
+ assert_pkt_fails("0007ERR");
+ assert_pkt_fails("0008ERRx");
+ assert_error_parses("0008ERR ", "", 0);
+ assert_error_parses("000EERR ERRMSG", "ERRMSG", 6);
+}
+
+void test_transports_smart_packet__comment_pkt(void)
+{
+ assert_comment_parses("0005#", "");
+ assert_comment_parses("000B#foobar", "#fooba");
+ assert_comment_parses("000C#foobar", "#foobar");
+ assert_comment_parses("001A#this is a comment\nfoo", "#this is a comment\nfoo");
+}
+
+void test_transports_smart_packet__ok_pkt(void)
+{
+ assert_pkt_fails("0007ok\n");
+ assert_ok_parses("0007ok ", "");
+ assert_ok_parses("0008ok \n", "");
+ assert_ok_parses("0008ok x", "x");
+ assert_ok_parses("0009ok x\n", "x");
+ assert_pkt_fails("001OK ref/foo/bar");
+ assert_ok_parses("0012ok ref/foo/bar", "ref/foo/bar");
+ assert_pkt_fails("0013OK ref/foo/bar\n");
+ assert_ok_parses("0013ok ref/foo/bar\n", "ref/foo/bar");
+}
+
+void test_transports_smart_packet__ng_pkt(void)
+{
+ /* TODO: same as for ok pkt */
+ assert_pkt_fails("0007ng\n");
+ assert_pkt_fails("0008ng \n");
+ assert_pkt_fails("000Bng ref\n");
+ assert_pkt_fails("000Bng ref\n");
+ /* TODO: is this a valid packet line? Probably not. */
+ assert_ng_parses("000Ang x\n", "", "x");
+ assert_ng_parses("000Fng ref msg\n", "ref", "msg");
+ assert_ng_parses("000Fng ref msg\n", "ref", "msg");
+}
+
+void test_transports_smart_packet__unpack_pkt(void)
+{
+ assert_unpack_parses("000Dunpack ok", 1);
+ assert_unpack_parses("000Dunpack ng error-msg", 0);
+ /* TODO: the following tests should fail */
+ assert_unpack_parses("000Aunpack", 0);
+ assert_unpack_parses("0011unpack foobar", 0);
+ assert_unpack_parses("0010unpack ng ok", 0);
+ assert_unpack_parses("0010unpack okfoo", 1);
+}
+
+void test_transports_smart_packet__ref_pkt(void)
+{
+ assert_pkt_fails("002C0000000000000000000000000000000000000000");
+ assert_pkt_fails("002D0000000000000000000000000000000000000000\n");
+ assert_pkt_fails("00300000000000000000000000000000000000000000HEAD");
+ assert_pkt_fails("004800000000x0000000000000000000000000000000 refs/heads/master\0multi_ack");
+ assert_ref_parses(
+ "003F0000000000000000000000000000000000000000 refs/heads/master\0",
+ "0000000000000000000000000000000000000000", "refs/heads/master", "");
+ assert_ref_parses(
+ "00480000000000000000000000000000000000000000 refs/heads/master\0multi_ack",
+ "0000000000000000000000000000000000000000", "refs/heads/master", "multi_ack");
+ assert_ref_parses(
+ "00460000000000000000000000000000000000000000 refs/heads/master\0one two",
+ "0000000000000000000000000000000000000000", "refs/heads/master", "one two");
+ assert_ref_parses(
+ "00310000000000000000000000000000000000000000 HEAD",
+ "0000000000000000000000000000000000000000", "HEAD", NULL);
+ assert_pkt_fails("0031000000000000000000000000000000000000000 HEAD");
+ assert_ref_parses(
+ "00360000000000000000000000000000000000000000 HEAD HEAD",
+ "0000000000000000000000000000000000000000", "HEAD HEAD", NULL);
+}
diff --git a/tests/libgit2/valgrind-supp-mac.txt b/tests/libgit2/valgrind-supp-mac.txt
new file mode 100644
index 0000000..3298abe
--- /dev/null
+++ b/tests/libgit2/valgrind-supp-mac.txt
@@ -0,0 +1,176 @@
+{
+ libgit2-git-error-set-buffer
+ Memcheck:Leak
+ ...
+ fun:git__realloc
+ fun:git_str_try_grow
+ fun:git_str_grow
+ fun:git_str_vprintf
+ fun:git_error_set
+}
+{
+ mac-setenv-leak-1
+ Memcheck:Leak
+ fun:malloc_zone_malloc
+ fun:__setenv
+ fun:setenv
+}
+{
+ mac-setenv-leak-2
+ Memcheck:Leak
+ fun:malloc_zone_malloc
+ fun:malloc_set_zone_name
+ ...
+ fun:init__zone0
+ fun:setenv
+}
+{
+ mac-dyld-initializer-leak
+ Memcheck:Leak
+ fun:malloc
+ ...
+ fun:dyld_register_image_state_change_handler
+ fun:_dyld_initializer
+}
+{
+ mac-tz-leak-1
+ Memcheck:Leak
+ ...
+ fun:token_table_add
+ fun:notify_register_check
+ fun:notify_register_tz
+}
+{
+ mac-tz-leak-2
+ Memcheck:Leak
+ fun:malloc
+ fun:tzload
+}
+{
+ mac-tz-leak-3
+ Memcheck:Leak
+ fun:malloc
+ fun:tzsetwall_basic
+}
+{
+ mac-tz-leak-4
+ Memcheck:Leak
+ fun:malloc
+ fun:gmtsub
+}
+{
+ mac-system-init-leak-1
+ Memcheck:Leak
+ ...
+ fun:_libxpc_initializer
+ fun:libSystem_initializer
+}
+{
+ mac-system-init-leak-2
+ Memcheck:Leak
+ ...
+ fun:__keymgr_initializer
+ fun:libSystem_initializer
+}
+{
+ mac-puts-leak
+ Memcheck:Leak
+ fun:malloc
+ fun:__smakebuf
+ ...
+ fun:puts
+}
+{
+ mac-ssl-uninitialized-1
+ Memcheck:Cond
+ obj:/usr/lib/libcrypto.0.9.8.dylib
+ ...
+ fun:ssl23_connect
+}
+{
+ mac-ssl-uninitialized-2
+ Memcheck:Cond
+ ...
+ obj:/usr/lib/libssl.0.9.8.dylib
+ ...
+ fun:ssl23_connect
+}
+{
+ mac-ssl-uninitialized-3
+ Memcheck:Value8
+ obj:/usr/lib/libcrypto.0.9.8.dylib
+ ...
+ fun:ssl23_connect
+}
+{
+ mac-ssl-leak-1
+ Memcheck:Leak
+ ...
+ fun:ERR_load_strings
+}
+{
+ mac-ssl-leak-2
+ Memcheck:Leak
+ ...
+ fun:SSL_library_init
+}
+{
+ mac-ssl-leak-3
+ Memcheck:Leak
+ ...
+ fun:si_module_with_name
+ fun:getaddrinfo
+}
+{
+ mac-ssl-leak-4
+ Memcheck:Leak
+ fun:malloc
+ fun:CRYPTO_malloc
+ ...
+ fun:ssl3_get_server_certificate
+}
+{
+ mac-ssl-leak-5
+ Memcheck:Leak
+ fun:malloc
+ fun:CRYPTO_malloc
+ ...
+ fun:ERR_put_error
+}
+{
+ clar-printf-buf
+ Memcheck:Leak
+ fun:malloc
+ fun:__smakebuf
+ ...
+ fun:printf
+ fun:clar_print_init
+}
+{
+ molo-1
+ Memcheck:Leak
+ fun:malloc_zone_malloc
+ ...
+ fun:_objc_init
+}
+{
+ molo-2
+ Memcheck:Leak
+ fun:malloc_zone_calloc
+ ...
+ fun:_objc_init
+}
+{
+ molo-3
+ Memcheck:Leak
+ fun:malloc
+ ...
+ fun:_objc_init
+}
+{
+ molo-4
+ Memcheck:Leak
+ fun:malloc
+ ...
+ fun:dyld_register_image_state_change_handler
+}
diff --git a/tests/libgit2/win32/forbidden.c b/tests/libgit2/win32/forbidden.c
new file mode 100644
index 0000000..c4e82e9
--- /dev/null
+++ b/tests/libgit2/win32/forbidden.c
@@ -0,0 +1,182 @@
+#include "clar_libgit2.h"
+
+#include "repository.h"
+#include "submodule.h"
+
+static const char *repo_name = "win32-forbidden";
+static git_repository *repo;
+
+void test_win32_forbidden__initialize(void)
+{
+ repo = cl_git_sandbox_init(repo_name);
+}
+
+void test_win32_forbidden__cleanup(void)
+{
+ cl_git_sandbox_cleanup();
+}
+
+void test_win32_forbidden__can_open_index(void)
+{
+ git_index *index;
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_assert_equal_i(7, git_index_entrycount(index));
+
+ /* ensure we can even write the unmodified index */
+ cl_git_pass(git_index_write(index));
+
+ git_index_free(index);
+}
+
+void test_win32_forbidden__can_add_forbidden_filename_with_entry(void)
+{
+ git_index *index;
+ git_index_entry entry = {{0}};
+
+ cl_git_pass(git_repository_index(&index, repo));
+
+ entry.path = "aux";
+ entry.mode = GIT_FILEMODE_BLOB;
+ git_oid__fromstr(&entry.id, "da623abd956bb2fd8052c708c7ed43f05d192d37", GIT_OID_SHA1);
+
+ cl_git_pass(git_index_add(index, &entry));
+
+ git_index_free(index);
+}
+
+void test_win32_forbidden__cannot_add_dot_git_even_with_entry(void)
+{
+ git_index *index;
+ git_index_entry entry = {{0}};
+
+ cl_git_pass(git_repository_index(&index, repo));
+
+ entry.path = "foo/.git";
+ entry.mode = GIT_FILEMODE_BLOB;
+ git_oid__fromstr(&entry.id, "da623abd956bb2fd8052c708c7ed43f05d192d37", GIT_OID_SHA1);
+
+ cl_git_fail(git_index_add(index, &entry));
+
+ git_index_free(index);
+}
+
+void test_win32_forbidden__cannot_add_forbidden_filename_from_filesystem(void)
+{
+ git_index *index;
+
+ /* since our function calls are very low-level, we can create `aux.`,
+ * but we should not be able to add it to the index
+ */
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_write2file("win32-forbidden/aux.", "foo\n", 4, O_RDWR | O_CREAT, 0666);
+
+#ifdef GIT_WIN32
+ cl_git_fail(git_index_add_bypath(index, "aux."));
+#else
+ cl_git_pass(git_index_add_bypath(index, "aux."));
+#endif
+
+ cl_must_pass(p_unlink("win32-forbidden/aux."));
+ git_index_free(index);
+}
+
+static int dummy_submodule_cb(
+ git_submodule *sm, const char *name, void *payload)
+{
+ GIT_UNUSED(sm);
+ GIT_UNUSED(name);
+ GIT_UNUSED(payload);
+ return 0;
+}
+
+void test_win32_forbidden__can_diff_tree_to_index(void)
+{
+ git_diff *diff;
+ git_tree *tree;
+
+ cl_git_pass(git_repository_head_tree(&tree, repo));
+ cl_git_pass(git_diff_tree_to_index(&diff, repo, tree, NULL, NULL));
+ cl_assert_equal_i(0, git_diff_num_deltas(diff));
+ git_diff_free(diff);
+ git_tree_free(tree);
+}
+
+void test_win32_forbidden__can_diff_tree_to_tree(void)
+{
+ git_diff *diff;
+ git_tree *tree;
+
+ cl_git_pass(git_repository_head_tree(&tree, repo));
+ cl_git_pass(git_diff_tree_to_tree(&diff, repo, tree, tree, NULL));
+ cl_assert_equal_i(0, git_diff_num_deltas(diff));
+ git_diff_free(diff);
+ git_tree_free(tree);
+}
+
+void test_win32_forbidden__can_diff_index_to_workdir(void)
+{
+ git_index *index;
+ git_diff *diff;
+ const git_diff_delta *delta;
+ git_tree *tree;
+ size_t i;
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_repository_head_tree(&tree, repo));
+ cl_git_pass(git_diff_index_to_workdir(&diff, repo, index, NULL));
+
+ for (i = 0; i < git_diff_num_deltas(diff); i++) {
+ delta = git_diff_get_delta(diff, i);
+ cl_assert_equal_i(GIT_DELTA_DELETED, delta->status);
+ }
+
+ git_diff_free(diff);
+ git_tree_free(tree);
+ git_index_free(index);
+}
+
+void test_win32_forbidden__checking_out_forbidden_index_fails(void)
+{
+#ifdef GIT_WIN32
+ git_index *index;
+ git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
+ git_diff *diff;
+ const git_diff_delta *delta;
+ git_tree *tree;
+ size_t num_deltas, i;
+
+ opts.checkout_strategy = GIT_CHECKOUT_FORCE;
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_fail(git_checkout_index(repo, index, &opts));
+
+ cl_git_pass(git_repository_head_tree(&tree, repo));
+ cl_git_pass(git_diff_index_to_workdir(&diff, repo, index, NULL));
+
+ num_deltas = git_diff_num_deltas(diff);
+
+ cl_assert(num_deltas > 0);
+
+ for (i = 0; i < num_deltas; i++) {
+ delta = git_diff_get_delta(diff, i);
+ cl_assert_equal_i(GIT_DELTA_DELETED, delta->status);
+ }
+
+ git_diff_free(diff);
+ git_tree_free(tree);
+ git_index_free(index);
+#endif
+}
+
+void test_win32_forbidden__can_query_submodules(void)
+{
+ cl_git_pass(git_submodule_foreach(repo, dummy_submodule_cb, NULL));
+}
+
+void test_win32_forbidden__can_blame_file(void)
+{
+ git_blame *blame;
+
+ cl_git_pass(git_blame_file(&blame, repo, "aux", NULL));
+ git_blame_free(blame);
+}
diff --git a/tests/libgit2/win32/longpath.c b/tests/libgit2/win32/longpath.c
new file mode 100644
index 0000000..4be86db
--- /dev/null
+++ b/tests/libgit2/win32/longpath.c
@@ -0,0 +1,130 @@
+#include "clar_libgit2.h"
+
+#include "git2/clone.h"
+#include "clone.h"
+#include "futils.h"
+#include "repository.h"
+
+static git_str path = GIT_STR_INIT;
+
+#define LONG_FILENAME "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.txt"
+
+void test_win32_longpath__initialize(void)
+{
+#ifdef GIT_WIN32
+ const char *base = clar_sandbox_path();
+ size_t base_len = strlen(base);
+ size_t remain = MAX_PATH - base_len;
+ size_t i;
+
+ git_str_clear(&path);
+ git_str_puts(&path, base);
+ git_str_putc(&path, '/');
+
+ cl_assert(remain < (MAX_PATH - 5));
+
+ for (i = 0; i < (remain - 5); i++)
+ git_str_putc(&path, 'a');
+#endif
+}
+
+void test_win32_longpath__cleanup(void)
+{
+ git_str_dispose(&path);
+ cl_git_sandbox_cleanup();
+}
+
+void test_win32_longpath__errmsg_on_checkout(void)
+{
+#ifdef GIT_WIN32
+ git_repository *repo;
+
+ cl_git_fail(git_clone(&repo, cl_fixture("testrepo.git"), path.ptr, NULL));
+ cl_assert(git__prefixcmp(git_error_last()->message, "path too long") == 0);
+#endif
+}
+
+void test_win32_longpath__workdir_path_validated(void)
+{
+#ifdef GIT_WIN32
+ git_repository *repo = cl_git_sandbox_init("testrepo");
+ git_str out = GIT_STR_INIT;
+
+ cl_git_pass(git_repository_workdir_path(&out, repo, "a.txt"));
+
+ /* even if the repo path is a drive letter, this is too long */
+ cl_git_fail(git_repository_workdir_path(&out, repo, LONG_FILENAME));
+ cl_assert(git__prefixcmp(git_error_last()->message, "path too long") == 0);
+
+ cl_repo_set_bool(repo, "core.longpaths", true);
+ cl_git_pass(git_repository_workdir_path(&out, repo, "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.txt"));
+ cl_git_pass(git_repository_workdir_path(&out, repo, LONG_FILENAME));
+ git_str_dispose(&out);
+#endif
+}
+
+#ifdef GIT_WIN32
+static void assert_longpath_status_and_add(git_repository *repo, const char *wddata, const char *repodata) {
+ git_index *index;
+ git_blob *blob;
+ git_str out = GIT_STR_INIT;
+ const git_index_entry *entry;
+ unsigned int status_flags;
+
+ cl_git_pass(git_repository_workdir_path(&out, repo, LONG_FILENAME));
+
+ cl_git_rewritefile(out.ptr, wddata);
+
+ cl_git_pass(git_status_file(&status_flags, repo, LONG_FILENAME));
+ cl_assert_equal_i(GIT_STATUS_WT_NEW, status_flags);
+
+ cl_git_pass(git_repository_index(&index, repo));
+ cl_git_pass(git_index_add_bypath(index, LONG_FILENAME));
+
+ cl_git_pass(git_status_file(&status_flags, repo, LONG_FILENAME));
+ cl_assert_equal_i(GIT_STATUS_INDEX_NEW, status_flags);
+
+ cl_assert((entry = git_index_get_bypath(index, LONG_FILENAME, 0)) != NULL);
+ cl_git_pass(git_blob_lookup(&blob, repo, &entry->id));
+ cl_assert_equal_s(repodata, git_blob_rawcontent(blob));
+
+ git_blob_free(blob);
+ git_index_free(index);
+ git_str_dispose(&out);
+}
+#endif
+
+void test_win32_longpath__status_and_add(void)
+{
+#ifdef GIT_WIN32
+ git_repository *repo = cl_git_sandbox_init("testrepo");
+
+ cl_repo_set_bool(repo, "core.longpaths", true);
+
+ /*
+ * Doing no content filtering, we expect the data we add
+ * to be the data in the repository.
+ */
+ assert_longpath_status_and_add(repo,
+ "This is a long path.\r\n",
+ "This is a long path.\r\n");
+#endif
+}
+
+void test_win32_longpath__status_and_add_with_filter(void)
+{
+#ifdef GIT_WIN32
+ git_repository *repo = cl_git_sandbox_init("testrepo");
+
+ cl_repo_set_bool(repo, "core.longpaths", true);
+ cl_repo_set_bool(repo, "core.autocrlf", true);
+
+ /*
+ * With `core.autocrlf`, we expect the data we add to have
+ * newline conversion performed.
+ */
+ assert_longpath_status_and_add(repo,
+ "This is a long path.\r\n",
+ "This is a long path.\n");
+#endif
+}
diff --git a/tests/libgit2/win32/systemdir.c b/tests/libgit2/win32/systemdir.c
new file mode 100644
index 0000000..9039f05
--- /dev/null
+++ b/tests/libgit2/win32/systemdir.c
@@ -0,0 +1,337 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+#include "sysdir.h"
+
+#ifdef GIT_WIN32
+static char *path_save;
+static git_str gfw_path_root = GIT_STR_INIT;
+static git_str gfw_registry_root = GIT_STR_INIT;
+#endif
+
+void test_win32_systemdir__initialize(void)
+{
+#ifdef GIT_WIN32
+ git_str path_env = GIT_STR_INIT;
+
+ path_save = cl_getenv("PATH");
+ git_win32__set_registry_system_dir(L"");
+
+ cl_git_pass(git_str_puts(&path_env, "C:\\GitTempTest\\Foo;\"c:\\program files\\doesnotexisttesttemp\";C:\\fakefakedoesnotexist"));
+ cl_setenv("PATH", path_env.ptr);
+
+ cl_git_pass(git_str_puts(&gfw_path_root, clar_sandbox_path()));
+ cl_git_pass(git_str_puts(&gfw_path_root, "/fake_gfw_path_install"));
+
+ cl_git_pass(git_str_puts(&gfw_registry_root, clar_sandbox_path()));
+ cl_git_pass(git_str_puts(&gfw_registry_root, "/fake_gfw_registry_install"));
+
+ git_str_dispose(&path_env);
+#endif
+}
+
+void test_win32_systemdir__cleanup(void)
+{
+#ifdef GIT_WIN32
+ cl_fixture_cleanup("fake_gfw_path_install");
+ cl_fixture_cleanup("fake_gfw_registry_install");
+ git_str_dispose(&gfw_path_root);
+ git_str_dispose(&gfw_registry_root);
+
+ cl_setenv("PATH", path_save);
+ git__free(path_save);
+ path_save = NULL;
+
+ git_win32__set_registry_system_dir(NULL);
+ cl_sandbox_set_search_path_defaults();
+#endif
+}
+
+#ifdef GIT_WIN32
+static void fix_path(git_str *s)
+{
+ char *c;
+
+ for (c = s->ptr; *c; c++) {
+ if (*c == '/')
+ *c = '\\';
+ }
+}
+
+static void populate_fake_gfw(
+ git_str *expected_etc_dir,
+ const char *root,
+ const char *token,
+ bool create_gitconfig,
+ bool create_mingw64_gitconfig,
+ bool add_to_path,
+ bool add_to_registry)
+{
+ git_str bin_path = GIT_STR_INIT, exe_path = GIT_STR_INIT,
+ etc_path = GIT_STR_INIT, mingw64_path = GIT_STR_INIT,
+ config_path = GIT_STR_INIT, path_env = GIT_STR_INIT,
+ config_data = GIT_STR_INIT;
+
+ cl_git_pass(git_str_puts(&bin_path, root));
+ cl_git_pass(git_str_puts(&bin_path, "/cmd"));
+ cl_git_pass(git_futils_mkdir_r(bin_path.ptr, 0755));
+
+ cl_git_pass(git_str_puts(&exe_path, bin_path.ptr));
+ cl_git_pass(git_str_puts(&exe_path, "/git.cmd"));
+ cl_git_mkfile(exe_path.ptr, "This is a fake executable.");
+
+ cl_git_pass(git_str_puts(&etc_path, root));
+ cl_git_pass(git_str_puts(&etc_path, "/etc"));
+ cl_git_pass(git_futils_mkdir_r(etc_path.ptr, 0755));
+
+ cl_git_pass(git_str_puts(&mingw64_path, root));
+ cl_git_pass(git_str_puts(&mingw64_path, "/mingw64/etc"));
+ cl_git_pass(git_futils_mkdir_r(mingw64_path.ptr, 0755));
+
+ if (create_gitconfig) {
+ git_str_clear(&config_data);
+ git_str_printf(&config_data, "[gfw]\n\ttest = etc %s\n", token);
+
+ cl_git_pass(git_str_puts(&config_path, etc_path.ptr));
+ cl_git_pass(git_str_puts(&config_path, "/gitconfig"));
+ cl_git_mkfile(config_path.ptr, config_data.ptr);
+ }
+
+ if (create_mingw64_gitconfig) {
+ git_str_clear(&config_data);
+ git_str_printf(&config_data, "[gfw]\n\ttest = mingw64 %s\n", token);
+
+ git_str_clear(&config_path);
+ cl_git_pass(git_str_puts(&config_path, mingw64_path.ptr));
+ cl_git_pass(git_str_puts(&config_path, "/gitconfig"));
+ cl_git_mkfile(config_path.ptr, config_data.ptr);
+ }
+
+ if (add_to_path) {
+ fix_path(&bin_path);
+ cl_git_pass(git_str_puts(&path_env, "C:\\GitTempTest\\Foo;\"c:\\program files\\doesnotexisttesttemp\";"));
+ cl_git_pass(git_str_puts(&path_env, bin_path.ptr));
+ cl_git_pass(git_str_puts(&path_env, ";C:\\fakefakedoesnotexist"));
+ cl_setenv("PATH", path_env.ptr);
+ }
+
+ if (add_to_registry) {
+ git_win32_path registry_path;
+ size_t offset = 0;
+
+ cl_assert(git_win32_path_from_utf8(registry_path, root) >= 0);
+ if (wcsncmp(registry_path, L"\\\\?\\", CONST_STRLEN("\\\\?\\")) == 0)
+ offset = CONST_STRLEN("\\\\?\\");
+ git_win32__set_registry_system_dir(registry_path + offset);
+ }
+
+ cl_git_pass(git_str_join(expected_etc_dir, GIT_PATH_LIST_SEPARATOR, expected_etc_dir->ptr, etc_path.ptr));
+ cl_git_pass(git_str_join(expected_etc_dir, GIT_PATH_LIST_SEPARATOR, expected_etc_dir->ptr, mingw64_path.ptr));
+
+ git_str_dispose(&bin_path);
+ git_str_dispose(&exe_path);
+ git_str_dispose(&etc_path);
+ git_str_dispose(&mingw64_path);
+ git_str_dispose(&config_path);
+ git_str_dispose(&path_env);
+ git_str_dispose(&config_data);
+}
+
+static void populate_fake_ecosystem(
+ git_str *expected_etc_dir,
+ bool create_gitconfig,
+ bool create_mingw64_gitconfig,
+ bool path,
+ bool registry)
+{
+ if (path)
+ populate_fake_gfw(expected_etc_dir, gfw_path_root.ptr, "path", create_gitconfig, create_mingw64_gitconfig, true, false);
+
+ if (registry)
+ populate_fake_gfw(expected_etc_dir, gfw_registry_root.ptr, "registry", create_gitconfig, create_mingw64_gitconfig, false, true);
+}
+#endif
+
+void test_win32_systemdir__finds_etc_in_path(void)
+{
+#ifdef GIT_WIN32
+ git_str expected = GIT_STR_INIT, out = GIT_STR_INIT;
+ git_config *cfg;
+ git_buf value = GIT_BUF_INIT;
+
+ populate_fake_ecosystem(&expected, true, false, true, false);
+
+ cl_git_pass(git_win32__find_system_dirs(&out, "etc"));
+ cl_assert_equal_s(out.ptr, expected.ptr);
+
+ git_sysdir_reset();
+
+ cl_git_pass(git_config_open_default(&cfg));
+ cl_git_pass(git_config_get_string_buf(&value, cfg, "gfw.test"));
+ cl_assert_equal_s("etc path", value.ptr);
+
+ git_buf_dispose(&value);
+ git_str_dispose(&expected);
+ git_str_dispose(&out);
+ git_config_free(cfg);
+#endif
+}
+
+void test_win32_systemdir__finds_mingw64_etc_in_path(void)
+{
+#ifdef GIT_WIN32
+ git_str expected = GIT_STR_INIT, out = GIT_STR_INIT;
+ git_config* cfg;
+ git_buf value = GIT_BUF_INIT;
+
+ populate_fake_ecosystem(&expected, false, true, true, false);
+
+ cl_git_pass(git_win32__find_system_dirs(&out, "etc"));
+ cl_assert_equal_s(out.ptr, expected.ptr);
+
+ git_sysdir_reset();
+
+ cl_git_pass(git_config_open_default(&cfg));
+ cl_git_pass(git_config_get_string_buf(&value, cfg, "gfw.test"));
+ cl_assert_equal_s("mingw64 path", value.ptr);
+
+ git_buf_dispose(&value);
+ git_str_dispose(&expected);
+ git_str_dispose(&out);
+ git_config_free(cfg);
+#endif
+}
+
+void test_win32_systemdir__prefers_etc_to_mingw64_in_path(void)
+{
+#ifdef GIT_WIN32
+ git_str expected = GIT_STR_INIT, out = GIT_STR_INIT;
+ git_config* cfg;
+ git_buf value = GIT_BUF_INIT;
+
+ populate_fake_ecosystem(&expected, true, true, true, false);
+
+ cl_git_pass(git_win32__find_system_dirs(&out, "etc"));
+ cl_assert_equal_s(out.ptr, expected.ptr);
+
+ git_sysdir_reset();
+
+ cl_git_pass(git_config_open_default(&cfg));
+ cl_git_pass(git_config_get_string_buf(&value, cfg, "gfw.test"));
+ cl_assert_equal_s("etc path", value.ptr);
+
+ git_buf_dispose(&value);
+ git_str_dispose(&expected);
+ git_str_dispose(&out);
+ git_config_free(cfg);
+#endif
+}
+
+void test_win32_systemdir__finds_etc_in_registry(void)
+{
+#ifdef GIT_WIN32
+ git_str expected = GIT_STR_INIT, out = GIT_STR_INIT;
+ git_config* cfg;
+ git_buf value = GIT_BUF_INIT;
+
+ populate_fake_ecosystem(&expected, true, false, false, true);
+
+ cl_git_pass(git_win32__find_system_dirs(&out, "etc"));
+ cl_assert_equal_s(out.ptr, expected.ptr);
+
+ git_sysdir_reset();
+
+ cl_git_pass(git_config_open_default(&cfg));
+ cl_git_pass(git_config_get_string_buf(&value, cfg, "gfw.test"));
+ cl_assert_equal_s("etc registry", value.ptr);
+
+ git_buf_dispose(&value);
+ git_str_dispose(&expected);
+ git_str_dispose(&out);
+ git_config_free(cfg);
+#endif
+}
+
+void test_win32_systemdir__finds_mingw64_etc_in_registry(void)
+{
+#ifdef GIT_WIN32
+ git_str expected = GIT_STR_INIT, out = GIT_STR_INIT;
+ git_config* cfg;
+ git_buf value = GIT_BUF_INIT;
+
+ populate_fake_ecosystem(&expected, false, true, false, true);
+
+ cl_git_pass(git_win32__find_system_dirs(&out, "etc"));
+ cl_assert_equal_s(out.ptr, expected.ptr);
+
+ git_sysdir_reset();
+
+ cl_git_pass(git_config_open_default(&cfg));
+ cl_git_pass(git_config_get_string_buf(&value, cfg, "gfw.test"));
+ cl_assert_equal_s("mingw64 registry", value.ptr);
+
+ git_buf_dispose(&value);
+ git_str_dispose(&expected);
+ git_str_dispose(&out);
+ git_config_free(cfg);
+#endif
+}
+
+void test_win32_systemdir__prefers_etc_to_mingw64_in_registry(void)
+{
+#ifdef GIT_WIN32
+ git_str expected = GIT_STR_INIT, out = GIT_STR_INIT;
+ git_config* cfg;
+ git_buf value = GIT_BUF_INIT;
+
+ populate_fake_ecosystem(&expected, true, true, false, true);
+
+ cl_git_pass(git_win32__find_system_dirs(&out, "etc"));
+ cl_assert_equal_s(out.ptr, expected.ptr);
+
+ git_sysdir_reset();
+
+ cl_git_pass(git_config_open_default(&cfg));
+ cl_git_pass(git_config_get_string_buf(&value, cfg, "gfw.test"));
+ cl_assert_equal_s("etc registry", value.ptr);
+
+ git_buf_dispose(&value);
+ git_str_dispose(&expected);
+ git_str_dispose(&out);
+ git_config_free(cfg);
+#endif
+}
+
+void test_win32_systemdir__prefers_path_to_registry(void)
+{
+#ifdef GIT_WIN32
+ git_str expected = GIT_STR_INIT, out = GIT_STR_INIT;
+ git_config* cfg;
+ git_buf value = GIT_BUF_INIT;
+
+ populate_fake_ecosystem(&expected, true, true, true, true);
+
+ cl_git_pass(git_win32__find_system_dirs(&out, "etc"));
+ cl_assert_equal_s(out.ptr, expected.ptr);
+
+ git_sysdir_reset();
+
+ cl_git_pass(git_config_open_default(&cfg));
+ cl_git_pass(git_config_get_string_buf(&value, cfg, "gfw.test"));
+ cl_assert_equal_s("etc path", value.ptr);
+
+ git_buf_dispose(&value);
+ git_str_dispose(&expected);
+ git_str_dispose(&out);
+ git_config_free(cfg);
+#endif
+}
+
+void test_win32_systemdir__no_git_installed(void)
+{
+#ifdef GIT_WIN32
+ git_str out = GIT_STR_INIT;
+
+ cl_git_pass(git_win32__find_system_dirs(&out, "etc"));
+ cl_assert_equal_s(out.ptr, "");
+#endif
+}
diff --git a/tests/libgit2/worktree/bare.c b/tests/libgit2/worktree/bare.c
new file mode 100644
index 0000000..7234dff
--- /dev/null
+++ b/tests/libgit2/worktree/bare.c
@@ -0,0 +1,72 @@
+#include "clar_libgit2.h"
+#include "worktree_helpers.h"
+#include "submodule/submodule_helpers.h"
+
+#define COMMON_REPO "testrepo.git"
+#define WORKTREE_REPO "worktree"
+
+static git_repository *g_repo;
+
+void test_worktree_bare__initialize(void)
+{
+ g_repo = cl_git_sandbox_init(COMMON_REPO);
+
+ cl_assert_equal_i(1, git_repository_is_bare(g_repo));
+ cl_assert_equal_i(0, git_repository_is_worktree(g_repo));
+}
+
+void test_worktree_bare__cleanup(void)
+{
+ cl_fixture_cleanup(WORKTREE_REPO);
+ cl_git_sandbox_cleanup();
+}
+
+void test_worktree_bare__list(void)
+{
+ git_strarray wts;
+
+ cl_git_pass(git_worktree_list(&wts, g_repo));
+ cl_assert_equal_i(wts.count, 0);
+
+ git_strarray_dispose(&wts);
+}
+
+void test_worktree_bare__add(void)
+{
+ git_worktree *wt;
+ git_repository *wtrepo;
+ git_strarray wts;
+
+ cl_git_pass(git_worktree_add(&wt, g_repo, "name", WORKTREE_REPO, NULL));
+
+ cl_git_pass(git_worktree_list(&wts, g_repo));
+ cl_assert_equal_i(wts.count, 1);
+
+ cl_git_pass(git_worktree_validate(wt));
+
+ cl_git_pass(git_repository_open(&wtrepo, WORKTREE_REPO));
+ cl_assert_equal_i(0, git_repository_is_bare(wtrepo));
+ cl_assert_equal_i(1, git_repository_is_worktree(wtrepo));
+
+ git_strarray_dispose(&wts);
+ git_worktree_free(wt);
+ git_repository_free(wtrepo);
+}
+
+void test_worktree_bare__repository_path(void)
+{
+ git_worktree *wt;
+ git_repository *wtrepo;
+
+ cl_git_pass(git_worktree_add(&wt, g_repo, "name", WORKTREE_REPO, NULL));
+ cl_assert_equal_s(git_worktree_path(wt), cl_git_sandbox_path(0, WORKTREE_REPO, NULL));
+
+ cl_git_pass(git_repository_open(&wtrepo, WORKTREE_REPO));
+ cl_assert_equal_s(git_repository_path(wtrepo), cl_git_sandbox_path(1, COMMON_REPO, "worktrees", "name", NULL));
+
+ cl_assert_equal_s(git_repository_commondir(g_repo), git_repository_commondir(wtrepo));
+ cl_assert_equal_s(git_repository_workdir(wtrepo), cl_git_sandbox_path(1, WORKTREE_REPO, NULL));
+
+ git_repository_free(wtrepo);
+ git_worktree_free(wt);
+}
diff --git a/tests/libgit2/worktree/config.c b/tests/libgit2/worktree/config.c
new file mode 100644
index 0000000..81dcfe1
--- /dev/null
+++ b/tests/libgit2/worktree/config.c
@@ -0,0 +1,47 @@
+#include "clar_libgit2.h"
+#include "worktree_helpers.h"
+
+#define COMMON_REPO "testrepo"
+#define WORKTREE_REPO "testrepo-worktree"
+
+static worktree_fixture fixture =
+ WORKTREE_FIXTURE_INIT(COMMON_REPO, WORKTREE_REPO);
+
+void test_worktree_config__initialize(void)
+{
+ setup_fixture_worktree(&fixture);
+}
+
+void test_worktree_config__cleanup(void)
+{
+ cleanup_fixture_worktree(&fixture);
+}
+
+void test_worktree_config__open(void)
+{
+ git_config *cfg;
+
+ cl_git_pass(git_repository_config(&cfg, fixture.worktree));
+ cl_assert(cfg != NULL);
+
+ git_config_free(cfg);
+}
+
+void test_worktree_config__set(void)
+{
+ git_config *cfg;
+ int32_t val;
+
+ cl_git_pass(git_repository_config(&cfg, fixture.worktree));
+ cl_git_pass(git_config_set_int32(cfg, "core.dummy", 5));
+ git_config_free(cfg);
+
+ /*
+ * reopen to verify configuration has been set in the
+ * common dir
+ */
+ cl_git_pass(git_repository_config(&cfg, fixture.repo));
+ cl_git_pass(git_config_get_int32(&val, cfg, "core.dummy"));
+ cl_assert_equal_i(val, 5);
+ git_config_free(cfg);
+}
diff --git a/tests/libgit2/worktree/merge.c b/tests/libgit2/worktree/merge.c
new file mode 100644
index 0000000..5b7e2a8
--- /dev/null
+++ b/tests/libgit2/worktree/merge.c
@@ -0,0 +1,121 @@
+#include "clar_libgit2.h"
+
+#include "worktree_helpers.h"
+#include "merge/merge_helpers.h"
+
+#define COMMON_REPO "testrepo"
+#define WORKTREE_REPO "testrepo-worktree"
+
+#define MASTER_BRANCH "refs/heads/master"
+#define CONFLICT_BRANCH "refs/heads/merge-conflict"
+
+#define CONFLICT_BRANCH_FILE_TXT \
+ "<<<<<<< HEAD\n" \
+ "hi\n" \
+ "bye!\n" \
+ "=======\n" \
+ "conflict\n" \
+ ">>>>>>> merge-conflict\n" \
+
+static worktree_fixture fixture =
+ WORKTREE_FIXTURE_INIT(COMMON_REPO, WORKTREE_REPO);
+
+static const char *merge_files[] = {
+ GIT_MERGE_HEAD_FILE,
+ GIT_ORIG_HEAD_FILE,
+ GIT_MERGE_MODE_FILE,
+ GIT_MERGE_MSG_FILE,
+};
+
+void test_worktree_merge__initialize(void)
+{
+ setup_fixture_worktree(&fixture);
+}
+
+void test_worktree_merge__cleanup(void)
+{
+ cleanup_fixture_worktree(&fixture);
+}
+
+void test_worktree_merge__merge_head(void)
+{
+ git_reference *theirs_ref, *ref;
+ git_annotated_commit *theirs;
+
+ cl_git_pass(git_reference_lookup(&theirs_ref, fixture.worktree, CONFLICT_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&theirs, fixture.worktree, theirs_ref));
+ cl_git_pass(git_merge(fixture.worktree, (const git_annotated_commit **)&theirs, 1, NULL, NULL));
+
+ cl_git_pass(git_reference_lookup(&ref, fixture.worktree, GIT_MERGE_HEAD_FILE));
+
+ git_reference_free(ref);
+ git_reference_free(theirs_ref);
+ git_annotated_commit_free(theirs);
+}
+
+void test_worktree_merge__merge_setup(void)
+{
+ git_reference *ours_ref, *theirs_ref;
+ git_annotated_commit *ours, *theirs;
+ git_str path = GIT_STR_INIT;
+ unsigned i;
+
+ cl_git_pass(git_reference_lookup(&ours_ref, fixture.worktree, MASTER_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&ours, fixture.worktree, ours_ref));
+
+ cl_git_pass(git_reference_lookup(&theirs_ref, fixture.worktree, CONFLICT_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&theirs, fixture.worktree, theirs_ref));
+
+ cl_git_pass(git_merge__setup(fixture.worktree,
+ ours, (const git_annotated_commit **)&theirs, 1));
+
+ for (i = 0; i < ARRAY_SIZE(merge_files); i++) {
+ cl_git_pass(git_str_joinpath(&path,
+ fixture.worktree->gitdir,
+ merge_files[i]));
+ cl_assert(git_fs_path_exists(path.ptr));
+ }
+
+ git_str_dispose(&path);
+ git_reference_free(ours_ref);
+ git_reference_free(theirs_ref);
+ git_annotated_commit_free(ours);
+ git_annotated_commit_free(theirs);
+}
+
+void test_worktree_merge__merge_conflict(void)
+{
+ git_str path = GIT_STR_INIT, buf = GIT_STR_INIT;
+ git_reference *theirs_ref;
+ git_annotated_commit *theirs;
+ git_index *index;
+ const git_index_entry *entry;
+ size_t i, conflicts = 0;
+
+ cl_git_pass(git_reference_lookup(&theirs_ref, fixture.worktree, CONFLICT_BRANCH));
+ cl_git_pass(git_annotated_commit_from_ref(&theirs, fixture.worktree, theirs_ref));
+
+ cl_git_pass(git_merge(fixture.worktree,
+ (const git_annotated_commit **)&theirs, 1, NULL, NULL));
+
+ cl_git_pass(git_repository_index(&index, fixture.worktree));
+ for (i = 0; i < git_index_entrycount(index); i++) {
+ cl_assert(entry = git_index_get_byindex(index, i));
+
+ if (git_index_entry_is_conflict(entry))
+ conflicts++;
+ }
+ cl_assert_equal_sz(conflicts, 3);
+
+ git_reference_free(theirs_ref);
+ git_annotated_commit_free(theirs);
+ git_index_free(index);
+
+ cl_git_pass(git_str_joinpath(&path, fixture.worktree->workdir, "branch_file.txt"));
+ cl_git_pass(git_futils_readbuffer(&buf, path.ptr));
+ cl_assert_equal_s(buf.ptr, CONFLICT_BRANCH_FILE_TXT);
+
+ git_str_dispose(&path);
+ git_str_dispose(&buf);
+}
+
diff --git a/tests/libgit2/worktree/open.c b/tests/libgit2/worktree/open.c
new file mode 100644
index 0000000..0c3fdc1
--- /dev/null
+++ b/tests/libgit2/worktree/open.c
@@ -0,0 +1,126 @@
+#include "clar_libgit2.h"
+#include "repository.h"
+#include "worktree.h"
+#include "worktree_helpers.h"
+
+#define COMMON_REPO "testrepo"
+#define WORKTREE_REPO "testrepo-worktree"
+
+static worktree_fixture fixture =
+ WORKTREE_FIXTURE_INIT(COMMON_REPO, WORKTREE_REPO);
+
+static void assert_worktree_valid(git_repository *wt, const char *parentdir, const char *wtdir)
+{
+ cl_assert(wt->is_worktree);
+
+ cl_assert_equal_s(wt->workdir, cl_git_sandbox_path(1, wtdir, NULL));
+ cl_assert_equal_s(wt->gitlink, cl_git_sandbox_path(0, wtdir, ".git", NULL));
+ cl_assert_equal_s(wt->gitdir, cl_git_sandbox_path(1, parentdir, ".git", "worktrees", wtdir, NULL));
+}
+
+void test_worktree_open__initialize(void)
+{
+ setup_fixture_worktree(&fixture);
+}
+
+void test_worktree_open__cleanup(void)
+{
+ cleanup_fixture_worktree(&fixture);
+}
+
+void test_worktree_open__repository(void)
+{
+ assert_worktree_valid(fixture.worktree, COMMON_REPO, WORKTREE_REPO);
+}
+
+void test_worktree_open__repository_through_workdir(void)
+{
+ git_repository *wt;
+
+ cl_git_pass(git_repository_open(&wt, WORKTREE_REPO));
+ assert_worktree_valid(wt, COMMON_REPO, WORKTREE_REPO);
+
+ git_repository_free(wt);
+}
+
+void test_worktree_open__repository_through_gitlink(void)
+{
+ git_repository *wt;
+
+ cl_git_pass(git_repository_open(&wt, WORKTREE_REPO "/.git"));
+ assert_worktree_valid(wt, COMMON_REPO, WORKTREE_REPO);
+
+ git_repository_free(wt);
+}
+
+void test_worktree_open__repository_through_gitdir(void)
+{
+ git_str gitdir_path = GIT_STR_INIT;
+ git_repository *wt;
+
+ cl_git_pass(git_str_joinpath(&gitdir_path, COMMON_REPO, ".git"));
+ cl_git_pass(git_str_joinpath(&gitdir_path, gitdir_path.ptr, "worktrees"));
+ cl_git_pass(git_str_joinpath(&gitdir_path, gitdir_path.ptr, "testrepo-worktree"));
+
+ cl_git_pass(git_repository_open(&wt, gitdir_path.ptr));
+ assert_worktree_valid(wt, COMMON_REPO, WORKTREE_REPO);
+
+ git_str_dispose(&gitdir_path);
+ git_repository_free(wt);
+}
+
+void test_worktree_open__open_discovered_worktree(void)
+{
+ git_buf path = GIT_BUF_INIT;
+ git_repository *repo;
+
+ cl_git_pass(git_repository_discover(&path,
+ git_repository_workdir(fixture.worktree), false, NULL));
+ cl_git_pass(git_repository_open(&repo, path.ptr));
+ cl_assert_equal_s(git_repository_workdir(fixture.worktree),
+ git_repository_workdir(repo));
+
+ git_buf_dispose(&path);
+ git_repository_free(repo);
+}
+
+void test_worktree_open__repository_with_nonexistent_parent(void)
+{
+ git_repository *repo;
+
+ cleanup_fixture_worktree(&fixture);
+
+ cl_fixture_sandbox(WORKTREE_REPO);
+ cl_git_pass(p_chdir(WORKTREE_REPO));
+ cl_git_pass(cl_rename(".gitted", ".git"));
+ cl_git_pass(p_chdir(".."));
+
+ cl_git_fail(git_repository_open(&repo, WORKTREE_REPO));
+
+ cl_fixture_cleanup(WORKTREE_REPO);
+}
+
+void test_worktree_open__open_from_repository(void)
+{
+ git_worktree *opened, *lookedup;
+
+ cl_git_pass(git_worktree_open_from_repository(&opened, fixture.worktree));
+ cl_git_pass(git_worktree_lookup(&lookedup, fixture.repo, WORKTREE_REPO));
+
+ cl_assert_equal_s(opened->name, lookedup->name);
+ cl_assert_equal_s(opened->gitdir_path, lookedup->gitdir_path);
+ cl_assert_equal_s(opened->gitlink_path, lookedup->gitlink_path);
+ cl_assert_equal_s(opened->parent_path, lookedup->parent_path);
+ cl_assert_equal_s(opened->commondir_path, lookedup->commondir_path);
+ cl_assert_equal_i(opened->locked, lookedup->locked);
+
+ git_worktree_free(opened);
+ git_worktree_free(lookedup);
+}
+
+void test_worktree_open__open_from_nonworktree_fails(void)
+{
+ git_worktree *wt;
+
+ cl_git_fail(git_worktree_open_from_repository(&wt, fixture.repo));
+}
diff --git a/tests/libgit2/worktree/reflog.c b/tests/libgit2/worktree/reflog.c
new file mode 100644
index 0000000..a68e72d
--- /dev/null
+++ b/tests/libgit2/worktree/reflog.c
@@ -0,0 +1,91 @@
+#include "clar_libgit2.h"
+#include "worktree_helpers.h"
+
+#include "reflog.h"
+
+#define COMMON_REPO "testrepo"
+#define WORKTREE_REPO "testrepo-worktree"
+
+#define REFLOG "refs/heads/testrepo-worktree"
+#define REFLOG_MESSAGE "reflog message"
+
+static worktree_fixture fixture =
+ WORKTREE_FIXTURE_INIT(COMMON_REPO, WORKTREE_REPO);
+
+void test_worktree_reflog__initialize(void)
+{
+ setup_fixture_worktree(&fixture);
+}
+
+void test_worktree_reflog__cleanup(void)
+{
+ cleanup_fixture_worktree(&fixture);
+}
+
+void test_worktree_reflog__read_worktree_HEAD(void)
+{
+ git_reflog *reflog;
+ const git_reflog_entry *entry;
+
+ cl_git_pass(git_reflog_read(&reflog, fixture.worktree, "HEAD"));
+ cl_assert_equal_i(1, git_reflog_entrycount(reflog));
+
+ entry = git_reflog_entry_byindex(reflog, 0);
+ cl_assert(entry != NULL);
+ cl_assert_equal_s("checkout: moving from 099fabac3a9ea935598528c27f866e34089c2eff to testrepo-worktree", git_reflog_entry_message(entry));
+
+ git_reflog_free(reflog);
+}
+
+void test_worktree_reflog__read_parent_HEAD(void)
+{
+ git_reflog *reflog;
+
+ cl_git_pass(git_reflog_read(&reflog, fixture.repo, "HEAD"));
+ /* there is no logs/HEAD in the parent repo */
+ cl_assert_equal_i(0, git_reflog_entrycount(reflog));
+
+ git_reflog_free(reflog);
+}
+
+void test_worktree_reflog__read(void)
+{
+ git_reflog *reflog;
+ const git_reflog_entry *entry;
+
+ cl_git_pass(git_reflog_read(&reflog, fixture.worktree, REFLOG));
+ cl_assert_equal_i(git_reflog_entrycount(reflog), 1);
+
+ entry = git_reflog_entry_byindex(reflog, 0);
+ cl_assert(entry != NULL);
+ cl_assert_equal_s(git_reflog_entry_message(entry), "branch: Created from HEAD");
+
+ git_reflog_free(reflog);
+}
+
+void test_worktree_reflog__append_then_read(void)
+{
+ git_reflog *reflog, *parent_reflog;
+ const git_reflog_entry *entry;
+ git_reference *head;
+ git_signature *sig;
+ const git_oid *oid;
+
+ cl_git_pass(git_repository_head(&head, fixture.worktree));
+ cl_assert((oid = git_reference_target(head)) != NULL);
+ cl_git_pass(git_signature_now(&sig, "foo", "foo@bar"));
+
+ cl_git_pass(git_reflog_read(&reflog, fixture.worktree, REFLOG));
+ cl_git_pass(git_reflog_append(reflog, oid, sig, REFLOG_MESSAGE));
+ git_reflog_write(reflog);
+
+ cl_git_pass(git_reflog_read(&parent_reflog, fixture.repo, REFLOG));
+ entry = git_reflog_entry_byindex(parent_reflog, 0);
+ cl_assert(git_oid_cmp(oid, &entry->oid_old) == 0);
+ cl_assert(git_oid_cmp(oid, &entry->oid_cur) == 0);
+
+ git_reference_free(head);
+ git_signature_free(sig);
+ git_reflog_free(reflog);
+ git_reflog_free(parent_reflog);
+}
diff --git a/tests/libgit2/worktree/refs.c b/tests/libgit2/worktree/refs.c
new file mode 100644
index 0000000..557726a
--- /dev/null
+++ b/tests/libgit2/worktree/refs.c
@@ -0,0 +1,198 @@
+#include "clar_libgit2.h"
+#include "path.h"
+#include "refs.h"
+#include "worktree.h"
+#include "worktree_helpers.h"
+
+#define COMMON_REPO "testrepo"
+#define WORKTREE_REPO "testrepo-worktree"
+
+static worktree_fixture fixture =
+ WORKTREE_FIXTURE_INIT(COMMON_REPO, WORKTREE_REPO);
+
+void test_worktree_refs__initialize(void)
+{
+ setup_fixture_worktree(&fixture);
+}
+
+void test_worktree_refs__cleanup(void)
+{
+ cleanup_fixture_worktree(&fixture);
+}
+
+void test_worktree_refs__list(void)
+{
+ git_strarray refs, wtrefs;
+ unsigned i, j;
+ int error = 0;
+
+ cl_git_pass(git_reference_list(&refs, fixture.repo));
+ cl_git_pass(git_reference_list(&wtrefs, fixture.worktree));
+
+ if (refs.count != wtrefs.count)
+ {
+ error = GIT_ERROR;
+ goto exit;
+ }
+
+ for (i = 0; i < refs.count; i++)
+ {
+ int found = 0;
+
+ for (j = 0; j < wtrefs.count; j++)
+ {
+ if (!strcmp(refs.strings[i], wtrefs.strings[j]))
+ {
+ found = 1;
+ break;
+ }
+ }
+
+ if (!found)
+ {
+ error = GIT_ERROR;
+ goto exit;
+ }
+ }
+
+exit:
+ git_strarray_dispose(&refs);
+ git_strarray_dispose(&wtrefs);
+ cl_git_pass(error);
+}
+
+void test_worktree_refs__read_head(void)
+{
+ git_reference *head;
+
+ cl_git_pass(git_repository_head(&head, fixture.worktree));
+
+ git_reference_free(head);
+}
+
+void test_worktree_refs__set_head_fails_when_worktree_wants_linked_repos_HEAD(void)
+{
+ git_reference *head;
+
+ cl_git_pass(git_repository_head(&head, fixture.repo));
+ cl_git_fail(git_repository_set_head(fixture.worktree, git_reference_name(head)));
+
+ git_reference_free(head);
+}
+
+void test_worktree_refs__set_head_fails_when_main_repo_wants_worktree_head(void)
+{
+ git_reference *head;
+
+ cl_git_pass(git_repository_head(&head, fixture.worktree));
+ cl_git_fail(git_repository_set_head(fixture.repo, git_reference_name(head)));
+
+ git_reference_free(head);
+}
+
+void test_worktree_refs__set_head_works_for_current_HEAD(void)
+{
+ git_reference *head;
+
+ cl_git_pass(git_repository_head(&head, fixture.repo));
+ cl_git_pass(git_repository_set_head(fixture.repo, git_reference_name(head)));
+
+ git_reference_free(head);
+}
+
+void test_worktree_refs__set_head_fails_when_already_checked_out(void)
+{
+ cl_git_fail(git_repository_set_head(fixture.repo, "refs/heads/testrepo-worktree"));
+}
+
+void test_worktree_refs__delete_fails_for_checked_out_branch(void)
+{
+ git_reference *branch;
+
+ cl_git_pass(git_branch_lookup(&branch, fixture.repo,
+ "testrepo-worktree", GIT_BRANCH_LOCAL));
+ cl_git_fail(git_branch_delete(branch));
+
+ git_reference_free(branch);
+}
+
+void test_worktree_refs__delete_succeeds_after_pruning_worktree(void)
+{
+ git_worktree_prune_options opts = GIT_WORKTREE_PRUNE_OPTIONS_INIT;
+ git_reference *branch;
+ git_worktree *worktree;
+
+ opts.flags = GIT_WORKTREE_PRUNE_VALID;
+
+ cl_git_pass(git_worktree_lookup(&worktree, fixture.repo, fixture.worktreename));
+ cl_git_pass(git_worktree_prune(worktree, &opts));
+ git_worktree_free(worktree);
+
+ cl_git_pass(git_branch_lookup(&branch, fixture.repo,
+ "testrepo-worktree", GIT_BRANCH_LOCAL));
+ cl_git_pass(git_branch_delete(branch));
+ git_reference_free(branch);
+}
+
+void test_worktree_refs__delete_unrelated_branch_on_worktree(void)
+{
+ git_reference *branch;
+
+ cl_git_pass(git_branch_lookup(&branch, fixture.worktree,
+ "merge-conflict", GIT_BRANCH_LOCAL));
+ cl_git_pass(git_branch_delete(branch));
+
+ git_reference_free(branch);
+}
+
+void test_worktree_refs__delete_unrelated_branch_on_parent(void)
+{
+ git_reference *branch;
+
+ cl_git_pass(git_branch_lookup(&branch, fixture.repo,
+ "merge-conflict", GIT_BRANCH_LOCAL));
+ cl_git_pass(git_branch_delete(branch));
+
+ git_reference_free(branch);
+}
+
+void test_worktree_refs__renaming_reference_updates_worktree_heads(void)
+{
+ git_reference *head, *branch, *renamed;
+
+ cl_git_pass(git_branch_lookup(&branch, fixture.repo,
+ "testrepo-worktree", GIT_BRANCH_LOCAL));
+ cl_git_pass(git_reference_rename(&renamed, branch, "refs/heads/renamed", 0, NULL));
+
+ cl_git_pass(git_reference_lookup(&head, fixture.worktree, GIT_HEAD_FILE));
+ cl_assert_equal_i(git_reference_type(head), GIT_REFERENCE_SYMBOLIC);
+ cl_assert_equal_s(git_reference_symbolic_target(head), "refs/heads/renamed");
+
+ git_reference_free(head);
+ git_reference_free(branch);
+ git_reference_free(renamed);
+}
+
+void test_worktree_refs__creating_refs_uses_commondir(void)
+{
+ git_reference *head, *branch, *lookup;
+ git_commit *commit;
+ git_str refpath = GIT_STR_INIT;
+
+ cl_git_pass(git_str_joinpath(&refpath,
+ git_repository_commondir(fixture.worktree), "refs/heads/testbranch"));
+ cl_assert(!git_fs_path_exists(refpath.ptr));
+
+ cl_git_pass(git_repository_head(&head, fixture.worktree));
+ cl_git_pass(git_commit_lookup(&commit, fixture.worktree, git_reference_target(head)));
+ cl_git_pass(git_branch_create(&branch, fixture.worktree, "testbranch", commit, 0));
+ cl_git_pass(git_branch_lookup(&lookup, fixture.worktree, "testbranch", GIT_BRANCH_LOCAL));
+ cl_assert(git_reference_cmp(branch, lookup) == 0);
+ cl_assert(git_fs_path_exists(refpath.ptr));
+
+ git_reference_free(lookup);
+ git_reference_free(branch);
+ git_reference_free(head);
+ git_commit_free(commit);
+ git_str_dispose(&refpath);
+}
diff --git a/tests/libgit2/worktree/repository.c b/tests/libgit2/worktree/repository.c
new file mode 100644
index 0000000..c4eeadd
--- /dev/null
+++ b/tests/libgit2/worktree/repository.c
@@ -0,0 +1,67 @@
+#include "clar_libgit2.h"
+#include "worktree_helpers.h"
+#include "submodule/submodule_helpers.h"
+
+#include "repository.h"
+
+#define COMMON_REPO "testrepo"
+#define WORKTREE_REPO "testrepo-worktree"
+
+static worktree_fixture fixture =
+ WORKTREE_FIXTURE_INIT(COMMON_REPO, WORKTREE_REPO);
+
+void test_worktree_repository__initialize(void)
+{
+ setup_fixture_worktree(&fixture);
+}
+
+void test_worktree_repository__cleanup(void)
+{
+ cleanup_fixture_worktree(&fixture);
+}
+
+void test_worktree_repository__head(void)
+{
+ git_reference *ref, *head;
+
+ cl_git_pass(git_reference_lookup(&ref, fixture.repo, "refs/heads/testrepo-worktree"));
+ cl_git_pass(git_repository_head_for_worktree(&head, fixture.repo, "testrepo-worktree"));
+ cl_assert(git_reference_cmp(ref, head) == 0);
+ cl_assert(git_reference_owner(ref) == fixture.repo);
+
+ git_reference_free(ref);
+ git_reference_free(head);
+}
+
+void test_worktree_repository__head_fails_for_invalid_worktree(void)
+{
+ git_reference *head = NULL;
+
+ cl_git_fail(git_repository_head_for_worktree(&head, fixture.repo, "invalid"));
+ cl_assert(head == NULL);
+}
+
+void test_worktree_repository__head_detached(void)
+{
+ git_reference *ref, *head;
+
+ cl_git_pass(git_reference_lookup(&ref, fixture.repo, "refs/heads/testrepo-worktree"));
+ cl_git_pass(git_repository_set_head_detached(fixture.worktree, &ref->target.oid));
+
+ cl_assert(git_repository_head_detached(fixture.worktree));
+ cl_assert(git_repository_head_detached_for_worktree(fixture.repo, "testrepo-worktree"));
+ cl_git_pass(git_repository_head_for_worktree(&head, fixture.repo, "testrepo-worktree"));
+
+ cl_assert_equal_oid(&ref->target.oid, &head->target.oid);
+
+ git_reference_free(ref);
+ git_reference_free(head);
+}
+
+void test_worktree_repository__head_detached_fails_for_invalid_worktree(void)
+{
+ git_reference *head = NULL;
+
+ cl_git_fail(git_repository_head_detached_for_worktree(fixture.repo, "invalid"));
+ cl_assert(head == NULL);
+}
diff --git a/tests/libgit2/worktree/submodule.c b/tests/libgit2/worktree/submodule.c
new file mode 100644
index 0000000..6b0c074
--- /dev/null
+++ b/tests/libgit2/worktree/submodule.c
@@ -0,0 +1,92 @@
+#include "clar_libgit2.h"
+#include "repository.h"
+#include "worktree.h"
+#include "worktree_helpers.h"
+
+#define WORKTREE_PARENT "submodules-worktree-parent"
+#define WORKTREE_CHILD "submodules-worktree-child"
+
+static worktree_fixture parent
+ = WORKTREE_FIXTURE_INIT("submodules", WORKTREE_PARENT);
+static worktree_fixture child
+ = WORKTREE_FIXTURE_INIT(NULL, WORKTREE_CHILD);
+
+void test_worktree_submodule__initialize(void)
+{
+ setup_fixture_worktree(&parent);
+
+ cl_git_pass(p_rename(
+ "submodules/testrepo/.gitted",
+ "submodules/testrepo/.git"));
+
+ setup_fixture_worktree(&child);
+}
+
+void test_worktree_submodule__cleanup(void)
+{
+ cleanup_fixture_worktree(&child);
+ cleanup_fixture_worktree(&parent);
+}
+
+void test_worktree_submodule__submodule_worktree_parent(void)
+{
+ cl_assert(git_repository_path(parent.worktree) != NULL);
+ cl_assert(git_repository_workdir(parent.worktree) != NULL);
+
+ cl_assert(!parent.repo->is_worktree);
+ cl_assert(parent.worktree->is_worktree);
+}
+
+void test_worktree_submodule__submodule_worktree_child(void)
+{
+ cl_assert(!parent.repo->is_worktree);
+ cl_assert(parent.worktree->is_worktree);
+ cl_assert(child.worktree->is_worktree);
+}
+
+void test_worktree_submodule__open_discovered_submodule_worktree(void)
+{
+ git_buf path = GIT_BUF_INIT;
+ git_repository *repo;
+
+ cl_git_pass(git_repository_discover(&path,
+ git_repository_workdir(child.worktree), false, NULL));
+ cl_git_pass(git_repository_open(&repo, path.ptr));
+ cl_assert_equal_s(git_repository_workdir(child.worktree),
+ git_repository_workdir(repo));
+
+ git_buf_dispose(&path);
+ git_repository_free(repo);
+}
+
+void test_worktree_submodule__resolve_relative_url(void)
+{
+ git_str wt_path = GIT_STR_INIT;
+ git_buf sm_relative_path = GIT_BUF_INIT, wt_relative_path = GIT_BUF_INIT;
+ git_repository *repo;
+ git_worktree *wt;
+
+ cl_git_pass(git_futils_mkdir("subdir", 0755, GIT_MKDIR_PATH));
+ cl_git_pass(git_fs_path_prettify_dir(&wt_path, "subdir", NULL));
+ cl_git_pass(git_str_joinpath(&wt_path, wt_path.ptr, "wt"));
+
+ /* Open child repository, which is a submodule */
+ cl_git_pass(git_repository_open(&child.repo, WORKTREE_CHILD));
+
+ /* Create worktree of submodule repository */
+ cl_git_pass(git_worktree_add(&wt, child.repo, "subdir", wt_path.ptr, NULL));
+ cl_git_pass(git_repository_open_from_worktree(&repo, wt));
+
+ cl_git_pass(git_submodule_resolve_url(&sm_relative_path, repo,
+ "../" WORKTREE_CHILD));
+ cl_git_pass(git_submodule_resolve_url(&wt_relative_path, child.repo,
+ "../" WORKTREE_CHILD));
+
+ cl_assert_equal_s(sm_relative_path.ptr, wt_relative_path.ptr);
+
+ git_worktree_free(wt);
+ git_repository_free(repo);
+ git_str_dispose(&wt_path);
+ git_buf_dispose(&sm_relative_path);
+ git_buf_dispose(&wt_relative_path);
+}
diff --git a/tests/libgit2/worktree/worktree.c b/tests/libgit2/worktree/worktree.c
new file mode 100644
index 0000000..fed5c92
--- /dev/null
+++ b/tests/libgit2/worktree/worktree.c
@@ -0,0 +1,660 @@
+#include "clar_libgit2.h"
+#include "worktree_helpers.h"
+#include "submodule/submodule_helpers.h"
+
+#include "checkout.h"
+#include "repository.h"
+#include "worktree.h"
+
+#define COMMON_REPO "testrepo"
+#define WORKTREE_REPO "testrepo-worktree"
+
+static worktree_fixture fixture =
+ WORKTREE_FIXTURE_INIT(COMMON_REPO, WORKTREE_REPO);
+
+void test_worktree_worktree__initialize(void)
+{
+ setup_fixture_worktree(&fixture);
+}
+
+void test_worktree_worktree__cleanup(void)
+{
+ cleanup_fixture_worktree(&fixture);
+}
+
+void test_worktree_worktree__list(void)
+{
+ git_strarray wts;
+
+ cl_git_pass(git_worktree_list(&wts, fixture.repo));
+ cl_assert_equal_i(wts.count, 1);
+ cl_assert_equal_s(wts.strings[0], "testrepo-worktree");
+
+ git_strarray_dispose(&wts);
+}
+
+void test_worktree_worktree__list_with_invalid_worktree_dirs(void)
+{
+ const char *filesets[3][2] = {
+ { "gitdir", "commondir" },
+ { "gitdir", "HEAD" },
+ { "HEAD", "commondir" },
+ };
+ git_str path = GIT_STR_INIT;
+ git_strarray wts;
+ size_t i, j, len;
+
+ cl_git_pass(git_str_joinpath(&path,
+ fixture.repo->commondir,
+ "worktrees/invalid"));
+ cl_git_pass(p_mkdir(path.ptr, 0755));
+
+ len = path.size;
+
+ for (i = 0; i < ARRAY_SIZE(filesets); i++) {
+
+ for (j = 0; j < ARRAY_SIZE(filesets[i]); j++) {
+ git_str_truncate(&path, len);
+ cl_git_pass(git_str_joinpath(&path, path.ptr, filesets[i][j]));
+ cl_git_pass(p_close(p_creat(path.ptr, 0644)));
+ }
+
+ cl_git_pass(git_worktree_list(&wts, fixture.worktree));
+ cl_assert_equal_i(wts.count, 1);
+ cl_assert_equal_s(wts.strings[0], "testrepo-worktree");
+ git_strarray_dispose(&wts);
+
+ for (j = 0; j < ARRAY_SIZE(filesets[i]); j++) {
+ git_str_truncate(&path, len);
+ cl_git_pass(git_str_joinpath(&path, path.ptr, filesets[i][j]));
+ p_unlink(path.ptr);
+ }
+ }
+
+ git_str_dispose(&path);
+}
+
+void test_worktree_worktree__list_in_worktree_repo(void)
+{
+ git_strarray wts;
+
+ cl_git_pass(git_worktree_list(&wts, fixture.worktree));
+ cl_assert_equal_i(wts.count, 1);
+ cl_assert_equal_s(wts.strings[0], "testrepo-worktree");
+
+ git_strarray_dispose(&wts);
+}
+
+void test_worktree_worktree__list_without_worktrees(void)
+{
+ git_repository *repo;
+ git_strarray wts;
+
+ repo = cl_git_sandbox_init("testrepo2");
+ cl_git_pass(git_worktree_list(&wts, repo));
+ cl_assert_equal_i(wts.count, 0);
+
+ git_repository_free(repo);
+}
+
+void test_worktree_worktree__lookup(void)
+{
+ git_worktree *wt;
+ git_str gitdir_path = GIT_STR_INIT;
+
+ cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+
+ cl_git_pass(git_str_joinpath(&gitdir_path, fixture.repo->commondir, "worktrees/testrepo-worktree/"));
+
+ cl_assert_equal_s(wt->gitdir_path, gitdir_path.ptr);
+ cl_assert_equal_s(wt->parent_path, fixture.repo->workdir);
+ cl_assert_equal_s(wt->gitlink_path, fixture.worktree->gitlink);
+ cl_assert_equal_s(wt->commondir_path, fixture.repo->gitdir);
+ cl_assert_equal_s(wt->commondir_path, fixture.repo->commondir);
+
+ git_str_dispose(&gitdir_path);
+ git_worktree_free(wt);
+}
+
+void test_worktree_worktree__lookup_nonexistent_worktree(void)
+{
+ git_worktree *wt;
+
+ cl_git_fail_with(GIT_ENOTFOUND, git_worktree_lookup(&wt, fixture.repo, "nonexistent"));
+ cl_assert_equal_p(wt, NULL);
+}
+
+void test_worktree_worktree__open(void)
+{
+ git_worktree *wt;
+ git_repository *repo;
+
+ cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+
+ cl_git_pass(git_repository_open_from_worktree(&repo, wt));
+ cl_assert_equal_s(git_repository_workdir(repo),
+ git_repository_workdir(fixture.worktree));
+
+ git_repository_free(repo);
+ git_worktree_free(wt);
+}
+
+void test_worktree_worktree__open_invalid_commondir(void)
+{
+ git_worktree *wt;
+ git_repository *repo;
+ git_str buf = GIT_STR_INIT, path = GIT_STR_INIT;
+
+ cl_git_pass(git_str_sets(&buf, "/path/to/nonexistent/commondir"));
+ cl_git_pass(git_str_joinpath(&path,
+ fixture.repo->commondir,
+ "worktrees/testrepo-worktree/commondir"));
+ cl_git_pass(git_futils_writebuffer(&buf, path.ptr, O_RDWR, 0644));
+
+ cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+ cl_git_fail(git_repository_open_from_worktree(&repo, wt));
+
+ git_str_dispose(&buf);
+ git_str_dispose(&path);
+ git_worktree_free(wt);
+}
+
+void test_worktree_worktree__open_invalid_gitdir(void)
+{
+ git_worktree *wt;
+ git_repository *repo;
+ git_str buf = GIT_STR_INIT, path = GIT_STR_INIT;
+
+ cl_git_pass(git_str_sets(&buf, "/path/to/nonexistent/gitdir"));
+ cl_git_pass(git_str_joinpath(&path,
+ fixture.repo->commondir,
+ "worktrees/testrepo-worktree/gitdir"));
+ cl_git_pass(git_futils_writebuffer(&buf, path.ptr, O_RDWR, 0644));
+
+ cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+ cl_git_fail(git_repository_open_from_worktree(&repo, wt));
+
+ git_str_dispose(&buf);
+ git_str_dispose(&path);
+ git_worktree_free(wt);
+}
+
+void test_worktree_worktree__open_invalid_parent(void)
+{
+ git_worktree *wt;
+ git_repository *repo;
+ git_str buf = GIT_STR_INIT;
+
+ cl_git_pass(git_str_sets(&buf, "/path/to/nonexistent/gitdir"));
+ cl_git_pass(git_futils_writebuffer(&buf,
+ fixture.worktree->gitlink, O_RDWR, 0644));
+
+ cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+ cl_git_fail(git_repository_open_from_worktree(&repo, wt));
+
+ git_str_dispose(&buf);
+ git_worktree_free(wt);
+}
+
+void test_worktree_worktree__init(void)
+{
+ git_worktree *wt;
+ git_repository *repo;
+ git_reference *branch;
+ git_str path = GIT_STR_INIT;
+
+ cl_git_pass(git_str_joinpath(&path, fixture.repo->workdir, "../worktree-new"));
+ cl_git_pass(git_worktree_add(&wt, fixture.repo, "worktree-new", path.ptr, NULL));
+
+ /* Open and verify created repo */
+ cl_git_pass(git_repository_open(&repo, path.ptr));
+ cl_assert(git__suffixcmp(git_repository_workdir(repo), "worktree-new/") == 0);
+ cl_git_pass(git_branch_lookup(&branch, repo, "worktree-new", GIT_BRANCH_LOCAL));
+
+ git_str_dispose(&path);
+ git_worktree_free(wt);
+ git_reference_free(branch);
+ git_repository_free(repo);
+}
+
+void test_worktree_worktree__add_locked(void)
+{
+ git_worktree *wt;
+ git_repository *repo;
+ git_reference *branch;
+ git_str path = GIT_STR_INIT;
+ git_worktree_add_options opts = GIT_WORKTREE_ADD_OPTIONS_INIT;
+
+ opts.lock = 1;
+
+ cl_git_pass(git_str_joinpath(&path, fixture.repo->workdir, "../worktree-locked"));
+ cl_git_pass(git_worktree_add(&wt, fixture.repo, "worktree-locked", path.ptr, &opts));
+
+ /* Open and verify created repo */
+ cl_assert(git_worktree_is_locked(NULL, wt));
+ cl_git_pass(git_repository_open(&repo, path.ptr));
+ cl_assert(git__suffixcmp(git_repository_workdir(repo), "worktree-locked/") == 0);
+ cl_git_pass(git_branch_lookup(&branch, repo, "worktree-locked", GIT_BRANCH_LOCAL));
+
+ git_str_dispose(&path);
+ git_worktree_free(wt);
+ git_reference_free(branch);
+ git_repository_free(repo);
+}
+
+void test_worktree_worktree__init_existing_branch(void)
+{
+ git_reference *head, *branch;
+ git_commit *commit;
+ git_worktree *wt;
+ git_str path = GIT_STR_INIT;
+
+ cl_git_pass(git_repository_head(&head, fixture.repo));
+ cl_git_pass(git_commit_lookup(&commit, fixture.repo, &head->target.oid));
+ cl_git_pass(git_branch_create(&branch, fixture.repo, "worktree-new", commit, false));
+
+ cl_git_pass(git_str_joinpath(&path, fixture.repo->workdir, "../worktree-new"));
+ cl_git_fail(git_worktree_add(&wt, fixture.repo, "worktree-new", path.ptr, NULL));
+
+ git_str_dispose(&path);
+ git_commit_free(commit);
+ git_reference_free(head);
+ git_reference_free(branch);
+}
+
+void test_worktree_worktree__add_with_explicit_branch(void)
+{
+ git_reference *head, *branch, *wthead;
+ git_commit *commit;
+ git_worktree *wt;
+ git_repository *wtrepo;
+ git_str path = GIT_STR_INIT;
+ git_worktree_add_options opts = GIT_WORKTREE_ADD_OPTIONS_INIT;
+
+ cl_git_pass(git_repository_head(&head, fixture.repo));
+ cl_git_pass(git_commit_lookup(&commit, fixture.repo, &head->target.oid));
+ cl_git_pass(git_branch_create(&branch, fixture.repo, "worktree-with-ref", commit, false));
+
+ opts.ref = branch;
+
+ cl_git_pass(git_str_joinpath(&path, fixture.repo->workdir, "../worktree-with-different-name"));
+ cl_git_pass(git_worktree_add(&wt, fixture.repo, "worktree-with-different-name", path.ptr, &opts));
+ cl_git_pass(git_repository_open_from_worktree(&wtrepo, wt));
+ cl_git_pass(git_repository_head(&wthead, wtrepo));
+ cl_assert_equal_s(git_reference_name(wthead), "refs/heads/worktree-with-ref");
+
+ git_str_dispose(&path);
+ git_commit_free(commit);
+ git_reference_free(head);
+ git_reference_free(branch);
+ git_reference_free(wthead);
+ git_repository_free(wtrepo);
+ git_worktree_free(wt);
+}
+
+void test_worktree_worktree__add_no_checkout(void)
+{
+ git_worktree *wt;
+ git_repository *wtrepo;
+ git_index *index;
+ git_str path = GIT_STR_INIT;
+ git_worktree_add_options opts = GIT_WORKTREE_ADD_OPTIONS_INIT;
+
+ opts.checkout_options.checkout_strategy = GIT_CHECKOUT_NONE;
+
+ cl_git_pass(git_str_joinpath(&path, fixture.repo->workdir, "../worktree-no-checkout"));
+ cl_git_pass(git_worktree_add(&wt, fixture.repo, "worktree-no-checkout", path.ptr, &opts));
+
+ cl_git_pass(git_repository_open(&wtrepo, path.ptr));
+ cl_git_pass(git_repository_index(&index, wtrepo));
+ cl_assert_equal_i(git_index_entrycount(index), 0);
+
+ git_str_dispose(&path);
+ git_worktree_free(wt);
+ git_index_free(index);
+ git_repository_free(wtrepo);
+}
+
+void test_worktree_worktree__init_existing_worktree(void)
+{
+ git_worktree *wt;
+ git_str path = GIT_STR_INIT;
+
+ cl_git_pass(git_str_joinpath(&path, fixture.repo->workdir, "../worktree-new"));
+ cl_git_fail(git_worktree_add(&wt, fixture.repo, "testrepo-worktree", path.ptr, NULL));
+
+ cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+ cl_assert_equal_s(wt->gitlink_path, fixture.worktree->gitlink);
+
+ git_str_dispose(&path);
+ git_worktree_free(wt);
+}
+
+void test_worktree_worktree__init_existing_path(void)
+{
+ const char *wtfiles[] = { "HEAD", "commondir", "gitdir", "index" };
+ git_worktree *wt;
+ git_str path = GIT_STR_INIT;
+ unsigned i;
+
+ /* Delete files to verify they have not been created by
+ * the init call */
+ for (i = 0; i < ARRAY_SIZE(wtfiles); i++) {
+ cl_git_pass(git_str_joinpath(&path,
+ fixture.worktree->gitdir, wtfiles[i]));
+ cl_git_pass(p_unlink(path.ptr));
+ }
+
+ cl_git_pass(git_str_joinpath(&path, fixture.repo->workdir, "../testrepo-worktree"));
+ cl_git_fail(git_worktree_add(&wt, fixture.repo, "worktree-new", path.ptr, NULL));
+
+ /* Verify files have not been re-created */
+ for (i = 0; i < ARRAY_SIZE(wtfiles); i++) {
+ cl_git_pass(git_str_joinpath(&path,
+ fixture.worktree->gitdir, wtfiles[i]));
+ cl_assert(!git_fs_path_exists(path.ptr));
+ }
+
+ git_str_dispose(&path);
+}
+
+void test_worktree_worktree__init_submodule(void)
+{
+ git_repository *repo, *sm, *wt;
+ git_worktree *worktree;
+ git_str path = GIT_STR_INIT;
+
+ cleanup_fixture_worktree(&fixture);
+ repo = setup_fixture_submod2();
+
+ cl_git_pass(git_str_joinpath(&path, repo->workdir, "sm_unchanged"));
+ cl_git_pass(git_repository_open(&sm, path.ptr));
+ cl_git_pass(git_str_joinpath(&path, repo->workdir, "../worktree/"));
+ cl_git_pass(git_worktree_add(&worktree, sm, "repo-worktree", path.ptr, NULL));
+ cl_git_pass(git_repository_open_from_worktree(&wt, worktree));
+
+ cl_git_pass(git_fs_path_prettify_dir(&path, path.ptr, NULL));
+ cl_assert_equal_s(path.ptr, wt->workdir);
+ cl_git_pass(git_fs_path_prettify_dir(&path, sm->commondir, NULL));
+ cl_assert_equal_s(sm->commondir, wt->commondir);
+
+ cl_git_pass(git_str_joinpath(&path, sm->gitdir, "worktrees/repo-worktree/"));
+ cl_assert_equal_s(path.ptr, wt->gitdir);
+
+ git_str_dispose(&path);
+ git_worktree_free(worktree);
+ git_repository_free(sm);
+ git_repository_free(wt);
+}
+
+void test_worktree_worktree__validate(void)
+{
+ git_worktree *wt;
+
+ cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+ cl_git_pass(git_worktree_validate(wt));
+
+ git_worktree_free(wt);
+}
+
+void test_worktree_worktree__name(void)
+{
+ git_worktree *wt;
+
+ cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+ cl_assert_equal_s(git_worktree_name(wt), "testrepo-worktree");
+
+ git_worktree_free(wt);
+}
+
+void test_worktree_worktree__path(void)
+{
+ git_worktree *wt;
+ git_str expected_path = GIT_STR_INIT;
+
+ cl_git_pass(git_str_joinpath(&expected_path, clar_sandbox_path(), "testrepo-worktree"));
+ cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+ cl_assert_equal_s(git_worktree_path(wt), expected_path.ptr);
+
+ git_str_dispose(&expected_path);
+ git_worktree_free(wt);
+}
+
+void test_worktree_worktree__validate_invalid_commondir(void)
+{
+ git_worktree *wt;
+
+ cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+ git__free(wt->commondir_path);
+ wt->commondir_path = "/path/to/invalid/commondir";
+
+ cl_git_fail(git_worktree_validate(wt));
+
+ wt->commondir_path = NULL;
+ git_worktree_free(wt);
+}
+
+void test_worktree_worktree__validate_invalid_gitdir(void)
+{
+ git_worktree *wt;
+
+ cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+ git__free(wt->gitdir_path);
+ wt->gitdir_path = "/path/to/invalid/gitdir";
+ cl_git_fail(git_worktree_validate(wt));
+
+ wt->gitdir_path = NULL;
+ git_worktree_free(wt);
+}
+
+void test_worktree_worktree__validate_invalid_parent(void)
+{
+ git_worktree *wt;
+
+ cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+ git__free(wt->parent_path);
+ wt->parent_path = "/path/to/invalid/parent";
+ cl_git_fail(git_worktree_validate(wt));
+
+ wt->parent_path = NULL;
+ git_worktree_free(wt);
+}
+
+void test_worktree_worktree__lock_with_reason(void)
+{
+ git_worktree *wt;
+ git_buf reason = GIT_BUF_INIT;
+
+ cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+
+ cl_assert(!git_worktree_is_locked(NULL, wt));
+ cl_git_pass(git_worktree_lock(wt, "because"));
+ cl_assert(git_worktree_is_locked(&reason, wt) > 0);
+ cl_assert_equal_s(reason.ptr, "because");
+ cl_assert(wt->locked);
+
+ git_buf_dispose(&reason);
+ git_worktree_free(wt);
+}
+
+void test_worktree_worktree__lock_without_reason(void)
+{
+ git_worktree *wt;
+ git_buf reason = GIT_BUF_INIT;
+
+ cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+
+ cl_assert(!git_worktree_is_locked(NULL, wt));
+ cl_git_pass(git_worktree_lock(wt, NULL));
+ cl_assert(git_worktree_is_locked(&reason, wt) > 0);
+ cl_assert_equal_i(reason.size, 0);
+ cl_assert(wt->locked);
+
+ git_buf_dispose(&reason);
+ git_worktree_free(wt);
+}
+
+void test_worktree_worktree__unlock_unlocked_worktree(void)
+{
+ git_worktree *wt;
+
+ cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+ cl_assert(!git_worktree_is_locked(NULL, wt));
+ cl_assert_equal_i(1, git_worktree_unlock(wt));
+ cl_assert(!wt->locked);
+
+ git_worktree_free(wt);
+}
+
+void test_worktree_worktree__unlock_locked_worktree(void)
+{
+ git_worktree *wt;
+
+ cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+ cl_git_pass(git_worktree_lock(wt, NULL));
+ cl_assert(git_worktree_is_locked(NULL, wt));
+ cl_assert_equal_i(0, git_worktree_unlock(wt));
+ cl_assert(!wt->locked);
+
+ git_worktree_free(wt);
+}
+
+void test_worktree_worktree__prune_without_opts_fails(void)
+{
+ git_worktree *wt;
+ git_repository *repo;
+
+ cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+ cl_git_fail(git_worktree_prune(wt, NULL));
+
+ /* Assert the repository is still valid */
+ cl_git_pass(git_repository_open_from_worktree(&repo, wt));
+
+ git_worktree_free(wt);
+ git_repository_free(repo);
+}
+
+void test_worktree_worktree__prune_valid(void)
+{
+ git_worktree_prune_options opts = GIT_WORKTREE_PRUNE_OPTIONS_INIT;
+ git_worktree *wt;
+ git_repository *repo;
+
+ opts.flags = GIT_WORKTREE_PRUNE_VALID;
+
+ cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+ cl_git_pass(git_worktree_prune(wt, &opts));
+
+ /* Assert the repository is not valid anymore */
+ cl_git_fail(git_repository_open_from_worktree(&repo, wt));
+
+ git_worktree_free(wt);
+ git_repository_free(repo);
+}
+
+void test_worktree_worktree__prune_locked(void)
+{
+ git_worktree_prune_options opts = GIT_WORKTREE_PRUNE_OPTIONS_INIT;
+ git_worktree *wt;
+ git_repository *repo;
+
+ cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+ cl_git_pass(git_worktree_lock(wt, NULL));
+
+ opts.flags = GIT_WORKTREE_PRUNE_VALID;
+ cl_git_fail(git_worktree_prune(wt, &opts));
+ /* Assert the repository is still valid */
+ cl_git_pass(git_repository_open_from_worktree(&repo, wt));
+
+ opts.flags = GIT_WORKTREE_PRUNE_VALID|GIT_WORKTREE_PRUNE_LOCKED;
+ cl_git_pass(git_worktree_prune(wt, &opts));
+
+ git_worktree_free(wt);
+ git_repository_free(repo);
+}
+
+void test_worktree_worktree__prune_gitdir_only(void)
+{
+ git_worktree_prune_options opts = GIT_WORKTREE_PRUNE_OPTIONS_INIT;
+ git_worktree *wt;
+
+ opts.flags = GIT_WORKTREE_PRUNE_VALID;
+ cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+ cl_git_pass(git_worktree_prune(wt, &opts));
+
+ cl_assert(!git_fs_path_exists(wt->gitdir_path));
+ cl_assert(git_fs_path_exists(wt->gitlink_path));
+
+ git_worktree_free(wt);
+}
+
+void test_worktree_worktree__prune_worktree(void)
+{
+ git_worktree_prune_options opts = GIT_WORKTREE_PRUNE_OPTIONS_INIT;
+ git_worktree *wt;
+
+ opts.flags = GIT_WORKTREE_PRUNE_VALID|GIT_WORKTREE_PRUNE_WORKING_TREE;
+
+ cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+ cl_git_pass(git_worktree_prune(wt, &opts));
+
+ cl_assert(!git_fs_path_exists(wt->gitdir_path));
+ cl_assert(!git_fs_path_exists(wt->gitlink_path));
+
+ git_worktree_free(wt);
+}
+
+static int foreach_worktree_cb(git_repository *worktree, void *payload)
+{
+ int *counter = (int *)payload;
+
+ switch (*counter) {
+ case 0:
+ cl_assert_equal_s(git_repository_path(fixture.repo),
+ git_repository_path(worktree));
+ cl_assert(!git_repository_is_worktree(worktree));
+ break;
+ case 1:
+ cl_assert_equal_s(git_repository_path(fixture.worktree),
+ git_repository_path(worktree));
+ cl_assert(git_repository_is_worktree(worktree));
+ break;
+ default:
+ cl_fail("more worktrees found than expected");
+ }
+
+ (*counter)++;
+
+ return 0;
+}
+
+void test_worktree_worktree__foreach_worktree_lists_all_worktrees(void)
+{
+ int counter = 0;
+ cl_git_pass(git_repository_foreach_worktree(fixture.repo, foreach_worktree_cb, &counter));
+}
+
+void test_worktree_worktree__validate_invalid_worktreedir(void)
+{
+ git_worktree *wt;
+
+ cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+ p_rename("testrepo-worktree", "testrepo-worktree-tmp");
+ cl_git_fail(git_worktree_validate(wt));
+ p_rename("testrepo-worktree-tmp", "testrepo-worktree");
+
+ git_worktree_free(wt);
+}
+
+void test_worktree_worktree__is_prunable_missing_repo(void)
+{
+ git_worktree *wt;
+
+ cl_git_pass(git_worktree_lookup(&wt, fixture.repo, "testrepo-worktree"));
+ p_rename("testrepo", "testrepo-tmp");
+ /* Should not be prunable since the repository moved */
+ cl_assert(!git_worktree_is_prunable(wt, NULL));
+ p_rename("testrepo-tmp", "testrepo");
+
+ git_worktree_free(wt);
+}
diff --git a/tests/libgit2/worktree/worktree_helpers.c b/tests/libgit2/worktree/worktree_helpers.c
new file mode 100644
index 0000000..6d4cdba
--- /dev/null
+++ b/tests/libgit2/worktree/worktree_helpers.c
@@ -0,0 +1,30 @@
+#include "clar_libgit2.h"
+#include "worktree_helpers.h"
+
+void cleanup_fixture_worktree(worktree_fixture *fixture)
+{
+ if (!fixture)
+ return;
+
+ if (fixture->repo) {
+ git_repository_free(fixture->repo);
+ fixture->repo = NULL;
+ }
+ if (fixture->worktree) {
+ git_repository_free(fixture->worktree);
+ fixture->worktree = NULL;
+ }
+
+ if (fixture->reponame)
+ cl_fixture_cleanup(fixture->reponame);
+ if (fixture->worktreename)
+ cl_fixture_cleanup(fixture->worktreename);
+}
+
+void setup_fixture_worktree(worktree_fixture *fixture)
+{
+ if (fixture->reponame)
+ fixture->repo = cl_git_sandbox_init(fixture->reponame);
+ if (fixture->worktreename)
+ fixture->worktree = cl_git_sandbox_init(fixture->worktreename);
+}
diff --git a/tests/libgit2/worktree/worktree_helpers.h b/tests/libgit2/worktree/worktree_helpers.h
new file mode 100644
index 0000000..35ea9ed
--- /dev/null
+++ b/tests/libgit2/worktree/worktree_helpers.h
@@ -0,0 +1,11 @@
+typedef struct {
+ const char *reponame;
+ const char *worktreename;
+ git_repository *repo;
+ git_repository *worktree;
+} worktree_fixture;
+
+#define WORKTREE_FIXTURE_INIT(repo, worktree) { (repo), (worktree), NULL, NULL }
+
+void cleanup_fixture_worktree(worktree_fixture *fixture);
+void setup_fixture_worktree(worktree_fixture *fixture);
diff --git a/tests/resources/.gitattributes b/tests/resources/.gitattributes
new file mode 100644
index 0000000..047f77f
--- /dev/null
+++ b/tests/resources/.gitattributes
@@ -0,0 +1,2 @@
+* binary
+*.sh text diff merge eol=lf
diff --git a/tests/resources/.gitignore b/tests/resources/.gitignore
new file mode 100644
index 0000000..43a19cc
--- /dev/null
+++ b/tests/resources/.gitignore
@@ -0,0 +1 @@
+discover.git
diff --git a/tests/resources/attr/.gitted/HEAD b/tests/resources/attr/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/attr/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/attr/.gitted/config b/tests/resources/attr/.gitted/config
new file mode 100644
index 0000000..af10792
--- /dev/null
+++ b/tests/resources/attr/.gitted/config
@@ -0,0 +1,6 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
diff --git a/tests/resources/attr/.gitted/description b/tests/resources/attr/.gitted/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/attr/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/attr/.gitted/index b/tests/resources/attr/.gitted/index
new file mode 100644
index 0000000..439ffb1
--- /dev/null
+++ b/tests/resources/attr/.gitted/index
Binary files differ
diff --git a/tests/resources/attr/.gitted/info/attributes b/tests/resources/attr/.gitted/info/attributes
new file mode 100644
index 0000000..5fe62a3
--- /dev/null
+++ b/tests/resources/attr/.gitted/info/attributes
@@ -0,0 +1,4 @@
+* repoattr
+a* foo !bar -baz
+sub/*.txt reposub
+sub/sub/*.txt reposubsub
diff --git a/tests/resources/attr/.gitted/info/exclude b/tests/resources/attr/.gitted/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/attr/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/attr/.gitted/logs/HEAD b/tests/resources/attr/.gitted/logs/HEAD
new file mode 100644
index 0000000..8ece39f
--- /dev/null
+++ b/tests/resources/attr/.gitted/logs/HEAD
@@ -0,0 +1,9 @@
+0000000000000000000000000000000000000000 6bab5c79cd5140d0f800917f550eb2a3dc32b0da Russell Belfer <arrbee@arrbee.com> 1324416995 -0800 commit (initial): initial test data
+6bab5c79cd5140d0f800917f550eb2a3dc32b0da 605812ab7fe421fdd325a935d35cb06a9234a7d7 Russell Belfer <arrbee@arrbee.com> 1325143098 -0800 commit: latest test updates
+605812ab7fe421fdd325a935d35cb06a9234a7d7 a5d76cad53f66f1312bd995909a5bab3c0820770 Russell Belfer <arrbee@arrbee.com> 1325281762 -0800 commit: more macro tests
+a5d76cad53f66f1312bd995909a5bab3c0820770 370fe9ec224ce33e71f9e5ec2bd1142ce9937a6a Russell Belfer <arrbee@arrbee.com> 1327611749 -0800 commit: Updating files so we can do diffs
+370fe9ec224ce33e71f9e5ec2bd1142ce9937a6a f5b0af1fb4f5c0cd7aad880711d368a07333c307 Russell Belfer <arrbee@arrbee.com> 1327621027 -0800 commit: Updating test data
+f5b0af1fb4f5c0cd7aad880711d368a07333c307 a97cc019851d401a4f1d091cb91a15890a0dd1ba Russell Belfer <arrbee@arrbee.com> 1328653313 -0800 commit: Some whitespace only changes for testing purposes
+a97cc019851d401a4f1d091cb91a15890a0dd1ba 217878ab49e1314388ea2e32dc6fdb58a1b969e0 Russell Belfer <arrbee@arrbee.com> 1332734901 -0700 commit: added files in sub/sub
+217878ab49e1314388ea2e32dc6fdb58a1b969e0 24fa9a9fc4e202313e24b648087495441dab432b Russell Belfer <arrbee@arrbee.com> 1332735555 -0700 commit: adding more files in sub for tree status
+24fa9a9fc4e202313e24b648087495441dab432b 8d0b9df9bd30be7910ddda60548d485bc302b911 yorah <yoram.harmelin@gmail.com> 1341230701 +0200 commit: Updating test data so we can test inter-hunk-context
diff --git a/tests/resources/attr/.gitted/logs/refs/heads/master b/tests/resources/attr/.gitted/logs/refs/heads/master
new file mode 100644
index 0000000..8ece39f
--- /dev/null
+++ b/tests/resources/attr/.gitted/logs/refs/heads/master
@@ -0,0 +1,9 @@
+0000000000000000000000000000000000000000 6bab5c79cd5140d0f800917f550eb2a3dc32b0da Russell Belfer <arrbee@arrbee.com> 1324416995 -0800 commit (initial): initial test data
+6bab5c79cd5140d0f800917f550eb2a3dc32b0da 605812ab7fe421fdd325a935d35cb06a9234a7d7 Russell Belfer <arrbee@arrbee.com> 1325143098 -0800 commit: latest test updates
+605812ab7fe421fdd325a935d35cb06a9234a7d7 a5d76cad53f66f1312bd995909a5bab3c0820770 Russell Belfer <arrbee@arrbee.com> 1325281762 -0800 commit: more macro tests
+a5d76cad53f66f1312bd995909a5bab3c0820770 370fe9ec224ce33e71f9e5ec2bd1142ce9937a6a Russell Belfer <arrbee@arrbee.com> 1327611749 -0800 commit: Updating files so we can do diffs
+370fe9ec224ce33e71f9e5ec2bd1142ce9937a6a f5b0af1fb4f5c0cd7aad880711d368a07333c307 Russell Belfer <arrbee@arrbee.com> 1327621027 -0800 commit: Updating test data
+f5b0af1fb4f5c0cd7aad880711d368a07333c307 a97cc019851d401a4f1d091cb91a15890a0dd1ba Russell Belfer <arrbee@arrbee.com> 1328653313 -0800 commit: Some whitespace only changes for testing purposes
+a97cc019851d401a4f1d091cb91a15890a0dd1ba 217878ab49e1314388ea2e32dc6fdb58a1b969e0 Russell Belfer <arrbee@arrbee.com> 1332734901 -0700 commit: added files in sub/sub
+217878ab49e1314388ea2e32dc6fdb58a1b969e0 24fa9a9fc4e202313e24b648087495441dab432b Russell Belfer <arrbee@arrbee.com> 1332735555 -0700 commit: adding more files in sub for tree status
+24fa9a9fc4e202313e24b648087495441dab432b 8d0b9df9bd30be7910ddda60548d485bc302b911 yorah <yoram.harmelin@gmail.com> 1341230701 +0200 commit: Updating test data so we can test inter-hunk-context
diff --git a/tests/resources/attr/.gitted/objects/10/8bb4e7fd7b16490dc33ff7d972151e73d7166e b/tests/resources/attr/.gitted/objects/10/8bb4e7fd7b16490dc33ff7d972151e73d7166e
new file mode 100644
index 0000000..edcf752
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/10/8bb4e7fd7b16490dc33ff7d972151e73d7166e
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/16/983da6643656bb44c43965ecb6855c6d574512 b/tests/resources/attr/.gitted/objects/16/983da6643656bb44c43965ecb6855c6d574512
new file mode 100644
index 0000000..e49c94a
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/16/983da6643656bb44c43965ecb6855c6d574512
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/21/7878ab49e1314388ea2e32dc6fdb58a1b969e0 b/tests/resources/attr/.gitted/objects/21/7878ab49e1314388ea2e32dc6fdb58a1b969e0
new file mode 100644
index 0000000..b537899
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/21/7878ab49e1314388ea2e32dc6fdb58a1b969e0
@@ -0,0 +1,4 @@
+xŽQ
+Â0DýÎ)öên“ØDÄ#xƒmvƒ…ÖJ’Þ߀7ðcx0¼IۺΠ­¨‚óž-¹ÌÁñ+e"¼vù‚Á‡œâ˜ùpÑwŽcJH1x‡Ô%Œ”¦HL>Dd¡‰ ïíµxîµê²ÀC—¬®\ʤzÿᔶõdí0Z‘àˆ#¢émÿغþÏÚ°ˆ
+äyÑ
+óê>{Ì–qK² \ No newline at end of file
diff --git a/tests/resources/attr/.gitted/objects/24/fa9a9fc4e202313e24b648087495441dab432b b/tests/resources/attr/.gitted/objects/24/fa9a9fc4e202313e24b648087495441dab432b
new file mode 100644
index 0000000..e7099bb
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/24/fa9a9fc4e202313e24b648087495441dab432b
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/29/29de282ce999e95183aedac6451d3384559c4b b/tests/resources/attr/.gitted/objects/29/29de282ce999e95183aedac6451d3384559c4b
new file mode 100644
index 0000000..ad84f08
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/29/29de282ce999e95183aedac6451d3384559c4b
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/2b/40c5aca159b04ea8d20ffe36cdf8b09369b14a b/tests/resources/attr/.gitted/objects/2b/40c5aca159b04ea8d20ffe36cdf8b09369b14a
new file mode 100644
index 0000000..0e23680
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/2b/40c5aca159b04ea8d20ffe36cdf8b09369b14a
@@ -0,0 +1 @@
+xmPÑj„0ì«ùŠ=úP8Z…ÞS¾¤”c£ñ hR6{=¼¯obâ™Ò"šÙafvŒšœ‚÷×æéä#3‰ά=7Pÿ%[8<Heì`&]@?aFZª®ªç@!…÷.ÊÈù:±ù½§…•ldðÚLG­|K’7~XÃN8¤˜·IÏdìê}¿øˆqŸó2cG¾œ7l¾5ÔV_pEûŸ®’#lZ´ŽGMòt[JÌÈͽ¥&©h¸±uÌ][‰Öß4‰-3;ËCˆg˜4¿x`ZÀ»YÃŒ“錻ú€bÝ^>ï yNlÍ£¡>c¯;gÓ•¥kÇYXÄ9b|Dª~VØ—)…v¿øñÎÜ• \ No newline at end of file
diff --git a/tests/resources/attr/.gitted/objects/2c/66e14f77196ea763fb1e41612c1aa2bc2d8ed2 b/tests/resources/attr/.gitted/objects/2c/66e14f77196ea763fb1e41612c1aa2bc2d8ed2
new file mode 100644
index 0000000..4b75d50
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/2c/66e14f77196ea763fb1e41612c1aa2bc2d8ed2
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/2d/e7dfe3588f3c7e9ad59e7d50ba90e3329df9d9 b/tests/resources/attr/.gitted/objects/2d/e7dfe3588f3c7e9ad59e7d50ba90e3329df9d9
new file mode 100644
index 0000000..e0fd046
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/2d/e7dfe3588f3c7e9ad59e7d50ba90e3329df9d9
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/37/0fe9ec224ce33e71f9e5ec2bd1142ce9937a6a b/tests/resources/attr/.gitted/objects/37/0fe9ec224ce33e71f9e5ec2bd1142ce9937a6a
new file mode 100644
index 0000000..9c37c59
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/37/0fe9ec224ce33e71f9e5ec2bd1142ce9937a6a
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/3a/6df026462ebafe455af9867d27eda20a9e0974 b/tests/resources/attr/.gitted/objects/3a/6df026462ebafe455af9867d27eda20a9e0974
new file mode 100644
index 0000000..c74add8
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/3a/6df026462ebafe455af9867d27eda20a9e0974
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/3b/74db7ab381105dc0d28f8295a77f6a82989292 b/tests/resources/attr/.gitted/objects/3b/74db7ab381105dc0d28f8295a77f6a82989292
new file mode 100644
index 0000000..e5cef35
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/3b/74db7ab381105dc0d28f8295a77f6a82989292
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/3e/42ffc54a663f9401cc25843d6c0e71a33e4249 b/tests/resources/attr/.gitted/objects/3e/42ffc54a663f9401cc25843d6c0e71a33e4249
new file mode 100644
index 0000000..091d79b
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/3e/42ffc54a663f9401cc25843d6c0e71a33e4249
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/45/141a79a77842c59a63229403220a4e4be74e3d b/tests/resources/attr/.gitted/objects/45/141a79a77842c59a63229403220a4e4be74e3d
new file mode 100644
index 0000000..5b58ef0
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/45/141a79a77842c59a63229403220a4e4be74e3d
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/45/5a314fa848d52ae1f11d254da4f60858fc97f4 b/tests/resources/attr/.gitted/objects/45/5a314fa848d52ae1f11d254da4f60858fc97f4
new file mode 100644
index 0000000..f90f0d7
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/45/5a314fa848d52ae1f11d254da4f60858fc97f4
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests/resources/attr/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
new file mode 100644
index 0000000..7ca4cee
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/4d/713dc48e6b1bd75b0d61ad078ba9ca3a56745d b/tests/resources/attr/.gitted/objects/4d/713dc48e6b1bd75b0d61ad078ba9ca3a56745d
new file mode 100644
index 0000000..eb1e8d0
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/4d/713dc48e6b1bd75b0d61ad078ba9ca3a56745d
@@ -0,0 +1,2 @@
+xÁÁ € @ßWŶàÇ
+|ø§k 9n$¡}gŠ«à:‡îÂ;5°1¥e–4ˆ\k_]‘ÞƒŸÙ­hœD¡k›ý'~ \ No newline at end of file
diff --git a/tests/resources/attr/.gitted/objects/4e/49ba8c5b6c32ff28cd9dcb60be34df50fcc485 b/tests/resources/attr/.gitted/objects/4e/49ba8c5b6c32ff28cd9dcb60be34df50fcc485
new file mode 100644
index 0000000..6fcc549
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/4e/49ba8c5b6c32ff28cd9dcb60be34df50fcc485
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/55/6f8c827b8e4a02ad5cab77dca2bcb3e226b0b3 b/tests/resources/attr/.gitted/objects/55/6f8c827b8e4a02ad5cab77dca2bcb3e226b0b3
new file mode 100644
index 0000000..4bcff1f
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/55/6f8c827b8e4a02ad5cab77dca2bcb3e226b0b3
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/58/19a185d77b03325aaf87cafc771db36f6ddca7 b/tests/resources/attr/.gitted/objects/58/19a185d77b03325aaf87cafc771db36f6ddca7
new file mode 100644
index 0000000..fe34eb6
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/58/19a185d77b03325aaf87cafc771db36f6ddca7
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/60/5812ab7fe421fdd325a935d35cb06a9234a7d7 b/tests/resources/attr/.gitted/objects/60/5812ab7fe421fdd325a935d35cb06a9234a7d7
new file mode 100644
index 0000000..b0cc51e
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/60/5812ab7fe421fdd325a935d35cb06a9234a7d7
@@ -0,0 +1,2 @@
+xNÛ Â0ã;SÜ Ë»•BŒÀ—ËU ¥´JÓý °?¶lÙ–y™çgcáU RbaâÙcG;¸l²ã DqÖ Z©Ê«AH”<Ç‘³×3Nâ¨ãä=J2d3[“0“¢½=–
+÷}Û¤¸I™¤Â™jM"×x™/ ­é[ÇŽØçTwûÇÖãÿ´U¡&[ƒ/ìkþ(õtJL \ No newline at end of file
diff --git a/tests/resources/attr/.gitted/objects/6b/ab5c79cd5140d0f800917f550eb2a3dc32b0da b/tests/resources/attr/.gitted/objects/6b/ab5c79cd5140d0f800917f550eb2a3dc32b0da
new file mode 100644
index 0000000..f51e11c
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/6b/ab5c79cd5140d0f800917f550eb2a3dc32b0da
@@ -0,0 +1,3 @@
+xÛ Ã0 Eûí)´@‹d'~@(¥#tÅQ¨ÁiÀQö¯¡ôëÂánÞ·­(Pôm"Ř2æh°s L+d{—"{Zœ“`øÔ÷Þàu‡Ô
+O©«4˜¸µYäñ›[Þ·;³Ã@>¥®M§ýS»þOmʧhá
+*‡ÂÂÊæ ¿<- \ No newline at end of file
diff --git a/tests/resources/attr/.gitted/objects/6d/968d62c89c7d9ea23a4c9a7b665d017c3d8ffd b/tests/resources/attr/.gitted/objects/6d/968d62c89c7d9ea23a4c9a7b665d017c3d8ffd
new file mode 100644
index 0000000..e832241
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/6d/968d62c89c7d9ea23a4c9a7b665d017c3d8ffd
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/71/7fc31f6b84f9d6fc3a4edbca259d7fc92beee2 b/tests/resources/attr/.gitted/objects/71/7fc31f6b84f9d6fc3a4edbca259d7fc92beee2
new file mode 100644
index 0000000..a80265c
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/71/7fc31f6b84f9d6fc3a4edbca259d7fc92beee2
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/8d/0b9df9bd30be7910ddda60548d485bc302b911 b/tests/resources/attr/.gitted/objects/8d/0b9df9bd30be7910ddda60548d485bc302b911
new file mode 100644
index 0000000..3dcf088
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/8d/0b9df9bd30be7910ddda60548d485bc302b911
@@ -0,0 +1 @@
+xŽKj1D³Ö)zolôiõŒ _"hiÚK2²L’ÛG!7Ȫ¯¨ÔJÉ,ù—ÑEÀPXÝÆDèÈSŒˆ ] /)Òê}¢Í/èUwîR§ˆ. Åj댋‘pÕë‚Á#š#:?ÇÞ:|·Î;¼þF9íÜ‹Ür=_ çÛ)µòÆ¡±N/ÚÀA[­ÕlçÃ!ÿqÕû}ã‘ë†<Lfx4øH\ÿº\çôqÖcj“¿†úƒTè \ No newline at end of file
diff --git a/tests/resources/attr/.gitted/objects/93/61f40bb97239cf55811892e14de2e344168ba1 b/tests/resources/attr/.gitted/objects/93/61f40bb97239cf55811892e14de2e344168ba1
new file mode 100644
index 0000000..4b57836
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/93/61f40bb97239cf55811892e14de2e344168ba1
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/94/da4faa0a6bfb8ee6ccf7153801a69202b31857 b/tests/resources/attr/.gitted/objects/94/da4faa0a6bfb8ee6ccf7153801a69202b31857
new file mode 100644
index 0000000..a9ddf5d
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/94/da4faa0a6bfb8ee6ccf7153801a69202b31857
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/96/089fd31ce1d3ee2afb0ba09ba063066932f027 b/tests/resources/attr/.gitted/objects/96/089fd31ce1d3ee2afb0ba09ba063066932f027
new file mode 100644
index 0000000..efa62f9
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/96/089fd31ce1d3ee2afb0ba09ba063066932f027
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/99/eae476896f4907224978b88e5ecaa6c5bb67a9 b/tests/resources/attr/.gitted/objects/99/eae476896f4907224978b88e5ecaa6c5bb67a9
new file mode 100644
index 0000000..8f5acc7
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/99/eae476896f4907224978b88e5ecaa6c5bb67a9
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/9e/5bdc47d6a80f2be0ea3049ad74231b94609242 b/tests/resources/attr/.gitted/objects/9e/5bdc47d6a80f2be0ea3049ad74231b94609242
new file mode 100644
index 0000000..d6385ec
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/9e/5bdc47d6a80f2be0ea3049ad74231b94609242
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/9f/b40b6675dde60b5697afceae91b66d908c02d9 b/tests/resources/attr/.gitted/objects/9f/b40b6675dde60b5697afceae91b66d908c02d9
new file mode 100644
index 0000000..7663ad0
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/9f/b40b6675dde60b5697afceae91b66d908c02d9
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/a0/f7217ae99f5ac3e88534f5cea267febc5fa85b b/tests/resources/attr/.gitted/objects/a0/f7217ae99f5ac3e88534f5cea267febc5fa85b
new file mode 100644
index 0000000..985c2e2
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/a0/f7217ae99f5ac3e88534f5cea267febc5fa85b
@@ -0,0 +1 @@
+x5Ž1Â0 E™}Š?–΀;•˜SâˆÔ®’”ŠÛ“Ðv´ýߢ8ŸO‡'FÈÈ:2r™ƒ)(¾ &¢Þ·«×9Z¼A Âð³¼Ñ¹r9Ýl¬ %¨˜ˆ„3ÑEo‚£.ÿV­Õi<Bñà F­©MÌb‰®+ÂÙŸ*vµªÛþìÖmõ÷¾¢ÞLK†Ý­D?+­N \ No newline at end of file
diff --git a/tests/resources/attr/.gitted/objects/a5/6bbcecaeac760cc26239384d2d4c614e7e4320 b/tests/resources/attr/.gitted/objects/a5/6bbcecaeac760cc26239384d2d4c614e7e4320
new file mode 100644
index 0000000..d898ae9
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/a5/6bbcecaeac760cc26239384d2d4c614e7e4320
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/a5/d76cad53f66f1312bd995909a5bab3c0820770 b/tests/resources/attr/.gitted/objects/a5/d76cad53f66f1312bd995909a5bab3c0820770
new file mode 100644
index 0000000..cd6a389
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/a5/d76cad53f66f1312bd995909a5bab3c0820770
@@ -0,0 +1,4 @@
+xŽ]
+‚!E{v³B>!"ZB;u¤à3Cmÿ í § ‡{.7µZŸ4âavfÈÖgBLÊEeP;NQÚ¬BŒLAnŲIÆç ÞÔù5ÁI»)MÑ6Z•œQ[
+h3Úe:
+ ùì}æ£u¸Æà}‡ï…;œ©÷È|ýÅ)µzµ&ô¦¼Óp”›”bÑõq®ú?¶¨­3TJ½Áä1‡ø3ÙJX \ No newline at end of file
diff --git a/tests/resources/attr/.gitted/objects/a9/7cc019851d401a4f1d091cb91a15890a0dd1ba b/tests/resources/attr/.gitted/objects/a9/7cc019851d401a4f1d091cb91a15890a0dd1ba
new file mode 100644
index 0000000..1a7ec0c
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/a9/7cc019851d401a4f1d091cb91a15890a0dd1ba
@@ -0,0 +1,2 @@
+xŽQjÄ0 DûíSè[ähc;PJéÚ(²¼ $q°–Þ¾†Þ _3oàIÞ÷µÁàÜK+ªàâäBtƒ„I|œ”â»LìgçÆˆÖ ÅR4'=¤qFN6Í÷4
+JôÌ1ôÖFrÑ‘zÃW[r¯«VÝ6øÔ-i7.eVýø‹WÉû;X‚‰,Á ¢émwlÿÏÛ|ç]ṬMëÉ¢ídáã¡RwêC[œW9sÕj~’Wy \ No newline at end of file
diff --git a/tests/resources/attr/.gitted/objects/b4/35cd5689a0fb54afbeda4ac20368aa480e8f04 b/tests/resources/attr/.gitted/objects/b4/35cd5689a0fb54afbeda4ac20368aa480e8f04
new file mode 100644
index 0000000..ffe3473
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/b4/35cd5689a0fb54afbeda4ac20368aa480e8f04
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/c0/091889c0c77142b87a1fa5123a6398a61d33e7 b/tests/resources/attr/.gitted/objects/c0/091889c0c77142b87a1fa5123a6398a61d33e7
new file mode 100644
index 0000000..11dc63c
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/c0/091889c0c77142b87a1fa5123a6398a61d33e7
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/c4/85abe35abd4aa6fd83b076a78bbea9e2e7e06c b/tests/resources/attr/.gitted/objects/c4/85abe35abd4aa6fd83b076a78bbea9e2e7e06c
new file mode 100644
index 0000000..58569ca
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/c4/85abe35abd4aa6fd83b076a78bbea9e2e7e06c
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/c7/aadd770d5907a8475c29e9ee21a27b88bf675d b/tests/resources/attr/.gitted/objects/c7/aadd770d5907a8475c29e9ee21a27b88bf675d
new file mode 100644
index 0000000..39aedb7
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/c7/aadd770d5907a8475c29e9ee21a27b88bf675d
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/c9/6bbb2c2557a8325ae1559e3ba79cdcecb23076 b/tests/resources/attr/.gitted/objects/c9/6bbb2c2557a8325ae1559e3ba79cdcecb23076
new file mode 100644
index 0000000..589f9ad
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/c9/6bbb2c2557a8325ae1559e3ba79cdcecb23076
@@ -0,0 +1,2 @@
+x5A
+Â0D]ÿSÌεèoàÂuJ~L0ýͯ¡··)¸xÃcfªœp¹]OOΊcñB µ˜6‘»!뢘´²Ã³‚{,áU<C¿j˜¹[ÁE-ŠÜ-¢Ò˜ð&#š¯)~ëÇäÆ=˜;;{.öùe"3A \ No newline at end of file
diff --git a/tests/resources/attr/.gitted/objects/ce/39a97a7fb1fa90bcf5e711249c1e507476ae0e b/tests/resources/attr/.gitted/objects/ce/39a97a7fb1fa90bcf5e711249c1e507476ae0e
new file mode 100644
index 0000000..1005f94
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/ce/39a97a7fb1fa90bcf5e711249c1e507476ae0e
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/d5/7da33c16b14326ecb05d19bbea908f5e4c47d9 b/tests/resources/attr/.gitted/objects/d5/7da33c16b14326ecb05d19bbea908f5e4c47d9
new file mode 100644
index 0000000..b96d40c
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/d5/7da33c16b14326ecb05d19bbea908f5e4c47d9
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/d8/00886d9c86731ae5c4a62b0b77c437015e00d2 b/tests/resources/attr/.gitted/objects/d8/00886d9c86731ae5c4a62b0b77c437015e00d2
new file mode 100644
index 0000000..83f3b72
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/d8/00886d9c86731ae5c4a62b0b77c437015e00d2
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/dc/cada462d3df8ac6de596fb8c896aba9344f941 b/tests/resources/attr/.gitted/objects/dc/cada462d3df8ac6de596fb8c896aba9344f941
new file mode 100644
index 0000000..ef62f8b
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/dc/cada462d3df8ac6de596fb8c896aba9344f941
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/de/863bff4976c9ed7e17a4da0fd524908dc84049 b/tests/resources/attr/.gitted/objects/de/863bff4976c9ed7e17a4da0fd524908dc84049
new file mode 100644
index 0000000..7d9b855
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/de/863bff4976c9ed7e17a4da0fd524908dc84049
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/e5/63cf4758f0d646f1b14b76016aa17fa9e549a4 b/tests/resources/attr/.gitted/objects/e5/63cf4758f0d646f1b14b76016aa17fa9e549a4
new file mode 100644
index 0000000..1bc1f0f
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/e5/63cf4758f0d646f1b14b76016aa17fa9e549a4
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/ec/b97df2a174987475ac816e3847fc8e9f6c596b b/tests/resources/attr/.gitted/objects/ec/b97df2a174987475ac816e3847fc8e9f6c596b
new file mode 100644
index 0000000..44d703b
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/ec/b97df2a174987475ac816e3847fc8e9f6c596b
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/ed/f3dcee4003d71f139777898882ccd097e34c53 b/tests/resources/attr/.gitted/objects/ed/f3dcee4003d71f139777898882ccd097e34c53
new file mode 100644
index 0000000..d281846
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/ed/f3dcee4003d71f139777898882ccd097e34c53
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/f2/c6d717cf4a5a3e6b02684155ab07b766982165 b/tests/resources/attr/.gitted/objects/f2/c6d717cf4a5a3e6b02684155ab07b766982165
new file mode 100644
index 0000000..27a25dc
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/f2/c6d717cf4a5a3e6b02684155ab07b766982165
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/f5/b0af1fb4f5c0cd7aad880711d368a07333c307 b/tests/resources/attr/.gitted/objects/f5/b0af1fb4f5c0cd7aad880711d368a07333c307
new file mode 100644
index 0000000..21faeb8
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/f5/b0af1fb4f5c0cd7aad880711d368a07333c307
@@ -0,0 +1,2 @@
+xN[j1 Ì·O¡ 4ÈRbÇPJÈ
+=€Ö;NûÂë½ ½A?†y 1“×y~7½žZ(¾¥2ªÏð£beàÁ8uå’Ja‰n³Š¥‘F.HÈ"— UD_®Ý£÷ÉHI£sv´×ZéûØwL=0Tú´ZàþGç¼Î_äUbßKèƒoÌ®§}cëçÿùv?Ûhí½<©aoÔµ¹_áEK \ No newline at end of file
diff --git a/tests/resources/attr/.gitted/objects/fb/5067b1aef3ac1ada4b379dbcb7d17255df7d78 b/tests/resources/attr/.gitted/objects/fb/5067b1aef3ac1ada4b379dbcb7d17255df7d78
new file mode 100644
index 0000000..6c8ff83
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/fb/5067b1aef3ac1ada4b379dbcb7d17255df7d78
Binary files differ
diff --git a/tests/resources/attr/.gitted/objects/fe/773770c5a6cc7185580c9204b1ff18a33ff3fc b/tests/resources/attr/.gitted/objects/fe/773770c5a6cc7185580c9204b1ff18a33ff3fc
new file mode 100644
index 0000000..e6fcbc0
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/fe/773770c5a6cc7185580c9204b1ff18a33ff3fc
@@ -0,0 +1 @@
+x5ŽAÂ0 9ûû„xBÜAâœG¤vÕ¤Tüž¤Ð£åõÙ<ûãîÂ#¥Î1ÂUT釛*ÑMúWlÎOCR˜2dÖѵC.„T“©ËÈI¹lQH/öœmYÛ¬UN[àžª€ß¬¬¥þBÖ@t¶Üð8~˜†Õ‹¿}}R#Ä#kAØdD_=-H– \ No newline at end of file
diff --git a/tests/resources/attr/.gitted/objects/ff/69f8639ce2e6010b3f33a74160aad98b48da2b b/tests/resources/attr/.gitted/objects/ff/69f8639ce2e6010b3f33a74160aad98b48da2b
new file mode 100644
index 0000000..b736c0b
--- /dev/null
+++ b/tests/resources/attr/.gitted/objects/ff/69f8639ce2e6010b3f33a74160aad98b48da2b
Binary files differ
diff --git a/tests/resources/attr/.gitted/refs/heads/master b/tests/resources/attr/.gitted/refs/heads/master
new file mode 100644
index 0000000..b3abfff
--- /dev/null
+++ b/tests/resources/attr/.gitted/refs/heads/master
@@ -0,0 +1 @@
+8d0b9df9bd30be7910ddda60548d485bc302b911
diff --git a/tests/resources/attr/attr0 b/tests/resources/attr/attr0
new file mode 100644
index 0000000..556f8c8
--- /dev/null
+++ b/tests/resources/attr/attr0
@@ -0,0 +1 @@
+* binary
diff --git a/tests/resources/attr/attr1 b/tests/resources/attr/attr1
new file mode 100644
index 0000000..3b74db7
--- /dev/null
+++ b/tests/resources/attr/attr1
@@ -0,0 +1,29 @@
+# a comment followed by some blank lines
+
+
+
+ # another comment that is indented
+
+# variations on fnmatch
+
+pat0 attr0
+!pat1 attr1
+pat2/ attr2
+pat3dir/pat3file attr3
+pat4.* attr4
+ *.pat5 attr5
+pat6/pat6/*.pat6 attr6
+
+pat7[a-e]??[xyz] attr7 # with a comment on the line
+
+pat8\ with\ spaces attr8
+
+ invalid # attr with no assignments doesn't count
+
+also/invalid
+
+invalid.again/
+
+# next attr is at eof
+
+ pat9 attr9 \ No newline at end of file
diff --git a/tests/resources/attr/attr2 b/tests/resources/attr/attr2
new file mode 100644
index 0000000..2c66e14
--- /dev/null
+++ b/tests/resources/attr/attr2
@@ -0,0 +1,21 @@
+
+# variations on assignments
+
+pat0 simple
+pat1 -neg
+* notundef
+pat2 !notundef
+pat3 assigned=test-value
+pat4 rule-with-more-chars=value-with-more-chars
+pat5 empty=
+pat6 -negempty=
+pat7 multiple -single values=1 also=a-really-long-value/* happy=yes!
+# the next line has trailing spaces
+pat8 again= another=12321
+patbad0 # empty assignment does not count
+# next line will be another simple empty assign that should not count
+ patbad1
+
+# BTW I think there are 11 valid rules and two "invalid" empty ones
+
+pat9 -at-eof \ No newline at end of file
diff --git a/tests/resources/attr/attr3 b/tests/resources/attr/attr3
new file mode 100644
index 0000000..c485abe
--- /dev/null
+++ b/tests/resources/attr/attr3
@@ -0,0 +1,4 @@
+# These are examples from the git-check-attr.1 man page
+*.java diff=java -crlf myAttr
+NoMyAttr.java !myAttr
+README caveat=unspecified
diff --git a/tests/resources/attr/attr4 b/tests/resources/attr/attr4
new file mode 100644
index 0000000..fa88df9
--- /dev/null
+++ b/tests/resources/attr/attr4
@@ -0,0 +1,7 @@
+# This is a comment
+ # This is also a comment
+*.java diff=java -crlf myAttr
+
+ NoMyAttr.java !myAttr
+
+ README caveat=unspecified
diff --git a/tests/resources/attr/binfile b/tests/resources/attr/binfile
new file mode 100644
index 0000000..d800886
--- /dev/null
+++ b/tests/resources/attr/binfile
@@ -0,0 +1 @@
+123 \ No newline at end of file
diff --git a/tests/resources/attr/dir/file b/tests/resources/attr/dir/file
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/attr/dir/file
diff --git a/tests/resources/attr/file b/tests/resources/attr/file
new file mode 100644
index 0000000..45b983b
--- /dev/null
+++ b/tests/resources/attr/file
@@ -0,0 +1 @@
+hi
diff --git a/tests/resources/attr/gitattributes b/tests/resources/attr/gitattributes
new file mode 100644
index 0000000..e038983
--- /dev/null
+++ b/tests/resources/attr/gitattributes
@@ -0,0 +1,29 @@
+* rootattr
+root_test2 -rootattr
+root_test3 !rootattr
+binfile binary
+abc foo bar baz
+does-not-exist foo=yes
+
+root_test2 multiattr
+root_test3 multi2=foo
+
+root_test3 multiattr=1 multiattr=2 multiattr=3 multi2=abc !multi2
+root_test2 multiattr=string -multiattr
+
+[attr]mymacro positive -negative !rootattr
+macro* mymacro another=77
+
+[attr]macro2 multi2 -multi2 multi3 !multi3 multi3=answer
+macro* macro2 macro2 macro2
+
+# let's try some malicious macro defs
+[attr]firstmacro -thirdmacro -secondmacro
+[attr]secondmacro firstmacro -firstmacro
+[attr]thirdmacro secondmacro=hahaha
+
+macro_bad firstmacro secondmacro thirdmacro
+
+# another test that Peff found was failing
+[attr]notest !test
+
diff --git a/tests/resources/attr/gitignore b/tests/resources/attr/gitignore
new file mode 100644
index 0000000..1929670
--- /dev/null
+++ b/tests/resources/attr/gitignore
@@ -0,0 +1,2 @@
+ign
+dir/
diff --git a/tests/resources/attr/ign b/tests/resources/attr/ign
new file mode 100644
index 0000000..592fd25
--- /dev/null
+++ b/tests/resources/attr/ign
@@ -0,0 +1 @@
+ignore me
diff --git a/tests/resources/attr/macro_bad b/tests/resources/attr/macro_bad
new file mode 100644
index 0000000..5819a18
--- /dev/null
+++ b/tests/resources/attr/macro_bad
@@ -0,0 +1 @@
+boo
diff --git a/tests/resources/attr/macro_test b/tests/resources/attr/macro_test
new file mode 100644
index 0000000..ff69f86
--- /dev/null
+++ b/tests/resources/attr/macro_test
@@ -0,0 +1 @@
+Yo
diff --git a/tests/resources/attr/root_test1 b/tests/resources/attr/root_test1
new file mode 100644
index 0000000..45141a7
--- /dev/null
+++ b/tests/resources/attr/root_test1
@@ -0,0 +1 @@
+Hello from the root
diff --git a/tests/resources/attr/root_test2 b/tests/resources/attr/root_test2
new file mode 100644
index 0000000..4d713dc
--- /dev/null
+++ b/tests/resources/attr/root_test2
@@ -0,0 +1,6 @@
+Hello from the root
+
+Some additional lines
+
+Down here below
+
diff --git a/tests/resources/attr/root_test3 b/tests/resources/attr/root_test3
new file mode 100644
index 0000000..108bb4e
--- /dev/null
+++ b/tests/resources/attr/root_test3
@@ -0,0 +1,19 @@
+Some additional lines
+
+
+ Down here below the other lines
+
+
+With even more at the end
+
+
+And lots of good stuff
+
+
+Anywhere you want
+
+
+Don't you think
+
+
+
diff --git a/tests/resources/attr/root_test4.txt b/tests/resources/attr/root_test4.txt
new file mode 100644
index 0000000..a0f7217
--- /dev/null
+++ b/tests/resources/attr/root_test4.txt
@@ -0,0 +1,14 @@
+Here is some stuff at the start
+
+This should go in one hunk (first)
+
+Some additional lines
+
+Down here below the other lines
+
+With even more at the end
+
+Followed by a second hunk of stuff (second)
+
+That happens down here
+
diff --git a/tests/resources/attr/sub/.gitattributes b/tests/resources/attr/sub/.gitattributes
new file mode 100644
index 0000000..329c1c5
--- /dev/null
+++ b/tests/resources/attr/sub/.gitattributes
@@ -0,0 +1,7 @@
+* subattr=yes -negattr
+*.txt another=zero
+sub/*.txt another=one
+ab* merge=filfre
+abc -foo -bar
+*.c frotz
+deep/file deepdeep
diff --git a/tests/resources/attr/sub/abc b/tests/resources/attr/sub/abc
new file mode 100644
index 0000000..3e42ffc
--- /dev/null
+++ b/tests/resources/attr/sub/abc
@@ -0,0 +1,37 @@
+# Test file from gitattributes(5) example:
+
+If you have these three gitattributes file:
+
+ (in $GIT_DIR/info/attributes)
+
+ a* foo !bar -baz
+
+ (in .gitattributes)
+ abc foo bar baz
+
+ (in t/.gitattributes)
+ ab* merge=filfre
+ abc -foo -bar
+ *.c frotz
+
+the attributes given to path t/abc are computed as follows:
+
+1. By examining t/.gitattributes (which is in the same directory as the path
+ in question), git finds that the first line matches. merge attribute is
+ set. It also finds that the second line matches, and attributes foo and
+ bar are unset.
+2. Then it examines .gitattributes (which is in the parent directory), and
+ finds that the first line matches, but t/.gitattributes file already
+ decided how merge, foo and bar attributes should be given to this path,
+ so it leaves foo and bar unset. Attribute baz is set.
+3. Finally it examines $GIT_DIR/info/attributes. This file is used to
+ override the in-tree settings. The first line is a match, and foo is set,
+ bar is reverted to unspecified state, and baz is unset.
+
+As the result, the attributes assignment to t/abc becomes:
+
+ foo set to true
+ bar unspecified
+ baz set to false
+ merge set to string value "filfre"
+ frotz unspecified
diff --git a/tests/resources/attr/sub/dir/file b/tests/resources/attr/sub/dir/file
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/attr/sub/dir/file
diff --git a/tests/resources/attr/sub/file b/tests/resources/attr/sub/file
new file mode 100644
index 0000000..45b983b
--- /dev/null
+++ b/tests/resources/attr/sub/file
@@ -0,0 +1 @@
+hi
diff --git a/tests/resources/attr/sub/ign/file b/tests/resources/attr/sub/ign/file
new file mode 100644
index 0000000..4dcd992
--- /dev/null
+++ b/tests/resources/attr/sub/ign/file
@@ -0,0 +1 @@
+in ignored dir
diff --git a/tests/resources/attr/sub/ign/sub/file b/tests/resources/attr/sub/ign/sub/file
new file mode 100644
index 0000000..88aca01
--- /dev/null
+++ b/tests/resources/attr/sub/ign/sub/file
@@ -0,0 +1 @@
+below ignored dir
diff --git a/tests/resources/attr/sub/sub/.gitattributes b/tests/resources/attr/sub/sub/.gitattributes
new file mode 100644
index 0000000..55225e4
--- /dev/null
+++ b/tests/resources/attr/sub/sub/.gitattributes
@@ -0,0 +1,3 @@
+d/* test=a/b/d/*
+d/yes notest
+
diff --git a/tests/resources/attr/sub/sub/dir b/tests/resources/attr/sub/sub/dir
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/attr/sub/sub/dir
diff --git a/tests/resources/attr/sub/sub/file b/tests/resources/attr/sub/sub/file
new file mode 100644
index 0000000..45b983b
--- /dev/null
+++ b/tests/resources/attr/sub/sub/file
@@ -0,0 +1 @@
+hi
diff --git a/tests/resources/attr/sub/sub/subsub.txt b/tests/resources/attr/sub/sub/subsub.txt
new file mode 100644
index 0000000..9e5bdc4
--- /dev/null
+++ b/tests/resources/attr/sub/sub/subsub.txt
@@ -0,0 +1 @@
+subsub
diff --git a/tests/resources/attr/sub/subdir_test1 b/tests/resources/attr/sub/subdir_test1
new file mode 100644
index 0000000..e563cf4
--- /dev/null
+++ b/tests/resources/attr/sub/subdir_test1
@@ -0,0 +1,2 @@
+Hello from the subdir
+
diff --git a/tests/resources/attr/sub/subdir_test2.txt b/tests/resources/attr/sub/subdir_test2.txt
new file mode 100644
index 0000000..fb5067b
--- /dev/null
+++ b/tests/resources/attr/sub/subdir_test2.txt
@@ -0,0 +1 @@
+Hello again
diff --git a/tests/resources/attr_index/.gitted/HEAD b/tests/resources/attr_index/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/attr_index/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/attr_index/.gitted/config b/tests/resources/attr_index/.gitted/config
new file mode 100644
index 0000000..af10792
--- /dev/null
+++ b/tests/resources/attr_index/.gitted/config
@@ -0,0 +1,6 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
diff --git a/tests/resources/attr_index/.gitted/description b/tests/resources/attr_index/.gitted/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/attr_index/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/attr_index/.gitted/index b/tests/resources/attr_index/.gitted/index
new file mode 100644
index 0000000..d874803
--- /dev/null
+++ b/tests/resources/attr_index/.gitted/index
Binary files differ
diff --git a/tests/resources/attr_index/.gitted/info/exclude b/tests/resources/attr_index/.gitted/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/attr_index/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/attr_index/.gitted/info/refs b/tests/resources/attr_index/.gitted/info/refs
new file mode 100644
index 0000000..60feca2
--- /dev/null
+++ b/tests/resources/attr_index/.gitted/info/refs
@@ -0,0 +1 @@
+58f7cf825b553ef7c26e5b9f8a23599c1a9ca296 refs/heads/master
diff --git a/tests/resources/attr_index/.gitted/logs/HEAD b/tests/resources/attr_index/.gitted/logs/HEAD
new file mode 100644
index 0000000..ffd298c
--- /dev/null
+++ b/tests/resources/attr_index/.gitted/logs/HEAD
@@ -0,0 +1,4 @@
+0000000000000000000000000000000000000000 67c1640e91ccbaf0793591be09bf572cf40c9a53 Russell Belfer <rb@github.com> 1335817070 -0700 commit (initial): Initial commit
+67c1640e91ccbaf0793591be09bf572cf40c9a53 d441d7d88f52c28c2b23940ce4c33756748425f9 Russell Belfer <rb@github.com> 1335817296 -0700 commit: Adding some files in subtrees
+d441d7d88f52c28c2b23940ce4c33756748425f9 67c1640e91ccbaf0793591be09bf572cf40c9a53 Russell Belfer <rb@github.com> 1335817353 -0700 HEAD^: updating HEAD
+67c1640e91ccbaf0793591be09bf572cf40c9a53 58f7cf825b553ef7c26e5b9f8a23599c1a9ca296 Russell Belfer <rb@github.com> 1335817372 -0700 commit: Adding subtree data
diff --git a/tests/resources/attr_index/.gitted/logs/refs/heads/master b/tests/resources/attr_index/.gitted/logs/refs/heads/master
new file mode 100644
index 0000000..ffd298c
--- /dev/null
+++ b/tests/resources/attr_index/.gitted/logs/refs/heads/master
@@ -0,0 +1,4 @@
+0000000000000000000000000000000000000000 67c1640e91ccbaf0793591be09bf572cf40c9a53 Russell Belfer <rb@github.com> 1335817070 -0700 commit (initial): Initial commit
+67c1640e91ccbaf0793591be09bf572cf40c9a53 d441d7d88f52c28c2b23940ce4c33756748425f9 Russell Belfer <rb@github.com> 1335817296 -0700 commit: Adding some files in subtrees
+d441d7d88f52c28c2b23940ce4c33756748425f9 67c1640e91ccbaf0793591be09bf572cf40c9a53 Russell Belfer <rb@github.com> 1335817353 -0700 HEAD^: updating HEAD
+67c1640e91ccbaf0793591be09bf572cf40c9a53 58f7cf825b553ef7c26e5b9f8a23599c1a9ca296 Russell Belfer <rb@github.com> 1335817372 -0700 commit: Adding subtree data
diff --git a/tests/resources/attr_index/.gitted/objects/38/12cfef36615db1788d4e63f90028007e17a348 b/tests/resources/attr_index/.gitted/objects/38/12cfef36615db1788d4e63f90028007e17a348
new file mode 100644
index 0000000..ee29915
--- /dev/null
+++ b/tests/resources/attr_index/.gitted/objects/38/12cfef36615db1788d4e63f90028007e17a348
@@ -0,0 +1,3 @@
+x•Ž[
+Ã Eûí*Ü@‹ŽPJé
+]€š™&“`Ìþëúw¸œ 'o¥ÌM¸K«D’ ‚q•4¤ÊËì5DFË#šä!!ˆ=VZ›DÏ.³Lˆ†:ƒ%L}ƒ!dCŽ¬ˆg›¶*ßçqвÈ-LUÞkz~ç6é–·òÚ«íàWå”}í}­«ÿ>Åg˾Õ{fžâú%ñ ¡Gò \ No newline at end of file
diff --git a/tests/resources/attr_index/.gitted/objects/59/d942b8be2784bc96db9b22202c10815c9a077b b/tests/resources/attr_index/.gitted/objects/59/d942b8be2784bc96db9b22202c10815c9a077b
new file mode 100644
index 0000000..ff33737
--- /dev/null
+++ b/tests/resources/attr_index/.gitted/objects/59/d942b8be2784bc96db9b22202c10815c9a077b
@@ -0,0 +1 @@
+x ÃÑ €0 @¿âÍà‡“¸@kR”’@ßÂ]½ã<¶K4±ÜnÕÔÅY‰á)l(a¨hF˜Hcƒcÿ^Ô \ No newline at end of file
diff --git a/tests/resources/attr_index/.gitted/objects/cd/f17ea3fe625ef812f4dce7f423f4f299287505 b/tests/resources/attr_index/.gitted/objects/cd/f17ea3fe625ef812f4dce7f423f4f299287505
new file mode 100644
index 0000000..2a41005
--- /dev/null
+++ b/tests/resources/attr_index/.gitted/objects/cd/f17ea3fe625ef812f4dce7f423f4f299287505
Binary files differ
diff --git a/tests/resources/attr_index/.gitted/objects/f7/2502ddd01412bb20796ff812af56fd53b82b52 b/tests/resources/attr_index/.gitted/objects/f7/2502ddd01412bb20796ff812af56fd53b82b52
new file mode 100644
index 0000000..0489280
--- /dev/null
+++ b/tests/resources/attr_index/.gitted/objects/f7/2502ddd01412bb20796ff812af56fd53b82b52
Binary files differ
diff --git a/tests/resources/attr_index/.gitted/objects/info/packs b/tests/resources/attr_index/.gitted/objects/info/packs
new file mode 100644
index 0000000..559dc74
--- /dev/null
+++ b/tests/resources/attr_index/.gitted/objects/info/packs
@@ -0,0 +1,2 @@
+P pack-4e6438607204ce78827e3885594b2c0bb4f13895.pack
+
diff --git a/tests/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.idx b/tests/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.idx
new file mode 100644
index 0000000..fbef4aa
--- /dev/null
+++ b/tests/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.idx
Binary files differ
diff --git a/tests/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.pack b/tests/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.pack
new file mode 100644
index 0000000..09c9e06
--- /dev/null
+++ b/tests/resources/attr_index/.gitted/objects/pack/pack-4e6438607204ce78827e3885594b2c0bb4f13895.pack
Binary files differ
diff --git a/tests/resources/attr_index/.gitted/packed-refs b/tests/resources/attr_index/.gitted/packed-refs
new file mode 100644
index 0000000..6b3e4de
--- /dev/null
+++ b/tests/resources/attr_index/.gitted/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+58f7cf825b553ef7c26e5b9f8a23599c1a9ca296 refs/heads/master
diff --git a/tests/resources/attr_index/.gitted/refs/heads/master b/tests/resources/attr_index/.gitted/refs/heads/master
new file mode 100644
index 0000000..9b75629
--- /dev/null
+++ b/tests/resources/attr_index/.gitted/refs/heads/master
@@ -0,0 +1 @@
+3812cfef36615db1788d4e63f90028007e17a348
diff --git a/tests/resources/attr_index/README.md b/tests/resources/attr_index/README.md
new file mode 100644
index 0000000..59d942b
--- /dev/null
+++ b/tests/resources/attr_index/README.md
@@ -0,0 +1 @@
+This is contains tests for when the index and work dir differ
diff --git a/tests/resources/attr_index/README.txt b/tests/resources/attr_index/README.txt
new file mode 100644
index 0000000..874c12b
--- /dev/null
+++ b/tests/resources/attr_index/README.txt
@@ -0,0 +1 @@
+This contains files for testing when the index and the workdir differ
diff --git a/tests/resources/attr_index/gitattributes b/tests/resources/attr_index/gitattributes
new file mode 100644
index 0000000..cdf17ea
--- /dev/null
+++ b/tests/resources/attr_index/gitattributes
@@ -0,0 +1,4 @@
+* bar
+*.txt -foo beep=10
+*.md blargh=goop -bar
+
diff --git a/tests/resources/attr_index/sub/sub/.gitattributes b/tests/resources/attr_index/sub/sub/.gitattributes
new file mode 100644
index 0000000..060c9a2
--- /dev/null
+++ b/tests/resources/attr_index/sub/sub/.gitattributes
@@ -0,0 +1,3 @@
+*.txt another=one again
+*.md bar=1234
+
diff --git a/tests/resources/attr_index/sub/sub/README.md b/tests/resources/attr_index/sub/sub/README.md
new file mode 100644
index 0000000..59652e3
--- /dev/null
+++ b/tests/resources/attr_index/sub/sub/README.md
@@ -0,0 +1 @@
+More testing
diff --git a/tests/resources/attr_index/sub/sub/README.txt b/tests/resources/attr_index/sub/sub/README.txt
new file mode 100644
index 0000000..59652e3
--- /dev/null
+++ b/tests/resources/attr_index/sub/sub/README.txt
@@ -0,0 +1 @@
+More testing
diff --git a/tests/resources/bad.index b/tests/resources/bad.index
new file mode 100644
index 0000000..5374654
--- /dev/null
+++ b/tests/resources/bad.index
Binary files differ
diff --git a/tests/resources/bad_tag.git/HEAD b/tests/resources/bad_tag.git/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/bad_tag.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/bad_tag.git/config b/tests/resources/bad_tag.git/config
new file mode 100644
index 0000000..2f89580
--- /dev/null
+++ b/tests/resources/bad_tag.git/config
@@ -0,0 +1,5 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = true
+ logallrefupdates = true
diff --git a/tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.idx b/tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.idx
new file mode 100644
index 0000000..c404aa1
--- /dev/null
+++ b/tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.idx
Binary files differ
diff --git a/tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.pack b/tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.pack
new file mode 100644
index 0000000..90eac50
--- /dev/null
+++ b/tests/resources/bad_tag.git/objects/pack/pack-7a28f4e000a17f49a41d7a79fc2f762a8a7d9164.pack
Binary files differ
diff --git a/tests/resources/bad_tag.git/packed-refs b/tests/resources/bad_tag.git/packed-refs
new file mode 100644
index 0000000..9da1645
--- /dev/null
+++ b/tests/resources/bad_tag.git/packed-refs
@@ -0,0 +1,5 @@
+# pack-refs with: peeled
+eda9f45a2a98d4c17a09d681d88569fa4ea91755 refs/tags/e90810b
+^e90810b8df3e80c413d903f631643c716887138d
+d3bacb8d3ff25876a961b1963b6515170d0151ab refs/tags/hello
+^6dcf9bf7541ee10456529833502442f385010c3d \ No newline at end of file
diff --git a/tests/resources/bad_tag.git/refs/dummy-marker.txt b/tests/resources/bad_tag.git/refs/dummy-marker.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/bad_tag.git/refs/dummy-marker.txt
diff --git a/tests/resources/big.index b/tests/resources/big.index
new file mode 100644
index 0000000..66932f1
--- /dev/null
+++ b/tests/resources/big.index
Binary files differ
diff --git a/tests/resources/binaryunicode/.gitted/HEAD b/tests/resources/binaryunicode/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/binaryunicode/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/binaryunicode/.gitted/config b/tests/resources/binaryunicode/.gitted/config
new file mode 100644
index 0000000..f9845fe
--- /dev/null
+++ b/tests/resources/binaryunicode/.gitted/config
@@ -0,0 +1,6 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ autocrlf = true
+ logallrefupdates = true
diff --git a/tests/resources/binaryunicode/.gitted/description b/tests/resources/binaryunicode/.gitted/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/binaryunicode/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/binaryunicode/.gitted/index b/tests/resources/binaryunicode/.gitted/index
new file mode 100644
index 0000000..a216d22
--- /dev/null
+++ b/tests/resources/binaryunicode/.gitted/index
Binary files differ
diff --git a/tests/resources/binaryunicode/.gitted/info/exclude b/tests/resources/binaryunicode/.gitted/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/binaryunicode/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/binaryunicode/.gitted/info/refs b/tests/resources/binaryunicode/.gitted/info/refs
new file mode 100644
index 0000000..128eea7
--- /dev/null
+++ b/tests/resources/binaryunicode/.gitted/info/refs
@@ -0,0 +1,3 @@
+39e046d1416a208265b754124d0d197b4c9c0c47 refs/heads/branch1
+9e7d8bcd4d24dd57e3f1179aaf7afe648ff50e80 refs/heads/branch2
+d2a291469f4c11f387600d189313b927ddfe891c refs/heads/master
diff --git a/tests/resources/binaryunicode/.gitted/objects/info/packs b/tests/resources/binaryunicode/.gitted/objects/info/packs
new file mode 100644
index 0000000..c2de8f5
--- /dev/null
+++ b/tests/resources/binaryunicode/.gitted/objects/info/packs
@@ -0,0 +1,2 @@
+P pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.pack
+
diff --git a/tests/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.idx b/tests/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.idx
new file mode 100644
index 0000000..8a05b2b
--- /dev/null
+++ b/tests/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.idx
Binary files differ
diff --git a/tests/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.pack b/tests/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.pack
new file mode 100644
index 0000000..6b5ddc4
--- /dev/null
+++ b/tests/resources/binaryunicode/.gitted/objects/pack/pack-c5bfca875b4995d7aba6e5abf36241f3c397327d.pack
Binary files differ
diff --git a/tests/resources/binaryunicode/.gitted/refs/heads/branch1 b/tests/resources/binaryunicode/.gitted/refs/heads/branch1
new file mode 100644
index 0000000..0595fbd
--- /dev/null
+++ b/tests/resources/binaryunicode/.gitted/refs/heads/branch1
@@ -0,0 +1 @@
+39e046d1416a208265b754124d0d197b4c9c0c47
diff --git a/tests/resources/binaryunicode/.gitted/refs/heads/branch2 b/tests/resources/binaryunicode/.gitted/refs/heads/branch2
new file mode 100644
index 0000000..d868566
--- /dev/null
+++ b/tests/resources/binaryunicode/.gitted/refs/heads/branch2
@@ -0,0 +1 @@
+9e7d8bcd4d24dd57e3f1179aaf7afe648ff50e80
diff --git a/tests/resources/binaryunicode/.gitted/refs/heads/master b/tests/resources/binaryunicode/.gitted/refs/heads/master
new file mode 100644
index 0000000..552d166
--- /dev/null
+++ b/tests/resources/binaryunicode/.gitted/refs/heads/master
@@ -0,0 +1 @@
+d2a291469f4c11f387600d189313b927ddfe891c
diff --git a/tests/resources/binaryunicode/file.txt b/tests/resources/binaryunicode/file.txt
new file mode 100644
index 0000000..2255035
--- /dev/null
+++ b/tests/resources/binaryunicode/file.txt
@@ -0,0 +1 @@
+Master branch.
diff --git a/tests/resources/blametest.git/HEAD b/tests/resources/blametest.git/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/blametest.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/blametest.git/config b/tests/resources/blametest.git/config
new file mode 100644
index 0000000..c53d818
--- /dev/null
+++ b/tests/resources/blametest.git/config
@@ -0,0 +1,5 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = true
+ ignorecase = true
diff --git a/tests/resources/blametest.git/description b/tests/resources/blametest.git/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/blametest.git/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/blametest.git/objects/0c/bab4d45fd61e55a1c9697f9f9cb07a12e15448 b/tests/resources/blametest.git/objects/0c/bab4d45fd61e55a1c9697f9f9cb07a12e15448
new file mode 100644
index 0000000..90331ce
--- /dev/null
+++ b/tests/resources/blametest.git/objects/0c/bab4d45fd61e55a1c9697f9f9cb07a12e15448
Binary files differ
diff --git a/tests/resources/blametest.git/objects/1a/ac69ae5d96461afc4d81d0066cb12f5b05a35b b/tests/resources/blametest.git/objects/1a/ac69ae5d96461afc4d81d0066cb12f5b05a35b
new file mode 100644
index 0000000..7189027
--- /dev/null
+++ b/tests/resources/blametest.git/objects/1a/ac69ae5d96461afc4d81d0066cb12f5b05a35b
Binary files differ
diff --git a/tests/resources/blametest.git/objects/1b/5f0775af166331c854bd8d1bca3450eaf2532a b/tests/resources/blametest.git/objects/1b/5f0775af166331c854bd8d1bca3450eaf2532a
new file mode 100644
index 0000000..e664306
--- /dev/null
+++ b/tests/resources/blametest.git/objects/1b/5f0775af166331c854bd8d1bca3450eaf2532a
Binary files differ
diff --git a/tests/resources/blametest.git/objects/1d/81379086fd6d91ee027e883cf6f4703a107dfb b/tests/resources/blametest.git/objects/1d/81379086fd6d91ee027e883cf6f4703a107dfb
new file mode 100644
index 0000000..a35dd56
--- /dev/null
+++ b/tests/resources/blametest.git/objects/1d/81379086fd6d91ee027e883cf6f4703a107dfb
Binary files differ
diff --git a/tests/resources/blametest.git/objects/34/73c3e21e76492d09b80b7c75569edc275dffcf b/tests/resources/blametest.git/objects/34/73c3e21e76492d09b80b7c75569edc275dffcf
new file mode 100644
index 0000000..887deff
--- /dev/null
+++ b/tests/resources/blametest.git/objects/34/73c3e21e76492d09b80b7c75569edc275dffcf
Binary files differ
diff --git a/tests/resources/blametest.git/objects/37/681a80ca21064efd5c3bf2ef41eb3d05a1428b b/tests/resources/blametest.git/objects/37/681a80ca21064efd5c3bf2ef41eb3d05a1428b
new file mode 100644
index 0000000..a6ca0fb
--- /dev/null
+++ b/tests/resources/blametest.git/objects/37/681a80ca21064efd5c3bf2ef41eb3d05a1428b
Binary files differ
diff --git a/tests/resources/blametest.git/objects/40/fcae7fb84378fdb037dc6a3ccbb33669c3f26d b/tests/resources/blametest.git/objects/40/fcae7fb84378fdb037dc6a3ccbb33669c3f26d
new file mode 100644
index 0000000..27fc76b
--- /dev/null
+++ b/tests/resources/blametest.git/objects/40/fcae7fb84378fdb037dc6a3ccbb33669c3f26d
Binary files differ
diff --git a/tests/resources/blametest.git/objects/46/ef45f4ae55c1f5dca64b9e1d7ca77c1798069b b/tests/resources/blametest.git/objects/46/ef45f4ae55c1f5dca64b9e1d7ca77c1798069b
new file mode 100644
index 0000000..487ac57
--- /dev/null
+++ b/tests/resources/blametest.git/objects/46/ef45f4ae55c1f5dca64b9e1d7ca77c1798069b
Binary files differ
diff --git a/tests/resources/blametest.git/objects/48/2f2c370e35c2c314fc1f96db2beb33f955a26a b/tests/resources/blametest.git/objects/48/2f2c370e35c2c314fc1f96db2beb33f955a26a
new file mode 100644
index 0000000..7da4cf5
--- /dev/null
+++ b/tests/resources/blametest.git/objects/48/2f2c370e35c2c314fc1f96db2beb33f955a26a
Binary files differ
diff --git a/tests/resources/blametest.git/objects/4b/0ca755f5bfd69ed6074f268b05bb0542a42c68 b/tests/resources/blametest.git/objects/4b/0ca755f5bfd69ed6074f268b05bb0542a42c68
new file mode 100644
index 0000000..698f5b1
--- /dev/null
+++ b/tests/resources/blametest.git/objects/4b/0ca755f5bfd69ed6074f268b05bb0542a42c68
Binary files differ
diff --git a/tests/resources/blametest.git/objects/4d/8400b7ce2d15ef5045c2775ed33e82a326786e b/tests/resources/blametest.git/objects/4d/8400b7ce2d15ef5045c2775ed33e82a326786e
new file mode 100644
index 0000000..6e754df
--- /dev/null
+++ b/tests/resources/blametest.git/objects/4d/8400b7ce2d15ef5045c2775ed33e82a326786e
Binary files differ
diff --git a/tests/resources/blametest.git/objects/4e/ecfea484f8005d101e547f6bfb07c99e2b114e b/tests/resources/blametest.git/objects/4e/ecfea484f8005d101e547f6bfb07c99e2b114e
new file mode 100644
index 0000000..79e0ada
--- /dev/null
+++ b/tests/resources/blametest.git/objects/4e/ecfea484f8005d101e547f6bfb07c99e2b114e
Binary files differ
diff --git a/tests/resources/blametest.git/objects/5a/572e2e94825f54b95417eacaa089d560c5a5e9 b/tests/resources/blametest.git/objects/5a/572e2e94825f54b95417eacaa089d560c5a5e9
new file mode 100644
index 0000000..348beab
--- /dev/null
+++ b/tests/resources/blametest.git/objects/5a/572e2e94825f54b95417eacaa089d560c5a5e9
Binary files differ
diff --git a/tests/resources/blametest.git/objects/63/d671eb32d250e4a83766ebbc60e818c1e1e93a b/tests/resources/blametest.git/objects/63/d671eb32d250e4a83766ebbc60e818c1e1e93a
new file mode 100644
index 0000000..5f41f29
--- /dev/null
+++ b/tests/resources/blametest.git/objects/63/d671eb32d250e4a83766ebbc60e818c1e1e93a
@@ -0,0 +1,3 @@
+x•A E]sŠÙu¥Ê@bŒ±GðÀP뢭Azk<ù’—¼Ÿ×y~60­–I³“1S´’=“-6÷FfC.†ìcp½õŠµ, $b
+¶8MF
+ᨹO!1eïÈò]TqkÓZáV¸··çô¾>žmÚÒ)¯ó49dì-Ñ#ªîm­üg©áw©ºTK@Î \ No newline at end of file
diff --git a/tests/resources/blametest.git/objects/63/eb57322e363e18d460da5ea8284f3cd2340b36 b/tests/resources/blametest.git/objects/63/eb57322e363e18d460da5ea8284f3cd2340b36
new file mode 100644
index 0000000..c6c285e
--- /dev/null
+++ b/tests/resources/blametest.git/objects/63/eb57322e363e18d460da5ea8284f3cd2340b36
Binary files differ
diff --git a/tests/resources/blametest.git/objects/66/53ff42313eb5c82806f145391b18a9699800c7 b/tests/resources/blametest.git/objects/66/53ff42313eb5c82806f145391b18a9699800c7
new file mode 100644
index 0000000..1f11409
--- /dev/null
+++ b/tests/resources/blametest.git/objects/66/53ff42313eb5c82806f145391b18a9699800c7
Binary files differ
diff --git a/tests/resources/blametest.git/objects/6b/52ee554131a5e7bacd15553fbd22408c5a8a6f b/tests/resources/blametest.git/objects/6b/52ee554131a5e7bacd15553fbd22408c5a8a6f
new file mode 100644
index 0000000..b53aa04
--- /dev/null
+++ b/tests/resources/blametest.git/objects/6b/52ee554131a5e7bacd15553fbd22408c5a8a6f
Binary files differ
diff --git a/tests/resources/blametest.git/objects/70/2c7aa5250abc42be69ef78ee8fa47a346cb2ce b/tests/resources/blametest.git/objects/70/2c7aa5250abc42be69ef78ee8fa47a346cb2ce
new file mode 100644
index 0000000..1f6f2da
--- /dev/null
+++ b/tests/resources/blametest.git/objects/70/2c7aa5250abc42be69ef78ee8fa47a346cb2ce
Binary files differ
diff --git a/tests/resources/blametest.git/objects/77/c796837eb003c81d2cd8a6577ef4e7edc61222 b/tests/resources/blametest.git/objects/77/c796837eb003c81d2cd8a6577ef4e7edc61222
new file mode 100644
index 0000000..ae5f740
--- /dev/null
+++ b/tests/resources/blametest.git/objects/77/c796837eb003c81d2cd8a6577ef4e7edc61222
Binary files differ
diff --git a/tests/resources/blametest.git/objects/7e/135d94af53b6c5edbae6a77df8a0f09375e823 b/tests/resources/blametest.git/objects/7e/135d94af53b6c5edbae6a77df8a0f09375e823
new file mode 100644
index 0000000..b374ed1
--- /dev/null
+++ b/tests/resources/blametest.git/objects/7e/135d94af53b6c5edbae6a77df8a0f09375e823
Binary files differ
diff --git a/tests/resources/blametest.git/objects/83/6bc00b06cb60eb0f629e237ad2b58adb2cfc7e b/tests/resources/blametest.git/objects/83/6bc00b06cb60eb0f629e237ad2b58adb2cfc7e
new file mode 100644
index 0000000..71f9c98
--- /dev/null
+++ b/tests/resources/blametest.git/objects/83/6bc00b06cb60eb0f629e237ad2b58adb2cfc7e
@@ -0,0 +1,3 @@
+xK
+1D]çÙÍB$ODÁ{¤{:Œà¢ Oï€Ì¬Õ£àA·y¾uíìú"¢ RA‘ˆÌ4Ö1„œØf/‰Å“]UϲȣëÔêX
+ŒM¬ÖÈ–,–sFc8©òêS[ô}⩼å£78p›OÚ„.%§÷fZÛua—\uùÝ®ƒú’JF€ \ No newline at end of file
diff --git a/tests/resources/blametest.git/objects/8b/137891791fe96927ad78e64b0aad7bded08bdc b/tests/resources/blametest.git/objects/8b/137891791fe96927ad78e64b0aad7bded08bdc
new file mode 100644
index 0000000..9d8f605
--- /dev/null
+++ b/tests/resources/blametest.git/objects/8b/137891791fe96927ad78e64b0aad7bded08bdc
Binary files differ
diff --git a/tests/resources/blametest.git/objects/92/5bddd7a536a66eecb32faa41abd5bc9c192311 b/tests/resources/blametest.git/objects/92/5bddd7a536a66eecb32faa41abd5bc9c192311
new file mode 100644
index 0000000..aba34fa
--- /dev/null
+++ b/tests/resources/blametest.git/objects/92/5bddd7a536a66eecb32faa41abd5bc9c192311
Binary files differ
diff --git a/tests/resources/blametest.git/objects/96/679d59cf9f74d69b3c920f258559b5e8c9a18a b/tests/resources/blametest.git/objects/96/679d59cf9f74d69b3c920f258559b5e8c9a18a
new file mode 100644
index 0000000..d716e3b
--- /dev/null
+++ b/tests/resources/blametest.git/objects/96/679d59cf9f74d69b3c920f258559b5e8c9a18a
Binary files differ
diff --git a/tests/resources/blametest.git/objects/98/89d6e5557761aa8e3607e80c874a6dc51ada7c b/tests/resources/blametest.git/objects/98/89d6e5557761aa8e3607e80c874a6dc51ada7c
new file mode 100644
index 0000000..12407f6
--- /dev/null
+++ b/tests/resources/blametest.git/objects/98/89d6e5557761aa8e3607e80c874a6dc51ada7c
Binary files differ
diff --git a/tests/resources/blametest.git/objects/a3/4ead35680be7b9704fc4c6d750d182e228e02b b/tests/resources/blametest.git/objects/a3/4ead35680be7b9704fc4c6d750d182e228e02b
new file mode 100644
index 0000000..121934e
--- /dev/null
+++ b/tests/resources/blametest.git/objects/a3/4ead35680be7b9704fc4c6d750d182e228e02b
Binary files differ
diff --git a/tests/resources/blametest.git/objects/a4/641ad869ffad601aa8347e0770e949bb6d90df b/tests/resources/blametest.git/objects/a4/641ad869ffad601aa8347e0770e949bb6d90df
new file mode 100644
index 0000000..e54827c
--- /dev/null
+++ b/tests/resources/blametest.git/objects/a4/641ad869ffad601aa8347e0770e949bb6d90df
Binary files differ
diff --git a/tests/resources/blametest.git/objects/a8/ba8436b5d8ccbdfd5be597c194e7bb8e0a092f b/tests/resources/blametest.git/objects/a8/ba8436b5d8ccbdfd5be597c194e7bb8e0a092f
new file mode 100644
index 0000000..0bab0ef
--- /dev/null
+++ b/tests/resources/blametest.git/objects/a8/ba8436b5d8ccbdfd5be597c194e7bb8e0a092f
@@ -0,0 +1 @@
+x+)JMU04¶`040031QHÔ+©(aàÙµåJü5¹Ð…'3ëçÏÙP%ô0Ī ¬à½Ñ}_ՉΓÍE{þ4ºÙ©œrª -3'¬æ§šý^ÃuUÛ'Ëݘ’¿aÝn®ÏP5¥é5÷æ?ŽŸÉöp¦‡ÍÑ9íÊÚ_$*£Û\9ˆ \ No newline at end of file
diff --git a/tests/resources/blametest.git/objects/aa/06ecca6c4ad6432ab9313e556ca92ba4bcf9e9 b/tests/resources/blametest.git/objects/aa/06ecca6c4ad6432ab9313e556ca92ba4bcf9e9
new file mode 100644
index 0000000..bc13bad
--- /dev/null
+++ b/tests/resources/blametest.git/objects/aa/06ecca6c4ad6432ab9313e556ca92ba4bcf9e9
@@ -0,0 +1 @@
+x•KÂ0 DYçÞu²ã6I%„P9'ÈÇ¡]ô£ÞŸ"NÀÌîI#½‰ë<O4ᩈYPè(O.SÛçÔÚÜ yÉÉ­ FV›/²TH^³å¾C¬“ÆĘÉv¡–£3ìrúÆ+¿×q-0ÈÏZüàÞ÷×TÇ=\â:߀ؠ%tÎÀ¢:èáVå¿•zü.5CóªŠA@ \ No newline at end of file
diff --git a/tests/resources/blametest.git/objects/ad/9cb4eac23df2fe5e1264287a5872ea2a1ff8b2 b/tests/resources/blametest.git/objects/ad/9cb4eac23df2fe5e1264287a5872ea2a1ff8b2
new file mode 100644
index 0000000..077e658
--- /dev/null
+++ b/tests/resources/blametest.git/objects/ad/9cb4eac23df2fe5e1264287a5872ea2a1ff8b2
Binary files differ
diff --git a/tests/resources/blametest.git/objects/b1/76dfc3a4dc8734e4c579f77236a9c8d0a965d2 b/tests/resources/blametest.git/objects/b1/76dfc3a4dc8734e4c579f77236a9c8d0a965d2
new file mode 100644
index 0000000..caf5cc1
--- /dev/null
+++ b/tests/resources/blametest.git/objects/b1/76dfc3a4dc8734e4c579f77236a9c8d0a965d2
Binary files differ
diff --git a/tests/resources/blametest.git/objects/b9/0bb887b7c03750ae6b352ffe76ab9d2e86ee7d b/tests/resources/blametest.git/objects/b9/0bb887b7c03750ae6b352ffe76ab9d2e86ee7d
new file mode 100644
index 0000000..d4f9cca
--- /dev/null
+++ b/tests/resources/blametest.git/objects/b9/0bb887b7c03750ae6b352ffe76ab9d2e86ee7d
Binary files differ
diff --git a/tests/resources/blametest.git/objects/b9/9f7ac0b88909253d829554c14af488c3b0f3a5 b/tests/resources/blametest.git/objects/b9/9f7ac0b88909253d829554c14af488c3b0f3a5
new file mode 100644
index 0000000..a428fd6
--- /dev/null
+++ b/tests/resources/blametest.git/objects/b9/9f7ac0b88909253d829554c14af488c3b0f3a5
@@ -0,0 +1,2 @@
+x•ŽK
+1]ç½›•ÒIg2Yˆˆâ <A>­3‹L vîoÀ¸{õR-eÐΤ1ƒ#ŽóBÆ0©}¶s˜9xãí‹R6d1’S¡ËZÜx‡§´Ð#œãçúÞdíñ”j¹€&‡‹F§ Ñ#ªAGKø?KݧÇôgõ2r \ No newline at end of file
diff --git a/tests/resources/blametest.git/objects/ba/9089263dce882885ad84513f31495bf9d31132 b/tests/resources/blametest.git/objects/ba/9089263dce882885ad84513f31495bf9d31132
new file mode 100644
index 0000000..4610c26
--- /dev/null
+++ b/tests/resources/blametest.git/objects/ba/9089263dce882885ad84513f31495bf9d31132
Binary files differ
diff --git a/tests/resources/blametest.git/objects/bc/7c5ac2bafe828a68e9d1d460343718d6fbe136 b/tests/resources/blametest.git/objects/bc/7c5ac2bafe828a68e9d1d460343718d6fbe136
new file mode 100644
index 0000000..4e6ad15
--- /dev/null
+++ b/tests/resources/blametest.git/objects/bc/7c5ac2bafe828a68e9d1d460343718d6fbe136
@@ -0,0 +1,3 @@
+x•AnÃ0 {Ö+tË))J´AÛ<!/ eºÉÁqàÊÿ¯¢Èu±³Àl[æùÞc*òÖW÷èÅ26t-DS¨V5C2ÀŠN#§Lá©«?zdy@7Jc*àY+ ÌnÖ¼bݧЅô¿¯
+ì­)·¬#gJjBH^
+7•dš­Mâtë·e_þˆ×¾êfñd?ß÷~Ûì½-ó9"1 BPžî.Ý_£Âåï‚Ãç!ü&íO \ No newline at end of file
diff --git a/tests/resources/blametest.git/objects/c3/8d3c99946b74173f9b037279f07d505195563f b/tests/resources/blametest.git/objects/c3/8d3c99946b74173f9b037279f07d505195563f
new file mode 100644
index 0000000..04fd9cc
--- /dev/null
+++ b/tests/resources/blametest.git/objects/c3/8d3c99946b74173f9b037279f07d505195563f
Binary files differ
diff --git a/tests/resources/blametest.git/objects/c4/c13c153a611418325c70d6e630fed373546c4d b/tests/resources/blametest.git/objects/c4/c13c153a611418325c70d6e630fed373546c4d
new file mode 100644
index 0000000..ba52060
--- /dev/null
+++ b/tests/resources/blametest.git/objects/c4/c13c153a611418325c70d6e630fed373546c4d
Binary files differ
diff --git a/tests/resources/blametest.git/objects/cf/e0e1e1e3ba18f149fd47f5e1aef6016b2260c3 b/tests/resources/blametest.git/objects/cf/e0e1e1e3ba18f149fd47f5e1aef6016b2260c3
new file mode 100644
index 0000000..2048e81
--- /dev/null
+++ b/tests/resources/blametest.git/objects/cf/e0e1e1e3ba18f149fd47f5e1aef6016b2260c3
Binary files differ
diff --git a/tests/resources/blametest.git/objects/d0/67729932057cdb7527a833d6799c4ddc520640 b/tests/resources/blametest.git/objects/d0/67729932057cdb7527a833d6799c4ddc520640
new file mode 100644
index 0000000..926c4bb
--- /dev/null
+++ b/tests/resources/blametest.git/objects/d0/67729932057cdb7527a833d6799c4ddc520640
@@ -0,0 +1 @@
+x+)JMU03c040031QHÔ+©(a˜Ñyíihyâª>3ö<í^¹G¥nÕ@$H­É\;ÍMêo㶜úѬ‹£Ƥ \ No newline at end of file
diff --git a/tests/resources/blametest.git/objects/d2/bc4f27cbb72260eeec350087d81a60a122efe9 b/tests/resources/blametest.git/objects/d2/bc4f27cbb72260eeec350087d81a60a122efe9
new file mode 100644
index 0000000..b4d6c6d
--- /dev/null
+++ b/tests/resources/blametest.git/objects/d2/bc4f27cbb72260eeec350087d81a60a122efe9
Binary files differ
diff --git a/tests/resources/blametest.git/objects/d3/c7316f0075debfe53b25e58f56b0a4b46e18c3 b/tests/resources/blametest.git/objects/d3/c7316f0075debfe53b25e58f56b0a4b46e18c3
new file mode 100644
index 0000000..73f00ee
--- /dev/null
+++ b/tests/resources/blametest.git/objects/d3/c7316f0075debfe53b25e58f56b0a4b46e18c3
@@ -0,0 +1,4 @@
+xm“ÉΣH„çÌSÔÝš†b©*¤îQ³Û`³c7öÅl ¿—§wæÖyH)?)R©PF1].Ý ƒ…¿–¹ª@ž‰ YÄ•EEKˆ•„ Ws…¼KBŽ¥®Ù\ @Hàêšg9ÈU¹P| ª!/p"Ì!ÉD$Š„a
+LeëÒN3Ч ÈÙ ø^OÓÏ9ËéRLóõ[1]þP +ˆP€`Ã@†¡>ôsàRÍ@ÉæEûÈFð½ø ?µûoð­Zÿ m®Í½kÀß¿JÖŒ \ÃÁΰ¥0òµßœè¼Ý[–$Y‘¤Rï4Mß«Ë9?ò¶{·–·äÙ.µÅ)æuià>$¥9{ÿs
+¤X€w‰¦Oçi®&EÔ4‘<ÏVWr}fJ¥ewHÖÄ}«ü™eo¦ÖÖ £}ȼ1<ñrï'ÆÝG§‹Þ„·Ä"î';LëkÞDNoÕ­¢sÙ0Ú݃4+ºnŸò¸¯ XõUÒyð%èUUùaïŸlkñò°¬§MrzûvìÙuvÌCØ{”âñn6t¨?ýÙ“^I2:ÉgNG+§z©Ï®µš=WÌ=#Ì©ßÅæHbNj솇}Æøq( ƒ˜»Ž¯\)p×½ƒ·Cóôã\rÝ%+‡¡¹±e÷"Ùn§g $w¤3z¢>M•ˆ˜F`Òe¤R€N¢Îƒú»9´Èæ·*=O&]­t6È{Îa²žõR~Ìñ›°ËpiSÊNÃ’ Ý:ø@úu*ëBÏÖ¸ÛùÊ8²¸9^Ïr„)Ü´V
+ÈÉü†¯Ý÷ ×:»lÛãM1`ˆØÏ|\k¸Kõ£©JÍh ¤ÜHpÖHzs%‹åRçûˆ÷v94Øóv_MAª(½E+QÑöf¥QsgA­÷Jî»6ÆFÎú/—xcòÒ+ߟհ«qœiäËáK苬Š_Æç'ÕÄÑŒG»UeÍQÀºÅy?!îkëØ{+0Æ’}7|cí^Cíż·£/FקXÖ( 29zÂ\6‡M`Ô£b¹ÏsÝ™F1½ÆÓcÞ¢ñËãt÷ƒ?y¨ÿ2£ÙêŸCIe òoËs¡þæÌIª \ No newline at end of file
diff --git a/tests/resources/blametest.git/objects/d6/7268771ef5244f4aa224df29d4e4ae0bed2fd8 b/tests/resources/blametest.git/objects/d6/7268771ef5244f4aa224df29d4e4ae0bed2fd8
new file mode 100644
index 0000000..ee48478
--- /dev/null
+++ b/tests/resources/blametest.git/objects/d6/7268771ef5244f4aa224df29d4e4ae0bed2fd8
Binary files differ
diff --git a/tests/resources/blametest.git/objects/d6/afeea2c4657c743dedab24a8a62da96f63547d b/tests/resources/blametest.git/objects/d6/afeea2c4657c743dedab24a8a62da96f63547d
new file mode 100644
index 0000000..f3e52be
--- /dev/null
+++ b/tests/resources/blametest.git/objects/d6/afeea2c4657c743dedab24a8a62da96f63547d
Binary files differ
diff --git a/tests/resources/blametest.git/objects/d9/3e87a0863c7ec5e772f99e72ca9efddf0ca718 b/tests/resources/blametest.git/objects/d9/3e87a0863c7ec5e772f99e72ca9efddf0ca718
new file mode 100644
index 0000000..e1c0f00
--- /dev/null
+++ b/tests/resources/blametest.git/objects/d9/3e87a0863c7ec5e772f99e72ca9efddf0ca718
Binary files differ
diff --git a/tests/resources/blametest.git/objects/da/237394e6132d20d30f175b9b73c8638fddddda b/tests/resources/blametest.git/objects/da/237394e6132d20d30f175b9b73c8638fddddda
new file mode 100644
index 0000000..e9e1383
--- /dev/null
+++ b/tests/resources/blametest.git/objects/da/237394e6132d20d30f175b9b73c8638fddddda
@@ -0,0 +1,4 @@
+x•K
+Â0@]÷³ëJ™|'"¢ÞÀ$“ÔvÑVbz žÀíƒïÉ:ÏS­ðÐj)Ñif£Ñ‘äDNS ÆdOÌbs§Ñ[ìÞ±–¥Ab(
+¦Y;“ƒfç¬(‚˜„ƒ‰®‹[×
+·²À³Õ¸%8§Ïõ5µqK'Yç (ã‘zF8b@ìvº·µòŸÕÝKý£ÿc–?S \ No newline at end of file
diff --git a/tests/resources/blametest.git/objects/de/9fe35f9906e1994e083cc59c87232bf418795b b/tests/resources/blametest.git/objects/de/9fe35f9906e1994e083cc59c87232bf418795b
new file mode 100644
index 0000000..11ec90d
--- /dev/null
+++ b/tests/resources/blametest.git/objects/de/9fe35f9906e1994e083cc59c87232bf418795b
Binary files differ
diff --git a/tests/resources/blametest.git/objects/e5/b41c1ea533f87388ab69b13baf0b5a562d6243 b/tests/resources/blametest.git/objects/e5/b41c1ea533f87388ab69b13baf0b5a562d6243
new file mode 100644
index 0000000..7e5586c
--- /dev/null
+++ b/tests/resources/blametest.git/objects/e5/b41c1ea533f87388ab69b13baf0b5a562d6243
Binary files differ
diff --git a/tests/resources/blametest.git/objects/ef/32df4d259143933715c74951f932d9892364d1 b/tests/resources/blametest.git/objects/ef/32df4d259143933715c74951f932d9892364d1
new file mode 100644
index 0000000..d021ccf
--- /dev/null
+++ b/tests/resources/blametest.git/objects/ef/32df4d259143933715c74951f932d9892364d1
Binary files differ
diff --git a/tests/resources/blametest.git/objects/f0/5190494260c2f6b6d045ac9bf27cb6d7e0abcc b/tests/resources/blametest.git/objects/f0/5190494260c2f6b6d045ac9bf27cb6d7e0abcc
new file mode 100644
index 0000000..6df7d34
--- /dev/null
+++ b/tests/resources/blametest.git/objects/f0/5190494260c2f6b6d045ac9bf27cb6d7e0abcc
Binary files differ
diff --git a/tests/resources/blametest.git/objects/f4/f4b926582a2c23c6e3ba05309eaa89244c1d68 b/tests/resources/blametest.git/objects/f4/f4b926582a2c23c6e3ba05309eaa89244c1d68
new file mode 100644
index 0000000..32c1cdf
--- /dev/null
+++ b/tests/resources/blametest.git/objects/f4/f4b926582a2c23c6e3ba05309eaa89244c1d68
Binary files differ
diff --git a/tests/resources/blametest.git/objects/f9/264f7fbd31ae7a18b7931ed8946fb0aebb0af3 b/tests/resources/blametest.git/objects/f9/264f7fbd31ae7a18b7931ed8946fb0aebb0af3
new file mode 100644
index 0000000..942a7ee
--- /dev/null
+++ b/tests/resources/blametest.git/objects/f9/264f7fbd31ae7a18b7931ed8946fb0aebb0af3
Binary files differ
diff --git a/tests/resources/blametest.git/objects/fa/01940156471352d5483b4f26b7c849dfaa7eef b/tests/resources/blametest.git/objects/fa/01940156471352d5483b4f26b7c849dfaa7eef
new file mode 100644
index 0000000..b392e44
--- /dev/null
+++ b/tests/resources/blametest.git/objects/fa/01940156471352d5483b4f26b7c849dfaa7eef
Binary files differ
diff --git a/tests/resources/blametest.git/refs/heads/master b/tests/resources/blametest.git/refs/heads/master
new file mode 100644
index 0000000..9d17158
--- /dev/null
+++ b/tests/resources/blametest.git/refs/heads/master
@@ -0,0 +1 @@
+d93e87a0863c7ec5e772f99e72ca9efddf0ca718
diff --git a/tests/resources/certs/61f2ddb6.0 b/tests/resources/certs/61f2ddb6.0
new file mode 100644
index 0000000..7d9ef6f
--- /dev/null
+++ b/tests/resources/certs/61f2ddb6.0
@@ -0,0 +1,31 @@
+-----BEGIN CERTIFICATE-----
+MIIFWzCCA0MCFESY816VkhBPUOsdp7djKW5q4ZVzMA0GCSqGSIb3DQEBCwUAMGox
+CzAJBgNVBAYTAlVTMRYwFAYDVQQIDA1NYXNzYWNodXNldHRzMRIwEAYDVQQHDAlD
+YW1icmlkZ2UxFDASBgNVBAoMC2xpYmdpdDIub3JnMRkwFwYDVQQDDBB0ZXN0Lmxp
+YmdpdDIub3JnMB4XDTIxMDgyNTE4NTExMVoXDTMxMDgyMzE4NTExMVowajELMAkG
+A1UEBhMCVVMxFjAUBgNVBAgMDU1hc3NhY2h1c2V0dHMxEjAQBgNVBAcMCUNhbWJy
+aWRnZTEUMBIGA1UECgwLbGliZ2l0Mi5vcmcxGTAXBgNVBAMMEHRlc3QubGliZ2l0
+Mi5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvaRUaM3IJh9N
+G6Yc7tHioUsIGU0MkzSvy/X6O/vONnuuioiJQyPIvfRSvZR2iQj8THTypDGhWn3r
+6h2wk5eOUGwJH2N9FrrlEBdpsMc7SKdiJXwTI30mkK3/qru8NzE71dgCkYp1xhKw
+edTkAFK+PkvyVLFL7K35cx8Bxfamyssdb+qGWa7g4P27CWUdvQgmurrzzPIMZiLD
+/cI1Kwer/N7nTY/6CSs9dcHTlanyZdf+mQ50+//vI4F6+OduGHJkxRF48jLUz1rz
+P3WGRMRbHjCmvWpX/9DLgqGk7XTy0hNgNUCit6kawwcv5y7SP/ii86MkynAHn5i8
+d+zhXjdrSSy8i0IbRJafnxmtrsmjGeIzraJSRqMlv7KKWEBz+alm6vlePnRUbWB7
+0po5uSsRPya6kJJCzMjIfKq1dgXq33m9jCG2wU+L4fEHVlEkFGXYTspMlIBNUjTc
+c45+e1EpamF8aHm32PP8gTF8fGZzQjOXmNW5g7t0joWMGZ+Ao2jYc1pG3SOARi36
+azrmB5/XJqbbfVZEzIue01fO/5R8RgabOP1qWUjH2KLb8zTDok+CW0ULNseU+MKf
+PHXG2OjxcR0vTqop2V6JlKTXXx3/TOD16/+mSrrPzNDejLrkvAH9oN38YpMBM8eg
+vfivHNRm0jjdGbv2OOPEBLEf1cNimQIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQBZ
+znFta24sWoqdgKXKAK5RHAh/HyOvTInwcXi9RU4XjYlbqNVs0ODR74VRZINoyAL2
+bo+x/iUuAp9+b8fjr79fpVof3nSMU7UtMcT1nvzVmaUYSkKQ0f/9vK4yg0kao1bV
+WwhIc0slKgOJjEicPVs3kd+duv5vakQeUajLPGM8SiS1F/nF67rIuZLdJn2Qp+im
+w5Q3Pjgqw5VrJxyk3AaUcntKHpWy1POLyNV79tXra6BxbtQVlRS0+h1MHELARDFx
+1ZtgyAe5YbWM7WrIiFKD4mmKZu4GMnJDXVpfUub5g0U/e7L/gg6Z1UyYZuln6axw
+RojuAHo1uAWFUsjhWLYV/7P/l/dC+7gFjvSsUqb1+U7jXObzfKjXo/FwYcy4VsVv
+xNbglbhdVjAo/YBTJuf3L0UZjSbxvQIYS+v8u1ECeWE6SH6cHRzryeo5wO4h8NJR
+n30xsvocHFbs4LWy5BVfMUo6wGUy0Y+1gSwSqVMv3JPuLwxUsv0HPdeC00Ab9cHq
+kYXPNZXg3a6orTDa4hJLdAm2V/fn/2KKJYlNj7iCL664QgoCHl7LFyLMiwFVCu5h
+4JjGL3Q+8MondaLZlq5YDmvtj979AyM/7qL4XAE2oofQ4J5dqnKKpMkWdAM/fI/9
+N5DK/4zMXJWgIED0yo2SSZHQmuqZplacOhmfjjZigQ==
+-----END CERTIFICATE-----
diff --git a/tests/resources/certs/db4f60b0.0 b/tests/resources/certs/db4f60b0.0
new file mode 100644
index 0000000..7d9ef6f
--- /dev/null
+++ b/tests/resources/certs/db4f60b0.0
@@ -0,0 +1,31 @@
+-----BEGIN CERTIFICATE-----
+MIIFWzCCA0MCFESY816VkhBPUOsdp7djKW5q4ZVzMA0GCSqGSIb3DQEBCwUAMGox
+CzAJBgNVBAYTAlVTMRYwFAYDVQQIDA1NYXNzYWNodXNldHRzMRIwEAYDVQQHDAlD
+YW1icmlkZ2UxFDASBgNVBAoMC2xpYmdpdDIub3JnMRkwFwYDVQQDDBB0ZXN0Lmxp
+YmdpdDIub3JnMB4XDTIxMDgyNTE4NTExMVoXDTMxMDgyMzE4NTExMVowajELMAkG
+A1UEBhMCVVMxFjAUBgNVBAgMDU1hc3NhY2h1c2V0dHMxEjAQBgNVBAcMCUNhbWJy
+aWRnZTEUMBIGA1UECgwLbGliZ2l0Mi5vcmcxGTAXBgNVBAMMEHRlc3QubGliZ2l0
+Mi5vcmcwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQCgvaRUaM3IJh9N
+G6Yc7tHioUsIGU0MkzSvy/X6O/vONnuuioiJQyPIvfRSvZR2iQj8THTypDGhWn3r
+6h2wk5eOUGwJH2N9FrrlEBdpsMc7SKdiJXwTI30mkK3/qru8NzE71dgCkYp1xhKw
+edTkAFK+PkvyVLFL7K35cx8Bxfamyssdb+qGWa7g4P27CWUdvQgmurrzzPIMZiLD
+/cI1Kwer/N7nTY/6CSs9dcHTlanyZdf+mQ50+//vI4F6+OduGHJkxRF48jLUz1rz
+P3WGRMRbHjCmvWpX/9DLgqGk7XTy0hNgNUCit6kawwcv5y7SP/ii86MkynAHn5i8
+d+zhXjdrSSy8i0IbRJafnxmtrsmjGeIzraJSRqMlv7KKWEBz+alm6vlePnRUbWB7
+0po5uSsRPya6kJJCzMjIfKq1dgXq33m9jCG2wU+L4fEHVlEkFGXYTspMlIBNUjTc
+c45+e1EpamF8aHm32PP8gTF8fGZzQjOXmNW5g7t0joWMGZ+Ao2jYc1pG3SOARi36
+azrmB5/XJqbbfVZEzIue01fO/5R8RgabOP1qWUjH2KLb8zTDok+CW0ULNseU+MKf
+PHXG2OjxcR0vTqop2V6JlKTXXx3/TOD16/+mSrrPzNDejLrkvAH9oN38YpMBM8eg
+vfivHNRm0jjdGbv2OOPEBLEf1cNimQIDAQABMA0GCSqGSIb3DQEBCwUAA4ICAQBZ
+znFta24sWoqdgKXKAK5RHAh/HyOvTInwcXi9RU4XjYlbqNVs0ODR74VRZINoyAL2
+bo+x/iUuAp9+b8fjr79fpVof3nSMU7UtMcT1nvzVmaUYSkKQ0f/9vK4yg0kao1bV
+WwhIc0slKgOJjEicPVs3kd+duv5vakQeUajLPGM8SiS1F/nF67rIuZLdJn2Qp+im
+w5Q3Pjgqw5VrJxyk3AaUcntKHpWy1POLyNV79tXra6BxbtQVlRS0+h1MHELARDFx
+1ZtgyAe5YbWM7WrIiFKD4mmKZu4GMnJDXVpfUub5g0U/e7L/gg6Z1UyYZuln6axw
+RojuAHo1uAWFUsjhWLYV/7P/l/dC+7gFjvSsUqb1+U7jXObzfKjXo/FwYcy4VsVv
+xNbglbhdVjAo/YBTJuf3L0UZjSbxvQIYS+v8u1ECeWE6SH6cHRzryeo5wO4h8NJR
+n30xsvocHFbs4LWy5BVfMUo6wGUy0Y+1gSwSqVMv3JPuLwxUsv0HPdeC00Ab9cHq
+kYXPNZXg3a6orTDa4hJLdAm2V/fn/2KKJYlNj7iCL664QgoCHl7LFyLMiwFVCu5h
+4JjGL3Q+8MondaLZlq5YDmvtj979AyM/7qL4XAE2oofQ4J5dqnKKpMkWdAM/fI/9
+N5DK/4zMXJWgIED0yo2SSZHQmuqZplacOhmfjjZigQ==
+-----END CERTIFICATE-----
diff --git a/tests/resources/cherrypick/.gitted/HEAD b/tests/resources/cherrypick/.gitted/HEAD
new file mode 100644
index 0000000..656ac0e
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/automerge-branch
diff --git a/tests/resources/cherrypick/.gitted/config b/tests/resources/cherrypick/.gitted/config
new file mode 100644
index 0000000..6c9406b
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/config
@@ -0,0 +1,7 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
+ precomposeunicode = true
diff --git a/tests/resources/cherrypick/.gitted/index b/tests/resources/cherrypick/.gitted/index
new file mode 100644
index 0000000..7291006
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/index
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/info/exclude b/tests/resources/cherrypick/.gitted/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/cherrypick/.gitted/objects/01/a2b453c2647c71ccfefc285f2266d1f00b8253 b/tests/resources/cherrypick/.gitted/objects/01/a2b453c2647c71ccfefc285f2266d1f00b8253
new file mode 100644
index 0000000..736a7f5
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/01/a2b453c2647c71ccfefc285f2266d1f00b8253
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/02/67838e09bbc5969bba035be2d27c8a6de694d8 b/tests/resources/cherrypick/.gitted/objects/02/67838e09bbc5969bba035be2d27c8a6de694d8
new file mode 100644
index 0000000..4eacb26
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/02/67838e09bbc5969bba035be2d27c8a6de694d8
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/06/3fc9f01e6e9ec2a8d8f749885e931875e50d37 b/tests/resources/cherrypick/.gitted/objects/06/3fc9f01e6e9ec2a8d8f749885e931875e50d37
new file mode 100644
index 0000000..48fa6ef
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/06/3fc9f01e6e9ec2a8d8f749885e931875e50d37
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/08/9ac03f76058b5ba0b44bb268f317f9242481e9 b/tests/resources/cherrypick/.gitted/objects/08/9ac03f76058b5ba0b44bb268f317f9242481e9
new file mode 100644
index 0000000..06d1c69
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/08/9ac03f76058b5ba0b44bb268f317f9242481e9
@@ -0,0 +1,3 @@
+x¥ÎA
+Â0@Q×9Å왤“i"î—Þ`L§µB¬MÓ…··àܾÅç§)籂#ÞÕ¢
+^CNØä™”Îb+˜%¸˜¬Š÷¨bÞRôU!õ‰zŒ1Jh¸õ)JO}딼ëƒ b½‘µ>¦WIóª \´äqy¬ŸŽÏŸ 祖QªÒ”O`›ÈDÍ6{tˆfÓmµê_sÓy‹‚@Ö2¨ù("O- \ No newline at end of file
diff --git a/tests/resources/cherrypick/.gitted/objects/0d/447a6c2528b06616cde3b209a4b4ea3dcb8d65 b/tests/resources/cherrypick/.gitted/objects/0d/447a6c2528b06616cde3b209a4b4ea3dcb8d65
new file mode 100644
index 0000000..9a3ea32
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/0d/447a6c2528b06616cde3b209a4b4ea3dcb8d65
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/11/24c2c1ae07b26fded662d6c3f3631d9dc16f88 b/tests/resources/cherrypick/.gitted/objects/11/24c2c1ae07b26fded662d6c3f3631d9dc16f88
new file mode 100644
index 0000000..62abc3c
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/11/24c2c1ae07b26fded662d6c3f3631d9dc16f88
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/12/905f4ea5b76f9d3fdcfe73e462201c06ae632a b/tests/resources/cherrypick/.gitted/objects/12/905f4ea5b76f9d3fdcfe73e462201c06ae632a
new file mode 100644
index 0000000..162844a
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/12/905f4ea5b76f9d3fdcfe73e462201c06ae632a
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/19/c5c7207054604b69c84d08a7571ef9672bb5c2 b/tests/resources/cherrypick/.gitted/objects/19/c5c7207054604b69c84d08a7571ef9672bb5c2
new file mode 100644
index 0000000..d5cd6d3
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/19/c5c7207054604b69c84d08a7571ef9672bb5c2
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/1c/2116845780455ecf916538c1cc27c4222452af b/tests/resources/cherrypick/.gitted/objects/1c/2116845780455ecf916538c1cc27c4222452af
new file mode 100644
index 0000000..f9a841d
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/1c/2116845780455ecf916538c1cc27c4222452af
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/1c/c85eb4ff0a8438fde1b14274c6f87f891b36a0 b/tests/resources/cherrypick/.gitted/objects/1c/c85eb4ff0a8438fde1b14274c6f87f891b36a0
new file mode 100644
index 0000000..98b792b
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/1c/c85eb4ff0a8438fde1b14274c6f87f891b36a0
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/1e/1cb7391d25dcd8daba88f1f627f3045982286c b/tests/resources/cherrypick/.gitted/objects/1e/1cb7391d25dcd8daba88f1f627f3045982286c
new file mode 100644
index 0000000..10a5be6
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/1e/1cb7391d25dcd8daba88f1f627f3045982286c
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/20/fc1a4c9d994021f43d33ab75e4252e27ca661d b/tests/resources/cherrypick/.gitted/objects/20/fc1a4c9d994021f43d33ab75e4252e27ca661d
new file mode 100644
index 0000000..c8b26cd
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/20/fc1a4c9d994021f43d33ab75e4252e27ca661d
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/28/d9eb4208074ad1cc84e71ccc908b34573f05d2 b/tests/resources/cherrypick/.gitted/objects/28/d9eb4208074ad1cc84e71ccc908b34573f05d2
new file mode 100644
index 0000000..80363b0
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/28/d9eb4208074ad1cc84e71ccc908b34573f05d2
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/2a/26c7e88b285613b302ba76712bc998863f3cbc b/tests/resources/cherrypick/.gitted/objects/2a/26c7e88b285613b302ba76712bc998863f3cbc
new file mode 100644
index 0000000..2831139
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/2a/26c7e88b285613b302ba76712bc998863f3cbc
@@ -0,0 +1 @@
+x¥MjÃ0…»Ö)f_jôï„]¡ËÜ`$—*Nä1´·¯C=@—ï{ðñ^YZ›lLOÒ™Á”‚³¯Uz‡ub“·£/±âX1™ì"iu£ÎWN9ºêbÙÒ„ºèZŽS”&r4£mrY:¼Q¹o¼Â+÷6¯—í{…ÃÇ/{?­ÒgÊÒŽ`\ŠÞk-<k«µÚé>Uø_uæ+5ž ÎŸìùx9þ…a?õ¨Õ7˜W… \ No newline at end of file
diff --git a/tests/resources/cherrypick/.gitted/objects/2a/c3b376093de405b0a951bff578655b1c2b7fa1 b/tests/resources/cherrypick/.gitted/objects/2a/c3b376093de405b0a951bff578655b1c2b7fa1
new file mode 100644
index 0000000..a3294d7
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/2a/c3b376093de405b0a951bff578655b1c2b7fa1
@@ -0,0 +1 @@
+x•ÁJÅ0E]ç+f÷¢¤“Išw‚àGLÒÉk…´š¦ ÿÞÊóÜž‡{óVëÒG{×›*¤ˆA9¹ ˆÊÄšcô:ÊPÜ”ˆCJ’Bž¬ù”¦k‡\2ËÌ]}ˆj‰¥PQÉãD6b”Á9ú¼5x“üuè¯Úê²ÏÇ÷O7v}Ù{[¤ËcÞê3 Ž‘‹žàÞ¢µæ¤çÔ®ÿ#þEÌ»¶ëy³Éšg¸TÙÏà–µoPÕÃM™Í/X˜ \ No newline at end of file
diff --git a/tests/resources/cherrypick/.gitted/objects/2c/acbcaabf785f1ac231e8519849d4ad38692f2c b/tests/resources/cherrypick/.gitted/objects/2c/acbcaabf785f1ac231e8519849d4ad38692f2c
new file mode 100644
index 0000000..74b48dd
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/2c/acbcaabf785f1ac231e8519849d4ad38692f2c
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/35/cb210149022c7379b0a67b0dec13cc628ff87d b/tests/resources/cherrypick/.gitted/objects/35/cb210149022c7379b0a67b0dec13cc628ff87d
new file mode 100644
index 0000000..c0466f4
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/35/cb210149022c7379b0a67b0dec13cc628ff87d
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/38/c05a857e831a7e759d83778bfc85d003e21c45 b/tests/resources/cherrypick/.gitted/objects/38/c05a857e831a7e759d83778bfc85d003e21c45
new file mode 100644
index 0000000..d4f1cf8
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/38/c05a857e831a7e759d83778bfc85d003e21c45
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/3f/9eed8946df9e2c737d3b8dc0b8e78959aacd92 b/tests/resources/cherrypick/.gitted/objects/3f/9eed8946df9e2c737d3b8dc0b8e78959aacd92
new file mode 100644
index 0000000..8c4d6d9
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/3f/9eed8946df9e2c737d3b8dc0b8e78959aacd92
@@ -0,0 +1,5 @@
+xMjÃ0…»Ö)fj¤ñH– „ì]æÃdÔ¸Dq#ËÐÞ¾
+-tßåûáã=YJ™`HO­ª‚t.DòS´ä½JN.ø1ŠÁIÉ#góÁUo $ e›Râ8†É‡¨–gÊj/žÉFŒì¼á­]–
+¯,÷MW8j-ózÙ¾VxyÿñÞk«37d){pc
+Ôˆ°³h­énŸÚô
+îbNzã¢gÈóUÇ¡}6xÞÿ‰¡ŸzÄæï*V8 \ No newline at end of file
diff --git a/tests/resources/cherrypick/.gitted/objects/40/9a1bec58bf35348e8b62b72bb9c1f45cf5a587 b/tests/resources/cherrypick/.gitted/objects/40/9a1bec58bf35348e8b62b72bb9c1f45cf5a587
new file mode 100644
index 0000000..60d5dca
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/40/9a1bec58bf35348e8b62b72bb9c1f45cf5a587
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/44/cd2ed2052c9c68f9a439d208e9614dc2a55c70 b/tests/resources/cherrypick/.gitted/objects/44/cd2ed2052c9c68f9a439d208e9614dc2a55c70
new file mode 100644
index 0000000..8697c4e
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/44/cd2ed2052c9c68f9a439d208e9614dc2a55c70
@@ -0,0 +1 @@
+x¥ÁNÄ0C9ç+推’É$M$´â†Ä‘XÍ&Úi!M%ø{ºð ÜìgɲÓZëÜÇp×› .É0¥˜c$¦ÍÖòetBèPpLì½Éꃛ,RITtŒ‘ƒõ£óA4E.TFr˜I lœâ½OkƒNŸ»lð,­ÎÛ´oðxýcoO[o3wÒZO`lôD.÷µV=¦vùW‰z•…«d(ó»Ø¡ux8ýš›ŽO·ô¼.çKã%MêÖ?ZØ \ No newline at end of file
diff --git a/tests/resources/cherrypick/.gitted/objects/48/7434cace79238a7091e2220611d4f20a765690 b/tests/resources/cherrypick/.gitted/objects/48/7434cace79238a7091e2220611d4f20a765690
new file mode 100644
index 0000000..a1fa599
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/48/7434cace79238a7091e2220611d4f20a765690
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/49/20ad2f17162dcc8823ad491444dcb87f5899c9 b/tests/resources/cherrypick/.gitted/objects/49/20ad2f17162dcc8823ad491444dcb87f5899c9
new file mode 100644
index 0000000..bf96fcc
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/49/20ad2f17162dcc8823ad491444dcb87f5899c9
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 b/tests/resources/cherrypick/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904
new file mode 100644
index 0000000..adf6411
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/4c/532774cc1fea37f6efc2256763a64d38c8cdde b/tests/resources/cherrypick/.gitted/objects/4c/532774cc1fea37f6efc2256763a64d38c8cdde
new file mode 100644
index 0000000..2e56d74
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/4c/532774cc1fea37f6efc2256763a64d38c8cdde
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/51/145af30d411a50195b66517d825e69bf57ed22 b/tests/resources/cherrypick/.gitted/objects/51/145af30d411a50195b66517d825e69bf57ed22
new file mode 100644
index 0000000..3e01376
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/51/145af30d411a50195b66517d825e69bf57ed22
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/54/61de53ffadbf15be4dd6345997c15689573209 b/tests/resources/cherrypick/.gitted/objects/54/61de53ffadbf15be4dd6345997c15689573209
new file mode 100644
index 0000000..7d2b233
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/54/61de53ffadbf15be4dd6345997c15689573209
@@ -0,0 +1,4 @@
+xMjÃ0…»Ö)fj¤ñH– „ì]æÃdÔ¸Dq#ËÐÞ¾
+-tßåûáã=YJ™`HO­ª‚t.DòS´ä½JN.ø1ŠÁIÉ#góÁUo $ e›Râ8†É‡¨–gÊj/žÉFŒì¼á­]–
+¯,÷MW8j-ózÙ¾VxyÿñÞk«37d){pc
+Ôˆ°³h­énŸÚô?J¿sÒ=Cž¯:í³ÁóþO ýÔ#6ßïäV< \ No newline at end of file
diff --git a/tests/resources/cherrypick/.gitted/objects/54/784f10955e92ab27e4fa832e40cb2baf1edbdc b/tests/resources/cherrypick/.gitted/objects/54/784f10955e92ab27e4fa832e40cb2baf1edbdc
new file mode 100644
index 0000000..2a5bcec
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/54/784f10955e92ab27e4fa832e40cb2baf1edbdc
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/56/3f6473a3858f99b80e5f93c660512ed38e1e6f b/tests/resources/cherrypick/.gitted/objects/56/3f6473a3858f99b80e5f93c660512ed38e1e6f
new file mode 100644
index 0000000..8847ed6
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/56/3f6473a3858f99b80e5f93c660512ed38e1e6f
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/58/a957ef0061c1a8ef995c855dfab4f5da8d6617 b/tests/resources/cherrypick/.gitted/objects/58/a957ef0061c1a8ef995c855dfab4f5da8d6617
new file mode 100644
index 0000000..f161a19
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/58/a957ef0061c1a8ef995c855dfab4f5da8d6617
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/5d/c7e1f440ce74d5503a0dfbc6c30e091475f774 b/tests/resources/cherrypick/.gitted/objects/5d/c7e1f440ce74d5503a0dfbc6c30e091475f774
new file mode 100644
index 0000000..77deeaf
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/5d/c7e1f440ce74d5503a0dfbc6c30e091475f774
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/5e/2206cda1c56430ad107a6866a829c159e0b9ea b/tests/resources/cherrypick/.gitted/objects/5e/2206cda1c56430ad107a6866a829c159e0b9ea
new file mode 100644
index 0000000..aa30f50
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/5e/2206cda1c56430ad107a6866a829c159e0b9ea
@@ -0,0 +1 @@
+x+)JMU044d040031QHËÌI5Ô+©(aøô¦§çãÊßU ÜE9sì‘ \uI‘X‘œÌvKYÕ;7níêøøMý3Kd“F’"c°¢ˆ•áï®x?3¦5ö×–¯·zÓÄѨ1* \ No newline at end of file
diff --git a/tests/resources/cherrypick/.gitted/objects/5f/77a2a13935ac62a629553f8944ad57b1ed8b4a b/tests/resources/cherrypick/.gitted/objects/5f/77a2a13935ac62a629553f8944ad57b1ed8b4a
new file mode 100644
index 0000000..5e622a1
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/5f/77a2a13935ac62a629553f8944ad57b1ed8b4a
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/63/c0d92b95253c4a40d3883f423a54be47d2c4c8 b/tests/resources/cherrypick/.gitted/objects/63/c0d92b95253c4a40d3883f423a54be47d2c4c8
new file mode 100644
index 0000000..eafe2c3
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/63/c0d92b95253c4a40d3883f423a54be47d2c4c8
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/6c/e83eb5f0fd34a10c3d25c6b36d2ed7ec0d6ce7 b/tests/resources/cherrypick/.gitted/objects/6c/e83eb5f0fd34a10c3d25c6b36d2ed7ec0d6ce7
new file mode 100644
index 0000000..1c1f503
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/6c/e83eb5f0fd34a10c3d25c6b36d2ed7ec0d6ce7
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/6d/1c2afe5eeb9e497528e2780ac468a5465cbc96 b/tests/resources/cherrypick/.gitted/objects/6d/1c2afe5eeb9e497528e2780ac468a5465cbc96
new file mode 100644
index 0000000..a98378a
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/6d/1c2afe5eeb9e497528e2780ac468a5465cbc96
@@ -0,0 +1 @@
+x¥Î=jÃ@@áÔ{Šé fvöLpgp“3ÌŽF–²œÕªðímðÒ¾âãɺ,sòþ«7Uàì$Ž ©¤š1 :HðEcÈ.d*1ŽQ«p5nzïà‚T²h}A"I.•ŠSÅAÅ:‘Hys ï}Z\YþvÝà¢m™·inpúý´ÛyëmæÎGY—o°®Ä`É$Dó®ïÕ®ÿBÌO{L|‡f^äðOA \ No newline at end of file
diff --git a/tests/resources/cherrypick/.gitted/objects/74/f06b5bfec6d33d7264f73606b57a7c0b963819 b/tests/resources/cherrypick/.gitted/objects/74/f06b5bfec6d33d7264f73606b57a7c0b963819
new file mode 100644
index 0000000..732011f
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/74/f06b5bfec6d33d7264f73606b57a7c0b963819
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/82/8b08c52d2cba30952e0e008f60b25b5ba0d41a b/tests/resources/cherrypick/.gitted/objects/82/8b08c52d2cba30952e0e008f60b25b5ba0d41a
new file mode 100644
index 0000000..302014b
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/82/8b08c52d2cba30952e0e008f60b25b5ba0d41a
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/85/36dd6f0ec3ddecb9f9b6c8c64c6d322cd01211 b/tests/resources/cherrypick/.gitted/objects/85/36dd6f0ec3ddecb9f9b6c8c64c6d322cd01211
new file mode 100644
index 0000000..db6faa9
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/85/36dd6f0ec3ddecb9f9b6c8c64c6d322cd01211
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/85/a4a1d791973644f24c72f5e89420d3064cc452 b/tests/resources/cherrypick/.gitted/objects/85/a4a1d791973644f24c72f5e89420d3064cc452
new file mode 100644
index 0000000..7fe69b6
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/85/a4a1d791973644f24c72f5e89420d3064cc452
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/8b/5c30499a71001189b647f4d5b57fa8f04897ce b/tests/resources/cherrypick/.gitted/objects/8b/5c30499a71001189b647f4d5b57fa8f04897ce
new file mode 100644
index 0000000..8b1638f
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/8b/5c30499a71001189b647f4d5b57fa8f04897ce
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/96/4ea3da044d9083181a88ba6701de9e35778bf4 b/tests/resources/cherrypick/.gitted/objects/96/4ea3da044d9083181a88ba6701de9e35778bf4
new file mode 100644
index 0000000..2dec33f
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/96/4ea3da044d9083181a88ba6701de9e35778bf4
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/9c/c39fca3765a2facbe31157f7d60c2602193f36 b/tests/resources/cherrypick/.gitted/objects/9c/c39fca3765a2facbe31157f7d60c2602193f36
new file mode 100644
index 0000000..0031445
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/9c/c39fca3765a2facbe31157f7d60c2602193f36
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/9c/cb9bf50c011fd58dcbaa65df917bf79539717f b/tests/resources/cherrypick/.gitted/objects/9c/cb9bf50c011fd58dcbaa65df917bf79539717f
new file mode 100644
index 0000000..1266aff
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/9c/cb9bf50c011fd58dcbaa65df917bf79539717f
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/a1/0b59f4280491afe6e430c30654a7acc67d4a33 b/tests/resources/cherrypick/.gitted/objects/a1/0b59f4280491afe6e430c30654a7acc67d4a33
new file mode 100644
index 0000000..7aa0a5d
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/a1/0b59f4280491afe6e430c30654a7acc67d4a33
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/a2/1b4bfe7a04ab18024fb57f4ae9a52a1acef394 b/tests/resources/cherrypick/.gitted/objects/a2/1b4bfe7a04ab18024fb57f4ae9a52a1acef394
new file mode 100644
index 0000000..07b7195
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/a2/1b4bfe7a04ab18024fb57f4ae9a52a1acef394
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/a4/3a050c588d4e92f11a6b139680923e9728477d b/tests/resources/cherrypick/.gitted/objects/a4/3a050c588d4e92f11a6b139680923e9728477d
new file mode 100644
index 0000000..4713fb2
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/a4/3a050c588d4e92f11a6b139680923e9728477d
@@ -0,0 +1 @@
+x¥ÎMjÃ0†á®uŠÙÂHŠþ ”BÜb4Ç.‘ÝJ2´· Gèöáãåã½”µƒqñ¥WÈ9¼ãl=#»`5GÎsDD5ê(ꋪlìXš!—„Æp°!e$2NÂÚ2{ç9†IÑÑ—½ÂøûW©emËñÛàõóÏîï­×•:y/o mò“‚7pBƒ¨†Ž«]þQ mw™`^¢Ïý§Ã¾A¡6ºê mT \ No newline at end of file
diff --git a/tests/resources/cherrypick/.gitted/objects/a5/8ca3fee5eb68b11adc2703e5843f968c9dad1e b/tests/resources/cherrypick/.gitted/objects/a5/8ca3fee5eb68b11adc2703e5843f968c9dad1e
new file mode 100644
index 0000000..1c3f2fb
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/a5/8ca3fee5eb68b11adc2703e5843f968c9dad1e
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/a6/61b5dec1004e2c62654ded3762370c27cf266b b/tests/resources/cherrypick/.gitted/objects/a6/61b5dec1004e2c62654ded3762370c27cf266b
new file mode 100644
index 0000000..d94a954
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/a6/61b5dec1004e2c62654ded3762370c27cf266b
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/a6/9ef8fcbb9a2c509a7dbf4f23d257eb551d5610 b/tests/resources/cherrypick/.gitted/objects/a6/9ef8fcbb9a2c509a7dbf4f23d257eb551d5610
new file mode 100644
index 0000000..69feba2
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/a6/9ef8fcbb9a2c509a7dbf4f23d257eb551d5610
@@ -0,0 +1 @@
+x¥Î1ŠÃ0@Ñ­uŠéA¤,!] åÞ`,/hÈr‘ÛÇ°GHûŠÏÏK­s¤ðÕ›*heæ”Éæ§8ªÅèýèJ*(&ó¦rÉTlJIØ…è«¥$…JD%YF–ÁÙú}ip•üÜt…‹¶:¯÷íµÂ÷ï¿ÝÎko³t9楞`p)9ö­5»î«]?Š˜}nsS¨ÚnjÞGPOL \ No newline at end of file
diff --git a/tests/resources/cherrypick/.gitted/objects/a8/3c6f70297b805dedc549e6583582966f6ebcab b/tests/resources/cherrypick/.gitted/objects/a8/3c6f70297b805dedc549e6583582966f6ebcab
new file mode 100644
index 0000000..5a6db50
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/a8/3c6f70297b805dedc549e6583582966f6ebcab
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/a9/020cd240774e4d672732bcb82d516d9685da76 b/tests/resources/cherrypick/.gitted/objects/a9/020cd240774e4d672732bcb82d516d9685da76
new file mode 100644
index 0000000..61741af
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/a9/020cd240774e4d672732bcb82d516d9685da76
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/ab/4115f808bc585b60f822da7020af86d20f62c8 b/tests/resources/cherrypick/.gitted/objects/ab/4115f808bc585b60f822da7020af86d20f62c8
new file mode 100644
index 0000000..08c4bef
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/ab/4115f808bc585b60f822da7020af86d20f62c8
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/ab/e4603bc7cd5b8167a267e0e2418fd2348f8cff b/tests/resources/cherrypick/.gitted/objects/ab/e4603bc7cd5b8167a267e0e2418fd2348f8cff
new file mode 100644
index 0000000..4e4fe6f
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/ab/e4603bc7cd5b8167a267e0e2418fd2348f8cff
@@ -0,0 +1,4 @@
+x¥KNÄ0DYç½›Ùî´?Bì8D»ÓžÉHNÀqÜžŒFœ€í«ª'•¬µÎÐƇÞT!‰`*Â<±+,YÑZ
+%LÞˆóÆÙ„ýðÅM—ì“–X$çÄNÈ$S.cq89
+š‰ìDÞš¿¾¦ìo{¢ìxŠFL)Æ”‰ÅÄÈÁ}ÞûemðÁò½ëïÚê¼]öŸ ^®wv~Ûz›¹ó³¬õ,&?Ži„GãŒz\ëú/Éð©í¬/rSåíž`^ú
+õ=Ý£áX¾fZ \ No newline at end of file
diff --git a/tests/resources/cherrypick/.gitted/objects/b8/26e9b36e22e949ec885e7a1f3db496bbab6cd0 b/tests/resources/cherrypick/.gitted/objects/b8/26e9b36e22e949ec885e7a1f3db496bbab6cd0
new file mode 100644
index 0000000..e3bf3a0
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/b8/26e9b36e22e949ec885e7a1f3db496bbab6cd0
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/ba/fbf6912c09505ac60575cd43d3f2aba3bd84d8 b/tests/resources/cherrypick/.gitted/objects/ba/fbf6912c09505ac60575cd43d3f2aba3bd84d8
new file mode 100644
index 0000000..956da8b
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/ba/fbf6912c09505ac60575cd43d3f2aba3bd84d8
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/bb/14296ffa9dfbf935ec9ce2f9ed7808d952226b b/tests/resources/cherrypick/.gitted/objects/bb/14296ffa9dfbf935ec9ce2f9ed7808d952226b
new file mode 100644
index 0000000..b558368
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/bb/14296ffa9dfbf935ec9ce2f9ed7808d952226b
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/bc/4dd0744364d1db380a9811bd264c101065231e b/tests/resources/cherrypick/.gitted/objects/bc/4dd0744364d1db380a9811bd264c101065231e
new file mode 100644
index 0000000..01d88a2
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/bc/4dd0744364d1db380a9811bd264c101065231e
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/bd/65d4083845ed5ed4e1fe5feb85ac395d0760c8 b/tests/resources/cherrypick/.gitted/objects/bd/65d4083845ed5ed4e1fe5feb85ac395d0760c8
new file mode 100644
index 0000000..6a0eccb
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/bd/65d4083845ed5ed4e1fe5feb85ac395d0760c8
@@ -0,0 +1,2 @@
+x¥ÍA
+B!€áÖžböAŒ6ŠBD» [¨o^Ï@$ݾ¡í·øÿÜj-Ú›ƒtf ä]²#““ã":dKiõ‹ Æû51S@RqÊÖ:<b~Opç^ËØægÀåõ³çmH/Qâ)·z}ÎjCˆpDƒ¨vÝ×ÂEÔK»<È \ No newline at end of file
diff --git a/tests/resources/cherrypick/.gitted/objects/bd/6ffc8c6c41f0f85ff9e3d61c9479516bac0024 b/tests/resources/cherrypick/.gitted/objects/bd/6ffc8c6c41f0f85ff9e3d61c9479516bac0024
new file mode 100644
index 0000000..56f8367
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/bd/6ffc8c6c41f0f85ff9e3d61c9479516bac0024
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/bd/a51965cb36c0c5731c8cb50b80a36cac81018e b/tests/resources/cherrypick/.gitted/objects/bd/a51965cb36c0c5731c8cb50b80a36cac81018e
new file mode 100644
index 0000000..1187a70
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/bd/a51965cb36c0c5731c8cb50b80a36cac81018e
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/ce/d8fb81b6ec534d5deaf2a48b4b96c799712507 b/tests/resources/cherrypick/.gitted/objects/ce/d8fb81b6ec534d5deaf2a48b4b96c799712507
new file mode 100644
index 0000000..569ee0c
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/ce/d8fb81b6ec534d5deaf2a48b4b96c799712507
@@ -0,0 +1 @@
+x¥ÎÍJÅ0@a×yŠì…Ëä?] ¾Åd2i+7¦)èÛ[ðÜ~‹Ã¡ÞÚ6¥öîafY–’ÏÕH1„ì¼K%¢+E!…(>qð>¥q”µehMÁ„”}ÈP˜”!ò:ÖCxεùŽôuò!ßx´íXÏŸC>}üÙòṟáÄõö,•IÞç‚“ Ä¥×êäEÄë9{ã±0æ;KZq_®¾ËºÝYÝæ÷¿t3V \ No newline at end of file
diff --git a/tests/resources/cherrypick/.gitted/objects/cf/c4f0999a8367568e049af4f72e452d40828a15 b/tests/resources/cherrypick/.gitted/objects/cf/c4f0999a8367568e049af4f72e452d40828a15
new file mode 100644
index 0000000..d7deb0b
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/cf/c4f0999a8367568e049af4f72e452d40828a15
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/d0/f21e17beb5b9d953b1d8349049818a4f2edd1e b/tests/resources/cherrypick/.gitted/objects/d0/f21e17beb5b9d953b1d8349049818a4f2edd1e
new file mode 100644
index 0000000..65c846f
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/d0/f21e17beb5b9d953b1d8349049818a4f2edd1e
@@ -0,0 +1 @@
+x¥ŽAjÃ0E»Ö)f_¶¤‘,¡]z‹‘<²]"«•ÇÐÞ¾‚!Ëÿø<^ª¥lÚá‹4fÀì=iM0HÉir: š<kiFGž§hI}Sã] õ™ã4FÇ qfÊšlÿÄà’Á¯è”µ6ø¤ôsòÜÊv¬çßׯ[Þi ]R-7è ÎÄ áuÐà:í©ÂOIÔû)µp[˜â!­´/=§î·;ë‹üŠú&éWY \ No newline at end of file
diff --git a/tests/resources/cherrypick/.gitted/objects/d3/d77487660ee3c0194ee01dc5eaf478782b1c7e b/tests/resources/cherrypick/.gitted/objects/d3/d77487660ee3c0194ee01dc5eaf478782b1c7e
new file mode 100644
index 0000000..b42df7e
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/d3/d77487660ee3c0194ee01dc5eaf478782b1c7e
@@ -0,0 +1 @@
+x¥ÎKJ1€a×9Eí…!ïI@df%x‹ª¤ú!“Ž¦«AooƒGpû-~þÒ[[l O2˜AW﯋ 6‘ŽÑÄRÙ‘Õ=yFW ¥ƒúÄÁ›@Õ“5l®Ä(×™šœÏÚçdúÉr­†²ôïX¾ÞáG[÷åøÙáåãÏæÛ.cEÁKéíŒËÑ»²…gmµV§ž«ÂÿŠ¨û!½ñ˜éÁPÜæs§o0­vùõ *Wd \ No newline at end of file
diff --git a/tests/resources/cherrypick/.gitted/objects/e2/33b9ed408a95e9d4b65fec7fc34943a556deb2 b/tests/resources/cherrypick/.gitted/objects/e2/33b9ed408a95e9d4b65fec7fc34943a556deb2
new file mode 100644
index 0000000..b344c9c
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/e2/33b9ed408a95e9d4b65fec7fc34943a556deb2
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/e5/183bfd18e3a0a691fadde2f0d5610b73282d31 b/tests/resources/cherrypick/.gitted/objects/e5/183bfd18e3a0a691fadde2f0d5610b73282d31
new file mode 100644
index 0000000..fdc0571
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/e5/183bfd18e3a0a691fadde2f0d5610b73282d31
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/e6/ae8889c40c77d7be02758235b5b3f7a4f2a129 b/tests/resources/cherrypick/.gitted/objects/e6/ae8889c40c77d7be02758235b5b3f7a4f2a129
new file mode 100644
index 0000000..3345907
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/e6/ae8889c40c77d7be02758235b5b3f7a4f2a129
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/e7/811a2bc55635f182750f0420da5ad232c1af91 b/tests/resources/cherrypick/.gitted/objects/e7/811a2bc55635f182750f0420da5ad232c1af91
new file mode 100644
index 0000000..2388730
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/e7/811a2bc55635f182750f0420da5ad232c1af91
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/e9/b63f3655b2ad80c0ff587389b5a9589a3a7110 b/tests/resources/cherrypick/.gitted/objects/e9/b63f3655b2ad80c0ff587389b5a9589a3a7110
new file mode 100644
index 0000000..ab0a27f
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/e9/b63f3655b2ad80c0ff587389b5a9589a3a7110
@@ -0,0 +1,2 @@
+x¥ÎMjÃ0@á®uŠÙŠ<Ë!E¡·É£ØÅ?‰$/rûz„n¿Åã¥}]çHÃG+ªzåè¼"*k
+¡×AºìÆHìc”èÓhÍ]Šn RN”-3Kp~è}PK,™ò€J=Ždéz#G›ö?’‡VøÖ²Îu:žN¿v»ÔVfiò•öõ cOÎ;‹ðiÑZóÖ÷jÓEÌu’í¦ Ëy^´š-˜P@ \ No newline at end of file
diff --git a/tests/resources/cherrypick/.gitted/objects/eb/da71fe44dcb60c53b8fbd53208a1204d32e959 b/tests/resources/cherrypick/.gitted/objects/eb/da71fe44dcb60c53b8fbd53208a1204d32e959
new file mode 100644
index 0000000..19d0c52
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/eb/da71fe44dcb60c53b8fbd53208a1204d32e959
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/f0/5ed049854c1596a7cc0e957fab34961077f3ae b/tests/resources/cherrypick/.gitted/objects/f0/5ed049854c1596a7cc0e957fab34961077f3ae
new file mode 100644
index 0000000..ab45439
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/f0/5ed049854c1596a7cc0e957fab34961077f3ae
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/f0/a4e1c66bb548cd2b22eebefda703872e969775 b/tests/resources/cherrypick/.gitted/objects/f0/a4e1c66bb548cd2b22eebefda703872e969775
new file mode 100644
index 0000000..558dd0a
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/f0/a4e1c66bb548cd2b22eebefda703872e969775
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/f2/ec8c8cf1a9fb7aa047a25a4308bfe860237ad4 b/tests/resources/cherrypick/.gitted/objects/f2/ec8c8cf1a9fb7aa047a25a4308bfe860237ad4
new file mode 100644
index 0000000..a011751
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/f2/ec8c8cf1a9fb7aa047a25a4308bfe860237ad4
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/f5/684c96bf40c709877b56404cd8a5dd2d2a7978 b/tests/resources/cherrypick/.gitted/objects/f5/684c96bf40c709877b56404cd8a5dd2d2a7978
new file mode 100644
index 0000000..83bb5a0
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/f5/684c96bf40c709877b56404cd8a5dd2d2a7978
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/objects/f9/0f9dcbdac2cce5cc166346160e19cb693ef4e8 b/tests/resources/cherrypick/.gitted/objects/f9/0f9dcbdac2cce5cc166346160e19cb693ef4e8
new file mode 100644
index 0000000..71be9f8
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/objects/f9/0f9dcbdac2cce5cc166346160e19cb693ef4e8
Binary files differ
diff --git a/tests/resources/cherrypick/.gitted/refs/heads/automerge-branch b/tests/resources/cherrypick/.gitted/refs/heads/automerge-branch
new file mode 100644
index 0000000..9330ef3
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/refs/heads/automerge-branch
@@ -0,0 +1 @@
+d3d77487660ee3c0194ee01dc5eaf478782b1c7e
diff --git a/tests/resources/cherrypick/.gitted/refs/heads/master b/tests/resources/cherrypick/.gitted/refs/heads/master
new file mode 100644
index 0000000..aa8913b
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/refs/heads/master
@@ -0,0 +1 @@
+2a26c7e88b285613b302ba76712bc998863f3cbc
diff --git a/tests/resources/cherrypick/.gitted/refs/heads/merge-branch b/tests/resources/cherrypick/.gitted/refs/heads/merge-branch
new file mode 100644
index 0000000..ea5b277
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/refs/heads/merge-branch
@@ -0,0 +1 @@
+abe4603bc7cd5b8167a267e0e2418fd2348f8cff
diff --git a/tests/resources/cherrypick/.gitted/refs/heads/merge-conflicts b/tests/resources/cherrypick/.gitted/refs/heads/merge-conflicts
new file mode 100644
index 0000000..f63f17e
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/refs/heads/merge-conflicts
@@ -0,0 +1 @@
+bafbf6912c09505ac60575cd43d3f2aba3bd84d8
diff --git a/tests/resources/cherrypick/.gitted/refs/heads/merge-mainline b/tests/resources/cherrypick/.gitted/refs/heads/merge-mainline
new file mode 100644
index 0000000..0ec5e45
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/refs/heads/merge-mainline
@@ -0,0 +1 @@
+cfc4f0999a8367568e049af4f72e452d40828a15
diff --git a/tests/resources/cherrypick/.gitted/refs/heads/orphan b/tests/resources/cherrypick/.gitted/refs/heads/orphan
new file mode 100644
index 0000000..f4d6a74
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/refs/heads/orphan
@@ -0,0 +1 @@
+74f06b5bfec6d33d7264f73606b57a7c0b963819
diff --git a/tests/resources/cherrypick/.gitted/refs/heads/renames b/tests/resources/cherrypick/.gitted/refs/heads/renames
new file mode 100644
index 0000000..df55871
--- /dev/null
+++ b/tests/resources/cherrypick/.gitted/refs/heads/renames
@@ -0,0 +1 @@
+44cd2ed2052c9c68f9a439d208e9614dc2a55c70
diff --git a/tests/resources/cherrypick/file1.txt b/tests/resources/cherrypick/file1.txt
new file mode 100644
index 0000000..38c05a8
--- /dev/null
+++ b/tests/resources/cherrypick/file1.txt
@@ -0,0 +1,15 @@
+!File 1
+File 1
+File 1
+File 1
+File 1
+File 1
+File 1
+File 1
+File 1
+File 1
+File 1
+File 1
+File 1
+File 1
+File 1
diff --git a/tests/resources/cherrypick/file2.txt b/tests/resources/cherrypick/file2.txt
new file mode 100644
index 0000000..a661b5d
--- /dev/null
+++ b/tests/resources/cherrypick/file2.txt
@@ -0,0 +1,15 @@
+!File 2
+File 2
+File 2
+File 2
+File 2
+File 2
+File 2
+File 2
+File 2
+File 2
+File 2
+File 2
+File 2
+File 2
+File 2
diff --git a/tests/resources/cherrypick/file3.txt b/tests/resources/cherrypick/file3.txt
new file mode 100644
index 0000000..85a4a1d
--- /dev/null
+++ b/tests/resources/cherrypick/file3.txt
@@ -0,0 +1,15 @@
+!File 3
+File 3
+File 3
+File 3
+File 3
+File 3
+File 3
+File 3
+File 3
+File 3
+File 3
+File 3
+File 3
+File 3
+File 3
diff --git a/tests/resources/config/.gitconfig b/tests/resources/config/.gitconfig
new file mode 100644
index 0000000..fa72bdd
--- /dev/null
+++ b/tests/resources/config/.gitconfig
@@ -0,0 +1,3 @@
+[core]
+ repositoryformatversion = 5
+ something = 2 \ No newline at end of file
diff --git a/tests/resources/config/config-include b/tests/resources/config/config-include
new file mode 100644
index 0000000..6b5e79d
--- /dev/null
+++ b/tests/resources/config/config-include
@@ -0,0 +1,2 @@
+[include]
+ path = config-included
diff --git a/tests/resources/config/config-included b/tests/resources/config/config-included
new file mode 100644
index 0000000..089ca08
--- /dev/null
+++ b/tests/resources/config/config-included
@@ -0,0 +1,2 @@
+[foo "bar"]
+ baz = huzzah
diff --git a/tests/resources/config/config-nosection b/tests/resources/config/config-nosection
new file mode 100644
index 0000000..dd2ee08
--- /dev/null
+++ b/tests/resources/config/config-nosection
@@ -0,0 +1 @@
+key = value
diff --git a/tests/resources/config/config-oom b/tests/resources/config/config-oom
new file mode 100644
index 0000000..41ed170
--- /dev/null
+++ b/tests/resources/config/config-oom
@@ -0,0 +1,40000 @@
+[aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa]
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
+a=b
diff --git a/tests/resources/config/config0 b/tests/resources/config/config0
new file mode 100644
index 0000000..85235c5
--- /dev/null
+++ b/tests/resources/config/config0
@@ -0,0 +1,7 @@
+# This is a test
+; of different comments
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true \ No newline at end of file
diff --git a/tests/resources/config/config1 b/tests/resources/config/config1
new file mode 100644
index 0000000..211dc9e
--- /dev/null
+++ b/tests/resources/config/config1
@@ -0,0 +1,5 @@
+# This one checks for case sensitivity
+[this "that"]
+ other = true
+[this "That"]
+ other = yes
diff --git a/tests/resources/config/config10 b/tests/resources/config/config10
new file mode 100644
index 0000000..dde1791
--- /dev/null
+++ b/tests/resources/config/config10
@@ -0,0 +1 @@
+[empty]
diff --git a/tests/resources/config/config11 b/tests/resources/config/config11
new file mode 100644
index 0000000..1d8a744
--- /dev/null
+++ b/tests/resources/config/config11
@@ -0,0 +1,5 @@
+[remote "ab"]
+ url = git://github.com/libgit2/libgit2
+ url = git://git.example.com/libgit2
+
+
diff --git a/tests/resources/config/config12 b/tests/resources/config/config12
new file mode 100644
index 0000000..2e92762
--- /dev/null
+++ b/tests/resources/config/config12
@@ -0,0 +1,23 @@
+[some "section"]
+ test = hi ; comment
+ test2 = hello ; comment
+ test3 = welcome #comment
+ other = "hello! \" ; ; ; " ; more test
+ other2 = "cool! \" # # # " # more test
+ multi = "hi, this is a ; \
+multiline comment # with ;\n special chars \
+and other stuff !@#"
+ multi2 = "good, this is a ; \
+multiline comment # with ;\n special chars \
+and other stuff !@#" #^^^
+ back = "this is \ba phrase"
+ dollar = some $sign
+ multiquotes = !ls "x" \
+ ls "# comment2" \
+ $HOME
+ multiquotes2 = !ls "x" \
+ ls "\"# comment2" \
+ $HOME\"
+ multiquotes3 = hi "# ho" there "are #" more \
+quotes
+ quotecomment = hi "# ho" there "are #" more # and a real comment
diff --git a/tests/resources/config/config13 b/tests/resources/config/config13
new file mode 100644
index 0000000..c1e0c56
--- /dev/null
+++ b/tests/resources/config/config13
@@ -0,0 +1,2 @@
+[core]
+ editor = \"C:/Program Files/Nonsense/bah.exe\" \"--some option\"
diff --git a/tests/resources/config/config14 b/tests/resources/config/config14
new file mode 100644
index 0000000..ef2198c
--- /dev/null
+++ b/tests/resources/config/config14
@@ -0,0 +1,4 @@
+[a]
+ b=c
+[d]
+ e = f
diff --git a/tests/resources/config/config15 b/tests/resources/config/config15
new file mode 100644
index 0000000..6d34f81
--- /dev/null
+++ b/tests/resources/config/config15
@@ -0,0 +1,3 @@
+[core]
+ dummy2 = 7
+ global = 17
diff --git a/tests/resources/config/config16 b/tests/resources/config/config16
new file mode 100644
index 0000000..f25cdb7
--- /dev/null
+++ b/tests/resources/config/config16
@@ -0,0 +1,3 @@
+[core]
+ dummy2 = 28
+ system = 11
diff --git a/tests/resources/config/config17 b/tests/resources/config/config17
new file mode 100644
index 0000000..ca25a86
--- /dev/null
+++ b/tests/resources/config/config17
@@ -0,0 +1,3 @@
+[core]
+ dummy2 = 7
+ global = 17 \ No newline at end of file
diff --git a/tests/resources/config/config18 b/tests/resources/config/config18
new file mode 100644
index 0000000..cb6fd5e
--- /dev/null
+++ b/tests/resources/config/config18
@@ -0,0 +1,5 @@
+[core]
+ int32global = 28
+ int64global = 9223372036854775803
+ boolglobal = true
+ stringglobal = I'm a global config value! \ No newline at end of file
diff --git a/tests/resources/config/config19 b/tests/resources/config/config19
new file mode 100644
index 0000000..f3ae5a6
--- /dev/null
+++ b/tests/resources/config/config19
@@ -0,0 +1,5 @@
+[core]
+ int32global = -1
+ int64global = -2
+ boolglobal = false
+ stringglobal = don't find me! \ No newline at end of file
diff --git a/tests/resources/config/config2 b/tests/resources/config/config2
new file mode 100644
index 0000000..60a3898
--- /dev/null
+++ b/tests/resources/config/config2
@@ -0,0 +1,5 @@
+; This one tests for multiline values
+[this "That"]
+ and = one one one \
+two two \
+three three \ No newline at end of file
diff --git a/tests/resources/config/config20 b/tests/resources/config/config20
new file mode 100644
index 0000000..8f0f12c
--- /dev/null
+++ b/tests/resources/config/config20
@@ -0,0 +1,11 @@
+[valid "[subsection]"]
+ something = a
+; we don't allow anything after closing "
+[sec "[subsec]/child"]
+ parent = grand
+[sec2 "[subsec2]/child2"]
+ type = dvcs
+[sec3 "escape\"quote"]
+ vcs = git
+[sec4 "escaping\\slash"]
+ lib = git2
diff --git a/tests/resources/config/config21 b/tests/resources/config/config21
new file mode 100644
index 0000000..aa5eb41
--- /dev/null
+++ b/tests/resources/config/config21
@@ -0,0 +1,5 @@
+[alias]
+ m = '\
+ ";" \
+ ";" \
+ '
diff --git a/tests/resources/config/config22 b/tests/resources/config/config22
new file mode 100644
index 0000000..2a8e528
--- /dev/null
+++ b/tests/resources/config/config22
@@ -0,0 +1,10 @@
+[alias]
+ m = cmd \
+";;" \
+";;" \
+bar
+ m2 = '\
+";" \
+";" \
+something \
+'
diff --git a/tests/resources/config/config3 b/tests/resources/config/config3
new file mode 100644
index 0000000..44a5e50
--- /dev/null
+++ b/tests/resources/config/config3
@@ -0,0 +1,3 @@
+# A [section.subsection] header is case-insensitive
+[section.SuBsection]
+ var = hello
diff --git a/tests/resources/config/config4 b/tests/resources/config/config4
new file mode 100644
index 0000000..9dd4041
--- /dev/null
+++ b/tests/resources/config/config4
@@ -0,0 +1,5 @@
+# A variable name on its own is valid
+[some.section]
+ variable
+# A variable and '=' is accepted, but it's not considered true
+ variableeq =
diff --git a/tests/resources/config/config5 b/tests/resources/config/config5
new file mode 100644
index 0000000..8ab60cc
--- /dev/null
+++ b/tests/resources/config/config5
@@ -0,0 +1,9 @@
+# Test for number suffixes
+[number]
+ simple = 1
+ k = 1k
+ kk = 1K
+ m = 1m
+ mm = 1M
+ g = 1g
+ gg = 1G
diff --git a/tests/resources/config/config6 b/tests/resources/config/config6
new file mode 100644
index 0000000..0f8f90a
--- /dev/null
+++ b/tests/resources/config/config6
@@ -0,0 +1,5 @@
+[valid "subsection"]
+ something = true
+
+[something "else"]
+ something = false
diff --git a/tests/resources/config/config7 b/tests/resources/config/config7
new file mode 100644
index 0000000..6af6fcf
--- /dev/null
+++ b/tests/resources/config/config7
@@ -0,0 +1,5 @@
+[valid "subsection"]
+ something = a
+; we don't allow anything after closing "
+[sec "subsec"x]
+ bleh = blah
diff --git a/tests/resources/config/config8 b/tests/resources/config/config8
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/config/config8
diff --git a/tests/resources/config/config9 b/tests/resources/config/config9
new file mode 100644
index 0000000..fcaac42
--- /dev/null
+++ b/tests/resources/config/config9
@@ -0,0 +1,9 @@
+[core]
+ dummy2 = 42
+ verylong = 1
+ dummy = 1
+
+[remote "ab"]
+ url = http://example.com/git/ab
+[remote "abba"]
+ url = http://example.com/git/abba
diff --git a/tests/resources/crlf.git/HEAD b/tests/resources/crlf.git/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/crlf.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/crlf.git/config b/tests/resources/crlf.git/config
new file mode 100644
index 0000000..a8e94d7
--- /dev/null
+++ b/tests/resources/crlf.git/config
@@ -0,0 +1,13 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
+ precomposeunicode = true
+[remote "origin"]
+ url = /Users/ethomson/libgit2/libgit2-3/tests/resources/crlf.git
+ fetch = +refs/heads/*:refs/remotes/origin/*
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/crlf.git/logs/HEAD b/tests/resources/crlf.git/logs/HEAD
new file mode 100644
index 0000000..44f6651
--- /dev/null
+++ b/tests/resources/crlf.git/logs/HEAD
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 6b9d5748663795f573ea857276eb2a5f8330efa0 Edward Thomson <ethomson@edwardthomson.com> 1563721143 +0100 clone: from /Users/ethomson/libgit2/libgit2-3/tests/resources/crlf.git
+6b9d5748663795f573ea857276eb2a5f8330efa0 124f4293444614aa8da53be149792c2e43e9bfd9 Edward Thomson <ethomson@edwardthomson.com> 1563721187 +0100 commit: subdir with no translation
diff --git a/tests/resources/crlf.git/logs/refs/heads/master b/tests/resources/crlf.git/logs/refs/heads/master
new file mode 100644
index 0000000..44f6651
--- /dev/null
+++ b/tests/resources/crlf.git/logs/refs/heads/master
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 6b9d5748663795f573ea857276eb2a5f8330efa0 Edward Thomson <ethomson@edwardthomson.com> 1563721143 +0100 clone: from /Users/ethomson/libgit2/libgit2-3/tests/resources/crlf.git
+6b9d5748663795f573ea857276eb2a5f8330efa0 124f4293444614aa8da53be149792c2e43e9bfd9 Edward Thomson <ethomson@edwardthomson.com> 1563721187 +0100 commit: subdir with no translation
diff --git a/tests/resources/crlf.git/logs/refs/remotes/origin/HEAD b/tests/resources/crlf.git/logs/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..6cca825
--- /dev/null
+++ b/tests/resources/crlf.git/logs/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 6b9d5748663795f573ea857276eb2a5f8330efa0 Edward Thomson <ethomson@edwardthomson.com> 1563721143 +0100 clone: from /Users/ethomson/libgit2/libgit2-3/tests/resources/crlf.git
diff --git a/tests/resources/crlf.git/objects/04/4bcd5c9bf5ebdd51e514a9a36457018f06f6e1 b/tests/resources/crlf.git/objects/04/4bcd5c9bf5ebdd51e514a9a36457018f06f6e1
new file mode 100644
index 0000000..a32a9b2
--- /dev/null
+++ b/tests/resources/crlf.git/objects/04/4bcd5c9bf5ebdd51e514a9a36457018f06f6e1
@@ -0,0 +1 @@
+x-ŽÁjÃ0D{ÖWì½4H+Ù+C(É¡·þ„¤]aÓØJU…|}•˜Ã̃“ʺ. к·VE@²æÄv™ÑÈ”²ËvB½x=%‰êªl ÈsDÒÉx÷H¦!x3E9ÅA‡hPÆdU¸´¹Tøâk¨ {éký+ÛAžàµv©¬Ÿ`¬ŸŒC2|h¯µê´ŸlRá{Ù~/Ë]`zµÃ-Ì¥<ÄÝüÓ]×MÄ5¼?]ud†»ÔñÖòrõÄ&K! \ No newline at end of file
diff --git a/tests/resources/crlf.git/objects/04/de00b358f13389948756732158eaaaefa1448c b/tests/resources/crlf.git/objects/04/de00b358f13389948756732158eaaaefa1448c
new file mode 100644
index 0000000..c3b7598
--- /dev/null
+++ b/tests/resources/crlf.git/objects/04/de00b358f13389948756732158eaaaefa1448c
Binary files differ
diff --git a/tests/resources/crlf.git/objects/05/5c8729cdcc372500a08db659c045e16c4409fb b/tests/resources/crlf.git/objects/05/5c8729cdcc372500a08db659c045e16c4409fb
new file mode 100644
index 0000000..44076ca
--- /dev/null
+++ b/tests/resources/crlf.git/objects/05/5c8729cdcc372500a08db659c045e16c4409fb
Binary files differ
diff --git a/tests/resources/crlf.git/objects/09/7722be9b67b48dfe3b19396d02fd535300ee46 b/tests/resources/crlf.git/objects/09/7722be9b67b48dfe3b19396d02fd535300ee46
new file mode 100644
index 0000000..5c5c24c
--- /dev/null
+++ b/tests/resources/crlf.git/objects/09/7722be9b67b48dfe3b19396d02fd535300ee46
Binary files differ
diff --git a/tests/resources/crlf.git/objects/0a/a76e474d259bd7c13eb726a1396c381db55c88 b/tests/resources/crlf.git/objects/0a/a76e474d259bd7c13eb726a1396c381db55c88
new file mode 100644
index 0000000..e118d66
--- /dev/null
+++ b/tests/resources/crlf.git/objects/0a/a76e474d259bd7c13eb726a1396c381db55c88
Binary files differ
diff --git a/tests/resources/crlf.git/objects/0d/06894e14df22e066763ae906e0ed3eb79c205f b/tests/resources/crlf.git/objects/0d/06894e14df22e066763ae906e0ed3eb79c205f
new file mode 100644
index 0000000..b7a1f32
--- /dev/null
+++ b/tests/resources/crlf.git/objects/0d/06894e14df22e066763ae906e0ed3eb79c205f
Binary files differ
diff --git a/tests/resources/crlf.git/objects/0e/052888828a954ca17e5882638e3c6a083e75c0 b/tests/resources/crlf.git/objects/0e/052888828a954ca17e5882638e3c6a083e75c0
new file mode 100644
index 0000000..746143f
--- /dev/null
+++ b/tests/resources/crlf.git/objects/0e/052888828a954ca17e5882638e3c6a083e75c0
Binary files differ
diff --git a/tests/resources/crlf.git/objects/0f/f5a53f19bfd2b5eea1ba550295c47515678987 b/tests/resources/crlf.git/objects/0f/f5a53f19bfd2b5eea1ba550295c47515678987
new file mode 100644
index 0000000..5366acd
--- /dev/null
+++ b/tests/resources/crlf.git/objects/0f/f5a53f19bfd2b5eea1ba550295c47515678987
Binary files differ
diff --git a/tests/resources/crlf.git/objects/12/4f4293444614aa8da53be149792c2e43e9bfd9 b/tests/resources/crlf.git/objects/12/4f4293444614aa8da53be149792c2e43e9bfd9
new file mode 100644
index 0000000..29c6740
--- /dev/null
+++ b/tests/resources/crlf.git/objects/12/4f4293444614aa8da53be149792c2e43e9bfd9
@@ -0,0 +1,4 @@
+x¥Ž]
+Â0„}Î)ö]üm’‚ˆ/ÞÀ l“--ØDÒ¯ooàÓÌ|ÃIJ®KíÔ¡Uf`"Ǩ=Ùhº´ì­±Q*¶.80yñ¤Ê¹‡„ÞçŒpBo˜zíšp
+ÆHžH
+ÚÛ\*ÜÒ‹j‚û\Ö­d8s§wåoñK§XÖ (ì£Z©àá(•”¢Ó~¶ñŸ3bÛÇ´Tx-m†\ UÊÛƒÚR²xjöU× \ No newline at end of file
diff --git a/tests/resources/crlf.git/objects/16/78031ee023a23bd3515e4e1693b661a69f0a73 b/tests/resources/crlf.git/objects/16/78031ee023a23bd3515e4e1693b661a69f0a73
new file mode 100644
index 0000000..4aa4ffb
--- /dev/null
+++ b/tests/resources/crlf.git/objects/16/78031ee023a23bd3515e4e1693b661a69f0a73
Binary files differ
diff --git a/tests/resources/crlf.git/objects/16/c72b67861f8524a5bebc05cd20472d3fca00da b/tests/resources/crlf.git/objects/16/c72b67861f8524a5bebc05cd20472d3fca00da
new file mode 100644
index 0000000..e2b1994
--- /dev/null
+++ b/tests/resources/crlf.git/objects/16/c72b67861f8524a5bebc05cd20472d3fca00da
Binary files differ
diff --git a/tests/resources/crlf.git/objects/18/c637c5d9aba6eed226ee1840cd1ca2e6c4e4c5 b/tests/resources/crlf.git/objects/18/c637c5d9aba6eed226ee1840cd1ca2e6c4e4c5
new file mode 100644
index 0000000..790eb13
--- /dev/null
+++ b/tests/resources/crlf.git/objects/18/c637c5d9aba6eed226ee1840cd1ca2e6c4e4c5
Binary files differ
diff --git a/tests/resources/crlf.git/objects/1e/c507638b806aba45d6142082885f2a9e88322d b/tests/resources/crlf.git/objects/1e/c507638b806aba45d6142082885f2a9e88322d
new file mode 100644
index 0000000..ca97967
--- /dev/null
+++ b/tests/resources/crlf.git/objects/1e/c507638b806aba45d6142082885f2a9e88322d
Binary files differ
diff --git a/tests/resources/crlf.git/objects/20/3555c5676d75cd80d69b50beb1f4b588c59ceb b/tests/resources/crlf.git/objects/20/3555c5676d75cd80d69b50beb1f4b588c59ceb
new file mode 100644
index 0000000..8038a9b
--- /dev/null
+++ b/tests/resources/crlf.git/objects/20/3555c5676d75cd80d69b50beb1f4b588c59ceb
Binary files differ
diff --git a/tests/resources/crlf.git/objects/23/f4582779e60bfa7f14750ad507399a58876611 b/tests/resources/crlf.git/objects/23/f4582779e60bfa7f14750ad507399a58876611
new file mode 100644
index 0000000..4a4e4dc
--- /dev/null
+++ b/tests/resources/crlf.git/objects/23/f4582779e60bfa7f14750ad507399a58876611
Binary files differ
diff --git a/tests/resources/crlf.git/objects/2a/d3df895f68f4dda6a0a815c620b909bdd27c05 b/tests/resources/crlf.git/objects/2a/d3df895f68f4dda6a0a815c620b909bdd27c05
new file mode 100644
index 0000000..f5421cf
--- /dev/null
+++ b/tests/resources/crlf.git/objects/2a/d3df895f68f4dda6a0a815c620b909bdd27c05
Binary files differ
diff --git a/tests/resources/crlf.git/objects/2b/55b4b94f655c857635b6a9005c056aa7de3532 b/tests/resources/crlf.git/objects/2b/55b4b94f655c857635b6a9005c056aa7de3532
new file mode 100644
index 0000000..031fd66
--- /dev/null
+++ b/tests/resources/crlf.git/objects/2b/55b4b94f655c857635b6a9005c056aa7de3532
@@ -0,0 +1,2 @@
+x-ŽKjÄ0D³Ö)z2tëcµa³™].¡O ›ÄV¢È„Éé£ µ¨zð RݶµƒÖøÔ›AvyIfLiò.²Ï…œPɘL0dÑõšì<ç¨=&b{Oñ.09o4Åœ¢ÃIË”Œ
+G_jƒkþ -ÃYÆÚ¾ë~‘ðX§T·W ãØ°çÙ NˆjÐq²Kƒ·uÿ:Ö_óÇ£]na©õ.ž–÷áZ²Zk7!<#W½°ú²F \ No newline at end of file
diff --git a/tests/resources/crlf.git/objects/2b/d9d81b51a867352bab307b89cbb5b4a69adfe1 b/tests/resources/crlf.git/objects/2b/d9d81b51a867352bab307b89cbb5b4a69adfe1
new file mode 100644
index 0000000..96d952e
--- /dev/null
+++ b/tests/resources/crlf.git/objects/2b/d9d81b51a867352bab307b89cbb5b4a69adfe1
Binary files differ
diff --git a/tests/resources/crlf.git/objects/2c/03f9f407b576eae80327864bab572e282a33ea b/tests/resources/crlf.git/objects/2c/03f9f407b576eae80327864bab572e282a33ea
new file mode 100644
index 0000000..0e4afbb
--- /dev/null
+++ b/tests/resources/crlf.git/objects/2c/03f9f407b576eae80327864bab572e282a33ea
Binary files differ
diff --git a/tests/resources/crlf.git/objects/33/cdead44e1c3ec178e39a4a69085280dbacf01b b/tests/resources/crlf.git/objects/33/cdead44e1c3ec178e39a4a69085280dbacf01b
new file mode 100644
index 0000000..72dc780
--- /dev/null
+++ b/tests/resources/crlf.git/objects/33/cdead44e1c3ec178e39a4a69085280dbacf01b
Binary files differ
diff --git a/tests/resources/crlf.git/objects/38/1cfe630df902bc29271a202d3277981180e4a6 b/tests/resources/crlf.git/objects/38/1cfe630df902bc29271a202d3277981180e4a6
new file mode 100644
index 0000000..0cf7072
--- /dev/null
+++ b/tests/resources/crlf.git/objects/38/1cfe630df902bc29271a202d3277981180e4a6
Binary files differ
diff --git a/tests/resources/crlf.git/objects/3f/96bdca0e37616026afaa325c148cec4aa62d04 b/tests/resources/crlf.git/objects/3f/96bdca0e37616026afaa325c148cec4aa62d04
new file mode 100644
index 0000000..a204fc9
--- /dev/null
+++ b/tests/resources/crlf.git/objects/3f/96bdca0e37616026afaa325c148cec4aa62d04
Binary files differ
diff --git a/tests/resources/crlf.git/objects/41/7786fc35b3c71aa546e3f95eb5da3c8dad8c41 b/tests/resources/crlf.git/objects/41/7786fc35b3c71aa546e3f95eb5da3c8dad8c41
new file mode 100644
index 0000000..ec57bde
--- /dev/null
+++ b/tests/resources/crlf.git/objects/41/7786fc35b3c71aa546e3f95eb5da3c8dad8c41
Binary files differ
diff --git a/tests/resources/crlf.git/objects/44/b0be18671a284f1156117b6338edac2663341c b/tests/resources/crlf.git/objects/44/b0be18671a284f1156117b6338edac2663341c
new file mode 100644
index 0000000..0576e62
--- /dev/null
+++ b/tests/resources/crlf.git/objects/44/b0be18671a284f1156117b6338edac2663341c
Binary files differ
diff --git a/tests/resources/crlf.git/objects/47/fbc2c28a18df0dc773276a253eb85c7516ca50 b/tests/resources/crlf.git/objects/47/fbc2c28a18df0dc773276a253eb85c7516ca50
new file mode 100644
index 0000000..d16db96
--- /dev/null
+++ b/tests/resources/crlf.git/objects/47/fbc2c28a18df0dc773276a253eb85c7516ca50
Binary files differ
diff --git a/tests/resources/crlf.git/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 b/tests/resources/crlf.git/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904
new file mode 100644
index 0000000..adf6411
--- /dev/null
+++ b/tests/resources/crlf.git/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904
Binary files differ
diff --git a/tests/resources/crlf.git/objects/55/1b8fce462bba005ab6d34a2244d8a3f6b03dd0 b/tests/resources/crlf.git/objects/55/1b8fce462bba005ab6d34a2244d8a3f6b03dd0
new file mode 100644
index 0000000..7501c88
--- /dev/null
+++ b/tests/resources/crlf.git/objects/55/1b8fce462bba005ab6d34a2244d8a3f6b03dd0
Binary files differ
diff --git a/tests/resources/crlf.git/objects/5a/fb6a14a864e30787857dd92af837e8cdd2cb1b b/tests/resources/crlf.git/objects/5a/fb6a14a864e30787857dd92af837e8cdd2cb1b
new file mode 100644
index 0000000..11a25c5
--- /dev/null
+++ b/tests/resources/crlf.git/objects/5a/fb6a14a864e30787857dd92af837e8cdd2cb1b
Binary files differ
diff --git a/tests/resources/crlf.git/objects/68/03c385642cebc8103fddd526ef395d75678a7e b/tests/resources/crlf.git/objects/68/03c385642cebc8103fddd526ef395d75678a7e
new file mode 100644
index 0000000..f8d489f
--- /dev/null
+++ b/tests/resources/crlf.git/objects/68/03c385642cebc8103fddd526ef395d75678a7e
@@ -0,0 +1,2 @@
+x¥ÎKjÄ0ЬuŠ¾À},µ !dÉ &h·ZØ0²‚¬ÁäöQvÙgW¼‚¢¸–²u°ŸzËÚå9OAH¢v£ ÓB¬ØhÉ9!õEMö}œÑcžfÔ˜52vÙ%ëmä` /¬èÑ×Úà=ÔÜÖZŽºÃ‹ ýMoeãVšû3×ò
+fr1ƒpÑ“Öjè8Úåêšäí.œ[_¡Ðþ Ÿ·K^©@ûèåükêÑ¡Yj \ No newline at end of file
diff --git a/tests/resources/crlf.git/objects/69/597764abeaa1a403ebf589d2ea579c6a8f877e b/tests/resources/crlf.git/objects/69/597764abeaa1a403ebf589d2ea579c6a8f877e
new file mode 100644
index 0000000..ee4f427
--- /dev/null
+++ b/tests/resources/crlf.git/objects/69/597764abeaa1a403ebf589d2ea579c6a8f877e
@@ -0,0 +1 @@
+xÎÑÂ0 €až3…ßOwr“&­%„`†NàÄŽˆ -JÃ!¶1|¿þ´Îsi`ݸkUÈ¢r.*{z¤‘Ägr>ɱcòbn\ui êcæ.x"’‚DÎ,–ÞN“Ã,†ïí¼V˜þu©Õ’.umgØëÌåzLÛïãü=@×[kðƒ¢IŸÇ¦ßismbY¸>!—«næJ“LÅ \ No newline at end of file
diff --git a/tests/resources/crlf.git/objects/6a/e3e9c11a51f0aabebcffcbd5c00f4beed143c9 b/tests/resources/crlf.git/objects/6a/e3e9c11a51f0aabebcffcbd5c00f4beed143c9
new file mode 100644
index 0000000..6c18a3a
--- /dev/null
+++ b/tests/resources/crlf.git/objects/6a/e3e9c11a51f0aabebcffcbd5c00f4beed143c9
Binary files differ
diff --git a/tests/resources/crlf.git/objects/6b/9d5748663795f573ea857276eb2a5f8330efa0 b/tests/resources/crlf.git/objects/6b/9d5748663795f573ea857276eb2a5f8330efa0
new file mode 100644
index 0000000..680c7cd
--- /dev/null
+++ b/tests/resources/crlf.git/objects/6b/9d5748663795f573ea857276eb2a5f8330efa0
@@ -0,0 +1 @@
+x¥ŽKÂ0CYç³Gªòk>B°à\`Ò™Ò.Ú 0ˆëS7`g?Ë–‡º,³€íÝN3°q1”³·¤=GN:e[Räb¡ 3{6êŽWÇÐxLÁ³Ó1ÅÔG¢lqLnkDv(¦(|ÊT\è…à:ÕåQW8ðF?êÄßà纡.G0}0ÎzìµÑZmt;+üçŒ:Aw›EÚ\žÂõY¦Sm \ No newline at end of file
diff --git a/tests/resources/crlf.git/objects/6c/589757f65a970a6cc07c71c3f3d2528c611cbc b/tests/resources/crlf.git/objects/6c/589757f65a970a6cc07c71c3f3d2528c611cbc
new file mode 100644
index 0000000..fe4da8c
--- /dev/null
+++ b/tests/resources/crlf.git/objects/6c/589757f65a970a6cc07c71c3f3d2528c611cbc
@@ -0,0 +1,2 @@
+x-ŽKNÃ0„Yçÿµò;Ž„PYp‚rÿ5j#Ç€¸=nÅnF3ói¸¬ëÒÀ…ñ©UUp$“dKÑbN£Ž¼)OL)`šPfµÃ'VÝpCV‰‘Y$ÏSÊÕ³³cÔqVIÑÎ:àW»”
+çoÝàÜêÂ×ZÚ^tÅåvâýðÓGÑW°Á9gLš2<kÌÀM+¼Ko |\ʺ—­;ó®Núþݱîï£õ>$8˜Ð1ÛìeU eÃú órÓ}ø.Q¯ \ No newline at end of file
diff --git a/tests/resources/crlf.git/objects/72/10e91413baa3d9b90215e970ae53397ecc526e b/tests/resources/crlf.git/objects/72/10e91413baa3d9b90215e970ae53397ecc526e
new file mode 100644
index 0000000..38c000d
--- /dev/null
+++ b/tests/resources/crlf.git/objects/72/10e91413baa3d9b90215e970ae53397ecc526e
Binary files differ
diff --git a/tests/resources/crlf.git/objects/77/afe26d93c49279ca90604c125496920753fede b/tests/resources/crlf.git/objects/77/afe26d93c49279ca90604c125496920753fede
new file mode 100644
index 0000000..a377cb0
--- /dev/null
+++ b/tests/resources/crlf.git/objects/77/afe26d93c49279ca90604c125496920753fede
Binary files differ
diff --git a/tests/resources/crlf.git/objects/78/db270c1841841f75a8157321bdcb50ab12e6c3 b/tests/resources/crlf.git/objects/78/db270c1841841f75a8157321bdcb50ab12e6c3
new file mode 100644
index 0000000..8a55bb0
--- /dev/null
+++ b/tests/resources/crlf.git/objects/78/db270c1841841f75a8157321bdcb50ab12e6c3
Binary files differ
diff --git a/tests/resources/crlf.git/objects/79/9770d1cff46753a57db7a066159b5610da6e3a b/tests/resources/crlf.git/objects/79/9770d1cff46753a57db7a066159b5610da6e3a
new file mode 100644
index 0000000..5c701b8
--- /dev/null
+++ b/tests/resources/crlf.git/objects/79/9770d1cff46753a57db7a066159b5610da6e3a
Binary files differ
diff --git a/tests/resources/crlf.git/objects/7c/ce67e58173e2b01f7db124ceaabe3183d19c49 b/tests/resources/crlf.git/objects/7c/ce67e58173e2b01f7db124ceaabe3183d19c49
new file mode 100644
index 0000000..8e836ab
--- /dev/null
+++ b/tests/resources/crlf.git/objects/7c/ce67e58173e2b01f7db124ceaabe3183d19c49
Binary files differ
diff --git a/tests/resources/crlf.git/objects/85/340755cfe5e28c2835781978bb1cece91b3d0f b/tests/resources/crlf.git/objects/85/340755cfe5e28c2835781978bb1cece91b3d0f
new file mode 100644
index 0000000..e83fbc2
--- /dev/null
+++ b/tests/resources/crlf.git/objects/85/340755cfe5e28c2835781978bb1cece91b3d0f
Binary files differ
diff --git a/tests/resources/crlf.git/objects/92/0e90a663bea5d740989d5f935f6dfb473a0c5d b/tests/resources/crlf.git/objects/92/0e90a663bea5d740989d5f935f6dfb473a0c5d
new file mode 100644
index 0000000..f872be6
--- /dev/null
+++ b/tests/resources/crlf.git/objects/92/0e90a663bea5d740989d5f935f6dfb473a0c5d
Binary files differ
diff --git a/tests/resources/crlf.git/objects/96/87e444bcbb85645cb496080434c292f1b57182 b/tests/resources/crlf.git/objects/96/87e444bcbb85645cb496080434c292f1b57182
new file mode 100644
index 0000000..5df64d8
--- /dev/null
+++ b/tests/resources/crlf.git/objects/96/87e444bcbb85645cb496080434c292f1b57182
@@ -0,0 +1 @@
+x¥ÎAJ1@Q×9Eí…!©T:™Y Þ¢ºRí´ÐMªAoïˆGpûŸ/mß7LùÁº*L¬Q‹„À)¬žyÑEÖU–šÄû•Õ(JqÜõfP2Q©Œ1¥”%¥™‰âL•æ¬Ó$QS Ž»¶¯,Ÿ‡xѾoãz|xzÿ³·ó°¾±ñIÚþ 0{ŠS@xôè½»ë}Õô_w©U+H»Ùï¾50†'û2÷ÿ=T‰ \ No newline at end of file
diff --git a/tests/resources/crlf.git/objects/97/449da2d225557c558ac244384d487e66c3e591 b/tests/resources/crlf.git/objects/97/449da2d225557c558ac244384d487e66c3e591
new file mode 100644
index 0000000..d3917a4
--- /dev/null
+++ b/tests/resources/crlf.git/objects/97/449da2d225557c558ac244384d487e66c3e591
Binary files differ
diff --git a/tests/resources/crlf.git/objects/9a/6c3533fef19abd6eec8e61206b5c51982b80d9 b/tests/resources/crlf.git/objects/9a/6c3533fef19abd6eec8e61206b5c51982b80d9
new file mode 100644
index 0000000..78fc8ae
--- /dev/null
+++ b/tests/resources/crlf.git/objects/9a/6c3533fef19abd6eec8e61206b5c51982b80d9
Binary files differ
diff --git a/tests/resources/crlf.git/objects/9d/29b5bb165bf65637ffcb5ededb82ddd7c3fd13 b/tests/resources/crlf.git/objects/9d/29b5bb165bf65637ffcb5ededb82ddd7c3fd13
new file mode 100644
index 0000000..106332d
--- /dev/null
+++ b/tests/resources/crlf.git/objects/9d/29b5bb165bf65637ffcb5ededb82ddd7c3fd13
Binary files differ
diff --git a/tests/resources/crlf.git/objects/a2/34455d62297f1856c4603686150c59fcb0aafe b/tests/resources/crlf.git/objects/a2/34455d62297f1856c4603686150c59fcb0aafe
new file mode 100644
index 0000000..7d204f4
--- /dev/null
+++ b/tests/resources/crlf.git/objects/a2/34455d62297f1856c4603686150c59fcb0aafe
Binary files differ
diff --git a/tests/resources/crlf.git/objects/a9/a2e8913c1dbe2812fac5e6b4e0a4bd5d0d5966 b/tests/resources/crlf.git/objects/a9/a2e8913c1dbe2812fac5e6b4e0a4bd5d0d5966
new file mode 100644
index 0000000..33d59f1
--- /dev/null
+++ b/tests/resources/crlf.git/objects/a9/a2e8913c1dbe2812fac5e6b4e0a4bd5d0d5966
@@ -0,0 +1 @@
+xKÊÉOR02aH.ÊIãåÂ$œž  \ No newline at end of file
diff --git a/tests/resources/crlf.git/objects/aa/f083a9cb53dac3669dcfa0e48921580d629ec7 b/tests/resources/crlf.git/objects/aa/f083a9cb53dac3669dcfa0e48921580d629ec7
new file mode 100644
index 0000000..38775d0
--- /dev/null
+++ b/tests/resources/crlf.git/objects/aa/f083a9cb53dac3669dcfa0e48921580d629ec7
Binary files differ
diff --git a/tests/resources/crlf.git/objects/af/6fcf6da196f615d7cda269b55b5c4ecfb4a5b3 b/tests/resources/crlf.git/objects/af/6fcf6da196f615d7cda269b55b5c4ecfb4a5b3
new file mode 100644
index 0000000..0acc974
--- /dev/null
+++ b/tests/resources/crlf.git/objects/af/6fcf6da196f615d7cda269b55b5c4ecfb4a5b3
Binary files differ
diff --git a/tests/resources/crlf.git/objects/b8/986fec0f7bde90f78ac72706e782d82f24f2f0 b/tests/resources/crlf.git/objects/b8/986fec0f7bde90f78ac72706e782d82f24f2f0
new file mode 100644
index 0000000..d745d20
--- /dev/null
+++ b/tests/resources/crlf.git/objects/b8/986fec0f7bde90f78ac72706e782d82f24f2f0
@@ -0,0 +1,3 @@
+x¥M
+Â0…]çs%3ÓDtá ¼À$™bÁ6¥F¼¾Q¼»÷ï¥2McònSWU`Ž6*ö¾C¡žĽGì¢w®×,‰|SŒÉ,²ê\‰¦à˜Ù#‹ôYö®8t); qÈÁȳÞÊ
+—ü’5ÃõV¦G™á -ý¨“~‹ŸÛ¥2=ù`ÑÁÖ²µ¦¥mlÕ?1æœ3È cn¶ ºHã]aïjÞ@U \ No newline at end of file
diff --git a/tests/resources/crlf.git/objects/bb/29a7b46b5d4ba3ea17b238ae561b81d59dc818 b/tests/resources/crlf.git/objects/bb/29a7b46b5d4ba3ea17b238ae561b81d59dc818
new file mode 100644
index 0000000..a08789b
--- /dev/null
+++ b/tests/resources/crlf.git/objects/bb/29a7b46b5d4ba3ea17b238ae561b81d59dc818
Binary files differ
diff --git a/tests/resources/crlf.git/objects/c3/e11722855ff260bd27418988ac1467c4e9e73a b/tests/resources/crlf.git/objects/c3/e11722855ff260bd27418988ac1467c4e9e73a
new file mode 100644
index 0000000..5f96dc7
--- /dev/null
+++ b/tests/resources/crlf.git/objects/c3/e11722855ff260bd27418988ac1467c4e9e73a
Binary files differ
diff --git a/tests/resources/crlf.git/objects/c8/d0b1ebcaccdd8f968c4aae3c2175e7fed651fe b/tests/resources/crlf.git/objects/c8/d0b1ebcaccdd8f968c4aae3c2175e7fed651fe
new file mode 100644
index 0000000..21e2ce0
--- /dev/null
+++ b/tests/resources/crlf.git/objects/c8/d0b1ebcaccdd8f968c4aae3c2175e7fed651fe
@@ -0,0 +1,2 @@
+x-ŽKnÃ0C»Ö)f8Ðod (‚tѤ4#؈e²ú9~• ;$˜öR–õK«ÌàIûˆ1*‡1;tfÌ9EdbŠ“&¢1™LʈÏPyk ­‰0ù˜‘#*FeƒÆY¥š²tÙ±á«Í{…Û7opkuI÷º·^¹„e½¦cøéÀ3ñ”ÕZ?™ NRI)Òócã
+ïÔ[ó^Ž}ëãÎ|¨+?ƒwîƒÆTÆX ƒ´#Þˆ ,¿Lê°æ!Õ5C^V>Ä=ßR~ \ No newline at end of file
diff --git a/tests/resources/crlf.git/objects/cd/574f5a2baa4c79504f8837b730fa0b11defe99 b/tests/resources/crlf.git/objects/cd/574f5a2baa4c79504f8837b730fa0b11defe99
new file mode 100644
index 0000000..e8d0202
--- /dev/null
+++ b/tests/resources/crlf.git/objects/cd/574f5a2baa4c79504f8837b730fa0b11defe99
Binary files differ
diff --git a/tests/resources/crlf.git/objects/cd/d3dacc5c0501d5ea57bbdf90e3d80176606139 b/tests/resources/crlf.git/objects/cd/d3dacc5c0501d5ea57bbdf90e3d80176606139
new file mode 100644
index 0000000..72cf3b0
--- /dev/null
+++ b/tests/resources/crlf.git/objects/cd/d3dacc5c0501d5ea57bbdf90e3d80176606139
Binary files differ
diff --git a/tests/resources/crlf.git/objects/d1/1e7ef63ba7db1db3b1b99cdbafc57a8549f8a4 b/tests/resources/crlf.git/objects/d1/1e7ef63ba7db1db3b1b99cdbafc57a8549f8a4
new file mode 100644
index 0000000..05d88fc
--- /dev/null
+++ b/tests/resources/crlf.git/objects/d1/1e7ef63ba7db1db3b1b99cdbafc57a8549f8a4
Binary files differ
diff --git a/tests/resources/crlf.git/objects/dc/88e3b917de821e25962bea7ec1f55c4ce2112c b/tests/resources/crlf.git/objects/dc/88e3b917de821e25962bea7ec1f55c4ce2112c
new file mode 100644
index 0000000..3db13aa
--- /dev/null
+++ b/tests/resources/crlf.git/objects/dc/88e3b917de821e25962bea7ec1f55c4ce2112c
Binary files differ
diff --git a/tests/resources/crlf.git/objects/de/5bfa165999d9d6c6dbafad2a7e709f93ec30fd b/tests/resources/crlf.git/objects/de/5bfa165999d9d6c6dbafad2a7e709f93ec30fd
new file mode 100644
index 0000000..e288b97
--- /dev/null
+++ b/tests/resources/crlf.git/objects/de/5bfa165999d9d6c6dbafad2a7e709f93ec30fd
Binary files differ
diff --git a/tests/resources/crlf.git/objects/e0/be8c0fa467f0a554484347c12802799d6c04fa b/tests/resources/crlf.git/objects/e0/be8c0fa467f0a554484347c12802799d6c04fa
new file mode 100644
index 0000000..b655485
--- /dev/null
+++ b/tests/resources/crlf.git/objects/e0/be8c0fa467f0a554484347c12802799d6c04fa
Binary files differ
diff --git a/tests/resources/crlf.git/objects/e1/379fd9942d04e7e80892b866d37bdb7da9e4e1 b/tests/resources/crlf.git/objects/e1/379fd9942d04e7e80892b866d37bdb7da9e4e1
new file mode 100644
index 0000000..01f8745
--- /dev/null
+++ b/tests/resources/crlf.git/objects/e1/379fd9942d04e7e80892b866d37bdb7da9e4e1
Binary files differ
diff --git a/tests/resources/crlf.git/objects/e5/062da7d7802cf492975eda580f09ac4876bd88 b/tests/resources/crlf.git/objects/e5/062da7d7802cf492975eda580f09ac4876bd88
new file mode 100644
index 0000000..62835b9
--- /dev/null
+++ b/tests/resources/crlf.git/objects/e5/062da7d7802cf492975eda580f09ac4876bd88
@@ -0,0 +1 @@
+x¥QŠ1DýÎ)új'd2"‹û)þxNÒÃÆ,3¯ï¸xÿª^AQ•j)×ÆÛU›D`ŽÔiJÖ»>Fc:D´&Rr.†ì1#÷ÎDõÇ“Ü¥,œ­H’î‚PÏ–}Á™€9rPGÅ6Ö ~ó“§ —±–¹Þa/ }«ƒü·Iµü€¶DÎx$k´ˆj¡ËØ&_Ö¨GÂæZ–¿×›ÌðÜ›Áñ|šÕ ǸUë \ No newline at end of file
diff --git a/tests/resources/crlf.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/crlf.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644
index 0000000..7112238
--- /dev/null
+++ b/tests/resources/crlf.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests/resources/crlf.git/objects/ea/030d3c6cec212069eca698cabaa5b4550f1511 b/tests/resources/crlf.git/objects/ea/030d3c6cec212069eca698cabaa5b4550f1511
new file mode 100644
index 0000000..117dc72
--- /dev/null
+++ b/tests/resources/crlf.git/objects/ea/030d3c6cec212069eca698cabaa5b4550f1511
Binary files differ
diff --git a/tests/resources/crlf.git/objects/ea/a6ce5bc192f4c3c19354e7434c01e4686e95d7 b/tests/resources/crlf.git/objects/ea/a6ce5bc192f4c3c19354e7434c01e4686e95d7
new file mode 100644
index 0000000..e994143
--- /dev/null
+++ b/tests/resources/crlf.git/objects/ea/a6ce5bc192f4c3c19354e7434c01e4686e95d7
Binary files differ
diff --git a/tests/resources/crlf.git/objects/ef/0dcd356d77221e9c27f4f3928ad28e80b87ceb b/tests/resources/crlf.git/objects/ef/0dcd356d77221e9c27f4f3928ad28e80b87ceb
new file mode 100644
index 0000000..33aceda
--- /dev/null
+++ b/tests/resources/crlf.git/objects/ef/0dcd356d77221e9c27f4f3928ad28e80b87ceb
Binary files differ
diff --git a/tests/resources/crlf.git/objects/f2/b745d7f47d114a3a6b31a7b628e61e804d1a58 b/tests/resources/crlf.git/objects/f2/b745d7f47d114a3a6b31a7b628e61e804d1a58
new file mode 100644
index 0000000..7b2e7a1
--- /dev/null
+++ b/tests/resources/crlf.git/objects/f2/b745d7f47d114a3a6b31a7b628e61e804d1a58
Binary files differ
diff --git a/tests/resources/crlf.git/objects/f4/d25b796d86387205a5498175d66e91d1e5006a b/tests/resources/crlf.git/objects/f4/d25b796d86387205a5498175d66e91d1e5006a
new file mode 100644
index 0000000..792b165
--- /dev/null
+++ b/tests/resources/crlf.git/objects/f4/d25b796d86387205a5498175d66e91d1e5006a
Binary files differ
diff --git a/tests/resources/crlf.git/objects/fa/1385d99a319b43c06f5309d1aae9fdd3adea46 b/tests/resources/crlf.git/objects/fa/1385d99a319b43c06f5309d1aae9fdd3adea46
new file mode 100644
index 0000000..d0dda45
--- /dev/null
+++ b/tests/resources/crlf.git/objects/fa/1385d99a319b43c06f5309d1aae9fdd3adea46
Binary files differ
diff --git a/tests/resources/crlf.git/objects/fe/085d9ace90cc675b87df15e1aeed0c3a31407f b/tests/resources/crlf.git/objects/fe/085d9ace90cc675b87df15e1aeed0c3a31407f
new file mode 100644
index 0000000..2e8d10b
--- /dev/null
+++ b/tests/resources/crlf.git/objects/fe/085d9ace90cc675b87df15e1aeed0c3a31407f
Binary files differ
diff --git a/tests/resources/crlf.git/objects/fe/ab3713c4659bb22700042b3c55b8d60d0a952b b/tests/resources/crlf.git/objects/fe/ab3713c4659bb22700042b3c55b8d60d0a952b
new file mode 100644
index 0000000..8552c7b
--- /dev/null
+++ b/tests/resources/crlf.git/objects/fe/ab3713c4659bb22700042b3c55b8d60d0a952b
Binary files differ
diff --git a/tests/resources/crlf.git/packed-refs b/tests/resources/crlf.git/packed-refs
new file mode 100644
index 0000000..33446e7
--- /dev/null
+++ b/tests/resources/crlf.git/packed-refs
@@ -0,0 +1,3 @@
+# pack-refs with: peeled fully-peeled sorted
+9687e444bcbb85645cb496080434c292f1b57182 refs/remotes/origin/empty-files
+6b9d5748663795f573ea857276eb2a5f8330efa0 refs/remotes/origin/master
diff --git a/tests/resources/crlf.git/refs/heads/empty-files b/tests/resources/crlf.git/refs/heads/empty-files
new file mode 100644
index 0000000..8f1fe61
--- /dev/null
+++ b/tests/resources/crlf.git/refs/heads/empty-files
@@ -0,0 +1 @@
+9687e444bcbb85645cb496080434c292f1b57182
diff --git a/tests/resources/crlf.git/refs/heads/ident b/tests/resources/crlf.git/refs/heads/ident
new file mode 100644
index 0000000..8732f0c
--- /dev/null
+++ b/tests/resources/crlf.git/refs/heads/ident
@@ -0,0 +1 @@
+b8986fec0f7bde90f78ac72706e782d82f24f2f0
diff --git a/tests/resources/crlf.git/refs/heads/master b/tests/resources/crlf.git/refs/heads/master
new file mode 100644
index 0000000..97c85bc
--- /dev/null
+++ b/tests/resources/crlf.git/refs/heads/master
@@ -0,0 +1 @@
+124f4293444614aa8da53be149792c2e43e9bfd9
diff --git a/tests/resources/crlf.git/refs/heads/no-ident b/tests/resources/crlf.git/refs/heads/no-ident
new file mode 100644
index 0000000..fa9a673
--- /dev/null
+++ b/tests/resources/crlf.git/refs/heads/no-ident
@@ -0,0 +1 @@
+1ec507638b806aba45d6142082885f2a9e88322d
diff --git a/tests/resources/crlf.git/refs/remotes/origin/HEAD b/tests/resources/crlf.git/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..6efe28f
--- /dev/null
+++ b/tests/resources/crlf.git/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/crlf/.gitattributes b/tests/resources/crlf/.gitattributes
new file mode 100644
index 0000000..fa1385d
--- /dev/null
+++ b/tests/resources/crlf/.gitattributes
@@ -0,0 +1 @@
+* -text
diff --git a/tests/resources/crlf/.gitted/HEAD b/tests/resources/crlf/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/crlf/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/crlf/.gitted/config b/tests/resources/crlf/.gitted/config
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf/.gitted/config
diff --git a/tests/resources/crlf/.gitted/index b/tests/resources/crlf/.gitted/index
new file mode 100644
index 0000000..e8d43e9
--- /dev/null
+++ b/tests/resources/crlf/.gitted/index
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/04/4bcd5c9bf5ebdd51e514a9a36457018f06f6e1 b/tests/resources/crlf/.gitted/objects/04/4bcd5c9bf5ebdd51e514a9a36457018f06f6e1
new file mode 100644
index 0000000..a32a9b2
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/04/4bcd5c9bf5ebdd51e514a9a36457018f06f6e1
@@ -0,0 +1 @@
+x-ŽÁjÃ0D{ÖWì½4H+Ù+C(É¡·þ„¤]aÓØJU…|}•˜Ã̃“ʺ. к·VE@²æÄv™ÑÈ”²ËvB½x=%‰êªl ÈsDÒÉx÷H¦!x3E9ÅA‡hPÆdU¸´¹Tøâk¨ {éký+ÛAžàµv©¬Ÿ`¬ŸŒC2|h¯µê´ŸlRá{Ù~/Ë]`zµÃ-Ì¥<ÄÝüÓ]×MÄ5¼?]ud†»ÔñÖòrõÄ&K! \ No newline at end of file
diff --git a/tests/resources/crlf/.gitted/objects/04/de00b358f13389948756732158eaaaefa1448c b/tests/resources/crlf/.gitted/objects/04/de00b358f13389948756732158eaaaefa1448c
new file mode 100644
index 0000000..c3b7598
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/04/de00b358f13389948756732158eaaaefa1448c
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/09/7722be9b67b48dfe3b19396d02fd535300ee46 b/tests/resources/crlf/.gitted/objects/09/7722be9b67b48dfe3b19396d02fd535300ee46
new file mode 100644
index 0000000..5c5c24c
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/09/7722be9b67b48dfe3b19396d02fd535300ee46
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/0a/a76e474d259bd7c13eb726a1396c381db55c88 b/tests/resources/crlf/.gitted/objects/0a/a76e474d259bd7c13eb726a1396c381db55c88
new file mode 100644
index 0000000..e118d66
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/0a/a76e474d259bd7c13eb726a1396c381db55c88
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/0d/06894e14df22e066763ae906e0ed3eb79c205f b/tests/resources/crlf/.gitted/objects/0d/06894e14df22e066763ae906e0ed3eb79c205f
new file mode 100644
index 0000000..b7a1f32
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/0d/06894e14df22e066763ae906e0ed3eb79c205f
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/0e/052888828a954ca17e5882638e3c6a083e75c0 b/tests/resources/crlf/.gitted/objects/0e/052888828a954ca17e5882638e3c6a083e75c0
new file mode 100644
index 0000000..746143f
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/0e/052888828a954ca17e5882638e3c6a083e75c0
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/0f/f5a53f19bfd2b5eea1ba550295c47515678987 b/tests/resources/crlf/.gitted/objects/0f/f5a53f19bfd2b5eea1ba550295c47515678987
new file mode 100644
index 0000000..5366acd
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/0f/f5a53f19bfd2b5eea1ba550295c47515678987
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/16/78031ee023a23bd3515e4e1693b661a69f0a73 b/tests/resources/crlf/.gitted/objects/16/78031ee023a23bd3515e4e1693b661a69f0a73
new file mode 100644
index 0000000..4aa4ffb
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/16/78031ee023a23bd3515e4e1693b661a69f0a73
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/16/c72b67861f8524a5bebc05cd20472d3fca00da b/tests/resources/crlf/.gitted/objects/16/c72b67861f8524a5bebc05cd20472d3fca00da
new file mode 100644
index 0000000..e2b1994
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/16/c72b67861f8524a5bebc05cd20472d3fca00da
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/18/c637c5d9aba6eed226ee1840cd1ca2e6c4e4c5 b/tests/resources/crlf/.gitted/objects/18/c637c5d9aba6eed226ee1840cd1ca2e6c4e4c5
new file mode 100644
index 0000000..790eb13
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/18/c637c5d9aba6eed226ee1840cd1ca2e6c4e4c5
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/20/3555c5676d75cd80d69b50beb1f4b588c59ceb b/tests/resources/crlf/.gitted/objects/20/3555c5676d75cd80d69b50beb1f4b588c59ceb
new file mode 100644
index 0000000..8038a9b
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/20/3555c5676d75cd80d69b50beb1f4b588c59ceb
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/23/f4582779e60bfa7f14750ad507399a58876611 b/tests/resources/crlf/.gitted/objects/23/f4582779e60bfa7f14750ad507399a58876611
new file mode 100644
index 0000000..4a4e4dc
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/23/f4582779e60bfa7f14750ad507399a58876611
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/2a/d3df895f68f4dda6a0a815c620b909bdd27c05 b/tests/resources/crlf/.gitted/objects/2a/d3df895f68f4dda6a0a815c620b909bdd27c05
new file mode 100644
index 0000000..f5421cf
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/2a/d3df895f68f4dda6a0a815c620b909bdd27c05
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/2b/55b4b94f655c857635b6a9005c056aa7de3532 b/tests/resources/crlf/.gitted/objects/2b/55b4b94f655c857635b6a9005c056aa7de3532
new file mode 100644
index 0000000..031fd66
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/2b/55b4b94f655c857635b6a9005c056aa7de3532
@@ -0,0 +1,2 @@
+x-ŽKjÄ0D³Ö)z2tëcµa³™].¡O ›ÄV¢È„Éé£ µ¨zð RݶµƒÖøÔ›AvyIfLiò.²Ï…œPɘL0dÑõšì<ç¨=&b{Oñ.09o4Åœ¢ÃIË”Œ
+G_jƒkþ -ÃYÆÚ¾ë~‘ðX§T·W ãØ°çÙ NˆjÐq²Kƒ·uÿ:Ö_óÇ£]na©õ.ž–÷áZ²Zk7!<#W½°ú²F \ No newline at end of file
diff --git a/tests/resources/crlf/.gitted/objects/2b/d9d81b51a867352bab307b89cbb5b4a69adfe1 b/tests/resources/crlf/.gitted/objects/2b/d9d81b51a867352bab307b89cbb5b4a69adfe1
new file mode 100644
index 0000000..96d952e
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/2b/d9d81b51a867352bab307b89cbb5b4a69adfe1
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/2c/03f9f407b576eae80327864bab572e282a33ea b/tests/resources/crlf/.gitted/objects/2c/03f9f407b576eae80327864bab572e282a33ea
new file mode 100644
index 0000000..0e4afbb
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/2c/03f9f407b576eae80327864bab572e282a33ea
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/33/cdead44e1c3ec178e39a4a69085280dbacf01b b/tests/resources/crlf/.gitted/objects/33/cdead44e1c3ec178e39a4a69085280dbacf01b
new file mode 100644
index 0000000..72dc780
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/33/cdead44e1c3ec178e39a4a69085280dbacf01b
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/38/1cfe630df902bc29271a202d3277981180e4a6 b/tests/resources/crlf/.gitted/objects/38/1cfe630df902bc29271a202d3277981180e4a6
new file mode 100644
index 0000000..0cf7072
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/38/1cfe630df902bc29271a202d3277981180e4a6
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/3f/96bdca0e37616026afaa325c148cec4aa62d04 b/tests/resources/crlf/.gitted/objects/3f/96bdca0e37616026afaa325c148cec4aa62d04
new file mode 100644
index 0000000..a204fc9
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/3f/96bdca0e37616026afaa325c148cec4aa62d04
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/41/7786fc35b3c71aa546e3f95eb5da3c8dad8c41 b/tests/resources/crlf/.gitted/objects/41/7786fc35b3c71aa546e3f95eb5da3c8dad8c41
new file mode 100644
index 0000000..ec57bde
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/41/7786fc35b3c71aa546e3f95eb5da3c8dad8c41
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/47/fbc2c28a18df0dc773276a253eb85c7516ca50 b/tests/resources/crlf/.gitted/objects/47/fbc2c28a18df0dc773276a253eb85c7516ca50
new file mode 100644
index 0000000..d16db96
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/47/fbc2c28a18df0dc773276a253eb85c7516ca50
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 b/tests/resources/crlf/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904
new file mode 100644
index 0000000..adf6411
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/5a/fb6a14a864e30787857dd92af837e8cdd2cb1b b/tests/resources/crlf/.gitted/objects/5a/fb6a14a864e30787857dd92af837e8cdd2cb1b
new file mode 100644
index 0000000..11a25c5
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/5a/fb6a14a864e30787857dd92af837e8cdd2cb1b
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/68/03c385642cebc8103fddd526ef395d75678a7e b/tests/resources/crlf/.gitted/objects/68/03c385642cebc8103fddd526ef395d75678a7e
new file mode 100644
index 0000000..f8d489f
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/68/03c385642cebc8103fddd526ef395d75678a7e
@@ -0,0 +1,2 @@
+x¥ÎKjÄ0ЬuŠ¾À},µ !dÉ &h·ZØ0²‚¬ÁäöQvÙgW¼‚¢¸–²u°ŸzËÚå9OAH¢v£ ÓB¬ØhÉ9!õEMö}œÑcžfÔ˜52vÙ%ëmä` /¬èÑ×Úà=ÔÜÖZŽºÃ‹ ýMoeãVšû3×ò
+fr1ƒpÑ“Öjè8Úåêšäí.œ[_¡Ðþ Ÿ·K^©@ûèåükêÑ¡Yj \ No newline at end of file
diff --git a/tests/resources/crlf/.gitted/objects/69/597764abeaa1a403ebf589d2ea579c6a8f877e b/tests/resources/crlf/.gitted/objects/69/597764abeaa1a403ebf589d2ea579c6a8f877e
new file mode 100644
index 0000000..ee4f427
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/69/597764abeaa1a403ebf589d2ea579c6a8f877e
@@ -0,0 +1 @@
+xÎÑÂ0 €až3…ßOwr“&­%„`†NàÄŽˆ -JÃ!¶1|¿þ´Îsi`ݸkUÈ¢r.*{z¤‘Ägr>ɱcòbn\ui êcæ.x"’‚DÎ,–ÞN“Ã,†ïí¼V˜þu©Õ’.umgØëÌåzLÛïãü=@×[kðƒ¢IŸÇ¦ßismbY¸>!—«næJ“LÅ \ No newline at end of file
diff --git a/tests/resources/crlf/.gitted/objects/6a/e3e9c11a51f0aabebcffcbd5c00f4beed143c9 b/tests/resources/crlf/.gitted/objects/6a/e3e9c11a51f0aabebcffcbd5c00f4beed143c9
new file mode 100644
index 0000000..6c18a3a
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/6a/e3e9c11a51f0aabebcffcbd5c00f4beed143c9
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/6c/589757f65a970a6cc07c71c3f3d2528c611cbc b/tests/resources/crlf/.gitted/objects/6c/589757f65a970a6cc07c71c3f3d2528c611cbc
new file mode 100644
index 0000000..fe4da8c
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/6c/589757f65a970a6cc07c71c3f3d2528c611cbc
@@ -0,0 +1,2 @@
+x-ŽKNÃ0„Yçÿµò;Ž„PYp‚rÿ5j#Ç€¸=nÅnF3ói¸¬ëÒÀ…ñ©UUp$“dKÑbN£Ž¼)OL)`šPfµÃ'VÝpCV‰‘Y$ÏSÊÕ³³cÔqVIÑÎ:àW»”
+çoÝàÜêÂ×ZÚ^tÅåvâýðÓGÑW°Á9gLš2<kÌÀM+¼Ko |\ʺ—­;ó®Núþݱîï£õ>$8˜Ð1ÛìeU eÃú órÓ}ø.Q¯ \ No newline at end of file
diff --git a/tests/resources/crlf/.gitted/objects/77/afe26d93c49279ca90604c125496920753fede b/tests/resources/crlf/.gitted/objects/77/afe26d93c49279ca90604c125496920753fede
new file mode 100644
index 0000000..a377cb0
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/77/afe26d93c49279ca90604c125496920753fede
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/78/db270c1841841f75a8157321bdcb50ab12e6c3 b/tests/resources/crlf/.gitted/objects/78/db270c1841841f75a8157321bdcb50ab12e6c3
new file mode 100644
index 0000000..8a55bb0
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/78/db270c1841841f75a8157321bdcb50ab12e6c3
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/79/9770d1cff46753a57db7a066159b5610da6e3a b/tests/resources/crlf/.gitted/objects/79/9770d1cff46753a57db7a066159b5610da6e3a
new file mode 100644
index 0000000..5c701b8
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/79/9770d1cff46753a57db7a066159b5610da6e3a
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/7c/ce67e58173e2b01f7db124ceaabe3183d19c49 b/tests/resources/crlf/.gitted/objects/7c/ce67e58173e2b01f7db124ceaabe3183d19c49
new file mode 100644
index 0000000..8e836ab
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/7c/ce67e58173e2b01f7db124ceaabe3183d19c49
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/85/340755cfe5e28c2835781978bb1cece91b3d0f b/tests/resources/crlf/.gitted/objects/85/340755cfe5e28c2835781978bb1cece91b3d0f
new file mode 100644
index 0000000..e83fbc2
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/85/340755cfe5e28c2835781978bb1cece91b3d0f
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/92/0e90a663bea5d740989d5f935f6dfb473a0c5d b/tests/resources/crlf/.gitted/objects/92/0e90a663bea5d740989d5f935f6dfb473a0c5d
new file mode 100644
index 0000000..f872be6
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/92/0e90a663bea5d740989d5f935f6dfb473a0c5d
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/96/87e444bcbb85645cb496080434c292f1b57182 b/tests/resources/crlf/.gitted/objects/96/87e444bcbb85645cb496080434c292f1b57182
new file mode 100644
index 0000000..5df64d8
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/96/87e444bcbb85645cb496080434c292f1b57182
@@ -0,0 +1 @@
+x¥ÎAJ1@Q×9Eí…!©T:™Y Þ¢ºRí´ÐMªAoïˆGpûŸ/mß7LùÁº*L¬Q‹„À)¬žyÑEÖU–šÄû•Õ(JqÜõfP2Q©Œ1¥”%¥™‰âL•æ¬Ó$QS Ž»¶¯,Ÿ‡xѾoãz|xzÿ³·ó°¾±ñIÚþ 0{ŠS@xôè½»ë}Õô_w©U+H»Ùï¾50†'û2÷ÿ=T‰ \ No newline at end of file
diff --git a/tests/resources/crlf/.gitted/objects/97/449da2d225557c558ac244384d487e66c3e591 b/tests/resources/crlf/.gitted/objects/97/449da2d225557c558ac244384d487e66c3e591
new file mode 100644
index 0000000..d3917a4
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/97/449da2d225557c558ac244384d487e66c3e591
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/9a/6c3533fef19abd6eec8e61206b5c51982b80d9 b/tests/resources/crlf/.gitted/objects/9a/6c3533fef19abd6eec8e61206b5c51982b80d9
new file mode 100644
index 0000000..78fc8ae
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/9a/6c3533fef19abd6eec8e61206b5c51982b80d9
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/9d/29b5bb165bf65637ffcb5ededb82ddd7c3fd13 b/tests/resources/crlf/.gitted/objects/9d/29b5bb165bf65637ffcb5ededb82ddd7c3fd13
new file mode 100644
index 0000000..106332d
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/9d/29b5bb165bf65637ffcb5ededb82ddd7c3fd13
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/a2/34455d62297f1856c4603686150c59fcb0aafe b/tests/resources/crlf/.gitted/objects/a2/34455d62297f1856c4603686150c59fcb0aafe
new file mode 100644
index 0000000..7d204f4
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/a2/34455d62297f1856c4603686150c59fcb0aafe
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/a9/a2e8913c1dbe2812fac5e6b4e0a4bd5d0d5966 b/tests/resources/crlf/.gitted/objects/a9/a2e8913c1dbe2812fac5e6b4e0a4bd5d0d5966
new file mode 100644
index 0000000..33d59f1
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/a9/a2e8913c1dbe2812fac5e6b4e0a4bd5d0d5966
@@ -0,0 +1 @@
+xKÊÉOR02aH.ÊIãåÂ$œž  \ No newline at end of file
diff --git a/tests/resources/crlf/.gitted/objects/aa/f083a9cb53dac3669dcfa0e48921580d629ec7 b/tests/resources/crlf/.gitted/objects/aa/f083a9cb53dac3669dcfa0e48921580d629ec7
new file mode 100644
index 0000000..38775d0
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/aa/f083a9cb53dac3669dcfa0e48921580d629ec7
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/af/6fcf6da196f615d7cda269b55b5c4ecfb4a5b3 b/tests/resources/crlf/.gitted/objects/af/6fcf6da196f615d7cda269b55b5c4ecfb4a5b3
new file mode 100644
index 0000000..0acc974
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/af/6fcf6da196f615d7cda269b55b5c4ecfb4a5b3
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/bb/29a7b46b5d4ba3ea17b238ae561b81d59dc818 b/tests/resources/crlf/.gitted/objects/bb/29a7b46b5d4ba3ea17b238ae561b81d59dc818
new file mode 100644
index 0000000..a08789b
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/bb/29a7b46b5d4ba3ea17b238ae561b81d59dc818
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/c3/e11722855ff260bd27418988ac1467c4e9e73a b/tests/resources/crlf/.gitted/objects/c3/e11722855ff260bd27418988ac1467c4e9e73a
new file mode 100644
index 0000000..5f96dc7
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/c3/e11722855ff260bd27418988ac1467c4e9e73a
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/c8/d0b1ebcaccdd8f968c4aae3c2175e7fed651fe b/tests/resources/crlf/.gitted/objects/c8/d0b1ebcaccdd8f968c4aae3c2175e7fed651fe
new file mode 100644
index 0000000..21e2ce0
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/c8/d0b1ebcaccdd8f968c4aae3c2175e7fed651fe
@@ -0,0 +1,2 @@
+x-ŽKnÃ0C»Ö)f8Ðod (‚tѤ4#؈e²ú9~• ;$˜öR–õK«ÌàIûˆ1*‡1;tfÌ9EdbŠ“&¢1™LʈÏPyk ­‰0ù˜‘#*FeƒÆY¥š²tÙ±á«Í{…Û7opkuI÷º·^¹„e½¦cøéÀ3ñ”ÕZ?™ NRI)Òócã
+ïÔ[ó^Ž}ëãÎ|¨+?ƒwîƒÆTÆX ƒ´#Þˆ ,¿Lê°æ!Õ5C^V>Ä=ßR~ \ No newline at end of file
diff --git a/tests/resources/crlf/.gitted/objects/cd/574f5a2baa4c79504f8837b730fa0b11defe99 b/tests/resources/crlf/.gitted/objects/cd/574f5a2baa4c79504f8837b730fa0b11defe99
new file mode 100644
index 0000000..e8d0202
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/cd/574f5a2baa4c79504f8837b730fa0b11defe99
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/cd/d3dacc5c0501d5ea57bbdf90e3d80176606139 b/tests/resources/crlf/.gitted/objects/cd/d3dacc5c0501d5ea57bbdf90e3d80176606139
new file mode 100644
index 0000000..72cf3b0
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/cd/d3dacc5c0501d5ea57bbdf90e3d80176606139
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/d1/1e7ef63ba7db1db3b1b99cdbafc57a8549f8a4 b/tests/resources/crlf/.gitted/objects/d1/1e7ef63ba7db1db3b1b99cdbafc57a8549f8a4
new file mode 100644
index 0000000..05d88fc
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/d1/1e7ef63ba7db1db3b1b99cdbafc57a8549f8a4
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/dc/88e3b917de821e25962bea7ec1f55c4ce2112c b/tests/resources/crlf/.gitted/objects/dc/88e3b917de821e25962bea7ec1f55c4ce2112c
new file mode 100644
index 0000000..3db13aa
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/dc/88e3b917de821e25962bea7ec1f55c4ce2112c
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/de/5bfa165999d9d6c6dbafad2a7e709f93ec30fd b/tests/resources/crlf/.gitted/objects/de/5bfa165999d9d6c6dbafad2a7e709f93ec30fd
new file mode 100644
index 0000000..e288b97
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/de/5bfa165999d9d6c6dbafad2a7e709f93ec30fd
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/e5/062da7d7802cf492975eda580f09ac4876bd88 b/tests/resources/crlf/.gitted/objects/e5/062da7d7802cf492975eda580f09ac4876bd88
new file mode 100644
index 0000000..62835b9
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/e5/062da7d7802cf492975eda580f09ac4876bd88
@@ -0,0 +1 @@
+x¥QŠ1DýÎ)új'd2"‹û)þxNÒÃÆ,3¯ï¸xÿª^AQ•j)×ÆÛU›D`ŽÔiJÖ»>Fc:D´&Rr.†ì1#÷ÎDõÇ“Ü¥,œ­H’î‚PÏ–}Á™€9rPGÅ6Ö ~ó“§ —±–¹Þa/ }«ƒü·Iµü€¶DÎx$k´ˆj¡ËØ&_Ö¨GÂæZ–¿×›ÌðÜ›Áñ|šÕ ǸUë \ No newline at end of file
diff --git a/tests/resources/crlf/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/crlf/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644
index 0000000..7112238
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/ea/030d3c6cec212069eca698cabaa5b4550f1511 b/tests/resources/crlf/.gitted/objects/ea/030d3c6cec212069eca698cabaa5b4550f1511
new file mode 100644
index 0000000..117dc72
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/ea/030d3c6cec212069eca698cabaa5b4550f1511
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/ef/0dcd356d77221e9c27f4f3928ad28e80b87ceb b/tests/resources/crlf/.gitted/objects/ef/0dcd356d77221e9c27f4f3928ad28e80b87ceb
new file mode 100644
index 0000000..33aceda
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/ef/0dcd356d77221e9c27f4f3928ad28e80b87ceb
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/f2/b745d7f47d114a3a6b31a7b628e61e804d1a58 b/tests/resources/crlf/.gitted/objects/f2/b745d7f47d114a3a6b31a7b628e61e804d1a58
new file mode 100644
index 0000000..7b2e7a1
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/f2/b745d7f47d114a3a6b31a7b628e61e804d1a58
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/f4/d25b796d86387205a5498175d66e91d1e5006a b/tests/resources/crlf/.gitted/objects/f4/d25b796d86387205a5498175d66e91d1e5006a
new file mode 100644
index 0000000..792b165
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/f4/d25b796d86387205a5498175d66e91d1e5006a
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/fe/085d9ace90cc675b87df15e1aeed0c3a31407f b/tests/resources/crlf/.gitted/objects/fe/085d9ace90cc675b87df15e1aeed0c3a31407f
new file mode 100644
index 0000000..2e8d10b
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/fe/085d9ace90cc675b87df15e1aeed0c3a31407f
Binary files differ
diff --git a/tests/resources/crlf/.gitted/objects/fe/ab3713c4659bb22700042b3c55b8d60d0a952b b/tests/resources/crlf/.gitted/objects/fe/ab3713c4659bb22700042b3c55b8d60d0a952b
new file mode 100644
index 0000000..8552c7b
--- /dev/null
+++ b/tests/resources/crlf/.gitted/objects/fe/ab3713c4659bb22700042b3c55b8d60d0a952b
Binary files differ
diff --git a/tests/resources/crlf/.gitted/refs/heads/empty-files b/tests/resources/crlf/.gitted/refs/heads/empty-files
new file mode 100644
index 0000000..8f1fe61
--- /dev/null
+++ b/tests/resources/crlf/.gitted/refs/heads/empty-files
@@ -0,0 +1 @@
+9687e444bcbb85645cb496080434c292f1b57182
diff --git a/tests/resources/crlf/.gitted/refs/heads/master b/tests/resources/crlf/.gitted/refs/heads/master
new file mode 100644
index 0000000..fe1d0aa
--- /dev/null
+++ b/tests/resources/crlf/.gitted/refs/heads/master
@@ -0,0 +1 @@
+5afb6a14a864e30787857dd92af837e8cdd2cb1b
diff --git a/tests/resources/crlf/all-crlf b/tests/resources/crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf/all-crlf-utf8bom b/tests/resources/crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf/all-lf b/tests/resources/crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf/all-lf-utf8bom b/tests/resources/crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf/binary-all-crlf b/tests/resources/crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf/binary-all-lf b/tests/resources/crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf/binary-mixed-lf-cr b/tests/resources/crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf/few-utf8-chars-crlf b/tests/resources/crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf/few-utf8-chars-lf b/tests/resources/crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf/many-utf8-chars-crlf b/tests/resources/crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf/many-utf8-chars-lf b/tests/resources/crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf/mixed-lf-cr b/tests/resources/crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf/mixed-lf-cr-crlf b/tests/resources/crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf/more-crlf b/tests/resources/crlf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf/more-crlf-utf8bom b/tests/resources/crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf/more-lf b/tests/resources/crlf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf/more-lf-utf8bom b/tests/resources/crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf/zero-byte b/tests/resources/crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf/zero-byte
diff --git a/tests/resources/crlf_data/.gitattributes b/tests/resources/crlf_data/.gitattributes
new file mode 100644
index 0000000..23d9bdb
--- /dev/null
+++ b/tests/resources/crlf_data/.gitattributes
@@ -0,0 +1 @@
+* binary
diff --git a/tests/resources/crlf_data/README b/tests/resources/crlf_data/README
new file mode 100644
index 0000000..0c8badf
--- /dev/null
+++ b/tests/resources/crlf_data/README
@@ -0,0 +1,3 @@
+This test data was generated using the `tests/resources/generate_crlf.sh`
+(posix and windows directories) and `tests/resources/generate_crlf_checkin.sh`
+(checkin_results directory) scripts. Please see these scripts for usage information.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,-text/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false,text_auto/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_false/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,-text/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/all-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/all-crlf-utf8bom.fail
new file mode 100644
index 0000000..213f972
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/all-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/all-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/all-crlf.fail
new file mode 100644
index 0000000..851ef6c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/binary-all-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/binary-all-crlf.fail
new file mode 100644
index 0000000..26690f3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/binary-all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-all-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..c6b706d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/few-utf8-chars-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/few-utf8-chars-crlf.fail
new file mode 100644
index 0000000..c23959d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/few-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'few-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/many-utf8-chars-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/many-utf8-chars-crlf.fail
new file mode 100644
index 0000000..2e093c9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/many-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'many-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..3c6cbac
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..43b93db
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/more-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/more-crlf.fail
new file mode 100644
index 0000000..b1a5dc9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/more-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/more-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..13c4e3c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/more-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/more-lf.fail
new file mode 100644
index 0000000..c964c72
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/more-lf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/all-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/all-lf-utf8bom.fail
new file mode 100644
index 0000000..547008e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/all-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/all-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/all-lf.fail
new file mode 100644
index 0000000..d992d47
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/binary-all-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/binary-all-lf.fail
new file mode 100644
index 0000000..180663e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/binary-all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-all-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..f87ce3f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/binary-mixed-lf-cr.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/binary-mixed-lf-cr.fail
new file mode 100644
index 0000000..63eac58
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/binary-mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/few-utf8-chars-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/few-utf8-chars-lf.fail
new file mode 100644
index 0000000..fabf466
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/few-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'few-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/many-utf8-chars-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/many-utf8-chars-lf.fail
new file mode 100644
index 0000000..b874f4b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/many-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'many-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..a353e0e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/mixed-lf-cr.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/mixed-lf-cr.fail
new file mode 100644
index 0000000..0287e32
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..e77b2b9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/more-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/more-crlf.fail
new file mode 100644
index 0000000..07f50c3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/more-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/more-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..6994a8d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/more-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/more-lf.fail
new file mode 100644
index 0000000..067a2e8
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/more-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/all-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/all-crlf-utf8bom.fail
new file mode 100644
index 0000000..213f972
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/all-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/all-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/all-crlf.fail
new file mode 100644
index 0000000..851ef6c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/binary-all-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/binary-all-crlf.fail
new file mode 100644
index 0000000..26690f3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/binary-all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-all-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..c6b706d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/few-utf8-chars-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/few-utf8-chars-crlf.fail
new file mode 100644
index 0000000..c23959d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/few-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'few-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/many-utf8-chars-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/many-utf8-chars-crlf.fail
new file mode 100644
index 0000000..2e093c9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/many-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'many-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..3c6cbac
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..43b93db
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/more-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/more-crlf.fail
new file mode 100644
index 0000000..b1a5dc9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/more-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/more-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..13c4e3c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/more-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/more-lf.fail
new file mode 100644
index 0000000..c964c72
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/more-lf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/all-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/all-lf-utf8bom.fail
new file mode 100644
index 0000000..547008e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/all-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/all-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/all-lf.fail
new file mode 100644
index 0000000..d992d47
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/binary-all-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/binary-all-lf.fail
new file mode 100644
index 0000000..180663e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/binary-all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-all-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..f87ce3f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr.fail
new file mode 100644
index 0000000..63eac58
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/few-utf8-chars-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/few-utf8-chars-lf.fail
new file mode 100644
index 0000000..fabf466
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/few-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'few-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/many-utf8-chars-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/many-utf8-chars-lf.fail
new file mode 100644
index 0000000..b874f4b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/many-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'many-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..a353e0e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/mixed-lf-cr.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/mixed-lf-cr.fail
new file mode 100644
index 0000000..0287e32
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..e77b2b9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/more-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/more-crlf.fail
new file mode 100644
index 0000000..07f50c3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/more-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/more-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..6994a8d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/more-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/more-lf.fail
new file mode 100644
index 0000000..067a2e8
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/more-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/all-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/all-crlf-utf8bom.fail
new file mode 100644
index 0000000..213f972
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/all-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/all-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/all-crlf.fail
new file mode 100644
index 0000000..851ef6c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/binary-all-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/binary-all-crlf.fail
new file mode 100644
index 0000000..26690f3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/binary-all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-all-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..c6b706d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/few-utf8-chars-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/few-utf8-chars-crlf.fail
new file mode 100644
index 0000000..c23959d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/few-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'few-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/many-utf8-chars-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/many-utf8-chars-crlf.fail
new file mode 100644
index 0000000..2e093c9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/many-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'many-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..3c6cbac
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..43b93db
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/more-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/more-crlf.fail
new file mode 100644
index 0000000..b1a5dc9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/more-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/more-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..13c4e3c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/more-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/more-lf.fail
new file mode 100644
index 0000000..c964c72
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/more-lf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/all-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/all-crlf-utf8bom.fail
new file mode 100644
index 0000000..213f972
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/all-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/all-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/all-crlf.fail
new file mode 100644
index 0000000..851ef6c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/binary-all-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/binary-all-crlf.fail
new file mode 100644
index 0000000..26690f3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/binary-all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-all-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..c6b706d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/few-utf8-chars-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/few-utf8-chars-crlf.fail
new file mode 100644
index 0000000..c23959d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/few-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'few-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/many-utf8-chars-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/many-utf8-chars-crlf.fail
new file mode 100644
index 0000000..2e093c9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/many-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'many-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..3c6cbac
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/more-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..43b93db
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/more-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/more-crlf.fail
new file mode 100644
index 0000000..b1a5dc9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/more-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/more-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/more-lf-utf8bom.fail
new file mode 100644
index 0000000..13c4e3c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/more-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/more-lf.fail
new file mode 100644
index 0000000..c964c72
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/more-lf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/all-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/all-lf-utf8bom.fail
new file mode 100644
index 0000000..547008e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/all-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/all-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/all-lf.fail
new file mode 100644
index 0000000..d992d47
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-lf.fail
new file mode 100644
index 0000000..fabf466
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'few-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-lf.fail
new file mode 100644
index 0000000..b874f4b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'many-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..e77b2b9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/more-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/more-crlf.fail
new file mode 100644
index 0000000..07f50c3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/more-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/more-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..6994a8d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/more-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/more-lf.fail
new file mode 100644
index 0000000..067a2e8
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/more-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/all-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/all-crlf-utf8bom.fail
new file mode 100644
index 0000000..213f972
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/all-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/all-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/all-crlf.fail
new file mode 100644
index 0000000..851ef6c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/few-utf8-chars-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/few-utf8-chars-crlf.fail
new file mode 100644
index 0000000..c23959d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/few-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'few-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/many-utf8-chars-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/many-utf8-chars-crlf.fail
new file mode 100644
index 0000000..2e093c9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/many-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'many-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..43b93db
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/more-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/more-crlf.fail
new file mode 100644
index 0000000..b1a5dc9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/more-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/more-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..13c4e3c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/more-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/more-lf.fail
new file mode 100644
index 0000000..c964c72
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/more-lf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/all-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/all-crlf-utf8bom.fail
new file mode 100644
index 0000000..213f972
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/all-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/all-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/all-crlf.fail
new file mode 100644
index 0000000..851ef6c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/few-utf8-chars-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/few-utf8-chars-crlf.fail
new file mode 100644
index 0000000..c23959d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/few-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'few-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/many-utf8-chars-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/many-utf8-chars-crlf.fail
new file mode 100644
index 0000000..2e093c9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/many-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'many-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/more-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..43b93db
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/more-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/more-crlf.fail
new file mode 100644
index 0000000..b1a5dc9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/more-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/more-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/more-lf-utf8bom.fail
new file mode 100644
index 0000000..13c4e3c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/more-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/more-lf.fail
new file mode 100644
index 0000000..c964c72
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/more-lf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true,text_auto/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_true/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,-text/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn,text_auto/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_false,safecrlf_warn/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,-text/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false,text_auto/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_false/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,-text/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/all-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/all-crlf-utf8bom.fail
new file mode 100644
index 0000000..213f972
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/all-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/all-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/all-crlf.fail
new file mode 100644
index 0000000..851ef6c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/binary-all-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/binary-all-crlf.fail
new file mode 100644
index 0000000..26690f3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/binary-all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-all-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..c6b706d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/few-utf8-chars-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/few-utf8-chars-crlf.fail
new file mode 100644
index 0000000..c23959d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/few-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'few-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/many-utf8-chars-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/many-utf8-chars-crlf.fail
new file mode 100644
index 0000000..2e093c9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/many-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'many-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..3c6cbac
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..43b93db
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/more-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/more-crlf.fail
new file mode 100644
index 0000000..b1a5dc9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/more-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/more-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..13c4e3c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/more-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/more-lf.fail
new file mode 100644
index 0000000..c964c72
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/more-lf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/all-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/all-lf-utf8bom.fail
new file mode 100644
index 0000000..547008e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/all-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/all-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/all-lf.fail
new file mode 100644
index 0000000..d992d47
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/binary-all-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/binary-all-lf.fail
new file mode 100644
index 0000000..180663e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/binary-all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-all-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..f87ce3f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/binary-mixed-lf-cr.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/binary-mixed-lf-cr.fail
new file mode 100644
index 0000000..63eac58
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/binary-mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/few-utf8-chars-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/few-utf8-chars-lf.fail
new file mode 100644
index 0000000..fabf466
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/few-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'few-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/many-utf8-chars-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/many-utf8-chars-lf.fail
new file mode 100644
index 0000000..b874f4b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/many-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'many-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..a353e0e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/mixed-lf-cr.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/mixed-lf-cr.fail
new file mode 100644
index 0000000..0287e32
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..e77b2b9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/more-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/more-crlf.fail
new file mode 100644
index 0000000..07f50c3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/more-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/more-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..6994a8d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/more-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/more-lf.fail
new file mode 100644
index 0000000..067a2e8
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/more-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/all-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/all-crlf-utf8bom.fail
new file mode 100644
index 0000000..213f972
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/all-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/all-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/all-crlf.fail
new file mode 100644
index 0000000..851ef6c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/binary-all-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/binary-all-crlf.fail
new file mode 100644
index 0000000..26690f3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/binary-all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-all-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..c6b706d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/few-utf8-chars-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/few-utf8-chars-crlf.fail
new file mode 100644
index 0000000..c23959d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/few-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'few-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/many-utf8-chars-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/many-utf8-chars-crlf.fail
new file mode 100644
index 0000000..2e093c9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/many-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'many-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..3c6cbac
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..43b93db
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/more-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/more-crlf.fail
new file mode 100644
index 0000000..b1a5dc9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/more-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/more-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..13c4e3c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/more-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/more-lf.fail
new file mode 100644
index 0000000..c964c72
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/more-lf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/all-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/all-lf-utf8bom.fail
new file mode 100644
index 0000000..547008e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/all-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/all-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/all-lf.fail
new file mode 100644
index 0000000..d992d47
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/binary-all-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/binary-all-lf.fail
new file mode 100644
index 0000000..180663e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/binary-all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-all-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..f87ce3f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr.fail
new file mode 100644
index 0000000..63eac58
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/few-utf8-chars-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/few-utf8-chars-lf.fail
new file mode 100644
index 0000000..fabf466
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/few-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'few-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/many-utf8-chars-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/many-utf8-chars-lf.fail
new file mode 100644
index 0000000..b874f4b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/many-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'many-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..a353e0e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/mixed-lf-cr.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/mixed-lf-cr.fail
new file mode 100644
index 0000000..0287e32
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..e77b2b9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/more-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/more-crlf.fail
new file mode 100644
index 0000000..07f50c3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/more-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/more-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..6994a8d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/more-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/more-lf.fail
new file mode 100644
index 0000000..067a2e8
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/more-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/all-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/all-crlf-utf8bom.fail
new file mode 100644
index 0000000..213f972
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/all-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/all-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/all-crlf.fail
new file mode 100644
index 0000000..851ef6c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/binary-all-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/binary-all-crlf.fail
new file mode 100644
index 0000000..26690f3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/binary-all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-all-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..c6b706d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/few-utf8-chars-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/few-utf8-chars-crlf.fail
new file mode 100644
index 0000000..c23959d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/few-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'few-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/many-utf8-chars-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/many-utf8-chars-crlf.fail
new file mode 100644
index 0000000..2e093c9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/many-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'many-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..3c6cbac
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..43b93db
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/more-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/more-crlf.fail
new file mode 100644
index 0000000..b1a5dc9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/more-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/more-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..13c4e3c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/more-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/more-lf.fail
new file mode 100644
index 0000000..c964c72
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/more-lf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/all-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/all-crlf-utf8bom.fail
new file mode 100644
index 0000000..213f972
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/all-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/all-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/all-crlf.fail
new file mode 100644
index 0000000..851ef6c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/binary-all-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/binary-all-crlf.fail
new file mode 100644
index 0000000..26690f3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/binary-all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-all-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..c6b706d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/few-utf8-chars-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/few-utf8-chars-crlf.fail
new file mode 100644
index 0000000..c23959d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/few-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'few-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/many-utf8-chars-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/many-utf8-chars-crlf.fail
new file mode 100644
index 0000000..2e093c9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/many-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'many-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..3c6cbac
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/more-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..43b93db
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/more-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/more-crlf.fail
new file mode 100644
index 0000000..b1a5dc9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/more-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/more-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/more-lf-utf8bom.fail
new file mode 100644
index 0000000..13c4e3c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/more-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/more-lf.fail
new file mode 100644
index 0000000..c964c72
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/more-lf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/all-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/all-lf-utf8bom.fail
new file mode 100644
index 0000000..547008e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/all-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/all-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/all-lf.fail
new file mode 100644
index 0000000..d992d47
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-lf.fail
new file mode 100644
index 0000000..fabf466
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'few-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-lf.fail
new file mode 100644
index 0000000..b874f4b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'many-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..e77b2b9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/more-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/more-crlf.fail
new file mode 100644
index 0000000..07f50c3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/more-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/more-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..6994a8d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/more-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/more-lf.fail
new file mode 100644
index 0000000..067a2e8
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/more-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/all-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/all-crlf-utf8bom.fail
new file mode 100644
index 0000000..213f972
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/all-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/all-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/all-crlf.fail
new file mode 100644
index 0000000..851ef6c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/few-utf8-chars-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/few-utf8-chars-crlf.fail
new file mode 100644
index 0000000..c23959d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/few-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'few-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/many-utf8-chars-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/many-utf8-chars-crlf.fail
new file mode 100644
index 0000000..2e093c9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/many-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'many-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..43b93db
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/more-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/more-crlf.fail
new file mode 100644
index 0000000..b1a5dc9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/more-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/more-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..13c4e3c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/more-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/more-lf.fail
new file mode 100644
index 0000000..c964c72
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/more-lf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/all-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/all-crlf-utf8bom.fail
new file mode 100644
index 0000000..213f972
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/all-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/all-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/all-crlf.fail
new file mode 100644
index 0000000..851ef6c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/few-utf8-chars-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/few-utf8-chars-crlf.fail
new file mode 100644
index 0000000..c23959d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/few-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'few-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/many-utf8-chars-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/many-utf8-chars-crlf.fail
new file mode 100644
index 0000000..2e093c9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/many-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'many-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/more-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..43b93db
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/more-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/more-crlf.fail
new file mode 100644
index 0000000..b1a5dc9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/more-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/more-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/more-lf-utf8bom.fail
new file mode 100644
index 0000000..13c4e3c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/more-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/more-lf.fail
new file mode 100644
index 0000000..c964c72
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/more-lf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true,text_auto/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/all-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/all-crlf-utf8bom.fail
new file mode 100644
index 0000000..213f972
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/all-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/all-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/all-crlf.fail
new file mode 100644
index 0000000..851ef6c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/few-utf8-chars-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/few-utf8-chars-crlf.fail
new file mode 100644
index 0000000..c23959d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/few-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'few-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/many-utf8-chars-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/many-utf8-chars-crlf.fail
new file mode 100644
index 0000000..2e093c9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/many-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'many-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/more-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..43b93db
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/more-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/more-crlf.fail
new file mode 100644
index 0000000..b1a5dc9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/more-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/more-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/more-lf-utf8bom.fail
new file mode 100644
index 0000000..13c4e3c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/more-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/more-lf.fail
new file mode 100644
index 0000000..c964c72
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/more-lf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_true/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,-text/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn,text_auto/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_input,safecrlf_warn/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,-text/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false,text_auto/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_false/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,-text/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/all-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/all-lf-utf8bom.fail
new file mode 100644
index 0000000..547008e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/all-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/all-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/all-lf.fail
new file mode 100644
index 0000000..d992d47
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/binary-all-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/binary-all-lf.fail
new file mode 100644
index 0000000..180663e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/binary-all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-all-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..f87ce3f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/binary-mixed-lf-cr.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/binary-mixed-lf-cr.fail
new file mode 100644
index 0000000..63eac58
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/binary-mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/few-utf8-chars-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/few-utf8-chars-lf.fail
new file mode 100644
index 0000000..fabf466
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/few-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'few-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/many-utf8-chars-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/many-utf8-chars-lf.fail
new file mode 100644
index 0000000..b874f4b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/many-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'many-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..a353e0e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/mixed-lf-cr.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/mixed-lf-cr.fail
new file mode 100644
index 0000000..0287e32
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..e77b2b9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/more-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/more-crlf.fail
new file mode 100644
index 0000000..07f50c3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/more-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/more-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..6994a8d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/more-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/more-lf.fail
new file mode 100644
index 0000000..067a2e8
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/more-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/all-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/all-lf-utf8bom.fail
new file mode 100644
index 0000000..547008e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/all-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/all-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/all-lf.fail
new file mode 100644
index 0000000..d992d47
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/binary-all-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/binary-all-lf.fail
new file mode 100644
index 0000000..180663e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/binary-all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-all-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..f87ce3f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/binary-mixed-lf-cr.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/binary-mixed-lf-cr.fail
new file mode 100644
index 0000000..63eac58
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/binary-mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/few-utf8-chars-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/few-utf8-chars-lf.fail
new file mode 100644
index 0000000..fabf466
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/few-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'few-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/many-utf8-chars-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/many-utf8-chars-lf.fail
new file mode 100644
index 0000000..b874f4b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/many-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'many-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..a353e0e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/mixed-lf-cr.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/mixed-lf-cr.fail
new file mode 100644
index 0000000..0287e32
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..e77b2b9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/more-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/more-crlf.fail
new file mode 100644
index 0000000..07f50c3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/more-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/more-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..6994a8d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/more-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/more-lf.fail
new file mode 100644
index 0000000..067a2e8
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/more-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/all-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/all-crlf-utf8bom.fail
new file mode 100644
index 0000000..213f972
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/all-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/all-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/all-crlf.fail
new file mode 100644
index 0000000..851ef6c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/binary-all-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/binary-all-crlf.fail
new file mode 100644
index 0000000..26690f3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/binary-all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-all-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..c6b706d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/few-utf8-chars-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/few-utf8-chars-crlf.fail
new file mode 100644
index 0000000..c23959d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/few-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'few-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/many-utf8-chars-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/many-utf8-chars-crlf.fail
new file mode 100644
index 0000000..2e093c9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/many-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'many-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..3c6cbac
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..43b93db
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/more-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/more-crlf.fail
new file mode 100644
index 0000000..b1a5dc9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/more-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/more-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..13c4e3c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/more-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/more-lf.fail
new file mode 100644
index 0000000..c964c72
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/more-lf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/all-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/all-lf-utf8bom.fail
new file mode 100644
index 0000000..547008e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/all-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/all-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/all-lf.fail
new file mode 100644
index 0000000..d992d47
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/binary-all-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/binary-all-lf.fail
new file mode 100644
index 0000000..180663e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/binary-all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-all-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..f87ce3f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr.fail
new file mode 100644
index 0000000..63eac58
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/few-utf8-chars-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/few-utf8-chars-lf.fail
new file mode 100644
index 0000000..fabf466
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/few-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'few-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/many-utf8-chars-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/many-utf8-chars-lf.fail
new file mode 100644
index 0000000..b874f4b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/many-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'many-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..a353e0e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/mixed-lf-cr.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/mixed-lf-cr.fail
new file mode 100644
index 0000000..0287e32
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..e77b2b9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/more-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/more-crlf.fail
new file mode 100644
index 0000000..07f50c3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/more-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/more-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..6994a8d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/more-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/more-lf.fail
new file mode 100644
index 0000000..067a2e8
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/more-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/all-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/all-crlf-utf8bom.fail
new file mode 100644
index 0000000..213f972
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/all-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/all-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/all-crlf.fail
new file mode 100644
index 0000000..851ef6c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/binary-all-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/binary-all-crlf.fail
new file mode 100644
index 0000000..26690f3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/binary-all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-all-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..c6b706d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/few-utf8-chars-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/few-utf8-chars-crlf.fail
new file mode 100644
index 0000000..c23959d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/few-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'few-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/many-utf8-chars-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/many-utf8-chars-crlf.fail
new file mode 100644
index 0000000..2e093c9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/many-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'many-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..3c6cbac
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..43b93db
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/more-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/more-crlf.fail
new file mode 100644
index 0000000..b1a5dc9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/more-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/more-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..13c4e3c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/more-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/more-lf.fail
new file mode 100644
index 0000000..c964c72
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/more-lf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/all-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/all-lf-utf8bom.fail
new file mode 100644
index 0000000..547008e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/all-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/all-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/all-lf.fail
new file mode 100644
index 0000000..d992d47
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/binary-all-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/binary-all-lf.fail
new file mode 100644
index 0000000..180663e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/binary-all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-all-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..f87ce3f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/binary-mixed-lf-cr.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/binary-mixed-lf-cr.fail
new file mode 100644
index 0000000..63eac58
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/binary-mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/few-utf8-chars-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/few-utf8-chars-lf.fail
new file mode 100644
index 0000000..fabf466
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/few-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'few-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/many-utf8-chars-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/many-utf8-chars-lf.fail
new file mode 100644
index 0000000..b874f4b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/many-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'many-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..a353e0e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/mixed-lf-cr.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/mixed-lf-cr.fail
new file mode 100644
index 0000000..0287e32
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/more-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..e77b2b9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/more-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/more-crlf.fail
new file mode 100644
index 0000000..07f50c3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/more-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/more-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/more-lf-utf8bom.fail
new file mode 100644
index 0000000..6994a8d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/more-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/more-lf.fail
new file mode 100644
index 0000000..067a2e8
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/more-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/all-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/all-lf-utf8bom.fail
new file mode 100644
index 0000000..547008e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/all-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/all-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/all-lf.fail
new file mode 100644
index 0000000..d992d47
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-lf.fail
new file mode 100644
index 0000000..fabf466
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'few-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-lf.fail
new file mode 100644
index 0000000..b874f4b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'many-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..e77b2b9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/more-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/more-crlf.fail
new file mode 100644
index 0000000..07f50c3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/more-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/more-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..6994a8d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/more-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/more-lf.fail
new file mode 100644
index 0000000..067a2e8
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/more-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/all-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/all-crlf-utf8bom.fail
new file mode 100644
index 0000000..213f972
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/all-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/all-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/all-crlf.fail
new file mode 100644
index 0000000..851ef6c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/few-utf8-chars-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/few-utf8-chars-crlf.fail
new file mode 100644
index 0000000..c23959d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/few-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'few-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/many-utf8-chars-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/many-utf8-chars-crlf.fail
new file mode 100644
index 0000000..2e093c9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/many-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'many-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..43b93db
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/more-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/more-crlf.fail
new file mode 100644
index 0000000..b1a5dc9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/more-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/more-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..13c4e3c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/more-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/more-lf.fail
new file mode 100644
index 0000000..c964c72
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/more-lf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/all-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/all-lf-utf8bom.fail
new file mode 100644
index 0000000..547008e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/all-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/all-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/all-lf.fail
new file mode 100644
index 0000000..d992d47
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/few-utf8-chars-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/few-utf8-chars-lf.fail
new file mode 100644
index 0000000..fabf466
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/few-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'few-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/many-utf8-chars-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/many-utf8-chars-lf.fail
new file mode 100644
index 0000000..b874f4b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/many-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'many-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/more-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..e77b2b9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/more-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/more-crlf.fail
new file mode 100644
index 0000000..07f50c3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/more-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/more-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/more-lf-utf8bom.fail
new file mode 100644
index 0000000..6994a8d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/more-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/more-lf.fail
new file mode 100644
index 0000000..067a2e8
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/more-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true,text_auto/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/all-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/all-lf-utf8bom.fail
new file mode 100644
index 0000000..547008e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/all-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/all-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/all-lf.fail
new file mode 100644
index 0000000..d992d47
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/few-utf8-chars-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/few-utf8-chars-lf.fail
new file mode 100644
index 0000000..fabf466
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/few-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'few-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/many-utf8-chars-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/many-utf8-chars-lf.fail
new file mode 100644
index 0000000..b874f4b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/many-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'many-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/more-crlf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..e77b2b9
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/more-crlf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/more-crlf.fail
new file mode 100644
index 0000000..07f50c3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/more-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/more-lf-utf8bom.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/more-lf-utf8bom.fail
new file mode 100644
index 0000000..6994a8d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/more-lf.fail b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/more-lf.fail
new file mode 100644
index 0000000..067a2e8
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/more-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf'
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_true/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,-text/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/HEAD b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/branches b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/branches
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/branches
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn,text_auto/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/binary-all-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/binary-all-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/mixed-lf-cr b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/more-crlf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/more-lf b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/zero-byte b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_odb/autocrlf_true,safecrlf_warn/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,-text/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/all-lf
new file mode 100644
index 0000000..f557a02
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..381cfe6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/more-crlf
new file mode 100644
index 0000000..e682c30
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..2e1220c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/more-lf
new file mode 100644
index 0000000..6da5b0c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..3fa59c8
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/all-lf
new file mode 100644
index 0000000..f557a02
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..381cfe6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/more-crlf
new file mode 100644
index 0000000..e682c30
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..2e1220c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/more-lf
new file mode 100644
index 0000000..6da5b0c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..3fa59c8
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/all-lf
new file mode 100644
index 0000000..f557a02
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..381cfe6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false,text_auto/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_false/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,-text/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/all-lf
new file mode 100644
index 0000000..f557a02
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..381cfe6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/more-crlf
new file mode 100644
index 0000000..e682c30
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..2e1220c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/more-lf
new file mode 100644
index 0000000..6da5b0c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..3fa59c8
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/all-lf
new file mode 100644
index 0000000..f557a02
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..381cfe6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/more-crlf
new file mode 100644
index 0000000..e682c30
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..2e1220c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/more-lf
new file mode 100644
index 0000000..6da5b0c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..3fa59c8
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/all-lf
new file mode 100644
index 0000000..f557a02
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..381cfe6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input,text_auto/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_input/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,-text/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/all-lf
new file mode 100644
index 0000000..f557a02
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/all-lf-utf8bom
new file mode 100644
index 0000000..381cfe6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/binary-all-lf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/mixed-lf-cr
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/more-crlf
new file mode 100644
index 0000000..e682c30
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..2e1220c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/more-lf
new file mode 100644
index 0000000..6da5b0c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/more-lf-utf8bom
new file mode 100644
index 0000000..3fa59c8
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/all-lf
new file mode 100644
index 0000000..f557a02
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..381cfe6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/more-crlf
new file mode 100644
index 0000000..e682c30
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..2e1220c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/more-lf
new file mode 100644
index 0000000..6da5b0c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..3fa59c8
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/all-lf
new file mode 100644
index 0000000..f557a02
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..381cfe6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/more-crlf
new file mode 100644
index 0000000..e682c30
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..2e1220c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/more-lf
new file mode 100644
index 0000000..6da5b0c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..3fa59c8
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/all-lf
new file mode 100644
index 0000000..f557a02
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/all-lf-utf8bom
new file mode 100644
index 0000000..381cfe6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/binary-all-lf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/binary-mixed-lf-cr
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/few-utf8-chars-lf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/many-utf8-chars-lf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/mixed-lf-cr
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/more-crlf
new file mode 100644
index 0000000..e682c30
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/more-crlf-utf8bom
new file mode 100644
index 0000000..2e1220c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/more-lf
new file mode 100644
index 0000000..6da5b0c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/more-lf-utf8bom
new file mode 100644
index 0000000..3fa59c8
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/all-lf
new file mode 100644
index 0000000..f557a02
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..381cfe6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/all-lf
new file mode 100644
index 0000000..f557a02
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/all-lf-utf8bom
new file mode 100644
index 0000000..381cfe6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/few-utf8-chars-lf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/many-utf8-chars-lf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true,text_auto/zero-byte
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/all-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/all-lf
new file mode 100644
index 0000000..f557a02
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/all-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/all-lf-utf8bom
new file mode 100644
index 0000000..381cfe6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/binary-all-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/binary-all-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/binary-mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/few-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/few-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/few-utf8-chars-lf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/many-utf8-chars-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/many-utf8-chars-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/many-utf8-chars-lf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/mixed-lf-cr b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/mixed-lf-cr-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/more-crlf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/more-crlf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/more-lf b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/more-lf-utf8bom b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/zero-byte b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/posix_to_workdir/autocrlf_true/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,-text/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false,text_auto/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_false/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,-text/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/all-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/all-lf-utf8bom.fail
new file mode 100644
index 0000000..547008e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/all-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/all-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/all-lf.fail
new file mode 100644
index 0000000..d992d47
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/binary-all-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/binary-all-lf.fail
new file mode 100644
index 0000000..180663e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/binary-all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-all-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..f87ce3f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/binary-mixed-lf-cr.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/binary-mixed-lf-cr.fail
new file mode 100644
index 0000000..63eac58
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/binary-mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/few-utf8-chars-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/few-utf8-chars-lf.fail
new file mode 100644
index 0000000..fabf466
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/few-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'few-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/many-utf8-chars-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/many-utf8-chars-lf.fail
new file mode 100644
index 0000000..b874f4b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/many-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'many-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..a353e0e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/mixed-lf-cr.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/mixed-lf-cr.fail
new file mode 100644
index 0000000..0287e32
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..e77b2b9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/more-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/more-crlf.fail
new file mode 100644
index 0000000..07f50c3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/more-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/more-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..6994a8d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/more-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/more-lf.fail
new file mode 100644
index 0000000..067a2e8
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/more-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/all-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/all-lf-utf8bom.fail
new file mode 100644
index 0000000..547008e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/all-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/all-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/all-lf.fail
new file mode 100644
index 0000000..d992d47
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/binary-all-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/binary-all-lf.fail
new file mode 100644
index 0000000..180663e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/binary-all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-all-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..f87ce3f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/binary-mixed-lf-cr.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/binary-mixed-lf-cr.fail
new file mode 100644
index 0000000..63eac58
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/binary-mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/few-utf8-chars-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/few-utf8-chars-lf.fail
new file mode 100644
index 0000000..fabf466
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/few-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'few-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/many-utf8-chars-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/many-utf8-chars-lf.fail
new file mode 100644
index 0000000..b874f4b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/many-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'many-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..a353e0e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/mixed-lf-cr.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/mixed-lf-cr.fail
new file mode 100644
index 0000000..0287e32
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..e77b2b9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/more-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/more-crlf.fail
new file mode 100644
index 0000000..07f50c3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/more-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/more-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..6994a8d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/more-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/more-lf.fail
new file mode 100644
index 0000000..067a2e8
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/more-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/all-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/all-crlf-utf8bom.fail
new file mode 100644
index 0000000..213f972
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/all-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/all-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/all-crlf.fail
new file mode 100644
index 0000000..851ef6c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/binary-all-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/binary-all-crlf.fail
new file mode 100644
index 0000000..26690f3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/binary-all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-all-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..c6b706d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/few-utf8-chars-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/few-utf8-chars-crlf.fail
new file mode 100644
index 0000000..c23959d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/few-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'few-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/many-utf8-chars-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/many-utf8-chars-crlf.fail
new file mode 100644
index 0000000..2e093c9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/many-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'many-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..3c6cbac
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..43b93db
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/more-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/more-crlf.fail
new file mode 100644
index 0000000..b1a5dc9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/more-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/more-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..13c4e3c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/more-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/more-lf.fail
new file mode 100644
index 0000000..c964c72
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/more-lf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/all-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/all-lf-utf8bom.fail
new file mode 100644
index 0000000..547008e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/all-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/all-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/all-lf.fail
new file mode 100644
index 0000000..d992d47
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/binary-all-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/binary-all-lf.fail
new file mode 100644
index 0000000..180663e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/binary-all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-all-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..f87ce3f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr.fail
new file mode 100644
index 0000000..63eac58
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/few-utf8-chars-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/few-utf8-chars-lf.fail
new file mode 100644
index 0000000..fabf466
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/few-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'few-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/many-utf8-chars-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/many-utf8-chars-lf.fail
new file mode 100644
index 0000000..b874f4b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/many-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'many-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..a353e0e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/mixed-lf-cr.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/mixed-lf-cr.fail
new file mode 100644
index 0000000..0287e32
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..e77b2b9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/more-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/more-crlf.fail
new file mode 100644
index 0000000..07f50c3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/more-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/more-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..6994a8d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/more-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/more-lf.fail
new file mode 100644
index 0000000..067a2e8
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/more-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/all-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/all-crlf-utf8bom.fail
new file mode 100644
index 0000000..213f972
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/all-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/all-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/all-crlf.fail
new file mode 100644
index 0000000..851ef6c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/binary-all-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/binary-all-crlf.fail
new file mode 100644
index 0000000..26690f3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/binary-all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-all-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..c6b706d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/few-utf8-chars-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/few-utf8-chars-crlf.fail
new file mode 100644
index 0000000..c23959d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/few-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'few-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/many-utf8-chars-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/many-utf8-chars-crlf.fail
new file mode 100644
index 0000000..2e093c9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/many-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'many-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..3c6cbac
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..43b93db
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/more-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/more-crlf.fail
new file mode 100644
index 0000000..b1a5dc9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/more-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/more-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..13c4e3c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/more-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/more-lf.fail
new file mode 100644
index 0000000..c964c72
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/more-lf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/all-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/all-lf-utf8bom.fail
new file mode 100644
index 0000000..547008e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/all-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/all-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/all-lf.fail
new file mode 100644
index 0000000..d992d47
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/binary-all-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/binary-all-lf.fail
new file mode 100644
index 0000000..180663e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/binary-all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-all-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..f87ce3f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/binary-mixed-lf-cr.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/binary-mixed-lf-cr.fail
new file mode 100644
index 0000000..63eac58
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/binary-mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/few-utf8-chars-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/few-utf8-chars-lf.fail
new file mode 100644
index 0000000..fabf466
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/few-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'few-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/many-utf8-chars-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/many-utf8-chars-lf.fail
new file mode 100644
index 0000000..b874f4b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/many-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'many-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..a353e0e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/mixed-lf-cr.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/mixed-lf-cr.fail
new file mode 100644
index 0000000..0287e32
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/more-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..e77b2b9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/more-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/more-crlf.fail
new file mode 100644
index 0000000..07f50c3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/more-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/more-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/more-lf-utf8bom.fail
new file mode 100644
index 0000000..6994a8d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/more-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/more-lf.fail
new file mode 100644
index 0000000..067a2e8
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/more-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/all-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/all-lf-utf8bom.fail
new file mode 100644
index 0000000..547008e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/all-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/all-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/all-lf.fail
new file mode 100644
index 0000000..d992d47
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-lf.fail
new file mode 100644
index 0000000..fabf466
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'few-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-lf.fail
new file mode 100644
index 0000000..b874f4b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'many-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..e77b2b9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/more-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/more-crlf.fail
new file mode 100644
index 0000000..07f50c3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/more-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/more-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..6994a8d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/more-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/more-lf.fail
new file mode 100644
index 0000000..067a2e8
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/more-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/all-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/all-crlf-utf8bom.fail
new file mode 100644
index 0000000..213f972
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/all-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/all-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/all-crlf.fail
new file mode 100644
index 0000000..851ef6c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/few-utf8-chars-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/few-utf8-chars-crlf.fail
new file mode 100644
index 0000000..c23959d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/few-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'few-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/many-utf8-chars-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/many-utf8-chars-crlf.fail
new file mode 100644
index 0000000..2e093c9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/many-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'many-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..43b93db
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/more-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/more-crlf.fail
new file mode 100644
index 0000000..b1a5dc9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/more-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/more-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..13c4e3c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/more-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/more-lf.fail
new file mode 100644
index 0000000..c964c72
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/more-lf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/all-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/all-lf-utf8bom.fail
new file mode 100644
index 0000000..547008e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/all-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/all-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/all-lf.fail
new file mode 100644
index 0000000..d992d47
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/few-utf8-chars-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/few-utf8-chars-lf.fail
new file mode 100644
index 0000000..fabf466
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/few-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'few-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/many-utf8-chars-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/many-utf8-chars-lf.fail
new file mode 100644
index 0000000..b874f4b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/many-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'many-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/more-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..e77b2b9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/more-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/more-crlf.fail
new file mode 100644
index 0000000..07f50c3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/more-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/more-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/more-lf-utf8bom.fail
new file mode 100644
index 0000000..6994a8d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/more-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/more-lf.fail
new file mode 100644
index 0000000..067a2e8
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/more-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true,text_auto/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_true/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,-text/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn,text_auto/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_false,safecrlf_warn/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,-text/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false,text_auto/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_false/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,-text/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/all-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/all-crlf-utf8bom.fail
new file mode 100644
index 0000000..213f972
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/all-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/all-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/all-crlf.fail
new file mode 100644
index 0000000..851ef6c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/binary-all-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/binary-all-crlf.fail
new file mode 100644
index 0000000..26690f3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/binary-all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-all-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..c6b706d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/few-utf8-chars-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/few-utf8-chars-crlf.fail
new file mode 100644
index 0000000..c23959d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/few-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'few-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/many-utf8-chars-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/many-utf8-chars-crlf.fail
new file mode 100644
index 0000000..2e093c9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/many-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'many-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..3c6cbac
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..43b93db
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/more-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/more-crlf.fail
new file mode 100644
index 0000000..b1a5dc9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/more-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/more-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..13c4e3c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/more-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/more-lf.fail
new file mode 100644
index 0000000..c964c72
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/more-lf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/all-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/all-lf-utf8bom.fail
new file mode 100644
index 0000000..547008e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/all-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/all-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/all-lf.fail
new file mode 100644
index 0000000..d992d47
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/binary-all-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/binary-all-lf.fail
new file mode 100644
index 0000000..180663e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/binary-all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-all-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..f87ce3f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/binary-mixed-lf-cr.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/binary-mixed-lf-cr.fail
new file mode 100644
index 0000000..63eac58
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/binary-mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/few-utf8-chars-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/few-utf8-chars-lf.fail
new file mode 100644
index 0000000..fabf466
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/few-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'few-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/many-utf8-chars-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/many-utf8-chars-lf.fail
new file mode 100644
index 0000000..b874f4b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/many-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'many-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..a353e0e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/mixed-lf-cr.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/mixed-lf-cr.fail
new file mode 100644
index 0000000..0287e32
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..e77b2b9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/more-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/more-crlf.fail
new file mode 100644
index 0000000..07f50c3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/more-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/more-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..6994a8d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/more-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/more-lf.fail
new file mode 100644
index 0000000..067a2e8
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/more-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/all-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/all-crlf-utf8bom.fail
new file mode 100644
index 0000000..213f972
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/all-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/all-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/all-crlf.fail
new file mode 100644
index 0000000..851ef6c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/binary-all-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/binary-all-crlf.fail
new file mode 100644
index 0000000..26690f3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/binary-all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-all-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..c6b706d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/few-utf8-chars-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/few-utf8-chars-crlf.fail
new file mode 100644
index 0000000..c23959d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/few-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'few-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/many-utf8-chars-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/many-utf8-chars-crlf.fail
new file mode 100644
index 0000000..2e093c9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/many-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'many-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..3c6cbac
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..43b93db
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/more-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/more-crlf.fail
new file mode 100644
index 0000000..b1a5dc9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/more-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/more-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..13c4e3c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/more-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/more-lf.fail
new file mode 100644
index 0000000..c964c72
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/more-lf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/all-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/all-lf-utf8bom.fail
new file mode 100644
index 0000000..547008e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/all-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/all-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/all-lf.fail
new file mode 100644
index 0000000..d992d47
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/binary-all-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/binary-all-lf.fail
new file mode 100644
index 0000000..180663e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/binary-all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-all-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..f87ce3f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr.fail
new file mode 100644
index 0000000..63eac58
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/few-utf8-chars-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/few-utf8-chars-lf.fail
new file mode 100644
index 0000000..fabf466
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/few-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'few-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/many-utf8-chars-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/many-utf8-chars-lf.fail
new file mode 100644
index 0000000..b874f4b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/many-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'many-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..a353e0e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/mixed-lf-cr.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/mixed-lf-cr.fail
new file mode 100644
index 0000000..0287e32
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..e77b2b9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/more-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/more-crlf.fail
new file mode 100644
index 0000000..07f50c3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/more-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/more-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..6994a8d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/more-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/more-lf.fail
new file mode 100644
index 0000000..067a2e8
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/more-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/all-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/all-crlf-utf8bom.fail
new file mode 100644
index 0000000..213f972
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/all-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/all-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/all-crlf.fail
new file mode 100644
index 0000000..851ef6c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/binary-all-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/binary-all-crlf.fail
new file mode 100644
index 0000000..26690f3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/binary-all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-all-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..c6b706d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/few-utf8-chars-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/few-utf8-chars-crlf.fail
new file mode 100644
index 0000000..c23959d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/few-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'few-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/many-utf8-chars-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/many-utf8-chars-crlf.fail
new file mode 100644
index 0000000..2e093c9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/many-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'many-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..3c6cbac
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..43b93db
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/more-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/more-crlf.fail
new file mode 100644
index 0000000..b1a5dc9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/more-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/more-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..13c4e3c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/more-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/more-lf.fail
new file mode 100644
index 0000000..c964c72
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/more-lf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/all-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/all-crlf-utf8bom.fail
new file mode 100644
index 0000000..213f972
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/all-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/all-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/all-crlf.fail
new file mode 100644
index 0000000..851ef6c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/binary-all-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/binary-all-crlf.fail
new file mode 100644
index 0000000..26690f3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/binary-all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-all-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..c6b706d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/few-utf8-chars-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/few-utf8-chars-crlf.fail
new file mode 100644
index 0000000..c23959d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/few-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'few-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/many-utf8-chars-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/many-utf8-chars-crlf.fail
new file mode 100644
index 0000000..2e093c9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/many-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'many-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..3c6cbac
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/more-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..43b93db
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/more-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/more-crlf.fail
new file mode 100644
index 0000000..b1a5dc9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/more-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/more-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/more-lf-utf8bom.fail
new file mode 100644
index 0000000..13c4e3c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/more-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/more-lf.fail
new file mode 100644
index 0000000..c964c72
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/more-lf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/all-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/all-lf-utf8bom.fail
new file mode 100644
index 0000000..547008e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/all-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/all-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/all-lf.fail
new file mode 100644
index 0000000..d992d47
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-lf.fail
new file mode 100644
index 0000000..fabf466
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'few-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-lf.fail
new file mode 100644
index 0000000..b874f4b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'many-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..e77b2b9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/more-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/more-crlf.fail
new file mode 100644
index 0000000..07f50c3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/more-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/more-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..6994a8d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/more-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/more-lf.fail
new file mode 100644
index 0000000..067a2e8
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/more-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/all-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/all-crlf-utf8bom.fail
new file mode 100644
index 0000000..213f972
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/all-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/all-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/all-crlf.fail
new file mode 100644
index 0000000..851ef6c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/few-utf8-chars-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/few-utf8-chars-crlf.fail
new file mode 100644
index 0000000..c23959d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/few-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'few-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/many-utf8-chars-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/many-utf8-chars-crlf.fail
new file mode 100644
index 0000000..2e093c9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/many-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'many-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..43b93db
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/more-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/more-crlf.fail
new file mode 100644
index 0000000..b1a5dc9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/more-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/more-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..13c4e3c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/more-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/more-lf.fail
new file mode 100644
index 0000000..c964c72
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/more-lf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/all-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/all-crlf-utf8bom.fail
new file mode 100644
index 0000000..213f972
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/all-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/all-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/all-crlf.fail
new file mode 100644
index 0000000..851ef6c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/few-utf8-chars-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/few-utf8-chars-crlf.fail
new file mode 100644
index 0000000..c23959d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/few-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'few-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/many-utf8-chars-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/many-utf8-chars-crlf.fail
new file mode 100644
index 0000000..2e093c9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/many-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'many-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/more-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..43b93db
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/more-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/more-crlf.fail
new file mode 100644
index 0000000..b1a5dc9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/more-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/more-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/more-lf-utf8bom.fail
new file mode 100644
index 0000000..13c4e3c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/more-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/more-lf.fail
new file mode 100644
index 0000000..c964c72
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/more-lf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true,text_auto/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/all-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/all-crlf-utf8bom.fail
new file mode 100644
index 0000000..213f972
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/all-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/all-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/all-crlf.fail
new file mode 100644
index 0000000..851ef6c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/few-utf8-chars-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/few-utf8-chars-crlf.fail
new file mode 100644
index 0000000..c23959d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/few-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'few-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/many-utf8-chars-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/many-utf8-chars-crlf.fail
new file mode 100644
index 0000000..2e093c9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/many-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'many-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/more-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..43b93db
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/more-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/more-crlf.fail
new file mode 100644
index 0000000..b1a5dc9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/more-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/more-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/more-lf-utf8bom.fail
new file mode 100644
index 0000000..13c4e3c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/more-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/more-lf.fail
new file mode 100644
index 0000000..c964c72
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/more-lf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_true/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,-text/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn,text_auto/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_input,safecrlf_warn/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,-text/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false,text_auto/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_false/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,-text/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/all-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/all-lf-utf8bom.fail
new file mode 100644
index 0000000..547008e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/all-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/all-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/all-lf.fail
new file mode 100644
index 0000000..d992d47
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/binary-all-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/binary-all-lf.fail
new file mode 100644
index 0000000..180663e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/binary-all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-all-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..f87ce3f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/binary-mixed-lf-cr.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/binary-mixed-lf-cr.fail
new file mode 100644
index 0000000..63eac58
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/binary-mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/few-utf8-chars-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/few-utf8-chars-lf.fail
new file mode 100644
index 0000000..fabf466
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/few-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'few-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/many-utf8-chars-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/many-utf8-chars-lf.fail
new file mode 100644
index 0000000..b874f4b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/many-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'many-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..a353e0e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/mixed-lf-cr.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/mixed-lf-cr.fail
new file mode 100644
index 0000000..0287e32
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..e77b2b9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/more-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/more-crlf.fail
new file mode 100644
index 0000000..07f50c3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/more-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/more-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..6994a8d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/more-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/more-lf.fail
new file mode 100644
index 0000000..067a2e8
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/more-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/all-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/all-lf-utf8bom.fail
new file mode 100644
index 0000000..547008e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/all-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/all-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/all-lf.fail
new file mode 100644
index 0000000..d992d47
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/binary-all-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/binary-all-lf.fail
new file mode 100644
index 0000000..180663e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/binary-all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-all-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..f87ce3f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/binary-mixed-lf-cr.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/binary-mixed-lf-cr.fail
new file mode 100644
index 0000000..63eac58
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/binary-mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/few-utf8-chars-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/few-utf8-chars-lf.fail
new file mode 100644
index 0000000..fabf466
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/few-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'few-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/many-utf8-chars-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/many-utf8-chars-lf.fail
new file mode 100644
index 0000000..b874f4b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/many-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'many-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..a353e0e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/mixed-lf-cr.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/mixed-lf-cr.fail
new file mode 100644
index 0000000..0287e32
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..e77b2b9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/more-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/more-crlf.fail
new file mode 100644
index 0000000..07f50c3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/more-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/more-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..6994a8d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/more-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/more-lf.fail
new file mode 100644
index 0000000..067a2e8
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/more-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/all-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/all-crlf-utf8bom.fail
new file mode 100644
index 0000000..213f972
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/all-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/all-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/all-crlf.fail
new file mode 100644
index 0000000..851ef6c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/binary-all-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/binary-all-crlf.fail
new file mode 100644
index 0000000..26690f3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/binary-all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-all-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..c6b706d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/few-utf8-chars-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/few-utf8-chars-crlf.fail
new file mode 100644
index 0000000..c23959d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/few-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'few-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/many-utf8-chars-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/many-utf8-chars-crlf.fail
new file mode 100644
index 0000000..2e093c9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/many-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'many-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..3c6cbac
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..43b93db
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/more-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/more-crlf.fail
new file mode 100644
index 0000000..b1a5dc9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/more-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/more-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..13c4e3c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/more-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/more-lf.fail
new file mode 100644
index 0000000..c964c72
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/more-lf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/all-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/all-lf-utf8bom.fail
new file mode 100644
index 0000000..547008e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/all-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/all-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/all-lf.fail
new file mode 100644
index 0000000..d992d47
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/binary-all-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/binary-all-lf.fail
new file mode 100644
index 0000000..180663e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/binary-all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-all-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..f87ce3f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr.fail
new file mode 100644
index 0000000..63eac58
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/binary-mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/few-utf8-chars-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/few-utf8-chars-lf.fail
new file mode 100644
index 0000000..fabf466
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/few-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'few-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/many-utf8-chars-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/many-utf8-chars-lf.fail
new file mode 100644
index 0000000..b874f4b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/many-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'many-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..a353e0e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/mixed-lf-cr.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/mixed-lf-cr.fail
new file mode 100644
index 0000000..0287e32
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..e77b2b9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/more-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/more-crlf.fail
new file mode 100644
index 0000000..07f50c3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/more-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/more-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..6994a8d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/more-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/more-lf.fail
new file mode 100644
index 0000000..067a2e8
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/more-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/all-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/all-crlf-utf8bom.fail
new file mode 100644
index 0000000..213f972
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/all-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/all-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/all-crlf.fail
new file mode 100644
index 0000000..851ef6c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/binary-all-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/binary-all-crlf.fail
new file mode 100644
index 0000000..26690f3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/binary-all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-all-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..c6b706d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/few-utf8-chars-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/few-utf8-chars-crlf.fail
new file mode 100644
index 0000000..c23959d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/few-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'few-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/many-utf8-chars-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/many-utf8-chars-crlf.fail
new file mode 100644
index 0000000..2e093c9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/many-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'many-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..3c6cbac
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..43b93db
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/more-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/more-crlf.fail
new file mode 100644
index 0000000..b1a5dc9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/more-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/more-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..13c4e3c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/more-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/more-lf.fail
new file mode 100644
index 0000000..c964c72
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/more-lf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/all-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/all-lf-utf8bom.fail
new file mode 100644
index 0000000..547008e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/all-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/all-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/all-lf.fail
new file mode 100644
index 0000000..d992d47
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/binary-all-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/binary-all-lf.fail
new file mode 100644
index 0000000..180663e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/binary-all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-all-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/binary-mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/binary-mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..f87ce3f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/binary-mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/binary-mixed-lf-cr.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/binary-mixed-lf-cr.fail
new file mode 100644
index 0000000..63eac58
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/binary-mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'binary-mixed-lf-cr'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/few-utf8-chars-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/few-utf8-chars-lf.fail
new file mode 100644
index 0000000..fabf466
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/few-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'few-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/many-utf8-chars-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/many-utf8-chars-lf.fail
new file mode 100644
index 0000000..b874f4b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/many-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'many-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/mixed-lf-cr-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/mixed-lf-cr-crlf.fail
new file mode 100644
index 0000000..a353e0e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/mixed-lf-cr-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/mixed-lf-cr.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/mixed-lf-cr.fail
new file mode 100644
index 0000000..0287e32
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/mixed-lf-cr.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'mixed-lf-cr'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/more-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..e77b2b9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/more-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/more-crlf.fail
new file mode 100644
index 0000000..07f50c3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/more-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/more-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/more-lf-utf8bom.fail
new file mode 100644
index 0000000..6994a8d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/more-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/more-lf.fail
new file mode 100644
index 0000000..067a2e8
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/more-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/all-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/all-lf-utf8bom.fail
new file mode 100644
index 0000000..547008e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/all-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/all-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/all-lf.fail
new file mode 100644
index 0000000..d992d47
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-lf.fail
new file mode 100644
index 0000000..fabf466
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/few-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'few-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-lf.fail
new file mode 100644
index 0000000..b874f4b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/many-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'many-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..e77b2b9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/more-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/more-crlf.fail
new file mode 100644
index 0000000..07f50c3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/more-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/more-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..6994a8d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/more-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/more-lf.fail
new file mode 100644
index 0000000..067a2e8
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/more-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/all-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/all-crlf-utf8bom.fail
new file mode 100644
index 0000000..213f972
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/all-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/all-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/all-crlf.fail
new file mode 100644
index 0000000..851ef6c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/all-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'all-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/few-utf8-chars-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/few-utf8-chars-crlf.fail
new file mode 100644
index 0000000..c23959d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/few-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'few-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/many-utf8-chars-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/many-utf8-chars-crlf.fail
new file mode 100644
index 0000000..2e093c9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/many-utf8-chars-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'many-utf8-chars-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/more-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..43b93db
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/more-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/more-crlf.fail
new file mode 100644
index 0000000..b1a5dc9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/more-crlf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/more-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/more-lf-utf8bom.fail
new file mode 100644
index 0000000..13c4e3c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/more-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/more-lf.fail
new file mode 100644
index 0000000..c964c72
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/more-lf.fail
@@ -0,0 +1 @@
+CRLF would be replaced by LF in 'more-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/all-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/all-lf-utf8bom.fail
new file mode 100644
index 0000000..547008e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/all-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/all-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/all-lf.fail
new file mode 100644
index 0000000..d992d47
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/few-utf8-chars-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/few-utf8-chars-lf.fail
new file mode 100644
index 0000000..fabf466
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/few-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'few-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/many-utf8-chars-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/many-utf8-chars-lf.fail
new file mode 100644
index 0000000..b874f4b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/many-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'many-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/more-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..e77b2b9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/more-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/more-crlf.fail
new file mode 100644
index 0000000..07f50c3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/more-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/more-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/more-lf-utf8bom.fail
new file mode 100644
index 0000000..6994a8d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/more-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/more-lf.fail
new file mode 100644
index 0000000..067a2e8
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/more-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true,text_auto/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/all-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/all-lf-utf8bom.fail
new file mode 100644
index 0000000..547008e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/all-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/all-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/all-lf.fail
new file mode 100644
index 0000000..d992d47
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/all-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'all-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/few-utf8-chars-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/few-utf8-chars-lf.fail
new file mode 100644
index 0000000..fabf466
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/few-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'few-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/many-utf8-chars-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/many-utf8-chars-lf.fail
new file mode 100644
index 0000000..b874f4b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/many-utf8-chars-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'many-utf8-chars-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/more-crlf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/more-crlf-utf8bom.fail
new file mode 100644
index 0000000..e77b2b9
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/more-crlf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/more-crlf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/more-crlf.fail
new file mode 100644
index 0000000..07f50c3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/more-crlf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-crlf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/more-lf-utf8bom.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/more-lf-utf8bom.fail
new file mode 100644
index 0000000..6994a8d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/more-lf-utf8bom.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf-utf8bom'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/more-lf.fail b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/more-lf.fail
new file mode 100644
index 0000000..067a2e8
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/more-lf.fail
@@ -0,0 +1 @@
+LF would be replaced by CRLF in 'more-lf'
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_true/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,-text/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/binary-all-crlf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn,text_auto/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/all-crlf
new file mode 100644
index 0000000..26e1a74
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/all-crlf-utf8bom
new file mode 100644
index 0000000..32c6f22
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/binary-all-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/binary-all-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/few-utf8-chars-crlf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/many-utf8-chars-crlf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/mixed-lf-cr b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/more-crlf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/more-crlf
new file mode 100644
index 0000000..37bb7fa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/more-crlf-utf8bom
new file mode 100644
index 0000000..16001a1
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/more-lf b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/more-lf
new file mode 100644
index 0000000..2cda6f2
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/more-lf-utf8bom
new file mode 100644
index 0000000..dc7d82a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/zero-byte b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_odb/autocrlf_true,safecrlf_warn/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,-text/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/all-lf
new file mode 100644
index 0000000..f557a02
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/all-lf-utf8bom
new file mode 100644
index 0000000..381cfe6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/binary-all-lf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/mixed-lf-cr
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/more-crlf
new file mode 100644
index 0000000..e682c30
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..2e1220c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/more-lf
new file mode 100644
index 0000000..6da5b0c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/more-lf-utf8bom
new file mode 100644
index 0000000..3fa59c8
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/all-lf
new file mode 100644
index 0000000..f557a02
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..381cfe6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/more-crlf
new file mode 100644
index 0000000..e682c30
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..2e1220c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/more-lf
new file mode 100644
index 0000000..6da5b0c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..3fa59c8
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/all-lf
new file mode 100644
index 0000000..f557a02
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..381cfe6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/more-crlf
new file mode 100644
index 0000000..e682c30
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..2e1220c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/more-lf
new file mode 100644
index 0000000..6da5b0c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..3fa59c8
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/all-lf
new file mode 100644
index 0000000..f557a02
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/all-lf-utf8bom
new file mode 100644
index 0000000..381cfe6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/binary-all-lf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/binary-mixed-lf-cr
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/few-utf8-chars-lf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/many-utf8-chars-lf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/mixed-lf-cr
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/more-crlf
new file mode 100644
index 0000000..e682c30
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/more-crlf-utf8bom
new file mode 100644
index 0000000..2e1220c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/more-lf
new file mode 100644
index 0000000..6da5b0c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/more-lf-utf8bom
new file mode 100644
index 0000000..3fa59c8
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/all-lf
new file mode 100644
index 0000000..f557a02
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..381cfe6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/all-lf
new file mode 100644
index 0000000..f557a02
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/all-lf-utf8bom
new file mode 100644
index 0000000..381cfe6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/few-utf8-chars-lf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/many-utf8-chars-lf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false,text_auto/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_false/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,-text/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/all-lf
new file mode 100644
index 0000000..f557a02
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..381cfe6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/more-crlf
new file mode 100644
index 0000000..e682c30
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..2e1220c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/more-lf
new file mode 100644
index 0000000..6da5b0c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..3fa59c8
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/all-lf
new file mode 100644
index 0000000..f557a02
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..381cfe6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/more-crlf
new file mode 100644
index 0000000..e682c30
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..2e1220c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/more-lf
new file mode 100644
index 0000000..6da5b0c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..3fa59c8
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/all-lf
new file mode 100644
index 0000000..f557a02
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..381cfe6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input,text_auto/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_input/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,-text/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/all-lf
new file mode 100644
index 0000000..f557a02
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/all-lf-utf8bom
new file mode 100644
index 0000000..381cfe6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/binary-all-lf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/mixed-lf-cr
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/more-crlf
new file mode 100644
index 0000000..e682c30
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..2e1220c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/more-lf
new file mode 100644
index 0000000..6da5b0c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/more-lf-utf8bom
new file mode 100644
index 0000000..3fa59c8
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/all-lf
new file mode 100644
index 0000000..f557a02
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..381cfe6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/more-crlf
new file mode 100644
index 0000000..e682c30
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..2e1220c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/more-lf
new file mode 100644
index 0000000..6da5b0c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..3fa59c8
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/all-lf
new file mode 100644
index 0000000..f557a02
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..381cfe6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/more-crlf
new file mode 100644
index 0000000..e682c30
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..2e1220c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/more-lf
new file mode 100644
index 0000000..6da5b0c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..3fa59c8
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/all-lf
new file mode 100644
index 0000000..f557a02
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/all-lf-utf8bom
new file mode 100644
index 0000000..381cfe6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/binary-all-lf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/binary-mixed-lf-cr
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..fd2abfa
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/few-utf8-chars-lf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/many-utf8-chars-lf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/mixed-lf-cr
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/mixed-lf-cr-crlf
new file mode 100644
index 0000000..df8c0f6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/more-crlf
new file mode 100644
index 0000000..e682c30
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/more-crlf-utf8bom
new file mode 100644
index 0000000..2e1220c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/more-lf
new file mode 100644
index 0000000..6da5b0c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/more-lf-utf8bom
new file mode 100644
index 0000000..3fa59c8
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/all-lf
new file mode 100644
index 0000000..f557a02
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/all-lf-utf8bom
new file mode 100644
index 0000000..381cfe6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/few-utf8-chars-lf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/many-utf8-chars-lf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_crlf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/all-lf
new file mode 100644
index 0000000..799770d
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/all-lf-utf8bom
new file mode 100644
index 0000000..7cce67e
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/few-utf8-chars-lf
new file mode 100644
index 0000000..f4d25b7
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/many-utf8-chars-lf
new file mode 100644
index 0000000..9a6c353
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto,eol_lf/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/all-lf
new file mode 100644
index 0000000..f557a02
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/all-lf-utf8bom
new file mode 100644
index 0000000..381cfe6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/few-utf8-chars-lf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/many-utf8-chars-lf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true,text_auto/zero-byte
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/all-crlf
new file mode 100644
index 0000000..a9a2e89
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/all-crlf
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/all-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/all-crlf-utf8bom
new file mode 100644
index 0000000..0aa76e4
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/all-crlf-utf8bom
@@ -0,0 +1,4 @@
+crlf
+crlf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/all-lf
new file mode 100644
index 0000000..f557a02
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/all-lf
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/all-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/all-lf-utf8bom
new file mode 100644
index 0000000..381cfe6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/all-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+lf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/binary-all-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/binary-all-crlf
new file mode 100644
index 0000000..8534075
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/binary-all-crlf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/binary-all-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/binary-all-lf
new file mode 100644
index 0000000..af6fcf6
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/binary-all-lf
@@ -0,0 +1,4 @@
+one
+two
+three
+four
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/binary-mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/binary-mixed-lf-cr
new file mode 100644
index 0000000..203555c
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/binary-mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/binary-mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/binary-mixed-lf-cr-crlf
new file mode 100644
index 0000000..aaf083a
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/binary-mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/few-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/few-utf8-chars-crlf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/few-utf8-chars-crlf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/few-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/few-utf8-chars-lf
new file mode 100644
index 0000000..0e05288
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/few-utf8-chars-lf
@@ -0,0 +1,22 @@
+âš½The rest is ASCII01.
+The rest is ASCII02.
+The rest is ASCII03.
+The rest is ASCII04.
+The rest is ASCII05.
+The rest is ASCII06.
+The rest is ASCII07.
+The rest is ASCII08.
+The rest is ASCII09.
+The rest is ASCII10.
+The rest is ASCII11.
+The rest is ASCII12.
+The rest is ASCII13.
+The rest is ASCII14.
+The rest is ASCII15.
+The rest is ASCII16.
+The rest is ASCII17.
+The rest is ASCII18.
+The rest is ASCII19.
+The rest is ASCII20.
+The rest is ASCII21.
+The rest is ASCII22.
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/many-utf8-chars-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/many-utf8-chars-crlf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/many-utf8-chars-crlf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/many-utf8-chars-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/many-utf8-chars-lf
new file mode 100644
index 0000000..cd574f5
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/many-utf8-chars-lf
@@ -0,0 +1,4 @@
+Lets sing!
+♫♪♬♩
+Eat food
+ðŸ…ðŸ•
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/mixed-lf-cr b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/mixed-lf-cr
new file mode 100644
index 0000000..d11e7ef
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/mixed-lf-cr
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/mixed-lf-cr-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/mixed-lf-cr-crlf
new file mode 100644
index 0000000..417786f
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/mixed-lf-cr-crlf
@@ -0,0 +1,3 @@
+one
+two three
+four \ No newline at end of file
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/more-crlf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/more-crlf
new file mode 100644
index 0000000..0ff5a53
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/more-crlf
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/more-crlf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/more-crlf-utf8bom
new file mode 100644
index 0000000..ea030d3
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/more-crlf-utf8bom
@@ -0,0 +1,5 @@
+crlf
+crlf
+lf
+crlf
+crlf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/more-lf b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/more-lf
new file mode 100644
index 0000000..04de00b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/more-lf
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/more-lf-utf8bom b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/more-lf-utf8bom
new file mode 100644
index 0000000..dc88e3b
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/more-lf-utf8bom
@@ -0,0 +1,5 @@
+lf
+lf
+crlf
+lf
+lf
diff --git a/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/zero-byte b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/zero-byte
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/crlf_data/windows_to_workdir/autocrlf_true/zero-byte
diff --git a/tests/resources/deprecated-mode.git/HEAD b/tests/resources/deprecated-mode.git/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/deprecated-mode.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/deprecated-mode.git/config b/tests/resources/deprecated-mode.git/config
new file mode 100644
index 0000000..f57351f
--- /dev/null
+++ b/tests/resources/deprecated-mode.git/config
@@ -0,0 +1,6 @@
+[core]
+ bare = true
+ repositoryformatversion = 0
+ filemode = false
+ logallrefupdates = true
+ ignorecase = true
diff --git a/tests/resources/deprecated-mode.git/description b/tests/resources/deprecated-mode.git/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/deprecated-mode.git/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/deprecated-mode.git/index b/tests/resources/deprecated-mode.git/index
new file mode 100644
index 0000000..6827406
--- /dev/null
+++ b/tests/resources/deprecated-mode.git/index
Binary files differ
diff --git a/tests/resources/deprecated-mode.git/info/exclude b/tests/resources/deprecated-mode.git/info/exclude
new file mode 100644
index 0000000..6d05881
--- /dev/null
+++ b/tests/resources/deprecated-mode.git/info/exclude
@@ -0,0 +1,2 @@
+# File patterns to ignore; see `git help ignore` for more information.
+# Lines that start with '#' are comments.
diff --git a/tests/resources/deprecated-mode.git/objects/06/262edc257418e9987caf999f9a7a3e1547adff b/tests/resources/deprecated-mode.git/objects/06/262edc257418e9987caf999f9a7a3e1547adff
new file mode 100644
index 0000000..a030cf7
--- /dev/null
+++ b/tests/resources/deprecated-mode.git/objects/06/262edc257418e9987caf999f9a7a3e1547adff
Binary files differ
diff --git a/tests/resources/deprecated-mode.git/objects/08/10fb7818088ff5ac41ee49199b51473b1bd6c7 b/tests/resources/deprecated-mode.git/objects/08/10fb7818088ff5ac41ee49199b51473b1bd6c7
new file mode 100644
index 0000000..52d5693
--- /dev/null
+++ b/tests/resources/deprecated-mode.git/objects/08/10fb7818088ff5ac41ee49199b51473b1bd6c7
Binary files differ
diff --git a/tests/resources/deprecated-mode.git/objects/1b/05fdaa881ee45b48cbaa5e9b037d667a47745e b/tests/resources/deprecated-mode.git/objects/1b/05fdaa881ee45b48cbaa5e9b037d667a47745e
new file mode 100644
index 0000000..ae7765a
--- /dev/null
+++ b/tests/resources/deprecated-mode.git/objects/1b/05fdaa881ee45b48cbaa5e9b037d667a47745e
Binary files differ
diff --git a/tests/resources/deprecated-mode.git/objects/3d/0970ec547fc41ef8a5882dde99c6adce65b021 b/tests/resources/deprecated-mode.git/objects/3d/0970ec547fc41ef8a5882dde99c6adce65b021
new file mode 100644
index 0000000..595283e
--- /dev/null
+++ b/tests/resources/deprecated-mode.git/objects/3d/0970ec547fc41ef8a5882dde99c6adce65b021
Binary files differ
diff --git a/tests/resources/deprecated-mode.git/refs/heads/master b/tests/resources/deprecated-mode.git/refs/heads/master
new file mode 100644
index 0000000..1e106df
--- /dev/null
+++ b/tests/resources/deprecated-mode.git/refs/heads/master
@@ -0,0 +1 @@
+06262edc257418e9987caf999f9a7a3e1547adff
diff --git a/tests/resources/describe/.gitted/HEAD b/tests/resources/describe/.gitted/HEAD
new file mode 100644
index 0000000..cb43805
--- /dev/null
+++ b/tests/resources/describe/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/describe/.gitted/config b/tests/resources/describe/.gitted/config
new file mode 100644
index 0000000..454e576
--- /dev/null
+++ b/tests/resources/describe/.gitted/config
@@ -0,0 +1,8 @@
+[core]
+ repositoryformatversion = 0
+ filemode = false
+ bare = false
+ logallrefupdates = true
+ symlinks = false
+ ignorecase = true
+ hideDotFiles = dotGitOnly
diff --git a/tests/resources/describe/.gitted/index b/tests/resources/describe/.gitted/index
new file mode 100644
index 0000000..f5f35e2
--- /dev/null
+++ b/tests/resources/describe/.gitted/index
Binary files differ
diff --git a/tests/resources/describe/.gitted/logs/HEAD b/tests/resources/describe/.gitted/logs/HEAD
new file mode 100644
index 0000000..fc49c6f
--- /dev/null
+++ b/tests/resources/describe/.gitted/logs/HEAD
@@ -0,0 +1,14 @@
+0000000000000000000000000000000000000000 108b485d8268ea595df8ffea74f0f4b186577d32 nulltoken <emeric.fermas@gmail.com> 1380209394 +0200 commit (initial): initial
+108b485d8268ea595df8ffea74f0f4b186577d32 4d6558b8fa764baeb0f19c1e857df91e0eda5a0f nulltoken <emeric.fermas@gmail.com> 1380209404 +0200 commit: second
+4d6558b8fa764baeb0f19c1e857df91e0eda5a0f b240c0fb88c5a629e00ebc1275fa1f33e364a705 nulltoken <emeric.fermas@gmail.com> 1380209414 +0200 commit: third
+b240c0fb88c5a629e00ebc1275fa1f33e364a705 81f4b1aac643e6983fab370eae8aefccecbf3a4c nulltoken <emeric.fermas@gmail.com> 1380209425 +0200 commit: A
+81f4b1aac643e6983fab370eae8aefccecbf3a4c 6126a5f9c57ebc81e64370ec3095184ad92dab1c nulltoken <emeric.fermas@gmail.com> 1380209445 +0200 commit: c
+6126a5f9c57ebc81e64370ec3095184ad92dab1c 4d6558b8fa764baeb0f19c1e857df91e0eda5a0f nulltoken <emeric.fermas@gmail.com> 1380209455 +0200 reset: moving to 4d6558b8fa764baeb0f19c1e857df91e0eda5a0f
+4d6558b8fa764baeb0f19c1e857df91e0eda5a0f 31fc9136820b507e938a9c6b88bf2c567a9f6f4b nulltoken <emeric.fermas@gmail.com> 1380209465 +0200 commit: B
+31fc9136820b507e938a9c6b88bf2c567a9f6f4b ce1c4f8b6120122e23d4442925d98c56c41917d8 nulltoken <emeric.fermas@gmail.com> 1380209486 +0200 merge c: Merge made by the 'recursive' strategy.
+ce1c4f8b6120122e23d4442925d98c56c41917d8 4d6558b8fa764baeb0f19c1e857df91e0eda5a0f nulltoken <emeric.fermas@gmail.com> 1380209486 +0200 reset: moving to 4d6558b8fa764baeb0f19c1e857df91e0eda5a0f
+4d6558b8fa764baeb0f19c1e857df91e0eda5a0f 6a12b56088706aa6c39ccd23b7c7ce60f3a0b9a1 nulltoken <emeric.fermas@gmail.com> 1380209496 +0200 commit: D
+6a12b56088706aa6c39ccd23b7c7ce60f3a0b9a1 1e016431ec7b22dd3e23f3e6f5f68f358f9227cf nulltoken <emeric.fermas@gmail.com> 1380209527 +0200 commit: another
+1e016431ec7b22dd3e23f3e6f5f68f358f9227cf a9eb02af13df030159e39f70330d5c8a47655691 nulltoken <emeric.fermas@gmail.com> 1380209547 +0200 commit: yet another
+a9eb02af13df030159e39f70330d5c8a47655691 949b98e208015bfc0e2f573debc34ae2f97a7f0e nulltoken <emeric.fermas@gmail.com> 1380209557 +0200 merge ce1c4f8b6120122e23d4442925d98c56c41917d8: Merge made by the 'recursive' strategy.
+949b98e208015bfc0e2f573debc34ae2f97a7f0e a6095f816e81f64651595d488badc42399837d6a nulltoken <emeric.fermas@gmail.com> 1380209567 +0200 commit: x
diff --git a/tests/resources/describe/.gitted/logs/refs/heads/master b/tests/resources/describe/.gitted/logs/refs/heads/master
new file mode 100644
index 0000000..fc49c6f
--- /dev/null
+++ b/tests/resources/describe/.gitted/logs/refs/heads/master
@@ -0,0 +1,14 @@
+0000000000000000000000000000000000000000 108b485d8268ea595df8ffea74f0f4b186577d32 nulltoken <emeric.fermas@gmail.com> 1380209394 +0200 commit (initial): initial
+108b485d8268ea595df8ffea74f0f4b186577d32 4d6558b8fa764baeb0f19c1e857df91e0eda5a0f nulltoken <emeric.fermas@gmail.com> 1380209404 +0200 commit: second
+4d6558b8fa764baeb0f19c1e857df91e0eda5a0f b240c0fb88c5a629e00ebc1275fa1f33e364a705 nulltoken <emeric.fermas@gmail.com> 1380209414 +0200 commit: third
+b240c0fb88c5a629e00ebc1275fa1f33e364a705 81f4b1aac643e6983fab370eae8aefccecbf3a4c nulltoken <emeric.fermas@gmail.com> 1380209425 +0200 commit: A
+81f4b1aac643e6983fab370eae8aefccecbf3a4c 6126a5f9c57ebc81e64370ec3095184ad92dab1c nulltoken <emeric.fermas@gmail.com> 1380209445 +0200 commit: c
+6126a5f9c57ebc81e64370ec3095184ad92dab1c 4d6558b8fa764baeb0f19c1e857df91e0eda5a0f nulltoken <emeric.fermas@gmail.com> 1380209455 +0200 reset: moving to 4d6558b8fa764baeb0f19c1e857df91e0eda5a0f
+4d6558b8fa764baeb0f19c1e857df91e0eda5a0f 31fc9136820b507e938a9c6b88bf2c567a9f6f4b nulltoken <emeric.fermas@gmail.com> 1380209465 +0200 commit: B
+31fc9136820b507e938a9c6b88bf2c567a9f6f4b ce1c4f8b6120122e23d4442925d98c56c41917d8 nulltoken <emeric.fermas@gmail.com> 1380209486 +0200 merge c: Merge made by the 'recursive' strategy.
+ce1c4f8b6120122e23d4442925d98c56c41917d8 4d6558b8fa764baeb0f19c1e857df91e0eda5a0f nulltoken <emeric.fermas@gmail.com> 1380209486 +0200 reset: moving to 4d6558b8fa764baeb0f19c1e857df91e0eda5a0f
+4d6558b8fa764baeb0f19c1e857df91e0eda5a0f 6a12b56088706aa6c39ccd23b7c7ce60f3a0b9a1 nulltoken <emeric.fermas@gmail.com> 1380209496 +0200 commit: D
+6a12b56088706aa6c39ccd23b7c7ce60f3a0b9a1 1e016431ec7b22dd3e23f3e6f5f68f358f9227cf nulltoken <emeric.fermas@gmail.com> 1380209527 +0200 commit: another
+1e016431ec7b22dd3e23f3e6f5f68f358f9227cf a9eb02af13df030159e39f70330d5c8a47655691 nulltoken <emeric.fermas@gmail.com> 1380209547 +0200 commit: yet another
+a9eb02af13df030159e39f70330d5c8a47655691 949b98e208015bfc0e2f573debc34ae2f97a7f0e nulltoken <emeric.fermas@gmail.com> 1380209557 +0200 merge ce1c4f8b6120122e23d4442925d98c56c41917d8: Merge made by the 'recursive' strategy.
+949b98e208015bfc0e2f573debc34ae2f97a7f0e a6095f816e81f64651595d488badc42399837d6a nulltoken <emeric.fermas@gmail.com> 1380209567 +0200 commit: x
diff --git a/tests/resources/describe/.gitted/objects/03/00021985931292d0611b9232e757035fefc04d b/tests/resources/describe/.gitted/objects/03/00021985931292d0611b9232e757035fefc04d
new file mode 100644
index 0000000..4b98de8
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/03/00021985931292d0611b9232e757035fefc04d
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/10/8b485d8268ea595df8ffea74f0f4b186577d32 b/tests/resources/describe/.gitted/objects/10/8b485d8268ea595df8ffea74f0f4b186577d32
new file mode 100644
index 0000000..0d6187b
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/10/8b485d8268ea595df8ffea74f0f4b186577d32
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/10/bd08b099ecb79184c60183f5c94ca915f427ad b/tests/resources/describe/.gitted/objects/10/bd08b099ecb79184c60183f5c94ca915f427ad
new file mode 100644
index 0000000..3540cfa
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/10/bd08b099ecb79184c60183f5c94ca915f427ad
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/17/8481050188cf00d7d9cd5a11e43ab8fab9294f b/tests/resources/describe/.gitted/objects/17/8481050188cf00d7d9cd5a11e43ab8fab9294f
new file mode 100644
index 0000000..f2eaf83
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/17/8481050188cf00d7d9cd5a11e43ab8fab9294f
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/19/1faf88a5826a99f475baaf8b13652c4e40bfe6 b/tests/resources/describe/.gitted/objects/19/1faf88a5826a99f475baaf8b13652c4e40bfe6
new file mode 100644
index 0000000..e44246b
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/19/1faf88a5826a99f475baaf8b13652c4e40bfe6
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/1e/016431ec7b22dd3e23f3e6f5f68f358f9227cf b/tests/resources/describe/.gitted/objects/1e/016431ec7b22dd3e23f3e6f5f68f358f9227cf
new file mode 100644
index 0000000..a876981
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/1e/016431ec7b22dd3e23f3e6f5f68f358f9227cf
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/22/3b7836fb19fdf64ba2d3cd6173c6a283141f78 b/tests/resources/describe/.gitted/objects/22/3b7836fb19fdf64ba2d3cd6173c6a283141f78
new file mode 100644
index 0000000..faf1fbe
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/22/3b7836fb19fdf64ba2d3cd6173c6a283141f78
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/25/d5edf8c0ef17e8a13b8da75913dcec4ea7afc1 b/tests/resources/describe/.gitted/objects/25/d5edf8c0ef17e8a13b8da75913dcec4ea7afc1
new file mode 100644
index 0000000..3353bf9
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/25/d5edf8c0ef17e8a13b8da75913dcec4ea7afc1
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/2b/df67abb163a4ffb2d7f3f0880c9fe5068ce782 b/tests/resources/describe/.gitted/objects/2b/df67abb163a4ffb2d7f3f0880c9fe5068ce782
new file mode 100644
index 0000000..d0398e6
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/2b/df67abb163a4ffb2d7f3f0880c9fe5068ce782
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/31/fc9136820b507e938a9c6b88bf2c567a9f6f4b b/tests/resources/describe/.gitted/objects/31/fc9136820b507e938a9c6b88bf2c567a9f6f4b
new file mode 100644
index 0000000..7752a95
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/31/fc9136820b507e938a9c6b88bf2c567a9f6f4b
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/42/8f9554a2eec22de29898819b579466af7c1583 b/tests/resources/describe/.gitted/objects/42/8f9554a2eec22de29898819b579466af7c1583
new file mode 100644
index 0000000..f27d552
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/42/8f9554a2eec22de29898819b579466af7c1583
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/4d/6558b8fa764baeb0f19c1e857df91e0eda5a0f b/tests/resources/describe/.gitted/objects/4d/6558b8fa764baeb0f19c1e857df91e0eda5a0f
new file mode 100644
index 0000000..311ee2f
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/4d/6558b8fa764baeb0f19c1e857df91e0eda5a0f
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/4f/2d9ce01ad5249cabdc6565366af8aff85b1525 b/tests/resources/describe/.gitted/objects/4f/2d9ce01ad5249cabdc6565366af8aff85b1525
new file mode 100644
index 0000000..f04379f
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/4f/2d9ce01ad5249cabdc6565366af8aff85b1525
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/52/912fbab0715dec53d43053966e78ad213ba359 b/tests/resources/describe/.gitted/objects/52/912fbab0715dec53d43053966e78ad213ba359
new file mode 100644
index 0000000..5deb7ec
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/52/912fbab0715dec53d43053966e78ad213ba359
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/56/26abf0f72e58d7a153368ba57db4c673c0e171 b/tests/resources/describe/.gitted/objects/56/26abf0f72e58d7a153368ba57db4c673c0e171
new file mode 100644
index 0000000..4d54474
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/56/26abf0f72e58d7a153368ba57db4c673c0e171
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/61/26a5f9c57ebc81e64370ec3095184ad92dab1c b/tests/resources/describe/.gitted/objects/61/26a5f9c57ebc81e64370ec3095184ad92dab1c
new file mode 100644
index 0000000..e71707f
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/61/26a5f9c57ebc81e64370ec3095184ad92dab1c
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/62/d8fe9f6db631bd3a19140699101c9e281c9f9d b/tests/resources/describe/.gitted/objects/62/d8fe9f6db631bd3a19140699101c9e281c9f9d
new file mode 100644
index 0000000..734f7dc
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/62/d8fe9f6db631bd3a19140699101c9e281c9f9d
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/65/a91bc2262480dce4c5979519aae6668368eb4e b/tests/resources/describe/.gitted/objects/65/a91bc2262480dce4c5979519aae6668368eb4e
new file mode 100644
index 0000000..ef9f53a
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/65/a91bc2262480dce4c5979519aae6668368eb4e
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/68/0166b6cd31f76354fee2572618e6b0142d05e6 b/tests/resources/describe/.gitted/objects/68/0166b6cd31f76354fee2572618e6b0142d05e6
new file mode 100644
index 0000000..36f1986
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/68/0166b6cd31f76354fee2572618e6b0142d05e6
@@ -0,0 +1,2 @@
+xŒK @]sŠÙ›4¤|c<Co0ŒS‚–Ö .¼½¸yy›÷:eÐ&žŽôîàH›4; Á£#rl#óÝØäÙ³8\-aŠ¤Uÿ¾ø¨µtÕÇcù3Kƒý³mýxÊ©Ò
+O«´Jï[®T¶i$WÐ6 Á8kç!¨Ô¢~U<)a \ No newline at end of file
diff --git a/tests/resources/describe/.gitted/objects/69/3a3de402bb23897ed5c931273e53c78eff0495 b/tests/resources/describe/.gitted/objects/69/3a3de402bb23897ed5c931273e53c78eff0495
new file mode 100644
index 0000000..80a08fc
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/69/3a3de402bb23897ed5c931273e53c78eff0495
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/6a/12b56088706aa6c39ccd23b7c7ce60f3a0b9a1 b/tests/resources/describe/.gitted/objects/6a/12b56088706aa6c39ccd23b7c7ce60f3a0b9a1
new file mode 100644
index 0000000..9c773e4
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/6a/12b56088706aa6c39ccd23b7c7ce60f3a0b9a1
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/6d/218e42592043041c4da016ff298cf241b86c3c b/tests/resources/describe/.gitted/objects/6d/218e42592043041c4da016ff298cf241b86c3c
new file mode 100644
index 0000000..59fc709
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/6d/218e42592043041c4da016ff298cf241b86c3c
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/75/bb152c600647586c226d98411b1d2f9861af5a b/tests/resources/describe/.gitted/objects/75/bb152c600647586c226d98411b1d2f9861af5a
new file mode 100644
index 0000000..a2016f4
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/75/bb152c600647586c226d98411b1d2f9861af5a
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/81/f4b1aac643e6983fab370eae8aefccecbf3a4c b/tests/resources/describe/.gitted/objects/81/f4b1aac643e6983fab370eae8aefccecbf3a4c
new file mode 100644
index 0000000..e47f3fb
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/81/f4b1aac643e6983fab370eae8aefccecbf3a4c
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/8e/c1d96451ff05451720e4e8968812c46b35e5e4 b/tests/resources/describe/.gitted/objects/8e/c1d96451ff05451720e4e8968812c46b35e5e4
new file mode 100644
index 0000000..432b2c1
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/8e/c1d96451ff05451720e4e8968812c46b35e5e4
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/94/9b98e208015bfc0e2f573debc34ae2f97a7f0e b/tests/resources/describe/.gitted/objects/94/9b98e208015bfc0e2f573debc34ae2f97a7f0e
new file mode 100644
index 0000000..2c7aafa
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/94/9b98e208015bfc0e2f573debc34ae2f97a7f0e
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/9c/06d71b8406ab97537e3acdc39a2c4ade7a9411 b/tests/resources/describe/.gitted/objects/9c/06d71b8406ab97537e3acdc39a2c4ade7a9411
new file mode 100644
index 0000000..5fff6fa
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/9c/06d71b8406ab97537e3acdc39a2c4ade7a9411
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/a6/095f816e81f64651595d488badc42399837d6a b/tests/resources/describe/.gitted/objects/a6/095f816e81f64651595d488badc42399837d6a
new file mode 100644
index 0000000..eb9ab14
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/a6/095f816e81f64651595d488badc42399837d6a
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/a9/e3325a07117aa5381e044a8d96c26eb30d729d b/tests/resources/describe/.gitted/objects/a9/e3325a07117aa5381e044a8d96c26eb30d729d
new file mode 100644
index 0000000..ee45b76
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/a9/e3325a07117aa5381e044a8d96c26eb30d729d
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/a9/eb02af13df030159e39f70330d5c8a47655691 b/tests/resources/describe/.gitted/objects/a9/eb02af13df030159e39f70330d5c8a47655691
new file mode 100644
index 0000000..320161a
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/a9/eb02af13df030159e39f70330d5c8a47655691
@@ -0,0 +1,2 @@
+xŽA
+à »öî Aý&1PJ¯òÕgc±fÑÛ×3t÷˜aà…’óÖ¤!uiqdöF/ì´ÑÃzbFLŠBÛEwé¬xsÅѤ†Ò“%0{cb$J„)ir‰F—cæŸm-Uç¾·òÂ!oȨ[jæÏã™yÛ‡Pò]jrʨe´³¼ö¡D§ýdß¹ø¢I>J[QÅhK% \ No newline at end of file
diff --git a/tests/resources/describe/.gitted/objects/aa/d8d5cef3915ab78b3227abaaac99b62db9eb54 b/tests/resources/describe/.gitted/objects/aa/d8d5cef3915ab78b3227abaaac99b62db9eb54
new file mode 100644
index 0000000..4cbaff1
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/aa/d8d5cef3915ab78b3227abaaac99b62db9eb54
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/aa/ddd4f14847e0e323924ec262c2343249a84f8b b/tests/resources/describe/.gitted/objects/aa/ddd4f14847e0e323924ec262c2343249a84f8b
new file mode 100644
index 0000000..651ec78
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/aa/ddd4f14847e0e323924ec262c2343249a84f8b
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/b2/40c0fb88c5a629e00ebc1275fa1f33e364a705 b/tests/resources/describe/.gitted/objects/b2/40c0fb88c5a629e00ebc1275fa1f33e364a705
new file mode 100644
index 0000000..fe86e7c
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/b2/40c0fb88c5a629e00ebc1275fa1f33e364a705
@@ -0,0 +1,3 @@
+xŽM
+à »öî AͧQ(¥WñçÙJc,ÖÜ¿ž¡«7 ¼Øj-ƒ+E—Ñner†´ÌYè9› Xg¬•*’ «†±ï8§d´¶Áf¿
+Ad预Õ[ÊNB yíEfþ¯Öùqîûhoü†Š^â’Ñ«ÿ>žÕ—}‰­Þ¹\­P‘$~ Ø´óäÀŸ9¯ÒûµGG¬ \ No newline at end of file
diff --git a/tests/resources/describe/.gitted/objects/ce/1c4f8b6120122e23d4442925d98c56c41917d8 b/tests/resources/describe/.gitted/objects/ce/1c4f8b6120122e23d4442925d98c56c41917d8
new file mode 100644
index 0000000..408c5da
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/ce/1c4f8b6120122e23d4442925d98c56c41917d8
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/d5/aab219a814ddbe4b3aaedf03cdea491b218ec4 b/tests/resources/describe/.gitted/objects/d5/aab219a814ddbe4b3aaedf03cdea491b218ec4
new file mode 100644
index 0000000..4512d16
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/d5/aab219a814ddbe4b3aaedf03cdea491b218ec4
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/f2/ad6c76f0115a6ba5b00456a849810e7ec0af20 b/tests/resources/describe/.gitted/objects/f2/ad6c76f0115a6ba5b00456a849810e7ec0af20
new file mode 100644
index 0000000..a364631
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/f2/ad6c76f0115a6ba5b00456a849810e7ec0af20
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/f7/0f10e4db19068f79bc43844b49f3eece45c4e8 b/tests/resources/describe/.gitted/objects/f7/0f10e4db19068f79bc43844b49f3eece45c4e8
new file mode 100644
index 0000000..2e15b4f
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/f7/0f10e4db19068f79bc43844b49f3eece45c4e8
Binary files differ
diff --git a/tests/resources/describe/.gitted/objects/f7/19efd430d52bcfc8566a43b2eb655688d38871 b/tests/resources/describe/.gitted/objects/f7/19efd430d52bcfc8566a43b2eb655688d38871
new file mode 100644
index 0000000..b2d51d9
--- /dev/null
+++ b/tests/resources/describe/.gitted/objects/f7/19efd430d52bcfc8566a43b2eb655688d38871
Binary files differ
diff --git a/tests/resources/describe/.gitted/refs/heads/master b/tests/resources/describe/.gitted/refs/heads/master
new file mode 100644
index 0000000..0b2a541
--- /dev/null
+++ b/tests/resources/describe/.gitted/refs/heads/master
@@ -0,0 +1 @@
+a6095f816e81f64651595d488badc42399837d6a
diff --git a/tests/resources/describe/.gitted/refs/tags/A b/tests/resources/describe/.gitted/refs/tags/A
new file mode 100644
index 0000000..aced4fd
--- /dev/null
+++ b/tests/resources/describe/.gitted/refs/tags/A
@@ -0,0 +1 @@
+aaddd4f14847e0e323924ec262c2343249a84f8b
diff --git a/tests/resources/describe/.gitted/refs/tags/B b/tests/resources/describe/.gitted/refs/tags/B
new file mode 100644
index 0000000..ab1a5e6
--- /dev/null
+++ b/tests/resources/describe/.gitted/refs/tags/B
@@ -0,0 +1 @@
+52912fbab0715dec53d43053966e78ad213ba359
diff --git a/tests/resources/describe/.gitted/refs/tags/D b/tests/resources/describe/.gitted/refs/tags/D
new file mode 100644
index 0000000..90f4208
--- /dev/null
+++ b/tests/resources/describe/.gitted/refs/tags/D
@@ -0,0 +1 @@
+10bd08b099ecb79184c60183f5c94ca915f427ad
diff --git a/tests/resources/describe/.gitted/refs/tags/R b/tests/resources/describe/.gitted/refs/tags/R
new file mode 100644
index 0000000..ef04b7c
--- /dev/null
+++ b/tests/resources/describe/.gitted/refs/tags/R
@@ -0,0 +1 @@
+680166b6cd31f76354fee2572618e6b0142d05e6
diff --git a/tests/resources/describe/.gitted/refs/tags/c b/tests/resources/describe/.gitted/refs/tags/c
new file mode 100644
index 0000000..650d82f
--- /dev/null
+++ b/tests/resources/describe/.gitted/refs/tags/c
@@ -0,0 +1 @@
+6126a5f9c57ebc81e64370ec3095184ad92dab1c
diff --git a/tests/resources/describe/.gitted/refs/tags/e b/tests/resources/describe/.gitted/refs/tags/e
new file mode 100644
index 0000000..5e88d6f
--- /dev/null
+++ b/tests/resources/describe/.gitted/refs/tags/e
@@ -0,0 +1 @@
+1e016431ec7b22dd3e23f3e6f5f68f358f9227cf
diff --git a/tests/resources/describe/another b/tests/resources/describe/another
new file mode 100644
index 0000000..a3d00fa
--- /dev/null
+++ b/tests/resources/describe/another
@@ -0,0 +1 @@
+DDD
diff --git a/tests/resources/describe/file b/tests/resources/describe/file
new file mode 100644
index 0000000..fd66be0
--- /dev/null
+++ b/tests/resources/describe/file
@@ -0,0 +1 @@
+X
diff --git a/tests/resources/describe/side b/tests/resources/describe/side
new file mode 100644
index 0000000..fd66be0
--- /dev/null
+++ b/tests/resources/describe/side
@@ -0,0 +1 @@
+X
diff --git a/tests/resources/diff/.gitted/HEAD b/tests/resources/diff/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/diff/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/diff/.gitted/config b/tests/resources/diff/.gitted/config
new file mode 100644
index 0000000..77a27ef
--- /dev/null
+++ b/tests/resources/diff/.gitted/config
@@ -0,0 +1,6 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = false
diff --git a/tests/resources/diff/.gitted/description b/tests/resources/diff/.gitted/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/diff/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/diff/.gitted/index b/tests/resources/diff/.gitted/index
new file mode 100644
index 0000000..e107187
--- /dev/null
+++ b/tests/resources/diff/.gitted/index
Binary files differ
diff --git a/tests/resources/diff/.gitted/info/exclude b/tests/resources/diff/.gitted/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/diff/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/diff/.gitted/logs/HEAD b/tests/resources/diff/.gitted/logs/HEAD
new file mode 100644
index 0000000..8c6f6fd
--- /dev/null
+++ b/tests/resources/diff/.gitted/logs/HEAD
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 d70d245ed97ed2aa596dd1af6536e4bfdb047b69 Russell Belfer <rb@github.com> 1347559804 -0700 commit (initial): initial commit
+d70d245ed97ed2aa596dd1af6536e4bfdb047b69 7a9e0b02e63179929fed24f0a3e0f19168114d10 Russell Belfer <rb@github.com> 1347560491 -0700 commit: some changes
diff --git a/tests/resources/diff/.gitted/logs/refs/heads/master b/tests/resources/diff/.gitted/logs/refs/heads/master
new file mode 100644
index 0000000..8c6f6fd
--- /dev/null
+++ b/tests/resources/diff/.gitted/logs/refs/heads/master
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 d70d245ed97ed2aa596dd1af6536e4bfdb047b69 Russell Belfer <rb@github.com> 1347559804 -0700 commit (initial): initial commit
+d70d245ed97ed2aa596dd1af6536e4bfdb047b69 7a9e0b02e63179929fed24f0a3e0f19168114d10 Russell Belfer <rb@github.com> 1347560491 -0700 commit: some changes
diff --git a/tests/resources/diff/.gitted/objects/29/ab7053bb4dde0298e03e2c179e890b7dd465a7 b/tests/resources/diff/.gitted/objects/29/ab7053bb4dde0298e03e2c179e890b7dd465a7
new file mode 100644
index 0000000..94f9a67
--- /dev/null
+++ b/tests/resources/diff/.gitted/objects/29/ab7053bb4dde0298e03e2c179e890b7dd465a7
Binary files differ
diff --git a/tests/resources/diff/.gitted/objects/3e/5bcbad2a68e5bc60a53b8388eea53a1a7ab847 b/tests/resources/diff/.gitted/objects/3e/5bcbad2a68e5bc60a53b8388eea53a1a7ab847
new file mode 100644
index 0000000..9fed523
--- /dev/null
+++ b/tests/resources/diff/.gitted/objects/3e/5bcbad2a68e5bc60a53b8388eea53a1a7ab847
Binary files differ
diff --git a/tests/resources/diff/.gitted/objects/54/6c735f16a3b44d9784075c2c0dab2ac9bf1989 b/tests/resources/diff/.gitted/objects/54/6c735f16a3b44d9784075c2c0dab2ac9bf1989
new file mode 100644
index 0000000..d7df4d6
--- /dev/null
+++ b/tests/resources/diff/.gitted/objects/54/6c735f16a3b44d9784075c2c0dab2ac9bf1989
Binary files differ
diff --git a/tests/resources/diff/.gitted/objects/7a/9e0b02e63179929fed24f0a3e0f19168114d10 b/tests/resources/diff/.gitted/objects/7a/9e0b02e63179929fed24f0a3e0f19168114d10
new file mode 100644
index 0000000..9bc25eb
--- /dev/null
+++ b/tests/resources/diff/.gitted/objects/7a/9e0b02e63179929fed24f0a3e0f19168114d10
Binary files differ
diff --git a/tests/resources/diff/.gitted/objects/7b/808f723a8ca90df319682c221187235af76693 b/tests/resources/diff/.gitted/objects/7b/808f723a8ca90df319682c221187235af76693
new file mode 100644
index 0000000..2fd266b
--- /dev/null
+++ b/tests/resources/diff/.gitted/objects/7b/808f723a8ca90df319682c221187235af76693
Binary files differ
diff --git a/tests/resources/diff/.gitted/objects/88/789109439c1e1c3cd45224001edee5304ed53c b/tests/resources/diff/.gitted/objects/88/789109439c1e1c3cd45224001edee5304ed53c
new file mode 100644
index 0000000..7598b59
--- /dev/null
+++ b/tests/resources/diff/.gitted/objects/88/789109439c1e1c3cd45224001edee5304ed53c
@@ -0,0 +1 @@
+x+)JMU07g040031QHÌË/ÉH-Ò+©(aÉ)Ž[¼Åwz {Œïj­“û%;¡ÊŠRSrSÁª4Wïö½Ç4ãŽø¼NîÚ+©Ë¶a \ No newline at end of file
diff --git a/tests/resources/diff/.gitted/objects/cb/8294e696339863df760b2ff5d1e275bee72455 b/tests/resources/diff/.gitted/objects/cb/8294e696339863df760b2ff5d1e275bee72455
new file mode 100644
index 0000000..86ebe04
--- /dev/null
+++ b/tests/resources/diff/.gitted/objects/cb/8294e696339863df760b2ff5d1e275bee72455
Binary files differ
diff --git a/tests/resources/diff/.gitted/objects/d7/0d245ed97ed2aa596dd1af6536e4bfdb047b69 b/tests/resources/diff/.gitted/objects/d7/0d245ed97ed2aa596dd1af6536e4bfdb047b69
new file mode 100644
index 0000000..99304c4
--- /dev/null
+++ b/tests/resources/diff/.gitted/objects/d7/0d245ed97ed2aa596dd1af6536e4bfdb047b69
@@ -0,0 +1 @@
+x•Û !óm·_ׄRB:XÝkVpWpµÿ© ¿‡™9±î{î ,^z#‚œôšŒ7JygÔš¬áA¦„« i1Y©Ù2úV¼ÇyR)𢒨Á½…ç'÷m„[¬û„ÒÑ;®áÊ-çl®ó¯Oô_“å#÷¼ø%Øœv8¤ \ No newline at end of file
diff --git a/tests/resources/diff/.gitted/refs/heads/master b/tests/resources/diff/.gitted/refs/heads/master
new file mode 100644
index 0000000..a83afc3
--- /dev/null
+++ b/tests/resources/diff/.gitted/refs/heads/master
@@ -0,0 +1 @@
+7a9e0b02e63179929fed24f0a3e0f19168114d10
diff --git a/tests/resources/diff/another.txt b/tests/resources/diff/another.txt
new file mode 100644
index 0000000..d0e0bae
--- /dev/null
+++ b/tests/resources/diff/another.txt
@@ -0,0 +1,38 @@
+Git is fast. With Git, nearly all operations are performed locally, giving
+it an huge speed advantage on centralized systems that constantly have to
+communicate with a server somewh3r3.
+
+For testing, large AWS instances were set up in the same availability
+zone. Git and SVN were installed on both machines, the Ruby repository was
+copied to both Git and SVN servers, and common operations were performed on
+both.
+
+In some cases the commands don't match up exactly. Here, matching on the
+lowest common denominator was attempted. For example, the 'commit' tests
+also include the time to push for Git, though most of the time you would not
+actually be pushing to the server immediately after a commit where the two
+commands cannot be separated in SVN.
+
+Note that this is the best case scenario for SVN - a server with no load
+with an 80MB/s bandwidth connection to the client machine. Nearly all of
+these times would be even worse for SVN if that connection was slower, while
+many of the Git times would not be affected.
+
+Clearly, in many of these common version control operations, Git is one or
+two orders of magnitude faster than SVN, even under ideal conditions for
+SVN.
+
+Let's see how common operations stack up against Subversion, a common
+centralized version control system that is similar to CVS or
+Perforce. Smaller is faster.
+
+One place where Git is slower is in the initial clone operation. Here, Git
+One place where Git is slower is in the initial clone operation. Here, Git
+One place where Git is slower is in the initial clone operation. Here, Git
+seen in the above charts, it's not considerably slower for an operation that
+is only performed once.
+
+It's also interesting to note that the size of the data on the client side
+is very similar even though Git also has every version of every file for the
+entire history of the project. This illustrates how efficient it is at
+compressing and storing data on the client side. \ No newline at end of file
diff --git a/tests/resources/diff/readme.txt b/tests/resources/diff/readme.txt
new file mode 100644
index 0000000..beedf28
--- /dev/null
+++ b/tests/resources/diff/readme.txt
@@ -0,0 +1,36 @@
+The Git feature that r3ally mak3s it stand apart from n3arly 3v3ry other SCM
+out there is its branching model.
+
+Git allows and encourages you to have multiple local branches that can be
+entirely independent of each other. The creation, merging, and deletion of
+those lines of development takes seconds.
+
+Git allows and encourages you to have multiple local branches that can be
+entirely independent of each other. The creation, merging, and deletion of
+those lines of development takes seconds.
+
+This means that you can do things like:
+
+Role-Bas3d Codelin3s. Have a branch that always contains only what goes to
+production, another that you merge work into for testing, and several
+smaller ones for day to day work.
+
+Feature Based Workflow. Create new branches for each new feature you're
+working on so you can seamlessly switch back and forth between them, then
+delete each branch when that feature gets merged into your main line.
+
+Disposable Experimentation. Create a branch to experiment in, realize it's
+not going to work, and just delete it - abandoning the work—with nobody else
+ever seeing it (even if you've pushed other branches in the meantime).
+
+Notably, when you push to a remote repository, you do not have to push all
+share it with others.
+
+Git allows and encourages you to have multiple local branches that can be
+entirely independent of each other. The creation, merging, and deletion of
+those lines of development takes seconds.
+
+There are ways to accomplish some of this with other systems, but the work
+involved is much more difficult and error-prone. Git makes this process
+incredibly easy and it changes the way most developers work when they learn
+it.!
diff --git a/tests/resources/diff_format_email/.gitted/HEAD b/tests/resources/diff_format_email/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/diff_format_email/.gitted/config b/tests/resources/diff_format_email/.gitted/config
new file mode 100644
index 0000000..6c9406b
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/config
@@ -0,0 +1,7 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
+ precomposeunicode = true
diff --git a/tests/resources/diff_format_email/.gitted/index b/tests/resources/diff_format_email/.gitted/index
new file mode 100644
index 0000000..4514a6b
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/index
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/info/exclude b/tests/resources/diff_format_email/.gitted/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/diff_format_email/.gitted/objects/06/b7b69a62cbd1e53c6c4e0c3f16473dcfdb4af6 b/tests/resources/diff_format_email/.gitted/objects/06/b7b69a62cbd1e53c6c4e0c3f16473dcfdb4af6
new file mode 100644
index 0000000..37588f1
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/06/b7b69a62cbd1e53c6c4e0c3f16473dcfdb4af6
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/07/594f324ebcf92902334c6016e30e716431dfbc b/tests/resources/diff_format_email/.gitted/objects/07/594f324ebcf92902334c6016e30e716431dfbc
new file mode 100644
index 0000000..bd27379
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/07/594f324ebcf92902334c6016e30e716431dfbc
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/0a/37045ca6d8503e9bcf06a12abbbc8e92664cce b/tests/resources/diff_format_email/.gitted/objects/0a/37045ca6d8503e9bcf06a12abbbc8e92664cce
new file mode 100644
index 0000000..1ece99c
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/0a/37045ca6d8503e9bcf06a12abbbc8e92664cce
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/0d/593fca18d1ab11deb6e8025c9fe417456fe883 b/tests/resources/diff_format_email/.gitted/objects/0d/593fca18d1ab11deb6e8025c9fe417456fe883
new file mode 100644
index 0000000..e49e107
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/0d/593fca18d1ab11deb6e8025c9fe417456fe883
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/0d/b2a262bc8c5c3cba55254730045a8258da7a37 b/tests/resources/diff_format_email/.gitted/objects/0d/b2a262bc8c5c3cba55254730045a8258da7a37
new file mode 100644
index 0000000..1399d7f
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/0d/b2a262bc8c5c3cba55254730045a8258da7a37
@@ -0,0 +1,2 @@
+x•ÎA
+Â0@Q×9Åì™f’6 z‚™tBCm-i¼¿â üû?½–¥4°„‡VUÁS´š)Eóàœ„hET¤ëƒ|}BÊd6®º6ÁÕ±=&§9Ø8KêÆ€<Æ¢dÃï6½*ܸՒfx4-ëÄulpÞöë6鱗\ ózçˆ"Ñ"šôûkú¿4w]yQÈå©PVØß2–j>ÄTIð \ No newline at end of file
diff --git a/tests/resources/diff_format_email/.gitted/objects/10/808fe9c9be5a190c0ba68d1a002233fb363508 b/tests/resources/diff_format_email/.gitted/objects/10/808fe9c9be5a190c0ba68d1a002233fb363508
new file mode 100644
index 0000000..90313fe
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/10/808fe9c9be5a190c0ba68d1a002233fb363508
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/13/ecf3d572dbc5e5b32c8ba067d1d1e0939572e8 b/tests/resources/diff_format_email/.gitted/objects/13/ecf3d572dbc5e5b32c8ba067d1d1e0939572e8
new file mode 100644
index 0000000..360dc3b
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/13/ecf3d572dbc5e5b32c8ba067d1d1e0939572e8
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/17/cfad36e93db7706b16bef5ef842ba1e5ca06ab b/tests/resources/diff_format_email/.gitted/objects/17/cfad36e93db7706b16bef5ef842ba1e5ca06ab
new file mode 100644
index 0000000..603aa49
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/17/cfad36e93db7706b16bef5ef842ba1e5ca06ab
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/1a/9932083f96b0db42552103d40076f62fa8235e b/tests/resources/diff_format_email/.gitted/objects/1a/9932083f96b0db42552103d40076f62fa8235e
new file mode 100644
index 0000000..b6f0453
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/1a/9932083f96b0db42552103d40076f62fa8235e
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/1a/e3be57f869687d983066a0f5d2aaea1b82ddc5 b/tests/resources/diff_format_email/.gitted/objects/1a/e3be57f869687d983066a0f5d2aaea1b82ddc5
new file mode 100644
index 0000000..be85c78
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/1a/e3be57f869687d983066a0f5d2aaea1b82ddc5
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/1b/525b0a6c5218b069b601ce91fce8eaf0a54e20 b/tests/resources/diff_format_email/.gitted/objects/1b/525b0a6c5218b069b601ce91fce8eaf0a54e20
new file mode 100644
index 0000000..e8145ed
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/1b/525b0a6c5218b069b601ce91fce8eaf0a54e20
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/1e/82c3b234e37da82e5b23e0e2a70bca68ee12c6 b/tests/resources/diff_format_email/.gitted/objects/1e/82c3b234e37da82e5b23e0e2a70bca68ee12c6
new file mode 100644
index 0000000..3ae87cf
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/1e/82c3b234e37da82e5b23e0e2a70bca68ee12c6
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/1e/875da9b1e67f853b2eec3e202c21c867097234 b/tests/resources/diff_format_email/.gitted/objects/1e/875da9b1e67f853b2eec3e202c21c867097234
new file mode 100644
index 0000000..f80b361
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/1e/875da9b1e67f853b2eec3e202c21c867097234
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/20/609dbbc32bbfc827528eec3fcea2d024e6dd8a b/tests/resources/diff_format_email/.gitted/objects/20/609dbbc32bbfc827528eec3fcea2d024e6dd8a
new file mode 100644
index 0000000..190c3f2
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/20/609dbbc32bbfc827528eec3fcea2d024e6dd8a
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/23/f92946d3f38bd090f700d3e8e7b728ffc58264 b/tests/resources/diff_format_email/.gitted/objects/23/f92946d3f38bd090f700d3e8e7b728ffc58264
new file mode 100644
index 0000000..42f49ae
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/23/f92946d3f38bd090f700d3e8e7b728ffc58264
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/24/97c5249408494e66e25070a8c74e49eaeeb6c3 b/tests/resources/diff_format_email/.gitted/objects/24/97c5249408494e66e25070a8c74e49eaeeb6c3
new file mode 100644
index 0000000..e1ede9a
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/24/97c5249408494e66e25070a8c74e49eaeeb6c3
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/24/9a4263be23b4d1c02484cb840b6eca4c6cf74d b/tests/resources/diff_format_email/.gitted/objects/24/9a4263be23b4d1c02484cb840b6eca4c6cf74d
new file mode 100644
index 0000000..1b8d689
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/24/9a4263be23b4d1c02484cb840b6eca4c6cf74d
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/25/2a3e19fd2c6fb7b20c111142c5bd5fb9ea6b8e b/tests/resources/diff_format_email/.gitted/objects/25/2a3e19fd2c6fb7b20c111142c5bd5fb9ea6b8e
new file mode 100644
index 0000000..10c3400
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/25/2a3e19fd2c6fb7b20c111142c5bd5fb9ea6b8e
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/27/93544db9060bab4f9169e5b89c82f9fa7c7fa6 b/tests/resources/diff_format_email/.gitted/objects/27/93544db9060bab4f9169e5b89c82f9fa7c7fa6
new file mode 100644
index 0000000..689f5b9
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/27/93544db9060bab4f9169e5b89c82f9fa7c7fa6
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/29/1f1ff3cbb9a6f153678d9657679e3d4bf257df b/tests/resources/diff_format_email/.gitted/objects/29/1f1ff3cbb9a6f153678d9657679e3d4bf257df
new file mode 100644
index 0000000..6af5dda
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/29/1f1ff3cbb9a6f153678d9657679e3d4bf257df
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/2f/f7b811eee62a73959350b1f7349f6f4d0c882d b/tests/resources/diff_format_email/.gitted/objects/2f/f7b811eee62a73959350b1f7349f6f4d0c882d
new file mode 100644
index 0000000..d2b911d
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/2f/f7b811eee62a73959350b1f7349f6f4d0c882d
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/39/91dce9e71a0641ca49a6a4eea6c9e7ff402ed4 b/tests/resources/diff_format_email/.gitted/objects/39/91dce9e71a0641ca49a6a4eea6c9e7ff402ed4
new file mode 100644
index 0000000..69d213d
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/39/91dce9e71a0641ca49a6a4eea6c9e7ff402ed4
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/39/ed6bbd76bca81c50db3aaca261456284f5d5b8 b/tests/resources/diff_format_email/.gitted/objects/39/ed6bbd76bca81c50db3aaca261456284f5d5b8
new file mode 100644
index 0000000..e08f03c
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/39/ed6bbd76bca81c50db3aaca261456284f5d5b8
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/45/eef2a9317e179984649de247269e38cd5d99cf b/tests/resources/diff_format_email/.gitted/objects/45/eef2a9317e179984649de247269e38cd5d99cf
new file mode 100644
index 0000000..e501456
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/45/eef2a9317e179984649de247269e38cd5d99cf
@@ -0,0 +1,2 @@
+x¥ÎÁJÅ0…a×yŠÙ_I›IqwÁ¥o0Lo+$¹&©àÛ[õÜþŸ””öƒŸzU…à&ÒÁFY„,ñ:¯ÞYš¼CT·8B¥Õ ŽæÎUs¯H,BL‘Â)ËÈ(‚ër.cŒažÃ }+^Y>mpÕšö¶_ žÞÿÚí¥õºsçG)éì&k}˜G¸à€hÎzR»þëļi*Ÿ
+RrÿñsŽ ç›Âý—Ôö’›ùÐXã \ No newline at end of file
diff --git a/tests/resources/diff_format_email/.gitted/objects/4a/076277b884c519a932be67e346db2ac80a98fa b/tests/resources/diff_format_email/.gitted/objects/4a/076277b884c519a932be67e346db2ac80a98fa
new file mode 100644
index 0000000..b855408
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/4a/076277b884c519a932be67e346db2ac80a98fa
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/4c/3bd7182ad66ea7aa20ba47ae82812b710d169c b/tests/resources/diff_format_email/.gitted/objects/4c/3bd7182ad66ea7aa20ba47ae82812b710d169c
new file mode 100644
index 0000000..65740b4
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/4c/3bd7182ad66ea7aa20ba47ae82812b710d169c
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/4c/a10087e696d2ba78d07b146a118e9a7096ed4f b/tests/resources/diff_format_email/.gitted/objects/4c/a10087e696d2ba78d07b146a118e9a7096ed4f
new file mode 100644
index 0000000..b05e7d6
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/4c/a10087e696d2ba78d07b146a118e9a7096ed4f
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/4d/de2b17d1c982cd988f21d24350a214401e4a1e b/tests/resources/diff_format_email/.gitted/objects/4d/de2b17d1c982cd988f21d24350a214401e4a1e
new file mode 100644
index 0000000..57a8dfe
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/4d/de2b17d1c982cd988f21d24350a214401e4a1e
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/4f/31e0248ac800a1edc78b74f74e86f5eba90e87 b/tests/resources/diff_format_email/.gitted/objects/4f/31e0248ac800a1edc78b74f74e86f5eba90e87
new file mode 100644
index 0000000..0f31b97
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/4f/31e0248ac800a1edc78b74f74e86f5eba90e87
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/50/17c9456d013b2c7712d29aab73b681c880f509 b/tests/resources/diff_format_email/.gitted/objects/50/17c9456d013b2c7712d29aab73b681c880f509
new file mode 100644
index 0000000..5b96aa5
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/50/17c9456d013b2c7712d29aab73b681c880f509
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/50/438cfa585c1d15cf3650ed1bf641da937cc261 b/tests/resources/diff_format_email/.gitted/objects/50/438cfa585c1d15cf3650ed1bf641da937cc261
new file mode 100644
index 0000000..8db9090
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/50/438cfa585c1d15cf3650ed1bf641da937cc261
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/52/19b9784f9a92d7bd7cb567a6d6a21bfb86697e b/tests/resources/diff_format_email/.gitted/objects/52/19b9784f9a92d7bd7cb567a6d6a21bfb86697e
new file mode 100644
index 0000000..534e3b0
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/52/19b9784f9a92d7bd7cb567a6d6a21bfb86697e
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/52/c3cd1ff6234b95fecbaf9ef13624da17697b8d b/tests/resources/diff_format_email/.gitted/objects/52/c3cd1ff6234b95fecbaf9ef13624da17697b8d
new file mode 100644
index 0000000..4bbc0ea
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/52/c3cd1ff6234b95fecbaf9ef13624da17697b8d
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/53/525d4cc3ef3ba4a5cbf69492fdffb4e4a74558 b/tests/resources/diff_format_email/.gitted/objects/53/525d4cc3ef3ba4a5cbf69492fdffb4e4a74558
new file mode 100644
index 0000000..b74d31f
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/53/525d4cc3ef3ba4a5cbf69492fdffb4e4a74558
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/53/92ef3c959f744b892bbebb168bbbb7b05c03f3 b/tests/resources/diff_format_email/.gitted/objects/53/92ef3c959f744b892bbebb168bbbb7b05c03f3
new file mode 100644
index 0000000..308c599
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/53/92ef3c959f744b892bbebb168bbbb7b05c03f3
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/55/0d730ba1b8c4937ea170b37c7ba91d792c0aaa b/tests/resources/diff_format_email/.gitted/objects/55/0d730ba1b8c4937ea170b37c7ba91d792c0aaa
new file mode 100644
index 0000000..680e487
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/55/0d730ba1b8c4937ea170b37c7ba91d792c0aaa
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/62/7e7e12d87e07a83fad5b6bfa25e86ead4a5270 b/tests/resources/diff_format_email/.gitted/objects/62/7e7e12d87e07a83fad5b6bfa25e86ead4a5270
new file mode 100644
index 0000000..269a5bc
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/62/7e7e12d87e07a83fad5b6bfa25e86ead4a5270
@@ -0,0 +1 @@
+x•MNÃ0…Yû³GŠ’ر] !8@%$NàŸqb5±#{Jéíq+`ÁŽõèûÞ›çò¶E!¦*ˆà§¤RR8=5ìÀÝ(M˜”œTïµþ µb»)˜´âº—A†Q¡äÚÞŽ…¤E3Ú`Ü Wœ™3-¹À›¡Ý Þ cZLñO{}ÙOµ‹Û3 Bh.µPx쇾gîÞðÿ$;fÃ\Ntkz‰´À†µšÙßc⊼£O{³ï˜|L3Hx5&ìàhN´Äú]ëG5oxY ÁšÓܺRÂÚL¸˜ØÞ¾‡7ÑÍSn½G15þjlö׎±×µ~ó1ÜÓÛÖf.f_*´ÕåÖó w¹t´6è T¡–; \ No newline at end of file
diff --git a/tests/resources/diff_format_email/.gitted/objects/66/81f1844dc677e5ff07ffd993461f5c441e6af5 b/tests/resources/diff_format_email/.gitted/objects/66/81f1844dc677e5ff07ffd993461f5c441e6af5
new file mode 100644
index 0000000..86a3828
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/66/81f1844dc677e5ff07ffd993461f5c441e6af5
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/69/ddefb5c245e2f9ee62bd4cabd8ebe60a01e448 b/tests/resources/diff_format_email/.gitted/objects/69/ddefb5c245e2f9ee62bd4cabd8ebe60a01e448
new file mode 100644
index 0000000..81b606f
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/69/ddefb5c245e2f9ee62bd4cabd8ebe60a01e448
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/6b/6c2067c6d968f9bddb9b900ee1ab7e5b067430 b/tests/resources/diff_format_email/.gitted/objects/6b/6c2067c6d968f9bddb9b900ee1ab7e5b067430
new file mode 100644
index 0000000..aa9d7b0
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/6b/6c2067c6d968f9bddb9b900ee1ab7e5b067430
@@ -0,0 +1,2 @@
+x¥Î;
+1…aë¬âö‚ä ˆX)¸‹$sãDˆ3æº{.Áö+þsšsjÀ…Þµ‚ '£fg=Cm⤄çˆA §<p&m¨5\H²¹‚Ï:0¥• ThaŒC…—J[;̱¨<Î^)‰ëmY Ü\xu¬pÅ’S]ú§Âññ³û¹¶’\s‡°æ01†¸dTžrJÉÐqµá_rIム/P¿Ký \ No newline at end of file
diff --git a/tests/resources/diff_format_email/.gitted/objects/6b/ef49b206b29d9c46456e075722cd1a48b41e4c b/tests/resources/diff_format_email/.gitted/objects/6b/ef49b206b29d9c46456e075722cd1a48b41e4c
new file mode 100644
index 0000000..49e9d65
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/6b/ef49b206b29d9c46456e075722cd1a48b41e4c
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/6c/15659c036377aebf3b4569959ca1f5bedb551f b/tests/resources/diff_format_email/.gitted/objects/6c/15659c036377aebf3b4569959ca1f5bedb551f
new file mode 100644
index 0000000..e32471b
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/6c/15659c036377aebf3b4569959ca1f5bedb551f
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/6e/05acc5a5dab507d91a0a0cc0fb05a3dd98892d b/tests/resources/diff_format_email/.gitted/objects/6e/05acc5a5dab507d91a0a0cc0fb05a3dd98892d
new file mode 100644
index 0000000..26ee224
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/6e/05acc5a5dab507d91a0a0cc0fb05a3dd98892d
@@ -0,0 +1,2 @@
+x¥ANÄ0 EYçÞ¨œ8“` f‡Ä’¸‰ËtD¦ºÜž"€åOúú¿,­Í!ñuUÈ1Õ£*Š…Cò•2G,2‰pžt+“{—®7)ŽœJªcEÅ,JT 1IâCˆ»›88Ùì²tx–ò±é
+OÚÛ¼^¶¯¯¿ìõ¼ZŸÅd(K;'Θ‘ ÝN÷©¦ÿ*q/z“¦¦ùMý`Ÿ÷§¿0ì§~´ûàW@ \ No newline at end of file
diff --git a/tests/resources/diff_format_email/.gitted/objects/73/09653445ecf038d3e3dd9ed55edb6cb541a4ba b/tests/resources/diff_format_email/.gitted/objects/73/09653445ecf038d3e3dd9ed55edb6cb541a4ba
new file mode 100644
index 0000000..ba9c5fa
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/73/09653445ecf038d3e3dd9ed55edb6cb541a4ba
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/74/6d514eae0c330261d37940cab33aa97fefbd93 b/tests/resources/diff_format_email/.gitted/objects/74/6d514eae0c330261d37940cab33aa97fefbd93
new file mode 100644
index 0000000..ee55d64
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/74/6d514eae0c330261d37940cab33aa97fefbd93
@@ -0,0 +1 @@
+x+)JMU01e040031QHËÌI5Ô+©(Ñ+JÍKÌMMaXY¾àB¸ØŒ¢î|ý²Ò-a'{"ÌŠw \ No newline at end of file
diff --git a/tests/resources/diff_format_email/.gitted/objects/74/a4d5394ebcfa7e9f445680897dfbc96586bc86 b/tests/resources/diff_format_email/.gitted/objects/74/a4d5394ebcfa7e9f445680897dfbc96586bc86
new file mode 100644
index 0000000..1bae4a6
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/74/a4d5394ebcfa7e9f445680897dfbc96586bc86
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/77/d0a3ed37236a7941d564f08d68d3b36462d231 b/tests/resources/diff_format_email/.gitted/objects/77/d0a3ed37236a7941d564f08d68d3b36462d231
new file mode 100644
index 0000000..db037ca
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/77/d0a3ed37236a7941d564f08d68d3b36462d231
@@ -0,0 +1,2 @@
+x¥Aj1 E³ö)´/ìØ„®
+¹…$ËÍ<N<žEoŸ¡ÛÇçñŸ´ZçèãatU@ì)3‹Cæ" 㓪¸"J˜-z 9'2Oêº HÑ%J(5¸IÙ2:õ| ¼ï¹Lž\t†¶qon$¯MWøÖ^çõ¾ý®p~ü±Ÿë:úLƒ>¥Õ LîqŠÞGø°h­Ùé~uè¿$æ+g¨­+H[Æ^`ÞÈQW \ No newline at end of file
diff --git a/tests/resources/diff_format_email/.gitted/objects/7a/de76dd34bba4733cf9878079f9fd4a456a9189 b/tests/resources/diff_format_email/.gitted/objects/7a/de76dd34bba4733cf9878079f9fd4a456a9189
new file mode 100644
index 0000000..cf9bdaa
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/7a/de76dd34bba4733cf9878079f9fd4a456a9189
@@ -0,0 +1,3 @@
+x¥ŽA
+Â0E]ç³d’4M"î÷`šm…´5IÞÞ¢Gpû>¼ÿâœÒXÁ4aW³°nˆŒ3â y Œ–,[F­í½ôH­oD-”eªÐ
+:ŠÑ‘cêzš0F¼÷Ûb™C×ÊÖ:Ì®_«¸HNcÖwãóÇçRóH•qN'Ð6x­½F {4ˆj£[j•¿$ê¶0Uåû_Æy*ê&R \ No newline at end of file
diff --git a/tests/resources/diff_format_email/.gitted/objects/7a/ff11da95ca2be0bfb74b06e7cc1c480559dbe7 b/tests/resources/diff_format_email/.gitted/objects/7a/ff11da95ca2be0bfb74b06e7cc1c480559dbe7
new file mode 100644
index 0000000..d8c9934
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/7a/ff11da95ca2be0bfb74b06e7cc1c480559dbe7
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/7f/854619451620f7fbcec7ea171675e615ce92b6 b/tests/resources/diff_format_email/.gitted/objects/7f/854619451620f7fbcec7ea171675e615ce92b6
new file mode 100644
index 0000000..df4cbb3
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/7f/854619451620f7fbcec7ea171675e615ce92b6
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/85/fa91713734c588c897dd6dd67a39576f67ae50 b/tests/resources/diff_format_email/.gitted/objects/85/fa91713734c588c897dd6dd67a39576f67ae50
new file mode 100644
index 0000000..54d3412
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/85/fa91713734c588c897dd6dd67a39576f67ae50
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/87/3806f6f27e631eb0b23e4b56bea2bfac14a373 b/tests/resources/diff_format_email/.gitted/objects/87/3806f6f27e631eb0b23e4b56bea2bfac14a373
new file mode 100644
index 0000000..890abcd
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/87/3806f6f27e631eb0b23e4b56bea2bfac14a373
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/89/47a46e2097638ca6040ad4877246f4186ec3bd b/tests/resources/diff_format_email/.gitted/objects/89/47a46e2097638ca6040ad4877246f4186ec3bd
new file mode 100644
index 0000000..d4018bf
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/89/47a46e2097638ca6040ad4877246f4186ec3bd
@@ -0,0 +1,2 @@
+x•ŽAjÃ0E³Ö)´/„ÑŒ,ÉJw….{ƒ‘ò8 ;•åEo_C{nßâ½WÖZçnYÒ©7À:¤8\uÌ!NiÌ@0qaWRˆ4FožÚ°t›¢$
+S˜8"ˆC¦ÌŸ‡¡œ'-ΫD1º÷ûÚ쇖¯›}G«óvß¿7{yü²ÛÛÖÛ¬]Ïe­¯ÖÉ¢‘¼Øb"sÐcµãßöÎñŸÄ|bÑŠ«ùþûLÊ \ No newline at end of file
diff --git a/tests/resources/diff_format_email/.gitted/objects/89/7d3af16ca9e420cd071b1c4541bd2b91d04c8c b/tests/resources/diff_format_email/.gitted/objects/89/7d3af16ca9e420cd071b1c4541bd2b91d04c8c
new file mode 100644
index 0000000..1dce143
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/89/7d3af16ca9e420cd071b1c4541bd2b91d04c8c
@@ -0,0 +1 @@
+x¥ÎANÄ0 @QÖ9…÷HȵÓ$•šY!q '±™ éÒt1·g$ŽÀö-¾~ÙÖµ ŸžFWb[hñ¡²qÊ´ˆXY“Æ)™•9Qðî[ºÞ¤È ƒ£¨'͘‰Õç9dÊ&eò‘ã²uøòsèïÚ׶_Žû¯_öyÚGo2ä¥lëL¼D3§ÏHˆî¡Õ¡ÿŠ¸s­Z!·›ô;X»ªû"Pq \ No newline at end of file
diff --git a/tests/resources/diff_format_email/.gitted/objects/8c/637df9661edd808932b2f5d383f2c41ced9f06 b/tests/resources/diff_format_email/.gitted/objects/8c/637df9661edd808932b2f5d383f2c41ced9f06
new file mode 100644
index 0000000..e7d672b
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/8c/637df9661edd808932b2f5d383f2c41ced9f06
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/8d/7523f6fcb2404257889abe0d96f093d9f524f9 b/tests/resources/diff_format_email/.gitted/objects/8d/7523f6fcb2404257889abe0d96f093d9f524f9
new file mode 100644
index 0000000..903ec75
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/8d/7523f6fcb2404257889abe0d96f093d9f524f9
@@ -0,0 +1 @@
+x¥ÎAJÅ0€a×9Åì™IÓ¤w‚à!f&_äõUÓtñnoÁ#¸ý¿nëÚø™F7JZ¹LÑòT$%ŒBQ¬ÎV—à…ÉfeŒ,î›»Ý,9•‰+EålÁ£L$¤a$ÅK¦‚Au|ŒËÖáõç°Þ¬¯m¿÷ž¿þÚçë>zãÁOº­/@SNOfGôˆî¬çê°!îc+­6+ íÆýµ]ÍýERÚ \ No newline at end of file
diff --git a/tests/resources/diff_format_email/.gitted/objects/8d/fa038554d5b682a51bda8ee3038cee6c63be76 b/tests/resources/diff_format_email/.gitted/objects/8d/fa038554d5b682a51bda8ee3038cee6c63be76
new file mode 100644
index 0000000..b5e08f9
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/8d/fa038554d5b682a51bda8ee3038cee6c63be76
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/92/64b96c6d104d0e07ae33d3007b6a48246c6f92 b/tests/resources/diff_format_email/.gitted/objects/92/64b96c6d104d0e07ae33d3007b6a48246c6f92
new file mode 100644
index 0000000..75b047a
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/92/64b96c6d104d0e07ae33d3007b6a48246c6f92
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/94/350226b3aa14efac831c803a51f7a09f3fc31a b/tests/resources/diff_format_email/.gitted/objects/94/350226b3aa14efac831c803a51f7a09f3fc31a
new file mode 100644
index 0000000..a5286bc
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/94/350226b3aa14efac831c803a51f7a09f3fc31a
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/94/75e21dcbc515af8f641576400e4b450e5f4c03 b/tests/resources/diff_format_email/.gitted/objects/94/75e21dcbc515af8f641576400e4b450e5f4c03
new file mode 100644
index 0000000..9e26e34
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/94/75e21dcbc515af8f641576400e4b450e5f4c03
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/94/aaae8954e8bb613de636071da663a621695911 b/tests/resources/diff_format_email/.gitted/objects/94/aaae8954e8bb613de636071da663a621695911
new file mode 100644
index 0000000..5fc167e
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/94/aaae8954e8bb613de636071da663a621695911
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/94/f84b3e65e8bcbe8bffef2c885339343a802dd4 b/tests/resources/diff_format_email/.gitted/objects/94/f84b3e65e8bcbe8bffef2c885339343a802dd4
new file mode 100644
index 0000000..03b4848
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/94/f84b3e65e8bcbe8bffef2c885339343a802dd4
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/9a/2d780ac2ea0aeabdb9d2a876e6bbfff17b2c44 b/tests/resources/diff_format_email/.gitted/objects/9a/2d780ac2ea0aeabdb9d2a876e6bbfff17b2c44
new file mode 100644
index 0000000..be48c27
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/9a/2d780ac2ea0aeabdb9d2a876e6bbfff17b2c44
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/9a/c0329b8b7a4046210d8b8b02ac02055667de63 b/tests/resources/diff_format_email/.gitted/objects/9a/c0329b8b7a4046210d8b8b02ac02055667de63
new file mode 100644
index 0000000..f322a7c
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/9a/c0329b8b7a4046210d8b8b02ac02055667de63
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/9a/c35ff15cd8864aeafd889e4826a3150f0b06c4 b/tests/resources/diff_format_email/.gitted/objects/9a/c35ff15cd8864aeafd889e4826a3150f0b06c4
new file mode 100644
index 0000000..75cd714
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/9a/c35ff15cd8864aeafd889e4826a3150f0b06c4
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/9b/997daca2a0beb5cc44b32c64f100a9a26d4d4b b/tests/resources/diff_format_email/.gitted/objects/9b/997daca2a0beb5cc44b32c64f100a9a26d4d4b
new file mode 100644
index 0000000..d84ab8d
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/9b/997daca2a0beb5cc44b32c64f100a9a26d4d4b
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/a3/ac918e3a6604294b239cb956363e83d71abb3b b/tests/resources/diff_format_email/.gitted/objects/a3/ac918e3a6604294b239cb956363e83d71abb3b
new file mode 100644
index 0000000..a693df5
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/a3/ac918e3a6604294b239cb956363e83d71abb3b
@@ -0,0 +1 @@
+x¥ÎMJ1@a×9Eí©TçDf7 xˆJRåD¦§5^Ìíðn¿ÅãÕm]ûòöih±çbSK5pâÂ)RDô.‘²V­¥Ù|óÛ„Ô¢§EƒÖBù˜Ræ"ØrPÌKËêÉi6|ÌË6àëÏ!;œe¬}¿÷^¿þìó´ÏÑyòKÝÖ7°KŽC ÏHˆæ¡Õ)ÿŠ˜­uíÒ ô;h¿ŠùÜüQK \ No newline at end of file
diff --git a/tests/resources/diff_format_email/.gitted/objects/a5/ac978d4f2a1784f847f41223a34c3e78934238 b/tests/resources/diff_format_email/.gitted/objects/a5/ac978d4f2a1784f847f41223a34c3e78934238
new file mode 100644
index 0000000..f048394
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/a5/ac978d4f2a1784f847f41223a34c3e78934238
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/a7/29eab45c84563135e8631d4010230bc0479f1f b/tests/resources/diff_format_email/.gitted/objects/a7/29eab45c84563135e8631d4010230bc0479f1f
new file mode 100644
index 0000000..5c1faf0
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/a7/29eab45c84563135e8631d4010230bc0479f1f
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/a7/a65f98355b5a7567bcc395f6f7936c9252cf57 b/tests/resources/diff_format_email/.gitted/objects/a7/a65f98355b5a7567bcc395f6f7936c9252cf57
new file mode 100644
index 0000000..36a4b7b
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/a7/a65f98355b5a7567bcc395f6f7936c9252cf57
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/a9/7157a0d0571698728b6f2f7675b456c98c5961 b/tests/resources/diff_format_email/.gitted/objects/a9/7157a0d0571698728b6f2f7675b456c98c5961
new file mode 100644
index 0000000..3baf494
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/a9/7157a0d0571698728b6f2f7675b456c98c5961
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/af/8f41d0cb7a3079a8f8e231ea2ab8b97837ce13 b/tests/resources/diff_format_email/.gitted/objects/af/8f41d0cb7a3079a8f8e231ea2ab8b97837ce13
new file mode 100644
index 0000000..f0dcaa9
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/af/8f41d0cb7a3079a8f8e231ea2ab8b97837ce13
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/b0/5cecf1949d192b6df852b3f71853ef820ee235 b/tests/resources/diff_format_email/.gitted/objects/b0/5cecf1949d192b6df852b3f71853ef820ee235
new file mode 100644
index 0000000..28e148f
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/b0/5cecf1949d192b6df852b3f71853ef820ee235
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/b4/f457c219dbb3517be908d4e70f0ada2fd8b8f9 b/tests/resources/diff_format_email/.gitted/objects/b4/f457c219dbb3517be908d4e70f0ada2fd8b8f9
new file mode 100644
index 0000000..0c74e76
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/b4/f457c219dbb3517be908d4e70f0ada2fd8b8f9
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/bb/07a00e58b260c4eb9a82f8afbc1d80ad9739bf b/tests/resources/diff_format_email/.gitted/objects/bb/07a00e58b260c4eb9a82f8afbc1d80ad9739bf
new file mode 100644
index 0000000..d2cb6d0
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/bb/07a00e58b260c4eb9a82f8afbc1d80ad9739bf
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/bd/474b2519cc15eab801ff851cc7d50f0dee49a1 b/tests/resources/diff_format_email/.gitted/objects/bd/474b2519cc15eab801ff851cc7d50f0dee49a1
new file mode 100644
index 0000000..2dc8208
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/bd/474b2519cc15eab801ff851cc7d50f0dee49a1
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/bd/f7ba6bc5c4e57ca6595928dcbe6753c8a663ff b/tests/resources/diff_format_email/.gitted/objects/bd/f7ba6bc5c4e57ca6595928dcbe6753c8a663ff
new file mode 100644
index 0000000..af0232a
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/bd/f7ba6bc5c4e57ca6595928dcbe6753c8a663ff
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/c7/1a05d36025c806496a74d46d7d596eb23295c4 b/tests/resources/diff_format_email/.gitted/objects/c7/1a05d36025c806496a74d46d7d596eb23295c4
new file mode 100644
index 0000000..2822fe3
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/c7/1a05d36025c806496a74d46d7d596eb23295c4
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/cb/a89408dc016f4caddb6dc886fcb58f587a78df b/tests/resources/diff_format_email/.gitted/objects/cb/a89408dc016f4caddb6dc886fcb58f587a78df
new file mode 100644
index 0000000..e03c749
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/cb/a89408dc016f4caddb6dc886fcb58f587a78df
@@ -0,0 +1,3 @@
+x¥ÎK
+Â0FaÇYE悤¹ÍCq&8t7É[!­¦éÀÝ[p N¿ÁáĹ”±IM~×* }ʬÈÓ'¬×lºØ´il´à¬xqÅÔ¤wä•Í6kK‚
+šÐcX‡Ì±ë™ ^Û0Wyãø^±È+j—aý,òôüÙã²´:rãCœËYvttZUOr¯´RbÓmµá¯ˆ¸câ‚$¾’ÏM‘ \ No newline at end of file
diff --git a/tests/resources/diff_format_email/.gitted/objects/cd/471f0d8770371e1bc78bcbb38db4c7e4106bd2 b/tests/resources/diff_format_email/.gitted/objects/cd/471f0d8770371e1bc78bcbb38db4c7e4106bd2
new file mode 100644
index 0000000..155b452
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/cd/471f0d8770371e1bc78bcbb38db4c7e4106bd2
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/cd/ed722d05305c6b181f188c118d2d9810f39bb8 b/tests/resources/diff_format_email/.gitted/objects/cd/ed722d05305c6b181f188c118d2d9810f39bb8
new file mode 100644
index 0000000..fd93636
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/cd/ed722d05305c6b181f188c118d2d9810f39bb8
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/ce/2792fcae8d704a56901754a0583a7418a21d8a b/tests/resources/diff_format_email/.gitted/objects/ce/2792fcae8d704a56901754a0583a7418a21d8a
new file mode 100644
index 0000000..5863cec
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/ce/2792fcae8d704a56901754a0583a7418a21d8a
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/d1/4aa252e52a709d03a3d3d0d965e177eb0a674e b/tests/resources/diff_format_email/.gitted/objects/d1/4aa252e52a709d03a3d3d0d965e177eb0a674e
new file mode 100644
index 0000000..a5d4d78
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/d1/4aa252e52a709d03a3d3d0d965e177eb0a674e
@@ -0,0 +1 @@
+x+)JMU01e040075UHËÌI5Ô+©(Ñ+JÍKÌMMaXY¾àB¸ØŒ¢î|ý²Ò-a'{"Íz \ No newline at end of file
diff --git a/tests/resources/diff_format_email/.gitted/objects/d3/b6b38486f620b5b532a8cc6e0198ab7c3f52e4 b/tests/resources/diff_format_email/.gitted/objects/d3/b6b38486f620b5b532a8cc6e0198ab7c3f52e4
new file mode 100644
index 0000000..5b2e664
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/d3/b6b38486f620b5b532a8cc6e0198ab7c3f52e4
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/d5/ff67764c82f729b13c26a09576570d884d9687 b/tests/resources/diff_format_email/.gitted/objects/d5/ff67764c82f729b13c26a09576570d884d9687
new file mode 100644
index 0000000..e838eeb
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/d5/ff67764c82f729b13c26a09576570d884d9687
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/d7/bb447df12c6a8aba8727005482fb211f11297a b/tests/resources/diff_format_email/.gitted/objects/d7/bb447df12c6a8aba8727005482fb211f11297a
new file mode 100644
index 0000000..85eb814
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/d7/bb447df12c6a8aba8727005482fb211f11297a
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/db/e8727e4806ae88ccc3f0755cae8f8cb7efa2cc b/tests/resources/diff_format_email/.gitted/objects/db/e8727e4806ae88ccc3f0755cae8f8cb7efa2cc
new file mode 100644
index 0000000..8e250ec
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/db/e8727e4806ae88ccc3f0755cae8f8cb7efa2cc
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/e1/2af77c510e8ce4c261a3758736109c2c2dd1f0 b/tests/resources/diff_format_email/.gitted/objects/e1/2af77c510e8ce4c261a3758736109c2c2dd1f0
new file mode 100644
index 0000000..92a89a2
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/e1/2af77c510e8ce4c261a3758736109c2c2dd1f0
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/diff_format_email/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644
index 0000000..7112238
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/e9/091231467304a5ef112de02361d795ef051ee1 b/tests/resources/diff_format_email/.gitted/objects/e9/091231467304a5ef112de02361d795ef051ee1
new file mode 100644
index 0000000..43a0062
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/e9/091231467304a5ef112de02361d795ef051ee1
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/ee/251372f131d82e575f16fe51c778406d88f8c2 b/tests/resources/diff_format_email/.gitted/objects/ee/251372f131d82e575f16fe51c778406d88f8c2
new file mode 100644
index 0000000..ee1edad
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/ee/251372f131d82e575f16fe51c778406d88f8c2
@@ -0,0 +1,2 @@
+x¥ÎM
+Â0@a×9Eö‚dò3“€ˆ+o‘¦¡¶¦)èí-x·ßâñÒ4Ž¥Imp×*³Ô9Sç˜u$\0NuÉØ1Û^%ïu/æXùÙ$eï,B°P«L¹Kœˆ# 9Fp‰ƒîPĵ S•·˜^+/òÊu,Ë°~y|üì~^Z-±ÅCšÆ“H‘²hå^i¥Ä¦Ûjã¿"âRÞë,¾HrKï \ No newline at end of file
diff --git a/tests/resources/diff_format_email/.gitted/objects/f3/d35bd592fefd8280fc0c302fa9f27dbdd721a3 b/tests/resources/diff_format_email/.gitted/objects/f3/d35bd592fefd8280fc0c302fa9f27dbdd721a3
new file mode 100644
index 0000000..62d747c
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/f3/d35bd592fefd8280fc0c302fa9f27dbdd721a3
@@ -0,0 +1 @@
+x¥ÍA @Qלbö&fhi)‰1îŒÞb€A1b#LÞÞ&=‚Û¿x?Ì¥díìN*3x“Ì`C§]ô¾´õìpŠ†-&¤H]Š“Ÿ’S´Èc®p£ðY¸Á…kÉí±|Ÿ[»Ÿ›ÔLB‡0—èÞYûq4°ÇQ­u] ÿ…¨ë;K¦lšúH°B¿ \ No newline at end of file
diff --git a/tests/resources/diff_format_email/.gitted/objects/f4/07be01334e07bfb8f57cd2078f0ee3eb61e085 b/tests/resources/diff_format_email/.gitted/objects/f4/07be01334e07bfb8f57cd2078f0ee3eb61e085
new file mode 100644
index 0000000..d858a87
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/f4/07be01334e07bfb8f57cd2078f0ee3eb61e085
@@ -0,0 +1 @@
+x¥ÎMNÄ0 @aÖ9…÷”8®ÓH±CBâNâ™v íL~Üž‘8Ûoñôò±mk¤øЫ*pÒÅ„–Æ31M¬6L1'4'rJÙ\¥êÞ!„bÅkñ=KˆäÊÄt²sá¹øä™ zgdôå¨ð!ù6´Á»ÖmmËøiðrù³ó[ëu•.ÏùØ^ÁùК<Z´ÖÜõ¾Úõ_ó9¾ûú´Œý ò"ûY›ùû÷PR \ No newline at end of file
diff --git a/tests/resources/diff_format_email/.gitted/objects/f9/e215d309644e24fa50d6bd6e6eedba166e56bc b/tests/resources/diff_format_email/.gitted/objects/f9/e215d309644e24fa50d6bd6e6eedba166e56bc
new file mode 100644
index 0000000..b9919c2
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/f9/e215d309644e24fa50d6bd6e6eedba166e56bc
@@ -0,0 +1,2 @@
+x¥ÎM
+Â0@a×9Eö‚ÌLÚL"®¼ÅäG­[Óôö<‚Ûoñxq,ehšŒÝ´š³î9ú®· ЊÌH‰¼H`¬Ãè\{ðj’šŸMwÑ„ÄèH’µYX„ HÇ’9¤À ­J–v«¾H|-yÖç\Ë0ߗϬ÷ŸÝŽs«ƒ4Ùű4ÏÀÐé-€Zu]mù¯ˆ: ïeR_K§ \ No newline at end of file
diff --git a/tests/resources/diff_format_email/.gitted/objects/fc/a0c10eb9f1af6494a448d5733d283f5232a514 b/tests/resources/diff_format_email/.gitted/objects/fc/a0c10eb9f1af6494a448d5733d283f5232a514
new file mode 100644
index 0000000..776221d
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/fc/a0c10eb9f1af6494a448d5733d283f5232a514
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/objects/ff/8d35b41494f7f0dc92f95d67f54fff274d3fcb b/tests/resources/diff_format_email/.gitted/objects/ff/8d35b41494f7f0dc92f95d67f54fff274d3fcb
new file mode 100644
index 0000000..9296425
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/objects/ff/8d35b41494f7f0dc92f95d67f54fff274d3fcb
Binary files differ
diff --git a/tests/resources/diff_format_email/.gitted/refs/heads/binary b/tests/resources/diff_format_email/.gitted/refs/heads/binary
new file mode 100644
index 0000000..7e563c9
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/refs/heads/binary
@@ -0,0 +1 @@
+a3ac918e3a6604294b239cb956363e83d71abb3b
diff --git a/tests/resources/diff_format_email/.gitted/refs/heads/master b/tests/resources/diff_format_email/.gitted/refs/heads/master
new file mode 100644
index 0000000..7d5e03f
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/refs/heads/master
@@ -0,0 +1 @@
+0db2a262bc8c5c3cba55254730045a8258da7a37
diff --git a/tests/resources/diff_format_email/.gitted/refs/heads/multihunk b/tests/resources/diff_format_email/.gitted/refs/heads/multihunk
new file mode 100644
index 0000000..41bd37f
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/refs/heads/multihunk
@@ -0,0 +1 @@
+cd471f0d8770371e1bc78bcbb38db4c7e4106bd2
diff --git a/tests/resources/diff_format_email/.gitted/refs/heads/rename b/tests/resources/diff_format_email/.gitted/refs/heads/rename
new file mode 100644
index 0000000..3025fbc
--- /dev/null
+++ b/tests/resources/diff_format_email/.gitted/refs/heads/rename
@@ -0,0 +1 @@
+4ca10087e696d2ba78d07b146a118e9a7096ed4f
diff --git a/tests/resources/diff_format_email/dir/renamed.txt b/tests/resources/diff_format_email/dir/renamed.txt
new file mode 100644
index 0000000..7aff11d
--- /dev/null
+++ b/tests/resources/diff_format_email/dir/renamed.txt
@@ -0,0 +1,5 @@
+file2
+file2
+file2
+file2!
+file2
diff --git a/tests/resources/diff_format_email/file1.txt.renamed b/tests/resources/diff_format_email/file1.txt.renamed
new file mode 100755
index 0000000..a97157a
--- /dev/null
+++ b/tests/resources/diff_format_email/file1.txt.renamed
@@ -0,0 +1,17 @@
+file1.txt
+file1.txt
+_file1.txt_
+file1.txt
+file1.txt
+file1.txt_renamed
+file1.txt
+
+
+file1.txt
+file1.txt
+file1.txt_renamed
+file1.txt
+file1.txt
+_file1.txt_
+_file1.txt_
+file1.txt
diff --git a/tests/resources/diff_format_email/file2.txt b/tests/resources/diff_format_email/file2.txt
new file mode 100644
index 0000000..7aff11d
--- /dev/null
+++ b/tests/resources/diff_format_email/file2.txt
@@ -0,0 +1,5 @@
+file2
+file2
+file2
+file2!
+file2
diff --git a/tests/resources/diff_format_email/file3.txt b/tests/resources/diff_format_email/file3.txt
new file mode 100644
index 0000000..c71a05d
--- /dev/null
+++ b/tests/resources/diff_format_email/file3.txt
@@ -0,0 +1,7 @@
+file3
+file3!
+file3
+file3
+file3
+file3
+file3
diff --git a/tests/resources/duplicate.git/COMMIT_EDITMSG b/tests/resources/duplicate.git/COMMIT_EDITMSG
new file mode 100644
index 0000000..01f9a2a
--- /dev/null
+++ b/tests/resources/duplicate.git/COMMIT_EDITMSG
@@ -0,0 +1 @@
+commit
diff --git a/tests/resources/duplicate.git/HEAD b/tests/resources/duplicate.git/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/duplicate.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/duplicate.git/config b/tests/resources/duplicate.git/config
new file mode 100644
index 0000000..a4ef456
--- /dev/null
+++ b/tests/resources/duplicate.git/config
@@ -0,0 +1,5 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = true
+ logallrefupdates = true
diff --git a/tests/resources/duplicate.git/description b/tests/resources/duplicate.git/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/duplicate.git/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/duplicate.git/index b/tests/resources/duplicate.git/index
new file mode 100644
index 0000000..a61e1c5
--- /dev/null
+++ b/tests/resources/duplicate.git/index
Binary files differ
diff --git a/tests/resources/duplicate.git/info/exclude b/tests/resources/duplicate.git/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/duplicate.git/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/duplicate.git/info/refs b/tests/resources/duplicate.git/info/refs
new file mode 100644
index 0000000..3b5bb03
--- /dev/null
+++ b/tests/resources/duplicate.git/info/refs
@@ -0,0 +1 @@
+8d2f05c97ef29a4697b37c30fe81c248ef411a23 refs/heads/master
diff --git a/tests/resources/duplicate.git/logs/HEAD b/tests/resources/duplicate.git/logs/HEAD
new file mode 100644
index 0000000..be9b4c6
--- /dev/null
+++ b/tests/resources/duplicate.git/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 8d2f05c97ef29a4697b37c30fe81c248ef411a23 Han-Wen Nienhuys <hanwen@xs4all.nl> 1336844322 -0300 commit (initial): commit
diff --git a/tests/resources/duplicate.git/logs/refs/heads/master b/tests/resources/duplicate.git/logs/refs/heads/master
new file mode 100644
index 0000000..be9b4c6
--- /dev/null
+++ b/tests/resources/duplicate.git/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 8d2f05c97ef29a4697b37c30fe81c248ef411a23 Han-Wen Nienhuys <hanwen@xs4all.nl> 1336844322 -0300 commit (initial): commit
diff --git a/tests/resources/duplicate.git/objects/03/8d718da6a1ebbc6a7780a96ed75a70cc2ad6e2 b/tests/resources/duplicate.git/objects/03/8d718da6a1ebbc6a7780a96ed75a70cc2ad6e2
new file mode 100644
index 0000000..7350d98
--- /dev/null
+++ b/tests/resources/duplicate.git/objects/03/8d718da6a1ebbc6a7780a96ed75a70cc2ad6e2
Binary files differ
diff --git a/tests/resources/duplicate.git/objects/0d/deadede9e6d6ccddce0ee1e5749eed0485e5ea b/tests/resources/duplicate.git/objects/0d/deadede9e6d6ccddce0ee1e5749eed0485e5ea
new file mode 100644
index 0000000..47c2a63
--- /dev/null
+++ b/tests/resources/duplicate.git/objects/0d/deadede9e6d6ccddce0ee1e5749eed0485e5ea
Binary files differ
diff --git a/tests/resources/duplicate.git/objects/ce/013625030ba8dba906f756967f9e9ca394464a b/tests/resources/duplicate.git/objects/ce/013625030ba8dba906f756967f9e9ca394464a
new file mode 100644
index 0000000..6802d49
--- /dev/null
+++ b/tests/resources/duplicate.git/objects/ce/013625030ba8dba906f756967f9e9ca394464a
Binary files differ
diff --git a/tests/resources/duplicate.git/objects/info/packs b/tests/resources/duplicate.git/objects/info/packs
new file mode 100644
index 0000000..d0fdf90
--- /dev/null
+++ b/tests/resources/duplicate.git/objects/info/packs
@@ -0,0 +1,3 @@
+P pack-e87994ad581c9af946de0eb890175c08cd005f38.pack
+P pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.pack
+
diff --git a/tests/resources/duplicate.git/objects/pack/pack-29a4896f0a0b9c9947b0927c57a5c03dcae052e3.idx b/tests/resources/duplicate.git/objects/pack/pack-29a4896f0a0b9c9947b0927c57a5c03dcae052e3.idx
new file mode 100644
index 0000000..acbed82
--- /dev/null
+++ b/tests/resources/duplicate.git/objects/pack/pack-29a4896f0a0b9c9947b0927c57a5c03dcae052e3.idx
Binary files differ
diff --git a/tests/resources/duplicate.git/objects/pack/pack-29a4896f0a0b9c9947b0927c57a5c03dcae052e3.pack b/tests/resources/duplicate.git/objects/pack/pack-29a4896f0a0b9c9947b0927c57a5c03dcae052e3.pack
new file mode 100644
index 0000000..652b0c9
--- /dev/null
+++ b/tests/resources/duplicate.git/objects/pack/pack-29a4896f0a0b9c9947b0927c57a5c03dcae052e3.pack
Binary files differ
diff --git a/tests/resources/duplicate.git/objects/pack/pack-b18eeacbd65cbd30a365d7564b45a468e8bd43d6.idx b/tests/resources/duplicate.git/objects/pack/pack-b18eeacbd65cbd30a365d7564b45a468e8bd43d6.idx
new file mode 100644
index 0000000..fff6855
--- /dev/null
+++ b/tests/resources/duplicate.git/objects/pack/pack-b18eeacbd65cbd30a365d7564b45a468e8bd43d6.idx
Binary files differ
diff --git a/tests/resources/duplicate.git/objects/pack/pack-b18eeacbd65cbd30a365d7564b45a468e8bd43d6.pack b/tests/resources/duplicate.git/objects/pack/pack-b18eeacbd65cbd30a365d7564b45a468e8bd43d6.pack
new file mode 100644
index 0000000..e3e5f0e
--- /dev/null
+++ b/tests/resources/duplicate.git/objects/pack/pack-b18eeacbd65cbd30a365d7564b45a468e8bd43d6.pack
Binary files differ
diff --git a/tests/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.idx b/tests/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.idx
new file mode 100644
index 0000000..fd8abee
--- /dev/null
+++ b/tests/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.idx
Binary files differ
diff --git a/tests/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.pack b/tests/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.pack
new file mode 100644
index 0000000..9879e08
--- /dev/null
+++ b/tests/resources/duplicate.git/objects/pack/pack-e87994ad581c9af946de0eb890175c08cd005f38.pack
Binary files differ
diff --git a/tests/resources/duplicate.git/objects/pack/pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.idx b/tests/resources/duplicate.git/objects/pack/pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.idx
new file mode 100644
index 0000000..9f78f6e
--- /dev/null
+++ b/tests/resources/duplicate.git/objects/pack/pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.idx
Binary files differ
diff --git a/tests/resources/duplicate.git/objects/pack/pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.pack b/tests/resources/duplicate.git/objects/pack/pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.pack
new file mode 100644
index 0000000..d1dd3b6
--- /dev/null
+++ b/tests/resources/duplicate.git/objects/pack/pack-f4ef1aa326265de7d05018ee51acc0a8717fe1ea.pack
Binary files differ
diff --git a/tests/resources/duplicate.git/packed-refs b/tests/resources/duplicate.git/packed-refs
new file mode 100644
index 0000000..9f0d4e4
--- /dev/null
+++ b/tests/resources/duplicate.git/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+8d2f05c97ef29a4697b37c30fe81c248ef411a23 refs/heads/master
diff --git a/tests/resources/duplicate.git/refs/heads/dummy-marker.txt b/tests/resources/duplicate.git/refs/heads/dummy-marker.txt
new file mode 100644
index 0000000..421376d
--- /dev/null
+++ b/tests/resources/duplicate.git/refs/heads/dummy-marker.txt
@@ -0,0 +1 @@
+dummy
diff --git a/tests/resources/empty_bare.git/HEAD b/tests/resources/empty_bare.git/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/empty_bare.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/empty_bare.git/config b/tests/resources/empty_bare.git/config
new file mode 100644
index 0000000..90e1647
--- /dev/null
+++ b/tests/resources/empty_bare.git/config
@@ -0,0 +1,7 @@
+[core]
+ repositoryformatversion = 0
+ filemode = false
+ bare = true
+ symlinks = false
+ ignorecase = true
+ hideDotFiles = dotGitOnly
diff --git a/tests/resources/empty_bare.git/description b/tests/resources/empty_bare.git/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/empty_bare.git/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/empty_bare.git/info/exclude b/tests/resources/empty_bare.git/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/empty_bare.git/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/empty_bare.git/objects/info/dummy-marker.txt b/tests/resources/empty_bare.git/objects/info/dummy-marker.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/empty_bare.git/objects/info/dummy-marker.txt
diff --git a/tests/resources/empty_bare.git/objects/pack/dummy-marker.txt b/tests/resources/empty_bare.git/objects/pack/dummy-marker.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/empty_bare.git/objects/pack/dummy-marker.txt
diff --git a/tests/resources/empty_bare.git/refs/heads/dummy-marker.txt b/tests/resources/empty_bare.git/refs/heads/dummy-marker.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/empty_bare.git/refs/heads/dummy-marker.txt
diff --git a/tests/resources/empty_bare.git/refs/tags/dummy-marker.txt b/tests/resources/empty_bare.git/refs/tags/dummy-marker.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/empty_bare.git/refs/tags/dummy-marker.txt
diff --git a/tests/resources/empty_standard_repo/.gitted/HEAD b/tests/resources/empty_standard_repo/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/empty_standard_repo/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/empty_standard_repo/.gitted/config b/tests/resources/empty_standard_repo/.gitted/config
new file mode 100644
index 0000000..78387c5
--- /dev/null
+++ b/tests/resources/empty_standard_repo/.gitted/config
@@ -0,0 +1,8 @@
+[core]
+ repositoryformatversion = 0
+ filemode = false
+ bare = false
+ logallrefupdates = true
+ symlinks = false
+ ignorecase = true
+ hideDotFiles = dotGitOnly
diff --git a/tests/resources/empty_standard_repo/.gitted/description b/tests/resources/empty_standard_repo/.gitted/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/empty_standard_repo/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/empty_standard_repo/.gitted/info/exclude b/tests/resources/empty_standard_repo/.gitted/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/empty_standard_repo/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/empty_standard_repo/.gitted/objects/info/dummy-marker.txt b/tests/resources/empty_standard_repo/.gitted/objects/info/dummy-marker.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/empty_standard_repo/.gitted/objects/info/dummy-marker.txt
diff --git a/tests/resources/empty_standard_repo/.gitted/objects/pack/dummy-marker.txt b/tests/resources/empty_standard_repo/.gitted/objects/pack/dummy-marker.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/empty_standard_repo/.gitted/objects/pack/dummy-marker.txt
diff --git a/tests/resources/empty_standard_repo/.gitted/refs/heads/dummy-marker.txt b/tests/resources/empty_standard_repo/.gitted/refs/heads/dummy-marker.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/empty_standard_repo/.gitted/refs/heads/dummy-marker.txt
diff --git a/tests/resources/empty_standard_repo/.gitted/refs/tags/dummy-marker.txt b/tests/resources/empty_standard_repo/.gitted/refs/tags/dummy-marker.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/empty_standard_repo/.gitted/refs/tags/dummy-marker.txt
diff --git a/tests/resources/filemodes/.gitted/HEAD b/tests/resources/filemodes/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/filemodes/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/filemodes/.gitted/config b/tests/resources/filemodes/.gitted/config
new file mode 100644
index 0000000..af10792
--- /dev/null
+++ b/tests/resources/filemodes/.gitted/config
@@ -0,0 +1,6 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
diff --git a/tests/resources/filemodes/.gitted/description b/tests/resources/filemodes/.gitted/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/filemodes/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/filemodes/.gitted/index b/tests/resources/filemodes/.gitted/index
new file mode 100644
index 0000000..b1b175a
--- /dev/null
+++ b/tests/resources/filemodes/.gitted/index
Binary files differ
diff --git a/tests/resources/filemodes/.gitted/info/exclude b/tests/resources/filemodes/.gitted/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/filemodes/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/filemodes/.gitted/logs/HEAD b/tests/resources/filemodes/.gitted/logs/HEAD
new file mode 100644
index 0000000..1cb6a84
--- /dev/null
+++ b/tests/resources/filemodes/.gitted/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 9962c8453ba6f0cf8dac7c5dcc2fa2897fa9964a Russell Belfer <rb@github.com> 1338847682 -0700 commit (initial): Initial commit of test data
diff --git a/tests/resources/filemodes/.gitted/logs/refs/heads/master b/tests/resources/filemodes/.gitted/logs/refs/heads/master
new file mode 100644
index 0000000..1cb6a84
--- /dev/null
+++ b/tests/resources/filemodes/.gitted/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 9962c8453ba6f0cf8dac7c5dcc2fa2897fa9964a Russell Belfer <rb@github.com> 1338847682 -0700 commit (initial): Initial commit of test data
diff --git a/tests/resources/filemodes/.gitted/objects/99/62c8453ba6f0cf8dac7c5dcc2fa2897fa9964a b/tests/resources/filemodes/.gitted/objects/99/62c8453ba6f0cf8dac7c5dcc2fa2897fa9964a
new file mode 100644
index 0000000..cbd2b55
--- /dev/null
+++ b/tests/resources/filemodes/.gitted/objects/99/62c8453ba6f0cf8dac7c5dcc2fa2897fa9964a
Binary files differ
diff --git a/tests/resources/filemodes/.gitted/objects/a5/c5dd0fc6c313159a69b1d19d7f61a9f978e8f1 b/tests/resources/filemodes/.gitted/objects/a5/c5dd0fc6c313159a69b1d19d7f61a9f978e8f1
new file mode 100644
index 0000000..a9eaf2c
--- /dev/null
+++ b/tests/resources/filemodes/.gitted/objects/a5/c5dd0fc6c313159a69b1d19d7f61a9f978e8f1
Binary files differ
diff --git a/tests/resources/filemodes/.gitted/objects/e7/48d196331bcb20267eaaee4ff3326cb73b8182 b/tests/resources/filemodes/.gitted/objects/e7/48d196331bcb20267eaaee4ff3326cb73b8182
new file mode 100644
index 0000000..9806602
--- /dev/null
+++ b/tests/resources/filemodes/.gitted/objects/e7/48d196331bcb20267eaaee4ff3326cb73b8182
Binary files differ
diff --git a/tests/resources/filemodes/.gitted/refs/heads/master b/tests/resources/filemodes/.gitted/refs/heads/master
new file mode 100644
index 0000000..9822d2d
--- /dev/null
+++ b/tests/resources/filemodes/.gitted/refs/heads/master
@@ -0,0 +1 @@
+9962c8453ba6f0cf8dac7c5dcc2fa2897fa9964a
diff --git a/tests/resources/filemodes/exec_off b/tests/resources/filemodes/exec_off
new file mode 100644
index 0000000..a5c5dd0
--- /dev/null
+++ b/tests/resources/filemodes/exec_off
@@ -0,0 +1 @@
+Howdy
diff --git a/tests/resources/filemodes/exec_off2on_staged b/tests/resources/filemodes/exec_off2on_staged
new file mode 100755
index 0000000..a5c5dd0
--- /dev/null
+++ b/tests/resources/filemodes/exec_off2on_staged
@@ -0,0 +1 @@
+Howdy
diff --git a/tests/resources/filemodes/exec_off2on_workdir b/tests/resources/filemodes/exec_off2on_workdir
new file mode 100755
index 0000000..a5c5dd0
--- /dev/null
+++ b/tests/resources/filemodes/exec_off2on_workdir
@@ -0,0 +1 @@
+Howdy
diff --git a/tests/resources/filemodes/exec_off_untracked b/tests/resources/filemodes/exec_off_untracked
new file mode 100644
index 0000000..a5c5dd0
--- /dev/null
+++ b/tests/resources/filemodes/exec_off_untracked
@@ -0,0 +1 @@
+Howdy
diff --git a/tests/resources/filemodes/exec_on b/tests/resources/filemodes/exec_on
new file mode 100755
index 0000000..a5c5dd0
--- /dev/null
+++ b/tests/resources/filemodes/exec_on
@@ -0,0 +1 @@
+Howdy
diff --git a/tests/resources/filemodes/exec_on2off_staged b/tests/resources/filemodes/exec_on2off_staged
new file mode 100644
index 0000000..a5c5dd0
--- /dev/null
+++ b/tests/resources/filemodes/exec_on2off_staged
@@ -0,0 +1 @@
+Howdy
diff --git a/tests/resources/filemodes/exec_on2off_workdir b/tests/resources/filemodes/exec_on2off_workdir
new file mode 100644
index 0000000..a5c5dd0
--- /dev/null
+++ b/tests/resources/filemodes/exec_on2off_workdir
@@ -0,0 +1 @@
+Howdy
diff --git a/tests/resources/filemodes/exec_on_untracked b/tests/resources/filemodes/exec_on_untracked
new file mode 100755
index 0000000..a5c5dd0
--- /dev/null
+++ b/tests/resources/filemodes/exec_on_untracked
@@ -0,0 +1 @@
+Howdy
diff --git a/tests/resources/generate_crlf.sh b/tests/resources/generate_crlf.sh
new file mode 100755
index 0000000..068cad0
--- /dev/null
+++ b/tests/resources/generate_crlf.sh
@@ -0,0 +1,147 @@
+#!/usr/bin/env bash
+#
+# This script will generate the test corpus for CR/LF data using git;
+# we create files with all possible line ending varieties (all LF, all
+# CRLF, mixed, etc) on all the possible line ending configurations
+# (`core.autocrlf=true`, `text=auto` in gitattributes, etc). This
+# allows us to validate that our configuration will match byte-for-byte
+# the configuration that git produces.
+#
+# To update the test resource data, from the test resource directory:
+# git rm -r ./crlf_data/{posix,windows}
+# sh ./generate_crlf.sh ./crlf ./crlf_data /tmp/crlf_gitdirs
+# git add ./crlf_data/{posix,windows}
+
+set -e
+
+if [ "$1" == "" -o "$2" == "" ]; then
+ echo "usage: $0 crlfrepo directory [tempdir]"
+ exit 1
+fi
+
+input=$1
+output=$2
+tempdir=$3
+
+set -u
+
+create_to_workdir_data() {
+ local input=$1
+ local output=$2
+ local tempdir=$3
+ local systype=$4
+ local autocrlf=$5
+ local attr=$6
+
+ local worktree="${output}/${systype}_to_workdir/autocrlf_${autocrlf}"
+
+ if [ "$attr" != "" ]; then
+ local attrdir=`echo $attr | sed -e "s/ /,/g" | sed -e "s/=/_/g"`
+ worktree="${worktree},${attrdir}"
+ fi
+
+ if [ "$tempdir" = "" ]; then
+ local gitdir="${worktree}/.git"
+ else
+ local gitdir="${tempdir}/generate_crlf_${RANDOM}"
+ fi
+
+ echo "Creating ${worktree}"
+ mkdir -p "${worktree}"
+
+ git clone --no-checkout --quiet --bare "${input}/.gitted" "${gitdir}"
+ git --work-tree="${worktree}" --git-dir="${gitdir}" config core.autocrlf ${autocrlf}
+
+ if [ "$attr" != "" ]; then
+ echo "* ${attr}" >> "${worktree}/.gitattributes"
+ fi
+
+ git --work-tree="${worktree}" --git-dir="${gitdir}" checkout HEAD
+
+ if [ "$attr" != "" ]; then
+ rm "${worktree}/.gitattributes"
+ fi
+
+ if [ "$tempdir" != "" ]; then
+ rm -rf "${gitdir}"
+ fi
+}
+
+create_to_odb_data() {
+ local input=$1
+ local output=$2
+ local tempdir=$3
+ local systype=$4
+ local autocrlf=$5
+ local safecrlf=$6
+ local attr=$7
+
+ local destdir="${output}/${systype}_to_odb/autocrlf_${autocrlf},safecrlf_${safecrlf}"
+
+ if [ "$attr" != "" ]; then
+ local attrdir=`echo $attr | sed -e "s/ /,/g" | sed -e "s/=/_/g"`
+ destdir="${destdir},${attrdir}"
+ fi
+
+ if [ "$tempdir" = "" ]; then
+ local workdir="${destdir}/_workdir"
+ else
+ local workdir="${tempdir}/generate_crlf_${RANDOM}"
+ fi
+
+ echo "Creating ${destdir}"
+ mkdir -p "${destdir}"
+
+ git init "${workdir}" >/dev/null
+ git --work-tree="${workdir}" --git-dir="${workdir}/.git" config core.autocrlf "${autocrlf}"
+ git --work-tree="${workdir}" --git-dir="${workdir}/.git" config core.safecrlf "${safecrlf}"
+
+ if [ "$attr" != "" ]; then
+ echo "* ${attr}" > "${workdir}/.gitattributes"
+ fi
+
+ cp ${input}/* ${workdir}
+
+ for path in ${workdir}/*; do
+ filename=$(basename $path)
+ failed=""
+ output=$(git --work-tree="${workdir}" --git-dir="${workdir}/.git" add ${filename} 2>&1) || failed=1
+
+ if [ ! -z "${failed}" -a "${output:0:35}" == "fatal: LF would be replaced by CRLF" ]; then
+ echo "LF would be replaced by CRLF in '${filename}'" > "${destdir}/${filename}.fail"
+ elif [ ! -z "${failed}" -a "${output:0:35}" == "fatal: CRLF would be replaced by LF" ]; then
+ echo "CRLF would be replaced by LF in '${filename}'" > "${destdir}/${filename}.fail"
+ elif [ ! -z "${failed}" ]; then
+ echo "failed to add ${filename}: ${output}" 1>&2
+ exit 1
+ else
+ git --work-tree="${workdir}" --git-dir="${workdir}/.git" cat-file blob ":${filename}" > "${destdir}/${filename}"
+ fi
+ done
+
+ if [ "$tempdir" != "" ]; then
+ rm -rf "${workdir}"
+ fi
+}
+
+if [[ `uname -s` == MINGW* ]]; then
+ systype="windows"
+else
+ systype="posix"
+fi
+
+for autocrlf in true false input; do
+ for attr in "" text text=auto -text crlf -crlf eol=lf eol=crlf \
+ "text eol=lf" "text eol=crlf" \
+ "text=auto eol=lf" "text=auto eol=crlf"; do
+
+ create_to_workdir_data "${input}" "${output}" "${tempdir}" \
+ "${systype}" "${autocrlf}" "${attr}"
+
+ for safecrlf in true false warn; do
+ create_to_odb_data "${input}" "${output}" "${tempdir}" \
+ "${systype}" "${autocrlf}" "${safecrlf}" "${attr}"
+ done
+ done
+done
+
diff --git a/tests/resources/git-sha256.index b/tests/resources/git-sha256.index
new file mode 100644
index 0000000..84b5cab
--- /dev/null
+++ b/tests/resources/git-sha256.index
Binary files differ
diff --git a/tests/resources/gitgit.index b/tests/resources/gitgit.index
new file mode 100644
index 0000000..215da64
--- /dev/null
+++ b/tests/resources/gitgit.index
Binary files differ
diff --git a/tests/resources/grafted.git/HEAD b/tests/resources/grafted.git/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/grafted.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/grafted.git/config b/tests/resources/grafted.git/config
new file mode 100644
index 0000000..e6da231
--- /dev/null
+++ b/tests/resources/grafted.git/config
@@ -0,0 +1,6 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = true
+ ignorecase = true
+ precomposeunicode = true
diff --git a/tests/resources/grafted.git/info/grafts b/tests/resources/grafted.git/info/grafts
new file mode 100644
index 0000000..bb9df8c
--- /dev/null
+++ b/tests/resources/grafted.git/info/grafts
@@ -0,0 +1,3 @@
+f503807ffa920e407a600cfaee96b7152259acc7 2f3053cbff8a4ca2f0666de364ddb734a28a31a9
+0512adebd3782157f0d5c9b22b043f87b4aaff9e 2f3053cbff8a4ca2f0666de364ddb734a28a31a9
+66cc22a015f6ca75b34c82d28f78ba663876bade e414f42f4e6bc6934563a2349a8600f0ab68618e 8a00e91619098618be97c0d2ceabb05a2c58edd9 1c18e80a276611bb9b146590616bbc5aebdf2945 2f3053cbff8a4ca2f0666de364ddb734a28a31a9
diff --git a/tests/resources/grafted.git/objects/05/12adebd3782157f0d5c9b22b043f87b4aaff9e b/tests/resources/grafted.git/objects/05/12adebd3782157f0d5c9b22b043f87b4aaff9e
new file mode 100644
index 0000000..16880d5
--- /dev/null
+++ b/tests/resources/grafted.git/objects/05/12adebd3782157f0d5c9b22b043f87b4aaff9e
Binary files differ
diff --git a/tests/resources/grafted.git/objects/1c/18e80a276611bb9b146590616bbc5aebdf2945 b/tests/resources/grafted.git/objects/1c/18e80a276611bb9b146590616bbc5aebdf2945
new file mode 100644
index 0000000..2c057b8
--- /dev/null
+++ b/tests/resources/grafted.git/objects/1c/18e80a276611bb9b146590616bbc5aebdf2945
Binary files differ
diff --git a/tests/resources/grafted.git/objects/1c/3f11eca55d76bc1bf7353ca7e4226246d353ed b/tests/resources/grafted.git/objects/1c/3f11eca55d76bc1bf7353ca7e4226246d353ed
new file mode 100644
index 0000000..b92a304
--- /dev/null
+++ b/tests/resources/grafted.git/objects/1c/3f11eca55d76bc1bf7353ca7e4226246d353ed
Binary files differ
diff --git a/tests/resources/grafted.git/objects/2a/f02ebff1fc0142d2380c98758d81c67b365869 b/tests/resources/grafted.git/objects/2a/f02ebff1fc0142d2380c98758d81c67b365869
new file mode 100644
index 0000000..ed3f874
--- /dev/null
+++ b/tests/resources/grafted.git/objects/2a/f02ebff1fc0142d2380c98758d81c67b365869
Binary files differ
diff --git a/tests/resources/grafted.git/objects/2b/ecadd3f1ecad07a054392421edf9c0e1c375b2 b/tests/resources/grafted.git/objects/2b/ecadd3f1ecad07a054392421edf9c0e1c375b2
new file mode 100644
index 0000000..724eedb
--- /dev/null
+++ b/tests/resources/grafted.git/objects/2b/ecadd3f1ecad07a054392421edf9c0e1c375b2
Binary files differ
diff --git a/tests/resources/grafted.git/objects/2f/3053cbff8a4ca2f0666de364ddb734a28a31a9 b/tests/resources/grafted.git/objects/2f/3053cbff8a4ca2f0666de364ddb734a28a31a9
new file mode 100644
index 0000000..3d124a6
--- /dev/null
+++ b/tests/resources/grafted.git/objects/2f/3053cbff8a4ca2f0666de364ddb734a28a31a9
Binary files differ
diff --git a/tests/resources/grafted.git/objects/45/342912745ba6f8893b1e126df4653a4355df1a b/tests/resources/grafted.git/objects/45/342912745ba6f8893b1e126df4653a4355df1a
new file mode 100644
index 0000000..4a8c471
--- /dev/null
+++ b/tests/resources/grafted.git/objects/45/342912745ba6f8893b1e126df4653a4355df1a
Binary files differ
diff --git a/tests/resources/grafted.git/objects/48/b2b333732644eafb385771a992b923fa88f135 b/tests/resources/grafted.git/objects/48/b2b333732644eafb385771a992b923fa88f135
new file mode 100644
index 0000000..ac64063
--- /dev/null
+++ b/tests/resources/grafted.git/objects/48/b2b333732644eafb385771a992b923fa88f135
Binary files differ
diff --git a/tests/resources/grafted.git/objects/5d/31bf4b437e1191b6c709c665f1bd329d0ed0bf b/tests/resources/grafted.git/objects/5d/31bf4b437e1191b6c709c665f1bd329d0ed0bf
new file mode 100644
index 0000000..47a0537
--- /dev/null
+++ b/tests/resources/grafted.git/objects/5d/31bf4b437e1191b6c709c665f1bd329d0ed0bf
Binary files differ
diff --git a/tests/resources/grafted.git/objects/66/cc22a015f6ca75b34c82d28f78ba663876bade b/tests/resources/grafted.git/objects/66/cc22a015f6ca75b34c82d28f78ba663876bade
new file mode 100644
index 0000000..c68b2cd
--- /dev/null
+++ b/tests/resources/grafted.git/objects/66/cc22a015f6ca75b34c82d28f78ba663876bade
@@ -0,0 +1,2 @@
+xœ¥ŽM
+Â0F]ç³Ê$™üDÜx=Á4N´Ð4ÒFϯÔ#¸úàñx|©–260dvmñap1° Ñaò}ö¹·N‹hL!E&}ã”BTO^dn ¤)“É$~øê–œ·l,õ=bF|ô:ŠâW{ÔÎm”y¸rYë ‡uÛN~øt/<N]ªåÚ¡EHkØ£ATi»Ú䯈ºH©o<N¢>Ñ«M, \ No newline at end of file
diff --git a/tests/resources/grafted.git/objects/6c/f192eb71cd3243c9fbbe2551012c4449de3fcf b/tests/resources/grafted.git/objects/6c/f192eb71cd3243c9fbbe2551012c4449de3fcf
new file mode 100644
index 0000000..a437f24
--- /dev/null
+++ b/tests/resources/grafted.git/objects/6c/f192eb71cd3243c9fbbe2551012c4449de3fcf
Binary files differ
diff --git a/tests/resources/grafted.git/objects/7c/9da502b2744b70522bb694cd607fb00104a233 b/tests/resources/grafted.git/objects/7c/9da502b2744b70522bb694cd607fb00104a233
new file mode 100644
index 0000000..b363584
--- /dev/null
+++ b/tests/resources/grafted.git/objects/7c/9da502b2744b70522bb694cd607fb00104a233
Binary files differ
diff --git a/tests/resources/grafted.git/objects/8a/00e91619098618be97c0d2ceabb05a2c58edd9 b/tests/resources/grafted.git/objects/8a/00e91619098618be97c0d2ceabb05a2c58edd9
new file mode 100644
index 0000000..887778a
--- /dev/null
+++ b/tests/resources/grafted.git/objects/8a/00e91619098618be97c0d2ceabb05a2c58edd9
@@ -0,0 +1,2 @@
+xœ¥ŽA
+Â0E]ç³Êt’4Dtá <Á$™Ô@“Jï¯Ô#¸úðïý°”’ч¶Š@B§5ú@b$Ñ'ÒØ[´iƒ÷±g&V/^¥6Hõ ]J<ŠAÇbH,2Þõ–ÈŽ‚SünÏe…{ËR«ÀƒË¶T8oûvòÃשpž»°” |›­Ódàˆ„¨Â~µÉ_u‹1× RžÅ¨ßšNC \ No newline at end of file
diff --git a/tests/resources/grafted.git/objects/a0/4de168dd5c43aa2af594d794d62e922f8b3b34 b/tests/resources/grafted.git/objects/a0/4de168dd5c43aa2af594d794d62e922f8b3b34
new file mode 100644
index 0000000..1ed3ed9
--- /dev/null
+++ b/tests/resources/grafted.git/objects/a0/4de168dd5c43aa2af594d794d62e922f8b3b34
Binary files differ
diff --git a/tests/resources/grafted.git/objects/b2/b4f9e5fe5dacbb2f98bd71d1dc86c7b571ddd1 b/tests/resources/grafted.git/objects/b2/b4f9e5fe5dacbb2f98bd71d1dc86c7b571ddd1
new file mode 100644
index 0000000..2adc857
--- /dev/null
+++ b/tests/resources/grafted.git/objects/b2/b4f9e5fe5dacbb2f98bd71d1dc86c7b571ddd1
Binary files differ
diff --git a/tests/resources/grafted.git/objects/ba/54010f8d41532eb130eba420f50248881f7fc2 b/tests/resources/grafted.git/objects/ba/54010f8d41532eb130eba420f50248881f7fc2
new file mode 100644
index 0000000..52a8872
--- /dev/null
+++ b/tests/resources/grafted.git/objects/ba/54010f8d41532eb130eba420f50248881f7fc2
Binary files differ
diff --git a/tests/resources/grafted.git/objects/d7/224d49d6d5aff6ade596ed74f4bcd4f77b29e2 b/tests/resources/grafted.git/objects/d7/224d49d6d5aff6ade596ed74f4bcd4f77b29e2
new file mode 100644
index 0000000..5b41b67
--- /dev/null
+++ b/tests/resources/grafted.git/objects/d7/224d49d6d5aff6ade596ed74f4bcd4f77b29e2
@@ -0,0 +1,2 @@
+xϴA
+Â0E]ç³ÊdÒDÜx=Á$™h¡I¤ž_­Gpõà/ÿ…šóÔ€,íÚ"ä%pŒ&é/Ñ1Úތԓ–˜Æ€¢ƒqÖ“zð"¥ZMÅGãÒÖ%Œ6ŒžÈcoÒà|ÏœÒ(ŠŸí^8·IJ¸r^kú±“ß|ºežæ.Ô|mÑ u„öHˆ*lW›ü%QÉõ%¦Y „ZÚ§bUoaRj \ No newline at end of file
diff --git a/tests/resources/grafted.git/objects/db/8e43f297a313c439530c977b733aaa8c10d54e b/tests/resources/grafted.git/objects/db/8e43f297a313c439530c977b733aaa8c10d54e
new file mode 100644
index 0000000..b9cf594
--- /dev/null
+++ b/tests/resources/grafted.git/objects/db/8e43f297a313c439530c977b733aaa8c10d54e
Binary files differ
diff --git a/tests/resources/grafted.git/objects/e4/14f42f4e6bc6934563a2349a8600f0ab68618e b/tests/resources/grafted.git/objects/e4/14f42f4e6bc6934563a2349a8600f0ab68618e
new file mode 100644
index 0000000..1a14959
--- /dev/null
+++ b/tests/resources/grafted.git/objects/e4/14f42f4e6bc6934563a2349a8600f0ab68618e
Binary files differ
diff --git a/tests/resources/grafted.git/objects/e6/7b587a57850c69f6f9351ee10c7c8a41dacc78 b/tests/resources/grafted.git/objects/e6/7b587a57850c69f6f9351ee10c7c8a41dacc78
new file mode 100644
index 0000000..213f9ac
--- /dev/null
+++ b/tests/resources/grafted.git/objects/e6/7b587a57850c69f6f9351ee10c7c8a41dacc78
Binary files differ
diff --git a/tests/resources/grafted.git/objects/f0/7330bc2e4ed4bd0bf2301505f6c6bbad01aa2a b/tests/resources/grafted.git/objects/f0/7330bc2e4ed4bd0bf2301505f6c6bbad01aa2a
new file mode 100644
index 0000000..f2d6488
--- /dev/null
+++ b/tests/resources/grafted.git/objects/f0/7330bc2e4ed4bd0bf2301505f6c6bbad01aa2a
Binary files differ
diff --git a/tests/resources/grafted.git/objects/f5/03807ffa920e407a600cfaee96b7152259acc7 b/tests/resources/grafted.git/objects/f5/03807ffa920e407a600cfaee96b7152259acc7
new file mode 100644
index 0000000..21436c1
--- /dev/null
+++ b/tests/resources/grafted.git/objects/f5/03807ffa920e407a600cfaee96b7152259acc7
@@ -0,0 +1,2 @@
+xϴA
+Â0E]ç³J&˜6)ºðž ™™Ô@“@ï¯Ô#¸ú·xŸZ)¹z{ê›D/É‹Mb9PŒ&yyBF&7Òí—™Q…wµ =K­ÏPöVáº;ÈOß–ò:P+3 Õƃ³6Z+:®»üQwæ\Hy£>zAƒ \ No newline at end of file
diff --git a/tests/resources/grafted.git/refs/heads/bottom b/tests/resources/grafted.git/refs/heads/bottom
new file mode 100644
index 0000000..10513e6
--- /dev/null
+++ b/tests/resources/grafted.git/refs/heads/bottom
@@ -0,0 +1 @@
+66cc22a015f6ca75b34c82d28f78ba663876bade
diff --git a/tests/resources/grafted.git/refs/heads/branch b/tests/resources/grafted.git/refs/heads/branch
new file mode 100644
index 0000000..d0fe5c2
--- /dev/null
+++ b/tests/resources/grafted.git/refs/heads/branch
@@ -0,0 +1 @@
+8a00e91619098618be97c0d2ceabb05a2c58edd9
diff --git a/tests/resources/grafted.git/refs/heads/master b/tests/resources/grafted.git/refs/heads/master
new file mode 100644
index 0000000..de809b9
--- /dev/null
+++ b/tests/resources/grafted.git/refs/heads/master
@@ -0,0 +1 @@
+2f3053cbff8a4ca2f0666de364ddb734a28a31a9
diff --git a/tests/resources/grafted.git/refs/heads/top b/tests/resources/grafted.git/refs/heads/top
new file mode 100644
index 0000000..ce22611
--- /dev/null
+++ b/tests/resources/grafted.git/refs/heads/top
@@ -0,0 +1 @@
+1c18e80a276611bb9b146590616bbc5aebdf2945
diff --git a/tests/resources/icase/.gitted/HEAD b/tests/resources/icase/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/icase/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/icase/.gitted/config b/tests/resources/icase/.gitted/config
new file mode 100644
index 0000000..bb4d11c
--- /dev/null
+++ b/tests/resources/icase/.gitted/config
@@ -0,0 +1,7 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
+ precomposeunicode = false
diff --git a/tests/resources/icase/.gitted/description b/tests/resources/icase/.gitted/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/icase/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/icase/.gitted/index b/tests/resources/icase/.gitted/index
new file mode 100644
index 0000000..f8288ec
--- /dev/null
+++ b/tests/resources/icase/.gitted/index
Binary files differ
diff --git a/tests/resources/icase/.gitted/info/exclude b/tests/resources/icase/.gitted/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/icase/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/icase/.gitted/logs/HEAD b/tests/resources/icase/.gitted/logs/HEAD
new file mode 100644
index 0000000..3b16bd1
--- /dev/null
+++ b/tests/resources/icase/.gitted/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 76d6e1d231b1085fcce151427e9899335de74be6 Russell Belfer <rb@github.com> 1359157123 -0800 commit (initial): initial commit
diff --git a/tests/resources/icase/.gitted/logs/refs/heads/master b/tests/resources/icase/.gitted/logs/refs/heads/master
new file mode 100644
index 0000000..3b16bd1
--- /dev/null
+++ b/tests/resources/icase/.gitted/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 76d6e1d231b1085fcce151427e9899335de74be6 Russell Belfer <rb@github.com> 1359157123 -0800 commit (initial): initial commit
diff --git a/tests/resources/icase/.gitted/objects/3e/257c57f136a1cb8f2b8e9a2e5bc8ec0258bdce b/tests/resources/icase/.gitted/objects/3e/257c57f136a1cb8f2b8e9a2e5bc8ec0258bdce
new file mode 100644
index 0000000..10691c7
--- /dev/null
+++ b/tests/resources/icase/.gitted/objects/3e/257c57f136a1cb8f2b8e9a2e5bc8ec0258bdce
Binary files differ
diff --git a/tests/resources/icase/.gitted/objects/4d/d6027d083575c7431396dc2a3174afeb393c93 b/tests/resources/icase/.gitted/objects/4d/d6027d083575c7431396dc2a3174afeb393c93
new file mode 100644
index 0000000..8ca70df
--- /dev/null
+++ b/tests/resources/icase/.gitted/objects/4d/d6027d083575c7431396dc2a3174afeb393c93
Binary files differ
diff --git a/tests/resources/icase/.gitted/objects/62/e0af52c199ec731fe4ad230041cd3286192d49 b/tests/resources/icase/.gitted/objects/62/e0af52c199ec731fe4ad230041cd3286192d49
new file mode 100644
index 0000000..e264aea
--- /dev/null
+++ b/tests/resources/icase/.gitted/objects/62/e0af52c199ec731fe4ad230041cd3286192d49
Binary files differ
diff --git a/tests/resources/icase/.gitted/objects/76/d6e1d231b1085fcce151427e9899335de74be6 b/tests/resources/icase/.gitted/objects/76/d6e1d231b1085fcce151427e9899335de74be6
new file mode 100644
index 0000000..24a4b3e
--- /dev/null
+++ b/tests/resources/icase/.gitted/objects/76/d6e1d231b1085fcce151427e9899335de74be6
@@ -0,0 +1,3 @@
+x•Û 1EýNÓ€’c² ‹X‚dƉÈÈ&ý°/çœËußsãñÔ›8±è}2î SH–‚,Ñ
+am1ЋEÅÑ·Úà9ŽCJ‡”$ nîïÜ·A®û
+ÆábÐëଃÖj®ó¯Oô_SåOî9ø%Ô)Œ9š \ No newline at end of file
diff --git a/tests/resources/icase/.gitted/objects/d4/4e18fb93b7107b5cd1b95d601591d77869a1b6 b/tests/resources/icase/.gitted/objects/d4/4e18fb93b7107b5cd1b95d601591d77869a1b6
new file mode 100644
index 0000000..32d8c49
--- /dev/null
+++ b/tests/resources/icase/.gitted/objects/d4/4e18fb93b7107b5cd1b95d601591d77869a1b6
Binary files differ
diff --git a/tests/resources/icase/.gitted/refs/heads/master b/tests/resources/icase/.gitted/refs/heads/master
new file mode 100644
index 0000000..37410ec
--- /dev/null
+++ b/tests/resources/icase/.gitted/refs/heads/master
@@ -0,0 +1 @@
+76d6e1d231b1085fcce151427e9899335de74be6
diff --git a/tests/resources/icase/B b/tests/resources/icase/B
new file mode 100644
index 0000000..d44e18f
--- /dev/null
+++ b/tests/resources/icase/B
@@ -0,0 +1 @@
+start
diff --git a/tests/resources/icase/D b/tests/resources/icase/D
new file mode 100644
index 0000000..d44e18f
--- /dev/null
+++ b/tests/resources/icase/D
@@ -0,0 +1 @@
+start
diff --git a/tests/resources/icase/F b/tests/resources/icase/F
new file mode 100644
index 0000000..d44e18f
--- /dev/null
+++ b/tests/resources/icase/F
@@ -0,0 +1 @@
+start
diff --git a/tests/resources/icase/H b/tests/resources/icase/H
new file mode 100644
index 0000000..d44e18f
--- /dev/null
+++ b/tests/resources/icase/H
@@ -0,0 +1 @@
+start
diff --git a/tests/resources/icase/J b/tests/resources/icase/J
new file mode 100644
index 0000000..d44e18f
--- /dev/null
+++ b/tests/resources/icase/J
@@ -0,0 +1 @@
+start
diff --git a/tests/resources/icase/L/1 b/tests/resources/icase/L/1
new file mode 100644
index 0000000..62e0af5
--- /dev/null
+++ b/tests/resources/icase/L/1
@@ -0,0 +1 @@
+sub
diff --git a/tests/resources/icase/L/B b/tests/resources/icase/L/B
new file mode 100644
index 0000000..62e0af5
--- /dev/null
+++ b/tests/resources/icase/L/B
@@ -0,0 +1 @@
+sub
diff --git a/tests/resources/icase/L/D b/tests/resources/icase/L/D
new file mode 100644
index 0000000..62e0af5
--- /dev/null
+++ b/tests/resources/icase/L/D
@@ -0,0 +1 @@
+sub
diff --git a/tests/resources/icase/L/a b/tests/resources/icase/L/a
new file mode 100644
index 0000000..62e0af5
--- /dev/null
+++ b/tests/resources/icase/L/a
@@ -0,0 +1 @@
+sub
diff --git a/tests/resources/icase/L/c b/tests/resources/icase/L/c
new file mode 100644
index 0000000..62e0af5
--- /dev/null
+++ b/tests/resources/icase/L/c
@@ -0,0 +1 @@
+sub
diff --git a/tests/resources/icase/a b/tests/resources/icase/a
new file mode 100644
index 0000000..d44e18f
--- /dev/null
+++ b/tests/resources/icase/a
@@ -0,0 +1 @@
+start
diff --git a/tests/resources/icase/c b/tests/resources/icase/c
new file mode 100644
index 0000000..d44e18f
--- /dev/null
+++ b/tests/resources/icase/c
@@ -0,0 +1 @@
+start
diff --git a/tests/resources/icase/e b/tests/resources/icase/e
new file mode 100644
index 0000000..d44e18f
--- /dev/null
+++ b/tests/resources/icase/e
@@ -0,0 +1 @@
+start
diff --git a/tests/resources/icase/g b/tests/resources/icase/g
new file mode 100644
index 0000000..d44e18f
--- /dev/null
+++ b/tests/resources/icase/g
@@ -0,0 +1 @@
+start
diff --git a/tests/resources/icase/i b/tests/resources/icase/i
new file mode 100644
index 0000000..d44e18f
--- /dev/null
+++ b/tests/resources/icase/i
@@ -0,0 +1 @@
+start
diff --git a/tests/resources/icase/k/1 b/tests/resources/icase/k/1
new file mode 100644
index 0000000..62e0af5
--- /dev/null
+++ b/tests/resources/icase/k/1
@@ -0,0 +1 @@
+sub
diff --git a/tests/resources/icase/k/B b/tests/resources/icase/k/B
new file mode 100644
index 0000000..62e0af5
--- /dev/null
+++ b/tests/resources/icase/k/B
@@ -0,0 +1 @@
+sub
diff --git a/tests/resources/icase/k/D b/tests/resources/icase/k/D
new file mode 100644
index 0000000..62e0af5
--- /dev/null
+++ b/tests/resources/icase/k/D
@@ -0,0 +1 @@
+sub
diff --git a/tests/resources/icase/k/a b/tests/resources/icase/k/a
new file mode 100644
index 0000000..62e0af5
--- /dev/null
+++ b/tests/resources/icase/k/a
@@ -0,0 +1 @@
+sub
diff --git a/tests/resources/icase/k/c b/tests/resources/icase/k/c
new file mode 100644
index 0000000..62e0af5
--- /dev/null
+++ b/tests/resources/icase/k/c
@@ -0,0 +1 @@
+sub
diff --git a/tests/resources/indexv4/.gitted/HEAD b/tests/resources/indexv4/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/indexv4/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/indexv4/.gitted/config b/tests/resources/indexv4/.gitted/config
new file mode 100644
index 0000000..515f483
--- /dev/null
+++ b/tests/resources/indexv4/.gitted/config
@@ -0,0 +1,5 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
diff --git a/tests/resources/indexv4/.gitted/index b/tests/resources/indexv4/.gitted/index
new file mode 100644
index 0000000..e8fc617
--- /dev/null
+++ b/tests/resources/indexv4/.gitted/index
Binary files differ
diff --git a/tests/resources/indexv4/.gitted/objects/4c/9109b3e671d851eec87e0e72f6305b582e7e99 b/tests/resources/indexv4/.gitted/objects/4c/9109b3e671d851eec87e0e72f6305b582e7e99
new file mode 100644
index 0000000..cedd594
--- /dev/null
+++ b/tests/resources/indexv4/.gitted/objects/4c/9109b3e671d851eec87e0e72f6305b582e7e99
Binary files differ
diff --git a/tests/resources/indexv4/.gitted/objects/b0/952dbb50bed5f01e03e31b296184cb183e54a7 b/tests/resources/indexv4/.gitted/objects/b0/952dbb50bed5f01e03e31b296184cb183e54a7
new file mode 100644
index 0000000..0ddc1d1
--- /dev/null
+++ b/tests/resources/indexv4/.gitted/objects/b0/952dbb50bed5f01e03e31b296184cb183e54a7
Binary files differ
diff --git a/tests/resources/indexv4/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/indexv4/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644
index 0000000..7112238
--- /dev/null
+++ b/tests/resources/indexv4/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests/resources/indexv4/.gitted/refs/heads/master b/tests/resources/indexv4/.gitted/refs/heads/master
new file mode 100644
index 0000000..f3e960e
--- /dev/null
+++ b/tests/resources/indexv4/.gitted/refs/heads/master
@@ -0,0 +1 @@
+b0952dbb50bed5f01e03e31b296184cb183e54a7
diff --git a/tests/resources/indexv4/file.tx b/tests/resources/indexv4/file.tx
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/indexv4/file.tx
diff --git a/tests/resources/indexv4/file.txt b/tests/resources/indexv4/file.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/indexv4/file.txt
diff --git a/tests/resources/indexv4/file.txz b/tests/resources/indexv4/file.txz
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/indexv4/file.txz
diff --git a/tests/resources/indexv4/foo b/tests/resources/indexv4/foo
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/indexv4/foo
diff --git a/tests/resources/indexv4/zzz b/tests/resources/indexv4/zzz
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/indexv4/zzz
diff --git a/tests/resources/issue_1397/.gitted/HEAD b/tests/resources/issue_1397/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/issue_1397/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/issue_1397/.gitted/config b/tests/resources/issue_1397/.gitted/config
new file mode 100644
index 0000000..ba5bbde
--- /dev/null
+++ b/tests/resources/issue_1397/.gitted/config
@@ -0,0 +1,6 @@
+[core]
+ bare = false
+ repositoryformatversion = 0
+ filemode = false
+ logallrefupdates = true
+ ignorecase = true
diff --git a/tests/resources/issue_1397/.gitted/index b/tests/resources/issue_1397/.gitted/index
new file mode 100644
index 0000000..fa0f541
--- /dev/null
+++ b/tests/resources/issue_1397/.gitted/index
Binary files differ
diff --git a/tests/resources/issue_1397/.gitted/objects/7f/483a738f867e5b21c8f377d70311f011eb48b5 b/tests/resources/issue_1397/.gitted/objects/7f/483a738f867e5b21c8f377d70311f011eb48b5
new file mode 100644
index 0000000..63bcb5d
--- /dev/null
+++ b/tests/resources/issue_1397/.gitted/objects/7f/483a738f867e5b21c8f377d70311f011eb48b5
@@ -0,0 +1,3 @@
+xÎQNÃ0 €ažs
+¿£!'nâTBÓî°¸ŽC+Öe\Äv€ÿӯǾoÑËèfPƒ¦PL8FΆ‘ÒÌ%ª_Ä‹b4æIÜ—tk²°Uœ¸êLdeIÁòd<3/˜|0¥šjÈää1Ö£ÃõÛ\Gßô³c…wÛe»]ô~úùߊÁS
+)ÏGxEèôÿqØsµÓUÚ‡8šÁmkæ~yIæ \ No newline at end of file
diff --git a/tests/resources/issue_1397/.gitted/objects/83/12e0889a9cbab77c732b6bc39b51a683e3a318 b/tests/resources/issue_1397/.gitted/objects/83/12e0889a9cbab77c732b6bc39b51a683e3a318
new file mode 100644
index 0000000..06b59fe
--- /dev/null
+++ b/tests/resources/issue_1397/.gitted/objects/83/12e0889a9cbab77c732b6bc39b51a683e3a318
Binary files differ
diff --git a/tests/resources/issue_1397/.gitted/objects/8a/7ef047fc933edb62e84e7977b0612ec3f6f283 b/tests/resources/issue_1397/.gitted/objects/8a/7ef047fc933edb62e84e7977b0612ec3f6f283
new file mode 100644
index 0000000..19cfbea
--- /dev/null
+++ b/tests/resources/issue_1397/.gitted/objects/8a/7ef047fc933edb62e84e7977b0612ec3f6f283
Binary files differ
diff --git a/tests/resources/issue_1397/.gitted/objects/8e/8f80088a9274fd23584992f587083ca1bcbbac b/tests/resources/issue_1397/.gitted/objects/8e/8f80088a9274fd23584992f587083ca1bcbbac
new file mode 100644
index 0000000..f5c776b
--- /dev/null
+++ b/tests/resources/issue_1397/.gitted/objects/8e/8f80088a9274fd23584992f587083ca1bcbbac
Binary files differ
diff --git a/tests/resources/issue_1397/.gitted/objects/f2/c62dea0372a0578e053697d5c1ba1ac05e774a b/tests/resources/issue_1397/.gitted/objects/f2/c62dea0372a0578e053697d5c1ba1ac05e774a
new file mode 100644
index 0000000..f932f36
--- /dev/null
+++ b/tests/resources/issue_1397/.gitted/objects/f2/c62dea0372a0578e053697d5c1ba1ac05e774a
Binary files differ
diff --git a/tests/resources/issue_1397/.gitted/objects/ff/3578d64d199d5b48d92bbb569e0a273e411741 b/tests/resources/issue_1397/.gitted/objects/ff/3578d64d199d5b48d92bbb569e0a273e411741
new file mode 100644
index 0000000..fbd7317
--- /dev/null
+++ b/tests/resources/issue_1397/.gitted/objects/ff/3578d64d199d5b48d92bbb569e0a273e411741
Binary files differ
diff --git a/tests/resources/issue_1397/.gitted/refs/heads/master b/tests/resources/issue_1397/.gitted/refs/heads/master
new file mode 100644
index 0000000..285bc08
--- /dev/null
+++ b/tests/resources/issue_1397/.gitted/refs/heads/master
@@ -0,0 +1 @@
+7f483a738f867e5b21c8f377d70311f011eb48b5
diff --git a/tests/resources/issue_1397/crlf_file.txt b/tests/resources/issue_1397/crlf_file.txt
new file mode 100644
index 0000000..8312e08
--- /dev/null
+++ b/tests/resources/issue_1397/crlf_file.txt
@@ -0,0 +1,3 @@
+first line
+second line
+both with crlf \ No newline at end of file
diff --git a/tests/resources/issue_1397/some_other_crlf_file.txt b/tests/resources/issue_1397/some_other_crlf_file.txt
new file mode 100644
index 0000000..8e8f800
--- /dev/null
+++ b/tests/resources/issue_1397/some_other_crlf_file.txt
@@ -0,0 +1,3 @@
+first line
+second line with some change
+both with crlf \ No newline at end of file
diff --git a/tests/resources/issue_592/.gitted/COMMIT_EDITMSG b/tests/resources/issue_592/.gitted/COMMIT_EDITMSG
new file mode 100644
index 0000000..5852f44
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/COMMIT_EDITMSG
@@ -0,0 +1 @@
+Initial commit
diff --git a/tests/resources/issue_592/.gitted/HEAD b/tests/resources/issue_592/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/issue_592/.gitted/config b/tests/resources/issue_592/.gitted/config
new file mode 100644
index 0000000..78387c5
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/config
@@ -0,0 +1,8 @@
+[core]
+ repositoryformatversion = 0
+ filemode = false
+ bare = false
+ logallrefupdates = true
+ symlinks = false
+ ignorecase = true
+ hideDotFiles = dotGitOnly
diff --git a/tests/resources/issue_592/.gitted/index b/tests/resources/issue_592/.gitted/index
new file mode 100644
index 0000000..be7a29d
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/index
Binary files differ
diff --git a/tests/resources/issue_592/.gitted/info/exclude b/tests/resources/issue_592/.gitted/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/issue_592/.gitted/logs/HEAD b/tests/resources/issue_592/.gitted/logs/HEAD
new file mode 100644
index 0000000..f19fe35
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/logs/HEAD
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 4d383e87f0371ba8fa353f3912db6862b2625e85 nulltoken <emeric.fermas@gmail.com> 1331989635 +0100 commit (initial): Initial commit
+4d383e87f0371ba8fa353f3912db6862b2625e85 e38fcc7a6060f5eb5b876e836b52ae4769363f21 nulltoken <emeric.fermas@gmail.com> 1332227062 +0100 commit (amend): Initial commit
diff --git a/tests/resources/issue_592/.gitted/logs/refs/heads/master b/tests/resources/issue_592/.gitted/logs/refs/heads/master
new file mode 100644
index 0000000..f19fe35
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/logs/refs/heads/master
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 4d383e87f0371ba8fa353f3912db6862b2625e85 nulltoken <emeric.fermas@gmail.com> 1331989635 +0100 commit (initial): Initial commit
+4d383e87f0371ba8fa353f3912db6862b2625e85 e38fcc7a6060f5eb5b876e836b52ae4769363f21 nulltoken <emeric.fermas@gmail.com> 1332227062 +0100 commit (amend): Initial commit
diff --git a/tests/resources/issue_592/.gitted/objects/06/07ee9d4ccce8e4c4fa13c2c7d727e7faba4e0e b/tests/resources/issue_592/.gitted/objects/06/07ee9d4ccce8e4c4fa13c2c7d727e7faba4e0e
new file mode 100644
index 0000000..05dec10
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/objects/06/07ee9d4ccce8e4c4fa13c2c7d727e7faba4e0e
Binary files differ
diff --git a/tests/resources/issue_592/.gitted/objects/49/363a72a90d9424240258cd3759f23788ecf1d8 b/tests/resources/issue_592/.gitted/objects/49/363a72a90d9424240258cd3759f23788ecf1d8
new file mode 100644
index 0000000..e997e1b
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/objects/49/363a72a90d9424240258cd3759f23788ecf1d8
Binary files differ
diff --git a/tests/resources/issue_592/.gitted/objects/4d/383e87f0371ba8fa353f3912db6862b2625e85 b/tests/resources/issue_592/.gitted/objects/4d/383e87f0371ba8fa353f3912db6862b2625e85
new file mode 100644
index 0000000..c49a8be
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/objects/4d/383e87f0371ba8fa353f3912db6862b2625e85
@@ -0,0 +1,2 @@
+xM
+Â0]ço/”¤I›DÜzŒçë æbz ÞÀíÀÌHÍ9v2Ëtê =k¬›,pâ+£øÍ>ðƒ4ïýU•=¥^ß(tAF‹2´ÌŸÛ3sLƒÔ|%c­Y—u¶µÑZô˜vü©«{‰=r¢_G}KÈ>ˆ \ No newline at end of file
diff --git a/tests/resources/issue_592/.gitted/objects/71/44be264b61825fbff68046fe999bdfe96a1792 b/tests/resources/issue_592/.gitted/objects/71/44be264b61825fbff68046fe999bdfe96a1792
new file mode 100644
index 0000000..25d44d9
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/objects/71/44be264b61825fbff68046fe999bdfe96a1792
Binary files differ
diff --git a/tests/resources/issue_592/.gitted/objects/be/de83ee10b5b3f00239660b00acec2d55fd0b84 b/tests/resources/issue_592/.gitted/objects/be/de83ee10b5b3f00239660b00acec2d55fd0b84
new file mode 100644
index 0000000..1d6e38d
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/objects/be/de83ee10b5b3f00239660b00acec2d55fd0b84
Binary files differ
diff --git a/tests/resources/issue_592/.gitted/objects/e3/8fcc7a6060f5eb5b876e836b52ae4769363f21 b/tests/resources/issue_592/.gitted/objects/e3/8fcc7a6060f5eb5b876e836b52ae4769363f21
new file mode 100644
index 0000000..36c5b9a
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/objects/e3/8fcc7a6060f5eb5b876e836b52ae4769363f21
Binary files differ
diff --git a/tests/resources/issue_592/.gitted/objects/f1/adef63cb08891a0942b76fc4b9c50c6c494bc7 b/tests/resources/issue_592/.gitted/objects/f1/adef63cb08891a0942b76fc4b9c50c6c494bc7
new file mode 100644
index 0000000..c08ecd5
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/objects/f1/adef63cb08891a0942b76fc4b9c50c6c494bc7
Binary files differ
diff --git a/tests/resources/issue_592/.gitted/refs/heads/master b/tests/resources/issue_592/.gitted/refs/heads/master
new file mode 100644
index 0000000..1f66696
--- /dev/null
+++ b/tests/resources/issue_592/.gitted/refs/heads/master
@@ -0,0 +1 @@
+e38fcc7a6060f5eb5b876e836b52ae4769363f21
diff --git a/tests/resources/issue_592/a.txt b/tests/resources/issue_592/a.txt
new file mode 100644
index 0000000..f1adef6
--- /dev/null
+++ b/tests/resources/issue_592/a.txt
@@ -0,0 +1 @@
+nothing here
diff --git a/tests/resources/issue_592/c/a.txt b/tests/resources/issue_592/c/a.txt
new file mode 100644
index 0000000..f1adef6
--- /dev/null
+++ b/tests/resources/issue_592/c/a.txt
@@ -0,0 +1 @@
+nothing here
diff --git a/tests/resources/issue_592/l.txt b/tests/resources/issue_592/l.txt
new file mode 100644
index 0000000..f1adef6
--- /dev/null
+++ b/tests/resources/issue_592/l.txt
@@ -0,0 +1 @@
+nothing here
diff --git a/tests/resources/issue_592/t/a.txt b/tests/resources/issue_592/t/a.txt
new file mode 100644
index 0000000..f1adef6
--- /dev/null
+++ b/tests/resources/issue_592/t/a.txt
@@ -0,0 +1 @@
+nothing here
diff --git a/tests/resources/issue_592/t/b.txt b/tests/resources/issue_592/t/b.txt
new file mode 100644
index 0000000..f1adef6
--- /dev/null
+++ b/tests/resources/issue_592/t/b.txt
@@ -0,0 +1 @@
+nothing here
diff --git a/tests/resources/issue_592b/.gitted/HEAD b/tests/resources/issue_592b/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/issue_592b/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/issue_592b/.gitted/config b/tests/resources/issue_592b/.gitted/config
new file mode 100644
index 0000000..af10792
--- /dev/null
+++ b/tests/resources/issue_592b/.gitted/config
@@ -0,0 +1,6 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
diff --git a/tests/resources/issue_592b/.gitted/description b/tests/resources/issue_592b/.gitted/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/issue_592b/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/issue_592b/.gitted/index b/tests/resources/issue_592b/.gitted/index
new file mode 100644
index 0000000..5964382
--- /dev/null
+++ b/tests/resources/issue_592b/.gitted/index
Binary files differ
diff --git a/tests/resources/issue_592b/.gitted/info/exclude b/tests/resources/issue_592b/.gitted/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/issue_592b/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/issue_592b/.gitted/logs/HEAD b/tests/resources/issue_592b/.gitted/logs/HEAD
new file mode 100644
index 0000000..6f3ba90
--- /dev/null
+++ b/tests/resources/issue_592b/.gitted/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 3fbf1852f72fd268e36457b13a18cdd9a4c9ea35 Russell Belfer <rb@github.com> 1337205933 -0700 commit (initial): Initial commit
diff --git a/tests/resources/issue_592b/.gitted/logs/refs/heads/master b/tests/resources/issue_592b/.gitted/logs/refs/heads/master
new file mode 100644
index 0000000..6f3ba90
--- /dev/null
+++ b/tests/resources/issue_592b/.gitted/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 3fbf1852f72fd268e36457b13a18cdd9a4c9ea35 Russell Belfer <rb@github.com> 1337205933 -0700 commit (initial): Initial commit
diff --git a/tests/resources/issue_592b/.gitted/objects/3f/bf1852f72fd268e36457b13a18cdd9a4c9ea35 b/tests/resources/issue_592b/.gitted/objects/3f/bf1852f72fd268e36457b13a18cdd9a4c9ea35
new file mode 100644
index 0000000..6eaf64b
--- /dev/null
+++ b/tests/resources/issue_592b/.gitted/objects/3f/bf1852f72fd268e36457b13a18cdd9a4c9ea35
@@ -0,0 +1,2 @@
+x•K
+1]ç}%Bwn½A§íq‰™Îý x·ªz¼µVƃv É‚còžÑ&”%9¦@˜9x¤dÝëŒìÙÐÐuëðû.µÂ]ê".=ßÞEבO¼µ+¸ÐÛ˜B€£EkÍ\çŸNô_Ó<>E Uø%Ìû•9 \ No newline at end of file
diff --git a/tests/resources/issue_592b/.gitted/objects/6f/a891d3e578c83e1c03bdb9e0fdd8e6e934157f b/tests/resources/issue_592b/.gitted/objects/6f/a891d3e578c83e1c03bdb9e0fdd8e6e934157f
new file mode 100644
index 0000000..c4becfe
--- /dev/null
+++ b/tests/resources/issue_592b/.gitted/objects/6f/a891d3e578c83e1c03bdb9e0fdd8e6e934157f
Binary files differ
diff --git a/tests/resources/issue_592b/.gitted/objects/80/07d41d5794e6ce4d4d2c97e370d5a9aa6d5213 b/tests/resources/issue_592b/.gitted/objects/80/07d41d5794e6ce4d4d2c97e370d5a9aa6d5213
new file mode 100644
index 0000000..aea14f2
--- /dev/null
+++ b/tests/resources/issue_592b/.gitted/objects/80/07d41d5794e6ce4d4d2c97e370d5a9aa6d5213
Binary files differ
diff --git a/tests/resources/issue_592b/.gitted/objects/a6/5fb6583a7c425284142f285bc359a2d6565513 b/tests/resources/issue_592b/.gitted/objects/a6/5fb6583a7c425284142f285bc359a2d6565513
new file mode 100644
index 0000000..9b74072
--- /dev/null
+++ b/tests/resources/issue_592b/.gitted/objects/a6/5fb6583a7c425284142f285bc359a2d6565513
Binary files differ
diff --git a/tests/resources/issue_592b/.gitted/objects/ae/be7a55922c7097ef91ca3a7bc327a901d87c2c b/tests/resources/issue_592b/.gitted/objects/ae/be7a55922c7097ef91ca3a7bc327a901d87c2c
new file mode 100644
index 0000000..1494ed8
--- /dev/null
+++ b/tests/resources/issue_592b/.gitted/objects/ae/be7a55922c7097ef91ca3a7bc327a901d87c2c
Binary files differ
diff --git a/tests/resources/issue_592b/.gitted/objects/b3/44b055867fcdc1f01eaa75056a43e868eb4fbc b/tests/resources/issue_592b/.gitted/objects/b3/44b055867fcdc1f01eaa75056a43e868eb4fbc
new file mode 100644
index 0000000..7a66266
--- /dev/null
+++ b/tests/resources/issue_592b/.gitted/objects/b3/44b055867fcdc1f01eaa75056a43e868eb4fbc
Binary files differ
diff --git a/tests/resources/issue_592b/.gitted/objects/f7/d75fbfad8b1d2e307ced287ea78aad403cdce3 b/tests/resources/issue_592b/.gitted/objects/f7/d75fbfad8b1d2e307ced287ea78aad403cdce3
new file mode 100644
index 0000000..65a1fd0
--- /dev/null
+++ b/tests/resources/issue_592b/.gitted/objects/f7/d75fbfad8b1d2e307ced287ea78aad403cdce3
Binary files differ
diff --git a/tests/resources/issue_592b/.gitted/refs/heads/master b/tests/resources/issue_592b/.gitted/refs/heads/master
new file mode 100644
index 0000000..c0a9ab4
--- /dev/null
+++ b/tests/resources/issue_592b/.gitted/refs/heads/master
@@ -0,0 +1 @@
+3fbf1852f72fd268e36457b13a18cdd9a4c9ea35
diff --git a/tests/resources/issue_592b/gitignore b/tests/resources/issue_592b/gitignore
new file mode 100644
index 0000000..8007d41
--- /dev/null
+++ b/tests/resources/issue_592b/gitignore
@@ -0,0 +1 @@
+ignored/
diff --git a/tests/resources/issue_592b/ignored/contained/ignored3.txt b/tests/resources/issue_592b/ignored/contained/ignored3.txt
new file mode 100644
index 0000000..b5dc7b0
--- /dev/null
+++ b/tests/resources/issue_592b/ignored/contained/ignored3.txt
@@ -0,0 +1 @@
+I'm ignored
diff --git a/tests/resources/issue_592b/ignored/contained/tracked3.txt b/tests/resources/issue_592b/ignored/contained/tracked3.txt
new file mode 100644
index 0000000..b344b05
--- /dev/null
+++ b/tests/resources/issue_592b/ignored/contained/tracked3.txt
@@ -0,0 +1 @@
+You added me anyhow
diff --git a/tests/resources/issue_592b/ignored/ignored2.txt b/tests/resources/issue_592b/ignored/ignored2.txt
new file mode 100644
index 0000000..b5dc7b0
--- /dev/null
+++ b/tests/resources/issue_592b/ignored/ignored2.txt
@@ -0,0 +1 @@
+I'm ignored
diff --git a/tests/resources/issue_592b/ignored/tracked2.txt b/tests/resources/issue_592b/ignored/tracked2.txt
new file mode 100644
index 0000000..6fa891d
--- /dev/null
+++ b/tests/resources/issue_592b/ignored/tracked2.txt
@@ -0,0 +1 @@
+You like me
diff --git a/tests/resources/issue_592b/ignored1.txt b/tests/resources/issue_592b/ignored1.txt
new file mode 100644
index 0000000..b5dc7b0
--- /dev/null
+++ b/tests/resources/issue_592b/ignored1.txt
@@ -0,0 +1 @@
+I'm ignored
diff --git a/tests/resources/issue_592b/tracked1.txt b/tests/resources/issue_592b/tracked1.txt
new file mode 100644
index 0000000..6fa891d
--- /dev/null
+++ b/tests/resources/issue_592b/tracked1.txt
@@ -0,0 +1 @@
+You like me
diff --git a/tests/resources/mailmap/.gitted/HEAD b/tests/resources/mailmap/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/mailmap/.gitted/config b/tests/resources/mailmap/.gitted/config
new file mode 100644
index 0000000..515f483
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/config
@@ -0,0 +1,5 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
diff --git a/tests/resources/mailmap/.gitted/description b/tests/resources/mailmap/.gitted/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/mailmap/.gitted/index b/tests/resources/mailmap/.gitted/index
new file mode 100644
index 0000000..af175ca
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/index
Binary files differ
diff --git a/tests/resources/mailmap/.gitted/info/exclude b/tests/resources/mailmap/.gitted/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/mailmap/.gitted/objects/00/1387531bed84262f137837125d4d998a9ba65d b/tests/resources/mailmap/.gitted/objects/00/1387531bed84262f137837125d4d998a9ba65d
new file mode 100644
index 0000000..1c56490
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/00/1387531bed84262f137837125d4d998a9ba65d
Binary files differ
diff --git a/tests/resources/mailmap/.gitted/objects/02/7b2816ae0d7a08ba656d0417c09b4eac18cf00 b/tests/resources/mailmap/.gitted/objects/02/7b2816ae0d7a08ba656d0417c09b4eac18cf00
new file mode 100644
index 0000000..a4e8249
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/02/7b2816ae0d7a08ba656d0417c09b4eac18cf00
Binary files differ
diff --git a/tests/resources/mailmap/.gitted/objects/09/20975110511365e56aec2263082d0c3d56d1fa b/tests/resources/mailmap/.gitted/objects/09/20975110511365e56aec2263082d0c3d56d1fa
new file mode 100644
index 0000000..49bceea
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/09/20975110511365e56aec2263082d0c3d56d1fa
Binary files differ
diff --git a/tests/resources/mailmap/.gitted/objects/0c/d99501dfbec781a22ff7b84426b7bb308e709a b/tests/resources/mailmap/.gitted/objects/0c/d99501dfbec781a22ff7b84426b7bb308e709a
new file mode 100644
index 0000000..23149a4
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/0c/d99501dfbec781a22ff7b84426b7bb308e709a
Binary files differ
diff --git a/tests/resources/mailmap/.gitted/objects/1e/1212e7674820c17f7b8797aee7bf38ece0e838 b/tests/resources/mailmap/.gitted/objects/1e/1212e7674820c17f7b8797aee7bf38ece0e838
new file mode 100644
index 0000000..89a8598
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/1e/1212e7674820c17f7b8797aee7bf38ece0e838
@@ -0,0 +1,2 @@
+xuŽ]
+Â0„}Î)öÊf7iZ(âÄ;l“û#5‚zz ¾êãÌ|ÃLœÇ±/@Œ›²¨BÍÁ:iˆ8³b ®ö“c´1Ö6çÌ3™›,:h|BŽ®IKZ…ä½÷,¶ëˆ4Ô9Ä”}#r™˜úxeh»Çù~ˆóx“éµ{>÷`=YÆŠ*†-:D³†ë±¢ œú«ÀQ^oh§UÊE‡¯qß­àºù3d>ØÜI \ No newline at end of file
diff --git a/tests/resources/mailmap/.gitted/objects/36/370b71f5aad1dd46bec5e14145280a843c9f49 b/tests/resources/mailmap/.gitted/objects/36/370b71f5aad1dd46bec5e14145280a843c9f49
new file mode 100644
index 0000000..5e8e3e5
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/36/370b71f5aad1dd46bec5e14145280a843c9f49
Binary files differ
diff --git a/tests/resources/mailmap/.gitted/objects/3a/1295dbc9234c0c5947c72803618c7112a01447 b/tests/resources/mailmap/.gitted/objects/3a/1295dbc9234c0c5947c72803618c7112a01447
new file mode 100644
index 0000000..347828c
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/3a/1295dbc9234c0c5947c72803618c7112a01447
@@ -0,0 +1,2 @@
+xmŽA
+Â0D]çÿ–ü$m("®Å;$??(¶Äêé-Š®dV3Ìð†ò4*(­Vµ0ƒÇ.¤¬Å>ÆH #TQ“E¥’eNdÄÅž+½‹äô"t]j]P­¶}hÙvl{g’5$ü­s]ñö0PÍ[ÊyºøùÑÜïÀV¡–´¬¥‘RÐûYå‡ÓÙÃÞ?ž<Ž0Ì‹ÛÖ#ŸàÚ,Å?sñ!5_Ô$^ïHÿ \ No newline at end of file
diff --git a/tests/resources/mailmap/.gitted/objects/3f/134546ae8fbe95a39dd20ea8c12b5fb0f48afb b/tests/resources/mailmap/.gitted/objects/3f/134546ae8fbe95a39dd20ea8c12b5fb0f48afb
new file mode 100644
index 0000000..489c610
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/3f/134546ae8fbe95a39dd20ea8c12b5fb0f48afb
@@ -0,0 +1,3 @@
+xmŽMnÂ0…»ö)æ {lbGB(¬ØTeÑŒq‰òcd\ zzeÛåûùô^Hó<@sø(™"÷½6ì<ÅØ{¤¹¥Æ5ÒG­[t6PkÐq§ÌKÅ
+²m¬q(ƒ²Ñzg[K̶"ŽKvÚ ú-·”á;Í Êž~ŽËFìBšï´¼öÏç Ô•–6
+vÒH)jXÎð5ŒŸôúãiZÑ‘ºrãi3ûZü×ÚÉpÞÖiUÝöeÝo‹µO™ \ No newline at end of file
diff --git a/tests/resources/mailmap/.gitted/objects/43/179dc93939196f59b25387b5e44e9e8794f84c b/tests/resources/mailmap/.gitted/objects/43/179dc93939196f59b25387b5e44e9e8794f84c
new file mode 100644
index 0000000..2f3693a
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/43/179dc93939196f59b25387b5e44e9e8794f84c
@@ -0,0 +1,2 @@
+xmÍA
+Â0P×9Å\À’¶“4…"ŵx‡1™ÐbÓHŒÐzz‹‚¸pù?ÿól aÌP6í.'f°-+Ô•cÃUÍ-:£|¥½Æ‹‘LöŽ´CA<ÄÇD:›coc 7š×bYPªª¬¥–5Â^¢”Â¾Ì Îã•àD듧 ºyK}xú÷bþ¹‹_çˈÒA:œ \ No newline at end of file
diff --git a/tests/resources/mailmap/.gitted/objects/46/b5bb908c78b575cac9f9e6e42ff9ba3f769a46 b/tests/resources/mailmap/.gitted/objects/46/b5bb908c78b575cac9f9e6e42ff9ba3f769a46
new file mode 100644
index 0000000..62c9db0
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/46/b5bb908c78b575cac9f9e6e42ff9ba3f769a46
Binary files differ
diff --git a/tests/resources/mailmap/.gitted/objects/4b/4d2010ba256ef339c1d1854d20249da7478f01 b/tests/resources/mailmap/.gitted/objects/4b/4d2010ba256ef339c1d1854d20249da7478f01
new file mode 100644
index 0000000..169c7e7
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/4b/4d2010ba256ef339c1d1854d20249da7478f01
Binary files differ
diff --git a/tests/resources/mailmap/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 b/tests/resources/mailmap/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904
new file mode 100644
index 0000000..adf6411
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904
Binary files differ
diff --git a/tests/resources/mailmap/.gitted/objects/4d/61d588546529ad27b2d77a3d6b05460ecb4be0 b/tests/resources/mailmap/.gitted/objects/4d/61d588546529ad27b2d77a3d6b05460ecb4be0
new file mode 100644
index 0000000..dabbf4e
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/4d/61d588546529ad27b2d77a3d6b05460ecb4be0
Binary files differ
diff --git a/tests/resources/mailmap/.gitted/objects/50/d69f4e64be2cff2cedde8f9b7f970257caf4dd b/tests/resources/mailmap/.gitted/objects/50/d69f4e64be2cff2cedde8f9b7f970257caf4dd
new file mode 100644
index 0000000..e6b337e
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/50/d69f4e64be2cff2cedde8f9b7f970257caf4dd
@@ -0,0 +1 @@
+xmŽMjÃ0…»Ö)æ ÒHcÉB–Y”Ò+Œì6±eã(äçôQé®tù~>Þë–y  m>Ê&ÁÖ&×ÙÞ“Ö=¢gOÄžR BÖ¹6¢Zy“\À&c¹†%¤(-±mûµpè FJQ'8EÅ·2,Üò%/÷ ‡u§S·Ì+ççþñ8‚!4V7ÎxØi§µªa½Vdƒ¯ñÂðÉÏ—LrU§2Èôk\÷µø®¾ëœÇäï˜z›`KN \ No newline at end of file
diff --git a/tests/resources/mailmap/.gitted/objects/61/293f4c3d7500d227a755a7a8258e28e53449b2 b/tests/resources/mailmap/.gitted/objects/61/293f4c3d7500d227a755a7a8258e28e53449b2
new file mode 100644
index 0000000..409e6fd
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/61/293f4c3d7500d227a755a7a8258e28e53449b2
Binary files differ
diff --git a/tests/resources/mailmap/.gitted/objects/62/7f0bd2f4fb5e949b79ba450d84676fa876b1c8 b/tests/resources/mailmap/.gitted/objects/62/7f0bd2f4fb5e949b79ba450d84676fa876b1c8
new file mode 100644
index 0000000..6009c30
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/62/7f0bd2f4fb5e949b79ba450d84676fa876b1c8
Binary files differ
diff --git a/tests/resources/mailmap/.gitted/objects/68/dfd5e5cb6138488680246d134f47ce559f4cf1 b/tests/resources/mailmap/.gitted/objects/68/dfd5e5cb6138488680246d134f47ce559f4cf1
new file mode 100644
index 0000000..ac5229f
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/68/dfd5e5cb6138488680246d134f47ce559f4cf1
Binary files differ
diff --git a/tests/resources/mailmap/.gitted/objects/69/b9768d022706dab26e2af4dd5a13f42039e36f b/tests/resources/mailmap/.gitted/objects/69/b9768d022706dab26e2af4dd5a13f42039e36f
new file mode 100644
index 0000000..bda7a5d
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/69/b9768d022706dab26e2af4dd5a13f42039e36f
Binary files differ
diff --git a/tests/resources/mailmap/.gitted/objects/6a/0fc44893d4867166f9d321f78c269b3e42b08b b/tests/resources/mailmap/.gitted/objects/6a/0fc44893d4867166f9d321f78c269b3e42b08b
new file mode 100644
index 0000000..9c70031
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/6a/0fc44893d4867166f9d321f78c269b3e42b08b
Binary files differ
diff --git a/tests/resources/mailmap/.gitted/objects/6c/dec08ab9bfcd5a3d889f27bbed650317e3ec13 b/tests/resources/mailmap/.gitted/objects/6c/dec08ab9bfcd5a3d889f27bbed650317e3ec13
new file mode 100644
index 0000000..856ba31
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/6c/dec08ab9bfcd5a3d889f27bbed650317e3ec13
Binary files differ
diff --git a/tests/resources/mailmap/.gitted/objects/71/00e631fb4d5deba31fdc8acc98f4fb5c1573fd b/tests/resources/mailmap/.gitted/objects/71/00e631fb4d5deba31fdc8acc98f4fb5c1573fd
new file mode 100644
index 0000000..3b20e6d
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/71/00e631fb4d5deba31fdc8acc98f4fb5c1573fd
Binary files differ
diff --git a/tests/resources/mailmap/.gitted/objects/7e/cbb98d860b304f622b38ce9ab8f08d14d981a8 b/tests/resources/mailmap/.gitted/objects/7e/cbb98d860b304f622b38ce9ab8f08d14d981a8
new file mode 100644
index 0000000..53e775e
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/7e/cbb98d860b304f622b38ce9ab8f08d14d981a8
Binary files differ
diff --git a/tests/resources/mailmap/.gitted/objects/7e/e7b9a4a2a1eda925f6260338c063d8211d5ad5 b/tests/resources/mailmap/.gitted/objects/7e/e7b9a4a2a1eda925f6260338c063d8211d5ad5
new file mode 100644
index 0000000..0d4e94b
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/7e/e7b9a4a2a1eda925f6260338c063d8211d5ad5
@@ -0,0 +1,2 @@
+xÁ
+Â0D=÷+z*‚— =ˆ…~AšFM²¡MAÿÞ´m¡'sšag²Ë+ •Xg›EŠB[o$öd­tF;™0(d=w¯åó¹Åüb¿\R•8´•k¢Ì«('8-XÙÞšñ‚䔬±kƒ¢Œ:—óÞ õ®¸ú§ø9õýÉÊ‹ÒGm ˜r<Ò/ ]ºhîÔH¯¦éžS?ž9´à.pì o›§3KÑ™ÜQ”'óÅ"µùä ¨ƒŠú \ No newline at end of file
diff --git a/tests/resources/mailmap/.gitted/objects/83/714a9223f3e072b85f0d4301cd2081fff3acf2 b/tests/resources/mailmap/.gitted/objects/83/714a9223f3e072b85f0d4301cd2081fff3acf2
new file mode 100644
index 0000000..a50f87e
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/83/714a9223f3e072b85f0d4301cd2081fff3acf2
Binary files differ
diff --git a/tests/resources/mailmap/.gitted/objects/87/ce8d4920a30ddb9547334e7c65806518863ff1 b/tests/resources/mailmap/.gitted/objects/87/ce8d4920a30ddb9547334e7c65806518863ff1
new file mode 100644
index 0000000..b8c4808
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/87/ce8d4920a30ddb9547334e7c65806518863ff1
@@ -0,0 +1,2 @@
+x}NKjC1 ìÚ§Ðd?=[†PÝ–ÞA²e^èKR’ž¾¡é²t5f˜)çãñ0 >n1¤†ZC£¦³eÊš²
+ÍX™bŠM8Eõ…݇t; HVT3WŽ¨R‹!èÄŲ(7äê©föÂN>Çrîð²H_í»ò û_Ü^¯Ïàçà'Œ3eØ !ºòónX‡·Ã»À«Ü¾l]awº«ýXl}—í=øGÝý·æ¾W=MV \ No newline at end of file
diff --git a/tests/resources/mailmap/.gitted/objects/8c/f0547fcb649b44ebaf39b8104982bb0abb4e69 b/tests/resources/mailmap/.gitted/objects/8c/f0547fcb649b44ebaf39b8104982bb0abb4e69
new file mode 100644
index 0000000..402a48e
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/8c/f0547fcb649b44ebaf39b8104982bb0abb4e69
Binary files differ
diff --git a/tests/resources/mailmap/.gitted/objects/94/7ff75c33ac7941a32fe9900118b6ba85ab2be9 b/tests/resources/mailmap/.gitted/objects/94/7ff75c33ac7941a32fe9900118b6ba85ab2be9
new file mode 100644
index 0000000..8b23320
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/94/7ff75c33ac7941a32fe9900118b6ba85ab2be9
Binary files differ
diff --git a/tests/resources/mailmap/.gitted/objects/95/d03c49d94de67d5a05553a1bb22e78f7cdf5ca b/tests/resources/mailmap/.gitted/objects/95/d03c49d94de67d5a05553a1bb22e78f7cdf5ca
new file mode 100644
index 0000000..cd91a3f
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/95/d03c49d94de67d5a05553a1bb22e78f7cdf5ca
@@ -0,0 +1 @@
+xmŽKnÃ0 D»Ö)xŠ$R`î¾hÏ@ªtcÄŸ@Q€¤§¯o»œ™7˜ÉÛ²Lœ/µ¨B A‘d»;Áˆ™s“’7ŽIØ‘2.ºVàh1p$^ó·f²Y•HÑŠ2£tä#¢á[=mÖ)Ÿôrû¹y[.¼>÷ûѽ%ç¼Ú`­ia;VµÀçtføàǯÎ3ôkSC=é¼×Cÿ©›¯ÆxßWûí©†ýÃsÏü·|Lµ \ No newline at end of file
diff --git a/tests/resources/mailmap/.gitted/objects/96/78a4325710507f7bf598a0fde5ebbd88148614 b/tests/resources/mailmap/.gitted/objects/96/78a4325710507f7bf598a0fde5ebbd88148614
new file mode 100644
index 0000000..8874366
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/96/78a4325710507f7bf598a0fde5ebbd88148614
Binary files differ
diff --git a/tests/resources/mailmap/.gitted/objects/a1/6db1cbb8817dddcf199c12d3c81221cf8eefc4 b/tests/resources/mailmap/.gitted/objects/a1/6db1cbb8817dddcf199c12d3c81221cf8eefc4
new file mode 100644
index 0000000..d23d8cc
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/a1/6db1cbb8817dddcf199c12d3c81221cf8eefc4
@@ -0,0 +1 @@
+x+)JMU06c040031QHËÌIÕ+©(aðöUØ¥š÷ÙòàÅV_•¹ËÝû+•» \ No newline at end of file
diff --git a/tests/resources/mailmap/.gitted/objects/a7/054a4b356b3ecdec60cee66e50beaa5b863755 b/tests/resources/mailmap/.gitted/objects/a7/054a4b356b3ecdec60cee66e50beaa5b863755
new file mode 100644
index 0000000..06a3abc
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/a7/054a4b356b3ecdec60cee66e50beaa5b863755
@@ -0,0 +1,3 @@
+xmNKjÃ0íZ§˜ 4ÌèI`‚]–lz‚‘44&þ[†¤§J¶Ù½/ïåeš†
+ÚÐG]E€½$‹ÚÛ|bIH]. á1¹è½!ÕW™+&]I9jc3f­Ï^4G
+ÙiF²Ö+ÞëeYaò• KûïÖçeºñü8Üï' §É`ë|¢ETÍlǪ¬p® ßüø“q„nn¬¯_ÂvhÁ7uõ³L_{è¶ûÒàÿ’zP˜G \ No newline at end of file
diff --git a/tests/resources/mailmap/.gitted/objects/a7/eb40274887baeb01a958ead80d106b5977312c b/tests/resources/mailmap/.gitted/objects/a7/eb40274887baeb01a958ead80d106b5977312c
new file mode 100644
index 0000000..39f70c2
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/a7/eb40274887baeb01a958ead80d106b5977312c
Binary files differ
diff --git a/tests/resources/mailmap/.gitted/objects/c9/e5462de8ec453e94d85f26f64b80ea76fda6d4 b/tests/resources/mailmap/.gitted/objects/c9/e5462de8ec453e94d85f26f64b80ea76fda6d4
new file mode 100644
index 0000000..dbf9523
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/c9/e5462de8ec453e94d85f26f64b80ea76fda6d4
Binary files differ
diff --git a/tests/resources/mailmap/.gitted/objects/d3/e5e624cc7bfb09ac1960ebb6c458021b098f87 b/tests/resources/mailmap/.gitted/objects/d3/e5e624cc7bfb09ac1960ebb6c458021b098f87
new file mode 100644
index 0000000..c69ebe8
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/d3/e5e624cc7bfb09ac1960ebb6c458021b098f87
Binary files differ
diff --git a/tests/resources/mailmap/.gitted/objects/f6/3578091d884c3066a003c50eb6c85ae7542269 b/tests/resources/mailmap/.gitted/objects/f6/3578091d884c3066a003c50eb6c85ae7542269
new file mode 100644
index 0000000..16fd918
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/f6/3578091d884c3066a003c50eb6c85ae7542269
@@ -0,0 +1,2 @@
+xŽ[jÃ0Eó­UÌôËBò_º…2ÒŒ°‰mE-$«¯¡;Èß¹.œ\×uî`zÓ8`dmí ‘)YKÅ32®x«Ý(‹ºS“­Ã`´t¦$Ï%ÑŽœ#å<ÆâK
+Ù„ÁVôÓ§Úàk¾|Òó%˧m_—>Éò/Ç\×3˜`Áˆ!‡öZ«Ýî‘]Þ¼«+3¤¥¦ïú+­Í,ê}ZL{ \ No newline at end of file
diff --git a/tests/resources/mailmap/.gitted/objects/fe/dd34e8baffdb2acfe9a6860bf339287ca942bc b/tests/resources/mailmap/.gitted/objects/fe/dd34e8baffdb2acfe9a6860bf339287ca942bc
new file mode 100644
index 0000000..a13b83d
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/fe/dd34e8baffdb2acfe9a6860bf339287ca942bc
Binary files differ
diff --git a/tests/resources/mailmap/.gitted/objects/fe/ef8f2135df4835496e4d576b1f1bd23510e1c5 b/tests/resources/mailmap/.gitted/objects/fe/ef8f2135df4835496e4d576b1f1bd23510e1c5
new file mode 100644
index 0000000..31f979b
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/objects/fe/ef8f2135df4835496e4d576b1f1bd23510e1c5
@@ -0,0 +1 @@
+xmŽKŠ1†]çu%JÒ 2¸ñ•¤2­Ó/º#¨§7(³˜åÿâûã4 —Úà¦,Ì`œñ2x•-QR)¡ -+Thu#©AÛŒ­˜iá±€•ÉUÍ똳Žœ7¹ >·^jë#eLIЭtÓÇi幃}¸}¯‡8 3ÝýþÊje¤C×ÀV¢”¢†õYáΗ‚=žÜ÷°«:”Žû±îjñŸ¹ø%]ßÄ¿,ñÐpKö \ No newline at end of file
diff --git a/tests/resources/mailmap/.gitted/refs/heads/master b/tests/resources/mailmap/.gitted/refs/heads/master
new file mode 100644
index 0000000..b6dd308
--- /dev/null
+++ b/tests/resources/mailmap/.gitted/refs/heads/master
@@ -0,0 +1 @@
+f63578091d884c3066a003c50eb6c85ae7542269
diff --git a/tests/resources/mailmap/.mailmap b/tests/resources/mailmap/.mailmap
new file mode 100644
index 0000000..7da2ed6
--- /dev/null
+++ b/tests/resources/mailmap/.mailmap
@@ -0,0 +1,9 @@
+# Simple Comment line
+<cto@company.xx> <cto@coompany.xx>
+Some Dude <some@dude.xx> nick1 <bugs@company.xx>
+Other Author <other@author.xx> nick2 <bugs@company.xx>
+Other Author <other@author.xx> <nick2@company.xx>
+Phil Hill <phil@company.xx> # Comment at end of line
+<joseph@company.xx> Joseph <bugs@company.xx>
+Santa Claus <santa.claus@northpole.xx> <me@company.xx>
+Untracked <untracked@company.xx>
diff --git a/tests/resources/mailmap/file.txt b/tests/resources/mailmap/file.txt
new file mode 100644
index 0000000..68dfd5e
--- /dev/null
+++ b/tests/resources/mailmap/file.txt
@@ -0,0 +1,10 @@
+Added by Brad <cto@coompany.xx>
+Added by Brad L. <cto@coompany.xx>
+Added by nick1 <bugs@company.xx>
+Added by nick2 <bugs@company.xx>
+Added by nick3 <bugs@company.xx>
+Added by Some Garbage <nick2@company.xx>
+Added by unknown <phil@company.xx>
+Added by Joseph <bugs@company.xx>
+Added by Clause <me@company.xx>
+Added by Charles <charles@charles.xx>
diff --git a/tests/resources/mailmap/file_override b/tests/resources/mailmap/file_override
new file mode 100644
index 0000000..94293a9
--- /dev/null
+++ b/tests/resources/mailmap/file_override
@@ -0,0 +1,2 @@
+File Override <phil@company.xx>
+Other Name <fileoverridename@company.xx>
diff --git a/tests/resources/merge-recursive/.gitted/HEAD b/tests/resources/merge-recursive/.gitted/HEAD
new file mode 100644
index 0000000..d7cef11
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/branchG-2
diff --git a/tests/resources/merge-recursive/.gitted/config b/tests/resources/merge-recursive/.gitted/config
new file mode 100644
index 0000000..6c9406b
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/config
@@ -0,0 +1,7 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
+ precomposeunicode = true
diff --git a/tests/resources/merge-recursive/.gitted/index b/tests/resources/merge-recursive/.gitted/index
new file mode 100644
index 0000000..571cfd0
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/index
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/info/refs b/tests/resources/merge-recursive/.gitted/info/refs
new file mode 100644
index 0000000..96482e6
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/info/refs
@@ -0,0 +1 @@
+7c7bf85e978f1d18c0566f702d2cb7766b9c8d4f refs/heads/master
diff --git a/tests/resources/merge-recursive/.gitted/objects/00/6b298c5702b04c00370d0414959765b82fd722 b/tests/resources/merge-recursive/.gitted/objects/00/6b298c5702b04c00370d0414959765b82fd722
new file mode 100644
index 0000000..d3fb85f
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/00/6b298c5702b04c00370d0414959765b82fd722
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/00/7f1ee2af8e5d99906867c4237510e1790a89b8 b/tests/resources/merge-recursive/.gitted/objects/00/7f1ee2af8e5d99906867c4237510e1790a89b8
new file mode 100644
index 0000000..d9399d7
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/00/7f1ee2af8e5d99906867c4237510e1790a89b8
@@ -0,0 +1,3 @@
+x¥NI
+1ô<¯ÈF²tgA<zóY:L™ÈñûFñBj£¨Øj-I„]߈faÑèh
+R)›<€+Î…sZdä8ÝýFkg$RÌQqe!‡&Zg¬ R Ãèûg_ÚÆ.éå·ÄnK«¶² ÷ÃNô ~j[=2€Ö2ŠÍ8Ÿ†;Îvúsfº¶Tr‰¾—qaà<Ëé `bQ \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/01/6eef4a6fefd36bdcaa93ad773449ddc5c73cbb b/tests/resources/merge-recursive/.gitted/objects/01/6eef4a6fefd36bdcaa93ad773449ddc5c73cbb
new file mode 100644
index 0000000..9008584
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/01/6eef4a6fefd36bdcaa93ad773449ddc5c73cbb
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/03/9d0da126f24b819a5a38186249c7f96be3824c b/tests/resources/merge-recursive/.gitted/objects/03/9d0da126f24b819a5a38186249c7f96be3824c
new file mode 100644
index 0000000..79ad635
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/03/9d0da126f24b819a5a38186249c7f96be3824c
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/05/63b7706dcdcf94bc0c02cd96c137940278eca9 b/tests/resources/merge-recursive/.gitted/objects/05/63b7706dcdcf94bc0c02cd96c137940278eca9
new file mode 100644
index 0000000..c6d38a7
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/05/63b7706dcdcf94bc0c02cd96c137940278eca9
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/05/c6a04ac101ab1a9836a95d5ec8d16b6f6304fd b/tests/resources/merge-recursive/.gitted/objects/05/c6a04ac101ab1a9836a95d5ec8d16b6f6304fd
new file mode 100644
index 0000000..c6a3a3b
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/05/c6a04ac101ab1a9836a95d5ec8d16b6f6304fd
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/06/d3fefb8726ab1099acc76e02dfb85e034b2538 b/tests/resources/merge-recursive/.gitted/objects/06/d3fefb8726ab1099acc76e02dfb85e034b2538
new file mode 100644
index 0000000..b3919aa
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/06/d3fefb8726ab1099acc76e02dfb85e034b2538
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/06/db153c36829fc656e05cdf5a3bf7183f3c10aa b/tests/resources/merge-recursive/.gitted/objects/06/db153c36829fc656e05cdf5a3bf7183f3c10aa
new file mode 100644
index 0000000..85887e0
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/06/db153c36829fc656e05cdf5a3bf7183f3c10aa
@@ -0,0 +1,2 @@
+x%P»nÃ@ ë|_ÁH‚ŠN™Š.}%)Q¶eû€óÉ8É1ò÷Õ9›@R$¥&Iƒ—×ç§÷ßãߧ¯Ë÷!„w6pFÃ,KîÒ£*ÊHL s¿ß¯¤#¢¡Ý0ÊÝ+3î²”0‹í0/`#Cib'‡Â]äl
+RôR°q£o©,ók´ñ¹>ü\ŽŸçóµvXɸìP£zIIÖM6iY],ÜW®z’p²Bîë¡zûPdF4òVÊåæ½ .¨·xÂV!‹y©“®~9˜Ö€¦0uõÌho°U`Ô$Þë,’µ_Rí:-êa2¡%Sw^cJ ®…>fFŸèæO‚ùvý×;„+‹Ÿ \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/07/10c3c796e0704361472ecb904413fca0107a25 b/tests/resources/merge-recursive/.gitted/objects/07/10c3c796e0704361472ecb904413fca0107a25
new file mode 100644
index 0000000..9f48594
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/07/10c3c796e0704361472ecb904413fca0107a25
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/07/2d89dcf3a7671ac34a8e875bb72fb39bcf14d7 b/tests/resources/merge-recursive/.gitted/objects/07/2d89dcf3a7671ac34a8e875bb72fb39bcf14d7
new file mode 100644
index 0000000..69104c9
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/07/2d89dcf3a7671ac34a8e875bb72fb39bcf14d7
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/08/f01e1bff7e442d574eb221913515b4bd27ccd6 b/tests/resources/merge-recursive/.gitted/objects/08/f01e1bff7e442d574eb221913515b4bd27ccd6
new file mode 100644
index 0000000..c2fbf5b
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/08/f01e1bff7e442d574eb221913515b4bd27ccd6
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/0b/b7ed583d7e9ad507e8b902594f5c9126ea456b b/tests/resources/merge-recursive/.gitted/objects/0b/b7ed583d7e9ad507e8b902594f5c9126ea456b
new file mode 100644
index 0000000..e7b4ba1
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/0b/b7ed583d7e9ad507e8b902594f5c9126ea456b
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/0b/beee1982b493330e375a85bbfddaba3d561556 b/tests/resources/merge-recursive/.gitted/objects/0b/beee1982b493330e375a85bbfddaba3d561556
new file mode 100644
index 0000000..66b9272
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/0b/beee1982b493330e375a85bbfddaba3d561556
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/0c/e202f64fa8356c1a32835fce4058ca76b0c7ed b/tests/resources/merge-recursive/.gitted/objects/0c/e202f64fa8356c1a32835fce4058ca76b0c7ed
new file mode 100644
index 0000000..9a84b71
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/0c/e202f64fa8356c1a32835fce4058ca76b0c7ed
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/0e/8126647ec607f0a14122cec4b15315d790c8ff b/tests/resources/merge-recursive/.gitted/objects/0e/8126647ec607f0a14122cec4b15315d790c8ff
new file mode 100644
index 0000000..c99a686
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/0e/8126647ec607f0a14122cec4b15315d790c8ff
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/0e/c39d71c1b074905350ce20ce3f0629f737a2a9 b/tests/resources/merge-recursive/.gitted/objects/0e/c39d71c1b074905350ce20ce3f0629f737a2a9
new file mode 100644
index 0000000..68808d2
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/0e/c39d71c1b074905350ce20ce3f0629f737a2a9
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/0f/a6ead2731b9d138afe38c336c9727ea05027a7 b/tests/resources/merge-recursive/.gitted/objects/0f/a6ead2731b9d138afe38c336c9727ea05027a7
new file mode 100644
index 0000000..b06362d
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/0f/a6ead2731b9d138afe38c336c9727ea05027a7
@@ -0,0 +1 @@
+x¥»JAEç+*Ûh¥k«ú"ÂbhæTwÕ8Ì´´-ûûŽ¯\0»/ÜÚÖõ2€<ÝŒnŒKIÊÎò\9¹Dµº`5TdÏ©Ä4½J·m€› ³I)ꈼ"-鄱晴ˆOú»'„3–YÉG ŠJAXSd#´(NPx’÷±´z•®ð¼´õ­mpg{ú©ì«øq·µ­÷€Ì>eÌ9ÁѱsÓžîç†ý3=Y1ø¦Áá¯pÙFƒÒe«ËùˆÓ2Õoz \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/12/4d4fe29d3433fdaa2f0f455d226f2c79d89cf3 b/tests/resources/merge-recursive/.gitted/objects/12/4d4fe29d3433fdaa2f0f455d226f2c79d89cf3
new file mode 100644
index 0000000..f0ea020
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/12/4d4fe29d3433fdaa2f0f455d226f2c79d89cf3
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/15/311229e70fa62653f73dde1d4deef1a8e47a11 b/tests/resources/merge-recursive/.gitted/objects/15/311229e70fa62653f73dde1d4deef1a8e47a11
new file mode 100644
index 0000000..8c21bb3
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/15/311229e70fa62653f73dde1d4deef1a8e47a11
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/15/faa0c9991f2d65686e844651faa2ff9827887b b/tests/resources/merge-recursive/.gitted/objects/15/faa0c9991f2d65686e844651faa2ff9827887b
new file mode 100644
index 0000000..54cd6f2
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/15/faa0c9991f2d65686e844651faa2ff9827887b
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/16/895aa5e13f8907d4adab81285557d938fad342 b/tests/resources/merge-recursive/.gitted/objects/16/895aa5e13f8907d4adab81285557d938fad342
new file mode 100644
index 0000000..4d3319b
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/16/895aa5e13f8907d4adab81285557d938fad342
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/17/946ad3088f931102e5d81f94cf2825fc188953 b/tests/resources/merge-recursive/.gitted/objects/17/946ad3088f931102e5d81f94cf2825fc188953
new file mode 100644
index 0000000..9cc133e
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/17/946ad3088f931102e5d81f94cf2825fc188953
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/18/2d0d250d1d7adcc60c178be5be98358b3a2fd1 b/tests/resources/merge-recursive/.gitted/objects/18/2d0d250d1d7adcc60c178be5be98358b3a2fd1
new file mode 100644
index 0000000..96674c8
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/18/2d0d250d1d7adcc60c178be5be98358b3a2fd1
@@ -0,0 +1,5 @@
+xŽK
+1]ç¹€’_§ñâ>ééè@ÆHÌÞÞñ
+î^<jË2i4îFg–9ʼn
++ŒÁ ¤3ØL6{vX
+`ÙFÐâ™:?†´CtŽM`y"Ò*iT)›R´3M„!ˆ´Ž{ëòòé3Ë+ךäqü ¾Ï·%Íõ@m9I Ú{ð¨œÜ« ”Øìvpð©xµµ‹/øE \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/1b/c7bcccf4bbdc8bfba2331a37ad5e9cf1dd321c b/tests/resources/merge-recursive/.gitted/objects/1b/c7bcccf4bbdc8bfba2331a37ad5e9cf1dd321c
new file mode 100644
index 0000000..8117167
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/1b/c7bcccf4bbdc8bfba2331a37ad5e9cf1dd321c
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/1b/de1883de4977ea3e664b315da951d1f614c3b1 b/tests/resources/merge-recursive/.gitted/objects/1b/de1883de4977ea3e664b315da951d1f614c3b1
new file mode 100644
index 0000000..67daf36
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/1b/de1883de4977ea3e664b315da951d1f614c3b1
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/1c/1bdb80c04233d1a9b9755913ee233987be6175 b/tests/resources/merge-recursive/.gitted/objects/1c/1bdb80c04233d1a9b9755913ee233987be6175
new file mode 100644
index 0000000..a214649
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/1c/1bdb80c04233d1a9b9755913ee233987be6175
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/1e/8dff96faaaa24f84943d2d9601dde61cb0398a b/tests/resources/merge-recursive/.gitted/objects/1e/8dff96faaaa24f84943d2d9601dde61cb0398a
new file mode 100644
index 0000000..b6185f2
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/1e/8dff96faaaa24f84943d2d9601dde61cb0398a
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/21/950d5e4e4d1a871b4dfcf72ecb6b9c162c434e b/tests/resources/merge-recursive/.gitted/objects/21/950d5e4e4d1a871b4dfcf72ecb6b9c162c434e
new file mode 100644
index 0000000..a877326
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/21/950d5e4e4d1a871b4dfcf72ecb6b9c162c434e
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/23/b427bf6278724433e64ef4cf6dc166c4f2e246 b/tests/resources/merge-recursive/.gitted/objects/23/b427bf6278724433e64ef4cf6dc166c4f2e246
new file mode 100644
index 0000000..03be909
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/23/b427bf6278724433e64ef4cf6dc166c4f2e246
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/23/cf2687a9327d55abbbd788ff04fa932072aebc b/tests/resources/merge-recursive/.gitted/objects/23/cf2687a9327d55abbbd788ff04fa932072aebc
new file mode 100644
index 0000000..5cc665a
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/23/cf2687a9327d55abbbd788ff04fa932072aebc
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/26/d3c94459b4faa08f009b15867993ca34153592 b/tests/resources/merge-recursive/.gitted/objects/26/d3c94459b4faa08f009b15867993ca34153592
new file mode 100644
index 0000000..dd7b1a2
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/26/d3c94459b4faa08f009b15867993ca34153592
@@ -0,0 +1,2 @@
+xŽA
+1 E]÷¹€’¤vÚ‚ˆ+O î3mF¦Ž”*x{ë\þ÷yðÒZÊÜ€‰7­ªBæH5ø‰ÃÀÈš:sYƒõBÁ9D²£3O©úhàûk1‡œT-£Ø‘¦¨™pt÷>N"1[!#¯v_+\>uV¸ê²Úo,ïӭȼìÒZŽ@Žæî2l1 šN{`Ó?Ts6_?B* \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/2c/ba583804a4a6fad1baf97c959be447238d1489 b/tests/resources/merge-recursive/.gitted/objects/2c/ba583804a4a6fad1baf97c959be447238d1489
new file mode 100644
index 0000000..c0a60a1
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/2c/ba583804a4a6fad1baf97c959be447238d1489
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/2e/7ae0d42fb7b6126f6a08ac6314ac07833a52f6 b/tests/resources/merge-recursive/.gitted/objects/2e/7ae0d42fb7b6126f6a08ac6314ac07833a52f6
new file mode 100644
index 0000000..6a9651c
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/2e/7ae0d42fb7b6126f6a08ac6314ac07833a52f6
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/30/39c07db695c8c99d0a7c7e32f0afe40eae0be0 b/tests/resources/merge-recursive/.gitted/objects/30/39c07db695c8c99d0a7c7e32f0afe40eae0be0
new file mode 100644
index 0000000..cc21b5c
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/30/39c07db695c8c99d0a7c7e32f0afe40eae0be0
@@ -0,0 +1,3 @@
+xŽQ
+!@ûösBÇÕ"úéÑÿ茵 mˆݾí
+}¾^^Z› ÁÍè"ÀÉâ‚ÉÇ(ÂÑJ–ý„®8ñÙiãÅŒêI]ö9’Õ8‹XÔd“)QØèäNûXˆ"[2Š^ã¾t¸|ú,p•Z ãõ}º5šë./íƸ¶[´V«]‡ü‘ª³ú¯¯Bü \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/34/8f16ffaeb73f319a75cec5b16a0a47d2d5e27c b/tests/resources/merge-recursive/.gitted/objects/34/8f16ffaeb73f319a75cec5b16a0a47d2d5e27c
new file mode 100644
index 0000000..bd1b1f6
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/34/8f16ffaeb73f319a75cec5b16a0a47d2d5e27c
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/35/8efd6f589384fa8baf92234db9c7899a53916e b/tests/resources/merge-recursive/.gitted/objects/35/8efd6f589384fa8baf92234db9c7899a53916e
new file mode 100644
index 0000000..aefc81a
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/35/8efd6f589384fa8baf92234db9c7899a53916e
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/35/dda4f3f9b3794d92a46d908790e550ed100eae b/tests/resources/merge-recursive/.gitted/objects/35/dda4f3f9b3794d92a46d908790e550ed100eae
new file mode 100644
index 0000000..d6cabb4
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/35/dda4f3f9b3794d92a46d908790e550ed100eae
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/36/71e42c8c8302d1a71c0ed7bf2b0a938e9e20f9 b/tests/resources/merge-recursive/.gitted/objects/36/71e42c8c8302d1a71c0ed7bf2b0a938e9e20f9
new file mode 100644
index 0000000..4f70ad6
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/36/71e42c8c8302d1a71c0ed7bf2b0a938e9e20f9
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/37/185b25a204309bf74817da1a607518f13ca3ed b/tests/resources/merge-recursive/.gitted/objects/37/185b25a204309bf74817da1a607518f13ca3ed
new file mode 100644
index 0000000..a8cf005
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/37/185b25a204309bf74817da1a607518f13ca3ed
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/37/a5054a9f9b4628e3924c5cb8f2147c6e2a3efc b/tests/resources/merge-recursive/.gitted/objects/37/a5054a9f9b4628e3924c5cb8f2147c6e2a3efc
new file mode 100644
index 0000000..4591f0e
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/37/a5054a9f9b4628e3924c5cb8f2147c6e2a3efc
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/38/55170cef875708da06ab9ad7fc6a73b531cda1 b/tests/resources/merge-recursive/.gitted/objects/38/55170cef875708da06ab9ad7fc6a73b531cda1
new file mode 100644
index 0000000..7da945e
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/38/55170cef875708da06ab9ad7fc6a73b531cda1
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/39/78944e4cd53edcc10a170ab2ff142f7295b958 b/tests/resources/merge-recursive/.gitted/objects/39/78944e4cd53edcc10a170ab2ff142f7295b958
new file mode 100644
index 0000000..b18fd48
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/39/78944e4cd53edcc10a170ab2ff142f7295b958
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/3a/0dc89a8bd20e74fae69d2e038b47360fafb02e b/tests/resources/merge-recursive/.gitted/objects/3a/0dc89a8bd20e74fae69d2e038b47360fafb02e
new file mode 100644
index 0000000..fdff502
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/3a/0dc89a8bd20e74fae69d2e038b47360fafb02e
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/3a/3f5a6ec1c968d1d2d5d20dee0d161a4351f279 b/tests/resources/merge-recursive/.gitted/objects/3a/3f5a6ec1c968d1d2d5d20dee0d161a4351f279
new file mode 100644
index 0000000..f39a127
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/3a/3f5a6ec1c968d1d2d5d20dee0d161a4351f279
@@ -0,0 +1 @@
+x¥OÛ !ô›*h@³àÂBbŒ?× ðXr&Þaµ}ÑØóÈLfR]–K—qÓ³°Q{— Ž€ `OzãÉšètɤµ¸…Æk—”(gØ“+*+—ÀX[F8뉬>¹ŒE„GŸk“S~…–åy®Ë½®òÀCý Û¥º¥B4Æ€³NnÄPÇØÎÖˆ)ËOWñ¯N: \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/3a/8c70144d0334721154b1e0529716b368483d6f b/tests/resources/merge-recursive/.gitted/objects/3a/8c70144d0334721154b1e0529716b368483d6f
new file mode 100644
index 0000000..958d17d
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/3a/8c70144d0334721154b1e0529716b368483d6f
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/3b/919b6e8a575b4779c8243ebea3e3beb436e88f b/tests/resources/merge-recursive/.gitted/objects/3b/919b6e8a575b4779c8243ebea3e3beb436e88f
new file mode 100644
index 0000000..c85731d
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/3b/919b6e8a575b4779c8243ebea3e3beb436e88f
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/3e/eff81b57a0ac15a5ab6bb3a8e92511a01a429c b/tests/resources/merge-recursive/.gitted/objects/3e/eff81b57a0ac15a5ab6bb3a8e92511a01a429c
new file mode 100644
index 0000000..6a6c654
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/3e/eff81b57a0ac15a5ab6bb3a8e92511a01a429c
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/3f/d41804a7906db846af5e868444782e546af46a b/tests/resources/merge-recursive/.gitted/objects/3f/d41804a7906db846af5e868444782e546af46a
new file mode 100644
index 0000000..4915b86
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/3f/d41804a7906db846af5e868444782e546af46a
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/40/9f5d072decec684331672f2d6c0a9bc3640adb b/tests/resources/merge-recursive/.gitted/objects/40/9f5d072decec684331672f2d6c0a9bc3640adb
new file mode 100644
index 0000000..b4c9005
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/40/9f5d072decec684331672f2d6c0a9bc3640adb
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/41/71bb8d40e9fc830d79b757dc06ec6c14548b78 b/tests/resources/merge-recursive/.gitted/objects/41/71bb8d40e9fc830d79b757dc06ec6c14548b78
new file mode 100644
index 0000000..5dc102d
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/41/71bb8d40e9fc830d79b757dc06ec6c14548b78
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/42/1b392106e079df6d412babd5636697938269ec b/tests/resources/merge-recursive/.gitted/objects/42/1b392106e079df6d412babd5636697938269ec
new file mode 100644
index 0000000..3a8324c
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/42/1b392106e079df6d412babd5636697938269ec
@@ -0,0 +1,2 @@
+x¥Q
+Â0DýÎ)re7ÉDüé ¼@“Ý¥‚m¤F½¾U¼3o`˜)uš.ÍzÄM[D¬Š‚Ó½,˜‚PH^‚w*)c&Îæ6,27›JÊJAºDêØQ£&ðìKN)ÆÜbT3<ÚXÛókXØžÇ:Ýël²Ò:É7ø¹]©ÓÑ:Ä¢‹v `VºŽmògéy½ü”ájÞ=ïO“ \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/42/44d13e2bbc38510320443bbb003f3967d12436 b/tests/resources/merge-recursive/.gitted/objects/42/44d13e2bbc38510320443bbb003f3967d12436
new file mode 100644
index 0000000..a19b191
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/42/44d13e2bbc38510320443bbb003f3967d12436
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/42/cdad903aef3e7b614675e6584a8be417941911 b/tests/resources/merge-recursive/.gitted/objects/42/cdad903aef3e7b614675e6584a8be417941911
new file mode 100644
index 0000000..99b5e6d
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/42/cdad903aef3e7b614675e6584a8be417941911
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/43/2faca0c62dc556ad71a22f23e541a46a8b0f6f b/tests/resources/merge-recursive/.gitted/objects/43/2faca0c62dc556ad71a22f23e541a46a8b0f6f
new file mode 100644
index 0000000..ff18f0e
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/43/2faca0c62dc556ad71a22f23e541a46a8b0f6f
@@ -0,0 +1,2 @@
+x+)JMU022g040031QH,.H,JL/-Ö+©(aø¿9/Ð>þ~WöŠ€EŒNéYZGj¡
+“RSÓÀj2¾Ièû´^ž'Ìy³¦Î51ìΆi05ù¥™99™`eÞ5a Ýz%õ潎’½Ç×ÐÊU–^”XV VtäÙ™Åo²ˆô]2üY~¿ÇPŽ1ª(¿²¸$µbãzùãõ7×Þg\Q·ñdLÉ”£3 ªÊRsÀjü/]3ßû”©VLõë›lgž{ÄW[ÿ \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/43/5424798e5e1b21dd4588d1c291ba4eb179a838 b/tests/resources/merge-recursive/.gitted/objects/43/5424798e5e1b21dd4588d1c291ba4eb179a838
new file mode 100644
index 0000000..58ab239
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/43/5424798e5e1b21dd4588d1c291ba4eb179a838
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/43/6ea75c99f527e4b42fddb46abedf7726eb719d b/tests/resources/merge-recursive/.gitted/objects/43/6ea75c99f527e4b42fddb46abedf7726eb719d
new file mode 100644
index 0000000..e8825d8
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/43/6ea75c99f527e4b42fddb46abedf7726eb719d
@@ -0,0 +1,2 @@
+x¥ŽKj1D³Ö)t‡n©õc¼™ev¾@»ÕÃÌbFf,“ëG ¹¡U¯ (iÛ¶vë}ôCÕ‚ft1RR‰f`$tNTèŽÁc¨©€äy6>tï–]‡R€ÙÇ!Þ=@̱Tä\
+Ç”½áW_Úa§úÍGµ·¥m϶۳úë®úWü§OiÛÅ"QÈ%ø”ì À :Îv}sÆ|µºÎ«p_Ç…¡éäÌ7QA \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/44/faf5fba1af850dae54f8b2345938d3c7ae479f b/tests/resources/merge-recursive/.gitted/objects/44/faf5fba1af850dae54f8b2345938d3c7ae479f
new file mode 100644
index 0000000..d0bc099
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/44/faf5fba1af850dae54f8b2345938d3c7ae479f
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/48/3065df53c0f4a02cdc6b2910b05d388fc17ffb b/tests/resources/merge-recursive/.gitted/objects/48/3065df53c0f4a02cdc6b2910b05d388fc17ffb
new file mode 100644
index 0000000..298251b
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/48/3065df53c0f4a02cdc6b2910b05d388fc17ffb
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/4a/06b258fed8a4d15967ec4253ae7366b70f727d b/tests/resources/merge-recursive/.gitted/objects/4a/06b258fed8a4d15967ec4253ae7366b70f727d
new file mode 100644
index 0000000..d3e1815
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/4a/06b258fed8a4d15967ec4253ae7366b70f727d
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/4b/7c5650008b2e747fe1809eeb5a1dde0e80850a b/tests/resources/merge-recursive/.gitted/objects/4b/7c5650008b2e747fe1809eeb5a1dde0e80850a
new file mode 100644
index 0000000..0163985
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/4b/7c5650008b2e747fe1809eeb5a1dde0e80850a
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 b/tests/resources/merge-recursive/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904
new file mode 100644
index 0000000..adf6411
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/4c/49317a0912ca559d2048bc329994eb7d10474f b/tests/resources/merge-recursive/.gitted/objects/4c/49317a0912ca559d2048bc329994eb7d10474f
new file mode 100644
index 0000000..d7bb4d3
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/4c/49317a0912ca559d2048bc329994eb7d10474f
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/4c/62e9482ed42c1a6d08891906b26126daa4a8f5 b/tests/resources/merge-recursive/.gitted/objects/4c/62e9482ed42c1a6d08891906b26126daa4a8f5
new file mode 100644
index 0000000..f3cee9a
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/4c/62e9482ed42c1a6d08891906b26126daa4a8f5
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/4d/fc1be85a9d6c9898152444d32b238b4aecf8cc b/tests/resources/merge-recursive/.gitted/objects/4d/fc1be85a9d6c9898152444d32b238b4aecf8cc
new file mode 100644
index 0000000..9db684d
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/4d/fc1be85a9d6c9898152444d32b238b4aecf8cc
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/4e/21d2d63357bde5027d1625f5ec6b430cdeb143 b/tests/resources/merge-recursive/.gitted/objects/4e/21d2d63357bde5027d1625f5ec6b430cdeb143
new file mode 100644
index 0000000..34f183d
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/4e/21d2d63357bde5027d1625f5ec6b430cdeb143
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/4e/70a6b06fc62481f80fbb74327849e7170eebff b/tests/resources/merge-recursive/.gitted/objects/4e/70a6b06fc62481f80fbb74327849e7170eebff
new file mode 100644
index 0000000..1dfcec5
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/4e/70a6b06fc62481f80fbb74327849e7170eebff
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/4f/4e85a0ab8515e34302721fbcec06fa9d9c1a9a b/tests/resources/merge-recursive/.gitted/objects/4f/4e85a0ab8515e34302721fbcec06fa9d9c1a9a
new file mode 100644
index 0000000..4752ea0
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/4f/4e85a0ab8515e34302721fbcec06fa9d9c1a9a
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/50/4dd93fb5b9c2a28c094c6e84ef0606de1e9b5c b/tests/resources/merge-recursive/.gitted/objects/50/4dd93fb5b9c2a28c094c6e84ef0606de1e9b5c
new file mode 100644
index 0000000..214d307
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/50/4dd93fb5b9c2a28c094c6e84ef0606de1e9b5c
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/50/dfa64a56b488fe8082371b182c8a3e3c942332 b/tests/resources/merge-recursive/.gitted/objects/50/dfa64a56b488fe8082371b182c8a3e3c942332
new file mode 100644
index 0000000..7c90c99
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/50/dfa64a56b488fe8082371b182c8a3e3c942332
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/50/e4facaafb746cfed89287206274193c1417288 b/tests/resources/merge-recursive/.gitted/objects/50/e4facaafb746cfed89287206274193c1417288
new file mode 100644
index 0000000..b1eaee5
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/50/e4facaafb746cfed89287206274193c1417288
@@ -0,0 +1,2 @@
+x+)JMU022g040031QH,.H,JL/-Ö+©(aø¿9/Ð>þ~WöŠ€EŒNéYZGj¡
+“RSÓÀj2¾Ièû´^ž'Ìy³¦Î51ìΆi05ù¥™99™`eÞ5a Ýz%õ潎’½Ç×ÐÊU–^”XV VtäÙ™Åo²ˆô]2üY~¿ÇPŽ1ª(¿²¸$µbãzùãõ7×Þg\Q·ñdLÉ”£3 ªÊRsÀjD-89S^75#¯Å-ð×¢ÿ3Ô;ªÊ\¥ \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/51/135c5884d7dd132fef3b432cca5826bab98f37 b/tests/resources/merge-recursive/.gitted/objects/51/135c5884d7dd132fef3b432cca5826bab98f37
new file mode 100644
index 0000000..95818f1
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/51/135c5884d7dd132fef3b432cca5826bab98f37
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/51/60ab78c1973dcd7cdebe2345dc8fcfc755e76d b/tests/resources/merge-recursive/.gitted/objects/51/60ab78c1973dcd7cdebe2345dc8fcfc755e76d
new file mode 100644
index 0000000..f3f99d7
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/51/60ab78c1973dcd7cdebe2345dc8fcfc755e76d
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/53/9bd011c4822c560c1d17cab095006b7a10f707 b/tests/resources/merge-recursive/.gitted/objects/53/9bd011c4822c560c1d17cab095006b7a10f707
new file mode 100644
index 0000000..3fa1e1f
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/53/9bd011c4822c560c1d17cab095006b7a10f707
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/56/07a8c4601a737daadd1f470bde3142aff57026 b/tests/resources/merge-recursive/.gitted/objects/56/07a8c4601a737daadd1f470bde3142aff57026
new file mode 100644
index 0000000..bf3639d
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/56/07a8c4601a737daadd1f470bde3142aff57026
@@ -0,0 +1 @@
+x¥MJ1„]Ï)ú#éÎ?<Ä‹·pç:Žó3‘¼ˆ×w=Ô¦ê£((éû~›`Mx˜CrB‰Y‹MSµ‰P£-}®âðtŽjè–zL`ÎJÁRv­R §jåBV8‰Ze&þõƒ6‹Õz¶ÍsTr͵̽2š±©.ü9·>à¥~ñ¨ð¶õýÞ¸èIÜó~“Ñï½ÍGéû s1GŠ!Áj¼1ËIÏcSÿ1±¼êxW(ƒÙàºÜŽÙóuÅå#rbV \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/56/fcbad344aafe519bafcc33c87b8e64849d82ab b/tests/resources/merge-recursive/.gitted/objects/56/fcbad344aafe519bafcc33c87b8e64849d82ab
new file mode 100644
index 0000000..06bea32
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/56/fcbad344aafe519bafcc33c87b8e64849d82ab
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/5a/47615db824433f816ba62217dda6d46c5a7640 b/tests/resources/merge-recursive/.gitted/objects/5a/47615db824433f816ba62217dda6d46c5a7640
new file mode 100644
index 0000000..c1e30ce
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/5a/47615db824433f816ba62217dda6d46c5a7640
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/5a/ba269b3be41fc8db38068d3948c8af543fe609 b/tests/resources/merge-recursive/.gitted/objects/5a/ba269b3be41fc8db38068d3948c8af543fe609
new file mode 100644
index 0000000..85bc8f5
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/5a/ba269b3be41fc8db38068d3948c8af543fe609
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/5b/8e1e56cb99e8b99ac22eec8aebf6422ecd08c0 b/tests/resources/merge-recursive/.gitted/objects/5b/8e1e56cb99e8b99ac22eec8aebf6422ecd08c0
new file mode 100644
index 0000000..c3e6d31
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/5b/8e1e56cb99e8b99ac22eec8aebf6422ecd08c0
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/5c/27b5f7c6f6dd4e5b4d64976741d56c2df8f48a b/tests/resources/merge-recursive/.gitted/objects/5c/27b5f7c6f6dd4e5b4d64976741d56c2df8f48a
new file mode 100644
index 0000000..783d085
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/5c/27b5f7c6f6dd4e5b4d64976741d56c2df8f48a
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/5d/998d5f278aff5693711bc48f6852aac4b603ad b/tests/resources/merge-recursive/.gitted/objects/5d/998d5f278aff5693711bc48f6852aac4b603ad
new file mode 100644
index 0000000..a7795f5
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/5d/998d5f278aff5693711bc48f6852aac4b603ad
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/5e/8747f5200fac0f945a07daf6163ca9cb1a8da9 b/tests/resources/merge-recursive/.gitted/objects/5e/8747f5200fac0f945a07daf6163ca9cb1a8da9
new file mode 100644
index 0000000..fa1c9e5
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/5e/8747f5200fac0f945a07daf6163ca9cb1a8da9
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/5f/18576d464946eb2338daeb8b4030019961f505 b/tests/resources/merge-recursive/.gitted/objects/5f/18576d464946eb2338daeb8b4030019961f505
new file mode 100644
index 0000000..d03742b
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/5f/18576d464946eb2338daeb8b4030019961f505
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/61/6d1209afac499b005f68309e1593b44899b054 b/tests/resources/merge-recursive/.gitted/objects/61/6d1209afac499b005f68309e1593b44899b054
new file mode 100644
index 0000000..6a06214
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/61/6d1209afac499b005f68309e1593b44899b054
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/63/e8773becdea9c3699c95a5740be5baa8be8d69 b/tests/resources/merge-recursive/.gitted/objects/63/e8773becdea9c3699c95a5740be5baa8be8d69
new file mode 100644
index 0000000..6d5c320
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/63/e8773becdea9c3699c95a5740be5baa8be8d69
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/65/bea8448ca5b3104628ffbca553c54bde54b0fc b/tests/resources/merge-recursive/.gitted/objects/65/bea8448ca5b3104628ffbca553c54bde54b0fc
new file mode 100644
index 0000000..031c913
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/65/bea8448ca5b3104628ffbca553c54bde54b0fc
@@ -0,0 +1,3 @@
+x¥ŽÑ !Dý¦
+а,ãØÀ»—3‘à jûžÆü›y“L^®¥\º¶˜6½‰h±9ºqDLÉ’ó~
+ä ¤ÉFŽ'¼ºQ“¥k$œ< ’!§!2°eÏÖ°ˆa€z˜lHŠ}®MŸøEõy®å^½—•~ÒQ¾Ã¯ír- nð`‚Õ[ãŒQ+]e»üy£žBWõyM… \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/66/6ffdfcf1eaa5641fa31064bf2607327e843c09 b/tests/resources/merge-recursive/.gitted/objects/66/6ffdfcf1eaa5641fa31064bf2607327e843c09
new file mode 100644
index 0000000..8d6be42
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/66/6ffdfcf1eaa5641fa31064bf2607327e843c09
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/68/a2e1ee61a23a4728fe6b35580fbbbf729df370 b/tests/resources/merge-recursive/.gitted/objects/68/a2e1ee61a23a4728fe6b35580fbbbf729df370
new file mode 100644
index 0000000..6d7c948
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/68/a2e1ee61a23a4728fe6b35580fbbbf729df370
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/68/af1fc7407fd9addf1701a87eb1c95c7494c598 b/tests/resources/merge-recursive/.gitted/objects/68/af1fc7407fd9addf1701a87eb1c95c7494c598
new file mode 100644
index 0000000..6aaf79f
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/68/af1fc7407fd9addf1701a87eb1c95c7494c598
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/68/f6182f4c85d39e1309d97c7e456156dc9c0096 b/tests/resources/merge-recursive/.gitted/objects/68/f6182f4c85d39e1309d97c7e456156dc9c0096
new file mode 100644
index 0000000..ed1de3a
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/68/f6182f4c85d39e1309d97c7e456156dc9c0096
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/6c/778edd0e4cf394f5a3df8b96db516024cc1bb8 b/tests/resources/merge-recursive/.gitted/objects/6c/778edd0e4cf394f5a3df8b96db516024cc1bb8
new file mode 100644
index 0000000..ec1db19
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/6c/778edd0e4cf394f5a3df8b96db516024cc1bb8
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/6e/f31d35a3f5abc1e24f4f9afa5cb2016f03fa2d b/tests/resources/merge-recursive/.gitted/objects/6e/f31d35a3f5abc1e24f4f9afa5cb2016f03fa2d
new file mode 100644
index 0000000..e95a5e2
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/6e/f31d35a3f5abc1e24f4f9afa5cb2016f03fa2d
@@ -0,0 +1 @@
+x¥AN!]sŠ¾€¦ahcÜüx ÿ$2õúŽgpW©Eå½:zß8‡5UA-YG„ “¤ˆÎzõAƒl²+ë&LLd>óÔcW.-’&ŽÍŠ)„ÆèÄÕÂBI5Šo&­û˜p“Ÿ<Þã€g½ì½ö½ÎqŽ¶žêè/`½çÄnÛ,<"!šË^C—þ#anr]ýÖüû]ç»Bɧš_*íPV \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/71/3e438567b28543235faf265c4c5b02b437c7fd b/tests/resources/merge-recursive/.gitted/objects/71/3e438567b28543235faf265c4c5b02b437c7fd
new file mode 100644
index 0000000..8b1f688
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/71/3e438567b28543235faf265c4c5b02b437c7fd
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/71/c50785d8d512293bd3af838b131f3da5829ebc b/tests/resources/merge-recursive/.gitted/objects/71/c50785d8d512293bd3af838b131f3da5829ebc
new file mode 100644
index 0000000..23c4033
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/71/c50785d8d512293bd3af838b131f3da5829ebc
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/72/3181f1bfd30e47a6d1d36a4d874e31e7a0a1a4 b/tests/resources/merge-recursive/.gitted/objects/72/3181f1bfd30e47a6d1d36a4d874e31e7a0a1a4
new file mode 100644
index 0000000..01d113e
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/72/3181f1bfd30e47a6d1d36a4d874e31e7a0a1a4
@@ -0,0 +1,2 @@
+x¥[
+1 Eýî*²¥í¤AÄŸÙÈ´)#8S©U·owàÇ…ääÁ½©.Ë¥ƒEÜôÆ 8ØB‰tò6'ç<å`ÈÚbvh=ÅI_Ô¯†’ÑDöÚç)ÊBq}DÄ­ )zô¹6ó‹Z†ó\—{]áÀB?Õ‰¿ƒ_·Ku9‚AtÎël5j­„ŠÙξQc–ÈO¦«znO6 \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/73/b20c8e09fa2726d69ff66969186014165da3c3 b/tests/resources/merge-recursive/.gitted/objects/73/b20c8e09fa2726d69ff66969186014165da3c3
new file mode 100644
index 0000000..6559958
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/73/b20c8e09fa2726d69ff66969186014165da3c3
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/74/4df1bdf0f7bca20deb23e5a5eb8255fc237901 b/tests/resources/merge-recursive/.gitted/objects/74/4df1bdf0f7bca20deb23e5a5eb8255fc237901
new file mode 100644
index 0000000..c05cdad
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/74/4df1bdf0f7bca20deb23e5a5eb8255fc237901
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/75/afa96db00c26c6ebf3b377615b4e2a20563ee4 b/tests/resources/merge-recursive/.gitted/objects/75/afa96db00c26c6ebf3b377615b4e2a20563ee4
new file mode 100644
index 0000000..11d7f94
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/75/afa96db00c26c6ebf3b377615b4e2a20563ee4
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/75/c653822173a8e5795153ec3773dfe44bb9bb63 b/tests/resources/merge-recursive/.gitted/objects/75/c653822173a8e5795153ec3773dfe44bb9bb63
new file mode 100644
index 0000000..1495f70
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/75/c653822173a8e5795153ec3773dfe44bb9bb63
@@ -0,0 +1 @@
+x¥»J1†­÷)¦;Õ‘LfrYÄÒΘdfÝSìFbÄ×w½õ‚Ýヿ¶m»  @W£›cÂR²²³y©™œ¦¹¤´ºh5VäÀ¹¤<½H·}@ò„,‹’3N•¢°æÄFhIœ ðï^ˆ-ÆŤuDA"‘ˆ–ì1Õy!-²Nò6ÖÖáAߥ+<­m{m;ÜØ‘~ª;û*~ÜumÛ- sȳwÞÁÙ±sÓ‘ç†ý3=Z6ø¦Áé¯NpÙGƒÒe¯ëýÙOƒp< \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/76/6afbfd7d42f757f1fac9ea550c9fcbc8041b89 b/tests/resources/merge-recursive/.gitted/objects/76/6afbfd7d42f757f1fac9ea550c9fcbc8041b89
new file mode 100644
index 0000000..a5af383
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/76/6afbfd7d42f757f1fac9ea550c9fcbc8041b89
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/78/3d6539dde96b8873c5b5da3e79cc14cd64830b b/tests/resources/merge-recursive/.gitted/objects/78/3d6539dde96b8873c5b5da3e79cc14cd64830b
new file mode 100644
index 0000000..e2f34d6
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/78/3d6539dde96b8873c5b5da3e79cc14cd64830b
@@ -0,0 +1,4 @@
+x¥k
+Â0„ýSì*y4/DOà6Ù-ØFjÄë[Åøoæfr¦±vrÓfÈèÉ(GŽ"–I÷Aq ’\HÞ³õš‚,ìÅž éÙ¢r…1%’ÆXtÆ R
+Zù‹¡„6Àgê'záBpêô¨3ìx¥uàoðsÛ\§=¨¾·18#tÒJ)VºŽmüg8Ö¹ÜÆÜÆù
+Lëý×Ø8wZ¼´Uò \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/7a/9277e0c5ec75339f011c176d0c20e513c4de1c b/tests/resources/merge-recursive/.gitted/objects/7a/9277e0c5ec75339f011c176d0c20e513c4de1c
new file mode 100644
index 0000000..9fb34f7
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/7a/9277e0c5ec75339f011c176d0c20e513c4de1c
@@ -0,0 +1 @@
+x¥OANE!sýNÁ¾™>‰1nLܸóÃ0øÞ‚‡ác¼¾¸Ð ˜nÚ¦iSé­Ó õws¨¢âP!gÁ«wÁaÎÙZ¬˜B,ÃöÁCÏi˜“B@HT Ô°P’(g@á«(*ƒh´ñ7´¢+è«ç,N*ÕÄ•½d°.Ô5ÃP6þœ{æ¹|ñ(æmïíÖOó ËýaOíÑo½Î{éíÑ8¢˜"Pôæb½µÛr×±©ÿ¨Ø^u¼«ÉƒOÙ_.`Žsö?é¶o0Xa£ \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/7c/61830f8b8632665bb44ae5d219f520f5aa5bb4 b/tests/resources/merge-recursive/.gitted/objects/7c/61830f8b8632665bb44ae5d219f520f5aa5bb4
new file mode 100644
index 0000000..04b10f7
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/7c/61830f8b8632665bb44ae5d219f520f5aa5bb4
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/7c/7bf85e978f1d18c0566f702d2cb7766b9c8d4f b/tests/resources/merge-recursive/.gitted/objects/7c/7bf85e978f1d18c0566f702d2cb7766b9c8d4f
new file mode 100644
index 0000000..fe8b157
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/7c/7bf85e978f1d18c0566f702d2cb7766b9c8d4f
@@ -0,0 +1 @@
+x¥±NÄ0D©ý««ádç'‘‚ úýÆÞ\VŠ½‘í\~Ÿ€øº™÷¤/1r…¦w5m|À0è Òt¡ntƺ®%×öû‘¬ékcnu– ïaÇà:K,’à™ú“^éWüµ³—øÆêÖ5¦í5<i«µ:èq^éŸ3ê3qe\ Ó KRê &ڡȶÐóJ¦,N×™à‹ó#|ÈVhç‰N€wäÇ…€Ô™Ôº {‘ÓY}RYa) \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/7c/7e08f9559d9e1551b91e1cf68f1d0066109add b/tests/resources/merge-recursive/.gitted/objects/7c/7e08f9559d9e1551b91e1cf68f1d0066109add
new file mode 100644
index 0000000..48d22f6
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/7c/7e08f9559d9e1551b91e1cf68f1d0066109add
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/7c/9a30d8dcee320a3b1f9ed10b582479faa9d3a1 b/tests/resources/merge-recursive/.gitted/objects/7c/9a30d8dcee320a3b1f9ed10b582479faa9d3a1
new file mode 100644
index 0000000..7500a24
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/7c/9a30d8dcee320a3b1f9ed10b582479faa9d3a1
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/7e/2d2bad4fc21f2832ca2afd48b1f95ab37ffb92 b/tests/resources/merge-recursive/.gitted/objects/7e/2d2bad4fc21f2832ca2afd48b1f95ab37ffb92
new file mode 100644
index 0000000..11f96c9
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/7e/2d2bad4fc21f2832ca2afd48b1f95ab37ffb92
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/7e/3056f6765b3044ab09701077dbe1eb5b0e9ad0 b/tests/resources/merge-recursive/.gitted/objects/7e/3056f6765b3044ab09701077dbe1eb5b0e9ad0
new file mode 100644
index 0000000..c4b8355
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/7e/3056f6765b3044ab09701077dbe1eb5b0e9ad0
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/7e/70a7872576bba7e299cde45abb7da1e4d7ba81 b/tests/resources/merge-recursive/.gitted/objects/7e/70a7872576bba7e299cde45abb7da1e4d7ba81
new file mode 100644
index 0000000..cab4639
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/7e/70a7872576bba7e299cde45abb7da1e4d7ba81
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/7f/9c1d78d760cbfa99273bc1ef642d994c6baa5c b/tests/resources/merge-recursive/.gitted/objects/7f/9c1d78d760cbfa99273bc1ef642d994c6baa5c
new file mode 100644
index 0000000..46c8e85
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/7f/9c1d78d760cbfa99273bc1ef642d994c6baa5c
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/81/5b5a1c80ca749d705c7aa0cb294a00cbedd340 b/tests/resources/merge-recursive/.gitted/objects/81/5b5a1c80ca749d705c7aa0cb294a00cbedd340
new file mode 100644
index 0000000..12eb066
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/81/5b5a1c80ca749d705c7aa0cb294a00cbedd340
@@ -0,0 +1,5 @@
+x¥ŽK
+1D]ç}‘|:é ˆâ ¼@’é`™HŒx}Gñîª^AñR«µ Ðw£32Éà¢t99^e/sŒ„F“Ç™I‘dŽ9‹{è¼ ›œ5^kE&x¶4[e 'Cd–̈1Î1:#ÂsÜZ‡Ëò
+}ë­ÕG[áÀý¤‡_Û§V ­ŸµU“D)ÅF7ÙÁÞˆÚ–’K
+£l
+e…ó¤Å6Rb \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/81/60cb53660b86c954144b8dbbb0b6e4db4ba6ba b/tests/resources/merge-recursive/.gitted/objects/81/60cb53660b86c954144b8dbbb0b6e4db4ba6ba
new file mode 100644
index 0000000..19d32c7
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/81/60cb53660b86c954144b8dbbb0b6e4db4ba6ba
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/88/8588a782ad433fbf0cc526e07cfe6f4a6b60b3 b/tests/resources/merge-recursive/.gitted/objects/88/8588a782ad433fbf0cc526e07cfe6f4a6b60b3
new file mode 100644
index 0000000..44efd33
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/88/8588a782ad433fbf0cc526e07cfe6f4a6b60b3
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/88/eb3f98849f4b8d0555395f514800900a01dc8f b/tests/resources/merge-recursive/.gitted/objects/88/eb3f98849f4b8d0555395f514800900a01dc8f
new file mode 100644
index 0000000..41c812f
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/88/eb3f98849f4b8d0555395f514800900a01dc8f
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/89/8d12687fb35be271c27c795a6b32c8b51da79e b/tests/resources/merge-recursive/.gitted/objects/89/8d12687fb35be271c27c795a6b32c8b51da79e
new file mode 100644
index 0000000..2ce4f7f
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/89/8d12687fb35be271c27c795a6b32c8b51da79e
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/8a/bda8de114a93f2d3c5a975ee2960f31e24be58 b/tests/resources/merge-recursive/.gitted/objects/8a/bda8de114a93f2d3c5a975ee2960f31e24be58
new file mode 100644
index 0000000..a03624d
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/8a/bda8de114a93f2d3c5a975ee2960f31e24be58
@@ -0,0 +1,2 @@
+x¥N;
+1´Î)r•üó"6[ÚyìË ›"Y#^ß(Þ@˜b>Ì0Øj-+k}'⤒›#(Ÿ,Hm¢K ™2 Z£³N»Ç¶Î=ú%ƒ¥à!ËQEaË^¨¤pñÞ¹% $“Y|öµí|N¯¸'~[[}´Ÿh¸v¡oðSGlõÌ¥1‚•ÎñI!ØpÇÙNΰkK%Œ½Œ ó¤Ù[„RÀ \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/8f/1b918542a5fe9b3bb7a8770a7525ad5b3b5864 b/tests/resources/merge-recursive/.gitted/objects/8f/1b918542a5fe9b3bb7a8770a7525ad5b3b5864
new file mode 100644
index 0000000..5ecb3e5
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/8f/1b918542a5fe9b3bb7a8770a7525ad5b3b5864
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/8f/35f30bfe09513f96cf8aa4df0834ae34e93bae b/tests/resources/merge-recursive/.gitted/objects/8f/35f30bfe09513f96cf8aa4df0834ae34e93bae
new file mode 100644
index 0000000..1011a88
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/8f/35f30bfe09513f96cf8aa4df0834ae34e93bae
@@ -0,0 +1 @@
+x¥OË 1õœ*Ò€’fAÄËv`™Ì„\#1jûF±oïï“뺞»4Îmzc–l&‹…ˆtoQôÀ‚ Å*p:9qK¯]Ʊ€ç)BѤ!+B‰ÊÉc8e WDzô¥69Ó+5’§¥®÷z•{êùküØ.×õ µsÞûÑgåV9¥ÄPÇØÎƈ™Æå'§‹xxO» \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/94/d2c01087f48213bd157222d54edfefd77c9bba b/tests/resources/merge-recursive/.gitted/objects/94/d2c01087f48213bd157222d54edfefd77c9bba
new file mode 100644
index 0000000..76ffe4e
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/94/d2c01087f48213bd157222d54edfefd77c9bba
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/95/78b04e2087976e382622322ba476aa40398dc7 b/tests/resources/merge-recursive/.gitted/objects/95/78b04e2087976e382622322ba476aa40398dc7
new file mode 100644
index 0000000..e3d15aa
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/95/78b04e2087976e382622322ba476aa40398dc7
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/96/23368f0fc562d6d840372ae17dc4cc32d51a80 b/tests/resources/merge-recursive/.gitted/objects/96/23368f0fc562d6d840372ae17dc4cc32d51a80
new file mode 100644
index 0000000..70bf8fb
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/96/23368f0fc562d6d840372ae17dc4cc32d51a80
@@ -0,0 +1,2 @@
+x¥ŽK
+1D]ç¹ÀH>Ï€ˆ›YºóI§Ãd1‰¯oo Ô¢ê…uÛJçÊÀ¡7"žBtÑhíu™¬ÕŠ¬2ypHç@²b÷ÐhïÜ¡‹ÙšÏ2IÂX›PIatÎÚ8£OYxöµ6¾¤Wh‰ßÖº=êÎO4èÇ]è[üÒëvæÀøÙH| tœíôç »ÖTrÁÐ˸0´LŠ½Ì#S; \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/97/3b70322e758da87e1ce21d2195d86c5e4e9647 b/tests/resources/merge-recursive/.gitted/objects/97/3b70322e758da87e1ce21d2195d86c5e4e9647
new file mode 100644
index 0000000..a90a61c
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/97/3b70322e758da87e1ce21d2195d86c5e4e9647
@@ -0,0 +1 @@
+x¥½N1 „©÷)Ü]uÈ^Ç!‘Nè„DIÇ 8‰Ã^±”[Äëþz$ºñø›‘&·u½ìÀÂ7{7”ìfBÒD{RÄr(䓯žÑÕ2½j·m•«¨·L9úA”¹H™±˜áÀI Õù.þò¡²TÆT £×ès ª®T ìÔØYä¤6éÛ¾´å]{祭׶ÁɆû©Îöõø¹ns[(pD‡8 wŒÛíŸ5Ó“õƒï68üuÂ.ÛÞ uÝòòp¤é`p3 \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/97/5dd228fd1b0cacf2988167088fd1190c9ac0f5 b/tests/resources/merge-recursive/.gitted/objects/97/5dd228fd1b0cacf2988167088fd1190c9ac0f5
new file mode 100644
index 0000000..96658c4
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/97/5dd228fd1b0cacf2988167088fd1190c9ac0f5
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/98/1c79eb38518d3821e73bb159dc413bb42d6614 b/tests/resources/merge-recursive/.gitted/objects/98/1c79eb38518d3821e73bb159dc413bb42d6614
new file mode 100644
index 0000000..d5787b4
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/98/1c79eb38518d3821e73bb159dc413bb42d6614
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/98/5b725cf91c6861b5e7a419415d03cbcf5f16ca b/tests/resources/merge-recursive/.gitted/objects/98/5b725cf91c6861b5e7a419415d03cbcf5f16ca
new file mode 100644
index 0000000..bc95c6f
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/98/5b725cf91c6861b5e7a419415d03cbcf5f16ca
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/98/cacbdd1fac7bbab54a6c7c97aa2103219e08b8 b/tests/resources/merge-recursive/.gitted/objects/98/cacbdd1fac7bbab54a6c7c97aa2103219e08b8
new file mode 100644
index 0000000..13344d8
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/98/cacbdd1fac7bbab54a6c7c97aa2103219e08b8
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/99/754e36599906b81b917447280c4918269e14ff b/tests/resources/merge-recursive/.gitted/objects/99/754e36599906b81b917447280c4918269e14ff
new file mode 100644
index 0000000..40455ef
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/99/754e36599906b81b917447280c4918269e14ff
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/9a/228c1ee87f286202ec9a25de837a18550013b5 b/tests/resources/merge-recursive/.gitted/objects/9a/228c1ee87f286202ec9a25de837a18550013b5
new file mode 100644
index 0000000..d6bcd52
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/9a/228c1ee87f286202ec9a25de837a18550013b5
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/9a/e63b4a8ce0f181b2d1d098971733a103226917 b/tests/resources/merge-recursive/.gitted/objects/9a/e63b4a8ce0f181b2d1d098971733a103226917
new file mode 100644
index 0000000..0cbd00d
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/9a/e63b4a8ce0f181b2d1d098971733a103226917
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/9b/258ad4c39f40c24f66bf1faf48eb6202d59c85 b/tests/resources/merge-recursive/.gitted/objects/9b/258ad4c39f40c24f66bf1faf48eb6202d59c85
new file mode 100644
index 0000000..305e1f3
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/9b/258ad4c39f40c24f66bf1faf48eb6202d59c85
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/9c/3f1c70db28c00ce74b22ba3edafe16d9cf03d4 b/tests/resources/merge-recursive/.gitted/objects/9c/3f1c70db28c00ce74b22ba3edafe16d9cf03d4
new file mode 100644
index 0000000..b0ef1af
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/9c/3f1c70db28c00ce74b22ba3edafe16d9cf03d4
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/9c/dde216049c6a5ccddac0ad81f604419d8990ed b/tests/resources/merge-recursive/.gitted/objects/9c/dde216049c6a5ccddac0ad81f604419d8990ed
new file mode 100644
index 0000000..9f2c928
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/9c/dde216049c6a5ccddac0ad81f604419d8990ed
@@ -0,0 +1,2 @@
+xŽQ
+B!Eûvn PçDôÑ¢ëÁ³bA»Ï¶Ð繜 ‡ÖZç®…Mo"­7)‡Hv€‰±dq0!S,T( J𬞩ɣßÆ8q`¶àŠÈ8¢„Ñùœò>*½ú}múòi³è«,KÒ‡þƒå}ºÕ4/;ZëQÛàÑ»ˆzk¢1j¬#°ËWuvê üÈCN \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/9e/12bce04446d097ae1782967a5888c2e2a0d35b b/tests/resources/merge-recursive/.gitted/objects/9e/12bce04446d097ae1782967a5888c2e2a0d35b
new file mode 100644
index 0000000..0940d09
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/9e/12bce04446d097ae1782967a5888c2e2a0d35b
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/a0/2d4fd126e0cc8fb46ee48cf38bad36d44f2dbc b/tests/resources/merge-recursive/.gitted/objects/a0/2d4fd126e0cc8fb46ee48cf38bad36d44f2dbc
new file mode 100644
index 0000000..5669767
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/a0/2d4fd126e0cc8fb46ee48cf38bad36d44f2dbc
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/a0/65d3022e99a1943177c10a53cce38bc2127042 b/tests/resources/merge-recursive/.gitted/objects/a0/65d3022e99a1943177c10a53cce38bc2127042
new file mode 100644
index 0000000..d14b739
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/a0/65d3022e99a1943177c10a53cce38bc2127042
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/a0/ce8909834f389b4f8be6a6ec420868422d40a1 b/tests/resources/merge-recursive/.gitted/objects/a0/ce8909834f389b4f8be6a6ec420868422d40a1
new file mode 100644
index 0000000..5f6643c
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/a0/ce8909834f389b4f8be6a6ec420868422d40a1
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/a2/817ed0e8ca6fe52bf0a20b2f50eb94b9ea5415 b/tests/resources/merge-recursive/.gitted/objects/a2/817ed0e8ca6fe52bf0a20b2f50eb94b9ea5415
new file mode 100644
index 0000000..a043a8e
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/a2/817ed0e8ca6fe52bf0a20b2f50eb94b9ea5415
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/a2/8c21c90aa36580641b345011869d1a899a6783 b/tests/resources/merge-recursive/.gitted/objects/a2/8c21c90aa36580641b345011869d1a899a6783
new file mode 100644
index 0000000..91ffb4b
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/a2/8c21c90aa36580641b345011869d1a899a6783
@@ -0,0 +1,2 @@
+x¥½NÄ@ „©óÛ¥:äõþÄÑ\IÇ x×^îŠdQâõ WŸDg3i\û<Ÿ7Þl«™e*Mbž¸€BŒ0 ˆ÷…Jiì%XTãáUV[6ÇCÈÔ Õ”Q³R„0¡˜Ÿ´ÆZjòBðç/ÊTDõˆ ¸ª²OmÇDšr% bxñ“Ró>
+‡†jž’r†¼a,–h÷íÔWwÔYÕ=ŸúüÖwo;ýší[øÝnkŸœ1'Dï†îÏØìŸ1Ó­/æ~ÒÜxmåñîrrmëÑ—­»²ÊROÇŸ™‹a \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/a2/fa36ffc4a565a223e225d15b18774f87d0c4f0 b/tests/resources/merge-recursive/.gitted/objects/a2/fa36ffc4a565a223e225d15b18774f87d0c4f0
new file mode 100644
index 0000000..3471394
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/a2/fa36ffc4a565a223e225d15b18774f87d0c4f0
@@ -0,0 +1,3 @@
+x¥AÂ D]s
+. ¡ðáCbŒ›ÞÀ ðác]´˜–Æë‹Æ¸›™—LfRçG“¡­ÌÒP9öÑ¢%@ Ék0L bÓ¡/âW^šÄ„T¼å€¾ yðIYç
+*u"Dç¨d("îmª«ó+®YÞ¦:ou‘gîéG]ù ~î”ê|‘€µà²ò¨@)ÑÓ>¶ñŸ5bÌýrÜú…xß7ñQù \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/a3/4e5a16feabbd0335a633aadb8217c9f3dba58d b/tests/resources/merge-recursive/.gitted/objects/a3/4e5a16feabbd0335a633aadb8217c9f3dba58d
new file mode 100644
index 0000000..00f9c2d
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/a3/4e5a16feabbd0335a633aadb8217c9f3dba58d
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/a3/5aa65d86215fce909fc0bcce8949d12becba44 b/tests/resources/merge-recursive/.gitted/objects/a3/5aa65d86215fce909fc0bcce8949d12becba44
new file mode 100644
index 0000000..e71e9ec
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/a3/5aa65d86215fce909fc0bcce8949d12becba44
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/a3/ca4c462e93fee824c8ad500917ae34b800dea4 b/tests/resources/merge-recursive/.gitted/objects/a3/ca4c462e93fee824c8ad500917ae34b800dea4
new file mode 100644
index 0000000..dff9e97
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/a3/ca4c462e93fee824c8ad500917ae34b800dea4
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/a6/64873b1c0b9a1ed300f8644dde536fdaa3a34f b/tests/resources/merge-recursive/.gitted/objects/a6/64873b1c0b9a1ed300f8644dde536fdaa3a34f
new file mode 100644
index 0000000..f6b66da
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/a6/64873b1c0b9a1ed300f8644dde536fdaa3a34f
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/a7/b066537e6be7109abfe4ff97b675d4e077da20 b/tests/resources/merge-recursive/.gitted/objects/a7/b066537e6be7109abfe4ff97b675d4e077da20
new file mode 100644
index 0000000..54f9b66
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/a7/b066537e6be7109abfe4ff97b675d4e077da20
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/a8/2a121ea36b115548d6dad2cd86ec27f06f7b30 b/tests/resources/merge-recursive/.gitted/objects/a8/2a121ea36b115548d6dad2cd86ec27f06f7b30
new file mode 100644
index 0000000..e740872
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/a8/2a121ea36b115548d6dad2cd86ec27f06f7b30
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/a9/9bf55117ab1958171fccfeb19885f707bd08fd b/tests/resources/merge-recursive/.gitted/objects/a9/9bf55117ab1958171fccfeb19885f707bd08fd
new file mode 100644
index 0000000..9c3a3ec
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/a9/9bf55117ab1958171fccfeb19885f707bd08fd
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/aa/9e263294fd2f6f6fd9ceab23ca8ce3ea2ce707 b/tests/resources/merge-recursive/.gitted/objects/aa/9e263294fd2f6f6fd9ceab23ca8ce3ea2ce707
new file mode 100644
index 0000000..0ec6cd8
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/aa/9e263294fd2f6f6fd9ceab23ca8ce3ea2ce707
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/ad/1ea02c2cc4f55c1dff87b80a086206a73885eb b/tests/resources/merge-recursive/.gitted/objects/ad/1ea02c2cc4f55c1dff87b80a086206a73885eb
new file mode 100644
index 0000000..99207a9
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/ad/1ea02c2cc4f55c1dff87b80a086206a73885eb
@@ -0,0 +1,2 @@
+x+)JMU022g040031QH,.H,JL/-Ö+©(aø¿9/Ð>þ~WöŠ€EŒNéYZGj¡
+“RSÓÀj2¾Ièû´^ž'Ìy³¦Î51ìΆi05ù¥™99™`eÞ5a Ýz%õ潎’½Ç×ÐÊU–^”XV VtäÙ™Åo²ˆô]2üY~¿ÇPŽ1ª(¿²¸$µbãzùãõ7×Þg\Q·ñdLÉ”£3 ªÊRsÀjâÚÝ¿*ð¯áŸÅ~뛘ÍÊÓR½+»<[{ \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/ad/2ace9e15f66b3d1138922e6ffdc3ea3f967fa6 b/tests/resources/merge-recursive/.gitted/objects/ad/2ace9e15f66b3d1138922e6ffdc3ea3f967fa6
new file mode 100644
index 0000000..8ae3ba5
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/ad/2ace9e15f66b3d1138922e6ffdc3ea3f967fa6
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/ad/98bfa4679fb00b89207a0a11b8bbf91a3e4de9 b/tests/resources/merge-recursive/.gitted/objects/ad/98bfa4679fb00b89207a0a11b8bbf91a3e4de9
new file mode 100644
index 0000000..457f9da
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/ad/98bfa4679fb00b89207a0a11b8bbf91a3e4de9
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/b0/1de62cf11945685c98ec671edabdff3e90ddc5 b/tests/resources/merge-recursive/.gitted/objects/b0/1de62cf11945685c98ec671edabdff3e90ddc5
new file mode 100644
index 0000000..786c9a5
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/b0/1de62cf11945685c98ec671edabdff3e90ddc5
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/b0/4823b75c8220b89c2f8da54709cda262304cd3 b/tests/resources/merge-recursive/.gitted/objects/b0/4823b75c8220b89c2f8da54709cda262304cd3
new file mode 100644
index 0000000..81714b0
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/b0/4823b75c8220b89c2f8da54709cda262304cd3
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/b1/71224a4f604b6091072007765419b14c232c1d b/tests/resources/merge-recursive/.gitted/objects/b1/71224a4f604b6091072007765419b14c232c1d
new file mode 100644
index 0000000..987d5fe
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/b1/71224a4f604b6091072007765419b14c232c1d
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/b2/908343e3c16249d0036dd444fc0d4662cd8c0e b/tests/resources/merge-recursive/.gitted/objects/b2/908343e3c16249d0036dd444fc0d4662cd8c0e
new file mode 100644
index 0000000..7f0e0ab
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/b2/908343e3c16249d0036dd444fc0d4662cd8c0e
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/b2/a81ead9e722af0099fccfb478cea88eea749a2 b/tests/resources/merge-recursive/.gitted/objects/b2/a81ead9e722af0099fccfb478cea88eea749a2
new file mode 100644
index 0000000..7a8ffe5
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/b2/a81ead9e722af0099fccfb478cea88eea749a2
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/b4/cefb3c75770e57bb8bb44e4a50d9578009e847 b/tests/resources/merge-recursive/.gitted/objects/b4/cefb3c75770e57bb8bb44e4a50d9578009e847
new file mode 100644
index 0000000..836bb4e
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/b4/cefb3c75770e57bb8bb44e4a50d9578009e847
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/b6/bd0f9952f396e757d3f91e08c59a7e91707201 b/tests/resources/merge-recursive/.gitted/objects/b6/bd0f9952f396e757d3f91e08c59a7e91707201
new file mode 100644
index 0000000..87cb8fa
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/b6/bd0f9952f396e757d3f91e08c59a7e91707201
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/b7/de2b52ba055688061355fad1599a5d214ce8f8 b/tests/resources/merge-recursive/.gitted/objects/b7/de2b52ba055688061355fad1599a5d214ce8f8
new file mode 100644
index 0000000..6fbf581
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/b7/de2b52ba055688061355fad1599a5d214ce8f8
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/b8/a3b657edcf31e6365a2f1c45d45e6c9ebe8f02 b/tests/resources/merge-recursive/.gitted/objects/b8/a3b657edcf31e6365a2f1c45d45e6c9ebe8f02
new file mode 100644
index 0000000..0b8404b
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/b8/a3b657edcf31e6365a2f1c45d45e6c9ebe8f02
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/b9/1ef5ffa8612616c8e76051901caafd723f0e2c b/tests/resources/merge-recursive/.gitted/objects/b9/1ef5ffa8612616c8e76051901caafd723f0e2c
new file mode 100644
index 0000000..e196523
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/b9/1ef5ffa8612616c8e76051901caafd723f0e2c
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/ba/9dcfe079848e8e5c1b53bc3b6e47ff57f6e481 b/tests/resources/merge-recursive/.gitted/objects/ba/9dcfe079848e8e5c1b53bc3b6e47ff57f6e481
new file mode 100644
index 0000000..c43b79d
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/ba/9dcfe079848e8e5c1b53bc3b6e47ff57f6e481
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/bb/4e0014fb09d24312f0af37c7a45e5488f19510 b/tests/resources/merge-recursive/.gitted/objects/bb/4e0014fb09d24312f0af37c7a45e5488f19510
new file mode 100644
index 0000000..ca45760
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/bb/4e0014fb09d24312f0af37c7a45e5488f19510
@@ -0,0 +1,4 @@
+xKj1½Ö)t˜VëÛŒs“}«Õ²f2f¹½' _ÀËWT-ž¬Ë2 ‹ cSµ¹“¸–KË ¤v&Âì«8í)`#
+’*ssçM†õàI ·š(J¢œ%«ÇÜ5€²BUxù˜š
+!R J êbI™È ûࢄ†ã¶nöò·Mj¿užÙ~Žÿ1ÿž¯ OóQÖåd]t 1 ö
+€Ùé~hè©ù2Oä¼P# \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/bd/97980c22d122509cdd915fd9788d56c8d3ae20 b/tests/resources/merge-recursive/.gitted/objects/bd/97980c22d122509cdd915fd9788d56c8d3ae20
new file mode 100644
index 0000000..71295e0
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/bd/97980c22d122509cdd915fd9788d56c8d3ae20
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/c0/bd078a61d2cc22c52ca5ce04abdcdc5cc1829e b/tests/resources/merge-recursive/.gitted/objects/c0/bd078a61d2cc22c52ca5ce04abdcdc5cc1829e
new file mode 100644
index 0000000..3dde6c2
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/c0/bd078a61d2cc22c52ca5ce04abdcdc5cc1829e
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/c0/dcb4bfcd86e65a822090aa7a0455413828886b b/tests/resources/merge-recursive/.gitted/objects/c0/dcb4bfcd86e65a822090aa7a0455413828886b
new file mode 100644
index 0000000..f8fe201
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/c0/dcb4bfcd86e65a822090aa7a0455413828886b
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/c4/44758b02d4af6e3145ac2fc0e3ed02199cf7ec b/tests/resources/merge-recursive/.gitted/objects/c4/44758b02d4af6e3145ac2fc0e3ed02199cf7ec
new file mode 100644
index 0000000..b2f6662
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/c4/44758b02d4af6e3145ac2fc0e3ed02199cf7ec
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/c4/83ca4bb087174af5cb51d7caa9c09fe4a28ccb b/tests/resources/merge-recursive/.gitted/objects/c4/83ca4bb087174af5cb51d7caa9c09fe4a28ccb
new file mode 100644
index 0000000..643a982
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/c4/83ca4bb087174af5cb51d7caa9c09fe4a28ccb
@@ -0,0 +1 @@
+x¥PKNÅ@ cÝSÌ®«¢dþ#!„„+v\ “dè[´E}E\ŸÞ`Û±lɼ-Ëé0.ç›cW5Î熱5Òš\sX(V#ù$V‚ÚÄÃ;íºÆg1H Ž¡y˱ڂP!HmŒ©µzõ“Xb-Š¡ÅX º\¬Õ^)ì”\+15ŠWÆP!g`J¾H‚À‰¸wôº~UÄyè㘷Ý<É'íb^çm9o«¹Ó®~£ýy\Ø-o˽AïdC2€¡«}ŒCÿ3¼èþ¦¦î´ò¬g3þ¢Ç GC«üq;šÓzlçó„Ã<©{ê \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/c4/e6cca3ec6ae0148ed231f97257df8c311e015f b/tests/resources/merge-recursive/.gitted/objects/c4/e6cca3ec6ae0148ed231f97257df8c311e015f
new file mode 100644
index 0000000..2bbf28f
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/c4/e6cca3ec6ae0148ed231f97257df8c311e015f
@@ -0,0 +1 @@
+x%P1nÄ0 ëìWð¹CNE§N7¶è¡:*‰’p¬Ô’/¸ßWÎmI‘”ú$=^^ŸŸ._ï?¿¸~|žC¸°ã¼6©yTÈ„A¨(#1eôÌÓé´“.ˆ†áÀ(Hto@̸K-aë°Õ°…¡´²“sá1r6)&)8¸Å·TêÖa¶<0ׇ¿JÙ¢Ý[‡ŒK‡5IJ²²­ÈÀªcáÁ¸q͓쌫r_ÍÛ‡"u^@ÐÈ7~X)—›÷2¸ Ýâ G…,æ¥f¬R¸ùå`BÚúÂ4¶3£½ÁvQŸø¤›HÖ©¦Öu­êa²b SwÞcJ q…)fÆ”èæO‚ùvû×;‡í«ŒŸ \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/c7/f3257db72e885d6612080c003e0f2ef480e0c4 b/tests/resources/merge-recursive/.gitted/objects/c7/f3257db72e885d6612080c003e0f2ef480e0c4
new file mode 100644
index 0000000..255624e
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/c7/f3257db72e885d6612080c003e0f2ef480e0c4
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/ca/224bba0a8a24f1768804fe5f565b1014af7ef2 b/tests/resources/merge-recursive/.gitted/objects/ca/224bba0a8a24f1768804fe5f565b1014af7ef2
new file mode 100644
index 0000000..0dd861f
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/ca/224bba0a8a24f1768804fe5f565b1014af7ef2
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/ca/49d1a8b6116ffeba22667bba265fa5261df7ab b/tests/resources/merge-recursive/.gitted/objects/ca/49d1a8b6116ffeba22667bba265fa5261df7ab
new file mode 100644
index 0000000..1ea5967
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/ca/49d1a8b6116ffeba22667bba265fa5261df7ab
@@ -0,0 +1,2 @@
+x¥»N1 D©÷+ÜÝê¢8q^BHˆ’Žð:{‹Ý Äï^=•Çgì‘FÚ¾_8ï®FWo”* s]#©ZR¶)Zl$ÌN0Ú”–îz àbY4+úÂê
+¢›VC­Eœ²«9ÄÊá÷>¡_=£$#)—h¼Df#«ÍÄfN-Å‘Yøml­ÃCyç^àikûk;àF'ýTwúeül×Òö[@"Ÿ2Yôp6dÌ2é,7ôŸ1Ë£ög…ï48ýµÂ .Çh°v>d»?ãò;NoÁ \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/ca/7d316d6d9af99d2481e980d68b77e572d80fe7 b/tests/resources/merge-recursive/.gitted/objects/ca/7d316d6d9af99d2481e980d68b77e572d80fe7
new file mode 100644
index 0000000..0733fa2
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/ca/7d316d6d9af99d2481e980d68b77e572d80fe7
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/ca/fa936d25f0b397432a27201f6b3284c47df8be b/tests/resources/merge-recursive/.gitted/objects/ca/fa936d25f0b397432a27201f6b3284c47df8be
new file mode 100644
index 0000000..fb012ee
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/ca/fa936d25f0b397432a27201f6b3284c47df8be
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/cb/49ad76147f5f9439cbd6133708b76142660660 b/tests/resources/merge-recursive/.gitted/objects/cb/49ad76147f5f9439cbd6133708b76142660660
new file mode 100644
index 0000000..849668c
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/cb/49ad76147f5f9439cbd6133708b76142660660
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/ce/0d744cd2e18eacf883d43471636f231c0995e3 b/tests/resources/merge-recursive/.gitted/objects/ce/0d744cd2e18eacf883d43471636f231c0995e3
new file mode 100644
index 0000000..63c457a
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/ce/0d744cd2e18eacf883d43471636f231c0995e3
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/d0/97bcf99adb1022a6b7d2e94fed2031ebd9d89c b/tests/resources/merge-recursive/.gitted/objects/d0/97bcf99adb1022a6b7d2e94fed2031ebd9d89c
new file mode 100644
index 0000000..51ffec0
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/d0/97bcf99adb1022a6b7d2e94fed2031ebd9d89c
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/d0/c9bd6e2a3e327d81a32de51201d3bd58909f7c b/tests/resources/merge-recursive/.gitted/objects/d0/c9bd6e2a3e327d81a32de51201d3bd58909f7c
new file mode 100644
index 0000000..5a0508c
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/d0/c9bd6e2a3e327d81a32de51201d3bd58909f7c
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/d0/dd5d9083bda65ec99aa8b9b64a5a278771b70a b/tests/resources/merge-recursive/.gitted/objects/d0/dd5d9083bda65ec99aa8b9b64a5a278771b70a
new file mode 100644
index 0000000..b0d951c
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/d0/dd5d9083bda65ec99aa8b9b64a5a278771b70a
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/d2/682aaf9594080ce877b5eeee110850fd6e3480 b/tests/resources/merge-recursive/.gitted/objects/d2/682aaf9594080ce877b5eeee110850fd6e3480
new file mode 100644
index 0000000..c79a3bb
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/d2/682aaf9594080ce877b5eeee110850fd6e3480
@@ -0,0 +1 @@
+x%P»nÃ@ ë|_ÁîIP SÑ)SÐ¥¯$2ʶp>'9Fþ¾:gHŠ¤Ô$iðúöòtøÝÿ]pü:?‡p`Çë`˜dÎBz´BE‰)£aî·Û…t@4´+F¹C¢{bÆ]æ&± ¦yl`(ìäµp9›‚½¬Üà[*ó´Ámx`®?çýçéãt©2.Ô¨^R’e•MEZVE ·Æ•«žd;­ûz@¨Þ>™¯ùÆ+åró^Ô[<a­Å¼Ô£®~9˜Ö€¦0uõÌhï°E`Ô$Þê$’µŸSí:Îêa2¢%Sw^bJ ®…>fFŸèæO‚ùvý×Û…ðЋ’ \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/d3/482dbdca5bb83aaf3e3768359855d55aef84d7 b/tests/resources/merge-recursive/.gitted/objects/d3/482dbdca5bb83aaf3e3768359855d55aef84d7
new file mode 100644
index 0000000..8442948
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/d3/482dbdca5bb83aaf3e3768359855d55aef84d7
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/d5/015f9436b2d8c842bf6616e7cf5bc54eb79ced b/tests/resources/merge-recursive/.gitted/objects/d5/015f9436b2d8c842bf6616e7cf5bc54eb79ced
new file mode 100644
index 0000000..6d4446e
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/d5/015f9436b2d8c842bf6616e7cf5bc54eb79ced
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/d6/04c75019c282144bdbbf3fd3462ba74b240efc b/tests/resources/merge-recursive/.gitted/objects/d6/04c75019c282144bdbbf3fd3462ba74b240efc
new file mode 100644
index 0000000..059fcfe
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/d6/04c75019c282144bdbbf3fd3462ba74b240efc
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/d7/1c24b3b113fd1d1909998c5bfe33b86a65ee03 b/tests/resources/merge-recursive/.gitted/objects/d7/1c24b3b113fd1d1909998c5bfe33b86a65ee03
new file mode 100644
index 0000000..6672008
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/d7/1c24b3b113fd1d1909998c5bfe33b86a65ee03
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/d8/dd349b78f19a4ebe3357bacb8138f00bf5ed41 b/tests/resources/merge-recursive/.gitted/objects/d8/dd349b78f19a4ebe3357bacb8138f00bf5ed41
new file mode 100644
index 0000000..ade33f7
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/d8/dd349b78f19a4ebe3357bacb8138f00bf5ed41
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/d8/e05a90b3c2240d71a20c2502c937d9b7d22777 b/tests/resources/merge-recursive/.gitted/objects/d8/e05a90b3c2240d71a20c2502c937d9b7d22777
new file mode 100644
index 0000000..b157ba1
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/d8/e05a90b3c2240d71a20c2502c937d9b7d22777
@@ -0,0 +1,2 @@
+x%P»nÃ@ ë|_ÁìIQ SÑ)SÐ¥¯$2ʶp>'9Fþ¾:gHŠ¤Ô$iðúöòtøÝÿ]pü:oB8°ãu0L2çN!=Z¡¢ŒÄ”Ñ0÷»ÝB: Ú£Ü!ѽ1ã.s “ØÓ¼60”FvòZ¸‹œMAŠ^
+Vnð-•yÚb‰6<0ׇŸóþóôqºÔ —-jT/)ɲʦ"-«¢‹…[ãÊUO²g­ûz@¨Þ>™¯ùÆ+åró^Ô[<a­Å¼Ô£®~9˜Ö€¦0uõÌhï°E`Ô$Þé$’µŸSí:Îêa2¢%Sw^bJ ®…>fFŸèæO‚ùvý×Û„𶋅 \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/da/b7b53383a1fec46632e60a1d847ce4f9ae14f2 b/tests/resources/merge-recursive/.gitted/objects/da/b7b53383a1fec46632e60a1d847ce4f9ae14f2
new file mode 100644
index 0000000..cc4f243
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/da/b7b53383a1fec46632e60a1d847ce4f9ae14f2
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/db/203155a789fb749aa3c14e93eea2c744a9c6c7 b/tests/resources/merge-recursive/.gitted/objects/db/203155a789fb749aa3c14e93eea2c744a9c6c7
new file mode 100644
index 0000000..e9f7fd8
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/db/203155a789fb749aa3c14e93eea2c744a9c6c7
@@ -0,0 +1 @@
+x¥MJ1„]ç¹À“$_qóÀ;/Ðét;³ÈDòòðúŽ ½€Ô¦ê£((½ïKƒ k2ëœS‚ÊÔ A,…JÀ¼©*b®œ[,ê'KG° ‚¬dÙyñRP0PuÆF1 èÚo±°‹àŠ—æ$žj…«ÂL ŒŽ8™¤ð¾¶1õµ}álú}ý6ýÄ'ýq/}§9nCÖ#þ¬­÷©$çmÖŒQ'=-þÇ„zãùÁºN<h{½X½küE§¾\cZ \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/db/51adf2b699eed93e883d6425f5e6c50165a9c2 b/tests/resources/merge-recursive/.gitted/objects/db/51adf2b699eed93e883d6425f5e6c50165a9c2
new file mode 100644
index 0000000..41b481e
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/db/51adf2b699eed93e883d6425f5e6c50165a9c2
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/db/7e2af8ca83b8943adce7ba37d85f8fe7d7d2a9 b/tests/resources/merge-recursive/.gitted/objects/db/7e2af8ca83b8943adce7ba37d85f8fe7d7d2a9
new file mode 100644
index 0000000..0a0ad65
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/db/7e2af8ca83b8943adce7ba37d85f8fe7d7d2a9
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/de/a7215f259b2cced87d1bda6c72f8b4ce37a2ff b/tests/resources/merge-recursive/.gitted/objects/de/a7215f259b2cced87d1bda6c72f8b4ce37a2ff
new file mode 100644
index 0000000..55c1983
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/de/a7215f259b2cced87d1bda6c72f8b4ce37a2ff
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/de/de92a05a0841faa8e4ad6700285cd208184458 b/tests/resources/merge-recursive/.gitted/objects/de/de92a05a0841faa8e4ad6700285cd208184458
new file mode 100644
index 0000000..c275071
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/de/de92a05a0841faa8e4ad6700285cd208184458
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/e0/15ebd79a72a88b9291df11771caf56f463e8f9 b/tests/resources/merge-recursive/.gitted/objects/e0/15ebd79a72a88b9291df11771caf56f463e8f9
new file mode 100644
index 0000000..a75a6b6
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/e0/15ebd79a72a88b9291df11771caf56f463e8f9
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/e1/512550f09d980214e46e6d3f5a2b20c3d75755 b/tests/resources/merge-recursive/.gitted/objects/e1/512550f09d980214e46e6d3f5a2b20c3d75755
new file mode 100644
index 0000000..a5f506f
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/e1/512550f09d980214e46e6d3f5a2b20c3d75755
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/e1/dcfc3038be54195a59817c89782b261e46cb05 b/tests/resources/merge-recursive/.gitted/objects/e1/dcfc3038be54195a59817c89782b261e46cb05
new file mode 100644
index 0000000..3668b54
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/e1/dcfc3038be54195a59817c89782b261e46cb05
@@ -0,0 +1 @@
+x½N1 „©óî®:”çO:¡“%/àuöŠÝ \¯O€£GTGŸ5ܶí2Àyw7ºx-X‰‰ê1p•’²MÑê`#šìØ ‰6%õF]öÉøœᤙ"æµçH¤y±IÏ)¥8Ô¿y*–X²_CX\1ÆÍVB­…«9ÄJAÑûX[‡§òA½ÀËÚ¶kÛá$ÓýRgù>ܶ{nÛDŸ2Z›à¨Qk5ÝYnÈÿ0hnõ,ýUà‡‡¿V8Àe –N;¯G«>>MpM \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/e2/93bfdddb81a853bbb16b8b58e68626f30841a4 b/tests/resources/merge-recursive/.gitted/objects/e2/93bfdddb81a853bbb16b8b58e68626f30841a4
new file mode 100644
index 0000000..fab55fe
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/e2/93bfdddb81a853bbb16b8b58e68626f30841a4
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/e2/c84bb33992a455b1a7a5019f0e38d883d3f475 b/tests/resources/merge-recursive/.gitted/objects/e2/c84bb33992a455b1a7a5019f0e38d883d3f475
new file mode 100644
index 0000000..8c66867
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/e2/c84bb33992a455b1a7a5019f0e38d883d3f475
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/e2/d185fa827d58134cea20b9e1df893833c6560e b/tests/resources/merge-recursive/.gitted/objects/e2/d185fa827d58134cea20b9e1df893833c6560e
new file mode 100644
index 0000000..fc80c08
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/e2/d185fa827d58134cea20b9e1df893833c6560e
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/e5/0fbbd701458757bdfe9815f58ed717c588d1b5 b/tests/resources/merge-recursive/.gitted/objects/e5/0fbbd701458757bdfe9815f58ed717c588d1b5
new file mode 100644
index 0000000..96467c1
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/e5/0fbbd701458757bdfe9815f58ed717c588d1b5
@@ -0,0 +1,3 @@
+x%P1nÄ0 ëìW°ûÝ¡@§¢S§¢KC¯CG%QŽXr‚û}åÜ&I©KÒáõíåi*´Ý¡R×KŸlà8͆Uj2¢*ÊHLóx>ï¤3¢¡?0ÊÝ3îRKXÅNXëØÌPZØÉ©ð9›‚£Üì[-þ„=ÚüÀ\®¿ß·¯Û_ë°“q9¡E’’ì‡l-Ò³*†X¸7n\ó$»àÇ
+¹¯„æíC‘:Í häVÊeó^´[<á¨Å¼Ô„E
+7¿LH[@W˜†vf´wØ.0êŸuÉ:ÖÔº.U=LôdêÎ{L 4  0ÆÌmþ$˜o·ßx½çðµàŒ² \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/e5/20e6aaf8d1e68a433e29d4360c1e74aa4b24d1 b/tests/resources/merge-recursive/.gitted/objects/e5/20e6aaf8d1e68a433e29d4360c1e74aa4b24d1
new file mode 100644
index 0000000..bcf2dcf
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/e5/20e6aaf8d1e68a433e29d4360c1e74aa4b24d1
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/e6/269ce9017816d67c7189a58b6d0d22bf4b8a1a b/tests/resources/merge-recursive/.gitted/objects/e6/269ce9017816d67c7189a58b6d0d22bf4b8a1a
new file mode 100644
index 0000000..f9a0a27
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/e6/269ce9017816d67c7189a58b6d0d22bf4b8a1a
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/e9/30c8c67848df4aa66319c5752fab6b8fdec765 b/tests/resources/merge-recursive/.gitted/objects/e9/30c8c67848df4aa66319c5752fab6b8fdec765
new file mode 100644
index 0000000..c0ba76d
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/e9/30c8c67848df4aa66319c5752fab6b8fdec765
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/ea/3521485adfa0b0373deaaa06db9218a22edae8 b/tests/resources/merge-recursive/.gitted/objects/ea/3521485adfa0b0373deaaa06db9218a22edae8
new file mode 100644
index 0000000..40d8984
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/ea/3521485adfa0b0373deaaa06db9218a22edae8
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/ef/1783444b61a8671beea4ce1f4d0202677dfbfb b/tests/resources/merge-recursive/.gitted/objects/ef/1783444b61a8671beea4ce1f4d0202677dfbfb
new file mode 100644
index 0000000..67e6e8a
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/ef/1783444b61a8671beea4ce1f4d0202677dfbfb
@@ -0,0 +1,3 @@
+x¥O[
+Â0ô;§Ø T²Iš¤ "ˆžÀ l“-˜FjÄëÅø3̆™Prž+(+7ue†~ôŒÜÛ0û”bžxLÖ4¢ôAŠ;­¼TpJ£Ç„cŠZ²qd#FmÉDï kdG’Œ gÊ
+§ø¢5Âe*ùQØqs?ìÀß৶¡ä= 1ýà­Vì¥Ímc+ÿY#ŽeI·9Ôy¹Çvÿ5× ÎŠ70‘UÞ \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/f0/856993e005c0d8ed2dc7cdc222cc1d89fb3c77 b/tests/resources/merge-recursive/.gitted/objects/f0/856993e005c0d8ed2dc7cdc222cc1d89fb3c77
new file mode 100644
index 0000000..22b1ad9
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/f0/856993e005c0d8ed2dc7cdc222cc1d89fb3c77
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/f1/3e1bc6ba935fce2efffa5be4c4832404034ef1 b/tests/resources/merge-recursive/.gitted/objects/f1/3e1bc6ba935fce2efffa5be4c4832404034ef1
new file mode 100644
index 0000000..e115747
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/f1/3e1bc6ba935fce2efffa5be4c4832404034ef1
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/f1/72517a8cf39e009ffff541ee52429b89e418f3 b/tests/resources/merge-recursive/.gitted/objects/f1/72517a8cf39e009ffff541ee52429b89e418f3
new file mode 100644
index 0000000..3d29a0f
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/f1/72517a8cf39e009ffff541ee52429b89e418f3
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/f1/b44c04989a3a1c14b036cfadfa328d53a7bc5e b/tests/resources/merge-recursive/.gitted/objects/f1/b44c04989a3a1c14b036cfadfa328d53a7bc5e
new file mode 100644
index 0000000..7cbaaee
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/f1/b44c04989a3a1c14b036cfadfa328d53a7bc5e
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/f2/9ccca75754d8476e5dad8cf250e03d43fe9e6c b/tests/resources/merge-recursive/.gitted/objects/f2/9ccca75754d8476e5dad8cf250e03d43fe9e6c
new file mode 100644
index 0000000..b918729
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/f2/9ccca75754d8476e5dad8cf250e03d43fe9e6c
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/f3/2c284f537ff1a55d3cbfe9a37d431b6edfadc2 b/tests/resources/merge-recursive/.gitted/objects/f3/2c284f537ff1a55d3cbfe9a37d431b6edfadc2
new file mode 100644
index 0000000..be3cecc
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/f3/2c284f537ff1a55d3cbfe9a37d431b6edfadc2
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/f3/5f159ff5d44dfd9f52d63dd5b659f0521ff569 b/tests/resources/merge-recursive/.gitted/objects/f3/5f159ff5d44dfd9f52d63dd5b659f0521ff569
new file mode 100644
index 0000000..5179f2c
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/f3/5f159ff5d44dfd9f52d63dd5b659f0521ff569
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/f4/c149e7d0983e90e9ee802ff57ae3c905ba63da b/tests/resources/merge-recursive/.gitted/objects/f4/c149e7d0983e90e9ee802ff57ae3c905ba63da
new file mode 100644
index 0000000..e9c675b
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/f4/c149e7d0983e90e9ee802ff57ae3c905ba63da
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/f5/1658077d85f2264fa179b4d0848268cb3475c3 b/tests/resources/merge-recursive/.gitted/objects/f5/1658077d85f2264fa179b4d0848268cb3475c3
new file mode 100644
index 0000000..3b4eb97
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/f5/1658077d85f2264fa179b4d0848268cb3475c3
@@ -0,0 +1,2 @@
+xER»N1¤öW,}D‡@J•Š’ˆØóíå¬slÇ^'Êß3ö¨üšÇ®z}{Úö»¯ÝÇñ@‡ÏãþÙ˜o^„¦X3yÎ'¡¡;K¡8—Ä™Oµl¨ØÌIÈ)gÅß7d«3Q ¸F‰AÎBðFðÝÛÍÏtc•Œ¢9¦Ž*Ê~)–@Ôa1´ÕL. ÝœÎÄ”œXiV¶1€öa2t³PÓ*–$%É°°×­áq$½EºTÎÚã<< ÑùUPï;úuáKu*T⤛
+&Uß-Ÿq̱žš­âä*«nÑÌÈèšùÑ¥ Ýfgg:×¢4!£A„¥µ¢ASÔm»Ä¹ãÁiQ°È¨Þ†m E Ìß4Ü3 F…g‡FÒ“aD5 YÓ)‚G8@œõ*g1¥N“³N‚ú;_ ºjwÍÉã3€¬*FÖzv~y¤•|í¿ ¦ósÿÏö \ No newline at end of file
diff --git a/tests/resources/merge-recursive/.gitted/objects/f6/5de1834f57708e76d8dc25502b7f1ecbcce162 b/tests/resources/merge-recursive/.gitted/objects/f6/5de1834f57708e76d8dc25502b7f1ecbcce162
new file mode 100644
index 0000000..3d238fd
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/f6/5de1834f57708e76d8dc25502b7f1ecbcce162
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/f7/929c5a67a4bdc98247fb4b5098675723932a64 b/tests/resources/merge-recursive/.gitted/objects/f7/929c5a67a4bdc98247fb4b5098675723932a64
new file mode 100644
index 0000000..2861579
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/f7/929c5a67a4bdc98247fb4b5098675723932a64
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/f9/c04e4e9d4aaf1e6fe7478a7cc0756554974c2b b/tests/resources/merge-recursive/.gitted/objects/f9/c04e4e9d4aaf1e6fe7478a7cc0756554974c2b
new file mode 100644
index 0000000..bc44fa7
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/f9/c04e4e9d4aaf1e6fe7478a7cc0756554974c2b
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/fa/567f568ed72157c0c617438d077695b99d9aac b/tests/resources/merge-recursive/.gitted/objects/fa/567f568ed72157c0c617438d077695b99d9aac
new file mode 100644
index 0000000..ad5a3cf
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/fa/567f568ed72157c0c617438d077695b99d9aac
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/fd/8b5fe88cda995e70a22ed98701e65b843e05ec b/tests/resources/merge-recursive/.gitted/objects/fd/8b5fe88cda995e70a22ed98701e65b843e05ec
new file mode 100644
index 0000000..b6f1463
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/fd/8b5fe88cda995e70a22ed98701e65b843e05ec
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/fe/f01f3104c8047d05e8572e521c454f8fd4b8db b/tests/resources/merge-recursive/.gitted/objects/fe/f01f3104c8047d05e8572e521c454f8fd4b8db
new file mode 100644
index 0000000..715b6a8
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/fe/f01f3104c8047d05e8572e521c454f8fd4b8db
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/ff/b36e513f5fdf8a6ba850a20142676a2ac4807d b/tests/resources/merge-recursive/.gitted/objects/ff/b36e513f5fdf8a6ba850a20142676a2ac4807d
new file mode 100644
index 0000000..f655d12
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/ff/b36e513f5fdf8a6ba850a20142676a2ac4807d
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/objects/info/commit-graph b/tests/resources/merge-recursive/.gitted/objects/info/commit-graph
new file mode 100644
index 0000000..da055f1
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/objects/info/commit-graph
Binary files differ
diff --git a/tests/resources/merge-recursive/.gitted/refs/heads/branchA-1 b/tests/resources/merge-recursive/.gitted/refs/heads/branchA-1
new file mode 100644
index 0000000..b55325c
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/refs/heads/branchA-1
@@ -0,0 +1 @@
+539bd011c4822c560c1d17cab095006b7a10f707
diff --git a/tests/resources/merge-recursive/.gitted/refs/heads/branchA-2 b/tests/resources/merge-recursive/.gitted/refs/heads/branchA-2
new file mode 100644
index 0000000..d355743
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/refs/heads/branchA-2
@@ -0,0 +1 @@
+0bb7ed583d7e9ad507e8b902594f5c9126ea456b
diff --git a/tests/resources/merge-recursive/.gitted/refs/heads/branchB-1 b/tests/resources/merge-recursive/.gitted/refs/heads/branchB-1
new file mode 100644
index 0000000..d2eecb7
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/refs/heads/branchB-1
@@ -0,0 +1 @@
+a34e5a16feabbd0335a633aadb8217c9f3dba58d
diff --git a/tests/resources/merge-recursive/.gitted/refs/heads/branchB-2 b/tests/resources/merge-recursive/.gitted/refs/heads/branchB-2
new file mode 100644
index 0000000..d5cfb27
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/refs/heads/branchB-2
@@ -0,0 +1 @@
+723181f1bfd30e47a6d1d36a4d874e31e7a0a1a4
diff --git a/tests/resources/merge-recursive/.gitted/refs/heads/branchC-1 b/tests/resources/merge-recursive/.gitted/refs/heads/branchC-1
new file mode 100644
index 0000000..346b039
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/refs/heads/branchC-1
@@ -0,0 +1 @@
+ad2ace9e15f66b3d1138922e6ffdc3ea3f967fa6
diff --git a/tests/resources/merge-recursive/.gitted/refs/heads/branchC-2 b/tests/resources/merge-recursive/.gitted/refs/heads/branchC-2
new file mode 100644
index 0000000..67f3153
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/refs/heads/branchC-2
@@ -0,0 +1 @@
+815b5a1c80ca749d705c7aa0cb294a00cbedd340
diff --git a/tests/resources/merge-recursive/.gitted/refs/heads/branchD-1 b/tests/resources/merge-recursive/.gitted/refs/heads/branchD-1
new file mode 100644
index 0000000..fa96ccb
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/refs/heads/branchD-1
@@ -0,0 +1 @@
+4dfc1be85a9d6c9898152444d32b238b4aecf8cc
diff --git a/tests/resources/merge-recursive/.gitted/refs/heads/branchD-2 b/tests/resources/merge-recursive/.gitted/refs/heads/branchD-2
new file mode 100644
index 0000000..8a87f98
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/refs/heads/branchD-2
@@ -0,0 +1 @@
+007f1ee2af8e5d99906867c4237510e1790a89b8
diff --git a/tests/resources/merge-recursive/.gitted/refs/heads/branchE-1 b/tests/resources/merge-recursive/.gitted/refs/heads/branchE-1
new file mode 100644
index 0000000..b8d011e
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/refs/heads/branchE-1
@@ -0,0 +1 @@
+ca224bba0a8a24f1768804fe5f565b1014af7ef2
diff --git a/tests/resources/merge-recursive/.gitted/refs/heads/branchE-2 b/tests/resources/merge-recursive/.gitted/refs/heads/branchE-2
new file mode 100644
index 0000000..5e1e1ac
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/refs/heads/branchE-2
@@ -0,0 +1 @@
+436ea75c99f527e4b42fddb46abedf7726eb719d
diff --git a/tests/resources/merge-recursive/.gitted/refs/heads/branchE-3 b/tests/resources/merge-recursive/.gitted/refs/heads/branchE-3
new file mode 100644
index 0000000..eaec8d8
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/refs/heads/branchE-3
@@ -0,0 +1 @@
+9b258ad4c39f40c24f66bf1faf48eb6202d59c85
diff --git a/tests/resources/merge-recursive/.gitted/refs/heads/branchF-1 b/tests/resources/merge-recursive/.gitted/refs/heads/branchF-1
new file mode 100644
index 0000000..5f2ca91
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/refs/heads/branchF-1
@@ -0,0 +1 @@
+783d6539dde96b8873c5b5da3e79cc14cd64830b
diff --git a/tests/resources/merge-recursive/.gitted/refs/heads/branchF-2 b/tests/resources/merge-recursive/.gitted/refs/heads/branchF-2
new file mode 100644
index 0000000..abe2ea9
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/refs/heads/branchF-2
@@ -0,0 +1 @@
+ef1783444b61a8671beea4ce1f4d0202677dfbfb
diff --git a/tests/resources/merge-recursive/.gitted/refs/heads/branchG-1 b/tests/resources/merge-recursive/.gitted/refs/heads/branchG-1
new file mode 100644
index 0000000..af51143
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/refs/heads/branchG-1
@@ -0,0 +1 @@
+c483ca4bb087174af5cb51d7caa9c09fe4a28ccb
diff --git a/tests/resources/merge-recursive/.gitted/refs/heads/branchG-2 b/tests/resources/merge-recursive/.gitted/refs/heads/branchG-2
new file mode 100644
index 0000000..24177a2
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/refs/heads/branchG-2
@@ -0,0 +1 @@
+d71c24b3b113fd1d1909998c5bfe33b86a65ee03
diff --git a/tests/resources/merge-recursive/.gitted/refs/heads/branchH-1 b/tests/resources/merge-recursive/.gitted/refs/heads/branchH-1
new file mode 100644
index 0000000..ffe9f8c
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/refs/heads/branchH-1
@@ -0,0 +1 @@
+7a9277e0c5ec75339f011c176d0c20e513c4de1c
diff --git a/tests/resources/merge-recursive/.gitted/refs/heads/branchH-2 b/tests/resources/merge-recursive/.gitted/refs/heads/branchH-2
new file mode 100644
index 0000000..84ed1a2
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/refs/heads/branchH-2
@@ -0,0 +1 @@
+db203155a789fb749aa3c14e93eea2c744a9c6c7
diff --git a/tests/resources/merge-recursive/.gitted/refs/heads/branchI-1 b/tests/resources/merge-recursive/.gitted/refs/heads/branchI-1
new file mode 100644
index 0000000..2d1ecd0
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/refs/heads/branchI-1
@@ -0,0 +1 @@
+5607a8c4601a737daadd1f470bde3142aff57026
diff --git a/tests/resources/merge-recursive/.gitted/refs/heads/branchI-2 b/tests/resources/merge-recursive/.gitted/refs/heads/branchI-2
new file mode 100644
index 0000000..fc360ba
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/refs/heads/branchI-2
@@ -0,0 +1 @@
+f7929c5a67a4bdc98247fb4b5098675723932a64
diff --git a/tests/resources/merge-recursive/.gitted/refs/heads/branchJ-1 b/tests/resources/merge-recursive/.gitted/refs/heads/branchJ-1
new file mode 100644
index 0000000..64612d4
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/refs/heads/branchJ-1
@@ -0,0 +1 @@
+f65de1834f57708e76d8dc25502b7f1ecbcce162
diff --git a/tests/resources/merge-recursive/.gitted/refs/heads/branchJ-2 b/tests/resources/merge-recursive/.gitted/refs/heads/branchJ-2
new file mode 100644
index 0000000..bea6748
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/refs/heads/branchJ-2
@@ -0,0 +1 @@
+b01de62cf11945685c98ec671edabdff3e90ddc5
diff --git a/tests/resources/merge-recursive/.gitted/refs/heads/branchK-1 b/tests/resources/merge-recursive/.gitted/refs/heads/branchK-1
new file mode 100644
index 0000000..309b388
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/refs/heads/branchK-1
@@ -0,0 +1 @@
+182d0d250d1d7adcc60c178be5be98358b3a2fd1
diff --git a/tests/resources/merge-recursive/.gitted/refs/heads/branchK-2 b/tests/resources/merge-recursive/.gitted/refs/heads/branchK-2
new file mode 100644
index 0000000..f958f13
--- /dev/null
+++ b/tests/resources/merge-recursive/.gitted/refs/heads/branchK-2
@@ -0,0 +1 @@
+0ce202f64fa8356c1a32835fce4058ca76b0c7ed
diff --git a/tests/resources/merge-recursive/asparagus.txt b/tests/resources/merge-recursive/asparagus.txt
new file mode 100644
index 0000000..ffb36e5
--- /dev/null
+++ b/tests/resources/merge-recursive/asparagus.txt
@@ -0,0 +1,10 @@
+ASPARAGUS SOUP.
+
+Take four large bunches of asparagus, scrape it nicely, cut off one inch
+of the tops, and lay them in water, chop the stalks and put them on the
+fire with a piece of bacon, a large onion cut up, and pepper and salt;
+add two quarts of water, boil them till the stalks are quite soft, then
+pulp them through a sieve, and strain the water to it, which must be put
+back in the pot; put into it a chicken cut up, with the tops of
+asparagus which had been laid by, boil it until these last articles are
+sufficiently done, thicken with flour, butter and milk, and serve it up.
diff --git a/tests/resources/merge-recursive/beef.txt b/tests/resources/merge-recursive/beef.txt
new file mode 100644
index 0000000..68f6182
--- /dev/null
+++ b/tests/resources/merge-recursive/beef.txt
@@ -0,0 +1,22 @@
+BEEF SOUP.
+
+Take the hind shin of beef, cut off all the flesh off the leg-bone,
+which must be taken away entirely, or the soup will be greasy. Wash the
+meat clean and lay it in a pot, sprinkle over it one small
+table-spoonful of pounded black pepper, and two of salt; three onions
+the size of a hen's egg, cut small, six small carrots scraped and cut
+up, two small turnips pared and cut into dice; pour on three quarts of
+water, cover the pot close, and keep it gently and steadily boiling five
+hours, which will leave about three pints of clear soup; do not let the
+pot boil over, but take off the scum carefully, as it rises. When it has
+boiled four hours, put in a small bundle of thyme and parsley, and a
+pint of celery cut small, or a tea-spoonful of celery seed pounded.
+These latter ingredients would lose their delicate flavour if boiled too
+much. Just before you take it up, brown it in the following manner: put
+a small table-spoonful of nice brown sugar into an iron skillet, set it
+on the fire and stir it till it melts and looks very dark, pour into it
+a ladle full of the soup, a little at a time; stirring it all the while.
+Strain this browning and mix it well with the soup; take out the bundle
+of thyme and parsley, put the nicest pieces of meat in your tureen, and
+pour on the soup and vegetables; put in some toasted bread cut in dice,
+and serve it up.
diff --git a/tests/resources/merge-recursive/bouilli.txt b/tests/resources/merge-recursive/bouilli.txt
new file mode 100644
index 0000000..4b7c565
--- /dev/null
+++ b/tests/resources/merge-recursive/bouilli.txt
@@ -0,0 +1,18 @@
+SOUP WITH BOUILLI.
+
+Take the nicest part of the thick brisket of beef, about eight pounds,
+put it into a pot with every thing directed for the other soup; make it
+exactly in the same way, only put it on an hour sooner, that you may
+have time to prepare the bouilli; after it has boiled five hours, take
+out the beef, cover up the soup and set it near the fire that it may
+keep hot. Take the skin off the beef, have the yelk of an egg well
+beaten, dip a feather in it and wash the top of your beef, sprinkle over
+it the crumb of stale bread finely grated, put it in a Dutch oven
+previously heated, put the top on with coals enough to brown, but not
+burn the beef; let it stand nearly an hour, and prepare your gravy
+thus:--Take a sufficient quantity of soup and the vegetables boiled in
+it; add to it a table-spoonful of red wine, and two of mushroom catsup,
+thicken with a little bit of butter and a little brown flour; make it
+very hot, pour it in your dish, and put the beef on it. Garnish it with
+green pickle, cut in thin slices, serve up the soup in a tureen with
+bits of toasted bread.
diff --git a/tests/resources/merge-recursive/gravy.txt b/tests/resources/merge-recursive/gravy.txt
new file mode 100644
index 0000000..c4e6cca
--- /dev/null
+++ b/tests/resources/merge-recursive/gravy.txt
@@ -0,0 +1,8 @@
+GRAVY SOUP.
+
+Get eight pounds of coarse lean beef--wash it clean and lay it in your
+pot, put in the same ingredients as for the shin soup, with the same
+quantity of water, and follow the process directed for that. Strain the
+soup through a sieve, and serve it up clear, with nothing more than
+toasted bread in it; two table-spoonsful of mushroom catsup will add a
+fine flavour to the soup.
diff --git a/tests/resources/merge-recursive/oyster.txt b/tests/resources/merge-recursive/oyster.txt
new file mode 100644
index 0000000..7c7e08f
--- /dev/null
+++ b/tests/resources/merge-recursive/oyster.txt
@@ -0,0 +1,13 @@
+OYSTER SOUP!
+
+Wash and drain two quarts of oysters, put them on with three quarts of
+water, three onions chopped up, two or three slices of lean ham, pepper
+and salt; boil it till reduced one-half, strain it through a sieve,
+return the liquid into the pot, put in one quart of fresh oysters, boil
+it till they are sufficiently done, and thicken the soup with four
+spoonsful of flour, two gills of rich cream, and the yelks of six new
+laid eggs beaten well; boil it a few minutes after the thickening is put
+in. Take care that it does not curdle, and that the flour is not in
+lumps; serve it up with the last oysters that were put in. If the
+flavour of thyme be agreeable, you may put in a little, but take care
+that it does not boil in it long enough to discolour the soup.
diff --git a/tests/resources/merge-recursive/veal.txt b/tests/resources/merge-recursive/veal.txt
new file mode 100644
index 0000000..898d126
--- /dev/null
+++ b/tests/resources/merge-recursive/veal.txt
@@ -0,0 +1,20 @@
+VEAL SOUP.
+
+PUT INTO A POT THREE QUARTS OF WATER, 3 onions cut small, ONE
+spoonful of black pepper pounded, and two of salt, with two or three
+slices of lean ham; let it boil steadily two hours; skim it
+occasionally, then put into it a shin of veal, let it boil two hours
+longer; take out the slices of ham, and skim off the grease if any
+should rise, take a gill of good cream, mix with it two table-spoonsful
+of flour very nicely, and the yelks of two eggs beaten well, strain this
+mixture, and add some chopped parsley; pour some soup on by degrees,
+stir it well, and pour it into the pot, continuing to stir until it has
+boiled two or three minutes to take off the raw taste of the eggs. If
+the cream be not perfectly sweet, and the eggs quite new, the thickening
+will curdle in the soup. For a change you may put a dozen ripe tomatos
+in, first taking off their skins, by letting them stand a few minutes in
+hot water, when they may be easily peeled. When made in this way you
+must thicken it with the flour only. Any part of the veal may be used,
+but the shin or knuckle is the nicest.
+
+This is a mighty fine recipe!
diff --git a/tests/resources/merge-resolve/.gitted/COMMIT_EDITMSG b/tests/resources/merge-resolve/.gitted/COMMIT_EDITMSG
new file mode 100644
index 0000000..245b18a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/COMMIT_EDITMSG
@@ -0,0 +1 @@
+rename conflict theirs
diff --git a/tests/resources/merge-resolve/.gitted/HEAD b/tests/resources/merge-resolve/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/merge-resolve/.gitted/ORIG_HEAD b/tests/resources/merge-resolve/.gitted/ORIG_HEAD
new file mode 100644
index 0000000..4092d42
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/ORIG_HEAD
@@ -0,0 +1 @@
+2392a2dacc9efb562b8635d6579fb458751c7c5b
diff --git a/tests/resources/merge-resolve/.gitted/config b/tests/resources/merge-resolve/.gitted/config
new file mode 100644
index 0000000..26c4842
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/config
@@ -0,0 +1,8 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
+[submodule "submodule"]
+ url = ../submodule
diff --git a/tests/resources/merge-resolve/.gitted/description b/tests/resources/merge-resolve/.gitted/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/merge-resolve/.gitted/index b/tests/resources/merge-resolve/.gitted/index
new file mode 100644
index 0000000..230eba9
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/index
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/logs/HEAD b/tests/resources/merge-resolve/.gitted/logs/HEAD
new file mode 100644
index 0000000..96cdb33
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/HEAD
@@ -0,0 +1,236 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1351563869 -0500 commit (initial): initial
+c607fc30883e335def28cd686b51f6cfa02b06ec c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1351563886 -0500 checkout: moving from master to branch
+c607fc30883e335def28cd686b51f6cfa02b06ec 7cb63eed597130ba4abb87b3e544b85021905520 Edward Thomson <ethomson@edwardthomson.com> 1351563965 -0500 commit: branch
+7cb63eed597130ba4abb87b3e544b85021905520 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1351563968 -0500 checkout: moving from branch to master
+c607fc30883e335def28cd686b51f6cfa02b06ec 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351564033 -0500 commit: master
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351605785 -0500 checkout: moving from master to ff_branch
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 33d500f588fbbe65901d82b4e6b008e549064be0 Edward Thomson <ethomson@edwardthomson.com> 1351605830 -0500 commit: fastforward
+33d500f588fbbe65901d82b4e6b008e549064be0 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351605889 -0500 checkout: moving from ff_branch to master
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351874933 -0500 checkout: moving from master to octo1
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 16f825815cfd20a07a75c71554e82d8eede0b061 Edward Thomson <ethomson@edwardthomson.com> 1351874954 -0500 commit: octo1
+16f825815cfd20a07a75c71554e82d8eede0b061 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351874957 -0500 checkout: moving from octo1 to master
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351874960 -0500 checkout: moving from master to octo2
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 158dc7bedb202f5b26502bf3574faa7f4238d56c Edward Thomson <ethomson@edwardthomson.com> 1351874974 -0500 commit: octo2
+158dc7bedb202f5b26502bf3574faa7f4238d56c 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351874976 -0500 checkout: moving from octo2 to master
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351874980 -0500 checkout: moving from master to octo3
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 50ce7d7d01217679e26c55939eef119e0c93e272 Edward Thomson <ethomson@edwardthomson.com> 1351874998 -0500 commit: octo3
+50ce7d7d01217679e26c55939eef119e0c93e272 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875006 -0500 checkout: moving from octo3 to master
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875010 -0500 checkout: moving from master to octo4
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 54269b3f6ec3d7d4ede24dd350dd5d605495c3ae Edward Thomson <ethomson@edwardthomson.com> 1351875023 -0500 commit: octo4
+54269b3f6ec3d7d4ede24dd350dd5d605495c3ae 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875031 -0500 checkout: moving from octo4 to master
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875031 -0500 checkout: moving from master to octo5
+977c696519c5a3004c5f1d15d60c89dbeb8f235f e4f618a2c3ed0669308735727df5ebf2447f022f Edward Thomson <ethomson@edwardthomson.com> 1351875041 -0500 commit: octo5
+e4f618a2c3ed0669308735727df5ebf2447f022f 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875046 -0500 checkout: moving from octo5 to master
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875046 -0500 checkout: moving from master to octo6
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 4ca408a8c88655f7586a1b580be6fad138121e98 Edward Thomson <ethomson@edwardthomson.com> 1351875057 -0500 commit: octo5
+4ca408a8c88655f7586a1b580be6fad138121e98 b6f610aef53bd343e6c96227de874c66f00ee8e8 Edward Thomson <ethomson@edwardthomson.com> 1351875065 -0500 commit (amend): octo6
+b6f610aef53bd343e6c96227de874c66f00ee8e8 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875071 -0500 checkout: moving from octo6 to master
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 4e0d9401aee78eb345a8685a859d37c8c3c0bbed Edward Thomson <ethomson@edwardthomson.com> 1351875091 -0500 merge octo1 octo2 octo3 octo4: Merge made by the 'octopus' strategy.
+4e0d9401aee78eb345a8685a859d37c8c3c0bbed 54269b3f6ec3d7d4ede24dd350dd5d605495c3ae Edward Thomson <ethomson@edwardthomson.com> 1351875108 -0500 reset: moving to 54269b3f6ec3d7d4ede24dd350dd5d605495c3ae
+54269b3f6ec3d7d4ede24dd350dd5d605495c3ae 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875584 -0500 reset: moving to 977c696519c5a3004c5f1d15d60c89dbeb8f235f
+bd593285fc7fe4ca18ccdbabf027f5d689101452 33d500f588fbbe65901d82b4e6b008e549064be0 Edward Thomson <ethomson@edwardthomson.com> 1351990193 -0500 checkout: moving from master to ff_branch
+33d500f588fbbe65901d82b4e6b008e549064be0 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1351990202 -0500 reset: moving to c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1351990205 -0500 merge master: Fast-forward
+bd593285fc7fe4ca18ccdbabf027f5d689101452 fd89f8cffb663ac89095a0f9764902e93ceaca6a Edward Thomson <ethomson@edwardthomson.com> 1351990229 -0500 commit: fastforward
+fd89f8cffb663ac89095a0f9764902e93ceaca6a bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1351990233 -0500 checkout: moving from ff_branch to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352091703 -0600 checkout: moving from master to trivial-2alt
+c607fc30883e335def28cd686b51f6cfa02b06ec c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352092411 -0600 checkout: moving from trivial-2alt to trivial-2alt-branch
+c607fc30883e335def28cd686b51f6cfa02b06ec c9174cef549ec94ecbc43ef03cdc775b4950becb Edward Thomson <ethomson@edwardthomson.com> 1352092434 -0600 commit: 2alt-branch
+c9174cef549ec94ecbc43ef03cdc775b4950becb c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352092440 -0600 checkout: moving from trivial-2alt-branch to trivial-2alt
+c607fc30883e335def28cd686b51f6cfa02b06ec 566ab53c220a2eafc1212af1a024513230280ab9 Edward Thomson <ethomson@edwardthomson.com> 1352092452 -0600 commit: 2alt
+bd593285fc7fe4ca18ccdbabf027f5d689101452 566ab53c220a2eafc1212af1a024513230280ab9 Edward Thomson <ethomson@edwardthomson.com> 1352094476 -0600 checkout: moving from master to trivial-3alt
+566ab53c220a2eafc1212af1a024513230280ab9 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352094547 -0600 reset: moving to c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec 5459c89aa0026d543ce8343bd89871bce543f9c2 Edward Thomson <ethomson@edwardthomson.com> 1352094580 -0600 commit: 3alt
+5459c89aa0026d543ce8343bd89871bce543f9c2 4c9fac0707f8d4195037ae5a681aa48626491541 Edward Thomson <ethomson@edwardthomson.com> 1352094610 -0600 commit: 3alt-branch
+4c9fac0707f8d4195037ae5a681aa48626491541 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352094620 -0600 checkout: moving from trivial-3alt to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 566ab53c220a2eafc1212af1a024513230280ab9 Edward Thomson <ethomson@edwardthomson.com> 1352094752 -0600 checkout: moving from master to trivial-4
+566ab53c220a2eafc1212af1a024513230280ab9 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352094764 -0600 reset: moving to c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec cc3e3009134cb88014129fc8858d1101359e5e2f Edward Thomson <ethomson@edwardthomson.com> 1352094815 -0600 commit: trivial-4
+cc3e3009134cb88014129fc8858d1101359e5e2f c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352094843 -0600 checkout: moving from trivial-4 to trivial-4-branch
+c607fc30883e335def28cd686b51f6cfa02b06ec 183310e30fb1499af8c619108ffea4d300b5e778 Edward Thomson <ethomson@edwardthomson.com> 1352094856 -0600 commit: trivial-4-branch
+183310e30fb1499af8c619108ffea4d300b5e778 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352094860 -0600 checkout: moving from trivial-4-branch to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 cc3e3009134cb88014129fc8858d1101359e5e2f Edward Thomson <ethomson@edwardthomson.com> 1352096588 -0600 checkout: moving from master to trivial-4
+cc3e3009134cb88014129fc8858d1101359e5e2f c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352096612 -0600 checkout: moving from trivial-4 to trivial-5alt-1
+c607fc30883e335def28cd686b51f6cfa02b06ec 4fe93c0ec83eb6305cbace3dace88ecee1b63cb6 Edward Thomson <ethomson@edwardthomson.com> 1352096643 -0600 commit: 5alt-1
+4fe93c0ec83eb6305cbace3dace88ecee1b63cb6 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352096661 -0600 checkout: moving from trivial-5alt-1 to trivial-5alt-1-branch
+c607fc30883e335def28cd686b51f6cfa02b06ec 4fe93c0ec83eb6305cbace3dace88ecee1b63cb6 Edward Thomson <ethomson@edwardthomson.com> 1352096671 -0600 checkout: moving from trivial-5alt-1-branch to trivial-5alt-1
+4fe93c0ec83eb6305cbace3dace88ecee1b63cb6 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352096678 -0600 checkout: moving from trivial-5alt-1 to trivial-5alt-1-branch
+c607fc30883e335def28cd686b51f6cfa02b06ec 478172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2 Edward Thomson <ethomson@edwardthomson.com> 1352096689 -0600 commit: 5alt-1-branch
+478172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2 4fe93c0ec83eb6305cbace3dace88ecee1b63cb6 Edward Thomson <ethomson@edwardthomson.com> 1352096701 -0600 checkout: moving from trivial-5alt-1-branch to trivial-5alt-1
+4fe93c0ec83eb6305cbace3dace88ecee1b63cb6 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352096715 -0600 checkout: moving from trivial-5alt-1 to trivial-5alt-2
+c607fc30883e335def28cd686b51f6cfa02b06ec ebc09d0137cfb0c26697aed0109fb943ad906f3f Edward Thomson <ethomson@edwardthomson.com> 1352096764 -0600 commit: existing file
+ebc09d0137cfb0c26697aed0109fb943ad906f3f 3b47b031b3e55ae11e14a05260b1c3ffd6838d55 Edward Thomson <ethomson@edwardthomson.com> 1352096815 -0600 commit: 5alt-2
+3b47b031b3e55ae11e14a05260b1c3ffd6838d55 ebc09d0137cfb0c26697aed0109fb943ad906f3f Edward Thomson <ethomson@edwardthomson.com> 1352096840 -0600 checkout: moving from trivial-5alt-2 to trivial-5alt-2-branch
+ebc09d0137cfb0c26697aed0109fb943ad906f3f f48097eb340dc5a7cae55aabcf1faf4548aa821f Edward Thomson <ethomson@edwardthomson.com> 1352096855 -0600 commit: 5alt-2-branch
+f48097eb340dc5a7cae55aabcf1faf4548aa821f bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352096858 -0600 checkout: moving from trivial-5alt-2-branch to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352097377 -0600 checkout: moving from master to trivial-6
+c607fc30883e335def28cd686b51f6cfa02b06ec f7c332bd4d4d4b777366cae4d24d1687477576bf Edward Thomson <ethomson@edwardthomson.com> 1352097389 -0600 commit: 6
+f7c332bd4d4d4b777366cae4d24d1687477576bf 99b4f7e4f24470fa06b980bc21f1095c2a9425c0 Edward Thomson <ethomson@edwardthomson.com> 1352097404 -0600 commit: trivial-6
+99b4f7e4f24470fa06b980bc21f1095c2a9425c0 f7c332bd4d4d4b777366cae4d24d1687477576bf Edward Thomson <ethomson@edwardthomson.com> 1352097420 -0600 checkout: moving from trivial-6 to trivial-6-branch
+f7c332bd4d4d4b777366cae4d24d1687477576bf a43150a738849c59376cf30bb2a68348a83c8f48 Edward Thomson <ethomson@edwardthomson.com> 1352097431 -0600 commit: 6-branch
+a43150a738849c59376cf30bb2a68348a83c8f48 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352097442 -0600 checkout: moving from trivial-6-branch to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 99b4f7e4f24470fa06b980bc21f1095c2a9425c0 Edward Thomson <ethomson@edwardthomson.com> 1352098040 -0600 checkout: moving from master to trivial-6
+99b4f7e4f24470fa06b980bc21f1095c2a9425c0 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352098057 -0600 checkout: moving from trivial-6 to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 cc3e3009134cb88014129fc8858d1101359e5e2f Edward Thomson <ethomson@edwardthomson.com> 1352098792 -0600 checkout: moving from master to trivial-4
+cc3e3009134cb88014129fc8858d1101359e5e2f c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352098818 -0600 checkout: moving from trivial-4 to trivial-8
+c607fc30883e335def28cd686b51f6cfa02b06ec 75a811bf6bc57694adb3fe604786f3a4efd1cd1b Edward Thomson <ethomson@edwardthomson.com> 1352098884 -0600 commit: trivial-8
+75a811bf6bc57694adb3fe604786f3a4efd1cd1b 75a811bf6bc57694adb3fe604786f3a4efd1cd1b Edward Thomson <ethomson@edwardthomson.com> 1352098947 -0600 checkout: moving from trivial-8 to trivial-8-branch
+75a811bf6bc57694adb3fe604786f3a4efd1cd1b 52d8bc572af2b6d4ee0d5e62ed5d1fbad92210a9 Edward Thomson <ethomson@edwardthomson.com> 1352098979 -0600 commit: trivial-8-branch
+52d8bc572af2b6d4ee0d5e62ed5d1fbad92210a9 75a811bf6bc57694adb3fe604786f3a4efd1cd1b Edward Thomson <ethomson@edwardthomson.com> 1352098982 -0600 checkout: moving from trivial-8-branch to trivial-8
+75a811bf6bc57694adb3fe604786f3a4efd1cd1b 3575826c96a975031d2c14368529cc5c4353a8fd Edward Thomson <ethomson@edwardthomson.com> 1352099000 -0600 commit: trivial-8
+3575826c96a975031d2c14368529cc5c4353a8fd bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352099008 -0600 checkout: moving from trivial-8 to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352099776 -0600 checkout: moving from master to trivial-7
+c607fc30883e335def28cd686b51f6cfa02b06ec 092ce8682d7f3a2a3a769a6daca58950168ba5c4 Edward Thomson <ethomson@edwardthomson.com> 1352099790 -0600 commit: trivial-7
+092ce8682d7f3a2a3a769a6daca58950168ba5c4 092ce8682d7f3a2a3a769a6daca58950168ba5c4 Edward Thomson <ethomson@edwardthomson.com> 1352099799 -0600 checkout: moving from trivial-7 to trivial-7-branch
+092ce8682d7f3a2a3a769a6daca58950168ba5c4 73cbfdc4fe843169e5b2af8dcad03cbf3acf306c Edward Thomson <ethomson@edwardthomson.com> 1352099812 -0600 commit: trivial-7-branch
+73cbfdc4fe843169e5b2af8dcad03cbf3acf306c 092ce8682d7f3a2a3a769a6daca58950168ba5c4 Edward Thomson <ethomson@edwardthomson.com> 1352099815 -0600 checkout: moving from trivial-7-branch to trivial-7
+092ce8682d7f3a2a3a769a6daca58950168ba5c4 73cbfdc4fe843169e5b2af8dcad03cbf3acf306c Edward Thomson <ethomson@edwardthomson.com> 1352099838 -0600 checkout: moving from trivial-7 to trivial-7-branch
+73cbfdc4fe843169e5b2af8dcad03cbf3acf306c 092ce8682d7f3a2a3a769a6daca58950168ba5c4 Edward Thomson <ethomson@edwardthomson.com> 1352099874 -0600 reset: moving to 092ce8682d7f3a2a3a769a6daca58950168ba5c4
+092ce8682d7f3a2a3a769a6daca58950168ba5c4 009b9cab6fdac02915a88ecd078b7a792ed802d8 Edward Thomson <ethomson@edwardthomson.com> 1352099921 -0600 commit: removed in 7
+009b9cab6fdac02915a88ecd078b7a792ed802d8 5195a1b480f66691b667f10a9e41e70115a78351 Edward Thomson <ethomson@edwardthomson.com> 1352099927 -0600 commit (amend): trivial-7-branch
+5195a1b480f66691b667f10a9e41e70115a78351 092ce8682d7f3a2a3a769a6daca58950168ba5c4 Edward Thomson <ethomson@edwardthomson.com> 1352099937 -0600 checkout: moving from trivial-7-branch to trivial-7
+092ce8682d7f3a2a3a769a6daca58950168ba5c4 d874671ef5b20184836cb983bb273e5280384d0b Edward Thomson <ethomson@edwardthomson.com> 1352099947 -0600 commit: trivial-7
+d874671ef5b20184836cb983bb273e5280384d0b bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352099949 -0600 checkout: moving from trivial-7 to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352100174 -0600 checkout: moving from master to trivial-10
+c607fc30883e335def28cd686b51f6cfa02b06ec 53825f41ac8d640612f9423a2f03a69f3d96809a Edward Thomson <ethomson@edwardthomson.com> 1352100193 -0600 commit: trivial-10
+53825f41ac8d640612f9423a2f03a69f3d96809a 53825f41ac8d640612f9423a2f03a69f3d96809a Edward Thomson <ethomson@edwardthomson.com> 1352100200 -0600 checkout: moving from trivial-10 to trivial-10-branch
+53825f41ac8d640612f9423a2f03a69f3d96809a 11f4f3c08b737f5fd896cbefa1425ee63b21b2fa Edward Thomson <ethomson@edwardthomson.com> 1352100211 -0600 commit: trivial-10-branch
+11f4f3c08b737f5fd896cbefa1425ee63b21b2fa 53825f41ac8d640612f9423a2f03a69f3d96809a Edward Thomson <ethomson@edwardthomson.com> 1352100214 -0600 checkout: moving from trivial-10-branch to trivial-10
+53825f41ac8d640612f9423a2f03a69f3d96809a 0ec5f433959cd46177f745903353efb5be08d151 Edward Thomson <ethomson@edwardthomson.com> 1352100223 -0600 commit: trivial-10
+0ec5f433959cd46177f745903353efb5be08d151 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352100225 -0600 checkout: moving from trivial-10 to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352100270 -0600 checkout: moving from master to trivial-9
+c607fc30883e335def28cd686b51f6cfa02b06ec f0053b8060bb3f0be5cbcc3147a07ece26bf097e Edward Thomson <ethomson@edwardthomson.com> 1352100304 -0600 commit: trivial-9
+f0053b8060bb3f0be5cbcc3147a07ece26bf097e f0053b8060bb3f0be5cbcc3147a07ece26bf097e Edward Thomson <ethomson@edwardthomson.com> 1352100310 -0600 checkout: moving from trivial-9 to trivial-9-branch
+f0053b8060bb3f0be5cbcc3147a07ece26bf097e 13d1be4ea52a6ced1d7a1d832f0ee3c399348e5e Edward Thomson <ethomson@edwardthomson.com> 1352100317 -0600 commit: trivial-9-branch
+13d1be4ea52a6ced1d7a1d832f0ee3c399348e5e f0053b8060bb3f0be5cbcc3147a07ece26bf097e Edward Thomson <ethomson@edwardthomson.com> 1352100319 -0600 checkout: moving from trivial-9-branch to trivial-9
+f0053b8060bb3f0be5cbcc3147a07ece26bf097e c35dee9bcc0e989f3b0c40f68372a9a51b6c4e6a Edward Thomson <ethomson@edwardthomson.com> 1352100333 -0600 commit: trivial-9
+c35dee9bcc0e989f3b0c40f68372a9a51b6c4e6a bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352100335 -0600 checkout: moving from trivial-9 to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352100576 -0600 checkout: moving from master to trivial-13
+c607fc30883e335def28cd686b51f6cfa02b06ec 8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa Edward Thomson <ethomson@edwardthomson.com> 1352100589 -0600 commit: trivial-13
+8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa 8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa Edward Thomson <ethomson@edwardthomson.com> 1352100604 -0600 checkout: moving from trivial-13 to trivial-13-branch
+8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa 05f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91c Edward Thomson <ethomson@edwardthomson.com> 1352100610 -0600 commit: trivial-13-branch
+05f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91c 8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa Edward Thomson <ethomson@edwardthomson.com> 1352100612 -0600 checkout: moving from trivial-13-branch to trivial-13
+8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa a3fabece9eb8748da810e1e08266fef9b7136ad4 Edward Thomson <ethomson@edwardthomson.com> 1352100625 -0600 commit: trivial-13
+a3fabece9eb8748da810e1e08266fef9b7136ad4 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352100627 -0600 checkout: moving from trivial-13 to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352100936 -0600 checkout: moving from master to trivial-11
+c607fc30883e335def28cd686b51f6cfa02b06ec 35632e43612c06a3ea924bfbacd48333da874c29 Edward Thomson <ethomson@edwardthomson.com> 1352100958 -0600 commit: trivial-11
+35632e43612c06a3ea924bfbacd48333da874c29 35632e43612c06a3ea924bfbacd48333da874c29 Edward Thomson <ethomson@edwardthomson.com> 1352100964 -0600 checkout: moving from trivial-11 to trivial-11-branch
+35632e43612c06a3ea924bfbacd48333da874c29 6718a45909532d1fcf5600d0877f7fe7e78f0b86 Edward Thomson <ethomson@edwardthomson.com> 1352100978 -0600 commit: trivial-11-branch
+6718a45909532d1fcf5600d0877f7fe7e78f0b86 35632e43612c06a3ea924bfbacd48333da874c29 Edward Thomson <ethomson@edwardthomson.com> 1352100981 -0600 checkout: moving from trivial-11-branch to trivial-11
+35632e43612c06a3ea924bfbacd48333da874c29 3168dca1a561889b045a6441909f4c56145e666d Edward Thomson <ethomson@edwardthomson.com> 1352100992 -0600 commit: trivial-11
+3168dca1a561889b045a6441909f4c56145e666d bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352100996 -0600 checkout: moving from trivial-11 to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352101098 -0600 checkout: moving from master to trivial-14
+c607fc30883e335def28cd686b51f6cfa02b06ec 596803b523203a4851c824c07366906f8353f4ad Edward Thomson <ethomson@edwardthomson.com> 1352101113 -0600 commit: trivial-14
+596803b523203a4851c824c07366906f8353f4ad 596803b523203a4851c824c07366906f8353f4ad Edward Thomson <ethomson@edwardthomson.com> 1352101117 -0600 checkout: moving from trivial-14 to trivial-14-branch
+596803b523203a4851c824c07366906f8353f4ad 8187117062b750eed4f93fd7e899f17b52ce554d Edward Thomson <ethomson@edwardthomson.com> 1352101132 -0600 commit: trivial-14-branch
+8187117062b750eed4f93fd7e899f17b52ce554d 596803b523203a4851c824c07366906f8353f4ad Edward Thomson <ethomson@edwardthomson.com> 1352101135 -0600 checkout: moving from trivial-14-branch to trivial-14
+596803b523203a4851c824c07366906f8353f4ad 7e2d058d5fedf8329db44db4fac610d6b1a89159 Edward Thomson <ethomson@edwardthomson.com> 1352101141 -0600 commit: trivial-14
+7e2d058d5fedf8329db44db4fac610d6b1a89159 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1352101145 -0600 checkout: moving from trivial-14 to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1353177749 -0600 checkout: moving from master to renames1
+c607fc30883e335def28cd686b51f6cfa02b06ec 412b32fb66137366147f1801ecc962452757d48a Edward Thomson <ethomson@edwardthomson.com> 1353177886 -0600 commit: renames
+412b32fb66137366147f1801ecc962452757d48a bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1353794607 -0600 checkout: moving from renames1 to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1353794647 -0600 checkout: moving from master to renames2
+bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1353794677 -0600 reset: moving to c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec ab40af3cb8a3ed2e2843e96d9aa7871336b94573 Edward Thomson <ethomson@edwardthomson.com> 1353794852 -0600 commit: renames2
+ab40af3cb8a3ed2e2843e96d9aa7871336b94573 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1353794883 -0600 checkout: moving from renames2 to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1354574697 -0600 checkout: moving from master to df_side1
+bd593285fc7fe4ca18ccdbabf027f5d689101452 d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1354574962 -0600 commit: df_ancestor
+d4207f77243500bec335ab477f9227fcdb1e271a c94b27e41064c521120627e07e2035cca1d24ffa Edward Thomson <ethomson@edwardthomson.com> 1354575027 -0600 commit: df_side1
+c94b27e41064c521120627e07e2035cca1d24ffa d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1354575070 -0600 checkout: moving from df_side1 to df_side2
+d4207f77243500bec335ab477f9227fcdb1e271a f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 Edward Thomson <ethomson@edwardthomson.com> 1354575206 -0600 commit: df_side2
+f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1354575381 -0600 checkout: moving from df_side2 to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 c94b27e41064c521120627e07e2035cca1d24ffa Edward Thomson <ethomson@edwardthomson.com> 1355017614 -0600 checkout: moving from master to df_side1
+c94b27e41064c521120627e07e2035cca1d24ffa a90bc3fb6f15181972a2959a921429efbd81a473 Edward Thomson <ethomson@edwardthomson.com> 1355017650 -0600 commit: df_added
+a90bc3fb6f15181972a2959a921429efbd81a473 c94b27e41064c521120627e07e2035cca1d24ffa Edward Thomson <ethomson@edwardthomson.com> 1355017673 -0600 checkout: moving from df_side1 to c94b27e
+c94b27e41064c521120627e07e2035cca1d24ffa d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1355017673 -0600 rebase -i (squash): updating HEAD
+d4207f77243500bec335ab477f9227fcdb1e271a 005b6fcc8fec71d2550bef8462d169b3c26aa14b Edward Thomson <ethomson@edwardthomson.com> 1355017673 -0600 rebase -i (squash): df_side1
+005b6fcc8fec71d2550bef8462d169b3c26aa14b 005b6fcc8fec71d2550bef8462d169b3c26aa14b Edward Thomson <ethomson@edwardthomson.com> 1355017676 -0600 rebase -i (finish): returning to refs/heads/df_side1
+005b6fcc8fec71d2550bef8462d169b3c26aa14b f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 Edward Thomson <ethomson@edwardthomson.com> 1355017715 -0600 reset: moving to df_side2
+f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 8c749d9968d4b10dcfb06c9f97d0e5d92d337071 Edward Thomson <ethomson@edwardthomson.com> 1355017744 -0600 commit: df_added
+8c749d9968d4b10dcfb06c9f97d0e5d92d337071 f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 Edward Thomson <ethomson@edwardthomson.com> 1355017754 -0600 checkout: moving from df_side1 to f8958bd
+f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1355017754 -0600 rebase -i (squash): updating HEAD
+d4207f77243500bec335ab477f9227fcdb1e271a 0204a84f822acbf6386b36d33f1f6bc68bbbf858 Edward Thomson <ethomson@edwardthomson.com> 1355017754 -0600 rebase -i (squash): df_side2
+0204a84f822acbf6386b36d33f1f6bc68bbbf858 0204a84f822acbf6386b36d33f1f6bc68bbbf858 Edward Thomson <ethomson@edwardthomson.com> 1355017756 -0600 rebase -i (finish): returning to refs/heads/df_side1
+0204a84f822acbf6386b36d33f1f6bc68bbbf858 005b6fcc8fec71d2550bef8462d169b3c26aa14b Edward Thomson <ethomson@edwardthomson.com> 1355017793 -0600 reset: moving to 005b6fcc8fec71d2550bef8462d169b3c26aa14b
+005b6fcc8fec71d2550bef8462d169b3c26aa14b 0204a84f822acbf6386b36d33f1f6bc68bbbf858 Edward Thomson <ethomson@edwardthomson.com> 1355017826 -0600 reset: moving to 0204a84
+0204a84f822acbf6386b36d33f1f6bc68bbbf858 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1355017847 -0600 checkout: moving from df_side1 to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 0204a84f822acbf6386b36d33f1f6bc68bbbf858 Edward Thomson <ethomson@edwardthomson.com> 1355168677 -0600 checkout: moving from master to df_side1
+005b6fcc8fec71d2550bef8462d169b3c26aa14b 005b6fcc8fec71d2550bef8462d169b3c26aa14b Edward Thomson <ethomson@edwardthomson.com> 1355168829 -0600 checkout: moving from df_side1 to df_side1
+005b6fcc8fec71d2550bef8462d169b3c26aa14b 005b6fcc8fec71d2550bef8462d169b3c26aa14b Edward Thomson <ethomson@edwardthomson.com> 1355168838 -0600 checkout: moving from df_side1 to df_side1
+005b6fcc8fec71d2550bef8462d169b3c26aa14b e8107f24196736b870a318a0e28f048e29f6feff Edward Thomson <ethomson@edwardthomson.com> 1355169065 -0600 commit: df_side1
+e8107f24196736b870a318a0e28f048e29f6feff 005b6fcc8fec71d2550bef8462d169b3c26aa14b Edward Thomson <ethomson@edwardthomson.com> 1355169081 -0600 checkout: moving from df_side1 to 005b6fc
+005b6fcc8fec71d2550bef8462d169b3c26aa14b d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1355169081 -0600 rebase -i (squash): updating HEAD
+d4207f77243500bec335ab477f9227fcdb1e271a 80a8fbb3abb1ba423d554e9630b8fc2e5698f86b Edward Thomson <ethomson@edwardthomson.com> 1355169081 -0600 rebase -i (squash): df_side1
+80a8fbb3abb1ba423d554e9630b8fc2e5698f86b 80a8fbb3abb1ba423d554e9630b8fc2e5698f86b Edward Thomson <ethomson@edwardthomson.com> 1355169084 -0600 rebase -i (finish): returning to refs/heads/df_side1
+80a8fbb3abb1ba423d554e9630b8fc2e5698f86b 0204a84f822acbf6386b36d33f1f6bc68bbbf858 Edward Thomson <ethomson@edwardthomson.com> 1355169141 -0600 checkout: moving from df_side1 to df_side2
+0204a84f822acbf6386b36d33f1f6bc68bbbf858 944f5dd1a867cab4c2bbcb896493435cae1dcc1a Edward Thomson <ethomson@edwardthomson.com> 1355169174 -0600 commit: both
+944f5dd1a867cab4c2bbcb896493435cae1dcc1a 0204a84f822acbf6386b36d33f1f6bc68bbbf858 Edward Thomson <ethomson@edwardthomson.com> 1355169182 -0600 checkout: moving from df_side2 to 0204a84
+0204a84f822acbf6386b36d33f1f6bc68bbbf858 d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1355169182 -0600 rebase -i (squash): updating HEAD
+d4207f77243500bec335ab477f9227fcdb1e271a 57079a46233ae2b6df62e9ade71c4948512abefb Edward Thomson <ethomson@edwardthomson.com> 1355169182 -0600 rebase -i (squash): df_side2
+57079a46233ae2b6df62e9ade71c4948512abefb 57079a46233ae2b6df62e9ade71c4948512abefb Edward Thomson <ethomson@edwardthomson.com> 1355169185 -0600 rebase -i (finish): returning to refs/heads/df_side2
+57079a46233ae2b6df62e9ade71c4948512abefb 80a8fbb3abb1ba423d554e9630b8fc2e5698f86b Edward Thomson <ethomson@edwardthomson.com> 1355169241 -0600 checkout: moving from df_side2 to df_side1
+80a8fbb3abb1ba423d554e9630b8fc2e5698f86b e65a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812 Edward Thomson <ethomson@edwardthomson.com> 1355169419 -0600 commit: side1
+e65a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812 80a8fbb3abb1ba423d554e9630b8fc2e5698f86b Edward Thomson <ethomson@edwardthomson.com> 1355169431 -0600 checkout: moving from df_side1 to 80a8fbb
+80a8fbb3abb1ba423d554e9630b8fc2e5698f86b d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1355169431 -0600 rebase -i (squash): updating HEAD
+d4207f77243500bec335ab477f9227fcdb1e271a 5dc1018e90b19654bee986b7a0c268804d39659d Edward Thomson <ethomson@edwardthomson.com> 1355169431 -0600 rebase -i (squash): df_side1
+5dc1018e90b19654bee986b7a0c268804d39659d 5dc1018e90b19654bee986b7a0c268804d39659d Edward Thomson <ethomson@edwardthomson.com> 1355169435 -0600 rebase -i (finish): returning to refs/heads/df_side1
+5dc1018e90b19654bee986b7a0c268804d39659d 57079a46233ae2b6df62e9ade71c4948512abefb Edward Thomson <ethomson@edwardthomson.com> 1355169439 -0600 checkout: moving from df_side1 to df_side2
+57079a46233ae2b6df62e9ade71c4948512abefb 58e853f66699fd02629fd50bde08082bc005933a Edward Thomson <ethomson@edwardthomson.com> 1355169460 -0600 commit: side2
+58e853f66699fd02629fd50bde08082bc005933a 57079a46233ae2b6df62e9ade71c4948512abefb Edward Thomson <ethomson@edwardthomson.com> 1355169469 -0600 checkout: moving from df_side2 to 57079a4
+57079a46233ae2b6df62e9ade71c4948512abefb d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1355169469 -0600 rebase -i (squash): updating HEAD
+d4207f77243500bec335ab477f9227fcdb1e271a fada9356aa3f74622327a3038ae9c6f92e1c5c1d Edward Thomson <ethomson@edwardthomson.com> 1355169469 -0600 rebase -i (squash): df_side2
+fada9356aa3f74622327a3038ae9c6f92e1c5c1d fada9356aa3f74622327a3038ae9c6f92e1c5c1d Edward Thomson <ethomson@edwardthomson.com> 1355169471 -0600 rebase -i (finish): returning to refs/heads/df_side2
+fada9356aa3f74622327a3038ae9c6f92e1c5c1d 5dc1018e90b19654bee986b7a0c268804d39659d Edward Thomson <ethomson@edwardthomson.com> 1355169494 -0600 checkout: moving from df_side2 to df_side1
+5dc1018e90b19654bee986b7a0c268804d39659d d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1355169663 -0600 checkout: moving from df_side1 to d4207f77243500bec335ab477f9227fcdb1e271a
+d4207f77243500bec335ab477f9227fcdb1e271a 849619b03ae540acee4d1edec96b86993da6b497 Edward Thomson <ethomson@edwardthomson.com> 1355169683 -0600 commit: both_dirs
+849619b03ae540acee4d1edec96b86993da6b497 d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1355169691 -0600 checkout: moving from 849619b03ae540acee4d1edec96b86993da6b497 to d4207f7
+d4207f77243500bec335ab477f9227fcdb1e271a bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1355169691 -0600 rebase -i (squash): updating HEAD
+bd593285fc7fe4ca18ccdbabf027f5d689101452 a765fb87eb2f7a1920b73b2d5a057f8f8476a42b Edward Thomson <ethomson@edwardthomson.com> 1355169691 -0600 rebase -i (squash): df_ancestor
+a765fb87eb2f7a1920b73b2d5a057f8f8476a42b 5dc1018e90b19654bee986b7a0c268804d39659d Edward Thomson <ethomson@edwardthomson.com> 1355169706 -0600 checkout: moving from a765fb87eb2f7a1920b73b2d5a057f8f8476a42b to df_side1
+5dc1018e90b19654bee986b7a0c268804d39659d a765fb87eb2f7a1920b73b2d5a057f8f8476a42b Edward Thomson <ethomson@edwardthomson.com> 1355169715 -0600 checkout: moving from df_side1 to a765fb87eb2f7a1920b73b2d5a057f8f8476a42b^0
+a765fb87eb2f7a1920b73b2d5a057f8f8476a42b bc744705e1d8a019993cf88f62bc4020f1b80919 Edward Thomson <ethomson@edwardthomson.com> 1355169801 -0600 commit: df_side1
+bc744705e1d8a019993cf88f62bc4020f1b80919 bc744705e1d8a019993cf88f62bc4020f1b80919 Edward Thomson <ethomson@edwardthomson.com> 1355169822 -0600 checkout: moving from bc744705e1d8a019993cf88f62bc4020f1b80919 to df_side1
+bc744705e1d8a019993cf88f62bc4020f1b80919 fada9356aa3f74622327a3038ae9c6f92e1c5c1d Edward Thomson <ethomson@edwardthomson.com> 1355169826 -0600 checkout: moving from df_side1 to df_side2
+fada9356aa3f74622327a3038ae9c6f92e1c5c1d a765fb87eb2f7a1920b73b2d5a057f8f8476a42b Edward Thomson <ethomson@edwardthomson.com> 1355169866 -0600 checkout: moving from df_side2 to a765fb87eb2f7a1920b73b2d5a057f8f8476a42b^0
+a765fb87eb2f7a1920b73b2d5a057f8f8476a42b 95646149ab6b6ba6edc83cff678582538b457b2b Edward Thomson <ethomson@edwardthomson.com> 1355169897 -0600 rebase: df_side2
+95646149ab6b6ba6edc83cff678582538b457b2b 95646149ab6b6ba6edc83cff678582538b457b2b Edward Thomson <ethomson@edwardthomson.com> 1355169897 -0600 rebase finished: returning to refs/heads/df_side2
+95646149ab6b6ba6edc83cff678582538b457b2b bc744705e1d8a019993cf88f62bc4020f1b80919 Edward Thomson <ethomson@edwardthomson.com> 1355169949 -0600 checkout: moving from df_side2 to df_side1
+bc744705e1d8a019993cf88f62bc4020f1b80919 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1355170046 -0600 checkout: moving from df_side1 to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1355181639 -0600 checkout: moving from master to df_ancestor
+bd593285fc7fe4ca18ccdbabf027f5d689101452 2da538570bc1e5b2c3e855bf702f35248ad0735f Edward Thomson <ethomson@edwardthomson.com> 1355181673 -0600 commit: df_ancestor
+2da538570bc1e5b2c3e855bf702f35248ad0735f a7dbfcbfc1a60709cb80b5ca24539008456531d0 Edward Thomson <ethomson@edwardthomson.com> 1355181715 -0600 commit: df_side1
+a7dbfcbfc1a60709cb80b5ca24539008456531d0 a7dbfcbfc1a60709cb80b5ca24539008456531d0 Edward Thomson <ethomson@edwardthomson.com> 1355181743 -0600 checkout: moving from df_ancestor to df_ancestor
+a7dbfcbfc1a60709cb80b5ca24539008456531d0 9a301fbe6fada7dcb74fcd7c20269b5c743459a7 Edward Thomson <ethomson@edwardthomson.com> 1355181775 -0600 commit: df_side2
+9a301fbe6fada7dcb74fcd7c20269b5c743459a7 a7dbfcbfc1a60709cb80b5ca24539008456531d0 Edward Thomson <ethomson@edwardthomson.com> 1355181793 -0600 checkout: moving from df_ancestor to df_side1
+a7dbfcbfc1a60709cb80b5ca24539008456531d0 9a301fbe6fada7dcb74fcd7c20269b5c743459a7 Edward Thomson <ethomson@edwardthomson.com> 1355181797 -0600 checkout: moving from df_side1 to df_side2
+9a301fbe6fada7dcb74fcd7c20269b5c743459a7 9a301fbe6fada7dcb74fcd7c20269b5c743459a7 Edward Thomson <ethomson@edwardthomson.com> 1355182062 -0600 checkout: moving from df_side2 to df_ancestor
+9a301fbe6fada7dcb74fcd7c20269b5c743459a7 2da538570bc1e5b2c3e855bf702f35248ad0735f Edward Thomson <ethomson@edwardthomson.com> 1355182067 -0600 reset: moving to 2da538570bc1e5b2c3e855bf702f35248ad0735f
+2da538570bc1e5b2c3e855bf702f35248ad0735f 2da538570bc1e5b2c3e855bf702f35248ad0735f Edward Thomson <ethomson@edwardthomson.com> 1355182087 -0600 checkout: moving from df_ancestor to df_side2
+2da538570bc1e5b2c3e855bf702f35248ad0735f fc90237dc4891fa6c69827fc465632225e391618 Edward Thomson <ethomson@edwardthomson.com> 1355182104 -0600 commit: df_side2
+fc90237dc4891fa6c69827fc465632225e391618 a7dbfcbfc1a60709cb80b5ca24539008456531d0 Edward Thomson <ethomson@edwardthomson.com> 1355182111 -0600 checkout: moving from df_side2 to df_side1
+a7dbfcbfc1a60709cb80b5ca24539008456531d0 fc90237dc4891fa6c69827fc465632225e391618 Edward Thomson <ethomson@edwardthomson.com> 1355182115 -0600 checkout: moving from df_side1 to df_side2
+fc90237dc4891fa6c69827fc465632225e391618 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1355182122 -0600 checkout: moving from df_side2 to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 d6cf6c7741b3316826af1314042550c97ded1d50 Edward Thomson <ethomson@edwardthomson.com> 1358997543 -0600 checkout: moving from master to unrelated
+d6cf6c7741b3316826af1314042550c97ded1d50 55b4e4687e7a0d9ca367016ed930f385d4022e6f Edward Thomson <ethomson@edwardthomson.com> 1358997664 -0600 commit: conflicting changes
+55b4e4687e7a0d9ca367016ed930f385d4022e6f bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1358997675 -0600 checkout: moving from unrelated to master
+bd593285fc7fe4ca18ccdbabf027f5d689101452 88e185910a15cd13bdf44854ad037f4842b03b29 Edward Thomson <ethomson@microsoft.com> 1365714471 -0500 checkout: moving from master to rename_conflict_ours
+88e185910a15cd13bdf44854ad037f4842b03b29 bef6e37b3ee632ba74159168836f382fed21d77d Edward Thomson <ethomson@microsoft.com> 1365714516 -0500 checkout: moving from rename_conflict_ours to bef6e37b3ee632ba74159168836f382fed21d77d
+bef6e37b3ee632ba74159168836f382fed21d77d 01f149e1b8f84bd8896aaff6d6b22af88459ded0 Edward Thomson <ethomson@microsoft.com> 1365714831 -0500 commit: rename ancestor
+0000000000000000000000000000000000000000 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson <ethomson@microsoft.com> 1365714958 -0500 commit (initial): rename conflict ancestor
+2392a2dacc9efb562b8635d6579fb458751c7c5b 88e185910a15cd13bdf44854ad037f4842b03b29 Edward Thomson <ethomson@microsoft.com> 1365714980 -0500 checkout: moving from rename_conflict_ancestor to rename_conflict_ours
+88e185910a15cd13bdf44854ad037f4842b03b29 7c2c5228c9e90170d4a35e6558e47163daf092e5 Edward Thomson <ethomson@microsoft.com> 1365715250 -0500 commit: rename conflict ours
+7c2c5228c9e90170d4a35e6558e47163daf092e5 2f4024ce528d36d8670c289cce5a7963e625bb0c Edward Thomson <ethomson@microsoft.com> 1365715274 -0500 checkout: moving from rename_conflict_ours to rename_conflict_theirs
+2f4024ce528d36d8670c289cce5a7963e625bb0c 56a638b76b75e068590ac999c2f8621e7f3e264c Edward Thomson <ethomson@microsoft.com> 1365715362 -0500 commit: rename conflict theirs
+56a638b76b75e068590ac999c2f8621e7f3e264c 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson <ethomson@microsoft.com> 1365715368 -0500 checkout: moving from rename_conflict_theirs to rename_conflict_ancestor
+2392a2dacc9efb562b8635d6579fb458751c7c5b 56a638b76b75e068590ac999c2f8621e7f3e264c Edward Thomson <ethomson@microsoft.com> 1365715371 -0500 checkout: moving from rename_conflict_ancestor to rename_conflict_theirs
+56a638b76b75e068590ac999c2f8621e7f3e264c 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson <ethomson@microsoft.com> 1365715404 -0500 checkout: moving from rename_conflict_theirs to rename_conflict_ancestor
+2392a2dacc9efb562b8635d6579fb458751c7c5b 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson <ethomson@microsoft.com> 1365715438 -0500 checkout: moving from rename_conflict_ancestor to rename_conflict_ours
+2392a2dacc9efb562b8635d6579fb458751c7c5b 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson <ethomson@microsoft.com> 1365715480 -0500 checkout: moving from rename_conflict_ours to rename_conflict_ancestor
+2392a2dacc9efb562b8635d6579fb458751c7c5b 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson <ethomson@microsoft.com> 1365715486 -0500 checkout: moving from rename_conflict_ancestor to rename_conflict_ours
+2392a2dacc9efb562b8635d6579fb458751c7c5b f3293571dcd708b6a3faf03818cd2844d000e198 Edward Thomson <ethomson@microsoft.com> 1365715538 -0500 commit: rename conflict ours
+f3293571dcd708b6a3faf03818cd2844d000e198 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson <ethomson@microsoft.com> 1365715546 -0500 checkout: moving from rename_conflict_ours to rename_conflict_ancestor
+2392a2dacc9efb562b8635d6579fb458751c7c5b 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson <ethomson@microsoft.com> 1365715550 -0500 checkout: moving from rename_conflict_ancestor to rename_conflict_thiers
+2392a2dacc9efb562b8635d6579fb458751c7c5b 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson <ethomson@microsoft.com> 1365715554 -0500 checkout: moving from rename_conflict_thiers to rename_conflict_ancestor
+2392a2dacc9efb562b8635d6579fb458751c7c5b 2392a2dacc9efb562b8635d6579fb458751c7c5b Edward Thomson <ethomson@microsoft.com> 1365715557 -0500 checkout: moving from rename_conflict_ancestor to rename_conflict_theirs
+2392a2dacc9efb562b8635d6579fb458751c7c5b a802e06f1782a9645b9851bc7202cee74a8a4972 Edward Thomson <ethomson@microsoft.com> 1365715572 -0500 commit: rename conflict theirs
+a802e06f1782a9645b9851bc7202cee74a8a4972 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@microsoft.com> 1365715620 -0500 checkout: moving from rename_conflict_theirs to master
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/branch
new file mode 100644
index 0000000..8b0acb7
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/branch
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1351563886 -0500 branch: Created from HEAD
+c607fc30883e335def28cd686b51f6cfa02b06ec 7cb63eed597130ba4abb87b3e544b85021905520 Edward Thomson <ethomson@edwardthomson.com> 1351563965 -0500 commit: branch
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/df_ancestor b/tests/resources/merge-resolve/.gitted/logs/refs/heads/df_ancestor
new file mode 100644
index 0000000..df7695a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/df_ancestor
@@ -0,0 +1,5 @@
+0000000000000000000000000000000000000000 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1355181639 -0600 branch: Created from HEAD
+bd593285fc7fe4ca18ccdbabf027f5d689101452 2da538570bc1e5b2c3e855bf702f35248ad0735f Edward Thomson <ethomson@edwardthomson.com> 1355181673 -0600 commit: df_ancestor
+2da538570bc1e5b2c3e855bf702f35248ad0735f a7dbfcbfc1a60709cb80b5ca24539008456531d0 Edward Thomson <ethomson@edwardthomson.com> 1355181715 -0600 commit: df_side1
+a7dbfcbfc1a60709cb80b5ca24539008456531d0 9a301fbe6fada7dcb74fcd7c20269b5c743459a7 Edward Thomson <ethomson@edwardthomson.com> 1355181775 -0600 commit: df_side2
+9a301fbe6fada7dcb74fcd7c20269b5c743459a7 2da538570bc1e5b2c3e855bf702f35248ad0735f Edward Thomson <ethomson@edwardthomson.com> 1355182067 -0600 reset: moving to 2da538570bc1e5b2c3e855bf702f35248ad0735f
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/df_side1 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/df_side1
new file mode 100644
index 0000000..a504ad6
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/df_side1
@@ -0,0 +1,14 @@
+0000000000000000000000000000000000000000 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1354574697 -0600 branch: Created from HEAD
+bd593285fc7fe4ca18ccdbabf027f5d689101452 d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1354574962 -0600 commit: df_ancestor
+d4207f77243500bec335ab477f9227fcdb1e271a c94b27e41064c521120627e07e2035cca1d24ffa Edward Thomson <ethomson@edwardthomson.com> 1354575027 -0600 commit: df_side1
+c94b27e41064c521120627e07e2035cca1d24ffa a90bc3fb6f15181972a2959a921429efbd81a473 Edward Thomson <ethomson@edwardthomson.com> 1355017650 -0600 commit: df_added
+a90bc3fb6f15181972a2959a921429efbd81a473 005b6fcc8fec71d2550bef8462d169b3c26aa14b Edward Thomson <ethomson@edwardthomson.com> 1355017676 -0600 rebase -i (finish): refs/heads/df_side1 onto c94b27e
+005b6fcc8fec71d2550bef8462d169b3c26aa14b f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 Edward Thomson <ethomson@edwardthomson.com> 1355017715 -0600 reset: moving to df_side2
+f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 8c749d9968d4b10dcfb06c9f97d0e5d92d337071 Edward Thomson <ethomson@edwardthomson.com> 1355017744 -0600 commit: df_added
+8c749d9968d4b10dcfb06c9f97d0e5d92d337071 0204a84f822acbf6386b36d33f1f6bc68bbbf858 Edward Thomson <ethomson@edwardthomson.com> 1355017756 -0600 rebase -i (finish): refs/heads/df_side1 onto f8958bd
+0204a84f822acbf6386b36d33f1f6bc68bbbf858 005b6fcc8fec71d2550bef8462d169b3c26aa14b Edward Thomson <ethomson@edwardthomson.com> 1355017793 -0600 reset: moving to 005b6fcc8fec71d2550bef8462d169b3c26aa14b
+005b6fcc8fec71d2550bef8462d169b3c26aa14b 0204a84f822acbf6386b36d33f1f6bc68bbbf858 Edward Thomson <ethomson@edwardthomson.com> 1355017826 -0600 reset: moving to 0204a84
+005b6fcc8fec71d2550bef8462d169b3c26aa14b e8107f24196736b870a318a0e28f048e29f6feff Edward Thomson <ethomson@edwardthomson.com> 1355169065 -0600 commit: df_side1
+e8107f24196736b870a318a0e28f048e29f6feff 80a8fbb3abb1ba423d554e9630b8fc2e5698f86b Edward Thomson <ethomson@edwardthomson.com> 1355169084 -0600 rebase -i (finish): refs/heads/df_side1 onto 005b6fc
+80a8fbb3abb1ba423d554e9630b8fc2e5698f86b e65a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812 Edward Thomson <ethomson@edwardthomson.com> 1355169419 -0600 commit: side1
+e65a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812 5dc1018e90b19654bee986b7a0c268804d39659d Edward Thomson <ethomson@edwardthomson.com> 1355169435 -0600 rebase -i (finish): refs/heads/df_side1 onto 80a8fbb
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/df_side2 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/df_side2
new file mode 100644
index 0000000..27d833e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/df_side2
@@ -0,0 +1,9 @@
+0000000000000000000000000000000000000000 d4207f77243500bec335ab477f9227fcdb1e271a Edward Thomson <ethomson@edwardthomson.com> 1354575051 -0600 branch: Created from d4207f77243500bec335ab477f9227fcdb1e271a
+d4207f77243500bec335ab477f9227fcdb1e271a f8958bdf4d365a84a9a178b1f5f35ff1dacbd884 Edward Thomson <ethomson@edwardthomson.com> 1354575206 -0600 commit: df_side2
+0204a84f822acbf6386b36d33f1f6bc68bbbf858 944f5dd1a867cab4c2bbcb896493435cae1dcc1a Edward Thomson <ethomson@edwardthomson.com> 1355169174 -0600 commit: both
+944f5dd1a867cab4c2bbcb896493435cae1dcc1a 57079a46233ae2b6df62e9ade71c4948512abefb Edward Thomson <ethomson@edwardthomson.com> 1355169185 -0600 rebase -i (finish): refs/heads/df_side2 onto 0204a84
+57079a46233ae2b6df62e9ade71c4948512abefb 58e853f66699fd02629fd50bde08082bc005933a Edward Thomson <ethomson@edwardthomson.com> 1355169460 -0600 commit: side2
+58e853f66699fd02629fd50bde08082bc005933a fada9356aa3f74622327a3038ae9c6f92e1c5c1d Edward Thomson <ethomson@edwardthomson.com> 1355169471 -0600 rebase -i (finish): refs/heads/df_side2 onto 57079a4
+fada9356aa3f74622327a3038ae9c6f92e1c5c1d 95646149ab6b6ba6edc83cff678582538b457b2b Edward Thomson <ethomson@edwardthomson.com> 1355169897 -0600 rebase finished: refs/heads/df_side2 onto a765fb87eb2f7a1920b73b2d5a057f8f8476a42b
+0000000000000000000000000000000000000000 2da538570bc1e5b2c3e855bf702f35248ad0735f Edward Thomson <ethomson@edwardthomson.com> 1355182087 -0600 branch: Created from HEAD
+2da538570bc1e5b2c3e855bf702f35248ad0735f fc90237dc4891fa6c69827fc465632225e391618 Edward Thomson <ethomson@edwardthomson.com> 1355182104 -0600 commit: df_side2
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/ff_branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/ff_branch
new file mode 100644
index 0000000..c470617
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/ff_branch
@@ -0,0 +1,5 @@
+0000000000000000000000000000000000000000 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351605785 -0500 branch: Created from HEAD
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 33d500f588fbbe65901d82b4e6b008e549064be0 Edward Thomson <ethomson@edwardthomson.com> 1351605830 -0500 commit: fastforward
+33d500f588fbbe65901d82b4e6b008e549064be0 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1351990202 -0500 reset: moving to c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1351990205 -0500 merge master: Fast-forward
+bd593285fc7fe4ca18ccdbabf027f5d689101452 fd89f8cffb663ac89095a0f9764902e93ceaca6a Edward Thomson <ethomson@edwardthomson.com> 1351990229 -0500 commit: fastforward
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/master b/tests/resources/merge-resolve/.gitted/logs/refs/heads/master
new file mode 100644
index 0000000..6047599
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/master
@@ -0,0 +1,5 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1351563869 -0500 commit (initial): initial
+c607fc30883e335def28cd686b51f6cfa02b06ec 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351564033 -0500 commit: master
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 4e0d9401aee78eb345a8685a859d37c8c3c0bbed Edward Thomson <ethomson@edwardthomson.com> 1351875091 -0500 merge octo1 octo2 octo3 octo4: Merge made by the 'octopus' strategy.
+4e0d9401aee78eb345a8685a859d37c8c3c0bbed 54269b3f6ec3d7d4ede24dd350dd5d605495c3ae Edward Thomson <ethomson@edwardthomson.com> 1351875108 -0500 reset: moving to 54269b3f6ec3d7d4ede24dd350dd5d605495c3ae
+54269b3f6ec3d7d4ede24dd350dd5d605495c3ae 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875584 -0500 reset: moving to 977c696519c5a3004c5f1d15d60c89dbeb8f235f
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo1 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo1
new file mode 100644
index 0000000..0b6c921
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo1
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351874933 -0500 branch: Created from HEAD
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 16f825815cfd20a07a75c71554e82d8eede0b061 Edward Thomson <ethomson@edwardthomson.com> 1351874954 -0500 commit: octo1
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo2 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo2
new file mode 100644
index 0000000..5392a4f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo2
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351874960 -0500 branch: Created from HEAD
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 158dc7bedb202f5b26502bf3574faa7f4238d56c Edward Thomson <ethomson@edwardthomson.com> 1351874974 -0500 commit: octo2
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo3 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo3
new file mode 100644
index 0000000..7db5617
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo3
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351874980 -0500 branch: Created from HEAD
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 50ce7d7d01217679e26c55939eef119e0c93e272 Edward Thomson <ethomson@edwardthomson.com> 1351874998 -0500 commit: octo3
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo4 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo4
new file mode 100644
index 0000000..b0f9e42
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo4
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875010 -0500 branch: Created from HEAD
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 54269b3f6ec3d7d4ede24dd350dd5d605495c3ae Edward Thomson <ethomson@edwardthomson.com> 1351875023 -0500 commit: octo4
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo5 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo5
new file mode 100644
index 0000000..614563e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo5
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875031 -0500 branch: Created from HEAD
+977c696519c5a3004c5f1d15d60c89dbeb8f235f e4f618a2c3ed0669308735727df5ebf2447f022f Edward Thomson <ethomson@edwardthomson.com> 1351875041 -0500 commit: octo5
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo6 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo6
new file mode 100644
index 0000000..4c812ea
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/octo6
@@ -0,0 +1,3 @@
+0000000000000000000000000000000000000000 977c696519c5a3004c5f1d15d60c89dbeb8f235f Edward Thomson <ethomson@edwardthomson.com> 1351875046 -0500 branch: Created from HEAD
+977c696519c5a3004c5f1d15d60c89dbeb8f235f 4ca408a8c88655f7586a1b580be6fad138121e98 Edward Thomson <ethomson@edwardthomson.com> 1351875057 -0500 commit: octo5
+4ca408a8c88655f7586a1b580be6fad138121e98 b6f610aef53bd343e6c96227de874c66f00ee8e8 Edward Thomson <ethomson@edwardthomson.com> 1351875065 -0500 commit (amend): octo6
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/renames1 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/renames1
new file mode 100644
index 0000000..58a7e05
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/renames1
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1353177745 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec 412b32fb66137366147f1801ecc962452757d48a Edward Thomson <ethomson@edwardthomson.com> 1353177886 -0600 commit: renames
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/renames2 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/renames2
new file mode 100644
index 0000000..5645ece
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/renames2
@@ -0,0 +1,3 @@
+0000000000000000000000000000000000000000 bd593285fc7fe4ca18ccdbabf027f5d689101452 Edward Thomson <ethomson@edwardthomson.com> 1353794647 -0600 branch: Created from HEAD
+bd593285fc7fe4ca18ccdbabf027f5d689101452 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1353794677 -0600 reset: moving to c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec ab40af3cb8a3ed2e2843e96d9aa7871336b94573 Edward Thomson <ethomson@edwardthomson.com> 1353794852 -0600 commit: renames2
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10
new file mode 100644
index 0000000..b6bd247
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10
@@ -0,0 +1,3 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352100171 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec 53825f41ac8d640612f9423a2f03a69f3d96809a Edward Thomson <ethomson@edwardthomson.com> 1352100193 -0600 commit: trivial-10
+53825f41ac8d640612f9423a2f03a69f3d96809a 0ec5f433959cd46177f745903353efb5be08d151 Edward Thomson <ethomson@edwardthomson.com> 1352100223 -0600 commit: trivial-10
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10-branch
new file mode 100644
index 0000000..14ce9e5
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-10-branch
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 53825f41ac8d640612f9423a2f03a69f3d96809a Edward Thomson <ethomson@edwardthomson.com> 1352100200 -0600 branch: Created from HEAD
+53825f41ac8d640612f9423a2f03a69f3d96809a 11f4f3c08b737f5fd896cbefa1425ee63b21b2fa Edward Thomson <ethomson@edwardthomson.com> 1352100211 -0600 commit: trivial-10-branch
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11
new file mode 100644
index 0000000..3e6b774
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11
@@ -0,0 +1,3 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352100930 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec 35632e43612c06a3ea924bfbacd48333da874c29 Edward Thomson <ethomson@edwardthomson.com> 1352100958 -0600 commit: trivial-11
+35632e43612c06a3ea924bfbacd48333da874c29 3168dca1a561889b045a6441909f4c56145e666d Edward Thomson <ethomson@edwardthomson.com> 1352100992 -0600 commit: trivial-11
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11-branch
new file mode 100644
index 0000000..30d5ec7
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-11-branch
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 35632e43612c06a3ea924bfbacd48333da874c29 Edward Thomson <ethomson@edwardthomson.com> 1352100964 -0600 branch: Created from HEAD
+35632e43612c06a3ea924bfbacd48333da874c29 6718a45909532d1fcf5600d0877f7fe7e78f0b86 Edward Thomson <ethomson@edwardthomson.com> 1352100978 -0600 commit: trivial-11-branch
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13
new file mode 100644
index 0000000..3a7302d
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13
@@ -0,0 +1,3 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352100559 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec 8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa Edward Thomson <ethomson@edwardthomson.com> 1352100589 -0600 commit: trivial-13
+8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa a3fabece9eb8748da810e1e08266fef9b7136ad4 Edward Thomson <ethomson@edwardthomson.com> 1352100625 -0600 commit: trivial-13
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13-branch
new file mode 100644
index 0000000..bb26042
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-13-branch
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa Edward Thomson <ethomson@edwardthomson.com> 1352100604 -0600 branch: Created from HEAD
+8f4433f8593ddd65b7dd43dd4564d841f4d9c8aa 05f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91c Edward Thomson <ethomson@edwardthomson.com> 1352100610 -0600 commit: trivial-13-branch
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14
new file mode 100644
index 0000000..4b70d28
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14
@@ -0,0 +1,3 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352101083 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec 596803b523203a4851c824c07366906f8353f4ad Edward Thomson <ethomson@edwardthomson.com> 1352101113 -0600 commit: trivial-14
+596803b523203a4851c824c07366906f8353f4ad 7e2d058d5fedf8329db44db4fac610d6b1a89159 Edward Thomson <ethomson@edwardthomson.com> 1352101141 -0600 commit: trivial-14
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14-branch
new file mode 100644
index 0000000..8e491ca
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-14-branch
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 596803b523203a4851c824c07366906f8353f4ad Edward Thomson <ethomson@edwardthomson.com> 1352101117 -0600 branch: Created from HEAD
+596803b523203a4851c824c07366906f8353f4ad 8187117062b750eed4f93fd7e899f17b52ce554d Edward Thomson <ethomson@edwardthomson.com> 1352101132 -0600 commit: trivial-14-branch
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt
new file mode 100644
index 0000000..a2a28d4
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352091695 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec 566ab53c220a2eafc1212af1a024513230280ab9 Edward Thomson <ethomson@edwardthomson.com> 1352092452 -0600 commit: 2alt
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt-branch
new file mode 100644
index 0000000..a0a48ae
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-2alt-branch
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352092411 -0600 branch: Created from HEAD
+c607fc30883e335def28cd686b51f6cfa02b06ec c9174cef549ec94ecbc43ef03cdc775b4950becb Edward Thomson <ethomson@edwardthomson.com> 1352092434 -0600 commit: 2alt-branch
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt
new file mode 100644
index 0000000..4374d38
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt
@@ -0,0 +1,3 @@
+566ab53c220a2eafc1212af1a024513230280ab9 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352094547 -0600 reset: moving to c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec 5459c89aa0026d543ce8343bd89871bce543f9c2 Edward Thomson <ethomson@edwardthomson.com> 1352094580 -0600 commit: 3alt
+5459c89aa0026d543ce8343bd89871bce543f9c2 4c9fac0707f8d4195037ae5a681aa48626491541 Edward Thomson <ethomson@edwardthomson.com> 1352094610 -0600 commit: 3alt-branch
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt-branch
new file mode 100644
index 0000000..7a2e6f8
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-3alt-branch
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352094594 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4
new file mode 100644
index 0000000..3ee6d25
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4
@@ -0,0 +1,2 @@
+566ab53c220a2eafc1212af1a024513230280ab9 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352094764 -0600 reset: moving to c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec cc3e3009134cb88014129fc8858d1101359e5e2f Edward Thomson <ethomson@edwardthomson.com> 1352094815 -0600 commit: trivial-4
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4-branch
new file mode 100644
index 0000000..51f8a92
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-4-branch
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352094830 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec 183310e30fb1499af8c619108ffea4d300b5e778 Edward Thomson <ethomson@edwardthomson.com> 1352094856 -0600 commit: trivial-4-branch
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1
new file mode 100644
index 0000000..1449702
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352096606 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec 4fe93c0ec83eb6305cbace3dace88ecee1b63cb6 Edward Thomson <ethomson@edwardthomson.com> 1352096643 -0600 commit: 5alt-1
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1-branch
new file mode 100644
index 0000000..4cff835
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-1-branch
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352096657 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec 478172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2 Edward Thomson <ethomson@edwardthomson.com> 1352096689 -0600 commit: 5alt-1-branch
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2
new file mode 100644
index 0000000..3ca077b
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2
@@ -0,0 +1,3 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352096711 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec ebc09d0137cfb0c26697aed0109fb943ad906f3f Edward Thomson <ethomson@edwardthomson.com> 1352096764 -0600 commit: existing file
+ebc09d0137cfb0c26697aed0109fb943ad906f3f 3b47b031b3e55ae11e14a05260b1c3ffd6838d55 Edward Thomson <ethomson@edwardthomson.com> 1352096815 -0600 commit: 5alt-2
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2-branch
new file mode 100644
index 0000000..e7bb901
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-5alt-2-branch
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 ebc09d0137cfb0c26697aed0109fb943ad906f3f Edward Thomson <ethomson@edwardthomson.com> 1352096833 -0600 branch: Created from ebc09d0
+ebc09d0137cfb0c26697aed0109fb943ad906f3f f48097eb340dc5a7cae55aabcf1faf4548aa821f Edward Thomson <ethomson@edwardthomson.com> 1352096855 -0600 commit: 5alt-2-branch
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6
new file mode 100644
index 0000000..7c717a2
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6
@@ -0,0 +1,3 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352097371 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec f7c332bd4d4d4b777366cae4d24d1687477576bf Edward Thomson <ethomson@edwardthomson.com> 1352097389 -0600 commit: 6
+f7c332bd4d4d4b777366cae4d24d1687477576bf 99b4f7e4f24470fa06b980bc21f1095c2a9425c0 Edward Thomson <ethomson@edwardthomson.com> 1352097404 -0600 commit: trivial-6
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6-branch
new file mode 100644
index 0000000..715f3ae
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-6-branch
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 f7c332bd4d4d4b777366cae4d24d1687477576bf Edward Thomson <ethomson@edwardthomson.com> 1352097414 -0600 branch: Created from f7c332bd4d4d4b777366cae4d24d1687477576bf
+f7c332bd4d4d4b777366cae4d24d1687477576bf a43150a738849c59376cf30bb2a68348a83c8f48 Edward Thomson <ethomson@edwardthomson.com> 1352097431 -0600 commit: 6-branch
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7
new file mode 100644
index 0000000..a014f17
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7
@@ -0,0 +1,3 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352099765 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec 092ce8682d7f3a2a3a769a6daca58950168ba5c4 Edward Thomson <ethomson@edwardthomson.com> 1352099790 -0600 commit: trivial-7
+092ce8682d7f3a2a3a769a6daca58950168ba5c4 d874671ef5b20184836cb983bb273e5280384d0b Edward Thomson <ethomson@edwardthomson.com> 1352099947 -0600 commit: trivial-7
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7-branch
new file mode 100644
index 0000000..22331d7
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-7-branch
@@ -0,0 +1,5 @@
+0000000000000000000000000000000000000000 092ce8682d7f3a2a3a769a6daca58950168ba5c4 Edward Thomson <ethomson@edwardthomson.com> 1352099799 -0600 branch: Created from HEAD
+092ce8682d7f3a2a3a769a6daca58950168ba5c4 73cbfdc4fe843169e5b2af8dcad03cbf3acf306c Edward Thomson <ethomson@edwardthomson.com> 1352099812 -0600 commit: trivial-7-branch
+73cbfdc4fe843169e5b2af8dcad03cbf3acf306c 092ce8682d7f3a2a3a769a6daca58950168ba5c4 Edward Thomson <ethomson@edwardthomson.com> 1352099874 -0600 reset: moving to 092ce8682d7f3a2a3a769a6daca58950168ba5c4
+092ce8682d7f3a2a3a769a6daca58950168ba5c4 009b9cab6fdac02915a88ecd078b7a792ed802d8 Edward Thomson <ethomson@edwardthomson.com> 1352099921 -0600 commit: removed in 7
+009b9cab6fdac02915a88ecd078b7a792ed802d8 5195a1b480f66691b667f10a9e41e70115a78351 Edward Thomson <ethomson@edwardthomson.com> 1352099927 -0600 commit (amend): trivial-7-branch
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8
new file mode 100644
index 0000000..7670c35
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8
@@ -0,0 +1,3 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352098816 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec 75a811bf6bc57694adb3fe604786f3a4efd1cd1b Edward Thomson <ethomson@edwardthomson.com> 1352098884 -0600 commit: trivial-8
+75a811bf6bc57694adb3fe604786f3a4efd1cd1b 3575826c96a975031d2c14368529cc5c4353a8fd Edward Thomson <ethomson@edwardthomson.com> 1352099000 -0600 commit: trivial-8
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8-branch
new file mode 100644
index 0000000..c4d68ed
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-8-branch
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 75a811bf6bc57694adb3fe604786f3a4efd1cd1b Edward Thomson <ethomson@edwardthomson.com> 1352098947 -0600 branch: Created from HEAD
+75a811bf6bc57694adb3fe604786f3a4efd1cd1b 52d8bc572af2b6d4ee0d5e62ed5d1fbad92210a9 Edward Thomson <ethomson@edwardthomson.com> 1352098979 -0600 commit: trivial-8-branch
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9 b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9
new file mode 100644
index 0000000..09a343b
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9
@@ -0,0 +1,3 @@
+0000000000000000000000000000000000000000 c607fc30883e335def28cd686b51f6cfa02b06ec Edward Thomson <ethomson@edwardthomson.com> 1352100268 -0600 branch: Created from c607fc30883e335def28cd686b51f6cfa02b06ec
+c607fc30883e335def28cd686b51f6cfa02b06ec f0053b8060bb3f0be5cbcc3147a07ece26bf097e Edward Thomson <ethomson@edwardthomson.com> 1352100304 -0600 commit: trivial-9
+f0053b8060bb3f0be5cbcc3147a07ece26bf097e c35dee9bcc0e989f3b0c40f68372a9a51b6c4e6a Edward Thomson <ethomson@edwardthomson.com> 1352100333 -0600 commit: trivial-9
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9-branch b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9-branch
new file mode 100644
index 0000000..1b126fb
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/trivial-9-branch
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 f0053b8060bb3f0be5cbcc3147a07ece26bf097e Edward Thomson <ethomson@edwardthomson.com> 1352100310 -0600 branch: Created from HEAD
+f0053b8060bb3f0be5cbcc3147a07ece26bf097e 13d1be4ea52a6ced1d7a1d832f0ee3c399348e5e Edward Thomson <ethomson@edwardthomson.com> 1352100317 -0600 commit: trivial-9-branch
diff --git a/tests/resources/merge-resolve/.gitted/logs/refs/heads/unrelated b/tests/resources/merge-resolve/.gitted/logs/refs/heads/unrelated
new file mode 100644
index 0000000..a83ffc2
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/logs/refs/heads/unrelated
@@ -0,0 +1 @@
+d6cf6c7741b3316826af1314042550c97ded1d50 55b4e4687e7a0d9ca367016ed930f385d4022e6f Edward Thomson <ethomson@edwardthomson.com> 1358997664 -0600 commit: conflicting changes
diff --git a/tests/resources/merge-resolve/.gitted/modules/submodule/HEAD b/tests/resources/merge-resolve/.gitted/modules/submodule/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/modules/submodule/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/merge-resolve/.gitted/modules/submodule/ORIG_HEAD b/tests/resources/merge-resolve/.gitted/modules/submodule/ORIG_HEAD
new file mode 100644
index 0000000..d1bfcf0
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/modules/submodule/ORIG_HEAD
@@ -0,0 +1 @@
+ae39c77c70cb6bad18bb471912460c4e1ba0f586
diff --git a/tests/resources/merge-resolve/.gitted/modules/submodule/config b/tests/resources/merge-resolve/.gitted/modules/submodule/config
new file mode 100644
index 0000000..575cc85
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/modules/submodule/config
@@ -0,0 +1,15 @@
+[core]
+ repositoryformatversion = 0
+ filemode = false
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../submodule
+ symlinks = false
+ ignorecase = true
+ hideDotFiles = dotGitOnly
+[remote "origin"]
+ url = c:/Temp/TestRepos/submodule
+ fetch = +refs/heads/*:refs/remotes/origin/*
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/merge-resolve/.gitted/modules/submodule/index b/tests/resources/merge-resolve/.gitted/modules/submodule/index
new file mode 100644
index 0000000..e948afb
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/modules/submodule/index
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/modules/submodule/info/exclude b/tests/resources/merge-resolve/.gitted/modules/submodule/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/modules/submodule/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/merge-resolve/.gitted/modules/submodule/objects/18/fae1354bba0a5f1e6a531f9988369142c24a9e b/tests/resources/merge-resolve/.gitted/modules/submodule/objects/18/fae1354bba0a5f1e6a531f9988369142c24a9e
new file mode 100644
index 0000000..fcf1c63
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/modules/submodule/objects/18/fae1354bba0a5f1e6a531f9988369142c24a9e
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/modules/submodule/objects/29/7aa6cd028b3336c7802c7a6f49143da4e1602d b/tests/resources/merge-resolve/.gitted/modules/submodule/objects/29/7aa6cd028b3336c7802c7a6f49143da4e1602d
new file mode 100644
index 0000000..aa9fc50
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/modules/submodule/objects/29/7aa6cd028b3336c7802c7a6f49143da4e1602d
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/modules/submodule/objects/38/6c80dc813b89d719797668f40c1be0a6efa996 b/tests/resources/merge-resolve/.gitted/modules/submodule/objects/38/6c80dc813b89d719797668f40c1be0a6efa996
new file mode 100644
index 0000000..bc9a32e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/modules/submodule/objects/38/6c80dc813b89d719797668f40c1be0a6efa996
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/modules/submodule/objects/ab/435a147bae6d5906ecfd0916a570c4ab3eeea8 b/tests/resources/merge-resolve/.gitted/modules/submodule/objects/ab/435a147bae6d5906ecfd0916a570c4ab3eeea8
new file mode 100644
index 0000000..65a8d75
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/modules/submodule/objects/ab/435a147bae6d5906ecfd0916a570c4ab3eeea8
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/modules/submodule/objects/ad/16e0a7684ea95bf892980a2ee412293ae979cc b/tests/resources/merge-resolve/.gitted/modules/submodule/objects/ad/16e0a7684ea95bf892980a2ee412293ae979cc
new file mode 100644
index 0000000..49e1aaf
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/modules/submodule/objects/ad/16e0a7684ea95bf892980a2ee412293ae979cc
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/modules/submodule/objects/ae/39c77c70cb6bad18bb471912460c4e1ba0f586 b/tests/resources/merge-resolve/.gitted/modules/submodule/objects/ae/39c77c70cb6bad18bb471912460c4e1ba0f586
new file mode 100644
index 0000000..6ceffdd
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/modules/submodule/objects/ae/39c77c70cb6bad18bb471912460c4e1ba0f586
@@ -0,0 +1,2 @@
+x¥ŽË !@=S hfù%Æx±†!ëÅ Æö]kðöò/{k©-âa] xW«s =,lÄP…‰
+Šë#g0KÈêIC¶© –ž–,5ù“1¡– ™9;aãlB«è=×>ô­|h}_{{õMŸe·?º¶þêuž¸·‹6˜Àšˆô"€Úí>:å„ʃ6^Õ¤±Kd \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/modules/submodule/objects/c2/0765f6e24e8bbb63a648d0d11d84da63170190 b/tests/resources/merge-resolve/.gitted/modules/submodule/objects/c2/0765f6e24e8bbb63a648d0d11d84da63170190
new file mode 100644
index 0000000..1478103
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/modules/submodule/objects/c2/0765f6e24e8bbb63a648d0d11d84da63170190
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/modules/submodule/objects/d3/d806a4bef96889117fd7ebac0e3cb5ec152932 b/tests/resources/merge-resolve/.gitted/modules/submodule/objects/d3/d806a4bef96889117fd7ebac0e3cb5ec152932
new file mode 100644
index 0000000..8df72a4
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/modules/submodule/objects/d3/d806a4bef96889117fd7ebac0e3cb5ec152932
@@ -0,0 +1,3 @@
+x¥A
+Â0E]çse¦mÚDÜx½À$™Ò‚é@šâõgpóø¼ÅQs^+·§ZD€ÃÐ[¦a
+,c²G‰sBO#Û ãÀ¡v†ºhGúpIðZ4ïºÁUšý­{^cÑ]çz‰šo@½ÇŽ\çÎèM³-\å ó<BÖt¼æµÌ·ÈA› \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/modules/submodule/objects/f1/065ff5593604072837fecaad3e2e268cb0147b b/tests/resources/merge-resolve/.gitted/modules/submodule/objects/f1/065ff5593604072837fecaad3e2e268cb0147b
new file mode 100644
index 0000000..e0d73e1
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/modules/submodule/objects/f1/065ff5593604072837fecaad3e2e268cb0147b
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/modules/submodule/packed-refs b/tests/resources/merge-resolve/.gitted/modules/submodule/packed-refs
new file mode 100644
index 0000000..992c4ee
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/modules/submodule/packed-refs
@@ -0,0 +1,3 @@
+# pack-refs with: peeled
+297aa6cd028b3336c7802c7a6f49143da4e1602d refs/remotes/origin/master
+ae39c77c70cb6bad18bb471912460c4e1ba0f586 refs/remotes/origin/submodule-branch
diff --git a/tests/resources/merge-resolve/.gitted/modules/submodule/refs/heads/master b/tests/resources/merge-resolve/.gitted/modules/submodule/refs/heads/master
new file mode 100644
index 0000000..fe28256
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/modules/submodule/refs/heads/master
@@ -0,0 +1 @@
+297aa6cd028b3336c7802c7a6f49143da4e1602d
diff --git a/tests/resources/merge-resolve/.gitted/modules/submodule/refs/remotes/origin/HEAD b/tests/resources/merge-resolve/.gitted/modules/submodule/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..6efe28f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/modules/submodule/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/merge-resolve/.gitted/objects/00/5b6fcc8fec71d2550bef8462d169b3c26aa14b b/tests/resources/merge-resolve/.gitted/objects/00/5b6fcc8fec71d2550bef8462d169b3c26aa14b
new file mode 100644
index 0000000..82a8da5
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/00/5b6fcc8fec71d2550bef8462d169b3c26aa14b
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/00/9b9cab6fdac02915a88ecd078b7a792ed802d8 b/tests/resources/merge-resolve/.gitted/objects/00/9b9cab6fdac02915a88ecd078b7a792ed802d8
new file mode 100644
index 0000000..f663a3c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/00/9b9cab6fdac02915a88ecd078b7a792ed802d8
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/00/c7d33f1ffa79d19c2272b370fcaeaadba49c08 b/tests/resources/merge-resolve/.gitted/objects/00/c7d33f1ffa79d19c2272b370fcaeaadba49c08
new file mode 100644
index 0000000..72698dc
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/00/c7d33f1ffa79d19c2272b370fcaeaadba49c08
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/01/f149e1b8f84bd8896aaff6d6b22af88459ded0 b/tests/resources/merge-resolve/.gitted/objects/01/f149e1b8f84bd8896aaff6d6b22af88459ded0
new file mode 100644
index 0000000..aa6336d
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/01/f149e1b8f84bd8896aaff6d6b22af88459ded0
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/02/04a84f822acbf6386b36d33f1f6bc68bbbf858 b/tests/resources/merge-resolve/.gitted/objects/02/04a84f822acbf6386b36d33f1f6bc68bbbf858
new file mode 100644
index 0000000..2f0a0e1
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/02/04a84f822acbf6386b36d33f1f6bc68bbbf858
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/02/251f990ca8e92e7ae61d3426163fa821c64001 b/tests/resources/merge-resolve/.gitted/objects/02/251f990ca8e92e7ae61d3426163fa821c64001
new file mode 100644
index 0000000..d623117
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/02/251f990ca8e92e7ae61d3426163fa821c64001
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/03/21415405cb906c46869919af56d51dbbe5e85c b/tests/resources/merge-resolve/.gitted/objects/03/21415405cb906c46869919af56d51dbbe5e85c
new file mode 100644
index 0000000..277bdcf
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/03/21415405cb906c46869919af56d51dbbe5e85c
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/03/2ebc5ab85d9553bb187d3cd40875ff23a63ed0 b/tests/resources/merge-resolve/.gitted/objects/03/2ebc5ab85d9553bb187d3cd40875ff23a63ed0
new file mode 100644
index 0000000..e5404d8
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/03/2ebc5ab85d9553bb187d3cd40875ff23a63ed0
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/03/b87706555accbf874ccd410dbda01e8e70a67f b/tests/resources/merge-resolve/.gitted/objects/03/b87706555accbf874ccd410dbda01e8e70a67f
new file mode 100644
index 0000000..0befcd7
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/03/b87706555accbf874ccd410dbda01e8e70a67f
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/03/dad1005e5d06d418f50b12e0bcd48ff2306a03 b/tests/resources/merge-resolve/.gitted/objects/03/dad1005e5d06d418f50b12e0bcd48ff2306a03
new file mode 100644
index 0000000..04011a2
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/03/dad1005e5d06d418f50b12e0bcd48ff2306a03
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/05/1ffd7901a442faf56b226161649074f15c7c47 b/tests/resources/merge-resolve/.gitted/objects/05/1ffd7901a442faf56b226161649074f15c7c47
new file mode 100644
index 0000000..65fa689
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/05/1ffd7901a442faf56b226161649074f15c7c47
@@ -0,0 +1 @@
+x+)JMU06·`040031QH,-ÉÏM-JOMLÊIÕ+©(aH:,»:ÎCÉýúô:ÞË ²o>ZC'g$楧¦èfæé&%æ%g€5¬ÎqYôÂeÒZoÇÝÙkÚMíæ2­éÆÔ›X\’ZDPC~^ZNfrIf^:Xéõ›ZHÙŠž1O(_œ,'º× jvn~JfZ&Ä5†&ȚؽùÁ„ +gÆz¬Ÿ¥•4íú3Ž^¨¦¢ÔÜü2 ÜüI{•|þ¹÷ 2m»gÜ˾‹©É1ÖËåüŠ5Ó¿Ü,\“µ})TC)0XÀ¡vÿ‰ùzÖ›¦9–¤×Mü°úÕ…'6óbó#— \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/05/8541fc37114bfc1dddf6bd6bffc7fae5c2e6fe b/tests/resources/merge-resolve/.gitted/objects/05/8541fc37114bfc1dddf6bd6bffc7fae5c2e6fe
new file mode 100644
index 0000000..d79dc30
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/05/8541fc37114bfc1dddf6bd6bffc7fae5c2e6fe
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/05/f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91c b/tests/resources/merge-resolve/.gitted/objects/05/f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91c
new file mode 100644
index 0000000..7b4b152
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/05/f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91c
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/07/a759da919f737221791d542f176ab49c88837f b/tests/resources/merge-resolve/.gitted/objects/07/a759da919f737221791d542f176ab49c88837f
new file mode 100644
index 0000000..a34b6c2
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/07/a759da919f737221791d542f176ab49c88837f
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/07/c514b04698e068892b31c8d352b85813b99c6e b/tests/resources/merge-resolve/.gitted/objects/07/c514b04698e068892b31c8d352b85813b99c6e
new file mode 100644
index 0000000..23ab921
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/07/c514b04698e068892b31c8d352b85813b99c6e
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/09/055301463b7f2f8ee5d368f8ed5c0a40ad8515 b/tests/resources/merge-resolve/.gitted/objects/09/055301463b7f2f8ee5d368f8ed5c0a40ad8515
new file mode 100644
index 0000000..bf5b0fc
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/09/055301463b7f2f8ee5d368f8ed5c0a40ad8515
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/09/17bb159596aea4d295f4857da77e8f96b3c7dc b/tests/resources/merge-resolve/.gitted/objects/09/17bb159596aea4d295f4857da77e8f96b3c7dc
new file mode 100644
index 0000000..9fb640d
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/09/17bb159596aea4d295f4857da77e8f96b3c7dc
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/09/2ce8682d7f3a2a3a769a6daca58950168ba5c4 b/tests/resources/merge-resolve/.gitted/objects/09/2ce8682d7f3a2a3a769a6daca58950168ba5c4
new file mode 100644
index 0000000..b709cf4
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/09/2ce8682d7f3a2a3a769a6daca58950168ba5c4
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/09/3bebf072dd4bbba88833667d6ffe454df199e1 b/tests/resources/merge-resolve/.gitted/objects/09/3bebf072dd4bbba88833667d6ffe454df199e1
new file mode 100644
index 0000000..ae13207
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/09/3bebf072dd4bbba88833667d6ffe454df199e1
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/09/768bed22680cdb0859683fa9677ccc8d5a25c1 b/tests/resources/merge-resolve/.gitted/objects/09/768bed22680cdb0859683fa9677ccc8d5a25c1
new file mode 100644
index 0000000..5f4b4da
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/09/768bed22680cdb0859683fa9677ccc8d5a25c1
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/0a/75d9aac1dc84fb5aa51f7325c0ab53242ddef7 b/tests/resources/merge-resolve/.gitted/objects/0a/75d9aac1dc84fb5aa51f7325c0ab53242ddef7
new file mode 100644
index 0000000..d537734
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/0a/75d9aac1dc84fb5aa51f7325c0ab53242ddef7
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/0c/fd6c54ef6532d862408f562309dc9c74a401e8 b/tests/resources/merge-resolve/.gitted/objects/0c/fd6c54ef6532d862408f562309dc9c74a401e8
new file mode 100644
index 0000000..40f628f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/0c/fd6c54ef6532d862408f562309dc9c74a401e8
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/0d/52e3a556e189ba0948ae56780918011c1b167d b/tests/resources/merge-resolve/.gitted/objects/0d/52e3a556e189ba0948ae56780918011c1b167d
new file mode 100644
index 0000000..4b633e5
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/0d/52e3a556e189ba0948ae56780918011c1b167d
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/0d/872f8e871a30208305978ecbf9e66d864f1638 b/tests/resources/merge-resolve/.gitted/objects/0d/872f8e871a30208305978ecbf9e66d864f1638
new file mode 100644
index 0000000..4cbc18e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/0d/872f8e871a30208305978ecbf9e66d864f1638
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/0e/c5f433959cd46177f745903353efb5be08d151 b/tests/resources/merge-resolve/.gitted/objects/0e/c5f433959cd46177f745903353efb5be08d151
new file mode 100644
index 0000000..1bee56c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/0e/c5f433959cd46177f745903353efb5be08d151
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/0f/3fc5dddc8964b9ac1040d0e957f9eb02d9efb3 b/tests/resources/merge-resolve/.gitted/objects/0f/3fc5dddc8964b9ac1040d0e957f9eb02d9efb3
new file mode 100644
index 0000000..d0ca42d
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/0f/3fc5dddc8964b9ac1040d0e957f9eb02d9efb3
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/11/aeee27ac45a8402c2fd5b875d66dd844e5df00 b/tests/resources/merge-resolve/.gitted/objects/11/aeee27ac45a8402c2fd5b875d66dd844e5df00
new file mode 100644
index 0000000..90e729f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/11/aeee27ac45a8402c2fd5b875d66dd844e5df00
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/11/deab00b2d3a6f5a3073988ac050c2d7b6655e2 b/tests/resources/merge-resolve/.gitted/objects/11/deab00b2d3a6f5a3073988ac050c2d7b6655e2
new file mode 100644
index 0000000..857b236
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/11/deab00b2d3a6f5a3073988ac050c2d7b6655e2
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/11/f4f3c08b737f5fd896cbefa1425ee63b21b2fa b/tests/resources/merge-resolve/.gitted/objects/11/f4f3c08b737f5fd896cbefa1425ee63b21b2fa
new file mode 100644
index 0000000..6555194
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/11/f4f3c08b737f5fd896cbefa1425ee63b21b2fa
@@ -0,0 +1 @@
+x¥ŽQ Dýæ\ fw)cüñ^` Û´‰-Q¯/oàßÌ›Ìdb^×¥j²´«EDC²$†­u‚>Œ ¡÷,Ö z@Œ8¢’ºq‘­jk<Ù©GŽ>¹Òz2Lva2)¸VeÅ:ç¢ÏéÅ%éËœ×{ÞôAý¨“|ƒŸÛǼ5K@ˆº mg«ü9£jYž _;„n,¼ÅY½y²P” \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/13/d1be4ea52a6ced1d7a1d832f0ee3c399348e5e b/tests/resources/merge-resolve/.gitted/objects/13/d1be4ea52a6ced1d7a1d832f0ee3c399348e5e
new file mode 100644
index 0000000..4e4e175
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/13/d1be4ea52a6ced1d7a1d832f0ee3c399348e5e
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/14/39088f509b79b1535b64193137d3ce4b240734 b/tests/resources/merge-resolve/.gitted/objects/14/39088f509b79b1535b64193137d3ce4b240734
new file mode 100644
index 0000000..51ddf6d
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/14/39088f509b79b1535b64193137d3ce4b240734
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/15/8dc7bedb202f5b26502bf3574faa7f4238d56c b/tests/resources/merge-resolve/.gitted/objects/15/8dc7bedb202f5b26502bf3574faa7f4238d56c
new file mode 100644
index 0000000..064423d
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/15/8dc7bedb202f5b26502bf3574faa7f4238d56c
@@ -0,0 +1,2 @@
+x¥ŽK!D]sŠ¾€¦ùCbŒoà i2. b¼¾£ñîªÞK*E½µÛep73UƒÓ¾*NYYôI²Ô”)–j¼ŠL:8§<‹{¼NˆÞ“‹ÎÊH6iDC¶Ê"mqH!–Ì9T¥mé9—>àR^i¸.½=ú
+GÞè'ù+~í@½@j+ƒ7ÑØ£EÝÎNþsFtš]‰7bN) \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/16/f825815cfd20a07a75c71554e82d8eede0b061 b/tests/resources/merge-resolve/.gitted/objects/16/f825815cfd20a07a75c71554e82d8eede0b061
new file mode 100644
index 0000000..82d6525
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/16/f825815cfd20a07a75c71554e82d8eede0b061
@@ -0,0 +1 @@
+x¥ŽK!D]sŠ¾€†šObŒoà Œ ƒ¯ïh¼»ª÷’Jqoí6A¹›£°JÙ€ˆ¬“T1h¢3§£·Î'LÕ ó.‰{eœc,a`ŠZJÃT1#e+Ù‡œJòUiª">çÒ\ò+Ž ×¥·G_áX6úIçò¿vàÞN€šÐ;ÈÀ^’”b£ÛÙYþœgGñ—MMµ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/17/8940b450f238a56c0d75b7955cb57b38191982 b/tests/resources/merge-resolve/.gitted/objects/17/8940b450f238a56c0d75b7955cb57b38191982
new file mode 100644
index 0000000..94e571e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/17/8940b450f238a56c0d75b7955cb57b38191982
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/18/3310e30fb1499af8c619108ffea4d300b5e778 b/tests/resources/merge-resolve/.gitted/objects/18/3310e30fb1499af8c619108ffea4d300b5e778
new file mode 100644
index 0000000..1c4010d
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/18/3310e30fb1499af8c619108ffea4d300b5e778
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/18/cb316b1cefa0f8a6946f0e201a8e1a6f845ab9 b/tests/resources/merge-resolve/.gitted/objects/18/cb316b1cefa0f8a6946f0e201a8e1a6f845ab9
new file mode 100644
index 0000000..30f3110
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/18/cb316b1cefa0f8a6946f0e201a8e1a6f845ab9
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/19/b7ac485269b672a101060894de3ba9c2a24dd1 b/tests/resources/merge-resolve/.gitted/objects/19/b7ac485269b672a101060894de3ba9c2a24dd1
new file mode 100644
index 0000000..e34ccb8
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/19/b7ac485269b672a101060894de3ba9c2a24dd1
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/1a/010b1c0f081b2e8901d55307a15c29ff30af0e b/tests/resources/merge-resolve/.gitted/objects/1a/010b1c0f081b2e8901d55307a15c29ff30af0e
new file mode 100644
index 0000000..6039df0
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/1a/010b1c0f081b2e8901d55307a15c29ff30af0e
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/1c/51d885170f57a0c4e8c69ff6363d91a5b51f85 b/tests/resources/merge-resolve/.gitted/objects/1c/51d885170f57a0c4e8c69ff6363d91a5b51f85
new file mode 100644
index 0000000..9a21e26
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/1c/51d885170f57a0c4e8c69ff6363d91a5b51f85
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/1c/ff9ec6a47a537380dedfdd17c9e76d74259a2b b/tests/resources/merge-resolve/.gitted/objects/1c/ff9ec6a47a537380dedfdd17c9e76d74259a2b
new file mode 100644
index 0000000..30802bc
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/1c/ff9ec6a47a537380dedfdd17c9e76d74259a2b
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/1e/4ff029aee68d0d69ef9eb6efa6cbf1ec732f99 b/tests/resources/merge-resolve/.gitted/objects/1e/4ff029aee68d0d69ef9eb6efa6cbf1ec732f99
new file mode 100644
index 0000000..5183b83
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/1e/4ff029aee68d0d69ef9eb6efa6cbf1ec732f99
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/1f/81433e3161efbf250576c58fede7f6b836f3d3 b/tests/resources/merge-resolve/.gitted/objects/1f/81433e3161efbf250576c58fede7f6b836f3d3
new file mode 100644
index 0000000..9708556
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/1f/81433e3161efbf250576c58fede7f6b836f3d3
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/20/91d94c8bd3eb0835dc5220de5e8bb310fa1513 b/tests/resources/merge-resolve/.gitted/objects/20/91d94c8bd3eb0835dc5220de5e8bb310fa1513
new file mode 100644
index 0000000..a843890
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/20/91d94c8bd3eb0835dc5220de5e8bb310fa1513
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/21/671e290278286fb2ce4c63d01699b67adce331 b/tests/resources/merge-resolve/.gitted/objects/21/671e290278286fb2ce4c63d01699b67adce331
new file mode 100644
index 0000000..b656d00
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/21/671e290278286fb2ce4c63d01699b67adce331
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/22/7792b52aaa0b238bea00ec7e509b02623f168c b/tests/resources/merge-resolve/.gitted/objects/22/7792b52aaa0b238bea00ec7e509b02623f168c
new file mode 100644
index 0000000..3bb19bb
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/22/7792b52aaa0b238bea00ec7e509b02623f168c
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/23/3c0919c998ed110a4b6ff36f353aec8b713487 b/tests/resources/merge-resolve/.gitted/objects/23/3c0919c998ed110a4b6ff36f353aec8b713487
new file mode 100644
index 0000000..d0c8c9e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/23/3c0919c998ed110a4b6ff36f353aec8b713487
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/23/92a2dacc9efb562b8635d6579fb458751c7c5b b/tests/resources/merge-resolve/.gitted/objects/23/92a2dacc9efb562b8635d6579fb458751c7c5b
new file mode 100644
index 0000000..86127a3
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/23/92a2dacc9efb562b8635d6579fb458751c7c5b
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/23/ed141a6ae1e798b2f721afedbe947c119111ba b/tests/resources/merge-resolve/.gitted/objects/23/ed141a6ae1e798b2f721afedbe947c119111ba
new file mode 100644
index 0000000..06dee3b
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/23/ed141a6ae1e798b2f721afedbe947c119111ba
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/24/1a1005cd9b980732741b74385b891142bcba28 b/tests/resources/merge-resolve/.gitted/objects/24/1a1005cd9b980732741b74385b891142bcba28
new file mode 100644
index 0000000..9b65f66
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/24/1a1005cd9b980732741b74385b891142bcba28
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/24/2591eb280ee9eeb2ce63524b9a8b9bc4cb515d b/tests/resources/merge-resolve/.gitted/objects/24/2591eb280ee9eeb2ce63524b9a8b9bc4cb515d
new file mode 100644
index 0000000..74a0137
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/24/2591eb280ee9eeb2ce63524b9a8b9bc4cb515d
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/24/90b9f1a079420870027deefb49f51d6656cf74 b/tests/resources/merge-resolve/.gitted/objects/24/90b9f1a079420870027deefb49f51d6656cf74
new file mode 100644
index 0000000..60497ca
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/24/90b9f1a079420870027deefb49f51d6656cf74
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/25/9d08ca43af9200e9ea9a098e44a5a350ebd9b3 b/tests/resources/merge-resolve/.gitted/objects/25/9d08ca43af9200e9ea9a098e44a5a350ebd9b3
new file mode 100644
index 0000000..2bae669
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/25/9d08ca43af9200e9ea9a098e44a5a350ebd9b3
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/25/c40b7660c08c8fb581f770312f41b9b03119d1 b/tests/resources/merge-resolve/.gitted/objects/25/c40b7660c08c8fb581f770312f41b9b03119d1
new file mode 100644
index 0000000..1852147
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/25/c40b7660c08c8fb581f770312f41b9b03119d1
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/26/153a3ff3649b6c2bb652d3f06878c6e0a172f9 b/tests/resources/merge-resolve/.gitted/objects/26/153a3ff3649b6c2bb652d3f06878c6e0a172f9
new file mode 100644
index 0000000..4fcaa07
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/26/153a3ff3649b6c2bb652d3f06878c6e0a172f9
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/27/133da702ba3c60af2a01e96c2555ff4045d692 b/tests/resources/merge-resolve/.gitted/objects/27/133da702ba3c60af2a01e96c2555ff4045d692
new file mode 100644
index 0000000..08e61f8
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/27/133da702ba3c60af2a01e96c2555ff4045d692
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/27/4bbe983022fb4c02f8a2bf2ebe8da4fe130054 b/tests/resources/merge-resolve/.gitted/objects/27/4bbe983022fb4c02f8a2bf2ebe8da4fe130054
new file mode 100644
index 0000000..c7afad2
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/27/4bbe983022fb4c02f8a2bf2ebe8da4fe130054
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/2a/f2d9bcbc73723ac988bb202d4397f72a6ca7a0 b/tests/resources/merge-resolve/.gitted/objects/2a/f2d9bcbc73723ac988bb202d4397f72a6ca7a0
new file mode 100644
index 0000000..0d65823
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/2a/f2d9bcbc73723ac988bb202d4397f72a6ca7a0
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/2b/0de5dc27505dcdd83a75c8bf1fcd9462cd7add b/tests/resources/merge-resolve/.gitted/objects/2b/0de5dc27505dcdd83a75c8bf1fcd9462cd7add
new file mode 100644
index 0000000..a95f926
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/2b/0de5dc27505dcdd83a75c8bf1fcd9462cd7add
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/2b/5f1f181ee3b58ea751f5dd5d8f9b445520a136 b/tests/resources/merge-resolve/.gitted/objects/2b/5f1f181ee3b58ea751f5dd5d8f9b445520a136
new file mode 100644
index 0000000..d24231e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/2b/5f1f181ee3b58ea751f5dd5d8f9b445520a136
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863 b/tests/resources/merge-resolve/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863
new file mode 100644
index 0000000..d10ca63
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/2b/fdd7e1b6c6ae993f23dfe8e84a8e06a772fa2a b/tests/resources/merge-resolve/.gitted/objects/2b/fdd7e1b6c6ae993f23dfe8e84a8e06a772fa2a
new file mode 100644
index 0000000..c86edfb
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/2b/fdd7e1b6c6ae993f23dfe8e84a8e06a772fa2a
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/2d/a538570bc1e5b2c3e855bf702f35248ad0735f b/tests/resources/merge-resolve/.gitted/objects/2d/a538570bc1e5b2c3e855bf702f35248ad0735f
new file mode 100644
index 0000000..83253f8
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/2d/a538570bc1e5b2c3e855bf702f35248ad0735f
@@ -0,0 +1,2 @@
+x¥ŽK
+1D]ç¹€ÒùN"n¼{ét:Œ‹L$ñúŽâ ÜU½EQ«õ>¤¶~7:³L ðÙD [´±5—¬É‡,Øy2e®ÐTت@”¦z*.û(ë´Àç˜[——üžåunum‹<òF?éÌ_ñkjõ$•qNå'#÷àÄF·³ƒÿœ¹Üp!^Gëâ 9+Q. \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/2f/2e37b7ebbae467978610896ca3aafcdad2ee67 b/tests/resources/merge-resolve/.gitted/objects/2f/2e37b7ebbae467978610896ca3aafcdad2ee67
new file mode 100644
index 0000000..7adffb1
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/2f/2e37b7ebbae467978610896ca3aafcdad2ee67
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/2f/4024ce528d36d8670c289cce5a7963e625bb0c b/tests/resources/merge-resolve/.gitted/objects/2f/4024ce528d36d8670c289cce5a7963e625bb0c
new file mode 100644
index 0000000..0100fd7
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/2f/4024ce528d36d8670c289cce5a7963e625bb0c
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/2f/56120107d680129a5d9791b521cb1e73a2ed31 b/tests/resources/merge-resolve/.gitted/objects/2f/56120107d680129a5d9791b521cb1e73a2ed31
new file mode 100644
index 0000000..1f5f597
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/2f/56120107d680129a5d9791b521cb1e73a2ed31
@@ -0,0 +1,3 @@
+xí[
+Â@ EýžUdR臸žy¤öBÌ”LJ·oÔnBð/pî#Ü"½Ðí>_|Å Ât@„
+“ñapg%hò•aãJYÁÕ®Aâ8Õ©í› fçà²ûN©ì¦¯4À;œ”h[º%cÍO¦ÆÎÕÑuJÑ÷çWÖyÎ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/2f/598248eeccfc27e5ca44d9d96383f6dfea7b16 b/tests/resources/merge-resolve/.gitted/objects/2f/598248eeccfc27e5ca44d9d96383f6dfea7b16
new file mode 100644
index 0000000..1d9f226
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/2f/598248eeccfc27e5ca44d9d96383f6dfea7b16
@@ -0,0 +1 @@
+x+)JMU067c040031QH,-ÉÏM-JOMLÊIÕ+©(aH:,»:ÎCÉýúô:ÞË ²o>ZC'g$楧¦èfæé&%æ%g€5¬ÎqYôÂeÒZoÇÝÙkÚMíæ2­éÆÔ›X\’ZDPC~^ZNfrIf^:Xéõ›ZHÙŠž1O(_œ,'º× jvQjn~Ä1–ÈÎÑ×3ßþzדôém9‹Wý¹ué]:¦$÷ßüI{•|þ¹÷ 2m»gÜ˾‹©Éý1ÖËåüŠ5Ó¿Ü,\“µ})TC)0PÀavý‰ùzÖ›¦9–¤×Mü°úÕ…'6óbˆ—¢ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/31/68dca1a561889b045a6441909f4c56145e666d b/tests/resources/merge-resolve/.gitted/objects/31/68dca1a561889b045a6441909f4c56145e666d
new file mode 100644
index 0000000..2de1c5a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/31/68dca1a561889b045a6441909f4c56145e666d
@@ -0,0 +1,2 @@
+x¥ŽQ
+Â0DýÎ)rJ²›MñÇxM²¥ÛHŒz}«xÿfÞƒaRY–¹ip´kUD $Ï1f¢Q2qê-Ó=Y£ëÁ3R7®²6äÄ¡·Œgàâ9e7 bæ¡w ‚âG›JÕçüâšõe*˽¬ú ý¤“|ůíSYŽÚ"5&Ðñƨng›ü9£ZŸ3_;kÕ¬dO¼ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/31/d5472536041a83d986829240bbbdc897c6f8a6 b/tests/resources/merge-resolve/.gitted/objects/31/d5472536041a83d986829240bbbdc897c6f8a6
new file mode 100644
index 0000000..5ec5acb
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/31/d5472536041a83d986829240bbbdc897c6f8a6
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/32/21dd512b7e2dc4b5bd03046df6c81b2ab2070b b/tests/resources/merge-resolve/.gitted/objects/32/21dd512b7e2dc4b5bd03046df6c81b2ab2070b
new file mode 100644
index 0000000..d36138d
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/32/21dd512b7e2dc4b5bd03046df6c81b2ab2070b
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/33/46d64325b39e5323733492cd55f808994a2475 b/tests/resources/merge-resolve/.gitted/objects/33/46d64325b39e5323733492cd55f808994a2475
new file mode 100644
index 0000000..11546ce
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/33/46d64325b39e5323733492cd55f808994a2475
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/33/d500f588fbbe65901d82b4e6b008e549064be0 b/tests/resources/merge-resolve/.gitted/objects/33/d500f588fbbe65901d82b4e6b008e549064be0
new file mode 100644
index 0000000..061a031
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/33/d500f588fbbe65901d82b4e6b008e549064be0
@@ -0,0 +1,2 @@
+x¥ŽA E]s
+. hbŒoত.Z Åx}[ã Üýÿ~òó¤Îó£këü¡·œµCAŒž<:km`d”„Ì‘d,!:𦳤žÜòÒõ‚øÁ“„œP1ÉPò qHcc±HEñ«Oµé[zsKú>Õy­‹>çî隿ï¤ÎmŒŠú 6ºÉöüç*¼öRÛn¢>úåOÇ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/34/8dcd41e2b467991578e92bedd16971b877ef1e b/tests/resources/merge-resolve/.gitted/objects/34/8dcd41e2b467991578e92bedd16971b877ef1e
new file mode 100644
index 0000000..fd61b6c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/34/8dcd41e2b467991578e92bedd16971b877ef1e
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/34/bfafff88eaf118402b44e6f3e2dbbf1a582b05 b/tests/resources/merge-resolve/.gitted/objects/34/bfafff88eaf118402b44e6f3e2dbbf1a582b05
new file mode 100644
index 0000000..c653cec
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/34/bfafff88eaf118402b44e6f3e2dbbf1a582b05
@@ -0,0 +1 @@
+x•ŽKj1D½Ö)ú úµ>`Œ7¾A. µ$<`MŒ¯orlŠ¢ ¸÷m‚¶þ4G­›·&údVdƒ[j2ÖµËJÉâ›C«ÑŠgu_GuÒ%ÅÚ2:ƒ3XúزÅàQ‘'Ì"½æÜÊ;?wîïp®kým×¾ÑàƒÛü&îPf!¢ ð%QJ±Ö%:ëûCˆeœzâ½=6šÀ¯qˆ;iOè \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/35/0c6eb3010efc403a6bed682332635314e9ed58 b/tests/resources/merge-resolve/.gitted/objects/35/0c6eb3010efc403a6bed682332635314e9ed58
new file mode 100644
index 0000000..2eee602
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/35/0c6eb3010efc403a6bed682332635314e9ed58
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/35/411bfb77cd2cc431f3a03a2b4976ed94b5d241 b/tests/resources/merge-resolve/.gitted/objects/35/411bfb77cd2cc431f3a03a2b4976ed94b5d241
new file mode 100644
index 0000000..ea024cc
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/35/411bfb77cd2cc431f3a03a2b4976ed94b5d241
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/35/4704d3613ad4228e4786fc76656b11e98236c4 b/tests/resources/merge-resolve/.gitted/objects/35/4704d3613ad4228e4786fc76656b11e98236c4
new file mode 100644
index 0000000..1dd13c4
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/35/4704d3613ad4228e4786fc76656b11e98236c4
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/35/632e43612c06a3ea924bfbacd48333da874c29 b/tests/resources/merge-resolve/.gitted/objects/35/632e43612c06a3ea924bfbacd48333da874c29
new file mode 100644
index 0000000..be7684f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/35/632e43612c06a3ea924bfbacd48333da874c29
@@ -0,0 +1 @@
+x¥NË !õLÓÀšd–MŒñb60ÀÝă¨í‹Æ¼½^,ëº40;·iUFf+)›³ˆ1vòBÁ939fG–(ôDIݸʵA$s´è½k]’l|Lä{IgŠ™Ñ$‰Šm.NéÅ5Áy.ë½\a/]ý £|ÛƲ@[g4âä< Hˆª«ýl“?gT«ËsáË µzCøP˜ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/35/75826c96a975031d2c14368529cc5c4353a8fd b/tests/resources/merge-resolve/.gitted/objects/35/75826c96a975031d2c14368529cc5c4353a8fd
new file mode 100644
index 0000000..24e33bc
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/35/75826c96a975031d2c14368529cc5c4353a8fd
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/36/219b49367146cb2e6a1555b5a9ebd4d0328495 b/tests/resources/merge-resolve/.gitted/objects/36/219b49367146cb2e6a1555b5a9ebd4d0328495
new file mode 100644
index 0000000..7f80443
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/36/219b49367146cb2e6a1555b5a9ebd4d0328495
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/36/4bbe4ce80c7bd31e6307dce77d46e3e1759fb3 b/tests/resources/merge-resolve/.gitted/objects/36/4bbe4ce80c7bd31e6307dce77d46e3e1759fb3
new file mode 100644
index 0000000..90fd965
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/36/4bbe4ce80c7bd31e6307dce77d46e3e1759fb3
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/37/48859b001c6e627e712a07951aee40afd19b41 b/tests/resources/merge-resolve/.gitted/objects/37/48859b001c6e627e712a07951aee40afd19b41
new file mode 100644
index 0000000..6a0c389
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/37/48859b001c6e627e712a07951aee40afd19b41
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/37/f53a5a14f64e91089a39ea58e71c87d81df765 b/tests/resources/merge-resolve/.gitted/objects/37/f53a5a14f64e91089a39ea58e71c87d81df765
new file mode 100644
index 0000000..95327ed
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/37/f53a5a14f64e91089a39ea58e71c87d81df765
@@ -0,0 +1 @@
+x•ŽAjC1 D»ö)´Ù‘e}%zƒ^Àß–ã,þwprý: tVÃÀ›™Ô¶í6ÀÃÇèªÀRBB‡Lh—,‹ª·9„À+‹%r¹Ç®û€h1¨•è¥%£p’"Åæ”=¥ÙâIue£¶ßù{†ŸÚ¶ß¶ÃIgúrçëmÔÇú™Úö–„ÙÏ-œ23/‡þ“'|óæRã~U˜‹k{ªæ£dJï \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/38/5c8a0f26ddf79e9041e15e17dc352ed2c4cced b/tests/resources/merge-resolve/.gitted/objects/38/5c8a0f26ddf79e9041e15e17dc352ed2c4cced
new file mode 100644
index 0000000..e95ff3a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/38/5c8a0f26ddf79e9041e15e17dc352ed2c4cced
@@ -0,0 +1,2 @@
+x-MK
+1 uS¼YÍRÄ…ñ™6C±…6Ò뛪ð¼okn÷ÇåYt àEpª iÅDûØCd§Éœ³dLõB+8ø%¨q‚±ƒk µÿú+Þe™Þ6¢ï‡©fHüBü·¯1J©ÕÓ4ùFà1l \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/3b/47b031b3e55ae11e14a05260b1c3ffd6838d55 b/tests/resources/merge-resolve/.gitted/objects/3b/47b031b3e55ae11e14a05260b1c3ffd6838d55
new file mode 100644
index 0000000..8208646
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/3b/47b031b3e55ae11e14a05260b1c3ffd6838d55
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/3b/bf0bf59b20df5d5fc58b9fc1dc07be637c301f b/tests/resources/merge-resolve/.gitted/objects/3b/bf0bf59b20df5d5fc58b9fc1dc07be637c301f
new file mode 100644
index 0000000..723a9ae
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/3b/bf0bf59b20df5d5fc58b9fc1dc07be637c301f
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/3e/f4d30382ca33fdeba9fda895a99e0891ba37aa b/tests/resources/merge-resolve/.gitted/objects/3e/f4d30382ca33fdeba9fda895a99e0891ba37aa
new file mode 100644
index 0000000..49ee152
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/3e/f4d30382ca33fdeba9fda895a99e0891ba37aa
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/3e/f9bfe82f9635518ae89152322f3b46fd4ba25b b/tests/resources/merge-resolve/.gitted/objects/3e/f9bfe82f9635518ae89152322f3b46fd4ba25b
new file mode 100644
index 0000000..3b5998c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/3e/f9bfe82f9635518ae89152322f3b46fd4ba25b
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/40/2784a46a4a3982294231594cbeb431f506d22c b/tests/resources/merge-resolve/.gitted/objects/40/2784a46a4a3982294231594cbeb431f506d22c
new file mode 100644
index 0000000..a17e05d
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/40/2784a46a4a3982294231594cbeb431f506d22c
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/41/2b32fb66137366147f1801ecc962452757d48a b/tests/resources/merge-resolve/.gitted/objects/41/2b32fb66137366147f1801ecc962452757d48a
new file mode 100644
index 0000000..b183dd7
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/41/2b32fb66137366147f1801ecc962452757d48a
@@ -0,0 +1,2 @@
+x¥ŽK
+1D]ç¹€ÒIO>"n¼èt:Œ‹™‰x}£xwU¯ xÜj½um'»ë«ˆ.®Ì9»=y 6Ø$@T8ÌÀ‰&Lhf4êA«Ü»f¡0BŒ(ˆ.K±‘³>9S<›À +zö¥­ú’_´f}]ZÝÚ]eÐO:Ëwøµ·zÒš†‹ÞƒPƒÙ.Þ¨aNU6õÎOÖ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/42/18670ab81cc219a9f94befb5c5dad90ec52648 b/tests/resources/merge-resolve/.gitted/objects/42/18670ab81cc219a9f94befb5c5dad90ec52648
new file mode 100644
index 0000000..33ead61
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/42/18670ab81cc219a9f94befb5c5dad90ec52648
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/43/aafd43bea779ec74317dc361f45ae3f532a505 b/tests/resources/merge-resolve/.gitted/objects/43/aafd43bea779ec74317dc361f45ae3f532a505
new file mode 100644
index 0000000..ac86823
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/43/aafd43bea779ec74317dc361f45ae3f532a505
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/43/c338656342227a3a3cd3aa85cbf784061f5425 b/tests/resources/merge-resolve/.gitted/objects/43/c338656342227a3a3cd3aa85cbf784061f5425
new file mode 100644
index 0000000..d977311
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/43/c338656342227a3a3cd3aa85cbf784061f5425
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/45/299c1ca5e07bba1fd90843056fb559f96b1f5a b/tests/resources/merge-resolve/.gitted/objects/45/299c1ca5e07bba1fd90843056fb559f96b1f5a
new file mode 100644
index 0000000..2093b44
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/45/299c1ca5e07bba1fd90843056fb559f96b1f5a
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/46/6daf8552b891e5c22bc58c9d7fc1a2eb8f0289 b/tests/resources/merge-resolve/.gitted/objects/46/6daf8552b891e5c22bc58c9d7fc1a2eb8f0289
new file mode 100644
index 0000000..c39b53a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/46/6daf8552b891e5c22bc58c9d7fc1a2eb8f0289
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/47/6dbb3e207313d1d8aaa120c6ad204bf1295e53 b/tests/resources/merge-resolve/.gitted/objects/47/6dbb3e207313d1d8aaa120c6ad204bf1295e53
new file mode 100644
index 0000000..3e5f66e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/47/6dbb3e207313d1d8aaa120c6ad204bf1295e53
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/47/8172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2 b/tests/resources/merge-resolve/.gitted/objects/47/8172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2
new file mode 100644
index 0000000..d9e250e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/47/8172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/49/130a28ef567af9a6a6104c38773fedfa5f9742 b/tests/resources/merge-resolve/.gitted/objects/49/130a28ef567af9a6a6104c38773fedfa5f9742
new file mode 100644
index 0000000..e2c49f5
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/49/130a28ef567af9a6a6104c38773fedfa5f9742
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/49/9df817155e4bdd3c6ee192a72c52f481818230 b/tests/resources/merge-resolve/.gitted/objects/49/9df817155e4bdd3c6ee192a72c52f481818230
new file mode 100644
index 0000000..9c7e471
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/49/9df817155e4bdd3c6ee192a72c52f481818230
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/49/fd9edac79d15c8fbfca2d481cbb900beba22a6 b/tests/resources/merge-resolve/.gitted/objects/49/fd9edac79d15c8fbfca2d481cbb900beba22a6
new file mode 100644
index 0000000..d808d9f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/49/fd9edac79d15c8fbfca2d481cbb900beba22a6
@@ -0,0 +1,3 @@
+xUÁ
+Â0D=ç+æ¼èÅ
+‚…þÆf»5dIŽ~¼ÑEhße3Ìó©xœ¯—ÓÍÀ2?ž®‡Ø°Å$Éô%+¢"SëRAºÂWRîm Kýn¸ä\t XZ/µŸhôº¥ÈÝMƱߙìþg2©»ñj># \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/4a/9550ebcc97ce22b22f45af7b829bb030d003f5 b/tests/resources/merge-resolve/.gitted/objects/4a/9550ebcc97ce22b22f45af7b829bb030d003f5
new file mode 100644
index 0000000..6ec674a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/4a/9550ebcc97ce22b22f45af7b829bb030d003f5
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/4b/253da36a0ae8bfce63aeabd8c5b58429925594 b/tests/resources/merge-resolve/.gitted/objects/4b/253da36a0ae8bfce63aeabd8c5b58429925594
new file mode 100644
index 0000000..1a40727
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/4b/253da36a0ae8bfce63aeabd8c5b58429925594
@@ -0,0 +1,2 @@
+x ÅA
+€0 @ÏûŠ¾AðAILm l¡¦ÿ×¹ŒæÔvGxÑ#ÿ6Ù3¬‚tW BØ6—§”ßÐ%´h” \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/4b/48deed3a433909bfd6b6ab3d4b91348b6af464 b/tests/resources/merge-resolve/.gitted/objects/4b/48deed3a433909bfd6b6ab3d4b91348b6af464
new file mode 100644
index 0000000..328c850
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/4b/48deed3a433909bfd6b6ab3d4b91348b6af464
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 b/tests/resources/merge-resolve/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904
new file mode 100644
index 0000000..adf6411
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/4c/9fac0707f8d4195037ae5a681aa48626491541 b/tests/resources/merge-resolve/.gitted/objects/4c/9fac0707f8d4195037ae5a681aa48626491541
new file mode 100644
index 0000000..6b8c85e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/4c/9fac0707f8d4195037ae5a681aa48626491541
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/4c/a408a8c88655f7586a1b580be6fad138121e98 b/tests/resources/merge-resolve/.gitted/objects/4c/a408a8c88655f7586a1b580be6fad138121e98
new file mode 100644
index 0000000..15cb7f2
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/4c/a408a8c88655f7586a1b580be6fad138121e98
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/4d/d1ef7569b18d92d93c0a35bb6b93049137b355 b/tests/resources/merge-resolve/.gitted/objects/4d/d1ef7569b18d92d93c0a35bb6b93049137b355
new file mode 100644
index 0000000..86a21ad
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/4d/d1ef7569b18d92d93c0a35bb6b93049137b355
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/4e/0d9401aee78eb345a8685a859d37c8c3c0bbed b/tests/resources/merge-resolve/.gitted/objects/4e/0d9401aee78eb345a8685a859d37c8c3c0bbed
new file mode 100644
index 0000000..57f7eb6
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/4e/0d9401aee78eb345a8685a859d37c8c3c0bbed
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/4e/886e602529caa9ab11d71f86634bd1b6e0de10 b/tests/resources/merge-resolve/.gitted/objects/4e/886e602529caa9ab11d71f86634bd1b6e0de10
new file mode 100644
index 0000000..53168a0
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/4e/886e602529caa9ab11d71f86634bd1b6e0de10
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/4e/b04c9e79e88f6640d01ff5b25ca2a60764f216 b/tests/resources/merge-resolve/.gitted/objects/4e/b04c9e79e88f6640d01ff5b25ca2a60764f216
new file mode 100644
index 0000000..f4ec0ef
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/4e/b04c9e79e88f6640d01ff5b25ca2a60764f216
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/4f/e93c0ec83eb6305cbace3dace88ecee1b63cb6 b/tests/resources/merge-resolve/.gitted/objects/4f/e93c0ec83eb6305cbace3dace88ecee1b63cb6
new file mode 100644
index 0000000..67dc684
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/4f/e93c0ec83eb6305cbace3dace88ecee1b63cb6
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/50/12fd565b1393bdfda1805d4ec38ce6619e1fd1 b/tests/resources/merge-resolve/.gitted/objects/50/12fd565b1393bdfda1805d4ec38ce6619e1fd1
new file mode 100644
index 0000000..d629a23
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/50/12fd565b1393bdfda1805d4ec38ce6619e1fd1
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/50/4f75ac95a71ef98051817618576a68505b92f9 b/tests/resources/merge-resolve/.gitted/objects/50/4f75ac95a71ef98051817618576a68505b92f9
new file mode 100644
index 0000000..1b24c72
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/50/4f75ac95a71ef98051817618576a68505b92f9
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/50/84fc2a88b6bdba8db93bd3953a8f4fdb470238 b/tests/resources/merge-resolve/.gitted/objects/50/84fc2a88b6bdba8db93bd3953a8f4fdb470238
new file mode 100644
index 0000000..84c9987
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/50/84fc2a88b6bdba8db93bd3953a8f4fdb470238
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/50/c5dc8cdfe40c688eb0a0e23be54dd57cae2e78 b/tests/resources/merge-resolve/.gitted/objects/50/c5dc8cdfe40c688eb0a0e23be54dd57cae2e78
new file mode 100644
index 0000000..c04baa1
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/50/c5dc8cdfe40c688eb0a0e23be54dd57cae2e78
@@ -0,0 +1,2 @@
+xŽ]
+Â0„}Î)öJ’vó">x/°I6ZhIÓžß*žÀ§a†ïƒ‰eš†ZûC«Ì`‰œF‡6;KL¶O)«.yìÑ8¢N¦‡^¼¨òÜ ƒ¡Ç}óa'¤ôÑ° öSØù˜Ù*›²´¶g©p£mHp_ëÌÎsÙh–ë/O¥>. Pi…FúŽ¥ñû±íÂ?¶¨,k˜JZGo»¾JT \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/50/ce7d7d01217679e26c55939eef119e0c93e272 b/tests/resources/merge-resolve/.gitted/objects/50/ce7d7d01217679e26c55939eef119e0c93e272
new file mode 100644
index 0000000..e2f9f67
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/50/ce7d7d01217679e26c55939eef119e0c93e272
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/51/95a1b480f66691b667f10a9e41e70115a78351 b/tests/resources/merge-resolve/.gitted/objects/51/95a1b480f66691b667f10a9e41e70115a78351
new file mode 100644
index 0000000..088ee54
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/51/95a1b480f66691b667f10a9e41e70115a78351
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/52/d8bc572af2b6d4ee0d5e62ed5d1fbad92210a9 b/tests/resources/merge-resolve/.gitted/objects/52/d8bc572af2b6d4ee0d5e62ed5d1fbad92210a9
new file mode 100644
index 0000000..6522209
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/52/d8bc572af2b6d4ee0d5e62ed5d1fbad92210a9
@@ -0,0 +1,3 @@
+x¥Ž]
+Â0„}Î)rÊnóÓD|ñ^`“ÝЂm$F½¾U¼o3ßä²,sÓ½Ã]«" #€ÇàÙbÈ"ö1± 9÷<Q7ª²6=8
+ˆ1û˜ÜàGKMv>²’cTôhS©úÌ/ª¬/SYîeÕÙè'ä+~mŸÊrÔh\c‡QwàÔF·³MþœQ­ÎÏ™®]èb¥5Mê ¨ÚRŸ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/53/825f41ac8d640612f9423a2f03a69f3d96809a b/tests/resources/merge-resolve/.gitted/objects/53/825f41ac8d640612f9423a2f03a69f3d96809a
new file mode 100644
index 0000000..08cb0b6
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/53/825f41ac8d640612f9423a2f03a69f3d96809a
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/54/269b3f6ec3d7d4ede24dd350dd5d605495c3ae b/tests/resources/merge-resolve/.gitted/objects/54/269b3f6ec3d7d4ede24dd350dd5d605495c3ae
new file mode 100644
index 0000000..4a24153
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/54/269b3f6ec3d7d4ede24dd350dd5d605495c3ae
@@ -0,0 +1,2 @@
+x¥ŽK
+1D]ç¹€ÒI¦ó7ÞÀ LºÓŒ‹L$F¼¾£xwUïAQÔj½ m'Ø^Šv9 dÁ”-° £Æœ„ „\¼ äÀˆºÏ½¬C§È'&Î`"ÃÙÅĹä(Ö¡¨ù9–Öõ…_sg}]Z}´UËF?é\¾â×ÔêI‡&ëô@mt;;ÊŸ3ªÑh“z“ÎNÀ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/54/59c89aa0026d543ce8343bd89871bce543f9c2 b/tests/resources/merge-resolve/.gitted/objects/54/59c89aa0026d543ce8343bd89871bce543f9c2
new file mode 100644
index 0000000..178b833
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/54/59c89aa0026d543ce8343bd89871bce543f9c2
@@ -0,0 +1,3 @@
+x¥ŽÑ !Dý¦
+Ð,,,ãØÀ,9O blßÓØ3ï%“ÉmY.C[L›ÑEtd–`\ %aBH%TƒvB G%QphÐqªêÎ]nCg‚P3BŒ(ˆ¾Hµ1Š4yS)W;IVüsëúT^Ü‹>Ïmy´›ÞËJ?é(_ñk»Ü–ƒ6è-$ç#è-€ZézvÈŸ3
+ù:ÔNqMB \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/54/7607c690372fe81fab8e3bb44c530e129118fd b/tests/resources/merge-resolve/.gitted/objects/54/7607c690372fe81fab8e3bb44c530e129118fd
new file mode 100644
index 0000000..dccd220
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/54/7607c690372fe81fab8e3bb44c530e129118fd
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/55/b4e4687e7a0d9ca367016ed930f385d4022e6f b/tests/resources/merge-resolve/.gitted/objects/55/b4e4687e7a0d9ca367016ed930f385d4022e6f
new file mode 100644
index 0000000..fb157a2
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/55/b4e4687e7a0d9ca367016ed930f385d4022e6f
@@ -0,0 +1 @@
+x¥ŽQjÃ0DûíSìZv¥ÕJ‚ú“ä²´Š µ•\¿Né ú7ó“û¶­Œã·±«‚Ôd-{á¹LìXçÀÖ'ÅŒ ÊçéžvmŠä*Ù{¦ÙZ’`$U²ÄÈÆ9ÌÑ-TNé{,}‡Ky¦½ÀuéÛ£78éA_éS‡¿ö‘ûv².ÄèEÞQ§ƒg‡þSsxZýZóXÛ ò’ÚMÓNRi \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/56/6ab53c220a2eafc1212af1a024513230280ab9 b/tests/resources/merge-resolve/.gitted/objects/56/6ab53c220a2eafc1212af1a024513230280ab9
new file mode 100644
index 0000000..a8855ae
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/56/6ab53c220a2eafc1212af1a024513230280ab9
@@ -0,0 +1,3 @@
+x¥ŽÑ !Dý¦
+Ð,˱‰1þØ ,°äLD blßÓØ3ï%“I­ÖËÐhÃft Ù¡XvŽÄøÂäYÍ‚ñ`L2ÑМջ܆NsI¼·b­ËRЧLž¢3…RaÀ$Iñs,­ëS~qÏú¼´úh7½—•~ÒQ¾â×v©Õƒ6Ö!œê-€ZézvÈŸ3
+ù:Ô9âM& \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/56/a638b76b75e068590ac999c2f8621e7f3e264c b/tests/resources/merge-resolve/.gitted/objects/56/a638b76b75e068590ac999c2f8621e7f3e264c
new file mode 100644
index 0000000..36289bf
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/56/a638b76b75e068590ac999c2f8621e7f3e264c
@@ -0,0 +1 @@
+x¥ŽAj!E³öu vuW©B6sƒ¹€–%ÝÛÁ6äúqÎÝçÁ<iµp o£«BNŒ¼D'LâJfç ­„”¸ lŽ<Æ4“yÆ®ç<–Íâ&JèóÊÙ³³‚>È$Ñ^•‘R²bâÏØ[‡{þ=Ãcoõj'|褯õUéíje¼K«Ÿ°¬Ln¡•n–¬5“ÎСÿP˜Y«‚´³|2`ìzôËü—zQ{ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/57/079a46233ae2b6df62e9ade71c4948512abefb b/tests/resources/merge-resolve/.gitted/objects/57/079a46233ae2b6df62e9ade71c4948512abefb
new file mode 100644
index 0000000..c7eabc4
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/57/079a46233ae2b6df62e9ade71c4948512abefb
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/58/43febcb23480df0b5edb22a21c59c772bb8e29 b/tests/resources/merge-resolve/.gitted/objects/58/43febcb23480df0b5edb22a21c59c772bb8e29
new file mode 100644
index 0000000..f6b2a2b
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/58/43febcb23480df0b5edb22a21c59c772bb8e29
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/58/87a5e516c53bd58efb0f02ec6aa031b6fe9ad7 b/tests/resources/merge-resolve/.gitted/objects/58/87a5e516c53bd58efb0f02ec6aa031b6fe9ad7
new file mode 100644
index 0000000..550d288
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/58/87a5e516c53bd58efb0f02ec6aa031b6fe9ad7
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/58/e853f66699fd02629fd50bde08082bc005933a b/tests/resources/merge-resolve/.gitted/objects/58/e853f66699fd02629fd50bde08082bc005933a
new file mode 100644
index 0000000..cf6db63
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/58/e853f66699fd02629fd50bde08082bc005933a
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/59/6803b523203a4851c824c07366906f8353f4ad b/tests/resources/merge-resolve/.gitted/objects/59/6803b523203a4851c824c07366906f8353f4ad
new file mode 100644
index 0000000..cbc8cbe
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/59/6803b523203a4851c824c07366906f8353f4ad
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/5c/2411f8075f48a6b2fdb85ebc0d371747c4df15 b/tests/resources/merge-resolve/.gitted/objects/5c/2411f8075f48a6b2fdb85ebc0d371747c4df15
new file mode 100644
index 0000000..7b41413
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/5c/2411f8075f48a6b2fdb85ebc0d371747c4df15
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/5c/341ead2ba6f2af98ce5ec3fe84f6b6d2899c0d b/tests/resources/merge-resolve/.gitted/objects/5c/341ead2ba6f2af98ce5ec3fe84f6b6d2899c0d
new file mode 100644
index 0000000..63c86bd
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/5c/341ead2ba6f2af98ce5ec3fe84f6b6d2899c0d
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/5c/3b68a71fc4fa5d362fd3875e53137c6a5ab7a5 b/tests/resources/merge-resolve/.gitted/objects/5c/3b68a71fc4fa5d362fd3875e53137c6a5ab7a5
new file mode 100644
index 0000000..5410014
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/5c/3b68a71fc4fa5d362fd3875e53137c6a5ab7a5
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/5d/c1018e90b19654bee986b7a0c268804d39659d b/tests/resources/merge-resolve/.gitted/objects/5d/c1018e90b19654bee986b7a0c268804d39659d
new file mode 100644
index 0000000..7500b99
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/5d/c1018e90b19654bee986b7a0c268804d39659d
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/5d/dd0fe66f990dc0e5cf9fec6d9b465240e9537f b/tests/resources/merge-resolve/.gitted/objects/5d/dd0fe66f990dc0e5cf9fec6d9b465240e9537f
new file mode 100644
index 0000000..9d8691e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/5d/dd0fe66f990dc0e5cf9fec6d9b465240e9537f
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/5e/b7bb6a146eb3c7fd3990b240a2308eceb1cf8d b/tests/resources/merge-resolve/.gitted/objects/5e/b7bb6a146eb3c7fd3990b240a2308eceb1cf8d
new file mode 100644
index 0000000..aca2666
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/5e/b7bb6a146eb3c7fd3990b240a2308eceb1cf8d
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/5f/bfbdc04b4eca46f54f4853a3c5a1dce28f5165 b/tests/resources/merge-resolve/.gitted/objects/5f/bfbdc04b4eca46f54f4853a3c5a1dce28f5165
new file mode 100644
index 0000000..aec3867
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/5f/bfbdc04b4eca46f54f4853a3c5a1dce28f5165
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/60/61fe116ecba0800c26113ea1a7dfac2e16eeaf b/tests/resources/merge-resolve/.gitted/objects/60/61fe116ecba0800c26113ea1a7dfac2e16eeaf
new file mode 100644
index 0000000..3f266f6
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/60/61fe116ecba0800c26113ea1a7dfac2e16eeaf
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/60/91fc2c036a382a69489e3f518ee5aae9a4e567 b/tests/resources/merge-resolve/.gitted/objects/60/91fc2c036a382a69489e3f518ee5aae9a4e567
new file mode 100644
index 0000000..fa63afb
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/60/91fc2c036a382a69489e3f518ee5aae9a4e567
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/61/340eeed7340fa6a8792def9a5938bb5d4434bb b/tests/resources/merge-resolve/.gitted/objects/61/340eeed7340fa6a8792def9a5938bb5d4434bb
new file mode 100644
index 0000000..e830caf
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/61/340eeed7340fa6a8792def9a5938bb5d4434bb
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/61/78885b38fe96e825ac0f492c0a941f288b37f6 b/tests/resources/merge-resolve/.gitted/objects/61/78885b38fe96e825ac0f492c0a941f288b37f6
new file mode 100644
index 0000000..bedc5f2
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/61/78885b38fe96e825ac0f492c0a941f288b37f6
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/62/12c31dab5e482247d7977e4f0dd3601decf13b b/tests/resources/merge-resolve/.gitted/objects/62/12c31dab5e482247d7977e4f0dd3601decf13b
new file mode 100644
index 0000000..b6f0607
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/62/12c31dab5e482247d7977e4f0dd3601decf13b
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/62/269111c3b02a9355badcb9da8678b1bf41787b b/tests/resources/merge-resolve/.gitted/objects/62/269111c3b02a9355badcb9da8678b1bf41787b
new file mode 100644
index 0000000..0edf659
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/62/269111c3b02a9355badcb9da8678b1bf41787b
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/62/33c6a0670228627f93c01cef32485a30403670 b/tests/resources/merge-resolve/.gitted/objects/62/33c6a0670228627f93c01cef32485a30403670
new file mode 100644
index 0000000..81428dd
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/62/33c6a0670228627f93c01cef32485a30403670
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/62/c4f6533c9a3894191fdcb96a3be935ade63f1a b/tests/resources/merge-resolve/.gitted/objects/62/c4f6533c9a3894191fdcb96a3be935ade63f1a
new file mode 100644
index 0000000..c0f822d
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/62/c4f6533c9a3894191fdcb96a3be935ade63f1a
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/63/247125386de9ec90a27ad36169307bf8a11a38 b/tests/resources/merge-resolve/.gitted/objects/63/247125386de9ec90a27ad36169307bf8a11a38
new file mode 100644
index 0000000..bc2d738
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/63/247125386de9ec90a27ad36169307bf8a11a38
@@ -0,0 +1 @@
+xÝ;1 D©}Šé¶A‹Vâô\ÀÙ8¬¥àHIVp|²?‰‚ÐLãyO“ÃuN7C] Í¥ƒËlãt¦:iAÐ(xiŒp‚,ÆOñÄæ¡;•æo†7 …UYZ B‡½ß÷ý]ÆdUmÔyk©ô[…½Úc©ñþÍ¥)©ñ!êX{¢¿Zó±ö \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/63/ec604d491161ddafdae4179843c26d54bd999a b/tests/resources/merge-resolve/.gitted/objects/63/ec604d491161ddafdae4179843c26d54bd999a
new file mode 100644
index 0000000..bc74da5
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/63/ec604d491161ddafdae4179843c26d54bd999a
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/67/110d77886b2af6309b9212961e72b8583e5fa9 b/tests/resources/merge-resolve/.gitted/objects/67/110d77886b2af6309b9212961e72b8583e5fa9
new file mode 100644
index 0000000..877bad7
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/67/110d77886b2af6309b9212961e72b8583e5fa9
@@ -0,0 +1 @@
+x¥=N1 „©÷î^rœ !J:.`'ûŠlP^×gâT3ú43Ò”Ñûuåp·¦*´Z£ %°ælÙÚ4irœÇH‰·žz,ê³¥ä[‰M]a“J©ÂÒbó5¤lÐ8OùX$XÕ³EaÇ")ŠUïœ$d2zO¸ñçÚÇ„—úųÂÛ>úmð¨'ýqÏýZ渶ÊèO`lF“³Oî1!n'=-ýÇÄöªó]A&e‡Ë¯^¶o––^Ý \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/67/18a45909532d1fcf5600d0877f7fe7e78f0b86 b/tests/resources/merge-resolve/.gitted/objects/67/18a45909532d1fcf5600d0877f7fe7e78f0b86
new file mode 100644
index 0000000..ffda698
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/67/18a45909532d1fcf5600d0877f7fe7e78f0b86
@@ -0,0 +1 @@
+x¥ŽÑM1 DùNi`‘ǹDBè~è€ǧ]‰Ý ƒöYð7óž4íû¾M8<ÌaæF䬵@Ò˜r*‹Ü85ÆV«±eV÷.Î鉋”0($!“b½UÑ35É—¨¡8¹ÏµÿÒ¾d4ÿºöý£þÉNú“®ö+þÚ£öýÙ#q@€rÉ~àNzžöÏ7Çö¹ÉÛ‚¸Ô!‡®î÷uQu \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/68/c6c84b091926c7d90aa6a79b2bc3bb6adccd8e b/tests/resources/merge-resolve/.gitted/objects/68/c6c84b091926c7d90aa6a79b2bc3bb6adccd8e
new file mode 100644
index 0000000..1e4b075
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/68/c6c84b091926c7d90aa6a79b2bc3bb6adccd8e
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/68/f7c02064019d89e40e51d7776b6f67914420a2 b/tests/resources/merge-resolve/.gitted/objects/68/f7c02064019d89e40e51d7776b6f67914420a2
new file mode 100644
index 0000000..809a5b3
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/68/f7c02064019d89e40e51d7776b6f67914420a2
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/69/f570c57b24ea7c086e94c5e574964798321435 b/tests/resources/merge-resolve/.gitted/objects/69/f570c57b24ea7c086e94c5e574964798321435
new file mode 100644
index 0000000..6975f0b
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/69/f570c57b24ea7c086e94c5e574964798321435
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/6a/e1a3967031a42cf955d9d5c2395211ac82f6cf b/tests/resources/merge-resolve/.gitted/objects/6a/e1a3967031a42cf955d9d5c2395211ac82f6cf
new file mode 100644
index 0000000..3b5713c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/6a/e1a3967031a42cf955d9d5c2395211ac82f6cf
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/6b/7e37be8ce0b897093f2878a9dcd8f396beda2c b/tests/resources/merge-resolve/.gitted/objects/6b/7e37be8ce0b897093f2878a9dcd8f396beda2c
new file mode 100644
index 0000000..c393186
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/6b/7e37be8ce0b897093f2878a9dcd8f396beda2c
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/6c/06dcd163587c2cc18be44857e0b71116382aeb b/tests/resources/merge-resolve/.gitted/objects/6c/06dcd163587c2cc18be44857e0b71116382aeb
new file mode 100644
index 0000000..2f54be8
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/6c/06dcd163587c2cc18be44857e0b71116382aeb
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/6e/3b9eb35214d4e31ed5789afc7d520ac798ce55 b/tests/resources/merge-resolve/.gitted/objects/6e/3b9eb35214d4e31ed5789afc7d520ac798ce55
new file mode 100644
index 0000000..c6100cb
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/6e/3b9eb35214d4e31ed5789afc7d520ac798ce55
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/6f/32739c3724d1d5f855299309f388606f407468 b/tests/resources/merge-resolve/.gitted/objects/6f/32739c3724d1d5f855299309f388606f407468
new file mode 100644
index 0000000..6741aa4
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/6f/32739c3724d1d5f855299309f388606f407468
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/6f/a33014764bf1120a454eb8437ae098238e409b b/tests/resources/merge-resolve/.gitted/objects/6f/a33014764bf1120a454eb8437ae098238e409b
new file mode 100644
index 0000000..973a4f6
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/6f/a33014764bf1120a454eb8437ae098238e409b
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/6f/be9fb85c86d7d1435f728da418bdff52c640a9 b/tests/resources/merge-resolve/.gitted/objects/6f/be9fb85c86d7d1435f728da418bdff52c640a9
new file mode 100644
index 0000000..a2c8d93
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/6f/be9fb85c86d7d1435f728da418bdff52c640a9
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/71/17467b18605a660ebe5586df69e2311ed5609f b/tests/resources/merge-resolve/.gitted/objects/71/17467b18605a660ebe5586df69e2311ed5609f
new file mode 100644
index 0000000..02e1831
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/71/17467b18605a660ebe5586df69e2311ed5609f
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/71/2ebba6669ea847d9829e4f1059d6c830c8b531 b/tests/resources/merge-resolve/.gitted/objects/71/2ebba6669ea847d9829e4f1059d6c830c8b531
new file mode 100644
index 0000000..dd7d58f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/71/2ebba6669ea847d9829e4f1059d6c830c8b531
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/71/add2d7b93d55bf3600f8a1582beceebbd050c8 b/tests/resources/merge-resolve/.gitted/objects/71/add2d7b93d55bf3600f8a1582beceebbd050c8
new file mode 100644
index 0000000..221afa3
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/71/add2d7b93d55bf3600f8a1582beceebbd050c8
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/72/cdb057b340205164478565e91eb71647e66891 b/tests/resources/merge-resolve/.gitted/objects/72/cdb057b340205164478565e91eb71647e66891
new file mode 100644
index 0000000..84aa833
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/72/cdb057b340205164478565e91eb71647e66891
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/72/ea499e108df5ff0a4a913e7655bbeeb1fb69f2 b/tests/resources/merge-resolve/.gitted/objects/72/ea499e108df5ff0a4a913e7655bbeeb1fb69f2
new file mode 100644
index 0000000..4886e49
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/72/ea499e108df5ff0a4a913e7655bbeeb1fb69f2
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/74/df13f0793afdaa972150bba976f7de8284914e b/tests/resources/merge-resolve/.gitted/objects/74/df13f0793afdaa972150bba976f7de8284914e
new file mode 100644
index 0000000..cb50e67
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/74/df13f0793afdaa972150bba976f7de8284914e
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/75/a811bf6bc57694adb3fe604786f3a4efd1cd1b b/tests/resources/merge-resolve/.gitted/objects/75/a811bf6bc57694adb3fe604786f3a4efd1cd1b
new file mode 100644
index 0000000..477fd87
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/75/a811bf6bc57694adb3fe604786f3a4efd1cd1b
@@ -0,0 +1,2 @@
+x¥ŽK
+1D]ç¹ÀHçk "n¼Hº;Ì€3‘õúŽâ ÜU½EQç©këý¦7 Ž€ !²7Xdc2±ÇR¬ƒ˜À©[j²tMv… :q.°‹Äc¦D* l†(¤Ò£µé3¿Rc}ë|¯‹>ÈJ?é$_ñk[ªóQ,ìÑë"€Zéz¶ËŸ3ª·é9¥ë€ê LŽOÊ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/76/63fce0130db092936b137cabd693ec234eb060 b/tests/resources/merge-resolve/.gitted/objects/76/63fce0130db092936b137cabd693ec234eb060
new file mode 100644
index 0000000..f578a4a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/76/63fce0130db092936b137cabd693ec234eb060
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/76/ab0e2868197ec158ddd6c78d8a0d2fd73d38f9 b/tests/resources/merge-resolve/.gitted/objects/76/ab0e2868197ec158ddd6c78d8a0d2fd73d38f9
new file mode 100644
index 0000000..4d41ad8
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/76/ab0e2868197ec158ddd6c78d8a0d2fd73d38f9
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/7a/a3edf2bcfee22398e6b55295aa56366b7aaf76 b/tests/resources/merge-resolve/.gitted/objects/7a/a3edf2bcfee22398e6b55295aa56366b7aaf76
new file mode 100644
index 0000000..09f1e4d
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/7a/a3edf2bcfee22398e6b55295aa56366b7aaf76
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/7a/a825857f87aea74ddf13d954568aa30dfcdeb4 b/tests/resources/merge-resolve/.gitted/objects/7a/a825857f87aea74ddf13d954568aa30dfcdeb4
new file mode 100644
index 0000000..b9c0630
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/7a/a825857f87aea74ddf13d954568aa30dfcdeb4
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/7a/f14d9c679baaef35555095f4f5d33e9a569ab9 b/tests/resources/merge-resolve/.gitted/objects/7a/f14d9c679baaef35555095f4f5d33e9a569ab9
new file mode 100644
index 0000000..b4c4ef7
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/7a/f14d9c679baaef35555095f4f5d33e9a569ab9
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/7c/04ca611203ed320c5f495b9813054dd23be3be b/tests/resources/merge-resolve/.gitted/objects/7c/04ca611203ed320c5f495b9813054dd23be3be
new file mode 100644
index 0000000..e3ba605
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/7c/04ca611203ed320c5f495b9813054dd23be3be
@@ -0,0 +1,2 @@
+x¥ŽQ Dýæ{ -,tcüñ^`%õƒb(ÆëKÏà|ͼd&k)¯³±§ÞDÀsžl¢è<fɇ4a¶“1BŒŽ8zs“­CvƒÅ˜„ºEëQüd¡˜ÅO>åEñ§¯µÁ#}¹%x®µìuƒ« z¸{yÅV÷šû%ÖrƒÉžÇ†·pÖ¨µtíòÇ„
+·¸ÎêUrL \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/7c/2c5228c9e90170d4a35e6558e47163daf092e5 b/tests/resources/merge-resolve/.gitted/objects/7c/2c5228c9e90170d4a35e6558e47163daf092e5
new file mode 100644
index 0000000..52fde92
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/7c/2c5228c9e90170d4a35e6558e47163daf092e5
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/7c/b63eed597130ba4abb87b3e544b85021905520 b/tests/resources/merge-resolve/.gitted/objects/7c/b63eed597130ba4abb87b3e544b85021905520
new file mode 100644
index 0000000..769f29c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/7c/b63eed597130ba4abb87b3e544b85021905520
@@ -0,0 +1,3 @@
+x¥ŽK
+1D]ç¹€Òùt'"n¼H:ÆÅL$F¼¾£xwUïAQÜ–å6´õf7ºˆI“T*zë±J
+–1#;@r´X]¬êžº¬C3A¨ì F'Îa‘j#Š”ÑTâšÀf a•žcn]_Ê+õ¢¯s[mÕGÙè'å+~íÀm9iãÐ ¹‰PïÔF·³CþœQ¹§•gõe"NÕ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/7e/2d058d5fedf8329db44db4fac610d6b1a89159 b/tests/resources/merge-resolve/.gitted/objects/7e/2d058d5fedf8329db44db4fac610d6b1a89159
new file mode 100644
index 0000000..d12d7b4
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/7e/2d058d5fedf8329db44db4fac610d6b1a89159
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/7f/7a2da58126226986d71c6ddfab4afba693280d b/tests/resources/merge-resolve/.gitted/objects/7f/7a2da58126226986d71c6ddfab4afba693280d
new file mode 100644
index 0000000..2f833c2
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/7f/7a2da58126226986d71c6ddfab4afba693280d
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/80/a8fbb3abb1ba423d554e9630b8fc2e5698f86b b/tests/resources/merge-resolve/.gitted/objects/80/a8fbb3abb1ba423d554e9630b8fc2e5698f86b
new file mode 100644
index 0000000..3daf6c3
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/80/a8fbb3abb1ba423d554e9630b8fc2e5698f86b
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/81/1c70fcb6d5bbd022d04cc31836d30b436f9551 b/tests/resources/merge-resolve/.gitted/objects/81/1c70fcb6d5bbd022d04cc31836d30b436f9551
new file mode 100644
index 0000000..6d87024
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/81/1c70fcb6d5bbd022d04cc31836d30b436f9551
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/81/87117062b750eed4f93fd7e899f17b52ce554d b/tests/resources/merge-resolve/.gitted/objects/81/87117062b750eed4f93fd7e899f17b52ce554d
new file mode 100644
index 0000000..19cac9f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/81/87117062b750eed4f93fd7e899f17b52ce554d
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/83/07d93a155903a5c49576583f0ce1f6ff897c0e b/tests/resources/merge-resolve/.gitted/objects/83/07d93a155903a5c49576583f0ce1f6ff897c0e
new file mode 100644
index 0000000..5a96a4e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/83/07d93a155903a5c49576583f0ce1f6ff897c0e
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/83/6b8b82b26cab22eaaed8820877c76d6c8bca19 b/tests/resources/merge-resolve/.gitted/objects/83/6b8b82b26cab22eaaed8820877c76d6c8bca19
new file mode 100644
index 0000000..99f8286
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/83/6b8b82b26cab22eaaed8820877c76d6c8bca19
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/83/824a8c6658768e2013905219cc8c64cc3d9a2e b/tests/resources/merge-resolve/.gitted/objects/83/824a8c6658768e2013905219cc8c64cc3d9a2e
new file mode 100644
index 0000000..066190f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/83/824a8c6658768e2013905219cc8c64cc3d9a2e
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/84/9619b03ae540acee4d1edec96b86993da6b497 b/tests/resources/merge-resolve/.gitted/objects/84/9619b03ae540acee4d1edec96b86993da6b497
new file mode 100644
index 0000000..67271ac
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/84/9619b03ae540acee4d1edec96b86993da6b497
@@ -0,0 +1,3 @@
+x¥ŽK
+1D]ç¹€Òùv7ÞÀ½t’3‹L$ñúŽâ ÜU½E¥Vë<¤¶v7:³"xç¥K@›R¶
+rÌŠ#Ç"Ôy2[ Xµ5 r2ÆQ´ˆå¨5–”£bŠ=ÇÔº¼æõ,oS«k[ä‰7úIþŠ_;¤VÏRç”?ú`ä<€ØèvvðŸ3"¶1ÝóÜWñEÇP \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/84/de84f8f3a6d63e636ee9ad81f4b80512fa9bbe b/tests/resources/merge-resolve/.gitted/objects/84/de84f8f3a6d63e636ee9ad81f4b80512fa9bbe
new file mode 100644
index 0000000..32f1461
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/84/de84f8f3a6d63e636ee9ad81f4b80512fa9bbe
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/86/088dae8bade454995b21a1c88107b0e1accdab b/tests/resources/merge-resolve/.gitted/objects/86/088dae8bade454995b21a1c88107b0e1accdab
new file mode 100644
index 0000000..623a747
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/86/088dae8bade454995b21a1c88107b0e1accdab
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/87/b4926260d77a3b851e71ecce06839bd650b231 b/tests/resources/merge-resolve/.gitted/objects/87/b4926260d77a3b851e71ecce06839bd650b231
new file mode 100644
index 0000000..91944ff
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/87/b4926260d77a3b851e71ecce06839bd650b231
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/88/e185910a15cd13bdf44854ad037f4842b03b29 b/tests/resources/merge-resolve/.gitted/objects/88/e185910a15cd13bdf44854ad037f4842b03b29
new file mode 100644
index 0000000..ae1c5e2
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/88/e185910a15cd13bdf44854ad037f4842b03b29
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/8a/ad9d0ea334951da47b621a475b39cc6ed759bf b/tests/resources/merge-resolve/.gitted/objects/8a/ad9d0ea334951da47b621a475b39cc6ed759bf
new file mode 100644
index 0000000..5e2c943
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/8a/ad9d0ea334951da47b621a475b39cc6ed759bf
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/8a/ae714f7d939309d7f132b30646d96743134a9f b/tests/resources/merge-resolve/.gitted/objects/8a/ae714f7d939309d7f132b30646d96743134a9f
new file mode 100644
index 0000000..34ff560
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/8a/ae714f7d939309d7f132b30646d96743134a9f
@@ -0,0 +1 @@
+x+)JMU06³`040031QH,-ÉÏM-JOMLÊIÕ+©(aH:,»:ÎCÉýúô:ÞË ²o>ZC'g$楧¦èfæé&%æ%g€5¬ÎqYôÂeÒZoÇÝÙkÚMíæ2­éÆÔ›X\’ZDPC~^ZNfrIf^:Xéõ›ZHÙŠž1O(_œ,'º× jv^j9È!Ɖ9%`¥<sBÞ§ÝHrèSæ¼3§d ã ¨Ò¢ÔÜü2 wßüI{•|þ¹÷ 2m»gÜ˾‹©ÉÝ1ÖËåüŠ5Ó¿Ü,\“µ})TC)00ÀavʉùzÖ›¦9–¤×Mü°úÕ…'6óbÊå’G \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/8b/095d8fd01594f4d14454d073e3ac57b9ce485f b/tests/resources/merge-resolve/.gitted/objects/8b/095d8fd01594f4d14454d073e3ac57b9ce485f
new file mode 100644
index 0000000..4ec0138
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/8b/095d8fd01594f4d14454d073e3ac57b9ce485f
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/8b/5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a b/tests/resources/merge-resolve/.gitted/objects/8b/5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a
new file mode 100644
index 0000000..f4249c2
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/8b/5b53cb2aa9ceb1139f5312fcfa3cc3c5a47c9a
@@ -0,0 +1 @@
+xí± À0 S{Š"2ŽŒd,0²‘^?&S¤HóÕÝŸ[Ï8ï눪E›`Ñ„ËrƒZŠ*êdŒ¥­ÆrlŒ,©± ÙcbF/ ·“¶÷'¿ûågв \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/8b/7cd60d49ce3a1a770ece43b7d29b5cf462a33a b/tests/resources/merge-resolve/.gitted/objects/8b/7cd60d49ce3a1a770ece43b7d29b5cf462a33a
new file mode 100644
index 0000000..790750c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/8b/7cd60d49ce3a1a770ece43b7d29b5cf462a33a
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/8b/fb012a6d809e499bd8d3e194a3929bc8995b93 b/tests/resources/merge-resolve/.gitted/objects/8b/fb012a6d809e499bd8d3e194a3929bc8995b93
new file mode 100644
index 0000000..a90ee08
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/8b/fb012a6d809e499bd8d3e194a3929bc8995b93
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/8c/749d9968d4b10dcfb06c9f97d0e5d92d337071 b/tests/resources/merge-resolve/.gitted/objects/8c/749d9968d4b10dcfb06c9f97d0e5d92d337071
new file mode 100644
index 0000000..e42393c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/8c/749d9968d4b10dcfb06c9f97d0e5d92d337071
@@ -0,0 +1,2 @@
+x¥ŽAB!C]s
+. a€ùcÜx÷f`†|_ b¼¾h¼»ö5mšëº^»¶ÞmzÑÙL“`ð”}$26#"8°ÅÆ`s.`Ԛܺ.!bH\<» i´"Á,K¦œ8¯èÙ—Úô‰_ÔXŸ—º>êMïeÐ:Ê7ø¹]®ëAƒC40ÏÞë­™ŒQƒŽ³]þœQ\.Ä,¬ÞVO  \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/8f/4433f8593ddd65b7dd43dd4564d841f4d9c8aa b/tests/resources/merge-resolve/.gitted/objects/8f/4433f8593ddd65b7dd43dd4564d841f4d9c8aa
new file mode 100644
index 0000000..d2de777
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/8f/4433f8593ddd65b7dd43dd4564d841f4d9c8aa
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/90/a336c7dacbe295159413559b0043b8bdc60d57 b/tests/resources/merge-resolve/.gitted/objects/90/a336c7dacbe295159413559b0043b8bdc60d57
new file mode 100644
index 0000000..35453eb
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/90/a336c7dacbe295159413559b0043b8bdc60d57
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/91/2b2d7819cf9c1029e414883857ed61d597a1a5 b/tests/resources/merge-resolve/.gitted/objects/91/2b2d7819cf9c1029e414883857ed61d597a1a5
new file mode 100644
index 0000000..d5df393
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/91/2b2d7819cf9c1029e414883857ed61d597a1a5
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/91/8bb3e09090a9995d48af9a2a6296d7e6088d1c b/tests/resources/merge-resolve/.gitted/objects/91/8bb3e09090a9995d48af9a2a6296d7e6088d1c
new file mode 100644
index 0000000..c214ab2
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/91/8bb3e09090a9995d48af9a2a6296d7e6088d1c
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/91/f44111cb1cb1358ac6944ad356ca1738813ea1 b/tests/resources/merge-resolve/.gitted/objects/91/f44111cb1cb1358ac6944ad356ca1738813ea1
new file mode 100644
index 0000000..51a456f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/91/f44111cb1cb1358ac6944ad356ca1738813ea1
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/92/7d4943cdbdc9a667db8e62cfd0a41870235c51 b/tests/resources/merge-resolve/.gitted/objects/92/7d4943cdbdc9a667db8e62cfd0a41870235c51
new file mode 100644
index 0000000..b6b92c8
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/92/7d4943cdbdc9a667db8e62cfd0a41870235c51
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/93/77fccdb210540b8c0520cc6e80eb632c20bd25 b/tests/resources/merge-resolve/.gitted/objects/93/77fccdb210540b8c0520cc6e80eb632c20bd25
new file mode 100644
index 0000000..4b2d93b
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/93/77fccdb210540b8c0520cc6e80eb632c20bd25
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/94/29c05dd6f6f39fc567b4ce923b16df5d3d7a7a b/tests/resources/merge-resolve/.gitted/objects/94/29c05dd6f6f39fc567b4ce923b16df5d3d7a7a
new file mode 100644
index 0000000..d4d93f5
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/94/29c05dd6f6f39fc567b4ce923b16df5d3d7a7a
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/94/4f5dd1a867cab4c2bbcb896493435cae1dcc1a b/tests/resources/merge-resolve/.gitted/objects/94/4f5dd1a867cab4c2bbcb896493435cae1dcc1a
new file mode 100644
index 0000000..1430938
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/94/4f5dd1a867cab4c2bbcb896493435cae1dcc1a
@@ -0,0 +1,2 @@
+x¥ŽK!D]s
+. zšÄ7ÞÀ Ð|2. b¼¾h¼»ªWÉKÅVëmH0~7zÎÒ"¦PÈ9`ò:»ÀÑQôi)QÈLEyq=oC*P6-"4„l0StAŽHÌ<u$Âs¬­ËKz…žäumõÑ6yÌ“~Ò9‡_;ÄVORç4z½X¹W¨”˜tžùOà6Vññ4NF \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/94/8ba6e701c1edab0c2d394fb7c5538334129793 b/tests/resources/merge-resolve/.gitted/objects/94/8ba6e701c1edab0c2d394fb7c5538334129793
new file mode 100644
index 0000000..b3e3ef9
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/94/8ba6e701c1edab0c2d394fb7c5538334129793
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/95/646149ab6b6ba6edc83cff678582538b457b2b b/tests/resources/merge-resolve/.gitted/objects/95/646149ab6b6ba6edc83cff678582538b457b2b
new file mode 100644
index 0000000..de9ba28
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/95/646149ab6b6ba6edc83cff678582538b457b2b
@@ -0,0 +1,3 @@
+x•ŽMÂ …]s
+. ™ÃObŒoàÞ eH»h1ãõEÓ ¸{?y_ÞX–enR}h•Y* üHFS
+€S ž!$À1…¨Å“*¯M’³˜£wUv4ôIt:ª„è²ÏÞ8KFEA¯6•*oéM5ÉûT–­¬òÌ=ýª+ÿŠÝƲ\ä Ñ CVÁˆžö³ÿÆà`ƒnLj”ÛœX‰iO\ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/95/9de65e568274120fdf9e3af9f77b1550122149 b/tests/resources/merge-resolve/.gitted/objects/95/9de65e568274120fdf9e3af9f77b1550122149
new file mode 100644
index 0000000..e998de8
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/95/9de65e568274120fdf9e3af9f77b1550122149
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/96/8ca794a4597f7f6abbb2b8d940b4078a0f3fd4 b/tests/resources/merge-resolve/.gitted/objects/96/8ca794a4597f7f6abbb2b8d940b4078a0f3fd4
new file mode 100644
index 0000000..359e43a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/96/8ca794a4597f7f6abbb2b8d940b4078a0f3fd4
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/96/bca8d4f05cc4c5e33e4389f80a1309e86fe054 b/tests/resources/merge-resolve/.gitted/objects/96/bca8d4f05cc4c5e33e4389f80a1309e86fe054
new file mode 100644
index 0000000..8938d3e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/96/bca8d4f05cc4c5e33e4389f80a1309e86fe054
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/97/7c696519c5a3004c5f1d15d60c89dbeb8f235f b/tests/resources/merge-resolve/.gitted/objects/97/7c696519c5a3004c5f1d15d60c89dbeb8f235f
new file mode 100644
index 0000000..e561b47
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/97/7c696519c5a3004c5f1d15d60c89dbeb8f235f
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/98/ba4205fcf31f5dd93c916d35fe3f3b3d0e6714 b/tests/resources/merge-resolve/.gitted/objects/98/ba4205fcf31f5dd93c916d35fe3f3b3d0e6714
new file mode 100644
index 0000000..6f5e979
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/98/ba4205fcf31f5dd93c916d35fe3f3b3d0e6714
@@ -0,0 +1 @@
+x-ŒÁ Ã0 ûÖ ÐþºG% È…­"ë×Mú!@yÉjÂýñ¼½Šv¬j‚¢:ïAÜÁ‹ÂAÇèM~dú¹­ãÐ{.3Ñ);ÔlÂç]vi›ú6Á„D%þ «¯¦9fú|.z \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/98/d52d07c0b0bbf2b46548f6aa521295c2cb55db b/tests/resources/merge-resolve/.gitted/objects/98/d52d07c0b0bbf2b46548f6aa521295c2cb55db
new file mode 100644
index 0000000..c8d636e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/98/d52d07c0b0bbf2b46548f6aa521295c2cb55db
@@ -0,0 +1,3 @@
+xíA
+€0=÷y„}NË®taÝJ[ï
+ú/^r“’´$ŒÓ<ô, ‡¨"1*[\™ †žYj Ñ‹(;Ôóm£9ƒ oNŒxcëz"1ï(»7„áy÷Û—.øõ®þ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/99/b4f7e4f24470fa06b980bc21f1095c2a9425c0 b/tests/resources/merge-resolve/.gitted/objects/99/b4f7e4f24470fa06b980bc21f1095c2a9425c0
new file mode 100644
index 0000000..01ad66e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/99/b4f7e4f24470fa06b980bc21f1095c2a9425c0
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/9a/301fbe6fada7dcb74fcd7c20269b5c743459a7 b/tests/resources/merge-resolve/.gitted/objects/9a/301fbe6fada7dcb74fcd7c20269b5c743459a7
new file mode 100644
index 0000000..f413cc5
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/9a/301fbe6fada7dcb74fcd7c20269b5c743459a7
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/9a/f731fa116d1eb9a6c0109562472cfee6f5a979 b/tests/resources/merge-resolve/.gitted/objects/9a/f731fa116d1eb9a6c0109562472cfee6f5a979
new file mode 100644
index 0000000..53233c4
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/9a/f731fa116d1eb9a6c0109562472cfee6f5a979
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/9c/0b6c34ef379a42d858f03fef38630f476b9102 b/tests/resources/merge-resolve/.gitted/objects/9c/0b6c34ef379a42d858f03fef38630f476b9102
new file mode 100644
index 0000000..e6f8500
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/9c/0b6c34ef379a42d858f03fef38630f476b9102
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/9e/7f4359c469f309b6057febf4c6e80742cbed5b b/tests/resources/merge-resolve/.gitted/objects/9e/7f4359c469f309b6057febf4c6e80742cbed5b
new file mode 100644
index 0000000..72b7c49
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/9e/7f4359c469f309b6057febf4c6e80742cbed5b
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/9e/fe7723802d4305142eee177e018fee1572c4f4 b/tests/resources/merge-resolve/.gitted/objects/9e/fe7723802d4305142eee177e018fee1572c4f4
new file mode 100644
index 0000000..c63fc2c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/9e/fe7723802d4305142eee177e018fee1572c4f4
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/9f/74397a3397b3585faf09e9926b110d7f654254 b/tests/resources/merge-resolve/.gitted/objects/9f/74397a3397b3585faf09e9926b110d7f654254
new file mode 100644
index 0000000..e7ec397
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/9f/74397a3397b3585faf09e9926b110d7f654254
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/a0/31a28ae70e33a641ce4b8a8f6317f1ab79dee4 b/tests/resources/merge-resolve/.gitted/objects/a0/31a28ae70e33a641ce4b8a8f6317f1ab79dee4
new file mode 100644
index 0000000..a6c05d1
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/a0/31a28ae70e33a641ce4b8a8f6317f1ab79dee4
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/a1/07e18a58f38c46086c8f8f1dcd54c40154eeb6 b/tests/resources/merge-resolve/.gitted/objects/a1/07e18a58f38c46086c8f8f1dcd54c40154eeb6
new file mode 100644
index 0000000..598c6a7
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/a1/07e18a58f38c46086c8f8f1dcd54c40154eeb6
@@ -0,0 +1,3 @@
+xK
+1]ç½$“Ä|`7.Ü{Îgœ“@&£×7^ÁZ=
+jÎÔa²îÐ[J pÑùàƒ‘FH ÎZïQIg#P4Èî}­ nñƒ-Âc­y«æ4ìo]ŸÔ×ÝŸB͘”Õúl¬Vpä6ì¨öôïŸÝ u´ô¦ja_É ?H \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/a2/d8d1824c68541cca94ffb90f79291eba495921 b/tests/resources/merge-resolve/.gitted/objects/a2/d8d1824c68541cca94ffb90f79291eba495921
new file mode 100644
index 0000000..2d3d947
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/a2/d8d1824c68541cca94ffb90f79291eba495921
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/a3/9a620dae5bc8b4e771cd4d251b7d080401a21e b/tests/resources/merge-resolve/.gitted/objects/a3/9a620dae5bc8b4e771cd4d251b7d080401a21e
new file mode 100644
index 0000000..4d22586
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/a3/9a620dae5bc8b4e771cd4d251b7d080401a21e
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/a3/fabece9eb8748da810e1e08266fef9b7136ad4 b/tests/resources/merge-resolve/.gitted/objects/a3/fabece9eb8748da810e1e08266fef9b7136ad4
new file mode 100644
index 0000000..24d7dbc
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/a3/fabece9eb8748da810e1e08266fef9b7136ad4
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/a4/1b1bb6d0be3c22fb654234c33b428e15c8cc27 b/tests/resources/merge-resolve/.gitted/objects/a4/1b1bb6d0be3c22fb654234c33b428e15c8cc27
new file mode 100644
index 0000000..60789ee
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/a4/1b1bb6d0be3c22fb654234c33b428e15c8cc27
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/a4/3150a738849c59376cf30bb2a68348a83c8f48 b/tests/resources/merge-resolve/.gitted/objects/a4/3150a738849c59376cf30bb2a68348a83c8f48
new file mode 100644
index 0000000..06ae09e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/a4/3150a738849c59376cf30bb2a68348a83c8f48
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/a5/563304ddf6caba25cb50323a2ea6f7dbfcadca b/tests/resources/merge-resolve/.gitted/objects/a5/563304ddf6caba25cb50323a2ea6f7dbfcadca
new file mode 100644
index 0000000..a831878
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/a5/563304ddf6caba25cb50323a2ea6f7dbfcadca
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/a7/08b253bd507417ec42d1467a7fd2d7519c4956 b/tests/resources/merge-resolve/.gitted/objects/a7/08b253bd507417ec42d1467a7fd2d7519c4956
new file mode 100644
index 0000000..bae752a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/a7/08b253bd507417ec42d1467a7fd2d7519c4956
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/a7/65fb87eb2f7a1920b73b2d5a057f8f8476a42b b/tests/resources/merge-resolve/.gitted/objects/a7/65fb87eb2f7a1920b73b2d5a057f8f8476a42b
new file mode 100644
index 0000000..30abd8b
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/a7/65fb87eb2f7a1920b73b2d5a057f8f8476a42b
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/a7/7a56a49f8f3ae242e02717f18ebbc60c5cc543 b/tests/resources/merge-resolve/.gitted/objects/a7/7a56a49f8f3ae242e02717f18ebbc60c5cc543
new file mode 100644
index 0000000..76dd5f9
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/a7/7a56a49f8f3ae242e02717f18ebbc60c5cc543
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/a7/dbfcbfc1a60709cb80b5ca24539008456531d0 b/tests/resources/merge-resolve/.gitted/objects/a7/dbfcbfc1a60709cb80b5ca24539008456531d0
new file mode 100644
index 0000000..67126c9
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/a7/dbfcbfc1a60709cb80b5ca24539008456531d0
@@ -0,0 +1 @@
+x¥NË !õL4 a˜ÀÄ/vàÝð²{`1,ÆöEcÞÞ?/ÖR–.õ„»Þ˜%3$“ÁL15fe53'4Á2ÅÃ7^G1yBGV…LAGä Ù*‘ôä|R) ÿìsmòš^¾%y›kÙê*O<Ôºð×ø±C¬å,‰À’{e”Cg;ÿ9#R¾oKboœ³NÀ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/a8/02e06f1782a9645b9851bc7202cee74a8a4972 b/tests/resources/merge-resolve/.gitted/objects/a8/02e06f1782a9645b9851bc7202cee74a8a4972
new file mode 100644
index 0000000..d39034b
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/a8/02e06f1782a9645b9851bc7202cee74a8a4972
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/a8/87dd39ad3edd610fc9083dcb61e40ab50673d1 b/tests/resources/merge-resolve/.gitted/objects/a8/87dd39ad3edd610fc9083dcb61e40ab50673d1
new file mode 100644
index 0000000..968c42a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/a8/87dd39ad3edd610fc9083dcb61e40ab50673d1
@@ -0,0 +1 @@
+x+)JMU067a040031QH,-ÉÏM-JOMLÊIÕ+©(aH:,»:ÎCÉýúô:ÞË ²o>ZC'g$楧¦èfæé&%æ%g€5¬ÎqYôÂeÒZoÇÝÙkÚMíæ2­éÆÔ›X\’ZDPC~^ZNfrIf^:Xéõ›ZHÙŠž1O(_œ,'º× jv^j¹nb^ŠnJfZZjQj^ XÃû#3ƒ|>²^Uó:þ'äAÝÔ2¿†R¨†¢ÔÜü2 ×ßüI{•|þ¹÷ 2m»gÜ˾‹©Éõ1ÖËåüŠ5Ó¿Ü,\“µ})TC)0HÀ!vʉùzÖ›¦9–¤×Mü°úÕ…'6óbG–x \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/a9/0bc3fb6f15181972a2959a921429efbd81a473 b/tests/resources/merge-resolve/.gitted/objects/a9/0bc3fb6f15181972a2959a921429efbd81a473
new file mode 100644
index 0000000..91113ee
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/a9/0bc3fb6f15181972a2959a921429efbd81a473
@@ -0,0 +1,2 @@
+x¥ŽK
+1D]ç}¥;ÿ7ÞÀ½dÒ=Œ‹‰¯ooà®^UQT®¥\;hk6½‰@¦™Â‰gŒ §5r’èƒÑ“–Œ]uOMnòdgÄz›&ÒècÆ圈õ¨'•ž}­ NüJá¼Öò¨7ØËp?ê(ßàG»\ËÈ8‡¼CØ¢GTÃg»ü9£x¹$faõxN" \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/ab/40af3cb8a3ed2e2843e96d9aa7871336b94573 b/tests/resources/merge-resolve/.gitted/objects/ab/40af3cb8a3ed2e2843e96d9aa7871336b94573
new file mode 100644
index 0000000..7da1da6
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ab/40af3cb8a3ed2e2843e96d9aa7871336b94573
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ab/6c44a2e84492ad4b41bb6bac87353e9d02ac8b b/tests/resources/merge-resolve/.gitted/objects/ab/6c44a2e84492ad4b41bb6bac87353e9d02ac8b
new file mode 100644
index 0000000..d840c1a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ab/6c44a2e84492ad4b41bb6bac87353e9d02ac8b
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ab/929391ac42572f92110f3deeb4f0844a951e22 b/tests/resources/merge-resolve/.gitted/objects/ab/929391ac42572f92110f3deeb4f0844a951e22
new file mode 100644
index 0000000..8840d00
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ab/929391ac42572f92110f3deeb4f0844a951e22
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ac/4045f965119e6998f4340ed0f411decfb3ec05 b/tests/resources/merge-resolve/.gitted/objects/ac/4045f965119e6998f4340ed0f411decfb3ec05
new file mode 100644
index 0000000..4c32d63
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ac/4045f965119e6998f4340ed0f411decfb3ec05
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ad/01aebfdf2ac13145efafe3f9fcf798882f1730 b/tests/resources/merge-resolve/.gitted/objects/ad/01aebfdf2ac13145efafe3f9fcf798882f1730
new file mode 100644
index 0000000..ae3ef8c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ad/01aebfdf2ac13145efafe3f9fcf798882f1730
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ad/26b598134264fd284292cb233fc0b2f25851da b/tests/resources/merge-resolve/.gitted/objects/ad/26b598134264fd284292cb233fc0b2f25851da
new file mode 100644
index 0000000..5819a2e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ad/26b598134264fd284292cb233fc0b2f25851da
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ad/a14492498136771f69dd451866cabcb0e9ef9a b/tests/resources/merge-resolve/.gitted/objects/ad/a14492498136771f69dd451866cabcb0e9ef9a
new file mode 100644
index 0000000..71023de
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ad/a14492498136771f69dd451866cabcb0e9ef9a
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ad/a55a45d14527dc3dfc714ea1c65d2e1e6fbe87 b/tests/resources/merge-resolve/.gitted/objects/ad/a55a45d14527dc3dfc714ea1c65d2e1e6fbe87
new file mode 100644
index 0000000..3091b8f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ad/a55a45d14527dc3dfc714ea1c65d2e1e6fbe87
@@ -0,0 +1 @@
+x+)JMU067d040031QH,-ÉÏM-JOMLÊIÕ+©(aH:,»:ÎCÉýúô:ÞË ²o>ZC'g$楧¦èfæé&%æ%g€5¬ÎqYôÂeÒZoÇÝÙkÚMíæ2­éÆÔ›X\’ZDPC~^ZNfrIf^:Xéõ›ZHÙŠž1O(_œ,'º× jvn~JfZ&Ä5†Æ`ÕÆnלU7Ï V.6™t6ôÇL/•R¨ê¢ÔÜü2 §ßüI{•|þ¹÷ 2m»gÜ˾‹©Éé1ÖËåüŠ5Ó¿Ü,\“µ})TC)0<ÀÁvʉùzÖ›¦9–¤×Mü°úÕ…'6ób±N’* \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/b2/d399ae15224e1d58066e3c8df70ce37de7a656 b/tests/resources/merge-resolve/.gitted/objects/b2/d399ae15224e1d58066e3c8df70ce37de7a656
new file mode 100644
index 0000000..20fa838
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/b2/d399ae15224e1d58066e3c8df70ce37de7a656
@@ -0,0 +1,2 @@
+xíQA1ôÌ+x€ÙÄ‹‰Ï¡-kI*5”f¿/»zõîÁ af˜!¤Ö^/·“W¸Jcܤ5LŒÆ›‰;+ŠBŸ6ÎHZP|`îóh>\(óÙ$“sà´î•íX·@š¢75€}57¹K
+¯µ+= ;g—® @нÒ!¬4Úè!Œ,\$\ \Âb/±ÉHsø©#þa¾¼÷QÄß \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/b4/2712cfe99a1a500b2a51fe984e0b8a7702ba11 b/tests/resources/merge-resolve/.gitted/objects/b4/2712cfe99a1a500b2a51fe984e0b8a7702ba11
new file mode 100644
index 0000000..2820b46
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/b4/2712cfe99a1a500b2a51fe984e0b8a7702ba11
@@ -0,0 +1,5 @@
+xíA
+À {öû†Bé{M1  ¨¥ß¯>£PØËË–‹3ŽýÜFÖŽ7Á¥E02 <Z
+XÐĨJ
+ÃAˆ^«Ú
+ündim=p#‹Yz¿÷“?¼ \»š \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/b6/9fe837e4cecfd4c9a40cdca7c138468687df07 b/tests/resources/merge-resolve/.gitted/objects/b6/9fe837e4cecfd4c9a40cdca7c138468687df07
new file mode 100644
index 0000000..6dbcb05
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/b6/9fe837e4cecfd4c9a40cdca7c138468687df07
@@ -0,0 +1,2 @@
+xí’A
+1E»Î)þf7í}ÌŒ!‚QHzýZºí\|üOùU½âµ?Ñe!‹pJk<ÙM”oQ-¤ËQéÑàáI>†Ûý¶‹XŽ0üš¹Ì’ê,)ë$;:¯‚­Ü·îþÍÆ(óä: \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/b6/f610aef53bd343e6c96227de874c66f00ee8e8 b/tests/resources/merge-resolve/.gitted/objects/b6/f610aef53bd343e6c96227de874c66f00ee8e8
new file mode 100644
index 0000000..fb102f1
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/b6/f610aef53bd343e6c96227de874c66f00ee8e8
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/b7/a2576f9fc20024ac9ef17cb134acbd1ac73127 b/tests/resources/merge-resolve/.gitted/objects/b7/a2576f9fc20024ac9ef17cb134acbd1ac73127
new file mode 100644
index 0000000..22f2d13
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/b7/a2576f9fc20024ac9ef17cb134acbd1ac73127
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/b8/a3a806d3950e8c0a03a34f234a92eff0e2c68d b/tests/resources/merge-resolve/.gitted/objects/b8/a3a806d3950e8c0a03a34f234a92eff0e2c68d
new file mode 100644
index 0000000..24f0299
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/b8/a3a806d3950e8c0a03a34f234a92eff0e2c68d
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ba/cac9b3493509aa15e1730e1545fc0919d1dae0 b/tests/resources/merge-resolve/.gitted/objects/ba/cac9b3493509aa15e1730e1545fc0919d1dae0
new file mode 100644
index 0000000..f35586f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ba/cac9b3493509aa15e1730e1545fc0919d1dae0
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/bc/744705e1d8a019993cf88f62bc4020f1b80919 b/tests/resources/merge-resolve/.gitted/objects/bc/744705e1d8a019993cf88f62bc4020f1b80919
new file mode 100644
index 0000000..0d4bdb3
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/bc/744705e1d8a019993cf88f62bc4020f1b80919
@@ -0,0 +1,2 @@
+x¥ŽK
+1D]ç}%ÿˆ¸ñî%t˜YÄH&âõâ ÜU½¢ŠJ­Öu€Ôj7:P(Ú#¥”¬ÌF§Ì„Ä£1”°Ù+kÙ#vºˆÎš‚ÞÊ⢒£S8«‘W|ñÚÙ¨%²øKëpɯØ3\—V·v‡#MúQgú?wH­ž@(c„ ž ØsË9›tžôç Ë嶭™{ŸkOó \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/bc/85d1aad435ff3705a8c30ace85f7542c5736cb b/tests/resources/merge-resolve/.gitted/objects/bc/85d1aad435ff3705a8c30ace85f7542c5736cb
new file mode 100644
index 0000000..ae529fe
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/bc/85d1aad435ff3705a8c30ace85f7542c5736cb
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/bc/95c75d59386147d1e79a87c33068d8dbfd71f2 b/tests/resources/merge-resolve/.gitted/objects/bc/95c75d59386147d1e79a87c33068d8dbfd71f2
new file mode 100644
index 0000000..436d5a0
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/bc/95c75d59386147d1e79a87c33068d8dbfd71f2
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/bd/593285fc7fe4ca18ccdbabf027f5d689101452 b/tests/resources/merge-resolve/.gitted/objects/bd/593285fc7fe4ca18ccdbabf027f5d689101452
new file mode 100644
index 0000000..75ab1f0
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/bd/593285fc7fe4ca18ccdbabf027f5d689101452
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/bd/867fbae2faa80b920b002b80b1c91bcade7784 b/tests/resources/merge-resolve/.gitted/objects/bd/867fbae2faa80b920b002b80b1c91bcade7784
new file mode 100644
index 0000000..0f74219
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/bd/867fbae2faa80b920b002b80b1c91bcade7784
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/bd/9cb4cd0a770cb9adcb5fce212142ef40ea1c35 b/tests/resources/merge-resolve/.gitted/objects/bd/9cb4cd0a770cb9adcb5fce212142ef40ea1c35
new file mode 100644
index 0000000..2aafdc6
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/bd/9cb4cd0a770cb9adcb5fce212142ef40ea1c35
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/be/f6e37b3ee632ba74159168836f382fed21d77d b/tests/resources/merge-resolve/.gitted/objects/be/f6e37b3ee632ba74159168836f382fed21d77d
new file mode 100644
index 0000000..6c24315
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/be/f6e37b3ee632ba74159168836f382fed21d77d
@@ -0,0 +1,2 @@
+x¥K
+1D]ç}¥»3?AÄ7p?ô$=Œ`Ò‰x}GñîªÞƒª`)Ý+0Ñ®Uhú.N“WÆÞ“!ÆÐIdl¦™øØjë<ëb®ñ%%Âm±´Z†“nô“.ú¿v–Î@¾Cdjy€=ˆn£ÛyÕ?g\Ñ,IÇ`y~ÜC%]«÷½¿H& \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/c0/6a9be584ac49aa02c5551312d9e2982c91df10 b/tests/resources/merge-resolve/.gitted/objects/c0/6a9be584ac49aa02c5551312d9e2982c91df10
new file mode 100644
index 0000000..963ef23
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/c0/6a9be584ac49aa02c5551312d9e2982c91df10
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/c1/b17981db0840109a820dae8674ee29684134ff b/tests/resources/merge-resolve/.gitted/objects/c1/b17981db0840109a820dae8674ee29684134ff
new file mode 100644
index 0000000..fdcf28c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/c1/b17981db0840109a820dae8674ee29684134ff
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/c1/b6a51bbb87c2f82b161412c3d20b59fc69b090 b/tests/resources/merge-resolve/.gitted/objects/c1/b6a51bbb87c2f82b161412c3d20b59fc69b090
new file mode 100644
index 0000000..3b369f8
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/c1/b6a51bbb87c2f82b161412c3d20b59fc69b090
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/c3/5dee9bcc0e989f3b0c40f68372a9a51b6c4e6a b/tests/resources/merge-resolve/.gitted/objects/c3/5dee9bcc0e989f3b0c40f68372a9a51b6c4e6a
new file mode 100644
index 0000000..d22b3b2
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/c3/5dee9bcc0e989f3b0c40f68372a9a51b6c4e6a
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/c3/d02eeef75183df7584d8d13ac03053910c1301 b/tests/resources/merge-resolve/.gitted/objects/c3/d02eeef75183df7584d8d13ac03053910c1301
new file mode 100644
index 0000000..2294f01
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/c3/d02eeef75183df7584d8d13ac03053910c1301
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/c4/efe31e9decccc8b2b4d3df9aac2cdfe2995618 b/tests/resources/merge-resolve/.gitted/objects/c4/efe31e9decccc8b2b4d3df9aac2cdfe2995618
new file mode 100644
index 0000000..c7572d5
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/c4/efe31e9decccc8b2b4d3df9aac2cdfe2995618
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/c5/0d0f1cb60b8b0fe1615ad20ace557e9d68d7bd b/tests/resources/merge-resolve/.gitted/objects/c5/0d0f1cb60b8b0fe1615ad20ace557e9d68d7bd
new file mode 100644
index 0000000..a1d5321
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/c5/0d0f1cb60b8b0fe1615ad20ace557e9d68d7bd
@@ -0,0 +1 @@
+x¥ŽKj1D³Ö)t˜–4ê–À˜l|ï>-f ‚¯9äÞU½‚â¥Vë6¤¶ô1:³Ì¸èè‰ !ÆüÀå>Z.¼P…0“x„Îû‘ ²¡h˜ÑèhQÖ+tÎ`1NÎZe¢,ÂÏX[—×ü =ËÛÚêÑvyæI_é‹ÿ†ÿvJ­^¤2 í$?ÁˆI§ìà7oÄ4•ï©íå{Kã>VÞú!~|”U= \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/c5/bbe550b9f09444bdddd3ecf3d97c0b42aa786c b/tests/resources/merge-resolve/.gitted/objects/c5/bbe550b9f09444bdddd3ecf3d97c0b42aa786c
new file mode 100644
index 0000000..2f2ada7
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/c5/bbe550b9f09444bdddd3ecf3d97c0b42aa786c
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/c6/07fc30883e335def28cd686b51f6cfa02b06ec b/tests/resources/merge-resolve/.gitted/objects/c6/07fc30883e335def28cd686b51f6cfa02b06ec
new file mode 100644
index 0000000..475b87e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/c6/07fc30883e335def28cd686b51f6cfa02b06ec
@@ -0,0 +1,2 @@
+x¥Q
+1 Dýî)r%±›n "þx/m[°[¨¯ooàßÌ{0k)¹…i×›*`â£ZavJ>,‚aò¢ìf<EZÈÍÉȳ¯µÁ5½¤%¸­µ<ê'ô“.ú¿vˆµœ,;ë]€=2¢tœwýsÆä-÷,wóÊ8@° \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/c6/92ecf62007c0ac9fb26e2aa884de2933de15ed b/tests/resources/merge-resolve/.gitted/objects/c6/92ecf62007c0ac9fb26e2aa884de2933de15ed
new file mode 100644
index 0000000..ae430bd
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/c6/92ecf62007c0ac9fb26e2aa884de2933de15ed
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/c8/26ef8b17b5cd2c4a0023f265f3a423b3aa0388 b/tests/resources/merge-resolve/.gitted/objects/c8/26ef8b17b5cd2c4a0023f265f3a423b3aa0388
new file mode 100644
index 0000000..b655d7c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/c8/26ef8b17b5cd2c4a0023f265f3a423b3aa0388
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/c8/f06f2e3bb2964174677e91f0abead0e43c9e5d b/tests/resources/merge-resolve/.gitted/objects/c8/f06f2e3bb2964174677e91f0abead0e43c9e5d
new file mode 100644
index 0000000..5dae4c3
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/c8/f06f2e3bb2964174677e91f0abead0e43c9e5d
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/c9/174cef549ec94ecbc43ef03cdc775b4950becb b/tests/resources/merge-resolve/.gitted/objects/c9/174cef549ec94ecbc43ef03cdc775b4950becb
new file mode 100644
index 0000000..da8dba2
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/c9/174cef549ec94ecbc43ef03cdc775b4950becb
@@ -0,0 +1,2 @@
+x¥ŽQ
+Â0DýÎ)rÊf“nSñÇxífCÛHŒx}«xÿfÞÀc¤,˵Y ´kUÕbïò8‚pÔu`%—|@rä3GtBÀ™;W]›‚!‹‡½zß'Í%Q¤iÓdœ€T ?Û\ª=§×d/sYeµÝè'ô;üÚ^Êr´Î÷#l`6ºmú§Æ ßZ7U^e6oVòO´ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/c9/4b27e41064c521120627e07e2035cca1d24ffa b/tests/resources/merge-resolve/.gitted/objects/c9/4b27e41064c521120627e07e2035cca1d24ffa
new file mode 100644
index 0000000..fd1ec9f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/c9/4b27e41064c521120627e07e2035cca1d24ffa
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ca/b2cf23998b40f1af2d9d9a756dc9e285a8df4b b/tests/resources/merge-resolve/.gitted/objects/ca/b2cf23998b40f1af2d9d9a756dc9e285a8df4b
new file mode 100644
index 0000000..32ba2aa
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ca/b2cf23998b40f1af2d9d9a756dc9e285a8df4b
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ca/ff6b7d44973f53e3e0cf31d0d695188b19aec6 b/tests/resources/merge-resolve/.gitted/objects/ca/ff6b7d44973f53e3e0cf31d0d695188b19aec6
new file mode 100644
index 0000000..6d0f600
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ca/ff6b7d44973f53e3e0cf31d0d695188b19aec6
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/cb/491780d82e46dc88a065b965ab307a038f2bc2 b/tests/resources/merge-resolve/.gitted/objects/cb/491780d82e46dc88a065b965ab307a038f2bc2
new file mode 100644
index 0000000..cf9cd7d
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/cb/491780d82e46dc88a065b965ab307a038f2bc2
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/cb/6693a788715b82440a54e0eacd19ba9f6ec559 b/tests/resources/merge-resolve/.gitted/objects/cb/6693a788715b82440a54e0eacd19ba9f6ec559
new file mode 100644
index 0000000..e11181a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/cb/6693a788715b82440a54e0eacd19ba9f6ec559
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/cc/338e4710c9b257106b8d16d82f86458d5beaf1 b/tests/resources/merge-resolve/.gitted/objects/cc/338e4710c9b257106b8d16d82f86458d5beaf1
new file mode 100644
index 0000000..85b3b81
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/cc/338e4710c9b257106b8d16d82f86458d5beaf1
@@ -0,0 +1,2 @@
+x¥ŽK!]sŠ¾€†æ3`bŒoà »ÉÌ1 Æë‹gpW©E½G­Öm€±x]“ˆ˜Èù6d
+ûƒçeaŽÎ‰ç¢µz¥.ϬŽDv Ù[êhŽ¥äD³[´Jﱶwþ¤ÎðX[ÝÛ.2ínu£ÞöVƉZ½Ú³F´!x8ê8÷¦G‡ü‘PÂÛP_’?KN \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/cc/3e3009134cb88014129fc8858d1101359e5e2f b/tests/resources/merge-resolve/.gitted/objects/cc/3e3009134cb88014129fc8858d1101359e5e2f
new file mode 100644
index 0000000..9a0cb7a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/cc/3e3009134cb88014129fc8858d1101359e5e2f
@@ -0,0 +1,2 @@
+x¥Ž]
+Â0„}Î)ö•Íß&_¼H“ -ØFbÔë[Åø6ó}0LªË2wPÆìzc†’­Ë*“sXbö‚ Rt”®#Gë$‰[l¼vH„®$ÞkÖÚf.ʧLžF+ ¥QHœD|ô©68çWl.S]îu…oô“Nü¿¶Ou9‚ÔVa0^ZÅF·³ÿœ½ÍÏ9^#Þ ØOd \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/cd/3e8d4aa06bdc781f264171030bc28f2b370fee b/tests/resources/merge-resolve/.gitted/objects/cd/3e8d4aa06bdc781f264171030bc28f2b370fee
new file mode 100644
index 0000000..144225d
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/cd/3e8d4aa06bdc781f264171030bc28f2b370fee
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ce/8860d49e3bea6fd745874a01b7c3e46da8cbc3 b/tests/resources/merge-resolve/.gitted/objects/ce/8860d49e3bea6fd745874a01b7c3e46da8cbc3
new file mode 100644
index 0000000..860f995
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ce/8860d49e3bea6fd745874a01b7c3e46da8cbc3
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ce/e656c392ad0557b3aae0fb411475c206e2926f b/tests/resources/merge-resolve/.gitted/objects/ce/e656c392ad0557b3aae0fb411475c206e2926f
new file mode 100644
index 0000000..ff0624c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ce/e656c392ad0557b3aae0fb411475c206e2926f
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/cf/8c5cc8a85a1ff5a4ba51e0bc7cf5665669924d b/tests/resources/merge-resolve/.gitted/objects/cf/8c5cc8a85a1ff5a4ba51e0bc7cf5665669924d
new file mode 100644
index 0000000..36b0289
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/cf/8c5cc8a85a1ff5a4ba51e0bc7cf5665669924d
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/d0/7ec190c306ec690bac349e87d01c4358e49bb2 b/tests/resources/merge-resolve/.gitted/objects/d0/7ec190c306ec690bac349e87d01c4358e49bb2
new file mode 100644
index 0000000..d52a56f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d0/7ec190c306ec690bac349e87d01c4358e49bb2
@@ -0,0 +1,2 @@
+xÕ± €0 ©™Â3¤ažObˆ%Ç‘S°=HTŒ@u:]uYG¦´¥%šLÚE™–;¸’uÌ`_?gŒÎ~0²òßÒ”.
+׋Pëû£ÓœñxvXië Ó­f! \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/d0/d4594e16f2e19107e3fa7ea63e7aaaff305ffb b/tests/resources/merge-resolve/.gitted/objects/d0/d4594e16f2e19107e3fa7ea63e7aaaff305ffb
new file mode 100644
index 0000000..5f7e286
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d0/d4594e16f2e19107e3fa7ea63e7aaaff305ffb
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/d2/f8637f2eab2507a1e13cbc9df4729ec386627e b/tests/resources/merge-resolve/.gitted/objects/d2/f8637f2eab2507a1e13cbc9df4729ec386627e
new file mode 100644
index 0000000..558a851
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d2/f8637f2eab2507a1e13cbc9df4729ec386627e
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/d3/3cedf513c059e0515653fa2c2e386631387a05 b/tests/resources/merge-resolve/.gitted/objects/d3/3cedf513c059e0515653fa2c2e386631387a05
new file mode 100644
index 0000000..d6d4c2b
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d3/3cedf513c059e0515653fa2c2e386631387a05
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/d3/719a5ae8e4d92276b5313ce976f6ee5af2b436 b/tests/resources/merge-resolve/.gitted/objects/d3/719a5ae8e4d92276b5313ce976f6ee5af2b436
new file mode 100644
index 0000000..930bf5a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d3/719a5ae8e4d92276b5313ce976f6ee5af2b436
@@ -0,0 +1,2 @@
+x=1Â0 E™}Š¿uAH…Ø¨Ä z‡¸ÔRHPÔŸ´T –¿÷ìBrh/ç]?ªaÐ ˜48¡,‘_â¡MúdköÄуTPF!«÷›TïZµQ?
+RÖ§F«Në™ÕÊÒ_J͆ÃÔ Øh’ª{Ÿßò(kÒËLKV1p–+Q‹êÑÕÝÑ® A× \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/d3/7aa3bbfe1c0c49b909781251b956dbabe85f96 b/tests/resources/merge-resolve/.gitted/objects/d3/7aa3bbfe1c0c49b909781251b956dbabe85f96
new file mode 100644
index 0000000..5902e0f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d3/7aa3bbfe1c0c49b909781251b956dbabe85f96
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/d3/7ad72a2052685fc6201c2af90103ad42d2079b b/tests/resources/merge-resolve/.gitted/objects/d3/7ad72a2052685fc6201c2af90103ad42d2079b
new file mode 100644
index 0000000..b2f39bf
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d3/7ad72a2052685fc6201c2af90103ad42d2079b
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/d4/207f77243500bec335ab477f9227fcdb1e271a b/tests/resources/merge-resolve/.gitted/objects/d4/207f77243500bec335ab477f9227fcdb1e271a
new file mode 100644
index 0000000..862e4e5
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d4/207f77243500bec335ab477f9227fcdb1e271a
@@ -0,0 +1,2 @@
+x¥ŽK
+1D]ç¹€’O'™7ÞÀ½t:ÝŒ‹™H&âõŸ«zŠ¢:Ï÷®Ä]oÌZÀBñBXlô(ƒƒ“昭+d<"«6^ºÎ%ŒÞ A( ¡ˆJÆ,Æ% %£5‚SøìSmúR^ØŠ¾Nu^뢼ÑO:óWüÚê|ÒÖ ÆèôÞDcÔF·³ÿœQEn¸¯½6õÔ#Qç \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46 b/tests/resources/merge-resolve/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46
new file mode 100644
index 0000000..0b3611a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/d5/093787ef302b941b6aab081b99fb4880038bd8 b/tests/resources/merge-resolve/.gitted/objects/d5/093787ef302b941b6aab081b99fb4880038bd8
new file mode 100644
index 0000000..7d73449
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d5/093787ef302b941b6aab081b99fb4880038bd8
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/d5/a61b0b4992a4f0caa887fa08b52431e727bb6f b/tests/resources/merge-resolve/.gitted/objects/d5/a61b0b4992a4f0caa887fa08b52431e727bb6f
new file mode 100644
index 0000000..a7921de
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d5/a61b0b4992a4f0caa887fa08b52431e727bb6f
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/d5/b6fc965c926a1bfc9ee456042b94088b5c5d21 b/tests/resources/merge-resolve/.gitted/objects/d5/b6fc965c926a1bfc9ee456042b94088b5c5d21
new file mode 100644
index 0000000..924bdbb
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d5/b6fc965c926a1bfc9ee456042b94088b5c5d21
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/d5/ec1152fe25e9fec00189eb00b3db71db24c218 b/tests/resources/merge-resolve/.gitted/objects/d5/ec1152fe25e9fec00189eb00b3db71db24c218
new file mode 100644
index 0000000..0d2534b
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d5/ec1152fe25e9fec00189eb00b3db71db24c218
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/d6/42b9770c66bba94a08df09b5efb095001f76d7 b/tests/resources/merge-resolve/.gitted/objects/d6/42b9770c66bba94a08df09b5efb095001f76d7
new file mode 100644
index 0000000..1671f9f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d6/42b9770c66bba94a08df09b5efb095001f76d7
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/d6/462fa3f5292857db599c54aea2bf91616230c5 b/tests/resources/merge-resolve/.gitted/objects/d6/462fa3f5292857db599c54aea2bf91616230c5
new file mode 100644
index 0000000..baae3f0
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d6/462fa3f5292857db599c54aea2bf91616230c5
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/d6/cf6c7741b3316826af1314042550c97ded1d50 b/tests/resources/merge-resolve/.gitted/objects/d6/cf6c7741b3316826af1314042550c97ded1d50
new file mode 100644
index 0000000..8f9ae1f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d6/cf6c7741b3316826af1314042550c97ded1d50
@@ -0,0 +1,2 @@
+x¥Q
+1 Dýî)r%µ¦i@Äoàj›e»…ÚÅë[Åø7ó̤Zʽƒ¿éMSLB‘­Nl°ìm öÑBºÉ~×>×—üŠ-Ãu®åY8ê ŸtÖ¯øµ]ªåÖQaǶèÍ ã¼ëŸ3f]š>b×lÞ(„A] \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/d7/308cc367b2cc23f710834ec1fd8ffbacf1b460 b/tests/resources/merge-resolve/.gitted/objects/d7/308cc367b2cc23f710834ec1fd8ffbacf1b460
new file mode 100644
index 0000000..b02cda4
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d7/308cc367b2cc23f710834ec1fd8ffbacf1b460
@@ -0,0 +1 @@
+x¥K @]sŠ¹€†)ÐÄ7ÞÀ ð™¦]ÐI(Æx{ñ î^Þâ½,µnлSoÌ`¬/¹Xä)Ù™B@GžÃ”¸œaòD¼ «øê«4x”wlž«ÔCv¸ò°?º×-79dé—,õh‚F4Žœµ×Z ;ÆÿH¨´í±}Ô·š=  \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/d8/74671ef5b20184836cb983bb273e5280384d0b b/tests/resources/merge-resolve/.gitted/objects/d8/74671ef5b20184836cb983bb273e5280384d0b
new file mode 100644
index 0000000..1d80378
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d8/74671ef5b20184836cb983bb273e5280384d0b
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/d8/dec75ff2f8b41d1c5bfef0cd57b7300c834f66 b/tests/resources/merge-resolve/.gitted/objects/d8/dec75ff2f8b41d1c5bfef0cd57b7300c834f66
new file mode 100644
index 0000000..74f807e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d8/dec75ff2f8b41d1c5bfef0cd57b7300c834f66
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/d8/fa77b6833082c1ea36b7828a582d4c43882450 b/tests/resources/merge-resolve/.gitted/objects/d8/fa77b6833082c1ea36b7828a582d4c43882450
new file mode 100644
index 0000000..9881453
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d8/fa77b6833082c1ea36b7828a582d4c43882450
@@ -0,0 +1 @@
+xíÐ1€ DQkN1¥–&6‡•%l²BkŒ·naa1Åk¦ø¤…°íëdI¢(ãU£rö'7‰‘»LŸ’AÅ,±+Wmð9 ŒI'UŸÄ͹ÿñ£_ÛÜ°N \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/d9/63979c237d08b6ba39062ee7bf64c7d34a27f8 b/tests/resources/merge-resolve/.gitted/objects/d9/63979c237d08b6ba39062ee7bf64c7d34a27f8
new file mode 100644
index 0000000..5fa1040
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/d9/63979c237d08b6ba39062ee7bf64c7d34a27f8
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/da/178208145ef585a1bd5ca5f4c9785d738df2cf b/tests/resources/merge-resolve/.gitted/objects/da/178208145ef585a1bd5ca5f4c9785d738df2cf
new file mode 100644
index 0000000..6292118
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/da/178208145ef585a1bd5ca5f4c9785d738df2cf
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/db/6261a7c65c7fd678520c9bb6f2c47582ab9ed5 b/tests/resources/merge-resolve/.gitted/objects/db/6261a7c65c7fd678520c9bb6f2c47582ab9ed5
new file mode 100644
index 0000000..b82e7fc
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/db/6261a7c65c7fd678520c9bb6f2c47582ab9ed5
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/dd/2ae5ab264e5592aa754235d5ad5eac8f0ecdfd b/tests/resources/merge-resolve/.gitted/objects/dd/2ae5ab264e5592aa754235d5ad5eac8f0ecdfd
new file mode 100644
index 0000000..55626a5
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/dd/2ae5ab264e5592aa754235d5ad5eac8f0ecdfd
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/dd/9a570c3400e6e07bc4d7651d6e20b08926b3d9 b/tests/resources/merge-resolve/.gitted/objects/dd/9a570c3400e6e07bc4d7651d6e20b08926b3d9
new file mode 100644
index 0000000..8fd60cb
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/dd/9a570c3400e6e07bc4d7651d6e20b08926b3d9
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/de/872ee3618b894992e9d1e18ba2ebe256a112f9 b/tests/resources/merge-resolve/.gitted/objects/de/872ee3618b894992e9d1e18ba2ebe256a112f9
new file mode 100644
index 0000000..04dda4a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/de/872ee3618b894992e9d1e18ba2ebe256a112f9
@@ -0,0 +1 @@
+xí± À S3Å‚…ŒlK–A†ˆõƒ²BÚ4W®¸b­ Ÿù˜—T5Á¢:§8ÔS»c€œ±Ô Ô»P`KäI¥Ë†O3Z½•”þàç‡&ØÝ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/df/e3f22baa1f6fce5447901c3086bae368de6bdd b/tests/resources/merge-resolve/.gitted/objects/df/e3f22baa1f6fce5447901c3086bae368de6bdd
new file mode 100644
index 0000000..e135694
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/df/e3f22baa1f6fce5447901c3086bae368de6bdd
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/e0/67f9361140f19391472df8a82d6610813c73b7 b/tests/resources/merge-resolve/.gitted/objects/e0/67f9361140f19391472df8a82d6610813c73b7
new file mode 100644
index 0000000..955431d
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/e0/67f9361140f19391472df8a82d6610813c73b7
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/e1/129b3cfb5898e0fbd606e0cb80b2755e50d161 b/tests/resources/merge-resolve/.gitted/objects/e1/129b3cfb5898e0fbd606e0cb80b2755e50d161
new file mode 100644
index 0000000..751f1dd
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/e1/129b3cfb5898e0fbd606e0cb80b2755e50d161
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/e1/7ace1492648c9dc5701bad5c47af9d1b60c4e9 b/tests/resources/merge-resolve/.gitted/objects/e1/7ace1492648c9dc5701bad5c47af9d1b60c4e9
new file mode 100644
index 0000000..4a812e5
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/e1/7ace1492648c9dc5701bad5c47af9d1b60c4e9
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/e2/6b8888956137218d8589368a3e606cf50fbb56 b/tests/resources/merge-resolve/.gitted/objects/e2/6b8888956137218d8589368a3e606cf50fbb56
new file mode 100644
index 0000000..d4ec2b9
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/e2/6b8888956137218d8589368a3e606cf50fbb56
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/e2/c6abbd55fed5ac71a5f2751e29b4a34726a595 b/tests/resources/merge-resolve/.gitted/objects/e2/c6abbd55fed5ac71a5f2751e29b4a34726a595
new file mode 100644
index 0000000..7b84ce9
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/e2/c6abbd55fed5ac71a5f2751e29b4a34726a595
@@ -0,0 +1 @@
+x+)JMU067f040031QH,-ÉÏM-JOMLÊIÕ+©(aH:,»:ÎCÉýúô:ÞË ²o>ZC'g$楧¦èfæé&%æ%g€5¬ÎqYôÂeÒZoÇÝÙkÚMíæ2­éÆÔ›X\’ZDPC~^ZNfrIf^:Xéõ›ZHÙŠž1O(_œ,'º× jvn~JfZ&Ô5ù%·˜º³\N´º¢ÔçÞö§,5[ðe“Ù¨ú¢ÔÜü2 ÇßüI{•|þ¹÷ 2m»gÜ˾‹©Éñ1ÖËåüŠ5Ó¿Ü,\“µ})TC)0DÀvú‰ùzÖ›¦9–¤×Mü°úÕ…'6ób‹” \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/e3/1e7ad3ed298f24e383c4950f4671993ec078e4 b/tests/resources/merge-resolve/.gitted/objects/e3/1e7ad3ed298f24e383c4950f4671993ec078e4
new file mode 100644
index 0000000..a28ded3
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/e3/1e7ad3ed298f24e383c4950f4671993ec078e4
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/e3/76fbdd06ebf021c92724da9f26f44212734e3e b/tests/resources/merge-resolve/.gitted/objects/e3/76fbdd06ebf021c92724da9f26f44212734e3e
new file mode 100644
index 0000000..8da2341
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/e3/76fbdd06ebf021c92724da9f26f44212734e3e
@@ -0,0 +1,3 @@
+xíAÂ@E]s
+`ö@Ì uHš)M=¾Scô®Üþü’zÂé:¢ÊŠ³(ãN+6Þ›D°¡Feð­­˜Y®g$+GˆÞä&F
+Ÿ½ì‹p‡þâG —4”mQÉ\±á85Æ#FìCð¥ï~QEóÀÄÀÚR—š½u)£;cáàâ6ü­öë'ÍjÄÇ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/e4/9f917b448d1340b31d76e54ba388268fd4c922 b/tests/resources/merge-resolve/.gitted/objects/e4/9f917b448d1340b31d76e54ba388268fd4c922
new file mode 100644
index 0000000..870c3e7
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/e4/9f917b448d1340b31d76e54ba388268fd4c922
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/e4/f618a2c3ed0669308735727df5ebf2447f022f b/tests/resources/merge-resolve/.gitted/objects/e4/f618a2c3ed0669308735727df5ebf2447f022f
new file mode 100644
index 0000000..c7e1ee9
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/e4/f618a2c3ed0669308735727df5ebf2447f022f
@@ -0,0 +1,2 @@
+x¥ŽK!D]s
+. išObŒoà i2. b¼¾h¼»ª÷’JQ«õ6äb`7:³DN.%•±œ4¹’u¦‹ÏÄIQñYÜcçmÈàÙ`Q¨ aQYa¶@>äÄÉ—Ecñ9ÖÖå%¿bÏòº¶úh›<ò¤Ÿtæ¯øµµz’J£òÁ(¹“γƒÿœFCñ_‹NŒ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/e5/060729746ca9888239cba08fdcf4bee907b406 b/tests/resources/merge-resolve/.gitted/objects/e5/060729746ca9888239cba08fdcf4bee907b406
new file mode 100644
index 0000000..33299c2
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/e5/060729746ca9888239cba08fdcf4bee907b406
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/e6/5a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812 b/tests/resources/merge-resolve/.gitted/objects/e6/5a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812
new file mode 100644
index 0000000..72f1cbc
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/e6/5a9bb2af9f4c2d1c375dd0f8f8a46cf9c68812
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/merge-resolve/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644
index 0000000..7112238
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/e8/107f24196736b870a318a0e28f048e29f6feff b/tests/resources/merge-resolve/.gitted/objects/e8/107f24196736b870a318a0e28f048e29f6feff
new file mode 100644
index 0000000..ffcf843
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/e8/107f24196736b870a318a0e28f048e29f6feff
@@ -0,0 +1,3 @@
+x¥ŽK!D]s
+. i~=LbŒoàÞ@ÓdfãõEã ÜU½—TŠj)k—Úš]oÌÒ¯mð„èü„ž5(3ƒÓj&Ô™4Íâß»póp™iRI;‘³·¨“Â9Ò‚²Q„g_j“—ô
+-ÉëRËVïòȃ~Ò™¿â×TËI*ãÜtr g;ÿ9#R¾mkb%ÞrLNÁ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/e9/2cdb7017dc6c5aed25cb4202c5b0104b872246 b/tests/resources/merge-resolve/.gitted/objects/e9/2cdb7017dc6c5aed25cb4202c5b0104b872246
new file mode 100644
index 0000000..cb1260e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/e9/2cdb7017dc6c5aed25cb4202c5b0104b872246
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/e9/ad6ec3e38364a3d07feda7c4197d4d845c53b5 b/tests/resources/merge-resolve/.gitted/objects/e9/ad6ec3e38364a3d07feda7c4197d4d845c53b5
new file mode 100644
index 0000000..da4a5ed
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/e9/ad6ec3e38364a3d07feda7c4197d4d845c53b5
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/e9/f48beccc62d535739bfbdebe0a55ed716d8366 b/tests/resources/merge-resolve/.gitted/objects/e9/f48beccc62d535739bfbdebe0a55ed716d8366
new file mode 100644
index 0000000..23c59e4
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/e9/f48beccc62d535739bfbdebe0a55ed716d8366
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/eb/c09d0137cfb0c26697aed0109fb943ad906f3f b/tests/resources/merge-resolve/.gitted/objects/eb/c09d0137cfb0c26697aed0109fb943ad906f3f
new file mode 100644
index 0000000..83b489d
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/eb/c09d0137cfb0c26697aed0109fb943ad906f3f
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ec/67e5a86adff465359f1c8f995e12dbdfa08d8a b/tests/resources/merge-resolve/.gitted/objects/ec/67e5a86adff465359f1c8f995e12dbdfa08d8a
new file mode 100644
index 0000000..8490346
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ec/67e5a86adff465359f1c8f995e12dbdfa08d8a
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ed/9523e62e453e50dd9be1606af19399b96e397a b/tests/resources/merge-resolve/.gitted/objects/ed/9523e62e453e50dd9be1606af19399b96e397a
new file mode 100644
index 0000000..7853e23
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ed/9523e62e453e50dd9be1606af19399b96e397a
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ee/1d6f164893c1866a323f072eeed36b855656be b/tests/resources/merge-resolve/.gitted/objects/ee/1d6f164893c1866a323f072eeed36b855656be
new file mode 100644
index 0000000..87d8080
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ee/1d6f164893c1866a323f072eeed36b855656be
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf b/tests/resources/merge-resolve/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf
new file mode 100644
index 0000000..974b72d
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ee/a9286df54245fea72c5b557291470eb825f38f b/tests/resources/merge-resolve/.gitted/objects/ee/a9286df54245fea72c5b557291470eb825f38f
new file mode 100644
index 0000000..ead0b2c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ee/a9286df54245fea72c5b557291470eb825f38f
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ef/58fdd8086c243bdc81f99e379acacfd21d32d6 b/tests/resources/merge-resolve/.gitted/objects/ef/58fdd8086c243bdc81f99e379acacfd21d32d6
new file mode 100644
index 0000000..55f79e0
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ef/58fdd8086c243bdc81f99e379acacfd21d32d6
@@ -0,0 +1,2 @@
+x ÉÁ À0Оâï:JŠB¢ÝŸæôOV
+Þñ´yáó5éê†5jã†q!’4÷Î{¡³:ýp;¼ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/ef/c499524cf105d5264ac7fc54e07e95764e8075 b/tests/resources/merge-resolve/.gitted/objects/ef/c499524cf105d5264ac7fc54e07e95764e8075
new file mode 100644
index 0000000..bc9350b
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ef/c499524cf105d5264ac7fc54e07e95764e8075
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ef/c9121fdedaf08ba180b53ebfbcf71bd488ed09 b/tests/resources/merge-resolve/.gitted/objects/ef/c9121fdedaf08ba180b53ebfbcf71bd488ed09
new file mode 100644
index 0000000..5f9cd30
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ef/c9121fdedaf08ba180b53ebfbcf71bd488ed09
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/f0/053b8060bb3f0be5cbcc3147a07ece26bf097e b/tests/resources/merge-resolve/.gitted/objects/f0/053b8060bb3f0be5cbcc3147a07ece26bf097e
new file mode 100644
index 0000000..c63d37f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/f0/053b8060bb3f0be5cbcc3147a07ece26bf097e
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/f0/ce2b8e4986084d9b308fb72709e414c23eb5e6 b/tests/resources/merge-resolve/.gitted/objects/f0/ce2b8e4986084d9b308fb72709e414c23eb5e6
new file mode 100644
index 0000000..e78c19f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/f0/ce2b8e4986084d9b308fb72709e414c23eb5e6
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/f2/0c9063fa0bda9a397c96947a7b687305c49753 b/tests/resources/merge-resolve/.gitted/objects/f2/0c9063fa0bda9a397c96947a7b687305c49753
new file mode 100644
index 0000000..34d9aed
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/f2/0c9063fa0bda9a397c96947a7b687305c49753
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/f2/9e7fb590551095230c6149cbe72f2e9104a796 b/tests/resources/merge-resolve/.gitted/objects/f2/9e7fb590551095230c6149cbe72f2e9104a796
new file mode 100644
index 0000000..663f6ae
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/f2/9e7fb590551095230c6149cbe72f2e9104a796
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/f2/e1550a0c9e53d5811175864a29536642ae3821 b/tests/resources/merge-resolve/.gitted/objects/f2/e1550a0c9e53d5811175864a29536642ae3821
new file mode 100644
index 0000000..1fdcbe2
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/f2/e1550a0c9e53d5811175864a29536642ae3821
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/f3/293571dcd708b6a3faf03818cd2844d000e198 b/tests/resources/merge-resolve/.gitted/objects/f3/293571dcd708b6a3faf03818cd2844d000e198
new file mode 100644
index 0000000..f748743
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/f3/293571dcd708b6a3faf03818cd2844d000e198
@@ -0,0 +1 @@
+x¥ŽKjÄ0³Ö)údµZ!›¹Á\@nIعƒ,3×Ͳ{¼¢XZÛë?F/\Eã12zcó’©"#êX1§]µÚ[Ô_êå˜GŒ&™œ˜c©+9³‡”ùXWKÁÓžiUé›t¸çgê›´Sø*“¾×OÛ¹Ë)u|²´oXp*" pÓ¤µšt†Žò…šÅ©`9êïÎäê§zÅKO\ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/f3/f1164b68b57b1995b658a828320e6df3081fae b/tests/resources/merge-resolve/.gitted/objects/f3/f1164b68b57b1995b658a828320e6df3081fae
new file mode 100644
index 0000000..5f0b4e4
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/f3/f1164b68b57b1995b658a828320e6df3081fae
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/f4/15caf3fcad16304cb424b67f0ee6b12dc03aae b/tests/resources/merge-resolve/.gitted/objects/f4/15caf3fcad16304cb424b67f0ee6b12dc03aae
new file mode 100644
index 0000000..21ce1a0
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/f4/15caf3fcad16304cb424b67f0ee6b12dc03aae
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/f4/8097eb340dc5a7cae55aabcf1faf4548aa821f b/tests/resources/merge-resolve/.gitted/objects/f4/8097eb340dc5a7cae55aabcf1faf4548aa821f
new file mode 100644
index 0000000..5a4a9a5
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/f4/8097eb340dc5a7cae55aabcf1faf4548aa821f
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/f5/504f36e6f4eb797a56fc5bac6c6c7f32969bf2 b/tests/resources/merge-resolve/.gitted/objects/f5/504f36e6f4eb797a56fc5bac6c6c7f32969bf2
new file mode 100644
index 0000000..2aa0c3b
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/f5/504f36e6f4eb797a56fc5bac6c6c7f32969bf2
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/f5/b50c85a87cac64d7eb3254cdd1aec9564c0293 b/tests/resources/merge-resolve/.gitted/objects/f5/b50c85a87cac64d7eb3254cdd1aec9564c0293
new file mode 100644
index 0000000..c1885cb
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/f5/b50c85a87cac64d7eb3254cdd1aec9564c0293
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/f5/f9dd5886a6ee20272be0aafc790cba43b31931 b/tests/resources/merge-resolve/.gitted/objects/f5/f9dd5886a6ee20272be0aafc790cba43b31931
new file mode 100644
index 0000000..17ad506
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/f5/f9dd5886a6ee20272be0aafc790cba43b31931
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/f6/65b45cde9b568009c6e6b7b568e89cfe717df8 b/tests/resources/merge-resolve/.gitted/objects/f6/65b45cde9b568009c6e6b7b568e89cfe717df8
new file mode 100644
index 0000000..7af50d7
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/f6/65b45cde9b568009c6e6b7b568e89cfe717df8
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/f6/be049e284c0f9dcbbc745543885be3502ea521 b/tests/resources/merge-resolve/.gitted/objects/f6/be049e284c0f9dcbbc745543885be3502ea521
new file mode 100644
index 0000000..12d3c25
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/f6/be049e284c0f9dcbbc745543885be3502ea521
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/f7/c332bd4d4d4b777366cae4d24d1687477576bf b/tests/resources/merge-resolve/.gitted/objects/f7/c332bd4d4d4b777366cae4d24d1687477576bf
new file mode 100644
index 0000000..b36bcea
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/f7/c332bd4d4d4b777366cae4d24d1687477576bf
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/f8/958bdf4d365a84a9a178b1f5f35ff1dacbd884 b/tests/resources/merge-resolve/.gitted/objects/f8/958bdf4d365a84a9a178b1f5f35ff1dacbd884
new file mode 100644
index 0000000..5dbbef2
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/f8/958bdf4d365a84a9a178b1f5f35ff1dacbd884
@@ -0,0 +1,2 @@
+x¥ŽK
+1D]ç}¥ÓI¦qã ÜK>f1’‰x}£xwU¯àQ±–²v kv½‰@`OŽ§<çHˆd}œ%kŽA›‘CÒ>²ÑÄêá›Ü;$KÈ™ybhŒóÁ2癈sLA ±öÊ?ûR\ÒË·×¥–­Þá(ƒ~ÒY¾Ã¯b-'ÐÆYÇŽp‚=NˆjÐq¶ËŸ•òm[“zí O+ \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/f9/7da95f156121bea8f978623628f4cbdbf30b36 b/tests/resources/merge-resolve/.gitted/objects/f9/7da95f156121bea8f978623628f4cbdbf30b36
new file mode 100644
index 0000000..d785511
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/f9/7da95f156121bea8f978623628f4cbdbf30b36
@@ -0,0 +1 @@
+xŽ1!E­9½‰…)Œ±±´óÃ0¸[¬–ׯà¯^^ò’Ïu]—®§)zщ²%ÊÞA). dgˆ¡DðCt“zS“W×dM‹X²cÁb3gðì/’‚¢½Ïµé[þPËú1×u«/}–at}.}ÞÓ‰ëzÑÖc€Æ¡>š15ìxÙåß^ÝiµúêòHø \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/fa/c03f2c5139618d87d53614c153823bf1f31396 b/tests/resources/merge-resolve/.gitted/objects/fa/c03f2c5139618d87d53614c153823bf1f31396
new file mode 100644
index 0000000..30e07e5
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/fa/c03f2c5139618d87d53614c153823bf1f31396
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/fa/da9356aa3f74622327a3038ae9c6f92e1c5c1d b/tests/resources/merge-resolve/.gitted/objects/fa/da9356aa3f74622327a3038ae9c6f92e1c5c1d
new file mode 100644
index 0000000..16ce49a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/fa/da9356aa3f74622327a3038ae9c6f92e1c5c1d
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/fb/738a106cfd097a4acb96ce132ecb1ad6c46b03 b/tests/resources/merge-resolve/.gitted/objects/fb/738a106cfd097a4acb96ce132ecb1ad6c46b03
new file mode 100644
index 0000000..4f1e726
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/fb/738a106cfd097a4acb96ce132ecb1ad6c46b03
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/fc/4c636d6515e9e261f9260dbcf3cc6eca97ea08 b/tests/resources/merge-resolve/.gitted/objects/fc/4c636d6515e9e261f9260dbcf3cc6eca97ea08
new file mode 100644
index 0000000..be8a810
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/fc/4c636d6515e9e261f9260dbcf3cc6eca97ea08
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/fc/7d7b805f7a9428574f4f802b2e34cd20ab9d99 b/tests/resources/merge-resolve/.gitted/objects/fc/7d7b805f7a9428574f4f802b2e34cd20ab9d99
new file mode 100644
index 0000000..20493e6
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/fc/7d7b805f7a9428574f4f802b2e34cd20ab9d99
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/fc/90237dc4891fa6c69827fc465632225e391618 b/tests/resources/merge-resolve/.gitted/objects/fc/90237dc4891fa6c69827fc465632225e391618
new file mode 100644
index 0000000..961814b
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/fc/90237dc4891fa6c69827fc465632225e391618
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/fd/57d2d6770fad8e9959124793a17f441b571e66 b/tests/resources/merge-resolve/.gitted/objects/fd/57d2d6770fad8e9959124793a17f441b571e66
new file mode 100644
index 0000000..21e6b2c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/fd/57d2d6770fad8e9959124793a17f441b571e66
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/fd/89f8cffb663ac89095a0f9764902e93ceaca6a b/tests/resources/merge-resolve/.gitted/objects/fd/89f8cffb663ac89095a0f9764902e93ceaca6a
new file mode 100644
index 0000000..2f9d83b
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/fd/89f8cffb663ac89095a0f9764902e93ceaca6a
@@ -0,0 +1,2 @@
+x¥ŽK!D]s
+. ¡{` cÜx/À§É¸`0 ÆëËoà®êURy±–òèõ|èY`ÀdPÌA!±íÜäŒå4C2d=x#ž¾ñÚe`Bgr´™uôàbLÁ‡¬Ðf“fG @þÕ—Úä-½}Kò¾Ô²ÕUžyÐ=]ù;üÚ)Ör‘0 Rˆ$Ê(%²ÿ¼Ùo=׶›ˆ‡OPw \ No newline at end of file
diff --git a/tests/resources/merge-resolve/.gitted/objects/fe/5407fc50a53aecb41d1a6e9ea7b612e581af87 b/tests/resources/merge-resolve/.gitted/objects/fe/5407fc50a53aecb41d1a6e9ea7b612e581af87
new file mode 100644
index 0000000..4ce7d22
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/fe/5407fc50a53aecb41d1a6e9ea7b612e581af87
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ff/49d07869831ad761bbdaea026086f8789bcb00 b/tests/resources/merge-resolve/.gitted/objects/ff/49d07869831ad761bbdaea026086f8789bcb00
new file mode 100644
index 0000000..eada39b
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ff/49d07869831ad761bbdaea026086f8789bcb00
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/objects/ff/b312248d607284c290023f9502eea010d34efd b/tests/resources/merge-resolve/.gitted/objects/ff/b312248d607284c290023f9502eea010d34efd
new file mode 100644
index 0000000..7e46c4f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/objects/ff/b312248d607284c290023f9502eea010d34efd
Binary files differ
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/branch b/tests/resources/merge-resolve/.gitted/refs/heads/branch
new file mode 100644
index 0000000..03f79a3
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/branch
@@ -0,0 +1 @@
+7cb63eed597130ba4abb87b3e544b85021905520
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/delete-submodule b/tests/resources/merge-resolve/.gitted/refs/heads/delete-submodule
new file mode 100644
index 0000000..1951316
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/delete-submodule
@@ -0,0 +1 @@
+50c5dc8cdfe40c688eb0a0e23be54dd57cae2e78
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/df_ancestor b/tests/resources/merge-resolve/.gitted/refs/heads/df_ancestor
new file mode 100644
index 0000000..4bc37ac
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/df_ancestor
@@ -0,0 +1 @@
+2da538570bc1e5b2c3e855bf702f35248ad0735f
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/df_side1 b/tests/resources/merge-resolve/.gitted/refs/heads/df_side1
new file mode 100644
index 0000000..ca6dd67
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/df_side1
@@ -0,0 +1 @@
+a7dbfcbfc1a60709cb80b5ca24539008456531d0
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/df_side2 b/tests/resources/merge-resolve/.gitted/refs/heads/df_side2
new file mode 100644
index 0000000..b8160f8
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/df_side2
@@ -0,0 +1 @@
+fc90237dc4891fa6c69827fc465632225e391618
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/ff_branch b/tests/resources/merge-resolve/.gitted/refs/heads/ff_branch
new file mode 100644
index 0000000..e9e9051
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/ff_branch
@@ -0,0 +1 @@
+fd89f8cffb663ac89095a0f9764902e93ceaca6a
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/master b/tests/resources/merge-resolve/.gitted/refs/heads/master
new file mode 100644
index 0000000..8a329ae
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/master
@@ -0,0 +1 @@
+bd593285fc7fe4ca18ccdbabf027f5d689101452
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/octo1 b/tests/resources/merge-resolve/.gitted/refs/heads/octo1
new file mode 100644
index 0000000..4d2c669
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/octo1
@@ -0,0 +1 @@
+16f825815cfd20a07a75c71554e82d8eede0b061
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/octo2 b/tests/resources/merge-resolve/.gitted/refs/heads/octo2
new file mode 100644
index 0000000..f503977
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/octo2
@@ -0,0 +1 @@
+158dc7bedb202f5b26502bf3574faa7f4238d56c
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/octo3 b/tests/resources/merge-resolve/.gitted/refs/heads/octo3
new file mode 100644
index 0000000..b92994f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/octo3
@@ -0,0 +1 @@
+50ce7d7d01217679e26c55939eef119e0c93e272
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/octo4 b/tests/resources/merge-resolve/.gitted/refs/heads/octo4
new file mode 100644
index 0000000..f33d57c
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/octo4
@@ -0,0 +1 @@
+54269b3f6ec3d7d4ede24dd350dd5d605495c3ae
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/octo5 b/tests/resources/merge-resolve/.gitted/refs/heads/octo5
new file mode 100644
index 0000000..e9f9433
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/octo5
@@ -0,0 +1 @@
+e4f618a2c3ed0669308735727df5ebf2447f022f
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/octo6 b/tests/resources/merge-resolve/.gitted/refs/heads/octo6
new file mode 100644
index 0000000..4c5a98a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/octo6
@@ -0,0 +1 @@
+b6f610aef53bd343e6c96227de874c66f00ee8e8
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/previous b/tests/resources/merge-resolve/.gitted/refs/heads/previous
new file mode 100644
index 0000000..7bc1a8d
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/previous
@@ -0,0 +1 @@
+c607fc30883e335def28cd686b51f6cfa02b06ec
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ancestor b/tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ancestor
new file mode 100644
index 0000000..4092d42
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ancestor
@@ -0,0 +1 @@
+2392a2dacc9efb562b8635d6579fb458751c7c5b
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ours b/tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ours
new file mode 100644
index 0000000..a1c50dc
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_ours
@@ -0,0 +1 @@
+34bfafff88eaf118402b44e6f3e2dbbf1a582b05
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_theirs b/tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_theirs
new file mode 100644
index 0000000..1309893
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/rename_conflict_theirs
@@ -0,0 +1 @@
+a802e06f1782a9645b9851bc7202cee74a8a4972
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/renames1 b/tests/resources/merge-resolve/.gitted/refs/heads/renames1
new file mode 100644
index 0000000..3d24810
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/renames1
@@ -0,0 +1 @@
+412b32fb66137366147f1801ecc962452757d48a
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/renames2 b/tests/resources/merge-resolve/.gitted/refs/heads/renames2
new file mode 100644
index 0000000..d226215
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/renames2
@@ -0,0 +1 @@
+ab40af3cb8a3ed2e2843e96d9aa7871336b94573
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/submodule_rename1 b/tests/resources/merge-resolve/.gitted/refs/heads/submodule_rename1
new file mode 100644
index 0000000..0ed914f
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/submodule_rename1
@@ -0,0 +1 @@
+f97da95f156121bea8f978623628f4cbdbf30b36
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/submodule_rename2 b/tests/resources/merge-resolve/.gitted/refs/heads/submodule_rename2
new file mode 100644
index 0000000..8e020cc
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/submodule_rename2
@@ -0,0 +1 @@
+37f53a5a14f64e91089a39ea58e71c87d81df765
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/submodules b/tests/resources/merge-resolve/.gitted/refs/heads/submodules
new file mode 100644
index 0000000..e5511ec
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/submodules
@@ -0,0 +1 @@
+d8dec75ff2f8b41d1c5bfef0cd57b7300c834f66
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/submodules-branch b/tests/resources/merge-resolve/.gitted/refs/heads/submodules-branch
new file mode 100644
index 0000000..7d47e07
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/submodules-branch
@@ -0,0 +1 @@
+811c70fcb6d5bbd022d04cc31836d30b436f9551
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/submodules-branch2 b/tests/resources/merge-resolve/.gitted/refs/heads/submodules-branch2
new file mode 100644
index 0000000..ced60d8
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/submodules-branch2
@@ -0,0 +1 @@
+7c04ca611203ed320c5f495b9813054dd23be3be
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-10 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-10
new file mode 100644
index 0000000..5b378cd
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-10
@@ -0,0 +1 @@
+0ec5f433959cd46177f745903353efb5be08d151
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-10-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-10-branch
new file mode 100644
index 0000000..b3db6c8
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-10-branch
@@ -0,0 +1 @@
+11f4f3c08b737f5fd896cbefa1425ee63b21b2fa
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-11 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-11
new file mode 100644
index 0000000..154de9a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-11
@@ -0,0 +1 @@
+3168dca1a561889b045a6441909f4c56145e666d
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-11-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-11-branch
new file mode 100644
index 0000000..2e41180
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-11-branch
@@ -0,0 +1 @@
+6718a45909532d1fcf5600d0877f7fe7e78f0b86
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-13 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-13
new file mode 100644
index 0000000..297573a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-13
@@ -0,0 +1 @@
+a3fabece9eb8748da810e1e08266fef9b7136ad4
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-13-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-13-branch
new file mode 100644
index 0000000..22e429a
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-13-branch
@@ -0,0 +1 @@
+05f3c1a2a56ca95c3d2ef28dc9ddf32b5cd6c91c
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-14 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-14
new file mode 100644
index 0000000..8905185
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-14
@@ -0,0 +1 @@
+7e2d058d5fedf8329db44db4fac610d6b1a89159
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-14-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-14-branch
new file mode 100644
index 0000000..0158f95
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-14-branch
@@ -0,0 +1 @@
+8187117062b750eed4f93fd7e899f17b52ce554d
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-2alt b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-2alt
new file mode 100644
index 0000000..4740741
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-2alt
@@ -0,0 +1 @@
+566ab53c220a2eafc1212af1a024513230280ab9
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-2alt-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-2alt-branch
new file mode 100644
index 0000000..2f5f1a4
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-2alt-branch
@@ -0,0 +1 @@
+c9174cef549ec94ecbc43ef03cdc775b4950becb
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-3alt b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-3alt
new file mode 100644
index 0000000..18e50ae
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-3alt
@@ -0,0 +1 @@
+4c9fac0707f8d4195037ae5a681aa48626491541
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-3alt-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-3alt-branch
new file mode 100644
index 0000000..7bc1a8d
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-3alt-branch
@@ -0,0 +1 @@
+c607fc30883e335def28cd686b51f6cfa02b06ec
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-4 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-4
new file mode 100644
index 0000000..f49bbf9
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-4
@@ -0,0 +1 @@
+cc3e3009134cb88014129fc8858d1101359e5e2f
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-4-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-4-branch
new file mode 100644
index 0000000..bff519e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-4-branch
@@ -0,0 +1 @@
+183310e30fb1499af8c619108ffea4d300b5e778
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1
new file mode 100644
index 0000000..963a7b3
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1
@@ -0,0 +1 @@
+4fe93c0ec83eb6305cbace3dace88ecee1b63cb6
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1-branch
new file mode 100644
index 0000000..4a22138
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-1-branch
@@ -0,0 +1 @@
+478172cb2f5ff9b514bc9d04d3bd5ef5840cb3b2
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2
new file mode 100644
index 0000000..aa4ada1
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2
@@ -0,0 +1 @@
+3b47b031b3e55ae11e14a05260b1c3ffd6838d55
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2-branch
new file mode 100644
index 0000000..5553cdb
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-5alt-2-branch
@@ -0,0 +1 @@
+f48097eb340dc5a7cae55aabcf1faf4548aa821f
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-6 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-6
new file mode 100644
index 0000000..fb685bb
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-6
@@ -0,0 +1 @@
+99b4f7e4f24470fa06b980bc21f1095c2a9425c0
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-6-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-6-branch
new file mode 100644
index 0000000..efc4c55
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-6-branch
@@ -0,0 +1 @@
+a43150a738849c59376cf30bb2a68348a83c8f48
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-7 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-7
new file mode 100644
index 0000000..9c94243
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-7
@@ -0,0 +1 @@
+d874671ef5b20184836cb983bb273e5280384d0b
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-7-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-7-branch
new file mode 100644
index 0000000..1762bb5
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-7-branch
@@ -0,0 +1 @@
+5195a1b480f66691b667f10a9e41e70115a78351
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-8 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-8
new file mode 100644
index 0000000..837c491
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-8
@@ -0,0 +1 @@
+3575826c96a975031d2c14368529cc5c4353a8fd
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-8-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-8-branch
new file mode 100644
index 0000000..874230e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-8-branch
@@ -0,0 +1 @@
+52d8bc572af2b6d4ee0d5e62ed5d1fbad92210a9
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-9 b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-9
new file mode 100644
index 0000000..b968a3e
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-9
@@ -0,0 +1 @@
+c35dee9bcc0e989f3b0c40f68372a9a51b6c4e6a
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/trivial-9-branch b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-9-branch
new file mode 100644
index 0000000..7f3097b
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/trivial-9-branch
@@ -0,0 +1 @@
+13d1be4ea52a6ced1d7a1d832f0ee3c399348e5e
diff --git a/tests/resources/merge-resolve/.gitted/refs/heads/unrelated b/tests/resources/merge-resolve/.gitted/refs/heads/unrelated
new file mode 100644
index 0000000..bb877be
--- /dev/null
+++ b/tests/resources/merge-resolve/.gitted/refs/heads/unrelated
@@ -0,0 +1 @@
+55b4e4687e7a0d9ca367016ed930f385d4022e6f
diff --git a/tests/resources/merge-resolve/added-in-master.txt b/tests/resources/merge-resolve/added-in-master.txt
new file mode 100644
index 0000000..233c091
--- /dev/null
+++ b/tests/resources/merge-resolve/added-in-master.txt
@@ -0,0 +1 @@
+this file is added in master
diff --git a/tests/resources/merge-resolve/automergeable.txt b/tests/resources/merge-resolve/automergeable.txt
new file mode 100644
index 0000000..ee3fa1b
--- /dev/null
+++ b/tests/resources/merge-resolve/automergeable.txt
@@ -0,0 +1,9 @@
+this file is changed in master
+this file is automergeable
+this file is automergeable
+this file is automergeable
+this file is automergeable
+this file is automergeable
+this file is automergeable
+this file is automergeable
+this file is automergeable
diff --git a/tests/resources/merge-resolve/changed-in-branch.txt b/tests/resources/merge-resolve/changed-in-branch.txt
new file mode 100644
index 0000000..ab6c44a
--- /dev/null
+++ b/tests/resources/merge-resolve/changed-in-branch.txt
@@ -0,0 +1 @@
+initial revision
diff --git a/tests/resources/merge-resolve/changed-in-master.txt b/tests/resources/merge-resolve/changed-in-master.txt
new file mode 100644
index 0000000..11deab0
--- /dev/null
+++ b/tests/resources/merge-resolve/changed-in-master.txt
@@ -0,0 +1 @@
+changed in master
diff --git a/tests/resources/merge-resolve/conflicting.txt b/tests/resources/merge-resolve/conflicting.txt
new file mode 100644
index 0000000..4e886e6
--- /dev/null
+++ b/tests/resources/merge-resolve/conflicting.txt
@@ -0,0 +1 @@
+this file is changed in master and branch
diff --git a/tests/resources/merge-resolve/removed-in-branch.txt b/tests/resources/merge-resolve/removed-in-branch.txt
new file mode 100644
index 0000000..dfe3f22
--- /dev/null
+++ b/tests/resources/merge-resolve/removed-in-branch.txt
@@ -0,0 +1 @@
+this is removed in branch
diff --git a/tests/resources/merge-resolve/unchanged.txt b/tests/resources/merge-resolve/unchanged.txt
new file mode 100644
index 0000000..c8f06f2
--- /dev/null
+++ b/tests/resources/merge-resolve/unchanged.txt
@@ -0,0 +1 @@
+this file is unchanged in both
diff --git a/tests/resources/merge-whitespace/.gitted/HEAD b/tests/resources/merge-whitespace/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/merge-whitespace/.gitted/config b/tests/resources/merge-whitespace/.gitted/config
new file mode 100644
index 0000000..6c9406b
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/config
@@ -0,0 +1,7 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
+ precomposeunicode = true
diff --git a/tests/resources/merge-whitespace/.gitted/index b/tests/resources/merge-whitespace/.gitted/index
new file mode 100644
index 0000000..534804a
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/index
Binary files differ
diff --git a/tests/resources/merge-whitespace/.gitted/objects/01/bd650462136a4f0a266dfc91ab93b3fef0f7cb b/tests/resources/merge-whitespace/.gitted/objects/01/bd650462136a4f0a266dfc91ab93b3fef0f7cb
new file mode 100644
index 0000000..074a77a
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/01/bd650462136a4f0a266dfc91ab93b3fef0f7cb
Binary files differ
diff --git a/tests/resources/merge-whitespace/.gitted/objects/08/3f868fb4324e32a4999173b2437b31d7a1ef25 b/tests/resources/merge-whitespace/.gitted/objects/08/3f868fb4324e32a4999173b2437b31d7a1ef25
new file mode 100644
index 0000000..ec6ed4d
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/08/3f868fb4324e32a4999173b2437b31d7a1ef25
Binary files differ
diff --git a/tests/resources/merge-whitespace/.gitted/objects/0a/a2acaa63cacc7a99fab0c2ce3d56572911df19 b/tests/resources/merge-whitespace/.gitted/objects/0a/a2acaa63cacc7a99fab0c2ce3d56572911df19
new file mode 100644
index 0000000..a2178ce
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/0a/a2acaa63cacc7a99fab0c2ce3d56572911df19
@@ -0,0 +1 @@
+xÍA @Qלbö&f€R!1ÆÑ[Lq°˜b#LÞÞ&z·ñ~œKÉ:àF*3gºžâ€¨½ó)qJZ&Bº™ˆ¤{;xk-2ή_ 78s-¹Ë»Ááñm÷S“šIhçrݼ·Îà ¢Zëºþq{ûCÔå™%Ó¤>4ÿ?« \ No newline at end of file
diff --git a/tests/resources/merge-whitespace/.gitted/objects/11/89e10a62aadf2fea8cd018afb52c1980f40b4f b/tests/resources/merge-whitespace/.gitted/objects/11/89e10a62aadf2fea8cd018afb52c1980f40b4f
new file mode 100644
index 0000000..f9f5984
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/11/89e10a62aadf2fea8cd018afb52c1980f40b4f
Binary files differ
diff --git a/tests/resources/merge-whitespace/.gitted/objects/24/2c8f6cf388e96e2c12b6e49cb7ae60167cba1e b/tests/resources/merge-whitespace/.gitted/objects/24/2c8f6cf388e96e2c12b6e49cb7ae60167cba1e
new file mode 100644
index 0000000..7cfc318
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/24/2c8f6cf388e96e2c12b6e49cb7ae60167cba1e
Binary files differ
diff --git a/tests/resources/merge-whitespace/.gitted/objects/25/246acb001858ffeffb03ea399fd2c0a163b832 b/tests/resources/merge-whitespace/.gitted/objects/25/246acb001858ffeffb03ea399fd2c0a163b832
new file mode 100644
index 0000000..f7160ce
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/25/246acb001858ffeffb03ea399fd2c0a163b832
Binary files differ
diff --git a/tests/resources/merge-whitespace/.gitted/objects/26/2f67de0de2e535a59ae1bc3c739601e98c354d b/tests/resources/merge-whitespace/.gitted/objects/26/2f67de0de2e535a59ae1bc3c739601e98c354d
new file mode 100644
index 0000000..c5a33b4
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/26/2f67de0de2e535a59ae1bc3c739601e98c354d
Binary files differ
diff --git a/tests/resources/merge-whitespace/.gitted/objects/2f/6727d2e570bf962d9dd926423cf6fe5072071a b/tests/resources/merge-whitespace/.gitted/objects/2f/6727d2e570bf962d9dd926423cf6fe5072071a
new file mode 100644
index 0000000..02bb788
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/2f/6727d2e570bf962d9dd926423cf6fe5072071a
Binary files differ
diff --git a/tests/resources/merge-whitespace/.gitted/objects/3c/43e7fc2a56fc825c31dfee65abd6dda8d16dca b/tests/resources/merge-whitespace/.gitted/objects/3c/43e7fc2a56fc825c31dfee65abd6dda8d16dca
new file mode 100644
index 0000000..0ec95b5
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/3c/43e7fc2a56fc825c31dfee65abd6dda8d16dca
Binary files differ
diff --git a/tests/resources/merge-whitespace/.gitted/objects/40/26a6c83f39c56881c9ac62e7582db9e3d33a4f b/tests/resources/merge-whitespace/.gitted/objects/40/26a6c83f39c56881c9ac62e7582db9e3d33a4f
new file mode 100644
index 0000000..50bdfc5
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/40/26a6c83f39c56881c9ac62e7582db9e3d33a4f
Binary files differ
diff --git a/tests/resources/merge-whitespace/.gitted/objects/42/dabb8d5dba2de103815a77e4369bb3966e64ef b/tests/resources/merge-whitespace/.gitted/objects/42/dabb8d5dba2de103815a77e4369bb3966e64ef
new file mode 100644
index 0000000..ae6dab7
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/42/dabb8d5dba2de103815a77e4369bb3966e64ef
Binary files differ
diff --git a/tests/resources/merge-whitespace/.gitted/objects/43/9230587f2eb38e9540a5c99e9831f65641eab9 b/tests/resources/merge-whitespace/.gitted/objects/43/9230587f2eb38e9540a5c99e9831f65641eab9
new file mode 100644
index 0000000..1b6354d
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/43/9230587f2eb38e9540a5c99e9831f65641eab9
Binary files differ
diff --git a/tests/resources/merge-whitespace/.gitted/objects/43/ad73e75e15f03bb0b4398a48a57ecfc20788e2 b/tests/resources/merge-whitespace/.gitted/objects/43/ad73e75e15f03bb0b4398a48a57ecfc20788e2
new file mode 100644
index 0000000..1936bc3
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/43/ad73e75e15f03bb0b4398a48a57ecfc20788e2
Binary files differ
diff --git a/tests/resources/merge-whitespace/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904 b/tests/resources/merge-whitespace/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904
new file mode 100644
index 0000000..adf6411
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/4b/825dc642cb6eb9a060e54bf8d69288fbee4904
Binary files differ
diff --git a/tests/resources/merge-whitespace/.gitted/objects/54/74989173042512ab630191ad71cdcedb646b9a b/tests/resources/merge-whitespace/.gitted/objects/54/74989173042512ab630191ad71cdcedb646b9a
new file mode 100644
index 0000000..cf12d4d
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/54/74989173042512ab630191ad71cdcedb646b9a
Binary files differ
diff --git a/tests/resources/merge-whitespace/.gitted/objects/5e/fb9bc29c482e023e40e0a2b3b7e49cec842034 b/tests/resources/merge-whitespace/.gitted/objects/5e/fb9bc29c482e023e40e0a2b3b7e49cec842034
new file mode 100644
index 0000000..d615c02
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/5e/fb9bc29c482e023e40e0a2b3b7e49cec842034
Binary files differ
diff --git a/tests/resources/merge-whitespace/.gitted/objects/70/d3d2e7d51a18fcc6f035a67e5c3f33069be04d b/tests/resources/merge-whitespace/.gitted/objects/70/d3d2e7d51a18fcc6f035a67e5c3f33069be04d
new file mode 100644
index 0000000..1718878
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/70/d3d2e7d51a18fcc6f035a67e5c3f33069be04d
Binary files differ
diff --git a/tests/resources/merge-whitespace/.gitted/objects/74/e83b6c5df14f1fba7c4ea1f99c6d007b591002 b/tests/resources/merge-whitespace/.gitted/objects/74/e83b6c5df14f1fba7c4ea1f99c6d007b591002
new file mode 100644
index 0000000..779e1bc
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/74/e83b6c5df14f1fba7c4ea1f99c6d007b591002
Binary files differ
diff --git a/tests/resources/merge-whitespace/.gitted/objects/77/f40c621ceae77ad8d756ef507bdbafe2713aa7 b/tests/resources/merge-whitespace/.gitted/objects/77/f40c621ceae77ad8d756ef507bdbafe2713aa7
new file mode 100644
index 0000000..f2efa93
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/77/f40c621ceae77ad8d756ef507bdbafe2713aa7
Binary files differ
diff --git a/tests/resources/merge-whitespace/.gitted/objects/9c/5362069759fb37ae036cef6e4b2f95c6c5eaab b/tests/resources/merge-whitespace/.gitted/objects/9c/5362069759fb37ae036cef6e4b2f95c6c5eaab
new file mode 100644
index 0000000..cc6ad10
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/9c/5362069759fb37ae036cef6e4b2f95c6c5eaab
Binary files differ
diff --git a/tests/resources/merge-whitespace/.gitted/objects/a2/9e7dabd68dfb38a717e6b1648713cd5c7adee2 b/tests/resources/merge-whitespace/.gitted/objects/a2/9e7dabd68dfb38a717e6b1648713cd5c7adee2
new file mode 100644
index 0000000..6e03437
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/a2/9e7dabd68dfb38a717e6b1648713cd5c7adee2
Binary files differ
diff --git a/tests/resources/merge-whitespace/.gitted/objects/a4/e6a86e07ef5afe036e26602fbbaa27496d00a9 b/tests/resources/merge-whitespace/.gitted/objects/a4/e6a86e07ef5afe036e26602fbbaa27496d00a9
new file mode 100644
index 0000000..95e0d71
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/a4/e6a86e07ef5afe036e26602fbbaa27496d00a9
@@ -0,0 +1,2 @@
+x¥MJÄ@…]÷)j7 A:Ý]ý"îÁ3 U5Õ“I´ÓYx{#£'põà㽞¬ó<uðC¼ëM‚§KòšP¬Ö3[¾d
+™0©Tq6å¬Î|PÓ¥CôÑÙX–Ê>‘ZEkÔÀ®”(¨Dü×çªA ³EªÎòP‚Êkô!äXs í}\¼’|îºÁ‹¶yÚÆýkƒÇ÷»>o½MÔéAÖù †ϱLî­³Öô¸Öõ_ó¦íªÀátË3Ÿ`Zúú‹Ïd¾“AeÏ \ No newline at end of file
diff --git a/tests/resources/merge-whitespace/.gitted/objects/a8/27eab4fd66ab37a6ebcfaa7b7e341abfd55947 b/tests/resources/merge-whitespace/.gitted/objects/a8/27eab4fd66ab37a6ebcfaa7b7e341abfd55947
new file mode 100644
index 0000000..fe2bdf4
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/a8/27eab4fd66ab37a6ebcfaa7b7e341abfd55947
Binary files differ
diff --git a/tests/resources/merge-whitespace/.gitted/objects/a9/66acc271e50b5d4595911752a77def0a5e5d40 b/tests/resources/merge-whitespace/.gitted/objects/a9/66acc271e50b5d4595911752a77def0a5e5d40
new file mode 100644
index 0000000..cd6f64d
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/a9/66acc271e50b5d4595911752a77def0a5e5d40
Binary files differ
diff --git a/tests/resources/merge-whitespace/.gitted/objects/b2/a69114f4897109fedf1aafea363cb2d2557029 b/tests/resources/merge-whitespace/.gitted/objects/b2/a69114f4897109fedf1aafea363cb2d2557029
new file mode 100644
index 0000000..b95a7be
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/b2/a69114f4897109fedf1aafea363cb2d2557029
Binary files differ
diff --git a/tests/resources/merge-whitespace/.gitted/objects/bc/83ac0422ba1082c80e406234910377984cfbb6 b/tests/resources/merge-whitespace/.gitted/objects/bc/83ac0422ba1082c80e406234910377984cfbb6
new file mode 100644
index 0000000..0ea77e8
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/bc/83ac0422ba1082c80e406234910377984cfbb6
Binary files differ
diff --git a/tests/resources/merge-whitespace/.gitted/objects/bf/e4ea5805af22a5b194259bda6f5f634486f891 b/tests/resources/merge-whitespace/.gitted/objects/bf/e4ea5805af22a5b194259bda6f5f634486f891
new file mode 100644
index 0000000..a27169e
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/bf/e4ea5805af22a5b194259bda6f5f634486f891
@@ -0,0 +1 @@
+x¥MjÃ0F»Ö)f’lýA)%›B/Ðõx4®°•Jcho_‡!Û÷àã}T×µX?¾Hc´‰CÆ)û˜çiˆL`??Æ`ÊŽff«nØxЈ Ñ„t¸”fœ4Yâ!;ï‚MÆäÙ$…»,µÁ'ÒÏÎ>¸­¥/û_‡×ëƒ}¿wiÏT×70£u)FïF8i«µ:è‘*üÔˆúZŠp¿!1Ìå÷(Á-ÕMîoêõzX \ No newline at end of file
diff --git a/tests/resources/merge-whitespace/.gitted/objects/c3/b1fb31424c98072542cc8e42b48c92e52f494a b/tests/resources/merge-whitespace/.gitted/objects/c3/b1fb31424c98072542cc8e42b48c92e52f494a
new file mode 100644
index 0000000..4006460
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/c3/b1fb31424c98072542cc8e42b48c92e52f494a
Binary files differ
diff --git a/tests/resources/merge-whitespace/.gitted/objects/c7/e2f386736445936f5ba181269a0e0967e280e8 b/tests/resources/merge-whitespace/.gitted/objects/c7/e2f386736445936f5ba181269a0e0967e280e8
new file mode 100644
index 0000000..821336f
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/c7/e2f386736445936f5ba181269a0e0967e280e8
@@ -0,0 +1,2 @@
+x¥AjÃ0E»Ö)f—E¡È’FA)Ù
+=C˜QF± ¶Y^ôöMI{‚®><>^Yçyêà‡øЛ*Ïçä5¡X­±|&Ę´Ôâl"Rg>¹éÒ!ôÑÙ˜æ*>±Z‹Ö¨A\ÍXbAe–¿¿T ÊH¹:Ç(C³œ9V¬Ñ‡@±R ï}\¼q¹îºÁ«¶yÚÆýkƒç;»·Þ&îüTÖù†¡…Gë¬57zKëú/‰y×vQÆKápß“`Zúú‹Ol¾$eÅ \ No newline at end of file
diff --git a/tests/resources/merge-whitespace/.gitted/objects/d9/5182053c31f8aa09df4fa225f4e668c5320b59 b/tests/resources/merge-whitespace/.gitted/objects/d9/5182053c31f8aa09df4fa225f4e668c5320b59
new file mode 100644
index 0000000..6b9483a
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/d9/5182053c31f8aa09df4fa225f4e668c5320b59
@@ -0,0 +1,5 @@
+x¥ÎM
+Â0@a×9Åì™$M›€ˆ®/àz:™Ø
+ý1IÞ^Á#¸ýÇË4Œ³»šE Ãh£‘.:MÚ'æ6¡uÔvâØ&k± ½`ÕJYæ
+Zû ©5D1™$ä9¢ö”zgX©Á¾IŠ¶:,nįM
+\%Oc¶wãógs©y¤J^¦èƸàCç,ìÑ ª¯~W«üQ÷a¬RVbh~,3\ÔgäRý \ No newline at end of file
diff --git a/tests/resources/merge-whitespace/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/merge-whitespace/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644
index 0000000..7112238
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests/resources/merge-whitespace/.gitted/objects/ec/5a35c75b8d3ee29bed37996b14e909d04fdcee b/tests/resources/merge-whitespace/.gitted/objects/ec/5a35c75b8d3ee29bed37996b14e909d04fdcee
new file mode 100644
index 0000000..d7fc301
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/ec/5a35c75b8d3ee29bed37996b14e909d04fdcee
Binary files differ
diff --git a/tests/resources/merge-whitespace/.gitted/objects/ee/3c2aac8e03224c323b58ecb1f9eef616745467 b/tests/resources/merge-whitespace/.gitted/objects/ee/3c2aac8e03224c323b58ecb1f9eef616745467
new file mode 100644
index 0000000..1acec06
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/ee/3c2aac8e03224c323b58ecb1f9eef616745467
Binary files differ
diff --git a/tests/resources/merge-whitespace/.gitted/objects/ef/e94a4bf4e697f7f0270f0d1b8a93af784a19d0 b/tests/resources/merge-whitespace/.gitted/objects/ef/e94a4bf4e697f7f0270f0d1b8a93af784a19d0
new file mode 100644
index 0000000..ca5af40
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/ef/e94a4bf4e697f7f0270f0d1b8a93af784a19d0
Binary files differ
diff --git a/tests/resources/merge-whitespace/.gitted/objects/f0/0c965d8307308469e537302baa73048488f162 b/tests/resources/merge-whitespace/.gitted/objects/f0/0c965d8307308469e537302baa73048488f162
new file mode 100644
index 0000000..343037e
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/f0/0c965d8307308469e537302baa73048488f162
Binary files differ
diff --git a/tests/resources/merge-whitespace/.gitted/objects/f1/90a0d111ca1688778657798743ddfb4ed4bd64 b/tests/resources/merge-whitespace/.gitted/objects/f1/90a0d111ca1688778657798743ddfb4ed4bd64
new file mode 100644
index 0000000..4e22912
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/f1/90a0d111ca1688778657798743ddfb4ed4bd64
@@ -0,0 +1,2 @@
+x¥ÎM
+Â0@a×9Eö‚äoš ˆ.o1I¦Z!¦éÂÛ[ðn¿Åã¥ZÊÔ¥±a׳äd!yˆ![fƒ‘³õˆCÔŽQaVn̉Y¼¨ñÜ%á0PJÆk!;@@­=ò>ó¨xS%híÚäÒ{åE^¹•iy¬ŸEŸ?»Ÿ—Þ&êtHµœ¤v0g¬Ü+£”Øt[íüWD\êÜ·qñÃMØ \ No newline at end of file
diff --git a/tests/resources/merge-whitespace/.gitted/objects/f4/9b2c244e9d3b0647fdfb95954c38fbfeecf3ad b/tests/resources/merge-whitespace/.gitted/objects/f4/9b2c244e9d3b0647fdfb95954c38fbfeecf3ad
new file mode 100644
index 0000000..437f667
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/f4/9b2c244e9d3b0647fdfb95954c38fbfeecf3ad
@@ -0,0 +1,2 @@
+x¥Í=
+1@aëœbzAf‡Iœ€ˆà-’ì¬!,æ§ðö.xÛ¯x/m¥ä“СWUà(dçä˜Rt}@‡j9.2;O"KTelÂèëVáÒ{hƒ»Ö’Û:> .¯Ÿ=o­×z8¥­\ab²^ÄžŽHˆf×}Ýõ¯ˆùR<ì \ No newline at end of file
diff --git a/tests/resources/merge-whitespace/.gitted/objects/f8/7905f99f0e66d179a8379d8ca4d8cbbd32c231 b/tests/resources/merge-whitespace/.gitted/objects/f8/7905f99f0e66d179a8379d8ca4d8cbbd32c231
new file mode 100644
index 0000000..d568dc1
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/objects/f8/7905f99f0e66d179a8379d8ca4d8cbbd32c231
@@ -0,0 +1 @@
+x¥KjÄ0D³ö)´›E tëg5„0»@ g0-©e;`;#Ë‹Ü>†™œ «‚WEQ•¶e™›2žZQÎö–aoÀj‡š£7€„œ{L9IŽÞúHÜ}s•µ©Lƒg’Á˜r±…µvÅŠ÷!9£!:úËGÍžm±z*’ 2aãMŠ:kçzÐÔñѦ­ªN·Cvõ.u™÷éøÙÕë××½Õ™¿¤mySxî=—ãÕ3h€î¤çµ&ÿ*é>¥Ž¢bå5Mêr×!iâu”‹š×¶=Ü´û´Äià \ No newline at end of file
diff --git a/tests/resources/merge-whitespace/.gitted/refs/heads/branch_a_change b/tests/resources/merge-whitespace/.gitted/refs/heads/branch_a_change
new file mode 100644
index 0000000..3a46eff
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/refs/heads/branch_a_change
@@ -0,0 +1 @@
+d95182053c31f8aa09df4fa225f4e668c5320b59
diff --git a/tests/resources/merge-whitespace/.gitted/refs/heads/branch_a_eol b/tests/resources/merge-whitespace/.gitted/refs/heads/branch_a_eol
new file mode 100644
index 0000000..a59d5b5
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/refs/heads/branch_a_eol
@@ -0,0 +1 @@
+9c5362069759fb37ae036cef6e4b2f95c6c5eaab
diff --git a/tests/resources/merge-whitespace/.gitted/refs/heads/branch_b_change b/tests/resources/merge-whitespace/.gitted/refs/heads/branch_b_change
new file mode 100644
index 0000000..c14ced3
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/refs/heads/branch_b_change
@@ -0,0 +1 @@
+b2a69114f4897109fedf1aafea363cb2d2557029
diff --git a/tests/resources/merge-whitespace/.gitted/refs/heads/branch_b_eol b/tests/resources/merge-whitespace/.gitted/refs/heads/branch_b_eol
new file mode 100644
index 0000000..9e25c6e
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/refs/heads/branch_b_eol
@@ -0,0 +1 @@
+bfe4ea5805af22a5b194259bda6f5f634486f891
diff --git a/tests/resources/merge-whitespace/.gitted/refs/heads/master b/tests/resources/merge-whitespace/.gitted/refs/heads/master
new file mode 100644
index 0000000..61e8ae7
--- /dev/null
+++ b/tests/resources/merge-whitespace/.gitted/refs/heads/master
@@ -0,0 +1 @@
+1189e10a62aadf2fea8cd018afb52c1980f40b4f
diff --git a/tests/resources/merge-whitespace/test.txt b/tests/resources/merge-whitespace/test.txt
new file mode 100644
index 0000000..74e83b6
--- /dev/null
+++ b/tests/resources/merge-whitespace/test.txt
@@ -0,0 +1,11 @@
+0
+1
+2
+3
+4
+5 XXX
+6
+7
+8
+9
+10
diff --git a/tests/resources/mergedrepo/.gitted/COMMIT_EDITMSG b/tests/resources/mergedrepo/.gitted/COMMIT_EDITMSG
new file mode 100644
index 0000000..1f7391f
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/COMMIT_EDITMSG
@@ -0,0 +1 @@
+master
diff --git a/tests/resources/mergedrepo/.gitted/HEAD b/tests/resources/mergedrepo/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/mergedrepo/.gitted/MERGE_HEAD b/tests/resources/mergedrepo/.gitted/MERGE_HEAD
new file mode 100644
index 0000000..a5bdf6e
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/MERGE_HEAD
@@ -0,0 +1 @@
+e2809157a7766f272e4cfe26e61ef2678a5357ff
diff --git a/tests/resources/mergedrepo/.gitted/MERGE_MODE b/tests/resources/mergedrepo/.gitted/MERGE_MODE
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/MERGE_MODE
diff --git a/tests/resources/mergedrepo/.gitted/MERGE_MSG b/tests/resources/mergedrepo/.gitted/MERGE_MSG
new file mode 100644
index 0000000..7c4d1f5
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/MERGE_MSG
@@ -0,0 +1,5 @@
+Merge branch 'branch'
+
+Conflicts:
+ conflicts-one.txt
+ conflicts-two.txt
diff --git a/tests/resources/mergedrepo/.gitted/ORIG_HEAD b/tests/resources/mergedrepo/.gitted/ORIG_HEAD
new file mode 100644
index 0000000..13d4d67
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/ORIG_HEAD
@@ -0,0 +1 @@
+3a34580a35add43a4cf361e8e9a30060a905c876
diff --git a/tests/resources/mergedrepo/.gitted/config b/tests/resources/mergedrepo/.gitted/config
new file mode 100644
index 0000000..af10792
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/config
@@ -0,0 +1,6 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
diff --git a/tests/resources/mergedrepo/.gitted/description b/tests/resources/mergedrepo/.gitted/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/mergedrepo/.gitted/index b/tests/resources/mergedrepo/.gitted/index
new file mode 100644
index 0000000..3d29f78
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/index
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/info/exclude b/tests/resources/mergedrepo/.gitted/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/mergedrepo/.gitted/logs/HEAD b/tests/resources/mergedrepo/.gitted/logs/HEAD
new file mode 100644
index 0000000..a385da6
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/logs/HEAD
@@ -0,0 +1,5 @@
+0000000000000000000000000000000000000000 9a05ccb4e0f948de03128e095f39dae6976751c5 Edward Thomson <ethomson@edwardthomson.com> 1351371828 -0500 commit (initial): initial
+9a05ccb4e0f948de03128e095f39dae6976751c5 9a05ccb4e0f948de03128e095f39dae6976751c5 Edward Thomson <ethomson@edwardthomson.com> 1351371835 -0500 checkout: moving from master to branch
+9a05ccb4e0f948de03128e095f39dae6976751c5 e2809157a7766f272e4cfe26e61ef2678a5357ff Edward Thomson <ethomson@edwardthomson.com> 1351371872 -0500 commit: branch
+e2809157a7766f272e4cfe26e61ef2678a5357ff 9a05ccb4e0f948de03128e095f39dae6976751c5 Edward Thomson <ethomson@edwardthomson.com> 1351371873 -0500 checkout: moving from branch to master
+9a05ccb4e0f948de03128e095f39dae6976751c5 3a34580a35add43a4cf361e8e9a30060a905c876 Edward Thomson <ethomson@edwardthomson.com> 1351372106 -0500 commit: master
diff --git a/tests/resources/mergedrepo/.gitted/logs/refs/heads/branch b/tests/resources/mergedrepo/.gitted/logs/refs/heads/branch
new file mode 100644
index 0000000..26a5e8d
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/logs/refs/heads/branch
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 9a05ccb4e0f948de03128e095f39dae6976751c5 Edward Thomson <ethomson@edwardthomson.com> 1351371835 -0500 branch: Created from HEAD
+9a05ccb4e0f948de03128e095f39dae6976751c5 e2809157a7766f272e4cfe26e61ef2678a5357ff Edward Thomson <ethomson@edwardthomson.com> 1351371872 -0500 commit: branch
diff --git a/tests/resources/mergedrepo/.gitted/logs/refs/heads/master b/tests/resources/mergedrepo/.gitted/logs/refs/heads/master
new file mode 100644
index 0000000..425f7bd
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/logs/refs/heads/master
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 9a05ccb4e0f948de03128e095f39dae6976751c5 Edward Thomson <ethomson@edwardthomson.com> 1351371828 -0500 commit (initial): initial
+9a05ccb4e0f948de03128e095f39dae6976751c5 3a34580a35add43a4cf361e8e9a30060a905c876 Edward Thomson <ethomson@edwardthomson.com> 1351372106 -0500 commit: master
diff --git a/tests/resources/mergedrepo/.gitted/objects/03/db1d37504ca0c4f7c26d7776b0e28bdea08712 b/tests/resources/mergedrepo/.gitted/objects/03/db1d37504ca0c4f7c26d7776b0e28bdea08712
new file mode 100644
index 0000000..9232f79
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/03/db1d37504ca0c4f7c26d7776b0e28bdea08712
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/17/0efc1023e0ed2390150bb4469c8456b63e8f91 b/tests/resources/mergedrepo/.gitted/objects/17/0efc1023e0ed2390150bb4469c8456b63e8f91
new file mode 100644
index 0000000..3e124d9
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/17/0efc1023e0ed2390150bb4469c8456b63e8f91
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/1f/85ca51b8e0aac893a621b61a9c2661d6aa6d81 b/tests/resources/mergedrepo/.gitted/objects/1f/85ca51b8e0aac893a621b61a9c2661d6aa6d81
new file mode 100644
index 0000000..7bb19c8
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/1f/85ca51b8e0aac893a621b61a9c2661d6aa6d81
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/22/0bd62631c8cf7a83ef39c6b94595f00517211e b/tests/resources/mergedrepo/.gitted/objects/22/0bd62631c8cf7a83ef39c6b94595f00517211e
new file mode 100644
index 0000000..487bcff
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/22/0bd62631c8cf7a83ef39c6b94595f00517211e
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/32/d55d59265db86dd690f0a7fc563db43e2bc6a6 b/tests/resources/mergedrepo/.gitted/objects/32/d55d59265db86dd690f0a7fc563db43e2bc6a6
new file mode 100644
index 0000000..2eb3954
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/32/d55d59265db86dd690f0a7fc563db43e2bc6a6
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/38/e2d82b9065a237904af4b780b4d68da6950534 b/tests/resources/mergedrepo/.gitted/objects/38/e2d82b9065a237904af4b780b4d68da6950534
new file mode 100644
index 0000000..ebe83cc
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/38/e2d82b9065a237904af4b780b4d68da6950534
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/3a/34580a35add43a4cf361e8e9a30060a905c876 b/tests/resources/mergedrepo/.gitted/objects/3a/34580a35add43a4cf361e8e9a30060a905c876
new file mode 100644
index 0000000..0d4095f
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/3a/34580a35add43a4cf361e8e9a30060a905c876
@@ -0,0 +1,2 @@
+x¥ŽK
+1D]ç}¥óíDÜx/Oã"F2¯ooà®ê<*·Zo”‘»Ñ™uI²h²hrÄlÊÊ"r YùT8¢'©Ä#v¾mÎÉ0.Áø¨¥òŒÁ.:”È.#+³ñ9ÖÖáR^±¸®­níGžô“Îü~í[=ÔVjRìÑ"ŠIçÙÁjDÛ”ˆ7|N` \ No newline at end of file
diff --git a/tests/resources/mergedrepo/.gitted/objects/44/58b8bc9e72b6c8755ae456f60e9844d0538d8c b/tests/resources/mergedrepo/.gitted/objects/44/58b8bc9e72b6c8755ae456f60e9844d0538d8c
new file mode 100644
index 0000000..33389c3
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/44/58b8bc9e72b6c8755ae456f60e9844d0538d8c
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/47/8871385b9cd03908c5383acfd568bef023c6b3 b/tests/resources/mergedrepo/.gitted/objects/47/8871385b9cd03908c5383acfd568bef023c6b3
new file mode 100644
index 0000000..5361ea6
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/47/8871385b9cd03908c5383acfd568bef023c6b3
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/51/6bd85f78061e09ccc714561d7b504672cb52da b/tests/resources/mergedrepo/.gitted/objects/51/6bd85f78061e09ccc714561d7b504672cb52da
new file mode 100644
index 0000000..a60da87
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/51/6bd85f78061e09ccc714561d7b504672cb52da
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/53/c1d95a01f4514b162066fc98564500c96c46ad b/tests/resources/mergedrepo/.gitted/objects/53/c1d95a01f4514b162066fc98564500c96c46ad
new file mode 100644
index 0000000..85e84d7
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/53/c1d95a01f4514b162066fc98564500c96c46ad
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/6a/ea5f295304c36144ad6e9247a291b7f8112399 b/tests/resources/mergedrepo/.gitted/objects/6a/ea5f295304c36144ad6e9247a291b7f8112399
new file mode 100644
index 0000000..b16b521
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/6a/ea5f295304c36144ad6e9247a291b7f8112399
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/70/68e30a7f0090ae32db35dfa1e4189d8780fcb8 b/tests/resources/mergedrepo/.gitted/objects/70/68e30a7f0090ae32db35dfa1e4189d8780fcb8
new file mode 100644
index 0000000..7c4e85f
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/70/68e30a7f0090ae32db35dfa1e4189d8780fcb8
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/75/938de1e367098b3e9a7b1ec3c4ac4548afffe4 b/tests/resources/mergedrepo/.gitted/objects/75/938de1e367098b3e9a7b1ec3c4ac4548afffe4
new file mode 100644
index 0000000..65173fc
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/75/938de1e367098b3e9a7b1ec3c4ac4548afffe4
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/7b/26923aaf452b1977eb08617c59475fb3f74b71 b/tests/resources/mergedrepo/.gitted/objects/7b/26923aaf452b1977eb08617c59475fb3f74b71
new file mode 100644
index 0000000..162fa44
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/7b/26923aaf452b1977eb08617c59475fb3f74b71
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/84/af62840be1b1c47b778a8a249f3ff45155038c b/tests/resources/mergedrepo/.gitted/objects/84/af62840be1b1c47b778a8a249f3ff45155038c
new file mode 100644
index 0000000..77a519f
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/84/af62840be1b1c47b778a8a249f3ff45155038c
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/88/71f7a2ee3addfc4ba39fbd0783c8e738d04cda b/tests/resources/mergedrepo/.gitted/objects/88/71f7a2ee3addfc4ba39fbd0783c8e738d04cda
new file mode 100644
index 0000000..f624cd4
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/88/71f7a2ee3addfc4ba39fbd0783c8e738d04cda
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/88/7b153b165d32409c70163e0f734c090f12f673 b/tests/resources/mergedrepo/.gitted/objects/88/7b153b165d32409c70163e0f734c090f12f673
new file mode 100644
index 0000000..096474c
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/88/7b153b165d32409c70163e0f734c090f12f673
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/8a/ad34cc83733590e74b93d0f7cf00375e2a735a b/tests/resources/mergedrepo/.gitted/objects/8a/ad34cc83733590e74b93d0f7cf00375e2a735a
new file mode 100644
index 0000000..a413bc6
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/8a/ad34cc83733590e74b93d0f7cf00375e2a735a
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/8b/3f43d2402825c200f835ca1762413e386fd0b2 b/tests/resources/mergedrepo/.gitted/objects/8b/3f43d2402825c200f835ca1762413e386fd0b2
new file mode 100644
index 0000000..3ac8f60
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/8b/3f43d2402825c200f835ca1762413e386fd0b2
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/8b/72416545c7e761b64cecad4f1686eae4078aa8 b/tests/resources/mergedrepo/.gitted/objects/8b/72416545c7e761b64cecad4f1686eae4078aa8
new file mode 100644
index 0000000..589a5ae
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/8b/72416545c7e761b64cecad4f1686eae4078aa8
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/8f/3c06cff9a83757cec40c80bc9bf31a2582bde9 b/tests/resources/mergedrepo/.gitted/objects/8f/3c06cff9a83757cec40c80bc9bf31a2582bde9
new file mode 100644
index 0000000..6503985
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/8f/3c06cff9a83757cec40c80bc9bf31a2582bde9
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/8f/fcc405925511824a2240a6d3686aa7f8c7ac50 b/tests/resources/mergedrepo/.gitted/objects/8f/fcc405925511824a2240a6d3686aa7f8c7ac50
new file mode 100644
index 0000000..2eaa808
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/8f/fcc405925511824a2240a6d3686aa7f8c7ac50
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/9a/05ccb4e0f948de03128e095f39dae6976751c5 b/tests/resources/mergedrepo/.gitted/objects/9a/05ccb4e0f948de03128e095f39dae6976751c5
new file mode 100644
index 0000000..7373a80
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/9a/05ccb4e0f948de03128e095f39dae6976751c5
@@ -0,0 +1 @@
+x¥Ñ !Dý¦Šm@³Ë ÉÅøc6Àq#‘#AŒí‹Æü›y/™ µ”Üœ:ô#$–l•tH:é—„*D³XÖh¬VœáŸ}« ®ëË·n[-ºÃý¤KüŠ_;…ZÎ@“¦‰ÉJ GÔˆbÐqÞãŸ3"ï¹goŒ«@I \ No newline at end of file
diff --git a/tests/resources/mergedrepo/.gitted/objects/9d/81f82fccc7dcd7de7a1ffead1815294c2e092c b/tests/resources/mergedrepo/.gitted/objects/9d/81f82fccc7dcd7de7a1ffead1815294c2e092c
new file mode 100644
index 0000000..c5a651f
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/9d/81f82fccc7dcd7de7a1ffead1815294c2e092c
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/b7/cedb8ad4cbb22b6363f9578cbd749797f7ef0d b/tests/resources/mergedrepo/.gitted/objects/b7/cedb8ad4cbb22b6363f9578cbd749797f7ef0d
new file mode 100644
index 0000000..3e14b5d
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/b7/cedb8ad4cbb22b6363f9578cbd749797f7ef0d
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/d0/1885ea594926eae9ba5b54ad76692af5969f51 b/tests/resources/mergedrepo/.gitted/objects/d0/1885ea594926eae9ba5b54ad76692af5969f51
new file mode 100644
index 0000000..a641adc
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/d0/1885ea594926eae9ba5b54ad76692af5969f51
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/e2/809157a7766f272e4cfe26e61ef2678a5357ff b/tests/resources/mergedrepo/.gitted/objects/e2/809157a7766f272e4cfe26e61ef2678a5357ff
new file mode 100644
index 0000000..fa86662
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/e2/809157a7766f272e4cfe26e61ef2678a5357ff
@@ -0,0 +1,3 @@
+x¥ŽK
+1D]ç¹€Òùt> âÆxžN‡q1‰¯ï(ÞÀ]Õ{P·e¹ m½Ù.¢S­Ì0[Dc’õd­
+Å…ˆbM‰Ôº¬Cgdž¼@Í>glÈX].$!ÇÑ0*zŽ¹u})/êE_ç¶<Úª²ÑO:ËWüÚÛrÒÆ¡qѤhõ@mt;;äÏ5uZyVoÓ\Mÿ \ No newline at end of file
diff --git a/tests/resources/mergedrepo/.gitted/objects/e6/2cac5c88b9928f2695b934c70efa4285324478 b/tests/resources/mergedrepo/.gitted/objects/e6/2cac5c88b9928f2695b934c70efa4285324478
new file mode 100644
index 0000000..c9841c6
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/e6/2cac5c88b9928f2695b934c70efa4285324478
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/objects/f7/2784290c151092abf04ce6b875068547f70406 b/tests/resources/mergedrepo/.gitted/objects/f7/2784290c151092abf04ce6b875068547f70406
new file mode 100644
index 0000000..cd587db
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/objects/f7/2784290c151092abf04ce6b875068547f70406
Binary files differ
diff --git a/tests/resources/mergedrepo/.gitted/refs/heads/branch b/tests/resources/mergedrepo/.gitted/refs/heads/branch
new file mode 100644
index 0000000..a5bdf6e
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/refs/heads/branch
@@ -0,0 +1 @@
+e2809157a7766f272e4cfe26e61ef2678a5357ff
diff --git a/tests/resources/mergedrepo/.gitted/refs/heads/master b/tests/resources/mergedrepo/.gitted/refs/heads/master
new file mode 100644
index 0000000..13d4d67
--- /dev/null
+++ b/tests/resources/mergedrepo/.gitted/refs/heads/master
@@ -0,0 +1 @@
+3a34580a35add43a4cf361e8e9a30060a905c876
diff --git a/tests/resources/mergedrepo/conflicts-one.txt b/tests/resources/mergedrepo/conflicts-one.txt
new file mode 100644
index 0000000..8aad34c
--- /dev/null
+++ b/tests/resources/mergedrepo/conflicts-one.txt
@@ -0,0 +1,5 @@
+<<<<<<< HEAD
+This is most certainly a conflict!
+=======
+This is a conflict!!!
+>>>>>>> branch
diff --git a/tests/resources/mergedrepo/conflicts-two.txt b/tests/resources/mergedrepo/conflicts-two.txt
new file mode 100644
index 0000000..e62cac5
--- /dev/null
+++ b/tests/resources/mergedrepo/conflicts-two.txt
@@ -0,0 +1,5 @@
+<<<<<<< HEAD
+This is without question another conflict!
+=======
+This is another conflict!!!
+>>>>>>> branch
diff --git a/tests/resources/mergedrepo/one.txt b/tests/resources/mergedrepo/one.txt
new file mode 100644
index 0000000..75938de
--- /dev/null
+++ b/tests/resources/mergedrepo/one.txt
@@ -0,0 +1,10 @@
+This is file one!
+This is file one.
+This is file one.
+This is file one.
+This is file one.
+This is file one.
+This is file one.
+This is file one.
+This is file one.
+This is file one!
diff --git a/tests/resources/mergedrepo/two.txt b/tests/resources/mergedrepo/two.txt
new file mode 100644
index 0000000..7b26923
--- /dev/null
+++ b/tests/resources/mergedrepo/two.txt
@@ -0,0 +1,12 @@
+This is file two!
+This is file two.
+This is file two.
+This is file two.
+This is file two.
+This is file two.
+This is file two.
+This is file two.
+This is file two.
+This is file two.
+This is file two.
+This is file two!
diff --git a/tests/resources/namespace.git/COMMIT_EDITMSG b/tests/resources/namespace.git/COMMIT_EDITMSG
new file mode 100644
index 0000000..8510665
--- /dev/null
+++ b/tests/resources/namespace.git/COMMIT_EDITMSG
@@ -0,0 +1 @@
+four
diff --git a/tests/resources/namespace.git/HEAD b/tests/resources/namespace.git/HEAD
new file mode 100644
index 0000000..60cbd74
--- /dev/null
+++ b/tests/resources/namespace.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/four
diff --git a/tests/resources/namespace.git/config b/tests/resources/namespace.git/config
new file mode 100644
index 0000000..6c9406b
--- /dev/null
+++ b/tests/resources/namespace.git/config
@@ -0,0 +1,7 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
+ precomposeunicode = true
diff --git a/tests/resources/namespace.git/description b/tests/resources/namespace.git/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/namespace.git/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/namespace.git/index b/tests/resources/namespace.git/index
new file mode 100644
index 0000000..5ed27fe
--- /dev/null
+++ b/tests/resources/namespace.git/index
Binary files differ
diff --git a/tests/resources/namespace.git/info/exclude b/tests/resources/namespace.git/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/namespace.git/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/namespace.git/logs/HEAD b/tests/resources/namespace.git/logs/HEAD
new file mode 100644
index 0000000..ca04976
--- /dev/null
+++ b/tests/resources/namespace.git/logs/HEAD
@@ -0,0 +1,9 @@
+0000000000000000000000000000000000000000 9ebfa6bdc9d38075d29d26aa5df89b1cf635b269 Edward Thomson <ethomson@edwardthomson.com> 1661110058 -0400 commit (initial): Hello, world.
+9ebfa6bdc9d38075d29d26aa5df89b1cf635b269 7eeaa70d7c5592db920a2e107ce3918bd4c8a425 Edward Thomson <ethomson@edwardthomson.com> 1661110068 -0400 commit: with enthusiasm
+7eeaa70d7c5592db920a2e107ce3918bd4c8a425 7eeaa70d7c5592db920a2e107ce3918bd4c8a425 Edward Thomson <ethomson@edwardthomson.com> 1661110072 -0400 checkout: moving from main to branch
+7eeaa70d7c5592db920a2e107ce3918bd4c8a425 3d669d1b33ec8add4609d8043d025527db4989eb Edward Thomson <ethomson@edwardthomson.com> 1661110088 -0400 commit: capitalize
+3d669d1b33ec8add4609d8043d025527db4989eb bfd17f429f4e2d121769213171ad57ca2e5173f9 Edward Thomson <ethomson@edwardthomson.com> 1661110104 -0400 commit: less enthusiastic
+bfd17f429f4e2d121769213171ad57ca2e5173f9 7eeaa70d7c5592db920a2e107ce3918bd4c8a425 Edward Thomson <ethomson@edwardthomson.com> 1661110107 -0400 checkout: moving from branch to main
+0000000000000000000000000000000000000000 420d51ce75a87909e29659da2072ffd3d5daf5b7 Edward Thomson <ethomson@edwardthomson.com> 1661110166 -0400 commit (initial): 1 2 3
+420d51ce75a87909e29659da2072ffd3d5daf5b7 420d51ce75a87909e29659da2072ffd3d5daf5b7 Edward Thomson <ethomson@edwardthomson.com> 1661110209 -0400 checkout: moving from one to four
+420d51ce75a87909e29659da2072ffd3d5daf5b7 04433ff5b52d6ad534fd6288de4a57b81cc12188 Edward Thomson <ethomson@edwardthomson.com> 1661110212 -0400 commit: four
diff --git a/tests/resources/namespace.git/logs/refs/heads/branch b/tests/resources/namespace.git/logs/refs/heads/branch
new file mode 100644
index 0000000..e3dfea4
--- /dev/null
+++ b/tests/resources/namespace.git/logs/refs/heads/branch
@@ -0,0 +1,3 @@
+0000000000000000000000000000000000000000 7eeaa70d7c5592db920a2e107ce3918bd4c8a425 Edward Thomson <ethomson@edwardthomson.com> 1661110072 -0400 branch: Created from HEAD
+7eeaa70d7c5592db920a2e107ce3918bd4c8a425 3d669d1b33ec8add4609d8043d025527db4989eb Edward Thomson <ethomson@edwardthomson.com> 1661110088 -0400 commit: capitalize
+3d669d1b33ec8add4609d8043d025527db4989eb bfd17f429f4e2d121769213171ad57ca2e5173f9 Edward Thomson <ethomson@edwardthomson.com> 1661110104 -0400 commit: less enthusiastic
diff --git a/tests/resources/namespace.git/logs/refs/heads/four b/tests/resources/namespace.git/logs/refs/heads/four
new file mode 100644
index 0000000..2abf339
--- /dev/null
+++ b/tests/resources/namespace.git/logs/refs/heads/four
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 420d51ce75a87909e29659da2072ffd3d5daf5b7 Edward Thomson <ethomson@edwardthomson.com> 1661110209 -0400 branch: Created from HEAD
+420d51ce75a87909e29659da2072ffd3d5daf5b7 04433ff5b52d6ad534fd6288de4a57b81cc12188 Edward Thomson <ethomson@edwardthomson.com> 1661110212 -0400 commit: four
diff --git a/tests/resources/namespace.git/logs/refs/heads/main b/tests/resources/namespace.git/logs/refs/heads/main
new file mode 100644
index 0000000..04de95a
--- /dev/null
+++ b/tests/resources/namespace.git/logs/refs/heads/main
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 9ebfa6bdc9d38075d29d26aa5df89b1cf635b269 Edward Thomson <ethomson@edwardthomson.com> 1661110058 -0400 commit (initial): Hello, world.
+9ebfa6bdc9d38075d29d26aa5df89b1cf635b269 7eeaa70d7c5592db920a2e107ce3918bd4c8a425 Edward Thomson <ethomson@edwardthomson.com> 1661110068 -0400 commit: with enthusiasm
diff --git a/tests/resources/namespace.git/logs/refs/heads/one b/tests/resources/namespace.git/logs/refs/heads/one
new file mode 100644
index 0000000..bd3c5fa
--- /dev/null
+++ b/tests/resources/namespace.git/logs/refs/heads/one
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 420d51ce75a87909e29659da2072ffd3d5daf5b7 Edward Thomson <ethomson@edwardthomson.com> 1661110166 -0400 commit (initial): 1 2 3
diff --git a/tests/resources/namespace.git/objects/04/433ff5b52d6ad534fd6288de4a57b81cc12188 b/tests/resources/namespace.git/objects/04/433ff5b52d6ad534fd6288de4a57b81cc12188
new file mode 100644
index 0000000..be49bee
--- /dev/null
+++ b/tests/resources/namespace.git/objects/04/433ff5b52d6ad534fd6288de4a57b81cc12188
Binary files differ
diff --git a/tests/resources/namespace.git/objects/0a/890bd10328d68f6d85efd2535e3a4c588ee8e6 b/tests/resources/namespace.git/objects/0a/890bd10328d68f6d85efd2535e3a4c588ee8e6
new file mode 100644
index 0000000..41f340b
--- /dev/null
+++ b/tests/resources/namespace.git/objects/0a/890bd10328d68f6d85efd2535e3a4c588ee8e6
Binary files differ
diff --git a/tests/resources/namespace.git/objects/10/fcb1c85bd6b3bc6f43e0a3932ff5859121a84e b/tests/resources/namespace.git/objects/10/fcb1c85bd6b3bc6f43e0a3932ff5859121a84e
new file mode 100644
index 0000000..3005ed1
--- /dev/null
+++ b/tests/resources/namespace.git/objects/10/fcb1c85bd6b3bc6f43e0a3932ff5859121a84e
Binary files differ
diff --git a/tests/resources/namespace.git/objects/24/bbdca8b223aaa3384d78312f730c58492aa30a b/tests/resources/namespace.git/objects/24/bbdca8b223aaa3384d78312f730c58492aa30a
new file mode 100644
index 0000000..c1e97d5
--- /dev/null
+++ b/tests/resources/namespace.git/objects/24/bbdca8b223aaa3384d78312f730c58492aa30a
Binary files differ
diff --git a/tests/resources/namespace.git/objects/27/0c611ee72c567bc1b2abec4cbc345bab9f15ba b/tests/resources/namespace.git/objects/27/0c611ee72c567bc1b2abec4cbc345bab9f15ba
new file mode 100644
index 0000000..300fb67
--- /dev/null
+++ b/tests/resources/namespace.git/objects/27/0c611ee72c567bc1b2abec4cbc345bab9f15ba
Binary files differ
diff --git a/tests/resources/namespace.git/objects/2b/df67abb163a4ffb2d7f3f0880c9fe5068ce782 b/tests/resources/namespace.git/objects/2b/df67abb163a4ffb2d7f3f0880c9fe5068ce782
new file mode 100644
index 0000000..d0398e6
--- /dev/null
+++ b/tests/resources/namespace.git/objects/2b/df67abb163a4ffb2d7f3f0880c9fe5068ce782
Binary files differ
diff --git a/tests/resources/namespace.git/objects/3d/669d1b33ec8add4609d8043d025527db4989eb b/tests/resources/namespace.git/objects/3d/669d1b33ec8add4609d8043d025527db4989eb
new file mode 100644
index 0000000..a926c9e
--- /dev/null
+++ b/tests/resources/namespace.git/objects/3d/669d1b33ec8add4609d8043d025527db4989eb
@@ -0,0 +1,2 @@
+x¥ŽQ
+Â0DýÎ)re“&ÝDüñ^`›ÝЂiJžÞ*ÞÀ¿™÷`˜TK™š¶ÎïÚ*¢%E‡Ò1#!zË€!aH™ÇÁš,ÙµÐ*sÓ(B„À˜¼–‡h¬À$]4a`—9ë=ÚXW}á'­¬¯c-÷:ë£lô“Îò¿vHµœ´é{c @z@mt;ÛäÏ•h™ݦ—¨7-)Q \ No newline at end of file
diff --git a/tests/resources/namespace.git/objects/42/0d51ce75a87909e29659da2072ffd3d5daf5b7 b/tests/resources/namespace.git/objects/42/0d51ce75a87909e29659da2072ffd3d5daf5b7
new file mode 100644
index 0000000..fb91b1b
--- /dev/null
+++ b/tests/resources/namespace.git/objects/42/0d51ce75a87909e29659da2072ffd3d5daf5b7
Binary files differ
diff --git a/tests/resources/namespace.git/objects/56/26abf0f72e58d7a153368ba57db4c673c0e171 b/tests/resources/namespace.git/objects/56/26abf0f72e58d7a153368ba57db4c673c0e171
new file mode 100644
index 0000000..4d54474
--- /dev/null
+++ b/tests/resources/namespace.git/objects/56/26abf0f72e58d7a153368ba57db4c673c0e171
Binary files differ
diff --git a/tests/resources/namespace.git/objects/56/300b5eae653453102ac1213e921973c066425b b/tests/resources/namespace.git/objects/56/300b5eae653453102ac1213e921973c066425b
new file mode 100644
index 0000000..59f5649
--- /dev/null
+++ b/tests/resources/namespace.git/objects/56/300b5eae653453102ac1213e921973c066425b
Binary files differ
diff --git a/tests/resources/namespace.git/objects/7e/eaa70d7c5592db920a2e107ce3918bd4c8a425 b/tests/resources/namespace.git/objects/7e/eaa70d7c5592db920a2e107ce3918bd4c8a425
new file mode 100644
index 0000000..55706bf
--- /dev/null
+++ b/tests/resources/namespace.git/objects/7e/eaa70d7c5592db920a2e107ce3918bd4c8a425
@@ -0,0 +1,2 @@
+x¥Ž] „}æ{Ͳ
+‰1¾x/°ü4íÅPš^_4ÞÀ·™o2“ %ç¥i<µšÐà} l=‘bf¥ìG«$M£Â íà¨SdñâšÖ.ù‰M¯¸¨,Ž:’‹d˜uœ¬ó2LFiOÆ ÞÛ\*<âÁ5Âs.y++\S§uOßàç.¡äHc¤”ˆÆÂDÑi?ÛÒŸ3âXÚ ýý¼o oY¼wR§ \ No newline at end of file
diff --git a/tests/resources/namespace.git/objects/85/10665149157c2bc901848c3e0b746954e9cbd9 b/tests/resources/namespace.git/objects/85/10665149157c2bc901848c3e0b746954e9cbd9
new file mode 100644
index 0000000..2f2fef7
--- /dev/null
+++ b/tests/resources/namespace.git/objects/85/10665149157c2bc901848c3e0b746954e9cbd9
Binary files differ
diff --git a/tests/resources/namespace.git/objects/9e/bfa6bdc9d38075d29d26aa5df89b1cf635b269 b/tests/resources/namespace.git/objects/9e/bfa6bdc9d38075d29d26aa5df89b1cf635b269
new file mode 100644
index 0000000..33ec058
--- /dev/null
+++ b/tests/resources/namespace.git/objects/9e/bfa6bdc9d38075d29d26aa5df89b1cf635b269
Binary files differ
diff --git a/tests/resources/namespace.git/objects/9e/f15e3c5c0c8c6913936f843ad967cbe5541f0d b/tests/resources/namespace.git/objects/9e/f15e3c5c0c8c6913936f843ad967cbe5541f0d
new file mode 100644
index 0000000..0759911
--- /dev/null
+++ b/tests/resources/namespace.git/objects/9e/f15e3c5c0c8c6913936f843ad967cbe5541f0d
Binary files differ
diff --git a/tests/resources/namespace.git/objects/af/5626b4a114abcb82d63db7c8082c3c4756e51b b/tests/resources/namespace.git/objects/af/5626b4a114abcb82d63db7c8082c3c4756e51b
new file mode 100644
index 0000000..822bc15
--- /dev/null
+++ b/tests/resources/namespace.git/objects/af/5626b4a114abcb82d63db7c8082c3c4756e51b
Binary files differ
diff --git a/tests/resources/namespace.git/objects/af/81e4bd99cbfe6f05a501f1e4c82db2bf803e02 b/tests/resources/namespace.git/objects/af/81e4bd99cbfe6f05a501f1e4c82db2bf803e02
new file mode 100644
index 0000000..a5e8ecb
--- /dev/null
+++ b/tests/resources/namespace.git/objects/af/81e4bd99cbfe6f05a501f1e4c82db2bf803e02
Binary files differ
diff --git a/tests/resources/namespace.git/objects/bf/d17f429f4e2d121769213171ad57ca2e5173f9 b/tests/resources/namespace.git/objects/bf/d17f429f4e2d121769213171ad57ca2e5173f9
new file mode 100644
index 0000000..ebd27b8
--- /dev/null
+++ b/tests/resources/namespace.git/objects/bf/d17f429f4e2d121769213171ad57ca2e5173f9
@@ -0,0 +1,2 @@
+x¥Ž]
+Â0„}Î)öÊæ× ˆøâ ¼@šÝÒ‚5’¤x}«xßf¾áËeYæÆ›]¯"€‰"¬Ñâ@c`ò2²ñÖ‹M.{"’ ž©Ê£ƒå"ëÁZÉ”˜]ÀÈ„Î2ïÍ‘)Ê ÒÚ§RáʯTnSYZyÀI6úIù¿vÈe9ƒAkìÑ!ªn²]þ¼Qwi 6ûimsj}Îê £•RÌ \ No newline at end of file
diff --git a/tests/resources/namespace.git/objects/ec/947e3dd7a7752d078f1ed0cfde7457b21fef58 b/tests/resources/namespace.git/objects/ec/947e3dd7a7752d078f1ed0cfde7457b21fef58
new file mode 100644
index 0000000..a6e1d1d
--- /dev/null
+++ b/tests/resources/namespace.git/objects/ec/947e3dd7a7752d078f1ed0cfde7457b21fef58
Binary files differ
diff --git a/tests/resources/namespace.git/objects/f7/19efd430d52bcfc8566a43b2eb655688d38871 b/tests/resources/namespace.git/objects/f7/19efd430d52bcfc8566a43b2eb655688d38871
new file mode 100644
index 0000000..b2d51d9
--- /dev/null
+++ b/tests/resources/namespace.git/objects/f7/19efd430d52bcfc8566a43b2eb655688d38871
Binary files differ
diff --git a/tests/resources/namespace.git/objects/f7/5ba05f340c51065cbea2e1fdbfe5fe13144c97 b/tests/resources/namespace.git/objects/f7/5ba05f340c51065cbea2e1fdbfe5fe13144c97
new file mode 100644
index 0000000..044001e
--- /dev/null
+++ b/tests/resources/namespace.git/objects/f7/5ba05f340c51065cbea2e1fdbfe5fe13144c97
Binary files differ
diff --git a/tests/resources/namespace.git/refs/heads/branch b/tests/resources/namespace.git/refs/heads/branch
new file mode 100644
index 0000000..6962546
--- /dev/null
+++ b/tests/resources/namespace.git/refs/heads/branch
@@ -0,0 +1 @@
+bfd17f429f4e2d121769213171ad57ca2e5173f9
diff --git a/tests/resources/namespace.git/refs/heads/main b/tests/resources/namespace.git/refs/heads/main
new file mode 100644
index 0000000..b869769
--- /dev/null
+++ b/tests/resources/namespace.git/refs/heads/main
@@ -0,0 +1 @@
+7eeaa70d7c5592db920a2e107ce3918bd4c8a425
diff --git a/tests/resources/namespace.git/refs/namespaces/name1/refs/heads/four b/tests/resources/namespace.git/refs/namespaces/name1/refs/heads/four
new file mode 100644
index 0000000..86fdb41
--- /dev/null
+++ b/tests/resources/namespace.git/refs/namespaces/name1/refs/heads/four
@@ -0,0 +1 @@
+04433ff5b52d6ad534fd6288de4a57b81cc12188
diff --git a/tests/resources/namespace.git/refs/namespaces/name1/refs/heads/one b/tests/resources/namespace.git/refs/namespaces/name1/refs/heads/one
new file mode 100644
index 0000000..22d8137
--- /dev/null
+++ b/tests/resources/namespace.git/refs/namespaces/name1/refs/heads/one
@@ -0,0 +1 @@
+420d51ce75a87909e29659da2072ffd3d5daf5b7
diff --git a/tests/resources/nasty/.gitted/HEAD b/tests/resources/nasty/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/nasty/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/nasty/.gitted/index b/tests/resources/nasty/.gitted/index
new file mode 100644
index 0000000..782a50d
--- /dev/null
+++ b/tests/resources/nasty/.gitted/index
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/02/28b21d477f67b9f7720565da9e760b84c8b85b b/tests/resources/nasty/.gitted/objects/02/28b21d477f67b9f7720565da9e760b84c8b85b
new file mode 100644
index 0000000..e7cd63a
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/02/28b21d477f67b9f7720565da9e760b84c8b85b
@@ -0,0 +1,3 @@
+x¥]
+!…{vw ^Gˆè¥Ô½b#8F´û,ÚAoç|œj)·èä®7"ˆAÚ ji±&ôÁ.(q¶ÉIg¼vBYæ=×çøô-Â5ײÕ4è'è+~m
+µ§Älì¹æœ :Î;ý9Ã.w¿å×ôõ’@õ \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/04/18f28a75dc0c4951c01842e0d794843a88178a b/tests/resources/nasty/.gitted/objects/04/18f28a75dc0c4951c01842e0d794843a88178a
new file mode 100644
index 0000000..7f8722e
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/04/18f28a75dc0c4951c01842e0d794843a88178a
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/04/fab819d8388295cbe3496310e4e53ef8f4a115 b/tests/resources/nasty/.gitted/objects/04/fab819d8388295cbe3496310e4e53ef8f4a115
new file mode 100644
index 0000000..688b970
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/04/fab819d8388295cbe3496310e4e53ef8f4a115
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/05/1229bf9d30ec923052ff42db8069ccdc17159d b/tests/resources/nasty/.gitted/objects/05/1229bf9d30ec923052ff42db8069ccdc17159d
new file mode 100644
index 0000000..4321601
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/05/1229bf9d30ec923052ff42db8069ccdc17159d
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/07/f9d4d85b75187e4db5b9cbcad3e6218582bd57 b/tests/resources/nasty/.gitted/objects/07/f9d4d85b75187e4db5b9cbcad3e6218582bd57
new file mode 100644
index 0000000..f040922
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/07/f9d4d85b75187e4db5b9cbcad3e6218582bd57
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/09/9ed86cb8501ae483b1855c351fe1a506ac9631 b/tests/resources/nasty/.gitted/objects/09/9ed86cb8501ae483b1855c351fe1a506ac9631
new file mode 100644
index 0000000..7738fc8
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/09/9ed86cb8501ae483b1855c351fe1a506ac9631
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/0a/78e40e54cc471c0415ca0680550f242e7843e2 b/tests/resources/nasty/.gitted/objects/0a/78e40e54cc471c0415ca0680550f242e7843e2
new file mode 100644
index 0000000..d59836e
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/0a/78e40e54cc471c0415ca0680550f242e7843e2
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/0b/8206dd72a3b3b932fb562f92d29199b9398390 b/tests/resources/nasty/.gitted/objects/0b/8206dd72a3b3b932fb562f92d29199b9398390
new file mode 100644
index 0000000..b063615
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/0b/8206dd72a3b3b932fb562f92d29199b9398390
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/0d/45fb57852c2229346a800bd3fc58e32527a21c b/tests/resources/nasty/.gitted/objects/0d/45fb57852c2229346a800bd3fc58e32527a21c
new file mode 100644
index 0000000..d0433a0
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/0d/45fb57852c2229346a800bd3fc58e32527a21c
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/10/cb44a89d1a9e8bf74de3f11a2a61ee833f13b1 b/tests/resources/nasty/.gitted/objects/10/cb44a89d1a9e8bf74de3f11a2a61ee833f13b1
new file mode 100644
index 0000000..9d14298
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/10/cb44a89d1a9e8bf74de3f11a2a61ee833f13b1
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/11/9f6cd3535de0e2a15654947a7b1a5affbf1406 b/tests/resources/nasty/.gitted/objects/11/9f6cd3535de0e2a15654947a7b1a5affbf1406
new file mode 100644
index 0000000..fb03b26
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/11/9f6cd3535de0e2a15654947a7b1a5affbf1406
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/12/12c12915820e1ad523b6305c0dcdefea8b7e97 b/tests/resources/nasty/.gitted/objects/12/12c12915820e1ad523b6305c0dcdefea8b7e97
new file mode 100644
index 0000000..95bc4c8
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/12/12c12915820e1ad523b6305c0dcdefea8b7e97
@@ -0,0 +1 @@
+x¥Q Dýæ{¶P ‰1þx½ÀÛ`"%¡ãíEã ü›y/™ %ç[œÕ®Uf°Q³r£v–Æ-Þ)oó„„ÁXM‹£ GK¥Â9>©F¸¦’·²Â;ý¤ů ¡ä# F3+«q„½œ¤öóÆΈ˶ôÞVAž \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/13/e5f8be09e8b7db074fb39b96e08215cc4a36f1 b/tests/resources/nasty/.gitted/objects/13/e5f8be09e8b7db074fb39b96e08215cc4a36f1
new file mode 100644
index 0000000..ea54830
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/13/e5f8be09e8b7db074fb39b96e08215cc4a36f1
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/14/e70ab559b4c6a8a6fc9b6f538bd1f3934be725 b/tests/resources/nasty/.gitted/objects/14/e70ab559b4c6a8a6fc9b6f538bd1f3934be725
new file mode 100644
index 0000000..371951a
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/14/e70ab559b4c6a8a6fc9b6f538bd1f3934be725
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/15/f7d9f9514eeb65b9588c49b10b1da145a729a2 b/tests/resources/nasty/.gitted/objects/15/f7d9f9514eeb65b9588c49b10b1da145a729a2
new file mode 100644
index 0000000..a7f3683
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/15/f7d9f9514eeb65b9588c49b10b1da145a729a2
@@ -0,0 +1,2 @@
+x1Â0 E™s
+ïH(iÚº‘baeâ&q”Á(5BÜžpÞôõ†ÿ¢Ôº*¸`wژѓ3ºìÝC”‡œ1øepB²>»™ ½´HƒKzSKp+R7yÀ‘»ý­s]c“M²¢Ô¸ÁÓŒK€½í˜.{Wùÿs¥M?P)“´|?ó \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/16/35c47d80914f0abfa43dd4234a948db5bdb107 b/tests/resources/nasty/.gitted/objects/16/35c47d80914f0abfa43dd4234a948db5bdb107
new file mode 100644
index 0000000..f82b82b
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/16/35c47d80914f0abfa43dd4234a948db5bdb107
@@ -0,0 +1,2 @@
+x=!…­9Åô&–Ÿ…Ä[+/ÀÂÝgcŒ·¯àkÞËW|/Q­ƒ
+òÀ ¼œŒ±gD’±®d?*k† ÝRœÒ‹ñÅ+5¸åwl+ÕNO8ã ¿u­[jÔ©ð)Q½€šôÍ>ÀQŽˆÇ/ãÿq?Pc‚=òú¼Õ?q \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/16/a701796bc3670e5c2fdaeccb7f1280c60b373f b/tests/resources/nasty/.gitted/objects/16/a701796bc3670e5c2fdaeccb7f1280c60b373f
new file mode 100644
index 0000000..46ed5c1
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/16/a701796bc3670e5c2fdaeccb7f1280c60b373f
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/19/1381ee74dec49c89f99a62d055cb1058ba0de9 b/tests/resources/nasty/.gitted/objects/19/1381ee74dec49c89f99a62d055cb1058ba0de9
new file mode 100644
index 0000000..f1619a2
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/19/1381ee74dec49c89f99a62d055cb1058ba0de9
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/1e/3c845808fa5883aa4bcf2f882172edb72a7a32 b/tests/resources/nasty/.gitted/objects/1e/3c845808fa5883aa4bcf2f882172edb72a7a32
new file mode 100644
index 0000000..e25f153
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/1e/3c845808fa5883aa4bcf2f882172edb72a7a32
@@ -0,0 +1,2 @@
+x¥Q Dýæ{›eaiIŒñÇè(Ð`"%¡ãíEã ü›y/™ñ%ç[iÕ®ÕƒæeæqbòDd•6nBœƒZ<OQÓèHzá-•
+çðt5À5•¼•±ÓO:ůøµÁ—|©¥±Z‹°GFöóÿœ—»ÛÒkx›\@| \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/24/676d5e93f9fa7b568f38d7bce01772908e982b b/tests/resources/nasty/.gitted/objects/24/676d5e93f9fa7b568f38d7bce01772908e982b
new file mode 100644
index 0000000..fc11c20
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/24/676d5e93f9fa7b568f38d7bce01772908e982b
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/26/b665c162f67acae67779445f3c7b9782b0a6d7 b/tests/resources/nasty/.gitted/objects/26/b665c162f67acae67779445f3c7b9782b0a6d7
new file mode 100644
index 0000000..6e1a85c
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/26/b665c162f67acae67779445f3c7b9782b0a6d7
@@ -0,0 +1 @@
+x¥Q Dýæ{›… ‰1þx½À–Ò`"%¡ãíEã ü›y/™ %ç[éÇ]«1-^éÀ#YB^m ÖN²ÒŽ&RnR¥3,øÑR©pžŸ\g¸¦’·²Â!vúI§ø¿6„’ µ$Th½=DÑi?oñÏq¹ó–^ÿx@¢ \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/27/db66b046536a0e4f64c4f8c3a490641c3fa5e5 b/tests/resources/nasty/.gitted/objects/27/db66b046536a0e4f64c4f8c3a490641c3fa5e5
new file mode 100644
index 0000000..10b555b
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/27/db66b046536a0e4f64c4f8c3a490641c3fa5e5
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/2a/9eb82c733e31ae312cee349084dcbc6f69639a b/tests/resources/nasty/.gitted/objects/2a/9eb82c733e31ae312cee349084dcbc6f69639a
new file mode 100644
index 0000000..c4717d5
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/2a/9eb82c733e31ae312cee349084dcbc6f69639a
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/2b/4b774d8c5441b22786531f34ffc77800cda8cf b/tests/resources/nasty/.gitted/objects/2b/4b774d8c5441b22786531f34ffc77800cda8cf
new file mode 100644
index 0000000..b286daa
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/2b/4b774d8c5441b22786531f34ffc77800cda8cf
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/2d/23d51590ec2f53fe4b5bb3e5ca62e35e4ef85a b/tests/resources/nasty/.gitted/objects/2d/23d51590ec2f53fe4b5bb3e5ca62e35e4ef85a
new file mode 100644
index 0000000..5d47d82
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/2d/23d51590ec2f53fe4b5bb3e5ca62e35e4ef85a
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/33/8190107c7ee7d8f5aa30061fc19b7d5ddcda86 b/tests/resources/nasty/.gitted/objects/33/8190107c7ee7d8f5aa30061fc19b7d5ddcda86
new file mode 100644
index 0000000..e539ccf
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/33/8190107c7ee7d8f5aa30061fc19b7d5ddcda86
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/35/ae236308929a536fb4e852278a9b98c42babb3 b/tests/resources/nasty/.gitted/objects/35/ae236308929a536fb4e852278a9b98c42babb3
new file mode 100644
index 0000000..b8633de
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/35/ae236308929a536fb4e852278a9b98c42babb3
@@ -0,0 +1 @@
+x;Â0©}Ší‘q !Z*.àÏZIa6²!n¹S=Mñ&Q­ƒZä"x…:zë-ÊaÌÚOÑØ £B?cŒV›´‘"¼x¥·ü-Ãc¥Úé gö·®uK:>%ªPÚØÉÍ~£ˆ!G—ñÿq?PC‚=ðúª?Ü \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/38/0b9e58872ccf1d858be4b0fc612514a080bc40 b/tests/resources/nasty/.gitted/objects/38/0b9e58872ccf1d858be4b0fc612514a080bc40
new file mode 100644
index 0000000..a911c3c
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/38/0b9e58872ccf1d858be4b0fc612514a080bc40
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/39/fb3af508440cf970b92767f6d081c811574d2a b/tests/resources/nasty/.gitted/objects/39/fb3af508440cf970b92767f6d081c811574d2a
new file mode 100644
index 0000000..3854748
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/39/fb3af508440cf970b92767f6d081c811574d2a
@@ -0,0 +1,2 @@
+x¥=
+B1„­sŠíÉÏ&® b#bÿ.³ûxq!F¼¾Q¼SÍ|3Ek½up;\õ&‘Î甸pò9X‰8²˜bºz» &?û¢ NüÊaZ´>ô{ôãŽò-~iS´À¡KDÖ¢‡µ2ƒŽó.ÎÖçËdÞΤ?ï \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/3b/24e5c751ee9c7c89df32a0d959748aa3d0112c b/tests/resources/nasty/.gitted/objects/3b/24e5c751ee9c7c89df32a0d959748aa3d0112c
new file mode 100644
index 0000000..5adcd14
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/3b/24e5c751ee9c7c89df32a0d959748aa3d0112c
@@ -0,0 +1,2 @@
+x1Â0 E™s
+ïHÈmš&•baeâ&‰ÕÁ(1BÜžpþôô†÷£”²) î´æ !ÌÙ±Ðù¦¹£#tœ˜)Y;Þ#z4ôÒU*\Ò›j‚Û*¥ÉŽ¹ÛË«4a=D)'F;¹Ù‡öØgºì¿šÿ/˜+5ý@¡OÒõ c?ä \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/42/1376db9e8aee847e9d774891e73098a7415e94 b/tests/resources/nasty/.gitted/objects/42/1376db9e8aee847e9d774891e73098a7415e94
new file mode 100644
index 0000000..0666ba7
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/42/1376db9e8aee847e9d774891e73098a7415e94
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/44/14ac920acabc3eb00e3cf9375eeb0cb6859c15 b/tests/resources/nasty/.gitted/objects/44/14ac920acabc3eb00e3cf9375eeb0cb6859c15
new file mode 100644
index 0000000..4eaaa0c
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/44/14ac920acabc3eb00e3cf9375eeb0cb6859c15
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/44/2894787eddb1e84a952f17a027590e2c6c02cd b/tests/resources/nasty/.gitted/objects/44/2894787eddb1e84a952f17a027590e2c6c02cd
new file mode 100644
index 0000000..c81b0e6
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/44/2894787eddb1e84a952f17a027590e2c6c02cd
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/46/fe10fa23259b089ab050788b06df979cd7d054 b/tests/resources/nasty/.gitted/objects/46/fe10fa23259b089ab050788b06df979cd7d054
new file mode 100644
index 0000000..6d1f52d
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/46/fe10fa23259b089ab050788b06df979cd7d054
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/4a/a347c8bb0456230f43f34833c97b9f52c40f62 b/tests/resources/nasty/.gitted/objects/4a/a347c8bb0456230f43f34833c97b9f52c40f62
new file mode 100644
index 0000000..2a54fe2
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/4a/a347c8bb0456230f43f34833c97b9f52c40f62
@@ -0,0 +1,3 @@
+x¥]
+Â0„}Î)ö–Ý6? ˆøâ ôi²!‚i ˆ·7Š7ðmæû`Æ—œo ÈN»V™ÁE©œ#^ ‡8£ä@ r\¬ŽÌ¬FãÉxá-•
+çðt5À5•¼•Üé'ø+~mð%$i;i‰#ìQ!ŠNûyã?gÄåî¶ôÞŠ½AÍ \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/4d/83272d0d372e1232ddc4ff3260d76fdfa2015a b/tests/resources/nasty/.gitted/objects/4d/83272d0d372e1232ddc4ff3260d76fdfa2015a
new file mode 100644
index 0000000..d362f1d
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/4d/83272d0d372e1232ddc4ff3260d76fdfa2015a
@@ -0,0 +1,2 @@
+x¥K
+1D]ç}¥“ÉDÜx/Oà NH"2·woà®ê=¨Š5çe€@}L<%´AD“Ì„VºI:%Ir§IDPÇüs̵Á-½|KpŸkîµÀ™vúIWúŠ_;Åš/À¥@Ç-WGTˆl§ûù ?gXñ}lзêºDX—ò`oÔ™Dù \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/53/41a7b545d71198b076b8ba3374a75c9a290640 b/tests/resources/nasty/.gitted/objects/53/41a7b545d71198b076b8ba3374a75c9a290640
new file mode 100644
index 0000000..fdfe6eb
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/53/41a7b545d71198b076b8ba3374a75c9a290640
@@ -0,0 +1,3 @@
+x¥]
+Â0„}Î)ö–ݤiñÅèò³%‚i ˆ·7Š7ðmæø&”œo Ȫ]«Ìàš­öÄÈ^‹”ËŒz"²Ñ“’1bˆµp–J…s|ºášJÞÊ
+îô“Nü~m%Fš¬ÒFØcW‰Nûyã?5ârw[z o·f@© \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/5d/1ee4f24f66dcd62a30248588d33804656b2073 b/tests/resources/nasty/.gitted/objects/5d/1ee4f24f66dcd62a30248588d33804656b2073
new file mode 100644
index 0000000..ffd9bfd
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/5d/1ee4f24f66dcd62a30248588d33804656b2073
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/65/94bdbad86bbc8d3ed0806a23827203fbab56c6 b/tests/resources/nasty/.gitted/objects/65/94bdbad86bbc8d3ed0806a23827203fbab56c6
new file mode 100644
index 0000000..fa990d4
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/65/94bdbad86bbc8d3ed0806a23827203fbab56c6
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/68/e8bce48725490c376d57ebc60f0170605951a5 b/tests/resources/nasty/.gitted/objects/68/e8bce48725490c376d57ebc60f0170605951a5
new file mode 100644
index 0000000..c23f815
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/68/e8bce48725490c376d57ebc60f0170605951a5
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/69/7dc3d723a018538eb819d5db2035c15109af73 b/tests/resources/nasty/.gitted/objects/69/7dc3d723a018538eb819d5db2035c15109af73
new file mode 100644
index 0000000..6d7d9f5
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/69/7dc3d723a018538eb819d5db2035c15109af73
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/6b/7d8a5a48a3c753b75a8fe5196f9c8704ac64ad b/tests/resources/nasty/.gitted/objects/6b/7d8a5a48a3c753b75a8fe5196f9c8704ac64ad
new file mode 100644
index 0000000..121277f
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/6b/7d8a5a48a3c753b75a8fe5196f9c8704ac64ad
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/6c/1f5f6fec515d33036b44c596bfae28fc460cba b/tests/resources/nasty/.gitted/objects/6c/1f5f6fec515d33036b44c596bfae28fc460cba
new file mode 100644
index 0000000..8172b7f
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/6c/1f5f6fec515d33036b44c596bfae28fc460cba
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/71/2ceb8eb3e57072447715bc4057c57aa50f629a b/tests/resources/nasty/.gitted/objects/71/2ceb8eb3e57072447715bc4057c57aa50f629a
new file mode 100644
index 0000000..9ed35d7
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/71/2ceb8eb3e57072447715bc4057c57aa50f629a
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/7a/0538bc4e20aecb36ef221f2077eb30ebe0bcb2 b/tests/resources/nasty/.gitted/objects/7a/0538bc4e20aecb36ef221f2077eb30ebe0bcb2
new file mode 100644
index 0000000..0c3ea26
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/7a/0538bc4e20aecb36ef221f2077eb30ebe0bcb2
@@ -0,0 +1,2 @@
+x1!E­9Åô&X!1ÆÆÖÊ ÌÂÝÇÀãíÅ+øªŸWü—¸ÖMÀD½“FÚ\‚‰9ئèÓBÖÅÙMŽ¼¥ŠCc¼Â—¬ÜàšßØ2ÜW®p¢aëR·Ô¸s‘Câz3Yççcˆ°×5äè
+ýÿ nØå<QÖ/Òä? \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/7a/e174dda8f105a582c593b52d74545a3565819d b/tests/resources/nasty/.gitted/objects/7a/e174dda8f105a582c593b52d74545a3565819d
new file mode 100644
index 0000000..17dec59
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/7a/e174dda8f105a582c593b52d74545a3565819d
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/7b/b1dd08b2c7d73084934954e4196e67004b0279 b/tests/resources/nasty/.gitted/objects/7b/b1dd08b2c7d73084934954e4196e67004b0279
new file mode 100644
index 0000000..e6640e6
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/7b/b1dd08b2c7d73084934954e4196e67004b0279
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/7d/4e382485ace068fb83b768ba1a1c674afbdc1d b/tests/resources/nasty/.gitted/objects/7d/4e382485ace068fb83b768ba1a1c674afbdc1d
new file mode 100644
index 0000000..f7be9ab
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/7d/4e382485ace068fb83b768ba1a1c674afbdc1d
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/7f/924ca37670afa06c7a481a2487b728b2c0185a b/tests/resources/nasty/.gitted/objects/7f/924ca37670afa06c7a481a2487b728b2c0185a
new file mode 100644
index 0000000..5964a27
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/7f/924ca37670afa06c7a481a2487b728b2c0185a
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/80/24458e7ee49c456fd8c45d3591e9936bf613b3 b/tests/resources/nasty/.gitted/objects/80/24458e7ee49c456fd8c45d3591e9936bf613b3
new file mode 100644
index 0000000..d2074aa
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/80/24458e7ee49c456fd8c45d3591e9936bf613b3
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/80/a8fe4f10626c50b3a4fd065a4604bafc9f30fa b/tests/resources/nasty/.gitted/objects/80/a8fe4f10626c50b3a4fd065a4604bafc9f30fa
new file mode 100644
index 0000000..ad27251
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/80/a8fe4f10626c50b3a4fd065a4604bafc9f30fa
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/81/e2b84864f16ebd285b34a2b1e87ebb41f4c230 b/tests/resources/nasty/.gitted/objects/81/e2b84864f16ebd285b34a2b1e87ebb41f4c230
new file mode 100644
index 0000000..c28ad0a
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/81/e2b84864f16ebd285b34a2b1e87ebb41f4c230
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/82/482ad2e683edfc14f7de359e4f9a5e88909c51 b/tests/resources/nasty/.gitted/objects/82/482ad2e683edfc14f7de359e4f9a5e88909c51
new file mode 100644
index 0000000..16ea98e
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/82/482ad2e683edfc14f7de359e4f9a5e88909c51
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/88/6c0f5f71057d846f71f05a05fdffad332bc070 b/tests/resources/nasty/.gitted/objects/88/6c0f5f71057d846f71f05a05fdffad332bc070
new file mode 100644
index 0000000..432eea2
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/88/6c0f5f71057d846f71f05a05fdffad332bc070
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/89/9ff28744bed5bece69c78ba752c7dc3e954629 b/tests/resources/nasty/.gitted/objects/89/9ff28744bed5bece69c78ba752c7dc3e954629
new file mode 100644
index 0000000..6f552e5
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/89/9ff28744bed5bece69c78ba752c7dc3e954629
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/8b/cbb6e0c0f9554efd5401e1ec14a4b2595eb3bf b/tests/resources/nasty/.gitted/objects/8b/cbb6e0c0f9554efd5401e1ec14a4b2595eb3bf
new file mode 100644
index 0000000..bba2035
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/8b/cbb6e0c0f9554efd5401e1ec14a4b2595eb3bf
@@ -0,0 +1,2 @@
+x¥]
+!…{vw ^F…ˆ^ZAmÀÑ+9‚cD»Ï¢ôvÎ÷Á9¡–rë€Nîz#‚—&:ΈÙÖ„)ðTVz‹z‰Æ-–ùGϵÁ9>}‹p͵lu… úI'úŠ_›B-G@…³SR*{®9gƒŽóNΰËÝoù5½ƒA) \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/8c/e7a3ef59c3d602a0296321eb964218f3d52fae b/tests/resources/nasty/.gitted/objects/8c/e7a3ef59c3d602a0296321eb964218f3d52fae
new file mode 100644
index 0000000..6f3484c
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/8c/e7a3ef59c3d602a0296321eb964218f3d52fae
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/8f/1dcd43aa0164eb6ec319c3ec8879ca5cf62c1e b/tests/resources/nasty/.gitted/objects/8f/1dcd43aa0164eb6ec319c3ec8879ca5cf62c1e
new file mode 100644
index 0000000..f802e5a
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/8f/1dcd43aa0164eb6ec319c3ec8879ca5cf62c1e
@@ -0,0 +1,2 @@
+x¥K
+1D]ç}‡|:c7ˆ¸ñztˆ` ÌDÄÛŸ«zªb«õÚÁ°ÛôEÈ"Ù¬Ìä$åh0ï’8Ï‚™ƒ"Ö½QáÑK[à”žaIp)­®í{ô“Žò¿6ÅV`ÐÌŒHÚÂV{­Õ ã¼ËŸ3ê| kyMoA/ \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/91/602c85bb50dd834205edd30435b77d5bb9ccf0 b/tests/resources/nasty/.gitted/objects/91/602c85bb50dd834205edd30435b77d5bb9ccf0
new file mode 100644
index 0000000..d7147fb
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/91/602c85bb50dd834205edd30435b77d5bb9ccf0
@@ -0,0 +1,3 @@
+x¥Q
+1 Dýî)r—d»ÛZñÇèB“¥‚µÐ­ˆ··Š7ðoÞ˜‰%çk
+vÓª*ÄiÔ£Š“°8kÉw`‰½šy¢(!~´T*œäÉUà’J^ËöÚí'õ[ühˆ%€&òH“ö8#šnûyÓ?gÌùÆkz o!çA2 \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/91/cd2c95af92883550b45fcc838013ae7e2954df b/tests/resources/nasty/.gitted/objects/91/cd2c95af92883550b45fcc838013ae7e2954df
new file mode 100644
index 0000000..da9d5c4
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/91/cd2c95af92883550b45fcc838013ae7e2954df
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/94/f37c29173c8fa45a232b17e745c82132b2fafd b/tests/resources/nasty/.gitted/objects/94/f37c29173c8fa45a232b17e745c82132b2fafd
new file mode 100644
index 0000000..475d26b
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/94/f37c29173c8fa45a232b17e745c82132b2fafd
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/96/156716851c0afb4702b0d2c4ac8c496a730e29 b/tests/resources/nasty/.gitted/objects/96/156716851c0afb4702b0d2c4ac8c496a730e29
new file mode 100644
index 0000000..57419bc
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/96/156716851c0afb4702b0d2c4ac8c496a730e29
@@ -0,0 +1 @@
+x;Â0©sŠí‘PüÙKÑÐRqµ½VR˜EÎ"Äí1W`ª§)Þ$©uU0aÜic›­Ëh0ŒœlAWØGŒÑ1&š,;dÏeF襋4¸æ7µ ÷Eê&8q·¿u©kj²IÑC’zcÇé8Ø¡ËÞUþÿa¸Ñ¦¨”àIº|\ä@o \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/96/3fdf003bf7261b9155c5748dc0945349b69e68 b/tests/resources/nasty/.gitted/objects/96/3fdf003bf7261b9155c5748dc0945349b69e68
new file mode 100644
index 0000000..ff1d33e
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/96/3fdf003bf7261b9155c5748dc0945349b69e68
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/97/c14994866466aeb73e769a6f34e07c7f4b53f7 b/tests/resources/nasty/.gitted/objects/97/c14994866466aeb73e769a6f34e07c7f4b53f7
new file mode 100644
index 0000000..9f76799
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/97/c14994866466aeb73e769a6f34e07c7f4b53f7
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/9a/b85e507899c19dca57778c9b6e5f1ec799b911 b/tests/resources/nasty/.gitted/objects/9a/b85e507899c19dca57778c9b6e5f1ec799b911
new file mode 100644
index 0000000..aa24a8f
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/9a/b85e507899c19dca57778c9b6e5f1ec799b911
@@ -0,0 +1,3 @@
+x=
+1F­sŠéÙ5ÿ bckåf' »EIFÄÛ¯à«>^ñ=âZ79N;i9ƒ[|
+hÑÔä­^¼ÅP²£+‘‚Ÿ ’3˜¾då×ôÆ–à¾ríü€Sö·.u£Æ‹ˆëæ£6Öùa? Ô£+ùÿuÃ.¨HðDY¿2Ù@% \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/9d/5898503adc01d763e279ac8fcefbe865b19031 b/tests/resources/nasty/.gitted/objects/9d/5898503adc01d763e279ac8fcefbe865b19031
new file mode 100644
index 0000000..7cb3106
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/9d/5898503adc01d763e279ac8fcefbe865b19031
@@ -0,0 +1,4 @@
+x¥O[Â ô›Sì4 R
+Icüñz
+Û´±t bÔÛK7ðk^ÉL&pJS%qS2ôTô=5­1vo•4Ñ5ƒt®‚÷zˆºu­þQFÎpŠOŸ#\FNw^ £ê®ìHßà§vÓ¤V袶°ÅQT·Žú³Fœß©çy
+0OËèu›yý$>›…Fû \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/9e/24726d64589ba02430da8cebb5712dad35593d b/tests/resources/nasty/.gitted/objects/9e/24726d64589ba02430da8cebb5712dad35593d
new file mode 100644
index 0000000..2cf9535
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/9e/24726d64589ba02430da8cebb5712dad35593d
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/9e/683cdaf9ea2727c891b4cf8f7f11e9e28a67ca b/tests/resources/nasty/.gitted/objects/9e/683cdaf9ea2727c891b4cf8f7f11e9e28a67ca
new file mode 100644
index 0000000..2e36dca
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/9e/683cdaf9ea2727c891b4cf8f7f11e9e28a67ca
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/a0/d89aa95628fcd6b64fd5b23dd56b906b06bfe2 b/tests/resources/nasty/.gitted/objects/a0/d89aa95628fcd6b64fd5b23dd56b906b06bfe2
new file mode 100644
index 0000000..c1de43b
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/a0/d89aa95628fcd6b64fd5b23dd56b906b06bfe2
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/a5/76a98d3279989226992610372035b76a01a3e9 b/tests/resources/nasty/.gitted/objects/a5/76a98d3279989226992610372035b76a01a3e9
new file mode 100644
index 0000000..75fa458
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/a5/76a98d3279989226992610372035b76a01a3e9
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/a7/8dde970cffbb71d67bef2a74aa72c6621d9819 b/tests/resources/nasty/.gitted/objects/a7/8dde970cffbb71d67bef2a74aa72c6621d9819
new file mode 100644
index 0000000..78c2fe4
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/a7/8dde970cffbb71d67bef2a74aa72c6621d9819
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/ac/84d85a425b2a21fd0ffccacac6c48823fc98c8 b/tests/resources/nasty/.gitted/objects/ac/84d85a425b2a21fd0ffccacac6c48823fc98c8
new file mode 100644
index 0000000..b08e247
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/ac/84d85a425b2a21fd0ffccacac6c48823fc98c8
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/af/45aa1eb7edf804ed10f70efb96fd178527c17c b/tests/resources/nasty/.gitted/objects/af/45aa1eb7edf804ed10f70efb96fd178527c17c
new file mode 100644
index 0000000..9e270bf
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/af/45aa1eb7edf804ed10f70efb96fd178527c17c
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/b1/1df9aee97a65817e8904a74f5e6a1c62c7a275 b/tests/resources/nasty/.gitted/objects/b1/1df9aee97a65817e8904a74f5e6a1c62c7a275
new file mode 100644
index 0000000..b2e0eda
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/b1/1df9aee97a65817e8904a74f5e6a1c62c7a275
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/b8/3795b1e0eb54f22f7056119db132500d0cdc05 b/tests/resources/nasty/.gitted/objects/b8/3795b1e0eb54f22f7056119db132500d0cdc05
new file mode 100644
index 0000000..6cee4f9
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/b8/3795b1e0eb54f22f7056119db132500d0cdc05
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/b8/edf3ad62dbcbc983857a5bfee7b0181ee1a513 b/tests/resources/nasty/.gitted/objects/b8/edf3ad62dbcbc983857a5bfee7b0181ee1a513
new file mode 100644
index 0000000..bf44626
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/b8/edf3ad62dbcbc983857a5bfee7b0181ee1a513
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/bb/29ec85546d29b0bcc314242660d7772b0a3803 b/tests/resources/nasty/.gitted/objects/bb/29ec85546d29b0bcc314242660d7772b0a3803
new file mode 100644
index 0000000..00ab02c
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/bb/29ec85546d29b0bcc314242660d7772b0a3803
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/bc/e2dabe5766838216d95f199d95aa4fd479a084 b/tests/resources/nasty/.gitted/objects/bc/e2dabe5766838216d95f199d95aa4fd479a084
new file mode 100644
index 0000000..b1eab10
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/bc/e2dabe5766838216d95f199d95aa4fd479a084
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/bf/7ab4723fcc57ecc7fceccf591d6c4773491569 b/tests/resources/nasty/.gitted/objects/bf/7ab4723fcc57ecc7fceccf591d6c4773491569
new file mode 100644
index 0000000..af02c6b
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/bf/7ab4723fcc57ecc7fceccf591d6c4773491569
@@ -0,0 +1,2 @@
+x¥K
+1D]ç}‡î|"n<^`¦;!‚™@&"ÞÞ(ÞÀ]Õ{PÅ%ç[òfÓj€Ž´ösôb0°×ŽÑj™÷¸óÌÂ4’ó¢¦GK¥ÂYžS¸¦’ײÀ!túI§ð¿6pÉG K#’ÓÆ¢괟·ð猺ܧ5½†7Õw@Ç \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/c2/a2ddd339574e5cbfd9228be840eb1bf496de4e b/tests/resources/nasty/.gitted/objects/c2/a2ddd339574e5cbfd9228be840eb1bf496de4e
new file mode 100644
index 0000000..939cf55
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/c2/a2ddd339574e5cbfd9228be840eb1bf496de4e
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/c3/a70f8a376f17adccfb52b48e2831bfef2a2172 b/tests/resources/nasty/.gitted/objects/c3/a70f8a376f17adccfb52b48e2831bfef2a2172
new file mode 100644
index 0000000..b43d3f1
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/c3/a70f8a376f17adccfb52b48e2831bfef2a2172
@@ -0,0 +1,2 @@
+xÍ=
+1`ë=Åô‚d'n6"6¶V^ ¿d‹8’Œˆ·7^ÁW=¾â½Àµn3©´”@y‹Êĸ¢Ó^{Ò˜ýb0F¤™hYMjr/)Üàß®E¸®pJCíR·Ð¸s–Càz†õq1«%Ø«‘iàø•ôÿÂts]>P]€§“ò?1 \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/c4/89e70ed6d9f6331770eae21a77d15afd11cd99 b/tests/resources/nasty/.gitted/objects/c4/89e70ed6d9f6331770eae21a77d15afd11cd99
new file mode 100644
index 0000000..1d76348
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/c4/89e70ed6d9f6331770eae21a77d15afd11cd99
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/c6/72414d4d08111145ef8202f21c95fa7e688aee b/tests/resources/nasty/.gitted/objects/c6/72414d4d08111145ef8202f21c95fa7e688aee
new file mode 100644
index 0000000..1b79b34
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/c6/72414d4d08111145ef8202f21c95fa7e688aee
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/c8/f98a1762ec016c30f0d73512df399dedefc3fd b/tests/resources/nasty/.gitted/objects/c8/f98a1762ec016c30f0d73512df399dedefc3fd
new file mode 100644
index 0000000..85ddc7f
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/c8/f98a1762ec016c30f0d73512df399dedefc3fd
@@ -0,0 +1,3 @@
+x=
+1F­sŠéÙÄ͈ØØZy1™%[ÄY’ñöÆ+øªW|/q­«€ŽÓN<´ÎKD¢èÑÙ =…8ÍèçÅ’CœI·
+_R¸Á5¿±e¸®Ÿp¢aëR×Ô¸ó"‡Äõ Úgë|ˆ°ŸjÈÑúÿAÝ°Ë*&ØPÊ+?ò \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/cc/bbfdb796f9b03298f5c7225e8f830784e1a3b1 b/tests/resources/nasty/.gitted/objects/cc/bbfdb796f9b03298f5c7225e8f830784e1a3b1
new file mode 100644
index 0000000..732474a
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/cc/bbfdb796f9b03298f5c7225e8f830784e1a3b1
@@ -0,0 +1,2 @@
+x¥OI
+1ôœWô]²tŒ#"^üè$­3‡LC&â÷͈?°Nµ@•¤”¹Õ~×*30rŽî`Ø›ÑÙèSê:f4³#´Ç‘8±¢W›¤Â-¿©f¸ORVYàÌÝÝØ•¿ÁO IÊ š0X°×ª»}¼ñŸ5jxÎíôZ`û¡>Þ›E® \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/cd/44b4ea1066b3fa1d4b3baad8dc1531aec287a6 b/tests/resources/nasty/.gitted/objects/cd/44b4ea1066b3fa1d4b3baad8dc1531aec287a6
new file mode 100644
index 0000000..51ad388
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/cd/44b4ea1066b3fa1d4b3baad8dc1531aec287a6
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/ce/22b3cd9a01efafc370879c1938e0c32fb6f195 b/tests/resources/nasty/.gitted/objects/ce/22b3cd9a01efafc370879c1938e0c32fb6f195
new file mode 100644
index 0000000..eb5acc3
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/ce/22b3cd9a01efafc370879c1938e0c32fb6f195
@@ -0,0 +1,3 @@
+x;
+B1E­ß*¦$ÿˆØØZ¹dÉ+âHÞˆ¸{ã<Õå÷ õ¾2È(v<j…œU¬¬5®¨˜EFÔÒ(£œÅ{¯²H:½¤7p)ï4
+Üõp¬ÓþÖ¹¯8h£;ú ¤ÒÆ:"ìÅd™rv¹þÿ°\ÓÆè ᙸ}®?F \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/cf/6fcf8cdf7e8d4cda3b11b0ba02d0d5125fbbd7 b/tests/resources/nasty/.gitted/objects/cf/6fcf8cdf7e8d4cda3b11b0ba02d0d5125fbbd7
new file mode 100644
index 0000000..f65e8ff
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/cf/6fcf8cdf7e8d4cda3b11b0ba02d0d5125fbbd7
@@ -0,0 +1,2 @@
+x¥]
+Â0„}Î)öJ²m“Šøâ ôùÙÐ`Ó•QoooàÛÌ70ÃxÎ9@%7e%kúh0ÒÇèœQAG­i­5èµF†^ ÂÞËÄ+ÃîÎç/0R¥u oðs;ÏyªE…ºÃ¶­ì¤•ÖñBÖˆÓ+;ž“‡9- çuæú©oG‚ \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/d2/eb26d4938550487de59a017a7bfee8ca46b5f4 b/tests/resources/nasty/.gitted/objects/d2/eb26d4938550487de59a017a7bfee8ca46b5f4
new file mode 100644
index 0000000..e458bf4
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/d2/eb26d4938550487de59a017a7bfee8ca46b5f4
@@ -0,0 +1,2 @@
+x¥Q
+B!Eûv³ã35!¢ŸVPÐqÄ Ÿà3¢ÝgÑú»÷¸—j)·Ò©MoÌ ·(­3”±Èšæ=›ä¼G2”UIøGϵÁ9>}‹p͵¬uúI'þŠ_›¨–#È4NYm¶¨Å ã¼óŸ3âr÷k~Mo"A< \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/dc/37c5f1521fb76fe1c1ac7b13187f9396a59247 b/tests/resources/nasty/.gitted/objects/dc/37c5f1521fb76fe1c1ac7b13187f9396a59247
new file mode 100644
index 0000000..57329de
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/dc/37c5f1521fb76fe1c1ac7b13187f9396a59247
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/de/bdc4a004fda6141a17d9c297617be70d40248f b/tests/resources/nasty/.gitted/objects/de/bdc4a004fda6141a17d9c297617be70d40248f
new file mode 100644
index 0000000..de34bd4
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/de/bdc4a004fda6141a17d9c297617be70d40248f
@@ -0,0 +1,2 @@
+x¥K
+1D]ç}‡tf&qã ôm§ÃÆ@&"ÞÞ(ÞÀ]Õ{PÅ%çk ã¦UbýÈ‘R2Î8ö/'Ÿ\B” Æ“uLŠm)ŽñI5Ây)y-wØI§Ÿt¯øµKÞNè4ζzÖZuÚÏ›ü9£N7Z—×ðmÐA¤ \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/e2/377bdbc93b30a34ed5deefedded89b947ff8f4 b/tests/resources/nasty/.gitted/objects/e2/377bdbc93b30a34ed5deefedded89b947ff8f4
new file mode 100644
index 0000000..f365908
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/e2/377bdbc93b30a34ed5deefedded89b947ff8f4
@@ -0,0 +1,2 @@
+x¥K
+1D]ç}‡îÉDÜx½@&i‰` d"âíâ ÜU½U±–rí@^nzcãØ-‘•³³V£´&iËK4xA²hP{MA‹ðè¹68¦gh ι–µÞaǃ~Ò¿âצXËH‘ñÒÎ$a‹Q :Î;ÿ9#N·°æ×ôÑ@W \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/e3/0b60b120761f44ebd0f0a7b0e9445ce8e11d68 b/tests/resources/nasty/.gitted/objects/e3/0b60b120761f44ebd0f0a7b0e9445ce8e11d68
new file mode 100644
index 0000000..1c1be16
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/e3/0b60b120761f44ebd0f0a7b0e9445ce8e11d68
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/e3/99c4fc4c07cb7947d2f3d966bc374df6ccc691 b/tests/resources/nasty/.gitted/objects/e3/99c4fc4c07cb7947d2f3d966bc374df6ccc691
new file mode 100644
index 0000000..d8c2379
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/e3/99c4fc4c07cb7947d2f3d966bc374df6ccc691
@@ -0,0 +1,2 @@
+x¥Q
+1 Dýî)r—¤›Ú.,â'Ð ”l¤‚µÐ­ˆ··Š7ðoæ=˜‘’óµMã¦UUÀèƒ2ªcö$Èä$â. sx±lÕÕšøh©T8.ÏX8§’×r‡Y;ý¤ƒ~ů RòˆÉ#… ¶èM§ý¼éŸ3æt‹kz o{˜@8 \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/e4/edb361e51932b5ccedbc7ee41b4d3a4289aece b/tests/resources/nasty/.gitted/objects/e4/edb361e51932b5ccedbc7ee41b4d3a4289aece
new file mode 100644
index 0000000..a9b1818
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/e4/edb361e51932b5ccedbc7ee41b4d3a4289aece
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/e5/1c3fa44fe981ec290c8f47fea736f3ff2af2a6 b/tests/resources/nasty/.gitted/objects/e5/1c3fa44fe981ec290c8f47fea736f3ff2af2a6
new file mode 100644
index 0000000..3d12f3a
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/e5/1c3fa44fe981ec290c8f47fea736f3ff2af2a6
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/e7/3a04f71f11ab9d7dde72ff793882757a03f16e b/tests/resources/nasty/.gitted/objects/e7/3a04f71f11ab9d7dde72ff793882757a03f16e
new file mode 100644
index 0000000..14144d7
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/e7/3a04f71f11ab9d7dde72ff793882757a03f16e
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/e8/68b1d6833710021785581a9e11dba8468f3a55 b/tests/resources/nasty/.gitted/objects/e8/68b1d6833710021785581a9e11dba8468f3a55
new file mode 100644
index 0000000..8311ad3
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/e8/68b1d6833710021785581a9e11dba8468f3a55
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/e8/7caf56c91ab8d14e4ee8eb56308533503d1885 b/tests/resources/nasty/.gitted/objects/e8/7caf56c91ab8d14e4ee8eb56308533503d1885
new file mode 100644
index 0000000..6e61c06
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/e8/7caf56c91ab8d14e4ee8eb56308533503d1885
@@ -0,0 +1,2 @@
+x¥K
+1D]ç}‡nó™ ˆ¸ñz|:D02ñöFñîªÞƒªPK¹v +7½1IÖiñŒ–?G³J^Zo ã²#‚rÒ$îÑsmpŠO×"\r-k½Ãžý¤#ůM¡–"c¥A©a‹Q :Î;ÿ9#Î7·æ×ô(ŸAD \ No newline at end of file
diff --git a/tests/resources/nasty/.gitted/objects/eb/82bf596b66f90e25f881ce9b92cb55bab4fdf5 b/tests/resources/nasty/.gitted/objects/eb/82bf596b66f90e25f881ce9b92cb55bab4fdf5
new file mode 100644
index 0000000..b886096
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/eb/82bf596b66f90e25f881ce9b92cb55bab4fdf5
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/ed/4bc023f61dc345ff0084b922b229d24de206e7 b/tests/resources/nasty/.gitted/objects/ed/4bc023f61dc345ff0084b922b229d24de206e7
new file mode 100644
index 0000000..d128a94
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/ed/4bc023f61dc345ff0084b922b229d24de206e7
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/ef/6ed8a2b15f95795aed82a974b995cace02dbfe b/tests/resources/nasty/.gitted/objects/ef/6ed8a2b15f95795aed82a974b995cace02dbfe
new file mode 100644
index 0000000..7357306
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/ef/6ed8a2b15f95795aed82a974b995cace02dbfe
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/f2/c059dab35f6534b3f16d90b2f1de308615320c b/tests/resources/nasty/.gitted/objects/f2/c059dab35f6534b3f16d90b2f1de308615320c
new file mode 100644
index 0000000..c3580d3
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/f2/c059dab35f6534b3f16d90b2f1de308615320c
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/fa/9cfdbeaaf3a91ff4b84d74412cd59d9b16a615 b/tests/resources/nasty/.gitted/objects/fa/9cfdbeaaf3a91ff4b84d74412cd59d9b16a615
new file mode 100644
index 0000000..890324e
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/fa/9cfdbeaaf3a91ff4b84d74412cd59d9b16a615
Binary files differ
diff --git a/tests/resources/nasty/.gitted/objects/fd/7a37d92197267e55e1fc0cc4f283a815bd79b8 b/tests/resources/nasty/.gitted/objects/fd/7a37d92197267e55e1fc0cc4f283a815bd79b8
new file mode 100644
index 0000000..c8d38ca
--- /dev/null
+++ b/tests/resources/nasty/.gitted/objects/fd/7a37d92197267e55e1fc0cc4f283a815bd79b8
Binary files differ
diff --git a/tests/resources/nasty/.gitted/refs/heads/dot_backslash_dotcapitalgit_path b/tests/resources/nasty/.gitted/refs/heads/dot_backslash_dotcapitalgit_path
new file mode 100644
index 0000000..06132bc
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dot_backslash_dotcapitalgit_path
@@ -0,0 +1 @@
+0228b21d477f67b9f7720565da9e760b84c8b85b
diff --git a/tests/resources/nasty/.gitted/refs/heads/dot_dotcapitalgit_path b/tests/resources/nasty/.gitted/refs/heads/dot_dotcapitalgit_path
new file mode 100644
index 0000000..fd12c3e
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dot_dotcapitalgit_path
@@ -0,0 +1 @@
+e2377bdbc93b30a34ed5deefedded89b947ff8f4
diff --git a/tests/resources/nasty/.gitted/refs/heads/dot_dotgit_path b/tests/resources/nasty/.gitted/refs/heads/dot_dotgit_path
new file mode 100644
index 0000000..1f9b2d4
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dot_dotgit_path
@@ -0,0 +1 @@
+4aa347c8bb0456230f43f34833c97b9f52c40f62
diff --git a/tests/resources/nasty/.gitted/refs/heads/dot_dotgit_tree b/tests/resources/nasty/.gitted/refs/heads/dot_dotgit_tree
new file mode 100644
index 0000000..dd9a6c0
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dot_dotgit_tree
@@ -0,0 +1 @@
+8bcbb6e0c0f9554efd5401e1ec14a4b2595eb3bf
diff --git a/tests/resources/nasty/.gitted/refs/heads/dot_git_colon b/tests/resources/nasty/.gitted/refs/heads/dot_git_colon
new file mode 100644
index 0000000..39052d9
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dot_git_colon
@@ -0,0 +1 @@
+4414ac920acabc3eb00e3cf9375eeb0cb6859c15
diff --git a/tests/resources/nasty/.gitted/refs/heads/dot_git_colon_stuff b/tests/resources/nasty/.gitted/refs/heads/dot_git_colon_stuff
new file mode 100644
index 0000000..a3bc39f
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dot_git_colon_stuff
@@ -0,0 +1 @@
+ccbbfdb796f9b03298f5c7225e8f830784e1a3b1
diff --git a/tests/resources/nasty/.gitted/refs/heads/dot_git_dot b/tests/resources/nasty/.gitted/refs/heads/dot_git_dot
new file mode 100644
index 0000000..b20a1e0
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dot_git_dot
@@ -0,0 +1 @@
+26b665c162f67acae67779445f3c7b9782b0a6d7
diff --git a/tests/resources/nasty/.gitted/refs/heads/dot_path b/tests/resources/nasty/.gitted/refs/heads/dot_path
new file mode 100644
index 0000000..b3c7ab6
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dot_path
@@ -0,0 +1 @@
+bf7ab4723fcc57ecc7fceccf591d6c4773491569
diff --git a/tests/resources/nasty/.gitted/refs/heads/dot_path_two b/tests/resources/nasty/.gitted/refs/heads/dot_path_two
new file mode 100644
index 0000000..515e983
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dot_path_two
@@ -0,0 +1 @@
+debdc4a004fda6141a17d9c297617be70d40248f
diff --git a/tests/resources/nasty/.gitted/refs/heads/dot_tree b/tests/resources/nasty/.gitted/refs/heads/dot_tree
new file mode 100644
index 0000000..cf95837
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dot_tree
@@ -0,0 +1 @@
+697dc3d723a018538eb819d5db2035c15109af73
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_backslash_path b/tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_backslash_path
new file mode 100644
index 0000000..6e4344d
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_backslash_path
@@ -0,0 +1 @@
+099ed86cb8501ae483b1855c351fe1a506ac9631
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_path b/tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_path
new file mode 100644
index 0000000..5822791
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_path
@@ -0,0 +1 @@
+e87caf56c91ab8d14e4ee8eb56308533503d1885
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_tree b/tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_tree
new file mode 100644
index 0000000..dfb7a1a
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotcapitalgit_tree
@@ -0,0 +1 @@
+39fb3af508440cf970b92767f6d081c811574d2a
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotdot_dotcapitalgit_path b/tests/resources/nasty/.gitted/refs/heads/dotdot_dotcapitalgit_path
new file mode 100644
index 0000000..6a24cd7
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotdot_dotcapitalgit_path
@@ -0,0 +1 @@
+d2eb26d4938550487de59a017a7bfee8ca46b5f4
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotdot_dotgit_path b/tests/resources/nasty/.gitted/refs/heads/dotdot_dotgit_path
new file mode 100644
index 0000000..4d79b3b
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotdot_dotgit_path
@@ -0,0 +1 @@
+1212c12915820e1ad523b6305c0dcdefea8b7e97
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotdot_dotgit_tree b/tests/resources/nasty/.gitted/refs/heads/dotdot_dotgit_tree
new file mode 100644
index 0000000..6ae117e
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotdot_dotgit_tree
@@ -0,0 +1 @@
+1e3c845808fa5883aa4bcf2f882172edb72a7a32
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotdot_path b/tests/resources/nasty/.gitted/refs/heads/dotdot_path
new file mode 100644
index 0000000..185e13b
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotdot_path
@@ -0,0 +1 @@
+91602c85bb50dd834205edd30435b77d5bb9ccf0
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotdot_tree b/tests/resources/nasty/.gitted/refs/heads/dotdot_tree
new file mode 100644
index 0000000..d30a7b5
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotdot_tree
@@ -0,0 +1 @@
+8f1dcd43aa0164eb6ec319c3ec8879ca5cf62c1e
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_alternate_data_stream b/tests/resources/nasty/.gitted/refs/heads/dotgit_alternate_data_stream
new file mode 100644
index 0000000..ecdd340
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_alternate_data_stream
@@ -0,0 +1 @@
+b8edf3ad62dbcbc983857a5bfee7b0181ee1a513
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_backslash_path b/tests/resources/nasty/.gitted/refs/heads/dotgit_backslash_path
new file mode 100644
index 0000000..6e4344d
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_backslash_path
@@ -0,0 +1 @@
+099ed86cb8501ae483b1855c351fe1a506ac9631
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_1 b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_1
new file mode 100644
index 0000000..dc48bd6
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_1
@@ -0,0 +1 @@
+46fe10fa23259b089ab050788b06df979cd7d054
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_10 b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_10
new file mode 100644
index 0000000..b3a9726
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_10
@@ -0,0 +1 @@
+9ab85e507899c19dca57778c9b6e5f1ec799b911
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_11 b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_11
new file mode 100644
index 0000000..edf2798
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_11
@@ -0,0 +1 @@
+15f7d9f9514eeb65b9588c49b10b1da145a729a2
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_12 b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_12
new file mode 100644
index 0000000..c4e682e
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_12
@@ -0,0 +1 @@
+c3a70f8a376f17adccfb52b48e2831bfef2a2172
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_13 b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_13
new file mode 100644
index 0000000..76a155c
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_13
@@ -0,0 +1 @@
+c2a2ddd339574e5cbfd9228be840eb1bf496de4e
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_14 b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_14
new file mode 100644
index 0000000..be2f835
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_14
@@ -0,0 +1 @@
+712ceb8eb3e57072447715bc4057c57aa50f629a
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_15 b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_15
new file mode 100644
index 0000000..3fdeece
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_15
@@ -0,0 +1 @@
+3b24e5c751ee9c7c89df32a0d959748aa3d0112c
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_16 b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_16
new file mode 100644
index 0000000..2739555
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_16
@@ -0,0 +1 @@
+c8f98a1762ec016c30f0d73512df399dedefc3fd
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_2 b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_2
new file mode 100644
index 0000000..480832e
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_2
@@ -0,0 +1 @@
+35ae236308929a536fb4e852278a9b98c42babb3
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_3 b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_3
new file mode 100644
index 0000000..8510ece
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_3
@@ -0,0 +1 @@
+96156716851c0afb4702b0d2c4ac8c496a730e29
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_4 b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_4
new file mode 100644
index 0000000..754b55e
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_4
@@ -0,0 +1 @@
+7a0538bc4e20aecb36ef221f2077eb30ebe0bcb2
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_5 b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_5
new file mode 100644
index 0000000..161ebc4
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_5
@@ -0,0 +1 @@
+1635c47d80914f0abfa43dd4234a948db5bdb107
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_6 b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_6
new file mode 100644
index 0000000..f8a5fa3
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_6
@@ -0,0 +1 @@
+9e24726d64589ba02430da8cebb5712dad35593d
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_7 b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_7
new file mode 100644
index 0000000..ad5ad1d
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_7
@@ -0,0 +1 @@
+ce22b3cd9a01efafc370879c1938e0c32fb6f195
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_8 b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_8
new file mode 100644
index 0000000..4d10c40
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_8
@@ -0,0 +1 @@
+a576a98d3279989226992610372035b76a01a3e9
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_9 b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_9
new file mode 100644
index 0000000..a935018
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_hfs_ignorable_9
@@ -0,0 +1 @@
+442894787eddb1e84a952f17a027590e2c6c02cd
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_path b/tests/resources/nasty/.gitted/refs/heads/dotgit_path
new file mode 100644
index 0000000..dd71efa
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_path
@@ -0,0 +1 @@
+5341a7b545d71198b076b8ba3374a75c9a290640
diff --git a/tests/resources/nasty/.gitted/refs/heads/dotgit_tree b/tests/resources/nasty/.gitted/refs/heads/dotgit_tree
new file mode 100644
index 0000000..3b7a08d
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/dotgit_tree
@@ -0,0 +1 @@
+6594bdbad86bbc8d3ed0806a23827203fbab56c6
diff --git a/tests/resources/nasty/.gitted/refs/heads/git_tilde1 b/tests/resources/nasty/.gitted/refs/heads/git_tilde1
new file mode 100644
index 0000000..d48a185
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/git_tilde1
@@ -0,0 +1 @@
+94f37c29173c8fa45a232b17e745c82132b2fafd
diff --git a/tests/resources/nasty/.gitted/refs/heads/git_tilde2 b/tests/resources/nasty/.gitted/refs/heads/git_tilde2
new file mode 100644
index 0000000..77082e1
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/git_tilde2
@@ -0,0 +1 @@
+899ff28744bed5bece69c78ba752c7dc3e954629
diff --git a/tests/resources/nasty/.gitted/refs/heads/git_tilde3 b/tests/resources/nasty/.gitted/refs/heads/git_tilde3
new file mode 100644
index 0000000..73022aa
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/git_tilde3
@@ -0,0 +1 @@
+fa9cfdbeaaf3a91ff4b84d74412cd59d9b16a615
diff --git a/tests/resources/nasty/.gitted/refs/heads/gitmodules-symlink b/tests/resources/nasty/.gitted/refs/heads/gitmodules-symlink
new file mode 100644
index 0000000..3a2499f
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/gitmodules-symlink
@@ -0,0 +1 @@
+e30b60b120761f44ebd0f0a7b0e9445ce8e11d68
diff --git a/tests/resources/nasty/.gitted/refs/heads/master b/tests/resources/nasty/.gitted/refs/heads/master
new file mode 100644
index 0000000..b193433
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/master
@@ -0,0 +1 @@
+e399c4fc4c07cb7947d2f3d966bc374df6ccc691
diff --git a/tests/resources/nasty/.gitted/refs/heads/symlink1 b/tests/resources/nasty/.gitted/refs/heads/symlink1
new file mode 100644
index 0000000..efa2e88
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/symlink1
@@ -0,0 +1 @@
+4d83272d0d372e1232ddc4ff3260d76fdfa2015a
diff --git a/tests/resources/nasty/.gitted/refs/heads/symlink2 b/tests/resources/nasty/.gitted/refs/heads/symlink2
new file mode 100644
index 0000000..e4f3d60
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/symlink2
@@ -0,0 +1 @@
+9d5898503adc01d763e279ac8fcefbe865b19031
diff --git a/tests/resources/nasty/.gitted/refs/heads/symlink3 b/tests/resources/nasty/.gitted/refs/heads/symlink3
new file mode 100644
index 0000000..2b33e4f
--- /dev/null
+++ b/tests/resources/nasty/.gitted/refs/heads/symlink3
@@ -0,0 +1 @@
+cf6fcf8cdf7e8d4cda3b11b0ba02d0d5125fbbd7
diff --git a/tests/resources/nsecs/.gitted/HEAD b/tests/resources/nsecs/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/nsecs/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/nsecs/.gitted/config b/tests/resources/nsecs/.gitted/config
new file mode 100644
index 0000000..78387c5
--- /dev/null
+++ b/tests/resources/nsecs/.gitted/config
@@ -0,0 +1,8 @@
+[core]
+ repositoryformatversion = 0
+ filemode = false
+ bare = false
+ logallrefupdates = true
+ symlinks = false
+ ignorecase = true
+ hideDotFiles = dotGitOnly
diff --git a/tests/resources/nsecs/.gitted/index b/tests/resources/nsecs/.gitted/index
new file mode 100644
index 0000000..9233f1b
--- /dev/null
+++ b/tests/resources/nsecs/.gitted/index
Binary files differ
diff --git a/tests/resources/nsecs/.gitted/objects/03/1986a8372d1442cfe9e3b54906a9aadc524a7e b/tests/resources/nsecs/.gitted/objects/03/1986a8372d1442cfe9e3b54906a9aadc524a7e
new file mode 100644
index 0000000..a813b74
--- /dev/null
+++ b/tests/resources/nsecs/.gitted/objects/03/1986a8372d1442cfe9e3b54906a9aadc524a7e
@@ -0,0 +1,2 @@
+x¥A
+Â0D]çÿJÓ4DéÖMu-éÏ/í"FÒHñöFñ®fæ ÌP aÊ ”ZåÄ r‹nðXÚÁ*ª4kU÷½iÐxK-#%ážyŒ Z¿¸äá2Æ0Ç;ì¸Ð;ð·ø¥ Å°‡JëZ7F¢µÔRŠBËyæ?gÄ?â<å˜^@‰]fË”G¸vííܵ§N¼UOKv \ No newline at end of file
diff --git a/tests/resources/nsecs/.gitted/objects/03/9afd91c98f82c14e425bb6796d8ca98e9c8cac b/tests/resources/nsecs/.gitted/objects/03/9afd91c98f82c14e425bb6796d8ca98e9c8cac
new file mode 100644
index 0000000..74bb7d3
--- /dev/null
+++ b/tests/resources/nsecs/.gitted/objects/03/9afd91c98f82c14e425bb6796d8ca98e9c8cac
Binary files differ
diff --git a/tests/resources/nsecs/.gitted/objects/6d/8b18077cc99abd8dda05a6062c646406abb2d4 b/tests/resources/nsecs/.gitted/objects/6d/8b18077cc99abd8dda05a6062c646406abb2d4
new file mode 100644
index 0000000..7bf3a95
--- /dev/null
+++ b/tests/resources/nsecs/.gitted/objects/6d/8b18077cc99abd8dda05a6062c646406abb2d4
Binary files differ
diff --git a/tests/resources/nsecs/.gitted/objects/c5/12b6c64656b87ea8caf37a32bc5a562d797745 b/tests/resources/nsecs/.gitted/objects/c5/12b6c64656b87ea8caf37a32bc5a562d797745
new file mode 100644
index 0000000..dcf4c8c
--- /dev/null
+++ b/tests/resources/nsecs/.gitted/objects/c5/12b6c64656b87ea8caf37a32bc5a562d797745
Binary files differ
diff --git a/tests/resources/nsecs/.gitted/objects/df/78d3d51c369e1d2f1eadb73464aadd931d56b4 b/tests/resources/nsecs/.gitted/objects/df/78d3d51c369e1d2f1eadb73464aadd931d56b4
new file mode 100644
index 0000000..df45d33
--- /dev/null
+++ b/tests/resources/nsecs/.gitted/objects/df/78d3d51c369e1d2f1eadb73464aadd931d56b4
Binary files differ
diff --git a/tests/resources/nsecs/.gitted/refs/heads/master b/tests/resources/nsecs/.gitted/refs/heads/master
new file mode 100644
index 0000000..3dda65b
--- /dev/null
+++ b/tests/resources/nsecs/.gitted/refs/heads/master
@@ -0,0 +1 @@
+031986a8372d1442cfe9e3b54906a9aadc524a7e
diff --git a/tests/resources/nsecs/a.txt b/tests/resources/nsecs/a.txt
new file mode 100644
index 0000000..be4c1ee
--- /dev/null
+++ b/tests/resources/nsecs/a.txt
@@ -0,0 +1 @@
+File A
diff --git a/tests/resources/nsecs/b.txt b/tests/resources/nsecs/b.txt
new file mode 100644
index 0000000..19a0af4
--- /dev/null
+++ b/tests/resources/nsecs/b.txt
@@ -0,0 +1 @@
+File B
diff --git a/tests/resources/nsecs/c.txt b/tests/resources/nsecs/c.txt
new file mode 100644
index 0000000..31d7760
--- /dev/null
+++ b/tests/resources/nsecs/c.txt
@@ -0,0 +1 @@
+File C
diff --git a/tests/resources/packfile-sha256/pack-b4a043c0ec5e079e8ac67d823776d752efc71661592db317474a0cf292915f31.idx b/tests/resources/packfile-sha256/pack-b4a043c0ec5e079e8ac67d823776d752efc71661592db317474a0cf292915f31.idx
new file mode 100644
index 0000000..7aa472c
--- /dev/null
+++ b/tests/resources/packfile-sha256/pack-b4a043c0ec5e079e8ac67d823776d752efc71661592db317474a0cf292915f31.idx
Binary files differ
diff --git a/tests/resources/packfile-sha256/pack-b4a043c0ec5e079e8ac67d823776d752efc71661592db317474a0cf292915f31.pack b/tests/resources/packfile-sha256/pack-b4a043c0ec5e079e8ac67d823776d752efc71661592db317474a0cf292915f31.pack
new file mode 100644
index 0000000..adbe70a
--- /dev/null
+++ b/tests/resources/packfile-sha256/pack-b4a043c0ec5e079e8ac67d823776d752efc71661592db317474a0cf292915f31.pack
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/HEAD b/tests/resources/partial-testrepo/.gitted/HEAD
new file mode 100644
index 0000000..4bfb9c9
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/dir
diff --git a/tests/resources/partial-testrepo/.gitted/config b/tests/resources/partial-testrepo/.gitted/config
new file mode 100644
index 0000000..99abaab
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/config
@@ -0,0 +1,7 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
+[branch "dir"]
diff --git a/tests/resources/partial-testrepo/.gitted/index b/tests/resources/partial-testrepo/.gitted/index
new file mode 100644
index 0000000..4f241f9
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/index
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 b/tests/resources/partial-testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
new file mode 100644
index 0000000..cedb2a2
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0e b/tests/resources/partial-testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0e
new file mode 100644
index 0000000..b7d944f
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0e
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925 b/tests/resources/partial-testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925
new file mode 100644
index 0000000..d37b93e
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 b/tests/resources/partial-testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
new file mode 100644
index 0000000..93a16f1
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd b/tests/resources/partial-testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
new file mode 100644
index 0000000..ba0bfb3
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests/resources/partial-testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
new file mode 100644
index 0000000..7ca4cee
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 b/tests/resources/partial-testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
new file mode 100644
index 0000000..8953b6c
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
@@ -0,0 +1,2 @@
+xŽQ
+Â0DýÎ)öÊ6›Í¦ "xO°‰‰-ØFb¼¿EoàÏ0 ¼Ç¤º,ske×[ÎPn8R,EpD?±gŸ}Ê^3² âÙ<µåµGŽhYKÄèÒ8ЖDAÉ)¿ÉÈ;gôݧÚàšjïp™4ÕŽ¯ô-çû¢óãêr‚ÁŠ;°s°GA4Ûº=ìùÖ(ôin7øIÌKÍFE \ No newline at end of file
diff --git a/tests/resources/partial-testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63 b/tests/resources/partial-testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63
new file mode 100644
index 0000000..e915021
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 b/tests/resources/partial-testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
new file mode 100644
index 0000000..c1f22c5
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
@@ -0,0 +1,2 @@
+xŽÛ 1EýNi@™Ék2 "X‚$ÙYW0YcÿíÀ¿Ã…s¸¥ÕzïÚÚõMDÏ€0æœ8!¶†ÉÌÞs‰ XŠªgÚdí::@X0»P¢wÙ"F/‰‰œÍRàˆUz÷¥múZZïú²¤ÒV}|•/œo5݇ÒêI£!¬1z Æ:vùÇUim}ê/¢>
+öF- \ No newline at end of file
diff --git a/tests/resources/partial-testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dcc b/tests/resources/partial-testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dcc
new file mode 100644
index 0000000..b669961
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dcc
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735 b/tests/resources/partial-testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735
new file mode 100644
index 0000000..9ff5eb2
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a b/tests/resources/partial-testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
new file mode 100644
index 0000000..2ef4faa
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d b/tests/resources/partial-testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
new file mode 100644
index 0000000..2f9b6b6
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479 b/tests/resources/partial-testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
new file mode 100644
index 0000000..5df58dd
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a b/tests/resources/partial-testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
new file mode 100644
index 0000000..a796124
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
@@ -0,0 +1,3 @@
+xŽ[
+Â0EýÎ*fÊäÕ¤ "¸W0“‡-ØFâtÿÝ—çpS[–YÀ˜x^
+Díb CLhutɉ}¥8X*4Zí¬sY½¨—UÀ‘AÃÖ ÌX3‡R«Mµ¶) s6è¼¢M¦ÖážšÜ&Jm…ó;}Çõ±Ðü<¥¶\@›à‚ÑÞpÄ€¨vº?”ò«jÛºLð«¨Ø?Hå \ No newline at end of file
diff --git a/tests/resources/partial-testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f b/tests/resources/partial-testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
new file mode 100644
index 0000000..f858869
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
@@ -0,0 +1,2 @@
+x;j1DëmdÓú·À˜ÇŽ|M«µ3`ŒV{ >€³âQ¯ ¸·vL0I?Í!š4–Z=Ê! ×¦8²F¢Ã’!rÖsQßyÈ9]$DŽ&„l6AÇ>jFWüÒµ IKNiûë§Z¢%¡SˆŒ‘
+‹Ò ­ÅʉøU~̽øä>'¼ï™û ¯wþ ×[ËÇ× ÷öÚDGÚ¡±ðŒQ-ºMù«>dܶ‘OÞáÒò}í\à8g_ШÂoYr \ No newline at end of file
diff --git a/tests/resources/partial-testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd b/tests/resources/partial-testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
new file mode 100644
index 0000000..d0d7e73
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 b/tests/resources/partial-testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
new file mode 100644
index 0000000..18a7f61
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd b/tests/resources/partial-testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
new file mode 100644
index 0000000..75f541f
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
@@ -0,0 +1,3 @@
+xŽQ
+Â0DýÎ)öʦ»I<‚'ØlR+˜Fj¼¿EoàÏ0<xÃh«õÞa Üõµ]È™­åXUlÞPF)Åz‘4yó”µ,\r 'SÂÄ-mI4
+‘Xhô”&òÌFÞ}n+\µõ—Y´-p|é·œoUz;-‘aÑlt{ØË?®I«,:ÃoÚR̳cHK \ No newline at end of file
diff --git a/tests/resources/partial-testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9 b/tests/resources/partial-testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9
new file mode 100644
index 0000000..7620c51
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83 b/tests/resources/partial-testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83
new file mode 100644
index 0000000..00940f0
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 b/tests/resources/partial-testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
new file mode 100644
index 0000000..0377096
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92 b/tests/resources/partial-testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
new file mode 100644
index 0000000..112998d
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765 b/tests/resources/partial-testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
new file mode 100644
index 0000000..12bf5f3
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
Binary files differ
diff --git a/tests/resources/partial-testrepo/.gitted/objects/pack/.gitkeep b/tests/resources/partial-testrepo/.gitted/objects/pack/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/objects/pack/.gitkeep
diff --git a/tests/resources/partial-testrepo/.gitted/refs/heads/dir b/tests/resources/partial-testrepo/.gitted/refs/heads/dir
new file mode 100644
index 0000000..4567d37
--- /dev/null
+++ b/tests/resources/partial-testrepo/.gitted/refs/heads/dir
@@ -0,0 +1 @@
+144344043ba4d4a405da03de3844aa829ae8be0e
diff --git a/tests/resources/peeled.git/HEAD b/tests/resources/peeled.git/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/peeled.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/peeled.git/config b/tests/resources/peeled.git/config
new file mode 100644
index 0000000..8830052
--- /dev/null
+++ b/tests/resources/peeled.git/config
@@ -0,0 +1,8 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = true
+[remote "origin"]
+ url = /home/peff/compile/libgit2/tests-clar/resources/peeled
+ fetch = +refs/*:refs/*
+ mirror = true
diff --git a/tests/resources/peeled.git/objects/info/packs b/tests/resources/peeled.git/objects/info/packs
new file mode 100644
index 0000000..0d88b32
--- /dev/null
+++ b/tests/resources/peeled.git/objects/info/packs
@@ -0,0 +1,2 @@
+P pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.pack
+
diff --git a/tests/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.idx b/tests/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.idx
new file mode 100644
index 0000000..9b79e9b
--- /dev/null
+++ b/tests/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.idx
Binary files differ
diff --git a/tests/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.pack b/tests/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.pack
new file mode 100644
index 0000000..2459ca2
--- /dev/null
+++ b/tests/resources/peeled.git/objects/pack/pack-e84773eaf3fce1774755580e3dbb8d9f3a1adc45.pack
Binary files differ
diff --git a/tests/resources/peeled.git/packed-refs b/tests/resources/peeled.git/packed-refs
new file mode 100644
index 0000000..078f237
--- /dev/null
+++ b/tests/resources/peeled.git/packed-refs
@@ -0,0 +1,6 @@
+# pack-refs with: peeled fully-peeled sorted
+c2596aa0151888587ec5c0187f261e63412d9e11 refs/foo/tag-outside-tags
+^0df1a5865c8abfc09f1f2182e6a31be550e99f07
+0df1a5865c8abfc09f1f2182e6a31be550e99f07 refs/heads/master
+c2596aa0151888587ec5c0187f261e63412d9e11 refs/tags/tag-inside-tags
+^0df1a5865c8abfc09f1f2182e6a31be550e99f07
diff --git a/tests/resources/peeled.git/refs/heads/master b/tests/resources/peeled.git/refs/heads/master
new file mode 100644
index 0000000..76c15e2
--- /dev/null
+++ b/tests/resources/peeled.git/refs/heads/master
@@ -0,0 +1 @@
+0df1a5865c8abfc09f1f2182e6a31be550e99f07
diff --git a/tests/resources/push.sh b/tests/resources/push.sh
new file mode 100644
index 0000000..3e77fb5
--- /dev/null
+++ b/tests/resources/push.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+#creates push_src repo for libgit2 push tests.
+set -eu
+
+#Create src repo for push
+mkdir push_src
+pushd push_src
+ git init
+
+ echo a > a.txt
+ git add .
+ git commit -m 'added a.txt'
+
+ mkdir fold
+ echo b > fold/b.txt
+ git add .
+ git commit -m 'added fold and fold/b.txt'
+
+ git branch b1 #b1 and b2 are the same
+ git branch b2
+
+ git checkout -b b3
+ echo edit >> a.txt
+ git add .
+ git commit -m 'edited a.txt'
+
+ git checkout -b b4 master
+ echo edit >> fold\b.txt
+ git add .
+ git commit -m 'edited fold\b.txt'
+
+ git checkout -b b5 master
+ git submodule add ../testrepo.git submodule
+ git commit -m "added submodule named 'submodule' pointing to '../testrepo.git'"
+
+ git checkout master
+ git merge -m "merge b3, b4, and b5 to master" b3 b4 b5
+
+ #Log commits to include in testcase
+ git log --format=oneline --decorate --graph
+ #*-. 951bbbb90e2259a4c8950db78946784fb53fcbce (HEAD, master) merge b3, b4, and b5 to master
+ #|\ \
+ #| | * fa38b91f199934685819bea316186d8b008c52a2 (b5) added submodule named 'submodule' pointing to '../testrepo.git'
+ #| * | 27b7ce66243eb1403862d05f958c002312df173d (b4) edited fold\b.txt
+ #| |/
+ #* | d9b63a88223d8367516f50bd131a5f7349b7f3e4 (b3) edited a.txt
+ #|/
+ #* a78705c3b2725f931d3ee05348d83cc26700f247 (b2, b1) added fold and fold/b.txt
+ #* 5c0bb3d1b9449d1cc69d7519fd05166f01840915 added a.txt
+
+ #fix paths so that we can add repo folders under libgit2 repo
+ #rename .git to .gitted
+ find . -name .git -exec mv -i '{}' '{}ted' \;
+ mv -i .gitmodules gitmodules
+popd
diff --git a/tests/resources/push_src/.gitted/COMMIT_EDITMSG b/tests/resources/push_src/.gitted/COMMIT_EDITMSG
new file mode 100644
index 0000000..b129508
--- /dev/null
+++ b/tests/resources/push_src/.gitted/COMMIT_EDITMSG
@@ -0,0 +1 @@
+added submodule named 'submodule' pointing to '../testrepo.git'
diff --git a/tests/resources/push_src/.gitted/HEAD b/tests/resources/push_src/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/push_src/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/push_src/.gitted/ORIG_HEAD b/tests/resources/push_src/.gitted/ORIG_HEAD
new file mode 100644
index 0000000..afadf9d
--- /dev/null
+++ b/tests/resources/push_src/.gitted/ORIG_HEAD
@@ -0,0 +1 @@
+a78705c3b2725f931d3ee05348d83cc26700f247
diff --git a/tests/resources/push_src/.gitted/config b/tests/resources/push_src/.gitted/config
new file mode 100644
index 0000000..51de031
--- /dev/null
+++ b/tests/resources/push_src/.gitted/config
@@ -0,0 +1,10 @@
+[core]
+ repositoryformatversion = 0
+ filemode = false
+ bare = false
+ logallrefupdates = true
+ symlinks = false
+ ignorecase = true
+ hideDotFiles = dotGitOnly
+[submodule "submodule"]
+ url = m:/dd/libgit2/tests-clar/resources/testrepo.git
diff --git a/tests/resources/push_src/.gitted/description b/tests/resources/push_src/.gitted/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/push_src/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/push_src/.gitted/index b/tests/resources/push_src/.gitted/index
new file mode 100644
index 0000000..0ef6594
--- /dev/null
+++ b/tests/resources/push_src/.gitted/index
Binary files differ
diff --git a/tests/resources/push_src/.gitted/info/exclude b/tests/resources/push_src/.gitted/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/push_src/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/push_src/.gitted/logs/HEAD b/tests/resources/push_src/.gitted/logs/HEAD
new file mode 100644
index 0000000..4ef336f
--- /dev/null
+++ b/tests/resources/push_src/.gitted/logs/HEAD
@@ -0,0 +1,10 @@
+0000000000000000000000000000000000000000 5c0bb3d1b9449d1cc69d7519fd05166f01840915 Congyi Wu <congyiwu@gmail.com> 1352923200 -0500 commit (initial): added a.txt
+5c0bb3d1b9449d1cc69d7519fd05166f01840915 a78705c3b2725f931d3ee05348d83cc26700f247 Congyi Wu <congyiwu@gmail.com> 1352923200 -0500 commit: added fold and fold/b.txt
+a78705c3b2725f931d3ee05348d83cc26700f247 a78705c3b2725f931d3ee05348d83cc26700f247 Congyi Wu <congyiwu@gmail.com> 1352923201 -0500 checkout: moving from master to b3
+a78705c3b2725f931d3ee05348d83cc26700f247 d9b63a88223d8367516f50bd131a5f7349b7f3e4 Congyi Wu <congyiwu@gmail.com> 1352923201 -0500 commit: edited a.txt
+d9b63a88223d8367516f50bd131a5f7349b7f3e4 a78705c3b2725f931d3ee05348d83cc26700f247 Congyi Wu <congyiwu@gmail.com> 1352923201 -0500 checkout: moving from b3 to b4
+a78705c3b2725f931d3ee05348d83cc26700f247 27b7ce66243eb1403862d05f958c002312df173d Congyi Wu <congyiwu@gmail.com> 1352923201 -0500 commit: edited fold\b.txt
+27b7ce66243eb1403862d05f958c002312df173d a78705c3b2725f931d3ee05348d83cc26700f247 Congyi Wu <congyiwu@gmail.com> 1352923201 -0500 checkout: moving from b4 to b5
+a78705c3b2725f931d3ee05348d83cc26700f247 fa38b91f199934685819bea316186d8b008c52a2 Congyi Wu <congyiwu@gmail.com> 1352923206 -0500 commit: added submodule named 'submodule' pointing to '../testrepo.git'
+fa38b91f199934685819bea316186d8b008c52a2 a78705c3b2725f931d3ee05348d83cc26700f247 Congyi Wu <congyiwu@gmail.com> 1352923207 -0500 checkout: moving from b5 to master
+a78705c3b2725f931d3ee05348d83cc26700f247 951bbbb90e2259a4c8950db78946784fb53fcbce Congyi Wu <congyiwu@gmail.com> 1352923207 -0500 merge b3 b4 b5: Merge made by the 'octopus' strategy.
diff --git a/tests/resources/push_src/.gitted/logs/refs/heads/b1 b/tests/resources/push_src/.gitted/logs/refs/heads/b1
new file mode 100644
index 0000000..390a03d
--- /dev/null
+++ b/tests/resources/push_src/.gitted/logs/refs/heads/b1
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 a78705c3b2725f931d3ee05348d83cc26700f247 Congyi Wu <congyiwu@gmail.com> 1352923200 -0500 branch: Created from master
diff --git a/tests/resources/push_src/.gitted/logs/refs/heads/b2 b/tests/resources/push_src/.gitted/logs/refs/heads/b2
new file mode 100644
index 0000000..390a03d
--- /dev/null
+++ b/tests/resources/push_src/.gitted/logs/refs/heads/b2
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 a78705c3b2725f931d3ee05348d83cc26700f247 Congyi Wu <congyiwu@gmail.com> 1352923200 -0500 branch: Created from master
diff --git a/tests/resources/push_src/.gitted/logs/refs/heads/b3 b/tests/resources/push_src/.gitted/logs/refs/heads/b3
new file mode 100644
index 0000000..01e302c
--- /dev/null
+++ b/tests/resources/push_src/.gitted/logs/refs/heads/b3
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 a78705c3b2725f931d3ee05348d83cc26700f247 Congyi Wu <congyiwu@gmail.com> 1352923201 -0500 branch: Created from HEAD
+a78705c3b2725f931d3ee05348d83cc26700f247 d9b63a88223d8367516f50bd131a5f7349b7f3e4 Congyi Wu <congyiwu@gmail.com> 1352923201 -0500 commit: edited a.txt
diff --git a/tests/resources/push_src/.gitted/logs/refs/heads/b4 b/tests/resources/push_src/.gitted/logs/refs/heads/b4
new file mode 100644
index 0000000..7afddc5
--- /dev/null
+++ b/tests/resources/push_src/.gitted/logs/refs/heads/b4
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 a78705c3b2725f931d3ee05348d83cc26700f247 Congyi Wu <congyiwu@gmail.com> 1352923201 -0500 branch: Created from master
+a78705c3b2725f931d3ee05348d83cc26700f247 27b7ce66243eb1403862d05f958c002312df173d Congyi Wu <congyiwu@gmail.com> 1352923201 -0500 commit: edited fold\b.txt
diff --git a/tests/resources/push_src/.gitted/logs/refs/heads/b5 b/tests/resources/push_src/.gitted/logs/refs/heads/b5
new file mode 100644
index 0000000..bc22567
--- /dev/null
+++ b/tests/resources/push_src/.gitted/logs/refs/heads/b5
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 a78705c3b2725f931d3ee05348d83cc26700f247 Congyi Wu <congyiwu@gmail.com> 1352923201 -0500 branch: Created from master
+a78705c3b2725f931d3ee05348d83cc26700f247 fa38b91f199934685819bea316186d8b008c52a2 Congyi Wu <congyiwu@gmail.com> 1352923206 -0500 commit: added submodule named 'submodule' pointing to '../testrepo.git'
diff --git a/tests/resources/push_src/.gitted/logs/refs/heads/master b/tests/resources/push_src/.gitted/logs/refs/heads/master
new file mode 100644
index 0000000..8aafa9c
--- /dev/null
+++ b/tests/resources/push_src/.gitted/logs/refs/heads/master
@@ -0,0 +1,3 @@
+0000000000000000000000000000000000000000 5c0bb3d1b9449d1cc69d7519fd05166f01840915 Congyi Wu <congyiwu@gmail.com> 1352923200 -0500 commit (initial): added a.txt
+5c0bb3d1b9449d1cc69d7519fd05166f01840915 a78705c3b2725f931d3ee05348d83cc26700f247 Congyi Wu <congyiwu@gmail.com> 1352923200 -0500 commit: added fold and fold/b.txt
+a78705c3b2725f931d3ee05348d83cc26700f247 951bbbb90e2259a4c8950db78946784fb53fcbce Congyi Wu <congyiwu@gmail.com> 1352923207 -0500 merge b3 b4 b5: Merge made by the 'octopus' strategy.
diff --git a/tests/resources/push_src/.gitted/modules/submodule/HEAD b/tests/resources/push_src/.gitted/modules/submodule/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/push_src/.gitted/modules/submodule/config b/tests/resources/push_src/.gitted/modules/submodule/config
new file mode 100644
index 0000000..5981007
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/config
@@ -0,0 +1,15 @@
+[core]
+ repositoryformatversion = 0
+ filemode = false
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../submodule
+ symlinks = false
+ ignorecase = true
+ hideDotFiles = dotGitOnly
+[remote "origin"]
+ fetch = +refs/heads/*:refs/remotes/origin/*
+ url = m:/dd/libgit2/tests-clar/resources/testrepo.git
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/push_src/.gitted/modules/submodule/description b/tests/resources/push_src/.gitted/modules/submodule/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/push_src/.gitted/modules/submodule/index b/tests/resources/push_src/.gitted/modules/submodule/index
new file mode 100644
index 0000000..8e44080
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/index
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/info/exclude b/tests/resources/push_src/.gitted/modules/submodule/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/push_src/.gitted/modules/submodule/logs/HEAD b/tests/resources/push_src/.gitted/modules/submodule/logs/HEAD
new file mode 100644
index 0000000..aedcdf2
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 a65fedf39aefe402d3bb6e24df4d4f5fe4547750 Congyi Wu <congyiwu@gmail.com> 1352923205 -0500 clone: from m:/dd/libgit2/tests-clar/resources/testrepo.git
diff --git a/tests/resources/push_src/.gitted/modules/submodule/logs/refs/heads/master b/tests/resources/push_src/.gitted/modules/submodule/logs/refs/heads/master
new file mode 100644
index 0000000..aedcdf2
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 a65fedf39aefe402d3bb6e24df4d4f5fe4547750 Congyi Wu <congyiwu@gmail.com> 1352923205 -0500 clone: from m:/dd/libgit2/tests-clar/resources/testrepo.git
diff --git a/tests/resources/push_src/.gitted/modules/submodule/logs/refs/remotes/origin/HEAD b/tests/resources/push_src/.gitted/modules/submodule/logs/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..aedcdf2
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/logs/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 a65fedf39aefe402d3bb6e24df4d4f5fe4547750 Congyi Wu <congyiwu@gmail.com> 1352923205 -0500 clone: from m:/dd/libgit2/tests-clar/resources/testrepo.git
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/08/b041783f40edfe12bb406c9c9a8a040177c125 b/tests/resources/push_src/.gitted/modules/submodule/objects/08/b041783f40edfe12bb406c9c9a8a040177c125
new file mode 100644
index 0000000..d1c032f
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/08/b041783f40edfe12bb406c9c9a8a040177c125
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 b/tests/resources/push_src/.gitted/modules/submodule/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
new file mode 100644
index 0000000..cedb2a2
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 b/tests/resources/push_src/.gitted/modules/submodule/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
new file mode 100644
index 0000000..93a16f1
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/18/10dff58d8a660512d4832e740f692884338ccd b/tests/resources/push_src/.gitted/modules/submodule/objects/18/10dff58d8a660512d4832e740f692884338ccd
new file mode 100644
index 0000000..ba0bfb3
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/18/10dff58d8a660512d4832e740f692884338ccd
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fd b/tests/resources/push_src/.gitted/modules/submodule/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fd
new file mode 100644
index 0000000..3ec5412
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fd
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/1b/8cbad43e867676df601306689fe7c3def5e689 b/tests/resources/push_src/.gitted/modules/submodule/objects/1b/8cbad43e867676df601306689fe7c3def5e689
new file mode 100644
index 0000000..6048d4b
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/1b/8cbad43e867676df601306689fe7c3def5e689
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b b/tests/resources/push_src/.gitted/modules/submodule/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
new file mode 100644
index 0000000..225c457
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10 b/tests/resources/push_src/.gitted/modules/submodule/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10
new file mode 100644
index 0000000..cb1ed57
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d b/tests/resources/push_src/.gitted/modules/submodule/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
new file mode 100644
index 0000000..df40d99
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7 b/tests/resources/push_src/.gitted/modules/submodule/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7
new file mode 100644
index 0000000..0a1500a
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54 b/tests/resources/push_src/.gitted/modules/submodule/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
new file mode 100644
index 0000000..321eaa8
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc b/tests/resources/push_src/.gitted/modules/submodule/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
new file mode 100644
index 0000000..9bb5b62
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests/resources/push_src/.gitted/modules/submodule/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
new file mode 100644
index 0000000..7ca4cee
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 b/tests/resources/push_src/.gitted/modules/submodule/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
new file mode 100644
index 0000000..8953b6c
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
@@ -0,0 +1,2 @@
+xŽQ
+Â0DýÎ)öÊ6›Í¦ "xO°‰‰-ØFb¼¿EoàÏ0 ¼Ç¤º,ske×[ÎPn8R,EpD?±gŸ}Ê^3² âÙ<µåµGŽhYKÄèÒ8ЖDAÉ)¿ÉÈ;gôݧÚàšjïp™4ÕŽ¯ô-çû¢óãêr‚ÁŠ;°s°GA4Ûº=ìùÖ(ôin7øIÌKÍFE \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/4b/22b35d44b5a4f589edf3dc89196399771796ea b/tests/resources/push_src/.gitted/modules/submodule/objects/4b/22b35d44b5a4f589edf3dc89196399771796ea
new file mode 100644
index 0000000..b4e5aa1
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/4b/22b35d44b5a4f589edf3dc89196399771796ea
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91 b/tests/resources/push_src/.gitted/modules/submodule/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91
new file mode 100644
index 0000000..351cff8
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 b/tests/resources/push_src/.gitted/modules/submodule/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
new file mode 100644
index 0000000..c1f22c5
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
@@ -0,0 +1,2 @@
+xŽÛ 1EýNi@™Ék2 "X‚$ÙYW0YcÿíÀ¿Ã…s¸¥ÕzïÚÚõMDÏ€0æœ8!¶†ÉÌÞs‰ XŠªgÚdí::@X0»P¢wÙ"F/‰‰œÍRàˆUz÷¥múZZïú²¤ÒV}|•/œo5݇ÒêI£!¬1z Æ:vùÇUim}ê/¢>
+öF- \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a b/tests/resources/push_src/.gitted/modules/submodule/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
new file mode 100644
index 0000000..2ef4faa
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af b/tests/resources/push_src/.gitted/modules/submodule/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
new file mode 100644
index 0000000..716b0c6
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
@@ -0,0 +1 @@
+xŽAj!³ö?0¨£ßÂ09Êo}HÚ6¨}ÿôjUPP©ÕZ&Yÿø˜ AÔ›±€pŒÁFdë¼÷pz[fŽYŒ½PÒqLJ.,Z§`™Å®Ð.ù`’vÙ ³q $Æ5+9çOëtœû>Û/úDE/龡W¯ï*e¿§VŸdf1>ð覭Öê²×äÄ›¹úÊ™F« ­ìTŽÙhœk.i¶^0Ô?P¼R, \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/7b/4384978d2493e851f9cca7858815fac9b10980 b/tests/resources/push_src/.gitted/modules/submodule/objects/7b/4384978d2493e851f9cca7858815fac9b10980
new file mode 100644
index 0000000..23c462f
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/7b/4384978d2493e851f9cca7858815fac9b10980
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/81/4889a078c031f61ed08ab5fa863aea9314344d b/tests/resources/push_src/.gitted/modules/submodule/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
new file mode 100644
index 0000000..2f9b6b6
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/84/96071c1b46c854b31185ea97743be6a8774479 b/tests/resources/push_src/.gitted/modules/submodule/objects/84/96071c1b46c854b31185ea97743be6a8774479
new file mode 100644
index 0000000..5df58dd
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/84/96071c1b46c854b31185ea97743be6a8774479
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe b/tests/resources/push_src/.gitted/modules/submodule/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe
new file mode 100644
index 0000000..71019a6
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe
@@ -0,0 +1,2 @@
+xŒM F]sŠ¹€†Ÿ41ÆxÝ(­I‹ÁéÂÛKݽ/_ÞãP@¡ÚÕø¢!8›)es
+” ¥N&FGSÆ„¹hÑ{+ßCç‰÷ÆZzvØF¡7ZàÎ-¬Îñó‡k™x\ã¡[PÆ8ï´ôGØK/¥^© lÊ>.4 \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162 b/tests/resources/push_src/.gitted/modules/submodule/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
new file mode 100644
index 0000000..4cc3f4d
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
@@ -0,0 +1 @@
+x+)JMU044b040031QrutñueX¡l¨ðmmA‹m›Ì£íJ}Gß;U‘T”˜—œŸ–™“ªWRQÂ`6ýš÷KÇ¥¶^/¾-*|òøWØ¥3P¥y©å`%ËEÛÞ±\&gŽÐ|Ÿ0§ÿ†{Ó1X \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4 b/tests/resources/push_src/.gitted/modules/submodule/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
new file mode 100644
index 0000000..bf7b2bb
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d2 b/tests/resources/push_src/.gitted/modules/submodule/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d2
new file mode 100644
index 0000000..7f1cfb2
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d2
@@ -0,0 +1,2 @@
+xŽM
+Â0F]ç³d2¤ñ®<A~&´`­ÄôþVàæãmïËë²ÌÈÒ¡7Uà$äJöL9yM!¢GuœªH¤&UÈæ›>;ÔÂÁ…¬£³X†ÂEÈŽ5R±£ ÛAÑE &n}ZÜæ<E}À=O[ÒÖáüÞéúÓ¼^À,ã^†#¢É¿ƒ]ÿPÍ`>™A¹ \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a b/tests/resources/push_src/.gitted/modules/submodule/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
new file mode 100644
index 0000000..a796124
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
@@ -0,0 +1,3 @@
+xŽ[
+Â0EýÎ*fÊäÕ¤ "¸W0“‡-ØFâtÿÝ—çpS[–YÀ˜x^
+Díb CLhutɉ}¥8X*4Zí¬sY½¨—UÀ‘AÃÖ ÌX3‡R«Mµ¶) s6è¼¢M¦ÖážšÜ&Jm…ó;}Çõ±Ðü<¥¶\@›à‚ÑÞpÄ€¨vº?”ò«jÛºLð«¨Ø?Hå \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f b/tests/resources/push_src/.gitted/modules/submodule/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
new file mode 100644
index 0000000..f858869
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
@@ -0,0 +1,2 @@
+x;j1DëmdÓú·À˜ÇŽ|M«µ3`ŒV{ >€³âQ¯ ¸·vL0I?Í!š4–Z=Ê! ×¦8²F¢Ã’!rÖsQßyÈ9]$DŽ&„l6AÇ>jFWüÒµ IKNiûë§Z¢%¡SˆŒ‘
+‹Ò ­ÅʉøU~̽øä>'¼ï™û ¯wþ ×[ËÇ× ÷öÚDGÚ¡±ðŒQ-ºMù«>dܶ‘OÞáÒò}í\à8g_ШÂoYr \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750 b/tests/resources/push_src/.gitted/modules/submodule/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
new file mode 100644
index 0000000..29c8e82
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
@@ -0,0 +1,3 @@
+xŽQ
+!@ûösBQ"‚ŽÐ ÆÙ± rÍîßÒú{<xð¤·öàîƪ
+™HlJSer!ZPTe*Žj°UÝÑEo^¼ê2 (†ˆ¬XSÅ€ED‘ƒO<Y¦šj$2üs_á&} ¸Î,}Ó[~p¹7~<ÒÛ:Ÿ £°·ÉZ³Ùípè?­1_ûåC0 \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd b/tests/resources/push_src/.gitted/modules/submodule/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
new file mode 100644
index 0000000..d0d7e73
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 b/tests/resources/push_src/.gitted/modules/submodule/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
new file mode 100644
index 0000000..18a7f61
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/ae/90f12eea699729ed24555e40b9fd669da12a12 b/tests/resources/push_src/.gitted/modules/submodule/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
new file mode 100644
index 0000000..d952546
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1 b/tests/resources/push_src/.gitted/modules/submodule/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
new file mode 100644
index 0000000..f460f25
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
@@ -0,0 +1,2 @@
+xÌA
+Â0…a×9ÅìIÒ ™€ˆp'î§1Ѷ‘v\x{cáýðVŸpƒvWûgŠ¾ÇŽ0xº[ ]"g†#{rDÆ Cot ­äûN œU $­ò?9-p+1Í^¤ÀQx®¯Ï9O\ÆC¬Ó Œm'D {mµVêú(+´ñælè¶,Þ \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593 b/tests/resources/push_src/.gitted/modules/submodule/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
new file mode 100644
index 0000000..f613670
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 b/tests/resources/push_src/.gitted/modules/submodule/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
new file mode 100644
index 0000000..0817229
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
@@ -0,0 +1,3 @@
+xKj1D³Ö)zçUBëÛ-0ÁuV9¦Õò<#£È÷ÏȲ+ŠW<Jú¶Ý&8Ê/s¨‚e‹µµÈ•KJ­«½S
+ØRvÌÁ{©æQ†îr«äY¹QN$H\Eµ²Íè=6áX5¦òÇK Fr)·(‰dC‡Î†”­–œ—jÊs®}À—ô9ác-Òw8Ëo¸\·r»¿IßÞÁ:
+l}F‚W$Ds´Ç£©ÿÙšOW…e”]V8-ÃÌÈ"U \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd b/tests/resources/push_src/.gitted/modules/submodule/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
new file mode 100644
index 0000000..75f541f
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
@@ -0,0 +1,3 @@
+xŽQ
+Â0DýÎ)öʦ»I<‚'ØlR+˜Fj¼¿EoàÏ0<xÃh«õÞa Üõµ]È™­åXUlÞPF)Åz‘4yó”µ,\r 'SÂÄ-mI4
+‘Xhô”&òÌFÞ}n+\µõ—Y´-p|é·œoUz;-‘aÑlt{ØË?®I«,:ÃoÚR̳cHK \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659 b/tests/resources/push_src/.gitted/modules/submodule/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659
new file mode 100644
index 0000000..f3b46b3
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f b/tests/resources/push_src/.gitted/modules/submodule/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
new file mode 100644
index 0000000..a67d6e6
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759 b/tests/resources/push_src/.gitted/modules/submodule/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759
new file mode 100644
index 0000000..2d47e6f
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/push_src/.gitted/modules/submodule/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644
index 0000000..7112238
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0 b/tests/resources/push_src/.gitted/modules/submodule/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
new file mode 100644
index 0000000..b135ecc
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3 b/tests/resources/push_src/.gitted/modules/submodule/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
new file mode 100644
index 0000000..82e2790
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 b/tests/resources/push_src/.gitted/modules/submodule/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
new file mode 100644
index 0000000..697c94c
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/fa/49b077972391ad58037050f2a75f74e3671e92 b/tests/resources/push_src/.gitted/modules/submodule/objects/fa/49b077972391ad58037050f2a75f74e3671e92
new file mode 100644
index 0000000..112998d
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/fa/49b077972391ad58037050f2a75f74e3671e92
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/fd/093bff70906175335656e6ce6ae05783708765 b/tests/resources/push_src/.gitted/modules/submodule/objects/fd/093bff70906175335656e6ce6ae05783708765
new file mode 100644
index 0000000..12bf5f3
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/fd/093bff70906175335656e6ce6ae05783708765
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09 b/tests/resources/push_src/.gitted/modules/submodule/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09
new file mode 100644
index 0000000..158aef2
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
new file mode 100644
index 0000000..5068f28
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
new file mode 100644
index 0000000..a6a1f30
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
new file mode 100644
index 0000000..94c3c71
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
new file mode 100644
index 0000000..74c7fe4
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
new file mode 100644
index 0000000..555cfa9
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
new file mode 100644
index 0000000..4d539ed
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
Binary files differ
diff --git a/tests/resources/push_src/.gitted/modules/submodule/packed-refs b/tests/resources/push_src/.gitted/modules/submodule/packed-refs
new file mode 100644
index 0000000..506a860
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/packed-refs
@@ -0,0 +1,24 @@
+# pack-refs with: peeled
+a4a7dce85cf63874e984719f4fdd239f5145052f refs/remotes/origin/br2
+a4a7dce85cf63874e984719f4fdd239f5145052f refs/remotes/origin/cannot-fetch
+e90810b8df3e80c413d903f631643c716887138d refs/remotes/origin/chomped
+258f0e2a959a364e40ed6603d5d44fbb24765b10 refs/remotes/origin/haacked
+a65fedf39aefe402d3bb6e24df4d4f5fe4547750 refs/remotes/origin/master
+a65fedf39aefe402d3bb6e24df4d4f5fe4547750 refs/remotes/origin/not-good
+41bc8c69075bbdb46c5c6f0566cc8cc5b46e8bd9 refs/remotes/origin/packed
+4a202b346bb0fb0db7eff3cffeb3c70babbd2045 refs/remotes/origin/packed-test
+763d71aadf09a7951596c9746c024e7eece7c7af refs/remotes/origin/subtrees
+e90810b8df3e80c413d903f631643c716887138d refs/remotes/origin/test
+9fd738e8f7967c078dceed8190330fc8648ee56a refs/remotes/origin/track-local
+e90810b8df3e80c413d903f631643c716887138d refs/remotes/origin/trailing
+521d87c1ec3aef9824daf6d96cc0ae3710766d91 refs/tags/annotated_tag_to_blob
+^1385f264afb75a56a5bec74243be9b367ba4ca08
+7b4384978d2493e851f9cca7858815fac9b10980 refs/tags/e90810b
+^e90810b8df3e80c413d903f631643c716887138d
+849a5e34a26815e821f865b8479f5815a47af0fe refs/tags/hard_tag
+^a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+1385f264afb75a56a5bec74243be9b367ba4ca08 refs/tags/point_to_blob
+b25fa35b38051e4ae45d4222e795f9df2e43f1d1 refs/tags/test
+^e90810b8df3e80c413d903f631643c716887138d
+849a5e34a26815e821f865b8479f5815a47af0fe refs/tags/wrapped_tag
+^a65fedf39aefe402d3bb6e24df4d4f5fe4547750
diff --git a/tests/resources/push_src/.gitted/modules/submodule/refs/heads/master b/tests/resources/push_src/.gitted/modules/submodule/refs/heads/master
new file mode 100644
index 0000000..3d8f0a4
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/refs/heads/master
@@ -0,0 +1 @@
+a65fedf39aefe402d3bb6e24df4d4f5fe4547750
diff --git a/tests/resources/push_src/.gitted/modules/submodule/refs/remotes/origin/HEAD b/tests/resources/push_src/.gitted/modules/submodule/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..6efe28f
--- /dev/null
+++ b/tests/resources/push_src/.gitted/modules/submodule/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/push_src/.gitted/objects/08/585692ce06452da6f82ae66b90d98b55536fca b/tests/resources/push_src/.gitted/objects/08/585692ce06452da6f82ae66b90d98b55536fca
new file mode 100644
index 0000000..39d126b
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/08/585692ce06452da6f82ae66b90d98b55536fca
@@ -0,0 +1 @@
+x+)JMU06f040031QHÔ+©(a¨˜!©”h­õ;A•EÿÛÞö®ƒ3ýZüÝ* \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/objects/27/b7ce66243eb1403862d05f958c002312df173d b/tests/resources/push_src/.gitted/objects/27/b7ce66243eb1403862d05f958c002312df173d
new file mode 100644
index 0000000..01d63b5
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/27/b7ce66243eb1403862d05f958c002312df173d
@@ -0,0 +1,4 @@
+x•ŽM
+Â0F]çsËt¦i<„7ù™Ñ‚m¤¤¨··xw÷àKešÆ
+Dý®." ªâmrš1°Ó@’’ê9Ú>RëûÈž¬y†Eæ
+Á mâHŽì&µ™EÐr7äS¢Þ!*u΄µÞËç2ß>#\V8¤ß|­§ÛÆG“Êt„–-ybÂöhÍF·Uþ/ä±J-|M}Wóã+GK \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/objects/28/905c54ea45a4bed8d7b90f51bd8bd81eec8840 b/tests/resources/push_src/.gitted/objects/28/905c54ea45a4bed8d7b90f51bd8bd81eec8840
new file mode 100644
index 0000000..dc10f68
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/28/905c54ea45a4bed8d7b90f51bd8bd81eec8840
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/36/6226fb970ac0caa9d3f55967ab01334a548f60 b/tests/resources/push_src/.gitted/objects/36/6226fb970ac0caa9d3f55967ab01334a548f60
new file mode 100644
index 0000000..45c4d92
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/36/6226fb970ac0caa9d3f55967ab01334a548f60
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/36/f79b2846017d3761e0a02d0bccd573e0f90c57 b/tests/resources/push_src/.gitted/objects/36/f79b2846017d3761e0a02d0bccd573e0f90c57
new file mode 100644
index 0000000..0bc57f2
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/36/f79b2846017d3761e0a02d0bccd573e0f90c57
@@ -0,0 +1,2 @@
+x%Œ]
+ƒ0„ûœSì”ÄÍB)}é ¼@bµnÑéíf`f`>ö3(§nÞibˆC°èû¾ë0öhQ6¢BåMv¨‡à2&-ø÷M0Q)+ ®Œêæª tNsÚà¿E*;}àžøJϲN픹­§(th­ÔÖ@#”BŒËºC•?ÉÀTÃ…oÅyk7ÿ \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/objects/5c/0bb3d1b9449d1cc69d7519fd05166f01840915 b/tests/resources/push_src/.gitted/objects/5c/0bb3d1b9449d1cc69d7519fd05166f01840915
new file mode 100644
index 0000000..8831821
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/5c/0bb3d1b9449d1cc69d7519fd05166f01840915
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/61/780798228d17af2d34fce4cfbdf35556832472 b/tests/resources/push_src/.gitted/objects/61/780798228d17af2d34fce4cfbdf35556832472
new file mode 100644
index 0000000..586bf17
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/61/780798228d17af2d34fce4cfbdf35556832472
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/64/fd55f9b6390202db5e5666fd1fb339089fba4d b/tests/resources/push_src/.gitted/objects/64/fd55f9b6390202db5e5666fd1fb339089fba4d
new file mode 100644
index 0000000..bcaaa91
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/64/fd55f9b6390202db5e5666fd1fb339089fba4d
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/78/981922613b2afb6025042ff6bd878ac1994e85 b/tests/resources/push_src/.gitted/objects/78/981922613b2afb6025042ff6bd878ac1994e85
new file mode 100644
index 0000000..e814d07
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/78/981922613b2afb6025042ff6bd878ac1994e85
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/80/5c54522e614f29f70d2413a0470247d8b424ac b/tests/resources/push_src/.gitted/objects/80/5c54522e614f29f70d2413a0470247d8b424ac
new file mode 100644
index 0000000..552670c
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/80/5c54522e614f29f70d2413a0470247d8b424ac
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/95/1bbbb90e2259a4c8950db78946784fb53fcbce b/tests/resources/push_src/.gitted/objects/95/1bbbb90e2259a4c8950db78946784fb53fcbce
new file mode 100644
index 0000000..596cd43
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/95/1bbbb90e2259a4c8950db78946784fb53fcbce
@@ -0,0 +1,2 @@
+x•[JÆ0…}î*f¿2—Ì$ÁEøœi’úƒm¥¶ˆ»·
+úîÛáã\8ã:Ï×DôfßZ ½ªöì&¹º65³^©»œ,åî%Ôá­lmÙ¡~;KJÌR“XT²®è•„Šö(!{ìÒ¯Ÿ£Ç±™qæP’qÅsPÓˆÈB\;EùëïE’gê”s–`IeoEÈ(YMŽ˜FåÂC9ö—uƒ§u™>¯ð|Àýø#?ŽÇi.××»q€D9³0F¸EENzþßÛÿ“Ãܶ©Ë<\ ,\a_a.ïgßðëd \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/objects/a7/8705c3b2725f931d3ee05348d83cc26700f247 b/tests/resources/push_src/.gitted/objects/a7/8705c3b2725f931d3ee05348d83cc26700f247
new file mode 100644
index 0000000..6ad835e
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/a7/8705c3b2725f931d3ee05348d83cc26700f247
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/b4/83ae7ba66decee9aee971f501221dea84b1498 b/tests/resources/push_src/.gitted/objects/b4/83ae7ba66decee9aee971f501221dea84b1498
new file mode 100644
index 0000000..1e0bd3b
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/b4/83ae7ba66decee9aee971f501221dea84b1498
@@ -0,0 +1,3 @@
+x5K
+1D]ç½¥;1i"^À•'èÎô|d`dŒ ooqQE½Í«*PàÝ¢+
+á 3…$, }ì%¢Rßw¬É+sç9»úy輨«ÍÐrøÃ`+ܦ2ŠÍp/ã[m­p~µuÝê8-—öSˆ™r„=¢Û,?ÃZ+g \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/objects/b4/e1f2b375a64c1ccd40c5ff6aa8bc96839ba4fd b/tests/resources/push_src/.gitted/objects/b4/e1f2b375a64c1ccd40c5ff6aa8bc96839ba4fd
new file mode 100644
index 0000000..4e650aa
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/b4/e1f2b375a64c1ccd40c5ff6aa8bc96839ba4fd
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/c1/0409136a7a75e025fa502a1b2fd7b62b77d279 b/tests/resources/push_src/.gitted/objects/c1/0409136a7a75e025fa502a1b2fd7b62b77d279
new file mode 100644
index 0000000..fcb2b32
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/c1/0409136a7a75e025fa502a1b2fd7b62b77d279
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/cd/881f90f2933db2e4cc26b8c71fe6037ac7fe4c b/tests/resources/push_src/.gitted/objects/cd/881f90f2933db2e4cc26b8c71fe6037ac7fe4c
new file mode 100644
index 0000000..ad42726
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/cd/881f90f2933db2e4cc26b8c71fe6037ac7fe4c
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/d9/b63a88223d8367516f50bd131a5f7349b7f3e4 b/tests/resources/push_src/.gitted/objects/d9/b63a88223d8367516f50bd131a5f7349b7f3e4
new file mode 100644
index 0000000..b471e21
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/d9/b63a88223d8367516f50bd131a5f7349b7f3e4
@@ -0,0 +1,2 @@
+x•ŽA
+Â0E]çsËd¦Ó$ "x×i2Õ‚m¤¤¨··zwŸ÷S™¦±‘ÝÕErŠ½gjÃÐ ![ÍŽ%wbY(z˜C/ÁšG\t®w(‰{r$C`›Y…[Ÿ=§DC¨u&®õV8—ùúá²Â!ýæs=]§8Þ›T¦#|;˜ÐÂÑltûWõÓh«fˆM}Uó¼QDM \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/objects/dc/ab83249f6f9d1ed735d651352a80519339b591 b/tests/resources/push_src/.gitted/objects/dc/ab83249f6f9d1ed735d651352a80519339b591
new file mode 100644
index 0000000..9f6b150
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/dc/ab83249f6f9d1ed735d651352a80519339b591
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/ee/a4f2705eeec2db3813f2430829afce99cd00b5 b/tests/resources/push_src/.gitted/objects/ee/a4f2705eeec2db3813f2430829afce99cd00b5
new file mode 100644
index 0000000..b7b81d5
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/ee/a4f2705eeec2db3813f2430829afce99cd00b5
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/f7/8a3106c85fb549c65198b2a2086276c6174928 b/tests/resources/push_src/.gitted/objects/f7/8a3106c85fb549c65198b2a2086276c6174928
new file mode 100644
index 0000000..b981357
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/f7/8a3106c85fb549c65198b2a2086276c6174928
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/f8/f7aefc2900a3d737cea9eee45729fd55761e1a b/tests/resources/push_src/.gitted/objects/f8/f7aefc2900a3d737cea9eee45729fd55761e1a
new file mode 100644
index 0000000..888354f
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/f8/f7aefc2900a3d737cea9eee45729fd55761e1a
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/fa/38b91f199934685819bea316186d8b008c52a2 b/tests/resources/push_src/.gitted/objects/fa/38b91f199934685819bea316186d8b008c52a2
new file mode 100644
index 0000000..13d9bca
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/fa/38b91f199934685819bea316186d8b008c52a2
@@ -0,0 +1,2 @@
+x•½J1„­ó§ÛÊ5›ÿ…‹>„urr²n’eo‚øö {»™f[)¹ƒ°â©_DmIiµ7
+7Ĩ8ꔌ÷.ànœÜƒW)²Ó_T;xë,×(ÃlÐi—[”D\K墓ˆÂXΓP–ùÑ?Ûï­ß>ÜðW~·£ø|_±•Wؤ»‚xæšs6éÜ×éÿIæc¤J‹ãNP}™~ù œ-מë½Á²®/ó„³­Gî û§X \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/objects/ff/83aa4c5e5d28e3bcba2f5c6e2adc61286a4e5e b/tests/resources/push_src/.gitted/objects/ff/83aa4c5e5d28e3bcba2f5c6e2adc61286a4e5e
new file mode 100644
index 0000000..10f25eb
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/ff/83aa4c5e5d28e3bcba2f5c6e2adc61286a4e5e
@@ -0,0 +1,4 @@
+x5ÍM
+1 `×=Eö¢4ÓNAÄ ¸òýÉüÈÀH ooq‘Ç{›/G@ò»5=$+”SOÝ) n¥xâ≻Ø[Æ@4úy
+h1Ú„v‡ÿ¥ÂmÎS”îyz'©
+çWk×-ŽóziÙQc<ÃÞ¢µfS~Âpv+… \ No newline at end of file
diff --git a/tests/resources/push_src/.gitted/objects/ff/fe95c7fd0a37fa2ed702f8f93b56b2196b3925 b/tests/resources/push_src/.gitted/objects/ff/fe95c7fd0a37fa2ed702f8f93b56b2196b3925
new file mode 100644
index 0000000..1cdc048
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/ff/fe95c7fd0a37fa2ed702f8f93b56b2196b3925
Binary files differ
diff --git a/tests/resources/push_src/.gitted/objects/pack/dummy b/tests/resources/push_src/.gitted/objects/pack/dummy
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/push_src/.gitted/objects/pack/dummy
diff --git a/tests/resources/push_src/.gitted/refs/heads/b1 b/tests/resources/push_src/.gitted/refs/heads/b1
new file mode 100644
index 0000000..afadf9d
--- /dev/null
+++ b/tests/resources/push_src/.gitted/refs/heads/b1
@@ -0,0 +1 @@
+a78705c3b2725f931d3ee05348d83cc26700f247
diff --git a/tests/resources/push_src/.gitted/refs/heads/b2 b/tests/resources/push_src/.gitted/refs/heads/b2
new file mode 100644
index 0000000..afadf9d
--- /dev/null
+++ b/tests/resources/push_src/.gitted/refs/heads/b2
@@ -0,0 +1 @@
+a78705c3b2725f931d3ee05348d83cc26700f247
diff --git a/tests/resources/push_src/.gitted/refs/heads/b3 b/tests/resources/push_src/.gitted/refs/heads/b3
new file mode 100644
index 0000000..3056bb4
--- /dev/null
+++ b/tests/resources/push_src/.gitted/refs/heads/b3
@@ -0,0 +1 @@
+d9b63a88223d8367516f50bd131a5f7349b7f3e4
diff --git a/tests/resources/push_src/.gitted/refs/heads/b4 b/tests/resources/push_src/.gitted/refs/heads/b4
new file mode 100644
index 0000000..efed6f0
--- /dev/null
+++ b/tests/resources/push_src/.gitted/refs/heads/b4
@@ -0,0 +1 @@
+27b7ce66243eb1403862d05f958c002312df173d
diff --git a/tests/resources/push_src/.gitted/refs/heads/b5 b/tests/resources/push_src/.gitted/refs/heads/b5
new file mode 100644
index 0000000..cf313ad
--- /dev/null
+++ b/tests/resources/push_src/.gitted/refs/heads/b5
@@ -0,0 +1 @@
+fa38b91f199934685819bea316186d8b008c52a2
diff --git a/tests/resources/push_src/.gitted/refs/heads/b6 b/tests/resources/push_src/.gitted/refs/heads/b6
new file mode 100644
index 0000000..711e466
--- /dev/null
+++ b/tests/resources/push_src/.gitted/refs/heads/b6
@@ -0,0 +1 @@
+951bbbb90e2259a4c8950db78946784fb53fcbce
diff --git a/tests/resources/push_src/.gitted/refs/tags/tag-blob b/tests/resources/push_src/.gitted/refs/tags/tag-blob
new file mode 100644
index 0000000..abfebf2
--- /dev/null
+++ b/tests/resources/push_src/.gitted/refs/tags/tag-blob
@@ -0,0 +1 @@
+b483ae7ba66decee9aee971f501221dea84b1498
diff --git a/tests/resources/push_src/.gitted/refs/tags/tag-commit b/tests/resources/push_src/.gitted/refs/tags/tag-commit
new file mode 100644
index 0000000..c023b84
--- /dev/null
+++ b/tests/resources/push_src/.gitted/refs/tags/tag-commit
@@ -0,0 +1 @@
+805c54522e614f29f70d2413a0470247d8b424ac
diff --git a/tests/resources/push_src/.gitted/refs/tags/tag-commit-two b/tests/resources/push_src/.gitted/refs/tags/tag-commit-two
new file mode 100644
index 0000000..abb3650
--- /dev/null
+++ b/tests/resources/push_src/.gitted/refs/tags/tag-commit-two
@@ -0,0 +1 @@
+36f79b2846017d3761e0a02d0bccd573e0f90c57
diff --git a/tests/resources/push_src/.gitted/refs/tags/tag-lightweight b/tests/resources/push_src/.gitted/refs/tags/tag-lightweight
new file mode 100644
index 0000000..711e466
--- /dev/null
+++ b/tests/resources/push_src/.gitted/refs/tags/tag-lightweight
@@ -0,0 +1 @@
+951bbbb90e2259a4c8950db78946784fb53fcbce
diff --git a/tests/resources/push_src/.gitted/refs/tags/tag-tag b/tests/resources/push_src/.gitted/refs/tags/tag-tag
new file mode 100644
index 0000000..d6cd748
--- /dev/null
+++ b/tests/resources/push_src/.gitted/refs/tags/tag-tag
@@ -0,0 +1 @@
+eea4f2705eeec2db3813f2430829afce99cd00b5
diff --git a/tests/resources/push_src/.gitted/refs/tags/tag-tree b/tests/resources/push_src/.gitted/refs/tags/tag-tree
new file mode 100644
index 0000000..7a530d3
--- /dev/null
+++ b/tests/resources/push_src/.gitted/refs/tags/tag-tree
@@ -0,0 +1 @@
+ff83aa4c5e5d28e3bcba2f5c6e2adc61286a4e5e
diff --git a/tests/resources/push_src/a.txt b/tests/resources/push_src/a.txt
new file mode 100644
index 0000000..f7eac1c
--- /dev/null
+++ b/tests/resources/push_src/a.txt
@@ -0,0 +1,2 @@
+a
+edit
diff --git a/tests/resources/push_src/fold/b.txt b/tests/resources/push_src/fold/b.txt
new file mode 100644
index 0000000..6178079
--- /dev/null
+++ b/tests/resources/push_src/fold/b.txt
@@ -0,0 +1 @@
+b
diff --git a/tests/resources/push_src/foldb.txt b/tests/resources/push_src/foldb.txt
new file mode 100644
index 0000000..5b38718
--- /dev/null
+++ b/tests/resources/push_src/foldb.txt
@@ -0,0 +1 @@
+edit
diff --git a/tests/resources/push_src/gitmodules b/tests/resources/push_src/gitmodules
new file mode 100644
index 0000000..f1734df
--- /dev/null
+++ b/tests/resources/push_src/gitmodules
@@ -0,0 +1,3 @@
+[submodule "submodule"]
+ path = submodule
+ url = ../testrepo.git
diff --git a/tests/resources/push_src/submodule/.gitted b/tests/resources/push_src/submodule/.gitted
new file mode 100644
index 0000000..3ffcf96
--- /dev/null
+++ b/tests/resources/push_src/submodule/.gitted
@@ -0,0 +1 @@
+gitdir: ../.git/modules/submodule
diff --git a/tests/resources/push_src/submodule/README b/tests/resources/push_src/submodule/README
new file mode 100644
index 0000000..ca8c647
--- /dev/null
+++ b/tests/resources/push_src/submodule/README
@@ -0,0 +1 @@
+hey there
diff --git a/tests/resources/push_src/submodule/branch_file.txt b/tests/resources/push_src/submodule/branch_file.txt
new file mode 100644
index 0000000..a269025
--- /dev/null
+++ b/tests/resources/push_src/submodule/branch_file.txt
@@ -0,0 +1,2 @@
+hi
+bye!
diff --git a/tests/resources/push_src/submodule/new.txt b/tests/resources/push_src/submodule/new.txt
new file mode 100644
index 0000000..8e0884e
--- /dev/null
+++ b/tests/resources/push_src/submodule/new.txt
@@ -0,0 +1 @@
+my new file
diff --git a/tests/resources/rebase-submodule/.gitted/HEAD b/tests/resources/rebase-submodule/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/rebase-submodule/.gitted/ORIG_HEAD b/tests/resources/rebase-submodule/.gitted/ORIG_HEAD
new file mode 100644
index 0000000..a1ccbb4
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/ORIG_HEAD
@@ -0,0 +1 @@
+5fdf086684daae0a8bc61a81afe178edc1e556e7
diff --git a/tests/resources/rebase-submodule/.gitted/config b/tests/resources/rebase-submodule/.gitted/config
new file mode 100644
index 0000000..af2095f
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/config
@@ -0,0 +1,9 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+[branch "asparagus"]
+ rebase = true
+[branch "master"]
+ rebase = true
diff --git a/tests/resources/rebase-submodule/.gitted/description b/tests/resources/rebase-submodule/.gitted/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/rebase-submodule/.gitted/index b/tests/resources/rebase-submodule/.gitted/index
new file mode 100644
index 0000000..b63efab
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/index
Binary files differ
diff --git a/tests/resources/rebase-submodule/.gitted/info/exclude b/tests/resources/rebase-submodule/.gitted/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/rebase-submodule/.gitted/info/refs b/tests/resources/rebase-submodule/.gitted/info/refs
new file mode 100644
index 0000000..230a649
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/info/refs
@@ -0,0 +1,4 @@
+c64ea52df5b31efd7b73769418dc9e25b8803d25 refs/heads/asparagus
+c64ea52df5b31efd7b73769418dc9e25b8803d25 refs/remotes/origin/HEAD
+c64ea52df5b31efd7b73769418dc9e25b8803d25 refs/remotes/origin/asparagus
+02a35db3f24db554b757b3009bc782784267c743 refs/remotes/origin/master
diff --git a/tests/resources/rebase-submodule/.gitted/objects/01/971e2453a407e4b9f6c865e2c37f4db21da294 b/tests/resources/rebase-submodule/.gitted/objects/01/971e2453a407e4b9f6c865e2c37f4db21da294
new file mode 100644
index 0000000..308b386
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/objects/01/971e2453a407e4b9f6c865e2c37f4db21da294
@@ -0,0 +1 @@
+xUÎAjÃ0ЮuŠ¹@ÂX–fd%¡4Rjˆ¬"ËÉõë–tÑÕ|>ÿÁÄZÊÜAã[o)G2R1ä ­ qbthçˆ&BÔ—oié?ûM»<Èà"Z¢Ì¨EÇÀL¦èÄdå·þY\ýcøØÚ’œ¤ÿ†sÖu¾Œµ¼Ã`œ³dˆhÕÞîOö]üçK}øû¼ž_÷XÛíO;Ãú¥ÕE<¬[(U¶{Rß1ØJ \ No newline at end of file
diff --git a/tests/resources/rebase-submodule/.gitted/objects/17/f8ae8ebdd08a4bb272f61b897b308ad42b1b12 b/tests/resources/rebase-submodule/.gitted/objects/17/f8ae8ebdd08a4bb272f61b897b308ad42b1b12
new file mode 100644
index 0000000..79e4c48
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/objects/17/f8ae8ebdd08a4bb272f61b897b308ad42b1b12
@@ -0,0 +1 @@
+x-KnÃ0 D»Ö)xMý€ È¢Yv× P'j«åôú•‹¬ÈâqÈX—¥t@=½õ–3p0qF e´ÄìÒ¤‚vÖ&©ì"²â‡[^;Hå­ÊHzb’6Sð³‰ÎèŒq²3¥€*1z¼÷GmpK¿Ü|=ê²Õ.y¸GwÍÿƒ—:Ǻ¼ƒ"© *CN’¤ÃÇöÜàƒŸelÙÛ:Äe­Oþ.ÛõUϵÝÚ9m¹ƒÖƒŸ5•¹Dîe$+èxoð}ßIJÑRC \ No newline at end of file
diff --git a/tests/resources/rebase-submodule/.gitted/objects/42/cdad903aef3e7b614675e6584a8be417941911 b/tests/resources/rebase-submodule/.gitted/objects/42/cdad903aef3e7b614675e6584a8be417941911
new file mode 100644
index 0000000..99b5e6d
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/objects/42/cdad903aef3e7b614675e6584a8be417941911
Binary files differ
diff --git a/tests/resources/rebase-submodule/.gitted/objects/4b/7c5650008b2e747fe1809eeb5a1dde0e80850a b/tests/resources/rebase-submodule/.gitted/objects/4b/7c5650008b2e747fe1809eeb5a1dde0e80850a
new file mode 100644
index 0000000..0163985
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/objects/4b/7c5650008b2e747fe1809eeb5a1dde0e80850a
Binary files differ
diff --git a/tests/resources/rebase-submodule/.gitted/objects/5b/1e8bccf7787e942aecf61912f94a2c274f85a5 b/tests/resources/rebase-submodule/.gitted/objects/5b/1e8bccf7787e942aecf61912f94a2c274f85a5
new file mode 100644
index 0000000..d4776d8
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/objects/5b/1e8bccf7787e942aecf61912f94a2c274f85a5
Binary files differ
diff --git a/tests/resources/rebase-submodule/.gitted/objects/68/af1fc7407fd9addf1701a87eb1c95c7494c598 b/tests/resources/rebase-submodule/.gitted/objects/68/af1fc7407fd9addf1701a87eb1c95c7494c598
new file mode 100644
index 0000000..6aaf79f
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/objects/68/af1fc7407fd9addf1701a87eb1c95c7494c598
Binary files differ
diff --git a/tests/resources/rebase-submodule/.gitted/objects/68/f6182f4c85d39e1309d97c7e456156dc9c0096 b/tests/resources/rebase-submodule/.gitted/objects/68/f6182f4c85d39e1309d97c7e456156dc9c0096
new file mode 100644
index 0000000..ed1de3a
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/objects/68/f6182f4c85d39e1309d97c7e456156dc9c0096
Binary files differ
diff --git a/tests/resources/rebase-submodule/.gitted/objects/7c/71f7606bd3bfb25d063c970804e7fc00b9605b b/tests/resources/rebase-submodule/.gitted/objects/7c/71f7606bd3bfb25d063c970804e7fc00b9605b
new file mode 100644
index 0000000..ef923e7
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/objects/7c/71f7606bd3bfb25d063c970804e7fc00b9605b
Binary files differ
diff --git a/tests/resources/rebase-submodule/.gitted/objects/7c/7bf85e978f1d18c0566f702d2cb7766b9c8d4f b/tests/resources/rebase-submodule/.gitted/objects/7c/7bf85e978f1d18c0566f702d2cb7766b9c8d4f
new file mode 100644
index 0000000..fe8b157
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/objects/7c/7bf85e978f1d18c0566f702d2cb7766b9c8d4f
@@ -0,0 +1 @@
+x¥±NÄ0D©ý««ádç'‘‚ úýÆÞ\VŠ½‘í\~Ÿ€øº™÷¤/1r…¦w5m|À0è Òt¡ntƺ®%×öû‘¬ékcnu– ïaÇà:K,’à™ú“^éWüµ³—øÆêÖ5¦í5<i«µ:èq^éŸ3ê3qe\ Ó KRê &ڡȶÐóJ¦,N×™à‹ó#|ÈVhç‰N€wäÇ…€Ô™Ôº {‘ÓY}RYa) \ No newline at end of file
diff --git a/tests/resources/rebase-submodule/.gitted/objects/a7/b066537e6be7109abfe4ff97b675d4e077da20 b/tests/resources/rebase-submodule/.gitted/objects/a7/b066537e6be7109abfe4ff97b675d4e077da20
new file mode 100644
index 0000000..54f9b66
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/objects/a7/b066537e6be7109abfe4ff97b675d4e077da20
Binary files differ
diff --git a/tests/resources/rebase-submodule/.gitted/objects/ab/6cf22b4c67a274aa8d31b5877d92341e8c2a9c b/tests/resources/rebase-submodule/.gitted/objects/ab/6cf22b4c67a274aa8d31b5877d92341e8c2a9c
new file mode 100644
index 0000000..5acdf36
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/objects/ab/6cf22b4c67a274aa8d31b5877d92341e8c2a9c
Binary files differ
diff --git a/tests/resources/rebase-submodule/.gitted/objects/c4/e6cca3ec6ae0148ed231f97257df8c311e015f b/tests/resources/rebase-submodule/.gitted/objects/c4/e6cca3ec6ae0148ed231f97257df8c311e015f
new file mode 100644
index 0000000..2bbf28f
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/objects/c4/e6cca3ec6ae0148ed231f97257df8c311e015f
@@ -0,0 +1 @@
+x%P1nÄ0 ëìWð¹CNE§N7¶è¡:*‰’p¬Ô’/¸ßWÎmI‘”ú$=^^ŸŸ._ï?¿¸~|žC¸°ã¼6©yTÈ„A¨(#1eôÌÓé´“.ˆ†áÀ(Hto@̸K-aë°Õ°…¡´²“sá1r6)&)8¸Å·TêÖa¶<0ׇ¿JÙ¢Ý[‡ŒK‡5IJ²²­ÈÀªcáÁ¸q͓쌫r_ÍÛ‡"u^@ÐÈ7~X)—›÷2¸ Ýâ G…,æ¥f¬R¸ùå`BÚúÂ4¶3£½ÁvQŸø¤›HÖ©¦Öu­êa²b SwÞcJ q…)fÆ”èæO‚ùvû×;‡í«ŒŸ \ No newline at end of file
diff --git a/tests/resources/rebase-submodule/.gitted/objects/f3/6de77de6f53dddafeb024ecaf375e45c3d9ddd b/tests/resources/rebase-submodule/.gitted/objects/f3/6de77de6f53dddafeb024ecaf375e45c3d9ddd
new file mode 100644
index 0000000..cd330a7
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/objects/f3/6de77de6f53dddafeb024ecaf375e45c3d9ddd
Binary files differ
diff --git a/tests/resources/rebase-submodule/.gitted/objects/ff/b36e513f5fdf8a6ba850a20142676a2ac4807d b/tests/resources/rebase-submodule/.gitted/objects/ff/b36e513f5fdf8a6ba850a20142676a2ac4807d
new file mode 100644
index 0000000..f655d12
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/objects/ff/b36e513f5fdf8a6ba850a20142676a2ac4807d
Binary files differ
diff --git a/tests/resources/rebase-submodule/.gitted/packed-refs b/tests/resources/rebase-submodule/.gitted/packed-refs
new file mode 100644
index 0000000..44ac842
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled fully-peeled
+c64ea52df5b31efd7b73769418dc9e25b8803d25 refs/heads/asparagus
diff --git a/tests/resources/rebase-submodule/.gitted/refs/heads/asparagus b/tests/resources/rebase-submodule/.gitted/refs/heads/asparagus
new file mode 100644
index 0000000..e6adde8
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/refs/heads/asparagus
@@ -0,0 +1 @@
+17f8ae8ebdd08a4bb272f61b897b308ad42b1b12
diff --git a/tests/resources/rebase-submodule/.gitted/refs/heads/master b/tests/resources/rebase-submodule/.gitted/refs/heads/master
new file mode 100644
index 0000000..2523d49
--- /dev/null
+++ b/tests/resources/rebase-submodule/.gitted/refs/heads/master
@@ -0,0 +1 @@
+01971e2453a407e4b9f6c865e2c37f4db21da294
diff --git a/tests/resources/rebase-submodule/asparagus.txt b/tests/resources/rebase-submodule/asparagus.txt
new file mode 100644
index 0000000..ffb36e5
--- /dev/null
+++ b/tests/resources/rebase-submodule/asparagus.txt
@@ -0,0 +1,10 @@
+ASPARAGUS SOUP.
+
+Take four large bunches of asparagus, scrape it nicely, cut off one inch
+of the tops, and lay them in water, chop the stalks and put them on the
+fire with a piece of bacon, a large onion cut up, and pepper and salt;
+add two quarts of water, boil them till the stalks are quite soft, then
+pulp them through a sieve, and strain the water to it, which must be put
+back in the pot; put into it a chicken cut up, with the tops of
+asparagus which had been laid by, boil it until these last articles are
+sufficiently done, thicken with flour, butter and milk, and serve it up.
diff --git a/tests/resources/rebase-submodule/beef.txt b/tests/resources/rebase-submodule/beef.txt
new file mode 100644
index 0000000..68f6182
--- /dev/null
+++ b/tests/resources/rebase-submodule/beef.txt
@@ -0,0 +1,22 @@
+BEEF SOUP.
+
+Take the hind shin of beef, cut off all the flesh off the leg-bone,
+which must be taken away entirely, or the soup will be greasy. Wash the
+meat clean and lay it in a pot, sprinkle over it one small
+table-spoonful of pounded black pepper, and two of salt; three onions
+the size of a hen's egg, cut small, six small carrots scraped and cut
+up, two small turnips pared and cut into dice; pour on three quarts of
+water, cover the pot close, and keep it gently and steadily boiling five
+hours, which will leave about three pints of clear soup; do not let the
+pot boil over, but take off the scum carefully, as it rises. When it has
+boiled four hours, put in a small bundle of thyme and parsley, and a
+pint of celery cut small, or a tea-spoonful of celery seed pounded.
+These latter ingredients would lose their delicate flavour if boiled too
+much. Just before you take it up, brown it in the following manner: put
+a small table-spoonful of nice brown sugar into an iron skillet, set it
+on the fire and stir it till it melts and looks very dark, pour into it
+a ladle full of the soup, a little at a time; stirring it all the while.
+Strain this browning and mix it well with the soup; take out the bundle
+of thyme and parsley, put the nicest pieces of meat in your tureen, and
+pour on the soup and vegetables; put in some toasted bread cut in dice,
+and serve it up.
diff --git a/tests/resources/rebase-submodule/bouilli.txt b/tests/resources/rebase-submodule/bouilli.txt
new file mode 100644
index 0000000..4b7c565
--- /dev/null
+++ b/tests/resources/rebase-submodule/bouilli.txt
@@ -0,0 +1,18 @@
+SOUP WITH BOUILLI.
+
+Take the nicest part of the thick brisket of beef, about eight pounds,
+put it into a pot with every thing directed for the other soup; make it
+exactly in the same way, only put it on an hour sooner, that you may
+have time to prepare the bouilli; after it has boiled five hours, take
+out the beef, cover up the soup and set it near the fire that it may
+keep hot. Take the skin off the beef, have the yelk of an egg well
+beaten, dip a feather in it and wash the top of your beef, sprinkle over
+it the crumb of stale bread finely grated, put it in a Dutch oven
+previously heated, put the top on with coals enough to brown, but not
+burn the beef; let it stand nearly an hour, and prepare your gravy
+thus:--Take a sufficient quantity of soup and the vegetables boiled in
+it; add to it a table-spoonful of red wine, and two of mushroom catsup,
+thicken with a little bit of butter and a little brown flour; make it
+very hot, pour it in your dish, and put the beef on it. Garnish it with
+green pickle, cut in thin slices, serve up the soup in a tureen with
+bits of toasted bread.
diff --git a/tests/resources/rebase-submodule/gitmodules b/tests/resources/rebase-submodule/gitmodules
new file mode 100644
index 0000000..f36de77
--- /dev/null
+++ b/tests/resources/rebase-submodule/gitmodules
@@ -0,0 +1,3 @@
+[submodule "my-submodule"]
+ path = my-submodule
+ url = bogus
diff --git a/tests/resources/rebase-submodule/gravy.txt b/tests/resources/rebase-submodule/gravy.txt
new file mode 100644
index 0000000..c4e6cca
--- /dev/null
+++ b/tests/resources/rebase-submodule/gravy.txt
@@ -0,0 +1,8 @@
+GRAVY SOUP.
+
+Get eight pounds of coarse lean beef--wash it clean and lay it in your
+pot, put in the same ingredients as for the shin soup, with the same
+quantity of water, and follow the process directed for that. Strain the
+soup through a sieve, and serve it up clear, with nothing more than
+toasted bread in it; two table-spoonsful of mushroom catsup will add a
+fine flavour to the soup.
diff --git a/tests/resources/rebase-submodule/oyster.txt b/tests/resources/rebase-submodule/oyster.txt
new file mode 100644
index 0000000..68af1fc
--- /dev/null
+++ b/tests/resources/rebase-submodule/oyster.txt
@@ -0,0 +1,13 @@
+OYSTER SOUP.
+
+Wash and drain two quarts of oysters, put them on with three quarts of
+water, three onions chopped up, two or three slices of lean ham, pepper
+and salt; boil it till reduced one-half, strain it through a sieve,
+return the liquid into the pot, put in one quart of fresh oysters, boil
+it till they are sufficiently done, and thicken the soup with four
+spoonsful of flour, two gills of rich cream, and the yelks of six new
+laid eggs beaten well; boil it a few minutes after the thickening is put
+in. Take care that it does not curdle, and that the flour is not in
+lumps; serve it up with the last oysters that were put in. If the
+flavour of thyme be agreeable, you may put in a little, but take care
+that it does not boil in it long enough to discolour the soup.
diff --git a/tests/resources/rebase-submodule/veal.txt b/tests/resources/rebase-submodule/veal.txt
new file mode 100644
index 0000000..a7b0665
--- /dev/null
+++ b/tests/resources/rebase-submodule/veal.txt
@@ -0,0 +1,18 @@
+VEAL SOUP.
+
+Put into a pot three quarts of water, three onions cut small, one
+spoonful of black pepper pounded, and two of salt, with two or three
+slices of lean ham; let it boil steadily two hours; skim it
+occasionally, then put into it a shin of veal, let it boil two hours
+longer; take out the slices of ham, and skim off the grease if any
+should rise, take a gill of good cream, mix with it two table-spoonsful
+of flour very nicely, and the yelks of two eggs beaten well, strain this
+mixture, and add some chopped parsley; pour some soup on by degrees,
+stir it well, and pour it into the pot, continuing to stir until it has
+boiled two or three minutes to take off the raw taste of the eggs. If
+the cream be not perfectly sweet, and the eggs quite new, the thickening
+will curdle in the soup. For a change you may put a dozen ripe tomatos
+in, first taking off their skins, by letting them stand a few minutes in
+hot water, when they may be easily peeled. When made in this way you
+must thicken it with the flour only. Any part of the veal may be used,
+but the shin or knuckle is the nicest.
diff --git a/tests/resources/rebase/.gitted/HEAD b/tests/resources/rebase/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/rebase/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/rebase/.gitted/config b/tests/resources/rebase/.gitted/config
new file mode 100644
index 0000000..17e58b1
--- /dev/null
+++ b/tests/resources/rebase/.gitted/config
@@ -0,0 +1,4 @@
+[core]
+ repositoryformatversion = 0
+ bare = false
+ logallrefupdates = true
diff --git a/tests/resources/rebase/.gitted/index b/tests/resources/rebase/.gitted/index
new file mode 100644
index 0000000..0f53a21
--- /dev/null
+++ b/tests/resources/rebase/.gitted/index
Binary files differ
diff --git a/tests/resources/rebase/.gitted/info/exclude b/tests/resources/rebase/.gitted/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/rebase/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/rebase/.gitted/logs/HEAD b/tests/resources/rebase/.gitted/logs/HEAD
new file mode 100644
index 0000000..62d3b16
--- /dev/null
+++ b/tests/resources/rebase/.gitted/logs/HEAD
@@ -0,0 +1 @@
+efad0b11c47cb2f0220cbd6f5b0f93bb99064b00 efad0b11c47cb2f0220cbd6f5b0f93bb99064b00 Edward Thomson <ethomson@edwardthomson.com> 1405623541 -0400 checkout: moving from master to master
diff --git a/tests/resources/rebase/.gitted/objects/00/66204dd469ee930e551fbcf123f98e211c99ce b/tests/resources/rebase/.gitted/objects/00/66204dd469ee930e551fbcf123f98e211c99ce
new file mode 100644
index 0000000..e6f72ce
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/00/66204dd469ee930e551fbcf123f98e211c99ce
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/00/f1b9a0948a7d5d14405eba6030efcdfbb8ff4a b/tests/resources/rebase/.gitted/objects/00/f1b9a0948a7d5d14405eba6030efcdfbb8ff4a
new file mode 100644
index 0000000..a23f526
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/00/f1b9a0948a7d5d14405eba6030efcdfbb8ff4a
@@ -0,0 +1,3 @@
+x¥ŽAnC! D»æ¾@*À𑪪YäÙGå/~ˆ(U¯ZåÙͼ‘f¦´}ßX
+o£«B%
+BU«#DGa‘‚9øì"R”H~±.-HæÎ]o˜}ñ–µH-±HZSÎÅYLÉyåIØU/ëjøg\[‡“ür8_ÛþÝnð¡“þ©/ýžî½´ýì¼fá€ÑL:Ï}±ÆE¶±ÍùVAú¦r¹+› øT \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/01/3cc32d341bab0e6f039f50f153c18986f16c58 b/tests/resources/rebase/.gitted/objects/01/3cc32d341bab0e6f039f50f153c18986f16c58
new file mode 100644
index 0000000..2e32bd3
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/01/3cc32d341bab0e6f039f50f153c18986f16c58
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/01/a17f7d154ab5bf9f8bfede3d82dd00ddf7e7dc b/tests/resources/rebase/.gitted/objects/01/a17f7d154ab5bf9f8bfede3d82dd00ddf7e7dc
new file mode 100644
index 0000000..c21329f
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/01/a17f7d154ab5bf9f8bfede3d82dd00ddf7e7dc
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/02/2d3b6bbd0bfbdf147319476fb8bf405691cb0d b/tests/resources/rebase/.gitted/objects/02/2d3b6bbd0bfbdf147319476fb8bf405691cb0d
new file mode 100644
index 0000000..b1f7468
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/02/2d3b6bbd0bfbdf147319476fb8bf405691cb0d
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/05/3808a709cf91385985369159b296cf61a177ac b/tests/resources/rebase/.gitted/objects/05/3808a709cf91385985369159b296cf61a177ac
new file mode 100644
index 0000000..c38c5b2
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/05/3808a709cf91385985369159b296cf61a177ac
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/0e/f2e2b2a2b8d6e1f8dff5e621e0eca21b693d0c b/tests/resources/rebase/.gitted/objects/0e/f2e2b2a2b8d6e1f8dff5e621e0eca21b693d0c
new file mode 100644
index 0000000..d8ef47c
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/0e/f2e2b2a2b8d6e1f8dff5e621e0eca21b693d0c
@@ -0,0 +1,3 @@
+x¥P»N1¤öWl—
+´g¯í³„"J:~`½^ç"åÎÈ1Aü=‚/ ›—Fš‘¶®çnrw£«B‘Y9g/U­
+¦ä“ÄJ“÷ÌÑ‘Mµ$óÆ]·$,jÐHÈ>K Êìœ"åY+FçÊÌ“y»»¢3YL¤8kf™¼Í®„â3ÖZ]ªI‚~Këð\>¸x]Úzm<ê®~£'ý1~Ùƒ´õ¡Ö:"¸GB4»ºúÏó¢ý´_Óy“§Î·Ïœ·Ñà¦|1_"fi \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/0f/5f6d3353be1a9966fa5767b7d604b051798224 b/tests/resources/rebase/.gitted/objects/0f/5f6d3353be1a9966fa5767b7d604b051798224
new file mode 100644
index 0000000..739aca3
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/0f/5f6d3353be1a9966fa5767b7d604b051798224
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/11/fac10ca1b9318ce361a0be0c3d889d777e299c b/tests/resources/rebase/.gitted/objects/11/fac10ca1b9318ce361a0be0c3d889d777e299c
new file mode 100644
index 0000000..5af5474
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/11/fac10ca1b9318ce361a0be0c3d889d777e299c
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/12/c084412b952396962eb420716df01022b847cc b/tests/resources/rebase/.gitted/objects/12/c084412b952396962eb420716df01022b847cc
new file mode 100644
index 0000000..5244e46
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/12/c084412b952396962eb420716df01022b847cc
@@ -0,0 +1,2 @@
+x¥ŽKjC1³Ö)æúÌèIB6^z— Œ¤äYAQ0¾½ãd×Õ Må¾ïm‚¥ø2‡¸Â8³ )!–m“JÄ¡È"Œ‰ML¨¾yÈe‚6.gg‹C“8iñU»XIWC.›ƒ¯Æg
+Šç¹8–+Ÿç¾ÿô ¼ÉjÿÒ‡<†'½æ¾¿ƒAMÞZô4j­V»d§üóFziµežm) ̉ǗÜÔK€U \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/12/f28ed978639d331269d9dc2b74e87db58e1057 b/tests/resources/rebase/.gitted/objects/12/f28ed978639d331269d9dc2b74e87db58e1057
new file mode 100644
index 0000000..b0dbc3e
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/12/f28ed978639d331269d9dc2b74e87db58e1057
@@ -0,0 +1,3 @@
+x¥O9nÃ0tÍWìbp)‰KF&e:`¹\Ú*$…|?´á¸›3˜‘º,sçÝ©mª€XXÐ
+cŠÑÁ#Û¤V†BÌD¤.F1¿¼éÚ€„R “F
+sØÉûBÖe'‰Èû%ä±>Ú½nðÿxËp½×e¯+\´«ô¥OãÅÎR—OÀ±—9ôãv´Ötµmúfù©y.³p›û„V÷þ‡oÇnþB#Vj \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/19/14d57ddf6c5c997664521cc94f190df46dc1c2 b/tests/resources/rebase/.gitted/objects/19/14d57ddf6c5c997664521cc94f190df46dc1c2
new file mode 100644
index 0000000..921f2cd
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/19/14d57ddf6c5c997664521cc94f190df46dc1c2
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/1b/1d19799fcc89fa3cb821581fcf7f2e8fd2cc4d b/tests/resources/rebase/.gitted/objects/1b/1d19799fcc89fa3cb821581fcf7f2e8fd2cc4d
new file mode 100644
index 0000000..3d206b0
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/1b/1d19799fcc89fa3cb821581fcf7f2e8fd2cc4d
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/1d/83f106355e4309a293e42ad2a2c4b8bdbe77ae b/tests/resources/rebase/.gitted/objects/1d/83f106355e4309a293e42ad2a2c4b8bdbe77ae
new file mode 100644
index 0000000..6230fdf
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/1d/83f106355e4309a293e42ad2a2c4b8bdbe77ae
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/1f/2214c1b13b134d5508f41f6a3b77cc6a8f5182 b/tests/resources/rebase/.gitted/objects/1f/2214c1b13b134d5508f41f6a3b77cc6a8f5182
new file mode 100644
index 0000000..84b875c
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/1f/2214c1b13b134d5508f41f6a3b77cc6a8f5182
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/20/db906c85e78c6dde82eb2ec6d3231c4b96fce8 b/tests/resources/rebase/.gitted/objects/20/db906c85e78c6dde82eb2ec6d3231c4b96fce8
new file mode 100644
index 0000000..2a908da
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/20/db906c85e78c6dde82eb2ec6d3231c4b96fce8
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/22/adb22bef75a0371e85ff6d82e5e60e4b425501 b/tests/resources/rebase/.gitted/objects/22/adb22bef75a0371e85ff6d82e5e60e4b425501
new file mode 100644
index 0000000..7f17ef0
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/22/adb22bef75a0371e85ff6d82e5e60e4b425501
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/2a/a3ce842094e08ebac152b3d6d5b0fff39f9c6e b/tests/resources/rebase/.gitted/objects/2a/a3ce842094e08ebac152b3d6d5b0fff39f9c6e
new file mode 100644
index 0000000..38eca43
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/2a/a3ce842094e08ebac152b3d6d5b0fff39f9c6e
@@ -0,0 +1 @@
+x¥ŽMn! F³æ¾@# 8RTeÓew¹€!™Å„ˆ’V½}hÕt÷ýHO/·m[¸…v£—Ñg$©H$ÄÈb…•Œ¨ÙW >*jUÌ{¹ R4¡Öd19ò™sD‘š\ÄCb¼ðäQ0ü×ÖáM¿¸+œ¯mûh78–¹þ¤Sù=þÚ>·í0Ø%:ç-‹ Öš¹NÙQþ‰1ïM׺fëTp0\:~›'fÐUN \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/2b/4ebffd3111546d278bb5df62e5630930b605fb b/tests/resources/rebase/.gitted/objects/2b/4ebffd3111546d278bb5df62e5630930b605fb
new file mode 100644
index 0000000..5bdfc1e
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/2b/4ebffd3111546d278bb5df62e5630930b605fb
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/30/69cc907e6294623e5917ef6de663928c1febfb b/tests/resources/rebase/.gitted/objects/30/69cc907e6294623e5917ef6de663928c1febfb
new file mode 100644
index 0000000..edd86f7
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/30/69cc907e6294623e5917ef6de663928c1febfb
@@ -0,0 +1 @@
+x¥Ž;n1 DSë¼€RŸ] 07.ÓåEÂ[¬eldøúÞ¾Aº™7ÀàI_×e€OóÇØT!&Æ[ËÅUö5´¹R¬L–ÑP±¸oz+Ab)ižçF¾ø8±Ù«Ç÷qéœÛƒ·?—¾þö+u§餯áÝ>¥¯_@Óä)§Œˆn§»ìÐÞ¸ïÞ[„Dz+ªª¹' TG \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/32/52a0692ace4c4c709f22011227d9dc4845f289 b/tests/resources/rebase/.gitted/objects/32/52a0692ace4c4c709f22011227d9dc4845f289
new file mode 100644
index 0000000..2b2434f
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/32/52a0692ace4c4c709f22011227d9dc4845f289
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/33/f915f9e4dbd9f4b24430e48731a59b45b15500 b/tests/resources/rebase/.gitted/objects/33/f915f9e4dbd9f4b24430e48731a59b45b15500
new file mode 100644
index 0000000..c33f179
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/33/f915f9e4dbd9f4b24430e48731a59b45b15500
@@ -0,0 +1 @@
+x¥Ûi1Eó­*¦iViÁ„@Hi@[­Â®LÚbÜÿî.çæÞZ€Þ¾Œ0YN"e1Æ8K}HÉ!dG‹^H;Iê'î¼ ðÙ' ŽWIJvDâ5ÌÉ{¢´æP¬¨x×¾Ãgù{¯koGßàÌ3ýWï|/î”{{cç¢F‚WmµV3°ƒŸœQ}“ïšGÝ.Ðz©Rsuââ1¿ÅËíPH¾ZÞ \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/34/86a9d4cdf0b7b4a702c199eed541dc3af13a03 b/tests/resources/rebase/.gitted/objects/34/86a9d4cdf0b7b4a702c199eed541dc3af13a03
new file mode 100644
index 0000000..fdbe16d
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/34/86a9d4cdf0b7b4a702c199eed541dc3af13a03
@@ -0,0 +1 @@
+xM½nÃ0 „;ë)îŒ,‚L-Э@ƒþ e›Ž…Ò¢*Quüö¥…è"Ç#ï#{–÷ÇãÝãÃëóÓ'Þ^>ÎçÎU!:g"\sLè}fÚº?ù»ú¬M_½Rî0ÑjMAbÁ`ý5u®„+Ÿ³˜± Ù'áãØê!ª` u&lÞ¨A·=MkŽ!µY®,žù„^#(.•7è*˜¥æ²#QD²Ô1™ɘÂ!Ic£4‹Ë~øÚK‘öÂRU%@ƒ/l4ÍËdH³_:¬Ag$J‰r#/žõän …eý–x¹9)[¾ÑÖtÀ»,^¥Àg²1Ž®1ÛðãÔn;†ÎÁÎ$æw¿/Š \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/3c/33b080bf75724c8899d8e703614cb59bfbd047 b/tests/resources/rebase/.gitted/objects/3c/33b080bf75724c8899d8e703614cb59bfbd047
new file mode 100644
index 0000000..8716898
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/3c/33b080bf75724c8899d8e703614cb59bfbd047
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/3d/a85aca38a95b44d77ef55a8deb445e49ba19b4 b/tests/resources/rebase/.gitted/objects/3d/a85aca38a95b44d77ef55a8deb445e49ba19b4
new file mode 100644
index 0000000..fa6d946
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/3d/a85aca38a95b44d77ef55a8deb445e49ba19b4
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/3e/8989b5a16d5258c935d998ef0e6bb139cc4757 b/tests/resources/rebase/.gitted/objects/3e/8989b5a16d5258c935d998ef0e6bb139cc4757
new file mode 100644
index 0000000..1bbf138
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/3e/8989b5a16d5258c935d998ef0e6bb139cc4757
@@ -0,0 +1,2 @@
+x¥ŽKj1 †»ö)tù1¶Jé&Ëìz%C'.®›\?nÈ ºûðñå¶ïu€[ÒËèªPŠ¦ÕÓŠ™"åÀ›%¬Cñ¢0+›oîzrÚ
+-º&*V,e\b, ¸¼¥ã¶f’P ÿŽsëp”wÏsÛÚÞt®éCdz½æ¶¿ƒ æœwÓÍ\§ìÐbÌ©I-5ó¨SÁÂhpUþ2w_ÎT
diff --git a/tests/resources/rebase/.gitted/objects/3f/05a038dd89f51ba2b3d7b14ba1f8c00f0e31ac b/tests/resources/rebase/.gitted/objects/3f/05a038dd89f51ba2b3d7b14ba1f8c00f0e31ac
new file mode 100644
index 0000000..26bd353
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/3f/05a038dd89f51ba2b3d7b14ba1f8c00f0e31ac
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/3f/d8d53cf02de539b9a25a5941030451f76a152f b/tests/resources/rebase/.gitted/objects/3f/d8d53cf02de539b9a25a5941030451f76a152f
new file mode 100644
index 0000000..91c4d95
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/3f/d8d53cf02de539b9a25a5941030451f76a152f
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/40/0d89e8ee6cd91b67b1f45de1ca190e1c580c6f b/tests/resources/rebase/.gitted/objects/40/0d89e8ee6cd91b67b1f45de1ca190e1c580c6f
new file mode 100644
index 0000000..f674753
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/40/0d89e8ee6cd91b67b1f45de1ca190e1c580c6f
@@ -0,0 +1 @@
+x¥O;Â0 eÎ)|P·•badãqC‡6¨ âúÄ ØÞGzŸT¦i¬àÉoê’3XŲ²¨ º!t¤Ü³»§Á%¶bqÉsQAñ{OÔ$†>ŠïI1aPTb´ƒëM|Ö{Yà,¯¸\ïeZË ‡ÜÔ:å¯ñc»T¦#¸OwÔ!lm°Ö4µ­ùÏs)2ê˜bÛ„Z ®íO¼=Wó>{VT \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/41/4dfc71ead79c07acd4ea47fecf91f289afc4b9 b/tests/resources/rebase/.gitted/objects/41/4dfc71ead79c07acd4ea47fecf91f289afc4b9
new file mode 100644
index 0000000..546815e
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/41/4dfc71ead79c07acd4ea47fecf91f289afc4b9
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/41/c5a0a761bb4a7670924c1af0800b30fe9a21be b/tests/resources/rebase/.gitted/objects/41/c5a0a761bb4a7670924c1af0800b30fe9a21be
new file mode 100644
index 0000000..ecb3992
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/41/c5a0a761bb4a7670924c1af0800b30fe9a21be
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/42/cdad903aef3e7b614675e6584a8be417941911 b/tests/resources/rebase/.gitted/objects/42/cdad903aef3e7b614675e6584a8be417941911
new file mode 100644
index 0000000..99b5e6d
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/42/cdad903aef3e7b614675e6584a8be417941911
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/44/c801fe026abbc141b52a4dec5df15fa98249c6 b/tests/resources/rebase/.gitted/objects/44/c801fe026abbc141b52a4dec5df15fa98249c6
new file mode 100644
index 0000000..eaf24ae
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/44/c801fe026abbc141b52a4dec5df15fa98249c6
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/4b/21eb6eeeec7f8fc89a1d334faff9bd5f5f8c34 b/tests/resources/rebase/.gitted/objects/4b/21eb6eeeec7f8fc89a1d334faff9bd5f5f8c34
new file mode 100644
index 0000000..0c9f4b9
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/4b/21eb6eeeec7f8fc89a1d334faff9bd5f5f8c34
@@ -0,0 +1,2 @@
+x¥]J1„}Î)úJOÒ™L`A¼èt:»3‘™,^ß(ÞÀ·úâ+é­Õ6ÐÃ8TÁ‰s 7L%ø`I¶-Ƽi@·.$ÉÇTRF
+æ“Ýl¾8ÂQ±8—i¡•‹J”¤ží:g<"¾[?à-ñ‘áýÖÛÙw¸èLÔ‹þîIz{†…ЯÖbpðˆ„hf:a‡þsƼö½|Tu¿Bë¹–*<êÄ!øœßøz?Í72ZÝ \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/4b/7c5650008b2e747fe1809eeb5a1dde0e80850a b/tests/resources/rebase/.gitted/objects/4b/7c5650008b2e747fe1809eeb5a1dde0e80850a
new file mode 100644
index 0000000..0163985
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/4b/7c5650008b2e747fe1809eeb5a1dde0e80850a
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/4b/ed71df7017283cac61bbf726197ad6a5a18b84 b/tests/resources/rebase/.gitted/objects/4b/ed71df7017283cac61bbf726197ad6a5a18b84
new file mode 100644
index 0000000..f206618
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/4b/ed71df7017283cac61bbf726197ad6a5a18b84
@@ -0,0 +1,2 @@
+x¥Ž;nB1ES{³ Ûø3–PD“’.ÇáGÆ€Ø=bt÷#nëº °?FAdÖ’IO‰ÅÎh$ÛœI¶[
+Ϋ?êr9æŠ^RÄjŠAÖ>„µ-–sŒ!äÄX\UtÇÖá»Ü¨ø9¶õÜN°“¹þ§½<WÛp[¿À¸ ³Ö& ŸÚi­æ:e‡¼‰Q‡V–º0e* ~;]ïêMÿU9 \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/4c/acc6f6e740a5bc64faa33e04b8ef0733d8a127 b/tests/resources/rebase/.gitted/objects/4c/acc6f6e740a5bc64faa33e04b8ef0733d8a127
new file mode 100644
index 0000000..36a6b31
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/4c/acc6f6e740a5bc64faa33e04b8ef0733d8a127
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/4f/b698bde45d7d2833e3f2aacfbfe8a7e7f60a65 b/tests/resources/rebase/.gitted/objects/4f/b698bde45d7d2833e3f2aacfbfe8a7e7f60a65
new file mode 100644
index 0000000..8bb69d0
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/4f/b698bde45d7d2833e3f2aacfbfe8a7e7f60a65
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/50/8be4ff49d38465ad3de58f66d38f70e59f881f b/tests/resources/rebase/.gitted/objects/50/8be4ff49d38465ad3de58f66d38f70e59f881f
new file mode 100644
index 0000000..7ce4452
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/50/8be4ff49d38465ad3de58f66d38f70e59f881f
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/53/f75e45a463033854e52fa8d39dc858e45537d0 b/tests/resources/rebase/.gitted/objects/53/f75e45a463033854e52fa8d39dc858e45537d0
new file mode 100644
index 0000000..f25ef1f
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/53/f75e45a463033854e52fa8d39dc858e45537d0
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/58/8e5d2f04d49707fe4aab865e1deacaf7ef6787 b/tests/resources/rebase/.gitted/objects/58/8e5d2f04d49707fe4aab865e1deacaf7ef6787
new file mode 100644
index 0000000..766adc1
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/58/8e5d2f04d49707fe4aab865e1deacaf7ef6787
@@ -0,0 +1 @@
+x¥ŽK!D]sŠ¾€†o‰1n\ºóÐ4™YÌ`FŒ×7pWõ*©<jË2wÐÎïúÆ Ù“3˜\†B*‰³±Å±Š\2%çB(Cvâž6^;‰‘(JϨ£EmØEå¹baDu U9×,Ò³OmƒKy¥­ÀmjË£­päA?éÌßá×Ô–(+j¼„½´RŠA‡lç?oĵ•¹Î”ú<,ô™¹Š7è_T \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/5a/72bf3bf964fdb176ffa4587312e69e2039695a b/tests/resources/rebase/.gitted/objects/5a/72bf3bf964fdb176ffa4587312e69e2039695a
new file mode 100644
index 0000000..80eb921
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/5a/72bf3bf964fdb176ffa4587312e69e2039695a
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/5b/1e8bccf7787e942aecf61912f94a2c274f85a5 b/tests/resources/rebase/.gitted/objects/5b/1e8bccf7787e942aecf61912f94a2c274f85a5
new file mode 100644
index 0000000..d4776d8
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/5b/1e8bccf7787e942aecf61912f94a2c274f85a5
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/60/29cb003b59f710f9a8ebd9da9ece2d73070b69 b/tests/resources/rebase/.gitted/objects/60/29cb003b59f710f9a8ebd9da9ece2d73070b69
new file mode 100644
index 0000000..a48e023
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/60/29cb003b59f710f9a8ebd9da9ece2d73070b69
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/61/139b9b40a3e489f4abbc6af14e10ae14006e47 b/tests/resources/rebase/.gitted/objects/61/139b9b40a3e489f4abbc6af14e10ae14006e47
new file mode 100644
index 0000000..b096a96
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/61/139b9b40a3e489f4abbc6af14e10ae14006e47
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/61/30e5fcbdce2aa8b3cfd84706c58a892e7d8dd0 b/tests/resources/rebase/.gitted/objects/61/30e5fcbdce2aa8b3cfd84706c58a892e7d8dd0
new file mode 100644
index 0000000..116da7c
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/61/30e5fcbdce2aa8b3cfd84706c58a892e7d8dd0
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/63/c18bf188b8a1ab0bad85161dc3fb43c48ed0db b/tests/resources/rebase/.gitted/objects/63/c18bf188b8a1ab0bad85161dc3fb43c48ed0db
new file mode 100644
index 0000000..297c432
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/63/c18bf188b8a1ab0bad85161dc3fb43c48ed0db
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/67/ed7afb256807556f9b74fa4f7c9284aaec1120 b/tests/resources/rebase/.gitted/objects/67/ed7afb256807556f9b74fa4f7c9284aaec1120
new file mode 100644
index 0000000..82da206
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/67/ed7afb256807556f9b74fa4f7c9284aaec1120
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/68/af1fc7407fd9addf1701a87eb1c95c7494c598 b/tests/resources/rebase/.gitted/objects/68/af1fc7407fd9addf1701a87eb1c95c7494c598
new file mode 100644
index 0000000..6aaf79f
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/68/af1fc7407fd9addf1701a87eb1c95c7494c598
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/68/f6182f4c85d39e1309d97c7e456156dc9c0096 b/tests/resources/rebase/.gitted/objects/68/f6182f4c85d39e1309d97c7e456156dc9c0096
new file mode 100644
index 0000000..ed1de3a
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/68/f6182f4c85d39e1309d97c7e456156dc9c0096
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/6c/8e16469b6ca09a07e00f0e07a5143c31dcfb64 b/tests/resources/rebase/.gitted/objects/6c/8e16469b6ca09a07e00f0e07a5143c31dcfb64
new file mode 100644
index 0000000..2b8d569
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/6c/8e16469b6ca09a07e00f0e07a5143c31dcfb64
@@ -0,0 +1 @@
+xERKnÛ0횧x²+Z +5U’-Dò(jd¢I†Ÿ¾}‡´Ú®Dˆ3ïËɸ _¿ûÒ}ûÞ¾œ ÇSÿ Ä(7Ââr€‘áL˜²U+E¸2zä9ÇQé :ÁjEæÖàé4âøüŒã¡Ã¯ÃÓ«à…´’ó</íÌx·òçmq•‰Bµ:_§b’f‹u¬g ñµ{c¤ò‹„«N+$¼&EEÊ$•³ »‹tV; •²¿“õ]ßwïh?1´¿ÇG!çéêð‘eHÕήarÚÜu%mê Õ0ñG։ݒš¢æ |6Uò…w‚Ëç"+jú¤;oLA²¿â¼â³N©ÁuÕjÅÛiñ£{la+Q”QïÒ#<Ë׶Î3¦â…þ›ª °ŒÇ~à¤Å¿6vìUΘˆ7ŒÔ|âJª3®([vVx"ñeLà´2\ª $b^­4Ùdn˜e#i箜‹áÇÀ`9qeµ¡‹6Ûî–Âg}Ù?ˆ?QÂȃ \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/6d/77ce8fa2cd93c6489236e33e45e35203ca748c b/tests/resources/rebase/.gitted/objects/6d/77ce8fa2cd93c6489236e33e45e35203ca748c
new file mode 100644
index 0000000..5c3f686
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/6d/77ce8fa2cd93c6489236e33e45e35203ca748c
@@ -0,0 +1 @@
+x+)JMU0¶`040031QHJ,ÊI­Ô+©(ahÞùóÞÿš‚z >þÔœ†rS57jÜÀ \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/6d/fb87d20f3dbca02da4a39890114fd9ba6a51e7 b/tests/resources/rebase/.gitted/objects/6d/fb87d20f3dbca02da4a39890114fd9ba6a51e7
new file mode 100644
index 0000000..039c669
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/6d/fb87d20f3dbca02da4a39890114fd9ba6a51e7
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/73/f346c88d965227a03c0af8d555870b8c5021d4 b/tests/resources/rebase/.gitted/objects/73/f346c88d965227a03c0af8d555870b8c5021d4
new file mode 100644
index 0000000..215df59
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/73/f346c88d965227a03c0af8d555870b8c5021d4
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/74/0a804e8963759c98e5b8cb912e15ae74a7a4a6 b/tests/resources/rebase/.gitted/objects/74/0a804e8963759c98e5b8cb912e15ae74a7a4a6
new file mode 100644
index 0000000..b817182
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/74/0a804e8963759c98e5b8cb912e15ae74a7a4a6
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/77/0f14546ee2563a26c52afa5cc4139a96e5d360 b/tests/resources/rebase/.gitted/objects/77/0f14546ee2563a26c52afa5cc4139a96e5d360
new file mode 100644
index 0000000..6091d54
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/77/0f14546ee2563a26c52afa5cc4139a96e5d360
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/78/c320b06544e23d786a9ec84ee93861f2933094 b/tests/resources/rebase/.gitted/objects/78/c320b06544e23d786a9ec84ee93861f2933094
new file mode 100644
index 0000000..afa39fb
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/78/c320b06544e23d786a9ec84ee93861f2933094
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/79/e28694aae0d3064b06f96a5207b943a2357f07 b/tests/resources/rebase/.gitted/objects/79/e28694aae0d3064b06f96a5207b943a2357f07
new file mode 100644
index 0000000..17ff306
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/79/e28694aae0d3064b06f96a5207b943a2357f07
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/7a/05900f340af0252aaa4e34941f040c5d2fe7f7 b/tests/resources/rebase/.gitted/objects/7a/05900f340af0252aaa4e34941f040c5d2fe7f7
new file mode 100644
index 0000000..43201f8
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/7a/05900f340af0252aaa4e34941f040c5d2fe7f7
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/7a/677f6201c8f9d46bdfe1f4b08cb504e360a34e b/tests/resources/rebase/.gitted/objects/7a/677f6201c8f9d46bdfe1f4b08cb504e360a34e
new file mode 100644
index 0000000..dc2fd5a
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/7a/677f6201c8f9d46bdfe1f4b08cb504e360a34e
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/7c/7bf85e978f1d18c0566f702d2cb7766b9c8d4f b/tests/resources/rebase/.gitted/objects/7c/7bf85e978f1d18c0566f702d2cb7766b9c8d4f
new file mode 100644
index 0000000..fe8b157
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/7c/7bf85e978f1d18c0566f702d2cb7766b9c8d4f
@@ -0,0 +1 @@
+x¥±NÄ0D©ý««ádç'‘‚ úýÆÞ\VŠ½‘í\~Ÿ€øº™÷¤/1r…¦w5m|À0è Òt¡ntƺ®%×öû‘¬ékcnu– ïaÇà:K,’à™ú“^éWüµ³—øÆêÖ5¦í5<i«µ:èq^éŸ3ê3qe\ Ó KRê &ڡȶÐóJ¦,N×™à‹ó#|ÈVhç‰N€wäÇ…€Ô™Ôº {‘ÓY}RYa) \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/7f/37fe2d7320360f8a9118b1ed8fba6f38481679 b/tests/resources/rebase/.gitted/objects/7f/37fe2d7320360f8a9118b1ed8fba6f38481679
new file mode 100644
index 0000000..400df28
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/7f/37fe2d7320360f8a9118b1ed8fba6f38481679
@@ -0,0 +1 @@
+x¥Ž½n1„©ýû óâ;{%„hR¦KùöG¸8ŒŒQ^?å ¢if¾‘FÃuÛJ\p×›*$ ¢ ÇäW2#V1‰FCˆAcFqÅ™Ü=7½uð«O‘ȘY>òšÐÏÉ[4Ôd‚ÌA\~ökmð!?¹ |]ëö¨78é /wÑwñ—\·3ø0Í ââì§0MnÐq¶ë?gÜg•b…s/ãB€j ­¨|ß5»_X$W‡ \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/80/32d630f37266bace093e353f7b97d7f8b20950 b/tests/resources/rebase/.gitted/objects/80/32d630f37266bace093e353f7b97d7f8b20950
new file mode 100644
index 0000000..07050df
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/80/32d630f37266bace093e353f7b97d7f8b20950
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/80/dce0e74f0534811db734a68c23b49f98584d7a b/tests/resources/rebase/.gitted/objects/80/dce0e74f0534811db734a68c23b49f98584d7a
new file mode 100644
index 0000000..1b98b0a
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/80/dce0e74f0534811db734a68c23b49f98584d7a
@@ -0,0 +1,2 @@
+xERÍNÃ0 朧ðTÜHœ* ¶Šm¦îj5KBâ0ííq²§¦©ýý¶·¾‡»‡û›v×µŸíëa»í¡»Uj¯g„ÑçVÇ#BŸ™0A§ £>æÔ@2QbpdÐ^x:ìaûòÛÍ
+Þ6Ok% <!°2¯Ý x—rsrpÖŒ±3ùP§k;§:Ö Ð~½ú¤òT#E„3ñ¡Á"¥×Æ;]DzGÞÉ 9\ɺU×­>¡Ý<î}ß?*= Àg_YG®v ½'{ÕÅdë ~ÕñW&FH~䦨٨m•|’èó±ÈJ„ßxåMµø+Î+¾ø—”8Od&8åÄÐ#„ÌJ,Ì%Š2<?–Ky¯ó‚idaÆS5ß@%õ×Æ‚=éA eÃj’“TRIEÙ‰³Â“P>ŠI€Œ•RuD•ò8’!tl/0x'Fxᮜ£•ŸAÀ2Keµ¡Ùyq‹ñ»þ9ܪðÊà \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/83/53b9f9deff7c707f280e0f656c80772cca7cd9 b/tests/resources/rebase/.gitted/objects/83/53b9f9deff7c707f280e0f656c80772cca7cd9
new file mode 100644
index 0000000..a4a7e3a
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/83/53b9f9deff7c707f280e0f656c80772cca7cd9
@@ -0,0 +1,2 @@
+xMAjÃ0E»Ö)þL6]…¬ÒÚBh‚í@»”íq,*kTi\7·ïØèJðçóæiÏ ·Û‡§}y<|¢:]ÎcΗ§wÔ¯åဗ·ã±÷hlòt+ C"Â÷d“ä%Ÿ­P*ÐÓ ŽCF; ¦X˜êíÏû²<Õr›l¤6tëÜat®¥B#âü âä¶PeJÁÅ•eòh½ß¡açáW
+âo™1ð”ò¢DQ·º€^#pBï~‘§Ð­–Z1ɶ_Ë(Ðòö'*°Êg¯6kד* v,0;)FJ«y¶^væ.’=ÏÿEà9\ïMJº_m§¸AÍ£ΰ‰cè·%ïõ°]çDo=† .#³öÍ óŒŠ \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/85/258e426a341cc1aa035ac7f6d18f84fed2ab38 b/tests/resources/rebase/.gitted/objects/85/258e426a341cc1aa035ac7f6d18f84fed2ab38
new file mode 100644
index 0000000..af1106d
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/85/258e426a341cc1aa035ac7f6d18f84fed2ab38
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/85/f34ce9ca9e0f33d4146afec9cbe5a26757500a b/tests/resources/rebase/.gitted/objects/85/f34ce9ca9e0f33d4146afec9cbe5a26757500a
new file mode 100644
index 0000000..03e7516
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/85/f34ce9ca9e0f33d4146afec9cbe5a26757500a
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/86/a5415741ed3754ccb0cac1fc19fd82587840a4 b/tests/resources/rebase/.gitted/objects/86/a5415741ed3754ccb0cac1fc19fd82587840a4
new file mode 100644
index 0000000..fe00a22
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/86/a5415741ed3754ccb0cac1fc19fd82587840a4
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/8d/1f13f93c4995760ac07d129246ac1ff64c0be9 b/tests/resources/rebase/.gitted/objects/8d/1f13f93c4995760ac07d129246ac1ff64c0be9
new file mode 100644
index 0000000..a66cfcc
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/8d/1f13f93c4995760ac07d129246ac1ff64c0be9
@@ -0,0 +1,2 @@
+x¥ŽAnÃ0 {Ö+øMÙ¹ôØ[>@“⃣ÀQïÇ-úƒÜvgÅh[×¥¥é£oî0P"Á±¨³²NX*ÆH4Y1åÌ©R.á&›_;˜MQh0$+CÎcå$³š`ÒšŒæÂAýÒ6ø¶§lçK[ïí
+ßéo:ùßðß¾´­GˆŒi¤˜™á1ìt—íþæMøi¶ÔE¥/»Ao0»×ðâ›S€ \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/8d/95ea62e621f1d38d230d9e7d206e41096d76af b/tests/resources/rebase/.gitted/objects/8d/95ea62e621f1d38d230d9e7d206e41096d76af
new file mode 100644
index 0000000..464de7c
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/8d/95ea62e621f1d38d230d9e7d206e41096d76af
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/8f/4de6c781b9ff9cedfd7f9f9f224e744f97b259 b/tests/resources/rebase/.gitted/objects/8f/4de6c781b9ff9cedfd7f9f9f224e744f97b259
new file mode 100644
index 0000000..faa9389
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/8f/4de6c781b9ff9cedfd7f9f9f224e744f97b259
@@ -0,0 +1 @@
+x+)JMU0·d040031QHJ,ÊI­Ô+©(a¨|Ô6eÕƒËlÞl?³‚Øw:/2­g‡ªJ)ÊLM‰/HM+tôýSøêúö5W^¹ÿ;?ñSçú#;a9$j \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/91/4f3c604d1098847b7fe275f659ee329878153f b/tests/resources/rebase/.gitted/objects/91/4f3c604d1098847b7fe275f659ee329878153f
new file mode 100644
index 0000000..726bf91
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/91/4f3c604d1098847b7fe275f659ee329878153f
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/92/54a37fde7e97f9a28dee2967fdb2c5d1ed94e9 b/tests/resources/rebase/.gitted/objects/92/54a37fde7e97f9a28dee2967fdb2c5d1ed94e9
new file mode 100644
index 0000000..10d6c13
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/92/54a37fde7e97f9a28dee2967fdb2c5d1ed94e9
@@ -0,0 +1 @@
+xERËnã0 ìY_Á0z+Z '5uè¢Îúœe…Ž…(–V¢äï—RŠöd‰Îp8­áéåùAv{ÙÊÍÐA× ûG!zùQúZø#ÛM oÃçj[wà&PÑ« N)VuPÁ,F£½U 1f·pyѳhÖÐokè›}WZŽ`Õ hÆ ?ÃUnšÏ5ˆ¤ì9˜g¢sKþŠõ®­á°ë· a¿«W50ó›\5ŸLˤá„,jGHþ.æÑ{ …0*K¯B¾¿Chàï Û¾ËÙ×m£3ö.HÆ–ÓÏ4á_2Ä㹉ª Z„O¶Œ|ákpé4óÑàÞu#Åþ²§âÈñ–*¸ÎFÏpI‘`D`bTúœW‘¡ÞÑk.ò½à™SsÃM] ÍKÎGÞ´øIã›{VG¦æ« Ÿ8’âŒ#J ;˽ù‘'PŒ¶Èû(bš&£ .dopäø²Ñ»vÑœ¬KÕ˜ˆ#+ ½{þv‹á«üÉ?ŠÿÐwǃ \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/95/39b2cc291d6a6b1b266df8474d31fdd344dd79 b/tests/resources/rebase/.gitted/objects/95/39b2cc291d6a6b1b266df8474d31fdd344dd79
new file mode 100644
index 0000000..9649434
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/95/39b2cc291d6a6b1b266df8474d31fdd344dd79
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/9a/8535dfcaf7554c728d874f047c5461fb2c71d1 b/tests/resources/rebase/.gitted/objects/9a/8535dfcaf7554c728d874f047c5461fb2c71d1
new file mode 100644
index 0000000..d997426
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/9a/8535dfcaf7554c728d874f047c5461fb2c71d1
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/9c/d483e7da23819d7f71d24e9843812337886753 b/tests/resources/rebase/.gitted/objects/9c/d483e7da23819d7f71d24e9843812337886753
new file mode 100644
index 0000000..d168455
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/9c/d483e7da23819d7f71d24e9843812337886753
@@ -0,0 +1 @@
+xMAjÃ0E»Ö)þL6]…¬ÒÚ€ÛÛv)ÛãXTѨҨ®o_ÙèJðçóæiZË-·Û‡§}U>QŸ.çRçKƒÓ;š×êpÀ˱,kð€VKs¾“—|ÒB¡À@Øv]$_¨úøç}Uš± ÚSíúunœ0zÓQ‘#PÆÙ…éÄȼP%güÊRõÛ¾,whÙXÁ•œØ21FN!.JäàóVã0ä0˜‚çäúÕ2WTÐÝ×2r´¼nI„]Xå£Í6k×RVõ­Àdd„'ï)¬æQ[Ù©»H´<ýew½7)äýÙ6ù ¾iá(cývdmþtßÉ7C>†Œ&"rî«?v„‹ê \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/a0/1a6ee390f65d834375e072952deaee0c5e92f7 b/tests/resources/rebase/.gitted/objects/a0/1a6ee390f65d834375e072952deaee0c5e92f7
new file mode 100644
index 0000000..eb98c9d
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/a0/1a6ee390f65d834375e072952deaee0c5e92f7
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/a0/fa65f96c1e3bdc7287e334229279dcc1248fa4 b/tests/resources/rebase/.gitted/objects/a0/fa65f96c1e3bdc7287e334229279dcc1248fa4
new file mode 100644
index 0000000..fd43545
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/a0/fa65f96c1e3bdc7287e334229279dcc1248fa4
@@ -0,0 +1,3 @@
+xERÍnà ޙ§ðD½M›ÔS6µ«´i©šöqL£¾ý ­ºSÀ|þ~ìtÆuðúþöR·ûúPZh›Ó~%ıþÞÀ¶9à§>|màãôû¹Û´àÑË Ï)VUAX­ÐÜ*P‰3€³\¶jÍŽ» ›}[´=yqægX$aà¦Ñù\ƒHÒL±À<˜³ù+M#Hðf+TÎ2-“†3W¬ft¶ü]Ì£÷
+a”†ÖBö=Ðâà’d ˜9:§Í]´)§§¾$MlÏ Te>™byækpéœmEW¼ëF
+’óåL…Èñ”*XF­F˜S$è8£àSE†zGë\ä{Á3§â† ÿC• d,9Ÿí‹ç6ܣ왚;ŒÔ|â•”d¼¢d9YÖ‰Èì€' •Ažw@Ó0h¥Ñ’¹AÏëËAïÚEs0.ñªºD¼²2ÐY›é‘õüɯÄ9Ï̃ \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/a1/25b9b655932711abceaf8962948e6b601d67b6 b/tests/resources/rebase/.gitted/objects/a1/25b9b655932711abceaf8962948e6b601d67b6
new file mode 100644
index 0000000..50bcee1
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/a1/25b9b655932711abceaf8962948e6b601d67b6
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/a7/00acc970eccccc73be53cd269462176544e6d1 b/tests/resources/rebase/.gitted/objects/a7/00acc970eccccc73be53cd269462176544e6d1
new file mode 100644
index 0000000..e5c62db
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/a7/00acc970eccccc73be53cd269462176544e6d1
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/a7/b066537e6be7109abfe4ff97b675d4e077da20 b/tests/resources/rebase/.gitted/objects/a7/b066537e6be7109abfe4ff97b675d4e077da20
new file mode 100644
index 0000000..54f9b66
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/a7/b066537e6be7109abfe4ff97b675d4e077da20
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/aa/4c42aecdfc7cd989bbc3209934ea7cda3f4d88 b/tests/resources/rebase/.gitted/objects/aa/4c42aecdfc7cd989bbc3209934ea7cda3f4d88
new file mode 100644
index 0000000..628c2d3
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/aa/4c42aecdfc7cd989bbc3209934ea7cda3f4d88
@@ -0,0 +1 @@
+x¥Á B1D=§Šm@Ùý=Ø l²~À¸#b÷F±o3o`^ÒZK‹~Õ›ˆdfñL“Ï6ìØ1GÏ9¦)̘1 5ôè‹68ó“ÃeÑz×ìeÐO:ÊwøµMÒz€É¡ßZë¦ÖèÍ CÞåÏsb.½ ½fˆÔ®ò2o! Fw \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/ab/25a53ef5622d443ecb0492b7516725f0deac8f b/tests/resources/rebase/.gitted/objects/ab/25a53ef5622d443ecb0492b7516725f0deac8f
new file mode 100644
index 0000000..83ef51e
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/ab/25a53ef5622d443ecb0492b7516725f0deac8f
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/ad/c97cfb874cdfb9d5ab17b54f3771dea6e02ccf b/tests/resources/rebase/.gitted/objects/ad/c97cfb874cdfb9d5ab17b54f3771dea6e02ccf
new file mode 100644
index 0000000..cc7ccd0
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/ad/c97cfb874cdfb9d5ab17b54f3771dea6e02ccf
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/ae/87cae12879a3c37d7cc994afc6395bcb0eaf99 b/tests/resources/rebase/.gitted/objects/ae/87cae12879a3c37d7cc994afc6395bcb0eaf99
new file mode 100644
index 0000000..5c8469e
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/ae/87cae12879a3c37d7cc994afc6395bcb0eaf99
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/b1/46bd7608eac53d9bf9e1a6963543588b555c64 b/tests/resources/rebase/.gitted/objects/b1/46bd7608eac53d9bf9e1a6963543588b555c64
new file mode 100644
index 0000000..3e4b975
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/b1/46bd7608eac53d9bf9e1a6963543588b555c64
@@ -0,0 +1 @@
+x¥ŽÁj1 D{öWè,¯e«B.=æÖÐÚ»‡ÃÆ!¿_7ôs˜yÔ¶mk‡@ù£ïª€Æ‚3NC±y¶ˆ–dšs.% !w“]¯ˆY©ó±Æ¯ì³i™9‘bU)bY-eÎN}i;|קì~–¶ÝÛŽ:èŸ;ë«øO‡Ò¶`ô”2#|úè½tœíú挻´ºÚZ¤¯ãAo0«šûcJT/ \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/b1/b94ec02f8ed87d0efa4c65fb38d5d6da7e8b32 b/tests/resources/rebase/.gitted/objects/b1/b94ec02f8ed87d0efa4c65fb38d5d6da7e8b32
new file mode 100644
index 0000000..d15c022
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/b1/b94ec02f8ed87d0efa4c65fb38d5d6da7e8b32
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/b6/72b141d48c369fee6c4deeb32a904387594365 b/tests/resources/rebase/.gitted/objects/b6/72b141d48c369fee6c4deeb32a904387594365
new file mode 100644
index 0000000..d8cdb71
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/b6/72b141d48c369fee6c4deeb32a904387594365
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/b7/c536a5883c8adaeb34d5e198c5a3dbbdc608b5 b/tests/resources/rebase/.gitted/objects/b7/c536a5883c8adaeb34d5e198c5a3dbbdc608b5
new file mode 100644
index 0000000..b594984
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/b7/c536a5883c8adaeb34d5e198c5a3dbbdc608b5
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/b9/f72b9158fa8c49fb4e4c10b26817ed867be803 b/tests/resources/rebase/.gitted/objects/b9/f72b9158fa8c49fb4e4c10b26817ed867be803
new file mode 100644
index 0000000..9701667
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/b9/f72b9158fa8c49fb4e4c10b26817ed867be803
@@ -0,0 +1,3 @@
+x¥O;N1 ¥žS¸Û
+d;Nf"!´ %=ÊÇaFb&«âö' {_[/Õ}ß:ò7½©‚)yÉÖ¤‚œÕ}`¬Bƒb©Ì.å2]BÓ£Ct3Gʲ$ã|QuI²j4<ŠYæÑ5Îþå‰."ÄÑ[6ÞyÇ…q&— 2ÇE攦píkmð˜?BËð¼Öý½p¯CýFgý1~Ù]ªû uÌã!Ü¢ NCãºþóÌô¤íU!¶p¤N1´7ý<Ávô
+¹mš_.¦/ÚÚdÛ \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/bc/cc8eabb5cfe2ec09959c7f4155aa73429fd604 b/tests/resources/rebase/.gitted/objects/bc/cc8eabb5cfe2ec09959c7f4155aa73429fd604
new file mode 100644
index 0000000..de778eb
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/bc/cc8eabb5cfe2ec09959c7f4155aa73429fd604
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/c4/e6cca3ec6ae0148ed231f97257df8c311e015f b/tests/resources/rebase/.gitted/objects/c4/e6cca3ec6ae0148ed231f97257df8c311e015f
new file mode 100644
index 0000000..2bbf28f
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/c4/e6cca3ec6ae0148ed231f97257df8c311e015f
@@ -0,0 +1 @@
+x%P1nÄ0 ëìWð¹CNE§N7¶è¡:*‰’p¬Ô’/¸ßWÎmI‘”ú$=^^ŸŸ._ï?¿¸~|žC¸°ã¼6©yTÈ„A¨(#1eôÌÓé´“.ˆ†áÀ(Hto@̸K-aë°Õ°…¡´²“sá1r6)&)8¸Å·TêÖa¶<0ׇ¿JÙ¢Ý[‡ŒK‡5IJ²²­ÈÀªcáÁ¸q͓쌫r_ÍÛ‡"u^@ÐÈ7~X)—›÷2¸ Ýâ G…,æ¥f¬R¸ùå`BÚúÂ4¶3£½ÁvQŸø¤›HÖ©¦Öu­êa²b SwÞcJ q…)fÆ”èæO‚ùvû×;‡í«ŒŸ \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/c5/17380440ed78865ffe3fa130b9738615c76618 b/tests/resources/rebase/.gitted/objects/c5/17380440ed78865ffe3fa130b9738615c76618
new file mode 100644
index 0000000..b9a52a3
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/c5/17380440ed78865ffe3fa130b9738615c76618
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/cb/20a10406172afd6ca3138ce36ecaf8b1269e8e b/tests/resources/rebase/.gitted/objects/cb/20a10406172afd6ca3138ce36ecaf8b1269e8e
new file mode 100644
index 0000000..acd6bdc
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/cb/20a10406172afd6ca3138ce36ecaf8b1269e8e
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/d4/82e77aecb8e07da43e4cad6e0dcb59219e12af b/tests/resources/rebase/.gitted/objects/d4/82e77aecb8e07da43e4cad6e0dcb59219e12af
new file mode 100644
index 0000000..af3bd17
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/d4/82e77aecb8e07da43e4cad6e0dcb59219e12af
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/d6/16d97082eb7bb2dc6f180a7cca940993b7a56f b/tests/resources/rebase/.gitted/objects/d6/16d97082eb7bb2dc6f180a7cca940993b7a56f
new file mode 100644
index 0000000..fa2e8d9
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/d6/16d97082eb7bb2dc6f180a7cca940993b7a56f
@@ -0,0 +1 @@
+x¥NK uÍ)æš)0-MŒqãÒ`Ð.Z ¢ÆۋƸ{ÿ¼çyª É­jßùÑJ@œD7D”Ä6ô”¼q‘byçVW.²´"³ â¬ÆVC'žCGÚ›–$)%3¦1ô¢ø^/¹À!>¹D8]ò|Ë l¥©´—¯ñc›çt©×„D°F‹¨šÚÎVùsFsœÒ¸Ní‚šá\øñRoV< \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/d6/b9ec0dfb972a6815ace42545cde5f2631cd776 b/tests/resources/rebase/.gitted/objects/d6/b9ec0dfb972a6815ace42545cde5f2631cd776
new file mode 100644
index 0000000..1239704
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/d6/b9ec0dfb972a6815ace42545cde5f2631cd776
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/d9/c5185186d95d233dc007c1927cb3bdd6cde35b b/tests/resources/rebase/.gitted/objects/d9/c5185186d95d233dc007c1927cb3bdd6cde35b
new file mode 100644
index 0000000..3c340db
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/d9/c5185186d95d233dc007c1927cb3bdd6cde35b
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/da/82b3a60c50cf5ac524ec3000d743447329465d b/tests/resources/rebase/.gitted/objects/da/82b3a60c50cf5ac524ec3000d743447329465d
new file mode 100644
index 0000000..352a13a
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/da/82b3a60c50cf5ac524ec3000d743447329465d
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/da/9c51a23d02d931a486f45ad18cda05cf5d2b94 b/tests/resources/rebase/.gitted/objects/da/9c51a23d02d931a486f45ad18cda05cf5d2b94
new file mode 100644
index 0000000..85b78ee
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/da/9c51a23d02d931a486f45ad18cda05cf5d2b94
@@ -0,0 +1,2 @@
+x¥Ž;!†­9Å\@3ÀÂ@bŒ¥à1·X1+Æë‹ÆØýäËWڲ̌£M_™! 5Õ[KÆûœ
+c´lÊ‘*IÈ£CuO+ß:P¡,Áq¤ ºêPÐy/„¦š’‰"–P'QéÙ¯m…S}¥µÂåÚ–G»ÁžÇúIGþ¿¶+m9€žÌhò¶8!ª±ÙÎbÔ¹ÕYæ’ú<4ô™YÔõ¯S• \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/dc/12ac1e10f2be70e8ecd52132a08da98a309c3a b/tests/resources/rebase/.gitted/objects/dc/12ac1e10f2be70e8ecd52132a08da98a309c3a
new file mode 100644
index 0000000..9907248
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/dc/12ac1e10f2be70e8ecd52132a08da98a309c3a
@@ -0,0 +1 @@
+x¥ŽMJ1…]çÅì•ÊϤ¦Ad@\ºóUIÅCw†LD¼½Q¼»÷Þ/µm«…»ÑUAI1eq‹U<ùhKfO‘¼gë$Ø#2/æÊ]÷š ‰/%«.¤ˆÙå(–-.Ÿâä ŒµuxÉŸÜ3¼­m»µu®?鬿à¯=¤¶= x$ŒD Üc@4s²CÿycžWíý ®5]êþ‡×–k©‰GJFQ-ó ºZy \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/df/d3d25264693fcd7348ad286f3c34f3f6b30918 b/tests/resources/rebase/.gitted/objects/df/d3d25264693fcd7348ad286f3c34f3f6b30918
new file mode 100644
index 0000000..3de3fda
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/df/d3d25264693fcd7348ad286f3c34f3f6b30918
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/e4/f809f826c1a9fc929874bc0e4644dd2f2a1af4 b/tests/resources/rebase/.gitted/objects/e4/f809f826c1a9fc929874bc0e4644dd2f2a1af4
new file mode 100644
index 0000000..e475019
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/e4/f809f826c1a9fc929874bc0e4644dd2f2a1af4
@@ -0,0 +1,3 @@
+x¥ŽMŠ1„]çïùym'0ˆ—î¼@%yÁÛ 1"ÞÞ8Ì fWõ_ªëºt²SØô&Beç
+Ü,ÖO15æȾ`ò ÖÁxÃpÈP?hrëpb I¹¤9åàCŒÉY‚cÁ p…³÷
+~©Žù‰–é|©ë½Þè[ý¤ƒüí+ÕuO†õ´³–YÓV³ÖjÐ!ÛåŸ7êTóR–„¾ C½RD»ÊK½ÿXV \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/e5/2ff405da5b7e1e9b0929939fa8405d81fe8a45 b/tests/resources/rebase/.gitted/objects/e5/2ff405da5b7e1e9b0929939fa8405d81fe8a45
new file mode 100644
index 0000000..30c1987
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/e5/2ff405da5b7e1e9b0929939fa8405d81fe8a45
@@ -0,0 +1,3 @@
+x¥Ž;j1†]ës˜‘V“&eºôaV3ƒUìÊ(
+¹~d“¤ûðñ•¶ïu€‹î4ºZŠ"KF×Å/)&—ƒc!,A²ÓdîÔ倨vË„Ù¯”8°õƒlqAѺm«ª'CßãÖ:¼ñu†[Û¿Ú™ë#½ÊóøkçÒö+؉ŠÎ…äá=¢™ë”òOŒyo\µu*Xh
+Ü«ðç]ÈüxV¢ \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/e7/bb00c4eab291e08361fda376733a12b4150aa9 b/tests/resources/rebase/.gitted/objects/e7/bb00c4eab291e08361fda376733a12b4150aa9
new file mode 100644
index 0000000..da96e9d
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/e7/bb00c4eab291e08361fda376733a12b4150aa9
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/e8/8cc0a6919a74599ce8e1dcb81eb2bbae33a645 b/tests/resources/rebase/.gitted/objects/e8/8cc0a6919a74599ce8e1dcb81eb2bbae33a645
new file mode 100644
index 0000000..e8ce172
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/e8/8cc0a6919a74599ce8e1dcb81eb2bbae33a645
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/e9/5f47e016dcc70b0b888df8e40e97b8aabafd4c b/tests/resources/rebase/.gitted/objects/e9/5f47e016dcc70b0b888df8e40e97b8aabafd4c
new file mode 100644
index 0000000..cc3312a
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/e9/5f47e016dcc70b0b888df8e40e97b8aabafd4c
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/e9/f22c10ffb378446c0bbcab7ee3d9d5a0040672 b/tests/resources/rebase/.gitted/objects/e9/f22c10ffb378446c0bbcab7ee3d9d5a0040672
new file mode 100644
index 0000000..3446b2f
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/e9/f22c10ffb378446c0bbcab7ee3d9d5a0040672
@@ -0,0 +1,2 @@
+xMÍjÃ0„{ÖS̘\z
+9µ`Ú‚iB~=Êö:•µª´ª“·ïÚèiav˜ývZÏ-ž·Û§×—cSá´¿6Æ.gì?q~?Ö5Þ>šæÐÚäé^AÆD„Ÿb“äEŸ­Pª0Ð ŽCFW%V&»:›«1wÉFêaC¿î]Fï:ªTiœ_2ƒ8¹/©RRpqÍ2y²Þïвóp‚+ñwÈ̹¤¼ Q@Ô«.`P œ0¸_Bäú•R-&Ùî{YZ怩ˆpP€>{¥Y½ži´S…ÙɈH1RZɳõ²3ìyþÏáúpRÒûJ[âgž¬p†M¤1†ny¯_Àö½í Z†ŒNkbõ›?ŒÂÊ \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/ec/725f5639730640f91cd0be5f2d6d7ac5d69c79 b/tests/resources/rebase/.gitted/objects/ec/725f5639730640f91cd0be5f2d6d7ac5d69c79
new file mode 100644
index 0000000..0fb3334
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/ec/725f5639730640f91cd0be5f2d6d7ac5d69c79
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/ed/f7b3ffde1624c60d2d6b1a2bb792d86de172e0 b/tests/resources/rebase/.gitted/objects/ed/f7b3ffde1624c60d2d6b1a2bb792d86de172e0
new file mode 100644
index 0000000..e2e98d6
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/ed/f7b3ffde1624c60d2d6b1a2bb792d86de172e0
@@ -0,0 +1,3 @@
+x¥OKN!uÍ)êh¡
+ctá f?ú8½èfÒb¼¾8™¸{Ÿä}¸oÛ:`Iø0U¨Â…ØZ¦Èb­Hª-PKÑž‰‚hEõ ³¹k=t@LÍrÒBÙ‚„Ì>!ùEnDˆ­p–h®~K?àC~ê!pºôí«ïð¢SýCoz3îì‰ûö
+!úD± <úè½›ê;ôŸ1î]dë¬ïŸóõ~¾ju¿Z’U„ \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/ee/23c5eeedadf8595c0ff60a366d970a165e373d b/tests/resources/rebase/.gitted/objects/ee/23c5eeedadf8595c0ff60a366d970a165e373d
new file mode 100644
index 0000000..b32600f
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/ee/23c5eeedadf8595c0ff60a366d970a165e373d
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/ee/f0edde5daa94da5f297d4ddb5dfbc1980f0902 b/tests/resources/rebase/.gitted/objects/ee/f0edde5daa94da5f297d4ddb5dfbc1980f0902
new file mode 100644
index 0000000..e9b3f58
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/ee/f0edde5daa94da5f297d4ddb5dfbc1980f0902
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/ef/ad0b11c47cb2f0220cbd6f5b0f93bb99064b00 b/tests/resources/rebase/.gitted/objects/ef/ad0b11c47cb2f0220cbd6f5b0f93bb99064b00
new file mode 100644
index 0000000..285e140
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/ef/ad0b11c47cb2f0220cbd6f5b0f93bb99064b00
@@ -0,0 +1 @@
+x¥=nÃ0 …3ë¼@ Š¢dŠ.»å¬D&l¶‚^¿jÐt{?ÀÃ÷J[×¥%:õ]r¤˜•)I`_ŠÁ¥L–ªÏ–Ù´’|…ìî²ëÖaŒ3¢F1¤H"ÂxfoÈXb%ÓÉ&'~k;|ÔoÙ+\nm=Úgé¯z×gñç^K[ßÀ3ÆD>Í ^ƺ‘Ø®ÿœqŸ­.¶éË@ˆÐÈ1þÈõq¸æ©V \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/f5/56d5fef35003561dc0b64b37057d7541239105 b/tests/resources/rebase/.gitted/objects/f5/56d5fef35003561dc0b64b37057d7541239105
new file mode 100644
index 0000000..f4143e1
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/f5/56d5fef35003561dc0b64b37057d7541239105
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/f6/3fa37e285bd11b0a7b48fa584a4091814a3ada b/tests/resources/rebase/.gitted/objects/f6/3fa37e285bd11b0a7b48fa584a4091814a3ada
new file mode 100644
index 0000000..e650383
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/f6/3fa37e285bd11b0a7b48fa584a4091814a3ada
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/f7/5c193a1df47186727179f24867bc4d27a8991f b/tests/resources/rebase/.gitted/objects/f7/5c193a1df47186727179f24867bc4d27a8991f
new file mode 100644
index 0000000..618cb68
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/f7/5c193a1df47186727179f24867bc4d27a8991f
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/f8/7d14a4a236582a0278a916340a793714256864 b/tests/resources/rebase/.gitted/objects/f8/7d14a4a236582a0278a916340a793714256864
new file mode 100644
index 0000000..1d29712
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/f8/7d14a4a236582a0278a916340a793714256864
@@ -0,0 +1,2 @@
+x¥ŽKŠAD]×)òUÙõqãÒÈÎ6ØÖЖz}Ûan0»ˆ<nó<uÀT6}Q…Õ4$1&+)E.X¥–h>N1‘Kà~hÑ[¯†Š#ŽU²«b–4cP¯LƼij£G¿´Žò¢Eà|ió½Ýà[WúIýþÚ·y!ú”‡œaë£÷n¥«l×Þ¸S“É&¦>­
+ôO¥«{äƒTÈ \ No newline at end of file
diff --git a/tests/resources/rebase/.gitted/objects/fc/e0584b379f535e50e036db587db71884ea6b36 b/tests/resources/rebase/.gitted/objects/fc/e0584b379f535e50e036db587db71884ea6b36
new file mode 100644
index 0000000..ce8b2fb
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/fc/e0584b379f535e50e036db587db71884ea6b36
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/ff/b36e513f5fdf8a6ba850a20142676a2ac4807d b/tests/resources/rebase/.gitted/objects/ff/b36e513f5fdf8a6ba850a20142676a2ac4807d
new file mode 100644
index 0000000..f655d12
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/ff/b36e513f5fdf8a6ba850a20142676a2ac4807d
Binary files differ
diff --git a/tests/resources/rebase/.gitted/objects/ff/dfa89389040a87008c4ab1834120d3046daaea b/tests/resources/rebase/.gitted/objects/ff/dfa89389040a87008c4ab1834120d3046daaea
new file mode 100644
index 0000000..54c938e
--- /dev/null
+++ b/tests/resources/rebase/.gitted/objects/ff/dfa89389040a87008c4ab1834120d3046daaea
Binary files differ
diff --git a/tests/resources/rebase/.gitted/refs/heads/asparagus b/tests/resources/rebase/.gitted/refs/heads/asparagus
new file mode 100644
index 0000000..a3c9d67
--- /dev/null
+++ b/tests/resources/rebase/.gitted/refs/heads/asparagus
@@ -0,0 +1 @@
+4b21eb6eeeec7f8fc89a1d334faff9bd5f5f8c34
diff --git a/tests/resources/rebase/.gitted/refs/heads/barley b/tests/resources/rebase/.gitted/refs/heads/barley
new file mode 100644
index 0000000..feab944
--- /dev/null
+++ b/tests/resources/rebase/.gitted/refs/heads/barley
@@ -0,0 +1 @@
+12c084412b952396962eb420716df01022b847cc
diff --git a/tests/resources/rebase/.gitted/refs/heads/beef b/tests/resources/rebase/.gitted/refs/heads/beef
new file mode 100644
index 0000000..1c69e6a
--- /dev/null
+++ b/tests/resources/rebase/.gitted/refs/heads/beef
@@ -0,0 +1 @@
+b146bd7608eac53d9bf9e1a6963543588b555c64
diff --git a/tests/resources/rebase/.gitted/refs/heads/deep_gravy b/tests/resources/rebase/.gitted/refs/heads/deep_gravy
new file mode 100644
index 0000000..efbe5f0
--- /dev/null
+++ b/tests/resources/rebase/.gitted/refs/heads/deep_gravy
@@ -0,0 +1 @@
+d9c5185186d95d233dc007c1927cb3bdd6cde35b
diff --git a/tests/resources/rebase/.gitted/refs/heads/dried_pea b/tests/resources/rebase/.gitted/refs/heads/dried_pea
new file mode 100644
index 0000000..9ede602
--- /dev/null
+++ b/tests/resources/rebase/.gitted/refs/heads/dried_pea
@@ -0,0 +1 @@
+7f37fe2d7320360f8a9118b1ed8fba6f38481679
diff --git a/tests/resources/rebase/.gitted/refs/heads/gravy b/tests/resources/rebase/.gitted/refs/heads/gravy
new file mode 100644
index 0000000..3753b73
--- /dev/null
+++ b/tests/resources/rebase/.gitted/refs/heads/gravy
@@ -0,0 +1 @@
+d616d97082eb7bb2dc6f180a7cca940993b7a56f
diff --git a/tests/resources/rebase/.gitted/refs/heads/green_pea b/tests/resources/rebase/.gitted/refs/heads/green_pea
new file mode 100644
index 0000000..3bffe27
--- /dev/null
+++ b/tests/resources/rebase/.gitted/refs/heads/green_pea
@@ -0,0 +1 @@
+d482e77aecb8e07da43e4cad6e0dcb59219e12af
diff --git a/tests/resources/rebase/.gitted/refs/heads/master b/tests/resources/rebase/.gitted/refs/heads/master
new file mode 100644
index 0000000..abbe9cc
--- /dev/null
+++ b/tests/resources/rebase/.gitted/refs/heads/master
@@ -0,0 +1 @@
+efad0b11c47cb2f0220cbd6f5b0f93bb99064b00
diff --git a/tests/resources/rebase/.gitted/refs/heads/veal b/tests/resources/rebase/.gitted/refs/heads/veal
new file mode 100644
index 0000000..484f489
--- /dev/null
+++ b/tests/resources/rebase/.gitted/refs/heads/veal
@@ -0,0 +1 @@
+f87d14a4a236582a0278a916340a793714256864
diff --git a/tests/resources/rebase/asparagus.txt b/tests/resources/rebase/asparagus.txt
new file mode 100644
index 0000000..67ed7af
--- /dev/null
+++ b/tests/resources/rebase/asparagus.txt
@@ -0,0 +1,10 @@
+ASPARAGUS SOUP.
+
+TAKE FOUR LARGE BUNCHES of asparagus, scrape it nicely, cut off one inch
+OF THE TOPS, and lay them in water, chop the stalks and put them on the
+FIRE WITH A PIECE OF BACON, a large onion cut up, and pepper and salt;
+ADD TWO QUARTS OF WATER, boil them till the stalks are quite soft, then
+PULP THEM THROUGH A SIEVE, and strain the water to it, which must be put
+back in the pot; put into it a chicken cut up, with the tops of
+asparagus which had been laid by, boil it until these last articles are
+sufficiently done, thicken with flour, butter and milk, and serve it up.
diff --git a/tests/resources/rebase/beef.txt b/tests/resources/rebase/beef.txt
new file mode 100644
index 0000000..68f6182
--- /dev/null
+++ b/tests/resources/rebase/beef.txt
@@ -0,0 +1,22 @@
+BEEF SOUP.
+
+Take the hind shin of beef, cut off all the flesh off the leg-bone,
+which must be taken away entirely, or the soup will be greasy. Wash the
+meat clean and lay it in a pot, sprinkle over it one small
+table-spoonful of pounded black pepper, and two of salt; three onions
+the size of a hen's egg, cut small, six small carrots scraped and cut
+up, two small turnips pared and cut into dice; pour on three quarts of
+water, cover the pot close, and keep it gently and steadily boiling five
+hours, which will leave about three pints of clear soup; do not let the
+pot boil over, but take off the scum carefully, as it rises. When it has
+boiled four hours, put in a small bundle of thyme and parsley, and a
+pint of celery cut small, or a tea-spoonful of celery seed pounded.
+These latter ingredients would lose their delicate flavour if boiled too
+much. Just before you take it up, brown it in the following manner: put
+a small table-spoonful of nice brown sugar into an iron skillet, set it
+on the fire and stir it till it melts and looks very dark, pour into it
+a ladle full of the soup, a little at a time; stirring it all the while.
+Strain this browning and mix it well with the soup; take out the bundle
+of thyme and parsley, put the nicest pieces of meat in your tureen, and
+pour on the soup and vegetables; put in some toasted bread cut in dice,
+and serve it up.
diff --git a/tests/resources/rebase/bouilli.txt b/tests/resources/rebase/bouilli.txt
new file mode 100644
index 0000000..4b7c565
--- /dev/null
+++ b/tests/resources/rebase/bouilli.txt
@@ -0,0 +1,18 @@
+SOUP WITH BOUILLI.
+
+Take the nicest part of the thick brisket of beef, about eight pounds,
+put it into a pot with every thing directed for the other soup; make it
+exactly in the same way, only put it on an hour sooner, that you may
+have time to prepare the bouilli; after it has boiled five hours, take
+out the beef, cover up the soup and set it near the fire that it may
+keep hot. Take the skin off the beef, have the yelk of an egg well
+beaten, dip a feather in it and wash the top of your beef, sprinkle over
+it the crumb of stale bread finely grated, put it in a Dutch oven
+previously heated, put the top on with coals enough to brown, but not
+burn the beef; let it stand nearly an hour, and prepare your gravy
+thus:--Take a sufficient quantity of soup and the vegetables boiled in
+it; add to it a table-spoonful of red wine, and two of mushroom catsup,
+thicken with a little bit of butter and a little brown flour; make it
+very hot, pour it in your dish, and put the beef on it. Garnish it with
+green pickle, cut in thin slices, serve up the soup in a tureen with
+bits of toasted bread.
diff --git a/tests/resources/rebase/gravy.txt b/tests/resources/rebase/gravy.txt
new file mode 100644
index 0000000..c4e6cca
--- /dev/null
+++ b/tests/resources/rebase/gravy.txt
@@ -0,0 +1,8 @@
+GRAVY SOUP.
+
+Get eight pounds of coarse lean beef--wash it clean and lay it in your
+pot, put in the same ingredients as for the shin soup, with the same
+quantity of water, and follow the process directed for that. Strain the
+soup through a sieve, and serve it up clear, with nothing more than
+toasted bread in it; two table-spoonsful of mushroom catsup will add a
+fine flavour to the soup.
diff --git a/tests/resources/rebase/oyster.txt b/tests/resources/rebase/oyster.txt
new file mode 100644
index 0000000..68af1fc
--- /dev/null
+++ b/tests/resources/rebase/oyster.txt
@@ -0,0 +1,13 @@
+OYSTER SOUP.
+
+Wash and drain two quarts of oysters, put them on with three quarts of
+water, three onions chopped up, two or three slices of lean ham, pepper
+and salt; boil it till reduced one-half, strain it through a sieve,
+return the liquid into the pot, put in one quart of fresh oysters, boil
+it till they are sufficiently done, and thicken the soup with four
+spoonsful of flour, two gills of rich cream, and the yelks of six new
+laid eggs beaten well; boil it a few minutes after the thickening is put
+in. Take care that it does not curdle, and that the flour is not in
+lumps; serve it up with the last oysters that were put in. If the
+flavour of thyme be agreeable, you may put in a little, but take care
+that it does not boil in it long enough to discolour the soup.
diff --git a/tests/resources/rebase/veal.txt b/tests/resources/rebase/veal.txt
new file mode 100644
index 0000000..a7b0665
--- /dev/null
+++ b/tests/resources/rebase/veal.txt
@@ -0,0 +1,18 @@
+VEAL SOUP.
+
+Put into a pot three quarts of water, three onions cut small, one
+spoonful of black pepper pounded, and two of salt, with two or three
+slices of lean ham; let it boil steadily two hours; skim it
+occasionally, then put into it a shin of veal, let it boil two hours
+longer; take out the slices of ham, and skim off the grease if any
+should rise, take a gill of good cream, mix with it two table-spoonsful
+of flour very nicely, and the yelks of two eggs beaten well, strain this
+mixture, and add some chopped parsley; pour some soup on by degrees,
+stir it well, and pour it into the pot, continuing to stir until it has
+boiled two or three minutes to take off the raw taste of the eggs. If
+the cream be not perfectly sweet, and the eggs quite new, the thickening
+will curdle in the soup. For a change you may put a dozen ripe tomatos
+in, first taking off their skins, by letting them stand a few minutes in
+hot water, when they may be easily peeled. When made in this way you
+must thicken it with the flour only. Any part of the veal may be used,
+but the shin or knuckle is the nicest.
diff --git a/tests/resources/redundant.git/HEAD b/tests/resources/redundant.git/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/redundant.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/redundant.git/config b/tests/resources/redundant.git/config
new file mode 100755
index 0000000..2f89580
--- /dev/null
+++ b/tests/resources/redundant.git/config
@@ -0,0 +1,5 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = true
+ logallrefupdates = true
diff --git a/tests/resources/redundant.git/objects/info/packs b/tests/resources/redundant.git/objects/info/packs
new file mode 100644
index 0000000..fbf960f
--- /dev/null
+++ b/tests/resources/redundant.git/objects/info/packs
@@ -0,0 +1,2 @@
+P pack-3d944c0c5bcb6b16209af847052c6ff1a521529d.pack
+
diff --git a/tests/resources/redundant.git/objects/pack/pack-3d944c0c5bcb6b16209af847052c6ff1a521529d.idx b/tests/resources/redundant.git/objects/pack/pack-3d944c0c5bcb6b16209af847052c6ff1a521529d.idx
new file mode 100644
index 0000000..d8e099a
--- /dev/null
+++ b/tests/resources/redundant.git/objects/pack/pack-3d944c0c5bcb6b16209af847052c6ff1a521529d.idx
Binary files differ
diff --git a/tests/resources/redundant.git/objects/pack/pack-3d944c0c5bcb6b16209af847052c6ff1a521529d.pack b/tests/resources/redundant.git/objects/pack/pack-3d944c0c5bcb6b16209af847052c6ff1a521529d.pack
new file mode 100644
index 0000000..02ea49f
--- /dev/null
+++ b/tests/resources/redundant.git/objects/pack/pack-3d944c0c5bcb6b16209af847052c6ff1a521529d.pack
Binary files differ
diff --git a/tests/resources/redundant.git/packed-refs b/tests/resources/redundant.git/packed-refs
new file mode 100644
index 0000000..e8bf04d
--- /dev/null
+++ b/tests/resources/redundant.git/packed-refs
@@ -0,0 +1,3 @@
+# pack-refs with: peeled fully-peeled
+e18fa2788e9c4e12d83150808a31dfbfb1ae364f refs/heads/master
+91f4b95df4a59504a9813ba66912562931d990e3 refs/heads/ref2/ref28
diff --git a/tests/resources/redundant.git/refs/.gitkeep b/tests/resources/redundant.git/refs/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/redundant.git/refs/.gitkeep
diff --git a/tests/resources/renames/.gitted/HEAD b/tests/resources/renames/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/renames/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/renames/.gitted/ORIG_HEAD b/tests/resources/renames/.gitted/ORIG_HEAD
new file mode 100644
index 0000000..642c319
--- /dev/null
+++ b/tests/resources/renames/.gitted/ORIG_HEAD
@@ -0,0 +1 @@
+19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13
diff --git a/tests/resources/renames/.gitted/config b/tests/resources/renames/.gitted/config
new file mode 100644
index 0000000..bb4d11c
--- /dev/null
+++ b/tests/resources/renames/.gitted/config
@@ -0,0 +1,7 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
+ precomposeunicode = false
diff --git a/tests/resources/renames/.gitted/description b/tests/resources/renames/.gitted/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/renames/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/renames/.gitted/index b/tests/resources/renames/.gitted/index
new file mode 100644
index 0000000..5882a8d
--- /dev/null
+++ b/tests/resources/renames/.gitted/index
Binary files differ
diff --git a/tests/resources/renames/.gitted/info/exclude b/tests/resources/renames/.gitted/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/renames/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/renames/.gitted/logs/HEAD b/tests/resources/renames/.gitted/logs/HEAD
new file mode 100644
index 0000000..e6da678
--- /dev/null
+++ b/tests/resources/renames/.gitted/logs/HEAD
@@ -0,0 +1,9 @@
+0000000000000000000000000000000000000000 31e47d8c1fa36d7f8d537b96158e3f024de0a9f2 Russell Belfer <rb@github.com> 1351024687 -0700 commit (initial): Initial commit
+31e47d8c1fa36d7f8d537b96158e3f024de0a9f2 2bc7f351d20b53f1c72c16c4b036e491c478c49a Russell Belfer <rb@github.com> 1351024817 -0700 commit: copy and rename with no change
+2bc7f351d20b53f1c72c16c4b036e491c478c49a 1c068dee5790ef1580cfc4cd670915b48d790084 Russell Belfer <rb@github.com> 1361485758 -0800 commit: rewrites, copies with changes, etc.
+1c068dee5790ef1580cfc4cd670915b48d790084 19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13 Russell Belfer <rb@github.com> 1361486360 -0800 commit: more renames and smallish modifications
+19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13 31e47d8c1fa36d7f8d537b96158e3f024de0a9f2 Kartikaya Gupta <kats@pancake.staktrace.com> 1618486966 -0400 checkout: moving from master to break_rewrite
+31e47d8c1fa36d7f8d537b96158e3f024de0a9f2 db98035f715427eef1f5e17f03e1801c05301e9e Kartikaya Gupta <kats@pancake.staktrace.com> 1618487039 -0400 commit: This test needs to start with a minimum of four files
+db98035f715427eef1f5e17f03e1801c05301e9e 7e7bfb88ba9bc65fd700fee1819cf1c317aafa56 Kartikaya Gupta <kats@pancake.staktrace.com> 1618487097 -0400 commit: Copy/modify files around
+7e7bfb88ba9bc65fd700fee1819cf1c317aafa56 19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13 Kartikaya Gupta <kats@pancake.staktrace.com> 1618487105 -0400 checkout: moving from break_rewrite to master
+19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13 19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13 Kartikaya Gupta <kats@pancake.staktrace.com> 1618487271 -0400 reset: moving to HEAD
diff --git a/tests/resources/renames/.gitted/logs/refs/heads/master b/tests/resources/renames/.gitted/logs/refs/heads/master
new file mode 100644
index 0000000..e697922
--- /dev/null
+++ b/tests/resources/renames/.gitted/logs/refs/heads/master
@@ -0,0 +1,4 @@
+0000000000000000000000000000000000000000 31e47d8c1fa36d7f8d537b96158e3f024de0a9f2 Russell Belfer <rb@github.com> 1351024687 -0700 commit (initial): Initial commit
+31e47d8c1fa36d7f8d537b96158e3f024de0a9f2 2bc7f351d20b53f1c72c16c4b036e491c478c49a Russell Belfer <rb@github.com> 1351024817 -0700 commit: copy and rename with no change
+2bc7f351d20b53f1c72c16c4b036e491c478c49a 1c068dee5790ef1580cfc4cd670915b48d790084 Russell Belfer <rb@github.com> 1361485758 -0800 commit: rewrites, copies with changes, etc.
+1c068dee5790ef1580cfc4cd670915b48d790084 19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13 Russell Belfer <rb@github.com> 1361486360 -0800 commit: more renames and smallish modifications
diff --git a/tests/resources/renames/.gitted/objects/03/da7ad872536bd448da8d88eb7165338bf923a7 b/tests/resources/renames/.gitted/objects/03/da7ad872536bd448da8d88eb7165338bf923a7
new file mode 100644
index 0000000..2ee8644
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/03/da7ad872536bd448da8d88eb7165338bf923a7
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/17/58bdd7c16a72ff7c17d8de0c957ced3ccad645 b/tests/resources/renames/.gitted/objects/17/58bdd7c16a72ff7c17d8de0c957ced3ccad645
new file mode 100644
index 0000000..01801ed
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/17/58bdd7c16a72ff7c17d8de0c957ced3ccad645
@@ -0,0 +1,5 @@
+xEͱ Â@QbWq ÿ®—ÃÅHôŸ¡_ž‰&{ëó]ãðy›û¸•¶Y¬X³û`Ÿì‹=¯Ý'í¶=Zo´Þh½Ñz£õFëÖí¿­­Ð
+­Ð
+­Ð
+­Ð
+MhBšÐ„&4¡ MhB3šÑŒf4£ÍhF3šÑŽKûxŒŒ \ No newline at end of file
diff --git a/tests/resources/renames/.gitted/objects/19/dd32dfb1520a64e5bbaae8dce6ef423dfa2f13 b/tests/resources/renames/.gitted/objects/19/dd32dfb1520a64e5bbaae8dce6ef423dfa2f13
new file mode 100644
index 0000000..4be4c69
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/19/dd32dfb1520a64e5bbaae8dce6ef423dfa2f13
@@ -0,0 +1 @@
+x•ŽÑM!Eý¦Ši@3¼Þc,Á\X °ýK þÞ{Nrbo­,xzYC¬<‡h[&“È?=fcvÎyƒèC£W¿<äZ #:J"vs’µ%Œ9š˜Ü½¶ÁPÚ’Q|¯³ø¾ç”ZáKj–ï#|þ”uÞá-ööúpÚ;Â+¢Úëî[ý¯©Z;’›Là+Ál\k™'´žJ.‘Wé×T¯;O
diff --git a/tests/resources/renames/.gitted/objects/1c/068dee5790ef1580cfc4cd670915b48d790084 b/tests/resources/renames/.gitted/objects/1c/068dee5790ef1580cfc4cd670915b48d790084
new file mode 100644
index 0000000..d65ab0a
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/1c/068dee5790ef1580cfc4cd670915b48d790084
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/2b/c7f351d20b53f1c72c16c4b036e491c478c49a b/tests/resources/renames/.gitted/objects/2b/c7f351d20b53f1c72c16c4b036e491c478c49a
new file mode 100644
index 0000000..93f1ccb
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/2b/c7f351d20b53f1c72c16c4b036e491c478c49a
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/2c/136d294960f7d939f1ed1903f1ced78b874c87 b/tests/resources/renames/.gitted/objects/2c/136d294960f7d939f1ed1903f1ced78b874c87
new file mode 100644
index 0000000..51b7cea
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/2c/136d294960f7d939f1ed1903f1ced78b874c87
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/31/e47d8c1fa36d7f8d537b96158e3f024de0a9f2 b/tests/resources/renames/.gitted/objects/31/e47d8c1fa36d7f8d537b96158e3f024de0a9f2
new file mode 100644
index 0000000..00ce532
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/31/e47d8c1fa36d7f8d537b96158e3f024de0a9f2
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/35/92953ff3ea5e8ba700c429f3aefe33c8806754 b/tests/resources/renames/.gitted/objects/35/92953ff3ea5e8ba700c429f3aefe33c8806754
new file mode 100644
index 0000000..886271d
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/35/92953ff3ea5e8ba700c429f3aefe33c8806754
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/36/020db6cdacaa93497f31edcd8f242ff9bc366d b/tests/resources/renames/.gitted/objects/36/020db6cdacaa93497f31edcd8f242ff9bc366d
new file mode 100644
index 0000000..f4f9303
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/36/020db6cdacaa93497f31edcd8f242ff9bc366d
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/3c/04741dd4b96c4ae4b00ec0f6e10c816a30aad2 b/tests/resources/renames/.gitted/objects/3c/04741dd4b96c4ae4b00ec0f6e10c816a30aad2
new file mode 100644
index 0000000..c236022
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/3c/04741dd4b96c4ae4b00ec0f6e10c816a30aad2
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/41/2a2eaf2c13103ea976b3b02c17abee7a358432 b/tests/resources/renames/.gitted/objects/41/2a2eaf2c13103ea976b3b02c17abee7a358432
new file mode 100644
index 0000000..d0d3334
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/41/2a2eaf2c13103ea976b3b02c17abee7a358432
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/41/a766bb079e18ff6a24571831bd751168799a02 b/tests/resources/renames/.gitted/objects/41/a766bb079e18ff6a24571831bd751168799a02
new file mode 100644
index 0000000..4bd7592
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/41/a766bb079e18ff6a24571831bd751168799a02
@@ -0,0 +1 @@
+x­ŽKNÄ0DYû½GDþ#!Ä€C¸ÓÝK‰=ò8pz‚¸‹ZÔ[Ô«¥í{`g÷4:3`N¨µŸ?ƒÆÎè…E‚Á ”Bp˜8™Õ=w®ÌLä, š`uŽžbÎœháÈâ­#ÉVŒSù·Öᣬopí½}Ãë%è˜Üsþmïǃûcª­ó}ûšÖ2nNKÛßÀDoô%xáE'­ÕIÏ׃ÿkO]‰J]AÊÆ0 ñƃ Z…ÊŸþ”êƆ_[ \ No newline at end of file
diff --git a/tests/resources/renames/.gitted/objects/42/10ffd5c390b21dd5483375e75288dea9ede512 b/tests/resources/renames/.gitted/objects/42/10ffd5c390b21dd5483375e75288dea9ede512
new file mode 100644
index 0000000..d351a6d
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/42/10ffd5c390b21dd5483375e75288dea9ede512
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/44/4a76ed3e45b183753f49376af30da8c3fe276a b/tests/resources/renames/.gitted/objects/44/4a76ed3e45b183753f49376af30da8c3fe276a
new file mode 100644
index 0000000..5ce12a3
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/44/4a76ed3e45b183753f49376af30da8c3fe276a
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/47/184c1e7eb22abcbed2bf4ee87d4e38096f7951 b/tests/resources/renames/.gitted/objects/47/184c1e7eb22abcbed2bf4ee87d4e38096f7951
new file mode 100644
index 0000000..aa91926
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/47/184c1e7eb22abcbed2bf4ee87d4e38096f7951
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/4e/4cae3e7dd56ed74bff39526d0469e554432953 b/tests/resources/renames/.gitted/objects/4e/4cae3e7dd56ed74bff39526d0469e554432953
new file mode 100644
index 0000000..5e6ebd5
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/4e/4cae3e7dd56ed74bff39526d0469e554432953
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/50/e90273af7d826ff0a95865bcd3ba8412c447d9 b/tests/resources/renames/.gitted/objects/50/e90273af7d826ff0a95865bcd3ba8412c447d9
new file mode 100644
index 0000000..a98d14e
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/50/e90273af7d826ff0a95865bcd3ba8412c447d9
@@ -0,0 +1,3 @@
+xmÍM
+Â0`×9Å\@ÉOÛd ˆîE¼@š&4hLI"âíî]Ícà}Ïå”bÉÕ®ïaBÙORvΡ5çÂ"¢
+“óbÀ0[kLo³ï¶ä·èpÍ­U˺ÝSŠ®äšC;¸œŽ ”æ8 ÜhØóŽsF_šl¾ÀeþØ2Ã}ɩ挞È-ý!Dg4*IDO;Ûê!Ð~)>m‰í ä®”¨Ï~*ùD‰ \ No newline at end of file
diff --git a/tests/resources/renames/.gitted/objects/5a/71babaaac78a758b52576a60cea3c218c8b546 b/tests/resources/renames/.gitted/objects/5a/71babaaac78a758b52576a60cea3c218c8b546
new file mode 100644
index 0000000..e685bfa
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/5a/71babaaac78a758b52576a60cea3c218c8b546
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/5e/26abc56a5a84d89790f45416648899cbe13109 b/tests/resources/renames/.gitted/objects/5e/26abc56a5a84d89790f45416648899cbe13109
new file mode 100644
index 0000000..2acd3d5
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/5e/26abc56a5a84d89790f45416648899cbe13109
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/61/8c6f2f8740bd6049b2fb9eb93fc15726462745 b/tests/resources/renames/.gitted/objects/61/8c6f2f8740bd6049b2fb9eb93fc15726462745
new file mode 100644
index 0000000..24eac54
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/61/8c6f2f8740bd6049b2fb9eb93fc15726462745
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/66/311f5cfbe7836c27510a3ba2f43e282e2c8bba b/tests/resources/renames/.gitted/objects/66/311f5cfbe7836c27510a3ba2f43e282e2c8bba
new file mode 100644
index 0000000..5ee28a7
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/66/311f5cfbe7836c27510a3ba2f43e282e2c8bba
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/7e/7bfb88ba9bc65fd700fee1819cf1c317aafa56 b/tests/resources/renames/.gitted/objects/7e/7bfb88ba9bc65fd700fee1819cf1c317aafa56
new file mode 100644
index 0000000..e53c843
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/7e/7bfb88ba9bc65fd700fee1819cf1c317aafa56
@@ -0,0 +1,2 @@
+x¥ŽKn„0³ö)ú™éö)Š"e1‹9EcÚ
+bÀÈ4 nî0ËWR•^®Ë2)ØŽ>´‰€'ËV¸ØLŽÐ ÷±܀׎<ˆDv!ygÍÆMV…qèºP"o£H¡„b¹TJHƒC’^ úW<¹é4óÉð86eøšY÷Ÿ×̳ÜvåYg¹åº|u”|ŠØGøDh.z½Uy·c~ëvÞ—:Nå„2½dnõXGó ©T´ \ No newline at end of file
diff --git a/tests/resources/renames/.gitted/objects/84/d8efa38af7ace2b302de0adbda16b1f1cd2e1b b/tests/resources/renames/.gitted/objects/84/d8efa38af7ace2b302de0adbda16b1f1cd2e1b
new file mode 100644
index 0000000..56f98fe
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/84/d8efa38af7ace2b302de0adbda16b1f1cd2e1b
@@ -0,0 +1 @@
+x•ŽQjÃ0ó­Sì¡X+Û² ”!4'XiŸˆHìeC¯ßtþg˜r_×fÄÖZRT•%Ç*Œ!q.¡&°ø˜4Ÿ‡0šÝ.›‘Oªµf?ñ óˆ)g,Z0£Žô•©>8yÚåÞé$Ö[¹ÒÙж‹t5:îïýúølëùÉ'Ï<óBà WÞ†ÿ›î¿½H6%Å ÷uJI§ \ No newline at end of file
diff --git a/tests/resources/renames/.gitted/objects/89/7dda8ecb7fa2e092bc3f9e2a179d7c1b0364db b/tests/resources/renames/.gitted/objects/89/7dda8ecb7fa2e092bc3f9e2a179d7c1b0364db
new file mode 100644
index 0000000..d104e66
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/89/7dda8ecb7fa2e092bc3f9e2a179d7c1b0364db
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/93/f538c45a57a87eb4c1e86f91c6ee41d66c7ba7 b/tests/resources/renames/.gitted/objects/93/f538c45a57a87eb4c1e86f91c6ee41d66c7ba7
new file mode 100644
index 0000000..39105f9
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/93/f538c45a57a87eb4c1e86f91c6ee41d66c7ba7
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/95/ceb126bf79e76020d8879a8b0d50a73307a97f b/tests/resources/renames/.gitted/objects/95/ceb126bf79e76020d8879a8b0d50a73307a97f
new file mode 100644
index 0000000..0486ba5
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/95/ceb126bf79e76020d8879a8b0d50a73307a97f
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/9a/69d960ae94b060f56c2a8702545e2bb1abb935 b/tests/resources/renames/.gitted/objects/9a/69d960ae94b060f56c2a8702545e2bb1abb935
new file mode 100644
index 0000000..f75178c
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/9a/69d960ae94b060f56c2a8702545e2bb1abb935
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/ad/0a8e55a104ac54a8a29ed4b84b49e76837a113 b/tests/resources/renames/.gitted/objects/ad/0a8e55a104ac54a8a29ed4b84b49e76837a113
new file mode 100644
index 0000000..440b7be
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/ad/0a8e55a104ac54a8a29ed4b84b49e76837a113
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/b9/25b224cc91f897001a9993fbce169fdaa8858f b/tests/resources/renames/.gitted/objects/b9/25b224cc91f897001a9993fbce169fdaa8858f
new file mode 100644
index 0000000..90e107f
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/b9/25b224cc91f897001a9993fbce169fdaa8858f
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/ba/8b004914491b129b4feff51b5fd8553b8e8156 b/tests/resources/renames/.gitted/objects/ba/8b004914491b129b4feff51b5fd8553b8e8156
new file mode 100644
index 0000000..c61d650
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/ba/8b004914491b129b4feff51b5fd8553b8e8156
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/be/053a189b0bbde545e0a3f59ce00b46ad29ce0d b/tests/resources/renames/.gitted/objects/be/053a189b0bbde545e0a3f59ce00b46ad29ce0d
new file mode 100644
index 0000000..de7aceb
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/be/053a189b0bbde545e0a3f59ce00b46ad29ce0d
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/bf/102db0c9c0c1513909a90e0611b5dddfc87c93 b/tests/resources/renames/.gitted/objects/bf/102db0c9c0c1513909a90e0611b5dddfc87c93
new file mode 100644
index 0000000..69ddd68
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/bf/102db0c9c0c1513909a90e0611b5dddfc87c93
@@ -0,0 +1,2 @@
+x+)JMU042a040031Qpttd83ƒÿ×Ñ–fÓ®œ-°[yéï³ÚËéPi'''†¨Â]»Vï*í
+ÏJ8·øĉ­nPiggg†×›·¯—U=á©«Ÿ”¡vR%bû¹¿ Pi¬º¼:6Ž \ No newline at end of file
diff --git a/tests/resources/renames/.gitted/objects/cc/980ffac5f1393696d4cd703ea9d2fde67dd367 b/tests/resources/renames/.gitted/objects/cc/980ffac5f1393696d4cd703ea9d2fde67dd367
new file mode 100644
index 0000000..ac7a005
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/cc/980ffac5f1393696d4cd703ea9d2fde67dd367
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/d7/9b202de198fa61b02424b9e25e840dc75e1323 b/tests/resources/renames/.gitted/objects/d7/9b202de198fa61b02424b9e25e840dc75e1323
new file mode 100644
index 0000000..daa2b39
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/d7/9b202de198fa61b02424b9e25e840dc75e1323
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/db/98035f715427eef1f5e17f03e1801c05301e9e b/tests/resources/renames/.gitted/objects/db/98035f715427eef1f5e17f03e1801c05301e9e
new file mode 100644
index 0000000..48bc2e1
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/db/98035f715427eef1f5e17f03e1801c05301e9e
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/e5/6d39ca6d946331aa79c9c443d46c8a6ed4f550 b/tests/resources/renames/.gitted/objects/e5/6d39ca6d946331aa79c9c443d46c8a6ed4f550
new file mode 100644
index 0000000..d6deb18
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/e5/6d39ca6d946331aa79c9c443d46c8a6ed4f550
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/ea/c43f5195a2cee53b7458d8dad16aedde10711b b/tests/resources/renames/.gitted/objects/ea/c43f5195a2cee53b7458d8dad16aedde10711b
new file mode 100644
index 0000000..2fb0250
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/ea/c43f5195a2cee53b7458d8dad16aedde10711b
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/ea/f4a3e3bfe68585e90cada20736ace491cd100b b/tests/resources/renames/.gitted/objects/ea/f4a3e3bfe68585e90cada20736ace491cd100b
new file mode 100644
index 0000000..f72df8d
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/ea/f4a3e3bfe68585e90cada20736ace491cd100b
@@ -0,0 +1,5 @@
+x}RÁªÔ@ô<_QžTÈ.¼“ÂâAáÉ[–‡â
+{ž¼t2C’žez’˜›áú%öLžº'!aº»ª«jê!Ôx{÷îÅ'¢+Îþ;“$œ)Ξ»Ý#±yÿ¿Ç˜³#q#¯h„­c° ÃQDX¶m­R|ŠaD*ÝOˆþ†+±”^ZI~ýøi>3aôÃàã!,R!-áïÉEaIÁ>äyš‰o*«¼4æˆRf¡ m&e¯ IAÑú™ò*!â;¢ždÍݬ‚…´Å
+êH¶o­¤
+ÃÄO®‚U¾DöyTVØHpwqÅH¼7§„Æ·­.ÈʆÎÎts6{Zä +öX\)Šª”ÑC–žì5QªÂä9
+%©ÌÅt*Ã&Ï&è°êv;|šÕÆ'4½ìÆéþþ Dƒu[°7h¯¿e!ÉNK*"C©-=Óòæ`´æ#ØŽ$EëÅe2õáâT|ùêå@NBsús¢¦lµ°Wö|/¶0¬÷aÈ¥üJ±ò¶Nêv)-šÚ¡˜iÛ¤3ÅëbäbO:uWMâˆNÓÜàóæ¶X²7¿Tóº \ No newline at end of file
diff --git a/tests/resources/renames/.gitted/objects/eb/b3b7af1d25c8492d2f626826c92458b7cefd60 b/tests/resources/renames/.gitted/objects/eb/b3b7af1d25c8492d2f626826c92458b7cefd60
new file mode 100644
index 0000000..813e7ad
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/eb/b3b7af1d25c8492d2f626826c92458b7cefd60
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/ed/2a95c4a6c295b9afcea50baff63ec544ccf600 b/tests/resources/renames/.gitted/objects/ed/2a95c4a6c295b9afcea50baff63ec544ccf600
new file mode 100644
index 0000000..4f439e2
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/ed/2a95c4a6c295b9afcea50baff63ec544ccf600
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/f6/7e2f70efe89665e829ea0d77c46965ad1307e4 b/tests/resources/renames/.gitted/objects/f6/7e2f70efe89665e829ea0d77c46965ad1307e4
new file mode 100644
index 0000000..1cad213
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/f6/7e2f70efe89665e829ea0d77c46965ad1307e4
Binary files differ
diff --git a/tests/resources/renames/.gitted/objects/f9/0d4fc20ecddf21eebe6a37e9225d244339d2b5 b/tests/resources/renames/.gitted/objects/f9/0d4fc20ecddf21eebe6a37e9225d244339d2b5
new file mode 100644
index 0000000..f6d933b
--- /dev/null
+++ b/tests/resources/renames/.gitted/objects/f9/0d4fc20ecddf21eebe6a37e9225d244339d2b5
Binary files differ
diff --git a/tests/resources/renames/.gitted/refs/heads/break_rewrite b/tests/resources/renames/.gitted/refs/heads/break_rewrite
new file mode 100644
index 0000000..d170915
--- /dev/null
+++ b/tests/resources/renames/.gitted/refs/heads/break_rewrite
@@ -0,0 +1 @@
+7e7bfb88ba9bc65fd700fee1819cf1c317aafa56
diff --git a/tests/resources/renames/.gitted/refs/heads/case-insensitive-status b/tests/resources/renames/.gitted/refs/heads/case-insensitive-status
new file mode 100644
index 0000000..5a92127
--- /dev/null
+++ b/tests/resources/renames/.gitted/refs/heads/case-insensitive-status
@@ -0,0 +1 @@
+41a766bb079e18ff6a24571831bd751168799a02
diff --git a/tests/resources/renames/.gitted/refs/heads/delete-and-rename b/tests/resources/renames/.gitted/refs/heads/delete-and-rename
new file mode 100644
index 0000000..f27fc21
--- /dev/null
+++ b/tests/resources/renames/.gitted/refs/heads/delete-and-rename
@@ -0,0 +1 @@
+be053a189b0bbde545e0a3f59ce00b46ad29ce0d
diff --git a/tests/resources/renames/.gitted/refs/heads/master b/tests/resources/renames/.gitted/refs/heads/master
new file mode 100644
index 0000000..642c319
--- /dev/null
+++ b/tests/resources/renames/.gitted/refs/heads/master
@@ -0,0 +1 @@
+19dd32dfb1520a64e5bbaae8dce6ef423dfa2f13
diff --git a/tests/resources/renames/.gitted/refs/heads/renames_similar b/tests/resources/renames/.gitted/refs/heads/renames_similar
new file mode 100644
index 0000000..8ffb6d9
--- /dev/null
+++ b/tests/resources/renames/.gitted/refs/heads/renames_similar
@@ -0,0 +1 @@
+444a76ed3e45b183753f49376af30da8c3fe276a
diff --git a/tests/resources/renames/.gitted/refs/heads/renames_similar_two b/tests/resources/renames/.gitted/refs/heads/renames_similar_two
new file mode 100644
index 0000000..4ee5d04
--- /dev/null
+++ b/tests/resources/renames/.gitted/refs/heads/renames_similar_two
@@ -0,0 +1 @@
+50e90273af7d826ff0a95865bcd3ba8412c447d9
diff --git a/tests/resources/renames/.gitted/refs/heads/rewrite-and-delete b/tests/resources/renames/.gitted/refs/heads/rewrite-and-delete
new file mode 100644
index 0000000..0c0ecad
--- /dev/null
+++ b/tests/resources/renames/.gitted/refs/heads/rewrite-and-delete
@@ -0,0 +1 @@
+84d8efa38af7ace2b302de0adbda16b1f1cd2e1b
diff --git a/tests/resources/renames/ikeepsix.txt b/tests/resources/renames/ikeepsix.txt
new file mode 100644
index 0000000..eaf4a3e
--- /dev/null
+++ b/tests/resources/renames/ikeepsix.txt
@@ -0,0 +1,27 @@
+I Keep Six Honest Serving-Men
+=============================
+
+She sends'em abroad on her own affairs,
+ From the second she opens her eyes—
+One million Hows, two million Wheres,
+And seven million Whys!
+
+I let them rest from nine till five,
+ For I am busy then,
+As well as breakfast, lunch, and tea,
+ For they are hungry men.
+But different folk have different views;
+I know a person small—
+She keeps ten million serving-men,
+Who get no rest at all!
+
+ -- Rudyard Kipling
+
+I KEEP six honest serving-men
+ (They taught me all I knew);
+Their names are What and Why and When
+ And How and Where and Who.
+I send them over land and sea,
+ I send them east and west;
+But after they have worked for me,
+ I give them all a rest.
diff --git a/tests/resources/renames/sixserving.txt b/tests/resources/renames/sixserving.txt
new file mode 100644
index 0000000..f90d4fc
--- /dev/null
+++ b/tests/resources/renames/sixserving.txt
@@ -0,0 +1,25 @@
+I KEEP six honest serving-men
+ (They taught me all I knew);
+Their names are What and Why and When
+ And How and Where and Who.
+I send them over land and sea,
+ I send them east and west;
+But after they have worked for me,
+ I give them all a rest.
+
+I let them rest from nine till five,
+ For I am busy then,
+As well as breakfast, lunch, and tea,
+ For they are hungry men.
+But different folk have different views;
+I know a person small—
+She keeps ten million serving-men,
+Who get no rest at all!
+
+She sends'em abroad on her own affairs,
+ From the second she opens her eyes—
+One million Hows, two million Wheres,
+And seven million Whys!
+
+ -- Rudyard Kipling
+
diff --git a/tests/resources/renames/songof7cities.txt b/tests/resources/renames/songof7cities.txt
new file mode 100644
index 0000000..4210ffd
--- /dev/null
+++ b/tests/resources/renames/songof7cities.txt
@@ -0,0 +1,49 @@
+The Song of Seven Cities
+------------------------
+
+I WAS Lord of Cities very sumptuously builded.
+Seven roaring Cities paid me tribute from afar.
+Ivory their outposts were--the guardrooms of them gilded,
+And garrisoned with Amazons invincible in war.
+
+All the world went softly when it walked before my Cities--
+Neither King nor Army vexed my peoples at their toil,
+Never horse nor chariot irked or overbore my Cities,
+Never Mob nor Ruler questioned whence they drew their spoil.
+
+Banded, mailed and arrogant from sunrise unto sunset;
+Singing while they sacked it, they possessed the land at large.
+Yet when men would rob them, they resisted, they made onset
+And pierced the smoke of battle with a thousand-sabred charge.
+
+So they warred and trafficked only yesterday, my Cities.
+To-day there is no mark or mound of where my Cities stood.
+For the River rose at midnight and it washed away my Cities.
+They are evened with Atlantis and the towns before the Flood.
+
+Rain on rain-gorged channels raised the water-levels round them,
+Freshet backed on freshet swelled and swept their world from sight,
+Till the emboldened floods linked arms and, flashing forward, drowned them--
+Drowned my Seven Cities and their peoples in one night!
+
+Low among the alders lie their derelict foundations,
+The beams wherein they trusted and the plinths whereon they built--
+My rulers and their treasure and their unborn populations,
+Dead, destroyed, aborted, and defiled with mud and silt!
+
+The Daughters of the Palace whom they cherished in my Cities,
+My silver-tongued Princesses, and the promise of their May--
+Their bridegrooms of the June-tide--all have perished in my Cities,
+With the harsh envenomed virgins that can neither love nor play.
+
+I was Lord of Cities--I will build anew my Cities,
+Seven, set on rocks, above the wrath of any flood.
+Nor will I rest from search till I have filled anew my Cities
+With peoples undefeated of the dark, enduring blood.
+
+To the sound of trumpets shall their seed restore my Cities
+Wealthy and well-weaponed, that once more may I behold
+All the world go softly when it walks before my Cities,
+And the horses and the chariots fleeing from them as of old!
+
+ -- Rudyard Kipling
diff --git a/tests/resources/renames/untimely.txt b/tests/resources/renames/untimely.txt
new file mode 100644
index 0000000..9a69d96
--- /dev/null
+++ b/tests/resources/renames/untimely.txt
@@ -0,0 +1,24 @@
+Untimely
+========
+
+Nothing in life has been made by man for man's using
+But it was shown long since to man in ages
+Lost as the name of the maker of it,
+Who received oppression and shame for his wages--
+Hate, avoidance, and scorn in his daily dealings--
+Until he perished, wholly confounded
+
+More to be pitied than he are the wise
+Souls which foresaw the evil of loosing
+Knowledge or Art before time, and aborted
+Noble devices and deep-wrought healings,
+Lest offense should arise.
+
+Heaven delivers on earth the Hour that cannot be
+ thwarted,
+Neither advanced, at the price of a world nor a soul,
+ and its Prophet
+Comes through the blood of the vanguards who
+ dreamed--too soon--it had sounded.
+
+ -- Rudyard Kipling
diff --git a/tests/resources/revert-rename.git/HEAD b/tests/resources/revert-rename.git/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/revert-rename.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/revert-rename.git/config b/tests/resources/revert-rename.git/config
new file mode 100644
index 0000000..a4ef456
--- /dev/null
+++ b/tests/resources/revert-rename.git/config
@@ -0,0 +1,5 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = true
+ logallrefupdates = true
diff --git a/tests/resources/revert-rename.git/index b/tests/resources/revert-rename.git/index
new file mode 100644
index 0000000..232325d
--- /dev/null
+++ b/tests/resources/revert-rename.git/index
Binary files differ
diff --git a/tests/resources/revert-rename.git/objects/info/packs b/tests/resources/revert-rename.git/objects/info/packs
new file mode 100644
index 0000000..5d97142
--- /dev/null
+++ b/tests/resources/revert-rename.git/objects/info/packs
@@ -0,0 +1,2 @@
+P pack-4363774fb90141e8aa7a326ace0566366114e869.pack
+
diff --git a/tests/resources/revert-rename.git/objects/pack/pack-4363774fb90141e8aa7a326ace0566366114e869.idx b/tests/resources/revert-rename.git/objects/pack/pack-4363774fb90141e8aa7a326ace0566366114e869.idx
new file mode 100644
index 0000000..3fb48ea
--- /dev/null
+++ b/tests/resources/revert-rename.git/objects/pack/pack-4363774fb90141e8aa7a326ace0566366114e869.idx
Binary files differ
diff --git a/tests/resources/revert-rename.git/objects/pack/pack-4363774fb90141e8aa7a326ace0566366114e869.pack b/tests/resources/revert-rename.git/objects/pack/pack-4363774fb90141e8aa7a326ace0566366114e869.pack
new file mode 100644
index 0000000..e078ec2
--- /dev/null
+++ b/tests/resources/revert-rename.git/objects/pack/pack-4363774fb90141e8aa7a326ace0566366114e869.pack
Binary files differ
diff --git a/tests/resources/revert-rename.git/packed-refs b/tests/resources/revert-rename.git/packed-refs
new file mode 100644
index 0000000..e062909
--- /dev/null
+++ b/tests/resources/revert-rename.git/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled fully-peeled sorted
+ecef6a85173b6f446873a13f7b5a7b54a85cd912 refs/heads/master
diff --git a/tests/resources/revert-rename.git/refs/heads/master b/tests/resources/revert-rename.git/refs/heads/master
new file mode 100644
index 0000000..3771102
--- /dev/null
+++ b/tests/resources/revert-rename.git/refs/heads/master
@@ -0,0 +1 @@
+ecef6a85173b6f446873a13f7b5a7b54a85cd912
diff --git a/tests/resources/revert/.gitted/HEAD b/tests/resources/revert/.gitted/HEAD
new file mode 100644
index 0000000..cb43805
--- /dev/null
+++ b/tests/resources/revert/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/revert/.gitted/config b/tests/resources/revert/.gitted/config
new file mode 100644
index 0000000..454e576
--- /dev/null
+++ b/tests/resources/revert/.gitted/config
@@ -0,0 +1,8 @@
+[core]
+ repositoryformatversion = 0
+ filemode = false
+ bare = false
+ logallrefupdates = true
+ symlinks = false
+ ignorecase = true
+ hideDotFiles = dotGitOnly
diff --git a/tests/resources/revert/.gitted/index b/tests/resources/revert/.gitted/index
new file mode 100644
index 0000000..3513c04
--- /dev/null
+++ b/tests/resources/revert/.gitted/index
Binary files differ
diff --git a/tests/resources/revert/.gitted/info/exclude b/tests/resources/revert/.gitted/info/exclude
new file mode 100644
index 0000000..f006809
--- /dev/null
+++ b/tests/resources/revert/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/revert/.gitted/objects/00/c97c9299419874a7bfc4d853d462c568e1be2d b/tests/resources/revert/.gitted/objects/00/c97c9299419874a7bfc4d853d462c568e1be2d
new file mode 100644
index 0000000..b4e9635
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/00/c97c9299419874a7bfc4d853d462c568e1be2d
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/0a/a8c7e40d342fff78d60b29a4ba8e993ed79c51 b/tests/resources/revert/.gitted/objects/0a/a8c7e40d342fff78d60b29a4ba8e993ed79c51
new file mode 100644
index 0000000..5c811ef
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/0a/a8c7e40d342fff78d60b29a4ba8e993ed79c51
@@ -0,0 +1,2 @@
+x¥K
+Â0@]çs%Ó4?qã ¼@2Ѐé@ñúÖ3¸{¼Å{$­ÕÜitf@ë¸ÄL9º°1…ÙOI—‰K™cÁäÑdrY¥÷X¥Ãcù¤¾Às•¶ËW>ìî­R—]ʸ´  GଭÖê°Çxð U·:jz©/¤’? \ No newline at end of file
diff --git a/tests/resources/revert/.gitted/objects/0a/b09ea6d4c3634bdf6c221626d8b6f7dd890767 b/tests/resources/revert/.gitted/objects/0a/b09ea6d4c3634bdf6c221626d8b6f7dd890767
new file mode 100644
index 0000000..c050e5a
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/0a/b09ea6d4c3634bdf6c221626d8b6f7dd890767
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/0a/d19525be6d8cae5e5deb2770fc244b65255057 b/tests/resources/revert/.gitted/objects/0a/d19525be6d8cae5e5deb2770fc244b65255057
new file mode 100644
index 0000000..4aa0459
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/0a/d19525be6d8cae5e5deb2770fc244b65255057
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/0c/db66192ee192f70f891f05a47636057420e871 b/tests/resources/revert/.gitted/objects/0c/db66192ee192f70f891f05a47636057420e871
new file mode 100644
index 0000000..31c107f
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/0c/db66192ee192f70f891f05a47636057420e871
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/0f/5bfcf58c558d865da6be0281d7795993646cee b/tests/resources/revert/.gitted/objects/0f/5bfcf58c558d865da6be0281d7795993646cee
new file mode 100644
index 0000000..fb4fc76
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/0f/5bfcf58c558d865da6be0281d7795993646cee
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/10/10c8f4711d60d04bad16197a0f4b0d4d19c542 b/tests/resources/revert/.gitted/objects/10/10c8f4711d60d04bad16197a0f4b0d4d19c542
new file mode 100644
index 0000000..083f746
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/10/10c8f4711d60d04bad16197a0f4b0d4d19c542
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/13/a6fdfd10bd74b1f258fb58801215985dd2e797 b/tests/resources/revert/.gitted/objects/13/a6fdfd10bd74b1f258fb58801215985dd2e797
new file mode 100644
index 0000000..3c54aab
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/13/a6fdfd10bd74b1f258fb58801215985dd2e797
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/13/ee9cd5d8e1023c218e0e1ea684ec0c582b5050 b/tests/resources/revert/.gitted/objects/13/ee9cd5d8e1023c218e0e1ea684ec0c582b5050
new file mode 100644
index 0000000..aed4647
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/13/ee9cd5d8e1023c218e0e1ea684ec0c582b5050
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/15/6ef9bcb968dccec8472a0f2eff49f1a713bc6b b/tests/resources/revert/.gitted/objects/15/6ef9bcb968dccec8472a0f2eff49f1a713bc6b
new file mode 100644
index 0000000..3ee2a18
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/15/6ef9bcb968dccec8472a0f2eff49f1a713bc6b
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/18/1aab27ddb37b40d9a284fb4733497006d57091 b/tests/resources/revert/.gitted/objects/18/1aab27ddb37b40d9a284fb4733497006d57091
new file mode 100644
index 0000000..6b422b8
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/18/1aab27ddb37b40d9a284fb4733497006d57091
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/1b/c915c5cb7185a9438de28a7b1a7dfe8c01ee7f b/tests/resources/revert/.gitted/objects/1b/c915c5cb7185a9438de28a7b1a7dfe8c01ee7f
new file mode 100644
index 0000000..0a6955b
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/1b/c915c5cb7185a9438de28a7b1a7dfe8c01ee7f
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/1f/a4e069a641f10f5fb7588138b2d147fcd22c36 b/tests/resources/revert/.gitted/objects/1f/a4e069a641f10f5fb7588138b2d147fcd22c36
new file mode 100644
index 0000000..3df134a
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/1f/a4e069a641f10f5fb7588138b2d147fcd22c36
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/1f/f0c423042b46cb1d617b81efb715defbe8054d b/tests/resources/revert/.gitted/objects/1f/f0c423042b46cb1d617b81efb715defbe8054d
new file mode 100644
index 0000000..2ed1a22
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/1f/f0c423042b46cb1d617b81efb715defbe8054d
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/21/a96a98ed84d45866e1de6e266fd3a61a4ae9dc b/tests/resources/revert/.gitted/objects/21/a96a98ed84d45866e1de6e266fd3a61a4ae9dc
new file mode 100644
index 0000000..95842db
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/21/a96a98ed84d45866e1de6e266fd3a61a4ae9dc
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/29/6a6d3be1dff05c5d1f631d2459389fa7b619eb b/tests/resources/revert/.gitted/objects/29/6a6d3be1dff05c5d1f631d2459389fa7b619eb
new file mode 100644
index 0000000..5555268
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/29/6a6d3be1dff05c5d1f631d2459389fa7b619eb
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/2d/440f2b3147d3dc7ad1085813478d6d869d5a4d b/tests/resources/revert/.gitted/objects/2d/440f2b3147d3dc7ad1085813478d6d869d5a4d
new file mode 100644
index 0000000..c06cc94
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/2d/440f2b3147d3dc7ad1085813478d6d869d5a4d
@@ -0,0 +1,2 @@
+x¥ŽK
+1D]ç}%Iç3"n¼Èt:΀I$ñúÆ3¸)ŠWð(ª9o´q‡Þ˜aaQ9ËRyífž"É]’JÍv Ym¯Ð¸tð“ñÑð„N9‡2%\F°!™/DHÆŠðîkmp‹ŸÐ"Üך÷Zà̃þÚ5oÔê^S?QÍP8Y…~ÖŽÒJ)G;ÿ¡Ž’žõ­<€ÖP¼‹/ælPÞ \ No newline at end of file
diff --git a/tests/resources/revert/.gitted/objects/33/c6fd981c49a2abf2971482089350bfc5cda8ea b/tests/resources/revert/.gitted/objects/33/c6fd981c49a2abf2971482089350bfc5cda8ea
new file mode 100644
index 0000000..683f27f
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/33/c6fd981c49a2abf2971482089350bfc5cda8ea
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/39/467716290f6df775a91cdb9a4eb39295018145 b/tests/resources/revert/.gitted/objects/39/467716290f6df775a91cdb9a4eb39295018145
new file mode 100644
index 0000000..7c1e01d
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/39/467716290f6df775a91cdb9a4eb39295018145
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/39/9fb3aba3d9d13f7d40a9254ce4402067ef3149 b/tests/resources/revert/.gitted/objects/39/9fb3aba3d9d13f7d40a9254ce4402067ef3149
new file mode 100644
index 0000000..6cb6839
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/39/9fb3aba3d9d13f7d40a9254ce4402067ef3149
@@ -0,0 +1,2 @@
+x¥ŽK!]s
+. á;@bŒoàzºA\0mŒ×Ïàª<äÖžC§£ç,3Ød@û ¢_ÑDò @§K6Å—5Tâ=oS$çT1«Õ.% @zªQ["-—D xÊ]Þèä½rÛy“ç<éo]Û;ï\Æ ¹]¤¶1YüåQE¥Ä¤óèÈ$l<ê,`…í‘ÅaGNÅ \ No newline at end of file
diff --git a/tests/resources/revert/.gitted/objects/3a/3ef367eaf3fe79effbfb0a56b269c04c2b59fe b/tests/resources/revert/.gitted/objects/3a/3ef367eaf3fe79effbfb0a56b269c04c2b59fe
new file mode 100644
index 0000000..b83806e
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/3a/3ef367eaf3fe79effbfb0a56b269c04c2b59fe
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/46/ff0854663aeb2182b9838c8da68e33ac23bc1e b/tests/resources/revert/.gitted/objects/46/ff0854663aeb2182b9838c8da68e33ac23bc1e
new file mode 100644
index 0000000..7064dab
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/46/ff0854663aeb2182b9838c8da68e33ac23bc1e
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/4b/8fcff56437e60f58e9a6bc630dd242ebf6ea2c b/tests/resources/revert/.gitted/objects/4b/8fcff56437e60f58e9a6bc630dd242ebf6ea2c
new file mode 100644
index 0000000..c0bd3db
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/4b/8fcff56437e60f58e9a6bc630dd242ebf6ea2c
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/52/c95c4264245469a0617e289a7d737f156826b4 b/tests/resources/revert/.gitted/objects/52/c95c4264245469a0617e289a7d737f156826b4
new file mode 100644
index 0000000..7cc9e96
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/52/c95c4264245469a0617e289a7d737f156826b4
@@ -0,0 +1,2 @@
+x¥=N1 …©s
+k›mØU~Ç !J´È8ÎÎIP&ÀõÉ2@¢°ä÷¤Ïï™jÎk+ñ¡7fð˜ŒCË^'…FF3¹4{–’”dCN")JìÅGh\:¸@‘І¤Q¡f²^E­ ³™ˆò`“vèEøìKmð¿C‹p]jÞj'î}{É+µºÕÔÏTó3(sqcŒ“p’NJ1ÜQ´ó?NˆwþâÖáðÆíÆ0·Phc¾«í´Ë#¬¥Wؽƒ×eÝ ýrìþüóãnk¹ ZB)CdÆÛ QMÚË4Å„è‚Wg,ÏÆk鷺(ëÎâçÛä \ No newline at end of file
diff --git a/tests/resources/revert/.gitted/objects/55/568c8de5322ff9a95d72747a239cdb64a19965 b/tests/resources/revert/.gitted/objects/55/568c8de5322ff9a95d72747a239cdb64a19965
new file mode 100644
index 0000000..4dae50b
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/55/568c8de5322ff9a95d72747a239cdb64a19965
@@ -0,0 +1 @@
+x¥ŽK!]sŠ¾€¦ù ãÆx莓ˆÄx}ñ î^jñªr«u`œÝÎ ZÈ1.‰§E£xYƒQÛ¸š¢]\ŒÉvQOêü€D1vX¬3"bYp5‰ÜJ‘S²\BÊ^+z[ëp)ê®·V_íGžô·Îu˽½šŒCnõSèµ ìÑ#ªIgèà?.Ô,¦Ê°•Y¾eº«/»ZOH \ No newline at end of file
diff --git a/tests/resources/revert/.gitted/objects/55/acf326a69f0aab7a974ec53ffa55a50bcac14e b/tests/resources/revert/.gitted/objects/55/acf326a69f0aab7a974ec53ffa55a50bcac14e
new file mode 100644
index 0000000..e9cdfc5
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/55/acf326a69f0aab7a974ec53ffa55a50bcac14e
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/5a/cdc74af27172ec491d213ee36cea7eb9ef2579 b/tests/resources/revert/.gitted/objects/5a/cdc74af27172ec491d213ee36cea7eb9ef2579
new file mode 100644
index 0000000..61055ea
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/5a/cdc74af27172ec491d213ee36cea7eb9ef2579
@@ -0,0 +1,3 @@
+x¥=N1 …©s
+w[-Š“8?B4”t\ ?3E&«L×'«' °ä÷é=?9÷Öö éifˆ6Tg¸æDhµIÞ³4=*·<>zRäªAq‹ƒ Æ:‡VYm©ÎQ ˜K
+ÑpÒA’èÑП5sÈ…Šg”Jg…«€‘£õ†³ÌäU"IRÄï¹õïå'ŽŸ[og?à…½oomÏ£Ÿ½ÎçÜÛ+ ö´fõÁuÅ¥Xt=6ù'Ä/†4â‘7¸´»:¯yý˜Lü:øc’ \ No newline at end of file
diff --git a/tests/resources/revert/.gitted/objects/6b/ccd0dc58cea5ccff86014f3d64b31bd8c02a37 b/tests/resources/revert/.gitted/objects/6b/ccd0dc58cea5ccff86014f3d64b31bd8c02a37
new file mode 100644
index 0000000..2664da4
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/6b/ccd0dc58cea5ccff86014f3d64b31bd8c02a37
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/71/eb9c2b53dbbf3c45fb28b27c850db4b7fb8011 b/tests/resources/revert/.gitted/objects/71/eb9c2b53dbbf3c45fb28b27c850db4b7fb8011
new file mode 100644
index 0000000..995a1e6
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/71/eb9c2b53dbbf3c45fb28b27c850db4b7fb8011
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/72/333f47d4e83616630ff3b0ffe4c0faebcc3c45 b/tests/resources/revert/.gitted/objects/72/333f47d4e83616630ff3b0ffe4c0faebcc3c45
new file mode 100644
index 0000000..1f69787
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/72/333f47d4e83616630ff3b0ffe4c0faebcc3c45
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/73/ec36fa120f8066963a0bc9105bb273dbd903d7 b/tests/resources/revert/.gitted/objects/73/ec36fa120f8066963a0bc9105bb273dbd903d7
new file mode 100644
index 0000000..3c8d2c2
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/73/ec36fa120f8066963a0bc9105bb273dbd903d7
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/74/7726e021bc5f44b86de60e3032fd6f9f1b8383 b/tests/resources/revert/.gitted/objects/74/7726e021bc5f44b86de60e3032fd6f9f1b8383
new file mode 100644
index 0000000..e4d1477
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/74/7726e021bc5f44b86de60e3032fd6f9f1b8383
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/75/ec9929465623f17ff3ad68c0438ea56faba815 b/tests/resources/revert/.gitted/objects/75/ec9929465623f17ff3ad68c0438ea56faba815
new file mode 100644
index 0000000..a3609c7
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/75/ec9929465623f17ff3ad68c0438ea56faba815
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/77/31926a337c4eaba1e2187d90ebfa0a93659382 b/tests/resources/revert/.gitted/objects/77/31926a337c4eaba1e2187d90ebfa0a93659382
new file mode 100644
index 0000000..b87fa15
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/77/31926a337c4eaba1e2187d90ebfa0a93659382
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/83/f65df4606c4f8dbf8da43de25de1b7e4c03238 b/tests/resources/revert/.gitted/objects/83/f65df4606c4f8dbf8da43de25de1b7e4c03238
new file mode 100644
index 0000000..152ed5f
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/83/f65df4606c4f8dbf8da43de25de1b7e4c03238
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/87/59ad453cf01cf7daf14e2a668f8218f9a678eb b/tests/resources/revert/.gitted/objects/87/59ad453cf01cf7daf14e2a668f8218f9a678eb
new file mode 100644
index 0000000..ab19acf
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/87/59ad453cf01cf7daf14e2a668f8218f9a678eb
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/8b/e77695228eadd004606af0508462457961ca4a b/tests/resources/revert/.gitted/objects/8b/e77695228eadd004606af0508462457961ca4a
new file mode 100644
index 0000000..951917c
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/8b/e77695228eadd004606af0508462457961ca4a
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/8f/d40e13fff575b63e86af87175e70fa7fb92f80 b/tests/resources/revert/.gitted/objects/8f/d40e13fff575b63e86af87175e70fa7fb92f80
new file mode 100644
index 0000000..9ff8617
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/8f/d40e13fff575b63e86af87175e70fa7fb92f80
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/97/e52d5e81f541080cd6b92829fb85bc4d81d90b b/tests/resources/revert/.gitted/objects/97/e52d5e81f541080cd6b92829fb85bc4d81d90b
new file mode 100644
index 0000000..416ae0f
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/97/e52d5e81f541080cd6b92829fb85bc4d81d90b
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/97/f3574e92f1730d365fb9e00c10e3c507c1cfe9 b/tests/resources/revert/.gitted/objects/97/f3574e92f1730d365fb9e00c10e3c507c1cfe9
new file mode 100644
index 0000000..12ebc58
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/97/f3574e92f1730d365fb9e00c10e3c507c1cfe9
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/9a/95fd974e03c5b93828ceedd28755965b5d5c60 b/tests/resources/revert/.gitted/objects/9a/95fd974e03c5b93828ceedd28755965b5d5c60
new file mode 100644
index 0000000..bb93a34
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/9a/95fd974e03c5b93828ceedd28755965b5d5c60
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/a6/9f74efcb51634b88e04ea81273158a85257f41 b/tests/resources/revert/.gitted/objects/a6/9f74efcb51634b88e04ea81273158a85257f41
new file mode 100644
index 0000000..b96831f
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/a6/9f74efcb51634b88e04ea81273158a85257f41
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/a8/c86221b400b836010567cc3593db6e96c1a83a b/tests/resources/revert/.gitted/objects/a8/c86221b400b836010567cc3593db6e96c1a83a
new file mode 100644
index 0000000..2965461
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/a8/c86221b400b836010567cc3593db6e96c1a83a
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/aa/7e281435d1fe6740d712f4bcc6fe89c425bedc b/tests/resources/revert/.gitted/objects/aa/7e281435d1fe6740d712f4bcc6fe89c425bedc
new file mode 100644
index 0000000..f0dd67d
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/aa/7e281435d1fe6740d712f4bcc6fe89c425bedc
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/ac/c4d33902092efeb3b714aa0b1007c329e2f2e6 b/tests/resources/revert/.gitted/objects/ac/c4d33902092efeb3b714aa0b1007c329e2f2e6
new file mode 100644
index 0000000..91bb68b
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/ac/c4d33902092efeb3b714aa0b1007c329e2f2e6
@@ -0,0 +1,2 @@
+xÍ[
+1 @QßJW‘ étÒ6 F,LâþWàßý8peé½ø1llU.LÊiT B‡<dQ­uȉˆ#MTI"ºò¶ç²Âv·?OçË7ঽ´ù>dN0z¸"!:ùMLÿ䮽š¹õ$Î \ No newline at end of file
diff --git a/tests/resources/revert/.gitted/objects/b7/a55408832174c54708906a372a9be2ffe3649b b/tests/resources/revert/.gitted/objects/b7/a55408832174c54708906a372a9be2ffe3649b
new file mode 100644
index 0000000..77d4e20
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/b7/a55408832174c54708906a372a9be2ffe3649b
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/be/ead165e017269e8dc0dd6f01195726a2e1e01b b/tests/resources/revert/.gitted/objects/be/ead165e017269e8dc0dd6f01195726a2e1e01b
new file mode 100644
index 0000000..57de3f2
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/be/ead165e017269e8dc0dd6f01195726a2e1e01b
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/ce/f56612d71a6af8d8015691e4865f7fece905b5 b/tests/resources/revert/.gitted/objects/ce/f56612d71a6af8d8015691e4865f7fece905b5
new file mode 100644
index 0000000..5645885
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/ce/f56612d71a6af8d8015691e4865f7fece905b5
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/d1/d403d22cbe24592d725f442835cf46fe60c8ac b/tests/resources/revert/.gitted/objects/d1/d403d22cbe24592d725f442835cf46fe60c8ac
new file mode 100644
index 0000000..2190bb4
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/d1/d403d22cbe24592d725f442835cf46fe60c8ac
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/dd/9a159c89509e73fd37d6af99619994cf7dfc06 b/tests/resources/revert/.gitted/objects/dd/9a159c89509e73fd37d6af99619994cf7dfc06
new file mode 100644
index 0000000..ed80d0a
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/dd/9a159c89509e73fd37d6af99619994cf7dfc06
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/e3/4ef1afe54eb526fd92eec66084125f340f1d65 b/tests/resources/revert/.gitted/objects/e3/4ef1afe54eb526fd92eec66084125f340f1d65
new file mode 100644
index 0000000..fc19ebd
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/e3/4ef1afe54eb526fd92eec66084125f340f1d65
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/e5/f831f064adf9224d8c3ce556959d9d61b3c0a9 b/tests/resources/revert/.gitted/objects/e5/f831f064adf9224d8c3ce556959d9d61b3c0a9
new file mode 100644
index 0000000..665f1a1
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/e5/f831f064adf9224d8c3ce556959d9d61b3c0a9
@@ -0,0 +1 @@
+x¥OIj1ôY¯h|é–[3„K>`üIÝbæ +h”äû™8y@À§*ª –Üj]œÑFWBÂì ÏD2¡ §(4Q˜#N(,²ckÞc×Û€0«³âÔSqLè1Ë”‚õ6”ä]Ê,ž$`2ñc,­Ã›|Å.p]ZÝÚ žuWØk]so[+ã”[}bbf´|†'dD³«ûСD˜‹~jpüêGc®ËºA¿ü–üûÔÉ|ki`ö \ No newline at end of file
diff --git a/tests/resources/revert/.gitted/objects/ea/392a157085bc32daccd59aa1998fe2f5fb9fc0 b/tests/resources/revert/.gitted/objects/ea/392a157085bc32daccd59aa1998fe2f5fb9fc0
new file mode 100644
index 0000000..1451a6a
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/ea/392a157085bc32daccd59aa1998fe2f5fb9fc0
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/eb/b03002cee5d66c7732dd06241119fe72ab96a5 b/tests/resources/revert/.gitted/objects/eb/b03002cee5d66c7732dd06241119fe72ab96a5
new file mode 100644
index 0000000..802125e
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/eb/b03002cee5d66c7732dd06241119fe72ab96a5
@@ -0,0 +1,2 @@
+x¥Á 1E=§Ši@I6&AÄ‹Ø@6;ÃÌdGlßXƒ‡Ÿwø¯HkUÁa<h'äålÉyf)ÌÑÆ̘\
+”,çÄóeb´&¿u•å“ûÏUÚ.\iØߺ·ZºìÂz*Ònà<†Áä#m°Ö ;ÂJ\˜ºU­ùe¾8[>` \ No newline at end of file
diff --git a/tests/resources/revert/.gitted/objects/ee/c6adcb2f3ceca0cadeccfe01b19382252ece9b b/tests/resources/revert/.gitted/objects/ee/c6adcb2f3ceca0cadeccfe01b19382252ece9b
new file mode 100644
index 0000000..f59f3d4
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/ee/c6adcb2f3ceca0cadeccfe01b19382252ece9b
Binary files differ
diff --git a/tests/resources/revert/.gitted/objects/f4/e107c230d08a60fb419d19869f1f282b272d9c b/tests/resources/revert/.gitted/objects/f4/e107c230d08a60fb419d19869f1f282b272d9c
new file mode 100644
index 0000000..029da1b
--- /dev/null
+++ b/tests/resources/revert/.gitted/objects/f4/e107c230d08a60fb419d19869f1f282b272d9c
Binary files differ
diff --git a/tests/resources/revert/.gitted/refs/heads/master b/tests/resources/revert/.gitted/refs/heads/master
new file mode 100644
index 0000000..180f407
--- /dev/null
+++ b/tests/resources/revert/.gitted/refs/heads/master
@@ -0,0 +1 @@
+2d440f2b3147d3dc7ad1085813478d6d869d5a4d
diff --git a/tests/resources/revert/.gitted/refs/heads/merges b/tests/resources/revert/.gitted/refs/heads/merges
new file mode 100644
index 0000000..6533a94
--- /dev/null
+++ b/tests/resources/revert/.gitted/refs/heads/merges
@@ -0,0 +1 @@
+5acdc74af27172ec491d213ee36cea7eb9ef2579
diff --git a/tests/resources/revert/.gitted/refs/heads/merges-branch b/tests/resources/revert/.gitted/refs/heads/merges-branch
new file mode 100644
index 0000000..febb29c
--- /dev/null
+++ b/tests/resources/revert/.gitted/refs/heads/merges-branch
@@ -0,0 +1 @@
+13ee9cd5d8e1023c218e0e1ea684ec0c582b5050
diff --git a/tests/resources/revert/.gitted/refs/heads/reverted-branch b/tests/resources/revert/.gitted/refs/heads/reverted-branch
new file mode 100644
index 0000000..16bb7a2
--- /dev/null
+++ b/tests/resources/revert/.gitted/refs/heads/reverted-branch
@@ -0,0 +1 @@
+52c95c4264245469a0617e289a7d737f156826b4
diff --git a/tests/resources/revert/.gitted/refs/heads/two b/tests/resources/revert/.gitted/refs/heads/two
new file mode 100644
index 0000000..69d76cf
--- /dev/null
+++ b/tests/resources/revert/.gitted/refs/heads/two
@@ -0,0 +1 @@
+75ec9929465623f17ff3ad68c0438ea56faba815
diff --git a/tests/resources/revert/file1.txt b/tests/resources/revert/file1.txt
new file mode 100644
index 0000000..84b2259
--- /dev/null
+++ b/tests/resources/revert/file1.txt
@@ -0,0 +1,14 @@
+!File one!
+!File one!
+File one!
+File one
+File one
+File one
+File one
+File one
+File one
+File one
+File one!
+!File one!
+!File one!
+!File one!
diff --git a/tests/resources/revert/file2.txt b/tests/resources/revert/file2.txt
new file mode 100644
index 0000000..acb5747
--- /dev/null
+++ b/tests/resources/revert/file2.txt
@@ -0,0 +1,16 @@
+File two
+File two
+File two
+File two
+File two
+File two
+File two
+File two
+File two
+File two
+File two
+File two
+File two
+File two
+File two
+File two
diff --git a/tests/resources/revert/file3.txt b/tests/resources/revert/file3.txt
new file mode 100644
index 0000000..b033059
--- /dev/null
+++ b/tests/resources/revert/file3.txt
@@ -0,0 +1,16 @@
+File three
+File three
+File three
+File three
+File three
+File three
+File three
+File three
+File three
+File three
+File three
+File three
+File three
+File three
+File three
+File three
diff --git a/tests/resources/revert/file6.txt b/tests/resources/revert/file6.txt
new file mode 100644
index 0000000..5c0cd5d
--- /dev/null
+++ b/tests/resources/revert/file6.txt
@@ -0,0 +1,14 @@
+File six, actually!
+File four!
+File four!
+File four!
+File four!
+File four!
+File four!
+File four!
+File four!
+File four!
+File four!
+File four!
+File four!
+File four!
diff --git a/tests/resources/revwalk.git/HEAD b/tests/resources/revwalk.git/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/revwalk.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/revwalk.git/config b/tests/resources/revwalk.git/config
new file mode 100644
index 0000000..7c968c3
--- /dev/null
+++ b/tests/resources/revwalk.git/config
@@ -0,0 +1,6 @@
+[core]
+ bare = true
+ repositoryformatversion = 0
+ filemode = false
+ symlinks = false
+ ignorecase = true
diff --git a/tests/resources/revwalk.git/description b/tests/resources/revwalk.git/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/revwalk.git/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/revwalk.git/objects/info/packs b/tests/resources/revwalk.git/objects/info/packs
new file mode 100644
index 0000000..d8d85b8
--- /dev/null
+++ b/tests/resources/revwalk.git/objects/info/packs
@@ -0,0 +1,2 @@
+P pack-9adacb9971981a1a3264fd473da5b800f2715959.pack
+
diff --git a/tests/resources/revwalk.git/objects/pack/pack-9adacb9971981a1a3264fd473da5b800f2715959.idx b/tests/resources/revwalk.git/objects/pack/pack-9adacb9971981a1a3264fd473da5b800f2715959.idx
new file mode 100644
index 0000000..e157b38
--- /dev/null
+++ b/tests/resources/revwalk.git/objects/pack/pack-9adacb9971981a1a3264fd473da5b800f2715959.idx
Binary files differ
diff --git a/tests/resources/revwalk.git/objects/pack/pack-9adacb9971981a1a3264fd473da5b800f2715959.pack b/tests/resources/revwalk.git/objects/pack/pack-9adacb9971981a1a3264fd473da5b800f2715959.pack
new file mode 100644
index 0000000..2a61f94
--- /dev/null
+++ b/tests/resources/revwalk.git/objects/pack/pack-9adacb9971981a1a3264fd473da5b800f2715959.pack
Binary files differ
diff --git a/tests/resources/revwalk.git/packed-refs b/tests/resources/revwalk.git/packed-refs
new file mode 100644
index 0000000..905a3db
--- /dev/null
+++ b/tests/resources/revwalk.git/packed-refs
@@ -0,0 +1,7 @@
+# pack-refs with: peeled fully-peeled sorted
+3ae0f53011bdb7e68f99bde4943449f36c1c318a refs/heads/A
+061978578d7c9ff2ba92dd36d31fd8d809871030 refs/heads/B
+743398b425d6c216d6cfaae3786b5bc436393ae5 refs/heads/C
+790ba0facf6fd103699a5c40cd19dad277ff49cd refs/heads/D
+d3d783066cf7d95def6844b9c5118c1e7bcce7df refs/heads/E
+d3d783066cf7d95def6844b9c5118c1e7bcce7df refs/heads/master
diff --git a/tests/resources/revwalk.git/refs/.gitkeep b/tests/resources/revwalk.git/refs/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/revwalk.git/refs/.gitkeep
diff --git a/tests/resources/self-signed.pem b/tests/resources/self-signed.pem
new file mode 100644
index 0000000..e13417e
--- /dev/null
+++ b/tests/resources/self-signed.pem
@@ -0,0 +1,20 @@
+-----BEGIN CERTIFICATE-----
+MIIDUzCCAjsCFAb11im6DYQyGJ0GNQCIehXtegq6MA0GCSqGSIb3DQEBCwUAMGYx
+CzAJBgNVBAYTAlVTMRYwFAYDVQQIDA1NYXNzYWNodXNldHRzMRIwEAYDVQQHDAlD
+YW1icmlkZ2UxEDAOBgNVBAoMB2xpYmdpdDIxGTAXBgNVBAMMEHRlc3QubGliZ2l0
+Mi5vcmcwHhcNMjEwODMwMDAyMTQyWhcNMzEwODI4MDAyMTQyWjBmMQswCQYDVQQG
+EwJVUzEWMBQGA1UECAwNTWFzc2FjaHVzZXR0czESMBAGA1UEBwwJQ2FtYnJpZGdl
+MRAwDgYDVQQKDAdsaWJnaXQyMRkwFwYDVQQDDBB0ZXN0LmxpYmdpdDIub3JnMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtqe6b1vnMni+z8Z+a2bGtykI
+ITvBged15rn+0qG6Fz+sn9bYG+ceFupztFfoN3cVpUgQDBTzr3CaAx036BlV0z8i
+CrG0Oh/XGL+9TITQLumEe4iGi8NoMSujBAyXPSNgmpzDmCTGrNFfmq3HzUtO8t3x
+i8OT7d9qCVjFimLvZbgnfHGQ38xvt1XyPgYIVqDQczmMEZ5BdYWB0A1VmnWuP2dH
+BgjwPEC3HwMmm1+PL0VoPTdvE5Su092Qdt8QsiA56466DQyll1d/omnOJfrK7z0N
+OnfDmnDpARSTy6vDofEAYUQoc3dyvBUk8IIzv2UDcR7fTVvYqseQReIOTEnXmQID
+AQABMA0GCSqGSIb3DQEBCwUAA4IBAQBmUEq+JhwWTbB5ODGOKrMG1fKJ+sf6ZH6M
+c4BgLEcdoi/nOTfPuw+ols72LuhH7NKaEcqxWev0jGF0WKqMcM8AGVbywZJ3mBWo
+sKdh6rAGFNkikW4TzhjtDfFbMR45Didl28Be7ieHQL4CQ0Lse3RMOxp250WpiEYV
+W2hIKMwIqOLKGShVD7lI+eHlv+QSH4yOYKHfRHve8s82Tac5OXinc8CJm9ySOtkO
+MfLgfkHtHdFBnV6OVbf4p/596MfMXdwT/bBxT6WPkDGc1AYhoDlmLFTpRgHIDCSK
+2wgV+qHppl7Kn+p3mFQ9sW/1IaRd+jNZOrgZ8Uu5tJ00OaqR/LVG
+-----END CERTIFICATE-----
diff --git a/tests/resources/sha1/empty b/tests/resources/sha1/empty
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/sha1/empty
diff --git a/tests/resources/sha1/hello_c b/tests/resources/sha1/hello_c
new file mode 100644
index 0000000..45950b2
--- /dev/null
+++ b/tests/resources/sha1/hello_c
@@ -0,0 +1,6 @@
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+ printf("Hello, %s\n", "world");
+}
diff --git a/tests/resources/sha1/shattered-1.pdf b/tests/resources/sha1/shattered-1.pdf
new file mode 100644
index 0000000..ba9aaa1
--- /dev/null
+++ b/tests/resources/sha1/shattered-1.pdf
Binary files differ
diff --git a/tests/resources/shallow.git/HEAD b/tests/resources/shallow.git/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/shallow.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/shallow.git/config b/tests/resources/shallow.git/config
new file mode 100644
index 0000000..a88b74b
--- /dev/null
+++ b/tests/resources/shallow.git/config
@@ -0,0 +1,8 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = true
+ ignorecase = true
+ precomposeunicode = false
+[remote "origin"]
+ url = file://testrepo.git
diff --git a/tests/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.idx b/tests/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.idx
new file mode 100644
index 0000000..bfc7d24
--- /dev/null
+++ b/tests/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.idx
Binary files differ
diff --git a/tests/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.pack b/tests/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.pack
new file mode 100644
index 0000000..ccc6932
--- /dev/null
+++ b/tests/resources/shallow.git/objects/pack/pack-706e49b161700946489570d96153e5be4dc31ad4.pack
Binary files differ
diff --git a/tests/resources/shallow.git/packed-refs b/tests/resources/shallow.git/packed-refs
new file mode 100644
index 0000000..97eed74
--- /dev/null
+++ b/tests/resources/shallow.git/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+a65fedf39aefe402d3bb6e24df4d4f5fe4547750 refs/heads/master
diff --git a/tests/resources/shallow.git/refs/.gitkeep b/tests/resources/shallow.git/refs/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/shallow.git/refs/.gitkeep
diff --git a/tests/resources/shallow.git/shallow b/tests/resources/shallow.git/shallow
new file mode 100644
index 0000000..9536ad8
--- /dev/null
+++ b/tests/resources/shallow.git/shallow
@@ -0,0 +1 @@
+be3563ae3f795b2b4353bcce3a527ad0a4f7f644
diff --git a/tests/resources/short_tag.git/HEAD b/tests/resources/short_tag.git/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/short_tag.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/short_tag.git/config b/tests/resources/short_tag.git/config
new file mode 100644
index 0000000..a4ef456
--- /dev/null
+++ b/tests/resources/short_tag.git/config
@@ -0,0 +1,5 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = true
+ logallrefupdates = true
diff --git a/tests/resources/short_tag.git/index b/tests/resources/short_tag.git/index
new file mode 100644
index 0000000..87fef78
--- /dev/null
+++ b/tests/resources/short_tag.git/index
Binary files differ
diff --git a/tests/resources/short_tag.git/objects/4a/5ed60bafcf4638b7c8356bd4ce1916bfede93c b/tests/resources/short_tag.git/objects/4a/5ed60bafcf4638b7c8356bd4ce1916bfede93c
new file mode 100644
index 0000000..aeb4e4b
--- /dev/null
+++ b/tests/resources/short_tag.git/objects/4a/5ed60bafcf4638b7c8356bd4ce1916bfede93c
Binary files differ
diff --git a/tests/resources/short_tag.git/objects/4d/5fcadc293a348e88f777dc0920f11e7d71441c b/tests/resources/short_tag.git/objects/4d/5fcadc293a348e88f777dc0920f11e7d71441c
new file mode 100644
index 0000000..806ce71
--- /dev/null
+++ b/tests/resources/short_tag.git/objects/4d/5fcadc293a348e88f777dc0920f11e7d71441c
Binary files differ
diff --git a/tests/resources/short_tag.git/objects/5d/a7760512a953e3c7c4e47e4392c7a4338fb729 b/tests/resources/short_tag.git/objects/5d/a7760512a953e3c7c4e47e4392c7a4338fb729
new file mode 100644
index 0000000..1192707
--- /dev/null
+++ b/tests/resources/short_tag.git/objects/5d/a7760512a953e3c7c4e47e4392c7a4338fb729
@@ -0,0 +1 @@
+xÌM‚0@aלb. ií%1Æ—pcÚé@Š”˜˜èé-Ëï-û¤6§&Bí E+‚pÐV¹Ð¡SƆ¨‘d/m(R¯°áïJ€%çÄ ×ÇR^‘vÜÒÊ©,GiƒÇ–Þðñ <Ó´3\©º­n‡ïcöinëåRé„SögÑ Ñüëu1 \ No newline at end of file
diff --git a/tests/resources/short_tag.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/short_tag.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644
index 0000000..7112238
--- /dev/null
+++ b/tests/resources/short_tag.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests/resources/short_tag.git/packed-refs b/tests/resources/short_tag.git/packed-refs
new file mode 100644
index 0000000..ca5197e
--- /dev/null
+++ b/tests/resources/short_tag.git/packed-refs
@@ -0,0 +1 @@
+5da7760512a953e3c7c4e47e4392c7a4338fb729 refs/tags/no_description
diff --git a/tests/resources/short_tag.git/refs/heads/master b/tests/resources/short_tag.git/refs/heads/master
new file mode 100644
index 0000000..fcefd1e
--- /dev/null
+++ b/tests/resources/short_tag.git/refs/heads/master
@@ -0,0 +1 @@
+4a5ed60bafcf4638b7c8356bd4ce1916bfede93c
diff --git a/tests/resources/splitindex/.gitted/HEAD b/tests/resources/splitindex/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/splitindex/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/splitindex/.gitted/config b/tests/resources/splitindex/.gitted/config
new file mode 100644
index 0000000..e9d0b6d
--- /dev/null
+++ b/tests/resources/splitindex/.gitted/config
@@ -0,0 +1,8 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
+ precomposeunicode = true
+ splitIndex = true
diff --git a/tests/resources/splitindex/.gitted/index b/tests/resources/splitindex/.gitted/index
new file mode 100644
index 0000000..ff34488
--- /dev/null
+++ b/tests/resources/splitindex/.gitted/index
Binary files differ
diff --git a/tests/resources/splitindex/.gitted/info/exclude b/tests/resources/splitindex/.gitted/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/splitindex/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/splitindex/.gitted/objects/.gitkeep b/tests/resources/splitindex/.gitted/objects/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/splitindex/.gitted/objects/.gitkeep
diff --git a/tests/resources/splitindex/.gitted/refs/.gitkeep b/tests/resources/splitindex/.gitted/refs/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/splitindex/.gitted/refs/.gitkeep
diff --git a/tests/resources/splitindex/.gitted/sharedindex.39d890139ee5356c7ef572216cebcd27aa41f9df b/tests/resources/splitindex/.gitted/sharedindex.39d890139ee5356c7ef572216cebcd27aa41f9df
new file mode 100644
index 0000000..3330d71
--- /dev/null
+++ b/tests/resources/splitindex/.gitted/sharedindex.39d890139ee5356c7ef572216cebcd27aa41f9df
Binary files differ
diff --git a/tests/resources/status/.gitted/COMMIT_EDITMSG b/tests/resources/status/.gitted/COMMIT_EDITMSG
new file mode 100644
index 0000000..1a25cd4
--- /dev/null
+++ b/tests/resources/status/.gitted/COMMIT_EDITMSG
@@ -0,0 +1 @@
+Add a file which name should appear before the "subdir/" folder while being dealt with by the treewalker
diff --git a/tests/resources/status/.gitted/HEAD b/tests/resources/status/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/status/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/status/.gitted/ORIG_HEAD b/tests/resources/status/.gitted/ORIG_HEAD
new file mode 100644
index 0000000..b46871f
--- /dev/null
+++ b/tests/resources/status/.gitted/ORIG_HEAD
@@ -0,0 +1 @@
+735b6a258cd196a8f7c9428419b02c1dca93fd75
diff --git a/tests/resources/status/.gitted/config b/tests/resources/status/.gitted/config
new file mode 100644
index 0000000..af10792
--- /dev/null
+++ b/tests/resources/status/.gitted/config
@@ -0,0 +1,6 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
diff --git a/tests/resources/status/.gitted/description b/tests/resources/status/.gitted/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/status/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/status/.gitted/index b/tests/resources/status/.gitted/index
new file mode 100644
index 0000000..2af99a1
--- /dev/null
+++ b/tests/resources/status/.gitted/index
Binary files differ
diff --git a/tests/resources/status/.gitted/info/exclude b/tests/resources/status/.gitted/info/exclude
new file mode 100644
index 0000000..0c4042a
--- /dev/null
+++ b/tests/resources/status/.gitted/info/exclude
@@ -0,0 +1,8 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
+ignored*
+
diff --git a/tests/resources/status/.gitted/logs/HEAD b/tests/resources/status/.gitted/logs/HEAD
new file mode 100644
index 0000000..7b95b3c
--- /dev/null
+++ b/tests/resources/status/.gitted/logs/HEAD
@@ -0,0 +1,3 @@
+0000000000000000000000000000000000000000 0017bd4ab1ec30440b17bae1680cff124ab5f1f6 Jason Penny <jasonpenny4@gmail.com> 1308050070 -0400 commit (initial): initial
+0017bd4ab1ec30440b17bae1680cff124ab5f1f6 735b6a258cd196a8f7c9428419b02c1dca93fd75 Jason Penny <jasonpenny4@gmail.com> 1308954538 -0400 commit: add subdir
+735b6a258cd196a8f7c9428419b02c1dca93fd75 26a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f nulltoken <emeric.fermas@gmail.com> 1319911544 +0200 commit: Add a file which name should appear before the "subdir/" folder while being dealt with by the treewalker
diff --git a/tests/resources/status/.gitted/logs/refs/heads/master b/tests/resources/status/.gitted/logs/refs/heads/master
new file mode 100644
index 0000000..7b95b3c
--- /dev/null
+++ b/tests/resources/status/.gitted/logs/refs/heads/master
@@ -0,0 +1,3 @@
+0000000000000000000000000000000000000000 0017bd4ab1ec30440b17bae1680cff124ab5f1f6 Jason Penny <jasonpenny4@gmail.com> 1308050070 -0400 commit (initial): initial
+0017bd4ab1ec30440b17bae1680cff124ab5f1f6 735b6a258cd196a8f7c9428419b02c1dca93fd75 Jason Penny <jasonpenny4@gmail.com> 1308954538 -0400 commit: add subdir
+735b6a258cd196a8f7c9428419b02c1dca93fd75 26a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f nulltoken <emeric.fermas@gmail.com> 1319911544 +0200 commit: Add a file which name should appear before the "subdir/" folder while being dealt with by the treewalker
diff --git a/tests/resources/status/.gitted/objects/00/17bd4ab1ec30440b17bae1680cff124ab5f1f6 b/tests/resources/status/.gitted/objects/00/17bd4ab1ec30440b17bae1680cff124ab5f1f6
new file mode 100644
index 0000000..b256d95
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/00/17bd4ab1ec30440b17bae1680cff124ab5f1f6
@@ -0,0 +1,2 @@
+xA E]sŠ¹€fh)‰1]»ò
+#SÀTºðö¶Wp÷ßK^~¨9§šÜ¡-"àC'Ø…)FvõbƒvÉ Þ"¶wŽŽ¼EÅk{Ö®ü©nRÊίÞû6ã#sšO¡æ 舄pDƒ¨6»6ù3W©¤–xV?¨Å9é \ No newline at end of file
diff --git a/tests/resources/status/.gitted/objects/06/1d42a44cacde5726057b67558821d95db96f19 b/tests/resources/status/.gitted/objects/06/1d42a44cacde5726057b67558821d95db96f19
new file mode 100644
index 0000000..82e02cb
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/06/1d42a44cacde5726057b67558821d95db96f19
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/18/88c805345ba265b0ee9449b8877b6064592058 b/tests/resources/status/.gitted/objects/18/88c805345ba265b0ee9449b8877b6064592058
new file mode 100644
index 0000000..e3cad2f
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/18/88c805345ba265b0ee9449b8877b6064592058
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/19/d9cc8584ac2c7dcf57d2680375e80f099dc481 b/tests/resources/status/.gitted/objects/19/d9cc8584ac2c7dcf57d2680375e80f099dc481
new file mode 100644
index 0000000..2d5e711
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/19/d9cc8584ac2c7dcf57d2680375e80f099dc481
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/26/a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f b/tests/resources/status/.gitted/objects/26/a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f
new file mode 100644
index 0000000..f7dddc4
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/26/a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f
@@ -0,0 +1,2 @@
+xMnÄ …»Î)¬ÙVšò† ªö(Ì€BDˆ¢Þ¾LÐöûüžž«¥¤RÈ·Þˆ@êà,Î9Ž‹òÜÌœtàìæ•ðNj6f`žM6Z;h©ì …ZÜÐÞp Ú™Y,37¯/Ü;42x­&<z¬ Ö#ç^Ÿ´Â
+µä®ZÁýëQ0å««å¸äÆp®æÞ™`lÛ²Ó?ñéÛ{@)œ1¹+‚=Ö#ö6°j#è‘à²Ö§öqP³®¨¥´>ÀægêìÏŸüÕÛ‰ùImú|½jñ \ No newline at end of file
diff --git a/tests/resources/status/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863 b/tests/resources/status/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863
new file mode 100644
index 0000000..d10ca63
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/32/504b727382542f9f089e24fddac5e78533e96c b/tests/resources/status/.gitted/objects/32/504b727382542f9f089e24fddac5e78533e96c
new file mode 100644
index 0000000..7fca67b
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/32/504b727382542f9f089e24fddac5e78533e96c
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/37/fcb02ccc1a85d1941e7f106d52dc3702dcf0d0 b/tests/resources/status/.gitted/objects/37/fcb02ccc1a85d1941e7f106d52dc3702dcf0d0
new file mode 100644
index 0000000..b75481b
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/37/fcb02ccc1a85d1941e7f106d52dc3702dcf0d0
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/45/2e4244b5d083ddf0460acf1ecc74db9dcfa11a b/tests/resources/status/.gitted/objects/45/2e4244b5d083ddf0460acf1ecc74db9dcfa11a
new file mode 100644
index 0000000..5b47461
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/45/2e4244b5d083ddf0460acf1ecc74db9dcfa11a
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/52/9a16e8e762d4acb7b9636ff540a00831f9155a b/tests/resources/status/.gitted/objects/52/9a16e8e762d4acb7b9636ff540a00831f9155a
new file mode 100644
index 0000000..615009a
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/52/9a16e8e762d4acb7b9636ff540a00831f9155a
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/53/ace0d1cc1145a5f4fe4f78a186a60263190733 b/tests/resources/status/.gitted/objects/53/ace0d1cc1145a5f4fe4f78a186a60263190733
new file mode 100644
index 0000000..cdb7e96
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/53/ace0d1cc1145a5f4fe4f78a186a60263190733
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/54/52d32f1dd538eb0405e8a83cc185f79e25e80f b/tests/resources/status/.gitted/objects/54/52d32f1dd538eb0405e8a83cc185f79e25e80f
new file mode 100644
index 0000000..a72dff6
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/54/52d32f1dd538eb0405e8a83cc185f79e25e80f
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/55/d316c9ba708999f1918e9677d01dfcae69c6b9 b/tests/resources/status/.gitted/objects/55/d316c9ba708999f1918e9677d01dfcae69c6b9
new file mode 100644
index 0000000..72807f3
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/55/d316c9ba708999f1918e9677d01dfcae69c6b9
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/70/bd9443ada07063e7fbf0b3ff5c13f7494d89c2 b/tests/resources/status/.gitted/objects/70/bd9443ada07063e7fbf0b3ff5c13f7494d89c2
new file mode 100644
index 0000000..3665a8f
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/70/bd9443ada07063e7fbf0b3ff5c13f7494d89c2
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/73/5b6a258cd196a8f7c9428419b02c1dca93fd75 b/tests/resources/status/.gitted/objects/73/5b6a258cd196a8f7c9428419b02c1dca93fd75
new file mode 100644
index 0000000..08e6fd2
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/73/5b6a258cd196a8f7c9428419b02c1dca93fd75
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/75/6e27627e67bfbc048d01ece5819c6de733d7ea b/tests/resources/status/.gitted/objects/75/6e27627e67bfbc048d01ece5819c6de733d7ea
new file mode 100644
index 0000000..8f3fa89
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/75/6e27627e67bfbc048d01ece5819c6de733d7ea
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/90/6ee7711f4f4928ddcb2a5f8fbc500deba0d2a8 b/tests/resources/status/.gitted/objects/90/6ee7711f4f4928ddcb2a5f8fbc500deba0d2a8
new file mode 100644
index 0000000..bb732b0
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/90/6ee7711f4f4928ddcb2a5f8fbc500deba0d2a8
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/90/b8c29d8ba39434d1c63e1b093daaa26e5bd972 b/tests/resources/status/.gitted/objects/90/b8c29d8ba39434d1c63e1b093daaa26e5bd972
new file mode 100644
index 0000000..7a96618
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/90/b8c29d8ba39434d1c63e1b093daaa26e5bd972
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/9c/2e02cdffa8d73e6c189074594477a6baf87960 b/tests/resources/status/.gitted/objects/9c/2e02cdffa8d73e6c189074594477a6baf87960
new file mode 100644
index 0000000..20a3c49
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/9c/2e02cdffa8d73e6c189074594477a6baf87960
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/a0/de7e0ac200c489c41c59dfa910154a70264e6e b/tests/resources/status/.gitted/objects/a0/de7e0ac200c489c41c59dfa910154a70264e6e
new file mode 100644
index 0000000..a1789c9
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/a0/de7e0ac200c489c41c59dfa910154a70264e6e
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/a6/191982709b746d5650e93c2acf34ef74e11504 b/tests/resources/status/.gitted/objects/a6/191982709b746d5650e93c2acf34ef74e11504
new file mode 100644
index 0000000..cc1f377
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/a6/191982709b746d5650e93c2acf34ef74e11504
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/a6/be623522ce87a1d862128ac42672604f7b468b b/tests/resources/status/.gitted/objects/a6/be623522ce87a1d862128ac42672604f7b468b
new file mode 100644
index 0000000..c472983
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/a6/be623522ce87a1d862128ac42672604f7b468b
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/aa/27a641456848200fdb7f7c99ba36f8a0952877 b/tests/resources/status/.gitted/objects/aa/27a641456848200fdb7f7c99ba36f8a0952877
new file mode 100644
index 0000000..a4669cc
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/aa/27a641456848200fdb7f7c99ba36f8a0952877
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46 b/tests/resources/status/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46
new file mode 100644
index 0000000..0b3611a
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/da/bc8af9bd6e9f5bbe96a176f1a24baf3d1f8916 b/tests/resources/status/.gitted/objects/da/bc8af9bd6e9f5bbe96a176f1a24baf3d1f8916
new file mode 100644
index 0000000..3e3c03c
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/da/bc8af9bd6e9f5bbe96a176f1a24baf3d1f8916
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/e8/ee89e15bbe9b20137715232387b3de5b28972e b/tests/resources/status/.gitted/objects/e8/ee89e15bbe9b20137715232387b3de5b28972e
new file mode 100644
index 0000000..cfc2413
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/e8/ee89e15bbe9b20137715232387b3de5b28972e
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/e9/b9107f290627c04d097733a10055af941f6bca b/tests/resources/status/.gitted/objects/e9/b9107f290627c04d097733a10055af941f6bca
new file mode 100644
index 0000000..1266d3e
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/e9/b9107f290627c04d097733a10055af941f6bca
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/ed/062903b8f6f3dccb2fa81117ba6590944ef9bd b/tests/resources/status/.gitted/objects/ed/062903b8f6f3dccb2fa81117ba6590944ef9bd
new file mode 100644
index 0000000..8fa8c17
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/ed/062903b8f6f3dccb2fa81117ba6590944ef9bd
Binary files differ
diff --git a/tests/resources/status/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf b/tests/resources/status/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf
new file mode 100644
index 0000000..974b72d
--- /dev/null
+++ b/tests/resources/status/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf
Binary files differ
diff --git a/tests/resources/status/.gitted/refs/heads/master b/tests/resources/status/.gitted/refs/heads/master
new file mode 100644
index 0000000..3e2e2a0
--- /dev/null
+++ b/tests/resources/status/.gitted/refs/heads/master
@@ -0,0 +1 @@
+26a125ee1bfc5df1e1b2e9441bbe63c8a7ae989f
diff --git a/tests/resources/status/current_file b/tests/resources/status/current_file
new file mode 100644
index 0000000..a0de7e0
--- /dev/null
+++ b/tests/resources/status/current_file
@@ -0,0 +1 @@
+current_file
diff --git a/tests/resources/status/ignored_file b/tests/resources/status/ignored_file
new file mode 100644
index 0000000..6a79f80
--- /dev/null
+++ b/tests/resources/status/ignored_file
@@ -0,0 +1 @@
+ignored_file
diff --git a/tests/resources/status/modified_file b/tests/resources/status/modified_file
new file mode 100644
index 0000000..0a53963
--- /dev/null
+++ b/tests/resources/status/modified_file
@@ -0,0 +1,2 @@
+modified_file
+modified_file
diff --git a/tests/resources/status/new_file b/tests/resources/status/new_file
new file mode 100644
index 0000000..d4fa860
--- /dev/null
+++ b/tests/resources/status/new_file
@@ -0,0 +1 @@
+new_file
diff --git a/tests/resources/status/staged_changes b/tests/resources/status/staged_changes
new file mode 100644
index 0000000..55d316c
--- /dev/null
+++ b/tests/resources/status/staged_changes
@@ -0,0 +1,2 @@
+staged_changes
+staged_changes
diff --git a/tests/resources/status/staged_changes_modified_file b/tests/resources/status/staged_changes_modified_file
new file mode 100644
index 0000000..011c344
--- /dev/null
+++ b/tests/resources/status/staged_changes_modified_file
@@ -0,0 +1,3 @@
+staged_changes_modified_file
+staged_changes_modified_file
+staged_changes_modified_file
diff --git a/tests/resources/status/staged_delete_modified_file b/tests/resources/status/staged_delete_modified_file
new file mode 100644
index 0000000..dabc8af
--- /dev/null
+++ b/tests/resources/status/staged_delete_modified_file
@@ -0,0 +1 @@
+staged_delete_modified_file
diff --git a/tests/resources/status/staged_new_file b/tests/resources/status/staged_new_file
new file mode 100644
index 0000000..529a16e
--- /dev/null
+++ b/tests/resources/status/staged_new_file
@@ -0,0 +1 @@
+staged_new_file
diff --git a/tests/resources/status/staged_new_file_modified_file b/tests/resources/status/staged_new_file_modified_file
new file mode 100644
index 0000000..8b090c0
--- /dev/null
+++ b/tests/resources/status/staged_new_file_modified_file
@@ -0,0 +1,2 @@
+staged_new_file_modified_file
+staged_new_file_modified_file
diff --git a/tests/resources/status/subdir.txt b/tests/resources/status/subdir.txt
new file mode 100644
index 0000000..e8ee89e
--- /dev/null
+++ b/tests/resources/status/subdir.txt
@@ -0,0 +1,2 @@
+Is it a bird?
+Is it a plane?
diff --git a/tests/resources/status/subdir/current_file b/tests/resources/status/subdir/current_file
new file mode 100644
index 0000000..53ace0d
--- /dev/null
+++ b/tests/resources/status/subdir/current_file
@@ -0,0 +1 @@
+subdir/current_file
diff --git a/tests/resources/status/subdir/modified_file b/tests/resources/status/subdir/modified_file
new file mode 100644
index 0000000..57274b7
--- /dev/null
+++ b/tests/resources/status/subdir/modified_file
@@ -0,0 +1,2 @@
+subdir/modified_file
+subdir/modified_file
diff --git a/tests/resources/status/subdir/new_file b/tests/resources/status/subdir/new_file
new file mode 100644
index 0000000..80a86a6
--- /dev/null
+++ b/tests/resources/status/subdir/new_file
@@ -0,0 +1 @@
+subdir/new_file
diff --git a/tests/resources/status/è¿™ b/tests/resources/status/è¿™
new file mode 100644
index 0000000..f0ff9a1
--- /dev/null
+++ b/tests/resources/status/è¿™
@@ -0,0 +1 @@
+This
diff --git a/tests/resources/sub.git/HEAD b/tests/resources/sub.git/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/sub.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/sub.git/config b/tests/resources/sub.git/config
new file mode 100644
index 0000000..78387c5
--- /dev/null
+++ b/tests/resources/sub.git/config
@@ -0,0 +1,8 @@
+[core]
+ repositoryformatversion = 0
+ filemode = false
+ bare = false
+ logallrefupdates = true
+ symlinks = false
+ ignorecase = true
+ hideDotFiles = dotGitOnly
diff --git a/tests/resources/sub.git/index b/tests/resources/sub.git/index
new file mode 100644
index 0000000..54be69e
--- /dev/null
+++ b/tests/resources/sub.git/index
Binary files differ
diff --git a/tests/resources/sub.git/logs/HEAD b/tests/resources/sub.git/logs/HEAD
new file mode 100644
index 0000000..f636268
--- /dev/null
+++ b/tests/resources/sub.git/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 b7a59b3f4ea13b985f8a1e0d3757d5cd3331add8 Edward Thomson <ethomson@microsoft.com> 1442522322 -0400 commit (initial): Initial revision
diff --git a/tests/resources/sub.git/logs/refs/heads/master b/tests/resources/sub.git/logs/refs/heads/master
new file mode 100644
index 0000000..f636268
--- /dev/null
+++ b/tests/resources/sub.git/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 b7a59b3f4ea13b985f8a1e0d3757d5cd3331add8 Edward Thomson <ethomson@microsoft.com> 1442522322 -0400 commit (initial): Initial revision
diff --git a/tests/resources/sub.git/objects/10/ddd6d257e01349d514541981aeecea6b2e741d b/tests/resources/sub.git/objects/10/ddd6d257e01349d514541981aeecea6b2e741d
new file mode 100644
index 0000000..a095b3f
--- /dev/null
+++ b/tests/resources/sub.git/objects/10/ddd6d257e01349d514541981aeecea6b2e741d
Binary files differ
diff --git a/tests/resources/sub.git/objects/17/6a458f94e0ea5272ce67c36bf30b6be9caf623 b/tests/resources/sub.git/objects/17/6a458f94e0ea5272ce67c36bf30b6be9caf623
new file mode 100644
index 0000000..ef83166
--- /dev/null
+++ b/tests/resources/sub.git/objects/17/6a458f94e0ea5272ce67c36bf30b6be9caf623
Binary files differ
diff --git a/tests/resources/sub.git/objects/94/c7d78d85c933d1d95b56bc2de01833ba8559fb b/tests/resources/sub.git/objects/94/c7d78d85c933d1d95b56bc2de01833ba8559fb
new file mode 100644
index 0000000..9adc11d
--- /dev/null
+++ b/tests/resources/sub.git/objects/94/c7d78d85c933d1d95b56bc2de01833ba8559fb
Binary files differ
diff --git a/tests/resources/sub.git/objects/b7/a59b3f4ea13b985f8a1e0d3757d5cd3331add8 b/tests/resources/sub.git/objects/b7/a59b3f4ea13b985f8a1e0d3757d5cd3331add8
new file mode 100644
index 0000000..221b55d
--- /dev/null
+++ b/tests/resources/sub.git/objects/b7/a59b3f4ea13b985f8a1e0d3757d5cd3331add8
Binary files differ
diff --git a/tests/resources/sub.git/objects/d0/ee23c41b28746d7e822511d7838bce784ae773 b/tests/resources/sub.git/objects/d0/ee23c41b28746d7e822511d7838bce784ae773
new file mode 100644
index 0000000..d9bb9c8
--- /dev/null
+++ b/tests/resources/sub.git/objects/d0/ee23c41b28746d7e822511d7838bce784ae773
Binary files differ
diff --git a/tests/resources/sub.git/refs/heads/master b/tests/resources/sub.git/refs/heads/master
new file mode 100644
index 0000000..0e4d6e2
--- /dev/null
+++ b/tests/resources/sub.git/refs/heads/master
@@ -0,0 +1 @@
+b7a59b3f4ea13b985f8a1e0d3757d5cd3331add8
diff --git a/tests/resources/submod2/.gitted/HEAD b/tests/resources/submod2/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/submod2/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submod2/.gitted/config b/tests/resources/submod2/.gitted/config
new file mode 100644
index 0000000..abc4207
--- /dev/null
+++ b/tests/resources/submod2/.gitted/config
@@ -0,0 +1,20 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
+[submodule "sm_missing_commits"]
+ url = ../submod2_target
+[submodule "sm_unchanged"]
+ url = ../submod2_target
+[submodule "sm_changed_file"]
+ url = ../submod2_target
+[submodule "sm_changed_index"]
+ url = ../submod2_target
+[submodule "sm_changed_head"]
+ url = ../submod2_target
+[submodule "sm_changed_untracked_file"]
+ url = ../submod2_target
+[submodule "sm_added_and_uncommited"]
+ url = ../submod2_target
diff --git a/tests/resources/submod2/.gitted/description b/tests/resources/submod2/.gitted/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/submod2/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/submod2/.gitted/index b/tests/resources/submod2/.gitted/index
new file mode 100644
index 0000000..0c17e86
--- /dev/null
+++ b/tests/resources/submod2/.gitted/index
Binary files differ
diff --git a/tests/resources/submod2/.gitted/info/exclude b/tests/resources/submod2/.gitted/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/submod2/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/submod2/.gitted/logs/HEAD b/tests/resources/submod2/.gitted/logs/HEAD
new file mode 100644
index 0000000..2cf2ca7
--- /dev/null
+++ b/tests/resources/submod2/.gitted/logs/HEAD
@@ -0,0 +1,4 @@
+0000000000000000000000000000000000000000 14fe9ccf104058df25e0a08361c4494e167ef243 Russell Belfer <rb@github.com> 1342559771 -0700 commit (initial): Initial commit
+14fe9ccf104058df25e0a08361c4494e167ef243 a9104bf89e911387244ef499413960ba472066d9 Russell Belfer <rb@github.com> 1342559831 -0700 commit: Adding a submodule
+a9104bf89e911387244ef499413960ba472066d9 5901da4f1c67756eeadc5121d206bec2431f253b Russell Belfer <rb@github.com> 1342560036 -0700 commit: Updating submodule
+5901da4f1c67756eeadc5121d206bec2431f253b 7484482eb8db738cafa696993664607500a3f2b9 Russell Belfer <rb@github.com> 1342560288 -0700 commit: Adding a bunch more test content
diff --git a/tests/resources/submod2/.gitted/logs/refs/heads/master b/tests/resources/submod2/.gitted/logs/refs/heads/master
new file mode 100644
index 0000000..2cf2ca7
--- /dev/null
+++ b/tests/resources/submod2/.gitted/logs/refs/heads/master
@@ -0,0 +1,4 @@
+0000000000000000000000000000000000000000 14fe9ccf104058df25e0a08361c4494e167ef243 Russell Belfer <rb@github.com> 1342559771 -0700 commit (initial): Initial commit
+14fe9ccf104058df25e0a08361c4494e167ef243 a9104bf89e911387244ef499413960ba472066d9 Russell Belfer <rb@github.com> 1342559831 -0700 commit: Adding a submodule
+a9104bf89e911387244ef499413960ba472066d9 5901da4f1c67756eeadc5121d206bec2431f253b Russell Belfer <rb@github.com> 1342560036 -0700 commit: Updating submodule
+5901da4f1c67756eeadc5121d206bec2431f253b 7484482eb8db738cafa696993664607500a3f2b9 Russell Belfer <rb@github.com> 1342560288 -0700 commit: Adding a bunch more test content
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/HEAD b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/config b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/config
new file mode 100644
index 0000000..2d0583e
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/config
@@ -0,0 +1,13 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../sm_added_and_uncommited
+ ignorecase = true
+[remote "origin"]
+ fetch = +refs/heads/*:refs/remotes/origin/*
+ url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/description b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/index b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/index
new file mode 100644
index 0000000..65140a5
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/index
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/info/exclude b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/HEAD b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/HEAD
new file mode 100644
index 0000000..53753e7
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560316 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/heads/master
new file mode 100644
index 0000000..53753e7
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560316 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..53753e7
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/logs/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560316 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 0000000..f4b7094
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 0000000..56c845e
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 0000000..bd179b5
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 0000000..ccf49bd
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 0000000..5302906
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 0000000..38c791e
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 0000000..a26d299
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 0000000..83d1ba4
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 0000000..6d27af8
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 0000000..1745884
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 0000000..83cc29f
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 0000000..55bda40
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/packed-refs b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/packed-refs
new file mode 100644
index 0000000..5a4ebc4
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/heads/master
new file mode 100644
index 0000000..e12c44d
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/heads/master
@@ -0,0 +1 @@
+480095882d281ed676fe5b863569520e54a7d5c0
diff --git a/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..6efe28f
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_added_and_uncommited/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_file/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/config b/tests/resources/submod2/.gitted/modules/sm_changed_file/config
new file mode 100644
index 0000000..10cc250
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/config
@@ -0,0 +1,13 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../sm_changed_file
+ ignorecase = true
+[remote "origin"]
+ fetch = +refs/heads/*:refs/remotes/origin/*
+ url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/description b/tests/resources/submod2/.gitted/modules/sm_changed_file/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/index b/tests/resources/submod2/.gitted/modules/sm_changed_file/index
new file mode 100644
index 0000000..6914a3b
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/index
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/info/exclude b/tests/resources/submod2/.gitted/modules/sm_changed_file/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/logs/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_file/logs/HEAD
new file mode 100644
index 0000000..e5cb63f
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560173 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/heads/master
new file mode 100644
index 0000000..e5cb63f
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560173 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..e5cb63f
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/logs/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560173 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 0000000..f4b7094
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 0000000..56c845e
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 0000000..bd179b5
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 0000000..ccf49bd
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 0000000..5302906
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 0000000..38c791e
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 0000000..a26d299
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 0000000..83d1ba4
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 0000000..6d27af8
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 0000000..1745884
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 0000000..83cc29f
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 0000000..55bda40
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/packed-refs b/tests/resources/submod2/.gitted/modules/sm_changed_file/packed-refs
new file mode 100644
index 0000000..5a4ebc4
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_changed_file/refs/heads/master
new file mode 100644
index 0000000..e12c44d
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/refs/heads/master
@@ -0,0 +1 @@
+480095882d281ed676fe5b863569520e54a7d5c0
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_file/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_file/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..6efe28f
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_file/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/COMMIT_EDITMSG b/tests/resources/submod2/.gitted/modules/sm_changed_head/COMMIT_EDITMSG
new file mode 100644
index 0000000..6b8d1e3
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/COMMIT_EDITMSG
@@ -0,0 +1 @@
+Making a change in a submodule
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_head/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/config b/tests/resources/submod2/.gitted/modules/sm_changed_head/config
new file mode 100644
index 0000000..7d00253
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/config
@@ -0,0 +1,13 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../sm_changed_head
+ ignorecase = true
+[remote "origin"]
+ fetch = +refs/heads/*:refs/remotes/origin/*
+ url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/description b/tests/resources/submod2/.gitted/modules/sm_changed_head/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/index b/tests/resources/submod2/.gitted/modules/sm_changed_head/index
new file mode 100644
index 0000000..728fa29
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/index
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/info/exclude b/tests/resources/submod2/.gitted/modules/sm_changed_head/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/logs/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_head/logs/HEAD
new file mode 100644
index 0000000..cabdeb2
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/logs/HEAD
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560179 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
+480095882d281ed676fe5b863569520e54a7d5c0 3d9386c507f6b093471a3e324085657a3c2b4247 Russell Belfer <rb@github.com> 1342560431 -0700 commit: Making a change in a submodule
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/heads/master
new file mode 100644
index 0000000..cabdeb2
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/heads/master
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560179 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
+480095882d281ed676fe5b863569520e54a7d5c0 3d9386c507f6b093471a3e324085657a3c2b4247 Russell Belfer <rb@github.com> 1342560431 -0700 commit: Making a change in a submodule
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..257ca21
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/logs/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560179 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 0000000..f4b7094
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 0000000..56c845e
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 0000000..bd179b5
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/3d/9386c507f6b093471a3e324085657a3c2b4247 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/3d/9386c507f6b093471a3e324085657a3c2b4247
new file mode 100644
index 0000000..a2c3716
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/3d/9386c507f6b093471a3e324085657a3c2b4247
@@ -0,0 +1,3 @@
+x•ŽKj!E3vµ„jµüÀ#<Þ<“ì@­êéO°uÿq ™.çÂ)×ql ´‰o­Š€÷sFa#Èv‰ÓÅ )g#{':ªßTål`b¤4ë0 ;ïf¡ár‘4
+Ùä™
+ªÔÛzUøî÷-û/Ùg©ð¨ù¹lmíù£\Ç'LÆjrhÍïèÕXG_êŸê+ýlç ÊšÎE`;ß=÷]ÔÞJç \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 0000000..ccf49bd
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 0000000..5302906
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 0000000..38c791e
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 0000000..a26d299
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 0000000..83d1ba4
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/77/fb0ed3e58568d6ad362c78de08ab8649d76e29 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/77/fb0ed3e58568d6ad362c78de08ab8649d76e29
new file mode 100644
index 0000000..f8a236f
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/77/fb0ed3e58568d6ad362c78de08ab8649d76e29
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 0000000..6d27af8
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 0000000..1745884
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 0000000..83cc29f
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/8e/b1e637ed9fc8e5454fa20d38f809091f9395f4 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/8e/b1e637ed9fc8e5454fa20d38f809091f9395f4
new file mode 100644
index 0000000..8155b3e
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/8e/b1e637ed9fc8e5454fa20d38f809091f9395f4
@@ -0,0 +1,2 @@
+xMM;
+1µÎ)Þ ÁZPÐÞÆr²3kÉ l²En¿ƒl!¼æýc±ˆóõrz§Üà ,¹º¡çe +ÚlEZxuPY…x QC³*ðf·uLácfR3ŠÍT0'Ò¯øjƒŠ°ð~G¦^s1Šèb2z’ƒÿùVkî]Ü5<·ûv¨'>ã \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 0000000..55bda40
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/packed-refs b/tests/resources/submod2/.gitted/modules/sm_changed_head/packed-refs
new file mode 100644
index 0000000..5a4ebc4
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_changed_head/refs/heads/master
new file mode 100644
index 0000000..ae079bd
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/refs/heads/master
@@ -0,0 +1 @@
+3d9386c507f6b093471a3e324085657a3c2b4247
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_head/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_head/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..6efe28f
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_head/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_index/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/config b/tests/resources/submod2/.gitted/modules/sm_changed_index/config
new file mode 100644
index 0000000..0274ff7
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/config
@@ -0,0 +1,13 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../sm_changed_index
+ ignorecase = true
+[remote "origin"]
+ fetch = +refs/heads/*:refs/remotes/origin/*
+ url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/description b/tests/resources/submod2/.gitted/modules/sm_changed_index/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/index b/tests/resources/submod2/.gitted/modules/sm_changed_index/index
new file mode 100644
index 0000000..6fad3b4
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/index
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/info/exclude b/tests/resources/submod2/.gitted/modules/sm_changed_index/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/logs/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_index/logs/HEAD
new file mode 100644
index 0000000..80eb541
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560175 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/heads/master
new file mode 100644
index 0000000..80eb541
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560175 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..80eb541
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/logs/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560175 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 0000000..f4b7094
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 0000000..56c845e
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 0000000..bd179b5
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 0000000..ccf49bd
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 0000000..5302906
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 0000000..38c791e
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 0000000..a26d299
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 0000000..83d1ba4
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 0000000..6d27af8
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 0000000..1745884
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 0000000..83cc29f
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/a0/2d31770687965547ab7a04cee199b29ee458d6 b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/a0/2d31770687965547ab7a04cee199b29ee458d6
new file mode 100644
index 0000000..cb3f5a0
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/a0/2d31770687965547ab7a04cee199b29ee458d6
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 0000000..55bda40
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/packed-refs b/tests/resources/submod2/.gitted/modules/sm_changed_index/packed-refs
new file mode 100644
index 0000000..5a4ebc4
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_changed_index/refs/heads/master
new file mode 100644
index 0000000..e12c44d
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/refs/heads/master
@@ -0,0 +1 @@
+480095882d281ed676fe5b863569520e54a7d5c0
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_index/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_index/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..6efe28f
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_index/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/config b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/config
new file mode 100644
index 0000000..7f25844
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/config
@@ -0,0 +1,13 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../sm_changed_untracked_file
+ ignorecase = true
+[remote "origin"]
+ fetch = +refs/heads/*:refs/remotes/origin/*
+ url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/description b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/index b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/index
new file mode 100644
index 0000000..598e30a
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/index
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/info/exclude b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/HEAD
new file mode 100644
index 0000000..d1beafb
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560186 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/heads/master
new file mode 100644
index 0000000..d1beafb
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560186 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..d1beafb
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/logs/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560186 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 0000000..f4b7094
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 0000000..56c845e
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 0000000..bd179b5
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 0000000..ccf49bd
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 0000000..5302906
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 0000000..38c791e
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 0000000..a26d299
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 0000000..83d1ba4
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 0000000..6d27af8
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 0000000..1745884
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 0000000..83cc29f
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 0000000..55bda40
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/packed-refs b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/packed-refs
new file mode 100644
index 0000000..5a4ebc4
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/heads/master
new file mode 100644
index 0000000..e12c44d
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/heads/master
@@ -0,0 +1 @@
+480095882d281ed676fe5b863569520e54a7d5c0
diff --git a/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..6efe28f
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_changed_untracked_file/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/HEAD b/tests/resources/submod2/.gitted/modules/sm_missing_commits/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/config b/tests/resources/submod2/.gitted/modules/sm_missing_commits/config
new file mode 100644
index 0000000..45fbb30
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/config
@@ -0,0 +1,13 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../sm_missing_commits
+ ignorecase = true
+[remote "origin"]
+ fetch = +refs/heads/*:refs/remotes/origin/*
+ url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/description b/tests/resources/submod2/.gitted/modules/sm_missing_commits/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/index b/tests/resources/submod2/.gitted/modules/sm_missing_commits/index
new file mode 100644
index 0000000..4903565
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/index
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/info/exclude b/tests/resources/submod2/.gitted/modules/sm_missing_commits/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/HEAD b/tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/HEAD
new file mode 100644
index 0000000..ee08c97
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 5e4963595a9774b90524d35a807169049de8ccad Russell Belfer <rb@github.com> 1342559796 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/heads/master
new file mode 100644
index 0000000..ee08c97
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 5e4963595a9774b90524d35a807169049de8ccad Russell Belfer <rb@github.com> 1342559796 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..ee08c97
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/logs/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 5e4963595a9774b90524d35a807169049de8ccad Russell Belfer <rb@github.com> 1342559796 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 0000000..f4b7094
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 0000000..56c845e
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 0000000..bd179b5
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 0000000..ccf49bd
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 0000000..38c791e
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 0000000..a26d299
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 0000000..6d27af8
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 0000000..83cc29f
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 0000000..55bda40
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/packed-refs b/tests/resources/submod2/.gitted/modules/sm_missing_commits/packed-refs
new file mode 100644
index 0000000..66fbf5d
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+5e4963595a9774b90524d35a807169049de8ccad refs/remotes/origin/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_missing_commits/refs/heads/master
new file mode 100644
index 0000000..3913aca
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/refs/heads/master
@@ -0,0 +1 @@
+5e4963595a9774b90524d35a807169049de8ccad
diff --git a/tests/resources/submod2/.gitted/modules/sm_missing_commits/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_missing_commits/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..6efe28f
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_missing_commits/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/HEAD b/tests/resources/submod2/.gitted/modules/sm_unchanged/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/config b/tests/resources/submod2/.gitted/modules/sm_unchanged/config
new file mode 100644
index 0000000..fc706c9
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/config
@@ -0,0 +1,13 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../sm_unchanged
+ ignorecase = true
+[remote "origin"]
+ fetch = +refs/heads/*:refs/remotes/origin/*
+ url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/description b/tests/resources/submod2/.gitted/modules/sm_unchanged/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/index b/tests/resources/submod2/.gitted/modules/sm_unchanged/index
new file mode 100644
index 0000000..629c849
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/index
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/info/exclude b/tests/resources/submod2/.gitted/modules/sm_unchanged/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/logs/HEAD b/tests/resources/submod2/.gitted/modules/sm_unchanged/logs/HEAD
new file mode 100644
index 0000000..7265328
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560169 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/heads/master
new file mode 100644
index 0000000..7265328
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560169 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..7265328
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/logs/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342560169 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/submod2_target
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 0000000..f4b7094
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 0000000..56c845e
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 0000000..bd179b5
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 0000000..ccf49bd
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 0000000..5302906
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 0000000..38c791e
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 0000000..a26d299
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 0000000..83d1ba4
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 0000000..6d27af8
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 0000000..1745884
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 0000000..83cc29f
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 0000000..55bda40
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/packed-refs b/tests/resources/submod2/.gitted/modules/sm_unchanged/packed-refs
new file mode 100644
index 0000000..5a4ebc4
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/refs/heads/master b/tests/resources/submod2/.gitted/modules/sm_unchanged/refs/heads/master
new file mode 100644
index 0000000..e12c44d
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/refs/heads/master
@@ -0,0 +1 @@
+480095882d281ed676fe5b863569520e54a7d5c0
diff --git a/tests/resources/submod2/.gitted/modules/sm_unchanged/refs/remotes/origin/HEAD b/tests/resources/submod2/.gitted/modules/sm_unchanged/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..6efe28f
--- /dev/null
+++ b/tests/resources/submod2/.gitted/modules/sm_unchanged/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/submod2/.gitted/objects/09/460e5b6cbcb05a3e404593c32a3aa7221eca0e b/tests/resources/submod2/.gitted/objects/09/460e5b6cbcb05a3e404593c32a3aa7221eca0e
new file mode 100644
index 0000000..f1ea5f4
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/09/460e5b6cbcb05a3e404593c32a3aa7221eca0e
Binary files differ
diff --git a/tests/resources/submod2/.gitted/objects/14/fe9ccf104058df25e0a08361c4494e167ef243 b/tests/resources/submod2/.gitted/objects/14/fe9ccf104058df25e0a08361c4494e167ef243
new file mode 100644
index 0000000..d3c8582
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/14/fe9ccf104058df25e0a08361c4494e167ef243
@@ -0,0 +1 @@
+x•M F]sŠ¹€fh¡ccŒ;·Þ€ŸÁ’@I(Ü_OàöË{ïs%çØ@’>µÊ ^!¹²F'½‘!諲l£_¼q4Íä´ÇE˜Þ¶Rá݃S‚'§ÀnÕ>>±mÝ^\Éw³š´^‰$œ‘ÅXÇ_迦xí±E“à—_.à9} \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/objects/22/ce3e0311dda73a5992d54a4a595518d3876ea7 b/tests/resources/submod2/.gitted/objects/22/ce3e0311dda73a5992d54a4a595518d3876ea7
new file mode 100644
index 0000000..fce6a94
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/22/ce3e0311dda73a5992d54a4a595518d3876ea7
@@ -0,0 +1,4 @@
+xµË
+Â0Eݶ_Qº·.
+.ü W"!1 æ!3 øù>+¶Š.¤Û9Ã=3Wº(«nÕ-¶”¥:;¨jòÜ[" WÑ{›¨Þ•ÅQ¤¾ZWï°,2º iviyh •“ÐT/‚=Ž{Ž‡ ¶!@b(¡bÎJcSËP¢¥rÅŒ
+è‡ð¡ã{ë`ì|%³imÐpú콡ÙÄ=ˆIÇÿW2›6‡„B@)|¼óÿ)g£ý™ \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/objects/25/5546424b0efb847b1bfc91dbf7348b277f8970 b/tests/resources/submod2/.gitted/objects/25/5546424b0efb847b1bfc91dbf7348b277f8970
new file mode 100644
index 0000000..2965bec
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/25/5546424b0efb847b1bfc91dbf7348b277f8970
Binary files differ
diff --git a/tests/resources/submod2/.gitted/objects/2a/30f1e6f94b20917005a21273f65b406d0f8bad b/tests/resources/submod2/.gitted/objects/2a/30f1e6f94b20917005a21273f65b406d0f8bad
new file mode 100644
index 0000000..08faf0f
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/2a/30f1e6f94b20917005a21273f65b406d0f8bad
Binary files differ
diff --git a/tests/resources/submod2/.gitted/objects/42/cfb95cd01bf9225b659b5ee3edcc78e8eeb478 b/tests/resources/submod2/.gitted/objects/42/cfb95cd01bf9225b659b5ee3edcc78e8eeb478
new file mode 100644
index 0000000..ee7848a
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/42/cfb95cd01bf9225b659b5ee3edcc78e8eeb478
Binary files differ
diff --git a/tests/resources/submod2/.gitted/objects/57/958699c2dc394f81cfc76950e9c3ac3025c398 b/tests/resources/submod2/.gitted/objects/57/958699c2dc394f81cfc76950e9c3ac3025c398
new file mode 100644
index 0000000..ca9203a
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/57/958699c2dc394f81cfc76950e9c3ac3025c398
Binary files differ
diff --git a/tests/resources/submod2/.gitted/objects/59/01da4f1c67756eeadc5121d206bec2431f253b b/tests/resources/submod2/.gitted/objects/59/01da4f1c67756eeadc5121d206bec2431f253b
new file mode 100644
index 0000000..9f88f6b
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/59/01da4f1c67756eeadc5121d206bec2431f253b
@@ -0,0 +1,2 @@
+x•ŽÛ 1EýNÓ€2yg@D,A°€$;YöE6éßmÁß{Λ·e™(å/­2ƒõdƒ#ÊjÈšL 2—ìYdÊ:fÊž ˆ=V^D’hR Ä$¥^ÃÅ©ÉaŠÆ+tn {ûnÞý8xžáÅsá
+÷šžãÔ¾=Ýò¶<@j£¬CÔ®èŹžÿÚ©þ[ŠÏ>Ä6­#=-ÛÐg?,¯FŒ \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/objects/60/7d96653d4d0a4f733107f7890c2e67b55b620d b/tests/resources/submod2/.gitted/objects/60/7d96653d4d0a4f733107f7890c2e67b55b620d
new file mode 100644
index 0000000..30bee40
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/60/7d96653d4d0a4f733107f7890c2e67b55b620d
Binary files differ
diff --git a/tests/resources/submod2/.gitted/objects/74/84482eb8db738cafa696993664607500a3f2b9 b/tests/resources/submod2/.gitted/objects/74/84482eb8db738cafa696993664607500a3f2b9
new file mode 100644
index 0000000..7901804
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/74/84482eb8db738cafa696993664607500a3f2b9
Binary files differ
diff --git a/tests/resources/submod2/.gitted/objects/7b/a4c5c3561daa5ab1a86215cfb0587e96d404d6 b/tests/resources/submod2/.gitted/objects/7b/a4c5c3561daa5ab1a86215cfb0587e96d404d6
new file mode 100644
index 0000000..cde89e5
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/7b/a4c5c3561daa5ab1a86215cfb0587e96d404d6
Binary files differ
diff --git a/tests/resources/submod2/.gitted/objects/87/3585b94bdeabccea991ea5e3ec1a277895b698 b/tests/resources/submod2/.gitted/objects/87/3585b94bdeabccea991ea5e3ec1a277895b698
new file mode 100644
index 0000000..41af98a
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/87/3585b94bdeabccea991ea5e3ec1a277895b698
Binary files differ
diff --git a/tests/resources/submod2/.gitted/objects/97/4cf7c73de336b0c4e019f918f3cee367d72e84 b/tests/resources/submod2/.gitted/objects/97/4cf7c73de336b0c4e019f918f3cee367d72e84
new file mode 100644
index 0000000..160f1ca
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/97/4cf7c73de336b0c4e019f918f3cee367d72e84
@@ -0,0 +1,2 @@
+xµË
+Â0Eݶ_º·Bqåg¸ yŒi ™IÀÏ÷Y±Up!ÝÎs¸£|R¬ï7«=’)XCAGä¢:…à25‡º:É<°-û„uUÐ_IÛò‡¤Y¢…\Ϥ%êAF fª{Gß qTœPsï”u¹ã(ÓZ{‰RA ô#ø̉£ó0m¾“Ų.8ïÞÑbáäìÇãÞù?{vÊŒ \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/objects/9d/bc299bc013ea253583b40bf327b5a6e4037b89 b/tests/resources/submod2/.gitted/objects/9d/bc299bc013ea253583b40bf327b5a6e4037b89
new file mode 100644
index 0000000..1ee5221
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/9d/bc299bc013ea253583b40bf327b5a6e4037b89
Binary files differ
diff --git a/tests/resources/submod2/.gitted/objects/a9/104bf89e911387244ef499413960ba472066d9 b/tests/resources/submod2/.gitted/objects/a9/104bf89e911387244ef499413960ba472066d9
new file mode 100644
index 0000000..2239e14
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/a9/104bf89e911387244ef499413960ba472066d9
Binary files differ
diff --git a/tests/resources/submod2/.gitted/objects/b6/14088620bbdc1d29549d223ceba0f4419fd4cb b/tests/resources/submod2/.gitted/objects/b6/14088620bbdc1d29549d223ceba0f4419fd4cb
new file mode 100644
index 0000000..a03ea66
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/b6/14088620bbdc1d29549d223ceba0f4419fd4cb
Binary files differ
diff --git a/tests/resources/submod2/.gitted/objects/d4/07f19e50c1da1ff584beafe0d6dac7237c5d06 b/tests/resources/submod2/.gitted/objects/d4/07f19e50c1da1ff584beafe0d6dac7237c5d06
new file mode 100644
index 0000000..292303e
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/d4/07f19e50c1da1ff584beafe0d6dac7237c5d06
Binary files differ
diff --git a/tests/resources/submod2/.gitted/objects/d9/3e95571d92cceb5de28c205f1d5f3cc8b88bc8 b/tests/resources/submod2/.gitted/objects/d9/3e95571d92cceb5de28c205f1d5f3cc8b88bc8
new file mode 100644
index 0000000..b92c7ee
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/d9/3e95571d92cceb5de28c205f1d5f3cc8b88bc8
@@ -0,0 +1,2 @@
+x•ÏÛ
+!€ánק}€ "‚.z’uRÉCx€}üΑۼøt¸ œ.׫Ù6î‚,iŸs&%ãÁ9“S¿#ݲ¦úIW¢=—a˜ßËf2A‹¼BYsÏñßÐa{c±¶^K3g¼Äñ³wMÍ F˜Üúøߥ4sÅçâ€òÇáõÎ÷'Nê°I \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/objects/e3/b83bf274ee065eee48734cf8c6dfaf5e81471c b/tests/resources/submod2/.gitted/objects/e3/b83bf274ee065eee48734cf8c6dfaf5e81471c
new file mode 100644
index 0000000..3c7750b
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/e3/b83bf274ee065eee48734cf8c6dfaf5e81471c
Binary files differ
diff --git a/tests/resources/submod2/.gitted/objects/f5/4414c25e6d24fe39f5c3f128d7c8a17bc23833 b/tests/resources/submod2/.gitted/objects/f5/4414c25e6d24fe39f5c3f128d7c8a17bc23833
new file mode 100644
index 0000000..219620b
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/f5/4414c25e6d24fe39f5c3f128d7c8a17bc23833
@@ -0,0 +1,2 @@
+xeÍÁ
+Â0„a¯íS„ÞíbOzð1<I Iº¤¤‘Íû+ˆ‚õ:?|ãsõæt9îh¾Ô¥e6Š- H[´¡–’ÃÜw§«¹šÿØwMò«Œ#½‘ɪ“ÈÚïж…Õm‘—_î; º$ž rò1éDÊPCvB¨Mcø‡ýI^ \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/objects/f9/90a25a74d1a8281ce2ab018ea8df66795cd60b b/tests/resources/submod2/.gitted/objects/f9/90a25a74d1a8281ce2ab018ea8df66795cd60b
new file mode 100644
index 0000000..883a40b
--- /dev/null
+++ b/tests/resources/submod2/.gitted/objects/f9/90a25a74d1a8281ce2ab018ea8df66795cd60b
@@ -0,0 +1 @@
+xEŒA€ =óŠý„ÿáZ)¤RE¿/ñb2·É «1¶uÙsé˜xôÁ§Å¡—îˆä>ßä2<E™nG=2,ýÉœTÄ’’4©Žî4!¼N¬$` \ No newline at end of file
diff --git a/tests/resources/submod2/.gitted/refs/heads/master b/tests/resources/submod2/.gitted/refs/heads/master
new file mode 100644
index 0000000..d1d38aa
--- /dev/null
+++ b/tests/resources/submod2/.gitted/refs/heads/master
@@ -0,0 +1 @@
+7484482eb8db738cafa696993664607500a3f2b9
diff --git a/tests/resources/submod2/README.txt b/tests/resources/submod2/README.txt
new file mode 100644
index 0000000..f990a25
--- /dev/null
+++ b/tests/resources/submod2/README.txt
@@ -0,0 +1,3 @@
+This is the submodule test data
+This repo will have a bunch of submodules in different states
+
diff --git a/tests/resources/submod2/gitmodules b/tests/resources/submod2/gitmodules
new file mode 100644
index 0000000..4c31108
--- /dev/null
+++ b/tests/resources/submod2/gitmodules
@@ -0,0 +1,24 @@
+[submodule "sm_missing_commits"]
+ path = sm_missing_commits
+ url = ../submod2_target
+[submodule "sm_unchanged"]
+ path = sm_unchanged
+ url = ../submod2_target
+[submodule "sm_changed_file"]
+ path = sm_changed_file
+ url = ../submod2_target
+[submodule "sm_changed_index"]
+ path = sm_changed_index
+ url = ../submod2_target
+[submodule "sm_changed_head"]
+ path = sm_changed_head
+ url = ../submod2_target
+[submodule "sm_changed_untracked_file"]
+ path = sm_changed_untracked_file
+ url = ../submod2_target
+[submodule "sm_added_and_uncommited"]
+ path = sm_added_and_uncommited
+ url = ../submod2_target
+[submodule "sm_gitmodules_only"]
+ path = sm_gitmodules_only
+ url = ../submod2_target
diff --git a/tests/resources/submod2/just_a_dir/contents b/tests/resources/submod2/just_a_dir/contents
new file mode 100644
index 0000000..7ba4c5c
--- /dev/null
+++ b/tests/resources/submod2/just_a_dir/contents
@@ -0,0 +1 @@
+This is a file in a plain directory
diff --git a/tests/resources/submod2/just_a_file b/tests/resources/submod2/just_a_file
new file mode 100644
index 0000000..42cfb95
--- /dev/null
+++ b/tests/resources/submod2/just_a_file
@@ -0,0 +1 @@
+This is just a plain file
diff --git a/tests/resources/submod2/not-submodule/.gitted/HEAD b/tests/resources/submod2/not-submodule/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/submod2/not-submodule/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submod2/not-submodule/.gitted/config b/tests/resources/submod2/not-submodule/.gitted/config
new file mode 100644
index 0000000..af10792
--- /dev/null
+++ b/tests/resources/submod2/not-submodule/.gitted/config
@@ -0,0 +1,6 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
diff --git a/tests/resources/submod2/not-submodule/.gitted/description b/tests/resources/submod2/not-submodule/.gitted/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/submod2/not-submodule/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/submod2/not-submodule/.gitted/index b/tests/resources/submod2/not-submodule/.gitted/index
new file mode 100644
index 0000000..f3fafa5
--- /dev/null
+++ b/tests/resources/submod2/not-submodule/.gitted/index
Binary files differ
diff --git a/tests/resources/submod2/not-submodule/.gitted/info/exclude b/tests/resources/submod2/not-submodule/.gitted/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/submod2/not-submodule/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/submod2/not-submodule/.gitted/logs/HEAD b/tests/resources/submod2/not-submodule/.gitted/logs/HEAD
new file mode 100644
index 0000000..1749e7d
--- /dev/null
+++ b/tests/resources/submod2/not-submodule/.gitted/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 68e92c611b80ee1ed8f38314ff9577f0d15b2444 Russell Belfer <rb@github.com> 1342560358 -0700 commit (initial): Initial commit
diff --git a/tests/resources/submod2/not-submodule/.gitted/logs/refs/heads/master b/tests/resources/submod2/not-submodule/.gitted/logs/refs/heads/master
new file mode 100644
index 0000000..1749e7d
--- /dev/null
+++ b/tests/resources/submod2/not-submodule/.gitted/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 68e92c611b80ee1ed8f38314ff9577f0d15b2444 Russell Belfer <rb@github.com> 1342560358 -0700 commit (initial): Initial commit
diff --git a/tests/resources/submod2/not-submodule/.gitted/objects/68/e92c611b80ee1ed8f38314ff9577f0d15b2444 b/tests/resources/submod2/not-submodule/.gitted/objects/68/e92c611b80ee1ed8f38314ff9577f0d15b2444
new file mode 100644
index 0000000..8892531
--- /dev/null
+++ b/tests/resources/submod2/not-submodule/.gitted/objects/68/e92c611b80ee1ed8f38314ff9577f0d15b2444
Binary files differ
diff --git a/tests/resources/submod2/not-submodule/.gitted/objects/71/ff9927d7c8a5639e062c38a7d35c433c424627 b/tests/resources/submod2/not-submodule/.gitted/objects/71/ff9927d7c8a5639e062c38a7d35c433c424627
new file mode 100644
index 0000000..c4e1a77
--- /dev/null
+++ b/tests/resources/submod2/not-submodule/.gitted/objects/71/ff9927d7c8a5639e062c38a7d35c433c424627
Binary files differ
diff --git a/tests/resources/submod2/not-submodule/.gitted/objects/f0/1d56b18efd353ef2bb93a4585d590a0847195e b/tests/resources/submod2/not-submodule/.gitted/objects/f0/1d56b18efd353ef2bb93a4585d590a0847195e
new file mode 100644
index 0000000..e9f1942
--- /dev/null
+++ b/tests/resources/submod2/not-submodule/.gitted/objects/f0/1d56b18efd353ef2bb93a4585d590a0847195e
Binary files differ
diff --git a/tests/resources/submod2/not-submodule/.gitted/refs/heads/master b/tests/resources/submod2/not-submodule/.gitted/refs/heads/master
new file mode 100644
index 0000000..0bd8514
--- /dev/null
+++ b/tests/resources/submod2/not-submodule/.gitted/refs/heads/master
@@ -0,0 +1 @@
+68e92c611b80ee1ed8f38314ff9577f0d15b2444
diff --git a/tests/resources/submod2/not-submodule/README.txt b/tests/resources/submod2/not-submodule/README.txt
new file mode 100644
index 0000000..71ff992
--- /dev/null
+++ b/tests/resources/submod2/not-submodule/README.txt
@@ -0,0 +1 @@
+This is a git repo but not a submodule
diff --git a/tests/resources/submod2/not/.gitted/notempty b/tests/resources/submod2/not/.gitted/notempty
new file mode 100644
index 0000000..9b33ac4
--- /dev/null
+++ b/tests/resources/submod2/not/.gitted/notempty
@@ -0,0 +1 @@
+fooled you
diff --git a/tests/resources/submod2/not/README.txt b/tests/resources/submod2/not/README.txt
new file mode 100644
index 0000000..4f6935b
--- /dev/null
+++ b/tests/resources/submod2/not/README.txt
@@ -0,0 +1 @@
+what am I really
diff --git a/tests/resources/submod2/sm_added_and_uncommited/.gitted b/tests/resources/submod2/sm_added_and_uncommited/.gitted
new file mode 100644
index 0000000..2b2a4cf
--- /dev/null
+++ b/tests/resources/submod2/sm_added_and_uncommited/.gitted
@@ -0,0 +1 @@
+gitdir: ../.git/modules/sm_added_and_uncommited
diff --git a/tests/resources/submod2/sm_added_and_uncommited/README.txt b/tests/resources/submod2/sm_added_and_uncommited/README.txt
new file mode 100644
index 0000000..780d739
--- /dev/null
+++ b/tests/resources/submod2/sm_added_and_uncommited/README.txt
@@ -0,0 +1,3 @@
+This is the target for submod2 submodule links.
+Don't add commits casually because you make break tests.
+
diff --git a/tests/resources/submod2/sm_added_and_uncommited/file_to_modify b/tests/resources/submod2/sm_added_and_uncommited/file_to_modify
new file mode 100644
index 0000000..789efbd
--- /dev/null
+++ b/tests/resources/submod2/sm_added_and_uncommited/file_to_modify
@@ -0,0 +1,3 @@
+This is a file to modify in submodules
+It already has some history.
+You can add local changes as needed.
diff --git a/tests/resources/submod2/sm_changed_file/.gitted b/tests/resources/submod2/sm_changed_file/.gitted
new file mode 100644
index 0000000..dc98b16
--- /dev/null
+++ b/tests/resources/submod2/sm_changed_file/.gitted
@@ -0,0 +1 @@
+gitdir: ../.git/modules/sm_changed_file
diff --git a/tests/resources/submod2/sm_changed_file/README.txt b/tests/resources/submod2/sm_changed_file/README.txt
new file mode 100644
index 0000000..780d739
--- /dev/null
+++ b/tests/resources/submod2/sm_changed_file/README.txt
@@ -0,0 +1,3 @@
+This is the target for submod2 submodule links.
+Don't add commits casually because you make break tests.
+
diff --git a/tests/resources/submod2/sm_changed_file/file_to_modify b/tests/resources/submod2/sm_changed_file/file_to_modify
new file mode 100644
index 0000000..e5ba671
--- /dev/null
+++ b/tests/resources/submod2/sm_changed_file/file_to_modify
@@ -0,0 +1,4 @@
+This is a file to modify in submodules
+It already has some history.
+You can add local changes as needed.
+In this case, the file is changed in the workdir
diff --git a/tests/resources/submod2/sm_changed_head/.gitted b/tests/resources/submod2/sm_changed_head/.gitted
new file mode 100644
index 0000000..d5419b6
--- /dev/null
+++ b/tests/resources/submod2/sm_changed_head/.gitted
@@ -0,0 +1 @@
+gitdir: ../.git/modules/sm_changed_head
diff --git a/tests/resources/submod2/sm_changed_head/README.txt b/tests/resources/submod2/sm_changed_head/README.txt
new file mode 100644
index 0000000..780d739
--- /dev/null
+++ b/tests/resources/submod2/sm_changed_head/README.txt
@@ -0,0 +1,3 @@
+This is the target for submod2 submodule links.
+Don't add commits casually because you make break tests.
+
diff --git a/tests/resources/submod2/sm_changed_head/file_to_modify b/tests/resources/submod2/sm_changed_head/file_to_modify
new file mode 100644
index 0000000..8eb1e63
--- /dev/null
+++ b/tests/resources/submod2/sm_changed_head/file_to_modify
@@ -0,0 +1,4 @@
+This is a file to modify in submodules
+It already has some history.
+You can add local changes as needed.
+This one has been changed and the change has been committed to HEAD.
diff --git a/tests/resources/submod2/sm_changed_index/.gitted b/tests/resources/submod2/sm_changed_index/.gitted
new file mode 100644
index 0000000..2c7a5b2
--- /dev/null
+++ b/tests/resources/submod2/sm_changed_index/.gitted
@@ -0,0 +1 @@
+gitdir: ../.git/modules/sm_changed_index
diff --git a/tests/resources/submod2/sm_changed_index/README.txt b/tests/resources/submod2/sm_changed_index/README.txt
new file mode 100644
index 0000000..780d739
--- /dev/null
+++ b/tests/resources/submod2/sm_changed_index/README.txt
@@ -0,0 +1,3 @@
+This is the target for submod2 submodule links.
+Don't add commits casually because you make break tests.
+
diff --git a/tests/resources/submod2/sm_changed_index/file_to_modify b/tests/resources/submod2/sm_changed_index/file_to_modify
new file mode 100644
index 0000000..a02d317
--- /dev/null
+++ b/tests/resources/submod2/sm_changed_index/file_to_modify
@@ -0,0 +1,4 @@
+This is a file to modify in submodules
+It already has some history.
+You can add local changes as needed.
+Here the file is changed in the index and the workdir
diff --git a/tests/resources/submod2/sm_changed_untracked_file/.gitted b/tests/resources/submod2/sm_changed_untracked_file/.gitted
new file mode 100644
index 0000000..9a10706
--- /dev/null
+++ b/tests/resources/submod2/sm_changed_untracked_file/.gitted
@@ -0,0 +1 @@
+gitdir: ../.git/modules/sm_changed_untracked_file
diff --git a/tests/resources/submod2/sm_changed_untracked_file/README.txt b/tests/resources/submod2/sm_changed_untracked_file/README.txt
new file mode 100644
index 0000000..780d739
--- /dev/null
+++ b/tests/resources/submod2/sm_changed_untracked_file/README.txt
@@ -0,0 +1,3 @@
+This is the target for submod2 submodule links.
+Don't add commits casually because you make break tests.
+
diff --git a/tests/resources/submod2/sm_changed_untracked_file/file_to_modify b/tests/resources/submod2/sm_changed_untracked_file/file_to_modify
new file mode 100644
index 0000000..789efbd
--- /dev/null
+++ b/tests/resources/submod2/sm_changed_untracked_file/file_to_modify
@@ -0,0 +1,3 @@
+This is a file to modify in submodules
+It already has some history.
+You can add local changes as needed.
diff --git a/tests/resources/submod2/sm_changed_untracked_file/i_am_untracked b/tests/resources/submod2/sm_changed_untracked_file/i_am_untracked
new file mode 100644
index 0000000..d2bae61
--- /dev/null
+++ b/tests/resources/submod2/sm_changed_untracked_file/i_am_untracked
@@ -0,0 +1 @@
+This file is untracked, but in a submodule
diff --git a/tests/resources/submod2/sm_missing_commits/.gitted b/tests/resources/submod2/sm_missing_commits/.gitted
new file mode 100644
index 0000000..70193be
--- /dev/null
+++ b/tests/resources/submod2/sm_missing_commits/.gitted
@@ -0,0 +1 @@
+gitdir: ../.git/modules/sm_missing_commits
diff --git a/tests/resources/submod2/sm_missing_commits/README.txt b/tests/resources/submod2/sm_missing_commits/README.txt
new file mode 100644
index 0000000..780d739
--- /dev/null
+++ b/tests/resources/submod2/sm_missing_commits/README.txt
@@ -0,0 +1,3 @@
+This is the target for submod2 submodule links.
+Don't add commits casually because you make break tests.
+
diff --git a/tests/resources/submod2/sm_missing_commits/file_to_modify b/tests/resources/submod2/sm_missing_commits/file_to_modify
new file mode 100644
index 0000000..8834b63
--- /dev/null
+++ b/tests/resources/submod2/sm_missing_commits/file_to_modify
@@ -0,0 +1,3 @@
+This is a file to modify in submodules
+It already has some history.
+
diff --git a/tests/resources/submod2/sm_unchanged/.gitted b/tests/resources/submod2/sm_unchanged/.gitted
new file mode 100644
index 0000000..51a679c
--- /dev/null
+++ b/tests/resources/submod2/sm_unchanged/.gitted
@@ -0,0 +1 @@
+gitdir: ../.git/modules/sm_unchanged
diff --git a/tests/resources/submod2/sm_unchanged/README.txt b/tests/resources/submod2/sm_unchanged/README.txt
new file mode 100644
index 0000000..780d739
--- /dev/null
+++ b/tests/resources/submod2/sm_unchanged/README.txt
@@ -0,0 +1,3 @@
+This is the target for submod2 submodule links.
+Don't add commits casually because you make break tests.
+
diff --git a/tests/resources/submod2/sm_unchanged/file_to_modify b/tests/resources/submod2/sm_unchanged/file_to_modify
new file mode 100644
index 0000000..789efbd
--- /dev/null
+++ b/tests/resources/submod2/sm_unchanged/file_to_modify
@@ -0,0 +1,3 @@
+This is a file to modify in submodules
+It already has some history.
+You can add local changes as needed.
diff --git a/tests/resources/submod2_target/.gitted/HEAD b/tests/resources/submod2_target/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submod2_target/.gitted/config b/tests/resources/submod2_target/.gitted/config
new file mode 100644
index 0000000..af10792
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/config
@@ -0,0 +1,6 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
diff --git a/tests/resources/submod2_target/.gitted/description b/tests/resources/submod2_target/.gitted/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/submod2_target/.gitted/index b/tests/resources/submod2_target/.gitted/index
new file mode 100644
index 0000000..eb3ff8c
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/index
Binary files differ
diff --git a/tests/resources/submod2_target/.gitted/info/exclude b/tests/resources/submod2_target/.gitted/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/submod2_target/.gitted/logs/HEAD b/tests/resources/submod2_target/.gitted/logs/HEAD
new file mode 100644
index 0000000..0ecd111
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/logs/HEAD
@@ -0,0 +1,4 @@
+0000000000000000000000000000000000000000 6b31c659545507c381e9cd34ec508f16c04e149e Russell Belfer <rb@github.com> 1342559662 -0700 commit (initial): Initial commit
+6b31c659545507c381e9cd34ec508f16c04e149e 41bd4bc3df978de695f67ace64c560913da11653 Russell Belfer <rb@github.com> 1342559709 -0700 commit: Adding test file
+41bd4bc3df978de695f67ace64c560913da11653 5e4963595a9774b90524d35a807169049de8ccad Russell Belfer <rb@github.com> 1342559726 -0700 commit: Updating test file
+5e4963595a9774b90524d35a807169049de8ccad 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342559925 -0700 commit: One more update
diff --git a/tests/resources/submod2_target/.gitted/logs/refs/heads/master b/tests/resources/submod2_target/.gitted/logs/refs/heads/master
new file mode 100644
index 0000000..0ecd111
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/logs/refs/heads/master
@@ -0,0 +1,4 @@
+0000000000000000000000000000000000000000 6b31c659545507c381e9cd34ec508f16c04e149e Russell Belfer <rb@github.com> 1342559662 -0700 commit (initial): Initial commit
+6b31c659545507c381e9cd34ec508f16c04e149e 41bd4bc3df978de695f67ace64c560913da11653 Russell Belfer <rb@github.com> 1342559709 -0700 commit: Adding test file
+41bd4bc3df978de695f67ace64c560913da11653 5e4963595a9774b90524d35a807169049de8ccad Russell Belfer <rb@github.com> 1342559726 -0700 commit: Updating test file
+5e4963595a9774b90524d35a807169049de8ccad 480095882d281ed676fe5b863569520e54a7d5c0 Russell Belfer <rb@github.com> 1342559925 -0700 commit: One more update
diff --git a/tests/resources/submod2_target/.gitted/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod2_target/.gitted/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 0000000..f4b7094
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests/resources/submod2_target/.gitted/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod2_target/.gitted/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 0000000..56c845e
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests/resources/submod2_target/.gitted/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod2_target/.gitted/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 0000000..bd179b5
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests/resources/submod2_target/.gitted/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod2_target/.gitted/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 0000000..ccf49bd
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests/resources/submod2_target/.gitted/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod2_target/.gitted/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 0000000..5302906
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests/resources/submod2_target/.gitted/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod2_target/.gitted/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 0000000..38c791e
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests/resources/submod2_target/.gitted/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod2_target/.gitted/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 0000000..a26d299
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests/resources/submod2_target/.gitted/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod2_target/.gitted/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 0000000..83d1ba4
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests/resources/submod2_target/.gitted/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod2_target/.gitted/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 0000000..6d27af8
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests/resources/submod2_target/.gitted/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod2_target/.gitted/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 0000000..1745884
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests/resources/submod2_target/.gitted/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod2_target/.gitted/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 0000000..83cc29f
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests/resources/submod2_target/.gitted/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod2_target/.gitted/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 0000000..55bda40
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests/resources/submod2_target/.gitted/refs/heads/master b/tests/resources/submod2_target/.gitted/refs/heads/master
new file mode 100644
index 0000000..e12c44d
--- /dev/null
+++ b/tests/resources/submod2_target/.gitted/refs/heads/master
@@ -0,0 +1 @@
+480095882d281ed676fe5b863569520e54a7d5c0
diff --git a/tests/resources/submod2_target/README.txt b/tests/resources/submod2_target/README.txt
new file mode 100644
index 0000000..780d739
--- /dev/null
+++ b/tests/resources/submod2_target/README.txt
@@ -0,0 +1,3 @@
+This is the target for submod2 submodule links.
+Don't add commits casually because you make break tests.
+
diff --git a/tests/resources/submod2_target/file_to_modify b/tests/resources/submod2_target/file_to_modify
new file mode 100644
index 0000000..789efbd
--- /dev/null
+++ b/tests/resources/submod2_target/file_to_modify
@@ -0,0 +1,3 @@
+This is a file to modify in submodules
+It already has some history.
+You can add local changes as needed.
diff --git a/tests/resources/submod3/.gitted/HEAD b/tests/resources/submod3/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/submod3/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submod3/.gitted/config b/tests/resources/submod3/.gitted/config
new file mode 100644
index 0000000..51fbbe9
--- /dev/null
+++ b/tests/resources/submod3/.gitted/config
@@ -0,0 +1,25 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+[submodule "One"]
+ url = ../submod2_target
+[submodule "TWO"]
+ url = ../submod2_target
+[submodule "three"]
+ url = ../submod2_target
+[submodule "FoUr"]
+ url = ../submod2_target
+[submodule "Five"]
+ url = ../submod2_target
+[submodule "six"]
+ url = ../submod2_target
+[submodule "sEvEn"]
+ url = ../submod2_target
+[submodule "EIGHT"]
+ url = ../submod2_target
+[submodule "nine"]
+ url = ../submod2_target
+[submodule "TEN"]
+ url = ../submod2_target
diff --git a/tests/resources/submod3/.gitted/index b/tests/resources/submod3/.gitted/index
new file mode 100644
index 0000000..fc688e2
--- /dev/null
+++ b/tests/resources/submod3/.gitted/index
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/EIGHT/HEAD b/tests/resources/submod3/.gitted/modules/EIGHT/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/EIGHT/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submod3/.gitted/modules/EIGHT/config b/tests/resources/submod3/.gitted/modules/EIGHT/config
new file mode 100755
index 0000000..c95068a
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/EIGHT/config
@@ -0,0 +1,12 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../EIGHT
+[remote "origin"]
+ url = /tmp/libgit2_tests_5hR8uP/submod2_target
+ fetch = +refs/heads/*:refs/remotes/origin/*
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/submod3/.gitted/modules/EIGHT/index b/tests/resources/submod3/.gitted/modules/EIGHT/index
new file mode 100644
index 0000000..4217b3f
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/EIGHT/index
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/EIGHT/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod3/.gitted/modules/EIGHT/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 0000000..f4b7094
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/EIGHT/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/EIGHT/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod3/.gitted/modules/EIGHT/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 0000000..56c845e
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/EIGHT/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/EIGHT/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod3/.gitted/modules/EIGHT/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 0000000..bd179b5
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/EIGHT/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/EIGHT/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod3/.gitted/modules/EIGHT/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 0000000..ccf49bd
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/EIGHT/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/EIGHT/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod3/.gitted/modules/EIGHT/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 0000000..5302906
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/EIGHT/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/EIGHT/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod3/.gitted/modules/EIGHT/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 0000000..38c791e
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/EIGHT/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/EIGHT/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod3/.gitted/modules/EIGHT/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 0000000..a26d299
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/EIGHT/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests/resources/submod3/.gitted/modules/EIGHT/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod3/.gitted/modules/EIGHT/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 0000000..83d1ba4
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/EIGHT/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/EIGHT/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod3/.gitted/modules/EIGHT/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 0000000..6d27af8
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/EIGHT/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests/resources/submod3/.gitted/modules/EIGHT/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod3/.gitted/modules/EIGHT/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 0000000..1745884
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/EIGHT/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests/resources/submod3/.gitted/modules/EIGHT/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod3/.gitted/modules/EIGHT/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 0000000..83cc29f
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/EIGHT/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/EIGHT/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod3/.gitted/modules/EIGHT/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 0000000..55bda40
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/EIGHT/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/EIGHT/packed-refs b/tests/resources/submod3/.gitted/modules/EIGHT/packed-refs
new file mode 100644
index 0000000..def303a
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/EIGHT/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled fully-peeled
+480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
diff --git a/tests/resources/submod3/.gitted/modules/EIGHT/refs/heads/master b/tests/resources/submod3/.gitted/modules/EIGHT/refs/heads/master
new file mode 100644
index 0000000..e12c44d
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/EIGHT/refs/heads/master
@@ -0,0 +1 @@
+480095882d281ed676fe5b863569520e54a7d5c0
diff --git a/tests/resources/submod3/.gitted/modules/EIGHT/refs/remotes/origin/HEAD b/tests/resources/submod3/.gitted/modules/EIGHT/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..6efe28f
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/EIGHT/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/submod3/.gitted/modules/Five/HEAD b/tests/resources/submod3/.gitted/modules/Five/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/Five/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submod3/.gitted/modules/Five/config b/tests/resources/submod3/.gitted/modules/Five/config
new file mode 100755
index 0000000..18259e8
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/Five/config
@@ -0,0 +1,12 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../Five
+[remote "origin"]
+ url = /tmp/libgit2_tests_5hR8uP/submod2_target
+ fetch = +refs/heads/*:refs/remotes/origin/*
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/submod3/.gitted/modules/Five/index b/tests/resources/submod3/.gitted/modules/Five/index
new file mode 100644
index 0000000..bd356c8
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/Five/index
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/Five/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod3/.gitted/modules/Five/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 0000000..f4b7094
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/Five/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/Five/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod3/.gitted/modules/Five/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 0000000..56c845e
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/Five/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/Five/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod3/.gitted/modules/Five/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 0000000..bd179b5
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/Five/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/Five/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod3/.gitted/modules/Five/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 0000000..ccf49bd
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/Five/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/Five/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod3/.gitted/modules/Five/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 0000000..5302906
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/Five/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/Five/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod3/.gitted/modules/Five/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 0000000..38c791e
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/Five/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/Five/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod3/.gitted/modules/Five/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 0000000..a26d299
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/Five/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests/resources/submod3/.gitted/modules/Five/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod3/.gitted/modules/Five/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 0000000..83d1ba4
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/Five/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/Five/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod3/.gitted/modules/Five/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 0000000..6d27af8
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/Five/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests/resources/submod3/.gitted/modules/Five/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod3/.gitted/modules/Five/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 0000000..1745884
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/Five/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests/resources/submod3/.gitted/modules/Five/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod3/.gitted/modules/Five/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 0000000..83cc29f
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/Five/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/Five/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod3/.gitted/modules/Five/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 0000000..55bda40
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/Five/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/Five/packed-refs b/tests/resources/submod3/.gitted/modules/Five/packed-refs
new file mode 100644
index 0000000..def303a
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/Five/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled fully-peeled
+480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
diff --git a/tests/resources/submod3/.gitted/modules/Five/refs/heads/master b/tests/resources/submod3/.gitted/modules/Five/refs/heads/master
new file mode 100644
index 0000000..e12c44d
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/Five/refs/heads/master
@@ -0,0 +1 @@
+480095882d281ed676fe5b863569520e54a7d5c0
diff --git a/tests/resources/submod3/.gitted/modules/Five/refs/remotes/origin/HEAD b/tests/resources/submod3/.gitted/modules/Five/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..6efe28f
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/Five/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/submod3/.gitted/modules/FoUr/HEAD b/tests/resources/submod3/.gitted/modules/FoUr/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/FoUr/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submod3/.gitted/modules/FoUr/config b/tests/resources/submod3/.gitted/modules/FoUr/config
new file mode 100755
index 0000000..8e41a32
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/FoUr/config
@@ -0,0 +1,12 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../FoUr
+[remote "origin"]
+ url = /tmp/libgit2_tests_5hR8uP/submod2_target
+ fetch = +refs/heads/*:refs/remotes/origin/*
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/submod3/.gitted/modules/FoUr/index b/tests/resources/submod3/.gitted/modules/FoUr/index
new file mode 100644
index 0000000..bfc441b
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/FoUr/index
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/FoUr/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod3/.gitted/modules/FoUr/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 0000000..f4b7094
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/FoUr/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/FoUr/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod3/.gitted/modules/FoUr/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 0000000..56c845e
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/FoUr/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/FoUr/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod3/.gitted/modules/FoUr/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 0000000..bd179b5
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/FoUr/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/FoUr/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod3/.gitted/modules/FoUr/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 0000000..ccf49bd
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/FoUr/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/FoUr/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod3/.gitted/modules/FoUr/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 0000000..5302906
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/FoUr/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/FoUr/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod3/.gitted/modules/FoUr/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 0000000..38c791e
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/FoUr/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/FoUr/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod3/.gitted/modules/FoUr/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 0000000..a26d299
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/FoUr/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests/resources/submod3/.gitted/modules/FoUr/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod3/.gitted/modules/FoUr/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 0000000..83d1ba4
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/FoUr/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/FoUr/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod3/.gitted/modules/FoUr/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 0000000..6d27af8
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/FoUr/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests/resources/submod3/.gitted/modules/FoUr/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod3/.gitted/modules/FoUr/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 0000000..1745884
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/FoUr/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests/resources/submod3/.gitted/modules/FoUr/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod3/.gitted/modules/FoUr/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 0000000..83cc29f
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/FoUr/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/FoUr/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod3/.gitted/modules/FoUr/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 0000000..55bda40
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/FoUr/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/FoUr/packed-refs b/tests/resources/submod3/.gitted/modules/FoUr/packed-refs
new file mode 100644
index 0000000..def303a
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/FoUr/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled fully-peeled
+480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
diff --git a/tests/resources/submod3/.gitted/modules/FoUr/refs/heads/master b/tests/resources/submod3/.gitted/modules/FoUr/refs/heads/master
new file mode 100644
index 0000000..e12c44d
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/FoUr/refs/heads/master
@@ -0,0 +1 @@
+480095882d281ed676fe5b863569520e54a7d5c0
diff --git a/tests/resources/submod3/.gitted/modules/FoUr/refs/remotes/origin/HEAD b/tests/resources/submod3/.gitted/modules/FoUr/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..6efe28f
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/FoUr/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/submod3/.gitted/modules/One/HEAD b/tests/resources/submod3/.gitted/modules/One/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/One/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submod3/.gitted/modules/One/config b/tests/resources/submod3/.gitted/modules/One/config
new file mode 100755
index 0000000..303b86d
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/One/config
@@ -0,0 +1,12 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../One
+[remote "origin"]
+ url = /tmp/libgit2_tests_5hR8uP/submod2_target
+ fetch = +refs/heads/*:refs/remotes/origin/*
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/submod3/.gitted/modules/One/index b/tests/resources/submod3/.gitted/modules/One/index
new file mode 100644
index 0000000..2fa766b
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/One/index
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/One/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod3/.gitted/modules/One/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 0000000..f4b7094
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/One/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/One/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod3/.gitted/modules/One/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 0000000..56c845e
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/One/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/One/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod3/.gitted/modules/One/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 0000000..bd179b5
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/One/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/One/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod3/.gitted/modules/One/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 0000000..ccf49bd
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/One/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/One/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod3/.gitted/modules/One/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 0000000..5302906
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/One/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/One/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod3/.gitted/modules/One/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 0000000..38c791e
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/One/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/One/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod3/.gitted/modules/One/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 0000000..a26d299
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/One/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests/resources/submod3/.gitted/modules/One/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod3/.gitted/modules/One/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 0000000..83d1ba4
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/One/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/One/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod3/.gitted/modules/One/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 0000000..6d27af8
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/One/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests/resources/submod3/.gitted/modules/One/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod3/.gitted/modules/One/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 0000000..1745884
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/One/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests/resources/submod3/.gitted/modules/One/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod3/.gitted/modules/One/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 0000000..83cc29f
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/One/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/One/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod3/.gitted/modules/One/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 0000000..55bda40
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/One/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/One/packed-refs b/tests/resources/submod3/.gitted/modules/One/packed-refs
new file mode 100644
index 0000000..def303a
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/One/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled fully-peeled
+480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
diff --git a/tests/resources/submod3/.gitted/modules/One/refs/heads/master b/tests/resources/submod3/.gitted/modules/One/refs/heads/master
new file mode 100644
index 0000000..e12c44d
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/One/refs/heads/master
@@ -0,0 +1 @@
+480095882d281ed676fe5b863569520e54a7d5c0
diff --git a/tests/resources/submod3/.gitted/modules/One/refs/remotes/origin/HEAD b/tests/resources/submod3/.gitted/modules/One/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..6efe28f
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/One/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/submod3/.gitted/modules/TEN/HEAD b/tests/resources/submod3/.gitted/modules/TEN/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TEN/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submod3/.gitted/modules/TEN/config b/tests/resources/submod3/.gitted/modules/TEN/config
new file mode 100755
index 0000000..0c83288
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TEN/config
@@ -0,0 +1,12 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../TEN
+[remote "origin"]
+ url = /tmp/libgit2_tests_5hR8uP/submod2_target
+ fetch = +refs/heads/*:refs/remotes/origin/*
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/submod3/.gitted/modules/TEN/index b/tests/resources/submod3/.gitted/modules/TEN/index
new file mode 100644
index 0000000..202f0e2
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TEN/index
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/TEN/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod3/.gitted/modules/TEN/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 0000000..f4b7094
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TEN/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/TEN/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod3/.gitted/modules/TEN/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 0000000..56c845e
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TEN/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/TEN/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod3/.gitted/modules/TEN/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 0000000..bd179b5
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TEN/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/TEN/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod3/.gitted/modules/TEN/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 0000000..ccf49bd
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TEN/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/TEN/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod3/.gitted/modules/TEN/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 0000000..5302906
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TEN/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/TEN/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod3/.gitted/modules/TEN/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 0000000..38c791e
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TEN/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/TEN/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod3/.gitted/modules/TEN/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 0000000..a26d299
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TEN/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests/resources/submod3/.gitted/modules/TEN/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod3/.gitted/modules/TEN/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 0000000..83d1ba4
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TEN/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/TEN/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod3/.gitted/modules/TEN/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 0000000..6d27af8
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TEN/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests/resources/submod3/.gitted/modules/TEN/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod3/.gitted/modules/TEN/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 0000000..1745884
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TEN/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests/resources/submod3/.gitted/modules/TEN/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod3/.gitted/modules/TEN/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 0000000..83cc29f
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TEN/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/TEN/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod3/.gitted/modules/TEN/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 0000000..55bda40
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TEN/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/TEN/packed-refs b/tests/resources/submod3/.gitted/modules/TEN/packed-refs
new file mode 100644
index 0000000..def303a
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TEN/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled fully-peeled
+480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
diff --git a/tests/resources/submod3/.gitted/modules/TEN/refs/heads/master b/tests/resources/submod3/.gitted/modules/TEN/refs/heads/master
new file mode 100644
index 0000000..e12c44d
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TEN/refs/heads/master
@@ -0,0 +1 @@
+480095882d281ed676fe5b863569520e54a7d5c0
diff --git a/tests/resources/submod3/.gitted/modules/TEN/refs/remotes/origin/HEAD b/tests/resources/submod3/.gitted/modules/TEN/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..6efe28f
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TEN/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/submod3/.gitted/modules/TWO/HEAD b/tests/resources/submod3/.gitted/modules/TWO/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TWO/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submod3/.gitted/modules/TWO/config b/tests/resources/submod3/.gitted/modules/TWO/config
new file mode 100755
index 0000000..4d90222
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TWO/config
@@ -0,0 +1,12 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../TWO
+[remote "origin"]
+ url = /tmp/libgit2_tests_5hR8uP/submod2_target
+ fetch = +refs/heads/*:refs/remotes/origin/*
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/submod3/.gitted/modules/TWO/index b/tests/resources/submod3/.gitted/modules/TWO/index
new file mode 100644
index 0000000..4205313
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TWO/index
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/TWO/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod3/.gitted/modules/TWO/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 0000000..f4b7094
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TWO/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/TWO/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod3/.gitted/modules/TWO/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 0000000..56c845e
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TWO/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/TWO/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod3/.gitted/modules/TWO/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 0000000..bd179b5
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TWO/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/TWO/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod3/.gitted/modules/TWO/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 0000000..ccf49bd
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TWO/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/TWO/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod3/.gitted/modules/TWO/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 0000000..5302906
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TWO/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/TWO/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod3/.gitted/modules/TWO/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 0000000..38c791e
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TWO/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/TWO/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod3/.gitted/modules/TWO/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 0000000..a26d299
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TWO/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests/resources/submod3/.gitted/modules/TWO/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod3/.gitted/modules/TWO/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 0000000..83d1ba4
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TWO/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/TWO/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod3/.gitted/modules/TWO/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 0000000..6d27af8
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TWO/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests/resources/submod3/.gitted/modules/TWO/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod3/.gitted/modules/TWO/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 0000000..1745884
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TWO/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests/resources/submod3/.gitted/modules/TWO/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod3/.gitted/modules/TWO/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 0000000..83cc29f
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TWO/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/TWO/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod3/.gitted/modules/TWO/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 0000000..55bda40
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TWO/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/TWO/packed-refs b/tests/resources/submod3/.gitted/modules/TWO/packed-refs
new file mode 100644
index 0000000..def303a
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TWO/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled fully-peeled
+480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
diff --git a/tests/resources/submod3/.gitted/modules/TWO/refs/heads/master b/tests/resources/submod3/.gitted/modules/TWO/refs/heads/master
new file mode 100644
index 0000000..e12c44d
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TWO/refs/heads/master
@@ -0,0 +1 @@
+480095882d281ed676fe5b863569520e54a7d5c0
diff --git a/tests/resources/submod3/.gitted/modules/TWO/refs/remotes/origin/HEAD b/tests/resources/submod3/.gitted/modules/TWO/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..6efe28f
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/TWO/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/submod3/.gitted/modules/nine/HEAD b/tests/resources/submod3/.gitted/modules/nine/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/nine/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submod3/.gitted/modules/nine/config b/tests/resources/submod3/.gitted/modules/nine/config
new file mode 100755
index 0000000..7c73f8a
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/nine/config
@@ -0,0 +1,12 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../nine
+[remote "origin"]
+ url = /tmp/libgit2_tests_5hR8uP/submod2_target
+ fetch = +refs/heads/*:refs/remotes/origin/*
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/submod3/.gitted/modules/nine/index b/tests/resources/submod3/.gitted/modules/nine/index
new file mode 100644
index 0000000..48e3e9e
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/nine/index
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/nine/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod3/.gitted/modules/nine/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 0000000..f4b7094
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/nine/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/nine/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod3/.gitted/modules/nine/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 0000000..56c845e
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/nine/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/nine/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod3/.gitted/modules/nine/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 0000000..bd179b5
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/nine/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/nine/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod3/.gitted/modules/nine/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 0000000..ccf49bd
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/nine/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/nine/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod3/.gitted/modules/nine/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 0000000..5302906
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/nine/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/nine/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod3/.gitted/modules/nine/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 0000000..38c791e
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/nine/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/nine/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod3/.gitted/modules/nine/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 0000000..a26d299
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/nine/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests/resources/submod3/.gitted/modules/nine/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod3/.gitted/modules/nine/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 0000000..83d1ba4
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/nine/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/nine/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod3/.gitted/modules/nine/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 0000000..6d27af8
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/nine/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests/resources/submod3/.gitted/modules/nine/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod3/.gitted/modules/nine/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 0000000..1745884
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/nine/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests/resources/submod3/.gitted/modules/nine/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod3/.gitted/modules/nine/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 0000000..83cc29f
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/nine/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/nine/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod3/.gitted/modules/nine/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 0000000..55bda40
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/nine/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/nine/packed-refs b/tests/resources/submod3/.gitted/modules/nine/packed-refs
new file mode 100644
index 0000000..def303a
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/nine/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled fully-peeled
+480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
diff --git a/tests/resources/submod3/.gitted/modules/nine/refs/heads/master b/tests/resources/submod3/.gitted/modules/nine/refs/heads/master
new file mode 100644
index 0000000..e12c44d
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/nine/refs/heads/master
@@ -0,0 +1 @@
+480095882d281ed676fe5b863569520e54a7d5c0
diff --git a/tests/resources/submod3/.gitted/modules/nine/refs/remotes/origin/HEAD b/tests/resources/submod3/.gitted/modules/nine/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..6efe28f
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/nine/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/submod3/.gitted/modules/sEvEn/HEAD b/tests/resources/submod3/.gitted/modules/sEvEn/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/sEvEn/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submod3/.gitted/modules/sEvEn/config b/tests/resources/submod3/.gitted/modules/sEvEn/config
new file mode 100755
index 0000000..1ef3be7
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/sEvEn/config
@@ -0,0 +1,12 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../sEvEn
+[remote "origin"]
+ url = /tmp/libgit2_tests_5hR8uP/submod2_target
+ fetch = +refs/heads/*:refs/remotes/origin/*
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/submod3/.gitted/modules/sEvEn/index b/tests/resources/submod3/.gitted/modules/sEvEn/index
new file mode 100644
index 0000000..0a3a842
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/sEvEn/index
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/sEvEn/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod3/.gitted/modules/sEvEn/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 0000000..f4b7094
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/sEvEn/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/sEvEn/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod3/.gitted/modules/sEvEn/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 0000000..56c845e
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/sEvEn/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/sEvEn/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod3/.gitted/modules/sEvEn/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 0000000..bd179b5
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/sEvEn/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/sEvEn/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod3/.gitted/modules/sEvEn/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 0000000..ccf49bd
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/sEvEn/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/sEvEn/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod3/.gitted/modules/sEvEn/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 0000000..5302906
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/sEvEn/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/sEvEn/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod3/.gitted/modules/sEvEn/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 0000000..38c791e
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/sEvEn/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/sEvEn/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod3/.gitted/modules/sEvEn/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 0000000..a26d299
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/sEvEn/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests/resources/submod3/.gitted/modules/sEvEn/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod3/.gitted/modules/sEvEn/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 0000000..83d1ba4
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/sEvEn/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/sEvEn/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod3/.gitted/modules/sEvEn/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 0000000..6d27af8
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/sEvEn/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests/resources/submod3/.gitted/modules/sEvEn/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod3/.gitted/modules/sEvEn/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 0000000..1745884
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/sEvEn/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests/resources/submod3/.gitted/modules/sEvEn/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod3/.gitted/modules/sEvEn/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 0000000..83cc29f
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/sEvEn/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/sEvEn/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod3/.gitted/modules/sEvEn/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 0000000..55bda40
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/sEvEn/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/sEvEn/packed-refs b/tests/resources/submod3/.gitted/modules/sEvEn/packed-refs
new file mode 100644
index 0000000..def303a
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/sEvEn/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled fully-peeled
+480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
diff --git a/tests/resources/submod3/.gitted/modules/sEvEn/refs/heads/master b/tests/resources/submod3/.gitted/modules/sEvEn/refs/heads/master
new file mode 100644
index 0000000..e12c44d
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/sEvEn/refs/heads/master
@@ -0,0 +1 @@
+480095882d281ed676fe5b863569520e54a7d5c0
diff --git a/tests/resources/submod3/.gitted/modules/sEvEn/refs/remotes/origin/HEAD b/tests/resources/submod3/.gitted/modules/sEvEn/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..6efe28f
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/sEvEn/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/submod3/.gitted/modules/six/HEAD b/tests/resources/submod3/.gitted/modules/six/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/six/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submod3/.gitted/modules/six/config b/tests/resources/submod3/.gitted/modules/six/config
new file mode 100755
index 0000000..4296e81
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/six/config
@@ -0,0 +1,12 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../six
+[remote "origin"]
+ url = /tmp/libgit2_tests_5hR8uP/submod2_target
+ fetch = +refs/heads/*:refs/remotes/origin/*
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/submod3/.gitted/modules/six/index b/tests/resources/submod3/.gitted/modules/six/index
new file mode 100644
index 0000000..fbd09fd
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/six/index
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/six/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod3/.gitted/modules/six/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 0000000..f4b7094
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/six/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/six/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod3/.gitted/modules/six/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 0000000..56c845e
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/six/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/six/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod3/.gitted/modules/six/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 0000000..bd179b5
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/six/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/six/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod3/.gitted/modules/six/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 0000000..ccf49bd
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/six/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/six/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod3/.gitted/modules/six/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 0000000..5302906
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/six/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/six/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod3/.gitted/modules/six/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 0000000..38c791e
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/six/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/six/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod3/.gitted/modules/six/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 0000000..a26d299
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/six/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests/resources/submod3/.gitted/modules/six/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod3/.gitted/modules/six/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 0000000..83d1ba4
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/six/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/six/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod3/.gitted/modules/six/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 0000000..6d27af8
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/six/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests/resources/submod3/.gitted/modules/six/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod3/.gitted/modules/six/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 0000000..1745884
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/six/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests/resources/submod3/.gitted/modules/six/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod3/.gitted/modules/six/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 0000000..83cc29f
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/six/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/six/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod3/.gitted/modules/six/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 0000000..55bda40
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/six/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/six/packed-refs b/tests/resources/submod3/.gitted/modules/six/packed-refs
new file mode 100644
index 0000000..def303a
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/six/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled fully-peeled
+480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
diff --git a/tests/resources/submod3/.gitted/modules/six/refs/heads/master b/tests/resources/submod3/.gitted/modules/six/refs/heads/master
new file mode 100644
index 0000000..e12c44d
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/six/refs/heads/master
@@ -0,0 +1 @@
+480095882d281ed676fe5b863569520e54a7d5c0
diff --git a/tests/resources/submod3/.gitted/modules/six/refs/remotes/origin/HEAD b/tests/resources/submod3/.gitted/modules/six/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..6efe28f
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/six/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/submod3/.gitted/modules/three/HEAD b/tests/resources/submod3/.gitted/modules/three/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/three/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submod3/.gitted/modules/three/config b/tests/resources/submod3/.gitted/modules/three/config
new file mode 100755
index 0000000..ba5c966
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/three/config
@@ -0,0 +1,12 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../three
+[remote "origin"]
+ url = /tmp/libgit2_tests_5hR8uP/submod2_target
+ fetch = +refs/heads/*:refs/remotes/origin/*
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/submod3/.gitted/modules/three/index b/tests/resources/submod3/.gitted/modules/three/index
new file mode 100644
index 0000000..ec4a944
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/three/index
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/three/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/submod3/.gitted/modules/three/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 0000000..f4b7094
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/three/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/three/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/submod3/.gitted/modules/three/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 0000000..56c845e
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/three/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/three/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/submod3/.gitted/modules/three/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 0000000..bd179b5
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/three/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/three/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/submod3/.gitted/modules/three/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 0000000..ccf49bd
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/three/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/three/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/submod3/.gitted/modules/three/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 0000000..5302906
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/three/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/three/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/submod3/.gitted/modules/three/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 0000000..38c791e
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/three/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/three/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/submod3/.gitted/modules/three/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 0000000..a26d299
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/three/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests/resources/submod3/.gitted/modules/three/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/submod3/.gitted/modules/three/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 0000000..83d1ba4
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/three/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/three/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/submod3/.gitted/modules/three/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 0000000..6d27af8
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/three/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests/resources/submod3/.gitted/modules/three/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/submod3/.gitted/modules/three/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 0000000..1745884
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/three/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests/resources/submod3/.gitted/modules/three/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/submod3/.gitted/modules/three/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 0000000..83cc29f
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/three/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/three/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/submod3/.gitted/modules/three/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 0000000..55bda40
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/three/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests/resources/submod3/.gitted/modules/three/packed-refs b/tests/resources/submod3/.gitted/modules/three/packed-refs
new file mode 100644
index 0000000..def303a
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/three/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled fully-peeled
+480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
diff --git a/tests/resources/submod3/.gitted/modules/three/refs/heads/master b/tests/resources/submod3/.gitted/modules/three/refs/heads/master
new file mode 100644
index 0000000..e12c44d
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/three/refs/heads/master
@@ -0,0 +1 @@
+480095882d281ed676fe5b863569520e54a7d5c0
diff --git a/tests/resources/submod3/.gitted/modules/three/refs/remotes/origin/HEAD b/tests/resources/submod3/.gitted/modules/three/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..6efe28f
--- /dev/null
+++ b/tests/resources/submod3/.gitted/modules/three/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/submod3/.gitted/objects/0d/db6db2835a283823cd700a1d18f17b0ba6520d b/tests/resources/submod3/.gitted/objects/0d/db6db2835a283823cd700a1d18f17b0ba6520d
new file mode 100644
index 0000000..8f1c5ca
--- /dev/null
+++ b/tests/resources/submod3/.gitted/objects/0d/db6db2835a283823cd700a1d18f17b0ba6520d
Binary files differ
diff --git a/tests/resources/submod3/.gitted/objects/0e/db4f085060fea212aecc25242d4c7685cdc01d b/tests/resources/submod3/.gitted/objects/0e/db4f085060fea212aecc25242d4c7685cdc01d
new file mode 100644
index 0000000..34179a4
--- /dev/null
+++ b/tests/resources/submod3/.gitted/objects/0e/db4f085060fea212aecc25242d4c7685cdc01d
Binary files differ
diff --git a/tests/resources/submod3/.gitted/objects/33/61720a7115648e518342a6524b29cc627ea11a b/tests/resources/submod3/.gitted/objects/33/61720a7115648e518342a6524b29cc627ea11a
new file mode 100644
index 0000000..c09eabc
--- /dev/null
+++ b/tests/resources/submod3/.gitted/objects/33/61720a7115648e518342a6524b29cc627ea11a
Binary files differ
diff --git a/tests/resources/submod3/.gitted/objects/4f/6ea8092cb19f39e25cd1b21c061893b6ce17bd b/tests/resources/submod3/.gitted/objects/4f/6ea8092cb19f39e25cd1b21c061893b6ce17bd
new file mode 100644
index 0000000..cf65393
--- /dev/null
+++ b/tests/resources/submod3/.gitted/objects/4f/6ea8092cb19f39e25cd1b21c061893b6ce17bd
Binary files differ
diff --git a/tests/resources/submod3/.gitted/objects/5b/6f93e4846d705ee2daa5d60348e7fc1c6715ed b/tests/resources/submod3/.gitted/objects/5b/6f93e4846d705ee2daa5d60348e7fc1c6715ed
new file mode 100644
index 0000000..591d4b8
--- /dev/null
+++ b/tests/resources/submod3/.gitted/objects/5b/6f93e4846d705ee2daa5d60348e7fc1c6715ed
Binary files differ
diff --git a/tests/resources/submod3/.gitted/objects/6e/7201a58feeaa462ac9f545928fe0b961ad9495 b/tests/resources/submod3/.gitted/objects/6e/7201a58feeaa462ac9f545928fe0b961ad9495
new file mode 100644
index 0000000..a5dc38e
--- /dev/null
+++ b/tests/resources/submod3/.gitted/objects/6e/7201a58feeaa462ac9f545928fe0b961ad9495
Binary files differ
diff --git a/tests/resources/submod3/.gitted/objects/6f/b39bdc90378a0a9a05a127da035e560ced3900 b/tests/resources/submod3/.gitted/objects/6f/b39bdc90378a0a9a05a127da035e560ced3900
new file mode 100644
index 0000000..315e3b8
--- /dev/null
+++ b/tests/resources/submod3/.gitted/objects/6f/b39bdc90378a0a9a05a127da035e560ced3900
Binary files differ
diff --git a/tests/resources/submod3/.gitted/objects/90/459b51713bde15eb97852ff22c29270752b432 b/tests/resources/submod3/.gitted/objects/90/459b51713bde15eb97852ff22c29270752b432
new file mode 100644
index 0000000..3a2c99c
--- /dev/null
+++ b/tests/resources/submod3/.gitted/objects/90/459b51713bde15eb97852ff22c29270752b432
@@ -0,0 +1 @@
+xÁ !E=SÅ4 pÀ%1Æ‹ØÀ,ÌÊ&²$,Äöżý¼ä½JÎk=Ñ¡Upr1¨ÙN‹39ÃÁ/–¬7ƒàìæèÉ[Ž¥Rá?\#<SÉ{Ùà*ƒþÖýµ¶ÔçS(ùzøäðl‘Õ ãµÉ¿¾uÈ%ö·ìê ZJ<ó \ No newline at end of file
diff --git a/tests/resources/submod3/.gitted/objects/d0/ea28557a5f50013ac72938bc285c2d8572e50d b/tests/resources/submod3/.gitted/objects/d0/ea28557a5f50013ac72938bc285c2d8572e50d
new file mode 100644
index 0000000..9b45f5c
--- /dev/null
+++ b/tests/resources/submod3/.gitted/objects/d0/ea28557a5f50013ac72938bc285c2d8572e50d
Binary files differ
diff --git a/tests/resources/submod3/.gitted/objects/d0/fe2af38ea8925d5b4982b56354ca17816b7e11 b/tests/resources/submod3/.gitted/objects/d0/fe2af38ea8925d5b4982b56354ca17816b7e11
new file mode 100644
index 0000000..4907b20
--- /dev/null
+++ b/tests/resources/submod3/.gitted/objects/d0/fe2af38ea8925d5b4982b56354ca17816b7e11
Binary files differ
diff --git a/tests/resources/submod3/.gitted/objects/e6/4b5c9e517bbb34962611400cde683690e56aa8 b/tests/resources/submod3/.gitted/objects/e6/4b5c9e517bbb34962611400cde683690e56aa8
new file mode 100644
index 0000000..7015847
--- /dev/null
+++ b/tests/resources/submod3/.gitted/objects/e6/4b5c9e517bbb34962611400cde683690e56aa8
Binary files differ
diff --git a/tests/resources/submod3/.gitted/objects/e7/b6f5010b47e84573eb670d8b31f19fccab6964 b/tests/resources/submod3/.gitted/objects/e7/b6f5010b47e84573eb670d8b31f19fccab6964
new file mode 100644
index 0000000..4b93a37
--- /dev/null
+++ b/tests/resources/submod3/.gitted/objects/e7/b6f5010b47e84573eb670d8b31f19fccab6964
Binary files differ
diff --git a/tests/resources/submod3/.gitted/refs/heads/master b/tests/resources/submod3/.gitted/refs/heads/master
new file mode 100644
index 0000000..706cfc4
--- /dev/null
+++ b/tests/resources/submod3/.gitted/refs/heads/master
@@ -0,0 +1 @@
+90459b51713bde15eb97852ff22c29270752b432
diff --git a/tests/resources/submod3/EIGHT/.gitted b/tests/resources/submod3/EIGHT/.gitted
new file mode 100644
index 0000000..85c3424
--- /dev/null
+++ b/tests/resources/submod3/EIGHT/.gitted
@@ -0,0 +1 @@
+gitdir: ../.git/modules/EIGHT
diff --git a/tests/resources/submod3/EIGHT/README.txt b/tests/resources/submod3/EIGHT/README.txt
new file mode 100644
index 0000000..780d739
--- /dev/null
+++ b/tests/resources/submod3/EIGHT/README.txt
@@ -0,0 +1,3 @@
+This is the target for submod2 submodule links.
+Don't add commits casually because you make break tests.
+
diff --git a/tests/resources/submod3/EIGHT/file_to_modify b/tests/resources/submod3/EIGHT/file_to_modify
new file mode 100644
index 0000000..789efbd
--- /dev/null
+++ b/tests/resources/submod3/EIGHT/file_to_modify
@@ -0,0 +1,3 @@
+This is a file to modify in submodules
+It already has some history.
+You can add local changes as needed.
diff --git a/tests/resources/submod3/Five/.gitted b/tests/resources/submod3/Five/.gitted
new file mode 100644
index 0000000..6a743bb
--- /dev/null
+++ b/tests/resources/submod3/Five/.gitted
@@ -0,0 +1 @@
+gitdir: ../.git/modules/Five
diff --git a/tests/resources/submod3/Five/README.txt b/tests/resources/submod3/Five/README.txt
new file mode 100644
index 0000000..780d739
--- /dev/null
+++ b/tests/resources/submod3/Five/README.txt
@@ -0,0 +1,3 @@
+This is the target for submod2 submodule links.
+Don't add commits casually because you make break tests.
+
diff --git a/tests/resources/submod3/Five/file_to_modify b/tests/resources/submod3/Five/file_to_modify
new file mode 100644
index 0000000..789efbd
--- /dev/null
+++ b/tests/resources/submod3/Five/file_to_modify
@@ -0,0 +1,3 @@
+This is a file to modify in submodules
+It already has some history.
+You can add local changes as needed.
diff --git a/tests/resources/submod3/FoUr/.gitted b/tests/resources/submod3/FoUr/.gitted
new file mode 100644
index 0000000..2cef334
--- /dev/null
+++ b/tests/resources/submod3/FoUr/.gitted
@@ -0,0 +1 @@
+gitdir: ../.git/modules/FoUr
diff --git a/tests/resources/submod3/FoUr/README.txt b/tests/resources/submod3/FoUr/README.txt
new file mode 100644
index 0000000..780d739
--- /dev/null
+++ b/tests/resources/submod3/FoUr/README.txt
@@ -0,0 +1,3 @@
+This is the target for submod2 submodule links.
+Don't add commits casually because you make break tests.
+
diff --git a/tests/resources/submod3/FoUr/file_to_modify b/tests/resources/submod3/FoUr/file_to_modify
new file mode 100644
index 0000000..789efbd
--- /dev/null
+++ b/tests/resources/submod3/FoUr/file_to_modify
@@ -0,0 +1,3 @@
+This is a file to modify in submodules
+It already has some history.
+You can add local changes as needed.
diff --git a/tests/resources/submod3/One/.gitted b/tests/resources/submod3/One/.gitted
new file mode 100644
index 0000000..8542888
--- /dev/null
+++ b/tests/resources/submod3/One/.gitted
@@ -0,0 +1 @@
+gitdir: ../.git/modules/One
diff --git a/tests/resources/submod3/One/README.txt b/tests/resources/submod3/One/README.txt
new file mode 100644
index 0000000..780d739
--- /dev/null
+++ b/tests/resources/submod3/One/README.txt
@@ -0,0 +1,3 @@
+This is the target for submod2 submodule links.
+Don't add commits casually because you make break tests.
+
diff --git a/tests/resources/submod3/One/file_to_modify b/tests/resources/submod3/One/file_to_modify
new file mode 100644
index 0000000..789efbd
--- /dev/null
+++ b/tests/resources/submod3/One/file_to_modify
@@ -0,0 +1,3 @@
+This is a file to modify in submodules
+It already has some history.
+You can add local changes as needed.
diff --git a/tests/resources/submod3/TEN/.gitted b/tests/resources/submod3/TEN/.gitted
new file mode 100644
index 0000000..f8f7b83
--- /dev/null
+++ b/tests/resources/submod3/TEN/.gitted
@@ -0,0 +1 @@
+gitdir: ../.git/modules/TEN
diff --git a/tests/resources/submod3/TEN/README.txt b/tests/resources/submod3/TEN/README.txt
new file mode 100644
index 0000000..780d739
--- /dev/null
+++ b/tests/resources/submod3/TEN/README.txt
@@ -0,0 +1,3 @@
+This is the target for submod2 submodule links.
+Don't add commits casually because you make break tests.
+
diff --git a/tests/resources/submod3/TEN/file_to_modify b/tests/resources/submod3/TEN/file_to_modify
new file mode 100644
index 0000000..789efbd
--- /dev/null
+++ b/tests/resources/submod3/TEN/file_to_modify
@@ -0,0 +1,3 @@
+This is a file to modify in submodules
+It already has some history.
+You can add local changes as needed.
diff --git a/tests/resources/submod3/TWO/.gitted b/tests/resources/submod3/TWO/.gitted
new file mode 100644
index 0000000..e1ddbd7
--- /dev/null
+++ b/tests/resources/submod3/TWO/.gitted
@@ -0,0 +1 @@
+gitdir: ../.git/modules/TWO
diff --git a/tests/resources/submod3/TWO/README.txt b/tests/resources/submod3/TWO/README.txt
new file mode 100644
index 0000000..780d739
--- /dev/null
+++ b/tests/resources/submod3/TWO/README.txt
@@ -0,0 +1,3 @@
+This is the target for submod2 submodule links.
+Don't add commits casually because you make break tests.
+
diff --git a/tests/resources/submod3/TWO/file_to_modify b/tests/resources/submod3/TWO/file_to_modify
new file mode 100644
index 0000000..789efbd
--- /dev/null
+++ b/tests/resources/submod3/TWO/file_to_modify
@@ -0,0 +1,3 @@
+This is a file to modify in submodules
+It already has some history.
+You can add local changes as needed.
diff --git a/tests/resources/submod3/gitmodules b/tests/resources/submod3/gitmodules
new file mode 100644
index 0000000..7308ab0
--- /dev/null
+++ b/tests/resources/submod3/gitmodules
@@ -0,0 +1,30 @@
+[submodule "One"]
+ path = One
+ url = /tmp/libgit2_tests_XJLtEE/submod2_target
+[submodule "TWO"]
+ path = TWO
+ url = /tmp/libgit2_tests_XJLtEE/submod2_target
+[submodule "three"]
+ path = three
+ url = /tmp/libgit2_tests_XJLtEE/submod2_target
+[submodule "FoUr"]
+ path = FoUr
+ url = /tmp/libgit2_tests_XJLtEE/submod2_target
+[submodule "Five"]
+ path = Five
+ url = /tmp/libgit2_tests_XJLtEE/submod2_target
+[submodule "six"]
+ path = six
+ url = /tmp/libgit2_tests_XJLtEE/submod2_target
+[submodule "sEvEn"]
+ path = sEvEn
+ url = /tmp/libgit2_tests_XJLtEE/submod2_target
+[submodule "EIGHT"]
+ path = EIGHT
+ url = /tmp/libgit2_tests_XJLtEE/submod2_target
+[submodule "nine"]
+ path = nine
+ url = /tmp/libgit2_tests_XJLtEE/submod2_target
+[submodule "TEN"]
+ path = TEN
+ url = /tmp/libgit2_tests_XJLtEE/submod2_target
diff --git a/tests/resources/submod3/nine/.gitted b/tests/resources/submod3/nine/.gitted
new file mode 100644
index 0000000..a49dfa7
--- /dev/null
+++ b/tests/resources/submod3/nine/.gitted
@@ -0,0 +1 @@
+gitdir: ../.git/modules/nine
diff --git a/tests/resources/submod3/nine/README.txt b/tests/resources/submod3/nine/README.txt
new file mode 100644
index 0000000..780d739
--- /dev/null
+++ b/tests/resources/submod3/nine/README.txt
@@ -0,0 +1,3 @@
+This is the target for submod2 submodule links.
+Don't add commits casually because you make break tests.
+
diff --git a/tests/resources/submod3/nine/file_to_modify b/tests/resources/submod3/nine/file_to_modify
new file mode 100644
index 0000000..789efbd
--- /dev/null
+++ b/tests/resources/submod3/nine/file_to_modify
@@ -0,0 +1,3 @@
+This is a file to modify in submodules
+It already has some history.
+You can add local changes as needed.
diff --git a/tests/resources/submod3/sEvEn/.gitted b/tests/resources/submod3/sEvEn/.gitted
new file mode 100644
index 0000000..20e83f0
--- /dev/null
+++ b/tests/resources/submod3/sEvEn/.gitted
@@ -0,0 +1 @@
+gitdir: ../.git/modules/sEvEn
diff --git a/tests/resources/submod3/sEvEn/README.txt b/tests/resources/submod3/sEvEn/README.txt
new file mode 100644
index 0000000..780d739
--- /dev/null
+++ b/tests/resources/submod3/sEvEn/README.txt
@@ -0,0 +1,3 @@
+This is the target for submod2 submodule links.
+Don't add commits casually because you make break tests.
+
diff --git a/tests/resources/submod3/sEvEn/file_to_modify b/tests/resources/submod3/sEvEn/file_to_modify
new file mode 100644
index 0000000..789efbd
--- /dev/null
+++ b/tests/resources/submod3/sEvEn/file_to_modify
@@ -0,0 +1,3 @@
+This is a file to modify in submodules
+It already has some history.
+You can add local changes as needed.
diff --git a/tests/resources/submod3/six/.gitted b/tests/resources/submod3/six/.gitted
new file mode 100644
index 0000000..4dec895
--- /dev/null
+++ b/tests/resources/submod3/six/.gitted
@@ -0,0 +1 @@
+gitdir: ../.git/modules/six
diff --git a/tests/resources/submod3/six/README.txt b/tests/resources/submod3/six/README.txt
new file mode 100644
index 0000000..780d739
--- /dev/null
+++ b/tests/resources/submod3/six/README.txt
@@ -0,0 +1,3 @@
+This is the target for submod2 submodule links.
+Don't add commits casually because you make break tests.
+
diff --git a/tests/resources/submod3/six/file_to_modify b/tests/resources/submod3/six/file_to_modify
new file mode 100644
index 0000000..789efbd
--- /dev/null
+++ b/tests/resources/submod3/six/file_to_modify
@@ -0,0 +1,3 @@
+This is a file to modify in submodules
+It already has some history.
+You can add local changes as needed.
diff --git a/tests/resources/submod3/three/.gitted b/tests/resources/submod3/three/.gitted
new file mode 100644
index 0000000..6025d2d
--- /dev/null
+++ b/tests/resources/submod3/three/.gitted
@@ -0,0 +1 @@
+gitdir: ../.git/modules/three
diff --git a/tests/resources/submod3/three/README.txt b/tests/resources/submod3/three/README.txt
new file mode 100644
index 0000000..780d739
--- /dev/null
+++ b/tests/resources/submod3/three/README.txt
@@ -0,0 +1,3 @@
+This is the target for submod2 submodule links.
+Don't add commits casually because you make break tests.
+
diff --git a/tests/resources/submod3/three/file_to_modify b/tests/resources/submod3/three/file_to_modify
new file mode 100644
index 0000000..789efbd
--- /dev/null
+++ b/tests/resources/submod3/three/file_to_modify
@@ -0,0 +1,3 @@
+This is a file to modify in submodules
+It already has some history.
+You can add local changes as needed.
diff --git a/tests/resources/submodule_simple/.gitmodules b/tests/resources/submodule_simple/.gitmodules
new file mode 100644
index 0000000..03150b4
--- /dev/null
+++ b/tests/resources/submodule_simple/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "testrepo"]
+ path = testrepo
+ url = ../testrepo.git
diff --git a/tests/resources/submodule_simple/.gitted/HEAD b/tests/resources/submodule_simple/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/submodule_simple/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submodule_simple/.gitted/config b/tests/resources/submodule_simple/.gitted/config
new file mode 100644
index 0000000..78387c5
--- /dev/null
+++ b/tests/resources/submodule_simple/.gitted/config
@@ -0,0 +1,8 @@
+[core]
+ repositoryformatversion = 0
+ filemode = false
+ bare = false
+ logallrefupdates = true
+ symlinks = false
+ ignorecase = true
+ hideDotFiles = dotGitOnly
diff --git a/tests/resources/submodule_simple/.gitted/description b/tests/resources/submodule_simple/.gitted/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/submodule_simple/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/submodule_simple/.gitted/index b/tests/resources/submodule_simple/.gitted/index
new file mode 100644
index 0000000..6e22d7f
--- /dev/null
+++ b/tests/resources/submodule_simple/.gitted/index
Binary files differ
diff --git a/tests/resources/submodule_simple/.gitted/objects/22/9cea838964f435d4fc2c11561ddb7447003609 b/tests/resources/submodule_simple/.gitted/objects/22/9cea838964f435d4fc2c11561ddb7447003609
new file mode 100644
index 0000000..9f0800d
--- /dev/null
+++ b/tests/resources/submodule_simple/.gitted/objects/22/9cea838964f435d4fc2c11561ddb7447003609
Binary files differ
diff --git a/tests/resources/submodule_simple/.gitted/objects/5b/19f7523fbf55c96153ff5a94875583f1115a36 b/tests/resources/submodule_simple/.gitted/objects/5b/19f7523fbf55c96153ff5a94875583f1115a36
new file mode 100644
index 0000000..d0681ac
--- /dev/null
+++ b/tests/resources/submodule_simple/.gitted/objects/5b/19f7523fbf55c96153ff5a94875583f1115a36
Binary files differ
diff --git a/tests/resources/submodule_simple/.gitted/objects/a8/575e6aaececba78823993e4f11abbc6172aabd b/tests/resources/submodule_simple/.gitted/objects/a8/575e6aaececba78823993e4f11abbc6172aabd
new file mode 100644
index 0000000..b8961b0
--- /dev/null
+++ b/tests/resources/submodule_simple/.gitted/objects/a8/575e6aaececba78823993e4f11abbc6172aabd
Binary files differ
diff --git a/tests/resources/submodule_simple/.gitted/objects/b4/f28943fad380f4ee3a9c6b95259b28204cc25a b/tests/resources/submodule_simple/.gitted/objects/b4/f28943fad380f4ee3a9c6b95259b28204cc25a
new file mode 100644
index 0000000..653238c
--- /dev/null
+++ b/tests/resources/submodule_simple/.gitted/objects/b4/f28943fad380f4ee3a9c6b95259b28204cc25a
Binary files differ
diff --git a/tests/resources/submodule_simple/.gitted/objects/d6/9ff504a3ba631f2fdb35bff93cc8cb8e85f4f8 b/tests/resources/submodule_simple/.gitted/objects/d6/9ff504a3ba631f2fdb35bff93cc8cb8e85f4f8
new file mode 100644
index 0000000..dabf65b
--- /dev/null
+++ b/tests/resources/submodule_simple/.gitted/objects/d6/9ff504a3ba631f2fdb35bff93cc8cb8e85f4f8
Binary files differ
diff --git a/tests/resources/submodule_simple/.gitted/packed-refs b/tests/resources/submodule_simple/.gitted/packed-refs
new file mode 100644
index 0000000..0400126
--- /dev/null
+++ b/tests/resources/submodule_simple/.gitted/packed-refs
@@ -0,0 +1,3 @@
+# pack-refs with: peeled fully-peeled
+229cea838964f435d4fc2c11561ddb7447003609 refs/remotes/origin/alternate_1
+a8575e6aaececba78823993e4f11abbc6172aabd refs/remotes/origin/master
diff --git a/tests/resources/submodule_simple/.gitted/refs/heads/alternate_1 b/tests/resources/submodule_simple/.gitted/refs/heads/alternate_1
new file mode 100644
index 0000000..6f51966
--- /dev/null
+++ b/tests/resources/submodule_simple/.gitted/refs/heads/alternate_1
@@ -0,0 +1 @@
+229cea838964f435d4fc2c11561ddb7447003609
diff --git a/tests/resources/submodule_simple/.gitted/refs/heads/master b/tests/resources/submodule_simple/.gitted/refs/heads/master
new file mode 100644
index 0000000..f01cfb2
--- /dev/null
+++ b/tests/resources/submodule_simple/.gitted/refs/heads/master
@@ -0,0 +1 @@
+a8575e6aaececba78823993e4f11abbc6172aabd
diff --git a/tests/resources/submodule_with_path/.gitmodules b/tests/resources/submodule_with_path/.gitmodules
new file mode 100644
index 0000000..ba34c47
--- /dev/null
+++ b/tests/resources/submodule_with_path/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "testrepo"]
+ path = lib/testrepo
+ url = ../testrepo.git
diff --git a/tests/resources/submodule_with_path/.gitted/HEAD b/tests/resources/submodule_with_path/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/submodule_with_path/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submodule_with_path/.gitted/config b/tests/resources/submodule_with_path/.gitted/config
new file mode 100644
index 0000000..78387c5
--- /dev/null
+++ b/tests/resources/submodule_with_path/.gitted/config
@@ -0,0 +1,8 @@
+[core]
+ repositoryformatversion = 0
+ filemode = false
+ bare = false
+ logallrefupdates = true
+ symlinks = false
+ ignorecase = true
+ hideDotFiles = dotGitOnly
diff --git a/tests/resources/submodule_with_path/.gitted/index b/tests/resources/submodule_with_path/.gitted/index
new file mode 100644
index 0000000..a740b4b
--- /dev/null
+++ b/tests/resources/submodule_with_path/.gitted/index
Binary files differ
diff --git a/tests/resources/submodule_with_path/.gitted/objects/18/372280a56a54340fa600aa91315065c6c4c693 b/tests/resources/submodule_with_path/.gitted/objects/18/372280a56a54340fa600aa91315065c6c4c693
new file mode 100644
index 0000000..d9b4313
--- /dev/null
+++ b/tests/resources/submodule_with_path/.gitted/objects/18/372280a56a54340fa600aa91315065c6c4c693
Binary files differ
diff --git a/tests/resources/submodule_with_path/.gitted/objects/36/683131578275f6a8fd1c539e0d5da0d8adff26 b/tests/resources/submodule_with_path/.gitted/objects/36/683131578275f6a8fd1c539e0d5da0d8adff26
new file mode 100644
index 0000000..366c161
--- /dev/null
+++ b/tests/resources/submodule_with_path/.gitted/objects/36/683131578275f6a8fd1c539e0d5da0d8adff26
Binary files differ
diff --git a/tests/resources/submodule_with_path/.gitted/objects/89/ca686bb21bfb75dda99a02313831a0c418f921 b/tests/resources/submodule_with_path/.gitted/objects/89/ca686bb21bfb75dda99a02313831a0c418f921
new file mode 100644
index 0000000..7c1af66
--- /dev/null
+++ b/tests/resources/submodule_with_path/.gitted/objects/89/ca686bb21bfb75dda99a02313831a0c418f921
Binary files differ
diff --git a/tests/resources/submodule_with_path/.gitted/objects/b1/620ef2628d10416a84d19c783e33dc4556c9c3 b/tests/resources/submodule_with_path/.gitted/objects/b1/620ef2628d10416a84d19c783e33dc4556c9c3
new file mode 100644
index 0000000..4475582
--- /dev/null
+++ b/tests/resources/submodule_with_path/.gitted/objects/b1/620ef2628d10416a84d19c783e33dc4556c9c3
Binary files differ
diff --git a/tests/resources/submodule_with_path/.gitted/objects/ba/34c47dc9d3d0b1bb335b45c9d26ba1f0fc90c7 b/tests/resources/submodule_with_path/.gitted/objects/ba/34c47dc9d3d0b1bb335b45c9d26ba1f0fc90c7
new file mode 100644
index 0000000..3069f15
--- /dev/null
+++ b/tests/resources/submodule_with_path/.gitted/objects/ba/34c47dc9d3d0b1bb335b45c9d26ba1f0fc90c7
Binary files differ
diff --git a/tests/resources/submodule_with_path/.gitted/objects/c8/4bf57ba2254dba216ab5c6eb1a19fe8bd0e0d6 b/tests/resources/submodule_with_path/.gitted/objects/c8/4bf57ba2254dba216ab5c6eb1a19fe8bd0e0d6
new file mode 100644
index 0000000..9f66456
--- /dev/null
+++ b/tests/resources/submodule_with_path/.gitted/objects/c8/4bf57ba2254dba216ab5c6eb1a19fe8bd0e0d6
Binary files differ
diff --git a/tests/resources/submodule_with_path/.gitted/objects/d5/45fc6b40ec9e67332b6a1d2dedcbdb1bffeb6b b/tests/resources/submodule_with_path/.gitted/objects/d5/45fc6b40ec9e67332b6a1d2dedcbdb1bffeb6b
new file mode 100644
index 0000000..56a8449
--- /dev/null
+++ b/tests/resources/submodule_with_path/.gitted/objects/d5/45fc6b40ec9e67332b6a1d2dedcbdb1bffeb6b
Binary files differ
diff --git a/tests/resources/submodule_with_path/.gitted/refs/heads/master b/tests/resources/submodule_with_path/.gitted/refs/heads/master
new file mode 100644
index 0000000..4b5a5a2
--- /dev/null
+++ b/tests/resources/submodule_with_path/.gitted/refs/heads/master
@@ -0,0 +1 @@
+89ca686bb21bfb75dda99a02313831a0c418f921
diff --git a/tests/resources/submodules-worktree-child/.gitted b/tests/resources/submodules-worktree-child/.gitted
new file mode 100644
index 0000000..03286f5
--- /dev/null
+++ b/tests/resources/submodules-worktree-child/.gitted
@@ -0,0 +1 @@
+gitdir: ../submodules/testrepo/.git/worktrees/submodules-worktree-child
diff --git a/tests/resources/submodules-worktree-child/README b/tests/resources/submodules-worktree-child/README
new file mode 100644
index 0000000..a823312
--- /dev/null
+++ b/tests/resources/submodules-worktree-child/README
@@ -0,0 +1 @@
+hey there
diff --git a/tests/resources/submodules-worktree-child/branch_file.txt b/tests/resources/submodules-worktree-child/branch_file.txt
new file mode 100644
index 0000000..3697d64
--- /dev/null
+++ b/tests/resources/submodules-worktree-child/branch_file.txt
@@ -0,0 +1,2 @@
+hi
+bye!
diff --git a/tests/resources/submodules-worktree-child/new.txt b/tests/resources/submodules-worktree-child/new.txt
new file mode 100644
index 0000000..a71586c
--- /dev/null
+++ b/tests/resources/submodules-worktree-child/new.txt
@@ -0,0 +1 @@
+my new file
diff --git a/tests/resources/submodules-worktree-parent/.gitmodules b/tests/resources/submodules-worktree-parent/.gitmodules
new file mode 100644
index 0000000..78308c9
--- /dev/null
+++ b/tests/resources/submodules-worktree-parent/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "testrepo"]
+ path = testrepo
+ url = /Users/rb/src/libgit2/tests/resources/testrepo.git
diff --git a/tests/resources/submodules-worktree-parent/.gitted b/tests/resources/submodules-worktree-parent/.gitted
new file mode 100644
index 0000000..87bd9ae
--- /dev/null
+++ b/tests/resources/submodules-worktree-parent/.gitted
@@ -0,0 +1 @@
+gitdir: ../submodules/.git/worktrees/submodules-worktree-parent
diff --git a/tests/resources/submodules-worktree-parent/deleted b/tests/resources/submodules-worktree-parent/deleted
new file mode 100644
index 0000000..092bfb9
--- /dev/null
+++ b/tests/resources/submodules-worktree-parent/deleted
@@ -0,0 +1 @@
+yo
diff --git a/tests/resources/submodules-worktree-parent/modified b/tests/resources/submodules-worktree-parent/modified
new file mode 100644
index 0000000..092bfb9
--- /dev/null
+++ b/tests/resources/submodules-worktree-parent/modified
@@ -0,0 +1 @@
+yo
diff --git a/tests/resources/submodules-worktree-parent/unmodified b/tests/resources/submodules-worktree-parent/unmodified
new file mode 100644
index 0000000..092bfb9
--- /dev/null
+++ b/tests/resources/submodules-worktree-parent/unmodified
@@ -0,0 +1 @@
+yo
diff --git a/tests/resources/submodules.git/HEAD b/tests/resources/submodules.git/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/submodules.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submodules.git/config b/tests/resources/submodules.git/config
new file mode 100644
index 0000000..07d359d
--- /dev/null
+++ b/tests/resources/submodules.git/config
@@ -0,0 +1,4 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = true
diff --git a/tests/resources/submodules.git/objects/26/a3b32a9b7d97486c5557f5902e8ac94638145e b/tests/resources/submodules.git/objects/26/a3b32a9b7d97486c5557f5902e8ac94638145e
new file mode 100644
index 0000000..2c3c2cb
--- /dev/null
+++ b/tests/resources/submodules.git/objects/26/a3b32a9b7d97486c5557f5902e8ac94638145e
@@ -0,0 +1,2 @@
+x%‰=
+€0 F]í)Š0à"ÃIŒ*•|Éý-t{?œ2ÇilV8¿ùô$±«Øm¡ýv»ãk­k*F DAÊ=(=|=6 ¬DAv=ÛÍA}™&'…Oò$= \ No newline at end of file
diff --git a/tests/resources/submodules.git/objects/78/308c9251cf4eee8b25a76c7d2790c73d797357 b/tests/resources/submodules.git/objects/78/308c9251cf4eee8b25a76c7d2790c73d797357
new file mode 100644
index 0000000..c85fb55
--- /dev/null
+++ b/tests/resources/submodules.git/objects/78/308c9251cf4eee8b25a76c7d2790c73d797357
Binary files differ
diff --git a/tests/resources/submodules.git/objects/97/896810b3210244a62a82458b8e0819ecfc6850 b/tests/resources/submodules.git/objects/97/896810b3210244a62a82458b8e0819ecfc6850
new file mode 100644
index 0000000..1c8dbdf
--- /dev/null
+++ b/tests/resources/submodules.git/objects/97/896810b3210244a62a82458b8e0819ecfc6850
@@ -0,0 +1,3 @@
+xŽ[
+Â0EýÎ*fʤS“ ˆˆKФéú4Ý¿wà×…Ã9pÓ2MC¥FôP @ãÜu.„.¶pÚ!²OYáƒdiYUÍ'Ì•8XïbPn¼ôÊ6
+ħԞ“¶1[qîÌ}0q«ï¥Ðc[WŒ#Ý1fºÄR:àö›SZ¦+Y‘Æ+{µtdÏlvº¬»þOmž¨u˜_´}è5Ôié·«ù` Kæ \ No newline at end of file
diff --git a/tests/resources/submodules.git/objects/b6/0fd986699ba4e9e68bea07cf8e793f323ef888 b/tests/resources/submodules.git/objects/b6/0fd986699ba4e9e68bea07cf8e793f323ef888
new file mode 100644
index 0000000..3d78bd6
--- /dev/null
+++ b/tests/resources/submodules.git/objects/b6/0fd986699ba4e9e68bea07cf8e793f323ef888
Binary files differ
diff --git a/tests/resources/submodules.git/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818 b/tests/resources/submodules.git/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818
new file mode 100644
index 0000000..6e0b49e
--- /dev/null
+++ b/tests/resources/submodules.git/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818
Binary files differ
diff --git a/tests/resources/submodules.git/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4ae b/tests/resources/submodules.git/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4ae
new file mode 100644
index 0000000..082a589
--- /dev/null
+++ b/tests/resources/submodules.git/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4ae
Binary files differ
diff --git a/tests/resources/submodules.git/objects/info/packs b/tests/resources/submodules.git/objects/info/packs
new file mode 100644
index 0000000..0785ef6
--- /dev/null
+++ b/tests/resources/submodules.git/objects/info/packs
@@ -0,0 +1,2 @@
+P pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack
+
diff --git a/tests/resources/submodules.git/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.idx b/tests/resources/submodules.git/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.idx
new file mode 100644
index 0000000..810fc31
--- /dev/null
+++ b/tests/resources/submodules.git/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.idx
Binary files differ
diff --git a/tests/resources/submodules.git/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack b/tests/resources/submodules.git/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack
new file mode 100644
index 0000000..c25c4a7
--- /dev/null
+++ b/tests/resources/submodules.git/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack
Binary files differ
diff --git a/tests/resources/submodules.git/refs/heads/master b/tests/resources/submodules.git/refs/heads/master
new file mode 100644
index 0000000..32b9358
--- /dev/null
+++ b/tests/resources/submodules.git/refs/heads/master
@@ -0,0 +1 @@
+97896810b3210244a62a82458b8e0819ecfc6850
diff --git a/tests/resources/submodules/.gitted/HEAD b/tests/resources/submodules/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/submodules/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submodules/.gitted/config b/tests/resources/submodules/.gitted/config
new file mode 100644
index 0000000..af10792
--- /dev/null
+++ b/tests/resources/submodules/.gitted/config
@@ -0,0 +1,6 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
diff --git a/tests/resources/submodules/.gitted/description b/tests/resources/submodules/.gitted/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/submodules/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/submodules/.gitted/index b/tests/resources/submodules/.gitted/index
new file mode 100644
index 0000000..97bf8ef
--- /dev/null
+++ b/tests/resources/submodules/.gitted/index
Binary files differ
diff --git a/tests/resources/submodules/.gitted/info/exclude b/tests/resources/submodules/.gitted/info/exclude
new file mode 100644
index 0000000..dfc4115
--- /dev/null
+++ b/tests/resources/submodules/.gitted/info/exclude
@@ -0,0 +1,8 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
+ignored
+
diff --git a/tests/resources/submodules/.gitted/info/refs b/tests/resources/submodules/.gitted/info/refs
new file mode 100644
index 0000000..ba17abd
--- /dev/null
+++ b/tests/resources/submodules/.gitted/info/refs
@@ -0,0 +1 @@
+09176a980273d801a3e37cc45c84af1366501ed9 refs/heads/master
diff --git a/tests/resources/submodules/.gitted/logs/HEAD b/tests/resources/submodules/.gitted/logs/HEAD
new file mode 100644
index 0000000..87a7bda
--- /dev/null
+++ b/tests/resources/submodules/.gitted/logs/HEAD
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 09176a980273d801a3e37cc45c84af1366501ed9 Russell Belfer <arrbee@arrbee.com> 1332365253 -0700 commit (initial): initial commit
+09176a980273d801a3e37cc45c84af1366501ed9 97896810b3210244a62a82458b8e0819ecfc6850 Russell Belfer <arrbee@arrbee.com> 1332780781 -0700 commit: Setting up gitmodules
diff --git a/tests/resources/submodules/.gitted/logs/refs/heads/master b/tests/resources/submodules/.gitted/logs/refs/heads/master
new file mode 100644
index 0000000..87a7bda
--- /dev/null
+++ b/tests/resources/submodules/.gitted/logs/refs/heads/master
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 09176a980273d801a3e37cc45c84af1366501ed9 Russell Belfer <arrbee@arrbee.com> 1332365253 -0700 commit (initial): initial commit
+09176a980273d801a3e37cc45c84af1366501ed9 97896810b3210244a62a82458b8e0819ecfc6850 Russell Belfer <arrbee@arrbee.com> 1332780781 -0700 commit: Setting up gitmodules
diff --git a/tests/resources/submodules/.gitted/logs/refs/heads/submodules-worktree-parent b/tests/resources/submodules/.gitted/logs/refs/heads/submodules-worktree-parent
new file mode 100644
index 0000000..65e9885
--- /dev/null
+++ b/tests/resources/submodules/.gitted/logs/refs/heads/submodules-worktree-parent
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 97896810b3210244a62a82458b8e0819ecfc6850 Patrick Steinhardt <ps@pks.im> 1447084240 +0100 branch: Created from HEAD
diff --git a/tests/resources/submodules/.gitted/objects/26/a3b32a9b7d97486c5557f5902e8ac94638145e b/tests/resources/submodules/.gitted/objects/26/a3b32a9b7d97486c5557f5902e8ac94638145e
new file mode 100644
index 0000000..2c3c2cb
--- /dev/null
+++ b/tests/resources/submodules/.gitted/objects/26/a3b32a9b7d97486c5557f5902e8ac94638145e
@@ -0,0 +1,2 @@
+x%‰=
+€0 F]í)Š0à"ÃIŒ*•|Éý-t{?œ2ÇilV8¿ùô$±«Øm¡ýv»ãk­k*F DAÊ=(=|=6 ¬DAv=ÛÍA}™&'…Oò$= \ No newline at end of file
diff --git a/tests/resources/submodules/.gitted/objects/78/308c9251cf4eee8b25a76c7d2790c73d797357 b/tests/resources/submodules/.gitted/objects/78/308c9251cf4eee8b25a76c7d2790c73d797357
new file mode 100644
index 0000000..c85fb55
--- /dev/null
+++ b/tests/resources/submodules/.gitted/objects/78/308c9251cf4eee8b25a76c7d2790c73d797357
Binary files differ
diff --git a/tests/resources/submodules/.gitted/objects/97/896810b3210244a62a82458b8e0819ecfc6850 b/tests/resources/submodules/.gitted/objects/97/896810b3210244a62a82458b8e0819ecfc6850
new file mode 100644
index 0000000..1c8dbdf
--- /dev/null
+++ b/tests/resources/submodules/.gitted/objects/97/896810b3210244a62a82458b8e0819ecfc6850
@@ -0,0 +1,3 @@
+xŽ[
+Â0EýÎ*fʤS“ ˆˆKФéú4Ý¿wà×…Ã9pÓ2MC¥FôP @ãÜu.„.¶pÚ!²OYáƒdiYUÍ'Ì•8XïbPn¼ôÊ6
+ħԞ“¶1[qîÌ}0q«ï¥Ðc[WŒ#Ý1fºÄR:àö›SZ¦+Y‘Æ+{µtdÏlvº¬»þOmž¨u˜_´}è5Ôié·«ù` Kæ \ No newline at end of file
diff --git a/tests/resources/submodules/.gitted/objects/b6/0fd986699ba4e9e68bea07cf8e793f323ef888 b/tests/resources/submodules/.gitted/objects/b6/0fd986699ba4e9e68bea07cf8e793f323ef888
new file mode 100644
index 0000000..3d78bd6
--- /dev/null
+++ b/tests/resources/submodules/.gitted/objects/b6/0fd986699ba4e9e68bea07cf8e793f323ef888
Binary files differ
diff --git a/tests/resources/submodules/.gitted/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818 b/tests/resources/submodules/.gitted/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818
new file mode 100644
index 0000000..6e0b49e
--- /dev/null
+++ b/tests/resources/submodules/.gitted/objects/d5/f7fc3f74f7dec08280f370a975b112e8f60818
Binary files differ
diff --git a/tests/resources/submodules/.gitted/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4ae b/tests/resources/submodules/.gitted/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4ae
new file mode 100644
index 0000000..082a589
--- /dev/null
+++ b/tests/resources/submodules/.gitted/objects/e3/50052cc767cd1fcb37e84e9a89e701925be4ae
Binary files differ
diff --git a/tests/resources/submodules/.gitted/objects/info/packs b/tests/resources/submodules/.gitted/objects/info/packs
new file mode 100644
index 0000000..0785ef6
--- /dev/null
+++ b/tests/resources/submodules/.gitted/objects/info/packs
@@ -0,0 +1,2 @@
+P pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack
+
diff --git a/tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.idx b/tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.idx
new file mode 100644
index 0000000..810fc31
--- /dev/null
+++ b/tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.idx
Binary files differ
diff --git a/tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack b/tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack
new file mode 100644
index 0000000..c25c4a7
--- /dev/null
+++ b/tests/resources/submodules/.gitted/objects/pack/pack-b69d04bb39ac274669e2184e45bd90015d02ef5b.pack
Binary files differ
diff --git a/tests/resources/submodules/.gitted/packed-refs b/tests/resources/submodules/.gitted/packed-refs
new file mode 100644
index 0000000..a645069
--- /dev/null
+++ b/tests/resources/submodules/.gitted/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+09176a980273d801a3e37cc45c84af1366501ed9 refs/heads/master
diff --git a/tests/resources/submodules/.gitted/refs/heads/master b/tests/resources/submodules/.gitted/refs/heads/master
new file mode 100644
index 0000000..32b9358
--- /dev/null
+++ b/tests/resources/submodules/.gitted/refs/heads/master
@@ -0,0 +1 @@
+97896810b3210244a62a82458b8e0819ecfc6850
diff --git a/tests/resources/submodules/.gitted/refs/heads/submodules-worktree-parent b/tests/resources/submodules/.gitted/refs/heads/submodules-worktree-parent
new file mode 100644
index 0000000..32b9358
--- /dev/null
+++ b/tests/resources/submodules/.gitted/refs/heads/submodules-worktree-parent
@@ -0,0 +1 @@
+97896810b3210244a62a82458b8e0819ecfc6850
diff --git a/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/HEAD b/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/HEAD
new file mode 100644
index 0000000..a07134b
--- /dev/null
+++ b/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/submodules-worktree-parent
diff --git a/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/ORIG_HEAD b/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/ORIG_HEAD
new file mode 100644
index 0000000..32b9358
--- /dev/null
+++ b/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/ORIG_HEAD
@@ -0,0 +1 @@
+97896810b3210244a62a82458b8e0819ecfc6850
diff --git a/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/commondir b/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/commondir
new file mode 100644
index 0000000..aab0408
--- /dev/null
+++ b/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/commondir
@@ -0,0 +1 @@
+../..
diff --git a/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/gitdir b/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/gitdir
new file mode 100644
index 0000000..eaaf13b
--- /dev/null
+++ b/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/gitdir
@@ -0,0 +1 @@
+../../../../submodules-worktree-parent/.git
diff --git a/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/index b/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/index
new file mode 100644
index 0000000..5b68f18
--- /dev/null
+++ b/tests/resources/submodules/.gitted/worktrees/submodules-worktree-parent/index
Binary files differ
diff --git a/tests/resources/submodules/added b/tests/resources/submodules/added
new file mode 100644
index 0000000..d5f7fc3
--- /dev/null
+++ b/tests/resources/submodules/added
@@ -0,0 +1 @@
+added
diff --git a/tests/resources/submodules/gitmodules b/tests/resources/submodules/gitmodules
new file mode 100644
index 0000000..2798b69
--- /dev/null
+++ b/tests/resources/submodules/gitmodules
@@ -0,0 +1,6 @@
+[submodule "testrepo"]
+ path = testrepo
+ url =
+[submodule ""]
+ path = testrepo
+ url = \ No newline at end of file
diff --git a/tests/resources/submodules/ignored b/tests/resources/submodules/ignored
new file mode 100644
index 0000000..092bfb9
--- /dev/null
+++ b/tests/resources/submodules/ignored
@@ -0,0 +1 @@
+yo
diff --git a/tests/resources/submodules/modified b/tests/resources/submodules/modified
new file mode 100644
index 0000000..452216e
--- /dev/null
+++ b/tests/resources/submodules/modified
@@ -0,0 +1,2 @@
+changed
+
diff --git a/tests/resources/submodules/testrepo/.gitted/HEAD b/tests/resources/submodules/testrepo/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/submodules/testrepo/.gitted/config b/tests/resources/submodules/testrepo/.gitted/config
new file mode 100644
index 0000000..8e55711
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/config
@@ -0,0 +1,9 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/submodules/testrepo/.gitted/description b/tests/resources/submodules/testrepo/.gitted/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/submodules/testrepo/.gitted/index b/tests/resources/submodules/testrepo/.gitted/index
new file mode 100644
index 0000000..3eb8d84
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/index
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/info/exclude b/tests/resources/submodules/testrepo/.gitted/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/submodules/testrepo/.gitted/logs/HEAD b/tests/resources/submodules/testrepo/.gitted/logs/HEAD
new file mode 100644
index 0000000..147643a
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 a65fedf39aefe402d3bb6e24df4d4f5fe4547750 Russell Belfer <arrbee@arrbee.com> 1332366307 -0700 clone: from /Users/rb/src/libgit2/tests/resources/testrepo.git
diff --git a/tests/resources/submodules/testrepo/.gitted/logs/refs/heads/master b/tests/resources/submodules/testrepo/.gitted/logs/refs/heads/master
new file mode 100644
index 0000000..147643a
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 a65fedf39aefe402d3bb6e24df4d4f5fe4547750 Russell Belfer <arrbee@arrbee.com> 1332366307 -0700 clone: from /Users/rb/src/libgit2/tests/resources/testrepo.git
diff --git a/tests/resources/submodules/testrepo/.gitted/logs/refs/heads/submodules-worktree-child b/tests/resources/submodules/testrepo/.gitted/logs/refs/heads/submodules-worktree-child
new file mode 100644
index 0000000..dd4650f
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/logs/refs/heads/submodules-worktree-child
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 a65fedf39aefe402d3bb6e24df4d4f5fe4547750 Patrick Steinhardt <ps@pks.im> 1447084252 +0100 branch: Created from HEAD
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 b/tests/resources/submodules/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
new file mode 100644
index 0000000..cedb2a2
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 b/tests/resources/submodules/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
new file mode 100644
index 0000000..93a16f1
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd b/tests/resources/submodules/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
new file mode 100644
index 0000000..ba0bfb3
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b b/tests/resources/submodules/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
new file mode 100644
index 0000000..225c457
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d b/tests/resources/submodules/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
new file mode 100644
index 0000000..df40d99
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54 b/tests/resources/submodules/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
new file mode 100644
index 0000000..321eaa8
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc b/tests/resources/submodules/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
new file mode 100644
index 0000000..9bb5b62
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests/resources/submodules/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
new file mode 100644
index 0000000..7ca4cee
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 b/tests/resources/submodules/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
new file mode 100644
index 0000000..8953b6c
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
@@ -0,0 +1,2 @@
+xŽQ
+Â0DýÎ)öÊ6›Í¦ "xO°‰‰-ØFb¼¿EoàÏ0 ¼Ç¤º,ske×[ÎPn8R,EpD?±gŸ}Ê^3² âÙ<µåµGŽhYKÄèÒ8ЖDAÉ)¿ÉÈ;gôݧÚàšjïp™4ÕŽ¯ô-çû¢óãêr‚ÁŠ;°s°GA4Ûº=ìùÖ(ôin7øIÌKÍFE \ No newline at end of file
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 b/tests/resources/submodules/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
new file mode 100644
index 0000000..c1f22c5
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
@@ -0,0 +1,2 @@
+xŽÛ 1EýNi@™Ék2 "X‚$ÙYW0YcÿíÀ¿Ã…s¸¥ÕzïÚÚõMDÏ€0æœ8!¶†ÉÌÞs‰ XŠªgÚdí::@X0»P¢wÙ"F/‰‰œÍRàˆUz÷¥múZZïú²¤ÒV}|•/œo5݇ÒêI£!¬1z Æ:vùÇUim}ê/¢>
+öF- \ No newline at end of file
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a b/tests/resources/submodules/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
new file mode 100644
index 0000000..2ef4faa
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af b/tests/resources/submodules/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
new file mode 100644
index 0000000..716b0c6
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
@@ -0,0 +1 @@
+xŽAj!³ö?0¨£ßÂ09Êo}HÚ6¨}ÿôjUPP©ÕZ&Yÿø˜ AÔ›±€pŒÁFdë¼÷pz[fŽYŒ½PÒqLJ.,Z§`™Å®Ð.ù`’vÙ ³q $Æ5+9çOëtœû>Û/úDE/龡W¯ï*e¿§VŸdf1>ð覭Öê²×äÄ›¹úÊ™F« ­ìTŽÙhœk.i¶^0Ô?P¼R, \ No newline at end of file
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980 b/tests/resources/submodules/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980
new file mode 100644
index 0000000..23c462f
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d b/tests/resources/submodules/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
new file mode 100644
index 0000000..2f9b6b6
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479 b/tests/resources/submodules/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
new file mode 100644
index 0000000..5df58dd
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162 b/tests/resources/submodules/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
new file mode 100644
index 0000000..4cc3f4d
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
@@ -0,0 +1 @@
+x+)JMU044b040031QrutñueX¡l¨ðmmA‹m›Ì£íJ}Gß;U‘T”˜—œŸ–™“ªWRQÂ`6ýš÷KÇ¥¶^/¾-*|òøWØ¥3P¥y©å`%ËEÛÞ±\&gŽÐ|Ÿ0§ÿ†{Ó1X \ No newline at end of file
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4 b/tests/resources/submodules/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
new file mode 100644
index 0000000..bf7b2bb
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a b/tests/resources/submodules/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
new file mode 100644
index 0000000..a796124
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
@@ -0,0 +1,3 @@
+xŽ[
+Â0EýÎ*fÊäÕ¤ "¸W0“‡-ØFâtÿÝ—çpS[–YÀ˜x^
+Díb CLhutɉ}¥8X*4Zí¬sY½¨—UÀ‘AÃÖ ÌX3‡R«Mµ¶) s6è¼¢M¦ÖážšÜ&Jm…ó;}Çõ±Ðü<¥¶\@›à‚ÑÞpÄ€¨vº?”ò«jÛºLð«¨Ø?Hå \ No newline at end of file
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f b/tests/resources/submodules/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
new file mode 100644
index 0000000..f858869
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
@@ -0,0 +1,2 @@
+x;j1DëmdÓú·À˜ÇŽ|M«µ3`ŒV{ >€³âQ¯ ¸·vL0I?Í!š4–Z=Ê! ×¦8²F¢Ã’!rÖsQßyÈ9]$DŽ&„l6AÇ>jFWüÒµ IKNiûë§Z¢%¡SˆŒ‘
+‹Ò ­ÅʉøU~̽øä>'¼ï™û ¯wþ ×[ËÇ× ÷öÚDGÚ¡±ðŒQ-ºMù«>dܶ‘OÞáÒò}í\à8g_ШÂoYr \ No newline at end of file
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750 b/tests/resources/submodules/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
new file mode 100644
index 0000000..29c8e82
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
@@ -0,0 +1,3 @@
+xŽQ
+!@ûösBQ"‚ŽÐ ÆÙ± rÍîßÒú{<xð¤·öàîƪ
+™HlJSer!ZPTe*Žj°UÝÑEo^¼ê2 (†ˆ¬XSÅ€ED‘ƒO<Y¦šj$2üs_á&} ¸Î,}Ó[~p¹7~<ÒÛ:Ÿ £°·ÉZ³Ùípè?­1_ûåC0 \ No newline at end of file
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd b/tests/resources/submodules/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
new file mode 100644
index 0000000..d0d7e73
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 b/tests/resources/submodules/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
new file mode 100644
index 0000000..18a7f61
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12 b/tests/resources/submodules/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
new file mode 100644
index 0000000..d952546
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1 b/tests/resources/submodules/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
new file mode 100644
index 0000000..f460f25
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
@@ -0,0 +1,2 @@
+xÌA
+Â0…a×9ÅìIÒ ™€ˆp'î§1Ѷ‘v\x{cáýðVŸpƒvWûgŠ¾ÇŽ0xº[ ]"g†#{rDÆ Cot ­äûN œU $­ò?9-p+1Í^¤ÀQx®¯Ï9O\ÆC¬Ó Œm'D {mµVêú(+´ñælè¶,Þ \ No newline at end of file
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593 b/tests/resources/submodules/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
new file mode 100644
index 0000000..f613670
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 b/tests/resources/submodules/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
new file mode 100644
index 0000000..0817229
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
@@ -0,0 +1,3 @@
+xKj1D³Ö)zçUBëÛ-0ÁuV9¦Õò<#£È÷ÏȲ+ŠW<Jú¶Ý&8Ê/s¨‚e‹µµÈ•KJ­«½S
+ØRvÌÁ{©æQ†îr«äY¹QN$H\Eµ²Íè=6áX5¦òÇK Fr)·(‰dC‡Î†”­–œ—jÊs®}À—ô9ác-Òw8Ëo¸\·r»¿IßÞÁ:
+l}F‚W$Ds´Ç£©ÿÙšOW…e”]V8-ÃÌÈ"U \ No newline at end of file
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd b/tests/resources/submodules/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
new file mode 100644
index 0000000..75f541f
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
@@ -0,0 +1,3 @@
+xŽQ
+Â0DýÎ)öʦ»I<‚'ØlR+˜Fj¼¿EoàÏ0<xÃh«õÞa Üõµ]È™­åXUlÞPF)Åz‘4yó”µ,\r 'SÂÄ-mI4
+‘Xhô”&òÌFÞ}n+\µõ—Y´-p|é·œoUz;-‘aÑlt{ØË?®I«,:ÃoÚR̳cHK \ No newline at end of file
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f b/tests/resources/submodules/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
new file mode 100644
index 0000000..a67d6e6
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/submodules/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644
index 0000000..7112238
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0 b/tests/resources/submodules/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
new file mode 100644
index 0000000..b135ecc
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3 b/tests/resources/submodules/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
new file mode 100644
index 0000000..82e2790
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 b/tests/resources/submodules/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
new file mode 100644
index 0000000..697c94c
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92 b/tests/resources/submodules/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
new file mode 100644
index 0000000..112998d
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765 b/tests/resources/submodules/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
new file mode 100644
index 0000000..12bf5f3
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
new file mode 100644
index 0000000..5068f28
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
new file mode 100644
index 0000000..a6a1f30
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
new file mode 100644
index 0000000..94c3c71
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
new file mode 100644
index 0000000..74c7fe4
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
new file mode 100644
index 0000000..555cfa9
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
new file mode 100644
index 0000000..4d539ed
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
Binary files differ
diff --git a/tests/resources/submodules/testrepo/.gitted/packed-refs b/tests/resources/submodules/testrepo/.gitted/packed-refs
new file mode 100644
index 0000000..9c0433e
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/packed-refs
@@ -0,0 +1,12 @@
+# pack-refs with: peeled
+b25fa35b38051e4ae45d4222e795f9df2e43f1d1 refs/tags/test
+^e90810b8df3e80c413d903f631643c716887138d
+1385f264afb75a56a5bec74243be9b367ba4ca08 refs/tags/point_to_blob
+7b4384978d2493e851f9cca7858815fac9b10980 refs/tags/e90810b
+^e90810b8df3e80c413d903f631643c716887138d
+e90810b8df3e80c413d903f631643c716887138d refs/remotes/origin/test
+763d71aadf09a7951596c9746c024e7eece7c7af refs/remotes/origin/subtrees
+4a202b346bb0fb0db7eff3cffeb3c70babbd2045 refs/remotes/origin/packed-test
+41bc8c69075bbdb46c5c6f0566cc8cc5b46e8bd9 refs/remotes/origin/packed
+a65fedf39aefe402d3bb6e24df4d4f5fe4547750 refs/remotes/origin/master
+a4a7dce85cf63874e984719f4fdd239f5145052f refs/remotes/origin/br2
diff --git a/tests/resources/submodules/testrepo/.gitted/refs/heads/master b/tests/resources/submodules/testrepo/.gitted/refs/heads/master
new file mode 100644
index 0000000..3d8f0a4
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/refs/heads/master
@@ -0,0 +1 @@
+a65fedf39aefe402d3bb6e24df4d4f5fe4547750
diff --git a/tests/resources/submodules/testrepo/.gitted/refs/heads/submodules-worktree-child b/tests/resources/submodules/testrepo/.gitted/refs/heads/submodules-worktree-child
new file mode 100644
index 0000000..3d8f0a4
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/refs/heads/submodules-worktree-child
@@ -0,0 +1 @@
+a65fedf39aefe402d3bb6e24df4d4f5fe4547750
diff --git a/tests/resources/submodules/testrepo/.gitted/refs/remotes/origin/HEAD b/tests/resources/submodules/testrepo/.gitted/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..6efe28f
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/HEAD b/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/HEAD
new file mode 100644
index 0000000..ef82bd4
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/submodules-worktree-child
diff --git a/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/ORIG_HEAD b/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/ORIG_HEAD
new file mode 100644
index 0000000..3d8f0a4
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/ORIG_HEAD
@@ -0,0 +1 @@
+a65fedf39aefe402d3bb6e24df4d4f5fe4547750
diff --git a/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/commondir b/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/commondir
new file mode 100644
index 0000000..aab0408
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/commondir
@@ -0,0 +1 @@
+../..
diff --git a/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/gitdir b/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/gitdir
new file mode 100644
index 0000000..b0ef96e
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/gitdir
@@ -0,0 +1 @@
+../../../../../submodules-worktree-child/.git
diff --git a/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/index b/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/index
new file mode 100644
index 0000000..52a42f9
--- /dev/null
+++ b/tests/resources/submodules/testrepo/.gitted/worktrees/submodules-worktree-child/index
Binary files differ
diff --git a/tests/resources/submodules/testrepo/README b/tests/resources/submodules/testrepo/README
new file mode 100644
index 0000000..a823312
--- /dev/null
+++ b/tests/resources/submodules/testrepo/README
@@ -0,0 +1 @@
+hey there
diff --git a/tests/resources/submodules/testrepo/branch_file.txt b/tests/resources/submodules/testrepo/branch_file.txt
new file mode 100644
index 0000000..3697d64
--- /dev/null
+++ b/tests/resources/submodules/testrepo/branch_file.txt
@@ -0,0 +1,2 @@
+hi
+bye!
diff --git a/tests/resources/submodules/testrepo/new.txt b/tests/resources/submodules/testrepo/new.txt
new file mode 100644
index 0000000..a71586c
--- /dev/null
+++ b/tests/resources/submodules/testrepo/new.txt
@@ -0,0 +1 @@
+my new file
diff --git a/tests/resources/submodules/unmodified b/tests/resources/submodules/unmodified
new file mode 100644
index 0000000..092bfb9
--- /dev/null
+++ b/tests/resources/submodules/unmodified
@@ -0,0 +1 @@
+yo
diff --git a/tests/resources/submodules/untracked b/tests/resources/submodules/untracked
new file mode 100644
index 0000000..092bfb9
--- /dev/null
+++ b/tests/resources/submodules/untracked
@@ -0,0 +1 @@
+yo
diff --git a/tests/resources/super/.gitted/COMMIT_EDITMSG b/tests/resources/super/.gitted/COMMIT_EDITMSG
new file mode 100644
index 0000000..e2d6b89
--- /dev/null
+++ b/tests/resources/super/.gitted/COMMIT_EDITMSG
@@ -0,0 +1 @@
+submodule
diff --git a/tests/resources/super/.gitted/HEAD b/tests/resources/super/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/super/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/super/.gitted/config b/tests/resources/super/.gitted/config
new file mode 100644
index 0000000..06a8b77
--- /dev/null
+++ b/tests/resources/super/.gitted/config
@@ -0,0 +1,10 @@
+[core]
+ repositoryformatversion = 0
+ filemode = false
+ bare = false
+ logallrefupdates = true
+ symlinks = false
+ ignorecase = true
+ hideDotFiles = dotGitOnly
+[submodule "sub"]
+ url = ../sub.git
diff --git a/tests/resources/super/.gitted/index b/tests/resources/super/.gitted/index
new file mode 100644
index 0000000..cc2ffff
--- /dev/null
+++ b/tests/resources/super/.gitted/index
Binary files differ
diff --git a/tests/resources/super/.gitted/objects/51/589c218bf77a8da9e9d8dbc097d76a742726c4 b/tests/resources/super/.gitted/objects/51/589c218bf77a8da9e9d8dbc097d76a742726c4
new file mode 100644
index 0000000..727d3a6
--- /dev/null
+++ b/tests/resources/super/.gitted/objects/51/589c218bf77a8da9e9d8dbc097d76a742726c4
Binary files differ
diff --git a/tests/resources/super/.gitted/objects/79/d0d58ca6aa1688a073d280169908454cad5b91 b/tests/resources/super/.gitted/objects/79/d0d58ca6aa1688a073d280169908454cad5b91
new file mode 100644
index 0000000..7fd889d
--- /dev/null
+++ b/tests/resources/super/.gitted/objects/79/d0d58ca6aa1688a073d280169908454cad5b91
Binary files differ
diff --git a/tests/resources/super/.gitted/objects/d7/57768b570a83e80d02edcc1032db14573e5034 b/tests/resources/super/.gitted/objects/d7/57768b570a83e80d02edcc1032db14573e5034
new file mode 100644
index 0000000..f26c68c
--- /dev/null
+++ b/tests/resources/super/.gitted/objects/d7/57768b570a83e80d02edcc1032db14573e5034
Binary files differ
diff --git a/tests/resources/super/.gitted/refs/heads/master b/tests/resources/super/.gitted/refs/heads/master
new file mode 100644
index 0000000..663a9dc
--- /dev/null
+++ b/tests/resources/super/.gitted/refs/heads/master
@@ -0,0 +1 @@
+79d0d58ca6aa1688a073d280169908454cad5b91
diff --git a/tests/resources/super/gitmodules b/tests/resources/super/gitmodules
new file mode 100644
index 0000000..a3d8f7f
--- /dev/null
+++ b/tests/resources/super/gitmodules
@@ -0,0 +1,3 @@
+[submodule "sub"]
+ path = sub
+ url = ../sub.git
diff --git a/tests/resources/template/branches/.gitignore b/tests/resources/template/branches/.gitignore
new file mode 100644
index 0000000..16868ce
--- /dev/null
+++ b/tests/resources/template/branches/.gitignore
@@ -0,0 +1,2 @@
+# This file should not be copied, nor should the
+# containing directory, since it is effectively "empty"
diff --git a/tests/resources/template/description b/tests/resources/template/description
new file mode 100644
index 0000000..ff04c4c
--- /dev/null
+++ b/tests/resources/template/description
@@ -0,0 +1 @@
+Edit this file 'description' to name the repository.
diff --git a/tests/resources/template/hooks/update.sample b/tests/resources/template/hooks/update.sample
new file mode 100755
index 0000000..3b5f412
--- /dev/null
+++ b/tests/resources/template/hooks/update.sample
@@ -0,0 +1,9 @@
+#!/bin/sh
+#
+# A sample hook to make sure that the `git_repository_init_ext()` function
+# can correctly copy a hook over and set it up with the correct permissions.
+#
+# To enable a hook, you copy the file and remove the ".sample" suffix, but
+# in this case, we're just making sure it gets copied correctly.
+
+echo "$GIT_DIR"
diff --git a/tests/resources/template/info/exclude b/tests/resources/template/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/template/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/testrepo-worktree/.gitted b/tests/resources/testrepo-worktree/.gitted
new file mode 100644
index 0000000..fe4556a
--- /dev/null
+++ b/tests/resources/testrepo-worktree/.gitted
@@ -0,0 +1 @@
+gitdir: ../testrepo/.git/worktrees/testrepo-worktree
diff --git a/tests/resources/testrepo-worktree/README b/tests/resources/testrepo-worktree/README
new file mode 100644
index 0000000..a823312
--- /dev/null
+++ b/tests/resources/testrepo-worktree/README
@@ -0,0 +1 @@
+hey there
diff --git a/tests/resources/testrepo-worktree/branch_file.txt b/tests/resources/testrepo-worktree/branch_file.txt
new file mode 100644
index 0000000..3697d64
--- /dev/null
+++ b/tests/resources/testrepo-worktree/branch_file.txt
@@ -0,0 +1,2 @@
+hi
+bye!
diff --git a/tests/resources/testrepo-worktree/link_to_new.txt b/tests/resources/testrepo-worktree/link_to_new.txt
new file mode 120000
index 0000000..c0528fd
--- /dev/null
+++ b/tests/resources/testrepo-worktree/link_to_new.txt
@@ -0,0 +1 @@
+new.txt \ No newline at end of file
diff --git a/tests/resources/testrepo-worktree/new.txt b/tests/resources/testrepo-worktree/new.txt
new file mode 100644
index 0000000..a71586c
--- /dev/null
+++ b/tests/resources/testrepo-worktree/new.txt
@@ -0,0 +1 @@
+my new file
diff --git a/tests/resources/testrepo.git/FETCH_HEAD b/tests/resources/testrepo.git/FETCH_HEAD
new file mode 100644
index 0000000..4844626
--- /dev/null
+++ b/tests/resources/testrepo.git/FETCH_HEAD
@@ -0,0 +1,2 @@
+a65fedf39aefe402d3bb6e24df4d4f5fe4547750 branch 'master' of git://example.com/git/testrepo.git
+258f0e2a959a364e40ed6603d5d44fbb24765b10 not-for-merge branch 'haacked' of git://example.com/git/testrepo.git
diff --git a/tests/resources/testrepo.git/HEAD b/tests/resources/testrepo.git/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/testrepo.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/testrepo.git/HEAD_TRACKER b/tests/resources/testrepo.git/HEAD_TRACKER
new file mode 100644
index 0000000..40d876b
--- /dev/null
+++ b/tests/resources/testrepo.git/HEAD_TRACKER
@@ -0,0 +1 @@
+ref: HEAD
diff --git a/tests/resources/testrepo.git/config b/tests/resources/testrepo.git/config
new file mode 100644
index 0000000..dfab4ee
--- /dev/null
+++ b/tests/resources/testrepo.git/config
@@ -0,0 +1,40 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = true
+ logallrefupdates = true
+[remote "test"]
+ url = git://github.com/libgit2/libgit2
+ fetch = +refs/heads/*:refs/remotes/test/*
+[remote "joshaber"]
+ url = git://github.com/libgit2/libgit2
+[remote "empty-remote-url"]
+ url =
+ pushurl =
+[remote "empty-remote-pushurl"]
+ pushurl =
+[remote "no-remote-url"]
+ fetch =
+[remote "test_with_pushurl"]
+ url = git://github.com/libgit2/fetchlibgit2
+ pushurl = git://github.com/libgit2/pushlibgit2
+ fetch = +refs/heads/*:refs/remotes/test_with_pushurl/*
+
+[branch "master"]
+ remote = test
+ merge = refs/heads/master
+[branch "track-local"]
+ remote = .
+ merge = refs/heads/master
+[branch "cannot-fetch"]
+ remote = joshaber
+ merge = refs/heads/cannot-fetch
+[branch "remoteless"]
+ remote =
+ merge = refs/heads/master
+[branch "mergeless"]
+ remote = test
+ merge =
+[branch "mergeandremoteless"]
+ remote =
+ merge =
diff --git a/tests/resources/testrepo.git/index b/tests/resources/testrepo.git/index
new file mode 100644
index 0000000..a27fb9c
--- /dev/null
+++ b/tests/resources/testrepo.git/index
Binary files differ
diff --git a/tests/resources/testrepo.git/logs/HEAD b/tests/resources/testrepo.git/logs/HEAD
new file mode 100644
index 0000000..9413b72
--- /dev/null
+++ b/tests/resources/testrepo.git/logs/HEAD
@@ -0,0 +1,7 @@
+0000000000000000000000000000000000000000 be3563ae3f795b2b4353bcce3a527ad0a4f7f644 Ben Straub <bstraub@github.com> 1335806563 -0700 clone: from /Users/ben/src/libgit2/tests/resources/testrepo.git
+be3563ae3f795b2b4353bcce3a527ad0a4f7f644 a65fedf39aefe402d3bb6e24df4d4f5fe4547750 Ben Straub <bstraub@github.com> 1335806603 -0900 commit:
+a65fedf39aefe402d3bb6e24df4d4f5fe4547750 5b5b025afb0b4c913b4c338a42934a3863bf3644 Ben Straub <bstraub@github.com> 1335806604 -0900 checkout: moving from master to 5b5b025
+5b5b025afb0b4c913b4c338a42934a3863bf3644 a65fedf39aefe402d3bb6e24df4d4f5fe4547750 Ben Straub <bstraub@github.com> 1335806605 -0900 checkout: moving from 5b5b025 to master
+a65fedf39aefe402d3bb6e24df4d4f5fe4547750 c47800c7266a2be04c571c04d5a6614691ea99bd Ben Straub <bstraub@github.com> 1335806608 -0900 checkout: moving from master to br2
+c47800c7266a2be04c571c04d5a6614691ea99bd a4a7dce85cf63874e984719f4fdd239f5145052f Ben Straub <bstraub@github.com> 1335806617 -0900 commit: checking in
+a4a7dce85cf63874e984719f4fdd239f5145052f a65fedf39aefe402d3bb6e24df4d4f5fe4547750 Ben Straub <bstraub@github.com> 1335806621 -0900 checkout: moving from br2 to master
diff --git a/tests/resources/testrepo.git/logs/refs/heads/br2 b/tests/resources/testrepo.git/logs/refs/heads/br2
new file mode 100644
index 0000000..4e27f6b
--- /dev/null
+++ b/tests/resources/testrepo.git/logs/refs/heads/br2
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 c47800c7266a2be04c571c04d5a6614691ea99bd Ben Straub <bstraub@github.com> 1335806608 -0700 branch: Created from refs/remotes/origin/br2
+a4a7dce85cf63874e984719f4fdd239f5145052f a4a7dce85cf63874e984719f4fdd239f5145052f Ben Straub <bstraub@github.com> 1335806617 -0700 commit: checking in
diff --git a/tests/resources/testrepo.git/logs/refs/heads/master b/tests/resources/testrepo.git/logs/refs/heads/master
new file mode 100644
index 0000000..e1c729a
--- /dev/null
+++ b/tests/resources/testrepo.git/logs/refs/heads/master
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 be3563ae3f795b2b4353bcce3a527ad0a4f7f644 Ben Straub <bstraub@github.com> 1335806563 -0800 clone: from /Users/ben/src/libgit2/tests/resources/testrepo.git
+be3563ae3f795b2b4353bcce3a527ad0a4f7f644 a65fedf39aefe402d3bb6e24df4d4f5fe4547750 Ben Straub <bstraub@github.com> 1335806603 -0800 commit: checking in
diff --git a/tests/resources/testrepo.git/logs/refs/heads/not-good b/tests/resources/testrepo.git/logs/refs/heads/not-good
new file mode 100644
index 0000000..bfbeacb
--- /dev/null
+++ b/tests/resources/testrepo.git/logs/refs/heads/not-good
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 a65fedf39aefe402d3bb6e24df4d4f5fe4547750 Ben Straub <bstraub@github.com> 1336761944 -0700 branch: Created from master
diff --git a/tests/resources/testrepo.git/logs/refs/heads/with-empty-log b/tests/resources/testrepo.git/logs/refs/heads/with-empty-log
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/testrepo.git/logs/refs/heads/with-empty-log
diff --git a/tests/resources/testrepo.git/logs/refs/remotes/origin/HEAD b/tests/resources/testrepo.git/logs/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..f1aac6d
--- /dev/null
+++ b/tests/resources/testrepo.git/logs/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 be3563ae3f795b2b4353bcce3a527ad0a4f7f644 Ben Straub <bstraub@github.com> 1335806563 -0700 clone: from /Users/ben/src/libgit2/tests/resources/testrepo.git
diff --git a/tests/resources/testrepo.git/logs/refs/remotes/test/master b/tests/resources/testrepo.git/logs/refs/remotes/test/master
new file mode 100644
index 0000000..8d49ba3
--- /dev/null
+++ b/tests/resources/testrepo.git/logs/refs/remotes/test/master
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000 a65fedf39aefe402d3bb6e24df4d4f5fe4547750 Ben Straub <bstraub@github.com> 1335806565 -0800 update by push
+a65fedf39aefe402d3bb6e24df4d4f5fe4547750 be3563ae3f795b2b4353bcce3a527ad0a4f7f644 Ben Straub <bstraub@github.com> 1335806688 -0800 update by push
diff --git a/tests/resources/testrepo.git/objects/08/b041783f40edfe12bb406c9c9a8a040177c125 b/tests/resources/testrepo.git/objects/08/b041783f40edfe12bb406c9c9a8a040177c125
new file mode 100644
index 0000000..d1c032f
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/08/b041783f40edfe12bb406c9c9a8a040177c125
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 b/tests/resources/testrepo.git/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
new file mode 100644
index 0000000..cedb2a2
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 b/tests/resources/testrepo.git/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
new file mode 100644
index 0000000..93a16f1
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/18/10dff58d8a660512d4832e740f692884338ccd b/tests/resources/testrepo.git/objects/18/10dff58d8a660512d4832e740f692884338ccd
new file mode 100644
index 0000000..ba0bfb3
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/18/10dff58d8a660512d4832e740f692884338ccd
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fd b/tests/resources/testrepo.git/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fd
new file mode 100644
index 0000000..3ec5412
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/1a/443023183e3f2bfbef8ac923cd81c1018a18fd
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/1b/8cbad43e867676df601306689fe7c3def5e689 b/tests/resources/testrepo.git/objects/1b/8cbad43e867676df601306689fe7c3def5e689
new file mode 100644
index 0000000..6048d4b
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/1b/8cbad43e867676df601306689fe7c3def5e689
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b b/tests/resources/testrepo.git/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
new file mode 100644
index 0000000..225c457
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10 b/tests/resources/testrepo.git/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10
new file mode 100644
index 0000000..cb1ed57
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/25/8f0e2a959a364e40ed6603d5d44fbb24765b10
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d b/tests/resources/testrepo.git/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
new file mode 100644
index 0000000..df40d99
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7 b/tests/resources/testrepo.git/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7
new file mode 100644
index 0000000..0a1500a
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/2d/59075e0681f540482d4f6223a68e0fef790bc7
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54 b/tests/resources/testrepo.git/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
new file mode 100644
index 0000000..321eaa8
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc b/tests/resources/testrepo.git/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
new file mode 100644
index 0000000..9bb5b62
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/43/da5ec3274dd061df152ff5e69853d562b01842 b/tests/resources/testrepo.git/objects/43/da5ec3274dd061df152ff5e69853d562b01842
new file mode 100644
index 0000000..298feec
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/43/da5ec3274dd061df152ff5e69853d562b01842
@@ -0,0 +1,2 @@
+x-Ž]jC!Fûì*f)þ]¡”@è
+Ú è8Æ õZŒ¡Û¯iûvøà>ê­í´±Os0ƒB%s)fŒÎÉMélÑhöV4¢5‰²øŠƒ Å&4Ñ•@:¡DÅ)oIr`’½$íLДˆ÷Yû€wêsÂ¥Fê¼ÜèÎ×÷Ïgêí”ö•Á`á$½”b­ëáäoù;Ž µ·ÛÃæU|ÐùºÏzOÿ¾u}ÐÚ/ß._¤ªð×~°øÕäJ \ No newline at end of file
diff --git a/tests/resources/testrepo.git/objects/43/e968a905a821532069bb413801d35b200631cf b/tests/resources/testrepo.git/objects/43/e968a905a821532069bb413801d35b200631cf
new file mode 100644
index 0000000..ec04abf
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/43/e968a905a821532069bb413801d35b200631cf
@@ -0,0 +1,4 @@
+xŽK
+1]ç}%ŸN'7ÞÀ äÓã 8‰¼¾ñ
+îê¼\×ué 5zc†¤ 6¤Ç8ùb,â”ÊDÎ2º0†'«Q¼bã­ƒÇ@Ò©¬Rö“QÊ[ŽÁ94‰)ú£qïsmp+ŸØ
+Ü纾ëgöG×ÇÒç=r]/ ´3š((‚£tRŠaÇËÎÿö¢>‹ø±EÎ \ No newline at end of file
diff --git a/tests/resources/testrepo.git/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests/resources/testrepo.git/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
new file mode 100644
index 0000000..7ca4cee
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 b/tests/resources/testrepo.git/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
new file mode 100644
index 0000000..8953b6c
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
@@ -0,0 +1,2 @@
+xŽQ
+Â0DýÎ)öÊ6›Í¦ "xO°‰‰-ØFb¼¿EoàÏ0 ¼Ç¤º,ske×[ÎPn8R,EpD?±gŸ}Ê^3² âÙ<µåµGŽhYKÄèÒ8ЖDAÉ)¿ÉÈ;gôݧÚàšjïp™4ÕŽ¯ô-çû¢óãêr‚ÁŠ;°s°GA4Ûº=ìùÖ(ôin7øIÌKÍFE \ No newline at end of file
diff --git a/tests/resources/testrepo.git/objects/4a/23e2e65ad4e31c4c9db7dc746650bfad082679 b/tests/resources/testrepo.git/objects/4a/23e2e65ad4e31c4c9db7dc746650bfad082679
new file mode 100644
index 0000000..18e3964
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/4a/23e2e65ad4e31c4c9db7dc746650bfad082679
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/4b/22b35d44b5a4f589edf3dc89196399771796ea b/tests/resources/testrepo.git/objects/4b/22b35d44b5a4f589edf3dc89196399771796ea
new file mode 100644
index 0000000..b4e5aa1
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/4b/22b35d44b5a4f589edf3dc89196399771796ea
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91 b/tests/resources/testrepo.git/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91
new file mode 100644
index 0000000..351cff8
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/52/1d87c1ec3aef9824daf6d96cc0ae3710766d91
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 b/tests/resources/testrepo.git/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
new file mode 100644
index 0000000..c1f22c5
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
@@ -0,0 +1,2 @@
+xŽÛ 1EýNi@™Ék2 "X‚$ÙYW0YcÿíÀ¿Ã…s¸¥ÕzïÚÚõMDÏ€0æœ8!¶†ÉÌÞs‰ XŠªgÚdí::@X0»P¢wÙ"F/‰‰œÍRàˆUz÷¥múZZïú²¤ÒV}|•/œo5݇ÒêI£!¬1z Æ:vùÇUim}ê/¢>
+öF- \ No newline at end of file
diff --git a/tests/resources/testrepo.git/objects/5d/0f8f7891e872d284beef38254882dc879b2602 b/tests/resources/testrepo.git/objects/5d/0f8f7891e872d284beef38254882dc879b2602
new file mode 100644
index 0000000..7a22451
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/5d/0f8f7891e872d284beef38254882dc879b2602
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/5f/34cd6e3285089647165983482cf90873d50940 b/tests/resources/testrepo.git/objects/5f/34cd6e3285089647165983482cf90873d50940
new file mode 100644
index 0000000..b1df3bd
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/5f/34cd6e3285089647165983482cf90873d50940
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a b/tests/resources/testrepo.git/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
new file mode 100644
index 0000000..2ef4faa
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af b/tests/resources/testrepo.git/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
new file mode 100644
index 0000000..716b0c6
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
@@ -0,0 +1 @@
+xŽAj!³ö?0¨£ßÂ09Êo}HÚ6¨}ÿôjUPP©ÕZ&Yÿø˜ AÔ›±€pŒÁFdë¼÷pz[fŽYŒ½PÒqLJ.,Z§`™Å®Ð.ù`’vÙ ³q $Æ5+9çOëtœû>Û/úDE/龡W¯ï*e¿§VŸdf1>ð覭Öê²×äÄ›¹úÊ™F« ­ìTŽÙhœk.i¶^0Ô?P¼R, \ No newline at end of file
diff --git a/tests/resources/testrepo.git/objects/7b/4384978d2493e851f9cca7858815fac9b10980 b/tests/resources/testrepo.git/objects/7b/4384978d2493e851f9cca7858815fac9b10980
new file mode 100644
index 0000000..23c462f
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/7b/4384978d2493e851f9cca7858815fac9b10980
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/81/4889a078c031f61ed08ab5fa863aea9314344d b/tests/resources/testrepo.git/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
new file mode 100644
index 0000000..2f9b6b6
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/84/96071c1b46c854b31185ea97743be6a8774479 b/tests/resources/testrepo.git/objects/84/96071c1b46c854b31185ea97743be6a8774479
new file mode 100644
index 0000000..5df58dd
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/84/96071c1b46c854b31185ea97743be6a8774479
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe b/tests/resources/testrepo.git/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe
new file mode 100644
index 0000000..71019a6
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/84/9a5e34a26815e821f865b8479f5815a47af0fe
@@ -0,0 +1,2 @@
+xŒM F]sŠ¹€†Ÿ41ÆxÝ(­I‹ÁéÂÛKݽ/_ÞãP@¡ÚÕø¢!8›)es
+” ¥N&FGSÆ„¹hÑ{+ßCç‰÷ÆZzvØF¡7ZàÎ-¬Îñó‡k™x\ã¡[PÆ8ï´ôGØK/¥^© lÊ>.4 \ No newline at end of file
diff --git a/tests/resources/testrepo.git/objects/8e/73b769e97678d684b809b163bebdae2911720f b/tests/resources/testrepo.git/objects/8e/73b769e97678d684b809b163bebdae2911720f
new file mode 100644
index 0000000..d75977a
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/8e/73b769e97678d684b809b163bebdae2911720f
@@ -0,0 +1,2 @@
+x½jÄ0„Sû)¶»*aõ»„ãš”éò+íúl8[A–ÉëÇ×äÒ 33¥®ëÒÁ¡yéM$ÌmÈ*Öç ã$q²äG?YŒAœ5< ßÜtëà¦8rÂÀ£5ÁŽ”³7nD#.d‹)~ŠNÈ0Ë„‰)R,‰|,h½’jQ*töóÑçÚàC~¸ |ÍuÝëïzÒ§ºÝ—>ù­Ôõ
+Æ’³d¼ðŠ„8œô\Õõ¿ùáSÛ]!7ÞÊ —ýÈÏsö ,[¯P2üfw^Ä \ No newline at end of file
diff --git a/tests/resources/testrepo.git/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162 b/tests/resources/testrepo.git/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
new file mode 100644
index 0000000..4cc3f4d
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
@@ -0,0 +1 @@
+x+)JMU044b040031QrutñueX¡l¨ðmmA‹m›Ì£íJ}Gß;U‘T”˜—œŸ–™“ªWRQÂ`6ýš÷KÇ¥¶^/¾-*|òøWØ¥3P¥y©å`%ËEÛÞ±\&gŽÐ|Ÿ0§ÿ†{Ó1X \ No newline at end of file
diff --git a/tests/resources/testrepo.git/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4 b/tests/resources/testrepo.git/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
new file mode 100644
index 0000000..bf7b2bb
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d2 b/tests/resources/testrepo.git/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d2
new file mode 100644
index 0000000..7f1cfb2
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/9f/13f7d0a9402c681f91dc590cf7b5470e6a77d2
@@ -0,0 +1,2 @@
+xŽM
+Â0F]ç³d2¤ñ®<A~&´`­ÄôþVàæãmïËë²ÌÈÒ¡7Uà$äJöL9yM!¢GuœªH¤&UÈæ›>;ÔÂÁ…¬£³X†ÂEÈŽ5R±£ ÛAÑE &n}ZÜæ<E}À=O[ÒÖáüÞéúÓ¼^À,ã^†#¢É¿ƒ]ÿPÍ`>™A¹ \ No newline at end of file
diff --git a/tests/resources/testrepo.git/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a b/tests/resources/testrepo.git/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
new file mode 100644
index 0000000..a796124
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
@@ -0,0 +1,3 @@
+xŽ[
+Â0EýÎ*fÊäÕ¤ "¸W0“‡-ØFâtÿÝ—çpS[–YÀ˜x^
+Díb CLhutɉ}¥8X*4Zí¬sY½¨—UÀ‘AÃÖ ÌX3‡R«Mµ¶) s6è¼¢M¦ÖážšÜ&Jm…ó;}Çõ±Ðü<¥¶\@›à‚ÑÞpÄ€¨vº?”ò«jÛºLð«¨Ø?Hå \ No newline at end of file
diff --git a/tests/resources/testrepo.git/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f b/tests/resources/testrepo.git/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
new file mode 100644
index 0000000..f858869
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
@@ -0,0 +1,2 @@
+x;j1DëmdÓú·À˜ÇŽ|M«µ3`ŒV{ >€³âQ¯ ¸·vL0I?Í!š4–Z=Ê! ×¦8²F¢Ã’!rÖsQßyÈ9]$DŽ&„l6AÇ>jFWüÒµ IKNiûë§Z¢%¡SˆŒ‘
+‹Ò ­ÅʉøU~̽øä>'¼ï™û ¯wþ ×[ËÇ× ÷öÚDGÚ¡±ðŒQ-ºMù«>dܶ‘OÞáÒò}í\à8g_ШÂoYr \ No newline at end of file
diff --git a/tests/resources/testrepo.git/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750 b/tests/resources/testrepo.git/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
new file mode 100644
index 0000000..29c8e82
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
@@ -0,0 +1,3 @@
+xŽQ
+!@ûösBQ"‚ŽÐ ÆÙ± rÍîßÒú{<xð¤·öàîƪ
+™HlJSer!ZPTe*Žj°UÝÑEo^¼ê2 (†ˆ¬XSÅ€ED‘ƒO<Y¦šj$2üs_á&} ¸Î,}Ó[~p¹7~<ÒÛ:Ÿ £°·ÉZ³Ùípè?­1_ûåC0 \ No newline at end of file
diff --git a/tests/resources/testrepo.git/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd b/tests/resources/testrepo.git/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
new file mode 100644
index 0000000..d0d7e73
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 b/tests/resources/testrepo.git/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
new file mode 100644
index 0000000..18a7f61
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/ae/90f12eea699729ed24555e40b9fd669da12a12 b/tests/resources/testrepo.git/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
new file mode 100644
index 0000000..d952546
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/b2/04707bbc546a1a770ef6ced37c7089cc3bfe6b b/tests/resources/testrepo.git/objects/b2/04707bbc546a1a770ef6ced37c7089cc3bfe6b
new file mode 100644
index 0000000..f9ec61c
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/b2/04707bbc546a1a770ef6ced37c7089cc3bfe6b
@@ -0,0 +1,2 @@
+x-Î]ŠÃ0 à}Î)t.þQ J),{‚öŽ-7º^\‡^Ò·AÌ7(”œ×FÓW«"ÀˆA%ŒÉ£y’Ù‘giTI´Ód†?_åÑÀ™#ê•Ø[‰Ó(-§DÎ0wdp²ƒßÚR*\Bi ~ÊŽÏðç[öëý;”|mµa´djRjè×þa“
+¿ñåk„ëRòs×Ò÷t¾­mÙæG"7±á©{ì~ø¥LD› \ No newline at end of file
diff --git a/tests/resources/testrepo.git/objects/b2/35959d89084af8d3544fbdf675e47944f86524 b/tests/resources/testrepo.git/objects/b2/35959d89084af8d3544fbdf675e47944f86524
new file mode 100644
index 0000000..7d563db
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/b2/35959d89084af8d3544fbdf675e47944f86524
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1 b/tests/resources/testrepo.git/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
new file mode 100644
index 0000000..f460f25
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
@@ -0,0 +1,2 @@
+xÌA
+Â0…a×9ÅìIÒ ™€ˆp'î§1Ѷ‘v\x{cáýðVŸpƒvWûgŠ¾ÇŽ0xº[ ]"g†#{rDÆ Cot ­äûN œU $­ò?9-p+1Í^¤ÀQx®¯Ï9O\ÆC¬Ó Œm'D {mµVêú(+´ñælè¶,Þ \ No newline at end of file
diff --git a/tests/resources/testrepo.git/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593 b/tests/resources/testrepo.git/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
new file mode 100644
index 0000000..f613670
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/b9/1e763008b10db366442469339f90a2b8400d0a b/tests/resources/testrepo.git/objects/b9/1e763008b10db366442469339f90a2b8400d0a
new file mode 100644
index 0000000..7bab59b
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/b9/1e763008b10db366442469339f90a2b8400d0a
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/bd/758010071961f28336333bc41e9c64c9a64866 b/tests/resources/testrepo.git/objects/bd/758010071961f28336333bc41e9c64c9a64866
new file mode 100644
index 0000000..c5e3b87
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/bd/758010071961f28336333bc41e9c64c9a64866
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 b/tests/resources/testrepo.git/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
new file mode 100644
index 0000000..0817229
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
@@ -0,0 +1,3 @@
+xKj1D³Ö)zçUBëÛ-0ÁuV9¦Õò<#£È÷ÏȲ+ŠW<Jú¶Ý&8Ê/s¨‚e‹µµÈ•KJ­«½S
+ØRvÌÁ{©æQ†îr«äY¹QN$H\Eµ²Íè=6áX5¦òÇK Fr)·(‰dC‡Î†”­–œ—jÊs®}À—ô9ác-Òw8Ëo¸\·r»¿IßÞÁ:
+l}F‚W$Ds´Ç£©ÿÙšOW…e”]V8-ÃÌÈ"U \ No newline at end of file
diff --git a/tests/resources/testrepo.git/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd b/tests/resources/testrepo.git/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
new file mode 100644
index 0000000..75f541f
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
@@ -0,0 +1,3 @@
+xŽQ
+Â0DýÎ)öʦ»I<‚'ØlR+˜Fj¼¿EoàÏ0<xÃh«õÞa Üõµ]È™­åXUlÞPF)Åz‘4yó”µ,\r 'SÂÄ-mI4
+‘Xhô”&òÌFÞ}n+\µõ—Y´-p|é·œoUz;-‘aÑlt{ØË?®I«,:ÃoÚR̳cHK \ No newline at end of file
diff --git a/tests/resources/testrepo.git/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659 b/tests/resources/testrepo.git/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659
new file mode 100644
index 0000000..f3b46b3
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/d0/7b0f9a8c89f1d9e74dc4fce6421dec5ef8a659
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f b/tests/resources/testrepo.git/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
new file mode 100644
index 0000000..a67d6e6
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759 b/tests/resources/testrepo.git/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759
new file mode 100644
index 0000000..2d47e6f
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/d7/1aab4f9b04b45ce09bcaa636a9be6231474759
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/db/4df74a2fc340a0d0cb0cafc0db471fdfff1048 b/tests/resources/testrepo.git/objects/db/4df74a2fc340a0d0cb0cafc0db471fdfff1048
new file mode 100644
index 0000000..5f3d50e
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/db/4df74a2fc340a0d0cb0cafc0db471fdfff1048
@@ -0,0 +1,2 @@
+x-ÎQJ1P¿sŠ¾€ÒIz2= ² ž@/tz7f"Ù,^߬úW¼¢¤ÕºpžFWËgkÙÊÆÑ­ë¶`â$´8JñŽ0c¸˜¯Øõ˜¹˜5
+ÉIÒˆŠJ>!+…³N–UÈÄÛ(­Ã»´1àµDi<_å7œ.5îŸOÒê X·[ÏÁ#®ˆf¶óáÐoù;ö ¥Õë]ë\¼§Óeå–þ=…À[@tÓÓô&õxH¿h‡šûYJn \ No newline at end of file
diff --git a/tests/resources/testrepo.git/objects/db/793a00a5615eca1aac97e42b3a68b1acfa8bfd b/tests/resources/testrepo.git/objects/db/793a00a5615eca1aac97e42b3a68b1acfa8bfd
new file mode 100644
index 0000000..ae82880
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/db/793a00a5615eca1aac97e42b3a68b1acfa8bfd
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/db/c0be625bed24b5d8f5d9a927484f2065d321af b/tests/resources/testrepo.git/objects/db/c0be625bed24b5d8f5d9a927484f2065d321af
new file mode 100644
index 0000000..b966b0b
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/db/c0be625bed24b5d8f5d9a927484f2065d321af
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/testrepo.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644
index 0000000..7112238
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0 b/tests/resources/testrepo.git/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
new file mode 100644
index 0000000..b135ecc
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/f0/a2a10243ca64f935dbe3dccb89ec8bf16bdace b/tests/resources/testrepo.git/objects/f0/a2a10243ca64f935dbe3dccb89ec8bf16bdace
new file mode 100644
index 0000000..1b299dc
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/f0/a2a10243ca64f935dbe3dccb89ec8bf16bdace
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3 b/tests/resources/testrepo.git/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
new file mode 100644
index 0000000..82e2790
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 b/tests/resources/testrepo.git/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
new file mode 100644
index 0000000..697c94c
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92 b/tests/resources/testrepo.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92
new file mode 100644
index 0000000..112998d
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/fa/49b077972391ad58037050f2a75f74e3671e92
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/fd/093bff70906175335656e6ce6ae05783708765 b/tests/resources/testrepo.git/objects/fd/093bff70906175335656e6ce6ae05783708765
new file mode 100644
index 0000000..12bf5f3
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/fd/093bff70906175335656e6ce6ae05783708765
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09 b/tests/resources/testrepo.git/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09
new file mode 100644
index 0000000..158aef2
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/fd/4959ce7510db09d4d8217fa2d1780413e05a09
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/info/commit-graph b/tests/resources/testrepo.git/objects/info/commit-graph
new file mode 100644
index 0000000..2ef31d8
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/info/commit-graph
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/pack/multi-pack-index b/tests/resources/testrepo.git/objects/pack/multi-pack-index
new file mode 100644
index 0000000..95102ae
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/pack/multi-pack-index
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx b/tests/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
new file mode 100644
index 0000000..5068f28
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack b/tests/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
new file mode 100644
index 0000000..a6a1f30
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx b/tests/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
new file mode 100644
index 0000000..94c3c71
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack b/tests/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
new file mode 100644
index 0000000..74c7fe4
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx b/tests/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
new file mode 100644
index 0000000..555cfa9
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
Binary files differ
diff --git a/tests/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack b/tests/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
new file mode 100644
index 0000000..4d539ed
--- /dev/null
+++ b/tests/resources/testrepo.git/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
Binary files differ
diff --git a/tests/resources/testrepo.git/packed-refs b/tests/resources/testrepo.git/packed-refs
new file mode 100644
index 0000000..fadd7e8
--- /dev/null
+++ b/tests/resources/testrepo.git/packed-refs
@@ -0,0 +1,3 @@
+# pack-refs with: peeled sorted
+41bc8c69075bbdb46c5c6f0566cc8cc5b46e8bd9 refs/heads/packed
+5b5b025afb0b4c913b4c338a42934a3863bf3644 refs/heads/packed-test
diff --git a/tests/resources/testrepo.git/refs/blobs/annotated_tag_to_blob b/tests/resources/testrepo.git/refs/blobs/annotated_tag_to_blob
new file mode 100644
index 0000000..6c146d6
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/blobs/annotated_tag_to_blob
@@ -0,0 +1 @@
+521d87c1ec3aef9824daf6d96cc0ae3710766d91
diff --git a/tests/resources/testrepo.git/refs/heads/br2 b/tests/resources/testrepo.git/refs/heads/br2
new file mode 100644
index 0000000..aab87e5
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/heads/br2
@@ -0,0 +1 @@
+a4a7dce85cf63874e984719f4fdd239f5145052f
diff --git a/tests/resources/testrepo.git/refs/heads/cannot-fetch b/tests/resources/testrepo.git/refs/heads/cannot-fetch
new file mode 100644
index 0000000..aab87e5
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/heads/cannot-fetch
@@ -0,0 +1 @@
+a4a7dce85cf63874e984719f4fdd239f5145052f
diff --git a/tests/resources/testrepo.git/refs/heads/chomped b/tests/resources/testrepo.git/refs/heads/chomped
new file mode 100644
index 0000000..0166a7f
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/heads/chomped
@@ -0,0 +1 @@
+e90810b8df3e80c413d903f631643c716887138d \ No newline at end of file
diff --git a/tests/resources/testrepo.git/refs/heads/haacked b/tests/resources/testrepo.git/refs/heads/haacked
new file mode 100644
index 0000000..17f5912
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/heads/haacked
@@ -0,0 +1 @@
+258f0e2a959a364e40ed6603d5d44fbb24765b10
diff --git a/tests/resources/testrepo.git/refs/heads/master b/tests/resources/testrepo.git/refs/heads/master
new file mode 100644
index 0000000..3d8f0a4
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/heads/master
@@ -0,0 +1 @@
+a65fedf39aefe402d3bb6e24df4d4f5fe4547750
diff --git a/tests/resources/testrepo.git/refs/heads/not-good b/tests/resources/testrepo.git/refs/heads/not-good
new file mode 100644
index 0000000..3d8f0a4
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/heads/not-good
@@ -0,0 +1 @@
+a65fedf39aefe402d3bb6e24df4d4f5fe4547750
diff --git a/tests/resources/testrepo.git/refs/heads/packed-test b/tests/resources/testrepo.git/refs/heads/packed-test
new file mode 100644
index 0000000..f2c14ad
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/heads/packed-test
@@ -0,0 +1 @@
+4a202b346bb0fb0db7eff3cffeb3c70babbd2045
diff --git a/tests/resources/testrepo.git/refs/heads/subtrees b/tests/resources/testrepo.git/refs/heads/subtrees
new file mode 100644
index 0000000..ad27e0b
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/heads/subtrees
@@ -0,0 +1 @@
+763d71aadf09a7951596c9746c024e7eece7c7af
diff --git a/tests/resources/testrepo.git/refs/heads/test b/tests/resources/testrepo.git/refs/heads/test
new file mode 100644
index 0000000..399c4c7
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/heads/test
@@ -0,0 +1 @@
+e90810b8df3e80c413d903f631643c716887138d
diff --git a/tests/resources/testrepo.git/refs/heads/track-local b/tests/resources/testrepo.git/refs/heads/track-local
new file mode 100644
index 0000000..f37febb
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/heads/track-local
@@ -0,0 +1 @@
+9fd738e8f7967c078dceed8190330fc8648ee56a
diff --git a/tests/resources/testrepo.git/refs/heads/trailing b/tests/resources/testrepo.git/refs/heads/trailing
new file mode 100644
index 0000000..2a4a6e6
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/heads/trailing
@@ -0,0 +1 @@
+e90810b8df3e80c413d903f631643c716887138d
diff --git a/tests/resources/testrepo.git/refs/heads/with-empty-log b/tests/resources/testrepo.git/refs/heads/with-empty-log
new file mode 100644
index 0000000..dae4cb2
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/heads/with-empty-log
@@ -0,0 +1 @@
+8496071c1b46c854b31185ea97743be6a8774479
diff --git a/tests/resources/testrepo.git/refs/notes/fanout b/tests/resources/testrepo.git/refs/notes/fanout
new file mode 100644
index 0000000..1f17036
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/notes/fanout
@@ -0,0 +1 @@
+d07b0f9a8c89f1d9e74dc4fce6421dec5ef8a659
diff --git a/tests/resources/testrepo.git/refs/remotes/test/master b/tests/resources/testrepo.git/refs/remotes/test/master
new file mode 100644
index 0000000..9536ad8
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/remotes/test/master
@@ -0,0 +1 @@
+be3563ae3f795b2b4353bcce3a527ad0a4f7f644
diff --git a/tests/resources/testrepo.git/refs/tags/annotated_tag_to_blob b/tests/resources/testrepo.git/refs/tags/annotated_tag_to_blob
new file mode 100644
index 0000000..6c146d6
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/tags/annotated_tag_to_blob
@@ -0,0 +1 @@
+521d87c1ec3aef9824daf6d96cc0ae3710766d91
diff --git a/tests/resources/testrepo.git/refs/tags/e90810b b/tests/resources/testrepo.git/refs/tags/e90810b
new file mode 100644
index 0000000..584495d
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/tags/e90810b
@@ -0,0 +1 @@
+7b4384978d2493e851f9cca7858815fac9b10980
diff --git a/tests/resources/testrepo.git/refs/tags/hard_tag b/tests/resources/testrepo.git/refs/tags/hard_tag
new file mode 100644
index 0000000..59ce656
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/tags/hard_tag
@@ -0,0 +1 @@
+849a5e34a26815e821f865b8479f5815a47af0fe
diff --git a/tests/resources/testrepo.git/refs/tags/point_to_blob b/tests/resources/testrepo.git/refs/tags/point_to_blob
new file mode 100644
index 0000000..f874a3f
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/tags/point_to_blob
@@ -0,0 +1 @@
+1385f264afb75a56a5bec74243be9b367ba4ca08
diff --git a/tests/resources/testrepo.git/refs/tags/taggerless b/tests/resources/testrepo.git/refs/tags/taggerless
new file mode 100644
index 0000000..f960c7d
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/tags/taggerless
@@ -0,0 +1 @@
+4a23e2e65ad4e31c4c9db7dc746650bfad082679
diff --git a/tests/resources/testrepo.git/refs/tags/test b/tests/resources/testrepo.git/refs/tags/test
new file mode 100644
index 0000000..6ee952a
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/tags/test
@@ -0,0 +1 @@
+b25fa35b38051e4ae45d4222e795f9df2e43f1d1
diff --git a/tests/resources/testrepo.git/refs/tags/wrapped_tag b/tests/resources/testrepo.git/refs/tags/wrapped_tag
new file mode 100644
index 0000000..59ce656
--- /dev/null
+++ b/tests/resources/testrepo.git/refs/tags/wrapped_tag
@@ -0,0 +1 @@
+849a5e34a26815e821f865b8479f5815a47af0fe
diff --git a/tests/resources/testrepo/.gitted/HEAD b/tests/resources/testrepo/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/testrepo/.gitted/HEAD_TRACKER b/tests/resources/testrepo/.gitted/HEAD_TRACKER
new file mode 100644
index 0000000..40d876b
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/HEAD_TRACKER
@@ -0,0 +1 @@
+ref: HEAD
diff --git a/tests/resources/testrepo/.gitted/config b/tests/resources/testrepo/.gitted/config
new file mode 100644
index 0000000..d011401
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/config
@@ -0,0 +1,8 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+[remote "test"]
+ url = git://github.com/libgit2/libgit2
+ fetch = +refs/heads/*:refs/remotes/test/*
diff --git a/tests/resources/testrepo/.gitted/index b/tests/resources/testrepo/.gitted/index
new file mode 100644
index 0000000..963f7ad
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/index
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/logs/refs/heads/testrepo-worktree b/tests/resources/testrepo/.gitted/logs/refs/heads/testrepo-worktree
new file mode 100644
index 0000000..93ab5f0
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/logs/refs/heads/testrepo-worktree
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 099fabac3a9ea935598528c27f866e34089c2eff Patrick Steinhardt <ps@pks.im> 1442484463 +0200 branch: Created from HEAD
diff --git a/tests/resources/testrepo/.gitted/objects/09/9fabac3a9ea935598528c27f866e34089c2eff b/tests/resources/testrepo/.gitted/objects/09/9fabac3a9ea935598528c27f866e34089c2eff
new file mode 100644
index 0000000..c60c78f
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/09/9fabac3a9ea935598528c27f866e34089c2eff
@@ -0,0 +1 @@
+xÎQ P¿9Å^@³ÂB!1F½‚'€î¢ÒJ?¼½Õ#ø7™ÉK¦ŸJhM›VE€,³·.3û¼§Þ¦ˆ‚ÔuVsHè-;õŠUÆÑÙ,œMˆ’…P³Iɉ&ÎÄ”×ìסŠK»O.2µո$8¤ùN·¡Ý—´ë§r„½!½l±CTk»lòUgfˆ0¿ËsêÓG( \ No newline at end of file
diff --git a/tests/resources/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 b/tests/resources/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
new file mode 100644
index 0000000..cedb2a2
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0e b/tests/resources/testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0e
new file mode 100644
index 0000000..b7d944f
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/14/4344043ba4d4a405da03de3844aa829ae8be0e
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925 b/tests/resources/testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925
new file mode 100644
index 0000000..d37b93e
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/16/8e4ebd1c667499548ae12403b19b22a5c5e925
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 b/tests/resources/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
new file mode 100644
index 0000000..93a16f1
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd b/tests/resources/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
new file mode 100644
index 0000000..ba0bfb3
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/1d/d0968be3ff95fcaecb6fa4245662db9fdc4568 b/tests/resources/testrepo/.gitted/objects/1d/d0968be3ff95fcaecb6fa4245662db9fdc4568
new file mode 100644
index 0000000..97c6b2c
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/1d/d0968be3ff95fcaecb6fa4245662db9fdc4568
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b b/tests/resources/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
new file mode 100644
index 0000000..225c457
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/1f/67fc4386b2d171e0d21be1c447e12660561f9b
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d b/tests/resources/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
new file mode 100644
index 0000000..df40d99
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/27/0b8ea76056d5cad83af921837702d3e3c2924d
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863 b/tests/resources/testrepo/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863
new file mode 100644
index 0000000..d10ca63
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/2b/d0a343aeef7a2cf0d158478966a6e587ff3863
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54 b/tests/resources/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
new file mode 100644
index 0000000..321eaa8
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/32/59a6bd5b57fb9c1281bb7ed3167b50f224cb54
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc b/tests/resources/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
new file mode 100644
index 0000000..9bb5b62
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/36/97d64be941a53d4ae8f6a271e4e3fa56b022cc
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests/resources/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
new file mode 100644
index 0000000..7ca4cee
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/45/dd856fdd4d89b884c340ba0e047752d9b085d6 b/tests/resources/testrepo/.gitted/objects/45/dd856fdd4d89b884c340ba0e047752d9b085d6
new file mode 100644
index 0000000..a83ed97
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/45/dd856fdd4d89b884c340ba0e047752d9b085d6
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 b/tests/resources/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
new file mode 100644
index 0000000..8953b6c
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
@@ -0,0 +1,2 @@
+xŽQ
+Â0DýÎ)öÊ6›Í¦ "xO°‰‰-ØFb¼¿EoàÏ0 ¼Ç¤º,ske×[ÎPn8R,EpD?±gŸ}Ê^3² âÙ<µåµGŽhYKÄèÒ8ЖDAÉ)¿ÉÈ;gôݧÚàšjïp™4ÕŽ¯ô-çû¢óãêr‚ÁŠ;°s°GA4Ûº=ìùÖ(ôin7øIÌKÍFE \ No newline at end of file
diff --git a/tests/resources/testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63 b/tests/resources/testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63
new file mode 100644
index 0000000..e915021
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/4e/0883eeeeebc1fb1735161cea82f7cb5fab7e63
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/4e/886e602529caa9ab11d71f86634bd1b6e0de10 b/tests/resources/testrepo/.gitted/objects/4e/886e602529caa9ab11d71f86634bd1b6e0de10
new file mode 100644
index 0000000..53168a0
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/4e/886e602529caa9ab11d71f86634bd1b6e0de10
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/57/43a3ef145d3638a0fa28233ca92897117ad74f b/tests/resources/testrepo/.gitted/objects/57/43a3ef145d3638a0fa28233ca92897117ad74f
new file mode 100644
index 0000000..85abb27
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/57/43a3ef145d3638a0fa28233ca92897117ad74f
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 b/tests/resources/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
new file mode 100644
index 0000000..c1f22c5
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
@@ -0,0 +1,2 @@
+xŽÛ 1EýNi@™Ék2 "X‚$ÙYW0YcÿíÀ¿Ã…s¸¥ÕzïÚÚõMDÏ€0æœ8!¶†ÉÌÞs‰ XŠªgÚdí::@X0»P¢wÙ"F/‰‰œÍRàˆUz÷¥múZZïú²¤ÒV}|•/œo5݇ÒêI£!¬1z Æ:vùÇUim}ê/¢>
+öF- \ No newline at end of file
diff --git a/tests/resources/testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dcc b/tests/resources/testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dcc
new file mode 100644
index 0000000..b669961
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/62/eb56dabb4b9929bc15dd9263c2c733b13d2dcc
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735 b/tests/resources/testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735
new file mode 100644
index 0000000..9ff5eb2
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/66/3adb09143767984f7be83a91effa47e128c735
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/6b/377958d8c6a4906e8573b53672a1a23a4e8ce6 b/tests/resources/testrepo/.gitted/objects/6b/377958d8c6a4906e8573b53672a1a23a4e8ce6
new file mode 100644
index 0000000..ee7c781
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/6b/377958d8c6a4906e8573b53672a1a23a4e8ce6
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/6b/9b767af9992b4abad5e24ffb1ba2d688ca602e b/tests/resources/testrepo/.gitted/objects/6b/9b767af9992b4abad5e24ffb1ba2d688ca602e
new file mode 100644
index 0000000..197685b
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/6b/9b767af9992b4abad5e24ffb1ba2d688ca602e
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/6f/d5c7dd2ab27b48c493023f794be09861e9045f b/tests/resources/testrepo/.gitted/objects/6f/d5c7dd2ab27b48c493023f794be09861e9045f
new file mode 100644
index 0000000..7f0c6fe
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/6f/d5c7dd2ab27b48c493023f794be09861e9045f
@@ -0,0 +1 @@
+x¥Aª!D³ö}G㌠á“Mr‚\ m[FãÇ1äú1gÈ¢ (¨G×Rr3ëCo"ÀvŽ^hq‘<7éÄAóY{žâ"Î&$ÄDýS“g˜([B!´Î¡wƳY’Ÿg±£ˆl$%E¯¾Ö·ø¦á±Ö²×'\d¤_w-™[Ýkê'®å¦³1hüµÓZt íòBÝó&;ô•:¼ó¶AÈñû"åm%ªþV \ No newline at end of file
diff --git a/tests/resources/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a b/tests/resources/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
new file mode 100644
index 0000000..2ef4faa
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af b/tests/resources/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
new file mode 100644
index 0000000..716b0c6
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/76/3d71aadf09a7951596c9746c024e7eece7c7af
@@ -0,0 +1 @@
+xŽAj!³ö?0¨£ßÂ09Êo}HÚ6¨}ÿôjUPP©ÕZ&Yÿø˜ AÔ›±€pŒÁFdë¼÷pz[fŽYŒ½PÒqLJ.,Z§`™Å®Ð.ù`’vÙ ³q $Æ5+9çOëtœû>Û/úDE/龡W¯ï*e¿§VŸdf1>ð覭Öê²×äÄ›¹úÊ™F« ­ìTŽÙhœk.i¶^0Ô?P¼R, \ No newline at end of file
diff --git a/tests/resources/testrepo/.gitted/objects/7b/2417a23b63e1fdde88c80e14b33247c6e5785a b/tests/resources/testrepo/.gitted/objects/7b/2417a23b63e1fdde88c80e14b33247c6e5785a
new file mode 100644
index 0000000..db778aa
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/7b/2417a23b63e1fdde88c80e14b33247c6e5785a
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980 b/tests/resources/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980
new file mode 100644
index 0000000..23c462f
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/7b/4384978d2493e851f9cca7858815fac9b10980
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d b/tests/resources/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
new file mode 100644
index 0000000..2f9b6b6
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479 b/tests/resources/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
new file mode 100644
index 0000000..5df58dd
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/87/380ae84009e9c503506c2f6143a4fc6c60bf80 b/tests/resources/testrepo/.gitted/objects/87/380ae84009e9c503506c2f6143a4fc6c60bf80
new file mode 100644
index 0000000..3042f57
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/87/380ae84009e9c503506c2f6143a4fc6c60bf80
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162 b/tests/resources/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
new file mode 100644
index 0000000..4cc3f4d
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/94/4c0f6e4dfa41595e6eb3ceecdb14f50fe18162
@@ -0,0 +1 @@
+x+)JMU044b040031QrutñueX¡l¨ðmmA‹m›Ì£íJ}Gß;U‘T”˜—œŸ–™“ªWRQÂ`6ýš÷KÇ¥¶^/¾-*|òøWØ¥3P¥y©å`%ËEÛÞ±\&gŽÐ|Ÿ0§ÿ†{Ó1X \ No newline at end of file
diff --git a/tests/resources/testrepo/.gitted/objects/97/328ac7e3bd0bcd3900cb3e7a624d71dd0df888 b/tests/resources/testrepo/.gitted/objects/97/328ac7e3bd0bcd3900cb3e7a624d71dd0df888
new file mode 100644
index 0000000..2aa37bc
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/97/328ac7e3bd0bcd3900cb3e7a624d71dd0df888
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/99/1f1b12603e1d78411c1b4042719f964efa7adf b/tests/resources/testrepo/.gitted/objects/99/1f1b12603e1d78411c1b4042719f964efa7adf
new file mode 100644
index 0000000..4173754
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/99/1f1b12603e1d78411c1b4042719f964efa7adf
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4 b/tests/resources/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
new file mode 100644
index 0000000..bf7b2bb
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/9a/03079b8a8ee85a0bee58bf9be3da8b62414ed4
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/9b/1719f5cf069568785080a0bbabbe7c377e22ae b/tests/resources/testrepo/.gitted/objects/9b/1719f5cf069568785080a0bbabbe7c377e22ae
new file mode 100644
index 0000000..13e3f58
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/9b/1719f5cf069568785080a0bbabbe7c377e22ae
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a b/tests/resources/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
new file mode 100644
index 0000000..a796124
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
@@ -0,0 +1,3 @@
+xŽ[
+Â0EýÎ*fÊäÕ¤ "¸W0“‡-ØFâtÿÝ—çpS[–YÀ˜x^
+Díb CLhutɉ}¥8X*4Zí¬sY½¨—UÀ‘AÃÖ ÌX3‡R«Mµ¶) s6è¼¢M¦ÖážšÜ&Jm…ó;}Çõ±Ðü<¥¶\@›à‚ÑÞpÄ€¨vº?”ò«jÛºLð«¨Ø?Hå \ No newline at end of file
diff --git a/tests/resources/testrepo/.gitted/objects/a3/8d028f71eaa590febb7d716b1ca32350cf70da b/tests/resources/testrepo/.gitted/objects/a3/8d028f71eaa590febb7d716b1ca32350cf70da
new file mode 100644
index 0000000..4df22ec
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/a3/8d028f71eaa590febb7d716b1ca32350cf70da
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f b/tests/resources/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
new file mode 100644
index 0000000..f858869
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
@@ -0,0 +1,2 @@
+x;j1DëmdÓú·À˜ÇŽ|M«µ3`ŒV{ >€³âQ¯ ¸·vL0I?Í!š4–Z=Ê! ×¦8²F¢Ã’!rÖsQßyÈ9]$DŽ&„l6AÇ>jFWüÒµ IKNiûë§Z¢%¡SˆŒ‘
+‹Ò ­ÅʉøU~̽øä>'¼ï™û ¯wþ ×[ËÇ× ÷öÚDGÚ¡±ðŒQ-ºMù«>dܶ‘OÞáÒò}í\à8g_ШÂoYr \ No newline at end of file
diff --git a/tests/resources/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750 b/tests/resources/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
new file mode 100644
index 0000000..29c8e82
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/a6/5fedf39aefe402d3bb6e24df4d4f5fe4547750
@@ -0,0 +1,3 @@
+xŽQ
+!@ûösBQ"‚ŽÐ ÆÙ± rÍîßÒú{<xð¤·öàîƪ
+™HlJSer!ZPTe*Žj°UÝÑEo^¼ê2 (†ˆ¬XSÅ€ED‘ƒO<Y¦šj$2üs_á&} ¸Î,}Ó[~p¹7~<ÒÛ:Ÿ £°·ÉZ³Ùípè?­1_ûåC0 \ No newline at end of file
diff --git a/tests/resources/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd b/tests/resources/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
new file mode 100644
index 0000000..d0d7e73
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 b/tests/resources/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
new file mode 100644
index 0000000..18a7f61
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/ad/edac69457183c8265c8a9614c1c4fed31d1ff3 b/tests/resources/testrepo/.gitted/objects/ad/edac69457183c8265c8a9614c1c4fed31d1ff3
new file mode 100644
index 0000000..c054fc0
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/ad/edac69457183c8265c8a9614c1c4fed31d1ff3
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12 b/tests/resources/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
new file mode 100644
index 0000000..d952546
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/ae/90f12eea699729ed24555e40b9fd669da12a12
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/af/e4393b2b2a965f06acf2ca9658eaa01e0cd6b6 b/tests/resources/testrepo/.gitted/objects/af/e4393b2b2a965f06acf2ca9658eaa01e0cd6b6
new file mode 100644
index 0000000..6948f1b
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/af/e4393b2b2a965f06acf2ca9658eaa01e0cd6b6
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1 b/tests/resources/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
new file mode 100644
index 0000000..f460f25
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/b2/5fa35b38051e4ae45d4222e795f9df2e43f1d1
@@ -0,0 +1,2 @@
+xÌA
+Â0…a×9ÅìIÒ ™€ˆp'î§1Ѷ‘v\x{cáýðVŸpƒvWûgŠ¾ÇŽ0xº[ ]"g†#{rDÆ Cot ­äûN œU $­ò?9-p+1Í^¤ÀQx®¯Ï9O\ÆC¬Ó Œm'D {mµVêú(+´ñælè¶,Þ \ No newline at end of file
diff --git a/tests/resources/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593 b/tests/resources/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
new file mode 100644
index 0000000..f613670
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/b6/361fc6a97178d8fc8639fdeed71c775ab52593
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 b/tests/resources/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
new file mode 100644
index 0000000..0817229
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
@@ -0,0 +1,3 @@
+xKj1D³Ö)zçUBëÛ-0ÁuV9¦Õò<#£È÷ÏȲ+ŠW<Jú¶Ý&8Ê/s¨‚e‹µµÈ•KJ­«½S
+ØRvÌÁ{©æQ†îr«äY¹QN$H\Eµ²Íè=6áX5¦òÇK Fr)·(‰dC‡Î†”­–œ—jÊs®}À—ô9ác-Òw8Ëo¸\·r»¿IßÞÁ:
+l}F‚W$Ds´Ç£©ÿÙšOW…e”]V8-ÃÌÈ"U \ No newline at end of file
diff --git a/tests/resources/testrepo/.gitted/objects/c0/528fd6cc988c0a40ce0be11bc192fc8dc5346e b/tests/resources/testrepo/.gitted/objects/c0/528fd6cc988c0a40ce0be11bc192fc8dc5346e
new file mode 100644
index 0000000..0401ab4
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/c0/528fd6cc988c0a40ce0be11bc192fc8dc5346e
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/c3/6d8ea75da8cb510fcb0c408c1d7e53f9a99dbe b/tests/resources/testrepo/.gitted/objects/c3/6d8ea75da8cb510fcb0c408c1d7e53f9a99dbe
new file mode 100644
index 0000000..0975f7f
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/c3/6d8ea75da8cb510fcb0c408c1d7e53f9a99dbe
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd b/tests/resources/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
new file mode 100644
index 0000000..75f541f
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
@@ -0,0 +1,3 @@
+xŽQ
+Â0DýÎ)öʦ»I<‚'ØlR+˜Fj¼¿EoàÏ0<xÃh«õÞa Üõµ]È™­åXUlÞPF)Åz‘4yó”µ,\r 'SÂÄ-mI4
+‘Xhô”&òÌFÞ}n+\µõ—Y´-p|é·œoUz;-‘aÑlt{ØË?®I«,:ÃoÚR̳cHK \ No newline at end of file
diff --git a/tests/resources/testrepo/.gitted/objects/ce/054d4c5e3c83522aed8bc061987b46b7ede3be b/tests/resources/testrepo/.gitted/objects/ce/054d4c5e3c83522aed8bc061987b46b7ede3be
new file mode 100644
index 0000000..4910e4c
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/ce/054d4c5e3c83522aed8bc061987b46b7ede3be
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9 b/tests/resources/testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9
new file mode 100644
index 0000000..7620c51
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/cf/80f8de9f1185bf3a05f993f6121880dd0cfbc9
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46 b/tests/resources/testrepo/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46
new file mode 100644
index 0000000..0b3611a
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/d4/27e0b2e138501a3d15cc376077a3631e15bd46
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83 b/tests/resources/testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83
new file mode 100644
index 0000000..00940f0
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/d5/2a8fe84ceedf260afe4f0287bbfca04a117e83
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f b/tests/resources/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
new file mode 100644
index 0000000..a67d6e6
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/d6/c93164c249c8000205dd4ec5cbca1b516d487f
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/e3/6900c3224db4adf4c7f7a09d4ac80247978a13 b/tests/resources/testrepo/.gitted/objects/e3/6900c3224db4adf4c7f7a09d4ac80247978a13
new file mode 100644
index 0000000..e74291f
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/e3/6900c3224db4adf4c7f7a09d4ac80247978a13
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644
index 0000000..7112238
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0 b/tests/resources/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
new file mode 100644
index 0000000..b135ecc
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/e7/b4ad382349ff96dd8199000580b9b1e2042eb0
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf b/tests/resources/testrepo/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf
new file mode 100644
index 0000000..974b72d
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/ee/3fa1b8c00aff7fe02065fdb50864bb0d932ccf
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3 b/tests/resources/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
new file mode 100644
index 0000000..82e2790
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/f1/425cef211cc08caa31e7b545ffb232acb098c3
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 b/tests/resources/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
new file mode 100644
index 0000000..697c94c
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/f9/ed4af42472941da45a3ce44458455ed227a6be b/tests/resources/testrepo/.gitted/objects/f9/ed4af42472941da45a3ce44458455ed227a6be
new file mode 100644
index 0000000..69c52fc
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/f9/ed4af42472941da45a3ce44458455ed227a6be
@@ -0,0 +1,2 @@
+x¥N[
+B!ìÛUø„zôêˆ~ÚA8WÜ 3ÌK-?‹vÐ|Íf&ÖR.]ë6½1Kç-pÖÖ%˜ Êd‚ˆ„& ×ÚSò6‹;5¾u©3Í Á9 ΄h|ÓÄ`UÀh8gAk_j“§ô¤–äy©åQorÏÃý°#ƒŸÚÅZR;*¸1*·j@ wœíügàǵÓ|eñ§OÏ \ No newline at end of file
diff --git a/tests/resources/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92 b/tests/resources/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
new file mode 100644
index 0000000..112998d
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765 b/tests/resources/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
new file mode 100644
index 0000000..12bf5f3
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx b/tests/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
new file mode 100644
index 0000000..5068f28
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.idx
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack b/tests/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
new file mode 100644
index 0000000..a6a1f30
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/pack/pack-a81e489679b7d3418f9ab594bda8ceb37dd4c695.pack
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx b/tests/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
new file mode 100644
index 0000000..94c3c71
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack b/tests/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
new file mode 100644
index 0000000..74c7fe4
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx b/tests/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
new file mode 100644
index 0000000..555cfa9
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.idx
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack b/tests/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
new file mode 100644
index 0000000..4d539ed
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/objects/pack/pack-d85f5d483273108c9d8dd0e4728ccf0b2982423a.pack
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/packed-refs b/tests/resources/testrepo/.gitted/packed-refs
new file mode 100644
index 0000000..b578986
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/packed-refs
@@ -0,0 +1,4 @@
+# pack-refs with: peeled sorted
+41bc8c69075bbdb46c5c6f0566cc8cc5b46e8bd9 refs/heads/packed
+5b5b025afb0b4c913b4c338a42934a3863bf3644 refs/heads/packed-test
+b25fa35b38051e4ae45d4222e795f9df2e43f1d1 refs/tags/packed-tag
diff --git a/tests/resources/testrepo/.gitted/refs/heads/br2 b/tests/resources/testrepo/.gitted/refs/heads/br2
new file mode 100644
index 0000000..aab87e5
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/refs/heads/br2
@@ -0,0 +1 @@
+a4a7dce85cf63874e984719f4fdd239f5145052f
diff --git a/tests/resources/testrepo/.gitted/refs/heads/dir b/tests/resources/testrepo/.gitted/refs/heads/dir
new file mode 100644
index 0000000..4567d37
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/refs/heads/dir
@@ -0,0 +1 @@
+144344043ba4d4a405da03de3844aa829ae8be0e
diff --git a/tests/resources/testrepo/.gitted/refs/heads/executable b/tests/resources/testrepo/.gitted/refs/heads/executable
new file mode 100644
index 0000000..2bdccea
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/refs/heads/executable
@@ -0,0 +1 @@
+f9ed4af42472941da45a3ce44458455ed227a6be
diff --git a/tests/resources/testrepo/.gitted/refs/heads/ident b/tests/resources/testrepo/.gitted/refs/heads/ident
new file mode 100644
index 0000000..2cfd880
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/refs/heads/ident
@@ -0,0 +1 @@
+6fd5c7dd2ab27b48c493023f794be09861e9045f
diff --git a/tests/resources/testrepo/.gitted/refs/heads/long-file-name b/tests/resources/testrepo/.gitted/refs/heads/long-file-name
new file mode 100644
index 0000000..1f942a7
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/refs/heads/long-file-name
@@ -0,0 +1 @@
+6b377958d8c6a4906e8573b53672a1a23a4e8ce6
diff --git a/tests/resources/testrepo/.gitted/refs/heads/master b/tests/resources/testrepo/.gitted/refs/heads/master
new file mode 100644
index 0000000..f31fe78
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/refs/heads/master
@@ -0,0 +1 @@
+099fabac3a9ea935598528c27f866e34089c2eff
diff --git a/tests/resources/testrepo/.gitted/refs/heads/merge-conflict b/tests/resources/testrepo/.gitted/refs/heads/merge-conflict
new file mode 100644
index 0000000..3e24a24
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/refs/heads/merge-conflict
@@ -0,0 +1 @@
+a38d028f71eaa590febb7d716b1ca32350cf70da
diff --git a/tests/resources/testrepo/.gitted/refs/heads/packed-test b/tests/resources/testrepo/.gitted/refs/heads/packed-test
new file mode 100644
index 0000000..f2c14ad
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/refs/heads/packed-test
@@ -0,0 +1 @@
+4a202b346bb0fb0db7eff3cffeb3c70babbd2045
diff --git a/tests/resources/testrepo/.gitted/refs/heads/subtrees b/tests/resources/testrepo/.gitted/refs/heads/subtrees
new file mode 100644
index 0000000..ad27e0b
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/refs/heads/subtrees
@@ -0,0 +1 @@
+763d71aadf09a7951596c9746c024e7eece7c7af
diff --git a/tests/resources/testrepo/.gitted/refs/heads/test b/tests/resources/testrepo/.gitted/refs/heads/test
new file mode 100644
index 0000000..399c4c7
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/refs/heads/test
@@ -0,0 +1 @@
+e90810b8df3e80c413d903f631643c716887138d
diff --git a/tests/resources/testrepo/.gitted/refs/heads/testrepo-worktree b/tests/resources/testrepo/.gitted/refs/heads/testrepo-worktree
new file mode 100644
index 0000000..f31fe78
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/refs/heads/testrepo-worktree
@@ -0,0 +1 @@
+099fabac3a9ea935598528c27f866e34089c2eff
diff --git a/tests/resources/testrepo/.gitted/refs/symref b/tests/resources/testrepo/.gitted/refs/symref
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/refs/symref
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/testrepo/.gitted/refs/tags/e90810b b/tests/resources/testrepo/.gitted/refs/tags/e90810b
new file mode 100644
index 0000000..584495d
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/refs/tags/e90810b
@@ -0,0 +1 @@
+7b4384978d2493e851f9cca7858815fac9b10980
diff --git a/tests/resources/testrepo/.gitted/refs/tags/foo/bar b/tests/resources/testrepo/.gitted/refs/tags/foo/bar
new file mode 100644
index 0000000..6ee952a
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/refs/tags/foo/bar
@@ -0,0 +1 @@
+b25fa35b38051e4ae45d4222e795f9df2e43f1d1
diff --git a/tests/resources/testrepo/.gitted/refs/tags/foo/foo/bar b/tests/resources/testrepo/.gitted/refs/tags/foo/foo/bar
new file mode 100644
index 0000000..6ee952a
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/refs/tags/foo/foo/bar
@@ -0,0 +1 @@
+b25fa35b38051e4ae45d4222e795f9df2e43f1d1
diff --git a/tests/resources/testrepo/.gitted/refs/tags/point_to_blob b/tests/resources/testrepo/.gitted/refs/tags/point_to_blob
new file mode 100644
index 0000000..f874a3f
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/refs/tags/point_to_blob
@@ -0,0 +1 @@
+1385f264afb75a56a5bec74243be9b367ba4ca08
diff --git a/tests/resources/testrepo/.gitted/refs/tags/test b/tests/resources/testrepo/.gitted/refs/tags/test
new file mode 100644
index 0000000..6ee952a
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/refs/tags/test
@@ -0,0 +1 @@
+b25fa35b38051e4ae45d4222e795f9df2e43f1d1
diff --git a/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/HEAD b/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/HEAD
new file mode 100644
index 0000000..1b8637e
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/testrepo-worktree
diff --git a/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/commondir b/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/commondir
new file mode 100644
index 0000000..aab0408
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/commondir
@@ -0,0 +1 @@
+../..
diff --git a/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/gitdir b/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/gitdir
new file mode 100644
index 0000000..0d37a57
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/gitdir
@@ -0,0 +1 @@
+../../../../testrepo-worktree/.git
diff --git a/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/index b/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/index
new file mode 100644
index 0000000..4114190
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/index
Binary files differ
diff --git a/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/logs/HEAD b/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/logs/HEAD
new file mode 100644
index 0000000..3bede50
--- /dev/null
+++ b/tests/resources/testrepo/.gitted/worktrees/testrepo-worktree/logs/HEAD
@@ -0,0 +1 @@
+099fabac3a9ea935598528c27f866e34089c2eff 099fabac3a9ea935598528c27f866e34089c2eff Patrick Steinhardt <ps@pks.im> 1442484463 +0200 checkout: moving from 099fabac3a9ea935598528c27f866e34089c2eff to testrepo-worktree
diff --git a/tests/resources/testrepo2/.gitted/HEAD b/tests/resources/testrepo2/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/testrepo2/.gitted/config b/tests/resources/testrepo2/.gitted/config
new file mode 100644
index 0000000..6966b8a
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/config
@@ -0,0 +1,49 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
+ precomposeunicode = false
+[remote "origin"]
+ url = https://github.com/libgit2/false.git
+ fetch = +refs/heads/*:refs/remotes/origin/*
+[remote "insteadof-url-fetch"]
+ url = http://example.com/url/fetch/libgit2
+ fetch = +refs/heads/*:refs/remotes/test/*
+[remote "insteadof-url-push"]
+ url = http://example.com/url/push/libgit2
+ fetch = +refs/heads/*:refs/remotes/test/*
+[remote "insteadof-url-both"]
+ url = http://example.com/url/both/libgit2
+ fetch = +refs/heads/*:refs/remotes/test/*
+[remote "insteadof-pushurl-fetch"]
+ url = http://example.com/url/fetch/libgit2
+ pushurl = http://example.com/url/fetch/libgit2-push
+ fetch = +refs/heads/*:refs/remotes/test/*
+[remote "insteadof-pushurl-push"]
+ url = http://example.com/url/push/libgit2
+ pushurl = http://example.com/url/push/libgit2-push
+ fetch = +refs/heads/*:refs/remotes/test/*
+[remote "insteadof-pushurl-both"]
+ url = http://example.com/url/both/libgit2
+ pushurl = http://example.com/url/both/libgit2-push
+ fetch = +refs/heads/*:refs/remotes/test/*
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
+ rebase = true
+[url "longer-non-prefix-match"]
+ # not applicable because it's not a prefix match
+ insteadOf = ttp://example.com/url/fetch/li
+[url "shorter-prefix"]
+ # not applicable because the matched prefix is shorter
+ insteadOf = http://example.com/url/fe
+[url "http://github.com/url/fetch"]
+ insteadOf = http://example.com/url/fetch
+[url "http://github.com/url/both"]
+ insteadOf = http://example.com/url/both
+[url "git@github.com:url/push"]
+ pushInsteadOf = http://example.com/url/push
+[url "git@github.com:url/both"]
+ pushInsteadOf = http://example.com/url/both
diff --git a/tests/resources/testrepo2/.gitted/description b/tests/resources/testrepo2/.gitted/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/testrepo2/.gitted/index b/tests/resources/testrepo2/.gitted/index
new file mode 100644
index 0000000..b614d07
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/index
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/info/exclude b/tests/resources/testrepo2/.gitted/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/testrepo2/.gitted/logs/HEAD b/tests/resources/testrepo2/.gitted/logs/HEAD
new file mode 100644
index 0000000..4e80c69
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 36060c58702ed4c2a40832c51758d5344201d89a Russell Belfer <rb@github.com> 1368278260 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/../../../rugged/test/fixtures/testrepo.git
diff --git a/tests/resources/testrepo2/.gitted/logs/refs/heads/master b/tests/resources/testrepo2/.gitted/logs/refs/heads/master
new file mode 100644
index 0000000..4e80c69
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 36060c58702ed4c2a40832c51758d5344201d89a Russell Belfer <rb@github.com> 1368278260 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/../../../rugged/test/fixtures/testrepo.git
diff --git a/tests/resources/testrepo2/.gitted/logs/refs/remotes/origin/HEAD b/tests/resources/testrepo2/.gitted/logs/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..4e80c69
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/logs/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000 36060c58702ed4c2a40832c51758d5344201d89a Russell Belfer <rb@github.com> 1368278260 -0700 clone: from /Users/rb/src/libgit2/tests-clar/resources/../../../rugged/test/fixtures/testrepo.git
diff --git a/tests/resources/testrepo2/.gitted/objects/0c/37a5391bbff43c37f0d0371823a5509eed5b1d b/tests/resources/testrepo2/.gitted/objects/0c/37a5391bbff43c37f0d0371823a5509eed5b1d
new file mode 100644
index 0000000..bfe146a
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/0c/37a5391bbff43c37f0d0371823a5509eed5b1d
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08 b/tests/resources/testrepo2/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
new file mode 100644
index 0000000..cedb2a2
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/13/85f264afb75a56a5bec74243be9b367ba4ca08
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7 b/tests/resources/testrepo2/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
new file mode 100644
index 0000000..93a16f1
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/18/1037049a54a1eb5fab404658a3a250b44335d7
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd b/tests/resources/testrepo2/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
new file mode 100644
index 0000000..ba0bfb3
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/18/10dff58d8a660512d4832e740f692884338ccd
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/2d/2eff63372b08adf0a9eb84109ccf7d19e2f3a2 b/tests/resources/testrepo2/.gitted/objects/2d/2eff63372b08adf0a9eb84109ccf7d19e2f3a2
new file mode 100644
index 0000000..3cd240d
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/2d/2eff63372b08adf0a9eb84109ccf7d19e2f3a2
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/36/060c58702ed4c2a40832c51758d5344201d89a b/tests/resources/testrepo2/.gitted/objects/36/060c58702ed4c2a40832c51758d5344201d89a
new file mode 100644
index 0000000..0c62460
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/36/060c58702ed4c2a40832c51758d5344201d89a
@@ -0,0 +1,2 @@
+xŽQ
+Â0ýÎ)re“Ý´ ˆÁ$Û­-˜FÒíý-ÁŸÇ00𸖲¨õ¾?i±L#»‚ÐHSŽŒS×#qÏ2²DÈæ“š¬jC|HS†L‹8$ò)áÐaž°#2i×¹6ûäªjsâºÚëÆ?¸¿JZÞ®åfçtΞÁ˜ÃUþiͶçqiÂZÛ"›ù_/H6 \ No newline at end of file
diff --git a/tests/resources/testrepo2/.gitted/objects/39/6c7f1adb7925f51ba13a75f48252f44c5a14a2 b/tests/resources/testrepo2/.gitted/objects/39/6c7f1adb7925f51ba13a75f48252f44c5a14a2
new file mode 100644
index 0000000..667704b
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/39/6c7f1adb7925f51ba13a75f48252f44c5a14a2
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057 b/tests/resources/testrepo2/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
new file mode 100644
index 0000000..7ca4cee
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/45/b983be36b73c0788dc9cbcb76cbb80fc7bb057
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045 b/tests/resources/testrepo2/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
new file mode 100644
index 0000000..8953b6c
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/4a/202b346bb0fb0db7eff3cffeb3c70babbd2045
@@ -0,0 +1,2 @@
+xŽQ
+Â0DýÎ)öÊ6›Í¦ "xO°‰‰-ØFb¼¿EoàÏ0 ¼Ç¤º,ske×[ÎPn8R,EpD?±gŸ}Ê^3² âÙ<µåµGŽhYKÄèÒ8ЖDAÉ)¿ÉÈ;gôݧÚàšjïp™4ÕŽ¯ô-çû¢óãêr‚ÁŠ;°s°GA4Ûº=ìùÖ(ôin7øIÌKÍFE \ No newline at end of file
diff --git a/tests/resources/testrepo2/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644 b/tests/resources/testrepo2/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
new file mode 100644
index 0000000..c1f22c5
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/5b/5b025afb0b4c913b4c338a42934a3863bf3644
@@ -0,0 +1,2 @@
+xŽÛ 1EýNi@™Ék2 "X‚$ÙYW0YcÿíÀ¿Ã…s¸¥ÕzïÚÚõMDÏ€0æœ8!¶†ÉÌÞs‰ XŠªgÚdí::@X0»P¢wÙ"F/‰‰œÍRàˆUz÷¥múZZïú²¤ÒV}|•/œo5݇ÒêI£!¬1z Æ:vùÇUim}ê/¢>
+öF- \ No newline at end of file
diff --git a/tests/resources/testrepo2/.gitted/objects/61/9f9935957e010c419cb9d15621916ddfcc0b96 b/tests/resources/testrepo2/.gitted/objects/61/9f9935957e010c419cb9d15621916ddfcc0b96
new file mode 100644
index 0000000..1fd79b4
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/61/9f9935957e010c419cb9d15621916ddfcc0b96
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a b/tests/resources/testrepo2/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
new file mode 100644
index 0000000..2ef4faa
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/75/057dd4114e74cca1d750d0aee1647c903cb60a
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/7f/043268ea43ce18e3540acaabf9e090c91965b0 b/tests/resources/testrepo2/.gitted/objects/7f/043268ea43ce18e3540acaabf9e090c91965b0
new file mode 100644
index 0000000..3d1016d
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/7f/043268ea43ce18e3540acaabf9e090c91965b0
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d b/tests/resources/testrepo2/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
new file mode 100644
index 0000000..2f9b6b6
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/81/4889a078c031f61ed08ab5fa863aea9314344d
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479 b/tests/resources/testrepo2/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
new file mode 100644
index 0000000..5df58dd
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/84/96071c1b46c854b31185ea97743be6a8774479
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a b/tests/resources/testrepo2/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
new file mode 100644
index 0000000..a796124
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/9f/d738e8f7967c078dceed8190330fc8648ee56a
@@ -0,0 +1,3 @@
+xŽ[
+Â0EýÎ*fÊäÕ¤ "¸W0“‡-ØFâtÿÝ—çpS[–YÀ˜x^
+Díb CLhutɉ}¥8X*4Zí¬sY½¨—UÀ‘AÃÖ ÌX3‡R«Mµ¶) s6è¼¢M¦ÖážšÜ&Jm…ó;}Çõ±Ðü<¥¶\@›à‚ÑÞpÄ€¨vº?”ò«jÛºLð«¨Ø?Hå \ No newline at end of file
diff --git a/tests/resources/testrepo2/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f b/tests/resources/testrepo2/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
new file mode 100644
index 0000000..f858869
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/a4/a7dce85cf63874e984719f4fdd239f5145052f
@@ -0,0 +1,2 @@
+x;j1DëmdÓú·À˜ÇŽ|M«µ3`ŒV{ >€³âQ¯ ¸·vL0I?Í!š4–Z=Ê! ×¦8²F¢Ã’!rÖsQßyÈ9]$DŽ&„l6AÇ>jFWüÒµ IKNiûë§Z¢%¡SˆŒ‘
+‹Ò ­ÅʉøU~̽øä>'¼ï™û ¯wþ ×[ËÇ× ÷öÚDGÚ¡±ðŒQ-ºMù«>dܶ‘OÞáÒò}í\à8g_ШÂoYr \ No newline at end of file
diff --git a/tests/resources/testrepo2/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd b/tests/resources/testrepo2/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
new file mode 100644
index 0000000..d0d7e73
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/a7/1586c1dfe8a71c6cbf6c129f404c5642ff31bd
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6 b/tests/resources/testrepo2/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
new file mode 100644
index 0000000..18a7f61
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/a8/233120f6ad708f843d861ce2b7228ec4e3dec6
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644 b/tests/resources/testrepo2/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
new file mode 100644
index 0000000..0817229
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/be/3563ae3f795b2b4353bcce3a527ad0a4f7f644
@@ -0,0 +1,3 @@
+xKj1D³Ö)zçUBëÛ-0ÁuV9¦Õò<#£È÷ÏȲ+ŠW<Jú¶Ý&8Ê/s¨‚e‹µµÈ•KJ­«½S
+ØRvÌÁ{©æQ†îr«äY¹QN$H\Eµ²Íè=6áX5¦òÇK Fr)·(‰dC‡Î†”­–œ—jÊs®}À—ô9ác-Òw8Ëo¸\·r»¿IßÞÁ:
+l}F‚W$Ds´Ç£©ÿÙšOW…e”]V8-ÃÌÈ"U \ No newline at end of file
diff --git a/tests/resources/testrepo2/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd b/tests/resources/testrepo2/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
new file mode 100644
index 0000000..75f541f
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/c4/7800c7266a2be04c571c04d5a6614691ea99bd
@@ -0,0 +1,3 @@
+xŽQ
+Â0DýÎ)öʦ»I<‚'ØlR+˜Fj¼¿EoàÏ0<xÃh«õÞa Üõµ]È™­åXUlÞPF)Åz‘4yó”µ,\r 'SÂÄ-mI4
+‘Xhô”&òÌFÞ}n+\µõ—Y´-p|é·œoUz;-‘aÑlt{ØË?®I«,:ÃoÚR̳cHK \ No newline at end of file
diff --git a/tests/resources/testrepo2/.gitted/objects/c4/dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b b/tests/resources/testrepo2/.gitted/objects/c4/dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b
new file mode 100644
index 0000000..599e160
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/c4/dc1555e4d4fa0e0c9c3fc46734c7c35b3ce90b
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/testrepo2/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644
index 0000000..7112238
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1 b/tests/resources/testrepo2/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
new file mode 100644
index 0000000..0377096
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/f6/0079018b664e4e79329a7ef9559c8d9e0378d1
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92 b/tests/resources/testrepo2/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
new file mode 100644
index 0000000..112998d
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/fa/49b077972391ad58037050f2a75f74e3671e92
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765 b/tests/resources/testrepo2/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
new file mode 100644
index 0000000..12bf5f3
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/fd/093bff70906175335656e6ce6ae05783708765
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx b/tests/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
new file mode 100644
index 0000000..94c3c71
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.idx
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack b/tests/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
new file mode 100644
index 0000000..74c7fe4
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/objects/pack/pack-d7c6adf9f61318f041845b01440d09aa7a91e1b5.pack
Binary files differ
diff --git a/tests/resources/testrepo2/.gitted/packed-refs b/tests/resources/testrepo2/.gitted/packed-refs
new file mode 100644
index 0000000..01de871
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/packed-refs
@@ -0,0 +1,6 @@
+# pack-refs with: peeled fully-peeled sorted
+36060c58702ed4c2a40832c51758d5344201d89a refs/remotes/origin/master
+41bc8c69075bbdb46c5c6f0566cc8cc5b46e8bd9 refs/remotes/origin/packed
+5b5b025afb0b4c913b4c338a42934a3863bf3644 refs/tags/v0.9
+0c37a5391bbff43c37f0d0371823a5509eed5b1d refs/tags/v1.0
+^5b5b025afb0b4c913b4c338a42934a3863bf3644
diff --git a/tests/resources/testrepo2/.gitted/refs/heads/master b/tests/resources/testrepo2/.gitted/refs/heads/master
new file mode 100644
index 0000000..a7eafce
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/refs/heads/master
@@ -0,0 +1 @@
+36060c58702ed4c2a40832c51758d5344201d89a
diff --git a/tests/resources/testrepo2/.gitted/refs/remotes/origin/HEAD b/tests/resources/testrepo2/.gitted/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..6efe28f
--- /dev/null
+++ b/tests/resources/testrepo2/.gitted/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/testrepo2/README b/tests/resources/testrepo2/README
new file mode 100644
index 0000000..1385f26
--- /dev/null
+++ b/tests/resources/testrepo2/README
@@ -0,0 +1 @@
+hey
diff --git a/tests/resources/testrepo2/new.txt b/tests/resources/testrepo2/new.txt
new file mode 100644
index 0000000..fa49b07
--- /dev/null
+++ b/tests/resources/testrepo2/new.txt
@@ -0,0 +1 @@
+new file
diff --git a/tests/resources/testrepo2/subdir/README b/tests/resources/testrepo2/subdir/README
new file mode 100644
index 0000000..1385f26
--- /dev/null
+++ b/tests/resources/testrepo2/subdir/README
@@ -0,0 +1 @@
+hey
diff --git a/tests/resources/testrepo2/subdir/new.txt b/tests/resources/testrepo2/subdir/new.txt
new file mode 100644
index 0000000..fa49b07
--- /dev/null
+++ b/tests/resources/testrepo2/subdir/new.txt
@@ -0,0 +1 @@
+new file
diff --git a/tests/resources/testrepo2/subdir/subdir2/README b/tests/resources/testrepo2/subdir/subdir2/README
new file mode 100644
index 0000000..1385f26
--- /dev/null
+++ b/tests/resources/testrepo2/subdir/subdir2/README
@@ -0,0 +1 @@
+hey
diff --git a/tests/resources/testrepo2/subdir/subdir2/new.txt b/tests/resources/testrepo2/subdir/subdir2/new.txt
new file mode 100644
index 0000000..fa49b07
--- /dev/null
+++ b/tests/resources/testrepo2/subdir/subdir2/new.txt
@@ -0,0 +1 @@
+new file
diff --git a/tests/resources/testrepo_256.git/FETCH_HEAD b/tests/resources/testrepo_256.git/FETCH_HEAD
new file mode 100644
index 0000000..4562d90
--- /dev/null
+++ b/tests/resources/testrepo_256.git/FETCH_HEAD
@@ -0,0 +1,2 @@
+decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f branch 'master' of git://example.com/git/testrepo.git
+7e9424c06052ca33bfc599bccadee60065d8664a9af7648a1455100c4f772e1c not-for-merge branch 'haacked' of git://example.com/git/testrepo.git
diff --git a/tests/resources/testrepo_256.git/HEAD b/tests/resources/testrepo_256.git/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/testrepo_256.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/testrepo_256.git/HEAD_TRACKER b/tests/resources/testrepo_256.git/HEAD_TRACKER
new file mode 100644
index 0000000..40d876b
--- /dev/null
+++ b/tests/resources/testrepo_256.git/HEAD_TRACKER
@@ -0,0 +1 @@
+ref: HEAD
diff --git a/tests/resources/testrepo_256.git/config b/tests/resources/testrepo_256.git/config
new file mode 100644
index 0000000..c1aac4a
--- /dev/null
+++ b/tests/resources/testrepo_256.git/config
@@ -0,0 +1,8 @@
+[core]
+ repositoryFormatVersion = 1
+ filemode = true
+ bare = true
+ ignorecase = true
+ precomposeunicode = true
+[extensions]
+ objectFormat = sha256
diff --git a/tests/resources/testrepo_256.git/description b/tests/resources/testrepo_256.git/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/testrepo_256.git/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/testrepo_256.git/hooks/applypatch-msg.sample b/tests/resources/testrepo_256.git/hooks/applypatch-msg.sample
new file mode 100755
index 0000000..a5d7b84
--- /dev/null
+++ b/tests/resources/testrepo_256.git/hooks/applypatch-msg.sample
@@ -0,0 +1,15 @@
+#!/bin/sh
+#
+# An example hook script to check the commit log message taken by
+# applypatch from an e-mail message.
+#
+# The hook should exit with non-zero status after issuing an
+# appropriate message if it wants to stop the commit. The hook is
+# allowed to edit the commit message file.
+#
+# To enable this hook, rename this file to "applypatch-msg".
+
+. git-sh-setup
+commitmsg="$(git rev-parse --git-path hooks/commit-msg)"
+test -x "$commitmsg" && exec "$commitmsg" ${1+"$@"}
+:
diff --git a/tests/resources/testrepo_256.git/hooks/commit-msg.sample b/tests/resources/testrepo_256.git/hooks/commit-msg.sample
new file mode 100755
index 0000000..b58d118
--- /dev/null
+++ b/tests/resources/testrepo_256.git/hooks/commit-msg.sample
@@ -0,0 +1,24 @@
+#!/bin/sh
+#
+# An example hook script to check the commit log message.
+# Called by "git commit" with one argument, the name of the file
+# that has the commit message. The hook should exit with non-zero
+# status after issuing an appropriate message if it wants to stop the
+# commit. The hook is allowed to edit the commit message file.
+#
+# To enable this hook, rename this file to "commit-msg".
+
+# Uncomment the below to add a Signed-off-by line to the message.
+# Doing this in a hook is a bad idea in general, but the prepare-commit-msg
+# hook is more suited to it.
+#
+# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
+# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
+
+# This example catches duplicate Signed-off-by lines.
+
+test "" = "$(grep '^Signed-off-by: ' "$1" |
+ sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || {
+ echo >&2 Duplicate Signed-off-by lines.
+ exit 1
+}
diff --git a/tests/resources/testrepo_256.git/hooks/fsmonitor-watchman.sample b/tests/resources/testrepo_256.git/hooks/fsmonitor-watchman.sample
new file mode 100755
index 0000000..14ed0aa
--- /dev/null
+++ b/tests/resources/testrepo_256.git/hooks/fsmonitor-watchman.sample
@@ -0,0 +1,173 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use IPC::Open2;
+
+# An example hook script to integrate Watchman
+# (https://facebook.github.io/watchman/) with git to speed up detecting
+# new and modified files.
+#
+# The hook is passed a version (currently 2) and last update token
+# formatted as a string and outputs to stdout a new update token and
+# all files that have been modified since the update token. Paths must
+# be relative to the root of the working tree and separated by a single NUL.
+#
+# To enable this hook, rename this file to "query-watchman" and set
+# 'git config core.fsmonitor .git/hooks/query-watchman'
+#
+my ($version, $last_update_token) = @ARGV;
+
+# Uncomment for debugging
+# print STDERR "$0 $version $last_update_token\n";
+
+# Check the hook interface version
+if ($version ne 2) {
+ die "Unsupported query-fsmonitor hook version '$version'.\n" .
+ "Falling back to scanning...\n";
+}
+
+my $git_work_tree = get_working_dir();
+
+my $retry = 1;
+
+my $json_pkg;
+eval {
+ require JSON::XS;
+ $json_pkg = "JSON::XS";
+ 1;
+} or do {
+ require JSON::PP;
+ $json_pkg = "JSON::PP";
+};
+
+launch_watchman();
+
+sub launch_watchman {
+ my $o = watchman_query();
+ if (is_work_tree_watched($o)) {
+ output_result($o->{clock}, @{$o->{files}});
+ }
+}
+
+sub output_result {
+ my ($clockid, @files) = @_;
+
+ # Uncomment for debugging watchman output
+ # open (my $fh, ">", ".git/watchman-output.out");
+ # binmode $fh, ":utf8";
+ # print $fh "$clockid\n@files\n";
+ # close $fh;
+
+ binmode STDOUT, ":utf8";
+ print $clockid;
+ print "\0";
+ local $, = "\0";
+ print @files;
+}
+
+sub watchman_clock {
+ my $response = qx/watchman clock "$git_work_tree"/;
+ die "Failed to get clock id on '$git_work_tree'.\n" .
+ "Falling back to scanning...\n" if $? != 0;
+
+ return $json_pkg->new->utf8->decode($response);
+}
+
+sub watchman_query {
+ my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'watchman -j --no-pretty')
+ or die "open2() failed: $!\n" .
+ "Falling back to scanning...\n";
+
+ # In the query expression below we're asking for names of files that
+ # changed since $last_update_token but not from the .git folder.
+ #
+ # To accomplish this, we're using the "since" generator to use the
+ # recency index to select candidate nodes and "fields" to limit the
+ # output to file names only. Then we're using the "expression" term to
+ # further constrain the results.
+ if (substr($last_update_token, 0, 1) eq "c") {
+ $last_update_token = "\"$last_update_token\"";
+ }
+ my $query = <<" END";
+ ["query", "$git_work_tree", {
+ "since": $last_update_token,
+ "fields": ["name"],
+ "expression": ["not", ["dirname", ".git"]]
+ }]
+ END
+
+ # Uncomment for debugging the watchman query
+ # open (my $fh, ">", ".git/watchman-query.json");
+ # print $fh $query;
+ # close $fh;
+
+ print CHLD_IN $query;
+ close CHLD_IN;
+ my $response = do {local $/; <CHLD_OUT>};
+
+ # Uncomment for debugging the watch response
+ # open ($fh, ">", ".git/watchman-response.json");
+ # print $fh $response;
+ # close $fh;
+
+ die "Watchman: command returned no output.\n" .
+ "Falling back to scanning...\n" if $response eq "";
+ die "Watchman: command returned invalid output: $response\n" .
+ "Falling back to scanning...\n" unless $response =~ /^\{/;
+
+ return $json_pkg->new->utf8->decode($response);
+}
+
+sub is_work_tree_watched {
+ my ($output) = @_;
+ my $error = $output->{error};
+ if ($retry > 0 and $error and $error =~ m/unable to resolve root .* directory (.*) is not watched/) {
+ $retry--;
+ my $response = qx/watchman watch "$git_work_tree"/;
+ die "Failed to make watchman watch '$git_work_tree'.\n" .
+ "Falling back to scanning...\n" if $? != 0;
+ $output = $json_pkg->new->utf8->decode($response);
+ $error = $output->{error};
+ die "Watchman: $error.\n" .
+ "Falling back to scanning...\n" if $error;
+
+ # Uncomment for debugging watchman output
+ # open (my $fh, ">", ".git/watchman-output.out");
+ # close $fh;
+
+ # Watchman will always return all files on the first query so
+ # return the fast "everything is dirty" flag to git and do the
+ # Watchman query just to get it over with now so we won't pay
+ # the cost in git to look up each individual file.
+ my $o = watchman_clock();
+ $error = $output->{error};
+
+ die "Watchman: $error.\n" .
+ "Falling back to scanning...\n" if $error;
+
+ output_result($o->{clock}, ("/"));
+ $last_update_token = $o->{clock};
+
+ eval { launch_watchman() };
+ return 0;
+ }
+
+ die "Watchman: $error.\n" .
+ "Falling back to scanning...\n" if $error;
+
+ return 1;
+}
+
+sub get_working_dir {
+ my $working_dir;
+ if ($^O =~ 'msys' || $^O =~ 'cygwin') {
+ $working_dir = Win32::GetCwd();
+ $working_dir =~ tr/\\/\//;
+ } else {
+ require Cwd;
+ $working_dir = Cwd::cwd();
+ }
+
+ return $working_dir;
+}
diff --git a/tests/resources/testrepo_256.git/hooks/post-update.sample b/tests/resources/testrepo_256.git/hooks/post-update.sample
new file mode 100755
index 0000000..ec17ec1
--- /dev/null
+++ b/tests/resources/testrepo_256.git/hooks/post-update.sample
@@ -0,0 +1,8 @@
+#!/bin/sh
+#
+# An example hook script to prepare a packed repository for use over
+# dumb transports.
+#
+# To enable this hook, rename this file to "post-update".
+
+exec git update-server-info
diff --git a/tests/resources/testrepo_256.git/hooks/pre-applypatch.sample b/tests/resources/testrepo_256.git/hooks/pre-applypatch.sample
new file mode 100755
index 0000000..4142082
--- /dev/null
+++ b/tests/resources/testrepo_256.git/hooks/pre-applypatch.sample
@@ -0,0 +1,14 @@
+#!/bin/sh
+#
+# An example hook script to verify what is about to be committed
+# by applypatch from an e-mail message.
+#
+# The hook should exit with non-zero status after issuing an
+# appropriate message if it wants to stop the commit.
+#
+# To enable this hook, rename this file to "pre-applypatch".
+
+. git-sh-setup
+precommit="$(git rev-parse --git-path hooks/pre-commit)"
+test -x "$precommit" && exec "$precommit" ${1+"$@"}
+:
diff --git a/tests/resources/testrepo_256.git/hooks/pre-commit.sample b/tests/resources/testrepo_256.git/hooks/pre-commit.sample
new file mode 100755
index 0000000..e144712
--- /dev/null
+++ b/tests/resources/testrepo_256.git/hooks/pre-commit.sample
@@ -0,0 +1,49 @@
+#!/bin/sh
+#
+# An example hook script to verify what is about to be committed.
+# Called by "git commit" with no arguments. The hook should
+# exit with non-zero status after issuing an appropriate message if
+# it wants to stop the commit.
+#
+# To enable this hook, rename this file to "pre-commit".
+
+if git rev-parse --verify HEAD >/dev/null 2>&1
+then
+ against=HEAD
+else
+ # Initial commit: diff against an empty tree object
+ against=$(git hash-object -t tree /dev/null)
+fi
+
+# If you want to allow non-ASCII filenames set this variable to true.
+allownonascii=$(git config --type=bool hooks.allownonascii)
+
+# Redirect output to stderr.
+exec 1>&2
+
+# Cross platform projects tend to avoid non-ASCII filenames; prevent
+# them from being added to the repository. We exploit the fact that the
+# printable range starts at the space character and ends with tilde.
+if [ "$allownonascii" != "true" ] &&
+ # Note that the use of brackets around a tr range is ok here, (it's
+ # even required, for portability to Solaris 10's /usr/bin/tr), since
+ # the square bracket bytes happen to fall in the designated range.
+ test $(git diff --cached --name-only --diff-filter=A -z $against |
+ LC_ALL=C tr -d '[ -~]\0' | wc -c) != 0
+then
+ cat <<\EOF
+Error: Attempt to add a non-ASCII file name.
+
+This can cause problems if you want to work with people on other platforms.
+
+To be portable it is advisable to rename the file.
+
+If you know what you are doing you can disable this check using:
+
+ git config hooks.allownonascii true
+EOF
+ exit 1
+fi
+
+# If there are whitespace errors, print the offending file names and fail.
+exec git diff-index --check --cached $against --
diff --git a/tests/resources/testrepo_256.git/hooks/pre-merge-commit.sample b/tests/resources/testrepo_256.git/hooks/pre-merge-commit.sample
new file mode 100755
index 0000000..399eab1
--- /dev/null
+++ b/tests/resources/testrepo_256.git/hooks/pre-merge-commit.sample
@@ -0,0 +1,13 @@
+#!/bin/sh
+#
+# An example hook script to verify what is about to be committed.
+# Called by "git merge" with no arguments. The hook should
+# exit with non-zero status after issuing an appropriate message to
+# stderr if it wants to stop the merge commit.
+#
+# To enable this hook, rename this file to "pre-merge-commit".
+
+. git-sh-setup
+test -x "$GIT_DIR/hooks/pre-commit" &&
+ exec "$GIT_DIR/hooks/pre-commit"
+:
diff --git a/tests/resources/testrepo_256.git/hooks/pre-push.sample b/tests/resources/testrepo_256.git/hooks/pre-push.sample
new file mode 100755
index 0000000..4ce688d
--- /dev/null
+++ b/tests/resources/testrepo_256.git/hooks/pre-push.sample
@@ -0,0 +1,53 @@
+#!/bin/sh
+
+# An example hook script to verify what is about to be pushed. Called by "git
+# push" after it has checked the remote status, but before anything has been
+# pushed. If this script exits with a non-zero status nothing will be pushed.
+#
+# This hook is called with the following parameters:
+#
+# $1 -- Name of the remote to which the push is being done
+# $2 -- URL to which the push is being done
+#
+# If pushing without using a named remote those arguments will be equal.
+#
+# Information about the commits which are being pushed is supplied as lines to
+# the standard input in the form:
+#
+# <local ref> <local oid> <remote ref> <remote oid>
+#
+# This sample shows how to prevent push of commits where the log message starts
+# with "WIP" (work in progress).
+
+remote="$1"
+url="$2"
+
+zero=$(git hash-object --stdin </dev/null | tr '[0-9a-f]' '0')
+
+while read local_ref local_oid remote_ref remote_oid
+do
+ if test "$local_oid" = "$zero"
+ then
+ # Handle delete
+ :
+ else
+ if test "$remote_oid" = "$zero"
+ then
+ # New branch, examine all commits
+ range="$local_oid"
+ else
+ # Update to existing branch, examine new commits
+ range="$remote_oid..$local_oid"
+ fi
+
+ # Check for WIP commit
+ commit=$(git rev-list -n 1 --grep '^WIP' "$range")
+ if test -n "$commit"
+ then
+ echo >&2 "Found WIP commit in $local_ref, not pushing"
+ exit 1
+ fi
+ fi
+done
+
+exit 0
diff --git a/tests/resources/testrepo_256.git/hooks/pre-rebase.sample b/tests/resources/testrepo_256.git/hooks/pre-rebase.sample
new file mode 100755
index 0000000..6cbef5c
--- /dev/null
+++ b/tests/resources/testrepo_256.git/hooks/pre-rebase.sample
@@ -0,0 +1,169 @@
+#!/bin/sh
+#
+# Copyright (c) 2006, 2008 Junio C Hamano
+#
+# The "pre-rebase" hook is run just before "git rebase" starts doing
+# its job, and can prevent the command from running by exiting with
+# non-zero status.
+#
+# The hook is called with the following parameters:
+#
+# $1 -- the upstream the series was forked from.
+# $2 -- the branch being rebased (or empty when rebasing the current branch).
+#
+# This sample shows how to prevent topic branches that are already
+# merged to 'next' branch from getting rebased, because allowing it
+# would result in rebasing already published history.
+
+publish=next
+basebranch="$1"
+if test "$#" = 2
+then
+ topic="refs/heads/$2"
+else
+ topic=`git symbolic-ref HEAD` ||
+ exit 0 ;# we do not interrupt rebasing detached HEAD
+fi
+
+case "$topic" in
+refs/heads/??/*)
+ ;;
+*)
+ exit 0 ;# we do not interrupt others.
+ ;;
+esac
+
+# Now we are dealing with a topic branch being rebased
+# on top of master. Is it OK to rebase it?
+
+# Does the topic really exist?
+git show-ref -q "$topic" || {
+ echo >&2 "No such branch $topic"
+ exit 1
+}
+
+# Is topic fully merged to master?
+not_in_master=`git rev-list --pretty=oneline ^master "$topic"`
+if test -z "$not_in_master"
+then
+ echo >&2 "$topic is fully merged to master; better remove it."
+ exit 1 ;# we could allow it, but there is no point.
+fi
+
+# Is topic ever merged to next? If so you should not be rebasing it.
+only_next_1=`git rev-list ^master "^$topic" ${publish} | sort`
+only_next_2=`git rev-list ^master ${publish} | sort`
+if test "$only_next_1" = "$only_next_2"
+then
+ not_in_topic=`git rev-list "^$topic" master`
+ if test -z "$not_in_topic"
+ then
+ echo >&2 "$topic is already up to date with master"
+ exit 1 ;# we could allow it, but there is no point.
+ else
+ exit 0
+ fi
+else
+ not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"`
+ /usr/bin/perl -e '
+ my $topic = $ARGV[0];
+ my $msg = "* $topic has commits already merged to public branch:\n";
+ my (%not_in_next) = map {
+ /^([0-9a-f]+) /;
+ ($1 => 1);
+ } split(/\n/, $ARGV[1]);
+ for my $elem (map {
+ /^([0-9a-f]+) (.*)$/;
+ [$1 => $2];
+ } split(/\n/, $ARGV[2])) {
+ if (!exists $not_in_next{$elem->[0]}) {
+ if ($msg) {
+ print STDERR $msg;
+ undef $msg;
+ }
+ print STDERR " $elem->[1]\n";
+ }
+ }
+ ' "$topic" "$not_in_next" "$not_in_master"
+ exit 1
+fi
+
+<<\DOC_END
+
+This sample hook safeguards topic branches that have been
+published from being rewound.
+
+The workflow assumed here is:
+
+ * Once a topic branch forks from "master", "master" is never
+ merged into it again (either directly or indirectly).
+
+ * Once a topic branch is fully cooked and merged into "master",
+ it is deleted. If you need to build on top of it to correct
+ earlier mistakes, a new topic branch is created by forking at
+ the tip of the "master". This is not strictly necessary, but
+ it makes it easier to keep your history simple.
+
+ * Whenever you need to test or publish your changes to topic
+ branches, merge them into "next" branch.
+
+The script, being an example, hardcodes the publish branch name
+to be "next", but it is trivial to make it configurable via
+$GIT_DIR/config mechanism.
+
+With this workflow, you would want to know:
+
+(1) ... if a topic branch has ever been merged to "next". Young
+ topic branches can have stupid mistakes you would rather
+ clean up before publishing, and things that have not been
+ merged into other branches can be easily rebased without
+ affecting other people. But once it is published, you would
+ not want to rewind it.
+
+(2) ... if a topic branch has been fully merged to "master".
+ Then you can delete it. More importantly, you should not
+ build on top of it -- other people may already want to
+ change things related to the topic as patches against your
+ "master", so if you need further changes, it is better to
+ fork the topic (perhaps with the same name) afresh from the
+ tip of "master".
+
+Let's look at this example:
+
+ o---o---o---o---o---o---o---o---o---o "next"
+ / / / /
+ / a---a---b A / /
+ / / / /
+ / / c---c---c---c B /
+ / / / \ /
+ / / / b---b C \ /
+ / / / / \ /
+ ---o---o---o---o---o---o---o---o---o---o---o "master"
+
+
+A, B and C are topic branches.
+
+ * A has one fix since it was merged up to "next".
+
+ * B has finished. It has been fully merged up to "master" and "next",
+ and is ready to be deleted.
+
+ * C has not merged to "next" at all.
+
+We would want to allow C to be rebased, refuse A, and encourage
+B to be deleted.
+
+To compute (1):
+
+ git rev-list ^master ^topic next
+ git rev-list ^master next
+
+ if these match, topic has not merged in next at all.
+
+To compute (2):
+
+ git rev-list master..topic
+
+ if this is empty, it is fully merged to "master".
+
+DOC_END
diff --git a/tests/resources/testrepo_256.git/hooks/pre-receive.sample b/tests/resources/testrepo_256.git/hooks/pre-receive.sample
new file mode 100755
index 0000000..a1fd29e
--- /dev/null
+++ b/tests/resources/testrepo_256.git/hooks/pre-receive.sample
@@ -0,0 +1,24 @@
+#!/bin/sh
+#
+# An example hook script to make use of push options.
+# The example simply echoes all push options that start with 'echoback='
+# and rejects all pushes when the "reject" push option is used.
+#
+# To enable this hook, rename this file to "pre-receive".
+
+if test -n "$GIT_PUSH_OPTION_COUNT"
+then
+ i=0
+ while test "$i" -lt "$GIT_PUSH_OPTION_COUNT"
+ do
+ eval "value=\$GIT_PUSH_OPTION_$i"
+ case "$value" in
+ echoback=*)
+ echo "echo from the pre-receive-hook: ${value#*=}" >&2
+ ;;
+ reject)
+ exit 1
+ esac
+ i=$((i + 1))
+ done
+fi
diff --git a/tests/resources/testrepo_256.git/hooks/prepare-commit-msg.sample b/tests/resources/testrepo_256.git/hooks/prepare-commit-msg.sample
new file mode 100755
index 0000000..10fa14c
--- /dev/null
+++ b/tests/resources/testrepo_256.git/hooks/prepare-commit-msg.sample
@@ -0,0 +1,42 @@
+#!/bin/sh
+#
+# An example hook script to prepare the commit log message.
+# Called by "git commit" with the name of the file that has the
+# commit message, followed by the description of the commit
+# message's source. The hook's purpose is to edit the commit
+# message file. If the hook fails with a non-zero status,
+# the commit is aborted.
+#
+# To enable this hook, rename this file to "prepare-commit-msg".
+
+# This hook includes three examples. The first one removes the
+# "# Please enter the commit message..." help message.
+#
+# The second includes the output of "git diff --name-status -r"
+# into the message, just before the "git status" output. It is
+# commented because it doesn't cope with --amend or with squashed
+# commits.
+#
+# The third example adds a Signed-off-by line to the message, that can
+# still be edited. This is rarely a good idea.
+
+COMMIT_MSG_FILE=$1
+COMMIT_SOURCE=$2
+SHA1=$3
+
+/usr/bin/perl -i.bak -ne 'print unless(m/^. Please enter the commit message/..m/^#$/)' "$COMMIT_MSG_FILE"
+
+# case "$COMMIT_SOURCE,$SHA1" in
+# ,|template,)
+# /usr/bin/perl -i.bak -pe '
+# print "\n" . `git diff --cached --name-status -r`
+# if /^#/ && $first++ == 0' "$COMMIT_MSG_FILE" ;;
+# *) ;;
+# esac
+
+# SOB=$(git var GIT_COMMITTER_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
+# git interpret-trailers --in-place --trailer "$SOB" "$COMMIT_MSG_FILE"
+# if test -z "$COMMIT_SOURCE"
+# then
+# /usr/bin/perl -i.bak -pe 'print "\n" if !$first_line++' "$COMMIT_MSG_FILE"
+# fi
diff --git a/tests/resources/testrepo_256.git/hooks/push-to-checkout.sample b/tests/resources/testrepo_256.git/hooks/push-to-checkout.sample
new file mode 100755
index 0000000..af5a0c0
--- /dev/null
+++ b/tests/resources/testrepo_256.git/hooks/push-to-checkout.sample
@@ -0,0 +1,78 @@
+#!/bin/sh
+
+# An example hook script to update a checked-out tree on a git push.
+#
+# This hook is invoked by git-receive-pack(1) when it reacts to git
+# push and updates reference(s) in its repository, and when the push
+# tries to update the branch that is currently checked out and the
+# receive.denyCurrentBranch configuration variable is set to
+# updateInstead.
+#
+# By default, such a push is refused if the working tree and the index
+# of the remote repository has any difference from the currently
+# checked out commit; when both the working tree and the index match
+# the current commit, they are updated to match the newly pushed tip
+# of the branch. This hook is to be used to override the default
+# behaviour; however the code below reimplements the default behaviour
+# as a starting point for convenient modification.
+#
+# The hook receives the commit with which the tip of the current
+# branch is going to be updated:
+commit=$1
+
+# It can exit with a non-zero status to refuse the push (when it does
+# so, it must not modify the index or the working tree).
+die () {
+ echo >&2 "$*"
+ exit 1
+}
+
+# Or it can make any necessary changes to the working tree and to the
+# index to bring them to the desired state when the tip of the current
+# branch is updated to the new commit, and exit with a zero status.
+#
+# For example, the hook can simply run git read-tree -u -m HEAD "$1"
+# in order to emulate git fetch that is run in the reverse direction
+# with git push, as the two-tree form of git read-tree -u -m is
+# essentially the same as git switch or git checkout that switches
+# branches while keeping the local changes in the working tree that do
+# not interfere with the difference between the branches.
+
+# The below is a more-or-less exact translation to shell of the C code
+# for the default behaviour for git's push-to-checkout hook defined in
+# the push_to_deploy() function in builtin/receive-pack.c.
+#
+# Note that the hook will be executed from the repository directory,
+# not from the working tree, so if you want to perform operations on
+# the working tree, you will have to adapt your code accordingly, e.g.
+# by adding "cd .." or using relative paths.
+
+if ! git update-index -q --ignore-submodules --refresh
+then
+ die "Up-to-date check failed"
+fi
+
+if ! git diff-files --quiet --ignore-submodules --
+then
+ die "Working directory has unstaged changes"
+fi
+
+# This is a rough translation of:
+#
+# head_has_history() ? "HEAD" : EMPTY_TREE_SHA1_HEX
+if git cat-file -e HEAD 2>/dev/null
+then
+ head=HEAD
+else
+ head=$(git hash-object -t tree --stdin </dev/null)
+fi
+
+if ! git diff-index --quiet --cached --ignore-submodules $head --
+then
+ die "Working directory has staged changes"
+fi
+
+if ! git read-tree -u -m "$commit"
+then
+ die "Could not update working tree to new HEAD"
+fi
diff --git a/tests/resources/testrepo_256.git/hooks/update.sample b/tests/resources/testrepo_256.git/hooks/update.sample
new file mode 100755
index 0000000..c4d426b
--- /dev/null
+++ b/tests/resources/testrepo_256.git/hooks/update.sample
@@ -0,0 +1,128 @@
+#!/bin/sh
+#
+# An example hook script to block unannotated tags from entering.
+# Called by "git receive-pack" with arguments: refname sha1-old sha1-new
+#
+# To enable this hook, rename this file to "update".
+#
+# Config
+# ------
+# hooks.allowunannotated
+# This boolean sets whether unannotated tags will be allowed into the
+# repository. By default they won't be.
+# hooks.allowdeletetag
+# This boolean sets whether deleting tags will be allowed in the
+# repository. By default they won't be.
+# hooks.allowmodifytag
+# This boolean sets whether a tag may be modified after creation. By default
+# it won't be.
+# hooks.allowdeletebranch
+# This boolean sets whether deleting branches will be allowed in the
+# repository. By default they won't be.
+# hooks.denycreatebranch
+# This boolean sets whether remotely creating branches will be denied
+# in the repository. By default this is allowed.
+#
+
+# --- Command line
+refname="$1"
+oldrev="$2"
+newrev="$3"
+
+# --- Safety check
+if [ -z "$GIT_DIR" ]; then
+ echo "Don't run this script from the command line." >&2
+ echo " (if you want, you could supply GIT_DIR then run" >&2
+ echo " $0 <ref> <oldrev> <newrev>)" >&2
+ exit 1
+fi
+
+if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
+ echo "usage: $0 <ref> <oldrev> <newrev>" >&2
+ exit 1
+fi
+
+# --- Config
+allowunannotated=$(git config --type=bool hooks.allowunannotated)
+allowdeletebranch=$(git config --type=bool hooks.allowdeletebranch)
+denycreatebranch=$(git config --type=bool hooks.denycreatebranch)
+allowdeletetag=$(git config --type=bool hooks.allowdeletetag)
+allowmodifytag=$(git config --type=bool hooks.allowmodifytag)
+
+# check for no description
+projectdesc=$(sed -e '1q' "$GIT_DIR/description")
+case "$projectdesc" in
+"Unnamed repository"* | "")
+ echo "*** Project description file hasn't been set" >&2
+ exit 1
+ ;;
+esac
+
+# --- Check types
+# if $newrev is 0000...0000, it's a commit to delete a ref.
+zero=$(git hash-object --stdin </dev/null | tr '[0-9a-f]' '0')
+if [ "$newrev" = "$zero" ]; then
+ newrev_type=delete
+else
+ newrev_type=$(git cat-file -t $newrev)
+fi
+
+case "$refname","$newrev_type" in
+ refs/tags/*,commit)
+ # un-annotated tag
+ short_refname=${refname##refs/tags/}
+ if [ "$allowunannotated" != "true" ]; then
+ echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2
+ echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2
+ exit 1
+ fi
+ ;;
+ refs/tags/*,delete)
+ # delete tag
+ if [ "$allowdeletetag" != "true" ]; then
+ echo "*** Deleting a tag is not allowed in this repository" >&2
+ exit 1
+ fi
+ ;;
+ refs/tags/*,tag)
+ # annotated tag
+ if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1
+ then
+ echo "*** Tag '$refname' already exists." >&2
+ echo "*** Modifying a tag is not allowed in this repository." >&2
+ exit 1
+ fi
+ ;;
+ refs/heads/*,commit)
+ # branch
+ if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then
+ echo "*** Creating a branch is not allowed in this repository" >&2
+ exit 1
+ fi
+ ;;
+ refs/heads/*,delete)
+ # delete branch
+ if [ "$allowdeletebranch" != "true" ]; then
+ echo "*** Deleting a branch is not allowed in this repository" >&2
+ exit 1
+ fi
+ ;;
+ refs/remotes/*,commit)
+ # tracking branch
+ ;;
+ refs/remotes/*,delete)
+ # delete tracking branch
+ if [ "$allowdeletebranch" != "true" ]; then
+ echo "*** Deleting a tracking branch is not allowed in this repository" >&2
+ exit 1
+ fi
+ ;;
+ *)
+ # Anything else (is there anything else?)
+ echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2
+ exit 1
+ ;;
+esac
+
+# --- Finished
+exit 0
diff --git a/tests/resources/testrepo_256.git/index b/tests/resources/testrepo_256.git/index
new file mode 100644
index 0000000..838e738
--- /dev/null
+++ b/tests/resources/testrepo_256.git/index
Binary files differ
diff --git a/tests/resources/testrepo_256.git/info/exclude b/tests/resources/testrepo_256.git/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/testrepo_256.git/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/testrepo_256.git/logs/HEAD b/tests/resources/testrepo_256.git/logs/HEAD
new file mode 100644
index 0000000..59a4bb5
--- /dev/null
+++ b/tests/resources/testrepo_256.git/logs/HEAD
@@ -0,0 +1,7 @@
+0000000000000000000000000000000000000000000000000000000000000000 1b4b74772bd83ff28bf44cda9be93f4afc2279623bb5b36c9194a660b7623c24 Ben Straub <bstraub@github.com> 1335806563 -0700 clone: from /Users/ben/src/libgit2/tests/resources/testrepo.git
+1b4b74772bd83ff28bf44cda9be93f4afc2279623bb5b36c9194a660b7623c24 decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f Ben Straub <bstraub@github.com> 1335806603 -0900 commit:
+decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f cb282e7c15fd8aeb2265cd621f5a228cb33dc84192980ca426cf9ab2a48cb9f0 Ben Straub <bstraub@github.com> 1335806604 -0900 checkout: moving from master to 5b5b025
+cb282e7c15fd8aeb2265cd621f5a228cb33dc84192980ca426cf9ab2a48cb9f0 decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f Ben Straub <bstraub@github.com> 1335806605 -0900 checkout: moving from 5b5b025 to master
+decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f 901505c3355518bee35475c5d3f23bac1dded688b2bd314cc32b7f157e100724 Ben Straub <bstraub@github.com> 1335806608 -0900 checkout: moving from master to br2
+901505c3355518bee35475c5d3f23bac1dded688b2bd314cc32b7f157e100724 a4813ef6708e6011e8187224297e83e4a285f58bf5eabb1db270351388603c95 Ben Straub <bstraub@github.com> 1335806617 -0900 commit: checking in
+a4813ef6708e6011e8187224297e83e4a285f58bf5eabb1db270351388603c95 decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f Ben Straub <bstraub@github.com> 1335806621 -0900 checkout: moving from br2 to master
diff --git a/tests/resources/testrepo_256.git/logs/refs/heads/br2 b/tests/resources/testrepo_256.git/logs/refs/heads/br2
new file mode 100644
index 0000000..e8ada81
--- /dev/null
+++ b/tests/resources/testrepo_256.git/logs/refs/heads/br2
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000000000000000000000000000 901505c3355518bee35475c5d3f23bac1dded688b2bd314cc32b7f157e100724 Ben Straub <bstraub@github.com> 1335806608 -0700 branch: Created from refs/remotes/origin/br2
+a4813ef6708e6011e8187224297e83e4a285f58bf5eabb1db270351388603c95 a4813ef6708e6011e8187224297e83e4a285f58bf5eabb1db270351388603c95 Ben Straub <bstraub@github.com> 1335806617 -0700 commit: checking in
diff --git a/tests/resources/testrepo_256.git/logs/refs/heads/master b/tests/resources/testrepo_256.git/logs/refs/heads/master
new file mode 100644
index 0000000..4b4c53f
--- /dev/null
+++ b/tests/resources/testrepo_256.git/logs/refs/heads/master
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000000000000000000000000000 1b4b74772bd83ff28bf44cda9be93f4afc2279623bb5b36c9194a660b7623c24 Ben Straub <bstraub@github.com> 1335806563 -0800 clone: from /Users/ben/src/libgit2/tests/resources/testrepo.git
+1b4b74772bd83ff28bf44cda9be93f4afc2279623bb5b36c9194a660b7623c24 decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f Ben Straub <bstraub@github.com> 1335806603 -0800 commit: checking in
diff --git a/tests/resources/testrepo_256.git/logs/refs/heads/not-good b/tests/resources/testrepo_256.git/logs/refs/heads/not-good
new file mode 100644
index 0000000..6bac45b
--- /dev/null
+++ b/tests/resources/testrepo_256.git/logs/refs/heads/not-good
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000000000000000000000000000 decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f Ben Straub <bstraub@github.com> 1336761944 -0700 branch: Created from master
diff --git a/tests/resources/testrepo_256.git/logs/refs/heads/with-empty-log b/tests/resources/testrepo_256.git/logs/refs/heads/with-empty-log
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/testrepo_256.git/logs/refs/heads/with-empty-log
diff --git a/tests/resources/testrepo_256.git/logs/refs/remotes/origin/HEAD b/tests/resources/testrepo_256.git/logs/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..c1c020c
--- /dev/null
+++ b/tests/resources/testrepo_256.git/logs/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000000000000000000000000000 1b4b74772bd83ff28bf44cda9be93f4afc2279623bb5b36c9194a660b7623c24 Ben Straub <bstraub@github.com> 1335806563 -0700 clone: from /Users/ben/src/libgit2/tests/resources/testrepo.git
diff --git a/tests/resources/testrepo_256.git/logs/refs/remotes/test/master b/tests/resources/testrepo_256.git/logs/refs/remotes/test/master
new file mode 100644
index 0000000..42bdaa2
--- /dev/null
+++ b/tests/resources/testrepo_256.git/logs/refs/remotes/test/master
@@ -0,0 +1,2 @@
+0000000000000000000000000000000000000000000000000000000000000000 decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f Ben Straub <bstraub@github.com> 1335806565 -0800 update by push
+decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f 1b4b74772bd83ff28bf44cda9be93f4afc2279623bb5b36c9194a660b7623c24 Ben Straub <bstraub@github.com> 1335806688 -0800 update by push
diff --git a/tests/resources/testrepo_256.git/object-idx b/tests/resources/testrepo_256.git/object-idx
new file mode 100644
index 0000000..c4ce7c4
--- /dev/null
+++ b/tests/resources/testrepo_256.git/object-idx
@@ -0,0 +1,1700 @@
+0007e4a07f4c922a647ac9930790e6640c61b8555d0e0195d164bce8685f2390 05bf6713e26fca9af0b116adf6fa1a0e4a3a957c
+002bf814b089dd7941f9cdd449e8c3a52815e8f8f5fdc918c4ff3fe4a2a88119 3e590fb2221ea3c117b7347956843d24132ac22b
+00404e6179d86039bbc01a925bdc34ccdab778bd1d824f5562aaa319c6c8f045 b91e763008b10db366442469339f90a2b8400d0a
+0052fc5cfde91bf84d96ea764b24ccd20ce9d98a354bf69a6bd89c2ad38f977b 00c5bdc46d8f91dda9f64086347d60c18d1a1f8f
+0092a5b445971910ad22ef5636caf11f6926695fa8eeaf34a4fb7abecc4a0e44 404e1182dc52d4caf284d0f39567cde79649b651
+00b1f32aab597e7bff93f9790bf580b030f4ed71d620a7178b2d9a2bad5b1f86 27c3c27807e7fd3c053e0bffc6da68051f343ba2
+0113a79463a552823f5865aeaa641137ad8741ac85ef923014a6a7c0c36aa1a3 73630ef121eb0e4a601df784dff973b321813f02
+01177bbe7923049c27d557ed912d7440e2925bda5a37492744a83c1ff2fdda1f 5f10ae02d45a1f030cf0255ff813d62fcf7e5575
+0118010feb81fe41b9df646d13866742a9070b56fd0ba9ab8dff828fc36c1f78 763d71aadf09a7951596c9746c024e7eece7c7af
+01731a6f97a36dc0299d3bd87b16f0679d675dc8bfc484dfc1173e3dea5914de 5f340851194fa963878fa70e6ef4b19bd2c74ee2
+01a1e78e1078db69cb6197fbc564851bb5861e6938638169c9111bfd08b608b0 11d973c9507b5768f2c312ad9ffa35f0baae8793
+01d396efb693b1b691ada667e623864b802bf4b57bd4fade0fb6d75688a9b99b d5d4b8cd7faeb7749015896a303f1d5331638d00
+0210a4981f476c0d98929dded65b4dc0ff653f0876c45ff08a6e7a1d647a4b2a d39941396f81224f394f1d59c31f64e2d0424fab
+0216b94271c2795357116d8f9ae45facd9bb1658c5a42ef38ea54e57833ffa3f bdf2334251d8e65a999b96fc99547ba99e2f0205
+0244a04d70a80f0e5baaefe7ac1cd73dcb00692f982e1e3af824252a5d48fb3d 182f2b39391ff4db4008554e97753fdba4e1b57e
+024e2a82f18d1034c0954bc91c96af19c98029aaab3f6aa8fde5ff1f5e70afe6 f57c220bb0949ba4d6da1967bbbbc7544c18c0d0
+027d76e06b173a74af36c5d1a10761c830b59ed8dc03acc58d1ae008b176160e 1b24fa1fb836589fef658723c5a68cda9a2647dc
+0285241dd82eda29ba38b91c04f6385fae1b1b88990f8d0c86a88b5e6401a495 7c25137751c32c3971a57cc66c564dfc321328e1
+028e02901e7de25b3a0079c438da76f03df181cba76d986d3445be0675af21c2 f206b37de925edb8e36dce24fddb125e493d2cc9
+02a6da582f284e708f78ab9360628740395d9003bae9cc9cd5dc47e042a95e58 39e01811cd58f4ea0f5f5612d24b3115ad0736f4
+02c3ca38a3cadcb769643ed692998c0f3984429600365a39e143e5984c33c871 a42df0a3df3e2524c532f9f75aea0ac69c3984c3
+02df938cfb169b0b6ba0dd16acdd727ea9364f7d48c55afed2f7dd889804065b b235959d89084af8d3544fbdf675e47944f86524
+02dfdb9fccff7beaad58de3ae93694e3f957cefa10945d4652b3b1af4ecc1e41 a84d1309159aa125367376b01ec4c76847a4f514
+03063757ec40cebe0042c667ddb85bc4404925390abe9ce38fbf89761b9929e7 820e02ccef8c90470ec7f351464f9212f9dc0651
+033de70fa8afc174d134a3dfda7a8477b550dc12019904d418d9d0bb77d75c11 336ab27f4ead4627cc51ed162c31bc01f13b793e
+03653094a4e4d102cb4ea4d29ca5d17dcd99a0d6997d0a6ea590b774333a4afa 03a9cba35dd9060f15a7309b438afaa246e80d5c
+037bb440672e051dfb647627dd961209d4d5f65a3134aa561e9a22dea0c2ddff 6e9fb9dad8184cde865b1b062163dc209042f88d
+039df023a072d4c4dedc8d69bdfd04955c9661ad0a6032d59417fae3d9a6e66f 93a16f146306fea6df405db1e6e065e52994ee5f
+03c29c5b092a27c7d22553dabbf94cdd05ea9892de6a017bb7ebad8cd381a72e 8529bb1ac5647c96f1af4e933f34de2e9d8921fb
+041ee569b4648c25a375895bf0e615b1e967164142dde5e3b74f42aebaa8b1db 15bffce9f7ce45f3b17d9357931667485a890455
+041f178780c21ca66c1a644e406bcc631e3374d3d3ff12963f27b7a2e4dcb98f f2ee83a040d7c1c26b85729558e7afc121d35651
+044fbdc5fa0794e3fe81c4cb6735792a7e2224ef108533d9e4d64dcc2f79f727 4738f4f3ea24fd89b4b4b27aa115eb6eaff9b397
+04504468bbc5da740dff1ee1bc21644ceac33a5a39e186624ac9c1700a005802 34a1dba52a7a0f2309c3bcca5ebd76dc8d2627c5
+046ec406f62cf062f45bcaa8881c901563595ad5e4e3eda3915979c8e8ac8786 c7417a02d56346deb435359fdba3de19c0be2f62
+048a56b75d20462e59f0b0c4f8c326013116a117654226fbd086eafc417b227b 894a57d31fdc8326a97814ad2403a45e7c8b6371
+04c49256444a1cca25ef7a6d0f6fb13db62c3a7d887c2b3bd0daf02b36665923 0e115c5be6bf252aa8cba76141eefb67f1541a06
+04cbb5dec4810b725cedad9ed47b8e070ebf7912e7f8a38b1524c7a5d18d6711 62ab0af99d2955901133fff37560bff1962e9b14
+050224b1d07966f578ce168d709148391b41eb6fd4b08e51fec8a549654a6780 c36f4cf1e38ec1bb9d9ad146ed572b89ecfc9f18
+05257051a1b3dc12041d32d8392ef288a18aa225b138ee92058a76f6453ffeed b6d7472f568134d9662dbe0753e692b7d57ed993
+052e483d293dd8832a959201ae52fbe0a3578a53dc964260c1ccd00563109d05 e8b17444a9abd2c9be360b44a8f8e13a4a2fdf27
+053bb083785b3e6287242a1e9b7cdebe356a25574b5f063538bfcf01d646f0d8 f15336fafccff5f3a05b4f4d1527254f25a364ef
+0541301a56755c364219db68258da2e70e9146e9cad185e78b188d560dfbfc38 45504b7b9013d55e193001c8336f3db0ec2a81ed
+058ec10e754833d209791fc619b8651f9f8738eceb4af6c9c4e096656b4b5d0c 639753339cadc1e57823094276afd3b57bdd6b62
+05a40f056205025b75b15337629dd90b2abe57a2622ca7968db2ffd4fa1198a7 77c0fe987fc4a3c24843c77615c3b6d2f53e27ab
+05a580d164ba0fb859466759e0db1d63795eee186a48ad7b6684337933ed16b7 448a7a7cf218a5988f12274945613e86e1c2de11
+05f7b70a01b0ade8afa5a5fcd19f12cc38faf337d10ec03ef4363d1a86f63750 e7b4ad382349ff96dd8199000580b9b1e2042eb0
+060c18716f5e2d8c74a30960f002096e43a4a8b0344e3df4f81222d0b7d73b89 7322d43c8aecf06b61944b8fd63d60758680a957
+061079b6d522c910cfe03f6549a63ab4ab43e212aaaf7dce90bdecd4c4f616d5 4b8e8b32c1ea1bee8c8c01795e46aa97006bb065
+06240c7992e7306c80b1d058777029034fc2a9028931dd93c5611bd56332b99a d5f668c547ff80ee9c5ec24613a2e552cb531c59
+062835d273525fdde70fef08a703cdff6fba048a2e1ef3e49a4a6f60e7e5c7e4 3e00d3bc4d8a36c974bbe91608da3b6b1af69caa
+063517f949bd44147862a7eb7210fc0baccdf6c5c02c18c51459c7f317d803ad 2e2a9ece460a44d8f30f3df9af1e3686a8e609ec
+0636b4292bfdd7274a977cb6f8b2ded8f315ea1bcd8dbedfca37964c2ed3d085 236e7579fed7763be77209efb8708960982f3cb3
+0642ed9f6735aa25854c90e266798a4843922fe3736a04252809e824bd4093d2 5db59540265ec48b82f604cd251907ca5d79b6fe
+06665948a581e547ac1dc883b35bab54682fc311f7a87d5563a76501ecba55fc 4f0adcd0e61eabe06fe32be66b16559537124b7a
+0676bc9e224b7a050b3e88cf7dd8f7fb148f521594a6d4132f0400bba6b1da83 8e9d2f904854c0dab03de84dcef103c1904b2115
+0709e20677dfeeb21242f9f7fa277ec464fbbfb56d303f8006c7e60c8e2be458 b3a2f90e54bcb32a88d13539e5672993c5037638
+0722571dc44f703576daf0dc43370557e6e9d83784b9454c98a7246ea3e0ab91 215da649e1c68079fb03f4f9bc0f196cca9855c8
+07314e5bfe0b98c1e061219664284ea9567e1d739530b3be8e3a6ef9527d59b9 9d3c39dfd8210faa558b0cc6fb427b680db343e7
+074acdefc8d17e7337df33b74c1ec5349ef0a0b18a7ec5fed41a2cf420db4f5e d8310141b69fa36657a620a045acad4e4ce0920c
+074e9049747d5816aa03be67451eb42c425d1e328730b4d999dee78c4d2579f5 caa7d9e0c8f8671cf8b8c9c16eb18d31ca4802d8
+07571960f3f64bbb4d31f24f79abe05daae0498f6b4b77cca3d8e8ceccbd75d3 ad7cf03cce61aba463b9a687e388260395c8e727
+075e288bb4b170c21e5a569c3cafec80736aead134829acee0f9d4ba5b966a24 e9f5e87781e88aa3afa7679783c204604479f9e9
+076202d7d8fb1d193628801f76208f3a4967fa031a7f94d9236a0fbbe6f022f5 221c73c758cdfd3e7f7dc45b10576d1ced8232b5
+0763eb50fe80aa21eda853b7da406425b172b1d0ec7bdb41369a15868fddfb6f 02e741c6fca96b6d2bb89d064c3d1e31625d474c
+076800113a06dabe75d578f2ce6018fc23027a83dc74cfd66f69f3c50f037ba0 270eb8c6bfbcece0f3abdf20f16f5a2c0b0e65e3
+078baf54914fab56842798c90fec863f15f67a22041e8aa54a88c43f059da050 3184a3abdfea231992254929ff4e275898e5bbf6
+07c31b4be5f3ec8f82c7ad6c91cbdf07ae876bb73041903d64fe8bac64bbcb6e 4ea481c61c59ab55169b7cbaae536ad50b49d6f0
+07dcde657a4d8046d02962b4fd67ee5b6ac27f712de66ae22c11d14406ecf092 b39843f3e42c8e062a29b2a64e3c3ce6a16eb72f
+08198b33785f1eaf950aaa9ade1961e1513461f7f799905a6a625946ebf2db8e 1f9a9b4edab6da73b1625fc99e57fd84b88feaf7
+0838ea9dbbfb9167ea97d3b395b06abce5c951386d5549d87b3c4fe1ea8cbfd2 52261e14080afcd9cb0b1b17dc4146f2b6b252bc
+083c2b8b445640d171e7aa667b0b32007006f786711032ebb82931cca69cc15b fd8430bc864cfcd5f10e5590f8a447e01b942bfe
+083cf84e4ec87e8df6bb8cf4b5e95a5bf0e6b97b892fb99286d652362f0f1a8e 2e5faa78b12048d2818bf37986516cd287219f9b
+083dc854acb0f53c854ca89defefdd7c9bc76bd6158a461045057c8febdee51d 2c4b7707f7e1d591ec1765ab7057a03283841c3e
+08896c87dca842ffa2e7f9ca1941fc1b231b4f0cf966c992251fffc01be4eb0c ee5f4b6e2c16c9cf2510fd7ea60afb830a3e89b2
+08af1ef3969b835c4058d3c19fd6f30d0dca883b9553061823f016db50b935be f4153cc66a588da14bfb5e0ce59dcd638e6cad2d
+092184eacb4296d03cd379da1e30b23163a3174e74f5f072b71dad644565d5bd e7bed373b521d9d5e20c3f7a2372a7eae76ae66a
+09ca781675e86449d0910da772dfad9c80525b52452ea837a9820c3bc349296f fb799dfe77c7a1c9e2806c0021d659b5588d9105
+09f22926cfddb415baa1a22193d765facfea02edfb140702fae90714dcd402c2 621dbe6381d0b5239b6ca2f206fee56877326d5c
+0a13ff7efebb553b2dc091008c6b27ae8fd8da265a0fc672ee0349533574a1d6 892bb2941b4c993ee188ba1d2da6c305dd5f6ff4
+0a280fa035c75607e5399cebe0e2e4ab44de35bd4d5c46a1accb47c8ea2bbee4 417f0abc9b41f01520df863567b25efd03bd281c
+0a45b25fa6ba0d67638d8230be426a7a61baf23ea734ceea38312a380dd42181 a78bbeca7f7efad6ff1652d4ab03d4fbf8bb4c48
+0a58869d818eb06ab4b6601c466755db3a7e0d310e87f9f5f340f4ff8f3d84af 55eee3fca2f24bdaef26ea42ce12b98682b8988e
+0a8400e1a7dfc68b38c9e82bb4d64f94a20f75ba0f026eb14d29ec978f66dd3a 41fb16c71644d9acab037f4a43d23627545d6d86
+0a85ae1fd17b2a87e83699ba3dac6e0ebb2efd62948ea70dcc127a250323b066 6dc05568057b37706c8453b3e24645a351fef6a3
+0a8c0add81b065b97451f7b47a9935f0e240251c5f90a89ced885cb7d4efc2ba b0a3077f9ef6e093f8d9869bdb0c07095bd722cb
+0abaf90248f239f967d2361480e8111858721d240de6e928a9537af9a495062b b6d031157980136043dfbca0e49514a19c973759
+0ad45dbb04ca242774c42f4d7283094c9d0018db25224ec4e2b1512bd3e21e5a 45fb9738926af78da7280cb84ea022e8f7da98c9
+0aea420f9ece0be17a656910e5a64f71a591f2bb4a8938090680925a262fb511 0bf364b355f1dff3c70116f8629774848dadb9db
+0aefd477d9e5b3f8d708a3cf6d78be6b670dfa2e2ec41244634f3b8f115d8e04 775e4b4c1296e9e3104f2a36ca9cf9356a130959
+0af326907c9f9a1c91cdfcba6e241582f97ade4535b16a1863b03938ca983104 29a210a471a1964a68f7f1f15214c626c58c1701
+0b1a932c7db41cb113d42e06acf29eddd00aaae9e39398328d475754930c6cb8 343a4b52f37094d193a27fde5ff20825d6d1e7c3
+0b2613505624d7fb949258282f13add0019ba5521f3075ad0f61bc1306b6d828 faa612641fdc2590a969e9a8600af696be701f07
+0b41ced8fc49173b868c15cba070c0167cc83eaf7c0bc62a19e2081d1e7f2676 2876a4c21cb5848079e2f2700312e54d4988c76f
+0b6b4c4b598c420ebc1b9e404f53bbac59c8992100a92fb557d679a65e6c7bd5 ee2eb6fafc239de8d88016a1579314908b643b87
+0b99d2b73b57e4d477d096a82661320656f2e61f1d6811f569abb99e2d321b5d 3864bcd2aa1fe77365e8310662c037309d4db56c
+0bb1a5866d49c4f22b173586a2d0f2a3d6a91d8ed4d8f6fc43a624df4afe00d3 1b9191dd8a427addca8309a59decc90267082bae
+0bb5c743db0d4ef0b30982921e78fe42863d48ea34a6a917bf740ac2d677f281 9689311c952655dd9e315aaef81ea7bfc60d7c7a
+0bd7502a0ec038d7740979ad9c9d2198f61b51bcf75b20259aa56e845c62c884 13edd1b7d5996a6b1ac38ca113f6fd751bf77827
+0bf8e96a28828be201f392a2f8d511d4b0d88296db716d64bd9e8268a5cf912a f019bd17a1769103dd82b885d7b46bb9a30bdc29
+0c0a6e5b0322e1f51e3a53c3e302901e9f6acfd0e3f9cf227bd02e61ddefd401 cd2c3cc64b9c003e5049d8a3efe4fed7154e6e3d
+0c4f26749b1dbfbf4487590dea5b6bb49564712cb3f8343434092fd9b2ef0a0f 5ac6648ec382ec2f2c9ef81e6ded0f2fe5819a5c
+0c55511a79e278b18657bfc60a0893d002d266c05809f54aa49337a0973a1eb9 6013ffa66ef5d856b2c4c72c44b1a63a7ccc2b57
+0c889bfb5e530a62c89b8c18ba11f4bf3417582a28c70b51f075ae74ab1c9a6c 5152c60f1d88042a52d66344b1d628d2465fef6c
+0cffbec3386e267c6d32c541b5eb3e0492851a5b35b05a506dc60b8de0410b46 7828fb1437298bbf3f31da65581775665eece9cd
+0d15b51f164a1237b5a6ea2f35f32d3f953c4476c1ca7edc91890cd5fb5d8541 18eb2b8adddc7eb7e2a0165e8d759ec5172a65a8
+0d737e3eab91d43f8080513e45fa2d708b3626020ff4b6900b236a613af8cb6e 34ff3378eac2b37e5220195e235a25a21f9b7e25
+0d8b5fe52e725773014a0487616540efea381d2af971ab5a18ab25d33ead1390 9aa923b26f49d08404fd272e0688f57c899d9116
+0da6d514fdcd1a5248f1ae2343991a7b08ee5da3169da9326ade767fe50bf166 74c7fe4f3a657d606a4a004c87336749079c0edf
+0def2b73f560d08d26ce006d956af37c429277d3b492839521d00403060fc189 50d97878f49fd4c950a7a494b8dafb0d4797b57c
+0e1ee44bdd5aeffc3a6601c83744e64a421a38d1c9355cd5aa20a1dd7c55d849 73473718290fe068243e8596fe649f200e8a6f34
+0e265ece40fee110c5a6cd29be1534c268f16e4f3c488bcfb9f3c8d1f46a4791 711223894375fe1186ac5bfffdc48fb1fa1e65cc
+0e278cfd338b1dcc4f15bebb53ea527e756337a337e46d29806ed28c23f5469c f5124bfd46444f572c5faef3d45b084635c106a0
+0e31ba4e455789640544ac02d4e478fcd75d39de130241e0d2bc80db11c7b7f2 8722a77ec384aa0bc87b8f8f6c1c7ab6aeffae83
+0e3e687c74a6be88d4c09233ebe1d1a75748e349f7013284742e200c56821d9a 58f7931837dc08b3f94dce8ffe743afa40b15f0f
+0e4970dd94378fe30b6519edf25dcff7679e395072715c12e6f365b74ef94415 624320d5f878cb1603c31bfd8cf1427fb6474443
+0e49790ea8b918a427928eab3e17f8925efc10e2c1a8c5bdef4c0eb20cac0c77 0e40ca2e7c706ac2f46af7f4f2d4bccb90bfbb5f
+0e51009a15c1599ba55888c464d5d806694702f0593bd59895e4a134026bd34b 9e41833ef25a52288a46441ed9ad03dccd0b16c0
+0e6a1aaa2118384e5ceb8af69725827ff4bd942200a9b831522dddfb7aac8287 763dd65d1e7ca14658649d7c4d1cc1e4bfd2c671
+0e85ff7bdd5c6ef0a1a449461d1d01b6a0bcd3393a994bc5113915d173810071 e17a3f5673e0dc4d9ade34a0a276e6c202506fdf
+0ea35e446e65bc1894a303aaff5ed807151bdbbdc8ab257510abab4e83fbf13b ab42d1514cd6b1007d794885dbcc0e7918e91b1f
+0ec37a6fc3216ad07f16cba63ab57f80dea7e4091484cb25e6d677abca268fb0 9e210cb8bb8d3ad9204d90a584098d43c456a5e8
+0ee0fc56010269854bdcbd4a1a55675b2fc29c4410217004dbf77d3ec948954a b5cadb77fd216aee5dc133012142de6f78d4d47f
+0ef8005c1111d4e19e9636d02032c8c3c295ba6f94598e4e782eb28cff273062 408509eef776dff28a36b63e8785ff04f7cf9846
+0ef9ad163c0b4599adf0444b293ad267012e784ca37dca8d8c286fa36914001e 3a6b70664b4c5bc0ea2e6d3fa8fb3b3b54d724dc
+0f89c2e306e636e3084aa0bfe57938fbcd22981e809b7fc031ff90250befb83b 226eca421e206b16826b86f9aa9706aa31680240
+0f8caf733b3a1530869d8d6faf6d4069acec05cebdc33f15a8effbf0c7daf370 e92aec82dae6f9fc5d96b0ca7ce39d750905d7db
+0fc8f0a12ec6889419fd677c9830b2d3feec2e9abf89415147cf0b3c6d444a35 182859f51a5cdd6ae9a1f75e21eb8d3f95d0a833
+1056c834175ba8749080270f4772ed2aad4ac1b74896a7cd36a75fa8a1aa53af 628c97ce7d9539e1009702b53fc7aab98c7f7759
+10b531c577e3e3fac1dee215ea33ea21202309a23b93352e1a9f09d6e9f45f4b 169d698699b441e9912283936a81aaab8e59c6ec
+10d12b721c027f4041d0a52d5c02da1cadaa1ae0d05051421a36b861c4df210b c960d6a3f97ebd360dc3c9ea00fdb3cd5dc56224
+112e60f7f4409f6427a2bc302108becea27da72e31f92ee3476c31347cda4cb0 d22990a266f309c6eca013259c35ce567a0dfe08
+117e89c749a48a0c10b47e9433f26efd38192395676086af83ea2d48306b9f9f b4164f93a830a383b69daec35b6902f0cfe7ac21
+11bd4567822919bcbbf44a1323a1af5a0fcf9e2c6e3d2ad1329990eb1c0e9f5d 83de31463c02d36138f1d76cc22c18613920cd3b
+11d0463a82345c2512bd704dd00211aefb7d5b8590ca92809122fd09486a9f06 d0a0d63086fae3b0682af7261df21f7d0f7f066d
+11d187fe3c4fde36184f7e171e9b6afd066563b4ed568d9e1e3195b8488be47e 7f8a1d79534d90ed868ba664343b058258ddc64d
+11f2111f69ec85dbbb408d68349c750a9ed94722b094e971f595f8aca5028713 0b19f4634685fdf3c2e58564038e614b8a8a563c
+11f58a21e063f88f8aca15a05a6bb5148056acdc619be09214bba78819727a92 58840c6f7e9973b3700eae294e6f219c34cc6962
+12240f46f98af843abb3e12053abd997a1a5cf7ca50ec460775b6dfec9594346 2c9eb23012b2c0effdf247a9dfdeda4dcc6ad8b5
+122a2103f404b68ff8cce6c6ae82effa1026b8a254212401bf5473d27d70efba e97179b6de293c8f288957dc556239a44fa52e3e
+1258e0cba7f787b1c74e00872bcf43799e5a7c77202685dc0ba60adc20da977b b2dea1ed674386234946b2604df68d71b677d2c1
+127437808b4e14417e5654a8e00dac615d334a14747d78d2ba1bd0df426ddc18 60711175510355240283174b84d16ee85fd599ec
+12a834fdb798b91600e94c6851fc231cfd20f3eb65dcd736f39fecf96bff2788 edc438eedf6854c51e1a0d7954a6849046f5a4f6
+12e4e4e3649c0817fd80bf805c8daf890b40fe6788fd479db758575278bb58a3 91e5038d62abc64e8be483f3f764e51b0496de27
+12e8b48d89fb071c73fb0736f784c6ef0fcc2d4178a1fe17ed6d7a8950e6943b 01316e2444ef01a3a2939d6d891b1c7ee49bd05d
+1331f4c99429b5934be1c4186c592069ee9167e73e65807cf8004ba54a151fc1 e740db6f334253940722dccd42ee45c96ee4f56f
+137343a187f9ed56ddc750ccbdc5f284c1240ca1061854f609b4893cfc0ee035 b24cf42e6753bfe5763fdd7639f9cd8b629907e1
+13c8c9dae9fa63b1ec48b8abb12312fc8df61b9414678f504fa68164a48eef28 31438e245492d85fd6da4d1406eba0fbde8332a4
+13e9b8e6cd38ad58df3b3d9dd3480c06e69ea21f02639404c44c57f376ff2ec9 cb8e90b049ec87e0a999952544839c3c17f88e37
+1402d1979a18fa32944a3e584bc2bf63f08fd50fd16b228690b7c8614923b603 62860787aba2b59b1dd0bb7b9a810fbf3428cf75
+14386136c01f7aa705d82984066e49b9a3380d4f7a7f42dd5cc51511314e9a11 a43ff639453d9a5e374cd655bfb562d2d87b9edd
+14747f3fe457467be5310c526d23808fcc18f5bf29feca812a3b3c74dba53a39 77c39760447694c5e9f5c7d09acbc788560a322d
+1483e39b34bf45eeceada3f43bd76b8f1d8af079128291ee1f0ff9868fff8539 483b17569d2655bd9945ceac8e95c4279b2f2b2a
+14a40b61fbea11fffdac7e51616ec7a7264e88a16a9021b4e252d443794d50d2 a27fb9c96feb433fb28a2713a863a7356d45eed7
+14ab853cff3d477dc79386905229f0854da0bd832125d6e8c3f3186073ec0709 b6d1b7d59f42493a4aa5440ce24070bbc4044f88
+14bd335f9d7188c778d44eba8801fe9bda46b66593291f5b9f7cd5f8888af12f 4a23e2e65ad4e31c4c9db7dc746650bfad082679
+15011ec9c79d290af2e08c865de53631aab299a27fc2a681903b3e3efab7dd47 b5cc24e623d5257f37da86627b8b314609945cde
+1503ed3428050930a44def1fb250fbee95bb021a61f3ebe831502871c7df0035 f78cfc187cfead79cbeafb6e0c30f882a9d7c14f
+15041151380e43cd57db1e6b758b00a2ec32782a3004e2b1ededc5e8bbd2e815 09fbd28ab7ae43f75acdf7c8adc6d148b8e78d57
+151c7527acc5b731199a03a932ae374331e16e5ae29256e98cb652f37669889b 91d973263a55708fa8255867b3202d81ef9c2868
+153c7d90e1734b2c9d1b32035aede6299a2a2eaeec425fabcd86706c8afc6194 faf2dae9a5d206471233bfa8698ecbdfb24785d1
+158217c424b47fba461c300c9f99cc27b50f7122c6156d4264c648197e634fd6 c14aa17cb87c64a3cc0f21e343389bcba1992d32
+15a4a79b6ab927befa6798c2028d37fa6cb4f6fe0bf80fdce6444a2fd9e67eca c72d81b5de8042aaefed06ca1abc1a248bfd0a4b
+15bef585bbfec8410833ebdcb1760b7013d1dc3e4a7085902f199ed303f26098 89217d8f1a70da2916d611c989e6b046cdb94fca
+15f128540ddf555e902e837a9e4bc39d752d70af7de1c91b9b0048c1bdf4ba31 f70dcd0924f1d58e9b68ec47fae83d29696865d7
+15f8faf9eb65aa6ddc66cd5a1519d7fd4ac08cde54d6b25e6fe1698c55661335 0c51be2c8e9c40f947c9527b1c633c78c3d56e0b
+160a14259c471b4d0ef1594494f33126d5763d958ae31bb14e95818399f6e3ee 01e0d82509eadc367a67209ac1150e299447cb25
+1632e799dac6927cdf520ba3cba8987a1258382dedacf72dd5b3316af2fd619f ad2fd2eb9ab0a392524bc5d4a90888d550bcbc0d
+165ec90aa4190bbe12ee415b294fd6d204c64afcf1ca64dd815782872b24ea26 904c0ac12b23548de524adae712241b423d765a3
+167fd44243abde82c97106ac315e0f026b447dcf1016918ee42c0777533e6c0e e7cd3468b748fba2eab5162991ff781e2ca8507b
+169a47e7a7d688f8e6ee34ba57c07f3d1654f0886d7e0be582ec76cb445af1d3 d1aa69929be363f0798f1ff226078c9912415f5f
+16e15f2497ec3aca6b94a96dd1ae3e2c8c5a351112345e16c1013ddb23fe9131 89d3150640c0ed5b386bc8113ac8999b20371ece
+175515c1c0df4fd74f9f4683321b84cedcac3419563a268562f08560ad7684ad 3b8ab0b93539049b0f7c4a09f5dad48a1d89cd49
+1784913c0c472812c39dd12870f8fb69346327bda8383d7c2ef22d611c808513 ea9fa2b55f597931edb375c234b42833321e6ebf
+179496410f66032c03bd2b7e8ddfc9c8c47820fab5615cc04d904989ce800498 3259a6bd5b57fb9c1281bb7ed3167b50f224cb54
+17c8bb815beb9afed2efb9fad7e2fff9ad127e2bbf742e7688d70ced51875c11 4575d8b63290baafbdc8345a297945514542f2e8
+181a0d8c3fae35ec8f21046d043457cfb07975ef6ee20b3b2a2b75ef47e6b54f 017867366494259fa88e71955be05758b17f2568
+185248d640acd5ef59cd18b373ebeebe0c8f5d31e8d0da8a39b69509b82a8b38 3cc1e1683dfe9fa759c81ee61b2871cf95f0a3b8
+188d0f5e289ebe28d3eb0027212ea0b1af1c84ebb0229c452922d277ad4f6b41 086ab3e1892c5375fccb647a4b1e2d9ed55ab750
+18c2e619f1850cba3b69767e9433d37b9eba4616521994568a09e6620fdc32c5 46c7d0de2393b0064ff2b909a34944f30a80d8c3
+18ca8ae1ea1ab24474fefe881b7ad27805bd8942051fa9421c277b0ea0909361 069a41c3acd6dfeba2d5ebd1ce0e568d5ce3ad51
+18cc02db4956ff4c1ac8b49430ac9aa889482ebdf985e8f218415038466e870f 83fc82fc80ee42dc76405745ac74a80f7b7a9fd0
+18d28fe4d7da8143a66562a085a1082caf7abdc6ee0dc13c48a509f82ef9a6b2 72afab8fbae47da683ed24f4a3b5deb01c73ea9e
+18de7aa78fb2acafc30a3bfb0b5620543d2d9c21b8474f0d02ea94ac16b3e414 c1f22c54fb7ae97543f8fe6c6ceff95734f4f9c3
+18de852cc5d52a03ac90027225dd12cad93079ca8e1a2dfb11214ee8dc06deea b447fb5a08337aa08134b854f8d65369f03c37c2
+18f2403ff16f42833737b7746497f5f79c014e1cb33bd639c47d7869cb07873b 480cc70e318d8a80657f36fb55967477050cf3ec
+190a1349522cc11f8682e34acca4ce4e1ea8508dfd77c24cefd461b65cead09e d71aab4f9b04b45ce09bcaa636a9be6231474759
+191782022d7767a9a15579163f106741358f2503fec2f9c4c093a6718c0c764e e5f6ef80021c1a1ce27300c4de4bc720a08ccfe1
+191cea84239bce688afb3e171e9e4b3aaae9d419515dd5629b3c01902210334b 2a7e671b12478a4246a11e8f3db61ac76f241e5e
+19762806906133d4265885d96213490c2b04f17560b8d724f91770913531488e 422ef9df86d2b3647e8d9a0525318b1d33dd9f34
+19ac68c5ed3b0a7a8b9f5a4a7098d002d70e0578f8735105ff622805287dc356 8e8169ce53213d649d52304e3a8f98ea04c35801
+19c0e0233f0ce56698402b2e9bc0c77a02e5089c3e28d03f55c9a3bcd6c1df57 76a8c44727406a76d251102bdee607871887a119
+19cd37eadbdbee580b4e2d650374b1e2517600ef4f45666547f9030a1de25a40 a23bec23d8075a05cd04df2f52fb2fddc8ab4185
+19e5ca97ae44d0f46334d1bbed31bcecc2b753ab4de4413e03c3020a2ca0e68e 8157f9e57bd9de5ab95b89fb9c7192a5668322f4
+19fb1c78b11f0f8bda658d6fa6cc63c0b573c0f6760ee5a9c2df6ce2cde00c5c 09fe9364461cf60dd1c46b0e9545b1e47bb1a297
+1a002e34aa1589da6dec7e55014927ebbfb007d33aa6d93dc1aea53a9711c061 e9c9018f4b898a2be9aa0d053153cb7d8c8af22c
+1a08ad814e0d9ee5f719a39c4efe32a55b5fa1bcfc7fde7cb527b30e2fdcf33a 1b30c5d6f15d06aaf5e4edc55d33d440890416fc
+1a174438f39a3ef0bb9d11bc1532ba677dfd530d2c71429eff281102f8128793 3517bc697600fa61730369f35cd91da86e12d595
+1a372ad827add2ebf07d3508c8636f891bf9fa15d173b7299614d543fab4208f efa3c49e538c7d0724cbd9c32b24bbcde100b3cb
+1a6a20523216002f961c5b6b0e2b9f93fd002adee3fb48c0b038c4219dd46b35 5e78a5bc12d1dcd94751e36dff18badcfd9dc89d
+1aa24b7ebd910f39a676f2ccab8e9a79f14842c20b55ed18e3dc297bdbaca279 70957110ce446c4e250f865760fb3da513cdcc92
+1ab4fa663d22416f45cb1a007d767a58d0abf5255bf86f888393dae637b37c3b d0a44bd6ed0be21b725a96c0891bbc79bc1a540c
+1afe3af49295268534bff09062da89d993de8bf539031ae5b5deed8c74e2ed76 b371c056bb696430f0dfcef0d5e2bf62944ff323
+1b01b7b52d7ef909782114be3168d4c11d9125138c36dbec219de41bcc729c1b 7f7103b840176225a27d40ff95c541ecaf74bb59
+1b0218569c4ddcbca6d3c5793c5e822ec57ae0a1132ba51edb9131b68b361946 a916c9b256fe6e1c75540972d2c6ec07acd46384
+1b1b914f2ebd1179c06130fee875bbdb44fba22bcd879e460c8cd295854120c8 a0571e5b921af9cc8aeaf14688ee22fe9146371f
+1b4b74772bd83ff28bf44cda9be93f4afc2279623bb5b36c9194a660b7623c24 be3563ae3f795b2b4353bcce3a527ad0a4f7f644
+1b8fc08818cc6f44cc340576ad2f114f2b15e4f8bc4d306e86e97f65e2043eaa bd1a70b317032b45e10b9d24515905c86baa071a
+1bd6ab383a4cbd2611367ec21c4742326fbeead3ff912f194af0f853e7b63304 004393eb8ee7f51fc57f25ebfee55193d74b3b07
+1c24b0165ded9e7bd0c8848c667f0b08a8212496303aeddf741d5e0e4da3f81a ac04bdf6e0ee6bb4286343bc70d745146eb1e506
+1c34ba81c57a802a66d3026e14cce495dfd8263ba7a98233c4927a74e441aa3f 08d5d00056a7237bf6c60f85a6e72b7549cf9133
+1c4cfeabeb57c496034724e4649ab5acbff04788b6dbef87c50167e16e91b571 5a408036aa992bffa67a0000ddd163e45be5f457
+1c7bbd4852fde2f3dbb2990d329a20e76ddb3b2b570416e4c6ba5633972f67eb bbd29af5b49251be2a6498bd84b488bb4304ae96
+1cfb8ae71e9e576d1b16b7bd1a62156d0641c6e51f5d76877be6de4f26410623 b1bb1d888f0c5e19278536d49fa77db035fac7ae
+1d2005951bd5058f1b001eeac119a95f15ae052239a17e43180fe02624d1ee54 a77eac7c388ce4cc9da95c0c66f0be2416eb1197
+1d3847d94a4153fa5b4171642f366908c7613ae239f8135f47a38baf47812292 2cf16038b9c815d6c2be936c90f76049483af23f
+1d4b030078160a5498398766e70bf11699359798f01d47bed25d1883eed4c139 c0972c6411e01048c50c98361c50ab1969ef8bcd
+1d7bd64ed18b5ce0ef15a15ca1294e92e403b58fdc621382e4dd8125b736a203 ac8abfa28ae1dcb1ff4bcc297a7269ad64e8f486
+1dacd6409a61346885f7998a7b2abc74bd00a588d100800217a3732b55d7e027 1763ca88b7bda3bb7e98f1dca8d49bb9dfa3373b
+1dd44fb999c3043ff69a7d846378969eb6b425971eaa49d14628fdb85ce58345 c23841c833ab35d82b32553d0659e815859e6c01
+1de74dd7ee94c061dfcf5906456ad25ee317925d6f6545ed7e5c8deb4dc2ef1e 7c106af8cc957a256d2a9dccecbe6b974a49a9c9
+1df5ac46362cb6d1e92e3b62ab4833c8b9db62bf69faec2456b88eee8d1922e8 14599df1093c4dd512ad3d54892c03102f7aeaac
+1e0acf5db1f4a90ada03fc3df61f0196d3ea09f3c592de0a83f2cc6d5fbb15a2 9bffded46b22351f3c12eb5c020ae005e57cbc90
+1e0ce38d00e8f3e613febc0f8b275e0fd7fbef8af293fd62698be46dfbbc937d 8ec696a4734f16479d091bc70574d23dd9fe7443
+1e82caea1fba091c1795671232b6de19e3c9b18015bb66a158ad4728bfc8e0b1 8b2e33413c0e43bda8a98c5ef090561605a366ab
+1e8a2396a612e14b5a4a7f23ab5cfc15df6abbcc5ab6ff9c89a3788ab450f0dd 3cc606359d5ac5c869901d89d8f25cac348f4c98
+1e9c076a9357a539b2126c275c8774c961ab8bda2dd6cd37750ca969dac15d6b 2944c0cded94117886bb85e4e41f7d4a3a553245
+1eaafe5b4dff4769bcb95b0adc254d77016aa32c1acf1d765cf44fb85c5c8b4b e2ec61507666a29ef12e60eed443146f8431edad
+1eb53bfa2e681bf9a76be9dd3a9c2a5deff28be13d29b2f633f74f9281eab61b ccbb507cc3bb0c961c35d08c7a1d18009ac3bf64
+1ebb7c1f5a6a2f0151c4bbec3788d0d065bae1f0bcd152c9a3ee35b41e1db2b6 efaa4277c709be26d6d475927569d592db39b4dc
+1ed3f54cc02a9ca7528af4986af6870319d1f9ea1d2a2ac59c45cc75277c34f8 575cdc563801dcbef0ff667322c8d00176771516
+1ef82afd4f727427bf8d10690824f403713558c930ab46a90c2572e214bf7aff a9d70a87634c0932d8c03976c80467cc5de7522b
+1f6ea2cab887a2ff4bb1557a36dd6bac9931ef1f36794ddd22b4b7b7276051fb bff8618112330763327cfa6ce6e914db84f51ddf
+1f7bf17705d025c6ec01f2c3e48ac73674b061f4d33704527b12220d34fbb32f 81657de62397e72bf47229c7a0a27f0325ddb8ff
+1f8268dfb6d72ca7bb47bef89869a745d98b950af1dd62d716b70edf33850f33 c5e5c7d94109f5b26ebb69ffceb81f0530fde633
+1f9efce84e985329c26351dbdc81516bcdf02d1e5d64a92fe097b62f9fda8228 8601554dda0b46a69fda76189c126fef0aaf1ba2
+1ffe1f5d30a25e6517f439e1a05b59bca7ca16fa26870557901218e2b3cdbd7a 6b0b66190924ec57928b827412a75ca7643ecc97
+200f1e71b8c5655069f997534192616f9a3344ea45caf96b889cace34b547572 2ed634540823fb7131f54c5f1179c8ff1c59dbbf
+204b6601aea846e40b93e2859b47760350d0d29d2e100def2f89a4d9aeee4e00 58653f4e36c26ebc8e3622e8f3922773082cb328
+206acf23f3313f369c63f14e9d85276e95b0dbaa3cf271e9ab13f6b03b70979e 9cb1d49a7a94f9cb757f414ae46186f1bb36b839
+20ce0489de3716bd85f6b5787f155d53bfc81ba930bf4329ed57dbe4bd99a367 818d98c0665384ae6ef6f7bb0f8d4b0c34acf9b5
+20d7a7a6fda600c0c703114b07d8469a9d188dbdbf1fb3494ac72b2af899905c 09e1e12190d1de05ea714c98cedc81777eb40545
+211597b7b8ae016a314953731f661ce2fb4061ca2f867668fb6114f1b2693b3e 46b55d1d63bd191a9f364400bc1c9112349c5ee9
+21492c6dbc50134219f63a081f52e3aa674d90070674a43332eccd2f4fde17eb 97603e2d1760651b6749067ce3d980ccab9c715f
+214e2d61bdf436636b18c56a7ff55107de6315c85e7d60ab793607b173413a17 ae692bf0e91255fb9ad8393edfb73c6065eb82e8
+21824283eef83846585f59963be963b547ec08faedd27f5a994ef76281d0d2d9 f5a98ad911c9ffe375ce43fe23c1a3e4ef47e999
+21ac834dcd64c8c3759d851ed4ebd3a9121338d2a77c5ee0853046cf13f7bd4a 19c6a202cea3421fb72f7e4c4eb90fa35c391554
+21c3050b5f1069492acf40767ef180b24b7949a9928d7bf52e4278b53b64467e 4430a3812186c60d7a3cc7048f97a4e05f3048be
+21c709ef5ac66209b70aef877fd0c8bd77701fde29e7e6343eaae9cfea3ddc7f 7ce8cf840e9a4d86680c0c13788fb744636658f8
+21d1e21f58b5003ebc6c47526f1efc9834c4fd77df0b7c201bb0809c881d5ed9 b290b470aa40a42fc5b24a408ab9250443aed5da
+21e1e1ebe45b2c1ef79ab050334e36a8015a546f0740bea4505e10d81a946f61 7b4384978d2493e851f9cca7858815fac9b10980
+2252e803f7d4a9c197725e54f40d4d10272edaa05570832be4ff849ae4431fbc c6736542ff91f0d0c6b6a8ecf08eccd70f202cbb
+22a3c8f57f9465c979a96ae07e60850d2b19bda93021c982fb9dcef491bd9aa2 c863948af21d4a58a4daaf359f38881263b3568f
+22b6705b86e4aa9120eff203af24709d483698d9f78695e86e82de121784b570 dfae6ed8f6dd8acc3b40a31811ea316239223559
+22ba8f17bfeeb46d141280d030a87674e2be73e2360c6b60f0d925778a894cff 791826cd86946d8aed19ccdf7d7530da2023585d
+22c61fc7b3feb0f7cb851cfc5fb6a3d84f11a63a4a32d9f06bb6ac0a332f4888 a4af2dd790a63f6312e6916dd0881bfee2cb3201
+22da3f3c129409fa9d299e05428fec54d0a4e49e69400d6808d9caf21d3e7253 880aa99fe67ce18436b9d253ca15565e22423f15
+22ea84a1b29b0fcc3889c026a418eaf785cd2f87702b023045e60c76021203b5 d74679498086d0fc2293dceb59155c696b11b86c
+23380226b60b663254cf2334626a2918975dbe0029ded408c957de839706747e 6110c89446f2281e5db9b798a0fa020fad6e63e1
+233c055cc3c747f868b73686726307a307dcadbb562d3271007e4ad25520330e 1e00a3436acf0452f969a8394ceb4ffaaabae689
+2370e245ad47ad0b2c88ad121138fbe3b4931a7c9a5b2102f27eaffb5f09f892 05664605d827b3b2d88b6de331f0c39a4b9ac09e
+23715dc3ee50fd82c9eca67b6bdd7a51c4c65d7508e32096bb42e755489b01fd b3e8a0abbf795114ca87ef5e714d0d2f36b84f04
+238a501cf11a036f2f248008d88e14af624bb07fced6390997a0fa6abdad950a f1425cef211cc08caa31e7b545ffb232acb098c3
+23b010f4991fbff884638d5e894e14280a3f59b07749acc5c40a35d2f955fa92 40a7c27acc737f9efa912a66cbdd29d75bf44c74
+23b3583e5e642fb73f2f025263c7016caa285589bbd8ab905f7b87af6269c3b7 813b24636f71e0ebf9aaf9afbcad21518f4e1072
+23cb97c200205dc84109c248be5bd719c23bdab52b52c51ec92fef9a48790833 6fdccd49f442a7204399ca9b418f017322dbded8
+23cea2b49eed4993c5b92a9d5f0b82efe4fae3837ea707d921de645d04479015 b0fb7372f242233d1d35ce7d8e74d3990cbc5841
+2470dca53501f9587cae6103c471eecfc8089cadc68de24d72eaabb17da90f51 1ff4954e2b4c365a517362e7a9b285532a79e52c
+24893e425252bc0e02b5f056663284f32bd3db39f8ee746c54835db081369516 1a039633309bdb88eb5e6c46d1f8c2ade51f09e6
+24aea63796fb2d407b9f1c63d19dcfe927f7f26af79966636c957bdf7dff58a1 ebae795e1fd8502328b176c3f63a4320c91de800
+24b7406da6799c945658578a2cac92132d7ecb27d03655c05ca8a6eb5971cf9f 3e0fb8fb56426d4645a50ce85fead9ad93ce59ba
+2545649a60be2cd66432e449bb1e0ebe15a05bcc82b50292ed406464e4e66c92 1937579fc4f2bc5d813f3c8b77688b37829b394e
+25579ffadad7d6abcb2a12feb00a49c2160a35b6e2806adf77445a20e5a90b1a 4c64f551d91b63e8b58d08d9d0674e1fd59b2afd
+255a9457976612734498fea47aec5875822a07030bfb87462afb021e7caba0da 9db3e7f23c37f8764357828dfd6e9ea4a58365f5
+25648e64000f520ab94d539a63eb0fe4fe2a32306a70a9ae5b32444071f8df65 982cc52d0e7c0a9ece1606c37d6b8d00f682f471
+25a4efebb38c55b8f2309ce5e3116b2b9287239952cc2fa174074e05c6e5875a fe3a6a42c87ff1239370c741a265f3997add87c1
+26149bf1ac4612f24b532ae50a12b15f26aace3718749624f008bde68670352a b204707bbc546a1a770ef6ced37c7089cc3bfe6b
+263939ccfe22fbd0bc6475d50b830395233a3417bca1fe7d1e74c35e9292f723 116caa6ac8ce8e28cfd7d2eb29670554cba3a71b
+263d72fa5cd7aa029340ef5e0bc8c409aa33ff8d1f5a4054919799e16c4c6967 46d8b885bd65158e8cb53266ba4b627b5991bce8
+2643ed9ec1615eb7e3504de7e9365473464cc93211b953392894f4e262d9f55c ced645ea9ca4170bd75c17cb9c2c8f19f935491d
+265b89b438f5161c93ce2d8346739c1f0462bc9688a4d549e93f4c0b03093ff7 02283e8165fe86c4974c7082aa1c255fc07ae3e1
+269f91868728e97e447d745490b0e7418e8d1bb6cb8ee5d8e766e1ae9a559b62 28c77740506e6099df65d8c7d40f9800c9574068
+26a2dac21f8f0939566570e48f7f4fcf89239c2746ef8d3dbf31d179c691808f af3b99d5be330dbbce0b9250c3a5fb05911908cc
+2726a64752aeac8c0bf4921a99e722dde7cebe782428c471fbb8dad1b2784b63 9469a286a2240165c93b1940280a86802daa5f85
+27783ad60355fe63839b8ab4d1f4e74675d2da204757b708dff7038b02bce77f e71445c43ca0d573e2f61300e454ceee357624b4
+27a8b0bdddea1bad3b442452d2cd2178ad738d0a595153f52a61b0759ce6c2bc 1a4bb64d48619e2a1ef0106be89bba17768bff63
+27db440f99ec1ff5418253bd4f354807842fea54c31053e2064de6ed0a45638e 66108a6375b0a3572ea897ddcf4470534c6d4247
+28129011003ebf7100cd50ea6bb41f61d88db419df95564bf4252aa70771b7fd 1ce3f92bf1c7b10aa4c76d1fd51094641ef53cbc
+281c36286eab5e534f1c2121a4cf2cd48a32b3773a3e78df500bed3f3c9747f3 fda23b974899e7e1f938619099280bfda13bdca9
+282258be802767efbdf8c3ae693049d0dd652b161ed25c75b85568bc32931ad4 e0ed676cdefdc14fc274668b67ebb2be310fc2d6
+28772383fe514695504d51d0dcbaebb5af526f76b3e262ccf54fb3b6001a7768 209849a44961d4fb86fc51710affec6d8c407cf7
+28aec9a4450d9de98db98a021f97026bf12f1328f66e53319669eb5adce5ed3e 4f6eadeb08b9d0d1e8b1b3eac8a34940adf29a2d
+28d4103b9d69469f94bcc9b762b5ff31b05b404eea961bcebc14e320209d9563 540240e7bb85dff664406a35eaeb222890228c0b
+28fc5830291e38452b4b43c54c5d5a7613ccb39c4866de51b890b55cc35207cf 98fffeb8462945a9c9f19d6e8bd40470afea58e0
+290d84a2cf108c074d3764ca8dc56e1215a5a7482837f098dff7a55e23d89d5f 4f9339df943c53117a5fc8e86e2f38716ff3a668
+297b1f32141df100021efd1a47d8315833d19d47097fccb14c80c9217d8840d9 ca481fc4f12cec56b71bd50ae72e3f088b69021f
+29aee3837e46aab84222310330ff8fd255096d4a22be608ddf847df5053debea 3f61a75703b0430062d1908fc0e3e682cb8b95e7
+29af12bc8b8dfc24018ada712ee5727e1185bf8db62cf5a0b693ec59995ced37 a2e130b3f5a8978bbcb28cf5743d7672c5ea10a6
+29c3d5b0abae694f9694c2f7f63eb20b80de152886642e5d0f5282b4792cf84c 3e89665eb6d5ab75051dc59fc8b63316908b19d1
+29c86d618bdd9b53a1df6ec30dd246a221bc2670aef3319b6d688a1965a78e65 4141dd83e5a7b99c7f2fb295709e2ec5a66aecc8
+2a23921ccd2ac045bb0bea1a077d8834d23f2c5292284511cc2ab2380eb5925d 0395b97d1e2df4bc7cf46a9a82b753a8908157b0
+2a497c0a14d1f5cb4d497b659b5ca43d2abd353a35ee86aa5ebd22b79e5b48e8 abf5681c033e83f92198fdce8d30c799a6e42af4
+2a53e939a3578ca6e258a421658d15fc90a4bfd310f8c6b1dce41a26bb09e2bb 4730b7224276579fcc8fc7fdb9bf796ef158fde4
+2a6348df1ac5a449ad2de13641b2993f5707c5aa37ec0108bc02caca9d93c4c6 dd1a5b5b3b7a1e3abd956a0808675001ee06347e
+2a6e55b7502ae9e503aa71712eb38a7db4a400f3476d3c8c91b0de39bf4a050b 279d8e37642a9aa06a4aff44694ab7a6eb68a60a
+2a744551456986934ef43201eee6334a89b1ae4e4c919ee2f8f09f893a6a1c51 2cb51104bbd83bed39bc8a178ea172b234a3d55e
+2a7492a020b054d3af4f1c6ad11021a153198359f50989c9cb2d3a58d347fa8f 1a57ba73220bc222e2d290820bd9f4157c8a504e
+2a7adfdbe35f2b25328c964f8e40855b92eae773accd60a2e3aa1717b6b70341 a6a1a6fa11f7d0c989afae4695d4661514cda8c8
+2aa41eec6c80ed40eeb14896e1716f9439c6a5e99725168bef1f876d0e48ebd3 12b66564dafa01d8a13b8ecf53474b1d305ea771
+2abdec884f1cda652bc5132328df25b97083f3c0d4527dbae2aacdd789ee134c f31fd16adbc7a2165c0091c22e89e50811f95df9
+2af226bda5c9ca85442284f6c50145bc14e632bf62258127f56c7ae77d295771 40721f6b1297f2a48aeb5c9a3ac095767b1153bf
+2b3b4a4a848d85bc2993d0a7e3bb36e354b5053e7cb6165dac164bee2dd9712b fa19c3ef71b1ec077434b0e08bf0757ddbaa22ae
+2b59f9fbfad86c6d61e684b441eddac921c532c40dd645884be08058db3d7fcb 8cb3e7d4c2c5ae36a7ec398299007af302bab6a3
+2b72c9f2e8b8a2b0e041410a14d94e9374b1d8fbbaeba9dd02dad3995d5700d8 7c982100f3c7b81e576b552484d58470631f2cbe
+2b86b328af6dac7919c87c258ed08cc08fddfc033eb0c8c197c45165ef435219 b7058c66c000c55f92fc4ffd7274a16b41b196f8
+2bc0fd59c1b97b08b6018f1243a34a00e0aa32742ae2319c07ed5bdb3d9b4269 e5aedc0644f6be56bba890d932e552cf9e08e717
+2bcdebb6b95252485caa69957ca19c85b9c4f99ee36e4292a74c8505fb8158f1 158e2b76aef44441ab2bfd42ee69873822a4d67b
+2bd621e4518cfff39970c47c32f704838519c36d67380758666e19699ed387ea d54b77f23f9cf4f0016dca8ceadeed48b1ad5686
+2c045ed1bfe1436578328ce00e3d7b6cf2e80742ff260b5c2b524f2dccdf97c7 86897ebdca9adf45f7789399e9c4786d819e09f9
+2c0f52f9ae1f34f280dfc1c755cbfaf2b9968fd3bab1f1eb16d3ac0fafd71940 8fac94df3035405c2e60b3799153ce7c428af6b9
+2c2688f8f004501fa4917b31de04513365c6ccb796a9319862e0f6d8ced5dbfc 0e46b1d04f022804f08de3466b5cb6b91fd366c0
+2c97e388a515dd8871f47fee997fda7ddb7626f68fabe2e2dfbed0d9e5410f04 13ce9f1b96279187249d56cc641252ff617fa35a
+2c9b933ff2dabc5a043061d2aab80d967e41b01840b9790a87c981afde9bc0a6 a256495f5eb22ac2e5de21f37b22ae3c497b3580
+2cc2ff33694c392abbde2169365b575e662405af09624502d3e151d205d50c25 e6823108533f65740e0e5e75d0fc335e97439b75
+2cdcfa2499b832dbf7b855e5a7d210d538be9674dd2dc3ac301a21783eb7b333 c7f0591ff41fa0633796122d09212d3fc325b4d6
+2d22bd62fa3abdac2de06753deca74c77bd2f4a00df6014e0bba6507c076d6ba 3dd02e2850f34d7bcbfa3ccd4e909867dc89598b
+2d851572773ae43b2bb09543fea4f36091522c46c0a9c494bded5cb3d0f302e1 f82a8eb4cb20e88d1030fd10d89286215a715396
+2db6069c27ca4c08b784048644c307e17d0afe29b55f6488398cb59f13feb2f2 dbc0be625bed24b5d8f5d9a927484f2065d321af
+2dc5fcaaf371da4d552fdcbfb79da7a4cec2792c823c9bd09a90defaf73b437e 698d940f85a59f00286c49af24b390be7cd8466d
+2dd4d7ceabcce385003eaf8bdf8c53fbc262cc3dbc2041eabc72a7fa4b6ddf1f 0b8979fc62c435ff5b984d0c13ee42d7ac2e03dd
+2def5408c21e4c0611f22321783bf0d4c21f8a9006a3b6522cf295f493ec34f2 5de89cbd8c0a0c8bf4fc43938f09e58936b8c2c7
+2df3172408416389faa2bb43248e82a2220d09c9f4b56b1d2093286d9a53843e fb20a5a4b6185d9188d82c874db3d9729ef31f3b
+2dff3f4b2fe826157fc7c3fc1b4c9736259160f718bfb7fdb4892bed18378c60 856017fbf64464e3797689463e95d75496c0d554
+2e2aa456dbbb8889923eb6713672427854020298a764967c50235a9a76d7ce4b cf5f7235b9c9689b133f6ea12015720b411329bd
+2e51a5916d344edef37d84ade43e3b93524ed39456a7ea3054bbdbe06e2534b6 d4191884cffeb8b2481197c4c4b7dae933493174
+2e5fde11fa87cedb853bea73f5ebcc9ed34830584c5a7ef74f083916cdd4e900 e896d00181e2053cf3cf6441732a13f6ca83f104
+2e900cde6077a860354bc38e8ccade669cadf2cdfa60396a3071215c5d32cbe4 33a790ddf892b1b7922a6bf78e4362965c7fb51c
+2ea5535daa4731ac4a94a7551a891f08e3c6f29cc2a864be5b164652f2f53630 11f76e30c38265404b042b0a4347e86b66132713
+2ecc823b86990906263a4369e86108176e5971872da7307b73c46f4ac7ed8a38 9890c3f69ebe264206de411473f6220fe2f9627c
+2f18dee4a944014c0e20e1a96046373cd58710db5cc74b584ed7d96a324f998f 4188d28f1c38240392d896fc79561cc461fb12c0
+2f403b3668fc75cd5a4b4dc572dcba3769ddc02d13bdaa3e1c0a144e976aff0a 6586dbd5849fb1369f4719df685763ed9ba48d5b
+2f468a1542ddd517af3dd118fead80293c5c77790af57e351189155e96dedc24 7a2b79d3e2f273750e3855d7a0b15a4c6acaccd4
+2faffb9919a0b6717066117ee9f156a40371e8ba0eb585170188a65ec89a8082 ab5685026cc2b70c18e2c43689a57944eacf59ed
+30312e4b199c64d45ad2bc917e2ac5364ca8f8901adf7ebd1492b7adc21ba915 e412d5a411cf4e9cad80ff3fb315c42a7e718ea1
+304352ddad641770fcc94ee4d9f957cde7aaf4c107dbf8b5ba14d543640bf7dd d0e7959d4b95ffec6198df6f5a7ae259b23a5f50
+307109fcf8b70083d2fb902f2316b8a0f790c3d9f73f7870f4eb774b00bbf44d d8603ed901d4af4d0d2b493d1164c74eae34f147
+31138d44b7d0bdc213b18a0845bd56025bd976cdbc1fc323901e9c50238b30e6 81723c22eae0570f3774ac588b572636f34a7467
+312f60f6c352b8c7367e976ec359c92196d0b17d491066d47279d4e1e2f44cad efa59b726a6d367badc5c39d83e86a1664616da2
+31ae8919eaf87822e8d11a589c35fee500b0d9ca51d0548c2bdb99d4fb299310 7c6be4f3fc4d952d4eab59ba2e553c6b77725bfa
+31b05c73b9305a342be592d83d59408dcd54a5adc94f0da7fb6dba61acc2929f e719ec29cf9da6022610b46b463b80d393d22778
+31cbc27193bede4e2ff534542aa46c1a8ed7c4d2f5e9c9e462bb99d73e5f7691 c27b446edd6db301478ee29880aaabfe2bb66539
+31d5e6deb4c8fb548582ca4d37fd37f2605231277340f5b402fd64904c542ebf 5e8220f46ca2e7002bba745d8c0708622c64150f
+31e01e45015260b88c5131254e3a3f72df292442143d2ae03224d61aa5177c24 8ea17a2c5a5bbf0f5aa72f73e6f980136d72e6b5
+31fccc3f5f0b0e514673895b4aba0dc2a54b7bf1441abe8c41b348ac997c5566 79038fb3d450dd7b37328281a717f275cb7eb7e0
+32ec9339b53b2bdc73e027642a1c990d3ada79a168de1cbcea2afd29fedc9709 027a51a064c6a3d91392f31f32b041bb1920ec60
+3302d4fbb5614b19c697d3f3bcae7ff785944cebcb095ceb418ae601dacfd986 75e6d95a87b9c6a5683a6f4b56fbfc9ae6e53a7e
+33139bd70082e3948104945187410076178d57d31dd2b50542b9e2b321e85776 e65ddb38019b6e2c268f8ab9c28eb19bb162b032
+3315093132a8f28bd202c0a9562d04eebea4943dcd1e1c754341b0389722042f cfe3a027ab12506d4144ee8a35669ae8fc4b7ab1
+3317ced38dfcc6285ecd4187b6fda9e9027cd26fe11a4a81e537e770a892745b 39c222290d1392e028478037e250bfd215635cb1
+332d62e05adc040df538a64bb4cdaa02cf5bcc83fc8a0c3a19081be7956fe1dc aa0c0f471d53ead366e80077a9fc82649edbb4bc
+335633f31770ec3147c2fa9305b5eb1fe95372bb00fc07b8f5a28d0aa40e9769 bb61d8117a8cae026fe4061e15c29a96aea3496e
+3366326706d1691a8e07d043d23df3a4a96945bf9377cbe95d4ca9e695ae34bc be876eda8beabd473360e2dbd1a2ae5e80f6455e
+3381a5bc62348132331d43b4c62f99c69e9d927072eb35fab9f10b9911d805de 15595ebcc29767ee05eb33adb216fc2e92af85f4
+338aa6b9127d19d369e1973f72e10306d02fae93bbefe85e40160dc40417db80 7aa92d44ad52b314e335fa1aa45096ebf284cc30
+33a9bfeba76f77472d80e57af9f6362744595f3dec1fb9e011446ccc43f0db0b 5d7f65df66f2bc9f3581ecb1a7932b31b8c98aa1
+33cba02acdd63428702757d61751c0dc47d51b011ba0f006774a2347763c5dd1 089c2d931b6b68dd82527430da51881d23bbd9a6
+33e415b835a670bb5c3c760efa0433ac0cbd2d44679f68f2df3a9ae7014cf2a8 1385f264afb75a56a5bec74243be9b367ba4ca08
+3447c9eb6bbfce8c4048e647e17d5c170564bb373efbafa65e3f2c64f56bd7f1 5dddf7c855f9c8e43954a99a6f85c098dfc7ed41
+345d2fbf1d306c7ea46a05f497793308357e9e17ab0e866446d2b9894378ddce d0d7e736e536a41bcb885005f8bf258c61cad682
+34e4e17c4c3b2175b0c8eff812e9c3c6bc689829b435c0f529cd73a69cdd7f2b 20c4a63e8cd8a70bc0f894ada6a54afbdefffadf
+34f79ad1c813b93d2ee11c830c2134815a31d9629e6aa9773338fedaab90976b 849a5e34a26815e821f865b8479f5815a47af0fe
+35511d2821ee472ed03f36d2e481f6d1d47a85d03ef56996b67f43b401e2732b 7a750b897cf8d5321db7b7f8a06e7d098b13a74b
+3576c3885a36e2e498004c5eb560a8b98ddb87778d2680aa24bf12c2a34a8870 e8d0c5322cff755bc73d89db95334482f920ccdd
+35f9aa36440bb182dd897a1a2c212fc1dc45e17ffd5cac859c87ec4ded0a36da 698b078c75c911b8361318db385d70cc84543a13
+3609a41c0506fe19d01fb8b4729923362675f191fe5f63fab3111ef804c48fdf ed1ea164cdbe3c4b200fb4fa19861ea90eaee222
+3612a2a9b865800bd6450c6cf21330676dadf843a578eb12d2e6cc6092ba3eb5 088a731f00a39fb3158dc9150b7c8d176df51867
+3612a2cc30e97df2d2bc86ec0aa57d7f332642cdc370d2b0df18eaa6a53bf220 0474870ab94adafa0bfca2fe128d6ea7fa2f3c99
+36693b61b675ecfe1babc324bbcef2e01bee0275b422152cd8c48593555d27ec a57e9a8cc15b61ae1ab908b362fd829b3e36e513
+3669ab580ce27ccc1dea9e89c70fdc98d0853d961dbac4dd0d56130f1784ac12 0bfeb8244a6a16625a0b6df594118e041f16a185
+36809d2768524a8d74cb2513e3f0e4d07e765b54230f7dcdde40298fe58a90e5 e3059f59ca36951d3817675131aad82c626c6d97
+3682fbc67dccfd36dc615ba17e3ade5fb7f1dcd6ebb425104317593be72e529f da5f70ee04085d85be124739bacefe69e829c0e1
+3699821b431131e6a684f5faa9e940a1097dd276bc5fd41e6a9f9424b442fbf2 098db3d369e28b5e5355db956df3fb45241f4feb
+36d63bb74fd24718c0d6113c70db5bc718357bf0a9b336dd23e0a08a51fdfe6a 4275617b02912a72f62ca7d2a1b8f22602af4de5
+36eac24505d4c4405864ccf2f30d79af178374166daeceefbf11e2f058d30d60 270b8ea76056d5cad83af921837702d3e3c2924d
+36eba302d2fb99d0b1c79bcec1198f1cbba6cc8c804a19d3bf2bd3883e5d9fbd 4e0d6d864d3632a820d8bf3dd89ff333b9bbd34d
+37113624744ef096dff48983ffb612b765818092c6b30fc0756f22050e9c9cdd 03c3f39467c727707a5522e6ecdb4fd2ef09f8b1
+3740df04c191176e7dcfb8ffc9eaa32a49474fad69a959fd03a1234ea1e5dae0 5b209c9654c83719b07d9fc1d2287ca361663fe1
+375f69d3d41e36d6904bfa86221690ec49de2a030664a362abaf86c426f9f7e0 df48000ac4f48570054e3a71a81916357997b680
+37930b649e0a232d9d5068e3eca85924117dd83095ee0855e2e9524fe227ab30 864af6d01aff80a76f16241ceb82c74d9df49e34
+37a3d0dc04bf341eba75c9a147cda7995d68a8f9ff1c7a43871b9c7a7e3a9ff8 e24efbdd0d24660b10a736020ecbb98453bcd99c
+37c7c3d1205434567f6127eb53c246e5760f03742e2157813c5809152d461491 f15b75039d7d4db8e989b85fc085e6f1412ea318
+37efd2fcbb9d39295e0c1521d7a4c53b9be48cc20028ebb1718aaff90149ab2a 7e78a9c8d5aba74a0c85d77ef590a9a9a6426bbb
+37fd90d6abe96a2b664d80443bc426a3d47ab5d60c68e06b29bb32b82c9c702c c0d11745152c0a3bd8bd0e94a8996ac952604ab4
+38245b6deef0fde5656f71d86a9d89a86886d42298e46ed740756281cc07ca18 4638c830017efa737b74da9e55236bd515ac569d
+3829e422229d9c5b4bc01df9388483e29b4e52935c4cf4c531904edf80f012b7 375649f879c2d828c5a10ab61992e5d84c016dbe
+384d76a2f80b78ad569d3619586c957e6dff985e2fadfcc99f2f58e4f1690be1 9e2cd50ab5fa56644adcaa13bef9e3dfc7bf8cee
+389af1fc5e97b1e7d9cb01b1e387b08862e26d67c3650788f51b8714c9f5fd6e 60f4720d8ef4cbbd09f7f9ad8e41bd77b4814cb8
+38ba5ee9660e8ec8b261e2edb1441987798bdaa4f19d478241a76df16b88d0e5 dea66ba25c0b724e0dbfdf494d50f7f3f2ca780b
+39bf1ac28cc3f8432ba7cfeeca6bfffd9a0fe641784db85de2eb0f57b7553869 b6361fc6a97178d8fc8639fdeed71c775ab52593
+3a0a4805f179ea4e115d542497fc755a2c1ea5792853609a25b26b299553df6d 684a1d3d44cbbc86381f61d4d9305012799d9f5d
+3a0cd33a47fcd0c7b8f8c1d407aaa53f648e25f2ffe2533a7e9c09c3d1b9da75 2f9b6b6e3d9250ba09360734aa47973a993b59d1
+3a103fcd415b1565358ec83f4341844d2b4cb68e1db409778a7604cc47ee8663 92ff6464177928722632f05a3083dfd9c5dcf47a
+3a1b33e58ce33cb9658a2ed1cb6215a4120f4d8d066cb7f4c5979db9070fa59a 1fcdf832d88aaf0f7abe480f5636d0cea10aa109
+3a32ac3a0452f13635a41fbef02879bffb3733491b4b791fc1766b6f208acb59 3a612bc5fb913bcd8dd90fc3042f23882d626a91
+3a3735b8a28278539697e5f6af5c45670d526fb7538ff1594e99df6331c382b4 495eefc6e0986083b95f87974942afebde688173
+3a70044491dcb883ea825b2e299c512f1877761862d5568028f7ac185cf38604 17e194d81de579d956587ed7411eeb77f88df277
+3aae8af9b31d378f55fe8390becba6e9d7a4cc07c2df45fad2289083bcc090cc 12dcefefdbc9972cf851906ca5c8e8fea46d1fcc
+3ab0a48f387c62d22abef3b52bab7bc7e41a3bce649e573912d0de4c79b68af4 0aaf8708b0f67ed9b99f1c1e2fd28492a671123d
+3b1b991ae70d1f5388ad16b63d2285e99ada7a618c6f5d01e50a6d4b33c4767d 2f06098183b0d7be350acbe39cdbaccff2df0c4a
+3b58565ee067f13349cd4f89aa396d10f71c69e168d5c48ea23de59734ec3ab1 f0a2a10243ca64f935dbe3dccb89ec8bf16bdace
+3b76cee4a13d688c4c7f83c2394d6a0b3908fb4289d917c439431d85e53e08d6 b3e014dd4810983396c9ab09edc7be1e0ed1ff51
+3b80aefa5801cc70308cfbb6300e7e04f9eeaae97703e0ced39d05f6ccb3a552 a74605e0562a64dd538a56a3f29ea85322f4220b
+3bb429f2e42b885e937592255fa2da4014dcdc63fadfcf62413f0660f438a85f 79ca2edcd4e7a801400fa33c2c705b8f03a5d7f5
+3bba055be795e5d49a89496f0219ea2f43cd9eb6cb084669eae97dc69bfeb459 a7f70745f85f445596125cae6e357e84b84cb925
+3bcdf0dbf3cf72b38fd65d0dbd8d6c6c4d9e3ddbf3916f03e1e6e4de72785c77 20d191c80cedbcc63ed52096b8b0e180c6bc16b1
+3bf9e14c7f197bfefdff9d52063713de4bff0b857f53b799db0f4050bd62d3c5 52f3dfe9b5593c9dd45980c36e2499f2e9de701d
+3c75c9f9f003694aaaff66b2efb4932dfb0216bb85d6ba5500d6f4a05e92b0b5 eeae5bdbc9a3075ff9ee6a08af804b5df11154ec
+3c96c1697a81679472afac3dd2723abe86f34f50cb2804eece8b0c942b3a6d9c d4d88ab7e48adcc92099c41b7a5e85d580f9d77f
+3cb25d62ced555f8409804796b32e06540c096ef858ad45c864f31503dbd4982 e3409fb59571d2eabd07d4d7c09a0fcf07b6cc5e
+3ccee1f77ce4b4db6e6ec7162538efcaf479f3ddd950ada7ac800abe7e9661d3 fe3c74b75812f161cf1838602ad7567143bcb593
+3ce006ae57f23548f85a5527e5cfd9ab29ae42aa1ba90057a807346404f8174f 94f7fb59f74edb08d375562d1814645dbe7ffcf2
+3d14ff2d57c0186871a99a497966bbdcd17e61c41739cbeb860efb0f7be31c18 dc5b2ce5d1c4c9c38b94af511ba60946b8761681
+3d45b4ff158011774b796fe55f7041f932df6dfdaaac2dee934a0b6f2a2e5d4f 8b049129be2405e428a3bae21ea9b99387a96afc
+3d47ad957bcd83e6603d706016af1b6158878b32c902fbc86d28918d640feb0c d490452617988fed2ef9cc3ff667396532426903
+3d8632d47f3f70b96b9391e692c79b6237506bf999a012e50c1c3b15998748c5 7fb15ecd2e65e379b6f3af1f25be0024c0be27ad
+3d8a6f490eba51281c43d4b67e0336a2947896a4bfaac3c0449a7dfc84411128 1e4ac7e37cb4d50c35b7a52202c646a81e5c3dac
+3dbaa90e18232adedec438665cb022c35cb97c999ede79097d3ba17b1810df0b ce4344fa507b0c6330a3559850c9cd186d1b052d
+3dbc65bd993e6e700790c5c51ff0e0027b55f55699293e73fcda7c5e53a8a3a8 1b6dfa9817a8f324f234f0f31cdc79c016b40264
+3de437349b8b47ba5cd83d5f70956594de271cda393792922fb2416e3f327f5a b7b0d0ed2d91c83b59914db77c3dbc34e36e2c0a
+3e00d8cae2726dc33879adf876b30c306f50e7a85b15c8add4a27f84d88616d7 1088490171d9b984d68b8b9be9ca003f4eafff59
+3e24bd404d914a9195b89466b1801a0bce6e55fec958ca7ca9a778a08fae6fa2 010f258ae4bf72c23d01e8902d7f5dc8e4533ab1
+3e29ebc6f1b56a649256d7c35fc227c530f1477638fcc68981b3626e49746928 ac7d251b493d022de34162880d30392aaf88cd3c
+3e5411e6190b46bd834ffe79f2bff3fe5a9f99ff2d4b5520bfe84cb22e295b38 6624a3e24af09a12acd9b5b98c3109fca580410e
+3e9c52a7e147ac9dc1a9e23ed7193642e108276f5e74397201c630bc1dcf20d8 c8df0297553b2e8c273ced67b3bd3d1603c9d4c7
+3eab74a6894d790767f3d92615a6887dcccaffa9e48fdd2ce482b5f17efcf9b9 111d5ccf0bb010c4e8d7af3eedfa12ef4c5e265b
+3ebff01bceb09f6f4e2a24596d6e281587981dde381be64240283c0d161e753b 64c993e1ed74492bfe41ddfaa8e5f7999b5b3952
+3ec0a36347125293ae217e7d4049dbda22951ff9cd0d5207118f6e963a7428d5 60ea5e87d97ab72853fe358b4de61a68ca7a3908
+3eee10d9f6089f9d03d64c65b9aea882217f833ed9538e7dc49c2ff73ec9e41d 0ccf3c2464ea829a6320156b5ee131337898f2cd
+3ef82d40c8f2f25a1f20d1e459d083eba406bad227af5881c6fe97aec74ca7ab a0b61b552c08fb538365870b3ff49d936a46c21a
+3f0764d48b89e7d99138b291b09ec88b9132fab0a60de5b6505ff9d9b383e843 8e9e423eddd73420ca4b824f54211208f33628c6
+3f429bf36329d28d40103a6b1813f92b8322bdffb7bd5943cea40569196e6838 eccaf6f8e31a3c87b89243153770455805ed4c1d
+3f5dca26a2f512d6681ce1957b8afba5e031bf63d52fd52d8f57093bf92391a4 7033f9ee0e52b08cb5679cd49b7b7999eaf9eaf8
+3f7fdefa5ab3f52b546c3cf59cd50d61c29efbe4a2c40b1d5ab8e50ef34fa398 3aed57726754bd5c09bcd425d9b99add5db50a78
+3f838c3c9c70ea829eb28dcb763341b8c730131251979eb1f60ed6819f6a500f ff02f4077fc8420d328c8b91c1f5b2e71a7c19c8
+3f881332ff93d2176b18dcd38c990146ff160b3eb83a262bc77462edbc246a32 66ce77617726f19d295e56cf7de330c848325646
+3f933cd9565bcc7b8be630d5626c9dede75c7aeca14473b2bc2b88d26417c7f8 29f0e90ff41bd7079d3e620e15370e3a8385884f
+3fb315ae00172e2f6cffb933843a407277ea426015fa6224865a7c63ecc37d8f 2dbdb824f0cfbf5f583e6b94aec753f276ed3368
+3fdd086cf7ac8ab728ff01b763ad65b9d1b7122580d88aee652b8ced232647fc 175396917d98ba13e5bc959bc3dc237cdd492036
+3fe6ac134c9e29c44bef2dbd0b11718e203347e4b867cecdedc20e3466bd9896 69dca959508cd04b226b3747c0c07f89f3bd1a87
+3feda025daa440402e3c020cd88a0e9378dd2442e55ed409c466376d5b424730 ea790f337b0e401f5e4acabf9af9c2bc756d5f3b
+40154096dfe3b0b1abf64230345d327bfb530fdc2eea093d407bc245a94cd71f d15e94c0d4eb0fe06dd1e25d7e9e3c4940417f56
+402b3758191ee821d652dc759baf30a8949a66e3b7d073e7e79d91c053a7207e caebc63b34886497fa2b6ed9814a327d3c91e545
+404b795df44dc8a0830397f7598ed7383ffc5c227c2c8a2223bcf9cac3e6a4cf cb98fecad0c4379496f32a3f7ce3aea2ca553b3a
+4066249c68b0d3c8b39ebe02c9188935900465acad02a49269710f56720fa58e d0fdf2dcff2f548952eec536ccc6d266550041bc
+4071ba3d3cd2494d4951a322550a934507c44cc7278c90d5f7458c512407fc8c e58c5633474c35eb16f9dec209d57aaf881b97cc
+40799f33b8cd9ca41f36a2f89d8ac8550537ad01dfb21fbc76f01eeb62f512d4 bfaf8c42eb8842abe206179fee864cfba87e3ca9
+40c6785bdf24b49d3b75f3f1f918dcdff55b2a001b66afd0a411b3e8439c9066 979709bdbc7b1c91505138e1aeab9f8b714480ed
+410cbca5119f2d6f16c3e42b52d56148d5f72d45679c92ffa3b6d9a2edb756ce 80176fc970e2964677967ca1a2a3689364513593
+4187fa8d107c1c86d8615ef4712f1f7c64829749a52a1e68c715ad9a531a6e2e 33f0f5ce19ce81d7d32752b140dd9f5871d72eca
+41aa03e11a4af5047dceb683d7a03cdeeb904c85f8a58464c99709defc5c0301 c45579ca160b99e14290db789f3c690c406b6dc0
+41b613b0200cd70a7f5f0dd85a98cff80cf770d2b69e3a270fda0ed2ea9e3577 4a9c7f5656c7c53f5172e16174b96ab44606de35
+41c166c241a4e878f444932a193501e12ab38ba0634747291df70e619dccee1c 3188ffdbb3a3d52e0f78f30c484533899224436e
+41ddb608f05a0551ca421a4c6031f9f0b0d1c165d8268462155614a741db2f50 ec66baf57304fbc94d8b1bf55a6264b6a34c5972
+41f0c76d526a391e6642bf1724a2f32c926d1d10262aad6183d7cacade456c87 cc7301d8a23a16789de6d298a6b1d2ad7c686184
+42582993ace8a3488078298b40b08e2e48bf576ab05b1bd36bb4884a9ce853d4 6add857acd3537b186d2c8d2c0e3027c364872c9
+426317ab5167d24de98d3d49614d713fa8e5ba435e1d9600c3b95d2fe9f2d785 f3489977f6f6d5839be9f17118bb723fbac74a89
+4277a22c721ffe0b89f6fad5e7779f6fe80a708bc471ab49bba329d41d5843ad b1bdce4f69d6893727df16ed00fed96d701fac06
+432fa95d99227269dcc7298e892e53873ae8b6a283041bab4febeb298fd3ae10 eda57c53e981161ad7cb25103fbccb31661181e5
+43630957599e6247ff790ed2a0021c6dd072687ea8952d64b767251f79135d42 53d25eef40016eb27787814df58e0b56e76d9326
+43723656763f3659a5e094a87df27c5b43d270840870dbe1734142e2a9466d7b 1711eef6e71be6d243016b5e138766b3777e08ef
+43989c902407cb7bc3753f4338f4847b21f53f49c4e9f750cb866507b64ab669 b438016ecda7648426036e66c4ed17fc93e71cfa
+43e084a4599ca42c476919917e3db8fde0045ee66305fd5e634b0c793c536a1b 4a202b346bb0fb0db7eff3cffeb3c70babbd2045
+43f15e413a5b988bfff18d10d4b3bd546b13084560e0a723f4500fc094e13558 783257d3b6a3279dc62d194bea3b7f301a899351
+43fc14cd889029d5ad6e285b60f162e73c9576d49068607e879a2a0e5127fa5a 44b6a517f15303e5ca4cadb090c2ae47cbdf0c68
+441eb4d681a9c013e67255dd2fa2fd722863bccb7fa9d9a9230b7aaf5f107ae0 47b0273228dd08a6ce617f8f3ab2092a2d4dca57
+443f41124cbcc448cff03bf4ed757ad88a5f5b711f761764bf37c64f7e900869 608d33fa676aab1ed350312cc07e02cd7f94847d
+4481293fc62420e43723b5402eb6ad4042f5f2951f9c9413c4ac3235edd4553a 08167bdf6e553be1acfce09de11aa69b1c3a807e
+44be0bb4fa20d53c8b321492e98794e9054b66ea200d626b9d9f8c990344b32a a41bae0b8aab4991d9d4bf9cddbaac3c6f38ef9e
+44c505e4121cbe40d889b39082f5bb700ccae8e93019335df7242e6c84f8e2dd 13b8725687aa88dbac7a9a34865984d6fa68dbbf
+44db3dd46bf12039f93ff4752baebc137f2f638089fb0e1a217c99528c3602d4 9fd8412e652899d03576190cbafe0ae194357827
+4516b0e63349c81abc6584cb11ce84ab8ba38b105f9de39d0d0a1455dba2478d dff79e27d3d2cdc09790ded80fe2ea8ff5d61034
+45b27cc016ef3dfa31290c1e564c82266dfd4e626d65c10e4b84717f59acaef6 76f293894f4ca1846f52513b420dd68bcb8da127
+460ad78c6b885a65e795a6ac9a4c85d5a6083b49f2a168cf14701b1aac7ab51e ad9f7df6bcf1b6e69be44c85641380848eea36b3
+461f1d5cecafb15ce791d599ac2114b5cd320cefeaebb90732970655e6f85050 aee8b26e6322bee55403a61634d7e01b77aea4c1
+46333d32b3801cf11d9f80b557245c9e32b0e05deca61dae968914fde159f0e5 0cfd861bd547b6520d1fc2e190e8359e0a9c9b90
+46698c7215f6b45759d9cc387e8d3121ee1ebaac4330cc1e1f38358b5d67b973 9bd5c5f028c505f9777c63ea85c5399bcb9cbfb8
+466cdfec4c74f3ed4fe53165e468a52df5dff9c6533ec433cdf235a73e099d32 5052bf355d9f8c52446561a39733a8767bf31e37
+4686fa9fcfbd200774f6b6e55dd719a3eea3da7933217c7da0efd14fa9fc3242 ed2292c12cde99459c5859362b45712deb1d5136
+46cf6ef7c306ad8d79ae690f643ffbda6d1cc3481e46f4202a166c6dd141cfb3 d3b23e08fe7235ea387b45453424409fb54ab7f4
+46fe85670f28bcacc7894ef9227f4816ff018994a67425ca92ca71f58f57c5a1 f5607b58a0364fa99eb1bab8e1c06fd948cc701b
+471b08f85206aaa22b74d87e57de5caf02fca30587512ffc81bbf32d63e9915d 58a948216a0b01a66b21ab7a2116adfd536cf922
+471fa4fc2da467dd94e57babb1912bbdb5e40b96c8129d46fc709c0bfc009bca b0a8568a7614806378a54db5706ee3b06ae58693
+473a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
+477a19a87cb58e25978dc3302ae5f089d4e4997257fa09e259f2f03ffc5572e4 c5bf0cd945f7bb251ab2d30ecec98e982bbeb49c
+47f06c894892f592fde64369a0850fd5d7b78d0e3ef825f66a064acf6781fa9e b923f2f97dc26e981c18da93b98bd5e7b44c3b79
+4815816fa5a372f9391088eb904b85bcd83b0f5117772b597691dc43fbf5bced 5df58dda56789631c78aeed62708e1b694440195
+48206be797f7ae1a97312fbc167d2f1d68a7a739844105ee0829635ad3db5001 5cb9d1b563afb84d201d9cfc98aa774d7c714266
+489243ccce2df9992af678d0bac3bbe47d19de6cf9577e4e993e036efb0b194a e6b63299b2cbc04ba8720f77425cb58c7774d4aa
+4892b87dbc8972e6de31028fdef2e8ef01f4adadb782e61e2995424a4e660c1b 55ea3a1ed6a61ba508ad6f9e8b70169fbd7746a4
+48d25469668795d0d0fcaa9964a1c7555b750a8d700869d5d8cd466b1f3b0881 2434915bce998e8aec539a19419f2c50f26fa6f9
+48fc94b3d4233d194db85973d6028ef5da29aa21aed20f8096539b837fdb9cbb 171aaf21d9f7582270c390962f61d3d2613c4d59
+49235ddeecf412d250153a630dd1372fa0ea2953f64edf66f9cc712c4e003504 41e13b227932a121b8971909081382da4236d674
+494fc3881f4dd0afeb9c67b9d03ee0a7bfbdc344db53d35ca91b23b27b8a25b1 8a208841484daebf2dc98866e6d2e44bd9467cf7
+49818c2e41a63cac45e51f99834d583c2623c48a2add67395238eb16609c7f43 d90233146d40105f042773fce6917fa4687b94a1
+4981f3f393dad6299538f859bfc10bfd1a46e6c1a76991a35f66c4a63563bf74 42fd40db68e5e5797531dc1f5de7aef39a2262c0
+49a226f8527a921c15864067e049ef18fe0e56672cd9be551eb1ac520cc2fbe0 29d1016d5fd281f10d5fd8183fd8bd2f1aa37121
+49d34d8e410e2bc0d4e83f55f013feff99a8412092b44fe18c388e2cb61539d9 01ef4fad010ce3b90426982172b2d93a9409797a
+49d6ca3fd96a83a837867c38d32bebffa599cd8f0e1442ec38f9881b33184f78 a1d34bc000cee6d72c3b5e329faa58424641611f
+49ea7b53ee934be208c963260e61d8939898040d12b4e1c166222b5a7400eb42 0ef9d2aa934d6e5861206e8c5dd829dc09f4026a
+49f1fcadf4ffce148c8dc15c655c325b0c52315f3e093ca5e60184b826f1d575 9b17380391d7e2e4cc26654ee4423ec14d379d28
+49f7c79a7b31b51b469de45d980e77ecb6540a138121dd60bdf399d0000db2da 29e77b4c60769b427a0cebf6573bcbe3b8779bec
+4a1b9c078e7bb20759a2d75e3a4b96827c851446c0261750b96aa5f286efe378 cf33ac7a3d8b2b8f6bb266518aadbf59de397608
+4a2c51c855f0dae37a9d35532cca06e82c6c00e1e35cdd5a1294ea79a0c89b8d 8b9579b77d705268991880156c59a6ecf3a00e32
+4a4ebb837e3c9883f35d0ecdc26d4bc76a0f665568b08f3e967096f2fd3fa537 502acd164fb115768d723144da2e7bb5a24891bb
+4a7732457b004efb34e8eb1405a9a26f66d28866d8f043ba3d732b4a640174bd 150bce88353a5b322b1600a5989e126cf8288562
+4a98d245f29b382f3bcfe34639530bb5d04f6d5074d9c22359b6c8a1d5b25540 1dd35dc8e28b539e6b2e15ea2a99d43ee4d2f282
+4ab6132f0fb323c66309c552e3d84cbe8a374258ac42bb51b727e02d881b04df 007e075337848055a92e218bdfe137451a4c9635
+4ac0a355c8c8c1d9f943292761fcbaf0f9a342b6dafa1ece1145903e2a07ab44 75d584305598407be4c53d42c85eca3cd58c973c
+4ae0fd2cd8725f4c1af5517529f28e608f1dae886966739d3a6889b8722e71a1 bde6fd7aa4ede6f79d41fa5be13567d12388c79c
+4ae3f76027b14d13e85f0f5498a4a697583940df57ac8733aa56d82900112a80 ec250c6e18e56d12714f9010e1b15e5feec5f473
+4ba2e6fe88717d069f04f6504a12052b38307bb7d50adf36b19152f50b533082 4a869a273478d93810045203fa44e2404d0e32a2
+4bb30c70803cdc8f5945d4f61437010efb53e992301ed4f406b0706d0edd26c8 3f84b7d30bcee174491ff0015816fe8831b1440a
+4bc0f94102287f8e84de5459edd04872cdb10a27652c49499a9689fa073566ed a6a793147da3bbdf15931e03128af729b5d0c4bb
+4bc142808884e472ee6cc331b132e66ef18f564d41efb055804ec1dd28efb3f5 a71586c1dfe8a71c6cbf6c129f404c5642ff31bd
+4bdc2bd639e67ab657405823281a1f4e8185eecbb81a4942278212d1fd47d941 4b09c08aefb8298b3ef03691eb8b4970ecadb1a3
+4bf5c64f79284904a0c0d54bbd380a63b7c7be4ded56619ac7aba0ac763345b1 d25c9bb2bf4c052f41ead5328a068f6828d75ba2
+4bff6d41212764361eba5a8167bc0168cb027f50724f3c53e00e2c435c92fd06 46f4e377719b3df0279e66f4d8d7677c7fa16dd2
+4c288bf404c8e93868ae54b4a9e93124c56c1c61184466629d03310065031e88 388e9c621a9deb2180cd601e30a540cdbd310b53
+4c989a75733680cdbe9068f0896f30aba0cb9c8cd4334e349dd815a2e6ae6b7c ae3a1615a8ab6b9a13ef98fb3f60efe727615310
+4ca78a50e220f784d9f0d792184efb85d2e78ca1bc8aa01713c794cb099c0cee c79dded300f279a6d8660b13f4fca3c684533449
+4cb34795575974e851198c515d39f43c8482c0f82f39518e7ea31b862168a6c5 ff5f93335a52245a405e12e9ad1091f4cb875b5a
+4cd983d076e60b69cf7cc6c645f66eba7df5032d271c9ee95d74193b8e3dcc79 795f44a7d7e1728199aa9ecacf35a55d3722e06c
+4ceb14f374b242ce6b94a527429bf46799f37071e59d43add03c79cc3f78f7ea 04e8824004970f6889b9df126b5db343013c82c9
+4d068ba9c35443df0a7723e96ec1eb1b0c86dafd4b6892a93491a5d818d944ad 98f3b5407694b1977ed2b4fe548831916e788ab8
+4d400cf49a7d61e1829d382168d34818c10f174e6141909e6f253142c48bc725 62496552534d8c5a4fc1839c20c33b0de4ba2b9b
+4d46d9719e425ef2dfb5bfba098d0b62e21b2b92d0731892eef70db0870e3744 e90810b8df3e80c413d903f631643c716887138d
+4d95fd7a1a738b7b73ff5b0c595d8953497404cb61a91b787ea266450bacb2ce 1d1be8ee22a69ad37b6cab510bf5f480dc6df779
+4dd37b9df07bcc7b45ce72a44e3f5fcaf5f0a9a5f3148963714eac4c99a60388 d03f567593f346a1ca96a57f8191def098d126e3
+4de260deee1620a10ad5f497cd446e3c6bea110a5baf5b82fc510bc6ce5074f1 c620f25d7fb92598b819001c8265d0d324275546
+4df8ed86acaac5dc82b5652170996ce459d39e3a441e9759b635b0bc4ecc43fd 4b22b35d44b5a4f589edf3dc89196399771796ea
+4e04d7c66334581183749a291a12d263492195f3418d12a06ad507705dcb8f5f 290ba2a03a1b3c4d6d76737df0c9a8964c0fc192
+4e731979325f7c7ea26f941615779e107442e05af41f66959ee130f0a6d0864b 37eb3fae578d0c74cd43af472a96e58eeb29c7d6
+4e7d3270a3ec5957db4fb47c36ca53e140252a975ae3019565280787952f28c6 8be856d8f8c8aab89f093583f091979aa2a4b8e3
+4e8061916861af6c1400709d97a34eb696efb43e9d862c53d9920fa0c71a76fe b580f2da89f867e9031f9d5dd8f58a20baf20b33
+4e8deea38a82e0ea12ef279bdb6669f3b31e1b10862ecfdf0f4a2b1c37ae14d0 96e4df3bdb0802fef56077e3f2b24fbe4b72b4a3
+4ed01278b8fd34390518ee993e4454928b40d85760ebc6fdfbc8e66e45ef7fa9 abb8a36f30d20e6f6c1ec2f5639c014e3cc18535
+4edae24c115b595fcb68ac98ea93339b9f2f168ecabacfab22cd6a8bab93114a f2a98a88cf3372bdb57d72c7136be6d42b5be1b4
+4eff51656a824127b4d8c5d9a6f4880830c91fe325fa8d213fd1afd6790affce cac5d927b51bf378cf599a02d38538cf7ab70146
+4f67cba8f2b69fb809b5b8303d82624e443647408af3466e59c10aedb2422e7f 0c7fda8e41f67823610884815c71cf47c6dde496
+4f79bfa3bbbcee0f835fb81706a3ea7264ea4ce29533000a24ee291748193d18 491442f97ec18dfdfa88912d20c2304d6ded378f
+4fccb0e68f9445601a507d0ee3d00531a914a409bd2fceed6c07bf3189c25362 1ca0fbb0838e8859d5928cfab9acd749e618b6ec
+4fe798a4c86598a3d39cc2e8e7cd4166f110d79e49d14fc0ce656016142b9ea7 018fce57e6ca903bffe9abadd2b8faf1e60c3899
+4fef7f7c937fa759ef00bce38f77e622a67b3bee32d6d98d50c84630e74c40dd 2bf93fa1071c3df7f43926c8e6c3bd2353d78160
+50085f68f619f133f16b57b7e3d18636eaa349151ebdcc0eac3f6de491de0828 e323fc5116fcc4fd39bbe3dbbeab1e2712052e09
+50141d4c9e46aed05c08d59a50a230e5507cf172166784d0f5bb6a1edca97377 c5696427b6d53a3f79baad35ea33c556884a410a
+502a0aef29fd792e608b9d0e312dec720cd1b1bbc41a22a5ff05b5679ac9de94 6c31ebf84bd6eff927f1e6cb5fdcb4ebf56e1e4c
+503680aa43ed7beea11c79ba04a43cfcbc84741e7e8b9fceeecf7eb3a3e46e62 7f12e62c5139c9c43ed54c3d44cd3a32263d245d
+505fee9e489599e4e4d0a3f966a981a7aa7ac60ba8c774b2b942f0dba4b60ca7 16ba02bda9682555bec1bfc9225921d7e949f16e
+50a8f69b7ad3d2fc73678d3b5a7d474998c04c83af503e57b2368a78e8e4975b 1cfb0ff4b6193c409e846a5992683559afd4be07
+50f19348faeb431dd64cbc6c14ffd04d1ed09f3cc7582d56d449cbef87a52b72 84a5237f8ce73ff4cdf8b644ddfa9f0bcedb45df
+50fae421c835c89e59c0103070a489a071ad663c992308b67cfb346218d1da78 f16c489d96a9a046cddb3de04198e96b65f73330
+51555c7599dab2ffe66391540f293df885d8f5859b17b8de3bb3ff7db3832619 a5ab36d1d5a5b73d0df5425e5b0ff25eda320563
+5216691483e994addc87468ca4d0e29b4ed2d2e85cdded14f5a0ff5604edf733 3bbeff3fc22b75c1a26f4ab9b64449b33002aea5
+52cc121e697beb8a03c666a7cef327534c6093ec79c3a53babcd5e8daf5beb4e a4e23d6af621573f5e01adeae0c34099f66df32f
+52dd20196726c4c9586b9fdd025af66adc7eb0373f5c62aba864fefb526cea55 64a47c01426a36cdb7e598d17018d5791e54bb97
+52e11d5ceb65af472f4665ebe312542aad2b6cd032093254539db22126f7f7e9 1906f2369d2906cfd4affeab0852b67c4a91ddca
+5327a985a8eae90deba5b4061d53c803cfd87ce7d5b6102b6ba8a440f1708a8d 32d5e64646c0c3a5229e2008e21a45a741e197f4
+5329dd2fd8557be5ad06b57882cf42e23d767cdc8a4b25e464fdb00890649e07 af795e498d411142ddb073e8ca2c5447c3295a4c
+532bf3caadaf4a236b802370418cfcf91668947474f540cfa8cfd54223435efd f9660220534f2ff457939a16634bdd5bc55b7984
+534f1da60db89d7c6279315acf64e3ccdd1d2892938f9bcd0ec9850f127bec92 38c513b9d1f6c19f191927003c5befd2fa70da9a
+538bde541f9512cf48fe308a110a279cd36ae036d3845db0bb7574832dda72f1 e667e1741dc49767b8563796879f344bc2b08197
+5398744baee56c62882d02aea2750298ac9f54415fbb62894d246d9c0a332676 94eb865e6b5e041f6ca6ad16ad102ece39ca3482
+540354213f70c39f4f6e39fafc65ea211a96e6925403a3857955d42097f52e39 8839d5b02bf316795bf8716c33efe255c3b6c8cd
+54139ddc224333e61424ecd3e0d1d3657422696d8215228bd5b26db14f63647c 0d9380246ef66cb3702c70bb9c1733fd200739ee
+548f39b3c94981fd63b03b135f58b8790e2acf91bcb754546c7ac87820fed04b 425aa0032cbe69c0ca70f1467e54e862fbc572fc
+548fc7632f9b3793ae8ca2f9bbf4c60d640973910cdf5567d252d5074dd79e8b 581a704418ba8f49517cbec2bbe44e281c2f5042
+54ac7856186ec30cf5e419d09fc567128eb24a7e73244f70f55b4860065fe055 74766fd92581310d08c6faff6998dbf307800282
+54e3102db46f8f1304dc4ab62763a71f915e4918ce4c7dba10c015a4f807fe6a f4e43ffc62d7d238e971ecdd8617d45ddac81caf
+5522689ad5f3e1754f7cc5daae217735eb70d0f68d55890333e8e7522ada298c 62da145b914438c95116b46e7ddf11ac3a56883b
+553b8a974a6f8d2a8cec357147161961adc5a0ad4b9f5fca91eb383d8043a16a f9d0c1adb25cdce71cac5f01e8333f201208a781
+561439ae8748339d28a29eb0497876fc8d47cf78c515a3c989739b674ed7df01 351cfa4a4983e1f9241542a855d53d93c951fb5f
+561c1a1f3786c440d90fa9d2fe5549290d95cf10881d334c11fb919d3c81b4bf 5d1e7929bb93b783b52bb144372985b8e5b9ba2c
+5629b9b5e602665557c9c7c31f9b2ab72e235ecf54ce1943c344dee027db8564 a1c0728d12b170e1e643f219fab53b03cd4e1b09
+56359702cb503f029d099bd51e6c54d47b0166dd43efa99ad2d05eb548773970 933cb118437ee8a4422e956197a8a4f09fd7e9df
+564017a60020eafdc626cf1a2caf4c57f128c489d23845e70e0c3c4dca8a187a 9ae72bd6780d6cd354719a1d84120e0690ffb5a7
+564d38d384f7e08bf03f021d1088837f3f4dd0c1b2c495fc3733c241e7586e19 58abd58a7f232ec3468da806682f39267d5b23bd
+567e9815220b48ce3e8b3710364d499f84be5e1beafbf04e4e9d0772839e6213 72ea4e962dcc9bc157391d5679f13276a21f8c37
+56abb178ac61ea5ad9e6d2e0eb7dd731c1ae88bd15af3c65623f8d93212b48c1 630785638fdad6d35d47213ba2867a702777dcd5
+56b44edb90e571e38446121cf36270dc284556d8b5e1150ec03f9fb2f9059a58 83f7e7bb0a88634bfee2c4cf3434a2418c5f2880
+56b5280262679ad90e14567f62e22ebc108c9765f8421beb643e2ce0499671ee 95d21f5564cb13338520ecc7fc50b55866a32b24
+56b6ff774f9a813cb058c31144b98d4254329cee2d00e595fb26336be9fa9b96 b376cb120f0113294b80e7fd540d5dc197797764
+57ab87cb9eaf3afb28933d81fb2e3ca43e699cc1f02da50a56c3afef103b7673 ae31ebb7078c0bb2fe5a3dd697195e2d9068dd92
+57b1d9ea18f9cc3a9c596ecec921938223879a49c375968ea034608e077283b6 76d50675cca89a89b263f23d2014c7de7f59565f
+580474d948cd2ebd2e5ce7a5b81b872d87ba4639c1ac4c0fa7a11a8eddf9827c 412ec4e4a6a7419bc1be00561fe474e54cb499fe
+5818ece015b48860c7e518f12b06399b60d7868b92c3fdea05f2d25d0077e476 498bccdfec1fc223c27c0d84030ff419058e452d
+582841d0c8af7a4d254d30790f1b07b3ac8f3f1c947be0a31791e84e307da00d 9de0a701bebd5d07bbae73bbdb3f8c39e204bd20
+585d35a8f1d6c41b0d48ab5722811c0daa8bb51164f24d77bb4459f95cb05169 44181c23ea6c39d51a4b481dc59ecf2cc3967e76
+587d967295661afcdb5a1a7162f59927c824268d19cb4d9b81fb5962d55b9bf2 225fe21522a98075bdc18dae90ce459f797ac366
+588e7ecc3c2704834f904a41fb90bd1091ac1eac8b5112bd60fb2382087aea82 9a827fad4fddacb757be5c6eb3e8c5f323ff9952
+58923faa87c7d559d308a114ec2b164e5d6046c889420ed1def6eef2d55106a2 785d3fe8e7db5ade2c2242fecd46c32a7f4dc59f
+58c150d2dbb9c2ee86c46b5124a9508f632c1fb35325d8a8fd1c59dae3bb4b42 6dafd0566786a417cfe1131ab804b162fcc79d15
+58cbb3fd7fb9820caf0f9c15b066ab41df8de6fdf1c83d72f3bb460a9922b2d3 7b08db51663cabc4794305fe36fb5e09c575d883
+5905b697743f4998b3deeb3da29d5270ea6bb52c90d4dadff6f28f0f72edb968 2ad730b25892080b9bcc990c05079a8d605946d9
+593b262bd7c0c40d61962a36f9e9983402bf8b4b585a6245a47b161a31193807 7c3f1a8504912d590d12048d32cd31d2d75d69ac
+59a1967792d9b5338bfe0bfb7658a932bd7a65b3aa8c8401dcd6f149bbe1e4ec 046c8a0d9a405b354f298c7a73e8755081a949b0
+59cc00acc478955e567e5d9eb9b888b8b538165673147538b6132b4113010171 e815adf588053e45a5f5410d44a586f69e66c1db
+5a025657f0e06e135eff8536b93877c7725b74f2317b6844c002ea135797c531 425b3cd58cc77a921d6970967aafaa4391cb7f4a
+5a0eac41da13655a7cfa4f4586d152049b9ef83b5cbb41c290e3529a856c7fbf 2a0665949d7f96b9776b6f36f60940351b47cdf4
+5a1fa5495a4229c962054c7a09623056643e55db9601d584db08039af54bd01b 33fb00b13d006c5497ac0152361769b650397dd6
+5a2d5699fea33657b42ba98c22b7898baaa0eda205a21cafdcb7e0f94b07bb9b 9a03079b8a8ee85a0bee58bf9be3da8b62414ed4
+5a417fc113d54074a22bcbf157923fd439fae36ef7c22cf96e6a99542809495b 200a9469e6f76a538b8539d671161a573c3ac91a
+5a48249041aa1dc714d3f4337776768d827320553e118cb4d99673dceb9c975e 0ebbc36594071221b685805590415d74624f41e3
+5a4fa7cf1e417c1032786472b9906074d75df51bd0a5870b34125d3e386d5b8d 4c67e2e95f0bb6f0e214bd7bf052f914d365f129
+5a9353a7576920c090a94ebf978f786d43418c111468ae2a343eb561324dfdf3 3442b9486ece0187998e802a3e98f9c97258133f
+5a9391ddc692a70f59eb4640f4f221bbdddcb4dc9ce0e8abcbbfb15ccb9a1513 beaa5abd55600b276ceba3363285fadddb906433
+5afcaa3ebee991ad963e8bc7d3d947da769dea7095973e5e6be1254138855e2b a62239680a8bbdf85cb5602325caa42abd141d66
+5b0f9a00dc1ccc48a7d52f512338cecd3f4f5b9706c227c5f8fd158419862da2 c63cb6a816126549e95c8585a65942981ff4d634
+5b264a6c0568f6e60ca657ca41b3608fca3a6ae87bd77ef9635edc75b5e421ec 6d681ba6396167845281717f4ae90389f884de48
+5b92057969cd5f96af9b9f1504b532bd3fa93b9dcd5d58c4d650c3ff640e54a2 b8d515dc02a663f0d260c69e23c6cc42a363d5d6
+5ba7253f47d390ee2c7c7afe8fd9a963a7a2674bbdadeb9a927665c9246306c4 e04529998989ba8ae3419538dd57969af819b241
+5c05f5112a30ead09ff2ff553056755dbf568c06127dccecdce9e2696fb41398 634a64b10a627e64f1a4370d085eed0be242b6ed
+5c14a1005d727dc5ce74effcae1dadc76a77533167c76347e04844c290aa7ad7 4c3bbc923330f9896bf5789864212cec21a8ca0b
+5c18ce500d2a3c3085f74b3e68dc4ebe703a6210b3343f50796cc15cb18c7f8e 20e7f426c3d56ba9e4b55f31a61835dd0031b464
+5c3bddffe44de776f2d7715ee97a62b190049e623d3b278ec131336f0a1cdd70 2506dae36e0f9cab6f81fdd825d808a9e4c12dd1
+5c54a41a0ef2f1b7f505d12cc7902120b6ba3ada7e023307792b56537fef2a49 e98f74e52cd1231424378d9d26ea40d1ee4d7fa2
+5c89f33a737d292d062924589b856045d9d24630fd3bd1afe5e65e53166cae89 192678b55c30d0652356a5021b521f8e205f3428
+5c8b348f9e152d98efd79d2990dbe4c702eabb06d4a1427472c1435be0a13cc9 50b20c4bd550428a6f33137bd52db4b69799f1ef
+5c8bea399f78d3d6a037a41cee2e763d00024180b66f2ec738d443b6a3dd7081 fd899f45951c15c1c5f7c34b1c864e91bd6556c6
+5ca8959deb2b8327458e0344523eb1ddeeef4bce03e35864640b452f84d26848 8e73b769e97678d684b809b163bebdae2911720f
+5cebe9f8fc63534c919bac2f0c8695c707e33647d44792b16e28702d26e0ed4e d25d5d3bce20e400407b010132579541d116a8c2
+5d021306f157c028290dc5eef52bad4a3998b50006fcaf283cad4e6918157c6a 54b9460fee5219cb5a57118bce066697704ef3f8
+5d2460e61d9d5cb4fbdb9a017d4761827f339c067ec209c8c44529a5d9c93239 36f0f61fc8578478a9dc9b33a1a0d07cdb98a5e5
+5d2e103059f5a5629d12e4bbec2e6abd1d89f225dee891dc630f67ac331209cd 96f34447e508c58b5444992d4522d93687c9ddc9
+5d33684397a6875ee09f048a93202b5ddcc468807ff5faa382f9508833af38d8 7ca4ceed50400af7e36b25ff200a7be95f0bc61f
+5d4e078cdc4960b27c7a0f8470c9d797ae21983e08efc65d0cf36583737b183d 0129895fa52dfb06cfe4f1f456d57d8e16453686
+5d7a52475763e50597142ac2dc2d1b33d771ab83f3a719fdc0e7b880bce4513a e589dde541c522ea4096a32308ee1db9bad53079
+5d809610df93ede304c638ccebbfb2aea1010020d5a04e25836320a890e40381 b20f1d8a6eba1c138a6a41206ed45ba0dfb7323e
+5dbb1fff5c0094b31b25b4635ab9fbee66d65fe5dda47dd0ac5f01dd69a84c6f 43da5ec3274dd061df152ff5e69853d562b01842
+5e0bdcfea7ab39bfe79b8e00a459a68f5b60b2b0e7600bd01c09a526734cc769 399b7d5ee364ac49da9ed64fcbcc4ce2e0badf56
+5e4e3a2f07a2d26f7ac4ae12b4157a7c6201141b08a04a296d5c3d32bfe9d4cf 5720bbffa1377642eccac7fbc34b35a4b44fae4e
+5e53b0f3e603756d985e82d66a1186c2467f2c5b027a1f4baae651a5a68d1aa0 c2ef0cadc189a076b8ed0d855da1e58502210507
+5e60eccf824d865dcee2ee9ad6c947207c3649dfc0bef1cb940c6d05168ff55b 627513e78ae0c8dbcdb62e371ea674495c841aca
+5e7c2a3395be2546d41331f9e310217396252fbff62f139e41493df6f47a548d f9fce2b42184f2181a5ceff90dab05f7cff12697
+5e92f55d41b34d254a0fc5e5270c7f78ba48d70743484489a2e81e0c9b420527 52f2390b4308fe51ecceecbc35a87bf6e74e9aa8
+5ecf05b5a0c0b45065f977149190b13ac8bcb18342c35cca02ad93a3e7236ee4 5213c69284b521c5a60ff677baadd36c97a5b333
+5f191eb81f39438560c516e2f091de40fe3b7e090b89b854d013caf446b0aa8e 51fcd3cac86ed8f6ec215146b79b0989d407ec48
+5f45642bfa66a6eabdfed22c7dc5fc170dbd9a4d93fad22e402b38cbbcc28444 bd0d32c74ae66ddafc9c875b391717e1bdfd6f8c
+5f45a18f90f2934e7c7985d05b2b5b3584886fd057c9202f26f562d6c3080038 112998d425717bb922ce74e8f6f0f831d8dc4510
+5f53944c9390e883fcd33819062a750cbbc9b483c15b554eedd351696583d215 f85c76fe4882c7a4befdea9224fa8c01574fdfce
+5f5f4cc82137a85080f6995d5fa9ed50980e07b0d68bf96c0d8c9834f8d4ae64 66a4bfac99c07b79d75c015aed8f5972f0a4a336
+5f877d011f066c0131dd3a53b96dd2f776651a96c610e7194fbc07de06d9e851 56846420552d85e7d5e8a19109594ce4e9792b7d
+5f884e140db9164254a551fec3b2da3596f9404b80a9e322ea01ae3a46f45247 6c1a2d8c39d1becfee3cbc738c9a126a29f98c92
+5fd43a5fd154026f2952c2974e251c98989a7350228cf84f27c58f0925eccc50 fa33b5202315671c762ee1612d5d153c55f728ed
+5fde3c5b024dee434df0642b1f2068bd65894a9fbc346e15d7bb75608f948ebf 32e41390c34dbd3f59c8cd78aa18adcc45bf57f9
+6004573477242428d7b6ee1f642da73ddc68de2218e36b8b037b068bc3de006b 0d65e1969a5c01a5ec64b6950f17f132bac18a2e
+60087c8cdabb0c7a8fc1cb6134747d82f02a2a920d4c2eb7dd783e7ab1f37850 b3715dc1acad54598fea69d118c992fff7ea4ee6
+60b2cd28c2b0b58ab103d838cf2ef4522a1c42490cb93e425df77f39d7105e03 458c271d8dc57f36e3c6824d54a7a4462bd3866d
+60c64d140395cf067197db3df59b33830a8ca7410c0732a1c1643dfbab2ed069 2ce6f8018fbaf125ed50e66adc46ff4f9d0a9288
+61068c6c7b2f2254e55c83317eadfd4ae210a6dd362dfdf4925e1ca20c345d17 b231ef3acdcf396dba5f83f89488be7519da551a
+610894ea28aa99982aba7156a1a6ee0d18652d43f7ed7b2dfd89fe1252b9e645 93d96a8a0e09ccd1526ae4e769b5d41c239db3fb
+6111c38997cbd2dffd851ad4d840c9dc45bf992d342b827bab5114ff519ab57b 1a2e9c0f19f0143c69fa2c4d5d95da3d5ccf1357
+6129f1672465ee7b9e2edef53fbf3846ab5d06e8e6a1d7fb51e31666a8b411f8 b0941f9c70ffe67f0387a827b338e64ecf3190f0
+613faf6e366095154ba6b9cefa47d7c3a664401174497daeba14a13c3e444fc7 77e9dca7958f1ef5a01dd17de6e5fd1175a13ec6
+61489e9e831f1d9001084d39b79f964c293db8620d679ea3596673c8a326446e 1810dff58d8a660512d4832e740f692884338ccd
+6152d01aacb9fd63bdb908a5153ec9319bc837b50a22322aed77ac01764364c2 32d6b9111a767ee0a7500f132f797280dd042367
+617e5bac096ddc00f0c17e6dcd6a084cd21ae6d4ef7369ba888060f3c7e0a988 2917c22c87060a43fedc431c0a125cb15c1e3bc2
+61894490bc16340ae5eabaddab9816b93d2231629f5c5709f0c09d4c9dccee4c c985f0cf48b852798f4c5752da322a34f6d386c0
+619f24a7f37f8ca922c83b3a1b9a384eb6a444ff3a2a52c712f3c60dde6f24fb af55d0cdeb280af2db8697e5afa506e081012719
+61d09b305f785a367a8e1cb85347dc38d4ae9de484dd3f2a5bffcb1af3b2417a 803a6b4d418c4b2d7463bf23a86a09cb2359ec98
+61decb10e8bcf036b7b0668b5f1e158381a26ad14f4420bff686801f9824aee4 1f8435d47ad5fcd9e92a80d1cb2b8836fb711366
+6257fc48e458a95396b6781a738dbae7866999cddfccef63eb16f0919bc2cb82 4d25aed8f9ae7653206031efdb0b682d62ece767
+627d6943fa03029b8c4d730ab2acd266509261d14042b1445c477505b29fee21 3ae3c85110b04ee92aa528c3da0956391e038cf7
+629e9c4fa5465689d98c29fe048a1aa7f52da0235c5d67842322cf5d183e1175 b72ca26740c71371258bd178188a3d981e39701e
+62b1222084376703ecc7737181def068375c47640206721dc4674491b8980a5f cd874895197adf33e41cb14cc4b5fa2fe7ff758f
+62b4aed734e93df5fa848ee90fcc38fa472e13581d05fa42f11f6aa8a388da89 4a143abf80a82b6e51a3a32e0be40b4e559f6a17
+62cc32311fb4f113536d2004edf25611cacaf657bc1f59552032c7cae29cfd6c 65912dec2c33ed1cbae320dfe24f868e10f30ee1
+62f462260b2bf90079c917b3ad2cbe8eb2c8bc136512961c839ac53845f7bdce 784b10b4b075a60fa18c703b88a744a30fa7eb3b
+63071afc7376250fef5787562b2463404ff4002698f92a61a774bda0a21a6ff8 22b3e83e3cdca53a20808202a795c73fb47880d9
+631b70c3c1335f8aec14d98eea4a36890c3a00bb2367761e8f7d1e209baf74d4 1b858839e1f08792f2e5e1165bb4fcffcc95fa86
+63fee1d7ec8c84e8854fe394dcd134a633d8f3de7c6a5749b7c12cb26b8912cc 99228417f71549fc6392a359d59d33050c055733
+6408a234856eb88bf60ecf977da417e65d38cca482547247a143aff624e66872 7ce66fdc1f7778999b3b3b87608771eb2f5c124e
+643cb3995f617257573a639e4b8639b46bd96335f61bf6d98ee3706397c75544 47ee6e20700957518dc21886f769ef415a592568
+646b7e4bee30efd073aa27ddca866ae7ff72bb284b035acf721306ac5dab00c0 56f77b95f87e01b56f732417c1d557f0b6dc0be9
+64887aae9bb21a35ea8a484354297a8a079b3bc20d11d253b71afc1ec36b1f74 0269c3f82d4a93b859ff58582b7c6f1843dbbbcc
+64dab4ad3eb854bcf575c7d9c9e9673021ba74203147d130a209ec2f04a98715 850cf44e8347194a57fd1fb637f0a12f1838ad38
+64f0605de2dcad18e95b1cbf6cbb85064f916406f63e8f76c9c223a4e59d1e55 eee3d3f684fda02fc8614dc4ea5c399ee71e7e4f
+650f6cd874aaa3fcfbfdaf49d6683dd6a3e9e3e45d21987dcd6d67d11bd12632 7cd60e59edbd298d5e2790e2e62aa5c4dd55e8a7
+652412419a24ba62a1d897f40aeb80eecbf873797b04a1bbb8d71918653ef65b 6dcf9bf7541ee10456529833502442f385010c3d
+653aedbf79f7c6e9377832bb81f3d408988d1ef51e130b08c29217a9a53daeec 4a59d93f4f618c2b2ede1b21c807cab24453056f
+656dac701c40a400c97a4d145190117d6f226b41ac39801032b5ce2f8a67881d db31e1df61d8586c1992036ce4585a69fa82076c
+659c01981f8ecb286bf26d7759cdf0e59f3250dbad69c5d74e38c5ca5c8ebd36 c1052be270f379ac8e245838c8362f89528c8e34
+65caa3413382890c53fa78aec55069027f1918985bf6e62c97716cb846b74f0f 0b59a10d29ac9446320f6b9424e427ca7548af77
+65e0543438a38463238bf25be5064d5e6a9a9b80f0d67c3babe8bac7a3f9b269 edf57b4b98fe7845c0f37b6d261f15eaa6a27556
+662c71991cb89a89f8b0d3479839daa1c3837455808983dcaa3c89131757d4dd 408de023c9bae4e7cbfdbf7ea65f0e41012f5a4c
+663848827efd772f21ab7953f2c3edfcca12400bccf92de5616e9ef57aa09897 7ab2d5413945768710d357e69ebe6ea89d0e0f75
+663ba0d9c53f18f52af699fcc1f7e1600098961324a48f3e264b98718e189498 c9c7b0e147c44682c9d4a100dce290c14762ff85
+6675306226a1869a0e696a57ae9f8172511d961199a2ec17ae6827e39e4304c3 52e47ee702754a385a930079d5e127486e52bf68
+66a993bb9ea76040f448de0e3bd65d1f9d88dd5192921cbf12f234c813466452 75e1e84d121120833cdaefc7e764878b7cd7d602
+66c5b943797fa9a7bfe9194d4f1550a9c4ad05d69c6d1a1be4e97932cf06a813 4c9a39732942f763191d47be51b414bbc81bbbfd
+66f3606b5eb16bb5a34fcea47edad9a68a54c2804c09da37afbed6bf512d5221 ac8237e18da19ff375f051a973bac91d6dbb2f56
+66f736d33cf07582c5d6366a9a1bc39d39937714e729dba38a7b61faf2b156b8 67b430937482d4d1d877ed3bc4a6ee743028c3ff
+66fe8385c6378bfa5ca5573bd0fdd773e4eadb0e86416b483f2c50c839859ecb 41bc8c69075bbdb46c5c6f0566cc8cc5b46e8bd9
+670332674a169ca0bb0f1d14bda945c03bd6687b756b4a54bd8da9208403fd07 5e9e1f22cc64d3a31b37e3d0c865228a71c4415f
+6705ab009e12496e243d5fae60f7ce6c77fe68325a8818ec75ff317ae80c5e78 eba91472284330a048142950c1de90afbbbe2c0a
+6727e691fc664f1f28bb23cc30d6b84c34382029e56edb264b2498117398f8fa eaa63e1496702ae9c36436969edddcf7e9817545
+673e079162dc9e99a8bc43698e05492357041a3a661b104d1d40fb37e89cf965 dab2ff4ba3ebf8cb58f5b52975a58c308b2ed283
+678d331b936a897f3ada21a29b403968b075611588c9b176abf5a05d415e275e c8d42b9bd1b6e45f48feadb5b02e152c5e5f1f62
+683b60d7962844415b532765aa212699c5414b0833974f292c233c6c8c380dda f2b93d4124d2a46ec7070cf6a40a3863e2c15556
+684b9226d707a7b783f0705986600b2c9c7decfbc9cc72938cedd113a8f0dc83 7e44c57fbdc0eefa79348d25870369b2f4b43ed0
+68a467e533bb9e4b2c5ad712d0f8d1b484bab740dd667814f1c9cac3a4a30fc8 19af3949ca8ce2831049a4755bce3091d7f4bceb
+69136323b1d7ba81e8cad8626dd83cba0c41f780caa43421f0e5f2462d78acf6 cb3ab00e68da089aecd496ba4e39b1c706ed7c68
+695bdb545f636d454b4825effd29db96c46d81772dda6c104f97dffe2ec509f1 4e8cda27ddc8be7db875ceb0f360c37734724c6d
+696503760a18787240ba52ad1abec3be6517bb802238e9469b3a8999cbd6432c c07204a1897aeeaa3c248d29dbfa9b033baf9755
+69c1a2bb3151164052ca0435160424d460eb6268858cbd027cc551a174a52785 5fdc7166eac68b0b0923439b8685a29ff4c2be27
+6a23c1c1d0570cfa36151e678751e16ae587b0a8c262efd011a4716555b345e2 4d503f884a241c507ec4e474e3e3463c2e394d52
+6a28b5afbed89366932ea57b68cdc739f347d242f0d6e6a0f8e9a794205a5213 11fd982b055012bc932fe17b6187629555000b76
+6a3ddbe9560f1509d2a15cf4f580ece49e83b83f9baecbc0f5ea512ee93e63aa e35ef00595a0767f16bbae5182f7dd1c07138834
+6a44891d48bbbe85ec77c332a4d809ed1d7bdd39be781785d7e8b4b31a97f586 963f3cc863ffc5d238de5ac3f425df6ef1e3e3ea
+6a4f8e777996a56c3d69559f2006bc0f0a990130aee3d2b29aa29fb3cf7fc437 1b1e2deb5f90be7f267af8d3d335bb47d648944e
+6a5f397f63599bff44400a30cf482d44d5b76808fd9b24c043e8da3909b7971d 85a843cc1363932d8cd5d1e88024b9357f0d5572
+6a837864fb44516a249c5df72b8674fb3f16107af84e2f38c9c4f2ae7bf8676a 357bf823711de56d93f51d9f4618ec2068d2244f
+6aaf452708cf86455c8ebef269c0cbcbf873993e9cf003e3eaae8569eb997da8 a1a73455eb3d2a9571208c4d4af74f4ec1b1d161
+6aee1b075ec6d9f63ec6ff0689e80f4df81dbd2c0c2bb17a8627d7f0602345fd ec6a1926d4465e61364a7a8450e8f4c86ed841f0
+6c04b20ecceba56f5ef3dff0d5b93a551d0c6b56311599a3633ef16c3f2604aa 0051f09759ad432aefbacba58fd306dba4a13f79
+6c14ae62c1369df64a8feb0dc01acc99445e13708726f5687343ca15185a3e9c 86dcfa927930bc90239e647cfaad5c7706b9f609
+6c18ed11d243aac62a14a6863430458296354e445f0ac7927d5231161ad90033 c945829d434378a0bd235640ce08144ec35a8cb3
+6c3b3ad04c2ed7bb11f59cc76d0a980cc087bc6b0b27d96d395af4ead3950200 e272b10388ebc4b7ed9fe2b42dd46e367193c218
+6ca4b09e9c92f8cfcddf218cfb39cd76062c88e0ddb2fa387bce031b902247cc 0a902f96c80acaf50a534832e1892cdc99ac6d31
+6cc829640a9596f33442bdd65ce68aa387caa63b8b0ca914a3af4b8143ead15e 8a6be5524f20162044c8e48a530abdabefefc083
+6d0df366aa3aa46ebc26fd4099838660b562e9d140ced9631fc091bdd5f72b32 3ba86b2be1383df8e6ac574538dba7d1d5d0120f
+6d159afbb9fc4660e17efcb7a3a0e1944cfec34d8f8b7d1794faa8507e5d42d0 19190397b94cd8b56e6cecbcbb4ed24c3d454d77
+6d16425f59d59f573f029a4128206020fb89e849280d0ef894b8cbfe38803af6 c994bb473428299c9d568c99805ff1b49949aef2
+6d429bf11e88cdcde78b43bd5227e273911cfbe30b8f56aa170ea00227811ba9 82b1db3b35e34c87862172bf3f4705c4aca8cc98
+6d47966640b12cf618c9bac61c60c2cc245ec18723ed4056fcb5e771e313b8d5 09cc79a662b98af20006f2601826d8fd6085a37b
+6d5fd291bb0f67444e99ab492f1bf1fcdf5dca09dab24cf331e05111b4cfc1a3 fa49b077972391ad58037050f2a75f74e3671e92
+6daeb00c6942fb4696f08232d22ca0dfa56d416450633b39cf491a18c6e2ef76 e8beee4c04ff7775b7180a7675acbe95d5af64f1
+6e22fb1278de7b49fe6b298192ffcbc3a2b1e8abcde77b9cb2ff67f3faacf3ed d9eea640862b43efa179924882eec554fcbce684
+6e490b63a52e4f2327be88b4b91812a05e62cb2500f8dcbaee56563803b47fa3 241514f3af6dd20d6d647db9ed37a6dc21d9532a
+6e657f9f175e434bad449932ac6211bbb129f4df1c9502f4ecce7a5cc1fb5fd6 2525ae8a3e074a4c65659ffaad29315fdeffb698
+6e6f43fbb89dcd1526fc897e2a42008f8af30ed890af079de9961befe36ed36b 1699efc4217d925c50abee60d196d9f6e738a72e
+6eab2abd4d5c1d1ffe7e473b3c8b2889bc8801457737f43255166f3ade399992 f68f0b1b936c397976127b08400c6e30c07cdfc8
+6ef8ae778c5f998b9beae6b2da18a6ea674b268a249c3b55fec38d1b8cadfaa4 9475785cffaf1ab6216a9a5f1e1b4daeee718657
+6f0055a8c0d2d3b66d21dbc546e605d7097533d949fdd85f08c5874c01ef1723 ce022069960b6347b20ee2f90681ffe7802d953f
+6f11d93bfb269ee8c7a506178f60c430abfac5d424acfd9c0b0b27b98e6ab49b aefe66d192771201e369fde830530f4475beec30
+6f26102d332afdc855dd129b9486952f8c6b7c071fd96a6f1f762c95a79a3d67 064301cc3528ec3bccd07dafe9af824d87a50dba
+6f3876830557f53900630d24eb67e39e64f116501ee781f18d5d3c1c823f255c eb815f96b04732b893bc17c209665b211e3cab0f
+6f6ea36739eba39758b45f4a0b0e7ac3d5c3facb390751101e2a8998c3a6f190 0f39781c25a06c1d38877adef4ff5a6a5f67f1ca
+6f7d74e7c76bad67c52697562414ac40cce152a6197b3621342f4009c141ca4e 556c36fd15024aa7e888c3739d74dadedc6ed424
+6fae137ba81d0d81c2a85759b99322e7ea8103bd7c8b85be3163b7e91e18c125 cfe96f31dfad7bab49977aa1df7302f7fafcb025
+702a6340ce839f0cd2601cf6fee0e4bb5b308fc8c0a731aee40d1bd1b0ed5f6d 4498654c7989a59db451179ed264ac43d3b7131f
+7030f925768d9beb65654ab8f436e3ca0a82b25eddefd237bf5a26a0441c2aa7 814889a078c031f61ed08ab5fa863aea9314344d
+7039e3408aab6a7cd4c16bc8ae9fa81407ac415c427466a602bee592d3ebb243 0238a56890c6185070331056033f4dfe61a275f7
+70bc69623357648bb7ce7526e92d8abea0299a9273b9fd28fe5f9ad55048445c 73dcf2876fecab9adba9ec9bc148ccbab431af12
+70c467f2c60c4c2b12e0a8343c454edcb457bb7daee059b6f7f625db44827ded 12a30e261b490439ded615bddd7d6e3a135fcd13
+70db96a9031877b2d11791f4a75ee2e2db4c6c657c9f1d5e30975e69108ee95d 4078e70ce62371860584b6f42312097b1fae94f8
+70e04bc26c957ee1d57dbd0ff0dc8d8086ebad49e3dd4fbc7b158b544eba4b21 c20cadb993d1ade2db86def0b3e107765d2ab3ee
+712b2b96ef712f696523967fc471a5c5d446dbefd9bd46853e6f3030fc4e42df d1ea30c399629de48ac3fe68869a21171ad131eb
+7151491618209e86b24c84d0bf4962828c4b1ece1924a44ee1d3cec82d0c7ef1 8b6f008edad28fec6e3732fce75c20b0608e4c46
+715588dc06122ab123e11c9590ef6fadb817b103da857e89d619f98381e194fb 33458aee86009abe87f4f182120e0a01dd828ed1
+715ac0c5f0e08622ce2dba0358e6180103dc26aaad65f463fe319ea28227c991 7ce510638eb4f25e6d74a788e941e27557831291
+716e598151cf540036a73177dbf04dff847cc5f0123027bad6111a2b747d6403 4b44ce23b4357ded8bdbdac545ef49a39d5bd6ea
+71fb49e1a6feda729eacebd50518cd5e0dac988101fff035bd27e66a2d1c020b 54b5223aaa8e0c973ad508f4741e2a96961c0f3f
+727a5310c154efc27b636efe2503aa36928392b543a121fd4c67e991e53be3e7 efdf2bd1d646b85961828b897633374b09249fcd
+72b2b1341efde56221a00b934ea70e0e0bac428cf3853f7f0432371cfb2694be 744e1c78fbfc9438ed8c947aa87beb000fe4bab9
+72c472319cf7d5d59bf9fea9e90b9785d7ab39340003fbc68619c11a9e583c2f 2f205b20fc16423c42b3ba51b2ea78d7b9ff3578
+72e5af4874e5dfd926179a7df9b22f5c784d0953c08f52f57b74ea4c429bcdad 3e337843bf57f0fd5b29d56e8678d23bedb78b49
+7348a064c19fb5f536cf9df91b0241da4dc493b2e4eac9b886b7de3f96b0c414 ca4bb51f66396b06853c444a1f3d259267a7bafa
+7350dbb12c8f696a5c1f758da310e02cbca318c96c7854e13ed9383a313c4a09 3c6a4c09f57517d0670e5e1d78587c643afb7d80
+737b6922276ec4b8ff150f102403c48c4ec60e6b3d4ff6904261bbe2ba2d61d0 552e23ba566d975b4dd48163b91021892504f021
+738ff86401dbc5af692c83e660a4d510603c3f36e782a1a32ebd0388db6411ed fd4959ce7510db09d4d8217fa2d1780413e05a09
+73a5d42076f213bc0ec6816f8a51e3786d5fb0eedb6d672b9793d582eec8477f a7c60cfc6d3af29669ef5cba9df325719b9f30cf
+73b4f3c4f3182e6c8dd2c98aeb2c7811556538e7673e4b325307c71685fbf5b6 fd093bff70906175335656e6ce6ae05783708765
+73de6bd126ba92253a3968a1081da3bb585f9010339fa8b9c02ab1c37f253bcb 6522d02a70408e526425ca1579215bcb6bc505a7
+73e67e592fa176648ad7fe7a65c8e3e782902ed1e6a2a03b2fdda1ce0648d705 0db888dbd4366d6fb2e5871a6bba39530e3bca70
+740441a445c2ab20d2ba91f9237116e39c25aacb7527bff9252a15a376ccf1cc ebd94df2050b09575346ce82a94079dba504f057
+7436189d4ef74b65e205d88fbe33f8b130e907b7951f03e12366ed6124342aab 88c3c7230f3d49807323df8f01546b8a7fdce9fc
+7453e1303e8cc0bc8fa0debfa7545e7f6b8b0036ffaf950c1d86f86710ca3bcc 562c86cad456bc709af6982e24256844661fa5c1
+74a75a34793adfc5dc91b0997b113e820f1a08ce3f2582580b21a63a22db2dce 977c1793305b307275e2a12335b22346efb342af
+74b69ca7e493351440a95d92487789420cadf3ced93cc3c4f9451e4483d7a1b4 1c2df27a59b90ce934bee51a95ba617efce1c77a
+74e28415489b8056cb9537fc207015aea0e828fafaacc5bccdc93b2f1609e479 be01c47996d638f76c9149a90a25939f624b6cb9
+7509f5f98d13f7a093342e712ea0876cf17e747f8e2d04738d8592401da74586 32a550a0213bc2254d0e46285d7df4e154573009
+753ddabab8ae9c1e733cda15e8e3c83dd43f5a2d939ae32cc3b30b0be1e91f96 4d8d0fd9cb6045075385701c3f933ec13345e9c4
+755b0be6d86fcd39eb376f2324bc61103ad7268463b6eef46314e079a4d2c34b db31c0661cd0bb4fc5104aa0f27b1986ffbf9214
+757f587c2ab935eefe8ac8e05e3ca862e9bb838cd525a6c4402839f532713b5e 360b107978af0651ca59f07430fd71ed52150e4a
+75b0ed5f4a2d5c810f34d867dbca51db6a596d4739abfaf24fcc0f05d99097a8 10b759e734e8299aa0dca08be935d95d886127b6
+75d6f2e4eba19cf2adaab6f58e16c9e3eecce4909efc53d6690f865a4f89b53a ce524c6b3a984455f474968d01ef86156e92db15
+7609c608c1097270356a6fe336a8756ff124d4a9c2e941bba26a6e8c3becdeb1 2ef4e5d838b6507bd61d457cf6466662b791c5c0
+762099bfceec4cf75e4f0cf913c7ac6a0ba6754249fb45579f7d820466045d26 933888647a5d500b90c472b5c99fadd6b8691c86
+7655c1e83698d9a5d2fa841438e1a0ec815d7daddbc7a21a7e930587c880a2e2 0817229bc1a1fd88bfdf4f35e2ca11172054756e
+7671193d6cd77b8b81681438be0bf15db49ef9b60f5389ee74b907d7254f403a 5069c192113c59f791d5f566c0a57f6714bb856a
+76c1bc10fdf66c17d7f9c02a12c80cda9d1f14fcf9a467e85160993f807abfa5 7cc26c12adbe5ab35986e2e57a572145c86bbc05
+76d5d21efad0c83f17a63c2a68ebd39136b56fc25f97e0697ccc6c73973c9753 81aba2b4fc907644c29735ebcceffa4ce01dd23a
+774fc4ddd5efeac3ba9d525a968612df8097a9c903ba0b5b33625ca86eece2b0 9a4973ba6bf406aafc48b98677422fe6e54cf764
+775a1415621a751174ecc0bd86be251ae64c305f39c0b6423f8bb5a2c4cace70 60811b0baebdec5b8663134d8b754b912039b6c4
+7771d48bc8502e2b23c5f87c5f82b446f90060d54a8094d4dd829da9cb5d716e b3178627077e4c6b240ed61c4e9a3f80e42d6c21
+778c14d7c53286273b58bbaf5f42aa7ce8bff5c63737cb1b5d3dd99fe43fa32a 46a29b40df56c1885fcc16a59a95bf319048fcdf
+77b15f8f906ba92609d8e70d038973964661a30ae5a1b9ab52f96dee694459b1 0ba17ded678e92f9150df14c2e9a310ff5a79baa
+78247330cfbfe12044d4a85428ab774b9a88413b618ed91d812cbdb1694be9eb 7e7c39379436c6c048670386fd4d470444232e30
+784989941709ad0a0f4a6e7c9065f081561dd146e7c84a535a38b8c2b89389e0 84b9cec72cc98dea3353325e42ef78b3f7e7c79e
+78568215f27ed3fe1e34ab5c944349511304e3f2b9721610599b3fe24ef90f60 42c07750c431b6bd77c6a9e1ff4997285932e352
+786ea2ae3bc3abb0f17a985bd588c7e95934a332b0ff095ded0e2192e29d42c1 938317c9319e3280b38d705ad1cf74830cb39eff
+78ec10765ac8bd54fb141ff1601aa5fce9abe86141d2b5457c0a3dcad48dbc61 166bd0fc25eac4f184a3185eb5994988cfd6c201
+790bf70318c215c3fd3d8bbf2c6aa799f7a9407409aaa79a6f286940dff143fb c96d985df9957e7c042290d868d44feed8a4fc08
+790f39b54161d26f82f98c7ebf3620b0fb7763fce49a5d2d1f6213671c2b1bae 8be50a412d448035b974312389e9a6703e71e8cb
+79239b015d62fe6c9c543e6f394372f63ac181b2219bf7eb451d397836377890 d48d51dea1dd8e90bb8c960e68afe72cabc5f6aa
+7941b4226958aa66c7f44e76550ed49408f8665dd32d823fc6c59eaa16ab8f02 6e2b3105c1c5fa82ae4e33a8c24d50d69c50782c
+795db8a78b6836d82b72af2904b6df4587356b800c06e74159d39b83f2ca76db ebe2b33d7c2366205d44620e57da3b0afde2524f
+7989c0a71ef11028135720da34e14d29e795c8e78e450cd85a49d0bc7913a8b1 1a7bae4d0f9d9314b905109b5b321219b7e6be3b
+79bc735b91f8dfa9379d1d6c21e2d519ac1bf0d04d48534864c9ff571df5297d e0637ddfbea67c8d7f557c709e095af8906e9176
+7a07cb0fae32a92c8faaa8f6aebee30b9e10b25e1d4f49726d28f223f4bd5255 c34eacedfaf132a6f506d2125588a9094eb82812
+7a135043f1708123027e529d2fc8102581d11dcc2dc51e32913014ead7132ddb 065d464e42f271efe08daf3f5b69dbe266266880
+7a1ae05e508530aed4f9bed33082bd4191feb167f2d351fa27180970fa375b09 b3be0fc7562d51df415c14abf671deb37d8f89ef
+7a1d4d9971f84fc5afe7f66d5925e78e67a8ea58e5569a519c40f671f44d0b8f fc3ab91bb4df267ae9de1f7b4ab596406485cffe
+7a5d59089d4a3dc1b9df7f4259be2ec0cf9f9819397b775d53a6592d62c471ff 4c65453197fd21460f0ddf2b722ec2fed6af221f
+7a7d3b7a1c17103608948e990a24f4a67f6728b7c8a5dbb3f369e55972a55d4c 73c4dd9253f8d43c436fc88e64096910f4f1b8ee
+7a907433347ef815623322907d4cc216939ba2c065306fa81b2fbbd49ac42789 4852017de90aab7f3f2af89ce8ce29fe8226af34
+7ac8c1830b6a44dfa1a41203f9368e79de094e5b62134bc3783069ca70080cd8 0acd9f8ba026c0e6845ca4cdb920cd27503752dc
+7b0d0dfa23c212f76f2317ba39d30710ab0014c9ac4bad0f5ae54ece22cba249 04dfbd14950661cefcaae35a7ee273c8775d1bf2
+7b5110bda6ac70764dc1801b16e91b662364cbeb80884b3f6a0ab3e4c4028ba7 f7c4cf4a40255c3b0ca8d8981121e329478a92dd
+7b5359b4e59ca2f60b3c528c5effabb858b81b1a9b5cbdbf70977379230f8f5e 2da4231d050f9761ba615b77fae3ad00a7d479a3
+7b761468247f90725be8d05d9744bea962f660617685c76bf7b2c58fb125dfd2 b55dd77ea78ddd49d9ee082e2785b3c1ad9de7d5
+7b9d5e67db60bd61f55eeed5ba200583330d36ff4ecb53a6c341c2a6eb3f681a 4063e6c4aa51155f18adbc2bcda3d0ee63880c2e
+7b9d61429850c13d9e3f280d37d51b2e0e3873495d610f371ab45807cf0b3bd3 e8a952561c4e59ca308d1973de46fc32ad91a022
+7bcb94b54e168e6e26a3cb840139c11c0ae7634203e8a614c3caf1eafcef5fcd 792a1db7cf2e1cdddaee0446a8cab4bf0bf2f6fb
+7bddd447d756b99d30b72a45986b2324e161a728cd3af6d44a089656395b60d0 d23f6863f89e23c82bf14b91de931cb446294af6
+7c0d59b28a150c519159ac4562b1a4dbc1348bc3caedf70af29584fe226988fc 431007a7fb7a938d3061cfbbb438ddc42ae95c8d
+7c2ef1ea335fde84cc76d1a10e4d4012c1f5238e170499606509ad3c4530ab39 ae23486285f4e16896afd53b20dd11180d26381a
+7c41edf4209d1b0b0e793a714e78107e7322d7f473fcef602d52aa5664bb5735 367ab010d69ec4fff533c770eba5b1882f79cb41
+7c50daf12d9002ad995f31e02d6dbf7851a586bba49118d87f69fa8ee71ff890 9e90ed0d151be3fe089cbabe216980e2f00ab242
+7c73285498380b4af00105c8d1f887472d50fe26cfd3acc2dab9725a716f3efa 6d091399f96a97ba9bf5f3d82b43179c82a93312
+7c79b5d0b74a35d095bd72c42c8855851df2a6f2b07e926259a819306ed7a657 a1a5bf7f5e8c069ac59fab62d2f7a9aedf198413
+7ca86b4b5f7fa4d66a6dd5715334af377f3627977caa9d68a3472a6e7c3433bc f971ce419affdcf8545527ed3e55e6310ab8731f
+7d31d02993384dd0237937ba6ca89cc504fff2d366aba10bb81110a82f4de656 2cb057eeac11a10cd4864a66d8cf4b5210bbe308
+7d3909567be3abdfc8a087e14457d7238887602385f1700ef81ad5d65f47d130 47fd30c3508b95c97bc9177a59fd0e2d0a034277
+7d3b8d28c951267a7fa4c1c75dc171d8f0bb826b9b919bfbad2aa6d181a53a52 ac3e84651504dfc63c144d35e6802a111fc7d75e
+7d41257ee4425e8a204c6d6a4ab54f46f3a370a01dcb5d5abcf54060f411c944 ab9e1881cb3b612e775f1ba27f22dad911fd0cc5
+7d991a5ee9468d8278d9d790567db72ca7c907b7397136758e18c267e39ead17 caa749d19261a3d284833bad6d8344bf33ba6e7a
+7da15ff8e29e2eac38e6f4fac5611f4c4a396ac483e0be7cae06c9186d2808b7 f5e3dd78875f51a20d22e4bbe3adbbaa20974387
+7da21d9aac71e4797714dcc6ca3350b265f7e144e43715e9f39a401c4bc96816 767f20ca88fcb1617bedd49d413884875befc79b
+7e4633ae1b0e83503dbea4417f9d5ccaf22b877c5a4522b6d1d2b16090ee2f6f 8496071c1b46c854b31185ea97743be6a8774479
+7e6e4b147775fef8d337da248ecaedb10ed1f00cd308c85cd142055b5ed414e6 78abdb650f92cb34a78cd9ff17a013f383dac0f9
+7e9424c06052ca33bfc599bccadee60065d8664a9af7648a1455100c4f772e1c 258f0e2a959a364e40ed6603d5d44fbb24765b10
+7e9a1ff18073a5033098583da250301e2f813fb87a718e66b6c21e6b9d78b8c1 960678d19dd854cb3d5f1d234959a680bb8656ed
+7f14ad6df05c4b6444d964a5b963c3c753e8ea8a6d4b2f51819a90b841f788cc 7dd8a9f71052cc12f8dd9c1eec2e99afbf08df76
+7f1c021c028dbba42ebf0688415150e8d9eebb1470b5b23ad41f9b4bc5149406 6267cbc52698d2158581d2d63a4667ada2d1df22
+7f1ff49eb6a35b7a50e8e1054e0690127b6fcb866cc2c32283a0b595c39a3a42 0e6e7fdcbf9cf558f09a1e2b1d9ae76b4209ccf0
+7f2d965918db64ad856f1b565281d55dc401d04493a57079b85724ebf834b6a7 b51eb250ed0cbda59d3108d04569fab9413909fd
+7f2f7afccb317bb3fdd28555f126846dc4eebe5d9ae7b8d8a1456e8ff85422ce d76d8a6390d1cf32138d98a91b1eb7e0275a12f5
+7fa1afc6ba5045aa7a04b5c88edb03a13ad98702fe1a98f7effaf0a128a83ec9 8669fbeeb6d19f564281bd064a21a31206bd18d2
+7fc47fb594ba964d7397406b5a2ea930a40e870652c0ce3a30c16797d537bb79 eb9264e3ac14439f2036d9e47d31f6f94f073187
+807dc79b9af571de3b9c82133b77e38f81eb8ebc766bcaf2b2b2c7fd58feee7e d9903730e055e85e16e62356b11ba755093e72b0
+807faa3586b7f1aff2797a7c39c135b0196647c9ae7ee8843322accc960b2f22 6faad60901e36538634f0d8b8ff3f21f83503c71
+8091b686de8bf697ef632dda9b3179f2419717275e3bfd2055b303489dbbfa47 1b8cbad43e867676df601306689fe7c3def5e689
+80c5983f528a83fb288b30f073e52614f1530e85904c71974a5343fc4b16a40a 8030223178f0bc0ba83a453e0b7d1585de4adc9d
+80ec1e36b983e68664e8357c538cd35b30638bb0cb99626f906d145e2d2e2558 fc3c3a2083e9f6f89e6bd53e9420e70d1e357c9b
+80f961fefc9978d410b73aecb091bc67e255e94f7e7c601f8a6addad6866b290 13de54b9267a98310d25bc6cb06caf569b2bc26d
+80fe2c1b41caeccb5aef5b42580afcb065b928c4f95a1094d46b9ca1e3702428 f3d6802e942425bc120b6971e3bd8545059eb063
+81274306bb28af9ffe7c212be4e48209b9e13f2c82ab3821bd402b175a10851a 72b9511ad87afa7591ea80101da12f8c17bef161
+812be9eef0a2edf14229e1d392ca8d4957eaa2e45c8d3949b90dc3aea7950648 0fcfda73c48cc5cbdc6b0a328b9fdd7f2ed43837
+814681285f754a19aa21096d18507b24910a4eccca24cfc30918e830f6de83f6 83131fe4c37bbc26706764ee63732315906beb89
+8155958bbda08eed88c8ac908dc44452ed38911cffa54ccc06076f30a1ffb1bf f60079018b664e4e79329a7ef9559c8d9e0378d1
+81a810b2374dd6ddede7ca2b3daf3cf470b54b7e9290d280c6d7e4e92f076247 3e9360aff20efa836e3592d1c017edaf881b936d
+81b4939f4b31e1107a389c4964f1a987098da179f9c574cf7e5449a4e50f537c d3231f4399eb8a1887fb901798d5ba4abc02df4d
+81ba4e67aa59ccb078e3dc9ff3075f50084ec1696bd867e8e4284fcfc34fe3cf 2ef4faa0f82efa00eeac6cae9e8b2abccc8566ee
+81c13819eb19176803f7052925538cd08f8d0e3036dcb6caba2e27f862dabe9d 0c68adb5d31b9605bd13b92ac56a3ca6c76e8ee7
+822761cc7e699abcf0acadbf1c7717045de822f5373bb9bcec7c56cfe35e2c60 b9de2100b3991e6ed3252d4b240eb84b87e0aa0a
+82462a4bf3fd4d0de14197547370d4708ffeb5c3bd75236ef485d67fcf04ef6a f34fd002b826e94e60ed67347ffbedd93793fb0c
+8259bf4baab8beb19e2e194f36fb573863dab4cf1898f9c3be52efb0620137fd b433410c8d0c3f8345621e9b7f1603656972c5cc
+825ca0a496df92f4a70387d4e605a509d083b4f990830ababe6062e70592373d 68b3dbceec6248d67e7bd8b3065fd8d5ff417c22
+826057e0780d68f304f3a202e8444b7283d92c1394c75866249e67f312fbe4ef 0a6dcaf4b0c72cb913e860eb634535704233ba66
+8265075eadf051a5d5ba5eb3d620ad0a859693239c82128869450a057638ae9e 49306beee0b66c81741bee1e004706b7e5c9921b
+827595cea6baed7b3a76e99c658c2efe68f41efd0b9c71e76c39849e0f11f4c4 06d6e03af9a9bcfd3c97687f1afde4516bdeec57
+827fdde8c1c0714a38efeaa0325f252745eedaaa32837a45bf68e0d58354571c 195d1f67da1749b0a8d39adc087c5cc196238b59
+829deacf4c5316eb399ee7ed590a2d51192977c8ff901b8143384dc649be1e22 56931d1ab4de660b27c56ce488245e78f8c713c3
+833ecfb032c26b1a04cd46875e99367a6162e1720c81ee410ce504d7f6aa7eed 255a0dabb28beb56096af18dc20187deca4a545c
+835a70ea81f05845bae544217f94ccb867c80850d363f7a237df4e3242bc96c9 4a65039859952e2ffe776d2e1afbe0c7ea02d14c
+836c31befd72ff44f39e142cad83023dfcd5d58816cee2aca9740b4566150c46 ca52813342a2d2cc406c3173f6b6d65a752fb783
+838057f426dadbb890b29a79f70d68f566b8c1d19d1d954036e0d95a681d2a28 8c57e3ed53bfd1cb5f5d907a171a86140be37e06
+83b17ae4b422cbf5002107b436fb2e5ca0fa6c3d0ec1838ddeca4362bec37624 25263fda46ebc849617f65306ce5c626f5575340
+83b6edb74bd99f1b5042e0ecc2403959f19e61674663c245e76084ace8db9a9b 640fbbfd48119f1c4217e81c644b28e31362e269
+83b9e2440ea84e5e467bcf921ebd6fda90c09a1d781e8976b1550e3b0302969a 0dac883d02d2cd142a85f44f642afad79853e982
+84005e38a65f4115d5c94790010dc57e1a3297f4aa89744f5927f208af758bd0 10aa3fa72afab7ee31e116ae06442fe0f7b79df2
+84503271d3c088446000ba59060bdd4ac4119f6a7cc021cbaae918e86f220c17 9979e66b2e87b085ea23db0b4839b52e09639d55
+848b4725950462479038f34889436ddad0d3842332417707821571cb0627a31b 760529d873e72200ecd6cf05179fa0573ce77fe1
+848e2e89858bf76ddc3208ce52d8a6633d0b9c86e74b9d447fa0aff3911666d0 5a6fa6e57189ab9577c62bcb4fefa2b80cc16730
+84cfd6056673cb703ce9fc11316be0f4e9bd58b0c69af563ec8fd8b36a105847 37e46f4918c08ff7af7145dc364481a316b34c44
+850a21529a706f522be5a979f4b2dc685755658d9bb42c9c929673a99cbb09ca 6baba0d067c8ce4f23a148cb1b7a256e6fbd76b3
+85513265e2670ddd1ee23214af654254e62ac330883340cc73f1329014e53c48 b7c891c629d298f2d82310d8ced2ee2e48084213
+8553f7236da96bd44bc84c4281b4b3c059510a908ea6a4bdb06ee01bfe39a4c4 d7f80d5fb81b7f1ca8443a2298bdc07eb3a144d6
+8559f031d21ea80c51c33842e3df2b69a4eadd01786fbb119b321d457b40fb6d 4e7596567f9f86fead9cd398d3e3b056a2867fdb
+855d77c6095e55b8d217076bf2f6bdaaf5852cc4b7c189660454cb311c6e3be3 f101cec32d82654159d264d177ed9ae0de42b6d7
+85b09a51ca2cbbbb68cffe492c905d4b20e8c3a4f988f5baf7f5032e2683e84a b3039beea6d0637e131b24e8b6d61005cc1f6515
+85c1508d2b524c0ca3001873e6f0f8d2f291be2dad43d12375faf37ce19f72e8 de2220a48fcf7901432f8093d3223e65f4072ff7
+85c2f81898a85957ef3bf6783a34d161a568cffde615c3248f7ccd0021b36060 18a7f61c29ea8c5c9a48e3b30bead7f058d06293
+85cb06c94423ebb8ec44805dcfb22cd56419a0d502cd6a7eb3d1e157fa0b0c27 cbf7144d8b79387bb97f115273515b9453e94b2f
+85e0897e275601bcfa3d653d02bf6b49530d66d5ba2fa2977a6638059cff46d4 4195117c1ca5211c7c74366ba45b4dd2c8f4ec0f
+8618d77ee61738f3e6679b5892a7ce404bd0177775fa65d647892fd42dd4736d 56933fffb8f2329d098fb9ad6085045a14c9ec2c
+865c71423b25f5c4d2565a0189352e4162207a3e3d711488eb05f6e6553dc664 7f09466ca8150a7bb9eb9728841b0099b340dccc
+867eab070ee07beadb0193608a62052fde41f94ee70c3574395679fc30c6f021 186a08f4b6d4a08b1692365cfef59755bd8c0485
+8699dbbfab2d020fa8fee7abebf6ce00fada2ca40eb0d09de73224daab3838ee e8cc449fe9c99f9ca35088e3247ab9c0088081fe
+86e228d9904af64586e9a8378005ba654681ff5be3c43ca930bf6b1f28d4395f 6336846bd5c88d32f93ae57d846683e61ab5c530
+86f7012dddeb074d3313851cb47e7e6d2a89fed1d23b030131b7b9796c63beb7 71d0a2d522bfbc72bd8f518ba69939b2123e3f68
+86fb56a271a202b67ce596425048d4a2d0b5d0f320a196d3a4c9b6ceddedf1ce 5001298e0c09ad9c34e4249bc5801c75e9754fa5
+8727c60dc37a204127adefe3fd92fb6e1740225f3a1d8985ce29458c322ff169 ce4be0c1c06f8e311b68f70c89165cff698d5c85
+87ca3d9823e24cb99700ca833ba7aaec3cb8d34eca32240779930de95fe7caff 27aa76e5ab35a01a87512c5f6b215d1c9c736bb0
+8810957e46cdfefbea090991ec0a237fe844f0b4cad1eefa99f05f3c421f3e3b d76261e048bdda2e6d2211aa308e675507bb434a
+882f8c78660ab5efc60c2259e8fdc601ccf75c16c5f787e689314197cd345793 796cf9d0a06b922a730ec98984876982c2d5bfd5
+884130c0e07babd81f0679e1ab15f50bcde3fd076fd540f42c5f7a450647c811 f5be02b49b5b3ae73bcebdc94a84893a012bf46c
+8864b5746d7c5780083bb98449a3f5bf78d8281e8c5e3fd12a8ccd9103eb3a1f fd0ec0333948dfe23265ac46be0205a436a8c3a5
+886637d6a4c8dc11525f740a2e3723692bc67f42753e7b7a75d4e9207027989f b2899380601ba014c011ae9cf00a675689764856
+8873ad21d0ff871dcd53e0b2c4446a7da3439519da9c7ee509bf9a09cbe20e16 e2fd2428625c296a3dcca706dab7873368ae2818
+88a6c8138e7dcc2f7dc252dedf17830b3bdcd67021df4538c05dae27293ee343 ca65c747f8fd5110284f21d624fddbf87f41f836
+88ea074339e8277b932e8ccd92bb661e1b3dba88d86d31e5029577b41da793d5 5e15176dac8b0baa6f7950f5f763608c83b29093
+88eee581883a2bf8cc278592faec71b3964b36033590ea99e211b298101d2357 c47baa4adba9bc69f3e4a78e4b6e295b1f20634a
+88fecc715077807bd7ee9e6a1dff65fde000379e0bf2a15cc2404469cef4c82b 5018a35e0b7e2eec7ce5050baf9c7343f3f74164
+890bb959ac8c20db603bf083bc82f55f9f42b6dca6581d941d0b361188abae3b e032d863f512c47b479bd984f8b6c8061f66b7d4
+89117557e4b6f0e5fde4d058c32582418fda69ed651551c0d682b0bd9a4f5343 ff87c872c7846c5725ec711b6d2005febab7d6be
+894fb499b85985208f105302d3cf3d9c5ef13b6e4c51ba4233656ca6aea65cf5 8add01539268300564482f854412cfe9839e980c
+897f5f26ba1178254ba5170ce0cb7a23a83dcfb1e528c87ad3e26b89c62cf9bd 44debd80c4908e439d4a505ad7f6de367635b0eb
+89cdf765f35bfe191fc18ece04228795566f412e0cd6fadb80102a8268c99085 605324ba5ba92d44cff024069be576817e41c7de
+89dc8ca30d409ad37a872d80b88661ef50d6c226a37989cd089e60c183609b35 6ca549d9a75628c1703ed6d2d06877351a268a88
+89f819abdee0700e804a202787da29c6c14aae8dd6d3b8cc04c50cef70c44756 802589d07105d24bc88c908c9e62edb3fd679895
+8a0042d434a2f8a2e8d47caa4eb454f388752fb3fe71150c1cea12e807cfdf1c dec93efc79e60f2680de3e666755d335967eec30
+8a1e1a6cc00519c4df8ce404c987751446fa299662308622cb63576fb52996d7 6f2cd729ae42988c1dd43588d3a6661ba48ad7a0
+8a5dc4432a7a135282a588c0249f63895f8cdd2d68906a2e94924ca31c89189d eb9ac8b7622f3c144605733fb4321b7cad8c41f9
+8a5df168a787cdbb8fc1560d31fa917061c53071da52148a8b63ebb16335091d 16d9bd5394fcbe6cc3dd5cae5124bd8400343e10
+8a9b600a21987e6ffddbad745f38c115797eacef9117043bd9d2da4835ee3cc4 0fd7e4bfba5b3a82be88d1057757ca8b2c5e6d26
+8ab64652ae74f7d9866fcecf118eb9994a0083fa7e52a4bc49a29e9887a2dd7f 9bdf5bf0bb8dfa08e11ea902931100c4605d1d09
+8acc0ab2fd3b22535ef3f534e51a9843fc8aa927903e1157434c9d72b1ff1503 257bd746cf4b3f1e048a640202493c3e3bcdc1ae
+8b399358a71ffc0adbbe2d3160310e19b5a08da84565b0b74f4ed24d572bd476 ba0bfb30cd31b4d6bcebfe9ff614794e4f5881b9
+8b592ef1a82b329ecbc463843ddb64c4378787bb632999461b743c8c41ec0d82 2683a6b43d73cae20a876eaf50b541b8efc18e82
+8b9acba2b7d2bbc3947820166fd0b7fe6e274a1387957cd6488730d243d762da ce02d0ef9ea8dc621df41d3f244858bd60f41d48
+8be5e8938f38464bf1cc68f917fc9538d598bd4e35462ce33777a65bfb98b504 e6e4e04556df092bce179810d25ce5330b34d8d5
+8bfa45d9dceeee0ca326757a5c7feb70c3bbec6084ffba27c44acf73270484c1 80829e69e76fcaf3eecf606794fb5beaf3561f17
+8c0f41bacf4388237e1e53c564f3a0fbd1dad819856035fd41370942389cfac0 01b905d074ca43b1b84f507264d3175e5659acb7
+8c1b59c9c4abecdd5107384206e51fff8ebb6eda359c6acc8ae9d608f777920b 3b104436374b9785fcca167c9d3bf1e751c1e56f
+8c241c359f8adedf6fd0497cc05f5821570e05b6d92537d7ad8d5d12fdccebc2 bca03c4e40b1f29603ff131d702c46591b8ff960
+8c53c0f9f0972a1c77e40549170b9ae51d365c200ffc4cb220628c5bce3dd0b2 d047b47aadf88501238f36f5c17dd0a50dc62087
+8c834cad51361fc89cee23325b17dfe11294cf9241f2b7579671e6b2cd45d79f 8844825b8244e0cc54c96d73469a956b507bbf8c
+8cb46741dbbee92758abcded6eca24fc2b74f43720c8525633a80410ff0e2b7e 331578fb1d3ff7013b312914fa9c78ee61d8ce52
+8ce4527e0214a617e08c7aca94006d77ae31755ddb5f757f635c0bafb35f2afd e8919be6068c14b5c99b828af9fb84769ce1d79e
+8cf83523411bffb0b74c7f8b5f9576b23ca2b2578f93ab8ba59bc6948efc74fb 974a17ff165327d457cb3d50324e122542255524
+8d4fd5f56519059e2797709107cd15d1e62715e26d83a27eec8160fcaa57a7e4 06a1c29eb70dd0b5f43cb58052641dfc6e8dd2fa
+8d79784fd1848028802597c0ca8342ae4b7c7eecdcec8a9f806f537798f74110 b5e02a3b76c602bcd6230826850672f828e67d23
+8d9d39e6fd6ad7cdda32b877633dc72a1fb7f4da9ef96aba6c284ee9f32ed71c d44cfd460e2c52d67dea14838b03219967cf9cb9
+8da4f409d9349020273e9fd00dc8f7a0aaa655b5351af47c9a220ee542a31ae9 7b2a0b6f0d56e59dacf05a30087b58c1ff93cad6
+8db27a1dfe3454a225917713da4b0ae56c617de64876e549541e8ab991f2d1a4 5ad63728f19e4c606be7455a8a7101c9255b2b21
+8de876e616d65723cf46a842086e338036f8d9ad6c6969ba07ce2abdb27271a6 0f6d2f4cd1a1391a98c6a89e2ee6a69c0afc0c9d
+8dfd647590d3913a31bb0d11ccea6df7655b8c1c2898c49e85550fd5491cc3b1 07b63040096107db747c6c80b02b0619e6983ec5
+8e3ce5aaff68c05137728f5845d627de532b2294b665c3d05eddf79972439909 b27d0a8066fd0fbddfcf8a30b4e77760147b0817
+8e50c5148de4a8ba9fc799c67dc669d6f82cf0a10ee9f03bfa2b5a3641762466 d1621f2e35b6db0d27cee8c6b1b03c2199d7dbd1
+8e579056a6928f364735405ea0694fd779e51a2a873fa32f3c1eee7db4ac186d 45bb348411937ed17e912aad83a14c151f922215
+8e61988c998c96a131cfe72225fce43e555bec4e590fa8c239373172a9d485ce 11261fbff21758444d426356ff6327ee01e90752
+8e67ab781e8d1b4f8c35972ebc695bd8645f51718ebc866be72ca3863956c3f8 b864df623e3b89ad678a888dce4b0d4c997f1ac3
+8e786640865ee4f226cc81422201c22dc4229852bcc61018a2ab7c73cc9c8e7c c3b53a3518b99b4ce31aa3f6e51dfecc5bc772e2
+8e7caefe45c938cd9dbba149c377c1cb4392457f11b1d8a258f464a24752a40c cc57d8aaae44f013e3caeefbf401ae96d858ec9b
+8ee7bff2cdb8e47b525d8d472fef394ba46ead8db298dd5bc09c89313bc4b1a5 e313f6d036812969a06ace47a56c539fbdf8e82f
+8eff6924525965875cdc054d19a4b3a00c5e7c57538ef7ede78461bb6829c818 5ddbd5edf83e034a62499a1a9c94645d7b9030dc
+8f74f6adef88cea42ab0854fae5f0e3f195edf161acfb12d5f65a5e9717f0cac e235f89f548d732c4e05baa54b8b3dbbaf5537e0
+8f9cede149494538f4de2ae7c79d5abf3e8309762ed0846a9d2839031cb7b456 7dd46af6b7c177bb38c3875ee6d79a78dc1076a9
+8ff22fe4eca09e4f6bec8e71cdda911f37d836532f568bffa0da55d90822320b 6bb7aa1318f7f31a346c71ef81d2b33c6fd41600
+90115fca32a9cd85c4c9c0ad4c23e1d2772e2e91a105d3ac3c92b76f895f8b92 5c0b005b617d01c512a8dbf9e9dc1aded652f244
+901505c3355518bee35475c5d3f23bac1dded688b2bd314cc32b7f157e100724 c47800c7266a2be04c571c04d5a6614691ea99bd
+902a50088f5721565b264e26665043ef0de4e9d2841414ede099b24b3793b1b2 e2401d52544ebf052730a63e8acfe4ccfb8fa0d0
+90445c1dd3c04315c85766841ef3f61e3bd6cf0685e03382dba4376426d365fc b6c9e4203b69a576ac5c462241a7e2f676cf551e
+906ddb4156860b47274ea8ecdac5f374ee64306d9b9f03c34bfc9df5afdeb7b4 74eff33f8be680e821d9674ab12da3c0b76dad23
+908288eddd0888db0d536666139e193ae7e2867d214033fae2557701a5d8a9f9 80133dad8aa9b6d8bec7050a77d603c53ecd8512
+90884a4cca02648ee29efaeb429c0af915e0d2eff2d9aa281aa8dfb2d86f415b 470704779e16fdd3872fd111089ded59aa12a576
+909cfa7665c9be2db4a58346155ac887ba00f6079b76f71b4f7d4ed9939cf0b9 eb942285420814778642962e7b4cc95636bce88f
+90d744132ccf86487aaf3f51ebc92ca3315c59c48e12b4d576e253be91a0c460 50acd0c53572be81b5c54379b2442b19ee240953
+9107412362d36b5e2350e5b3e3b6950ed4519963bc036b84509f452bfb73e580 a24f1bb1a8d16429bb17455a5f332eec88e777f8
+9112f74e9c8db2d4574bb1d568099294486e293e7625b3d6baa257970ac1f81a 1765d783910805c69509416785b2b780d9b36866
+912aafce2240cef7a68ef8c0e53e10b0d7450818c471f5b09f8f718458ef9c4e 1e564f1ef0e3a4a5fe861b46024daf93bde4834d
+9197064ab68802fb19dde16fbffd2a40053b37850ea393274d10b3cf5fbfbaf4 d337abf625b6912fb8501993b7fb2b472d5ebcd3
+91c9d8df151e1161258091beb0822444b9af63bdebb8aec39417153503c0b669 f528160ee7dcb57d2c56341befcc00aa80393b5d
+92128dfb792caea934f5218807ec993867b0c8487a3de69ebac33e067f64d38a 700f55d91d7b55665594676a4bada1f1457a0598
+921be768179935cee45bce4a0b2d27ebdcf05ed06c7d10fff2d9cf900298024c 78f9923320d8c6ff69015ffa8c0410f6599a3499
+924786a7e975fc305e917d97cda11a71eafae57d7b3c916cd84536a0c2487a76 685145309c964db9a49d6b7ef96140db6830c131
+929ca73a254eb7b0dee0a7476e09ba2a53c0b6975473c9d7574afc0228c34e73 c22bd2f5514799cef5657d67dec855c6b32a7eb7
+92a2ce8258f27dd45ea652d5336dc3685dd3d60627011f6215e923318f7325af 15c883bae698321aba3b162df438254512b316eb
+92e6474ccbb1fde2326731bf69e57142cb428ea3e916b71b2980e4348ede2a15 0e3ce9a193137134d355caabb98126cab35ac236
+92eb919acbba3d8094a46d43a4fcfaeb6243859c3c15b2897e0f7f50be66af37 4e67695cb9de10a3dd32f78cfa01eed91ebf155d
+92ee0adc53ff899c85d7f42cece06277f063d89e0b496e735fb68b25d02e2b93 c1044cde11b3aaad524cb3ea49d75227ed7e5f48
+92f0bd5f6ea173aba9d25ffe352a2a7246dc32e30f0aa2905cf566464db2182b c117a5386c76f01b4c444f608f4cddae9d1d2233
+93010571894137ed6a1d91c6caaa46adf9437aa37e672ec4ea55ae0fc434d601 f6baaae117d058baa0865206b834cd8e32fa4e6e
+930d5c174308a2d7109171b86756093b656cf7f129524b1fd121f66a840f63b5 2351b9dd3b52f5421a6dd9e2effb503497b1c447
+931093620e5f050e2127fb0b96786ebaa9ee6535fb698ec01b5f7a800fa27cbe 1a443023183e3f2bfbef8ac923cd81c1018a18fd
+9315ebbeb52ce6c79a2998a3bd9486831c6d2b2b3925326a3308aff6e5d879e7 524570f2c3b95b5a9f1c8df74893451e04f1017a
+9391424f1c846bd6e8aa6a2ab9d1c9896f415068f516217c20484705763082d2 04c9c16e55c53fc12c2eed43e3d7e42f78fe7005
+939db02ed91a33c32932ca7c00f3c5f18bca0c2d348c06efe566f70bd3b2d18d 96f702218fe6bccb09568c2ec24cc186ace8c575
+93b0ac3a6caa8be7b891ec756913b8f3f3071a20ac1de863b16c8037e2f4c21a 84a13dc9b196c2b0db6b0f664e4ab2bf9d6ce9ac
+93c3547a4e0bdce7c7dc444b4cc7204be32bea44e740daf0a41cd32a069fbabf 948171a894ff8a25635232606c5afa7bc3224e38
+9454571eb895fa350d59a9fec2b5b2ca135e1938dace531702ca77bc342424d5 4633dd81e3462424635f7d231a2373971ce736d4
+94a67a2dfa7ceb5a3416e25db885e3bfceba064499fc4b817d7945cd271b1b75 1801cf406d4b17f019a789ffeb9346c1cdb29491
+94db24aca3f8e07f481744f62633730feb4fc47605280381a08be510ae971ffe 91822c50ebe4f9bf5bbb8308ecf9f6557062775c
+94ed253efa9e86fc636805c294c441d08b89b455903c0c14e9b16587fec081f5 43e968a905a821532069bb413801d35b200631cf
+94f81211f6452f3b46656d5ffe4ced3377ddab58a2e9063a6a4b15cba49cc08f cf16919dcf33635a64999185522263bee1428e26
+9558e844b96edf250889c0660f121678993205d280a3e488cec0605b979f370a 7b6c856c6e276e9a3689914797d2598a37f4e1f9
+9565fe314fe3bf77a01611ffc14a48246459ad6a19ea01d4082522622fbd001c 1cd20d3af067093c571be764bef038be1a39b502
+957a8f955cfabb1507ba2fb3bd3a859a9f74b4707424dac86ac543db0d69c667 09845e76453ec9527128f73fd5ac7114e9158432
+959c738f7d2c764ebe052a66001643400118c7851aba3b836377623501a14222 1b9e92c73bd223661fe63dffa0c9138da4e1ea80
+95a2dc8f7a4fd47c27fc95dc9d90923d11032f4f436c410c60beb6f4d5b6984b e2337f39c3a4ab046b7a572fce89df27451fd2bb
+95bb14319c7449829409166e9e1d1a750938bc52ba7523d752150636556803cb 5440906feb5e41241a699ee413475b7911284c41
+95c49d1abdc7b172424e8d7db1f142590e40c04a882441728b1e9b2268848cf8 8953b6cefc86a339924d5d0ceb7fd523a149fddc
+963954464526b415ff976b8a59efb85e521ba5239faac654bed92979fa976f92 14dc301e7f99cd5f88098e32366951f803e9832b
+9670e7ceb635f35942559001b8c628211efb5bc4f57380909523fd828c706f93 73bb602a74eda507838372d66093c8e85246159d
+969287ad751eaab33d0109bb39436e2babafaecd5ef8405734713431c64250cb 468b12adb15f5546e7c5536f72204605fbd83f6d
+9699ea5560b7b35db0fdcd4c7d50269402d5624d72cb1a8646b33b65dd416c07 42608eb49e4be32002462b51e86065368b342c05
+96c18f0297e38d01f4b2dacddea4259aea6b2961eb0822bd2c0c3f6029030045 45b983be36b73c0788dc9cbcb76cbb80fc7bb057
+975c9b644598f22c51616e9c8afdadff852639aad6c08b6c7bae1a34e2d812ee e34dee0c7f0ac8abf228369e1016eb6016c40758
+97a021b36da9610ff840213c9277124fe653804e85f2081f6dedba953b5706df 8a7d625f53726fc7703dc0a413686d1ec15488e0
+97b0e3661fa0caa6200c50381233f8320b907540ceb9d17ac94fedc66fd093c2 cf6cccf1297284833a9a03138a1f5738fa1c6c94
+97ba6db30e1bcc6f9e5916915fea9dc0e205599c97586f7846b9a456e5e9b6b5 b47ba8387c1d7209d4976af9c3928f6ce93ed545
+97e00ba4c8028ea91e7131c8c7596227cd03d3d2d14ed2d179fe0f305ebbca39 6f6a17db05a83620cef4572761831c20a70ba9b9
+97f425b009376be705660ee81095773e10fc99bee7ad513046fa0108bdec5d22 1594f6c292f52e077142ffc0f35b544e2c5dc108
+9832b7f86876eb62f53f0fc649fbcae18dd43f2ea05ac7302239f7f91848d554 db01475e9527b251dc429b03a96ecfdb2928aa1d
+98488e25f5011c5d690f33397fd78528c38921473a0a4bb4b3a4d70b0f5a8d84 8a8cd3e5f3aebdbf53e677b176e3a434231a26f5
+984ef110535289c1e7afa6a33058e1ced09d7e0da058e419d8ddf8e8ca060b24 07f4f5f82f7825893865e8404eb2bfcf4a2e6932
+9862a961bf9c8b544304d2dc638c340b9c9aa6168ab4eb93e309326adb2f4499 657cec1480bf4d4bc58119da119f12a5b0545b8a
+987d206013d0289d45845304b00c8f9adfa2255ccbb3545db0157f79c7cb8ce5 655d381a1948783d7d26ff9ec5ef54ed6bbefb29
+987db14ff31dd4b8eb26b09aa1abcfaab773ba9c27b6040d47033678e7cfd0e7 121c862c3a2bc66c12b64903e508d8bb8d2904c3
+988a04ad188e91c521853685421f1b7d8b0a00767a6fee4c58a98bd1d449c3fb 710a14e927dc232d94caf0fe87a42f0ffffe4b79
+98ab296fe0c2cb1cf8c37553f659614482e37c84f0b50fbb63e9eb33cdf1f85a 94b6d09da1161aceeaa537f55802d794cf021627
+98bdca06d862a2b96154af5b34e7567f60421b307d1b067a0218d6f3532eea0e 6cdf0445fc0de286fdb7a343a5dfcd1937666132
+98ea93628189727f2a49d3fc8800e24062ee013433b4914e2f8a6406be1b733c 944a1233c2873c5e21f941460c3940f8a563bdf6
+991cd4eb50d0bd943326a09398c691e3715278549d2e62720c1a90495996fb85 51eb2f90a1469542799823e0f3249520bf8f9cf3
+9932236d190601ab8f93e8ce8ce0fa59dba7f393d917390c1d53b34ef05b41dd 5614dc18f626902796fee9c87f1f703694e59295
+993f6b6de11fdbd469b7f1fab0d06ae9c97e47b4b15fea92903cc8a5f1dd981a 5b9c679bf878b9eec21b022f662da96601343fdf
+99556a8adf928dd8b307733e4ecb5a8ef495b490011158d71960ee1b0a0e0718 e3fe32b6a4ecb340fa7c9802285711cc52b8a101
+995d546eb0fbe7f94b918920c68b83f8f92a2c433e48fb458374d347982cdc33 35df53402ea1bd8079cab9bdbd887c79319baff6
+995f00a0542d56fea94d5ec6bd4912016c7bfcd9f0609103766bb8c8a0d1fd50 daee7af942c5f7d25a00e3ec4d25eb587c61ac94
+99676bb02a367fe46c82e0d17e7c0f3e85a6c39c3a7f51233c85bfc73a20401d 8c870fcebb8f625a8e172a49a44153af8f37c8b7
+9969afffd2839b9abfae52451171231fa3d701bd6b3d0be45c2381cc993278cf c7cc7cc6e3a1480a18fafcd08574a337648f68f8
+99836286369db283d666122f999d6bae55d099525ecfa19678919c75de3ef920 558d6aece77743bc4be56a0a62ca9838131d217f
+998a830952adcca170929a187cdf235190873515a9d8577dc7610373720be0dc 05504cd34d6ed35cbc7c99708d226758a1cf8b57
+998da46ec222491d7911e92bdd6711408dae8e47f65007634cf0286f78d042f4 0daa6cdcad92746551f78edfa93be0bc188d4f4a
+9998dfc144a21e3e124bef5dc0f9a60e0feb92d600ac1e0fa77a329b69f0d256 4319860c60d84ec2513be34ee1a65dec5eceb6c4
+99add46dddd278865eff998db5c7e17e806ab0696d7cc8514ca638873fd4c6be 3fdbbc28dc8eef8c0bc32fa6940a9468307f2dc9
+99e8fd0d2d4d41d946a1bd932c4dc4c600ec25ec1ca10d3dbcbac0542c85f01d 84675cbcb6c1213c1e662afac915c861999fad7c
+99f3b405443221141eb0fd1e0cca5d355f893983749b7fb455769fba434e7945 0266163a49e280c4f5ed1e08facd36a2bd716bcf
+99f81d6e58403d464cb05e5425c6db0219ac6a03dd4b12d8db19f8ad7894eadb d435e706e0daa21e06d16f00d0605d598fc21b38
+9a2db41cb4dfbd68346f5aae4ef7a75a2756853da451e62b52999bd40712fa08 f57d4aab1617ffb4b923ba461db47073df131478
+9a3436818efa95ceed279b481957c3881d4387824b3ac7d6a4e5c4cf57d1339f 9a2edb897c93a7d1bfc28aaaab1b9634dd684ff9
+9abb7801e72353060a1490ffa3331674d6882558e7d6458de397b4b00d31ff8c 50330c02bd4fd95c9db1fcf2f97f4218e42b7226
+9ac1548112cb48d9fbe867db82c9a73eb6557f77d941f8fce68ff14d276d3e87 2e6776cae6c71d745beb9e102a4a18429f3c4cb6
+9ac284bc88212994d14f96edf294d17a7a4796193e3138f84f9e60eb1294cca2 dbafe4e0c5def65ebcbf13789a84e996cdf8aa25
+9ac9dca313f55249c085fcaff33f161594a35636e465e10368e4ab9e7b977b87 91f3b517f65492e5225d853826e3837d404c6ca4
+9b66436f2b9350f6d20d44171b51b0dc1b8859499ceb556249635125a86578d0 f2e2e7e17751b629cc3c13b0a7d0cab059cd1df4
+9b91942519f6ea0ba7841d14598c082615cbf66066995e85859813d3eedb43d1 62ee0b89958479ae19eb68f18bb23de238a6a2f0
+9bd609366724dee19ab10d3f795965cbedf785ce8572f56dcddd42f48454ac91 16b9dc5202084b857d167c8af606422df771b8ab
+9c0f06e8344fe1af55ae50f4be06d090a443c24455252539371c9aab581699cc 1213764b28dcd72fce1be359db724fef38265ad5
+9c486e5fab95b06f1208424d889811dfaa8d498ba8a64af6faf2eab78e005baf d4594747a02e72be9399b3a54018676e0799be22
+9c4b7bf896a50fcad21d07e1a9c21786876c78350dffa6bf7309ceee396942a3 3d3552e8fd709214634435ee188e6fb4930e7b39
+9c627d7d80e595ff49c610c35fdf73d3e04757b12c75b724270cc51b1778f781 8c08cf624be754f3ba55777638f6cf54007dc516
+9c67024053a5a19e5c5f27bbd02419c09194a13d4cdce62d5873b9d6289a5e55 e5ff267406ab4a456be0b46e38fadf8fe822e13b
+9c72eecdf03014bab3444907a5c9b1abacf3068537c5c7651f9492326205cb5c ca88eb6224eb55fefdf6ed43963b5d34c3558d03
+9c7b63b5572f9c6e49382f5ccb71e77747a7fb3723d13dc91f62fe6e3bbaa5ba b3099082e5108151c0cc93ec6bbf8b7d6a28c4fb
+9c84470c221d8722ddc005be2e4e9f662bd4ddedbdbcdf0999328b84b7aa14ed 20bb1bec4aac38a69c0a6fcc62919a472e33a136
+9c9d32c9c37ea95e0e7a22420e60f28c4ec7d16076fbdbeb9e16485fd7323975 3aeb4c1456431b730322d7a7d88f5a2ff60299ad
+9ca320473e41a10134f39c3d2361a6bc82c68bf5328ece73146233bddb2bbecf 485eaa8bb045019e615302bafa10302f86a01553
+9ccfa556cd7f73b426a7bedb7fc3a850e94f8c5ac1d71b9afa365a89005aff54 db793a00a5615eca1aac97e42b3a68b1acfa8bfd
+9cfa4ebe14380d4922f098d03f17be12019fa52ed3e602b9925ff90f55c5a314 c94eb4aa7665d04c8412a72f3554401d2e89e03c
+9d59e5d73d2bf09ae93c4a9d7afd5d4b7e983a20fbffbdf97ad78eb4d949f3c5 1554ef7957dd89f4d94f00dfbcf7989d3a409306
+9da6f7f0cc86c1260d40470e395f1e86aad5ab8e27d29bea60cec1d35c017b64 b946f1a6f5fd8b14500fe986d18d1d008cd00f18
+9daab17c25f647d652c72c8cc3cf4602c270a369beebc7d0b67238897bbc426b ae90f12eea699729ed24555e40b9fd669da12a12
+9db520a5a88c7d75b86a4faa1ce9010edf38af922f400c69a4a93aef69e25c4c 32581d0093429770d044a60eb0e9cc0462bedb13
+9dd0778372a1290bbaef59f6c04ad41776480beb00e529f6534550ccdec849bd aeb13951b1f97e44e4274201683e090cd7644046
+9e0c79efc2ddaf22864c1fde0db347832bc1a4e132fae802e95b6b0c5853c2cb 8399c6e8a4fce8eb0448429ad557eae77d2d78ab
+9e2bad8166fbe654963d974e55a2f08da38a9e3d1db676a5a2c14ac3714b01d5 ee0be6f12e1e53703f2320e67389b85643305422
+9e77f6b7446eb082bcc2cccff17f7b83c69fbcb03117ea110012cb8bbfc37515 93407057f0107cacd2ec2fd97f940ed842caba94
+9e9f5a22329db59e32b25d8bded97e479d9fd505c6b61a52e6fd3113af5e88cd ed6e85be8971b49a86ef11745b5a27bfa4f4cb2f
+9eb5ebb282122e48e20e161b3715061c4381c5e6a24512b0109c195d2f2ba73d 154eeb7642b8abbce7954016ba549e399ffeea24
+9ebb9db12bca36755036e35e51abff05eecdd42a23c620a2a372d7cfa55fee52 213e720ca80207fe1bb48ad95e2c422e17281a9a
+9ec93e31545ade8f8122f1ba534269ea72fd84f1ce0b71c51f40f74b23cae7b3 a399208309046656ecc01f7653c5d5b8905fc16e
+9eee45dbee480fe07ecda366c972abae9c1a4f322f4e2a00143638b6b877fe10 8929a79acf82b9414cc37bb9f464c9d0bcb384d8
+9f063b7c95baf5482f968577719e51ae9feafc11121b1d1c2b1460ed47ac713f a870abdd5f33c3cdd75c9c6229207346d1a3e5a7
+9f61cea7f0b3e6e51d51b3c63cdf7a875704cf5d8bdc20d1b4a05daff2275286 f863bba4dd0964e82908ab6ddec36bfc26f8bc48
+9f61f57710e2f3a52ba694faac763f14fa68329d758e713152956f67156aad3e eb160e949ef7a76c384dbbfa0d61d580a11d095b
+9f6353cd9e03b3325fd0ea23ff06cca2e7871b39d0dd546d023743ed4d0e9c81 47d75e0475f54c1f51fa4f3a508e3a71bb3cf27a
+9f68e485be09cb4f0406832c3043ff73bbaba9057dd32ca5120bf58fb968dcd8 c341bd71e5bc97d012fe7d788f5d95ad61f421c9
+9f7d9c3ff7c7873a474c6e71d117de23c0ca92afd7dd838e6425de0259d807a7 d17bf1cee2b79f0c371cffa58246faec7fa70292
+a05508af8a460097b5be87d0d1adebd9965d001c00c0342d5823d62f5d504147 d8b9e83ad587491de5e0ce5e105d6732da7efd02
+a0806f2837164079433fb98454b3beb353b38625d7f1f2e5b2737909a2fd7f3d 9b52f4120bf54829149cb3ef756dba5018fb812f
+a0a1c80c937e6f8c7f4b9deacd53dd76b6f9bda0710ef14762f26b5c7722d85c 488de7f1158680124370c16d9b5787f2cffb290f
+a0b19bf38b63a7127318ca02600424c2cd491023af9dce6d707c0e5891751207 bd65f47097a6ff27de3e1d908ec1d53cc6b0fe48
+a0e4131177d5b53a0a0008fd5ffa88bc29c07ee6c3020adb8f45751e28eb16c7 5c7507cc2e5b04fb9acba04042d900f15271da8e
+a0f3dac8fa0e22dcf356aecbbdf79440715687c1053bc59b83354f276d688ceb b196a807b323f2748ffc6b1d42cd0812d04c9a40
+a0f8444fdfdc6276b3b2ab225d14358833ac50141d73bc10cd016a063437e6e9 d7fbfe155fbddb41936407612fbce9c34276b671
+a1034d13a76b0d29b83f6dadc5a44826ada4accc7889f4b2f6ba880763f2ec36 634e22b2323599edd0d177e2eaf69e7ddafe49b2
+a1053628fac1667c26c16ef757cfb87f4bc6553b90a659cd39bb1d46c39bbcf5 ee1765e529ab6b44adbcc6a0ff6e9ac1359c6cd6
+a13eb24e3a3948f74a9d83f553c937e82cc6e2fbdb7948bd12f2794d822fbb6f cb5245e80f90f14c1b8daadf95bfc08d29c2467b
+a13ecbc514b571721a9a1c92af7f89d473a5fe13228904f8d17368e71f273ea1 702bd70595a7b19afc48a1f784a6505be68469d4
+a14fd01f6c75813480bc15c6f3ec6f8a2b18c541212c93fd96f6287fe82bc104 6829e2c4923538bd75c6ad6a2590c1aca8d4a212
+a15a130a98c1cae5e6dd70c309b31bc21fcb9ea731653d21054e0df82d4f252f c18626eef6cbd1b420b85fc9f8a44d5031ad2fcd
+a1b4063379bedd4c5c0940aa2dc8c07b509d3a5164a853f1b5a20d156a7c5c33 b4440c0becb8f009bab65b3b3c901a409c49add2
+a1d1162067d6018f0b1d90daa0ea14dc570052758b6a3171d7895073fc15d992 7350e6337a2d94c2d2a105845fc78ba0ddc89ea5
+a204c52c18c7bcbf5cfd33f2739b6a67af9938ef27d904cbf102f5dda766044c 0c3aa34742a8470a4ab4acc60e061bbd9a85650b
+a2127a5161de12892e23011d647ef0e291101a6120b75de2a8cad765e56311f7 880dd9e150364fa22ada592cb5daf913e775cfbd
+a215658dd4f0927c758ffcd6487b359161f0ddacd2543cf0fe86bf085c174bda 870880896dd1c399bb940510903a3639ee61bf68
+a27bb9f32d0647d311aca00a8f7664d5b30182a0eda31b1352f003bbf22eb58b 36cf0f3c2a9b192dde9ab57992dd730a54a2e302
+a31067ab4135316257f12ca5852ee49031e0cc3c62fc7e36857356a1311f864e bc7ab9aaf82cd5778fe998117b465b5feeb58c41
+a33b7c23c1f42033a24b58f5ea0d33aba4b6fe18d4bff86f0f70a4d266b66b69 64b5d4fbe7bb8ac0a5dbde1fef81dd84b593324e
+a344b247217e51783935ecdcea5837be2f47a56172d3dcc2069936ef3d25dea5 7c866536731236d38f1d75711a2af253d479dd8f
+a37c93ab010ecc9c7f017623c38324ae2b7d06385eca57fc6da35102a2c54a54 e5a2858907d153905dbc09d1445779caac0adc6d
+a389c085c2a136c9a2c2b4a818b851a87af9904f933d7fd6aa99846208efe800 f501265f976a745e5c0d21104635d695227171cd
+a3a074de072e21878238d9d7b7b1081fd4dbf3bdc50c145e85e6e4d7a09d7909 0967f654549002d2b6e8495c10cd0bb20ff3afff
+a3b7619b6d836ece7d4dcd5d35eb3cd636b6ad1fb22bfea768ea5d4102cc9ece efd690f67a58c2ea1c3984397c2aefebd716d7ce
+a3c8832d145147c568d4954618aca12117705d399fc02f00272fa32f3ff95ae0 dca7499de88fd02b75a47bee8def272c14b5f7bf
+a3d3035f44784137774392f722a38f0d5a43b9df0a631531d0e47c48779cd940 141d3dd299cb117c277cfe0907b0ddaccdaa75ac
+a3e39ed0693d43349755e98d7e125ac17b5d3d2a5dc232b8d5b2ba01302ace04 c15c5619a51d0a0dc6bec0d9961f9ef50c5c2178
+a43230765f7a5a3d422ba2e7cfeed85ad722551db2ac6784b04f6f8b0e1001b5 ea2adb2520150efa688d3c48f03a66de3312bd4c
+a46ca0d0e30ed9fed1eb8939c497f79aba95b9e8e4e712079d73a6e6ebc70d72 f0374c882b77ba4a05ea2d50f506239da26a60fd
+a4813ef6708e6011e8187224297e83e4a285f58bf5eabb1db270351388603c95 a4a7dce85cf63874e984719f4fdd239f5145052f
+a4c29fb8becd3a01d36d8cae4b12b4d3709e6b45eea54d27f2ae2e43c9ba9ae7 e8b638f771ea8e8ed9049b56148fcb2a86625f7a
+a4f0c2fd0cdb1ab3dacb8758af443e9536c5987f7c66c5fa89e2cc79a1b61c92 da7791a0235d172e7be4f5c299387ed1c95c0921
+a4f8a0eab46e58b3afa198ed68ef78d6fec944abd7ffa6fab63a8af923aa973a 521b4e69c6e0a257724d59f3c803f4eb11e0521f
+a50ed806c69fd2c6864e9658b0294feb4f7c24970cd4a785b3788beb190266d1 a7fcf7cf6ab5763af5a332041327182130235ab4
+a5191d9e8f879091e1ba926dd387711ad308a6a0b110dc58a4c9897209b14605 523412bef70b1d4cb92c574c89bf7225e205d489
+a52d36b5dd7c95a9e8596f01aded392be81ae538591f5d990459592bed96be7b cc5aaa5fa825a103a547d381c7a5c0524c4853cd
+a560d1fa1edf114f57b402e16d662c17b1e3b7e8601ff7dcea6615ba7f1e48ef a20d733a9fa79fa5b4cbb9639864f93325ec27a6
+a564b979c786a7a32c6e8477e940a2a43238c57e8fa6be1f8ef7954944c41b89 571a65a81061ea62a11fa210ea6819322be4cba6
+a5ab6e423fa4649571d39b9a29484a0bb17cc70534ee6a410882d72ab1e31597 45a5f836b0bbdb163a13ce0e1ba1e8f7acffb5e6
+a5ad182a309ce0243d95b53ca5eecd2a139795321ff24f6fbc548ebe1f425729 f52cd06b7d0641754c6f7124382b9fb397e4070e
+a630a33919b24191dc33f2e09b46196fbc7cb4b866b6416b8980431735146a5e 0392558815cbd6674d2187260948cf5a1fb22e6e
+a657e241eb57d29d515eeb128f31ad3a8012e71be44872b0778f278df6ab9e4b 0735f982dd5a183a7fa901b0103ea4b0719af5e1
+a66bda0109d2b3c9bc87970da81bd91076b5f871febbc860f09ae997668b6800 12bf5f3e3470d90db177ccf1b5e8126409377fc6
+a679511b71b78a1b0781d6cf5e3c4524c1f213d671a5acc777ef36a2ec9b0f37 b98b09da1d6c4ec25911b2fd859282a41f4225e1
+a6ce423d816299e53b9b87164b3153dc3a4bcd733c33be1c2a13f41f140b69a4 f494e47725e1cb76e98b5aeb9bdb7f8d8271537f
+a710c2c837a5a6f27f2b7f5a198c7797f148ae6a37a2209748845adc9d0bef8e 0087a575a0655d2e53e8ba4feffcc321dcb8cbc3
+a766d34d210e448439135e553915e30b62f09b926c669b7e2d5cb073e96bccfb 3bc2493e6ac633c6119b7899505a5a0513ae4e44
+a787901c8e7f31a8450a152bd8aa7033203693d8ce807348e53125a47afd7c24 eadc9ce6cb6cb722723e228d9e44f001d4236b4c
+a7b3a389f8d3a36403a8b2bdc6b9b2579c81d79f91347655f50ce4356b4f2831 7f07667d4c520a81c4fd6b5631c119bf2de791e9
+a7b9c8cf9c7469bf18c8fd079039fb8d1de1867de46294726c110899d94b5ae1 3d2577feb4985c61a7e393777d78cd9a8e8a516c
+a7bf36aba7ea2fc2eca014f1ff42ae012ef82b450220cdd5e77d7c5bee306c71 c10a39190029484263750293825632cbdaa9757a
+a7dc9999e70a00c97b97f71fa0fa877e92e29a09ef6eae72de2ee112d3854ff0 42150e0aa48a3ee95ffe74b7f937111a4afe17cf
+a7f778c6ab0c219aa051ebca04abf1397864be8c9c69873792c6bc8953426874 ae5ceb4b269403c6d1653c7bb32e71ede7398892
+a7fb6dbb2154115eab700f4e4a6a6eeddc30d3f8a14f0ccacfa98d9347ccff8d 4893b75d346a20df101789352b198beee55901a0
+a86aa44f58a3f3c347dbd0f99a84c1ff6ddd41c58b19697c49d3193f65873a08 c8f5cfa836b403abafe7256ce964e65b24b3fc0b
+a8a227647205ac1f01b263c1a81b8776337124a6e519b67ac69ffd4d2274f682 ce53c27f666673c2af8d406447078ea03bd95f6b
+a8c3ec591a4e8c04a28e1251e391aa6218df72cf9b5f0ae0eba6eca6877902b6 94996dfd9511a12bfa9190d97d33d2bf6599f1fd
+a8c79d136e0c69d1c0d2281bdf7acfeac0f55d6239b0141e17a6de422628c7d4 696bbac1204a1d56b0f482568b1a7d504747138a
+a8d8a08205cfdf47585c32111e5327b328c0fe68098eeb58c939cab2e83785e7 3f88988aa04971faf2b1170fb8a5f7a0e2cf5f8a
+a8eea8c78cf7e55ec2cfa539d860b5e5bcc4760277d1403f2c3d06c583e3905a a0cfd1297d1eed63d0690c167d67e50ce06b6791
+a91887e522da2b66ed048d77858041c9cdb2fbfb0b4d463b971911217aa3a4a6 c2550609e37e0778814d328c47eb84a7ede6931f
+a91e8591cdbcab2353f3d0f75a016d31c14157fa7ba9657bb72bea1772be10de 82bca8633fcfbafe94997fac9fb855bd93e36bcc
+aa11e156cf0329ca3cf624ca8a185356315552962035a549ddf6f09268402084 18c321ca3c1f1d360c8f6c793cd48afeddb4b54e
+aa4089cbc630982cab278c49d506c357fc9a8dc27c002ae1a6f8af32d5bb3e89 966f3c578c405f92510a54de59ef2263a8b217a9
+aa61d4adf622265ec814c1a97198d2bcd3f58fb08989cb9beda32a4d0aab6697 bf92206f8b633b88a66dca4a911777630b06fbac
+aa65747207487983a77cd65713928d62e1b326b93a330ab74e9c9517b42f9ec0 75d979718393e2d0e6d1399e6c1e760cd739ce35
+aa77e77d6dbd35635d786aa503c8a85b08274c73ddc420ce75b66b8dfac4860e 6e9df7119d93ba93371c317285688b49a40f56c9
+aa793f0e9d9d746eba8a7a60cb4981f7e24ce9691910350d7df9b9e94c7567b9 e01b107b4f77f8f98645adac0206a504f2d29d7c
+aa83de8b0f4b0c6d26c97e1048eebff482698b79131cf68b0671eafc40444d08 8654e891d437bfac94db90e4c1f6878e0efd052e
+aa8c472ac90d32654157b4a43e31135a9a864a3f02c2358c3b78316cf175b5fd 1cdb9a4f4f307b8d0a3439d6d496e7ffa0a19917
+aaa1b07f32e2a452b7c24d7068bd63f27349668ad1c834cca9d9a2abc2ce49b1 4260699b37a7a0cc6be231d232d424a858a7d111
+aaa6f15815044f9acc8541d8d28612affafc1cc820a084798ef762ac6978784d 6bdfcbc41317d86eda9bb191e87a57651e8c6245
+aad6240d16f18620120c221b902c9777d220fff8c5a0cbc8071ef6e826f6c630 4801a2389fa38676d2a42061f5e3841324667b10
+aafa750212c82433e5b7a0ade7048fa1951800536992c59f6d66c46b2337ba6b 3cd8b687d5dc8006a5eae9e9c4dac212e8df67d1
+ab2a5eb5541f5accca9d9491bf53002b131b547493d6c248c1f51628468b91ee 157999b26778bbe322028a30869648267bee7995
+ab4ca17ec21ddc7e0bb1bff660ed18d90053425adf7d85aa320454e873a18b75 c29a831c884ea240437bd2459e60ffb383dcd149
+abee32b3339d1566d75613ea61f40c14bdfc5b101b60fde4f44b58dd06667640 181037049a54a1eb5fab404658a3a250b44335d7
+ac2a850fed51b1da02abb706f3da5768337aa2d49b414add21a668e2497ae125 2cd78883e494f23858067087893c38219aa4fe76
+ac2b785aef8cb5a9b563764be378ea45a50cf163d8bc37daea68fe2cabe064e8 6eef8dddafa405da3230fc1dbfbfc7ee8565fa26
+ac49410f64fad10760838866a40107573e42c86908c83ece433d64b7a8b57f7f 2f1c5d509ac5bffb3c62f710a1c2c542e126dfd1
+ac7908fb8ac8b6f3aec395f7b5a82f320ac634496b34c4e5534bb291bed1a8e2 c6039d8f24940f4bebcb7391e705b6b375f8fb93
+ac88849a26c126b03fc6fcb17cb23ec563e87a5f63b7afe800ad0f436128ce98 af292c99c6148d772af3315a1c74e83330e7ead7
+acc7061eeb4b06b989b9b95db3a726463793a54752bdaf978f2b643064e61650 37870a4c5009c19f3da47997b40b786f6f2f8b58
+ad00202dae3bd195b18e458e7f527e04135398a1d5f9aa3c25291803c26482de b6fca797357066296e2cd9bc9bdbbcd2489a67a4
+ad05c66a177f844f8239a1d186a1f803a4daa6286c959538838e222bc6337dc9 4ff1dd0992dd6baafdb5e166be6f9f23b59bdf87
+ad150ac016bb6e3d11d5d640927038619573205f34285a0270426b5d0d9171ca c867e4ff48cb00173ab50011a7cd593ab9d3905e
+ad69d65326774c8e0f1a8fe7b218d2f4786db7657a5d6b03d8f43d8265fd3c44 3d35c193ff6508d92b3986a52c103b2c1fddea0f
+ad90f638cb67720b20b904478471504acebacc7bb36e5dcad3e882acec496fed bed08a0b30b72a9d4aed7f1af8c8ca124e8d64b9
+adbab8789a2829bab3ae26bae696810557ec48f125e57e26e25b22a7f375a551 c405f734c2050e2e4ad3469d087368a29a98d27e
+adced03f19d05aee4179e749bf666dbadbf031a12cd11796e4e463b0e2eba5ed 5ec2b329adcb74a8bdf99871f0f6c048fc90de70
+add26f5a369d9230fda1aee868bff357cfb63e39bbc21abcbe5e87a77dd70393 8e0a161f97b84d42c44757798dfc0449b1ad3c9a
+adde54361f01131673d95e3f74e73b5b4645913941e061efc66bd5c85c40bb02 a0c0512a8bb09a7fc87c41528ba63083b44056fd
+ade7d297ede7bb58008da582de1253f0a55cb76e82d1cd376f82ccf97f70bced cf7992bde17ce7a79cab5f0c1fcbe8a0108721ed
+adf2fe0b01c0f20184cc5f4cafbaa7dd71f292a8f06ed0ae3690b21be95f661d 615cef56af79a5f5b39e4f337cc5d6e5e5c1759a
+adfb7008e983eca01cd37bbe9717a22ab7f62bc731f2d5bae26fb99589628853 e33a4bd5f9681cae1b8956247dc73812a7406615
+ae235704494a6aa176dc2c4dad7705e40ca8932b44796287278bb70878206bb7 b92ae7738d06508c5fbfd1f2d08fb5190343ffc8
+ae29942a0032a27d0dd5dd802d5feebf1500cec3f37b9b0997070e9ade4df219 f6b73d281810e3ecb7e984ab7c951ba52b72c10c
+ae52d4711e8115fa72fbb0397f995fd9163849ddd522582c1efee23a4371b846 bc95d2dc839d6993e4ed0a13966361fff93c270f
+ae558ea7cc33669b88e4cba1d9448d5638f5ad5c6effc852948555f57f8321af 8baf75bedd0f29bcb61efc7c7c95761dabce5e63
+ae8542d347eead448e089ffb0c35067c2df35b7abfd9eec4f62eeb701c59b05b b7af25b7c9b2f436fd6a5cbabf9d974d6111f6d1
+aea29dc305d40e362df25c3fdeed5502fd56b182af01b7740d297a24459333c5 a8233120f6ad708f843d861ce2b7228ec4e3dec6
+af17bf05f3b3dbd780997fd54887b276c0c972aa0615ec4cf8da9bd960d560ec 1181cfdd77061b39dfcd72660eafcc1c578fef0b
+af2ed32dc25c587ab2afe464a73b49a0c5454fa2bf12ae538a2165c373ac2de5 bab41e61c338ed174de4cad036da7a5edb40d28c
+af34d66ddd3c768aae865ba6958dd9158ea5f5c47fbb5348a8e359f85f89cbef 9e4700a6033d42474c800cac6dc0095bebb14c76
+af4b69c54262883b42d42c72e8383a20843ca1521021808f73dad9216c5af6cf 6607b1c62646fb35b6f55f22c11a1aa14f0a1145
+af8cc9a93a75ef2e04ebd8aac5c62beb755ce4c1162d8c045ef578dcfe25ceb1 d9f106b9f6fe553c649cf682ed66b72567dcc367
+af90f5cacbfe4caeb5726627f9ac36e5bb49e8497b1d5428858cbe64a332cec0 f161ce12cbe824defc0e445bcfdbbb6f3d5050c6
+afcd6780d3255be3d03402a43209f1c5c32cdb3bcb23068b94e6e9247ad13e7c dcc1b764890f85180542055150c2eb8a7f3cb9f5
+afe1fad6f6b22eea530ff7b373d6b9b787b39792f720e6fcd0692ba6ef99e02f c0220fca67f48b8a5d4163d53b1486224be3a198
+b01b505b9db0c6fe12355a5dd4384184eb067779e23028ac2e1ab9a504bb1bf0 14be9ebaa30a1d6085e889eecaf2a24968eea24e
+b01e2a8d4c07527e6492e7fad1f749dff75216d4f129e0d11ca5caf9ab6d8273 b60488e1d7564716dc2ae7ce078a415280bd7df6
+b0516a64349a18f3d4a48850359ab66ec681ee6575103523acf4fc9b5e201e08 739bfc1798bb97563e72c0491ccf8fdfecaf6a47
+b0675d24f637e6c2c05b09b88dd2169fa169c37dbfade733a5e0af0bb1e8080e 4386ee2acbcbbf1fdf06c75b91da3a9653075c6f
+b06e3a0bfe758a68c5c6b9720f281b2526c19564179a19a0ba1844a45b089e9a 8ec389978cc56e25a6a5bdf6be993c7664e762b5
+b0a25f084f327104a3da545a926c6b89526d5399fdfec65d1accf43c8f197bf0 6e9ac9c85e1329debc18a30c520e59130d5b190e
+b0a6aa2b450048e43155fd6d655cada03ba242a2c1b31438f93638a69e891727 94093ddb4164980ef98d78b1de318eb0ed74d933
+b0cb0a55f09a8192d882c14405fa0e8a366db3d07c17214f41614fa571566ff2 3e1d42b7d980e29595572e550d5ce58c0a34f15f
+b0e890ff4d407faf502bc1eda5a18bb9f08f5761c2da4f3d6a6d563dd2f0b1e5 ffb55c532ccc1cb3c47eeeaac6c64240ab88fe29
+b1328b04eba6d6c573e7c7aa9eff201a654a1cd2c077ec9fdfad0499d259e7bf 2ca59d57a4451602a6db44da4b2d37a505a84698
+b167ee336fa10383d04670681af258d040347e6e1edc71d61af722cd19f1192e f2ddf1eecca39a307ab8544c2076a59441eaf5e0
+b185929191c7ff2f9df80b08b2698ec2811f3466cfd520aa4df2a8b777aea135 89b765b0e9f0b54f9dc85fa0a982774d07a935fa
+b186cc0087e119842e1b7c70dc70f705d7da0013de7eb434a6f48dc299acec75 57b5df4e635d8324ca75b0dd33cb96271b426e23
+b195873b48c824d995c974a3497ade7f62d2cd818bf388775cfa721de4068ebd 08b041783f40edfe12bb406c9c9a8a040177c125
+b1a61b0e46d4d5ff7268e381a6f7d32eee81e5c1304481e62ae5fba97870c960 9eebf8791489850089738a95773d2f3c1d56d89e
+b1c6b80f177e1869b39215b2ee4089474d685459fbda1bc4936457567036812c 7e822f212370d60dc701504a5512544891f3ae05
+b1d6e5441c09075f9ee5aa32d8501acaa0c57f5812b316a74bd7911786dbb1b2 4915947ace431f2d1b4fe920d4cc446ca2388169
+b1d8511cbdce9fa2ff79665dc3ada50e6ea82b506cf11decd40539e6478b4458 c1b62b2eded035d22584c427487503335f519d4c
+b21c8c27a05a3f0bf9f0f44ebf05e11d9c591b04cfdaff7cc860310356d71827 db4df74a2fc340a0d0cb0cafc0db471fdfff1048
+b22a9ce429c6518de3a48dc441da4387fcec90fc88631499e61702ee5a8f8b30 345881abc471d0c65c8bb7e4cb3be10571cd32ee
+b236af68cc7637d84a5cfa565e8b666c303570844c3d3dda6534a30b4824c352 eb2da5c7c3cbd270a1f945007ad098eadb4a68cd
+b2732eaa1b3cb8942b40bf8da2ba51b735540dcb5605c29b694d7bf1cd500e9e cc15381ea2855139cb87d3a5d9aa64841f98c1fd
+b2bc5b027b583a336347be589add47e0a89fecc19dd8a154b826c363af9a4345 da7ad841bb28fffb5546b529a00463206e47c8e1
+b2d355727ea29d217971a84e2a88527e0f6996015bcd01e4a2adb18910dc3aad fe6f9f29340241491470ad8296ce178ebcb428f4
+b30bc6949534a21693aeb43b8379df1b6dc621136a2c6c356e3f6bb86b9858f2 d570b36d8ea2d61450a40ca03c3cff9b144bd147
+b3364fd9c7680d53ca88929ade181defe8a00009e26e073ad7204fb7571aeca8 da8182721657446127a006281e0f1bb4219be952
+b34f4bbdec1daaa7d6e1e99aaa1e6cded1bec27170881f5c6527fffaf55adce7 1bb11859357114684a1a740e471edf1a7541b82a
+b370899e23e1a0b5e50780586fe822e2e6e17dc03a2e56020cc1d11e072f5160 a9984a4e60291958a7cf0045758f47db3d214040
+b3836efc8cd5eb1c0ecc0d99df3644902cbccf9b6c7c38c3fc65900492cacb21 41c1bdce587d5c8b8ca03a5a8691ea3f09f16316
+b38a7a2ee69d55f021efa91caf59e23bccdaf6b8a9c3f83acd978aa177587537 bff873e9853ed99fed52c25f7ad29f78b27dcec2
+b38c89863825241eef7e7090bb11233910a0117436211183502d501ecc5b2879 e4e56117de8b3bd0bd899701da4712caee27c7d6
+b3ac32466d95d82432e7aa5a2080be9e3cf9b69f8620177053557d982a5f6eea 6909925086c86d40934c73e6d7a4b3886d5037c1
+b3fbad0e2e846628da5954b92c1c9b9bce24236f166547c5352503d7abe6984d d132e975594a1f22a5f3a480f5695e6001367b53
+b487428ddffd22b992094ae29527df9741cc33075a2ec0f4533bdf26459724ba 3316387b546b1d495ce646422e95dc209cd23705
+b4b7236050c279fcd2b6b05b6539c178614db05615eca1895ee1e9de98d73b44 a57d1866c26829f12095fd4f20f359e7552c52f9
+b4c3e22b042d111d7546f8d3bb4d3468236d28caf926732b8ef6c93eefbad39d 0cd5264e932f5dfc68959d11b5a3bb3a8714aa87
+b4ff5a6d6ac893413f20f16c9b3c999a5703e5e1238c50585ed32a64368a54d9 334fff1495c554746ca66f2359281b7de69f35be
+b51d286a9549d2d2d6ee18aa839b2fedb8f3b4235bd763553a2a415548e7c81f 6e0fa05b58c140a42ef25dc74041e30f25772b2c
+b5454cc2a66d713d5be85d5297def275ddd102817d96014c46f438814446e29e 5e98d798bcc9613cc34a083719c378d5c66bb9a2
+b61b940a8cd979a32e005682c5c09c22053675e2db24ea6b4b28cc75e9c10890 5f34cd6e3285089647165983482cf90873d50940
+b6cff922b0be268a213f763aad8e357bf2f30e905ce7ee47d586eff02c5ad26a 1aaa4daefcdf508ae661e81131d810472fba488b
+b6e8427e3afe1cf0095d0f5aeca0642c4fb12d4e529f62e8264b6c4db72f04b4 6fe7568fc3861c334cb008fd85d57d9647249ef5
+b71ee6c8837efa5b3ba3361f88c321d391ac05f41d5b2506cad39319e80716e4 c02d0b160b82ee72469c269f13de4c26a7ea09cb
+b7238ffe9e20cb2e62107858809248620802f8f15c603594daace4681810bfca acf362a92101202f5f09c9b51db352be27b5bf7e
+b73c6046e934b9a76e5b9c587c18a20b7a69852801d6b3517bafe09b78d1cec4 964c192fbc2997e871c6101cb63f69b3ec377b66
+b75cad007d473a24d06563276d7b254378f2a0935d231cd82b5332944f22ad1c 7b6e8067ec96acef9a4184b43210d583b6d2f99a
+b790ebf3f307102c84da36abefd196078e569ec4279f9cba04bec0ed68ae93fa ffc359bfbb59bdfc5ca1fc95c9bdc618f89dd8d7
+b7bbec7e6e8cc5a640d966739b0c7ff09f5710f534b278ddda5a5edd554dbac1 d42e47121844049cea15900ad772f51164397573
+b7e7455bb13e40eb23c5521f75bd912da55c7cc4882f62cb1a5ab0780f04faf8 a732c71d3e34cd444e9e0386b797207bc116ca42
+b7f1e115ee187338ff5720b118395dafd6087ee4ebbd63883bcb4c239cc1ec2d a59b93fe881d5983360984cc2f81b4d12d306d8a
+b83624f6ac0995273c0034a7ab8c68929bdc91b69ad54ef94979b93eba3f6022 9fd738e8f7967c078dceed8190330fc8648ee56a
+b88071883c4741b0f044dd83f33c8b7fd514a6534f212aff1c00efbd96cb0220 7f11132869231c5461b911c054577d101cff4b1b
+b884efcbb7ccfbdb587e2049b9b28a7d025d3a1a5f9c1d7708f39a2a2431fe89 04133b0608f824a5d662b85c866b2e84244ec0c9
+b89ca9872ffc92ed33fbf3e18f6acf5f9ac58c70a7408137a6f042b7ced845f7 40e1af83bd6b13b718c52673d2ad68edf54b6d6f
+b9134c331629e9cb29d1bbc03904d911a942b451e087543fc16deb0116391297 6fc72e46de3df0c3842dab302bbacf697a63abab
+b96f8b46286c8c2a0e73d2e320abf35334e7821e561a888f85db612b2871a1d1 f4113b8f6c796edfd95d2d178dd27140e8d5eb40
+b98e3de756c625603d536f742b69bf11955d439631f286712927f5975d560e1f d2ef83fcdc0c9cad633e7b654ab789d17f583f30
+b9fa227a38b8d66e3c2acc8a463ea37768496b746d370b95c4803505fb79b84e ff4fd6b1cde132c6cc6278ad7873d7047c897d2f
+ba038509caf46d121547a01557da21517aace95267ee6ae54bb7db1776fb5825 ccd7ccdd6a61f941642d75c3870d82cdea9e6fa6
+ba0a8d2454aefb8f2723fb6fa5a1424439b63bf2f6f47d575aee73a76a542a73 b4b1f3118c4afc6034b5f99eddd18f7dbf15e5d5
+ba0dc4a1787f9159a174b1e010018af3ad2f5f84c8e8eab98f67540663ae82b9 fe4db4d4c93b5fc5d85aab2198393dcfdfd390a3
+ba312594582a5a746b8fe46d639f8db0314522ead4339ee6b7299802443ba5c0 d9a9eb65ed8db7f1200aa017e5ea2204266e7ae4
+ba3e0a5d754cb8fb417f0b8826d943fce3007ea5fa332abca0eaf31ceb99caab 41cb5ab070140451804663d84b8190d1c5d546cc
+bb04bb109db80e12067bb064a5e3eb34c5090e41b4ea8f289c2542efd21fd8a2 662a69f7b8260cdd2461918f55353740451b0ae3
+bb5497a9ce810703e014a2ac6fa1eaf22625ab103c7d141ef40343fbd9db2a71 89442107982a1e968f643808a9e4c9227c1cba2e
+bb5d090bb1914554341daf6c1dd6fe4e5c25e466818e5012ebae812392b58934 bc2c876e099e4a12b7b28779abba186216355c53
+bb680fba7ce88de98d4881df75bbc7921b97102a8c67ea9d9b45baf765a4c354 0a420998aa494c4b9152041bf5d8ebb80a2c4cb9
+bb6a768c5f4888cb5f37b641ae0475c1e8806f53e1e55acf6befc7ca727a170e d1487fd5790bffdaa06c181d53cb966a4521e899
+bba3e6952c7a791c03cb65d4919df3ad767346cde441854153327e24a829c1d0 860bc8dfd43318cc0513e98d4696df7378b6077c
+bbe6aae28af3c8f82c5f1d6cf882f4eda9165750e08de45e9796134a294bf83d e074873b8c02e2a3d3beef03dd3c8ee02cf3ce9e
+bc1b9a106a7ea9414b5149bc9a25a49df09cbbd91a99e5ffd53f52462792e28d 5abe9edb69b20ea386597b062e94f2679cb81fc6
+bc3c9e7c3339eec5a01dbac0a679b06e04124c4f51c79fbc16ea8a68323c0654 fbbfdf9f13e92da2dc31b56081302f09fdb62e49
+bca7f0c01fb672c05f86199908ae2f853f58f0adffb9b97b897b37c2b5633cf4 0cc8c5d79ad42f4673374b0e9cb00c3777e55a8b
+bcbb54aab5ec52d387c70baaceb1ae313b312d354319b95c58b59d5140bef0fe bc584bb33674be3b3925c7bbc129f1f53af21869
+bcbc6e0586bfa74313dcd57a61c4e958abc0ac7abb3be0f2d82d0d08a3d5b357 755a4f75d2184b2ad4ef107932c86c7eecd0806d
+bcc4fa242e55c5e08f64900407dca1cf2451806830905be616f339bf7a5580aa 50298f44a45eda3a29dae82dbe911b5aa176ac07
+bce08cb2060cdbba7216cdee466762f3cc8e3dd655387e5f5092a748ead67f6d c3ffa781d527d08ddd3cf626ade4685fadce498c
+bce2be789b7c5f0d236cb597083143d261c705c2a66a88f4ed547ed9e2065793 87f7f37296ff41d6f2c8603d68f159cdabd4510d
+bd15edba8822cf76e60aec16a0edca6b464f22bedb2f9d55078f51cfc7134405 9ddea2008d33878aded26b0c64ae82ef6def49b4
+bd32ba8cf33cd3049b5dbb3c18c0b0478a52389cfcddc7e7b80f66577dd3ad0e 6524d538430cad9215962d6d01b078afd7720bc6
+bd531ed2f7d5971c642a86f8fbd5347e3b85e2619c891ecd4bcb4e23976f35b4 0bc152bdf2707576e6ef9331189607c89f9ef8a1
+bd69bb8de277fcd70b2accb62a064eacd6ad2e01817188f5577eae25cee3a35b ba6f870d2cccbb6439c43979a9fdfa72c05b1a73
+bda24eb65236b353a3c5ce73585fca46ac4339a386a463afd3e28f69fcae213d 8c7af5e16d87c12dee61f9aaa6cfd06ed93a08ea
+bdc530e0b98dd736cd812408eeed9aa0d393bbd0630b355eb7601e61f0dbc7c4 90f9ff6755330b685feff6c3d81782ee3592ab04
+bdc6e8229c3beab1f6fb0439687b325467e2a8fcd6267373881c429f2f9a3f78 4711d372e66dfac8218c4d5eafcf03dbdd4706ef
+bdddc903f4cb49c257ce31b6e651349f9808d82c320223c339a66a03a9f7829a ab415d59cdf8cd11da6d5281c75423691c0a34e4
+bdf2066a28e11603a1af04157ee4aad97814279fe500340eb3465797cbd3be23 3697d64be941a53d4ae8f6a271e4e3fa56b022cc
+be173dd892e4502149db328c9372962a08ccfe705a22fbfc89190dd52ac9f939 2a6d97c7907f685ae4b18d31d2ef6ec7099298f6
+be218dbc2fc82105b177a9a07b4e0f615ead9b9be02b13b2a239265812c1e739 8c1460102ab097ef014291e939693cc38daa7e98
+be3a95fe895bbfaa243ae46c89573d9836914465791d41ef6404c14453ad9e2f c51355c1972fbaf3cbb5c7be21939d2f4445e9bd
+be7648b598b8c830353f3d8a9da3b434ab4b636edd7c5755171912682061e943 e5d1faefab1bb51f3d0fff2d5e2cfe4fe96a9c22
+be954c7dd99bb22f2730f3c65b8a6ae9d1940cb909cc89e6d83de1aa5ded6946 690624bdfc9dde7d14609ca798b61bbf5fc9e9c1
+bee70212dae887b4181f4c256a008a0509792568c82ce6f78ea5170dc5141b82 58b932645c948010c10a9edb2e7f1f51fc165230
+bf148c977af56b2c4f132adeb987da6eb63b309d7e1755889bd8c17cc9e28520 ecb100a9c8feb1d3bcdd6e13ebd4097eae84be9a
+bf278d619d1bd8cb9e47d70df1929ff019a1f6c4d8bbe070628213778238551c 1f798df229a70aaa4ee221ecc0e24025ad92ed9a
+bf37e4ecb48fa5813c8c587c13b1dbf65c9de2278ca1093b02d241bd148aa36b bb2bad2a2f6cf5dca3d3157b68112ef9cd0c5817
+bf409f1aa76256282ebf1a7cc6c9f4220be9ffc47b2cc42248fcc5cbb67bce1f 30c62a2d5a8d644f1311d4f7fe3f6a788e4c8188
+bf543f7bbd00b73f6ab1cd8203c5279ee3c319705580525916a86f2916a5cbe2 a0d9ac96c359e218d35b6faff16e63ff6271f1bc
+bf575bb55d3a86838a50d8c74f46cb4f00515f36e639a1c169ade92eb7ed5822 82324ac1a5ec79069877ed5d807c87762a16ed9d
+bf66badfda7b5d2157db65c5310cfdc4e904d7d5da57ac5abe17542de612f856 8f0c4b543f4bb6eb1518ecfc3d4699e43108d393
+bfa3b3b9a161d354e2254a444b12c412210e9689c17e51bfc318ce4bb4360f19 2d59075e0681f540482d4f6223a68e0fef790bc7
+bfb8433faf78ecf75ed9fc3bd8a717827bc186db50e525ec46ad318d6182f91a 0a7ee40d31baff4e6d9b302446547e53bdd08b64
+bfbe326cc9d9fd4dc3904de4c9fb919194f3215baff612b6d3f403e2522fe163 8382cd353101c0963d5f401bbbeab5f6e0248f07
+bfc0ee6fe04854c11011539f38cc6b9b73e0c445bac2008de2fb877123efc2e3 e044baa468a1c74f9f9da36805445f6888358b49
+bfcc4074ac517ed24d61b0aaa96359f304c3dc97e95f336269ed474ea846ada5 5d0f8f7891e872d284beef38254882dc879b2602
+bfde5e9939f87db0b49bb0ad5303c14d555a32b2db03dc63218af87fc9ae7cbd d7a09674532ffc0bda73e7c6e028bb78120c85e6
+c0011465109f2622a12479cb68d0117547513d48254a74c1a905ee88943263c2 e8dc745f5603c43ab9d69e790ba6d600f24d108d
+c06ee60e4559ac134deab08997476c059b4baf28973f75ac00310f9c0c0df532 e8abd2f25c5cfb7e6f296699f1bc120a78bcb7ba
+c07142deecd175549344fbf31f3987ff54e6fdd013b15d502e83f67edee4be4c 67df18d487d28edf3fbd965836f212f7edd2fa6c
+c09c5282575731a965aa96bc17770d001552a22577cd80110737c2889a2b4833 4b09231a1e78cfc8e19477c2302ab80b689cb8cf
+c0abe8f37fbbea90e1ff3d571c86ea4c6fadfa7f7f12a4a712c821b065f2b0a5 a0a6eea81880dcc9841f68766351df223be95f1c
+c0b3f002ab7ed5b861ba27d5566f8d54ce86ae8e3f0d7039eebf52941a164e0a 0867c7b57704e5c98cbf67d46df03dfd549cb0b0
+c0c38ad9820923073ff986beaedb4daf3217b3028e48622bca80411442ef6047 629bac23f626ff2f0aff71580b3f46742643b14c
+c0f1323124d0f0a45ad1511ff43041f766fe110e56d0b063d41deaa9f443b61f 5fac2a68ae5bcead128415169a68fc21d9e0f038
+c0f85283375a304cb98d2b47d3b0456a10b934bf1f89bc3f80d6c4df2087460d bcc85e9ef60749eb8a4cd9db1c9f7b3b974ff407
+c11677e589c7d737baef01be425cd0c2912b78f6e426f1750a3b7d5cfdb123ee e45535849cb7187e22c9fd11d14e0eb37f5d7dc8
+c123679643c715765e9fe6859d5a5eece56404b03f29266eea7c0e83ae14263a 6d646a4ddbd3c0fa824b193aa83b48b38b48848f
+c13a073132718d2674e3aa4bc56a039582e61cedc9e178ef9862588a7f8c7675 5cae6c25279bdd525124b7686d67072059bb0fd5
+c15299caeb002894ed0743a79dfd1cc520d3aa71a2a84e377ed2fd7827096969 1b61137f9a2d6f90f46ea3a7fa7b89b8f839b202
+c159593092a4a8e5c4dfd9ca42fbc98c67ef75060f87b92c4bc677b809be0a80 ca3939e682a3198a3d18f42b7fbf84823312dc61
+c1599569b3f7b5ab531371b6c762c9d578a794e6289a1943f6e6e45ca02ea05d 00f43bfc81fc4ee3a1f92d56808aacfc8089766a
+c194ab25a95d8b7d4897515a6fb62a723e809afd7c1039b7a50fcda3c2edb2db 475fe13a56409b64b9ebbcfa1a0e77b27d21d189
+c1f503e2869f51ae5487b9c89dc93892aa92e58dff2cdbf19348056997a17a8d 94c3c71da52ca3d4761c4e9041b384d9bc75ad9b
+c223ddc0bf50e860fd0e85b9b4d47b74409106098826fe330a9d98f2e3a8ba7d 1ae06d0bcc8ac256710c81ba92a16a644edbf456
+c223f1b579bad18635efdeffa7a8ff40567d03fa427e08bb90d9878f958d8021 deb106bfd2d36ecf9f0079224c12022201a39ad1
+c23068515ad0d427ea509d3b0a3b2a6c5d6ab433d6531a10da6b7d63ca0d3594 8a3653350f78e3e8e91f2e14747d6dca4b435d8a
+c258f010a08328a29cde33411d955520e0375fcbbcc14b7636a70f7536c32ef6 b25fa35b38051e4ae45d4222e795f9df2e43f1d1
+c259153848cb0a2d858dbcbf7b4f40546f2fb60c31421f9726da11953cfea9eb 8997caa24765077a3c3b70deb995819488bceb90
+c268fbe2c4169776699a64bf021f1767ca74c21970090388777465437910c3cd 5ee2fe777c41d9ad9e7eb9fece56fe21f830c5cd
+c284394baf0418c32e379cb3bcd5caf190e83e7a8b5056789d800f27be6462aa c5c58a0dad7a0cf18859f29326a0b7a44f23e4ca
+c28e313f349b558f23f9b4c7d209d5f0faa3ad79edb8260fb3fb154e5f578905 22bef3bc97a19b18125656bcd77b517680168970
+c2b1f6e286be2959e3725a1d890e178cda1c31116467d5cc0c799825b17c8d74 6a5c2bc91cc42f38753da59a3291a6d9bad0c852
+c2b535bfc3501f0b4e179d5d6f0e2cea613940fa3813be5923db7e71e190849f e00e46abe4c542e17c8bc83d72cf5be8018d7b0e
+c2c43938f901a6206679cf488e48f38742c36dd7bee0e027fc298093b88d9cf2 b3106cd34a7b69d2d3b3913415423bb2c45701f2
+c3055d62611a3247b1863e5b5955ebd263d8b8e3367d8efe283bfe0462b4db1d 19d13c65e11f45c3be6666c6c4c9732c6f4ab40a
+c30d06fd49797e3135652d654db0de122dd83f8400df1c7a0e95ca3720defb0d c073337a4dd7276931b4b3fdbc3f0040e9441793
+c31ec989f2f75e339b9b7b106d5c1f5386197bd4bb16140fa8e0305e03ced1d7 5b7487bee63e7f5cb6ea1d9090d52ae631296cd8
+c339c838d5d21d9c6c6e4b7efede56c1580e7e957b5da395cee2f689f108709c d3cf2a0e30b4d3ab11340efb3d9af75e61b7de8b
+c3f4fedca384b7225c7bbecd31c52d6158b1ee4872d0a85fce2d44ad357a6b3c 581dc350d5e699a9428caaf6a59fca78709f4bbe
+c436565f09dca3e9556afa41fcc79ecb57c448a462d6c310df86b8b35d5fb604 43bb43402ea3fdb9ed6b4aa82876c38f46d1eb72
+c494dee1d03eddaa9eb923ea640f07f660eecb9661499495899c1601b885c2cc 81d3ab652c6af3483f72ed93b089edb999e7d140
+c4b306fe1f9564616cf048af4e566ea7b404ad069c972ef433d21469dd93056e 4caa8962a6af9fac72ab81afb03779bc3acff910
+c4fb0a710b3cafe7f8f43319d72d308a678c12cebf0682860b1035352cb5d4cc 2ba363e5885997c504921f8c8f7278a97fbe753a
+c51ca339b4668e4ff289c3e9ed3f6c11de42c85e2c5491fcecdad93432a2b13d b8fce12b6878b56b0d7be4ce7123b4feef44c2c3
+c51eb1e41d61d89ddebdd78357baaec018226bff72a4c9296934b1c48fd785f7 f1b16987ec81874cb9cc3d6a2e7d533c950fa68f
+c52bf4d013448d7c53a6598f85de0cfdbd2eb110a058974534ffc27c174a9ff0 92142d22d55a2d449d5c563f6e72d827512cdd49
+c5446a46d4c6637c2f45ce4fa113c64ced344a4eaccd3ecbf2107a85fbe18fd8 47015c6bbd6b6736985a562bf227a989c6340df8
+c564b89940661e3223cd7ded73de0206cfc21827bc0b9bc44e95d411d1df1b0e 01768b00b6b8cf1f5e34f7f416e0d506497cfec8
+c57f2bab01f9798f75e61c27f55a91c743b8d23075728699e84b9280dcaaf900 2812156aaff1a1d7785d56809e8cc6643083ac4c
+c5afc2ee84f7b25ef38420d60f97b8906c178cfa90497d9f5ac5d1825b0e0cb3 f76c2592b57baa77423bb382d2e86898992d883e
+c5dd7dbd64522fc4b9b92c6a56065eba728d026a8d9274c30ae26cca30525d46 b38ab8c4a1c9e346c9520ff9377be3ca45b62f23
+c5fa0af1a59da387fa8082d801573bdb342bdd59346f7599b5f8e9697dc1f076 89039682651474c6e2bf9abcc02cb71897f8b4e1
+c64531faf49ed50c4fb19066a734fd236e6bfe093efd59596dd7d802fd311d71 02e4e5bb290f13ce81d239c1d590621e2d146671
+c67a633f4eb848ff63a1941b3ca756dd08b5226684821768d918e0d839ccc4ee 833ae912e8d5bd6309a2ebab027b9ce85253587a
+c67e84e246ad6aefab06f12ef980425062b46b04a66efe482927dd937b5b76f5 eeeb57b574aa97501bd7f477330a2bca8d1bdfe3
+c6bb20cd7be8c9ff8b9cd6246f5dd2928d2428b6a0b2e38e7012e565bf7d9bae c6ebb4a9404e6cb7ed09f0d775c093f33a82f9b8
+c6e7cde92dc4e7be645bf8ec2b6d2ae9953f7c9ae934cea12fb087a9673629be 64883249d3cbf1db4523d95beceaf0f9f8e6485c
+c72158b0050e8c9fb81af7d4265c0cdc389cae3cf235232051eadd50f4c1f931 c4c199245f930c01729a0d7c60dc05d9dfc5f53b
+c733169129f390cf971099f88fec9eea3e23de7bedb2c72a0599010d6f9030df 6018dbfa702b818590ae682bf9bbede716ef290e
+c76564a8a44e38ab10e01f08b5064779775d050848ca85b41ef2b58cd07fb661 a1c96890e1b1fa314c4b4a3746c86a31e4a9d78b
+c76e3695d0e5e27713dc9b34d2e93cbe8a3c780f0c829ebe2fbbc9561fe2b0af 621c87ce34d888782dc907b62eece21f94b951dd
+c7aa36af51e9e39744a3d71a524f3228eb0ed8c7816cd863c81daa83bc33394b ecd016dbd912f8176bf9d8ade8b896e0e200ddab
+c7b0a7eae7a77dd7a50c57b9221b7955c083c8aa2401b20ab7db4738d93faf88 7b5d9df4c3cff229a132e5684f5e5e7bf6219463
+c7c775e8d426131b0ccf13245977f948f9e6f92f11bcdc748f2e005e8662eb84 84f7ad61f33939cd7bf04d8db04cc9ae7cecd8ed
+c7ff2786a6ce3a0531eb2f4ed36456392a5bfe79b2b53e8f55ff987b31058eb1 1e5dd57214dbec1b166c881c1c44c8930923da17
+c85e35eec23dea4089aee7a2dc7f6d937ad7e13c66bdbd7eef37bd6336418609 b10489944b9ead17427551759d180d10203e06ba
+c87c2cf84057545f38b55aba1b610af68236dbe0dcd196a81f69a050489572c1 088171c4dcffec240af3df4cfb03580d5bbc2a6e
+c9158067646a4f730de9e833b1c57412e5fdbac9391f1d374eacbef4d5906f48 f269af47f67816813da4b4a06935bec131ad7155
+c95f52be90f0078415cd38052e756f4937c8d98dd5404b2833e7d1505744d6dd 89525c4620a1065dd47fd7d3960cc560a289d3ae
+c9a1e208a73a83131879e9a986b05691ff7b082a45f79bd840f1b87e3bd34948 7335ffc31f110ce77ba291719841231921dcb17b
+c9ae9e3895579effbbee5aa744a268beacd3be882e7306a8361eaffd7de9d89c c16281e3250d7103ad6149960c6918b2ffa459ff
+c9dc53358a0d83bee1caae40ee81d752abf4962a9f206702e24a447b766b5bd7 fdbec189efb657c8325962b494875987881a356b
+c9ef872fd3e8065a122b989550e08b24e38c91b6598be9d4e8b13cd17f55067f b74eefdaa96e4a781d649d043524fe7620aa7e86
+ca1bf49cb84d97e4e068bb833a4f2f151401bffcd3aad55fdaf496aca3cf7001 1ddf7a3bc63dd2ffbd9438c3b25881de9ac0bff3
+ca2b746be39b1d1ae42925727ddfdc75ba08bc650657647da081931ed1a905fd ce9eddc98909836d71b0cbacbc05951b90880964
+ca31f7336e882a233a2943787c5e94ba024ac9a4f763cb1d9bfd8e63aa7f7269 9f13f7d0a9402c681f91dc590cf7b5470e6a77d2
+ca3660522468fd1d803bbf34a9c1685a0cbdd1b923528d69e6103955b03d749b 784536a2a4cb6805d0f64821337ea0b4b20daf0a
+ca5237cc6b66c598930504f24af69c3011786d0d9aa5d5da2219f2f10bdde3fe 22237f6c06ff14efd3c233debc41b91c3df29514
+ca72a5c3e0c926b9e8abb95d8cd4110646dfd0dc5f2b283d48b781cbe7ac80e3 7b592ecfb272916e95ec2180a02361f23ed1bb37
+ca9468483c7625c1ec1fc8e19a3a2cc3697d9caa3c4d9df9e51d80791658d4ce 70aab459aa2df8f13b828a06976969ba83612f36
+cacc70435c5fd40c4e75ac083c898a5f35105b072d3863bab571cc63b424cdf0 6b7d1f7996feac885365b05637efc5a4e40a0e5f
+cb1bb869adbd4a5bf2861b40afacd0f89906ed7f2d2ff4ceefec444f0166373c 0985c00bcf2bc8403f5d0c4957f3c2c8bc2808b4
+cb252a39ce30cd7b6859222db23553977f7805004501de9bf5ce2b5cb05bee7f e25bc23dbed506f090c7b7794568171d992785d0
+cb282e7c15fd8aeb2265cd621f5a228cb33dc84192980ca426cf9ab2a48cb9f0 5b5b025afb0b4c913b4c338a42934a3863bf3644
+cbdba7ee4f46c2afb421f33275c78d6bef60194f81571e5f9b027416872d1efa 7f5dd37769557fb8ce6eb0f82dbca7394eb94fcc
+cc360fbcb49b10d8e9b6c5b95564133fd0ca52489f158e37422fc9d6c09a10fb 61650ac5909362f425cb2c1056cbae080c594b24
+cc3d272d457c7e26c5d611923841511a1766bcd58e5be66433698627e6fb3f9d 90bbaa9a809c3a768d873a9cc7d52b4f3bf3d1b9
+cc8a9debdfda8ea2c6c7ff5e0c9b5d63cc23455e53a1653d42f58c1ad34c8db2 cedb2a22e6914c3bbbed90bbedf8fd2095bf5a7d
+cc9e079a90406c7642c6177117674d5e542b08b2db88464291eab80ceefd84ea 19970ce5d4f01cf1e0c540623e7ca9cd6e0d9c7e
+cc9f9094162658c03f18809a18b701a42499695da0c257c9e85c33abbf55b43f 1137d90e655031d2c56e925d51af666d0eb0b949
+cca7b385bd43a402abd3527290d1acd5f68bcee3f331bafdc389a2b4d1cee92b 48bef82ec4dd52cd440661294e511e4ec183988e
+ccafd013fd2ea60aab89c7548df3ef5ec8ec2e7bfb8c1bd50236790fce491c3c 8d330c18a62adc01b16cdd3cf19fde92ef0dafba
+ccb5a03da85607c230d111abfa899655d1b00e6529101a40d42f6acb059dff9f d6c93164c249c8000205dd4ec5cbca1b516d487f
+cce687695f024952191b9a882f65d6659e2bf75ff6d70311c35d1a17a7a72be3 028ef0de72da1ad4d1b2ee4a125ede81e3f2ebed
+cd0eac51035ebff474d4995e000cbf329bb0793da89b81dabab277ee3977140a 6acffa8eb2a20e3391b5f64c961d7176315f1c1f
+cd179a62d181b98a8b7e7dbbeae518694d9cb6314a7ed06c67c5c44c64a6bf17 1764376034ce4f4c899f141c121e1be9167d1e83
+cd9398b37060a3710a0ea0125ea12e0fbfff1ad1f5a835ea880ebab058545bee 0428026e2169038ce215dc7a616cf2f85d9833ba
+ce00531bf1b7ed6c57f8dd8a02fd86e7f9e961019962d5ad5093fafb7d851888 d74fb65670afa5bd44b5d9b996a2dc8e409d1d92
+ce354c4274883074fbfc8f1db21fd783e76450619fe4c09ccb8234f308c9ca14 095a3d15445daaad79df8ea9edb4c7b78ba2cfa6
+ce3ac8622b4d6f466e837b0c7cff550d040be27d80b941950b3f5515ad66cfcf 523f154de39d4da383a9e790d513d111a68b7c51
+ce55f9f5ab1d799a9ffaa839539af196d13f35677b3d0761b0fe034e764f8d07 1093c8ff4cb78fcf5f79dbbeedcb6e824bd4e253
+ce601c82543fb1fd705d7c45deceabca8dfcd7b1f156aa4cce6a8ad1efe7d61a efaa56ae9705f9dbc24ff0b27daff60c382efb9a
+ce82899d44732510ed59ffdf65793e8b87aba085bbe791592648c2e54149e31c 66a0d3e080b950bd021b41e88344b320ff6cd788
+cea0392913fe99400ea093a60cc9bfdf9bbf5595c3b431c19e55e2fc6817edf2 59b80d2729ddb3883b6033165586e4a8ba206e1b
+cea8d32b6932211a24dda86e4f78a7ae9ab96f7473e401a644fd050f3cf98722 238e54bcfff45d477a17cc5a0f51f8ee4bc68bad
+cf03eedf2689e76a91a1a230b386e6c9d74167205fe089f44062a96bf7470e89 770de6abbf0bb31fb200f0ea0f86ba2cdafda441
+cf096f21a02aa07d39587714d828e04cd237457a026545866f3fe520ec55646b 3e9e69098af2014a0c3fe9e46cddcb5365dd538b
+cf1d995805102eaa9b331c1f6175a3bc900d02712c0136d5eb4994acd628955d 0856dd76baf3d609acb5ffe3b13753cd5a046482
+cf21a1ff89546dc52c6a7d5fcb6d95bdde4210fca4b6fe3e7e61945c09c37ab8 840fb8b7cb13069c2c5f691beb30c8039b384526
+cf329ab6c0737e02eeb28014efc5968aeb023de5ac3bc00f18b254deb46deb0f d6694db21c2f0a7e111ea1b0d9686cadfbd2c7b8
+cf8449d51d5dd28842df865aa457d144e93f12aa1491e553137780ee78f12cc6 1fec05920387b516347793c23c32667b08208fd9
+cf84e5be57f8d5d51f136d3d137b654c602721c469c1b0a58e7e95647a9cf1c0 d07b0f9a8c89f1d9e74dc4fce6421dec5ef8a659
+cf8e9045fd1d957f8010c75d60039bcab5510d34cd0d74ee63d3e5d35c135b6d 28f9832928c52e08c7db5a0d31fde054f51fc475
+cf9d92824b6450da8a2dc21ffe27b92d49b9f4fe7625aef81eb2ffc9023d2d25 60b2b64f87b2e7956a2a1b5cbc5b08ace8140703
+cfaeef6c51a5ff131946932d81b5d0adf3ea351c12c69d11a6ea24c579063914 93bf51fc98691be66471275974cf020974864199
+cfd1c700cc165ffae0f209da9e257bf5a81073b72f99c65b9229ffb7863ce861 54bdf67864a5d9dc16fc548f2748462b3fb63697
+cfdd565f4cbc315760c287d57714852e1a4894eef9c715332fd556f2e114a9f1 bf2fe2acca17d13356ce802ba9dc8343f710dfb7
+d045e845cde8405d26b094b960aea32e0ac01d5822b9e9bed9a62e036e453a1c 6e19d89f34fc5516f5c01542ab0050bf127fabd1
+d04809ed41a6c0d0b713efd6d7e586a7a05287929a296a4b963938c7c01cc317 c545d2d17706399afcf4482163359b03b485fa7c
+d048ba2ef4fafa502a44cbc1a50cd58359b9bc243b84a08f541a08ca5f621137 a93f42a5b5e9de40fa645a9ff1e276a021c9542b
+d06600e96b3e787b0487b24a3a1ebc255fce0bac6f59d24fcd84814628032faf 56d0888fe6c6144ff623eecb768013569e9458dc
+d0665f4f91544ffe89e66d84e7c24b6d87186fbbe3866bdefc5027d8068f1fa3 61950787a1b26ddd5e6f28ccfb381b91c7cf5236
+d088ed0704b46349ee0f8758cb05f1ca5fae4ef74383034d29ecd87b010f6c10 dd95fb53d92f45a9901b5b5e983760a850d98850
+d09b74a21594a6ea4388732a10b7d1d73eae0f94fe3d5f4855fc80444d885372 0c01d80a09b0908856d5494d3c7ac7e6d22f1126
+d0c992aae9cde855b17ac826234a73255ecb09534cdcfa633d90640f6a4324d9 affd84ed8ec7ce67612fe3c12a80f8164b101f6a
+d0fc7f52dc42358506e7f3f3be72f5271994abb104b9397ab3e19bb42361504d 53fc32d17276939fc79ed05badaef2db09990016
+d19c2efd2154cd79d3b430619d99438464b038da8540cf407f76119204ab0782 5a646197ed0c131e5326b5fcd95ad88a1fa3f2de
+d1a49a45a5f109805fbf478a95715b528644d0e0f10b03dd2dacb4261dbe0094 28cdde6f9faf5a78c048dbc17259f89dd4b26e29
+d1d60af9799c7be57ab4d80713420c9038e360c0164a540247614916996a4af5 3bf9a46136794395a845084011283d07ebc3e958
+d1ddefb4e0093d5a1626d9a1b6757ba2b63a062fbdf2df846f8ed602f2af6166 89b6f61f7b4d3ac809de9427927a8cd515b4bc1e
+d1e8c4b8fbda2e1696c9b9ff4e0aa9ed3b7caee2017c01c06525d08e982e2c3b bc23042895607cd8127e5de36f3d330cf20edb57
+d1f527040a42190f2005017fd900e895a77f9f8849deb117f24450a437c27527 749a91d742b7535e1a746ae6708350705e270748
+d1fae456ffe8eac24049a507d590380f8b9e44593bc33e465fcf94be9cc88cee 89cadd27f3c2f56f5c943e55416dab3ee2d4fb40
+d217cd3b30193bf232ef480c7338f40d2abe46d5c104b485edb6e5997e4606cf 383d52d47751e52c11b097d54e14b436bd41ddd8
+d21854ac4b6ae500514b5e6d7874258bb86400934d411ed47345853edc50bdf4 ab3f234e5f33426172ecead8d964b927bb281850
+d252ef75129500a3f78f16cabc6e0a824d4c1bc3e86b9e0a18bad7d389353cc6 7d685c7221b01af31c9808145ccd5a356dc67f27
+d279a299cec8dc4fcfcf8f6bd4c3ec5769628693a3d459f1dde581754c3b2745 d65577e1580cc68402df5827fd9ab4154edc0fe6
+d297d0f4a84ed8d5cbeac8e262906504d1238d59e9bb0adc45699eaf1da67372 199447c2a54e662c9db370c9a6c89219a32f9fc3
+d29c10f22acb667743faf888201a16d5a52dfdd3168c434bc7b6418d24f0bc87 2d38f94e73d3a6df5d650ddc6835d75498437d22
+d2ab425d6092770366bc3dff5276e3a869221bc7b6d22e99c089d556e7eb8331 d01f7573ac34c2f502bd1cf18cde73480c741151
+d36979c8da3118ed133cc999f0d4bb1aac029f59b2a6975893f41ed2d5c97342 a73891d7c5aab19750b0f1999120759e6ab0eb32
+d37ab9a95d602c7979d3dfae35fca327b313836f11e8511e1f29dd3eebe0e5aa 3d1f0e5811ec9f56650e70f8b27acaba9f34e602
+d3834b1c2da88ed1e98a742f7e8f326cd53ab464cbc726887d9cb019eed6ebc2 1a895dd787a5699c21d7925e9cdffa66f23605c4
+d3c2cc39665ee7d8e3f16069e0e27f35813e27c802d5a98be106dda765effd1d 3f541ffa6803066aa2db492a8229e0b35b5862c3
+d3cc02106ea9b78cc5d1ebf609a4931036ae3ff283ced382252ee713ddd5417b 5690f02e87e4fd31dfa9fd7c9c01aba03603cde8
+d3e3ade60c7ad970e4ee6eeab5fded32ee932e29d6dcb3656d7ed3bc9cfbe3ff bc403249eeeb37ce4cf73dc66711c13c5d19639e
+d3fa679ce39641a538cc30576fd8980999a6edbebfb8a4ad187dd406e2dc4156 bc874e116fb4e785081344c2ae39aed3bd072288
+d3faee8d75868598e59a88279f0509327844800b54e51fa96283d3c8e3e7578f 6533aadc21270052d8d05835b1e30d8b13a2f164
+d451619d5bee5af825ded81f99b3862ae60fe8b182ff10b72db8d005c173dd6d a7335c5170f4a963c8d67782fa35222d03d8a16b
+d455043371b8730cc74f280b64e25183233fdd330acf54ff76526861d513bda5 f92446126451b27205551041c92f5f7f84bf6d99
+d4698b848903dbdef9f5ffcc0496b1c82bcff777107819638591bc5c93fa2942 dba81bc591c332550be284f9414350861b6b066a
+d46a8a40c5bba0b053951a958ab021f19eedf8fa4e5a75fe1927d33ca498c973 a920633902d32a6fcba87c729b14e08341bfa097
+d47674eaf38c4547b4a490e5fcfa7d68ca91e8aeed2386942daf07bd1f96ca97 f29249340c11b069e46b70fc145c1d7fbe213b94
+d4923d7b828b897cc6628e6e026fc956a347002f62e1f6f52c022c2c9c664954 418382dff1ffb8bdfba833f4d8bbcde58b1e7f47
+d4fab9558c0aae0a9010ac44434c891492e63358a30960b40e48b0d0e5f70694 9a9e5abd7e7d2ce46fd6dff590360da875b81247
+d4fb1e12bb875235a16a5037ebaa06cca35204c1fa89c9abff02282beb74820b 9655ff12d31716c9049912eb6378a305346652c4
+d4ff08d0ea45b6d8eb47fafd430e2496b132b2a0617b6cd77809eab2612f9823 eab36c05f0ca151dc5732210223d0abc9e9ab321
+d509bbc6e4420c5d0b833045f466b02c02922bc4bee55f91d4b6751eb1a87074 061605020c8f212d696a2a3d28e20c7091c09443
+d509fa76ff5944e25f48c2476736b6239a53f0463cba6ebc488464d087951951 100746511cc45c9f1ad6721c4ef5be49222fee4d
+d5473cb18ca962ea72ddc29df047c3fc1aba13e5cef68e600f5563135730b357 7e02841efbb3d464e61bd4dee8d6031afccc6a70
+d58aa1f4d8c35426d90279e0a0b81669fbe25bd200f969fe75440f8022f98ec7 b81dd80e8cb77046b6196a1dec3f8f356307fcfb
+d5a9b3e16e2756a30bc62fa92552f73d9d3e02a344f047e3b24ae1392ae90c69 59c513fca0ea668f5c815cfff903279a4e859731
+d5abb2bf61fa4675ce068d8842ac6bcbe7c097d035ed5e79ab83694b92e3b8ae d5ef6a1381ec4cbb9b78979d2ea7c6459a48efa4
+d5c255a2cc319613a97ec8b66dd44e5a8c4d8328a3559840627511c05625b4bb 9514445e534ffb33b50c8752a7c9267e3db3a4bb
+d5d0a313ddd0d6d451ce2178da7d7ba19e1f968f3603b80193699ad5a8b51744 068817d917a1c86a5fc33e7925fbee9e2128d648
+d5dee571df1d0b72a7e820d9a08d770561776d044894b626c3ea556cb804b64b ed473e2431c5c013f214e0ee975e3535b6196679
+d6569dea7a2dbf053eb60492f35432622090918137696fd26f01fc69b4dcd67d b32af570d6088f5ec58b08d0f7ad3a74265667f4
+d6a3a68c9c9ac80171333351683d26ca70792679a830155047ca4f85207be32a d4b9769056bc3f7d22860c87fffbe9d4a9065c03
+d6d2e87e6de8690efd26b8c5b58de28dcb3c9bd2b7659eccf34468d71a7a4478 bf55f407d6d9418e51f42ea7a3a6aadf17388349
+d70b0c834e8a1efde1158c15681472b3c7d12da4d1167ed62581e0ca64d184e7 a9e9df3b695e91894be66c30366f0e2bcc73ff96
+d714f59c54cb4bc5a57b9c68b44bf21e2ce63a45f1030baf115ab3c6e7af9709 ef810961d54761c4954df5566da929cba3459561
+d73158b62753ea4eed99124b3dcdc4c873a4ac449204a932b46f46a4da4c4778 8e5a43b2781f75268ca9cbb71993b377cd1d6106
+d79e913b4137117b7f8fc2a8d184373f657d6f71bbeaca0fe83b7757ce486108 def425bf8568b9c1e20879bf5be6f9c52b7361c4
+d7a0fa6d2cc7b83596b51ba9029ee05c553c79eaf3f22298a407c7cbfde61d07 f8588696bcb81e3cf38fc47f2536cf8e25331c7a
+d81b2839dc735c3ea774b024636a3f0cb13fe75373e9d1e81365fb1013090e81 89ec4c021467168e5507cf91b712b6873ebe8ade
+d88b60d2641df3656381dc8e201abb820a414de03eb63c065b06a2ab37d3f5ca 521d87c1ec3aef9824daf6d96cc0ae3710766d91
+d8bbcaceb334b4a84eae3db74b9201390a13f9cdc2705f5fd68d1c124baf745b 126a72cbe290b3a6a8caf8732e886d3d21c6bed1
+d91051fa024310949c3ea677750d8dded9dd9091ff410826644521c852b15ef6 259ad1a27fc30dc0167d8dd506cb800bd128abc5
+d91df71dc184de6b93ac0d3fced0cdccbdb3f8bedfa433f57aeec533ec657861 65ee31bd49393a45dac5fd1593aba0d332c829f6
+d963a010dcf03c6b21fca8bb99725da1d2443a9dc0a610c5ecbd25f46858a7a2 2bf7e29956df690eb8462a49aa1f8ad9a85047bc
+d970d1a6296d149bfb8283b8b4a9a6f7e9ee320c5d46a5ef216e10400df2d281 90d4d2f0fc362beabbbf76b4ffda0828229c198d
+d9750ce4daa558910affe057a0a21ff130abafffa5cd3b9bd8857ccff7875543 b370dc5521c2dea03a3d2d1d1209b2691de80323
+d990f65cc071cb0668259dd2f7c1acff8710fe2196fc56e4f43496208ec0edc2 eb7100b307f2c35e7a2d1b05125cb58702da2897
+d9c20b21c4037980a27b4cf716771f1877ccebce89f08b9d07f5b35f4bfff8e4 42281e007e15713713b2ff4324d60360332e194e
+da38f65b32fb03ce332e0c4238edae0a733e4cd793d849522bb4e0bdd0af608c 8ef9060a954118a698fc10e20acdc430566a100f
+db5ffb15114676bd1246296817d68013ead717c9efd093ffcbbccc805e2b50dd ccb8c44ae6da1b71f65c26324aa39ca35206271b
+dbc10ea8c211fef2c138839705ebcf10b6d1261f9af7fb1da206fb8b19050222 bbbf64b0d5c1b962db9d58cadeff78c11bd5c515
+dbd89b614245ebb6f859f64e47f5caccdc93ca05609e83ca5ca2ba478f2d6f31 436cda88b3b747c66549eb0fc0c89d1102f967c1
+dbf3df54acf12a89a09b6f1e7fa9aa29685f419e9a595ec6389181ecd9880bf0 c470e0e0a392d57a681df5d337fd64fbbd9928f2
+dc1d3d6e12bd685f89221ca5051ed749c4ccd59083e2c26d815e6326b8bf8cd7 98822557cf753bf7219fb362c32c9330d35e5f60
+dc8a2c27640a60ce6b6c5424325abfd5d6ca1ee7fcdbf49b7772c107fc0a6cc5 0cf02ff92d5c08516ff2253f59daad1c4c582d8f
+dd2e1251481bdf69c7e256ee65ce366ecd4ea43c6aaa61b30bc2d4be17b3fe57 c9280f9f1b0482a337c3250f951431030f9fc91b
+dd60b4f51b88aef523ee1cbd6e71b8166b9427c3656cd2d1524cacd9e768e4fb c15648cbd059b92c177586ab1701a167222c7681
+dd6281df774f9e6af8b68a79fbd7c3bb5eab20c85c3385980b8c98141d080c72 36fd65cf283151b18e93dcb5e625fefec79762d9
+dd81a2885ff56d3d275ea52e79809d2c2665fc64949bf3add600d0b19d169f59 2733c6ad2584625f9849e9824927829190fadad1
+dd9b1d5ca653752cef167e034324198971ab6f2f38f3db9db571cb2985759f00 8ed341c55ed4d6f4cdc8bf4f0ca18a08c93f6962
+ddc8a477387d01547a809399b37acb2456faf0b527b5795b3caa5c2d1cd3ecfe 9b3577eda0e6bb13d18a304763b91665575adfd3
+ddcf2e7ae93da5f152a605d82c821b530f88d8f09e5600a09e44e7c89463b3cb 2061cfee59ecd5074bfcc0eac7dc5b3637d290f5
+de11650c5186daa434fa253c05372fc92c34c69af7afdc4137aa62adffe216d5 f9395075e3c7bef6cee2ca5140610896f4855c72
+de82cc04db42cf7ec85c7d6847517ab322ffd81674669fff30b2092c4d10d319 71628d165d4273c15f1ad2320ec382c8f29f566a
+deae56ba25b1e1fd5c971fc027ba413e4efebc393c1edab6aa887881d111ce46 21648b45356a49c76d58acf3590bd98ad12b3d58
+deb5d5c69e8ec5f11412724decfcb6913ae501fcecd381350521e3dd30a84cb3 098ac57a13165290bad223e4949fc6f13960b604
+decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f a65fedf39aefe402d3bb6e24df4d4f5fe4547750
+def8ae3f46a6251e846b25383d6989ff7931be0906da0e6049d25669df260c7e 53495ca95b5d1fb2db527d35e6a8825bea12b51f
+df21e6bbf78ab81e603c9a0eb8a3f2c2edc2659101553405ca57f48d13d49773 5189d8ea8bb07be6dbcc0aaae2294f2b1840d335
+df70ff5ce6431e87f4ecb612cf69aee57b2d8c514e51da1728af6e140ad2ef9d 4870103f0d85d833fafcac9f3eea8b17f9151125
+df7592900e9c0c8191e659cdb9b7a02f0eafc08333ca122bb65f90c466993bbf bbb0d9beb03f3f2d2d81c2199525e7b459f1216a
+df773ded7bc7739ffa988358979e1ecf8cba729f6eb8234fe48bd3804d909194 5673434fff718d93ac738a03403442f16abeecba
+df7c2829b803b4d008d50676565314253dc23a2f8b80a9d2abec2f8fc027e1f1 6a133ac4d1120d3bbf563d61338bbcdfad4b6ebe
+df7ca8caf338f34197d9bc1e346c66690321f27dedcef793f2eb8f99976cfd57 9eb7976448bf684dfb41993ef6c76098978ff933
+e01ed58736f8d22949babb858e5b7c9e9e8c6da45f125c92ff74e4ebc30f678f 6c48d15ce6a8b8cc415eacb9192c5a7b93c3dc94
+e0243b96d2d6668ddccbf4e1744c365b8bb828f0edccc3adeed9cee2fcc0718b 11bb049bdddf1d203dfe8fd6031c4dba328a7525
+e08ee0ca30e32a912f051a443280fd3bbcf593f5e23b5c5a60cd40dfb8b31c38 e3805dff27023ffc98b1e8bb5f8e71e613f134f8
+e08ff41efc23feee5e8dadf3d3901ddbd0e80d4f39c1a893a9b1a4ca6e457c2b 24f3b8d8e8c8f3a1b495bac67d0c493d425ab6e3
+e0920162225ac182b02b72a4999007ce63491fcc0d5fb5b130c8ab6ace46d5e9 36b7cdb6a1a2e685c7141406808366d4c4b9f98e
+e0c95bbc34c21872d8a284055d1861b542037744cfcec048c261b5dcaf0d8c4b 15f71b9429442ed616b66b986814e94f983d4050
+e0d289871eae81a3c89d3760d65ec61601a007c010a129ee02f3762961389ac5 e96aa1e25e4ad7dab5d397227ba8f14d302bb393
+e0e636769168575a7c6a7f262ab2e3f24d15aef148224a75949b19bec3204555 09b79f8c2ea7aac83efa086f2ee2377b11c4986e
+e13a3a7900774366f6927fbd5006c86b97e0c500873120a4ee73fea158b0a0a7 1ae36609fa336f5d3373eedc18f00c4f05194931
+e153a6b70868bf54a33528407abb749a5ec4a278e36ad587585a807439e75792 001d938dbe69b6251f4a03cf374235c72fd0a0d2
+e15e12b19203d0869a45ad94af16bbad57b91dcfd8d70a23291da3eb39d1db70 87a63e99a903a009e04760bc4fdc195ea25922ee
+e1853ed6756da2e3c8c3902b9d373d06158e0e42daf8cd1b364059bcf705825c b42f3f731f6709055f7baf5bce3f39881d4c62ac
+e1e91e53ba1b4c377a3a86f493517ed6af162dfcd79191cf0c6ed6cd1846eb64 9c3d59f42c90513a69cb72d3680656ecd6fcc309
+e1f458e5f0d9f51709fecc937aa77830d32d5a06c35fb5a71f37beeaeb5e14e5 9e300586fb4dbcd7a7e7592e8bd36bae946bcf61
+e2040ad7e0b914aab16f77a98f629a8df48c3d5c6346c591a5bf0b6bb19cc6b1 d4627ae2e5d2510b641c806f023335206bb4b575
+e207a266e228414023223530eb77c64b10f2f5124f3354deb45aff04c1db98b9 c059510ad1b45ab58390e042d7dee1ac46703854
+e207de850c812ada0f0196143822e6f60237a7de1f189bad75ad55337706ba73 eb1228f46b3c2adf90a4a4f4093dc12434c71bcf
+e2199e776f22c6c9a6f60a74711cf168914dddabef1082bb18027baa9d224c0d c6dbfe02ff6359247b49b0bfbd55dbe659a90def
+e2b8d0a4730f8678f5c0bf72165f12a58aea0b5bba85405da8e17aa3e4c672ab d7ae061cab626313265730ce7c33203ea371b497
+e305332f5c4735afd3f441fb9fe7c270df8e8cd77ee73e6f9c00c5cb2cd74c5f 66ae897b9fa0efb9ca0097531be2be366c82d125
+e32cf5f9c0e63f632a2e85c2f57b4d74e352d382fd9e449c5660d5f42696b5b9 88db4b005a4f3883f127d8ff0fa39ea7344d5b35
+e3c7bbc5d5fc396e20897c2124898b3601c494e6051e1a5162938d664e872477 5e95e5342bb02f75240eb1b5978246e2f10c2810
+e43f7ff09259618dd58412fa34610db1c46f6b1f11d7e61c74d8ee91f15afee1 c62b80f26a9b4253aab953c3d860d18ee042bd5c
+e486f397c337cb2ab4ed93f7e4bdfe0e96eb2f19b4666b4189d60cc961db4d56 40aa5f7bd409d7467369818208e9eeacae39523f
+e488ea88853da32feb570d822cd2458bb7b525f7c946d5956967ea6c2ebde4d5 6a10662131e014c3753a6490644428760fbf1c9a
+e4891afff0042d2e3ec44f05b0c973d9bac7bff295b053b614f62377aebb4014 ab9c9b9ee553fd11f9f6d5534e09337c769904d8
+e4cb9e225a817b0f5695fc52c0ce05860a23b345e8338987c7038fd68b46747f 8dfd652805e877abaca7383ad28d8eaa5b9a7e04
+e50daa85ddf947a33ee311516011a786e12a1e5cd1b9b60fe34fcff79af97c6e 0703e6ccf92d6f62c552c7be1af34bfc45fafe34
+e51625cf3375b401cb07f27c258fbe0deed231836136c72670c9beb02f022b52 a45b803b8bc607c1d8fe2ad0f559359574231548
+e51d1f186aaa3e81f6545e45e30138f520f9cddac17f121e3a6dfff5f4286185 d9bb4c3ba46cf7de51761352c7b04a2d49633bcd
+e5714ecc9e216d2d23e0c9265aeb7f94917f5445a06e988f40677020a557c1b4 ff7c7576aad8b6d87100eb1a62c0bfcdd6e24e01
+e5a8af67e6a6ef9eab9989c16c19bfeb1503a4d9b3c2100e6a98d07155b0c16f 789527fb457255cc90b2b79dab55b67023c7b8d2
+e5dd60bc585584073807044262f8c611f3c23cbba01562c81ceeb7a046226b28 2e3e1ba4b6f21a028ff2d8f17febfe22fe93afe3
+e5ea38108d603a6ed8dbb0b8455abe6d971f2d60920207e67ba646d267ece305 6f4e2c42d9138bfbf3e0f908f1308828cc6f2178
+e60d23fb634bb7ba008578d69619ecb715555c539e5e1a09995c9fc1a2fc8f89 4324de04e80f5cee8a6d8d72ae7eff4a7d7e632e
+e625f75b91fa95253eeb059ff3a4329509939b2af57d406ca6eadbd385441136 6d8e93b8384392ca44be9693c850dfa04b9469af
+e64b4ef0c042862ed0071ccc2b2dc8766392d69a1c69ddf9f6e04f61b79bf424 f8ed08a88d6f982e040feeeb2240fec0b5e4b151
+e64ee640eb592c7f2d9592b700c416b96e395d114c13752555ac220d850bc739 2b1ad31b77c1c1e9de3af7863e3916f2fcbdd1ee
+e6ffc310d72a72943da3796f9716d5bf6ec72c477eba5d55cd44b542ed50bb8c 2cdc4544233b503a5aff7dd1baa4ba8743fef7ab
+e702c3dc4fc84a70aa440a441e905982e17e8c5bf0bdfb58ccf6862ddfe02d2f 963dd0fe0fdb9581df0159efeff615de0771709f
+e71ffd37f2119fcce2a02248cfc164238040138822152ce6c8959b35486036ef 06d2a40e85256ae9426dcf3847b0384c547c2252
+e7481fdba48ca2efe4928cbd4a631e313450ec901be85092bfc2428544ea7dfb 1fd98a61779c499739cbc3f34f351eb40d3cb4ee
+e7675558e5b9b4a8f0eb006d18979a6019cc8493836ad5b7f06ca75acce29892 02f498e4bb85187f9fbc0b0e2eee06d5c9c27a0d
+e77505da06f70c52541f5c07441cf6205da770f7a4fee730757d12962172b93b ed10f7124a75cec05081b751801b6a0a0e42e56c
+e77be3b1ba3bd6211b30953bbcb31b52e8c0ac0c68f89ed62bc708caa489cd2d f89099c671ed8df50e040b7a62989b451366d084
+e84639624a2e2f2f89bd215bf640e61374cc0f3385b646d0d59a85bd7baafd62 c770eeb29c9c95d27b9df2d30b6f47d62e78d07b
+e85c03d559c7b05c4b6ed5b796dd42a5d018e9f3cfcfdd4645a110e3695f32d6 5071d53225a4dad9e07bd0c9f04a45d0948bad06
+e86d64b9d11cfd3d2a06196fcb3785d0b716500d06b1daba9f582a0368c78b27 0617d6a892682a5d4b4081a759f84ad57b28c577
+e8965fae4238a25fba359135d62087dd0e7fccbae626d5004601b6e9c50461f8 0e7fa1fe6837a46fad03e7fa9a3df0815364982d
+e8a865ff7fc36bb0f8cc2460fd7b2977189b5b6144c69e14500d5478cbc9ad6d a79612435aa5b1996ff8ab70e789c8864e55ad00
+e8bbf40ee280bc43b33c04df2250903b75e92f2497e91759cf1cad753c23be6c fc58168adf502d0c0ef614c3111a7038fc8c09c8
+e8dcc2ca3e4e882fb1a824c72543408d014ceaaa04f0657432baab8ee69e1195 8d762aafd2108c0f4fee8133dd2213e3658c66af
+e8ef4c26d3c9ea277e490ff6e7d270093e14dbd1c01a3f9d78dc972bac88d87c 46fccbd742b48fecb83be48e16885b86bb299027
+e8fd9ef5c4543a16e74c80cd9bbee3e2b387072a14c7bc653e8f96864b7a4bfe d1adf1be64991ef9c94d836d8075f232b73b640f
+e91c29c398d4ba343251bf45e859ee4820c2e5cabef1148565809bfeaf000f5f 2e997a35970424b1d6a6de26f5182898c6bcb1f2
+e92d166df3d560dde2087eb6bb14e2951618a37fced2fba2cce748ea2e159382 a651bba3601c34d3ea21c2cd5b898fad29854433
+e9570c71d360e851fd1945be15a2c4875ebeeba9d9dca7848b72c0e35a562bb0 6457fa0a84176ab4efb565b64170dff4175d68b9
+e986254345a65c0b48ecb229c1f47d9c7b8c03b1dd8cf6cf5bb6a877efac7086 97ba57f428e6d74fdbbe4144b8c9bb46b9ba5315
+e9b7c2531cbe327ebf55552276f330835034014be5e5d603b9f1e1ee5dc28465 9285c652cadce66ec115e1b079648e64a9bf0c67
+e9b83772d89c5948ee301059939d6c37f803e5f6055894a2d169715421d3f0ab 5fc06ac83625f97d0b03000c4c224aa86c3a350f
+e9ba6f1e38d419a6e3e66a8859ab291f70cc042192866fe0f0c76229f7acf18d 498bc0906810bd43c6fbc73385fecb7f2d04be3a
+ea2ec997ce482c0d1d2b83f7e5bb6c3ec634c2018b8dd4e4a67cb3a108bf1f57 3a2aabdce127a367aa27a4c27025199230e26c15
+ea462d69e697a2d9c70e2445739290c6a4a2d649561c0544f5bb027f9a90248b 58b0cbea74c160c61ec70768568e150c4d31f633
+ea926306b1bab6d3f25f45609907eb6dff91a1460b25e63bf4a0494c70e7a269 9fcf811e00fa469688943a9152c16d4ee90fb9a9
+eacf702e5d2a14313d65279dfe65a9d1be0188107a3cbe5573b268cebe483876 1eea99f3a439dfa65fc9d86f7fb4bc2338fd7dc0
+eb128916f9450004d9468de8c8458ded8db58ba01fbf36201aa16f7255269e30 bc4153d7a1038ee96648f97f75aff45c22501412
+eb38ab81898b34d0060be1e38fffc4e04545dc24ab8aa517550d801e1d98865b 798d19706dbf082766df35d66087d40b1a685a7d
+eb61f971c9c8dceec3dfe54fbedbaa9a07e2ebbb0cd44fc38617431fa213ea44 aeef9c0679084b1a3048b81831d4ef3d9d7eb10f
+ebb24827556f1cfccf502c909626674796e7e1e1cfd08ca430f3490e9f1622f2 e6fe30dc427d63dde4107715ec219c92a017c864
+ebd4e93650254bfaeae56dbe5ba868427274f867d5d859e406f5981f3ce454f9 4df1521318774135d66161a37fd1a06423d0dc7d
+ebead5965196dfaeab52b1a5d92b78e54493fdaa78f72268d4cc69b61d5feee1 1f67fc4386b2d171e0d21be1c447e12660561f9b
+ec05ea37ad15fbda3b72d3de3cddfd82b411da97a4dd5dea9bd4c5be71f2fdbf 89528ef257ac5bd32f4980146df41872732ba2ac
+ec0afa8e0f8d3f924ae29e9ce6e314f06a3327a596763e7c0afea13c6deff8d3 2066aa3e804f6344b131387e17e64dd20df24cf8
+ec0e609764571fd852ef82c0f2272fd4940fc593dfa28a1a2aa4118f764babd0 f5121b1e6ac7f7e40c08e5c072c05a936d6237c6
+ec12fa9a4f7c288ea3ca253b0cde3250f01f2420de8da0e882e15f7c1e875d3d 2c09181f38234634e478130191323c7f74958c33
+ec1866b39026366e69ab8e167c15312c27f5eda4c0afdf9367ac3d76f56bf8e7 4e7e9f60b7e2049b7f5697daf133161a18ef688f
+ec21b1d718a46155b66932c5ea66255e651ff00131904f5c15b13e278ed394ab ee3f1ebf8ca0d0bb7040a888fcefc14873cc1484
+ec9d0c0503395aba37c14bad6f94e30773413e51f4c4192f637be5079922e9b8 37334566347064df74299d0479ba44ee7cd844e4
+ecbf01d97e2c2d6c04e428632edbfe9b67dbbc2f22967c35610d634f8534ece2 838bceb4b87e6a65042f955c90fdc809b34ba3ba
+ecc250a19bce5bb071ba73708879b69c8d93cc655ba47964873087260da82532 872ad88d663eb6eac5365bd64f0d2ed2820b2017
+ed1002021629a4a96d2d53168633fd4bec45fc52a4dc3514d09becfb1f4846d1 52121c9b19c8a7625126766102b2a8145c1c0632
+ed10f9520d5f0d6bbd9c467ed83a239df0cba94ae9031602e02f6ad6256c459e 4fc3874b118752e40de556b1c3e7b4a9f1737d00
+ed41f28b6dec2c88eb9b4825a1ec79f770ddd524ff8e8c8dc117697cd8f3f039 1f3ae5e22cbbd99f678fddf3eb1a86726cbca831
+ed994d1acd346cb201da40004ad4f59cd42270537a086298c89f81873ed078d5 0fa9bc03e0832bd0e4f9b9a4735a3301bb37a8d7
+edf6e0dbfaf8a7ad89b05b5768981eeedd7a2bda4b1d0fae07aa2a9d49bb39df 32679a9544d83e5403202c4d5efb61ad02492847
+ee16964dd77360202417cb8e958673a3be107fcb1551a7b71f6985d2102c472e 7c297e8eb9ca1fedca286fb5877353c7356c380d
+ee4981c3f6d4d973a1f7d99998848fe4b778ed2762a31cae46f9090bf47aa0e1 a7c182c59414cece10c819989bce3f1247f4eacc
+eed74bc11a8f6e68a63c093bcb7b793c3a77b18239d9340a75dd690b4e790dc1 ddc1e625381d4fcb00107f5640030d0dbedcfd0e
+eee9bb8a199012d24a2f94bb633930f5bbdd4b59d7aeddd430d7d5b0c0abde4a 960ca1d7799e02b72ca828373c3fff04e2cf0334
+ef08c989d6195b9dbc09f3053b94f642d84925d5d5f3a2dfd28be482d77f1d97 3aa9ebb63e642246946ca397c8498df46fe6db0a
+ef461668dec669ddde121af3bfcc11f173be86c4fee529001cc95e22ead79a2b 8e9e1e4f95e44ecfc1230a91036257ccbefe605b
+ef5dd9207c9041337f20818723f4baa003a8e0e55646ca726a62b4e7f831583c e3c672819ce10dd2207fd8df6a52eade74f00996
+ef9f17d60e921db4ae02169736de6f4da6b6ea76cf3419ca30494fbe2a40f0ac 77bad856f6306f1c1243ce0f4e363477205d3cbb
+efc4b8fd4b0b2586fce256b107ee2a052d11d26f99d85ce0478c3d49d1b2186b 8edc2805f1f11b63e44bf81f4557f8b473612b69
+f00a3d864ac7cc74bfc0d238ba10b171fe7b6f5f2dbe7aa08ea07f99865bafd2 5bcc94ea49a378e97626436c5b28be17b54eeac6
+f02b8fcaa64271d82d9021dfea900a09fd74997a61c21642da82fee6e6b20f21 f3687dc858ea475e3691834bf4ec00a2fe8c3b27
+f039acbef00c0dffe624ad124760b27a085358a81f9ddfc071a2eb2e44035097 5841342a0a9453dca640dc53d2ea18ed70409c86
+f043d8f788777ec076d2e6072c36ecebc8772847e7effbd7fb04256181ea1e65 f09aecf46ef68bfb4da47e593082805ebc644d5a
+f08cad6328b43e133362254e1468ead13b1a5f39a22d45fd956a90a0a860b401 32db4c9153f750f183f5f6a11e4a1e5f093a739a
+f09899f54bb2a1feee2a205c24b5c2e9ffad5ead4fe15b2f21285ae0c973ec60 18f4b6b56c084ab586170cc47e5f8d55ffe1add5
+f0a7a226654089b999b5c92b34d304a9a7c7c874a6484b2437909a6dd225e382 a4f863af4d0055126435eb01278940aaf727d2b3
+f0b7aba704d8816667f0465022228bbd93ec18502f1ba75d0e4105c49ddf1a99 0b30644f7cc496eaceba0130713c7be51021bd72
+f1982fbf726534f88311eb2eac67ebb828c10bbedbe596ddb956413b5e9e1f17 4033d6b8cb15107abc2288b64f405d595f4d68f1
+f1b2af9976ce14a31b39e96b27b97a72606393c5f87c500b933795959fbc5122 9bdb75947178f72df30314d66bc9bcc90df643d7
+f1d8da2c2039b56e385ff2f76f1f2585b5d708ef1b8b5527f253a335ada68d83 88ccfe32b8a8455a7d6e709666494c33c7387050
+f28ac33bebcfce2579b1e8e91bd6749c7b06a7e5825bd6fc2375e3e724f20612 5235041760e8186f96bd757bfa378ce5adc90b4b
+f2a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736 944c0f6e4dfa41595e6eb3ceecdb14f50fe18162
+f2b78ab9d3df69f23d3e9a9e03b63a466a06c760c4eead34f90e10187f86acc2 487addc854add2a93bfb25f29cd7a5c2ce3bc40f
+f2c8da1a7c2eb49ff25c47441f0b3f387faeddde1b37d0ad2f3f6a63f5327978 bd758010071961f28336333bc41e9c64c9a64866
+f2ccc71ac9c89dd36f44b911594484abb969c62f640f9cbf60c853896b6d1d13 93a527ab2bf2555e28fbdfdf31960326c4055d2f
+f2d5807c5279cfdfcf302fe93d1ac90025a03a57132ea1d852f80f0ba0a1158e 898dabc73fa99e31f117c366498046cb33c8a24b
+f2f432405c8298ed098d704a9590d7f41451b16c834520580fc1270fae0f8b17 bce499af70d434e89c9a110374f1b7c6dd3621df
+f31459efb9367c5a19c9dd24c75107423d5773066922ea5e55eaeb6490979562 75057dd4114e74cca1d750d0aee1647c903cb60a
+f32ab793f10b04cea82cb3bda32a0b9595b5f69ca89608f88d3572914de6f720 feb1bd8ab25180e447be9862c852a45143e98375
+f34224f82528a95407c2202f0340d08ce68435c02c440932d4a098ace60907cb 022d8b4e47f1ee40270ad3beafaeb5c025ea1b76
+f355aa3c4819d7e2e235e2638c11eefff5ea1872c2304889bee6e843da7a382b 3d40a87326d25342300fb50d78744671440c5408
+f362826c827aa3bcbeb3ff8b71bba08d7440b89ab53fe95d61b8922d01f46e28 e0743ad4031231e71700abdc6fdbe94f189d20e5
+f3c7b2b4491f7edb2150c74d063ef6f49c81ed818d8b447215f3b6c3f6091cbd ea59a210dbab63d18bbb616fc27087d68dc59777
+f4076ec94766226a8ee1d52c84ffe070d8d58cfa111e845bfc7e3e99e314f19f 43288a0733c1dc0a506a1e45087bb7f9bd87d047
+f430e24c70cbfe819b5f201e3ebc34a3dad712ceb08ee4f13fd8bd07a6fa6d44 ea28d017cc7a2b9a329dec6bd91236f5424d72b6
+f47bbbbe82207974769ce35769b74de8abc56d4770efca4a2cb755d55c150390 6a840ebc2b332a23243a5590965eefaeeb460bcb
+f47e3b2db30186f550ec8c5c0182409514e10cdd3e66a936e0ce57349df8eddf bf05bcbabc31dc7627bccd33535c5bcc6be2498e
+f47ffce6447e860dd48eefbbd7aacc7aca083eefbf2df94f9b96113c58c08206 ed8ab62f1be3062011dbb3c6033952e4657abd94
+f54e47e3fb25c0fdedf1a58eae2aafa499f95c821b702623778455cbe8cba53b 2e85427e16f2823de8324bb47d07f6f78a38746c
+f5656eab7d016da5fbe176ba06fd46f809e8f3163fc143887055f4dadfd829a2 86726c5a8c1200f732412fd3ec7601f3d2100801
+f5a821e9f70620ad45e86113ee0ee42211caedeb0db3ff3088500dda553c046f 00a27258320f37a893d4c8e52a991997706c730c
+f5be968ddbfc5aba6d676304dd513eaf8b5fb877235c1d5b8c76e8a93f24bd0c 5fcb851f4bbff2968e56b9792ce8310a2eb50002
+f5bf0317f8e2f3280613bd0f1574b5d9f2f0760b0335091ed3fabcab327af221 1fd9c75f95b22df3f52a0891dd36d3c18e9fc0d2
+f5c6feab3b2b51d205f6bc4b3d8fa2a8307ea5f61031b711204778104bfdcb4a 61bfa90a8f4300c31966e3309cd2461507b02264
+f5ef3ebcfc8f054e80fd190746057638615b9479d3917b594e00838ae2b69890 7b7d17f9b6bcb21de339efa2e1ffdef5f15cc5d3
+f5f86c720ab7d820ae97ab3ee2efb1009109a3a54dafb4e986b4f810b7016d21 8c7833b361ef6a4c68d72865bbf312fc3d806ce1
+f63c392c2e03143c9c3b37fdeea26691076a424f336b45bbafe3dcb4537ad29f 129156d84004e59c894c7e273ba9b5824bb9ea0f
+f63c4d6eb84809e54f87f531118170c28758305df7888104365e74742cdac390 2ee716e9a389bfbb76261371c1e65348ed1b9a38
+f64b9eec0c8df9ad7a5a2a50b9bbdb9f8da4b55c9f3176cde323aff07de313c8 e2b5e2857b75278155944594c2beee36079b6b1d
+f64f370eb4726c47b6c90efe54938119ebc024e49fe07e7eec0207b42827b98b e7ed799defef0499d47c8d8330955ab387ad8852
+f65ec16ed7554295cfdd98a0a5b67c1995c0b7b2a6778c8b36ea2e5b94cc5902 75f541f109d783d298a774cb9f6e1e7891965949
+f6633a3ba290c6e9f4b37ee0d3532e47cdf16cb2a43a7049ff3a3210044e3121 29c52c3a0cda152f11dd39d6217b46f6f246bb40
+f66691f32eb9b23a029b43251bfa994d16481fe97903057e121b76e4e78f6ff4 c01c3fae7251098d7af1b459bcd0786e81d4616d
+f66c29d989b01cf3feb6fb2bb7329f8acab284376c3b860f3d7e0fff904c4298 532a2f46e4a8ffc15bb5a13d9a02364197115d63
+f69bc64f14b90aa437c84a4b4d0e4ace3bb8c911a16ea8d77a6ed781c1956a02 9396ec28d49129250ff79463cf588e1a8512984f
+f69f3d4f7b8713dbb3e3adf1f64d6b8f88eadb77bf4f358fc28c32c756b5df9f d42c7e287c39463759ee11e4fe7bc9d722e487f6
+f705853cd543d8f69b6e57c3bd580ae4cd8b6ebcac63460fb322e0133cd0dfa9 406ee23fcb2900af9eaaff243f20ec9e07494920
+f718026e4badd0b9b38989a5538a627858e3319cd8b155925f00daff5768c138 7f73f247cd1be8def4d07063ddb72bbb2e6ade45
+f76f10b126720bb7d7577603fb3b54f0cf0ca722fdbc29c924379f4ccff97c14 09197a342b635784bfbaddcf6b8afc01d142f8f1
+f7c7c0e0d32eefee600c90a65bfca1564c643d30451276d3a4085930586db81d 8a086f872a3bb2cf8e437f0e374ea18e5cbaa899
+f7cbb8c92f729f08203f027870bcf2a1a71c94199408c3bc6bb7f06859212989 187613a431e241f9aff9884bfee28c3ca82ae1eb
+f7e772589bf8ab335ea3a7bb2c2d8158755e0b2bc08b34d80eaf50d070fe6607 083f644dce33ceb8318ab7f1fcae008d6bae2b7d
+f7ee8274a6a3963b96ba1b041f25ff1136cbb2ff07ffe04566f321f02f4099dc 2b8988ea7a90b45116bc4d4101fbdc678c48b410
+f7f06b26124f95366b331eea19be3182ff9dc0ea208ea1de2235e22249683a1e fafedcb78f9bf54e54f0a819ad1461c2ae3b4463
+f82729f2cc042ac51302f20404d9e218148448a3ed6d6c7936b063aa4532eb80 4d5b2a922151ea86d986e012971896622f5d3f77
+f88e32bdeb55ef0690249212cf64c42a4255101a95b74e3dbf78e6ba34f67def ac7c4c450b9a90d66957511688dcddcd011281fd
+f8b2eda314c8c39ac692033eade0bc766ca39c8e8c0a40fc0a25c67fcda825da 3a33c7b37f55d5bd30ff944625499e64919b9a9d
+f8b45f792840019909fd35f9dcb98082b3bc39373268d467e1b00f1da5ac71e5 cff54d138945ef4de384e9d2759291d0c13ea90a
+f8e91f003a1b5b5c5c842f5a27be8f5054c71bbd97ec516d0d8293f4eff40372 160148d67a3f12f0f393e23bc4b7e4e58c0eb921
+f92a00b48199229aedc6c30dc4157eb40b364efc1d843f91b67fe7113681ecb3 5130f2343aa863e7f17ddc0c65f71a993b061a07
+f979948512aa2fcdcad17b3fb76c3cbb196f28f88af5aecb1c29b2f2bbfa51a1 ca9840588d1718b996d4e0987b2caee0931e92f2
+f99100a033a521b0bb725db49ff10e6f27bf21ee6a18c3bfdfce294a3c70eb93 d4043ee9d97031e94f205110996d2381dd26c540
+f9bc81246e5d5f703bc5cfcf7dc637f1ac952dfbb4ece2f84d68d84e1bef99ff 713417dfe4608035e6615918b57a6edd78a3955f
+f9e46cd8f3f9eba5b9530820893f31ec9b135df075cde7e1331ec1834616a405 29a61a7f86f3703914b82d1d06b1c6e2775c684d
+fa2a467607e74eeb7d2865994f8a105837ad47c7f34d930a219544a187899dae 4428dfb78da0d5cb229763c8ff3278f6f4ee5bfc
+fa2ded3190944dfaf1a56eee66b9e298edc098aa834ed57952f04771f7ad5f45 de141d4bb983dad861835e181e7ecd5692a58aba
+fa50bed23c21de9e38ae6492dda179c028b9b618cfffec62311cd8e52bd90a12 d0f0c327029d770d1b10efc7a6c93080ed7d6844
+fa6a2b7e588e57115d2772c0d5250d886757404fc510a5956be8de4926e94c01 4f1355c91100d12f9e7202f91b245df0c110867c
+fa9b280330449aaf7fbd9709144d458cd54593a29045c6562a840a8580a80554 8cc293419bf3b5eaf51c8a90f3a1a86c2b415121
+faa7ca59426e17f6b34fa407d06cd634faaabeb4abe26df12296e05a17c98eef bfe05675d4e8f6b59d50932add8790f1a06b10ee
+fafc05a1d0b7614ba32f428eb52f3439ffbfed9a817e5ae069364cfc3fa3e4d4 fe1ca6bd22b5d8353ce6c2f3aba80805c438a7a5
+fb21dc7e0744c92621f4d94df822be5c275d509431d647c5e1cdd0cee85a57ad 2e7e8953cd4d7bd21729b29e1f7eae3ccf941b20
+fb56a4825bdf67a9ccfaf2d3962c16c49294fb565743f8ef3b3adf6ff721e729 47c31f584e09d79f3674987582a60c7bb6b673c0
+fb74b8e06683d2154d9ca50317e5e3581e1d0dea24229d4aad83ff0296e384bb 44bc212b00ca8cbdcc2751bd48534ee274318445
+fb93630decf348bd577c39423a6905f7f119e371eb098d0199e888fbee4154fe 0be51ee21b376c8848207ef5608aa7b3798d5020
+fbb6481c1ce45e504fe1233d0aaf228384b9739908d69d753da9fa7baffdb64a bc5aba43f785c144c9b5c6406ffbce6a7fec5304
+fbc60a067b3286e1547e78c5d195099580125b2b80e8e44716a313a4ebb66e6f 7a3b12fc4fb66960d4660e3e24af873bc07ac932
+fbc712543e696976405b7a070164abf6ac7f379b66f74dc2538563d4380a2004 7a6cf8153799563464d6f1761e55352c327b4122
+fc2157d0df676f018ad7a001a8150b8c5cae4313e50a69021f90cc82d8c0366d f9e43c1b9b200572edf0dbf333184151db14b711
+fc36ce993654a9c2f2ba5944b90d9fbb8b52bf3e45da4efcff1b137812573e63 8ba79944d51268d80b65372d508cf4d0ba239a4d
+fc7d032290d1d7af6b021e29f190d7a534aa3a31ecd9fd72e4a246ba7dc13e96 edc21d065b345f6af9ab4c3f07acc3f137436b27
+fc899c4e3fd5e61b944f9a9d4bc143888a80f4a11836024d441d0fef00cd355d 75636f4771c5ccf864814b83431ab7cebb091506
+fcc409c956764fbd4904916ec1a1dfb00ad9492ad11aa85fc272b94ff2412517 427c5e85811a8c7062c2c4621342e4e9c445817d
+fcfa61e2c38f220b1cf11a62ae7f6120b699c6f4fcdc0b0e02d31cfc2f1550f4 661900993cf98d9854e4d9c8584ae215e1eba148
+fd21b90bdf88489115886ad6a66f842e4b3b99f2c47c74f107a3012206a0975d dd583895e15b6ddbcca3ee7e8a1f7d8b39c25d1e
+fd6ab50ef7201a166b711edd386b9649ea052562bbab8a09ae5714f5f9c5a24d c215be4120385fc5f358e8a0609c94f6998184e7
+fd77de19e02087045739e074b6368724fd41d66a3e196b96c4092efc76b9a325 f83b69a6538188e9dad0b6dc09e5476bf36c9a1e
+fdf50c9b7119e7018b4a2f5c3235802104674e7d467f86608c7fee73c22321df b2bc567f25e3e07d0d6d55fab16f33423d863107
+fdf78aeb5b52940bbb069a7e2ae5495b1e77fcf4f9bb699bbe2b8372526bd3b3 b31b32f52231610f4e8c2c98e1ba5834b323555d
+fe191677f9b0be2d6232dffe0d8cf68edec0106e1c57efb1f084205ab56389b4 88bb8fe275f64c587e9d2f960cffa059d2b76fb7
+fe20b68ee605e6b7aeed310d188b6b597f7c184dc10166f645a683d77bdcfd7a 03770969aa16f40d4192c733408f152fc17b1f19
+fe9a766dd36f1a877fcec8eedede85556ff031d42678699c5350ee9117498a28 339a0ed36a822ace408b5084f8e3bc3e63b868aa
+fea0d6a630e0c517db1c57ab26a4229f48ac0cb61563777509633198bb60eadf a5274d8192aca15b24b1c7ddcba42726f5418ea4
+feae789c354caab8c9f38b38e18c04c41d7381d402eab1ca87befdeb5fa62539 65654b679591001c402b3925a31ea80f9e083969
+fec50a36447cf7f413ee48757b0f3d80322f3939d984556bda46600139cb5a89 0234c186bacb5144ca13f69f0dc9bfcc8ec7a075
+ff04d2f2105e9fc8f103e4b5840b46b3914a317e9c53cf523be81ca59cb5f023 6d34f01ce0ab2a3a198bd43ec250a5062d8e1217
+ff14a5e654a9963042a8183b2c7c356bb52440bc05f62ec193ba77a94c1f5290 bed3229b555ecd18bc7824c3ead7d2372b5f4f5a
+ff1854a73752a8779bf1d78e840d85b7e406fcc16a4cb7d1bb6a35bc60da49bb a52aa75b56d579790985af4ade1e73437d50b414
+ff3f1e98e17179dc0503307f2a01732096ea309288c2ceddcf71dbcce290691a fe9d965dc040c687edc1acefeb5a7919b14c6340
+ff503ebb130051b71bc883c83861389f4f4a66ebcfd5c05d5b2a7b805e45e773 143b052f17fbb3ac1310ecdb2bce4a10b9d2821e
+ff77a7e762c77408890442669da2a3a6ae2d0f130e8b953ce85edd0b81cb6f5e f7cce7c2d30260da7e0cc04e14ec618b832ce327
+ff77d10d1335090c32371bfb5179f915266de4425afaf4387dfca0bf2f6f98a2 86ff216d8fe665daecde956b38de7ec33b074477
+ffbbfce866283c7571b728f367b998d05b3767cb26600953db1b237b442fc0bd 16a67770b7d8d72317c4b775213c23a8bd74f5e0
+fff5cbc10ffea865d69aba64082ae17479c522e8e0305e678469749282bf0a18 afadc73a392f8cc8e2cc77dd62a7433dd3bafa8c
diff --git a/tests/resources/testrepo_256.git/objects/00/404e6179d86039bbc01a925bdc34ccdab778bd1d824f5562aaa319c6c8f045 b/tests/resources/testrepo_256.git/objects/00/404e6179d86039bbc01a925bdc34ccdab778bd1d824f5562aaa319c6c8f045
new file mode 100644
index 0000000..8d8d1d8
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/00/404e6179d86039bbc01a925bdc34ccdab778bd1d824f5562aaa319c6c8f045
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/01/18010feb81fe41b9df646d13866742a9070b56fd0ba9ab8dff828fc36c1f78 b/tests/resources/testrepo_256.git/objects/01/18010feb81fe41b9df646d13866742a9070b56fd0ba9ab8dff828fc36c1f78
new file mode 100644
index 0000000..c7fbd7e
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/01/18010feb81fe41b9df646d13866742a9070b56fd0ba9ab8dff828fc36c1f78
@@ -0,0 +1 @@
+x[NÅ0 ùî*üt•8§B°”Øq ¢iPš»º~GgŽ42zo œ±/kªÂ^rfK‚¡FO%BI"NªÉdwVe¡b8º”vb‘·ß<õ\°LçB6Ýk< ÅUtœÅ–¢%¦ÄÈÅYÿ#SµÔCè·ü\ßcÂù<Ž5~ô„7í:›<ªÎž¯¯žÛñÑßÁ:›n3&‚WƒÆl7½£–þSß>Kkt…Ú½ kÀõäҦʳéµý÷_‘ \ No newline at end of file
diff --git a/tests/resources/testrepo_256.git/objects/02/df938cfb169b0b6ba0dd16acdd727ea9364f7d48c55afed2f7dd889804065b b/tests/resources/testrepo_256.git/objects/02/df938cfb169b0b6ba0dd16acdd727ea9364f7d48c55afed2f7dd889804065b
new file mode 100644
index 0000000..cdfafac
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/02/df938cfb169b0b6ba0dd16acdd727ea9364f7d48c55afed2f7dd889804065b
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/05/f7b70a01b0ade8afa5a5fcd19f12cc38faf337d10ec03ef4363d1a86f63750 b/tests/resources/testrepo_256.git/objects/05/f7b70a01b0ade8afa5a5fcd19f12cc38faf337d10ec03ef4363d1a86f63750
new file mode 100644
index 0000000..b135ecc
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/05/f7b70a01b0ade8afa5a5fcd19f12cc38faf337d10ec03ef4363d1a86f63750
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/14/bd335f9d7188c778d44eba8801fe9bda46b66593291f5b9f7cd5f8888af12f b/tests/resources/testrepo_256.git/objects/14/bd335f9d7188c778d44eba8801fe9bda46b66593291f5b9f7cd5f8888af12f
new file mode 100644
index 0000000..58b2d09
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/14/bd335f9d7188c778d44eba8801fe9bda46b66593291f5b9f7cd5f8888af12f
@@ -0,0 +1 @@
+x ̱ƒ0 ÐÔžÂ#ÈB k„¾¹ä‘ j²}(^ûrÝ«Ùãô¶¬²„i3ÏÃg¾’õ _ÜœÝ8H§Ö¡N] “Š”ü}P·ó8žYò®o;¾o\Wùw×  \ No newline at end of file
diff --git a/tests/resources/testrepo_256.git/objects/17/9496410f66032c03bd2b7e8ddfc9c8c47820fab5615cc04d904989ce800498 b/tests/resources/testrepo_256.git/objects/17/9496410f66032c03bd2b7e8ddfc9c8c47820fab5615cc04d904989ce800498
new file mode 100644
index 0000000..9715764
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/17/9496410f66032c03bd2b7e8ddfc9c8c47820fab5615cc04d904989ce800498
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/19/0a1349522cc11f8682e34acca4ce4e1ea8508dfd77c24cefd461b65cead09e b/tests/resources/testrepo_256.git/objects/19/0a1349522cc11f8682e34acca4ce4e1ea8508dfd77c24cefd461b65cead09e
new file mode 100644
index 0000000..554d191
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/19/0a1349522cc11f8682e34acca4ce4e1ea8508dfd77c24cefd461b65cead09e
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/1b/4b74772bd83ff28bf44cda9be93f4afc2279623bb5b36c9194a660b7623c24 b/tests/resources/testrepo_256.git/objects/1b/4b74772bd83ff28bf44cda9be93f4afc2279623bb5b36c9194a660b7623c24
new file mode 100644
index 0000000..d5c518e
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/1b/4b74772bd83ff28bf44cda9be93f4afc2279623bb5b36c9194a660b7623c24
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/21/e1e1ebe45b2c1ef79ab050334e36a8015a546f0740bea4505e10d81a946f61 b/tests/resources/testrepo_256.git/objects/21/e1e1ebe45b2c1ef79ab050334e36a8015a546f0740bea4505e10d81a946f61
new file mode 100644
index 0000000..31aa9e5
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/21/e1e1ebe45b2c1ef79ab050334e36a8015a546f0740bea4505e10d81a946f61
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/23/8a501cf11a036f2f248008d88e14af624bb07fced6390997a0fa6abdad950a b/tests/resources/testrepo_256.git/objects/23/8a501cf11a036f2f248008d88e14af624bb07fced6390997a0fa6abdad950a
new file mode 100644
index 0000000..66dc15d
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/23/8a501cf11a036f2f248008d88e14af624bb07fced6390997a0fa6abdad950a
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/26/149bf1ac4612f24b532ae50a12b15f26aace3718749624f008bde68670352a b/tests/resources/testrepo_256.git/objects/26/149bf1ac4612f24b532ae50a12b15f26aace3718749624f008bde68670352a
new file mode 100644
index 0000000..bee6a42
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/26/149bf1ac4612f24b532ae50a12b15f26aace3718749624f008bde68670352a
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/2d/b6069c27ca4c08b784048644c307e17d0afe29b55f6488398cb59f13feb2f2 b/tests/resources/testrepo_256.git/objects/2d/b6069c27ca4c08b784048644c307e17d0afe29b55f6488398cb59f13feb2f2
new file mode 100644
index 0000000..3dfd546
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/2d/b6069c27ca4c08b784048644c307e17d0afe29b55f6488398cb59f13feb2f2
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/33/e415b835a670bb5c3c760efa0433ac0cbd2d44679f68f2df3a9ae7014cf2a8 b/tests/resources/testrepo_256.git/objects/33/e415b835a670bb5c3c760efa0433ac0cbd2d44679f68f2df3a9ae7014cf2a8
new file mode 100644
index 0000000..cedb2a2
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/33/e415b835a670bb5c3c760efa0433ac0cbd2d44679f68f2df3a9ae7014cf2a8
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/34/f79ad1c813b93d2ee11c830c2134815a31d9629e6aa9773338fedaab90976b b/tests/resources/testrepo_256.git/objects/34/f79ad1c813b93d2ee11c830c2134815a31d9629e6aa9773338fedaab90976b
new file mode 100644
index 0000000..3abc52f
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/34/f79ad1c813b93d2ee11c830c2134815a31d9629e6aa9773338fedaab90976b
@@ -0,0 +1 @@
+xÌM‚0†a×=Å\@3ÃôcŒWнi;-`, ooq÷~‹ï)¾²æ°„WŠ$EŸ3£¡Î¶B™}#) ‹ö-Æ$È„dŒI”¥íˆ­±±9ÑY•ï;A\¦i,ªT{ð«<kì£O+ÜÒ ÷²ú-À9|þqíÇ2láT_ fëlƒ®…#:D¥UYfØÁm4 \ No newline at end of file
diff --git a/tests/resources/testrepo_256.git/objects/36/eac24505d4c4405864ccf2f30d79af178374166daeceefbf11e2f058d30d60 b/tests/resources/testrepo_256.git/objects/36/eac24505d4c4405864ccf2f30d79af178374166daeceefbf11e2f058d30d60
new file mode 100644
index 0000000..df40d99
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/36/eac24505d4c4405864ccf2f30d79af178374166daeceefbf11e2f058d30d60
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/39/bf1ac28cc3f8432ba7cfeeca6bfffd9a0fe641784db85de2eb0f57b7553869 b/tests/resources/testrepo_256.git/objects/39/bf1ac28cc3f8432ba7cfeeca6bfffd9a0fe641784db85de2eb0f57b7553869
new file mode 100644
index 0000000..c05cacf
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/39/bf1ac28cc3f8432ba7cfeeca6bfffd9a0fe641784db85de2eb0f57b7553869
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/3b/58565ee067f13349cd4f89aa396d10f71c69e168d5c48ea23de59734ec3ab1 b/tests/resources/testrepo_256.git/objects/3b/58565ee067f13349cd4f89aa396d10f71c69e168d5c48ea23de59734ec3ab1
new file mode 100644
index 0000000..1b299dc
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/3b/58565ee067f13349cd4f89aa396d10f71c69e168d5c48ea23de59734ec3ab1
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/43/e084a4599ca42c476919917e3db8fde0045ee66305fd5e634b0c793c536a1b b/tests/resources/testrepo_256.git/objects/43/e084a4599ca42c476919917e3db8fde0045ee66305fd5e634b0c793c536a1b
new file mode 100644
index 0000000..2acf1b6
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/43/e084a4599ca42c476919917e3db8fde0045ee66305fd5e634b0c793c536a1b
@@ -0,0 +1 @@
+xQj!Dóí)ú ÚÚ¶Â{„œ m53Y—‰¹ÿÊæù((ªx¥ã8ö Èô2ÏÖ€} Ýë’KØ¢¦ZQs’VP99GɧƑ} Å#yËÊ.&ê¥S‰æ.g»MЂ‹guÔë“ÆHZ#ºN‚˜´x_5—1'«0jÏRPÂêr·F~ç6NøÔ1'\7ÑqƒË>ÍÇ×!û÷›Žãr`t¼Z¶Ö¬t=ší?¬˜Û~Vø1ÅfT \ No newline at end of file
diff --git a/tests/resources/testrepo_256.git/objects/47/3a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813 b/tests/resources/testrepo_256.git/objects/47/3a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813
new file mode 100644
index 0000000..7112238
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/47/3a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/4b/c142808884e472ee6cc331b132e66ef18f564d41efb055804ec1dd28efb3f5 b/tests/resources/testrepo_256.git/objects/4b/c142808884e472ee6cc331b132e66ef18f564d41efb055804ec1dd28efb3f5
new file mode 100644
index 0000000..d0d7e73
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/4b/c142808884e472ee6cc331b132e66ef18f564d41efb055804ec1dd28efb3f5
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/4d/f8ed86acaac5dc82b5652170996ce459d39e3a441e9759b635b0bc4ecc43fd b/tests/resources/testrepo_256.git/objects/4d/f8ed86acaac5dc82b5652170996ce459d39e3a441e9759b635b0bc4ecc43fd
new file mode 100644
index 0000000..8dc1932
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/4d/f8ed86acaac5dc82b5652170996ce459d39e3a441e9759b635b0bc4ecc43fd
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/5a/2d5699fea33657b42ba98c22b7898baaa0eda205a21cafdcb7e0f94b07bb9b b/tests/resources/testrepo_256.git/objects/5a/2d5699fea33657b42ba98c22b7898baaa0eda205a21cafdcb7e0f94b07bb9b
new file mode 100644
index 0000000..dd99313
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/5a/2d5699fea33657b42ba98c22b7898baaa0eda205a21cafdcb7e0f94b07bb9b
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/5c/a8959deb2b8327458e0344523eb1ddeeef4bce03e35864640b452f84d26848 b/tests/resources/testrepo_256.git/objects/5c/a8959deb2b8327458e0344523eb1ddeeef4bce03e35864640b452f84d26848
new file mode 100644
index 0000000..39e27c0
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/5c/a8959deb2b8327458e0344523eb1ddeeef4bce03e35864640b452f84d26848
@@ -0,0 +1 @@
+x1N1E©÷îRÆöØKÑPÒqÏxœ”]£#®Spº¯'ý÷¥/}Û.ÃøäŸÆ¡j\å1‹KRP€8RDImªPšºÌ!´ˆD>“pÈÍú¦ìš[¾Ë¡û0µºàµ•¬›D ‚¸Œ‚hëSf !ƒ‹šÙÆ@©éµ-üyÀZ S>©¢å\çn¬ÖSŒ ]É€Cl¸äÂT[#GM|Û-å>Ö~˜úSŽj¾Ö¾Ýún^uÒGz?_ÆzçéÛ›±.y—,b0ÏS ˤó¡ÿí/ŸzœÕðQvYÍévçÇÉ·“¹ì£›~­Ë/(jrS \ No newline at end of file
diff --git a/tests/resources/testrepo_256.git/objects/5d/bb1fff5c0094b31b25b4635ab9fbee66d65fe5dda47dd0ac5f01dd69a84c6f b/tests/resources/testrepo_256.git/objects/5d/bb1fff5c0094b31b25b4635ab9fbee66d65fe5dda47dd0ac5f01dd69a84c6f
new file mode 100644
index 0000000..17fae64
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/5d/bb1fff5c0094b31b25b4635ab9fbee66d65fe5dda47dd0ac5f01dd69a84c6f
@@ -0,0 +1,3 @@
+xuKj1D³žSô¤–Üê† !'H. OgÀ3
+²Œ¯9É6»¢¨÷ rݶµ2>õ¦
+d=‹Š²³³-bŒ5ì‹“dòÅ•Ä„¦Pî(DÁeŽÉ{Òé+6Ý;Ì>±‹4KÆĆ­¦rLfˆ &#9Á,ö¿Ýo}© >ríÞ–˜ë/×üNç-®—ç\·W°<[Çâá`‚1ÓhÇ£® ÞË=¶ŸKÝ®Z‡ñ‘Nçµ/·ôÇ{"‚ï?¥÷¼À¯ê®Ó7bÆW \ No newline at end of file
diff --git a/tests/resources/testrepo_256.git/objects/61/489e9e831f1d9001084d39b79f964c293db8620d679ea3596673c8a326446e b/tests/resources/testrepo_256.git/objects/61/489e9e831f1d9001084d39b79f964c293db8620d679ea3596673c8a326446e
new file mode 100644
index 0000000..0bece84
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/61/489e9e831f1d9001084d39b79f964c293db8620d679ea3596673c8a326446e
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/6d/5fd291bb0f67444e99ab492f1bf1fcdf5dca09dab24cf331e05111b4cfc1a3 b/tests/resources/testrepo_256.git/objects/6d/5fd291bb0f67444e99ab492f1bf1fcdf5dca09dab24cf331e05111b4cfc1a3
new file mode 100644
index 0000000..112998d
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/6d/5fd291bb0f67444e99ab492f1bf1fcdf5dca09dab24cf331e05111b4cfc1a3
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/70/30f925768d9beb65654ab8f436e3ca0a82b25eddefd237bf5a26a0441c2aa7 b/tests/resources/testrepo_256.git/objects/70/30f925768d9beb65654ab8f436e3ca0a82b25eddefd237bf5a26a0441c2aa7
new file mode 100644
index 0000000..860cad1
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/70/30f925768d9beb65654ab8f436e3ca0a82b25eddefd237bf5a26a0441c2aa7
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/73/8ff86401dbc5af692c83e660a4d510603c3f36e782a1a32ebd0388db6411ed b/tests/resources/testrepo_256.git/objects/73/8ff86401dbc5af692c83e660a4d510603c3f36e782a1a32ebd0388db6411ed
new file mode 100644
index 0000000..4c973ea
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/73/8ff86401dbc5af692c83e660a4d510603c3f36e782a1a32ebd0388db6411ed
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/73/b4f3c4f3182e6c8dd2c98aeb2c7811556538e7673e4b325307c71685fbf5b6 b/tests/resources/testrepo_256.git/objects/73/b4f3c4f3182e6c8dd2c98aeb2c7811556538e7673e4b325307c71685fbf5b6
new file mode 100644
index 0000000..67b84c4
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/73/b4f3c4f3182e6c8dd2c98aeb2c7811556538e7673e4b325307c71685fbf5b6
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/7e/4633ae1b0e83503dbea4417f9d5ccaf22b877c5a4522b6d1d2b16090ee2f6f b/tests/resources/testrepo_256.git/objects/7e/4633ae1b0e83503dbea4417f9d5ccaf22b877c5a4522b6d1d2b16090ee2f6f
new file mode 100644
index 0000000..993a62b
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/7e/4633ae1b0e83503dbea4417f9d5ccaf22b877c5a4522b6d1d2b16090ee2f6f
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/7e/9424c06052ca33bfc599bccadee60065d8664a9af7648a1455100c4f772e1c b/tests/resources/testrepo_256.git/objects/7e/9424c06052ca33bfc599bccadee60065d8664a9af7648a1455100c4f772e1c
new file mode 100644
index 0000000..70bf64e
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/7e/9424c06052ca33bfc599bccadee60065d8664a9af7648a1455100c4f772e1c
@@ -0,0 +1,2 @@
+xÏAj1 …á®çÚ‚-i,B(]µ(tiÙ2’™ ƒ=~]zƒnÿÅ÷xe_×¥Æù©f]òb¨µ…$Öa­9)yI Ù'ñ‚2i«èæYÉÇTU[f™îù°­ƒWVaÔ©5sùu,QãÜ
+¢¤€¤:„P’OœCp*£ä)?ú×~Àùó'x]6x˹\­^àÜöýžóz¿Ù©ìë<!űÆÏÎ;7:nuû70½oK_ò þ¤égƒV \ No newline at end of file
diff --git a/tests/resources/testrepo_256.git/objects/80/91b686de8bf697ef632dda9b3179f2419717275e3bfd2055b303489dbbfa47 b/tests/resources/testrepo_256.git/objects/80/91b686de8bf697ef632dda9b3179f2419717275e3bfd2055b303489dbbfa47
new file mode 100644
index 0000000..dec3810
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/80/91b686de8bf697ef632dda9b3179f2419717275e3bfd2055b303489dbbfa47
@@ -0,0 +1 @@
+x+)JMU01c040031QrutñueX·hîaÖ+|fºŸbìï½ eú¶±i=ãö^Í*×ÉÆGêOæ \ No newline at end of file
diff --git a/tests/resources/testrepo_256.git/objects/81/55958bbda08eed88c8ac908dc44452ed38911cffa54ccc06076f30a1ffb1bf b/tests/resources/testrepo_256.git/objects/81/55958bbda08eed88c8ac908dc44452ed38911cffa54ccc06076f30a1ffb1bf
new file mode 100644
index 0000000..e610e3b
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/81/55958bbda08eed88c8ac908dc44452ed38911cffa54ccc06076f30a1ffb1bf
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/90/1505c3355518bee35475c5d3f23bac1dded688b2bd314cc32b7f157e100724 b/tests/resources/testrepo_256.git/objects/90/1505c3355518bee35475c5d3f23bac1dded688b2bd314cc32b7f157e100724
new file mode 100644
index 0000000..09d0abf
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/90/1505c3355518bee35475c5d3f23bac1dded688b2bd314cc32b7f157e100724
@@ -0,0 +1 @@
+xQJ1 „}î)z¥M›¦<‚'HÓÔýÁÝÊZïï¢7ðm˜ùf`tîû}y ú°N3?RÌÈ6§BŠY¹wÈJeH‰R(…LÐM¬•Ì‰±€û”ÓŽåµA#8z½€‚Ú ÄPµ¥ÔµæÈÀ5¨d(:XH¾2ÁÉ÷Úæéßt®å_7ÑyøÛ—þŠ—÷]îO:÷gr©röBp—{=ZöŸ®k§ºù¿ ?s?T¬U… \ No newline at end of file
diff --git a/tests/resources/testrepo_256.git/objects/93/1093620e5f050e2127fb0b96786ebaa9ee6535fb698ec01b5f7a800fa27cbe b/tests/resources/testrepo_256.git/objects/93/1093620e5f050e2127fb0b96786ebaa9ee6535fb698ec01b5f7a800fa27cbe
new file mode 100644
index 0000000..70431af
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/93/1093620e5f050e2127fb0b96786ebaa9ee6535fb698ec01b5f7a800fa27cbe
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/94/ed253efa9e86fc636805c294c441d08b89b455903c0c14e9b16587fec081f5 b/tests/resources/testrepo_256.git/objects/94/ed253efa9e86fc636805c294c441d08b89b455903c0c14e9b16587fec081f5
new file mode 100644
index 0000000..41bcd18
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/94/ed253efa9e86fc636805c294c441d08b89b455903c0c14e9b16587fec081f5
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/96/c18f0297e38d01f4b2dacddea4259aea6b2961eb0822bd2c0c3f6029030045 b/tests/resources/testrepo_256.git/objects/96/c18f0297e38d01f4b2dacddea4259aea6b2961eb0822bd2c0c3f6029030045
new file mode 100644
index 0000000..7ca4cee
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/96/c18f0297e38d01f4b2dacddea4259aea6b2961eb0822bd2c0c3f6029030045
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/9c/cfa556cd7f73b426a7bedb7fc3a850e94f8c5ac1d71b9afa365a89005aff54 b/tests/resources/testrepo_256.git/objects/9c/cfa556cd7f73b426a7bedb7fc3a850e94f8c5ac1d71b9afa365a89005aff54
new file mode 100644
index 0000000..6e585cd
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/9c/cfa556cd7f73b426a7bedb7fc3a850e94f8c5ac1d71b9afa365a89005aff54
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/9d/aab17c25f647d652c72c8cc3cf4602c270a369beebc7d0b67238897bbc426b b/tests/resources/testrepo_256.git/objects/9d/aab17c25f647d652c72c8cc3cf4602c270a369beebc7d0b67238897bbc426b
new file mode 100644
index 0000000..74b8385
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/9d/aab17c25f647d652c72c8cc3cf4602c270a369beebc7d0b67238897bbc426b
@@ -0,0 +1 @@
+x+)JMU0´°d040031Qrutñue0~"ºÃtYÁî›2¾_,Ækxö꺤ÏÏøtßjÖsFŸO+L €@!1‰A¹+@æ£s¾¾JÇ>‘õIÞêÏ]³äœ¾àWÖÞµS¹ Æ&%æ%għeæ¤ê•T”0L;ØÏ4ýq/ã—M·ÎÞ[¢:ëU¶fâk¥½:<ö šÌ ®Pmy©å`å¹ñ—&îæOwñ›¹ÚS_úãŸû±§8omòùlø PpËùƒ‹¨TO \ No newline at end of file
diff --git a/tests/resources/testrepo_256.git/objects/a4/813ef6708e6011e8187224297e83e4a285f58bf5eabb1db270351388603c95 b/tests/resources/testrepo_256.git/objects/a4/813ef6708e6011e8187224297e83e4a285f58bf5eabb1db270351388603c95
new file mode 100644
index 0000000..2419974
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/a4/813ef6708e6011e8187224297e83e4a285f58bf5eabb1db270351388603c95
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/ab/ee32b3339d1566d75613ea61f40c14bdfc5b101b60fde4f44b58dd06667640 b/tests/resources/testrepo_256.git/objects/ab/ee32b3339d1566d75613ea61f40c14bdfc5b101b60fde4f44b58dd06667640
new file mode 100644
index 0000000..b390250
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/ab/ee32b3339d1566d75613ea61f40c14bdfc5b101b60fde4f44b58dd06667640
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/ae/a29dc305d40e362df25c3fdeed5502fd56b182af01b7740d297a24459333c5 b/tests/resources/testrepo_256.git/objects/ae/a29dc305d40e362df25c3fdeed5502fd56b182af01b7740d297a24459333c5
new file mode 100644
index 0000000..18a7f61
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/ae/a29dc305d40e362df25c3fdeed5502fd56b182af01b7740d297a24459333c5
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/b1/95873b48c824d995c974a3497ade7f62d2cd818bf388775cfa721de4068ebd b/tests/resources/testrepo_256.git/objects/b1/95873b48c824d995c974a3497ade7f62d2cd818bf388775cfa721de4068ebd
new file mode 100644
index 0000000..d1c032f
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/b1/95873b48c824d995c974a3497ade7f62d2cd818bf388775cfa721de4068ebd
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/b2/1c8c27a05a3f0bf9f0f44ebf05e11d9c591b04cfdaff7cc860310356d71827 b/tests/resources/testrepo_256.git/objects/b2/1c8c27a05a3f0bf9f0f44ebf05e11d9c591b04cfdaff7cc860310356d71827
new file mode 100644
index 0000000..c6da2ff
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/b2/1c8c27a05a3f0bf9f0f44ebf05e11d9c591b04cfdaff7cc860310356d71827
@@ -0,0 +1 @@
+xuQj1 DûíSè)²WëÕB)д°e%^¨×Åqèõë´ýÍß0Ì{0RKÙ:8vO½©‚å³µleåà–e1ršEO³LŽ0aò8?Ú™¯ÐtBŠäBÒàÉG'Q**MYÉŸ'áhY…íL¸õ\¼KíN9HÝáå*¿áx)aû|–Z^Áº…ØN¼pA4£º6xKß¡%øȵ\ï´ã=/[Ï·øÏ“÷¼zD7x¼‰-ì’áÏuWó¯W \ No newline at end of file
diff --git a/tests/resources/testrepo_256.git/objects/b6/1b940a8cd979a32e005682c5c09c22053675e2db24ea6b4b28cc75e9c10890 b/tests/resources/testrepo_256.git/objects/b6/1b940a8cd979a32e005682c5c09c22053675e2db24ea6b4b28cc75e9c10890
new file mode 100644
index 0000000..b1df3bd
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/b6/1b940a8cd979a32e005682c5c09c22053675e2db24ea6b4b28cc75e9c10890
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/b8/3624f6ac0995273c0034a7ab8c68929bdc91b69ad54ef94979b93eba3f6022 b/tests/resources/testrepo_256.git/objects/b8/3624f6ac0995273c0034a7ab8c68929bdc91b69ad54ef94979b93eba3f6022
new file mode 100644
index 0000000..3e36331
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/b8/3624f6ac0995273c0034a7ab8c68929bdc91b69ad54ef94979b93eba3f6022
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/bd/f2066a28e11603a1af04157ee4aad97814279fe500340eb3465797cbd3be23 b/tests/resources/testrepo_256.git/objects/bd/f2066a28e11603a1af04157ee4aad97814279fe500340eb3465797cbd3be23
new file mode 100644
index 0000000..9bb5b62
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/bd/f2066a28e11603a1af04157ee4aad97814279fe500340eb3465797cbd3be23
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/bf/a3b3b9a161d354e2254a444b12c412210e9689c17e51bfc318ce4bb4360f19 b/tests/resources/testrepo_256.git/objects/bf/a3b3b9a161d354e2254a444b12c412210e9689c17e51bfc318ce4bb4360f19
new file mode 100644
index 0000000..3cbf7e6
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/bf/a3b3b9a161d354e2254a444b12c412210e9689c17e51bfc318ce4bb4360f19
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/bf/cc4074ac517ed24d61b0aaa96359f304c3dc97e95f336269ed474ea846ada5 b/tests/resources/testrepo_256.git/objects/bf/cc4074ac517ed24d61b0aaa96359f304c3dc97e95f336269ed474ea846ada5
new file mode 100644
index 0000000..be8b99b
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/bf/cc4074ac517ed24d61b0aaa96359f304c3dc97e95f336269ed474ea846ada5
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/c2/58f010a08328a29cde33411d955520e0375fcbbcc14b7636a70f7536c32ef6 b/tests/resources/testrepo_256.git/objects/c2/58f010a08328a29cde33411d955520e0375fcbbcc14b7636a70f7536c32ef6
new file mode 100644
index 0000000..9d2ceb1
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/c2/58f010a08328a29cde33411d955520e0375fcbbcc14b7636a70f7536c32ef6
@@ -0,0 +1,2 @@
+xŒÁ‚0D=÷+önbvK[JbŒ?àÍxßÖ¥¢Öƒ/™I^æð” P»1=%+X’5IœO6“´uà =V•“*pDòì]h±v˜„G/„÷HÜ,o £¿€r1Ë@eÖŠLpë²
+ž´ƒ£ò0¾¾çÒs÷>ä±?ÙH¾ŽÎ!ìÑ"s}t3,åͳIÿmø3‘ \ No newline at end of file
diff --git a/tests/resources/testrepo_256.git/objects/ca/31f7336e882a233a2943787c5e94ba024ac9a4f763cb1d9bfd8e63aa7f7269 b/tests/resources/testrepo_256.git/objects/ca/31f7336e882a233a2943787c5e94ba024ac9a4f763cb1d9bfd8e63aa7f7269
new file mode 100644
index 0000000..cfcdac3
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/ca/31f7336e882a233a2943787c5e94ba024ac9a4f763cb1d9bfd8e63aa7f7269
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/cb/282e7c15fd8aeb2265cd621f5a228cb33dc84192980ca426cf9ab2a48cb9f0 b/tests/resources/testrepo_256.git/objects/cb/282e7c15fd8aeb2265cd621f5a228cb33dc84192980ca426cf9ab2a48cb9f0
new file mode 100644
index 0000000..77d9ec2
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/cb/282e7c15fd8aeb2265cd621f5a228cb33dc84192980ca426cf9ab2a48cb9f0
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/cc/b5a03da85607c230d111abfa899655d1b00e6529101a40d42f6acb059dff9f b/tests/resources/testrepo_256.git/objects/cc/b5a03da85607c230d111abfa899655d1b00e6529101a40d42f6acb059dff9f
new file mode 100644
index 0000000..a67d6e6
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/cc/b5a03da85607c230d111abfa899655d1b00e6529101a40d42f6acb059dff9f
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/cf/84e5be57f8d5d51f136d3d137b654c602721c469c1b0a58e7e95647a9cf1c0 b/tests/resources/testrepo_256.git/objects/cf/84e5be57f8d5d51f136d3d137b654c602721c469c1b0a58e7e95647a9cf1c0
new file mode 100644
index 0000000..ec37060
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/cf/84e5be57f8d5d51f136d3d137b654c602721c469c1b0a58e7e95647a9cf1c0
@@ -0,0 +1,3 @@
+x•ŽA
+Â0E]ç³ëBÌ$© ˆx¯ ™dª…ÆHŒ ·7xWÞãÃ%祑޴*<Æ}À“qVˆœ ÖZFŠ‰P‹'q/yŽ§(–ÙšQÏèUxµ[©Ö%
+~8É;äÇ*»XòИ‰¼vä`«QkÕm¿Ð䯑:—&O)IþÀp]ÚåÞÝ%V M˜kÉ°.Ü©/××Eÿ \ No newline at end of file
diff --git a/tests/resources/testrepo_256.git/objects/d8/8b60d2641df3656381dc8e201abb820a414de03eb63c065b06a2ab37d3f5ca b/tests/resources/testrepo_256.git/objects/d8/8b60d2641df3656381dc8e201abb820a414de03eb63c065b06a2ab37d3f5ca
new file mode 100644
index 0000000..6845087
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/d8/8b60d2641df3656381dc8e201abb820a414de03eb63c065b06a2ab37d3f5ca
@@ -0,0 +1 @@
+x5ÌÁjÃ0 €áýêyP”ÈŽ(£ÐëÖW²#‡´q4Rõ°·_/=þ|ðÏÐôǦ›d"ñMH=î"¦2åØ¡FOÄsšÚÉû.¥ëK;â%bãsi¹wö÷+VMÎ^gÞ656™ÆW¦ã[fÙa{®«é]68I•}ÉÇ"{åÇy®¼¬Ç¬õ "Š}ð‰-¢sߪwøá\.Zëb÷Ø=B \ No newline at end of file
diff --git a/tests/resources/testrepo_256.git/objects/de/caff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f b/tests/resources/testrepo_256.git/objects/de/caff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f
new file mode 100644
index 0000000..a53ab84
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/de/caff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/eb/ead5965196dfaeab52b1a5d92b78e54493fdaa78f72268d4cc69b61d5feee1 b/tests/resources/testrepo_256.git/objects/eb/ead5965196dfaeab52b1a5d92b78e54493fdaa78f72268d4cc69b61d5feee1
new file mode 100644
index 0000000..225c457
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/eb/ead5965196dfaeab52b1a5d92b78e54493fdaa78f72268d4cc69b61d5feee1
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/f2/a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736 b/tests/resources/testrepo_256.git/objects/f2/a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736
new file mode 100644
index 0000000..58d51e5
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/f2/a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/f2/c8da1a7c2eb49ff25c47441f0b3f387faeddde1b37d0ad2f3f6a63f5327978 b/tests/resources/testrepo_256.git/objects/f2/c8da1a7c2eb49ff25c47441f0b3f387faeddde1b37d0ad2f3f6a63f5327978
new file mode 100644
index 0000000..04bf5eb
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/f2/c8da1a7c2eb49ff25c47441f0b3f387faeddde1b37d0ad2f3f6a63f5327978
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/f3/1459efb9367c5a19c9dd24c75107423d5773066922ea5e55eaeb6490979562 b/tests/resources/testrepo_256.git/objects/f3/1459efb9367c5a19c9dd24c75107423d5773066922ea5e55eaeb6490979562
new file mode 100644
index 0000000..37a289e
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/f3/1459efb9367c5a19c9dd24c75107423d5773066922ea5e55eaeb6490979562
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/pack/pack-b87f1f214098b19ce092afb9ef6e7643653c03e7f91faa27b767e3eb8225f0f6.idx b/tests/resources/testrepo_256.git/objects/pack/pack-b87f1f214098b19ce092afb9ef6e7643653c03e7f91faa27b767e3eb8225f0f6.idx
new file mode 100644
index 0000000..897e8a4
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/pack/pack-b87f1f214098b19ce092afb9ef6e7643653c03e7f91faa27b767e3eb8225f0f6.idx
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/pack/pack-b87f1f214098b19ce092afb9ef6e7643653c03e7f91faa27b767e3eb8225f0f6.pack b/tests/resources/testrepo_256.git/objects/pack/pack-b87f1f214098b19ce092afb9ef6e7643653c03e7f91faa27b767e3eb8225f0f6.pack
new file mode 100644
index 0000000..9c85578
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/pack/pack-b87f1f214098b19ce092afb9ef6e7643653c03e7f91faa27b767e3eb8225f0f6.pack
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/pack/pack-e2f07f30db7e480ea84a0e64ee791b9b270067124b2609019b74f33f256f33fa.idx b/tests/resources/testrepo_256.git/objects/pack/pack-e2f07f30db7e480ea84a0e64ee791b9b270067124b2609019b74f33f256f33fa.idx
new file mode 100644
index 0000000..9e2ec99
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/pack/pack-e2f07f30db7e480ea84a0e64ee791b9b270067124b2609019b74f33f256f33fa.idx
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/pack/pack-e2f07f30db7e480ea84a0e64ee791b9b270067124b2609019b74f33f256f33fa.pack b/tests/resources/testrepo_256.git/objects/pack/pack-e2f07f30db7e480ea84a0e64ee791b9b270067124b2609019b74f33f256f33fa.pack
new file mode 100644
index 0000000..66cd292
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/pack/pack-e2f07f30db7e480ea84a0e64ee791b9b270067124b2609019b74f33f256f33fa.pack
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/pack/pack-f72bbfa35af982c2a60735152c80b24ee981cf102db76764c383f9b87935d0d3.idx b/tests/resources/testrepo_256.git/objects/pack/pack-f72bbfa35af982c2a60735152c80b24ee981cf102db76764c383f9b87935d0d3.idx
new file mode 100644
index 0000000..1d197e8
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/pack/pack-f72bbfa35af982c2a60735152c80b24ee981cf102db76764c383f9b87935d0d3.idx
Binary files differ
diff --git a/tests/resources/testrepo_256.git/objects/pack/pack-f72bbfa35af982c2a60735152c80b24ee981cf102db76764c383f9b87935d0d3.pack b/tests/resources/testrepo_256.git/objects/pack/pack-f72bbfa35af982c2a60735152c80b24ee981cf102db76764c383f9b87935d0d3.pack
new file mode 100644
index 0000000..5b615e1
--- /dev/null
+++ b/tests/resources/testrepo_256.git/objects/pack/pack-f72bbfa35af982c2a60735152c80b24ee981cf102db76764c383f9b87935d0d3.pack
Binary files differ
diff --git a/tests/resources/testrepo_256.git/packed-refs b/tests/resources/testrepo_256.git/packed-refs
new file mode 100644
index 0000000..995685f
--- /dev/null
+++ b/tests/resources/testrepo_256.git/packed-refs
@@ -0,0 +1,3 @@
+# pack-refs with: peeled sorted
+66fe8385c6378bfa5ca5573bd0fdd773e4eadb0e86416b483f2c50c839859ecb refs/heads/packed
+cb282e7c15fd8aeb2265cd621f5a228cb33dc84192980ca426cf9ab2a48cb9f0 refs/heads/packed-test
diff --git a/tests/resources/testrepo_256.git/refs/blobs/annotated_tag_to_blob b/tests/resources/testrepo_256.git/refs/blobs/annotated_tag_to_blob
new file mode 100644
index 0000000..2723f89
--- /dev/null
+++ b/tests/resources/testrepo_256.git/refs/blobs/annotated_tag_to_blob
@@ -0,0 +1 @@
+d88b60d2641df3656381dc8e201abb820a414de03eb63c065b06a2ab37d3f5ca
diff --git a/tests/resources/testrepo_256.git/refs/heads/br2 b/tests/resources/testrepo_256.git/refs/heads/br2
new file mode 100644
index 0000000..2abd96e
--- /dev/null
+++ b/tests/resources/testrepo_256.git/refs/heads/br2
@@ -0,0 +1 @@
+a4813ef6708e6011e8187224297e83e4a285f58bf5eabb1db270351388603c95
diff --git a/tests/resources/testrepo_256.git/refs/heads/cannot-fetch b/tests/resources/testrepo_256.git/refs/heads/cannot-fetch
new file mode 100644
index 0000000..2abd96e
--- /dev/null
+++ b/tests/resources/testrepo_256.git/refs/heads/cannot-fetch
@@ -0,0 +1 @@
+a4813ef6708e6011e8187224297e83e4a285f58bf5eabb1db270351388603c95
diff --git a/tests/resources/testrepo_256.git/refs/heads/chomped b/tests/resources/testrepo_256.git/refs/heads/chomped
new file mode 100644
index 0000000..de0d953
--- /dev/null
+++ b/tests/resources/testrepo_256.git/refs/heads/chomped
@@ -0,0 +1 @@
+4d46d9719e425ef2dfb5bfba098d0b62e21b2b92d0731892eef70db0870e3744
diff --git a/tests/resources/testrepo_256.git/refs/heads/haacked b/tests/resources/testrepo_256.git/refs/heads/haacked
new file mode 100644
index 0000000..5feda4d
--- /dev/null
+++ b/tests/resources/testrepo_256.git/refs/heads/haacked
@@ -0,0 +1 @@
+7e9424c06052ca33bfc599bccadee60065d8664a9af7648a1455100c4f772e1c
diff --git a/tests/resources/testrepo_256.git/refs/heads/master b/tests/resources/testrepo_256.git/refs/heads/master
new file mode 100644
index 0000000..106231c
--- /dev/null
+++ b/tests/resources/testrepo_256.git/refs/heads/master
@@ -0,0 +1 @@
+decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f
diff --git a/tests/resources/testrepo_256.git/refs/heads/not-good b/tests/resources/testrepo_256.git/refs/heads/not-good
new file mode 100644
index 0000000..106231c
--- /dev/null
+++ b/tests/resources/testrepo_256.git/refs/heads/not-good
@@ -0,0 +1 @@
+decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f
diff --git a/tests/resources/testrepo_256.git/refs/heads/packed-test b/tests/resources/testrepo_256.git/refs/heads/packed-test
new file mode 100644
index 0000000..7c2c5e4
--- /dev/null
+++ b/tests/resources/testrepo_256.git/refs/heads/packed-test
@@ -0,0 +1 @@
+43e084a4599ca42c476919917e3db8fde0045ee66305fd5e634b0c793c536a1b
diff --git a/tests/resources/testrepo_256.git/refs/heads/subtrees b/tests/resources/testrepo_256.git/refs/heads/subtrees
new file mode 100644
index 0000000..3b352e1
--- /dev/null
+++ b/tests/resources/testrepo_256.git/refs/heads/subtrees
@@ -0,0 +1 @@
+0118010feb81fe41b9df646d13866742a9070b56fd0ba9ab8dff828fc36c1f78
diff --git a/tests/resources/testrepo_256.git/refs/heads/test b/tests/resources/testrepo_256.git/refs/heads/test
new file mode 100644
index 0000000..de0d953
--- /dev/null
+++ b/tests/resources/testrepo_256.git/refs/heads/test
@@ -0,0 +1 @@
+4d46d9719e425ef2dfb5bfba098d0b62e21b2b92d0731892eef70db0870e3744
diff --git a/tests/resources/testrepo_256.git/refs/heads/track-local b/tests/resources/testrepo_256.git/refs/heads/track-local
new file mode 100644
index 0000000..8f6ecbe
--- /dev/null
+++ b/tests/resources/testrepo_256.git/refs/heads/track-local
@@ -0,0 +1 @@
+b83624f6ac0995273c0034a7ab8c68929bdc91b69ad54ef94979b93eba3f6022
diff --git a/tests/resources/testrepo_256.git/refs/heads/trailing b/tests/resources/testrepo_256.git/refs/heads/trailing
new file mode 100644
index 0000000..de0d953
--- /dev/null
+++ b/tests/resources/testrepo_256.git/refs/heads/trailing
@@ -0,0 +1 @@
+4d46d9719e425ef2dfb5bfba098d0b62e21b2b92d0731892eef70db0870e3744
diff --git a/tests/resources/testrepo_256.git/refs/heads/with-empty-log b/tests/resources/testrepo_256.git/refs/heads/with-empty-log
new file mode 100644
index 0000000..bd392a7
--- /dev/null
+++ b/tests/resources/testrepo_256.git/refs/heads/with-empty-log
@@ -0,0 +1 @@
+7e4633ae1b0e83503dbea4417f9d5ccaf22b877c5a4522b6d1d2b16090ee2f6f
diff --git a/tests/resources/testrepo_256.git/refs/notes/fanout b/tests/resources/testrepo_256.git/refs/notes/fanout
new file mode 100644
index 0000000..307abb4
--- /dev/null
+++ b/tests/resources/testrepo_256.git/refs/notes/fanout
@@ -0,0 +1 @@
+cf84e5be57f8d5d51f136d3d137b654c602721c469c1b0a58e7e95647a9cf1c0
diff --git a/tests/resources/testrepo_256.git/refs/remotes/test/master b/tests/resources/testrepo_256.git/refs/remotes/test/master
new file mode 100644
index 0000000..ee4aa14
--- /dev/null
+++ b/tests/resources/testrepo_256.git/refs/remotes/test/master
@@ -0,0 +1 @@
+1b4b74772bd83ff28bf44cda9be93f4afc2279623bb5b36c9194a660b7623c24
diff --git a/tests/resources/testrepo_256.git/refs/tags/annotated_tag_to_blob b/tests/resources/testrepo_256.git/refs/tags/annotated_tag_to_blob
new file mode 100644
index 0000000..2723f89
--- /dev/null
+++ b/tests/resources/testrepo_256.git/refs/tags/annotated_tag_to_blob
@@ -0,0 +1 @@
+d88b60d2641df3656381dc8e201abb820a414de03eb63c065b06a2ab37d3f5ca
diff --git a/tests/resources/testrepo_256.git/refs/tags/e90810b b/tests/resources/testrepo_256.git/refs/tags/e90810b
new file mode 100644
index 0000000..eb8846a
--- /dev/null
+++ b/tests/resources/testrepo_256.git/refs/tags/e90810b
@@ -0,0 +1 @@
+21e1e1ebe45b2c1ef79ab050334e36a8015a546f0740bea4505e10d81a946f61
diff --git a/tests/resources/testrepo_256.git/refs/tags/hard_tag b/tests/resources/testrepo_256.git/refs/tags/hard_tag
new file mode 100644
index 0000000..05dfe06
--- /dev/null
+++ b/tests/resources/testrepo_256.git/refs/tags/hard_tag
@@ -0,0 +1 @@
+34f79ad1c813b93d2ee11c830c2134815a31d9629e6aa9773338fedaab90976b
diff --git a/tests/resources/testrepo_256.git/refs/tags/point_to_blob b/tests/resources/testrepo_256.git/refs/tags/point_to_blob
new file mode 100644
index 0000000..5efdc77
--- /dev/null
+++ b/tests/resources/testrepo_256.git/refs/tags/point_to_blob
@@ -0,0 +1 @@
+33e415b835a670bb5c3c760efa0433ac0cbd2d44679f68f2df3a9ae7014cf2a8
diff --git a/tests/resources/testrepo_256.git/refs/tags/taggerless b/tests/resources/testrepo_256.git/refs/tags/taggerless
new file mode 100644
index 0000000..45547b7
--- /dev/null
+++ b/tests/resources/testrepo_256.git/refs/tags/taggerless
@@ -0,0 +1 @@
+14bd335f9d7188c778d44eba8801fe9bda46b66593291f5b9f7cd5f8888af12f
diff --git a/tests/resources/testrepo_256.git/refs/tags/test b/tests/resources/testrepo_256.git/refs/tags/test
new file mode 100644
index 0000000..a7d2fef
--- /dev/null
+++ b/tests/resources/testrepo_256.git/refs/tags/test
@@ -0,0 +1 @@
+c258f010a08328a29cde33411d955520e0375fcbbcc14b7636a70f7536c32ef6
diff --git a/tests/resources/testrepo_256.git/refs/tags/wrapped_tag b/tests/resources/testrepo_256.git/refs/tags/wrapped_tag
new file mode 100644
index 0000000..05dfe06
--- /dev/null
+++ b/tests/resources/testrepo_256.git/refs/tags/wrapped_tag
@@ -0,0 +1 @@
+34f79ad1c813b93d2ee11c830c2134815a31d9629e6aa9773338fedaab90976b
diff --git a/tests/resources/testrepo_256/.gitted/HEAD b/tests/resources/testrepo_256/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/testrepo_256/.gitted/config b/tests/resources/testrepo_256/.gitted/config
new file mode 100644
index 0000000..ba975e1
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/config
@@ -0,0 +1,15 @@
+[core]
+ repositoryformatversion = 1
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
+ precomposeunicode = true
+[remote "origin"]
+ url = /Users/ethomson/Personal/Projects/libgit2/libgit2/tests/resources/testrepo_256.git
+ fetch = +refs/heads/*:refs/remotes/origin/*
+[extensions]
+ objectformat = sha256
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/testrepo_256/.gitted/description b/tests/resources/testrepo_256/.gitted/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/testrepo_256/.gitted/index b/tests/resources/testrepo_256/.gitted/index
new file mode 100644
index 0000000..6b18426
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/index
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/info/exclude b/tests/resources/testrepo_256/.gitted/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/testrepo_256/.gitted/logs/HEAD b/tests/resources/testrepo_256/.gitted/logs/HEAD
new file mode 100644
index 0000000..3592303
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/logs/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000000000000000000000000000 decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f Edward Thomson <ethomson@vercel.com> 1680595792 +0100 clone: from /Users/ethomson/Personal/Projects/libgit2/libgit2/tests/resources/testrepo_256.git
diff --git a/tests/resources/testrepo_256/.gitted/logs/refs/heads/master b/tests/resources/testrepo_256/.gitted/logs/refs/heads/master
new file mode 100644
index 0000000..3592303
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/logs/refs/heads/master
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000000000000000000000000000 decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f Edward Thomson <ethomson@vercel.com> 1680595792 +0100 clone: from /Users/ethomson/Personal/Projects/libgit2/libgit2/tests/resources/testrepo_256.git
diff --git a/tests/resources/testrepo_256/.gitted/logs/refs/remotes/origin/HEAD b/tests/resources/testrepo_256/.gitted/logs/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..3592303
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/logs/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+0000000000000000000000000000000000000000000000000000000000000000 decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f Edward Thomson <ethomson@vercel.com> 1680595792 +0100 clone: from /Users/ethomson/Personal/Projects/libgit2/libgit2/tests/resources/testrepo_256.git
diff --git a/tests/resources/testrepo_256/.gitted/objects/00/404e6179d86039bbc01a925bdc34ccdab778bd1d824f5562aaa319c6c8f045 b/tests/resources/testrepo_256/.gitted/objects/00/404e6179d86039bbc01a925bdc34ccdab778bd1d824f5562aaa319c6c8f045
new file mode 100644
index 0000000..8d8d1d8
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/00/404e6179d86039bbc01a925bdc34ccdab778bd1d824f5562aaa319c6c8f045
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/01/18010feb81fe41b9df646d13866742a9070b56fd0ba9ab8dff828fc36c1f78 b/tests/resources/testrepo_256/.gitted/objects/01/18010feb81fe41b9df646d13866742a9070b56fd0ba9ab8dff828fc36c1f78
new file mode 100644
index 0000000..c7fbd7e
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/01/18010feb81fe41b9df646d13866742a9070b56fd0ba9ab8dff828fc36c1f78
@@ -0,0 +1 @@
+x[NÅ0 ùî*üt•8§B°”Øq ¢iPš»º~GgŽ42zo œ±/kªÂ^rfK‚¡FO%BI"NªÉdwVe¡b8º”vb‘·ß<õ\°LçB6Ýk< ÅUtœÅ–¢%¦ÄÈÅYÿ#SµÔCè·ü\ßcÂù<Ž5~ô„7í:›<ªÎž¯¯žÛñÑßÁ:›n3&‚WƒÆl7½£–þSß>Kkt…Ú½ kÀõäҦʳéµý÷_‘ \ No newline at end of file
diff --git a/tests/resources/testrepo_256/.gitted/objects/02/df938cfb169b0b6ba0dd16acdd727ea9364f7d48c55afed2f7dd889804065b b/tests/resources/testrepo_256/.gitted/objects/02/df938cfb169b0b6ba0dd16acdd727ea9364f7d48c55afed2f7dd889804065b
new file mode 100644
index 0000000..cdfafac
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/02/df938cfb169b0b6ba0dd16acdd727ea9364f7d48c55afed2f7dd889804065b
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/05/f7b70a01b0ade8afa5a5fcd19f12cc38faf337d10ec03ef4363d1a86f63750 b/tests/resources/testrepo_256/.gitted/objects/05/f7b70a01b0ade8afa5a5fcd19f12cc38faf337d10ec03ef4363d1a86f63750
new file mode 100644
index 0000000..b135ecc
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/05/f7b70a01b0ade8afa5a5fcd19f12cc38faf337d10ec03ef4363d1a86f63750
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/14/bd335f9d7188c778d44eba8801fe9bda46b66593291f5b9f7cd5f8888af12f b/tests/resources/testrepo_256/.gitted/objects/14/bd335f9d7188c778d44eba8801fe9bda46b66593291f5b9f7cd5f8888af12f
new file mode 100644
index 0000000..58b2d09
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/14/bd335f9d7188c778d44eba8801fe9bda46b66593291f5b9f7cd5f8888af12f
@@ -0,0 +1 @@
+x ̱ƒ0 ÐÔžÂ#ÈB k„¾¹ä‘ j²}(^ûrÝ«Ùãô¶¬²„i3ÏÃg¾’õ _ÜœÝ8H§Ö¡N] “Š”ü}P·ó8žYò®o;¾o\Wùw×  \ No newline at end of file
diff --git a/tests/resources/testrepo_256/.gitted/objects/17/9496410f66032c03bd2b7e8ddfc9c8c47820fab5615cc04d904989ce800498 b/tests/resources/testrepo_256/.gitted/objects/17/9496410f66032c03bd2b7e8ddfc9c8c47820fab5615cc04d904989ce800498
new file mode 100644
index 0000000..9715764
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/17/9496410f66032c03bd2b7e8ddfc9c8c47820fab5615cc04d904989ce800498
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/19/0a1349522cc11f8682e34acca4ce4e1ea8508dfd77c24cefd461b65cead09e b/tests/resources/testrepo_256/.gitted/objects/19/0a1349522cc11f8682e34acca4ce4e1ea8508dfd77c24cefd461b65cead09e
new file mode 100644
index 0000000..554d191
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/19/0a1349522cc11f8682e34acca4ce4e1ea8508dfd77c24cefd461b65cead09e
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/1b/4b74772bd83ff28bf44cda9be93f4afc2279623bb5b36c9194a660b7623c24 b/tests/resources/testrepo_256/.gitted/objects/1b/4b74772bd83ff28bf44cda9be93f4afc2279623bb5b36c9194a660b7623c24
new file mode 100644
index 0000000..d5c518e
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/1b/4b74772bd83ff28bf44cda9be93f4afc2279623bb5b36c9194a660b7623c24
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/21/e1e1ebe45b2c1ef79ab050334e36a8015a546f0740bea4505e10d81a946f61 b/tests/resources/testrepo_256/.gitted/objects/21/e1e1ebe45b2c1ef79ab050334e36a8015a546f0740bea4505e10d81a946f61
new file mode 100644
index 0000000..31aa9e5
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/21/e1e1ebe45b2c1ef79ab050334e36a8015a546f0740bea4505e10d81a946f61
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/23/8a501cf11a036f2f248008d88e14af624bb07fced6390997a0fa6abdad950a b/tests/resources/testrepo_256/.gitted/objects/23/8a501cf11a036f2f248008d88e14af624bb07fced6390997a0fa6abdad950a
new file mode 100644
index 0000000..66dc15d
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/23/8a501cf11a036f2f248008d88e14af624bb07fced6390997a0fa6abdad950a
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/26/149bf1ac4612f24b532ae50a12b15f26aace3718749624f008bde68670352a b/tests/resources/testrepo_256/.gitted/objects/26/149bf1ac4612f24b532ae50a12b15f26aace3718749624f008bde68670352a
new file mode 100644
index 0000000..bee6a42
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/26/149bf1ac4612f24b532ae50a12b15f26aace3718749624f008bde68670352a
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/2d/b6069c27ca4c08b784048644c307e17d0afe29b55f6488398cb59f13feb2f2 b/tests/resources/testrepo_256/.gitted/objects/2d/b6069c27ca4c08b784048644c307e17d0afe29b55f6488398cb59f13feb2f2
new file mode 100644
index 0000000..3dfd546
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/2d/b6069c27ca4c08b784048644c307e17d0afe29b55f6488398cb59f13feb2f2
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/33/e415b835a670bb5c3c760efa0433ac0cbd2d44679f68f2df3a9ae7014cf2a8 b/tests/resources/testrepo_256/.gitted/objects/33/e415b835a670bb5c3c760efa0433ac0cbd2d44679f68f2df3a9ae7014cf2a8
new file mode 100644
index 0000000..cedb2a2
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/33/e415b835a670bb5c3c760efa0433ac0cbd2d44679f68f2df3a9ae7014cf2a8
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/34/f79ad1c813b93d2ee11c830c2134815a31d9629e6aa9773338fedaab90976b b/tests/resources/testrepo_256/.gitted/objects/34/f79ad1c813b93d2ee11c830c2134815a31d9629e6aa9773338fedaab90976b
new file mode 100644
index 0000000..3abc52f
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/34/f79ad1c813b93d2ee11c830c2134815a31d9629e6aa9773338fedaab90976b
@@ -0,0 +1 @@
+xÌM‚0†a×=Å\@3ÃôcŒWнi;-`, ooq÷~‹ï)¾²æ°„WŠ$EŸ3£¡Î¶B™}#) ‹ö-Æ$È„dŒI”¥íˆ­±±9ÑY•ï;A\¦i,ªT{ð«<kì£O+ÜÒ ÷²ú-À9|þqíÇ2láT_ fëlƒ®…#:D¥UYfØÁm4 \ No newline at end of file
diff --git a/tests/resources/testrepo_256/.gitted/objects/36/eac24505d4c4405864ccf2f30d79af178374166daeceefbf11e2f058d30d60 b/tests/resources/testrepo_256/.gitted/objects/36/eac24505d4c4405864ccf2f30d79af178374166daeceefbf11e2f058d30d60
new file mode 100644
index 0000000..df40d99
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/36/eac24505d4c4405864ccf2f30d79af178374166daeceefbf11e2f058d30d60
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/39/bf1ac28cc3f8432ba7cfeeca6bfffd9a0fe641784db85de2eb0f57b7553869 b/tests/resources/testrepo_256/.gitted/objects/39/bf1ac28cc3f8432ba7cfeeca6bfffd9a0fe641784db85de2eb0f57b7553869
new file mode 100644
index 0000000..c05cacf
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/39/bf1ac28cc3f8432ba7cfeeca6bfffd9a0fe641784db85de2eb0f57b7553869
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/3b/58565ee067f13349cd4f89aa396d10f71c69e168d5c48ea23de59734ec3ab1 b/tests/resources/testrepo_256/.gitted/objects/3b/58565ee067f13349cd4f89aa396d10f71c69e168d5c48ea23de59734ec3ab1
new file mode 100644
index 0000000..1b299dc
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/3b/58565ee067f13349cd4f89aa396d10f71c69e168d5c48ea23de59734ec3ab1
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/43/e084a4599ca42c476919917e3db8fde0045ee66305fd5e634b0c793c536a1b b/tests/resources/testrepo_256/.gitted/objects/43/e084a4599ca42c476919917e3db8fde0045ee66305fd5e634b0c793c536a1b
new file mode 100644
index 0000000..2acf1b6
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/43/e084a4599ca42c476919917e3db8fde0045ee66305fd5e634b0c793c536a1b
@@ -0,0 +1 @@
+xQj!Dóí)ú ÚÚ¶Â{„œ m53Y—‰¹ÿÊæù((ªx¥ã8ö Èô2ÏÖ€} Ýë’KØ¢¦ZQs’VP99GɧƑ} Å#yËÊ.&ê¥S‰æ.g»MЂ‹guÔë“ÆHZ#ºN‚˜´x_5—1'«0jÏRPÂêr·F~ç6NøÔ1'\7ÑqƒË>ÍÇ×!û÷›Žãr`t¼Z¶Ö¬t=ší?¬˜Û~Vø1ÅfT \ No newline at end of file
diff --git a/tests/resources/testrepo_256/.gitted/objects/47/3a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813 b/tests/resources/testrepo_256/.gitted/objects/47/3a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813
new file mode 100644
index 0000000..7112238
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/47/3a0f4c3be8a93681a267e3b1e9a7dcda1185436fe141f7749120a303721813
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/4b/c142808884e472ee6cc331b132e66ef18f564d41efb055804ec1dd28efb3f5 b/tests/resources/testrepo_256/.gitted/objects/4b/c142808884e472ee6cc331b132e66ef18f564d41efb055804ec1dd28efb3f5
new file mode 100644
index 0000000..d0d7e73
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/4b/c142808884e472ee6cc331b132e66ef18f564d41efb055804ec1dd28efb3f5
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/4d/f8ed86acaac5dc82b5652170996ce459d39e3a441e9759b635b0bc4ecc43fd b/tests/resources/testrepo_256/.gitted/objects/4d/f8ed86acaac5dc82b5652170996ce459d39e3a441e9759b635b0bc4ecc43fd
new file mode 100644
index 0000000..8dc1932
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/4d/f8ed86acaac5dc82b5652170996ce459d39e3a441e9759b635b0bc4ecc43fd
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/5a/2d5699fea33657b42ba98c22b7898baaa0eda205a21cafdcb7e0f94b07bb9b b/tests/resources/testrepo_256/.gitted/objects/5a/2d5699fea33657b42ba98c22b7898baaa0eda205a21cafdcb7e0f94b07bb9b
new file mode 100644
index 0000000..dd99313
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/5a/2d5699fea33657b42ba98c22b7898baaa0eda205a21cafdcb7e0f94b07bb9b
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/5c/a8959deb2b8327458e0344523eb1ddeeef4bce03e35864640b452f84d26848 b/tests/resources/testrepo_256/.gitted/objects/5c/a8959deb2b8327458e0344523eb1ddeeef4bce03e35864640b452f84d26848
new file mode 100644
index 0000000..39e27c0
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/5c/a8959deb2b8327458e0344523eb1ddeeef4bce03e35864640b452f84d26848
@@ -0,0 +1 @@
+x1N1E©÷îRÆöØKÑPÒqÏxœ”]£#®Spº¯'ý÷¥/}Û.ÃøäŸÆ¡j\å1‹KRP€8RDImªPšºÌ!´ˆD>“pÈÍú¦ìš[¾Ë¡û0µºàµ•¬›D ‚¸Œ‚hëSf !ƒ‹šÙÆ@©éµ-üyÀZ S>©¢å\çn¬ÖSŒ ]É€Cl¸äÂT[#GM|Û-å>Ö~˜úSŽj¾Ö¾Ýún^uÒGz?_ÆzçéÛ›±.y—,b0ÏS ˤó¡ÿí/ŸzœÕðQvYÍévçÇÉ·“¹ì£›~­Ë/(jrS \ No newline at end of file
diff --git a/tests/resources/testrepo_256/.gitted/objects/5d/bb1fff5c0094b31b25b4635ab9fbee66d65fe5dda47dd0ac5f01dd69a84c6f b/tests/resources/testrepo_256/.gitted/objects/5d/bb1fff5c0094b31b25b4635ab9fbee66d65fe5dda47dd0ac5f01dd69a84c6f
new file mode 100644
index 0000000..17fae64
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/5d/bb1fff5c0094b31b25b4635ab9fbee66d65fe5dda47dd0ac5f01dd69a84c6f
@@ -0,0 +1,3 @@
+xuKj1D³žSô¤–Üê† !'H. OgÀ3
+²Œ¯9É6»¢¨÷ rݶµ2>õ¦
+d=‹Š²³³-bŒ5ì‹“dòÅ•Ä„¦Pî(DÁeŽÉ{Òé+6Ý;Ì>±‹4KÆĆ­¦rLfˆ &#9Á,ö¿Ýo}© >ríÞ–˜ë/×üNç-®—ç\·W°<[Çâá`‚1ÓhÇ£® ÞË=¶ŸKÝ®Z‡ñ‘Nçµ/·ôÇ{"‚ï?¥÷¼À¯ê®Ó7bÆW \ No newline at end of file
diff --git a/tests/resources/testrepo_256/.gitted/objects/61/489e9e831f1d9001084d39b79f964c293db8620d679ea3596673c8a326446e b/tests/resources/testrepo_256/.gitted/objects/61/489e9e831f1d9001084d39b79f964c293db8620d679ea3596673c8a326446e
new file mode 100644
index 0000000..0bece84
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/61/489e9e831f1d9001084d39b79f964c293db8620d679ea3596673c8a326446e
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/6d/5fd291bb0f67444e99ab492f1bf1fcdf5dca09dab24cf331e05111b4cfc1a3 b/tests/resources/testrepo_256/.gitted/objects/6d/5fd291bb0f67444e99ab492f1bf1fcdf5dca09dab24cf331e05111b4cfc1a3
new file mode 100644
index 0000000..112998d
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/6d/5fd291bb0f67444e99ab492f1bf1fcdf5dca09dab24cf331e05111b4cfc1a3
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/70/30f925768d9beb65654ab8f436e3ca0a82b25eddefd237bf5a26a0441c2aa7 b/tests/resources/testrepo_256/.gitted/objects/70/30f925768d9beb65654ab8f436e3ca0a82b25eddefd237bf5a26a0441c2aa7
new file mode 100644
index 0000000..860cad1
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/70/30f925768d9beb65654ab8f436e3ca0a82b25eddefd237bf5a26a0441c2aa7
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/73/8ff86401dbc5af692c83e660a4d510603c3f36e782a1a32ebd0388db6411ed b/tests/resources/testrepo_256/.gitted/objects/73/8ff86401dbc5af692c83e660a4d510603c3f36e782a1a32ebd0388db6411ed
new file mode 100644
index 0000000..4c973ea
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/73/8ff86401dbc5af692c83e660a4d510603c3f36e782a1a32ebd0388db6411ed
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/73/b4f3c4f3182e6c8dd2c98aeb2c7811556538e7673e4b325307c71685fbf5b6 b/tests/resources/testrepo_256/.gitted/objects/73/b4f3c4f3182e6c8dd2c98aeb2c7811556538e7673e4b325307c71685fbf5b6
new file mode 100644
index 0000000..67b84c4
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/73/b4f3c4f3182e6c8dd2c98aeb2c7811556538e7673e4b325307c71685fbf5b6
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/7e/4633ae1b0e83503dbea4417f9d5ccaf22b877c5a4522b6d1d2b16090ee2f6f b/tests/resources/testrepo_256/.gitted/objects/7e/4633ae1b0e83503dbea4417f9d5ccaf22b877c5a4522b6d1d2b16090ee2f6f
new file mode 100644
index 0000000..993a62b
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/7e/4633ae1b0e83503dbea4417f9d5ccaf22b877c5a4522b6d1d2b16090ee2f6f
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/7e/9424c06052ca33bfc599bccadee60065d8664a9af7648a1455100c4f772e1c b/tests/resources/testrepo_256/.gitted/objects/7e/9424c06052ca33bfc599bccadee60065d8664a9af7648a1455100c4f772e1c
new file mode 100644
index 0000000..70bf64e
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/7e/9424c06052ca33bfc599bccadee60065d8664a9af7648a1455100c4f772e1c
@@ -0,0 +1,2 @@
+xÏAj1 …á®çÚ‚-i,B(]µ(tiÙ2’™ ƒ=~]zƒnÿÅ÷xe_×¥Æù©f]òb¨µ…$Öa­9)yI Ù'ñ‚2i«èæYÉÇTU[f™îù°­ƒWVaÔ©5sùu,QãÜ
+¢¤€¤:„P’OœCp*£ä)?ú×~Àùó'x]6x˹\­^àÜöýžóz¿Ù©ìë<!űÆÏÎ;7:nuû70½oK_ò þ¤égƒV \ No newline at end of file
diff --git a/tests/resources/testrepo_256/.gitted/objects/80/91b686de8bf697ef632dda9b3179f2419717275e3bfd2055b303489dbbfa47 b/tests/resources/testrepo_256/.gitted/objects/80/91b686de8bf697ef632dda9b3179f2419717275e3bfd2055b303489dbbfa47
new file mode 100644
index 0000000..dec3810
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/80/91b686de8bf697ef632dda9b3179f2419717275e3bfd2055b303489dbbfa47
@@ -0,0 +1 @@
+x+)JMU01c040031QrutñueX·hîaÖ+|fºŸbìï½ eú¶±i=ãö^Í*×ÉÆGêOæ \ No newline at end of file
diff --git a/tests/resources/testrepo_256/.gitted/objects/81/55958bbda08eed88c8ac908dc44452ed38911cffa54ccc06076f30a1ffb1bf b/tests/resources/testrepo_256/.gitted/objects/81/55958bbda08eed88c8ac908dc44452ed38911cffa54ccc06076f30a1ffb1bf
new file mode 100644
index 0000000..e610e3b
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/81/55958bbda08eed88c8ac908dc44452ed38911cffa54ccc06076f30a1ffb1bf
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/90/1505c3355518bee35475c5d3f23bac1dded688b2bd314cc32b7f157e100724 b/tests/resources/testrepo_256/.gitted/objects/90/1505c3355518bee35475c5d3f23bac1dded688b2bd314cc32b7f157e100724
new file mode 100644
index 0000000..09d0abf
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/90/1505c3355518bee35475c5d3f23bac1dded688b2bd314cc32b7f157e100724
@@ -0,0 +1 @@
+xQJ1 „}î)z¥M›¦<‚'HÓÔýÁÝÊZïï¢7ðm˜ùf`tîû}y ú°N3?RÌÈ6§BŠY¹wÈJeH‰R(…LÐM¬•Ì‰±€û”ÓŽåµA#8z½€‚Ú ÄPµ¥ÔµæÈÀ5¨d(:XH¾2ÁÉ÷Úæéßt®å_7ÑyøÛ—þŠ—÷]îO:÷gr©röBp—{=ZöŸ®k§ºù¿ ?s?T¬U… \ No newline at end of file
diff --git a/tests/resources/testrepo_256/.gitted/objects/93/1093620e5f050e2127fb0b96786ebaa9ee6535fb698ec01b5f7a800fa27cbe b/tests/resources/testrepo_256/.gitted/objects/93/1093620e5f050e2127fb0b96786ebaa9ee6535fb698ec01b5f7a800fa27cbe
new file mode 100644
index 0000000..70431af
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/93/1093620e5f050e2127fb0b96786ebaa9ee6535fb698ec01b5f7a800fa27cbe
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/94/ed253efa9e86fc636805c294c441d08b89b455903c0c14e9b16587fec081f5 b/tests/resources/testrepo_256/.gitted/objects/94/ed253efa9e86fc636805c294c441d08b89b455903c0c14e9b16587fec081f5
new file mode 100644
index 0000000..41bcd18
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/94/ed253efa9e86fc636805c294c441d08b89b455903c0c14e9b16587fec081f5
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/96/c18f0297e38d01f4b2dacddea4259aea6b2961eb0822bd2c0c3f6029030045 b/tests/resources/testrepo_256/.gitted/objects/96/c18f0297e38d01f4b2dacddea4259aea6b2961eb0822bd2c0c3f6029030045
new file mode 100644
index 0000000..7ca4cee
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/96/c18f0297e38d01f4b2dacddea4259aea6b2961eb0822bd2c0c3f6029030045
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/9c/cfa556cd7f73b426a7bedb7fc3a850e94f8c5ac1d71b9afa365a89005aff54 b/tests/resources/testrepo_256/.gitted/objects/9c/cfa556cd7f73b426a7bedb7fc3a850e94f8c5ac1d71b9afa365a89005aff54
new file mode 100644
index 0000000..6e585cd
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/9c/cfa556cd7f73b426a7bedb7fc3a850e94f8c5ac1d71b9afa365a89005aff54
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/9d/aab17c25f647d652c72c8cc3cf4602c270a369beebc7d0b67238897bbc426b b/tests/resources/testrepo_256/.gitted/objects/9d/aab17c25f647d652c72c8cc3cf4602c270a369beebc7d0b67238897bbc426b
new file mode 100644
index 0000000..74b8385
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/9d/aab17c25f647d652c72c8cc3cf4602c270a369beebc7d0b67238897bbc426b
@@ -0,0 +1 @@
+x+)JMU0´°d040031Qrutñue0~"ºÃtYÁî›2¾_,Ækxö꺤ÏÏøtßjÖsFŸO+L €@!1‰A¹+@æ£s¾¾JÇ>‘õIÞêÏ]³äœ¾àWÖÞµS¹ Æ&%æ%għeæ¤ê•T”0L;ØÏ4ýq/ã—M·ÎÞ[¢:ëU¶fâk¥½:<ö šÌ ®Pmy©å`å¹ñ—&îæOwñ›¹ÚS_úãŸû±§8omòùlø PpËùƒ‹¨TO \ No newline at end of file
diff --git a/tests/resources/testrepo_256/.gitted/objects/a4/813ef6708e6011e8187224297e83e4a285f58bf5eabb1db270351388603c95 b/tests/resources/testrepo_256/.gitted/objects/a4/813ef6708e6011e8187224297e83e4a285f58bf5eabb1db270351388603c95
new file mode 100644
index 0000000..2419974
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/a4/813ef6708e6011e8187224297e83e4a285f58bf5eabb1db270351388603c95
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/ab/ee32b3339d1566d75613ea61f40c14bdfc5b101b60fde4f44b58dd06667640 b/tests/resources/testrepo_256/.gitted/objects/ab/ee32b3339d1566d75613ea61f40c14bdfc5b101b60fde4f44b58dd06667640
new file mode 100644
index 0000000..b390250
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/ab/ee32b3339d1566d75613ea61f40c14bdfc5b101b60fde4f44b58dd06667640
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/ae/a29dc305d40e362df25c3fdeed5502fd56b182af01b7740d297a24459333c5 b/tests/resources/testrepo_256/.gitted/objects/ae/a29dc305d40e362df25c3fdeed5502fd56b182af01b7740d297a24459333c5
new file mode 100644
index 0000000..18a7f61
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/ae/a29dc305d40e362df25c3fdeed5502fd56b182af01b7740d297a24459333c5
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/b1/95873b48c824d995c974a3497ade7f62d2cd818bf388775cfa721de4068ebd b/tests/resources/testrepo_256/.gitted/objects/b1/95873b48c824d995c974a3497ade7f62d2cd818bf388775cfa721de4068ebd
new file mode 100644
index 0000000..d1c032f
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/b1/95873b48c824d995c974a3497ade7f62d2cd818bf388775cfa721de4068ebd
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/b2/1c8c27a05a3f0bf9f0f44ebf05e11d9c591b04cfdaff7cc860310356d71827 b/tests/resources/testrepo_256/.gitted/objects/b2/1c8c27a05a3f0bf9f0f44ebf05e11d9c591b04cfdaff7cc860310356d71827
new file mode 100644
index 0000000..c6da2ff
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/b2/1c8c27a05a3f0bf9f0f44ebf05e11d9c591b04cfdaff7cc860310356d71827
@@ -0,0 +1 @@
+xuQj1 DûíSè)²WëÕB)д°e%^¨×Åqèõë´ýÍß0Ì{0RKÙ:8vO½©‚å³µleåà–e1ršEO³LŽ0aò8?Ú™¯ÐtBŠäBÒàÉG'Q**MYÉŸ'áhY…íL¸õ\¼KíN9HÝáå*¿áx)aû|–Z^Áº…ØN¼pA4£º6xKß¡%øȵ\ï´ã=/[Ï·øÏ“÷¼zD7x¼‰-ì’áÏuWó¯W \ No newline at end of file
diff --git a/tests/resources/testrepo_256/.gitted/objects/b6/1b940a8cd979a32e005682c5c09c22053675e2db24ea6b4b28cc75e9c10890 b/tests/resources/testrepo_256/.gitted/objects/b6/1b940a8cd979a32e005682c5c09c22053675e2db24ea6b4b28cc75e9c10890
new file mode 100644
index 0000000..b1df3bd
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/b6/1b940a8cd979a32e005682c5c09c22053675e2db24ea6b4b28cc75e9c10890
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/b8/3624f6ac0995273c0034a7ab8c68929bdc91b69ad54ef94979b93eba3f6022 b/tests/resources/testrepo_256/.gitted/objects/b8/3624f6ac0995273c0034a7ab8c68929bdc91b69ad54ef94979b93eba3f6022
new file mode 100644
index 0000000..3e36331
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/b8/3624f6ac0995273c0034a7ab8c68929bdc91b69ad54ef94979b93eba3f6022
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/bd/f2066a28e11603a1af04157ee4aad97814279fe500340eb3465797cbd3be23 b/tests/resources/testrepo_256/.gitted/objects/bd/f2066a28e11603a1af04157ee4aad97814279fe500340eb3465797cbd3be23
new file mode 100644
index 0000000..9bb5b62
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/bd/f2066a28e11603a1af04157ee4aad97814279fe500340eb3465797cbd3be23
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/bf/a3b3b9a161d354e2254a444b12c412210e9689c17e51bfc318ce4bb4360f19 b/tests/resources/testrepo_256/.gitted/objects/bf/a3b3b9a161d354e2254a444b12c412210e9689c17e51bfc318ce4bb4360f19
new file mode 100644
index 0000000..3cbf7e6
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/bf/a3b3b9a161d354e2254a444b12c412210e9689c17e51bfc318ce4bb4360f19
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/bf/cc4074ac517ed24d61b0aaa96359f304c3dc97e95f336269ed474ea846ada5 b/tests/resources/testrepo_256/.gitted/objects/bf/cc4074ac517ed24d61b0aaa96359f304c3dc97e95f336269ed474ea846ada5
new file mode 100644
index 0000000..be8b99b
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/bf/cc4074ac517ed24d61b0aaa96359f304c3dc97e95f336269ed474ea846ada5
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/c2/58f010a08328a29cde33411d955520e0375fcbbcc14b7636a70f7536c32ef6 b/tests/resources/testrepo_256/.gitted/objects/c2/58f010a08328a29cde33411d955520e0375fcbbcc14b7636a70f7536c32ef6
new file mode 100644
index 0000000..9d2ceb1
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/c2/58f010a08328a29cde33411d955520e0375fcbbcc14b7636a70f7536c32ef6
@@ -0,0 +1,2 @@
+xŒÁ‚0D=÷+önbvK[JbŒ?àÍxßÖ¥¢Öƒ/™I^æð” P»1=%+X’5IœO6“´uà =V•“*pDòì]h±v˜„G/„÷HÜ,o £¿€r1Ë@eÖŠLpë²
+ž´ƒ£ò0¾¾çÒs÷>ä±?ÙH¾ŽÎ!ìÑ"s}t3,åͳIÿmø3‘ \ No newline at end of file
diff --git a/tests/resources/testrepo_256/.gitted/objects/ca/31f7336e882a233a2943787c5e94ba024ac9a4f763cb1d9bfd8e63aa7f7269 b/tests/resources/testrepo_256/.gitted/objects/ca/31f7336e882a233a2943787c5e94ba024ac9a4f763cb1d9bfd8e63aa7f7269
new file mode 100644
index 0000000..cfcdac3
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/ca/31f7336e882a233a2943787c5e94ba024ac9a4f763cb1d9bfd8e63aa7f7269
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/cb/282e7c15fd8aeb2265cd621f5a228cb33dc84192980ca426cf9ab2a48cb9f0 b/tests/resources/testrepo_256/.gitted/objects/cb/282e7c15fd8aeb2265cd621f5a228cb33dc84192980ca426cf9ab2a48cb9f0
new file mode 100644
index 0000000..77d9ec2
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/cb/282e7c15fd8aeb2265cd621f5a228cb33dc84192980ca426cf9ab2a48cb9f0
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/cc/b5a03da85607c230d111abfa899655d1b00e6529101a40d42f6acb059dff9f b/tests/resources/testrepo_256/.gitted/objects/cc/b5a03da85607c230d111abfa899655d1b00e6529101a40d42f6acb059dff9f
new file mode 100644
index 0000000..a67d6e6
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/cc/b5a03da85607c230d111abfa899655d1b00e6529101a40d42f6acb059dff9f
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/cf/84e5be57f8d5d51f136d3d137b654c602721c469c1b0a58e7e95647a9cf1c0 b/tests/resources/testrepo_256/.gitted/objects/cf/84e5be57f8d5d51f136d3d137b654c602721c469c1b0a58e7e95647a9cf1c0
new file mode 100644
index 0000000..ec37060
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/cf/84e5be57f8d5d51f136d3d137b654c602721c469c1b0a58e7e95647a9cf1c0
@@ -0,0 +1,3 @@
+x•ŽA
+Â0E]ç³ëBÌ$© ˆx¯ ™dª…ÆHŒ ·7xWÞãÃ%祑޴*<Æ}À“qVˆœ ÖZFŠ‰P‹'q/yŽ§(–ÙšQÏèUxµ[©Ö%
+~8É;äÇ*»XòИ‰¼vä`«QkÕm¿Ð䯑:—&O)IþÀp]ÚåÞÝ%V M˜kÉ°.Ü©/××Eÿ \ No newline at end of file
diff --git a/tests/resources/testrepo_256/.gitted/objects/d8/8b60d2641df3656381dc8e201abb820a414de03eb63c065b06a2ab37d3f5ca b/tests/resources/testrepo_256/.gitted/objects/d8/8b60d2641df3656381dc8e201abb820a414de03eb63c065b06a2ab37d3f5ca
new file mode 100644
index 0000000..6845087
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/d8/8b60d2641df3656381dc8e201abb820a414de03eb63c065b06a2ab37d3f5ca
@@ -0,0 +1 @@
+x5ÌÁjÃ0 €áýêyP”ÈŽ(£ÐëÖW²#‡´q4Rõ°·_/=þ|ðÏÐôǦ›d"ñMH=î"¦2åØ¡FOÄsšÚÉû.¥ëK;â%bãsi¹wö÷+VMÎ^gÞ656™ÆW¦ã[fÙa{®«é]68I•}ÉÇ"{åÇy®¼¬Ç¬õ "Š}ð‰-¢sߪwøá\.Zëb÷Ø=B \ No newline at end of file
diff --git a/tests/resources/testrepo_256/.gitted/objects/de/caff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f b/tests/resources/testrepo_256/.gitted/objects/de/caff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f
new file mode 100644
index 0000000..a53ab84
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/de/caff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/eb/ead5965196dfaeab52b1a5d92b78e54493fdaa78f72268d4cc69b61d5feee1 b/tests/resources/testrepo_256/.gitted/objects/eb/ead5965196dfaeab52b1a5d92b78e54493fdaa78f72268d4cc69b61d5feee1
new file mode 100644
index 0000000..225c457
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/eb/ead5965196dfaeab52b1a5d92b78e54493fdaa78f72268d4cc69b61d5feee1
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/f2/a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736 b/tests/resources/testrepo_256/.gitted/objects/f2/a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736
new file mode 100644
index 0000000..58d51e5
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/f2/a108f86a3b4fd9ad75ed55e9cb3cb46e348fca3b9dba3db64f7c9f64b8a736
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/f2/c8da1a7c2eb49ff25c47441f0b3f387faeddde1b37d0ad2f3f6a63f5327978 b/tests/resources/testrepo_256/.gitted/objects/f2/c8da1a7c2eb49ff25c47441f0b3f387faeddde1b37d0ad2f3f6a63f5327978
new file mode 100644
index 0000000..04bf5eb
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/f2/c8da1a7c2eb49ff25c47441f0b3f387faeddde1b37d0ad2f3f6a63f5327978
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/f3/1459efb9367c5a19c9dd24c75107423d5773066922ea5e55eaeb6490979562 b/tests/resources/testrepo_256/.gitted/objects/f3/1459efb9367c5a19c9dd24c75107423d5773066922ea5e55eaeb6490979562
new file mode 100644
index 0000000..37a289e
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/f3/1459efb9367c5a19c9dd24c75107423d5773066922ea5e55eaeb6490979562
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/pack/pack-b87f1f214098b19ce092afb9ef6e7643653c03e7f91faa27b767e3eb8225f0f6.idx b/tests/resources/testrepo_256/.gitted/objects/pack/pack-b87f1f214098b19ce092afb9ef6e7643653c03e7f91faa27b767e3eb8225f0f6.idx
new file mode 100644
index 0000000..897e8a4
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/pack/pack-b87f1f214098b19ce092afb9ef6e7643653c03e7f91faa27b767e3eb8225f0f6.idx
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/pack/pack-b87f1f214098b19ce092afb9ef6e7643653c03e7f91faa27b767e3eb8225f0f6.pack b/tests/resources/testrepo_256/.gitted/objects/pack/pack-b87f1f214098b19ce092afb9ef6e7643653c03e7f91faa27b767e3eb8225f0f6.pack
new file mode 100644
index 0000000..9c85578
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/pack/pack-b87f1f214098b19ce092afb9ef6e7643653c03e7f91faa27b767e3eb8225f0f6.pack
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/pack/pack-e2f07f30db7e480ea84a0e64ee791b9b270067124b2609019b74f33f256f33fa.idx b/tests/resources/testrepo_256/.gitted/objects/pack/pack-e2f07f30db7e480ea84a0e64ee791b9b270067124b2609019b74f33f256f33fa.idx
new file mode 100644
index 0000000..9e2ec99
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/pack/pack-e2f07f30db7e480ea84a0e64ee791b9b270067124b2609019b74f33f256f33fa.idx
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/pack/pack-e2f07f30db7e480ea84a0e64ee791b9b270067124b2609019b74f33f256f33fa.pack b/tests/resources/testrepo_256/.gitted/objects/pack/pack-e2f07f30db7e480ea84a0e64ee791b9b270067124b2609019b74f33f256f33fa.pack
new file mode 100644
index 0000000..66cd292
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/pack/pack-e2f07f30db7e480ea84a0e64ee791b9b270067124b2609019b74f33f256f33fa.pack
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/pack/pack-f72bbfa35af982c2a60735152c80b24ee981cf102db76764c383f9b87935d0d3.idx b/tests/resources/testrepo_256/.gitted/objects/pack/pack-f72bbfa35af982c2a60735152c80b24ee981cf102db76764c383f9b87935d0d3.idx
new file mode 100644
index 0000000..1d197e8
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/pack/pack-f72bbfa35af982c2a60735152c80b24ee981cf102db76764c383f9b87935d0d3.idx
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/objects/pack/pack-f72bbfa35af982c2a60735152c80b24ee981cf102db76764c383f9b87935d0d3.pack b/tests/resources/testrepo_256/.gitted/objects/pack/pack-f72bbfa35af982c2a60735152c80b24ee981cf102db76764c383f9b87935d0d3.pack
new file mode 100644
index 0000000..5b615e1
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/objects/pack/pack-f72bbfa35af982c2a60735152c80b24ee981cf102db76764c383f9b87935d0d3.pack
Binary files differ
diff --git a/tests/resources/testrepo_256/.gitted/packed-refs b/tests/resources/testrepo_256/.gitted/packed-refs
new file mode 100644
index 0000000..75626ad
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/packed-refs
@@ -0,0 +1,27 @@
+# pack-refs with: peeled fully-peeled sorted
+a4813ef6708e6011e8187224297e83e4a285f58bf5eabb1db270351388603c95 refs/remotes/origin/br2
+a4813ef6708e6011e8187224297e83e4a285f58bf5eabb1db270351388603c95 refs/remotes/origin/cannot-fetch
+4d46d9719e425ef2dfb5bfba098d0b62e21b2b92d0731892eef70db0870e3744 refs/remotes/origin/chomped
+7e9424c06052ca33bfc599bccadee60065d8664a9af7648a1455100c4f772e1c refs/remotes/origin/haacked
+decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f refs/remotes/origin/master
+decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f refs/remotes/origin/not-good
+66fe8385c6378bfa5ca5573bd0fdd773e4eadb0e86416b483f2c50c839859ecb refs/remotes/origin/packed
+43e084a4599ca42c476919917e3db8fde0045ee66305fd5e634b0c793c536a1b refs/remotes/origin/packed-test
+0118010feb81fe41b9df646d13866742a9070b56fd0ba9ab8dff828fc36c1f78 refs/remotes/origin/subtrees
+4d46d9719e425ef2dfb5bfba098d0b62e21b2b92d0731892eef70db0870e3744 refs/remotes/origin/test
+b83624f6ac0995273c0034a7ab8c68929bdc91b69ad54ef94979b93eba3f6022 refs/remotes/origin/track-local
+4d46d9719e425ef2dfb5bfba098d0b62e21b2b92d0731892eef70db0870e3744 refs/remotes/origin/trailing
+7e4633ae1b0e83503dbea4417f9d5ccaf22b877c5a4522b6d1d2b16090ee2f6f refs/remotes/origin/with-empty-log
+d88b60d2641df3656381dc8e201abb820a414de03eb63c065b06a2ab37d3f5ca refs/tags/annotated_tag_to_blob
+^33e415b835a670bb5c3c760efa0433ac0cbd2d44679f68f2df3a9ae7014cf2a8
+21e1e1ebe45b2c1ef79ab050334e36a8015a546f0740bea4505e10d81a946f61 refs/tags/e90810b
+^4d46d9719e425ef2dfb5bfba098d0b62e21b2b92d0731892eef70db0870e3744
+34f79ad1c813b93d2ee11c830c2134815a31d9629e6aa9773338fedaab90976b refs/tags/hard_tag
+^decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f
+33e415b835a670bb5c3c760efa0433ac0cbd2d44679f68f2df3a9ae7014cf2a8 refs/tags/point_to_blob
+14bd335f9d7188c778d44eba8801fe9bda46b66593291f5b9f7cd5f8888af12f refs/tags/taggerless
+^4d46d9719e425ef2dfb5bfba098d0b62e21b2b92d0731892eef70db0870e3744
+c258f010a08328a29cde33411d955520e0375fcbbcc14b7636a70f7536c32ef6 refs/tags/test
+^4d46d9719e425ef2dfb5bfba098d0b62e21b2b92d0731892eef70db0870e3744
+34f79ad1c813b93d2ee11c830c2134815a31d9629e6aa9773338fedaab90976b refs/tags/wrapped_tag
+^decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f
diff --git a/tests/resources/testrepo_256/.gitted/refs/heads/master b/tests/resources/testrepo_256/.gitted/refs/heads/master
new file mode 100644
index 0000000..106231c
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/refs/heads/master
@@ -0,0 +1 @@
+decaff3051968d1f3a2defd3d4a80ced03101555e1fd8913b3544026c0717d4f
diff --git a/tests/resources/testrepo_256/.gitted/refs/remotes/origin/HEAD b/tests/resources/testrepo_256/.gitted/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..6efe28f
--- /dev/null
+++ b/tests/resources/testrepo_256/.gitted/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/testrepo_256/README b/tests/resources/testrepo_256/README
new file mode 100644
index 0000000..a823312
--- /dev/null
+++ b/tests/resources/testrepo_256/README
@@ -0,0 +1 @@
+hey there
diff --git a/tests/resources/testrepo_256/branch_file.txt b/tests/resources/testrepo_256/branch_file.txt
new file mode 100644
index 0000000..3697d64
--- /dev/null
+++ b/tests/resources/testrepo_256/branch_file.txt
@@ -0,0 +1,2 @@
+hi
+bye!
diff --git a/tests/resources/testrepo_256/new.txt b/tests/resources/testrepo_256/new.txt
new file mode 100644
index 0000000..a71586c
--- /dev/null
+++ b/tests/resources/testrepo_256/new.txt
@@ -0,0 +1 @@
+my new file
diff --git a/tests/resources/twowaymerge.git/HEAD b/tests/resources/twowaymerge.git/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/twowaymerge.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/twowaymerge.git/config b/tests/resources/twowaymerge.git/config
new file mode 100644
index 0000000..c53d818
--- /dev/null
+++ b/tests/resources/twowaymerge.git/config
@@ -0,0 +1,5 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = true
+ ignorecase = true
diff --git a/tests/resources/twowaymerge.git/description b/tests/resources/twowaymerge.git/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/twowaymerge.git/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/twowaymerge.git/info/exclude b/tests/resources/twowaymerge.git/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/twowaymerge.git/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/twowaymerge.git/objects/0c/8a3f1f3d5f421cf83048c7c73ee3b55a5e0f29 b/tests/resources/twowaymerge.git/objects/0c/8a3f1f3d5f421cf83048c7c73ee3b55a5e0f29
new file mode 100644
index 0000000..12698af
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/0c/8a3f1f3d5f421cf83048c7c73ee3b55a5e0f29
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/10/2dce8e3081f398e4bdd9fd894dc85ac3ca6a67 b/tests/resources/twowaymerge.git/objects/10/2dce8e3081f398e4bdd9fd894dc85ac3ca6a67
new file mode 100644
index 0000000..3806ee7
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/10/2dce8e3081f398e4bdd9fd894dc85ac3ca6a67
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/17/7d8634a28e26ec7819284752757ebe01a479d5 b/tests/resources/twowaymerge.git/objects/17/7d8634a28e26ec7819284752757ebe01a479d5
new file mode 100644
index 0000000..e91e06d
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/17/7d8634a28e26ec7819284752757ebe01a479d5
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/1c/30b88f5f3ee66d78df6520a7de9e89b890818b b/tests/resources/twowaymerge.git/objects/1c/30b88f5f3ee66d78df6520a7de9e89b890818b
new file mode 100644
index 0000000..57d1a0f
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/1c/30b88f5f3ee66d78df6520a7de9e89b890818b
@@ -0,0 +1,3 @@
+xŽM
+Â0F]çsËÌä§ ˆ¸Üz‚Iš¶Ši¤¦÷·^Áå{<>¾TKy4`6‡¶æ š,y’9j§GJì8ÁÇÞb¢‘\Œfõ–5/ Ç^‰8v¹'ö‚ÙËœì`SÆ%[›ë
+÷T[ƒ[×úŠ,púüÌsºL6o±Kµœ´5Ø;ŽèÕn÷›-ÿ= ²úÿDà \ No newline at end of file
diff --git a/tests/resources/twowaymerge.git/objects/1f/4c0311a24b63f6fc209a59a1e404942d4a5006 b/tests/resources/twowaymerge.git/objects/1f/4c0311a24b63f6fc209a59a1e404942d4a5006
new file mode 100644
index 0000000..99288fd
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/1f/4c0311a24b63f6fc209a59a1e404942d4a5006
@@ -0,0 +1,2 @@
+xÍ=Â0 @aæœÂ ²Mê6BlH¬œ ¿m!RqïO¹ë7¼[­‹ rÐ5g°N’Xƒ‹Å±)Eg]ÏDY2c R8xã7Û
+ØTáÞÁ­½Rõo8~òœ®Ó¢óºØêèÔ[”™àˆ#¢Ùußjþ;`¼ùÔÙ7ó \ No newline at end of file
diff --git a/tests/resources/twowaymerge.git/objects/22/24e191514cb4bd8c566d80dac22dfcb1e9bb83 b/tests/resources/twowaymerge.git/objects/22/24e191514cb4bd8c566d80dac22dfcb1e9bb83
new file mode 100644
index 0000000..48466ea
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/22/24e191514cb4bd8c566d80dac22dfcb1e9bb83
@@ -0,0 +1,3 @@
+xŽK
+Â0@]çsK&¤ ˆ¸Üz‚™4éÛHMïo½‚Û÷àñbY–©‚1tª[JàbŽlɈµâ¥í4vÉ¡±Lâ³ ì'—Õ›·´V`B¦û .
+ëÎIöm ï1õZ¨Ç x¯cÙàK­ðhà^^ýÂ+\>?2·aªã.M,Ë°µtTB‹pÖ^kuÐc³¦¿jV_«sFh \ No newline at end of file
diff --git a/tests/resources/twowaymerge.git/objects/29/6e56023cdc034d2735fee8c0d85a659d1b07f4 b/tests/resources/twowaymerge.git/objects/29/6e56023cdc034d2735fee8c0d85a659d1b07f4
new file mode 100644
index 0000000..aa3fccd
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/29/6e56023cdc034d2735fee8c0d85a659d1b07f4
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/31/51880ae2b363f1c262cf98b750c1f169a0d432 b/tests/resources/twowaymerge.git/objects/31/51880ae2b363f1c262cf98b750c1f169a0d432
new file mode 100644
index 0000000..235d42b
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/31/51880ae2b363f1c262cf98b750c1f169a0d432
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/3b/287f8730c81d0b763c2d294618a5e32b67b4f8 b/tests/resources/twowaymerge.git/objects/3b/287f8730c81d0b763c2d294618a5e32b67b4f8
new file mode 100644
index 0000000..56ddac5
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/3b/287f8730c81d0b763c2d294618a5e32b67b4f8
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/42/b7311aa626e712891940c1ec5d5cba201946a4 b/tests/resources/twowaymerge.git/objects/42/b7311aa626e712891940c1ec5d5cba201946a4
new file mode 100644
index 0000000..a8e6581
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/42/b7311aa626e712891940c1ec5d5cba201946a4
@@ -0,0 +1,3 @@
+xν Â0@ajOq cßù"!D‡DËþKÂ1
+Îþ„h_ñéÅZÊ£AßÛC[s†aÌÈžp´I³±‰#‹„lB…œqÈêí×¼4ðZ"¡Ç(œyGF¢¢ød#y«ò[›ë
+÷X[ƒ[×úJÅ/púüÊsºL6o¡‹µœA²èX„ሂ¨öºo¶ü7 ’úÔ¸Ec \ No newline at end of file
diff --git a/tests/resources/twowaymerge.git/objects/49/6d6428b9cf92981dc9495211e6e1120fb6f2ba b/tests/resources/twowaymerge.git/objects/49/6d6428b9cf92981dc9495211e6e1120fb6f2ba
new file mode 100644
index 0000000..978bc34
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/49/6d6428b9cf92981dc9495211e6e1120fb6f2ba
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/59/b0cf7d74659e1cdb13305319d6d4ce2733c118 b/tests/resources/twowaymerge.git/objects/59/b0cf7d74659e1cdb13305319d6d4ce2733c118
new file mode 100644
index 0000000..30b507c
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/59/b0cf7d74659e1cdb13305319d6d4ce2733c118
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/6a/b5d28acbf3c3bdff276f7ccfdf29c1520e542f b/tests/resources/twowaymerge.git/objects/6a/b5d28acbf3c3bdff276f7ccfdf29c1520e542f
new file mode 100644
index 0000000..ff6a386
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/6a/b5d28acbf3c3bdff276f7ccfdf29c1520e542f
@@ -0,0 +1 @@
+xÎM‚0@a×=Å\@2ýcÜ™¸õeÚ†Rƒåþâܾŗǵ”¹RæÔ¶”@¢Šœ(i$™uOÉ 1ö9Ro"“ ¬9¸à¼x‡-­ ü@¬µcc3;ê-KvHÊ+‡9ÙèÁFe¼{›êO®­Á£ƒ{]b +\>¿òoãܦ}踖+Hm zšàŒ„(Žzl¶ô7 ñ•œF- \ No newline at end of file
diff --git a/tests/resources/twowaymerge.git/objects/6c/fca542b55b8b37017e6125a4b8f59a6eae6f11 b/tests/resources/twowaymerge.git/objects/6c/fca542b55b8b37017e6125a4b8f59a6eae6f11
new file mode 100644
index 0000000..9a969a2
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/6c/fca542b55b8b37017e6125a4b8f59a6eae6f11
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/76/5b32c65d38f04c4f287abda055818ec0f26912 b/tests/resources/twowaymerge.git/objects/76/5b32c65d38f04c4f287abda055818ec0f26912
new file mode 100644
index 0000000..493bbc0
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/76/5b32c65d38f04c4f287abda055818ec0f26912
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/7b/8c336c45fc6895c1c60827260fe5d798e5d247 b/tests/resources/twowaymerge.git/objects/7b/8c336c45fc6895c1c60827260fe5d798e5d247
new file mode 100644
index 0000000..19e7ef4
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/7b/8c336c45fc6895c1c60827260fe5d798e5d247
@@ -0,0 +1,3 @@
+xÎA @Qלb.`3 S(‰1îLÜz‚B[µÅTzëܾÅÏež§
+D|¨kJCß3f‰´íȵœ‚uÙ ›L>YGMÌV½eMK9¢ÑZˆƒ5ÙæHè¥õ¢#{¦ž¥E´J¶:–î±Ô
+·®åÕϲÀéó“Çp¦:n¡‰e>ƒ6-£sH GìÕ®ûfMÔS}ZE² \ No newline at end of file
diff --git a/tests/resources/twowaymerge.git/objects/82/bf9a1a10a4b25c1f14c9607b60970705e92545 b/tests/resources/twowaymerge.git/objects/82/bf9a1a10a4b25c1f14c9607b60970705e92545
new file mode 100644
index 0000000..89b0b9f
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/82/bf9a1a10a4b25c1f14c9607b60970705e92545
@@ -0,0 +1 @@
+xŽË Â09»Šm€hã_ÖBܸRן„cœþ1-p§yšPKy4RÚ–D›GŒF»ÀJvÉFE>‡1#q²Ž j§ÅÛoimbvSŽŠYSbEr²Š¸»Q"eÓÑ{+üÞ–ºÁ=ÔÖà6Àµ¾bñ+œ>?òœ/ó£-;¡–3ŒÊhœ¬C‚#¢è´g¶ô÷XÄÌyF¤ \ No newline at end of file
diff --git a/tests/resources/twowaymerge.git/objects/8b/82fb1794cb1c8c7f172ec730a4c2db0ae3e650 b/tests/resources/twowaymerge.git/objects/8b/82fb1794cb1c8c7f172ec730a4c2db0ae3e650
new file mode 100644
index 0000000..8e9b758
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/8b/82fb1794cb1c8c7f172ec730a4c2db0ae3e650
@@ -0,0 +1,3 @@
+xν Â0@ajOq ù7BtH´Lp¾ó%A8FÁÙŸ°íW<=ª¥Ì ¬õ‡¶æ x"ÊŽØ$—%1†dÄcÏDNLˆ:Yv=©7®yiÐc
+l¤$Ž\b{‰DÂbOd‚Õ9x+
+·6ÕT[ƒ{·úâ‚ œ??yŽ×qnÓ–:ªåƯcÔÞÁQZ«]÷Í–ÿ¨¢¾7 H† \ No newline at end of file
diff --git a/tests/resources/twowaymerge.git/objects/9a/40a2f11c191f180c47e54b11567cb3c1e89b30 b/tests/resources/twowaymerge.git/objects/9a/40a2f11c191f180c47e54b11567cb3c1e89b30
new file mode 100644
index 0000000..1de1224
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/9a/40a2f11c191f180c47e54b11567cb3c1e89b30
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/9b/219343610c88a1187c996d0dc58330b55cee28 b/tests/resources/twowaymerge.git/objects/9b/219343610c88a1187c996d0dc58330b55cee28
new file mode 100644
index 0000000..8b64b43
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/9b/219343610c88a1187c996d0dc58330b55cee28
@@ -0,0 +1,2 @@
+xÏKj1Ьç½óÊFj}Z‚¼3²Ê ¤VÏÇdFA#ß?’\ ËzÅu]—FÓSo" ‰JðÆ& ‚^˜‚Ž,9$G’Eéd)7|¦&[6”(FU"&Žh< ¯FÉc4AÆ¿>"ZÑQ;m9Û\;ïKP%1b9k‰93¤GŸkƒw®½Ãënõ£¬iƒçý[îÓuZúüÈ®ë hã¬"RÞÂY¥†C[]þ=0¼I›rKÏp—¶÷óO:Á²õ
+»pÝʯ _¾(c‡ \ No newline at end of file
diff --git a/tests/resources/twowaymerge.git/objects/9f/e06a50f4d1634d6c6879854d01d80857388706 b/tests/resources/twowaymerge.git/objects/9f/e06a50f4d1634d6c6879854d01d80857388706
new file mode 100644
index 0000000..055de01
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/9f/e06a50f4d1634d6c6879854d01d80857388706
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/a4/1a49f8f5cd9b6cb14a076bf8394881ed0b4d19 b/tests/resources/twowaymerge.git/objects/a4/1a49f8f5cd9b6cb14a076bf8394881ed0b4d19
new file mode 100644
index 0000000..cb4d34e
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/a4/1a49f8f5cd9b6cb14a076bf8394881ed0b4d19
@@ -0,0 +1,3 @@
+xν Â0@ajOq Ýù7'!D‡DËöÙ A$FÁÙŸ°íW<=©ó<5ÐÚÚZ
+8N(CÈÁzÇ…$'2!Î>[):#D½zǵ, zŽ £MÚ d…=†ä‘t…µ³NÅ­=ê
+w©­Á­ƒk}å9.púüä9^Æ©=¶ÔIÏ@ÆY ž‰áˆ=¢ÚußlåÔ°Dâ \ No newline at end of file
diff --git a/tests/resources/twowaymerge.git/objects/a9/53a018c5b10b20c86e69fef55ebc8ad4c5a417 b/tests/resources/twowaymerge.git/objects/a9/53a018c5b10b20c86e69fef55ebc8ad4c5a417
new file mode 100644
index 0000000..8235f18
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/a9/53a018c5b10b20c86e69fef55ebc8ad4c5a417
@@ -0,0 +1 @@
+xÍJÄ0…]÷)înV3$¹is"îÁ•Oû“NŶ’ɼ¿ñ\žï|8²¯ëÒ!dzèÍ ªÔdXG/ޫϹp*‰¢C³X³ˆº@ZÂ8|•f[VŸ0HD™H“E]6¯”g¶I#g«*ñÏ­9UEæHÆH!MḦÕñh‚ºR¦¡Üûuoð.{ïðz—ýSײÁãí‡|ÌÏóÒ¯w¾È¾>Ç1º4‘C8;rn8èq«Û¿†7k³·²ÉNui·~þM§áÜ^­ \ No newline at end of file
diff --git a/tests/resources/twowaymerge.git/objects/a9/cce3cd1b3efbda5b1f4a6dcc3f1570b2d3d74c b/tests/resources/twowaymerge.git/objects/a9/cce3cd1b3efbda5b1f4a6dcc3f1570b2d3d74c
new file mode 100644
index 0000000..4da7e82
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/a9/cce3cd1b3efbda5b1f4a6dcc3f1570b2d3d74c
@@ -0,0 +1 @@
+x+)JMU044c040031QHdx6÷ÑìM¯9{wk®+ºqèIOðD¨d6>É|’¹X%>½9j \ No newline at end of file
diff --git a/tests/resources/twowaymerge.git/objects/bd/1732c43c68d712ad09e1d872b9be6d4b9efdc4 b/tests/resources/twowaymerge.git/objects/bd/1732c43c68d712ad09e1d872b9be6d4b9efdc4
new file mode 100644
index 0000000..b9b6012
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/bd/1732c43c68d712ad09e1d872b9be6d4b9efdc4
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/c3/7a783c20d92ac92362a78a32860f7eebf938ef b/tests/resources/twowaymerge.git/objects/c3/7a783c20d92ac92362a78a32860f7eebf938ef
new file mode 100644
index 0000000..041e890
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/c3/7a783c20d92ac92362a78a32860f7eebf938ef
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/cb/dd40facab1682754eb67f7a43f29e672903cf6 b/tests/resources/twowaymerge.git/objects/cb/dd40facab1682754eb67f7a43f29e672903cf6
new file mode 100644
index 0000000..ccb156d
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/cb/dd40facab1682754eb67f7a43f29e672903cf6
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/cd/f97fd3bb48eb3827638bb33d208f5fd32d0aa6 b/tests/resources/twowaymerge.git/objects/cd/f97fd3bb48eb3827638bb33d208f5fd32d0aa6
new file mode 100644
index 0000000..0e028dc
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/cd/f97fd3bb48eb3827638bb33d208f5fd32d0aa6
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/d6/f10d549cb335b9e6d38afc1f0088be69b50494 b/tests/resources/twowaymerge.git/objects/d6/f10d549cb335b9e6d38afc1f0088be69b50494
new file mode 100644
index 0000000..b298c52
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/d6/f10d549cb335b9e6d38afc1f0088be69b50494
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/d9/acdc7ae7632adfeec67fa73c1e343cf4d1f47e b/tests/resources/twowaymerge.git/objects/d9/acdc7ae7632adfeec67fa73c1e343cf4d1f47e
new file mode 100644
index 0000000..de94528
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/d9/acdc7ae7632adfeec67fa73c1e343cf4d1f47e
@@ -0,0 +1 @@
+x+)JMU044c040031QHdx6÷ÑìM¯9{wk®+ºqèIOðD¨d>É4|’éX%:79U \ No newline at end of file
diff --git a/tests/resources/twowaymerge.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/twowaymerge.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644
index 0000000..7112238
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests/resources/twowaymerge.git/objects/ef/0488f0b722f0be8bcb90a7730ac7efafd1d694 b/tests/resources/twowaymerge.git/objects/ef/0488f0b722f0be8bcb90a7730ac7efafd1d694
new file mode 100644
index 0000000..00f7d36
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/ef/0488f0b722f0be8bcb90a7730ac7efafd1d694
@@ -0,0 +1 @@
+xŽM F]sŠ¹€Í0Ã_cÜ™¸õ@¡ÕH1•Þ_¼‚Û—ï}y±–òh@¤mK 8³ÙYÆèä„ÁŽ4ѨŒt^'¦`lPÙ‰·ßÒÚ Ï<g™yÒY‘ŒÙ1*m´œ­»„™Fá÷¶Ô ·®õ5¿Âéó#Ïù2?Ú²‡!ÖrÉZ¡5ÆŽpD‡(:í™-ý} ²ø#E¸ \ No newline at end of file
diff --git a/tests/resources/twowaymerge.git/objects/fc/f7e3f51c11d199ab7a78403ee4f9ccd028da25 b/tests/resources/twowaymerge.git/objects/fc/f7e3f51c11d199ab7a78403ee4f9ccd028da25
new file mode 100644
index 0000000..54989ea
--- /dev/null
+++ b/tests/resources/twowaymerge.git/objects/fc/f7e3f51c11d199ab7a78403ee4f9ccd028da25
Binary files differ
diff --git a/tests/resources/twowaymerge.git/refs/heads/first-branch b/tests/resources/twowaymerge.git/refs/heads/first-branch
new file mode 100644
index 0000000..ef0dead
--- /dev/null
+++ b/tests/resources/twowaymerge.git/refs/heads/first-branch
@@ -0,0 +1 @@
+2224e191514cb4bd8c566d80dac22dfcb1e9bb83
diff --git a/tests/resources/twowaymerge.git/refs/heads/master b/tests/resources/twowaymerge.git/refs/heads/master
new file mode 100644
index 0000000..ebf18f5
--- /dev/null
+++ b/tests/resources/twowaymerge.git/refs/heads/master
@@ -0,0 +1 @@
+1c30b88f5f3ee66d78df6520a7de9e89b890818b
diff --git a/tests/resources/twowaymerge.git/refs/heads/second-branch b/tests/resources/twowaymerge.git/refs/heads/second-branch
new file mode 100644
index 0000000..586a14a
--- /dev/null
+++ b/tests/resources/twowaymerge.git/refs/heads/second-branch
@@ -0,0 +1 @@
+9b219343610c88a1187c996d0dc58330b55cee28
diff --git a/tests/resources/typechanges/.gitted/HEAD b/tests/resources/typechanges/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/typechanges/.gitted/config b/tests/resources/typechanges/.gitted/config
new file mode 100644
index 0000000..4cc6e1d
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/config
@@ -0,0 +1,12 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
+[submodule "e"]
+ url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target/.git
+[submodule "d"]
+ url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target/.git
+[submodule "b"]
+ url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target/.git
diff --git a/tests/resources/typechanges/.gitted/description b/tests/resources/typechanges/.gitted/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/typechanges/.gitted/index b/tests/resources/typechanges/.gitted/index
new file mode 100644
index 0000000..4f6d12a
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/index
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/info/exclude b/tests/resources/typechanges/.gitted/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/typechanges/.gitted/modules/b/HEAD b/tests/resources/typechanges/.gitted/modules/b/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/typechanges/.gitted/modules/b/config b/tests/resources/typechanges/.gitted/modules/b/config
new file mode 100644
index 0000000..f57cd4a
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/config
@@ -0,0 +1,13 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../b
+ ignorecase = true
+[remote "origin"]
+ fetch = +refs/heads/*:refs/remotes/origin/*
+ url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target/.git
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/typechanges/.gitted/modules/b/description b/tests/resources/typechanges/.gitted/modules/b/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/typechanges/.gitted/modules/b/index b/tests/resources/typechanges/.gitted/modules/b/index
new file mode 100644
index 0000000..c16a026
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/index
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/b/info/exclude b/tests/resources/typechanges/.gitted/modules/b/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/typechanges/.gitted/modules/b/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/typechanges/.gitted/modules/b/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 0000000..f4b7094
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/b/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/typechanges/.gitted/modules/b/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 0000000..56c845e
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/b/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/typechanges/.gitted/modules/b/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 0000000..bd179b5
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/b/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/typechanges/.gitted/modules/b/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 0000000..ccf49bd
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/b/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/typechanges/.gitted/modules/b/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 0000000..5302906
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/b/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/typechanges/.gitted/modules/b/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 0000000..38c791e
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/b/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/typechanges/.gitted/modules/b/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 0000000..a26d299
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests/resources/typechanges/.gitted/modules/b/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/typechanges/.gitted/modules/b/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 0000000..83d1ba4
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/b/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/typechanges/.gitted/modules/b/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 0000000..6d27af8
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests/resources/typechanges/.gitted/modules/b/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/typechanges/.gitted/modules/b/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 0000000..1745884
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests/resources/typechanges/.gitted/modules/b/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/typechanges/.gitted/modules/b/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 0000000..83cc29f
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/b/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/typechanges/.gitted/modules/b/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 0000000..55bda40
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/b/packed-refs b/tests/resources/typechanges/.gitted/modules/b/packed-refs
new file mode 100644
index 0000000..5a4ebc4
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
diff --git a/tests/resources/typechanges/.gitted/modules/b/refs/heads/master b/tests/resources/typechanges/.gitted/modules/b/refs/heads/master
new file mode 100644
index 0000000..e12c44d
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/refs/heads/master
@@ -0,0 +1 @@
+480095882d281ed676fe5b863569520e54a7d5c0
diff --git a/tests/resources/typechanges/.gitted/modules/b/refs/remotes/origin/HEAD b/tests/resources/typechanges/.gitted/modules/b/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..6efe28f
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/b/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/typechanges/.gitted/modules/d/HEAD b/tests/resources/typechanges/.gitted/modules/d/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/typechanges/.gitted/modules/d/config b/tests/resources/typechanges/.gitted/modules/d/config
new file mode 100644
index 0000000..42e1bdd
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/config
@@ -0,0 +1,13 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../d
+ ignorecase = true
+[remote "origin"]
+ fetch = +refs/heads/*:refs/remotes/origin/*
+ url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target/.git
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/typechanges/.gitted/modules/d/description b/tests/resources/typechanges/.gitted/modules/d/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/typechanges/.gitted/modules/d/index b/tests/resources/typechanges/.gitted/modules/d/index
new file mode 100644
index 0000000..86d0266
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/index
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/d/info/exclude b/tests/resources/typechanges/.gitted/modules/d/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/typechanges/.gitted/modules/d/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/typechanges/.gitted/modules/d/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 0000000..f4b7094
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/d/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/typechanges/.gitted/modules/d/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 0000000..56c845e
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/d/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/typechanges/.gitted/modules/d/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 0000000..bd179b5
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/d/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/typechanges/.gitted/modules/d/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 0000000..ccf49bd
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/d/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/typechanges/.gitted/modules/d/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 0000000..5302906
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/d/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/typechanges/.gitted/modules/d/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 0000000..38c791e
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/d/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/typechanges/.gitted/modules/d/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 0000000..a26d299
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests/resources/typechanges/.gitted/modules/d/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/typechanges/.gitted/modules/d/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 0000000..83d1ba4
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/d/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/typechanges/.gitted/modules/d/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 0000000..6d27af8
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests/resources/typechanges/.gitted/modules/d/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/typechanges/.gitted/modules/d/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 0000000..1745884
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests/resources/typechanges/.gitted/modules/d/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/typechanges/.gitted/modules/d/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 0000000..83cc29f
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/d/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/typechanges/.gitted/modules/d/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 0000000..55bda40
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/d/packed-refs b/tests/resources/typechanges/.gitted/modules/d/packed-refs
new file mode 100644
index 0000000..5a4ebc4
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
diff --git a/tests/resources/typechanges/.gitted/modules/d/refs/heads/master b/tests/resources/typechanges/.gitted/modules/d/refs/heads/master
new file mode 100644
index 0000000..e12c44d
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/refs/heads/master
@@ -0,0 +1 @@
+480095882d281ed676fe5b863569520e54a7d5c0
diff --git a/tests/resources/typechanges/.gitted/modules/d/refs/remotes/origin/HEAD b/tests/resources/typechanges/.gitted/modules/d/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..6efe28f
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/d/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/typechanges/.gitted/modules/e/HEAD b/tests/resources/typechanges/.gitted/modules/e/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/typechanges/.gitted/modules/e/config b/tests/resources/typechanges/.gitted/modules/e/config
new file mode 100644
index 0000000..89b3b9b
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/config
@@ -0,0 +1,13 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ worktree = ../../../e
+ ignorecase = true
+[remote "origin"]
+ fetch = +refs/heads/*:refs/remotes/origin/*
+ url = /Users/rb/src/libgit2/tests-clar/resources/submod2_target/.git
+[branch "master"]
+ remote = origin
+ merge = refs/heads/master
diff --git a/tests/resources/typechanges/.gitted/modules/e/description b/tests/resources/typechanges/.gitted/modules/e/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/typechanges/.gitted/modules/e/index b/tests/resources/typechanges/.gitted/modules/e/index
new file mode 100644
index 0000000..cd6e2da
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/index
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/e/info/exclude b/tests/resources/typechanges/.gitted/modules/e/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/typechanges/.gitted/modules/e/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1 b/tests/resources/typechanges/.gitted/modules/e/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
new file mode 100644
index 0000000..f4b7094
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/06/362fe2fdb7010d0e447b4fb450d405420479a1
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/e/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484 b/tests/resources/typechanges/.gitted/modules/e/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
new file mode 100644
index 0000000..56c845e
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/0e/6a3ca48bd47cfe67681acf39aa0b10a0b92484
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/e/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5 b/tests/resources/typechanges/.gitted/modules/e/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
new file mode 100644
index 0000000..bd179b5
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/17/d0ece6e96460a06592d9d9d000de37ba4232c5
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/e/objects/41/bd4bc3df978de695f67ace64c560913da11653 b/tests/resources/typechanges/.gitted/modules/e/objects/41/bd4bc3df978de695f67ace64c560913da11653
new file mode 100644
index 0000000..ccf49bd
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/41/bd4bc3df978de695f67ace64c560913da11653
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/e/objects/48/0095882d281ed676fe5b863569520e54a7d5c0 b/tests/resources/typechanges/.gitted/modules/e/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
new file mode 100644
index 0000000..5302906
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/48/0095882d281ed676fe5b863569520e54a7d5c0
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/e/objects/5e/4963595a9774b90524d35a807169049de8ccad b/tests/resources/typechanges/.gitted/modules/e/objects/5e/4963595a9774b90524d35a807169049de8ccad
new file mode 100644
index 0000000..38c791e
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/5e/4963595a9774b90524d35a807169049de8ccad
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/e/objects/6b/31c659545507c381e9cd34ec508f16c04e149e b/tests/resources/typechanges/.gitted/modules/e/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
new file mode 100644
index 0000000..a26d299
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/6b/31c659545507c381e9cd34ec508f16c04e149e
@@ -0,0 +1,2 @@
+x•Q
+!EûvoÅÓy*Ñ_¿í@Çg#h‚£ûOhý^Î9w«¥¤ÒêSoÌ€f1*²ŠÁ[”‰¬§èIc Ô¤ìê¤p£ïµÁkç Α\›¿¿S߇¿lµÜ@.¤´^QpF‹(æ:ÿúDÿ5Åó“zr~ ñen8 \ No newline at end of file
diff --git a/tests/resources/typechanges/.gitted/modules/e/objects/73/ba924a80437097795ae839e66e187c55d3babf b/tests/resources/typechanges/.gitted/modules/e/objects/73/ba924a80437097795ae839e66e187c55d3babf
new file mode 100644
index 0000000..83d1ba4
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/73/ba924a80437097795ae839e66e187c55d3babf
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/e/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a b/tests/resources/typechanges/.gitted/modules/e/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
new file mode 100644
index 0000000..6d27af8
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/78/0d7397f5e8f8f477fb55b7af3accc2154b2d4a
@@ -0,0 +1,2 @@
+x-Ë1Â0 FaæžâßØ0pŽÀìÄÐ(N-ÅöÐÛÓ¡Ò“¾é±ãq]>ksÅ*š? |m“‡Õçiª@ÛÖý¶¼m»¨V£…£'©î`)”.Ø-1¨ x
+u„xãòt(+ \ No newline at end of file
diff --git a/tests/resources/typechanges/.gitted/modules/e/objects/78/9efbdadaa4a582778d4584385495559ea0994b b/tests/resources/typechanges/.gitted/modules/e/objects/78/9efbdadaa4a582778d4584385495559ea0994b
new file mode 100644
index 0000000..1745884
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/78/9efbdadaa4a582778d4584385495559ea0994b
@@ -0,0 +1,2 @@
+x Œ± …0 )ÞŠ?= ¥ÉÄNŠlO¤k®¸‹jÛúÿ¹8&„«¨ ãr ”
+ïqJWñ°7¾B<ÉáöfÙìK8­#Q1C-‘"eª·Ì«£Š°ð>¼'@ \ No newline at end of file
diff --git a/tests/resources/typechanges/.gitted/modules/e/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e b/tests/resources/typechanges/.gitted/modules/e/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
new file mode 100644
index 0000000..83cc29f
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/88/34b635dd468a83cb012f6feace968c1c9f5d6e
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/e/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5 b/tests/resources/typechanges/.gitted/modules/e/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
new file mode 100644
index 0000000..55bda40
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/objects/d0/5f2cd5cc77addf68ed6f50d622c9a4f732e6c5
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/modules/e/packed-refs b/tests/resources/typechanges/.gitted/modules/e/packed-refs
new file mode 100644
index 0000000..5a4ebc4
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled
+480095882d281ed676fe5b863569520e54a7d5c0 refs/remotes/origin/master
diff --git a/tests/resources/typechanges/.gitted/modules/e/refs/heads/master b/tests/resources/typechanges/.gitted/modules/e/refs/heads/master
new file mode 100644
index 0000000..e12c44d
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/refs/heads/master
@@ -0,0 +1 @@
+480095882d281ed676fe5b863569520e54a7d5c0
diff --git a/tests/resources/typechanges/.gitted/modules/e/refs/remotes/origin/HEAD b/tests/resources/typechanges/.gitted/modules/e/refs/remotes/origin/HEAD
new file mode 100644
index 0000000..6efe28f
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/modules/e/refs/remotes/origin/HEAD
@@ -0,0 +1 @@
+ref: refs/remotes/origin/master
diff --git a/tests/resources/typechanges/.gitted/objects/0d/78578795b7ca49fd8df6c4b6d27c5c02d991d8 b/tests/resources/typechanges/.gitted/objects/0d/78578795b7ca49fd8df6c4b6d27c5c02d991d8
new file mode 100644
index 0000000..f2d02f4
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/0d/78578795b7ca49fd8df6c4b6d27c5c02d991d8
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/0e/7ed140b514b8cae23254cb8656fe1674403aff b/tests/resources/typechanges/.gitted/objects/0e/7ed140b514b8cae23254cb8656fe1674403aff
new file mode 100644
index 0000000..527964c
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/0e/7ed140b514b8cae23254cb8656fe1674403aff
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/0f/f461da9689266f482d8f6654a4400b4e33c586 b/tests/resources/typechanges/.gitted/objects/0f/f461da9689266f482d8f6654a4400b4e33c586
new file mode 100644
index 0000000..2694e4f
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/0f/f461da9689266f482d8f6654a4400b4e33c586
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/18/aa7e45bbe4c3cc24a0b079696c59d36675af97 b/tests/resources/typechanges/.gitted/objects/18/aa7e45bbe4c3cc24a0b079696c59d36675af97
new file mode 100644
index 0000000..032a960
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/18/aa7e45bbe4c3cc24a0b079696c59d36675af97
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/1b/63caae4a5ca96f78e8dfefc376c6a39a142475 b/tests/resources/typechanges/.gitted/objects/1b/63caae4a5ca96f78e8dfefc376c6a39a142475
new file mode 100644
index 0000000..d32622e
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/1b/63caae4a5ca96f78e8dfefc376c6a39a142475
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/1e/abe82aa3b2365a394f6108f24435df6e193d02 b/tests/resources/typechanges/.gitted/objects/1e/abe82aa3b2365a394f6108f24435df6e193d02
new file mode 100644
index 0000000..42d5f92
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/1e/abe82aa3b2365a394f6108f24435df6e193d02
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/42/061c01a1c70097d1e4579f29a5adf40abdec95 b/tests/resources/typechanges/.gitted/objects/42/061c01a1c70097d1e4579f29a5adf40abdec95
new file mode 100644
index 0000000..0a8f32e
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/42/061c01a1c70097d1e4579f29a5adf40abdec95
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/46/2838cee476a87e7cff32196b66fa18ed756592 b/tests/resources/typechanges/.gitted/objects/46/2838cee476a87e7cff32196b66fa18ed756592
new file mode 100644
index 0000000..52af51f
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/46/2838cee476a87e7cff32196b66fa18ed756592
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/63/499e4ea8e096b831515ceb1d5a7593e4d87ae5 b/tests/resources/typechanges/.gitted/objects/63/499e4ea8e096b831515ceb1d5a7593e4d87ae5
new file mode 100644
index 0000000..afafa89
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/63/499e4ea8e096b831515ceb1d5a7593e4d87ae5
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/68/1af94e10eaf262f3ab7cb9b8fd5f4158ba4d3e b/tests/resources/typechanges/.gitted/objects/68/1af94e10eaf262f3ab7cb9b8fd5f4158ba4d3e
new file mode 100644
index 0000000..9e518fc
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/68/1af94e10eaf262f3ab7cb9b8fd5f4158ba4d3e
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/6a/9008602b811e69a9b7a2d83496f39a794fdeeb b/tests/resources/typechanges/.gitted/objects/6a/9008602b811e69a9b7a2d83496f39a794fdeeb
new file mode 100644
index 0000000..a245727
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/6a/9008602b811e69a9b7a2d83496f39a794fdeeb
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/6e/ae26c90e8ccc4d16208972119c40635489c6f0 b/tests/resources/typechanges/.gitted/objects/6e/ae26c90e8ccc4d16208972119c40635489c6f0
new file mode 100644
index 0000000..ea35cd3
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/6e/ae26c90e8ccc4d16208972119c40635489c6f0
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/6f/39eabbb8a7541515e0d35971078bccb502e7e0 b/tests/resources/typechanges/.gitted/objects/6f/39eabbb8a7541515e0d35971078bccb502e7e0
new file mode 100644
index 0000000..c548175
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/6f/39eabbb8a7541515e0d35971078bccb502e7e0
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/71/54d3083461536dfc71ad5542f3e65e723a06c4 b/tests/resources/typechanges/.gitted/objects/71/54d3083461536dfc71ad5542f3e65e723a06c4
new file mode 100644
index 0000000..9fdd8f2
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/71/54d3083461536dfc71ad5542f3e65e723a06c4
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/75/56c1d893a4c0ca85ac8ac51de47ff399758729 b/tests/resources/typechanges/.gitted/objects/75/56c1d893a4c0ca85ac8ac51de47ff399758729
new file mode 100644
index 0000000..d43630f
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/75/56c1d893a4c0ca85ac8ac51de47ff399758729
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/76/fef844064c26d5e06c2508240dae661e7231b2 b/tests/resources/typechanges/.gitted/objects/76/fef844064c26d5e06c2508240dae661e7231b2
new file mode 100644
index 0000000..355ce4b
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/76/fef844064c26d5e06c2508240dae661e7231b2
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/79/b9f23e85f55ea36a472a902e875bc1121a94cb b/tests/resources/typechanges/.gitted/objects/79/b9f23e85f55ea36a472a902e875bc1121a94cb
new file mode 100644
index 0000000..2b07ad2
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/79/b9f23e85f55ea36a472a902e875bc1121a94cb
@@ -0,0 +1,2 @@
+x•A E]sŠ¹€f€JbŒqçÖ í`I@
+÷—ĸýyïýµäH;ŸZeBrž6L˜P«Yº%8½²&v‹4JmÖ¢ÔÛ^*¼úqpJðà¸Âµúû;¶½ûËZò ¤žœ’Æ 3ZD1Öñ×ú¯)žŸØ"%ø%Ä–38_ \ No newline at end of file
diff --git a/tests/resources/typechanges/.gitted/objects/85/28da0ea65eacf1f74f9ed6696adbac547963ad b/tests/resources/typechanges/.gitted/objects/85/28da0ea65eacf1f74f9ed6696adbac547963ad
new file mode 100644
index 0000000..6d2da6c
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/85/28da0ea65eacf1f74f9ed6696adbac547963ad
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/8b/3726b365824ad5a07c537247f4bc73ed7d37ea b/tests/resources/typechanges/.gitted/objects/8b/3726b365824ad5a07c537247f4bc73ed7d37ea
new file mode 100644
index 0000000..3dc333b
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/8b/3726b365824ad5a07c537247f4bc73ed7d37ea
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/93/3e28c1c8a68838a763d250bdf0b2c6068289c3 b/tests/resources/typechanges/.gitted/objects/93/3e28c1c8a68838a763d250bdf0b2c6068289c3
new file mode 100644
index 0000000..02ad0e9
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/93/3e28c1c8a68838a763d250bdf0b2c6068289c3
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/96/2710fe5b4e453e9e827945b3487c525968ec4a b/tests/resources/typechanges/.gitted/objects/96/2710fe5b4e453e9e827945b3487c525968ec4a
new file mode 100644
index 0000000..d06b06a
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/96/2710fe5b4e453e9e827945b3487c525968ec4a
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/96/6cf1b3598e195b31b2cde3784f9a19f0728a6f b/tests/resources/typechanges/.gitted/objects/96/6cf1b3598e195b31b2cde3784f9a19f0728a6f
new file mode 100644
index 0000000..5f9ffd4
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/96/6cf1b3598e195b31b2cde3784f9a19f0728a6f
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/99/e8bab9ece009f0fba7eb41f850f4c12bedb9b7 b/tests/resources/typechanges/.gitted/objects/99/e8bab9ece009f0fba7eb41f850f4c12bedb9b7
new file mode 100644
index 0000000..ac17def
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/99/e8bab9ece009f0fba7eb41f850f4c12bedb9b7
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/9b/19edf33a03a0c59cdfc113bfa5c06179bf9b1a b/tests/resources/typechanges/.gitted/objects/9b/19edf33a03a0c59cdfc113bfa5c06179bf9b1a
new file mode 100644
index 0000000..7ab83ae
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/9b/19edf33a03a0c59cdfc113bfa5c06179bf9b1a
@@ -0,0 +1,5 @@
+x•ŽI
+1E]箕LTˆè ¼A†J·Ðƒ¤Ó÷7WpûyïñÓ¶,ŸZÑ©Uf cXcR ƒC4¼3Y2æ"£NN:ÔHɈo¨¼6 ,µ’žs’ˆòÁjf—#îk½G›¶
+ïcßyžáÉsá
+·ã§MG¼¦m¹ƒ2–´Bò
+.ÒK)úÚÿµŽþkŠ×Ö‘w8ñžCCà \ No newline at end of file
diff --git a/tests/resources/typechanges/.gitted/objects/9b/db75b73836a99e3dbeea640a81de81031fdc29 b/tests/resources/typechanges/.gitted/objects/9b/db75b73836a99e3dbeea640a81de81031fdc29
new file mode 100644
index 0000000..aed4d81
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/9b/db75b73836a99e3dbeea640a81de81031fdc29
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/9d/0235c7a7edc0889a18f97a42ee6db9fe688447 b/tests/resources/typechanges/.gitted/objects/9d/0235c7a7edc0889a18f97a42ee6db9fe688447
new file mode 100644
index 0000000..3e02a41
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/9d/0235c7a7edc0889a18f97a42ee6db9fe688447
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/9e/ffc457877f109b2a4319e14bee613a15f2a00d b/tests/resources/typechanges/.gitted/objects/9e/ffc457877f109b2a4319e14bee613a15f2a00d
new file mode 100644
index 0000000..fb24100
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/9e/ffc457877f109b2a4319e14bee613a15f2a00d
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/a0/a9bad6f6f40325198f938a0e3ae981622d7707 b/tests/resources/typechanges/.gitted/objects/a0/a9bad6f6f40325198f938a0e3ae981622d7707
new file mode 100644
index 0000000..b6b7db7
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/a0/a9bad6f6f40325198f938a0e3ae981622d7707
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/b1/977dc4e573b812d4619754c98138c56999dc0d b/tests/resources/typechanges/.gitted/objects/b1/977dc4e573b812d4619754c98138c56999dc0d
new file mode 100644
index 0000000..e133405
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/b1/977dc4e573b812d4619754c98138c56999dc0d
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/d7/5992dd02391e128dac332dcc78d649dd9ab095 b/tests/resources/typechanges/.gitted/objects/d7/5992dd02391e128dac332dcc78d649dd9ab095
new file mode 100644
index 0000000..65f1f53
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/d7/5992dd02391e128dac332dcc78d649dd9ab095
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/da/e2709d638df52212b1f43ff61797ebfedfcc7c b/tests/resources/typechanges/.gitted/objects/da/e2709d638df52212b1f43ff61797ebfedfcc7c
new file mode 100644
index 0000000..355faa6
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/da/e2709d638df52212b1f43ff61797ebfedfcc7c
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/e1/152adcb9adf37ec551ada9ba377ab53aec3bad b/tests/resources/typechanges/.gitted/objects/e1/152adcb9adf37ec551ada9ba377ab53aec3bad
new file mode 100644
index 0000000..c68fdcf
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/e1/152adcb9adf37ec551ada9ba377ab53aec3bad
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/e4/ed436a9eb0f198cda722886a5f8d6d6c836b7b b/tests/resources/typechanges/.gitted/objects/e4/ed436a9eb0f198cda722886a5f8d6d6c836b7b
new file mode 100644
index 0000000..c9229ba
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/e4/ed436a9eb0f198cda722886a5f8d6d6c836b7b
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391 b/tests/resources/typechanges/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
new file mode 100644
index 0000000..7112238
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/e6/9de29bb2d1d6434b8b29ae775ad8c2e48c5391
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/f2/0b79342712e0b2315647cd8227a573fd3bc46e b/tests/resources/typechanges/.gitted/objects/f2/0b79342712e0b2315647cd8227a573fd3bc46e
new file mode 100644
index 0000000..3962ba6
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/f2/0b79342712e0b2315647cd8227a573fd3bc46e
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/objects/fd/e0147e3b59f381635a3b016e3fe6dacb70779d b/tests/resources/typechanges/.gitted/objects/fd/e0147e3b59f381635a3b016e3fe6dacb70779d
new file mode 100644
index 0000000..e3663da
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/objects/fd/e0147e3b59f381635a3b016e3fe6dacb70779d
Binary files differ
diff --git a/tests/resources/typechanges/.gitted/refs/heads/master b/tests/resources/typechanges/.gitted/refs/heads/master
new file mode 100644
index 0000000..546481a
--- /dev/null
+++ b/tests/resources/typechanges/.gitted/refs/heads/master
@@ -0,0 +1 @@
+6eae26c90e8ccc4d16208972119c40635489c6f0
diff --git a/tests/resources/typechanges/README.md b/tests/resources/typechanges/README.md
new file mode 100644
index 0000000..1f5a95a
--- /dev/null
+++ b/tests/resources/typechanges/README.md
@@ -0,0 +1,43 @@
+This is a test repo for libgit2 where tree entries have type changes
+
+Types
+-----
+
+The key types that could be found in tree entries are:
+
+1. GIT_FILEMODE_NEW = 0000000 (i.e. file does not exist)
+2. GIT_FILEMODE_TREE = 0040000
+3. GIT_FILEMODE_BLOB = 0100644
+4. GIT_FILEMODE_BLOB_EXECUTABLE = 0100755
+5. GIT_FILEMODE_LINK = 0120000
+6. GIT_FILEMODE_COMMIT = 0160000
+
+I will try to have every type of transition somewhere in the history
+of this repo.
+
+Commits
+-------
+
+* `a(1--1) b(1--1) c(1--1) d(1--1) e(1--1)`
+ **Initial commit**<br>
+ `79b9f23e85f55ea36a472a902e875bc1121a94cb`
+* `a(1->2) b(1->3) c(1->4) d(1->5) e(1->6)`
+ **Create content**<br>
+ `9bdb75b73836a99e3dbeea640a81de81031fdc29`
+* `a(2->3) b(3->4) c(4->5) d(5->6) e(6->2)`
+ **Changes #1**<br>
+ `0e7ed140b514b8cae23254cb8656fe1674403aff`
+* `a(3->5) b(4->6) c(5->2) d(6->3) e(2->4)`
+ **Changes #2**<br>
+ `9d0235c7a7edc0889a18f97a42ee6db9fe688447`
+* `a(5->3) b(6->4) c(2->5) d(3->6) e(4->2)`
+ **Changes #3**<br>
+ `9b19edf33a03a0c59cdfc113bfa5c06179bf9b1a`
+* `a(3->2) b(4->3) c(5->4) d(6->5) e(2->6)`
+ **Changes #4**<br>
+ `1b63caae4a5ca96f78e8dfefc376c6a39a142475`<br>
+ Matches **Changes #1** except README.md
+* `a(2->1) b(3->1) c(4->1) d(5->1) e(6->1)`
+ **Changes #5**<br>
+ `6eae26c90e8ccc4d16208972119c40635489c6f0`<br>
+ Matches **Initial commit** except README.md and .gitmodules
diff --git a/tests/resources/typechanges/gitmodules b/tests/resources/typechanges/gitmodules
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/typechanges/gitmodules
diff --git a/tests/resources/unsymlinked.git/HEAD b/tests/resources/unsymlinked.git/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/unsymlinked.git/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/unsymlinked.git/config b/tests/resources/unsymlinked.git/config
new file mode 100644
index 0000000..f57351f
--- /dev/null
+++ b/tests/resources/unsymlinked.git/config
@@ -0,0 +1,6 @@
+[core]
+ bare = true
+ repositoryformatversion = 0
+ filemode = false
+ logallrefupdates = true
+ ignorecase = true
diff --git a/tests/resources/unsymlinked.git/description b/tests/resources/unsymlinked.git/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/unsymlinked.git/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/unsymlinked.git/info/exclude b/tests/resources/unsymlinked.git/info/exclude
new file mode 100644
index 0000000..6d05881
--- /dev/null
+++ b/tests/resources/unsymlinked.git/info/exclude
@@ -0,0 +1,2 @@
+# File patterns to ignore; see `git help ignore` for more information.
+# Lines that start with '#' are comments.
diff --git a/tests/resources/unsymlinked.git/objects/08/8b64704e0d6b8bd061dea879418cb5442a3fbf b/tests/resources/unsymlinked.git/objects/08/8b64704e0d6b8bd061dea879418cb5442a3fbf
new file mode 100644
index 0000000..953262f
--- /dev/null
+++ b/tests/resources/unsymlinked.git/objects/08/8b64704e0d6b8bd061dea879418cb5442a3fbf
Binary files differ
diff --git a/tests/resources/unsymlinked.git/objects/13/a5e939bca25940c069fd2169d993dba328e30b b/tests/resources/unsymlinked.git/objects/13/a5e939bca25940c069fd2169d993dba328e30b
new file mode 100644
index 0000000..91ec8fa
--- /dev/null
+++ b/tests/resources/unsymlinked.git/objects/13/a5e939bca25940c069fd2169d993dba328e30b
Binary files differ
diff --git a/tests/resources/unsymlinked.git/objects/19/bf568e59e3a0b363cafb4106226e62d4a4c41c b/tests/resources/unsymlinked.git/objects/19/bf568e59e3a0b363cafb4106226e62d4a4c41c
new file mode 100644
index 0000000..94afd01
--- /dev/null
+++ b/tests/resources/unsymlinked.git/objects/19/bf568e59e3a0b363cafb4106226e62d4a4c41c
Binary files differ
diff --git a/tests/resources/unsymlinked.git/objects/58/1fadd35b4cf320d102a152f918729011604773 b/tests/resources/unsymlinked.git/objects/58/1fadd35b4cf320d102a152f918729011604773
new file mode 100644
index 0000000..5b33d02
--- /dev/null
+++ b/tests/resources/unsymlinked.git/objects/58/1fadd35b4cf320d102a152f918729011604773
Binary files differ
diff --git a/tests/resources/unsymlinked.git/objects/5c/87b6791e8b13da658a14d1ef7e09b5dc3bac8c b/tests/resources/unsymlinked.git/objects/5c/87b6791e8b13da658a14d1ef7e09b5dc3bac8c
new file mode 100644
index 0000000..67eb149
--- /dev/null
+++ b/tests/resources/unsymlinked.git/objects/5c/87b6791e8b13da658a14d1ef7e09b5dc3bac8c
Binary files differ
diff --git a/tests/resources/unsymlinked.git/objects/6f/e5f5398af85fb3de8a6aba0339b6d3bfa26a27 b/tests/resources/unsymlinked.git/objects/6f/e5f5398af85fb3de8a6aba0339b6d3bfa26a27
new file mode 100644
index 0000000..c1ea0de
--- /dev/null
+++ b/tests/resources/unsymlinked.git/objects/6f/e5f5398af85fb3de8a6aba0339b6d3bfa26a27
Binary files differ
diff --git a/tests/resources/unsymlinked.git/objects/7f/ccd75616ec188b8f1b23d67506a334cc34a49d b/tests/resources/unsymlinked.git/objects/7f/ccd75616ec188b8f1b23d67506a334cc34a49d
new file mode 100644
index 0000000..0285055
--- /dev/null
+++ b/tests/resources/unsymlinked.git/objects/7f/ccd75616ec188b8f1b23d67506a334cc34a49d
Binary files differ
diff --git a/tests/resources/unsymlinked.git/objects/80/6999882bf91d24241e4077906b9017605eb1f3 b/tests/resources/unsymlinked.git/objects/80/6999882bf91d24241e4077906b9017605eb1f3
new file mode 100644
index 0000000..e866a75
--- /dev/null
+++ b/tests/resources/unsymlinked.git/objects/80/6999882bf91d24241e4077906b9017605eb1f3
Binary files differ
diff --git a/tests/resources/unsymlinked.git/objects/83/7d176303c5005505ec1e4a30231c40930c0230 b/tests/resources/unsymlinked.git/objects/83/7d176303c5005505ec1e4a30231c40930c0230
new file mode 100644
index 0000000..189ab04
--- /dev/null
+++ b/tests/resources/unsymlinked.git/objects/83/7d176303c5005505ec1e4a30231c40930c0230
Binary files differ
diff --git a/tests/resources/unsymlinked.git/objects/a8/595ccca04f40818ae0155c8f9c77a230e597b6 b/tests/resources/unsymlinked.git/objects/a8/595ccca04f40818ae0155c8f9c77a230e597b6
new file mode 100644
index 0000000..a2ef6be
--- /dev/null
+++ b/tests/resources/unsymlinked.git/objects/a8/595ccca04f40818ae0155c8f9c77a230e597b6
@@ -0,0 +1,2 @@
+x•Ž[
+Â0EýÎ*æ_“¤yˆÝ€{˜4ShZ))¸| ®À¯ ÎáN[ks=èKß™Á¶Åš¨[’ÉÈQ"4&&—M*¤i/Þ´óÚ! ‹1† S‰*Ÿ™Añ€ÞGt)¢ò-'UŒ £×m‡ñ7O cc¸Õ¹=zåå´µ;(ãPY«‡+*DqÒó^ç¿E!¥œ*­/Î0¯}Z?<ÒÂPæ…¥øšÈJp \ No newline at end of file
diff --git a/tests/resources/unsymlinked.git/objects/cf/8f1cf5cce859c438d6cc067284cb5e161206e7 b/tests/resources/unsymlinked.git/objects/cf/8f1cf5cce859c438d6cc067284cb5e161206e7
new file mode 100644
index 0000000..ec274cb
--- /dev/null
+++ b/tests/resources/unsymlinked.git/objects/cf/8f1cf5cce859c438d6cc067284cb5e161206e7
Binary files differ
diff --git a/tests/resources/unsymlinked.git/objects/d5/278d05c8607ec420bfee4cf219fbc0eeebfd6a b/tests/resources/unsymlinked.git/objects/d5/278d05c8607ec420bfee4cf219fbc0eeebfd6a
new file mode 100644
index 0000000..c1b6a51
--- /dev/null
+++ b/tests/resources/unsymlinked.git/objects/d5/278d05c8607ec420bfee4cf219fbc0eeebfd6a
Binary files differ
diff --git a/tests/resources/unsymlinked.git/objects/f4/e16fb76536591a41454194058d048d8e4dd2e9 b/tests/resources/unsymlinked.git/objects/f4/e16fb76536591a41454194058d048d8e4dd2e9
new file mode 100644
index 0000000..ad751ad
--- /dev/null
+++ b/tests/resources/unsymlinked.git/objects/f4/e16fb76536591a41454194058d048d8e4dd2e9
Binary files differ
diff --git a/tests/resources/unsymlinked.git/objects/f9/e65619d93fdf2673882e0a261c5e93b1a84006 b/tests/resources/unsymlinked.git/objects/f9/e65619d93fdf2673882e0a261c5e93b1a84006
new file mode 100644
index 0000000..f87cd42
--- /dev/null
+++ b/tests/resources/unsymlinked.git/objects/f9/e65619d93fdf2673882e0a261c5e93b1a84006
Binary files differ
diff --git a/tests/resources/unsymlinked.git/refs/heads/exe-file b/tests/resources/unsymlinked.git/refs/heads/exe-file
new file mode 100644
index 0000000..b96ef46
--- /dev/null
+++ b/tests/resources/unsymlinked.git/refs/heads/exe-file
@@ -0,0 +1 @@
+a8595ccca04f40818ae0155c8f9c77a230e597b6
diff --git a/tests/resources/unsymlinked.git/refs/heads/master b/tests/resources/unsymlinked.git/refs/heads/master
new file mode 100644
index 0000000..96c17ab
--- /dev/null
+++ b/tests/resources/unsymlinked.git/refs/heads/master
@@ -0,0 +1 @@
+7fccd75616ec188b8f1b23d67506a334cc34a49d
diff --git a/tests/resources/unsymlinked.git/refs/heads/reg-file b/tests/resources/unsymlinked.git/refs/heads/reg-file
new file mode 100644
index 0000000..b428c00
--- /dev/null
+++ b/tests/resources/unsymlinked.git/refs/heads/reg-file
@@ -0,0 +1 @@
+806999882bf91d24241e4077906b9017605eb1f3
diff --git a/tests/resources/userdiff/.gitted/HEAD b/tests/resources/userdiff/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/userdiff/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/userdiff/.gitted/config b/tests/resources/userdiff/.gitted/config
new file mode 100644
index 0000000..6c9406b
--- /dev/null
+++ b/tests/resources/userdiff/.gitted/config
@@ -0,0 +1,7 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
+ precomposeunicode = true
diff --git a/tests/resources/userdiff/.gitted/description b/tests/resources/userdiff/.gitted/description
new file mode 100644
index 0000000..498b267
--- /dev/null
+++ b/tests/resources/userdiff/.gitted/description
@@ -0,0 +1 @@
+Unnamed repository; edit this file 'description' to name the repository.
diff --git a/tests/resources/userdiff/.gitted/index b/tests/resources/userdiff/.gitted/index
new file mode 100644
index 0000000..548dc7b
--- /dev/null
+++ b/tests/resources/userdiff/.gitted/index
Binary files differ
diff --git a/tests/resources/userdiff/.gitted/info/refs b/tests/resources/userdiff/.gitted/info/refs
new file mode 100644
index 0000000..b074314
--- /dev/null
+++ b/tests/resources/userdiff/.gitted/info/refs
@@ -0,0 +1 @@
+60e3f7b244a5305e2c9fa4ef0e897f3b14f3b8dd refs/heads/master
diff --git a/tests/resources/userdiff/.gitted/objects/09/65b377c214bbe5e0d18fcdaf556df7fa7ed7c8 b/tests/resources/userdiff/.gitted/objects/09/65b377c214bbe5e0d18fcdaf556df7fa7ed7c8
new file mode 100644
index 0000000..cbaf4c1
--- /dev/null
+++ b/tests/resources/userdiff/.gitted/objects/09/65b377c214bbe5e0d18fcdaf556df7fa7ed7c8
Binary files differ
diff --git a/tests/resources/userdiff/.gitted/objects/0c/20ef1409ae1df4d5a76cdbd98d5c33ccdb6bcc b/tests/resources/userdiff/.gitted/objects/0c/20ef1409ae1df4d5a76cdbd98d5c33ccdb6bcc
new file mode 100644
index 0000000..fa149da
--- /dev/null
+++ b/tests/resources/userdiff/.gitted/objects/0c/20ef1409ae1df4d5a76cdbd98d5c33ccdb6bcc
Binary files differ
diff --git a/tests/resources/userdiff/.gitted/objects/39/ea75107a09091ba54ff86fcc780b59477e42cd b/tests/resources/userdiff/.gitted/objects/39/ea75107a09091ba54ff86fcc780b59477e42cd
new file mode 100644
index 0000000..7483736
--- /dev/null
+++ b/tests/resources/userdiff/.gitted/objects/39/ea75107a09091ba54ff86fcc780b59477e42cd
Binary files differ
diff --git a/tests/resources/userdiff/.gitted/objects/3c/c08384deae5957247bc36776ab626cc9e0582b b/tests/resources/userdiff/.gitted/objects/3c/c08384deae5957247bc36776ab626cc9e0582b
new file mode 100644
index 0000000..29b72fb
--- /dev/null
+++ b/tests/resources/userdiff/.gitted/objects/3c/c08384deae5957247bc36776ab626cc9e0582b
Binary files differ
diff --git a/tests/resources/userdiff/.gitted/objects/46/8d6f2afc940e14c76347fa9af26e429a3c9044 b/tests/resources/userdiff/.gitted/objects/46/8d6f2afc940e14c76347fa9af26e429a3c9044
new file mode 100644
index 0000000..5fc8391
--- /dev/null
+++ b/tests/resources/userdiff/.gitted/objects/46/8d6f2afc940e14c76347fa9af26e429a3c9044
Binary files differ
diff --git a/tests/resources/userdiff/.gitted/objects/53/917973acfe0111f93c2cfaacf854be245880e8 b/tests/resources/userdiff/.gitted/objects/53/917973acfe0111f93c2cfaacf854be245880e8
new file mode 100644
index 0000000..debf7e4
--- /dev/null
+++ b/tests/resources/userdiff/.gitted/objects/53/917973acfe0111f93c2cfaacf854be245880e8
Binary files differ
diff --git a/tests/resources/userdiff/.gitted/objects/63/1d44e0c72e8cd1b594fa11d7d1ee8a6d67ff67 b/tests/resources/userdiff/.gitted/objects/63/1d44e0c72e8cd1b594fa11d7d1ee8a6d67ff67
new file mode 100644
index 0000000..e8b884c
--- /dev/null
+++ b/tests/resources/userdiff/.gitted/objects/63/1d44e0c72e8cd1b594fa11d7d1ee8a6d67ff67
Binary files differ
diff --git a/tests/resources/userdiff/.gitted/objects/f3/be389d351e4bcc6dcc4b5fe22134ef0f63f8bd b/tests/resources/userdiff/.gitted/objects/f3/be389d351e4bcc6dcc4b5fe22134ef0f63f8bd
new file mode 100644
index 0000000..cfbef99
--- /dev/null
+++ b/tests/resources/userdiff/.gitted/objects/f3/be389d351e4bcc6dcc4b5fe22134ef0f63f8bd
Binary files differ
diff --git a/tests/resources/userdiff/.gitted/objects/info/packs b/tests/resources/userdiff/.gitted/objects/info/packs
new file mode 100644
index 0000000..0c5fc2a
--- /dev/null
+++ b/tests/resources/userdiff/.gitted/objects/info/packs
@@ -0,0 +1,2 @@
+P pack-1652578900ac63564f2a24b9714529821276ceb9.pack
+
diff --git a/tests/resources/userdiff/.gitted/objects/pack/pack-1652578900ac63564f2a24b9714529821276ceb9.idx b/tests/resources/userdiff/.gitted/objects/pack/pack-1652578900ac63564f2a24b9714529821276ceb9.idx
new file mode 100644
index 0000000..6f4381c
--- /dev/null
+++ b/tests/resources/userdiff/.gitted/objects/pack/pack-1652578900ac63564f2a24b9714529821276ceb9.idx
Binary files differ
diff --git a/tests/resources/userdiff/.gitted/objects/pack/pack-1652578900ac63564f2a24b9714529821276ceb9.pack b/tests/resources/userdiff/.gitted/objects/pack/pack-1652578900ac63564f2a24b9714529821276ceb9.pack
new file mode 100644
index 0000000..39bd1d8
--- /dev/null
+++ b/tests/resources/userdiff/.gitted/objects/pack/pack-1652578900ac63564f2a24b9714529821276ceb9.pack
Binary files differ
diff --git a/tests/resources/userdiff/.gitted/packed-refs b/tests/resources/userdiff/.gitted/packed-refs
new file mode 100644
index 0000000..802f67c
--- /dev/null
+++ b/tests/resources/userdiff/.gitted/packed-refs
@@ -0,0 +1,2 @@
+# pack-refs with: peeled fully-peeled
+60e3f7b244a5305e2c9fa4ef0e897f3b14f3b8dd refs/heads/master
diff --git a/tests/resources/userdiff/.gitted/refs/dummy-marker.txt b/tests/resources/userdiff/.gitted/refs/dummy-marker.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/tests/resources/userdiff/.gitted/refs/dummy-marker.txt
diff --git a/tests/resources/userdiff/after/file.html b/tests/resources/userdiff/after/file.html
new file mode 100644
index 0000000..2320e2f
--- /dev/null
+++ b/tests/resources/userdiff/after/file.html
@@ -0,0 +1,41 @@
+<html>
+<body>
+ <h1 id="first section">
+ <ol>
+ <li>item 1.1</li>
+ <li>item 1.2 changed</li>
+ <li>item 1.3 changed</li>
+ <li>item 1.4</li>
+ <li>item 1.5</li>
+ <li>item 1.6</li>
+ <li>item 1.7</li>
+ <li>item 1.8</li>
+ <li>item 1.9</li>
+ <li>item 1.10 added</li>
+ </ol>
+ </h1>
+ <h1 id="second section">
+ <ol>
+ <li>item 2.1</li>
+ <li>item 2.2</li>
+ <li>item 2.3</li>
+ <li>item 2.4</li>
+ <li>item 2.5</li>
+ <li>item 2.6</li>
+ <li>item 2.7 changed</li>
+ <li>item 2.7.1 added</li>
+ <li>item 2.8</li>
+ </ol>
+ </h1>
+ <h1 id="third section">
+ <ol>
+ <li>item 3.1</li>
+ <li>item 3.2</li>
+ <li>item 3.3</li>
+ <li>item 3.4</li>
+ <li>item 3.5</li>
+ <li>item 3.6</li>
+ </ol>
+ </h1>
+</body>
+</html>
diff --git a/tests/resources/userdiff/after/file.javascript b/tests/resources/userdiff/after/file.javascript
new file mode 100644
index 0000000..5391797
--- /dev/null
+++ b/tests/resources/userdiff/after/file.javascript
@@ -0,0 +1,108 @@
+define(function(require, exports, module) {
+ module.exports = Player;
+
+ var Key = require("./key")
+ , Direction = require("./direction");
+
+ function Player(game) {
+ this.game = game;
+
+ this.image = new Image("./assets/fighter.png");
+ this.game.resources.add(this.image);
+
+ this.x = 0;
+ this.y = 0;
+
+ this.pixelX = 10;
+ this.pixelY = 10;
+
+ this.animationStep = 0;
+ }
+
+ Player.prototype.update = function() {
+ if (!this.isWalking()) {
+ this.handleInput();
+ }
+
+ if (this.isWalking()) {
+ // Increase the animation step.
+ this.animationStep = ++this.animationStep % 60;
+
+ if (this.x * 32 > this.pixelX) {
+ this.pixelX++;
+ } else if (this.x * 32 < this.pixelX) {
+ this.pixelX--;
+ }
+
+ if (this.y * 32 > this.pixelY) {
+ this.pixelY++;
+ } else if (this.y * 32 < this.pixelY) {
+ this.pixelY--;
+ }
+ } else {
+ // Reset the animation step.
+ this.animationStep = 0;
+ }
+ };
+
+ Player.prototype.handleInput = function() {
+ var keyboard = this.game.keyboard, finalAction, action, inputs = {
+ 'moveDown': keyboard.isDown(Key.DOWN),
+ 'moveUp': keyboard.isDown(Key.UP),
+ 'moveLeft': keyboard.isDown(Key.LEFT),
+ 'moveRight': keyboard.isDown(Key.RIGHT)
+ };
+
+ for (action in inputs) {
+ if (inputs[action]) {
+ if (!finalAction || inputs[finalAction] < inputs[action]) {
+ finalAction = action;
+ }
+ }
+ }
+
+ this[finalAction] && this[finalAction]();
+ };
+
+ Player.prototype.isWalking = function() {
+ return this.x * 32 != this.pixelX || this.y * 32 != this.pixelY;
+ };
+
+ Player.prototype.moveDown = function() {
+ this.y += 1;
+ this.direction = Direction.DOWN;
+ };
+
+ Player.prototype.moveUp = function() {
+ this.y -= 1;
+ this.direction = Direction.UP;
+ };
+
+ Player.prototype.moveLeft = function() {
+ this.x -= 5;
+ this.direction = Direction.LEFT;
+ };
+
+ Player.prototype.moveRight = function() {
+ this.x += 1;
+ this.direction = Direction.RIGHT;
+ };
+
+ Player.prototype.draw = function(context) {
+ var offsetX = Math.floor(this.animationStep / 15) * 32, offsetY = 0;
+
+ switch(this.direction) {
+ case Direction.UP:
+ offsetY = 48 * 3;
+ break;
+ case Direction.RIGHT:
+ offsetY = 48 * 2;
+ break;
+ case Direction.LEFT:
+ offsetY = 48;
+ break;
+ }
+
+ context.drawImage(this.image.data, offsetX, offsetY, 32, 48, this.pixelX, this.pixelY, 32, 48);
+ };
+});
diff --git a/tests/resources/userdiff/after/file.php b/tests/resources/userdiff/after/file.php
new file mode 100644
index 0000000..967d646
--- /dev/null
+++ b/tests/resources/userdiff/after/file.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace Faker;
+
+/**
+ * Proxy for other generators, to return only unique values. Works with
+ * Faker\Generator\Base->unique()
+ */
+class UniqueGenerator
+{
+ protected $generator;
+ protected $maxRetries;
+ protected $moreStuff;
+ protected $uniques = array();
+
+ public function __construct(Generator $generator, $maxRetries)
+ {
+ $this->generator = $generator;
+ $this->maxRetries = $maxRetries + 1;
+ }
+
+ /**
+ * Catch and proxy all generator calls but return only unique values
+ */
+ public function __get($attribute)
+ {
+ return $this->__call($attribute, array());
+ }
+
+ /**
+ * Catch and proxy all generator calls with arguments but return only unique values
+ */
+ public function __call($name, $arguments)
+ {
+ $i = 0;
+ if (!isset($this->uniques[$name])) {
+ $this->uniques[$name] = array();
+ }
+ do {
+ $res = call_user_func_array(array($this->generator, $name), $arguments);
+ $i++;
+ if ($i >= $this->maxRetries) {
+ throw new \OverflowException(sprintf('Maximum retries of %d reached without finding a unique value', $this->maxRetries));
+ }
+ } while (in_array($res, $this->uniques[$name]));
+ $this->uniques[$name][]= $res;
+
+ return $res;
+ }
+}
diff --git a/tests/resources/userdiff/before/file.html b/tests/resources/userdiff/before/file.html
new file mode 100644
index 0000000..872d196
--- /dev/null
+++ b/tests/resources/userdiff/before/file.html
@@ -0,0 +1,41 @@
+<html>
+<body>
+ <h1 id="first section">
+ <ol>
+ <li>item 1.1</li>
+ <li>item 1.2</li>
+ <li>item 1.3</li>
+ <li>item 1.4</li>
+ <li>item 1.5</li>
+ <li>item 1.6</li>
+ <li>item 1.7</li>
+ <li>item 1.8</li>
+ <li>item 1.9</li>
+ </ol>
+ </h1>
+ <h1 id="second section">
+ <ol>
+ <li>item 2.1</li>
+ <li>item 2.2</li>
+ <li>item 2.3</li>
+ <li>item 2.4</li>
+ <li>item 2.5</li>
+ <li>item 2.6</li>
+ <li>item 2.7</li>
+ <li>item 2.8</li>
+ </ol>
+ </h1>
+ <h1 id="third section">
+ <ol>
+ <li>item 3.1</li>
+ <li>item 3.2</li>
+ <li>item 3.3</li>
+ <li>item 3.4</li>
+ <li>item 3.5</li>
+ <li>item 3.6</li>
+ <li>item 3.7</li>
+ <li>item 3.8</li>
+ </ol>
+ </h1>
+</body>
+</html>
diff --git a/tests/resources/userdiff/before/file.javascript b/tests/resources/userdiff/before/file.javascript
new file mode 100644
index 0000000..0965b37
--- /dev/null
+++ b/tests/resources/userdiff/before/file.javascript
@@ -0,0 +1,109 @@
+define(function(require, exports, module) {
+ module.exports = Player;
+
+ var Key = require("./key")
+ , Direction = require("./direction")
+ , Image = require("./image");
+
+ function Player(game) {
+ this.game = game;
+
+ this.image = new Image("./assets/fighter.png");
+ this.game.resources.add(this.image);
+
+ this.x = 0;
+ this.y = 0;
+
+ this.pixelX = 0;
+ this.pixelY = 0;
+
+ this.animationStep = 0;
+ }
+
+ Player.prototype.update = function() {
+ if (!this.isWalking()) {
+ this.handleInput();
+ }
+
+ if (this.isWalking()) {
+ // Increase the animation step.
+ this.animationStep = ++this.animationStep % 60;
+
+ if (this.x * 32 > this.pixelX) {
+ this.pixelX++;
+ } else if (this.x * 32 < this.pixelX) {
+ this.pixelX--;
+ }
+
+ if (this.y * 32 > this.pixelY) {
+ this.pixelY++;
+ } else if (this.y * 32 < this.pixelY) {
+ this.pixelY--;
+ }
+ } else {
+ // Reset the animation step.
+ this.animationStep = 0;
+ }
+ };
+
+ Player.prototype.handleInput = function() {
+ var keyboard = this.game.keyboard, finalAction, action, inputs = {
+ 'moveDown': keyboard.isDown(Key.DOWN),
+ 'moveUp': keyboard.isDown(Key.UP),
+ 'moveLeft': keyboard.isDown(Key.LEFT),
+ 'moveRight': keyboard.isDown(Key.RIGHT)
+ };
+
+ for (action in inputs) {
+ if (inputs[action]) {
+ if (!finalAction || inputs[finalAction] < inputs[action]) {
+ finalAction = action;
+ }
+ }
+ }
+
+ this[finalAction] && this[finalAction]();
+ };
+
+ Player.prototype.isWalking = function() {
+ return this.x * 32 != this.pixelX || this.y * 32 != this.pixelY;
+ };
+
+ Player.prototype.moveDown = function() {
+ this.y += 1;
+ this.direction = Direction.DOWN;
+ };
+
+ Player.prototype.moveUp = function() {
+ this.y -= 1;
+ this.direction = Direction.UP;
+ };
+
+ Player.prototype.moveLeft = function() {
+ this.x -= 1;
+ this.direction = Direction.LEFT;
+ };
+
+ Player.prototype.moveRight = function() {
+ this.x += 1;
+ this.direction = Direction.RIGHT;
+ };
+
+ Player.prototype.draw = function(context) {
+ var offsetX = Math.floor(this.animationStep / 15) * 32, offsetY = 0;
+
+ switch(this.direction) {
+ case Direction.UP:
+ offsetY = 48 * 3;
+ break;
+ case Direction.RIGHT:
+ offsetY = 48 * 2;
+ break;
+ case Direction.LEFT:
+ offsetY = 48;
+ break;
+ }
+
+ context.drawImage(this.image.data, offsetX, offsetY, 32, 48, this.pixelX, this.pixelY - 16, 32, 48);
+ };
+});
diff --git a/tests/resources/userdiff/before/file.php b/tests/resources/userdiff/before/file.php
new file mode 100644
index 0000000..63250ad
--- /dev/null
+++ b/tests/resources/userdiff/before/file.php
@@ -0,0 +1,49 @@
+<?php
+
+namespace Faker;
+
+/**
+ * Proxy for other generators, to return only unique values. Works with
+ * Faker\Generator\Base->unique()
+ */
+class UniqueGenerator
+{
+ protected $generator;
+ protected $maxRetries;
+ protected $uniques = array();
+
+ public function __construct(Generator $generator, $maxRetries)
+ {
+ $this->generator = $generator;
+ $this->maxRetries = $maxRetries;
+ }
+
+ /**
+ * Catch and proxy all generator calls but return only unique values
+ */
+ public function __get($attribute)
+ {
+ return $this->__call($attribute, array());
+ }
+
+ /**
+ * Catch and proxy all generator calls with arguments but return only unique values
+ */
+ public function __call($name, $arguments)
+ {
+ if (!isset($this->uniques[$name])) {
+ $this->uniques[$name] = array();
+ }
+ $i = 0;
+ do {
+ $res = call_user_func_array(array($this->generator, $name), $arguments);
+ $i++;
+ if ($i > $this->maxRetries) {
+ throw new \OverflowException(sprintf('Maximum retries of %d reached without finding a unique value', $this->maxRetries));
+ }
+ } while (in_array($res, $this->uniques[$name]));
+ $this->uniques[$name][]= $res;
+
+ return $res;
+ }
+}
diff --git a/tests/resources/userdiff/expected/driver/diff.html b/tests/resources/userdiff/expected/driver/diff.html
new file mode 100644
index 0000000..5a428e7
--- /dev/null
+++ b/tests/resources/userdiff/expected/driver/diff.html
@@ -0,0 +1,26 @@
+diff --git a/files/file.html b/files/file.html
+index 872d196..2320e2f 100644
+--- a/files/file.html
++++ b/files/file.html
+@@ -5,4 +5,4 @@ <h1 id="first section">
+ <li>item 1.1</li>
+- <li>item 1.2</li>
+- <li>item 1.3</li>
++ <li>item 1.2 changed</li>
++ <li>item 1.3 changed</li>
+ <li>item 1.4</li>
+@@ -13,2 +13,3 @@ <h1 id="first section">
+ <li>item 1.9</li>
++ <li>item 1.10 added</li>
+ </ol>
+@@ -23,3 +24,4 @@ <h1 id="second section">
+ <li>item 2.6</li>
+- <li>item 2.7</li>
++ <li>item 2.7 changed</li>
++ <li>item 2.7.1 added</li>
+ <li>item 2.8</li>
+@@ -35,4 +37,2 @@ <h1 id="third section">
+ <li>item 3.6</li>
+- <li>item 3.7</li>
+- <li>item 3.8</li>
+ </ol>
diff --git a/tests/resources/userdiff/expected/driver/diff.javascript b/tests/resources/userdiff/expected/driver/diff.javascript
new file mode 100644
index 0000000..4cefe5c
--- /dev/null
+++ b/tests/resources/userdiff/expected/driver/diff.javascript
@@ -0,0 +1,27 @@
+diff --git a/files/file.javascript b/files/file.javascript
+index 0965b37..5391797 100644
+--- a/files/file.javascript
++++ b/files/file.javascript
+@@ -4,4 +4,3 @@ function(require, exports, module)
+ var Key = require("./key")
+- , Direction = require("./direction")
+- , Image = require("./image");
++ , Direction = require("./direction");
+
+@@ -16,4 +15,4 @@ function Player(game)
+
+- this.pixelX = 0;
+- this.pixelY = 0;
++ this.pixelX = 10;
++ this.pixelY = 10;
+
+@@ -82,3 +81,3 @@ Player.prototype.moveUp = function()
+ Player.prototype.moveLeft = function() {
+- this.x -= 1;
++ this.x -= 5;
+ this.direction = Direction.LEFT;
+@@ -106,3 +105,3 @@ Player.prototype.draw = function(context)
+
+- context.drawImage(this.image.data, offsetX, offsetY, 32, 48, this.pixelX, this.pixelY - 16, 32, 48);
++ context.drawImage(this.image.data, offsetX, offsetY, 32, 48, this.pixelX, this.pixelY, 32, 48);
+ };
diff --git a/tests/resources/userdiff/expected/driver/diff.php b/tests/resources/userdiff/expected/driver/diff.php
new file mode 100644
index 0000000..9711b5b
--- /dev/null
+++ b/tests/resources/userdiff/expected/driver/diff.php
@@ -0,0 +1,26 @@
+diff --git a/files/file.php b/files/file.php
+index 63250ad..967d646 100644
+--- a/files/file.php
++++ b/files/file.php
+@@ -12,2 +12,3 @@ class UniqueGenerator
+ protected $maxRetries;
++ protected $moreStuff;
+ protected $uniques = array();
+@@ -17,3 +18,3 @@ public function __construct(Generator $generator, $maxRetries)
+ $this->generator = $generator;
+- $this->maxRetries = $maxRetries;
++ $this->maxRetries = $maxRetries + 1;
+ }
+@@ -33,10 +34,10 @@ public function __call($name, $arguments)
+ {
++ $i = 0;
+ if (!isset($this->uniques[$name])) {
+ $this->uniques[$name] = array();
+ }
+- $i = 0;
+ do {
+ $res = call_user_func_array(array($this->generator, $name), $arguments);
+ $i++;
+- if ($i > $this->maxRetries) {
++ if ($i >= $this->maxRetries) {
+ throw new \OverflowException(sprintf('Maximum retries of %d reached without finding a unique value', $this->maxRetries));
diff --git a/tests/resources/userdiff/expected/nodriver/diff.html b/tests/resources/userdiff/expected/nodriver/diff.html
new file mode 100644
index 0000000..2ea4b8a
--- /dev/null
+++ b/tests/resources/userdiff/expected/nodriver/diff.html
@@ -0,0 +1,26 @@
+diff --git a/files/file.html b/files/file.html
+index 872d196..2320e2f 100644
+--- a/files/file.html
++++ b/files/file.html
+@@ -5,4 +5,4 @@
+ <li>item 1.1</li>
+- <li>item 1.2</li>
+- <li>item 1.3</li>
++ <li>item 1.2 changed</li>
++ <li>item 1.3 changed</li>
+ <li>item 1.4</li>
+@@ -13,2 +13,3 @@
+ <li>item 1.9</li>
++ <li>item 1.10 added</li>
+ </ol>
+@@ -23,3 +24,4 @@
+ <li>item 2.6</li>
+- <li>item 2.7</li>
++ <li>item 2.7 changed</li>
++ <li>item 2.7.1 added</li>
+ <li>item 2.8</li>
+@@ -35,4 +37,2 @@
+ <li>item 3.6</li>
+- <li>item 3.7</li>
+- <li>item 3.8</li>
+ </ol>
diff --git a/tests/resources/userdiff/expected/nodriver/diff.javascript b/tests/resources/userdiff/expected/nodriver/diff.javascript
new file mode 100644
index 0000000..4bbd547
--- /dev/null
+++ b/tests/resources/userdiff/expected/nodriver/diff.javascript
@@ -0,0 +1,27 @@
+diff --git a/files/file.javascript b/files/file.javascript
+index 0965b37..5391797 100644
+--- a/files/file.javascript
++++ b/files/file.javascript
+@@ -4,4 +4,3 @@ define(function(require, exports, module) {
+ var Key = require("./key")
+- , Direction = require("./direction")
+- , Image = require("./image");
++ , Direction = require("./direction");
+
+@@ -16,4 +15,4 @@ define(function(require, exports, module) {
+
+- this.pixelX = 0;
+- this.pixelY = 0;
++ this.pixelX = 10;
++ this.pixelY = 10;
+
+@@ -82,3 +81,3 @@ define(function(require, exports, module) {
+ Player.prototype.moveLeft = function() {
+- this.x -= 1;
++ this.x -= 5;
+ this.direction = Direction.LEFT;
+@@ -106,3 +105,3 @@ define(function(require, exports, module) {
+
+- context.drawImage(this.image.data, offsetX, offsetY, 32, 48, this.pixelX, this.pixelY - 16, 32, 48);
++ context.drawImage(this.image.data, offsetX, offsetY, 32, 48, this.pixelX, this.pixelY, 32, 48);
+ };
diff --git a/tests/resources/userdiff/expected/nodriver/diff.php b/tests/resources/userdiff/expected/nodriver/diff.php
new file mode 100644
index 0000000..e77c094
--- /dev/null
+++ b/tests/resources/userdiff/expected/nodriver/diff.php
@@ -0,0 +1,26 @@
+diff --git a/files/file.php b/files/file.php
+index 63250ad..967d646 100644
+--- a/files/file.php
++++ b/files/file.php
+@@ -12,2 +12,3 @@ class UniqueGenerator
+ protected $maxRetries;
++ protected $moreStuff;
+ protected $uniques = array();
+@@ -17,3 +18,3 @@ class UniqueGenerator
+ $this->generator = $generator;
+- $this->maxRetries = $maxRetries;
++ $this->maxRetries = $maxRetries + 1;
+ }
+@@ -33,10 +34,10 @@ class UniqueGenerator
+ {
++ $i = 0;
+ if (!isset($this->uniques[$name])) {
+ $this->uniques[$name] = array();
+ }
+- $i = 0;
+ do {
+ $res = call_user_func_array(array($this->generator, $name), $arguments);
+ $i++;
+- if ($i > $this->maxRetries) {
++ if ($i >= $this->maxRetries) {
+ throw new \OverflowException(sprintf('Maximum retries of %d reached without finding a unique value', $this->maxRetries));
diff --git a/tests/resources/userdiff/files/file.html b/tests/resources/userdiff/files/file.html
new file mode 100644
index 0000000..2320e2f
--- /dev/null
+++ b/tests/resources/userdiff/files/file.html
@@ -0,0 +1,41 @@
+<html>
+<body>
+ <h1 id="first section">
+ <ol>
+ <li>item 1.1</li>
+ <li>item 1.2 changed</li>
+ <li>item 1.3 changed</li>
+ <li>item 1.4</li>
+ <li>item 1.5</li>
+ <li>item 1.6</li>
+ <li>item 1.7</li>
+ <li>item 1.8</li>
+ <li>item 1.9</li>
+ <li>item 1.10 added</li>
+ </ol>
+ </h1>
+ <h1 id="second section">
+ <ol>
+ <li>item 2.1</li>
+ <li>item 2.2</li>
+ <li>item 2.3</li>
+ <li>item 2.4</li>
+ <li>item 2.5</li>
+ <li>item 2.6</li>
+ <li>item 2.7 changed</li>
+ <li>item 2.7.1 added</li>
+ <li>item 2.8</li>
+ </ol>
+ </h1>
+ <h1 id="third section">
+ <ol>
+ <li>item 3.1</li>
+ <li>item 3.2</li>
+ <li>item 3.3</li>
+ <li>item 3.4</li>
+ <li>item 3.5</li>
+ <li>item 3.6</li>
+ </ol>
+ </h1>
+</body>
+</html>
diff --git a/tests/resources/userdiff/files/file.javascript b/tests/resources/userdiff/files/file.javascript
new file mode 100644
index 0000000..5391797
--- /dev/null
+++ b/tests/resources/userdiff/files/file.javascript
@@ -0,0 +1,108 @@
+define(function(require, exports, module) {
+ module.exports = Player;
+
+ var Key = require("./key")
+ , Direction = require("./direction");
+
+ function Player(game) {
+ this.game = game;
+
+ this.image = new Image("./assets/fighter.png");
+ this.game.resources.add(this.image);
+
+ this.x = 0;
+ this.y = 0;
+
+ this.pixelX = 10;
+ this.pixelY = 10;
+
+ this.animationStep = 0;
+ }
+
+ Player.prototype.update = function() {
+ if (!this.isWalking()) {
+ this.handleInput();
+ }
+
+ if (this.isWalking()) {
+ // Increase the animation step.
+ this.animationStep = ++this.animationStep % 60;
+
+ if (this.x * 32 > this.pixelX) {
+ this.pixelX++;
+ } else if (this.x * 32 < this.pixelX) {
+ this.pixelX--;
+ }
+
+ if (this.y * 32 > this.pixelY) {
+ this.pixelY++;
+ } else if (this.y * 32 < this.pixelY) {
+ this.pixelY--;
+ }
+ } else {
+ // Reset the animation step.
+ this.animationStep = 0;
+ }
+ };
+
+ Player.prototype.handleInput = function() {
+ var keyboard = this.game.keyboard, finalAction, action, inputs = {
+ 'moveDown': keyboard.isDown(Key.DOWN),
+ 'moveUp': keyboard.isDown(Key.UP),
+ 'moveLeft': keyboard.isDown(Key.LEFT),
+ 'moveRight': keyboard.isDown(Key.RIGHT)
+ };
+
+ for (action in inputs) {
+ if (inputs[action]) {
+ if (!finalAction || inputs[finalAction] < inputs[action]) {
+ finalAction = action;
+ }
+ }
+ }
+
+ this[finalAction] && this[finalAction]();
+ };
+
+ Player.prototype.isWalking = function() {
+ return this.x * 32 != this.pixelX || this.y * 32 != this.pixelY;
+ };
+
+ Player.prototype.moveDown = function() {
+ this.y += 1;
+ this.direction = Direction.DOWN;
+ };
+
+ Player.prototype.moveUp = function() {
+ this.y -= 1;
+ this.direction = Direction.UP;
+ };
+
+ Player.prototype.moveLeft = function() {
+ this.x -= 5;
+ this.direction = Direction.LEFT;
+ };
+
+ Player.prototype.moveRight = function() {
+ this.x += 1;
+ this.direction = Direction.RIGHT;
+ };
+
+ Player.prototype.draw = function(context) {
+ var offsetX = Math.floor(this.animationStep / 15) * 32, offsetY = 0;
+
+ switch(this.direction) {
+ case Direction.UP:
+ offsetY = 48 * 3;
+ break;
+ case Direction.RIGHT:
+ offsetY = 48 * 2;
+ break;
+ case Direction.LEFT:
+ offsetY = 48;
+ break;
+ }
+
+ context.drawImage(this.image.data, offsetX, offsetY, 32, 48, this.pixelX, this.pixelY, 32, 48);
+ };
+});
diff --git a/tests/resources/userdiff/files/file.php b/tests/resources/userdiff/files/file.php
new file mode 100644
index 0000000..967d646
--- /dev/null
+++ b/tests/resources/userdiff/files/file.php
@@ -0,0 +1,50 @@
+<?php
+
+namespace Faker;
+
+/**
+ * Proxy for other generators, to return only unique values. Works with
+ * Faker\Generator\Base->unique()
+ */
+class UniqueGenerator
+{
+ protected $generator;
+ protected $maxRetries;
+ protected $moreStuff;
+ protected $uniques = array();
+
+ public function __construct(Generator $generator, $maxRetries)
+ {
+ $this->generator = $generator;
+ $this->maxRetries = $maxRetries + 1;
+ }
+
+ /**
+ * Catch and proxy all generator calls but return only unique values
+ */
+ public function __get($attribute)
+ {
+ return $this->__call($attribute, array());
+ }
+
+ /**
+ * Catch and proxy all generator calls with arguments but return only unique values
+ */
+ public function __call($name, $arguments)
+ {
+ $i = 0;
+ if (!isset($this->uniques[$name])) {
+ $this->uniques[$name] = array();
+ }
+ do {
+ $res = call_user_func_array(array($this->generator, $name), $arguments);
+ $i++;
+ if ($i >= $this->maxRetries) {
+ throw new \OverflowException(sprintf('Maximum retries of %d reached without finding a unique value', $this->maxRetries));
+ }
+ } while (in_array($res, $this->uniques[$name]));
+ $this->uniques[$name][]= $res;
+
+ return $res;
+ }
+}
diff --git a/tests/resources/win32-forbidden/.gitted/HEAD b/tests/resources/win32-forbidden/.gitted/HEAD
new file mode 100644
index 0000000..cb089cd
--- /dev/null
+++ b/tests/resources/win32-forbidden/.gitted/HEAD
@@ -0,0 +1 @@
+ref: refs/heads/master
diff --git a/tests/resources/win32-forbidden/.gitted/config b/tests/resources/win32-forbidden/.gitted/config
new file mode 100644
index 0000000..6c9406b
--- /dev/null
+++ b/tests/resources/win32-forbidden/.gitted/config
@@ -0,0 +1,7 @@
+[core]
+ repositoryformatversion = 0
+ filemode = true
+ bare = false
+ logallrefupdates = true
+ ignorecase = true
+ precomposeunicode = true
diff --git a/tests/resources/win32-forbidden/.gitted/index b/tests/resources/win32-forbidden/.gitted/index
new file mode 100644
index 0000000..1202dd9
--- /dev/null
+++ b/tests/resources/win32-forbidden/.gitted/index
Binary files differ
diff --git a/tests/resources/win32-forbidden/.gitted/info/exclude b/tests/resources/win32-forbidden/.gitted/info/exclude
new file mode 100644
index 0000000..a5196d1
--- /dev/null
+++ b/tests/resources/win32-forbidden/.gitted/info/exclude
@@ -0,0 +1,6 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
diff --git a/tests/resources/win32-forbidden/.gitted/objects/10/68072702a28a82c78902cf5bf82c3864cf4356 b/tests/resources/win32-forbidden/.gitted/objects/10/68072702a28a82c78902cf5bf82c3864cf4356
new file mode 100644
index 0000000..2d3b31a
--- /dev/null
+++ b/tests/resources/win32-forbidden/.gitted/objects/10/68072702a28a82c78902cf5bf82c3864cf4356
Binary files differ
diff --git a/tests/resources/win32-forbidden/.gitted/objects/17/6a458f94e0ea5272ce67c36bf30b6be9caf623 b/tests/resources/win32-forbidden/.gitted/objects/17/6a458f94e0ea5272ce67c36bf30b6be9caf623
new file mode 100644
index 0000000..ef83166
--- /dev/null
+++ b/tests/resources/win32-forbidden/.gitted/objects/17/6a458f94e0ea5272ce67c36bf30b6be9caf623
Binary files differ
diff --git a/tests/resources/win32-forbidden/.gitted/objects/2d/7445a749d25269f32724aa621cb70b196bcc40 b/tests/resources/win32-forbidden/.gitted/objects/2d/7445a749d25269f32724aa621cb70b196bcc40
new file mode 100644
index 0000000..baddb1f
--- /dev/null
+++ b/tests/resources/win32-forbidden/.gitted/objects/2d/7445a749d25269f32724aa621cb70b196bcc40
Binary files differ
diff --git a/tests/resources/win32-forbidden/.gitted/objects/34/96991d72d500af36edef68bbfcccd1661d88db b/tests/resources/win32-forbidden/.gitted/objects/34/96991d72d500af36edef68bbfcccd1661d88db
new file mode 100644
index 0000000..71b6172
--- /dev/null
+++ b/tests/resources/win32-forbidden/.gitted/objects/34/96991d72d500af36edef68bbfcccd1661d88db
@@ -0,0 +1,3 @@
+x•Í
+!…[ûw„ãï†hÓ¢}/à8šB*8N½~½@guÎÇÕœS‡ Í¡7ïa¢
+©fš2ËÐ"s e.È%ŒÁQ —ŠØ½ÇÚ຾m[ákÞjÙúm—Gêq_N®æ3LBJÅ”FG:B¯Ýÿã ®„ùùäVROö Í¿Ò–j!èÖ=ö \ No newline at end of file
diff --git a/tests/resources/win32-forbidden/.gitted/objects/8f/45aad6f23b9509f8786c617e19c127ae76609a b/tests/resources/win32-forbidden/.gitted/objects/8f/45aad6f23b9509f8786c617e19c127ae76609a
new file mode 100644
index 0000000..8bcd980
--- /dev/null
+++ b/tests/resources/win32-forbidden/.gitted/objects/8f/45aad6f23b9509f8786c617e19c127ae76609a
@@ -0,0 +1,2 @@
+xÁA
+€ ÐÖâ/kÓ.ð ]`„™¾ŽhÞ¾÷"=â Ë•ò€e*’ ¨·UŠÂ+¶äMí%çý´O4ÊcÞ˱þá– \ No newline at end of file
diff --git a/tests/resources/win32-forbidden/.gitted/objects/da/623abd956bb2fd8052c708c7ed43f05d192d37 b/tests/resources/win32-forbidden/.gitted/objects/da/623abd956bb2fd8052c708c7ed43f05d192d37
new file mode 100644
index 0000000..9234623
--- /dev/null
+++ b/tests/resources/win32-forbidden/.gitted/objects/da/623abd956bb2fd8052c708c7ed43f05d192d37
Binary files differ
diff --git a/tests/resources/win32-forbidden/.gitted/objects/ea/c7621a652e5261ef1c1d3e7ae31b0d84fcbaba b/tests/resources/win32-forbidden/.gitted/objects/ea/c7621a652e5261ef1c1d3e7ae31b0d84fcbaba
new file mode 100644
index 0000000..32b3f02
--- /dev/null
+++ b/tests/resources/win32-forbidden/.gitted/objects/ea/c7621a652e5261ef1c1d3e7ae31b0d84fcbaba
@@ -0,0 +1,3 @@
+xM
+B!F»Š;BoW}BD“ÍÛ€¥
+>_m?ÛBgôqàãøVJ ³=FÀ ‰¤ÕdJTæqDd­BáæNå¼'Îì6Rëp ÛÜS+k«pŠÓþÖå™GÚÜÁ·rAR*Tz!Øó ›vVGü÷Ïn5l_Ðã;¯¹Uö>H \ No newline at end of file
diff --git a/tests/resources/win32-forbidden/.gitted/refs/heads/master b/tests/resources/win32-forbidden/.gitted/refs/heads/master
new file mode 100644
index 0000000..47fce0f
--- /dev/null
+++ b/tests/resources/win32-forbidden/.gitted/refs/heads/master
@@ -0,0 +1 @@
+3496991d72d500af36edef68bbfcccd1661d88db
diff --git a/tests/util/CMakeLists.txt b/tests/util/CMakeLists.txt
new file mode 100644
index 0000000..232590f
--- /dev/null
+++ b/tests/util/CMakeLists.txt
@@ -0,0 +1,68 @@
+# util: the unit tests for libgit2's utility library
+
+set(Python_ADDITIONAL_VERSIONS 3 2.7)
+find_package(PythonInterp)
+
+if(NOT PYTHONINTERP_FOUND)
+ message(FATAL_ERROR "Could not find a python interpeter, which is needed to build the tests. "
+ "Make sure python is available, or pass -DBUILD_TESTS=OFF to skip building the tests")
+ENDIF()
+
+set(CLAR_PATH "${libgit2_SOURCE_DIR}/tests/clar")
+set(CLAR_FIXTURES "${libgit2_SOURCE_DIR}/tests/resources/")
+set(TEST_PATH "${CMAKE_CURRENT_SOURCE_DIR}")
+add_definitions(-DCLAR_FIXTURE_PATH=\"${CLAR_FIXTURES}\")
+add_definitions(-DCLAR_TMPDIR=\"libgit2_tests\")
+add_definitions(-DCLAR_WIN32_LONGPATHS)
+add_definitions(-D_FILE_OFFSET_BITS=64)
+
+# Ensure that we do not use deprecated functions internally
+add_definitions(-DGIT_DEPRECATE_HARD)
+
+set(TEST_INCLUDES "${CLAR_PATH}" "${TEST_PATH}" "${CMAKE_CURRENT_BINARY_DIR}")
+file(GLOB_RECURSE SRC_TEST ${TEST_PATH}/*.c ${TEST_PATH}/*.h ${TEST_PATH}/*/*.c ${TEST_PATH}/*/*.h)
+file(GLOB_RECURSE SRC_CLAR ${CLAR_PATH}/*.c ${CLAR_PATH}/*.h)
+
+if(MSVC_IDE)
+ list(APPEND SRC_TEST "precompiled.c")
+endif()
+
+add_custom_command(
+ OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/clar.suite ${CMAKE_CURRENT_BINARY_DIR}/clar_suite.h
+ COMMAND ${PYTHON_EXECUTABLE} ${CLAR_PATH}/generate.py -o "${CMAKE_CURRENT_BINARY_DIR}" -f .
+ DEPENDS ${SRC_TEST}
+ WORKING_DIRECTORY ${TEST_PATH})
+
+set_source_files_properties(
+ ${CLAR_PATH}/clar.c
+ PROPERTIES OBJECT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/clar.suite)
+
+add_executable(util_tests ${SRC_CLAR} ${SRC_TEST} ${LIBGIT2_OBJECTS})
+
+set_target_properties(util_tests PROPERTIES C_STANDARD 90)
+set_target_properties(util_tests PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${libgit2_BINARY_DIR})
+
+target_include_directories(util_tests PRIVATE ${TEST_INCLUDES} ${LIBGIT2_INCLUDES} ${LIBGIT2_DEPENDENCY_INCLUDES})
+target_include_directories(util_tests SYSTEM PRIVATE ${LIBGIT2_SYSTEM_INCLUDES})
+target_link_libraries(util_tests ${LIBGIT2_SYSTEM_LIBS})
+
+ide_split_sources(util_tests)
+
+#
+# Old versions of gcc require us to declare our test functions; don't do
+# this on newer compilers to avoid unnecessary recompilation.
+#
+if(CMAKE_COMPILER_IS_GNUCC AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0)
+ target_compile_options(util_tests PRIVATE -include "clar_suite.h")
+endif()
+
+if(MSVC_IDE)
+ # Precompiled headers
+ set_target_properties(util_tests PROPERTIES COMPILE_FLAGS "/Yuprecompiled.h /FIprecompiled.h")
+ set_source_files_properties("precompiled.c" COMPILE_FLAGS "/Ycprecompiled.h")
+endif()
+
+enable_testing()
+
+include(AddClarTest)
+add_clar_test(util_tests util -v)
diff --git a/tests/util/alloc.c b/tests/util/alloc.c
new file mode 100644
index 0000000..492394a
--- /dev/null
+++ b/tests/util/alloc.c
@@ -0,0 +1,68 @@
+#include "clar_libgit2.h"
+#include "clar_libgit2_alloc.h"
+#include "alloc.h"
+
+void test_alloc__cleanup(void)
+{
+ cl_alloc_reset();
+}
+
+void test_alloc__oom(void)
+{
+ void *ptr = NULL;
+
+ cl_alloc_limit(0);
+
+ cl_assert(git__malloc(1) == NULL);
+ cl_assert(git__calloc(1, 1) == NULL);
+ cl_assert(git__realloc(ptr, 1) == NULL);
+ cl_assert(git__strdup("test") == NULL);
+ cl_assert(git__strndup("test", 4) == NULL);
+}
+
+void test_alloc__single_byte_is_exhausted(void)
+{
+ void *ptr;
+
+ cl_alloc_limit(1);
+
+ cl_assert(ptr = git__malloc(1));
+ cl_assert(git__malloc(1) == NULL);
+ git__free(ptr);
+}
+
+void test_alloc__free_replenishes_byte(void)
+{
+ void *ptr;
+
+ cl_alloc_limit(1);
+
+ cl_assert(ptr = git__malloc(1));
+ cl_assert(git__malloc(1) == NULL);
+ git__free(ptr);
+ cl_assert(ptr = git__malloc(1));
+ git__free(ptr);
+}
+
+void test_alloc__realloc(void)
+{
+ char *ptr = NULL;
+
+ cl_alloc_limit(3);
+
+ cl_assert(ptr = git__realloc(ptr, 1));
+ *ptr = 'x';
+
+ cl_assert(ptr = git__realloc(ptr, 1));
+ cl_assert_equal_i(*ptr, 'x');
+
+ cl_assert(ptr = git__realloc(ptr, 2));
+ cl_assert_equal_i(*ptr, 'x');
+
+ cl_assert(git__realloc(ptr, 2) == NULL);
+
+ cl_assert(ptr = git__realloc(ptr, 1));
+ cl_assert_equal_i(*ptr, 'x');
+
+ git__free(ptr);
+}
diff --git a/tests/util/array.c b/tests/util/array.c
new file mode 100644
index 0000000..39fbc81
--- /dev/null
+++ b/tests/util/array.c
@@ -0,0 +1,57 @@
+#include "clar_libgit2.h"
+#include "array.h"
+
+static int int_lookup(const void *k, const void *a)
+{
+ const int *one = (const int *)k;
+ int *two = (int *)a;
+
+ return *one - *two;
+}
+
+#define expect_pos(k, n, ret) \
+ key = (k); \
+ cl_assert_equal_i((ret), \
+ git_array_search(&p, integers, int_lookup, &key)); \
+ cl_assert_equal_i((n), p);
+
+void test_array__bsearch2(void)
+{
+ git_array_t(int) integers = GIT_ARRAY_INIT;
+ int *i, key;
+ size_t p;
+
+ i = git_array_alloc(integers); *i = 2;
+ i = git_array_alloc(integers); *i = 3;
+ i = git_array_alloc(integers); *i = 5;
+ i = git_array_alloc(integers); *i = 7;
+ i = git_array_alloc(integers); *i = 7;
+ i = git_array_alloc(integers); *i = 8;
+ i = git_array_alloc(integers); *i = 13;
+ i = git_array_alloc(integers); *i = 21;
+ i = git_array_alloc(integers); *i = 25;
+ i = git_array_alloc(integers); *i = 42;
+ i = git_array_alloc(integers); *i = 69;
+ i = git_array_alloc(integers); *i = 121;
+ i = git_array_alloc(integers); *i = 256;
+ i = git_array_alloc(integers); *i = 512;
+ i = git_array_alloc(integers); *i = 513;
+ i = git_array_alloc(integers); *i = 514;
+ i = git_array_alloc(integers); *i = 516;
+ i = git_array_alloc(integers); *i = 516;
+ i = git_array_alloc(integers); *i = 517;
+
+ /* value to search for, expected position, return code */
+ expect_pos(3, 1, GIT_OK);
+ expect_pos(2, 0, GIT_OK);
+ expect_pos(1, 0, GIT_ENOTFOUND);
+ expect_pos(25, 8, GIT_OK);
+ expect_pos(26, 9, GIT_ENOTFOUND);
+ expect_pos(42, 9, GIT_OK);
+ expect_pos(50, 10, GIT_ENOTFOUND);
+ expect_pos(68, 10, GIT_ENOTFOUND);
+ expect_pos(256, 12, GIT_OK);
+
+ git_array_clear(integers);
+}
+
diff --git a/tests/util/assert.c b/tests/util/assert.c
new file mode 100644
index 0000000..3babc47
--- /dev/null
+++ b/tests/util/assert.c
@@ -0,0 +1,117 @@
+#ifdef GIT_ASSERT_HARD
+# undef GIT_ASSERT_HARD
+#endif
+
+#define GIT_ASSERT_HARD 0
+
+#include "clar_libgit2.h"
+
+static const char *hello_world = "hello, world";
+static const char *fail = "FAIL";
+
+static int dummy_fn(const char *myarg)
+{
+ GIT_ASSERT_ARG(myarg);
+ GIT_ASSERT_ARG(myarg != hello_world);
+ return 0;
+}
+
+static const char *fn_returns_string(const char *myarg)
+{
+ GIT_ASSERT_ARG_WITH_RETVAL(myarg, fail);
+ GIT_ASSERT_ARG_WITH_RETVAL(myarg != hello_world, fail);
+
+ return myarg;
+}
+
+static int bad_math(void)
+{
+ GIT_ASSERT(1 + 1 == 3);
+ return 42;
+}
+
+static const char *bad_returns_string(void)
+{
+ GIT_ASSERT_WITH_RETVAL(1 + 1 == 3, NULL);
+ return hello_world;
+}
+
+static int has_cleanup(void)
+{
+ int error = 42;
+
+ GIT_ASSERT_WITH_CLEANUP(1 + 1 == 3, {
+ error = 99;
+ goto foobar;
+ });
+
+ return 0;
+
+foobar:
+ return error;
+}
+
+void test_assert__argument(void)
+{
+ cl_git_fail(dummy_fn(NULL));
+ cl_assert(git_error_last());
+ cl_assert_equal_i(GIT_ERROR_INVALID, git_error_last()->klass);
+ cl_assert_equal_s("invalid argument: 'myarg'", git_error_last()->message);
+
+ cl_git_fail(dummy_fn(hello_world));
+ cl_assert(git_error_last());
+ cl_assert_equal_i(GIT_ERROR_INVALID, git_error_last()->klass);
+ cl_assert_equal_s("invalid argument: 'myarg != hello_world'", git_error_last()->message);
+
+ cl_git_pass(dummy_fn("foo"));
+}
+
+void test_assert__argument_with_non_int_return_type(void)
+{
+ const char *foo = "foo";
+
+ cl_assert_equal_p(fail, fn_returns_string(NULL));
+ cl_assert_equal_i(GIT_ERROR_INVALID, git_error_last()->klass);
+ cl_assert_equal_s("invalid argument: 'myarg'", git_error_last()->message);
+
+ cl_assert_equal_p(fail, fn_returns_string(hello_world));
+ cl_assert_equal_i(GIT_ERROR_INVALID, git_error_last()->klass);
+ cl_assert_equal_s("invalid argument: 'myarg != hello_world'", git_error_last()->message);
+
+ cl_assert_equal_p(foo, fn_returns_string(foo));
+}
+
+void test_assert__argument_with_void_return_type(void)
+{
+ const char *foo = "foo";
+
+ git_error_clear();
+ fn_returns_string(hello_world);
+ cl_assert_equal_i(GIT_ERROR_INVALID, git_error_last()->klass);
+ cl_assert_equal_s("invalid argument: 'myarg != hello_world'", git_error_last()->message);
+
+ git_error_clear();
+ cl_assert_equal_p(foo, fn_returns_string(foo));
+ cl_assert_equal_p(NULL, git_error_last());
+}
+
+void test_assert__internal(void)
+{
+ cl_git_fail(bad_math());
+ cl_assert(git_error_last());
+ cl_assert_equal_i(GIT_ERROR_INTERNAL, git_error_last()->klass);
+ cl_assert_equal_s("unrecoverable internal error: '1 + 1 == 3'", git_error_last()->message);
+
+ cl_assert_equal_p(NULL, bad_returns_string());
+ cl_assert(git_error_last());
+ cl_assert_equal_i(GIT_ERROR_INTERNAL, git_error_last()->klass);
+ cl_assert_equal_s("unrecoverable internal error: '1 + 1 == 3'", git_error_last()->message);
+}
+
+void test_assert__with_cleanup(void)
+{
+ cl_git_fail_with(99, has_cleanup());
+ cl_assert(git_error_last());
+ cl_assert_equal_i(GIT_ERROR_INTERNAL, git_error_last()->klass);
+ cl_assert_equal_s("unrecoverable internal error: '1 + 1 == 3'", git_error_last()->message);
+}
diff --git a/tests/util/bitvec.c b/tests/util/bitvec.c
new file mode 100644
index 0000000..f784589
--- /dev/null
+++ b/tests/util/bitvec.c
@@ -0,0 +1,64 @@
+#include "clar_libgit2.h"
+#include "bitvec.h"
+
+#if 0
+static void print_bitvec(git_bitvec *bv)
+{
+ int b;
+
+ if (!bv->length) {
+ for (b = 63; b >= 0; --b)
+ fprintf(stderr, "%d", (bv->u.bits & (1ul << b)) ? 1 : 0);
+ } else {
+ for (b = bv->length * 8; b >= 0; --b)
+ fprintf(stderr, "%d", (bv->u.ptr[b >> 3] & (b & 0x0ff)) ? 1 : 0);
+ }
+ fprintf(stderr, "\n");
+}
+#endif
+
+static void set_some_bits(git_bitvec *bv, size_t length)
+{
+ size_t i;
+
+ for (i = 0; i < length; ++i) {
+ if (i % 3 == 0 || i % 7 == 0)
+ git_bitvec_set(bv, i, true);
+ }
+}
+
+static void check_some_bits(git_bitvec *bv, size_t length)
+{
+ size_t i;
+
+ for (i = 0; i < length; ++i)
+ cl_assert_equal_b(i % 3 == 0 || i % 7 == 0, git_bitvec_get(bv, i));
+}
+
+void test_bitvec__0(void)
+{
+ git_bitvec bv;
+
+ cl_git_pass(git_bitvec_init(&bv, 32));
+ set_some_bits(&bv, 16);
+ check_some_bits(&bv, 16);
+ git_bitvec_clear(&bv);
+ set_some_bits(&bv, 32);
+ check_some_bits(&bv, 32);
+ git_bitvec_clear(&bv);
+ set_some_bits(&bv, 64);
+ check_some_bits(&bv, 64);
+ git_bitvec_free(&bv);
+
+ cl_git_pass(git_bitvec_init(&bv, 128));
+ set_some_bits(&bv, 32);
+ check_some_bits(&bv, 32);
+ set_some_bits(&bv, 128);
+ check_some_bits(&bv, 128);
+ git_bitvec_free(&bv);
+
+ cl_git_pass(git_bitvec_init(&bv, 4000));
+ set_some_bits(&bv, 4000);
+ check_some_bits(&bv, 4000);
+ git_bitvec_free(&bv);
+}
diff --git a/tests/util/copy.c b/tests/util/copy.c
new file mode 100644
index 0000000..2613730
--- /dev/null
+++ b/tests/util/copy.c
@@ -0,0 +1,162 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+#include "posix.h"
+
+void test_copy__file(void)
+{
+ struct stat st = {0};
+ const char *content = "This is some stuff to copy\n";
+
+ cl_git_mkfile("copy_me", content);
+
+ cl_git_pass(git_futils_cp("copy_me", "copy_me_two", 0664));
+
+ cl_git_pass(git_fs_path_lstat("copy_me_two", &st));
+ cl_assert(S_ISREG(st.st_mode));
+
+ if (!cl_is_env_set("GITTEST_FLAKY_STAT"))
+ cl_assert_equal_sz(strlen(content), (size_t)st.st_size);
+
+ cl_git_pass(p_unlink("copy_me_two"));
+ cl_git_pass(p_unlink("copy_me"));
+}
+
+void test_copy__file_in_dir(void)
+{
+ struct stat st = {0};
+ const char *content = "This is some other stuff to copy\n";
+
+ cl_git_pass(git_futils_mkdir("an_dir/in_a_dir", 0775, GIT_MKDIR_PATH));
+ cl_git_mkfile("an_dir/in_a_dir/copy_me", content);
+ cl_assert(git_fs_path_isdir("an_dir"));
+
+ cl_git_pass(git_futils_mkpath2file
+ ("an_dir/second_dir/and_more/copy_me_two", 0775));
+
+ cl_git_pass(git_futils_cp
+ ("an_dir/in_a_dir/copy_me",
+ "an_dir/second_dir/and_more/copy_me_two",
+ 0664));
+
+ cl_git_pass(git_fs_path_lstat("an_dir/second_dir/and_more/copy_me_two", &st));
+ cl_assert(S_ISREG(st.st_mode));
+
+ if (!cl_is_env_set("GITTEST_FLAKY_STAT"))
+ cl_assert_equal_sz(strlen(content), (size_t)st.st_size);
+
+ cl_git_pass(git_futils_rmdir_r("an_dir", NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_assert(!git_fs_path_isdir("an_dir"));
+}
+
+#ifndef GIT_WIN32
+static void assert_hard_link(const char *path)
+{
+ /* we assert this by checking that there's more than one link to the file */
+ struct stat st;
+
+ cl_assert(git_fs_path_isfile(path));
+ cl_git_pass(p_stat(path, &st));
+ cl_assert(st.st_nlink > 1);
+}
+#endif
+
+void test_copy__tree(void)
+{
+ struct stat st;
+ const char *content = "File content\n";
+
+ cl_git_pass(git_futils_mkdir("src/b", 0775, GIT_MKDIR_PATH));
+ cl_git_pass(git_futils_mkdir("src/c/d", 0775, GIT_MKDIR_PATH));
+ cl_git_pass(git_futils_mkdir("src/c/e", 0775, GIT_MKDIR_PATH));
+
+ cl_git_mkfile("src/f1", content);
+ cl_git_mkfile("src/b/f2", content);
+ cl_git_mkfile("src/c/f3", content);
+ cl_git_mkfile("src/c/d/f4", content);
+ cl_git_mkfile("src/c/d/.f5", content);
+
+#ifndef GIT_WIN32
+ cl_assert(p_symlink("../../b/f2", "src/c/d/l1") == 0);
+#endif
+
+ cl_assert(git_fs_path_isdir("src"));
+ cl_assert(git_fs_path_isdir("src/b"));
+ cl_assert(git_fs_path_isdir("src/c/d"));
+ cl_assert(git_fs_path_isfile("src/c/d/f4"));
+
+ /* copy with no empty dirs, yes links, no dotfiles, no overwrite */
+
+ cl_git_pass(
+ git_futils_cp_r("src", "t1", GIT_CPDIR_COPY_SYMLINKS, 0) );
+
+ cl_assert(git_fs_path_isdir("t1"));
+ cl_assert(git_fs_path_isdir("t1/b"));
+ cl_assert(git_fs_path_isdir("t1/c"));
+ cl_assert(git_fs_path_isdir("t1/c/d"));
+ cl_assert(!git_fs_path_isdir("t1/c/e"));
+
+ cl_assert(git_fs_path_isfile("t1/f1"));
+ cl_assert(git_fs_path_isfile("t1/b/f2"));
+ cl_assert(git_fs_path_isfile("t1/c/f3"));
+ cl_assert(git_fs_path_isfile("t1/c/d/f4"));
+ cl_assert(!git_fs_path_isfile("t1/c/d/.f5"));
+
+ memset(&st, 0, sizeof(struct stat));
+ cl_git_pass(git_fs_path_lstat("t1/c/f3", &st));
+ cl_assert(S_ISREG(st.st_mode));
+
+ if (!cl_is_env_set("GITTEST_FLAKY_STAT"))
+ cl_assert_equal_sz(strlen(content), (size_t)st.st_size);
+
+#ifndef GIT_WIN32
+ memset(&st, 0, sizeof(struct stat));
+ cl_git_pass(git_fs_path_lstat("t1/c/d/l1", &st));
+ cl_assert(S_ISLNK(st.st_mode));
+#endif
+
+ cl_git_pass(git_futils_rmdir_r("t1", NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_assert(!git_fs_path_isdir("t1"));
+
+ /* copy with empty dirs, no links, yes dotfiles, no overwrite */
+
+ cl_git_pass(
+ git_futils_cp_r("src", "t2", GIT_CPDIR_CREATE_EMPTY_DIRS | GIT_CPDIR_COPY_DOTFILES, 0) );
+
+ cl_assert(git_fs_path_isdir("t2"));
+ cl_assert(git_fs_path_isdir("t2/b"));
+ cl_assert(git_fs_path_isdir("t2/c"));
+ cl_assert(git_fs_path_isdir("t2/c/d"));
+ cl_assert(git_fs_path_isdir("t2/c/e"));
+
+ cl_assert(git_fs_path_isfile("t2/f1"));
+ cl_assert(git_fs_path_isfile("t2/b/f2"));
+ cl_assert(git_fs_path_isfile("t2/c/f3"));
+ cl_assert(git_fs_path_isfile("t2/c/d/f4"));
+ cl_assert(git_fs_path_isfile("t2/c/d/.f5"));
+
+#ifndef GIT_WIN32
+ memset(&st, 0, sizeof(struct stat));
+ cl_git_fail(git_fs_path_lstat("t2/c/d/l1", &st));
+#endif
+
+ cl_git_pass(git_futils_rmdir_r("t2", NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_assert(!git_fs_path_isdir("t2"));
+
+#ifndef GIT_WIN32
+ cl_git_pass(git_futils_cp_r("src", "t3", GIT_CPDIR_CREATE_EMPTY_DIRS | GIT_CPDIR_LINK_FILES, 0));
+ cl_assert(git_fs_path_isdir("t3"));
+
+ cl_assert(git_fs_path_isdir("t3"));
+ cl_assert(git_fs_path_isdir("t3/b"));
+ cl_assert(git_fs_path_isdir("t3/c"));
+ cl_assert(git_fs_path_isdir("t3/c/d"));
+ cl_assert(git_fs_path_isdir("t3/c/e"));
+
+ assert_hard_link("t3/f1");
+ assert_hard_link("t3/b/f2");
+ assert_hard_link("t3/c/f3");
+ assert_hard_link("t3/c/d/f4");
+#endif
+
+ cl_git_pass(git_futils_rmdir_r("src", NULL, GIT_RMDIR_REMOVE_FILES));
+}
diff --git a/tests/util/crlf.h b/tests/util/crlf.h
new file mode 100644
index 0000000..786edfc
--- /dev/null
+++ b/tests/util/crlf.h
@@ -0,0 +1,30 @@
+#ifndef INCLUDE_filter_crlf_h__
+#define INCLUDE_filter_crlf_h__
+
+/*
+ * file content for files in the resources/crlf repository
+ */
+
+#define UTF8_BOM "\xEF\xBB\xBF"
+
+#define ALL_CRLF_TEXT_RAW "crlf\r\ncrlf\r\ncrlf\r\ncrlf\r\n"
+#define ALL_LF_TEXT_RAW "lf\nlf\nlf\nlf\nlf\n"
+#define MORE_CRLF_TEXT_RAW "crlf\r\ncrlf\r\nlf\ncrlf\r\ncrlf\r\n"
+#define MORE_LF_TEXT_RAW "lf\nlf\ncrlf\r\nlf\nlf\n"
+
+#define ALL_CRLF_TEXT_AS_CRLF ALL_CRLF_TEXT_RAW
+#define ALL_LF_TEXT_AS_CRLF "lf\r\nlf\r\nlf\r\nlf\r\nlf\r\n"
+#define MORE_CRLF_TEXT_AS_CRLF "crlf\r\ncrlf\r\nlf\r\ncrlf\r\ncrlf\r\n"
+#define MORE_LF_TEXT_AS_CRLF "lf\r\nlf\r\ncrlf\r\nlf\r\nlf\r\n"
+
+#define ALL_CRLF_TEXT_AS_LF "crlf\ncrlf\ncrlf\ncrlf\n"
+#define ALL_LF_TEXT_AS_LF ALL_LF_TEXT_RAW
+#define MORE_CRLF_TEXT_AS_LF "crlf\ncrlf\nlf\ncrlf\ncrlf\n"
+#define MORE_LF_TEXT_AS_LF "lf\nlf\ncrlf\nlf\nlf\n"
+
+#define FEW_UTF8_CRLF_RAW "\xe2\x9a\xbdThe rest is ASCII01.\r\nThe rest is ASCII02.\r\nThe rest is ASCII03.\r\nThe rest is ASCII04.\r\nThe rest is ASCII05.\r\nThe rest is ASCII06.\r\nThe rest is ASCII07.\r\nThe rest is ASCII08.\r\nThe rest is ASCII09.\r\nThe rest is ASCII10.\r\nThe rest is ASCII11.\r\nThe rest is ASCII12.\r\nThe rest is ASCII13.\r\nThe rest is ASCII14.\r\nThe rest is ASCII15.\r\nThe rest is ASCII16.\r\nThe rest is ASCII17.\r\nThe rest is ASCII18.\r\nThe rest is ASCII19.\r\nThe rest is ASCII20.\r\nThe rest is ASCII21.\r\nThe rest is ASCII22.\r\n"
+#define FEW_UTF8_LF_RAW "\xe2\x9a\xbdThe rest is ASCII01.\nThe rest is ASCII02.\nThe rest is ASCII03.\nThe rest is ASCII04.\nThe rest is ASCII05.\nThe rest is ASCII06.\nThe rest is ASCII07.\nThe rest is ASCII08.\nThe rest is ASCII09.\nThe rest is ASCII10.\nThe rest is ASCII11.\nThe rest is ASCII12.\nThe rest is ASCII13.\nThe rest is ASCII14.\nThe rest is ASCII15.\nThe rest is ASCII16.\nThe rest is ASCII17.\nThe rest is ASCII18.\nThe rest is ASCII19.\nThe rest is ASCII20.\nThe rest is ASCII21.\nThe rest is ASCII22.\n"
+#define MANY_UTF8_CRLF_RAW "Lets sing!\r\n\xe2\x99\xab\xe2\x99\xaa\xe2\x99\xac\xe2\x99\xa9\r\nEat food\r\n\xf0\x9f\x8d\x85\xf0\x9f\x8d\x95\r\n"
+#define MANY_UTF8_LF_RAW "Lets sing!\n\xe2\x99\xab\xe2\x99\xaa\xe2\x99\xac\xe2\x99\xa9\nEat food\n\xf0\x9f\x8d\x85\xf0\x9f\x8d\x95\n"
+
+#endif
diff --git a/tests/util/dirent.c b/tests/util/dirent.c
new file mode 100644
index 0000000..5840c67
--- /dev/null
+++ b/tests/util/dirent.c
@@ -0,0 +1,306 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+
+typedef struct name_data {
+ int count; /* return count */
+ char *name; /* filename */
+} name_data;
+
+typedef struct walk_data {
+ char *sub; /* sub-directory name */
+ name_data *names; /* name state data */
+ git_str path;
+} walk_data;
+
+
+static char *top_dir = "dir-walk";
+static walk_data *state_loc;
+
+static void setup(walk_data *d)
+{
+ name_data *n;
+
+ cl_must_pass(p_mkdir(top_dir, 0777));
+
+ cl_must_pass(p_chdir(top_dir));
+
+ if (strcmp(d->sub, ".") != 0)
+ cl_must_pass(p_mkdir(d->sub, 0777));
+
+ cl_git_pass(git_str_sets(&d->path, d->sub));
+
+ state_loc = d;
+
+ for (n = d->names; n->name; n++) {
+ git_file fd = p_creat(n->name, 0666);
+ cl_assert(fd >= 0);
+ p_close(fd);
+ n->count = 0;
+ }
+}
+
+static void dirent_cleanup__cb(void *_d)
+{
+ walk_data *d = _d;
+ name_data *n;
+
+ for (n = d->names; n->name; n++) {
+ cl_must_pass(p_unlink(n->name));
+ }
+
+ if (strcmp(d->sub, ".") != 0)
+ cl_must_pass(p_rmdir(d->sub));
+
+ cl_must_pass(p_chdir(".."));
+
+ cl_must_pass(p_rmdir(top_dir));
+
+ git_str_dispose(&d->path);
+}
+
+static void check_counts(walk_data *d)
+{
+ name_data *n;
+
+ for (n = d->names; n->name; n++) {
+ cl_assert(n->count == 1);
+ }
+}
+
+static int update_count(name_data *data, const char *name)
+{
+ name_data *n;
+
+ for (n = data; n->name; n++) {
+ if (!strcmp(n->name, name)) {
+ n->count++;
+ return 0;
+ }
+ }
+
+ return GIT_ERROR;
+}
+
+static int one_entry(void *state, git_str *path)
+{
+ walk_data *d = (walk_data *) state;
+
+ if (state != state_loc)
+ return GIT_ERROR;
+
+ if (path != &d->path)
+ return GIT_ERROR;
+
+ return update_count(d->names, path->ptr);
+}
+
+
+static name_data dot_names[] = {
+ { 0, "./a" },
+ { 0, "./asdf" },
+ { 0, "./pack-foo.pack" },
+ { 0, NULL }
+};
+static walk_data dot = {
+ ".",
+ dot_names,
+ GIT_STR_INIT
+};
+
+/* make sure that the '.' folder is not traversed */
+void test_dirent__dont_traverse_dot(void)
+{
+ cl_set_cleanup(&dirent_cleanup__cb, &dot);
+ setup(&dot);
+
+ cl_git_pass(git_fs_path_direach(&dot.path, 0, one_entry, &dot));
+
+ check_counts(&dot);
+}
+
+
+static name_data sub_names[] = {
+ { 0, "sub/a" },
+ { 0, "sub/asdf" },
+ { 0, "sub/pack-foo.pack" },
+ { 0, NULL }
+};
+static walk_data sub = {
+ "sub",
+ sub_names,
+ GIT_STR_INIT
+};
+
+/* traverse a subfolder */
+void test_dirent__traverse_subfolder(void)
+{
+ cl_set_cleanup(&dirent_cleanup__cb, &sub);
+ setup(&sub);
+
+ cl_git_pass(git_fs_path_direach(&sub.path, 0, one_entry, &sub));
+
+ check_counts(&sub);
+}
+
+
+static walk_data sub_slash = {
+ "sub/",
+ sub_names,
+ GIT_STR_INIT
+};
+
+/* traverse a slash-terminated subfolder */
+void test_dirent__traverse_slash_terminated_folder(void)
+{
+ cl_set_cleanup(&dirent_cleanup__cb, &sub_slash);
+ setup(&sub_slash);
+
+ cl_git_pass(git_fs_path_direach(&sub_slash.path, 0, one_entry, &sub_slash));
+
+ check_counts(&sub_slash);
+}
+
+
+static name_data empty_names[] = {
+ { 0, NULL }
+};
+static walk_data empty = {
+ "empty",
+ empty_names,
+ GIT_STR_INIT
+};
+
+/* make sure that empty folders are not traversed */
+void test_dirent__dont_traverse_empty_folders(void)
+{
+ cl_set_cleanup(&dirent_cleanup__cb, &empty);
+ setup(&empty);
+
+ cl_git_pass(git_fs_path_direach(&empty.path, 0, one_entry, &empty));
+
+ check_counts(&empty);
+
+ /* make sure callback not called */
+ cl_assert(git_fs_path_is_empty_dir(empty.path.ptr));
+}
+
+static name_data odd_names[] = {
+ { 0, "odd/.a" },
+ { 0, "odd/..c" },
+ /* the following don't work on cygwin/win32 */
+ /* { 0, "odd/.b." }, */
+ /* { 0, "odd/..d.." }, */
+ { 0, NULL }
+};
+static walk_data odd = {
+ "odd",
+ odd_names,
+ GIT_STR_INIT
+};
+
+/* make sure that strange looking filenames ('..c') are traversed */
+void test_dirent__traverse_weird_filenames(void)
+{
+ cl_set_cleanup(&dirent_cleanup__cb, &odd);
+ setup(&odd);
+
+ cl_git_pass(git_fs_path_direach(&odd.path, 0, one_entry, &odd));
+
+ check_counts(&odd);
+}
+
+/* test filename length limits */
+void test_dirent__length_limits(void)
+{
+ char *big_filename = (char *)git__malloc(FILENAME_MAX + 1);
+ memset(big_filename, 'a', FILENAME_MAX + 1);
+ big_filename[FILENAME_MAX] = 0;
+
+ cl_must_fail(p_creat(big_filename, 0666));
+
+ git__free(big_filename);
+}
+
+void test_dirent__empty_dir(void)
+{
+ cl_must_pass(p_mkdir("empty_dir", 0777));
+ cl_assert(git_fs_path_is_empty_dir("empty_dir"));
+
+ cl_git_mkfile("empty_dir/content", "whatever\n");
+ cl_assert(!git_fs_path_is_empty_dir("empty_dir"));
+ cl_assert(!git_fs_path_is_empty_dir("empty_dir/content"));
+
+ cl_must_pass(p_unlink("empty_dir/content"));
+
+ cl_must_pass(p_mkdir("empty_dir/content", 0777));
+ cl_assert(!git_fs_path_is_empty_dir("empty_dir"));
+ cl_assert(git_fs_path_is_empty_dir("empty_dir/content"));
+
+ cl_must_pass(p_rmdir("empty_dir/content"));
+
+ cl_must_pass(p_rmdir("empty_dir"));
+}
+
+static void handle_next(git_fs_path_diriter *diriter, walk_data *walk)
+{
+ const char *fullpath, *filename;
+ size_t fullpath_len, filename_len;
+
+ cl_git_pass(git_fs_path_diriter_fullpath(&fullpath, &fullpath_len, diriter));
+ cl_git_pass(git_fs_path_diriter_filename(&filename, &filename_len, diriter));
+
+ cl_assert_equal_strn(fullpath, "sub/", 4);
+ cl_assert_equal_s(fullpath+4, filename);
+
+ update_count(walk->names, fullpath);
+}
+
+/* test directory iterator */
+void test_dirent__diriter_with_fullname(void)
+{
+ git_fs_path_diriter diriter = GIT_FS_PATH_DIRITER_INIT;
+ int error;
+
+ cl_set_cleanup(&dirent_cleanup__cb, &sub);
+ setup(&sub);
+
+ cl_git_pass(git_fs_path_diriter_init(&diriter, sub.path.ptr, 0));
+
+ while ((error = git_fs_path_diriter_next(&diriter)) == 0)
+ handle_next(&diriter, &sub);
+
+ cl_assert_equal_i(error, GIT_ITEROVER);
+
+ git_fs_path_diriter_free(&diriter);
+
+ check_counts(&sub);
+}
+
+void test_dirent__diriter_at_directory_root(void)
+{
+ git_fs_path_diriter diriter = GIT_FS_PATH_DIRITER_INIT;
+ const char *sandbox_path, *path;
+ char *root_path;
+ size_t path_len;
+ int root_offset, error;
+
+ sandbox_path = clar_sandbox_path();
+ cl_assert((root_offset = git_fs_path_root(sandbox_path)) >= 0);
+
+ cl_assert(root_path = git__calloc(1, root_offset + 2));
+ strncpy(root_path, sandbox_path, root_offset + 1);
+
+ cl_git_pass(git_fs_path_diriter_init(&diriter, root_path, 0));
+
+ while ((error = git_fs_path_diriter_next(&diriter)) == 0) {
+ cl_git_pass(git_fs_path_diriter_fullpath(&path, &path_len, &diriter));
+
+ cl_assert(path_len > (size_t)(root_offset + 1));
+ cl_assert(path[root_offset+1] != '/');
+ }
+
+ cl_assert_equal_i(error, GIT_ITEROVER);
+
+ git_fs_path_diriter_free(&diriter);
+ git__free(root_path);
+}
diff --git a/tests/util/encoding.c b/tests/util/encoding.c
new file mode 100644
index 0000000..a25a334
--- /dev/null
+++ b/tests/util/encoding.c
@@ -0,0 +1,42 @@
+#include "clar_libgit2.h"
+#include "varint.h"
+
+void test_encoding__decode(void)
+{
+ const unsigned char *buf = (unsigned char *)"AB";
+ size_t size;
+
+ cl_assert(git_decode_varint(buf, &size) == 65);
+ cl_assert(size == 1);
+
+ buf = (unsigned char *)"\xfe\xdc\xbaXY";
+ cl_assert(git_decode_varint(buf, &size) == 267869656);
+ cl_assert(size == 4);
+
+ buf = (unsigned char *)"\xaa\xaa\xfe\xdc\xbaXY";
+ cl_assert(git_decode_varint(buf, &size) == UINT64_C(1489279344088));
+ cl_assert(size == 6);
+
+ buf = (unsigned char *)"\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xfe\xdc\xbaXY";
+ cl_assert(git_decode_varint(buf, &size) == 0);
+ cl_assert(size == 0);
+
+}
+
+void test_encoding__encode(void)
+{
+ unsigned char buf[100];
+ cl_assert(git_encode_varint(buf, 100, 65) == 1);
+ cl_assert(buf[0] == 'A');
+
+ cl_assert(git_encode_varint(buf, 1, 1) == 1);
+ cl_assert(!memcmp(buf, "\x01", 1));
+
+ cl_assert(git_encode_varint(buf, 100, 267869656) == 4);
+ cl_assert(!memcmp(buf, "\xfe\xdc\xbaX", 4));
+
+ cl_assert(git_encode_varint(buf, 100, UINT64_C(1489279344088)) == 6);
+ cl_assert(!memcmp(buf, "\xaa\xaa\xfe\xdc\xbaX", 6));
+
+ cl_assert(git_encode_varint(buf, 1, UINT64_C(1489279344088)) == -1);
+}
diff --git a/tests/util/errors.c b/tests/util/errors.c
new file mode 100644
index 0000000..78654a7
--- /dev/null
+++ b/tests/util/errors.c
@@ -0,0 +1,222 @@
+#include "clar_libgit2.h"
+
+void test_errors__public_api(void)
+{
+ char *str_in_error;
+
+ git_error_clear();
+ cl_assert(git_error_last() == NULL);
+
+ git_error_set_oom();
+
+ cl_assert(git_error_last() != NULL);
+ cl_assert(git_error_last()->klass == GIT_ERROR_NOMEMORY);
+ str_in_error = strstr(git_error_last()->message, "memory");
+ cl_assert(str_in_error != NULL);
+
+ git_error_clear();
+
+ git_error_set_str(GIT_ERROR_REPOSITORY, "This is a test");
+
+ cl_assert(git_error_last() != NULL);
+ str_in_error = strstr(git_error_last()->message, "This is a test");
+ cl_assert(str_in_error != NULL);
+
+ git_error_clear();
+ cl_assert(git_error_last() == NULL);
+}
+
+#include "common.h"
+#include "util.h"
+#include "posix.h"
+
+void test_errors__new_school(void)
+{
+ char *str_in_error;
+
+ git_error_clear();
+ cl_assert(git_error_last() == NULL);
+
+ git_error_set_oom(); /* internal fn */
+
+ cl_assert(git_error_last() != NULL);
+ cl_assert(git_error_last()->klass == GIT_ERROR_NOMEMORY);
+ str_in_error = strstr(git_error_last()->message, "memory");
+ cl_assert(str_in_error != NULL);
+
+ git_error_clear();
+
+ git_error_set(GIT_ERROR_REPOSITORY, "This is a test"); /* internal fn */
+
+ cl_assert(git_error_last() != NULL);
+ str_in_error = strstr(git_error_last()->message, "This is a test");
+ cl_assert(str_in_error != NULL);
+
+ git_error_clear();
+ cl_assert(git_error_last() == NULL);
+
+ do {
+ struct stat st;
+ memset(&st, 0, sizeof(st));
+ cl_assert(p_lstat("this_file_does_not_exist", &st) < 0);
+ GIT_UNUSED(st);
+ } while (false);
+ git_error_set(GIT_ERROR_OS, "stat failed"); /* internal fn */
+
+ cl_assert(git_error_last() != NULL);
+ str_in_error = strstr(git_error_last()->message, "stat failed");
+ cl_assert(str_in_error != NULL);
+ cl_assert(git__prefixcmp(str_in_error, "stat failed: ") == 0);
+ cl_assert(strlen(str_in_error) > strlen("stat failed: "));
+
+#ifdef GIT_WIN32
+ git_error_clear();
+
+ /* The MSDN docs use this to generate a sample error */
+ cl_assert(GetProcessId(NULL) == 0);
+ git_error_set(GIT_ERROR_OS, "GetProcessId failed"); /* internal fn */
+
+ cl_assert(git_error_last() != NULL);
+ str_in_error = strstr(git_error_last()->message, "GetProcessId failed");
+ cl_assert(str_in_error != NULL);
+ cl_assert(git__prefixcmp(str_in_error, "GetProcessId failed: ") == 0);
+ cl_assert(strlen(str_in_error) > strlen("GetProcessId failed: "));
+#endif
+
+ git_error_clear();
+}
+
+void test_errors__restore(void)
+{
+ git_error_state err_state = {0};
+
+ git_error_clear();
+ cl_assert(git_error_last() == NULL);
+
+ cl_assert_equal_i(0, git_error_state_capture(&err_state, 0));
+
+ memset(&err_state, 0x0, sizeof(git_error_state));
+
+ git_error_set(42, "Foo: %s", "bar");
+ cl_assert_equal_i(-1, git_error_state_capture(&err_state, -1));
+
+ cl_assert(git_error_last() == NULL);
+
+ git_error_set(99, "Bar: %s", "foo");
+
+ git_error_state_restore(&err_state);
+
+ cl_assert_equal_i(42, git_error_last()->klass);
+ cl_assert_equal_s("Foo: bar", git_error_last()->message);
+}
+
+void test_errors__free_state(void)
+{
+ git_error_state err_state = {0};
+
+ git_error_clear();
+
+ git_error_set(42, "Foo: %s", "bar");
+ cl_assert_equal_i(-1, git_error_state_capture(&err_state, -1));
+
+ git_error_set(99, "Bar: %s", "foo");
+
+ git_error_state_free(&err_state);
+
+ cl_assert_equal_i(99, git_error_last()->klass);
+ cl_assert_equal_s("Bar: foo", git_error_last()->message);
+
+ git_error_state_restore(&err_state);
+
+ cl_assert(git_error_last() == NULL);
+}
+
+void test_errors__restore_oom(void)
+{
+ git_error_state err_state = {0};
+ const git_error *oom_error = NULL;
+
+ git_error_clear();
+
+ git_error_set_oom(); /* internal fn */
+ oom_error = git_error_last();
+ cl_assert(oom_error);
+
+ cl_assert_equal_i(-1, git_error_state_capture(&err_state, -1));
+
+ cl_assert(git_error_last() == NULL);
+ cl_assert_equal_i(GIT_ERROR_NOMEMORY, err_state.error_msg.klass);
+ cl_assert_equal_s("Out of memory", err_state.error_msg.message);
+
+ git_error_state_restore(&err_state);
+
+ cl_assert(git_error_last()->klass == GIT_ERROR_NOMEMORY);
+ cl_assert_(git_error_last() == oom_error, "static oom error not restored");
+
+ git_error_clear();
+}
+
+static int test_arraysize_multiply(size_t nelem, size_t size)
+{
+ size_t out;
+ GIT_ERROR_CHECK_ALLOC_MULTIPLY(&out, nelem, size);
+ return 0;
+}
+
+void test_errors__integer_overflow_alloc_multiply(void)
+{
+ cl_git_pass(test_arraysize_multiply(10, 10));
+ cl_git_pass(test_arraysize_multiply(1000, 1000));
+ cl_git_pass(test_arraysize_multiply(SIZE_MAX/sizeof(void *), sizeof(void *)));
+ cl_git_pass(test_arraysize_multiply(0, 10));
+ cl_git_pass(test_arraysize_multiply(10, 0));
+
+ cl_git_fail(test_arraysize_multiply(SIZE_MAX-1, sizeof(void *)));
+ cl_git_fail(test_arraysize_multiply((SIZE_MAX/sizeof(void *))+1, sizeof(void *)));
+
+ cl_assert_equal_i(GIT_ERROR_NOMEMORY, git_error_last()->klass);
+ cl_assert_equal_s("Out of memory", git_error_last()->message);
+}
+
+static int test_arraysize_add(size_t one, size_t two)
+{
+ size_t out;
+ GIT_ERROR_CHECK_ALLOC_ADD(&out, one, two);
+ return 0;
+}
+
+void test_errors__integer_overflow_alloc_add(void)
+{
+ cl_git_pass(test_arraysize_add(10, 10));
+ cl_git_pass(test_arraysize_add(1000, 1000));
+ cl_git_pass(test_arraysize_add(SIZE_MAX-10, 10));
+
+ cl_git_fail(test_arraysize_multiply(SIZE_MAX-1, 2));
+ cl_git_fail(test_arraysize_multiply(SIZE_MAX, SIZE_MAX));
+
+ cl_assert_equal_i(GIT_ERROR_NOMEMORY, git_error_last()->klass);
+ cl_assert_equal_s("Out of memory", git_error_last()->message);
+}
+
+void test_errors__integer_overflow_sets_oom(void)
+{
+ size_t out;
+
+ git_error_clear();
+ cl_assert(!GIT_ADD_SIZET_OVERFLOW(&out, SIZE_MAX-1, 1));
+ cl_assert_equal_p(NULL, git_error_last());
+
+ git_error_clear();
+ cl_assert(!GIT_ADD_SIZET_OVERFLOW(&out, 42, 69));
+ cl_assert_equal_p(NULL, git_error_last());
+
+ git_error_clear();
+ cl_assert(GIT_ADD_SIZET_OVERFLOW(&out, SIZE_MAX, SIZE_MAX));
+ cl_assert_equal_i(GIT_ERROR_NOMEMORY, git_error_last()->klass);
+ cl_assert_equal_s("Out of memory", git_error_last()->message);
+
+ git_error_clear();
+ cl_assert(GIT_ADD_SIZET_OVERFLOW(&out, SIZE_MAX, SIZE_MAX));
+ cl_assert_equal_i(GIT_ERROR_NOMEMORY, git_error_last()->klass);
+ cl_assert_equal_s("Out of memory", git_error_last()->message);
+}
diff --git a/tests/util/filebuf.c b/tests/util/filebuf.c
new file mode 100644
index 0000000..29b8211
--- /dev/null
+++ b/tests/util/filebuf.c
@@ -0,0 +1,267 @@
+#include "clar_libgit2.h"
+#include "filebuf.h"
+
+/* make sure git_filebuf_open doesn't delete an existing lock */
+void test_filebuf__0(void)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ int fd;
+ char test[] = "test", testlock[] = "test.lock";
+
+ fd = p_creat(testlock, 0744); /* -V536 */
+
+ cl_must_pass(fd);
+ cl_must_pass(p_close(fd));
+
+ cl_git_fail(git_filebuf_open(&file, test, 0, 0666));
+ cl_assert(git_fs_path_exists(testlock));
+
+ cl_must_pass(p_unlink(testlock));
+}
+
+
+/* make sure GIT_FILEBUF_APPEND works as expected */
+void test_filebuf__1(void)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ char test[] = "test";
+
+ cl_git_mkfile(test, "libgit2 rocks\n");
+
+ cl_git_pass(git_filebuf_open(&file, test, GIT_FILEBUF_APPEND, 0666));
+ cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks"));
+ cl_git_pass(git_filebuf_commit(&file));
+
+ cl_assert_equal_file("libgit2 rocks\nlibgit2 rocks\n", 0, test);
+
+ cl_must_pass(p_unlink(test));
+}
+
+
+/* make sure git_filebuf_write writes large buffer correctly */
+void test_filebuf__2(void)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ char test[] = "test";
+ unsigned char buf[4096 * 4]; /* 2 * WRITE_BUFFER_SIZE */
+
+ memset(buf, 0xfe, sizeof(buf));
+
+ cl_git_pass(git_filebuf_open(&file, test, 0, 0666));
+ cl_git_pass(git_filebuf_write(&file, buf, sizeof(buf)));
+ cl_git_pass(git_filebuf_commit(&file));
+
+ cl_assert_equal_file((char *)buf, sizeof(buf), test);
+
+ cl_must_pass(p_unlink(test));
+}
+
+/* make sure git_filebuf_cleanup clears the buffer */
+void test_filebuf__4(void)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ char test[] = "test";
+
+ cl_assert(file.buffer == NULL);
+
+ cl_git_pass(git_filebuf_open(&file, test, 0, 0666));
+ cl_assert(file.buffer != NULL);
+
+ git_filebuf_cleanup(&file);
+ cl_assert(file.buffer == NULL);
+}
+
+
+/* make sure git_filebuf_commit clears the buffer */
+void test_filebuf__5(void)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ char test[] = "test";
+
+ cl_assert(file.buffer == NULL);
+
+ cl_git_pass(git_filebuf_open(&file, test, 0, 0666));
+ cl_assert(file.buffer != NULL);
+ cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks"));
+ cl_assert(file.buffer != NULL);
+
+ cl_git_pass(git_filebuf_commit(&file));
+ cl_assert(file.buffer == NULL);
+
+ cl_must_pass(p_unlink(test));
+}
+
+
+/* make sure git_filebuf_commit takes umask into account */
+void test_filebuf__umask(void)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ char test[] = "test";
+ struct stat statbuf;
+ mode_t mask, os_mask;
+
+#ifdef GIT_WIN32
+ os_mask = 0600;
+#else
+ os_mask = 0777;
+#endif
+
+ p_umask(mask = p_umask(0));
+
+ cl_assert(file.buffer == NULL);
+
+ cl_git_pass(git_filebuf_open(&file, test, 0, 0666));
+ cl_assert(file.buffer != NULL);
+ cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks"));
+ cl_assert(file.buffer != NULL);
+
+ cl_git_pass(git_filebuf_commit(&file));
+ cl_assert(file.buffer == NULL);
+
+ cl_must_pass(p_stat("test", &statbuf));
+ cl_assert_equal_i(statbuf.st_mode & os_mask, (0666 & ~mask) & os_mask);
+
+ cl_must_pass(p_unlink(test));
+}
+
+void test_filebuf__rename_error(void)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ char *dir = "subdir", *test = "subdir/test", *test_lock = "subdir/test.lock";
+ int fd;
+
+#ifndef GIT_WIN32
+ cl_skip();
+#endif
+
+ cl_git_pass(p_mkdir(dir, 0666));
+ cl_git_mkfile(test, "dummy content");
+ fd = p_open(test, O_RDONLY);
+ cl_assert(fd > 0);
+ cl_git_pass(git_filebuf_open(&file, test, 0, 0666));
+
+ cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks"));
+
+ cl_assert_equal_i(true, git_fs_path_exists(test_lock));
+
+ cl_git_fail(git_filebuf_commit(&file));
+ p_close(fd);
+
+ git_filebuf_cleanup(&file);
+
+ cl_assert_equal_i(false, git_fs_path_exists(test_lock));
+}
+
+void test_filebuf__symlink_follow(void)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ const char *dir = "linkdir", *source = "linkdir/link";
+
+ if (!git_fs_path_supports_symlinks(clar_sandbox_path()))
+ cl_skip();
+
+ cl_git_pass(p_mkdir(dir, 0777));
+ cl_git_pass(p_symlink("target", source));
+
+ cl_git_pass(git_filebuf_open(&file, source, 0, 0666));
+ cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks"));
+
+ cl_assert_equal_i(true, git_fs_path_exists("linkdir/target.lock"));
+
+ cl_git_pass(git_filebuf_commit(&file));
+ cl_assert_equal_i(true, git_fs_path_exists("linkdir/target"));
+
+ git_filebuf_cleanup(&file);
+
+ /* The second time around, the target file does exist */
+ cl_git_pass(git_filebuf_open(&file, source, 0, 0666));
+ cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks"));
+
+ cl_assert_equal_i(true, git_fs_path_exists("linkdir/target.lock"));
+
+ cl_git_pass(git_filebuf_commit(&file));
+ cl_assert_equal_i(true, git_fs_path_exists("linkdir/target"));
+
+ git_filebuf_cleanup(&file);
+ cl_git_pass(git_futils_rmdir_r(dir, NULL, GIT_RMDIR_REMOVE_FILES));
+}
+
+void test_filebuf__symlink_follow_absolute_paths(void)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ git_str source = GIT_STR_INIT, target = GIT_STR_INIT;
+
+ if (!git_fs_path_supports_symlinks(clar_sandbox_path()))
+ cl_skip();
+
+ cl_git_pass(git_str_joinpath(&source, clar_sandbox_path(), "linkdir/link"));
+ cl_git_pass(git_str_joinpath(&target, clar_sandbox_path(), "linkdir/target"));
+ cl_git_pass(p_mkdir("linkdir", 0777));
+ cl_git_pass(p_symlink(target.ptr, source.ptr));
+
+ cl_git_pass(git_filebuf_open(&file, source.ptr, 0, 0666));
+ cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks"));
+
+ cl_assert_equal_i(true, git_fs_path_exists("linkdir/target.lock"));
+
+ cl_git_pass(git_filebuf_commit(&file));
+ cl_assert_equal_i(true, git_fs_path_exists("linkdir/target"));
+
+ git_filebuf_cleanup(&file);
+ git_str_dispose(&source);
+ git_str_dispose(&target);
+
+ cl_git_pass(git_futils_rmdir_r("linkdir", NULL, GIT_RMDIR_REMOVE_FILES));
+}
+
+void test_filebuf__symlink_depth(void)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+ const char *dir = "linkdir", *source = "linkdir/link";
+
+ if (!git_fs_path_supports_symlinks(clar_sandbox_path()))
+ cl_skip();
+
+ cl_git_pass(p_mkdir(dir, 0777));
+ /* Endless loop */
+ cl_git_pass(p_symlink("link", source));
+
+ cl_git_fail(git_filebuf_open(&file, source, 0, 0666));
+
+ cl_git_pass(git_futils_rmdir_r(dir, NULL, GIT_RMDIR_REMOVE_FILES));
+}
+
+void test_filebuf__hidden_file(void)
+{
+#ifndef GIT_WIN32
+ cl_skip();
+#else
+ git_filebuf file = GIT_FILEBUF_INIT;
+ char *dir = "hidden", *test = "hidden/test";
+ bool hidden;
+
+ cl_git_pass(p_mkdir(dir, 0666));
+ cl_git_mkfile(test, "dummy content");
+
+ cl_git_pass(git_win32__set_hidden(test, true));
+ cl_git_pass(git_win32__hidden(&hidden, test));
+ cl_assert(hidden);
+
+ cl_git_pass(git_filebuf_open(&file, test, 0, 0666));
+
+ cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks"));
+
+ cl_git_pass(git_filebuf_commit(&file));
+
+ git_filebuf_cleanup(&file);
+#endif
+}
+
+void test_filebuf__detects_directory(void)
+{
+ git_filebuf file = GIT_FILEBUF_INIT;
+
+ cl_must_pass(p_mkdir("foo", 0777));
+ cl_git_fail_with(GIT_EDIRECTORY, git_filebuf_open(&file, "foo", 0, 0666));
+ cl_must_pass(p_rmdir("foo"));
+}
diff --git a/tests/util/ftruncate.c b/tests/util/ftruncate.c
new file mode 100644
index 0000000..e60e5d9
--- /dev/null
+++ b/tests/util/ftruncate.c
@@ -0,0 +1,48 @@
+/**
+ * Some tests for p_ftruncate() to ensure that
+ * properly handles large (2Gb+) files.
+ */
+
+#include "clar_libgit2.h"
+
+static const char *filename = "core_ftruncate.txt";
+static int fd = -1;
+
+void test_ftruncate__initialize(void)
+{
+ if (!cl_is_env_set("GITTEST_INVASIVE_FS_SIZE"))
+ cl_skip();
+
+ cl_must_pass((fd = p_open(filename, O_CREAT | O_RDWR, 0644)));
+}
+
+void test_ftruncate__cleanup(void)
+{
+ if (fd < 0)
+ return;
+
+ p_close(fd);
+ fd = 0;
+
+ p_unlink(filename);
+}
+
+static void _extend(off64_t i64len)
+{
+ struct stat st;
+ int error;
+
+ cl_assert((error = p_ftruncate(fd, i64len)) == 0);
+ cl_assert((error = p_fstat(fd, &st)) == 0);
+ cl_assert(st.st_size == i64len);
+}
+
+void test_ftruncate__2gb(void)
+{
+ _extend(0x80000001);
+}
+
+void test_ftruncate__4gb(void)
+{
+ _extend(0x100000001);
+}
diff --git a/tests/util/futils.c b/tests/util/futils.c
new file mode 100644
index 0000000..993b14d
--- /dev/null
+++ b/tests/util/futils.c
@@ -0,0 +1,115 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+
+/* Fixture setup and teardown */
+void test_futils__initialize(void)
+{
+ cl_must_pass(p_mkdir("futils", 0777));
+}
+
+void test_futils__cleanup(void)
+{
+ cl_fixture_cleanup("futils");
+}
+
+void test_futils__writebuffer(void)
+{
+ git_str out = GIT_STR_INIT,
+ append = GIT_STR_INIT;
+
+ /* create a new file */
+ git_str_puts(&out, "hello!\n");
+ git_str_printf(&out, "this is a %s\n", "test");
+
+ cl_git_pass(git_futils_writebuffer(&out, "futils/test-file", O_RDWR|O_CREAT, 0666));
+
+ cl_assert_equal_file(out.ptr, out.size, "futils/test-file");
+
+ /* append some more data */
+ git_str_puts(&append, "And some more!\n");
+ git_str_put(&out, append.ptr, append.size);
+
+ cl_git_pass(git_futils_writebuffer(&append, "futils/test-file", O_RDWR|O_APPEND, 0666));
+
+ cl_assert_equal_file(out.ptr, out.size, "futils/test-file");
+
+ git_str_dispose(&out);
+ git_str_dispose(&append);
+}
+
+void test_futils__write_hidden_file(void)
+{
+#ifndef GIT_WIN32
+ cl_skip();
+#else
+ git_str out = GIT_STR_INIT, append = GIT_STR_INIT;
+ bool hidden;
+
+ git_str_puts(&out, "hidden file.\n");
+ git_futils_writebuffer(&out, "futils/test-file", O_RDWR | O_CREAT, 0666);
+
+ cl_git_pass(git_win32__set_hidden("futils/test-file", true));
+
+ /* append some more data */
+ git_str_puts(&append, "And some more!\n");
+ git_str_put(&out, append.ptr, append.size);
+
+ cl_git_pass(git_futils_writebuffer(&append, "futils/test-file", O_RDWR | O_APPEND, 0666));
+
+ cl_assert_equal_file(out.ptr, out.size, "futils/test-file");
+
+ cl_git_pass(git_win32__hidden(&hidden, "futils/test-file"));
+ cl_assert(hidden);
+
+ git_str_dispose(&out);
+ git_str_dispose(&append);
+#endif
+}
+
+void test_futils__recursive_rmdir_keeps_symlink_targets(void)
+{
+ if (!git_fs_path_supports_symlinks(clar_sandbox_path()))
+ cl_skip();
+
+ cl_git_pass(git_futils_mkdir_r("a/b", 0777));
+ cl_git_pass(git_futils_mkdir_r("dir-target", 0777));
+ cl_git_mkfile("dir-target/file", "Contents");
+ cl_git_mkfile("file-target", "Contents");
+ cl_must_pass(p_symlink("dir-target", "a/symlink"));
+ cl_must_pass(p_symlink("file-target", "a/b/symlink"));
+
+ cl_git_pass(git_futils_rmdir_r("a", NULL, GIT_RMDIR_REMOVE_FILES));
+
+ cl_assert(git_fs_path_exists("dir-target"));
+ cl_assert(git_fs_path_exists("file-target"));
+
+ cl_must_pass(p_unlink("dir-target/file"));
+ cl_must_pass(p_rmdir("dir-target"));
+ cl_must_pass(p_unlink("file-target"));
+}
+
+void test_futils__mktmp_umask(void)
+{
+#ifdef GIT_WIN32
+ cl_skip();
+#else
+ git_str path = GIT_STR_INIT;
+ struct stat st;
+ int fd;
+
+ umask(0);
+ cl_assert((fd = git_futils_mktmp(&path, "foo", 0777)) >= 0);
+ cl_must_pass(p_fstat(fd, &st));
+ cl_assert_equal_i(st.st_mode & 0777, 0777);
+ cl_must_pass(p_unlink(path.ptr));
+ close(fd);
+
+ umask(077);
+ cl_assert((fd = git_futils_mktmp(&path, "foo", 0777)) >= 0);
+ cl_must_pass(p_fstat(fd, &st));
+ cl_assert_equal_i(st.st_mode & 0777, 0700);
+ cl_must_pass(p_unlink(path.ptr));
+ close(fd);
+ git_str_dispose(&path);
+#endif
+}
diff --git a/tests/util/gitstr.c b/tests/util/gitstr.c
new file mode 100644
index 0000000..aea3556
--- /dev/null
+++ b/tests/util/gitstr.c
@@ -0,0 +1,1044 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+
+#define TESTSTR "Have you seen that? Have you seeeen that??"
+const char *test_string = TESTSTR;
+const char *test_string_x2 = TESTSTR TESTSTR;
+
+#define TESTSTR_4096 REP1024("1234")
+#define TESTSTR_8192 REP1024("12341234")
+const char *test_4096 = TESTSTR_4096;
+const char *test_8192 = TESTSTR_8192;
+
+/* test basic data concatenation */
+void test_gitstr__0(void)
+{
+ git_str buf = GIT_STR_INIT;
+
+ cl_assert(buf.size == 0);
+
+ git_str_puts(&buf, test_string);
+ cl_assert(git_str_oom(&buf) == 0);
+ cl_assert_equal_s(test_string, git_str_cstr(&buf));
+
+ git_str_puts(&buf, test_string);
+ cl_assert(git_str_oom(&buf) == 0);
+ cl_assert_equal_s(test_string_x2, git_str_cstr(&buf));
+
+ git_str_dispose(&buf);
+}
+
+/* test git_str_printf */
+void test_gitstr__1(void)
+{
+ git_str buf = GIT_STR_INIT;
+
+ git_str_printf(&buf, "%s %s %d ", "shoop", "da", 23);
+ cl_assert(git_str_oom(&buf) == 0);
+ cl_assert_equal_s("shoop da 23 ", git_str_cstr(&buf));
+
+ git_str_printf(&buf, "%s %d", "woop", 42);
+ cl_assert(git_str_oom(&buf) == 0);
+ cl_assert_equal_s("shoop da 23 woop 42", git_str_cstr(&buf));
+
+ git_str_dispose(&buf);
+}
+
+/* more thorough test of concatenation options */
+void test_gitstr__2(void)
+{
+ git_str buf = GIT_STR_INIT;
+ int i;
+ char data[128];
+
+ cl_assert(buf.size == 0);
+
+ /* this must be safe to do */
+ git_str_dispose(&buf);
+ cl_assert(buf.size == 0);
+ cl_assert(buf.asize == 0);
+
+ /* empty buffer should be empty string */
+ cl_assert_equal_s("", git_str_cstr(&buf));
+ cl_assert(buf.size == 0);
+ /* cl_assert(buf.asize == 0); -- should not assume what git_str does */
+
+ /* free should set us back to the beginning */
+ git_str_dispose(&buf);
+ cl_assert(buf.size == 0);
+ cl_assert(buf.asize == 0);
+
+ /* add letter */
+ git_str_putc(&buf, '+');
+ cl_assert(git_str_oom(&buf) == 0);
+ cl_assert_equal_s("+", git_str_cstr(&buf));
+
+ /* add letter again */
+ git_str_putc(&buf, '+');
+ cl_assert(git_str_oom(&buf) == 0);
+ cl_assert_equal_s("++", git_str_cstr(&buf));
+
+ /* let's try that a few times */
+ for (i = 0; i < 16; ++i) {
+ git_str_putc(&buf, '+');
+ cl_assert(git_str_oom(&buf) == 0);
+ }
+ cl_assert_equal_s("++++++++++++++++++", git_str_cstr(&buf));
+
+ git_str_dispose(&buf);
+
+ /* add data */
+ git_str_put(&buf, "xo", 2);
+ cl_assert(git_str_oom(&buf) == 0);
+ cl_assert_equal_s("xo", git_str_cstr(&buf));
+
+ /* add letter again */
+ git_str_put(&buf, "xo", 2);
+ cl_assert(git_str_oom(&buf) == 0);
+ cl_assert_equal_s("xoxo", git_str_cstr(&buf));
+
+ /* let's try that a few times */
+ for (i = 0; i < 16; ++i) {
+ git_str_put(&buf, "xo", 2);
+ cl_assert(git_str_oom(&buf) == 0);
+ }
+ cl_assert_equal_s("xoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxo",
+ git_str_cstr(&buf));
+
+ git_str_dispose(&buf);
+
+ /* set to string */
+ git_str_sets(&buf, test_string);
+ cl_assert(git_str_oom(&buf) == 0);
+ cl_assert_equal_s(test_string, git_str_cstr(&buf));
+
+ /* append string */
+ git_str_puts(&buf, test_string);
+ cl_assert(git_str_oom(&buf) == 0);
+ cl_assert_equal_s(test_string_x2, git_str_cstr(&buf));
+
+ /* set to string again (should overwrite - not append) */
+ git_str_sets(&buf, test_string);
+ cl_assert(git_str_oom(&buf) == 0);
+ cl_assert_equal_s(test_string, git_str_cstr(&buf));
+
+ /* test clear */
+ git_str_clear(&buf);
+ cl_assert_equal_s("", git_str_cstr(&buf));
+
+ git_str_dispose(&buf);
+
+ /* test extracting data into buffer */
+ git_str_puts(&buf, REP4("0123456789"));
+ cl_assert(git_str_oom(&buf) == 0);
+
+ git_str_copy_cstr(data, sizeof(data), &buf);
+ cl_assert_equal_s(REP4("0123456789"), data);
+ git_str_copy_cstr(data, 11, &buf);
+ cl_assert_equal_s("0123456789", data);
+ git_str_copy_cstr(data, 3, &buf);
+ cl_assert_equal_s("01", data);
+ git_str_copy_cstr(data, 1, &buf);
+ cl_assert_equal_s("", data);
+
+ git_str_copy_cstr(data, sizeof(data), &buf);
+ cl_assert_equal_s(REP4("0123456789"), data);
+
+ git_str_sets(&buf, REP256("x"));
+ git_str_copy_cstr(data, sizeof(data), &buf);
+ /* since sizeof(data) == 128, only 127 bytes should be copied */
+ cl_assert_equal_s(REP4(REP16("x")) REP16("x") REP16("x")
+ REP16("x") "xxxxxxxxxxxxxxx", data);
+
+ git_str_dispose(&buf);
+
+ git_str_copy_cstr(data, sizeof(data), &buf);
+ cl_assert_equal_s("", data);
+}
+
+/* let's do some tests with larger buffers to push our limits */
+void test_gitstr__3(void)
+{
+ git_str buf = GIT_STR_INIT;
+
+ /* set to string */
+ git_str_set(&buf, test_4096, 4096);
+ cl_assert(git_str_oom(&buf) == 0);
+ cl_assert_equal_s(test_4096, git_str_cstr(&buf));
+
+ /* append string */
+ git_str_puts(&buf, test_4096);
+ cl_assert(git_str_oom(&buf) == 0);
+ cl_assert_equal_s(test_8192, git_str_cstr(&buf));
+
+ /* set to string again (should overwrite - not append) */
+ git_str_set(&buf, test_4096, 4096);
+ cl_assert(git_str_oom(&buf) == 0);
+ cl_assert_equal_s(test_4096, git_str_cstr(&buf));
+
+ git_str_dispose(&buf);
+}
+
+/* let's try some producer/consumer tests */
+void test_gitstr__4(void)
+{
+ git_str buf = GIT_STR_INIT;
+ int i;
+
+ for (i = 0; i < 10; ++i) {
+ git_str_puts(&buf, "1234"); /* add 4 */
+ cl_assert(git_str_oom(&buf) == 0);
+ git_str_consume(&buf, buf.ptr + 2); /* eat the first two */
+ cl_assert(strlen(git_str_cstr(&buf)) == (size_t)((i + 1) * 2));
+ }
+ /* we have appended 1234 10x and removed the first 20 letters */
+ cl_assert_equal_s("12341234123412341234", git_str_cstr(&buf));
+
+ git_str_consume(&buf, NULL);
+ cl_assert_equal_s("12341234123412341234", git_str_cstr(&buf));
+
+ git_str_consume(&buf, "invalid pointer");
+ cl_assert_equal_s("12341234123412341234", git_str_cstr(&buf));
+
+ git_str_consume(&buf, buf.ptr);
+ cl_assert_equal_s("12341234123412341234", git_str_cstr(&buf));
+
+ git_str_consume(&buf, buf.ptr + 1);
+ cl_assert_equal_s("2341234123412341234", git_str_cstr(&buf));
+
+ git_str_consume(&buf, buf.ptr + buf.size);
+ cl_assert_equal_s("", git_str_cstr(&buf));
+
+ git_str_dispose(&buf);
+}
+
+
+static void
+check_buf_append(
+ const char* data_a,
+ const char* data_b,
+ const char* expected_data,
+ size_t expected_size,
+ size_t expected_asize)
+{
+ git_str tgt = GIT_STR_INIT;
+
+ git_str_sets(&tgt, data_a);
+ cl_assert(git_str_oom(&tgt) == 0);
+ git_str_puts(&tgt, data_b);
+ cl_assert(git_str_oom(&tgt) == 0);
+ cl_assert_equal_s(expected_data, git_str_cstr(&tgt));
+ cl_assert_equal_i(tgt.size, expected_size);
+ if (expected_asize > 0)
+ cl_assert_equal_i(tgt.asize, expected_asize);
+
+ git_str_dispose(&tgt);
+}
+
+static void
+check_buf_append_abc(
+ const char* buf_a,
+ const char* buf_b,
+ const char* buf_c,
+ const char* expected_ab,
+ const char* expected_abc,
+ const char* expected_abca,
+ const char* expected_abcab,
+ const char* expected_abcabc)
+{
+ git_str buf = GIT_STR_INIT;
+
+ git_str_sets(&buf, buf_a);
+ cl_assert(git_str_oom(&buf) == 0);
+ cl_assert_equal_s(buf_a, git_str_cstr(&buf));
+
+ git_str_puts(&buf, buf_b);
+ cl_assert(git_str_oom(&buf) == 0);
+ cl_assert_equal_s(expected_ab, git_str_cstr(&buf));
+
+ git_str_puts(&buf, buf_c);
+ cl_assert(git_str_oom(&buf) == 0);
+ cl_assert_equal_s(expected_abc, git_str_cstr(&buf));
+
+ git_str_puts(&buf, buf_a);
+ cl_assert(git_str_oom(&buf) == 0);
+ cl_assert_equal_s(expected_abca, git_str_cstr(&buf));
+
+ git_str_puts(&buf, buf_b);
+ cl_assert(git_str_oom(&buf) == 0);
+ cl_assert_equal_s(expected_abcab, git_str_cstr(&buf));
+
+ git_str_puts(&buf, buf_c);
+ cl_assert(git_str_oom(&buf) == 0);
+ cl_assert_equal_s(expected_abcabc, git_str_cstr(&buf));
+
+ git_str_dispose(&buf);
+}
+
+/* more variations on append tests */
+void test_gitstr__5(void)
+{
+ check_buf_append("", "", "", 0, 0);
+ check_buf_append("a", "", "a", 1, 0);
+ check_buf_append("", "a", "a", 1, 8);
+ check_buf_append("", "a", "a", 1, 8);
+ check_buf_append("a", "b", "ab", 2, 8);
+ check_buf_append("", "abcdefgh", "abcdefgh", 8, 16);
+ check_buf_append("abcdefgh", "", "abcdefgh", 8, 16);
+
+ /* buffer with starting asize will grow to:
+ * 1 -> 2, 2 -> 3, 3 -> 5, 4 -> 6, 5 -> 8, 6 -> 9,
+ * 7 -> 11, 8 -> 12, 9 -> 14, 10 -> 15, 11 -> 17, 12 -> 18,
+ * 13 -> 20, 14 -> 21, 15 -> 23, 16 -> 24, 17 -> 26, 18 -> 27,
+ * 19 -> 29, 20 -> 30, 21 -> 32, 22 -> 33, 23 -> 35, 24 -> 36,
+ * ...
+ * follow sequence until value > target size,
+ * then round up to nearest multiple of 8.
+ */
+
+ check_buf_append("abcdefgh", "/", "abcdefgh/", 9, 16);
+ check_buf_append("abcdefgh", "ijklmno", "abcdefghijklmno", 15, 16);
+ check_buf_append("abcdefgh", "ijklmnop", "abcdefghijklmnop", 16, 24);
+ check_buf_append("0123456789", "0123456789",
+ "01234567890123456789", 20, 24);
+ check_buf_append(REP16("x"), REP16("o"),
+ REP16("x") REP16("o"), 32, 40);
+
+ check_buf_append(test_4096, "", test_4096, 4096, 4104);
+ check_buf_append(test_4096, test_4096, test_8192, 8192, 8200);
+
+ /* check sequences of appends */
+ check_buf_append_abc("a", "b", "c",
+ "ab", "abc", "abca", "abcab", "abcabc");
+ check_buf_append_abc("a1", "b2", "c3",
+ "a1b2", "a1b2c3", "a1b2c3a1",
+ "a1b2c3a1b2", "a1b2c3a1b2c3");
+ check_buf_append_abc("a1/", "b2/", "c3/",
+ "a1/b2/", "a1/b2/c3/", "a1/b2/c3/a1/",
+ "a1/b2/c3/a1/b2/", "a1/b2/c3/a1/b2/c3/");
+}
+
+/* test swap */
+void test_gitstr__6(void)
+{
+ git_str a = GIT_STR_INIT;
+ git_str b = GIT_STR_INIT;
+
+ git_str_sets(&a, "foo");
+ cl_assert(git_str_oom(&a) == 0);
+ git_str_sets(&b, "bar");
+ cl_assert(git_str_oom(&b) == 0);
+
+ cl_assert_equal_s("foo", git_str_cstr(&a));
+ cl_assert_equal_s("bar", git_str_cstr(&b));
+
+ git_str_swap(&a, &b);
+
+ cl_assert_equal_s("bar", git_str_cstr(&a));
+ cl_assert_equal_s("foo", git_str_cstr(&b));
+
+ git_str_dispose(&a);
+ git_str_dispose(&b);
+}
+
+
+/* test detach/attach data */
+void test_gitstr__7(void)
+{
+ const char *fun = "This is fun";
+ git_str a = GIT_STR_INIT;
+ char *b = NULL;
+
+ git_str_sets(&a, "foo");
+ cl_assert(git_str_oom(&a) == 0);
+ cl_assert_equal_s("foo", git_str_cstr(&a));
+
+ b = git_str_detach(&a);
+
+ cl_assert_equal_s("foo", b);
+ cl_assert_equal_s("", a.ptr);
+ git__free(b);
+
+ b = git_str_detach(&a);
+
+ cl_assert_equal_s(NULL, b);
+ cl_assert_equal_s("", a.ptr);
+
+ git_str_dispose(&a);
+
+ b = git__strdup(fun);
+ git_str_attach(&a, b, 0);
+
+ cl_assert_equal_s(fun, a.ptr);
+ cl_assert(a.size == strlen(fun));
+ cl_assert(a.asize == strlen(fun) + 1);
+
+ git_str_dispose(&a);
+
+ b = git__strdup(fun);
+ git_str_attach(&a, b, strlen(fun) + 1);
+
+ cl_assert_equal_s(fun, a.ptr);
+ cl_assert(a.size == strlen(fun));
+ cl_assert(a.asize == strlen(fun) + 1);
+
+ git_str_dispose(&a);
+}
+
+
+static void
+check_joinbuf_2(
+ const char *a,
+ const char *b,
+ const char *expected)
+{
+ char sep = '/';
+ git_str buf = GIT_STR_INIT;
+
+ git_str_join(&buf, sep, a, b);
+ cl_assert(git_str_oom(&buf) == 0);
+ cl_assert_equal_s(expected, git_str_cstr(&buf));
+ git_str_dispose(&buf);
+}
+
+static void
+check_joinbuf_overlapped(
+ const char *oldval,
+ int ofs_a,
+ const char *b,
+ const char *expected)
+{
+ char sep = '/';
+ git_str buf = GIT_STR_INIT;
+
+ git_str_sets(&buf, oldval);
+ git_str_join(&buf, sep, buf.ptr + ofs_a, b);
+ cl_assert(git_str_oom(&buf) == 0);
+ cl_assert_equal_s(expected, git_str_cstr(&buf));
+ git_str_dispose(&buf);
+}
+
+static void
+check_joinbuf_n_2(
+ const char *a,
+ const char *b,
+ const char *expected)
+{
+ char sep = '/';
+ git_str buf = GIT_STR_INIT;
+
+ git_str_sets(&buf, a);
+ cl_assert(git_str_oom(&buf) == 0);
+
+ git_str_join_n(&buf, sep, 1, b);
+ cl_assert(git_str_oom(&buf) == 0);
+ cl_assert_equal_s(expected, git_str_cstr(&buf));
+
+ git_str_dispose(&buf);
+}
+
+static void
+check_joinbuf_n_4(
+ const char *a,
+ const char *b,
+ const char *c,
+ const char *d,
+ const char *expected)
+{
+ char sep = ';';
+ git_str buf = GIT_STR_INIT;
+ git_str_join_n(&buf, sep, 4, a, b, c, d);
+ cl_assert(git_str_oom(&buf) == 0);
+ cl_assert_equal_s(expected, git_str_cstr(&buf));
+ git_str_dispose(&buf);
+}
+
+/* test join */
+void test_gitstr__8(void)
+{
+ git_str a = GIT_STR_INIT;
+
+ git_str_join_n(&a, '/', 1, "foo");
+ cl_assert(git_str_oom(&a) == 0);
+ cl_assert_equal_s("foo", git_str_cstr(&a));
+
+ git_str_join_n(&a, '/', 1, "bar");
+ cl_assert(git_str_oom(&a) == 0);
+ cl_assert_equal_s("foo/bar", git_str_cstr(&a));
+
+ git_str_join_n(&a, '/', 1, "baz");
+ cl_assert(git_str_oom(&a) == 0);
+ cl_assert_equal_s("foo/bar/baz", git_str_cstr(&a));
+
+ git_str_dispose(&a);
+
+ check_joinbuf_2(NULL, "", "");
+ check_joinbuf_2(NULL, "a", "a");
+ check_joinbuf_2(NULL, "/a", "/a");
+ check_joinbuf_2("", "", "");
+ check_joinbuf_2("", "a", "a");
+ check_joinbuf_2("", "/a", "/a");
+ check_joinbuf_2("a", "", "a/");
+ check_joinbuf_2("a", "/", "a/");
+ check_joinbuf_2("a", "b", "a/b");
+ check_joinbuf_2("/", "a", "/a");
+ check_joinbuf_2("/", "", "/");
+ check_joinbuf_2("/a", "/b", "/a/b");
+ check_joinbuf_2("/a", "/b/", "/a/b/");
+ check_joinbuf_2("/a/", "b/", "/a/b/");
+ check_joinbuf_2("/a/", "/b/", "/a/b/");
+ check_joinbuf_2("/a/", "//b/", "/a/b/");
+ check_joinbuf_2("/abcd", "/defg", "/abcd/defg");
+ check_joinbuf_2("/abcd", "/defg/", "/abcd/defg/");
+ check_joinbuf_2("/abcd/", "defg/", "/abcd/defg/");
+ check_joinbuf_2("/abcd/", "/defg/", "/abcd/defg/");
+
+ check_joinbuf_overlapped("abcd", 0, "efg", "abcd/efg");
+ check_joinbuf_overlapped("abcd", 1, "efg", "bcd/efg");
+ check_joinbuf_overlapped("abcd", 2, "efg", "cd/efg");
+ check_joinbuf_overlapped("abcd", 3, "efg", "d/efg");
+ check_joinbuf_overlapped("abcd", 4, "efg", "efg");
+ check_joinbuf_overlapped("abc/", 2, "efg", "c/efg");
+ check_joinbuf_overlapped("abc/", 3, "efg", "/efg");
+ check_joinbuf_overlapped("abc/", 4, "efg", "efg");
+ check_joinbuf_overlapped("abcd", 3, "", "d/");
+ check_joinbuf_overlapped("abcd", 4, "", "");
+ check_joinbuf_overlapped("abc/", 2, "", "c/");
+ check_joinbuf_overlapped("abc/", 3, "", "/");
+ check_joinbuf_overlapped("abc/", 4, "", "");
+
+ check_joinbuf_n_2("", "", "");
+ check_joinbuf_n_2("", "a", "a");
+ check_joinbuf_n_2("", "/a", "/a");
+ check_joinbuf_n_2("a", "", "a/");
+ check_joinbuf_n_2("a", "/", "a/");
+ check_joinbuf_n_2("a", "b", "a/b");
+ check_joinbuf_n_2("/", "a", "/a");
+ check_joinbuf_n_2("/", "", "/");
+ check_joinbuf_n_2("/a", "/b", "/a/b");
+ check_joinbuf_n_2("/a", "/b/", "/a/b/");
+ check_joinbuf_n_2("/a/", "b/", "/a/b/");
+ check_joinbuf_n_2("/a/", "/b/", "/a/b/");
+ check_joinbuf_n_2("/abcd", "/defg", "/abcd/defg");
+ check_joinbuf_n_2("/abcd", "/defg/", "/abcd/defg/");
+ check_joinbuf_n_2("/abcd/", "defg/", "/abcd/defg/");
+ check_joinbuf_n_2("/abcd/", "/defg/", "/abcd/defg/");
+
+ check_joinbuf_n_4("", "", "", "", "");
+ check_joinbuf_n_4("", "a", "", "", "a;");
+ check_joinbuf_n_4("a", "", "", "", "a;");
+ check_joinbuf_n_4("", "", "", "a", "a");
+ check_joinbuf_n_4("a", "b", "", ";c;d;", "a;b;c;d;");
+ check_joinbuf_n_4("a", "b", "", ";c;d", "a;b;c;d");
+ check_joinbuf_n_4("abcd", "efgh", "ijkl", "mnop", "abcd;efgh;ijkl;mnop");
+ check_joinbuf_n_4("abcd;", "efgh;", "ijkl;", "mnop;", "abcd;efgh;ijkl;mnop;");
+ check_joinbuf_n_4(";abcd;", ";efgh;", ";ijkl;", ";mnop;", ";abcd;efgh;ijkl;mnop;");
+}
+
+void test_gitstr__9(void)
+{
+ git_str buf = GIT_STR_INIT;
+
+ /* just some exhaustive tests of various separator placement */
+ char *a[] = { "", "-", "a-", "-a", "-a-" };
+ char *b[] = { "", "-", "b-", "-b", "-b-" };
+ char sep[] = { 0, '-', '/' };
+ char *expect_null[] = { "", "-", "a-", "-a", "-a-",
+ "-", "--", "a--", "-a-", "-a--",
+ "b-", "-b-", "a-b-", "-ab-", "-a-b-",
+ "-b", "--b", "a--b", "-a-b", "-a--b",
+ "-b-", "--b-", "a--b-", "-a-b-", "-a--b-" };
+ char *expect_dash[] = { "", "-", "a-", "-a-", "-a-",
+ "-", "-", "a-", "-a-", "-a-",
+ "b-", "-b-", "a-b-", "-a-b-", "-a-b-",
+ "-b", "-b", "a-b", "-a-b", "-a-b",
+ "-b-", "-b-", "a-b-", "-a-b-", "-a-b-" };
+ char *expect_slas[] = { "", "-/", "a-/", "-a/", "-a-/",
+ "-", "-/-", "a-/-", "-a/-", "-a-/-",
+ "b-", "-/b-", "a-/b-", "-a/b-", "-a-/b-",
+ "-b", "-/-b", "a-/-b", "-a/-b", "-a-/-b",
+ "-b-", "-/-b-", "a-/-b-", "-a/-b-", "-a-/-b-" };
+ char **expect_values[] = { expect_null, expect_dash, expect_slas };
+ char separator, **expect;
+ unsigned int s, i, j;
+
+ for (s = 0; s < sizeof(sep) / sizeof(char); ++s) {
+ separator = sep[s];
+ expect = expect_values[s];
+
+ for (j = 0; j < sizeof(b) / sizeof(char*); ++j) {
+ for (i = 0; i < sizeof(a) / sizeof(char*); ++i) {
+ git_str_join(&buf, separator, a[i], b[j]);
+ cl_assert_equal_s(*expect, buf.ptr);
+ expect++;
+ }
+ }
+ }
+
+ git_str_dispose(&buf);
+}
+
+void test_gitstr__10(void)
+{
+ git_str a = GIT_STR_INIT;
+
+ cl_git_pass(git_str_join_n(&a, '/', 1, "test"));
+ cl_assert_equal_s(a.ptr, "test");
+ cl_git_pass(git_str_join_n(&a, '/', 1, "string"));
+ cl_assert_equal_s(a.ptr, "test/string");
+ git_str_clear(&a);
+ cl_git_pass(git_str_join_n(&a, '/', 3, "test", "string", "join"));
+ cl_assert_equal_s(a.ptr, "test/string/join");
+ cl_git_pass(git_str_join_n(&a, '/', 2, a.ptr, "more"));
+ cl_assert_equal_s(a.ptr, "test/string/join/test/string/join/more");
+
+ git_str_dispose(&a);
+}
+
+void test_gitstr__join3(void)
+{
+ git_str a = GIT_STR_INIT;
+
+ cl_git_pass(git_str_join3(&a, '/', "test", "string", "join"));
+ cl_assert_equal_s("test/string/join", a.ptr);
+ cl_git_pass(git_str_join3(&a, '/', "test/", "string", "join"));
+ cl_assert_equal_s("test/string/join", a.ptr);
+ cl_git_pass(git_str_join3(&a, '/', "test/", "/string", "join"));
+ cl_assert_equal_s("test/string/join", a.ptr);
+ cl_git_pass(git_str_join3(&a, '/', "test/", "/string/", "join"));
+ cl_assert_equal_s("test/string/join", a.ptr);
+ cl_git_pass(git_str_join3(&a, '/', "test/", "/string/", "/join"));
+ cl_assert_equal_s("test/string/join", a.ptr);
+
+ cl_git_pass(git_str_join3(&a, '/', "", "string", "join"));
+ cl_assert_equal_s("string/join", a.ptr);
+ cl_git_pass(git_str_join3(&a, '/', "", "string/", "join"));
+ cl_assert_equal_s("string/join", a.ptr);
+ cl_git_pass(git_str_join3(&a, '/', "", "string/", "/join"));
+ cl_assert_equal_s("string/join", a.ptr);
+
+ cl_git_pass(git_str_join3(&a, '/', "string", "", "join"));
+ cl_assert_equal_s("string/join", a.ptr);
+ cl_git_pass(git_str_join3(&a, '/', "string/", "", "join"));
+ cl_assert_equal_s("string/join", a.ptr);
+ cl_git_pass(git_str_join3(&a, '/', "string/", "", "/join"));
+ cl_assert_equal_s("string/join", a.ptr);
+
+ git_str_dispose(&a);
+}
+
+void test_gitstr__11(void)
+{
+ git_str a = GIT_STR_INIT;
+ char *t1[] = { "nothing", "in", "common" };
+ char *t2[] = { "something", "something else", "some other" };
+ char *t3[] = { "something", "some fun", "no fun" };
+ char *t4[] = { "happy", "happier", "happiest" };
+ char *t5[] = { "happiest", "happier", "happy" };
+ char *t6[] = { "no", "nope", "" };
+ char *t7[] = { "", "doesn't matter" };
+
+ cl_git_pass(git_str_common_prefix(&a, t1, 3));
+ cl_assert_equal_s(a.ptr, "");
+
+ cl_git_pass(git_str_common_prefix(&a, t2, 3));
+ cl_assert_equal_s(a.ptr, "some");
+
+ cl_git_pass(git_str_common_prefix(&a, t3, 3));
+ cl_assert_equal_s(a.ptr, "");
+
+ cl_git_pass(git_str_common_prefix(&a, t4, 3));
+ cl_assert_equal_s(a.ptr, "happ");
+
+ cl_git_pass(git_str_common_prefix(&a, t5, 3));
+ cl_assert_equal_s(a.ptr, "happ");
+
+ cl_git_pass(git_str_common_prefix(&a, t6, 3));
+ cl_assert_equal_s(a.ptr, "");
+
+ cl_git_pass(git_str_common_prefix(&a, t7, 3));
+ cl_assert_equal_s(a.ptr, "");
+
+ git_str_dispose(&a);
+}
+
+void test_gitstr__rfind_variants(void)
+{
+ git_str a = GIT_STR_INIT;
+ ssize_t len;
+
+ cl_git_pass(git_str_sets(&a, "/this/is/it/"));
+
+ len = (ssize_t)git_str_len(&a);
+
+ cl_assert(git_str_rfind(&a, '/') == len - 1);
+ cl_assert(git_str_rfind_next(&a, '/') == len - 4);
+
+ cl_assert(git_str_rfind(&a, 'i') == len - 3);
+ cl_assert(git_str_rfind_next(&a, 'i') == len - 3);
+
+ cl_assert(git_str_rfind(&a, 'h') == 2);
+ cl_assert(git_str_rfind_next(&a, 'h') == 2);
+
+ cl_assert(git_str_rfind(&a, 'q') == -1);
+ cl_assert(git_str_rfind_next(&a, 'q') == -1);
+
+ git_str_dispose(&a);
+}
+
+void test_gitstr__puts_escaped(void)
+{
+ git_str a = GIT_STR_INIT;
+
+ git_str_clear(&a);
+ cl_git_pass(git_str_puts_escaped(&a, "this is a test", "", ""));
+ cl_assert_equal_s("this is a test", a.ptr);
+
+ git_str_clear(&a);
+ cl_git_pass(git_str_puts_escaped(&a, "this is a test", "t", "\\"));
+ cl_assert_equal_s("\\this is a \\tes\\t", a.ptr);
+
+ git_str_clear(&a);
+ cl_git_pass(git_str_puts_escaped(&a, "this is a test", "i ", "__"));
+ cl_assert_equal_s("th__is__ __is__ a__ test", a.ptr);
+
+ git_str_clear(&a);
+ cl_git_pass(git_str_puts_escape_regex(&a, "^match\\s*[A-Z]+.*"));
+ cl_assert_equal_s("\\^match\\\\s\\*\\[A-Z\\]\\+\\.\\*", a.ptr);
+
+ git_str_dispose(&a);
+}
+
+static void assert_unescape(char *expected, char *to_unescape) {
+ git_str buf = GIT_STR_INIT;
+
+ cl_git_pass(git_str_sets(&buf, to_unescape));
+ git_str_unescape(&buf);
+ cl_assert_equal_s(expected, buf.ptr);
+ cl_assert_equal_sz(strlen(expected), buf.size);
+
+ git_str_dispose(&buf);
+}
+
+void test_gitstr__unescape(void)
+{
+ assert_unescape("Escaped\\", "Es\\ca\\ped\\");
+ assert_unescape("Es\\caped\\", "Es\\\\ca\\ped\\\\");
+ assert_unescape("\\", "\\");
+ assert_unescape("\\", "\\\\");
+ assert_unescape("", "");
+}
+
+void test_gitstr__encode_base64(void)
+{
+ git_str buf = GIT_STR_INIT;
+
+ /* t h i s
+ * 0x 74 68 69 73
+ * 0b 01110100 01101000 01101001 01110011
+ * 0b 011101 000110 100001 101001 011100 110000
+ * 0x 1d 06 21 29 1c 30
+ * d G h p c w
+ */
+ cl_git_pass(git_str_encode_base64(&buf, "this", 4));
+ cl_assert_equal_s("dGhpcw==", buf.ptr);
+
+ git_str_clear(&buf);
+ cl_git_pass(git_str_encode_base64(&buf, "this!", 5));
+ cl_assert_equal_s("dGhpcyE=", buf.ptr);
+
+ git_str_clear(&buf);
+ cl_git_pass(git_str_encode_base64(&buf, "this!\n", 6));
+ cl_assert_equal_s("dGhpcyEK", buf.ptr);
+
+ git_str_dispose(&buf);
+}
+
+void test_gitstr__decode_base64(void)
+{
+ git_str buf = GIT_STR_INIT;
+
+ cl_git_pass(git_str_decode_base64(&buf, "dGhpcw==", 8));
+ cl_assert_equal_s("this", buf.ptr);
+
+ git_str_clear(&buf);
+ cl_git_pass(git_str_decode_base64(&buf, "dGhpcyE=", 8));
+ cl_assert_equal_s("this!", buf.ptr);
+
+ git_str_clear(&buf);
+ cl_git_pass(git_str_decode_base64(&buf, "dGhpcyEK", 8));
+ cl_assert_equal_s("this!\n", buf.ptr);
+
+ cl_git_fail(git_str_decode_base64(&buf, "This is not a valid base64 string!!!", 36));
+ cl_assert_equal_s("this!\n", buf.ptr);
+
+ git_str_dispose(&buf);
+}
+
+void test_gitstr__encode_base85(void)
+{
+ git_str buf = GIT_STR_INIT;
+
+ cl_git_pass(git_str_encode_base85(&buf, "this", 4));
+ cl_assert_equal_s("bZBXF", buf.ptr);
+ git_str_clear(&buf);
+
+ cl_git_pass(git_str_encode_base85(&buf, "two rnds", 8));
+ cl_assert_equal_s("ba!tca&BaE", buf.ptr);
+ git_str_clear(&buf);
+
+ cl_git_pass(git_str_encode_base85(&buf, "this is base 85 encoded",
+ strlen("this is base 85 encoded")));
+ cl_assert_equal_s("bZBXFAZc?TVqtS-AUHK3Wo~0{WMyOk", buf.ptr);
+ git_str_clear(&buf);
+
+ git_str_dispose(&buf);
+}
+
+void test_gitstr__decode_base85(void)
+{
+ git_str buf = GIT_STR_INIT;
+
+ cl_git_pass(git_str_decode_base85(&buf, "bZBXF", 5, 4));
+ cl_assert_equal_sz(4, buf.size);
+ cl_assert_equal_s("this", buf.ptr);
+ git_str_clear(&buf);
+
+ cl_git_pass(git_str_decode_base85(&buf, "ba!tca&BaE", 10, 8));
+ cl_assert_equal_sz(8, buf.size);
+ cl_assert_equal_s("two rnds", buf.ptr);
+ git_str_clear(&buf);
+
+ cl_git_pass(git_str_decode_base85(&buf, "bZBXFAZc?TVqtS-AUHK3Wo~0{WMyOk", 30, 23));
+ cl_assert_equal_sz(23, buf.size);
+ cl_assert_equal_s("this is base 85 encoded", buf.ptr);
+ git_str_clear(&buf);
+
+ git_str_dispose(&buf);
+}
+
+void test_gitstr__decode_base85_fails_gracefully(void)
+{
+ git_str buf = GIT_STR_INIT;
+
+ git_str_puts(&buf, "foobar");
+
+ cl_git_fail(git_str_decode_base85(&buf, "invalid charsZZ", 15, 42));
+ cl_git_fail(git_str_decode_base85(&buf, "invalidchars__ ", 15, 42));
+ cl_git_fail(git_str_decode_base85(&buf, "overflowZZ~~~~~", 15, 42));
+ cl_git_fail(git_str_decode_base85(&buf, "truncated", 9, 42));
+ cl_assert_equal_sz(6, buf.size);
+ cl_assert_equal_s("foobar", buf.ptr);
+
+ git_str_dispose(&buf);
+}
+
+void test_gitstr__classify_with_utf8(void)
+{
+ char *data0 = "Simple text\n";
+ size_t data0len = 12;
+ char *data1 = "Is that UTF-8 data I see…\nYep!\n";
+ size_t data1len = 31;
+ char *data2 = "Internal NUL!!!\000\n\nI see you!\n";
+ size_t data2len = 29;
+ char *data3 = "\xef\xbb\xbfThis is UTF-8 with a BOM.\n";
+ size_t data3len = 20;
+ git_str b;
+
+ b.ptr = data0; b.size = b.asize = data0len;
+ cl_assert(!git_str_is_binary(&b));
+ cl_assert(!git_str_contains_nul(&b));
+
+ b.ptr = data1; b.size = b.asize = data1len;
+ cl_assert(!git_str_is_binary(&b));
+ cl_assert(!git_str_contains_nul(&b));
+
+ b.ptr = data2; b.size = b.asize = data2len;
+ cl_assert(git_str_is_binary(&b));
+ cl_assert(git_str_contains_nul(&b));
+
+ b.ptr = data3; b.size = b.asize = data3len;
+ cl_assert(!git_str_is_binary(&b));
+ cl_assert(!git_str_contains_nul(&b));
+}
+
+#include "crlf.h"
+
+#define check_buf(expected,buf) do { \
+ cl_assert_equal_s(expected, buf.ptr); \
+ cl_assert_equal_sz(strlen(expected), buf.size); } while (0)
+
+void test_gitstr__lf_and_crlf_conversions(void)
+{
+ git_str src = GIT_STR_INIT, tgt = GIT_STR_INIT;
+
+ /* LF source */
+
+ git_str_sets(&src, "lf\nlf\nlf\nlf\n");
+
+ cl_git_pass(git_str_lf_to_crlf(&tgt, &src));
+ check_buf("lf\r\nlf\r\nlf\r\nlf\r\n", tgt);
+
+ cl_git_pass(git_str_crlf_to_lf(&tgt, &src));
+ check_buf(src.ptr, tgt);
+
+ git_str_sets(&src, "\nlf\nlf\nlf\nlf\nlf");
+
+ cl_git_pass(git_str_lf_to_crlf(&tgt, &src));
+ check_buf("\r\nlf\r\nlf\r\nlf\r\nlf\r\nlf", tgt);
+
+ cl_git_pass(git_str_crlf_to_lf(&tgt, &src));
+ check_buf(src.ptr, tgt);
+
+ /* CRLF source */
+
+ git_str_sets(&src, "crlf\r\ncrlf\r\ncrlf\r\ncrlf\r\n");
+
+ cl_git_pass(git_str_lf_to_crlf(&tgt, &src));
+ check_buf("crlf\r\ncrlf\r\ncrlf\r\ncrlf\r\n", tgt);
+
+ git_str_sets(&src, "crlf\r\ncrlf\r\ncrlf\r\ncrlf\r\n");
+
+ cl_git_pass(git_str_crlf_to_lf(&tgt, &src));
+ check_buf("crlf\ncrlf\ncrlf\ncrlf\n", tgt);
+
+ git_str_sets(&src, "\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf");
+
+ cl_git_pass(git_str_lf_to_crlf(&tgt, &src));
+ check_buf("\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf", tgt);
+
+ git_str_sets(&src, "\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf\r\ncrlf");
+
+ cl_git_pass(git_str_crlf_to_lf(&tgt, &src));
+ check_buf("\ncrlf\ncrlf\ncrlf\ncrlf\ncrlf", tgt);
+
+ /* CRLF in LF text */
+
+ git_str_sets(&src, "\nlf\nlf\ncrlf\r\nlf\nlf\ncrlf\r\n");
+
+ cl_git_pass(git_str_lf_to_crlf(&tgt, &src));
+ check_buf("\r\nlf\r\nlf\r\ncrlf\r\nlf\r\nlf\r\ncrlf\r\n", tgt);
+
+ git_str_sets(&src, "\nlf\nlf\ncrlf\r\nlf\nlf\ncrlf\r\n");
+
+ cl_git_pass(git_str_crlf_to_lf(&tgt, &src));
+ check_buf("\nlf\nlf\ncrlf\nlf\nlf\ncrlf\n", tgt);
+
+ /* LF in CRLF text */
+
+ git_str_sets(&src, "\ncrlf\r\ncrlf\r\nlf\ncrlf\r\ncrlf");
+
+ cl_git_pass(git_str_lf_to_crlf(&tgt, &src));
+ check_buf("\r\ncrlf\r\ncrlf\r\nlf\r\ncrlf\r\ncrlf", tgt);
+
+ cl_git_pass(git_str_crlf_to_lf(&tgt, &src));
+ check_buf("\ncrlf\ncrlf\nlf\ncrlf\ncrlf", tgt);
+
+ /* bare CR test */
+
+ git_str_sets(&src, "\rcrlf\r\nlf\nlf\ncr\rcrlf\r\nlf\ncr\r");
+
+ cl_git_pass(git_str_lf_to_crlf(&tgt, &src));
+ check_buf("\rcrlf\r\nlf\r\nlf\r\ncr\rcrlf\r\nlf\r\ncr\r", tgt);
+
+ git_str_sets(&src, "\rcrlf\r\nlf\nlf\ncr\rcrlf\r\nlf\ncr\r");
+
+ cl_git_pass(git_str_crlf_to_lf(&tgt, &src));
+ check_buf("\rcrlf\nlf\nlf\ncr\rcrlf\nlf\ncr\r", tgt);
+
+ git_str_sets(&src, "\rcr\r");
+ cl_git_pass(git_str_lf_to_crlf(&tgt, &src));
+ check_buf(src.ptr, tgt);
+ cl_git_pass(git_str_crlf_to_lf(&tgt, &src));
+ check_buf("\rcr\r", tgt);
+
+ git_str_dispose(&src);
+ git_str_dispose(&tgt);
+
+ /* blob correspondence tests */
+
+ git_str_sets(&src, ALL_CRLF_TEXT_RAW);
+ cl_git_pass(git_str_lf_to_crlf(&tgt, &src));
+ check_buf(ALL_CRLF_TEXT_AS_CRLF, tgt);
+ git_str_sets(&src, ALL_CRLF_TEXT_RAW);
+ cl_git_pass(git_str_crlf_to_lf(&tgt, &src));
+ check_buf(ALL_CRLF_TEXT_AS_LF, tgt);
+ git_str_dispose(&src);
+ git_str_dispose(&tgt);
+
+ git_str_sets(&src, ALL_LF_TEXT_RAW);
+ cl_git_pass(git_str_lf_to_crlf(&tgt, &src));
+ check_buf(ALL_LF_TEXT_AS_CRLF, tgt);
+ git_str_sets(&src, ALL_LF_TEXT_RAW);
+ cl_git_pass(git_str_crlf_to_lf(&tgt, &src));
+ check_buf(ALL_LF_TEXT_AS_LF, tgt);
+ git_str_dispose(&src);
+ git_str_dispose(&tgt);
+
+ git_str_sets(&src, MORE_CRLF_TEXT_RAW);
+ cl_git_pass(git_str_lf_to_crlf(&tgt, &src));
+ check_buf(MORE_CRLF_TEXT_AS_CRLF, tgt);
+ git_str_sets(&src, MORE_CRLF_TEXT_RAW);
+ cl_git_pass(git_str_crlf_to_lf(&tgt, &src));
+ check_buf(MORE_CRLF_TEXT_AS_LF, tgt);
+ git_str_dispose(&src);
+ git_str_dispose(&tgt);
+
+ git_str_sets(&src, MORE_LF_TEXT_RAW);
+ cl_git_pass(git_str_lf_to_crlf(&tgt, &src));
+ check_buf(MORE_LF_TEXT_AS_CRLF, tgt);
+ git_str_sets(&src, MORE_LF_TEXT_RAW);
+ cl_git_pass(git_str_crlf_to_lf(&tgt, &src));
+ check_buf(MORE_LF_TEXT_AS_LF, tgt);
+ git_str_dispose(&src);
+ git_str_dispose(&tgt);
+}
+
+void test_gitstr__dont_grow_borrowed(void)
+{
+ const char *somestring = "blah blah";
+ git_str buf = GIT_STR_INIT;
+
+ git_str_attach_notowned(&buf, somestring, strlen(somestring) + 1);
+ cl_assert_equal_p(somestring, buf.ptr);
+ cl_assert_equal_i(0, buf.asize);
+ cl_assert_equal_i(strlen(somestring) + 1, buf.size);
+
+ cl_git_fail_with(GIT_EINVALID, git_str_grow(&buf, 1024));
+}
+
+void test_gitstr__dont_hit_infinite_loop_when_resizing(void)
+{
+ git_str buf = GIT_STR_INIT;
+
+ cl_git_pass(git_str_puts(&buf, "foobar"));
+ /*
+ * We do not care whether this succeeds or fails, which
+ * would depend on platform-specific allocation
+ * semantics. We only want to know that the function
+ * actually returns.
+ */
+ (void)git_str_try_grow(&buf, SIZE_MAX, true);
+
+ git_str_dispose(&buf);
+}
+
+void test_gitstr__avoid_printing_into_oom_buffer(void)
+{
+ git_str buf = GIT_STR_INIT;
+
+ /* Emulate OOM situation with a previous allocation */
+ buf.asize = 8;
+ buf.ptr = git_str__oom;
+
+ /*
+ * Print the same string again. As the buffer still has
+ * an `asize` of 8 due to the previous print,
+ * `ENSURE_SIZE` would not try to reallocate the array at
+ * all. As it didn't explicitly check for `git_str__oom`
+ * in earlier versions, this would've resulted in it
+ * returning successfully and thus `git_str_puts` would
+ * just print into the `git_str__oom` array.
+ */
+ cl_git_fail(git_str_puts(&buf, "foobar"));
+}
diff --git a/tests/util/hex.c b/tests/util/hex.c
new file mode 100644
index 0000000..c5fea0a
--- /dev/null
+++ b/tests/util/hex.c
@@ -0,0 +1,22 @@
+#include "clar_libgit2.h"
+#include "util.h"
+
+void test_hex__fromhex(void)
+{
+ /* Passing cases */
+ cl_assert(git__fromhex('0') == 0x0);
+ cl_assert(git__fromhex('1') == 0x1);
+ cl_assert(git__fromhex('3') == 0x3);
+ cl_assert(git__fromhex('9') == 0x9);
+ cl_assert(git__fromhex('A') == 0xa);
+ cl_assert(git__fromhex('C') == 0xc);
+ cl_assert(git__fromhex('F') == 0xf);
+ cl_assert(git__fromhex('a') == 0xa);
+ cl_assert(git__fromhex('c') == 0xc);
+ cl_assert(git__fromhex('f') == 0xf);
+
+ /* Failing cases */
+ cl_assert(git__fromhex('g') == -1);
+ cl_assert(git__fromhex('z') == -1);
+ cl_assert(git__fromhex('X') == -1);
+}
diff --git a/tests/util/hostname.c b/tests/util/hostname.c
new file mode 100644
index 0000000..5d8bfe2
--- /dev/null
+++ b/tests/util/hostname.c
@@ -0,0 +1,13 @@
+#include "clar_libgit2.h"
+#include "net.h"
+
+void test_hostname__matches_cert(void)
+{
+ cl_assert_equal_b(true, git_net_hostname_matches_cert("www.example.org", "*.example.org"));
+ cl_assert_equal_b(true, git_net_hostname_matches_cert("www.foo.example.org", "*.foo.example.org"));
+ cl_assert_equal_b(false, git_net_hostname_matches_cert("foo.example.org", "*.foo.example.org"));
+ cl_assert_equal_b(false, git_net_hostname_matches_cert("www.example.org", "*.foo.example.org"));
+ cl_assert_equal_b(false, git_net_hostname_matches_cert("example.org", "*.example.org"));
+ cl_assert_equal_b(false, git_net_hostname_matches_cert("www.foo.example.org", "*.example.org"));
+ cl_assert_equal_b(false, git_net_hostname_matches_cert("blah.www.www.example.org", "*.example.org"));
+}
diff --git a/tests/util/iconv.c b/tests/util/iconv.c
new file mode 100644
index 0000000..e14aebb
--- /dev/null
+++ b/tests/util/iconv.c
@@ -0,0 +1,78 @@
+#include "clar_libgit2.h"
+#include "fs_path.h"
+
+#ifdef GIT_USE_ICONV
+static git_fs_path_iconv_t ic;
+static char *nfc = "\xC3\x85\x73\x74\x72\xC3\xB6\x6D";
+static char *nfd = "\x41\xCC\x8A\x73\x74\x72\x6F\xCC\x88\x6D";
+#endif
+
+void test_iconv__initialize(void)
+{
+#ifdef GIT_USE_ICONV
+ cl_git_pass(git_fs_path_iconv_init_precompose(&ic));
+#endif
+}
+
+void test_iconv__cleanup(void)
+{
+#ifdef GIT_USE_ICONV
+ git_fs_path_iconv_clear(&ic);
+#endif
+}
+
+void test_iconv__unchanged(void)
+{
+#ifdef GIT_USE_ICONV
+ const char *data = "Ascii data", *original = data;
+ size_t datalen = strlen(data);
+
+ cl_git_pass(git_fs_path_iconv(&ic, &data, &datalen));
+ GIT_UNUSED(datalen);
+
+ /* There are no high bits set, so this should leave data untouched */
+ cl_assert(data == original);
+#endif
+}
+
+void test_iconv__decomposed_to_precomposed(void)
+{
+#ifdef GIT_USE_ICONV
+ const char *data = nfd;
+ size_t datalen, nfdlen = strlen(nfd);
+
+ datalen = nfdlen;
+ cl_git_pass(git_fs_path_iconv(&ic, &data, &datalen));
+ GIT_UNUSED(datalen);
+
+ /* The decomposed nfd string should be transformed to the nfc form
+ * (on platforms where iconv is enabled, of course).
+ */
+ cl_assert_equal_s(nfc, data);
+
+ /* should be able to do it multiple times with the same git_fs_path_iconv_t */
+ data = nfd; datalen = nfdlen;
+ cl_git_pass(git_fs_path_iconv(&ic, &data, &datalen));
+ cl_assert_equal_s(nfc, data);
+
+ data = nfd; datalen = nfdlen;
+ cl_git_pass(git_fs_path_iconv(&ic, &data, &datalen));
+ cl_assert_equal_s(nfc, data);
+#endif
+}
+
+void test_iconv__precomposed_is_unmodified(void)
+{
+#ifdef GIT_USE_ICONV
+ const char *data = nfc;
+ size_t datalen = strlen(nfc);
+
+ cl_git_pass(git_fs_path_iconv(&ic, &data, &datalen));
+ GIT_UNUSED(datalen);
+
+ /* data is already in precomposed form, so even though some bytes have
+ * the high-bit set, the iconv transform should result in no change.
+ */
+ cl_assert_equal_s(nfc, data);
+#endif
+}
diff --git a/tests/util/init.c b/tests/util/init.c
new file mode 100644
index 0000000..78b3dd0
--- /dev/null
+++ b/tests/util/init.c
@@ -0,0 +1,54 @@
+#include "clar_libgit2.h"
+
+void test_init__returns_count(void)
+{
+ /* libgit2_tests initializes us first, so we have an existing
+ * initialization.
+ */
+ cl_assert_equal_i(2, git_libgit2_init());
+ cl_assert_equal_i(3, git_libgit2_init());
+
+ cl_assert_equal_i(2, git_libgit2_shutdown());
+ cl_assert_equal_i(1, git_libgit2_shutdown());
+}
+
+void test_init__reinit_succeeds(void)
+{
+ cl_assert_equal_i(0, git_libgit2_shutdown());
+ cl_assert_equal_i(1, git_libgit2_init());
+ cl_sandbox_set_search_path_defaults();
+}
+
+#ifdef GIT_THREADS
+static void *reinit(void *unused)
+{
+ unsigned i;
+
+ for (i = 0; i < 20; i++) {
+ cl_assert(git_libgit2_init() > 0);
+ cl_assert(git_libgit2_shutdown() >= 0);
+ }
+
+ return unused;
+}
+#endif
+
+void test_init__concurrent_init_succeeds(void)
+{
+#ifdef GIT_THREADS
+ git_thread threads[10];
+ unsigned i;
+
+ cl_assert_equal_i(2, git_libgit2_init());
+
+ for (i = 0; i < ARRAY_SIZE(threads); i++)
+ git_thread_create(&threads[i], reinit, NULL);
+ for (i = 0; i < ARRAY_SIZE(threads); i++)
+ git_thread_join(&threads[i], NULL);
+
+ cl_assert_equal_i(1, git_libgit2_shutdown());
+ cl_sandbox_set_search_path_defaults();
+#else
+ cl_skip();
+#endif
+}
diff --git a/tests/util/integer.c b/tests/util/integer.c
new file mode 100644
index 0000000..2226ab4
--- /dev/null
+++ b/tests/util/integer.c
@@ -0,0 +1,253 @@
+#include "clar_libgit2.h"
+
+void test_integer__multiply_int64_no_overflow(void)
+{
+#if !defined(git__multiply_int64_overflow)
+ int64_t result = 0;
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(0x0)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(0x1)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(-0x1)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(0x2)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(-0x2)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(0x7ffffffffffffff)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(-0x7ffffffffffffff)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(0x800000000000000)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(-0x800000000000000)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(0x7fffffffffffffff)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(-0x7fffffffffffffff)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x0), INT64_C(-0x8000000000000000)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(0x0)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(0x1)));
+ cl_assert_equal_i(result, INT64_C(0x1));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(-0x1)));
+ cl_assert_equal_i(result, INT64_C(-0x1));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(0x2)));
+ cl_assert_equal_i(result, INT64_C(0x2));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(-0x2)));
+ cl_assert_equal_i(result, INT64_C(-0x2));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(0x7ffffffffffffff)));
+ cl_assert_equal_i(result, INT64_C(0x7ffffffffffffff));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(-0x7ffffffffffffff)));
+ cl_assert_equal_i(result, INT64_C(-0x7ffffffffffffff));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(0x800000000000000)));
+ cl_assert_equal_i(result, INT64_C(0x800000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(-0x800000000000000)));
+ cl_assert_equal_i(result, INT64_C(-0x800000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(0x7fffffffffffffff)));
+ cl_assert_equal_i(result, INT64_C(0x7fffffffffffffff));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(-0x7fffffffffffffff)));
+ cl_assert_equal_i(result, INT64_C(-0x7fffffffffffffff));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(0x0)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(0x1)));
+ cl_assert_equal_i(result, INT64_C(-0x1));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(-0x1)));
+ cl_assert_equal_i(result, INT64_C(0x1));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(0x2)));
+ cl_assert_equal_i(result, INT64_C(-0x2));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(-0x2)));
+ cl_assert_equal_i(result, INT64_C(0x2));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(0x7ffffffffffffff)));
+ cl_assert_equal_i(result, INT64_C(-0x7ffffffffffffff));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(-0x7ffffffffffffff)));
+ cl_assert_equal_i(result, INT64_C(0x7ffffffffffffff));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(0x800000000000000)));
+ cl_assert_equal_i(result, INT64_C(-0x800000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(-0x800000000000000)));
+ cl_assert_equal_i(result, INT64_C(0x800000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(0x7fffffffffffffff)));
+ cl_assert_equal_i(result, INT64_C(-0x7fffffffffffffff));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(-0x7fffffffffffffff)));
+ cl_assert_equal_i(result, INT64_C(0x7fffffffffffffff));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(0x0)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(0x1)));
+ cl_assert_equal_i(result, INT64_C(0x2));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(-0x1)));
+ cl_assert_equal_i(result, INT64_C(-0x2));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(0x2)));
+ cl_assert_equal_i(result, INT64_C(0x4));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(-0x2)));
+ cl_assert_equal_i(result, INT64_C(-0x4));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(0x7ffffffffffffff)));
+ cl_assert_equal_i(result, INT64_C(0xffffffffffffffe));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(-0x7ffffffffffffff)));
+ cl_assert_equal_i(result, INT64_C(-0xffffffffffffffe));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(0x800000000000000)));
+ cl_assert_equal_i(result, INT64_C(0x1000000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(-0x800000000000000)));
+ cl_assert_equal_i(result, INT64_C(-0x1000000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(0x0)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(0x1)));
+ cl_assert_equal_i(result, INT64_C(-0x2));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(-0x1)));
+ cl_assert_equal_i(result, INT64_C(0x2));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(0x2)));
+ cl_assert_equal_i(result, INT64_C(-0x4));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(-0x2)));
+ cl_assert_equal_i(result, INT64_C(0x4));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(0x7ffffffffffffff)));
+ cl_assert_equal_i(result, INT64_C(-0xffffffffffffffe));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(-0x7ffffffffffffff)));
+ cl_assert_equal_i(result, INT64_C(0xffffffffffffffe));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(0x800000000000000)));
+ cl_assert_equal_i(result, INT64_C(-0x1000000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(-0x800000000000000)));
+ cl_assert_equal_i(result, INT64_C(0x1000000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(-0x4000000000000000)));
+ cl_assert_equal_i(result, INT64_C(-0x8000000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(0x0)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(0x1)));
+ cl_assert_equal_i(result, INT64_C(0x7ffffffffffffff));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(-0x1)));
+ cl_assert_equal_i(result, INT64_C(-0x7ffffffffffffff));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(0x2)));
+ cl_assert_equal_i(result, INT64_C(0xffffffffffffffe));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(-0x2)));
+ cl_assert_equal_i(result, INT64_C(-0xffffffffffffffe));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(0x0)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(0x1)));
+ cl_assert_equal_i(result, INT64_C(-0x7ffffffffffffff));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(-0x1)));
+ cl_assert_equal_i(result, INT64_C(0x7ffffffffffffff));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(0x2)));
+ cl_assert_equal_i(result, INT64_C(-0xffffffffffffffe));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(-0x2)));
+ cl_assert_equal_i(result, INT64_C(0xffffffffffffffe));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(0x0)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(0x1)));
+ cl_assert_equal_i(result, INT64_C(0x800000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(-0x1)));
+ cl_assert_equal_i(result, INT64_C(-0x800000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(0x2)));
+ cl_assert_equal_i(result, INT64_C(0x1000000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(-0x2)));
+ cl_assert_equal_i(result, INT64_C(-0x1000000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(0x0)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(0x1)));
+ cl_assert_equal_i(result, INT64_C(-0x800000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(-0x1)));
+ cl_assert_equal_i(result, INT64_C(0x800000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(0x2)));
+ cl_assert_equal_i(result, INT64_C(-0x1000000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(-0x2)));
+ cl_assert_equal_i(result, INT64_C(0x1000000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(0x0)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(0x1)));
+ cl_assert_equal_i(result, INT64_C(0x7fffffffffffffff));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(-0x1)));
+ cl_assert_equal_i(result, INT64_C(-0x7fffffffffffffff));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x4000000000000000), INT64_C(0x2)));
+ cl_assert_equal_i(result, INT64_C(-0x8000000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(0x0)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(0x1)));
+ cl_assert_equal_i(result, INT64_C(-0x7fffffffffffffff));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(-0x1)));
+ cl_assert_equal_i(result, INT64_C(0x7fffffffffffffff));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(0x0)));
+ cl_assert_equal_i(result, INT64_C(0x0));
+#endif
+}
+
+void test_integer__multiply_int64_overflow(void)
+{
+#if !defined(git__multiply_int64_overflow)
+ int64_t result = 0;
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(0x4000000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(-0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x2), INT64_C(-0x8000000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(-0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x2), INT64_C(-0x8000000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(0x7ffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(-0x7ffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(0x800000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(-0x800000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(-0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7ffffffffffffff), INT64_C(-0x8000000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(0x7ffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(-0x7ffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(0x800000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(-0x800000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(-0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7ffffffffffffff), INT64_C(-0x8000000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(0x7ffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(-0x7ffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(0x800000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(-0x800000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(-0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x800000000000000), INT64_C(-0x8000000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(0x7ffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(-0x7ffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(0x800000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(-0x800000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(-0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x800000000000000), INT64_C(-0x8000000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x4000000000000000), INT64_C(0x2)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(0x2)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(-0x2)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(0x7ffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(-0x7ffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(0x800000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(-0x800000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(-0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x7fffffffffffffff), INT64_C(-0x8000000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(0x2)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(-0x2)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(0x7ffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(-0x7ffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(0x800000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(-0x800000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(-0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x7fffffffffffffff), INT64_C(-0x8000000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(0x2)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(-0x2)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(0x7ffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(-0x7ffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(0x800000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(-0x800000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(-0x7fffffffffffffff)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(-0x8000000000000000)));
+#endif
+}
+
+void test_integer__multiply_int64_edge_cases(void)
+{
+#if !defined(git__multiply_int64_overflow)
+ int64_t result = 0;
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(-0x1)));
+ cl_assert_equal_i(result, INT64_C(-0x8000000000000000));
+ cl_assert(!git__multiply_int64_overflow(&result, INT64_C(-0x1), INT64_C(-0x8000000000000000)));
+ cl_assert_equal_i(result, INT64_C(-0x8000000000000000));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(0x1), INT64_C(-0x8000000000000000)));
+ cl_assert(git__multiply_int64_overflow(&result, INT64_C(-0x8000000000000000), INT64_C(0x1)));
+#endif
+}
diff --git a/tests/util/link.c b/tests/util/link.c
new file mode 100644
index 0000000..5909e26
--- /dev/null
+++ b/tests/util/link.c
@@ -0,0 +1,630 @@
+#include "clar_libgit2.h"
+#include "posix.h"
+
+#ifdef GIT_WIN32
+# include "win32/reparse.h"
+#endif
+
+void test_link__cleanup(void)
+{
+#ifdef GIT_WIN32
+ RemoveDirectory("lstat_junction");
+ RemoveDirectory("lstat_dangling");
+ RemoveDirectory("lstat_dangling_dir");
+ RemoveDirectory("lstat_dangling_junction");
+
+ RemoveDirectory("stat_junction");
+ RemoveDirectory("stat_dangling");
+ RemoveDirectory("stat_dangling_dir");
+ RemoveDirectory("stat_dangling_junction");
+#endif
+}
+
+#ifdef GIT_WIN32
+static bool should_run(void)
+{
+ static SID_IDENTIFIER_AUTHORITY authority = { SECURITY_NT_AUTHORITY };
+ PSID admin_sid;
+ BOOL is_admin;
+
+ cl_win32_pass(AllocateAndInitializeSid(&authority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &admin_sid));
+ cl_win32_pass(CheckTokenMembership(NULL, admin_sid, &is_admin));
+ FreeSid(admin_sid);
+
+ return is_admin ? true : false;
+}
+#else
+static bool should_run(void)
+{
+ return true;
+}
+#endif
+
+static void do_symlink(const char *old, const char *new, int is_dir)
+{
+#ifndef GIT_WIN32
+ GIT_UNUSED(is_dir);
+
+ cl_must_pass(symlink(old, new));
+#else
+ typedef DWORD (WINAPI *create_symlink_func)(LPCTSTR, LPCTSTR, DWORD);
+ HMODULE module;
+ create_symlink_func pCreateSymbolicLink;
+
+ cl_assert(module = GetModuleHandle("kernel32"));
+ cl_assert(pCreateSymbolicLink = (create_symlink_func)(void *)GetProcAddress(module, "CreateSymbolicLinkA"));
+
+ cl_win32_pass(pCreateSymbolicLink(new, old, is_dir));
+#endif
+}
+
+static void do_hardlink(const char *old, const char *new)
+{
+#ifndef GIT_WIN32
+ cl_must_pass(link(old, new));
+#else
+ typedef DWORD (WINAPI *create_hardlink_func)(LPCTSTR, LPCTSTR, LPSECURITY_ATTRIBUTES);
+ HMODULE module;
+ create_hardlink_func pCreateHardLink;
+
+ cl_assert(module = GetModuleHandle("kernel32"));
+ cl_assert(pCreateHardLink = (create_hardlink_func)(void *)GetProcAddress(module, "CreateHardLinkA"));
+
+ cl_win32_pass(pCreateHardLink(new, old, 0));
+#endif
+}
+
+#ifdef GIT_WIN32
+
+static void do_junction(const char *old, const char *new)
+{
+ GIT_REPARSE_DATA_BUFFER *reparse_buf;
+ HANDLE handle;
+ git_str unparsed_buf = GIT_STR_INIT;
+ wchar_t *subst_utf16, *print_utf16;
+ DWORD ioctl_ret;
+ int subst_utf16_len, subst_byte_len, print_utf16_len, print_byte_len, ret;
+ USHORT reparse_buflen;
+ size_t i;
+
+ /* Junction targets must be the unparsed name, starting with \??\, using
+ * backslashes instead of forward, and end in a trailing backslash.
+ * eg: \??\C:\Foo\
+ */
+ git_str_puts(&unparsed_buf, "\\??\\");
+
+ for (i = 0; i < strlen(old); i++)
+ git_str_putc(&unparsed_buf, old[i] == '/' ? '\\' : old[i]);
+
+ git_str_putc(&unparsed_buf, '\\');
+
+ subst_utf16_len = git_utf8_to_16(NULL, 0, git_str_cstr(&unparsed_buf));
+ subst_byte_len = subst_utf16_len * sizeof(WCHAR);
+
+ print_utf16_len = subst_utf16_len - 4;
+ print_byte_len = subst_byte_len - (4 * sizeof(WCHAR));
+
+ /* The junction must be an empty directory before the junction attribute
+ * can be added.
+ */
+ cl_win32_pass(CreateDirectoryA(new, NULL));
+
+ handle = CreateFileA(new, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
+ FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ cl_win32_pass(handle != INVALID_HANDLE_VALUE);
+
+ reparse_buflen = (USHORT)(REPARSE_DATA_HEADER_SIZE +
+ REPARSE_DATA_MOUNTPOINT_HEADER_SIZE +
+ subst_byte_len + sizeof(WCHAR) +
+ print_byte_len + sizeof(WCHAR));
+
+ reparse_buf = LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT, reparse_buflen);
+ cl_assert(reparse_buf);
+
+ subst_utf16 = reparse_buf->ReparseBuffer.MountPoint.PathBuffer;
+ print_utf16 = subst_utf16 + subst_utf16_len + 1;
+
+ ret = git_utf8_to_16(subst_utf16, subst_utf16_len + 1,
+ git_str_cstr(&unparsed_buf));
+ cl_assert_equal_i(subst_utf16_len, ret);
+
+ ret = git_utf8_to_16(print_utf16,
+ print_utf16_len + 1, git_str_cstr(&unparsed_buf) + 4);
+ cl_assert_equal_i(print_utf16_len, ret);
+
+ reparse_buf->ReparseTag = IO_REPARSE_TAG_MOUNT_POINT;
+ reparse_buf->ReparseBuffer.MountPoint.SubstituteNameOffset = 0;
+ reparse_buf->ReparseBuffer.MountPoint.SubstituteNameLength = subst_byte_len;
+ reparse_buf->ReparseBuffer.MountPoint.PrintNameOffset = (USHORT)(subst_byte_len + sizeof(WCHAR));
+ reparse_buf->ReparseBuffer.MountPoint.PrintNameLength = print_byte_len;
+ reparse_buf->ReparseDataLength = reparse_buflen - REPARSE_DATA_HEADER_SIZE;
+
+ cl_win32_pass(DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT,
+ reparse_buf, reparse_buflen, NULL, 0, &ioctl_ret, NULL));
+
+ CloseHandle(handle);
+ LocalFree(reparse_buf);
+
+ git_str_dispose(&unparsed_buf);
+}
+
+static void do_custom_reparse(const char *path)
+{
+ REPARSE_GUID_DATA_BUFFER *reparse_buf;
+ HANDLE handle;
+ DWORD ioctl_ret;
+
+ const char *reparse_data = "Reparse points are silly.";
+ size_t reparse_buflen = REPARSE_GUID_DATA_BUFFER_HEADER_SIZE +
+ strlen(reparse_data) + 1;
+
+ reparse_buf = LocalAlloc(LMEM_FIXED|LMEM_ZEROINIT, reparse_buflen);
+ cl_assert(reparse_buf);
+
+ reparse_buf->ReparseTag = 42;
+ reparse_buf->ReparseDataLength = (WORD)(strlen(reparse_data) + 1);
+
+ reparse_buf->ReparseGuid.Data1 = 0xdeadbeef;
+ reparse_buf->ReparseGuid.Data2 = 0xdead;
+ reparse_buf->ReparseGuid.Data3 = 0xbeef;
+ reparse_buf->ReparseGuid.Data4[0] = 42;
+ reparse_buf->ReparseGuid.Data4[1] = 42;
+ reparse_buf->ReparseGuid.Data4[2] = 42;
+ reparse_buf->ReparseGuid.Data4[3] = 42;
+ reparse_buf->ReparseGuid.Data4[4] = 42;
+ reparse_buf->ReparseGuid.Data4[5] = 42;
+ reparse_buf->ReparseGuid.Data4[6] = 42;
+ reparse_buf->ReparseGuid.Data4[7] = 42;
+ reparse_buf->ReparseGuid.Data4[8] = 42;
+
+ memcpy(reparse_buf->GenericReparseBuffer.DataBuffer,
+ reparse_data, strlen(reparse_data) + 1);
+
+ handle = CreateFileA(path, GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
+ FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, NULL);
+ cl_win32_pass(handle != INVALID_HANDLE_VALUE);
+
+ cl_win32_pass(DeviceIoControl(handle, FSCTL_SET_REPARSE_POINT,
+ reparse_buf,
+ reparse_buf->ReparseDataLength + REPARSE_GUID_DATA_BUFFER_HEADER_SIZE,
+ NULL, 0, &ioctl_ret, NULL));
+
+ CloseHandle(handle);
+ LocalFree(reparse_buf);
+}
+
+#endif
+
+void test_link__stat_regular_file(void)
+{
+ struct stat st;
+
+ cl_git_rewritefile("stat_regfile", "This is a regular file!\n");
+
+ cl_must_pass(p_stat("stat_regfile", &st));
+ cl_assert(S_ISREG(st.st_mode));
+ cl_assert_equal_i(24, st.st_size);
+}
+
+void test_link__lstat_regular_file(void)
+{
+ struct stat st;
+
+ cl_git_rewritefile("lstat_regfile", "This is a regular file!\n");
+
+ cl_must_pass(p_stat("lstat_regfile", &st));
+ cl_assert(S_ISREG(st.st_mode));
+ cl_assert_equal_i(24, st.st_size);
+}
+
+void test_link__stat_symlink(void)
+{
+ struct stat st;
+
+ if (!should_run())
+ clar__skip();
+
+ cl_git_rewritefile("stat_target", "This is the target of a symbolic link.\n");
+ do_symlink("stat_target", "stat_symlink", 0);
+
+ cl_must_pass(p_stat("stat_target", &st));
+ cl_assert(S_ISREG(st.st_mode));
+ cl_assert_equal_i(39, st.st_size);
+
+ cl_must_pass(p_stat("stat_symlink", &st));
+ cl_assert(S_ISREG(st.st_mode));
+ cl_assert_equal_i(39, st.st_size);
+}
+
+void test_link__stat_symlink_directory(void)
+{
+ struct stat st;
+
+ if (!should_run())
+ clar__skip();
+
+ p_mkdir("stat_dirtarget", 0777);
+ do_symlink("stat_dirtarget", "stat_dirlink", 1);
+
+ cl_must_pass(p_stat("stat_dirtarget", &st));
+ cl_assert(S_ISDIR(st.st_mode));
+
+ cl_must_pass(p_stat("stat_dirlink", &st));
+ cl_assert(S_ISDIR(st.st_mode));
+}
+
+void test_link__stat_symlink_chain(void)
+{
+ struct stat st;
+
+ if (!should_run())
+ clar__skip();
+
+ cl_git_rewritefile("stat_final_target", "Final target of some symbolic links...\n");
+ do_symlink("stat_final_target", "stat_chain_3", 0);
+ do_symlink("stat_chain_3", "stat_chain_2", 0);
+ do_symlink("stat_chain_2", "stat_chain_1", 0);
+
+ cl_must_pass(p_stat("stat_chain_1", &st));
+ cl_assert(S_ISREG(st.st_mode));
+ cl_assert_equal_i(39, st.st_size);
+}
+
+void test_link__stat_dangling_symlink(void)
+{
+ struct stat st;
+
+ if (!should_run())
+ clar__skip();
+
+ do_symlink("stat_nonexistent", "stat_dangling", 0);
+
+ cl_must_fail(p_stat("stat_nonexistent", &st));
+ cl_must_fail(p_stat("stat_dangling", &st));
+}
+
+void test_link__stat_dangling_symlink_directory(void)
+{
+ struct stat st;
+
+ if (!should_run())
+ clar__skip();
+
+ do_symlink("stat_nonexistent", "stat_dangling_dir", 1);
+
+ cl_must_fail(p_stat("stat_nonexistent_dir", &st));
+ cl_must_fail(p_stat("stat_dangling", &st));
+}
+
+void test_link__lstat_symlink(void)
+{
+ git_str target_path = GIT_STR_INIT;
+ struct stat st;
+
+ if (!should_run())
+ clar__skip();
+
+ /* Windows always writes the canonical path as the link target, so
+ * write the full path on all platforms.
+ */
+ git_str_join(&target_path, '/', clar_sandbox_path(), "lstat_target");
+
+ cl_git_rewritefile("lstat_target", "This is the target of a symbolic link.\n");
+ do_symlink(git_str_cstr(&target_path), "lstat_symlink", 0);
+
+ cl_must_pass(p_lstat("lstat_target", &st));
+ cl_assert(S_ISREG(st.st_mode));
+ cl_assert_equal_i(39, st.st_size);
+
+ cl_must_pass(p_lstat("lstat_symlink", &st));
+ cl_assert(S_ISLNK(st.st_mode));
+ cl_assert_equal_i(git_str_len(&target_path), st.st_size);
+
+ git_str_dispose(&target_path);
+}
+
+void test_link__lstat_symlink_directory(void)
+{
+ git_str target_path = GIT_STR_INIT;
+ struct stat st;
+
+ if (!should_run())
+ clar__skip();
+
+ git_str_join(&target_path, '/', clar_sandbox_path(), "lstat_dirtarget");
+
+ p_mkdir("lstat_dirtarget", 0777);
+ do_symlink(git_str_cstr(&target_path), "lstat_dirlink", 1);
+
+ cl_must_pass(p_lstat("lstat_dirtarget", &st));
+ cl_assert(S_ISDIR(st.st_mode));
+
+ cl_must_pass(p_lstat("lstat_dirlink", &st));
+ cl_assert(S_ISLNK(st.st_mode));
+ cl_assert_equal_i(git_str_len(&target_path), st.st_size);
+
+ git_str_dispose(&target_path);
+}
+
+void test_link__lstat_dangling_symlink(void)
+{
+ struct stat st;
+
+ if (!should_run())
+ clar__skip();
+
+ do_symlink("lstat_nonexistent", "lstat_dangling", 0);
+
+ cl_must_fail(p_lstat("lstat_nonexistent", &st));
+
+ cl_must_pass(p_lstat("lstat_dangling", &st));
+ cl_assert(S_ISLNK(st.st_mode));
+ cl_assert_equal_i(strlen("lstat_nonexistent"), st.st_size);
+}
+
+void test_link__lstat_dangling_symlink_directory(void)
+{
+ struct stat st;
+
+ if (!should_run())
+ clar__skip();
+
+ do_symlink("lstat_nonexistent", "lstat_dangling_dir", 1);
+
+ cl_must_fail(p_lstat("lstat_nonexistent", &st));
+
+ cl_must_pass(p_lstat("lstat_dangling_dir", &st));
+ cl_assert(S_ISLNK(st.st_mode));
+ cl_assert_equal_i(strlen("lstat_nonexistent"), st.st_size);
+}
+
+void test_link__stat_junction(void)
+{
+#ifdef GIT_WIN32
+ git_str target_path = GIT_STR_INIT;
+ struct stat st;
+
+ git_str_join(&target_path, '/', clar_sandbox_path(), "stat_junctarget");
+
+ p_mkdir("stat_junctarget", 0777);
+ do_junction(git_str_cstr(&target_path), "stat_junction");
+
+ cl_must_pass(p_stat("stat_junctarget", &st));
+ cl_assert(S_ISDIR(st.st_mode));
+
+ cl_must_pass(p_stat("stat_junction", &st));
+ cl_assert(S_ISDIR(st.st_mode));
+
+ git_str_dispose(&target_path);
+#endif
+}
+
+void test_link__stat_dangling_junction(void)
+{
+#ifdef GIT_WIN32
+ git_str target_path = GIT_STR_INIT;
+ struct stat st;
+
+ git_str_join(&target_path, '/', clar_sandbox_path(), "stat_nonexistent_junctarget");
+
+ p_mkdir("stat_nonexistent_junctarget", 0777);
+ do_junction(git_str_cstr(&target_path), "stat_dangling_junction");
+
+ RemoveDirectory("stat_nonexistent_junctarget");
+
+ cl_must_fail(p_stat("stat_nonexistent_junctarget", &st));
+ cl_must_fail(p_stat("stat_dangling_junction", &st));
+
+ git_str_dispose(&target_path);
+#endif
+}
+
+void test_link__lstat_junction(void)
+{
+#ifdef GIT_WIN32
+ git_str target_path = GIT_STR_INIT;
+ struct stat st;
+
+ git_str_join(&target_path, '/', clar_sandbox_path(), "lstat_junctarget");
+
+ p_mkdir("lstat_junctarget", 0777);
+ do_junction(git_str_cstr(&target_path), "lstat_junction");
+
+ cl_must_pass(p_lstat("lstat_junctarget", &st));
+ cl_assert(S_ISDIR(st.st_mode));
+
+ cl_must_pass(p_lstat("lstat_junction", &st));
+ cl_assert(S_ISLNK(st.st_mode));
+
+ git_str_dispose(&target_path);
+#endif
+}
+
+void test_link__lstat_dangling_junction(void)
+{
+#ifdef GIT_WIN32
+ git_str target_path = GIT_STR_INIT;
+ struct stat st;
+
+ git_str_join(&target_path, '/', clar_sandbox_path(), "lstat_nonexistent_junctarget");
+
+ p_mkdir("lstat_nonexistent_junctarget", 0777);
+ do_junction(git_str_cstr(&target_path), "lstat_dangling_junction");
+
+ RemoveDirectory("lstat_nonexistent_junctarget");
+
+ cl_must_fail(p_lstat("lstat_nonexistent_junctarget", &st));
+
+ cl_must_pass(p_lstat("lstat_dangling_junction", &st));
+ cl_assert(S_ISLNK(st.st_mode));
+ cl_assert_equal_i(git_str_len(&target_path), st.st_size);
+
+ git_str_dispose(&target_path);
+#endif
+}
+
+void test_link__stat_hardlink(void)
+{
+ struct stat st;
+
+ if (!should_run())
+ clar__skip();
+
+ cl_git_rewritefile("stat_hardlink1", "This file has many names!\n");
+ do_hardlink("stat_hardlink1", "stat_hardlink2");
+
+ cl_must_pass(p_stat("stat_hardlink1", &st));
+ cl_assert(S_ISREG(st.st_mode));
+ cl_assert_equal_i(26, st.st_size);
+
+ cl_must_pass(p_stat("stat_hardlink2", &st));
+ cl_assert(S_ISREG(st.st_mode));
+ cl_assert_equal_i(26, st.st_size);
+}
+
+void test_link__lstat_hardlink(void)
+{
+ struct stat st;
+
+ if (!should_run())
+ clar__skip();
+
+ cl_git_rewritefile("lstat_hardlink1", "This file has many names!\n");
+ do_hardlink("lstat_hardlink1", "lstat_hardlink2");
+
+ cl_must_pass(p_lstat("lstat_hardlink1", &st));
+ cl_assert(S_ISREG(st.st_mode));
+ cl_assert_equal_i(26, st.st_size);
+
+ cl_must_pass(p_lstat("lstat_hardlink2", &st));
+ cl_assert(S_ISREG(st.st_mode));
+ cl_assert_equal_i(26, st.st_size);
+}
+
+void test_link__stat_reparse_point(void)
+{
+#ifdef GIT_WIN32
+ struct stat st;
+
+ /* Generic reparse points should be treated as regular files, only
+ * symlinks and junctions should be treated as links.
+ */
+
+ cl_git_rewritefile("stat_reparse", "This is a reparse point!\n");
+ do_custom_reparse("stat_reparse");
+
+ cl_must_pass(p_lstat("stat_reparse", &st));
+ cl_assert(S_ISREG(st.st_mode));
+ cl_assert_equal_i(25, st.st_size);
+#endif
+}
+
+void test_link__lstat_reparse_point(void)
+{
+#ifdef GIT_WIN32
+ struct stat st;
+
+ cl_git_rewritefile("lstat_reparse", "This is a reparse point!\n");
+ do_custom_reparse("lstat_reparse");
+
+ cl_must_pass(p_lstat("lstat_reparse", &st));
+ cl_assert(S_ISREG(st.st_mode));
+ cl_assert_equal_i(25, st.st_size);
+#endif
+}
+
+void test_link__readlink_nonexistent_file(void)
+{
+ char buf[2048];
+
+ cl_must_fail(p_readlink("readlink_nonexistent", buf, 2048));
+ cl_assert_equal_i(ENOENT, errno);
+}
+
+void test_link__readlink_normal_file(void)
+{
+ char buf[2048];
+
+ cl_git_rewritefile("readlink_regfile", "This is a regular file!\n");
+ cl_must_fail(p_readlink("readlink_regfile", buf, 2048));
+ cl_assert_equal_i(EINVAL, errno);
+}
+
+void test_link__readlink_symlink(void)
+{
+ git_str target_path = GIT_STR_INIT;
+ int len;
+ char buf[2048];
+
+ if (!should_run())
+ clar__skip();
+
+ git_str_join(&target_path, '/', clar_sandbox_path(), "readlink_target");
+
+ cl_git_rewritefile("readlink_target", "This is the target of a symlink\n");
+ do_symlink(git_str_cstr(&target_path), "readlink_link", 0);
+
+ len = p_readlink("readlink_link", buf, 2048);
+ cl_must_pass(len);
+
+ buf[len] = 0;
+
+ cl_assert_equal_s(git_str_cstr(&target_path), buf);
+
+ git_str_dispose(&target_path);
+}
+
+void test_link__readlink_dangling(void)
+{
+ git_str target_path = GIT_STR_INIT;
+ int len;
+ char buf[2048];
+
+ if (!should_run())
+ clar__skip();
+
+ git_str_join(&target_path, '/', clar_sandbox_path(), "readlink_nonexistent");
+
+ do_symlink(git_str_cstr(&target_path), "readlink_dangling", 0);
+
+ len = p_readlink("readlink_dangling", buf, 2048);
+ cl_must_pass(len);
+
+ buf[len] = 0;
+
+ cl_assert_equal_s(git_str_cstr(&target_path), buf);
+
+ git_str_dispose(&target_path);
+}
+
+void test_link__readlink_multiple(void)
+{
+ git_str target_path = GIT_STR_INIT,
+ path3 = GIT_STR_INIT, path2 = GIT_STR_INIT, path1 = GIT_STR_INIT;
+ int len;
+ char buf[2048];
+
+ if (!should_run())
+ clar__skip();
+
+ git_str_join(&target_path, '/', clar_sandbox_path(), "readlink_final");
+ git_str_join(&path3, '/', clar_sandbox_path(), "readlink_3");
+ git_str_join(&path2, '/', clar_sandbox_path(), "readlink_2");
+ git_str_join(&path1, '/', clar_sandbox_path(), "readlink_1");
+
+ do_symlink(git_str_cstr(&target_path), git_str_cstr(&path3), 0);
+ do_symlink(git_str_cstr(&path3), git_str_cstr(&path2), 0);
+ do_symlink(git_str_cstr(&path2), git_str_cstr(&path1), 0);
+
+ len = p_readlink("readlink_1", buf, 2048);
+ cl_must_pass(len);
+
+ buf[len] = 0;
+
+ cl_assert_equal_s(git_str_cstr(&path2), buf);
+
+ git_str_dispose(&path1);
+ git_str_dispose(&path2);
+ git_str_dispose(&path3);
+ git_str_dispose(&target_path);
+}
diff --git a/tests/util/memmem.c b/tests/util/memmem.c
new file mode 100644
index 0000000..1c713e8
--- /dev/null
+++ b/tests/util/memmem.c
@@ -0,0 +1,46 @@
+#include "clar_libgit2.h"
+
+static void assert_found(const char *haystack, const char *needle, size_t expected_pos)
+{
+ cl_assert_equal_p(git__memmem(haystack, haystack ? strlen(haystack) : 0,
+ needle, needle ? strlen(needle) : 0),
+ haystack + expected_pos);
+}
+
+static void assert_absent(const char *haystack, const char *needle)
+{
+ cl_assert_equal_p(git__memmem(haystack, haystack ? strlen(haystack) : 0,
+ needle, needle ? strlen(needle) : 0),
+ NULL);
+}
+
+void test_memmem__found(void)
+{
+ assert_found("a", "a", 0);
+ assert_found("ab", "a", 0);
+ assert_found("ba", "a", 1);
+ assert_found("aa", "a", 0);
+ assert_found("aab", "aa", 0);
+ assert_found("baa", "aa", 1);
+ assert_found("dabc", "abc", 1);
+ assert_found("abababc", "abc", 4);
+}
+
+void test_memmem__absent(void)
+{
+ assert_absent("a", "b");
+ assert_absent("a", "aa");
+ assert_absent("ba", "ab");
+ assert_absent("ba", "ab");
+ assert_absent("abc", "abcd");
+ assert_absent("abcabcabc", "bcac");
+}
+
+void test_memmem__edgecases(void)
+{
+ assert_absent(NULL, NULL);
+ assert_absent("a", NULL);
+ assert_absent(NULL, "a");
+ assert_absent("", "a");
+ assert_absent("a", "");
+}
diff --git a/tests/util/mkdir.c b/tests/util/mkdir.c
new file mode 100644
index 0000000..8658eec
--- /dev/null
+++ b/tests/util/mkdir.c
@@ -0,0 +1,291 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+#include "posix.h"
+
+static void cleanup_basic_dirs(void *ref)
+{
+ GIT_UNUSED(ref);
+ git_futils_rmdir_r("d0", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
+ git_futils_rmdir_r("d1", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
+ git_futils_rmdir_r("d2", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
+ git_futils_rmdir_r("d3", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
+ git_futils_rmdir_r("d4", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
+}
+
+void test_mkdir__absolute(void)
+{
+ git_str path = GIT_STR_INIT;
+
+ cl_set_cleanup(cleanup_basic_dirs, NULL);
+
+ git_str_joinpath(&path, clar_sandbox_path(), "d0");
+
+ /* make a directory */
+ cl_assert(!git_fs_path_isdir(path.ptr));
+ cl_git_pass(git_futils_mkdir(path.ptr, 0755, 0));
+ cl_assert(git_fs_path_isdir(path.ptr));
+
+ git_str_joinpath(&path, path.ptr, "subdir");
+ cl_assert(!git_fs_path_isdir(path.ptr));
+ cl_git_pass(git_futils_mkdir(path.ptr, 0755, 0));
+ cl_assert(git_fs_path_isdir(path.ptr));
+
+ /* ensure mkdir_r works for a single subdir */
+ git_str_joinpath(&path, path.ptr, "another");
+ cl_assert(!git_fs_path_isdir(path.ptr));
+ cl_git_pass(git_futils_mkdir_r(path.ptr, 0755));
+ cl_assert(git_fs_path_isdir(path.ptr));
+
+ /* ensure mkdir_r works */
+ git_str_joinpath(&path, clar_sandbox_path(), "d1/foo/bar/asdf");
+ cl_assert(!git_fs_path_isdir(path.ptr));
+ cl_git_pass(git_futils_mkdir_r(path.ptr, 0755));
+ cl_assert(git_fs_path_isdir(path.ptr));
+
+ /* ensure we don't imply recursive */
+ git_str_joinpath(&path, clar_sandbox_path(), "d2/foo/bar/asdf");
+ cl_assert(!git_fs_path_isdir(path.ptr));
+ cl_git_fail(git_futils_mkdir(path.ptr, 0755, 0));
+ cl_assert(!git_fs_path_isdir(path.ptr));
+
+ git_str_dispose(&path);
+}
+
+void test_mkdir__basic(void)
+{
+ cl_set_cleanup(cleanup_basic_dirs, NULL);
+
+ /* make a directory */
+ cl_assert(!git_fs_path_isdir("d0"));
+ cl_git_pass(git_futils_mkdir("d0", 0755, 0));
+ cl_assert(git_fs_path_isdir("d0"));
+
+ /* make a path */
+ cl_assert(!git_fs_path_isdir("d1"));
+ cl_git_pass(git_futils_mkdir("d1/d1.1/d1.2", 0755, GIT_MKDIR_PATH));
+ cl_assert(git_fs_path_isdir("d1"));
+ cl_assert(git_fs_path_isdir("d1/d1.1"));
+ cl_assert(git_fs_path_isdir("d1/d1.1/d1.2"));
+
+ /* make a dir exclusively */
+ cl_assert(!git_fs_path_isdir("d2"));
+ cl_git_pass(git_futils_mkdir("d2", 0755, GIT_MKDIR_EXCL));
+ cl_assert(git_fs_path_isdir("d2"));
+
+ /* make exclusive failure */
+ cl_git_fail(git_futils_mkdir("d2", 0755, GIT_MKDIR_EXCL));
+
+ /* make a path exclusively */
+ cl_assert(!git_fs_path_isdir("d3"));
+ cl_git_pass(git_futils_mkdir("d3/d3.1/d3.2", 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
+ cl_assert(git_fs_path_isdir("d3"));
+ cl_assert(git_fs_path_isdir("d3/d3.1/d3.2"));
+
+ /* make exclusive path failure */
+ cl_git_fail(git_futils_mkdir("d3/d3.1/d3.2", 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL));
+ /* ??? Should EXCL only apply to the last item in the path? */
+
+ /* path with trailing slash? */
+ cl_assert(!git_fs_path_isdir("d4"));
+ cl_git_pass(git_futils_mkdir("d4/d4.1/", 0755, GIT_MKDIR_PATH));
+ cl_assert(git_fs_path_isdir("d4/d4.1"));
+}
+
+static void cleanup_basedir(void *ref)
+{
+ GIT_UNUSED(ref);
+ git_futils_rmdir_r("base", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
+}
+
+void test_mkdir__with_base(void)
+{
+#define BASEDIR "base/dir/here"
+
+ cl_set_cleanup(cleanup_basedir, NULL);
+
+ cl_git_pass(git_futils_mkdir(BASEDIR, 0755, GIT_MKDIR_PATH));
+
+ cl_git_pass(git_futils_mkdir_relative("a", BASEDIR, 0755, 0, NULL));
+ cl_assert(git_fs_path_isdir(BASEDIR "/a"));
+
+ cl_git_pass(git_futils_mkdir_relative("b/b1/b2", BASEDIR, 0755, GIT_MKDIR_PATH, NULL));
+ cl_assert(git_fs_path_isdir(BASEDIR "/b/b1/b2"));
+
+ /* exclusive with existing base */
+ cl_git_pass(git_futils_mkdir_relative("c/c1/c2", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL, NULL));
+
+ /* fail: exclusive with duplicated suffix */
+ cl_git_fail(git_futils_mkdir_relative("c/c1/c3", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL, NULL));
+
+ /* fail: exclusive with any duplicated component */
+ cl_git_fail(git_futils_mkdir_relative("c/cz/cz", BASEDIR, 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL, NULL));
+
+ /* success: exclusive without path */
+ cl_git_pass(git_futils_mkdir_relative("c/c1/c3", BASEDIR, 0755, GIT_MKDIR_EXCL, NULL));
+
+ /* path with shorter base and existing dirs */
+ cl_git_pass(git_futils_mkdir_relative("dir/here/d/", "base", 0755, GIT_MKDIR_PATH, NULL));
+ cl_assert(git_fs_path_isdir("base/dir/here/d"));
+
+ /* fail: path with shorter base and existing dirs */
+ cl_git_fail(git_futils_mkdir_relative("dir/here/e/", "base", 0755, GIT_MKDIR_PATH | GIT_MKDIR_EXCL, NULL));
+
+ /* fail: base with missing components */
+ cl_git_fail(git_futils_mkdir_relative("f/", "base/missing", 0755, GIT_MKDIR_PATH, NULL));
+
+ /* success: shift missing component to path */
+ cl_git_pass(git_futils_mkdir_relative("missing/f/", "base/", 0755, GIT_MKDIR_PATH, NULL));
+}
+
+static void cleanup_chmod_root(void *ref)
+{
+ mode_t *mode = ref;
+
+ if (mode != NULL) {
+ (void)p_umask(*mode);
+ git__free(mode);
+ }
+
+ git_futils_rmdir_r("r", NULL, GIT_RMDIR_EMPTY_HIERARCHY);
+}
+
+#define check_mode(X,A) check_mode_at_line((X), (A), __FILE__, __func__, __LINE__)
+
+static void check_mode_at_line(
+ mode_t expected, mode_t actual,
+ const char *file, const char *func, int line)
+{
+ /* FAT filesystems don't support exec bit, nor group/world bits */
+ if (!cl_is_chmod_supported()) {
+ expected &= 0600;
+ actual &= 0600;
+ }
+
+ clar__assert_equal(
+ file, func, line, "expected_mode != actual_mode", 1,
+ "%07o", (int)expected, (int)(actual & 0777));
+}
+
+void test_mkdir__chmods(void)
+{
+ struct stat st;
+ mode_t *old = git__malloc(sizeof(mode_t));
+ *old = p_umask(022);
+
+ cl_set_cleanup(cleanup_chmod_root, old);
+
+ cl_git_pass(git_futils_mkdir("r", 0777, 0));
+
+ cl_git_pass(git_futils_mkdir_relative("mode/is/important", "r", 0777, GIT_MKDIR_PATH, NULL));
+
+ cl_git_pass(git_fs_path_lstat("r/mode", &st));
+ check_mode(0755, st.st_mode);
+ cl_git_pass(git_fs_path_lstat("r/mode/is", &st));
+ check_mode(0755, st.st_mode);
+ cl_git_pass(git_fs_path_lstat("r/mode/is/important", &st));
+ check_mode(0755, st.st_mode);
+
+ cl_git_pass(git_futils_mkdir_relative("mode2/is2/important2", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD, NULL));
+
+ cl_git_pass(git_fs_path_lstat("r/mode2", &st));
+ check_mode(0755, st.st_mode);
+ cl_git_pass(git_fs_path_lstat("r/mode2/is2", &st));
+ check_mode(0755, st.st_mode);
+ cl_git_pass(git_fs_path_lstat("r/mode2/is2/important2", &st));
+ check_mode(0777, st.st_mode);
+
+ cl_git_pass(git_futils_mkdir_relative("mode3/is3/important3", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD_PATH, NULL));
+
+ cl_git_pass(git_fs_path_lstat("r/mode3", &st));
+ check_mode(0777, st.st_mode);
+ cl_git_pass(git_fs_path_lstat("r/mode3/is3", &st));
+ check_mode(0777, st.st_mode);
+ cl_git_pass(git_fs_path_lstat("r/mode3/is3/important3", &st));
+ check_mode(0777, st.st_mode);
+
+ /* test that we chmod existing dir */
+
+ cl_git_pass(git_futils_mkdir_relative("mode/is/important", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD, NULL));
+
+ cl_git_pass(git_fs_path_lstat("r/mode", &st));
+ check_mode(0755, st.st_mode);
+ cl_git_pass(git_fs_path_lstat("r/mode/is", &st));
+ check_mode(0755, st.st_mode);
+ cl_git_pass(git_fs_path_lstat("r/mode/is/important", &st));
+ check_mode(0777, st.st_mode);
+
+ /* test that we chmod even existing dirs if CHMOD_PATH is set */
+
+ cl_git_pass(git_futils_mkdir_relative("mode2/is2/important2.1", "r", 0777, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD_PATH, NULL));
+
+ cl_git_pass(git_fs_path_lstat("r/mode2", &st));
+ check_mode(0777, st.st_mode);
+ cl_git_pass(git_fs_path_lstat("r/mode2/is2", &st));
+ check_mode(0777, st.st_mode);
+ cl_git_pass(git_fs_path_lstat("r/mode2/is2/important2.1", &st));
+ check_mode(0777, st.st_mode);
+}
+
+void test_mkdir__keeps_parent_symlinks(void)
+{
+#ifndef GIT_WIN32
+ git_str path = GIT_STR_INIT;
+
+ cl_set_cleanup(cleanup_basic_dirs, NULL);
+
+ /* make a directory */
+ cl_assert(!git_fs_path_isdir("d0"));
+ cl_git_pass(git_futils_mkdir("d0", 0755, 0));
+ cl_assert(git_fs_path_isdir("d0"));
+
+ cl_must_pass(symlink("d0", "d1"));
+ cl_assert(git_fs_path_islink("d1"));
+
+ cl_git_pass(git_futils_mkdir("d1/foo/bar", 0755, GIT_MKDIR_PATH|GIT_MKDIR_REMOVE_SYMLINKS));
+ cl_assert(git_fs_path_islink("d1"));
+ cl_assert(git_fs_path_isdir("d1/foo/bar"));
+ cl_assert(git_fs_path_isdir("d0/foo/bar"));
+
+ cl_must_pass(symlink("d0", "d2"));
+ cl_assert(git_fs_path_islink("d2"));
+
+ git_str_joinpath(&path, clar_sandbox_path(), "d2/other/dir");
+
+ cl_git_pass(git_futils_mkdir(path.ptr, 0755, GIT_MKDIR_PATH|GIT_MKDIR_REMOVE_SYMLINKS));
+ cl_assert(git_fs_path_islink("d2"));
+ cl_assert(git_fs_path_isdir("d2/other/dir"));
+ cl_assert(git_fs_path_isdir("d0/other/dir"));
+
+ git_str_dispose(&path);
+#endif
+}
+
+void test_mkdir__mkdir_path_inside_unwriteable_parent(void)
+{
+ struct stat st;
+ mode_t *old;
+
+ /* FAT filesystems don't support exec bit, nor group/world bits */
+ if (!cl_is_chmod_supported())
+ return;
+
+ cl_assert((old = git__malloc(sizeof(mode_t))) != NULL);
+ *old = p_umask(022);
+ cl_set_cleanup(cleanup_chmod_root, old);
+
+ cl_git_pass(git_futils_mkdir("r", 0777, 0));
+ cl_git_pass(git_futils_mkdir_relative("mode/is/important", "r", 0777, GIT_MKDIR_PATH, NULL));
+ cl_git_pass(git_fs_path_lstat("r/mode", &st));
+ check_mode(0755, st.st_mode);
+
+ cl_must_pass(p_chmod("r/mode", 0111));
+ cl_git_pass(git_fs_path_lstat("r/mode", &st));
+ check_mode(0111, st.st_mode);
+
+ cl_git_pass(
+ git_futils_mkdir_relative("mode/is/okay/inside", "r", 0777, GIT_MKDIR_PATH, NULL));
+ cl_git_pass(git_fs_path_lstat("r/mode/is/okay/inside", &st));
+ check_mode(0755, st.st_mode);
+
+ cl_must_pass(p_chmod("r/mode", 0777));
+}
diff --git a/tests/util/path.c b/tests/util/path.c
new file mode 100644
index 0000000..02ec42f
--- /dev/null
+++ b/tests/util/path.c
@@ -0,0 +1,768 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+#include "fs_path.h"
+
+#ifndef GIT_WIN32
+# include <unistd.h>
+#endif
+
+static char *path_save;
+
+void test_path__initialize(void)
+{
+ path_save = cl_getenv("PATH");
+}
+
+void test_path__cleanup(void)
+{
+ cl_setenv("PATH", path_save);
+ git__free(path_save);
+ path_save = NULL;
+}
+
+static void
+check_dirname(const char *A, const char *B)
+{
+ git_str dir = GIT_STR_INIT;
+ char *dir2;
+
+ cl_assert(git_fs_path_dirname_r(&dir, A) >= 0);
+ cl_assert_equal_s(B, dir.ptr);
+ git_str_dispose(&dir);
+
+ cl_assert((dir2 = git_fs_path_dirname(A)) != NULL);
+ cl_assert_equal_s(B, dir2);
+ git__free(dir2);
+}
+
+static void
+check_basename(const char *A, const char *B)
+{
+ git_str base = GIT_STR_INIT;
+ char *base2;
+
+ cl_assert(git_fs_path_basename_r(&base, A) >= 0);
+ cl_assert_equal_s(B, base.ptr);
+ git_str_dispose(&base);
+
+ cl_assert((base2 = git_fs_path_basename(A)) != NULL);
+ cl_assert_equal_s(B, base2);
+ git__free(base2);
+}
+
+static void
+check_joinpath(const char *path_a, const char *path_b, const char *expected_path)
+{
+ git_str joined_path = GIT_STR_INIT;
+
+ cl_git_pass(git_str_joinpath(&joined_path, path_a, path_b));
+ cl_assert_equal_s(expected_path, joined_path.ptr);
+
+ git_str_dispose(&joined_path);
+}
+
+static void
+check_joinpath_n(
+ const char *path_a,
+ const char *path_b,
+ const char *path_c,
+ const char *path_d,
+ const char *expected_path)
+{
+ git_str joined_path = GIT_STR_INIT;
+
+ cl_git_pass(git_str_join_n(&joined_path, '/', 4,
+ path_a, path_b, path_c, path_d));
+ cl_assert_equal_s(expected_path, joined_path.ptr);
+
+ git_str_dispose(&joined_path);
+}
+
+static void check_setenv(const char* name, const char* value)
+{
+ char* check;
+
+ cl_git_pass(cl_setenv(name, value));
+ check = cl_getenv(name);
+
+ if (value)
+ cl_assert_equal_s(value, check);
+ else
+ cl_assert(check == NULL);
+
+ git__free(check);
+}
+
+/* get the dirname of a path */
+void test_path__00_dirname(void)
+{
+ check_dirname(NULL, ".");
+ check_dirname("", ".");
+ check_dirname("a", ".");
+ check_dirname("/", "/");
+ check_dirname("/usr", "/");
+ check_dirname("/usr/", "/");
+ check_dirname("/usr/lib", "/usr");
+ check_dirname("/usr/lib/", "/usr");
+ check_dirname("/usr/lib//", "/usr");
+ check_dirname("usr/lib", "usr");
+ check_dirname("usr/lib/", "usr");
+ check_dirname("usr/lib//", "usr");
+ check_dirname(".git/", ".");
+
+ check_dirname(REP16("/abc"), REP15("/abc"));
+
+#ifdef GIT_WIN32
+ check_dirname("C:/", "C:/");
+ check_dirname("C:", "C:/");
+ check_dirname("C:/path/", "C:/");
+ check_dirname("C:/path", "C:/");
+ check_dirname("//computername/", "//computername/");
+ check_dirname("//computername", "//computername/");
+ check_dirname("//computername/path/", "//computername/");
+ check_dirname("//computername/path", "//computername/");
+ check_dirname("//computername/sub/path/", "//computername/sub");
+ check_dirname("//computername/sub/path", "//computername/sub");
+#endif
+}
+
+/* get the base name of a path */
+void test_path__01_basename(void)
+{
+ check_basename(NULL, ".");
+ check_basename("", ".");
+ check_basename("a", "a");
+ check_basename("/", "/");
+ check_basename("/usr", "usr");
+ check_basename("/usr/", "usr");
+ check_basename("/usr/lib", "lib");
+ check_basename("/usr/lib//", "lib");
+ check_basename("usr/lib", "lib");
+
+ check_basename(REP16("/abc"), "abc");
+ check_basename(REP1024("/abc"), "abc");
+}
+
+/* properly join path components */
+void test_path__05_joins(void)
+{
+ check_joinpath("", "", "");
+ check_joinpath("", "a", "a");
+ check_joinpath("", "/a", "/a");
+ check_joinpath("a", "", "a/");
+ check_joinpath("a", "/", "a/");
+ check_joinpath("a", "b", "a/b");
+ check_joinpath("/", "a", "/a");
+ check_joinpath("/", "", "/");
+ check_joinpath("/a", "/b", "/a/b");
+ check_joinpath("/a", "/b/", "/a/b/");
+ check_joinpath("/a/", "b/", "/a/b/");
+ check_joinpath("/a/", "/b/", "/a/b/");
+
+ check_joinpath("/abcd", "/defg", "/abcd/defg");
+ check_joinpath("/abcd", "/defg/", "/abcd/defg/");
+ check_joinpath("/abcd/", "defg/", "/abcd/defg/");
+ check_joinpath("/abcd/", "/defg/", "/abcd/defg/");
+
+ check_joinpath("/abcdefgh", "/12345678", "/abcdefgh/12345678");
+ check_joinpath("/abcdefgh", "/12345678/", "/abcdefgh/12345678/");
+ check_joinpath("/abcdefgh/", "12345678/", "/abcdefgh/12345678/");
+
+ check_joinpath(REP1024("aaaa"), "", REP1024("aaaa") "/");
+ check_joinpath(REP1024("aaaa/"), "", REP1024("aaaa/"));
+ check_joinpath(REP1024("/aaaa"), "", REP1024("/aaaa") "/");
+
+ check_joinpath(REP1024("aaaa"), REP1024("bbbb"),
+ REP1024("aaaa") "/" REP1024("bbbb"));
+ check_joinpath(REP1024("/aaaa"), REP1024("/bbbb"),
+ REP1024("/aaaa") REP1024("/bbbb"));
+}
+
+/* properly join path components for more than one path */
+void test_path__06_long_joins(void)
+{
+ check_joinpath_n("", "", "", "", "");
+ check_joinpath_n("", "a", "", "", "a/");
+ check_joinpath_n("a", "", "", "", "a/");
+ check_joinpath_n("", "", "", "a", "a");
+ check_joinpath_n("a", "b", "", "/c/d/", "a/b/c/d/");
+ check_joinpath_n("a", "b", "", "/c/d", "a/b/c/d");
+ check_joinpath_n("abcd", "efgh", "ijkl", "mnop", "abcd/efgh/ijkl/mnop");
+ check_joinpath_n("abcd/", "efgh/", "ijkl/", "mnop/", "abcd/efgh/ijkl/mnop/");
+ check_joinpath_n("/abcd/", "/efgh/", "/ijkl/", "/mnop/", "/abcd/efgh/ijkl/mnop/");
+
+ check_joinpath_n(REP1024("a"), REP1024("b"), REP1024("c"), REP1024("d"),
+ REP1024("a") "/" REP1024("b") "/"
+ REP1024("c") "/" REP1024("d"));
+ check_joinpath_n(REP1024("/a"), REP1024("/b"), REP1024("/c"), REP1024("/d"),
+ REP1024("/a") REP1024("/b")
+ REP1024("/c") REP1024("/d"));
+}
+
+
+static void
+check_path_to_dir(
+ const char* path,
+ const char* expected)
+{
+ git_str tgt = GIT_STR_INIT;
+
+ git_str_sets(&tgt, path);
+ cl_git_pass(git_fs_path_to_dir(&tgt));
+ cl_assert_equal_s(expected, tgt.ptr);
+
+ git_str_dispose(&tgt);
+}
+
+static void
+check_string_to_dir(
+ const char* path,
+ size_t maxlen,
+ const char* expected)
+{
+ size_t len = strlen(path);
+ char *buf = git__malloc(len + 2);
+ cl_assert(buf);
+
+ strncpy(buf, path, len + 2);
+
+ git_fs_path_string_to_dir(buf, maxlen);
+
+ cl_assert_equal_s(expected, buf);
+
+ git__free(buf);
+}
+
+/* convert paths to dirs */
+void test_path__07_path_to_dir(void)
+{
+ check_path_to_dir("", "");
+ check_path_to_dir(".", "./");
+ check_path_to_dir("./", "./");
+ check_path_to_dir("a/", "a/");
+ check_path_to_dir("ab", "ab/");
+ /* make sure we try just under and just over an expansion that will
+ * require a realloc
+ */
+ check_path_to_dir("abcdef", "abcdef/");
+ check_path_to_dir("abcdefg", "abcdefg/");
+ check_path_to_dir("abcdefgh", "abcdefgh/");
+ check_path_to_dir("abcdefghi", "abcdefghi/");
+ check_path_to_dir(REP1024("abcd") "/", REP1024("abcd") "/");
+ check_path_to_dir(REP1024("abcd"), REP1024("abcd") "/");
+
+ check_string_to_dir("", 1, "");
+ check_string_to_dir(".", 1, ".");
+ check_string_to_dir(".", 2, "./");
+ check_string_to_dir(".", 3, "./");
+ check_string_to_dir("abcd", 3, "abcd");
+ check_string_to_dir("abcd", 4, "abcd");
+ check_string_to_dir("abcd", 5, "abcd/");
+ check_string_to_dir("abcd", 6, "abcd/");
+}
+
+/* join path to itself */
+void test_path__08_self_join(void)
+{
+ git_str path = GIT_STR_INIT;
+ size_t asize = 0;
+
+ asize = path.asize;
+ cl_git_pass(git_str_sets(&path, "/foo"));
+ cl_assert_equal_s(path.ptr, "/foo");
+ cl_assert(asize < path.asize);
+
+ asize = path.asize;
+ cl_git_pass(git_str_joinpath(&path, path.ptr, "this is a new string"));
+ cl_assert_equal_s(path.ptr, "/foo/this is a new string");
+ cl_assert(asize < path.asize);
+
+ asize = path.asize;
+ cl_git_pass(git_str_joinpath(&path, path.ptr, "/grow the buffer, grow the buffer, grow the buffer"));
+ cl_assert_equal_s(path.ptr, "/foo/this is a new string/grow the buffer, grow the buffer, grow the buffer");
+ cl_assert(asize < path.asize);
+
+ git_str_dispose(&path);
+ cl_git_pass(git_str_sets(&path, "/foo/bar"));
+
+ cl_git_pass(git_str_joinpath(&path, path.ptr + 4, "baz"));
+ cl_assert_equal_s(path.ptr, "/bar/baz");
+
+ asize = path.asize;
+ cl_git_pass(git_str_joinpath(&path, path.ptr + 4, "somethinglongenoughtorealloc"));
+ cl_assert_equal_s(path.ptr, "/baz/somethinglongenoughtorealloc");
+ cl_assert(asize < path.asize);
+
+ git_str_dispose(&path);
+}
+
+static void check_percent_decoding(const char *expected_result, const char *input)
+{
+ git_str buf = GIT_STR_INIT;
+
+ cl_git_pass(git__percent_decode(&buf, input));
+ cl_assert_equal_s(expected_result, git_str_cstr(&buf));
+
+ git_str_dispose(&buf);
+}
+
+void test_path__09_percent_decode(void)
+{
+ check_percent_decoding("abcd", "abcd");
+ check_percent_decoding("a2%", "a2%");
+ check_percent_decoding("a2%3", "a2%3");
+ check_percent_decoding("a2%%3", "a2%%3");
+ check_percent_decoding("a2%3z", "a2%3z");
+ check_percent_decoding("a,", "a%2c");
+ check_percent_decoding("a21", "a2%31");
+ check_percent_decoding("a2%1", "a2%%31");
+ check_percent_decoding("a bc ", "a%20bc%20");
+ check_percent_decoding("Vicent Mart" "\355", "Vicent%20Mart%ED");
+}
+
+static void check_fromurl(const char *expected_result, const char *input, int should_fail)
+{
+ git_str buf = GIT_STR_INIT;
+
+ assert(should_fail || expected_result);
+
+ if (!should_fail) {
+ cl_git_pass(git_fs_path_fromurl(&buf, input));
+ cl_assert_equal_s(expected_result, git_str_cstr(&buf));
+ } else
+ cl_git_fail(git_fs_path_fromurl(&buf, input));
+
+ git_str_dispose(&buf);
+}
+
+#ifdef GIT_WIN32
+#define ABS_PATH_MARKER ""
+#else
+#define ABS_PATH_MARKER "/"
+#endif
+
+void test_path__10_fromurl(void)
+{
+ /* Failing cases */
+ check_fromurl(NULL, "a", 1);
+ check_fromurl(NULL, "http:///c:/Temp%20folder/note.txt", 1);
+ check_fromurl(NULL, "file://c:/Temp%20folder/note.txt", 1);
+ check_fromurl(NULL, "file:////c:/Temp%20folder/note.txt", 1);
+ check_fromurl(NULL, "file:///", 1);
+ check_fromurl(NULL, "file:////", 1);
+ check_fromurl(NULL, "file://servername/c:/Temp%20folder/note.txt", 1);
+
+ /* Passing cases */
+ check_fromurl(ABS_PATH_MARKER "c:/Temp folder/note.txt", "file:///c:/Temp%20folder/note.txt", 0);
+ check_fromurl(ABS_PATH_MARKER "c:/Temp folder/note.txt", "file://localhost/c:/Temp%20folder/note.txt", 0);
+ check_fromurl(ABS_PATH_MARKER "c:/Temp+folder/note.txt", "file:///c:/Temp+folder/note.txt", 0);
+ check_fromurl(ABS_PATH_MARKER "a", "file:///a", 0);
+}
+
+typedef struct {
+ int expect_idx;
+ int cancel_after;
+ char **expect;
+} check_walkup_info;
+
+#define CANCEL_VALUE 1234
+
+static int check_one_walkup_step(void *ref, const char *path)
+{
+ check_walkup_info *info = (check_walkup_info *)ref;
+
+ if (!info->cancel_after) {
+ cl_assert_equal_s(info->expect[info->expect_idx], "[CANCEL]");
+ return CANCEL_VALUE;
+ }
+ info->cancel_after--;
+
+ cl_assert(info->expect[info->expect_idx] != NULL);
+ cl_assert_equal_s(info->expect[info->expect_idx], path);
+ info->expect_idx++;
+
+ return 0;
+}
+
+void test_path__11_walkup(void)
+{
+ git_str p = GIT_STR_INIT;
+
+ char *expect[] = {
+ /* 1 */ "/a/b/c/d/e/", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
+ /* 2 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
+ /* 3 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
+ /* 4 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", "/a/", "/", NULL,
+ /* 5 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", NULL,
+ /* 6 */ "/a/b/c/d/e", "/a/b/c/d/", "/a/b/c/", "/a/b/", NULL,
+ /* 7 */ "this_is_a_path", "", NULL,
+ /* 8 */ "this_is_a_path/", "", NULL,
+ /* 9 */ "///a///b///c///d///e///", "///a///b///c///d///", "///a///b///c///", "///a///b///", "///a///", "///", NULL,
+ /* 10 */ "a/b/c/", "a/b/", "a/", "", NULL,
+ /* 11 */ "a/b/c", "a/b/", "a/", "", NULL,
+ /* 12 */ "a/b/c/", "a/b/", "a/", NULL,
+ /* 13 */ "", NULL,
+ /* 14 */ "/", NULL,
+ /* 15 */ NULL
+ };
+
+ char *root[] = {
+ /* 1 */ NULL,
+ /* 2 */ NULL,
+ /* 3 */ "/",
+ /* 4 */ "",
+ /* 5 */ "/a/b",
+ /* 6 */ "/a/b/",
+ /* 7 */ NULL,
+ /* 8 */ NULL,
+ /* 9 */ NULL,
+ /* 10 */ NULL,
+ /* 11 */ NULL,
+ /* 12 */ "a/",
+ /* 13 */ NULL,
+ /* 14 */ NULL,
+ };
+
+ int i, j;
+ check_walkup_info info;
+
+ info.expect = expect;
+ info.cancel_after = -1;
+
+ for (i = 0, j = 0; expect[i] != NULL; i++, j++) {
+
+ git_str_sets(&p, expect[i]);
+
+ info.expect_idx = i;
+ cl_git_pass(
+ git_fs_path_walk_up(&p, root[j], check_one_walkup_step, &info)
+ );
+
+ cl_assert_equal_s(p.ptr, expect[i]);
+ cl_assert(expect[info.expect_idx] == NULL);
+ i = info.expect_idx;
+ }
+
+ git_str_dispose(&p);
+}
+
+void test_path__11a_walkup_cancel(void)
+{
+ git_str p = GIT_STR_INIT;
+ int cancel[] = { 3, 2, 1, 0 };
+ char *expect[] = {
+ "/a/b/c/d/e/", "/a/b/c/d/", "/a/b/c/", "[CANCEL]", NULL,
+ "/a/b/c/d/e", "/a/b/c/d/", "[CANCEL]", NULL,
+ "/a/b/c/d/e", "[CANCEL]", NULL,
+ "[CANCEL]", NULL,
+ NULL
+ };
+ char *root[] = { NULL, NULL, "/", "", NULL };
+ int i, j;
+ check_walkup_info info;
+
+ info.expect = expect;
+
+ for (i = 0, j = 0; expect[i] != NULL; i++, j++) {
+
+ git_str_sets(&p, expect[i]);
+
+ info.cancel_after = cancel[j];
+ info.expect_idx = i;
+
+ cl_assert_equal_i(
+ CANCEL_VALUE,
+ git_fs_path_walk_up(&p, root[j], check_one_walkup_step, &info)
+ );
+
+ /* skip to next run of expectations */
+ while (expect[i] != NULL) i++;
+ }
+
+ git_str_dispose(&p);
+}
+
+void test_path__12_offset_to_path_root(void)
+{
+ cl_assert(git_fs_path_root("non/rooted/path") == -1);
+ cl_assert(git_fs_path_root("/rooted/path") == 0);
+
+#ifdef GIT_WIN32
+ /* Windows specific tests */
+ cl_assert(git_fs_path_root("C:non/rooted/path") == -1);
+ cl_assert(git_fs_path_root("C:/rooted/path") == 2);
+ cl_assert(git_fs_path_root("//computername/sharefolder/resource") == 14);
+ cl_assert(git_fs_path_root("//computername/sharefolder") == 14);
+ cl_assert(git_fs_path_root("//computername") == -1);
+#endif
+}
+
+#define NON_EXISTING_FILEPATH "i_hope_i_do_not_exist"
+
+void test_path__13_cannot_prettify_a_non_existing_file(void)
+{
+ git_str p = GIT_STR_INIT;
+
+ cl_assert_equal_b(git_fs_path_exists(NON_EXISTING_FILEPATH), false);
+ cl_assert_equal_i(GIT_ENOTFOUND, git_fs_path_prettify(&p, NON_EXISTING_FILEPATH, NULL));
+ cl_assert_equal_i(GIT_ENOTFOUND, git_fs_path_prettify(&p, NON_EXISTING_FILEPATH "/so-do-i", NULL));
+
+ git_str_dispose(&p);
+}
+
+void test_path__14_apply_relative(void)
+{
+ git_str p = GIT_STR_INIT;
+
+ cl_git_pass(git_str_sets(&p, "/this/is/a/base"));
+
+ cl_git_pass(git_fs_path_apply_relative(&p, "../test"));
+ cl_assert_equal_s("/this/is/a/test", p.ptr);
+
+ cl_git_pass(git_fs_path_apply_relative(&p, "../../the/./end"));
+ cl_assert_equal_s("/this/is/the/end", p.ptr);
+
+ cl_git_pass(git_fs_path_apply_relative(&p, "./of/this/../the/string"));
+ cl_assert_equal_s("/this/is/the/end/of/the/string", p.ptr);
+
+ cl_git_pass(git_fs_path_apply_relative(&p, "../../../../../.."));
+ cl_assert_equal_s("/this/", p.ptr);
+
+ cl_git_pass(git_fs_path_apply_relative(&p, "../"));
+ cl_assert_equal_s("/", p.ptr);
+
+ cl_git_fail(git_fs_path_apply_relative(&p, "../../.."));
+
+
+ cl_git_pass(git_str_sets(&p, "d:/another/test"));
+
+ cl_git_pass(git_fs_path_apply_relative(&p, "../.."));
+ cl_assert_equal_s("d:/", p.ptr);
+
+ cl_git_pass(git_fs_path_apply_relative(&p, "from/here/to/../and/./back/."));
+ cl_assert_equal_s("d:/from/here/and/back/", p.ptr);
+
+
+ cl_git_pass(git_str_sets(&p, "https://my.url.com/test.git"));
+
+ cl_git_pass(git_fs_path_apply_relative(&p, "../another.git"));
+ cl_assert_equal_s("https://my.url.com/another.git", p.ptr);
+
+ cl_git_pass(git_fs_path_apply_relative(&p, "../full/path/url.patch"));
+ cl_assert_equal_s("https://my.url.com/full/path/url.patch", p.ptr);
+
+ cl_git_pass(git_fs_path_apply_relative(&p, ".."));
+ cl_assert_equal_s("https://my.url.com/full/path/", p.ptr);
+
+ cl_git_pass(git_fs_path_apply_relative(&p, "../../../"));
+ cl_assert_equal_s("https://", p.ptr);
+
+
+ cl_git_pass(git_str_sets(&p, "../../this/is/relative"));
+
+ cl_git_pass(git_fs_path_apply_relative(&p, "../../preserves/the/prefix"));
+ cl_assert_equal_s("../../this/preserves/the/prefix", p.ptr);
+
+ cl_git_pass(git_fs_path_apply_relative(&p, "../../../../that"));
+ cl_assert_equal_s("../../that", p.ptr);
+
+ cl_git_pass(git_fs_path_apply_relative(&p, "../there"));
+ cl_assert_equal_s("../../there", p.ptr);
+ git_str_dispose(&p);
+}
+
+static void assert_resolve_relative(
+ git_str *buf, const char *expected, const char *path)
+{
+ cl_git_pass(git_str_sets(buf, path));
+ cl_git_pass(git_fs_path_resolve_relative(buf, 0));
+ cl_assert_equal_s(expected, buf->ptr);
+}
+
+void test_path__15_resolve_relative(void)
+{
+ git_str buf = GIT_STR_INIT;
+
+ assert_resolve_relative(&buf, "", "");
+ assert_resolve_relative(&buf, "", ".");
+ assert_resolve_relative(&buf, "", "./");
+ assert_resolve_relative(&buf, "..", "..");
+ assert_resolve_relative(&buf, "../", "../");
+ assert_resolve_relative(&buf, "..", "./..");
+ assert_resolve_relative(&buf, "../", "./../");
+ assert_resolve_relative(&buf, "../", "../.");
+ assert_resolve_relative(&buf, "../", ".././");
+ assert_resolve_relative(&buf, "../..", "../..");
+ assert_resolve_relative(&buf, "../../", "../../");
+
+ assert_resolve_relative(&buf, "/", "/");
+ assert_resolve_relative(&buf, "/", "/.");
+
+ assert_resolve_relative(&buf, "", "a/..");
+ assert_resolve_relative(&buf, "", "a/../");
+ assert_resolve_relative(&buf, "", "a/../.");
+
+ assert_resolve_relative(&buf, "/a", "/a");
+ assert_resolve_relative(&buf, "/a/", "/a/.");
+ assert_resolve_relative(&buf, "/", "/a/../");
+ assert_resolve_relative(&buf, "/", "/a/../.");
+ assert_resolve_relative(&buf, "/", "/a/.././");
+
+ assert_resolve_relative(&buf, "a", "a");
+ assert_resolve_relative(&buf, "a/", "a/");
+ assert_resolve_relative(&buf, "a/", "a/.");
+ assert_resolve_relative(&buf, "a/", "a/./");
+
+ assert_resolve_relative(&buf, "a/b", "a//b");
+ assert_resolve_relative(&buf, "a/b/c", "a/b/c");
+ assert_resolve_relative(&buf, "b/c", "./b/c");
+ assert_resolve_relative(&buf, "a/c", "a/./c");
+ assert_resolve_relative(&buf, "a/b/", "a/b/.");
+
+ assert_resolve_relative(&buf, "/a/b/c", "///a/b/c");
+ assert_resolve_relative(&buf, "/", "////");
+ assert_resolve_relative(&buf, "/a", "///a");
+ assert_resolve_relative(&buf, "/", "///.");
+ assert_resolve_relative(&buf, "/", "///a/..");
+
+ assert_resolve_relative(&buf, "../../path", "../../test//../././path");
+ assert_resolve_relative(&buf, "../d", "a/b/../../../c/../d");
+
+ cl_git_pass(git_str_sets(&buf, "/.."));
+ cl_git_fail(git_fs_path_resolve_relative(&buf, 0));
+
+ cl_git_pass(git_str_sets(&buf, "/./.."));
+ cl_git_fail(git_fs_path_resolve_relative(&buf, 0));
+
+ cl_git_pass(git_str_sets(&buf, "/.//.."));
+ cl_git_fail(git_fs_path_resolve_relative(&buf, 0));
+
+ cl_git_pass(git_str_sets(&buf, "/../."));
+ cl_git_fail(git_fs_path_resolve_relative(&buf, 0));
+
+ cl_git_pass(git_str_sets(&buf, "/../.././../a"));
+ cl_git_fail(git_fs_path_resolve_relative(&buf, 0));
+
+ cl_git_pass(git_str_sets(&buf, "////.."));
+ cl_git_fail(git_fs_path_resolve_relative(&buf, 0));
+
+ /* things that start with Windows network paths */
+#ifdef GIT_WIN32
+ assert_resolve_relative(&buf, "//a/b/c", "//a/b/c");
+ assert_resolve_relative(&buf, "//a/", "//a/b/..");
+ assert_resolve_relative(&buf, "//a/b/c", "//a/Q/../b/x/y/../../c");
+
+ cl_git_pass(git_str_sets(&buf, "//a/b/../.."));
+ cl_git_fail(git_fs_path_resolve_relative(&buf, 0));
+#else
+ assert_resolve_relative(&buf, "/a/b/c", "//a/b/c");
+ assert_resolve_relative(&buf, "/a/", "//a/b/..");
+ assert_resolve_relative(&buf, "/a/b/c", "//a/Q/../b/x/y/../../c");
+ assert_resolve_relative(&buf, "/", "//a/b/../..");
+#endif
+
+ git_str_dispose(&buf);
+}
+
+#define assert_common_dirlen(i, p, q) \
+ cl_assert_equal_i((i), git_fs_path_common_dirlen((p), (q)));
+
+void test_path__16_resolve_relative(void)
+{
+ assert_common_dirlen(0, "", "");
+ assert_common_dirlen(0, "", "bar.txt");
+ assert_common_dirlen(0, "foo.txt", "bar.txt");
+ assert_common_dirlen(0, "foo.txt", "");
+ assert_common_dirlen(0, "foo/bar.txt", "bar/foo.txt");
+ assert_common_dirlen(0, "foo/bar.txt", "../foo.txt");
+
+ assert_common_dirlen(1, "/one.txt", "/two.txt");
+ assert_common_dirlen(4, "foo/one.txt", "foo/two.txt");
+ assert_common_dirlen(5, "/foo/one.txt", "/foo/two.txt");
+
+ assert_common_dirlen(6, "a/b/c/foo.txt", "a/b/c/d/e/bar.txt");
+ assert_common_dirlen(7, "/a/b/c/foo.txt", "/a/b/c/d/e/bar.txt");
+}
+
+static void fix_path(git_str *s)
+{
+#ifndef GIT_WIN32
+ GIT_UNUSED(s);
+#else
+ char* c;
+
+ for (c = s->ptr; *c; c++) {
+ if (*c == '/')
+ *c = '\\';
+ }
+#endif
+}
+
+void test_path__find_exe_in_path(void)
+{
+ char *orig_path;
+ git_str sandbox_path = GIT_STR_INIT;
+ git_str new_path = GIT_STR_INIT, full_path = GIT_STR_INIT,
+ dummy_path = GIT_STR_INIT;
+
+#ifdef GIT_WIN32
+ static const char *bogus_path_1 = "c:\\does\\not\\exist\\";
+ static const char *bogus_path_2 = "e:\\non\\existent";
+#else
+ static const char *bogus_path_1 = "/this/path/does/not/exist/";
+ static const char *bogus_path_2 = "/non/existent";
+#endif
+
+ orig_path = cl_getenv("PATH");
+
+ git_str_puts(&sandbox_path, clar_sandbox_path());
+ git_str_joinpath(&dummy_path, sandbox_path.ptr, "dummmmmmmy_libgit2_file");
+ cl_git_rewritefile(dummy_path.ptr, "this is a dummy file");
+
+ fix_path(&sandbox_path);
+ fix_path(&dummy_path);
+
+ cl_git_pass(git_str_printf(&new_path, "%s%c%s%c%s%c%s",
+ bogus_path_1, GIT_PATH_LIST_SEPARATOR,
+ orig_path, GIT_PATH_LIST_SEPARATOR,
+ sandbox_path.ptr, GIT_PATH_LIST_SEPARATOR,
+ bogus_path_2));
+
+ check_setenv("PATH", new_path.ptr);
+
+ cl_git_fail_with(GIT_ENOTFOUND, git_fs_path_find_executable(&full_path, "this_file_does_not_exist"));
+ cl_git_pass(git_fs_path_find_executable(&full_path, "dummmmmmmy_libgit2_file"));
+
+ cl_assert_equal_s(full_path.ptr, dummy_path.ptr);
+
+ git_str_dispose(&full_path);
+ git_str_dispose(&new_path);
+ git_str_dispose(&dummy_path);
+ git_str_dispose(&sandbox_path);
+ git__free(orig_path);
+}
+
+void test_path__validate_current_user_ownership(void)
+{
+ bool is_cur;
+
+ cl_must_pass(p_mkdir("testdir", 0777));
+ cl_git_pass(git_fs_path_owner_is_current_user(&is_cur, "testdir"));
+ cl_assert_equal_i(is_cur, 1);
+
+ cl_git_rewritefile("testfile", "This is a test file.");
+ cl_git_pass(git_fs_path_owner_is_current_user(&is_cur, "testfile"));
+ cl_assert_equal_i(is_cur, 1);
+
+#ifdef GIT_WIN32
+ cl_git_pass(git_fs_path_owner_is_current_user(&is_cur, "C:\\"));
+ cl_assert_equal_i(is_cur, 0);
+
+ cl_git_fail(git_fs_path_owner_is_current_user(&is_cur, "c:\\path\\does\\not\\exist"));
+#else
+ cl_git_pass(git_fs_path_owner_is_current_user(&is_cur, "/"));
+ cl_assert_equal_i(is_cur, (geteuid() == 0));
+
+ cl_git_fail(git_fs_path_owner_is_current_user(&is_cur, "/path/does/not/exist"));
+#endif
+}
diff --git a/tests/util/path/core.c b/tests/util/path/core.c
new file mode 100644
index 0000000..f30f6c0
--- /dev/null
+++ b/tests/util/path/core.c
@@ -0,0 +1,343 @@
+#include "clar_libgit2.h"
+#include "fs_path.h"
+
+static void test_make_relative(
+ const char *expected_path,
+ const char *path,
+ const char *parent,
+ int expected_status)
+{
+ git_str buf = GIT_STR_INIT;
+ git_str_puts(&buf, path);
+ cl_assert_equal_i(expected_status, git_fs_path_make_relative(&buf, parent));
+ cl_assert_equal_s(expected_path, buf.ptr);
+ git_str_dispose(&buf);
+}
+
+void test_path_core__make_relative(void)
+{
+ test_make_relative("foo.c", "/path/to/foo.c", "/path/to", 0);
+ test_make_relative("bar/foo.c", "/path/to/bar/foo.c", "/path/to", 0);
+ test_make_relative("foo.c", "/path/to/foo.c", "/path/to/", 0);
+
+ test_make_relative("", "/path/to", "/path/to", 0);
+ test_make_relative("", "/path/to", "/path/to/", 0);
+
+ test_make_relative("../", "/path/to", "/path/to/foo", 0);
+
+ test_make_relative("../foo.c", "/path/to/foo.c", "/path/to/bar", 0);
+ test_make_relative("../bar/foo.c", "/path/to/bar/foo.c", "/path/to/baz", 0);
+
+ test_make_relative("../../foo.c", "/path/to/foo.c", "/path/to/foo/bar", 0);
+ test_make_relative("../../foo/bar.c", "/path/to/foo/bar.c", "/path/to/bar/foo", 0);
+
+ test_make_relative("../../foo.c", "/foo.c", "/bar/foo", 0);
+
+ test_make_relative("foo.c", "/path/to/foo.c", "/path/to/", 0);
+ test_make_relative("../foo.c", "/path/to/foo.c", "/path/to/bar/", 0);
+
+ test_make_relative("foo.c", "d:/path/to/foo.c", "d:/path/to", 0);
+
+ test_make_relative("../foo", "/foo", "/bar", 0);
+ test_make_relative("path/to/foo.c", "/path/to/foo.c", "/", 0);
+ test_make_relative("../foo", "path/to/foo", "path/to/bar", 0);
+
+ test_make_relative("/path/to/foo.c", "/path/to/foo.c", "d:/path/to", GIT_ENOTFOUND);
+ test_make_relative("d:/path/to/foo.c", "d:/path/to/foo.c", "/path/to", GIT_ENOTFOUND);
+
+ test_make_relative("/path/to/foo.c", "/path/to/foo.c", "not-a-rooted-path", GIT_ENOTFOUND);
+ test_make_relative("not-a-rooted-path", "not-a-rooted-path", "/path/to", GIT_ENOTFOUND);
+
+ test_make_relative("/path", "/path", "pathtofoo", GIT_ENOTFOUND);
+ test_make_relative("path", "path", "pathtofoo", GIT_ENOTFOUND);
+}
+
+void test_path_core__isvalid_standard(void)
+{
+ cl_assert_equal_b(true, git_fs_path_is_valid("foo/bar", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("foo/bar/file.txt", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("foo/bar/.file", 0));
+}
+
+/* Ensure that `is_valid_str` only reads str->size bytes */
+void test_path_core__isvalid_standard_str(void)
+{
+ git_str str = GIT_STR_INIT_CONST("foo/bar//zap", 0);
+ unsigned int flags = GIT_FS_PATH_REJECT_EMPTY_COMPONENT;
+
+ str.size = 0;
+ cl_assert_equal_b(false, git_fs_path_str_is_valid(&str, flags));
+
+ str.size = 3;
+ cl_assert_equal_b(true, git_fs_path_str_is_valid(&str, flags));
+
+ str.size = 4;
+ cl_assert_equal_b(false, git_fs_path_str_is_valid(&str, flags));
+
+ str.size = 5;
+ cl_assert_equal_b(true, git_fs_path_str_is_valid(&str, flags));
+
+ str.size = 7;
+ cl_assert_equal_b(true, git_fs_path_str_is_valid(&str, flags));
+
+ str.size = 8;
+ cl_assert_equal_b(false, git_fs_path_str_is_valid(&str, flags));
+
+ str.size = strlen(str.ptr);
+ cl_assert_equal_b(false, git_fs_path_str_is_valid(&str, flags));
+}
+
+void test_path_core__isvalid_empty_dir_component(void)
+{
+ unsigned int flags = GIT_FS_PATH_REJECT_EMPTY_COMPONENT;
+
+ /* empty component */
+ cl_assert_equal_b(true, git_fs_path_is_valid("foo//bar", 0));
+
+ /* leading slash */
+ cl_assert_equal_b(true, git_fs_path_is_valid("/", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("/foo", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("/foo/bar", 0));
+
+ /* trailing slash */
+ cl_assert_equal_b(true, git_fs_path_is_valid("foo/", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("foo/bar/", 0));
+
+
+ /* empty component */
+ cl_assert_equal_b(false, git_fs_path_is_valid("foo//bar", flags));
+
+ /* leading slash */
+ cl_assert_equal_b(false, git_fs_path_is_valid("/", flags));
+ cl_assert_equal_b(false, git_fs_path_is_valid("/foo", flags));
+ cl_assert_equal_b(false, git_fs_path_is_valid("/foo/bar", flags));
+
+ /* trailing slash */
+ cl_assert_equal_b(false, git_fs_path_is_valid("foo/", flags));
+ cl_assert_equal_b(false, git_fs_path_is_valid("foo/bar/", flags));
+}
+
+void test_path_core__isvalid_dot_and_dotdot(void)
+{
+ cl_assert_equal_b(true, git_fs_path_is_valid(".", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("./foo", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("foo/.", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("./foo", 0));
+
+ cl_assert_equal_b(true, git_fs_path_is_valid("..", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("../foo", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("foo/..", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("../foo", 0));
+
+ cl_assert_equal_b(false, git_fs_path_is_valid(".", GIT_FS_PATH_REJECT_TRAVERSAL));
+ cl_assert_equal_b(false, git_fs_path_is_valid("./foo", GIT_FS_PATH_REJECT_TRAVERSAL));
+ cl_assert_equal_b(false, git_fs_path_is_valid("foo/.", GIT_FS_PATH_REJECT_TRAVERSAL));
+ cl_assert_equal_b(false, git_fs_path_is_valid("./foo", GIT_FS_PATH_REJECT_TRAVERSAL));
+
+ cl_assert_equal_b(false, git_fs_path_is_valid("..", GIT_FS_PATH_REJECT_TRAVERSAL));
+ cl_assert_equal_b(false, git_fs_path_is_valid("../foo", GIT_FS_PATH_REJECT_TRAVERSAL));
+ cl_assert_equal_b(false, git_fs_path_is_valid("foo/..", GIT_FS_PATH_REJECT_TRAVERSAL));
+ cl_assert_equal_b(false, git_fs_path_is_valid("../foo", GIT_FS_PATH_REJECT_TRAVERSAL));
+}
+
+void test_path_core__isvalid_backslash(void)
+{
+ cl_assert_equal_b(true, git_fs_path_is_valid("foo\\file.txt", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("foo/bar\\file.txt", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("foo/bar\\", 0));
+
+ cl_assert_equal_b(false, git_fs_path_is_valid("foo\\file.txt", GIT_FS_PATH_REJECT_BACKSLASH));
+ cl_assert_equal_b(false, git_fs_path_is_valid("foo/bar\\file.txt", GIT_FS_PATH_REJECT_BACKSLASH));
+ cl_assert_equal_b(false, git_fs_path_is_valid("foo/bar\\", GIT_FS_PATH_REJECT_BACKSLASH));
+}
+
+void test_path_core__isvalid_trailing_dot(void)
+{
+ cl_assert_equal_b(true, git_fs_path_is_valid("foo.", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("foo...", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("foo/bar.", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("foo./bar", 0));
+
+ cl_assert_equal_b(false, git_fs_path_is_valid("foo.", GIT_FS_PATH_REJECT_TRAILING_DOT));
+ cl_assert_equal_b(false, git_fs_path_is_valid("foo...", GIT_FS_PATH_REJECT_TRAILING_DOT));
+ cl_assert_equal_b(false, git_fs_path_is_valid("foo/bar.", GIT_FS_PATH_REJECT_TRAILING_DOT));
+ cl_assert_equal_b(false, git_fs_path_is_valid("foo./bar", GIT_FS_PATH_REJECT_TRAILING_DOT));
+}
+
+void test_path_core__isvalid_trailing_space(void)
+{
+ cl_assert_equal_b(true, git_fs_path_is_valid("foo ", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("foo ", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("foo/bar ", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid(" ", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("foo /bar", 0));
+
+ cl_assert_equal_b(false, git_fs_path_is_valid("foo ", GIT_FS_PATH_REJECT_TRAILING_SPACE));
+ cl_assert_equal_b(false, git_fs_path_is_valid("foo ", GIT_FS_PATH_REJECT_TRAILING_SPACE));
+ cl_assert_equal_b(false, git_fs_path_is_valid("foo/bar ", GIT_FS_PATH_REJECT_TRAILING_SPACE));
+ cl_assert_equal_b(false, git_fs_path_is_valid(" ", GIT_FS_PATH_REJECT_TRAILING_SPACE));
+ cl_assert_equal_b(false, git_fs_path_is_valid("foo /bar", GIT_FS_PATH_REJECT_TRAILING_SPACE));
+}
+
+void test_path_core__isvalid_trailing_colon(void)
+{
+ cl_assert_equal_b(true, git_fs_path_is_valid("foo:", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("foo/bar:", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid(":", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("foo:/bar", 0));
+
+ cl_assert_equal_b(false, git_fs_path_is_valid("foo:", GIT_FS_PATH_REJECT_TRAILING_COLON));
+ cl_assert_equal_b(false, git_fs_path_is_valid("foo/bar:", GIT_FS_PATH_REJECT_TRAILING_COLON));
+ cl_assert_equal_b(false, git_fs_path_is_valid(":", GIT_FS_PATH_REJECT_TRAILING_COLON));
+ cl_assert_equal_b(false, git_fs_path_is_valid("foo:/bar", GIT_FS_PATH_REJECT_TRAILING_COLON));
+}
+
+void test_path_core__isvalid_dos_paths(void)
+{
+ cl_assert_equal_b(true, git_fs_path_is_valid("aux", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("aux.", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("aux:", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("aux.asdf", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("aux.asdf\\zippy", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("aux:asdf\\foobar", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("con", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("prn", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("nul", 0));
+
+ cl_assert_equal_b(false, git_fs_path_is_valid("aux", GIT_FS_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_fs_path_is_valid("aux.", GIT_FS_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_fs_path_is_valid("aux:", GIT_FS_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_fs_path_is_valid("aux.asdf", GIT_FS_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_fs_path_is_valid("aux.asdf\\zippy", GIT_FS_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_fs_path_is_valid("aux:asdf\\foobar", GIT_FS_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_fs_path_is_valid("con", GIT_FS_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_fs_path_is_valid("prn", GIT_FS_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_fs_path_is_valid("nul", GIT_FS_PATH_REJECT_DOS_PATHS));
+
+ cl_assert_equal_b(true, git_fs_path_is_valid("aux1", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("aux1", GIT_FS_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(true, git_fs_path_is_valid("auxn", GIT_FS_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(true, git_fs_path_is_valid("aux\\foo", GIT_FS_PATH_REJECT_DOS_PATHS));
+}
+
+void test_path_core__isvalid_dos_paths_withnum(void)
+{
+ cl_assert_equal_b(true, git_fs_path_is_valid("com1", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("com1.", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("com1:", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("com1.asdf", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("com1.asdf\\zippy", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("com1:asdf\\foobar", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("com1\\foo", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("lpt1", 0));
+
+ cl_assert_equal_b(false, git_fs_path_is_valid("com1", GIT_FS_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_fs_path_is_valid("com1.", GIT_FS_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_fs_path_is_valid("com1:", GIT_FS_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_fs_path_is_valid("com1.asdf", GIT_FS_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_fs_path_is_valid("com1.asdf\\zippy", GIT_FS_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_fs_path_is_valid("com1:asdf\\foobar", GIT_FS_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_fs_path_is_valid("com1/foo", GIT_FS_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(false, git_fs_path_is_valid("lpt1", GIT_FS_PATH_REJECT_DOS_PATHS));
+
+ cl_assert_equal_b(true, git_fs_path_is_valid("com0", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("com0", GIT_FS_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(true, git_fs_path_is_valid("com10", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("com10", GIT_FS_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(true, git_fs_path_is_valid("comn", GIT_FS_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(true, git_fs_path_is_valid("com1\\foo", GIT_FS_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(true, git_fs_path_is_valid("lpt0", GIT_FS_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(true, git_fs_path_is_valid("lpt10", GIT_FS_PATH_REJECT_DOS_PATHS));
+ cl_assert_equal_b(true, git_fs_path_is_valid("lptn", GIT_FS_PATH_REJECT_DOS_PATHS));
+}
+
+void test_path_core__isvalid_nt_chars(void)
+{
+ cl_assert_equal_b(true, git_fs_path_is_valid("asdf\001foo", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("asdf\037bar", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("asdf<bar", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("asdf>foo", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("asdf:foo", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("asdf\"bar", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("asdf|foo", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("asdf?bar", 0));
+ cl_assert_equal_b(true, git_fs_path_is_valid("asdf*bar", 0));
+
+ cl_assert_equal_b(false, git_fs_path_is_valid("asdf\001foo", GIT_FS_PATH_REJECT_NT_CHARS));
+ cl_assert_equal_b(false, git_fs_path_is_valid("asdf\037bar", GIT_FS_PATH_REJECT_NT_CHARS));
+ cl_assert_equal_b(false, git_fs_path_is_valid("asdf<bar", GIT_FS_PATH_REJECT_NT_CHARS));
+ cl_assert_equal_b(false, git_fs_path_is_valid("asdf>foo", GIT_FS_PATH_REJECT_NT_CHARS));
+ cl_assert_equal_b(false, git_fs_path_is_valid("asdf:foo", GIT_FS_PATH_REJECT_NT_CHARS));
+ cl_assert_equal_b(false, git_fs_path_is_valid("asdf\"bar", GIT_FS_PATH_REJECT_NT_CHARS));
+ cl_assert_equal_b(false, git_fs_path_is_valid("asdf|foo", GIT_FS_PATH_REJECT_NT_CHARS));
+ cl_assert_equal_b(false, git_fs_path_is_valid("asdf?bar", GIT_FS_PATH_REJECT_NT_CHARS));
+ cl_assert_equal_b(false, git_fs_path_is_valid("asdf*bar", GIT_FS_PATH_REJECT_NT_CHARS));
+}
+
+static void test_join_unrooted(
+ const char *expected_result,
+ ssize_t expected_rootlen,
+ const char *path,
+ const char *base)
+{
+ git_str result = GIT_STR_INIT;
+ ssize_t root_at;
+
+ cl_git_pass(git_fs_path_join_unrooted(&result, path, base, &root_at));
+ cl_assert_equal_s(expected_result, result.ptr);
+ cl_assert_equal_i(expected_rootlen, root_at);
+
+ git_str_dispose(&result);
+}
+
+void test_path_core__join_unrooted(void)
+{
+ git_str out = GIT_STR_INIT;
+
+ test_join_unrooted("foo", 0, "foo", NULL);
+ test_join_unrooted("foo/bar", 0, "foo/bar", NULL);
+
+ /* Relative paths have base prepended */
+ test_join_unrooted("/foo/bar", 4, "bar", "/foo");
+ test_join_unrooted("/foo/bar/foobar", 4, "bar/foobar", "/foo");
+ test_join_unrooted("c:/foo/bar/foobar", 6, "bar/foobar", "c:/foo");
+ test_join_unrooted("c:/foo/bar/foobar", 10, "foobar", "c:/foo/bar");
+
+ /* Absolute paths are not prepended with base */
+ test_join_unrooted("/foo", 0, "/foo", "/asdf");
+ test_join_unrooted("/foo/bar", 0, "/foo/bar", "/asdf");
+
+ /* Drive letter is given as root length on Windows */
+ test_join_unrooted("c:/foo", 2, "c:/foo", "c:/asdf");
+ test_join_unrooted("c:/foo/bar", 2, "c:/foo/bar", "c:/asdf");
+
+#ifdef GIT_WIN32
+ /* Paths starting with '\\' are absolute */
+ test_join_unrooted("\\bar", 0, "\\bar", "c:/foo/");
+ test_join_unrooted("\\\\network\\bar", 9, "\\\\network\\bar", "c:/foo/");
+#else
+ /* Paths starting with '\\' are not absolute on non-Windows systems */
+ test_join_unrooted("/foo/\\bar", 4, "\\bar", "/foo");
+ test_join_unrooted("c:/foo/\\bar", 7, "\\bar", "c:/foo/");
+#endif
+
+ /* Base is returned when it's provided and is the prefix */
+ test_join_unrooted("c:/foo/bar/foobar", 6, "c:/foo/bar/foobar", "c:/foo");
+ test_join_unrooted("c:/foo/bar/foobar", 10, "c:/foo/bar/foobar", "c:/foo/bar");
+
+ /* Trailing slash in the base is ignored */
+ test_join_unrooted("c:/foo/bar/foobar", 6, "c:/foo/bar/foobar", "c:/foo/");
+
+ git_str_dispose(&out);
+}
+
+void test_path_core__join_unrooted_respects_funny_windows_roots(void)
+{
+ test_join_unrooted("💩:/foo/bar/foobar", 9, "bar/foobar", "💩:/foo");
+ test_join_unrooted("💩:/foo/bar/foobar", 13, "foobar", "💩:/foo/bar");
+ test_join_unrooted("💩:/foo", 5, "💩:/foo", "💩:/asdf");
+ test_join_unrooted("💩:/foo/bar", 5, "💩:/foo/bar", "💩:/asdf");
+ test_join_unrooted("💩:/foo/bar/foobar", 9, "💩:/foo/bar/foobar", "💩:/foo");
+ test_join_unrooted("💩:/foo/bar/foobar", 13, "💩:/foo/bar/foobar", "💩:/foo/bar");
+ test_join_unrooted("💩:/foo/bar/foobar", 9, "💩:/foo/bar/foobar", "💩:/foo/");
+}
diff --git a/tests/util/path/win32.c b/tests/util/path/win32.c
new file mode 100644
index 0000000..1aaf686
--- /dev/null
+++ b/tests/util/path/win32.c
@@ -0,0 +1,282 @@
+
+#include "clar_libgit2.h"
+
+#ifdef GIT_WIN32
+#include "win32/path_w32.h"
+#endif
+
+#ifdef GIT_WIN32
+static void test_utf8_to_utf16(const char *utf8_in, const wchar_t *utf16_expected)
+{
+ git_win32_path path_utf16;
+ int path_utf16len;
+
+ cl_assert((path_utf16len = git_win32_path_from_utf8(path_utf16, utf8_in)) >= 0);
+ cl_assert_equal_wcs(utf16_expected, path_utf16);
+ cl_assert_equal_i(wcslen(utf16_expected), path_utf16len);
+}
+
+static void test_utf8_to_utf16_relative(const char* utf8_in, const wchar_t* utf16_expected)
+{
+ git_win32_path path_utf16;
+ int path_utf16len;
+
+ cl_assert((path_utf16len = git_win32_path_relative_from_utf8(path_utf16, utf8_in)) >= 0);
+ cl_assert_equal_wcs(utf16_expected, path_utf16);
+ cl_assert_equal_i(wcslen(utf16_expected), path_utf16len);
+}
+#endif
+
+void test_path_win32__utf8_to_utf16(void)
+{
+#ifdef GIT_WIN32
+ test_utf8_to_utf16("C:\\", L"\\\\?\\C:\\");
+ test_utf8_to_utf16("c:\\", L"\\\\?\\c:\\");
+ test_utf8_to_utf16("C:/", L"\\\\?\\C:\\");
+ test_utf8_to_utf16("c:/", L"\\\\?\\c:\\");
+#endif
+}
+
+void test_path_win32__removes_trailing_slash(void)
+{
+#ifdef GIT_WIN32
+ test_utf8_to_utf16("C:\\Foo\\", L"\\\\?\\C:\\Foo");
+ test_utf8_to_utf16("C:\\Foo\\\\", L"\\\\?\\C:\\Foo");
+ test_utf8_to_utf16("C:\\Foo\\\\", L"\\\\?\\C:\\Foo");
+ test_utf8_to_utf16("C:/Foo/", L"\\\\?\\C:\\Foo");
+ test_utf8_to_utf16("C:/Foo///", L"\\\\?\\C:\\Foo");
+#endif
+}
+
+void test_path_win32__squashes_multiple_slashes(void)
+{
+#ifdef GIT_WIN32
+ test_utf8_to_utf16("C:\\\\Foo\\Bar\\\\Foobar", L"\\\\?\\C:\\Foo\\Bar\\Foobar");
+ test_utf8_to_utf16("C://Foo/Bar///Foobar", L"\\\\?\\C:\\Foo\\Bar\\Foobar");
+#endif
+}
+
+void test_path_win32__unc(void)
+{
+#ifdef GIT_WIN32
+ test_utf8_to_utf16("\\\\server\\c$\\unc\\path", L"\\\\?\\UNC\\server\\c$\\unc\\path");
+ test_utf8_to_utf16("//server/git/style/unc/path", L"\\\\?\\UNC\\server\\git\\style\\unc\\path");
+#endif
+}
+
+void test_path_win32__honors_max_path(void)
+{
+#ifdef GIT_WIN32
+ git_win32_path path_utf16;
+
+ test_utf8_to_utf16("C:\\This path is 261 characters which is fine for our path handling functions which cope with paths longer than MAX_PATH\\0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghijk",
+ L"\\\\?\\C:\\This path is 261 characters which is fine for our path handling functions which cope with paths longer than MAX_PATH\\0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghijk");
+
+ cl_check_fail(git_win32_path_from_utf8(path_utf16, "C:\\This path is 4097 chars and exceeds our maximum path length on Windows which is limited to 4096 characters\\alas\\0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij0123456789abcdefghij01"));
+
+#endif
+}
+
+void test_path_win32__dot_and_dotdot(void)
+{
+#ifdef GIT_WIN32
+ test_utf8_to_utf16("C:\\Foo\\..\\Foobar", L"\\\\?\\C:\\Foobar");
+ test_utf8_to_utf16("C:\\Foo\\Bar\\..\\Foobar", L"\\\\?\\C:\\Foo\\Foobar");
+ test_utf8_to_utf16("C:\\Foo\\Bar\\..\\Foobar\\..", L"\\\\?\\C:\\Foo");
+ test_utf8_to_utf16("C:\\Foobar\\..", L"\\\\?\\C:\\");
+ test_utf8_to_utf16("C:/Foo/Bar/../Foobar", L"\\\\?\\C:\\Foo\\Foobar");
+ test_utf8_to_utf16("C:/Foo/Bar/../Foobar/../Asdf/", L"\\\\?\\C:\\Foo\\Asdf");
+ test_utf8_to_utf16("C:/Foo/Bar/../Foobar/..", L"\\\\?\\C:\\Foo");
+ test_utf8_to_utf16("C:/Foo/..", L"\\\\?\\C:\\");
+
+ test_utf8_to_utf16("C:\\Foo\\Bar\\.\\Foobar", L"\\\\?\\C:\\Foo\\Bar\\Foobar");
+ test_utf8_to_utf16("C:\\.\\Foo\\.\\Bar\\.\\Foobar\\.\\", L"\\\\?\\C:\\Foo\\Bar\\Foobar");
+ test_utf8_to_utf16("C:/Foo/Bar/./Foobar", L"\\\\?\\C:\\Foo\\Bar\\Foobar");
+ test_utf8_to_utf16("C:/Foo/../Bar/./Foobar/../", L"\\\\?\\C:\\Bar");
+
+ test_utf8_to_utf16("C:\\Foo\\..\\..\\Bar", L"\\\\?\\C:\\Bar");
+#endif
+}
+
+void test_path_win32__absolute_from_no_drive_letter(void)
+{
+#ifdef GIT_WIN32
+ test_utf8_to_utf16("\\Foo", L"\\\\?\\C:\\Foo");
+ test_utf8_to_utf16("\\Foo\\Bar", L"\\\\?\\C:\\Foo\\Bar");
+ test_utf8_to_utf16("/Foo/Bar", L"\\\\?\\C:\\Foo\\Bar");
+#endif
+}
+
+void test_path_win32__absolute_from_relative(void)
+{
+#ifdef GIT_WIN32
+ char cwd_backup[MAX_PATH];
+
+ cl_must_pass(p_getcwd(cwd_backup, MAX_PATH));
+ cl_must_pass(p_chdir("C:/"));
+
+ test_utf8_to_utf16("Foo", L"\\\\?\\C:\\Foo");
+ test_utf8_to_utf16("..\\..\\Foo", L"\\\\?\\C:\\Foo");
+ test_utf8_to_utf16("Foo\\..", L"\\\\?\\C:\\");
+ test_utf8_to_utf16("Foo\\..\\..", L"\\\\?\\C:\\");
+ test_utf8_to_utf16("", L"\\\\?\\C:\\");
+
+ cl_must_pass(p_chdir("C:/Windows"));
+
+ test_utf8_to_utf16("Foo", L"\\\\?\\C:\\Windows\\Foo");
+ test_utf8_to_utf16("Foo\\Bar", L"\\\\?\\C:\\Windows\\Foo\\Bar");
+ test_utf8_to_utf16("..\\Foo", L"\\\\?\\C:\\Foo");
+ test_utf8_to_utf16("Foo\\..\\Bar", L"\\\\?\\C:\\Windows\\Bar");
+ test_utf8_to_utf16("", L"\\\\?\\C:\\Windows");
+
+ cl_must_pass(p_chdir(cwd_backup));
+#endif
+}
+
+void test_path_win32__keeps_relative(void)
+{
+#ifdef GIT_WIN32
+ /* Relative paths stay relative */
+ test_utf8_to_utf16_relative("Foo", L"Foo");
+ test_utf8_to_utf16_relative("..\\..\\Foo", L"..\\..\\Foo");
+ test_utf8_to_utf16_relative("Foo\\..", L"Foo\\..");
+ test_utf8_to_utf16_relative("Foo\\..\\..", L"Foo\\..\\..");
+ test_utf8_to_utf16_relative("Foo\\Bar", L"Foo\\Bar");
+ test_utf8_to_utf16_relative("Foo\\..\\Bar", L"Foo\\..\\Bar");
+ test_utf8_to_utf16_relative("../../Foo", L"..\\..\\Foo");
+ test_utf8_to_utf16_relative("Foo/..", L"Foo\\..");
+ test_utf8_to_utf16_relative("Foo/../..", L"Foo\\..\\..");
+ test_utf8_to_utf16_relative("Foo/Bar", L"Foo\\Bar");
+ test_utf8_to_utf16_relative("Foo/../Bar", L"Foo\\..\\Bar");
+ test_utf8_to_utf16_relative("Foo/../Bar/", L"Foo\\..\\Bar\\");
+ test_utf8_to_utf16_relative("", L"");
+
+ /* Absolute paths are canonicalized */
+ test_utf8_to_utf16_relative("\\Foo", L"\\\\?\\C:\\Foo");
+ test_utf8_to_utf16_relative("/Foo/Bar/", L"\\\\?\\C:\\Foo\\Bar");
+ test_utf8_to_utf16_relative("\\\\server\\c$\\unc\\path", L"\\\\?\\UNC\\server\\c$\\unc\\path");
+#endif
+}
+
+#ifdef GIT_WIN32
+static void test_canonicalize(const wchar_t *in, const wchar_t *expected)
+{
+ git_win32_path canonical;
+
+ cl_assert(wcslen(in) < MAX_PATH);
+ wcscpy(canonical, in);
+
+ cl_must_pass(git_win32_path_canonicalize(canonical));
+ cl_assert_equal_wcs(expected, canonical);
+}
+#endif
+
+static void test_remove_namespace(const wchar_t *in, const wchar_t *expected)
+{
+#ifdef GIT_WIN32
+ git_win32_path canonical;
+
+ cl_assert(wcslen(in) < MAX_PATH);
+ wcscpy(canonical, in);
+
+ git_win32_path_remove_namespace(canonical, wcslen(in));
+ cl_assert_equal_wcs(expected, canonical);
+#else
+ GIT_UNUSED(in);
+ GIT_UNUSED(expected);
+#endif
+}
+
+void test_path_win32__remove_namespace(void)
+{
+ test_remove_namespace(L"\\\\?\\C:\\Temp\\Foo", L"C:\\Temp\\Foo");
+ test_remove_namespace(L"\\\\?\\C:\\", L"C:\\");
+ test_remove_namespace(L"\\\\?\\", L"");
+
+ test_remove_namespace(L"\\??\\C:\\Temp\\Foo", L"C:\\Temp\\Foo");
+ test_remove_namespace(L"\\??\\C:\\", L"C:\\");
+ test_remove_namespace(L"\\??\\", L"");
+
+ test_remove_namespace(L"\\\\?\\UNC\\server\\C$\\folder", L"\\\\server\\C$\\folder");
+ test_remove_namespace(L"\\\\?\\UNC\\server\\C$\\folder", L"\\\\server\\C$\\folder");
+ test_remove_namespace(L"\\\\?\\UNC\\server\\C$", L"\\\\server\\C$");
+ test_remove_namespace(L"\\\\?\\UNC\\server\\", L"\\\\server");
+ test_remove_namespace(L"\\\\?\\UNC\\server", L"\\\\server");
+
+ test_remove_namespace(L"\\??\\UNC\\server\\C$\\folder", L"\\\\server\\C$\\folder");
+ test_remove_namespace(L"\\??\\UNC\\server\\C$\\folder", L"\\\\server\\C$\\folder");
+ test_remove_namespace(L"\\??\\UNC\\server\\C$", L"\\\\server\\C$");
+ test_remove_namespace(L"\\??\\UNC\\server\\", L"\\\\server");
+ test_remove_namespace(L"\\??\\UNC\\server", L"\\\\server");
+
+ test_remove_namespace(L"\\\\server\\C$\\folder", L"\\\\server\\C$\\folder");
+ test_remove_namespace(L"\\\\server\\C$", L"\\\\server\\C$");
+ test_remove_namespace(L"\\\\server\\", L"\\\\server");
+ test_remove_namespace(L"\\\\server", L"\\\\server");
+
+ test_remove_namespace(L"C:\\Foo\\Bar", L"C:\\Foo\\Bar");
+ test_remove_namespace(L"C:\\", L"C:\\");
+ test_remove_namespace(L"", L"");
+
+}
+
+void test_path_win32__canonicalize(void)
+{
+#ifdef GIT_WIN32
+ test_canonicalize(L"C:\\Foo\\Bar", L"C:\\Foo\\Bar");
+ test_canonicalize(L"C:\\Foo\\", L"C:\\Foo");
+ test_canonicalize(L"C:\\Foo\\\\", L"C:\\Foo");
+ test_canonicalize(L"C:\\Foo\\..\\Bar", L"C:\\Bar");
+ test_canonicalize(L"C:\\Foo\\..\\..\\Bar", L"C:\\Bar");
+ test_canonicalize(L"C:\\Foo\\..\\..\\..\\..\\", L"C:\\");
+ test_canonicalize(L"C:/Foo/Bar", L"C:\\Foo\\Bar");
+ test_canonicalize(L"C:/", L"C:\\");
+
+ test_canonicalize(L"\\\\?\\C:\\Foo\\Bar", L"\\\\?\\C:\\Foo\\Bar");
+ test_canonicalize(L"\\\\?\\C:\\Foo\\Bar\\", L"\\\\?\\C:\\Foo\\Bar");
+ test_canonicalize(L"\\\\?\\C:\\\\Foo\\.\\Bar\\\\..\\", L"\\\\?\\C:\\Foo");
+ test_canonicalize(L"\\\\?\\C:\\\\", L"\\\\?\\C:\\");
+ test_canonicalize(L"//?/C:/", L"\\\\?\\C:\\");
+ test_canonicalize(L"//?/C:/../../Foo/", L"\\\\?\\C:\\Foo");
+ test_canonicalize(L"//?/C:/Foo/../../", L"\\\\?\\C:\\");
+
+ test_canonicalize(L"\\\\?\\UNC\\server\\C$\\folder", L"\\\\?\\UNC\\server\\C$\\folder");
+ test_canonicalize(L"\\\\?\\UNC\\server\\C$\\folder\\", L"\\\\?\\UNC\\server\\C$\\folder");
+ test_canonicalize(L"\\\\?\\UNC\\server\\C$\\folder\\", L"\\\\?\\UNC\\server\\C$\\folder");
+ test_canonicalize(L"\\\\?\\UNC\\server\\C$\\folder\\..\\..\\..\\..\\share\\", L"\\\\?\\UNC\\server\\share");
+
+ test_canonicalize(L"\\\\server\\share", L"\\\\server\\share");
+ test_canonicalize(L"\\\\server\\share\\", L"\\\\server\\share");
+ test_canonicalize(L"\\\\server\\share\\\\foo\\\\bar", L"\\\\server\\share\\foo\\bar");
+ test_canonicalize(L"\\\\server\\\\share\\\\foo\\\\bar", L"\\\\server\\share\\foo\\bar");
+ test_canonicalize(L"\\\\server\\share\\..\\foo", L"\\\\server\\foo");
+ test_canonicalize(L"\\\\server\\..\\..\\share\\.\\foo", L"\\\\server\\share\\foo");
+#endif
+}
+
+void test_path_win32__8dot3_name(void)
+{
+#ifdef GIT_WIN32
+ char *shortname;
+
+ if (!cl_sandbox_supports_8dot3())
+ clar__skip();
+
+ /* Some guaranteed short names */
+ cl_assert_equal_s("PROGRA~1", (shortname = git_win32_path_8dot3_name("C:\\Program Files")));
+ git__free(shortname);
+
+ cl_assert_equal_s("WINDOWS", (shortname = git_win32_path_8dot3_name("C:\\WINDOWS")));
+ git__free(shortname);
+
+ /* Create some predictable short names */
+ cl_must_pass(p_mkdir(".foo", 0777));
+ cl_assert_equal_s("FOO~1", (shortname = git_win32_path_8dot3_name(".foo")));
+ git__free(shortname);
+
+ cl_git_write2file("bar~1", "foobar\n", 7, O_RDWR|O_CREAT, 0666);
+ cl_must_pass(p_mkdir(".bar", 0777));
+ cl_assert_equal_s("BAR~2", (shortname = git_win32_path_8dot3_name(".bar")));
+ git__free(shortname);
+#endif
+}
diff --git a/tests/util/pool.c b/tests/util/pool.c
new file mode 100644
index 0000000..464aad7
--- /dev/null
+++ b/tests/util/pool.c
@@ -0,0 +1,62 @@
+#include "clar_libgit2.h"
+#include "pool.h"
+#include "git2/oid.h"
+
+void test_pool__0(void)
+{
+ int i;
+ git_pool p;
+ void *ptr;
+
+ git_pool_init(&p, 1);
+
+ for (i = 1; i < 10000; i *= 2) {
+ ptr = git_pool_malloc(&p, i);
+ cl_assert(ptr != NULL);
+ cl_assert(git_pool__ptr_in_pool(&p, ptr));
+ cl_assert(!git_pool__ptr_in_pool(&p, &i));
+ }
+
+ git_pool_clear(&p);
+}
+
+void test_pool__1(void)
+{
+ int i;
+ git_pool p;
+
+ git_pool_init(&p, 1);
+ p.page_size = 4000;
+
+ for (i = 2010; i > 0; i--)
+ cl_assert(git_pool_malloc(&p, i) != NULL);
+
+#ifndef GIT_DEBUG_POOL
+ /* with fixed page size, allocation must end up with these values */
+ cl_assert_equal_i(591, git_pool__open_pages(&p));
+#endif
+ git_pool_clear(&p);
+
+ git_pool_init(&p, 1);
+ p.page_size = 4120;
+
+ for (i = 2010; i > 0; i--)
+ cl_assert(git_pool_malloc(&p, i) != NULL);
+
+#ifndef GIT_DEBUG_POOL
+ /* with fixed page size, allocation must end up with these values */
+ cl_assert_equal_i(sizeof(void *) == 8 ? 575 : 573, git_pool__open_pages(&p));
+#endif
+ git_pool_clear(&p);
+}
+
+void test_pool__strndup_limit(void)
+{
+ git_pool p;
+
+ git_pool_init(&p, 1);
+ /* ensure 64 bit doesn't overflow */
+ cl_assert(git_pool_strndup(&p, "foo", (size_t)-1) == NULL);
+ git_pool_clear(&p);
+}
+
diff --git a/tests/util/posix.c b/tests/util/posix.c
new file mode 100644
index 0000000..155f03a
--- /dev/null
+++ b/tests/util/posix.c
@@ -0,0 +1,238 @@
+#ifndef _WIN32
+# include <arpa/inet.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+#else
+# include <ws2tcpip.h>
+# ifdef _MSC_VER
+# pragma comment(lib, "ws2_32")
+# endif
+#endif
+
+#include "clar_libgit2.h"
+#include "futils.h"
+#include "posix.h"
+
+void test_posix__initialize(void)
+{
+#ifdef GIT_WIN32
+ /* on win32, the WSA context needs to be initialized
+ * before any socket calls can be performed */
+ WSADATA wsd;
+
+ cl_git_pass(WSAStartup(MAKEWORD(2,2), &wsd));
+ cl_assert(LOBYTE(wsd.wVersion) == 2 && HIBYTE(wsd.wVersion) == 2);
+#endif
+}
+
+static bool supports_ipv6(void)
+{
+#ifdef GIT_WIN32
+ /* IPv6 is supported on Vista and newer */
+ return git_has_win32_version(6, 0, 0);
+#else
+ return 1;
+#endif
+}
+
+void test_posix__inet_pton(void)
+{
+ struct in_addr addr;
+ struct in6_addr addr6;
+ size_t i;
+
+ struct in_addr_data {
+ const char *p;
+ const uint8_t n[4];
+ };
+
+ struct in6_addr_data {
+ const char *p;
+ const uint8_t n[16];
+ };
+
+ static struct in_addr_data in_addr_data[] = {
+ { "0.0.0.0", { 0, 0, 0, 0 } },
+ { "10.42.101.8", { 10, 42, 101, 8 } },
+ { "127.0.0.1", { 127, 0, 0, 1 } },
+ { "140.177.10.12", { 140, 177, 10, 12 } },
+ { "204.232.175.90", { 204, 232, 175, 90 } },
+ { "255.255.255.255", { 255, 255, 255, 255 } },
+ };
+
+ static struct in6_addr_data in6_addr_data[] = {
+ { "::", { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } },
+ { "::1", { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } },
+ { "0:0:0:0:0:0:0:1", { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 } },
+ { "2001:db8:8714:3a90::12", { 0x20, 0x01, 0x0d, 0xb8, 0x87, 0x14, 0x3a, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12 } },
+ { "fe80::f8ba:c2d6:86be:3645", { 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xba, 0xc2, 0xd6, 0x86, 0xbe, 0x36, 0x45 } },
+ { "::ffff:204.152.189.116", { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcc, 0x98, 0xbd, 0x74 } },
+ };
+
+ /* Test some ipv4 addresses */
+ for (i = 0; i < 6; i++) {
+ cl_assert(p_inet_pton(AF_INET, in_addr_data[i].p, &addr) == 1);
+ cl_assert(memcmp(&addr, in_addr_data[i].n, sizeof(struct in_addr)) == 0);
+ }
+
+ /* Test some ipv6 addresses */
+ if (supports_ipv6())
+ {
+ for (i = 0; i < 6; i++) {
+ cl_assert(p_inet_pton(AF_INET6, in6_addr_data[i].p, &addr6) == 1);
+ cl_assert(memcmp(&addr6, in6_addr_data[i].n, sizeof(struct in6_addr)) == 0);
+ }
+ }
+
+ /* Test some invalid strings */
+ cl_assert(p_inet_pton(AF_INET, "", &addr) == 0);
+ cl_assert(p_inet_pton(AF_INET, "foo", &addr) == 0);
+ cl_assert(p_inet_pton(AF_INET, " 127.0.0.1", &addr) == 0);
+ cl_assert(p_inet_pton(AF_INET, "bar", &addr) == 0);
+ cl_assert(p_inet_pton(AF_INET, "10.foo.bar.1", &addr) == 0);
+
+ /* Test unsupported address families */
+ cl_git_fail(p_inet_pton(INT_MAX-1, "52.472", &addr));
+ cl_assert_equal_i(EAFNOSUPPORT, errno);
+}
+
+void test_posix__utimes(void)
+{
+ struct p_timeval times[2];
+ struct stat st;
+ time_t curtime;
+ int fd;
+
+ /* test p_utimes */
+ times[0].tv_sec = 1234567890;
+ times[0].tv_usec = 0;
+ times[1].tv_sec = 1234567890;
+ times[1].tv_usec = 0;
+
+ cl_git_mkfile("foo", "Dummy file.");
+ cl_must_pass(p_utimes("foo", times));
+
+ cl_must_pass(p_stat("foo", &st));
+ cl_assert_equal_i(1234567890, st.st_atime);
+ cl_assert_equal_i(1234567890, st.st_mtime);
+
+
+ /* test p_futimes */
+ times[0].tv_sec = 1414141414;
+ times[0].tv_usec = 0;
+ times[1].tv_sec = 1414141414;
+ times[1].tv_usec = 0;
+
+ cl_must_pass(fd = p_open("foo", O_RDWR));
+ cl_must_pass(p_futimes(fd, times));
+ cl_must_pass(p_close(fd));
+
+ cl_must_pass(p_stat("foo", &st));
+ cl_assert_equal_i(1414141414, st.st_atime);
+ cl_assert_equal_i(1414141414, st.st_mtime);
+
+
+ /* test p_utimes with current time, assume that
+ * it takes < 5 seconds to get the time...!
+ */
+ cl_must_pass(p_utimes("foo", NULL));
+
+ curtime = time(NULL);
+ cl_must_pass(p_stat("foo", &st));
+ cl_assert((st.st_atime - curtime) < 5);
+ cl_assert((st.st_mtime - curtime) < 5);
+
+ cl_must_pass(p_unlink("foo"));
+}
+
+void test_posix__unlink_removes_symlink(void)
+{
+ if (!git_fs_path_supports_symlinks(clar_sandbox_path()))
+ clar__skip();
+
+ cl_git_mkfile("file", "Dummy file.");
+ cl_git_pass(git_futils_mkdir("dir", 0777, 0));
+
+ cl_must_pass(p_symlink("file", "file-symlink"));
+ cl_must_pass(p_symlink("dir", "dir-symlink"));
+
+ cl_must_pass(p_unlink("file-symlink"));
+ cl_must_pass(p_unlink("dir-symlink"));
+
+ cl_assert(git_fs_path_exists("file"));
+ cl_assert(git_fs_path_exists("dir"));
+
+ cl_must_pass(p_unlink("file"));
+ cl_must_pass(p_rmdir("dir"));
+}
+
+void test_posix__symlink_resolves_to_correct_type(void)
+{
+ git_str contents = GIT_STR_INIT;
+
+ if (!git_fs_path_supports_symlinks(clar_sandbox_path()))
+ clar__skip();
+
+ cl_must_pass(git_futils_mkdir("dir", 0777, 0));
+ cl_must_pass(git_futils_mkdir("file", 0777, 0));
+ cl_git_mkfile("dir/file", "symlink target");
+
+ cl_git_pass(p_symlink("file", "dir/link"));
+
+ cl_git_pass(git_futils_readbuffer(&contents, "dir/file"));
+ cl_assert_equal_s(contents.ptr, "symlink target");
+
+ cl_must_pass(p_unlink("dir/link"));
+ cl_must_pass(p_unlink("dir/file"));
+ cl_must_pass(p_rmdir("dir"));
+ cl_must_pass(p_rmdir("file"));
+
+ git_str_dispose(&contents);
+}
+
+void test_posix__relative_symlink(void)
+{
+ git_str contents = GIT_STR_INIT;
+
+ if (!git_fs_path_supports_symlinks(clar_sandbox_path()))
+ clar__skip();
+
+ cl_must_pass(git_futils_mkdir("dir", 0777, 0));
+ cl_git_mkfile("file", "contents");
+ cl_git_pass(p_symlink("../file", "dir/link"));
+ cl_git_pass(git_futils_readbuffer(&contents, "dir/link"));
+ cl_assert_equal_s(contents.ptr, "contents");
+
+ cl_must_pass(p_unlink("file"));
+ cl_must_pass(p_unlink("dir/link"));
+ cl_must_pass(p_rmdir("dir"));
+
+ git_str_dispose(&contents);
+}
+
+void test_posix__symlink_to_file_across_dirs(void)
+{
+ git_str contents = GIT_STR_INIT;
+
+ if (!git_fs_path_supports_symlinks(clar_sandbox_path()))
+ clar__skip();
+
+ /*
+ * Create a relative symlink that points into another
+ * directory. This used to not work on Win32, where we
+ * forgot to convert directory separators to
+ * Windows-style ones.
+ */
+ cl_must_pass(git_futils_mkdir("dir", 0777, 0));
+ cl_git_mkfile("dir/target", "symlink target");
+ cl_git_pass(p_symlink("dir/target", "link"));
+
+ cl_git_pass(git_futils_readbuffer(&contents, "dir/target"));
+ cl_assert_equal_s(contents.ptr, "symlink target");
+
+ cl_must_pass(p_unlink("dir/target"));
+ cl_must_pass(p_unlink("link"));
+ cl_must_pass(p_rmdir("dir"));
+
+ git_str_dispose(&contents);
+}
diff --git a/tests/util/pqueue.c b/tests/util/pqueue.c
new file mode 100644
index 0000000..38931ec
--- /dev/null
+++ b/tests/util/pqueue.c
@@ -0,0 +1,150 @@
+#include "clar_libgit2.h"
+#include "pqueue.h"
+
+static int cmp_ints(const void *v1, const void *v2)
+{
+ int i1 = *(int *)v1, i2 = *(int *)v2;
+ return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0;
+}
+
+void test_pqueue__items_are_put_in_order(void)
+{
+ git_pqueue pq;
+ int i, vals[20];
+
+ cl_git_pass(git_pqueue_init(&pq, 0, 20, cmp_ints));
+
+ for (i = 0; i < 20; ++i) {
+ if (i < 10)
+ vals[i] = 10 - i; /* 10 down to 1 */
+ else
+ vals[i] = i + 1; /* 11 up to 20 */
+
+ cl_git_pass(git_pqueue_insert(&pq, &vals[i]));
+ }
+
+ cl_assert_equal_i(20, git_pqueue_size(&pq));
+
+ for (i = 1; i <= 20; ++i) {
+ void *p = git_pqueue_pop(&pq);
+ cl_assert(p);
+ cl_assert_equal_i(i, *(int *)p);
+ }
+
+ cl_assert_equal_i(0, git_pqueue_size(&pq));
+
+ git_pqueue_free(&pq);
+}
+
+void test_pqueue__interleave_inserts_and_pops(void)
+{
+ git_pqueue pq;
+ int chunk, v, i, vals[200];
+
+ cl_git_pass(git_pqueue_init(&pq, 0, 20, cmp_ints));
+
+ for (v = 0, chunk = 20; chunk <= 200; chunk += 20) {
+ /* push the next 20 */
+ for (; v < chunk; ++v) {
+ vals[v] = (v & 1) ? 200 - v : v;
+ cl_git_pass(git_pqueue_insert(&pq, &vals[v]));
+ }
+
+ /* pop the lowest 10 */
+ for (i = 0; i < 10; ++i)
+ (void)git_pqueue_pop(&pq);
+ }
+
+ cl_assert_equal_i(100, git_pqueue_size(&pq));
+
+ /* at this point, we've popped 0-99 */
+
+ for (v = 100; v < 200; ++v) {
+ void *p = git_pqueue_pop(&pq);
+ cl_assert(p);
+ cl_assert_equal_i(v, *(int *)p);
+ }
+
+ cl_assert_equal_i(0, git_pqueue_size(&pq));
+
+ git_pqueue_free(&pq);
+}
+
+void test_pqueue__max_heap_size(void)
+{
+ git_pqueue pq;
+ int i, vals[100];
+
+ cl_git_pass(git_pqueue_init(&pq, GIT_PQUEUE_FIXED_SIZE, 50, cmp_ints));
+
+ for (i = 0; i < 100; ++i) {
+ vals[i] = (i & 1) ? 100 - i : i;
+ cl_git_pass(git_pqueue_insert(&pq, &vals[i]));
+ }
+
+ cl_assert_equal_i(50, git_pqueue_size(&pq));
+
+ for (i = 50; i < 100; ++i) {
+ void *p = git_pqueue_pop(&pq);
+ cl_assert(p);
+ cl_assert_equal_i(i, *(int *)p);
+ }
+
+ cl_assert_equal_i(0, git_pqueue_size(&pq));
+
+ git_pqueue_free(&pq);
+}
+
+void test_pqueue__max_heap_size_without_comparison(void)
+{
+ git_pqueue pq;
+ int i, vals[100] = { 0 };
+
+ cl_git_pass(git_pqueue_init(&pq, GIT_PQUEUE_FIXED_SIZE, 50, NULL));
+
+ for (i = 0; i < 100; ++i)
+ cl_git_pass(git_pqueue_insert(&pq, &vals[i]));
+
+ cl_assert_equal_i(50, git_pqueue_size(&pq));
+
+ /* As we have no comparison function, we cannot make any
+ * actual assumptions about which entries are part of the
+ * pqueue */
+ for (i = 0; i < 50; ++i)
+ cl_assert(git_pqueue_pop(&pq));
+
+ cl_assert_equal_i(0, git_pqueue_size(&pq));
+
+ git_pqueue_free(&pq);
+}
+
+static int cmp_ints_like_commit_time(const void *a, const void *b)
+{
+ return *((const int *)a) < *((const int *)b);
+}
+
+void test_pqueue__interleaved_pushes_and_pops(void)
+{
+ git_pqueue pq;
+ int i, j, *val;
+ static int commands[] =
+ { 6, 9, 8, 0, 5, 0, 7, 0, 4, 3, 0, 0, 0, 4, 0, 2, 0, 1, 0, 0, -1 };
+ static int expected[] =
+ { 9, 8, 7, 6, 5, 4, 4, 3, 2, 1, -1 };
+
+ cl_git_pass(git_pqueue_init(&pq, 0, 10, cmp_ints_like_commit_time));
+
+ for (i = 0, j = 0; commands[i] >= 0; ++i) {
+ if (!commands[i]) {
+ cl_assert((val = git_pqueue_pop(&pq)) != NULL);
+ cl_assert_equal_i(expected[j], *val);
+ ++j;
+ } else {
+ cl_git_pass(git_pqueue_insert(&pq, &commands[i]));
+ }
+ }
+
+ cl_assert_equal_i(0, git_pqueue_size(&pq));
+ git_pqueue_free(&pq);
+}
+
diff --git a/tests/util/precompiled.c b/tests/util/precompiled.c
new file mode 100644
index 0000000..5f656a4
--- /dev/null
+++ b/tests/util/precompiled.c
@@ -0,0 +1 @@
+#include "precompiled.h"
diff --git a/tests/util/precompiled.h b/tests/util/precompiled.h
new file mode 100644
index 0000000..6fa2142
--- /dev/null
+++ b/tests/util/precompiled.h
@@ -0,0 +1,3 @@
+#include "common.h"
+#include "clar.h"
+#include "clar_libgit2.h"
diff --git a/tests/util/qsort.c b/tests/util/qsort.c
new file mode 100644
index 0000000..d8fa20a
--- /dev/null
+++ b/tests/util/qsort.c
@@ -0,0 +1,90 @@
+#include "clar_libgit2.h"
+
+#define assert_sorted(a, cmp) \
+ _assert_sorted(a, ARRAY_SIZE(a), sizeof(*a), cmp)
+
+struct big_entries {
+ char c[311];
+};
+
+static void _assert_sorted(void *els, size_t n, size_t elsize, git__sort_r_cmp cmp)
+{
+ int8_t *p = els;
+
+ git__qsort_r(p, n, elsize, cmp, NULL);
+ while (n-- > 1) {
+ cl_assert(cmp(p, p + elsize, NULL) <= 0);
+ p += elsize;
+ }
+}
+
+static int cmp_big(const void *_a, const void *_b, void *payload)
+{
+ const struct big_entries *a = (const struct big_entries *)_a, *b = (const struct big_entries *)_b;
+ GIT_UNUSED(payload);
+ return (a->c[0] < b->c[0]) ? -1 : (a->c[0] > b->c[0]) ? +1 : 0;
+}
+
+static int cmp_int(const void *_a, const void *_b, void *payload)
+{
+ int a = *(const int *)_a, b = *(const int *)_b;
+ GIT_UNUSED(payload);
+ return (a < b) ? -1 : (a > b) ? +1 : 0;
+}
+
+static int cmp_str(const void *_a, const void *_b, void *payload)
+{
+ GIT_UNUSED(payload);
+ return strcmp((const char *) _a, (const char *) _b);
+}
+
+void test_qsort__array_with_single_entry(void)
+{
+ int a[] = { 10 };
+ assert_sorted(a, cmp_int);
+}
+
+void test_qsort__array_with_equal_entries(void)
+{
+ int a[] = { 4, 4, 4, 4 };
+ assert_sorted(a, cmp_int);
+}
+
+void test_qsort__sorted_array(void)
+{
+ int a[] = { 1, 10 };
+ assert_sorted(a, cmp_int);
+}
+
+void test_qsort__unsorted_array(void)
+{
+ int a[] = { 123, 9, 412938, 10, 234, 89 };
+ assert_sorted(a, cmp_int);
+}
+
+void test_qsort__sorting_strings(void)
+{
+ char *a[] = { "foo", "bar", "baz" };
+ assert_sorted(a, cmp_str);
+}
+
+void test_qsort__sorting_big_entries(void)
+{
+ struct big_entries a[5];
+
+ memset(&a, 0, sizeof(a));
+
+ memset(a[0].c, 'w', sizeof(a[0].c) - 1);
+ memset(a[1].c, 'c', sizeof(a[1].c) - 1);
+ memset(a[2].c, 'w', sizeof(a[2].c) - 1);
+ memset(a[3].c, 'h', sizeof(a[3].c) - 1);
+ memset(a[4].c, 'a', sizeof(a[4].c) - 1);
+
+ assert_sorted(a, cmp_big);
+
+ cl_assert_equal_i(strspn(a[0].c, "a"), sizeof(a[0].c) - 1);
+ cl_assert_equal_i(strspn(a[1].c, "c"), sizeof(a[1].c) - 1);
+ cl_assert_equal_i(strspn(a[2].c, "h"), sizeof(a[2].c) - 1);
+ cl_assert_equal_i(strspn(a[3].c, "w"), sizeof(a[3].c) - 1);
+ cl_assert_equal_i(strspn(a[4].c, "w"), sizeof(a[4].c) - 1);
+}
diff --git a/tests/util/regexp.c b/tests/util/regexp.c
new file mode 100644
index 0000000..a76955d
--- /dev/null
+++ b/tests/util/regexp.c
@@ -0,0 +1,197 @@
+#include "clar_libgit2.h"
+
+#include <locale.h>
+
+#include "regexp.h"
+
+#if LC_ALL > 0
+static const char *old_locales[LC_ALL];
+#endif
+
+static git_regexp regex;
+
+void test_regexp__initialize(void)
+{
+#if LC_ALL > 0
+ memset(&old_locales, 0, sizeof(old_locales));
+#endif
+}
+
+void test_regexp__cleanup(void)
+{
+ git_regexp_dispose(&regex);
+}
+
+static void try_set_locale(int category)
+{
+#if LC_ALL > 0
+ old_locales[category] = setlocale(category, NULL);
+#endif
+
+ if (!setlocale(category, "UTF-8") &&
+ !setlocale(category, "c.utf8") &&
+ !setlocale(category, "en_US.UTF-8"))
+ cl_skip();
+
+ if (MB_CUR_MAX == 1)
+ cl_fail("Expected locale to be switched to multibyte");
+}
+
+
+void test_regexp__compile_ignores_global_locale_ctype(void)
+{
+ try_set_locale(LC_CTYPE);
+ cl_git_pass(git_regexp_compile(&regex, "[\xc0-\xff][\x80-\xbf]", 0));
+}
+
+void test_regexp__compile_ignores_global_locale_collate(void)
+{
+#ifdef GIT_WIN32
+ cl_skip();
+#endif
+
+ try_set_locale(LC_COLLATE);
+ cl_git_pass(git_regexp_compile(&regex, "[\xc0-\xff][\x80-\xbf]", 0));
+}
+
+void test_regexp__regex_matches_digits_with_locale(void)
+{
+ char c, str[2];
+
+#ifdef GIT_WIN32
+ cl_skip();
+#endif
+
+ try_set_locale(LC_COLLATE);
+ try_set_locale(LC_CTYPE);
+
+ cl_git_pass(git_regexp_compile(&regex, "[[:digit:]]", 0));
+
+ str[1] = '\0';
+ for (c = '0'; c <= '9'; c++) {
+ str[0] = c;
+ cl_git_pass(git_regexp_match(&regex, str));
+ }
+}
+
+void test_regexp__regex_matches_alphabet_with_locale(void)
+{
+ char c, str[2];
+
+#ifdef GIT_WIN32
+ cl_skip();
+#endif
+
+ try_set_locale(LC_COLLATE);
+ try_set_locale(LC_CTYPE);
+
+ cl_git_pass(git_regexp_compile(&regex, "[[:alpha:]]", 0));
+
+ str[1] = '\0';
+ for (c = 'a'; c <= 'z'; c++) {
+ str[0] = c;
+ cl_git_pass(git_regexp_match(&regex, str));
+ }
+ for (c = 'A'; c <= 'Z'; c++) {
+ str[0] = c;
+ cl_git_pass(git_regexp_match(&regex, str));
+ }
+}
+
+void test_regexp__simple_search_matches(void)
+{
+ cl_git_pass(git_regexp_compile(&regex, "a", 0));
+ cl_git_pass(git_regexp_search(&regex, "a", 0, NULL));
+}
+
+void test_regexp__case_insensitive_search_matches(void)
+{
+ cl_git_pass(git_regexp_compile(&regex, "a", GIT_REGEXP_ICASE));
+ cl_git_pass(git_regexp_search(&regex, "A", 0, NULL));
+}
+
+void test_regexp__nonmatching_search_returns_error(void)
+{
+ cl_git_pass(git_regexp_compile(&regex, "a", 0));
+ cl_git_fail(git_regexp_search(&regex, "b", 0, NULL));
+}
+
+void test_regexp__search_finds_complete_match(void)
+{
+ git_regmatch matches[1];
+
+ cl_git_pass(git_regexp_compile(&regex, "abc", 0));
+ cl_git_pass(git_regexp_search(&regex, "abc", 1, matches));
+ cl_assert_equal_i(matches[0].start, 0);
+ cl_assert_equal_i(matches[0].end, 3);
+}
+
+void test_regexp__search_finds_correct_offsets(void)
+{
+ git_regmatch matches[3];
+
+ cl_git_pass(git_regexp_compile(&regex, "(a*)(b*)", 0));
+ cl_git_pass(git_regexp_search(&regex, "ab", 3, matches));
+ cl_assert_equal_i(matches[0].start, 0);
+ cl_assert_equal_i(matches[0].end, 2);
+ cl_assert_equal_i(matches[1].start, 0);
+ cl_assert_equal_i(matches[1].end, 1);
+ cl_assert_equal_i(matches[2].start, 1);
+ cl_assert_equal_i(matches[2].end, 2);
+}
+
+void test_regexp__search_finds_empty_group(void)
+{
+ git_regmatch matches[3];
+
+ cl_git_pass(git_regexp_compile(&regex, "(a*)(b*)c", 0));
+ cl_git_pass(git_regexp_search(&regex, "ac", 3, matches));
+ cl_assert_equal_i(matches[0].start, 0);
+ cl_assert_equal_i(matches[0].end, 2);
+ cl_assert_equal_i(matches[1].start, 0);
+ cl_assert_equal_i(matches[1].end, 1);
+ cl_assert_equal_i(matches[2].start, 1);
+ cl_assert_equal_i(matches[2].end, 1);
+}
+
+void test_regexp__search_fills_matches_with_first_matching_groups(void)
+{
+ git_regmatch matches[2];
+
+ cl_git_pass(git_regexp_compile(&regex, "(a)(b)(c)", 0));
+ cl_git_pass(git_regexp_search(&regex, "abc", 2, matches));
+ cl_assert_equal_i(matches[0].start, 0);
+ cl_assert_equal_i(matches[0].end, 3);
+ cl_assert_equal_i(matches[1].start, 0);
+ cl_assert_equal_i(matches[1].end, 1);
+}
+
+void test_regexp__search_skips_nonmatching_group(void)
+{
+ git_regmatch matches[4];
+
+ cl_git_pass(git_regexp_compile(&regex, "(a)(b)?(c)", 0));
+ cl_git_pass(git_regexp_search(&regex, "ac", 4, matches));
+ cl_assert_equal_i(matches[0].start, 0);
+ cl_assert_equal_i(matches[0].end, 2);
+ cl_assert_equal_i(matches[1].start, 0);
+ cl_assert_equal_i(matches[1].end, 1);
+ cl_assert_equal_i(matches[2].start, -1);
+ cl_assert_equal_i(matches[2].end, -1);
+ cl_assert_equal_i(matches[3].start, 1);
+ cl_assert_equal_i(matches[3].end, 2);
+}
+
+void test_regexp__search_initializes_trailing_nonmatching_groups(void)
+{
+ git_regmatch matches[3];
+
+ cl_git_pass(git_regexp_compile(&regex, "(a)bc", 0));
+ cl_git_pass(git_regexp_search(&regex, "abc", 3, matches));
+ cl_assert_equal_i(matches[0].start, 0);
+ cl_assert_equal_i(matches[0].end, 3);
+ cl_assert_equal_i(matches[1].start, 0);
+ cl_assert_equal_i(matches[1].end, 1);
+ cl_assert_equal_i(matches[2].start, -1);
+ cl_assert_equal_i(matches[2].end, -1);
+}
diff --git a/tests/util/rmdir.c b/tests/util/rmdir.c
new file mode 100644
index 0000000..71ec05f
--- /dev/null
+++ b/tests/util/rmdir.c
@@ -0,0 +1,120 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+
+static const char *empty_tmp_dir = "test_gitfo_rmdir_recurs_test";
+
+void test_rmdir__initialize(void)
+{
+ git_str path = GIT_STR_INIT;
+
+ cl_must_pass(p_mkdir(empty_tmp_dir, 0777));
+
+ cl_git_pass(git_str_joinpath(&path, empty_tmp_dir, "/one"));
+ cl_must_pass(p_mkdir(path.ptr, 0777));
+
+ cl_git_pass(git_str_joinpath(&path, empty_tmp_dir, "/one/two_one"));
+ cl_must_pass(p_mkdir(path.ptr, 0777));
+
+ cl_git_pass(git_str_joinpath(&path, empty_tmp_dir, "/one/two_two"));
+ cl_must_pass(p_mkdir(path.ptr, 0777));
+
+ cl_git_pass(git_str_joinpath(&path, empty_tmp_dir, "/one/two_two/three"));
+ cl_must_pass(p_mkdir(path.ptr, 0777));
+
+ cl_git_pass(git_str_joinpath(&path, empty_tmp_dir, "/two"));
+ cl_must_pass(p_mkdir(path.ptr, 0777));
+
+ git_str_dispose(&path);
+}
+
+void test_rmdir__cleanup(void)
+{
+ if (git_fs_path_exists(empty_tmp_dir))
+ cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_RMDIR_REMOVE_FILES));
+}
+
+/* make sure empty dir can be deleted recursively */
+void test_rmdir__delete_recursive(void)
+{
+ git_str path = GIT_STR_INIT;
+ cl_git_pass(git_str_joinpath(&path, empty_tmp_dir, "/one"));
+ cl_assert(git_fs_path_exists(git_str_cstr(&path)));
+
+ cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_RMDIR_EMPTY_HIERARCHY));
+
+ cl_assert(!git_fs_path_exists(git_str_cstr(&path)));
+
+ git_str_dispose(&path);
+}
+
+/* make sure non-empty dir cannot be deleted recursively */
+void test_rmdir__fail_to_delete_non_empty_dir(void)
+{
+ git_str file = GIT_STR_INIT;
+
+ cl_git_pass(git_str_joinpath(&file, empty_tmp_dir, "/two/file.txt"));
+
+ cl_git_mkfile(git_str_cstr(&file), "dummy");
+
+ cl_git_fail(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_RMDIR_EMPTY_HIERARCHY));
+
+ cl_must_pass(p_unlink(file.ptr));
+ cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_RMDIR_EMPTY_HIERARCHY));
+
+ cl_assert(!git_fs_path_exists(empty_tmp_dir));
+
+ git_str_dispose(&file);
+}
+
+void test_rmdir__keep_base(void)
+{
+ cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_RMDIR_SKIP_ROOT));
+ cl_assert(git_fs_path_exists(empty_tmp_dir));
+}
+
+void test_rmdir__can_skip_non_empty_dir(void)
+{
+ git_str file = GIT_STR_INIT;
+
+ cl_git_pass(git_str_joinpath(&file, empty_tmp_dir, "/two/file.txt"));
+
+ cl_git_mkfile(git_str_cstr(&file), "dummy");
+
+ cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_RMDIR_SKIP_NONEMPTY));
+ cl_assert(git_fs_path_exists(git_str_cstr(&file)) == true);
+
+ cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_RMDIR_REMOVE_FILES));
+ cl_assert(git_fs_path_exists(empty_tmp_dir) == false);
+
+ git_str_dispose(&file);
+}
+
+void test_rmdir__can_remove_empty_parents(void)
+{
+ git_str file = GIT_STR_INIT;
+
+ cl_git_pass(
+ git_str_joinpath(&file, empty_tmp_dir, "/one/two_two/three/file.txt"));
+ cl_git_mkfile(git_str_cstr(&file), "dummy");
+ cl_assert(git_fs_path_isfile(git_str_cstr(&file)));
+
+ cl_git_pass(git_futils_rmdir_r("one/two_two/three/file.txt", empty_tmp_dir,
+ GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_EMPTY_PARENTS));
+
+ cl_assert(!git_fs_path_exists(git_str_cstr(&file)));
+
+ git_str_rtruncate_at_char(&file, '/'); /* three (only contained file.txt) */
+ cl_assert(!git_fs_path_exists(git_str_cstr(&file)));
+
+ git_str_rtruncate_at_char(&file, '/'); /* two_two (only contained three) */
+ cl_assert(!git_fs_path_exists(git_str_cstr(&file)));
+
+ git_str_rtruncate_at_char(&file, '/'); /* one (contained two_one also) */
+ cl_assert(git_fs_path_exists(git_str_cstr(&file)));
+
+ cl_assert(git_fs_path_exists(empty_tmp_dir) == true);
+
+ git_str_dispose(&file);
+
+ cl_git_pass(git_futils_rmdir_r(empty_tmp_dir, NULL, GIT_RMDIR_EMPTY_HIERARCHY));
+}
diff --git a/tests/util/sha1.c b/tests/util/sha1.c
new file mode 100644
index 0000000..fe4032c
--- /dev/null
+++ b/tests/util/sha1.c
@@ -0,0 +1,100 @@
+#include "clar_libgit2.h"
+#include "hash.h"
+
+#define FIXTURE_DIR "sha1"
+
+#ifdef GIT_SHA1_WIN32
+static git_hash_win32_provider_t orig_provider;
+#endif
+
+void test_sha1__initialize(void)
+{
+#ifdef GIT_SHA1_WIN32
+ orig_provider = git_hash_win32_provider();
+#endif
+
+ cl_fixture_sandbox(FIXTURE_DIR);
+}
+
+void test_sha1__cleanup(void)
+{
+#ifdef GIT_SHA1_WIN32
+ git_hash_win32_set_provider(orig_provider);
+#endif
+
+ cl_fixture_cleanup(FIXTURE_DIR);
+}
+
+static int sha1_file(unsigned char *out, const char *filename)
+{
+ git_hash_ctx ctx;
+ char buf[2048];
+ int fd, ret;
+ ssize_t read_len;
+
+ fd = p_open(filename, O_RDONLY);
+ cl_assert(fd >= 0);
+
+ cl_git_pass(git_hash_ctx_init(&ctx, GIT_HASH_ALGORITHM_SHA1));
+
+ while ((read_len = p_read(fd, buf, 2048)) > 0)
+ cl_git_pass(git_hash_update(&ctx, buf, (size_t)read_len));
+
+ cl_assert_equal_i(0, read_len);
+ p_close(fd);
+
+ ret = git_hash_final(out, &ctx);
+ git_hash_ctx_cleanup(&ctx);
+
+ return ret;
+}
+
+void test_sha1__sum(void)
+{
+ unsigned char expected[GIT_HASH_SHA1_SIZE] = {
+ 0x4e, 0x72, 0x67, 0x9e, 0x3e, 0xa4, 0xd0, 0x4e, 0x0c, 0x64,
+ 0x2f, 0x02, 0x9e, 0x61, 0xeb, 0x80, 0x56, 0xc7, 0xed, 0x94
+ };
+ unsigned char actual[GIT_HASH_SHA1_SIZE];
+
+ cl_git_pass(sha1_file(actual, FIXTURE_DIR "/hello_c"));
+ cl_assert_equal_i(0, memcmp(expected, actual, GIT_HASH_SHA1_SIZE));
+}
+
+/* test that sha1 collision detection works when enabled */
+void test_sha1__detect_collision_attack(void)
+{
+ unsigned char actual[GIT_HASH_SHA1_SIZE];
+ unsigned char expected[GIT_HASH_SHA1_SIZE] = {
+ 0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17,
+ 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a
+ };
+
+#ifdef GIT_SHA1_COLLISIONDETECT
+ GIT_UNUSED(&expected);
+ cl_git_fail(sha1_file(actual, FIXTURE_DIR "/shattered-1.pdf"));
+ cl_assert_equal_s("SHA1 collision attack detected", git_error_last()->message);
+#else
+ cl_git_pass(sha1_file(actual, FIXTURE_DIR "/shattered-1.pdf"));
+ cl_assert_equal_i(0, memcmp(expected, actual, GIT_HASH_SHA1_SIZE));
+#endif
+}
+
+void test_sha1__win32_providers(void)
+{
+#ifdef GIT_SHA1_WIN32
+ unsigned char expected[GIT_HASH_SHA1_SIZE] = {
+ 0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17,
+ 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a
+ };
+ unsigned char actual[GIT_HASH_SHA1_SIZE];
+
+ git_hash_win32_set_provider(GIT_HASH_WIN32_CRYPTOAPI);
+ cl_git_pass(sha1_file(actual, FIXTURE_DIR "/shattered-1.pdf"));
+ cl_assert_equal_i(0, memcmp(expected, actual, GIT_HASH_SHA1_SIZE));
+
+ git_hash_win32_set_provider(GIT_HASH_WIN32_CNG);
+ cl_git_pass(sha1_file(actual, FIXTURE_DIR "/shattered-1.pdf"));
+ cl_assert_equal_i(0, memcmp(expected, actual, GIT_HASH_SHA1_SIZE));
+#endif
+}
diff --git a/tests/util/sha256.c b/tests/util/sha256.c
new file mode 100644
index 0000000..75b3d73
--- /dev/null
+++ b/tests/util/sha256.c
@@ -0,0 +1,113 @@
+#include "clar_libgit2.h"
+#include "hash.h"
+
+#define FIXTURE_DIR "sha1"
+
+#ifdef GIT_SHA256_WIN32
+static git_hash_win32_provider_t orig_provider;
+#endif
+
+void test_sha256__initialize(void)
+{
+#ifdef GIT_SHA256_WIN32
+ orig_provider = git_hash_win32_provider();
+#endif
+
+ cl_fixture_sandbox(FIXTURE_DIR);
+}
+
+void test_sha256__cleanup(void)
+{
+#ifdef GIT_SHA256_WIN32
+ git_hash_win32_set_provider(orig_provider);
+#endif
+
+ cl_fixture_cleanup(FIXTURE_DIR);
+}
+
+static int sha256_file(unsigned char *out, const char *filename)
+{
+ git_hash_ctx ctx;
+ char buf[2048];
+ int fd, ret;
+ ssize_t read_len;
+
+ fd = p_open(filename, O_RDONLY);
+ cl_assert(fd >= 0);
+
+ cl_git_pass(git_hash_ctx_init(&ctx, GIT_HASH_ALGORITHM_SHA256));
+
+ while ((read_len = p_read(fd, buf, 2048)) > 0)
+ cl_git_pass(git_hash_update(&ctx, buf, (size_t)read_len));
+
+ cl_assert_equal_i(0, read_len);
+ p_close(fd);
+
+ ret = git_hash_final(out, &ctx);
+ git_hash_ctx_cleanup(&ctx);
+
+ return ret;
+}
+
+void test_sha256__empty(void)
+{
+ unsigned char expected[GIT_HASH_SHA256_SIZE] = {
+ 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14,
+ 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24,
+ 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c,
+ 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55
+ };
+ unsigned char actual[GIT_HASH_SHA256_SIZE];
+
+ cl_git_pass(sha256_file(actual, FIXTURE_DIR "/empty"));
+ cl_assert_equal_i(0, memcmp(expected, actual, GIT_HASH_SHA256_SIZE));
+}
+
+void test_sha256__hello(void)
+{
+ unsigned char expected[GIT_HASH_SHA256_SIZE] = {
+ 0xaa, 0x32, 0x7f, 0xae, 0x5c, 0x91, 0x58, 0x3a,
+ 0x4f, 0xb6, 0x54, 0xcc, 0xb6, 0xc2, 0xb1, 0x0c,
+ 0x77, 0xd7, 0x49, 0xc9, 0x91, 0x2a, 0x8d, 0x6b,
+ 0x47, 0x26, 0x13, 0xc0, 0xa0, 0x4b, 0x4d, 0xad
+ };
+ unsigned char actual[GIT_HASH_SHA256_SIZE];
+
+ cl_git_pass(sha256_file(actual, FIXTURE_DIR "/hello_c"));
+ cl_assert_equal_i(0, memcmp(expected, actual, GIT_HASH_SHA256_SIZE));
+}
+
+void test_sha256__pdf(void)
+{
+ unsigned char expected[GIT_HASH_SHA256_SIZE] = {
+ 0x2b, 0xb7, 0x87, 0xa7, 0x3e, 0x37, 0x35, 0x2f,
+ 0x92, 0x38, 0x3a, 0xbe, 0x7e, 0x29, 0x02, 0x93,
+ 0x6d, 0x10, 0x59, 0xad, 0x9f, 0x1b, 0xa6, 0xda,
+ 0xaa, 0x9c, 0x1e, 0x58, 0xee, 0x69, 0x70, 0xd0
+ };
+ unsigned char actual[GIT_HASH_SHA256_SIZE];
+
+ cl_git_pass(sha256_file(actual, FIXTURE_DIR "/shattered-1.pdf"));
+ cl_assert_equal_i(0, memcmp(expected, actual, GIT_HASH_SHA256_SIZE));
+}
+
+void test_sha256__win32_providers(void)
+{
+#ifdef GIT_SHA256_WIN32
+ unsigned char expected[GIT_HASH_SHA256_SIZE] = {
+ 0x2b, 0xb7, 0x87, 0xa7, 0x3e, 0x37, 0x35, 0x2f,
+ 0x92, 0x38, 0x3a, 0xbe, 0x7e, 0x29, 0x02, 0x93,
+ 0x6d, 0x10, 0x59, 0xad, 0x9f, 0x1b, 0xa6, 0xda,
+ 0xaa, 0x9c, 0x1e, 0x58, 0xee, 0x69, 0x70, 0xd0
+ };
+ unsigned char actual[GIT_HASH_SHA256_SIZE];
+
+ git_hash_win32_set_provider(GIT_HASH_WIN32_CRYPTOAPI);
+ cl_git_pass(sha256_file(actual, FIXTURE_DIR "/shattered-1.pdf"));
+ cl_assert_equal_i(0, memcmp(expected, actual, GIT_HASH_SHA256_SIZE));
+
+ git_hash_win32_set_provider(GIT_HASH_WIN32_CNG);
+ cl_git_pass(sha256_file(actual, FIXTURE_DIR "/shattered-1.pdf"));
+ cl_assert_equal_i(0, memcmp(expected, actual, GIT_HASH_SHA256_SIZE));
+#endif
+}
diff --git a/tests/util/sortedcache.c b/tests/util/sortedcache.c
new file mode 100644
index 0000000..72da7ae
--- /dev/null
+++ b/tests/util/sortedcache.c
@@ -0,0 +1,363 @@
+#include "clar_libgit2.h"
+#include "sortedcache.h"
+
+static int name_only_cmp(const void *a, const void *b)
+{
+ return strcmp(a, b);
+}
+
+void test_sortedcache__name_only(void)
+{
+ git_sortedcache *sc;
+ void *item;
+ size_t pos;
+
+ cl_git_pass(git_sortedcache_new(
+ &sc, 0, NULL, NULL, name_only_cmp, NULL));
+
+ cl_git_pass(git_sortedcache_wlock(sc));
+ cl_git_pass(git_sortedcache_upsert(&item, sc, "aaa"));
+ cl_git_pass(git_sortedcache_upsert(&item, sc, "bbb"));
+ cl_git_pass(git_sortedcache_upsert(&item, sc, "zzz"));
+ cl_git_pass(git_sortedcache_upsert(&item, sc, "mmm"));
+ cl_git_pass(git_sortedcache_upsert(&item, sc, "iii"));
+ git_sortedcache_wunlock(sc);
+
+ cl_assert_equal_sz(5, git_sortedcache_entrycount(sc));
+
+ cl_assert((item = git_sortedcache_lookup(sc, "aaa")) != NULL);
+ cl_assert_equal_s("aaa", item);
+ cl_assert((item = git_sortedcache_lookup(sc, "mmm")) != NULL);
+ cl_assert_equal_s("mmm", item);
+ cl_assert((item = git_sortedcache_lookup(sc, "zzz")) != NULL);
+ cl_assert_equal_s("zzz", item);
+ cl_assert(git_sortedcache_lookup(sc, "qqq") == NULL);
+
+ cl_assert((item = git_sortedcache_entry(sc, 0)) != NULL);
+ cl_assert_equal_s("aaa", item);
+ cl_assert((item = git_sortedcache_entry(sc, 1)) != NULL);
+ cl_assert_equal_s("bbb", item);
+ cl_assert((item = git_sortedcache_entry(sc, 2)) != NULL);
+ cl_assert_equal_s("iii", item);
+ cl_assert((item = git_sortedcache_entry(sc, 3)) != NULL);
+ cl_assert_equal_s("mmm", item);
+ cl_assert((item = git_sortedcache_entry(sc, 4)) != NULL);
+ cl_assert_equal_s("zzz", item);
+ cl_assert(git_sortedcache_entry(sc, 5) == NULL);
+
+ cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "aaa"));
+ cl_assert_equal_sz(0, pos);
+ cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "iii"));
+ cl_assert_equal_sz(2, pos);
+ cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "zzz"));
+ cl_assert_equal_sz(4, pos);
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "abc"));
+
+ cl_git_pass(git_sortedcache_clear(sc, true));
+
+ cl_assert_equal_sz(0, git_sortedcache_entrycount(sc));
+ cl_assert(git_sortedcache_entry(sc, 0) == NULL);
+ cl_assert(git_sortedcache_lookup(sc, "aaa") == NULL);
+ cl_assert(git_sortedcache_entry(sc, 0) == NULL);
+
+ git_sortedcache_free(sc);
+}
+
+typedef struct {
+ int value;
+ char smaller_value;
+ char path[GIT_FLEX_ARRAY];
+} sortedcache_test_struct;
+
+static int sortedcache_test_struct_cmp(const void *a_, const void *b_)
+{
+ const sortedcache_test_struct *a = a_, *b = b_;
+ return strcmp(a->path, b->path);
+}
+
+static void sortedcache_test_struct_free(void *payload, void *item_)
+{
+ sortedcache_test_struct *item = item_;
+ int *count = payload;
+ (*count)++;
+ item->smaller_value = 0;
+}
+
+void test_sortedcache__in_memory(void)
+{
+ git_sortedcache *sc;
+ sortedcache_test_struct *item;
+ int free_count = 0;
+
+ cl_git_pass(git_sortedcache_new(
+ &sc, offsetof(sortedcache_test_struct, path),
+ sortedcache_test_struct_free, &free_count,
+ sortedcache_test_struct_cmp, NULL));
+
+ cl_git_pass(git_sortedcache_wlock(sc));
+ cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "aaa"));
+ item->value = 10;
+ item->smaller_value = 1;
+ cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "bbb"));
+ item->value = 20;
+ item->smaller_value = 2;
+ cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "zzz"));
+ item->value = 30;
+ item->smaller_value = 26;
+ cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "mmm"));
+ item->value = 40;
+ item->smaller_value = 14;
+ cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "iii"));
+ item->value = 50;
+ item->smaller_value = 9;
+ git_sortedcache_wunlock(sc);
+
+ cl_assert_equal_sz(5, git_sortedcache_entrycount(sc));
+
+ cl_git_pass(git_sortedcache_rlock(sc));
+
+ cl_assert((item = git_sortedcache_lookup(sc, "aaa")) != NULL);
+ cl_assert_equal_s("aaa", item->path);
+ cl_assert_equal_i(10, item->value);
+ cl_assert((item = git_sortedcache_lookup(sc, "mmm")) != NULL);
+ cl_assert_equal_s("mmm", item->path);
+ cl_assert_equal_i(40, item->value);
+ cl_assert((item = git_sortedcache_lookup(sc, "zzz")) != NULL);
+ cl_assert_equal_s("zzz", item->path);
+ cl_assert_equal_i(30, item->value);
+ cl_assert(git_sortedcache_lookup(sc, "abc") == NULL);
+
+ /* not on Windows:
+ * cl_git_pass(git_sortedcache_rlock(sc)); -- grab more than one
+ */
+
+ cl_assert((item = git_sortedcache_entry(sc, 0)) != NULL);
+ cl_assert_equal_s("aaa", item->path);
+ cl_assert_equal_i(10, item->value);
+ cl_assert((item = git_sortedcache_entry(sc, 1)) != NULL);
+ cl_assert_equal_s("bbb", item->path);
+ cl_assert_equal_i(20, item->value);
+ cl_assert((item = git_sortedcache_entry(sc, 2)) != NULL);
+ cl_assert_equal_s("iii", item->path);
+ cl_assert_equal_i(50, item->value);
+ cl_assert((item = git_sortedcache_entry(sc, 3)) != NULL);
+ cl_assert_equal_s("mmm", item->path);
+ cl_assert_equal_i(40, item->value);
+ cl_assert((item = git_sortedcache_entry(sc, 4)) != NULL);
+ cl_assert_equal_s("zzz", item->path);
+ cl_assert_equal_i(30, item->value);
+ cl_assert(git_sortedcache_entry(sc, 5) == NULL);
+
+ git_sortedcache_runlock(sc);
+ /* git_sortedcache_runlock(sc); */
+
+ cl_assert_equal_i(0, free_count);
+
+ cl_git_pass(git_sortedcache_clear(sc, true));
+
+ cl_assert_equal_i(5, free_count);
+
+ cl_assert_equal_sz(0, git_sortedcache_entrycount(sc));
+ cl_assert(git_sortedcache_entry(sc, 0) == NULL);
+ cl_assert(git_sortedcache_lookup(sc, "aaa") == NULL);
+ cl_assert(git_sortedcache_entry(sc, 0) == NULL);
+
+ free_count = 0;
+
+ cl_git_pass(git_sortedcache_wlock(sc));
+ cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "testing"));
+ item->value = 10;
+ item->smaller_value = 3;
+ cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "again"));
+ item->value = 20;
+ item->smaller_value = 1;
+ cl_git_pass(git_sortedcache_upsert((void **)&item, sc, "final"));
+ item->value = 30;
+ item->smaller_value = 2;
+ git_sortedcache_wunlock(sc);
+
+ cl_assert_equal_sz(3, git_sortedcache_entrycount(sc));
+
+ cl_assert((item = git_sortedcache_lookup(sc, "testing")) != NULL);
+ cl_assert_equal_s("testing", item->path);
+ cl_assert_equal_i(10, item->value);
+ cl_assert((item = git_sortedcache_lookup(sc, "again")) != NULL);
+ cl_assert_equal_s("again", item->path);
+ cl_assert_equal_i(20, item->value);
+ cl_assert((item = git_sortedcache_lookup(sc, "final")) != NULL);
+ cl_assert_equal_s("final", item->path);
+ cl_assert_equal_i(30, item->value);
+ cl_assert(git_sortedcache_lookup(sc, "zzz") == NULL);
+
+ cl_assert((item = git_sortedcache_entry(sc, 0)) != NULL);
+ cl_assert_equal_s("again", item->path);
+ cl_assert_equal_i(20, item->value);
+ cl_assert((item = git_sortedcache_entry(sc, 1)) != NULL);
+ cl_assert_equal_s("final", item->path);
+ cl_assert_equal_i(30, item->value);
+ cl_assert((item = git_sortedcache_entry(sc, 2)) != NULL);
+ cl_assert_equal_s("testing", item->path);
+ cl_assert_equal_i(10, item->value);
+ cl_assert(git_sortedcache_entry(sc, 3) == NULL);
+
+ {
+ size_t pos;
+
+ cl_git_pass(git_sortedcache_wlock(sc));
+
+ cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "again"));
+ cl_assert_equal_sz(0, pos);
+ cl_git_pass(git_sortedcache_remove(sc, pos));
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "again"));
+
+ cl_assert_equal_sz(2, git_sortedcache_entrycount(sc));
+
+ cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "testing"));
+ cl_assert_equal_sz(1, pos);
+ cl_git_pass(git_sortedcache_remove(sc, pos));
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "testing"));
+
+ cl_assert_equal_sz(1, git_sortedcache_entrycount(sc));
+
+ cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "final"));
+ cl_assert_equal_sz(0, pos);
+ cl_git_pass(git_sortedcache_remove(sc, pos));
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "final"));
+
+ cl_assert_equal_sz(0, git_sortedcache_entrycount(sc));
+
+ git_sortedcache_wunlock(sc);
+ }
+
+ git_sortedcache_free(sc);
+
+ cl_assert_equal_i(3, free_count);
+}
+
+static void sortedcache_test_reload(git_sortedcache *sc)
+{
+ int count = 0;
+ git_str buf = GIT_STR_INIT;
+ char *scan, *after;
+ sortedcache_test_struct *item;
+
+ cl_assert(git_sortedcache_lockandload(sc, &buf) > 0);
+
+ cl_git_pass(git_sortedcache_clear(sc, false)); /* clear once we already have lock */
+
+ for (scan = buf.ptr; *scan; scan = after + 1) {
+ int val = strtol(scan, &after, 0);
+ cl_assert(after > scan);
+ scan = after;
+
+ for (scan = after; git__isspace(*scan); ++scan) /* find start */;
+ for (after = scan; *after && *after != '\n'; ++after) /* find eol */;
+ *after = '\0';
+
+ cl_git_pass(git_sortedcache_upsert((void **)&item, sc, scan));
+
+ item->value = val;
+ item->smaller_value = (char)(count++);
+ }
+
+ git_sortedcache_wunlock(sc);
+
+ git_str_dispose(&buf);
+}
+
+void test_sortedcache__on_disk(void)
+{
+ git_sortedcache *sc;
+ sortedcache_test_struct *item;
+ int free_count = 0;
+ size_t pos;
+
+ cl_git_mkfile("cacheitems.txt", "10 abc\n20 bcd\n30 cde\n");
+
+ cl_git_pass(git_sortedcache_new(
+ &sc, offsetof(sortedcache_test_struct, path),
+ sortedcache_test_struct_free, &free_count,
+ sortedcache_test_struct_cmp, "cacheitems.txt"));
+
+ /* should need to reload the first time */
+
+ sortedcache_test_reload(sc);
+
+ /* test what we loaded */
+
+ cl_assert_equal_sz(3, git_sortedcache_entrycount(sc));
+
+ cl_assert((item = git_sortedcache_lookup(sc, "abc")) != NULL);
+ cl_assert_equal_s("abc", item->path);
+ cl_assert_equal_i(10, item->value);
+ cl_assert((item = git_sortedcache_lookup(sc, "cde")) != NULL);
+ cl_assert_equal_s("cde", item->path);
+ cl_assert_equal_i(30, item->value);
+ cl_assert(git_sortedcache_lookup(sc, "aaa") == NULL);
+
+ cl_assert((item = git_sortedcache_entry(sc, 0)) != NULL);
+ cl_assert_equal_s("abc", item->path);
+ cl_assert_equal_i(10, item->value);
+ cl_assert((item = git_sortedcache_entry(sc, 1)) != NULL);
+ cl_assert_equal_s("bcd", item->path);
+ cl_assert_equal_i(20, item->value);
+ cl_assert(git_sortedcache_entry(sc, 3) == NULL);
+
+ /* should not need to reload this time */
+
+ cl_assert_equal_i(0, git_sortedcache_lockandload(sc, NULL));
+
+ /* rewrite ondisk file and reload */
+
+ cl_assert_equal_i(0, free_count);
+
+ cl_git_rewritefile(
+ "cacheitems.txt", "100 abc\n200 zzz\n500 aaa\n10 final\n");
+ sortedcache_test_reload(sc);
+
+ cl_assert_equal_i(3, free_count);
+
+ /* test what we loaded */
+
+ cl_assert_equal_sz(4, git_sortedcache_entrycount(sc));
+
+ cl_assert((item = git_sortedcache_lookup(sc, "abc")) != NULL);
+ cl_assert_equal_s("abc", item->path);
+ cl_assert_equal_i(100, item->value);
+ cl_assert((item = git_sortedcache_lookup(sc, "final")) != NULL);
+ cl_assert_equal_s("final", item->path);
+ cl_assert_equal_i(10, item->value);
+ cl_assert(git_sortedcache_lookup(sc, "cde") == NULL);
+
+ cl_assert((item = git_sortedcache_entry(sc, 0)) != NULL);
+ cl_assert_equal_s("aaa", item->path);
+ cl_assert_equal_i(500, item->value);
+ cl_assert((item = git_sortedcache_entry(sc, 2)) != NULL);
+ cl_assert_equal_s("final", item->path);
+ cl_assert_equal_i(10, item->value);
+ cl_assert((item = git_sortedcache_entry(sc, 3)) != NULL);
+ cl_assert_equal_s("zzz", item->path);
+ cl_assert_equal_i(200, item->value);
+
+ cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "aaa"));
+ cl_assert_equal_sz(0, pos);
+ cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "abc"));
+ cl_assert_equal_sz(1, pos);
+ cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "final"));
+ cl_assert_equal_sz(2, pos);
+ cl_git_pass(git_sortedcache_lookup_index(&pos, sc, "zzz"));
+ cl_assert_equal_sz(3, pos);
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "missing"));
+ cl_assert_equal_i(
+ GIT_ENOTFOUND, git_sortedcache_lookup_index(&pos, sc, "cde"));
+
+ git_sortedcache_free(sc);
+
+ cl_assert_equal_i(7, free_count);
+}
+
diff --git a/tests/util/stat.c b/tests/util/stat.c
new file mode 100644
index 0000000..84c23fb
--- /dev/null
+++ b/tests/util/stat.c
@@ -0,0 +1,113 @@
+#include "clar_libgit2.h"
+#include "futils.h"
+#include "posix.h"
+
+void test_stat__initialize(void)
+{
+ cl_git_pass(git_futils_mkdir("root/d1/d2", 0755, GIT_MKDIR_PATH));
+ cl_git_mkfile("root/file", "whatever\n");
+ cl_git_mkfile("root/d1/file", "whatever\n");
+}
+
+void test_stat__cleanup(void)
+{
+ git_futils_rmdir_r("root", NULL, GIT_RMDIR_REMOVE_FILES);
+}
+
+#define cl_assert_error(val) \
+ do { err = errno; cl_assert_equal_i((val), err); } while (0)
+
+void test_stat__0(void)
+{
+ struct stat st;
+ int err;
+
+ cl_assert_equal_i(0, p_lstat("root", &st));
+ cl_assert(S_ISDIR(st.st_mode));
+ cl_assert_error(0);
+
+ cl_assert_equal_i(0, p_lstat("root/", &st));
+ cl_assert(S_ISDIR(st.st_mode));
+ cl_assert_error(0);
+
+ cl_assert_equal_i(0, p_lstat("root/file", &st));
+ cl_assert(S_ISREG(st.st_mode));
+ cl_assert_error(0);
+
+ cl_assert_equal_i(0, p_lstat("root/d1", &st));
+ cl_assert(S_ISDIR(st.st_mode));
+ cl_assert_error(0);
+
+ cl_assert_equal_i(0, p_lstat("root/d1/", &st));
+ cl_assert(S_ISDIR(st.st_mode));
+ cl_assert_error(0);
+
+ cl_assert_equal_i(0, p_lstat("root/d1/file", &st));
+ cl_assert(S_ISREG(st.st_mode));
+ cl_assert_error(0);
+
+ cl_assert(p_lstat("root/missing", &st) < 0);
+ cl_assert_error(ENOENT);
+
+ cl_assert(p_lstat("root/missing/but/could/be/created", &st) < 0);
+ cl_assert_error(ENOENT);
+
+ cl_assert(p_lstat_posixly("root/missing/but/could/be/created", &st) < 0);
+ cl_assert_error(ENOENT);
+
+ cl_assert(p_lstat("root/d1/missing", &st) < 0);
+ cl_assert_error(ENOENT);
+
+ cl_assert(p_lstat("root/d1/missing/deeper/path", &st) < 0);
+ cl_assert_error(ENOENT);
+
+ cl_assert(p_lstat_posixly("root/d1/missing/deeper/path", &st) < 0);
+ cl_assert_error(ENOENT);
+
+ cl_assert(p_lstat_posixly("root/d1/file/deeper/path", &st) < 0);
+ cl_assert_error(ENOTDIR);
+
+ cl_assert(p_lstat("root/file/invalid", &st) < 0);
+#ifdef GIT_WIN32
+ cl_assert_error(ENOENT);
+#else
+ cl_assert_error(ENOTDIR);
+#endif
+
+ cl_assert(p_lstat_posixly("root/file/invalid", &st) < 0);
+ cl_assert_error(ENOTDIR);
+
+ cl_assert(p_lstat("root/file/invalid/deeper_path", &st) < 0);
+#ifdef GIT_WIN32
+ cl_assert_error(ENOENT);
+#else
+ cl_assert_error(ENOTDIR);
+#endif
+
+ cl_assert(p_lstat_posixly("root/file/invalid/deeper_path", &st) < 0);
+ cl_assert_error(ENOTDIR);
+
+ cl_assert(p_lstat_posixly("root/d1/file/extra", &st) < 0);
+ cl_assert_error(ENOTDIR);
+
+ cl_assert(p_lstat_posixly("root/d1/file/further/invalid/items", &st) < 0);
+ cl_assert_error(ENOTDIR);
+}
+
+void test_stat__root(void)
+{
+ const char *sandbox = clar_sandbox_path();
+ git_str root = GIT_STR_INIT;
+ int root_len;
+ struct stat st;
+
+ root_len = git_fs_path_root(sandbox);
+ cl_assert(root_len >= 0);
+
+ git_str_set(&root, sandbox, root_len+1);
+
+ cl_must_pass(p_stat(root.ptr, &st));
+ cl_assert(S_ISDIR(st.st_mode));
+
+ git_str_dispose(&root);
+}
diff --git a/tests/util/str/basic.c b/tests/util/str/basic.c
new file mode 100644
index 0000000..5d25568
--- /dev/null
+++ b/tests/util/str/basic.c
@@ -0,0 +1,50 @@
+#include "clar_libgit2.h"
+
+static const char *test_string = "Have you seen that? Have you seeeen that??";
+
+void test_str_basic__resize(void)
+{
+ git_str buf1 = GIT_STR_INIT;
+ git_str_puts(&buf1, test_string);
+ cl_assert(git_str_oom(&buf1) == 0);
+ cl_assert_equal_s(git_str_cstr(&buf1), test_string);
+
+ git_str_puts(&buf1, test_string);
+ cl_assert(strlen(git_str_cstr(&buf1)) == strlen(test_string) * 2);
+ git_str_dispose(&buf1);
+}
+
+void test_str_basic__resize_incremental(void)
+{
+ git_str buf1 = GIT_STR_INIT;
+
+ /* Presently, asking for 6 bytes will round up to 8. */
+ cl_git_pass(git_str_puts(&buf1, "Hello"));
+ cl_assert_equal_i(5, buf1.size);
+ cl_assert_equal_i(8, buf1.asize);
+
+ /* Ensure an additional byte does not realloc. */
+ cl_git_pass(git_str_grow_by(&buf1, 1));
+ cl_assert_equal_i(5, buf1.size);
+ cl_assert_equal_i(8, buf1.asize);
+
+ /* But requesting many does. */
+ cl_git_pass(git_str_grow_by(&buf1, 16));
+ cl_assert_equal_i(5, buf1.size);
+ cl_assert(buf1.asize > 8);
+
+ git_str_dispose(&buf1);
+}
+
+void test_str_basic__printf(void)
+{
+ git_str buf2 = GIT_STR_INIT;
+ git_str_printf(&buf2, "%s %s %d ", "shoop", "da", 23);
+ cl_assert(git_str_oom(&buf2) == 0);
+ cl_assert_equal_s(git_str_cstr(&buf2), "shoop da 23 ");
+
+ git_str_printf(&buf2, "%s %d", "woop", 42);
+ cl_assert(git_str_oom(&buf2) == 0);
+ cl_assert_equal_s(git_str_cstr(&buf2), "shoop da 23 woop 42");
+ git_str_dispose(&buf2);
+}
diff --git a/tests/util/str/oom.c b/tests/util/str/oom.c
new file mode 100644
index 0000000..810c1f2
--- /dev/null
+++ b/tests/util/str/oom.c
@@ -0,0 +1,71 @@
+#include "clar_libgit2.h"
+#include "clar_libgit2_alloc.h"
+
+/* Override default allocators with ones that will fail predictably. */
+
+static git_allocator std_alloc;
+static git_allocator oom_alloc;
+
+static void *oom_malloc(size_t n, const char *file, int line)
+{
+ /* Reject any allocation of more than 100 bytes */
+ return (n > 100) ? NULL : std_alloc.gmalloc(n, file, line);
+}
+
+static void *oom_realloc(void *p, size_t n, const char *file, int line)
+{
+ /* Reject any allocation of more than 100 bytes */
+ return (n > 100) ? NULL : std_alloc.grealloc(p, n, file, line);
+}
+
+void test_str_oom__initialize(void)
+{
+ git_stdalloc_init_allocator(&std_alloc);
+ git_stdalloc_init_allocator(&oom_alloc);
+
+ oom_alloc.gmalloc = oom_malloc;
+ oom_alloc.grealloc = oom_realloc;
+
+ cl_git_pass(git_allocator_setup(&oom_alloc));
+}
+
+void test_str_oom__cleanup(void)
+{
+ cl_git_pass(git_allocator_setup(NULL));
+}
+
+void test_str_oom__grow(void)
+{
+ git_str buf = GIT_STR_INIT;
+
+ cl_git_pass(git_str_grow(&buf, 42));
+ cl_assert(!git_str_oom(&buf));
+
+ cl_assert(git_str_grow(&buf, 101) == -1);
+ cl_assert(git_str_oom(&buf));
+
+ git_str_dispose(&buf);
+}
+
+void test_str_oom__grow_by(void)
+{
+ git_str buf = GIT_STR_INIT;
+
+ cl_git_pass(git_str_grow_by(&buf, 42));
+ cl_assert(!git_str_oom(&buf));
+
+ cl_assert(git_str_grow_by(&buf, 101) == -1);
+ cl_assert(git_str_oom(&buf));
+}
+
+void test_str_oom__allocation_failure(void)
+{
+ git_str buf = GIT_STR_INIT;
+
+ cl_alloc_limit(10);
+
+ cl_git_pass(git_str_puts(&buf, "foobar"));
+ cl_git_fail(git_str_puts(&buf, "foobar"));
+
+ cl_alloc_reset();
+}
diff --git a/tests/util/str/percent.c b/tests/util/str/percent.c
new file mode 100644
index 0000000..3393890
--- /dev/null
+++ b/tests/util/str/percent.c
@@ -0,0 +1,48 @@
+#include "clar_libgit2.h"
+
+static void expect_decode_pass(const char *expected, const char *encoded)
+{
+ git_str in = GIT_STR_INIT, out = GIT_STR_INIT;
+
+ /*
+ * ensure that we only read the given length of the input buffer
+ * by putting garbage at the end. this will ensure that we do
+ * not, eg, rely on nul-termination or walk off the end of the buf.
+ */
+ cl_git_pass(git_str_puts(&in, encoded));
+ cl_git_pass(git_str_PUTS(&in, "TRAILER"));
+
+ cl_git_pass(git_str_decode_percent(&out, in.ptr, strlen(encoded)));
+
+ cl_assert_equal_s(expected, git_str_cstr(&out));
+ cl_assert_equal_i(strlen(expected), git_str_len(&out));
+
+ git_str_dispose(&in);
+ git_str_dispose(&out);
+}
+
+void test_str_percent__decode_succeeds(void)
+{
+ expect_decode_pass("", "");
+ expect_decode_pass(" ", "%20");
+ expect_decode_pass("a", "a");
+ expect_decode_pass(" a", "%20a");
+ expect_decode_pass("a ", "a%20");
+ expect_decode_pass("github.com", "github.com");
+ expect_decode_pass("github.com", "githu%62.com");
+ expect_decode_pass("github.com", "github%2ecom");
+ expect_decode_pass("foo bar baz", "foo%20bar%20baz");
+ expect_decode_pass("foo bar baz", "foo%20bar%20baz");
+ expect_decode_pass("foo bar ", "foo%20bar%20");
+}
+
+void test_str_percent__ignores_invalid(void)
+{
+ expect_decode_pass("githu%%.com", "githu%%.com");
+ expect_decode_pass("github.co%2", "github.co%2");
+ expect_decode_pass("github%2.com", "github%2.com");
+ expect_decode_pass("githu%2z.com", "githu%2z.com");
+ expect_decode_pass("github.co%9z", "github.co%9z");
+ expect_decode_pass("github.co%2", "github.co%2");
+ expect_decode_pass("github.co%", "github.co%");
+}
diff --git a/tests/util/str/quote.c b/tests/util/str/quote.c
new file mode 100644
index 0000000..2c65462
--- /dev/null
+++ b/tests/util/str/quote.c
@@ -0,0 +1,87 @@
+#include "clar_libgit2.h"
+
+static void expect_quote_pass(const char *expected, const char *str)
+{
+ git_str buf = GIT_STR_INIT;
+
+ cl_git_pass(git_str_puts(&buf, str));
+ cl_git_pass(git_str_quote(&buf));
+
+ cl_assert_equal_s(expected, git_str_cstr(&buf));
+ cl_assert_equal_i(strlen(expected), git_str_len(&buf));
+
+ git_str_dispose(&buf);
+}
+
+void test_str_quote__quote_succeeds(void)
+{
+ expect_quote_pass("", "");
+ expect_quote_pass("foo", "foo");
+ expect_quote_pass("foo/bar/baz.c", "foo/bar/baz.c");
+ expect_quote_pass("foo bar", "foo bar");
+ expect_quote_pass("\"\\\"leading quote\"", "\"leading quote");
+ expect_quote_pass("\"slash\\\\y\"", "slash\\y");
+ expect_quote_pass("\"foo\\r\\nbar\"", "foo\r\nbar");
+ expect_quote_pass("\"foo\\177bar\"", "foo\177bar");
+ expect_quote_pass("\"foo\\001bar\"", "foo\001bar");
+ expect_quote_pass("\"foo\\377bar\"", "foo\377bar");
+}
+
+static void expect_unquote_pass(const char *expected, const char *quoted)
+{
+ git_str buf = GIT_STR_INIT;
+
+ cl_git_pass(git_str_puts(&buf, quoted));
+ cl_git_pass(git_str_unquote(&buf));
+
+ cl_assert_equal_s(expected, git_str_cstr(&buf));
+ cl_assert_equal_i(strlen(expected), git_str_len(&buf));
+
+ git_str_dispose(&buf);
+}
+
+static void expect_unquote_fail(const char *quoted)
+{
+ git_str buf = GIT_STR_INIT;
+
+ cl_git_pass(git_str_puts(&buf, quoted));
+ cl_git_fail(git_str_unquote(&buf));
+
+ git_str_dispose(&buf);
+}
+
+void test_str_quote__unquote_succeeds(void)
+{
+ expect_unquote_pass("", "\"\"");
+ expect_unquote_pass(" ", "\" \"");
+ expect_unquote_pass("foo", "\"foo\"");
+ expect_unquote_pass("foo bar", "\"foo bar\"");
+ expect_unquote_pass("foo\"bar", "\"foo\\\"bar\"");
+ expect_unquote_pass("foo\\bar", "\"foo\\\\bar\"");
+ expect_unquote_pass("foo\tbar", "\"foo\\tbar\"");
+ expect_unquote_pass("\vfoo\tbar\n", "\"\\vfoo\\tbar\\n\"");
+ expect_unquote_pass("foo\nbar", "\"foo\\012bar\"");
+ expect_unquote_pass("foo\r\nbar", "\"foo\\015\\012bar\"");
+ expect_unquote_pass("foo\r\nbar", "\"\\146\\157\\157\\015\\012\\142\\141\\162\"");
+ expect_unquote_pass("newline: \n", "\"newline: \\012\"");
+ expect_unquote_pass("0xff: \377", "\"0xff: \\377\"");
+}
+
+void test_str_quote__unquote_fails(void)
+{
+ expect_unquote_fail("no quotes at all");
+ expect_unquote_fail("\"no trailing quote");
+ expect_unquote_fail("no leading quote\"");
+ expect_unquote_fail("\"invalid \\z escape char\"");
+ expect_unquote_fail("\"\\q invalid escape char\"");
+ expect_unquote_fail("\"invalid escape char \\p\"");
+ expect_unquote_fail("\"invalid \\1 escape char \"");
+ expect_unquote_fail("\"invalid \\14 escape char \"");
+ expect_unquote_fail("\"invalid \\280 escape char\"");
+ expect_unquote_fail("\"invalid \\378 escape char\"");
+ expect_unquote_fail("\"invalid \\380 escape char\"");
+ expect_unquote_fail("\"invalid \\411 escape char\"");
+ expect_unquote_fail("\"truncated escape char \\\"");
+ expect_unquote_fail("\"truncated escape char \\0\"");
+ expect_unquote_fail("\"truncated escape char \\01\"");
+}
diff --git a/tests/util/str/splice.c b/tests/util/str/splice.c
new file mode 100644
index 0000000..14e844e
--- /dev/null
+++ b/tests/util/str/splice.c
@@ -0,0 +1,92 @@
+#include "clar_libgit2.h"
+
+static git_str _buf;
+
+void test_str_splice__initialize(void) {
+ git_str_init(&_buf, 16);
+}
+
+void test_str_splice__cleanup(void) {
+ git_str_dispose(&_buf);
+}
+
+void test_str_splice__preprend(void)
+{
+ git_str_sets(&_buf, "world!");
+
+ cl_git_pass(git_str_splice(&_buf, 0, 0, "Hello Dolly", strlen("Hello ")));
+
+ cl_assert_equal_s("Hello world!", git_str_cstr(&_buf));
+}
+
+void test_str_splice__append(void)
+{
+ git_str_sets(&_buf, "Hello");
+
+ cl_git_pass(git_str_splice(&_buf, git_str_len(&_buf), 0, " world!", strlen(" world!")));
+
+ cl_assert_equal_s("Hello world!", git_str_cstr(&_buf));
+}
+
+void test_str_splice__insert_at(void)
+{
+ git_str_sets(&_buf, "Hell world!");
+
+ cl_git_pass(git_str_splice(&_buf, strlen("Hell"), 0, "o", strlen("o")));
+
+ cl_assert_equal_s("Hello world!", git_str_cstr(&_buf));
+}
+
+void test_str_splice__remove_at(void)
+{
+ git_str_sets(&_buf, "Hello world of warcraft!");
+
+ cl_git_pass(git_str_splice(&_buf, strlen("Hello world"), strlen(" of warcraft"), "", 0));
+
+ cl_assert_equal_s("Hello world!", git_str_cstr(&_buf));
+}
+
+void test_str_splice__replace(void)
+{
+ git_str_sets(&_buf, "Hell0 w0rld!");
+
+ cl_git_pass(git_str_splice(&_buf, strlen("Hell"), strlen("0 w0"), "o wo", strlen("o wo")));
+
+ cl_assert_equal_s("Hello world!", git_str_cstr(&_buf));
+}
+
+void test_str_splice__replace_with_longer(void)
+{
+ git_str_sets(&_buf, "Hello you!");
+
+ cl_git_pass(git_str_splice(&_buf, strlen("Hello "), strlen("you"), "world", strlen("world")));
+
+ cl_assert_equal_s("Hello world!", git_str_cstr(&_buf));
+}
+
+void test_str_splice__replace_with_shorter(void)
+{
+ git_str_sets(&_buf, "Brave new world!");
+
+ cl_git_pass(git_str_splice(&_buf, 0, strlen("Brave new"), "Hello", strlen("Hello")));
+
+ cl_assert_equal_s("Hello world!", git_str_cstr(&_buf));
+}
+
+void test_str_splice__truncate(void)
+{
+ git_str_sets(&_buf, "Hello world!!");
+
+ cl_git_pass(git_str_splice(&_buf, strlen("Hello world!"), strlen("!"), "", 0));
+
+ cl_assert_equal_s("Hello world!", git_str_cstr(&_buf));
+}
+
+void test_str_splice__dont_do_anything(void)
+{
+ git_str_sets(&_buf, "Hello world!");
+
+ cl_git_pass(git_str_splice(&_buf, 3, 0, "Hello", 0));
+
+ cl_assert_equal_s("Hello world!", git_str_cstr(&_buf));
+}
diff --git a/tests/util/string.c b/tests/util/string.c
new file mode 100644
index 0000000..de04dea
--- /dev/null
+++ b/tests/util/string.c
@@ -0,0 +1,136 @@
+#include "clar_libgit2.h"
+
+/* compare prefixes */
+void test_string__0(void)
+{
+ cl_assert(git__prefixcmp("", "") == 0);
+ cl_assert(git__prefixcmp("a", "") == 0);
+ cl_assert(git__prefixcmp("", "a") < 0);
+ cl_assert(git__prefixcmp("a", "b") < 0);
+ cl_assert(git__prefixcmp("b", "a") > 0);
+ cl_assert(git__prefixcmp("ab", "a") == 0);
+ cl_assert(git__prefixcmp("ab", "ac") < 0);
+ cl_assert(git__prefixcmp("ab", "aa") > 0);
+}
+
+/* compare suffixes */
+void test_string__1(void)
+{
+ cl_assert(git__suffixcmp("", "") == 0);
+ cl_assert(git__suffixcmp("a", "") == 0);
+ cl_assert(git__suffixcmp("", "a") < 0);
+ cl_assert(git__suffixcmp("a", "b") < 0);
+ cl_assert(git__suffixcmp("b", "a") > 0);
+ cl_assert(git__suffixcmp("ba", "a") == 0);
+ cl_assert(git__suffixcmp("zaa", "ac") < 0);
+ cl_assert(git__suffixcmp("zaz", "ac") > 0);
+}
+
+/* compare icase sorting with case equality */
+void test_string__2(void)
+{
+ cl_assert(git__strcasesort_cmp("", "") == 0);
+ cl_assert(git__strcasesort_cmp("foo", "foo") == 0);
+ cl_assert(git__strcasesort_cmp("foo", "bar") > 0);
+ cl_assert(git__strcasesort_cmp("bar", "foo") < 0);
+ cl_assert(git__strcasesort_cmp("foo", "FOO") > 0);
+ cl_assert(git__strcasesort_cmp("FOO", "foo") < 0);
+ cl_assert(git__strcasesort_cmp("foo", "BAR") > 0);
+ cl_assert(git__strcasesort_cmp("BAR", "foo") < 0);
+ cl_assert(git__strcasesort_cmp("fooBar", "foobar") < 0);
+}
+
+/* compare prefixes with len */
+void test_string__prefixncmp(void)
+{
+ cl_assert(git__prefixncmp("", 0, "") == 0);
+ cl_assert(git__prefixncmp("a", 1, "") == 0);
+ cl_assert(git__prefixncmp("", 0, "a") < 0);
+ cl_assert(git__prefixncmp("a", 1, "b") < 0);
+ cl_assert(git__prefixncmp("b", 1, "a") > 0);
+ cl_assert(git__prefixncmp("ab", 2, "a") == 0);
+ cl_assert(git__prefixncmp("ab", 1, "a") == 0);
+ cl_assert(git__prefixncmp("ab", 2, "ac") < 0);
+ cl_assert(git__prefixncmp("a", 1, "ac") < 0);
+ cl_assert(git__prefixncmp("ab", 1, "ac") < 0);
+ cl_assert(git__prefixncmp("ab", 2, "aa") > 0);
+ cl_assert(git__prefixncmp("ab", 1, "aa") < 0);
+}
+
+/* compare prefixes with len */
+void test_string__prefixncmp_icase(void)
+{
+ cl_assert(git__prefixncmp_icase("", 0, "") == 0);
+ cl_assert(git__prefixncmp_icase("a", 1, "") == 0);
+ cl_assert(git__prefixncmp_icase("", 0, "a") < 0);
+ cl_assert(git__prefixncmp_icase("a", 1, "b") < 0);
+ cl_assert(git__prefixncmp_icase("A", 1, "b") < 0);
+ cl_assert(git__prefixncmp_icase("a", 1, "B") < 0);
+ cl_assert(git__prefixncmp_icase("b", 1, "a") > 0);
+ cl_assert(git__prefixncmp_icase("B", 1, "a") > 0);
+ cl_assert(git__prefixncmp_icase("b", 1, "A") > 0);
+ cl_assert(git__prefixncmp_icase("ab", 2, "a") == 0);
+ cl_assert(git__prefixncmp_icase("Ab", 2, "a") == 0);
+ cl_assert(git__prefixncmp_icase("ab", 2, "A") == 0);
+ cl_assert(git__prefixncmp_icase("ab", 1, "a") == 0);
+ cl_assert(git__prefixncmp_icase("ab", 2, "ac") < 0);
+ cl_assert(git__prefixncmp_icase("Ab", 2, "ac") < 0);
+ cl_assert(git__prefixncmp_icase("ab", 2, "Ac") < 0);
+ cl_assert(git__prefixncmp_icase("a", 1, "ac") < 0);
+ cl_assert(git__prefixncmp_icase("ab", 1, "ac") < 0);
+ cl_assert(git__prefixncmp_icase("ab", 2, "aa") > 0);
+ cl_assert(git__prefixncmp_icase("ab", 1, "aa") < 0);
+}
+
+void test_string__strcmp(void)
+{
+ cl_assert(git__strcmp("", "") == 0);
+ cl_assert(git__strcmp("foo", "foo") == 0);
+ cl_assert(git__strcmp("Foo", "foo") < 0);
+ cl_assert(git__strcmp("foo", "FOO") > 0);
+ cl_assert(git__strcmp("foo", "fOO") > 0);
+
+ cl_assert(strcmp("rt\303\202of", "rt dev\302\266h") > 0);
+ cl_assert(strcmp("e\342\202\254ghi=", "et") > 0);
+ cl_assert(strcmp("rt dev\302\266h", "rt\303\202of") < 0);
+ cl_assert(strcmp("et", "e\342\202\254ghi=") < 0);
+ cl_assert(strcmp("\303\215", "\303\255") < 0);
+
+ cl_assert(git__strcmp("rt\303\202of", "rt dev\302\266h") > 0);
+ cl_assert(git__strcmp("e\342\202\254ghi=", "et") > 0);
+ cl_assert(git__strcmp("rt dev\302\266h", "rt\303\202of") < 0);
+ cl_assert(git__strcmp("et", "e\342\202\254ghi=") < 0);
+ cl_assert(git__strcmp("\303\215", "\303\255") < 0);
+}
+
+void test_string__strcasecmp(void)
+{
+ cl_assert(git__strcasecmp("", "") == 0);
+ cl_assert(git__strcasecmp("foo", "foo") == 0);
+ cl_assert(git__strcasecmp("foo", "Foo") == 0);
+ cl_assert(git__strcasecmp("foo", "FOO") == 0);
+ cl_assert(git__strcasecmp("foo", "fOO") == 0);
+
+ cl_assert(strcasecmp("rt\303\202of", "rt dev\302\266h") > 0);
+ cl_assert(strcasecmp("e\342\202\254ghi=", "et") > 0);
+ cl_assert(strcasecmp("rt dev\302\266h", "rt\303\202of") < 0);
+ cl_assert(strcasecmp("et", "e\342\202\254ghi=") < 0);
+ cl_assert(strcasecmp("\303\215", "\303\255") < 0);
+
+ cl_assert(git__strcasecmp("rt\303\202of", "rt dev\302\266h") > 0);
+ cl_assert(git__strcasecmp("e\342\202\254ghi=", "et") > 0);
+ cl_assert(git__strcasecmp("rt dev\302\266h", "rt\303\202of") < 0);
+ cl_assert(git__strcasecmp("et", "e\342\202\254ghi=") < 0);
+ cl_assert(git__strcasecmp("\303\215", "\303\255") < 0);
+}
+
+void test_string__strlcmp(void)
+{
+ const char foo[3] = { 'f', 'o', 'o' };
+
+ cl_assert(git__strlcmp("foo", "foo", 3) == 0);
+ cl_assert(git__strlcmp("foo", foo, 3) == 0);
+ cl_assert(git__strlcmp("foo", "foobar", 3) == 0);
+ cl_assert(git__strlcmp("foobar", "foo", 3) > 0);
+ cl_assert(git__strlcmp("foo", "foobar", 6) < 0);
+}
diff --git a/tests/util/strmap.c b/tests/util/strmap.c
new file mode 100644
index 0000000..c4f5c86
--- /dev/null
+++ b/tests/util/strmap.c
@@ -0,0 +1,190 @@
+#include "clar_libgit2.h"
+#include "strmap.h"
+
+static git_strmap *g_table;
+
+void test_strmap__initialize(void)
+{
+ cl_git_pass(git_strmap_new(&g_table));
+ cl_assert(g_table != NULL);
+}
+
+void test_strmap__cleanup(void)
+{
+ git_strmap_free(g_table);
+}
+
+void test_strmap__0(void)
+{
+ cl_assert(git_strmap_size(g_table) == 0);
+}
+
+static void insert_strings(git_strmap *table, size_t count)
+{
+ size_t i, j, over;
+ char *str;
+
+ for (i = 0; i < count; ++i) {
+ str = malloc(10);
+ for (j = 0; j < 10; ++j)
+ str[j] = 'a' + (i % 26);
+ str[9] = '\0';
+
+ /* if > 26, then encode larger value in first letters */
+ for (j = 0, over = i / 26; over > 0; j++, over = over / 26)
+ str[j] = 'A' + (over % 26);
+
+ cl_git_pass(git_strmap_set(table, str, str));
+ }
+
+ cl_assert_equal_i(git_strmap_size(table), count);
+}
+
+void test_strmap__inserted_strings_can_be_retrieved(void)
+{
+ char *str;
+ int i;
+
+ insert_strings(g_table, 20);
+
+ cl_assert(git_strmap_exists(g_table, "aaaaaaaaa"));
+ cl_assert(git_strmap_exists(g_table, "ggggggggg"));
+ cl_assert(!git_strmap_exists(g_table, "aaaaaaaab"));
+ cl_assert(!git_strmap_exists(g_table, "abcdefghi"));
+
+ i = 0;
+ git_strmap_foreach_value(g_table, str, { i++; free(str); });
+ cl_assert(i == 20);
+}
+
+void test_strmap__deleted_entry_cannot_be_retrieved(void)
+{
+ char *str;
+ int i;
+
+ insert_strings(g_table, 20);
+
+ cl_assert(git_strmap_exists(g_table, "bbbbbbbbb"));
+ str = git_strmap_get(g_table, "bbbbbbbbb");
+ cl_assert_equal_s(str, "bbbbbbbbb");
+ cl_git_pass(git_strmap_delete(g_table, "bbbbbbbbb"));
+ free(str);
+
+ cl_assert(!git_strmap_exists(g_table, "bbbbbbbbb"));
+
+ i = 0;
+ git_strmap_foreach_value(g_table, str, { i++; free(str); });
+ cl_assert_equal_i(i, 19);
+}
+
+void test_strmap__inserting_many_keys_succeeds(void)
+{
+ char *str;
+ int i;
+
+ insert_strings(g_table, 10000);
+
+ i = 0;
+ git_strmap_foreach_value(g_table, str, { i++; free(str); });
+ cl_assert_equal_i(i, 10000);
+}
+
+void test_strmap__get_succeeds_with_existing_entries(void)
+{
+ const char *keys[] = { "foo", "bar", "gobble" };
+ char *values[] = { "oof", "rab", "elbbog" };
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(keys); i++)
+ cl_git_pass(git_strmap_set(g_table, keys[i], values[i]));
+
+ cl_assert_equal_s(git_strmap_get(g_table, "foo"), "oof");
+ cl_assert_equal_s(git_strmap_get(g_table, "bar"), "rab");
+ cl_assert_equal_s(git_strmap_get(g_table, "gobble"), "elbbog");
+}
+
+void test_strmap__get_returns_null_on_nonexisting_key(void)
+{
+ const char *keys[] = { "foo", "bar", "gobble" };
+ char *values[] = { "oof", "rab", "elbbog" };
+ size_t i;
+
+ for (i = 0; i < ARRAY_SIZE(keys); i++)
+ cl_git_pass(git_strmap_set(g_table, keys[i], values[i]));
+
+ cl_assert_equal_p(git_strmap_get(g_table, "other"), NULL);
+}
+
+void test_strmap__set_persists_key(void)
+{
+ cl_git_pass(git_strmap_set(g_table, "foo", "oof"));
+ cl_assert_equal_s(git_strmap_get(g_table, "foo"), "oof");
+}
+
+void test_strmap__set_persists_multpile_keys(void)
+{
+ cl_git_pass(git_strmap_set(g_table, "foo", "oof"));
+ cl_git_pass(git_strmap_set(g_table, "bar", "rab"));
+ cl_assert_equal_s(git_strmap_get(g_table, "foo"), "oof");
+ cl_assert_equal_s(git_strmap_get(g_table, "bar"), "rab");
+}
+
+void test_strmap__set_updates_existing_key(void)
+{
+ cl_git_pass(git_strmap_set(g_table, "foo", "oof"));
+ cl_git_pass(git_strmap_set(g_table, "bar", "rab"));
+ cl_git_pass(git_strmap_set(g_table, "gobble", "elbbog"));
+ cl_assert_equal_i(git_strmap_size(g_table), 3);
+
+ cl_git_pass(git_strmap_set(g_table, "foo", "other"));
+ cl_assert_equal_i(git_strmap_size(g_table), 3);
+
+ cl_assert_equal_s(git_strmap_get(g_table, "foo"), "other");
+}
+
+void test_strmap__iteration(void)
+{
+ struct {
+ char *key;
+ char *value;
+ int seen;
+ } entries[] = {
+ { "foo", "oof" },
+ { "bar", "rab" },
+ { "gobble", "elbbog" },
+ };
+ const char *key, *value;
+ size_t i, n;
+
+ for (i = 0; i < ARRAY_SIZE(entries); i++)
+ cl_git_pass(git_strmap_set(g_table, entries[i].key, entries[i].value));
+
+ i = 0, n = 0;
+ while (git_strmap_iterate((void **) &value, g_table, &i, &key) == 0) {
+ size_t j;
+
+ for (j = 0; j < ARRAY_SIZE(entries); j++) {
+ if (strcmp(entries[j].key, key))
+ continue;
+
+ cl_assert_equal_i(entries[j].seen, 0);
+ cl_assert_equal_s(entries[j].value, value);
+ entries[j].seen++;
+ break;
+ }
+
+ n++;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(entries); i++)
+ cl_assert_equal_i(entries[i].seen, 1);
+
+ cl_assert_equal_i(n, ARRAY_SIZE(entries));
+}
+
+void test_strmap__iterating_empty_map_stops_immediately(void)
+{
+ size_t i = 0;
+
+ cl_git_fail_with(git_strmap_iterate(NULL, g_table, &i, NULL), GIT_ITEROVER);
+}
diff --git a/tests/util/strtol.c b/tests/util/strtol.c
new file mode 100644
index 0000000..54c63ca
--- /dev/null
+++ b/tests/util/strtol.c
@@ -0,0 +1,128 @@
+#include "clar_libgit2.h"
+
+static void assert_l32_parses(const char *string, int32_t expected, int base)
+{
+ int32_t i;
+ cl_git_pass(git__strntol32(&i, string, strlen(string), NULL, base));
+ cl_assert_equal_i(i, expected);
+}
+
+static void assert_l32_fails(const char *string, int base)
+{
+ int32_t i;
+ cl_git_fail(git__strntol32(&i, string, strlen(string), NULL, base));
+}
+
+static void assert_l64_parses(const char *string, int64_t expected, int base)
+{
+ int64_t i;
+ cl_git_pass(git__strntol64(&i, string, strlen(string), NULL, base));
+ cl_assert_equal_i(i, expected);
+}
+
+static void assert_l64_fails(const char *string, int base)
+{
+ int64_t i;
+ cl_git_fail(git__strntol64(&i, string, strlen(string), NULL, base));
+}
+
+void test_strtol__int32(void)
+{
+ assert_l32_parses("123", 123, 10);
+ assert_l32_parses(" +123 ", 123, 10);
+ assert_l32_parses(" -123 ", -123, 10);
+ assert_l32_parses(" +2147483647 ", 2147483647, 10);
+ assert_l32_parses(" -2147483648 ", INT64_C(-2147483648), 10);
+ assert_l32_parses("A", 10, 16);
+ assert_l32_parses("1x1", 1, 10);
+
+ assert_l32_fails("", 10);
+ assert_l32_fails("a", 10);
+ assert_l32_fails("x10x", 10);
+ assert_l32_fails(" 2147483657 ", 10);
+ assert_l32_fails(" -2147483657 ", 10);
+}
+
+void test_strtol__int64(void)
+{
+ assert_l64_parses("123", 123, 10);
+ assert_l64_parses(" +123 ", 123, 10);
+ assert_l64_parses(" -123 ", -123, 10);
+ assert_l64_parses(" +2147483647 ", 2147483647, 10);
+ assert_l64_parses(" -2147483648 ", INT64_C(-2147483648), 10);
+ assert_l64_parses(" 2147483657 ", INT64_C(2147483657), 10);
+ assert_l64_parses(" -2147483657 ", INT64_C(-2147483657), 10);
+ assert_l64_parses(" 9223372036854775807 ", INT64_MAX, 10);
+ assert_l64_parses(" -9223372036854775808 ", INT64_MIN, 10);
+ assert_l64_parses(" 0x7fffffffffffffff ", INT64_MAX, 16);
+ assert_l64_parses(" -0x8000000000000000 ", INT64_MIN, 16);
+ assert_l64_parses("1a", 26, 16);
+ assert_l64_parses("1A", 26, 16);
+
+ assert_l64_fails("", 10);
+ assert_l64_fails("a", 10);
+ assert_l64_fails("x10x", 10);
+ assert_l64_fails("0x8000000000000000", 16);
+ assert_l64_fails("-0x8000000000000001", 16);
+}
+
+void test_strtol__base_autodetection(void)
+{
+ assert_l64_parses("0", 0, 0);
+ assert_l64_parses("00", 0, 0);
+ assert_l64_parses("0x", 0, 0);
+ assert_l64_parses("0foobar", 0, 0);
+ assert_l64_parses("07", 7, 0);
+ assert_l64_parses("017", 15, 0);
+ assert_l64_parses("0x8", 8, 0);
+ assert_l64_parses("0x18", 24, 0);
+}
+
+void test_strtol__buffer_length_with_autodetection_truncates(void)
+{
+ int64_t i64;
+
+ cl_git_pass(git__strntol64(&i64, "011", 2, NULL, 0));
+ cl_assert_equal_i(i64, 1);
+ cl_git_pass(git__strntol64(&i64, "0x11", 3, NULL, 0));
+ cl_assert_equal_i(i64, 1);
+}
+
+void test_strtol__buffer_length_truncates(void)
+{
+ int32_t i32;
+ int64_t i64;
+
+ cl_git_pass(git__strntol32(&i32, "11", 1, NULL, 10));
+ cl_assert_equal_i(i32, 1);
+
+ cl_git_pass(git__strntol64(&i64, "11", 1, NULL, 10));
+ cl_assert_equal_i(i64, 1);
+}
+
+void test_strtol__buffer_length_with_leading_ws_truncates(void)
+{
+ int64_t i64;
+
+ cl_git_fail(git__strntol64(&i64, " 1", 1, NULL, 10));
+
+ cl_git_pass(git__strntol64(&i64, " 11", 2, NULL, 10));
+ cl_assert_equal_i(i64, 1);
+}
+
+void test_strtol__buffer_length_with_leading_sign_truncates(void)
+{
+ int64_t i64;
+
+ cl_git_fail(git__strntol64(&i64, "-1", 1, NULL, 10));
+
+ cl_git_pass(git__strntol64(&i64, "-11", 2, NULL, 10));
+ cl_assert_equal_i(i64, -1);
+}
+
+void test_strtol__error_message_cuts_off(void)
+{
+ assert_l32_fails("2147483657foobar", 10);
+ cl_assert(strstr(git_error_last()->message, "2147483657") != NULL);
+ cl_assert(strstr(git_error_last()->message, "foobar") == NULL);
+}
diff --git a/tests/util/url/http.c b/tests/util/url/http.c
new file mode 100644
index 0000000..8823889
--- /dev/null
+++ b/tests/util/url/http.c
@@ -0,0 +1,752 @@
+#include "clar_libgit2.h"
+#include "net.h"
+
+static git_net_url conndata;
+
+void test_url_http__initialize(void)
+{
+ memset(&conndata, 0, sizeof(conndata));
+}
+
+void test_url_http__cleanup(void)
+{
+ git_net_url_dispose(&conndata);
+}
+
+/* Hostname */
+
+void test_url_http__has_scheme(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata, "http://example.com/resource"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_p(conndata.query, NULL);
+ cl_assert_equal_p(conndata.fragment, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_http__no_scheme(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata, "example.com/resource"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_p(conndata.query, NULL);
+ cl_assert_equal_p(conndata.fragment, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_http__hostname_root(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata, "example.com/"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_p(conndata.query, NULL);
+ cl_assert_equal_p(conndata.fragment, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_http__hostname_implied_root(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata, "example.com"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_p(conndata.query, NULL);
+ cl_assert_equal_p(conndata.fragment, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_http__hostname_numeric(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata, "8888888/"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "8888888");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_p(conndata.query, NULL);
+ cl_assert_equal_p(conndata.fragment, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_http__hostname_implied_root_custom_port(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata, "example.com:42"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "42");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_p(conndata.query, NULL);
+ cl_assert_equal_p(conndata.fragment, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_http__hostname_implied_root_empty_port(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata, "example.com:"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_p(conndata.query, NULL);
+ cl_assert_equal_p(conndata.fragment, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_http__hostname_encoded_password(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata,
+ "user:pass%2fis%40bad@hostname.com:1234/"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "hostname.com");
+ cl_assert_equal_s(conndata.port, "1234");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass/is@bad");
+ cl_assert_equal_p(conndata.query, NULL);
+ cl_assert_equal_p(conndata.fragment, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_http__hostname_user(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata,
+ "user@example.com/resource"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_p(conndata.query, NULL);
+ cl_assert_equal_p(conndata.fragment, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_http__hostname_user_pass(void)
+{
+ /* user:pass@hostname.tld/resource */
+ cl_git_pass(git_net_url_parse_http(&conndata,
+ "user:pass@example.com/resource"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass");
+ cl_assert_equal_p(conndata.query, NULL);
+ cl_assert_equal_p(conndata.fragment, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_http__hostname_port(void)
+{
+ /* hostname.tld:port/resource */
+ cl_git_pass(git_net_url_parse_http(&conndata,
+ "example.com:9191/resource"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_p(conndata.query, NULL);
+ cl_assert_equal_p(conndata.fragment, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_http__hostname_empty_port(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata, "example.com:/resource"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_p(conndata.query, NULL);
+ cl_assert_equal_p(conndata.fragment, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_http__hostname_user_port(void)
+{
+ /* user@hostname.tld:port/resource */
+ cl_git_pass(git_net_url_parse_http(&conndata,
+ "user@example.com:9191/resource"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_p(conndata.query, NULL);
+ cl_assert_equal_p(conndata.fragment, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_http__hostname_user_pass_port(void)
+{
+ /* user:pass@hostname.tld:port/resource */
+ cl_git_pass(git_net_url_parse_http(&conndata,
+ "user:pass@example.com:9191/resource"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass");
+ cl_assert_equal_p(conndata.query, NULL);
+ cl_assert_equal_p(conndata.fragment, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_http__hostname_user_pass_port_query(void)
+{
+ /* user:pass@hostname.tld:port/resource */
+ cl_git_pass(git_net_url_parse_http(&conndata,
+ "user:pass@example.com:9191/resource?query=q&foo=bar&z=asdf"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass");
+ cl_assert_equal_s(conndata.query, "query=q&foo=bar&z=asdf");
+ cl_assert_equal_p(conndata.fragment, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_http__hostname_user_pass_port_fragment(void)
+{
+ /* user:pass@hostname.tld:port/resource */
+ cl_git_pass(git_net_url_parse_http(&conndata,
+ "user:pass@example.com:9191/resource#fragment"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass");
+ cl_assert_equal_p(conndata.query, NULL);
+ cl_assert_equal_s(conndata.fragment, "fragment");
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_http__hostname_user_pass_port_query_fragment(void)
+{
+ /* user:pass@hostname.tld:port/resource */
+ cl_git_pass(git_net_url_parse_http(&conndata,
+ "user:pass@example.com:9191/resource?query=q&foo=bar&z=asdf#fragment"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass");
+ cl_assert_equal_s(conndata.query, "query=q&foo=bar&z=asdf");
+ cl_assert_equal_s(conndata.fragment, "fragment");
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_http__fragment_with_question_mark(void)
+{
+ /* user:pass@hostname.tld:port/resource */
+ cl_git_pass(git_net_url_parse_http(&conndata,
+ "user:pass@example.com:9191/resource#fragment_with?question_mark"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass");
+ cl_assert_equal_p(conndata.query, NULL);
+ cl_assert_equal_s(conndata.fragment, "fragment_with?question_mark");
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+/* IPv4 addresses */
+
+void test_url_http__ipv4_trivial(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata, "192.168.1.1/resource"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_http__ipv4_root(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata, "192.168.1.1/"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_http__ipv4_implied_root(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata, "192.168.1.1"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_http__ipv4_implied_root_custom_port(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata, "192.168.1.1:42"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "42");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_http__ipv4_implied_root_empty_port(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata, "192.168.1.1:"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_http__ipv4_encoded_password(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata,
+ "user:pass%2fis%40bad@192.168.1.1:1234/"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "1234");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass/is@bad");
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_http__ipv4_user(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata,
+ "user@192.168.1.1/resource"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_http__ipv4_user_pass(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata,
+ "user:pass@192.168.1.1/resource"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass");
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_http__ipv4_port(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata,
+ "192.168.1.1:9191/resource"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_http__ipv4_empty_port(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata, "192.168.1.1:/resource"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_http__ipv4_user_port(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata,
+ "user@192.168.1.1:9191/resource"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_http__ipv4_user_pass_port(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata,
+ "user:pass@192.168.1.1:9191/resource"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass");
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+/* IPv6 addresses */
+
+void test_url_http__ipv6_trivial(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata, "[fe80::dcad:beff:fe00:0001]/resource"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_http__ipv6_root(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata, "[fe80::dcad:beff:fe00:0001]/"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_http__ipv6_implied_root(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata, "[fe80::dcad:beff:fe00:0001]"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_http__ipv6_implied_root_custom_port(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata, "[fe80::dcad:beff:fe00:0001]:42"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "42");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_http__ipv6_implied_root_empty_port(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata, "[fe80::dcad:beff:fe00:0001]:"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_http__ipv6_encoded_password(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata,
+ "user:pass%2fis%40bad@[fe80::dcad:beff:fe00:0001]:1234/"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "1234");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass/is@bad");
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_http__ipv6_user(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata,
+ "user@[fe80::dcad:beff:fe00:0001]/resource"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_http__ipv6_user_pass(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata,
+ "user:pass@[fe80::dcad:beff:fe00:0001]/resource"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass");
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_http__ipv6_port(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata,
+ "[fe80::dcad:beff:fe00:0001]:9191/resource"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_http__ipv6_empty_port(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata, "[fe80::dcad:beff:fe00:0001]:/resource"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_http__ipv6_user_port(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata,
+ "user@[fe80::dcad:beff:fe00:0001]:9191/resource"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_http__ipv6_user_pass_port(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata,
+ "user:pass@[fe80::dcad:beff:fe00:0001]:9191/resource"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass");
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_http__ipv6_invalid_addresses(void)
+{
+ /* Opening bracket missing */
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "fe80::dcad:beff:fe00:0001]/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "fe80::dcad:beff:fe00:0001]/"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "fe80::dcad:beff:fe00:0001]"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "fe80::dcad:beff:fe00:0001]:42"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "fe80::dcad:beff:fe00:0001]:"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "user:pass%2fis%40bad@fe80::dcad:beff:fe00:0001]:1234/"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "user@fe80::dcad:beff:fe00:0001]/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "user:pass@fe80::dcad:beff:fe00:0001]/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "fe80::dcad:beff:fe00:0001]:9191/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "fe80::dcad:beff:fe00:0001]:/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "user@fe80::dcad:beff:fe00:0001]:9191/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "user:pass@fe80::dcad:beff:fe00:0001]:9191/resource"));
+
+ /* Closing bracket missing */
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "[fe80::dcad:beff:fe00:0001/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "[fe80::dcad:beff:fe00:0001/"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "[fe80::dcad:beff:fe00:0001"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "[fe80::dcad:beff:fe00:0001:42"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "[fe80::dcad:beff:fe00:0001:"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "user:pass%2fis%40bad@[fe80::dcad:beff:fe00:0001:1234/"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "user@[fe80::dcad:beff:fe00:0001/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "user:pass@[fe80::dcad:beff:fe00:0001/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "[fe80::dcad:beff:fe00:0001:9191/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "[fe80::dcad:beff:fe00:0001:/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "user@[fe80::dcad:beff:fe00:0001:9191/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "user:pass@[fe80::dcad:beff:fe00:0001:9191/resource"));
+
+ /* Both brackets missing */
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "fe80::dcad:beff:fe00:0001/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "fe80::dcad:beff:fe00:0001/"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "fe80::dcad:beff:fe00:0001"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "fe80::dcad:beff:fe00:0001:42"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "fe80::dcad:beff:fe00:0001:"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "user:pass%2fis%40bad@fe80::dcad:beff:fe00:0001:1234/"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "user@fe80::dcad:beff:fe00:0001/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "user:pass@fe80::dcad:beff:fe00:0001/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "fe80::dcad:beff:fe00:0001:9191/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "fe80::dcad:beff:fe00:0001:/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "user@fe80::dcad:beff:fe00:0001:9191/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "user:pass@fe80::dcad:beff:fe00:0001:9191/resource"));
+
+ /* Invalid character inside address */
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata, "[fe8o::dcad:beff:fe00:0001]/resource"));
+
+ /* Characters before/after braces */
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "fe80::[dcad:beff:fe00:0001]/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "cafe[fe80::dcad:beff:fe00:0001]/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_http(&conndata,
+ "[fe80::dcad:beff:fe00:0001]cafe/resource"));
+}
+
+/* Oddities */
+
+void test_url_http__invalid_scheme_is_relative(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata, "foo!bar://host:42/path/to/project?query_string=yes"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "foo!bar");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "//host:42/path/to/project");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_s(conndata.query, "query_string=yes");
+ cl_assert_equal_p(conndata.fragment, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_http__scheme_case_is_normalized(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata, "GIT+SSH://host:42/path/to/project"));
+ cl_assert_equal_s(conndata.scheme, "git+ssh");
+}
+
+void test_url_http__no_scheme_relative_path(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata, "path"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "path");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_http__no_scheme_absolute_path(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata, "/path"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_p(conndata.host, NULL);
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/path");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_http__empty_path_with_empty_authority(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata, ""));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_p(conndata.host, NULL);
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_http__spaces_in_the_name(void)
+{
+ cl_git_pass(git_net_url_parse_http(&conndata, "libgit2@dev.azure.com/libgit2/test/_git/spaces%20in%20the%20name"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "dev.azure.com");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/libgit2/test/_git/spaces%20in%20the%20name");
+ cl_assert_equal_s(conndata.username, "libgit2");
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
diff --git a/tests/util/url/joinpath.c b/tests/util/url/joinpath.c
new file mode 100644
index 0000000..6027093
--- /dev/null
+++ b/tests/util/url/joinpath.c
@@ -0,0 +1,193 @@
+#include "clar_libgit2.h"
+#include "net.h"
+
+static git_net_url source, target;
+
+void test_url_joinpath__initialize(void)
+{
+ memset(&source, 0, sizeof(source));
+ memset(&target, 0, sizeof(target));
+}
+
+void test_url_joinpath__cleanup(void)
+{
+ git_net_url_dispose(&source);
+ git_net_url_dispose(&target);
+}
+
+void test_url_joinpath__target_paths_and_queries(void)
+{
+ cl_git_pass(git_net_url_parse(&source, "http://example.com/a/b"));
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/c/d"));
+ cl_assert_equal_s(target.path, "/a/b/c/d");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/c/d?foo"));
+ cl_assert_equal_s(target.path, "/a/b/c/d");
+ cl_assert_equal_s(target.query, "foo");
+ git_net_url_dispose(&target);
+}
+
+void test_url_joinpath__source_query_removed(void)
+{
+ cl_git_pass(git_net_url_parse(&source, "http://example.com/a/b?query&one&two"));
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/c/d"));
+ cl_assert_equal_s(target.path, "/a/b/c/d");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/c/d?foo"));
+ cl_assert_equal_s(target.path, "/a/b/c/d");
+ cl_assert_equal_s(target.query, "foo");
+ git_net_url_dispose(&target);
+}
+
+void test_url_joinpath__source_lacks_path(void)
+{
+ cl_git_pass(git_net_url_parse(&source, "http://example.com"));
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/"));
+ cl_assert_equal_s(target.path, "/");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, ""));
+ cl_assert_equal_s(target.path, "/");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "asdf"));
+ cl_assert_equal_s(target.path, "/asdf");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/asdf"));
+ cl_assert_equal_s(target.path, "/asdf");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/foo/bar"));
+ cl_assert_equal_s(target.path, "/foo/bar");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "asdf?hello"));
+ cl_assert_equal_s(target.path, "/asdf");
+ cl_assert_equal_s(target.query, "hello");
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/asdf?hello"));
+ cl_assert_equal_s(target.path, "/asdf");
+ cl_assert_equal_s(target.query, "hello");
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/foo/bar?hello"));
+ cl_assert_equal_s(target.path, "/foo/bar");
+ cl_assert_equal_s(target.query, "hello");
+ git_net_url_dispose(&target);
+}
+
+void test_url_joinpath__source_is_slash(void)
+{
+ cl_git_pass(git_net_url_parse(&source, "http://example.com/"));
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/"));
+ cl_assert_equal_s(target.path, "/");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, ""));
+ cl_assert_equal_s(target.path, "/");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "asdf"));
+ cl_assert_equal_s(target.path, "/asdf");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/asdf"));
+ cl_assert_equal_s(target.path, "/asdf");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/foo/bar"));
+ cl_assert_equal_s(target.path, "/foo/bar");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "asdf?hello"));
+ cl_assert_equal_s(target.path, "/asdf");
+ cl_assert_equal_s(target.query, "hello");
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/asdf?hello"));
+ cl_assert_equal_s(target.path, "/asdf");
+ cl_assert_equal_s(target.query, "hello");
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/foo/bar?hello"));
+ cl_assert_equal_s(target.path, "/foo/bar");
+ cl_assert_equal_s(target.query, "hello");
+ git_net_url_dispose(&target);
+}
+
+
+void test_url_joinpath__source_has_query(void)
+{
+ cl_git_pass(git_net_url_parse(&source, "http://example.com?query"));
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/"));
+ cl_assert_equal_s(target.path, "/");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, ""));
+ cl_assert_equal_s(target.path, "/");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "asdf"));
+ cl_assert_equal_s(target.path, "/asdf");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/asdf"));
+ cl_assert_equal_s(target.path, "/asdf");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/foo/bar"));
+ cl_assert_equal_s(target.path, "/foo/bar");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "asdf?hello"));
+ cl_assert_equal_s(target.path, "/asdf");
+ cl_assert_equal_s(target.query, "hello");
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/asdf?hello"));
+ cl_assert_equal_s(target.path, "/asdf");
+ cl_assert_equal_s(target.query, "hello");
+ git_net_url_dispose(&target);
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/foo/bar?hello"));
+ cl_assert_equal_s(target.path, "/foo/bar");
+ cl_assert_equal_s(target.query, "hello");
+ git_net_url_dispose(&target);
+}
+
+
+void test_url_joinpath__empty_query_ignored(void)
+{
+ cl_git_pass(git_net_url_parse(&source, "http://example.com/foo"));
+
+ cl_git_pass(git_net_url_joinpath(&target, &source, "/bar/baz?"));
+ cl_assert_equal_s(target.path, "/foo/bar/baz");
+ cl_assert_equal_p(target.query, NULL);
+ git_net_url_dispose(&target);
+}
diff --git a/tests/util/url/parse.c b/tests/util/url/parse.c
new file mode 100644
index 0000000..35486f7
--- /dev/null
+++ b/tests/util/url/parse.c
@@ -0,0 +1,805 @@
+#include "clar_libgit2.h"
+#include "net.h"
+
+static git_net_url conndata;
+
+void test_url_parse__initialize(void)
+{
+ memset(&conndata, 0, sizeof(conndata));
+}
+
+void test_url_parse__cleanup(void)
+{
+ git_net_url_dispose(&conndata);
+}
+
+/* Hostname */
+
+void test_url_parse__hostname_trivial(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://example.com/resource"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_p(conndata.query, NULL);
+ cl_assert_equal_p(conndata.fragment, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_parse__hostname_root(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://example.com/"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_p(conndata.query, NULL);
+ cl_assert_equal_p(conndata.fragment, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_parse__hostname_implied_root(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://example.com"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_p(conndata.query, NULL);
+ cl_assert_equal_p(conndata.fragment, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_parse__hostname_numeric(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://8888888/"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "8888888");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_p(conndata.query, NULL);
+ cl_assert_equal_p(conndata.fragment, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_parse__hostname_implied_root_custom_port(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://example.com:42"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "42");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_p(conndata.query, NULL);
+ cl_assert_equal_p(conndata.fragment, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_parse__hostname_implied_root_empty_port(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://example.com:"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_p(conndata.query, NULL);
+ cl_assert_equal_p(conndata.fragment, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_parse__hostname_encoded_password(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user:pass%2fis%40bad@hostname.com:1234/"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "hostname.com");
+ cl_assert_equal_s(conndata.port, "1234");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass/is@bad");
+ cl_assert_equal_p(conndata.query, NULL);
+ cl_assert_equal_p(conndata.fragment, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_parse__hostname_user(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user@example.com/resource"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "443");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_p(conndata.query, NULL);
+ cl_assert_equal_p(conndata.fragment, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_parse__hostname_user_pass(void)
+{
+ /* user:pass@hostname.tld/resource */
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user:pass@example.com/resource"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "443");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass");
+ cl_assert_equal_p(conndata.query, NULL);
+ cl_assert_equal_p(conndata.fragment, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_parse__hostname_port(void)
+{
+ /* hostname.tld:port/resource */
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://example.com:9191/resource"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_p(conndata.query, NULL);
+ cl_assert_equal_p(conndata.fragment, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_parse__hostname_empty_port(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://example.com:/resource"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_p(conndata.query, NULL);
+ cl_assert_equal_p(conndata.fragment, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_parse__hostname_user_port(void)
+{
+ /* user@hostname.tld:port/resource */
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user@example.com:9191/resource"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_p(conndata.query, NULL);
+ cl_assert_equal_p(conndata.fragment, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_parse__hostname_user_pass_port(void)
+{
+ /* user:pass@hostname.tld:port/resource */
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user:pass@example.com:9191/resource"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass");
+ cl_assert_equal_p(conndata.query, NULL);
+ cl_assert_equal_p(conndata.fragment, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_parse__hostname_user_pass_port_query(void)
+{
+ /* user:pass@hostname.tld:port/resource */
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user:pass@example.com:9191/resource?query=q&foo=bar&z=asdf"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass");
+ cl_assert_equal_s(conndata.query, "query=q&foo=bar&z=asdf");
+ cl_assert_equal_p(conndata.fragment, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_parse__hostname_user_pass_port_fragment(void)
+{
+ /* user:pass@hostname.tld:port/resource */
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user:pass@example.com:9191/resource#fragment"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass");
+ cl_assert_equal_p(conndata.query, NULL);
+ cl_assert_equal_s(conndata.fragment, "fragment");
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_parse__hostname_user_pass_port_query_fragment(void)
+{
+ /* user:pass@hostname.tld:port/resource */
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user:pass@example.com:9191/resource?query=q&foo=bar&z=asdf#fragment"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass");
+ cl_assert_equal_s(conndata.query, "query=q&foo=bar&z=asdf");
+ cl_assert_equal_s(conndata.fragment, "fragment");
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_parse__fragment_with_question_mark(void)
+{
+ /* user:pass@hostname.tld:port/resource */
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user:pass@example.com:9191/resource#fragment_with?question_mark"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass");
+ cl_assert_equal_p(conndata.query, NULL);
+ cl_assert_equal_s(conndata.fragment, "fragment_with?question_mark");
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+/* IPv4 addresses */
+
+void test_url_parse__ipv4_trivial(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://192.168.1.1/resource"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_parse__ipv4_root(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://192.168.1.1/"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_parse__ipv4_implied_root(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://192.168.1.1"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_parse__ipv4_implied_root_custom_port(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://192.168.1.1:42"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "42");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_parse__ipv4_implied_root_empty_port(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://192.168.1.1:"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_parse__ipv4_encoded_password(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user:pass%2fis%40bad@192.168.1.1:1234/"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "1234");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass/is@bad");
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_parse__ipv4_user(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user@192.168.1.1/resource"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "443");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_parse__ipv4_user_pass(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user:pass@192.168.1.1/resource"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "443");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass");
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_parse__ipv4_port(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://192.168.1.1:9191/resource"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_parse__ipv4_empty_port(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://192.168.1.1:/resource"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_parse__ipv4_user_port(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user@192.168.1.1:9191/resource"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_parse__ipv4_user_pass_port(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user:pass@192.168.1.1:9191/resource"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "192.168.1.1");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass");
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+/* IPv6 addresses */
+
+void test_url_parse__ipv6_trivial(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://[fe80::dcad:beff:fe00:0001]/resource"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_parse__ipv6_root(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://[fe80::dcad:beff:fe00:0001]/"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_parse__ipv6_implied_root(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://[fe80::dcad:beff:fe00:0001]"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_parse__ipv6_implied_root_custom_port(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://[fe80::dcad:beff:fe00:0001]:42"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "42");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_parse__ipv6_implied_root_empty_port(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://[fe80::dcad:beff:fe00:0001]:"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_parse__ipv6_encoded_password(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user:pass%2fis%40bad@[fe80::dcad:beff:fe00:0001]:1234/"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "1234");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass/is@bad");
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_parse__ipv6_user(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user@[fe80::dcad:beff:fe00:0001]/resource"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "443");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_parse__ipv6_user_pass(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user:pass@[fe80::dcad:beff:fe00:0001]/resource"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "443");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass");
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_parse__ipv6_port(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://[fe80::dcad:beff:fe00:0001]:9191/resource"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_parse__ipv6_empty_port(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://[fe80::dcad:beff:fe00:0001]:/resource"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_parse__ipv6_user_port(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user@[fe80::dcad:beff:fe00:0001]:9191/resource"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_parse__ipv6_user_pass_port(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user:pass@[fe80::dcad:beff:fe00:0001]:9191/resource"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "fe80::dcad:beff:fe00:0001");
+ cl_assert_equal_s(conndata.port, "9191");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "user");
+ cl_assert_equal_s(conndata.password, "pass");
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_parse__ipv6_invalid_addresses(void)
+{
+ /* Opening bracket missing */
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://fe80::dcad:beff:fe00:0001]/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://fe80::dcad:beff:fe00:0001]/"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://fe80::dcad:beff:fe00:0001]"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://fe80::dcad:beff:fe00:0001]:42"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://fe80::dcad:beff:fe00:0001]:"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://user:pass%2fis%40bad@fe80::dcad:beff:fe00:0001]:1234/"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://user@fe80::dcad:beff:fe00:0001]/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://user:pass@fe80::dcad:beff:fe00:0001]/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://fe80::dcad:beff:fe00:0001]:9191/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://fe80::dcad:beff:fe00:0001]:/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://user@fe80::dcad:beff:fe00:0001]:9191/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://user:pass@fe80::dcad:beff:fe00:0001]:9191/resource"));
+
+ /* Closing bracket missing */
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://[fe80::dcad:beff:fe00:0001/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://[fe80::dcad:beff:fe00:0001/"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://[fe80::dcad:beff:fe00:0001"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://[fe80::dcad:beff:fe00:0001:42"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://[fe80::dcad:beff:fe00:0001:"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://user:pass%2fis%40bad@[fe80::dcad:beff:fe00:0001:1234/"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://user@[fe80::dcad:beff:fe00:0001/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://user:pass@[fe80::dcad:beff:fe00:0001/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://[fe80::dcad:beff:fe00:0001:9191/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://[fe80::dcad:beff:fe00:0001:/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://user@[fe80::dcad:beff:fe00:0001:9191/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://user:pass@[fe80::dcad:beff:fe00:0001:9191/resource"));
+
+ /* Both brackets missing */
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://fe80::dcad:beff:fe00:0001/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://fe80::dcad:beff:fe00:0001/"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://fe80::dcad:beff:fe00:0001"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://fe80::dcad:beff:fe00:0001:42"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://fe80::dcad:beff:fe00:0001:"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://user:pass%2fis%40bad@fe80::dcad:beff:fe00:0001:1234/"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://user@fe80::dcad:beff:fe00:0001/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://user:pass@fe80::dcad:beff:fe00:0001/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://fe80::dcad:beff:fe00:0001:9191/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://fe80::dcad:beff:fe00:0001:/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://user@fe80::dcad:beff:fe00:0001:9191/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "https://user:pass@fe80::dcad:beff:fe00:0001:9191/resource"));
+
+ /* Invalid character inside address */
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata, "http://[fe8o::dcad:beff:fe00:0001]/resource"));
+
+ /* Characters before/after braces */
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://fe80::[dcad:beff:fe00:0001]/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://cafe[fe80::dcad:beff:fe00:0001]/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse(&conndata,
+ "http://[fe80::dcad:beff:fe00:0001]cafe/resource"));
+}
+
+/* Oddities */
+
+void test_url_parse__empty_scheme(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "://example.com/resource"));
+ cl_assert_equal_s(conndata.scheme, NULL);
+ cl_assert_equal_s(conndata.host, NULL);
+ cl_assert_equal_s(conndata.port, NULL);
+ cl_assert_equal_s(conndata.path, "//example.com/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_p(conndata.query, NULL);
+ cl_assert_equal_p(conndata.fragment, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_parse__invalid_scheme_is_relative(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "foo!bar://host:42/path/to/project?query_string=yes"));
+ cl_assert_equal_p(conndata.scheme, NULL);
+ cl_assert_equal_p(conndata.host, NULL);
+ cl_assert_equal_p(conndata.port, NULL);
+ cl_assert_equal_s(conndata.path, "foo!bar://host:42/path/to/project");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_s(conndata.query, "query_string=yes");
+ cl_assert_equal_p(conndata.fragment, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_parse__scheme_case_is_normalized(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "GIT+SSH://host:42/path/to/project"));
+ cl_assert_equal_s(conndata.scheme, "git+ssh");
+}
+
+void test_url_parse__nonhierarchical_scheme(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "mailto:foobar@example.com"));
+ cl_assert_equal_s(conndata.scheme, "mailto");
+ cl_assert_equal_p(conndata.host, NULL);
+ cl_assert_equal_p(conndata.port, NULL);
+ cl_assert_equal_s(conndata.path, "foobar@example.com");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_parse__no_scheme_relative_path(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "path"));
+ cl_assert_equal_p(conndata.scheme, NULL);
+ cl_assert_equal_p(conndata.host, NULL);
+ cl_assert_equal_p(conndata.port, NULL);
+ cl_assert_equal_s(conndata.path, "path");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_parse__no_scheme_absolute_path(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "/path"));
+ cl_assert_equal_p(conndata.scheme, NULL);
+ cl_assert_equal_p(conndata.host, NULL);
+ cl_assert_equal_p(conndata.port, NULL);
+ cl_assert_equal_s(conndata.path, "/path");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_parse__empty_path(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "mailto:"));
+ cl_assert_equal_s(conndata.scheme, "mailto");
+ cl_assert_equal_p(conndata.host, NULL);
+ cl_assert_equal_p(conndata.port, NULL);
+ cl_assert_equal_s(conndata.path, NULL);
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_parse__empty_path_with_empty_authority(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "http://"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_p(conndata.host, NULL);
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_parse__http_follows_the_rfc(void)
+{
+ cl_git_fail(git_net_url_parse(&conndata, "https://my.email.address@gmail.com@source.developers.google.com:4433/p/my-project/r/my-repository"));
+}
+
+void test_url_parse__ssh_from_terrible_google_rfc_violating_products(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "ssh://my.email.address@gmail.com@source.developers.google.com:2022/p/my-project/r/my-repository"));
+ cl_assert_equal_s(conndata.scheme, "ssh");
+ cl_assert_equal_s(conndata.host, "source.developers.google.com");
+ cl_assert_equal_s(conndata.port, "2022");
+ cl_assert_equal_s(conndata.path, "/p/my-project/r/my-repository");
+ cl_assert_equal_s(conndata.username, "my.email.address@gmail.com");
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_parse__ssh_with_password_from_terrible_google_rfc_violating_products(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "ssh://my.email.address@gmail.com:seekret@source.developers.google.com:2022/p/my-project/r/my-repository"));
+ cl_assert_equal_s(conndata.scheme, "ssh");
+ cl_assert_equal_s(conndata.host, "source.developers.google.com");
+ cl_assert_equal_s(conndata.port, "2022");
+ cl_assert_equal_s(conndata.path, "/p/my-project/r/my-repository");
+ cl_assert_equal_s(conndata.username, "my.email.address@gmail.com");
+ cl_assert_equal_s(conndata.password, "seekret");
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_parse__spaces_in_the_name(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata, "https://libgit2@dev.azure.com/libgit2/test/_git/spaces%20in%20the%20name"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "dev.azure.com");
+ cl_assert_equal_s(conndata.port, "443");
+ cl_assert_equal_s(conndata.path, "/libgit2/test/_git/spaces%20in%20the%20name");
+ cl_assert_equal_s(conndata.username, "libgit2");
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
diff --git a/tests/util/url/pattern.c b/tests/util/url/pattern.c
new file mode 100644
index 0000000..f183d7f
--- /dev/null
+++ b/tests/util/url/pattern.c
@@ -0,0 +1,103 @@
+#include "clar_libgit2.h"
+#include "net.h"
+
+struct url_pattern {
+ const char *url;
+ const char *pattern;
+ bool matches;
+};
+
+void test_url_pattern__single(void)
+{
+ git_net_url url;
+ size_t i;
+
+ struct url_pattern url_patterns[] = {
+ /* Wildcard matches */
+ { "https://example.com/", "", false },
+ { "https://example.com/", "*", true },
+
+ /* Literal and wildcard matches */
+ { "https://example.com/", "example.com", true },
+ { "https://example.com/", ".example.com", true },
+ { "https://example.com/", "*.example.com", true },
+ { "https://www.example.com/", "www.example.com", true },
+ { "https://www.example.com/", ".example.com", true },
+ { "https://www.example.com/", "*.example.com", true },
+
+ /* Literal and wildcard failures */
+ { "https://example.com/", "example.org", false },
+ { "https://example.com/", ".example.org", false },
+ { "https://example.com/", "*.example.org", false },
+ { "https://foo.example.com/", "www.example.com", false },
+
+ /*
+ * A port in the pattern is optional; if no port is
+ * present, it matches *all* ports.
+ */
+ { "https://example.com/", "example.com:443", true },
+ { "https://example.com/", "example.com:80", false },
+ { "https://example.com:1443/", "example.com", true },
+
+ /* Failures with similar prefix/suffix */
+ { "https://texample.com/", "example.com", false },
+ { "https://example.com/", "mexample.com", false },
+ { "https://example.com:44/", "example.com:443", false },
+ { "https://example.com:443/", "example.com:44", false },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(url_patterns); i++) {
+ cl_git_pass(git_net_url_parse(&url, url_patterns[i].url));
+ cl_assert_(git_net_url_matches_pattern(&url, url_patterns[i].pattern) == url_patterns[i].matches, url_patterns[i].pattern);
+ git_net_url_dispose(&url);
+ }
+}
+
+void test_url_pattern__list(void)
+{
+ git_net_url url;
+ size_t i;
+
+ struct url_pattern url_patterns[] = {
+ /* Wildcard matches */
+ { "https://example.com/", "", false },
+ { "https://example.com/", "*", true },
+ { "https://example.com/", ",example.com,", true },
+ { "https://example.com/", "foo,,example.com,,bar", true },
+ { "https://example.com/", "foo,,zzz,,*,,bar", true },
+
+ /* Literals */
+ { "https://example.com/", "example.com", true },
+ { "https://example.com/", "foo.bar,example.com", true },
+ { "https://example.com/", "foo.bar", false },
+ { "https://example.com/", "foo.bar,example.org", false },
+ { "https://www.example.com/", "foo.example.com,www.example.com,bar.example.com", true },
+ { "https://www.example.com/", "foo.example.com,baz.example.com,bar.example.com", false },
+ { "https://foo.example.com/", "www.example.com", false },
+ { "https://foo.example.com/", "bar.example.com,www.example.com,", false },
+
+ /* Wildcards */
+ { "https://example.com/", ".example.com", true },
+ { "https://example.com/", "*.example.com", true },
+ { "https://example.com/", "foo.com,bar.com,.example.com", true },
+ { "https://example.com/", ".foo.com,.bar.com,.example.com", true },
+ { "https://example.com/", ".foo.com,.bar.com,asdf.com", false },
+ { "https://example.com/", "*.foo,*.bar,*.example.com,*.asdf", true },
+ { "https://example.com/", "*.foo,*.bar,*.asdf", false },
+
+
+ /* Ports! */
+ { "https://example.com/", "example.com:443", true },
+ { "https://example.com/", "example.com:42,example.com:443,example.com:99", true },
+ { "https://example.com/", "example.com:42,example.com:80,example.org:443", false },
+ { "https://example.com:1443/", "example.com", true },
+ { "https://example.com:44/", "example.com:443", false },
+ { "https://example.com:443/", "example.com:44", false },
+ };
+
+ for (i = 0; i < ARRAY_SIZE(url_patterns); i++) {
+ cl_git_pass(git_net_url_parse(&url, url_patterns[i].url));
+ cl_assert_(git_net_url_matches_pattern_list(&url, url_patterns[i].pattern) == url_patterns[i].matches, url_patterns[i].pattern);
+ git_net_url_dispose(&url);
+ }
+}
diff --git a/tests/util/url/redirect.c b/tests/util/url/redirect.c
new file mode 100644
index 0000000..a6f99dc
--- /dev/null
+++ b/tests/util/url/redirect.c
@@ -0,0 +1,146 @@
+#include "clar_libgit2.h"
+#include "net.h"
+
+static git_net_url conndata;
+
+void test_url_redirect__initialize(void)
+{
+ memset(&conndata, 0, sizeof(conndata));
+}
+
+void test_url_redirect__cleanup(void)
+{
+ git_net_url_dispose(&conndata);
+}
+
+void test_url_redirect__redirect_http(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "http://example.com/foo/bar/baz"));
+ cl_git_pass(git_net_url_apply_redirect(&conndata,
+ "http://example.com/foo/bar/baz", false, "bar/baz"));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/foo/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+}
+
+void test_url_redirect__redirect_ssl(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://example.com/foo/bar/baz"));
+ cl_git_pass(git_net_url_apply_redirect(&conndata,
+ "https://example.com/foo/bar/baz", false, "bar/baz"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "443");
+ cl_assert_equal_s(conndata.path, "/foo/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+}
+
+void test_url_redirect__redirect_leaves_root_path(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://example.com/foo/bar/baz"));
+ cl_git_pass(git_net_url_apply_redirect(&conndata,
+ "https://example.com/foo/bar/baz", false, "/foo/bar/baz"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "443");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+}
+
+void test_url_redirect__redirect_encoded_username_password(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://user%2fname:pass%40word%zyx%v@example.com/foo/bar/baz"));
+ cl_git_pass(git_net_url_apply_redirect(&conndata,
+ "https://user%2fname:pass%40word%zyx%v@example.com/foo/bar/baz", false, "bar/baz"));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "443");
+ cl_assert_equal_s(conndata.path, "/foo/");
+ cl_assert_equal_s(conndata.username, "user/name");
+ cl_assert_equal_s(conndata.password, "pass@word%zyx%v");
+}
+
+void test_url_redirect__redirect_cross_host_allowed(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://bar.com/bar/baz"));
+ cl_git_pass(git_net_url_apply_redirect(&conndata,
+ "https://foo.com/bar/baz", true, NULL));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "foo.com");
+ cl_assert_equal_s(conndata.port, "443");
+ cl_assert_equal_s(conndata.path, "/bar/baz");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+}
+
+void test_url_redirect__redirect_cross_host_denied(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://bar.com/bar/baz"));
+ cl_git_fail_with(git_net_url_apply_redirect(&conndata,
+ "https://foo.com/bar/baz", false, NULL), -1);
+}
+
+void test_url_redirect__redirect_http_downgrade_denied(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://foo.com/bar/baz"));
+ cl_git_fail_with(git_net_url_apply_redirect(&conndata,
+ "http://foo.com/bar/baz", true, NULL), -1);
+}
+
+void test_url_redirect__redirect_relative(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "http://foo.com/bar/baz/biff"));
+ cl_git_pass(git_net_url_apply_redirect(&conndata,
+ "/zap/baz/biff?bam", true, NULL));
+ cl_assert_equal_s(conndata.scheme, "http");
+ cl_assert_equal_s(conndata.host, "foo.com");
+ cl_assert_equal_s(conndata.port, "80");
+ cl_assert_equal_s(conndata.path, "/zap/baz/biff?bam");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+}
+
+void test_url_redirect__redirect_relative_ssl(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://foo.com/bar/baz/biff"));
+ cl_git_pass(git_net_url_apply_redirect(&conndata,
+ "/zap/baz/biff?bam", true, NULL));
+ cl_assert_equal_s(conndata.scheme, "https");
+ cl_assert_equal_s(conndata.host, "foo.com");
+ cl_assert_equal_s(conndata.port, "443");
+ cl_assert_equal_s(conndata.path, "/zap/baz/biff?bam");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+}
+
+void test_url_redirect__service_query_no_query_params_in_location(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://foo.com/bar/info/refs?service=git-upload-pack"));
+ cl_git_pass(git_net_url_apply_redirect(&conndata,
+ "/baz/info/refs", true, "/info/refs?service=git-upload-pack"));
+ cl_assert_equal_s(conndata.path, "/baz");
+}
+
+void test_url_redirect__service_query_with_query_params_in_location(void)
+{
+ cl_git_pass(git_net_url_parse(&conndata,
+ "https://foo.com/bar/info/refs?service=git-upload-pack"));
+ cl_git_pass(git_net_url_apply_redirect(&conndata,
+ "/baz/info/refs?service=git-upload-pack", true, "/info/refs?service=git-upload-pack"));
+ cl_assert_equal_s(conndata.path, "/baz");
+}
diff --git a/tests/util/url/scp.c b/tests/util/url/scp.c
new file mode 100644
index 0000000..0e0dce1
--- /dev/null
+++ b/tests/util/url/scp.c
@@ -0,0 +1,317 @@
+#include "clar_libgit2.h"
+#include "net.h"
+
+static git_net_url conndata;
+
+void test_url_scp__initialize(void)
+{
+ memset(&conndata, 0, sizeof(conndata));
+}
+
+void test_url_scp__cleanup(void)
+{
+ git_net_url_dispose(&conndata);
+}
+
+/* Hostname */
+
+void test_url_scp__hostname_trivial(void)
+{
+ cl_git_pass(git_net_url_parse_scp(&conndata, "example.com:/resource"));
+ cl_assert_equal_s(conndata.scheme, "ssh");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "22");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_scp__hostname_bracketed(void)
+{
+ cl_git_pass(git_net_url_parse_scp(&conndata, "[example.com]:/resource"));
+ cl_assert_equal_s(conndata.scheme, "ssh");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "22");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_scp__hostname_root(void)
+{
+ cl_git_pass(git_net_url_parse_scp(&conndata, "example.com:/"));
+ cl_assert_equal_s(conndata.scheme, "ssh");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "22");
+ cl_assert_equal_s(conndata.path, "/");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_scp__hostname_user(void)
+{
+ cl_git_pass(git_net_url_parse_scp(&conndata, "git@example.com:/resource"));
+ cl_assert_equal_s(conndata.scheme, "ssh");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "22");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "git");
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_scp__hostname_user_bracketed(void)
+{
+ cl_git_pass(git_net_url_parse_scp(&conndata, "[git@example.com]:/resource"));
+ cl_assert_equal_s(conndata.scheme, "ssh");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "22");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "git");
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_scp__hostname_port(void)
+{
+ cl_git_pass(git_net_url_parse_scp(&conndata, "[example.com:42]:/resource"));
+ cl_assert_equal_s(conndata.scheme, "ssh");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "42");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_scp__hostname_user_port(void)
+{
+ cl_git_pass(git_net_url_parse_scp(&conndata, "[git@example.com:42]:/resource"));
+ cl_assert_equal_s(conndata.scheme, "ssh");
+ cl_assert_equal_s(conndata.host, "example.com");
+ cl_assert_equal_s(conndata.port, "42");
+ cl_assert_equal_s(conndata.path, "/resource");
+ cl_assert_equal_s(conndata.username, "git");
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_scp__ipv4_trivial(void)
+{
+ cl_git_pass(git_net_url_parse_scp(&conndata, "192.168.99.88:/resource/a/b/c"));
+ cl_assert_equal_s(conndata.scheme, "ssh");
+ cl_assert_equal_s(conndata.host, "192.168.99.88");
+ cl_assert_equal_s(conndata.port, "22");
+ cl_assert_equal_s(conndata.path, "/resource/a/b/c");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_scp__ipv4_bracketed(void)
+{
+ cl_git_pass(git_net_url_parse_scp(&conndata, "[192.168.99.88]:/resource/a/b/c"));
+ cl_assert_equal_s(conndata.scheme, "ssh");
+ cl_assert_equal_s(conndata.host, "192.168.99.88");
+ cl_assert_equal_s(conndata.port, "22");
+ cl_assert_equal_s(conndata.path, "/resource/a/b/c");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_scp__ipv4_user(void)
+{
+ cl_git_pass(git_net_url_parse_scp(&conndata, "git@192.168.99.88:/resource/a/b/c"));
+ cl_assert_equal_s(conndata.scheme, "ssh");
+ cl_assert_equal_s(conndata.host, "192.168.99.88");
+ cl_assert_equal_s(conndata.port, "22");
+ cl_assert_equal_s(conndata.path, "/resource/a/b/c");
+ cl_assert_equal_s(conndata.username, "git");
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_scp__ipv4_port(void)
+{
+ cl_git_pass(git_net_url_parse_scp(&conndata, "[192.168.99.88:1111]:/resource/a/b/c"));
+ cl_assert_equal_s(conndata.scheme, "ssh");
+ cl_assert_equal_s(conndata.host, "192.168.99.88");
+ cl_assert_equal_s(conndata.port, "1111");
+ cl_assert_equal_s(conndata.path, "/resource/a/b/c");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_scp__ipv4_user_port(void)
+{
+ cl_git_pass(git_net_url_parse_scp(&conndata, "[git@192.168.99.88:1111]:/resource/a/b/c"));
+ cl_assert_equal_s(conndata.scheme, "ssh");
+ cl_assert_equal_s(conndata.host, "192.168.99.88");
+ cl_assert_equal_s(conndata.port, "1111");
+ cl_assert_equal_s(conndata.path, "/resource/a/b/c");
+ cl_assert_equal_s(conndata.username, "git");
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_scp__ipv6_trivial(void)
+{
+ cl_git_pass(git_net_url_parse_scp(&conndata, "[fe80::dcad:beff:fe00:0001]:/resource/foo"));
+ cl_assert_equal_s(conndata.scheme, "ssh");
+ cl_assert_equal_s(conndata.host, "[fe80::dcad:beff:fe00:0001]");
+ cl_assert_equal_s(conndata.port, "22");
+ cl_assert_equal_s(conndata.path, "/resource/foo");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_scp__ipv6_user(void)
+{
+ cl_git_pass(git_net_url_parse_scp(&conndata, "git@[fe80::dcad:beff:fe00:0001]:/resource/foo"));
+ cl_assert_equal_s(conndata.scheme, "ssh");
+ cl_assert_equal_s(conndata.host, "[fe80::dcad:beff:fe00:0001]");
+ cl_assert_equal_s(conndata.port, "22");
+ cl_assert_equal_s(conndata.path, "/resource/foo");
+ cl_assert_equal_s(conndata.username, "git");
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_scp__ipv6_port(void)
+{
+ cl_git_pass(git_net_url_parse_scp(&conndata, "[[fe80::dcad:beff:fe00:0001]:99]:/resource/foo"));
+ cl_assert_equal_s(conndata.scheme, "ssh");
+ cl_assert_equal_s(conndata.host, "[fe80::dcad:beff:fe00:0001]");
+ cl_assert_equal_s(conndata.port, "99");
+ cl_assert_equal_s(conndata.path, "/resource/foo");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_scp__ipv6_user_port(void)
+{
+ cl_git_pass(git_net_url_parse_scp(&conndata, "[git@[fe80::dcad:beff:fe00:0001]:99]:/resource/foo"));
+ cl_assert_equal_s(conndata.scheme, "ssh");
+ cl_assert_equal_s(conndata.host, "[fe80::dcad:beff:fe00:0001]");
+ cl_assert_equal_s(conndata.port, "99");
+ cl_assert_equal_s(conndata.path, "/resource/foo");
+ cl_assert_equal_s(conndata.username, "git");
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 0);
+}
+
+void test_url_scp__hexhost_and_port(void)
+{
+ cl_git_pass(git_net_url_parse_scp(&conndata, "[fe:22]:/resource/foo"));
+ cl_assert_equal_s(conndata.scheme, "ssh");
+ cl_assert_equal_s(conndata.host, "fe");
+ cl_assert_equal_s(conndata.port, "22");
+ cl_assert_equal_s(conndata.path, "/resource/foo");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_scp__malformed_ipv6_one(void)
+{
+ cl_git_pass(git_net_url_parse_scp(&conndata, "fe80::dcad:beff:fe00:0001]:/resource"));
+ cl_assert_equal_s(conndata.scheme, "ssh");
+ cl_assert_equal_s(conndata.host, "fe80");
+ cl_assert_equal_s(conndata.port, "22");
+ cl_assert_equal_s(conndata.path, ":dcad:beff:fe00:0001]:/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_scp__malformed_ipv6_two(void)
+{
+ cl_git_pass(git_net_url_parse_scp(&conndata, "[fe80::dcad:beff:fe00:0001]:42]:/resource"));
+ cl_assert_equal_s(conndata.scheme, "ssh");
+ cl_assert_equal_s(conndata.host, "[fe80::dcad:beff:fe00:0001]");
+ cl_assert_equal_s(conndata.port, "22");
+ cl_assert_equal_s(conndata.path, "42]:/resource");
+ cl_assert_equal_p(conndata.username, NULL);
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_scp__malformed_ipv6_with_user(void)
+{
+ cl_git_pass(git_net_url_parse_scp(&conndata, "git@[fe80::dcad:beff:fe00:0001]:42]:/resource"));
+ cl_assert_equal_s(conndata.scheme, "ssh");
+ cl_assert_equal_s(conndata.host, "[fe80::dcad:beff:fe00:0001]");
+ cl_assert_equal_s(conndata.port, "22");
+ cl_assert_equal_s(conndata.path, "42]:/resource");
+ cl_assert_equal_s(conndata.username, "git");
+ cl_assert_equal_p(conndata.password, NULL);
+ cl_assert_equal_i(git_net_url_is_default_port(&conndata), 1);
+}
+
+void test_url_scp__invalid_addresses(void)
+{
+ /* Path is required */
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_scp(&conndata,
+ "example.com"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_scp(&conndata,
+ "example.com:"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_scp(&conndata,
+ "[example.com:42]:"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_scp(&conndata,
+ "[git@example.com:42]:"));
+
+ /* Host is required */
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_scp(&conndata,
+ ":"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_scp(&conndata,
+ ":foo"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_scp(&conndata,
+ "git@:foo"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_scp(&conndata,
+ "[]:"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_scp(&conndata,
+ "git@[]:"));
+
+ /* User is required if specified */
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_scp(&conndata,
+ "@example.com:foo"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_scp(&conndata,
+ "@:foo"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_scp(&conndata,
+ "[@localhost:22]:foo"));
+
+ /* Port is required in brackets */
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_scp(&conndata,
+ "[example.com:]:foo"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_scp(&conndata,
+ "[git@example.com:]:foo"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_scp(&conndata,
+ "[fe:]:foo"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_scp(&conndata,
+ "[@localhost]:foo"));
+
+ /* Extra brackets are disallowed */
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_scp(&conndata,
+ "[git@[[fe80::dcad:beff:fe00:0001]]:42]:foo"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_scp(&conndata,
+ "[[git@[fe80::dcad:beff:fe00:0001]]:42]:foo"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_scp(&conndata,
+ "[[git@[fe80::dcad:beff:fe00:0001]:42]]:foo"));
+
+ /* Closing bracket missing */
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_scp(&conndata,
+ "[fe80::dcad:beff:fe00:0001:/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_scp(&conndata,
+ "[[fe80::dcad:beff:fe00:0001]:42:/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_scp(&conndata,
+ "[git@[fe80::dcad:beff:fe00:0001]:42:/resource"));
+ cl_git_fail_with(GIT_EINVALIDSPEC, git_net_url_parse_scp(&conndata,
+ "[git@[fe80::dcad:beff:fe00:0001:42]:/resource"));
+}
diff --git a/tests/util/url/valid.c b/tests/util/url/valid.c
new file mode 100644
index 0000000..797b697
--- /dev/null
+++ b/tests/util/url/valid.c
@@ -0,0 +1,17 @@
+#include "clar_libgit2.h"
+#include "net.h"
+
+void test_url_valid__test(void)
+{
+ cl_assert(git_net_str_is_url("http://example.com/"));
+ cl_assert(git_net_str_is_url("file://localhost/tmp/foo/"));
+ cl_assert(git_net_str_is_url("ssh://user@host:42/tmp"));
+ cl_assert(git_net_str_is_url("git+ssh://user@host:42/tmp"));
+ cl_assert(git_net_str_is_url("ssh+git://user@host:42/tmp"));
+ cl_assert(git_net_str_is_url("https://user:pass@example.com/foo/bar"));
+
+ cl_assert(!git_net_str_is_url("host:foo.git"));
+ cl_assert(!git_net_str_is_url("host:/foo.git"));
+ cl_assert(!git_net_str_is_url("[host:42]:/foo.git"));
+ cl_assert(!git_net_str_is_url("[user@host:42]:/foo.git"));
+}
diff --git a/tests/util/utf8.c b/tests/util/utf8.c
new file mode 100644
index 0000000..3987603
--- /dev/null
+++ b/tests/util/utf8.c
@@ -0,0 +1,20 @@
+#include "clar_libgit2.h"
+#include "utf8.h"
+
+void test_utf8__char_length(void)
+{
+ cl_assert_equal_i(0, git_utf8_char_length("", 0));
+ cl_assert_equal_i(1, git_utf8_char_length("$", 1));
+ cl_assert_equal_i(5, git_utf8_char_length("abcde", 5));
+ cl_assert_equal_i(1, git_utf8_char_length("\xc2\xa2", 2));
+ cl_assert_equal_i(2, git_utf8_char_length("\x24\xc2\xa2", 3));
+ cl_assert_equal_i(1, git_utf8_char_length("\xf0\x90\x8d\x88", 4));
+
+ /* uncontinued character counted as single characters */
+ cl_assert_equal_i(2, git_utf8_char_length("\x24\xc2", 2));
+ cl_assert_equal_i(3, git_utf8_char_length("\x24\xc2\xc2\xa2", 4));
+
+ /* invalid characters are counted as single characters */
+ cl_assert_equal_i(4, git_utf8_char_length("\x24\xc0\xc0\x34", 4));
+ cl_assert_equal_i(4, git_utf8_char_length("\x24\xf5\xfd\xc2", 4));
+}
diff --git a/tests/util/vector.c b/tests/util/vector.c
new file mode 100644
index 0000000..04afaa4
--- /dev/null
+++ b/tests/util/vector.c
@@ -0,0 +1,430 @@
+#include <stdint.h>
+
+#include "clar_libgit2.h"
+#include "vector.h"
+
+/* initial size of 1 would cause writing past array bounds */
+void test_vector__0(void)
+{
+ git_vector x;
+ int i;
+ cl_git_pass(git_vector_init(&x, 1, NULL));
+ for (i = 0; i < 10; ++i) {
+ git_vector_insert(&x, (void*) 0xabc);
+ }
+ git_vector_free(&x);
+}
+
+
+/* don't read past array bounds on remove() */
+void test_vector__1(void)
+{
+ git_vector x;
+ /* make initial capacity exact for our insertions. */
+ cl_git_pass(git_vector_init(&x, 3, NULL));
+ git_vector_insert(&x, (void*) 0xabc);
+ git_vector_insert(&x, (void*) 0xdef);
+ git_vector_insert(&x, (void*) 0x123);
+
+ git_vector_remove(&x, 0); /* used to read past array bounds. */
+ git_vector_free(&x);
+}
+
+
+static int test_cmp(const void *a, const void *b)
+{
+ return *(const int *)a - *(const int *)b;
+}
+
+/* remove duplicates */
+void test_vector__2(void)
+{
+ git_vector x;
+ int *ptrs[2];
+
+ ptrs[0] = git__malloc(sizeof(int));
+ ptrs[1] = git__malloc(sizeof(int));
+
+ *ptrs[0] = 2;
+ *ptrs[1] = 1;
+
+ cl_git_pass(git_vector_init(&x, 5, test_cmp));
+ cl_git_pass(git_vector_insert(&x, ptrs[0]));
+ cl_git_pass(git_vector_insert(&x, ptrs[1]));
+ cl_git_pass(git_vector_insert(&x, ptrs[1]));
+ cl_git_pass(git_vector_insert(&x, ptrs[0]));
+ cl_git_pass(git_vector_insert(&x, ptrs[1]));
+ cl_assert(x.length == 5);
+
+ git_vector_uniq(&x, NULL);
+ cl_assert(x.length == 2);
+
+ git_vector_free(&x);
+
+ git__free(ptrs[0]);
+ git__free(ptrs[1]);
+}
+
+
+static int compare_them(const void *a, const void *b)
+{
+ return (int)((intptr_t)a - (intptr_t)b);
+}
+
+/* insert_sorted */
+void test_vector__3(void)
+{
+ git_vector x;
+ intptr_t i;
+ cl_git_pass(git_vector_init(&x, 1, &compare_them));
+
+ for (i = 0; i < 10; i += 2) {
+ git_vector_insert_sorted(&x, (void*)(i + 1), NULL);
+ }
+
+ for (i = 9; i > 0; i -= 2) {
+ git_vector_insert_sorted(&x, (void*)(i + 1), NULL);
+ }
+
+ cl_assert(x.length == 10);
+ for (i = 0; i < 10; ++i) {
+ cl_assert(git_vector_get(&x, i) == (void*)(i + 1));
+ }
+
+ git_vector_free(&x);
+}
+
+/* insert_sorted with duplicates */
+void test_vector__4(void)
+{
+ git_vector x;
+ intptr_t i;
+ cl_git_pass(git_vector_init(&x, 1, &compare_them));
+
+ for (i = 0; i < 10; i += 2) {
+ git_vector_insert_sorted(&x, (void*)(i + 1), NULL);
+ }
+
+ for (i = 9; i > 0; i -= 2) {
+ git_vector_insert_sorted(&x, (void*)(i + 1), NULL);
+ }
+
+ for (i = 0; i < 10; i += 2) {
+ git_vector_insert_sorted(&x, (void*)(i + 1), NULL);
+ }
+
+ for (i = 9; i > 0; i -= 2) {
+ git_vector_insert_sorted(&x, (void*)(i + 1), NULL);
+ }
+
+ cl_assert(x.length == 20);
+ for (i = 0; i < 20; ++i) {
+ cl_assert(git_vector_get(&x, i) == (void*)(i / 2 + 1));
+ }
+
+ git_vector_free(&x);
+}
+
+typedef struct {
+ int content;
+ int count;
+} my_struct;
+
+static int _struct_count = 0;
+
+static int compare_structs(const void *a, const void *b)
+{
+ return ((const my_struct *)a)->content -
+ ((const my_struct *)b)->content;
+}
+
+static int merge_structs(void **old_raw, void *new)
+{
+ my_struct *old = *(my_struct **)old_raw;
+ cl_assert(((my_struct *)old)->content == ((my_struct *)new)->content);
+ ((my_struct *)old)->count += 1;
+ git__free(new);
+ _struct_count--;
+ return GIT_EEXISTS;
+}
+
+static my_struct *alloc_struct(int value)
+{
+ my_struct *st = git__malloc(sizeof(my_struct));
+ st->content = value;
+ st->count = 0;
+ _struct_count++;
+ return st;
+}
+
+/* insert_sorted with duplicates and special handling */
+void test_vector__5(void)
+{
+ git_vector x;
+ int i;
+
+ cl_git_pass(git_vector_init(&x, 1, &compare_structs));
+
+ for (i = 0; i < 10; i += 2)
+ git_vector_insert_sorted(&x, alloc_struct(i), &merge_structs);
+
+ for (i = 9; i > 0; i -= 2)
+ git_vector_insert_sorted(&x, alloc_struct(i), &merge_structs);
+
+ cl_assert(x.length == 10);
+ cl_assert(_struct_count == 10);
+
+ for (i = 0; i < 10; i += 2)
+ git_vector_insert_sorted(&x, alloc_struct(i), &merge_structs);
+
+ for (i = 9; i > 0; i -= 2)
+ git_vector_insert_sorted(&x, alloc_struct(i), &merge_structs);
+
+ cl_assert(x.length == 10);
+ cl_assert(_struct_count == 10);
+
+ for (i = 0; i < 10; ++i) {
+ cl_assert(((my_struct *)git_vector_get(&x, i))->content == i);
+ git__free(git_vector_get(&x, i));
+ _struct_count--;
+ }
+
+ git_vector_free(&x);
+}
+
+static int remove_ones(const git_vector *v, size_t idx, void *p)
+{
+ GIT_UNUSED(p);
+ return (git_vector_get(v, idx) == (void *)0x001);
+}
+
+/* Test removal based on callback */
+void test_vector__remove_matching(void)
+{
+ git_vector x;
+ size_t i;
+ void *compare;
+
+ cl_git_pass(git_vector_init(&x, 1, NULL));
+ git_vector_insert(&x, (void*) 0x001);
+
+ cl_assert(x.length == 1);
+ git_vector_remove_matching(&x, remove_ones, NULL);
+ cl_assert(x.length == 0);
+
+ git_vector_insert(&x, (void*) 0x001);
+ git_vector_insert(&x, (void*) 0x001);
+ git_vector_insert(&x, (void*) 0x001);
+
+ cl_assert(x.length == 3);
+ git_vector_remove_matching(&x, remove_ones, NULL);
+ cl_assert(x.length == 0);
+
+ git_vector_insert(&x, (void*) 0x002);
+ git_vector_insert(&x, (void*) 0x001);
+ git_vector_insert(&x, (void*) 0x002);
+ git_vector_insert(&x, (void*) 0x001);
+
+ cl_assert(x.length == 4);
+ git_vector_remove_matching(&x, remove_ones, NULL);
+ cl_assert(x.length == 2);
+
+ git_vector_foreach(&x, i, compare) {
+ cl_assert(compare != (void *)0x001);
+ }
+
+ git_vector_clear(&x);
+
+ git_vector_insert(&x, (void*) 0x001);
+ git_vector_insert(&x, (void*) 0x002);
+ git_vector_insert(&x, (void*) 0x002);
+ git_vector_insert(&x, (void*) 0x001);
+
+ cl_assert(x.length == 4);
+ git_vector_remove_matching(&x, remove_ones, NULL);
+ cl_assert(x.length == 2);
+
+ git_vector_foreach(&x, i, compare) {
+ cl_assert(compare != (void *)0x001);
+ }
+
+ git_vector_clear(&x);
+
+ git_vector_insert(&x, (void*) 0x002);
+ git_vector_insert(&x, (void*) 0x001);
+ git_vector_insert(&x, (void*) 0x002);
+ git_vector_insert(&x, (void*) 0x001);
+
+ cl_assert(x.length == 4);
+ git_vector_remove_matching(&x, remove_ones, NULL);
+ cl_assert(x.length == 2);
+
+ git_vector_foreach(&x, i, compare) {
+ cl_assert(compare != (void *)0x001);
+ }
+
+ git_vector_clear(&x);
+
+ git_vector_insert(&x, (void*) 0x002);
+ git_vector_insert(&x, (void*) 0x003);
+ git_vector_insert(&x, (void*) 0x002);
+ git_vector_insert(&x, (void*) 0x003);
+
+ cl_assert(x.length == 4);
+ git_vector_remove_matching(&x, remove_ones, NULL);
+ cl_assert(x.length == 4);
+
+ git_vector_free(&x);
+}
+
+static void assert_vector(git_vector *x, void *expected[], size_t len)
+{
+ size_t i;
+
+ cl_assert_equal_i(len, x->length);
+
+ for (i = 0; i < len; i++)
+ cl_assert(expected[i] == x->contents[i]);
+}
+
+void test_vector__grow_and_shrink(void)
+{
+ git_vector x = GIT_VECTOR_INIT;
+ void *expected1[] = {
+ (void *)0x02, (void *)0x03, (void *)0x04, (void *)0x05,
+ (void *)0x06, (void *)0x07, (void *)0x08, (void *)0x09,
+ (void *)0x0a
+ };
+ void *expected2[] = {
+ (void *)0x02, (void *)0x04, (void *)0x05, (void *)0x06,
+ (void *)0x07, (void *)0x08, (void *)0x09, (void *)0x0a
+ };
+ void *expected3[] = {
+ (void *)0x02, (void *)0x04, (void *)0x05, (void *)0x06,
+ (void *)0x0a
+ };
+ void *expected4[] = {
+ (void *)0x02, (void *)0x04, (void *)0x05
+ };
+ void *expected5[] = {
+ (void *)0x00, (void *)0x00, (void *)0x02, (void *)0x04,
+ (void *)0x05
+ };
+ void *expected6[] = {
+ (void *)0x00, (void *)0x00, (void *)0x02, (void *)0x04,
+ (void *)0x05, (void *)0x00
+ };
+ void *expected7[] = {
+ (void *)0x00, (void *)0x00, (void *)0x02, (void *)0x04,
+ (void *)0x00, (void *)0x00, (void *)0x00, (void *)0x05,
+ (void *)0x00
+ };
+ void *expected8[] = {
+ (void *)0x04, (void *)0x00, (void *)0x00, (void *)0x00,
+ (void *)0x05, (void *)0x00
+ };
+ void *expected9[] = {
+ (void *)0x04, (void *)0x00, (void *)0x05, (void *)0x00
+ };
+ void *expectedA[] = { (void *)0x04, (void *)0x00 };
+ void *expectedB[] = { (void *)0x04 };
+
+ git_vector_insert(&x, (void *)0x01);
+ git_vector_insert(&x, (void *)0x02);
+ git_vector_insert(&x, (void *)0x03);
+ git_vector_insert(&x, (void *)0x04);
+ git_vector_insert(&x, (void *)0x05);
+ git_vector_insert(&x, (void *)0x06);
+ git_vector_insert(&x, (void *)0x07);
+ git_vector_insert(&x, (void *)0x08);
+ git_vector_insert(&x, (void *)0x09);
+ git_vector_insert(&x, (void *)0x0a);
+
+ git_vector_remove_range(&x, 0, 1);
+ assert_vector(&x, expected1, ARRAY_SIZE(expected1));
+
+ git_vector_remove_range(&x, 1, 1);
+ assert_vector(&x, expected2, ARRAY_SIZE(expected2));
+
+ git_vector_remove_range(&x, 4, 3);
+ assert_vector(&x, expected3, ARRAY_SIZE(expected3));
+
+ git_vector_remove_range(&x, 3, 2);
+ assert_vector(&x, expected4, ARRAY_SIZE(expected4));
+
+ git_vector_insert_null(&x, 0, 2);
+ assert_vector(&x, expected5, ARRAY_SIZE(expected5));
+
+ git_vector_insert_null(&x, 5, 1);
+ assert_vector(&x, expected6, ARRAY_SIZE(expected6));
+
+ git_vector_insert_null(&x, 4, 3);
+ assert_vector(&x, expected7, ARRAY_SIZE(expected7));
+
+ git_vector_remove_range(&x, 0, 3);
+ assert_vector(&x, expected8, ARRAY_SIZE(expected8));
+
+ git_vector_remove_range(&x, 1, 2);
+ assert_vector(&x, expected9, ARRAY_SIZE(expected9));
+
+ git_vector_remove_range(&x, 2, 2);
+ assert_vector(&x, expectedA, ARRAY_SIZE(expectedA));
+
+ git_vector_remove_range(&x, 1, 1);
+ assert_vector(&x, expectedB, ARRAY_SIZE(expectedB));
+
+ git_vector_remove_range(&x, 0, 1);
+ assert_vector(&x, NULL, 0);
+
+ git_vector_free(&x);
+}
+
+void test_vector__reverse(void)
+{
+ git_vector v = GIT_VECTOR_INIT;
+ size_t i;
+
+ void *in1[] = {(void *) 0x0, (void *) 0x1, (void *) 0x2, (void *) 0x3};
+ void *out1[] = {(void *) 0x3, (void *) 0x2, (void *) 0x1, (void *) 0x0};
+
+ void *in2[] = {(void *) 0x0, (void *) 0x1, (void *) 0x2, (void *) 0x3, (void *) 0x4};
+ void *out2[] = {(void *) 0x4, (void *) 0x3, (void *) 0x2, (void *) 0x1, (void *) 0x0};
+
+ for (i = 0; i < 4; i++)
+ cl_git_pass(git_vector_insert(&v, in1[i]));
+
+ git_vector_reverse(&v);
+
+ for (i = 0; i < 4; i++)
+ cl_assert_equal_p(out1[i], git_vector_get(&v, i));
+
+ git_vector_clear(&v);
+ for (i = 0; i < 5; i++)
+ cl_git_pass(git_vector_insert(&v, in2[i]));
+
+ git_vector_reverse(&v);
+
+ for (i = 0; i < 5; i++)
+ cl_assert_equal_p(out2[i], git_vector_get(&v, i));
+
+ git_vector_free(&v);
+}
+
+void test_vector__dup_empty_vector(void)
+{
+ git_vector v = GIT_VECTOR_INIT;
+ git_vector dup = GIT_VECTOR_INIT;
+ int dummy;
+
+ cl_assert_equal_i(0, v.length);
+
+ cl_git_pass(git_vector_dup(&dup, &v, v._cmp));
+ cl_assert_equal_i(0, dup._alloc_size);
+ cl_assert_equal_i(0, dup.length);
+
+ cl_git_pass(git_vector_insert(&dup, &dummy));
+ cl_assert_equal_i(8, dup._alloc_size);
+ cl_assert_equal_i(1, dup.length);
+
+ git_vector_free(&dup);
+}
diff --git a/tests/util/wildmatch.c b/tests/util/wildmatch.c
new file mode 100644
index 0000000..a5af61a
--- /dev/null
+++ b/tests/util/wildmatch.c
@@ -0,0 +1,248 @@
+#include "clar_libgit2.h"
+
+#include "wildmatch.h"
+
+#define assert_matches(string, pattern, wildmatch, iwildmatch, pathmatch, ipathmatch) \
+ assert_matches_(string, pattern, wildmatch, iwildmatch, pathmatch, ipathmatch, __FILE__, __func__, __LINE__)
+
+static void assert_matches_(const char *string, const char *pattern,
+ char expected_wildmatch, char expected_iwildmatch,
+ char expected_pathmatch, char expected_ipathmatch,
+ const char *file, const char *func, size_t line)
+{
+ if (wildmatch(pattern, string, WM_PATHNAME) == expected_wildmatch)
+ clar__fail(file, func, line, "Test failed (wildmatch).", string, 1);
+ if (wildmatch(pattern, string, WM_PATHNAME|WM_CASEFOLD) == expected_iwildmatch)
+ clar__fail(file, func, line, "Test failed (iwildmatch).", string, 1);
+ if (wildmatch(pattern, string, 0) == expected_pathmatch)
+ clar__fail(file, func, line, "Test failed (pathmatch).", string, 1);
+ if (wildmatch(pattern, string, WM_CASEFOLD) == expected_ipathmatch)
+ clar__fail(file, func, line, "Test failed (ipathmatch).", string, 1);
+}
+
+/*
+ * Below testcases are imported from git.git, t3070-wildmatch,sh at tag v2.22.0.
+ * Note that we've only imported the direct wildcard tests, but not the matching
+ * tests for git-ls-files.
+ */
+
+void test_wildmatch__basic_wildmatch(void)
+{
+ assert_matches("foo", "foo", 1, 1, 1, 1);
+ assert_matches("foo", "bar", 0, 0, 0, 0);
+ assert_matches("", "", 1, 1, 1, 1);
+ assert_matches("foo", "???", 1, 1, 1, 1);
+ assert_matches("foo", "??", 0, 0, 0, 0);
+ assert_matches("foo", "*", 1, 1, 1, 1);
+ assert_matches("foo", "f*", 1, 1, 1, 1);
+ assert_matches("foo", "*f", 0, 0, 0, 0);
+ assert_matches("foo", "*foo*", 1, 1, 1, 1);
+ assert_matches("foobar", "*ob*a*r*", 1, 1, 1, 1);
+ assert_matches("aaaaaaabababab", "*ab", 1, 1, 1, 1);
+ assert_matches("foo*", "foo\\*", 1, 1, 1, 1);
+ assert_matches("foobar", "foo\\*bar", 0, 0, 0, 0);
+ assert_matches("f\\oo", "f\\\\oo", 1, 1, 1, 1);
+ assert_matches("ball", "*[al]?", 1, 1, 1, 1);
+ assert_matches("ten", "[ten]", 0, 0, 0, 0);
+ assert_matches("ten", "**[!te]", 1, 1, 1, 1);
+ assert_matches("ten", "**[!ten]", 0, 0, 0, 0);
+ assert_matches("ten", "t[a-g]n", 1, 1, 1, 1);
+ assert_matches("ten", "t[!a-g]n", 0, 0, 0, 0);
+ assert_matches("ton", "t[!a-g]n", 1, 1, 1, 1);
+ assert_matches("ton", "t[^a-g]n", 1, 1, 1, 1);
+ assert_matches("a]b", "a[]]b", 1, 1, 1, 1);
+ assert_matches("a-b", "a[]-]b", 1, 1, 1, 1);
+ assert_matches("a]b", "a[]-]b", 1, 1, 1, 1);
+ assert_matches("aab", "a[]-]b", 0, 0, 0, 0);
+ assert_matches("aab", "a[]a-]b", 1, 1, 1, 1);
+ assert_matches("]", "]", 1, 1, 1, 1);
+}
+
+void test_wildmatch__slash_matching_features(void)
+{
+ assert_matches("foo/baz/bar", "foo*bar", 0, 0, 1, 1);
+ assert_matches("foo/baz/bar", "foo**bar", 0, 0, 1, 1);
+ assert_matches("foobazbar", "foo**bar", 1, 1, 1, 1);
+ assert_matches("foo/baz/bar", "foo/**/bar", 1, 1, 1, 1);
+ assert_matches("foo/baz/bar", "foo/**/**/bar", 1, 1, 0, 0);
+ assert_matches("foo/b/a/z/bar", "foo/**/bar", 1, 1, 1, 1);
+ assert_matches("foo/b/a/z/bar", "foo/**/**/bar", 1, 1, 1, 1);
+ assert_matches("foo/bar", "foo/**/bar", 1, 1, 0, 0);
+ assert_matches("foo/bar", "foo/**/**/bar", 1, 1, 0, 0);
+ assert_matches("foo/bar", "foo?bar", 0, 0, 1, 1);
+ assert_matches("foo/bar", "foo[/]bar", 0, 0, 1, 1);
+ assert_matches("foo/bar", "foo[^a-z]bar", 0, 0, 1, 1);
+ assert_matches("foo/bar", "f[^eiu][^eiu][^eiu][^eiu][^eiu]r", 0, 0, 1, 1);
+ assert_matches("foo-bar", "f[^eiu][^eiu][^eiu][^eiu][^eiu]r", 1, 1, 1, 1);
+ assert_matches("foo", "**/foo", 1, 1, 0, 0);
+ assert_matches("XXX/foo", "**/foo", 1, 1, 1, 1);
+ assert_matches("bar/baz/foo", "**/foo", 1, 1, 1, 1);
+ assert_matches("bar/baz/foo", "*/foo", 0, 0, 1, 1);
+ assert_matches("foo/bar/baz", "**/bar*", 0, 0, 1, 1);
+ assert_matches("deep/foo/bar/baz", "**/bar/*", 1, 1, 1, 1);
+ assert_matches("deep/foo/bar/baz/", "**/bar/*", 0, 0, 1, 1);
+ assert_matches("deep/foo/bar/baz/", "**/bar/**", 1, 1, 1, 1);
+ assert_matches("deep/foo/bar", "**/bar/*", 0, 0, 0, 0);
+ assert_matches("deep/foo/bar/", "**/bar/**", 1, 1, 1, 1);
+ assert_matches("foo/bar/baz", "**/bar**", 0, 0, 1, 1);
+ assert_matches("foo/bar/baz/x", "*/bar/**", 1, 1, 1, 1);
+ assert_matches("deep/foo/bar/baz/x", "*/bar/**", 0, 0, 1, 1);
+ assert_matches("deep/foo/bar/baz/x", "**/bar/*/*", 1, 1, 1, 1);
+}
+
+void test_wildmatch__various_additional(void)
+{
+ assert_matches("acrt", "a[c-c]st", 0, 0, 0, 0);
+ assert_matches("acrt", "a[c-c]rt", 1, 1, 1, 1);
+ assert_matches("]", "[!]-]", 0, 0, 0, 0);
+ assert_matches("a", "[!]-]", 1, 1, 1, 1);
+ assert_matches("", "\\", 0, 0, 0, 0);
+ assert_matches("\\", "\\", 0, 0, 0, 0);
+ assert_matches("XXX/\\", "*/\\", 0, 0, 0, 0);
+ assert_matches("XXX/\\", "*/\\\\", 1, 1, 1, 1);
+ assert_matches("foo", "foo", 1, 1, 1, 1);
+ assert_matches("@foo", "@foo", 1, 1, 1, 1);
+ assert_matches("foo", "@foo", 0, 0, 0, 0);
+ assert_matches("[ab]", "\\[ab]", 1, 1, 1, 1);
+ assert_matches("[ab]", "[[]ab]", 1, 1, 1, 1);
+ assert_matches("[ab]", "[[:]ab]", 1, 1, 1, 1);
+ assert_matches("[ab]", "[[::]ab]", 0, 0, 0, 0);
+ assert_matches("[ab]", "[[:digit]ab]", 1, 1, 1, 1);
+ assert_matches("[ab]", "[\\[:]ab]", 1, 1, 1, 1);
+ assert_matches("?a?b", "\\??\\?b", 1, 1, 1, 1);
+ assert_matches("abc", "\\a\\b\\c", 1, 1, 1, 1);
+ assert_matches("foo", "", 0, 0, 0, 0);
+ assert_matches("foo/bar/baz/to", "**/t[o]", 1, 1, 1, 1);
+}
+
+void test_wildmatch__character_classes(void)
+{
+ assert_matches("a1B", "[[:alpha:]][[:digit:]][[:upper:]]", 1, 1, 1, 1);
+ assert_matches("a", "[[:digit:][:upper:][:space:]]", 0, 1, 0, 1);
+ assert_matches("A", "[[:digit:][:upper:][:space:]]", 1, 1, 1, 1);
+ assert_matches("1", "[[:digit:][:upper:][:space:]]", 1, 1, 1, 1);
+ assert_matches("1", "[[:digit:][:upper:][:spaci:]]", 0, 0, 0, 0);
+ assert_matches(" ", "[[:digit:][:upper:][:space:]]", 1, 1, 1, 1);
+ assert_matches(".", "[[:digit:][:upper:][:space:]]", 0, 0, 0, 0);
+ assert_matches(".", "[[:digit:][:punct:][:space:]]", 1, 1, 1, 1);
+ assert_matches("5", "[[:xdigit:]]", 1, 1, 1, 1);
+ assert_matches("f", "[[:xdigit:]]", 1, 1, 1, 1);
+ assert_matches("D", "[[:xdigit:]]", 1, 1, 1, 1);
+ assert_matches("_", "[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]", 1, 1, 1, 1);
+ assert_matches(".", "[^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:lower:][:space:][:upper:][:xdigit:]]", 1, 1, 1, 1);
+ assert_matches("5", "[a-c[:digit:]x-z]", 1, 1, 1, 1);
+ assert_matches("b", "[a-c[:digit:]x-z]", 1, 1, 1, 1);
+ assert_matches("y", "[a-c[:digit:]x-z]", 1, 1, 1, 1);
+ assert_matches("q", "[a-c[:digit:]x-z]", 0, 0, 0, 0);
+}
+
+void test_wildmatch__additional_with_malformed(void)
+{
+ assert_matches("]", "[\\\\-^]", 1, 1, 1, 1);
+ assert_matches("[", "[\\\\-^]", 0, 0, 0, 0);
+ assert_matches("-", "[\\-_]", 1, 1, 1, 1);
+ assert_matches("]", "[\\]]", 1, 1, 1, 1);
+ assert_matches("\\]", "[\\]]", 0, 0, 0, 0);
+ assert_matches("\\", "[\\]]", 0, 0, 0, 0);
+ assert_matches("ab", "a[]b", 0, 0, 0, 0);
+ assert_matches("a[]b", "a[]b", 0, 0, 0, 0);
+ assert_matches("ab[", "ab[", 0, 0, 0, 0);
+ assert_matches("ab", "[!", 0, 0, 0, 0);
+ assert_matches("ab", "[-", 0, 0, 0, 0);
+ assert_matches("-", "[-]", 1, 1, 1, 1);
+ assert_matches("-", "[a-", 0, 0, 0, 0);
+ assert_matches("-", "[!a-", 0, 0, 0, 0);
+ assert_matches("-", "[--A]", 1, 1, 1, 1);
+ assert_matches("5", "[--A]", 1, 1, 1, 1);
+ assert_matches(" ", "[ --]", 1, 1, 1, 1);
+ assert_matches("$", "[ --]", 1, 1, 1, 1);
+ assert_matches("-", "[ --]", 1, 1, 1, 1);
+ assert_matches("0", "[ --]", 0, 0, 0, 0);
+ assert_matches("-", "[---]", 1, 1, 1, 1);
+ assert_matches("-", "[------]", 1, 1, 1, 1);
+ assert_matches("j", "[a-e-n]", 0, 0, 0, 0);
+ assert_matches("-", "[a-e-n]", 1, 1, 1, 1);
+ assert_matches("a", "[!------]", 1, 1, 1, 1);
+ assert_matches("[", "[]-a]", 0, 0, 0, 0);
+ assert_matches("^", "[]-a]", 1, 1, 1, 1);
+ assert_matches("^", "[!]-a]", 0, 0, 0, 0);
+ assert_matches("[", "[!]-a]", 1, 1, 1, 1);
+ assert_matches("^", "[a^bc]", 1, 1, 1, 1);
+ assert_matches("-b]", "[a-]b]", 1, 1, 1, 1);
+ assert_matches("\\", "[\\]", 0, 0, 0, 0);
+ assert_matches("\\", "[\\\\]", 1, 1, 1, 1);
+ assert_matches("\\", "[!\\\\]", 0, 0, 0, 0);
+ assert_matches("G", "[A-\\\\]", 1, 1, 1, 1);
+ assert_matches("aaabbb", "b*a", 0, 0, 0, 0);
+ assert_matches("aabcaa", "*ba*", 0, 0, 0, 0);
+ assert_matches(",", "[,]", 1, 1, 1, 1);
+ assert_matches(",", "[\\\\,]", 1, 1, 1, 1);
+ assert_matches("\\", "[\\\\,]", 1, 1, 1, 1);
+ assert_matches("-", "[,-.]", 1, 1, 1, 1);
+ assert_matches("+", "[,-.]", 0, 0, 0, 0);
+ assert_matches("-.]", "[,-.]", 0, 0, 0, 0);
+ assert_matches("2", "[\\1-\\3]", 1, 1, 1, 1);
+ assert_matches("3", "[\\1-\\3]", 1, 1, 1, 1);
+ assert_matches("4", "[\\1-\\3]", 0, 0, 0, 0);
+ assert_matches("\\", "[[-\\]]", 1, 1, 1, 1);
+ assert_matches("[", "[[-\\]]", 1, 1, 1, 1);
+ assert_matches("]", "[[-\\]]", 1, 1, 1, 1);
+ assert_matches("-", "[[-\\]]", 0, 0, 0, 0);
+}
+
+void test_wildmatch__recursion(void)
+{
+ assert_matches("-adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1", "-*-*-*-*-*-*-12-*-*-*-m-*-*-*", 1, 1, 1, 1);
+ assert_matches("-adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1", "-*-*-*-*-*-*-12-*-*-*-m-*-*-*", 0, 0, 0, 0);
+ assert_matches("-adobe-courier-bold-o-normal--12-120-75-75-/-70-iso8859-1", "-*-*-*-*-*-*-12-*-*-*-m-*-*-*", 0, 0, 0, 0);
+ assert_matches("XXX/adobe/courier/bold/o/normal//12/120/75/75/m/70/iso8859/1", "XXX/*/*/*/*/*/*/12/*/*/*/m/*/*/*", 1, 1, 1, 1);
+ assert_matches("XXX/adobe/courier/bold/o/normal//12/120/75/75/X/70/iso8859/1", "XXX/*/*/*/*/*/*/12/*/*/*/m/*/*/*", 0, 0, 0, 0);
+ assert_matches("abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txt", "**/*a*b*g*n*t", 1, 1, 1, 1);
+ assert_matches("abcd/abcdefg/abcdefghijk/abcdefghijklmnop.txtz", "**/*a*b*g*n*t", 0, 0, 0, 0);
+ assert_matches("foo", "*/*/*", 0, 0, 0, 0);
+ assert_matches("foo/bar", "*/*/*", 0, 0, 0, 0);
+ assert_matches("foo/bba/arr", "*/*/*", 1, 1, 1, 1);
+ assert_matches("foo/bb/aa/rr", "*/*/*", 0, 0, 1, 1);
+ assert_matches("foo/bb/aa/rr", "**/**/**", 1, 1, 1, 1);
+ assert_matches("abcXdefXghi", "*X*i", 1, 1, 1, 1);
+ assert_matches("ab/cXd/efXg/hi", "*X*i", 0, 0, 1, 1);
+ assert_matches("ab/cXd/efXg/hi", "*/*X*/*/*i", 1, 1, 1, 1);
+ assert_matches("ab/cXd/efXg/hi", "**/*X*/**/*i", 1, 1, 1, 1);
+}
+
+void test_wildmatch__pathmatch(void)
+{
+ assert_matches("foo", "fo", 0, 0, 0, 0);
+ assert_matches("foo/bar", "foo/bar", 1, 1, 1, 1);
+ assert_matches("foo/bar", "foo/*", 1, 1, 1, 1);
+ assert_matches("foo/bba/arr", "foo/*", 0, 0, 1, 1);
+ assert_matches("foo/bba/arr", "foo/**", 1, 1, 1, 1);
+ assert_matches("foo/bba/arr", "foo*", 0, 0, 1, 1);
+ assert_matches("foo/bba/arr", "foo**", 0, 0, 1, 1);
+ assert_matches("foo/bba/arr", "foo/*arr", 0, 0, 1, 1);
+ assert_matches("foo/bba/arr", "foo/**arr", 0, 0, 1, 1);
+ assert_matches("foo/bba/arr", "foo/*z", 0, 0, 0, 0);
+ assert_matches("foo/bba/arr", "foo/**z", 0, 0, 0, 0);
+ assert_matches("foo/bar", "foo?bar", 0, 0, 1, 1);
+ assert_matches("foo/bar", "foo[/]bar", 0, 0, 1, 1);
+ assert_matches("foo/bar", "foo[^a-z]bar", 0, 0, 1, 1);
+ assert_matches("ab/cXd/efXg/hi", "*Xg*i", 0, 0, 1, 1);
+}
+
+void test_wildmatch__case_sensitivity(void)
+{
+ assert_matches("a", "[A-Z]", 0, 1, 0, 1);
+ assert_matches("A", "[A-Z]", 1, 1, 1, 1);
+ assert_matches("A", "[a-z]", 0, 1, 0, 1);
+ assert_matches("a", "[a-z]", 1, 1, 1, 1);
+ assert_matches("a", "[[:upper:]]", 0, 1, 0, 1);
+ assert_matches("A", "[[:upper:]]", 1, 1, 1, 1);
+ assert_matches("A", "[[:lower:]]", 0, 1, 0, 1);
+ assert_matches("a", "[[:lower:]]", 1, 1, 1, 1);
+ assert_matches("A", "[B-Za]", 0, 1, 0, 1);
+ assert_matches("a", "[B-Za]", 1, 1, 1, 1);
+ assert_matches("A", "[B-a]", 0, 1, 0, 1);
+ assert_matches("a", "[B-a]", 1, 1, 1, 1);
+ assert_matches("z", "[Z-y]", 0, 1, 0, 1);
+ assert_matches("Z", "[Z-y]", 1, 1, 1, 1);
+}
diff --git a/tests/util/zstream.c b/tests/util/zstream.c
new file mode 100644
index 0000000..5c89895
--- /dev/null
+++ b/tests/util/zstream.c
@@ -0,0 +1,167 @@
+#include "clar_libgit2.h"
+#include "zstream.h"
+
+static const char *data = "This is a test test test of This is a test";
+
+#define INFLATE_EXTRA 2
+
+static void assert_zlib_equal_(
+ const void *expected, size_t e_len,
+ const void *compressed, size_t c_len,
+ const char *msg, const char *file, const char *func, int line)
+{
+ z_stream stream;
+ char *expanded = git__calloc(1, e_len + INFLATE_EXTRA);
+ cl_assert(expanded);
+
+ memset(&stream, 0, sizeof(stream));
+ stream.next_out = (Bytef *)expanded;
+ stream.avail_out = (uInt)(e_len + INFLATE_EXTRA);
+ stream.next_in = (Bytef *)compressed;
+ stream.avail_in = (uInt)c_len;
+
+ cl_assert(inflateInit(&stream) == Z_OK);
+ cl_assert(inflate(&stream, Z_FINISH));
+ inflateEnd(&stream);
+
+ clar__assert_equal(
+ file, func, line, msg, 1,
+ "%d", (int)stream.total_out, (int)e_len);
+ clar__assert_equal(
+ file, func, line, "Buffer len was not exact match", 1,
+ "%d", (int)stream.avail_out, (int)INFLATE_EXTRA);
+
+ clar__assert(
+ memcmp(expanded, expected, e_len) == 0,
+ file, func, line, "uncompressed data did not match", NULL, 1);
+
+ git__free(expanded);
+}
+
+#define assert_zlib_equal(E,EL,C,CL) \
+ assert_zlib_equal_(E, EL, C, CL, #EL " != " #CL, __FILE__, __func__, (int)__LINE__)
+
+void test_zstream__basic(void)
+{
+ git_zstream z = GIT_ZSTREAM_INIT;
+ char out[128];
+ size_t outlen = sizeof(out);
+
+ cl_git_pass(git_zstream_init(&z, GIT_ZSTREAM_DEFLATE));
+ cl_git_pass(git_zstream_set_input(&z, data, strlen(data) + 1));
+ cl_git_pass(git_zstream_get_output(out, &outlen, &z));
+ cl_assert(git_zstream_done(&z));
+ cl_assert(outlen > 0);
+ git_zstream_free(&z);
+
+ assert_zlib_equal(data, strlen(data) + 1, out, outlen);
+}
+
+void test_zstream__fails_on_trailing_garbage(void)
+{
+ git_str deflated = GIT_STR_INIT, inflated = GIT_STR_INIT;
+ char i = 0;
+
+ /* compress a simple string */
+ git_zstream_deflatebuf(&deflated, "foobar!!", 8);
+
+ /* append some garbage */
+ for (i = 0; i < 10; i++) {
+ git_str_putc(&deflated, i);
+ }
+
+ cl_git_fail(git_zstream_inflatebuf(&inflated, deflated.ptr, deflated.size));
+
+ git_str_dispose(&deflated);
+ git_str_dispose(&inflated);
+}
+
+void test_zstream__buffer(void)
+{
+ git_str out = GIT_STR_INIT;
+ cl_git_pass(git_zstream_deflatebuf(&out, data, strlen(data) + 1));
+ assert_zlib_equal(data, strlen(data) + 1, out.ptr, out.size);
+ git_str_dispose(&out);
+}
+
+#define BIG_STRING_PART "Big Data IS Big - Long Data IS Long - We need a buffer larger than 1024 x 1024 to make sure we trigger chunked compression - Big Big Data IS Bigger than Big - Long Long Data IS Longer than Long"
+
+static void compress_and_decompress_input_various_ways(git_str *input)
+{
+ git_str out1 = GIT_STR_INIT, out2 = GIT_STR_INIT;
+ git_str inflated = GIT_STR_INIT;
+ size_t i, fixed_size = max(input->size / 2, 256);
+ char *fixed = git__malloc(fixed_size);
+ cl_assert(fixed);
+
+ /* compress with deflatebuf */
+
+ cl_git_pass(git_zstream_deflatebuf(&out1, input->ptr, input->size));
+ assert_zlib_equal(input->ptr, input->size, out1.ptr, out1.size);
+
+ /* compress with various fixed size buffer (accumulating the output) */
+
+ for (i = 0; i < 3; ++i) {
+ git_zstream zs = GIT_ZSTREAM_INIT;
+ size_t use_fixed_size;
+
+ switch (i) {
+ case 0: use_fixed_size = 256; break;
+ case 1: use_fixed_size = fixed_size / 2; break;
+ case 2: use_fixed_size = fixed_size; break;
+ }
+ cl_assert(use_fixed_size <= fixed_size);
+
+ cl_git_pass(git_zstream_init(&zs, GIT_ZSTREAM_DEFLATE));
+ cl_git_pass(git_zstream_set_input(&zs, input->ptr, input->size));
+
+ while (!git_zstream_done(&zs)) {
+ size_t written = use_fixed_size;
+ cl_git_pass(git_zstream_get_output(fixed, &written, &zs));
+ cl_git_pass(git_str_put(&out2, fixed, written));
+ }
+
+ git_zstream_free(&zs);
+ assert_zlib_equal(input->ptr, input->size, out2.ptr, out2.size);
+
+ /* did both approaches give the same data? */
+ cl_assert_equal_sz(out1.size, out2.size);
+ cl_assert(!memcmp(out1.ptr, out2.ptr, out1.size));
+
+ git_str_dispose(&out2);
+ }
+
+ cl_git_pass(git_zstream_inflatebuf(&inflated, out1.ptr, out1.size));
+ cl_assert_equal_i(input->size, inflated.size);
+ cl_assert(memcmp(input->ptr, inflated.ptr, inflated.size) == 0);
+
+ git_str_dispose(&out1);
+ git_str_dispose(&inflated);
+ git__free(fixed);
+}
+
+void test_zstream__big_data(void)
+{
+ git_str in = GIT_STR_INIT;
+ size_t scan, target;
+
+ for (target = 1024; target <= 1024 * 1024 * 4; target *= 8) {
+
+ /* make a big string that's easy to compress */
+ git_str_clear(&in);
+ while (in.size < target)
+ cl_git_pass(
+ git_str_put(&in, BIG_STRING_PART, strlen(BIG_STRING_PART)));
+
+ compress_and_decompress_input_various_ways(&in);
+
+ /* make a big string that's hard to compress */
+ srand(0xabad1dea);
+ for (scan = 0; scan < in.size; ++scan)
+ in.ptr[scan] = (char)rand();
+
+ compress_and_decompress_input_various_ways(&in);
+ }
+
+ git_str_dispose(&in);
+}